Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 1fc7547d authored by Ben Dooks's avatar Ben Dooks Committed by Linus Torvalds
Browse files

[PATCH] S3C24XX: GPIO based SPI driver



SPI driver for SPI by GPIO on the Samsung S3C24XX series of SoC processors.

Signed-off-by: default avatarBen Dooks <ben-linux@fluff.org>
Cc: Greg KH <greg@kroah.com>
Cc: David Brownell <david-b@pacbell.net>
Signed-off-by: default avatarAndrew Morton <akpm@osdl.org>
Signed-off-by: default avatarLinus Torvalds <torvalds@osdl.org>
parent 1b81d663
Loading
Loading
Loading
Loading
+8 −0
Original line number Diff line number Diff line
@@ -95,6 +95,14 @@ config SPI_PXA2XX
	  The driver can be configured to use any SSP port and additional
	  documentation can be found a Documentation/spi/pxa2xx.

config SPI_S3C24XX_GPIO
	tristate "Samsung S3C24XX series SPI by GPIO"
	depends on SPI_MASTER && ARCH_S3C2410 && SPI_BITBANG && EXPERIMENTAL
	help
	  SPI driver for Samsung S3C24XX series ARM SoCs using
	  GPIO lines to provide the SPI bus. This can be used where
	  the inbuilt hardware cannot provide the transfer mode, or
	  where the board is using non hardware connected pins.
#
# Add new SPI master controllers in alphabetical order above this line
#
+1 −0
Original line number Diff line number Diff line
@@ -15,6 +15,7 @@ obj-$(CONFIG_SPI_BITBANG) += spi_bitbang.o
obj-$(CONFIG_SPI_BUTTERFLY)		+= spi_butterfly.o
obj-$(CONFIG_SPI_PXA2XX)		+= pxa2xx_spi.o
obj-$(CONFIG_SPI_MPC83xx)		+= spi_mpc83xx.o
obj-$(CONFIG_SPI_S3C24XX_GPIO)		+= spi_s3c24xx_gpio.o
# 	... add above this line ...

# SPI protocol drivers (device/link on bus)
+1 −0
Original line number Diff line number Diff line
@@ -321,6 +321,7 @@ static void butterfly_attach(struct parport *p)
	 * (firmware resets at45, acts as spi slave) or neither (we ignore
	 * both, AVR uses AT45).  Here we expect firmware for the first option.
	 */

	pp->info[0].max_speed_hz = 15 * 1000 * 1000;
	strcpy(pp->info[0].modalias, "mtd_dataflash");
	pp->info[0].platform_data = &flash;
