Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit c92ec7c7 authored by Mark Brown's avatar Mark Brown
Browse files

Merge remote-tracking branches 'spi/topic/s3c64xx', 'spi/topic/sg',...

Merge remote-tracking branches 'spi/topic/s3c64xx', 'spi/topic/sg', 'spi/topic/sh-msiof', 'spi/topic/spidev' and 'spi/topic/stats' into spi-next
Loading
Loading
Loading
Loading
+2 −2
Original line number Diff line number Diff line
@@ -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;
+9 −11
Original line number Diff line number Diff line
@@ -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;
@@ -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)
@@ -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)
@@ -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,
};

@@ -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);
+178 −6
Original line number Diff line number Diff line
@@ -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.
@@ -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;
}
@@ -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);
@@ -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);
		}
@@ -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;
@@ -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;
@@ -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
@@ -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);
@@ -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);
@@ -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);

@@ -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;
+4 −4
Original line number Diff line number Diff line
@@ -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)) {
+64 −0
Original line number Diff line number Diff line
@@ -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.
@@ -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.
@@ -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.
 *
@@ -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:
@@ -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
@@ -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;