Merge tag 'v4.3-rc1' into spi-fix-doc
authorMark Brown <broonie@kernel.org>
Wed, 16 Sep 2015 19:44:25 +0000 (20:44 +0100)
committerMark Brown <broonie@kernel.org>
Wed, 16 Sep 2015 19:44:25 +0000 (20:44 +0100)
Linux 4.3-rc1

1  2 
drivers/spi/spi.c

diff --combined drivers/spi/spi.c
@@@ -67,11 -67,141 +67,141 @@@ modalias_show(struct device *dev, struc
  }
  static DEVICE_ATTR_RO(modalias);
  
+ #define SPI_STATISTICS_ATTRS(field, file)                             \
+ static ssize_t spi_master_##field##_show(struct device *dev,          \
+                                        struct device_attribute *attr, \
+                                        char *buf)                     \
+ {                                                                     \
+       struct spi_master *master = container_of(dev,                   \
+                                                struct spi_master, dev); \
+       return spi_statistics_##field##_show(&master->statistics, buf); \
+ }                                                                     \
+ static struct device_attribute dev_attr_spi_master_##field = {                \
+       .attr = { .name = file, .mode = S_IRUGO },                      \
+       .show = spi_master_##field##_show,                              \
+ };                                                                    \
+ static ssize_t spi_device_##field##_show(struct device *dev,          \
+                                        struct device_attribute *attr, \
+                                       char *buf)                      \
+ {                                                                     \
+       struct spi_device *spi = container_of(dev,                      \
+                                             struct spi_device, dev);  \
+       return spi_statistics_##field##_show(&spi->statistics, buf);    \
+ }                                                                     \
+ static struct device_attribute dev_attr_spi_device_##field = {                \
+       .attr = { .name = file, .mode = S_IRUGO },                      \
+       .show = spi_device_##field##_show,                              \
+ }
+ #define SPI_STATISTICS_SHOW_NAME(name, file, field, format_string)    \
+ static ssize_t spi_statistics_##name##_show(struct spi_statistics *stat, \
+                                           char *buf)                  \
+ {                                                                     \
+       unsigned long flags;                                            \
+       ssize_t len;                                                    \
+       spin_lock_irqsave(&stat->lock, flags);                          \
+       len = sprintf(buf, format_string, stat->field);                 \
+       spin_unlock_irqrestore(&stat->lock, flags);                     \
+       return len;                                                     \
+ }                                                                     \
+ SPI_STATISTICS_ATTRS(name, file)
+ #define SPI_STATISTICS_SHOW(field, format_string)                     \
+       SPI_STATISTICS_SHOW_NAME(field, __stringify(field),             \
+                                field, format_string)
+ SPI_STATISTICS_SHOW(messages, "%lu");
+ SPI_STATISTICS_SHOW(transfers, "%lu");
+ SPI_STATISTICS_SHOW(errors, "%lu");
+ SPI_STATISTICS_SHOW(timedout, "%lu");
+ SPI_STATISTICS_SHOW(spi_sync, "%lu");
+ SPI_STATISTICS_SHOW(spi_sync_immediate, "%lu");
+ SPI_STATISTICS_SHOW(spi_async, "%lu");
+ SPI_STATISTICS_SHOW(bytes, "%llu");
+ SPI_STATISTICS_SHOW(bytes_rx, "%llu");
+ SPI_STATISTICS_SHOW(bytes_tx, "%llu");
  static struct attribute *spi_dev_attrs[] = {
        &dev_attr_modalias.attr,
        NULL,
  };
