Creation of Cybook 2416 (actually Gen4) repository

This commit is contained in:
mlt
2009-12-18 17:10:00 +00:00
committed by godzil
commit 76f20f4d40
13791 changed files with 6812321 additions and 0 deletions

231
drivers/spi/Kconfig Normal file
View File

@@ -0,0 +1,231 @@
#
# SPI driver configuration
#
# NOTE: the reason this doesn't show SPI slave support is mostly that
# nobody's needed a slave side API yet. The master-role API is not
# fully appropriate there, so it'd need some thought to do well.
#
menu "SPI support"
config SPI
bool "SPI support"
help
The "Serial Peripheral Interface" is a low level synchronous
protocol. Chips that support SPI can have data transfer rates
up to several tens of Mbit/sec. Chips are addressed with a
controller and a chipselect. Most SPI slaves don't support
dynamic device discovery; some are even write-only or read-only.
SPI is widely used by microcontrollers to talk with sensors,
eeprom and flash memory, codecs and various other controller
chips, analog to digital (and d-to-a) converters, and more.
MMC and SD cards can be accessed using SPI protocol; and for
DataFlash cards used in MMC sockets, SPI must always be used.
SPI is one of a family of similar protocols using a four wire
interface (select, clock, data in, data out) including Microwire
(half duplex), SSP, SSI, and PSP. This driver framework should
work with most such devices and controllers.
config SPI_DEBUG
boolean "Debug support for SPI drivers"
depends on SPI && DEBUG_KERNEL
help
Say "yes" to enable debug messaging (like dev_dbg and pr_debug),
sysfs, and debugfs support in SPI controller and protocol drivers.
#
# MASTER side ... talking to discrete SPI slave chips including microcontrollers
#
config SPI_MASTER
# boolean "SPI Master Support"
boolean
default SPI
help
If your system has an master-capable SPI controller (which
provides the clock and chipselect), you can enable that
controller and the protocol drivers for the SPI slave chips
that are connected.
comment "SPI Master Controller Drivers"
depends on SPI_MASTER
config SPI_ATMEL
tristate "Atmel SPI Controller"
depends on (ARCH_AT91 || AVR32) && SPI_MASTER
help
This selects a driver for the Atmel SPI Controller, present on
many AT32 (AVR32) and AT91 (ARM) chips.
config SPI_BITBANG
tristate "Bitbanging SPI master"
depends on SPI_MASTER && EXPERIMENTAL
help
With a few GPIO pins, your system can bitbang the SPI protocol.
Select this to get SPI support through I/O pins (GPIO, parallel
port, etc). Or, some systems' SPI master controller drivers use
this code to manage the per-word or per-transfer accesses to the
hardware shift registers.
This is library code, and is automatically selected by drivers that
need it. You only need to select this explicitly to support driver
modules that aren't part of this kernel tree.
config SPI_BUTTERFLY
tristate "Parallel port adapter for AVR Butterfly (DEVELOPMENT)"
depends on SPI_MASTER && PARPORT && EXPERIMENTAL
select SPI_BITBANG
help
This uses a custom parallel port cable to connect to an AVR
Butterfly <http://www.atmel.com/products/avr/butterfly>, an
inexpensive battery powered microcontroller evaluation board.
This same cable can be used to flash new firmware.
config SPI_IMX
tristate "Freescale iMX SPI controller"
depends on SPI_MASTER && ARCH_IMX && EXPERIMENTAL
help
This enables using the Freescale iMX SPI controller in master
mode.
config SPI_MPC83xx
tristate "Freescale MPC83xx SPI controller"
depends on SPI_MASTER && PPC_83xx && EXPERIMENTAL
select SPI_BITBANG
help
This enables using the Freescale MPC83xx SPI controller in master
mode.
Note, this driver uniquely supports the SPI controller on the MPC83xx
family of PowerPC processors. The MPC83xx uses a simple set of shift
registers for data (opposed to the CPM based descriptor model).
config SPI_OMAP_UWIRE
tristate "OMAP1 MicroWire"
depends on SPI_MASTER && ARCH_OMAP1
select SPI_BITBANG
help
This hooks up to the MicroWire controller on OMAP1 chips.
config SPI_PXA2XX
tristate "PXA2xx SSP SPI master"
depends on SPI_MASTER && ARCH_PXA && EXPERIMENTAL
help
This enables using a PXA2xx SSP port as a SPI master controller.
The driver can be configured to use any SSP port and additional
documentation can be found a Documentation/spi/pxa2xx.
config SPI_S3C24XX
tristate "Samsung S3C24XX series SPI"
depends on SPI_MASTER && ARCH_S3C2410 && EXPERIMENTAL
help
SPI driver for Samsung S3C24XX series ARM SoCs
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.
config HS_SPI_S3C2443
tristate "S3C2443 High Speed SPI Driver"
depends on SPI && MACH_SMDK2443
help
Say Y here to include support for SPI controller in the
Samsung S3C2443 based System-on-Chip devices.
config HS_SPI_S3C2450
tristate "S3C2450 High Speed SPI Driver"
depends on SPI && MACH_SMDK2450
help
Say Y here to include support for SPI controller in the
Samsung S3C2450 based System-on-Chip devices.
config HS_SPI_S3C2416
tristate "S3C2416 High Speed SPI Driver"
depends on SPI && MACH_SMDK2416
help
Say Y here to include support for SPI controller in the
Samsung S3C2416 based System-on-Chip devices.
config HS_SPI_S3C6400
tristate "S3C6400 High Speed SPI Driver"
depends on SPI && MACH_SMDK6400
help
Say Y here to include support for SPI controller in the
Samsung S3C6400 based System-on-Chip devices.
config HS_SPI_S3C6410
tristate "S3C6410 High Speed SPI Driver"
depends on SPI && (MACH_SMDK6410 )
help
Say Y here to include support for SPI controller in the
Samsung S3C6410 based System-on-Chip devices.
choice
prompt "High Speed SPI Clock Implementations"
depends on HS_SPI_S3C6400 || HS_SPI_S3C2443 || HS_SPI_S3C2450 || HS_SPI_S3C6410 || HS_SPI_S3C2416
config SPICLK_PCLK
bool "PCLK"
depends on HS_SPI_S3C6400 || HS_SPI_S3C2443 || HS_SPI_S3C2450 || HS_SPI_S3C6410 || HS_SPI_S3C2416
help
Say Y here to include support for pclk source.
config SPICLK_EPLL
bool "EPLL"
depends on HS_SPI_S3C6400 || HS_SPI_S3C2443 || HS_SPI_S3C2450 || HS_SPI_S3C6410 || HS_SPI_S3C2416
help
Say Y here to include support for EPLL source.
config SPICLK_USBCLK
bool "USBCLK"
depends on HS_SPI_S3C6400 || HS_SPI_S3C6410
help
Say Y here to include support for USB clock source.
endchoice
config WORD_TRANSIZE
bool "Tansfer size as Word"
depends on HS_SPI_S3C6400 || HS_SPI_S3C6410
help
Say Y here to include support for word transfer size.
#
# Add new SPI master controllers in alphabetical order above this line
#
#
# There are lots of SPI device types, with sensors and memory
# being probably the most widely used ones.
#
comment "SPI Protocol Masters"
depends on SPI_MASTER
config SPI_AT25
tristate "SPI EEPROMs from most vendors"
depends on SPI_MASTER && SYSFS
help
Enable this driver to get read/write support to most SPI EEPROMs,
after you configure the board init code to know about each eeprom
on your target board.
This driver can also be built as a module. If so, the module
will be called at25.
#
# Add new SPI protocol masters in alphabetical order above this line
#
# (slave support would go here)
endmenu # "SPI support"

61
drivers/spi/Makefile Normal file
View File

@@ -0,0 +1,61 @@
#
# Makefile for kernel SPI drivers.
#
ifeq ($(CONFIG_SPI_DEBUG),y)
EXTRA_CFLAGS += -DDEBUG
endif
# small core, mostly translating board-specific
# config declarations into driver model code
obj-$(CONFIG_SPI_MASTER) += spi.o
# SPI master controller drivers (bus)
obj-$(CONFIG_SPI_BITBANG) += spi_bitbang.o
obj-$(CONFIG_SPI_ATMEL) += atmel_spi.o
obj-$(CONFIG_SPI_BUTTERFLY) += spi_butterfly.o
obj-$(CONFIG_SPI_IMX) += spi_imx.o
obj-$(CONFIG_SPI_PXA2XX) += pxa2xx_spi.o
obj-$(CONFIG_SPI_OMAP_UWIRE) += omap_uwire.o
obj-$(CONFIG_SPI_MPC83xx) += spi_mpc83xx.o
# ... add above this line ...
# SPI protocol drivers (device/link on bus)
obj-$(CONFIG_SPI_AT25) += at25.o
# ... add above this line ...
# SPI slave controller drivers (upstream link)
# ... add above this line ...
# SPI slave drivers (protocol for that link)
# ... add above this line ...
ifeq ($(CONFIG_MACH_SMDK2443),y)
obj-$(CONFIG_SPI) += spi-dev.o hspi-s3c2443.o
endif
ifeq ($(CONFIG_MACH_SMDK2450),y)
obj-$(CONFIG_SPI) += spi-dev.o hspi-s3c2443.o
endif
ifeq ($(CONFIG_MACH_SMDK2416),y)
#obj-$(CONFIG_SPI) += spi-dev.o hspi-s3c2443.o
obj-$(CONFIG_HS_SPI_S3C2416) += spi-dev.o hspi-s3c2443.o
endif
ifeq ($(CONFIG_MACH_ASUS2416X),y)
obj-$(CONFIG_SPI) += spi-dev.o hspi-s3c2443.o
endif
ifeq ($(CONFIG_MACH_SMDK6400),y)
obj-$(CONFIG_SPI) += spi-dev.o hspi-s3c6400.o
endif
ifeq ($(CONFIG_MACH_SMDK6410),y)
obj-$(CONFIG_SPI) += spi-dev.o hspi-s3c6400.o
endif
# SPI master controller drivers (bus)
obj-$(CONFIG_SPI_S3C24XX_GPIO) += spi_s3c24xx_gpio.o
obj-$(CONFIG_SPI_S3C24XX) += spi_s3c24xx.o
# ... add above this line ...

381
drivers/spi/at25.c Normal file
View File

@@ -0,0 +1,381 @@
/*
* at25.c -- support most SPI EEPROMs, such as Atmel AT25 models
*
* Copyright (C) 2006 David Brownell
*
* 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.
*/
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/module.h>
#include <linux/slab.h>
#include <linux/delay.h>
#include <linux/device.h>
#include <linux/sched.h>
#include <linux/spi/spi.h>
#include <linux/spi/eeprom.h>
struct at25_data {
struct spi_device *spi;
struct mutex lock;
struct spi_eeprom chip;
struct bin_attribute bin;
unsigned addrlen;
};
#define AT25_WREN 0x06 /* latch the write enable */
#define AT25_WRDI 0x04 /* reset the write enable */
#define AT25_RDSR 0x05 /* read status register */
#define AT25_WRSR 0x01 /* write status register */
#define AT25_READ 0x03 /* read byte(s) */
#define AT25_WRITE 0x02 /* write byte(s)/sector */
#define AT25_SR_nRDY 0x01 /* nRDY = write-in-progress */
#define AT25_SR_WEN 0x02 /* write enable (latched) */
#define AT25_SR_BP0 0x04 /* BP for software writeprotect */
#define AT25_SR_BP1 0x08
#define AT25_SR_WPEN 0x80 /* writeprotect enable */
#define EE_MAXADDRLEN 3 /* 24 bit addresses, up to 2 MBytes */
/* Specs often allow 5 msec for a page write, sometimes 20 msec;
* it's important to recover from write timeouts.
*/
#define EE_TIMEOUT 25
/*-------------------------------------------------------------------------*/
#define io_limit PAGE_SIZE /* bytes */
static ssize_t
at25_ee_read(
struct at25_data *at25,
char *buf,
unsigned offset,
size_t count
)
{
u8 command[EE_MAXADDRLEN + 1];
u8 *cp;
ssize_t status;
struct spi_transfer t[2];
struct spi_message m;
cp = command;
*cp++ = AT25_READ;
/* 8/16/24-bit address is written MSB first */
switch (at25->addrlen) {
default: /* case 3 */
*cp++ = offset >> 16;
case 2:
*cp++ = offset >> 8;
case 1:
case 0: /* can't happen: for better codegen */
*cp++ = offset >> 0;
}
spi_message_init(&m);
memset(t, 0, sizeof t);
t[0].tx_buf = command;
t[0].len = at25->addrlen + 1;
spi_message_add_tail(&t[0], &m);
t[1].rx_buf = buf;
t[1].len = count;
spi_message_add_tail(&t[1], &m);
mutex_lock(&at25->lock);
/* Read it all at once.
*
* REVISIT that's potentially a problem with large chips, if
* other devices on the bus need to be accessed regularly or
* this chip is clocked very slowly
*/
status = spi_sync(at25->spi, &m);
dev_dbg(&at25->spi->dev,
"read %Zd bytes at %d --> %d\n",
count, offset, (int) status);
mutex_unlock(&at25->lock);
return status ? status : count;
}
static ssize_t
at25_bin_read(struct kobject *kobj, char *buf, loff_t off, size_t count)
{
struct device *dev;
struct at25_data *at25;
dev = container_of(kobj, struct device, kobj);
at25 = dev_get_drvdata(dev);
if (unlikely(off >= at25->bin.size))
return 0;
if ((off + count) > at25->bin.size)
count = at25->bin.size - off;
if (unlikely(!count))
return count;
return at25_ee_read(at25, buf, off, count);
}
static ssize_t
at25_ee_write(struct at25_data *at25, char *buf, loff_t off, size_t count)
{
ssize_t status = 0;
unsigned written = 0;
unsigned buf_size;
u8 *bounce;
/* Temp buffer starts with command and address */
buf_size = at25->chip.page_size;
if (buf_size > io_limit)
buf_size = io_limit;
bounce = kmalloc(buf_size + at25->addrlen + 1, GFP_KERNEL);
if (!bounce)
return -ENOMEM;
/* For write, rollover is within the page ... so we write at
* most one page, then manually roll over to the next page.
*/
bounce[0] = AT25_WRITE;
mutex_lock(&at25->lock);
do {
unsigned long timeout, retries;
unsigned segment;
unsigned offset = (unsigned) off;
u8 *cp = bounce + 1;
*cp = AT25_WREN;
status = spi_write(at25->spi, cp, 1);
if (status < 0) {
dev_dbg(&at25->spi->dev, "WREN --> %d\n",
(int) status);
break;
}
/* 8/16/24-bit address is written MSB first */
switch (at25->addrlen) {
default: /* case 3 */
*cp++ = offset >> 16;
case 2:
*cp++ = offset >> 8;
case 1:
case 0: /* can't happen: for better codegen */
*cp++ = offset >> 0;
}
/* Write as much of a page as we can */
segment = buf_size - (offset % buf_size);
if (segment > count)
segment = count;
memcpy(cp, buf, segment);
status = spi_write(at25->spi, bounce,
segment + at25->addrlen + 1);
dev_dbg(&at25->spi->dev,
"write %u bytes at %u --> %d\n",
segment, offset, (int) status);
if (status < 0)
break;
/* REVISIT this should detect (or prevent) failed writes
* to readonly sections of the EEPROM...
*/
/* Wait for non-busy status */
timeout = jiffies + msecs_to_jiffies(EE_TIMEOUT);
retries = 0;
do {
int sr;
sr = spi_w8r8(at25->spi, AT25_RDSR);
if (sr < 0 || (sr & AT25_SR_nRDY)) {
dev_dbg(&at25->spi->dev,
"rdsr --> %d (%02x)\n", sr, sr);
/* at HZ=100, this is sloooow */
msleep(1);
continue;
}
if (!(sr & AT25_SR_nRDY))
break;
} while (retries++ < 3 || time_before_eq(jiffies, timeout));
if (time_after(jiffies, timeout)) {
dev_err(&at25->spi->dev,
"write %d bytes offset %d, "
"timeout after %u msecs\n",
segment, offset,
jiffies_to_msecs(jiffies -
(timeout - EE_TIMEOUT)));
status = -ETIMEDOUT;
break;
}
off += segment;
buf += segment;
count -= segment;
written += segment;
} while (count > 0);
mutex_unlock(&at25->lock);
kfree(bounce);
return written ? written : status;
}
static ssize_t
at25_bin_write(struct kobject *kobj, char *buf, loff_t off, size_t count)
{
struct device *dev;
struct at25_data *at25;
dev = container_of(kobj, struct device, kobj);
at25 = dev_get_drvdata(dev);
if (unlikely(off >= at25->bin.size))
return -EFBIG;
if ((off + count) > at25->bin.size)
count = at25->bin.size - off;
if (unlikely(!count))
return count;
return at25_ee_write(at25, buf, off, count);
}
/*-------------------------------------------------------------------------*/
static int at25_probe(struct spi_device *spi)
{
struct at25_data *at25 = NULL;
const struct spi_eeprom *chip;
int err;
int sr;
int addrlen;
/* Chip description */
chip = spi->dev.platform_data;
if (!chip) {
dev_dbg(&spi->dev, "no chip description\n");
err = -ENODEV;
goto fail;
}
/* For now we only support 8/16/24 bit addressing */
if (chip->flags & EE_ADDR1)
addrlen = 1;
else if (chip->flags & EE_ADDR2)
addrlen = 2;
else if (chip->flags & EE_ADDR3)
addrlen = 3;
else {
dev_dbg(&spi->dev, "unsupported address type\n");
err = -EINVAL;
goto fail;
}
/* Ping the chip ... the status register is pretty portable,
* unlike probing manufacturer IDs. We do expect that system
* firmware didn't write it in the past few milliseconds!
*/
sr = spi_w8r8(spi, AT25_RDSR);
if (sr < 0 || sr & AT25_SR_nRDY) {
dev_dbg(&spi->dev, "rdsr --> %d (%02x)\n", sr, sr);
err = -ENXIO;
goto fail;
}
if (!(at25 = kzalloc(sizeof *at25, GFP_KERNEL))) {
err = -ENOMEM;
goto fail;
}
mutex_init(&at25->lock);
at25->chip = *chip;
at25->spi = spi_dev_get(spi);
dev_set_drvdata(&spi->dev, at25);
at25->addrlen = addrlen;
/* Export the EEPROM bytes through sysfs, since that's convenient.
* Default to root-only access to the data; EEPROMs often hold data
* that's sensitive for read and/or write, like ethernet addresses,
* security codes, board-specific manufacturing calibrations, etc.
*/
at25->bin.attr.name = "eeprom";
at25->bin.attr.mode = S_IRUSR;
at25->bin.attr.owner = THIS_MODULE;
at25->bin.read = at25_bin_read;
at25->bin.size = at25->chip.byte_len;
if (!(chip->flags & EE_READONLY)) {
at25->bin.write = at25_bin_write;
at25->bin.attr.mode |= S_IWUSR;
}
err = sysfs_create_bin_file(&spi->dev.kobj, &at25->bin);
if (err)
goto fail;
dev_info(&spi->dev, "%Zd %s %s eeprom%s, pagesize %u\n",
(at25->bin.size < 1024)
? at25->bin.size
: (at25->bin.size / 1024),
(at25->bin.size < 1024) ? "Byte" : "KByte",
at25->chip.name,
(chip->flags & EE_READONLY) ? " (readonly)" : "",
at25->chip.page_size);
return 0;
fail:
dev_dbg(&spi->dev, "probe err %d\n", err);
kfree(at25);
return err;
}
static int __devexit at25_remove(struct spi_device *spi)
{
struct at25_data *at25;
at25 = dev_get_drvdata(&spi->dev);
sysfs_remove_bin_file(&spi->dev.kobj, &at25->bin);
kfree(at25);
return 0;
}
/*-------------------------------------------------------------------------*/
static struct spi_driver at25_driver = {
.driver = {
.name = "at25",
.owner = THIS_MODULE,
},
.probe = at25_probe,
.remove = __devexit_p(at25_remove),
};
static int __init at25_init(void)
{
return spi_register_driver(&at25_driver);
}
module_init(at25_init);
static void __exit at25_exit(void)
{
spi_unregister_driver(&at25_driver);
}
module_exit(at25_exit);
MODULE_DESCRIPTION("Driver for most SPI EEPROMs");
MODULE_AUTHOR("David Brownell");
MODULE_LICENSE("GPL");

682
drivers/spi/atmel_spi.c Normal file
View File

