Commit dc87c98e authored by Grant Likely's avatar Grant Likely

spi: split up spi_new_device() to allow two stage registration.

spi_new_device() allocates and registers an spi device all in one swoop.
If the driver needs to add extra data to the spi_device before it is
registered, then this causes problems.  This is needed for OF device
tree support so that the SPI device tree helper can add a pointer to
the device node after the device is allocated, but before the device
is registered.  OF aware SPI devices can then retrieve data out of the
device node to populate a platform data structure.

This patch splits the allocation and registration portions of code out
of spi_new_device() and creates two new functions; spi_alloc_device()
and spi_register_device().  spi_new_device() is modified to use the new
functions for allocation and registration.  None of the existing users
of spi_new_device() should be affected by this change.

Drivers using the new API can forego the use of spi_board_info
structure to describe the device layout and populate data into the
spi_device structure directly.

This change is in preparation for adding an OF device tree parser to
generate spi_devices based on data in the device tree.
Signed-off-by: default avatarGrant Likely <grant.likely@secretlab.ca>
Acked-by: default avatarDavid Brownell <dbrownell@users.sourceforge.net>
parent 3f07af49
...@@ -178,6 +178,96 @@ struct boardinfo { ...@@ -178,6 +178,96 @@ struct boardinfo {
static LIST_HEAD(board_list); static LIST_HEAD(board_list);
static DEFINE_MUTEX(board_lock); 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.
*
* Returns a pointer to the new device, or NULL.
*/
struct spi_device *spi_alloc_device(struct spi_master *master)
{
struct spi_device *spi;
struct device *dev = master->dev.parent;
if (!spi_master_get(master))
return NULL;
spi = kzalloc(sizeof *spi, GFP_KERNEL);
if (!spi) {
dev_err(dev, "cannot alloc spi_device\n");
spi_master_put(master);
return NULL;
}
spi->master = master;
spi->dev.parent = dev;
spi->dev.bus = &spi_bus_type;
spi->dev.release = spidev_release;
device_initialize(&spi->dev);
return spi;
}
EXPORT_SYMBOL_GPL(spi_alloc_device);
/**
* 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.
*
* Returns 0 on success; non-zero on failure
*/
int spi_add_device(struct spi_device *spi)
{
struct device *dev = spi->master->dev.parent;
int status;
/* Chipselects are numbered 0..max; validate. */
if (spi->chip_select >= spi->master->num_chipselect) {
dev_err(dev, "cs%d >= max %d\n",
spi->chip_select,
spi->master->num_chipselect);
return -EINVAL;
}
/* Set the bus ID string */
snprintf(spi->dev.bus_id, sizeof spi->dev.bus_id,
"%s.%u", spi->master->dev.bus_id,
spi->chip_select);
/* drivers may modify this initial i/o setup */
status = spi->master->setup(spi);
if (status < 0) {
dev_err(dev, "can't %s %s, status %d\n",
"setup", spi->dev.bus_id, status);
return status;
}
/* driver core catches callers that misbehave by defining
* devices that already exist.
*/
status = device_add(&spi->dev);
if (status < 0) {
dev_err(dev, "can't %s %s, status %d\n",
"add", spi->dev.bus_id, status);
return status;
}
dev_dbg(dev, "registered child %s\n", spi->dev.bus_id);
return 0;
}
EXPORT_SYMBOL_GPL(spi_add_device);
/** /**
* spi_new_device - instantiate one new SPI device * spi_new_device - instantiate one new SPI device
...@@ -197,7 +287,6 @@ struct spi_device *spi_new_device(struct spi_master *master, ...@@ -197,7 +287,6 @@ struct spi_device *spi_new_device(struct spi_master *master,
struct spi_board_info *chip) struct spi_board_info *chip)
{ {
struct spi_device *proxy; struct spi_device *proxy;
struct device *dev = master->dev.parent;
int status; int status;
/* NOTE: caller did any chip->bus_num checks necessary. /* NOTE: caller did any chip->bus_num checks necessary.
...@@ -207,66 +296,28 @@ struct spi_device *spi_new_device(struct spi_master *master, ...@@ -207,66 +296,28 @@ struct spi_device *spi_new_device(struct spi_master *master,
* suggests syslogged diagnostics are best here (ugh). * suggests syslogged diagnostics are best here (ugh).
*/ */
/* Chipselects are numbered 0..max; validate. */ proxy = spi_alloc_device(master);
if (chip->chip_select >= master->num_chipselect) { if (!proxy)
dev_err(dev, "cs%d > max %d\n",
chip->chip_select,
master->num_chipselect);
return NULL;
}
if (!spi_master_get(master))
return NULL; return NULL;
WARN_ON(strlen(chip->modalias) >= sizeof(proxy->modalias)); WARN_ON(strlen(chip->modalias) >= sizeof(proxy->modalias));
proxy = kzalloc(sizeof *proxy, GFP_KERNEL);
if (!proxy) {
dev_err(dev, "can't alloc dev for cs%d\n",
chip->chip_select);
goto fail;
}
proxy->master = master;
proxy->chip_select = chip->chip_select; proxy->chip_select = chip->chip_select;
proxy->max_speed_hz = chip->max_speed_hz; proxy->max_speed_hz = chip->max_speed_hz;
proxy->mode = chip->mode; proxy->mode = chip->mode;
proxy->irq = chip->irq; proxy->irq = chip->irq;
strlcpy(proxy->modalias, chip->modalias, sizeof(proxy->modalias)); strlcpy(proxy->modalias, chip->modalias, sizeof(proxy->modalias));
snprintf(proxy->dev.bus_id, sizeof proxy->dev.bus_id,
"%s.%u", master->dev.bus_id,
chip->chip_select);
proxy->dev.parent = dev;
proxy->dev.bus = &spi_bus_type;
proxy->dev.platform_data = (void *) chip->platform_data; proxy->dev.platform_data = (void *) chip->platform_data;
proxy->controller_data = chip->controller_data; proxy->controller_data = chip->controller_data;
proxy->controller_state = NULL; proxy->controller_state = NULL;
proxy->dev.release = spidev_release;
/* drivers may modify this initial i/o setup */ status = spi_add_device(proxy);
status = master->setup(proxy);
if (status < 0) { if (status < 0) {
dev_err(dev, "can't %s %s, status %d\n", spi_dev_put(proxy);
"setup", proxy->dev.bus_id, status); return NULL;
goto fail;
} }
/* driver core catches callers that misbehave by defining
* devices that already exist.
*/
status = device_register(&proxy->dev);
if (status < 0) {
dev_err(dev, "can't %s %s, status %d\n",
"add", proxy->dev.bus_id, status);
goto fail;
}
dev_dbg(dev, "registered child %s\n", proxy->dev.bus_id);
return proxy; return proxy;
fail:
spi_master_put(master);
kfree(proxy);
return NULL;
} }
EXPORT_SYMBOL_GPL(spi_new_device); EXPORT_SYMBOL_GPL(spi_new_device);
......
...@@ -778,7 +778,19 @@ spi_register_board_info(struct spi_board_info const *info, unsigned n) ...@@ -778,7 +778,19 @@ spi_register_board_info(struct spi_board_info const *info, unsigned n)
* use spi_new_device() to describe each device. You can also call * use spi_new_device() to describe each device. You can also call
* spi_unregister_device() to start making that device vanish, but * spi_unregister_device() to start making that device vanish, but
* normally that would be handled by spi_unregister_master(). * normally that would be handled by spi_unregister_master().
*
* You can also use spi_alloc_device() and spi_add_device() to use a two
* stage registration sequence for each spi_device. This gives the caller
* some more control over the spi_device structure before it is registered,
* but requires that caller to initialize fields that would otherwise
* be defined using the board info.
*/ */
extern struct spi_device *
spi_alloc_device(struct spi_master *master);
extern int
spi_add_device(struct spi_device *spi);
extern struct spi_device * extern struct spi_device *
spi_new_device(struct spi_master *, struct spi_board_info *); spi_new_device(struct spi_master *, struct spi_board_info *);
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment