This selects a driver for the Atmel SPI Controller, present on
many AT32 (AVR32) and AT91 (ARM) chips.
++++config SPI_AU1550
++++ tristate "Au1550/Au1200/Au1300 SPI Controller"
++++ depends on MIPS_ALCHEMY
++++ select SPI_BITBANG
++++ help
++++ If you say yes to this option, support will be included for the
++++ PSC SPI controller found on Au1550, Au1200 and Au1300 series.
++++
++++config SPI_AXI_SPI_ENGINE
++++ tristate "Analog Devices AXI SPI Engine controller"
++++ depends on HAS_IOMEM
++++ help
++++ This enables support for the Analog Devices AXI SPI Engine SPI controller.
++++ It is part of the SPI Engine framework that is used in some Analog Devices
++++ reference designs for FPGAs.
++++
config SPI_BCM2835
tristate "BCM2835 SPI controller"
depends on GPIOLIB
depends on ARCH_BCM2835 || COMPILE_TEST
---- depends on GPIOLIB
help
This selects a driver for the Broadcom BCM2835 SPI master.
config SPI_BCM2835AUX
tristate "BCM2835 SPI auxiliary controller"
---- depends on ARCH_BCM2835 || COMPILE_TEST
---- depends on GPIOLIB
++++ depends on (ARCH_BCM2835 && GPIOLIB) || COMPILE_TEST
help
This selects a driver for the Broadcom BCM2835 SPI aux master.
help
Enable support for a SPI bus via the Blackfin SPORT peripheral.
----config SPI_AU1550
---- tristate "Au1550/Au1200/Au1300 SPI Controller"
---- depends on MIPS_ALCHEMY
---- select SPI_BITBANG
---- help
---- If you say yes to this option, support will be included for the
---- PSC SPI controller found on Au1550, Au1200 and Au1300 series.
----
config SPI_BCM53XX
tristate "Broadcom BCM53xx SPI controller"
depends on ARCH_BCM_5301X
help
SPI master controller for DaVinci/DA8x/OMAP-L/AM1x SPI modules.
++++config SPI_DESIGNWARE
++++ tristate "DesignWare SPI controller core support"
++++ help
++++ general driver for SPI controller core from DesignWare
++++
++++config SPI_DW_PCI
++++ tristate "PCI interface driver for DW SPI core"
++++ depends on SPI_DESIGNWARE && PCI
++++
++++config SPI_DW_MID_DMA
++++ bool "DMA support for DW SPI controller on Intel MID platform"
++++ depends on SPI_DW_PCI && DW_DMAC_PCI
++++
++++config SPI_DW_MMIO
++++ tristate "Memory-mapped io interface driver for DW SPI core"
++++ depends on SPI_DESIGNWARE
++++
config SPI_DLN2
tristate "Diolan DLN-2 USB SPI adapter"
depends on MFD_DLN2
which interfaces to an LM70 temperature sensor using
a parallel port.
+ +++config SPI_LP8841_RTC
+ +++ tristate "ICP DAS LP-8841 SPI Controller for RTC"
+ +++ depends on MACH_PXA27X_DT || COMPILE_TEST
+ +++ help
+ +++ This driver provides an SPI master device to drive Maxim
+ +++ DS-1302 real time clock.
+ +++
+ +++ Say N here unless you plan to run the kernel on an ICP DAS
+ +++ LP-8x4x industrial computer.
+ +++
config SPI_MPC52xx
tristate "Freescale MPC52xx SPI (non-PSC) controller support"
depends on PPC_MPC52xx
say Y or M here.If you are not sure, say N.
SPI drivers for Mediatek MT65XX and MT81XX series ARM SoCs.
++++config SPI_NUC900
++++ tristate "Nuvoton NUC900 series SPI"
++++ depends on ARCH_W90X900
++++ select SPI_BITBANG
++++ help
++++ SPI driver for Nuvoton NUC900 series ARM SoCs
++++
config SPI_OC_TINY
tristate "OpenCores tiny SPI"
depends on GPIOLIB || COMPILE_TEST
help
This selects a driver for the PPC4xx SPI Controller.
---- config SPI_PXA2XX_DMA
---- def_bool y
---- depends on SPI_PXA2XX
----
config SPI_PXA2XX
tristate "PXA2xx SSP SPI master"
depends on (ARCH_PXA || PCI || ACPI)
help
Enables Xilinx GQSPI controller driver for Zynq UltraScale+ MPSoC.
----config SPI_NUC900
---- tristate "Nuvoton NUC900 series SPI"
---- depends on ARCH_W90X900
---- select SPI_BITBANG
---- help
---- SPI driver for Nuvoton NUC900 series ARM SoCs
----
#
# Add new SPI master controllers in alphabetical order above this line
#
----config SPI_DESIGNWARE
---- tristate "DesignWare SPI controller core support"
---- help
---- general driver for SPI controller core from DesignWare
----
----config SPI_DW_PCI
---- tristate "PCI interface driver for DW SPI core"
---- depends on SPI_DESIGNWARE && PCI
----
----config SPI_DW_MID_DMA
---- bool "DMA support for DW SPI controller on Intel MID platform"
---- depends on SPI_DW_PCI && DW_DMAC_PCI
----
----config SPI_DW_MMIO
---- tristate "Memory-mapped io interface driver for DW SPI core"
---- depends on SPI_DESIGNWARE
----
#
# There are lots of SPI device types, with sensors and memory
# being probably the most widely used ones.
SPI_STATISTICS_TRANSFER_BYTES_HISTO(15, "32768-65535");
SPI_STATISTICS_TRANSFER_BYTES_HISTO(16, "65536+");
++ ++SPI_STATISTICS_SHOW(transfers_split_maxsize, "%lu");
++ ++
static struct attribute *spi_dev_attrs[] = {
&dev_attr_modalias.attr,
NULL,
&dev_attr_spi_device_transfer_bytes_histo14.attr,
&dev_attr_spi_device_transfer_bytes_histo15.attr,
&dev_attr_spi_device_transfer_bytes_histo16.attr,
++ ++ &dev_attr_spi_device_transfers_split_maxsize.attr,
NULL,
};
&dev_attr_spi_master_transfer_bytes_histo14.attr,
&dev_attr_spi_master_transfer_bytes_histo15.attr,
&dev_attr_spi_master_transfer_bytes_histo16.attr,
++ ++ &dev_attr_spi_master_transfers_split_maxsize.attr,
NULL,
};
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;
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);
}
sg_set_buf(&sgt->sgl[i], sg_buf, min);
}
----
buf += min;
len -= min;
}
if (msg->status && master->handle_err)
master->handle_err(master, msg);
++ ++ spi_res_release(master, msg);
++ ++
spi_finalize_current_message(master);
return ret;
* __spi_pump_messages - function which processes spi message queue
* @master: master to process queue for
* @in_kthread: true if we are in the context of the message pump thread
++++ * @bus_locked: true if the bus mutex is held when calling this function
*
* This function checks if there is any spi message in the queue that
* needs processing and if so call out to the driver to initialize hardware
* inside spi_sync(); the queue extraction handling at the top of the
* function should deal with this safely.
*/
----static void __spi_pump_messages(struct spi_master *master, bool in_kthread)
++++static void __spi_pump_messages(struct spi_master *master, bool in_kthread,
++++ bool bus_locked)
{
unsigned long flags;
bool was_busy = false;
}
}
++++ if (!bus_locked)
++++ mutex_lock(&master->bus_lock_mutex);
++++
trace_spi_message_start(master->cur_msg);
if (master->prepare_message) {
"failed to prepare message: %d\n", ret);
master->cur_msg->status = ret;
spi_finalize_current_message(master);
---- return;
++++ goto out;
}
master->cur_msg_prepared = true;
}
if (ret) {
master->cur_msg->status = ret;
spi_finalize_current_message(master);
---- return;
++++ goto out;
}
ret = master->transfer_one_message(master, master->cur_msg);
if (ret) {
dev_err(&master->dev,
"failed to transfer one message from queue\n");
---- return;
++++ goto out;
}
++++
++++out:
++++ if (!bus_locked)
++++ mutex_unlock(&master->bus_lock_mutex);
++++
++++ /* Prod the scheduler in case transfer_one() was busy waiting */
++++ if (!ret)
++++ cond_resched();
}
/**
struct spi_master *master =
container_of(work, struct spi_master, pump_messages);
---- __spi_pump_messages(master, true);
++++ __spi_pump_messages(master, true, false);
}
static int spi_init_queue(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)
}
EXPORT_SYMBOL_GPL(spi_busnum_to_master);
++ ++/*-------------------------------------------------------------------------*/
++ ++
++ ++/* Core methods for SPI resource management */
++ ++
++ ++/**
++ ++ * spi_res_alloc - allocate a spi resource that is life-cycle managed
++ ++ * during the processing of a spi_message while using
++ ++ * spi_transfer_one
++ ++ * @spi: the spi device for which we allocate memory
++ ++ * @release: the release code to execute for this resource
++ ++ * @size: size to alloc and return
++ ++ * @gfp: GFP allocation flags
++ ++ *
++ ++ * Return: the pointer to the allocated data
++ ++ *
++ ++ * This may get enhanced in the future to allocate from a memory pool
++ ++ * of the @spi_device or @spi_master to avoid repeated allocations.
++ ++ */
++ ++void *spi_res_alloc(struct spi_device *spi,
++ ++ spi_res_release_t release,
++ ++ size_t size, gfp_t gfp)
++ ++{
++ ++ struct spi_res *sres;
++ ++
++ ++ sres = kzalloc(sizeof(*sres) + size, gfp);
++ ++ if (!sres)
++ ++ return NULL;
++ ++
++ ++ INIT_LIST_HEAD(&sres->entry);
++ ++ sres->release = release;
++ ++
++ ++ return sres->data;
++ ++}
++ ++EXPORT_SYMBOL_GPL(spi_res_alloc);
++ ++
++ ++/**
++ ++ * spi_res_free - free an spi resource
++ ++ * @res: pointer to the custom data of a resource
++ ++ *
++ ++ */
++ ++void spi_res_free(void *res)
++ ++{
++ ++ struct spi_res *sres = container_of(res, struct spi_res, data);
++ ++
++ ++ if (!res)
++ ++ return;
++ ++
++ ++ WARN_ON(!list_empty(&sres->entry));
++ ++ kfree(sres);
++ ++}
++ ++EXPORT_SYMBOL_GPL(spi_res_free);
++ ++
++ ++/**
++ ++ * spi_res_add - add a spi_res to the spi_message
++ ++ * @message: the spi message
++ ++ * @res: the spi_resource
++ ++ */
++ ++void spi_res_add(struct spi_message *message, void *res)
++ ++{
++ ++ struct spi_res *sres = container_of(res, struct spi_res, data);
++ ++
++ ++ WARN_ON(!list_empty(&sres->entry));
++ ++ list_add_tail(&sres->entry, &message->resources);
++ ++}
++ ++EXPORT_SYMBOL_GPL(spi_res_add);
++ ++
++ ++/**
++ ++ * spi_res_release - release all spi resources for this message
++ ++ * @master: the @spi_master
++ ++ * @message: the @spi_message
++ ++ */
++ ++void spi_res_release(struct spi_master *master,
++ ++ struct spi_message *message)
++ ++{
++ ++ struct spi_res *res;
++ ++
++ ++ while (!list_empty(&message->resources)) {
++ ++ res = list_last_entry(&message->resources,
++ ++ struct spi_res, entry);
++ ++
++ ++ if (res->release)
++ ++ res->release(master, message, res->data);
++ ++
++ ++ list_del(&res->entry);
++ ++
++ ++ kfree(res);
++ ++ }
++ ++}
++ ++EXPORT_SYMBOL_GPL(spi_res_release);
++ ++
++ ++/*-------------------------------------------------------------------------*/
++ ++
++ ++/* Core methods for spi_message alterations */
++ ++
++ ++static void __spi_replace_transfers_release(struct spi_master *master,
++ ++ struct spi_message *msg,
++ ++ void *res)
++ ++{
++ ++ struct spi_replaced_transfers *rxfer = res;
++ ++ size_t i;
++ ++
++ ++ /* call extra callback if requested */
++ ++ if (rxfer->release)
++ ++ rxfer->release(master, msg, res);
++ ++
++ ++ /* insert replaced transfers back into the message */
++ ++ list_splice(&rxfer->replaced_transfers, rxfer->replaced_after);
++ ++
++ ++ /* remove the formerly inserted entries */
++ ++ for (i = 0; i < rxfer->inserted; i++)
++ ++ list_del(&rxfer->inserted_transfers[i].transfer_list);
++ ++}
++ ++
++ ++/**
++ ++ * spi_replace_transfers - replace transfers with several transfers
++ ++ * and register change with spi_message.resources
++ ++ * @msg: the spi_message we work upon
++ ++ * @xfer_first: the first spi_transfer we want to replace
++ ++ * @remove: number of transfers to remove
++ ++ * @insert: the number of transfers we want to insert instead
++ ++ * @release: extra release code necessary in some circumstances
++ ++ * @extradatasize: extra data to allocate (with alignment guarantees
++ ++ * of struct @spi_transfer)
++ ++ * @gfp: gfp flags
++ ++ *
++ ++ * Returns: pointer to @spi_replaced_transfers,
++ ++ * PTR_ERR(...) in case of errors.
++ ++ */
++ ++struct spi_replaced_transfers *spi_replace_transfers(
++ ++ struct spi_message *msg,
++ ++ struct spi_transfer *xfer_first,
++ ++ size_t remove,
++ ++ size_t insert,
++ ++ spi_replaced_release_t release,
++ ++ size_t extradatasize,
++ ++ gfp_t gfp)
++ ++{
++ ++ struct spi_replaced_transfers *rxfer;
++ ++ struct spi_transfer *xfer;
++ ++ size_t i;
++ ++
++ ++ /* allocate the structure using spi_res */
++ ++ rxfer = spi_res_alloc(msg->spi, __spi_replace_transfers_release,
++ ++ insert * sizeof(struct spi_transfer)
++ ++ + sizeof(struct spi_replaced_transfers)
++ ++ + extradatasize,
++ ++ gfp);
++ ++ if (!rxfer)
++ ++ return ERR_PTR(-ENOMEM);
++ ++
++ ++ /* the release code to invoke before running the generic release */
++ ++ rxfer->release = release;
++ ++
++ ++ /* assign extradata */
++ ++ if (extradatasize)
++ ++ rxfer->extradata =
++ ++ &rxfer->inserted_transfers[insert];
++ ++
++ ++ /* init the replaced_transfers list */
++ ++ INIT_LIST_HEAD(&rxfer->replaced_transfers);
++ ++
++ ++ /* assign the list_entry after which we should reinsert
++ ++ * the @replaced_transfers - it may be spi_message.messages!
++ ++ */
++ ++ rxfer->replaced_after = xfer_first->transfer_list.prev;
++ ++
++ ++ /* remove the requested number of transfers */
++ ++ for (i = 0; i < remove; i++) {
++ ++ /* if the entry after replaced_after it is msg->transfers
++ ++ * then we have been requested to remove more transfers
++ ++ * than are in the list
++ ++ */
++ ++ if (rxfer->replaced_after->next == &msg->transfers) {
++ ++ dev_err(&msg->spi->dev,
++ ++ "requested to remove more spi_transfers than are available\n");
++ ++ /* insert replaced transfers back into the message */
++ ++ list_splice(&rxfer->replaced_transfers,
++ ++ rxfer->replaced_after);
++ ++
++ ++ /* free the spi_replace_transfer structure */
++ ++ spi_res_free(rxfer);
++ ++
++ ++ /* and return with an error */
++ ++ return ERR_PTR(-EINVAL);
++ ++ }
++ ++
++ ++ /* remove the entry after replaced_after from list of
++ ++ * transfers and add it to list of replaced_transfers
++ ++ */
++ ++ list_move_tail(rxfer->replaced_after->next,
++ ++ &rxfer->replaced_transfers);
++ ++ }
++ ++
++ ++ /* create copy of the given xfer with identical settings
++ ++ * based on the first transfer to get removed
++ ++ */
++ ++ for (i = 0; i < insert; i++) {
++ ++ /* we need to run in reverse order */
++ ++ xfer = &rxfer->inserted_transfers[insert - 1 - i];
++ ++
++ ++ /* copy all spi_transfer data */
++ ++ memcpy(xfer, xfer_first, sizeof(*xfer));
++ ++
++ ++ /* add to list */
++ ++ list_add(&xfer->transfer_list, rxfer->replaced_after);
++ ++
++ ++ /* clear cs_change and delay_usecs for all but the last */
++ ++ if (i) {
++ ++ xfer->cs_change = false;
++ ++ xfer->delay_usecs = 0;
++ ++ }
++ ++ }
++ ++
++ ++ /* set up inserted */
++ ++ rxfer->inserted = insert;
++ ++
++ ++ /* and register it with spi_res/spi_message */
++ ++ spi_res_add(msg, rxfer);
++ ++
++ ++ return rxfer;
++ ++}
++ ++EXPORT_SYMBOL_GPL(spi_replace_transfers);
++ ++
++ ++static int __spi_split_transfer_maxsize(struct spi_master *master,
++ ++ struct spi_message *msg,
++ ++ struct spi_transfer **xferp,
++ ++ size_t maxsize,
++ ++ gfp_t gfp)
++ ++{
++ ++ struct spi_transfer *xfer = *xferp, *xfers;
++ ++ struct spi_replaced_transfers *srt;
++ ++ size_t offset;
++ ++ size_t count, i;
++ ++
++ ++ /* warn once about this fact that we are splitting a transfer */
++ ++ dev_warn_once(&msg->spi->dev,
++ ++ "spi_transfer of length %i exceed max length of %zu - needed to split transfers\n",
++ ++ xfer->len, maxsize);
++ ++
++ ++ /* calculate how many we have to replace */
++ ++ count = DIV_ROUND_UP(xfer->len, maxsize);
++ ++
++ ++ /* create replacement */
++ ++ srt = spi_replace_transfers(msg, xfer, 1, count, NULL, 0, gfp);
++ ++ if (IS_ERR(srt))
++ ++ return PTR_ERR(srt);
++ ++ xfers = srt->inserted_transfers;
++ ++
++ ++ /* now handle each of those newly inserted spi_transfers
++ ++ * note that the replacements spi_transfers all are preset
++ ++ * to the same values as *xferp, so tx_buf, rx_buf and len
++ ++ * are all identical (as well as most others)
++ ++ * so we just have to fix up len and the pointers.
++ ++ *
++ ++ * this also includes support for the depreciated
++ ++ * spi_message.is_dma_mapped interface
++ ++ */
++ ++
++ ++ /* the first transfer just needs the length modified, so we
++ ++ * run it outside the loop
++ ++ */
++ ++ xfers[0].len = min_t(size_t, maxsize, xfer[0].len);
++ ++
++ ++ /* all the others need rx_buf/tx_buf also set */
++ ++ for (i = 1, offset = maxsize; i < count; offset += maxsize, i++) {
++ ++ /* update rx_buf, tx_buf and dma */
++ ++ if (xfers[i].rx_buf)
++ ++ xfers[i].rx_buf += offset;
++ ++ if (xfers[i].rx_dma)
++ ++ xfers[i].rx_dma += offset;
++ ++ if (xfers[i].tx_buf)
++ ++ xfers[i].tx_buf += offset;
++ ++ if (xfers[i].tx_dma)
++ ++ xfers[i].tx_dma += offset;
++ ++
++ ++ /* update length */
++ ++ xfers[i].len = min(maxsize, xfers[i].len - offset);
++ ++ }
++ ++
++ ++ /* we set up xferp to the last entry we have inserted,
++ ++ * so that we skip those already split transfers
++ ++ */
++ ++ *xferp = &xfers[count - 1];
++ ++
++ ++ /* increment statistics counters */
++ ++ SPI_STATISTICS_INCREMENT_FIELD(&master->statistics,
++ ++ transfers_split_maxsize);
++ ++ SPI_STATISTICS_INCREMENT_FIELD(&msg->spi->statistics,
++ ++ transfers_split_maxsize);
++ ++
++ ++ return 0;
++ ++}
++ ++
++ ++/**
++ ++ * spi_split_tranfers_maxsize - split spi transfers into multiple transfers
++ ++ * when an individual transfer exceeds a
++ ++ * certain size
++ ++ * @master: the @spi_master for this transfer
++ ++ * @msg: the @spi_message to transform
++ ++ * @maxsize: the maximum when to apply this
++ ++ * @gfp: GFP allocation flags
++ ++ *
++ ++ * Return: status of transformation
++ ++ */
++ ++int spi_split_transfers_maxsize(struct spi_master *master,
++ ++ struct spi_message *msg,
++ ++ size_t maxsize,
++ ++ gfp_t gfp)
++ ++{
++ ++ struct spi_transfer *xfer;
++ ++ int ret;
++ ++
++ ++ /* iterate over the transfer_list,
++ ++ * but note that xfer is advanced to the last transfer inserted
++ ++ * to avoid checking sizes again unnecessarily (also xfer does
++ ++ * potentiall belong to a different list by the time the
++ ++ * replacement has happened
++ ++ */
++ ++ list_for_each_entry(xfer, &msg->transfers, transfer_list) {
++ ++ if (xfer->len > maxsize) {
++ ++ ret = __spi_split_transfer_maxsize(
++ ++ master, msg, &xfer, maxsize, gfp);
++ ++ if (ret)
++ ++ return ret;
++ ++ }
++ ++ }
++ ++
++ ++ return 0;
++ ++}
++ ++EXPORT_SYMBOL_GPL(spi_split_transfers_maxsize);
/*-------------------------------------------------------------------------*/
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
spi_sync_immediate);
SPI_STATISTICS_INCREMENT_FIELD(&spi->statistics,
spi_sync_immediate);
---- __spi_pump_messages(master, false);
++++ __spi_pump_messages(master, false, bus_locked);
}
wait_for_completion(&done);
struct dma_chan;
struct spi_master;
struct spi_transfer;
++++struct spi_flash_read_message;
/*
* INTERFACES between SPI master-side drivers and SPI infrastructure.
*
* @transfer_bytes_histo:
* transfer bytes histogramm
++ ++ *
++ ++ * @transfers_split_maxsize:
++ ++ * number of transfers that have been split because of
++ ++ * maxsize limit
*/
struct spi_statistics {
spinlock_t lock; /* lock for the whole structure */
#define SPI_STATISTICS_HISTO_SIZE 17
unsigned long transfer_bytes_histo[SPI_STATISTICS_HISTO_SIZE];
++ ++
++ ++ unsigned long transfers_split_maxsize;
};
void spi_statistics_add_transfer_stats(struct spi_statistics *stats,
* @min_speed_hz: Lowest supported transfer speed
* @max_speed_hz: Highest supported transfer speed
* @flags: other constraints relevant to this driver
++++ * @max_transfer_size: function that returns the max transfer size for
++++ * a &spi_device; may be %NULL, so the default %SIZE_MAX will be used.
* @bus_lock_spinlock: spinlock for SPI bus locking
* @bus_lock_mutex: mutex for SPI bus locking
* @bus_lock_flag: indicates that the SPI bus is locked for exclusive use
* @handle_err: the subsystem calls the driver to handle an error that occurs
* in the generic implementation of transfer_one_message().
* @unprepare_message: undo any work done by prepare_message().
++++ * @spi_flash_read: to support spi-controller hardwares that provide
++++ * accelerated interface to read from flash devices.
* @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).
* @dma_rx: DMA receive channel
* @dummy_rx: dummy receive buffer for full-duplex devices
* @dummy_tx: dummy transmit buffer for full-duplex devices
+++ * @fw_translate_cs: If the boot firmware uses different numbering scheme
+++ * what Linux expects, this optional hook can be used to translate
+++ * between the two.
*
* Each SPI master controller can communicate with one or more @spi_device
* children. These make a small bus, sharing MOSI, MISO and SCK signals
struct spi_message *message);
int (*unprepare_message)(struct spi_master *master,
struct spi_message *message);
++++ int (*spi_flash_read)(struct spi_device *spi,
++++ struct spi_flash_read_message *msg);
/*
* These hooks are for drivers that use a generic implementation
/* dummy data for full duplex devices */
void *dummy_rx;
void *dummy_tx;
+++
+++ int (*fw_translate_cs)(struct spi_master *master, unsigned cs);
};
static inline void *spi_master_get_devdata(struct spi_master *master)
extern struct spi_master *spi_busnum_to_master(u16 busnum);
++ ++/*
++ ++ * SPI resource management while processing a SPI message
++ ++ */
++ ++
++ ++/**
++ ++ * struct spi_res - spi resource management structure
++ ++ * @entry: list entry
++ ++ * @release: release code called prior to freeing this resource
++ ++ * @data: extra data allocated for the specific use-case
++ ++ *
++ ++ * this is based on ideas from devres, but focused on life-cycle
++ ++ * management during spi_message processing
++ ++ */
++ ++typedef void (*spi_res_release_t)(struct spi_master *master,
++ ++ struct spi_message *msg,
++ ++ void *res);
++ ++struct spi_res {
++ ++ struct list_head entry;
++ ++ spi_res_release_t release;
++ ++ unsigned long long data[]; /* guarantee ull alignment */
++ ++};
++ ++
++ ++extern void *spi_res_alloc(struct spi_device *spi,
++ ++ spi_res_release_t release,
++ ++ size_t size, gfp_t gfp);
++ ++extern void spi_res_add(struct spi_message *message, void *res);
++ ++extern void spi_res_free(void *res);
++ ++
++ ++extern void spi_res_release(struct spi_master *master,
++ ++ struct spi_message *message);
++ ++
/*---------------------------------------------------------------------------*/
/*
* @status: zero for success, else negative errno
* @queue: for use by whichever driver currently owns the message
* @state: for use by whichever driver currently owns the message
++ ++ * @resources: for resource management when the spi message is processed
*
* A @spi_message is used to execute an atomic sequence of data transfers,
* each represented by a struct spi_transfer. The sequence is "atomic"
*/
struct list_head queue;
void *state;
++ ++
++ ++ /* list of spi_res reources when the spi message is processed */
++ ++ struct list_head resources;
};
static inline void spi_message_init_no_memset(struct spi_message *m)
{
INIT_LIST_HEAD(&m->transfers);
++ ++ INIT_LIST_HEAD(&m->resources);
}
static inline void spi_message_init(struct spi_message *m)
/*---------------------------------------------------------------------------*/
++ ++/* SPI transfer replacement methods which make use of spi_res */
++ ++
++ ++struct spi_replaced_transfers;
++ ++typedef void (*spi_replaced_release_t)(struct spi_master *master,
++ ++ struct spi_message *msg,
++ ++ struct spi_replaced_transfers *res);
++ ++/**
++ ++ * struct spi_replaced_transfers - structure describing the spi_transfer
++ ++ * replacements that have occurred
++ ++ * so that they can get reverted
++ ++ * @release: some extra release code to get executed prior to
++ ++ * relasing this structure
++ ++ * @extradata: pointer to some extra data if requested or NULL
++ ++ * @replaced_transfers: transfers that have been replaced and which need
++ ++ * to get restored
++ ++ * @replaced_after: the transfer after which the @replaced_transfers
++ ++ * are to get re-inserted
++ ++ * @inserted: number of transfers inserted
++ ++ * @inserted_transfers: array of spi_transfers of array-size @inserted,
++ ++ * that have been replacing replaced_transfers
++ ++ *
++ ++ * note: that @extradata will point to @inserted_transfers[@inserted]
++ ++ * if some extra allocation is requested, so alignment will be the same
++ ++ * as for spi_transfers
++ ++ */
++ ++struct spi_replaced_transfers {
++ ++ spi_replaced_release_t release;
++ ++ void *extradata;
++ ++ struct list_head replaced_transfers;
++ ++ struct list_head *replaced_after;
++ ++ size_t inserted;
++ ++ struct spi_transfer inserted_transfers[];
++ ++};
++ ++
++ ++extern struct spi_replaced_transfers *spi_replace_transfers(
++ ++ struct spi_message *msg,
++ ++ struct spi_transfer *xfer_first,
++ ++ size_t remove,
++ ++ size_t insert,
++ ++ spi_replaced_release_t release,
++ ++ size_t extradatasize,
++ ++ gfp_t gfp);
++ ++
++ ++/*---------------------------------------------------------------------------*/
++ ++
++ ++/* SPI transfer transformation methods */
++ ++
++ ++extern int spi_split_transfers_maxsize(struct spi_master *master,
++ ++ struct spi_message *msg,
++ ++ size_t maxsize,
++ ++ gfp_t gfp);
++ ++
++ ++/*---------------------------------------------------------------------------*/
++ ++
/* All these synchronous SPI transfer routines are utilities layered
* over the core async transfer primitive. Here, "synchronous" means
* they will sleep uninterruptibly until the async transfer completes.
return be16_to_cpu(result);
}
++++/**
++++ * struct spi_flash_read_message - flash specific information for
++++ * spi-masters that provide accelerated flash read interfaces
++++ * @buf: buffer to read data
++++ * @from: offset within the flash from where data is to be read
++++ * @len: length of data to be read
++++ * @retlen: actual length of data read
++++ * @read_opcode: read_opcode to be used to communicate with flash
++++ * @addr_width: number of address bytes
++++ * @dummy_bytes: number of dummy bytes
++++ * @opcode_nbits: number of lines to send opcode
++++ * @addr_nbits: number of lines to send address
++++ * @data_nbits: number of lines for data
++++ */
++++struct spi_flash_read_message {
++++ void *buf;
++++ loff_t from;
++++ size_t len;
++++ size_t retlen;
++++ u8 read_opcode;
++++ u8 addr_width;
++++ u8 dummy_bytes;
++++ u8 opcode_nbits;
++++ u8 addr_nbits;
++++ u8 data_nbits;
++++};
++++
++++/* SPI core interface for flash read support */
++++static inline bool spi_flash_read_supported(struct spi_device *spi)
++++{
++++ return spi->master->spi_flash_read ? true : false;
++++}
++++
++++int spi_flash_read(struct spi_device *spi,
++++ struct spi_flash_read_message *msg);
++++
/*---------------------------------------------------------------------------*/
/*