Merge branch 'next-spi' of git://git.secretlab.ca/git/linux-2.6

* 'next-spi' of git://git.secretlab.ca/git/linux-2.6: (23 commits)
  spi: fix probe/remove section markings
  Add OMAP spi100k driver
  spi-imx: don't access struct device directly but use dev_get_platdata
  spi-imx: Add mx25 support
  spi-imx: use positive logic to distinguish cpu variants
  spi-imx: correct check for platform_get_irq failing
  ARM: NUC900: Add spi driver support for nuc900
  spi: SuperH MSIOF SPI Master driver V2
  spi: fix spidev compilation failure when VERBOSE is defined
  spi/au1550_spi: fix setupxfer not to override cfg with zeros
  spi/mpc8xxx: don't use __exit_p to wrap plat_mpc8xxx_spi_remove
  spi/i.MX: fix broken error handling for gpio_request
  spi/i.mx: drain MXC SPI transfer buffer when probing device
  MAINTAINERS: add SPI co-maintainer.
  spi/xilinx_spi: fix incorrect casting
  spi/mpc52xx-spi: minor cleanups
  xilinx_spi: add a platform driver using the xilinx_spi common module.
  xilinx_spi: add support for the DS570 IP.
  xilinx_spi: Switch to iomem functions and support little endian.
  xilinx_spi: Split into of driver and generic part.
  ...
This commit is contained in:
Linus Torvalds 2009-12-14 10:22:11 -08:00
commit 478e4e9d7a
19 changed files with 2483 additions and 228 deletions

View File

@ -5080,6 +5080,7 @@ F: drivers/char/specialix*
SPI SUBSYSTEM
M: David Brownell <dbrownell@users.sourceforge.net>
M: Grant Likely <grant.likely@secretlab.ca>
L: spi-devel-general@lists.sourceforge.net
S: Maintained
F: Documentation/spi/

View File

@ -0,0 +1,35 @@
/*
* arch/arm/mach-w90x900/include/mach/nuc900_spi.h
*
* Copyright (c) 2009 Nuvoton technology corporation.
*
* Wan ZongShun <mcuos.com@gmail.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;version 2 of the License.
*
*/
#ifndef __ASM_ARCH_SPI_H
#define __ASM_ARCH_SPI_H
extern void mfp_set_groupg(struct device *dev);
struct nuc900_spi_info {
unsigned int num_cs;
unsigned int lsb;
unsigned int txneg;
unsigned int rxneg;
unsigned int divider;
unsigned int sleep;
unsigned int txnum;
unsigned int txbitlen;
int bus_num;
};
struct nuc900_spi_chip {
unsigned char bits_per_word;
};
#endif /* __ASM_ARCH_SPI_H */

View File

@ -169,6 +169,12 @@ config SPI_OMAP24XX
SPI master controller for OMAP24xx/OMAP34xx Multichannel SPI
(McSPI) modules.
config SPI_OMAP_100K
tristate "OMAP SPI 100K"
depends on SPI_MASTER && (ARCH_OMAP850 || ARCH_OMAP730)
help
OMAP SPI 100K master controller for omap7xx boards.
config SPI_ORION
tristate "Orion SPI master (EXPERIMENTAL)"
depends on PLAT_ORION && EXPERIMENTAL
@ -220,6 +226,13 @@ config SPI_S3C24XX_GPIO
the inbuilt hardware cannot provide the transfer mode, or
where the board is using non hardware connected pins.
config SPI_SH_MSIOF
tristate "SuperH MSIOF SPI controller"
depends on SUPERH && HAVE_CLK
select SPI_BITBANG
help
SPI driver for SuperH MSIOF blocks.
config SPI_SH_SCI
tristate "SuperH SCI SPI controller"
depends on SUPERH
@ -240,15 +253,38 @@ config SPI_TXX9
SPI driver for Toshiba TXx9 MIPS SoCs
config SPI_XILINX
tristate "Xilinx SPI controller"
depends on (XILINX_VIRTEX || MICROBLAZE) && EXPERIMENTAL
tristate "Xilinx SPI controller common module"
depends on HAS_IOMEM && EXPERIMENTAL
select SPI_BITBANG
select SPI_XILINX_OF if (XILINX_VIRTEX || MICROBLAZE)
help
This exposes the SPI controller IP from the Xilinx EDK.
See the "OPB Serial Peripheral Interface (SPI) (v1.00e)"
Product Specification document (DS464) for hardware details.
Or for the DS570, see "XPS Serial Peripheral Interface (SPI) (v2.00b)"
config SPI_XILINX_OF
tristate "Xilinx SPI controller OF device"
depends on SPI_XILINX && (XILINX_VIRTEX || MICROBLAZE)
help
This is the OF driver for the SPI controller IP from the Xilinx EDK.
config SPI_XILINX_PLTFM
tristate "Xilinx SPI controller platform device"
depends on SPI_XILINX
help
This is the platform driver for the SPI controller IP
from the Xilinx EDK.
config SPI_NUC900
tristate "Nuvoton NUC900 series SPI"
depends on ARCH_W90X900 && EXPERIMENTAL
select SPI_BITBANG
help
SPI driver for Nuvoton NUC900 series ARM SoCs
#
# Add new SPI master controllers in alphabetical order above this line
#

View File

@ -22,6 +22,7 @@ obj-$(CONFIG_SPI_LM70_LLP) += spi_lm70llp.o
obj-$(CONFIG_SPI_PXA2XX) += pxa2xx_spi.o
obj-$(CONFIG_SPI_OMAP_UWIRE) += omap_uwire.o
obj-$(CONFIG_SPI_OMAP24XX) += omap2_mcspi.o
obj-$(CONFIG_SPI_OMAP_100K) += omap_spi_100k.o
obj-$(CONFIG_SPI_ORION) += orion_spi.o
obj-$(CONFIG_SPI_PL022) += amba-pl022.o
obj-$(CONFIG_SPI_MPC52xx_PSC) += mpc52xx_psc_spi.o
@ -32,8 +33,12 @@ obj-$(CONFIG_SPI_S3C24XX_GPIO) += spi_s3c24xx_gpio.o
obj-$(CONFIG_SPI_S3C24XX) += spi_s3c24xx.o
obj-$(CONFIG_SPI_TXX9) += spi_txx9.o
obj-$(CONFIG_SPI_XILINX) += xilinx_spi.o
obj-$(CONFIG_SPI_XILINX_OF) += xilinx_spi_of.o
obj-$(CONFIG_SPI_XILINX_PLTFM) += xilinx_spi_pltfm.o
obj-$(CONFIG_SPI_SH_SCI) += spi_sh_sci.o
obj-$(CONFIG_SPI_SH_MSIOF) += spi_sh_msiof.o
obj-$(CONFIG_SPI_STMP3XXX) += spi_stmp.o
obj-$(CONFIG_SPI_NUC900) += spi_nuc900.o
# ... add above this line ...
# SPI protocol drivers (device/link on bus)

View File

