cregit-Linux how code gets into the kernel

Release 4.11 drivers/spi/spi.c

Directory: drivers/spi
/*
 * SPI init/core code
 *
 * Copyright (C) 2005 David Brownell
 * Copyright (C) 2008 Secret Lab Technologies Ltd.
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 */

#include <linux/kernel.h>
#include <linux/device.h>
#include <linux/init.h>
#include <linux/cache.h>
#include <linux/dma-mapping.h>
#include <linux/dmaengine.h>
#include <linux/mutex.h>
#include <linux/of_device.h>
#include <linux/of_irq.h>
#include <linux/clk/clk-conf.h>
#include <linux/slab.h>
#include <linux/mod_devicetable.h>
#include <linux/spi/spi.h>
#include <linux/of_gpio.h>
#include <linux/pm_runtime.h>
#include <linux/pm_domain.h>
#include <linux/export.h>
#include <linux/sched/rt.h>
#include <uapi/linux/sched/types.h>
#include <linux/delay.h>
#include <linux/kthread.h>
#include <linux/ioport.h>
#include <linux/acpi.h>
#include <linux/highmem.h>


#define CREATE_TRACE_POINTS
#include <trace/events/spi.h>


static void spidev_release(struct device *dev) { struct spi_device *spi = to_spi_device(dev); /* spi masters may cleanup for released devices */ if (spi->master->cleanup) spi->master->cleanup(spi); spi_master_put(spi->master); kfree(spi); }

Contributors

PersonTokensPropCommitsCommitProp
David Brownell5098.04%266.67%
Roman Tereshonkov11.96%133.33%
Total51100.00%3100.00%


static ssize_t modalias_show(struct device *dev, struct device_attribute *a, char *buf) { const struct spi_device *spi = to_spi_device(dev); int len; len = acpi_device_modalias(dev, buf, PAGE_SIZE - 1); if (len != -ENODEV) return len; return sprintf(buf, "%s%s\n", SPI_MODULE_PREFIX, spi->modalias); }

Contributors

PersonTokensPropCommitsCommitProp
David Brownell4157.75%125.00%
Rui Zhang2636.62%125.00%
Grant C. Likely34.23%125.00%
Kay Sievers11.41%125.00%
Total71100.00%4100.00%

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 = to_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"); #define SPI_STATISTICS_TRANSFER_BYTES_HISTO(index, number) \ SPI_STATISTICS_SHOW_NAME(transfer_bytes_histo##index, \ "transfer_bytes_histo_" number, \ transfer_bytes_histo[index], "%lu") SPI_STATISTICS_TRANSFER_BYTES_HISTO(0, "0-1"); SPI_STATISTICS_TRANSFER_BYTES_HISTO(1, "2-3"); SPI_STATISTICS_TRANSFER_BYTES_HISTO(2, "4-7"); SPI_STATISTICS_TRANSFER_BYTES_HISTO(3, "8-15"); SPI_STATISTICS_TRANSFER_BYTES_HISTO(4, "16-31"); SPI_STATISTICS_TRANSFER_BYTES_HISTO(5, "32-63"); SPI_STATISTICS_TRANSFER_BYTES_HISTO(6, "64-127"); SPI_STATISTICS_TRANSFER_BYTES_HISTO(7, "128-255"); SPI_STATISTICS_TRANSFER_BYTES_HISTO(8, "256-511"); SPI_STATISTICS_TRANSFER_BYTES_HISTO(9, "512-1023"); SPI_STATISTICS_TRANSFER_BYTES_HISTO(10, "1024-2047"); SPI_STATISTICS_TRANSFER_BYTES_HISTO(11, "2048-4095"); SPI_STATISTICS_TRANSFER_BYTES_HISTO(12, "4096-8191"); SPI_STATISTICS_TRANSFER_BYTES_HISTO(13, "8192-16383"); SPI_STATISTICS_TRANSFER_BYTES_HISTO(14, "16384-32767"); 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, }; 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, &dev_attr_spi_device_transfer_bytes_histo0.attr, &dev_attr_spi_device_transfer_bytes_histo1.attr, &dev_attr_spi_device_transfer_bytes_histo2.attr, &dev_attr_spi_device_transfer_bytes_histo3.attr, &dev_attr_spi_device_transfer_bytes_histo4.attr, &dev_attr_spi_device_transfer_bytes_histo5.attr, &dev_attr_spi_device_transfer_bytes_histo6.attr, &dev_attr_spi_device_transfer_bytes_histo7.attr, &dev_attr_spi_device_transfer_bytes_histo8.attr, &dev_attr_spi_device_transfer_bytes_histo9.attr, &dev_attr_spi_device_transfer_bytes_histo10.attr, &dev_attr_spi_device_transfer_bytes_histo11.attr, &dev_attr_spi_device_transfer_bytes_histo12.attr, &dev_attr_spi_device_transfer_bytes_histo13.attr, &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, }; 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, &dev_attr_spi_master_transfer_bytes_histo0.attr, &dev_attr_spi_master_transfer_bytes_histo1.attr, &dev_attr_spi_master_transfer_bytes_histo2.attr, &dev_attr_spi_master_transfer_bytes_histo3.attr, &dev_attr_spi_master_transfer_bytes_histo4.attr, &dev_attr_spi_master_transfer_bytes_histo5.attr, &dev_attr_spi_master_transfer_bytes_histo6.attr, &dev_attr_spi_master_transfer_bytes_histo7.attr, &dev_attr_spi_master_transfer_bytes_histo8.attr, &dev_attr_spi_master_transfer_bytes_histo9.attr, &dev_attr_spi_master_transfer_bytes_histo10.attr, &dev_attr_spi_master_transfer_bytes_histo11.attr, &dev_attr_spi_master_transfer_bytes_histo12.attr, &dev_attr_spi_master_transfer_bytes_histo13.attr, &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, }; 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; int l2len = min(fls(xfer->len), SPI_STATISTICS_HISTO_SIZE) - 1; if (l2len < 0) l2len = 0; spin_lock_irqsave(&stats->lock, flags); stats->transfers++; stats->transfer_bytes_histo[l2len]++; 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); }

Contributors

PersonTokensPropCommitsCommitProp
Martin Sperl144100.00%2100.00%
Total144100.00%2100.00%

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. */
static const struct spi_device_id *spi_match_id(const struct spi_device_id *id, const struct spi_device *sdev) { while (id->name[0]) { if (!strcmp(sdev->modalias, id->name)) return id; id++; } return NULL; }

Contributors

PersonTokensPropCommitsCommitProp
Anton Vorontsov55100.00%1100.00%
Total55100.00%1100.00%


const struct spi_device_id *spi_get_device_id(const struct spi_device *sdev) { const struct spi_driver *sdrv = to_spi_driver(sdev->dev.driver); return spi_match_id(sdrv->id_table, sdev); }

Contributors

PersonTokensPropCommitsCommitProp
Anton Vorontsov39100.00%1100.00%
Total39100.00%1100.00%

EXPORT_SYMBOL_GPL(spi_get_device_id);
static int spi_match_device(struct device *dev, struct device_driver *drv) { const struct spi_device *spi = to_spi_device(dev); const struct spi_driver *sdrv = to_spi_driver(drv); /* Attempt an OF style match */ if (of_driver_match_device(dev, drv)) return 1; /* Then try ACPI */ if (acpi_driver_match_device(dev, drv)) return 1; if (sdrv->id_table) return !!spi_match_id(sdrv->id_table, spi); return strcmp(spi->modalias, drv->name) == 0; }

Contributors

PersonTokensPropCommitsCommitProp
David Brownell4041.67%120.00%
Anton Vorontsov2930.21%120.00%
Mika Westerberg1313.54%120.00%
Sinan Akman1313.54%120.00%
Kay Sievers11.04%120.00%
Total96100.00%5100.00%


static int spi_uevent(struct device *dev, struct kobj_uevent_env *env) { const struct spi_device *spi = to_spi_device(dev); int rc; rc = acpi_device_uevent_modalias(dev, env); if (rc != -ENODEV) return rc; add_uevent_var(env, "MODALIAS=%s%s", SPI_MODULE_PREFIX, spi->modalias); return 0; }

Contributors

PersonTokensPropCommitsCommitProp
David Brownell3553.85%125.00%
Rui Zhang2233.85%125.00%
Kay Sievers57.69%125.00%
Anton Vorontsov34.62%125.00%
Total65100.00%4100.00%

struct bus_type spi_bus_type = { .name = "spi", .dev_groups = spi_dev_groups, .match = spi_match_device, .uevent = spi_uevent, }; EXPORT_SYMBOL_GPL(spi_bus_type);
static int spi_drv_probe(struct device *dev) { const struct spi_driver *sdrv = to_spi_driver(dev->driver); struct spi_device *spi = to_spi_device(dev); int ret; ret = of_clk_set_defaults(dev->of_node, false); if (ret) return ret; if (dev->of_node) { spi->irq = of_irq_get(dev->of_node, 0); if (spi->irq == -EPROBE_DEFER) return -EPROBE_DEFER; if (spi->irq < 0) spi->irq = 0; } ret = dev_pm_domain_attach(dev, true); if (ret != -EPROBE_DEFER) { ret = sdrv->probe(spi); if (ret) dev_pm_domain_detach(dev, true); } return ret; }

Contributors

PersonTokensPropCommitsCommitProp
Jon Hunter5940.97%120.00%
David Brownell3222.22%120.00%
Mika Westerberg2215.28%120.00%
Sylwester Nawrocki1812.50%120.00%
Ulf Hansson139.03%120.00%
Total144100.00%5100.00%


static int spi_drv_remove(struct device *dev) { const struct spi_driver *sdrv = to_spi_driver(dev->driver); int ret; ret = sdrv->remove(to_spi_device(dev)); dev_pm_domain_detach(dev, true); return ret; }

Contributors

PersonTokensPropCommitsCommitProp
David Brownell3265.31%125.00%
Mika Westerberg1224.49%125.00%
Jean Delvare48.16%125.00%
Ulf Hansson12.04%125.00%
Total49100.00%4100.00%


static void spi_drv_shutdown(struct device *dev) { const struct spi_driver *sdrv = to_spi_driver(dev->driver); sdrv->shutdown(to_spi_device(dev)); }

Contributors

PersonTokensPropCommitsCommitProp
David Brownell34100.00%1100.00%
Total34100.00%1100.00%

/** * __spi_register_driver - register a SPI driver * @owner: owner module of the driver to register * @sdrv: the driver to register * Context: can sleep * * Return: zero on success, else a negative error code. */
int __spi_register_driver(struct module *owner, struct spi_driver *sdrv) { sdrv->driver.owner = owner; sdrv->driver.bus = &spi_bus_type; if (sdrv->probe) sdrv->driver.probe = spi_drv_probe; if (sdrv->remove) sdrv->driver.remove = spi_drv_remove; if (sdrv->shutdown) sdrv->driver.shutdown = spi_drv_shutdown; return driver_register(&sdrv->driver); }

Contributors

PersonTokensPropCommitsCommitProp
David Brownell6983.13%150.00%
Andrew F. Davis1416.87%150.00%
Total83100.00%2100.00%

EXPORT_SYMBOL_GPL(__spi_register_driver); /*-------------------------------------------------------------------------*/ /* SPI devices should normally not be created by SPI device drivers; that * would make them board-specific. Similarly with SPI master drivers. * Device registration normally goes into like arch/.../mach.../board-YYY.c * with other readonly (flashable) information about mainboard devices. */ struct boardinfo { struct list_head list; struct spi_board_info board_info; }; static LIST_HEAD(board_list); static LIST_HEAD(spi_master_list); /* * Used to protect add/del opertion for board_info list and * spi_master list, and their matching process */ static DEFINE_MUTEX(board_lock); /** * spi_alloc_device - Allocate a new SPI device * @master: Controller to which device is connected * Context: can sleep * * Allows a driver to allocate and initialize a spi_device without * registering it immediately. This allows a driver to directly * fill the spi_device with device parameters before calling * spi_add_device() on it. * * Caller is responsible to call spi_add_device() on the returned * spi_device structure to add it to the SPI master. If the caller * needs to discard the spi_device without adding it, then it should * call spi_dev_put() on it. * * Return: a pointer to the new device, or NULL. */
struct spi_device *spi_alloc_device(struct spi_master *master) { struct spi_device *spi; if (!spi_master_get(master)) return NULL; spi = kzalloc(sizeof(*spi), GFP_KERNEL); if (!spi) { spi_master_put(master); return NULL; } spi->master = master; spi->dev.parent = &master->dev; 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; }

