Loading drivers/spi/spi-s3c64xx.c +2 −2 Original line number Diff line number Diff line Loading @@ -1191,8 +1191,8 @@ static int s3c64xx_spi_probe(struct platform_device *pdev) dev_dbg(&pdev->dev, "Samsung SoC SPI Driver loaded for Bus SPI-%d with %d Slaves attached\n", sdd->port_id, master->num_chipselect); dev_dbg(&pdev->dev, "\tIOmem=[%pR]\tDMA=[Rx-%d, Tx-%d]\n", mem_res, dev_dbg(&pdev->dev, "\tIOmem=[%pR]\tFIFO %dbytes\tDMA=[Rx-%d, Tx-%d]\n", mem_res, (FIFO_LVL_MASK(sdd) >> 1) + 1, sdd->rx_dma.dmach, sdd->tx_dma.dmach); return 0; Loading drivers/spi/spi-sh-msiof.c +9 −11 Original line number Diff line number Diff line Loading @@ -48,8 +48,8 @@ struct sh_msiof_spi_priv { const struct sh_msiof_chipdata *chipdata; struct sh_msiof_spi_info *info; struct completion done; int tx_fifo_size; int rx_fifo_size; unsigned int tx_fifo_size; unsigned int rx_fifo_size; void *tx_dma_page; void *rx_dma_page; dma_addr_t tx_dma_addr; Loading Loading @@ -95,8 +95,6 @@ struct sh_msiof_spi_priv { #define MDR2_WDLEN1(i) (((i) - 1) << 16) /* Word Count (1-64/256 (SH, A1))) */ #define MDR2_GRPMASK1 0x00000001 /* Group Output Mask 1 (SH, A1) */ #define MAX_WDLEN 256U /* TSCR and RSCR */ #define SCR_BRPS_MASK 0x1f00 /* Prescaler Setting (1-32) */ #define SCR_BRPS(i) (((i) - 1) << 8) Loading Loading @@ -850,7 +848,12 @@ static int sh_msiof_transfer_one(struct spi_master *master, * DMA supports 32-bit words only, hence pack 8-bit and 16-bit * words, with byte resp. word swapping. */ unsigned int l = min(len, MAX_WDLEN * 4); unsigned int l = 0; if (tx_buf) l = min(len, p->tx_fifo_size * 4); if (rx_buf) l = min(len, p->rx_fifo_size * 4); if (bits <= 8) { if (l & 3) Loading Loading @@ -963,7 +966,7 @@ static const struct sh_msiof_chipdata sh_data = { static const struct sh_msiof_chipdata r8a779x_data = { .tx_fifo_size = 64, .rx_fifo_size = 256, .rx_fifo_size = 64, .master_flags = SPI_MASTER_MUST_TX, }; Loading Loading @@ -1265,11 +1268,6 @@ static int sh_msiof_spi_remove(struct platform_device *pdev) static const struct platform_device_id spi_driver_ids[] = { { "spi_sh_msiof", (kernel_ulong_t)&sh_data }, { "spi_r8a7790_msiof", (kernel_ulong_t)&r8a779x_data }, { "spi_r8a7791_msiof", (kernel_ulong_t)&r8a779x_data }, { "spi_r8a7792_msiof", (kernel_ulong_t)&r8a779x_data }, { "spi_r8a7793_msiof", (kernel_ulong_t)&r8a779x_data }, { "spi_r8a7794_msiof", (kernel_ulong_t)&r8a779x_data }, {}, }; MODULE_DEVICE_TABLE(platform, spi_driver_ids); Loading drivers/spi/spi.c +178 −6 Original line number Diff line number Diff line Loading @@ -67,11 +67,141 @@ modalias_show(struct device *dev, struct device_attribute *a, char *buf) } 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. Loading Loading @@ -249,6 +379,9 @@ struct spi_device *spi_alloc_device(struct spi_master *master) 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; } Loading Loading @@ -476,21 +609,30 @@ 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); 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); Loading @@ -499,6 +641,7 @@ static int spi_map_buf(struct spi_master *master, struct device *dev, 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); } Loading Loading @@ -703,17 +846,29 @@ static int spi_transfer_one_message(struct spi_master *master, 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; Loading @@ -729,6 +884,10 @@ static int spi_transfer_one_message(struct spi_master *master, } 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; Loading Loading @@ -1430,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 Loading Loading @@ -1599,6 +1758,8 @@ int spi_register_master(struct spi_master *master) goto done; } } /* add statistics */ spin_lock_init(&master->statistics.lock); mutex_lock(&board_lock); list_add_tail(&master->list, &spi_master_list); Loading Loading @@ -1966,6 +2127,9 @@ static int __spi_async(struct spi_device *spi, struct spi_message *message) 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); Loading Loading @@ -2102,6 +2266,9 @@ static int __spi_sync(struct spi_device *spi, struct spi_message *message, 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); Loading Loading @@ -2129,8 +2296,13 @@ static int __spi_sync(struct spi_device *spi, struct spi_message *message, /* 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; Loading drivers/spi/spidev.c +4 −4 Original line number Diff line number Diff line Loading @@ -709,7 +709,7 @@ static int spidev_probe(struct spi_device *spi) /* * spidev should never be referenced in DT without a specific * compatbile string, it is a Linux implementation thing * compatible string, it is a Linux implementation thing * rather than a description of the hardware. */ if (spi->dev.of_node && !of_match_device(spidev_dt_ids, &spi->dev)) { Loading include/linux/spi/spi.h +64 −0 Original line number Diff line number Diff line Loading @@ -23,6 +23,8 @@ #include <linux/scatterlist.h> struct dma_chan; struct spi_master; struct spi_transfer; /* * INTERFACES between SPI master-side drivers and SPI infrastructure. Loading @@ -30,6 +32,59 @@ struct dma_chan; */ extern struct bus_type spi_bus_type; /** * struct spi_statistics - statistics for spi transfers * @clock: lock protecting this structure * * @messages: number of spi-messages handled * @transfers: number of spi_transfers handled * @errors: number of errors during spi_transfer * @timedout: number of timeouts during spi_transfer * * @spi_sync: number of times spi_sync is used * @spi_sync_immediate: * number of times spi_sync is executed immediately * in calling context without queuing and scheduling * @spi_async: number of times spi_async is used * * @bytes: number of bytes transferred to/from device * @bytes_tx: number of bytes sent to device * @bytes_rx: number of bytes received from device * */ struct spi_statistics { spinlock_t lock; /* lock for the whole structure */ unsigned long messages; unsigned long transfers; unsigned long errors; unsigned long timedout; unsigned long spi_sync; unsigned long spi_sync_immediate; unsigned long spi_async; unsigned long long bytes; unsigned long long bytes_rx; unsigned long long bytes_tx; }; void spi_statistics_add_transfer_stats(struct spi_statistics *stats, struct spi_transfer *xfer, struct spi_master *master); #define SPI_STATISTICS_ADD_TO_FIELD(stats, field, count) \ do { \ unsigned long flags; \ spin_lock_irqsave(&(stats)->lock, flags); \ (stats)->field += count; \ spin_unlock_irqrestore(&(stats)->lock, flags); \ } while (0) #define SPI_STATISTICS_INCREMENT_FIELD(stats, field) \ SPI_STATISTICS_ADD_TO_FIELD(stats, field, 1) /** * struct spi_device - Master side proxy for an SPI slave device * @dev: Driver model representation of the device. Loading Loading @@ -60,6 +115,8 @@ extern struct bus_type spi_bus_type; * @cs_gpio: gpio number of the chipselect line (optional, -ENOENT when * when not using a GPIO line) * * @statistics: statistics for the spi_device * * A @spi_device is used to interchange data between an SPI slave * (usually a discrete chip) and CPU memory. * Loading Loading @@ -98,6 +155,9 @@ struct spi_device { char modalias[SPI_NAME_SIZE]; int cs_gpio; /* chip select gpio */ /* the statistics */ struct spi_statistics statistics; /* * likely need more hooks for more protocol options affecting how * the controller talks to each chip, like: Loading Loading @@ -296,6 +356,7 @@ static inline void spi_unregister_driver(struct spi_driver *sdrv) * @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 * are not GPIOs (driven by the SPI controller itself). * @statistics: statistics for the spi_master * @dma_tx: DMA transmit channel * @dma_rx: DMA receive channel * @dummy_rx: dummy receive buffer for full-duplex devices Loading Loading @@ -452,6 +513,9 @@ struct spi_master { /* gpio chip select */ int *cs_gpios; /* statistics */ struct spi_statistics statistics; /* DMA channels for use with core dmaengine helpers */ struct dma_chan *dma_tx; struct dma_chan *dma_rx; Loading Loading
drivers/spi/spi-s3c64xx.c +2 −2 Original line number Diff line number Diff line Loading @@ -1191,8 +1191,8 @@ static int s3c64xx_spi_probe(struct platform_device *pdev) dev_dbg(&pdev->dev, "Samsung SoC SPI Driver loaded for Bus SPI-%d with %d Slaves attached\n", sdd->port_id, master->num_chipselect); dev_dbg(&pdev->dev, "\tIOmem=[%pR]\tDMA=[Rx-%d, Tx-%d]\n", mem_res, dev_dbg(&pdev->dev, "\tIOmem=[%pR]\tFIFO %dbytes\tDMA=[Rx-%d, Tx-%d]\n", mem_res, (FIFO_LVL_MASK(sdd) >> 1) + 1, sdd->rx_dma.dmach, sdd->tx_dma.dmach); return 0; Loading
drivers/spi/spi-sh-msiof.c +9 −11 Original line number Diff line number Diff line Loading @@ -48,8 +48,8 @@ struct sh_msiof_spi_priv { const struct sh_msiof_chipdata *chipdata; struct sh_msiof_spi_info *info; struct completion done; int tx_fifo_size; int rx_fifo_size; unsigned int tx_fifo_size; unsigned int rx_fifo_size; void *tx_dma_page; void *rx_dma_page; dma_addr_t tx_dma_addr; Loading Loading @@ -95,8 +95,6 @@ struct sh_msiof_spi_priv { #define MDR2_WDLEN1(i) (((i) - 1) << 16) /* Word Count (1-64/256 (SH, A1))) */ #define MDR2_GRPMASK1 0x00000001 /* Group Output Mask 1 (SH, A1) */ #define MAX_WDLEN 256U /* TSCR and RSCR */ #define SCR_BRPS_MASK 0x1f00 /* Prescaler Setting (1-32) */ #define SCR_BRPS(i) (((i) - 1) << 8) Loading Loading @@ -850,7 +848,12 @@ static int sh_msiof_transfer_one(struct spi_master *master, * DMA supports 32-bit words only, hence pack 8-bit and 16-bit * words, with byte resp. word swapping. */ unsigned int l = min(len, MAX_WDLEN * 4); unsigned int l = 0; if (tx_buf) l = min(len, p->tx_fifo_size * 4); if (rx_buf) l = min(len, p->rx_fifo_size * 4); if (bits <= 8) { if (l & 3) Loading Loading @@ -963,7 +966,7 @@ static const struct sh_msiof_chipdata sh_data = { static const struct sh_msiof_chipdata r8a779x_data = { .tx_fifo_size = 64, .rx_fifo_size = 256, .rx_fifo_size = 64, .master_flags = SPI_MASTER_MUST_TX, }; Loading Loading @@ -1265,11 +1268,6 @@ static int sh_msiof_spi_remove(struct platform_device *pdev) static const struct platform_device_id spi_driver_ids[] = { { "spi_sh_msiof", (kernel_ulong_t)&sh_data }, { "spi_r8a7790_msiof", (kernel_ulong_t)&r8a779x_data }, { "spi_r8a7791_msiof", (kernel_ulong_t)&r8a779x_data }, { "spi_r8a7792_msiof", (kernel_ulong_t)&r8a779x_data }, { "spi_r8a7793_msiof", (kernel_ulong_t)&r8a779x_data }, { "spi_r8a7794_msiof", (kernel_ulong_t)&r8a779x_data }, {}, }; MODULE_DEVICE_TABLE(platform, spi_driver_ids); Loading
drivers/spi/spi.c +178 −6 Original line number Diff line number Diff line Loading @@ -67,11 +67,141 @@ modalias_show(struct device *dev, struct device_attribute *a, char *buf) } 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. Loading Loading @@ -249,6 +379,9 @@ struct spi_device *spi_alloc_device(struct spi_master *master) 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; } Loading Loading @@ -476,21 +609,30 @@ 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); 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); Loading @@ -499,6 +641,7 @@ static int spi_map_buf(struct spi_master *master, struct device *dev, 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); } Loading Loading @@ -703,17 +846,29 @@ static int spi_transfer_one_message(struct spi_master *master, 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; Loading @@ -729,6 +884,10 @@ static int spi_transfer_one_message(struct spi_master *master, } 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; Loading Loading @@ -1430,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 Loading Loading @@ -1599,6 +1758,8 @@ int spi_register_master(struct spi_master *master) goto done; } } /* add statistics */ spin_lock_init(&master->statistics.lock); mutex_lock(&board_lock); list_add_tail(&master->list, &spi_master_list); Loading Loading @@ -1966,6 +2127,9 @@ static int __spi_async(struct spi_device *spi, struct spi_message *message) 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); Loading Loading @@ -2102,6 +2266,9 @@ static int __spi_sync(struct spi_device *spi, struct spi_message *message, 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); Loading Loading @@ -2129,8 +2296,13 @@ static int __spi_sync(struct spi_device *spi, struct spi_message *message, /* 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; Loading
drivers/spi/spidev.c +4 −4 Original line number Diff line number Diff line Loading @@ -709,7 +709,7 @@ static int spidev_probe(struct spi_device *spi) /* * spidev should never be referenced in DT without a specific * compatbile string, it is a Linux implementation thing * compatible string, it is a Linux implementation thing * rather than a description of the hardware. */ if (spi->dev.of_node && !of_match_device(spidev_dt_ids, &spi->dev)) { Loading
include/linux/spi/spi.h +64 −0 Original line number Diff line number Diff line Loading @@ -23,6 +23,8 @@ #include <linux/scatterlist.h> struct dma_chan; struct spi_master; struct spi_transfer; /* * INTERFACES between SPI master-side drivers and SPI infrastructure. Loading @@ -30,6 +32,59 @@ struct dma_chan; */ extern struct bus_type spi_bus_type; /** * struct spi_statistics - statistics for spi transfers * @clock: lock protecting this structure * * @messages: number of spi-messages handled * @transfers: number of spi_transfers handled * @errors: number of errors during spi_transfer * @timedout: number of timeouts during spi_transfer * * @spi_sync: number of times spi_sync is used * @spi_sync_immediate: * number of times spi_sync is executed immediately * in calling context without queuing and scheduling * @spi_async: number of times spi_async is used * * @bytes: number of bytes transferred to/from device * @bytes_tx: number of bytes sent to device * @bytes_rx: number of bytes received from device * */ struct spi_statistics { spinlock_t lock; /* lock for the whole structure */ unsigned long messages; unsigned long transfers; unsigned long errors; unsigned long timedout; unsigned long spi_sync; unsigned long spi_sync_immediate; unsigned long spi_async; unsigned long long bytes; unsigned long long bytes_rx; unsigned long long bytes_tx; }; void spi_statistics_add_transfer_stats(struct spi_statistics *stats, struct spi_transfer *xfer, struct spi_master *master); #define SPI_STATISTICS_ADD_TO_FIELD(stats, field, count) \ do { \ unsigned long flags; \ spin_lock_irqsave(&(stats)->lock, flags); \ (stats)->field += count; \ spin_unlock_irqrestore(&(stats)->lock, flags); \ } while (0) #define SPI_STATISTICS_INCREMENT_FIELD(stats, field) \ SPI_STATISTICS_ADD_TO_FIELD(stats, field, 1) /** * struct spi_device - Master side proxy for an SPI slave device * @dev: Driver model representation of the device. Loading Loading @@ -60,6 +115,8 @@ extern struct bus_type spi_bus_type; * @cs_gpio: gpio number of the chipselect line (optional, -ENOENT when * when not using a GPIO line) * * @statistics: statistics for the spi_device * * A @spi_device is used to interchange data between an SPI slave * (usually a discrete chip) and CPU memory. * Loading Loading @@ -98,6 +155,9 @@ struct spi_device { char modalias[SPI_NAME_SIZE]; int cs_gpio; /* chip select gpio */ /* the statistics */ struct spi_statistics statistics; /* * likely need more hooks for more protocol options affecting how * the controller talks to each chip, like: Loading Loading @@ -296,6 +356,7 @@ static inline void spi_unregister_driver(struct spi_driver *sdrv) * @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 * are not GPIOs (driven by the SPI controller itself). * @statistics: statistics for the spi_master * @dma_tx: DMA transmit channel * @dma_rx: DMA receive channel * @dummy_rx: dummy receive buffer for full-duplex devices Loading Loading @@ -452,6 +513,9 @@ struct spi_master { /* gpio chip select */ int *cs_gpios; /* statistics */ struct spi_statistics statistics; /* DMA channels for use with core dmaengine helpers */ struct dma_chan *dma_tx; struct dma_chan *dma_rx; Loading