summaryrefslogtreecommitdiffstats
path: root/drivers/spi/spi.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/spi/spi.c')
-rw-r--r--drivers/spi/spi.c129
1 files changed, 92 insertions, 37 deletions
diff --git a/drivers/spi/spi.c b/drivers/spi/spi.c
index d9311d4af5..c627d88954 100644
--- a/drivers/spi/spi.c
+++ b/drivers/spi/spi.c
@@ -1,21 +1,10 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
* Copyright (C) 2008 Sascha Hauer, Pengutronix
*
* Derived from Linux SPI Framework
*
* Copyright (C) 2005 David Brownell
- *
- * 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 <common.h>
@@ -23,6 +12,7 @@
#include <spi/spi.h>
#include <xfuncs.h>
#include <malloc.h>
+#include <slice.h>
#include <errno.h>
#include <init.h>
#include <of.h>
@@ -40,6 +30,7 @@ struct boardinfo {
};
static LIST_HEAD(board_list);
+static LIST_HEAD(spi_controller_list);
/**
* spi_new_device - instantiate one new SPI device
@@ -82,7 +73,7 @@ struct spi_device *spi_new_device(struct spi_controller *ctrl,
/* allocate a free id for this chip */
proxy->dev.id = DEVICE_ID_DYNAMIC;
proxy->dev.type_data = proxy;
- proxy->dev.device_node = chip->device_node;
+ proxy->dev.of_node = chip->device_node;
proxy->dev.parent = ctrl->dev;
proxy->master = proxy->controller = ctrl;
@@ -103,7 +94,11 @@ struct spi_device *spi_new_device(struct spi_controller *ctrl,
goto fail;
}
- register_device(&proxy->dev);
+ status = register_device(&proxy->dev);
+ if (status)
+ goto fail;
+
+ chip->device_node->dev = &proxy->dev;
return proxy;
fail:
@@ -115,9 +110,8 @@ EXPORT_SYMBOL(spi_new_device);
static void spi_of_register_slaves(struct spi_controller *ctrl)
{
struct device_node *n;
- struct spi_board_info chip;
struct property *reg;
- struct device_node *node = ctrl->dev->device_node;
+ struct device_node *node = ctrl->dev->of_node;
if (!IS_ENABLED(CONFIG_OFDEVICE))
return;
@@ -126,7 +120,14 @@ static void spi_of_register_slaves(struct spi_controller *ctrl)
return;
for_each_available_child_of_node(node, n) {
- memset(&chip, 0, sizeof(chip));
+ struct spi_board_info chip = {};
+
+ if (n->dev) {
+ dev_dbg(ctrl->dev, "skipping already registered %s\n",
+ dev_name(n->dev));
+ continue;
+ }
+
chip.name = xstrdup(n->name);
chip.bus_num = ctrl->bus_num;
/* Mode (clock phase/polarity/etc.) */
@@ -145,7 +146,18 @@ static void spi_of_register_slaves(struct spi_controller *ctrl)
continue;
chip.chip_select = of_read_number(reg->value, 1);
chip.device_node = n;
- spi_register_board_info(&chip, 1);
+ spi_new_device(ctrl, &chip);
+ }
+}
+
+static void spi_controller_rescan(struct device *dev)
+{
+ struct spi_controller *ctrl;
+
+ list_for_each_entry(ctrl, &spi_controller_list, list) {
+ if (ctrl->dev != dev)
+ continue;
+ spi_of_register_slaves(ctrl);
}
}
@@ -203,8 +215,6 @@ static void scan_boardinfo(struct spi_controller *ctrl)
}
}
-static LIST_HEAD(spi_controller_list);
-
static int spi_controller_check_ops(struct spi_controller *ctlr)
{
/*
@@ -260,14 +270,16 @@ int spi_register_controller(struct spi_controller *ctrl)
if (status)
return status;
+ slice_init(&ctrl->slice, dev_name(ctrl->dev));
+
/* even if it's just one always-selected device, there must
* be at least one chipselect
*/
if (ctrl->num_chipselect == 0)
return -EINVAL;
- if ((ctrl->bus_num < 0) && ctrl->dev->device_node)
- ctrl->bus_num = of_alias_get_id(ctrl->dev->device_node, "spi");
+ if ((ctrl->bus_num < 0) && ctrl->dev->of_node)
+ ctrl->bus_num = of_alias_get_id(ctrl->dev->of_node, "spi");
/* convention: dynamically assigned bus IDs count down from the max */
if (ctrl->bus_num < 0)
@@ -281,9 +293,12 @@ int spi_register_controller(struct spi_controller *ctrl)
scan_boardinfo(ctrl);
status = 0;
+ if (!ctrl->dev->rescan)
+ ctrl->dev->rescan = spi_controller_rescan;
+
return status;
}
-EXPORT_SYMBOL(spi_register_ctrl);
+EXPORT_SYMBOL(spi_register_controller);
struct spi_controller *spi_get_controller(int bus)
{
@@ -297,9 +312,62 @@ struct spi_controller *spi_get_controller(int bus)
return NULL;
}
+static int __spi_validate(struct spi_device *spi, struct spi_message *message)
+{
+ struct spi_controller *ctlr = spi->controller;
+ struct spi_transfer *xfer;
+ int w_size;
+
+ if (list_empty(&message->transfers))
+ return -EINVAL;
+
+ list_for_each_entry(xfer, &message->transfers, transfer_list) {
+ 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 (ctlr->max_speed_hz && xfer->speed_hz > ctlr->max_speed_hz)
+ xfer->speed_hz = ctlr->max_speed_hz;
+
+ /*
+ * 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;
+ }
+
+ message->status = -EINPROGRESS;
+
+ return 0;
+}
+
int spi_sync(struct spi_device *spi, struct spi_message *message)
{
- return spi->controller->transfer(spi, message);
+ int status;
+ int ret;
+
+ status = __spi_validate(spi, message);
+ if (status != 0)
+ return status;
+
+ slice_acquire(&spi->controller->slice);
+
+ ret = spi->controller->transfer(spi, message);
+
+ slice_release(&spi->controller->slice);
+
+ return ret;
}
/**
@@ -344,22 +412,9 @@ int spi_write_then_read(struct spi_device *spi,
}
EXPORT_SYMBOL(spi_write_then_read);
-static int spi_probe(struct device_d *dev)
-{
- return dev->driver->probe(dev);
-}
-
-static void spi_remove(struct device_d *dev)
-{
- if (dev->driver->remove)
- dev->driver->remove(dev);
-}
-
struct bus_type spi_bus = {
.name = "spi",
.match = device_match_of_modalias,
- .probe = spi_probe,
- .remove = spi_remove,
};
static int spi_bus_init(void)