@@ -0,0 +1,682 @@
/*
* Driver for Atmel AT32 and AT91 SPI Controllers
*
* Copyright (C) 2006 Atmel Corporation
*
* 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/init.h>
#include <linux/clk.h>
#include <linux/module.h>
#include <linux/platform_device.h>
#include <linux/delay.h>
#include <linux/dma-mapping.h>
#include <linux/err.h>
#include <linux/interrupt.h>
#include <linux/spi/spi.h>
#include <asm/io.h>
#include <asm/arch/board.h>
#include <asm/arch/gpio.h>
#ifdef CONFIG_ARCH_AT91
#include <asm/arch/cpu.h>
#endif
#include "atmel_spi.h"
/*
* The core SPI transfer engine just talks to a register bank to set up
* DMA transfers; transfer queue progress is driven by IRQs. The clock
* framework provides the base clock, subdivided for each spi_device.
*
* Newer controllers, marked with "new_1" flag, have:
* - CR.LASTXFER
* - SPI_MR.DIV32 may become FDIV or must-be-zero (here: always zero)
* - SPI_SR.TXEMPTY, SPI_SR.NSSR (and corresponding irqs)
* - SPI_CSRx.CSAAT
* - SPI_CSRx.SBCR allows faster clocking
*/
struct atmel_spi {
spinlock_t lock;
void __iomem *regs;
int irq;
struct clk *clk;
struct platform_device *pdev;
unsigned new_1:1;
u8 stopping;
struct list_head queue;
struct spi_transfer *current_transfer;
unsigned long remaining_bytes;
void *buffer;
dma_addr_t buffer_dma;
};
#define BUFFER_SIZE PAGE_SIZE
#define INVALID_DMA_ADDRESS 0xffffffff
/*
* Earlier SPI controllers (e.g. on at91rm9200) have a design bug whereby
* they assume that spi slave device state will not change on deselect, so
* that automagic deselection is OK. Not so! Workaround uses nCSx pins
* as GPIOs; or newer controllers have CSAAT and friends.
*
* Since the CSAAT functionality is a bit weird on newer controllers
* as well, we use GPIO to control nCSx pins on all controllers.
*/
static inline void cs_activate(struct spi_device *spi)
{
unsigned gpio = (unsigned) spi->controller_data;
unsigned active = spi->mode & SPI_CS_HIGH;
dev_dbg(&spi->dev, "activate %u%s\n", gpio, active ? " (high)" : "");
gpio_set_value(gpio, active);
}
static inline void cs_deactivate(struct spi_device *spi)
{
unsigned gpio = (unsigned) spi->controller_data;
unsigned active = spi->mode & SPI_CS_HIGH;
dev_dbg(&spi->dev, "DEactivate %u%s\n", gpio, active ? " (low)" : "");
gpio_set_value(gpio, !active);
}
/*
* Submit next transfer for DMA.
* lock is held, spi irq is blocked
*/
static void atmel_spi_next_xfer(struct spi_master *master,
struct spi_message *msg)
{
struct atmel_spi *as = spi_master_get_devdata(master);
struct spi_transfer *xfer;
u32 len;
dma_addr_t tx_dma, rx_dma;
xfer = as->current_transfer;
if (!xfer || as->remaining_bytes == 0) {
if (xfer)
xfer = list_entry(xfer->transfer_list.next,
struct spi_transfer, transfer_list);
else
xfer = list_entry(msg->transfers.next,
struct spi_transfer, transfer_list);
as->remaining_bytes = xfer->len;
as->current_transfer = xfer;
}
len = as->remaining_bytes;
tx_dma = xfer->tx_dma;
rx_dma = xfer->rx_dma;
/* use scratch buffer only when rx or tx data is unspecified */
if (rx_dma == INVALID_DMA_ADDRESS) {
rx_dma = as->buffer_dma;
if (len > BUFFER_SIZE)
len = BUFFER_SIZE;
}
if (tx_dma == INVALID_DMA_ADDRESS) {
tx_dma = as->buffer_dma;
if (len > BUFFER_SIZE)
len = BUFFER_SIZE;
memset(as->buffer, 0, len);
dma_sync_single_for_device(&as->pdev->dev,
as->buffer_dma, len, DMA_TO_DEVICE);
}
spi_writel(as, RPR, rx_dma);
spi_writel(as, TPR, tx_dma);
as->remaining_bytes -= len;
if (msg->spi->bits_per_word > 8)
len >>= 1;
/* REVISIT: when xfer->delay_usecs == 0, the PDC "next transfer"
* mechanism might help avoid the IRQ latency between transfers
*
* We're also waiting for ENDRX before we start the next
* transfer because we need to handle some difficult timing
* issues otherwise. If we wait for ENDTX in one transfer and
* then starts waiting for ENDRX in the next, it's difficult
* to tell the difference between the ENDRX interrupt we're
* actually waiting for and the ENDRX interrupt of the
* previous transfer.
*
* It should be doable, though. Just not now...
*/
spi_writel(as, TNCR, 0);
spi_writel(as, RNCR, 0);
spi_writel(as, IER, SPI_BIT(ENDRX) | SPI_BIT(OVRES));
dev_dbg(&msg->spi->dev,
" start xfer %p: len %u tx %p/%08x rx %p/%08x imr %03x\n",
xfer, xfer->len, xfer->tx_buf, xfer->tx_dma,
xfer->rx_buf, xfer->rx_dma, spi_readl(as, IMR));
spi_writel(as, TCR, len);
spi_writel(as, RCR, len);
spi_writel(as, PTCR, SPI_BIT(TXTEN) | SPI_BIT(RXTEN));
}
static void atmel_spi_next_message(struct spi_master *master)
{
struct atmel_spi *as = spi_master_get_devdata(master);
struct spi_message *msg;
u32 mr;
BUG_ON(as->current_transfer);
msg = list_entry(as->queue.next, struct spi_message, queue);
/* Select the chip */
mr = spi_readl(as, MR);
mr = SPI_BFINS(PCS, ~(1 << msg->spi->chip_select), mr);
spi_writel(as, MR, mr);
cs_activate(msg->spi);
atmel_spi_next_xfer(master, msg);
}
static void
atmel_spi_dma_map_xfer(struct atmel_spi *as, struct spi_transfer *xfer)
{
xfer->tx_dma = xfer->rx_dma = INVALID_DMA_ADDRESS;
if (xfer->tx_buf)
xfer->tx_dma = dma_map_single(&as->pdev->dev,
(void *) xfer->tx_buf, xfer->len,
DMA_TO_DEVICE);
if (xfer->rx_buf)
xfer->rx_dma = dma_map_single(&as->pdev->dev,
xfer->rx_buf, xfer->len,
DMA_FROM_DEVICE);
}
static void atmel_spi_dma_unmap_xfer(struct spi_master *master,
struct spi_transfer *xfer)
{
if (xfer->tx_dma != INVALID_DMA_ADDRESS)
dma_unmap_single(master->cdev.dev, xfer->tx_dma,
xfer->len, DMA_TO_DEVICE);
if (xfer->rx_dma != INVALID_DMA_ADDRESS)
dma_unmap_single(master->cdev.dev, xfer->rx_dma,
xfer->len, DMA_FROM_DEVICE);
}
static void
atmel_spi_msg_done(struct spi_master *master, struct atmel_spi *as,
struct spi_message *msg, int status)
{
cs_deactivate(msg->spi);
list_del(&msg->queue);
msg->status = status;
dev_dbg(master->cdev.dev,
"xfer complete: %u bytes transferred\n",
msg->actual_length);
spin_unlock(&as->lock);
msg->complete(msg->context);
spin_lock(&as->lock);
as->current_transfer = NULL;
/* continue if needed */
if (list_empty(&as->queue) || as->stopping)
spi_writel(as, PTCR, SPI_BIT(RXTDIS) | SPI_BIT(TXTDIS));
else
atmel_spi_next_message(master);
}
static irqreturn_t
atmel_spi_interrupt(int irq, void *dev_id)
{
struct spi_master *master = dev_id;
struct atmel_spi *as = spi_master_get_devdata(master);
struct spi_message *msg;
struct spi_transfer *xfer;
u32 status, pending, imr;
int ret = IRQ_NONE;
spin_lock(&as->lock);
xfer = as->current_transfer;
msg = list_entry(as->queue.next, struct spi_message, queue);
imr = spi_readl(as, IMR);
status = spi_readl(as, SR);
pending = status & imr;
if (pending & SPI_BIT(OVRES)) {
int timeout;
ret = IRQ_HANDLED;
spi_writel(as, IDR, (SPI_BIT(ENDTX) | SPI_BIT(ENDRX)
| SPI_BIT(OVRES)));
/*
* When we get an overrun, we disregard the current
* transfer. Data will not be copied back from any
* bounce buffer and msg->actual_len will not be
* updated with the last xfer.
*
* We will also not process any remaning transfers in
* the message.
*
* First, stop the transfer and unmap the DMA buffers.
*/
spi_writel(as, PTCR, SPI_BIT(RXTDIS) | SPI_BIT(TXTDIS));
if (!msg->is_dma_mapped)
atmel_spi_dma_unmap_xfer(master, xfer);
/* REVISIT: udelay in irq is unfriendly */
if (xfer->delay_usecs)
udelay(xfer->delay_usecs);
dev_warn(master->cdev.dev, "fifo overrun (%u/%u remaining)\n",
spi_readl(as, TCR), spi_readl(as, RCR));
/*
* Clean up DMA registers and make sure the data
* registers are empty.
*/
spi_writel(as, RNCR, 0);
spi_writel(as, TNCR, 0);
spi_writel(as, RCR, 0);
spi_writel(as, TCR, 0);
for (timeout = 1000; timeout; timeout--)
if (spi_readl(as, SR) & SPI_BIT(TXEMPTY))
break;
if (!timeout)
dev_warn(master->cdev.dev,
"timeout waiting for TXEMPTY");
while (spi_readl(as, SR) & SPI_BIT(RDRF))
spi_readl(as, RDR);
/* Clear any overrun happening while cleaning up */
spi_readl(as, SR);
atmel_spi_msg_done(master, as, msg, -EIO);
} else if (pending & SPI_BIT(ENDRX)) {
ret = IRQ_HANDLED;
spi_writel(as, IDR, pending);
if (as->remaining_bytes == 0) {
msg->actual_length += xfer->len;
if (!msg->is_dma_mapped)
atmel_spi_dma_unmap_xfer(master, xfer);
/* REVISIT: udelay in irq is unfriendly */
if (xfer->delay_usecs)
udelay(xfer->delay_usecs);
if (msg->transfers.prev == &xfer->transfer_list) {
/* report completed message */
atmel_spi_msg_done(master, as, msg, 0);
} else {
if (xfer->cs_change) {
cs_deactivate(msg->spi);
udelay(1);
cs_activate(msg->spi);
}
/*
* Not done yet. Submit the next transfer.
*
* FIXME handle protocol options for xfer
*/
atmel_spi_next_xfer(master, msg);
}
} else {
/*
* Keep going, we still have data to send in
* the current transfer.
*/
atmel_spi_next_xfer(master, msg);
}
}
spin_unlock(&as->lock);
return ret;
}
#define MODEBITS (SPI_CPOL | SPI_CPHA | SPI_CS_HIGH)
static int atmel_spi_setup(struct spi_device *spi)
{
struct atmel_spi *as;
u32 scbr, csr;
unsigned int bits = spi->bits_per_word;
unsigned long bus_hz, sck_hz;
unsigned int npcs_pin;
int ret;
as = spi_master_get_devdata(spi->master);
if (as->stopping)
return -ESHUTDOWN;
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 (bits == 0)
bits = 8;
if (bits < 8 || bits > 16) {
dev_dbg(&spi->dev,
"setup: invalid bits_per_word %u (8 to 16)\n",
bits);
return -EINVAL;
}
if (spi->mode & ~MODEBITS) {
dev_dbg(&spi->dev, "setup: unsupported mode bits %x\n",
spi->mode & ~MODEBITS);
return -EINVAL;
}
/* speed zero convention is used by some upper layers */
bus_hz = clk_get_rate(as->clk);
if (spi->max_speed_hz) {
/* assume div32/fdiv/mbz == 0 */
if (!as->new_1)
bus_hz /= 2;
scbr = ((bus_hz + spi->max_speed_hz - 1)
/ spi->max_speed_hz);
if (scbr >= (1 << SPI_SCBR_SIZE)) {
dev_dbg(&spi->dev, "setup: %d Hz too slow, scbr %u\n",
spi->max_speed_hz, scbr);
return -EINVAL;
}
} else
scbr = 0xff;
sck_hz = bus_hz / scbr;
csr = SPI_BF(SCBR, scbr) | SPI_BF(BITS, bits - 8);
if (spi->mode & SPI_CPOL)
csr |= SPI_BIT(CPOL);
if (!(spi->mode & SPI_CPHA))
csr |= SPI_BIT(NCPHA);
/* TODO: DLYBS and DLYBCT */
csr |= SPI_BF(DLYBS, 10);
csr |= SPI_BF(DLYBCT, 10);
/* chipselect must have been muxed as GPIO (e.g. in board setup) */
npcs_pin = (unsigned int)spi->controller_data;
if (!spi->controller_state) {
ret = gpio_request(npcs_pin, "spi_npcs");
if (ret)
return ret;
spi->controller_state = (void *)npcs_pin;
gpio_direction_output(npcs_pin, !(spi->mode & SPI_CS_HIGH));
}
dev_dbg(&spi->dev,
"setup: %lu Hz bpw %u mode 0x%x -> csr%d %08x\n",
sck_hz, bits, spi->mode, spi->chip_select, csr);
spi_writel(as, CSR0 + 4 * spi->chip_select, csr);
return 0;
}
static int atmel_spi_transfer(struct spi_device *spi, struct spi_message *msg)
{
struct atmel_spi *as;
struct spi_transfer *xfer;
unsigned long flags;
struct device *controller = spi->master->cdev.dev;
as = spi_master_get_devdata(spi->master);
dev_dbg(controller, "new message %p submitted for %s\n",
msg, spi->dev.bus_id);
if (unlikely(list_empty(&msg->transfers)
|| !spi->max_speed_hz))
return -EINVAL;
if (as->stopping)
return -ESHUTDOWN;
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");
return -EINVAL;
}
/* FIXME implement these protocol options!! */
if (xfer->bits_per_word || xfer->speed_hz) {
dev_dbg(&spi->dev, "no protocol options yet\n");
return -ENOPROTOOPT;
}
}
/* scrub dcache "early" */
if (!msg->is_dma_mapped) {
list_for_each_entry(xfer, &msg->transfers, transfer_list)
atmel_spi_dma_map_xfer(as, xfer);
}
list_for_each_entry(xfer, &msg->transfers, transfer_list) {
dev_dbg(controller,
" xfer %p: len %u tx %p/%08x rx %p/%08x\n",
xfer, xfer->len,
xfer->tx_buf, xfer->tx_dma,
xfer->rx_buf, xfer->rx_dma);
}
msg->status = -EINPROGRESS;
msg->actual_length = 0;
spin_lock_irqsave(&as->lock, flags);
list_add_tail(&msg->queue, &as->queue);
if (!as->current_transfer)
atmel_spi_next_message(spi->master);
spin_unlock_irqrestore(&as->lock, flags);
return 0;
}
static void atmel_spi_cleanup(struct spi_device *spi)
{
if (spi->controller_state)
gpio_free((unsigned int)spi->controller_data);
}
/*-------------------------------------------------------------------------*/
static int __init atmel_spi_probe(struct platform_device *pdev)
{
struct resource *regs;
int irq;
struct clk *clk;
int ret;
struct spi_master *master;
struct atmel_spi *as;
regs = platform_get_resource(pdev, IORESOURCE_MEM, 0);
if (!regs)
return -ENXIO;
irq = platform_get_irq(pdev, 0);
if (irq < 0)
return irq;
clk = clk_get(&pdev->dev, "spi_clk");
if (IS_ERR(clk))
return PTR_ERR(clk);
/* setup spi core then atmel-specific driver state */
ret = -ENOMEM;
master = spi_alloc_master(&pdev->dev, sizeof *as);
if (!master)
goto out_free;
master->bus_num = pdev->id;
master->num_chipselect = 4;
master->setup = atmel_spi_setup;
master->transfer = atmel_spi_transfer;
master->cleanup = atmel_spi_cleanup;
platform_set_drvdata(pdev, master);
as = spi_master_get_devdata(master);
as->buffer = dma_alloc_coherent(&pdev->dev, BUFFER_SIZE,
&as->buffer_dma, GFP_KERNEL);
if (!as->buffer)
goto out_free;
spin_lock_init(&as->lock);
INIT_LIST_HEAD(&as->queue);
as->pdev = pdev;
as->regs = ioremap(regs->start, (regs->end - regs->start) + 1);
if (!as->regs)
goto out_free_buffer;
as->irq = irq;
as->clk = clk;
#ifdef CONFIG_ARCH_AT91
if (!cpu_is_at91rm9200())
as->new_1 = 1;
#endif
ret = request_irq(irq, atmel_spi_interrupt, 0,
pdev->dev.bus_id, master);
if (ret)
goto out_unmap_regs;
/* Initialize the hardware */
clk_enable(clk);
spi_writel(as, CR, SPI_BIT(SWRST));
spi_writel(as, MR, SPI_BIT(MSTR) | SPI_BIT(MODFDIS));
spi_writel(as, PTCR, SPI_BIT(RXTDIS) | SPI_BIT(TXTDIS));
spi_writel(as, CR, SPI_BIT(SPIEN));
/* go! */
dev_info(&pdev->dev, "Atmel SPI Controller at 0x%08lx (irq %d)\n",
(unsigned long)regs->start, irq);
ret = spi_register_master(master);
if (ret)
goto out_reset_hw;
return 0;
out_reset_hw:
spi_writel(as, CR, SPI_BIT(SWRST));
clk_disable(clk);
free_irq(irq, master);
out_unmap_regs:
iounmap(as->regs);
out_free_buffer:
dma_free_coherent(&pdev->dev, BUFFER_SIZE, as->buffer,
as->buffer_dma);
out_free:
clk_put(clk);
spi_master_put(master);
return ret;
}
static int __exit atmel_spi_remove(struct platform_device *pdev)
{
struct spi_master *master = platform_get_drvdata(pdev);
struct atmel_spi *as = spi_master_get_devdata(master);
struct spi_message *msg;
/* reset the hardware and block queue progress */
spin_lock_irq(&as->lock);
as->stopping = 1;
spi_writel(as, CR, SPI_BIT(SWRST));
spi_readl(as, SR);
spin_unlock_irq(&as->lock);
/* Terminate remaining queued transfers */
list_for_each_entry(msg, &as->queue, queue) {
/* REVISIT unmapping the dma is a NOP on ARM and AVR32
* but we shouldn't depend on that...
*/
msg->status = -ESHUTDOWN;
msg->complete(msg->context);
}
dma_free_coherent(&pdev->dev, BUFFER_SIZE, as->buffer,
as->buffer_dma);
clk_disable(as->clk);
clk_put(as->clk);
free_irq(as->irq, master);
iounmap(as->regs);
spi_unregister_master(master);
return 0;
}
#ifdef CONFIG_PM
static int atmel_spi_suspend(struct platform_device *pdev, pm_message_t mesg)
{
struct spi_master *master = platform_get_drvdata(pdev);
struct atmel_spi *as = spi_master_get_devdata(master);
clk_disable(as->clk);
return 0;
}
static int atmel_spi_resume(struct platform_device *pdev)
{
struct spi_master *master = platform_get_drvdata(pdev);
struct atmel_spi *as = spi_master_get_devdata(master);
clk_enable(as->clk);
return 0;
}
#else
#define atmel_spi_suspend NULL
#define atmel_spi_resume NULL
#endif
static struct platform_driver atmel_spi_driver = {
.driver = {
.name = "atmel_spi",
.owner = THIS_MODULE,
},
.suspend = atmel_spi_suspend,
.resume = atmel_spi_resume,
.remove = __exit_p(atmel_spi_remove),
};
static int __init atmel_spi_init(void)
{
return platform_driver_probe(&atmel_spi_driver, atmel_spi_probe);
}
module_init(atmel_spi_init);
static void __exit atmel_spi_exit(void)
{
platform_driver_unregister(&atmel_spi_driver);
}
module_exit(atmel_spi_exit);
MODULE_DESCRIPTION("Atmel AT32/AT91 SPI Controller driver");
MODULE_AUTHOR("Haavard Skinnemoen <hskinnemoen@atmel.com>");
MODULE_LICENSE("GPL");

167
drivers/spi/atmel_spi.h Normal file
View File

