spi: Updates for 3.6

Since Grant is even more specacularly busy than usual for the time being
 I've been collecting SPI patches for him for this release - probably
 things will revert back to Grant before the next release.  There's
 nothing too exciting here, mostly it's simple driver specific stuff:
 
 - Add spi: to the modaliases of SPI devices to provide namespacing.
 - A driver for AD-FMCOMMS1-EBZ.
 - DT binding for Orion.
 - Fixes and cleanups for i.MX, PL0022, OMAP and bitbang drivers.
 
 There may be a few more fixes I've missed, people keep sending me new
 things.
 -----BEGIN PGP SIGNATURE-----
 Version: GnuPG v1.4.12 (GNU/Linux)
 
 iQIcBAABAgAGBQJQD+gTAAoJEBus8iNuMP3diecP/j88CTuSVi2C9LQ6irty2q6y
 62eLoR5m5J9zqqRlsFLkerxuanlwyD0WPR4B86SZMr/TQbXkLtmdVyQwSieyMfnN
 4HSrFQawCnVANoBrvnKa1vHfP4ouBStXIZa5v/iSAC4tN4K/wVGUvl6Kral5+ktC
 5BRwcEdcrVR4KnD74i7ombopM/hl6fRdNS+TwgfXlGwGFHPYbHOa+aJZnGUORWfw
 30jrKLap8cTGLDTFQ+Lu8WPzap+RuGlMSWGhXlYrivKNepvpt/WEMhJnfJ/ADLqO
 ASGe3C69xGTF6YRETSoH5+1VZjRhf+S9y/nV5gkQZKtDDGez5iFmCGZtv/m87u6/
 nIuTICa7QjI9bvP9novq0h6EUwURyL4dtdC4VI3IedIpgFiKGxz3J63YJLsIc7tH
 5jXPJAkyU/yj23eDywftYZskK3iNYOthB2VO3JPcmGQRXeMWDvTPXXJBcOy5fvW0
 aOmE+vXINiQnmddBBNIuhPGRlJUGSpHLaioz+H2+unrNPHjQnv+CSHt7YuYwjTGp
 IFFwVSUK6r4O+/W7ciKalVQr0bjTM6J3wO0sCqxta1qVoI89RgcM3h3XwXTCb6Ly
 0Ywgn+3W71XDiY+L4VvYRTcur2kGB/rmIsIORuqS/FkhR9iuIx7u30d6KmlGR2ak
 i+cExj0Qo/pF4TXDw22W
 =I2b9
 -----END PGP SIGNATURE-----

Merge tag 'spi-3.6' into v3.5-rc7-dt-v3

spi: Updates for 3.6

Since Grant is even more specacularly busy than usual for the time being
I've been collecting SPI patches for him for this release - probably
things will revert back to Grant before the next release.  There's
nothing too exciting here, mostly it's simple driver specific stuff:

- Add spi: to the modaliases of SPI devices to provide namespacing.
- A driver for AD-FMCOMMS1-EBZ.
- DT binding for Orion.
- Fixes and cleanups for i.MX, PL0022, OMAP and bitbang drivers.

There may be a few more fixes I've missed, people keep sending me new
things.
This commit is contained in:
Andrew Lunn 2012-07-27 16:45:04 +02:00
commit 4297103560
13 changed files with 430 additions and 47 deletions

View File

@ -0,0 +1,19 @@
Marvell Orion SPI device
Required properties:
- compatible : should be "marvell,orion-spi".
- reg : offset and length of the register set for the device
- cell-index : Which of multiple SPI controllers is this.
Optional properties:
- interrupts : Is currently not used.
Example:
spi@10600 {
compatible = "marvell,orion-spi";
#address-cells = <1>;
#size-cells = <0>;
cell-index = <0>;
reg = <0x10600 0x28>;
interrupts = <23>;
status = "disabled";
};

View File

@ -357,7 +357,7 @@ config SPI_STMP3XXX
config SPI_TEGRA config SPI_TEGRA
tristate "Nvidia Tegra SPI controller" tristate "Nvidia Tegra SPI controller"
depends on ARCH_TEGRA && TEGRA_SYSTEM_DMA depends on ARCH_TEGRA && (TEGRA_SYSTEM_DMA || TEGRA20_APB_DMA)
help help
SPI driver for NVidia Tegra SoCs SPI driver for NVidia Tegra SoCs
@ -384,6 +384,13 @@ config SPI_TXX9
help help
SPI driver for Toshiba TXx9 MIPS SoCs SPI driver for Toshiba TXx9 MIPS SoCs
config SPI_XCOMM
tristate "Analog Devices AD-FMCOMMS1-EBZ SPI-I2C-bridge driver"
depends on I2C
help
Support for the SPI-I2C bridge found on the Analog Devices
AD-FMCOMMS1-EBZ board.
config SPI_XILINX config SPI_XILINX
tristate "Xilinx SPI controller common module" tristate "Xilinx SPI controller common module"
depends on HAS_IOMEM && EXPERIMENTAL depends on HAS_IOMEM && EXPERIMENTAL

View File