Contributors

PersonTokensPropCommitsCommitProp
David Brownell5748.31%333.33%
Grant C. Likely3832.20%111.11%
Martin Sperl108.47%111.11%
Jean-Christophe Plagniol-Villard65.08%111.11%
Jingoo Han32.54%111.11%
Laurent Pinchart32.54%111.11%
Andreas Larsson10.85%111.11%
Total118100.00%9100.00%

EXPORT_SYMBOL_GPL(spi_alloc_device);
static void spi_dev_set_name(struct spi_device *spi) { struct acpi_device *adev = ACPI_COMPANION(&spi->dev); if (adev) { dev_set_name(&spi->dev, "spi-%s", acpi_dev_name(adev)); return; } dev_set_name(&spi->dev, "%s.%u", dev_name(&spi->master->dev), spi->chip_select); }

Contributors

PersonTokensPropCommitsCommitProp
Jarkko Nikula70100.00%1100.00%
Total70100.00%1100.00%


static int spi_dev_check(struct device *dev, void *data) { struct spi_device *spi = to_spi_device(dev); struct spi_device *new_spi = data; if (spi->master == new_spi->master && spi->chip_select == new_spi->chip_select) return -EBUSY; return 0; }

Contributors

PersonTokensPropCommitsCommitProp
Mika Westerberg57100.00%1100.00%
Total57100.00%1100.00%

/** * spi_add_device - Add spi_device allocated with spi_alloc_device * @spi: spi_device to register * * Companion function to spi_alloc_device. Devices allocated with * spi_alloc_device can be added onto the spi bus with this function. * * Return: 0 on success; negative errno on failure */
int spi_add_device(struct spi_device *spi) { static DEFINE_MUTEX(spi_add_lock); struct spi_master *master = spi->master; struct device *dev = master->dev.parent; int status; /* Chipselects are numbered 0..max; validate. */ if (spi->chip_select >= master->num_chipselect) { dev_err(dev, "cs%d >= max %d\n", spi->chip_select, master->num_chipselect); return -EINVAL; } /* Set the bus ID string */ spi_dev_set_name(spi); /* We need to make sure there's no other device with this * chipselect **BEFORE** we call setup(), else we'll trash * its configuration. Lock against concurrent add() calls. */ mutex_lock(&spi_add_lock); status = bus_for_each_dev(&spi_bus_type, NULL, spi, spi_dev_check); if (status) { dev_err(dev, "chipselect %d already in use\n", spi->chip_select); goto done; } if (master->cs_gpios) spi->cs_gpio = master->cs_gpios[spi->chip_select]; /* Drivers may modify this initial i/o setup, but will * normally rely on the device being setup. Devices * using SPI_CS_HIGH can't coexist well otherwise... */ status = spi_setup(spi); if (status < 0) { dev_err(dev, "can't setup %s, status %d\n", dev_name(&spi->dev), status); goto done; } /* Device may be bound to an active driver when this returns */ status = device_add(&spi->dev); if (status < 0) dev_err(dev, "can't add %s, status %d\n", dev_name(&spi->dev), status); else dev_dbg(dev, "registered child %s\n", dev_name(&spi->dev)); done: mutex_unlock(&spi_add_lock); return status; }

Contributors

PersonTokensPropCommitsCommitProp
David Brownell12151.71%433.33%
Grant C. Likely6126.07%216.67%
Jean-Christophe Plagniol-Villard2811.97%18.33%
Kay Sievers125.13%18.33%
Mika Westerberg52.14%18.33%
Roman Tereshonkov41.71%18.33%
Linus Walleij20.85%18.33%
Jarkko Nikula10.43%18.33%
Total234100.00%12100.00%

EXPORT_SYMBOL_GPL(spi_add_device); /** * spi_new_device - instantiate one new SPI device * @master: Controller to which device is connected * @chip: Describes the SPI device * Context: can sleep * * On typical mainboards, this is purely internal; and it's not needed * after board init creates the hard-wired devices. Some development * platforms may not be able to use spi_register_board_info though, and * this is exported so that for example a USB or parport based adapter * driver could add devices (which it would learn about out-of-band). * * Return: the new device, or NULL. */
struct spi_device *spi_new_device(struct spi_master *master, struct spi_board_info *chip) { struct spi_device *proxy; int status; /* NOTE: caller did any chip->bus_num checks necessary. * * Also, unless we change the return value convention to use * error-or-pointer (not NULL-or-pointer), troubleshootability * suggests syslogged diagnostics are best here (ugh). */ proxy = spi_alloc_device(master); if (!proxy) return NULL; WARN_ON(strlen(chip->modalias) >= sizeof(proxy->modalias)); proxy->chip_select = chip->chip_select; proxy->max_speed_hz = chip->max_speed_hz; proxy->mode = chip->mode; proxy->irq = chip->irq; strlcpy(proxy->modalias, chip->modalias, sizeof(proxy->modalias)); proxy->dev.platform_data = (void *) chip->platform_data; proxy->controller_data = chip->controller_data; proxy->controller_state = NULL; status = spi_add_device(proxy); if (status < 0) { spi_dev_put(proxy); return NULL; } return proxy; }

Contributors

PersonTokensPropCommitsCommitProp
Grant C. Likely15092.59%133.33%
David Brownell127.41%266.67%
Total162100.00%3100.00%

EXPORT_SYMBOL_GPL(spi_new_device); /** * spi_unregister_device - unregister a single SPI device * @spi: spi_device to unregister * * Start making the passed SPI device vanish. Normally this would be handled * by spi_unregister_master(). */
void spi_unregister_device(struct spi_device *spi) { if (!spi) return; if (spi->dev.of_node) { of_node_clear_flag(spi->dev.of_node, OF_POPULATED); of_node_put(spi->dev.of_node); } if (ACPI_COMPANION(&spi->dev)) acpi_device_clear_enumerated(ACPI_COMPANION(&spi->dev)); device_unregister(&spi->dev); }

Contributors

PersonTokensPropCommitsCommitProp
Geert Uytterhoeven4357.33%250.00%
Octavian Purdila2128.00%125.00%
Johan Hovold1114.67%125.00%
Total75100.00%4100.00%

EXPORT_SYMBOL_GPL(spi_unregister_device);
static void spi_match_master_to_boardinfo(struct spi_master *master, struct spi_board_info *bi) { struct spi_device *dev; if (master->bus_num != bi->bus_num) return; dev = spi_new_device(master, bi); if (!dev) dev_err(master->dev.parent, "can't create new device for %s\n", bi->modalias); }

Contributors

PersonTokensPropCommitsCommitProp
Feng Tang61100.00%1100.00%
Total61100.00%1100.00%

/** * spi_register_board_info - register SPI devices for a given board * @info: array of chip descriptors * @n: how many descriptors are provided * Context: can sleep * * Board-specific early init code calls this (probably during arch_initcall) * with segments of the SPI device table. Any device nodes are created later, * after the relevant parent SPI controller (bus_num) is defined. We keep * this table of devices forever, so that reloading a controller driver will * not make Linux forget about these hard-wired devices. * * Other code can also call this, e.g. a particular add-on board might provide * SPI devices through its expansion connector, so code initializing that board * would naturally declare its SPI devices. * * The board info passed can safely be __initdata ... but be careful of * any embedded pointers (platform_data, etc), they're copied as-is. * * Return: zero on success, else a negative error code. */
int spi_register_board_info(struct spi_board_info const *info, unsigned n) { struct boardinfo *bi; int i; if (!n) return -EINVAL; bi = kcalloc(n, sizeof(*bi), GFP_KERNEL); if (!bi) return -ENOMEM; for (i = 0; i < n; i++, bi++, info++) { struct spi_master *master; memcpy(&bi->board_info, info, sizeof(*info)); mutex_lock(&board_lock); list_add_tail(&bi->list, &board_list); list_for_each_entry(master, &spi_master_list, list) spi_match_master_to_boardinfo(master, &bi->board_info); mutex_unlock(&board_lock); } return 0; }

Contributors

PersonTokensPropCommitsCommitProp
David Brownell8560.28%116.67%
Feng Tang4229.79%116.67%
Xiubo Li96.38%116.67%
Matthias Kaehlcke21.42%116.67%
SF Markus Elfring21.42%116.67%
Grant C. Likely10.71%116.67%
Total141100.00%6100.00%

/*-------------------------------------------------------------------------*/
static void spi_set_cs(struct spi_device *spi, bool enable) { if (spi->mode & SPI_CS_HIGH) enable = !enable; if (gpio_is_valid(spi->cs_gpio)) { gpio_set_value(spi->cs_gpio, !enable); /* Some SPI masters need both GPIO CS & slave_select */ if ((spi->master->flags & SPI_MASTER_GPIO_SS) && spi->master->set_cs) spi->master->set_cs(spi, !enable); } else if (spi->master->set_cs) { spi->master->set_cs(spi, !enable); } }

Contributors

PersonTokensPropCommitsCommitProp
Mark Brown6462.75%133.33%
Thor Thayer3534.31%133.33%
Andy Shevchenko32.94%133.33%
Total102100.00%3100.00%

