#include <linux/spi/spi.h>
 #include <linux/spi/spi-mem.h>
 #include <linux/of_gpio.h>
+#include <linux/gpio/consumer.h>
 #include <linux/pm_runtime.h>
 #include <linux/pm_domain.h>
 #include <linux/property.h>
                goto done;
        }
 
-       if (ctlr->cs_gpios)
+       /* Descriptors take precedence */
+       if (ctlr->cs_gpiods)
+               spi->cs_gpiod = ctlr->cs_gpiods[spi->chip_select];
+       else if (ctlr->cs_gpios)
                spi->cs_gpio = ctlr->cs_gpios[spi->chip_select];
 
        /* Drivers may modify this initial i/o setup, but will
        if (spi->mode & SPI_CS_HIGH)
                enable = !enable;
 
-       if (gpio_is_valid(spi->cs_gpio)) {
-               /* Honour the SPI_NO_CS flag */
-               if (!(spi->mode & SPI_NO_CS))
-                       gpio_set_value(spi->cs_gpio, !enable);
+       if (spi->cs_gpiod || gpio_is_valid(spi->cs_gpio)) {
+               /*
+                * Honour the SPI_NO_CS flag and invert the enable line, as
+                * active low is default for SPI. Execution paths that handle
+                * polarity inversion in gpiolib (such as device tree) will
+                * enforce active high using the SPI_CS_HIGH resulting in a
+                * double inversion through the code above.
+                */
+               if (!(spi->mode & SPI_NO_CS)) {
+                       if (spi->cs_gpiod)
+                               gpiod_set_value(spi->cs_gpiod, !enable);
+                       else
+                               gpio_set_value(spi->cs_gpio, !enable);
+               }
                /* Some SPI masters need both GPIO CS & slave_select */
                if ((spi->controller->flags & SPI_MASTER_GPIO_SS) &&
                    spi->controller->set_cs)
                spi->mode |= SPI_CPHA;
        if (of_property_read_bool(nc, "spi-cpol"))
                spi->mode |= SPI_CPOL;
-       if (of_property_read_bool(nc, "spi-cs-high"))
-               spi->mode |= SPI_CS_HIGH;
        if (of_property_read_bool(nc, "spi-3wire"))
                spi->mode |= SPI_3WIRE;
        if (of_property_read_bool(nc, "spi-lsb-first"))
                spi->mode |= SPI_LSB_FIRST;
 
+       /*
+        * For descriptors associated with the device, polarity inversion is
+        * handled in the gpiolib, so all chip selects are "active high" in
+        * the logical sense, the gpiolib will invert the line if need be.
+        */
+       if (ctlr->use_gpio_descriptors)
+               spi->mode |= SPI_CS_HIGH;
+       else if (of_property_read_bool(nc, "spi-cs-high"))
+               spi->mode |= SPI_CS_HIGH;
+
        /* Device DUAL/QUAD mode */
        if (!of_property_read_u32(nc, "spi-tx-bus-width", &value)) {
                switch (value) {
 }
 #endif
 
+/**
+ * spi_get_gpio_descs() - grab chip select GPIOs for the master
+ * @ctlr: The SPI master to grab GPIO descriptors for
+ */
+static int spi_get_gpio_descs(struct spi_controller *ctlr)
+{
+       int nb, i;
+       struct gpio_desc **cs;
+       struct device *dev = &ctlr->dev;
+
+       nb = gpiod_count(dev, "cs");
+       ctlr->num_chipselect = max_t(int, nb, ctlr->num_chipselect);
+
+       /* No GPIOs at all is fine, else return the error */
+       if (nb == 0 || nb == -ENOENT)
+               return 0;
+       else if (nb < 0)
+               return nb;
+
+       cs = devm_kcalloc(dev, ctlr->num_chipselect, sizeof(*cs),
+                         GFP_KERNEL);
+       if (!cs)
+               return -ENOMEM;
+       ctlr->cs_gpiods = cs;
+
+       for (i = 0; i < nb; i++) {
+               /*
+                * Most chipselects are active low, the inverted
+                * semantics are handled by special quirks in gpiolib,
+                * so initializing them GPIOD_OUT_LOW here means
+                * "unasserted", in most cases this will drive the physical
+                * line high.
+                */
+               cs[i] = devm_gpiod_get_index_optional(dev, "cs", i,
+                                                     GPIOD_OUT_LOW);
+
+               if (cs[i]) {
+                       /*
+                        * If we find a CS GPIO, name it after the device and
+                        * chip select line.
+                        */
+                       char *gpioname;
+
+                       gpioname = devm_kasprintf(dev, GFP_KERNEL, "%s CS%d",
+                                                 dev_name(dev), i);
+                       if (!gpioname)
+                               return -ENOMEM;
+                       gpiod_set_consumer_name(cs[i], gpioname);
+               }
+       }
+
+       return 0;
+}
+
 static int spi_controller_check_ops(struct spi_controller *ctlr)
 {
        /*
                return status;
 
        if (!spi_controller_is_slave(ctlr)) {
-               status = of_spi_register_master(ctlr);
-               if (status)
-                       return status;
+               if (ctlr->use_gpio_descriptors) {
+                       status = spi_get_gpio_descs(ctlr);
+                       if (status)
+                               return status;
+               } else {
+                       /* Legacy code path for GPIOs from DT */
+                       status = of_spi_register_master(ctlr);
+                       if (status)
+                               return status;
+               }
        }
 
        /* even if it's just one always-selected device, there must
         * cs_change is set for each transfer.
         */
        if ((spi->mode & SPI_CS_WORD) && (!(ctlr->mode_bits & SPI_CS_WORD) ||
+                                         spi->cs_gpiod ||
                                          gpio_is_valid(spi->cs_gpio))) {
                size_t maxsize;
                int ret;
 
 #include <linux/kthread.h>
 #include <linux/completion.h>
 #include <linux/scatterlist.h>
+#include <linux/gpio/consumer.h>
 
 struct dma_chan;
 struct property_entry;
  * @modalias: Name of the driver to use with this device, or an alias
  *     for that name.  This appears in the sysfs "modalias" attribute
  *     for driver coldplugging, and in uevents used for hotplugging
- * @cs_gpio: gpio number of the chipselect line (optional, -ENOENT when
+ * @cs_gpio: LEGACY: gpio number of the chipselect line (optional, -ENOENT when
+ *     not using a GPIO line) use cs_gpiod in new drivers by opting in on
+ *     the spi_master.
+ * @cs_gpiod: gpio descriptor of the chipselect line (optional, NULL when
  *     not using a GPIO line)
  *
  * @statistics: statistics for the spi_device
        void                    *controller_data;
        char                    modalias[SPI_NAME_SIZE];
        const char              *driver_override;
-       int                     cs_gpio;        /* chip select gpio */
+       int                     cs_gpio;        /* LEGACY: chip select gpio */
+       struct gpio_desc        *cs_gpiod;      /* chip select gpio desc */
 
        /* the statistics */
        struct spi_statistics   statistics;
  *          controller has native support for memory like operations.
  * @unprepare_message: undo any work done by prepare_message().
  * @slave_abort: abort the ongoing transfer request on an SPI slave controller
- * @cs_gpios: Array of GPIOs to use as chip select lines; one per CS
- *     number. Any individual value may be -ENOENT for CS lines that
+ * @cs_gpios: LEGACY: array of GPIO descs to use as chip select lines; one per
+ *     CS number. Any individual value may be -ENOENT for CS lines that
+ *     are not GPIOs (driven by the SPI controller itself). Use the cs_gpiods
+ *     in new drivers.
+ * @cs_gpiods: Array of GPIO descs to use as chip select lines; one per CS
+ *     number. Any individual value may be NULL for CS lines that
  *     are not GPIOs (driven by the SPI controller itself).
+ * @use_gpio_descriptors: Turns on the code in the SPI core to parse and grab
+ *     GPIO descriptors rather than using global GPIO numbers grabbed by the
+ *     driver. This will fill in @cs_gpiods and @cs_gpios should not be used,
+ *     and SPI devices will have the cs_gpiod assigned rather than cs_gpio.
  * @statistics: statistics for the spi_controller
  * @dma_tx: DMA transmit channel
  * @dma_rx: DMA receive channel
 
        /* gpio chip select */
        int                     *cs_gpios;
+       struct gpio_desc        **cs_gpiods;
+       bool                    use_gpio_descriptors;
 
        /* statistics */
        struct spi_statistics   statistics;