- ATTRIBUTE_GROUPS(spi_dev);
+ static const struct attribute_group spi_dev_group = {
+       .attrs  = spi_dev_attrs,
+ };
+ static struct attribute *spi_device_statistics_attrs[] = {
+       &dev_attr_spi_device_messages.attr,
+       &dev_attr_spi_device_transfers.attr,
+       &dev_attr_spi_device_errors.attr,
+       &dev_attr_spi_device_timedout.attr,
+       &dev_attr_spi_device_spi_sync.attr,
+       &dev_attr_spi_device_spi_sync_immediate.attr,
+       &dev_attr_spi_device_spi_async.attr,
+       &dev_attr_spi_device_bytes.attr,
+       &dev_attr_spi_device_bytes_rx.attr,
+       &dev_attr_spi_device_bytes_tx.attr,
+       NULL,
+ };
+ static const struct attribute_group spi_device_statistics_group = {
+       .name  = "statistics",
+       .attrs  = spi_device_statistics_attrs,
+ };
+ static const struct attribute_group *spi_dev_groups[] = {
+       &spi_dev_group,
+       &spi_device_statistics_group,
+       NULL,
+ };
+ static struct attribute *spi_master_statistics_attrs[] = {
+       &dev_attr_spi_master_messages.attr,
+       &dev_attr_spi_master_transfers.attr,
+       &dev_attr_spi_master_errors.attr,
+       &dev_attr_spi_master_timedout.attr,
+       &dev_attr_spi_master_spi_sync.attr,
+       &dev_attr_spi_master_spi_sync_immediate.attr,
+       &dev_attr_spi_master_spi_async.attr,
+       &dev_attr_spi_master_bytes.attr,
+       &dev_attr_spi_master_bytes_rx.attr,
+       &dev_attr_spi_master_bytes_tx.attr,
+       NULL,
+ };
+ static const struct attribute_group spi_master_statistics_group = {
+       .name  = "statistics",
+       .attrs  = spi_master_statistics_attrs,
+ };
+ static const struct attribute_group *spi_master_groups[] = {
+       &spi_master_statistics_group,
+       NULL,
+ };
+ void spi_statistics_add_transfer_stats(struct spi_statistics *stats,
+                                      struct spi_transfer *xfer,
+                                      struct spi_master *master)
+ {
+       unsigned long flags;
+       spin_lock_irqsave(&stats->lock, flags);
+       stats->transfers++;
+       stats->bytes += xfer->len;
+       if ((xfer->tx_buf) &&
+           (xfer->tx_buf != master->dummy_tx))
+               stats->bytes_tx += xfer->len;
+       if ((xfer->rx_buf) &&
+           (xfer->rx_buf != master->dummy_rx))
+               stats->bytes_rx += xfer->len;
+       spin_unlock_irqrestore(&stats->lock, flags);
+ }
+ EXPORT_SYMBOL_GPL(spi_statistics_add_transfer_stats);
  
  /* modalias support makes "modprobe $MODALIAS" new-style hotplug work,
   * and the sysfs version makes coldplug work too.
@@@ -249,6 -379,9 +379,9 @@@ struct spi_device *spi_alloc_device(str
        spi->dev.bus = &spi_bus_type;
        spi->dev.release = spidev_release;
        spi->cs_gpio = -ENOENT;
+       spin_lock_init(&spi->statistics.lock);
        device_initialize(&spi->dev);
        return spi;
  }
@@@ -476,21 -609,30 +609,30 @@@ static int spi_map_buf(struct spi_maste
                       enum dma_data_direction dir)
  {
        const bool vmalloced_buf = is_vmalloc_addr(buf);
-       const int desc_len = vmalloced_buf ? PAGE_SIZE : master->max_dma_len;
-       const int sgs = DIV_ROUND_UP(len, desc_len);
+       int desc_len;
+       int sgs;
        struct page *vm_page;
        void *sg_buf;
        size_t min;
        int i, ret;
  
+       if (vmalloced_buf) {
+               desc_len = PAGE_SIZE;
+               sgs = DIV_ROUND_UP(len + offset_in_page(buf), desc_len);
+       } else {
+               desc_len = master->max_dma_len;
+               sgs = DIV_ROUND_UP(len, desc_len);
+       }
        ret = sg_alloc_table(sgt, sgs, GFP_KERNEL);
        if (ret != 0)
                return ret;
  
        for (i = 0; i < sgs; i++) {
-               min = min_t(size_t, len, desc_len);
  
                if (vmalloced_buf) {
+                       min = min_t(size_t,
+                                   len, desc_len - offset_in_page(buf));
                        vm_page = vmalloc_to_page(buf);
                        if (!vm_page) {
                                sg_free_table(sgt);
                        sg_set_page(&sgt->sgl[i], vm_page,
                                    min, offset_in_page(buf));
                } else {
+                       min = min_t(size_t, len, desc_len);
                        sg_buf = buf;
                        sg_set_buf(&sgt->sgl[i], sg_buf, min);
                }
@@@ -539,8 -682,15 +682,15 @@@ static int __spi_map_msg(struct spi_mas
        if (!master->can_dma)
                return 0;
  
-       tx_dev = master->dma_tx->device->dev;
-       rx_dev = master->dma_rx->device->dev;
+       if (master->dma_tx)
+               tx_dev = master->dma_tx->device->dev;
+       else
+               tx_dev = &master->dev;
+       if (master->dma_rx)
+               rx_dev = master->dma_rx->device->dev;
+       else
+               rx_dev = &master->dev;
  
        list_for_each_entry(xfer, &msg->transfers, transfer_list) {
                if (!master->can_dma(master, msg->spi, xfer))
@@@ -579,8 -729,15 +729,15 @@@ static int __spi_unmap_msg(struct spi_m
        if (!master->cur_msg_mapped || !master->can_dma)
                return 0;
  
-       tx_dev = master->dma_tx->device->dev;
-       rx_dev = master->dma_rx->device->dev;
+       if (master->dma_tx)
+               tx_dev = master->dma_tx->device->dev;
+       else
+               tx_dev = &master->dev;
+       if (master->dma_rx)
+               rx_dev = master->dma_rx->device->dev;
+       else
+               rx_dev = &master->dev;
  
        list_for_each_entry(xfer, &msg->transfers, transfer_list) {
                if (!master->can_dma(master, msg->spi, xfer))
@@@ -689,17 -846,29 +846,29 @@@ static int spi_transfer_one_message(str
        bool keep_cs = false;
        int ret = 0;
        unsigned long ms = 1;
+       struct spi_statistics *statm = &master->statistics;
+       struct spi_statistics *stats = &msg->spi->statistics;
  
        spi_set_cs(msg->spi, true);
  
+       SPI_STATISTICS_INCREMENT_FIELD(statm, messages);
+       SPI_STATISTICS_INCREMENT_FIELD(stats, messages);
        list_for_each_entry(xfer, &msg->transfers, transfer_list) {
                trace_spi_transfer_start(msg, xfer);
  
+               spi_statistics_add_transfer_stats(statm, xfer, master);
+               spi_statistics_add_transfer_stats(stats, xfer, master);
                if (xfer->tx_buf || xfer->rx_buf) {
                        reinit_completion(&master->xfer_completion);
  
                        ret = master->transfer_one(master, msg->spi, xfer);
                        if (ret < 0) {
+                               SPI_STATISTICS_INCREMENT_FIELD(statm,
+                                                              errors);
+                               SPI_STATISTICS_INCREMENT_FIELD(stats,
+                                                              errors);
                                dev_err(&msg->spi->dev,
                                        "SPI transfer failed: %d\n", ret);
                                goto out;
                        }
  
                        if (ms == 0) {
+                               SPI_STATISTICS_INCREMENT_FIELD(statm,
+                                                              timedout);
+                               SPI_STATISTICS_INCREMENT_FIELD(stats,
+                                                              timedout);
                                dev_err(&msg->spi->dev,
                                        "SPI transfer timed out\n");
                                msg->status = -ETIMEDOUT;
@@@ -1416,10 -1589,10 +1589,10 @@@ static struct class spi_master_class = 
        .name           = "spi_master",
        .owner          = THIS_MODULE,
        .dev_release    = spi_master_release,
+       .dev_groups     = spi_master_groups,
  };
  
  
  /**
   * spi_alloc_master - allocate SPI master controller
   * @dev: the controller, possibly using the platform_bus
   *
   * The caller is responsible for assigning the bus number and initializing
   * the master's methods before calling spi_register_master(); and (after errors
 - * adding the device) calling spi_master_put() and kfree() to prevent a memory
 - * leak.
 + * adding the device) calling spi_master_put() to prevent a memory leak.
   */
  struct spi_master *spi_alloc_master(struct device *dev, unsigned size)
  {
@@@ -1584,6 -1758,8 +1757,8 @@@ int spi_register_master(struct spi_mast
                        goto done;
                }
        }
