Merge remote-tracking branches 'spi/topic/s3c64xx', 'spi/topic/sg', 'spi/topic/sh-msiof', 'spi/topic/spidev' and 'spi/topic/stats' into spi-next
This commit is contained in:
Коммит
c92ec7c7ae
|
@ -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",
|
dev_dbg(&pdev->dev, "Samsung SoC SPI Driver loaded for Bus SPI-%d with %d Slaves attached\n",
|
||||||
sdd->port_id, master->num_chipselect);
|
sdd->port_id, master->num_chipselect);
|
||||||
dev_dbg(&pdev->dev, "\tIOmem=[%pR]\tDMA=[Rx-%d, Tx-%d]\n",
|
dev_dbg(&pdev->dev, "\tIOmem=[%pR]\tFIFO %dbytes\tDMA=[Rx-%d, Tx-%d]\n",
|
||||||
mem_res,
|
mem_res, (FIFO_LVL_MASK(sdd) >> 1) + 1,
|
||||||
sdd->rx_dma.dmach, sdd->tx_dma.dmach);
|
sdd->rx_dma.dmach, sdd->tx_dma.dmach);
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
|
|
|
@ -48,8 +48,8 @@ struct sh_msiof_spi_priv {
|
||||||
const struct sh_msiof_chipdata *chipdata;
|
const struct sh_msiof_chipdata *chipdata;
|
||||||
struct sh_msiof_spi_info *info;
|
struct sh_msiof_spi_info *info;
|
||||||
struct completion done;
|
struct completion done;
|
||||||
int tx_fifo_size;
|
unsigned int tx_fifo_size;
|
||||||
int rx_fifo_size;
|
unsigned int rx_fifo_size;
|
||||||
void *tx_dma_page;
|
void *tx_dma_page;
|
||||||
void *rx_dma_page;
|
void *rx_dma_page;
|
||||||
dma_addr_t tx_dma_addr;
|
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_WDLEN1(i) (((i) - 1) << 16) /* Word Count (1-64/256 (SH, A1))) */
|
||||||
#define MDR2_GRPMASK1 0x00000001 /* Group Output Mask 1 (SH, A1) */
|
#define MDR2_GRPMASK1 0x00000001 /* Group Output Mask 1 (SH, A1) */
|
||||||
|
|
||||||
#define MAX_WDLEN 256U
|
|
||||||
|
|
||||||
/* TSCR and RSCR */
|
/* TSCR and RSCR */
|
||||||
#define SCR_BRPS_MASK 0x1f00 /* Prescaler Setting (1-32) */
|
#define SCR_BRPS_MASK 0x1f00 /* Prescaler Setting (1-32) */
|
||||||
#define SCR_BRPS(i) (((i) - 1) << 8)
|
#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
|
* DMA supports 32-bit words only, hence pack 8-bit and 16-bit
|
||||||
* words, with byte resp. word swapping.
|
* 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 (bits <= 8) {
|
||||||
if (l & 3)
|
if (l & 3)
|
||||||
|
@ -963,7 +966,7 @@ static const struct sh_msiof_chipdata sh_data = {
|
||||||
|
|
||||||
static const struct sh_msiof_chipdata r8a779x_data = {
|
static const struct sh_msiof_chipdata r8a779x_data = {
|
||||||
.tx_fifo_size = 64,
|
.tx_fifo_size = 64,
|
||||||
.rx_fifo_size = 256,
|
.rx_fifo_size = 64,
|
||||||
.master_flags = SPI_MASTER_MUST_TX,
|
.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[] = {
|
static const struct platform_device_id spi_driver_ids[] = {
|
||||||
{ "spi_sh_msiof", (kernel_ulong_t)&sh_data },
|
{ "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);
|
MODULE_DEVICE_TABLE(platform, spi_driver_ids);
|
||||||
|
|
|
@ -67,11 +67,141 @@ modalias_show(struct device *dev, struct device_attribute *a, char *buf)
|
||||||
}
|
}
|
||||||
static DEVICE_ATTR_RO(modalias);
|
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[] = {
|
static struct attribute *spi_dev_attrs[] = {
|
||||||
&dev_attr_modalias.attr,
|
&dev_attr_modalias.attr,
|
||||||
NULL,
|
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,
|
/* modalias support makes "modprobe $MODALIAS" new-style hotplug work,
|
||||||
* and the sysfs version makes coldplug work too.
|
* 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.bus = &spi_bus_type;
|
||||||
spi->dev.release = spidev_release;
|
spi->dev.release = spidev_release;
|
||||||
spi->cs_gpio = -ENOENT;
|
spi->cs_gpio = -ENOENT;
|
||||||
|
|
||||||
|
spin_lock_init(&spi->statistics.lock);
|
||||||
|
|
||||||
device_initialize(&spi->dev);
|
device_initialize(&spi->dev);
|
||||||
return spi;
|
return spi;
|
||||||
}
|
}
|
||||||
|
@ -476,21 +609,30 @@ static int spi_map_buf(struct spi_master *master, struct device *dev,
|
||||||
enum dma_data_direction dir)
|
enum dma_data_direction dir)
|
||||||
{
|
{
|
||||||
const bool vmalloced_buf = is_vmalloc_addr(buf);
|
const bool vmalloced_buf = is_vmalloc_addr(buf);
|
||||||
const int desc_len = vmalloced_buf ? PAGE_SIZE : master->max_dma_len;
|
int desc_len;
|
||||||
const int sgs = DIV_ROUND_UP(len, desc_len);
|
int sgs;
|
||||||
struct page *vm_page;
|
struct page *vm_page;
|
||||||
void *sg_buf;
|
void *sg_buf;
|
||||||
size_t min;
|
size_t min;
|
||||||
int i, ret;
|
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);
|
ret = sg_alloc_table(sgt, sgs, GFP_KERNEL);
|
||||||
if (ret != 0)
|
if (ret != 0)
|
||||||
return ret;
|
return ret;
|
||||||
|
|
||||||
for (i = 0; i < sgs; i++) {
|
for (i = 0; i < sgs; i++) {
|
||||||
min = min_t(size_t, len, desc_len);
|
|
||||||
|
|
||||||
if (vmalloced_buf) {
|
if (vmalloced_buf) {
|
||||||
|
min = min_t(size_t,
|
||||||
|
len, desc_len - offset_in_page(buf));
|
||||||
vm_page = vmalloc_to_page(buf);
|
vm_page = vmalloc_to_page(buf);
|
||||||
if (!vm_page) {
|
if (!vm_page) {
|
||||||
sg_free_table(sgt);
|
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,
|
sg_set_page(&sgt->sgl[i], vm_page,
|
||||||
min, offset_in_page(buf));
|
min, offset_in_page(buf));
|
||||||
} else {
|
} else {
|
||||||
|
min = min_t(size_t, len, desc_len);
|
||||||
sg_buf = buf;
|
sg_buf = buf;
|
||||||
sg_set_buf(&sgt->sgl[i], sg_buf, min);
|
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;
|
bool keep_cs = false;
|
||||||
int ret = 0;
|
int ret = 0;
|
||||||
unsigned long ms = 1;
|
unsigned long ms = 1;
|
||||||
|
struct spi_statistics *statm = &master->statistics;
|
||||||
|
struct spi_statistics *stats = &msg->spi->statistics;
|
||||||
|
|
||||||
spi_set_cs(msg->spi, true);
|
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) {
|
list_for_each_entry(xfer, &msg->transfers, transfer_list) {
|
||||||
trace_spi_transfer_start(msg, xfer);
|
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) {
|
if (xfer->tx_buf || xfer->rx_buf) {
|
||||||
reinit_completion(&master->xfer_completion);
|
reinit_completion(&master->xfer_completion);
|
||||||
|
|
||||||
ret = master->transfer_one(master, msg->spi, xfer);
|
ret = master->transfer_one(master, msg->spi, xfer);
|
||||||
if (ret < 0) {
|
if (ret < 0) {
|
||||||
|
SPI_STATISTICS_INCREMENT_FIELD(statm,
|
||||||
|
errors);
|
||||||
|
SPI_STATISTICS_INCREMENT_FIELD(stats,
|
||||||
|
errors);
|
||||||
dev_err(&msg->spi->dev,
|
dev_err(&msg->spi->dev,
|
||||||
"SPI transfer failed: %d\n", ret);
|
"SPI transfer failed: %d\n", ret);
|
||||||
goto out;
|
goto out;
|
||||||
|
@ -729,6 +884,10 @@ static int spi_transfer_one_message(struct spi_master *master,
|
||||||
}
|
}
|
||||||
|
|
||||||
if (ms == 0) {
|
if (ms == 0) {
|
||||||
|
SPI_STATISTICS_INCREMENT_FIELD(statm,
|
||||||
|
timedout);
|
||||||
|
SPI_STATISTICS_INCREMENT_FIELD(stats,
|
||||||
|
timedout);
|
||||||
dev_err(&msg->spi->dev,
|
dev_err(&msg->spi->dev,
|
||||||
"SPI transfer timed out\n");
|
"SPI transfer timed out\n");
|
||||||
msg->status = -ETIMEDOUT;
|
msg->status = -ETIMEDOUT;
|
||||||
|
@ -1430,10 +1589,10 @@ static struct class spi_master_class = {
|
||||||
.name = "spi_master",
|
.name = "spi_master",
|
||||||
.owner = THIS_MODULE,
|
.owner = THIS_MODULE,
|
||||||
.dev_release = spi_master_release,
|
.dev_release = spi_master_release,
|
||||||
|
.dev_groups = spi_master_groups,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* spi_alloc_master - allocate SPI master controller
|
* spi_alloc_master - allocate SPI master controller
|
||||||
* @dev: the controller, possibly using the platform_bus
|
* @dev: the controller, possibly using the platform_bus
|
||||||
|
@ -1599,6 +1758,8 @@ int spi_register_master(struct spi_master *master)
|
||||||
goto done;
|
goto done;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
/* add statistics */
|
||||||
|
spin_lock_init(&master->statistics.lock);
|
||||||
|
|
||||||
mutex_lock(&board_lock);
|
mutex_lock(&board_lock);
|
||||||
list_add_tail(&master->list, &spi_master_list);
|
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;
|
message->spi = spi;
|
||||||
|
|
||||||
|
SPI_STATISTICS_INCREMENT_FIELD(&master->statistics, spi_async);
|
||||||
|
SPI_STATISTICS_INCREMENT_FIELD(&spi->statistics, spi_async);
|
||||||
|
|
||||||
trace_spi_message_submit(message);
|
trace_spi_message_submit(message);
|
||||||
|
|
||||||
return master->transfer(spi, 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->context = &done;
|
||||||
message->spi = spi;
|
message->spi = spi;
|
||||||
|
|
||||||
|
SPI_STATISTICS_INCREMENT_FIELD(&master->statistics, spi_sync);
|
||||||
|
SPI_STATISTICS_INCREMENT_FIELD(&spi->statistics, spi_sync);
|
||||||
|
|
||||||
if (!bus_locked)
|
if (!bus_locked)
|
||||||
mutex_lock(&master->bus_lock_mutex);
|
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
|
/* Push out the messages in the calling context if we
|
||||||
* can.
|
* 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);
|
__spi_pump_messages(master, false);
|
||||||
|
}
|
||||||
|
|
||||||
wait_for_completion(&done);
|
wait_for_completion(&done);
|
||||||
status = message->status;
|
status = message->status;
|
||||||
|
|
|
@ -602,11 +602,11 @@ static int spidev_open(struct inode *inode, struct file *filp)
|
||||||
if (!spidev->tx_buffer) {
|
if (!spidev->tx_buffer) {
|
||||||
spidev->tx_buffer = kmalloc(bufsiz, GFP_KERNEL);
|
spidev->tx_buffer = kmalloc(bufsiz, GFP_KERNEL);
|
||||||
if (!spidev->tx_buffer) {
|
if (!spidev->tx_buffer) {
|
||||||
dev_dbg(&spidev->spi->dev, "open/ENOMEM\n");
|
dev_dbg(&spidev->spi->dev, "open/ENOMEM\n");
|
||||||
status = -ENOMEM;
|
status = -ENOMEM;
|
||||||
goto err_find_dev;
|
goto err_find_dev;
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (!spidev->rx_buffer) {
|
if (!spidev->rx_buffer) {
|
||||||
spidev->rx_buffer = kmalloc(bufsiz, GFP_KERNEL);
|
spidev->rx_buffer = kmalloc(bufsiz, GFP_KERNEL);
|
||||||
|
@ -709,7 +709,7 @@ static int spidev_probe(struct spi_device *spi)
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* spidev should never be referenced in DT without a specific
|
* 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.
|
* rather than a description of the hardware.
|
||||||
*/
|
*/
|
||||||
if (spi->dev.of_node && !of_match_device(spidev_dt_ids, &spi->dev)) {
|
if (spi->dev.of_node && !of_match_device(spidev_dt_ids, &spi->dev)) {
|
||||||
|
|
|
@ -23,6 +23,8 @@
|
||||||
#include <linux/scatterlist.h>
|
#include <linux/scatterlist.h>
|
||||||
|
|
||||||
struct dma_chan;
|
struct dma_chan;
|
||||||
|
struct spi_master;
|
||||||
|
struct spi_transfer;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* INTERFACES between SPI master-side drivers and SPI infrastructure.
|
* INTERFACES between SPI master-side drivers and SPI infrastructure.
|
||||||
|
@ -30,6 +32,59 @@ struct dma_chan;
|
||||||
*/
|
*/
|
||||||
extern struct bus_type spi_bus_type;
|
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
|
* struct spi_device - Master side proxy for an SPI slave device
|
||||||
* @dev: Driver model representation of the 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
|
* @cs_gpio: gpio number of the chipselect line (optional, -ENOENT when
|
||||||
* when not using a GPIO line)
|
* when not using a GPIO line)
|
||||||
*
|
*
|
||||||
|
* @statistics: statistics for the spi_device
|
||||||
|
*
|
||||||
* A @spi_device is used to interchange data between an SPI slave
|
* A @spi_device is used to interchange data between an SPI slave
|
||||||
* (usually a discrete chip) and CPU memory.
|
* (usually a discrete chip) and CPU memory.
|
||||||
*
|
*
|
||||||
|
@ -98,6 +155,9 @@ struct spi_device {
|
||||||
char modalias[SPI_NAME_SIZE];
|
char modalias[SPI_NAME_SIZE];
|
||||||
int cs_gpio; /* chip select gpio */
|
int cs_gpio; /* chip select gpio */
|
||||||
|
|
||||||
|
/* the statistics */
|
||||||
|
struct spi_statistics statistics;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* likely need more hooks for more protocol options affecting how
|
* likely need more hooks for more protocol options affecting how
|
||||||
* the controller talks to each chip, like:
|
* 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
|
* @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
|
* number. Any individual value may be -ENOENT for CS lines that
|
||||||
* are not GPIOs (driven by the SPI controller itself).
|
* are not GPIOs (driven by the SPI controller itself).
|
||||||
|
* @statistics: statistics for the spi_master
|
||||||
* @dma_tx: DMA transmit channel
|
* @dma_tx: DMA transmit channel
|
||||||
* @dma_rx: DMA receive channel
|
* @dma_rx: DMA receive channel
|
||||||
* @dummy_rx: dummy receive buffer for full-duplex devices
|
* @dummy_rx: dummy receive buffer for full-duplex devices
|
||||||
|
@ -452,6 +513,9 @@ struct spi_master {
|
||||||
/* gpio chip select */
|
/* gpio chip select */
|
||||||
int *cs_gpios;
|
int *cs_gpios;
|
||||||
|
|
||||||
|
/* statistics */
|
||||||
|
struct spi_statistics statistics;
|
||||||
|
|
||||||
/* DMA channels for use with core dmaengine helpers */
|
/* DMA channels for use with core dmaengine helpers */
|
||||||
struct dma_chan *dma_tx;
|
struct dma_chan *dma_tx;
|
||||||
struct dma_chan *dma_rx;
|
struct dma_chan *dma_rx;
|
||||||
|
|
Загрузка…
Ссылка в новой задаче