diff options
Diffstat (limited to 'drivers/spi/spi.c')
| -rw-r--r-- | drivers/spi/spi.c | 98 | 
1 files changed, 95 insertions, 3 deletions
| diff --git a/drivers/spi/spi.c b/drivers/spi/spi.c index 3d8f662e4fe9..1041cb83d67a 100644 --- a/drivers/spi/spi.c +++ b/drivers/spi/spi.c @@ -2,6 +2,7 @@   * SPI init/core code   *   * Copyright (C) 2005 David Brownell + * Copyright (C) 2008 Secret Lab Technologies Ltd.   *   * 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 @@ -19,15 +20,16 @@   */  #include <linux/kernel.h> +#include <linux/kmod.h>  #include <linux/device.h>  #include <linux/init.h>  #include <linux/cache.h>  #include <linux/mutex.h>  #include <linux/of_device.h> +#include <linux/of_irq.h>  #include <linux/slab.h>  #include <linux/mod_devicetable.h>  #include <linux/spi/spi.h> -#include <linux/of_spi.h>  #include <linux/pm_runtime.h>  #include <linux/export.h>  #include <linux/sched.h> @@ -530,7 +532,7 @@ static void spi_pump_messages(struct kthread_work *work)  	/* Lock queue and check for queue work */  	spin_lock_irqsave(&master->queue_lock, flags);  	if (list_empty(&master->queue) || !master->running) { -		if (master->busy) { +		if (master->busy && master->unprepare_transfer_hardware) {  			ret = master->unprepare_transfer_hardware(master);  			if (ret) {  				spin_unlock_irqrestore(&master->queue_lock, flags); @@ -560,7 +562,7 @@ static void spi_pump_messages(struct kthread_work *work)  		master->busy = true;  	spin_unlock_irqrestore(&master->queue_lock, flags); -	if (!was_busy) { +	if (!was_busy && master->prepare_transfer_hardware) {  		ret = master->prepare_transfer_hardware(master);  		if (ret) {  			dev_err(&master->dev, @@ -798,6 +800,94 @@ err_init_queue:  /*-------------------------------------------------------------------------*/ +#if defined(CONFIG_OF) && !defined(CONFIG_SPARC) +/** + * of_register_spi_devices() - Register child devices onto the SPI bus + * @master:	Pointer to spi_master device + * + * Registers an spi_device for each child node of master node which has a 'reg' + * property. + */ +static void of_register_spi_devices(struct spi_master *master) +{ +	struct spi_device *spi; +	struct device_node *nc; +	const __be32 *prop; +	int rc; +	int len; + +	if (!master->dev.of_node) +		return; + +	for_each_child_of_node(master->dev.of_node, 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 = be32_to_cpup(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; +		if (of_find_property(nc, "spi-cs-high", NULL)) +			spi->mode |= SPI_CS_HIGH; + +		/* 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 = be32_to_cpup(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.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); +		} + +	} +} +#else +static void of_register_spi_devices(struct spi_master *master) { } +#endif +  static void spi_master_release(struct device *dev)  {  	struct spi_master *master; @@ -846,6 +936,8 @@ struct spi_master *spi_alloc_master(struct device *dev, unsigned size)  		return NULL;  	device_initialize(&master->dev); +	master->bus_num = -1; +	master->num_chipselect = 1;  	master->dev.class = &spi_master_class;  	master->dev.parent = get_device(dev);  	spi_master_set_devdata(master, &master[1]); | 
