aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/spi/spi.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/spi/spi.c')
-rw-r--r--drivers/spi/spi.c142
1 files changed, 102 insertions, 40 deletions
diff --git a/drivers/spi/spi.c b/drivers/spi/spi.c
index 4fcbb0aa71d3..6e65524cbfd9 100644
--- a/drivers/spi/spi.c
+++ b/drivers/spi/spi.c
@@ -40,9 +40,14 @@
#include <linux/ioport.h>
#include <linux/acpi.h>
#include <linux/highmem.h>
+#include <linux/idr.h>
+#include <linux/platform_data/x86/apple.h>
#define CREATE_TRACE_POINTS
#include <trace/events/spi.h>
+#define SPI_DYN_FIRST_BUS_NUM 0
+
+static DEFINE_IDR(spi_master_idr);
static void spidev_release(struct device *dev)
{
@@ -321,8 +326,7 @@ static int spi_uevent(struct device *dev, struct kobj_uevent_env *env)
if (rc != -ENODEV)
return rc;
- add_uevent_var(env, "MODALIAS=%s%s", SPI_MODULE_PREFIX, spi->modalias);
- return 0;
+ return add_uevent_var(env, "MODALIAS=%s%s", SPI_MODULE_PREFIX, spi->modalias);
}
struct bus_type spi_bus_type = {
@@ -421,6 +425,7 @@ static LIST_HEAD(spi_controller_list);
/*
* Used to protect add/del opertion for board_info list and
* spi_controller list, and their matching process
+ * also used to protect object of type struct idr
*/
static DEFINE_MUTEX(board_lock);
@@ -1533,15 +1538,15 @@ static int of_spi_parse_dt(struct spi_controller *ctlr, struct spi_device *spi,
int rc;
/* Mode (clock phase/polarity/etc.) */
- if (of_find_property(nc, "spi-cpha", NULL))
+ if (of_property_read_bool(nc, "spi-cpha"))
spi->mode |= SPI_CPHA;
- if (of_find_property(nc, "spi-cpol", NULL))
+ if (of_property_read_bool(nc, "spi-cpol"))
spi->mode |= SPI_CPOL;
- if (of_find_property(nc, "spi-cs-high", NULL))
+ if (of_property_read_bool(nc, "spi-cs-high"))
spi->mode |= SPI_CS_HIGH;
- if (of_find_property(nc, "spi-3wire", NULL))
+ if (of_property_read_bool(nc, "spi-3wire"))
spi->mode |= SPI_3WIRE;
- if (of_find_property(nc, "spi-lsb-first", NULL))
+ if (of_property_read_bool(nc, "spi-lsb-first"))
spi->mode |= SPI_LSB_FIRST;
/* Device DUAL/QUAD mode */
@@ -1583,8 +1588,8 @@ static int of_spi_parse_dt(struct spi_controller *ctlr, struct spi_device *spi,
if (spi_controller_is_slave(ctlr)) {
if (strcmp(nc->name, "slave")) {
- dev_err(&ctlr->dev, "%s is not called 'slave'\n",
- nc->full_name);
+ dev_err(&ctlr->dev, "%pOF is not called 'slave'\n",
+ nc);
return -EINVAL;
}
return 0;
@@ -1593,8 +1598,8 @@ static int of_spi_parse_dt(struct spi_controller *ctlr, struct spi_device *spi,
/* Device address */
rc = of_property_read_u32(nc, "reg", &value);
if (rc) {
- dev_err(&ctlr->dev, "%s has no valid 'reg' property (%d)\n",
- nc->full_name, rc);
+ dev_err(&ctlr->dev, "%pOF has no valid 'reg' property (%d)\n",
+ nc, rc);
return rc;
}
spi->chip_select = value;
@@ -1603,8 +1608,7 @@ static int of_spi_parse_dt(struct spi_controller *ctlr, struct spi_device *spi,
rc = of_property_read_u32(nc, "spi-max-frequency", &value);
if (rc) {
dev_err(&ctlr->dev,
- "%s has no valid 'spi-max-frequency' property (%d)\n",
- nc->full_name, rc);
+ "%pOF has no valid 'spi-max-frequency' property (%d)\n", nc, rc);
return rc;
}
spi->max_speed_hz = value;
@@ -1621,8 +1625,7 @@ of_register_spi_device(struct spi_controller *ctlr, struct device_node *nc)
/* Alloc an spi_device */
spi = spi_alloc_device(ctlr);
if (!spi) {
- dev_err(&ctlr->dev, "spi_device alloc error for %s\n",
- nc->full_name);
+ dev_err(&ctlr->dev, "spi_device alloc error for %pOF\n", nc);
rc = -ENOMEM;
goto err_out;
}
@@ -1631,8 +1634,7 @@ of_register_spi_device(struct spi_controller *ctlr, struct device_node *nc)
rc = of_modalias_node(nc, spi->modalias,
sizeof(spi->modalias));
if (rc < 0) {
- dev_err(&ctlr->dev, "cannot find modalias for %s\n",
- nc->full_name);
+ dev_err(&ctlr->dev, "cannot find modalias for %pOF\n", nc);
goto err_out;
}
@@ -1647,8 +1649,7 @@ of_register_spi_device(struct spi_controller *ctlr, struct device_node *nc)
/* Register the new device */
rc = spi_add_device(spi);
if (rc) {
- dev_err(&ctlr->dev, "spi_device register error %s\n",
- nc->full_name);
+ dev_err(&ctlr->dev, "spi_device register error %pOF\n", nc);
goto err_of_node_put;
}
@@ -1682,8 +1683,7 @@ static void of_register_spi_devices(struct spi_controller *ctlr)
spi = of_register_spi_device(ctlr, nc);
if (IS_ERR(spi)) {
dev_warn(&ctlr->dev,
- "Failed to create SPI device for %s\n",
- nc->full_name);
+ "Failed to create SPI device for %pOF\n", nc);
of_node_clear_flag(nc, OF_POPULATED);
}
}
@@ -1693,6 +1693,35 @@ static void of_register_spi_devices(struct spi_controller *ctlr) { }
#endif
#ifdef CONFIG_ACPI
+static void acpi_spi_parse_apple_properties(struct spi_device *spi)
+{
+ struct acpi_device *dev = ACPI_COMPANION(&spi->dev);
+ const union acpi_object *obj;
+
+ if (!x86_apple_machine)
+ return;
+
+ if (!acpi_dev_get_property(dev, "spiSclkPeriod", ACPI_TYPE_BUFFER, &obj)
+ && obj->buffer.length >= 4)
+ spi->max_speed_hz = NSEC_PER_SEC / *(u32 *)obj->buffer.pointer;
+
+ if (!acpi_dev_get_property(dev, "spiWordSize", ACPI_TYPE_BUFFER, &obj)
+ && obj->buffer.length == 8)
+ spi->bits_per_word = *(u64 *)obj->buffer.pointer;
+
+ if (!acpi_dev_get_property(dev, "spiBitOrder", ACPI_TYPE_BUFFER, &obj)
+ && obj->buffer.length == 8 && !*(u64 *)obj->buffer.pointer)
+ spi->mode |= SPI_LSB_FIRST;
+
+ if (!acpi_dev_get_property(dev, "spiSPO", ACPI_TYPE_BUFFER, &obj)
+ && obj->buffer.length == 8 && *(u64 *)obj->buffer.pointer)
+ spi->mode |= SPI_CPOL;
+
+ if (!acpi_dev_get_property(dev, "spiSPH", ACPI_TYPE_BUFFER, &obj)
+ && obj->buffer.length == 8 && *(u64 *)obj->buffer.pointer)
+ spi->mode |= SPI_CPHA;
+}
+
static int acpi_spi_add_resource(struct acpi_resource *ares, void *data)
{
struct spi_device *spi = data;
@@ -1766,6 +1795,8 @@ static acpi_status acpi_register_spi_device(struct spi_controller *ctlr,
acpi_spi_add_resource, spi);
acpi_dev_free_resource_list(&resource_list);
+ acpi_spi_parse_apple_properties(spi);
+
if (ret < 0 || !spi->max_speed_hz) {
spi_dev_put(spi);
return AE_OK;
@@ -2052,11 +2083,10 @@ static int of_spi_register_master(struct spi_controller *ctlr)
*/
int spi_register_controller(struct spi_controller *ctlr)
{
- static atomic_t dyn_bus_id = ATOMIC_INIT((1<<15) - 1);
struct device *dev = ctlr->dev.parent;
struct boardinfo *bi;
int status = -ENODEV;
- int dynamic = 0;
+ int id;
if (!dev)
return -ENODEV;
@@ -2072,19 +2102,28 @@ int spi_register_controller(struct spi_controller *ctlr)
*/
if (ctlr->num_chipselect == 0)
return -EINVAL;
-
- if ((ctlr->bus_num < 0) && ctlr->dev.of_node)
- ctlr->bus_num = of_alias_get_id(ctlr->dev.of_node, "spi");
-
- /* convention: dynamically assigned bus IDs count down from the max */
+ /* allocate dynamic bus number using Linux idr */
+ if ((ctlr->bus_num < 0) && ctlr->dev.of_node) {
+ id = of_alias_get_id(ctlr->dev.of_node, "spi");
+ if (id >= 0) {
+ ctlr->bus_num = id;
+ mutex_lock(&board_lock);
+ id = idr_alloc(&spi_master_idr, ctlr, ctlr->bus_num,
+ ctlr->bus_num + 1, GFP_KERNEL);
+ mutex_unlock(&board_lock);
+ if (WARN(id < 0, "couldn't get idr"))
+ return id == -ENOSPC ? -EBUSY : id;
+ }
+ }
if (ctlr->bus_num < 0) {
- /* FIXME switch to an IDR based scheme, something like
- * I2C now uses, so we can't run out of "dynamic" IDs
- */
- ctlr->bus_num = atomic_dec_return(&dyn_bus_id);
- dynamic = 1;
+ mutex_lock(&board_lock);
+ id = idr_alloc(&spi_master_idr, ctlr, SPI_DYN_FIRST_BUS_NUM, 0,
+ GFP_KERNEL);
+ mutex_unlock(&board_lock);
+ if (WARN(id < 0, "couldn't get idr"))
+ return id;
+ ctlr->bus_num = id;
}
-
INIT_LIST_HEAD(&ctlr->queue);
spin_lock_init(&ctlr->queue_lock);
spin_lock_init(&ctlr->bus_lock_spinlock);
@@ -2100,11 +2139,16 @@ int spi_register_controller(struct spi_controller *ctlr)
*/
dev_set_name(&ctlr->dev, "spi%u", ctlr->bus_num);
status = device_add(&ctlr->dev);
- if (status < 0)
+ if (status < 0) {
+ /* free bus id */
+ mutex_lock(&board_lock);
+ idr_remove(&spi_master_idr, ctlr->bus_num);
+ mutex_unlock(&board_lock);
goto done;
- dev_dbg(dev, "registered %s %s%s\n",
+ }
+ dev_dbg(dev, "registered %s %s\n",
spi_controller_is_slave(ctlr) ? "slave" : "master",
- dev_name(&ctlr->dev), dynamic ? " (dynamic)" : "");
+ dev_name(&ctlr->dev));
/* If we're using a queued driver, start the queue */
if (ctlr->transfer)
@@ -2113,6 +2157,10 @@ int spi_register_controller(struct spi_controller *ctlr)
status = spi_controller_initialize_queue(ctlr);
if (status) {
device_del(&ctlr->dev);
+ /* free bus id */
+ mutex_lock(&board_lock);
+ idr_remove(&spi_master_idr, ctlr->bus_num);
+ mutex_unlock(&board_lock);
goto done;
}
}
@@ -2191,19 +2239,33 @@ static int __unregister(struct device *dev, void *null)
*/
void spi_unregister_controller(struct spi_controller *ctlr)
{
+ struct spi_controller *found;
int dummy;
+ /* First make sure that this controller was ever added */
+ mutex_lock(&board_lock);
+ found = idr_find(&spi_master_idr, ctlr->bus_num);
+ mutex_unlock(&board_lock);
+ if (found != ctlr) {
+ dev_dbg(&ctlr->dev,
+ "attempting to delete unregistered controller [%s]\n",
+ dev_name(&ctlr->dev));
+ return;
+ }
if (ctlr->queued) {
if (spi_destroy_queue(ctlr))
dev_err(&ctlr->dev, "queue remove failed\n");
}
-
mutex_lock(&board_lock);
list_del(&ctlr->list);
mutex_unlock(&board_lock);
dummy = device_for_each_child(&ctlr->dev, NULL, __unregister);
device_unregister(&ctlr->dev);
+ /* free bus id */
+ mutex_lock(&board_lock);
+ idr_remove(&spi_master_idr, ctlr->bus_num);
+ mutex_unlock(&board_lock);
}
EXPORT_SYMBOL_GPL(spi_unregister_controller);
@@ -3311,8 +3373,8 @@ static int of_spi_notify(struct notifier_block *nb, unsigned long action,
put_device(&ctlr->dev);
if (IS_ERR(spi)) {
- pr_err("%s: failed to create for '%s'\n",
- __func__, rd->dn->full_name);
+ pr_err("%s: failed to create for '%pOF'\n",
+ __func__, rd->dn);
of_node_clear_flag(rd->dn, OF_POPULATED);
return notifier_from_errno(PTR_ERR(spi));
}