Skip to content

Commit

Permalink
spi: fsl: Convert to use CS GPIO descriptors
Browse files Browse the repository at this point in the history
This converts the Freescale SPI master driver to use GPIO
descriptors for chip select handling.

The Freescale (fsl) driver has a lot of quirks to look up
"gpios" rather than "cs-gpios" from the device tree.
After the prior patch that will make gpiolib return the
GPIO descriptor for "gpios" in response to a request for
"cs-gpios", this code can be cut down quite a bit.

The driver has custom handling of chip select rather
than using the core (which may be possible but not
done in this patch) so it still needs to refer directly
to spi->cs_gpiod to set the chip select.

Cc: Shawn Guo <shawnguo@kernel.org>
Cc: Sascha Hauer <s.hauer@pengutronix.de>
Cc: Pengutronix Kernel Team <kernel@pengutronix.de>
Cc: Fabio Estevam <festevam@gmail.com>
Cc: NXP Linux Team <linux-imx@nxp.com>
Signed-off-by: Linus Walleij <linus.walleij@linaro.org>
Link: https://lore.kernel.org/r/20190804003539.985-1-linus.walleij@linaro.org
Signed-off-by: Mark Brown <broonie@kernel.org>
  • Loading branch information
linusw authored and broonie committed Aug 28, 2019
1 parent c55be30 commit 0f0581b
Show file tree
Hide file tree
Showing 2 changed files with 25 additions and 171 deletions.
3 changes: 0 additions & 3 deletions drivers/spi/spi-fsl-lib.h
Expand Up @@ -91,9 +91,6 @@ static inline u32 mpc8xxx_spi_read_reg(__be32 __iomem *reg)

struct mpc8xxx_spi_probe_info {
struct fsl_spi_platform_data pdata;
int ngpios;
int *gpios;
bool *alow_flags;
__be32 __iomem *immr_spi_cs;
};

Expand Down
193 changes: 25 additions & 168 deletions drivers/spi/spi-fsl-spi.c
Expand Up @@ -18,7 +18,7 @@
#include <linux/delay.h>
#include <linux/dma-mapping.h>
#include <linux/fsl_devices.h>
#include <linux/gpio.h>
#include <linux/gpio/consumer.h>
#include <linux/interrupt.h>
#include <linux/irq.h>
#include <linux/kernel.h>
Expand All @@ -28,7 +28,6 @@
#include <linux/of.h>
#include <linux/of_address.h>
#include <linux/of_irq.h>
#include <linux/of_gpio.h>
#include <linux/of_platform.h>
#include <linux/platform_device.h>
#include <linux/spi/spi.h>
Expand Down Expand Up @@ -481,32 +480,6 @@ static int fsl_spi_setup(struct spi_device *spi)
return retval;
}

if (mpc8xxx_spi->type == TYPE_GRLIB) {
if (gpio_is_valid(spi->cs_gpio)) {
int desel;

retval = gpio_request(spi->cs_gpio,
dev_name(&spi->dev));
if (retval)
return retval;

desel = !(spi->mode & SPI_CS_HIGH);
retval = gpio_direction_output(spi->cs_gpio, desel);
if (retval) {
gpio_free(spi->cs_gpio);
return retval;
}
} else if (spi->cs_gpio != -ENOENT) {
if (spi->cs_gpio < 0)
return spi->cs_gpio;
return -EINVAL;
}
/* When spi->cs_gpio == -ENOENT, a hole in the phandle list
* indicates to use native chipselect if present, or allow for
* an always selected chip
*/
}

/* Initialize chipselect - might be active for SPI_CS_HIGH mode */
fsl_spi_chipselect(spi, BITBANG_CS_INACTIVE);

Expand All @@ -515,12 +488,8 @@ static int fsl_spi_setup(struct spi_device *spi)

static void fsl_spi_cleanup(struct spi_device *spi)
{
struct mpc8xxx_spi *mpc8xxx_spi = spi_master_get_devdata(spi->master);
struct spi_mpc8xxx_cs *cs = spi_get_ctldata(spi);

if (mpc8xxx_spi->type == TYPE_GRLIB && gpio_is_valid(spi->cs_gpio))
gpio_free(spi->cs_gpio);

kfree(cs);
spi_set_ctldata(spi, NULL);
}
Expand Down Expand Up @@ -586,8 +555,8 @@ static void fsl_spi_grlib_cs_control(struct spi_device *spi, bool on)
u32 slvsel;
u16 cs = spi->chip_select;

