diff options
author | Sascha Hauer <s.hauer@pengutronix.de> | 2008-03-14 12:59:55 +0100 |
---|---|---|
committer | Sascha Hauer <s.hauer@pengutronix.de> | 2008-03-14 12:59:55 +0100 |
commit | a14a5c02f0063e0cf7aae85aace499a080b790c1 (patch) | |
tree | 8fdd107ec20cc9093c12d2baa85b054b14ff1d01 /drivers/spi/spi.c | |
parent | 906eea397a2f4378c4cab841fb832211e0414ce9 (diff) | |
download | barebox-a14a5c02f0063e0cf7aae85aace499a080b790c1.tar.gz barebox-a14a5c02f0063e0cf7aae85aace499a080b790c1.tar.xz |
first (partly) running spi support
Diffstat (limited to 'drivers/spi/spi.c')
-rw-r--r-- | drivers/spi/spi.c | 191 |
1 files changed, 187 insertions, 4 deletions
diff --git a/drivers/spi/spi.c b/drivers/spi/spi.c index 765e34d5e2..cba12552b6 100644 --- a/drivers/spi/spi.c +++ b/drivers/spi/spi.c @@ -1,14 +1,197 @@ +/* + * 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. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, + * MA 02111-1307 USA + * + */ + #include <common.h> #include <spi/spi.h> +#include <xfuncs.h> +#include <malloc.h> +#include <errno.h> + +/* SPI devices should normally not be created by SPI device drivers; that + * would make them board-specific. Similarly with SPI master drivers. + * Device registration normally goes into like arch/.../mach.../board-YYY.c + * with other readonly (flashable) information about mainboard devices. + */ + +struct boardinfo { + struct list_head list; + unsigned n_board_info; + struct spi_board_info board_info[0]; +}; + +static LIST_HEAD(board_list); -int spi_register_boardinfo(struct spi_board_info *info, int num) +/** + * spi_new_device - instantiate one new SPI device + * @master: Controller to which device is connected + * @chip: Describes the SPI device + * Context: can sleep + * + * On typical mainboards, this is purely internal; and it's not needed + * after board init creates the hard-wired devices. Some development + * platforms may not be able to use spi_register_board_info though, and + * this is exported so that for example a USB or parport based adapter + * driver could add devices (which it would learn about out-of-band). + * + * Returns the new device, or NULL. + */ +struct spi_device *spi_new_device(struct spi_master *master, + struct spi_board_info *chip) { - printf("%s\n", __FUNCTION__); + struct spi_device *proxy; + int status; + + /* Chipselects are numbered 0..max; validate. */ + if (chip->chip_select >= master->num_chipselect) { + debug("cs%d > max %d\n", + chip->chip_select, + master->num_chipselect); + return NULL; + } + + proxy = xzalloc(sizeof *proxy); + proxy->master = master; + proxy->chip_select = chip->chip_select; + proxy->max_speed_hz = chip->max_speed_hz; + proxy->mode = chip->mode; + strcpy(proxy->dev.name, chip->name); + strcpy(proxy->dev.id, "pmic"); + proxy->dev.type_data = proxy; + status = register_device(&proxy->dev); + + /* drivers may modify this initial i/o setup */ + status = master->setup(proxy); + if (status < 0) { + printf("can't %s %s, status %d\n", + "setup", proxy->dev.id, status); + goto fail; + } + + return proxy; + +fail: + free(proxy); + return NULL; +} +EXPORT_SYMBOL(spi_new_device); + +/** + * spi_register_board_info - register SPI devices for a given board + * @info: array of chip descriptors + * @n: how many descriptors are provided + * Context: can sleep + * + * Board-specific early init code calls this (probably during arch_initcall) + * with segments of the SPI device table. Any device nodes are created later, + * after the relevant parent SPI controller (bus_num) is defined. We keep + * this table of devices forever, so that reloading a controller driver will + * not make Linux forget about these hard-wired devices. + * + * Other code can also call this, e.g. a particular add-on board might provide + * SPI devices through its expansion connector, so code initializing that board + * would naturally declare its SPI devices. + * + * The board info passed can safely be __initdata ... but be careful of + * any embedded pointers (platform_data, etc), they're copied as-is. + */ +int +spi_register_board_info(struct spi_board_info const *info, int n) +{ + struct boardinfo *bi; + + bi = xmalloc(sizeof(*bi) + n * sizeof *info); + + bi->n_board_info = n; + memcpy(bi->board_info, info, n * sizeof *info); + + list_add_tail(&bi->list, &board_list); + return 0; } +static void scan_boardinfo(struct spi_master *master) +{ + struct boardinfo *bi; + + list_for_each_entry(bi, &board_list, list) { + struct spi_board_info *chip = bi->board_info; + unsigned n; + + for (n = bi->n_board_info; n > 0; n--, chip++) { + debug("%s %d %d\n", __FUNCTION__, chip->bus_num, master->bus_num); + if (chip->bus_num != master->bus_num) + continue; + /* NOTE: this relies on spi_new_device to + * issue diagnostics when given bogus inputs + */ + (void) spi_new_device(master, chip); + } + } +} + +/** + * spi_register_master - register SPI master controller + * @master: initialized master, originally from spi_alloc_master() + * Context: can sleep + * + * SPI master controllers connect to their drivers using some non-SPI bus, + * such as the platform bus. The final stage of probe() in that code + * includes calling spi_register_master() to hook up to this SPI bus glue. + * + * SPI controllers use board specific (often SOC specific) bus numbers, + * and board-specific addressing for SPI devices combines those numbers + * with chip select numbers. Since SPI does not directly support dynamic + * device identification, boards need configuration tables telling which + * chip is at which address. + * + * This must be called from context that can sleep. It returns zero on + * success, else a negative error code (dropping the master's refcount). + * After a successful return, the caller is responsible for calling + * spi_unregister_master(). + */ int spi_register_master(struct spi_master *master) { - printf("%s\n", __FUNCTION__); - return 0; + int status = -ENODEV; + + debug("%s: %s:%s\n", __FUNCTION__, master->dev->name, master->dev->id); + + /* even if it's just one always-selected device, there must + * be at least one chipselect + */ + if (master->num_chipselect == 0) + return -EINVAL; + + /* populate children from any spi device tables */ + scan_boardinfo(master); + status = 0; + + return status; } +EXPORT_SYMBOL(spi_register_master); + +int spi_sync(struct spi_device *spi, struct spi_message *message) +{ + return spi->master->transfer(spi, message); +} + |