+       /* add statistics */
+       spin_lock_init(&master->statistics.lock);
  
        mutex_lock(&board_lock);
        list_add_tail(&master->list, &spi_master_list);
@@@ -1739,6 -1915,20 +1914,20 @@@ EXPORT_SYMBOL_GPL(spi_busnum_to_master)
   * other core methods are currently defined as inline functions.
   */
  
+ static int __spi_validate_bits_per_word(struct spi_master *master, u8 bits_per_word)
+ {
+       if (master->bits_per_word_mask) {
+               /* Only 32 bits fit in the mask */
+               if (bits_per_word > 32)
+                       return -EINVAL;
+               if (!(master->bits_per_word_mask &
+                               SPI_BPW_MASK(bits_per_word)))
+                       return -EINVAL;
+       }
+       return 0;
+ }
  /**
   * spi_setup - setup SPI mode and clock rate
   * @spi: the device whose settings are being modified
@@@ -1797,6 -1987,9 +1986,9 @@@ int spi_setup(struct spi_device *spi
        if (!spi->bits_per_word)
                spi->bits_per_word = 8;
  
+       if (__spi_validate_bits_per_word(spi->master, spi->bits_per_word))
+               return -EINVAL;
        if (!spi->max_speed_hz)
                spi->max_speed_hz = spi->master->max_speed_hz;
  
@@@ -1859,19 -2052,15 +2051,15 @@@ static int __spi_validate(struct spi_de
  
                if (!xfer->speed_hz)
                        xfer->speed_hz = spi->max_speed_hz;
+               if (!xfer->speed_hz)
+                       xfer->speed_hz = master->max_speed_hz;
  
                if (master->max_speed_hz &&
                    xfer->speed_hz > master->max_speed_hz)
                        xfer->speed_hz = master->max_speed_hz;
  
-               if (master->bits_per_word_mask) {
-                       /* Only 32 bits fit in the mask */
-                       if (xfer->bits_per_word > 32)
-                               return -EINVAL;
-                       if (!(master->bits_per_word_mask &
-                                       BIT(xfer->bits_per_word - 1)))
-                               return -EINVAL;
-               }
+               if (__spi_validate_bits_per_word(master, xfer->bits_per_word))
+                       return -EINVAL;
  
                /*
                 * SPI transfer length should be multiple of SPI word size
@@@ -1938,6 -2127,9 +2126,9 @@@ static int __spi_async(struct spi_devic
  
        message->spi = spi;
  
+       SPI_STATISTICS_INCREMENT_FIELD(&master->statistics, spi_async);
+       SPI_STATISTICS_INCREMENT_FIELD(&spi->statistics, spi_async);
        trace_spi_message_submit(message);
  
        return master->transfer(spi, message);
@@@ -2074,6 -2266,9 +2265,9 @@@ static int __spi_sync(struct spi_devic
        message->context = &done;
        message->spi = spi;
  
+       SPI_STATISTICS_INCREMENT_FIELD(&master->statistics, spi_sync);
+       SPI_STATISTICS_INCREMENT_FIELD(&spi->statistics, spi_sync);
        if (!bus_locked)
                mutex_lock(&master->bus_lock_mutex);
  
                /* Push out the messages in the calling context if we
                 * can.
                 */
-               if (master->transfer == spi_queued_transfer)
+               if (master->transfer == spi_queued_transfer) {
+                       SPI_STATISTICS_INCREMENT_FIELD(&master->statistics,
+                                                      spi_sync_immediate);
+                       SPI_STATISTICS_INCREMENT_FIELD(&spi->statistics,
+                                                      spi_sync_immediate);
                        __spi_pump_messages(master, false);
+               }
  
                wait_for_completion(&done);
                status = message->status;