Merge remote-tracking branches 'spi/topic/acpi', 'spi/topic/axi-engine', 'spi/topic...
[deliverable/linux.git] / drivers / spi / spi.c
index 47eff8012a7715c362eb3ac3e706f8a9ea6e71c2..e86148fd458bb62e35a6ec95369032251219eae6 100644 (file)
@@ -702,6 +702,7 @@ static int spi_map_buf(struct spi_master *master, struct device *dev,
                       enum dma_data_direction dir)
 {
        const bool vmalloced_buf = is_vmalloc_addr(buf);
+       unsigned int max_seg_size = dma_get_max_seg_size(dev);
        int desc_len;
        int sgs;
        struct page *vm_page;
@@ -710,10 +711,10 @@ static int spi_map_buf(struct spi_master *master, struct device *dev,
        int i, ret;
 
        if (vmalloced_buf) {
-               desc_len = PAGE_SIZE;
+               desc_len = min_t(int, max_seg_size, PAGE_SIZE);
                sgs = DIV_ROUND_UP(len + offset_in_page(buf), desc_len);
        } else {
-               desc_len = master->max_dma_len;
+               desc_len = min_t(int, max_seg_size, master->max_dma_len);
                sgs = DIV_ROUND_UP(len, desc_len);
        }
 
@@ -739,7 +740,6 @@ static int spi_map_buf(struct spi_master *master, struct device *dev,
                        sg_set_buf(&sgt->sgl[i], sg_buf, min);
                }
 
-
                buf += min;
                len -= min;
        }
@@ -1152,6 +1152,7 @@ static void __spi_pump_messages(struct spi_master *master, bool in_kthread)
                }
        }
 
+       mutex_lock(&master->bus_lock_mutex);
        trace_spi_message_start(master->cur_msg);
 
        if (master->prepare_message) {
@@ -1161,6 +1162,7 @@ static void __spi_pump_messages(struct spi_master *master, bool in_kthread)
                                "failed to prepare message: %d\n", ret);
                        master->cur_msg->status = ret;
                        spi_finalize_current_message(master);
+                       mutex_unlock(&master->bus_lock_mutex);
                        return;
                }
                master->cur_msg_prepared = true;
@@ -1170,6 +1172,7 @@ static void __spi_pump_messages(struct spi_master *master, bool in_kthread)
        if (ret) {
                master->cur_msg->status = ret;
                spi_finalize_current_message(master);
+               mutex_unlock(&master->bus_lock_mutex);
                return;
        }
 
@@ -1177,8 +1180,13 @@ static void __spi_pump_messages(struct spi_master *master, bool in_kthread)
        if (ret) {
                dev_err(&master->dev,
                        "failed to transfer one message from queue\n");
+               mutex_unlock(&master->bus_lock_mutex);
                return;
        }
+       mutex_unlock(&master->bus_lock_mutex);
+
+       /* Prod the scheduler in case transfer_one() was busy waiting */
+       cond_resched();
 }
 
 /**
@@ -1581,13 +1589,30 @@ static void of_register_spi_devices(struct spi_master *master) { }
 static int acpi_spi_add_resource(struct acpi_resource *ares, void *data)
 {
        struct spi_device *spi = data;
+       struct spi_master *master = spi->master;
 
        if (ares->type == ACPI_RESOURCE_TYPE_SERIAL_BUS) {
                struct acpi_resource_spi_serialbus *sb;
 
                sb = &ares->data.spi_serial_bus;
                if (sb->type == ACPI_RESOURCE_SERIAL_TYPE_SPI) {
-                       spi->chip_select = sb->device_selection;
+                       /*
+                        * ACPI DeviceSelection numbering is handled by the
+                        * host controller driver in Windows and can vary
+                        * from driver to driver. In Linux we always expect
+                        * 0 .. max - 1 so we need to ask the driver to
+                        * translate between the two schemes.
+                        */
+                       if (master->fw_translate_cs) {
+                               int cs = master->fw_translate_cs(master,
+                                               sb->device_selection);
+                               if (cs < 0)
+                                       return cs;
+                               spi->chip_select = cs;
+                       } else {
+                               spi->chip_select = sb->device_selection;
+                       }
+
                        spi->max_speed_hz = sb->connection_speed;
 
                        if (sb->clock_phase == ACPI_SPI_SECOND_PHASE)
@@ -2351,6 +2376,46 @@ int spi_async_locked(struct spi_device *spi, struct spi_message *message)
 EXPORT_SYMBOL_GPL(spi_async_locked);
 
 
+int spi_flash_read(struct spi_device *spi,
+                  struct spi_flash_read_message *msg)
+
+{
+       struct spi_master *master = spi->master;
+       int ret;
+
+       if ((msg->opcode_nbits == SPI_NBITS_DUAL ||
+            msg->addr_nbits == SPI_NBITS_DUAL) &&
+           !(spi->mode & (SPI_TX_DUAL | SPI_TX_QUAD)))
+               return -EINVAL;
+       if ((msg->opcode_nbits == SPI_NBITS_QUAD ||
+            msg->addr_nbits == SPI_NBITS_QUAD) &&
+           !(spi->mode & SPI_TX_QUAD))
+               return -EINVAL;
+       if (msg->data_nbits == SPI_NBITS_DUAL &&
+           !(spi->mode & (SPI_RX_DUAL | SPI_RX_QUAD)))
+               return -EINVAL;
+       if (msg->data_nbits == SPI_NBITS_QUAD &&
+           !(spi->mode &  SPI_RX_QUAD))
+               return -EINVAL;
+
+       if (master->auto_runtime_pm) {
+               ret = pm_runtime_get_sync(master->dev.parent);
+               if (ret < 0) {
+                       dev_err(&master->dev, "Failed to power device: %d\n",
+                               ret);
+                       return ret;
+               }
+       }
+       mutex_lock(&master->bus_lock_mutex);
+       ret = master->spi_flash_read(spi, msg);
+       mutex_unlock(&master->bus_lock_mutex);
+       if (master->auto_runtime_pm)
+               pm_runtime_put(master->dev.parent);
+
+       return ret;
+}
+EXPORT_SYMBOL_GPL(spi_flash_read);
+
 /*-------------------------------------------------------------------------*/
 
 /* Utility methods for SPI master protocol drivers, layered on
This page took 0.026624 seconds and 5 git commands to generate.