#ifdef CONFIG_HAS_DMA
static int spi_map_buf(struct spi_master *master, struct device *dev, struct sg_table *sgt, void *buf, size_t len, enum dma_data_direction dir) { const bool vmalloced_buf = is_vmalloc_addr(buf); unsigned int max_seg_size = dma_get_max_seg_size(dev); #ifdef CONFIG_HIGHMEM const bool kmap_buf = ((unsigned long)buf >= PKMAP_BASE && (unsigned long)buf < (PKMAP_BASE + (LAST_PKMAP * PAGE_SIZE))); #else const bool kmap_buf = false; #endif int desc_len; int sgs; struct page *vm_page; struct scatterlist *sg; void *sg_buf; size_t min; int i, ret; if (vmalloced_buf || kmap_buf) { desc_len = min_t(int, max_seg_size, PAGE_SIZE); sgs = DIV_ROUND_UP(len + offset_in_page(buf), desc_len); } else if (virt_addr_valid(buf)) { desc_len = min_t(int, max_seg_size, master->max_dma_len); sgs = DIV_ROUND_UP(len, desc_len); } else { return -EINVAL; } ret = sg_alloc_table(sgt, sgs, GFP_KERNEL); if (ret != 0) return ret; sg = &sgt->sgl[0]; for (i = 0; i < sgs; i++) { if (vmalloced_buf || kmap_buf) { min = min_t(size_t, len, desc_len - offset_in_page(buf)); if (vmalloced_buf) vm_page = vmalloc_to_page(buf); else vm_page = kmap_to_page(buf); if (!vm_page) { sg_free_table(sgt); return -ENOMEM; } sg_set_page(sg, vm_page, min, offset_in_page(buf)); } else { min = min_t(size_t, len, desc_len); sg_buf = buf; sg_set_buf(sg, sg_buf, min); } buf += min; len -= min; sg = sg_next(sg); } ret = dma_map_sg(dev, sgt->sgl, sgt->nents, dir); if (!ret) ret = -ENOMEM; if (ret < 0) { sg_free_table(sgt); return ret; } sgt->nents = ret; return 0; }

Contributors

PersonTokensPropCommitsCommitProp
Mark Brown20249.88%222.22%
Vignesh R7418.27%222.22%
Andrew Gabbasov6215.31%111.11%
Juan Gutierrez245.93%111.11%
Andy Shevchenko235.68%111.11%
Charles Keepax102.47%111.11%
Geert Uytterhoeven102.47%111.11%
Total405100.00%9100.00%


static void spi_unmap_buf(struct spi_master *master, struct device *dev, struct sg_table *sgt, enum dma_data_direction dir) { if (sgt->orig_nents) { dma_unmap_sg(dev, sgt->sgl, sgt->orig_nents, dir); sg_free_table(sgt); } }

Contributors

PersonTokensPropCommitsCommitProp
Mark Brown53100.00%2100.00%
Total53100.00%2100.00%


static int __spi_map_msg(struct spi_master *master, struct spi_message *msg) { struct device *tx_dev, *rx_dev; struct spi_transfer *xfer; int ret; if (!master->can_dma) return 0; if (master->dma_tx) tx_dev = master->dma_tx->device->dev; else tx_dev = master->dev.parent; if (master->dma_rx) rx_dev = master->dma_rx->device->dev; else rx_dev = master->dev.parent; list_for_each_entry(xfer, &msg->transfers, transfer_list) { if (!master->can_dma(master, msg->spi, xfer)) continue; if (xfer->tx_buf != NULL) { ret = spi_map_buf(master, tx_dev, &xfer->tx_sg, (void *)xfer->tx_buf, xfer->len, DMA_TO_DEVICE); if (ret != 0) return ret; } if (xfer->rx_buf != NULL) { ret = spi_map_buf(master, rx_dev, &xfer->rx_sg, xfer->rx_buf, xfer->len, DMA_FROM_DEVICE); if (ret != 0) { spi_unmap_buf(master, tx_dev, &xfer->tx_sg, DMA_TO_DEVICE); return ret; } } } master->cur_msg_mapped = true; return 0; }

Contributors

PersonTokensPropCommitsCommitProp
Mark Brown18076.92%333.33%
Leilk Liu2611.11%111.11%
David Brownell135.56%111.11%
Feng Tang62.56%111.11%
Geert Uytterhoeven52.14%222.22%
Daniel Kurtz41.71%111.11%
Total234100.00%9100.00%


static int __spi_unmap_msg(struct spi_master *master, struct spi_message *msg) { struct spi_transfer *xfer; struct device *tx_dev, *rx_dev; if (!master->cur_msg_mapped || !master->can_dma) return 0; if (master->dma_tx) tx_dev = master->dma_tx->device->dev; else tx_dev = master->dev.parent; if (master->dma_rx) rx_dev = master->dma_rx->device->dev; else rx_dev = master->dev.parent; list_for_each_entry(xfer, &msg->transfers, transfer_list) { if (!master->can_dma(master, msg->spi, xfer)) continue; spi_unmap_buf(master, rx_dev, &xfer->rx_sg, DMA_FROM_DEVICE); spi_unmap_buf(master, tx_dev, &xfer->tx_sg, DMA_TO_DEVICE); } return 0; }

Contributors

PersonTokensPropCommitsCommitProp
Mark Brown11776.97%342.86%
Leilk Liu2617.11%114.29%
Geert Uytterhoeven42.63%114.29%
Daniel Kurtz42.63%114.29%
Martin Sperl10.66%114.29%
Total152100.00%7100.00%

#else /* !CONFIG_HAS_DMA */
static inline int spi_map_buf(struct spi_master *master, struct device *dev, struct sg_table *sgt, void *buf, size_t len, enum dma_data_direction dir) { return -EINVAL; }

Contributors

PersonTokensPropCommitsCommitProp
Vignesh R37100.00%1100.00%
Total37100.00%1100.00%


static inline void spi_unmap_buf(struct spi_master *master, struct device *dev, struct sg_table *sgt, enum dma_data_direction dir) { }

Contributors

PersonTokensPropCommitsCommitProp
Vignesh R25100.00%1100.00%
Total25100.00%1100.00%


static inline int __spi_map_msg(struct spi_master *master, struct spi_message *msg) { return 0; }

Contributors

PersonTokensPropCommitsCommitProp
Geert Uytterhoeven20100.00%1100.00%
Total20100.00%1100.00%


static inline int __spi_unmap_msg(struct spi_master *master, struct spi_message *msg) { return 0; }

Contributors

PersonTokensPropCommitsCommitProp
Geert Uytterhoeven1995.00%150.00%
Martin Sperl15.00%150.00%
Total20100.00%2100.00%

#endif /* !CONFIG_HAS_DMA */
static inline int spi_unmap_msg(struct spi_master *master, struct spi_message *msg) { struct spi_transfer *xfer; list_for_each_entry(xfer, &msg->transfers, transfer_list) { /* * Restore the original value of tx_buf or rx_buf if they are * NULL. */ if (xfer->tx_buf == master->dummy_tx) xfer->tx_buf = NULL; if (xfer->rx_buf == master->dummy_rx) xfer->rx_buf = NULL; } return __spi_unmap_msg(master, msg); }

Contributors

PersonTokensPropCommitsCommitProp
Martin Sperl73100.00%1100.00%
Total73100.00%1100.00%


static int spi_map_msg(struct spi_master *master, struct spi_message *msg) { struct spi_transfer *xfer; void *tmp; unsigned int max_tx, max_rx; if (master->flags & (SPI_MASTER_MUST_RX | SPI_MASTER_MUST_TX)) { max_tx = 0; max_rx = 0; list_for_each_entry(xfer, &msg->transfers, transfer_list) { if ((master->flags & SPI_MASTER_MUST_TX) && !xfer->tx_buf) max_tx = max(xfer->len, max_tx); if ((master->flags & SPI_MASTER_MUST_RX) && !xfer->rx_buf) max_rx = max(xfer->len, max_rx); } if (max_tx) { tmp = krealloc(master->dummy_tx, max_tx, GFP_KERNEL | GFP_DMA); if (!tmp) return -ENOMEM; master->dummy_tx = tmp; memset(tmp, 0, max_tx); } if (max_rx) { tmp = krealloc(master->dummy_rx, max_rx, GFP_KERNEL | GFP_DMA); if (!tmp) return -ENOMEM; master->dummy_rx = tmp; } if (max_tx || max_rx) { list_for_each_entry(xfer, &msg->transfers, transfer_list) { if (!xfer->tx_buf) xfer->tx_buf = master->dummy_tx; if (!xfer->rx_buf) xfer->rx_buf = master->dummy_rx; } } } return __spi_map_msg(master, msg); }

Contributors

PersonTokensPropCommitsCommitProp
Geert Uytterhoeven252100.00%1100.00%
Total252100.00%1100.00%

/* * spi_transfer_one_message - Default implementation of transfer_one_message() * * This is a standard implementation of transfer_one_message() for * drivers which implement a transfer_one() operation. It provides * standard handling of delays and chip select management. */
static int spi_transfer_one_message(struct spi_master *master, struct spi_message *msg) { struct spi_transfer *xfer; bool keep_cs = false; int ret = 0; unsigned long long ms = 1; struct spi_statistics *statm = &master->statistics; struct spi_statistics *stats = &msg->spi->statistics; spi_set_cs(msg->spi, true); SPI_STATISTICS_INCREMENT_FIELD(statm, messages); SPI_STATISTICS_INCREMENT_FIELD(stats, messages); list_for_each_entry(xfer, &msg->transfers, transfer_list) { trace_spi_transfer_start(msg, xfer); spi_statistics_add_transfer_stats(statm, xfer, master); spi_statistics_add_transfer_stats(stats, xfer, master); if (xfer->tx_buf || xfer->rx_buf) { reinit_completion(&master->xfer_completion); ret = master->transfer_one(master, msg->spi, xfer); if (ret < 0) { SPI_STATISTICS_INCREMENT_FIELD(statm, errors); SPI_STATISTICS_INCREMENT_FIELD(stats, errors); dev_err(&msg->spi->dev, "SPI transfer failed: %d\n", ret); goto out; } if (ret > 0) { ret = 0; ms = 8LL * 1000LL * xfer->len; do_div(ms, xfer->speed_hz); ms += ms + 100; /* some tolerance */ if (ms > UINT_MAX) ms = UINT_MAX; ms = wait_for_completion_timeout(&master->xfer_completion, msecs_to_jiffies(ms)); } 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; } } else { if (xfer->len) dev_err(&msg->spi->dev, "Bufferless transfer has length %u\n", xfer->len); } trace_spi_transfer_stop(msg, xfer); if (msg->status != -EINPROGRESS) goto out; if (xfer->delay_usecs) { u16 us = xfer->delay_usecs; if (us <= 10) udelay(us); else usleep_range(us, us + DIV_ROUND_UP(us, 10)); } if (xfer->cs_change) { if (list_is_last(&xfer->transfer_list, &msg->transfers)) { keep_cs = true; } else { spi_set_cs(msg->spi, false); udelay(10); spi_set_cs(msg->spi, true); } } msg->actual_length += xfer->len; } out: if (ret != 0 || !keep_cs) spi_set_cs(msg->spi, false); if (msg->status == -EINPROGRESS) msg->status = ret; if (msg->status && master->handle_err) master->handle_err(master, msg); spi_res_release(master, msg); spi_finalize_current_message(master); return ret; }

Contributors

PersonTokensPropCommitsCommitProp
Mark Brown34066.28%428.57%
Martin Sperl8917.35%214.29%
Daniel Kurtz305.85%17.14%
Sien Wu214.09%17.14%
Andy Shevchenko152.92%17.14%
Axel Lin71.36%17.14%
Geert Uytterhoeven40.78%17.14%
Harini Katakam30.58%17.14%
Wolfram Sang20.39%17.14%
Nicholas Mc Guire20.39%17.14%
Total513100.00%14100.00%

/** * spi_finalize_current_transfer - report completion of a transfer * @master: the master reporting completion * * Called by SPI drivers using the core transfer_one_message() * implementation to notify it that the current interrupt driven * transfer has finished and the next one may be scheduled. */
void spi_finalize_current_transfer(struct spi_master *master) { complete(&master->xfer_completion); }

Contributors

PersonTokensPropCommitsCommitProp
Mark Brown18100.00%1100.00%
Total18100.00%1100.00%

