diff options
Diffstat (limited to 'drivers/spi/spi-sh-sci.c')
| -rw-r--r-- | drivers/spi/spi-sh-sci.c | 198 | 
1 files changed, 198 insertions, 0 deletions
diff --git a/drivers/spi/spi-sh-sci.c b/drivers/spi/spi-sh-sci.c new file mode 100644 index 00000000000..b83dd733684 --- /dev/null +++ b/drivers/spi/spi-sh-sci.c @@ -0,0 +1,198 @@ +/* + * SH SCI SPI interface + * + * Copyright (c) 2008 Magnus Damm + * + * Based on S3C24XX GPIO based SPI driver, which is: + *   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 <linux/kernel.h> +#include <linux/delay.h> +#include <linux/spinlock.h> +#include <linux/platform_device.h> + +#include <linux/spi/spi.h> +#include <linux/spi/spi_bitbang.h> +#include <linux/module.h> + +#include <asm/spi.h> +#include <asm/io.h> + +struct sh_sci_spi { +	struct spi_bitbang bitbang; + +	void __iomem *membase; +	unsigned char val; +	struct sh_spi_info *info; +	struct platform_device *dev; +}; + +#define SCSPTR(sp)	(sp->membase + 0x1c) +#define PIN_SCK		(1 << 2) +#define PIN_TXD		(1 << 0) +#define PIN_RXD		PIN_TXD +#define PIN_INIT	((1 << 1) | (1 << 3) | PIN_SCK | PIN_TXD) + +static inline void setbits(struct sh_sci_spi *sp, int bits, int on) +{ +	/* +	 * We are the only user of SCSPTR so no locking is required. +	 * Reading bit 2 and 0 in SCSPTR gives pin state as input. +	 * Writing the same bits sets the output value. +	 * This makes regular read-modify-write difficult so we +	 * use sp->val to keep track of the latest register value. +	 */ + +	if (on) +		sp->val |= bits; +	else +		sp->val &= ~bits; + +	iowrite8(sp->val, SCSPTR(sp)); +} + +static inline void setsck(struct spi_device *dev, int on) +{ +	setbits(spi_master_get_devdata(dev->master), PIN_SCK, on); +} + +static inline void setmosi(struct spi_device *dev, int on) +{ +	setbits(spi_master_get_devdata(dev->master), PIN_TXD, on); +} + +static inline u32 getmiso(struct spi_device *dev) +{ +	struct sh_sci_spi *sp = spi_master_get_devdata(dev->master); + +	return (ioread8(SCSPTR(sp)) & PIN_RXD) ? 1 : 0; +} + +#define spidelay(x) ndelay(x) + +#include "spi-bitbang-txrx.h" + +static u32 sh_sci_spi_txrx_mode0(struct spi_device *spi, +				      unsigned nsecs, u32 word, u8 bits) +{ +	return bitbang_txrx_be_cpha0(spi, nsecs, 0, 0, word, bits); +} + +static u32 sh_sci_spi_txrx_mode1(struct spi_device *spi, +				      unsigned nsecs, u32 word, u8 bits) +{ +	return bitbang_txrx_be_cpha1(spi, nsecs, 0, 0, word, bits); +} + +static u32 sh_sci_spi_txrx_mode2(struct spi_device *spi, +				      unsigned nsecs, u32 word, u8 bits) +{ +	return bitbang_txrx_be_cpha0(spi, nsecs, 1, 0, word, bits); +} + +static u32 sh_sci_spi_txrx_mode3(struct spi_device *spi, +				      unsigned nsecs, u32 word, u8 bits) +{ +	return bitbang_txrx_be_cpha1(spi, nsecs, 1, 0, word, bits); +} + +static void sh_sci_spi_chipselect(struct spi_device *dev, int value) +{ +	struct sh_sci_spi *sp = spi_master_get_devdata(dev->master); + +	if (sp->info->chip_select) +		(sp->info->chip_select)(sp->info, dev->chip_select, value); +} + +static int sh_sci_spi_probe(struct platform_device *dev) +{ +	struct resource	*r; +	struct spi_master *master; +	struct sh_sci_spi *sp; +	int ret; + +	master = spi_alloc_master(&dev->dev, sizeof(struct sh_sci_spi)); +	if (master == NULL) { +		dev_err(&dev->dev, "failed to allocate spi master\n"); +		ret = -ENOMEM; +		goto err0; +	} + +	sp = spi_master_get_devdata(master); + +	platform_set_drvdata(dev, sp); +	sp->info = dev_get_platdata(&dev->dev); +	if (!sp->info) { +		dev_err(&dev->dev, "platform data is missing\n"); +		ret = -ENOENT; +		goto err1; +	} + +	/* setup spi bitbang adaptor */ +	sp->bitbang.master = master; +	sp->bitbang.master->bus_num = sp->info->bus_num; +	sp->bitbang.master->num_chipselect = sp->info->num_chipselect; +	sp->bitbang.chipselect = sh_sci_spi_chipselect; + +	sp->bitbang.txrx_word[SPI_MODE_0] = sh_sci_spi_txrx_mode0; +	sp->bitbang.txrx_word[SPI_MODE_1] = sh_sci_spi_txrx_mode1; +	sp->bitbang.txrx_word[SPI_MODE_2] = sh_sci_spi_txrx_mode2; +	sp->bitbang.txrx_word[SPI_MODE_3] = sh_sci_spi_txrx_mode3; + +	r = platform_get_resource(dev, IORESOURCE_MEM, 0); +	if (r == NULL) { +		ret = -ENOENT; +		goto err1; +	} +	sp->membase = ioremap(r->start, resource_size(r)); +	if (!sp->membase) { +		ret = -ENXIO; +		goto err1; +	} +	sp->val = ioread8(SCSPTR(sp)); +	setbits(sp, PIN_INIT, 1); + +	ret = spi_bitbang_start(&sp->bitbang); +	if (!ret) +		return 0; + +	setbits(sp, PIN_INIT, 0); +	iounmap(sp->membase); + err1: +	spi_master_put(sp->bitbang.master); + err0: +	return ret; +} + +static int sh_sci_spi_remove(struct platform_device *dev) +{ +	struct sh_sci_spi *sp = platform_get_drvdata(dev); + +	spi_bitbang_stop(&sp->bitbang); +	setbits(sp, PIN_INIT, 0); +	iounmap(sp->membase); +	spi_master_put(sp->bitbang.master); +	return 0; +} + +static struct platform_driver sh_sci_spi_drv = { +	.probe		= sh_sci_spi_probe, +	.remove		= sh_sci_spi_remove, +	.driver		= { +		.name	= "spi_sh_sci", +		.owner	= THIS_MODULE, +	}, +}; +module_platform_driver(sh_sci_spi_drv); + +MODULE_DESCRIPTION("SH SCI SPI Driver"); +MODULE_AUTHOR("Magnus Damm <damm@opensource.se>"); +MODULE_LICENSE("GPL"); +MODULE_ALIAS("platform:spi_sh_sci");  | 
