diff options
Diffstat (limited to 'drivers/spi/spi-sunxi.c')
-rw-r--r-- | drivers/spi/spi-sunxi.c | 38 |
1 files changed, 18 insertions, 20 deletions
diff --git a/drivers/spi/spi-sunxi.c b/drivers/spi/spi-sunxi.c index 9ec6b359e22..88550b8ea84 100644 --- a/drivers/spi/spi-sunxi.c +++ b/drivers/spi/spi-sunxi.c @@ -18,7 +18,6 @@ * SPDX-License-Identifier: GPL-2.0+ */ -#include <common.h> #include <clk.h> #include <dm.h> #include <log.h> @@ -136,7 +135,6 @@ struct sun4i_spi_variant { struct sun4i_spi_plat { struct sun4i_spi_variant *variant; u32 base; - u32 max_hz; }; struct sun4i_spi_priv { @@ -235,15 +233,24 @@ err_ahb: static void sun4i_spi_set_speed_mode(struct udevice *dev) { struct sun4i_spi_priv *priv = dev_get_priv(dev); - unsigned int div; + unsigned int div, div_cdr2; u32 reg; /* + * The uclass should take care that this won't happen. But anyway, + * avoid a div-by-zero exception. + */ + if (!priv->freq) + return; + + /* * Setup clock divider. * * We have two choices there. Either we can use the clock * divide rate 1, which is calculated thanks to this formula: * SPI_CLK = MOD_CLK / (2 ^ (cdr + 1)) + * Or for sun6i/sun8i variants: + * SPI_CLK = MOD_CLK / (2 ^ cdr) * Or we can use CDR2, which is calculated with the formula: * SPI_CLK = MOD_CLK / (2 * (cdr + 1)) * Whether we use the former or the latter is set through the @@ -251,18 +258,18 @@ static void sun4i_spi_set_speed_mode(struct udevice *dev) * * First try CDR2, and if we can't reach the expected * frequency, fall back to CDR1. + * There is one exception if the requested clock is the input + * clock. In that case we always use CDR1 because we'll get a + * 1:1 ration for sun6i/sun8i variants. */ div = DIV_ROUND_UP(SUNXI_INPUT_CLOCK, priv->freq); + div_cdr2 = DIV_ROUND_UP(div, 2); reg = readl(SPI_REG(priv, SPI_CCR)); - if ((div / 2) <= (SUN4I_CLK_CTL_CDR2_MASK + 1)) { - div /= 2; - if (div > 0) - div--; - + if (div != 1 && (div_cdr2 <= (SUN4I_CLK_CTL_CDR2_MASK + 1))) { reg &= ~(SUN4I_CLK_CTL_CDR2_MASK | SUN4I_CLK_CTL_DRS); - reg |= SUN4I_CLK_CTL_CDR2(div) | SUN4I_CLK_CTL_DRS; + reg |= SUN4I_CLK_CTL_CDR2(div_cdr2 - 1) | SUN4I_CLK_CTL_DRS; } else { div = fls(div - 1); /* The F1C100s encodes the divider as 2^(n+1) */ @@ -402,11 +409,10 @@ static int sun4i_spi_xfer(struct udevice *dev, unsigned int bitlen, static int sun4i_spi_set_speed(struct udevice *dev, uint speed) { - struct sun4i_spi_plat *plat = dev_get_plat(dev); struct sun4i_spi_priv *priv = dev_get_priv(dev); - if (speed > plat->max_hz) - speed = plat->max_hz; + if (speed > SUN4I_SPI_MAX_RATE) + speed = SUN4I_SPI_MAX_RATE; if (speed < SUN4I_SPI_MIN_RATE) speed = SUN4I_SPI_MIN_RATE; @@ -459,7 +465,6 @@ static int sun4i_spi_probe(struct udevice *bus) priv->variant = plat->variant; priv->base = plat->base; - priv->freq = plat->max_hz; return 0; } @@ -467,16 +472,9 @@ static int sun4i_spi_probe(struct udevice *bus) static int sun4i_spi_of_to_plat(struct udevice *bus) { struct sun4i_spi_plat *plat = dev_get_plat(bus); - int node = dev_of_offset(bus); plat->base = dev_read_addr(bus); plat->variant = (struct sun4i_spi_variant *)dev_get_driver_data(bus); - plat->max_hz = fdtdec_get_int(gd->fdt_blob, node, - "spi-max-frequency", - SUN4I_SPI_DEFAULT_RATE); - - if (plat->max_hz > SUN4I_SPI_MAX_RATE) - plat->max_hz = SUN4I_SPI_MAX_RATE; return 0; } |