From cda1e3fb0f390b3cc4df1be25c0d6557e81ec3bb Mon Sep 17 00:00:00 2001 From: Michal Simek Date: Fri, 26 Jan 2018 13:17:04 +0100 Subject: fpga: Simplify error path in fpga_add Check !desc earlier to simplify code. Signed-off-by: Michal Simek Acked-by: Simon Goldschmidt Reviewed-by: Simon Goldschmidt --- drivers/fpga/fpga.c | 21 +++++++++++---------- 1 file changed, 11 insertions(+), 10 deletions(-) (limited to 'drivers') diff --git a/drivers/fpga/fpga.c b/drivers/fpga/fpga.c index 6aead27f162..ac01612d751 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); -- cgit v1.3.1 From d77081cf50489a441739481e096411858c632276 Mon Sep 17 00:00:00 2001 From: Siva Durga Prasad Paladugu Date: Tue, 20 Feb 2018 11:56:19 +0530 Subject: net: zynq_gem: Dont run any phy detection logic for GMII case This patch bypasses phy detection logic for GMII interface and just depend on phy address received from DT. This patch is required as phy detection logic is different for some phys like xilinx phy which can be connected over SGMII and GMII interface. This fixes the issue of ethernet failures when xilinx phy is connected over GMII interface. Signed-off-by: Siva Durga Prasad Paladugu Signed-off-by: Michal Simek Acked-by: Joe Hershberger --- drivers/net/zynq_gem.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/net/zynq_gem.c b/drivers/net/zynq_gem.c index 2cc49bca922..1390c36c614 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"); -- cgit v1.3.1 From 58afff43e3a8f31344cbbc6a3f09bd3f7a2a70eb Mon Sep 17 00:00:00 2001 From: Michal Simek Date: Wed, 21 Feb 2018 15:06:20 +0100 Subject: clk: zynq: Show watchdog clock rate properly watchdog clock is also connected to cpu 1X clocksource. Zynq> clk dump ... Before: swdt 4294967290 After: swdt 111111110 Signed-off-by: Michal Simek --- drivers/clk/clk_zynq.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/clk/clk_zynq.c b/drivers/clk/clk_zynq.c index 50f2a65c205..3845e073097 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; -- cgit v1.3.1 From 42537ca4c890bb31ddf11a5cac578d5a2c81bf37 Mon Sep 17 00:00:00 2001 From: Shreenidhi Shedi Date: Wed, 21 Feb 2018 16:50:20 +0100 Subject: watchdog: Add Cadence watchdog driver This IP can be found on Zynq and ZynqMP devices. The driver was tested with reset-on-timeout; feature. Also adding WATCHDOG symbol to Kconfig because it is required. Signed-off-by: Shreenidhi Shedi Signed-off-by: Michal Simek --- drivers/watchdog/Kconfig | 16 +++ drivers/watchdog/Makefile | 1 + drivers/watchdog/cdns_wdt.c | 276 ++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 293 insertions(+) create mode 100644 drivers/watchdog/cdns_wdt.c (limited to 'drivers') diff --git a/drivers/watchdog/Kconfig b/drivers/watchdog/Kconfig index fc46b6774d5..8a66e479ab8 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 ab6a6b79e1d..4b97df3ab67 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 00000000000..71733cf8ba1 --- /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 + * + * SPDX-License-Identifier: GPL-2.0 + */ + +#include +#include +#include +#include +#include + +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, +}; -- cgit v1.3.1 From 2453c695185f8fd8257f370b746acdfe5ee56408 Mon Sep 17 00:00:00 2001 From: Vipul Kumar Date: Mon, 5 Mar 2018 15:24:59 +0530 Subject: arm64: zynqmp: nand: Fixed NAND erase issue for size 1GiB or more NAND erase was not happening for size 1GiB or more. Erase command was executing successfully but in actual, it was not erasing. This patch fixed erase issue for 1 GiB or more size nand. Signed-off-by: Vipul Kumar Signed-off-by: Michal Simek --- drivers/mtd/nand/arasan_nfc.c | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) (limited to 'drivers') diff --git a/drivers/mtd/nand/arasan_nfc.c b/drivers/mtd/nand/arasan_nfc.c index 3c9a0215c53..9c82c7db33f 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; -- cgit v1.3.1 From a79b590f78ca9ad2d786e3ae3180cca02cb02bfc Mon Sep 17 00:00:00 2001 From: Vipul Kumar Date: Wed, 7 Mar 2018 14:52:44 +0530 Subject: arm64: zynqmp: Print the value of pl clocks and wdt clock using clk dump This patch print pl clocks (pl0...pl3) and watchdog clock using clk dump. Signed-off-by: Vipul Kumar Signed-off-by: Michal Simek --- drivers/clk/clk_zynqmp.c | 75 ++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 75 insertions(+) (limited to 'drivers') diff --git a/drivers/clk/clk_zynqmp.c b/drivers/clk/clk_zynqmp.c index bcc62904f17..4ef8662af56 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; } -- cgit v1.3.1