[PATCH v5 0/2] spi: driver for Cirrus EP93xx SPI controller
H Hartley Sweeten
hartleys at visionengravers.com
Wed Apr 28 18:30:25 EDT 2010
On Wednesday, April 28, 2010 10:51 AM, Mika Westerberg wrote:
>
> Hello,
>
> This series implements SPI master driver for Cirrus Logic EP93xx SPI
> controllers.
>
> This is fifth iteration of the driver.
>
> Changes to the previous version:
> - addressed review comments
> - added priming the TX FIFO when transfer is started
> - in case of ROR interrupt we clear it
> - added documentation in Documentation/spi/ep93xx_spi which provides
> sample code how the driver can be hooked into platform data
>
> I tested this on TS-7260 board with at25 and mmc_spi drivers. Now that I have
> Sim.One board, which is EP9307 based, I also tested with that (I hacked the
> board a bit to get EGPIO9 as a chip select).
>
> Note that patch 2/2 depends on patch that is already in Russell's patch
> tracking system:
> http://www.arm.linux.org.uk/developer/patches/viewpatch.php?id=5998/1
>
Mika,
Following is a patch to allow using built-in gpios, external gpio expanders
(spi/i2c/etc.) or really any other mechanism for the chip select.
Using your example in Documentation/spi/ep93xx_spi as a starting point, the
modified setup code would look like this:
+...
+#include <linux/gpio.h>
+#include <linux/spi/spi.h>
+
+#include <mach/ep93xx_spi.h>
+
+/* this is our GPIO line used for chip select */
+#define MMC_CHIP_SELECT_GPIO EP93XX_GPIO_LINE_EGPIO9
+
+static int ts72xx_mmc_spi_setup(struct spi_device *spi)
+{
+ int err;
+
+ err = gpio_request(MMC_CHIP_SELECT_GPIO, spi->modalias);
+ if (err)
+ return err;
+
+ gpio_direction_output(MMC_CHIP_SELECT_GPIO, 1);
+
+ return 0;
+}
+
+static void ts72xx_mmc_spi_cleanup(struct spi_device *spi)
+{
+ gpio_set_value(MMC_CHIP_SELECT_GPIO, 1);
+ gpio_direction_input(MMC_CHIP_SELECT_GPIO);
+ gpio_free(MMC_CHIP_SELECT_GPIO);
+}
+
+static void ts72xx_mmc_spi_cs_control(struct spi_device *spi, int value)
+{
+ gpio_set_value(MMC_CHIP_SELECT_GPIO, value);
+}
+
+static struct ep93xx_spi_chip_ops ts72xx_mmc_spi_ops = {
+ .setup = ts72xx_mmc_spi_setup,
+ .cleanup = ts72xx_mmc_spi_cleanup,
+ .cs_control = ts72xx_mmc_spi_cs_control,
+};
+
+static struct spi_board_info ts72xx_spi_devices[] __initconst = {
+ {
+ .modalias = "mmc_spi",
+ .controller_data = &ts72xx_mmc_spi_ops,
+ /*
+ * We use 10 MHz even though the maximum is 7.4 MHz. The driver
+ * will limit it automatically to max. frequency.
+ */
+ .max_speed_hz = 10 * 1000 * 1000,
+ .bus_num = 0,
+ .chip_select = 0,
+ .mode = SPI_MODE_0,
+ },
+};
+
+static struct ep93xx_spi_info ts72xx_spi_info = {
+ .num_chipselect = ARRAY_SIZE(ts72xx_spi_devices),
+};
+
+static void __init ts72xx_init_machine(void)
+{
+ ...
+ ep93xx_register_spi(&ts72xx_spi_info, ts72xx_spi_devices,
+ ARRAY_SIZE(ts72xx_spi_devices));
+}
Every spi device in struct spi_board_info would have it's own private
struct ep93xx_spi_chip_ops which is passed in the controller_data
field.
Note, this isn't a git produced patch. I currently don't have my development
tree under git control...
---
Expand the chip select options for the ep93xx_spi driver to allow using
spi/i2c/etc. gpio expanders to provide the chip selects.
This introduces a per-chip structure to provide setup/cleanup callbacks
for the chip select mechanism and a cs_control callback that is used to
actually select/deselect the device.
Signed-off-by: H Hartley Sweeten <hsweeten at visionengravers.com>
---
--- arch/arm/mach-ep93xx/include/mach/ep93xx_spi.h.orig 2010-04-28 14:36:14.000000000 -0700
+++ arch/arm/mach-ep93xx/include/mach/ep93xx_spi.h 2010-04-28 15:27:39.000000000 -0700
@@ -7,25 +7,20 @@ struct spi_device;
* struct ep93xx_spi_info - EP93xx specific SPI descriptor
* @num_chipselect: number of chip selects on this board, must be
* at least one
- * @cs_control: chip select control function. Can be %NULL if not needed.
- *
- * This structure is passed from board support files to EP93xx SPI controller
- * driver. It provides callback hook to control chip select lines that are
- * allocated in board support files during the board initialization.
*/
struct ep93xx_spi_info {
int num_chipselect;
- /*
- * cs_control() - control board chipselect GPIO lines
- * @spi: SPI device whose chipselect is controlled
- * @value: value to set the chip select line to
- *
- * This function is used to control board specific chip select lines.
- * @value is either %0 or %1.
- *
- * This function is called from thread context and can sleep if
- * necessary.
- */
+};
+
+/**
+ * struct ep93xx_spi_chip_ops - operation callbacks for SPI slave device
+ * @setup: setup the chip select mechanism
+ * @cleanup: cleanup the chip select mechanism
+ * @cs_control: control the device chip select
+ */
+struct ep93xx_spi_chip_ops {
+ int (*setup)(struct spi_device *spi);
+ void (*cleanup)(struct spi_device *spi);
void (*cs_control)(struct spi_device *spi, int value);
};
--- drivers/spi/ep93xx_spi.c.orig 2010-04-28 13:06:07.000000000 -0700
+++ drivers/spi/ep93xx_spi.c 2010-04-28 15:24:47.000000000 -0700
@@ -84,7 +84,6 @@
* @rx: current byte in transfer to receive
* @fifo_level: how full is FIFO (%0..%SPI_FIFO_SIZE - %1). Receiving one
* frame decreases this level and sending one frame increases it.
- * @cs_control: chip select control function
*
* This structure holds EP93xx SPI controller specific information. When
* @running is %true, driver accepts transfer requests from protocol drivers.
@@ -113,7 +112,6 @@ struct ep93xx_spi {
size_t tx;
size_t rx;
size_t fifo_level;
- void (*cs_control)(struct spi_device *, int);
};
/**
@@ -123,6 +121,7 @@ struct ep93xx_spi {
* @div_cpsr: cpsr (pre-scaler) divider
* @div_scr: scr divider
* @dss: bits per word (4 - 16 bits)
+ * @ops: private chip operations
*
* This structure is used to store hardware register specific settings for each
* SPI device. Settings are written to hardware by function
@@ -134,6 +133,7 @@ struct ep93xx_spi_chip {
u8 div_cpsr;
u8 div_scr;
u8 dss;
+ struct ep93xx_spi_chip_ops *ops;
};
/* converts bits per word to CR0.DSS value */
@@ -283,7 +283,6 @@ static int ep93xx_spi_calc_divisors(cons
/**
* ep93xx_spi_cs_control() - controls chipselect for given device
- * @espi: ep93xx SPI controller struct
* @spi: SPI device to select/deselect
* @control: select (%true) / deselect (%false)
*
@@ -291,14 +290,13 @@ static int ep93xx_spi_calc_divisors(cons
*
* Note that this function is called from a thread context and can sleep.
*/
-static inline void ep93xx_spi_cs_control(const struct ep93xx_spi *espi,
- struct spi_device *spi,
- bool control)
+static void ep93xx_spi_cs_control(struct spi_device *spi, bool control)
{
+ struct ep93xx_spi_chip *chip = spi_get_ctldata(spi);
int value = (spi->mode & SPI_CS_HIGH) ? control : !control;
- if (espi->cs_control)
- espi->cs_control(spi, value);
+ if (chip->ops && chip->ops->cs_control)
+ chip->ops->cs_control(spi, value);
}
/**
@@ -323,12 +321,25 @@ static int ep93xx_spi_setup(struct spi_d
chip = spi_get_ctldata(spi);
if (!chip) {
+ dev_dbg(&espi->pdev->dev, "initial setup for %s\n",
+ spi->modalias);
+
chip = kzalloc(sizeof(*chip), GFP_KERNEL);
if (!chip)
return -ENOMEM;
- spi_set_ctldata(spi, chip);
chip->spi = spi;
+ chip->ops = spi->controller_data;
+
+ if (chip->ops && chip->ops->setup) {
+ ret = chip->ops->setup(spi);
+ if (ret) {
+ kfree(chip);
+ return ret;
+ }
+ }
+
+ spi_set_ctldata(spi, chip);
}
if (spi->max_speed_hz != chip->rate) {
@@ -345,7 +356,7 @@ static int ep93xx_spi_setup(struct spi_d
chip->dss = bits_per_word_to_dss(spi->bits_per_word);
- ep93xx_spi_cs_control(espi, spi, false);
+ ep93xx_spi_cs_control(spi, false);
return 0;
}
@@ -414,6 +425,8 @@ static void ep93xx_spi_cleanup(struct sp
chip = spi_get_ctldata(spi);
if (chip) {
+ if (chip->ops && chip->ops->cleanup)
+ chip->ops->cleanup(spi);
spi_set_ctldata(spi, NULL);
kfree(chip);
}
@@ -610,9 +623,9 @@ static void ep93xx_spi_process_transfer(
* chipselect briefly, we let the scheduler to handle
* any "delay" here.
*/
- ep93xx_spi_cs_control(espi, msg->spi, false);
+ ep93xx_spi_cs_control(msg->spi, false);
cond_resched();
- ep93xx_spi_cs_control(espi, msg->spi, true);
+ ep93xx_spi_cs_control(msg->spi, true);
}
}
@@ -674,7 +687,7 @@ static void ep93xx_spi_process_message(s
* the chipselect.
*/
ep93xx_spi_chip_setup(espi, spi_get_ctldata(msg->spi));
- ep93xx_spi_cs_control(espi, msg->spi, true);
+ ep93xx_spi_cs_control(msg->spi, true);
list_for_each_entry(t, &msg->transfers, transfer_list) {
ep93xx_spi_process_transfer(espi, msg, t);
@@ -686,7 +699,7 @@ static void ep93xx_spi_process_message(s
* Now the whole message is transferred (or failed for some reason). We
* deselect the device and disable the SPI controller.
*/
- ep93xx_spi_cs_control(espi, msg->spi, false);
+ ep93xx_spi_cs_control(msg->spi, false);
ep93xx_spi_disable(espi);
}
@@ -811,7 +824,6 @@ static int __init ep93xx_spi_probe(struc
platform_set_drvdata(pdev, master);
espi = spi_master_get_devdata(master);
- espi->cs_control = info->cs_control;
espi->clk = clk_get(&pdev->dev, NULL);
if (IS_ERR(espi->clk)) {
@@ -860,7 +872,7 @@ static int __init ep93xx_spi_probe(struc
}
error = request_irq(espi->irq, ep93xx_spi_interrupt, 0,
- "ep93xx-spi", espi);
+ "ep93xx-spi", espi);
if (error) {
dev_err(&pdev->dev, "failed to request irq\n");
goto fail_unmap_regs;
@@ -878,7 +890,7 @@ static int __init ep93xx_spi_probe(struc
/* make sure that the hardware is disabled */
ep93xx_spi_write_u8(espi, SSPCR1, 0);
- error = spi_register_master(master);
+ error = spi_register_master(master);
if (error) {
dev_err(&pdev->dev, "failed to register SPI master\n");
goto fail_free_queue;
More information about the linux-arm-kernel
mailing list