Merge remote-tracking branch 'spi/fix/core' into spi-linus
[linux-drm-fsl-dcu.git] / drivers / spi / spi.c
index 99c05f639e1b59e22abd2ef9752006a8ae2c00b7..349ebba4b1992afdf703e689022dfbf67b07f626 100644 (file)
@@ -39,6 +39,9 @@
 #include <linux/ioport.h>
 #include <linux/acpi.h>
 
+#define CREATE_TRACE_POINTS
+#include <trace/events/spi.h>
+
 static void spidev_release(struct device *dev)
 {
        struct spi_device       *spi = to_spi_device(dev);
@@ -242,15 +245,27 @@ EXPORT_SYMBOL_GPL(spi_bus_type);
 static int spi_drv_probe(struct device *dev)
 {
        const struct spi_driver         *sdrv = to_spi_driver(dev->driver);
+       struct spi_device               *spi = to_spi_device(dev);
+       int ret;
 
-       return sdrv->probe(to_spi_device(dev));
+       acpi_dev_pm_attach(&spi->dev, true);
+       ret = sdrv->probe(spi);
+       if (ret)
+               acpi_dev_pm_detach(&spi->dev, true);
+
+       return ret;
 }
 
 static int spi_drv_remove(struct device *dev)
 {
        const struct spi_driver         *sdrv = to_spi_driver(dev->driver);
+       struct spi_device               *spi = to_spi_device(dev);
+       int ret;
+
+       ret = sdrv->remove(spi);
+       acpi_dev_pm_detach(&spi->dev, true);
 
-       return sdrv->remove(to_spi_device(dev));
+       return ret;
 }
 
 static void spi_drv_shutdown(struct device *dev)
@@ -342,6 +357,19 @@ struct spi_device *spi_alloc_device(struct spi_master *master)
 }
 EXPORT_SYMBOL_GPL(spi_alloc_device);
 
+static void spi_dev_set_name(struct spi_device *spi)
+{
+       struct acpi_device *adev = ACPI_COMPANION(&spi->dev);
+
+       if (adev) {
+               dev_set_name(&spi->dev, "spi-%s", acpi_dev_name(adev));
+               return;
+       }
+
+       dev_set_name(&spi->dev, "%s.%u", dev_name(&spi->master->dev),
+                    spi->chip_select);
+}
+
 /**
  * spi_add_device - Add spi_device allocated with spi_alloc_device
  * @spi: spi_device to register
@@ -368,9 +396,7 @@ int spi_add_device(struct spi_device *spi)
        }
 
        /* Set the bus ID string */
-       dev_set_name(&spi->dev, "%s.%u", dev_name(&spi->master->dev),
-                       spi->chip_select);
-
+       spi_dev_set_name(spi);
 
        /* We need to make sure there's no other device with this
         * chipselect **BEFORE** we call setup(), else we'll trash
@@ -525,6 +551,95 @@ int spi_register_board_info(struct spi_board_info const *info, unsigned n)
 
 /*-------------------------------------------------------------------------*/
 
