Merge branch 'broonie/spi-next' of git://git.kernel.org/pub/scm/linux/kernel/git/broonie/misc.git
Minor features and bug fixes for PXA, OMAP and GPIO deivce drivers and a cosmetic change to the bitbang driver. Signed-off-by: Grant Likely <grant.likely@secretlab.ca>
This commit is contained in:
Коммит
f305a0a8d7
|
@ -7178,6 +7178,7 @@ F: drivers/clk/spear/
|
||||||
|
|
||||||
SPI SUBSYSTEM
|
SPI SUBSYSTEM
|
||||||
M: Grant Likely <grant.likely@secretlab.ca>
|
M: Grant Likely <grant.likely@secretlab.ca>
|
||||||
|
M: Mark Brown <broonie@opensource.wolfsonmicro.com>
|
||||||
L: spi-devel-general@lists.sourceforge.net
|
L: spi-devel-general@lists.sourceforge.net
|
||||||
Q: http://patchwork.kernel.org/project/spi-devel-general/list/
|
Q: http://patchwork.kernel.org/project/spi-devel-general/list/
|
||||||
T: git git://git.secretlab.ca/git/linux-2.6.git
|
T: git git://git.secretlab.ca/git/linux-2.6.git
|
||||||
|
|
|
@ -299,7 +299,7 @@ config SPI_PPC4xx
|
||||||
|
|
||||||
config SPI_PXA2XX
|
config SPI_PXA2XX
|
||||||
tristate "PXA2xx SSP SPI master"
|
tristate "PXA2xx SSP SPI master"
|
||||||
depends on (ARCH_PXA || (X86_32 && PCI)) && EXPERIMENTAL
|
depends on ARCH_PXA || PCI
|
||||||
select PXA_SSP if ARCH_PXA
|
select PXA_SSP if ARCH_PXA
|
||||||
help
|
help
|
||||||
This enables using a PXA2xx or Sodaville SSP port as a SPI master
|
This enables using a PXA2xx or Sodaville SSP port as a SPI master
|
||||||
|
@ -307,7 +307,7 @@ config SPI_PXA2XX
|
||||||
additional documentation can be found a Documentation/spi/pxa2xx.
|
additional documentation can be found a Documentation/spi/pxa2xx.
|
||||||
|
|
||||||
config SPI_PXA2XX_PCI
|
config SPI_PXA2XX_PCI
|
||||||
def_bool SPI_PXA2XX && X86_32 && PCI
|
def_tristate SPI_PXA2XX && PCI
|
||||||
|
|
||||||
config SPI_RSPI
|
config SPI_RSPI
|
||||||
tristate "Renesas RSPI controller"
|
tristate "Renesas RSPI controller"
|
||||||
|
|
|
@ -427,40 +427,41 @@ EXPORT_SYMBOL_GPL(spi_bitbang_transfer);
|
||||||
*/
|
*/
|
||||||
int spi_bitbang_start(struct spi_bitbang *bitbang)
|
int spi_bitbang_start(struct spi_bitbang *bitbang)
|
||||||
{
|
{
|
||||||
int status;
|
struct spi_master *master = bitbang->master;
|
||||||
|
int status;
|
||||||
|
|
||||||
if (!bitbang->master || !bitbang->chipselect)
|
if (!master || !bitbang->chipselect)
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
|
|
||||||
INIT_WORK(&bitbang->work, bitbang_work);
|
INIT_WORK(&bitbang->work, bitbang_work);
|
||||||
spin_lock_init(&bitbang->lock);
|
spin_lock_init(&bitbang->lock);
|
||||||
INIT_LIST_HEAD(&bitbang->queue);
|
INIT_LIST_HEAD(&bitbang->queue);
|
||||||
|
|
||||||
if (!bitbang->master->mode_bits)
|
if (!master->mode_bits)
|
||||||
bitbang->master->mode_bits = SPI_CPOL | SPI_CPHA | bitbang->flags;
|
master->mode_bits = SPI_CPOL | SPI_CPHA | bitbang->flags;
|
||||||
|
|
||||||
if (!bitbang->master->transfer)
|
if (!master->transfer)
|
||||||
bitbang->master->transfer = spi_bitbang_transfer;
|
master->transfer = spi_bitbang_transfer;
|
||||||
if (!bitbang->txrx_bufs) {
|
if (!bitbang->txrx_bufs) {
|
||||||
bitbang->use_dma = 0;
|
bitbang->use_dma = 0;
|
||||||
bitbang->txrx_bufs = spi_bitbang_bufs;
|
bitbang->txrx_bufs = spi_bitbang_bufs;
|
||||||
if (!bitbang->master->setup) {
|
if (!master->setup) {
|
||||||
if (!bitbang->setup_transfer)
|
if (!bitbang->setup_transfer)
|
||||||
bitbang->setup_transfer =
|
bitbang->setup_transfer =
|
||||||
spi_bitbang_setup_transfer;
|
spi_bitbang_setup_transfer;
|
||||||
bitbang->master->setup = spi_bitbang_setup;
|
master->setup = spi_bitbang_setup;
|
||||||
bitbang->master->cleanup = spi_bitbang_cleanup;
|
master->cleanup = spi_bitbang_cleanup;
|
||||||
}
|
}
|
||||||
} else if (!bitbang->master->setup)
|
} else if (!master->setup)
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
if (bitbang->master->transfer == spi_bitbang_transfer &&
|
if (master->transfer == spi_bitbang_transfer &&
|
||||||
!bitbang->setup_transfer)
|
!bitbang->setup_transfer)
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
|
|
||||||
/* this task is the only thing to touch the SPI bits */
|
/* this task is the only thing to touch the SPI bits */
|
||||||
bitbang->busy = 0;
|
bitbang->busy = 0;
|
||||||
bitbang->workqueue = create_singlethread_workqueue(
|
bitbang->workqueue = create_singlethread_workqueue(
|
||||||
dev_name(bitbang->master->dev.parent));
|
dev_name(master->dev.parent));
|
||||||
if (bitbang->workqueue == NULL) {
|
if (bitbang->workqueue == NULL) {
|
||||||
status = -EBUSY;
|
status = -EBUSY;
|
||||||
goto err1;
|
goto err1;
|
||||||
|
@ -469,7 +470,7 @@ int spi_bitbang_start(struct spi_bitbang *bitbang)
|
||||||
/* driver may get busy before register() returns, especially
|
/* driver may get busy before register() returns, especially
|
||||||
* if someone registered boardinfo for devices
|
* if someone registered boardinfo for devices
|
||||||
*/
|
*/
|
||||||
status = spi_register_master(bitbang->master);
|
status = spi_register_master(master);
|
||||||
if (status < 0)
|
if (status < 0)
|
||||||
goto err2;
|
goto err2;
|
||||||
|
|
||||||
|
|
|
@ -365,9 +365,26 @@ static int spi_gpio_probe_dt(struct platform_device *pdev)
|
||||||
if (!pdata)
|
if (!pdata)
|
||||||
return -ENOMEM;
|
return -ENOMEM;
|
||||||
|
|
||||||
pdata->sck = of_get_named_gpio(np, "gpio-sck", 0);
|
ret = of_get_named_gpio(np, "gpio-sck", 0);
|
||||||
pdata->miso = of_get_named_gpio(np, "gpio-miso", 0);
|
if (ret < 0) {
|
||||||
pdata->mosi = of_get_named_gpio(np, "gpio-mosi", 0);
|
dev_err(&pdev->dev, "gpio-sck property not found\n");
|
||||||
|
goto error_free;
|
||||||
|
}
|
||||||
|
pdata->sck = ret;
|
||||||
|
|
||||||
|
ret = of_get_named_gpio(np, "gpio-miso", 0);
|
||||||
|
if (ret < 0) {
|
||||||
|
dev_info(&pdev->dev, "gpio-miso property not found, switching to no-rx mode\n");
|
||||||
|
pdata->miso = SPI_GPIO_NO_MISO;
|
||||||
|
} else
|
||||||
|
pdata->miso = ret;
|
||||||
|
|
||||||
|
ret = of_get_named_gpio(np, "gpio-mosi", 0);
|
||||||
|
if (ret < 0) {
|
||||||
|
dev_info(&pdev->dev, "gpio-mosi property not found, switching to no-tx mode\n");
|
||||||
|
pdata->mosi = SPI_GPIO_NO_MOSI;
|
||||||
|
} else
|
||||||
|
pdata->mosi = ret;
|
||||||
|
|
||||||
ret = of_property_read_u32(np, "num-chipselects", &tmp);
|
ret = of_property_read_u32(np, "num-chipselects", &tmp);
|
||||||
if (ret < 0) {
|
if (ret < 0) {
|
||||||
|
|
|
@ -927,6 +927,7 @@ static void omap2_mcspi_work(struct omap2_mcspi *mcspi, struct spi_message *m)
|
||||||
|
|
||||||
struct spi_device *spi;
|
struct spi_device *spi;
|
||||||
struct spi_transfer *t = NULL;
|
struct spi_transfer *t = NULL;
|
||||||
|
struct spi_master *master;
|
||||||
int cs_active = 0;
|
int cs_active = 0;
|
||||||
struct omap2_mcspi_cs *cs;
|
struct omap2_mcspi_cs *cs;
|
||||||
struct omap2_mcspi_device_config *cd;
|
struct omap2_mcspi_device_config *cd;
|
||||||
|
@ -935,6 +936,7 @@ static void omap2_mcspi_work(struct omap2_mcspi *mcspi, struct spi_message *m)
|
||||||
u32 chconf;
|
u32 chconf;
|
||||||
|
|
||||||
spi = m->spi;
|
spi = m->spi;
|
||||||
|
master = spi->master;
|
||||||
cs = spi->controller_state;
|
cs = spi->controller_state;
|
||||||
cd = spi->controller_data;
|
cd = spi->controller_data;
|
||||||
|
|
||||||
|
@ -952,6 +954,14 @@ static void omap2_mcspi_work(struct omap2_mcspi *mcspi, struct spi_message *m)
|
||||||
if (!t->speed_hz && !t->bits_per_word)
|
if (!t->speed_hz && !t->bits_per_word)
|
||||||
par_override = 0;
|
par_override = 0;
|
||||||
}
|
}
|
||||||
|
if (cd && cd->cs_per_word) {
|
||||||
|
chconf = mcspi->ctx.modulctrl;
|
||||||
|
chconf &= ~OMAP2_MCSPI_MODULCTRL_SINGLE;
|
||||||
|
mcspi_write_reg(master, OMAP2_MCSPI_MODULCTRL, chconf);
|
||||||
|
mcspi->ctx.modulctrl =
|
||||||
|
mcspi_read_cs_reg(spi, OMAP2_MCSPI_MODULCTRL);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
if (!cs_active) {
|
if (!cs_active) {
|
||||||
omap2_mcspi_force_cs(spi, 1);
|
omap2_mcspi_force_cs(spi, 1);
|
||||||
|
@ -1013,6 +1023,14 @@ static void omap2_mcspi_work(struct omap2_mcspi *mcspi, struct spi_message *m)
|
||||||
if (cs_active)
|
if (cs_active)
|
||||||
omap2_mcspi_force_cs(spi, 0);
|
omap2_mcspi_force_cs(spi, 0);
|
||||||
|
|
||||||
|
if (cd && cd->cs_per_word) {
|
||||||
|
chconf = mcspi->ctx.modulctrl;
|
||||||
|
chconf |= OMAP2_MCSPI_MODULCTRL_SINGLE;
|
||||||
|
mcspi_write_reg(master, OMAP2_MCSPI_MODULCTRL, chconf);
|
||||||
|
mcspi->ctx.modulctrl =
|
||||||
|
mcspi_read_cs_reg(spi, OMAP2_MCSPI_MODULCTRL);
|
||||||
|
}
|
||||||
|
|
||||||
omap2_mcspi_set_enable(spi, 0);
|
omap2_mcspi_set_enable(spi, 0);
|
||||||
|
|
||||||
m->status = status;
|
m->status = status;
|
||||||
|
@ -1020,7 +1038,7 @@ static void omap2_mcspi_work(struct omap2_mcspi *mcspi, struct spi_message *m)
|
||||||
}
|
}
|
||||||
|
|
||||||
static int omap2_mcspi_transfer_one_message(struct spi_master *master,
|
static int omap2_mcspi_transfer_one_message(struct spi_master *master,
|
||||||
struct spi_message *m)
|
struct spi_message *m)
|
||||||
{
|
{
|
||||||
struct omap2_mcspi *mcspi;
|
struct omap2_mcspi *mcspi;
|
||||||
struct spi_transfer *t;
|
struct spi_transfer *t;
|
||||||
|
@ -1041,7 +1059,7 @@ static int omap2_mcspi_transfer_one_message(struct spi_master *master,
|
||||||
|| (len && !(rx_buf || tx_buf))
|
|| (len && !(rx_buf || tx_buf))
|
||||||
|| (t->bits_per_word &&
|
|| (t->bits_per_word &&
|
||||||
( t->bits_per_word < 4
|
( t->bits_per_word < 4
|
||||||
|| t->bits_per_word > 32))) {
|
|| t->bits_per_word > 32))) {
|
||||||
dev_dbg(mcspi->dev, "transfer: %d Hz, %d %s%s, %d bpw\n",
|
dev_dbg(mcspi->dev, "transfer: %d Hz, %d %s%s, %d bpw\n",
|
||||||
t->speed_hz,
|
t->speed_hz,
|
||||||
len,
|
len,
|
||||||
|
@ -1052,8 +1070,8 @@ static int omap2_mcspi_transfer_one_message(struct spi_master *master,
|
||||||
}
|
}
|
||||||
if (t->speed_hz && t->speed_hz < (OMAP2_MCSPI_MAX_FREQ >> 15)) {
|
if (t->speed_hz && t->speed_hz < (OMAP2_MCSPI_MAX_FREQ >> 15)) {
|
||||||
dev_dbg(mcspi->dev, "speed_hz %d below minimum %d Hz\n",
|
dev_dbg(mcspi->dev, "speed_hz %d below minimum %d Hz\n",
|
||||||
t->speed_hz,
|
t->speed_hz,
|
||||||
OMAP2_MCSPI_MAX_FREQ >> 15);
|
OMAP2_MCSPI_MAX_FREQ >> 15);
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1099,7 +1117,7 @@ static int omap2_mcspi_master_setup(struct omap2_mcspi *mcspi)
|
||||||
return ret;
|
return ret;
|
||||||
|
|
||||||
mcspi_write_reg(master, OMAP2_MCSPI_WAKEUPENABLE,
|
mcspi_write_reg(master, OMAP2_MCSPI_WAKEUPENABLE,
|
||||||
OMAP2_MCSPI_WAKEUPENABLE_WKEN);
|
OMAP2_MCSPI_WAKEUPENABLE_WKEN);
|
||||||
ctx->wakeupenable = OMAP2_MCSPI_WAKEUPENABLE_WKEN;
|
ctx->wakeupenable = OMAP2_MCSPI_WAKEUPENABLE_WKEN;
|
||||||
|
|
||||||
omap2_mcspi_set_master_mode(master);
|
omap2_mcspi_set_master_mode(master);
|
||||||
|
@ -1228,7 +1246,7 @@ static int omap2_mcspi_probe(struct platform_device *pdev)
|
||||||
|
|
||||||
sprintf(dma_ch_name, "rx%d", i);
|
sprintf(dma_ch_name, "rx%d", i);
|
||||||
dma_res = platform_get_resource_byname(pdev, IORESOURCE_DMA,
|
dma_res = platform_get_resource_byname(pdev, IORESOURCE_DMA,
|
||||||
dma_ch_name);
|
dma_ch_name);
|
||||||
if (!dma_res) {
|
if (!dma_res) {
|
||||||
dev_dbg(&pdev->dev, "cannot get DMA RX channel\n");
|
dev_dbg(&pdev->dev, "cannot get DMA RX channel\n");
|
||||||
status = -ENODEV;
|
status = -ENODEV;
|
||||||
|
@ -1238,7 +1256,7 @@ static int omap2_mcspi_probe(struct platform_device *pdev)
|
||||||
mcspi->dma_channels[i].dma_rx_sync_dev = dma_res->start;
|
mcspi->dma_channels[i].dma_rx_sync_dev = dma_res->start;
|
||||||
sprintf(dma_ch_name, "tx%d", i);
|
sprintf(dma_ch_name, "tx%d", i);
|
||||||
dma_res = platform_get_resource_byname(pdev, IORESOURCE_DMA,
|
dma_res = platform_get_resource_byname(pdev, IORESOURCE_DMA,
|
||||||
dma_ch_name);
|
dma_ch_name);
|
||||||
if (!dma_res) {
|
if (!dma_res) {
|
||||||
dev_dbg(&pdev->dev, "cannot get DMA TX channel\n");
|
dev_dbg(&pdev->dev, "cannot get DMA TX channel\n");
|
||||||
status = -ENODEV;
|
status = -ENODEV;
|
||||||
|
@ -1254,7 +1272,7 @@ static int omap2_mcspi_probe(struct platform_device *pdev)
|
||||||
pinctrl = devm_pinctrl_get_select_default(&pdev->dev);
|
pinctrl = devm_pinctrl_get_select_default(&pdev->dev);
|
||||||
if (IS_ERR(pinctrl))
|
if (IS_ERR(pinctrl))
|
||||||
dev_warn(&pdev->dev,
|
dev_warn(&pdev->dev,
|
||||||
"pins are not configured from the driver\n");
|
"pins are not configured from the driver\n");
|
||||||
|
|
||||||
pm_runtime_use_autosuspend(&pdev->dev);
|
pm_runtime_use_autosuspend(&pdev->dev);
|
||||||
pm_runtime_set_autosuspend_delay(&pdev->dev, SPI_AUTOSUSPEND_TIMEOUT);
|
pm_runtime_set_autosuspend_delay(&pdev->dev, SPI_AUTOSUSPEND_TIMEOUT);
|
||||||
|
|
|
@ -8,147 +8,58 @@
|
||||||
#include <linux/module.h>
|
#include <linux/module.h>
|
||||||
#include <linux/spi/pxa2xx_spi.h>
|
#include <linux/spi/pxa2xx_spi.h>
|
||||||
|
|
||||||
struct ce4100_info {
|
|
||||||
struct ssp_device ssp;
|
|
||||||
struct platform_device *spi_pdev;
|
|
||||||
};
|
|
||||||
|
|
||||||
static DEFINE_MUTEX(ssp_lock);
|
|
||||||
static LIST_HEAD(ssp_list);
|
|
||||||
|
|
||||||
struct ssp_device *pxa_ssp_request(int port, const char *label)
|
|
||||||
{
|
|
||||||
struct ssp_device *ssp = NULL;
|
|
||||||
|
|
||||||
mutex_lock(&ssp_lock);
|
|
||||||
|
|
||||||
list_for_each_entry(ssp, &ssp_list, node) {
|
|
||||||
if (ssp->port_id == port && ssp->use_count == 0) {
|
|
||||||
ssp->use_count++;
|
|
||||||
ssp->label = label;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
mutex_unlock(&ssp_lock);
|
|
||||||
|
|
||||||
if (&ssp->node == &ssp_list)
|
|
||||||
return NULL;
|
|
||||||
|
|
||||||
return ssp;
|
|
||||||
}
|
|
||||||
EXPORT_SYMBOL_GPL(pxa_ssp_request);
|
|
||||||
|
|
||||||
void pxa_ssp_free(struct ssp_device *ssp)
|
|
||||||
{
|
|
||||||
mutex_lock(&ssp_lock);
|
|
||||||
if (ssp->use_count) {
|
|
||||||
ssp->use_count--;
|
|
||||||
ssp->label = NULL;
|
|
||||||
} else
|
|
||||||
dev_err(&ssp->pdev->dev, "device already free\n");
|
|
||||||
mutex_unlock(&ssp_lock);
|
|
||||||
}
|
|
||||||
EXPORT_SYMBOL_GPL(pxa_ssp_free);
|
|
||||||
|
|
||||||
static int ce4100_spi_probe(struct pci_dev *dev,
|
static int ce4100_spi_probe(struct pci_dev *dev,
|
||||||
const struct pci_device_id *ent)
|
const struct pci_device_id *ent)
|
||||||
{
|
{
|
||||||
|
struct platform_device_info pi;
|
||||||
int ret;
|
int ret;
|
||||||
resource_size_t phys_beg;
|
|
||||||
resource_size_t phys_len;
|
|
||||||
struct ce4100_info *spi_info;
|
|
||||||
struct platform_device *pdev;
|
struct platform_device *pdev;
|
||||||
struct pxa2xx_spi_master spi_pdata;
|
struct pxa2xx_spi_master spi_pdata;
|
||||||
struct ssp_device *ssp;
|
struct ssp_device *ssp;
|
||||||
|
|
||||||
ret = pci_enable_device(dev);
|
ret = pcim_enable_device(dev);
|
||||||
if (ret)
|
if (ret)
|
||||||
return ret;
|
return ret;
|
||||||
|
|
||||||
phys_beg = pci_resource_start(dev, 0);
|
ret = pcim_iomap_regions(dev, 1 << 0, "PXA2xx SPI");
|
||||||
phys_len = pci_resource_len(dev, 0);
|
if (!ret)
|
||||||
|
|
||||||
if (!request_mem_region(phys_beg, phys_len,
|
|
||||||
"CE4100 SPI")) {
|
|
||||||
dev_err(&dev->dev, "Can't request register space.\n");
|
|
||||||
ret = -EBUSY;
|
|
||||||
return ret;
|
return ret;
|
||||||
}
|
|
||||||
|
|
||||||
pdev = platform_device_alloc("pxa2xx-spi", dev->devfn);
|
|
||||||
spi_info = kzalloc(sizeof(*spi_info), GFP_KERNEL);
|
|
||||||
if (!pdev || !spi_info ) {
|
|
||||||
ret = -ENOMEM;
|
|
||||||
goto err_nomem;
|
|
||||||
}
|
|
||||||
memset(&spi_pdata, 0, sizeof(spi_pdata));
|
memset(&spi_pdata, 0, sizeof(spi_pdata));
|
||||||
spi_pdata.num_chipselect = dev->devfn;
|
spi_pdata.num_chipselect = dev->devfn;
|
||||||
|
|
||||||
ret = platform_device_add_data(pdev, &spi_pdata, sizeof(spi_pdata));
|
ssp = &spi_pdata.ssp;
|
||||||
if (ret)
|
|
||||||
goto err_nomem;
|
|
||||||
|
|
||||||
pdev->dev.parent = &dev->dev;
|
|
||||||
pdev->dev.of_node = dev->dev.of_node;
|
|
||||||
ssp = &spi_info->ssp;
|
|
||||||
ssp->phys_base = pci_resource_start(dev, 0);
|
ssp->phys_base = pci_resource_start(dev, 0);
|
||||||
ssp->mmio_base = ioremap(phys_beg, phys_len);
|
ssp->mmio_base = pcim_iomap_table(dev)[0];
|
||||||
if (!ssp->mmio_base) {
|
if (!ssp->mmio_base) {
|
||||||
dev_err(&pdev->dev, "failed to ioremap() registers\n");
|
dev_err(&dev->dev, "failed to ioremap() registers\n");
|
||||||
ret = -EIO;
|
return -EIO;
|
||||||
goto err_nomem;
|
|
||||||
}
|
}
|
||||||
ssp->irq = dev->irq;
|
ssp->irq = dev->irq;
|
||||||
ssp->port_id = pdev->id;
|
ssp->port_id = dev->devfn;
|
||||||
ssp->type = PXA25x_SSP;
|
ssp->type = PXA25x_SSP;
|
||||||
|
|
||||||
mutex_lock(&ssp_lock);
|
memset(&pi, 0, sizeof(pi));
|
||||||
list_add(&ssp->node, &ssp_list);
|
pi.parent = &dev->dev;
|
||||||
mutex_unlock(&ssp_lock);
|
pi.name = "pxa2xx-spi";
|
||||||
|
pi.id = ssp->port_id;
|
||||||
|
pi.data = &spi_pdata;
|
||||||
|
pi.size_data = sizeof(spi_pdata);
|
||||||
|
|
||||||
pci_set_drvdata(dev, spi_info);
|
pdev = platform_device_register_full(&pi);
|
||||||
|
if (!pdev)
|
||||||
|
return -ENOMEM;
|
||||||
|
|
||||||
ret = platform_device_add(pdev);
|
pci_set_drvdata(dev, pdev);
|
||||||
if (ret)
|
|
||||||
goto err_dev_add;
|
|
||||||
|
|
||||||
return ret;
|
return 0;
|
||||||
|
|
||||||
err_dev_add:
|
|
||||||
pci_set_drvdata(dev, NULL);
|
|
||||||
mutex_lock(&ssp_lock);
|
|
||||||
list_del(&ssp->node);
|
|
||||||
mutex_unlock(&ssp_lock);
|
|
||||||
iounmap(ssp->mmio_base);
|
|
||||||
|
|
||||||
err_nomem:
|
|
||||||
release_mem_region(phys_beg, phys_len);
|
|
||||||
platform_device_put(pdev);
|
|
||||||
kfree(spi_info);
|
|
||||||
return ret;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static void ce4100_spi_remove(struct pci_dev *dev)
|
static void ce4100_spi_remove(struct pci_dev *dev)
|
||||||
{
|
{
|
||||||
struct ce4100_info *spi_info;
|
struct platform_device *pdev = pci_get_drvdata(dev);
|
||||||
struct ssp_device *ssp;
|
|
||||||
|
|
||||||
spi_info = pci_get_drvdata(dev);
|
platform_device_unregister(pdev);
|
||||||
ssp = &spi_info->ssp;
|
|
||||||
platform_device_unregister(spi_info->spi_pdev);
|
|
||||||
|
|
||||||
iounmap(ssp->mmio_base);
|
|
||||||
release_mem_region(pci_resource_start(dev, 0),
|
|
||||||
pci_resource_len(dev, 0));
|
|
||||||
|
|
||||||
mutex_lock(&ssp_lock);
|
|
||||||
list_del(&ssp->node);
|
|
||||||
mutex_unlock(&ssp_lock);
|
|
||||||
|
|
||||||
pci_set_drvdata(dev, NULL);
|
|
||||||
pci_disable_device(dev);
|
|
||||||
kfree(spi_info);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static DEFINE_PCI_DEVICE_TABLE(ce4100_spi_devices) = {
|
static DEFINE_PCI_DEVICE_TABLE(ce4100_spi_devices) = {
|
||||||
|
|
|
@ -30,6 +30,7 @@
|
||||||
#include <linux/delay.h>
|
#include <linux/delay.h>
|
||||||
#include <linux/gpio.h>
|
#include <linux/gpio.h>
|
||||||
#include <linux/slab.h>
|
#include <linux/slab.h>
|
||||||
|
#include <linux/clk.h>
|
||||||
|
|
||||||
#include <asm/io.h>
|
#include <asm/io.h>
|
||||||
#include <asm/irq.h>
|
#include <asm/irq.h>
|
||||||
|
@ -47,7 +48,7 @@ MODULE_ALIAS("platform:pxa2xx-spi");
|
||||||
|
|
||||||
#define DMA_INT_MASK (DCSR_ENDINTR | DCSR_STARTINTR | DCSR_BUSERR)
|
#define DMA_INT_MASK (DCSR_ENDINTR | DCSR_STARTINTR | DCSR_BUSERR)
|
||||||
#define RESET_DMA_CHANNEL (DCSR_NODESC | DMA_INT_MASK)
|
#define RESET_DMA_CHANNEL (DCSR_NODESC | DMA_INT_MASK)
|
||||||
#define IS_DMA_ALIGNED(x) ((((u32)(x)) & 0x07) == 0)
|
#define IS_DMA_ALIGNED(x) IS_ALIGNED((unsigned long)(x), DMA_ALIGNMENT)
|
||||||
#define MAX_DMA_LEN 8191
|
#define MAX_DMA_LEN 8191
|
||||||
#define DMA_ALIGNMENT 8
|
#define DMA_ALIGNMENT 8
|
||||||
|
|
||||||
|
@ -85,9 +86,6 @@ DEFINE_SSP_REG(SSPSP, 0x2c)
|
||||||
#define DONE_STATE ((void*)2)
|
#define DONE_STATE ((void*)2)
|
||||||
#define ERROR_STATE ((void*)-1)
|
#define ERROR_STATE ((void*)-1)
|
||||||
|
|
||||||
#define QUEUE_RUNNING 0
|
|
||||||
#define QUEUE_STOPPED 1
|
|
||||||
|
|
||||||
struct driver_data {
|
struct driver_data {
|
||||||
/* Driver model hookup */
|
/* Driver model hookup */
|
||||||
struct platform_device *pdev;
|
struct platform_device *pdev;
|
||||||
|
@ -117,13 +115,8 @@ struct driver_data {
|
||||||
u32 clear_sr;
|
u32 clear_sr;
|
||||||
u32 mask_sr;
|
u32 mask_sr;
|
||||||
|
|
||||||
/* Driver message queue */
|
/* Maximun clock rate */
|
||||||
struct workqueue_struct *workqueue;
|
unsigned long max_clk_rate;
|
||||||
struct work_struct pump_messages;
|
|
||||||
spinlock_t lock;
|
|
||||||
struct list_head queue;
|
|
||||||
int busy;
|
|
||||||
int run;
|
|
||||||
|
|
||||||
/* Message Transfer pump */
|
/* Message Transfer pump */
|
||||||
struct tasklet_struct pump_transfers;
|
struct tasklet_struct pump_transfers;
|
||||||
|
@ -173,8 +166,6 @@ struct chip_data {
|
||||||
void (*cs_control)(u32 command);
|
void (*cs_control)(u32 command);
|
||||||
};
|
};
|
||||||
|
|
||||||
static void pump_messages(struct work_struct *work);
|
|
||||||
|
|
||||||
static void cs_assert(struct driver_data *drv_data)
|
static void cs_assert(struct driver_data *drv_data)
|
||||||
{
|
{
|
||||||
struct chip_data *chip = drv_data->cur_chip;
|
struct chip_data *chip = drv_data->cur_chip;
|
||||||
|
@ -444,15 +435,11 @@ static void unmap_dma_buffers(struct driver_data *drv_data)
|
||||||
static void giveback(struct driver_data *drv_data)
|
static void giveback(struct driver_data *drv_data)
|
||||||
{
|
{
|
||||||
struct spi_transfer* last_transfer;
|
struct spi_transfer* last_transfer;
|
||||||
unsigned long flags;
|
|
||||||
struct spi_message *msg;
|
struct spi_message *msg;
|
||||||
|
|
||||||
spin_lock_irqsave(&drv_data->lock, flags);
|
|
||||||
msg = drv_data->cur_msg;
|
msg = drv_data->cur_msg;
|
||||||
drv_data->cur_msg = NULL;
|
drv_data->cur_msg = NULL;
|
||||||
drv_data->cur_transfer = NULL;
|
drv_data->cur_transfer = NULL;
|
||||||
queue_work(drv_data->workqueue, &drv_data->pump_messages);
|
|
||||||
spin_unlock_irqrestore(&drv_data->lock, flags);
|
|
||||||
|
|
||||||
last_transfer = list_entry(msg->transfers.prev,
|
last_transfer = list_entry(msg->transfers.prev,
|
||||||
struct spi_transfer,
|
struct spi_transfer,
|
||||||
|
@ -481,13 +468,7 @@ static void giveback(struct driver_data *drv_data)
|
||||||
*/
|
*/
|
||||||
|
|
||||||
/* get a pointer to the next message, if any */
|
/* get a pointer to the next message, if any */
|
||||||
spin_lock_irqsave(&drv_data->lock, flags);
|
next_msg = spi_get_next_queued_message(drv_data->master);
|
||||||
if (list_empty(&drv_data->queue))
|
|
||||||
next_msg = NULL;
|
|
||||||
else
|
|
||||||
next_msg = list_entry(drv_data->queue.next,
|
|
||||||
struct spi_message, queue);
|
|
||||||
spin_unlock_irqrestore(&drv_data->lock, flags);
|
|
||||||
|
|
||||||
/* see if the next and current messages point
|
/* see if the next and current messages point
|
||||||
* to the same chip
|
* to the same chip
|
||||||
|
@ -498,10 +479,7 @@ static void giveback(struct driver_data *drv_data)
|
||||||
cs_deassert(drv_data);
|
cs_deassert(drv_data);
|
||||||
}
|
}
|
||||||
|
|
||||||
msg->state = NULL;
|
spi_finalize_current_message(drv_data->master);
|
||||||
if (msg->complete)
|
|
||||||
msg->complete(msg->context);
|
|
||||||
|
|
||||||
drv_data->cur_chip = NULL;
|
drv_data->cur_chip = NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -917,9 +895,12 @@ static int set_dma_burst_and_threshold(struct chip_data *chip,
|
||||||
return retval;
|
return retval;
|
||||||
}
|
}
|
||||||
|
|
||||||
static unsigned int ssp_get_clk_div(struct ssp_device *ssp, int rate)
|
static unsigned int ssp_get_clk_div(struct driver_data *drv_data, int rate)
|
||||||
{
|
{
|
||||||
unsigned long ssp_clk = clk_get_rate(ssp->clk);
|
unsigned long ssp_clk = drv_data->max_clk_rate;
|
||||||
|
const struct ssp_device *ssp = drv_data->ssp;
|
||||||
|
|
||||||
|
rate = min_t(int, ssp_clk, rate);
|
||||||
|
|
||||||
if (ssp->type == PXA25x_SSP || ssp->type == CE4100_SSP)
|
if (ssp->type == PXA25x_SSP || ssp->type == CE4100_SSP)
|
||||||
return ((ssp_clk / (2 * rate) - 1) & 0xff) << 8;
|
return ((ssp_clk / (2 * rate) - 1) & 0xff) << 8;
|
||||||
|
@ -934,7 +915,6 @@ static void pump_transfers(unsigned long data)
|
||||||
struct spi_transfer *transfer = NULL;
|
struct spi_transfer *transfer = NULL;
|
||||||
struct spi_transfer *previous = NULL;
|
struct spi_transfer *previous = NULL;
|
||||||
struct chip_data *chip = NULL;
|
struct chip_data *chip = NULL;
|
||||||
struct ssp_device *ssp = drv_data->ssp;
|
|
||||||
void __iomem *reg = drv_data->ioaddr;
|
void __iomem *reg = drv_data->ioaddr;
|
||||||
u32 clk_div = 0;
|
u32 clk_div = 0;
|
||||||
u8 bits = 0;
|
u8 bits = 0;
|
||||||
|
@ -1031,7 +1011,7 @@ static void pump_transfers(unsigned long data)
|
||||||
if (transfer->bits_per_word)
|
if (transfer->bits_per_word)
|
||||||
bits = transfer->bits_per_word;
|
bits = transfer->bits_per_word;
|
||||||
|
|
||||||
clk_div = ssp_get_clk_div(ssp, speed);
|
clk_div = ssp_get_clk_div(drv_data, speed);
|
||||||
|
|
||||||
if (bits <= 8) {
|
if (bits <= 8) {
|
||||||
drv_data->n_bytes = 1;
|
drv_data->n_bytes = 1;
|
||||||
|
@ -1176,31 +1156,12 @@ static void pump_transfers(unsigned long data)
|
||||||
write_SSCR1(cr1, reg);
|
write_SSCR1(cr1, reg);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void pump_messages(struct work_struct *work)
|
static int pxa2xx_spi_transfer_one_message(struct spi_master *master,
|
||||||
|
struct spi_message *msg)
|
||||||
{
|
{
|
||||||
struct driver_data *drv_data =
|
struct driver_data *drv_data = spi_master_get_devdata(master);
|
||||||
container_of(work, struct driver_data, pump_messages);
|
|
||||||
unsigned long flags;
|
|
||||||
|
|
||||||
/* Lock queue and check for queue work */
|
|
||||||
spin_lock_irqsave(&drv_data->lock, flags);
|
|
||||||
if (list_empty(&drv_data->queue) || drv_data->run == QUEUE_STOPPED) {
|
|
||||||
drv_data->busy = 0;
|
|
||||||
spin_unlock_irqrestore(&drv_data->lock, flags);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Make sure we are not already running a message */
|
|
||||||
if (drv_data->cur_msg) {
|
|
||||||
spin_unlock_irqrestore(&drv_data->lock, flags);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Extract head of queue */
|
|
||||||
drv_data->cur_msg = list_entry(drv_data->queue.next,
|
|
||||||
struct spi_message, queue);
|
|
||||||
list_del_init(&drv_data->cur_msg->queue);
|
|
||||||
|
|
||||||
|
drv_data->cur_msg = msg;
|
||||||
/* Initial message state*/
|
/* Initial message state*/
|
||||||
drv_data->cur_msg->state = START_STATE;
|
drv_data->cur_msg->state = START_STATE;
|
||||||
drv_data->cur_transfer = list_entry(drv_data->cur_msg->transfers.next,
|
drv_data->cur_transfer = list_entry(drv_data->cur_msg->transfers.next,
|
||||||
|
@ -1213,34 +1174,6 @@ static void pump_messages(struct work_struct *work)
|
||||||
|
|
||||||
/* Mark as busy and launch transfers */
|
/* Mark as busy and launch transfers */
|
||||||
tasklet_schedule(&drv_data->pump_transfers);
|
tasklet_schedule(&drv_data->pump_transfers);
|
||||||
|
|
||||||
drv_data->busy = 1;
|
|
||||||
spin_unlock_irqrestore(&drv_data->lock, flags);
|
|
||||||
}
|
|
||||||
|
|
||||||
static int transfer(struct spi_device *spi, struct spi_message *msg)
|
|
||||||
{
|
|
||||||
struct driver_data *drv_data = spi_master_get_devdata(spi->master);
|
|
||||||
unsigned long flags;
|
|
||||||
|
|
||||||
spin_lock_irqsave(&drv_data->lock, flags);
|
|
||||||
|
|
||||||
if (drv_data->run == QUEUE_STOPPED) {
|
|
||||||
spin_unlock_irqrestore(&drv_data->lock, flags);
|
|
||||||
return -ESHUTDOWN;
|
|
||||||
}
|
|
||||||
|
|
||||||
msg->actual_length = 0;
|
|
||||||
msg->status = -EINPROGRESS;
|
|
||||||
msg->state = START_STATE;
|
|
||||||
|
|
||||||
list_add_tail(&msg->queue, &drv_data->queue);
|
|
||||||
|
|
||||||
if (drv_data->run == QUEUE_RUNNING && !drv_data->busy)
|
|
||||||
queue_work(drv_data->workqueue, &drv_data->pump_messages);
|
|
||||||
|
|
||||||
spin_unlock_irqrestore(&drv_data->lock, flags);
|
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1287,7 +1220,6 @@ static int setup(struct spi_device *spi)
|
||||||
struct pxa2xx_spi_chip *chip_info = NULL;
|
struct pxa2xx_spi_chip *chip_info = NULL;
|
||||||
struct chip_data *chip;
|
struct chip_data *chip;
|
||||||
struct driver_data *drv_data = spi_master_get_devdata(spi->master);
|
struct driver_data *drv_data = spi_master_get_devdata(spi->master);
|
||||||
struct ssp_device *ssp = drv_data->ssp;
|
|
||||||
unsigned int clk_div;
|
unsigned int clk_div;
|
||||||
uint tx_thres = TX_THRESH_DFLT;
|
uint tx_thres = TX_THRESH_DFLT;
|
||||||
uint rx_thres = RX_THRESH_DFLT;
|
uint rx_thres = RX_THRESH_DFLT;
|
||||||
|
@ -1369,7 +1301,7 @@ static int setup(struct spi_device *spi)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
clk_div = ssp_get_clk_div(ssp, spi->max_speed_hz);
|
clk_div = ssp_get_clk_div(drv_data, spi->max_speed_hz);
|
||||||
chip->speed_hz = spi->max_speed_hz;
|
chip->speed_hz = spi->max_speed_hz;
|
||||||
|
|
||||||
chip->cr0 = clk_div
|
chip->cr0 = clk_div
|
||||||
|
@ -1385,12 +1317,12 @@ static int setup(struct spi_device *spi)
|
||||||
/* NOTE: PXA25x_SSP _could_ use external clocking ... */
|
/* NOTE: PXA25x_SSP _could_ use external clocking ... */
|
||||||
if (!pxa25x_ssp_comp(drv_data))
|
if (!pxa25x_ssp_comp(drv_data))
|
||||||
dev_dbg(&spi->dev, "%ld Hz actual, %s\n",
|
dev_dbg(&spi->dev, "%ld Hz actual, %s\n",
|
||||||
clk_get_rate(ssp->clk)
|
drv_data->max_clk_rate
|
||||||
/ (1 + ((chip->cr0 & SSCR0_SCR(0xfff)) >> 8)),
|
/ (1 + ((chip->cr0 & SSCR0_SCR(0xfff)) >> 8)),
|
||||||
chip->enable_dma ? "DMA" : "PIO");
|
chip->enable_dma ? "DMA" : "PIO");
|
||||||
else
|
else
|
||||||
dev_dbg(&spi->dev, "%ld Hz actual, %s\n",
|
dev_dbg(&spi->dev, "%ld Hz actual, %s\n",
|
||||||
clk_get_rate(ssp->clk) / 2
|
drv_data->max_clk_rate / 2
|
||||||
/ (1 + ((chip->cr0 & SSCR0_SCR(0x0ff)) >> 8)),
|
/ (1 + ((chip->cr0 & SSCR0_SCR(0x0ff)) >> 8)),
|
||||||
chip->enable_dma ? "DMA" : "PIO");
|
chip->enable_dma ? "DMA" : "PIO");
|
||||||
|
|
||||||
|
@ -1438,94 +1370,6 @@ static void cleanup(struct spi_device *spi)
|
||||||
kfree(chip);
|
kfree(chip);
|
||||||
}
|
}
|
||||||
|
|
||||||
static int init_queue(struct driver_data *drv_data)
|
|
||||||
{
|
|
||||||
INIT_LIST_HEAD(&drv_data->queue);
|
|
||||||
spin_lock_init(&drv_data->lock);
|
|
||||||
|
|
||||||
drv_data->run = QUEUE_STOPPED;
|
|
||||||
drv_data->busy = 0;
|
|
||||||
|
|
||||||
tasklet_init(&drv_data->pump_transfers,
|
|
||||||
pump_transfers, (unsigned long)drv_data);
|
|
||||||
|
|
||||||
INIT_WORK(&drv_data->pump_messages, pump_messages);
|
|
||||||
drv_data->workqueue = create_singlethread_workqueue(
|
|
||||||
dev_name(drv_data->master->dev.parent));
|
|
||||||
if (drv_data->workqueue == NULL)
|
|
||||||
return -EBUSY;
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int start_queue(struct driver_data *drv_data)
|
|
||||||
{
|
|
||||||
unsigned long flags;
|
|
||||||
|
|
||||||
spin_lock_irqsave(&drv_data->lock, flags);
|
|
||||||
|
|
||||||
if (drv_data->run == QUEUE_RUNNING || drv_data->busy) {
|
|
||||||
spin_unlock_irqrestore(&drv_data->lock, flags);
|
|
||||||
return -EBUSY;
|
|
||||||
}
|
|
||||||
|
|
||||||
drv_data->run = QUEUE_RUNNING;
|
|
||||||
drv_data->cur_msg = NULL;
|
|
||||||
drv_data->cur_transfer = NULL;
|
|
||||||
drv_data->cur_chip = NULL;
|
|
||||||
spin_unlock_irqrestore(&drv_data->lock, flags);
|
|
||||||
|
|
||||||
queue_work(drv_data->workqueue, &drv_data->pump_messages);
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int stop_queue(struct driver_data *drv_data)
|
|
||||||
{
|
|
||||||
unsigned long flags;
|
|
||||||
unsigned limit = 500;
|
|
||||||
int status = 0;
|
|
||||||
|
|
||||||
spin_lock_irqsave(&drv_data->lock, flags);
|
|
||||||
|
|
||||||
/* This is a bit lame, but is optimized for the common execution path.
|
|
||||||
* A wait_queue on the drv_data->busy could be used, but then the common
|
|
||||||
* execution path (pump_messages) would be required to call wake_up or
|
|
||||||
* friends on every SPI message. Do this instead */
|
|
||||||
drv_data->run = QUEUE_STOPPED;
|
|
||||||
while ((!list_empty(&drv_data->queue) || drv_data->busy) && limit--) {
|
|
||||||
spin_unlock_irqrestore(&drv_data->lock, flags);
|
|
||||||
msleep(10);
|
|
||||||
spin_lock_irqsave(&drv_data->lock, flags);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!list_empty(&drv_data->queue) || drv_data->busy)
|
|
||||||
status = -EBUSY;
|
|
||||||
|
|
||||||
spin_unlock_irqrestore(&drv_data->lock, flags);
|
|
||||||
|
|
||||||
return status;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int destroy_queue(struct driver_data *drv_data)
|
|
||||||
{
|
|
||||||
int status;
|
|
||||||
|
|
||||||
status = stop_queue(drv_data);
|
|
||||||
/* we are unloading the module or failing to load (only two calls
|
|
||||||
* to this routine), and neither call can handle a return value.
|
|
||||||
* However, destroy_workqueue calls flush_workqueue, and that will
|
|
||||||
* block until all work is done. If the reason that stop_queue
|
|
||||||
* timed out is that the work will never finish, then it does no
|
|
||||||
* good to call destroy_workqueue, so return anyway. */
|
|
||||||
if (status != 0)
|
|
||||||
return status;
|
|
||||||
|
|
||||||
destroy_workqueue(drv_data->workqueue);
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int pxa2xx_spi_probe(struct platform_device *pdev)
|
static int pxa2xx_spi_probe(struct platform_device *pdev)
|
||||||
{
|
{
|
||||||
struct device *dev = &pdev->dev;
|
struct device *dev = &pdev->dev;
|
||||||
|
@ -1535,11 +1379,18 @@ static int pxa2xx_spi_probe(struct platform_device *pdev)
|
||||||
struct ssp_device *ssp;
|
struct ssp_device *ssp;
|
||||||
int status;
|
int status;
|
||||||
|
|
||||||
platform_info = dev->platform_data;
|
platform_info = dev_get_platdata(dev);
|
||||||
|
if (!platform_info) {
|
||||||
|
dev_err(&pdev->dev, "missing platform data\n");
|
||||||
|
return -ENODEV;
|
||||||
|
}
|
||||||
|
|
||||||
ssp = pxa_ssp_request(pdev->id, pdev->name);
|
ssp = pxa_ssp_request(pdev->id, pdev->name);
|
||||||
if (ssp == NULL) {
|
if (!ssp)
|
||||||
dev_err(&pdev->dev, "failed to request SSP%d\n", pdev->id);
|
ssp = &platform_info->ssp;
|
||||||
|
|
||||||
|
if (!ssp->mmio_base) {
|
||||||
|
dev_err(&pdev->dev, "failed to get ssp\n");
|
||||||
return -ENODEV;
|
return -ENODEV;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1561,16 +1412,15 @@ static int pxa2xx_spi_probe(struct platform_device *pdev)
|
||||||
/* the spi->mode bits understood by this driver: */
|
/* the spi->mode bits understood by this driver: */
|
||||||
master->mode_bits = SPI_CPOL | SPI_CPHA | SPI_CS_HIGH;
|
master->mode_bits = SPI_CPOL | SPI_CPHA | SPI_CS_HIGH;
|
||||||
|
|
||||||
master->bus_num = pdev->id;
|
master->bus_num = ssp->port_id;
|
||||||
master->num_chipselect = platform_info->num_chipselect;
|
master->num_chipselect = platform_info->num_chipselect;
|
||||||
master->dma_alignment = DMA_ALIGNMENT;
|
master->dma_alignment = DMA_ALIGNMENT;
|
||||||
master->cleanup = cleanup;
|
master->cleanup = cleanup;
|
||||||
master->setup = setup;
|
master->setup = setup;
|
||||||
master->transfer = transfer;
|
master->transfer_one_message = pxa2xx_spi_transfer_one_message;
|
||||||
|
|
||||||
drv_data->ssp_type = ssp->type;
|
drv_data->ssp_type = ssp->type;
|
||||||
drv_data->null_dma_buf = (u32 *)ALIGN((u32)(drv_data +
|
drv_data->null_dma_buf = (u32 *)PTR_ALIGN(&drv_data[1], DMA_ALIGNMENT);
|
||||||
sizeof(struct driver_data)), 8);
|
|
||||||
|
|
||||||
drv_data->ioaddr = ssp->mmio_base;
|
drv_data->ioaddr = ssp->mmio_base;
|
||||||
drv_data->ssdr_physical = ssp->phys_base + SSDR;
|
drv_data->ssdr_physical = ssp->phys_base + SSDR;
|
||||||
|
@ -1625,7 +1475,9 @@ static int pxa2xx_spi_probe(struct platform_device *pdev)
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Enable SOC clock */
|
/* Enable SOC clock */
|
||||||
clk_enable(ssp->clk);
|
clk_prepare_enable(ssp->clk);
|
||||||
|
|
||||||
|
drv_data->max_clk_rate = clk_get_rate(ssp->clk);
|
||||||
|
|
||||||
/* Load default SSP configuration */
|
/* Load default SSP configuration */
|
||||||
write_SSCR0(0, drv_data->ioaddr);
|
write_SSCR0(0, drv_data->ioaddr);
|
||||||
|
@ -1640,33 +1492,21 @@ static int pxa2xx_spi_probe(struct platform_device *pdev)
|
||||||
write_SSTO(0, drv_data->ioaddr);
|
write_SSTO(0, drv_data->ioaddr);
|
||||||
write_SSPSP(0, drv_data->ioaddr);
|
write_SSPSP(0, drv_data->ioaddr);
|
||||||
|
|
||||||
/* Initial and start queue */
|
tasklet_init(&drv_data->pump_transfers, pump_transfers,
|
||||||
status = init_queue(drv_data);
|
(unsigned long)drv_data);
|
||||||
if (status != 0) {
|
|
||||||
dev_err(&pdev->dev, "problem initializing queue\n");
|
|
||||||
goto out_error_clock_enabled;
|
|
||||||
}
|
|
||||||
status = start_queue(drv_data);
|
|
||||||
if (status != 0) {
|
|
||||||
dev_err(&pdev->dev, "problem starting queue\n");
|
|
||||||
goto out_error_clock_enabled;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Register with the SPI framework */
|
/* Register with the SPI framework */
|
||||||
platform_set_drvdata(pdev, drv_data);
|
platform_set_drvdata(pdev, drv_data);
|
||||||
status = spi_register_master(master);
|
status = spi_register_master(master);
|
||||||
if (status != 0) {
|
if (status != 0) {
|
||||||
dev_err(&pdev->dev, "problem registering spi master\n");
|
dev_err(&pdev->dev, "problem registering spi master\n");
|
||||||
goto out_error_queue_alloc;
|
goto out_error_clock_enabled;
|
||||||
}
|
}
|
||||||
|
|
||||||
return status;
|
return status;
|
||||||
|
|
||||||
out_error_queue_alloc:
|
|
||||||
destroy_queue(drv_data);
|
|
||||||
|
|
||||||
out_error_clock_enabled:
|
out_error_clock_enabled:
|
||||||
clk_disable(ssp->clk);
|
clk_disable_unprepare(ssp->clk);
|
||||||
|
|
||||||
out_error_dma_alloc:
|
out_error_dma_alloc:
|
||||||
if (drv_data->tx_channel != -1)
|
if (drv_data->tx_channel != -1)
|
||||||
|
@ -1687,29 +1527,14 @@ static int pxa2xx_spi_remove(struct platform_device *pdev)
|
||||||
{
|
{
|
||||||
struct driver_data *drv_data = platform_get_drvdata(pdev);
|
struct driver_data *drv_data = platform_get_drvdata(pdev);
|
||||||
struct ssp_device *ssp;
|
struct ssp_device *ssp;
|
||||||
int status = 0;
|
|
||||||
|
|
||||||
if (!drv_data)
|
if (!drv_data)
|
||||||
return 0;
|
return 0;
|
||||||
ssp = drv_data->ssp;
|
ssp = drv_data->ssp;
|
||||||
|
|
||||||
/* Remove the queue */
|
|
||||||
status = destroy_queue(drv_data);
|
|
||||||
if (status != 0)
|
|
||||||
/* the kernel does not check the return status of this
|
|
||||||
* this routine (mod->exit, within the kernel). Therefore
|
|
||||||
* nothing is gained by returning from here, the module is
|
|
||||||
* going away regardless, and we should not leave any more
|
|
||||||
* resources allocated than necessary. We cannot free the
|
|
||||||
* message memory in drv_data->queue, but we can release the
|
|
||||||
* resources below. I think the kernel should honor -EBUSY
|
|
||||||
* returns but... */
|
|
||||||
dev_err(&pdev->dev, "pxa2xx_spi_remove: workqueue will not "
|
|
||||||
"complete, message memory not freed\n");
|
|
||||||
|
|
||||||
/* Disable the SSP at the peripheral and SOC level */
|
/* Disable the SSP at the peripheral and SOC level */
|
||||||
write_SSCR0(0, drv_data->ioaddr);
|
write_SSCR0(0, drv_data->ioaddr);
|
||||||
clk_disable(ssp->clk);
|
clk_disable_unprepare(ssp->clk);
|
||||||
|
|
||||||
/* Release DMA */
|
/* Release DMA */
|
||||||
if (drv_data->master_info->enable_dma) {
|
if (drv_data->master_info->enable_dma) {
|
||||||
|
@ -1749,11 +1574,11 @@ static int pxa2xx_spi_suspend(struct device *dev)
|
||||||
struct ssp_device *ssp = drv_data->ssp;
|
struct ssp_device *ssp = drv_data->ssp;
|
||||||
int status = 0;
|
int status = 0;
|
||||||
|
|
||||||
status = stop_queue(drv_data);
|
status = spi_master_suspend(drv_data->master);
|
||||||
if (status != 0)
|
if (status != 0)
|
||||||
return status;
|
return status;
|
||||||
write_SSCR0(0, drv_data->ioaddr);
|
write_SSCR0(0, drv_data->ioaddr);
|
||||||
clk_disable(ssp->clk);
|
clk_disable_unprepare(ssp->clk);
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
@ -1772,10 +1597,10 @@ static int pxa2xx_spi_resume(struct device *dev)
|
||||||
DRCMR_MAPVLD | drv_data->tx_channel;
|
DRCMR_MAPVLD | drv_data->tx_channel;
|
||||||
|
|
||||||
/* Enable the SSP clock */
|
/* Enable the SSP clock */
|
||||||
clk_enable(ssp->clk);
|
clk_prepare_enable(ssp->clk);
|
||||||
|
|
||||||
/* Start the queue running */
|
/* Start the queue running */
|
||||||
status = start_queue(drv_data);
|
status = spi_master_resume(drv_data->master);
|
||||||
if (status != 0) {
|
if (status != 0) {
|
||||||
dev_err(dev, "problem starting queue (%d)\n", status);
|
dev_err(dev, "problem starting queue (%d)\n", status);
|
||||||
return status;
|
return status;
|
||||||
|
|
|
@ -957,6 +957,8 @@ static int s3c64xx_spi_setup(struct spi_device *spi)
|
||||||
if (spi->max_speed_hz >= speed) {
|
if (spi->max_speed_hz >= speed) {
|
||||||
spi->max_speed_hz = speed;
|
spi->max_speed_hz = speed;
|
||||||
} else {
|
} else {
|
||||||
|
dev_err(&spi->dev, "Can't set %dHz transfer speed\n",
|
||||||
|
spi->max_speed_hz);
|
||||||
err = -EINVAL;
|
err = -EINVAL;
|
||||||
goto setup_exit;
|
goto setup_exit;
|
||||||
}
|
}
|
||||||
|
|
|
@ -22,6 +22,9 @@ struct omap2_mcspi_dev_attr {
|
||||||
|
|
||||||
struct omap2_mcspi_device_config {
|
struct omap2_mcspi_device_config {
|
||||||
unsigned turbo_mode:1;
|
unsigned turbo_mode:1;
|
||||||
|
|
||||||
|
/* toggle chip select after every word */
|
||||||
|
unsigned cs_per_word:1;
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
|
@ -206,6 +206,15 @@ static inline u32 pxa_ssp_read_reg(struct ssp_device *dev, u32 reg)
|
||||||
return __raw_readl(dev->mmio_base + reg);
|
return __raw_readl(dev->mmio_base + reg);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#ifdef CONFIG_ARCH_PXA
|
||||||
struct ssp_device *pxa_ssp_request(int port, const char *label);
|
struct ssp_device *pxa_ssp_request(int port, const char *label);
|
||||||
void pxa_ssp_free(struct ssp_device *);
|
void pxa_ssp_free(struct ssp_device *);
|
||||||
|
#else
|
||||||
|
static inline struct ssp_device *pxa_ssp_request(int port, const char *label)
|
||||||
|
{
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
static inline void pxa_ssp_free(struct ssp_device *ssp) {}
|
||||||
|
#endif
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
|
@ -28,6 +28,9 @@ struct pxa2xx_spi_master {
|
||||||
u32 clock_enable;
|
u32 clock_enable;
|
||||||
u16 num_chipselect;
|
u16 num_chipselect;
|
||||||
u8 enable_dma;
|
u8 enable_dma;
|
||||||
|
|
||||||
|
/* For non-PXA arches */
|
||||||
|
struct ssp_device ssp;
|
||||||
};
|
};
|
||||||
|
|
||||||
/* spi_board_info.controller_data for SPI slave devices,
|
/* spi_board_info.controller_data for SPI slave devices,
|
||||||
|
@ -130,23 +133,5 @@ static inline void pxa_free_dma(int dma_ch)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
|
||||||
* The CE4100 does not have the clk framework implemented and SPI clock can
|
|
||||||
* not be switched on/off or the divider changed.
|
|
||||||
*/
|
|
||||||
static inline void clk_disable(struct clk *clk)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
static inline int clk_enable(struct clk *clk)
|
|
||||||
{
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
static inline unsigned long clk_get_rate(struct clk *clk)
|
|
||||||
{
|
|
||||||
return 3686400;
|
|
||||||
}
|
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
#endif
|
#endif
|
||||||
|
|
|
@ -62,8 +62,8 @@
|
||||||
*/
|
*/
|
||||||
struct spi_gpio_platform_data {
|
struct spi_gpio_platform_data {
|
||||||
unsigned sck;
|
unsigned sck;
|
||||||
unsigned mosi;
|
unsigned long mosi;
|
||||||
unsigned miso;
|
unsigned long miso;
|
||||||
|
|
||||||
u16 num_chipselect;
|
u16 num_chipselect;
|
||||||
};
|
};
|
||||||
|
|
Загрузка…
Ссылка в новой задаче