@ -237,8 +237,14 @@ static int au1550_spi_setupxfer(struct spi_device *spi, struct spi_transfer *t)
unsigned bpw, hz;
u32 cfg, stat;
bpw = t ? t->bits_per_word : spi->bits_per_word;
hz = t ? t->speed_hz : spi->max_speed_hz;
bpw = spi->bits_per_word;
hz = spi->max_speed_hz;
if (t) {
if (t->bits_per_word)
bpw = t->bits_per_word;
if (t->speed_hz)
hz = t->speed_hz;
}
if (bpw < 4 || bpw > 24) {
dev_err(&spi->dev, "setupxfer: invalid bits_per_word=%d\n",

View File

@ -18,9 +18,9 @@
#include <linux/interrupt.h>
#include <linux/delay.h>
#include <linux/spi/spi.h>
#include <linux/spi/mpc52xx_spi.h>
#include <linux/of_spi.h>
#include <linux/io.h>
#include <linux/of_gpio.h>
#include <asm/time.h>
#include <asm/mpc52xx.h>
@ -53,7 +53,7 @@ MODULE_LICENSE("GPL");
/* FSM state return values */
#define FSM_STOP 0 /* Nothing more for the state machine to */
/* do. If something interesting happens */
/* then and IRQ will be received */
/* then an IRQ will be received */
#define FSM_POLL 1 /* need to poll for completion, an IRQ is */
/* not expected */
#define FSM_CONTINUE 2 /* Keep iterating the state machine */
@ -61,13 +61,12 @@ MODULE_LICENSE("GPL");
/* Driver internal data */
struct mpc52xx_spi {
struct spi_master *master;
u32 sysclk;
void __iomem *regs;
int irq0; /* MODF irq */
int irq1; /* SPIF irq */
int ipb_freq;
unsigned int ipb_freq;
/* Statistics */
/* Statistics; not used now, but will be reintroduced for debugfs */
int msg_count;
int wcol_count;
int wcol_ticks;
@ -79,7 +78,6 @@ struct mpc52xx_spi {
spinlock_t lock;
struct work_struct work;
/* Details of current transfer (length, and buffer pointers) */
struct spi_message *message; /* current message */
struct spi_transfer *transfer; /* current transfer */
@ -89,6 +87,8 @@ struct mpc52xx_spi {
u8 *rx_buf;
const u8 *tx_buf;
int cs_change;
int gpio_cs_count;
unsigned int *gpio_cs;
};
/*
@ -96,7 +96,13 @@ struct mpc52xx_spi {
*/
static void mpc52xx_spi_chipsel(struct mpc52xx_spi *ms, int value)
{
out_8(ms->regs + SPI_PORTDATA, value ? 0 : 0x08);
int cs;
if (ms->gpio_cs_count > 0) {
cs = ms->message->spi->chip_select;
gpio_set_value(ms->gpio_cs[cs], value ? 0 : 1);
} else
out_8(ms->regs + SPI_PORTDATA, value ? 0 : 0x08);
}
/*
@ -221,7 +227,7 @@ static int mpc52xx_spi_fsmstate_transfer(int irq, struct mpc52xx_spi *ms,
ms->wcol_tx_timestamp = get_tbl();
data = 0;
if (ms->tx_buf)
data = *(ms->tx_buf-1);
data = *(ms->tx_buf - 1);
out_8(ms->regs + SPI_DATA, data); /* try again */
return FSM_CONTINUE;
} else if (status & SPI_STATUS_MODF) {
@ -390,7 +396,9 @@ static int __devinit mpc52xx_spi_probe(struct of_device *op,
struct spi_master *master;
struct mpc52xx_spi *ms;
void __iomem *regs;
int rc;
u8 ctrl1;
int rc, i = 0;
int gpio_cs;
/* MMIO registers */
dev_dbg(&op->dev, "probing mpc5200 SPI device\n");
@ -399,7 +407,8 @@ static int __devinit mpc52xx_spi_probe(struct of_device *op,
return -ENODEV;
/* initialize the device */
out_8(regs+SPI_CTRL1, SPI_CTRL1_SPIE | SPI_CTRL1_SPE | SPI_CTRL1_MSTR);
ctrl1 = SPI_CTRL1_SPIE | SPI_CTRL1_SPE | SPI_CTRL1_MSTR;
out_8(regs + SPI_CTRL1, ctrl1);
out_8(regs + SPI_CTRL2, 0x0);
out_8(regs + SPI_DATADIR, 0xe); /* Set output pins */
out_8(regs + SPI_PORTDATA, 0x8); /* Deassert /SS signal */
@ -409,6 +418,8 @@ static int __devinit mpc52xx_spi_probe(struct of_device *op,
* on the SPI bus. This fault will also occur if the SPI signals
* are not connected to any pins (port_config setting) */
in_8(regs + SPI_STATUS);
out_8(regs + SPI_CTRL1, ctrl1);
in_8(regs + SPI_DATA);
if (in_8(regs + SPI_STATUS) & SPI_STATUS_MODF) {
dev_err(&op->dev, "mode fault; is port_config correct?\n");
@ -422,10 +433,12 @@ static int __devinit mpc52xx_spi_probe(struct of_device *op,
rc = -ENOMEM;
goto err_alloc;
}
master->bus_num = -1;
master->num_chipselect = 1;
master->setup = mpc52xx_spi_setup;
master->transfer = mpc52xx_spi_transfer;
master->mode_bits = SPI_CPOL | SPI_CPHA | SPI_LSB_FIRST;
dev_set_drvdata(&op->dev, master);
ms = spi_master_get_devdata(master);
@ -435,16 +448,51 @@ static int __devinit mpc52xx_spi_probe(struct of_device *op,
ms->irq1 = irq_of_parse_and_map(op->node, 1);
ms->state = mpc52xx_spi_fsmstate_idle;
ms->ipb_freq = mpc5xxx_get_bus_frequency(op->node);
ms->gpio_cs_count = of_gpio_count(op->node);
if (ms->gpio_cs_count > 0) {
master->num_chipselect = ms->gpio_cs_count;
ms->gpio_cs = kmalloc(ms->gpio_cs_count * sizeof(unsigned int),
GFP_KERNEL);
if (!ms->gpio_cs) {
rc = -ENOMEM;
goto err_alloc;
}
for (i = 0; i < ms->gpio_cs_count; i++) {
gpio_cs = of_get_gpio(op->node, i);
if (gpio_cs < 0) {
dev_err(&op->dev,
"could not parse the gpio field "
"in oftree\n");
rc = -ENODEV;
goto err_gpio;
}
rc = gpio_request(gpio_cs, dev_name(&op->dev));
if (rc) {
dev_err(&op->dev,
"can't request spi cs gpio #%d "
"on gpio line %d\n", i, gpio_cs);
goto err_gpio;
}
gpio_direction_output(gpio_cs, 1);
ms->gpio_cs[i] = gpio_cs;
}
} else {
master->num_chipselect = 1;
}
spin_lock_init(&ms->lock);
INIT_LIST_HEAD(&ms->queue);
INIT_WORK(&ms->work, mpc52xx_spi_wq);
/* Decide if interrupts can be used */
if (ms->irq0 && ms->irq1) {
rc = request_irq(ms->irq0, mpc52xx_spi_irq, IRQF_SAMPLE_RANDOM,
rc = request_irq(ms->irq0, mpc52xx_spi_irq, 0,
"mpc5200-spi-modf", ms);
rc |= request_irq(ms->irq1, mpc52xx_spi_irq, IRQF_SAMPLE_RANDOM,
"mpc5200-spi-spiF", ms);
rc |= request_irq(ms->irq1, mpc52xx_spi_irq, 0,
"mpc5200-spi-spif", ms);
if (rc) {
free_irq(ms->irq0, ms);
free_irq(ms->irq1, ms);
@ -471,6 +519,11 @@ static int __devinit mpc52xx_spi_probe(struct of_device *op,
err_register:
dev_err(&ms->master->dev, "initialization failed\n");
spi_master_put(master);
err_gpio:
while (i-- > 0)
gpio_free(ms->gpio_cs[i]);
kfree(ms->gpio_cs);
err_alloc:
err_init:
iounmap(regs);
@ -481,10 +534,15 @@ static int __devexit mpc52xx_spi_remove(struct of_device *op)
{
struct spi_master *master = dev_get_drvdata(&op->dev);
struct mpc52xx_spi *ms = spi_master_get_devdata(master);
int i;
free_irq(ms->irq0, ms);
free_irq(ms->irq1, ms);
for (i = 0; i < ms->gpio_cs_count; i++)
gpio_free(ms->gpio_cs[i]);
kfree(ms->gpio_cs);
spi_unregister_master(master);
spi_master_put(master);
iounmap(ms->regs);

635
drivers/spi/omap_spi_100k.c Normal file
View File

@ -0,0 +1,635 @@
/*
* OMAP7xx SPI 100k controller driver
* Author: Fabrice Crohas <fcrohas@gmail.com>
* from original omap1_mcspi driver
*
* Copyright (C) 2005, 2006 Nokia Corporation
* Author: Samuel Ortiz <samuel.ortiz@nokia.com> and
* Juha Yrj<EFBFBD>l<EFBFBD> <juha.yrjola@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 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/init.h>
#include <linux/interrupt.h>
#include <linux/module.h>
#include <linux/device.h>
#include <linux/delay.h>
#include <linux/platform_device.h>
#include <linux/err.h>
#include <linux/clk.h>
#include <linux/io.h>
#include <linux/gpio.h>
#include <linux/spi/spi.h>
#include <plat/clock.h>
#define OMAP1_SPI100K_MAX_FREQ 48000000
#define ICR_SPITAS (OMAP7XX_ICR_BASE + 0x12)
#define SPI_SETUP1 0x00
#define SPI_SETUP2 0x02
#define SPI_CTRL 0x04
#define SPI_STATUS 0x06
#define SPI_TX_LSB 0x08
#define SPI_TX_MSB 0x0a
#define SPI_RX_LSB 0x0c
#define SPI_RX_MSB 0x0e
#define SPI_SETUP1_INT_READ_ENABLE (1UL << 5)
#define SPI_SETUP1_INT_WRITE_ENABLE (1UL << 4)
#define SPI_SETUP1_CLOCK_DIVISOR(x) ((x) << 1)
#define SPI_SETUP1_CLOCK_ENABLE (1UL << 0)
#define SPI_SETUP2_ACTIVE_EDGE_FALLING (0UL << 0)
#define SPI_SETUP2_ACTIVE_EDGE_RISING (1UL << 0)
#define SPI_SETUP2_NEGATIVE_LEVEL (0UL << 5)
#define SPI_SETUP2_POSITIVE_LEVEL (1UL << 5)
#define SPI_SETUP2_LEVEL_TRIGGER (0UL << 10)
#define SPI_SETUP2_EDGE_TRIGGER (1UL << 10)
#define SPI_CTRL_SEN(x) ((x) << 7)
#define SPI_CTRL_WORD_SIZE(x) (((x) - 1) << 2)
#define SPI_CTRL_WR (1UL << 1)
#define SPI_CTRL_RD (1UL << 0)
#define SPI_STATUS_WE (1UL << 1)
#define SPI_STATUS_RD (1UL << 0)
#define WRITE 0
#define READ 1
/* use PIO for small transfers, avoiding DMA setup/teardown overhead and
* cache operations; better heuristics consider wordsize and bitrate.
*/
#define DMA_MIN_BYTES 8
#define SPI_RUNNING 0
#define SPI_SHUTDOWN 1
struct omap1_spi100k {
struct work_struct work;
/* lock protects queue and registers */
spinlock_t lock;
struct list_head msg_queue;
struct spi_master *master;
struct clk *ick;
struct clk *fck;
/* Virtual base address of the controller */
void __iomem *base;
/* State of the SPI */
unsigned int state;
};
struct omap1_spi100k_cs {
void __iomem *base;
int word_len;
};
static struct workqueue_struct *omap1_spi100k_wq;
#define MOD_REG_BIT(val, mask, set) do { \
if (set) \
val |= mask; \
else \
val &= ~mask; \
} while (0)
static void spi100k_enable_clock(struct spi_master *master)
{
unsigned int val;
struct omap1_spi100k *spi100k = spi_master_get_devdata(master);
/* enable SPI */
val = readw(spi100k->base + SPI_SETUP1);
val |= SPI_SETUP1_CLOCK_ENABLE;
writew(val, spi100k->base + SPI_SETUP1);
}
static void spi100k_disable_clock(struct spi_master *master)
{
unsigned int val;
struct omap1_spi100k *spi100k = spi_master_get_devdata(master);
/* disable SPI */
val = readw(spi100k->base + SPI_SETUP1);
val &= ~SPI_SETUP1_CLOCK_ENABLE;
writew(val, spi100k->base + SPI_SETUP1);
}
static void spi100k_write_data(struct spi_master *master, int len, int data)
{
struct omap1_spi100k *spi100k = spi_master_get_devdata(master);
/* write 16-bit word */
spi100k_enable_clock(master);
writew( data , spi100k->base + SPI_TX_MSB);
writew(SPI_CTRL_SEN(0) |
SPI_CTRL_WORD_SIZE(len) |
SPI_CTRL_WR,
spi100k->base + SPI_CTRL);
/* Wait for bit ack send change */
while((readw(spi100k->base + SPI_STATUS) & SPI_STATUS_WE) != SPI_STATUS_WE);
udelay(1000);
spi100k_disable_clock(master);
}
static int spi100k_read_data(struct spi_master *master, int len)
{
int dataH,dataL;
struct omap1_spi100k *spi100k = spi_master_get_devdata(master);
spi100k_enable_clock(master);
writew(SPI_CTRL_SEN(0) |
SPI_CTRL_WORD_SIZE(len) |
SPI_CTRL_RD,
spi100k->base + SPI_CTRL);
while((readw(spi100k->base + SPI_STATUS) & SPI_STATUS_RD) != SPI_STATUS_RD);
udelay(1000);
dataL = readw(spi100k->base + SPI_RX_LSB);
dataH = readw(spi100k->base + SPI_RX_MSB);
spi100k_disable_clock(master);
return dataL;
}
static void spi100k_open(struct spi_master *master)
{
/* get control of SPI */
struct omap1_spi100k *spi100k = spi_master_get_devdata(master);
writew(SPI_SETUP1_INT_READ_ENABLE |
SPI_SETUP1_INT_WRITE_ENABLE |
SPI_SETUP1_CLOCK_DIVISOR(0), spi100k->base + SPI_SETUP1);
/* configure clock and interrupts */
writew(SPI_SETUP2_ACTIVE_EDGE_FALLING |
SPI_SETUP2_NEGATIVE_LEVEL |
SPI_SETUP2_LEVEL_TRIGGER, spi100k->base + SPI_SETUP2);
}
static void omap1_spi100k_force_cs(struct omap1_spi100k *spi100k, int enable)
{
if (enable)
writew(0x05fc, spi100k->base + SPI_CTRL);
else
writew(0x05fd, spi100k->base + SPI_CTRL);
}
static unsigned
omap1_spi100k_txrx_pio(struct spi_device *spi, struct spi_transfer *xfer)
{
struct omap1_spi100k *spi100k;
struct omap1_spi100k_cs *cs = spi->controller_state;
unsigned int count, c;
int word_len;
spi100k = spi_master_get_devdata(spi->master);
count = xfer->len;
c = count;
word_len = cs->word_len;
/* RX_ONLY mode needs dummy data in TX reg */
if (xfer->tx_buf == NULL)
spi100k_write_data(spi->master,word_len, 0);
if (word_len <= 8) {
u8 *rx;
const u8 *tx;
rx = xfer->rx_buf;
tx = xfer->tx_buf;
do {
c-=1;
if (xfer->tx_buf != NULL)
spi100k_write_data(spi->master,word_len, *tx);
if (xfer->rx_buf != NULL)
*rx = spi100k_read_data(spi->master,word_len);
} while(c);
} else if (word_len <= 16) {
u16 *rx;
const u16 *tx;
rx = xfer->rx_buf;
tx = xfer->tx_buf;
do {
c-=2;
if (xfer->tx_buf != NULL)
spi100k_write_data(spi->master,word_len, *tx++);
if (xfer->rx_buf != NULL)
*rx++ = spi100k_read_data(spi->master,word_len);
} while(c);
} else if (word_len <= 32) {
u32 *rx;
const u32 *tx;
rx = xfer->rx_buf;
tx = xfer->tx_buf;
do {
c-=4;
if (xfer->tx_buf != NULL)
spi100k_write_data(spi->master,word_len, *tx);
if (xfer->rx_buf != NULL)
*rx = spi100k_read_data(spi->master,word_len);
} while(c);
}
return count - c;
}
/* called only when no transfer is active to this device */
static int omap1_spi100k_setup_transfer(struct spi_device *spi,
struct spi_transfer *t)
{
struct omap1_spi100k *spi100k = spi_master_get_devdata(spi->master);
struct omap1_spi100k_cs *cs = spi->controller_state;
u8 word_len = spi->bits_per_word;
if (t != NULL && t->bits_per_word)
word_len = t->bits_per_word;
if (!word_len)
word_len = 8;
if (spi->bits_per_word > 32)
return -EINVAL;
cs->word_len = word_len;
/* SPI init before transfer */
writew(0x3e , spi100k->base + SPI_SETUP1);
writew(0x00 , spi100k->base + SPI_STATUS);
writew(0x3e , spi100k->base + SPI_CTRL);
return 0;
}
/* the spi->mode bits understood by this driver: */
#define MODEBITS (SPI_CPOL | SPI_CPHA | SPI_CS_HIGH)
static int omap1_spi100k_setup(struct spi_device *spi)
{
int ret;
struct omap1_spi100k *spi100k;
struct omap1_spi100k_cs *cs = spi->controller_state;
if (spi->bits_per_word < 4 || spi->bits_per_word > 32) {
dev_dbg(&spi->dev, "setup: unsupported %d bit words\n",
spi->bits_per_word);
return -EINVAL;
}
spi100k = spi_master_get_devdata(spi->master);
if (!cs) {
cs = kzalloc(sizeof *cs, GFP_KERNEL);
if (!cs)
return -ENOMEM;
cs->base = spi100k->base + spi->chip_select * 0x14;
spi->controller_state = cs;
}
spi100k_open(spi->master);
clk_enable(spi100k->ick);
clk_enable(spi100k->fck);
ret = omap1_spi100k_setup_transfer(spi, NULL);
clk_disable(spi100k->ick);
clk_disable(spi100k->fck);
return ret;
}
static void omap1_spi100k_work(struct work_struct *work)
{
struct omap1_spi100k *spi100k;
int status = 0;
spi100k = container_of(work, struct omap1_spi100k, work);
spin_lock_irq(&spi100k->lock);
clk_enable(spi100k->ick);
clk_enable(spi100k->fck);
/* We only enable one channel at a time -- the one whose message is
* at the head of the queue -- although this controller would gladly
* arbitrate among multiple channels. This corresponds to "single
* channel" master mode. As a side effect, we need to manage the
* chipselect with the FORCE bit ... CS != channel enable.
*/
while (!list_empty(&spi100k->msg_queue)) {
struct spi_message *m;
struct spi_device *spi;
struct spi_transfer *t = NULL;
int cs_active = 0;
struct omap1_spi100k_cs *cs;
int par_override = 0;
m = container_of(spi100k->msg_queue.next, struct spi_message,
queue);
list_del_init(&m->queue);
spin_unlock_irq(&spi100k->lock);
spi = m->spi;
cs = spi->controller_state;
list_for_each_entry(t, &m->transfers, transfer_list) {
if (t->tx_buf == NULL && t->rx_buf == NULL && t->len) {
status = -EINVAL;
break;
}
if (par_override || t->speed_hz || t->bits_per_word) {
par_override = 1;
status = omap1_spi100k_setup_transfer(spi, t);
if (status < 0)
break;
if (!t->speed_hz && !t->bits_per_word)
par_override = 0;
}
if (!cs_active) {
omap1_spi100k_force_cs(spi100k, 1);
cs_active = 1;
}
if (t->len) {
unsigned count;
/* RX_ONLY mode needs dummy data in TX reg */
if (t->tx_buf == NULL)
spi100k_write_data(spi->master, 8, 0);
count = omap1_spi100k_txrx_pio(spi, t);
m->actual_length += count;
if (count != t->len) {
status = -EIO;
break;
}
}
if (t->delay_usecs)
udelay(t->delay_usecs);
/* ignore the "leave it on after last xfer" hint */
if (t->cs_change) {
omap1_spi100k_force_cs(spi100k, 0);
cs_active = 0;
}
}
/* Restore defaults if they were overriden */
if (par_override) {
par_override = 0;
status = omap1_spi100k_setup_transfer(spi, NULL);
}
if (cs_active)
omap1_spi100k_force_cs(spi100k, 0);
m->status = status;
m->complete(m->context);
spin_lock_irq(&spi100k->lock);
}
clk_disable(spi100k->ick);
clk_disable(spi100k->fck);
spin_unlock_irq(&spi100k->lock);
if (status < 0)
printk(KERN_WARNING "spi transfer failed with %d\n", status);
}
static int omap1_spi100k_transfer(struct spi_device *spi, struct spi_message *m)
{
struct omap1_spi100k *spi100k;
unsigned long flags;
struct spi_transfer *t;
m->actual_length = 0;
m->status = -EINPROGRESS;
spi100k = spi_master_get_devdata(spi->master);
/* Don't accept new work if we're shutting down */
if (spi100k->state == SPI_SHUTDOWN)
return -ESHUTDOWN;
/* reject invalid messages and transfers */
if (list_empty(&m->transfers) || !m->complete)
return -EINVAL;
list_for_each_entry(t, &m->transfers, transfer_list) {
const void *tx_buf = t->tx_buf;
void *rx_buf = t->rx_buf;
unsigned len = t->len;
if (t->speed_hz > OMAP1_SPI100K_MAX_FREQ
|| (len && !(rx_buf || tx_buf))
|| (t->bits_per_word &&
( t->bits_per_word < 4
|| t->bits_per_word > 32))) {
dev_dbg(&spi->dev, "transfer: %d Hz, %d %s%s, %d bpw\n",
t->speed_hz,
len,
tx_buf ? "tx" : "",
rx_buf ? "rx" : "",
t->bits_per_word);
return -EINVAL;
}
if (t->speed_hz && t->speed_hz < OMAP1_SPI100K_MAX_FREQ/(1<<16)) {
dev_dbg(&spi->dev, "%d Hz max exceeds %d\n",
t->speed_hz,
OMAP1_SPI100K_MAX_FREQ/(1<<16));
return -EINVAL;
}
}
spin_lock_irqsave(&spi100k->lock, flags);
list_add_tail(&m->queue, &spi100k->msg_queue);
queue_work(omap1_spi100k_wq, &spi100k->work);
spin_unlock_irqrestore(&spi100k->lock, flags);
return 0;
}
static int __init omap1_spi100k_reset(struct omap1_spi100k *spi100k)
{
return 0;
}
static int __devinit omap1_spi100k_probe(struct platform_device *pdev)
{
struct spi_master *master;
struct omap1_spi100k *spi100k;
int status = 0;
if (!pdev->id)
return -EINVAL;
master = spi_alloc_master(&pdev->dev, sizeof *spi100k);
if (master == NULL) {
dev_dbg(&pdev->dev, "master allocation failed\n");
return -ENOMEM;
}
if (pdev->id != -1)
master->bus_num = pdev->id;
master->setup = omap1_spi100k_setup;
master->transfer = omap1_spi100k_transfer;
master->cleanup = NULL;
master->num_chipselect = 2;
master->mode_bits = MODEBITS;
dev_set_drvdata(&pdev->dev, master);
spi100k = spi_master_get_devdata(master);
spi100k->master = master;
/*
* The memory region base address is taken as the platform_data.
* You should allocate this with ioremap() before initializing
* the SPI.
*/
spi100k->base = (void __iomem *) pdev->dev.platform_data;
INIT_WORK(&spi100k->work, omap1_spi100k_work);
spin_lock_init(&spi100k->lock);
INIT_LIST_HEAD(&spi100k->msg_queue);
spi100k->ick = clk_get(&pdev->dev, "ick");
if (IS_ERR(spi100k->ick)) {
dev_dbg(&pdev->dev, "can't get spi100k_ick\n");
status = PTR_ERR(spi100k->ick);
goto err1;
}
spi100k->fck = clk_get(&pdev->dev, "fck");
if (IS_ERR(spi100k->fck)) {
dev_dbg(&pdev->dev, "can't get spi100k_fck\n");
status = PTR_ERR(spi100k->fck);
goto err2;
}
if (omap1_spi100k_reset(spi100k) < 0)
goto err3;
status = spi_register_master(master);
if (status < 0)
goto err3;
spi100k->state = SPI_RUNNING;
return status;
err3:
clk_put(spi100k->fck);
err2:
clk_put(spi100k->ick);
err1:
spi_master_put(master);
return status;
}
static int __exit omap1_spi100k_remove(struct platform_device *pdev)
{
struct spi_master *master;
struct omap1_spi100k *spi100k;
struct resource *r;
unsigned limit = 500;
unsigned long flags;
int status = 0;
master = dev_get_drvdata(&pdev->dev);
spi100k = spi_master_get_devdata(master);
spin_lock_irqsave(&spi100k->lock, flags);
spi100k->state = SPI_SHUTDOWN;
while (!list_empty(&spi100k->msg_queue) && limit--) {
spin_unlock_irqrestore(&spi100k->lock, flags);
msleep(10);
spin_lock_irqsave(&spi100k->lock, flags);
}
if (!list_empty(&spi100k->msg_queue))
status = -EBUSY;
spin_unlock_irqrestore(&spi100k->lock, flags);
if (status != 0)
return status;
clk_put(spi100k->fck);
clk_put(spi100k->ick);
r = platform_get_resource(pdev, IORESOURCE_MEM, 0);
spi_unregister_master(master);
return 0;
}
static struct platform_driver omap1_spi100k_driver = {
.driver = {
.name = "omap1_spi100k",
.owner = THIS_MODULE,
},
.remove = __exit_p(omap1_spi100k_remove),
};
static int __init omap1_spi100k_init(void)
{
omap1_spi100k_wq = create_singlethread_workqueue(
omap1_spi100k_driver.driver.name);
if (omap1_spi100k_wq == NULL)
return -1;
return platform_driver_probe(&omap1_spi100k_driver, omap1_spi100k_probe);
}
static void __exit omap1_spi100k_exit(void)
{
platform_driver_unregister(&omap1_spi100k_driver);
destroy_workqueue(omap1_spi100k_wq);
}
module_init(omap1_spi100k_init);
module_exit(omap1_spi100k_exit);
MODULE_DESCRIPTION("OMAP7xx SPI 100k controller driver");
MODULE_AUTHOR("Fabrice Crohas <fcrohas@gmail.com>");
MODULE_LICENSE("GPL");

View File

@ -44,6 +44,9 @@
#define MXC_CSPIINT 0x0c
#define MXC_RESET 0x1c
#define MX3_CSPISTAT 0x14
#define MX3_CSPISTAT_RR (1 << 3)
/* generic defines to abstract from the different register layouts */
#define MXC_INT_RR (1 << 0) /* Receive data ready interrupt */
#define MXC_INT_TE (1 << 1) /* Transmit FIFO empty interrupt */
@ -205,7 +208,7 @@ static int mx31_config(struct spi_imx_data *spi_imx,
if (cpu_is_mx31())
reg |= (config->bpw - 1) << MX31_CSPICTRL_BC_SHIFT;
else if (cpu_is_mx35()) {
else if (cpu_is_mx25() || cpu_is_mx35()) {
reg |= (config->bpw - 1) << MX35_CSPICTRL_BL_SHIFT;
reg |= MX31_CSPICTRL_SSCTL;
}
@ -219,7 +222,7 @@ static int mx31_config(struct spi_imx_data *spi_imx,
if (config->cs < 0) {
if (cpu_is_mx31())
reg |= (config->cs + 32) << MX31_CSPICTRL_CS_SHIFT;
else if (cpu_is_mx35())
else if (cpu_is_mx25() || cpu_is_mx35())
reg |= (config->cs + 32) << MX35_CSPICTRL_CS_SHIFT;
}
@ -481,7 +484,7 @@ static void spi_imx_cleanup(struct spi_device *spi)
{
}
static int __init spi_imx_probe(struct platform_device *pdev)
static int __devinit spi_imx_probe(struct platform_device *pdev)
{
struct spi_imx_master *mxc_platform_info;
struct spi_master *master;
@ -489,7 +492,7 @@ static int __init spi_imx_probe(struct platform_device *pdev)
struct resource *res;
int i, ret;
mxc_platform_info = (struct spi_imx_master *)pdev->dev.platform_data;
mxc_platform_info = dev_get_platdata(&pdev->dev);
if (!mxc_platform_info) {
dev_err(&pdev->dev, "can't get the platform data\n");
return -EINVAL;
@ -513,11 +516,12 @@ static int __init spi_imx_probe(struct platform_device *pdev)
continue;
ret = gpio_request(spi_imx->chipselect[i], DRIVER_NAME);
if (ret) {
i--;
while (i > 0)
while (i > 0) {
i--;
if (spi_imx->chipselect[i] >= 0)
gpio_free(spi_imx->chipselect[i--]);
dev_err(&pdev->dev, "can't get cs gpios");
gpio_free(spi_imx->chipselect[i]);
}
dev_err(&pdev->dev, "can't get cs gpios\n");
goto out_master_put;
}
}
@ -551,7 +555,7 @@ static int __init spi_imx_probe(struct platform_device *pdev)
}
spi_imx->irq = platform_get_irq(pdev, 0);
if (!spi_imx->irq) {
if (spi_imx->irq <= 0) {
ret = -EINVAL;
goto out_iounmap;
}
@ -562,7 +566,7 @@ static int __init spi_imx_probe(struct platform_device *pdev)
goto out_iounmap;
}
if (cpu_is_mx31() || cpu_is_mx35()) {
if (cpu_is_mx25() || cpu_is_mx31() || cpu_is_mx35()) {
spi_imx->intctrl = mx31_intctrl;
spi_imx->config = mx31_config;
spi_imx->trigger = mx31_trigger;
@ -590,9 +594,14 @@ static int __init spi_imx_probe(struct platform_device *pdev)
clk_enable(spi_imx->clk);
spi_imx->spi_clk = clk_get_rate(spi_imx->clk);
if (!cpu_is_mx31() || !cpu_is_mx35())
if (cpu_is_mx1() || cpu_is_mx21() || cpu_is_mx27())
writel(1, spi_imx->base + MXC_RESET);
/* drain receive buffer */
if (cpu_is_mx25() || cpu_is_mx31() || cpu_is_mx35())
while (readl(spi_imx->base + MX3_CSPISTAT) & MX3_CSPISTAT_RR)
readl(spi_imx->base + MXC_CSPIRXDATA);
spi_imx->intctrl(spi_imx, 0);
ret = spi_bitbang_start(&spi_imx->bitbang);
@ -625,7 +634,7 @@ static int __init spi_imx_probe(struct platform_device *pdev)
return ret;
}
static int __exit spi_imx_remove(struct platform_device *pdev)
static int __devexit spi_imx_remove(struct platform_device *pdev)
{
struct spi_master *master = platform_get_drvdata(pdev);
struct resource *res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
@ -659,7 +668,7 @@ static struct platform_driver spi_imx_driver = {
.owner = THIS_MODULE,
},
.probe = spi_imx_probe,
.remove = __exit_p(spi_imx_remove),
.remove = __devexit_p(spi_imx_remove),
};
static int __init spi_imx_init(void)

View File

@ -1356,7 +1356,7 @@ static int __devexit plat_mpc8xxx_spi_remove(struct platform_device *pdev)
MODULE_ALIAS("platform:mpc8xxx_spi");
static struct platform_driver mpc8xxx_spi_driver = {
.probe = plat_mpc8xxx_spi_probe,
.remove = __exit_p(plat_mpc8xxx_spi_remove),
.remove = __devexit_p(plat_mpc8xxx_spi_remove),
.driver = {
.name = "mpc8xxx_spi",
.owner = THIS_MODULE,

504
drivers/spi/spi_nuc900.c Normal file
View File

@ -0,0 +1,504 @@
/* linux/drivers/spi/spi_nuc900.c
*
* Copyright (c) 2009 Nuvoton technology.
* Wan ZongShun <mcuos.com@gmail.com>
*
* 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/device.h>
#include <linux/platform_device.h>
#include <linux/gpio.h>
#include <linux/io.h>
#include <linux/spi/spi.h>
#include <linux/spi/spi_bitbang.h>
#include <mach/nuc900_spi.h>
/* usi registers offset */
#define USI_CNT 0x00
#define USI_DIV 0x04
#define USI_SSR 0x08
#define USI_RX0 0x10
#define USI_TX0 0x10
/* usi register bit */
#define ENINT (0x01 << 17)
#define ENFLG (0x01 << 16)
#define TXNUM (0x03 << 8)
#define TXNEG (0x01 << 2)
#define RXNEG (0x01 << 1)
#define LSB (0x01 << 10)
#define SELECTLEV (0x01 << 2)
#define SELECTPOL (0x01 << 31)
#define SELECTSLAVE 0x01
#define GOBUSY 0x01
struct nuc900_spi {
struct spi_bitbang bitbang;
struct completion done;
void __iomem *regs;
int irq;
int len;
int count;
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 nuc900_spi_info *pdata;
spinlock_t lock;
struct resource *res;
};
static inline struct nuc900_spi *to_hw(struct spi_device *sdev)
{
return spi_master_get_devdata(sdev->master);
}
static void nuc900_slave_select(struct spi_device *spi, unsigned int ssr)
{
struct nuc900_spi *hw = to_hw(spi);
unsigned int val;
unsigned int cs = spi->mode & SPI_CS_HIGH ? 1 : 0;
unsigned int cpol = spi->mode & SPI_CPOL ? 1 : 0;
unsigned long flags;
spin_lock_irqsave(&hw->lock, flags);
val = __raw_readl(hw->regs + USI_SSR);
if (!cs)
val &= ~SELECTLEV;
else
val |= SELECTLEV;
if (!ssr)
val &= ~SELECTSLAVE;
else
val |= SELECTSLAVE;
__raw_writel(val, hw->regs + USI_SSR);
val = __raw_readl(hw->regs + USI_CNT);
if (!cpol)
val &= ~SELECTPOL;
else
val |= SELECTPOL;
__raw_writel(val, hw->regs + USI_CNT);
spin_unlock_irqrestore(&hw->lock, flags);
}
static void nuc900_spi_chipsel(struct spi_device *spi, int value)
{
switch (value) {
case BITBANG_CS_INACTIVE:
nuc900_slave_select(spi, 0);
break;
case BITBANG_CS_ACTIVE:
nuc900_slave_select(spi, 1);
break;
}
}
static void nuc900_spi_setup_txnum(struct nuc900_spi *hw,
unsigned int txnum)
{
unsigned int val;
unsigned long flags;
spin_lock_irqsave(&hw->lock, flags);
val = __raw_readl(hw->regs + USI_CNT);
if (!txnum)
val &= ~TXNUM;
else
val |= txnum << 0x08;
__raw_writel(val, hw->regs + USI_CNT);
spin_unlock_irqrestore(&hw->lock, flags);
}
static void nuc900_spi_setup_txbitlen(struct nuc900_spi *hw,
unsigned int txbitlen)
{
unsigned int val;
unsigned long flags;
spin_lock_irqsave(&hw->lock, flags);
val = __raw_readl(hw->regs + USI_CNT);
val |= (txbitlen << 0x03);
__raw_writel(val, hw->regs + USI_CNT);
spin_unlock_irqrestore(&hw->lock, flags);
}
static void nuc900_spi_gobusy(struct nuc900_spi *hw)
{
unsigned int val;
unsigned long flags;
spin_lock_irqsave(&hw->lock, flags);
val = __raw_readl(hw->regs + USI_CNT);
val |= GOBUSY;
__raw_writel(val, hw->regs + USI_CNT);
spin_unlock_irqrestore(&hw->lock, flags);
}
static int nuc900_spi_setupxfer(struct spi_device *spi,
struct spi_transfer *t)
{
return 0;
}
static int nuc900_spi_setup(struct spi_device *spi)
{
return 0;
}
static inline unsigned int hw_txbyte(struct nuc900_spi *hw, int count)
{
return hw->tx ? hw->tx[count] : 0;
}
static int nuc900_spi_txrx(struct spi_device *spi, struct spi_transfer *t)
{
struct nuc900_spi *hw = to_hw(spi);
hw->tx = t->tx_buf;
hw->rx = t->rx_buf;
hw->len = t->len;
hw->count = 0;
__raw_writel(hw_txbyte(hw, 0x0), hw->regs + USI_TX0);
nuc900_spi_gobusy(hw);
wait_for_completion(&hw->done);
return hw->count;
}
static irqreturn_t nuc900_spi_irq(int irq, void *dev)
{
struct nuc900_spi *hw = dev;
unsigned int status;
unsigned int count = hw->count;
status = __raw_readl(hw->regs + USI_CNT);
__raw_writel(status, hw->regs + USI_CNT);
if (status & ENFLG) {
hw->count++;
if (hw->rx)
hw->rx[count] = __raw_readl(hw->regs + USI_RX0);
count++;
if (count < hw->len) {
__raw_writel(hw_txbyte(hw, count), hw->regs + USI_TX0);
nuc900_spi_gobusy(hw);
} else {
complete(&hw->done);
}
return IRQ_HANDLED;
}
complete(&hw->done);
return IRQ_HANDLED;
}
static void nuc900_tx_edge(struct nuc900_spi *hw, unsigned int edge)
{
unsigned int val;
unsigned long flags;
spin_lock_irqsave(&hw->lock, flags);
val = __raw_readl(hw->regs + USI_CNT);
if (edge)
val |= TXNEG;
else
val &= ~TXNEG;
__raw_writel(val, hw->regs + USI_CNT);
spin_unlock_irqrestore(&hw->lock, flags);
}
static void nuc900_rx_edge(struct nuc900_spi *hw, unsigned int edge)
{
unsigned int val;
unsigned long flags;
spin_lock_irqsave(&hw->lock, flags);
val = __raw_readl(hw->regs + USI_CNT);
if (edge)
val |= RXNEG;
else
val &= ~RXNEG;
__raw_writel(val, hw->regs + USI_CNT);
spin_unlock_irqrestore(&hw->lock, flags);
}
static void nuc900_send_first(struct nuc900_spi *hw, unsigned int lsb)
{
unsigned int val;
unsigned long flags;
spin_lock_irqsave(&hw->lock, flags);
val = __raw_readl(hw->regs + USI_CNT);
if (lsb)
val |= LSB;
else
val &= ~LSB;
__raw_writel(val, hw->regs + USI_CNT);
spin_unlock_irqrestore(&hw->lock, flags);
}
static void nuc900_set_sleep(struct nuc900_spi *hw, unsigned int sleep)
{
unsigned int val;
unsigned long flags;
spin_lock_irqsave(&hw->lock, flags);
val = __raw_readl(hw->regs + USI_CNT);
if (sleep)
val |= (sleep << 12);
else
val &= ~(0x0f << 12);
__raw_writel(val, hw->regs + USI_CNT);
spin_unlock_irqrestore(&hw->lock, flags);
}
static void nuc900_enable_int(struct nuc900_spi *hw)
{
unsigned int val;
unsigned long flags;
spin_lock_irqsave(&hw->lock, flags);
val = __raw_readl(hw->regs + USI_CNT);
val |= ENINT;
__raw_writel(val, hw->regs + USI_CNT);
spin_unlock_irqrestore(&hw->lock, flags);
}
static void nuc900_set_divider(struct nuc900_spi *hw)
{
__raw_writel(hw->pdata->divider, hw->regs + USI_DIV);
}
static void nuc900_init_spi(struct nuc900_spi *hw)
{
clk_enable(hw->clk);
spin_lock_init(&hw->lock);
nuc900_tx_edge(hw, hw->pdata->txneg);
nuc900_rx_edge(hw, hw->pdata->rxneg);
nuc900_send_first(hw, hw->pdata->lsb);
nuc900_set_sleep(hw, hw->pdata->sleep);
nuc900_spi_setup_txbitlen(hw, hw->pdata->txbitlen);
nuc900_spi_setup_txnum(hw, hw->pdata->txnum);
nuc900_set_divider(hw);
nuc900_enable_int(hw);
}
static int __devinit nuc900_spi_probe(struct platform_device *pdev)
{
struct nuc900_spi *hw;
struct spi_master *master;
int err = 0;
master = spi_alloc_master(&pdev->dev, sizeof(struct nuc900_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 nuc900_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_pdata;
}
platform_set_drvdata(pdev, hw);
init_completion(&hw->done);
master->mode_bits = SPI_MODE_0;
master->num_chipselect = hw->pdata->num_cs;
master->bus_num = hw->pdata->bus_num;
hw->bitbang.master = hw->master;
hw->bitbang.setup_transfer = nuc900_spi_setupxfer;
hw->bitbang.chipselect = nuc900_spi_chipsel;
hw->bitbang.txrx_bufs = nuc900_spi_txrx;
hw->bitbang.master->setup = nuc900_spi_setup;
hw->res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
if (hw->res == NULL) {
dev_err(&pdev->dev, "Cannot get IORESOURCE_MEM\n");
err = -ENOENT;
goto err_pdata;
}
hw->ioarea = request_mem_region(hw->res->start,
resource_size(hw->res), pdev->name);
if (hw->ioarea == NULL) {
dev_err(&pdev->dev, "Cannot reserve region\n");
err = -ENXIO;
goto err_pdata;
}
hw->regs = ioremap(hw->res->start, resource_size(hw->res));
if (hw->regs == NULL) {
dev_err(&pdev->dev, "Cannot map IO\n");
err = -ENXIO;
goto err_iomap;
}
hw->irq = platform_get_irq(pdev, 0);
if (hw->irq < 0) {
dev_err(&pdev->dev, "No IRQ specified\n");
err = -ENOENT;
goto err_irq;
}
err = request_irq(hw->irq, nuc900_spi_irq, 0, pdev->name, hw);
if (err) {
dev_err(&pdev->dev, "Cannot claim IRQ\n");
goto err_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_clk;
}
mfp_set_groupg(&pdev->dev);
nuc900_init_spi(hw);
err = spi_bitbang_start(&hw->bitbang);
if (err) {
dev_err(&pdev->dev, "Failed to register SPI master\n");
goto err_register;
}
return 0;
err_register:
clk_disable(hw->clk);
clk_put(hw->clk);
err_clk:
free_irq(hw->irq, hw);
err_irq:
iounmap(hw->regs);
err_iomap:
release_mem_region(hw->res->start, resource_size(hw->res));
kfree(hw->ioarea);
err_pdata:
spi_master_put(hw->master);;
err_nomem:
return err;
}
static int __devexit nuc900_spi_remove(struct platform_device *dev)
{
struct nuc900_spi *hw = platform_get_drvdata(dev);
free_irq(hw->irq, hw);
platform_set_drvdata(dev, NULL);
spi_unregister_master(hw->master);
clk_disable(hw->clk);
clk_put(hw->clk);
iounmap(hw->regs);
release_mem_region(hw->res->start, resource_size(hw->res));
kfree(hw->ioarea);
spi_master_put(hw->master);
return 0;
}
static struct platform_driver nuc900_spi_driver = {
.probe = nuc900_spi_probe,
.remove = __devexit_p(nuc900_spi_remove),
.driver = {
.name = "nuc900-spi",
.owner = THIS_MODULE,
},
};
static int __init nuc900_spi_init(void)
{
return platform_driver_register(&nuc900_spi_driver);
}
static void __exit nuc900_spi_exit(void)
{
platform_driver_unregister(&nuc900_spi_driver);
}
module_init(nuc900_spi_init);
module_exit(nuc900_spi_exit);
MODULE_AUTHOR("Wan ZongShun <mcuos.com@gmail.com>");
MODULE_DESCRIPTION("nuc900 spi driver!");
MODULE_LICENSE("GPL");
MODULE_ALIAS("platform:nuc900-spi");

691
drivers/spi/spi_sh_msiof.c Normal file
View File

@ -0,0 +1,691 @@
/*
* SuperH MSIOF SPI Master Interface
*
* Copyright (c) 2009 Magnus Damm
*
* 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/interrupt.h>
#include <linux/platform_device.h>
#include <linux/completion.h>
#include <linux/pm_runtime.h>
#include <linux/gpio.h>
#include <linux/bitmap.h>
#include <linux/clk.h>
#include <linux/io.h>
#include <linux/spi/spi.h>
#include <linux/spi/spi_bitbang.h>
#include <linux/spi/sh_msiof.h>
#include <asm/spi.h>
#include <asm/unaligned.h>
struct sh_msiof_spi_priv {
struct spi_bitbang bitbang; /* must be first for spi_bitbang.c */
void __iomem *mapbase;
struct clk *clk;
struct platform_device *pdev;
struct sh_msiof_spi_info *info;
struct completion done;
unsigned long flags;
int tx_fifo_size;
int rx_fifo_size;
};
#define TMDR1 0x00
#define TMDR2 0x04
#define TMDR3 0x08
#define RMDR1 0x10
#define RMDR2 0x14
#define RMDR3 0x18
#define TSCR 0x20
#define RSCR 0x22
#define CTR 0x28
#define FCTR 0x30
#define STR 0x40
#define IER 0x44
#define TDR1 0x48
#define TDR2 0x4c
#define TFDR 0x50
#define RDR1 0x58
#define RDR2 0x5c
#define RFDR 0x60
#define CTR_TSCKE (1 << 15)
#define CTR_TFSE (1 << 14)
#define CTR_TXE (1 << 9)
#define CTR_RXE (1 << 8)
#define STR_TEOF (1 << 23)
#define STR_REOF (1 << 7)
static unsigned long sh_msiof_read(struct sh_msiof_spi_priv *p, int reg_offs)
{
switch (reg_offs) {
case TSCR:
case RSCR:
return ioread16(p->mapbase + reg_offs);
default:
return ioread32(p->mapbase + reg_offs);
}
}
static void sh_msiof_write(struct sh_msiof_spi_priv *p, int reg_offs,
unsigned long value)
{
switch (reg_offs) {
case TSCR:
case RSCR:
iowrite16(value, p->mapbase + reg_offs);
break;
default:
iowrite32(value, p->mapbase + reg_offs);
break;
}
}
static int sh_msiof_modify_ctr_wait(struct sh_msiof_spi_priv *p,
unsigned long clr, unsigned long set)
{
unsigned long mask = clr | set;
unsigned long data;
int k;
data = sh_msiof_read(p, CTR);
data &= ~clr;
data |= set;
sh_msiof_write(p, CTR, data);
for (k = 100; k > 0; k--) {
if ((sh_msiof_read(p, CTR) & mask) == set)
break;
udelay(10);
}
return k > 0 ? 0 : -ETIMEDOUT;
}
static irqreturn_t sh_msiof_spi_irq(int irq, void *data)
{
struct sh_msiof_spi_priv *p = data;
/* just disable the interrupt and wake up */
sh_msiof_write(p, IER, 0);
complete(&p->done);
return IRQ_HANDLED;
}
static struct {
unsigned short div;
unsigned short scr;
} const sh_msiof_spi_clk_table[] = {
{ 1, 0x0007 },
{ 2, 0x0000 },
{ 4, 0x0001 },
{ 8, 0x0002 },
{ 16, 0x0003 },
{ 32, 0x0004 },
{ 64, 0x1f00 },
{ 128, 0x1f01 },
{ 256, 0x1f02 },
{ 512, 0x1f03 },
{ 1024, 0x1f04 },
};
static void sh_msiof_spi_set_clk_regs(struct sh_msiof_spi_priv *p,
unsigned long parent_rate,
unsigned long spi_hz)
{
unsigned long div = 1024;
size_t k;
if (!WARN_ON(!spi_hz || !parent_rate))
div = parent_rate / spi_hz;
/* TODO: make more fine grained */
for (k = 0; k < ARRAY_SIZE(sh_msiof_spi_clk_table); k++) {
if (sh_msiof_spi_clk_table[k].div >= div)
break;
}
k = min_t(int, k, ARRAY_SIZE(sh_msiof_spi_clk_table) - 1);
sh_msiof_write(p, TSCR, sh_msiof_spi_clk_table[k].scr);
sh_msiof_write(p, RSCR, sh_msiof_spi_clk_table[k].scr);
}
static void sh_msiof_spi_set_pin_regs(struct sh_msiof_spi_priv *p,
int cpol, int cpha,
int tx_hi_z, int lsb_first)
{
unsigned long tmp;
int edge;
/*
* CPOL CPHA TSCKIZ RSCKIZ TEDG REDG(!)
* 0 0 10 10 1 0
* 0 1 10 10 0 1
* 1 0 11 11 0 1
* 1 1 11 11 1 0
*
* (!) Note: REDG is inverted recommended data sheet setting
*/
sh_msiof_write(p, FCTR, 0);
sh_msiof_write(p, TMDR1, 0xe2000005 | (lsb_first << 24));
sh_msiof_write(p, RMDR1, 0x22000005 | (lsb_first << 24));
tmp = 0xa0000000;
tmp |= cpol << 30; /* TSCKIZ */
tmp |= cpol << 28; /* RSCKIZ */
edge = cpol ? cpha : !cpha;
tmp |= edge << 27; /* TEDG */
tmp |= !edge << 26; /* REDG */
tmp |= (tx_hi_z ? 2 : 0) << 22; /* TXDIZ */
sh_msiof_write(p, CTR, tmp);
}
static void sh_msiof_spi_set_mode_regs(struct sh_msiof_spi_priv *p,
const void *tx_buf, void *rx_buf,
int bits, int words)
{
unsigned long dr2;
dr2 = ((bits - 1) << 24) | ((words - 1) << 16);
if (tx_buf)
sh_msiof_write(p, TMDR2, dr2);
else
sh_msiof_write(p, TMDR2, dr2 | 1);
if (rx_buf)
sh_msiof_write(p, RMDR2, dr2);
sh_msiof_write(p, IER, STR_TEOF | STR_REOF);
}
static void sh_msiof_reset_str(struct sh_msiof_spi_priv *p)
{
sh_msiof_write(p, STR, sh_msiof_read(p, STR));
}
static void sh_msiof_spi_write_fifo_8(struct sh_msiof_spi_priv *p,
const void *tx_buf, int words, int fs)
{
const unsigned char *buf_8 = tx_buf;
int k;
for (k = 0; k < words; k++)
sh_msiof_write(p, TFDR, buf_8[k] << fs);
}
static void sh_msiof_spi_write_fifo_16(struct sh_msiof_spi_priv *p,
const void *tx_buf, int words, int fs)
{
const unsigned short *buf_16 = tx_buf;
int k;
for (k = 0; k < words; k++)
sh_msiof_write(p, TFDR, buf_16[k] << fs);
}
static void sh_msiof_spi_write_fifo_16u(struct sh_msiof_spi_priv *p,
const void *tx_buf, int words, int fs)
{
const unsigned short *buf_16 = tx_buf;
int k;
for (k = 0; k < words; k++)
sh_msiof_write(p, TFDR, get_unaligned(&buf_16[k]) << fs);
}
static void sh_msiof_spi_write_fifo_32(struct sh_msiof_spi_priv *p,
const void *tx_buf, int words, int fs)
{
const unsigned int *buf_32 = tx_buf;
int k;
for (k = 0; k < words; k++)
sh_msiof_write(p, TFDR, buf_32[k] << fs);
}
static void sh_msiof_spi_write_fifo_32u(struct sh_msiof_spi_priv *p,
const void *tx_buf, int words, int fs)
{
const unsigned int *buf_32 = tx_buf;
int k;
for (k = 0; k < words; k++)
sh_msiof_write(p, TFDR, get_unaligned(&buf_32[k]) << fs);
}
static void sh_msiof_spi_read_fifo_8(struct sh_msiof_spi_priv *p,
void *rx_buf, int words, int fs)
{
unsigned char *buf_8 = rx_buf;
int k;
for (k = 0; k < words; k++)
buf_8[k] = sh_msiof_read(p, RFDR) >> fs;
}
static void sh_msiof_spi_read_fifo_16(struct sh_msiof_spi_priv *p,
void *rx_buf, int words, int fs)
{
unsigned short *buf_16 = rx_buf;
int k;
for (k = 0; k < words; k++)
buf_16[k] = sh_msiof_read(p, RFDR) >> fs;
}
static void sh_msiof_spi_read_fifo_16u(struct sh_msiof_spi_priv *p,
void *rx_buf, int words, int fs)
{
unsigned short *buf_16 = rx_buf;
int k;
for (k = 0; k < words; k++)
put_unaligned(sh_msiof_read(p, RFDR) >> fs, &buf_16[k]);
}
static void sh_msiof_spi_read_fifo_32(struct sh_msiof_spi_priv *p,
void *rx_buf, int words, int fs)
{
unsigned int *buf_32 = rx_buf;
int k;
for (k = 0; k < words; k++)
buf_32[k] = sh_msiof_read(p, RFDR) >> fs;
}
static void sh_msiof_spi_read_fifo_32u(struct sh_msiof_spi_priv *p,
void *rx_buf, int words, int fs)
{
unsigned int *buf_32 = rx_buf;
int k;
for (k = 0; k < words; k++)
put_unaligned(sh_msiof_read(p, RFDR) >> fs, &buf_32[k]);
}
static int sh_msiof_spi_bits(struct spi_device *spi, struct spi_transfer *t)
{
int bits;
bits = t ? t->bits_per_word : 0;
bits = bits ? bits : spi->bits_per_word;
return bits;
}
static unsigned long sh_msiof_spi_hz(struct spi_device *spi,
struct spi_transfer *t)
{
unsigned long hz;
hz = t ? t->speed_hz : 0;
hz = hz ? hz : spi->max_speed_hz;
return hz;
}
static int sh_msiof_spi_setup_transfer(struct spi_device *spi,
struct spi_transfer *t)
{
int bits;
/* noting to check hz values against since parent clock is disabled */
bits = sh_msiof_spi_bits(spi, t);
if (bits < 8)
return -EINVAL;
if (bits > 32)
return -EINVAL;
return spi_bitbang_setup_transfer(spi, t);
}
static void sh_msiof_spi_chipselect(struct spi_device *spi, int is_on)
{
struct sh_msiof_spi_priv *p = spi_master_get_devdata(spi->master);
int value;
/* chip select is active low unless SPI_CS_HIGH is set */
if (spi->mode & SPI_CS_HIGH)
value = (is_on == BITBANG_CS_ACTIVE) ? 1 : 0;
else
value = (is_on == BITBANG_CS_ACTIVE) ? 0 : 1;
if (is_on == BITBANG_CS_ACTIVE) {
if (!test_and_set_bit(0, &p->flags)) {
pm_runtime_get_sync(&p->pdev->dev);
clk_enable(p->clk);
}
/* Configure pins before asserting CS */
sh_msiof_spi_set_pin_regs(p, !!(spi->mode & SPI_CPOL),
!!(spi->mode & SPI_CPHA),
!!(spi->mode & SPI_3WIRE),
!!(spi->mode & SPI_LSB_FIRST));
}
/* use spi->controller data for CS (same strategy as spi_gpio) */
gpio_set_value((unsigned)spi->controller_data, value);
if (is_on == BITBANG_CS_INACTIVE) {
if (test_and_clear_bit(0, &p->flags)) {
clk_disable(p->clk);
pm_runtime_put(&p->pdev->dev);
}
}
}
static int sh_msiof_spi_txrx_once(struct sh_msiof_spi_priv *p,
void (*tx_fifo)(struct sh_msiof_spi_priv *,
const void *, int, int),
void (*rx_fifo)(struct sh_msiof_spi_priv *,
void *, int, int),
const void *tx_buf, void *rx_buf,
int words, int bits)
{
int fifo_shift;
int ret;
/* limit maximum word transfer to rx/tx fifo size */
if (tx_buf)
words = min_t(int, words, p->tx_fifo_size);
if (rx_buf)
words = min_t(int, words, p->rx_fifo_size);
/* the fifo contents need shifting */
fifo_shift = 32 - bits;
/* setup msiof transfer mode registers */
sh_msiof_spi_set_mode_regs(p, tx_buf, rx_buf, bits, words);
/* write tx fifo */
if (tx_buf)
tx_fifo(p, tx_buf, words, fifo_shift);
/* setup clock and rx/tx signals */
ret = sh_msiof_modify_ctr_wait(p, 0, CTR_TSCKE);
if (rx_buf)
ret = ret ? ret : sh_msiof_modify_ctr_wait(p, 0, CTR_RXE);
ret = ret ? ret : sh_msiof_modify_ctr_wait(p, 0, CTR_TXE);
/* start by setting frame bit */
INIT_COMPLETION(p->done);
ret = ret ? ret : sh_msiof_modify_ctr_wait(p, 0, CTR_TFSE);
if (ret) {
dev_err(&p->pdev->dev, "failed to start hardware\n");
goto err;
}
/* wait for tx fifo to be emptied / rx fifo to be filled */
wait_for_completion(&p->done);
/* read rx fifo */
if (rx_buf)
rx_fifo(p, rx_buf, words, fifo_shift);
/* clear status bits */
sh_msiof_reset_str(p);
/* shut down frame, tx/tx and clock signals */
ret = sh_msiof_modify_ctr_wait(p, CTR_TFSE, 0);
ret = ret ? ret : sh_msiof_modify_ctr_wait(p, CTR_TXE, 0);
if (rx_buf)
ret = ret ? ret : sh_msiof_modify_ctr_wait(p, CTR_RXE, 0);
ret = ret ? ret : sh_msiof_modify_ctr_wait(p, CTR_TSCKE, 0);
if (ret) {
dev_err(&p->pdev->dev, "failed to shut down hardware\n");
goto err;
}
return words;
err:
sh_msiof_write(p, IER, 0);
return ret;
}
static int sh_msiof_spi_txrx(struct spi_device *spi, struct spi_transfer *t)
{
struct sh_msiof_spi_priv *p = spi_master_get_devdata(spi->master);
void (*tx_fifo)(struct sh_msiof_spi_priv *, const void *, int, int);
void (*rx_fifo)(struct sh_msiof_spi_priv *, void *, int, int);
int bits;
int bytes_per_word;
int bytes_done;
int words;
int n;
bits = sh_msiof_spi_bits(spi, t);
/* setup bytes per word and fifo read/write functions */
if (bits <= 8) {
bytes_per_word = 1;
tx_fifo = sh_msiof_spi_write_fifo_8;
rx_fifo = sh_msiof_spi_read_fifo_8;
} else if (bits <= 16) {
bytes_per_word = 2;
if ((unsigned long)t->tx_buf & 0x01)
tx_fifo = sh_msiof_spi_write_fifo_16u;
else
tx_fifo = sh_msiof_spi_write_fifo_16;
if ((unsigned long)t->rx_buf & 0x01)
rx_fifo = sh_msiof_spi_read_fifo_16u;
else
rx_fifo = sh_msiof_spi_read_fifo_16;
} else {
bytes_per_word = 4;
if ((unsigned long)t->tx_buf & 0x03)
tx_fifo = sh_msiof_spi_write_fifo_32u;
else
tx_fifo = sh_msiof_spi_write_fifo_32;
if ((unsigned long)t->rx_buf & 0x03)
rx_fifo = sh_msiof_spi_read_fifo_32u;
else
rx_fifo = sh_msiof_spi_read_fifo_32;
}
/* setup clocks (clock already enabled in chipselect()) */
sh_msiof_spi_set_clk_regs(p, clk_get_rate(p->clk),
sh_msiof_spi_hz(spi, t));
/* transfer in fifo sized chunks */
words = t->len / bytes_per_word;
bytes_done = 0;
while (bytes_done < t->len) {
n = sh_msiof_spi_txrx_once(p, tx_fifo, rx_fifo,
t->tx_buf + bytes_done,
t->rx_buf + bytes_done,
words, bits);
if (n < 0)
break;
bytes_done += n * bytes_per_word;
words -= n;
}
return bytes_done;
}
static u32 sh_msiof_spi_txrx_word(struct spi_device *spi, unsigned nsecs,
u32 word, u8 bits)
{
BUG(); /* unused but needed by bitbang code */
return 0;
}
static int sh_msiof_spi_probe(struct platform_device *pdev)
{
struct resource *r;
struct spi_master *master;
struct sh_msiof_spi_priv *p;
char clk_name[16];
int i;
int ret;
master = spi_alloc_master(&pdev->dev, sizeof(struct sh_msiof_spi_priv));
if (master == NULL) {
dev_err(&pdev->dev, "failed to allocate spi master\n");
ret = -ENOMEM;
goto err0;
}
p = spi_master_get_devdata(master);
platform_set_drvdata(pdev, p);
p->info = pdev->dev.platform_data;
init_completion(&p->done);
snprintf(clk_name, sizeof(clk_name), "msiof%d", pdev->id);
p->clk = clk_get(&pdev->dev, clk_name);
if (IS_ERR(p->clk)) {
dev_err(&pdev->dev, "cannot get clock \"%s\"\n", clk_name);
ret = PTR_ERR(p->clk);
goto err1;
}
r = platform_get_resource(pdev, IORESOURCE_MEM, 0);
i = platform_get_irq(pdev, 0);
if (!r || i < 0) {
dev_err(&pdev->dev, "cannot get platform resources\n");
ret = -ENOENT;
goto err2;
}
p->mapbase = ioremap_nocache(r->start, resource_size(r));
if (!p->mapbase) {
dev_err(&pdev->dev, "unable to ioremap\n");
ret = -ENXIO;
goto err2;
}
ret = request_irq(i, sh_msiof_spi_irq, IRQF_DISABLED,
dev_name(&pdev->dev), p);
if (ret) {
dev_err(&pdev->dev, "unable to request irq\n");
goto err3;
}
p->pdev = pdev;
pm_runtime_enable(&pdev->dev);
/* The standard version of MSIOF use 64 word FIFOs */
p->tx_fifo_size = 64;
p->rx_fifo_size = 64;
/* Platform data may override FIFO sizes */
if (p->info->tx_fifo_override)
p->tx_fifo_size = p->info->tx_fifo_override;
if (p->info->rx_fifo_override)
p->rx_fifo_size = p->info->rx_fifo_override;
/* init master and bitbang code */
master->mode_bits = SPI_CPOL | SPI_CPHA | SPI_CS_HIGH;
master->mode_bits |= SPI_LSB_FIRST | SPI_3WIRE;
master->flags = 0;
master->bus_num = pdev->id;
master->num_chipselect = p->info->num_chipselect;
master->setup = spi_bitbang_setup;
master->cleanup = spi_bitbang_cleanup;
p->bitbang.master = master;
p->bitbang.chipselect = sh_msiof_spi_chipselect;
p->bitbang.setup_transfer = sh_msiof_spi_setup_transfer;
p->bitbang.txrx_bufs = sh_msiof_spi_txrx;
p->bitbang.txrx_word[SPI_MODE_0] = sh_msiof_spi_txrx_word;
p->bitbang.txrx_word[SPI_MODE_1] = sh_msiof_spi_txrx_word;
p->bitbang.txrx_word[SPI_MODE_2] = sh_msiof_spi_txrx_word;
p->bitbang.txrx_word[SPI_MODE_3] = sh_msiof_spi_txrx_word;
ret = spi_bitbang_start(&p->bitbang);
if (ret == 0)
return 0;
pm_runtime_disable(&pdev->dev);
err3:
iounmap(p->mapbase);
err2:
clk_put(p->clk);
err1:
spi_master_put(master);
err0:
return ret;
}
static int sh_msiof_spi_remove(struct platform_device *pdev)
{
struct sh_msiof_spi_priv *p = platform_get_drvdata(pdev);
int ret;
ret = spi_bitbang_stop(&p->bitbang);
if (!ret) {
pm_runtime_disable(&pdev->dev);
free_irq(platform_get_irq(pdev, 0), sh_msiof_spi_irq);
iounmap(p->mapbase);
clk_put(p->clk);
spi_master_put(p->bitbang.master);
}
return ret;
}
static int sh_msiof_spi_runtime_nop(struct device *dev)
{
/* Runtime PM callback shared between ->runtime_suspend()
* and ->runtime_resume(). Simply returns success.
*
* This driver re-initializes all registers after
* pm_runtime_get_sync() anyway so there is no need
* to save and restore registers here.
*/
return 0;
}
static struct dev_pm_ops sh_msiof_spi_dev_pm_ops = {
.runtime_suspend = sh_msiof_spi_runtime_nop,
.runtime_resume = sh_msiof_spi_runtime_nop,
};
static struct platform_driver sh_msiof_spi_drv = {
.probe = sh_msiof_spi_probe,
.remove = sh_msiof_spi_remove,
.driver = {
.name = "spi_sh_msiof",
.owner = THIS_MODULE,
.pm = &sh_msiof_spi_dev_pm_ops,
},
};
static int __init sh_msiof_spi_init(void)
{
return platform_driver_register(&sh_msiof_spi_drv);
}
module_init(sh_msiof_spi_init);
static void __exit sh_msiof_spi_exit(void)
{
platform_driver_unregister(&sh_msiof_spi_drv);
}
module_exit(sh_msiof_spi_exit);
MODULE_DESCRIPTION("SuperH MSIOF SPI Master Interface Driver");
MODULE_AUTHOR("Magnus Damm");
MODULE_LICENSE("GPL v2");
MODULE_ALIAS("platform:spi_sh_msiof");

View File

@ -266,15 +266,15 @@ static int spidev_message(struct spidev_data *spidev,
k_tmp->delay_usecs = u_tmp->delay_usecs;
k_tmp->speed_hz = u_tmp->speed_hz;
#ifdef VERBOSE
dev_dbg(&spi->dev,
dev_dbg(&spidev->spi->dev,
" xfer len %zd %s%s%s%dbits %u usec %uHz\n",
u_tmp->len,
u_tmp->rx_buf ? "rx " : "",
u_tmp->tx_buf ? "tx " : "",
u_tmp->cs_change ? "cs " : "",
u_tmp->bits_per_word ? : spi->bits_per_word,
u_tmp->bits_per_word ? : spidev->spi->bits_per_word,
u_tmp->delay_usecs,
u_tmp->speed_hz ? : spi->max_speed_hz);
u_tmp->speed_hz ? : spidev->spi->max_speed_hz);
#endif
spi_message_add_tail(k_tmp, &msg);
}

View File

@ -14,22 +14,20 @@
#include <linux/module.h>
#include <linux/init.h>
#include <linux/interrupt.h>
#include <linux/platform_device.h>
#include <linux/of_platform.h>
#include <linux/of_device.h>
#include <linux/of_spi.h>
#include <linux/spi/spi.h>
#include <linux/spi/spi_bitbang.h>
#include <linux/io.h>
#include "xilinx_spi.h"
#include <linux/spi/xilinx_spi.h>
#define XILINX_SPI_NAME "xilinx_spi"
/* Register definitions as per "OPB Serial Peripheral Interface (SPI) (v1.00e)
* Product Specification", DS464
*/
#define XSPI_CR_OFFSET 0x62 /* 16-bit Control Register */
#define XSPI_CR_OFFSET 0x60 /* Control Register */
#define XSPI_CR_ENABLE 0x02
#define XSPI_CR_MASTER_MODE 0x04
@ -40,8 +38,9 @@
#define XSPI_CR_RXFIFO_RESET 0x40
#define XSPI_CR_MANUAL_SSELECT 0x80
#define XSPI_CR_TRANS_INHIBIT 0x100
#define XSPI_CR_LSB_FIRST 0x200
#define XSPI_SR_OFFSET 0x67 /* 8-bit Status Register */
#define XSPI_SR_OFFSET 0x64 /* Status Register */
#define XSPI_SR_RX_EMPTY_MASK 0x01 /* Receive FIFO is empty */
#define XSPI_SR_RX_FULL_MASK 0x02 /* Receive FIFO is full */
@ -49,8 +48,8 @@
#define XSPI_SR_TX_FULL_MASK 0x08 /* Transmit FIFO is full */
#define XSPI_SR_MODE_FAULT_MASK 0x10 /* Mode fault error */
#define XSPI_TXD_OFFSET 0x6b /* 8-bit Data Transmit Register */
#define XSPI_RXD_OFFSET 0x6f /* 8-bit Data Receive Register */
#define XSPI_TXD_OFFSET 0x68 /* Data Transmit Register */
#define XSPI_RXD_OFFSET 0x6c /* Data Receive Register */
#define XSPI_SSR_OFFSET 0x70 /* 32-bit Slave Select Register */
@ -70,6 +69,7 @@
#define XSPI_INTR_TX_UNDERRUN 0x08 /* TxFIFO was underrun */
#define XSPI_INTR_RX_FULL 0x10 /* RxFIFO is full */
#define XSPI_INTR_RX_OVERRUN 0x20 /* RxFIFO was overrun */
#define XSPI_INTR_TX_HALF_EMPTY 0x40 /* TxFIFO is half empty */
#define XIPIF_V123B_RESETR_OFFSET 0x40 /* IPIF reset register */
#define XIPIF_V123B_RESET_MASK 0x0a /* the value to write */
@ -78,35 +78,85 @@ struct xilinx_spi {
/* bitbang has to be first */
struct spi_bitbang bitbang;
struct completion done;
struct resource mem; /* phys mem */
void __iomem *regs; /* virt. address of the control registers */
u32 irq;
u32 speed_hz; /* SCK has a fixed frequency of speed_hz Hz */
u8 *rx_ptr; /* pointer in the Tx buffer */
const u8 *tx_ptr; /* pointer in the Rx buffer */
int remaining_bytes; /* the number of bytes left to transfer */
u8 bits_per_word;
unsigned int (*read_fn) (void __iomem *);
void (*write_fn) (u32, void __iomem *);
void (*tx_fn) (struct xilinx_spi *);
void (*rx_fn) (struct xilinx_spi *);
};
static void xspi_init_hw(void __iomem *regs_base)
static void xspi_tx8(struct xilinx_spi *xspi)
{
xspi->write_fn(*xspi->tx_ptr, xspi->regs + XSPI_TXD_OFFSET);
xspi->tx_ptr++;
}
static void xspi_tx16(struct xilinx_spi *xspi)
{
xspi->write_fn(*(u16 *)(xspi->tx_ptr), xspi->regs + XSPI_TXD_OFFSET);
xspi->tx_ptr += 2;
}
static void xspi_tx32(struct xilinx_spi *xspi)
{
xspi->write_fn(*(u32 *)(xspi->tx_ptr), xspi->regs + XSPI_TXD_OFFSET);
xspi->tx_ptr += 4;
}
static void xspi_rx8(struct xilinx_spi *xspi)
{
u32 data = xspi->read_fn(xspi->regs + XSPI_RXD_OFFSET);
if (xspi->rx_ptr) {
*xspi->rx_ptr = data & 0xff;
xspi->rx_ptr++;
}
}
static void xspi_rx16(struct xilinx_spi *xspi)
{
u32 data = xspi->read_fn(xspi->regs + XSPI_RXD_OFFSET);
if (xspi->rx_ptr) {
*(u16 *)(xspi->rx_ptr) = data & 0xffff;
xspi->rx_ptr += 2;
}
}
static void xspi_rx32(struct xilinx_spi *xspi)
{
u32 data = xspi->read_fn(xspi->regs + XSPI_RXD_OFFSET);
if (xspi->rx_ptr) {
*(u32 *)(xspi->rx_ptr) = data;
xspi->rx_ptr += 4;
}
}
static void xspi_init_hw(struct xilinx_spi *xspi)
{
void __iomem *regs_base = xspi->regs;
/* Reset the SPI device */
out_be32(regs_base + XIPIF_V123B_RESETR_OFFSET,
XIPIF_V123B_RESET_MASK);
xspi->write_fn(XIPIF_V123B_RESET_MASK,
regs_base + XIPIF_V123B_RESETR_OFFSET);
/* Disable all the interrupts just in case */
out_be32(regs_base + XIPIF_V123B_IIER_OFFSET, 0);
xspi->write_fn(0, regs_base + XIPIF_V123B_IIER_OFFSET);
/* Enable the global IPIF interrupt */
out_be32(regs_base + XIPIF_V123B_DGIER_OFFSET,
XIPIF_V123B_GINTR_ENABLE);
xspi->write_fn(XIPIF_V123B_GINTR_ENABLE,
regs_base + XIPIF_V123B_DGIER_OFFSET);
/* Deselect the slave on the SPI bus */
out_be32(regs_base + XSPI_SSR_OFFSET, 0xffff);
xspi->write_fn(0xffff, regs_base + XSPI_SSR_OFFSET);
/* Disable the transmitter, enable Manual Slave Select Assertion,
* put SPI controller into master mode, and enable it */
out_be16(regs_base + XSPI_CR_OFFSET,
XSPI_CR_TRANS_INHIBIT | XSPI_CR_MANUAL_SSELECT
| XSPI_CR_MASTER_MODE | XSPI_CR_ENABLE);
xspi->write_fn(XSPI_CR_TRANS_INHIBIT | XSPI_CR_MANUAL_SSELECT |
XSPI_CR_MASTER_MODE | XSPI_CR_ENABLE | XSPI_CR_TXFIFO_RESET |
XSPI_CR_RXFIFO_RESET, regs_base + XSPI_CR_OFFSET);
}
static void xilinx_spi_chipselect(struct spi_device *spi, int is_on)
@ -115,16 +165,16 @@ static void xilinx_spi_chipselect(struct spi_device *spi, int is_on)
if (is_on == BITBANG_CS_INACTIVE) {
/* Deselect the slave on the SPI bus */
out_be32(xspi->regs + XSPI_SSR_OFFSET, 0xffff);
xspi->write_fn(0xffff, xspi->regs + XSPI_SSR_OFFSET);
} else if (is_on == BITBANG_CS_ACTIVE) {
/* Set the SPI clock phase and polarity */
u16 cr = in_be16(xspi->regs + XSPI_CR_OFFSET)
u16 cr = xspi->read_fn(xspi->regs + XSPI_CR_OFFSET)
& ~XSPI_CR_MODE_MASK;
if (spi->mode & SPI_CPHA)
cr |= XSPI_CR_CPHA;
if (spi->mode & SPI_CPOL)
cr |= XSPI_CR_CPOL;
out_be16(xspi->regs + XSPI_CR_OFFSET, cr);
xspi->write_fn(cr, xspi->regs + XSPI_CR_OFFSET);
/* We do not check spi->max_speed_hz here as the SPI clock
* frequency is not software programmable (the IP block design
@ -132,25 +182,27 @@ static void xilinx_spi_chipselect(struct spi_device *spi, int is_on)
*/
/* Activate the chip select */
out_be32(xspi->regs + XSPI_SSR_OFFSET,
~(0x0001 << spi->chip_select));
xspi->write_fn(~(0x0001 << spi->chip_select),
xspi->regs + XSPI_SSR_OFFSET);
}
}
/* spi_bitbang requires custom setup_transfer() to be defined if there is a
* custom txrx_bufs(). We have nothing to setup here as the SPI IP block
* supports just 8 bits per word, and SPI clock can't be changed in software.
* Check for 8 bits per word. Chip select delay calculations could be
* supports 8 or 16 bits per word which cannot be changed in software.
* SPI clock can't be changed in software either.
* Check for correct bits per word. Chip select delay calculations could be
* added here as soon as bitbang_work() can be made aware of the delay value.
*/
static int xilinx_spi_setup_transfer(struct spi_device *spi,
struct spi_transfer *t)
{
struct xilinx_spi *xspi = spi_master_get_devdata(spi->master);
u8 bits_per_word;
bits_per_word = (t && t->bits_per_word)
? t->bits_per_word : spi->bits_per_word;
if (bits_per_word != 8) {
if (bits_per_word != xspi->bits_per_word) {
dev_err(&spi->dev, "%s, unsupported bits_per_word=%d\n",
__func__, bits_per_word);
return -EINVAL;
@ -161,17 +213,16 @@ static int xilinx_spi_setup_transfer(struct spi_device *spi,
static int xilinx_spi_setup(struct spi_device *spi)
{
struct spi_bitbang *bitbang;
struct xilinx_spi *xspi;
int retval;
xspi = spi_master_get_devdata(spi->master);
bitbang = &xspi->bitbang;
retval = xilinx_spi_setup_transfer(spi, NULL);
if (retval < 0)
return retval;
/* always return 0, we can not check the number of bits.
* There are cases when SPI setup is called before any driver is
* there, in that case the SPI core defaults to 8 bits, which we
* do not support in some cases. But if we return an error, the
* SPI device would not be registered and no driver can get hold of it
* When the driver is there, it will call SPI setup again with the
* correct number of bits per transfer.
* If a driver setups with the wrong bit number, it will fail when
* it tries to do a transfer
*/
return 0;
}
@ -180,15 +231,14 @@ static void xilinx_spi_fill_tx_fifo(struct xilinx_spi *xspi)
u8 sr;
/* Fill the Tx FIFO with as many bytes as possible */
sr = in_8(xspi->regs + XSPI_SR_OFFSET);
sr = xspi->read_fn(xspi->regs + XSPI_SR_OFFSET);
while ((sr & XSPI_SR_TX_FULL_MASK) == 0 && xspi->remaining_bytes > 0) {
if (xspi->tx_ptr) {
out_8(xspi->regs + XSPI_TXD_OFFSET, *xspi->tx_ptr++);
} else {
out_8(xspi->regs + XSPI_TXD_OFFSET, 0);
}
xspi->remaining_bytes--;
sr = in_8(xspi->regs + XSPI_SR_OFFSET);
if (xspi->tx_ptr)
xspi->tx_fn(xspi);
else
xspi->write_fn(0, xspi->regs + XSPI_TXD_OFFSET);
xspi->remaining_bytes -= xspi->bits_per_word / 8;
sr = xspi->read_fn(xspi->regs + XSPI_SR_OFFSET);
}
}
@ -210,18 +260,19 @@ static int xilinx_spi_txrx_bufs(struct spi_device *spi, struct spi_transfer *t)
/* Enable the transmit empty interrupt, which we use to determine
* progress on the transmission.
*/
ipif_ier = in_be32(xspi->regs + XIPIF_V123B_IIER_OFFSET);
out_be32(xspi->regs + XIPIF_V123B_IIER_OFFSET,
ipif_ier | XSPI_INTR_TX_EMPTY);
ipif_ier = xspi->read_fn(xspi->regs + XIPIF_V123B_IIER_OFFSET);
xspi->write_fn(ipif_ier | XSPI_INTR_TX_EMPTY,
xspi->regs + XIPIF_V123B_IIER_OFFSET);
/* Start the transfer by not inhibiting the transmitter any longer */
cr = in_be16(xspi->regs + XSPI_CR_OFFSET) & ~XSPI_CR_TRANS_INHIBIT;
out_be16(xspi->regs + XSPI_CR_OFFSET, cr);
cr = xspi->read_fn(xspi->regs + XSPI_CR_OFFSET) &
~XSPI_CR_TRANS_INHIBIT;
xspi->write_fn(cr, xspi->regs + XSPI_CR_OFFSET);
wait_for_completion(&xspi->done);
/* Disable the transmit empty interrupt */
out_be32(xspi->regs + XIPIF_V123B_IIER_OFFSET, ipif_ier);
xspi->write_fn(ipif_ier, xspi->regs + XIPIF_V123B_IIER_OFFSET);
return t->len - xspi->remaining_bytes;
}
@ -238,8 +289,8 @@ static irqreturn_t xilinx_spi_irq(int irq, void *dev_id)
u32 ipif_isr;
/* Get the IPIF interrupts, and clear them immediately */
ipif_isr = in_be32(xspi->regs + XIPIF_V123B_IISR_OFFSET);
out_be32(xspi->regs + XIPIF_V123B_IISR_OFFSET, ipif_isr);
ipif_isr = xspi->read_fn(xspi->regs + XIPIF_V123B_IISR_OFFSET);
xspi->write_fn(ipif_isr, xspi->regs + XIPIF_V123B_IISR_OFFSET);
if (ipif_isr & XSPI_INTR_TX_EMPTY) { /* Transmission completed */
u16 cr;
@ -250,20 +301,15 @@ static irqreturn_t xilinx_spi_irq(int irq, void *dev_id)
* transmitter while the Isr refills the transmit register/FIFO,
* or make sure it is stopped if we're done.
*/
cr = in_be16(xspi->regs + XSPI_CR_OFFSET);
out_be16(xspi->regs + XSPI_CR_OFFSET,
cr | XSPI_CR_TRANS_INHIBIT);
cr = xspi->read_fn(xspi->regs + XSPI_CR_OFFSET);
xspi->write_fn(cr | XSPI_CR_TRANS_INHIBIT,
xspi->regs + XSPI_CR_OFFSET);
/* Read out all the data from the Rx FIFO */
sr = in_8(xspi->regs + XSPI_SR_OFFSET);
sr = xspi->read_fn(xspi->regs + XSPI_SR_OFFSET);
while ((sr & XSPI_SR_RX_EMPTY_MASK) == 0) {
u8 data;
data = in_8(xspi->regs + XSPI_RXD_OFFSET);
if (xspi->rx_ptr) {
*xspi->rx_ptr++ = data;
}
sr = in_8(xspi->regs + XSPI_SR_OFFSET);
xspi->rx_fn(xspi);
sr = xspi->read_fn(xspi->regs + XSPI_SR_OFFSET);
}
/* See if there is more data to send */
@ -272,7 +318,7 @@ static irqreturn_t xilinx_spi_irq(int irq, void *dev_id)
/* Start the transfer by not inhibiting the
* transmitter any longer
*/
out_be16(xspi->regs + XSPI_CR_OFFSET, cr);
xspi->write_fn(cr, xspi->regs + XSPI_CR_OFFSET);
} else {
/* No more data to send.
* Indicate the transfer is completed.
@ -284,40 +330,22 @@ static irqreturn_t xilinx_spi_irq(int irq, void *dev_id)
return IRQ_HANDLED;
}
static int __init xilinx_spi_of_probe(struct of_device *ofdev,
const struct of_device_id *match)
struct spi_master *xilinx_spi_init(struct device *dev, struct resource *mem,
u32 irq, s16 bus_num)
{
struct spi_master *master;
struct xilinx_spi *xspi;
struct resource r_irq_struct;
struct resource r_mem_struct;
struct xspi_platform_data *pdata = dev->platform_data;
int ret;
struct resource *r_irq = &r_irq_struct;
struct resource *r_mem = &r_mem_struct;
int rc = 0;
const u32 *prop;
int len;
/* Get resources(memory, IRQ) associated with the device */
master = spi_alloc_master(&ofdev->dev, sizeof(struct xilinx_spi));
if (master == NULL) {
return -ENOMEM;
if (!pdata) {
dev_err(dev, "No platform data attached\n");
return NULL;
}
dev_set_drvdata(&ofdev->dev, master);
rc = of_address_to_resource(ofdev->node, 0, r_mem);
if (rc) {
dev_warn(&ofdev->dev, "invalid address\n");
goto put_master;
}
rc = of_irq_to_resource(ofdev->node, 0, r_irq);
if (rc == NO_IRQ) {
dev_warn(&ofdev->dev, "no IRQ found\n");
goto put_master;
}
master = spi_alloc_master(dev, sizeof(struct xilinx_spi));
if (!master)
return NULL;
/* the spi->mode bits understood by this driver: */
master->mode_bits = SPI_CPOL | SPI_CPHA;
@ -330,128 +358,87 @@ static int __init xilinx_spi_of_probe(struct of_device *ofdev,
xspi->bitbang.master->setup = xilinx_spi_setup;
init_completion(&xspi->done);
xspi->irq = r_irq->start;
if (!request_mem_region(r_mem->start,
r_mem->end - r_mem->start + 1, XILINX_SPI_NAME)) {
rc = -ENXIO;
dev_warn(&ofdev->dev, "memory request failure\n");
if (!request_mem_region(mem->start, resource_size(mem),
XILINX_SPI_NAME))
goto put_master;
}
xspi->regs = ioremap(r_mem->start, r_mem->end - r_mem->start + 1);
xspi->regs = ioremap(mem->start, resource_size(mem));
if (xspi->regs == NULL) {
rc = -ENOMEM;
dev_warn(&ofdev->dev, "ioremap failure\n");
goto release_mem;
dev_warn(dev, "ioremap failure\n");
goto map_failed;
}
xspi->irq = r_irq->start;
/* dynamic bus assignment */
master->bus_num = -1;
master->bus_num = bus_num;
master->num_chipselect = pdata->num_chipselect;
/* number of slave select bits is required */
prop = of_get_property(ofdev->node, "xlnx,num-ss-bits", &len);
if (!prop || len < sizeof(*prop)) {
dev_warn(&ofdev->dev, "no 'xlnx,num-ss-bits' property\n");
xspi->mem = *mem;
xspi->irq = irq;
if (pdata->little_endian) {
xspi->read_fn = ioread32;
xspi->write_fn = iowrite32;
} else {
xspi->read_fn = ioread32be;
xspi->write_fn = iowrite32be;
}
xspi->bits_per_word = pdata->bits_per_word;
if (xspi->bits_per_word == 8) {
xspi->tx_fn = xspi_tx8;
xspi->rx_fn = xspi_rx8;
} else if (xspi->bits_per_word == 16) {
xspi->tx_fn = xspi_tx16;
xspi->rx_fn = xspi_rx16;
} else if (xspi->bits_per_word == 32) {
xspi->tx_fn = xspi_tx32;
xspi->rx_fn = xspi_rx32;
} else
goto unmap_io;
}
master->num_chipselect = *prop;
/* SPI controller initializations */
xspi_init_hw(xspi->regs);
xspi_init_hw(xspi);
/* Register for SPI Interrupt */
rc = request_irq(xspi->irq, xilinx_spi_irq, 0, XILINX_SPI_NAME, xspi);
if (rc != 0) {
dev_warn(&ofdev->dev, "irq request failure: %d\n", xspi->irq);
ret = request_irq(xspi->irq, xilinx_spi_irq, 0, XILINX_SPI_NAME, xspi);
if (ret)
goto unmap_io;
}
rc = spi_bitbang_start(&xspi->bitbang);
if (rc != 0) {
dev_err(&ofdev->dev, "spi_bitbang_start FAILED\n");
ret = spi_bitbang_start(&xspi->bitbang);
if (ret) {
dev_err(dev, "spi_bitbang_start FAILED\n");
goto free_irq;
}
dev_info(&ofdev->dev, "at 0x%08X mapped to 0x%08X, irq=%d\n",
(unsigned int)r_mem->start, (u32)xspi->regs, xspi->irq);
/* Add any subnodes on the SPI bus */
of_register_spi_devices(master, ofdev->node);
return rc;
dev_info(dev, "at 0x%08llX mapped to 0x%p, irq=%d\n",
(unsigned long long)mem->start, xspi->regs, xspi->irq);
return master;
free_irq:
free_irq(xspi->irq, xspi);
unmap_io:
iounmap(xspi->regs);
release_mem:
release_mem_region(r_mem->start, resource_size(r_mem));
map_failed:
release_mem_region(mem->start, resource_size(mem));
put_master:
spi_master_put(master);
return rc;
return NULL;
}
EXPORT_SYMBOL(xilinx_spi_init);
static int __devexit xilinx_spi_remove(struct of_device *ofdev)
void xilinx_spi_deinit(struct spi_master *master)
{
struct xilinx_spi *xspi;
struct spi_master *master;
struct resource r_mem;
master = platform_get_drvdata(ofdev);
xspi = spi_master_get_devdata(master);
spi_bitbang_stop(&xspi->bitbang);
free_irq(xspi->irq, xspi);
iounmap(xspi->regs);
if (!of_address_to_resource(ofdev->node, 0, &r_mem))
release_mem_region(r_mem.start, resource_size(&r_mem));
dev_set_drvdata(&ofdev->dev, 0);
release_mem_region(xspi->mem.start, resource_size(&xspi->mem));
spi_master_put(xspi->bitbang.master);
return 0;
}
EXPORT_SYMBOL(xilinx_spi_deinit);
/* work with hotplug and coldplug */
MODULE_ALIAS("platform:" XILINX_SPI_NAME);
static int __exit xilinx_spi_of_remove(struct of_device *op)
{
return xilinx_spi_remove(op);
}
static struct of_device_id xilinx_spi_of_match[] = {
{ .compatible = "xlnx,xps-spi-2.00.a", },
{ .compatible = "xlnx,xps-spi-2.00.b", },
{}
};
MODULE_DEVICE_TABLE(of, xilinx_spi_of_match);
static struct of_platform_driver xilinx_spi_of_driver = {
.owner = THIS_MODULE,
.name = "xilinx-xps-spi",
.match_table = xilinx_spi_of_match,
.probe = xilinx_spi_of_probe,
.remove = __exit_p(xilinx_spi_of_remove),
.driver = {
.name = "xilinx-xps-spi",
.owner = THIS_MODULE,
},
};
static int __init xilinx_spi_init(void)
{
return of_register_platform_driver(&xilinx_spi_of_driver);
}
module_init(xilinx_spi_init);
static void __exit xilinx_spi_exit(void)
{
of_unregister_platform_driver(&xilinx_spi_of_driver);
}
module_exit(xilinx_spi_exit);
MODULE_AUTHOR("MontaVista Software, Inc. <source@mvista.com>");
MODULE_DESCRIPTION("Xilinx SPI driver");
MODULE_LICENSE("GPL");

32
drivers/spi/xilinx_spi.h Normal file
View File

@ -0,0 +1,32 @@
/*
* Xilinx SPI device driver API and platform data header file
*
* Copyright (c) 2009 Intel 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.
*
* 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 _XILINX_SPI_H_
#define _XILINX_SPI_H_
#include <linux/spi/spi.h>
#include <linux/spi/spi_bitbang.h>
#define XILINX_SPI_NAME "xilinx_spi"
struct spi_master *xilinx_spi_init(struct device *dev, struct resource *mem,
u32 irq, s16 bus_num);
void xilinx_spi_deinit(struct spi_master *master);
#endif

134
drivers/spi/xilinx_spi_of.c Normal file
View File

@ -0,0 +1,134 @@
/*
* Xilinx SPI OF device driver
*
* Copyright (c) 2009 Intel 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.
*
* 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.
*/
/* Supports:
* Xilinx SPI devices as OF devices
*
* Inspired by xilinx_spi.c, 2002-2007 (c) MontaVista Software, Inc.
*/
#include <linux/module.h>
#include <linux/init.h>
#include <linux/interrupt.h>
#include <linux/io.h>
#include <linux/of_platform.h>
#include <linux/of_device.h>
#include <linux/of_spi.h>
#include <linux/spi/xilinx_spi.h>
#include "xilinx_spi.h"
static int __devinit xilinx_spi_of_probe(struct of_device *ofdev,
const struct of_device_id *match)
{
struct spi_master *master;
struct xspi_platform_data *pdata;
struct resource r_mem;
struct resource r_irq;
int rc = 0;
const u32 *prop;
int len;
rc = of_address_to_resource(ofdev->node, 0, &r_mem);
if (rc) {
dev_warn(&ofdev->dev, "invalid address\n");
return rc;
}
rc = of_irq_to_resource(ofdev->node, 0, &r_irq);
if (rc == NO_IRQ) {
dev_warn(&ofdev->dev, "no IRQ found\n");
return -ENODEV;
}
ofdev->dev.platform_data =
kzalloc(sizeof(struct xspi_platform_data), GFP_KERNEL);
pdata = ofdev->dev.platform_data;
if (!pdata)
return -ENOMEM;
/* number of slave select bits is required */
prop = of_get_property(ofdev->node, "xlnx,num-ss-bits", &len);
if (!prop || len < sizeof(*prop)) {
dev_warn(&ofdev->dev, "no 'xlnx,num-ss-bits' property\n");
return -EINVAL;
}
pdata->num_chipselect = *prop;
pdata->bits_per_word = 8;
master = xilinx_spi_init(&ofdev->dev, &r_mem, r_irq.start, -1);
if (!master)
return -ENODEV;
dev_set_drvdata(&ofdev->dev, master);
/* Add any subnodes on the SPI bus */
of_register_spi_devices(master, ofdev->node);
return 0;
}
static int __devexit xilinx_spi_remove(struct of_device *ofdev)
{
xilinx_spi_deinit(dev_get_drvdata(&ofdev->dev));
dev_set_drvdata(&ofdev->dev, 0);
kfree(ofdev->dev.platform_data);
ofdev->dev.platform_data = NULL;
return 0;
}
static int __exit xilinx_spi_of_remove(struct of_device *op)
{
return xilinx_spi_remove(op);
}
static struct of_device_id xilinx_spi_of_match[] = {
{ .compatible = "xlnx,xps-spi-2.00.a", },
{ .compatible = "xlnx,xps-spi-2.00.b", },
{}
};
MODULE_DEVICE_TABLE(of, xilinx_spi_of_match);
static struct of_platform_driver xilinx_spi_of_driver = {
.match_table = xilinx_spi_of_match,
.probe = xilinx_spi_of_probe,
.remove = __exit_p(xilinx_spi_of_remove),
.driver = {
.name = "xilinx-xps-spi",
.owner = THIS_MODULE,
},
};
static int __init xilinx_spi_of_init(void)
{
return of_register_platform_driver(&xilinx_spi_of_driver);
}
module_init(xilinx_spi_of_init);
static void __exit xilinx_spi_of_exit(void)
{
of_unregister_platform_driver(&xilinx_spi_of_driver);
}
module_exit(xilinx_spi_of_exit);
MODULE_AUTHOR("Mocean Laboratories <info@mocean-labs.com>");
MODULE_DESCRIPTION("Xilinx SPI platform driver");
MODULE_LICENSE("GPL v2");

View File

@ -0,0 +1,102 @@
/*
* Support for Xilinx SPI platform devices
* Copyright (c) 2009 Intel 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.
*
* 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.
*/
/* Supports:
* Xilinx SPI devices as platform devices
*
* Inspired by xilinx_spi.c, 2002-2007 (c) MontaVista Software, Inc.
*/
#include <linux/module.h>
#include <linux/init.h>
#include <linux/interrupt.h>
#include <linux/io.h>
#include <linux/platform_device.h>
#include <linux/spi/spi.h>
#include <linux/spi/spi_bitbang.h>
#include <linux/spi/xilinx_spi.h>
#include "xilinx_spi.h"
static int __devinit xilinx_spi_probe(struct platform_device *dev)
{
struct xspi_platform_data *pdata;
struct resource *r;
int irq;
struct spi_master *master;
u8 i;
pdata = dev->dev.platform_data;
if (!pdata)
return -ENODEV;
r = platform_get_resource(dev, IORESOURCE_MEM, 0);
if (!r)
return -ENODEV;
irq = platform_get_irq(dev, 0);
if (irq < 0)
return -ENXIO;
master = xilinx_spi_init(&dev->dev, r, irq, dev->id);
if (!master)
return -ENODEV;
for (i = 0; i < pdata->num_devices; i++)
spi_new_device(master, pdata->devices + i);
platform_set_drvdata(dev, master);
return 0;
}
static int __devexit xilinx_spi_remove(struct platform_device *dev)
{
xilinx_spi_deinit(platform_get_drvdata(dev));
platform_set_drvdata(dev, 0);
return 0;
}
/* work with hotplug and coldplug */
MODULE_ALIAS("platform:" XILINX_SPI_NAME);
static struct platform_driver xilinx_spi_driver = {
.probe = xilinx_spi_probe,
.remove = __devexit_p(xilinx_spi_remove),
.driver = {
.name = XILINX_SPI_NAME,
.owner = THIS_MODULE,
},
};
static int __init xilinx_spi_pltfm_init(void)
{
return platform_driver_register(&xilinx_spi_driver);
}
module_init(xilinx_spi_pltfm_init);
static void __exit xilinx_spi_pltfm_exit(void)
{
platform_driver_unregister(&xilinx_spi_driver);
}
module_exit(xilinx_spi_pltfm_exit);
MODULE_AUTHOR("Mocean Laboratories <info@mocean-labs.com>");
MODULE_DESCRIPTION("Xilinx SPI platform driver");
MODULE_LICENSE("GPL v2");

View File

@ -1,10 +0,0 @@
#ifndef INCLUDE_MPC5200_SPI_H
#define INCLUDE_MPC5200_SPI_H
extern void mpc52xx_spi_set_premessage_hook(struct spi_master *master,
void (*hook)(struct spi_message *m,
void *context),
void *hook_context);
#endif

View File

@ -0,0 +1,10 @@
#ifndef __SPI_SH_MSIOF_H__
#define __SPI_SH_MSIOF_H__
struct sh_msiof_spi_info {
int tx_fifo_override;
int rx_fifo_override;
u16 num_chipselect;
};
#endif /* __SPI_SH_MSIOF_H__ */

View File

@ -0,0 +1,20 @@
#ifndef __LINUX_SPI_XILINX_SPI_H
#define __LINUX_SPI_XILINX_SPI_H
/**
* struct xspi_platform_data - Platform data of the Xilinx SPI driver
* @num_chipselect: Number of chip select by the IP.
* @little_endian: If registers should be accessed little endian or not.
* @bits_per_word: Number of bits per word.
* @devices: Devices to add when the driver is probed.
* @num_devices: Number of devices in the devices array.
*/
struct xspi_platform_data {
u16 num_chipselect;
bool little_endian;
u8 bits_per_word;
struct spi_board_info *devices;
u8 num_devices;
};
#endif /* __LINUX_SPI_XILINX_SPI_H */