forked from luck/tmp_suning_uos_patched
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:
commit
4297103560
19
Documentation/devicetree/bindings/spi/spi-orion.txt
Normal file
19
Documentation/devicetree/bindings/spi/spi-orion.txt
Normal 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";
|
||||
};
|
|
@ -357,7 +357,7 @@ config SPI_STMP3XXX
|
|||
|
||||
config SPI_TEGRA
|
||||
tristate "Nvidia Tegra SPI controller"
|
||||
depends on ARCH_TEGRA && TEGRA_SYSTEM_DMA
|
||||
depends on ARCH_TEGRA && (TEGRA_SYSTEM_DMA || TEGRA20_APB_DMA)
|
||||
help
|
||||
SPI driver for NVidia Tegra SoCs
|
||||
|
||||
|
@ -384,6 +384,13 @@ config SPI_TXX9
|
|||
help
|
||||
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
|
||||
tristate "Xilinx SPI controller common module"
|
||||
depends on HAS_IOMEM && EXPERIMENTAL
|
||||
|
|
|
@ -61,5 +61,6 @@ obj-$(CONFIG_SPI_TI_SSP) += spi-ti-ssp.o
|
|||
obj-$(CONFIG_SPI_TLE62X0) += spi-tle62x0.o
|
||||
obj-$(CONFIG_SPI_TOPCLIFF_PCH) += spi-topcliff-pch.o
|
||||
obj-$(CONFIG_SPI_TXX9) += spi-txx9.o
|
||||
obj-$(CONFIG_SPI_XCOMM) += spi-xcomm.o
|
||||
obj-$(CONFIG_SPI_XILINX) += spi-xilinx.o
|
||||
|
||||
|
|
|
@ -129,7 +129,7 @@ static void bcm63xx_spi_setup_transfer(struct spi_device *spi,
|
|||
|
||||
/* Find the closest clock configuration */
|
||||
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];
|
||||
break;
|
||||
}
|
||||
|
|
|
@ -235,7 +235,8 @@ static int spi_gpio_setup(struct spi_device *spi)
|
|||
status = gpio_request(cs, dev_name(&spi->dev));
|
||||
if (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)
|
||||
|
|
|
@ -626,7 +626,7 @@ static void spi_imx_chipselect(struct spi_device *spi, int is_active)
|
|||
int active = is_active != BITBANG_CS_INACTIVE;
|
||||
int dev_is_lowactive = !(spi->mode & SPI_CS_HIGH);
|
||||
|
||||
if (gpio < 0)
|
||||
if (!gpio_is_valid(gpio))
|
||||
return;
|
||||
|
||||
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;
|
||||
if (!config.bpw)
|
||||
config.bpw = spi->bits_per_word;
|
||||
if (!config.speed_hz)
|
||||
config.speed_hz = spi->max_speed_hz;
|
||||
|
||||
/* Initialize the functions for transfer */
|
||||
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__,
|
||||
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);
|
||||
|
||||
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++) {
|
||||
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];
|
||||
|
||||
spi_imx->chipselect[i] = cs_gpio;
|
||||
if (cs_gpio < 0)
|
||||
if (!gpio_is_valid(cs_gpio))
|
||||
continue;
|
||||
|
||||
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));
|
||||
out_gpio_free:
|
||||
while (--i >= 0) {
|
||||
if (spi_imx->chipselect[i] >= 0)
|
||||
if (gpio_is_valid(spi_imx->chipselect[i]))
|
||||
gpio_free(spi_imx->chipselect[i]);
|
||||
}
|
||||
spi_master_put(master);
|
||||
|
@ -922,7 +920,7 @@ static int __devexit spi_imx_remove(struct platform_device *pdev)
|
|||
iounmap(spi_imx->base);
|
||||
|
||||
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]);
|
||||
|
||||
spi_master_put(master);
|
||||
|
|
|
@ -388,7 +388,8 @@ omap2_mcspi_txrx_dma(struct spi_device *spi, struct spi_transfer *xfer)
|
|||
|
||||
if (tx != NULL) {
|
||||
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 */
|
||||
if (rx == NULL) {
|
||||
|
@ -403,7 +404,8 @@ omap2_mcspi_txrx_dma(struct spi_device *spi, struct spi_transfer *xfer)
|
|||
|
||||
if (rx != NULL) {
|
||||
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);
|
||||
|
||||
if (l & OMAP2_MCSPI_CHCONF_TURBO) {
|
||||
|
@ -1032,7 +1034,7 @@ static int omap2_mcspi_transfer_one_message(struct spi_master *master,
|
|||
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 omap2_mcspi_regs *ctx = &mcspi->ctx;
|
||||
|
|
|
@ -17,6 +17,7 @@
|
|||
#include <linux/io.h>
|
||||
#include <linux/spi/spi.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/clk.h>
|
||||
#include <asm/unaligned.h>
|
||||
|
||||
|
@ -45,7 +46,6 @@ struct orion_spi {
|
|||
void __iomem *base;
|
||||
unsigned int max_speed;
|
||||
unsigned int min_speed;
|
||||
struct orion_spi_info *spi_info;
|
||||
struct clk *clk;
|
||||
};
|
||||
|
||||
|
@ -450,11 +450,10 @@ static int __init orion_spi_probe(struct platform_device *pdev)
|
|||
struct spi_master *master;
|
||||
struct orion_spi *spi;
|
||||
struct resource *r;
|
||||
struct orion_spi_info *spi_info;
|
||||
unsigned long tclk_hz;
|
||||
int status = 0;
|
||||
|
||||
spi_info = pdev->dev.platform_data;
|
||||
const u32 *iprop;
|
||||
int size;
|
||||
|
||||
master = spi_alloc_master(&pdev->dev, sizeof *spi);
|
||||
if (master == NULL) {
|
||||
|
@ -464,6 +463,12 @@ static int __init orion_spi_probe(struct platform_device *pdev)
|
|||
|
||||
if (pdev->id != -1)
|
||||
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 */
|
||||
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->master = master;
|
||||
spi->spi_info = spi_info;
|
||||
|
||||
spi->clk = clk_get(&pdev->dev, NULL);
|
||||
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)
|
||||
goto out_rel_mem;
|
||||
|
||||
master->dev.of_node = pdev->dev.of_node;
|
||||
status = spi_register_master(master);
|
||||
if (status < 0)
|
||||
goto out_rel_mem;
|
||||
|
@ -552,10 +557,17 @@ static int __exit orion_spi_remove(struct platform_device *pdev)
|
|||
|
||||
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 = {
|
||||
.driver = {
|
||||
.name = DRIVER_NAME,
|
||||
.owner = THIS_MODULE,
|
||||
.of_match_table = of_match_ptr(orion_spi_of_match_table),
|
||||
},
|
||||
.remove = __exit_p(orion_spi_remove),
|
||||
};
|
||||
|
|
|
@ -489,6 +489,11 @@ static void giveback(struct pl022 *pl022)
|
|||
pl022->cur_transfer = NULL;
|
||||
pl022->cur_chip = NULL;
|
||||
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",
|
||||
adev->res.start, pl022->virtbase);
|
||||
|
||||
pm_runtime_enable(dev);
|
||||
pm_runtime_resume(dev);
|
||||
|
||||
pl022->clk = clk_get(&adev->dev, NULL);
|
||||
if (IS_ERR(pl022->clk)) {
|
||||
status = PTR_ERR(pl022->clk);
|
||||
|
@ -2158,6 +2166,7 @@ pl022_remove(struct amba_device *adev)
|
|||
clk_disable(pl022->clk);
|
||||
clk_unprepare(pl022->clk);
|
||||
clk_put(pl022->clk);
|
||||
pm_runtime_disable(&adev->dev);
|
||||
iounmap(pl022->virtbase);
|
||||
amba_release_regions(adev);
|
||||
tasklet_disable(&pl022->pump_transfers);
|
||||
|
@ -2251,15 +2260,6 @@ static struct vendor_data vendor_st_pl023 = {
|
|||
.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[] = {
|
||||
{
|
||||
/*
|
||||
|
@ -2291,11 +2291,6 @@ static struct amba_id pl022_ids[] = {
|
|||
.mask = 0xffffffff,
|
||||
.data = &vendor_st_pl023,
|
||||
},
|
||||
{
|
||||
.id = 0x10080023,
|
||||
.mask = 0xffffffff,
|
||||
.data = &vendor_db5500_pl023,
|
||||
},
|
||||
{ 0, 0 },
|
||||
};
|
||||
|
||||
|
|
|
@ -30,6 +30,7 @@
|
|||
#include <linux/delay.h>
|
||||
|
||||
#include <linux/spi/spi.h>
|
||||
#include <linux/dmaengine.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
|
||||
* for the generic case.
|
||||
*/
|
||||
int dma_req_len;
|
||||
#if defined(CONFIG_TEGRA_SYSTEM_DMA)
|
||||
struct tegra_dma_req rx_dma_req;
|
||||
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;
|
||||
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,
|
||||
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 &= ~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);
|
||||
|
||||
#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);
|
||||
#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;
|
||||
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);
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
@ -318,9 +344,8 @@ static void spi_tegra_start_message(struct spi_device *spi,
|
|||
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;
|
||||
struct spi_message *m;
|
||||
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);
|
||||
}
|
||||
#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)
|
||||
{
|
||||
|
@ -471,6 +509,9 @@ static int __devinit spi_tegra_probe(struct platform_device *pdev)
|
|||
struct spi_tegra_data *tspi;
|
||||
struct resource *r;
|
||||
int ret;
|
||||
#if !defined(CONFIG_TEGRA_SYSTEM_DMA)
|
||||
dma_cap_mask_t mask;
|
||||
#endif
|
||||
|
||||
master = spi_alloc_master(&pdev->dev, sizeof *tspi);
|
||||
if (master == NULL) {
|
||||
|
@ -522,12 +563,24 @@ static int __devinit spi_tegra_probe(struct platform_device *pdev)
|
|||
|
||||
INIT_LIST_HEAD(&tspi->queue);
|
||||
|
||||
#if defined(CONFIG_TEGRA_SYSTEM_DMA)
|
||||
tspi->rx_dma = tegra_dma_allocate_channel(TEGRA_DMA_MODE_ONESHOT);
|
||||
if (!tspi->rx_dma) {
|
||||
dev_err(&pdev->dev, "can not allocate rx dma channel\n");
|
||||
ret = -ENODEV;
|
||||
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_phys, GFP_KERNEL);
|
||||
|
@ -537,6 +590,7 @@ static int __devinit spi_tegra_probe(struct platform_device *pdev)
|
|||
goto err4;
|
||||
}
|
||||
|
||||
#if defined(CONFIG_TEGRA_SYSTEM_DMA)
|
||||
tspi->rx_dma_req.complete = tegra_spi_rx_dma_complete;
|
||||
tspi->rx_dma_req.to_memory = 1;
|
||||
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.req_sel = spi_tegra_req_sels[pdev->id];
|
||||
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;
|
||||
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,
|
||||
tspi->rx_bb, tspi->rx_bb_phys);
|
||||
err4:
|
||||
#if defined(CONFIG_TEGRA_SYSTEM_DMA)
|
||||
tegra_dma_free_channel(tspi->rx_dma);
|
||||
#else
|
||||
dma_release_channel(tspi->rx_dma);
|
||||
#endif
|
||||
err3:
|
||||
clk_put(tspi->clk);
|
||||
err2:
|
||||
|
@ -581,7 +656,11 @@ static int __devexit spi_tegra_remove(struct platform_device *pdev)
|
|||
tspi = spi_master_get_devdata(master);
|
||||
|
||||
spi_unregister_master(master);
|
||||
#if defined(CONFIG_TEGRA_SYSTEM_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,
|
||||
tspi->rx_bb, tspi->rx_bb_phys);
|
||||
|
|
276
drivers/spi/spi-xcomm.c
Normal file
276
drivers/spi/spi-xcomm.c
Normal 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");
|
|
@ -53,7 +53,7 @@ modalias_show(struct device *dev, struct device_attribute *a, char *buf)
|
|||
{
|
||||
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[] = {
|
||||
|
|
|
@ -231,6 +231,7 @@ enum ssp_chip_select {
|
|||
struct dma_chan;
|
||||
/**
|
||||
* 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
|
||||
* SPI slaves, and are numbered from zero to num_chipselects - 1.
|
||||
* 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
|
||||
* 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)
|
||||
* @hierarchy: sets whether interface is master or slave
|
||||
* @slave_tx_disable: SSPTXD is disconnected (in slave mode only)
|
||||
* @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
|
||||
* @rx_lev_trig: Rx 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
|
||||
* @wait_state: Microwire interface: Wait state
|
||||
* @duplex: Microwire interface: Full/Half duplex
|
||||
|
@ -279,8 +274,6 @@ struct pl022_ssp_controller {
|
|||
* before sampling the incoming line
|
||||
* @cs_control: function pointer to board-specific function to
|
||||
* 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 {
|
||||
enum ssp_interface iface;
|
||||
|
|
Loading…
Reference in New Issue
Block a user