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
|
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
|
||||||
|
|
|
@ -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
|
||||||
|
|
||||||
|
|
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
|
@ -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)
|
||||||
|
|
|
@ -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);
|
||||||
|
|
|
@ -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;
|
||||||
|
|
|
@ -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),
|
||||||
};
|
};
|
||||||
|
|
|
@ -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 },
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -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
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);
|
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[] = {
|
||||||
|
|
|
@ -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;
|
||||||
|
|
Loading…
Reference in New Issue
Block a user