]> asedeno.scripts.mit.edu Git - linux.git/blobdiff - drivers/spi/spi-gpio.c
spi: bcm2835: Drop dma_pending flag
[linux.git] / drivers / spi / spi-gpio.c
index 53b35c56a557ae8a7f6ffbfaa23ae6a02e3853de..1d3e23ec20a61fd926d29585fbd29d4d9d9d1593 100644 (file)
@@ -1,18 +1,9 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
 /*
  * SPI master driver using generic bitbanged GPIO
  *
  * Copyright (C) 2006,2008 David Brownell
  * Copyright (C) 2017 Linus Walleij
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
  */
 #include <linux/kernel.h>
 #include <linux/module.h>
  * platform_device->driver_data ... points to spi_gpio
  *
  * spi->controller_state ... reserved for bitbang framework code
- * spi->controller_data ... holds chipselect GPIO
  *
  * spi->master->dev.driver_data ... points to spi_gpio->bitbang
  */
 
 struct spi_gpio {
        struct spi_bitbang              bitbang;
-       struct spi_gpio_platform_data   pdata;
-       struct platform_device          *pdev;
        struct gpio_desc                *sck;
        struct gpio_desc                *miso;
        struct gpio_desc                *mosi;
        struct gpio_desc                **cs_gpios;
-       bool                            has_cs;
 };
 
 /*----------------------------------------------------------------------*/
@@ -96,12 +83,6 @@ spi_to_spi_gpio(const struct spi_device *spi)
        return spi_gpio;
 }
 
