From 0df27d687c4639208d6e378907ca00f68cd06da6 Mon Sep 17 00:00:00 2001 From: Lihua Zhao Date: Wed, 18 Mar 2020 07:32:07 -0700 Subject: [PATCH 01/10] image-fit: Allow loading FIT image for VxWorks This adds the check against IH_OS_VXWORKS during FIT image load, to allow loading FIT image for VxWorks. Signed-off-by: Lihua Zhao Signed-off-by: Bin Meng Reviewed-by: Bin Meng --- common/image-fit.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/common/image-fit.c b/common/image-fit.c index 4435bc4f1d..6da69d25ff 100644 --- a/common/image-fit.c +++ b/common/image-fit.c @@ -2007,7 +2007,8 @@ int fit_image_load(bootm_headers_t *images, ulong addr, fit_image_check_os(fit, noffset, IH_OS_LINUX) || fit_image_check_os(fit, noffset, IH_OS_U_BOOT) || fit_image_check_os(fit, noffset, IH_OS_OPENRTOS) || - fit_image_check_os(fit, noffset, IH_OS_EFI); + fit_image_check_os(fit, noffset, IH_OS_EFI) || + fit_image_check_os(fit, noffset, IH_OS_VXWORKS); /* * If either of the checks fail, we should report an error, but From 71a3e5c51c96fe4011ef2cea8490e523786de863 Mon Sep 17 00:00:00 2001 From: Heinrich Schuchardt Date: Sun, 29 Mar 2020 19:26:57 +0000 Subject: [PATCH 02/10] cmd: mmc: fix typo 'a EMMC' %s/a EMMC/an eMMC/g Signed-off-by: Heinrich Schuchardt Reviewed-by: Jaehoon Chung --- cmd/mmc.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/cmd/mmc.c b/cmd/mmc.c index 6f3cb85cc0..1860a3f2e5 100644 --- a/cmd/mmc.c +++ b/cmd/mmc.c @@ -264,7 +264,7 @@ static int do_mmcrpmb(cmd_tbl_t *cmdtp, int flag, return CMD_RET_FAILURE; if (!(mmc->version & MMC_VERSION_MMC)) { - printf("It is not a EMMC device\n"); + printf("It is not an eMMC device\n"); return CMD_RET_FAILURE; } if (mmc->version < MMC_VERSION_4_41) { @@ -718,7 +718,7 @@ static int do_mmc_boot_resize(cmd_tbl_t *cmdtp, int flag, return CMD_RET_FAILURE; if (IS_SD(mmc)) { - printf("It is not a EMMC device\n"); + printf("It is not an eMMC device\n"); return CMD_RET_FAILURE; } From 1d7ad9fa051e5b3057bccb6384ab62e4f9c206b2 Mon Sep 17 00:00:00 2001 From: Rasmus Villemoes Date: Tue, 28 Jan 2020 12:04:33 +0000 Subject: [PATCH 03/10] gpio: mpc8xxx: don't modify gpdat when setting gpio as input Since some chips don't support reading back the value of output gpios from the gpdat register, we should not do a RMW cycle (i.e., the clrbits_be32) on the gpdat register when setting a gpio as input, as that might accidentally change the value of some other (still configured as output) gpio. The extra indirection through mpc8xxx_gpio_set_in() does not help readability, so just fold the gpdir update into mpc8xxx_gpio_direction_input(). Signed-off-by: Rasmus Villemoes --- drivers/gpio/mpc8xxx_gpio.c | 12 ++++-------- 1 file changed, 4 insertions(+), 8 deletions(-) diff --git a/drivers/gpio/mpc8xxx_gpio.c b/drivers/gpio/mpc8xxx_gpio.c index c273c2c8a4..d4f3092df0 100644 --- a/drivers/gpio/mpc8xxx_gpio.c +++ b/drivers/gpio/mpc8xxx_gpio.c @@ -57,13 +57,6 @@ static inline u32 mpc8xxx_gpio_get_dir(struct ccsr_gpio *base, u32 mask) return in_be32(&base->gpdir) & mask; } -static inline void mpc8xxx_gpio_set_in(struct ccsr_gpio *base, u32 gpios) -{ - clrbits_be32(&base->gpdat, gpios); - /* GPDIR register 0 -> input */ - clrbits_be32(&base->gpdir, gpios); -} - static inline void mpc8xxx_gpio_set_low(struct ccsr_gpio *base, u32 gpios) { clrbits_be32(&base->gpdat, gpios); @@ -100,8 +93,11 @@ static inline void mpc8xxx_gpio_open_drain_off(struct ccsr_gpio *base, static int mpc8xxx_gpio_direction_input(struct udevice *dev, uint gpio) { struct mpc8xxx_gpio_data *data = dev_get_priv(dev); + u32 mask = gpio_mask(gpio); + + /* GPDIR register 0 -> input */ + clrbits_be32(&data->base->gpdir, mask); - mpc8xxx_gpio_set_in(data->base, gpio_mask(gpio)); return 0; } From dd4cf53f9864362d9e92472298ad60dcbb8b7e72 Mon Sep 17 00:00:00 2001 From: Rasmus Villemoes Date: Tue, 28 Jan 2020 12:04:34 +0000 Subject: [PATCH 04/10] gpio: mpc8xxx: don't do RMW on gpdat register when setting value The driver correctly handles reading back the value of an output gpio by reading from the shadow register for output, and from gpdat for inputs. Unfortunately, when setting the value of some gpio, we do a RMW cycle on the gpdat register without taking the shadow register into account, thus accidentally setting other output gpios (at least those whose value cannot be read back) to 0 at the same time. When changing a gpio from input to output, we still need to make sure it initially has the requested value. So, the procedure is - update the shadow register - compute the new gpdir register - write the bitwise and of the shadow and new gpdir register to gpdat - write the new gpdir register Signed-off-by: Rasmus Villemoes --- drivers/gpio/mpc8xxx_gpio.c | 29 +++++++++++------------------ 1 file changed, 11 insertions(+), 18 deletions(-) diff --git a/drivers/gpio/mpc8xxx_gpio.c b/drivers/gpio/mpc8xxx_gpio.c index d4f3092df0..4b385b8b39 100644 --- a/drivers/gpio/mpc8xxx_gpio.c +++ b/drivers/gpio/mpc8xxx_gpio.c @@ -57,20 +57,6 @@ static inline u32 mpc8xxx_gpio_get_dir(struct ccsr_gpio *base, u32 mask) return in_be32(&base->gpdir) & mask; } -static inline void mpc8xxx_gpio_set_low(struct ccsr_gpio *base, u32 gpios) -{ - clrbits_be32(&base->gpdat, gpios); - /* GPDIR register 1 -> output */ - setbits_be32(&base->gpdir, gpios); -} - -static inline void mpc8xxx_gpio_set_high(struct ccsr_gpio *base, u32 gpios) -{ - setbits_be32(&base->gpdat, gpios); - /* GPDIR register 1 -> output */ - setbits_be32(&base->gpdir, gpios); -} - static inline int mpc8xxx_gpio_open_drain_val(struct ccsr_gpio *base, u32 mask) { return in_be32(&base->gpodr) & mask; @@ -104,14 +90,21 @@ static int mpc8xxx_gpio_direction_input(struct udevice *dev, uint gpio) static int mpc8xxx_gpio_set_value(struct udevice *dev, uint gpio, int value) { struct mpc8xxx_gpio_data *data = dev_get_priv(dev); + struct ccsr_gpio *base = data->base; + u32 mask = gpio_mask(gpio); + u32 gpdir; if (value) { - data->dat_shadow |= gpio_mask(gpio); - mpc8xxx_gpio_set_high(data->base, gpio_mask(gpio)); + data->dat_shadow |= mask; } else { - data->dat_shadow &= ~gpio_mask(gpio); - mpc8xxx_gpio_set_low(data->base, gpio_mask(gpio)); + data->dat_shadow &= ~mask; } + + gpdir = in_be32(&base->gpdir); + gpdir |= gpio_mask(gpio); + out_be32(&base->gpdat, gpdir & data->dat_shadow); + out_be32(&base->gpdir, gpdir); + return 0; } From 3fb22bc2f825ea1b3326edc5b32d711a759a265f Mon Sep 17 00:00:00 2001 From: "Klaus H. Sorensen" Date: Tue, 11 Feb 2020 15:20:22 +0000 Subject: [PATCH 05/10] gpio/mpc83xx_spisel_boot.c: gpio driver for SPISEL_BOOT signal Some SoCs in the mpc83xx family, e.g. mpc8309, have a dedicated spi chip select, SPISEL_BOOT, that is used by the boot code to boot from flash. This chip select will typically be used to select a SPI boot flash. The SPISEL_BOOT signal is controlled by a single bit in the SPI_CS register. Implement a gpio driver for the spi chip select register. This allows a spi driver capable of using gpios as chip select, to bind a chip select to SPISEL_BOOT. It may be a little odd to do this as a GPIO driver, since the signal is neither GP or I, but it is quite convenient to present it to the spi driver that way. The alternative it to teach mpc8xxx_spi to handle the SPISEL_BOOT signal itself (that is how it's done in the linux kernel, see commit 69b921acae8a) Signed-off-by: Klaus H. Sorensen Signed-off-by: Rasmus Villemoes --- .../gpio/fsl,mpc83xx-spisel-boot.txt | 22 +++ drivers/gpio/Kconfig | 8 + drivers/gpio/Makefile | 1 + drivers/gpio/mpc83xx_spisel_boot.c | 148 ++++++++++++++++++ 4 files changed, 179 insertions(+) create mode 100644 doc/device-tree-bindings/gpio/fsl,mpc83xx-spisel-boot.txt create mode 100644 drivers/gpio/mpc83xx_spisel_boot.c diff --git a/doc/device-tree-bindings/gpio/fsl,mpc83xx-spisel-boot.txt b/doc/device-tree-bindings/gpio/fsl,mpc83xx-spisel-boot.txt new file mode 100644 index 0000000000..52d8bb0a5c --- /dev/null +++ b/doc/device-tree-bindings/gpio/fsl,mpc83xx-spisel-boot.txt @@ -0,0 +1,22 @@ +MPC83xx SPISEL_BOOT gpio controller + +Provide access to MPC83xx SPISEL_BOOT signal as a gpio to allow it to be +easily bound as a SPI controller chip select. + +The SPISEL_BOOT signal is always an output. + +Required properties: + +- compatible: must be "fsl,mpc83xx-spisel-boot" or "fsl,mpc8309-spisel-boot". +- reg: must point to the SPI_CS register in the SoC register map. +- ngpios: number of gpios provided by driver, normally 1. + +Example: + + spisel_boot: spisel_boot@14c { + compatible = "fsl,mpc8309-spisel-boot"; + reg = <0x14c 0x04>; + #gpio-cells = <2>; + device_type = "gpio"; + ngpios = <1>; + }; diff --git a/drivers/gpio/Kconfig b/drivers/gpio/Kconfig index f751a8b9ea..2081520f42 100644 --- a/drivers/gpio/Kconfig +++ b/drivers/gpio/Kconfig @@ -423,6 +423,14 @@ config MPC8XXX_GPIO value setting, the open-drain feature, which can configure individual GPIOs to work as open-drain outputs, is supported. +config MPC83XX_SPISEL_BOOT + bool "Freescale MPC83XX SPISEL_BOOT driver" + depends on DM_GPIO && ARCH_MPC830X + help + GPIO driver to set/clear dedicated SPISEL_BOOT output on MPC83XX. + + This pin is typically used as spi chip select to a spi nor flash. + config MT7621_GPIO bool "MediaTek MT7621 GPIO driver" depends on DM_GPIO && SOC_MT7628 diff --git a/drivers/gpio/Makefile b/drivers/gpio/Makefile index 9dd5a58389..7638259007 100644 --- a/drivers/gpio/Makefile +++ b/drivers/gpio/Makefile @@ -40,6 +40,7 @@ obj-$(CONFIG_DM644X_GPIO) += da8xx_gpio.o obj-$(CONFIG_ALTERA_PIO) += altera_pio.o obj-$(CONFIG_MPC83XX_GPIO) += mpc83xx_gpio.o obj-$(CONFIG_MPC8XXX_GPIO) += mpc8xxx_gpio.o +obj-$(CONFIG_MPC83XX_SPISEL_BOOT) += mpc83xx_spisel_boot.o obj-$(CONFIG_SH_GPIO_PFC) += sh_pfc.o obj-$(CONFIG_OMAP_GPIO) += omap_gpio.o obj-$(CONFIG_DB8500_GPIO) += db8500_gpio.o diff --git a/drivers/gpio/mpc83xx_spisel_boot.c b/drivers/gpio/mpc83xx_spisel_boot.c new file mode 100644 index 0000000000..c7b08404d9 --- /dev/null +++ b/drivers/gpio/mpc83xx_spisel_boot.c @@ -0,0 +1,148 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * (C) Copyright 2019 DEIF A/S + * + * GPIO driver to set/clear SPISEL_BOOT pin on mpc83xx. + */ + +#include +#include +#include +#include + +struct mpc83xx_spisel_boot { + u32 __iomem *spi_cs; + ulong addr; + uint gpio_count; + ulong type; +}; + +static u32 gpio_mask(uint gpio) +{ + return (1U << (31 - (gpio))); +} + +static int mpc83xx_spisel_boot_direction_input(struct udevice *dev, uint gpio) +{ + return -EINVAL; +} + +static int mpc83xx_spisel_boot_set_value(struct udevice *dev, uint gpio, int value) +{ + struct mpc83xx_spisel_boot *data = dev_get_priv(dev); + + debug("%s: gpio=%d, value=%u, gpio_mask=0x%08x\n", __func__, + gpio, value, gpio_mask(gpio)); + + if (value) + setbits_be32(data->spi_cs, gpio_mask(gpio)); + else + clrbits_be32(data->spi_cs, gpio_mask(gpio)); + + return 0; +} + +static int mpc83xx_spisel_boot_direction_output(struct udevice *dev, uint gpio, int value) +{ + return 0; +} + +static int mpc83xx_spisel_boot_get_value(struct udevice *dev, uint gpio) +{ + struct mpc83xx_spisel_boot *data = dev_get_priv(dev); + + return !!(in_be32(data->spi_cs) & gpio_mask(gpio)); +} + +static int mpc83xx_spisel_boot_get_function(struct udevice *dev, uint gpio) +{ + return GPIOF_OUTPUT; +} + +#if CONFIG_IS_ENABLED(OF_CONTROL) +static int mpc83xx_spisel_boot_ofdata_to_platdata(struct udevice *dev) +{ + struct mpc8xxx_gpio_plat *plat = dev_get_platdata(dev); + fdt_addr_t addr; + u32 reg[2]; + + dev_read_u32_array(dev, "reg", reg, 2); + addr = dev_translate_address(dev, reg); + + plat->addr = addr; + plat->size = reg[1]; + plat->ngpios = dev_read_u32_default(dev, "ngpios", 1); + + return 0; +} +#endif + +static int mpc83xx_spisel_boot_platdata_to_priv(struct udevice *dev) +{ + struct mpc83xx_spisel_boot *priv = dev_get_priv(dev); + struct mpc8xxx_gpio_plat *plat = dev_get_platdata(dev); + unsigned long size = plat->size; + ulong driver_data = dev_get_driver_data(dev); + + if (size == 0) + size = 0x04; + + priv->addr = plat->addr; + priv->spi_cs = map_sysmem(plat->addr, size); + + if (!priv->spi_cs) + return -ENOMEM; + + priv->gpio_count = plat->ngpios; + + priv->type = driver_data; + + return 0; +} + +static int mpc83xx_spisel_boot_probe(struct udevice *dev) +{ + struct gpio_dev_priv *uc_priv = dev_get_uclass_priv(dev); + struct mpc83xx_spisel_boot *data = dev_get_priv(dev); + char name[32], *str; + + mpc83xx_spisel_boot_platdata_to_priv(dev); + + snprintf(name, sizeof(name), "MPC@%lx_", data->addr); + str = strdup(name); + + if (!str) + return -ENOMEM; + + uc_priv->bank_name = str; + uc_priv->gpio_count = data->gpio_count; + + return 0; +} + +static const struct dm_gpio_ops mpc83xx_spisel_boot_ops = { + .direction_input = mpc83xx_spisel_boot_direction_input, + .direction_output = mpc83xx_spisel_boot_direction_output, + .get_value = mpc83xx_spisel_boot_get_value, + .set_value = mpc83xx_spisel_boot_set_value, + .get_function = mpc83xx_spisel_boot_get_function, +}; + +static const struct udevice_id mpc83xx_spisel_boot_ids[] = { + { .compatible = "fsl,mpc8309-spisel-boot" }, + { .compatible = "fsl,mpc83xx-spisel-boot" }, + { /* sentinel */ } +}; + +U_BOOT_DRIVER(spisel_boot_mpc83xx) = { + .name = "spisel_boot_mpc83xx", + .id = UCLASS_GPIO, + .ops = &mpc83xx_spisel_boot_ops, +#if CONFIG_IS_ENABLED(OF_CONTROL) + .ofdata_to_platdata = mpc83xx_spisel_boot_ofdata_to_platdata, + .platdata_auto_alloc_size = sizeof(struct mpc8xxx_gpio_plat), + .of_match = mpc83xx_spisel_boot_ids, +#endif + .probe = mpc83xx_spisel_boot_probe, + .priv_auto_alloc_size = sizeof(struct mpc83xx_spisel_boot), +}; From 96e68c1621a416cf5b1e933206d23ff86b246fb4 Mon Sep 17 00:00:00 2001 From: Rasmus Villemoes Date: Tue, 11 Feb 2020 15:20:23 +0000 Subject: [PATCH 06/10] gazerbeam: add clocks property to SPI node Prepare for supporting setting different speeds in mpc8xxx_spi.c. Signed-off-by: Rasmus Villemoes --- arch/powerpc/dts/gdsys/mpc8308.dtsi | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/arch/powerpc/dts/gdsys/mpc8308.dtsi b/arch/powerpc/dts/gdsys/mpc8308.dtsi index 23e7403d91..1a319e2328 100644 --- a/arch/powerpc/dts/gdsys/mpc8308.dtsi +++ b/arch/powerpc/dts/gdsys/mpc8308.dtsi @@ -17,6 +17,7 @@ /dts-v1/; #include +#include / { compatible = "fsl,mpc8308rdb"; @@ -50,6 +51,11 @@ }; }; + socclocks: clocks { + compatible = "fsl,mpc8308-clk"; + #clock-cells = <1>; + }; + board_lbc: localbus@e0005000 { #address-cells = <2>; #size-cells = <1>; @@ -173,6 +179,7 @@ reg = <0x7000 0x1000>; interrupts = <16 0x8>; interrupt-parent = <&ipic>; + clocks = <&socclocks MPC83XX_CLK_CSB>; mode = "cpu"; }; From 1a7b462dee310e83b5ed45ff5a56aa348d07b03a Mon Sep 17 00:00:00 2001 From: Rasmus Villemoes Date: Tue, 11 Feb 2020 15:20:24 +0000 Subject: [PATCH 07/10] mpc8xxx_spi: put max_cs to use Currently, max_cs is write-only; it's just set in mpc8xxx_spi_ofdata_to_platdata and not otherwise used. My mpc8309 was always resetting during an "sf probe 0". It turns out dm_gpio_set_dir_flags() was being called with garbage, since nothing had initialized priv->gpios[0] - our device tree used "cs-gpios" rather than "gpios", so gpio_request_list_by_name() had returned 0. That would have been a lot easier to figure out if the chip select index was sanity checked, so rename max_cs to cs_count, and reject a xfer with a too large cs index. Signed-off-by: Rasmus Villemoes --- drivers/spi/mpc8xxx_spi.c | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/drivers/spi/mpc8xxx_spi.c b/drivers/spi/mpc8xxx_spi.c index 1c7bf10f91..ac4d0a9bae 100644 --- a/drivers/spi/mpc8xxx_spi.c +++ b/drivers/spi/mpc8xxx_spi.c @@ -35,7 +35,7 @@ enum { struct mpc8xxx_priv { spi8xxx_t *spi; struct gpio_desc gpios[16]; - int max_cs; + int cs_count; }; static inline u32 to_prescale_mod(u32 val) @@ -74,7 +74,7 @@ static int mpc8xxx_spi_ofdata_to_platdata(struct udevice *dev) if (ret < 0) return -EINVAL; - priv->max_cs = ret; + priv->cs_count = ret; return 0; } @@ -131,6 +131,11 @@ static int mpc8xxx_spi_xfer(struct udevice *dev, uint bitlen, debug("%s: slave %s:%u dout %08X din %08X bitlen %u\n", __func__, bus->name, platdata->cs, *(uint *)dout, *(uint *)din, bitlen); + if (platdata->cs >= priv->cs_count) { + dev_err(dev, "chip select index %d too large (cs_count=%d)\n", + platdata->cs, priv->cs_count); + return -EINVAL; + } if (flags & SPI_XFER_BEGIN) mpc8xxx_spi_cs_activate(dev); From 391c40048b01dacd50243a594f74775a3ec60104 Mon Sep 17 00:00:00 2001 From: Rasmus Villemoes Date: Tue, 11 Feb 2020 15:20:25 +0000 Subject: [PATCH 08/10] mpc8xxx_spi: always use 8-bit characters, don't read or write garbage MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit There are a few problems with the current driver. First, it unconditionally reads from dout/writes to din whether or not those pointers are NULL. So for example a simple "sf probe" ends up writing four bytes at address 0: => md.l 0x0 8 00000000: 45454545 45454545 05050505 05050505 EEEEEEEE........ 00000010: 00000000 00000000 07070707 07070707 ................ => sf probe 0 mpc8xxx_spi_xfer: slave spi@7000:0 dout 0FB53618 din 00000000 bitlen 8 mpc8xxx_spi_xfer: slave spi@7000:0 dout 00000000 din 0FB536B8 bitlen 48 SF: Detected s25sl032p with page size 256 Bytes, erase size 64 KiB, total 4 MiB => md.l 0x0 8 00000000: ff000000 45454545 05050505 05050505 ....EEEE........ 00000010: 00000000 00000000 07070707 07070707 ................ (here I've change the first debug statement to a printf, and made it print the din/dout pointers rather than the uints they point at). Second, as we can also see above, it always writes a full 32 bits, even if a smaller amount was requested. So for example => mw.l $loadaddr 0xaabbccdd 8 => md.l $loadaddr 8 02000000: aabbccdd aabbccdd aabbccdd aabbccdd ................ 02000010: aabbccdd aabbccdd aabbccdd aabbccdd ................ => sf read $loadaddr 0x400 6 device 0 offset 0x400, size 0x6 mpc8xxx_spi_xfer: slave spi@7000:0 dout 0FB536E8 din 00000000 bitlen 40 mpc8xxx_spi_xfer: slave spi@7000:0 dout 00000000 din 02000000 bitlen 48 SF: 6 bytes @ 0x400 Read: OK => sf read 0x02000010 0x400 8 device 0 offset 0x400, size 0x8 mpc8xxx_spi_xfer: slave spi@7000:0 dout 0FB53848 din 00000000 bitlen 40 mpc8xxx_spi_xfer: slave spi@7000:0 dout 00000000 din 02000010 bitlen 64 SF: 8 bytes @ 0x400 Read: OK => md.l $loadaddr 8 02000000: 45454545 45450000 aabbccdd aabbccdd EEEEEE.......... 02000010: 45454545 45454545 aabbccdd aabbccdd EEEEEEEE........ Finally, when the bitlen is 24 mod 32 (e.g. requesting to read 3 or 7 bytes), the last three bytes and up being the wrong ones, since the driver does a full 32 bit read and then shifts the wrong byte out: => mw.l $loadaddr 0xaabbccdd 4 => md.l $loadaddr 4 02000000: aabbccdd aabbccdd aabbccdd aabbccdd ................ => sf read $loadaddr 0x444 10 device 0 offset 0x444, size 0x10 mpc8xxx_spi_xfer: slave spi@7000:0 dout 0FB536E8 din 00000000 bitlen 40 mpc8xxx_spi_xfer: slave spi@7000:0 dout 00000000 din 02000000 bitlen 128 SF: 16 bytes @ 0x444 Read: OK => md.l $loadaddr 4 02000000: 552d426f 6f742032 3031392e 30342d30 U-Boot 2019.04-0 => mw.l $loadaddr 0xaabbccdd 4 => sf read $loadaddr 0x444 0xb device 0 offset 0x444, size 0xb mpc8xxx_spi_xfer: slave spi@7000:0 dout 0FB536E8 din 00000000 bitlen 40 mpc8xxx_spi_xfer: slave spi@7000:0 dout 00000000 din 02000000 bitlen 88 SF: 11 bytes @ 0x444 Read: OK => md.l $loadaddr 4 02000000: 552d426f 6f742032 31392e00 aabbccdd U-Boot 219...... Fix all of that by always using a character size of 8, and reject transfers that are not a whole number of bytes. While it ends being more work for the CPU, we're mostly bounded by the speed of the SPI bus, and we avoid writing to the mode register in every loop. Based on work by Klaus H. Sørensen. Cc: Klaus H. Sorensen Signed-off-by: Rasmus Villemoes --- drivers/spi/mpc8xxx_spi.c | 80 +++++++++++++-------------------------- 1 file changed, 27 insertions(+), 53 deletions(-) diff --git a/drivers/spi/mpc8xxx_spi.c b/drivers/spi/mpc8xxx_spi.c index ac4d0a9bae..8ef2451411 100644 --- a/drivers/spi/mpc8xxx_spi.c +++ b/drivers/spi/mpc8xxx_spi.c @@ -27,6 +27,7 @@ enum { SPI_MODE_EN = BIT(31 - 7), /* Enable interface */ SPI_MODE_LEN_MASK = 0xf00000, + SPI_MODE_LEN_SHIFT = 20, SPI_MODE_PM_MASK = 0xf0000, SPI_COM_LST = BIT(31 - 9), @@ -43,23 +44,8 @@ static inline u32 to_prescale_mod(u32 val) return (min(val, (u32)15) << 16); } -static void set_char_len(spi8xxx_t *spi, u32 val) -{ - clrsetbits_be32(&spi->mode, SPI_MODE_LEN_MASK, (val << 20)); -} - #define SPI_TIMEOUT 1000 -static int __spi_set_speed(spi8xxx_t *spi, uint speed) -{ - /* TODO(mario.six@gdsys.cc): This only ever sets one fixed speed */ - - /* Use SYSCLK / 8 (16.67MHz typ.) */ - clrsetbits_be32(&spi->mode, SPI_MODE_PM_MASK, to_prescale_mod(1)); - - return 0; -} - static int mpc8xxx_spi_ofdata_to_platdata(struct udevice *dev) { struct mpc8xxx_priv *priv = dev_get_priv(dev); @@ -82,14 +68,22 @@ static int mpc8xxx_spi_ofdata_to_platdata(struct udevice *dev) static int mpc8xxx_spi_probe(struct udevice *dev) { struct mpc8xxx_priv *priv = dev_get_priv(dev); + spi8xxx_t *spi = priv->spi; /* * SPI pins on the MPC83xx are not muxed, so all we do is initialize * some registers */ - out_be32(&priv->spi->mode, SPI_MODE_REV | SPI_MODE_MS | SPI_MODE_EN); + out_be32(&priv->spi->mode, SPI_MODE_REV | SPI_MODE_MS); - __spi_set_speed(priv->spi, 16666667); + /* set len to 8 bits */ + setbits_be32(&spi->mode, (8 - 1) << SPI_MODE_LEN_SHIFT); + + /* TODO(mario.six@gdsys.cc): This only ever sets one fixed speed */ + /* Use SYSCLK / 8 (16.67MHz typ.) */ + clrsetbits_be32(&spi->mode, SPI_MODE_PM_MASK, to_prescale_mod(1)); + + setbits_be32(&spi->mode, SPI_MODE_EN); /* Clear all SPI events */ setbits_be32(&priv->spi->event, 0xffffffff); @@ -126,50 +120,35 @@ static int mpc8xxx_spi_xfer(struct udevice *dev, uint bitlen, struct mpc8xxx_priv *priv = dev_get_priv(bus); spi8xxx_t *spi = priv->spi; struct dm_spi_slave_platdata *platdata = dev_get_parent_platdata(dev); - u32 tmpdin = 0; - int num_blks = DIV_ROUND_UP(bitlen, 32); + u32 tmpdin = 0, tmpdout = 0, n; + const u8 *cout = dout; + u8 *cin = din; debug("%s: slave %s:%u dout %08X din %08X bitlen %u\n", __func__, - bus->name, platdata->cs, *(uint *)dout, *(uint *)din, bitlen); + bus->name, platdata->cs, (uint)dout, (uint)din, bitlen); if (platdata->cs >= priv->cs_count) { dev_err(dev, "chip select index %d too large (cs_count=%d)\n", platdata->cs, priv->cs_count); return -EINVAL; } + if (bitlen % 8) { + printf("*** spi_xfer: bitlen must be multiple of 8\n"); + return -ENOTSUPP; + } if (flags & SPI_XFER_BEGIN) mpc8xxx_spi_cs_activate(dev); /* Clear all SPI events */ setbits_be32(&spi->event, 0xffffffff); + n = bitlen / 8; - /* Handle data in 32-bit chunks */ - while (num_blks--) { - u32 tmpdout = 0; - uchar xfer_bitlen = (bitlen >= 32 ? 32 : bitlen); + /* Handle data in 8-bit chunks */ + while (n--) { ulong start; - clrbits_be32(&spi->mode, SPI_MODE_EN); - - /* Set up length for this transfer */ - - if (bitlen <= 4) /* 4 bits or less */ - set_char_len(spi, 3); - else if (bitlen <= 16) /* at most 16 bits */ - set_char_len(spi, bitlen - 1); - else /* more than 16 bits -> full 32 bit transfer */ - set_char_len(spi, 0); - - setbits_be32(&spi->mode, SPI_MODE_EN); - - /* Shift data so it's msb-justified */ - tmpdout = *(u32 *)dout >> (32 - xfer_bitlen); - - if (bitlen > 32) { - /* Set up the next iteration if sending > 32 bits */ - bitlen -= 32; - dout += 4; - } + if (cout) + tmpdout = *cout++; /* Write the data out */ out_be32(&spi->tx, tmpdout); @@ -193,11 +172,8 @@ static int mpc8xxx_spi_xfer(struct udevice *dev, uint bitlen, tmpdin = in_be32(&spi->rx); setbits_be32(&spi->event, SPI_EV_NE); - *(u32 *)din = (tmpdin << (32 - xfer_bitlen)); - if (xfer_bitlen == 32) { - /* Advance output buffer by 32 bits */ - din += 4; - } + if (cin) + *cin++ = tmpdin; /* * Only bail when we've had both NE and NF events. @@ -228,9 +204,7 @@ static int mpc8xxx_spi_xfer(struct udevice *dev, uint bitlen, static int mpc8xxx_spi_set_speed(struct udevice *dev, uint speed) { - struct mpc8xxx_priv *priv = dev_get_priv(dev); - - return __spi_set_speed(priv->spi, speed); + return 0; } static int mpc8xxx_spi_set_mode(struct udevice *dev, uint mode) From 4856cc7a972a7679cdbb33532ad932a3e865b18e Mon Sep 17 00:00:00 2001 From: Rasmus Villemoes Date: Tue, 11 Feb 2020 15:20:25 +0000 Subject: [PATCH 09/10] mpc8xxx_spi: implement real ->set_speed MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Not all boards have the same CSB frequency, nor do every SPI slave necessarily support running at 16.7 MHz. So implement ->set_speed; that also allows using a smaller PM (i.e., 0) for slaves that do support a higher speed. Based on work by Klaus H. Sørensen. Cc: Klaus H. Sorensen Signed-off-by: Rasmus Villemoes --- drivers/spi/mpc8xxx_spi.c | 64 ++++++++++++++++++++++++++++++++------- 1 file changed, 53 insertions(+), 11 deletions(-) diff --git a/drivers/spi/mpc8xxx_spi.c b/drivers/spi/mpc8xxx_spi.c index 8ef2451411..1bde31ad34 100644 --- a/drivers/spi/mpc8xxx_spi.c +++ b/drivers/spi/mpc8xxx_spi.c @@ -5,6 +5,7 @@ */ #include +#include #include #include #include @@ -28,6 +29,7 @@ enum { SPI_MODE_LEN_MASK = 0xf00000, SPI_MODE_LEN_SHIFT = 20, + SPI_MODE_PM_SHIFT = 16, SPI_MODE_PM_MASK = 0xf0000, SPI_COM_LST = BIT(31 - 9), @@ -37,24 +39,19 @@ struct mpc8xxx_priv { spi8xxx_t *spi; struct gpio_desc gpios[16]; int cs_count; + ulong clk_rate; }; -static inline u32 to_prescale_mod(u32 val) -{ - return (min(val, (u32)15) << 16); -} - #define SPI_TIMEOUT 1000 static int mpc8xxx_spi_ofdata_to_platdata(struct udevice *dev) { struct mpc8xxx_priv *priv = dev_get_priv(dev); + struct clk clk; int ret; priv->spi = (spi8xxx_t *)dev_read_addr(dev); - /* TODO(mario.six@gdsys.cc): Read clock and save the value */ - ret = gpio_request_list_by_name(dev, "gpios", priv->gpios, ARRAY_SIZE(priv->gpios), GPIOD_IS_OUT | GPIOD_ACTIVE_LOW); if (ret < 0) @@ -62,6 +59,18 @@ static int mpc8xxx_spi_ofdata_to_platdata(struct udevice *dev) priv->cs_count = ret; + ret = clk_get_by_index(dev, 0, &clk); + if (ret) { + dev_err(dev, "%s: clock not defined\n", __func__); + return ret; + } + + priv->clk_rate = clk_get_rate(&clk); + if (!priv->clk_rate) { + dev_err(dev, "%s: failed to get clock rate\n", __func__); + return -EINVAL; + } + return 0; } @@ -79,10 +88,6 @@ static int mpc8xxx_spi_probe(struct udevice *dev) /* set len to 8 bits */ setbits_be32(&spi->mode, (8 - 1) << SPI_MODE_LEN_SHIFT); - /* TODO(mario.six@gdsys.cc): This only ever sets one fixed speed */ - /* Use SYSCLK / 8 (16.67MHz typ.) */ - clrsetbits_be32(&spi->mode, SPI_MODE_PM_MASK, to_prescale_mod(1)); - setbits_be32(&spi->mode, SPI_MODE_EN); /* Clear all SPI events */ @@ -204,6 +209,43 @@ static int mpc8xxx_spi_xfer(struct udevice *dev, uint bitlen, static int mpc8xxx_spi_set_speed(struct udevice *dev, uint speed) { + struct mpc8xxx_priv *priv = dev_get_priv(dev); + spi8xxx_t *spi = priv->spi; + u32 bits, mask, div16, pm; + u32 mode; + ulong clk; + + clk = priv->clk_rate; + if (clk / 64 > speed) { + div16 = SPI_MODE_DIV16; + clk /= 16; + } else { + div16 = 0; + } + pm = (clk - 1)/(4*speed) + 1; + if (pm > 16) { + dev_err(dev, "requested speed %u too small\n", speed); + return -EINVAL; + } + pm--; + + bits = div16 | (pm << SPI_MODE_PM_SHIFT); + mask = SPI_MODE_DIV16 | SPI_MODE_PM_MASK; + mode = in_be32(&spi->mode); + if ((mode & mask) != bits) { + /* Must clear mode[EN] while changing speed. */ + mode &= ~(mask | SPI_MODE_EN); + out_be32(&spi->mode, mode); + mode |= bits; + out_be32(&spi->mode, mode); + mode |= SPI_MODE_EN; + out_be32(&spi->mode, mode); + } + + debug("requested speed %u, set speed to %lu/(%s4*%u) == %lu\n", + speed, priv->clk_rate, div16 ? "16*" : "", pm + 1, + clk/(4*(pm + 1))); + return 0; } From 7f6b0f3357bd25c7ed23f8f5a76a99ab9a0fd398 Mon Sep 17 00:00:00 2001 From: Michal Simek Date: Thu, 26 Mar 2020 15:01:29 +0100 Subject: [PATCH 10/10] net: macb: Fix incorrect write function name when MACB_ZYNQ is enabled. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit When MACB_ZYNQ is enabled there is compilation warnings drivers/net/macb.c: In function ‘_macb_init’: drivers/net/macb.h:675:33: error: ‘MACB_DMACFG’ undeclared (first use in this function); did you mean ‘MACB_MCF’? writel((value), (port)->regs + MACB_##reg) ^~~~~ It has been caused by changing macros name by commit below. Fixes: 6c636514d499 ("net: macb: sync header definitions as taken from Linux") Signed-off-by: Michal Simek Acked-by: Joe Hershberger --- drivers/net/macb.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/net/macb.c b/drivers/net/macb.c index 631b53b093..bd588cab06 100644 --- a/drivers/net/macb.c +++ b/drivers/net/macb.c @@ -807,7 +807,7 @@ static int _macb_init(struct macb_device *macb, const char *name) macb->next_rx_tail = 0; #ifdef CONFIG_MACB_ZYNQ - macb_writel(macb, DMACFG, MACB_ZYNQ_GEM_DMACR_INIT); + gem_writel(macb, DMACFG, MACB_ZYNQ_GEM_DMACR_INIT); #endif macb_writel(macb, RBQP, macb->rx_ring_dma);