@ -61,5 +61,6 @@ obj-$(CONFIG_SPI_TI_SSP) += spi-ti-ssp.o
obj-$(CONFIG_SPI_TLE62X0) += spi-tle62x0.o obj-$(CONFIG_SPI_TLE62X0) += spi-tle62x0.o
obj-$(CONFIG_SPI_TOPCLIFF_PCH) += spi-topcliff-pch.o obj-$(CONFIG_SPI_TOPCLIFF_PCH) += spi-topcliff-pch.o
obj-$(CONFIG_SPI_TXX9) += spi-txx9.o obj-$(CONFIG_SPI_TXX9) += spi-txx9.o
obj-$(CONFIG_SPI_XCOMM) += spi-xcomm.o
obj-$(CONFIG_SPI_XILINX) += spi-xilinx.o obj-$(CONFIG_SPI_XILINX) += spi-xilinx.o

View File

@ -129,7 +129,7 @@ static void bcm63xx_spi_setup_transfer(struct spi_device *spi,
/* Find the closest clock configuration */ /* Find the closest clock configuration */
for (i = 0; i < SPI_CLK_MASK; i++) { for (i = 0; i < SPI_CLK_MASK; i++) {
if (hz <= bcm63xx_spi_freq_table[i][0]) { if (hz >= bcm63xx_spi_freq_table[i][0]) {
clk_cfg = bcm63xx_spi_freq_table[i][1]; clk_cfg = bcm63xx_spi_freq_table[i][1];
break; break;
} }

View File

@ -235,7 +235,8 @@ static int spi_gpio_setup(struct spi_device *spi)
status = gpio_request(cs, dev_name(&spi->dev)); status = gpio_request(cs, dev_name(&spi->dev));
if (status) if (status)
return status; return status;
status = gpio_direction_output(cs, spi->mode & SPI_CS_HIGH); status = gpio_direction_output(cs,
!(spi->mode & SPI_CS_HIGH));
} }
} }
if (!status) if (!status)

View File

