Merge commit 'gcl/gcl-next'
This commit is contained in:
commit
d65d830ca0
9 changed files with 317 additions and 105 deletions
|
@ -13,3 +13,9 @@ config OF_I2C
|
|||
depends on PPC_OF && I2C
|
||||
help
|
||||
OpenFirmware I2C accessors
|
||||
|
||||
config OF_SPI
|
||||
def_tristate SPI
|
||||
depends on OF && PPC_OF && SPI
|
||||
help
|
||||
OpenFirmware SPI accessors
|
||||
|
|
|
@ -2,3 +2,4 @@ obj-y = base.o
|
|||
obj-$(CONFIG_OF_DEVICE) += device.o platform.o
|
||||
obj-$(CONFIG_OF_GPIO) += gpio.o
|
||||
obj-$(CONFIG_OF_I2C) += of_i2c.o
|
||||
obj-$(CONFIG_OF_SPI) += of_spi.o
|
||||
|
|
|
@ -385,3 +385,91 @@ struct device_node *of_find_matching_node(struct device_node *from,
|
|||
return np;
|
||||
}
|
||||
EXPORT_SYMBOL(of_find_matching_node);
|
||||
|
||||
/**
|
||||
* of_modalias_table: Table of explicit compatible ==> modalias mappings
|
||||
*
|
||||
* This table allows particulare compatible property values to be mapped
|
||||
* to modalias strings. This is useful for busses which do not directly
|
||||
* understand the OF device tree but are populated based on data contained
|
||||
* within the device tree. SPI and I2C are the two current users of this
|
||||
* table.
|
||||
*
|
||||
* In most cases, devices do not need to be listed in this table because
|
||||
* the modalias value can be derived directly from the compatible table.
|
||||
* However, if for any reason a value cannot be derived, then this table
|
||||
* provides a method to override the implicit derivation.
|
||||
*
|
||||
* At the moment, a single table is used for all bus types because it is
|
||||
* assumed that the data size is small and that the compatible values
|
||||
* should already be distinct enough to differentiate between SPI, I2C
|
||||
* and other devices.
|
||||
*/
|
||||
struct of_modalias_table {
|
||||
char *of_device;
|
||||
char *modalias;
|
||||
};
|
||||
static struct of_modalias_table of_modalias_table[] = {
|
||||
/* Empty for now; add entries as needed */
|
||||
};
|
||||
|
||||
/**
|
||||
* of_modalias_node - Lookup appropriate modalias for a device node
|
||||
* @node: pointer to a device tree node
|
||||
* @modalias: Pointer to buffer that modalias value will be copied into
|
||||
* @len: Length of modalias value
|
||||
*
|
||||
* Based on the value of the compatible property, this routine will determine
|
||||
* an appropriate modalias value for a particular device tree node. Three
|
||||
* separate methods are used to derive a modalias value.
|
||||
*
|
||||
* First method is to lookup the compatible value in of_modalias_table.
|
||||
* Second is to look for a "linux,<modalias>" entry in the compatible list
|
||||
* and used that for modalias. Third is to strip off the manufacturer
|
||||
* prefix from the first compatible entry and use the remainder as modalias
|
||||
*
|
||||
* This routine returns 0 on success
|
||||
*/
|
||||
int of_modalias_node(struct device_node *node, char *modalias, int len)
|
||||
{
|
||||
int i, cplen;
|
||||
const char *compatible;
|
||||
const char *p;
|
||||
|
||||
/* 1. search for exception list entry */
|
||||
for (i = 0; i < ARRAY_SIZE(of_modalias_table); i++) {
|
||||
compatible = of_modalias_table[i].of_device;
|
||||
if (!of_device_is_compatible(node, compatible))
|
||||
continue;
|
||||
strlcpy(modalias, of_modalias_table[i].modalias, len);
|
||||
return 0;
|
||||
}
|
||||
|
||||
compatible = of_get_property(node, "compatible", &cplen);
|
||||
if (!compatible)
|
||||
return -ENODEV;
|
||||
|
||||
/* 2. search for linux,<modalias> entry */
|
||||
p = compatible;
|
||||
while (cplen > 0) {
|
||||
if (!strncmp(p, "linux,", 6)) {
|
||||
p += 6;
|
||||
strlcpy(modalias, p, len);
|
||||
return 0;
|
||||
}
|
||||
|
||||
i = strlen(p) + 1;
|
||||
p += i;
|
||||
cplen -= i;
|
||||
}
|
||||
|
||||
/* 3. take first compatible entry and strip manufacturer */
|
||||
p = strchr(compatible, ',');
|
||||
if (!p)
|
||||
return -ENODEV;
|
||||
p++;
|
||||
strlcpy(modalias, p, len);
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(of_modalias_node);
|
||||
|
||||
|
|
|
@ -16,62 +16,6 @@
|
|||
#include <linux/of_i2c.h>
|
||||
#include <linux/module.h>
|
||||
|
||||
struct i2c_driver_device {
|
||||
char *of_device;
|
||||
char *i2c_type;
|
||||
};
|
||||
|
||||
static struct i2c_driver_device i2c_devices[] = {
|
||||
};
|
||||
|
||||
static int of_find_i2c_driver(struct device_node *node,
|
||||
struct i2c_board_info *info)
|
||||
{
|
||||
int i, cplen;
|
||||
const char *compatible;
|
||||
const char *p;
|
||||
|
||||
/* 1. search for exception list entry */
|
||||
for (i = 0; i < ARRAY_SIZE(i2c_devices); i++) {
|
||||
if (!of_device_is_compatible(node, i2c_devices[i].of_device))
|
||||
continue;
|
||||
if (strlcpy(info->type, i2c_devices[i].i2c_type,
|
||||
I2C_NAME_SIZE) >= I2C_NAME_SIZE)
|
||||
return -ENOMEM;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
compatible = of_get_property(node, "compatible", &cplen);
|
||||
if (!compatible)
|
||||
return -ENODEV;
|
||||
|
||||
/* 2. search for linux,<i2c-type> entry */
|
||||
p = compatible;
|
||||
while (cplen > 0) {
|
||||
if (!strncmp(p, "linux,", 6)) {
|
||||
p += 6;
|
||||
if (strlcpy(info->type, p,
|
||||
I2C_NAME_SIZE) >= I2C_NAME_SIZE)
|
||||
return -ENOMEM;
|
||||
return 0;
|
||||
}
|
||||
|
||||
i = strlen(p) + 1;
|
||||
p += i;
|
||||
cplen -= i;
|
||||
}
|
||||
|
||||
/* 3. take fist compatible entry and strip manufacturer */
|
||||
p = strchr(compatible, ',');
|
||||
if (!p)
|
||||
return -ENODEV;
|
||||
p++;
|
||||
if (strlcpy(info->type, p, I2C_NAME_SIZE) >= I2C_NAME_SIZE)
|
||||
return -ENOMEM;
|
||||
return 0;
|
||||
}
|
||||
|
||||
void of_register_i2c_devices(struct i2c_adapter *adap,
|
||||
struct device_node *adap_node)
|
||||
{
|
||||
|
@ -83,6 +27,9 @@ void of_register_i2c_devices(struct i2c_adapter *adap,
|
|||
const u32 *addr;
|
||||
int len;
|
||||
|
||||
if (of_modalias_node(node, info.type, sizeof(info.type)) < 0)
|
||||
continue;
|
||||
|
||||
addr = of_get_property(node, "reg", &len);
|
||||
if (!addr || len < sizeof(int) || *addr > (1 << 10) - 1) {
|
||||
printk(KERN_ERR
|
||||
|
@ -92,11 +39,6 @@ void of_register_i2c_devices(struct i2c_adapter *adap,
|
|||
|
||||
info.irq = irq_of_parse_and_map(node, 0);
|
||||
|
||||
if (of_find_i2c_driver(node, &info) < 0) {
|
||||
irq_dispose_mapping(info.irq);
|
||||
continue;
|
||||
}
|
||||
|
||||
info.addr = *addr;
|
||||
|
||||
request_module(info.type);
|
||||
|
|
93
drivers/of/of_spi.c
Normal file
93
drivers/of/of_spi.c
Normal file
|
@ -0,0 +1,93 @@
|
|||
/*
|
||||
* SPI OF support routines
|
||||
* Copyright (C) 2008 Secret Lab Technologies Ltd.
|
||||
*
|
||||
* Support routines for deriving SPI device attachments from the device
|
||||
* tree.
|
||||
*/
|
||||
|
||||
#include <linux/of.h>
|
||||
#include <linux/device.h>
|
||||
#include <linux/spi/spi.h>
|
||||
#include <linux/of_spi.h>
|
||||
|
||||
/**
|
||||
* of_register_spi_devices - Register child devices onto the SPI bus
|
||||
* @master: Pointer to spi_master device
|
||||
* @np: parent node of SPI device nodes
|
||||
*
|
||||
* Registers an spi_device for each child node of 'np' which has a 'reg'
|
||||
* property.
|
||||
*/
|
||||
void of_register_spi_devices(struct spi_master *master, struct device_node *np)
|
||||
{
|
||||
struct spi_device *spi;
|
||||
struct device_node *nc;
|
||||
const u32 *prop;
|
||||
int rc;
|
||||
int len;
|
||||
|
||||
for_each_child_of_node(np, nc) {
|
||||
/* 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);
|
||||
spi_dev_put(spi);
|
||||
continue;
|
||||
}
|
||||
|
||||
/* Select device driver */
|
||||
if (of_modalias_node(nc, spi->modalias,
|
||||
sizeof(spi->modalias)) < 0) {
|
||||
dev_err(&master->dev, "cannot find modalias for %s\n",
|
||||
nc->full_name);
|
||||
spi_dev_put(spi);
|
||||
continue;
|
||||
}
|
||||
|
||||
/* Device address */
|
||||
prop = of_get_property(nc, "reg", &len);
|
||||
if (!prop || len < sizeof(*prop)) {
|
||||
dev_err(&master->dev, "%s has no 'reg' property\n",
|
||||
nc->full_name);
|
||||
spi_dev_put(spi);
|
||||
continue;
|
||||
}
|
||||
spi->chip_select = *prop;
|
||||
|
||||
/* 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;
|
||||
|
||||
/* Device speed */
|
||||
prop = of_get_property(nc, "spi-max-frequency", &len);
|
||||
if (!prop || len < sizeof(*prop)) {
|
||||
dev_err(&master->dev, "%s has no 'spi-max-frequency' property\n",
|
||||
nc->full_name);
|
||||
spi_dev_put(spi);
|
||||
continue;
|
||||
}
|
||||
spi->max_speed_hz = *prop;
|
||||
|
||||
/* IRQ */
|
||||
spi->irq = irq_of_parse_and_map(nc, 0);
|
||||
|
||||
/* Store a pointer to the node in the device structure */
|
||||
of_node_get(nc);
|
||||
spi->dev.archdata.of_node = nc;
|
||||
|
||||
/* Register the new device */
|
||||
request_module(spi->modalias);
|
||||
rc = spi_add_device(spi);
|
||||
if (rc) {
|
||||
dev_err(&master->dev, "spi_device register error %s\n",
|
||||
nc->full_name);
|
||||
spi_dev_put(spi);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
EXPORT_SYMBOL(of_register_spi_devices);
|
|
@ -178,6 +178,96 @@ struct boardinfo {
|
|||
static LIST_HEAD(board_list);
|
||||
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
|
||||
|
@ -197,7 +287,6 @@ struct spi_device *spi_new_device(struct spi_master *master,
|
|||
struct spi_board_info *chip)
|
||||
{
|
||||
struct spi_device *proxy;
|
||||
struct device *dev = master->dev.parent;
|
||||
int status;
|
||||
|
||||
/* NOTE: caller did any chip->bus_num checks necessary.
|
||||
|
@ -207,66 +296,28 @@ struct spi_device *spi_new_device(struct spi_master *master,
|
|||
* suggests syslogged diagnostics are best here (ugh).
|
||||
*/
|
||||
|
||||
/* Chipselects are numbered 0..max; validate. */
|
||||
if (chip->chip_select >= master->num_chipselect) {
|
||||
dev_err(dev, "cs%d > max %d\n",
|
||||
chip->chip_select,
|
||||
master->num_chipselect);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (!spi_master_get(master))
|
||||
proxy = spi_alloc_device(master);
|
||||
if (!proxy)
|
||||
return NULL;
|
||||
|
||||
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->max_speed_hz = chip->max_speed_hz;
|
||||
proxy->mode = chip->mode;
|
||||
proxy->irq = chip->irq;
|
||||
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->controller_data = chip->controller_data;
|
||||
proxy->controller_state = NULL;
|
||||
proxy->dev.release = spidev_release;
|
||||
|
||||
/* drivers may modify this initial i/o setup */
|
||||
status = master->setup(proxy);
|
||||
status = spi_add_device(proxy);
|
||||
if (status < 0) {
|
||||
dev_err(dev, "can't %s %s, status %d\n",
|
||||
"setup", proxy->dev.bus_id, status);
|
||||
goto fail;
|
||||
spi_dev_put(proxy);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* 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;
|
||||
|
||||
fail:
|
||||
spi_master_put(master);
|
||||
kfree(proxy);
|
||||
return NULL;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(spi_new_device);
|
||||
|
||||
|
|
|
@ -70,5 +70,6 @@ extern int of_n_addr_cells(struct device_node *np);
|
|||
extern int of_n_size_cells(struct device_node *np);
|
||||
extern const struct of_device_id *of_match_node(
|
||||
const struct of_device_id *matches, const struct device_node *node);
|
||||
extern int of_modalias_node(struct device_node *node, char *modalias, int len);
|
||||
|
||||
#endif /* _LINUX_OF_H */
|
||||
|
|
18
include/linux/of_spi.h
Normal file
18
include/linux/of_spi.h
Normal file
|
@ -0,0 +1,18 @@
|
|||
/*
|
||||
* OpenFirmware SPI support routines
|
||||
* Copyright (C) 2008 Secret Lab Technologies Ltd.
|
||||
*
|
||||
* Support routines for deriving SPI device attachments from the device
|
||||
* tree.
|
||||
*/
|
||||
|
||||
#ifndef __LINUX_OF_SPI_H
|
||||
#define __LINUX_OF_SPI_H
|
||||
|
||||
#include <linux/of.h>
|
||||
#include <linux/spi/spi.h>
|
||||
|
||||
extern void of_register_spi_devices(struct spi_master *master,
|
||||
struct device_node *np);
|
||||
|
||||
#endif /* __LINUX_OF_SPI */
|
|
@ -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
|
||||
* spi_unregister_device() to start making that device vanish, but
|
||||
* 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 *
|
||||
spi_new_device(struct spi_master *, struct spi_board_info *);
|
||||
|
||||
|
|
Loading…
Reference in a new issue