diff options
author | Tom Rini <trini@konsulko.com> | 2018-03-25 12:00:00 -0400 |
---|---|---|
committer | Tom Rini <trini@konsulko.com> | 2018-03-25 12:00:00 -0400 |
commit | 89a650e0ffb89faaea1b9e6ad8cf2b38203435f2 (patch) | |
tree | 4827f512e3c1e6f2887133bbb9cb3ee9497ba603 /drivers | |
parent | 423effc04a195ce6a464eadadfa7f765bf786889 (diff) | |
parent | 8ae3d0b50cd22aebcd87022dc357d1cf0f3a879b (diff) |
Merge tag 'xilinx-for-v2018.05' of git://git.denx.de/u-boot-microblaze
Xilinx changes for v2018.05
- Fix mkimage recognition
- Update all my fragments
ZynqMP:
- Use clk driver
- Support loading elfs in el1
- Various DTS and defconfig changes
- Enable newer pmufw versions
- Support more clocks
- Remove ep108
- Secure image support
- Fix memtest setup
Zynq:
- Enabling watchdog driver
- Support more clocks
- defconfig changes
fpga:
- Simplify error path
net:
- GMII case update
Diffstat (limited to 'drivers')
-rw-r--r-- | drivers/clk/clk_zynq.c | 2 | ||||
-rw-r--r-- | drivers/clk/clk_zynqmp.c | 75 | ||||
-rw-r--r-- | drivers/fpga/fpga.c | 21 | ||||
-rw-r--r-- | drivers/mtd/nand/arasan_nfc.c | 7 | ||||
-rw-r--r-- | drivers/net/zynq_gem.c | 3 | ||||
-rw-r--r-- | drivers/watchdog/Kconfig | 16 | ||||
-rw-r--r-- | drivers/watchdog/Makefile | 1 | ||||
-rw-r--r-- | drivers/watchdog/cdns_wdt.c | 276 |
8 files changed, 386 insertions, 15 deletions
diff --git a/drivers/clk/clk_zynq.c b/drivers/clk/clk_zynq.c index 50f2a65c20..3845e07309 100644 --- a/drivers/clk/clk_zynq.c +++ b/drivers/clk/clk_zynq.c @@ -394,7 +394,7 @@ static ulong zynq_clk_get_rate(struct clk *clk) return zynq_clk_get_peripheral_rate(priv, id, two_divs); case dma_clk: return zynq_clk_get_cpu_rate(priv, cpu_2x_clk); - case usb0_aper_clk ... smc_aper_clk: + case usb0_aper_clk ... swdt_clk: return zynq_clk_get_cpu_rate(priv, cpu_1x_clk); default: return -ENXIO; diff --git a/drivers/clk/clk_zynqmp.c b/drivers/clk/clk_zynqmp.c index bcc62904f1..4ef8662af5 100644 --- a/drivers/clk/clk_zynqmp.c +++ b/drivers/clk/clk_zynqmp.c @@ -226,6 +226,18 @@ static u32 zynqmp_clk_get_register(enum zynqmp_clk id) return CRL_APB_CAN0_REF_CTRL; case can1_ref: return CRL_APB_CAN1_REF_CTRL; + case pl0: + return CRL_APB_PL0_REF_CTRL; + case pl1: + return CRL_APB_PL1_REF_CTRL; + case pl2: + return CRL_APB_PL2_REF_CTRL; + case pl3: + return CRL_APB_PL3_REF_CTRL; + case wdt: + return CRF_APB_TOPSW_LSBUS_CTRL; + case iopll_to_fpd: + return CRL_APB_IOPLL_TO_FPD_CTRL; default: debug("Invalid clk id%d\n", id); } @@ -278,6 +290,22 @@ static enum zynqmp_clk zynqmp_clk_get_peripheral_pll(u32 clk_ctrl) } } +static enum zynqmp_clk zynqmp_clk_get_wdt_pll(u32 clk_ctrl) +{ + u32 srcsel = (clk_ctrl & CLK_CTRL_SRCSEL_MASK) >> + CLK_CTRL_SRCSEL_SHIFT; + + switch (srcsel) { + case 2: + return iopll_to_fpd; + case 3: + return dpll; + case 0 ... 1: + default: + return apll; + } +} + static ulong zynqmp_clk_get_pll_src(ulong clk_ctrl, struct zynqmp_clk_priv *priv, bool is_pre_src) @@ -420,6 +448,49 @@ static ulong zynqmp_clk_get_peripheral_rate(struct zynqmp_clk_priv *priv, DIV_ROUND_CLOSEST(pllrate, div0), div1); } +static ulong zynqmp_clk_get_wdt_rate(struct zynqmp_clk_priv *priv, + enum zynqmp_clk id, bool two_divs) +{ + enum zynqmp_clk pll; + u32 clk_ctrl, div0; + u32 div1 = 1; + int ret; + ulong pllrate; + + ret = zynqmp_mmio_read(zynqmp_clk_get_register(id), &clk_ctrl); + if (ret) { + printf("%d %s mio read fail\n", __LINE__, __func__); + return -EIO; + } + + div0 = (clk_ctrl & CLK_CTRL_DIV0_MASK) >> CLK_CTRL_DIV0_SHIFT; + if (!div0) + div0 = 1; + + pll = zynqmp_clk_get_wdt_pll(clk_ctrl); + if (two_divs) { + ret = zynqmp_mmio_read(zynqmp_clk_get_register(pll), &clk_ctrl); + if (ret) { + printf("%d %s mio read fail\n", __LINE__, __func__); + return -EIO; + } + div1 = (clk_ctrl & CLK_CTRL_DIV0_MASK) >> CLK_CTRL_DIV0_SHIFT; + if (!div1) + div1 = 1; + } + + if (pll == iopll_to_fpd) + pll = iopll; + + pllrate = zynqmp_clk_get_pll_rate(priv, pll); + if (IS_ERR_VALUE(pllrate)) + return pllrate; + + return + DIV_ROUND_CLOSEST( + DIV_ROUND_CLOSEST(pllrate, div0), div1); +} + static unsigned long zynqmp_clk_calc_peripheral_two_divs(ulong rate, ulong pll_rate, u32 *div0, u32 *div1) @@ -510,8 +581,12 @@ static ulong zynqmp_clk_get_rate(struct clk *clk) return zynqmp_clk_get_ddr_rate(priv); case gem0_ref ... gem3_ref: case qspi_ref ... can1_ref: + case pl0 ... pl3: two_divs = true; return zynqmp_clk_get_peripheral_rate(priv, id, two_divs); + case wdt: + two_divs = true; + return zynqmp_clk_get_wdt_rate(priv, id, two_divs); default: return -ENXIO; } diff --git a/drivers/fpga/fpga.c b/drivers/fpga/fpga.c index 6aead27f16..ac01612d75 100644 --- a/drivers/fpga/fpga.c +++ b/drivers/fpga/fpga.c @@ -148,20 +148,21 @@ int fpga_add(fpga_type devtype, void *desc) { int devnum = FPGA_INVALID_DEVICE; + if (!desc) { + printf("%s: NULL device descriptor\n", __func__); + return devnum; + } + if (next_desc < 0) { printf("%s: FPGA support not initialized!\n", __func__); } else if ((devtype > fpga_min_type) && (devtype < fpga_undefined)) { - if (desc) { - if (next_desc < CONFIG_MAX_FPGA_DEVICES) { - devnum = next_desc; - desc_table[next_desc].devtype = devtype; - desc_table[next_desc++].devdesc = desc; - } else { - printf("%s: Exceeded Max FPGA device count\n", - __func__); - } + if (next_desc < CONFIG_MAX_FPGA_DEVICES) { + devnum = next_desc; + desc_table[next_desc].devtype = devtype; + desc_table[next_desc++].devdesc = desc; } else { - printf("%s: NULL device descriptor\n", __func__); + printf("%s: Exceeded Max FPGA device count\n", + __func__); } } else { printf("%s: Unsupported FPGA type %d\n", __func__, devtype); diff --git a/drivers/mtd/nand/arasan_nfc.c b/drivers/mtd/nand/arasan_nfc.c index 3c9a0215c5..9c82c7db33 100644 --- a/drivers/mtd/nand/arasan_nfc.c +++ b/drivers/mtd/nand/arasan_nfc.c @@ -86,7 +86,7 @@ struct arasan_nand_command_format { #define ARASAN_NAND_CMD_ADDR_CYCL_MASK 0x70000000 #define ARASAN_NAND_CMD_ADDR_CYCL_SHIFT 28 -#define ARASAN_NAND_MEM_ADDR1_PAGE_MASK 0xFFFF0000 +#define ARASAN_NAND_MEM_ADDR1_PAGE_MASK 0xFFFF #define ARASAN_NAND_MEM_ADDR1_COL_MASK 0xFFFF #define ARASAN_NAND_MEM_ADDR1_PAGE_SHIFT 16 #define ARASAN_NAND_MEM_ADDR2_PAGE_MASK 0xFF @@ -795,10 +795,11 @@ static int arasan_nand_erase(struct arasan_nand_command_format *curr_cmd, writel(reg_val, &arasan_nand_base->cmd_reg); - page = (page_addr << ARASAN_NAND_MEM_ADDR1_PAGE_SHIFT) & + page = (page_addr >> ARASAN_NAND_MEM_ADDR1_PAGE_SHIFT) & ARASAN_NAND_MEM_ADDR1_PAGE_MASK; column = page_addr & ARASAN_NAND_MEM_ADDR1_COL_MASK; - writel(page | column, &arasan_nand_base->memadr_reg1); + writel(column | (page << ARASAN_NAND_MEM_ADDR1_PAGE_SHIFT), + &arasan_nand_base->memadr_reg1); reg_val = readl(&arasan_nand_base->memadr_reg2); reg_val &= ~ARASAN_NAND_MEM_ADDR2_PAGE_MASK; diff --git a/drivers/net/zynq_gem.c b/drivers/net/zynq_gem.c index 2cc49bca92..1390c36c61 100644 --- a/drivers/net/zynq_gem.c +++ b/drivers/net/zynq_gem.c @@ -325,7 +325,8 @@ static int zynq_phy_init(struct udevice *dev) /* Enable only MDIO bus */ writel(ZYNQ_GEM_NWCTRL_MDEN_MASK, ®s->nwctrl); - if (priv->interface != PHY_INTERFACE_MODE_SGMII) { + if ((priv->interface != PHY_INTERFACE_MODE_SGMII) && + (priv->interface != PHY_INTERFACE_MODE_GMII)) { ret = phy_detection(dev); if (ret) { printf("GEM PHY init failed\n"); diff --git a/drivers/watchdog/Kconfig b/drivers/watchdog/Kconfig index fc46b6774d..8a66e479ab 100644 --- a/drivers/watchdog/Kconfig +++ b/drivers/watchdog/Kconfig @@ -1,5 +1,13 @@ menu "Watchdog Timer Support" +config WATCHDOG + bool "Enable U-Boot watchdog reset" + help + This option enables U-Boot watchdog support where U-Boot is using + watchdog_reset function to service watchdog device in U-Boot. Enable + this option if you want to service enabled watchdog by U-Boot. Disable + this option if you want U-Boot to start watchdog but never service it. + config HW_WATCHDOG bool @@ -78,4 +86,12 @@ config WDT_ORION Select this to enable Orion watchdog timer, which can be found on some Marvell Armada chips. +config WDT_CDNS + bool "Cadence watchdog timer support" + depends on WDT + imply WATCHDOG + help + Select this to enable Cadence watchdog timer, which can be found on some + Xilinx Microzed Platform. + endmenu diff --git a/drivers/watchdog/Makefile b/drivers/watchdog/Makefile index ab6a6b79e1..4b97df3ab6 100644 --- a/drivers/watchdog/Makefile +++ b/drivers/watchdog/Makefile @@ -22,3 +22,4 @@ obj-$(CONFIG_WDT_ASPEED) += ast_wdt.o obj-$(CONFIG_WDT_BCM6345) += bcm6345_wdt.o obj-$(CONFIG_BCM2835_WDT) += bcm2835_wdt.o obj-$(CONFIG_WDT_ORION) += orion_wdt.o +obj-$(CONFIG_WDT_CDNS) += cdns_wdt.o diff --git a/drivers/watchdog/cdns_wdt.c b/drivers/watchdog/cdns_wdt.c new file mode 100644 index 0000000000..71733cf8ba --- /dev/null +++ b/drivers/watchdog/cdns_wdt.c @@ -0,0 +1,276 @@ +/* + * Cadence WDT driver - Used by Xilinx Zynq + * Reference: Linux kernel Cadence watchdog driver. + * + * Author(s): Shreenidhi Shedi <yesshedi@gmail.com> + * + * SPDX-License-Identifier: GPL-2.0 + */ + +#include <common.h> +#include <dm.h> +#include <wdt.h> +#include <clk.h> +#include <linux/io.h> + +DECLARE_GLOBAL_DATA_PTR; + +struct cdns_regs { + u32 zmr; /* WD Zero mode register, offset - 0x0 */ + u32 ccr; /* Counter Control Register offset - 0x4 */ + u32 restart; /* Restart key register, offset - 0x8 */ + u32 status; /* Status Register, offset - 0xC */ +}; + +struct cdns_wdt_priv { + bool rst; + u32 timeout; + void __iomem *reg; + struct cdns_regs *regs; +}; + +#define CDNS_WDT_DEFAULT_TIMEOUT 10 + +/* Supports 1 - 516 sec */ +#define CDNS_WDT_MIN_TIMEOUT 1 +#define CDNS_WDT_MAX_TIMEOUT 516 + +/* Restart key */ +#define CDNS_WDT_RESTART_KEY 0x00001999 + +/* Counter register access key */ +#define CDNS_WDT_REGISTER_ACCESS_KEY 0x00920000 + +/* Counter value divisor */ +#define CDNS_WDT_COUNTER_VALUE_DIVISOR 0x1000 + +/* Clock prescaler value and selection */ +#define CDNS_WDT_PRESCALE_64 64 +#define CDNS_WDT_PRESCALE_512 512 +#define CDNS_WDT_PRESCALE_4096 4096 +#define CDNS_WDT_PRESCALE_SELECT_64 1 +#define CDNS_WDT_PRESCALE_SELECT_512 2 +#define CDNS_WDT_PRESCALE_SELECT_4096 3 + +/* Input clock frequency */ +#define CDNS_WDT_CLK_75MHZ 75000000 + +/* Counter maximum value */ +#define CDNS_WDT_COUNTER_MAX 0xFFF + +/********************* Register Map **********************************/ + +/* + * Zero Mode Register - This register controls how the time out is indicated + * and also contains the access code to allow writes to the register (0xABC). + */ +#define CDNS_WDT_ZMR_WDEN_MASK 0x00000001 /* Enable the WDT */ +#define CDNS_WDT_ZMR_RSTEN_MASK 0x00000002 /* Enable the reset output */ +#define CDNS_WDT_ZMR_IRQEN_MASK 0x00000004 /* Enable IRQ output */ +#define CDNS_WDT_ZMR_RSTLEN_16 0x00000030 /* Reset pulse of 16 pclk cycles */ +#define CDNS_WDT_ZMR_ZKEY_VAL 0x00ABC000 /* Access key, 0xABC << 12 */ + +/* + * Counter Control register - This register controls how fast the timer runs + * and the reset value and also contains the access code to allow writes to + * the register. + */ +#define CDNS_WDT_CCR_CRV_MASK 0x00003FFC /* Counter reset value */ + +/* Write access to Registers */ +static inline void cdns_wdt_writereg(u32 *addr, u32 val) +{ + writel(val, addr); +} + +/** + * cdns_wdt_reset - Reload the watchdog timer (i.e. pat the watchdog). + * + * @dev: Watchdog device + * + * Write the restart key value (0x00001999) to the restart register. + * + * Return: Always 0 + */ +static int cdns_wdt_reset(struct udevice *dev) +{ + struct cdns_wdt_priv *priv = dev_get_priv(dev); + + debug("%s\n", __func__); + + cdns_wdt_writereg(&priv->regs->restart, CDNS_WDT_RESTART_KEY); + + return 0; +} + +/** + * cdns_wdt_start - Enable and start the watchdog. + * + * @dev: Watchdog device + * @timeout: Timeout value + * @flags: Driver flags + * + * The counter value is calculated according to the formula: + * count = (timeout * clock) / prescaler + 1. + * + * The calculated count is divided by 0x1000 to obtain the field value + * to write to counter control register. + * + * Clears the contents of prescaler and counter reset value. Sets the + * prescaler to 4096 and the calculated count and access key + * to write to CCR Register. + * + * Sets the WDT (WDEN bit) and either the Reset signal(RSTEN bit) + * or Interrupt signal(IRQEN) with a specified cycles and the access + * key to write to ZMR Register. + * + * Return: Upon success 0, failure -1. + */ +static int cdns_wdt_start(struct udevice *dev, u64 timeout, ulong flags) +{ + ulong clk_f; + u32 count, prescaler, ctrl_clksel, data = 0; + struct clk clock; + struct cdns_wdt_priv *priv = dev_get_priv(dev); + + if (clk_get_by_index(dev, 0, &clock) < 0) { + dev_err(dev, "failed to get clock\n"); + return -1; + } + + clk_f = clk_get_rate(&clock); + if (IS_ERR_VALUE(clk_f)) { + dev_err(dev, "failed to get rate\n"); + return -1; + } + + debug("%s: CLK_FREQ %ld, timeout %lld\n", __func__, clk_f, timeout); + + if ((timeout < CDNS_WDT_MIN_TIMEOUT) || + (timeout > CDNS_WDT_MAX_TIMEOUT)) { + timeout = priv->timeout; + } + + if (clk_f <= CDNS_WDT_CLK_75MHZ) { + prescaler = CDNS_WDT_PRESCALE_512; + ctrl_clksel = CDNS_WDT_PRESCALE_SELECT_512; + } else { + prescaler = CDNS_WDT_PRESCALE_4096; + ctrl_clksel = CDNS_WDT_PRESCALE_SELECT_4096; + } + + /* + * Counter value divisor to obtain the value of + * counter reset to be written to control register. + */ + count = (timeout * (clk_f / prescaler)) / + CDNS_WDT_COUNTER_VALUE_DIVISOR + 1; + + if (count > CDNS_WDT_COUNTER_MAX) + count = CDNS_WDT_COUNTER_MAX; + + cdns_wdt_writereg(&priv->regs->zmr, CDNS_WDT_ZMR_ZKEY_VAL); + + count = (count << 2) & CDNS_WDT_CCR_CRV_MASK; + + /* Write counter access key first to be able write to register */ + data = count | CDNS_WDT_REGISTER_ACCESS_KEY | ctrl_clksel; + cdns_wdt_writereg(&priv->regs->ccr, data); + + data = CDNS_WDT_ZMR_WDEN_MASK | CDNS_WDT_ZMR_RSTLEN_16 | + CDNS_WDT_ZMR_ZKEY_VAL; + + /* Reset on timeout if specified in device tree. */ + if (priv->rst) { + data |= CDNS_WDT_ZMR_RSTEN_MASK; + data &= ~CDNS_WDT_ZMR_IRQEN_MASK; + } else { + data &= ~CDNS_WDT_ZMR_RSTEN_MASK; + data |= CDNS_WDT_ZMR_IRQEN_MASK; + } + + cdns_wdt_writereg(&priv->regs->zmr, data); + cdns_wdt_writereg(&priv->regs->restart, CDNS_WDT_RESTART_KEY); + + return 0; +} + +/** + * cdns_wdt_stop - Stop the watchdog. + * + * @dev: Watchdog device + * + * Read the contents of the ZMR register, clear the WDEN bit in the register + * and set the access key for successful write. + * + * Return: Always 0 + */ +static int cdns_wdt_stop(struct udevice *dev) +{ + struct cdns_wdt_priv *priv = dev_get_priv(dev); + + cdns_wdt_writereg(&priv->regs->zmr, + CDNS_WDT_ZMR_ZKEY_VAL & (~CDNS_WDT_ZMR_WDEN_MASK)); + + return 0; +} + +/** + * cdns_wdt_probe - Probe call for the device. + * + * @dev: Handle to the udevice structure. + * + * Return: Always 0. + */ +static int cdns_wdt_probe(struct udevice *dev) +{ + struct cdns_wdt_priv *priv = dev_get_priv(dev); + + debug("%s: Probing wdt%u\n", __func__, dev->seq); + + priv->reg = ioremap((u32)priv->regs, sizeof(struct cdns_regs)); + + cdns_wdt_stop(dev); + + return 0; +} + +static int cdns_wdt_ofdata_to_platdata(struct udevice *dev) +{ + int node = dev_of_offset(dev); + struct cdns_wdt_priv *priv = dev_get_priv(dev); + + priv->regs = devfdt_get_addr_ptr(dev); + if (IS_ERR(priv->regs)) + return PTR_ERR(priv->regs); + + priv->timeout = fdtdec_get_int(gd->fdt_blob, node, "timeout-sec", + CDNS_WDT_DEFAULT_TIMEOUT); + + priv->rst = fdtdec_get_bool(gd->fdt_blob, node, "reset-on-timeout"); + + debug("%s: timeout %d, reset %d\n", __func__, priv->timeout, priv->rst); + + return 0; +} + +static const struct wdt_ops cdns_wdt_ops = { + .start = cdns_wdt_start, + .reset = cdns_wdt_reset, + .stop = cdns_wdt_stop, +}; + +static const struct udevice_id cdns_wdt_ids[] = { + { .compatible = "cdns,wdt-r1p2" }, + {} +}; + +U_BOOT_DRIVER(cdns_wdt) = { + .name = "cdns_wdt", + .id = UCLASS_WDT, + .of_match = cdns_wdt_ids, + .probe = cdns_wdt_probe, + .priv_auto_alloc_size = sizeof(struct cdns_wdt_priv), + .ofdata_to_platdata = cdns_wdt_ofdata_to_platdata, + .ops = &cdns_wdt_ops, +}; |