+static void spi_set_cs(struct spi_device *spi, bool enable)
+{
+       if (spi->mode & SPI_CS_HIGH)
+               enable = !enable;
+
+       if (spi->cs_gpio >= 0)
+               gpio_set_value(spi->cs_gpio, !enable);
+       else if (spi->master->set_cs)
+               spi->master->set_cs(spi, !enable);
+}
+
+/*
+ * spi_transfer_one_message - Default implementation of transfer_one_message()
+ *
+ * This is a standard implementation of transfer_one_message() for
+ * drivers which impelment a transfer_one() operation.  It provides
+ * standard handling of delays and chip select management.
+ */
+static int spi_transfer_one_message(struct spi_master *master,
+                                   struct spi_message *msg)
+{
+       struct spi_transfer *xfer;
+       bool cur_cs = true;
+       bool keep_cs = false;
+       int ret = 0;
+
+       spi_set_cs(msg->spi, true);
+
+       list_for_each_entry(xfer, &msg->transfers, transfer_list) {
+               trace_spi_transfer_start(msg, xfer);
+
+               reinit_completion(&master->xfer_completion);
+
+               ret = master->transfer_one(master, msg->spi, xfer);
+               if (ret < 0) {
+                       dev_err(&msg->spi->dev,
+                               "SPI transfer failed: %d\n", ret);
+                       goto out;
+               }
+
+               if (ret > 0)
+                       wait_for_completion(&master->xfer_completion);
+
+               trace_spi_transfer_stop(msg, xfer);
+
+               if (msg->status != -EINPROGRESS)
+                       goto out;
+
+               if (xfer->delay_usecs)
+                       udelay(xfer->delay_usecs);
+
+               if (xfer->cs_change) {
+                       if (list_is_last(&xfer->transfer_list,
+                                        &msg->transfers)) {
+                               keep_cs = true;
+                       } else {
+                               cur_cs = !cur_cs;
+                               spi_set_cs(msg->spi, cur_cs);
+                       }
+               }
+
+               msg->actual_length += xfer->len;
+       }
+
+out:
+       if (ret != 0 || !keep_cs)
+               spi_set_cs(msg->spi, false);
+
+       if (msg->status == -EINPROGRESS)
+               msg->status = ret;
+
+       spi_finalize_current_message(master);
+
+       return ret;
+}
+
+/**
+ * spi_finalize_current_transfer - report completion of a transfer
+ *
+ * Called by SPI drivers using the core transfer_one_message()
+ * implementation to notify it that the current interrupt driven
+ * transfer has finised and the next one may be scheduled.
+ */
+void spi_finalize_current_transfer(struct spi_master *master)
+{
+       complete(&master->xfer_completion);
+}
+EXPORT_SYMBOL_GPL(spi_finalize_current_transfer);
+
 /**
  * spi_pump_messages - kthread work function which processes spi message queue
  * @work: pointer to kthread work struct contained in the master struct
@@ -559,6 +674,7 @@ static void spi_pump_messages(struct kthread_work *work)
                        pm_runtime_mark_last_busy(master->dev.parent);
                        pm_runtime_put_autosuspend(master->dev.parent);
                }
+               trace_spi_master_idle(master);
                return;
        }
 
@@ -587,6 +703,9 @@ static void spi_pump_messages(struct kthread_work *work)
                }
        }
 
+       if (!was_busy)
+               trace_spi_master_busy(master);
+
        if (!was_busy && master->prepare_transfer_hardware) {
                ret = master->prepare_transfer_hardware(master);
                if (ret) {
@@ -599,6 +718,20 @@ static void spi_pump_messages(struct kthread_work *work)
                }
        }
 
+       trace_spi_message_start(master->cur_msg);
+
+       if (master->prepare_message) {
+               ret = master->prepare_message(master, master->cur_msg);
+               if (ret) {
+                       dev_err(&master->dev,
+                               "failed to prepare message: %d\n", ret);
+                       master->cur_msg->status = ret;
+                       spi_finalize_current_message(master);
+                       return;
+               }
+               master->cur_msg_prepared = true;
+       }
+
        ret = master->transfer_one_message(master, master->cur_msg);
        if (ret) {
                dev_err(&master->dev,
@@ -680,6 +813,7 @@ void spi_finalize_current_message(struct spi_master *master)
 {
        struct spi_message *mesg;
        unsigned long flags;
+       int ret;
 
        spin_lock_irqsave(&master->queue_lock, flags);
        mesg = master->cur_msg;
@@ -688,9 +822,20 @@ void spi_finalize_current_message(struct spi_master *master)
        queue_kthread_work(&master->kworker, &master->pump_messages);
        spin_unlock_irqrestore(&master->queue_lock, flags);
 
+       if (master->cur_msg_prepared && master->unprepare_message) {
+               ret = master->unprepare_message(master, mesg);
+               if (ret) {
+                       dev_err(&master->dev,
+                               "failed to unprepare message: %d\n", ret);
+               }
+       }
+       master->cur_msg_prepared = false;
+
        mesg->state = NULL;
        if (mesg->complete)
                mesg->complete(mesg->context);
+
+       trace_spi_message_done(mesg);
 }
 EXPORT_SYMBOL_GPL(spi_finalize_current_message);
 
@@ -805,6 +950,8 @@ static int spi_master_initialize_queue(struct spi_master *master)
 
        master->queued = true;
        master->transfer = spi_queued_transfer;
+       if (!master->transfer_one_message)
+               master->transfer_one_message = spi_transfer_one_message;
 
        /* Initialize and start queue */
        ret = spi_init_queue(master);
@@ -1008,7 +1155,7 @@ static acpi_status acpi_spi_add_device(acpi_handle handle, u32 level,
                return AE_NO_MEMORY;
        }
 
-       ACPI_HANDLE_SET(&spi->dev, handle);
+       ACPI_COMPANION_SET(&spi->dev, adev);
        spi->irq = -1;
 
        INIT_LIST_HEAD(&resource_list);
@@ -1021,8 +1168,10 @@ static acpi_status acpi_spi_add_device(acpi_handle handle, u32 level,
                return AE_OK;
        }
 
+       adev->power.flags.ignore_parent = true;
        strlcpy(spi->modalias, acpi_device_hid(adev), sizeof(spi->modalias));
        if (spi_add_device(spi)) {
+               adev->power.flags.ignore_parent = false;
                dev_err(&master->dev, "failed to add SPI device %s from ACPI\n",
                        dev_name(&adev->dev));
                spi_dev_put(spi);
@@ -1205,6 +1354,7 @@ int spi_register_master(struct spi_master *master)
        spin_lock_init(&master->bus_lock_spinlock);
        mutex_init(&master->bus_lock_mutex);
        master->bus_lock_flag = 0;
+       init_completion(&master->xfer_completion);
 
        /* register the device, then userspace will see it.
         * registration fails if the bus ID is in use.
@@ -1265,7 +1415,7 @@ int devm_spi_register_master(struct device *dev, struct spi_master *master)
                return -ENOMEM;
 
        ret = spi_register_master(master);
-       if (ret != 0) {
+       if (!ret) {
                *ptr = master;
                devres_add(dev, ptr);
        } else {
@@ -1451,6 +1601,10 @@ static int __spi_async(struct spi_device *spi, struct spi_message *message)
        struct spi_master *master = spi->master;
        struct spi_transfer *xfer;
 
+       message->spi = spi;
+
+       trace_spi_message_submit(message);
+
        if (list_empty(&message->transfers))
                return -EINVAL;
        if (!message->complete)
@@ -1550,7 +1704,6 @@ static int __spi_async(struct spi_device *spi, struct spi_message *message)
                }
        }
 
-       message->spi = spi;
        message->status = -EINPROGRESS;
        return master->transfer(spi, message);
 }