+188 −0
Original line number Diff line number Diff line
/* linux/drivers/spi/spi_s3c24xx_gpio.c
 *
 * Copyright (c) 2006 Ben Dooks
 * Copyright (c) 2006 Simtec Electronics
 *
 * S3C24XX GPIO based SPI driver
 *
 * 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/config.h>
#include <linux/kernel.h>
#include <linux/init.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 <asm/arch/regs-gpio.h>
#include <asm/arch/spi-gpio.h>
#include <asm/arch/hardware.h>

struct s3c2410_spigpio {
	struct spi_bitbang		 bitbang;

	struct s3c2410_spigpio_info	*info;
	struct platform_device		*dev;
};

static inline struct s3c2410_spigpio *spidev_to_sg(struct spi_device *spi)
{
	return spi->controller_data;
}

static inline void setsck(struct spi_device *dev, int on)
{
	struct s3c2410_spigpio *sg = spidev_to_sg(dev);
	s3c2410_gpio_setpin(sg->info->pin_clk, on ? 1 : 0);
}

static inline void setmosi(struct spi_device *dev, int on)
{
	struct s3c2410_spigpio *sg = spidev_to_sg(dev);
	s3c2410_gpio_setpin(sg->info->pin_mosi, on ? 1 : 0);
}

static inline u32 getmiso(struct spi_device *dev)
{
	struct s3c2410_spigpio *sg = spidev_to_sg(dev);
	return s3c2410_gpio_getpin(sg->info->pin_miso) ? 1 : 0;
}

#define spidelay(x) ndelay(x)

#define	EXPAND_BITBANG_TXRX
#include <linux/spi/spi_bitbang.h>


static u32 s3c2410_spigpio_txrx_mode0(struct spi_device *spi,
				      unsigned nsecs, u32 word, u8 bits)
{
	return bitbang_txrx_be_cpha0(spi, nsecs, 0, word, bits);
}

static u32 s3c2410_spigpio_txrx_mode1(struct spi_device *spi,
				      unsigned nsecs, u32 word, u8 bits)
{
	return bitbang_txrx_be_cpha1(spi, nsecs, 0, word, bits);
}

static void s3c2410_spigpio_chipselect(struct spi_device *dev, int value)
{
	struct s3c2410_spigpio *sg = spidev_to_sg(dev);

	if (sg->info && sg->info->chip_select)
		(sg->info->chip_select)(sg->info, value);
}

static int s3c2410_spigpio_probe(struct platform_device *dev)
{
	struct spi_master	*master;
	struct s3c2410_spigpio  *sp;
	int ret;
	int i;

	master = spi_alloc_master(&dev->dev, sizeof(struct s3c2410_spigpio));
	if (master == NULL) {
		dev_err(&dev->dev, "failed to allocate spi master\n");
		ret = -ENOMEM;
		goto err;
	}

	sp = spi_master_get_devdata(master);

	platform_set_drvdata(dev, sp);

	/* copy in the plkatform data */
	sp->info = dev->dev.platform_data;

	/* setup spi bitbang adaptor */
	sp->bitbang.master = spi_master_get(master);
	sp->bitbang.chipselect = s3c2410_spigpio_chipselect;

	sp->bitbang.txrx_word[SPI_MODE_0] = s3c2410_spigpio_txrx_mode0;
	sp->bitbang.txrx_word[SPI_MODE_1] = s3c2410_spigpio_txrx_mode1;

	/* set state of spi pins */
	s3c2410_gpio_setpin(sp->info->pin_clk, 0);
	s3c2410_gpio_setpin(sp->info->pin_mosi, 0);

	s3c2410_gpio_cfgpin(sp->info->pin_clk, S3C2410_GPIO_OUTPUT);
	s3c2410_gpio_cfgpin(sp->info->pin_mosi, S3C2410_GPIO_OUTPUT);
	s3c2410_gpio_cfgpin(sp->info->pin_miso, S3C2410_GPIO_INPUT);

	ret = spi_bitbang_start(&sp->bitbang);
	if (ret)
		goto err_no_bitbang;

	/* register the chips to go with the board */

	for (i = 0; i < sp->info->board_size; i++) {
		dev_info(&dev->dev, "registering %p: %s\n",
			 &sp->info->board_info[i],
			 sp->info->board_info[i].modalias);

		sp->info->board_info[i].controller_data = sp;
		spi_new_device(master, sp->info->board_info + i);
	}

	return 0;

 err_no_bitbang:
	spi_master_put(sp->bitbang.master);
 err:
	return ret;

}

static int s3c2410_spigpio_remove(struct platform_device *dev)
{
	struct s3c2410_spigpio *sp = platform_get_drvdata(dev);

	spi_bitbang_stop(&sp->bitbang);
	spi_master_put(sp->bitbang.master);

	return 0;
}

/* all gpio should be held over suspend/resume, so we should
 * not need to deal with this
*/

#define s3c2410_spigpio_suspend NULL
#define s3c2410_spigpio_resume NULL


static struct platform_driver s3c2410_spigpio_drv = {
	.probe		= s3c2410_spigpio_probe,
        .remove		= s3c2410_spigpio_remove,
        .suspend	= s3c2410_spigpio_suspend,
        .resume		= s3c2410_spigpio_resume,
        .driver		= {
		.name	= "s3c24xx-spi-gpio",
		.owner	= THIS_MODULE,
        },
};

static int __init s3c2410_spigpio_init(void)
{
        return platform_driver_register(&s3c2410_spigpio_drv);
}

static void __exit s3c2410_spigpio_exit(void)
{
        platform_driver_unregister(&s3c2410_spigpio_drv);
}

module_init(s3c2410_spigpio_init);
module_exit(s3c2410_spigpio_exit);

MODULE_DESCRIPTION("S3C24XX SPI Driver");
MODULE_AUTHOR("Ben Dooks, <ben@simtec.co.uk>");
MODULE_LICENSE("GPL");
+31 −0
Original line number Diff line number Diff line
/* linux/include/asm-arm/arch-s3c2410/spi.h
 *
 * Copyright (c) 2006 Simtec Electronics
 *	Ben Dooks <ben@simtec.co.uk>
 *
 * S3C2410 - SPI Controller platfrom_device info
 *
 * 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.
*/

#ifndef __ASM_ARCH_SPIGPIO_H
#define __ASM_ARCH_SPIGPIO_H __FILE__

struct s3c2410_spigpio_info;
struct spi_board_info;

struct s3c2410_spigpio_info {
	unsigned long		 pin_clk;
	unsigned long		 pin_mosi;
	unsigned long		 pin_miso;

	unsigned long		 board_size;
	struct spi_board_info	*board_info;

	void (*chip_select)(struct s3c2410_spigpio_info *spi, int cs);
};


#endif /* __ASM_ARCH_SPIGPIO_H */