@@ -0,0 +1,167 @@
/*
* Register definitions for Atmel Serial Peripheral Interface (SPI)
*
* Copyright (C) 2006 Atmel Corporation
*
* 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 __ATMEL_SPI_H__
#define __ATMEL_SPI_H__
/* SPI register offsets */
#define SPI_CR 0x0000
#define SPI_MR 0x0004
#define SPI_RDR 0x0008
#define SPI_TDR 0x000c
#define SPI_SR 0x0010
#define SPI_IER 0x0014
#define SPI_IDR 0x0018
#define SPI_IMR 0x001c
#define SPI_CSR0 0x0030
#define SPI_CSR1 0x0034
#define SPI_CSR2 0x0038
#define SPI_CSR3 0x003c
#define SPI_RPR 0x0100
#define SPI_RCR 0x0104
#define SPI_TPR 0x0108
#define SPI_TCR 0x010c
#define SPI_RNPR 0x0110
#define SPI_RNCR 0x0114
#define SPI_TNPR 0x0118
#define SPI_TNCR 0x011c
#define SPI_PTCR 0x0120
#define SPI_PTSR 0x0124
/* Bitfields in CR */
#define SPI_SPIEN_OFFSET 0
#define SPI_SPIEN_SIZE 1
#define SPI_SPIDIS_OFFSET 1
#define SPI_SPIDIS_SIZE 1
#define SPI_SWRST_OFFSET 7
#define SPI_SWRST_SIZE 1
#define SPI_LASTXFER_OFFSET 24
#define SPI_LASTXFER_SIZE 1
/* Bitfields in MR */
#define SPI_MSTR_OFFSET 0
#define SPI_MSTR_SIZE 1
#define SPI_PS_OFFSET 1
#define SPI_PS_SIZE 1
#define SPI_PCSDEC_OFFSET 2
#define SPI_PCSDEC_SIZE 1
#define SPI_FDIV_OFFSET 3
#define SPI_FDIV_SIZE 1
#define SPI_MODFDIS_OFFSET 4
#define SPI_MODFDIS_SIZE 1
#define SPI_LLB_OFFSET 7
#define SPI_LLB_SIZE 1
#define SPI_PCS_OFFSET 16
#define SPI_PCS_SIZE 4
#define SPI_DLYBCS_OFFSET 24
#define SPI_DLYBCS_SIZE 8
/* Bitfields in RDR */
#define SPI_RD_OFFSET 0
#define SPI_RD_SIZE 16
/* Bitfields in TDR */
#define SPI_TD_OFFSET 0
#define SPI_TD_SIZE 16
/* Bitfields in SR */
#define SPI_RDRF_OFFSET 0
#define SPI_RDRF_SIZE 1
#define SPI_TDRE_OFFSET 1
#define SPI_TDRE_SIZE 1
#define SPI_MODF_OFFSET 2
#define SPI_MODF_SIZE 1
#define SPI_OVRES_OFFSET 3
#define SPI_OVRES_SIZE 1
#define SPI_ENDRX_OFFSET 4
#define SPI_ENDRX_SIZE 1
#define SPI_ENDTX_OFFSET 5
#define SPI_ENDTX_SIZE 1
#define SPI_RXBUFF_OFFSET 6
#define SPI_RXBUFF_SIZE 1
#define SPI_TXBUFE_OFFSET 7
#define SPI_TXBUFE_SIZE 1
#define SPI_NSSR_OFFSET 8
#define SPI_NSSR_SIZE 1
#define SPI_TXEMPTY_OFFSET 9
#define SPI_TXEMPTY_SIZE 1
#define SPI_SPIENS_OFFSET 16
#define SPI_SPIENS_SIZE 1
/* Bitfields in CSR0 */
#define SPI_CPOL_OFFSET 0
#define SPI_CPOL_SIZE 1
#define SPI_NCPHA_OFFSET 1
#define SPI_NCPHA_SIZE 1
#define SPI_CSAAT_OFFSET 3
#define SPI_CSAAT_SIZE 1
#define SPI_BITS_OFFSET 4
#define SPI_BITS_SIZE 4
#define SPI_SCBR_OFFSET 8
#define SPI_SCBR_SIZE 8
#define SPI_DLYBS_OFFSET 16
#define SPI_DLYBS_SIZE 8
#define SPI_DLYBCT_OFFSET 24
#define SPI_DLYBCT_SIZE 8
/* Bitfields in RCR */
#define SPI_RXCTR_OFFSET 0
#define SPI_RXCTR_SIZE 16
/* Bitfields in TCR */
#define SPI_TXCTR_OFFSET 0
#define SPI_TXCTR_SIZE 16
/* Bitfields in RNCR */
#define SPI_RXNCR_OFFSET 0
#define SPI_RXNCR_SIZE 16
/* Bitfields in TNCR */
#define SPI_TXNCR_OFFSET 0
#define SPI_TXNCR_SIZE 16
/* Bitfields in PTCR */
#define SPI_RXTEN_OFFSET 0
#define SPI_RXTEN_SIZE 1
#define SPI_RXTDIS_OFFSET 1
#define SPI_RXTDIS_SIZE 1
#define SPI_TXTEN_OFFSET 8
#define SPI_TXTEN_SIZE 1
#define SPI_TXTDIS_OFFSET 9
#define SPI_TXTDIS_SIZE 1
/* Constants for BITS */
#define SPI_BITS_8_BPT 0
#define SPI_BITS_9_BPT 1
#define SPI_BITS_10_BPT 2
#define SPI_BITS_11_BPT 3
#define SPI_BITS_12_BPT 4
#define SPI_BITS_13_BPT 5
#define SPI_BITS_14_BPT 6
#define SPI_BITS_15_BPT 7
#define SPI_BITS_16_BPT 8
/* Bit manipulation macros */
#define SPI_BIT(name) \
(1 << SPI_##name##_OFFSET)
#define SPI_BF(name,value) \
(((value) & ((1 << SPI_##name##_SIZE) - 1)) << SPI_##name##_OFFSET)
#define SPI_BFEXT(name,value) \
(((value) >> SPI_##name##_OFFSET) & ((1 << SPI_##name##_SIZE) - 1))
#define SPI_BFINS(name,value,old) \
( ((old) & ~(((1 << SPI_##name##_SIZE) - 1) << SPI_##name##_OFFSET)) \
| SPI_BF(name,value))
/* Register access macros */
#define spi_readl(port,reg) \
__raw_readl((port)->regs + SPI_##reg)
#define spi_writel(port,reg,value) \
__raw_writel((value), (port)->regs + SPI_##reg)
#endif /* __ATMEL_SPI_H__ */

1313
drivers/spi/hspi-s3c2443.c Normal file

File diff suppressed because it is too large Load Diff

113
drivers/spi/hspi-s3c2443.h Normal file
View File

@@ -0,0 +1,113 @@
/* ------------------------------------------------------------------------- */
/* */
/* spi-s3c6400.h - definitions of s3c6400 specific spi interface */
/* */
/* ------------------------------------------------------------------------- */
/* Copyright (C) 2006 Samsung Electronics Co. ltd.
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.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
/* ------------------------------------------------------------------------- */
#ifndef _S3C2443_SPI_H
#define _S3C2443_SPI_H
#include <asm/dma.h>
#include <asm/arch/dma.h>
#define S3C_DCON_HANDSHAKE (1<<31)
#define S3C_DCON_SYNC_PCLK (0<<30)
//#define SPI_CHANNEL 1
#if(SPI_CHANNEL==0)
/* SPI CHANNEL 0 */
#define S3C_SPI_TX_DATA_REG 0x52000018 //SPI TX data
#define S3C_SPI_RX_DATA_REG 0x5200001C //SPI RX data
#else
/* SPI CHANNEL 1 */
#define S3C_SPI_TX_DATA_REG 0x59000018 //SPI TX data
#define S3C_SPI_RX_DATA_REG 0x5900001C //SPI RX data
#endif
/* DMA channel to be used for the SPI interface. */
#define S3C_SPI_DMA 0
/* DMA transfer unit (byte). */
#define S3C_DMA_XFER_BYTE 1
#define S3C_DMA_XFER_WORD 4
/* DMA configuration setup byte. */
#define S3C_DCON_SPI1 (S3C_DCON_HANDSHAKE | S3C_DCON_SYNC_PCLK)
/* DMA hardware configuration mode (DISRCC register). */
#define S3C_SPI1_DMA_HWCFG 3
#define S3C_SPI_DMA_HWCFG 3
#define DMA_BUFFER_SIZE 1500
/* spi controller state */
int req_dma_flag = 1;
enum s3c_spi_state {
STATE_IDLE,
STATE_XFER_TX,
STATE_XFER_RX,
STATE_STOP
};
/* vivek, 2009-05-14 11:01 Notes: define clients for dma read and write channels*/
//static struct s3c2410_dma_client s3c2443spi_dma_client = {
// .name = "s3c2443-spi-dma",
//};
static struct s3c2410_dma_client s3c2443spi_dma_clientw = {
.name = "s3c2443-spi-dmaw",
};
static struct s3c2410_dma_client s3c2443spi_dma_clientr = {
.name = "s3c2443-spi-dmar",
};
struct s3c_spi {
spinlock_t lock;
struct semaphore sem;
int nr;
int dma;
/* vivek, 2009-05-14 10:45 Notes: define dmar and dmaw for both write and read channels*/
int dmar;
int dmaw;
u_int subchannel;/* user fragment index */
dma_addr_t dmabuf_addr;
struct spi_msg *msg;
unsigned int msg_num;
unsigned int msg_idx;
unsigned int msg_ptr;
unsigned int msg_rd_ptr;
enum s3c_spi_state state;
void __iomem *regs;
struct clk *clk;
struct device *dev;
struct resource *irq;
struct resource *ioarea;
struct spi_dev spidev;
/* GeorgeKuo: */
struct completion comp;
};
#endif /* _S3C6400_SPI_H */

784
drivers/spi/hspi-s3c6400.c Normal file
View File

@@ -0,0 +1,784 @@
/* spi-s3c6400.c
*
* Copyright (C) 2006 Samsung Electronics Co. Ltd.
*
* S3C6400 SPI Controller
*
* 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.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/init.h>
#include <linux/time.h>
#include <linux/interrupt.h>
#include <linux/sched.h>
#include <linux/delay.h>
#include <linux/errno.h>
#include <linux/err.h>
#include <linux/device.h>
#include <linux/platform_device.h>
#include <linux/clk.h>
#include <asm/hardware.h>
#include <asm/irq.h>
#include <asm/io.h>
#include <asm/arch/regs-s3c6400-clock.h>
#include <asm/arch/regs-gpio.h>
#include <asm/arch/regs-spi.h>
#include <asm/dma.h>
#include "spi-dev.h"
#include "hspi-s3c6400.h"
//#define S3C_SPI_DEBUG
#ifdef S3C_SPI_DEBUG
#define DBG(x...) printk(x)
#define DEBUG printk("%s :: %d\n",__FUNCTION__,__LINE__)
void print_reg(struct s3c_spi *spi)
{
printk("CH_CFG = 0x%08x\n",readl(spi->regs + S3C_CH_CFG));
printk("CLK_CFG = 0x%08x\n",readl(spi->regs + S3C_CLK_CFG));
printk("MODE_CFG = 0x%08x\n",readl(spi->regs + S3C_MODE_CFG));
printk("SLAVE_CFG = 0x%08x\n",readl(spi->regs + S3C_SLAVE_SEL));
printk("INT_EN = 0x%08x\n",readl(spi->regs + S3C_SPI_INT_EN));
printk("SPI_STATUS = 0x%08x\n",readl(spi->regs + S3C_SPI_STATUS));
printk("PACKET_CNT = 0x%08x\n",readl(spi->regs + S3C_PACKET_CNT));
printk("PEND_CLR = 0x%08x\n",readl(spi->regs + S3C_PENDING_CLR));
printk("SWAP_CFG = 0x%08x\n",readl(spi->regs + S3C_SWAP_CFG));
printk("FB_CLK = 0x%08x\n",readl(spi->regs + S3C_FB_CLK));
}
#else
#define DEBUG
#define DBG(x...) do { } while (0)
void print_reg(struct s3c_spi *spi)
{
}
#endif
static void s3c_spi_free(struct s3c_spi *spi)
{
if (spi->clk != NULL && !IS_ERR(spi->clk)) {
clk_disable(spi->clk);
clk_put(spi->clk);
spi->clk = NULL;
}
if (spi->regs != NULL) {
iounmap(spi->regs);
spi->regs = NULL;
}
if (spi->ioarea != NULL) {
release_resource(spi->ioarea);
kfree(spi->ioarea);
spi->ioarea = NULL;
}
}
static int s3c_spi_hw_init(struct s3c_spi *spi)
{
#ifdef CONFIG_SPICLK_PCLK
clk_enable(spi->clk);
#elif defined (CONFIG_SPICLK_EPLL)
writel((readl(S3C_PCLK_GATE)|S3C_CLKCON_PCLK_SPI0|S3C_CLKCON_PCLK_SPI1),S3C_PCLK_GATE);
writel((readl(S3C_SCLK_GATE)|S3C_CLKCON_SCLK_SPI0|S3C_CLKCON_SCLK_SPI1),S3C_SCLK_GATE);
writel(readl(S3C_CLK_SRC)|S3C_CLKSRC_MPLL_CLKSEL, S3C_CLK_SRC);
/* Set SPi Clock to MOUT(266Mhz)*/
if(SPI_CHANNEL == 0)
writel((readl(S3C_CLK_SRC)&~(0x3<<14))|(1<<14), S3C_CLK_SRC);
else /* SPI_CHANNEL = 1 */
writel((readl(S3C_CLK_SRC)&~(0x3<<16))|(1<<16), S3C_CLK_SRC);
/* CLK_DIV2 setting */
/* SPI Input Clock(88.87Mhz) = 266.66Mhz / (2 + 1)*/
writel(((readl(S3C_CLK_DIV2) & ~(0xff << 0)) | 2) , S3C_CLK_DIV2);
#elif defined (CONFIG_SPICLK_USBCLK)
writel((readl(S3C_PCLK_GATE)| S3C_CLKCON_PCLK_SPI0|S3C_CLKCON_PCLK_SPI1),S3C_PCLK_GATE);
writel((readl(S3C_SCLK_GATE)|S3C_CLKCON_SCLK_SPI0_48|S3C_CLKCON_SCLK_SPI1_48),S3C_SCLK_GATE);
#else
#error you must define correct confige file.
#endif
/* initialize the gpio */
if(SPI_CHANNEL == 0) {
s3c_gpio_cfgpin(S3C_GPC0, S3C_GPC0_SPI_MISO0);
s3c_gpio_cfgpin(S3C_GPC1, S3C_GPC1_SPI_CLK0);
s3c_gpio_cfgpin(S3C_GPC2, S3C_GPC2_SPI_MOSI0);
s3c_gpio_cfgpin(S3C_GPC3, S3C_GPC3_SPI_CS0);
s3c_gpio_pullup(S3C_GPC0,1);
s3c_gpio_pullup(S3C_GPC1,1);
s3c_gpio_pullup(S3C_GPC2,1);
s3c_gpio_pullup(S3C_GPC3,1);
} else {
s3c_gpio_cfgpin(S3C_GPC4, S3C_GPC4_SPI_MISO1);
s3c_gpio_cfgpin(S3C_GPC5, S3C_GPC5_SPI_CLK1);
s3c_gpio_cfgpin(S3C_GPC6, S3C_GPC6_SPI_MOSI1);
s3c_gpio_cfgpin(S3C_GPC7, S3C_GPC7_SPI_CS1);
s3c_gpio_pullup(S3C_GPC4,1);
s3c_gpio_pullup(S3C_GPC5,1);
s3c_gpio_pullup(S3C_GPC6,1);
s3c_gpio_pullup(S3C_GPC7,1);
}
return 0;
}
static int s3c_spi_dma_init(struct s3c_spi *spi, int mode)
{
// TX
if (mode == 0) {
if(SPI_CHANNEL == 0)
s3c2410_dma_devconfig(spi->subchannel, S3C2410_DMASRC_MEM, 0, S3C_SPI_TX_DATA_REG);
else // SPI_CHANNEL = 1
s3c2410_dma_devconfig(spi->subchannel, S3C2410_DMASRC_MEM, 0, S3C_SPI_TX_DATA_REG);
}
// RX
if (mode == 1) {
if(SPI_CHANNEL == 0)
s3c2410_dma_devconfig(spi->subchannel, S3C2410_DMASRC_HW, 0, S3C_SPI_RX_DATA_REG);
else // SPI_CHANNEL = 1
s3c2410_dma_devconfig(spi->subchannel, S3C2410_DMASRC_HW, 0, S3C_SPI_RX_DATA_REG);
}
#ifdef CONFIG_WORD_TRANSIZE
s3c2410_dma_config(spi->subchannel, S3C_DMA_XFER_WORD, S3C_DMA_XFER_WORD);
#else
s3c2410_dma_config(spi->subchannel, S3C_DMA_XFER_BYTE, S3C_DMA_XFER_BYTE);
#endif
s3c2410_dma_setflags(spi->subchannel, S3C2410_DMAF_AUTOSTART);
return 0;
}
static inline void s3c_spi_write_fifo(struct s3c_spi *spi)
{
u32 wdata = 0;
if (spi->msg->wbuf){
wdata = spi->msg->wbuf[spi->msg_ptr++];
} else {
spi->msg_ptr++;
wdata = 0xff;
}
DBG("wdata = %x\n",wdata);
writel(wdata, spi->regs + S3C_SPI_TX_DATA);
}
/* s3c_spi_master_complete
*
* complete the message and wake up the caller, using the given return code,
* or zero to mean ok.
*/
static inline void s3c_spi_master_complete(struct s3c_spi *spi, int ret)
{
spi->msg_ptr = 0;
spi->msg_rd_ptr = 0;
spi->msg->flags = 0;
spi->msg = NULL;
spi->msg_idx ++;
spi->msg_num = 0;
writel(0xff, spi->regs + S3C_PENDING_CLR);
if (ret)
spi->msg_idx = ret;
}
static int s3c_spi_done(struct s3c_spi *spi)
{
u32 spi_clkcfg;
/* initialize the gpio */
if(SPI_CHANNEL == 0) {
s3c_gpio_cfgpin(S3C_GPC0, 0);
s3c_gpio_cfgpin(S3C_GPC1, 0);
s3c_gpio_cfgpin(S3C_GPC2, 0);
s3c_gpio_cfgpin(S3C_GPC3, 0);
} else {
s3c_gpio_cfgpin(S3C_GPC4, 0);
s3c_gpio_cfgpin(S3C_GPC5, 0);
s3c_gpio_cfgpin(S3C_GPC6, 0);
s3c_gpio_cfgpin(S3C_GPC7, 0);
}
spi_clkcfg = readl( spi->regs + S3C_CLK_CFG);
spi_clkcfg &= SPI_ENCLK_DISABLE;
writel( spi_clkcfg , spi->regs + S3C_CLK_CFG);
return 0;
}
static inline void s3c_spi_stop(struct s3c_spi *spi, int ret)
{
writel(0x0, spi->regs + S3C_SPI_INT_EN);
writel(0x1f, spi->regs + S3C_PENDING_CLR);
writel(0x0, spi->regs + S3C_CH_CFG);
s3c_spi_done(spi);
spi->state = STATE_IDLE;
s3c_spi_master_complete(spi, ret);
print_reg(spi);
up(&spi->sem);
}
void s3c_spi_dma_cb(struct s3c2410_dma_chan *dma_ch, void *buf_id,
int size, enum s3c2410_dma_buffresult result)
{
struct s3c_spi *spi = (struct s3c_spi *)buf_id;
unsigned long status = 0;
status = readl(spi->regs + S3C_SPI_STATUS);
pr_debug("DMA call back\n");
if (spi->msg->wbuf)
while (!(readl(spi->regs +S3C_SPI_STATUS) & SPI_STUS_TX_DONE)) {}
//s3c_spi_stop(spi, status);
s3c_spi_stop(spi, 0);
}
/* s3c_spi_message_start
*
* configure the spi controler and transmit start of a message onto the bus
*/
static void s3c_spi_message_start(struct s3c_spi *spi)
{
struct spi_msg *msg = spi->msg;
u32 spi_chcfg = 0, spi_slavecfg, spi_inten= 0, spi_packet=0;
// u8 prescaler = 0; // 44.435 Mhz
u8 prescaler = 1; // 22.2175 Mhz
// u8 prescaler = 2; // 14.81 Mhz
// u8 prescaler = 3; // 11.10875 Mhz
// u8 prescaler = 4; // 8.887Mhz
u32 spi_clkcfg = 0, spi_modecfg = 0 ;
/* initialise the spi controller */
s3c_spi_hw_init(spi);
/* 1. Set transfer type (CPOL & CPHA set) */
spi_chcfg = SPI_CH_RISING | SPI_CH_FORMAT_A;
if (spi->msg->flags & SPI_M_MODE_MASTER) {
spi_chcfg |= SPI_CH_MASTER;
} else if(spi->msg->flags & SPI_M_MODE_SLAVE){
spi_chcfg |= SPI_CH_SLAVE;
}
writel( spi_chcfg , spi->regs + S3C_CH_CFG);
/* 2. Set clock configuration register */
spi_clkcfg = SPI_ENCLK_ENABLE;
#if defined CONFIG_SPICLK_PCLK
spi_clkcfg |= SPI_CLKSEL_PCLK;
#elif defined CONFIG_SPICLK_EPLL
spi_clkcfg |= SPI_CLKSEL_ECLK;
#elif defined CONFIG_SPICLK_USBCLK
spi_clkcfg |= SPI_CLKSEL_USBCLK;
#else
#error you must define correct confige file.
#endif
writel( spi_clkcfg , spi->regs + S3C_CLK_CFG);
spi_clkcfg = readl( spi->regs + S3C_CLK_CFG);
/* SPI clockout = clock source / (2 * (prescaler +1)) */
spi_clkcfg |= prescaler;
writel( spi_clkcfg , spi->regs + S3C_CLK_CFG);
/* 3. Set SPI MODE configuration register */
#ifdef CONFIG_WORD_TRANSIZE
spi_modecfg = SPI_MODE_CH_TSZ_WORD| SPI_MODE_BUS_TSZ_WORD;
#else
spi_modecfg = SPI_MODE_CH_TSZ_BYTE| SPI_MODE_BUS_TSZ_BYTE;
#endif
spi_modecfg |= SPI_MODE_TXDMA_OFF| SPI_MODE_SINGLE| SPI_MODE_RXDMA_OFF;
if (msg->flags & SPI_M_DMA_MODE) {
spi_modecfg |= SPI_MODE_TXDMA_ON| SPI_MODE_RXDMA_ON;
}
if (msg->wbuf)
spi_modecfg |= ( 0x3f << 5); /* Tx FIFO trigger level in INT mode */
if (msg->rbuf)
spi_modecfg |= ( 0x3f << 11); /* Rx FIFO trigger level in INT mode */
spi_modecfg |= ( 0x3ff << 19);
writel(spi_modecfg, spi->regs + S3C_MODE_CFG);
/* 4. Set SPI INT_EN register */
if (msg->wbuf)
spi_inten = SPI_INT_TX_FIFORDY_EN|SPI_INT_TX_UNDERRUN_EN|SPI_INT_TX_OVERRUN_EN;
if (msg->rbuf){
spi_inten = SPI_INT_RX_FIFORDY_EN|SPI_INT_RX_UNDERRUN_EN|SPI_INT_RX_OVERRUN_EN|SPI_INT_TRAILING_EN ;
}
writel(spi_inten, spi->regs + S3C_SPI_INT_EN);
writel(0x1f, spi->regs + S3C_PENDING_CLR);
/* 5. Set Packet Count configuration register */
spi_packet = SPI_PACKET_CNT_EN;
spi_packet |= 0xffff;
writel(spi_packet, spi->regs + S3C_PACKET_CNT);
/* 6. Set Tx or Rx Channel on */
spi_chcfg = readl(spi->regs + S3C_CH_CFG);
spi_chcfg |= SPI_CH_TXCH_OFF | SPI_CH_RXCH_OFF;
if (msg->wbuf)
spi_chcfg |= SPI_CH_TXCH_ON;
if (msg->rbuf)
spi_chcfg |= SPI_CH_RXCH_ON;
writel(spi_chcfg, spi->regs + S3C_CH_CFG);
if (msg->flags & SPI_M_DMA_MODE) {
spi->dma = S3C_SPI_DMA;
if (msg->wbuf)
spi->subchannel = DMACH_SPI0_OUT;
if (msg->rbuf)
spi->subchannel = DMACH_SPI0_IN;
if (s3c2410_dma_request(spi->subchannel, &s3c6400spi_dma_client, NULL)) {
printk(KERN_WARNING "unable to get DMA channel.\n" );
}
s3c2410_dma_set_buffdone_fn(spi->subchannel, s3c_spi_dma_cb);
s3c2410_dma_set_opfn(spi->subchannel, NULL);
if (msg->wbuf)
s3c_spi_dma_init(spi, 0);
if (msg->rbuf)
s3c_spi_dma_init(spi, 1);
s3c2410_dma_enqueue(spi->subchannel, (void *) spi, spi->dmabuf_addr, spi->msg->len);
}
/* 7. Set nSS low to start Tx or Rx operation */
spi_slavecfg = readl(spi->regs + S3C_SLAVE_SEL);
spi_slavecfg &= SPI_SLAVE_SIG_ACT;
spi_slavecfg |= (0x3f << 4);
writel(spi_slavecfg, spi->regs + S3C_SLAVE_SEL);
print_reg(spi);
}
/* is_msgend
*
* returns TRUE if we reached the end of the current message
*/
static inline int tx_msgend(struct s3c_spi *spi)
{
return spi->msg_ptr >= spi->msg->len;
}
static inline int rx_msgend(struct s3c_spi *spi)
{
return spi->msg_rd_ptr >= spi->msg->len;
}
/* spi_s3c_irq_nextbyte
*
* process an interrupt and work out what to do
*/
static void spi_s3c_irq_nextbyte(struct s3c_spi *spi, unsigned long spsta)
{
DBG("spi->state = %d \n",spi->state);
switch (spi->state) {
case STATE_IDLE:
DBG("%s: called in STATE_IDLE\n", __FUNCTION__);
break;
case STATE_STOP:
udelay(200);
s3c_spi_stop(spi, 0);
DBG("%s: called in STATE_STOP\n", __FUNCTION__);
break;
case STATE_XFER_TX:
print_reg(spi);
DBG("msg_ptr = 0x%x, len = 0x%x \n", spi->msg_ptr ,spi->msg->len);
while(!(tx_msgend(spi)))
s3c_spi_write_fifo(spi);
print_reg(spi);
spi->state = STATE_STOP;
break;
case STATE_XFER_RX:
print_reg(spi);
DBG("msg_rd_ptr = 0x%x, len = 0x%x \n", spi->msg_rd_ptr ,spi->msg->len);
while(!(rx_msgend(spi))){
spi->msg->rbuf[spi->msg_rd_ptr++] = readl(spi->regs + S3C_SPI_RX_DATA);
DBG("msg_rd_ptr = 0x%x, len = 0x%x \n", spi->msg_rd_ptr ,spi->msg->len);
DBG("msg_rbuf = 0x%x\n", spi->msg->rbuf[spi->msg_rd_ptr - 1]);
}
DBG("msg_rd_ptr = 0x%x, len = 0x%x \n", spi->msg_rd_ptr ,spi->msg->len);
print_reg(spi);
s3c_spi_stop(spi, 0);
break;
default:
dev_err(spi->dev, "%s: called with Invalid option\n", __FUNCTION__);
}
return;
}
/* s3c_spi_irq
*
* top level IRQ servicing routine
*/
static irqreturn_t s3c_spi_irq(int irqno, void *dev_id)
{
struct s3c_spi *spi = dev_id;
unsigned long spi_sts;
spi_sts = readl(spi->regs + S3C_SPI_STATUS);
if (spi_sts & SPI_STUS_RX_OVERRUN_ERR) {
printk("hspi : Rx overrun error detected\n");
}
if (spi_sts & SPI_STUS_RX_UNDERRUN_ERR) {
printk("hspi : Rx underrun error detected\n");
}
if (spi_sts & SPI_STUS_TX_OVERRUN_ERR) {
printk("hspi : Tx overrun error detected\n");
}
if (spi_sts & SPI_STUS_TX_UNDERRUN_ERR) {
printk("hspi : Tx underrun error detected\n");
}
/* pretty much this leaves us with the fact that we've
* transmitted or received whatever byte we last sent */
spi_s3c_irq_nextbyte(spi, spi_sts);
return IRQ_HANDLED;
}
static int s3c_spi_doxfer(struct s3c_spi *spi, struct spi_msg msgs[], int num)
{
int ret;
spin_lock_irq(&spi->lock);
spi->msg = msgs;
spi->msg_num = num;
spi->msg_ptr = 0;
spi->msg_rd_ptr = 0;
spi->msg_idx = 0;
if (spi->msg->flags & SPI_M_DMA_MODE) {
spi->dmabuf_addr = spi->spidev.dmabuf;
pr_debug("spi->dmabuf_addr = 0x%x\n",spi->dmabuf_addr);
}
if (spi->msg->wbuf) {
spi->state = STATE_XFER_TX;
} else if (spi->msg->rbuf) {
spi->state = STATE_XFER_RX;
} else {
dev_err(spi->dev,"Unknown functionality \n");
return -ESRCH;
}
s3c_spi_message_start(spi);
if (down_interruptible(&spi->sem))
return -EINTR;
spin_unlock_irq(&spi->lock);
ret = spi->msg_idx;
return ret;
}
/* s3c_spi_xfer
*
* first port of call from the spi bus code when an message needs
* transfering across the spi bus.
*/
static int s3c_spi_xfer(struct spi_dev *spi_dev,
struct spi_msg msgs[], int num)
{
struct s3c_spi *spi = (struct s3c_spi *)spi_dev->algo_data;
int retry;
int ret;
for (retry = 0; retry < spi_dev->retries; retry++) {
ret = s3c_spi_doxfer(spi, msgs, num);
print_reg(spi);
if (ret != -EAGAIN)
return ret;
printk("Retrying transmission (%d)\n", retry);
udelay(100);
}
return -EREMOTEIO;
}
static int s3c_spi_close(struct spi_dev *spi_dev)
{
struct s3c_spi *spi = (struct s3c_spi *)spi_dev->algo_data;
u32 spi_clkcfg;
s3c2410_dma_free(spi->subchannel, &s3c6400spi_dma_client);
if(SPI_CHANNEL == 0) {
s3c_gpio_cfgpin(S3C_GPC0, 0);
s3c_gpio_cfgpin(S3C_GPC1, 0);
s3c_gpio_cfgpin(S3C_GPC2, 0);
s3c_gpio_cfgpin(S3C_GPC3, 0);
} else {
s3c_gpio_cfgpin(S3C_GPC4, 0);
s3c_gpio_cfgpin(S3C_GPC5, 0);
s3c_gpio_cfgpin(S3C_GPC6, 0);
s3c_gpio_cfgpin(S3C_GPC7, 0);
}
spi_clkcfg = readl( spi->regs + S3C_CLK_CFG);
spi_clkcfg &= SPI_ENCLK_DISABLE;
writel( spi_clkcfg , spi->regs + S3C_CLK_CFG);
/* Buffer Clear after finish xfer */
writel( 0x20, spi->regs + S3C_CH_CFG);
writel( 0x0, spi->regs + S3C_CH_CFG);
return 0;
}
/* spi bus registration info */
static struct spi_algorithm s3c_spi_algorithm = {
.name = "S3C6400-spi-algorithm",
.master_xfer = s3c_spi_xfer,
.close = s3c_spi_close,
};
static struct s3c_spi s3c_spi[2] = {
[0] = {
.lock = SPIN_LOCK_UNLOCKED,
.spidev = {
.algo = &s3c_spi_algorithm,
.retries = 2,
.timeout = 5,
}
},
[1] = {
.lock = SPIN_LOCK_UNLOCKED,
.spidev = {
.algo = &s3c_spi_algorithm,
.retries = 2,
.timeout = 5,
}
},
};
/* s3c_spi_probe
*
* called by the bus driver when a suitable device is found
*/
static int s3c_spi_probe(struct platform_device *pdev)
{
struct s3c_spi *spi = &s3c_spi[pdev->id];
struct resource *res;
int ret;
/* find the clock and enable it */
sema_init(&spi->sem, 0);
spi->nr = pdev->id;
spi->dev = &pdev->dev;
spi->clk = clk_get(&pdev->dev, "spi");
if (IS_ERR(spi->clk)) {
dev_err(&pdev->dev, "cannot get clock\n");
ret = -ENOENT;
goto out;
}
/* map the registers */
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
if (res == NULL) {
dev_err(&pdev->dev, "cannot find IO resource\n");
ret = -ENOENT;
goto out;
}
spi->ioarea = request_mem_region(res->start, (res->end - res->start) + 1, pdev->name);
if (spi->ioarea == NULL) {
dev_err(&pdev->dev, "cannot request IO\n");
ret = -ENXIO;
goto out;
}
spi->regs = ioremap(res->start, (res->end - res->start) + 1);
if (spi->regs == NULL) {
dev_err(&pdev->dev, "cannot map IO\n");
ret = -ENXIO;
goto out;
}
printk("hspi registers %p (%p, %p)\n", spi->regs, spi->ioarea, res);
/* setup info block for the spi core */
spi->spidev.algo_data = spi;
spi->spidev.dev.parent = &pdev->dev;
spi->spidev.minor = spi->nr;
init_MUTEX(&spi->spidev.bus_lock);
/* find the IRQ for this unit (note, this relies on the init call to
* ensure no current IRQs pending
*/
res = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
if (res == NULL) {
printk("hspi cannot find IRQ\n");
ret = -ENOENT;
goto out;
}
ret = request_irq(res->start, s3c_spi_irq, SA_INTERRUPT,
pdev->name, spi);
if (ret != 0) {
printk("hspi cannot claim IRQ\n");
goto out;
}
printk("hspi irq resource %p (%d)\n", res, res->start);
ret = spi_attach_spidev(&spi->spidev);
if (ret < 0) {
dev_err(&pdev->dev, "failed to add adapter to spi core\n");
goto out;
}
dev_set_drvdata(&pdev->dev, spi);
dev_info(&pdev->dev, "%s: S3C SPI adapter\n", spi->dev->bus_id);
printk("%s: S3C SPI adapter\n", spi->dev->bus_id);
out:
if (ret < 0)
s3c_spi_free(spi);
return ret;
}
/* s3c_spi_remove
*
* called when device is removed from the bus
*/
static int s3c_spi_remove(struct platform_device *pdev)
{
struct s3c_spi *spi = dev_get_drvdata(&pdev->dev);
if (spi != NULL) {
spi_detach_spidev(&spi->spidev);
s3c_spi_free(spi);
dev_set_drvdata(&pdev->dev, NULL);
}
return 0;
}
#ifdef CONFIG_PM
static int s3c_spi_suspend(struct platform_device *pdev, pm_message_t msg)
{
struct s3c_spi *hw = platform_get_drvdata(pdev);
clk_disable(hw->clk);
return 0;
}
static int s3c_spi_resume(struct platform_device *pdev)
{
struct s3c_spi *hw = platform_get_drvdata(pdev);
clk_enable(hw->clk);
return 0;
}
#else
#define s3c_spi_suspend NULL
#define s3c_spi_resume NULL
#endif
/* device driver for platform bus bits */
static struct platform_driver s3c_spi_driver = {
.probe = s3c_spi_probe,
.remove = s3c_spi_remove,
#ifdef CONFIG_PM
.suspend = s3c_spi_suspend,
.resume = s3c_spi_resume,
#endif
.driver = {
.name = "s3c2410-spi",
.owner = THIS_MODULE,
.bus = &platform_bus_type,
},
};
static int __init s3c_spi_driver_init(void)
{
printk(KERN_INFO "S3C6400 SPI Driver \n");
return platform_driver_register(&s3c_spi_driver);
}
static void __exit s3c_spi_driver_exit(void)
{
platform_driver_unregister(&s3c_spi_driver);
}
module_init(s3c_spi_driver_init);
module_exit(s3c_spi_driver_exit);
MODULE_DESCRIPTION("S3C6400 SPI Bus driver");
MODULE_AUTHOR("Ryu Euiyoul<steven.ryu@samsung.com>");
MODULE_LICENSE("GPL");

View File

@@ -0,0 +1,95 @@
/* ------------------------------------------------------------------------- */
/* */
/* spi-s3c6400.h - definitions of s3c6400 specific spi interface */
/* */
/* ------------------------------------------------------------------------- */
/* Copyright (C) 2006 Samsung Electronics Co. ltd.
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.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
/* ------------------------------------------------------------------------- */
#ifndef _S3C6400_SPI_H
#define _S3C6400_SPI_H
#include <asm/dma.h>
#include <asm/arch/dma.h>
//#define SPI_CHANNEL 1
#if(SPI_CHANNEL==0)
/* SPI CHANNEL 0 */
#define S3C_SPI_TX_DATA_REG 0x7F00B018 //SPI TX data
#define S3C_SPI_RX_DATA_REG 0x7F00B01C //SPI RX data
#else
/* SPI CHANNEL 1 */
#define S3C_SPI_TX_DATA_REG 0x7F00C018 //SPI TX data
#define S3C_SPI_RX_DATA_REG 0x7F00C01C //SPI RX data
#endif
/* DMA channel to be used for the SPI interface. */
#define S3C_SPI_DMA 0
/* DMA transfer unit (byte). */
#define S3C_DMA_XFER_BYTE 1
#define S3C_DMA_XFER_WORD 4
/* DMA configuration setup byte. */
#define S3C_DCON_SPI1 (S3C_DCON_HANDSHAKE | S3C_DCON_SYNC_PCLK)
/* DMA hardware configuration mode (DISRCC register). */
#define S3C_SPI1_DMA_HWCFG 3
#define S3C_SPI_DMA_HWCFG 3
#define DMA_BUFFER_SIZE 1500
/* spi controller state */
int req_dma_flag = 1;
enum s3c_spi_state {
STATE_IDLE,
STATE_XFER_TX,
STATE_XFER_RX,
STATE_STOP
};
static struct s3c2410_dma_client s3c6400spi_dma_client = {
.name = "s3c6400-spi-dma",
};
struct s3c_spi {
spinlock_t lock;
struct semaphore sem;
int nr;
int dma;
u_int subchannel;/* user fragment index */
dma_addr_t dmabuf_addr;
struct spi_msg *msg;
unsigned int msg_num;
unsigned int msg_idx;
unsigned int msg_ptr;
unsigned int msg_rd_ptr;
enum s3c_spi_state state;
void __iomem *regs;
struct clk *clk;
struct device *dev;
struct resource *irq;
struct resource *ioarea;
struct spi_dev spidev;
};
#endif /* _S3C6400_SPI_H */

572
drivers/spi/omap_uwire.c Normal file
View File

@@ -0,0 +1,572 @@
/*
* omap_uwire.c -- MicroWire interface driver for OMAP
*
* Copyright 2003 MontaVista Software Inc. <source@mvista.com>
*
* Ported to 2.6 OMAP uwire interface.
* Copyright (C) 2004 Texas Instruments.
*
* Generalization patches by Juha Yrjola <juha.yrjola@nokia.com>
*
* Copyright (C) 2005 David Brownell (ported to 2.6 SPI interface)
* Copyright (C) 2006 Nokia
*
* Many updates by Imre Deak <imre.deak@nokia.com>
*
* 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 SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
* IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
* USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
* ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 675 Mass Ave, Cambridge, MA 02139, USA.
*/
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/delay.h>
#include <linux/platform_device.h>
#include <linux/workqueue.h>
#include <linux/interrupt.h>
#include <linux/err.h>
#include <linux/clk.h>
#include <linux/spi/spi.h>
#include <linux/spi/spi_bitbang.h>
#include <asm/system.h>
#include <asm/irq.h>
#include <asm/hardware.h>
#include <asm/io.h>
#include <asm/mach-types.h>
#include <asm/arch/mux.h>
#include <asm/arch/omap730.h> /* OMAP730_IO_CONF registers */
/* FIXME address is now a platform device resource,
* and irqs should show there too...
*/
#define UWIRE_BASE_PHYS 0xFFFB3000
#define UWIRE_BASE ((void *__iomem)IO_ADDRESS(UWIRE_BASE_PHYS))
/* uWire Registers: */
#define UWIRE_IO_SIZE 0x20
#define UWIRE_TDR 0x00
#define UWIRE_RDR 0x00
#define UWIRE_CSR 0x01
#define UWIRE_SR1 0x02
#define UWIRE_SR2 0x03
#define UWIRE_SR3 0x04
#define UWIRE_SR4 0x05
#define UWIRE_SR5 0x06
/* CSR bits */
#define RDRB (1 << 15)
#define CSRB (1 << 14)
#define START (1 << 13)
#define CS_CMD (1 << 12)
/* SR1 or SR2 bits */
#define UWIRE_READ_FALLING_EDGE 0x0001
#define UWIRE_READ_RISING_EDGE 0x0000
#define UWIRE_WRITE_FALLING_EDGE 0x0000
#define UWIRE_WRITE_RISING_EDGE 0x0002
#define UWIRE_CS_ACTIVE_LOW 0x0000
#define UWIRE_CS_ACTIVE_HIGH 0x0004
#define UWIRE_FREQ_DIV_2 0x0000
#define UWIRE_FREQ_DIV_4 0x0008
#define UWIRE_FREQ_DIV_8 0x0010
#define UWIRE_CHK_READY 0x0020
#define UWIRE_CLK_INVERTED 0x0040
struct uwire_spi {
struct spi_bitbang bitbang;
struct clk *ck;
};
struct uwire_state {
unsigned bits_per_word;
unsigned div1_idx;
};
/* REVISIT compile time constant for idx_shift? */
static unsigned int uwire_idx_shift;
static inline void uwire_write_reg(int idx, u16 val)
{
__raw_writew(val, UWIRE_BASE + (idx << uwire_idx_shift));
}
static inline u16 uwire_read_reg(int idx)
{
return __raw_readw(UWIRE_BASE + (idx << uwire_idx_shift));
}
static inline void omap_uwire_configure_mode(u8 cs, unsigned long flags)
{
u16 w, val = 0;
int shift, reg;
if (flags & UWIRE_CLK_INVERTED)
val ^= 0x03;
val = flags & 0x3f;
if (cs & 1)
shift = 6;
else
shift = 0;
if (cs <= 1)
reg = UWIRE_SR1;
else
reg = UWIRE_SR2;
w = uwire_read_reg(reg);
w &= ~(0x3f << shift);
w |= val << shift;
uwire_write_reg(reg, w);
}
static int wait_uwire_csr_flag(u16 mask, u16 val, int might_not_catch)
{
u16 w;
int c = 0;
unsigned long max_jiffies = jiffies + HZ;
for (;;) {
w = uwire_read_reg(UWIRE_CSR);
if ((w & mask) == val)
break;
if (time_after(jiffies, max_jiffies)) {
printk(KERN_ERR "%s: timeout. reg=%#06x "
"mask=%#06x val=%#06x\n",
__FUNCTION__, w, mask, val);
return -1;
}
c++;
if (might_not_catch && c > 64)
break;
}
return 0;
}
static void uwire_set_clk1_div(int div1_idx)
{
u16 w;
w = uwire_read_reg(UWIRE_SR3);
w &= ~(0x03 << 1);
w |= div1_idx << 1;
uwire_write_reg(UWIRE_SR3, w);
}
static void uwire_chipselect(struct spi_device *spi, int value)
{
struct uwire_state *ust = spi->controller_state;
u16 w;
int old_cs;
BUG_ON(wait_uwire_csr_flag(CSRB, 0, 0));
w = uwire_read_reg(UWIRE_CSR);
old_cs = (w >> 10) & 0x03;
if (value == BITBANG_CS_INACTIVE || old_cs != spi->chip_select) {
/* Deselect this CS, or the previous CS */
w &= ~CS_CMD;
uwire_write_reg(UWIRE_CSR, w);
}
/* activate specfied chipselect */
if (value == BITBANG_CS_ACTIVE) {
uwire_set_clk1_div(ust->div1_idx);
/* invert clock? */
if (spi->mode & SPI_CPOL)
uwire_write_reg(UWIRE_SR4, 1);
else
uwire_write_reg(UWIRE_SR4, 0);
w = spi->chip_select << 10;
w |= CS_CMD;
uwire_write_reg(UWIRE_CSR, w);
}
}
static int uwire_txrx(struct spi_device *spi, struct spi_transfer *t)
{
struct uwire_state *ust = spi->controller_state;
unsigned len = t->len;
unsigned bits = ust->bits_per_word;
unsigned bytes;
u16 val, w;
int status = 0;;
if (!t->tx_buf && !t->rx_buf)
return 0;
/* Microwire doesn't read and write concurrently */
if (t->tx_buf && t->rx_buf)
return -EPERM;
w = spi->chip_select << 10;
w |= CS_CMD;
if (t->tx_buf) {
const u8 *buf = t->tx_buf;
/* NOTE: DMA could be used for TX transfers */
/* write one or two bytes at a time */
while (len >= 1) {
/* tx bit 15 is first sent; we byteswap multibyte words
* (msb-first) on the way out from memory.
*/
val = *buf++;
if (bits > 8) {
bytes = 2;
val |= *buf++ << 8;
} else
bytes = 1;
val <<= 16 - bits;
#ifdef VERBOSE
pr_debug("%s: write-%d =%04x\n",
spi->dev.bus_id, bits, val);
#endif
if (wait_uwire_csr_flag(CSRB, 0, 0))
goto eio;
uwire_write_reg(UWIRE_TDR, val);
/* start write */
val = START | w | (bits << 5);
uwire_write_reg(UWIRE_CSR, val);
len -= bytes;
/* Wait till write actually starts.
* This is needed with MPU clock 60+ MHz.
* REVISIT: we may not have time to catch it...
*/
if (wait_uwire_csr_flag(CSRB, CSRB, 1))
goto eio;
status += bytes;
}
/* REVISIT: save this for later to get more i/o overlap */
if (wait_uwire_csr_flag(CSRB, 0, 0))
goto eio;
} else if (t->rx_buf) {
u8 *buf = t->rx_buf;
/* read one or two bytes at a time */
while (len) {
if (bits > 8) {
bytes = 2;
} else
bytes = 1;
/* start read */
val = START | w | (bits << 0);
uwire_write_reg(UWIRE_CSR, val);
len -= bytes;
/* Wait till read actually starts */
(void) wait_uwire_csr_flag(CSRB, CSRB, 1);
if (wait_uwire_csr_flag(RDRB | CSRB,
RDRB, 0))
goto eio;
/* rx bit 0 is last received; multibyte words will
* be properly byteswapped on the way to memory.
*/
val = uwire_read_reg(UWIRE_RDR);
val &= (1 << bits) - 1;
*buf++ = (u8) val;
if (bytes == 2)
*buf++ = val >> 8;
status += bytes;
#ifdef VERBOSE
pr_debug("%s: read-%d =%04x\n",
spi->dev.bus_id, bits, val);
#endif
}
}
return status;
eio:
return -EIO;
}
static int uwire_setup_transfer(struct spi_device *spi, struct spi_transfer *t)
{
struct uwire_state *ust = spi->controller_state;
struct uwire_spi *uwire;
unsigned flags = 0;
unsigned bits;
unsigned hz;
unsigned long rate;
int div1_idx;
int div1;
int div2;
int status;
uwire = spi_master_get_devdata(spi->master);
if (spi->chip_select > 3) {
pr_debug("%s: cs%d?\n", spi->dev.bus_id, spi->chip_select);
status = -ENODEV;
goto done;
}
bits = spi->bits_per_word;
if (t != NULL && t->bits_per_word)
bits = t->bits_per_word;
if (!bits)
bits = 8;
if (bits > 16) {
pr_debug("%s: wordsize %d?\n", spi->dev.bus_id, bits);
status = -ENODEV;
goto done;
}
ust->bits_per_word = bits;
/* mode 0..3, clock inverted separately;
* standard nCS signaling;
* don't treat DI=high as "not ready"
*/
if (spi->mode & SPI_CS_HIGH)
flags |= UWIRE_CS_ACTIVE_HIGH;
if (spi->mode & SPI_CPOL)
flags |= UWIRE_CLK_INVERTED;
switch (spi->mode & (SPI_CPOL | SPI_CPHA)) {
case SPI_MODE_0:
case SPI_MODE_3:
flags |= UWIRE_WRITE_RISING_EDGE | UWIRE_READ_FALLING_EDGE;
break;
case SPI_MODE_1:
case SPI_MODE_2:
flags |= UWIRE_WRITE_FALLING_EDGE | UWIRE_READ_RISING_EDGE;
break;
}
/* assume it's already enabled */
rate = clk_get_rate(uwire->ck);
hz = spi->max_speed_hz;
if (t != NULL && t->speed_hz)
hz = t->speed_hz;
if (!hz) {
pr_debug("%s: zero speed?\n", spi->dev.bus_id);
status = -EINVAL;
goto done;
}
/* F_INT = mpu_xor_clk / DIV1 */
for (div1_idx = 0; div1_idx < 4; div1_idx++) {
switch (div1_idx) {
case 0:
div1 = 2;
break;
case 1:
div1 = 4;
break;
case 2:
div1 = 7;
break;
default:
case 3:
div1 = 10;
break;
}
div2 = (rate / div1 + hz - 1) / hz;
if (div2 <= 8)
break;
}
if (div1_idx == 4) {
pr_debug("%s: lowest clock %ld, need %d\n",
spi->dev.bus_id, rate / 10 / 8, hz);
status = -EDOM;
goto done;
}
/* we have to cache this and reset in uwire_chipselect as this is a
* global parameter and another uwire device can change it under
* us */
ust->div1_idx = div1_idx;
uwire_set_clk1_div(div1_idx);
rate /= div1;
switch (div2) {
case 0:
case 1:
case 2:
flags |= UWIRE_FREQ_DIV_2;
rate /= 2;
break;
case 3:
case 4:
flags |= UWIRE_FREQ_DIV_4;
rate /= 4;
break;
case 5:
case 6:
case 7:
case 8:
flags |= UWIRE_FREQ_DIV_8;
rate /= 8;
break;
}
omap_uwire_configure_mode(spi->chip_select, flags);
pr_debug("%s: uwire flags %02x, armxor %lu KHz, SCK %lu KHz\n",
__FUNCTION__, flags,
clk_get_rate(uwire->ck) / 1000,
rate / 1000);
status = 0;
done:
return status;
}
static int uwire_setup(struct spi_device *spi)
{
struct uwire_state *ust = spi->controller_state;
if (ust == NULL) {
ust = kzalloc(sizeof(*ust), GFP_KERNEL);
if (ust == NULL)
return -ENOMEM;
spi->controller_state = ust;
}
return uwire_setup_transfer(spi, NULL);
}
static void uwire_cleanup(struct spi_device *spi)
{
kfree(spi->controller_state);
}
static void uwire_off(struct uwire_spi *uwire)
{
uwire_write_reg(UWIRE_SR3, 0);
clk_disable(uwire->ck);
clk_put(uwire->ck);
spi_master_put(uwire->bitbang.master);
}
static int uwire_probe(struct platform_device *pdev)
{
struct spi_master *master;
struct uwire_spi *uwire;
int status;
master = spi_alloc_master(&pdev->dev, sizeof *uwire);
if (!master)
return -ENODEV;
uwire = spi_master_get_devdata(master);
dev_set_drvdata(&pdev->dev, uwire);
uwire->ck = clk_get(&pdev->dev, "armxor_ck");
if (!uwire->ck || IS_ERR(uwire->ck)) {
dev_dbg(&pdev->dev, "no mpu_xor_clk ?\n");
spi_master_put(master);
return -ENODEV;
}
clk_enable(uwire->ck);
if (cpu_is_omap730())
uwire_idx_shift = 1;
else
uwire_idx_shift = 2;
uwire_write_reg(UWIRE_SR3, 1);
master->bus_num = 2; /* "official" */
master->num_chipselect = 4;
master->setup = uwire_setup;
master->cleanup = uwire_cleanup;
uwire->bitbang.master = master;
uwire->bitbang.chipselect = uwire_chipselect;
uwire->bitbang.setup_transfer = uwire_setup_transfer;
uwire->bitbang.txrx_bufs = uwire_txrx;
status = spi_bitbang_start(&uwire->bitbang);
if (status < 0)
uwire_off(uwire);
return status;
}
static int uwire_remove(struct platform_device *pdev)
{
struct uwire_spi *uwire = dev_get_drvdata(&pdev->dev);
int status;
// FIXME remove all child devices, somewhere ...
status = spi_bitbang_stop(&uwire->bitbang);
uwire_off(uwire);
return status;
}
static struct platform_driver uwire_driver = {
.driver = {
.name = "omap_uwire",
.bus = &platform_bus_type,
.owner = THIS_MODULE,
},
.probe = uwire_probe,
.remove = uwire_remove,
// suspend ... unuse ck
// resume ... use ck
};
static int __init omap_uwire_init(void)
{
/* FIXME move these into the relevant board init code. also, include
* H3 support; it uses tsc2101 like H2 (on a different chipselect).
*/
if (machine_is_omap_h2()) {
/* defaults: W21 SDO, U18 SDI, V19 SCL */
omap_cfg_reg(N14_1610_UWIRE_CS0);
omap_cfg_reg(N15_1610_UWIRE_CS1);
}
if (machine_is_omap_perseus2()) {
/* configure pins: MPU_UW_nSCS1, MPU_UW_SDO, MPU_UW_SCLK */
int val = omap_readl(OMAP730_IO_CONF_9) & ~0x00EEE000;
omap_writel(val | 0x00AAA000, OMAP730_IO_CONF_9);
}
return platform_driver_register(&uwire_driver);
}
static void __exit omap_uwire_exit(void)
{
platform_driver_unregister(&uwire_driver);
}
subsys_initcall(omap_uwire_init);
module_exit(omap_uwire_exit);
MODULE_LICENSE("GPL");

1635
drivers/spi/pxa2xx_spi.c Normal file

File diff suppressed because it is too large Load Diff

390
drivers/spi/spi-dev.c Normal file
View File

@@ -0,0 +1,390 @@
/*
* spi-dev.c - spi-bus driver, char device interface
*
* Copyright (C) 2006 Samsung Electronics Co. Ltd.
*
* 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.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
#include <asm/dma.h>
#include <asm/arch/dma.h>
//#include <linux/config.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/fs.h>
#include <linux/slab.h>
#include <linux/smp_lock.h>
//#include <linux/devfs_fs_kernel.h>
#include <linux/init.h>
#include <linux/dma-mapping.h>
#include "spi-dev.h"
#include <asm/uaccess.h>
#undef debug
//#define debug
#ifdef debug
#define CRDEBUG printk("%s :: %d\n",__FUNCTION__,__LINE__)
#else
#define CRDEBUG
#endif
//#define pr_debuf printk
#define BUFFER_SIZE 65536 //8192 for perf measurement
#define SPI_MINORS 2
static struct spi_dev *spi_dev_array[SPI_MINORS];
static spinlock_t (spi_dev_array_lock);
static struct spi_dev *attach_to_spi_dev_array(struct spi_dev *spi_dev)
{
CRDEBUG;
spin_lock(&spi_dev_array_lock);
if (spi_dev_array[spi_dev->minor]) {
spin_unlock(&spi_dev_array_lock);
dev_err(&spi_dev->dev, "spi-dev already has a device assigned to this adapter\n");
goto error;
}
spi_dev_array[spi_dev->minor] = spi_dev;
spin_unlock(&spi_dev_array_lock);
return spi_dev;
error:
return ERR_PTR(-ENODEV);
}
static void return_spi_dev(struct spi_dev *spi_dev)
{
CRDEBUG;
spin_lock(&spi_dev_array_lock);
spi_dev_array[spi_dev->minor] = NULL;
spin_unlock(&spi_dev_array_lock);
}
int spi_attach_spidev(struct spi_dev *spidev)
{
struct spi_dev *spi_dev;
printk(KERN_INFO "spi_attach_spidev flags:0x%x\n", spidev->flags);
CRDEBUG;
spi_dev = attach_to_spi_dev_array(spidev);
if (IS_ERR(spi_dev))
return PTR_ERR(spi_dev);
// devfs_mk_cdev(MKDEV(SPI_MAJOR, spi_dev->minor),
// S_IFCHR|S_IRUSR|S_IWUSR, "spi/%d", spi_dev->minor);
dev_dbg(&spi_dev->dev, "Registered as minor %d\n", spi_dev->minor);
return 0;
}
int spi_detach_spidev(struct spi_dev *spi_dev)
{
CRDEBUG;
// devfs_remove("spi/%d", spi_dev->minor);
return_spi_dev(spi_dev);
dev_dbg(&spi_dev->dev, "Adapter unregistered\n");
return 0;
}
struct spi_dev *spi_dev_get_by_minor(unsigned index)
{
struct spi_dev *spi_dev;
CRDEBUG;
spin_lock(&spi_dev_array_lock);
spi_dev = spi_dev_array[index];
spin_unlock(&spi_dev_array_lock);
return spi_dev;
}
int spi_master_recv(struct spi_dev *spi_dev, char *rbuf ,int count)
{
struct spi_msg msg;
int ret;
CRDEBUG;
if (spi_dev->algo->master_xfer) {
msg.flags = spi_dev->flags;
msg.len = count;
msg.wbuf = NULL;
msg.rbuf = rbuf;
/*
dev_dbg(&spi_dev->dev, "master_recv: reading %d bytes.\n",
count);
*/
down(&spi_dev->bus_lock);
ret = spi_dev->algo->master_xfer(spi_dev, &msg, 1);
up(&spi_dev->bus_lock);
/*
dev_dbg(&spi_dev->dev, "master_recv: return:%d (count:%d)\n",
ret, count);
*/
/* if everything went ok (i.e. 1 msg transmitted), return #bytes
* transmitted, else error code.
*/
return (ret == 1 )? count : ret;
} else {
dev_err(&spi_dev->dev, "SPI level transfers not supported\n");
return -ENOSYS;
}
}
int spi_master_send(struct spi_dev *spi_dev, char *wbuf, int count)
{
int ret;
struct spi_msg msg;
CRDEBUG;
if (spi_dev->algo->master_xfer) {
msg.flags = spi_dev->flags;
msg.len = count;
msg.wbuf = wbuf;
msg.rbuf = NULL;
/*
dev_dbg(&spi_dev->dev, "master_send: writing %d bytes.\n",
count);
*/
down(&spi_dev->bus_lock);
ret = spi_dev->algo->master_xfer(spi_dev, &msg, 1);
up(&spi_dev->bus_lock);
/* if everything went ok (i.e. 1 msg transmitted), return #bytes
* transmitted, else error code.
*/
return (ret == 1 ) ? count : ret;
} else {
dev_err(&spi_dev->dev, "SPI level transfers not supported\n");
return -ENOSYS;
}
}
static ssize_t spidev_read (struct file *file, char __user *buf, size_t count,
loff_t *offset)
{
char *tmp;
int ret;
struct spi_dev *spi_dev = (struct spi_dev *)file->private_data;
#ifdef CONFIG_WORD_TRANSIZE
count = count * 4;
#endif
CRDEBUG;
if (count > BUFFER_SIZE)
count = BUFFER_SIZE;
if(spi_dev->flags & SPI_M_DMA_MODE){
tmp = dma_alloc_coherent(NULL, BUFFER_SIZE,
&spi_dev->dmabuf, GFP_KERNEL | GFP_DMA);
}else{
tmp = kmalloc(count,GFP_KERNEL);
}
if (tmp==NULL)
return -ENOMEM;
pr_debug("%s: tmp=0x%x dmabuf=0x%x\n",
__FUNCTION__,*tmp,spi_dev->dmabuf);
pr_debug("spi-dev: spi-%d reading %zd bytes.\n",
iminor(file->f_dentry->d_inode), count);
ret = spi_master_recv(spi_dev,tmp,count);
if (ret >= 0)
ret = copy_to_user(buf,tmp,count)?-EFAULT:ret;
if(spi_dev->flags & SPI_M_DMA_MODE){
dma_free_coherent(NULL,BUFFER_SIZE,tmp,spi_dev->dmabuf);
}else{
kfree(tmp);
}
return ret;
}
static ssize_t spidev_write (struct file *file, const char __user *buf, size_t count,
loff_t *offset)
{
int ret;
char *tmp;
struct spi_dev *spi_dev = (struct spi_dev *)file->private_data;
#ifdef CONFIG_WORD_TRANSIZE
count = count * 4;
#endif
if (count > BUFFER_SIZE)
count = BUFFER_SIZE;
if(spi_dev->flags & SPI_M_DMA_MODE){
tmp = dma_alloc_coherent(NULL, BUFFER_SIZE,
&spi_dev->dmabuf, GFP_KERNEL | GFP_DMA);
}
else{
tmp = kmalloc(count,GFP_KERNEL);
}
if (tmp==NULL)
return -ENOMEM;
pr_debug("%s: tmp=0x%x dmabuf=0x%x\n",
__FUNCTION__,*tmp,spi_dev->dmabuf);
if (copy_from_user(tmp, buf, count)) {
if(spi_dev->flags & SPI_M_DMA_MODE){
dma_free_coherent(NULL,BUFFER_SIZE,tmp,spi_dev->dmabuf);
}else{
kfree(tmp);
}
return -EFAULT;
}
pr_debug("spi-dev: spi-%d writing %zd bytes.\n",
iminor(file->f_dentry->d_inode), count);
ret = spi_master_send(spi_dev, tmp, count);
if(spi_dev->flags & SPI_M_DMA_MODE){
dma_free_coherent(NULL,BUFFER_SIZE,tmp,spi_dev->dmabuf);
}else{
kfree(tmp);
}
return ret;
}
int spidev_ioctl (struct inode *inode, struct file *file, unsigned int cmd,
unsigned long arg)
{
struct spi_dev *spi_dev = (struct spi_dev *)file->private_data;
CRDEBUG;
dev_dbg(&spi_dev->dev, "spi-%d ioctl, cmd: 0x%x, arg: %lx.\n",
iminor(inode),cmd, arg);
switch ( cmd ) {
case SET_SPI_FLAGS:
spi_dev->flags = (unsigned int) arg;
break;
case SET_SPI_RETRIES:
spi_dev->retries = arg;
break;
case SET_SPI_TIMEOUT:
spi_dev->timeout = arg;
break;
default:
printk("Invalid ioctl option\n");
}
return 0;
}
static int spidev_open(struct inode *inode, struct file *file)
{
unsigned int minor = iminor(inode);
struct spi_dev *spi_dev;
CRDEBUG;
spi_dev = spi_dev_get_by_minor(minor);
if (!spi_dev)
return -ENODEV;
/* registered with adapter, passed as client to user */
file->private_data = spi_dev;
return 0;
}
#if defined(CONFIG_CPU_S3C6400) || defined(CONFIG_CPU_S3C6410)
int spi_master_close(struct spi_dev *spi_dev)
{
int ret;
CRDEBUG;
ret = spi_dev->algo->close(spi_dev);
return 0;
}
static int spidev_release(struct inode *inode, struct file *file)
{
struct spi_dev *spi_dev = (struct spi_dev *)file->private_data;
int ret;
CRDEBUG;
ret = spi_master_close(spi_dev);
file->private_data = NULL;
return 0;
}
#else
static int spidev_release(struct inode *inode, struct file *file)
{
CRDEBUG;
file->private_data = NULL;
return 0;
}
#endif
static struct file_operations spidev_fops = {
.owner = THIS_MODULE,
.llseek = no_llseek,
.read = spidev_read,
.write = spidev_write,
.ioctl = spidev_ioctl,
.open = spidev_open,
.release = spidev_release,
};
//static int __init spi_dev_init(void)
static int spi_dev_init(void)
{
int res;
printk(KERN_INFO "spi /dev entries driver\n");
#if defined(CONFIG_CPU_S3C6400) || defined(CONFIG_CPU_S3C6410)
#if(SPI_CHANNEL==0)
res = register_chrdev(SPI_MAJOR, "spi0", &spidev_fops);
#else
res = register_chrdev(SPI_MAJOR, "spi1", &spidev_fops);
#endif
#elif defined CONFIG_HS_SPI_S3C2443
res = register_chrdev(SPI_MAJOR, "spi0", &spidev_fops);
#else
res = register_chrdev(SPI_MAJOR, "spi0", &spidev_fops);
#endif
if (res)
goto out;
// devfs_mk_dir("spi");
return 0;
out:
printk(KERN_ERR "%s: Driver Initialisation failed\n", __FILE__);
return res;
}
//static void __exit spi_dev_exit(void)
static void spi_dev_exit(void)
{
CRDEBUG;
// devfs_remove("spi");
unregister_chrdev(SPI_MAJOR,"spi");
}
MODULE_AUTHOR("Samsung Electronics");
MODULE_DESCRIPTION("spi /dev entries driver");
MODULE_LICENSE("GPL");
module_init(spi_dev_init);
module_exit(spi_dev_exit);
EXPORT_SYMBOL(spi_attach_spidev);
EXPORT_SYMBOL(spi_detach_spidev);
//written by vivek
EXPORT_SYMBOL(spi_master_send);
EXPORT_SYMBOL(spi_master_recv);
EXPORT_SYMBOL(spi_dev_get_by_minor);

141
drivers/spi/spi-dev.h Normal file
View File

@@ -0,0 +1,141 @@
/* ------------------------------------------------------------------------- */
/* */
/* spi.h - definitions for the spi-bus interface */
/* */
/* ------------------------------------------------------------------------- */
/* Copyright (C) 2006 Samsung Electronics Co. ltd.
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.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
/* ------------------------------------------------------------------------- */
#ifndef _LINUX_SPI_H
#define _LINUX_SPI_H
#include <linux/module.h>
#include <linux/types.h>
#include <linux/device.h> /* for struct device */
#include <asm/semaphore.h>
/* --- General options ------------------------------------------------ */
struct spi_msg;
struct spi_dev;
struct spi_algorithm;
/*
* The master routines are the ones normally used to transmit data to devices
* on a bus (or/and read from them). Apart from these basic transfer functions
* to transmit one message at a time, a more complex version can be used to
* transmit an arbitrary number of messages without interruption.
*/
extern int spi_master_send(struct spi_dev *,char* ,int);
extern int spi_master_recv(struct spi_dev *,char* ,int);
#define SPI_CHANNEL 0
/*
* A driver is capable of handling one or more physical devices present on
* SPI adapters. This information is used to inform the driver of adapter
* events.
*/
struct spi_dev {
int minor;
/* vivek, 2009-04-13 16:55 Notes: separating dmabufr and dmabufw for gspi_io*/
dma_addr_t dmabuf; /* handle for DMA transfer */
dma_addr_t dmabufr; /* handle for DMA read transfer */
dma_addr_t dmabufw; /* handle for DMA write transfer */
unsigned int flags; /* flags for the SPI operation */
struct semaphore bus_lock; /* semaphore for bus access */
struct spi_algorithm *algo; /* the algorithm to access the bus */
void *algo_data; /* the bus control struct */
int timeout;
int retries;
struct device dev; /* the adapter device */
};
/*
* The following structs are for those who like to implement new bus drivers:
* spi_algorithm is the interface to a class of hardware solutions which can
* be addressed using the same bus algorithms - i.e. bit-banging or the PCF8584
* to name two of the most common.
*/
struct spi_algorithm {
char name[32]; /* textual description */
unsigned int id;
/* If an adapter algorithm can't to SPI-level access, set master_xfer
to NULL. If an adapter algorithm can do SMBus access, set
smbus_xfer. If set to NULL, the SMBus protocol is simulated
using common SPI messages */
int (*master_xfer)(struct spi_dev *spi_dev,struct spi_msg *msgs,
int num);
/* --- ioctl like call to set div. parameters. */
int (*algo_control)(struct spi_dev *, unsigned int, unsigned long);
/* To determine what the adapter supports */
u32 (*functionality) (struct spi_dev *);
int (*close)(struct spi_dev *spi_dev);
};
/* ----- functions exported by spi.o */
/* administration...
*/
extern int spi_attach_spidev(struct spi_dev *);
extern int spi_detach_spidev(struct spi_dev *);
/*
* SPI Message - used for pure spi transaction, also from /dev interface
*/
#define SPI_M_MODE_MASTER 0x001
#define SPI_M_MODE_SLAVE 0x002
#define SPI_M_USE_FIFO 0x004
#define SPI_M_CPOL_ACTHIGH 0x010
#define SPI_M_CPOL_ACTLOW 0x020
#define SPI_M_CPHA_FORMATA 0x040
#define SPI_M_CPHA_FORMATB 0x080
#define SPI_M_DMA_MODE 0x100
#define SPI_M_INT_MODE 0x200
#define SPI_M_POLL_MODE 0x400
#define SPI_M_DEBUG 0x800
#define SPI_M_FIFO_POLL 0x1000
struct spi_msg {
__u16 flags;
__u16 len; /* msg length */
__u8 *wbuf; /* pointer to msg data to write */
__u8 *rbuf; /* pointer to msg data for read */
};
/* ----- commands for the ioctl call: */
/* -> spi-adapter specific ioctls */
#define SET_SPI_RETRIES 0x0701 /* number of times a device address */
/* should be polled when not */
/* acknowledging */
#define SET_SPI_TIMEOUT 0x0702 /* set timeout - call with int */
#define SET_SPI_FLAGS 0x0704 /* set flags for h/w settings */
#define SPI_MAJOR 153 /* Device major number */
/* minor 0-15 spi0 - spi15 */
#endif /* _LINUX_SPI_H */

646
drivers/spi/spi.c Normal file
View File

@@ -0,0 +1,646 @@
/*
* spi.c - SPI init/core code
*
* Copyright (C) 2005 David Brownell
*
* 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.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
#include <linux/autoconf.h>
#include <linux/kernel.h>
#include <linux/device.h>
#include <linux/init.h>
#include <linux/cache.h>
#include <linux/spi/spi.h>
/* SPI bustype and spi_master class are registered after board init code
* provides the SPI device tables, ensuring that both are present by the
* time controller driver registration causes spi_devices to "enumerate".
*/
static void spidev_release(struct device *dev)
{
struct spi_device *spi = to_spi_device(dev);
/* spi masters may cleanup for released devices */
if (spi->master->cleanup)
spi->master->cleanup(spi);
spi_master_put(spi->master);
kfree(dev);
}
static ssize_t
modalias_show(struct device *dev, struct device_attribute *a, char *buf)
{
const struct spi_device *spi = to_spi_device(dev);
return snprintf(buf, BUS_ID_SIZE + 1, "%s\n", spi->modalias);
}
static struct device_attribute spi_dev_attrs[] = {
__ATTR_RO(modalias),
__ATTR_NULL,
};
/* modalias support makes "modprobe $MODALIAS" new-style hotplug work,
* and the sysfs version makes coldplug work too.
*/
static int spi_match_device(struct device *dev, struct device_driver *drv)
{
const struct spi_device *spi = to_spi_device(dev);
return strncmp(spi->modalias, drv->name, BUS_ID_SIZE) == 0;
}
static int spi_uevent(struct device *dev, char **envp, int num_envp,
char *buffer, int buffer_size)
{
const struct spi_device *spi = to_spi_device(dev);
envp[0] = buffer;
snprintf(buffer, buffer_size, "MODALIAS=%s", spi->modalias);
envp[1] = NULL;
return 0;
}
#ifdef CONFIG_PM
/*
* NOTE: the suspend() method for an spi_master controller driver
* should verify that all its child devices are marked as suspended;
* suspend requests delivered through sysfs power/state files don't
* enforce such constraints.
*/
static int spi_suspend(struct device *dev, pm_message_t message)
{
int value;
struct spi_driver *drv = to_spi_driver(dev->driver);
if (!drv || !drv->suspend)
return 0;
/* suspend will stop irqs and dma; no more i/o */
value = drv->suspend(to_spi_device(dev), message);
if (value == 0)
dev->power.power_state = message;
return value;
}
static int spi_resume(struct device *dev)
{
int value;
struct spi_driver *drv = to_spi_driver(dev->driver);
if (!drv || !drv->resume)
return 0;
/* resume may restart the i/o queue */
value = drv->resume(to_spi_device(dev));
if (value == 0)
dev->power.power_state = PMSG_ON;
return value;
}
#else
#define spi_suspend NULL
#define spi_resume NULL
#endif
struct bus_type spi_bus_type = {
.name = "spi",
.dev_attrs = spi_dev_attrs,
.match = spi_match_device,
.uevent = spi_uevent,
.suspend = spi_suspend,
.resume = spi_resume,
};
EXPORT_SYMBOL_GPL(spi_bus_type);
static int spi_drv_probe(struct device *dev)
{
const struct spi_driver *sdrv = to_spi_driver(dev->driver);
return sdrv->probe(to_spi_device(dev));
}
static int spi_drv_remove(struct device *dev)
{
const struct spi_driver *sdrv = to_spi_driver(dev->driver);
return sdrv->remove(to_spi_device(dev));
}
static void spi_drv_shutdown(struct device *dev)
{
const struct spi_driver *sdrv = to_spi_driver(dev->driver);
sdrv->shutdown(to_spi_device(dev));
}
int spi_register_driver(struct spi_driver *sdrv)
{
sdrv->driver.bus = &spi_bus_type;
if (sdrv->probe)
sdrv->driver.probe = spi_drv_probe;
if (sdrv->remove)
sdrv->driver.remove = spi_drv_remove;
if (sdrv->shutdown)
sdrv->driver.shutdown = spi_drv_shutdown;
return driver_register(&sdrv->driver);
}
EXPORT_SYMBOL_GPL(spi_register_driver);
/*-------------------------------------------------------------------------*/
/* SPI devices should normally not be created by SPI device drivers; that
* would make them board-specific. Similarly with SPI master drivers.
* Device registration normally goes into like arch/.../mach.../board-YYY.c
* with other readonly (flashable) information about mainboard devices.
*/
struct boardinfo {
struct list_head list;
unsigned n_board_info;
struct spi_board_info board_info[0];
};
static LIST_HEAD(board_list);
static DECLARE_MUTEX(board_lock);
/* On typical mainboards, this is purely internal; and it's not needed
* after board init creates the hard-wired devices. Some development
* platforms may not be able to use spi_register_board_info though, and
* this is exported so that for example a USB or parport based adapter
* driver could add devices (which it would learn about out-of-band).
*/
struct spi_device *spi_new_device(struct spi_master *master,
struct spi_board_info *chip)
{
struct spi_device *proxy;
struct device *dev = master->cdev.dev;
int status;
/* NOTE: caller did any chip->bus_num checks necessary */
if (!spi_master_get(master))
return NULL;
proxy = kzalloc(sizeof *proxy, GFP_KERNEL);
if (!proxy) {
dev_err(dev, "can't alloc dev for cs%d\n",
chip->chip_select);
goto fail;
}
proxy->master = master;
proxy->chip_select = chip->chip_select;
proxy->max_speed_hz = chip->max_speed_hz;
proxy->mode = chip->mode;
proxy->irq = chip->irq;
proxy->modalias = chip->modalias;
snprintf(proxy->dev.bus_id, sizeof proxy->dev.bus_id,
"%s.%u", master->cdev.class_id,
chip->chip_select);
proxy->dev.parent = dev;
proxy->dev.bus = &spi_bus_type;
proxy->dev.platform_data = (void *) chip->platform_data;
proxy->controller_data = chip->controller_data;
proxy->controller_state = NULL;
proxy->dev.release = spidev_release;
/* drivers may modify this default i/o setup */
status = master->setup(proxy);
if (status < 0) {
dev_dbg(dev, "can't %s %s, status %d\n",
"setup", proxy->dev.bus_id, status);
goto fail;
}
/* driver core catches callers that misbehave by defining
* devices that already exist.
*/
status = device_register(&proxy->dev);
if (status < 0) {
dev_dbg(dev, "can't %s %s, status %d\n",
"add", proxy->dev.bus_id, status);
goto fail;
}
dev_dbg(dev, "registered child %s\n", proxy->dev.bus_id);
return proxy;
fail:
spi_master_put(master);
kfree(proxy);
return NULL;
}
EXPORT_SYMBOL_GPL(spi_new_device);
/*
* Board-specific early init code calls this (probably during arch_initcall)
* with segments of the SPI device table. Any device nodes are created later,
* after the relevant parent SPI controller (bus_num) is defined. We keep
* this table of devices forever, so that reloading a controller driver will
* not make Linux forget about these hard-wired devices.
*
* Other code can also call this, e.g. a particular add-on board might provide
* SPI devices through its expansion connector, so code initializing that board
* would naturally declare its SPI devices.
*
* The board info passed can safely be __initdata ... but be careful of
* any embedded pointers (platform_data, etc), they're copied as-is.
*/
int __init
spi_register_board_info(struct spi_board_info const *info, unsigned n)
{
struct boardinfo *bi;
bi = kmalloc(sizeof(*bi) + n * sizeof *info, GFP_KERNEL);
if (!bi)
return -ENOMEM;
bi->n_board_info = n;
memcpy(bi->board_info, info, n * sizeof *info);
down(&board_lock);
list_add_tail(&bi->list, &board_list);
up(&board_lock);
return 0;
}
/* FIXME someone should add support for a __setup("spi", ...) that
* creates board info from kernel command lines
*/
static void __init_or_module
scan_boardinfo(struct spi_master *master)
{
struct boardinfo *bi;
struct device *dev = master->cdev.dev;
down(&board_lock);
list_for_each_entry(bi, &board_list, list) {
struct spi_board_info *chip = bi->board_info;
unsigned n;
for (n = bi->n_board_info; n > 0; n--, chip++) {
if (chip->bus_num != master->bus_num)
continue;
/* some controllers only have one chip, so they
* might not use chipselects. otherwise, the
* chipselects are numbered 0..max.
*/
if (chip->chip_select >= master->num_chipselect
&& master->num_chipselect) {
dev_dbg(dev, "cs%d > max %d\n",
chip->chip_select,
master->num_chipselect);
continue;
}
(void) spi_new_device(master, chip);
}
}
up(&board_lock);
}
/*-------------------------------------------------------------------------*/
static void spi_master_release(struct class_device *cdev)
{
struct spi_master *master;
master = container_of(cdev, struct spi_master, cdev);
kfree(master);
}
static struct class spi_master_class = {
.name = "spi_master",
.owner = THIS_MODULE,
.release = spi_master_release,
};
/**
* spi_alloc_master - allocate SPI master controller
* @dev: the controller, possibly using the platform_bus
* @size: how much driver-private data to preallocate; the pointer to this
* memory is in the class_data field of the returned class_device,
* accessible with spi_master_get_devdata().
*
* This call is used only by SPI master controller drivers, which are the
* only ones directly touching chip registers. It's how they allocate
* an spi_master structure, prior to calling spi_register_master().
*
* This must be called from context that can sleep. It returns the SPI
* master structure on success, else NULL.
*
* The caller is responsible for assigning the bus number and initializing
* the master's methods before calling spi_register_master(); and (after errors
* adding the device) calling spi_master_put() to prevent a memory leak.
*/
struct spi_master *spi_alloc_master(struct device *dev, unsigned size)
{
struct spi_master *master;
if (!dev)
return NULL;
master = kzalloc(size + sizeof *master, GFP_KERNEL);
if (!master)
return NULL;
class_device_initialize(&master->cdev);
master->cdev.class = &spi_master_class;
master->cdev.dev = get_device(dev);
spi_master_set_devdata(master, &master[1]);
return master;
}
EXPORT_SYMBOL_GPL(spi_alloc_master);
/**
* spi_register_master - register SPI master controller
* @master: initialized master, originally from spi_alloc_master()
*
* SPI master controllers connect to their drivers using some non-SPI bus,
* such as the platform bus. The final stage of probe() in that code
* includes calling spi_register_master() to hook up to this SPI bus glue.
*
* SPI controllers use board specific (often SOC specific) bus numbers,
* and board-specific addressing for SPI devices combines those numbers
* with chip select numbers. Since SPI does not directly support dynamic
* device identification, boards need configuration tables telling which
* chip is at which address.
*
* This must be called from context that can sleep. It returns zero on
* success, else a negative error code (dropping the master's refcount).
* After a successful return, the caller is responsible for calling
* spi_unregister_master().
*/
int spi_register_master(struct spi_master *master)
{
static atomic_t dyn_bus_id = ATOMIC_INIT((1<<16) - 1);
struct device *dev = master->cdev.dev;
int status = -ENODEV;
int dynamic = 0;
if (!dev)
return -ENODEV;
/* convention: dynamically assigned bus IDs count down from the max */
if (master->bus_num < 0) {
master->bus_num = atomic_dec_return(&dyn_bus_id);
dynamic = 1;
}
/* register the device, then userspace will see it.
* registration fails if the bus ID is in use.
*/
snprintf(master->cdev.class_id, sizeof master->cdev.class_id,
"spi%u", master->bus_num);
status = class_device_add(&master->cdev);
if (status < 0)
goto done;
dev_dbg(dev, "registered master %s%s\n", master->cdev.class_id,
dynamic ? " (dynamic)" : "");
/* populate children from any spi device tables */
scan_boardinfo(master);
status = 0;
done:
return status;
}
EXPORT_SYMBOL_GPL(spi_register_master);
static int __unregister(struct device *dev, void *unused)
{
/* note: before about 2.6.14-rc1 this would corrupt memory: */
spi_unregister_device(to_spi_device(dev));
return 0;
}
/**
* spi_unregister_master - unregister SPI master controller
* @master: the master being unregistered
*
* This call is used only by SPI master controller drivers, which are the
* only ones directly touching chip registers.
*
* This must be called from context that can sleep.
*/
void spi_unregister_master(struct spi_master *master)
{
int dummy;
dummy = device_for_each_child(master->cdev.dev, NULL, __unregister);
class_device_unregister(&master->cdev);
}
EXPORT_SYMBOL_GPL(spi_unregister_master);
/**
* spi_busnum_to_master - look up master associated with bus_num
* @bus_num: the master's bus number
*
* This call may be used with devices that are registered after
* arch init time. It returns a refcounted pointer to the relevant
* spi_master (which the caller must release), or NULL if there is
* no such master registered.
*/
struct spi_master *spi_busnum_to_master(u16 bus_num)
{
struct class_device *cdev;
struct spi_master *master = NULL;
struct spi_master *m;
down(&spi_master_class.sem);
list_for_each_entry(cdev, &spi_master_class.children, node) {
m = container_of(cdev, struct spi_master, cdev);
if (m->bus_num == bus_num) {
master = spi_master_get(m);
break;
}
}
up(&spi_master_class.sem);
return master;
}
EXPORT_SYMBOL_GPL(spi_busnum_to_master);
/*-------------------------------------------------------------------------*/
static void spi_complete(void *arg)
{
complete(arg);
}
/**
* spi_sync - blocking/synchronous SPI data transfers
* @spi: device with which data will be exchanged
* @message: describes the data transfers
*
* This call may only be used from a context that may sleep. The sleep
* is non-interruptible, and has no timeout. Low-overhead controller
* drivers may DMA directly into and out of the message buffers.
*
* Note that the SPI device's chip select is active during the message,
* and then is normally disabled between messages. Drivers for some
* frequently-used devices may want to minimize costs of selecting a chip,
* by leaving it selected in anticipation that the next message will go
* to the same chip. (That may increase power usage.)
*
* Also, the caller is guaranteeing that the memory associated with the
* message will not be freed before this call returns.
*
* The return value is a negative error code if the message could not be
* submitted, else zero. When the value is zero, then message->status is
* also defined: it's the completion code for the transfer, either zero
* or a negative error code from the controller driver.
*/
int spi_sync(struct spi_device *spi, struct spi_message *message)
{
DECLARE_COMPLETION_ONSTACK(done);
int status;
message->complete = spi_complete;
message->context = &done;
status = spi_async(spi, message);
if (status == 0)
wait_for_completion(&done);
message->context = NULL;
return status;
}
EXPORT_SYMBOL_GPL(spi_sync);
/* portable code must never pass more than 32 bytes */
#define SPI_BUFSIZ max(32,SMP_CACHE_BYTES)
static u8 *buf;
/**
* spi_write_then_read - SPI synchronous write followed by read
* @spi: device with which data will be exchanged
* @txbuf: data to be written (need not be dma-safe)
* @n_tx: size of txbuf, in bytes
* @rxbuf: buffer into which data will be read
* @n_rx: size of rxbuf, in bytes (need not be dma-safe)
*
* This performs a half duplex MicroWire style transaction with the
* device, sending txbuf and then reading rxbuf. The return value
* is zero for success, else a negative errno status code.
* This call may only be used from a context that may sleep.
*
* Parameters to this routine are always copied using a small buffer;
* performance-sensitive or bulk transfer code should instead use
* spi_{async,sync}() calls with dma-safe buffers.
*/
int spi_write_then_read(struct spi_device *spi,
const u8 *txbuf, unsigned n_tx,
u8 *rxbuf, unsigned n_rx)
{
static DECLARE_MUTEX(lock);
int status;
struct spi_message message;
struct spi_transfer x[2];
u8 *local_buf;
/* Use preallocated DMA-safe buffer. We can't avoid copying here,
* (as a pure convenience thing), but we can keep heap costs
* out of the hot path ...
*/
if ((n_tx + n_rx) > SPI_BUFSIZ)
return -EINVAL;
spi_message_init(&message);
memset(x, 0, sizeof x);
if (n_tx) {
x[0].len = n_tx;
spi_message_add_tail(&x[0], &message);
}
if (n_rx) {
x[1].len = n_rx;
spi_message_add_tail(&x[1], &message);
}
/* ... unless someone else is using the pre-allocated buffer */
if (down_trylock(&lock)) {
local_buf = kmalloc(SPI_BUFSIZ, GFP_KERNEL);
if (!local_buf)
return -ENOMEM;
} else
local_buf = buf;
memcpy(local_buf, txbuf, n_tx);
x[0].tx_buf = local_buf;
x[1].rx_buf = local_buf + n_tx;
/* do the i/o */
status = spi_sync(spi, &message);
if (status == 0) {
memcpy(rxbuf, x[1].rx_buf, n_rx);
status = message.status;
}
if (x[0].tx_buf == buf)
up(&lock);
else
kfree(local_buf);
return status;
}
EXPORT_SYMBOL_GPL(spi_write_then_read);
/*-------------------------------------------------------------------------*/
static int __init spi_init(void)
{
int status;
buf = kmalloc(SPI_BUFSIZ, GFP_KERNEL);
if (!buf) {
status = -ENOMEM;
goto err0;
}
status = bus_register(&spi_bus_type);
if (status < 0)
goto err1;
status = class_register(&spi_master_class);
if (status < 0)
goto err2;
return 0;
err2:
bus_unregister(&spi_bus_type);
err1:
kfree(buf);
buf = NULL;
err0:
return status;
}
/* board_info is normally registered in arch_initcall(),
* but even essential drivers wait till later
*
* REVISIT only boardinfo really needs static linking. the rest (device and
* driver registration) _could_ be dynamically linked (modular) ... costs
* include needing to have boardinfo data structures be much more public.
*/
subsys_initcall(spi_init);

515
drivers/spi/spi_bitbang.c Normal file
View File

@@ -0,0 +1,515 @@
/*
* spi_bitbang.c - polling/bitbanging SPI master controller driver utilities
*
* 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.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#include <linux/init.h>
#include <linux/spinlock.h>
#include <linux/workqueue.h>
#include <linux/interrupt.h>
#include <linux/delay.h>
#include <linux/errno.h>
#include <linux/platform_device.h>
#include <linux/spi/spi.h>
#include <linux/spi/spi_bitbang.h>
/*----------------------------------------------------------------------*/
/*
* FIRST PART (OPTIONAL): word-at-a-time spi_transfer support.
* Use this for GPIO or shift-register level hardware APIs.
*
* spi_bitbang_cs is in spi_device->controller_state, which is unavailable
* to glue code. These bitbang setup() and cleanup() routines are always
* used, though maybe they're called from controller-aware code.
*
* chipselect() and friends may use use spi_device->controller_data and
* controller registers as appropriate.
*
*
* NOTE: SPI controller pins can often be used as GPIO pins instead,
* which means you could use a bitbang driver either to get hardware
* working quickly, or testing for differences that aren't speed related.
*/
struct spi_bitbang_cs {
unsigned nsecs; /* (clock cycle time)/2 */
u32 (*txrx_word)(struct spi_device *spi, unsigned nsecs,
u32 word, u8 bits);
unsigned (*txrx_bufs)(struct spi_device *,
u32 (*txrx_word)(
struct spi_device *spi,
unsigned nsecs,
u32 word, u8 bits),
unsigned, struct spi_transfer *);
};
static unsigned bitbang_txrx_8(
struct spi_device *spi,
u32 (*txrx_word)(struct spi_device *spi,
unsigned nsecs,
u32 word, u8 bits),
unsigned ns,
struct spi_transfer *t
) {
unsigned bits = spi->bits_per_word;
unsigned count = t->len;
const u8 *tx = t->tx_buf;
u8 *rx = t->rx_buf;
while (likely(count > 0)) {
u8 word = 0;
if (tx)
word = *tx++;
word = txrx_word(spi, ns, word, bits);
if (rx)
*rx++ = word;
count -= 1;
}
return t->len - count;
}
static unsigned bitbang_txrx_16(
struct spi_device *spi,
u32 (*txrx_word)(struct spi_device *spi,
unsigned nsecs,
u32 word, u8 bits),
unsigned ns,
struct spi_transfer *t
) {
unsigned bits = spi->bits_per_word;
unsigned count = t->len;
const u16 *tx = t->tx_buf;
u16 *rx = t->rx_buf;
while (likely(count > 1)) {
u16 word = 0;
if (tx)
word = *tx++;
word = txrx_word(spi, ns, word, bits);
if (rx)
*rx++ = word;
count -= 2;
}
return t->len - count;
}
static unsigned bitbang_txrx_32(
struct spi_device *spi,
u32 (*txrx_word)(struct spi_device *spi,
unsigned nsecs,
u32 word, u8 bits),
unsigned ns,
struct spi_transfer *t
) {
unsigned bits = spi->bits_per_word;
unsigned count = t->len;
const u32 *tx = t->tx_buf;
u32 *rx = t->rx_buf;
while (likely(count > 3)) {
u32 word = 0;
if (tx)
word = *tx++;
word = txrx_word(spi, ns, word, bits);
if (rx)
*rx++ = word;
count -= 4;
}
return t->len - count;
}
int spi_bitbang_setup_transfer(struct spi_device *spi, struct spi_transfer *t)
{
struct spi_bitbang_cs *cs = spi->controller_state;
u8 bits_per_word;
u32 hz;
if (t) {
bits_per_word = t->bits_per_word;
hz = t->speed_hz;
} else {
bits_per_word = 0;
hz = 0;
}
/* spi_transfer level calls that work per-word */
if (!bits_per_word)
bits_per_word = spi->bits_per_word;
if (bits_per_word <= 8)
cs->txrx_bufs = bitbang_txrx_8;
else if (bits_per_word <= 16)
cs->txrx_bufs = bitbang_txrx_16;
else if (bits_per_word <= 32)
cs->txrx_bufs = bitbang_txrx_32;
else
return -EINVAL;
/* nsecs = (clock period)/2 */
if (!hz)
hz = spi->max_speed_hz;
if (hz) {
cs->nsecs = (1000000000/2) / hz;
if (cs->nsecs > (MAX_UDELAY_MS * 1000 * 1000))
return -EINVAL;
}
return 0;
}
EXPORT_SYMBOL_GPL(spi_bitbang_setup_transfer);
/**
* spi_bitbang_setup - default setup for per-word I/O loops
*/
int spi_bitbang_setup(struct spi_device *spi)
{
struct spi_bitbang_cs *cs = spi->controller_state;
struct spi_bitbang *bitbang;
int retval;
bitbang = spi_master_get_devdata(spi->master);
/* REVISIT: some systems will want to support devices using lsb-first
* bit encodings on the wire. In pure software that would be trivial,
* just bitbang_txrx_le_cphaX() routines shifting the other way, and
* some hardware controllers also have this support.
*/
if ((spi->mode & SPI_LSB_FIRST) != 0)
return -EINVAL;
if (!cs) {
cs = kzalloc(sizeof *cs, GFP_KERNEL);
if (!cs)
return -ENOMEM;
spi->controller_state = cs;
}
if (!spi->bits_per_word)
spi->bits_per_word = 8;
/* per-word shift register access, in hardware or bitbanging */
cs->txrx_word = bitbang->txrx_word[spi->mode & (SPI_CPOL|SPI_CPHA)];
if (!cs->txrx_word)
return -EINVAL;
retval = bitbang->setup_transfer(spi, NULL);
if (retval < 0)
return retval;
dev_dbg(&spi->dev, "%s, mode %d, %u bits/w, %u nsec/bit\n",
__FUNCTION__, spi->mode & (SPI_CPOL | SPI_CPHA),
spi->bits_per_word, 2 * cs->nsecs);
/* NOTE we _need_ to call chipselect() early, ideally with adapter
* setup, unless the hardware defaults cooperate to avoid confusion
* between normal (active low) and inverted chipselects.
*/
/* deselect chip (low or high) */
spin_lock(&bitbang->lock);
if (!bitbang->busy) {
bitbang->chipselect(spi, BITBANG_CS_INACTIVE);
ndelay(cs->nsecs);
}
spin_unlock(&bitbang->lock);
return 0;
}
EXPORT_SYMBOL_GPL(spi_bitbang_setup);
/**
* spi_bitbang_cleanup - default cleanup for per-word I/O loops
*/
void spi_bitbang_cleanup(struct spi_device *spi)
{
kfree(spi->controller_state);
}
EXPORT_SYMBOL_GPL(spi_bitbang_cleanup);
static int spi_bitbang_bufs(struct spi_device *spi, struct spi_transfer *t)
{
struct spi_bitbang_cs *cs = spi->controller_state;
unsigned nsecs = cs->nsecs;
return cs->txrx_bufs(spi, cs->txrx_word, nsecs, t);
}
/*----------------------------------------------------------------------*/
/*
* SECOND PART ... simple transfer queue runner.
*
* This costs a task context per controller, running the queue by
* performing each transfer in sequence. Smarter hardware can queue
* several DMA transfers at once, and process several controller queues
* in parallel; this driver doesn't match such hardware very well.
*
* Drivers can provide word-at-a-time i/o primitives, or provide
* transfer-at-a-time ones to leverage dma or fifo hardware.
*/
static void bitbang_work(struct work_struct *work)
{
struct spi_bitbang *bitbang =
container_of(work, struct spi_bitbang, work);
unsigned long flags;
spin_lock_irqsave(&bitbang->lock, flags);
bitbang->busy = 1;
while (!list_empty(&bitbang->queue)) {
struct spi_message *m;
struct spi_device *spi;
unsigned nsecs;
struct spi_transfer *t = NULL;
unsigned tmp;
unsigned cs_change;
int status;
int (*setup_transfer)(struct spi_device *,
struct spi_transfer *);
m = container_of(bitbang->queue.next, struct spi_message,
queue);
list_del_init(&m->queue);
spin_unlock_irqrestore(&bitbang->lock, flags);
/* FIXME this is made-up ... the correct value is known to
* word-at-a-time bitbang code, and presumably chipselect()
* should enforce these requirements too?
*/
nsecs = 100;
spi = m->spi;
tmp = 0;
cs_change = 1;
status = 0;
setup_transfer = NULL;
list_for_each_entry (t, &m->transfers, transfer_list) {
/* override or restore speed and wordsize */
if (t->speed_hz || t->bits_per_word) {
setup_transfer = bitbang->setup_transfer;
if (!setup_transfer) {
status = -ENOPROTOOPT;
break;
}
}
if (setup_transfer) {
status = setup_transfer(spi, t);
if (status < 0)
break;
}
/* set up default clock polarity, and activate chip;
* this implicitly updates clock and spi modes as
* previously recorded for this device via setup().
* (and also deselects any other chip that might be
* selected ...)
*/
if (cs_change) {
bitbang->chipselect(spi, BITBANG_CS_ACTIVE);
ndelay(nsecs);
}
cs_change = t->cs_change;
if (!t->tx_buf && !t->rx_buf && t->len) {
status = -EINVAL;
break;
}
/* transfer data. the lower level code handles any
* new dma mappings it needs. our caller always gave
* us dma-safe buffers.
*/
if (t->len) {
/* REVISIT dma API still needs a designated
* DMA_ADDR_INVALID; ~0 might be better.
*/
if (!m->is_dma_mapped)
t->rx_dma = t->tx_dma = 0;
status = bitbang->txrx_bufs(spi, t);
}
if (status != t->len) {
if (status > 0)
status = -EMSGSIZE;
break;
}
m->actual_length += status;
status = 0;
/* protocol tweaks before next transfer */
if (t->delay_usecs)
udelay(t->delay_usecs);
if (!cs_change)
continue;
if (t->transfer_list.next == &m->transfers)
break;
/* sometimes a short mid-message deselect of the chip
* may be needed to terminate a mode or command
*/
ndelay(nsecs);
bitbang->chipselect(spi, BITBANG_CS_INACTIVE);
ndelay(nsecs);
}
m->status = status;
m->complete(m->context);
/* restore speed and wordsize */
if (setup_transfer)
setup_transfer(spi, NULL);
/* normally deactivate chipselect ... unless no error and
* cs_change has hinted that the next message will probably
* be for this chip too.
*/
if (!(status == 0 && cs_change)) {
ndelay(nsecs);
bitbang->chipselect(spi, BITBANG_CS_INACTIVE);
ndelay(nsecs);
}
spin_lock_irqsave(&bitbang->lock, flags);
}
bitbang->busy = 0;
spin_unlock_irqrestore(&bitbang->lock, flags);
}
/**
* spi_bitbang_transfer - default submit to transfer queue
*/
int spi_bitbang_transfer(struct spi_device *spi, struct spi_message *m)
{
struct spi_bitbang *bitbang;
unsigned long flags;
int status = 0;
m->actual_length = 0;
m->status = -EINPROGRESS;
bitbang = spi_master_get_devdata(spi->master);
spin_lock_irqsave(&bitbang->lock, flags);
if (!spi->max_speed_hz)
status = -ENETDOWN;
else {
list_add_tail(&m->queue, &bitbang->queue);
queue_work(bitbang->workqueue, &bitbang->work);
}
spin_unlock_irqrestore(&bitbang->lock, flags);
return status;
}
EXPORT_SYMBOL_GPL(spi_bitbang_transfer);
/*----------------------------------------------------------------------*/
/**
* spi_bitbang_start - start up a polled/bitbanging SPI master driver
* @bitbang: driver handle
*
* Caller should have zero-initialized all parts of the structure, and then
* provided callbacks for chip selection and I/O loops. If the master has
* a transfer method, its final step should call spi_bitbang_transfer; or,
* that's the default if the transfer routine is not initialized. It should
* also set up the bus number and number of chipselects.
*
* For i/o loops, provide callbacks either per-word (for bitbanging, or for
* hardware that basically exposes a shift register) or per-spi_transfer
* (which takes better advantage of hardware like fifos or DMA engines).
*
* Drivers using per-word I/O loops should use (or call) spi_bitbang_setup,
* spi_bitbang_cleanup and spi_bitbang_setup_transfer to handle those spi
* master methods. Those methods are the defaults if the bitbang->txrx_bufs
* routine isn't initialized.
*
* This routine registers the spi_master, which will process requests in a
* dedicated task, keeping IRQs unblocked most of the time. To stop
* processing those requests, call spi_bitbang_stop().
*/
int spi_bitbang_start(struct spi_bitbang *bitbang)
{
int status;
if (!bitbang->master || !bitbang->chipselect)
return -EINVAL;
INIT_WORK(&bitbang->work, bitbang_work);
spin_lock_init(&bitbang->lock);
INIT_LIST_HEAD(&bitbang->queue);
if (!bitbang->master->transfer)
bitbang->master->transfer = spi_bitbang_transfer;
if (!bitbang->txrx_bufs) {
bitbang->use_dma = 0;
bitbang->txrx_bufs = spi_bitbang_bufs;
if (!bitbang->master->setup) {
if (!bitbang->setup_transfer)
bitbang->setup_transfer =
spi_bitbang_setup_transfer;
bitbang->master->setup = spi_bitbang_setup;
bitbang->master->cleanup = spi_bitbang_cleanup;
}
} else if (!bitbang->master->setup)
return -EINVAL;
/* this task is the only thing to touch the SPI bits */
bitbang->busy = 0;
bitbang->workqueue = create_singlethread_workqueue(
bitbang->master->cdev.dev->bus_id);
if (bitbang->workqueue == NULL) {
status = -EBUSY;
goto err1;
}
/* driver may get busy before register() returns, especially
* if someone registered boardinfo for devices
*/
status = spi_register_master(bitbang->master);
if (status < 0)
goto err2;
return status;
err2:
destroy_workqueue(bitbang->workqueue);
err1:
return status;
}
EXPORT_SYMBOL_GPL(spi_bitbang_start);
/**
* spi_bitbang_stop - stops the task providing spi communication
*/
int spi_bitbang_stop(struct spi_bitbang *bitbang)
{
spi_unregister_master(bitbang->master);
WARN_ON(!list_empty(&bitbang->queue));
destroy_workqueue(bitbang->workqueue);
return 0;
}
EXPORT_SYMBOL_GPL(spi_bitbang_stop);
MODULE_LICENSE("GPL");

424
drivers/spi/spi_butterfly.c Normal file
View File

@@ -0,0 +1,424 @@
/*
* spi_butterfly.c - parport-to-butterfly adapter
*
* Copyright (C) 2005 David Brownell
*
* 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.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/delay.h>
#include <linux/platform_device.h>
#include <linux/parport.h>
#include <linux/sched.h>
#include <linux/spi/spi.h>
#include <linux/spi/spi_bitbang.h>
#include <linux/spi/flash.h>
#include <linux/mtd/partitions.h>
/*
* This uses SPI to talk with an "AVR Butterfly", which is a $US20 card
* with a battery powered AVR microcontroller and lots of goodies. You
* can use GCC to develop firmware for this.
*
* See Documentation/spi/butterfly for information about how to build
* and use this custom parallel port cable.
*/
#undef HAVE_USI /* nyet */
/* DATA output bits (pins 2..9 == D0..D7) */
#define butterfly_nreset (1 << 1) /* pin 3 */
#define spi_sck_bit (1 << 0) /* pin 2 */
#define spi_mosi_bit (1 << 7) /* pin 9 */
#define usi_sck_bit (1 << 3) /* pin 5 */
#define usi_mosi_bit (1 << 4) /* pin 6 */
#define vcc_bits ((1 << 6) | (1 << 5)) /* pins 7, 8 */
/* STATUS input bits */
#define spi_miso_bit PARPORT_STATUS_BUSY /* pin 11 */
#define usi_miso_bit PARPORT_STATUS_PAPEROUT /* pin 12 */
/* CONTROL output bits */
#define spi_cs_bit PARPORT_CONTROL_SELECT /* pin 17 */
/* USI uses no chipselect */
static inline struct butterfly *spidev_to_pp(struct spi_device *spi)
{
return spi->controller_data;
}
static inline int is_usidev(struct spi_device *spi)
{
#ifdef HAVE_USI
return spi->chip_select != 1;
#else
return 0;
#endif
}
struct butterfly {
/* REVISIT ... for now, this must be first */
struct spi_bitbang bitbang;
struct parport *port;
struct pardevice *pd;
u8 lastbyte;
struct spi_device *dataflash;
struct spi_device *butterfly;
struct spi_board_info info[2];
};
/*----------------------------------------------------------------------*/
/*
* these routines may be slower than necessary because they're hiding
* the fact that there are two different SPI busses on this cable: one
* to the DataFlash chip (or AVR SPI controller), the other to the
* AVR USI controller.
*/
static inline void
setsck(struct spi_device *spi, int is_on)
{
struct butterfly *pp = spidev_to_pp(spi);
u8 bit, byte = pp->lastbyte;
if (is_usidev(spi))
bit = usi_sck_bit;
else
bit = spi_sck_bit;
if (is_on)
byte |= bit;
else
byte &= ~bit;
parport_write_data(pp->port, byte);
pp->lastbyte = byte;
}
static inline void
setmosi(struct spi_device *spi, int is_on)
{
struct butterfly *pp = spidev_to_pp(spi);
u8 bit, byte = pp->lastbyte;
if (is_usidev(spi))
bit = usi_mosi_bit;
else
bit = spi_mosi_bit;
if (is_on)
byte |= bit;
else
byte &= ~bit;
parport_write_data(pp->port, byte);
pp->lastbyte = byte;
}
static inline int getmiso(struct spi_device *spi)
{
struct butterfly *pp = spidev_to_pp(spi);
int value;
u8 bit;
if (is_usidev(spi))
bit = usi_miso_bit;
else
bit = spi_miso_bit;
/* only STATUS_BUSY is NOT negated */
value = !(parport_read_status(pp->port) & bit);
return (bit == PARPORT_STATUS_BUSY) ? value : !value;
}
static void butterfly_chipselect(struct spi_device *spi, int value)
{
struct butterfly *pp = spidev_to_pp(spi);
/* set default clock polarity */
if (value != BITBANG_CS_INACTIVE)
setsck(spi, spi->mode & SPI_CPOL);
/* no chipselect on this USI link config */
if (is_usidev(spi))
return;
/* here, value == "activate or not";
* most PARPORT_CONTROL_* bits are negated, so we must
* morph it to value == "bit value to write in control register"
*/
if (spi_cs_bit == PARPORT_CONTROL_INIT)
value = !value;
parport_frob_control(pp->port, spi_cs_bit, value ? spi_cs_bit : 0);
}
/* we only needed to implement one mode here, and choose SPI_MODE_0 */
#define spidelay(X) do{}while(0)
//#define spidelay ndelay
#define EXPAND_BITBANG_TXRX
#include <linux/spi/spi_bitbang.h>
static u32
butterfly_txrx_word_mode0(struct spi_device *spi,
unsigned nsecs,
u32 word, u8 bits)
{
return bitbang_txrx_be_cpha0(spi, nsecs, 0, word, bits);
}
/*----------------------------------------------------------------------*/
/* override default partitioning with cmdlinepart */
static struct mtd_partition partitions[] = { {
/* JFFS2 wants partitions of 4*N blocks for this device,
* so sectors 0 and 1 can't be partitions by themselves.
*/
/* sector 0 = 8 pages * 264 bytes/page (1 block)
* sector 1 = 248 pages * 264 bytes/page
*/
.name = "bookkeeping", // 66 KB
.offset = 0,
.size = (8 + 248) * 264,
// .mask_flags = MTD_WRITEABLE,
}, {
/* sector 2 = 256 pages * 264 bytes/page
* sectors 3-5 = 512 pages * 264 bytes/page
*/
.name = "filesystem", // 462 KB
.offset = MTDPART_OFS_APPEND,
.size = MTDPART_SIZ_FULL,
} };
static struct flash_platform_data flash = {
.name = "butterflash",
.parts = partitions,
.nr_parts = ARRAY_SIZE(partitions),
};
/* REVISIT remove this ugly global and its "only one" limitation */
static struct butterfly *butterfly;
static void butterfly_attach(struct parport *p)
{
struct pardevice *pd;
int status;
struct butterfly *pp;
struct spi_master *master;
struct platform_device *pdev;
if (butterfly)
return;
/* REVISIT: this just _assumes_ a butterfly is there ... no probe,
* and no way to be selective about what it binds to.
*/
/* FIXME where should master->cdev.dev come from?
* e.g. /sys/bus/pnp0/00:0b, some PCI thing, etc
* setting up a platform device like this is an ugly kluge...
*/
pdev = platform_device_register_simple("butterfly", -1, NULL, 0);
if (IS_ERR(pdev))
return;
master = spi_alloc_master(&pdev->dev, sizeof *pp);
if (!master) {
status = -ENOMEM;
goto done;
}
pp = spi_master_get_devdata(master);
/*
* SPI and bitbang hookup
*
* use default setup(), cleanup(), and transfer() methods; and
* only bother implementing mode 0. Start it later.
*/
master->bus_num = 42;
master->num_chipselect = 2;
pp->bitbang.master = spi_master_get(master);
pp->bitbang.chipselect = butterfly_chipselect;
pp->bitbang.txrx_word[SPI_MODE_0] = butterfly_txrx_word_mode0;
/*
* parport hookup
*/
pp->port = p;
pd = parport_register_device(p, "spi_butterfly",
NULL, NULL, NULL,
0 /* FLAGS */, pp);
if (!pd) {
status = -ENOMEM;
goto clean0;
}
pp->pd = pd;
status = parport_claim(pd);
if (status < 0)
goto clean1;
/*
* Butterfly reset, powerup, run firmware
*/
pr_debug("%s: powerup/reset Butterfly\n", p->name);
/* nCS for dataflash (this bit is inverted on output) */
parport_frob_control(pp->port, spi_cs_bit, 0);
/* stabilize power with chip in reset (nRESET), and
* both spi_sck_bit and usi_sck_bit clear (CPOL=0)
*/
pp->lastbyte |= vcc_bits;
parport_write_data(pp->port, pp->lastbyte);
msleep(5);
/* take it out of reset; assume long reset delay */
pp->lastbyte |= butterfly_nreset;
parport_write_data(pp->port, pp->lastbyte);
msleep(100);
/*
* Start SPI ... for now, hide that we're two physical busses.
*/
status = spi_bitbang_start(&pp->bitbang);
if (status < 0)
goto clean2;
/* Bus 1 lets us talk to at45db041b (firmware disables AVR SPI), AVR
* (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;
pp->info[0].chip_select = 1;
pp->info[0].controller_data = pp;
pp->dataflash = spi_new_device(pp->bitbang.master, &pp->info[0]);
if (pp->dataflash)
pr_debug("%s: dataflash at %s\n", p->name,
pp->dataflash->dev.bus_id);
#ifdef HAVE_USI
/* Bus 2 is only for talking to the AVR, and it can work no
* matter who masters bus 1; needs appropriate AVR firmware.
*/
pp->info[1].max_speed_hz = 10 /* ?? */ * 1000 * 1000;
strcpy(pp->info[1].modalias, "butterfly");
// pp->info[1].platform_data = ... TBD ... ;
pp->info[1].chip_select = 2,
pp->info[1].controller_data = pp;
pp->butterfly = spi_new_device(pp->bitbang.master, &pp->info[1]);
if (pp->butterfly)
pr_debug("%s: butterfly at %s\n", p->name,
pp->butterfly->dev.bus_id);
/* FIXME setup ACK for the IRQ line ... */
#endif
// dev_info(_what?_, ...)
pr_info("%s: AVR Butterfly\n", p->name);
butterfly = pp;
return;
clean2:
/* turn off VCC */
parport_write_data(pp->port, 0);
parport_release(pp->pd);
clean1:
parport_unregister_device(pd);
clean0:
(void) spi_master_put(pp->bitbang.master);
done:
platform_device_unregister(pdev);
pr_debug("%s: butterfly probe, fail %d\n", p->name, status);
}
static void butterfly_detach(struct parport *p)
{
struct butterfly *pp;
struct platform_device *pdev;
int status;
/* FIXME this global is ugly ... but, how to quickly get from
* the parport to the "struct butterfly" associated with it?
* "old school" driver-internal device lists?
*/
if (!butterfly || butterfly->port != p)
return;
pp = butterfly;
butterfly = NULL;
/* stop() unregisters child devices too */
pdev = to_platform_device(pp->bitbang.master->cdev.dev);
status = spi_bitbang_stop(&pp->bitbang);
/* turn off VCC */
parport_write_data(pp->port, 0);
msleep(10);
parport_release(pp->pd);
parport_unregister_device(pp->pd);
(void) spi_master_put(pp->bitbang.master);
platform_device_unregister(pdev);
}
static struct parport_driver butterfly_driver = {
.name = "spi_butterfly",
.attach = butterfly_attach,
.detach = butterfly_detach,
};
static int __init butterfly_init(void)
{
return parport_register_driver(&butterfly_driver);
}
device_initcall(butterfly_init);
static void __exit butterfly_exit(void)
{
parport_unregister_driver(&butterfly_driver);
}
module_exit(butterfly_exit);
MODULE_DESCRIPTION("Parport Adapter driver for AVR Butterfly");
MODULE_LICENSE("GPL");

1768
drivers/spi/spi_imx.c Normal file

File diff suppressed because it is too large Load Diff

484
drivers/spi/spi_mpc83xx.c Normal file
View File

@@ -0,0 +1,484 @@
/*
* MPC83xx SPI controller driver.
*
* Maintainer: Kumar Gala
*
* Copyright (C) 2006 Polycom, Inc.
*
* 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.
*/
#include <linux/module.h>
#include <linux/init.h>
#include <linux/types.h>
#include <linux/kernel.h>
#include <linux/completion.h>
#include <linux/interrupt.h>
#include <linux/delay.h>
#include <linux/irq.h>
#include <linux/device.h>
#include <linux/spi/spi.h>
#include <linux/spi/spi_bitbang.h>
#include <linux/platform_device.h>
#include <linux/fsl_devices.h>
#include <asm/irq.h>
#include <asm/io.h>
/* SPI Controller registers */
struct mpc83xx_spi_reg {
u8 res1[0x20];
__be32 mode;
__be32 event;
__be32 mask;
__be32 command;
__be32 transmit;
__be32 receive;
};
/* SPI Controller mode register definitions */
#define SPMODE_CI_INACTIVEHIGH (1 << 29)
#define SPMODE_CP_BEGIN_EDGECLK (1 << 28)
#define SPMODE_DIV16 (1 << 27)
#define SPMODE_REV (1 << 26)
#define SPMODE_MS (1 << 25)
#define SPMODE_ENABLE (1 << 24)
#define SPMODE_LEN(x) ((x) << 20)
#define SPMODE_PM(x) ((x) << 16)
/*
* Default for SPI Mode:
* SPI MODE 0 (inactive low, phase middle, MSB, 8-bit length, slow clk
*/
#define SPMODE_INIT_VAL (SPMODE_CI_INACTIVEHIGH | SPMODE_DIV16 | SPMODE_REV | \
SPMODE_MS | SPMODE_LEN(7) | SPMODE_PM(0xf))
/* SPIE register values */
#define SPIE_NE 0x00000200 /* Not empty */
#define SPIE_NF 0x00000100 /* Not full */
/* SPIM register values */
#define SPIM_NE 0x00000200 /* Not empty */
#define SPIM_NF 0x00000100 /* Not full */
/* SPI Controller driver's private data. */
struct mpc83xx_spi {
/* bitbang has to be first */
struct spi_bitbang bitbang;
struct completion done;
struct mpc83xx_spi_reg __iomem *base;
/* rx & tx bufs from the spi_transfer */
const void *tx;
void *rx;
/* functions to deal with different sized buffers */
void (*get_rx) (u32 rx_data, struct mpc83xx_spi *);
u32(*get_tx) (struct mpc83xx_spi *);
unsigned int count;
u32 irq;
unsigned nsecs; /* (clock cycle time)/2 */
u32 sysclk;
void (*activate_cs) (u8 cs, u8 polarity);
void (*deactivate_cs) (u8 cs, u8 polarity);
};
static inline void mpc83xx_spi_write_reg(__be32 __iomem * reg, u32 val)
{
out_be32(reg, val);
}
static inline u32 mpc83xx_spi_read_reg(__be32 __iomem * reg)
{
return in_be32(reg);
}
#define MPC83XX_SPI_RX_BUF(type) \
void mpc83xx_spi_rx_buf_##type(u32 data, struct mpc83xx_spi *mpc83xx_spi) \
{ \
type * rx = mpc83xx_spi->rx; \
*rx++ = (type)data; \
mpc83xx_spi->rx = rx; \
}
#define MPC83XX_SPI_TX_BUF(type) \
u32 mpc83xx_spi_tx_buf_##type(struct mpc83xx_spi *mpc83xx_spi) \
{ \
u32 data; \
const type * tx = mpc83xx_spi->tx; \
if (!tx) \
return 0; \
data = *tx++; \
mpc83xx_spi->tx = tx; \
return data; \
}
MPC83XX_SPI_RX_BUF(u8)
MPC83XX_SPI_RX_BUF(u16)
MPC83XX_SPI_RX_BUF(u32)
MPC83XX_SPI_TX_BUF(u8)
MPC83XX_SPI_TX_BUF(u16)
MPC83XX_SPI_TX_BUF(u32)
static void mpc83xx_spi_chipselect(struct spi_device *spi, int value)
{
struct mpc83xx_spi *mpc83xx_spi;
u8 pol = spi->mode & SPI_CS_HIGH ? 1 : 0;
mpc83xx_spi = spi_master_get_devdata(spi->master);
if (value == BITBANG_CS_INACTIVE) {
if (mpc83xx_spi->deactivate_cs)
mpc83xx_spi->deactivate_cs(spi->chip_select, pol);
}
if (value == BITBANG_CS_ACTIVE) {
u32 regval = mpc83xx_spi_read_reg(&mpc83xx_spi->base->mode);
u32 len = spi->bits_per_word;
if (len == 32)
len = 0;
else
len = len - 1;
/* mask out bits we are going to set */
regval &= ~0x38ff0000;
if (spi->mode & SPI_CPHA)
regval |= SPMODE_CP_BEGIN_EDGECLK;
if (spi->mode & SPI_CPOL)
regval |= SPMODE_CI_INACTIVEHIGH;
regval |= SPMODE_LEN(len);
if ((mpc83xx_spi->sysclk / spi->max_speed_hz) >= 64) {
u8 pm = mpc83xx_spi->sysclk / (spi->max_speed_hz * 64);
regval |= SPMODE_PM(pm) | SPMODE_DIV16;
} else {
u8 pm = mpc83xx_spi->sysclk / (spi->max_speed_hz * 4);
regval |= SPMODE_PM(pm);
}
mpc83xx_spi_write_reg(&mpc83xx_spi->base->mode, regval);
if (mpc83xx_spi->activate_cs)
mpc83xx_spi->activate_cs(spi->chip_select, pol);
}
}
static
int mpc83xx_spi_setup_transfer(struct spi_device *spi, struct spi_transfer *t)
{
struct mpc83xx_spi *mpc83xx_spi;
u32 regval;
u8 bits_per_word;
u32 hz;
mpc83xx_spi = spi_master_get_devdata(spi->master);
if (t) {
bits_per_word = t->bits_per_word;
hz = t->speed_hz;
} else {
bits_per_word = 0;
hz = 0;
}
/* spi_transfer level calls that work per-word */
if (!bits_per_word)
bits_per_word = spi->bits_per_word;
/* Make sure its a bit width we support [4..16, 32] */
if ((bits_per_word < 4)
|| ((bits_per_word > 16) && (bits_per_word != 32)))
return -EINVAL;
if (bits_per_word <= 8) {
mpc83xx_spi->get_rx = mpc83xx_spi_rx_buf_u8;
mpc83xx_spi->get_tx = mpc83xx_spi_tx_buf_u8;
} else if (bits_per_word <= 16) {
mpc83xx_spi->get_rx = mpc83xx_spi_rx_buf_u16;
mpc83xx_spi->get_tx = mpc83xx_spi_tx_buf_u16;
} else if (bits_per_word <= 32) {
mpc83xx_spi->get_rx = mpc83xx_spi_rx_buf_u32;
mpc83xx_spi->get_tx = mpc83xx_spi_tx_buf_u32;
} else
return -EINVAL;
/* nsecs = (clock period)/2 */
if (!hz)
hz = spi->max_speed_hz;
mpc83xx_spi->nsecs = (1000000000 / 2) / hz;
if (mpc83xx_spi->nsecs > MAX_UDELAY_MS * 1000)
return -EINVAL;
if (bits_per_word == 32)
bits_per_word = 0;
else
bits_per_word = bits_per_word - 1;
regval = mpc83xx_spi_read_reg(&mpc83xx_spi->base->mode);
/* Mask out bits_per_wordgth */
regval &= 0xff0fffff;
regval |= SPMODE_LEN(bits_per_word);
mpc83xx_spi_write_reg(&mpc83xx_spi->base->mode, regval);
return 0;
}
static int mpc83xx_spi_setup(struct spi_device *spi)
{
struct spi_bitbang *bitbang;
struct mpc83xx_spi *mpc83xx_spi;
int retval;
if (!spi->max_speed_hz)
return -EINVAL;
bitbang = spi_master_get_devdata(spi->master);
mpc83xx_spi = spi_master_get_devdata(spi->master);
if (!spi->bits_per_word)
spi->bits_per_word = 8;
retval = mpc83xx_spi_setup_transfer(spi, NULL);
if (retval < 0)
return retval;
dev_dbg(&spi->dev, "%s, mode %d, %u bits/w, %u nsec\n",
__FUNCTION__, spi->mode & (SPI_CPOL | SPI_CPHA),
spi->bits_per_word, 2 * mpc83xx_spi->nsecs);
/* NOTE we _need_ to call chipselect() early, ideally with adapter
* setup, unless the hardware defaults cooperate to avoid confusion
* between normal (active low) and inverted chipselects.
*/
/* deselect chip (low or high) */
spin_lock(&bitbang->lock);
if (!bitbang->busy) {
bitbang->chipselect(spi, BITBANG_CS_INACTIVE);
ndelay(mpc83xx_spi->nsecs);
}
spin_unlock(&bitbang->lock);
return 0;
}
static int mpc83xx_spi_bufs(struct spi_device *spi, struct spi_transfer *t)
{
struct mpc83xx_spi *mpc83xx_spi;
u32 word;
mpc83xx_spi = spi_master_get_devdata(spi->master);
mpc83xx_spi->tx = t->tx_buf;
mpc83xx_spi->rx = t->rx_buf;
mpc83xx_spi->count = t->len;
INIT_COMPLETION(mpc83xx_spi->done);
/* enable rx ints */
mpc83xx_spi_write_reg(&mpc83xx_spi->base->mask, SPIM_NE);
/* transmit word */
word = mpc83xx_spi->get_tx(mpc83xx_spi);
mpc83xx_spi_write_reg(&mpc83xx_spi->base->transmit, word);
wait_for_completion(&mpc83xx_spi->done);
/* disable rx ints */
mpc83xx_spi_write_reg(&mpc83xx_spi->base->mask, 0);
return t->len - mpc83xx_spi->count;
}
irqreturn_t mpc83xx_spi_irq(s32 irq, void *context_data)
{
struct mpc83xx_spi *mpc83xx_spi = context_data;
u32 event;
irqreturn_t ret = IRQ_NONE;
/* Get interrupt events(tx/rx) */
event = mpc83xx_spi_read_reg(&mpc83xx_spi->base->event);
/* We need handle RX first */
if (event & SPIE_NE) {
u32 rx_data = mpc83xx_spi_read_reg(&mpc83xx_spi->base->receive);
if (mpc83xx_spi->rx)
mpc83xx_spi->get_rx(rx_data, mpc83xx_spi);
ret = IRQ_HANDLED;
}
if ((event & SPIE_NF) == 0)
/* spin until TX is done */
while (((event =
mpc83xx_spi_read_reg(&mpc83xx_spi->base->event)) &
SPIE_NF) == 0)
cpu_relax();
mpc83xx_spi->count -= 1;
if (mpc83xx_spi->count) {
if (mpc83xx_spi->tx) {
u32 word = mpc83xx_spi->get_tx(mpc83xx_spi);
mpc83xx_spi_write_reg(&mpc83xx_spi->base->transmit,
word);
}
} else {
complete(&mpc83xx_spi->done);
}
/* Clear the events */
mpc83xx_spi_write_reg(&mpc83xx_spi->base->event, event);
return ret;
}
static int __init mpc83xx_spi_probe(struct platform_device *dev)
{
struct spi_master *master;
struct mpc83xx_spi *mpc83xx_spi;
struct fsl_spi_platform_data *pdata;
struct resource *r;
u32 regval;
int ret = 0;
/* Get resources(memory, IRQ) associated with the device */
master = spi_alloc_master(&dev->dev, sizeof(struct mpc83xx_spi));
if (master == NULL) {
ret = -ENOMEM;
goto err;
}
platform_set_drvdata(dev, master);
pdata = dev->dev.platform_data;
if (pdata == NULL) {
ret = -ENODEV;
goto free_master;
}
r = platform_get_resource(dev, IORESOURCE_MEM, 0);
if (r == NULL) {
ret = -ENODEV;
goto free_master;
}
mpc83xx_spi = spi_master_get_devdata(master);
mpc83xx_spi->bitbang.master = spi_master_get(master);
mpc83xx_spi->bitbang.chipselect = mpc83xx_spi_chipselect;
mpc83xx_spi->bitbang.setup_transfer = mpc83xx_spi_setup_transfer;
mpc83xx_spi->bitbang.txrx_bufs = mpc83xx_spi_bufs;
mpc83xx_spi->sysclk = pdata->sysclk;
mpc83xx_spi->activate_cs = pdata->activate_cs;
mpc83xx_spi->deactivate_cs = pdata->deactivate_cs;
mpc83xx_spi->get_rx = mpc83xx_spi_rx_buf_u8;
mpc83xx_spi->get_tx = mpc83xx_spi_tx_buf_u8;
mpc83xx_spi->bitbang.master->setup = mpc83xx_spi_setup;
init_completion(&mpc83xx_spi->done);
mpc83xx_spi->base = ioremap(r->start, r->end - r->start + 1);
if (mpc83xx_spi->base == NULL) {
ret = -ENOMEM;
goto put_master;
}
mpc83xx_spi->irq = platform_get_irq(dev, 0);
if (mpc83xx_spi->irq < 0) {
ret = -ENXIO;
goto unmap_io;
}
/* Register for SPI Interrupt */
ret = request_irq(mpc83xx_spi->irq, mpc83xx_spi_irq,
0, "mpc83xx_spi", mpc83xx_spi);
if (ret != 0)
goto unmap_io;
master->bus_num = pdata->bus_num;
master->num_chipselect = pdata->max_chipselect;
/* SPI controller initializations */
mpc83xx_spi_write_reg(&mpc83xx_spi->base->mode, 0);
mpc83xx_spi_write_reg(&mpc83xx_spi->base->mask, 0);
mpc83xx_spi_write_reg(&mpc83xx_spi->base->command, 0);
mpc83xx_spi_write_reg(&mpc83xx_spi->base->event, 0xffffffff);
/* Enable SPI interface */
regval = pdata->initial_spmode | SPMODE_INIT_VAL | SPMODE_ENABLE;
mpc83xx_spi_write_reg(&mpc83xx_spi->base->mode, regval);
ret = spi_bitbang_start(&mpc83xx_spi->bitbang);
if (ret != 0)
goto free_irq;
printk(KERN_INFO
"%s: MPC83xx SPI Controller driver at 0x%p (irq = %d)\n",
dev->dev.bus_id, mpc83xx_spi->base, mpc83xx_spi->irq);
return ret;
free_irq:
free_irq(mpc83xx_spi->irq, mpc83xx_spi);
unmap_io:
iounmap(mpc83xx_spi->base);
put_master:
spi_master_put(master);
free_master:
kfree(master);
err:
return ret;
}
static int __devexit mpc83xx_spi_remove(struct platform_device *dev)
{
struct mpc83xx_spi *mpc83xx_spi;
struct spi_master *master;
master = platform_get_drvdata(dev);
mpc83xx_spi = spi_master_get_devdata(master);
spi_bitbang_stop(&mpc83xx_spi->bitbang);
free_irq(mpc83xx_spi->irq, mpc83xx_spi);
iounmap(mpc83xx_spi->base);
spi_master_put(mpc83xx_spi->bitbang.master);
return 0;
}
static struct platform_driver mpc83xx_spi_driver = {
.probe = mpc83xx_spi_probe,
.remove = __devexit_p(mpc83xx_spi_remove),
.driver = {
.name = "mpc83xx_spi",
},
};
static int __init mpc83xx_spi_init(void)
{
return platform_driver_register(&mpc83xx_spi_driver);
}
static void __exit mpc83xx_spi_exit(void)
{
platform_driver_unregister(&mpc83xx_spi_driver);
}
module_init(mpc83xx_spi_init);
module_exit(mpc83xx_spi_exit);
MODULE_AUTHOR("Kumar Gala");
MODULE_DESCRIPTION("Simple MPC83xx SPI Driver");
MODULE_LICENSE("GPL");

452
drivers/spi/spi_s3c24xx.c Normal file
View File

@@ -0,0 +1,452 @@
/* linux/drivers/spi/spi_s3c24xx.c
*
* Copyright (c) 2006 Ben Dooks
* Copyright (c) 2006 Simtec Electronics
* 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 version 2 as
* published by the Free Software Foundation.
*
*/
#include <linux/init.h>
#include <linux/spinlock.h>
#include <linux/workqueue.h>
#include <linux/interrupt.h>
#include <linux/delay.h>
#include <linux/errno.h>
#include <linux/err.h>
#include <linux/clk.h>
#include <linux/platform_device.h>
#include <linux/spi/spi.h>
#include <linux/spi/spi_bitbang.h>
#include <asm/io.h>
#include <asm/dma.h>
#include <asm/hardware.h>
#include <asm/arch/regs-gpio.h>
#include <asm/arch/regs-spi.h>
#include <asm/arch/spi.h>
struct s3c24xx_spi {
/* bitbang has to be first */
struct spi_bitbang bitbang;
struct completion done;
void __iomem *regs;
int irq;
int len;
int count;
void (*set_cs)(struct s3c2410_spi_info *spi,
int cs, int pol);
/* data buffers */
const unsigned char *tx;
unsigned char *rx;
struct clk *clk;
struct resource *ioarea;
struct spi_master *master;
struct spi_device *curdev;
struct device *dev;
struct s3c2410_spi_info *pdata;
};
#define SPCON_DEFAULT (S3C2410_SPCON_MSTR | S3C2410_SPCON_SMOD_INT)
#define SPPIN_DEFAULT (S3C2410_SPPIN_KEEP)
static inline struct s3c24xx_spi *to_hw(struct spi_device *sdev)
{
return spi_master_get_devdata(sdev->master);
}
static void s3c24xx_spi_gpiocs(struct s3c2410_spi_info *spi, int cs, int pol)
{
s3c2410_gpio_setpin(spi->pin_cs, pol);
}
static void s3c24xx_spi_chipsel(struct spi_device *spi, int value)
{
struct s3c24xx_spi *hw = to_hw(spi);
unsigned int cspol = spi->mode & SPI_CS_HIGH ? 1 : 0;
unsigned int spcon;
switch (value) {
case BITBANG_CS_INACTIVE:
hw->set_cs(hw->pdata, spi->chip_select, cspol^1);
break;
case BITBANG_CS_ACTIVE:
spcon = readb(hw->regs + S3C2410_SPCON);
if (spi->mode & SPI_CPHA)
spcon |= S3C2410_SPCON_CPHA_FMTB;
else
spcon &= ~S3C2410_SPCON_CPHA_FMTB;
if (spi->mode & SPI_CPOL)
spcon |= S3C2410_SPCON_CPOL_HIGH;
else
spcon &= ~S3C2410_SPCON_CPOL_HIGH;
spcon |= S3C2410_SPCON_ENSCK;
/* write new configration */
writeb(spcon, hw->regs + S3C2410_SPCON);
hw->set_cs(hw->pdata, spi->chip_select, cspol);
break;
}
}
static int s3c24xx_spi_setupxfer(struct spi_device *spi,
struct spi_transfer *t)
{
struct s3c24xx_spi *hw = to_hw(spi);
unsigned int bpw;
unsigned int hz;
unsigned int div;
bpw = t ? t->bits_per_word : spi->bits_per_word;
hz = t ? t->speed_hz : spi->max_speed_hz;
if (bpw != 8) {
dev_err(&spi->dev, "invalid bits-per-word (%d)\n", bpw);
return -EINVAL;
}
div = clk_get_rate(hw->clk) / hz;
/* is clk = pclk / (2 * (pre+1)), or is it
* clk = (pclk * 2) / ( pre + 1) */
div = (div / 2) - 1;
if (div < 0)
div = 1;
if (div > 255)
div = 255;
dev_dbg(&spi->dev, "setting pre-scaler to %d (hz %d)\n", div, hz);
writeb(div, hw->regs + S3C2410_SPPRE);
spin_lock(&hw->bitbang.lock);
if (!hw->bitbang.busy) {
hw->bitbang.chipselect(spi, BITBANG_CS_INACTIVE);
/* need to ndelay for 0.5 clocktick ? */
}
spin_unlock(&hw->bitbang.lock);
return 0;
}
static int s3c24xx_spi_setup(struct spi_device *spi)
{
int ret;
if (!spi->bits_per_word)
spi->bits_per_word = 8;
if ((spi->mode & SPI_LSB_FIRST) != 0)
return -EINVAL;
ret = s3c24xx_spi_setupxfer(spi, NULL);
if (ret < 0) {
dev_err(&spi->dev, "setupxfer returned %d\n", ret);
return ret;
}
dev_dbg(&spi->dev, "%s: mode %d, %u bpw, %d hz\n",
__FUNCTION__, spi->mode, spi->bits_per_word,
spi->max_speed_hz);
return 0;
}
static inline unsigned int hw_txbyte(struct s3c24xx_spi *hw, int count)
{
return hw->tx ? hw->tx[count] : 0;
}
static int s3c24xx_spi_txrx(struct spi_device *spi, struct spi_transfer *t)
{
struct s3c24xx_spi *hw = to_hw(spi);
dev_dbg(&spi->dev, "txrx: tx %p, rx %p, len %d\n",
t->tx_buf, t->rx_buf, t->len);
hw->tx = t->tx_buf;
hw->rx = t->rx_buf;
hw->len = t->len;
hw->count = 0;
/* send the first byte */
writeb(hw_txbyte(hw, 0), hw->regs + S3C2410_SPTDAT);
wait_for_completion(&hw->done);
return hw->count;
}
static irqreturn_t s3c24xx_spi_irq(int irq, void *dev)
{
struct s3c24xx_spi *hw = dev;
unsigned int spsta = readb(hw->regs + S3C2410_SPSTA);
unsigned int count = hw->count;
if (spsta & S3C2410_SPSTA_DCOL) {
dev_dbg(hw->dev, "data-collision\n");
complete(&hw->done);
goto irq_done;
}
if (!(spsta & S3C2410_SPSTA_READY)) {
dev_dbg(hw->dev, "spi not ready for tx?\n");
complete(&hw->done);
goto irq_done;
}
hw->count++;
if (hw->rx)
hw->rx[count] = readb(hw->regs + S3C2410_SPRDAT);
count++;
if (count < hw->len)
writeb(hw_txbyte(hw, count), hw->regs + S3C2410_SPTDAT);
else
complete(&hw->done);
irq_done:
return IRQ_HANDLED;
}
static int s3c24xx_spi_probe(struct platform_device *pdev)
{
struct s3c24xx_spi *hw;
struct spi_master *master;
struct spi_board_info *bi;
struct resource *res;
int err = 0;
int i;
master = spi_alloc_master(&pdev->dev, sizeof(struct s3c24xx_spi));
if (master == NULL) {
dev_err(&pdev->dev, "No memory for spi_master\n");
err = -ENOMEM;
goto err_nomem;
}
hw = spi_master_get_devdata(master);
memset(hw, 0, sizeof(struct s3c24xx_spi));
hw->master = spi_master_get(master);
hw->pdata = pdev->dev.platform_data;
hw->dev = &pdev->dev;
if (hw->pdata == NULL) {
dev_err(&pdev->dev, "No platform data supplied\n");
err = -ENOENT;
goto err_no_pdata;
}
platform_set_drvdata(pdev, hw);
init_completion(&hw->done);
/* setup the state for the bitbang driver */
hw->bitbang.master = hw->master;
hw->bitbang.setup_transfer = s3c24xx_spi_setupxfer;
hw->bitbang.chipselect = s3c24xx_spi_chipsel;
hw->bitbang.txrx_bufs = s3c24xx_spi_txrx;
hw->bitbang.master->setup = s3c24xx_spi_setup;
dev_dbg(hw->dev, "bitbang at %p\n", &hw->bitbang);
/* find and map our resources */
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
if (res == NULL) {
dev_err(&pdev->dev, "Cannot get IORESOURCE_MEM\n");
err = -ENOENT;
goto err_no_iores;
}
hw->ioarea = request_mem_region(res->start, (res->end - res->start)+1,
pdev->name);
if (hw->ioarea == NULL) {
dev_err(&pdev->dev, "Cannot reserve region\n");
err = -ENXIO;
goto err_no_iores;
}
hw->regs = ioremap(res->start, (res->end - res->start)+1);
if (hw->regs == NULL) {
dev_err(&pdev->dev, "Cannot map IO\n");
err = -ENXIO;
goto err_no_iomap;
}
hw->irq = platform_get_irq(pdev, 0);
if (hw->irq < 0) {
dev_err(&pdev->dev, "No IRQ specified\n");
err = -ENOENT;
goto err_no_irq;
}
err = request_irq(hw->irq, s3c24xx_spi_irq, 0, pdev->name, hw);
if (err) {
dev_err(&pdev->dev, "Cannot claim IRQ\n");
goto err_no_irq;
}
hw->clk = clk_get(&pdev->dev, "spi");
if (IS_ERR(hw->clk)) {
dev_err(&pdev->dev, "No clock for device\n");
err = PTR_ERR(hw->clk);
goto err_no_clk;
}
/* for the moment, permanently enable the clock */
clk_enable(hw->clk);
/* program defaults into the registers */
writeb(0xff, hw->regs + S3C2410_SPPRE);
writeb(SPPIN_DEFAULT, hw->regs + S3C2410_SPPIN);
writeb(SPCON_DEFAULT, hw->regs + S3C2410_SPCON);
/* setup any gpio we can */
if (!hw->pdata->set_cs) {
hw->set_cs = s3c24xx_spi_gpiocs;
s3c2410_gpio_setpin(hw->pdata->pin_cs, 1);
s3c2410_gpio_cfgpin(hw->pdata->pin_cs, S3C2410_GPIO_OUTPUT);
} else
hw->set_cs = hw->pdata->set_cs;
/* register our spi controller */
err = spi_bitbang_start(&hw->bitbang);
if (err) {
dev_err(&pdev->dev, "Failed to register SPI master\n");
goto err_register;
}
dev_dbg(hw->dev, "shutdown=%d\n", hw->bitbang.shutdown);
/* register all the devices associated */
bi = &hw->pdata->board_info[0];
for (i = 0; i < hw->pdata->board_size; i++, bi++) {
dev_info(hw->dev, "registering %s\n", bi->modalias);
bi->controller_data = hw;
spi_new_device(master, bi);
}
return 0;
err_register:
clk_disable(hw->clk);
clk_put(hw->clk);
err_no_clk:
free_irq(hw->irq, hw);
err_no_irq:
iounmap(hw->regs);
err_no_iomap:
release_resource(hw->ioarea);
kfree(hw->ioarea);
err_no_iores:
err_no_pdata:
spi_master_put(hw->master);;
err_nomem:
return err;
}
static int s3c24xx_spi_remove(struct platform_device *dev)
{
struct s3c24xx_spi *hw = platform_get_drvdata(dev);
platform_set_drvdata(dev, NULL);
spi_unregister_master(hw->master);
clk_disable(hw->clk);
clk_put(hw->clk);
free_irq(hw->irq, hw);
iounmap(hw->regs);
release_resource(hw->ioarea);
kfree(hw->ioarea);
spi_master_put(hw->master);
return 0;
}
#ifdef CONFIG_PM
static int s3c24xx_spi_suspend(struct platform_device *pdev, pm_message_t msg)
{
struct s3c24xx_spi *hw = platform_get_drvdata(pdev);
clk_disable(hw->clk);
return 0;
}
static int s3c24xx_spi_resume(struct platform_device *pdev)
{
struct s3c24xx_spi *hw = platform_get_drvdata(pdev);
clk_enable(hw->clk);
return 0;
}
#else
#define s3c24xx_spi_suspend NULL
#define s3c24xx_spi_resume NULL
#endif
static struct platform_driver s3c24xx_spidrv = {
.probe = s3c24xx_spi_probe,
.remove = s3c24xx_spi_remove,
.suspend = s3c24xx_spi_suspend,
.resume = s3c24xx_spi_resume,
.driver = {
.name = "s3c2410-spi",
.owner = THIS_MODULE,
},
};
static int __init s3c24xx_spi_init(void)
{
return platform_driver_register(&s3c24xx_spidrv);
}
static void __exit s3c24xx_spi_exit(void)
{
platform_driver_unregister(&s3c24xx_spidrv);
}
module_init(s3c24xx_spi_init);
module_exit(s3c24xx_spi_exit);
MODULE_DESCRIPTION("S3C24XX SPI Driver");
MODULE_AUTHOR("Ben Dooks, <ben@simtec.co.uk>");
MODULE_LICENSE("GPL");

View File

@@ -0,0 +1,203 @@
/* 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/kernel.h>
#include <linux/init.h>
#include <linux/delay.h>
#include <linux/spinlock.h>
#include <linux/workqueue.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/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 u32 s3c2410_spigpio_txrx_mode2(struct spi_device *spi,
unsigned nsecs, u32 word, u8 bits)
{
return bitbang_txrx_be_cpha0(spi, nsecs, 1, word, bits);
}
static u32 s3c2410_spigpio_txrx_mode3(struct spi_device *spi,
unsigned nsecs, u32 word, u8 bits)
{
return bitbang_txrx_be_cpha1(spi, nsecs, 1, 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;
sp->bitbang.txrx_word[SPI_MODE_2] = s3c2410_spigpio_txrx_mode2;
sp->bitbang.txrx_word[SPI_MODE_3] = s3c2410_spigpio_txrx_mode3;
/* 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");