diff options
author | Tom Rini <trini@konsulko.com> | 2022-03-14 17:40:36 -0400 |
---|---|---|
committer | Tom Rini <trini@konsulko.com> | 2022-03-14 17:40:36 -0400 |
commit | 2abf048ab7b835787d6627423559832f2b18f253 (patch) | |
tree | afed2c3a11ae600fec9b55ef8c50d5133b65ba87 /drivers | |
parent | 7b958ede8c795501717b972b67473ea4cdc09b45 (diff) | |
parent | c149bf41404e34014e37de32fac332892b11bd4a (diff) |
Merge tag 'v2022.04-rc4' into next
Prepare v2022.04-rc4
Diffstat (limited to 'drivers')
26 files changed, 1176 insertions, 159 deletions
diff --git a/drivers/gpio/Kconfig b/drivers/gpio/Kconfig index be64303d7f..12972cdb4e 100644 --- a/drivers/gpio/Kconfig +++ b/drivers/gpio/Kconfig @@ -552,4 +552,10 @@ config ZYNQMP_GPIO_MODEPIN are accessed using xilinx firmware. In modepin register, [3:0] bits set direction, [7:4] bits read IO, [11:8] bits set/clear IO. +config SL28CPLD_GPIO + bool "Kontron sl28cpld GPIO driver" + depends on DM_GPIO && SL28CPLD + help + Support GPIO access on Kontron sl28cpld board management controllers. + endif diff --git a/drivers/gpio/Makefile b/drivers/gpio/Makefile index 720297952c..44f015397f 100644 --- a/drivers/gpio/Makefile +++ b/drivers/gpio/Makefile @@ -71,4 +71,5 @@ obj-$(CONFIG_NX_GPIO) += nx_gpio.o obj-$(CONFIG_SIFIVE_GPIO) += sifive-gpio.o obj-$(CONFIG_NOMADIK_GPIO) += nmk_gpio.o obj-$(CONFIG_MAX7320_GPIO) += max7320_gpio.o +obj-$(CONFIG_SL28CPLD_GPIO) += sl28cpld-gpio.o obj-$(CONFIG_ZYNQMP_GPIO_MODEPIN) += zynqmp_gpio_modepin.o diff --git a/drivers/gpio/sl28cpld-gpio.c b/drivers/gpio/sl28cpld-gpio.c new file mode 100644 index 0000000000..700fc3df29 --- /dev/null +++ b/drivers/gpio/sl28cpld-gpio.c @@ -0,0 +1,165 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * GPIO driver for the sl28cpld + * + * Copyright (c) 2021 Michael Walle <michael@walle.cc> + */ + +#include <common.h> +#include <dm.h> +#include <asm/gpio.h> +#include <sl28cpld.h> + +/* GPIO flavor */ +#define SL28CPLD_GPIO_DIR 0x00 +#define SL28CPLD_GPIO_OUT 0x01 +#define SL28CPLD_GPIO_IN 0x02 + +/* input-only flavor */ +#define SL28CPLD_GPI_IN 0x00 + +/* output-only flavor */ +#define SL28CPLD_GPO_OUT 0x00 + +enum { + SL28CPLD_GPIO, + SL28CPLD_GPI, + SL28CPLD_GPO, +}; + +static int sl28cpld_gpio_get_value(struct udevice *dev, unsigned int gpio) +{ + ulong type = dev_get_driver_data(dev); + int val, reg; + + switch (type) { + case SL28CPLD_GPIO: + reg = SL28CPLD_GPIO_IN; + break; + case SL28CPLD_GPI: + reg = SL28CPLD_GPI_IN; + break; + case SL28CPLD_GPO: + /* we are output only, thus just return the output value */ + reg = SL28CPLD_GPO_OUT; + break; + default: + return -EINVAL; + } + + val = sl28cpld_read(dev, reg); + + return val < 0 ? val : !!(val & BIT(gpio)); +} + +static int sl28cpld_gpio_set_value(struct udevice *dev, unsigned int gpio, + int value) +{ + ulong type = dev_get_driver_data(dev); + uint reg; + + switch (type) { + case SL28CPLD_GPIO: + reg = SL28CPLD_GPIO_OUT; + break; + case SL28CPLD_GPO: + reg = SL28CPLD_GPO_OUT; + break; + case SL28CPLD_GPI: + default: + return -EINVAL; + } + + if (value) + return sl28cpld_update(dev, reg, 0, BIT(gpio)); + else + return sl28cpld_update(dev, reg, BIT(gpio), 0); +} + +static int sl28cpld_gpio_direction_input(struct udevice *dev, unsigned int gpio) +{ + ulong type = dev_get_driver_data(dev); + + switch (type) { + case SL28CPLD_GPI: + return 0; + case SL28CPLD_GPIO: + return sl28cpld_update(dev, SL28CPLD_GPIO_DIR, BIT(gpio), 0); + case SL28CPLD_GPO: + default: + return -EINVAL; + } +} + +static int sl28cpld_gpio_direction_output(struct udevice *dev, + unsigned int gpio, int value) +{ + ulong type = dev_get_driver_data(dev); + int ret; + + /* set_value() will report an error if we are input-only */ + ret = sl28cpld_gpio_set_value(dev, gpio, value); + if (ret) + return ret; + + if (type == SL28CPLD_GPIO) + return sl28cpld_update(dev, SL28CPLD_GPIO_DIR, 0, BIT(gpio)); + + return 0; +} + +static int sl28cpld_gpio_get_function(struct udevice *dev, unsigned int gpio) +{ + ulong type = dev_get_driver_data(dev); + int val; + + switch (type) { + case SL28CPLD_GPIO: + val = sl28cpld_read(dev, SL28CPLD_GPIO_DIR); + if (val < 0) + return val; + if (val & BIT(gpio)) + return GPIOF_OUTPUT; + else + return GPIOF_INPUT; + case SL28CPLD_GPI: + return GPIOF_INPUT; + case SL28CPLD_GPO: + return GPIOF_OUTPUT; + default: + return -EINVAL; + } +} + +static const struct dm_gpio_ops sl28cpld_gpio_ops = { + .direction_input = sl28cpld_gpio_direction_input, + .direction_output = sl28cpld_gpio_direction_output, + .get_value = sl28cpld_gpio_get_value, + .set_value = sl28cpld_gpio_set_value, + .get_function = sl28cpld_gpio_get_function, +}; + +static int sl28cpld_gpio_probe(struct udevice *dev) +{ + struct gpio_dev_priv *uc_priv = dev_get_uclass_priv(dev); + + uc_priv->gpio_count = 8; + uc_priv->bank_name = dev_read_name(dev); + + return 0; +} + +static const struct udevice_id sl28cpld_gpio_ids[] = { + { .compatible = "kontron,sl28cpld-gpio", .data = SL28CPLD_GPIO}, + { .compatible = "kontron,sl28cpld-gpo", .data = SL28CPLD_GPO}, + { .compatible = "kontron,sl28cpld-gpi", .data = SL28CPLD_GPI}, + { } +}; + +U_BOOT_DRIVER(sl28cpld_gpio) = { + .name = "sl28cpld_gpio", + .id = UCLASS_GPIO, + .of_match = sl28cpld_gpio_ids, + .probe = sl28cpld_gpio_probe, + .ops = &sl28cpld_gpio_ops, +}; diff --git a/drivers/misc/Kconfig b/drivers/misc/Kconfig index 0ade3e32b0..7029bb7b5c 100644 --- a/drivers/misc/Kconfig +++ b/drivers/misc/Kconfig @@ -512,4 +512,12 @@ config ESM_PMIC config FSL_IFC bool +config SL28CPLD + bool "Enable Kontron sl28cpld multi-function driver" + depends on DM_I2C + help + Support for the Kontron sl28cpld management controller. This is + the base driver which provides common access methods for the + sub-drivers. + endmenu diff --git a/drivers/misc/Makefile b/drivers/misc/Makefile index bca7b24e99..f22eff601a 100644 --- a/drivers/misc/Makefile +++ b/drivers/misc/Makefile @@ -82,3 +82,4 @@ obj-$(CONFIG_MICROCHIP_FLEXCOM) += microchip_flexcom.o obj-$(CONFIG_K3_AVS0) += k3_avs.o obj-$(CONFIG_ESM_K3) += k3_esm.o obj-$(CONFIG_ESM_PMIC) += esm_pmic.o +obj-$(CONFIG_SL28CPLD) += sl28cpld.o diff --git a/drivers/misc/sl28cpld.c b/drivers/misc/sl28cpld.c new file mode 100644 index 0000000000..01ef1c6178 --- /dev/null +++ b/drivers/misc/sl28cpld.c @@ -0,0 +1,105 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright (c) 2021 Michael Walle <michael@walle.cc> + */ + +#include <common.h> +#include <dm.h> +#include <i2c.h> + +struct sl28cpld_child_plat { + uint offset; +}; + +/* + * The access methods works either with the first argument being a child + * device or with the MFD device itself. + */ +static int sl28cpld_read_child(struct udevice *dev, uint offset) +{ + struct sl28cpld_child_plat *plat = dev_get_parent_plat(dev); + struct udevice *mfd = dev_get_parent(dev); + + return dm_i2c_reg_read(mfd, offset + plat->offset); +} + +int sl28cpld_read(struct udevice *dev, uint offset) +{ + if (dev->driver == DM_DRIVER_GET(sl28cpld)) + return dm_i2c_reg_read(dev, offset); + else + return sl28cpld_read_child(dev, offset); +} + +static int sl28cpld_write_child(struct udevice *dev, uint offset, + uint8_t value) +{ + struct sl28cpld_child_plat *plat = dev_get_parent_plat(dev); + struct udevice *mfd = dev_get_parent(dev); + + return dm_i2c_reg_write(mfd, offset + plat->offset, value); +} + +int sl28cpld_write(struct udevice *dev, uint offset, uint8_t value) +{ + if (dev->driver == DM_DRIVER_GET(sl28cpld)) + return dm_i2c_reg_write(dev, offset, value); + else + return sl28cpld_write_child(dev, offset, value); +} + +int sl28cpld_update(struct udevice *dev, uint offset, uint8_t clear, + uint8_t set) +{ + int val; + + val = sl28cpld_read(dev, offset); + if (val < 0) + return val; + + val &= ~clear; + val |= set; + + return sl28cpld_write(dev, offset, val); +} + +static int sl28cpld_probe(struct udevice *dev) +{ + i2c_set_chip_flags(dev, DM_I2C_CHIP_RD_ADDRESS | + DM_I2C_CHIP_WR_ADDRESS); + + return 0; +} + +static int sl28cpld_child_post_bind(struct udevice *dev) +{ + struct sl28cpld_child_plat *plat = dev_get_parent_plat(dev); + int offset; + + if (!dev_has_ofnode(dev)) + return 0; + + offset = dev_read_u32_default(dev, "reg", -1); + if (offset == -1) + return -EINVAL; + + plat->offset = offset; + + return 0; +} + +static const struct udevice_id sl28cpld_ids[] = { + { .compatible = "kontron,sl28cpld" }, + {} +}; + +U_BOOT_DRIVER(sl28cpld) = { + .name = "sl28cpld", + .id = UCLASS_NOP, + .of_match = sl28cpld_ids, + .probe = sl28cpld_probe, + .bind = dm_scan_fdt_dev, + .flags = DM_FLAG_PRE_RELOC, + .per_child_plat_auto = sizeof(struct sl28cpld_child_plat), + .child_post_bind = sl28cpld_child_post_bind, +}; diff --git a/drivers/mmc/rockchip_sdhci.c b/drivers/mmc/rockchip_sdhci.c index 278473899c..b91df05de4 100644 --- a/drivers/mmc/rockchip_sdhci.c +++ b/drivers/mmc/rockchip_sdhci.c @@ -90,9 +90,33 @@ struct rockchip_sdhc { }; struct sdhci_data { - int (*emmc_set_clock)(struct sdhci_host *host, unsigned int clock); int (*emmc_phy_init)(struct udevice *dev); int (*get_phy)(struct udevice *dev); + + /** + * set_control_reg() - Set SDHCI control registers + * + * This is the set_control_reg() SDHCI operation that should be + * used for the hardware this driver data is associated with. + * Normally, this is used to set up control registers for + * voltage level and UHS speed mode. + * + * @host: SDHCI host structure + */ + void (*set_control_reg)(struct sdhci_host *host); + + /** + * set_ios_post() - Host specific hook after set_ios() calls + * + * This is the set_ios_post() SDHCI operation that should be + * used for the hardware this driver data is associated with. + * Normally, this is a hook that is called after sdhci_set_ios() + * that does any necessary host-specific configuration. + * + * @host: SDHCI host structure + * Return: 0 if successful, -ve on error + */ + int (*set_ios_post)(struct sdhci_host *host); }; static int rk3399_emmc_phy_init(struct udevice *dev) @@ -182,15 +206,28 @@ static int rk3399_emmc_get_phy(struct udevice *dev) return 0; } -static int rk3399_sdhci_emmc_set_clock(struct sdhci_host *host, unsigned int clock) +static void rk3399_sdhci_set_control_reg(struct sdhci_host *host) { struct rockchip_sdhc *priv = container_of(host, struct rockchip_sdhc, host); + struct mmc *mmc = host->mmc; + uint clock = mmc->tran_speed; int cycle_phy = host->clock != clock && clock > EMMC_MIN_FREQ; if (cycle_phy) rk3399_emmc_phy_power_off(priv->phy); - sdhci_set_clock(host->mmc, clock); + sdhci_set_control_reg(host); +}; + +static int rk3399_sdhci_set_ios_post(struct sdhci_host *host) +{ + struct rockchip_sdhc *priv = container_of(host, struct rockchip_sdhc, host); + struct mmc *mmc = host->mmc; + uint clock = mmc->tran_speed; + int cycle_phy = host->clock != clock && clock > EMMC_MIN_FREQ; + + if (!clock) + clock = mmc->clock; if (cycle_phy) rk3399_emmc_phy_power_on(priv->phy, clock); @@ -269,10 +306,8 @@ static int rk3568_emmc_get_phy(struct udevice *dev) return 0; } -static int rockchip_sdhci_set_ios_post(struct sdhci_host *host) +static int rk3568_sdhci_set_ios_post(struct sdhci_host *host) { - struct rockchip_sdhc *priv = container_of(host, struct rockchip_sdhc, host); - struct sdhci_data *data = (struct sdhci_data *)dev_get_driver_data(priv->dev); struct mmc *mmc = host->mmc; uint clock = mmc->tran_speed; u32 reg; @@ -280,8 +315,7 @@ static int rockchip_sdhci_set_ios_post(struct sdhci_host *host) if (!clock) clock = mmc->clock; - if (data->emmc_set_clock) - data->emmc_set_clock(host, clock); + rk3568_sdhci_emmc_set_clock(host, clock); if (mmc->selected_mode == MMC_HS_400 || mmc->selected_mode == MMC_HS_400_ES) { reg = sdhci_readw(host, SDHCI_HOST_CONTROL2); @@ -295,6 +329,26 @@ static int rockchip_sdhci_set_ios_post(struct sdhci_host *host) return 0; } +static void rockchip_sdhci_set_control_reg(struct sdhci_host *host) +{ + struct rockchip_sdhc *priv = container_of(host, struct rockchip_sdhc, host); + struct sdhci_data *data = (struct sdhci_data *)dev_get_driver_data(priv->dev); + + if (data->set_control_reg) + data->set_control_reg(host); +} + +static int rockchip_sdhci_set_ios_post(struct sdhci_host *host) +{ + struct rockchip_sdhc *priv = container_of(host, struct rockchip_sdhc, host); + struct sdhci_data *data = (struct sdhci_data *)dev_get_driver_data(priv->dev); + + if (data->set_ios_post) + return data->set_ios_post(host); + + return 0; +} + static int rockchip_sdhci_execute_tuning(struct mmc *mmc, u8 opcode) { struct sdhci_host *host = dev_get_priv(mmc->dev); @@ -358,6 +412,7 @@ static int rockchip_sdhci_execute_tuning(struct mmc *mmc, u8 opcode) static struct sdhci_ops rockchip_sdhci_ops = { .set_ios_post = rockchip_sdhci_set_ios_post, .platform_execute_tuning = &rockchip_sdhci_execute_tuning, + .set_control_reg = rockchip_sdhci_set_control_reg, }; static int rockchip_sdhci_probe(struct udevice *dev) @@ -436,15 +491,16 @@ static int rockchip_sdhci_bind(struct udevice *dev) } static const struct sdhci_data rk3399_data = { - .emmc_set_clock = rk3399_sdhci_emmc_set_clock, .get_phy = rk3399_emmc_get_phy, .emmc_phy_init = rk3399_emmc_phy_init, + .set_control_reg = rk3399_sdhci_set_control_reg, + .set_ios_post = rk3399_sdhci_set_ios_post, }; static const struct sdhci_data rk3568_data = { - .emmc_set_clock = rk3568_sdhci_emmc_set_clock, .get_phy = rk3568_emmc_get_phy, .emmc_phy_init = rk3568_emmc_phy_init, + .set_ios_post = rk3568_sdhci_set_ios_post, }; static const struct udevice_id sdhci_ids[] = { diff --git a/drivers/mtd/spi/spi-nor-ids.c b/drivers/mtd/spi/spi-nor-ids.c index b551ebd75e..763bab04c6 100644 --- a/drivers/mtd/spi/spi-nor-ids.c +++ b/drivers/mtd/spi/spi-nor-ids.c @@ -119,7 +119,7 @@ const struct flash_info spi_nor_ids[] = { }, { INFO("gd25lq128", 0xc86018, 0, 64 * 1024, 256, - SECT_4K | SPI_NOR_DUAL_READ | + SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ | SPI_NOR_HAS_LOCK | SPI_NOR_HAS_TB) }, { diff --git a/drivers/pci/pci_mvebu.c b/drivers/pci/pci_mvebu.c index 5a0a59a8b9..f07669374d 100644 --- a/drivers/pci/pci_mvebu.c +++ b/drivers/pci/pci_mvebu.c @@ -30,37 +30,25 @@ #include <linux/sizes.h> /* PCIe unit register offsets */ -#define SELECT(x, n) ((x >> n) & 1UL) - -#define PCIE_DEV_ID_OFF 0x0000 -#define PCIE_CMD_OFF 0x0004 -#define PCIE_DEV_REV_OFF 0x0008 -#define PCIE_BAR_LO_OFF(n) (0x0010 + ((n) << 3)) -#define PCIE_BAR_HI_OFF(n) (0x0014 + ((n) << 3)) -#define PCIE_EXP_ROM_BAR_OFF 0x0030 -#define PCIE_CAPAB_OFF 0x0060 -#define PCIE_CTRL_STAT_OFF 0x0068 -#define PCIE_HEADER_LOG_4_OFF 0x0128 -#define PCIE_BAR_CTRL_OFF(n) (0x1804 + (((n) - 1) * 4)) -#define PCIE_WIN04_CTRL_OFF(n) (0x1820 + ((n) << 4)) -#define PCIE_WIN04_BASE_OFF(n) (0x1824 + ((n) << 4)) -#define PCIE_WIN04_REMAP_OFF(n) (0x182c + ((n) << 4)) -#define PCIE_WIN5_CTRL_OFF 0x1880 -#define PCIE_WIN5_BASE_OFF 0x1884 -#define PCIE_WIN5_REMAP_OFF 0x188c -#define PCIE_CONF_ADDR_OFF 0x18f8 -#define PCIE_CONF_DATA_OFF 0x18fc -#define PCIE_MASK_OFF 0x1910 -#define PCIE_MASK_ENABLE_INTS (0xf << 24) -#define PCIE_CTRL_OFF 0x1a00 -#define PCIE_CTRL_X1_MODE BIT(0) -#define PCIE_CTRL_RC_MODE BIT(1) -#define PCIE_STAT_OFF 0x1a04 -#define PCIE_STAT_BUS (0xff << 8) -#define PCIE_STAT_DEV (0x1f << 16) -#define PCIE_STAT_LINK_DOWN BIT(0) -#define PCIE_DEBUG_CTRL 0x1a60 -#define PCIE_DEBUG_SOFT_RESET BIT(20) +#define MVPCIE_ROOT_PORT_PCI_CFG_OFF 0x0000 +#define MVPCIE_ROOT_PORT_PCI_EXP_OFF 0x0060 +#define MVPCIE_BAR_LO_OFF(n) (0x0010 + ((n) << 3)) +#define MVPCIE_BAR_HI_OFF(n) (0x0014 + ((n) << 3)) +#define MVPCIE_BAR_CTRL_OFF(n) (0x1804 + (((n) - 1) * 4)) +#define MVPCIE_WIN04_CTRL_OFF(n) (0x1820 + ((n) << 4)) +#define MVPCIE_WIN04_BASE_OFF(n) (0x1824 + ((n) << 4)) +#define MVPCIE_WIN04_REMAP_OFF(n) (0x182c + ((n) << 4)) +#define MVPCIE_WIN5_CTRL_OFF 0x1880 +#define MVPCIE_WIN5_BASE_OFF 0x1884 +#define MVPCIE_WIN5_REMAP_OFF 0x188c +#define MVPCIE_CONF_ADDR_OFF 0x18f8 +#define MVPCIE_CONF_DATA_OFF 0x18fc +#define MVPCIE_CTRL_OFF 0x1a00 +#define MVPCIE_CTRL_RC_MODE BIT(1) +#define MVPCIE_STAT_OFF 0x1a04 +#define MVPCIE_STAT_BUS (0xff << 8) +#define MVPCIE_STAT_DEV (0x1f << 16) +#define MVPCIE_STAT_LINK_DOWN BIT(0) #define LINK_WAIT_RETRIES 100 #define LINK_WAIT_TIMEOUT 1000 @@ -77,7 +65,6 @@ struct mvebu_pcie { u32 lane; bool is_x4; int devfn; - u32 lane_mask; int sec_busno; char name[16]; unsigned int mem_target; @@ -90,8 +77,8 @@ struct mvebu_pcie { static inline bool mvebu_pcie_link_up(struct mvebu_pcie *pcie) { u32 val; - val = readl(pcie->base + PCIE_STAT_OFF); - return !(val & PCIE_STAT_LINK_DOWN); + val = readl(pcie->base + MVPCIE_STAT_OFF); + return !(val & MVPCIE_STAT_LINK_DOWN); } static void mvebu_pcie_wait_for_link(struct mvebu_pcie *pcie) @@ -115,20 +102,20 @@ static void mvebu_pcie_set_local_bus_nr(struct mvebu_pcie *pcie, int busno) { u32 stat; - stat = readl(pcie->base + PCIE_STAT_OFF); - stat &= ~PCIE_STAT_BUS; + stat = readl(pcie->base + MVPCIE_STAT_OFF); + stat &= ~MVPCIE_STAT_BUS; stat |= busno << 8; - writel(stat, pcie->base + PCIE_STAT_OFF); + writel(stat, pcie->base + MVPCIE_STAT_OFF); } static void mvebu_pcie_set_local_dev_nr(struct mvebu_pcie *pcie, int devno) { u32 stat; - stat = readl(pcie->base + PCIE_STAT_OFF); - stat &= ~PCIE_STAT_DEV; + stat = readl(pcie->base + MVPCIE_STAT_OFF); + stat &= ~MVPCIE_STAT_DEV; stat |= devno << 16; - writel(stat, pcie->base + PCIE_STAT_OFF); + writel(stat, pcie->base + MVPCIE_STAT_OFF); } static inline struct mvebu_pcie *hose_to_pcie(struct pci_controller *hose) @@ -198,18 +185,18 @@ static int mvebu_pcie_read_config(const struct udevice *bus, pci_dev_t bdf, addr = PCI_CONF1_EXT_ADDRESS(busno, PCI_DEV(bdf), PCI_FUNC(bdf), offset); /* write address */ - writel(addr, pcie->base + PCIE_CONF_ADDR_OFF); + writel(addr, pcie->base + MVPCIE_CONF_ADDR_OFF); /* read data */ switch (size) { case PCI_SIZE_8: - data = readb(pcie->base + PCIE_CONF_DATA_OFF + (offset & 3)); + data = readb(pcie->base + MVPCIE_CONF_DATA_OFF + (offset & 3)); break; case PCI_SIZE_16: - data = readw(pcie->base + PCIE_CONF_DATA_OFF + (offset & 2)); + data = readw(pcie->base + MVPCIE_CONF_DATA_OFF + (offset & 2)); break; case PCI_SIZE_32: - data = readl(pcie->base + PCIE_CONF_DATA_OFF); + data = readl(pcie->base + MVPCIE_CONF_DATA_OFF); break; default: return -EINVAL; @@ -289,18 +276,18 @@ static int mvebu_pcie_write_config(struct udevice *bus, pci_dev_t bdf, addr = PCI_CONF1_EXT_ADDRESS(busno, PCI_DEV(bdf), PCI_FUNC(bdf), offset); /* write address */ - writel(addr, pcie->base + PCIE_CONF_ADDR_OFF); + writel(addr, pcie->base + MVPCIE_CONF_ADDR_OFF); /* write data */ switch (size) { case PCI_SIZE_8: - writeb(value, pcie->base + PCIE_CONF_DATA_OFF + (offset & 3)); + writeb(value, pcie->base + MVPCIE_CONF_DATA_OFF + (offset & 3)); break; case PCI_SIZE_16: - writew(value, pcie->base + PCIE_CONF_DATA_OFF + (offset & 2)); + writew(value, pcie->base + MVPCIE_CONF_DATA_OFF + (offset & 2)); break; case PCI_SIZE_32: - writel(value, pcie->base + PCIE_CONF_DATA_OFF); + writel(value, pcie->base + MVPCIE_CONF_DATA_OFF); break; default: return -EINVAL; @@ -324,20 +311,20 @@ static void mvebu_pcie_setup_wins(struct mvebu_pcie *pcie) /* First, disable and clear BARs and windows. */ for (i = 1; i < 3; i++) { - writel(0, pcie->base + PCIE_BAR_CTRL_OFF(i)); - writel(0, pcie->base + PCIE_BAR_LO_OFF(i)); - writel(0, pcie->base + PCIE_BAR_HI_OFF(i)); + writel(0, pcie->base + MVPCIE_BAR_CTRL_OFF(i)); + writel(0, pcie->base + MVPCIE_BAR_LO_OFF(i)); + writel(0, pcie->base + MVPCIE_BAR_HI_OFF(i)); } for (i = 0; i < 5; i++) { - writel(0, pcie->base + PCIE_WIN04_CTRL_OFF(i)); - writel(0, pcie->base + PCIE_WIN04_BASE_OFF(i)); - writel(0, pcie->base + PCIE_WIN04_REMAP_OFF(i)); + writel(0, pcie->base + MVPCIE_WIN04_CTRL_OFF(i)); + writel(0, pcie->base + MVPCIE_WIN04_BASE_OFF(i)); + writel(0, pcie->base + MVPCIE_WIN04_REMAP_OFF(i)); } - writel(0, pcie->base + PCIE_WIN5_CTRL_OFF); - writel(0, pcie->base + PCIE_WIN5_BASE_OFF); - writel(0, pcie->base + PCIE_WIN5_REMAP_OFF); + writel(0, pcie->base + MVPCIE_WIN5_CTRL_OFF); + writel(0, pcie->base + MVPCIE_WIN5_BASE_OFF); + writel(0, pcie->base + MVPCIE_WIN5_REMAP_OFF); /* Setup windows for DDR banks. Count total DDR size on the fly. */ size = 0; @@ -345,12 +332,12 @@ static void mvebu_pcie_setup_wins(struct mvebu_pcie *pcie) const struct mbus_dram_window *cs = dram->cs + i; writel(cs->base & 0xffff0000, - pcie->base + PCIE_WIN04_BASE_OFF(i)); - writel(0, pcie->base + PCIE_WIN04_REMAP_OFF(i)); + pcie->base + MVPCIE_WIN04_BASE_OFF(i)); + writel(0, pcie->base + MVPCIE_WIN04_REMAP_OFF(i)); writel(((cs->size - 1) & 0xffff0000) | (cs->mbus_attr << 8) | (dram->mbus_dram_target_id << 4) | 1, - pcie->base + PCIE_WIN04_CTRL_OFF(i)); + pcie->base + MVPCIE_WIN04_CTRL_OFF(i)); size += cs->size; } @@ -360,14 +347,14 @@ static void mvebu_pcie_setup_wins(struct mvebu_pcie *pcie) size = 1 << fls(size); /* Setup BAR[1] to all DRAM banks. */ - writel(dram->cs[0].base | 0xc, pcie->base + PCIE_BAR_LO_OFF(1)); - writel(0, pcie->base + PCIE_BAR_HI_OFF(1)); + writel(dram->cs[0].base | 0xc, pcie->base + MVPCIE_BAR_LO_OFF(1)); + writel(0, pcie->base + MVPCIE_BAR_HI_OFF(1)); writel(((size - 1) & 0xffff0000) | 0x1, - pcie->base + PCIE_BAR_CTRL_OFF(1)); + pcie->base + MVPCIE_BAR_CTRL_OFF(1)); /* Setup BAR[0] to internal registers. */ - writel(pcie->intregs, pcie->base + PCIE_BAR_LO_OFF(0)); - writel(0, pcie->base + PCIE_BAR_HI_OFF(0)); + writel(pcie->intregs, pcie->base + MVPCIE_BAR_LO_OFF(0)); + writel(0, pcie->base + MVPCIE_BAR_HI_OFF(0)); } /* Only enable PCIe link, do not setup it */ @@ -406,9 +393,9 @@ static void mvebu_pcie_setup_link(struct mvebu_pcie *pcie) u32 reg; /* Setup PCIe controller to Root Complex mode */ - reg = readl(pcie->base + PCIE_CTRL_OFF); - reg |= PCIE_CTRL_RC_MODE; - writel(reg, pcie->base + PCIE_CTRL_OFF); + reg = readl(pcie->base + MVPCIE_CTRL_OFF); + reg |= MVPCIE_CTRL_RC_MODE; + writel(reg, pcie->base + MVPCIE_CTRL_OFF); /* * Set Maximum Link Width to X1 or X4 in Root Port's PCIe Link @@ -417,10 +404,10 @@ static void mvebu_pcie_setup_link(struct mvebu_pcie *pcie) * be set to number of SerDes PCIe lanes (1 or 4). If this register is * not set correctly then link with endpoint card is not established. */ - reg = readl(pcie->base + PCIE_CAPAB_OFF + PCI_EXP_LNKCAP); + reg = readl(pcie->base + MVPCIE_ROOT_PORT_PCI_EXP_OFF + PCI_EXP_LNKCAP); reg &= ~PCI_EXP_LNKCAP_MLW; reg |= (pcie->is_x4 ? 4 : 1) << 4; - writel(reg, pcie->base + PCIE_CAPAB_OFF + PCI_EXP_LNKCAP); + writel(reg, pcie->base + MVPCIE_ROOT_PORT_PCI_EXP_OFF + PCI_EXP_LNKCAP); } static int mvebu_pcie_probe(struct udevice *dev) @@ -443,7 +430,7 @@ static int mvebu_pcie_probe(struct udevice *dev) * have the same format in Marvell's specification as in PCIe * specification, but their meaning is totally different and they do * different things: they are aliased into internal mvebu registers - * (e.g. PCIE_BAR_LO_OFF) and these should not be changed or + * (e.g. MVPCIE_BAR_LO_OFF) and these should not be changed or * reconfigured by pci device drivers. * * So our driver converts Type 0 config space to Type 1 and reports @@ -451,10 +438,10 @@ static int mvebu_pcie_probe(struct udevice *dev) * Type 1 registers is redirected to the virtual cfgcache[] buffer, * which avoids changing unrelated registers. */ - reg = readl(pcie->base + PCIE_DEV_REV_OFF); + reg = readl(pcie->base + MVPCIE_ROOT_PORT_PCI_CFG_OFF + PCI_CLASS_REVISION); reg &= ~0xffffff00; reg |= (PCI_CLASS_BRIDGE_PCI << 8) << 8; - writel(reg, pcie->base + PCIE_DEV_REV_OFF); + writel(reg, pcie->base + MVPCIE_ROOT_PORT_PCI_CFG_OFF + PCI_CLASS_REVISION); /* * mvebu uses local bus number and local device number to determinate diff --git a/drivers/phy/cadence/phy-cadence-sierra.c b/drivers/phy/cadence/phy-cadence-sierra.c index d95d4b432a..fc5044fd5d 100644 --- a/drivers/phy/cadence/phy-cadence-sierra.c +++ b/drivers/phy/cadence/phy-cadence-sierra.c @@ -358,26 +358,10 @@ static inline int cdns_reset_deassert(struct reset_control *rst) return 0; } -static inline struct cdns_sierra_inst *phy_get_drvdata(struct phy *phy) +static int cdns_sierra_link_init(struct phy *gphy) { - struct cdns_sierra_phy *sp = dev_get_priv(phy->dev); - int index; - - if (phy->id >= SIERRA_MAX_LANES) - return NULL; - - for (index = 0; index < sp->nsubnodes; index++) { - if (phy->id == sp->phys[index]->mlane) - return sp->phys[index]; - } - - return NULL; -} - -static int cdns_sierra_phy_init(struct phy *gphy) -{ - struct cdns_sierra_inst *ins = phy_get_drvdata(gphy); - struct cdns_sierra_phy *phy = dev_get_priv(gphy->dev); + struct cdns_sierra_inst *ins = dev_get_priv(gphy->dev); + struct cdns_sierra_phy *phy = dev_get_priv(gphy->dev->parent); struct cdns_sierra_data *init_data = phy->init_data; struct cdns_sierra_vals *pma_cmn_vals, *pma_ln_vals; enum cdns_sierra_phy_type phy_type = ins->phy_type; @@ -443,10 +427,11 @@ static int cdns_sierra_phy_init(struct phy *gphy) return 0; } -static int cdns_sierra_phy_on(struct phy *gphy) +static int cdns_sierra_link_on(struct phy *gphy) { - struct cdns_sierra_inst *ins = phy_get_drvdata(gphy); - struct cdns_sierra_phy *sp = dev_get_priv(gphy->dev); + struct cdns_sierra_inst *ins = dev_get_priv(gphy->dev); + struct cdns_sierra_phy *sp = dev_get_priv(gphy->dev->parent); + struct udevice *dev = gphy->dev; u32 val; int ret; @@ -503,16 +488,16 @@ static int cdns_sierra_phy_on(struct phy *gphy) return ret; } -static int cdns_sierra_phy_off(struct phy *gphy) +static int cdns_sierra_link_off(struct phy *gphy) { - struct cdns_sierra_inst *ins = phy_get_drvdata(gphy); + struct cdns_sierra_inst *ins = dev_get_priv(gphy->dev); return reset_assert_bulk(ins->lnk_rst); } -static int cdns_sierra_phy_reset(struct phy *gphy) +static int cdns_sierra_link_reset(struct phy *gphy) { - struct cdns_sierra_phy *sp = dev_get_priv(gphy->dev); + struct cdns_sierra_phy *sp = dev_get_priv(gphy->dev->parent); reset_control_assert(sp->phy_rst); reset_control_deassert(sp->phy_rst); @@ -520,10 +505,10 @@ static int cdns_sierra_phy_reset(struct phy *gphy) }; static const struct phy_ops ops = { - .init = cdns_sierra_phy_init, - .power_on = cdns_sierra_phy_on, - .power_off = cdns_sierra_phy_off, - .reset = cdns_sierra_phy_reset, + .init = cdns_sierra_link_init, + .power_on = cdns_sierra_link_on, + .power_off = cdns_sierra_link_off, + .reset = cdns_sierra_link_reset, }; struct cdns_sierra_pll_mux_sel { @@ -580,7 +565,7 @@ static const struct clk_ops cdns_sierra_pll_mux_ops = { .set_parent = cdns_sierra_pll_mux_set_parent, }; -int cdns_sierra_pll_mux_probe(struct udevice *dev) +static int cdns_sierra_pll_mux_probe(struct udevice *dev) { struct cdns_sierra_pll_mux *priv = dev_get_priv(dev); struct cdns_sierra_phy *sp = dev_get_priv(dev->parent); @@ -1012,9 +997,8 @@ static int cdns_sierra_phy_get_resets(struct cdns_sierra_phy *sp, return 0; } -static int cdns_sierra_bind_link_nodes(struct cdns_sierra_phy *sp) +static int cdns_sierra_phy_bind(struct udevice *dev) { - struct udevice *dev = sp->dev; struct driver *link_drv; ofnode child; int rc; @@ -1079,6 +1063,7 @@ U_BOOT_DRIVER(sierra_phy_link) = { .name = "sierra_phy_link", .id = UCLASS_PHY, .probe = cdns_sierra_link_probe, + .ops = &ops, .priv_auto = sizeof(struct cdns_sierra_inst), }; @@ -1141,10 +1126,6 @@ static int cdns_sierra_phy_probe(struct udevice *dev) } sp->autoconf = dev_read_bool(dev, "cdns,autoconf"); - /* Binding link nodes as children to serdes */ - ret = cdns_sierra_bind_link_nodes(sp); - if (ret) - goto clk_disable; dev_info(dev, "sierra probed\n"); return 0; @@ -1971,10 +1952,10 @@ static const struct udevice_id cdns_sierra_id_table[] = { U_BOOT_DRIVER(sierra_phy_provider) = { .name = "cdns,sierra", - .id = UCLASS_PHY, + .id = UCLASS_MISC, .of_match = cdns_sierra_id_table, .probe = cdns_sierra_phy_probe, .remove = cdns_sierra_phy_remove, - .ops = &ops, + .bind = cdns_sierra_phy_bind, .priv_auto = sizeof(struct cdns_sierra_phy), }; diff --git a/drivers/phy/nop-phy.c b/drivers/phy/nop-phy.c index e2ee6e9206..d0904f4f07 100644 --- a/drivers/phy/nop-phy.c +++ b/drivers/phy/nop-phy.c @@ -45,11 +45,13 @@ static int nop_phy_init(struct phy *phy) #if CONFIG_IS_ENABLED(DM_GPIO) /* Take phy out of reset */ - ret = dm_gpio_set_value(&priv->reset_gpio, false); - if (ret) { - if (CONFIG_IS_ENABLED(CLK)) - clk_disable_bulk(&priv->bulk); - return ret; + if (dm_gpio_is_valid(&priv->reset_gpio)) { + ret = dm_gpio_set_value(&priv->reset_gpio, false); + if (ret) { + if (CONFIG_IS_ENABLED(CLK)) + clk_disable_bulk(&priv->bulk); + return ret; + } } #endif return 0; diff --git a/drivers/pinctrl/mvebu/pinctrl-armada-37xx.c b/drivers/pinctrl/mvebu/pinctrl-armada-37xx.c index 1cf1f06f10..e76ef153e6 100644 --- a/drivers/pinctrl/mvebu/pinctrl-armada-37xx.c +++ b/drivers/pinctrl/mvebu/pinctrl-armada-37xx.c @@ -162,11 +162,11 @@ static struct armada_37xx_pin_group armada_37xx_nb_groups[] = { PIN_GRP_GPIO("emmc_nb", 27, 9, BIT(2), "emmc"), PIN_GRP_GPIO_3("pwm0", 11, 1, BIT(3) | BIT(20), 0, BIT(20), BIT(3), "pwm", "led"), - PIN_GRP_GPIO_3("pwm1", 11, 1, BIT(4) | BIT(21), 0, BIT(21), BIT(4), + PIN_GRP_GPIO_3("pwm1", 12, 1, BIT(4) | BIT(21), 0, BIT(21), BIT(4), "pwm", "led"), - PIN_GRP_GPIO_3("pwm2", 11, 1, BIT(5) | BIT(22), 0, BIT(22), BIT(5), + PIN_GRP_GPIO_3("pwm2", 13, 1, BIT(5) | BIT(22), 0, BIT(22), BIT(5), "pwm", "led"), - PIN_GRP_GPIO_3("pwm3", 11, 1, BIT(6) | BIT(23), 0, BIT(23), BIT(6), + PIN_GRP_GPIO_3("pwm3", 14, 1, BIT(6) | BIT(23), 0, BIT(23), BIT(6), "pwm", "led"), PIN_GRP_GPIO("pmic1", 7, 1, BIT(7), "pmic"), PIN_GRP_GPIO("pmic0", 6, 1, BIT(8), "pmic"), diff --git a/drivers/rtc/armada38x.c b/drivers/rtc/armada38x.c index 2d264acf77..2af64e3912 100644 --- a/drivers/rtc/armada38x.c +++ b/drivers/rtc/armada38x.c @@ -121,7 +121,7 @@ static int armada38x_rtc_reset(struct udevice *dev) armada38x_rtc_write(0, rtc, RTC_CONF_TEST); mdelay(500); armada38x_rtc_write(0, rtc, RTC_TIME); - armada38x_rtc_write(BIT(0) | BIT(1), 0, RTC_STATUS); + armada38x_rtc_write(BIT(0) | BIT(1), rtc, RTC_STATUS); } return 0; diff --git a/drivers/serial/serial-uclass.c b/drivers/serial/serial-uclass.c index 362cedd955..f30f352bd7 100644 --- a/drivers/serial/serial-uclass.c +++ b/drivers/serial/serial-uclass.c @@ -66,7 +66,8 @@ static int serial_check_stdout(const void *blob, struct udevice **devp) */ if (node > 0 && !lists_bind_fdt(gd->dm_root, offset_to_ofnode(node), devp, NULL, false)) { - if (!device_probe(*devp)) + if (device_get_uclass_id(*devp) == UCLASS_SERIAL && + !device_probe(*devp)) return 0; } diff --git a/drivers/spi/Kconfig b/drivers/spi/Kconfig index 0a6a85f9c4..423a757141 100644 --- a/drivers/spi/Kconfig +++ b/drivers/spi/Kconfig @@ -185,6 +185,12 @@ config ICH_SPI access the SPI NOR flash on platforms embedding this Intel ICH IP core. +config IPROC_QSPI + bool "Broadcom iProc QSPI Flash Controller driver" + help + Enable Broadcom iProc QSPI Flash Controller driver. + This driver can be used to access the SPI NOR flash. + config KIRKWOOD_SPI bool "Marvell Kirkwood SPI Driver" help diff --git a/drivers/spi/Makefile b/drivers/spi/Makefile index bea746f3e3..7f43f843ca 100644 --- a/drivers/spi/Makefile +++ b/drivers/spi/Makefile @@ -34,6 +34,7 @@ obj-$(CONFIG_FSL_DSPI) += fsl_dspi.o obj-$(CONFIG_FSL_ESPI) += fsl_espi.o obj-$(CONFIG_SYNQUACER_SPI) += spi-synquacer.o obj-$(CONFIG_ICH_SPI) += ich.o +obj-$(CONFIG_IPROC_QSPI) += iproc_qspi.o obj-$(CONFIG_KIRKWOOD_SPI) += kirkwood_spi.o obj-$(CONFIG_MESON_SPIFC) += meson_spifc.o obj-$(CONFIG_MPC8XX_SPI) += mpc8xx_spi.o diff --git a/drivers/spi/cadence_qspi.c b/drivers/spi/cadence_qspi.c index d1b3808c4d..db680618ee 100644 --- a/drivers/spi/cadence_qspi.c +++ b/drivers/spi/cadence_qspi.c @@ -201,11 +201,9 @@ static int cadence_spi_probe(struct udevice *bus) } } - ret = reset_get_bulk(bus, &priv->resets); - if (ret) - dev_warn(bus, "Can't get reset: %d\n", ret); - else - reset_deassert_bulk(&priv->resets); + priv->resets = devm_reset_bulk_get_optional(bus); + if (priv->resets) + reset_deassert_bulk(priv->resets); if (!priv->qspi_is_init) { cadence_qspi_apb_controller_init(plat); @@ -220,8 +218,12 @@ static int cadence_spi_probe(struct udevice *bus) static int cadence_spi_remove(struct udevice *dev) { struct cadence_spi_priv *priv = dev_get_priv(dev); + int ret = 0; + + if (priv->resets) + ret = reset_release_bulk(priv->resets); - return reset_release_bulk(&priv->resets); + return ret; } static int cadence_spi_set_mode(struct udevice *bus, uint mode) diff --git a/drivers/spi/cadence_qspi.h b/drivers/spi/cadence_qspi.h index 49b401168f..19345cac5a 100644 --- a/drivers/spi/cadence_qspi.h +++ b/drivers/spi/cadence_qspi.h @@ -56,7 +56,7 @@ struct cadence_spi_priv { unsigned int qspi_calibrated_cs; unsigned int previous_hz; - struct reset_ctl_bulk resets; + struct reset_ctl_bulk *resets; }; /* Functions call declaration */ diff --git a/drivers/spi/designware_spi.c b/drivers/spi/designware_spi.c index 742121140d..fc22f540fe 100644 --- a/drivers/spi/designware_spi.c +++ b/drivers/spi/designware_spi.c @@ -572,7 +572,7 @@ static int dw_spi_exec_op(struct spi_slave *slave, const struct spi_mem_op *op) int pos, i, ret = 0; struct udevice *bus = slave->dev->parent; struct dw_spi_priv *priv = dev_get_priv(bus); - u8 op_len = sizeof(op->cmd.opcode) + op->addr.nbytes + op->dummy.nbytes; + u8 op_len = op->cmd.nbytes + op->addr.nbytes + op->dummy.nbytes; u8 op_buf[op_len]; u32 cr0; diff --git a/drivers/spi/iproc_qspi.c b/drivers/spi/iproc_qspi.c new file mode 100644 index 0000000000..b5c274314b --- /dev/null +++ b/drivers/spi/iproc_qspi.c @@ -0,0 +1,576 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright 2020-2021 Broadcom + */ + +#include <common.h> +#include <dm.h> +#include <spi.h> +#include <spi-mem.h> +#include <asm/io.h> +#include <linux/delay.h> +#include <linux/err.h> +#include <linux/iopoll.h> +#include <linux/log2.h> + +/* Delay required to change the mode of operation */ +#define BUSY_DELAY_US 1 +#define BUSY_TIMEOUT_US 200000 +#define DWORD_ALIGNED(a) (!(((ulong)(a)) & 3)) + +/* Chip attributes */ +#define QSPI_AXI_CLK 175000000 +#define SPBR_MIN 8U +#define SPBR_MAX 255U +#define NUM_CDRAM 16U + +#define CDRAM_PCS0 2 +#define CDRAM_CONT BIT(7) +#define CDRAM_BITS_EN BIT(6) +#define CDRAM_QUAD_MODE BIT(8) +#define CDRAM_RBIT_INPUT BIT(10) +#define MSPI_SPE BIT(6) +#define MSPI_CONT_AFTER_CMD BIT(7) +#define MSPI_MSTR BIT(7) + +/* Register fields */ +#define MSPI_SPCR0_MSB_BITS_8 0x00000020 +#define BSPI_RAF_CONTROL_START_MASK 0x00000001 +#define BSPI_RAF_STATUS_SESSION_BUSY_MASK 0x00000001 +#define BSPI_RAF_STATUS_FIFO_EMPTY_MASK 0x00000002 +#define BSPI_STRAP_OVERRIDE_DATA_QUAD_SHIFT 3 +#define BSPI_STRAP_OVERRIDE_4BYTE_SHIFT 2 +#define BSPI_STRAP_OVERRIDE_DATA_DUAL_SHIFT 1 +#define BSPI_STRAP_OVERRIDE_SHIFT 0 +#define BSPI_BPC_DATA_SHIFT 0 +#define BSPI_BPC_MODE_SHIFT 8 +#define BSPI_BPC_ADDR_SHIFT 16 +#define BSPI_BPC_CMD_SHIFT 24 +#define BSPI_BPP_ADDR_SHIFT 16 + +/* MSPI registers */ +#define MSPI_SPCR0_LSB_REG 0x000 +#define MSPI_SPCR0_MSB_REG 0x004 +#define MSPI_SPCR1_LSB_REG 0x008 +#define MSPI_SPCR1_MSB_REG 0x00c +#define MSPI_NEWQP_REG 0x010 +#define MSPI_ENDQP_REG 0x014 +#define MSPI_SPCR2_REG 0x018 +#define MSPI_STATUS_REG 0x020 +#define MSPI_CPTQP_REG 0x024 +#define MSPI_TX_REG 0x040 +#define MSPI_RX_REG 0x0c0 +#define MSPI_CDRAM_REG 0x140 +#define MSPI_WRITE_LOCK_REG 0x180 +#define MSPI_DISABLE_FLUSH_GEN_REG 0x184 + +/* BSPI registers */ +#define BSPI_REVISION_ID_REG 0x000 +#define BSPI_SCRATCH_REG 0x004 +#define BSPI_MAST_N_BOOT_CTRL_REG 0x008 +#define BSPI_BUSY_STATUS_REG 0x00c +#define BSPI_INTR_STATUS_REG 0x010 +#define BSPI_B0_STATUS_REG 0x014 +#define BSPI_B0_CTRL_REG 0x018 +#define BSPI_B1_STATUS_REG 0x01c +#define BSPI_B1_CTRL_REG 0x020 +#define BSPI_STRAP_OVERRIDE_CTRL_REG 0x024 +#define BSPI_FLEX_MODE_ENABLE_REG 0x028 +#define BSPI_BITS_PER_CYCLE_REG 0x02C +#define BSPI_BITS_PER_PHASE_REG 0x030 +#define BSPI_CMD_AND_MODE_BYTE_REG 0x034 +#define BSPI_FLASH_UPPER_ADDR_BYTE_REG 0x038 +#define BSPI_XOR_VALUE_REG 0x03C +#define BSPI_XOR_ENABLE_REG 0x040 +#define BSPI_PIO_MODE_ENABLE_REG 0x044 +#define BSPI_PIO_IODIR_REG 0x048 +#define BSPI_PIO_DATA_REG 0x04C + +/* RAF registers */ +#define BSPI_RAF_START_ADDRESS_REG 0x00 +#define BSPI_RAF_NUM_WORDS_REG 0x04 +#define BSPI_RAF_CTRL_REG 0x08 +#define BSPI_RAF_FULLNESS_REG 0x0C +#define BSPI_RAF_WATERMARK_REG 0x10 +#define BSPI_RAF_STATUS_REG 0x14 +#define BSPI_RAF_READ_DATA_REG 0x18 +#define BSPI_RAF_WORD_CNT_REG 0x1C +#define BSPI_RAF_CURR_ADDR_REG 0x20 + +#define XFER_DUAL BIT(30) +#define XFER_QUAD BIT(31) + +#define FLUSH_BIT BIT(0) +#define MAST_N_BOOT_BIT BIT(0) +#define WRITE_LOCK_BIT BIT(0) + +#define CEIL(m, n) (((m) + (n) - 1) / (n)) +#define UPPER_BYTE_MASK 0xFF000000 +#define SIZE_16MB 0x001000000 + +/* + * struct bcmspi_priv - qspi private structure + * + * @bspi_addr: bspi read address + * @bspi_4byte_addr: bspi 4 byte address mode + * @mspi: mspi registers block address + * @bspi: bspi registers block address + * @bspi_raf: bspi raf registers block address + */ +struct bcmspi_priv { + u32 bspi_addr; + bool bspi_4byte_addr; + fdt_addr_t mspi; + fdt_addr_t bspi; + fdt_addr_t bspi_raf; +}; + +/* BSPI mode */ + +static void bspi_flush_prefetch_buffers(struct bcmspi_priv *priv) +{ + writel(0, priv->bspi + BSPI_B0_CTRL_REG); + writel(0, priv->bspi + BSPI_B1_CTRL_REG); + writel(FLUSH_BIT, priv->bspi + BSPI_B0_CTRL_REG); + writel(FLUSH_BIT, priv->bspi + BSPI_B1_CTRL_REG); +} + +static int bspi_enable(struct bcmspi_priv *priv) +{ + /* Disable write lock */ + writel(0, priv->mspi + MSPI_WRITE_LOCK_REG); + /* Flush prefetch buffers */ + bspi_flush_prefetch_buffers(priv); + /* Switch to BSPI */ + writel(0, priv->bspi + BSPI_MAST_N_BOOT_CTRL_REG); + + return 0; +} + +static int bspi_disable(struct bcmspi_priv *priv) +{ + int ret; + uint val; + + if ((readl(priv->bspi + BSPI_MAST_N_BOOT_CTRL_REG) & 1) == 0) { + ret = readl_poll_timeout(priv->bspi + BSPI_BUSY_STATUS_REG, val, !(val & 1), + BUSY_TIMEOUT_US); + if (ret) { + printf("%s: Failed to disable bspi, device busy\n", __func__); + return ret; + } + + /* Switch to MSPI */ + writel(MAST_N_BOOT_BIT, priv->bspi + BSPI_MAST_N_BOOT_CTRL_REG); + udelay(BUSY_DELAY_US); + + val = readl(priv->bspi + BSPI_MAST_N_BOOT_CTRL_REG); + if (!(val & 1)) { + printf("%s: Failed to enable mspi\n", __func__); + return -EBUSY; + } + } + + /* Enable write lock */ + writel(WRITE_LOCK_BIT, priv->mspi + MSPI_WRITE_LOCK_REG); + + return 0; +} + +static int bspi_read_via_raf(struct bcmspi_priv *priv, u8 *rx, uint bytes) +{ + u32 status; + uint words; + int aligned; + int ret; + + /* + * Flush data from the previous session (unlikely) + * Read outstanding bits in the poll condition to empty FIFO + */ + ret = readl_poll_timeout(priv->bspi_raf + BSPI_RAF_STATUS_REG, + status, + (!readl(priv->bspi_raf + BSPI_RAF_READ_DATA_REG) && + status & BSPI_RAF_STATUS_FIFO_EMPTY_MASK) && + !(status & BSPI_RAF_STATUS_SESSION_BUSY_MASK), + BUSY_TIMEOUT_US); + if (ret) { + printf("%s: Failed to flush fifo\n", __func__); + return ret; + } + + /* Transfer is in words */ + words = CEIL(bytes, 4); + + /* Setup hardware */ + if (priv->bspi_4byte_addr) { + u32 val = priv->bspi_addr & UPPER_BYTE_MASK; + + if (val != readl(priv->bspi + BSPI_FLASH_UPPER_ADDR_BYTE_REG)) { + writel(val, priv->bspi + BSPI_FLASH_UPPER_ADDR_BYTE_REG); + bspi_flush_prefetch_buffers(priv); + } + } + + writel(priv->bspi_addr & ~UPPER_BYTE_MASK, priv->bspi_raf + BSPI_RAF_START_ADDRESS_REG); + writel(words, priv->bspi_raf + BSPI_RAF_NUM_WORDS_REG); + writel(0, priv->bspi_raf + BSPI_RAF_WATERMARK_REG); + + /* Start reading */ + writel(BSPI_RAF_CONTROL_START_MASK, priv->bspi_raf + BSPI_RAF_CTRL_REG); + aligned = DWORD_ALIGNED(rx); + while (bytes) { + status = readl(priv->bspi_raf + BSPI_RAF_STATUS_REG); + if (!(status & BSPI_RAF_STATUS_FIFO_EMPTY_MASK)) { + /* RAF is LE only, convert data to host endianness */ + u32 data = le32_to_cpu(readl(priv->bspi_raf + BSPI_RAF_READ_DATA_REG)); + + /* Check if we can use the whole word */ + if (aligned && bytes >= 4) { + *(u32 *)rx = data; + rx += 4; + bytes -= 4; + } else { + uint chunk = min(bytes, 4U); + + /* Read out bytes one by one */ + while (chunk) { + *rx++ = (u8)data; + data >>= 8; + chunk--; + bytes--; + } + } + + continue; + } + if (!(status & BSPI_RAF_STATUS_SESSION_BUSY_MASK)) { + /* FIFO is empty and the session is done */ + break; + } + } + + return 0; +} + +static int bspi_read(struct bcmspi_priv *priv, u8 *rx, uint bytes) +{ + int ret; + + /* Transfer data */ + while (bytes > 0) { + /* Special handing since RAF cannot go across 16MB boundary */ + uint trans = bytes; + /* Divide into multiple transfers if it goes across the 16MB boundary */ + if (priv->bspi_4byte_addr && (priv->bspi_addr >> 24) != + ((priv->bspi_addr + bytes) >> 24)) + trans = SIZE_16MB - (priv->bspi_addr & ~UPPER_BYTE_MASK); + + ret = bspi_read_via_raf(priv, rx, trans); + if (ret) + return ret; + + priv->bspi_addr += trans; + rx += trans; + bytes -= trans; + } + + bspi_flush_prefetch_buffers(priv); + return 0; +} + +static void bspi_set_flex_mode(struct bcmspi_priv *priv, const struct spi_mem_op *op) +{ + int bpp = (op->dummy.nbytes * 8) / op->dummy.buswidth; + int cmd = op->cmd.opcode; + int bpc = ilog2(op->data.buswidth) << BSPI_BPC_DATA_SHIFT | + ilog2(op->addr.buswidth) << BSPI_BPC_ADDR_SHIFT | + ilog2(op->cmd.buswidth) << BSPI_BPC_CMD_SHIFT; + int so = BIT(BSPI_STRAP_OVERRIDE_SHIFT) | + (op->data.buswidth > 1) << BSPI_STRAP_OVERRIDE_DATA_DUAL_SHIFT | + (op->addr.nbytes > 3) << BSPI_STRAP_OVERRIDE_4BYTE_SHIFT | + (op->data.buswidth > 3) << BSPI_STRAP_OVERRIDE_DATA_QUAD_SHIFT; + + /* Disable flex mode first */ + writel(0, priv->bspi + BSPI_FLEX_MODE_ENABLE_REG); + + /* Configure single, dual or quad mode */ + writel(bpc, priv->bspi + BSPI_BITS_PER_CYCLE_REG); + + /* Opcode */ + writel(cmd, priv->bspi + BSPI_CMD_AND_MODE_BYTE_REG); + + /* Count of dummy cycles */ + writel(bpp, priv->bspi + BSPI_BITS_PER_PHASE_REG); + + /* Enable 4-byte address */ + if (priv->bspi_4byte_addr) { + setbits_le32(priv->bspi + BSPI_BITS_PER_PHASE_REG, BIT(BSPI_BPP_ADDR_SHIFT)); + } else { + clrbits_le32(priv->bspi + BSPI_BITS_PER_PHASE_REG, BIT(BSPI_BPP_ADDR_SHIFT)); + writel(0, priv->bspi + BSPI_FLASH_UPPER_ADDR_BYTE_REG); + } + + /* Enable flex mode to take effect */ + writel(1, priv->bspi + BSPI_FLEX_MODE_ENABLE_REG); + + /* Flush prefetch buffers since 32MB window BSPI could be used */ + bspi_flush_prefetch_buffers(priv); + + /* Override the strap settings */ + writel(so, priv->bspi + BSPI_STRAP_OVERRIDE_CTRL_REG); +} + +static int bspi_exec_op(struct spi_slave *slave, const struct spi_mem_op *op) +{ + struct udevice *bus = dev_get_parent(slave->dev); + struct bcmspi_priv *priv = dev_get_priv(bus); + int ret = -ENOTSUPP; + + /* BSPI read */ + if (op->data.dir == SPI_MEM_DATA_IN && + op->data.nbytes && op->addr.nbytes) { + priv->bspi_4byte_addr = (op->addr.nbytes > 3); + priv->bspi_addr = op->addr.val; + bspi_set_flex_mode(priv, op); + ret = bspi_read(priv, op->data.buf.in, op->data.nbytes); + } + + return ret; +} + +static const struct spi_controller_mem_ops bspi_mem_ops = { + .exec_op = bspi_exec_op, +}; + +/* MSPI mode */ + +static int mspi_exec(struct bcmspi_priv *priv, uint bytes, const u8 *tx, u8 *rx, ulong flags) +{ + u32 cdr = CDRAM_PCS0 | CDRAM_CONT; + bool use_16bits = !(bytes & 1); + + if (flags & XFER_QUAD) { + cdr |= CDRAM_QUAD_MODE; + + if (!tx) + cdr |= CDRAM_RBIT_INPUT; + } + + while (bytes) { + uint chunk; + uint queues; + uint i; + uint val; + int ret; + + if (use_16bits) { + chunk = min(bytes, NUM_CDRAM * 2); + queues = (chunk + 1) / 2; + bytes -= chunk; + + /* Fill CDRAMs */ + for (i = 0; i < queues; i++) + writel(cdr | CDRAM_BITS_EN, priv->mspi + MSPI_CDRAM_REG + 4 * i); + + /* Fill TXRAMs */ + for (i = 0; i < chunk; i++) + writel(tx ? tx[i] : 0xff, priv->mspi + MSPI_TX_REG + 4 * i); + } else { + /* Determine how many bytes to process this time */ + chunk = min(bytes, NUM_CDRAM); + queues = chunk; + bytes -= chunk; + + /* Fill CDRAMs and TXRAMS */ + for (i = 0; i < chunk; i++) { + writel(cdr, priv->mspi + MSPI_CDRAM_REG + 4 * i); + writel(tx ? tx[i] : 0xff, priv->mspi + MSPI_TX_REG + 8 * i); + } + } + + /* Setup queue pointers */ + writel(0, priv->mspi + MSPI_NEWQP_REG); + writel(queues - 1, priv->mspi + MSPI_ENDQP_REG); + + /* Deassert CS if requested and it's the last transfer */ + if (bytes == 0 && (flags & SPI_XFER_END)) + clrbits_le32(priv->mspi + MSPI_CDRAM_REG + ((queues - 1) << 2), CDRAM_CONT); + + /* Kick off */ + writel(0, priv->mspi + MSPI_STATUS_REG); + if (bytes == 0 && (flags & SPI_XFER_END)) + writel(MSPI_SPE, priv->mspi + MSPI_SPCR2_REG); + else + writel(MSPI_SPE | MSPI_CONT_AFTER_CMD, + priv->mspi + MSPI_SPCR2_REG); + + ret = readl_poll_timeout(priv->mspi + MSPI_STATUS_REG, val, (val & 1), + BUSY_TIMEOUT_US); + if (ret) { + printf("%s: Failed to disable bspi, device busy\n", __func__); + return ret; + } + + /* Read data out */ + if (rx) { + if (use_16bits) { + for (i = 0; i < chunk; i++) + rx[i] = readl(priv->mspi + MSPI_RX_REG + 4 * i) & 0xff; + } else { + for (i = 0; i < chunk; i++) + rx[i] = readl(priv->mspi + MSPI_RX_REG + 8 * i + 4) & 0xff; + } + } + + /* Advance pointers */ + if (tx) + tx += chunk; + if (rx) + rx += chunk; + } + + return 0; +} + +static int mspi_xfer(struct udevice *dev, uint bitlen, const void *dout, void *din, ulong flags) +{ + struct udevice *bus = dev_get_parent(dev); + struct bcmspi_priv *priv = dev_get_priv(bus); + uint bytes; + int ret = 0; + + /* we can only transfer multiples of 8 bits */ + if (bitlen % 8) + return -EPROTONOSUPPORT; + + bytes = bitlen / 8; + + if (flags & SPI_XFER_BEGIN) { + /* Switch to MSPI */ + ret = bspi_disable(priv); + if (ret) + return ret; + } + + /* MSPI: Transfer */ + if (bytes) + ret = mspi_exec(priv, bytes, dout, din, flags); + + if (flags & SPI_XFER_END) { + /* Switch back to BSPI */ + ret = bspi_enable(priv); + if (ret) + return ret; + } + + return ret; +} + +/* iProc interface */ + +static int iproc_qspi_set_speed(struct udevice *bus, uint speed) +{ + struct bcmspi_priv *priv = dev_get_priv(bus); + uint spbr; + + /* MSPI: SCK configuration */ + spbr = (QSPI_AXI_CLK - 1) / (2 * speed) + 1; + writel(max(min(spbr, SPBR_MAX), SPBR_MIN), priv->mspi + MSPI_SPCR0_LSB_REG); + + return 0; +} + +static int iproc_qspi_set_mode(struct udevice *bus, uint mode) +{ + struct bcmspi_priv *priv = dev_get_priv(bus); + + /* MSPI: set master bit and mode */ + writel(MSPI_MSTR /* Master */ | (mode & 3), priv->mspi + MSPI_SPCR0_MSB_REG); + + return 0; +} + +static int iproc_qspi_claim_bus(struct udevice *dev) +{ + /* Nothing to do */ + return 0; +} + +static int iproc_qspi_release_bus(struct udevice *dev) +{ + struct udevice *bus = dev_get_parent(dev); + struct bcmspi_priv *priv = dev_get_priv(bus); + + /* Make sure no operation is in progress */ + writel(0, priv->mspi + MSPI_SPCR2_REG); + udelay(BUSY_DELAY_US); + + return 0; +} + +static int iproc_qspi_of_to_plat(struct udevice *bus) +{ + struct bcmspi_priv *priv = dev_get_priv(bus); + + priv->bspi = dev_read_addr_name(bus, "bspi"); + if (IS_ERR((void *)priv->bspi)) { + printf("%s: Failed to get bspi base address\n", __func__); + return PTR_ERR((void *)priv->bspi); + } + + priv->bspi_raf = dev_read_addr_name(bus, "bspi_raf"); + if (IS_ERR((void *)priv->bspi_raf)) { + printf("%s: Failed to get bspi_raf base address\n", __func__); + return PTR_ERR((void *)priv->bspi_raf); + } + + priv->mspi = dev_read_addr_name(bus, "mspi"); + if (IS_ERR((void *)priv->mspi)) { + printf("%s: Failed to get mspi base address\n", __func__); + return PTR_ERR((void *)priv->mspi); + } + + return 0; +} + +static int iproc_qspi_probe(struct udevice *bus) +{ + struct bcmspi_priv *priv = dev_get_priv(bus); + + /* configure mspi */ + writel(0, priv->mspi + MSPI_SPCR1_LSB_REG); + writel(0, priv->mspi + MSPI_SPCR1_MSB_REG); + writel(0, priv->mspi + MSPI_NEWQP_REG); + writel(0, priv->mspi + MSPI_ENDQP_REG); + writel(0, priv->mspi + MSPI_SPCR2_REG); + + /* configure bspi */ + bspi_enable(priv); + + return 0; +} + +static const struct dm_spi_ops iproc_qspi_ops = { + .claim_bus = iproc_qspi_claim_bus, + .release_bus = iproc_qspi_release_bus, + .xfer = mspi_xfer, + .set_speed = iproc_qspi_set_speed, + .set_mode = iproc_qspi_set_mode, + .mem_ops = &bspi_mem_ops, +}; + +static const struct udevice_id iproc_qspi_ids[] = { + { .compatible = "brcm,iproc-qspi" }, + { } +}; + +U_BOOT_DRIVER(iproc_qspi) = { + .name = "iproc_qspi", + .id = UCLASS_SPI, + .of_match = iproc_qspi_ids, + .ops = &iproc_qspi_ops, + .of_to_plat = iproc_qspi_of_to_plat, + .priv_auto = sizeof(struct bcmspi_priv), + .probe = iproc_qspi_probe, +}; diff --git a/drivers/watchdog/Kconfig b/drivers/watchdog/Kconfig index 1f3ca99f9a..5b614cfa6e 100644 --- a/drivers/watchdog/Kconfig +++ b/drivers/watchdog/Kconfig @@ -267,6 +267,13 @@ config WDT_SBSA In the single stage mode, when the timeout is reached, your system will be reset by WS1. The first signal (WS0) is ignored. +config WDT_SL28CPLD + bool "sl28cpld watchdog timer support" + depends on WDT && SL28CPLD + help + Enable support for the watchdog timer in the Kontron sl28cpld + management controller. + config WDT_SP805 bool "SP805 watchdog timer support" depends on WDT diff --git a/drivers/watchdog/Makefile b/drivers/watchdog/Makefile index 6d2b3822c0..a35bd559f5 100644 --- a/drivers/watchdog/Makefile +++ b/drivers/watchdog/Makefile @@ -35,6 +35,7 @@ obj-$(CONFIG_WDT_OCTEONTX) += octeontx_wdt.o obj-$(CONFIG_WDT_OMAP3) += omap_wdt.o obj-$(CONFIG_WDT_SBSA) += sbsa_gwdt.o obj-$(CONFIG_WDT_K3_RTI) += rti_wdt.o +obj-$(CONFIG_WDT_SL28CPLD) += sl28cpld-wdt.o obj-$(CONFIG_WDT_SP805) += sp805_wdt.o obj-$(CONFIG_WDT_STM32MP) += stm32mp_wdt.o obj-$(CONFIG_WDT_SUNXI) += sunxi_wdt.o diff --git a/drivers/watchdog/armada-37xx-wdt.c b/drivers/watchdog/armada-37xx-wdt.c index 2e119b9b5a..bacebbc792 100644 --- a/drivers/watchdog/armada-37xx-wdt.c +++ b/drivers/watchdog/armada-37xx-wdt.c @@ -58,13 +58,11 @@ static void counter_disable(struct a37xx_wdt *priv, int id) clrbits_le32(priv->reg + CNTR_CTRL(id), CNTR_CTRL_ENABLE); } -static int init_counter(struct a37xx_wdt *priv, int id, u32 mode, u32 trig_src) +static void init_counter(struct a37xx_wdt *priv, int id, u32 mode, u32 trig_src) { u32 reg; reg = readl(priv->reg + CNTR_CTRL(id)); - if (reg & CNTR_CTRL_ACTIVE) - return -EBUSY; reg &= ~(CNTR_CTRL_MODE_MASK | CNTR_CTRL_PRESCALE_MASK | CNTR_CTRL_TRIG_SRC_MASK); @@ -79,8 +77,6 @@ static int init_counter(struct a37xx_wdt *priv, int id, u32 mode, u32 trig_src) reg |= trig_src; writel(reg, priv->reg + CNTR_CTRL(id)); - - return 0; } static int a37xx_wdt_reset(struct udevice *dev) @@ -116,16 +112,9 @@ static int a37xx_wdt_expire_now(struct udevice *dev, ulong flags) static int a37xx_wdt_start(struct udevice *dev, u64 ms, ulong flags) { struct a37xx_wdt *priv = dev_get_priv(dev); - int err; - - err = init_counter(priv, 0, CNTR_CTRL_MODE_ONESHOT, 0); - if (err < 0) - return err; - err = init_counter(priv, 1, CNTR_CTRL_MODE_HWSIG, - CNTR_CTRL_TRIG_SRC_PREV_CNTR); - if (err < 0) - return err; + init_counter(priv, 0, CNTR_CTRL_MODE_ONESHOT, 0); + init_counter(priv, 1, CNTR_CTRL_MODE_HWSIG, CNTR_CTRL_TRIG_SRC_PREV_CNTR); priv->timeout = ms * priv->clk_rate / 1000 / CNTR_CTRL_PRESCALE_MIN; diff --git a/drivers/watchdog/rti_wdt.c b/drivers/watchdog/rti_wdt.c index 253286d349..8d93f19b98 100644 --- a/drivers/watchdog/rti_wdt.c +++ b/drivers/watchdog/rti_wdt.c @@ -41,7 +41,7 @@ struct rti_wdt_priv { phys_addr_t regs; - unsigned int clk_khz; + unsigned int clk_hz; }; #ifdef CONFIG_WDT_K3_RTI_LOAD_FW @@ -139,7 +139,7 @@ static int rti_wdt_start(struct udevice *dev, u64 timeout_ms, ulong flags) if (ret < 0) return ret; - timer_margin = timeout_ms * priv->clk_khz / 1000; + timer_margin = timeout_ms * priv->clk_hz / 1000; timer_margin >>= WDT_PRELOAD_SHIFT; if (timer_margin > WDT_PRELOAD_MAX) timer_margin = WDT_PRELOAD_MAX; @@ -185,7 +185,15 @@ static int rti_wdt_probe(struct udevice *dev) if (ret) return ret; - priv->clk_khz = clk_get_rate(&clk); + priv->clk_hz = clk_get_rate(&clk); + + /* + * If watchdog is running at 32k clock, it is not accurate. + * Adjust frequency down in this case so that it does not expire + * earlier than expected. + */ + if (priv->clk_hz < 32768) + priv->clk_hz = priv->clk_hz * 9 / 10; return 0; } diff --git a/drivers/watchdog/sl28cpld-wdt.c b/drivers/watchdog/sl28cpld-wdt.c new file mode 100644 index 0000000000..af5a6b1a28 --- /dev/null +++ b/drivers/watchdog/sl28cpld-wdt.c @@ -0,0 +1,109 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Watchdog driver for the sl28cpld + * + * Copyright (c) 2021 Michael Walle <michael@walle.cc> + */ + +#include <common.h> +#include <dm.h> +#include <wdt.h> +#include <sl28cpld.h> +#include <div64.h> + +#define SL28CPLD_WDT_CTRL 0x00 +#define WDT_CTRL_EN0 BIT(0) +#define WDT_CTRL_EN1 BIT(1) +#define WDT_CTRL_EN_MASK GENMASK(1, 0) +#define WDT_CTRL_LOCK BIT(2) +#define WDT_CTRL_ASSERT_SYS_RESET BIT(6) +#define WDT_CTRL_ASSERT_WDT_TIMEOUT BIT(7) +#define SL28CPLD_WDT_TIMEOUT 0x01 +#define SL28CPLD_WDT_KICK 0x02 +#define WDT_KICK_VALUE 0x6b + +static int sl28cpld_wdt_reset(struct udevice *dev) +{ + return sl28cpld_write(dev, SL28CPLD_WDT_KICK, WDT_KICK_VALUE); +} + +static int sl28cpld_wdt_start(struct udevice *dev, u64 timeout, ulong flags) +{ + int ret, val; + + val = sl28cpld_read(dev, SL28CPLD_WDT_CTRL); + if (val < 0) + return val; + + /* (1) disable watchdog */ + val &= ~WDT_CTRL_EN_MASK; + ret = sl28cpld_write(dev, SL28CPLD_WDT_CTRL, val); + if (ret) + return ret; + + /* (2) set timeout */ + ret = sl28cpld_write(dev, SL28CPLD_WDT_TIMEOUT, lldiv(timeout, 1000)); + if (ret) + return ret; + + /* (3) kick it, will reset timer to the timeout value */ + ret = sl28cpld_wdt_reset(dev); + if (ret) + return ret; + + /* (4) enable either recovery or normal one */ + if (flags & BIT(0)) + val |= WDT_CTRL_EN1; + else + val |= WDT_CTRL_EN0; + + if (flags & BIT(1)) + val |= WDT_CTRL_LOCK; + + if (flags & BIT(2)) + val &= ~WDT_CTRL_ASSERT_SYS_RESET; + else + val |= WDT_CTRL_ASSERT_SYS_RESET; + + if (flags & BIT(3)) + val |= WDT_CTRL_ASSERT_WDT_TIMEOUT; + else + val &= ~WDT_CTRL_ASSERT_WDT_TIMEOUT; + + return sl28cpld_write(dev, SL28CPLD_WDT_CTRL, val); +} + +static int sl28cpld_wdt_stop(struct udevice *dev) +{ + int val; + + val = sl28cpld_read(dev, SL28CPLD_WDT_CTRL); + if (val < 0) + return val; + + return sl28cpld_write(dev, SL28CPLD_WDT_CTRL, val & ~WDT_CTRL_EN_MASK); +} + +static int sl28cpld_wdt_expire_now(struct udevice *dev, ulong flags) +{ + return sl28cpld_wdt_start(dev, 0, flags); +} + +static const struct wdt_ops sl28cpld_wdt_ops = { + .start = sl28cpld_wdt_start, + .reset = sl28cpld_wdt_reset, + .stop = sl28cpld_wdt_stop, + .expire_now = sl28cpld_wdt_expire_now, +}; + +static const struct udevice_id sl28cpld_wdt_ids[] = { + { .compatible = "kontron,sl28cpld-wdt", }, + {} +}; + +U_BOOT_DRIVER(sl28cpld_wdt) = { + .name = "sl28cpld-wdt", + .id = UCLASS_WDT, + .of_match = sl28cpld_wdt_ids, + .ops = &sl28cpld_wdt_ops, +}; diff --git a/drivers/watchdog/wdt-uclass.c b/drivers/watchdog/wdt-uclass.c index 6d0f473867..dbf556467d 100644 --- a/drivers/watchdog/wdt-uclass.c +++ b/drivers/watchdog/wdt-uclass.c @@ -36,6 +36,8 @@ struct wdt_priv { ulong next_reset; /* Whether watchdog_start() has been called on the device. */ bool running; + /* No autostart */ + bool noautostart; }; static void init_watchdog_dev(struct udevice *dev) @@ -52,7 +54,7 @@ static void init_watchdog_dev(struct udevice *dev) dev->name); } - if (!IS_ENABLED(CONFIG_WATCHDOG_AUTOSTART)) { + if (!IS_ENABLED(CONFIG_WATCHDOG_AUTOSTART) || priv->noautostart) { printf("WDT: Not starting %s\n", dev->name); return; } @@ -256,16 +258,19 @@ static int wdt_pre_probe(struct udevice *dev) * indicated by a hw_margin_ms property. */ ulong reset_period = 1000; + bool noautostart = false; struct wdt_priv *priv; if (CONFIG_IS_ENABLED(OF_CONTROL) && !CONFIG_IS_ENABLED(OF_PLATDATA)) { timeout = dev_read_u32_default(dev, "timeout-sec", timeout); reset_period = dev_read_u32_default(dev, "hw_margin_ms", 4 * reset_period) / 4; + noautostart = dev_read_bool(dev, "u-boot,noautostart"); } priv = dev_get_uclass_priv(dev); priv->timeout = timeout; priv->reset_period = reset_period; + priv->noautostart = noautostart; /* * Pretend this device was last reset "long" ago so the first * watchdog_reset will actually call its ->reset method. |