EXPORT_SYMBOL_GPL(spi_finalize_current_transfer); /** * __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 * * 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 * and transfer each message. * * Note that it is called both from the kthread itself and also from * 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) { unsigned long flags; bool was_busy = false; int ret; /* Lock queue */ spin_lock_irqsave(&master->queue_lock, flags); /* Make sure we are not already running a message */ if (master->cur_msg) { spin_unlock_irqrestore(&master->queue_lock, flags); return; } /* If another context is idling the device then defer */ if (master->idling) { kthread_queue_work(&master->kworker, &master->pump_messages); spin_unlock_irqrestore(&master->queue_lock, flags); return; } /* Check if the queue is idle */ if (list_empty(&master->queue) || !master->running) { if (!master->busy) { spin_unlock_irqrestore(&master->queue_lock, flags); return; } /* Only do teardown in the thread */ if (!in_kthread) { kthread_queue_work(&master->kworker, &master->pump_messages); spin_unlock_irqrestore(&master->queue_lock, flags); return; } master->busy = false; master->idling = true; spin_unlock_irqrestore(&master->queue_lock, flags); kfree(master->dummy_rx); master->dummy_rx = NULL; kfree(master->dummy_tx); master->dummy_tx = NULL; if (master->unprepare_transfer_hardware && master->unprepare_transfer_hardware(master)) dev_err(&master->dev, "failed to unprepare transfer hardware\n"); if (master->auto_runtime_pm) { pm_runtime_mark_last_busy(master->dev.parent); pm_runtime_put_autosuspend(master->dev.parent); } trace_spi_master_idle(master); spin_lock_irqsave(&master->queue_lock, flags); master->idling = false; spin_unlock_irqrestore(&master->queue_lock, flags); return; } /* Extract head of queue */ master->cur_msg = list_first_entry(&master->queue, struct spi_message, queue); list_del_init(&master->cur_msg->queue); if (master->busy) was_busy = true; else master->busy = true; spin_unlock_irqrestore(&master->queue_lock, flags); mutex_lock(&master->io_mutex); if (!was_busy && 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); mutex_unlock(&master->io_mutex); return; } } if (!was_busy) trace_spi_master_busy(master); if (!was_busy && master->prepare_transfer_hardware) { ret = master->prepare_transfer_hardware(master); if (ret) { dev_err(&master->dev, "failed to prepare transfer hardware\n"); if (master->auto_runtime_pm) pm_runtime_put(master->dev.parent); mutex_unlock(&master->io_mutex); return; } } trace_spi_message_start(master->cur_msg); if (master->prepare_message) { ret = master->prepare_message(master, master->cur_msg); if (ret) { dev_err(&master->dev, "failed to prepare message: %d\n", ret); master->cur_msg->status = ret; spi_finalize_current_message(master); goto out; } master->cur_msg_prepared = true; } ret = spi_map_msg(master, master->cur_msg); if (ret) { master->cur_msg->status = ret; spi_finalize_current_message(master); 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"); goto out; } out: mutex_unlock(&master->io_mutex); /* Prod the scheduler in case transfer_one() was busy waiting */ if (!ret) cond_resched(); }

Contributors

PersonTokensPropCommitsCommitProp
Mark Brown36057.60%1147.83%
Linus Walleij15124.16%14.35%
David Brownell487.68%313.04%
Bryan Freed243.84%14.35%
Jon Hunter162.56%14.35%
Dan Carpenter101.60%14.35%
Vignesh R71.12%14.35%
Shubhrajyoti Datta40.64%14.35%
Petr Mladek20.32%14.35%
Axel Lin20.32%14.35%
Geert Uytterhoeven10.16%14.35%
Total625100.00%23100.00%

/** * spi_pump_messages - kthread work function which processes spi message queue * @work: pointer to kthread work struct contained in the master struct */
static void spi_pump_messages(struct kthread_work *work) { struct spi_master *master = container_of(work, struct spi_master, pump_messages); __spi_pump_messages(master, true); }

Contributors

PersonTokensPropCommitsCommitProp
Mark Brown33100.00%1100.00%
Total33100.00%1100.00%


static int spi_init_queue(struct spi_master *master) { struct sched_param param = { .sched_priority = MAX_RT_PRIO - 1 }; master->running = false; master->busy = false; kthread_init_worker(&master->kworker); master->kworker_task = kthread_run(kthread_worker_fn, &master->kworker, "%s", dev_name(&master->dev)); if (IS_ERR(master->kworker_task)) { dev_err(&master->dev, "failed to create message pump task\n"); return PTR_ERR(master->kworker_task); } kthread_init_work(&master->pump_messages, spi_pump_messages); /* * Master config will indicate if this controller should run the * message pump with high (realtime) priority to reduce the transfer * latency on the bus by minimising the delay between a transfer * request and the scheduling of the message pump thread. Without this * setting the message pump thread will remain at default priority. */ if (master->rt) { dev_info(&master->dev, "will run message pump with realtime priority\n"); sched_setscheduler(master->kworker_task, SCHED_FIFO, &param); } return 0; }

Contributors

PersonTokensPropCommitsCommitProp
Linus Walleij12286.52%116.67%
David Brownell85.67%116.67%
Jarkko Nikula64.26%116.67%
Petr Mladek21.42%116.67%
Kees Cook21.42%116.67%
Adrian Bunk10.71%116.67%
Total141100.00%6100.00%

/** * spi_get_next_queued_message() - called by driver to check for queued * messages * @master: the master to check for queued messages * * If there are more messages in the queue, the next message is returned from * this call. * * Return: the next message in the queue, else NULL if the queue is empty. */
struct spi_message *spi_get_next_queued_message(struct spi_master *master) { struct spi_message *next; unsigned long flags; /* get a pointer to the next message, if any */ spin_lock_irqsave(&master->queue_lock, flags); next = list_first_entry_or_null(&master->queue, struct spi_message, queue); spin_unlock_irqrestore(&master->queue_lock, flags); return next; }

Contributors

PersonTokensPropCommitsCommitProp
Linus Walleij5896.67%150.00%
Axel Lin23.33%150.00%
Total60100.00%2100.00%

EXPORT_SYMBOL_GPL(spi_get_next_queued_message); /** * spi_finalize_current_message() - the current message is complete * @master: the master to return the message to * * Called by the driver to notify the core that the message in the front of the * queue is complete and can be removed from the queue. */
void spi_finalize_current_message(struct spi_master *master) { struct spi_message *mesg; unsigned long flags; int ret; spin_lock_irqsave(&master->queue_lock, flags); mesg = master->cur_msg; spin_unlock_irqrestore(&master->queue_lock, flags); spi_unmap_msg(master, mesg); if (master->cur_msg_prepared && master->unprepare_message) { ret = master->unprepare_message(master, mesg); if (ret) { dev_err(&master->dev, "failed to unprepare message: %d\n", ret); } } spin_lock_irqsave(&master->queue_lock, flags); master->cur_msg = NULL; master->cur_msg_prepared = false; kthread_queue_work(&master->kworker, &master->pump_messages); spin_unlock_irqrestore(&master->queue_lock, flags); trace_spi_message_done(mesg); mesg->state = NULL; if (mesg->complete) mesg->complete(mesg->context); }

Contributors

PersonTokensPropCommitsCommitProp
Linus Walleij6639.52%116.67%
Mark Brown5734.13%233.33%
Martin Sperl4023.95%116.67%
Uwe Kleine-König31.80%116.67%
Petr Mladek10.60%116.67%
Total167100.00%6100.00%

EXPORT_SYMBOL_GPL(spi_finalize_current_message);
static int spi_start_queue(struct spi_master *master) { unsigned long flags; spin_lock_irqsave(&master->queue_lock, flags); if (master->running || master->busy) { spin_unlock_irqrestore(&master->queue_lock, flags); return -EBUSY; } master->running = true; master->cur_msg = NULL; spin_unlock_irqrestore(&master->queue_lock, flags); kthread_queue_work(&master->kworker, &master->pump_messages); return 0; }

Contributors

PersonTokensPropCommitsCommitProp
Linus Walleij8898.88%150.00%
Petr Mladek11.12%150.00%
Total89100.00%2100.00%


static int spi_stop_queue(struct spi_master *master) { unsigned long flags; unsigned limit = 500; int ret = 0; spin_lock_irqsave(&master->queue_lock, flags); /* * This is a bit lame, but is optimized for the common execution path. * A wait_queue on the master->busy could be used, but then the common * execution path (pump_messages) would be required to call wake_up or * friends on every SPI message. Do this instead. */ while ((!list_empty(&master->queue) || master->busy) && limit--) { spin_unlock_irqrestore(&master->queue_lock, flags); usleep_range(10000, 11000); spin_lock_irqsave(&master->queue_lock, flags); } if (!list_empty(&master->queue) || master->busy) ret = -EBUSY; else master->running = false; spin_unlock_irqrestore(&master->queue_lock, flags); if (ret) { dev_warn(&master->dev, "could not stop message queue\n"); return ret; } return ret; }

Contributors

PersonTokensPropCommitsCommitProp
Linus Walleij14097.22%150.00%
Axel Lin42.78%150.00%
Total144100.00%2100.00%


static int spi_destroy_queue(struct spi_master *master) { int ret; ret = spi_stop_queue(master); /* * kthread_flush_worker will block until all work is done. * If the reason that stop_queue timed out is that the work will never * finish, then it does no good to call flush/stop thread, so * return anyway. */ if (ret) { dev_err(&master->dev, "problem destroying queue\n"); return ret; } kthread_flush_worker(&master->kworker); kthread_stop(master->kworker_task); return 0; }

Contributors

PersonTokensPropCommitsCommitProp
Linus Walleij5796.61%150.00%
Petr Mladek23.39%150.00%
Total59100.00%2100.00%


static int __spi_queued_transfer(struct spi_device *spi, struct spi_message *msg, bool need_pump) { struct spi_master *master = spi->master; unsigned long flags; spin_lock_irqsave(&master->queue_lock, flags); if (!master->running) { spin_unlock_irqrestore(&master->queue_lock, flags); return -ESHUTDOWN; } msg->actual_length = 0; msg->status = -EINPROGRESS; list_add_tail(&msg->queue, &master->queue); if (!master->busy && need_pump) kthread_queue_work(&master->kworker, &master->pump_messages); spin_unlock_irqrestore(&master->queue_lock, flags); return 0; }

Contributors

PersonTokensPropCommitsCommitProp
Linus Walleij11994.44%133.33%
Mark Brown64.76%133.33%
Petr Mladek10.79%133.33%
Total126100.00%3100.00%

/** * spi_queued_transfer - transfer function for queued transfers * @spi: spi device which is requesting transfer * @msg: spi message which is to handled is queued to driver queue * * Return: zero on success, else a negative error code. */
static int spi_queued_transfer(struct spi_device *spi, struct spi_message *msg) { return __spi_queued_transfer(spi, msg, true); }

Contributors

PersonTokensPropCommitsCommitProp
Mark Brown26100.00%1100.00%
Total26100.00%1100.00%


static int spi_master_initialize_queue(struct spi_master *master) { int ret; master->transfer = spi_queued_transfer; if (!master->transfer_one_message) master->transfer_one_message = spi_transfer_one_message; /* Initialize and start queue */ ret = spi_init_queue(master); if (ret) { dev_err(&master->dev, "problem initializing queue\n"); goto err_init_queue; } master->queued = true; ret = spi_start_queue(master); if (ret) { dev_err(&master->dev, "problem starting queue\n"); goto err_start_queue; } return 0; err_start_queue: spi_destroy_queue(master); err_init_queue: return ret; }

Contributors

PersonTokensPropCommitsCommitProp
Linus Walleij8680.37%133.33%
Mark Brown2119.63%266.67%
Total107100.00%3100.00%

/*-------------------------------------------------------------------------*/ #if defined(CONFIG_OF)
static int of_spi_parse_dt(struct spi_master *master, struct spi_device *spi, struct device_node *nc) { u32 value; int rc; /* Device address */ rc = of_property_read_u32(nc, "reg", &value); if (rc) { dev_err(&master->dev, "%s has no valid 'reg' property (%d)\n", nc->full_name, rc); return rc; } spi->chip_select = value; /* Mode (clock phase/polarity/etc.) */ if (of_find_property(nc, "spi-cpha", NULL)) spi->mode |= SPI_CPHA; if (of_find_property(nc, "spi-cpol", NULL)) spi->mode |= SPI_CPOL; if (of_find_property(nc, "spi-cs-high", NULL)) spi->mode |= SPI_CS_HIGH; if (of_find_property(nc, "spi-3wire", NULL)) spi->mode |= SPI_3WIRE; if (of_find_property(nc, "spi-lsb-first", NULL)) spi->mode |= SPI_LSB_FIRST; /* Device DUAL/QUAD mode */ if (!of_property_read_u32(nc, "spi-tx-bus-width", &value)) { switch (value) { case 1: break; case 2: spi->mode |= SPI_TX_DUAL; break; case 4: spi->mode |= SPI_TX_QUAD; break; default: dev_warn(&master->dev, "spi-tx-bus-width %d not supported\n", value); break; } } if (!of_property_read_u32(nc, "spi-rx-bus-width", &value)) { switch (value) { case 1: break; case 2: spi->mode |= SPI_RX_DUAL; break; case 4: spi->mode |= SPI_RX_QUAD; break; default: dev_warn(&master->dev, "spi-rx-bus-width %d not supported\n", value); break; } } /* Device speed */ rc = of_property_read_u32(nc, "spi-max-frequency", &value); if (rc) { dev_err(&master->dev, "%s has no valid 'spi-max-frequency' property (%d)\n", nc->full_name, rc); return rc; } spi->max_speed_hz = value; return 0; }

Contributors

PersonTokensPropCommitsCommitProp
Grant C. Likely12137.46%110.00%
wangyuhang9128.17%220.00%
Trent Piepho3711.46%110.00%
Geert Uytterhoeven309.29%220.00%
Lars-Peter Clausen175.26%110.00%
Zhao Qiang175.26%110.00%
Pantelis Antoniou61.86%110.00%
Mark Brown41.24%110.00%
Total323100.00%10100.00%


static struct spi_device * of_register_spi_device(struct spi_master *master, struct device_node *nc) { struct spi_device *spi; int rc; /* Alloc an spi_device */ spi = spi_alloc_device(master); if (!spi) { dev_err(&master->dev, "spi_device alloc error for %s\n", nc->full_name); rc = -ENOMEM; goto err_out; } /* Select device driver */ rc = of_modalias_node(nc, spi->modalias, sizeof(spi->modalias)); if (rc < 0) { dev_err(&master->dev, "cannot find modalias for %s\n", nc->full_name); goto err_out; } rc = of_spi_parse_dt(master, spi, nc); if (rc) goto err_out; /* Store a pointer to the node in the device structure */ of_node_get(nc); spi->dev.of_node = nc; /* Register the new device */ rc = spi_add_device(spi); if (rc) { dev_err(&master->dev, "spi_device register error %s\n", nc->full_name); goto err_of_node_put; } return spi; err_of_node_put: of_node_put(nc); err_out: spi_dev_put(spi); return ERR_PTR(rc); }

Contributors

PersonTokensPropCommitsCommitProp
Geert Uytterhoeven11660.10%125.00%
Grant C. Likely5226.94%125.00%
Pantelis Antoniou178.81%125.00%
Johan Hovold84.15%125.00%
Total193100.00%4100.00%

/** * of_register_spi_devices() - Register child devices onto the SPI bus * @master: Pointer to spi_master device * * Registers an spi_device for each child node of master node which has a 'reg' * property. */
static void of_register_spi_devices(struct spi_master *master) { struct spi_device *spi; struct device_node *nc; if (!master->dev.of_node) return; for_each_available_child_of_node(master->dev.of_node, nc) { if (of_node_test_and_set_flag(nc, OF_POPULATED)) continue; spi = of_register_spi_device(master, nc); if (IS_ERR(spi)) { dev_warn(&master->dev, "Failed to create SPI device for %s\n", nc->full_name); of_node_clear_flag(nc, OF_POPULATED); } } }

Contributors

PersonTokensPropCommitsCommitProp
Pantelis Antoniou6776.14%125.00%
Geert Uytterhoeven1011.36%125.00%
Ralf Ramsauer910.23%125.00%
Grant C. Likely22.27%125.00%
Total88100.00%4100.00%

#else
static void of_register_spi_devices(struct spi_master *master) { }

Contributors

PersonTokensPropCommitsCommitProp
Grant C. Likely10100.00%1100.00%
Total10100.00%1100.00%

#endif #ifdef CONFIG_ACPI
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) { /* * 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) spi->mode |= SPI_CPHA; if (sb->clock_polarity == ACPI_SPI_START_HIGH) spi->mode |= SPI_CPOL; if (sb->device_polarity == ACPI_SPI_ACTIVE_HIGH) spi->mode |= SPI_CS_HIGH; } } else if (spi->irq < 0) { struct resource r; if (acpi_dev_resource_interrupt(ares, 0, &r)) spi->irq = r.start; } /* Always tell the ACPI core to skip this resource */ return 1; }

