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:
Коммит
478e4e9d7a
|
@ -5080,6 +5080,7 @@ F: drivers/char/specialix*
|
||||||
|
|
||||||
SPI SUBSYSTEM
|
SPI SUBSYSTEM
|
||||||
M: David Brownell <dbrownell@users.sourceforge.net>
|
M: David Brownell <dbrownell@users.sourceforge.net>
|
||||||
|
M: Grant Likely <grant.likely@secretlab.ca>
|
||||||
L: spi-devel-general@lists.sourceforge.net
|
L: spi-devel-general@lists.sourceforge.net
|
||||||
S: Maintained
|
S: Maintained
|
||||||
F: Documentation/spi/
|
F: Documentation/spi/
|
||||||
|
|
|
@ -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 */
|
|
@ -169,6 +169,12 @@ config SPI_OMAP24XX
|
||||||
SPI master controller for OMAP24xx/OMAP34xx Multichannel SPI
|
SPI master controller for OMAP24xx/OMAP34xx Multichannel SPI
|
||||||
(McSPI) modules.
|
(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
|
config SPI_ORION
|
||||||
tristate "Orion SPI master (EXPERIMENTAL)"
|
tristate "Orion SPI master (EXPERIMENTAL)"
|
||||||
depends on PLAT_ORION && EXPERIMENTAL
|
depends on PLAT_ORION && EXPERIMENTAL
|
||||||
|
@ -220,6 +226,13 @@ config SPI_S3C24XX_GPIO
|
||||||
the inbuilt hardware cannot provide the transfer mode, or
|
the inbuilt hardware cannot provide the transfer mode, or
|
||||||
where the board is using non hardware connected pins.
|
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
|
config SPI_SH_SCI
|
||||||
tristate "SuperH SCI SPI controller"
|
tristate "SuperH SCI SPI controller"
|
||||||
depends on SUPERH
|
depends on SUPERH
|
||||||
|
@ -240,15 +253,38 @@ config SPI_TXX9
|
||||||
SPI driver for Toshiba TXx9 MIPS SoCs
|
SPI driver for Toshiba TXx9 MIPS SoCs
|
||||||
|
|
||||||
config SPI_XILINX
|
config SPI_XILINX
|
||||||
tristate "Xilinx SPI controller"
|
tristate "Xilinx SPI controller common module"
|
||||||
depends on (XILINX_VIRTEX || MICROBLAZE) && EXPERIMENTAL
|
depends on HAS_IOMEM && EXPERIMENTAL
|
||||||
select SPI_BITBANG
|
select SPI_BITBANG
|
||||||
|
select SPI_XILINX_OF if (XILINX_VIRTEX || MICROBLAZE)
|
||||||
help
|
help
|
||||||
This exposes the SPI controller IP from the Xilinx EDK.
|
This exposes the SPI controller IP from the Xilinx EDK.
|
||||||
|
|
||||||
See the "OPB Serial Peripheral Interface (SPI) (v1.00e)"
|
See the "OPB Serial Peripheral Interface (SPI) (v1.00e)"
|
||||||
Product Specification document (DS464) for hardware details.
|
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
|
# Add new SPI master controllers in alphabetical order above this line
|
||||||
#
|
#
|
||||||
|
|
|
@ -22,6 +22,7 @@ obj-$(CONFIG_SPI_LM70_LLP) += spi_lm70llp.o
|
||||||
obj-$(CONFIG_SPI_PXA2XX) += pxa2xx_spi.o
|
obj-$(CONFIG_SPI_PXA2XX) += pxa2xx_spi.o
|
||||||
obj-$(CONFIG_SPI_OMAP_UWIRE) += omap_uwire.o
|
obj-$(CONFIG_SPI_OMAP_UWIRE) += omap_uwire.o
|
||||||
obj-$(CONFIG_SPI_OMAP24XX) += omap2_mcspi.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_ORION) += orion_spi.o
|
||||||
obj-$(CONFIG_SPI_PL022) += amba-pl022.o
|
obj-$(CONFIG_SPI_PL022) += amba-pl022.o
|
||||||
obj-$(CONFIG_SPI_MPC52xx_PSC) += mpc52xx_psc_spi.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_S3C24XX) += spi_s3c24xx.o
|
||||||
obj-$(CONFIG_SPI_TXX9) += spi_txx9.o
|
obj-$(CONFIG_SPI_TXX9) += spi_txx9.o
|
||||||
obj-$(CONFIG_SPI_XILINX) += xilinx_spi.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_SCI) += spi_sh_sci.o
|
||||||
|
obj-$(CONFIG_SPI_SH_MSIOF) += spi_sh_msiof.o
|
||||||
obj-$(CONFIG_SPI_STMP3XXX) += spi_stmp.o
|
obj-$(CONFIG_SPI_STMP3XXX) += spi_stmp.o
|
||||||
|
obj-$(CONFIG_SPI_NUC900) += spi_nuc900.o
|
||||||
# ... add above this line ...
|
# ... add above this line ...
|
||||||
|
|
||||||
# SPI protocol drivers (device/link on bus)
|
# SPI protocol drivers (device/link on bus)
|
||||||
|
|
|
@ -237,8 +237,14 @@ static int au1550_spi_setupxfer(struct spi_device *spi, struct spi_transfer *t)
|
||||||
unsigned bpw, hz;
|
unsigned bpw, hz;
|
||||||
u32 cfg, stat;
|
u32 cfg, stat;
|
||||||
|
|
||||||
bpw = t ? t->bits_per_word : spi->bits_per_word;
|
bpw = spi->bits_per_word;
|
||||||
hz = t ? t->speed_hz : spi->max_speed_hz;
|
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) {
|
if (bpw < 4 || bpw > 24) {
|
||||||
dev_err(&spi->dev, "setupxfer: invalid bits_per_word=%d\n",
|
dev_err(&spi->dev, "setupxfer: invalid bits_per_word=%d\n",
|
||||||
|
|
|
@ -18,9 +18,9 @@
|
||||||
#include <linux/interrupt.h>
|
#include <linux/interrupt.h>
|
||||||
#include <linux/delay.h>
|
#include <linux/delay.h>
|
||||||
#include <linux/spi/spi.h>
|
#include <linux/spi/spi.h>
|
||||||
#include <linux/spi/mpc52xx_spi.h>
|
|
||||||
#include <linux/of_spi.h>
|
#include <linux/of_spi.h>
|
||||||
#include <linux/io.h>
|
#include <linux/io.h>
|
||||||
|
#include <linux/of_gpio.h>
|
||||||
#include <asm/time.h>
|
#include <asm/time.h>
|
||||||
#include <asm/mpc52xx.h>
|
#include <asm/mpc52xx.h>
|
||||||
|
|
||||||
|
@ -53,7 +53,7 @@ MODULE_LICENSE("GPL");
|
||||||
/* FSM state return values */
|
/* FSM state return values */
|
||||||
#define FSM_STOP 0 /* Nothing more for the state machine to */
|
#define FSM_STOP 0 /* Nothing more for the state machine to */
|
||||||
/* do. If something interesting happens */
|
/* 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 */
|
#define FSM_POLL 1 /* need to poll for completion, an IRQ is */
|
||||||
/* not expected */
|
/* not expected */
|
||||||
#define FSM_CONTINUE 2 /* Keep iterating the state machine */
|
#define FSM_CONTINUE 2 /* Keep iterating the state machine */
|
||||||
|
@ -61,13 +61,12 @@ MODULE_LICENSE("GPL");
|
||||||
/* Driver internal data */
|
/* Driver internal data */
|
||||||
struct mpc52xx_spi {
|
struct mpc52xx_spi {
|
||||||
struct spi_master *master;
|
struct spi_master *master;
|
||||||
u32 sysclk;
|
|
||||||
void __iomem *regs;
|
void __iomem *regs;
|
||||||
int irq0; /* MODF irq */
|
int irq0; /* MODF irq */
|
||||||
int irq1; /* SPIF 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 msg_count;
|
||||||
int wcol_count;
|
int wcol_count;
|
||||||
int wcol_ticks;
|
int wcol_ticks;
|
||||||
|
@ -79,7 +78,6 @@ struct mpc52xx_spi {
|
||||||
spinlock_t lock;
|
spinlock_t lock;
|
||||||
struct work_struct work;
|
struct work_struct work;
|
||||||
|
|
||||||
|
|
||||||
/* Details of current transfer (length, and buffer pointers) */
|
/* Details of current transfer (length, and buffer pointers) */
|
||||||
struct spi_message *message; /* current message */
|
struct spi_message *message; /* current message */
|
||||||
struct spi_transfer *transfer; /* current transfer */
|
struct spi_transfer *transfer; /* current transfer */
|
||||||
|
@ -89,6 +87,8 @@ struct mpc52xx_spi {
|
||||||
u8 *rx_buf;
|
u8 *rx_buf;
|
||||||
const u8 *tx_buf;
|
const u8 *tx_buf;
|
||||||
int cs_change;
|
int cs_change;
|
||||||
|
int gpio_cs_count;
|
||||||
|
unsigned int *gpio_cs;
|
||||||
};
|
};
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@ -96,6 +96,12 @@ struct mpc52xx_spi {
|
||||||
*/
|
*/
|
||||||
static void mpc52xx_spi_chipsel(struct mpc52xx_spi *ms, int value)
|
static void mpc52xx_spi_chipsel(struct mpc52xx_spi *ms, int value)
|
||||||
{
|
{
|
||||||
|
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);
|
out_8(ms->regs + SPI_PORTDATA, value ? 0 : 0x08);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -390,7 +396,9 @@ static int __devinit mpc52xx_spi_probe(struct of_device *op,
|
||||||
struct spi_master *master;
|
struct spi_master *master;
|
||||||
struct mpc52xx_spi *ms;
|
struct mpc52xx_spi *ms;
|
||||||
void __iomem *regs;
|
void __iomem *regs;
|
||||||
int rc;
|
u8 ctrl1;
|
||||||
|
int rc, i = 0;
|
||||||
|
int gpio_cs;
|
||||||
|
|
||||||
/* MMIO registers */
|
/* MMIO registers */
|
||||||
dev_dbg(&op->dev, "probing mpc5200 SPI device\n");
|
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;
|
return -ENODEV;
|
||||||
|
|
||||||
/* initialize the device */
|
/* 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_CTRL2, 0x0);
|
||||||
out_8(regs + SPI_DATADIR, 0xe); /* Set output pins */
|
out_8(regs + SPI_DATADIR, 0xe); /* Set output pins */
|
||||||
out_8(regs + SPI_PORTDATA, 0x8); /* Deassert /SS signal */
|
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
|
* on the SPI bus. This fault will also occur if the SPI signals
|
||||||
* are not connected to any pins (port_config setting) */
|
* are not connected to any pins (port_config setting) */
|
||||||
in_8(regs + SPI_STATUS);
|
in_8(regs + SPI_STATUS);
|
||||||
|
out_8(regs + SPI_CTRL1, ctrl1);
|
||||||
|
|
||||||
in_8(regs + SPI_DATA);
|
in_8(regs + SPI_DATA);
|
||||||
if (in_8(regs + SPI_STATUS) & SPI_STATUS_MODF) {
|
if (in_8(regs + SPI_STATUS) & SPI_STATUS_MODF) {
|
||||||
dev_err(&op->dev, "mode fault; is port_config correct?\n");
|
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;
|
rc = -ENOMEM;
|
||||||
goto err_alloc;
|
goto err_alloc;
|
||||||
}
|
}
|
||||||
|
|
||||||
master->bus_num = -1;
|
master->bus_num = -1;
|
||||||
master->num_chipselect = 1;
|
|
||||||
master->setup = mpc52xx_spi_setup;
|
master->setup = mpc52xx_spi_setup;
|
||||||
master->transfer = mpc52xx_spi_transfer;
|
master->transfer = mpc52xx_spi_transfer;
|
||||||
|
master->mode_bits = SPI_CPOL | SPI_CPHA | SPI_LSB_FIRST;
|
||||||
|
|
||||||
dev_set_drvdata(&op->dev, master);
|
dev_set_drvdata(&op->dev, master);
|
||||||
|
|
||||||
ms = spi_master_get_devdata(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->irq1 = irq_of_parse_and_map(op->node, 1);
|
||||||
ms->state = mpc52xx_spi_fsmstate_idle;
|
ms->state = mpc52xx_spi_fsmstate_idle;
|
||||||
ms->ipb_freq = mpc5xxx_get_bus_frequency(op->node);
|
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);
|
spin_lock_init(&ms->lock);
|
||||||
INIT_LIST_HEAD(&ms->queue);
|
INIT_LIST_HEAD(&ms->queue);
|
||||||
INIT_WORK(&ms->work, mpc52xx_spi_wq);
|
INIT_WORK(&ms->work, mpc52xx_spi_wq);
|
||||||
|
|
||||||
/* Decide if interrupts can be used */
|
/* Decide if interrupts can be used */
|
||||||
if (ms->irq0 && ms->irq1) {
|
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);
|
"mpc5200-spi-modf", ms);
|
||||||
rc |= request_irq(ms->irq1, mpc52xx_spi_irq, IRQF_SAMPLE_RANDOM,
|
rc |= request_irq(ms->irq1, mpc52xx_spi_irq, 0,
|
||||||
"mpc5200-spi-spiF", ms);
|
"mpc5200-spi-spif", ms);
|
||||||
if (rc) {
|
if (rc) {
|
||||||
free_irq(ms->irq0, ms);
|
free_irq(ms->irq0, ms);
|
||||||
free_irq(ms->irq1, ms);
|
free_irq(ms->irq1, ms);
|
||||||
|
@ -471,6 +519,11 @@ static int __devinit mpc52xx_spi_probe(struct of_device *op,
|
||||||
err_register:
|
err_register:
|
||||||
dev_err(&ms->master->dev, "initialization failed\n");
|
dev_err(&ms->master->dev, "initialization failed\n");
|
||||||
spi_master_put(master);
|
spi_master_put(master);
|
||||||
|
err_gpio:
|
||||||
|
while (i-- > 0)
|
||||||
|
gpio_free(ms->gpio_cs[i]);
|
||||||
|
|
||||||
|
kfree(ms->gpio_cs);
|
||||||
err_alloc:
|
err_alloc:
|
||||||
err_init:
|
err_init:
|
||||||
iounmap(regs);
|
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 spi_master *master = dev_get_drvdata(&op->dev);
|
||||||
struct mpc52xx_spi *ms = spi_master_get_devdata(master);
|
struct mpc52xx_spi *ms = spi_master_get_devdata(master);
|
||||||
|
int i;
|
||||||
|
|
||||||
free_irq(ms->irq0, ms);
|
free_irq(ms->irq0, ms);
|
||||||
free_irq(ms->irq1, 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_unregister_master(master);
|
||||||
spi_master_put(master);
|
spi_master_put(master);
|
||||||
iounmap(ms->regs);
|
iounmap(ms->regs);
|
||||||
|
|
|
@ -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");
|
||||||
|
|
|
@ -44,6 +44,9 @@
|
||||||
#define MXC_CSPIINT 0x0c
|
#define MXC_CSPIINT 0x0c
|
||||||
#define MXC_RESET 0x1c
|
#define MXC_RESET 0x1c
|
||||||
|
|
||||||
|
#define MX3_CSPISTAT 0x14
|
||||||
|
#define MX3_CSPISTAT_RR (1 << 3)
|
||||||
|
|
||||||
/* generic defines to abstract from the different register layouts */
|
/* generic defines to abstract from the different register layouts */
|
||||||
#define MXC_INT_RR (1 << 0) /* Receive data ready interrupt */
|
#define MXC_INT_RR (1 << 0) /* Receive data ready interrupt */
|
||||||
#define MXC_INT_TE (1 << 1) /* Transmit FIFO empty 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())
|
if (cpu_is_mx31())
|
||||||
reg |= (config->bpw - 1) << MX31_CSPICTRL_BC_SHIFT;
|
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 |= (config->bpw - 1) << MX35_CSPICTRL_BL_SHIFT;
|
||||||
reg |= MX31_CSPICTRL_SSCTL;
|
reg |= MX31_CSPICTRL_SSCTL;
|
||||||
}
|
}
|
||||||
|
@ -219,7 +222,7 @@ static int mx31_config(struct spi_imx_data *spi_imx,
|
||||||
if (config->cs < 0) {
|
if (config->cs < 0) {
|
||||||
if (cpu_is_mx31())
|
if (cpu_is_mx31())
|
||||||
reg |= (config->cs + 32) << MX31_CSPICTRL_CS_SHIFT;
|
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;
|
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_imx_master *mxc_platform_info;
|
||||||
struct spi_master *master;
|
struct spi_master *master;
|
||||||
|
@ -489,7 +492,7 @@ static int __init spi_imx_probe(struct platform_device *pdev)
|
||||||
struct resource *res;
|
struct resource *res;
|
||||||
int i, ret;
|
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) {
|
if (!mxc_platform_info) {
|
||||||
dev_err(&pdev->dev, "can't get the platform data\n");
|
dev_err(&pdev->dev, "can't get the platform data\n");
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
|
@ -513,11 +516,12 @@ static int __init spi_imx_probe(struct platform_device *pdev)
|
||||||
continue;
|
continue;
|
||||||
ret = gpio_request(spi_imx->chipselect[i], DRIVER_NAME);
|
ret = gpio_request(spi_imx->chipselect[i], DRIVER_NAME);
|
||||||
if (ret) {
|
if (ret) {
|
||||||
|
while (i > 0) {
|
||||||
i--;
|
i--;
|
||||||
while (i > 0)
|
|
||||||
if (spi_imx->chipselect[i] >= 0)
|
if (spi_imx->chipselect[i] >= 0)
|
||||||
gpio_free(spi_imx->chipselect[i--]);
|
gpio_free(spi_imx->chipselect[i]);
|
||||||
dev_err(&pdev->dev, "can't get cs gpios");
|
}
|
||||||
|
dev_err(&pdev->dev, "can't get cs gpios\n");
|
||||||
goto out_master_put;
|
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);
|
spi_imx->irq = platform_get_irq(pdev, 0);
|
||||||
if (!spi_imx->irq) {
|
if (spi_imx->irq <= 0) {
|
||||||
ret = -EINVAL;
|
ret = -EINVAL;
|
||||||
goto out_iounmap;
|
goto out_iounmap;
|
||||||
}
|
}
|
||||||
|
@ -562,7 +566,7 @@ static int __init spi_imx_probe(struct platform_device *pdev)
|
||||||
goto out_iounmap;
|
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->intctrl = mx31_intctrl;
|
||||||
spi_imx->config = mx31_config;
|
spi_imx->config = mx31_config;
|
||||||
spi_imx->trigger = mx31_trigger;
|
spi_imx->trigger = mx31_trigger;
|
||||||
|
@ -590,9 +594,14 @@ static int __init spi_imx_probe(struct platform_device *pdev)
|
||||||
clk_enable(spi_imx->clk);
|
clk_enable(spi_imx->clk);
|
||||||
spi_imx->spi_clk = clk_get_rate(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);
|
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);
|
spi_imx->intctrl(spi_imx, 0);
|
||||||
|
|
||||||
ret = spi_bitbang_start(&spi_imx->bitbang);
|
ret = spi_bitbang_start(&spi_imx->bitbang);
|
||||||
|
@ -625,7 +634,7 @@ out_master_put:
|
||||||
return ret;
|
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 spi_master *master = platform_get_drvdata(pdev);
|
||||||
struct resource *res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
struct resource *res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
||||||
|
@ -659,7 +668,7 @@ static struct platform_driver spi_imx_driver = {
|
||||||
.owner = THIS_MODULE,
|
.owner = THIS_MODULE,
|
||||||
},
|
},
|
||||||
.probe = spi_imx_probe,
|
.probe = spi_imx_probe,
|
||||||
.remove = __exit_p(spi_imx_remove),
|
.remove = __devexit_p(spi_imx_remove),
|
||||||
};
|
};
|
||||||
|
|
||||||
static int __init spi_imx_init(void)
|
static int __init spi_imx_init(void)
|
||||||
|
|
|
@ -1356,7 +1356,7 @@ static int __devexit plat_mpc8xxx_spi_remove(struct platform_device *pdev)
|
||||||
MODULE_ALIAS("platform:mpc8xxx_spi");
|
MODULE_ALIAS("platform:mpc8xxx_spi");
|
||||||
static struct platform_driver mpc8xxx_spi_driver = {
|
static struct platform_driver mpc8xxx_spi_driver = {
|
||||||
.probe = plat_mpc8xxx_spi_probe,
|
.probe = plat_mpc8xxx_spi_probe,
|
||||||
.remove = __exit_p(plat_mpc8xxx_spi_remove),
|
.remove = __devexit_p(plat_mpc8xxx_spi_remove),
|
||||||
.driver = {
|
.driver = {
|
||||||
.name = "mpc8xxx_spi",
|
.name = "mpc8xxx_spi",
|
||||||
.owner = THIS_MODULE,
|
.owner = THIS_MODULE,
|
||||||
|
|
|
@ -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");
|
|
@ -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");
|
|
@ -266,15 +266,15 @@ static int spidev_message(struct spidev_data *spidev,
|
||||||
k_tmp->delay_usecs = u_tmp->delay_usecs;
|
k_tmp->delay_usecs = u_tmp->delay_usecs;
|
||||||
k_tmp->speed_hz = u_tmp->speed_hz;
|
k_tmp->speed_hz = u_tmp->speed_hz;
|
||||||
#ifdef VERBOSE
|
#ifdef VERBOSE
|
||||||
dev_dbg(&spi->dev,
|
dev_dbg(&spidev->spi->dev,
|
||||||
" xfer len %zd %s%s%s%dbits %u usec %uHz\n",
|
" xfer len %zd %s%s%s%dbits %u usec %uHz\n",
|
||||||
u_tmp->len,
|
u_tmp->len,
|
||||||
u_tmp->rx_buf ? "rx " : "",
|
u_tmp->rx_buf ? "rx " : "",
|
||||||
u_tmp->tx_buf ? "tx " : "",
|
u_tmp->tx_buf ? "tx " : "",
|
||||||
u_tmp->cs_change ? "cs " : "",
|
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->delay_usecs,
|
||||||
u_tmp->speed_hz ? : spi->max_speed_hz);
|
u_tmp->speed_hz ? : spidev->spi->max_speed_hz);
|
||||||
#endif
|
#endif
|
||||||
spi_message_add_tail(k_tmp, &msg);
|
spi_message_add_tail(k_tmp, &msg);
|
||||||
}
|
}
|
||||||
|
|
|
@ -14,22 +14,20 @@
|
||||||
#include <linux/module.h>
|
#include <linux/module.h>
|
||||||
#include <linux/init.h>
|
#include <linux/init.h>
|
||||||
#include <linux/interrupt.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.h>
|
||||||
#include <linux/spi/spi_bitbang.h>
|
#include <linux/spi/spi_bitbang.h>
|
||||||
#include <linux/io.h>
|
#include <linux/io.h>
|
||||||
|
|
||||||
|
#include "xilinx_spi.h"
|
||||||
|
#include <linux/spi/xilinx_spi.h>
|
||||||
|
|
||||||
#define XILINX_SPI_NAME "xilinx_spi"
|
#define XILINX_SPI_NAME "xilinx_spi"
|
||||||
|
|
||||||
/* Register definitions as per "OPB Serial Peripheral Interface (SPI) (v1.00e)
|
/* Register definitions as per "OPB Serial Peripheral Interface (SPI) (v1.00e)
|
||||||
* Product Specification", DS464
|
* 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_ENABLE 0x02
|
||||||
#define XSPI_CR_MASTER_MODE 0x04
|
#define XSPI_CR_MASTER_MODE 0x04
|
||||||
|
@ -40,8 +38,9 @@
|
||||||
#define XSPI_CR_RXFIFO_RESET 0x40
|
#define XSPI_CR_RXFIFO_RESET 0x40
|
||||||
#define XSPI_CR_MANUAL_SSELECT 0x80
|
#define XSPI_CR_MANUAL_SSELECT 0x80
|
||||||
#define XSPI_CR_TRANS_INHIBIT 0x100
|
#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_EMPTY_MASK 0x01 /* Receive FIFO is empty */
|
||||||
#define XSPI_SR_RX_FULL_MASK 0x02 /* Receive FIFO is full */
|
#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_TX_FULL_MASK 0x08 /* Transmit FIFO is full */
|
||||||
#define XSPI_SR_MODE_FAULT_MASK 0x10 /* Mode fault error */
|
#define XSPI_SR_MODE_FAULT_MASK 0x10 /* Mode fault error */
|
||||||
|
|
||||||
#define XSPI_TXD_OFFSET 0x6b /* 8-bit Data Transmit Register */
|
#define XSPI_TXD_OFFSET 0x68 /* Data Transmit Register */
|
||||||
#define XSPI_RXD_OFFSET 0x6f /* 8-bit Data Receive Register */
|
#define XSPI_RXD_OFFSET 0x6c /* Data Receive Register */
|
||||||
|
|
||||||
#define XSPI_SSR_OFFSET 0x70 /* 32-bit Slave Select 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_TX_UNDERRUN 0x08 /* TxFIFO was underrun */
|
||||||
#define XSPI_INTR_RX_FULL 0x10 /* RxFIFO is full */
|
#define XSPI_INTR_RX_FULL 0x10 /* RxFIFO is full */
|
||||||
#define XSPI_INTR_RX_OVERRUN 0x20 /* RxFIFO was overrun */
|
#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_RESETR_OFFSET 0x40 /* IPIF reset register */
|
||||||
#define XIPIF_V123B_RESET_MASK 0x0a /* the value to write */
|
#define XIPIF_V123B_RESET_MASK 0x0a /* the value to write */
|
||||||
|
@ -78,35 +78,85 @@ struct xilinx_spi {
|
||||||
/* bitbang has to be first */
|
/* bitbang has to be first */
|
||||||
struct spi_bitbang bitbang;
|
struct spi_bitbang bitbang;
|
||||||
struct completion done;
|
struct completion done;
|
||||||
|
struct resource mem; /* phys mem */
|
||||||
void __iomem *regs; /* virt. address of the control registers */
|
void __iomem *regs; /* virt. address of the control registers */
|
||||||
|
|
||||||
u32 irq;
|
u32 irq;
|
||||||
|
|
||||||
u32 speed_hz; /* SCK has a fixed frequency of speed_hz Hz */
|
|
||||||
|
|
||||||
u8 *rx_ptr; /* pointer in the Tx buffer */
|
u8 *rx_ptr; /* pointer in the Tx buffer */
|
||||||
const u8 *tx_ptr; /* pointer in the Rx buffer */
|
const u8 *tx_ptr; /* pointer in the Rx buffer */
|
||||||
int remaining_bytes; /* the number of bytes left to transfer */
|
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 */
|
/* Reset the SPI device */
|
||||||
out_be32(regs_base + XIPIF_V123B_RESETR_OFFSET,
|
xspi->write_fn(XIPIF_V123B_RESET_MASK,
|
||||||
XIPIF_V123B_RESET_MASK);
|
regs_base + XIPIF_V123B_RESETR_OFFSET);
|
||||||
/* Disable all the interrupts just in case */
|
/* 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 */
|
/* Enable the global IPIF interrupt */
|
||||||
out_be32(regs_base + XIPIF_V123B_DGIER_OFFSET,
|
xspi->write_fn(XIPIF_V123B_GINTR_ENABLE,
|
||||||
XIPIF_V123B_GINTR_ENABLE);
|
regs_base + XIPIF_V123B_DGIER_OFFSET);
|
||||||
/* Deselect the slave on the SPI bus */
|
/* 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,
|
/* Disable the transmitter, enable Manual Slave Select Assertion,
|
||||||
* put SPI controller into master mode, and enable it */
|
* put SPI controller into master mode, and enable it */
|
||||||
out_be16(regs_base + XSPI_CR_OFFSET,
|
xspi->write_fn(XSPI_CR_TRANS_INHIBIT | XSPI_CR_MANUAL_SSELECT |
|
||||||
XSPI_CR_TRANS_INHIBIT | XSPI_CR_MANUAL_SSELECT
|
XSPI_CR_MASTER_MODE | XSPI_CR_ENABLE | XSPI_CR_TXFIFO_RESET |
|
||||||
| XSPI_CR_MASTER_MODE | XSPI_CR_ENABLE);
|
XSPI_CR_RXFIFO_RESET, regs_base + XSPI_CR_OFFSET);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void xilinx_spi_chipselect(struct spi_device *spi, int is_on)
|
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) {
|
if (is_on == BITBANG_CS_INACTIVE) {
|
||||||
/* Deselect the slave on the SPI bus */
|
/* 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) {
|
} else if (is_on == BITBANG_CS_ACTIVE) {
|
||||||
/* Set the SPI clock phase and polarity */
|
/* 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;
|
& ~XSPI_CR_MODE_MASK;
|
||||||
if (spi->mode & SPI_CPHA)
|
if (spi->mode & SPI_CPHA)
|
||||||
cr |= XSPI_CR_CPHA;
|
cr |= XSPI_CR_CPHA;
|
||||||
if (spi->mode & SPI_CPOL)
|
if (spi->mode & SPI_CPOL)
|
||||||
cr |= XSPI_CR_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
|
/* We do not check spi->max_speed_hz here as the SPI clock
|
||||||
* frequency is not software programmable (the IP block design
|
* 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 */
|
/* Activate the chip select */
|
||||||
out_be32(xspi->regs + XSPI_SSR_OFFSET,
|
xspi->write_fn(~(0x0001 << spi->chip_select),
|
||||||
~(0x0001 << spi->chip_select));
|
xspi->regs + XSPI_SSR_OFFSET);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/* spi_bitbang requires custom setup_transfer() to be defined if there is a
|
/* 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
|
* 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.
|
* supports 8 or 16 bits per word which cannot be changed in software.
|
||||||
* Check for 8 bits per word. Chip select delay calculations could be
|
* 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.
|
* 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,
|
static int xilinx_spi_setup_transfer(struct spi_device *spi,
|
||||||
struct spi_transfer *t)
|
struct spi_transfer *t)
|
||||||
{
|
{
|
||||||
|
struct xilinx_spi *xspi = spi_master_get_devdata(spi->master);
|
||||||
u8 bits_per_word;
|
u8 bits_per_word;
|
||||||
|
|
||||||
bits_per_word = (t && t->bits_per_word)
|
bits_per_word = (t && t->bits_per_word)
|
||||||
? t->bits_per_word : spi->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",
|
dev_err(&spi->dev, "%s, unsupported bits_per_word=%d\n",
|
||||||
__func__, bits_per_word);
|
__func__, bits_per_word);
|
||||||
return -EINVAL;
|
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)
|
static int xilinx_spi_setup(struct spi_device *spi)
|
||||||
{
|
{
|
||||||
struct spi_bitbang *bitbang;
|
/* always return 0, we can not check the number of bits.
|
||||||
struct xilinx_spi *xspi;
|
* There are cases when SPI setup is called before any driver is
|
||||||
int retval;
|
* 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
|
||||||
xspi = spi_master_get_devdata(spi->master);
|
* SPI device would not be registered and no driver can get hold of it
|
||||||
bitbang = &xspi->bitbang;
|
* When the driver is there, it will call SPI setup again with the
|
||||||
|
* correct number of bits per transfer.
|
||||||
retval = xilinx_spi_setup_transfer(spi, NULL);
|
* If a driver setups with the wrong bit number, it will fail when
|
||||||
if (retval < 0)
|
* it tries to do a transfer
|
||||||
return retval;
|
*/
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -180,15 +231,14 @@ static void xilinx_spi_fill_tx_fifo(struct xilinx_spi *xspi)
|
||||||
u8 sr;
|
u8 sr;
|
||||||
|
|
||||||
/* Fill the Tx FIFO with as many bytes as possible */
|
/* 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) {
|
while ((sr & XSPI_SR_TX_FULL_MASK) == 0 && xspi->remaining_bytes > 0) {
|
||||||
if (xspi->tx_ptr) {
|
if (xspi->tx_ptr)
|
||||||
out_8(xspi->regs + XSPI_TXD_OFFSET, *xspi->tx_ptr++);
|
xspi->tx_fn(xspi);
|
||||||
} else {
|
else
|
||||||
out_8(xspi->regs + XSPI_TXD_OFFSET, 0);
|
xspi->write_fn(0, xspi->regs + XSPI_TXD_OFFSET);
|
||||||
}
|
xspi->remaining_bytes -= xspi->bits_per_word / 8;
|
||||||
xspi->remaining_bytes--;
|
sr = xspi->read_fn(xspi->regs + XSPI_SR_OFFSET);
|
||||||
sr = in_8(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
|
/* Enable the transmit empty interrupt, which we use to determine
|
||||||
* progress on the transmission.
|
* progress on the transmission.
|
||||||
*/
|
*/
|
||||||
ipif_ier = in_be32(xspi->regs + XIPIF_V123B_IIER_OFFSET);
|
ipif_ier = xspi->read_fn(xspi->regs + XIPIF_V123B_IIER_OFFSET);
|
||||||
out_be32(xspi->regs + XIPIF_V123B_IIER_OFFSET,
|
xspi->write_fn(ipif_ier | XSPI_INTR_TX_EMPTY,
|
||||||
ipif_ier | XSPI_INTR_TX_EMPTY);
|
xspi->regs + XIPIF_V123B_IIER_OFFSET);
|
||||||
|
|
||||||
/* Start the transfer by not inhibiting the transmitter any longer */
|
/* Start the transfer by not inhibiting the transmitter any longer */
|
||||||
cr = in_be16(xspi->regs + XSPI_CR_OFFSET) & ~XSPI_CR_TRANS_INHIBIT;
|
cr = xspi->read_fn(xspi->regs + XSPI_CR_OFFSET) &
|
||||||
out_be16(xspi->regs + XSPI_CR_OFFSET, cr);
|
~XSPI_CR_TRANS_INHIBIT;
|
||||||
|
xspi->write_fn(cr, xspi->regs + XSPI_CR_OFFSET);
|
||||||
|
|
||||||
wait_for_completion(&xspi->done);
|
wait_for_completion(&xspi->done);
|
||||||
|
|
||||||
/* Disable the transmit empty interrupt */
|
/* 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;
|
return t->len - xspi->remaining_bytes;
|
||||||
}
|
}
|
||||||
|
@ -238,8 +289,8 @@ static irqreturn_t xilinx_spi_irq(int irq, void *dev_id)
|
||||||
u32 ipif_isr;
|
u32 ipif_isr;
|
||||||
|
|
||||||
/* Get the IPIF interrupts, and clear them immediately */
|
/* Get the IPIF interrupts, and clear them immediately */
|
||||||
ipif_isr = in_be32(xspi->regs + XIPIF_V123B_IISR_OFFSET);
|
ipif_isr = xspi->read_fn(xspi->regs + XIPIF_V123B_IISR_OFFSET);
|
||||||
out_be32(xspi->regs + XIPIF_V123B_IISR_OFFSET, ipif_isr);
|
xspi->write_fn(ipif_isr, xspi->regs + XIPIF_V123B_IISR_OFFSET);
|
||||||
|
|
||||||
if (ipif_isr & XSPI_INTR_TX_EMPTY) { /* Transmission completed */
|
if (ipif_isr & XSPI_INTR_TX_EMPTY) { /* Transmission completed */
|
||||||
u16 cr;
|
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,
|
* transmitter while the Isr refills the transmit register/FIFO,
|
||||||
* or make sure it is stopped if we're done.
|
* or make sure it is stopped if we're done.
|
||||||
*/
|
*/
|
||||||
cr = in_be16(xspi->regs + XSPI_CR_OFFSET);
|
cr = xspi->read_fn(xspi->regs + XSPI_CR_OFFSET);
|
||||||
out_be16(xspi->regs + XSPI_CR_OFFSET,
|
xspi->write_fn(cr | XSPI_CR_TRANS_INHIBIT,
|
||||||
cr | XSPI_CR_TRANS_INHIBIT);
|
xspi->regs + XSPI_CR_OFFSET);
|
||||||
|
|
||||||
/* Read out all the data from the Rx FIFO */
|
/* 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) {
|
while ((sr & XSPI_SR_RX_EMPTY_MASK) == 0) {
|
||||||
u8 data;
|
xspi->rx_fn(xspi);
|
||||||
|
sr = xspi->read_fn(xspi->regs + XSPI_SR_OFFSET);
|
||||||
data = in_8(xspi->regs + XSPI_RXD_OFFSET);
|
|
||||||
if (xspi->rx_ptr) {
|
|
||||||
*xspi->rx_ptr++ = data;
|
|
||||||
}
|
|
||||||
sr = in_8(xspi->regs + XSPI_SR_OFFSET);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* See if there is more data to send */
|
/* 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
|
/* Start the transfer by not inhibiting the
|
||||||
* transmitter any longer
|
* transmitter any longer
|
||||||
*/
|
*/
|
||||||
out_be16(xspi->regs + XSPI_CR_OFFSET, cr);
|
xspi->write_fn(cr, xspi->regs + XSPI_CR_OFFSET);
|
||||||
} else {
|
} else {
|
||||||
/* No more data to send.
|
/* No more data to send.
|
||||||
* Indicate the transfer is completed.
|
* Indicate the transfer is completed.
|
||||||
|
@ -284,40 +330,22 @@ static irqreturn_t xilinx_spi_irq(int irq, void *dev_id)
|
||||||
return IRQ_HANDLED;
|
return IRQ_HANDLED;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int __init xilinx_spi_of_probe(struct of_device *ofdev,
|
struct spi_master *xilinx_spi_init(struct device *dev, struct resource *mem,
|
||||||
const struct of_device_id *match)
|
u32 irq, s16 bus_num)
|
||||||
{
|
{
|
||||||
struct spi_master *master;
|
struct spi_master *master;
|
||||||
struct xilinx_spi *xspi;
|
struct xilinx_spi *xspi;
|
||||||
struct resource r_irq_struct;
|
struct xspi_platform_data *pdata = dev->platform_data;
|
||||||
struct resource r_mem_struct;
|
int ret;
|
||||||
|
|
||||||
struct resource *r_irq = &r_irq_struct;
|
if (!pdata) {
|
||||||
struct resource *r_mem = &r_mem_struct;
|
dev_err(dev, "No platform data attached\n");
|
||||||
int rc = 0;
|
return NULL;
|
||||||
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;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
dev_set_drvdata(&ofdev->dev, master);
|
master = spi_alloc_master(dev, sizeof(struct xilinx_spi));
|
||||||
|
if (!master)
|
||||||
rc = of_address_to_resource(ofdev->node, 0, r_mem);
|
return NULL;
|
||||||
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;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* the spi->mode bits understood by this driver: */
|
/* the spi->mode bits understood by this driver: */
|
||||||
master->mode_bits = SPI_CPOL | SPI_CPHA;
|
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;
|
xspi->bitbang.master->setup = xilinx_spi_setup;
|
||||||
init_completion(&xspi->done);
|
init_completion(&xspi->done);
|
||||||
|
|
||||||
xspi->irq = r_irq->start;
|
if (!request_mem_region(mem->start, resource_size(mem),
|
||||||
|
XILINX_SPI_NAME))
|
||||||
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");
|
|
||||||
goto put_master;
|
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) {
|
if (xspi->regs == NULL) {
|
||||||
rc = -ENOMEM;
|
dev_warn(dev, "ioremap failure\n");
|
||||||
dev_warn(&ofdev->dev, "ioremap failure\n");
|
goto map_failed;
|
||||||
goto release_mem;
|
|
||||||
}
|
}
|
||||||
xspi->irq = r_irq->start;
|
|
||||||
|
|
||||||
/* dynamic bus assignment */
|
master->bus_num = bus_num;
|
||||||
master->bus_num = -1;
|
master->num_chipselect = pdata->num_chipselect;
|
||||||
|
|
||||||
/* number of slave select bits is required */
|
xspi->mem = *mem;
|
||||||
prop = of_get_property(ofdev->node, "xlnx,num-ss-bits", &len);
|
xspi->irq = irq;
|
||||||
if (!prop || len < sizeof(*prop)) {
|
if (pdata->little_endian) {
|
||||||
dev_warn(&ofdev->dev, "no 'xlnx,num-ss-bits' property\n");
|
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;
|
goto unmap_io;
|
||||||
}
|
|
||||||
master->num_chipselect = *prop;
|
|
||||||
|
|
||||||
/* SPI controller initializations */
|
/* SPI controller initializations */
|
||||||
xspi_init_hw(xspi->regs);
|
xspi_init_hw(xspi);
|
||||||
|
|
||||||
/* Register for SPI Interrupt */
|
/* Register for SPI Interrupt */
|
||||||
rc = request_irq(xspi->irq, xilinx_spi_irq, 0, XILINX_SPI_NAME, xspi);
|
ret = request_irq(xspi->irq, xilinx_spi_irq, 0, XILINX_SPI_NAME, xspi);
|
||||||
if (rc != 0) {
|
if (ret)
|
||||||
dev_warn(&ofdev->dev, "irq request failure: %d\n", xspi->irq);
|
|
||||||
goto unmap_io;
|
goto unmap_io;
|
||||||
}
|
|
||||||
|
|
||||||
rc = spi_bitbang_start(&xspi->bitbang);
|
ret = spi_bitbang_start(&xspi->bitbang);
|
||||||
if (rc != 0) {
|
if (ret) {
|
||||||
dev_err(&ofdev->dev, "spi_bitbang_start FAILED\n");
|
dev_err(dev, "spi_bitbang_start FAILED\n");
|
||||||
goto free_irq;
|
goto free_irq;
|
||||||
}
|
}
|
||||||
|
|
||||||
dev_info(&ofdev->dev, "at 0x%08X mapped to 0x%08X, irq=%d\n",
|
dev_info(dev, "at 0x%08llX mapped to 0x%p, irq=%d\n",
|
||||||
(unsigned int)r_mem->start, (u32)xspi->regs, xspi->irq);
|
(unsigned long long)mem->start, xspi->regs, xspi->irq);
|
||||||
|
return master;
|
||||||
/* Add any subnodes on the SPI bus */
|
|
||||||
of_register_spi_devices(master, ofdev->node);
|
|
||||||
|
|
||||||
return rc;
|
|
||||||
|
|
||||||
free_irq:
|
free_irq:
|
||||||
free_irq(xspi->irq, xspi);
|
free_irq(xspi->irq, xspi);
|
||||||
unmap_io:
|
unmap_io:
|
||||||
iounmap(xspi->regs);
|
iounmap(xspi->regs);
|
||||||
release_mem:
|
map_failed:
|
||||||
release_mem_region(r_mem->start, resource_size(r_mem));
|
release_mem_region(mem->start, resource_size(mem));
|
||||||
put_master:
|
put_master:
|
||||||
spi_master_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 xilinx_spi *xspi;
|
||||||
struct spi_master *master;
|
|
||||||
struct resource r_mem;
|
|
||||||
|
|
||||||
master = platform_get_drvdata(ofdev);
|
|
||||||
xspi = spi_master_get_devdata(master);
|
xspi = spi_master_get_devdata(master);
|
||||||
|
|
||||||
spi_bitbang_stop(&xspi->bitbang);
|
spi_bitbang_stop(&xspi->bitbang);
|
||||||
free_irq(xspi->irq, xspi);
|
free_irq(xspi->irq, xspi);
|
||||||
iounmap(xspi->regs);
|
iounmap(xspi->regs);
|
||||||
if (!of_address_to_resource(ofdev->node, 0, &r_mem))
|
|
||||||
release_mem_region(r_mem.start, resource_size(&r_mem));
|
release_mem_region(xspi->mem.start, resource_size(&xspi->mem));
|
||||||
dev_set_drvdata(&ofdev->dev, 0);
|
|
||||||
spi_master_put(xspi->bitbang.master);
|
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_AUTHOR("MontaVista Software, Inc. <source@mvista.com>");
|
||||||
MODULE_DESCRIPTION("Xilinx SPI driver");
|
MODULE_DESCRIPTION("Xilinx SPI driver");
|
||||||
MODULE_LICENSE("GPL");
|
MODULE_LICENSE("GPL");
|
||||||
|
|
|
@ -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
|
|
@ -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");
|
|
@ -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");
|
|
@ -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
|
|
|
@ -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__ */
|
|
@ -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 */
|
Загрузка…
Ссылка в новой задаче