]> www.pilppa.org Git - linux-2.6-omap-h63xx.git/commitdiff
Merge commit 'gcl/gcl-next'
authorBenjamin Herrenschmidt <benh@kernel.crashing.org>
Mon, 28 Jul 2008 06:30:40 +0000 (16:30 +1000)
committerBenjamin Herrenschmidt <benh@kernel.crashing.org>
Mon, 28 Jul 2008 06:30:40 +0000 (16:30 +1000)
drivers/of/Kconfig
drivers/of/Makefile
drivers/of/base.c
drivers/of/of_i2c.c
drivers/of/of_spi.c [new file with mode: 0644]
drivers/spi/spi.c
include/linux/of.h
include/linux/of_spi.h [new file with mode: 0644]
include/linux/spi/spi.h

index 1d7ec3129349b2cefeee64afd68fb92ae85a17e5..f821dbc952a42bb2e53a49c110decc859eff3d55 100644 (file)
@@ -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
index 548772e871fd8e52a1ed66156fc0c129e84660af..4c3c6f8e36f51577281f881f6edf464834847111 100644 (file)
@@ -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
index 23ffb7c0caf24148277ddd19c7430b5c90fb6fcb..ad8ac1a8af28689acc7198b22fe95a888d62e6a6 100644 (file)
@@ -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);
+
index 344e1b03dd8b708661fcb08982d27ebd79bd586b..6a98dc8aa30b1af9a1e8ce9a2a8264073a631eee 100644 (file)
 #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);
diff --git a/drivers/of/of_spi.c b/drivers/of/of_spi.c
new file mode 100644 (file)
index 0000000..b01eec0
--- /dev/null
@@ -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);
index ecca4a6a6f94a8b02e9d8143ae2be88d11bd00b5..964124b60db2560b12f15d08dbdeeff43a058e16 100644 (file)
@@ -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);
 
index 59a61bdc98b69b58da4f6f9797c131a275c690d1..79886ade070f2e791274c92814bc04bfbcff4947 100644 (file)
@@ -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 */
diff --git a/include/linux/of_spi.h b/include/linux/of_spi.h
new file mode 100644 (file)
index 0000000..5f71ee8
--- /dev/null
@@ -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 */
index a9cc29d466535f64b7f3cccdcc25c5f43cd96a40..4be01bb4437748c27f80aa79733c3a1360ca13e0 100644 (file)
@@ -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 *);