diff options
author | Rasmus Villemoes <rasmus.villemoes@prevas.dk> | 2019-03-06 13:32:05 +0300 |
---|---|---|
committer | Mark Brown <broonie@kernel.org> | 2019-03-18 17:47:24 +0300 |
commit | 69b921acae8a5b7feef03921d9b42c3634f3b9d1 (patch) | |
tree | e58070328b6fbd2601d2f300fa3315b2f75b9691 /drivers/spi/spi-fsl-spi.c | |
parent | 84d8df745b622ada89ef427541ad03e928269f09 (diff) | |
download | linux-69b921acae8a5b7feef03921d9b42c3634f3b9d1.tar.xz |
spi: spi-fsl-spi: support use of the SPISEL_BOOT signal on MPC8309
The MPC8309 has a dedicated signal, SPISEL_BOOT, usually used as chip
select for the flash device from which the bootloader is loaded. It is
not an ordinary gpio, but is simply controlled via the SPI_CS register
in the system configuration.
To allow accessing such a spi slave, we need to teach
fsl_spi_cs_control() how to control the SPISEL_BOOT signal. To
distinguish the gpio-controlled slaves, continue to have those use
chip_select values of 0..ngpios-1, and use chip_select == ngpios for
the boot flash.
I'm not too happy with all the ifdeffery, but it seems to be necessary
for guarding the sysdev/fsl_soc.h and use of
get_immrbase() (spi-fsl-lib.c already contains similar ifdeffery).
Googling suggests that the MPC8306 is similar, with the SPI_CS
register at the same offset.
Signed-off-by: Rasmus Villemoes <rasmus.villemoes@prevas.dk>
Signed-off-by: Mark Brown <broonie@kernel.org>
Diffstat (limited to 'drivers/spi/spi-fsl-spi.c')
-rw-r--r-- | drivers/spi/spi-fsl-spi.c | 40 |
1 files changed, 35 insertions, 5 deletions
diff --git a/drivers/spi/spi-fsl-spi.c b/drivers/spi/spi-fsl-spi.c index 8f2e97857e8b..3d7b50c65f36 100644 --- a/drivers/spi/spi-fsl-spi.c +++ b/drivers/spi/spi-fsl-spi.c @@ -39,6 +39,14 @@ #include <linux/spi/spi_bitbang.h> #include <linux/types.h> +#ifdef CONFIG_FSL_SOC +#include <sysdev/fsl_soc.h> +#endif + +/* Specific to the MPC8306/MPC8309 */ +#define IMMR_SPI_CS_OFFSET 0x14c +#define SPI_BOOT_SEL_BIT 0x80000000 + #include "spi-fsl-lib.h" #include "spi-fsl-cpm.h" #include "spi-fsl-spi.h" @@ -701,10 +709,17 @@ static void fsl_spi_cs_control(struct spi_device *spi, bool on) struct fsl_spi_platform_data *pdata = dev_get_platdata(dev); struct mpc8xxx_spi_probe_info *pinfo = to_of_pinfo(pdata); u16 cs = spi->chip_select; - int gpio = pinfo->gpios[cs]; - bool alow = pinfo->alow_flags[cs]; - gpio_set_value(gpio, on ^ alow); + if (cs < pinfo->ngpios) { + int gpio = pinfo->gpios[cs]; + bool alow = pinfo->alow_flags[cs]; + + gpio_set_value(gpio, on ^ alow); + } else { + if (WARN_ON_ONCE(cs > pinfo->ngpios || !pinfo->immr_spi_cs)) + return; + iowrite32be(on ? SPI_BOOT_SEL_BIT : 0, pinfo->immr_spi_cs); + } } static int of_fsl_spi_get_chipselects(struct device *dev) @@ -712,12 +727,15 @@ static int of_fsl_spi_get_chipselects(struct device *dev) struct device_node *np = dev->of_node; struct fsl_spi_platform_data *pdata = dev_get_platdata(dev); struct mpc8xxx_spi_probe_info *pinfo = to_of_pinfo(pdata); + bool spisel_boot = IS_ENABLED(CONFIG_FSL_SOC) && + of_property_read_bool(np, "fsl,spisel_boot"); int ngpios; int i = 0; int ret; ngpios = of_gpio_count(np); - if (ngpios <= 0) { + ngpios = max(ngpios, 0); + if (ngpios == 0 && !spisel_boot) { /* * SPI w/o chip-select line. One SPI device is still permitted * though. @@ -726,6 +744,7 @@ static int of_fsl_spi_get_chipselects(struct device *dev) return 0; } + pinfo->ngpios = ngpios; pinfo->gpios = kmalloc_array(ngpios, sizeof(*pinfo->gpios), GFP_KERNEL); if (!pinfo->gpios) @@ -769,7 +788,18 @@ static int of_fsl_spi_get_chipselects(struct device *dev) } } - pdata->max_chipselect = ngpios; +#if IS_ENABLED(CONFIG_FSL_SOC) + if (spisel_boot) { + pinfo->immr_spi_cs = ioremap(get_immrbase() + IMMR_SPI_CS_OFFSET, 4); + if (!pinfo->immr_spi_cs) { + ret = -ENOMEM; + i = ngpios - 1; + goto err_loop; + } + } +#endif + + pdata->max_chipselect = ngpios + spisel_boot; pdata->cs_control = fsl_spi_cs_control; return 0; |