/* * Simple bitbanged-GPIO SPI driver for ETRAX FS et al. * * Copyright (c) 2007 Axis Communications AB * * Author: Hans-Peter Nilsson, inspired by earlier work by * Andre Spanberg but mostly by copying large parts of * spi_s3c24xx_gpio.c, hence also: * Copyright (c) 2006 Ben Dooks * Copyright (c) 2006 Simtec Electronics * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as * published by the Free Software Foundation. * */ #include #include #include #include #include #include #include #include /* Our main driver state. */ struct crisv32_spi_hw_info { struct crisv32_iopin sclk; struct crisv32_iopin mosi; struct crisv32_iopin miso; struct crisv32_iopin cs; }; /* * The driver state hides behind the spi_bitbang state. We're * responsible for allocating that, so we can get a little something * for ourselves. */ struct crisv32_spi_gpio_devdata { struct spi_bitbang bitbang; struct crisv32_spi_hw_info pins; }; /* Helper function getting the driver state from a spi_device. */ static inline struct crisv32_spi_hw_info *spidev_to_hw(struct spi_device *spi) { struct crisv32_spi_gpio_devdata *dd = spi_master_get_devdata(spi->master); return &dd->pins; } /* The SPI-bitbang functions: see spi_bitbang.h at EXPAND_BITBANG_TXRX. */ static inline void setsck(struct spi_device *spi, int is_on) { crisv32_io_set(&spidev_to_hw(spi)->sclk, is_on != 0); } static inline void setmosi(struct spi_device *spi, int is_on) { crisv32_io_set(&spidev_to_hw(spi)->mosi, is_on != 0); } static inline u32 getmiso(struct spi_device *spi) { return crisv32_io_rd(&spidev_to_hw(spi)->miso) != 0 ? 1 : 0; } #define spidelay(x) ndelay(x) #define EXPAND_BITBANG_TXRX #include /* * SPI-bitbang word transmit-functions for the four SPI modes, * dispatching to the inlined functions we just included. */ static u32 crisv32_spi_gpio_txrx_mode0(struct spi_device *spi, unsigned nsecs, u32 word, u8 bits) { return bitbang_txrx_be_cpha0(spi, nsecs, 0, word, bits); } static u32 crisv32_spi_gpio_txrx_mode1(struct spi_device *spi, unsigned nsecs, u32 word, u8 bits) { return bitbang_txrx_be_cpha1(spi, nsecs, 0, word, bits); } static u32 crisv32_spi_gpio_txrx_mode2(struct spi_device *spi, unsigned nsecs, u32 word, u8 bits) { return bitbang_txrx_be_cpha0(spi, nsecs, 1, word, bits); } static u32 crisv32_spi_gpio_txrx_mode3(struct spi_device *spi, unsigned nsecs, u32 word, u8 bits) { return bitbang_txrx_be_cpha1(spi, nsecs, 1, word, bits); } /* SPI-bitbang chip-select function. */ static void crisv32_spi_gpio_chipselect(struct spi_device *spi, int value) { if (spi->mode & SPI_CS_HIGH) crisv32_io_set(&spidev_to_hw(spi)->cs, value == BITBANG_CS_ACTIVE ? 1 : 0); else crisv32_io_set(&spidev_to_hw(spi)->cs, value == BITBANG_CS_ACTIVE ? 0 : 1); } /* Platform-device probe function. */ static int __devinit crisv32_spi_gpio_probe(struct platform_device *dev) { struct spi_master *master; struct crisv32_spi_gpio_devdata *dd; struct resource *res; struct crisv32_spi_gpio_controller_data *gc; int ret = 0; /* * We need to get the controller data as a hardware resource, * or else it wouldn't be available until *after* the * spi_bitbang_start call! */ res = platform_get_resource_byname(dev, 0, "controller_data_ptr"); if (res == NULL) { dev_err(&dev->dev, "can't get controller_data resource\n"); return -EIO; } gc = (struct crisv32_spi_gpio_controller_data *) res->start; master = spi_alloc_master(&dev->dev, sizeof *dd); if (master == NULL) { dev_err(&dev->dev, "failed to allocate spi master\n"); ret = -ENOMEM; goto err; } dd = spi_master_get_devdata(master); platform_set_drvdata(dev, dd); /* * The device data asks for this driver, and holds the id * number, which must be unique among the same-type devices. * We use this as the number of this SPI bus. */ master->bus_num = dev->id; /* * Allocate pins. Note that thus being allocated as GPIO, we * don't have to deconfigure them at the end or if something * fails. */ if ((ret = crisv32_io_get_name(&dd->pins.cs, gc->cs)) != 0 || (ret = crisv32_io_get_name(&dd->pins.miso, gc->miso)) != 0 || (ret = crisv32_io_get_name(&dd->pins.mosi, gc->mosi)) != 0 || (ret = crisv32_io_get_name(&dd->pins.sclk, gc->sclk)) != 0) goto err_no_pins; /* Set directions of the SPI pins. */ crisv32_io_set_dir(&dd->pins.cs, crisv32_io_dir_out); crisv32_io_set_dir(&dd->pins.sclk, crisv32_io_dir_out); crisv32_io_set_dir(&dd->pins.miso, crisv32_io_dir_in); crisv32_io_set_dir(&dd->pins.mosi, crisv32_io_dir_out); /* Set state of the SPI pins. */ dev_dbg(&dev->dev, "cs.port 0x%x, pin: %d\n" dd->pins.cs.port, dd->pins.cs.bit); /* * Can't use crisv32_spi_gpio_chipselect(spi, 1) here; we * don't have a proper "spi" until after spi_bitbang_start. */ crisv32_io_set(&dd->pins.cs, 1); crisv32_io_set(&dd->pins.sclk, 0); crisv32_io_set(&dd->pins.mosi, 0); /* Setup SPI bitbang adapter hooks. */ dd->bitbang.master = spi_master_get(master); dd->bitbang.chipselect = crisv32_spi_gpio_chipselect; dd->bitbang.txrx_word[SPI_MODE_0] = crisv32_spi_gpio_txrx_mode0; dd->bitbang.txrx_word[SPI_MODE_1] = crisv32_spi_gpio_txrx_mode1; dd->bitbang.txrx_word[SPI_MODE_2] = crisv32_spi_gpio_txrx_mode2; dd->bitbang.txrx_word[SPI_MODE_3] = crisv32_spi_gpio_txrx_mode3; ret = spi_bitbang_start(&dd->bitbang); if (ret) goto err_no_bitbang; printk (KERN_INFO "CRIS v32 SPI driver for GPIO" " (cs: %s, miso: %s, mosi: %s, sclk: %s)\n", gc->cs, gc->miso, gc->mosi, gc->sclk); return 0; err_no_bitbang: spi_master_put(dd->bitbang.master); err_no_pins: platform_set_drvdata(dev, NULL); err: return ret; } /* Platform-device remove-function. */ static int __devexit crisv32_spi_gpio_remove(struct platform_device *dev) { struct crisv32_spi_gpio_devdata *dd = platform_get_drvdata(dev); int ret; ret = spi_bitbang_stop(&dd->bitbang); if (ret != 0) return ret; spi_master_put(dd->bitbang.master); platform_set_drvdata(dev, NULL); return 0; } /* * For the time being, there's no suspend/resume support to care * about, so we let those handlers default to NULL. */ static struct platform_driver crisv32_spi_gpio_drv = { .probe = crisv32_spi_gpio_probe, .remove = __devexit_p(crisv32_spi_gpio_remove), .driver = { .name = "spi_crisv32_gpio", .owner = THIS_MODULE, }, }; /* Module init function. */ static int __devinit crisv32_spi_gpio_init(void) { return platform_driver_register(&crisv32_spi_gpio_drv); } /* Module exit function. */ static void __devexit crisv32_spi_gpio_exit(void) { platform_driver_unregister(&crisv32_spi_gpio_drv); } module_init(crisv32_spi_gpio_init); module_exit(crisv32_spi_gpio_exit); MODULE_DESCRIPTION("CRIS v32 SPI-GPIO Driver"); MODULE_AUTHOR("Hans-Peter Nilsson, "); MODULE_LICENSE("GPL");