From patchwork Mon Jan 6 14:09:46 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Rasmus Villemoes X-Patchwork-Id: 239160 List-Id: U-Boot discussion From: rasmus.villemoes at prevas.dk (Rasmus Villemoes) Date: Mon, 6 Jan 2020 14:09:46 +0000 Subject: [PATCH 1/4] gazerbeam: add clocks property to SPI node In-Reply-To: <20200106140931.1136-1-rasmus.villemoes@prevas.dk> References: <20200106140931.1136-1-rasmus.villemoes@prevas.dk> Message-ID: <20200106140931.1136-2-rasmus.villemoes@prevas.dk> 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 at 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 patchwork Mon Jan 6 14:09:47 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Rasmus Villemoes X-Patchwork-Id: 239159 List-Id: U-Boot discussion From: rasmus.villemoes at prevas.dk (Rasmus Villemoes) Date: Mon, 6 Jan 2020 14:09:47 +0000 Subject: [PATCH 2/4] mpc8xxx_spi: put max_cs to use In-Reply-To: <20200106140931.1136-1-rasmus.villemoes@prevas.dk> References: <20200106140931.1136-1-rasmus.villemoes@prevas.dk> Message-ID: <20200106140931.1136-3-rasmus.villemoes@prevas.dk> 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 patchwork Mon Jan 6 14:09:49 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-Patchwork-Submitter: Rasmus Villemoes X-Patchwork-Id: 239161 List-Id: U-Boot discussion From: rasmus.villemoes at prevas.dk (Rasmus Villemoes) Date: Mon, 6 Jan 2020 14:09:49 +0000 Subject: [PATCH 3/4] mpc8xxx_spi: always use 8-bit characters, don't read or write garbage In-Reply-To: <20200106140931.1136-1-rasmus.villemoes@prevas.dk> References: <20200106140931.1136-1-rasmus.villemoes@prevas.dk> Message-ID: <20200106140931.1136-4-rasmus.villemoes@prevas.dk> 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 at 7000:0 dout 0FB53618 din 00000000 bitlen 8 mpc8xxx_spi_xfer: slave spi at 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 at 7000:0 dout 0FB536E8 din 00000000 bitlen 40 mpc8xxx_spi_xfer: slave spi at 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 at 7000:0 dout 0FB53848 din 00000000 bitlen 40 mpc8xxx_spi_xfer: slave spi at 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 at 7000:0 dout 0FB536E8 din 00000000 bitlen 40 mpc8xxx_spi_xfer: slave spi at 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 at 7000:0 dout 0FB536E8 din 00000000 bitlen 40 mpc8xxx_spi_xfer: slave spi at 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 at 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); + + /* set len to 8 bits */ + setbits_be32(&spi->mode, (8 - 1) << SPI_MODE_LEN_SHIFT); - __spi_set_speed(priv->spi, 16666667); + /* TODO(mario.six at 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 patchwork Mon Jan 6 14:09:50 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-Patchwork-Submitter: Rasmus Villemoes X-Patchwork-Id: 239162 List-Id: U-Boot discussion From: rasmus.villemoes at prevas.dk (Rasmus Villemoes) Date: Mon, 6 Jan 2020 14:09:50 +0000 Subject: [PATCH 4/4] mpc8xxx_spi: implement real ->set_speed In-Reply-To: <20200106140931.1136-1-rasmus.villemoes@prevas.dk> References: <20200106140931.1136-1-rasmus.villemoes@prevas.dk> Message-ID: <20200106140931.1136-5-rasmus.villemoes@prevas.dk> 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 at 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 at 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; }