Commit 4856cc7a972a7679cdbb33532ad932a3e865b18e

Authored by Rasmus Villemoes
Committed by Tom Rini
1 parent 391c40048b

mpc8xxx_spi: implement real ->set_speed

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 <khso@prevas.dk>
Signed-off-by: Rasmus Villemoes <rasmus.villemoes@prevas.dk>

Showing 1 changed file with 53 additions and 11 deletions Side-by-side Diff

drivers/spi/mpc8xxx_spi.c
... ... @@ -5,6 +5,7 @@
5 5 */
6 6  
7 7 #include <common.h>
  8 +#include <clk.h>
8 9 #include <dm.h>
9 10 #include <errno.h>
10 11 #include <malloc.h>
... ... @@ -28,6 +29,7 @@
28 29  
29 30 SPI_MODE_LEN_MASK = 0xf00000,
30 31 SPI_MODE_LEN_SHIFT = 20,
  32 + SPI_MODE_PM_SHIFT = 16,
31 33 SPI_MODE_PM_MASK = 0xf0000,
32 34  
33 35 SPI_COM_LST = BIT(31 - 9),
34 36  
35 37  
36 38  
... ... @@ -37,24 +39,19 @@
37 39 spi8xxx_t *spi;
38 40 struct gpio_desc gpios[16];
39 41 int cs_count;
  42 + ulong clk_rate;
40 43 };
41 44  
42   -static inline u32 to_prescale_mod(u32 val)
43   -{
44   - return (min(val, (u32)15) << 16);
45   -}
46   -
47 45 #define SPI_TIMEOUT 1000
48 46  
49 47 static int mpc8xxx_spi_ofdata_to_platdata(struct udevice *dev)
50 48 {
51 49 struct mpc8xxx_priv *priv = dev_get_priv(dev);
  50 + struct clk clk;
52 51 int ret;
53 52  
54 53 priv->spi = (spi8xxx_t *)dev_read_addr(dev);
55 54  
56   - /* TODO(mario.six@gdsys.cc): Read clock and save the value */
57   -
58 55 ret = gpio_request_list_by_name(dev, "gpios", priv->gpios,
59 56 ARRAY_SIZE(priv->gpios), GPIOD_IS_OUT | GPIOD_ACTIVE_LOW);
60 57 if (ret < 0)
... ... @@ -62,6 +59,18 @@
62 59  
63 60 priv->cs_count = ret;
64 61  
  62 + ret = clk_get_by_index(dev, 0, &clk);
  63 + if (ret) {
  64 + dev_err(dev, "%s: clock not defined\n", __func__);
  65 + return ret;
  66 + }
  67 +
  68 + priv->clk_rate = clk_get_rate(&clk);
  69 + if (!priv->clk_rate) {
  70 + dev_err(dev, "%s: failed to get clock rate\n", __func__);
  71 + return -EINVAL;
  72 + }
  73 +
65 74 return 0;
66 75 }
67 76  
... ... @@ -79,10 +88,6 @@
79 88 /* set len to 8 bits */
80 89 setbits_be32(&spi->mode, (8 - 1) << SPI_MODE_LEN_SHIFT);
81 90  
82   - /* TODO(mario.six@gdsys.cc): This only ever sets one fixed speed */
83   - /* Use SYSCLK / 8 (16.67MHz typ.) */
84   - clrsetbits_be32(&spi->mode, SPI_MODE_PM_MASK, to_prescale_mod(1));
85   -
86 91 setbits_be32(&spi->mode, SPI_MODE_EN);
87 92  
88 93 /* Clear all SPI events */
... ... @@ -204,6 +209,43 @@
204 209  
205 210 static int mpc8xxx_spi_set_speed(struct udevice *dev, uint speed)
206 211 {
  212 + struct mpc8xxx_priv *priv = dev_get_priv(dev);
  213 + spi8xxx_t *spi = priv->spi;
  214 + u32 bits, mask, div16, pm;
  215 + u32 mode;
  216 + ulong clk;
  217 +
  218 + clk = priv->clk_rate;
  219 + if (clk / 64 > speed) {
  220 + div16 = SPI_MODE_DIV16;
  221 + clk /= 16;
  222 + } else {
  223 + div16 = 0;
  224 + }
  225 + pm = (clk - 1)/(4*speed) + 1;
  226 + if (pm > 16) {
  227 + dev_err(dev, "requested speed %u too small\n", speed);
  228 + return -EINVAL;
  229 + }
  230 + pm--;
  231 +
  232 + bits = div16 | (pm << SPI_MODE_PM_SHIFT);
  233 + mask = SPI_MODE_DIV16 | SPI_MODE_PM_MASK;
  234 + mode = in_be32(&spi->mode);
  235 + if ((mode & mask) != bits) {
  236 + /* Must clear mode[EN] while changing speed. */
  237 + mode &= ~(mask | SPI_MODE_EN);
  238 + out_be32(&spi->mode, mode);
  239 + mode |= bits;
  240 + out_be32(&spi->mode, mode);
  241 + mode |= SPI_MODE_EN;
  242 + out_be32(&spi->mode, mode);
  243 + }
  244 +
  245 + debug("requested speed %u, set speed to %lu/(%s4*%u) == %lu\n",
  246 + speed, priv->clk_rate, div16 ? "16*" : "", pm + 1,
  247 + clk/(4*(pm + 1)));
  248 +
207 249 return 0;
208 250 }
209 251