Contributors

PersonTokensPropCommitsCommitProp
Mika Westerberg203100.00%2100.00%
Total203100.00%2100.00%


static acpi_status acpi_register_spi_device(struct spi_master *master, struct acpi_device *adev) { struct list_head resource_list; struct spi_device *spi; int ret; if (acpi_bus_get_status(adev) || !adev->status.present || acpi_device_enumerated(adev)) return AE_OK; spi = spi_alloc_device(master); if (!spi) { dev_err(&master->dev, "failed to allocate SPI device for %s\n", dev_name(&adev->dev)); return AE_NO_MEMORY; } ACPI_COMPANION_SET(&spi->dev, adev); spi->irq = -1; INIT_LIST_HEAD(&resource_list); ret = acpi_dev_get_resources(adev, &resource_list, acpi_spi_add_resource, spi); acpi_dev_free_resource_list(&resource_list); if (ret < 0 || !spi->max_speed_hz) { spi_dev_put(spi); return AE_OK; } acpi_set_modalias(adev, acpi_device_hid(adev), spi->modalias, sizeof(spi->modalias)); if (spi->irq < 0) spi->irq = acpi_dev_gpio_irq_get(adev, 0); acpi_device_set_enumerated(adev); adev->power.flags.ignore_parent = true; if (spi_add_device(spi)) { adev->power.flags.ignore_parent = false; dev_err(&master->dev, "failed to add SPI device %s from ACPI\n", dev_name(&adev->dev)); spi_dev_put(spi); } return AE_OK; }

Contributors

PersonTokensPropCommitsCommitProp
Mika Westerberg19076.31%233.33%
Dan O'Donovan218.43%116.67%
Christophe Ricard197.63%116.67%
Octavian Purdila176.83%116.67%
Rafael J. Wysocki20.80%116.67%
Total249100.00%6100.00%


static acpi_status acpi_spi_add_device(acpi_handle handle, u32 level, void *data, void **return_value) { struct spi_master *master = data; struct acpi_device *adev; if (acpi_bus_get_device(handle, &adev)) return AE_OK; return acpi_register_spi_device(master, adev); }

Contributors

PersonTokensPropCommitsCommitProp
Octavian Purdila54100.00%1100.00%
Total54100.00%1100.00%


static void acpi_register_spi_devices(struct spi_master *master) { acpi_status status; acpi_handle handle; handle = ACPI_HANDLE(master->dev.parent); if (!handle) return; status = acpi_walk_namespace(ACPI_TYPE_DEVICE, handle, 1, acpi_spi_add_device, NULL, master, NULL); if (ACPI_FAILURE(status)) dev_warn(&master->dev, "failed to enumerate SPI slaves\n"); }

Contributors

PersonTokensPropCommitsCommitProp
Mika Westerberg6897.14%150.00%
Rafael J. Wysocki22.86%150.00%
Total70100.00%2100.00%

#else
static inline void acpi_register_spi_devices(struct spi_master *master) {}

Contributors

PersonTokensPropCommitsCommitProp
Mika Westerberg11100.00%1100.00%
Total11100.00%1100.00%

#endif /* CONFIG_ACPI */
static void spi_master_release(struct device *dev) { struct spi_master *master; master = container_of(dev, struct spi_master, dev); kfree(master); }

Contributors

PersonTokensPropCommitsCommitProp
Linus Walleij33100.00%1100.00%
Total33100.00%1100.00%

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 * @size: how much zeroed driver-private data to allocate; the pointer to this * memory is in the driver_data field of the returned device, * accessible with spi_master_get_devdata(). * Context: can sleep * * This call is used only by SPI master controller drivers, which are the * only ones directly touching chip registers. It's how they allocate * an spi_master structure, prior to calling spi_register_master(). * * This must be called from context that can sleep. * * The caller is responsible for assigning the bus number and initializing * the master's methods before calling spi_register_master(); and (after errors * adding the device) calling spi_master_put() to prevent a memory leak. * * Return: the SPI master structure on success, else NULL. */
struct spi_master *spi_alloc_master(struct device *dev, unsigned size) { struct spi_master *master; if (!dev) return NULL; master = kzalloc(size + sizeof(*master), GFP_KERNEL); if (!master) return NULL; device_initialize(&master->dev); master->bus_num = -1; master->num_chipselect = 1; master->dev.class = &spi_master_class; master->dev.parent = dev; pm_suspend_ignore_children(&master->dev, true); spi_master_set_devdata(master, &master[1]); return master; }

Contributors

PersonTokensPropCommitsCommitProp
Linus Walleij9785.84%250.00%
Grant C. Likely1311.50%125.00%
Jingoo Han32.65%125.00%
Total113100.00%4100.00%

EXPORT_SYMBOL_GPL(spi_alloc_master); #ifdef CONFIG_OF
static int of_spi_register_master(struct spi_master *master) { int nb, i, *cs; struct device_node *np = master->dev.of_node; if (!np) return 0; nb = of_gpio_named_count(np, "cs-gpios"); master->num_chipselect = max_t(int, nb, master->num_chipselect); /* Return error only for an incorrectly formed cs-gpios property */ if (nb == 0 || nb == -ENOENT) return 0; else if (nb < 0) return nb; cs = devm_kzalloc(&master->dev, sizeof(int) * master->num_chipselect, GFP_KERNEL); master->cs_gpios = cs; if (!master->cs_gpios) return -ENOMEM; for (i = 0; i < master->num_chipselect; i++) cs[i] = -ENOENT; for (i = 0; i < nb; i++) cs[i] = of_get_named_gpio(np, "cs-gpios", i); return 0; }

Contributors

PersonTokensPropCommitsCommitProp
Jean-Christophe Plagniol-Villard13676.40%116.67%
Andreas Larsson3720.79%350.00%
Jingoo Han31.69%116.67%
Grant C. Likely21.12%116.67%
Total178100.00%6100.00%

#else
static int of_spi_register_master(struct spi_master *master) { return 0; }

Contributors

PersonTokensPropCommitsCommitProp
Jean-Christophe Plagniol-Villard14100.00%1100.00%
Total14100.00%1100.00%

#endif /** * spi_register_master - register SPI master controller * @master: initialized master, originally from spi_alloc_master() * Context: can sleep * * SPI master controllers connect to their drivers using some non-SPI bus, * such as the platform bus. The final stage of probe() in that code * includes calling spi_register_master() to hook up to this SPI bus glue. * * SPI controllers use board specific (often SOC specific) bus numbers, * and board-specific addressing for SPI devices combines those numbers * with chip select numbers. Since SPI does not directly support dynamic * device identification, boards need configuration tables telling which * chip is at which address. * * This must be called from context that can sleep. It returns zero on * success, else a negative error code (dropping the master's refcount). * After a successful return, the caller is responsible for calling * spi_unregister_master(). * * Return: zero on success, else a negative error code. */
int spi_register_master(struct spi_master *master) { static atomic_t dyn_bus_id = ATOMIC_INIT((1<<15) - 1); struct device *dev = master->dev.parent; struct boardinfo *bi; int status = -ENODEV; int dynamic = 0; if (!dev) return -ENODEV; status = of_spi_register_master(master); if (status) return status; /* even if it's just one always-selected device, there must * be at least one chipselect */ if (master->num_chipselect == 0) return -EINVAL; if ((master->bus_num < 0) && master->dev.of_node) master->bus_num = of_alias_get_id(master->dev.of_node, "spi"); /* convention: dynamically assigned bus IDs count down from the max */ if (master->bus_num < 0) { /* FIXME switch to an IDR based scheme, something like * I2C now uses, so we can't run out of "dynamic" IDs */ master->bus_num = atomic_dec_return(&dyn_bus_id); dynamic = 1; } INIT_LIST_HEAD(&master->queue); spin_lock_init(&master->queue_lock); spin_lock_init(&master->bus_lock_spinlock); mutex_init(&master->bus_lock_mutex); mutex_init(&master->io_mutex); master->bus_lock_flag = 0; init_completion(&master->xfer_completion); if (!master->max_dma_len) master->max_dma_len = INT_MAX; /* register the device, then userspace will see it. * registration fails if the bus ID is in use. */ dev_set_name(&master->dev, "spi%u", master->bus_num); status = device_add(&master->dev); if (status < 0) goto done; dev_dbg(dev, "registered master %s%s\n", dev_name(&master->dev), dynamic ? " (dynamic)" : ""); /* If we're using a queued driver, start the queue */ if (master->transfer) dev_info(dev, "master is unqueued, this is deprecated\n"); else { status = spi_master_initialize_queue(master); if (status) { device_del(&master->dev); goto done; } } /* add statistics */ spin_lock_init(&master->statistics.lock); mutex_lock(&board_lock); list_add_tail(&master->list, &spi_master_list); list_for_each_entry(bi, &board_list, list) spi_match_master_to_boardinfo(master, &bi->board_info); mutex_unlock(&board_lock); /* Register devices from the device tree and ACPI */ of_register_spi_devices(master); acpi_register_spi_devices(master); done: return status; }

Contributors

PersonTokensPropCommitsCommitProp
David Brownell12934.31%627.27%
Linus Walleij5715.16%14.55%
Mark Brown4511.97%418.18%
Feng Tang4211.17%14.55%
Grant C. Likely318.24%14.55%
Ernst Schwab225.85%14.55%
Jean-Christophe Plagniol-Villard143.72%14.55%
Martin Sperl112.93%14.55%
Mika Westerberg61.60%14.55%
Tony Jones61.60%14.55%
Kay Sievers61.60%14.55%
Anatolij Gustschin51.33%14.55%
Greg Kroah-Hartman10.27%14.55%
Axel Lin10.27%14.55%
Total376100.00%22100.00%

EXPORT_SYMBOL_GPL(spi_register_master);
static void devm_spi_unregister(struct device *dev, void *res) { spi_unregister_master(*(struct spi_master **)res); }

Contributors

PersonTokensPropCommitsCommitProp
Mark Brown27100.00%1100.00%
Total27100.00%1100.00%

/** * dev_spi_register_master - register managed SPI master controller * @dev: device managing SPI master * @master: initialized master, originally from spi_alloc_master() * Context: can sleep * * Register a SPI device as with spi_register_master() which will * automatically be unregister * * Return: zero on success, else a negative error code. */
int devm_spi_register_master(struct device *dev, struct spi_master *master) { struct spi_master **ptr; int ret; ptr = devres_alloc(devm_spi_unregister, sizeof(*ptr), GFP_KERNEL); if (!ptr) return -ENOMEM; ret = spi_register_master(master); if (!ret) { *ptr = master; devres_add(dev, ptr); } else { devres_free(ptr); } return ret; }

Contributors

PersonTokensPropCommitsCommitProp
Mark Brown8498.82%150.00%
Stephen Warren11.18%150.00%
Total85100.00%2100.00%

EXPORT_SYMBOL_GPL(devm_spi_register_master);
static int __unregister(struct device *dev, void *null) { spi_unregister_device(to_spi_device(dev)); return 0; }