@ -626,7 +626,7 @@ static void spi_imx_chipselect(struct spi_device *spi, int is_active)
int active = is_active != BITBANG_CS_INACTIVE; int active = is_active != BITBANG_CS_INACTIVE;
int dev_is_lowactive = !(spi->mode & SPI_CS_HIGH); int dev_is_lowactive = !(spi->mode & SPI_CS_HIGH);
if (gpio < 0) if (!gpio_is_valid(gpio))
return; return;
gpio_set_value(gpio, dev_is_lowactive ^ active); gpio_set_value(gpio, dev_is_lowactive ^ active);
@ -688,8 +688,6 @@ static int spi_imx_setupxfer(struct spi_device *spi,
config.speed_hz = spi->max_speed_hz; config.speed_hz = spi->max_speed_hz;
if (!config.bpw) if (!config.bpw)
config.bpw = spi->bits_per_word; config.bpw = spi->bits_per_word;
if (!config.speed_hz)
config.speed_hz = spi->max_speed_hz;
/* Initialize the functions for transfer */ /* Initialize the functions for transfer */
if (config.bpw <= 8) { if (config.bpw <= 8) {
@ -738,7 +736,7 @@ static int spi_imx_setup(struct spi_device *spi)
dev_dbg(&spi->dev, "%s: mode %d, %u bpw, %d hz\n", __func__, dev_dbg(&spi->dev, "%s: mode %d, %u bpw, %d hz\n", __func__,
spi->mode, spi->bits_per_word, spi->max_speed_hz); spi->mode, spi->bits_per_word, spi->max_speed_hz);
if (gpio >= 0) if (gpio_is_valid(gpio))
gpio_direction_output(gpio, spi->mode & SPI_CS_HIGH ? 0 : 1); gpio_direction_output(gpio, spi->mode & SPI_CS_HIGH ? 0 : 1);
spi_imx_chipselect(spi, BITBANG_CS_INACTIVE); spi_imx_chipselect(spi, BITBANG_CS_INACTIVE);
@ -791,11 +789,11 @@ static int __devinit spi_imx_probe(struct platform_device *pdev)
for (i = 0; i < master->num_chipselect; i++) { for (i = 0; i < master->num_chipselect; i++) {
int cs_gpio = of_get_named_gpio(np, "cs-gpios", i); int cs_gpio = of_get_named_gpio(np, "cs-gpios", i);
if (cs_gpio < 0 && mxc_platform_info) if (!gpio_is_valid(cs_gpio) && mxc_platform_info)
cs_gpio = mxc_platform_info->chipselect[i]; cs_gpio = mxc_platform_info->chipselect[i];
spi_imx->chipselect[i] = cs_gpio; spi_imx->chipselect[i] = cs_gpio;
if (cs_gpio < 0) if (!gpio_is_valid(cs_gpio))
continue; continue;
ret = gpio_request(spi_imx->chipselect[i], DRIVER_NAME); ret = gpio_request(spi_imx->chipselect[i], DRIVER_NAME);
@ -897,7 +895,7 @@ static int __devinit spi_imx_probe(struct platform_device *pdev)
release_mem_region(res->start, resource_size(res)); release_mem_region(res->start, resource_size(res));
out_gpio_free: out_gpio_free:
while (--i >= 0) { while (--i >= 0) {
if (spi_imx->chipselect[i] >= 0) if (gpio_is_valid(spi_imx->chipselect[i]))
gpio_free(spi_imx->chipselect[i]); gpio_free(spi_imx->chipselect[i]);
} }
spi_master_put(master); spi_master_put(master);
@ -922,7 +920,7 @@ static int __devexit spi_imx_remove(struct platform_device *pdev)
iounmap(spi_imx->base); iounmap(spi_imx->base);
for (i = 0; i < master->num_chipselect; i++) for (i = 0; i < master->num_chipselect; i++)
if (spi_imx->chipselect[i] >= 0) if (gpio_is_valid(spi_imx->chipselect[i]))
gpio_free(spi_imx->chipselect[i]); gpio_free(spi_imx->chipselect[i]);
spi_master_put(master); spi_master_put(master);

View File

@ -388,7 +388,8 @@ omap2_mcspi_txrx_dma(struct spi_device *spi, struct spi_transfer *xfer)
if (tx != NULL) { if (tx != NULL) {
wait_for_completion(&mcspi_dma->dma_tx_completion); wait_for_completion(&mcspi_dma->dma_tx_completion);
dma_unmap_single(&spi->dev, xfer->tx_dma, count, DMA_TO_DEVICE); dma_unmap_single(mcspi->dev, xfer->tx_dma, count,
DMA_TO_DEVICE);
/* for TX_ONLY mode, be sure all words have shifted out */ /* for TX_ONLY mode, be sure all words have shifted out */
if (rx == NULL) { if (rx == NULL) {
@ -403,7 +404,8 @@ omap2_mcspi_txrx_dma(struct spi_device *spi, struct spi_transfer *xfer)
if (rx != NULL) { if (rx != NULL) {
wait_for_completion(&mcspi_dma->dma_rx_completion); wait_for_completion(&mcspi_dma->dma_rx_completion);
dma_unmap_single(&spi->dev, xfer->rx_dma, count, DMA_FROM_DEVICE); dma_unmap_single(mcspi->dev, xfer->rx_dma, count,
DMA_FROM_DEVICE);
omap2_mcspi_set_enable(spi, 0); omap2_mcspi_set_enable(spi, 0);
if (l & OMAP2_MCSPI_CHCONF_TURBO) { if (l & OMAP2_MCSPI_CHCONF_TURBO) {
@ -1032,7 +1034,7 @@ static int omap2_mcspi_transfer_one_message(struct spi_master *master,
return 0; return 0;
} }
static int __init omap2_mcspi_master_setup(struct omap2_mcspi *mcspi) static int __devinit omap2_mcspi_master_setup(struct omap2_mcspi *mcspi)
{ {
struct spi_master *master = mcspi->master; struct spi_master *master = mcspi->master;
struct omap2_mcspi_regs *ctx = &mcspi->ctx; struct omap2_mcspi_regs *ctx = &mcspi->ctx;

View File

@ -17,6 +17,7 @@
#include <linux/io.h> #include <linux/io.h>
#include <linux/spi/spi.h> #include <linux/spi/spi.h>
#include <linux/module.h> #include <linux/module.h>
#include <linux/of.h>
#include <linux/clk.h> #include <linux/clk.h>
#include <asm/unaligned.h> #include <asm/unaligned.h>
@ -45,7 +46,6 @@ struct orion_spi {
void __iomem *base; void __iomem *base;
unsigned int max_speed; unsigned int max_speed;
unsigned int min_speed; unsigned int min_speed;
struct orion_spi_info *spi_info;
struct clk *clk; struct clk *clk;
}; };
@ -450,11 +450,10 @@ static int __init orion_spi_probe(struct platform_device *pdev)
struct spi_master *master; struct spi_master *master;
struct orion_spi *spi; struct orion_spi *spi;
struct resource *r; struct resource *r;
struct orion_spi_info *spi_info;
unsigned long tclk_hz; unsigned long tclk_hz;
int status = 0; int status = 0;
const u32 *iprop;
spi_info = pdev->dev.platform_data; int size;
master = spi_alloc_master(&pdev->dev, sizeof *spi); master = spi_alloc_master(&pdev->dev, sizeof *spi);
if (master == NULL) { if (master == NULL) {
@ -464,6 +463,12 @@ static int __init orion_spi_probe(struct platform_device *pdev)
if (pdev->id != -1) if (pdev->id != -1)
master->bus_num = pdev->id; master->bus_num = pdev->id;
if (pdev->dev.of_node) {
iprop = of_get_property(pdev->dev.of_node, "cell-index",
&size);
if (iprop && size == sizeof(*iprop))
master->bus_num = *iprop;
}
/* we support only mode 0, and no options */ /* we support only mode 0, and no options */
master->mode_bits = 0; master->mode_bits = 0;
@ -476,7 +481,6 @@ static int __init orion_spi_probe(struct platform_device *pdev)
spi = spi_master_get_devdata(master); spi = spi_master_get_devdata(master);
spi->master = master; spi->master = master;
spi->spi_info = spi_info;
spi->clk = clk_get(&pdev->dev, NULL); spi->clk = clk_get(&pdev->dev, NULL);
if (IS_ERR(spi->clk)) { if (IS_ERR(spi->clk)) {
@ -511,6 +515,7 @@ static int __init orion_spi_probe(struct platform_device *pdev)
if (orion_spi_reset(spi) < 0) if (orion_spi_reset(spi) < 0)
goto out_rel_mem; goto out_rel_mem;
master->dev.of_node = pdev->dev.of_node;
status = spi_register_master(master); status = spi_register_master(master);
if (status < 0) if (status < 0)
goto out_rel_mem; goto out_rel_mem;
@ -552,10 +557,17 @@ static int __exit orion_spi_remove(struct platform_device *pdev)
MODULE_ALIAS("platform:" DRIVER_NAME); MODULE_ALIAS("platform:" DRIVER_NAME);
static const struct of_device_id orion_spi_of_match_table[] __devinitdata = {
{ .compatible = "marvell,orion-spi", },
{}
};
MODULE_DEVICE_TABLE(of, orion_spi_of_match_table);
static struct platform_driver orion_spi_driver = { static struct platform_driver orion_spi_driver = {
.driver = { .driver = {
.name = DRIVER_NAME, .name = DRIVER_NAME,
.owner = THIS_MODULE, .owner = THIS_MODULE,
.of_match_table = of_match_ptr(orion_spi_of_match_table),
}, },
.remove = __exit_p(orion_spi_remove), .remove = __exit_p(orion_spi_remove),
}; };

View File

@ -489,6 +489,11 @@ static void giveback(struct pl022 *pl022)
pl022->cur_transfer = NULL; pl022->cur_transfer = NULL;
pl022->cur_chip = NULL; pl022->cur_chip = NULL;
spi_finalize_current_message(pl022->master); spi_finalize_current_message(pl022->master);
/* disable the SPI/SSP operation */
writew((readw(SSP_CR1(pl022->virtbase)) &
(~SSP_CR1_MASK_SSE)), SSP_CR1(pl022->virtbase));
} }
/** /**
@ -2048,6 +2053,9 @@ pl022_probe(struct amba_device *adev, const struct amba_id *id)
printk(KERN_INFO "pl022: mapped registers from 0x%08x to %p\n", printk(KERN_INFO "pl022: mapped registers from 0x%08x to %p\n",
adev->res.start, pl022->virtbase); adev->res.start, pl022->virtbase);
pm_runtime_enable(dev);
pm_runtime_resume(dev);
pl022->clk = clk_get(&adev->dev, NULL); pl022->clk = clk_get(&adev->dev, NULL);
if (IS_ERR(pl022->clk)) { if (IS_ERR(pl022->clk)) {
status = PTR_ERR(pl022->clk); status = PTR_ERR(pl022->clk);
@ -2158,6 +2166,7 @@ pl022_remove(struct amba_device *adev)
clk_disable(pl022->clk); clk_disable(pl022->clk);
clk_unprepare(pl022->clk); clk_unprepare(pl022->clk);
clk_put(pl022->clk); clk_put(pl022->clk);
pm_runtime_disable(&adev->dev);
iounmap(pl022->virtbase); iounmap(pl022->virtbase);
amba_release_regions(adev); amba_release_regions(adev);
tasklet_disable(&pl022->pump_transfers); tasklet_disable(&pl022->pump_transfers);
@ -2251,15 +2260,6 @@ static struct vendor_data vendor_st_pl023 = {
.loopback = false, .loopback = false,
}; };
static struct vendor_data vendor_db5500_pl023 = {
.fifodepth = 32,
.max_bpw = 32,
.unidir = false,
.extended_cr = true,
.pl023 = true,
.loopback = true,
};
static struct amba_id pl022_ids[] = { static struct amba_id pl022_ids[] = {
{ {
/* /*
@ -2291,11 +2291,6 @@ static struct amba_id pl022_ids[] = {
.mask = 0xffffffff, .mask = 0xffffffff,
.data = &vendor_st_pl023, .data = &vendor_st_pl023,
}, },
{
.id = 0x10080023,
.mask = 0xffffffff,
.data = &vendor_db5500_pl023,
},
{ 0, 0 }, { 0, 0 },
}; };

View File

@ -30,6 +30,7 @@
#include <linux/delay.h> #include <linux/delay.h>
#include <linux/spi/spi.h> #include <linux/spi/spi.h>
#include <linux/dmaengine.h>
#include <mach/dma.h> #include <mach/dma.h>
@ -162,12 +163,23 @@ struct spi_tegra_data {
* require transfers to be 4 byte aligned we need a bounce buffer * require transfers to be 4 byte aligned we need a bounce buffer
* for the generic case. * for the generic case.
*/ */
int dma_req_len;
#if defined(CONFIG_TEGRA_SYSTEM_DMA)
struct tegra_dma_req rx_dma_req; struct tegra_dma_req rx_dma_req;
struct tegra_dma_channel *rx_dma; struct tegra_dma_channel *rx_dma;
#else
struct dma_chan *rx_dma;
struct dma_slave_config sconfig;
struct dma_async_tx_descriptor *rx_dma_desc;
dma_cookie_t rx_cookie;
#endif
u32 *rx_bb; u32 *rx_bb;
dma_addr_t rx_bb_phys; dma_addr_t rx_bb_phys;
}; };
#if !defined(CONFIG_TEGRA_SYSTEM_DMA)
static void tegra_spi_rx_dma_complete(void *args);
#endif
static inline unsigned long spi_tegra_readl(struct spi_tegra_data *tspi, static inline unsigned long spi_tegra_readl(struct spi_tegra_data *tspi,
unsigned long reg) unsigned long reg)
@ -190,10 +202,24 @@ static void spi_tegra_go(struct spi_tegra_data *tspi)
val = spi_tegra_readl(tspi, SLINK_DMA_CTL); val = spi_tegra_readl(tspi, SLINK_DMA_CTL);
val &= ~SLINK_DMA_BLOCK_SIZE(~0) & ~SLINK_DMA_EN; val &= ~SLINK_DMA_BLOCK_SIZE(~0) & ~SLINK_DMA_EN;
val |= SLINK_DMA_BLOCK_SIZE(tspi->rx_dma_req.size / 4 - 1); val |= SLINK_DMA_BLOCK_SIZE(tspi->dma_req_len / 4 - 1);
spi_tegra_writel(tspi, val, SLINK_DMA_CTL); spi_tegra_writel(tspi, val, SLINK_DMA_CTL);
#if defined(CONFIG_TEGRA_SYSTEM_DMA)
tspi->rx_dma_req.size = tspi->dma_req_len;
tegra_dma_enqueue_req(tspi->rx_dma, &tspi->rx_dma_req); tegra_dma_enqueue_req(tspi->rx_dma, &tspi->rx_dma_req);
#else
tspi->rx_dma_desc = dmaengine_prep_slave_single(tspi->rx_dma,
tspi->rx_bb_phys, tspi->dma_req_len,
DMA_DEV_TO_MEM, DMA_PREP_INTERRUPT);
if (!tspi->rx_dma_desc) {
dev_err(&tspi->pdev->dev, "dmaengine slave prep failed\n");
return;
}
tspi->rx_dma_desc->callback = tegra_spi_rx_dma_complete;
tspi->rx_dma_desc->callback_param = tspi;
tspi->rx_cookie = dmaengine_submit(tspi->rx_dma_desc);
dma_async_issue_pending(tspi->rx_dma);
#endif
val |= SLINK_DMA_EN; val |= SLINK_DMA_EN;
spi_tegra_writel(tspi, val, SLINK_DMA_CTL); spi_tegra_writel(tspi, val, SLINK_DMA_CTL);
@ -221,7 +247,7 @@ static unsigned spi_tegra_fill_tx_fifo(struct spi_tegra_data *tspi,
spi_tegra_writel(tspi, val, SLINK_TX_FIFO); spi_tegra_writel(tspi, val, SLINK_TX_FIFO);
} }
tspi->rx_dma_req.size = len / tspi->cur_bytes_per_word * 4; tspi->dma_req_len = len / tspi->cur_bytes_per_word * 4;
return len; return len;
} }
@ -318,9 +344,8 @@ static void spi_tegra_start_message(struct spi_device *spi,
spi_tegra_start_transfer(spi, t); spi_tegra_start_transfer(spi, t);
} }
static void tegra_spi_rx_dma_complete(struct tegra_dma_req *req) static void handle_spi_rx_dma_complete(struct spi_tegra_data *tspi)
{ {
struct spi_tegra_data *tspi = req->dev;
unsigned long flags; unsigned long flags;
struct spi_message *m; struct spi_message *m;
struct spi_device *spi; struct spi_device *spi;
@ -380,6 +405,19 @@ static void tegra_spi_rx_dma_complete(struct tegra_dma_req *req)
spin_unlock_irqrestore(&tspi->lock, flags); spin_unlock_irqrestore(&tspi->lock, flags);
} }
#if defined(CONFIG_TEGRA_SYSTEM_DMA)
static void tegra_spi_rx_dma_complete(struct tegra_dma_req *req)
{
struct spi_tegra_data *tspi = req->dev;
handle_spi_rx_dma_complete(tspi);
}
#else
static void tegra_spi_rx_dma_complete(void *args)
{
struct spi_tegra_data *tspi = args;
handle_spi_rx_dma_complete(tspi);
}
#endif
static int spi_tegra_setup(struct spi_device *spi) static int spi_tegra_setup(struct spi_device *spi)
{ {
@ -471,6 +509,9 @@ static int __devinit spi_tegra_probe(struct platform_device *pdev)
struct spi_tegra_data *tspi; struct spi_tegra_data *tspi;
struct resource *r; struct resource *r;
int ret; int ret;
#if !defined(CONFIG_TEGRA_SYSTEM_DMA)
dma_cap_mask_t mask;
#endif
master = spi_alloc_master(&pdev->dev, sizeof *tspi); master = spi_alloc_master(&pdev->dev, sizeof *tspi);
if (master == NULL) { if (master == NULL) {
@ -522,12 +563,24 @@ static int __devinit spi_tegra_probe(struct platform_device *pdev)
INIT_LIST_HEAD(&tspi->queue); INIT_LIST_HEAD(&tspi->queue);
#if defined(CONFIG_TEGRA_SYSTEM_DMA)
tspi->rx_dma = tegra_dma_allocate_channel(TEGRA_DMA_MODE_ONESHOT); tspi->rx_dma = tegra_dma_allocate_channel(TEGRA_DMA_MODE_ONESHOT);
if (!tspi->rx_dma) { if (!tspi->rx_dma) {
dev_err(&pdev->dev, "can not allocate rx dma channel\n"); dev_err(&pdev->dev, "can not allocate rx dma channel\n");
ret = -ENODEV; ret = -ENODEV;
goto err3; goto err3;
} }
#else
dma_cap_zero(mask);
dma_cap_set(DMA_SLAVE, mask);
tspi->rx_dma = dma_request_channel(mask, NULL, NULL);
if (!tspi->rx_dma) {
dev_err(&pdev->dev, "can not allocate rx dma channel\n");
ret = -ENODEV;
goto err3;
}
#endif
tspi->rx_bb = dma_alloc_coherent(&pdev->dev, sizeof(u32) * BB_LEN, tspi->rx_bb = dma_alloc_coherent(&pdev->dev, sizeof(u32) * BB_LEN,
&tspi->rx_bb_phys, GFP_KERNEL); &tspi->rx_bb_phys, GFP_KERNEL);
@ -537,6 +590,7 @@ static int __devinit spi_tegra_probe(struct platform_device *pdev)
goto err4; goto err4;
} }
#if defined(CONFIG_TEGRA_SYSTEM_DMA)
tspi->rx_dma_req.complete = tegra_spi_rx_dma_complete; tspi->rx_dma_req.complete = tegra_spi_rx_dma_complete;
tspi->rx_dma_req.to_memory = 1; tspi->rx_dma_req.to_memory = 1;
tspi->rx_dma_req.dest_addr = tspi->rx_bb_phys; tspi->rx_dma_req.dest_addr = tspi->rx_bb_phys;
@ -546,6 +600,23 @@ static int __devinit spi_tegra_probe(struct platform_device *pdev)
tspi->rx_dma_req.source_wrap = 4; tspi->rx_dma_req.source_wrap = 4;
tspi->rx_dma_req.req_sel = spi_tegra_req_sels[pdev->id]; tspi->rx_dma_req.req_sel = spi_tegra_req_sels[pdev->id];
tspi->rx_dma_req.dev = tspi; tspi->rx_dma_req.dev = tspi;
#else
/* Dmaengine Dma slave config */
tspi->sconfig.src_addr = tspi->phys + SLINK_RX_FIFO;
tspi->sconfig.dst_addr = tspi->phys + SLINK_RX_FIFO;
tspi->sconfig.src_addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES;
tspi->sconfig.dst_addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES;
tspi->sconfig.slave_id = spi_tegra_req_sels[pdev->id];
tspi->sconfig.src_maxburst = 1;
tspi->sconfig.dst_maxburst = 1;
ret = dmaengine_device_control(tspi->rx_dma,
DMA_SLAVE_CONFIG, (unsigned long) &tspi->sconfig);
if (ret < 0) {
dev_err(&pdev->dev, "can not do slave configure for dma %d\n",
ret);
goto err4;
}
#endif
master->dev.of_node = pdev->dev.of_node; master->dev.of_node = pdev->dev.of_node;
ret = spi_register_master(master); ret = spi_register_master(master);
@ -559,7 +630,11 @@ static int __devinit spi_tegra_probe(struct platform_device *pdev)
dma_free_coherent(&pdev->dev, sizeof(u32) * BB_LEN, dma_free_coherent(&pdev->dev, sizeof(u32) * BB_LEN,
tspi->rx_bb, tspi->rx_bb_phys); tspi->rx_bb, tspi->rx_bb_phys);
err4: err4:
#if defined(CONFIG_TEGRA_SYSTEM_DMA)
tegra_dma_free_channel(tspi->rx_dma); tegra_dma_free_channel(tspi->rx_dma);
#else
dma_release_channel(tspi->rx_dma);
#endif
err3: err3:
clk_put(tspi->clk); clk_put(tspi->clk);
err2: err2:
@ -581,7 +656,11 @@ static int __devexit spi_tegra_remove(struct platform_device *pdev)
tspi = spi_master_get_devdata(master); tspi = spi_master_get_devdata(master);
spi_unregister_master(master); spi_unregister_master(master);
#if defined(CONFIG_TEGRA_SYSTEM_DMA)
tegra_dma_free_channel(tspi->rx_dma); tegra_dma_free_channel(tspi->rx_dma);
#else
dma_release_channel(tspi->rx_dma);
#endif
dma_free_coherent(&pdev->dev, sizeof(u32) * BB_LEN, dma_free_coherent(&pdev->dev, sizeof(u32) * BB_LEN,
tspi->rx_bb, tspi->rx_bb_phys); tspi->rx_bb, tspi->rx_bb_phys);

276
drivers/spi/spi-xcomm.c Normal file
View File

@ -0,0 +1,276 @@
/*
* Analog Devices AD-FMCOMMS1-EBZ board I2C-SPI bridge driver
*
* Copyright 2012 Analog Devices Inc.
* Author: Lars-Peter Clausen <lars@metafoo.de>
*
* Licensed under the GPL-2 or later.
*/
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/module.h>
#include <linux/delay.h>
#include <linux/i2c.h>
#include <linux/spi/spi.h>
#include <asm/unaligned.h>
#define SPI_XCOMM_SETTINGS_LEN_OFFSET 10
#define SPI_XCOMM_SETTINGS_3WIRE BIT(6)
#define SPI_XCOMM_SETTINGS_CS_HIGH BIT(5)
#define SPI_XCOMM_SETTINGS_SAMPLE_END BIT(4)
#define SPI_XCOMM_SETTINGS_CPHA BIT(3)
#define SPI_XCOMM_SETTINGS_CPOL BIT(2)
#define SPI_XCOMM_SETTINGS_CLOCK_DIV_MASK 0x3
#define SPI_XCOMM_SETTINGS_CLOCK_DIV_64 0x2
#define SPI_XCOMM_SETTINGS_CLOCK_DIV_16 0x1
#define SPI_XCOMM_SETTINGS_CLOCK_DIV_4 0x0
#define SPI_XCOMM_CMD_UPDATE_CONFIG 0x03
#define SPI_XCOMM_CMD_WRITE 0x04
#define SPI_XCOMM_CLOCK 48000000
struct spi_xcomm {
struct i2c_client *i2c;
uint16_t settings;
uint16_t chipselect;
unsigned int current_speed;
uint8_t buf[63];
};
static int spi_xcomm_sync_config(struct spi_xcomm *spi_xcomm, unsigned int len)
{
uint16_t settings;
uint8_t *buf = spi_xcomm->buf;
settings = spi_xcomm->settings;
settings |= len << SPI_XCOMM_SETTINGS_LEN_OFFSET;
buf[0] = SPI_XCOMM_CMD_UPDATE_CONFIG;
put_unaligned_be16(settings, &buf[1]);
put_unaligned_be16(spi_xcomm->chipselect, &buf[3]);
return i2c_master_send(spi_xcomm->i2c, buf, 5);
}
static void spi_xcomm_chipselect(struct spi_xcomm *spi_xcomm,
struct spi_device *spi, int is_active)
{
unsigned long cs = spi->chip_select;
uint16_t chipselect = spi_xcomm->chipselect;
if (is_active)
chipselect |= BIT(cs);
else
chipselect &= ~BIT(cs);
spi_xcomm->chipselect = chipselect;
}
static int spi_xcomm_setup_transfer(struct spi_xcomm *spi_xcomm,
struct spi_device *spi, struct spi_transfer *t, unsigned int *settings)
{
unsigned int speed;
if ((t->bits_per_word && t->bits_per_word != 8) || t->len > 62)
return -EINVAL;
speed = t->speed_hz ? t->speed_hz : spi->max_speed_hz;
if (speed != spi_xcomm->current_speed) {
unsigned int divider = DIV_ROUND_UP(SPI_XCOMM_CLOCK, speed);
if (divider >= 64)
*settings |= SPI_XCOMM_SETTINGS_CLOCK_DIV_64;
else if (divider >= 16)
*settings |= SPI_XCOMM_SETTINGS_CLOCK_DIV_16;
else
*settings |= SPI_XCOMM_SETTINGS_CLOCK_DIV_4;
spi_xcomm->current_speed = speed;
}
if (spi->mode & SPI_CPOL)
*settings |= SPI_XCOMM_SETTINGS_CPOL;
else
*settings &= ~SPI_XCOMM_SETTINGS_CPOL;
if (spi->mode & SPI_CPHA)
*settings &= ~SPI_XCOMM_SETTINGS_CPHA;
else
*settings |= SPI_XCOMM_SETTINGS_CPHA;
if (spi->mode & SPI_3WIRE)
*settings |= SPI_XCOMM_SETTINGS_3WIRE;
else
*settings &= ~SPI_XCOMM_SETTINGS_3WIRE;
return 0;
}
static int spi_xcomm_txrx_bufs(struct spi_xcomm *spi_xcomm,
struct spi_device *spi, struct spi_transfer *t)
{
int ret;
if (t->tx_buf) {
spi_xcomm->buf[0] = SPI_XCOMM_CMD_WRITE;
memcpy(spi_xcomm->buf + 1, t->tx_buf, t->len);
ret = i2c_master_send(spi_xcomm->i2c, spi_xcomm->buf, t->len + 1);
if (ret < 0)
return ret;
else if (ret != t->len + 1)
return -EIO;
} else if (t->rx_buf) {
ret = i2c_master_recv(spi_xcomm->i2c, t->rx_buf, t->len);
if (ret < 0)
return ret;
else if (ret != t->len)
return -EIO;
}
return t->len;
}
static int spi_xcomm_transfer_one(struct spi_master *master,
struct spi_message *msg)
{
struct spi_xcomm *spi_xcomm = spi_master_get_devdata(master);
unsigned int settings = spi_xcomm->settings;
struct spi_device *spi = msg->spi;
unsigned cs_change = 0;
struct spi_transfer *t;
bool is_first = true;
int status = 0;
bool is_last;
is_first = true;
spi_xcomm_chipselect(spi_xcomm, spi, true);
list_for_each_entry(t, &msg->transfers, transfer_list) {
if (!t->tx_buf && !t->rx_buf && t->len) {
status = -EINVAL;
break;
}
status = spi_xcomm_setup_transfer(spi_xcomm, spi, t, &settings);
if (status < 0)
break;
is_last = list_is_last(&t->transfer_list, &msg->transfers);
cs_change = t->cs_change;
if (cs_change ^ is_last)
settings |= BIT(5);
else
settings &= ~BIT(5);
if (t->rx_buf) {
spi_xcomm->settings = settings;
status = spi_xcomm_sync_config(spi_xcomm, t->len);
if (status < 0)
break;
} else if (settings != spi_xcomm->settings || is_first) {
spi_xcomm->settings = settings;
status = spi_xcomm_sync_config(spi_xcomm, 0);
if (status < 0)
break;
}
if (t->len) {
status = spi_xcomm_txrx_bufs(spi_xcomm, spi, t);
if (status < 0)
break;
if (status > 0)
msg->actual_length += status;
}
status = 0;
if (t->delay_usecs)
udelay(t->delay_usecs);
is_first = false;
}
if (status != 0 || !cs_change)
spi_xcomm_chipselect(spi_xcomm, spi, false);
msg->status = status;
spi_finalize_current_message(master);
return status;
}
static int spi_xcomm_setup(struct spi_device *spi)
{
if (spi->bits_per_word != 8)
return -EINVAL;
return 0;
}
static int __devinit spi_xcomm_probe(struct i2c_client *i2c,
const struct i2c_device_id *id)
{
struct spi_xcomm *spi_xcomm;
struct spi_master *master;
int ret;
master = spi_alloc_master(&i2c->dev, sizeof(*spi_xcomm));
if (!master)
return -ENOMEM;
spi_xcomm = spi_master_get_devdata(master);
spi_xcomm->i2c = i2c;
master->num_chipselect = 16;
master->mode_bits = SPI_CPHA | SPI_CPOL | SPI_3WIRE;
master->flags = SPI_MASTER_HALF_DUPLEX;
master->setup = spi_xcomm_setup;
master->transfer_one_message = spi_xcomm_transfer_one;
master->dev.of_node = i2c->dev.of_node;
i2c_set_clientdata(i2c, master);
ret = spi_register_master(master);
if (ret < 0)
spi_master_put(master);
return ret;
}
static int __devexit spi_xcomm_remove(struct i2c_client *i2c)
{
struct spi_master *master = i2c_get_clientdata(i2c);
spi_unregister_master(master);
return 0;
}
static const struct i2c_device_id spi_xcomm_ids[] = {
{ "spi-xcomm" },
{ },
};
static struct i2c_driver spi_xcomm_driver = {
.driver = {
.name = "spi-xcomm",
.owner = THIS_MODULE,
},
.id_table = spi_xcomm_ids,
.probe = spi_xcomm_probe,
.remove = __devexit_p(spi_xcomm_remove),
};
module_i2c_driver(spi_xcomm_driver);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("Lars-Peter Clausen <lars@metafoo.de>");
MODULE_DESCRIPTION("Analog Devices AD-FMCOMMS1-EBZ board I2C-SPI bridge driver");

View File

@ -53,7 +53,7 @@ modalias_show(struct device *dev, struct device_attribute *a, char *buf)
{ {
const struct spi_device *spi = to_spi_device(dev); const struct spi_device *spi = to_spi_device(dev);
return sprintf(buf, "%s\n", spi->modalias); return sprintf(buf, "%s%s\n", SPI_MODULE_PREFIX, spi->modalias);
} }
static struct device_attribute spi_dev_attrs[] = { static struct device_attribute spi_dev_attrs[] = {

View File

@ -231,6 +231,7 @@ enum ssp_chip_select {
struct dma_chan; struct dma_chan;
/** /**
* struct pl022_ssp_master - device.platform_data for SPI controller devices. * struct pl022_ssp_master - device.platform_data for SPI controller devices.
* @bus_id: identifier for this bus
* @num_chipselect: chipselects are used to distinguish individual * @num_chipselect: chipselects are used to distinguish individual
* SPI slaves, and are numbered from zero to num_chipselects - 1. * SPI slaves, and are numbered from zero to num_chipselects - 1.
* each slave has a chipselect signal, but it's common that not * each slave has a chipselect signal, but it's common that not
@ -259,19 +260,13 @@ struct pl022_ssp_controller {
* struct ssp_config_chip - spi_board_info.controller_data for SPI * struct ssp_config_chip - spi_board_info.controller_data for SPI
* slave devices, copied to spi_device.controller_data. * slave devices, copied to spi_device.controller_data.
* *
* @lbm: used for test purpose to internally connect RX and TX
* @iface: Interface type(Motorola, TI, Microwire, Universal) * @iface: Interface type(Motorola, TI, Microwire, Universal)
* @hierarchy: sets whether interface is master or slave * @hierarchy: sets whether interface is master or slave
* @slave_tx_disable: SSPTXD is disconnected (in slave mode only) * @slave_tx_disable: SSPTXD is disconnected (in slave mode only)
* @clk_freq: Tune freq parameters of SSP(when in master mode) * @clk_freq: Tune freq parameters of SSP(when in master mode)
* @endian_rx: Endianess of Data in Rx FIFO
* @endian_tx: Endianess of Data in Tx FIFO
* @data_size: Width of data element(4 to 32 bits)
* @com_mode: communication mode: polling, Interrupt or DMA * @com_mode: communication mode: polling, Interrupt or DMA
* @rx_lev_trig: Rx FIFO watermark level (for IT & DMA mode) * @rx_lev_trig: Rx FIFO watermark level (for IT & DMA mode)
* @tx_lev_trig: Tx FIFO watermark level (for IT & DMA mode) * @tx_lev_trig: Tx FIFO watermark level (for IT & DMA mode)
* @clk_phase: Motorola SPI interface Clock phase
* @clk_pol: Motorola SPI interface Clock polarity
* @ctrl_len: Microwire interface: Control length * @ctrl_len: Microwire interface: Control length
* @wait_state: Microwire interface: Wait state * @wait_state: Microwire interface: Wait state
* @duplex: Microwire interface: Full/Half duplex * @duplex: Microwire interface: Full/Half duplex
@ -279,8 +274,6 @@ struct pl022_ssp_controller {
* before sampling the incoming line * before sampling the incoming line
* @cs_control: function pointer to board-specific function to * @cs_control: function pointer to board-specific function to
* assert/deassert I/O port to control HW generation of devices chip-select. * assert/deassert I/O port to control HW generation of devices chip-select.
* @dma_xfer_type: Type of DMA xfer (Mem-to-periph or Periph-to-Periph)
* @dma_config: DMA configuration for SSP controller and peripheral
*/ */
struct pl022_config_chip { struct pl022_config_chip {
enum ssp_interface iface; enum ssp_interface iface;