diff options
Diffstat (limited to 'recipes/linux/linux-2.6.27/ts72xx/0019-EP93xx-SPI-driver.patch')
-rw-r--r-- | recipes/linux/linux-2.6.27/ts72xx/0019-EP93xx-SPI-driver.patch | 989 |
1 files changed, 989 insertions, 0 deletions
diff --git a/recipes/linux/linux-2.6.27/ts72xx/0019-EP93xx-SPI-driver.patch b/recipes/linux/linux-2.6.27/ts72xx/0019-EP93xx-SPI-driver.patch new file mode 100644 index 0000000000..185fa71be8 --- /dev/null +++ b/recipes/linux/linux-2.6.27/ts72xx/0019-EP93xx-SPI-driver.patch @@ -0,0 +1,989 @@ +From 007d9f94b01c399553cff4143ae3d696b8189b57 Mon Sep 17 00:00:00 2001 +From: Matthieu Crapet <mcrapet@gmail.com> +Date: Sun, 4 Jan 2009 01:51:24 +0100 +Subject: [PATCH] EP93xx SPI driver +MIME-Version: 1.0 +Content-Type: text/plain; charset=utf-8 +Content-Transfer-Encoding: 8bit + +Signed-off-by: Petr Å tetiar <ynezz@true.cz> +--- + arch/arm/mach-ep93xx/clock.c | 6 +- + arch/arm/mach-ep93xx/core.c | 29 ++ + arch/arm/mach-ep93xx/include/mach/ep93xx-regs.h | 1 + + arch/arm/mach-ep93xx/include/mach/spi.h | 18 + + arch/arm/mach-ep93xx/ts72xx.c | 37 ++- + drivers/spi/Kconfig | 13 + + drivers/spi/Makefile | 2 + + drivers/spi/spi_ep93xx.c | 496 +++++++++++++++++++++++ + drivers/spi/spi_ep93xx.h | 61 +++ + drivers/spi/tmp124.c | 158 +++++++ + 10 files changed, 819 insertions(+), 2 deletions(-) + create mode 100644 arch/arm/mach-ep93xx/include/mach/spi.h + create mode 100644 drivers/spi/spi_ep93xx.c + create mode 100644 drivers/spi/spi_ep93xx.h + create mode 100644 drivers/spi/tmp124.c + +diff --git a/arch/arm/mach-ep93xx/clock.c b/arch/arm/mach-ep93xx/clock.c +index 6062e47..2b47048 100644 +--- a/arch/arm/mach-ep93xx/clock.c ++++ b/arch/arm/mach-ep93xx/clock.c +@@ -51,7 +51,10 @@ static struct clk clk_usb_host = { + .enable_reg = EP93XX_SYSCON_CLOCK_CONTROL, + .enable_mask = EP93XX_SYSCON_CLOCK_USH_EN, + }; +- ++static struct clk clk_ssp = { ++ .name = "sspclk", ++ .rate = 7400000, ++}; + + static struct clk *clocks[] = { + &clk_uart, +@@ -61,6 +64,7 @@ static struct clk *clocks[] = { + &clk_p, + &clk_pll2, + &clk_usb_host, ++ &clk_ssp, + }; + + struct clk *clk_get(struct device *dev, const char *id) +diff --git a/arch/arm/mach-ep93xx/core.c b/arch/arm/mach-ep93xx/core.c +index f689531..00c2316 100644 +--- a/arch/arm/mach-ep93xx/core.c ++++ b/arch/arm/mach-ep93xx/core.c +@@ -41,6 +41,7 @@ + #include <asm/setup.h> + #include <asm/memory.h> + #include <mach/hardware.h> ++#include <mach/spi.h> + #include <asm/irq.h> + #include <asm/system.h> + #include <asm/tlbflush.h> +@@ -484,6 +485,33 @@ static struct platform_device ep93xx_ohci_device = { + }; + + ++static struct resource ep93xx_ssp_resources[] = { ++ { ++ .start = EP93XX_SPI_BASE, // virtual addresses ++ .end = EP93XX_SPI_BASE + 0x14, ++ .flags = IORESOURCE_MEM, ++ }, { ++ .start = IRQ_EP93XX_SSP, // overrun in receive fifo ++ .end = IRQ_EP93XX_SSP, ++ .flags = IORESOURCE_IRQ, ++ } ++}; ++ ++static struct ep93xx_spi_data ep93xx_ssp_data = { ++ .chip_select_num = 4, ++}; ++ ++static struct platform_device ep93xx_ssp_device = { ++ .name = "ep93xx-spi", ++ .id = 1, ++ .resource = ep93xx_ssp_resources, ++ .num_resources = ARRAY_SIZE(ep93xx_ssp_resources), ++ .dev = { ++ .platform_data = &ep93xx_ssp_data, ++ } ++}; ++ ++ + static const struct gpio_led ep93xx_led_pins[] = { + { + .name = "green", +@@ -563,4 +591,5 @@ void __init ep93xx_init_devices(void) + platform_device_register(&ep93xx_gpio_leds); + platform_device_register(&ep93xx_rtc_device); + platform_device_register(&ep93xx_ohci_device); ++ platform_device_register(&ep93xx_ssp_device); + } +diff --git a/arch/arm/mach-ep93xx/include/mach/ep93xx-regs.h b/arch/arm/mach-ep93xx/include/mach/ep93xx-regs.h +index e26b41b..be0b9d4 100644 +--- a/arch/arm/mach-ep93xx/include/mach/ep93xx-regs.h ++++ b/arch/arm/mach-ep93xx/include/mach/ep93xx-regs.h +@@ -97,6 +97,7 @@ + #define EP93XX_AAC_BASE (EP93XX_APB_VIRT_BASE + 0x00080000) + + #define EP93XX_SPI_BASE (EP93XX_APB_VIRT_BASE + 0x000a0000) ++#define EP93XX_SPI_PHYS_BASE (EP93XX_APB_PHYS_BASE + 0x000a0000) + + #define EP93XX_IRDA_BASE (EP93XX_APB_VIRT_BASE + 0x000b0000) + +diff --git a/arch/arm/mach-ep93xx/include/mach/spi.h b/arch/arm/mach-ep93xx/include/mach/spi.h +new file mode 100644 +index 0000000..0e07fc9 +--- /dev/null ++++ b/arch/arm/mach-ep93xx/include/mach/spi.h +@@ -0,0 +1,18 @@ ++/* ++ * arch/arm/mach-ep93xx/include/mach/spi.h ++ */ ++ ++struct ep93xx_spi_data { ++ u16 chip_select_num; ++}; ++ ++ ++/* spi_board_info.controller_data for SPI slave devices */ ++struct ep93xx_spi_chip { ++ void (*cs_control)(u32 command); ++}; ++ ++/* Chip-select state */ ++#define SPI_CS_ASSERT 0x1 ++#define SPI_CS_DEASSERT 0x2 ++#define SPI_CS_INIT 0x4 +diff --git a/arch/arm/mach-ep93xx/ts72xx.c b/arch/arm/mach-ep93xx/ts72xx.c +index 9835b05..2141e73 100644 +--- a/arch/arm/mach-ep93xx/ts72xx.c ++++ b/arch/arm/mach-ep93xx/ts72xx.c +@@ -19,8 +19,11 @@ + #include <linux/mtd/physmap.h> + #include <linux/platform_device.h> + #include <linux/m48t86.h> ++#include <linux/spi/spi.h> + #include <asm/io.h> + #include <mach/hardware.h> ++#include <mach/spi.h> ++#include <mach/gpio.h> + #include <asm/mach-types.h> + #include <asm/mach/arch.h> + #include <asm/mach/map.h> +@@ -233,6 +236,34 @@ static struct platform_device ts72xx_max197_device = { + .resource = ts72xx_max197_resources, + }; + ++#if defined(CONFIG_SPI_MASTER) ++void tmp124_spi_cs(u32 command) // FGPIO[2] ++{ ++ if (command & SPI_CS_ASSERT) { ++ gpio_set_value(EP93XX_GPIO_LINE_MCCD2, 0); ++ } else if (command & SPI_CS_DEASSERT) { ++ gpio_set_value(EP93XX_GPIO_LINE_MCCD2, 1); ++ } else if (command & SPI_CS_INIT) { ++ gpio_direction_output(EP93XX_GPIO_LINE_MCCD2, 1); ++ } ++} ++ ++static struct ep93xx_spi_chip tmp124_hw = { ++ .cs_control = tmp124_spi_cs, ++}; ++ ++static struct spi_board_info ts72xx_spi_bus[] __initdata = { ++ { ++ /* TMP124 */ ++ .modalias = "tmp124", ++ .controller_data = &tmp124_hw, ++ .bus_num = 1, ++ .chip_select = 0, ++ .max_speed_hz = 2 * 1000 * 1000, ++ } ++}; ++#endif ++ + static void __init ts72xx_init_machine(void) + { + ep93xx_init_devices(); +@@ -251,7 +282,11 @@ static void __init ts72xx_init_machine(void) + + if (is_max197_installed()) { + platform_device_register(&ts72xx_max197_device); +- } ++ } ++ ++ #if defined(CONFIG_SPI_MASTER) ++ spi_register_board_info(ts72xx_spi_bus, ARRAY_SIZE(ts72xx_spi_bus)); ++ #endif + } + + MACHINE_START(TS72XX, "Technologic Systems TS-72xx SBC") +diff --git a/drivers/spi/Kconfig b/drivers/spi/Kconfig +index b9d0efb..51b55b5 100644 +--- a/drivers/spi/Kconfig ++++ b/drivers/spi/Kconfig +@@ -204,6 +204,12 @@ config SPI_XILINX + See the "OPB Serial Peripheral Interface (SPI) (v1.00e)" + Product Specification document (DS464) for hardware details. + ++config SPI_EP93XX ++ tristate "EP93XX SPI controller" ++ depends on SPI_MASTER ++ help ++ Simple SPI driver for EP93xx. ++ + # + # Add new SPI master controllers in alphabetical order above this line + # +@@ -243,6 +249,13 @@ config SPI_TLE62X0 + sysfs interface, with each line presented as a kind of GPIO + exposing both switch control and diagnostic feedback. + ++config SPI_TMP124 ++ tristate "Texas Instruments TMP1224, TMP124" ++ depends on SPI_MASTER && SYSFS ++ help ++ SPI driver for TMP12X temperature sensor chips. ++ This provides a sysfs entry for temperature reading (2°C accurate). ++ + # + # Add new SPI protocol masters in alphabetical order above this line + # +diff --git a/drivers/spi/Makefile b/drivers/spi/Makefile +index ccf18de..1effdf3 100644 +--- a/drivers/spi/Makefile ++++ b/drivers/spi/Makefile +@@ -28,6 +28,7 @@ obj-$(CONFIG_SPI_S3C24XX_GPIO) += spi_s3c24xx_gpio.o + obj-$(CONFIG_SPI_S3C24XX) += spi_s3c24xx.o + obj-$(CONFIG_SPI_TXX9) += spi_txx9.o + obj-$(CONFIG_SPI_XILINX) += xilinx_spi.o ++obj-$(CONFIG_SPI_EP93XX) += spi_ep93xx.o + obj-$(CONFIG_SPI_SH_SCI) += spi_sh_sci.o + # ... add above this line ... + +@@ -35,6 +36,7 @@ obj-$(CONFIG_SPI_SH_SCI) += spi_sh_sci.o + obj-$(CONFIG_SPI_AT25) += at25.o + obj-$(CONFIG_SPI_SPIDEV) += spidev.o + obj-$(CONFIG_SPI_TLE62X0) += tle62x0.o ++obj-$(CONFIG_SPI_TMP124) += tmp124.o + # ... add above this line ... + + # SPI slave controller drivers (upstream link) +diff --git a/drivers/spi/spi_ep93xx.c b/drivers/spi/spi_ep93xx.c +new file mode 100644 +index 0000000..0f51b00 +--- /dev/null ++++ b/drivers/spi/spi_ep93xx.c +@@ -0,0 +1,496 @@ ++/* ++ * EP93xx SPI driver ++ * ++ * (c) Copyright 2008 Matthieu Crapet <mcrapet@gmail.com> ++ * Based on pxa2xx_spi.c by Stephen Street / StreetFire Sound Labs ++ * ++ * 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 ++ * the Free Software Foundation; either version 2 of the License, or ++ * (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * Notes: ++ * - Uses SSP IP of processor ++ * - Restricted to SPI master mode ++ * - No DMA transfer ++ * - No power management support ++ */ ++ ++#include <linux/init.h> ++#include <linux/module.h> ++#include <linux/device.h> ++#include <linux/interrupt.h> ++#include <linux/delay.h> ++#include <linux/platform_device.h> ++#include <linux/spi/spi.h> ++#include <linux/clk.h> ++#include <asm/irq.h> ++#include <mach/hardware.h> ++#include <mach/spi.h> ++ ++#include <linux/sched.h> ++#include <linux/spinlock.h> ++#include <linux/workqueue.h> ++ ++#include "spi_ep93xx.h" ++ ++ ++struct ep93xx_spi { ++ struct spi_master *master; /* SPI framework hookup */ ++ void __iomem *ioaddr; /* Virtual base address to SSP registers */ ++ u32 freq_max; ++ u32 freq_min; ++ struct clk *clk; ++ ++ struct workqueue_struct *workqueue; ++ struct work_struct work; ++ spinlock_t lock; ++ struct list_head queue; ++ ++ struct ep93xx_spi_chip *cs_chip; /* Chip Select function */ ++}; ++ ++static inline u16 read_reg(void *base, off_t offset) ++{ ++ return __raw_readw(base + offset); ++} ++ ++static inline void write_reg(u16 v, void *base, off_t offset) ++{ ++ __raw_writew(v, base + offset); ++} ++ ++/* ++ * compute SCR and CPSDVR bits to setup spi clock based on main input clock rate ++ * that was specified in platform data structure ++ * according to datasheet: ++ * tempclk = sspclk / cpsdvr ++ * spiclk = tempclk / (scr + 1) ++ * SCR valid range is 0..255 ++ * CPSDVR valid range is 2..254 ++ */ ++static int spi_speed_set(struct ep93xx_spi *drv_data, unsigned speed_hz) ++{ ++ unsigned long mainclk_hz = clk_get_rate(drv_data->clk); ++ u32 cpsdvr, scr; ++ u16 ssp_cr0; ++ ++ for (cpsdvr = 2; cpsdvr <= 254; cpsdvr+=2) { ++ scr = DIV_ROUND_UP(mainclk_hz / speed_hz, cpsdvr); ++ /* now we have SCR+1 in scr, so count with that */ ++ if (scr == 0) { /* speed_hz too big */ ++ return -EINVAL; ++ } ++ if (scr <= (255 + 1)) ++ break; /* we have valid scr and cpsdvr */ ++ } ++ if (cpsdvr > 254) { ++ /* speed_hz is too small, set to minimum speed */ ++ scr = cpsdvr + 1; ++ cpsdvr--; ++ } ++ scr--; ++ write_reg(cpsdvr, drv_data->ioaddr, SSPCPSR); ++ ssp_cr0 = read_reg(drv_data->ioaddr, SSPCR0); ++ ssp_cr0 &= ~(SSP_CONTROL_SCR(0xff)); ++ write_reg((ssp_cr0 | SSP_CONTROL_SCR(scr)), drv_data->ioaddr, SSPCR0); ++ ++ return 0; ++} ++ ++static irqreturn_t ssp_int(int irq, void *dev_id) ++{ ++ struct ep93xx_spi *drv_data = dev_id; ++ write_reg(SSP_SSPIxx_RORIS, drv_data->ioaddr, SSPIxR); /* clear it */ ++ ++ printk(KERN_WARNING "SSP overrun\n"); ++ return IRQ_HANDLED; ++} ++ ++static int transfer_one_work(struct ep93xx_spi *drv_data, struct spi_message *msg) ++{ ++ struct spi_device *spi = msg->spi; ++ struct spi_transfer *xfer; ++ int i; ++ u8 *p; ++ ++ drv_data->cs_chip->cs_control(SPI_CS_ASSERT); ++ ++ list_for_each_entry(xfer, &msg->transfers, transfer_list) { ++ if (!(xfer->tx_buf || xfer->rx_buf)) { ++ dev_dbg(&spi->dev, "missing rx or tx buf\n"); ++ drv_data->cs_chip->cs_control(SPI_CS_DEASSERT); ++ return -EINVAL; ++ } ++ ++ if (xfer->bits_per_word) { ++ u16 v = read_reg(drv_data->ioaddr, SSPCR0); ++ v = v & SSP_CONTROL_DSS_MASK; ++ v = v | ((xfer->bits_per_word - 1) & SSP_CONTROL_DSS_MASK); ++ write_reg(v, drv_data->ioaddr, SSPCR0); ++ } ++ ++ if (xfer->speed_hz) { ++ if (spi_speed_set(drv_data,xfer->speed_hz) != 0){ ++ dev_err(&spi->dev, "xfer speed hz invalid\n"); ++ return -EINVAL; ++ } ++ } ++ ++ if (xfer->tx_buf) { ++ p = (u8 *)xfer->tx_buf; ++ ++ if ((spi->bits_per_word == 16 && xfer->bits_per_word == 0) || ++ (xfer->bits_per_word == 16)) { ++ for (i = 0; i < xfer->len; i+=2) ++ write_reg((p[i] << 8) + p[i+1], drv_data->ioaddr, SSPDR); ++ } else { ++ for (i = 0; i < xfer->len; i++) ++ write_reg(p[i], drv_data->ioaddr, SSPDR); ++ } ++ } ++ ++ if (xfer->rx_buf) { ++ u16 v; ++ p = xfer->rx_buf; ++ ++ if ((spi->bits_per_word == 16 && xfer->bits_per_word == 0) || ++ (xfer->bits_per_word == 16)) { ++ for (i = 0; i < xfer->len; i+=2) { ++ v = read_reg(drv_data->ioaddr, SSPDR); ++ p[i] = v >> 8; ++ p[i+1] = v & 0xFF; ++ } ++ } else { ++ for (i = 0; i < xfer->len; i++) ++ p[i] = read_reg(drv_data->ioaddr, SSPDR); ++ } ++ } ++ ++ /* restore device bits_per_word */ ++ if (xfer->bits_per_word) { ++ u16 v = read_reg(drv_data->ioaddr, SSPCR0); ++ v = v & SSP_CONTROL_DSS_MASK; ++ v |= spi->bits_per_word - 1; ++ write_reg(v, drv_data->ioaddr, SSPCR0); ++ } ++ ++ /* restore device speed_hz */ ++ if (xfer->speed_hz) { ++ if (spi_speed_set(drv_data,spi->max_speed_hz) != 0) ++ return -EINVAL; ++ } ++ ++ dev_dbg(&spi->dev, "transfer: len=%u, tx_buf=%p, rx_buf=%p\n", xfer->len, xfer->tx_buf, xfer->rx_buf); ++ } ++ ++ if (xfer->delay_usecs) ++ udelay(xfer->delay_usecs); ++ drv_data->cs_chip->cs_control(SPI_CS_DEASSERT); ++ ++ msg->actual_length = 0; ++ msg->status = 0; ++ ++ if (msg->complete) ++ msg->complete(msg->context); ++ ++ return 0; ++} ++ ++ ++static void ssp_work(struct work_struct *work) ++{ ++ struct ep93xx_spi *drv_data = container_of(work, struct ep93xx_spi, work); ++ unsigned long flags; ++ ++ spin_lock_irqsave(&drv_data->lock, flags); ++ while (!list_empty(&drv_data->queue)) { ++ struct spi_message *m; ++ ++ m = container_of(drv_data->queue.next, struct spi_message, queue); ++ list_del_init(&m->queue); ++ spin_unlock_irqrestore(&drv_data->lock, flags); ++ ++ transfer_one_work(drv_data, m); ++ ++ spin_lock_irqsave(&drv_data->lock, flags); ++ } ++ spin_unlock_irqrestore(&drv_data->lock, flags); ++} ++ ++ ++static int ssp_transfer(struct spi_device *spi, struct spi_message *m) ++{ ++ struct spi_master *master = spi->master; ++ struct ep93xx_spi *drv_data = spi_master_get_devdata(master); ++ struct spi_transfer *t; ++ unsigned long flags; ++ ++ m->actual_length = 0; ++ ++ /* check each transfer's parameters */ ++ list_for_each_entry (t, &m->transfers, transfer_list) { ++ u32 speed_hz = t->speed_hz ? t->speed_hz : spi->max_speed_hz; ++ u8 bits_per_word = t->bits_per_word ? t->bits_per_word : spi->bits_per_word; ++ ++ if (!t->tx_buf && !t->rx_buf && t->len) ++ return -EINVAL; ++ if (bits_per_word < 4 || bits_per_word > 16) ++ return -EINVAL; ++ /*if (t->len & ((bits_per_word >> 3) - 1)) ++ return -EINVAL;*/ ++ if (speed_hz < drv_data->freq_min || speed_hz > drv_data->freq_max) ++ return -EINVAL; ++ } ++ ++ spin_lock_irqsave(&drv_data->lock, flags); ++ list_add_tail(&m->queue, &drv_data->queue); ++ queue_work(drv_data->workqueue, &drv_data->work); ++ spin_unlock_irqrestore(&drv_data->lock, flags); ++ ++ return 0; ++ ++} ++ ++/* the spi->mode bits understood by this driver: */ ++#define MODEBITS (SPI_CPOL | SPI_CPHA) ++ ++static int ssp_setup(struct spi_device *spi) ++{ ++ struct ep93xx_spi *drv_data = spi_master_get_devdata(spi->master); ++ struct ep93xx_spi_chip *chip_info; ++ u16 v; ++ ++ /* Get controller data */ ++ chip_info = spi->controller_data; ++ if (!chip_info) { ++ dev_err(&spi->dev, "setup: controller data required\n"); ++ return -EINVAL; ++ } ++ drv_data->cs_chip = chip_info; ++ drv_data->cs_chip->cs_control(SPI_CS_INIT); ++ ++ if (!spi->bits_per_word) { ++ spi->bits_per_word = 8; ++ } ++ ++ if (spi->bits_per_word < 4 || spi->bits_per_word > 16) { ++ dev_dbg(&spi->dev, "setup: unsupported %d bit words\n", ++ spi->bits_per_word); ++ return -EINVAL; ++ } ++ ++ if (spi->chip_select > spi->master->num_chipselect) { ++ dev_dbg(&spi->dev, "setup: invalid chipselect %u (%u defined)\n", ++ spi->chip_select, spi->master->num_chipselect); ++ return -EINVAL; ++ } ++ ++ if (spi->mode & ~MODEBITS) { ++ dev_dbg(&spi->dev, "setup: unsupported mode bits %x\n", ++ spi->mode & ~MODEBITS); ++ return -EINVAL; ++ } ++ ++ v = read_reg(drv_data->ioaddr, SSPCR0); ++ ++ if (spi->mode & SPI_CPOL) ++ v |= SSP_CONTROL_SPO; ++ else ++ v &= ~SSP_CONTROL_SPO; ++ ++ if (spi->mode & SPI_CPHA) ++ v |= SSP_CONTROL_SPH; ++ else ++ v &= ~SSP_CONTROL_SPH; ++ ++ v = v & SSP_CONTROL_DSS_MASK; ++ v |= spi->bits_per_word - 1; ++ ++ write_reg(v, drv_data->ioaddr, SSPCR0); ++ ++ if(!spi->max_speed_hz){ ++ spi->max_speed_hz = drv_data->freq_min; ++ }else if(spi->max_speed_hz > drv_data->freq_max || ++ spi->max_speed_hz < drv_data->freq_min){ ++ return -EINVAL; ++ } ++ ++ if (spi_speed_set(drv_data,spi->max_speed_hz) != 0){ ++ dev_dbg(&spi->dev, "setup: unsupported speed %u\n", spi->max_speed_hz); ++ return -EINVAL; ++ } ++ ++ return 0; ++} ++ ++ ++static void ssp_cleanup(struct spi_device *spi) ++{ ++ struct ep93xx_spi *drv_data = spi_master_get_devdata(spi->master); ++ drv_data->cs_chip = NULL; ++} ++ ++ ++static int __init spi_ep93xx_probe(struct platform_device *pdev) ++{ ++ struct device *dev = &pdev->dev; ++ struct spi_master *master; ++ struct ep93xx_spi_data *spi_data = pdev->dev.platform_data; ++ struct ep93xx_spi *drv_data = 0; ++ struct resource *memory_resource; ++ int irq, status = 0, min_div = 2, max_div = 254*(255+1); ++ ++ ++ /* Check I2SonSSP bit (ssp pins and i2s pins are multiplexed) */ ++ if (readl(EP93XX_SYSCON_DEVICE_CONFIG) & 0x80) ++ return -ENODEV; ++ ++ /* Allocate master with space for drv_data */ ++ master = spi_alloc_master(dev, sizeof(struct ep93xx_spi)); ++ if (!master) { ++ dev_err(&pdev->dev, "can not alloc spi_master\n"); ++ return -ENOMEM; ++ } ++ ++ /* Setup register addresses */ ++ memory_resource = platform_get_resource(pdev, IORESOURCE_MEM, 0); ++ if (!memory_resource) { ++ dev_err(&pdev->dev, "memory resources not defined\n"); ++ status = -ENODEV; ++ goto out_error_master_alloc; ++ } ++ ++ drv_data = spi_master_get_devdata(master); ++ drv_data->master = master; ++ drv_data->ioaddr = (unsigned long *)memory_resource->start; ++ drv_data->clk = clk_get(&pdev->dev, "sspclk"); ++ drv_data->freq_max = clk_get_rate(drv_data->clk) / min_div; ++ drv_data->freq_min = clk_get_rate(drv_data->clk) / max_div + 1; ++ ++ INIT_WORK(&drv_data->work, ssp_work); ++ spin_lock_init(&drv_data->lock); ++ INIT_LIST_HEAD(&drv_data->queue); ++ ++ drv_data->workqueue = create_singlethread_workqueue(master->dev.parent->bus_id); ++ if (!drv_data->workqueue){ ++ status = -EBUSY; ++ goto out_error_master_alloc; ++ } ++ ++ master->bus_num = pdev->id; ++ master->num_chipselect = spi_data->chip_select_num; ++ master->cleanup = ssp_cleanup; ++ master->setup = ssp_setup; ++ master->transfer = ssp_transfer; ++ ++ /* Attach to IRQ */ ++ irq = platform_get_irq(pdev, 0); ++ if (irq < 0) { ++ dev_err(&pdev->dev, "irq resource not defined\n"); ++ status = -ENODEV; ++ goto out_error_master_alloc; ++ } ++ ++ status = request_irq(irq, ssp_int, 0, dev->bus_id, drv_data); ++ if (status < 0) { ++ dev_err(&pdev->dev, "can not get IRQ\n"); ++ goto out_error_master_alloc; ++ } ++ ++ /* Load default SSP configuration */ ++ write_reg(SSP_CONTROL_SSE, drv_data->ioaddr, SSPCR1); ++ write_reg(SPI_DEFAULT0, drv_data->ioaddr, SSPCR0); ++ write_reg(SPI_DEFAULT_DIVISOR, drv_data->ioaddr, SSPCPSR); ++ write_reg(0x00, drv_data->ioaddr, SSPCR1); ++ ++ /* Register with the SPI framework */ ++ platform_set_drvdata(pdev, drv_data); ++ status = spi_register_master(master); ++ if (status != 0) { ++ dev_err(&pdev->dev, "problem registering spi master\n"); ++ goto out_error_irq_alloc; ++ } ++ ++ write_reg(SPI_DEFAULT1, drv_data->ioaddr, SSPCR1); ++ return status; ++ ++out_error_irq_alloc: ++ free_irq(irq, drv_data); ++ ++out_error_master_alloc: ++ spi_master_put(master); ++ return status; ++} ++ ++static int spi_ep93xx_remove(struct platform_device *pdev) ++{ ++ struct ep93xx_spi *drv_data = platform_get_drvdata(pdev); ++ int irq; ++ ++ if (!drv_data) ++ return 0; ++ ++ /* Disable SSP (clear SSE bit) */ ++ write_reg(0x00, drv_data->ioaddr, SSPCR1); ++ ++ /* Release IRQ */ ++ irq = platform_get_irq(pdev, 0); ++ ++ if (irq >= 0) ++ free_irq(irq, drv_data); ++ ++ /* Disconnect from the SPI framework */ ++ spi_unregister_master(drv_data->master); ++ ++ /* Remove the workqueue */ ++ destroy_workqueue(drv_data->workqueue); ++ ++ /* Prevent double remove */ ++ platform_set_drvdata(pdev, NULL); ++ ++ spi_master_put(drv_data->master); ++ ++ return 0; ++} ++ ++static void spi_ep93xx_shutdown(struct platform_device *pdev) ++{ ++ int status = 0; ++ ++ if ((status = spi_ep93xx_remove(pdev)) != 0) ++ dev_err(&pdev->dev, "shutdown failed with %d\n", status); ++} ++ ++static struct platform_driver ep93xx_spi_platform_driver = { ++ .driver = { ++ .name = "ep93xx-spi", ++ .bus = &platform_bus_type, ++ .owner = THIS_MODULE, ++ }, ++ .remove = __exit_p(spi_ep93xx_remove), ++ .shutdown = spi_ep93xx_shutdown, ++}; ++ ++static int __init spi_ep93xx_init(void) ++{ ++ return platform_driver_probe(&ep93xx_spi_platform_driver, spi_ep93xx_probe); ++} ++ ++static void __exit spi_ep93xx_exit(void) ++{ ++ platform_driver_unregister(&ep93xx_spi_platform_driver); ++} ++ ++module_init(spi_ep93xx_init); ++module_exit(spi_ep93xx_exit); ++ ++MODULE_AUTHOR("Matthieu Crapet <mcrapet@gmail.com>"); ++MODULE_DESCRIPTION("EP93xx SPI Controller Driver"); ++MODULE_LICENSE("GPL"); ++MODULE_VERSION("0.21"); +diff --git a/drivers/spi/spi_ep93xx.h b/drivers/spi/spi_ep93xx.h +new file mode 100644 +index 0000000..6fad735 +--- /dev/null ++++ b/drivers/spi/spi_ep93xx.h +@@ -0,0 +1,61 @@ ++/* ++ * EP93xx SPI (simple) include ++ * ++ * (c) Copyright 2008 Matthieu Crapet <mcrapet@gmail.com> ++ * Based on pxa2xx_spi.c by Stephen Street / StreetFire Sound Labs ++ * ++ * 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 ++ * the Free Software Foundation; either version 2 of the License, or ++ * (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ */ ++ ++/* SSP Registers */ ++#define SSPCR0 0x00 /* Control register 0 */ ++#define SSPCR1 0x04 /* Control register 1 */ ++#define SSPDR 0x08 /* Receice FIFO data register (16-bit read) */ ++ /* Transmit FIFO data register (16-bit write) */ ++#define SSPSR 0x0C /* Status register */ ++#define SSPCPSR 0x10 /* Clock prescale register (from 2 to 254, even number) */ ++#define SSPIxR 0x14 /* Interrupt identification register (read) */ ++ /* Interrupt clear register (write) */ ++ ++/* SSP control registers bit fields & masks */ ++#define SSP_CONTROL_SCR(x) (((x) & 0xFF) << 8) /* Serial clock rate = SCLKOUT / CPSDVR / (1+SCR) */ ++#define SSP_CONTROL_SPH (1 << 7) /* SCLKOUT phase (for SPI only) */ ++#define SSP_CONTROL_SPO (1 << 6) /* SCLKOUT polarity (for SPI only) */ ++#define SSP_CONTROL_FRF(x) (((x) & 3) << 4) /* Frame format (0=SPI) */ ++#define SSP_CONTROL_DSS_4BIT_DATA 3 ++#define SSP_CONTROL_DSS_8BIT_DATA 7 ++#define SSP_CONTROL_DSS_15BIT_DATA 14 ++#define SSP_CONTROL_DSS_16BIT_DATA 15 ++#define SSP_CONTROL_DSS_MASK 0xF ++#define SSP_CONTROL_MS (1 << 5) /* 0=master, 1=slave (can be modified when SSE=0) */ ++#define SSP_CONTROL_SSE (1 << 4) /* SSP operation enable (=1), disable (=0) */ ++#define SSP_CONTROL_LBM (1 << 3) /* Loop back mode */ ++#define SSP_CONTROL_RORIE (1 << 2) /* Interrupt enable : overrun condition */ ++#define SSP_CONTROL_TIE (1 << 1) /* Interrupt enable : transmit fifo */ ++#define SSP_CONTROL_RIE (1 << 0) /* Interrupt enable : receive fifo */ ++ ++/* SSP status register (read only) */ ++#define SSP_STATUS_BUSY (1 << 4) /* Busy flag (0: SSP is idle) */ ++#define SSP_STATUS_RFF (1 << 3) /* Receive fifo full ? (1=full) */ ++#define SSP_STATUS_RNE (1 << 2) /* Receive fifo not empty ? (0=empty) */ ++#define SSP_STATUS_TNF (1 << 1) /* Transmit fifo not full ? (0=full) */ ++#define SSP_STATUS_TFE (1 << 0) /* Transmit fifo empty ? (1=empty) */ ++ ++/* SSP SSPIIR/SSPICR register (write 1 to clear interrupt) */ ++#define SSP_SSPIxx_RORIS (1 << 2) /* Receive fifo overrun interrupt status */ ++#define SSP_SSPIxx_TIS (1 << 1) /* Transmit fifo service request interrupt status */ ++#define SSP_SSPIxx_RIS (1 << 0) /* Receive fifo service request interrupt status */ ++ ++/* Default configuration values */ ++#define SPI_DEFAULT0 (SSP_CONTROL_DSS_16BIT_DATA | SSP_CONTROL_FRF(0) | SSP_CONTROL_SCR(0)) ++#define SPI_DEFAULT1 (SSP_CONTROL_SSE | SSP_CONTROL_RORIE) ++#define SPI_DEFAULT_DIVISOR 254 +diff --git a/drivers/spi/tmp124.c b/drivers/spi/tmp124.c +new file mode 100644 +index 0000000..7f7deaf +--- /dev/null ++++ b/drivers/spi/tmp124.c +@@ -0,0 +1,158 @@ ++/* ++ * TMP124 SPI protocol driver ++ * ++ * (c) Copyright 2008 Matthieu Crapet <mcrapet@gmail.com> ++ * Based on tle62x0.c by Ben Dooks, <ben@simtec.co.uk> ++ * ++ * 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 ++ * the Free Software Foundation; either version 2 of the License, or ++ * (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * Note: The chip uses a '3-wire SPI' (miso and mosi are the same pin). ++ */ ++ ++#include <linux/device.h> ++#include <linux/kernel.h> ++#include <linux/spi/spi.h> ++ ++struct tmp124_state { ++ struct spi_device *bus; ++ u8 tx_buff[2]; ++ u8 rx_buff[2]; ++}; ++ ++ ++static inline int tmp124_write_then_read(struct tmp124_state *st) ++{ ++ struct spi_message msg; ++ struct spi_transfer xfer[2] = { ++ { ++ .tx_buf = st->tx_buff, ++ .rx_buf = NULL, ++ .len = 2, ++ .delay_usecs = 1000, ++ }, { ++ .tx_buf = NULL, ++ .rx_buf = st->rx_buff, ++ .len = 2, ++ } ++ }; ++ ++ spi_message_init(&msg); ++ spi_message_add_tail(&xfer[0], &msg); ++ spi_message_add_tail(&xfer[1], &msg); ++ ++ return spi_sync(st->bus, &msg); ++} ++ ++ ++static ssize_t tmp124_temperature_show(struct device *dev, ++ struct device_attribute *attr, char *buf) ++{ ++ struct tmp124_state *st = dev_get_drvdata(dev); ++ int ret; ++ ++ st->tx_buff[0] = 0x80; ++ st->tx_buff[1] = 0x00; ++ ++ ret = tmp124_write_then_read(st); ++ if (ret < 0) { ++ dev_err(&st->bus->dev, "tmp124_write_then_read\n"); ++ ret = 0; ++ } else { ++ signed short v = (st->rx_buff[0] << 8) + st->rx_buff[1]; ++ signed long val; ++ ++ val = v >> 3; ++ ++ /* 2 digit precision (0.0625*100) */ ++ val = (val * 50) / 8; ++ ret = snprintf(buf, PAGE_SIZE, "%ld.%02d\n", val/100, abs(val%100)); ++ } ++ return ret; ++} ++ ++ ++static DEVICE_ATTR(temperature, S_IRUGO, tmp124_temperature_show, NULL); ++ ++ ++static int __devinit tmp124_probe(struct spi_device *spi) ++{ ++ struct tmp124_state *st; ++ int ret; ++ ++ st = kzalloc(sizeof(struct tmp124_state), GFP_KERNEL); ++ if (st == NULL) { ++ dev_err(&spi->dev, "no memory for device state\n"); ++ return -ENOMEM; ++ } ++ ++ /* required config */ ++ spi->bits_per_word = 16; ++ ++ st->bus = spi; ++ ++ ret = spi_setup(spi); ++ if (ret) { ++ dev_err(&spi->dev, "setup device\n"); ++ goto err; ++ } ++ ++ ret = device_create_file(&spi->dev, &dev_attr_temperature); ++ if (ret) { ++ dev_err(&spi->dev, "cannot create temperature attribute\n"); ++ goto err; ++ } ++ ++ spi_set_drvdata(spi, st); ++ return 0; ++ ++err: ++ kfree(st); ++ return ret; ++} ++ ++ ++static int __devexit tmp124_remove(struct spi_device *spi) ++{ ++ struct tmp124_state *st = spi_get_drvdata(spi); ++ ++ device_remove_file(&spi->dev, &dev_attr_temperature); ++ kfree(st); ++ ++ return 0; ++} ++ ++ ++static struct spi_driver tmp124_driver = { ++ .driver = { ++ .name = "tmp124", ++ .owner = THIS_MODULE, ++ }, ++ .probe = tmp124_probe, ++ .remove = __devexit_p(tmp124_remove), ++}; ++ ++static __init int tmp124_init(void) ++{ ++ return spi_register_driver(&tmp124_driver); ++} ++ ++static __exit void tmp124_exit(void) ++{ ++ spi_unregister_driver(&tmp124_driver); ++} ++ ++module_init(tmp124_init); ++module_exit(tmp124_exit); ++ ++MODULE_AUTHOR("Matthieu Crapet <mcrapet@gmail.com>"); ++MODULE_DESCRIPTION("TMP124 SPI Protocol Driver"); ++MODULE_LICENSE("GPL"); ++MODULE_VERSION("0.1"); +-- +1.6.0.4 + |