Contributors

PersonTokensPropCommitsCommitProp
David Brownell2596.15%266.67%
David Lamparter13.85%133.33%
Total26100.00%3100.00%

/** * spi_unregister_master - unregister SPI master controller * @master: the master being unregistered * Context: can sleep * * This call is used only by SPI master controller drivers, which are the * only ones directly touching chip registers. * * This must be called from context that can sleep. */
void spi_unregister_master(struct spi_master *master) { int dummy; if (master->queued) { if (spi_destroy_queue(master)) dev_err(&master->dev, "queue remove failed\n"); } mutex_lock(&board_lock); list_del(&master->list); mutex_unlock(&board_lock); dummy = device_for_each_child(&master->dev, NULL, __unregister); device_unregister(&master->dev); }

Contributors

PersonTokensPropCommitsCommitProp
Linus Walleij2531.25%112.50%
Feng Tang2430.00%112.50%
David Brownell2126.25%225.00%
Jeff Garzik56.25%112.50%
Tony Jones22.50%112.50%
Sebastian Andrzej Siewior22.50%112.50%
David Lamparter11.25%112.50%
Total80100.00%8100.00%

EXPORT_SYMBOL_GPL(spi_unregister_master);
int spi_master_suspend(struct spi_master *master) { int ret; /* Basically no-ops for non-queued masters */ if (!master->queued) return 0; ret = spi_stop_queue(master); if (ret) dev_err(&master->dev, "queue stop failed\n"); return ret; }

Contributors

PersonTokensPropCommitsCommitProp
Linus Walleij48100.00%1100.00%
Total48100.00%1100.00%

EXPORT_SYMBOL_GPL(spi_master_suspend);
int spi_master_resume(struct spi_master *master) { int ret; if (!master->queued) return 0; ret = spi_start_queue(master); if (ret) dev_err(&master->dev, "queue restart failed\n"); return ret; }

Contributors

PersonTokensPropCommitsCommitProp
Linus Walleij47100.00%1100.00%
Total47100.00%1100.00%

EXPORT_SYMBOL_GPL(spi_master_resume);
static int __spi_master_match(struct device *dev, const void *data) { struct spi_master *m; const u16 *bus_num = data; m = container_of(dev, struct spi_master, dev); return m->bus_num == *bus_num; }

Contributors

PersonTokensPropCommitsCommitProp
Dave Young4695.83%150.00%
Michał Mirosław24.17%150.00%
Total48100.00%2100.00%

/** * spi_busnum_to_master - look up master associated with bus_num * @bus_num: the master's bus number * Context: can sleep * * This call may be used with devices that are registered after * arch init time. It returns a refcounted pointer to the relevant * spi_master (which the caller must release), or NULL if there is * no such master registered. * * Return: the SPI master structure on success, else NULL. */
struct spi_master *spi_busnum_to_master(u16 bus_num) { struct device *dev; struct spi_master *master = NULL; dev = class_find_device(&spi_master_class, NULL, &bus_num, __spi_master_match); if (dev) master = container_of(dev, struct spi_master, dev); /* reference got in class_find_device */ return master; }

Contributors

PersonTokensPropCommitsCommitProp
David Brownell2543.86%120.00%
Dave Young1322.81%120.00%
Atsushi Nemoto1322.81%120.00%
Tony Jones47.02%120.00%
Greg Kroah-Hartman23.51%120.00%
Total57100.00%5100.00%

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; }

Contributors

PersonTokensPropCommitsCommitProp
Martin Sperl5176.12%150.00%
Stefan Brüns1623.88%150.00%
Total67100.00%2100.00%

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); }

Contributors

PersonTokensPropCommitsCommitProp
Martin Sperl4085.11%133.33%
David Brownell510.64%133.33%
wangyuhang24.26%133.33%
Total47100.00%3100.00%

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); }

Contributors

PersonTokensPropCommitsCommitProp
Martin Sperl54100.00%1100.00%
Total54100.00%1100.00%

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); } }

Contributors

PersonTokensPropCommitsCommitProp
Martin Sperl80100.00%1100.00%
Total80100.00%1100.00%

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); }

Contributors

PersonTokensPropCommitsCommitProp
Martin Sperl90100.00%1100.00%
Total90100.00%1100.00%

/** * 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; }

Contributors

PersonTokensPropCommitsCommitProp
Martin Sperl312100.00%1100.00%
Total312100.00%1100.00%

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; }

Contributors

PersonTokensPropCommitsCommitProp
Martin Sperl29096.03%120.00%
Dan Carpenter72.32%120.00%
Fabio Estevam51.66%360.00%
Total302100.00%5100.00%

/** * 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; }

Contributors

PersonTokensPropCommitsCommitProp
Martin Sperl76100.00%1100.00%
Total76100.00%1100.00%

EXPORT_SYMBOL_GPL(spi_split_transfers_maxsize); /*-------------------------------------------------------------------------*/ /* Core methods for SPI master protocol drivers. Some of the * other core methods are currently defined as inline functions. */
static int __spi_validate_bits_per_word(struct spi_master *master, u8 bits_per_word) { if (master->bits_per_word_mask) { /* Only 32 bits fit in the mask */ if (bits_per_word > 32) return -EINVAL; if (!(master->bits_per_word_mask & SPI_BPW_MASK(bits_per_word))) return -EINVAL; } return 0; }

Contributors

PersonTokensPropCommitsCommitProp
Stefan Brüns54100.00%1100.00%
Total54100.00%1100.00%

/** * spi_setup - setup SPI mode and clock rate * @spi: the device whose settings are being modified * Context: can sleep, and no requests are queued to the device * * SPI protocol drivers may need to update the transfer mode if the * device doesn't work with its default. They may likewise need * to update clock rates or word sizes from initial values. This function * changes those settings, and must be called from a context that can sleep. * Except for SPI_CS_HIGH, which takes effect immediately, the changes take * effect the next time the device is selected and data is transferred to * or from it. When this function returns, the spi device is deselected. * * Note that this call will fail if the protocol driver specifies an option * that the underlying controller or its driver does not support. For * example, not all hardware supports wire transfers using nine bit words, * LSB-first wire encoding, or active-high chipselects. * * Return: zero on success, else a negative error code. */
int spi_setup(struct spi_device *spi) { unsigned bad_bits, ugly_bits; int status; /* check mode to prevent that DUAL and QUAD set at the same time */ if (((spi->mode & SPI_TX_DUAL) && (spi->mode & SPI_TX_QUAD)) || ((spi->mode & SPI_RX_DUAL) && (spi->mode & SPI_RX_QUAD))) { dev_err(&spi->dev, "setup: can not select dual and quad at the same time\n"); return -EINVAL; } /* if it is SPI_3WIRE mode, DUAL and QUAD should be forbidden */ if ((spi->mode & SPI_3WIRE) && (spi->mode & (SPI_TX_DUAL | SPI_TX_QUAD | SPI_RX_DUAL | SPI_RX_QUAD))) return -EINVAL; /* help drivers fail *cleanly* when they need options * that aren't supported with their current master */ bad_bits = spi->mode & ~spi->master->mode_bits; ugly_bits = bad_bits & (SPI_TX_DUAL | SPI_TX_QUAD | SPI_RX_DUAL | SPI_RX_QUAD); if (ugly_bits) { dev_warn(&spi->dev, "setup: ignoring unsupported mode bits %x\n", ugly_bits); spi->mode &= ~ugly_bits; bad_bits &= ~ugly_bits; } if (bad_bits) { dev_err(&spi->dev, "setup: unsupported mode bits %x\n", bad_bits); return -EINVAL; } if (!spi->bits_per_word) spi->bits_per_word = 8; status = __spi_validate_bits_per_word(spi->master, spi->bits_per_word); if (status) return status; if (!spi->max_speed_hz) spi->max_speed_hz = spi->master->max_speed_hz; if (spi->master->setup) status = spi->master->setup(spi); spi_set_cs(spi, false); dev_dbg(&spi->dev, "setup mode %d, %s%s%s%s%u bits/w, %u Hz max --> %d\n", (int) (spi->mode & (SPI_CPOL | SPI_CPHA)), (spi->mode & SPI_CS_HIGH) ? "cs_high, " : "", (spi->mode & SPI_LSB_FIRST) ? "lsb, " : "", (spi->mode & SPI_3WIRE) ? "3wire, " : "", (spi->mode & SPI_LOOP) ? "loopback, " : "", spi->bits_per_word, spi->max_speed_hz, status); return status; }

Contributors

PersonTokensPropCommitsCommitProp
David Brownell16046.24%218.18%
wangyuhang8624.86%19.09%
Geert Uytterhoeven4613.29%19.09%
Axel Lin174.91%19.09%
Stefan Brüns154.34%19.09%
Andy Shevchenko72.02%19.09%
Franklin S Cooper Jr72.02%19.09%
Laxman Dewangan61.73%19.09%
Jingoo Han10.29%19.09%
Linus Walleij10.29%19.09%
Total346100.00%11100.00%

EXPORT_SYMBOL_GPL(spi_setup);
static int __spi_validate(struct spi_device *spi, struct spi_message *message) { struct spi_master *master = spi->master; struct spi_transfer *xfer; int w_size; if (list_empty(&message->transfers)) return -EINVAL; /* Half-duplex links include original MicroWire, and ones with * only one data pin like SPI_3WIRE (switches direction) or where * either MOSI or MISO is missing. They can also be caused by * software limitations. */ if ((master->flags & SPI_MASTER_HALF_DUPLEX) || (spi->mode & SPI_3WIRE)) { unsigned flags = master->flags; list_for_each_entry(xfer, &message->transfers, transfer_list) { if (xfer->rx_buf && xfer->tx_buf) return -EINVAL; if ((flags & SPI_MASTER_NO_TX) && xfer->tx_buf) return -EINVAL; if ((flags & SPI_MASTER_NO_RX) && xfer->rx_buf) return -EINVAL; } } /** * Set transfer bits_per_word and max speed as spi device default if * it is not set for this transfer. * Set transfer tx_nbits and rx_nbits as single transfer default * (SPI_NBITS_SINGLE) if it is not set for this transfer. */ message->frame_length = 0; list_for_each_entry(xfer, &message->transfers, transfer_list) { message->frame_length += xfer->len; if (!xfer->bits_per_word) xfer->bits_per_word = spi->bits_per_word; if (!xfer->speed_hz) xfer->speed_hz = spi->max_speed_hz; if (!xfer->speed_hz) xfer->speed_hz = master->max_speed_hz; if (master->max_speed_hz && xfer->speed_hz > master->max_speed_hz) xfer->speed_hz = master->max_speed_hz; if (__spi_validate_bits_per_word(master, xfer->bits_per_word)) return -EINVAL; /* * SPI transfer length should be multiple of SPI word size * where SPI word size should be power-of-two multiple */ if (xfer->bits_per_word <= 8) w_size = 1; else if (xfer->bits_per_word <= 16) w_size = 2; else w_size = 4; /* No partial transfers accepted */ if (xfer->len % w_size) return -EINVAL; if (xfer->speed_hz && master->min_speed_hz && xfer->speed_hz < master->min_speed_hz) return -EINVAL; if (xfer->tx_buf && !xfer->tx_nbits) xfer->tx_nbits = SPI_NBITS_SINGLE; if (xfer->rx_buf && !xfer->rx_nbits) xfer->rx_nbits = SPI_NBITS_SINGLE; /* check transfer tx/rx_nbits: * 1. check the value matches one of single, dual and quad * 2. check tx/rx_nbits match the mode in spi_device */ if (xfer->tx_buf) { if (xfer->tx_nbits != SPI_NBITS_SINGLE && xfer->tx_nbits != SPI_NBITS_DUAL && xfer->tx_nbits != SPI_NBITS_QUAD) return -EINVAL; if ((xfer->tx_nbits == SPI_NBITS_DUAL) && !(spi->mode & (SPI_TX_DUAL | SPI_TX_QUAD))) return -EINVAL; if ((xfer->tx_nbits == SPI_NBITS_QUAD) && !(spi->mode & SPI_TX_QUAD)) return -EINVAL; } /* check transfer rx_nbits */ if (xfer->rx_buf) { if (xfer->rx_nbits != SPI_NBITS_SINGLE && xfer->rx_nbits != SPI_NBITS_DUAL && xfer->rx_nbits != SPI_NBITS_QUAD) return -EINVAL; if ((xfer->rx_nbits == SPI_NBITS_DUAL) && !(spi->mode & (SPI_RX_DUAL | SPI_RX_QUAD))) return -EINVAL; if ((xfer->rx_nbits == SPI_NBITS_QUAD) && !(spi->mode & SPI_RX_QUAD)) return -EINVAL; } } message->status = -EINPROGRESS; return 0; }