-static inline struct spi_gpio_platform_data *__pure
-spi_to_pdata(const struct spi_device *spi)
-{
-       return &spi_to_spi_gpio(spi)->pdata;
-}
-
 /* These helpers are in turn called by the bitbang inlines */
 static inline void setsck(const struct spi_device *spi, int is_on)
 {
@@ -224,7 +205,7 @@ static void spi_gpio_chipselect(struct spi_device *spi, int is_active)
                gpiod_set_value_cansleep(spi_gpio->sck, spi->mode & SPI_CPOL);
 
        /* Drive chip select line, if we have one */
-       if (spi_gpio->has_cs) {
+       if (spi_gpio->cs_gpios) {
                struct gpio_desc *cs = spi_gpio->cs_gpios[spi->chip_select];
 
                /* SPI chip selects are normally active-low */
@@ -242,10 +223,12 @@ static int spi_gpio_setup(struct spi_device *spi)
         * The CS GPIOs have already been
         * initialized from the descriptor lookup.
         */
-       cs = spi_gpio->cs_gpios[spi->chip_select];
-       if (!spi->controller_state && cs)
-               status = gpiod_direction_output(cs,
-                                               !(spi->mode & SPI_CS_HIGH));
+       if (spi_gpio->cs_gpios) {
+               cs = spi_gpio->cs_gpios[spi->chip_select];
+               if (!spi->controller_state && cs)
+                       status = gpiod_direction_output(cs,
+                                                 !(spi->mode & SPI_CS_HIGH));
+       }
 
        if (!status)
                status = spi_bitbang_setup(spi);
@@ -296,41 +279,18 @@ static void spi_gpio_cleanup(struct spi_device *spi)
  * floating signals.  (A weak pulldown would save power too, but many
  * drivers expect to see all-ones data as the no slave "response".)
  */
-static int spi_gpio_request(struct device *dev,
-                           struct spi_gpio *spi_gpio,
-                           unsigned int num_chipselects,
-                           u16 *mflags)
+static int spi_gpio_request(struct device *dev, struct spi_gpio *spi_gpio)
 {
-       int i;
-
        spi_gpio->mosi = devm_gpiod_get_optional(dev, "mosi", GPIOD_OUT_LOW);
        if (IS_ERR(spi_gpio->mosi))
                return PTR_ERR(spi_gpio->mosi);
-       if (!spi_gpio->mosi)
-               /* HW configuration without MOSI pin */
-               *mflags |= SPI_MASTER_NO_TX;
 
        spi_gpio->miso = devm_gpiod_get_optional(dev, "miso", GPIOD_IN);
        if (IS_ERR(spi_gpio->miso))
                return PTR_ERR(spi_gpio->miso);
-       /*
-        * No setting SPI_MASTER_NO_RX here - if there is only a MOSI
-        * pin connected the host can still do RX by changing the
-        * direction of the line.
-        */
 
        spi_gpio->sck = devm_gpiod_get(dev, "sck", GPIOD_OUT_LOW);
-       if (IS_ERR(spi_gpio->sck))
-               return PTR_ERR(spi_gpio->sck);
-
-       for (i = 0; i < num_chipselects; i++) {
-               spi_gpio->cs_gpios[i] = devm_gpiod_get_index(dev, "cs",
-                                                            i, GPIOD_OUT_HIGH);
-               if (IS_ERR(spi_gpio->cs_gpios[i]))
-                       return PTR_ERR(spi_gpio->cs_gpios[i]);
-       }
-
-       return 0;
+       return PTR_ERR_OR_ZERO(spi_gpio->sck);
 }
 
 #ifdef CONFIG_OF
@@ -340,142 +300,140 @@ static const struct of_device_id spi_gpio_dt_ids[] = {
 };
 MODULE_DEVICE_TABLE(of, spi_gpio_dt_ids);
 
-static int spi_gpio_probe_dt(struct platform_device *pdev)
+static int spi_gpio_probe_dt(struct platform_device *pdev,
+                            struct spi_master *master)
 {
-       int ret;
-       u32 tmp;
-       struct spi_gpio_platform_data   *pdata;
-       struct device_node *np = pdev->dev.of_node;
-       const struct of_device_id *of_id =
-                       of_match_device(spi_gpio_dt_ids, &pdev->dev);
-
-       if (!of_id)
-               return 0;
+       master->dev.of_node = pdev->dev.of_node;
+       master->use_gpio_descriptors = true;
 
-       pdata = devm_kzalloc(&pdev->dev, sizeof(*pdata), GFP_KERNEL);
-       if (!pdata)
-               return -ENOMEM;
+       return 0;
+}
+#else
+static inline int spi_gpio_probe_dt(struct platform_device *pdev,
+                                   struct spi_master *master)
+{
+       return 0;
+}
+#endif
 
+static int spi_gpio_probe_pdata(struct platform_device *pdev,
+                               struct spi_master *master)
+{
+       struct device *dev = &pdev->dev;
+       struct spi_gpio_platform_data *pdata = dev_get_platdata(dev);
+       struct spi_gpio *spi_gpio = spi_master_get_devdata(master);
+       int i;
 
-       ret = of_property_read_u32(np, "num-chipselects", &tmp);
-       if (ret < 0) {
-               dev_err(&pdev->dev, "num-chipselects property not found\n");
-               goto error_free;
-       }
+#ifdef GENERIC_BITBANG
+       if (!pdata || !pdata->num_chipselect)
+               return -ENODEV;
+#endif
+       /*
+        * The master needs to think there is a chipselect even if not
+        * connected
+        */
+       master->num_chipselect = pdata->num_chipselect ?: 1;
 
-       pdata->num_chipselect = tmp;
-       pdev->dev.platform_data = pdata;
+       spi_gpio->cs_gpios = devm_kcalloc(dev, master->num_chipselect,
+                                         sizeof(*spi_gpio->cs_gpios),
+                                         GFP_KERNEL);
+       if (!spi_gpio->cs_gpios)
+               return -ENOMEM;
 
-       return 1;
+       for (i = 0; i < master->num_chipselect; i++) {
+               spi_gpio->cs_gpios[i] = devm_gpiod_get_index(dev, "cs", i,
+                                                            GPIOD_OUT_HIGH);
+               if (IS_ERR(spi_gpio->cs_gpios[i]))
+                       return PTR_ERR(spi_gpio->cs_gpios[i]);
+       }
 
-error_free:
-       devm_kfree(&pdev->dev, pdata);
-       return ret;
+       return 0;
 }
-#else
-static inline int spi_gpio_probe_dt(struct platform_device *pdev)
+
+static void spi_gpio_put(void *data)
 {
-       return 0;
+       spi_master_put(data);
 }
-#endif
 
 static int spi_gpio_probe(struct platform_device *pdev)
 {
        int                             status;
        struct spi_master               *master;
        struct spi_gpio                 *spi_gpio;
-       struct spi_gpio_platform_data   *pdata;
-       u16 master_flags = 0;
-       bool use_of = 0;
+       struct device                   *dev = &pdev->dev;
+       struct spi_bitbang              *bb;
+       const struct of_device_id       *of_id;
 
-       status = spi_gpio_probe_dt(pdev);
-       if (status < 0)
-               return status;
-       if (status > 0)
-               use_of = 1;
-
-       pdata = dev_get_platdata(&pdev->dev);
-#ifdef GENERIC_BITBANG
-       if (!pdata || (!use_of && !pdata->num_chipselect))
-               return -ENODEV;
-#endif
+       of_id = of_match_device(spi_gpio_dt_ids, &pdev->dev);
 
-       master = spi_alloc_master(&pdev->dev, sizeof(*spi_gpio));
+       master = spi_alloc_master(dev, sizeof(*spi_gpio));
        if (!master)
                return -ENOMEM;
 
-       spi_gpio = spi_master_get_devdata(master);
-
-       spi_gpio->cs_gpios = devm_kcalloc(&pdev->dev,
-                               pdata->num_chipselect,
-                               sizeof(*spi_gpio->cs_gpios),
-                               GFP_KERNEL);
-       if (!spi_gpio->cs_gpios)
-               return -ENOMEM;
+       status = devm_add_action_or_reset(&pdev->dev, spi_gpio_put, master);
+       if (status)
+               return status;
 
-       platform_set_drvdata(pdev, spi_gpio);
+       if (of_id)
+               status = spi_gpio_probe_dt(pdev, master);
+       else
+               status = spi_gpio_probe_pdata(pdev, master);
 
-       /* Determine if we have chip selects connected */
-       spi_gpio->has_cs = !!pdata->num_chipselect;
+       if (status)
+               return status;
 
-       spi_gpio->pdev = pdev;
-       if (pdata)
-               spi_gpio->pdata = *pdata;
+       spi_gpio = spi_master_get_devdata(master);
 
-       status = spi_gpio_request(&pdev->dev, spi_gpio,
-                                 pdata->num_chipselect, &master_flags);
+       status = spi_gpio_request(dev, spi_gpio);
        if (status)
                return status;
 
        master->bits_per_word_mask = SPI_BPW_RANGE_MASK(1, 32);
        master->mode_bits = SPI_3WIRE | SPI_3WIRE_HIZ | SPI_CPHA | SPI_CPOL |
                            SPI_CS_HIGH;
-       master->flags = master_flags;
+       if (!spi_gpio->mosi) {
+               /* HW configuration without MOSI pin
+                *
+                * No setting SPI_MASTER_NO_RX here - if there is only
+                * a MOSI pin connected the host can still do RX by
+                * changing the direction of the line.
+                */
+               master->flags = SPI_MASTER_NO_TX;
+       }
+
        master->bus_num = pdev->id;
-       /* The master needs to think there is a chipselect even if not connected */
-       master->num_chipselect = spi_gpio->has_cs ? pdata->num_chipselect : 1;
        master->setup = spi_gpio_setup;
        master->cleanup = spi_gpio_cleanup;
-#ifdef CONFIG_OF
-       master->dev.of_node = pdev->dev.of_node;
-#endif
 
-       spi_gpio->bitbang.master = master;
-       spi_gpio->bitbang.chipselect = spi_gpio_chipselect;
-       spi_gpio->bitbang.set_line_direction = spi_gpio_set_direction;
-
-       if ((master_flags & SPI_MASTER_NO_TX) == 0) {
-               spi_gpio->bitbang.txrx_word[SPI_MODE_0] = spi_gpio_txrx_word_mode0;
-               spi_gpio->bitbang.txrx_word[SPI_MODE_1] = spi_gpio_txrx_word_mode1;
-               spi_gpio->bitbang.txrx_word[SPI_MODE_2] = spi_gpio_txrx_word_mode2;
-               spi_gpio->bitbang.txrx_word[SPI_MODE_3] = spi_gpio_txrx_word_mode3;
+       bb = &spi_gpio->bitbang;
+       bb->master = master;
+       /*
+        * There is some additional business, apart from driving the CS GPIO
+        * line, that we need to do on selection. This makes the local
+        * callback for chipselect always get called.
+        */
+       master->flags |= SPI_MASTER_GPIO_SS;
+       bb->chipselect = spi_gpio_chipselect;
+       bb->set_line_direction = spi_gpio_set_direction;
+
+       if (master->flags & SPI_MASTER_NO_TX) {
+               bb->txrx_word[SPI_MODE_0] = spi_gpio_spec_txrx_word_mode0;
+               bb->txrx_word[SPI_MODE_1] = spi_gpio_spec_txrx_word_mode1;
+               bb->txrx_word[SPI_MODE_2] = spi_gpio_spec_txrx_word_mode2;
+               bb->txrx_word[SPI_MODE_3] = spi_gpio_spec_txrx_word_mode3;
        } else {
-               spi_gpio->bitbang.txrx_word[SPI_MODE_0] = spi_gpio_spec_txrx_word_mode0;
-               spi_gpio->bitbang.txrx_word[SPI_MODE_1] = spi_gpio_spec_txrx_word_mode1;
-               spi_gpio->bitbang.txrx_word[SPI_MODE_2] = spi_gpio_spec_txrx_word_mode2;
-               spi_gpio->bitbang.txrx_word[SPI_MODE_3] = spi_gpio_spec_txrx_word_mode3;
+               bb->txrx_word[SPI_MODE_0] = spi_gpio_txrx_word_mode0;
+               bb->txrx_word[SPI_MODE_1] = spi_gpio_txrx_word_mode1;
+               bb->txrx_word[SPI_MODE_2] = spi_gpio_txrx_word_mode2;
+               bb->txrx_word[SPI_MODE_3] = spi_gpio_txrx_word_mode3;
        }
-       spi_gpio->bitbang.setup_transfer = spi_bitbang_setup_transfer;
+       bb->setup_transfer = spi_bitbang_setup_transfer;
 
-       status = spi_bitbang_start(&spi_gpio->bitbang);
+       status = spi_bitbang_init(&spi_gpio->bitbang);
        if (status)
-               spi_master_put(master);
-
-       return status;
-}
-
-static int spi_gpio_remove(struct platform_device *pdev)
-{
-       struct spi_gpio                 *spi_gpio;
-
-       spi_gpio = platform_get_drvdata(pdev);
-
-       /* stop() unregisters child devices too */
-       spi_bitbang_stop(&spi_gpio->bitbang);
-
-       spi_master_put(spi_gpio->bitbang.master);
+               return status;
 
-       return 0;
+       return devm_spi_register_master(&pdev->dev, spi_master_get(master));
 }
 
 MODULE_ALIAS("platform:" DRIVER_NAME);
@@ -486,7 +444,6 @@ static struct platform_driver spi_gpio_driver = {
                .of_match_table = of_match_ptr(spi_gpio_dt_ids),
        },
        .probe          = spi_gpio_probe,
-       .remove         = spi_gpio_remove,
 };
 module_platform_driver(spi_gpio_driver);