if (gpio_is_valid(spi->cs_gpio)) {
gpio_set_value(spi->cs_gpio, on);
if (spi->cs_gpiod) {
gpiod_set_value(spi->cs_gpiod, on);
} else if (cs < mpc8xxx_spi->native_chipselects) {
slvsel = mpc8xxx_spi_read_reg(&reg_base->slvsel);
slvsel = on ? (slvsel | (1 << cs)) : (slvsel & ~(1 << cs));
Expand Down Expand Up @@ -718,139 +687,19 @@ static struct spi_master * fsl_spi_probe(struct device *dev,

static void fsl_spi_cs_control(struct spi_device *spi, bool on)
{
struct device *dev = spi->dev.parent->parent;
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;

if (cs < pinfo->ngpios) {
int gpio = pinfo->gpios[cs];
bool alow = pinfo->alow_flags[cs];

gpio_set_value(gpio, on ^ alow);
if (spi->cs_gpiod) {
gpiod_set_value(spi->cs_gpiod, on);
} else {
if (WARN_ON_ONCE(cs > pinfo->ngpios || !pinfo->immr_spi_cs))
struct device *dev = spi->dev.parent->parent;
struct fsl_spi_platform_data *pdata = dev_get_platdata(dev);
struct mpc8xxx_spi_probe_info *pinfo = to_of_pinfo(pdata);

if (WARN_ON_ONCE(!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)
{
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);
ngpios = max(ngpios, 0);
if (ngpios == 0 && !spisel_boot) {
/*
* SPI w/o chip-select line. One SPI device is still permitted
* though.
*/
pdata->max_chipselect = 1;
return 0;
}

pinfo->ngpios = ngpios;
pinfo->gpios = kmalloc_array(ngpios, sizeof(*pinfo->gpios),
GFP_KERNEL);
if (!pinfo->gpios)
return -ENOMEM;
memset(pinfo->gpios, -1, ngpios * sizeof(*pinfo->gpios));

pinfo->alow_flags = kcalloc(ngpios, sizeof(*pinfo->alow_flags),
GFP_KERNEL);
if (!pinfo->alow_flags) {
ret = -ENOMEM;
goto err_alloc_flags;
}

for (; i < ngpios; i++) {
int gpio;
enum of_gpio_flags flags;

gpio = of_get_gpio_flags(np, i, &flags);
if (!gpio_is_valid(gpio)) {
dev_err(dev, "invalid gpio #%d: %d\n", i, gpio);
ret = gpio;
goto err_loop;
}

ret = gpio_request(gpio, dev_name(dev));
if (ret) {
dev_err(dev, "can't request gpio #%d: %d\n", i, ret);
goto err_loop;
}

pinfo->gpios[i] = gpio;
pinfo->alow_flags[i] = flags & OF_GPIO_ACTIVE_LOW;

ret = gpio_direction_output(pinfo->gpios[i],
pinfo->alow_flags[i]);
if (ret) {
dev_err(dev,
"can't set output direction for gpio #%d: %d\n",
i, ret);
goto err_loop;
}
}

#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;

err_loop:
while (i >= 0) {
if (gpio_is_valid(pinfo->gpios[i]))
gpio_free(pinfo->gpios[i]);
i--;
}

kfree(pinfo->alow_flags);
pinfo->alow_flags = NULL;
err_alloc_flags:
kfree(pinfo->gpios);
pinfo->gpios = NULL;
return ret;
}

static int of_fsl_spi_free_chipselects(struct device *dev)
{
struct fsl_spi_platform_data *pdata = dev_get_platdata(dev);
struct mpc8xxx_spi_probe_info *pinfo = to_of_pinfo(pdata);
int i;

if (!pinfo->gpios)
return 0;

for (i = 0; i < pdata->max_chipselect; i++) {
if (gpio_is_valid(pinfo->gpios[i]))
gpio_free(pinfo->gpios[i]);
}

kfree(pinfo->gpios);
kfree(pinfo->alow_flags);
return 0;
}

static int of_fsl_spi_probe(struct platform_device *ofdev)
{
struct device *dev = &ofdev->dev;
Expand All @@ -866,9 +715,21 @@ static int of_fsl_spi_probe(struct platform_device *ofdev)

type = fsl_spi_get_type(&ofdev->dev);
if (type == TYPE_FSL) {
ret = of_fsl_spi_get_chipselects(dev);
if (ret)
goto err;
struct fsl_spi_platform_data *pdata = dev_get_platdata(dev);
#if IS_ENABLED(CONFIG_FSL_SOC)
struct mpc8xxx_spi_probe_info *pinfo = to_of_pinfo(pdata);
bool spisel_boot = of_property_read_bool(np, "fsl,spisel_boot");

if (spisel_boot) {
pinfo->immr_spi_cs = ioremap(get_immrbase() + IMMR_SPI_CS_OFFSET, 4);
if (!pinfo->immr_spi_cs) {
ret = -ENOMEM;
goto err;
}
}
#endif

pdata->cs_control = fsl_spi_cs_control;
}

ret = of_address_to_resource(np, 0, &mem);
Expand All @@ -891,8 +752,6 @@ static int of_fsl_spi_probe(struct platform_device *ofdev)

err:
irq_dispose_mapping(irq);
if (type == TYPE_FSL)
of_fsl_spi_free_chipselects(dev);
return ret;
}

Expand All @@ -902,8 +761,6 @@ static int of_fsl_spi_remove(struct platform_device *ofdev)
struct mpc8xxx_spi *mpc8xxx_spi = spi_master_get_devdata(master);

fsl_spi_cpm_free(mpc8xxx_spi);
if (mpc8xxx_spi->type == TYPE_FSL)
of_fsl_spi_free_chipselects(&ofdev->dev);
return 0;
}

Expand Down

0 comments on commit 0f0581b

Please sign in to comment.