Contributors

PersonTokensPropCommitsCommitProp
wangyuhang18435.80%211.11%
David Brownell11422.18%15.56%
Mark Brown5310.31%422.22%
Laxman Dewangan468.95%211.11%
Ivan T. Ivanov356.81%15.56%
Gabor Juhos305.84%15.56%
Sourav Poddar234.47%211.11%
Stephen Warren183.50%15.56%
Martin Sperl61.17%15.56%
Stefan Brüns30.58%15.56%
Ernst Schwab10.19%15.56%
Geert Uytterhoeven10.19%15.56%
Total514100.00%18100.00%


static int __spi_async(struct spi_device *spi, struct spi_message *message) { struct spi_master *master = spi->master; 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); }

Contributors

PersonTokensPropCommitsCommitProp
Mark Brown3654.55%133.33%
Martin Sperl2030.30%133.33%
David Brownell1015.15%133.33%
Total66100.00%3100.00%

/** * spi_async - asynchronous SPI transfer * @spi: device with which data will be exchanged * @message: describes the data transfers, including completion callback * Context: any (irqs may be blocked, etc) * * This call may be used in_irq and other contexts which can't sleep, * as well as from task contexts which can sleep. * * The completion callback is invoked in a context which can't sleep. * Before that invocation, the value of message->status is undefined. * When the callback is issued, message->status holds either zero (to * indicate complete success) or a negative error code. After that * callback returns, the driver which issued the transfer request may * deallocate the associated memory; it's no longer in use by any SPI * core or controller driver code. * * Note that although all messages to a spi_device are handled in * FIFO order, messages may go to different devices in other orders. * Some device might be higher priority, or have various "hard" access * time requirements, for example. * * On detection of any fault during the transfer, processing of * the entire message is aborted, and the device is deselected. * Until returning from the associated message completion callback, * no other spi_message queued to that device will be processed. * (This rule applies equally to all the synchronous transfer calls, * which are wrappers around this core asynchronous primitive.) * * Return: zero on success, else a negative error code. */
int spi_async(struct spi_device *spi, struct spi_message *message) { struct spi_master *master = spi->master; int ret; unsigned long flags; ret = __spi_validate(spi, message); if (ret != 0) return ret; spin_lock_irqsave(&master->bus_lock_spinlock, flags); if (master->bus_lock_flag) ret = -EBUSY; else ret = __spi_async(spi, message); spin_unlock_irqrestore(&master->bus_lock_spinlock, flags); return ret; }

Contributors

PersonTokensPropCommitsCommitProp
Ernst Schwab7580.65%150.00%
Mark Brown1819.35%150.00%
Total93100.00%2100.00%

EXPORT_SYMBOL_GPL(spi_async); /** * spi_async_locked - version of spi_async with exclusive bus usage * @spi: device with which data will be exchanged * @message: describes the data transfers, including completion callback * Context: any (irqs may be blocked, etc) * * This call may be used in_irq and other contexts which can't sleep, * as well as from task contexts which can sleep. * * The completion callback is invoked in a context which can't sleep. * Before that invocation, the value of message->status is undefined. * When the callback is issued, message->status holds either zero (to * indicate complete success) or a negative error code. After that * callback returns, the driver which issued the transfer request may * deallocate the associated memory; it's no longer in use by any SPI * core or controller driver code. * * Note that although all messages to a spi_device are handled in * FIFO order, messages may go to different devices in other orders. * Some device might be higher priority, or have various "hard" access * time requirements, for example. * * On detection of any fault during the transfer, processing of * the entire message is aborted, and the device is deselected. * Until returning from the associated message completion callback, * no other spi_message queued to that device will be processed. * (This rule applies equally to all the synchronous transfer calls, * which are wrappers around this core asynchronous primitive.) * * Return: zero on success, else a negative error code. */
int spi_async_locked(struct spi_device *spi, struct spi_message *message) { struct spi_master *master = spi->master; int ret; unsigned long flags; ret = __spi_validate(spi, message); if (ret != 0) return ret; spin_lock_irqsave(&master->bus_lock_spinlock, flags); ret = __spi_async(spi, message); spin_unlock_irqrestore(&master->bus_lock_spinlock, flags); return ret; }

Contributors

PersonTokensPropCommitsCommitProp
Ernst Schwab6377.78%150.00%
Mark Brown1822.22%150.00%
Total81100.00%2100.00%

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; struct device *rx_dev = NULL; 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); mutex_lock(&master->io_mutex); if (master->dma_rx) { rx_dev = master->dma_rx->device->dev; ret = spi_map_buf(master, rx_dev, &msg->rx_sg, msg->buf, msg->len, DMA_FROM_DEVICE); if (!ret) msg->cur_msg_mapped = true; } ret = master->spi_flash_read(spi, msg); if (msg->cur_msg_mapped) spi_unmap_buf(master, rx_dev, &msg->rx_sg, DMA_FROM_DEVICE); mutex_unlock(&master->io_mutex); mutex_unlock(&master->bus_lock_mutex); if (master->auto_runtime_pm) pm_runtime_put(master->dev.parent); return ret; }

Contributors

PersonTokensPropCommitsCommitProp
Vignesh R30796.54%266.67%
Mark Brown113.46%133.33%
Total318100.00%3100.00%

EXPORT_SYMBOL_GPL(spi_flash_read); /*-------------------------------------------------------------------------*/ /* Utility methods for SPI master protocol drivers, layered on * top of the core. Some other utility methods are defined as * inline functions. */
static void spi_complete(void *arg) { complete(arg); }

Contributors

PersonTokensPropCommitsCommitProp
Andrew Morton15100.00%1100.00%
Total15100.00%1100.00%


static int __spi_sync(struct spi_device *spi, struct spi_message *message) { DECLARE_COMPLETION_ONSTACK(done); int status; struct spi_master *master = spi->master; unsigned long flags; status = __spi_validate(spi, message); if (status != 0) return status; message->complete = spi_complete; message->context = &done; message->spi = spi; SPI_STATISTICS_INCREMENT_FIELD(&master->statistics, spi_sync); SPI_STATISTICS_INCREMENT_FIELD(&spi->statistics, spi_sync); /* If we're not using the legacy transfer method then we will * try to transfer in the calling context so special case. * This code would be less tricky if we could remove the * support for driver implemented message queues. */ if (master->transfer == spi_queued_transfer) { spin_lock_irqsave(&master->bus_lock_spinlock, flags); trace_spi_message_submit(message); status = __spi_queued_transfer(spi, message, false); spin_unlock_irqrestore(&master->bus_lock_spinlock, flags); } else { status = spi_async_locked(spi, message); } if (status == 0) { /* Push out the messages in the calling context if we * can. */ 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; } message->context = NULL; return status; }

Contributors

PersonTokensPropCommitsCommitProp
Mark Brown9442.73%225.00%
David Brownell6228.18%112.50%
Martin Sperl4219.09%112.50%
Ernst Schwab125.45%112.50%
Marc Pignat83.64%112.50%
Ingo Molnar10.45%112.50%
Andrew Morton10.45%112.50%
Total220100.00%8100.00%

/** * spi_sync - blocking/synchronous SPI data transfers * @spi: device with which data will be exchanged * @message: describes the data transfers * Context: can sleep * * This call may only be used from a context that may sleep. The sleep * is non-interruptible, and has no timeout. Low-overhead controller * drivers may DMA directly into and out of the message buffers. * * Note that the SPI device's chip select is active during the message, * and then is normally disabled between messages. Drivers for some * frequently-used devices may want to minimize costs of selecting a chip, * by leaving it selected in anticipation that the next message will go * to the same chip. (That may increase power usage.) * * Also, the caller is guaranteeing that the memory associated with the * message will not be freed before this call returns. * * Return: zero on success, else a negative error code. */
int spi_sync(struct spi_device *spi, struct spi_message *message) { int ret; mutex_lock(&spi->master->bus_lock_mutex); ret = __spi_sync(spi, message); mutex_unlock(&spi->master->bus_lock_mutex); return ret; }

Contributors

PersonTokensPropCommitsCommitProp
Mark Brown2448.00%133.33%
Ernst Schwab2244.00%133.33%
Heiko Stübner48.00%133.33%
Total50100.00%3100.00%

EXPORT_SYMBOL_GPL(spi_sync); /** * spi_sync_locked - version of spi_sync with exclusive bus usage * @spi: device with which data will be exchanged * @message: describes the data transfers * Context: can sleep * * This call may only be used from a context that may sleep. The sleep * is non-interruptible, and has no timeout. Low-overhead controller * drivers may DMA directly into and out of the message buffers. * * This call should be used by drivers that require exclusive access to the * SPI bus. It has to be preceded by a spi_bus_lock call. The SPI bus must * be released by a spi_bus_unlock call when the exclusive access is over. * * Return: zero on success, else a negative error code. */
int spi_sync_locked(struct spi_device *spi, struct spi_message *message) { return __spi_sync(spi, message); }

Contributors

PersonTokensPropCommitsCommitProp
Ernst Schwab23100.00%1100.00%
Total23100.00%1100.00%

EXPORT_SYMBOL_GPL(spi_sync_locked); /** * spi_bus_lock - obtain a lock for exclusive SPI bus usage * @master: SPI bus master that should be locked for exclusive bus access * Context: can sleep * * This call may only be used from a context that may sleep. The sleep * is non-interruptible, and has no timeout. * * This call should be used by drivers that require exclusive access to the * SPI bus. The SPI bus must be released by a spi_bus_unlock call when the * exclusive access is over. Data transfer must be done by spi_sync_locked * and spi_async_locked calls when the SPI bus lock is held. * * Return: always zero. */
int spi_bus_lock(struct spi_master *master) { unsigned long flags; mutex_lock(&master->bus_lock_mutex); spin_lock_irqsave(&master->bus_lock_spinlock, flags); master->bus_lock_flag = 1; spin_unlock_irqrestore(&master->bus_lock_spinlock, flags); /* mutex remains locked until spi_bus_unlock is called */ return 0; }

Contributors

PersonTokensPropCommitsCommitProp
Ernst Schwab52100.00%1100.00%
Total52100.00%1100.00%

EXPORT_SYMBOL_GPL(spi_bus_lock); /** * spi_bus_unlock - release the lock for exclusive SPI bus usage * @master: SPI bus master that was locked for exclusive bus access * Context: can sleep * * This call may only be used from a context that may sleep. The sleep * is non-interruptible, and has no timeout. * * This call releases an SPI bus lock previously obtained by an spi_bus_lock * call. * * Return: always zero. */
int spi_bus_unlock(struct spi_master *master) { master->bus_lock_flag = 0; mutex_unlock(&master->bus_lock_mutex); return 0; }

Contributors

PersonTokensPropCommitsCommitProp
Ernst Schwab27100.00%1100.00%
Total27100.00%1100.00%

EXPORT_SYMBOL_GPL(spi_bus_unlock); /* portable code must never pass more than 32 bytes */ #define SPI_BUFSIZ max(32, SMP_CACHE_BYTES) static u8 *buf; /** * spi_write_then_read - SPI synchronous write followed by read * @spi: device with which data will be exchanged * @txbuf: data to be written (need not be dma-safe) * @n_tx: size of txbuf, in bytes * @rxbuf: buffer into which data will be read (need not be dma-safe) * @n_rx: size of rxbuf, in bytes * Context: can sleep * * This performs a half duplex MicroWire style transaction with the * device, sending txbuf and then reading rxbuf. The return value * is zero for success, else a negative errno status code. * This call may only be used from a context that may sleep. * * Parameters to this routine are always copied using a small buffer; * portable code should never use this for more than 32 bytes. * Performance-sensitive or bulk transfer code should instead use * spi_{async,sync}() calls with dma-safe buffers. * * Return: zero on success, else a negative error code. */
int spi_write_then_read(struct spi_device *spi, const void *txbuf, unsigned n_tx, void *rxbuf, unsigned n_rx) { static DEFINE_MUTEX(lock); int status; struct spi_message message; struct spi_transfer x[2]; u8 *local_buf; /* Use preallocated DMA-safe buffer if we can. We can't avoid * copying here, (as a pure convenience thing), but we can * keep heap costs out of the hot path unless someone else is * using the pre-allocated buffer or the transfer is too large. */ if ((n_tx + n_rx) > SPI_BUFSIZ || !mutex_trylock(&lock)) { local_buf = kmalloc(max((unsigned)SPI_BUFSIZ, n_tx + n_rx), GFP_KERNEL | GFP_DMA); if (!local_buf) return -ENOMEM; } else { local_buf = buf; } spi_message_init(&message); memset(x, 0, sizeof(x)); if (n_tx) { x[0].len = n_tx; spi_message_add_tail(&x[0], &message); } if (n_rx) { x[1].len = n_rx; spi_message_add_tail(&x[1], &message); } memcpy(local_buf, txbuf, n_tx); x[0].tx_buf = local_buf; x[1].rx_buf = local_buf + n_tx; /* do the i/o */ status = spi_sync(spi, &message); if (status == 0) memcpy(rxbuf, x[1].rx_buf, n_rx); if (x[0].tx_buf == buf) mutex_unlock(&lock); else kfree(local_buf); return status; }

Contributors

PersonTokensPropCommitsCommitProp
David Brownell18469.70%333.33%
Mark Brown4316.29%333.33%
Vitaly Wool3212.12%111.11%
Grant C. Likely31.14%111.11%
Jingoo Han20.76%111.11%
Total264100.00%9100.00%

EXPORT_SYMBOL_GPL(spi_write_then_read); /*-------------------------------------------------------------------------*/ #if IS_ENABLED(CONFIG_OF_DYNAMIC)
static int __spi_of_device_match(struct device *dev, void *data) { return dev->of_node == data; }

Contributors

PersonTokensPropCommitsCommitProp
Pantelis Antoniou22100.00%1100.00%
Total22100.00%1100.00%

/* must call put_device() when done with returned spi_device device */
static struct spi_device *of_find_spi_device_by_node(struct device_node *node) { struct device *dev = bus_find_device(&spi_bus_type, NULL, node, __spi_of_device_match); return dev ? to_spi_device(dev) : NULL; }

Contributors

PersonTokensPropCommitsCommitProp
Pantelis Antoniou40100.00%1100.00%
Total40100.00%1100.00%


static int __spi_of_master_match(struct device *dev, const void *data) { return dev->of_node == data; }

Contributors

PersonTokensPropCommitsCommitProp
Pantelis Antoniou23100.00%1100.00%
Total23100.00%1100.00%

/* the spi masters are not using spi_bus, so we find it with another way */
static struct spi_master *of_find_spi_master_by_node(struct device_node *node) { struct device *dev; dev = class_find_device(&spi_master_class, NULL, node, __spi_of_master_match); if (!dev) return NULL; /* reference got in class_find_device */ return container_of(dev, struct spi_master, dev); }

Contributors

PersonTokensPropCommitsCommitProp
Pantelis Antoniou52100.00%1100.00%
Total52100.00%1100.00%


static int of_spi_notify(struct notifier_block *nb, unsigned long action, void *arg) { struct of_reconfig_data *rd = arg; struct spi_master *master; struct spi_device *spi; switch (of_reconfig_get_state_change(action, arg)) { case OF_RECONFIG_CHANGE_ADD: master = of_find_spi_master_by_node(rd->dn->parent); if (master == NULL) return NOTIFY_OK; /* not for us */ if (of_node_test_and_set_flag(rd->dn, OF_POPULATED)) { put_device(&master->dev); return NOTIFY_OK; } spi = of_register_spi_device(master, rd->dn); put_device(&master->dev); if (IS_ERR(spi)) { pr_err("%s: failed to create for '%s'\n", __func__, rd->dn->full_name); of_node_clear_flag(rd->dn, OF_POPULATED); return notifier_from_errno(PTR_ERR(spi)); } break; case OF_RECONFIG_CHANGE_REMOVE: /* already depopulated? */ if (!of_node_check_flag(rd->dn, OF_POPULATED)) return NOTIFY_OK; /* find our device by node */ spi = of_find_spi_device_by_node(rd->dn); if (spi == NULL) return NOTIFY_OK; /* no? not meant for us */ /* unregister takes one ref away */ spi_unregister_device(spi); /* and put the reference of the find */ put_device(&spi->dev); break; } return NOTIFY_OK; }

Contributors

PersonTokensPropCommitsCommitProp
Pantelis Antoniou16477.00%133.33%
Geert Uytterhoeven4018.78%133.33%
Ralf Ramsauer94.23%133.33%
Total213100.00%3100.00%

static struct notifier_block spi_of_notifier = { .notifier_call = of_spi_notify, }; #else /* IS_ENABLED(CONFIG_OF_DYNAMIC) */ extern struct notifier_block spi_of_notifier; #endif /* IS_ENABLED(CONFIG_OF_DYNAMIC) */ #if IS_ENABLED(CONFIG_ACPI)
static int spi_acpi_master_match(struct device *dev, const void *data) { return ACPI_COMPANION(dev->parent) == data; }

Contributors

PersonTokensPropCommitsCommitProp
Octavian Purdila26100.00%1100.00%
Total26100.00%1100.00%


static int spi_acpi_device_match(struct device *dev, void *data) { return ACPI_COMPANION(dev) == data; }

Contributors

PersonTokensPropCommitsCommitProp
Octavian Purdila23100.00%1100.00%
Total23100.00%1100.00%


static struct spi_master *acpi_spi_find_master_by_adev(struct acpi_device *adev) { struct device *dev; dev = class_find_device(&spi_master_class, NULL, adev, spi_acpi_master_match); if (!dev) return NULL; return container_of(dev, struct spi_master, dev); }

Contributors

PersonTokensPropCommitsCommitProp
Octavian Purdila51100.00%1100.00%
Total51100.00%1100.00%


static struct spi_device *acpi_spi_find_device_by_adev(struct acpi_device *adev) { struct device *dev; dev = bus_find_device(&spi_bus_type, NULL, adev, spi_acpi_device_match); return dev ? to_spi_device(dev) : NULL; }

Contributors

PersonTokensPropCommitsCommitProp
Octavian Purdila42100.00%1100.00%
Total42100.00%1100.00%


static int acpi_spi_notify(struct notifier_block *nb, unsigned long value, void *arg) { struct acpi_device *adev = arg; struct spi_master *master; struct spi_device *spi; switch (value) { case ACPI_RECONFIG_DEVICE_ADD: master = acpi_spi_find_master_by_adev(adev->parent); if (!master) break; acpi_register_spi_device(master, adev); put_device(&master->dev); break; case ACPI_RECONFIG_DEVICE_REMOVE: if (!acpi_device_enumerated(adev)) break; spi = acpi_spi_find_device_by_adev(adev); if (!spi) break; spi_unregister_device(spi); put_device(&spi->dev); break; } return NOTIFY_OK; }

Contributors

PersonTokensPropCommitsCommitProp
Octavian Purdila118100.00%1100.00%
Total118100.00%1100.00%

static struct notifier_block spi_acpi_notifier = { .notifier_call = acpi_spi_notify, }; #else extern struct notifier_block spi_acpi_notifier; #endif
static int __init spi_init(void) { int status; buf = kmalloc(SPI_BUFSIZ, GFP_KERNEL); if (!buf) { status = -ENOMEM; goto err0; } status = bus_register(&spi_bus_type); if (status < 0) goto err1; status = class_register(&spi_master_class); if (status < 0) goto err2; if (IS_ENABLED(CONFIG_OF_DYNAMIC)) WARN_ON(of_reconfig_notifier_register(&spi_of_notifier)); if (IS_ENABLED(CONFIG_ACPI)) WARN_ON(acpi_reconfig_notifier_register(&spi_acpi_notifier)); return 0; err2: bus_unregister(&spi_bus_type); err1: kfree(buf); buf = NULL; err0: return status; }

Contributors

PersonTokensPropCommitsCommitProp
David Brownell9674.42%233.33%
Octavian Purdila1612.40%116.67%
Pantelis Antoniou1511.63%116.67%
Fabio Estevam10.78%116.67%
Christoph Lameter10.78%116.67%
Total129100.00%6100.00%

/* board_info is normally registered in arch_initcall(), * but even essential drivers wait till later * * REVISIT only boardinfo really needs static linking. the rest (device and * driver registration) _could_ be dynamically linked (modular) ... costs * include needing to have boardinfo data structures be much more public. */ postcore_initcall(spi_init);

Overall Contributors

PersonTokensPropCommitsCommitProp
Martin Sperl209916.68%94.52%
Mark Brown196115.58%2814.07%
David Brownell165313.14%168.04%
Linus Walleij12549.97%31.51%
Geert Uytterhoeven6164.90%115.53%
Mika Westerberg6014.78%42.01%
Grant C. Likely5114.06%94.52%
Vignesh R4583.64%42.01%
Pantelis Antoniou4383.48%21.01%
Octavian Purdila3953.14%10.50%
wangyuhang3632.88%31.51%
Ernst Schwab3172.52%10.50%
Jean-Christophe Plagniol-Villard2081.65%10.50%
Feng Tang1821.45%10.50%
Anton Vorontsov1331.06%21.01%
Stefan Brüns880.70%10.50%
Jarkko Nikula780.62%31.51%
Jon Hunter750.60%21.01%
Andrew Gabbasov620.49%10.50%
Dave Young590.47%10.50%
Leilk Liu520.41%10.50%
Laxman Dewangan520.41%31.51%
Rui Zhang480.38%10.50%
Andy Shevchenko480.38%42.01%
Andreas Larsson380.30%31.51%
Daniel Kurtz380.30%21.01%
Trent Piepho370.29%10.50%
Ivan T. Ivanov350.28%10.50%
Thor Thayer350.28%10.50%
Axel Lin330.26%63.02%
Vitaly Wool320.25%10.50%
Gabor Juhos300.24%10.50%
Kay Sievers250.20%21.01%
Bryan Freed240.19%10.50%
Juan Gutierrez240.19%10.50%
Sourav Poddar230.18%21.01%
Sien Wu210.17%10.50%
Sylwester Nawrocki210.17%10.50%
Dan O'Donovan210.17%10.50%
Greg Kroah-Hartman210.17%31.51%
Johan Hovold190.15%10.50%
Stephen Warren190.15%21.01%
Christophe Ricard190.15%10.50%
Javier Martinez Canillas190.15%21.01%
Ralf Ramsauer180.14%10.50%
Lars-Peter Clausen170.14%10.50%
Ulf Hansson170.14%21.01%
Zhao Qiang170.14%10.50%
Dan Carpenter170.14%21.01%
Andrew Morton160.13%10.50%
Sinan Akman160.13%10.50%
Andrew F. Davis150.12%10.50%
Atsushi Nemoto130.10%10.50%
Jingoo Han130.10%10.50%
Tony Jones120.10%10.50%
Charles Keepax100.08%10.50%
Petr Mladek90.07%10.50%
Xiubo Li90.07%10.50%
Marc Pignat80.06%10.50%
Franklin S Cooper Jr70.06%10.50%
Anatolij Gustschin70.06%10.50%
Matthias Kaehlcke60.05%10.50%
Fabio Estevam60.05%42.01%
Jeff Garzik50.04%10.50%
Roman Tereshonkov50.04%21.01%
Jean Delvare40.03%10.50%
Rafael J. Wysocki40.03%21.01%
Shubhrajyoti Datta40.03%10.50%
Ingo Molnar40.03%21.01%
Heiko Stübner40.03%10.50%
Laurent Pinchart30.02%10.50%
Paul Gortmaker30.02%10.50%
Uwe Kleine-König30.02%10.50%
Harini Katakam30.02%10.50%
Sebastian Andrzej Siewior20.02%10.50%
Nicholas Mc Guire20.02%10.50%
SF Markus Elfring20.02%10.50%
Thierry Reding20.02%21.01%
Wolfram Sang20.02%10.50%
David Lamparter20.02%10.50%
Michał Mirosław20.02%10.50%
Tejun Heo20.02%10.50%
Kees Cook20.02%10.50%
Moritz Fischer10.01%10.50%
Geliang Tang10.01%10.50%
Adrian Bunk10.01%10.50%
Clark Williams10.01%10.50%
Christoph Lameter10.01%10.50%
Total12583100.00%199100.00%
Directory: drivers/spi
Information contained on this website is for historical information purposes only and does not indicate or represent copyright ownership.
Created with cregit.