diff options
Diffstat (limited to 'drivers/spi')
-rw-r--r-- | drivers/spi/Kconfig | 1 | ||||
-rw-r--r-- | drivers/spi/omap2_mcspi.c | 6 | ||||
-rw-r--r-- | drivers/spi/pxa2xx_spi.c | 139 | ||||
-rw-r--r-- | drivers/spi/spi.c | 26 |
4 files changed, 72 insertions, 100 deletions
diff --git a/drivers/spi/Kconfig b/drivers/spi/Kconfig index abf05048c63..aaaea81e412 100644 --- a/drivers/spi/Kconfig +++ b/drivers/spi/Kconfig @@ -153,6 +153,7 @@ config SPI_OMAP24XX config SPI_PXA2XX tristate "PXA2xx SSP SPI master" depends on SPI_MASTER && ARCH_PXA && EXPERIMENTAL + select PXA_SSP help This enables using a PXA2xx SSP port as a SPI master controller. The driver can be configured to use any SSP port and additional diff --git a/drivers/spi/omap2_mcspi.c b/drivers/spi/omap2_mcspi.c index 3cdab131c4a..ea61724ae22 100644 --- a/drivers/spi/omap2_mcspi.c +++ b/drivers/spi/omap2_mcspi.c @@ -350,6 +350,7 @@ omap2_mcspi_txrx_pio(struct spi_device *spi, struct spi_transfer *xfer) tx = xfer->tx_buf; do { + c -= 1; if (tx != NULL) { if (mcspi_wait_for_reg_bit(chstat_reg, OMAP2_MCSPI_CHSTAT_TXS) < 0) { @@ -380,7 +381,6 @@ omap2_mcspi_txrx_pio(struct spi_device *spi, struct spi_transfer *xfer) word_len, *(rx - 1)); #endif } - c -= 1; } while (c); } else if (word_len <= 16) { u16 *rx; @@ -389,6 +389,7 @@ omap2_mcspi_txrx_pio(struct spi_device *spi, struct spi_transfer *xfer) rx = xfer->rx_buf; tx = xfer->tx_buf; do { + c -= 2; if (tx != NULL) { if (mcspi_wait_for_reg_bit(chstat_reg, OMAP2_MCSPI_CHSTAT_TXS) < 0) { @@ -419,7 +420,6 @@ omap2_mcspi_txrx_pio(struct spi_device *spi, struct spi_transfer *xfer) word_len, *(rx - 1)); #endif } - c -= 2; } while (c); } else if (word_len <= 32) { u32 *rx; @@ -428,6 +428,7 @@ omap2_mcspi_txrx_pio(struct spi_device *spi, struct spi_transfer *xfer) rx = xfer->rx_buf; tx = xfer->tx_buf; do { + c -= 4; if (tx != NULL) { if (mcspi_wait_for_reg_bit(chstat_reg, OMAP2_MCSPI_CHSTAT_TXS) < 0) { @@ -458,7 +459,6 @@ omap2_mcspi_txrx_pio(struct spi_device *spi, struct spi_transfer *xfer) word_len, *(rx - 1)); #endif } - c -= 4; } while (c); } diff --git a/drivers/spi/pxa2xx_spi.c b/drivers/spi/pxa2xx_spi.c index 1c2ab541d37..eb817b8eb02 100644 --- a/drivers/spi/pxa2xx_spi.c +++ b/drivers/spi/pxa2xx_spi.c @@ -27,6 +27,7 @@ #include <linux/spi/spi.h> #include <linux/workqueue.h> #include <linux/delay.h> +#include <linux/clk.h> #include <asm/io.h> #include <asm/irq.h> @@ -36,6 +37,8 @@ #include <asm/arch/hardware.h> #include <asm/arch/pxa-regs.h> +#include <asm/arch/regs-ssp.h> +#include <asm/arch/ssp.h> #include <asm/arch/pxa2xx_spi.h> MODULE_AUTHOR("Stephen Street"); @@ -80,6 +83,9 @@ struct driver_data { /* Driver model hookup */ struct platform_device *pdev; + /* SSP Info */ + struct ssp_device *ssp; + /* SPI framework hookup */ enum pxa_ssp_type ssp_type; struct spi_master *master; @@ -778,6 +784,16 @@ int set_dma_burst_and_threshold(struct chip_data *chip, struct spi_device *spi, return retval; } +static unsigned int ssp_get_clk_div(struct ssp_device *ssp, int rate) +{ + unsigned long ssp_clk = clk_get_rate(ssp->clk); + + if (ssp->type == PXA25x_SSP) + return ((ssp_clk / (2 * rate) - 1) & 0xff) << 8; + else + return ((ssp_clk / rate - 1) & 0xfff) << 8; +} + static void pump_transfers(unsigned long data) { struct driver_data *drv_data = (struct driver_data *)data; @@ -785,6 +801,7 @@ static void pump_transfers(unsigned long data) struct spi_transfer *transfer = NULL; struct spi_transfer *previous = NULL; struct chip_data *chip = NULL; + struct ssp_device *ssp = drv_data->ssp; void *reg = drv_data->ioaddr; u32 clk_div = 0; u8 bits = 0; @@ -866,12 +883,7 @@ static void pump_transfers(unsigned long data) if (transfer->bits_per_word) bits = transfer->bits_per_word; - if (reg == SSP1_VIRT) - clk_div = SSP1_SerClkDiv(speed); - else if (reg == SSP2_VIRT) - clk_div = SSP2_SerClkDiv(speed); - else if (reg == SSP3_VIRT) - clk_div = SSP3_SerClkDiv(speed); + clk_div = ssp_get_clk_div(ssp, speed); if (bits <= 8) { drv_data->n_bytes = 1; @@ -1074,6 +1086,7 @@ static int setup(struct spi_device *spi) struct pxa2xx_spi_chip *chip_info = NULL; struct chip_data *chip; struct driver_data *drv_data = spi_master_get_devdata(spi->master); + struct ssp_device *ssp = drv_data->ssp; unsigned int clk_div; if (!spi->bits_per_word) @@ -1157,18 +1170,7 @@ static int setup(struct spi_device *spi) } } - if (drv_data->ioaddr == SSP1_VIRT) - clk_div = SSP1_SerClkDiv(spi->max_speed_hz); - else if (drv_data->ioaddr == SSP2_VIRT) - clk_div = SSP2_SerClkDiv(spi->max_speed_hz); - else if (drv_data->ioaddr == SSP3_VIRT) - clk_div = SSP3_SerClkDiv(spi->max_speed_hz); - else - { - dev_err(&spi->dev, "failed setup: unknown IO address=0x%p\n", - drv_data->ioaddr); - return -ENODEV; - } + clk_div = ssp_get_clk_div(ssp, spi->max_speed_hz); chip->speed_hz = spi->max_speed_hz; chip->cr0 = clk_div @@ -1183,15 +1185,15 @@ static int setup(struct spi_device *spi) /* NOTE: PXA25x_SSP _could_ use external clocking ... */ if (drv_data->ssp_type != PXA25x_SSP) - dev_dbg(&spi->dev, "%d bits/word, %d Hz, mode %d\n", + dev_dbg(&spi->dev, "%d bits/word, %ld Hz, mode %d\n", spi->bits_per_word, - (CLOCK_SPEED_HZ) + clk_get_rate(ssp->clk) / (1 + ((chip->cr0 & SSCR0_SCR) >> 8)), spi->mode & 0x3); else - dev_dbg(&spi->dev, "%d bits/word, %d Hz, mode %d\n", + dev_dbg(&spi->dev, "%d bits/word, %ld Hz, mode %d\n", spi->bits_per_word, - (CLOCK_SPEED_HZ/2) + clk_get_rate(ssp->clk) / (1 + ((chip->cr0 & SSCR0_SCR) >> 8)), spi->mode & 0x3); @@ -1323,14 +1325,14 @@ static int __init pxa2xx_spi_probe(struct platform_device *pdev) struct pxa2xx_spi_master *platform_info; struct spi_master *master; struct driver_data *drv_data = 0; - struct resource *memory_resource; - int irq; + struct ssp_device *ssp; int status = 0; platform_info = dev->platform_data; - if (platform_info->ssp_type == SSP_UNDEFINED) { - dev_err(&pdev->dev, "undefined SSP\n"); + ssp = ssp_request(pdev->id, pdev->name); + if (ssp == NULL) { + dev_err(&pdev->dev, "failed to request SSP%d\n", pdev->id); return -ENODEV; } @@ -1338,12 +1340,14 @@ static int __init pxa2xx_spi_probe(struct platform_device *pdev) master = spi_alloc_master(dev, sizeof(struct driver_data) + 16); if (!master) { dev_err(&pdev->dev, "can not alloc spi_master\n"); + ssp_free(ssp); return -ENOMEM; } drv_data = spi_master_get_devdata(master); drv_data->master = master; drv_data->master_info = platform_info; drv_data->pdev = pdev; + drv_data->ssp = ssp; master->bus_num = pdev->id; master->num_chipselect = platform_info->num_chipselect; @@ -1351,21 +1355,13 @@ static int __init pxa2xx_spi_probe(struct platform_device *pdev) master->setup = setup; master->transfer = transfer; - drv_data->ssp_type = platform_info->ssp_type; + drv_data->ssp_type = ssp->type; drv_data->null_dma_buf = (u32 *)ALIGN((u32)(drv_data + sizeof(struct driver_data)), 8); - /* Setup register addresses */ - memory_resource = platform_get_resource(pdev, IORESOURCE_MEM, 0); - if (!memory_resource) { - dev_err(&pdev->dev, "memory resources not defined\n"); - status = -ENODEV; - goto out_error_master_alloc; - } - - drv_data->ioaddr = (void *)io_p2v((unsigned long)(memory_resource->start)); - drv_data->ssdr_physical = memory_resource->start + 0x00000010; - if (platform_info->ssp_type == PXA25x_SSP) { + drv_data->ioaddr = ssp->mmio_base; + drv_data->ssdr_physical = ssp->phys_base + SSDR; + if (ssp->type == PXA25x_SSP) { drv_data->int_cr1 = SSCR1_TIE | SSCR1_RIE; drv_data->dma_cr1 = 0; drv_data->clear_sr = SSSR_ROR; @@ -1377,15 +1373,7 @@ static int __init pxa2xx_spi_probe(struct platform_device *pdev) drv_data->mask_sr = SSSR_TINT | SSSR_RFS | SSSR_TFS | SSSR_ROR; } - /* Attach to IRQ */ - irq = platform_get_irq(pdev, 0); - if (irq < 0) { - dev_err(&pdev->dev, "irq resource not defined\n"); - status = -ENODEV; - goto out_error_master_alloc; - } - - status = request_irq(irq, ssp_int, 0, dev->bus_id, drv_data); + status = request_irq(ssp->irq, ssp_int, 0, dev->bus_id, drv_data); if (status < 0) { dev_err(&pdev->dev, "can not get IRQ\n"); goto out_error_master_alloc; @@ -1418,29 +1406,12 @@ static int __init pxa2xx_spi_probe(struct platform_device *pdev) goto out_error_dma_alloc; } - if (drv_data->ioaddr == SSP1_VIRT) { - DRCMRRXSSDR = DRCMR_MAPVLD - | drv_data->rx_channel; - DRCMRTXSSDR = DRCMR_MAPVLD - | drv_data->tx_channel; - } else if (drv_data->ioaddr == SSP2_VIRT) { - DRCMRRXSS2DR = DRCMR_MAPVLD - | drv_data->rx_channel; - DRCMRTXSS2DR = DRCMR_MAPVLD - | drv_data->tx_channel; - } else if (drv_data->ioaddr == SSP3_VIRT) { - DRCMRRXSS3DR = DRCMR_MAPVLD - | drv_data->rx_channel; - DRCMRTXSS3DR = DRCMR_MAPVLD - | drv_data->tx_channel; - } else { - dev_err(dev, "bad SSP type\n"); - goto out_error_dma_alloc; - } + DRCMR(ssp->drcmr_rx) = DRCMR_MAPVLD | drv_data->rx_channel; + DRCMR(ssp->drcmr_tx) = DRCMR_MAPVLD | drv_data->tx_channel; } /* Enable SOC clock */ - pxa_set_cken(platform_info->clock_enable, 1); + clk_enable(ssp->clk); /* Load default SSP configuration */ write_SSCR0(0, drv_data->ioaddr); @@ -1479,7 +1450,7 @@ out_error_queue_alloc: destroy_queue(drv_data); out_error_clock_enabled: - pxa_set_cken(platform_info->clock_enable, 0); + clk_disable(ssp->clk); out_error_dma_alloc: if (drv_data->tx_channel != -1) @@ -1488,17 +1459,18 @@ out_error_dma_alloc: pxa_free_dma(drv_data->rx_channel); out_error_irq_alloc: - free_irq(irq, drv_data); + free_irq(ssp->irq, drv_data); out_error_master_alloc: spi_master_put(master); + ssp_free(ssp); return status; } static int pxa2xx_spi_remove(struct platform_device *pdev) { struct driver_data *drv_data = platform_get_drvdata(pdev); - int irq; + struct ssp_device *ssp = drv_data->ssp; int status = 0; if (!drv_data) @@ -1520,28 +1492,21 @@ static int pxa2xx_spi_remove(struct platform_device *pdev) /* Disable the SSP at the peripheral and SOC level */ write_SSCR0(0, drv_data->ioaddr); - pxa_set_cken(drv_data->master_info->clock_enable, 0); + clk_disable(ssp->clk); /* Release DMA */ if (drv_data->master_info->enable_dma) { - if (drv_data->ioaddr == SSP1_VIRT) { - DRCMRRXSSDR = 0; - DRCMRTXSSDR = 0; - } else if (drv_data->ioaddr == SSP2_VIRT) { - DRCMRRXSS2DR = 0; - DRCMRTXSS2DR = 0; - } else if (drv_data->ioaddr == SSP3_VIRT) { - DRCMRRXSS3DR = 0; - DRCMRTXSS3DR = 0; - } + DRCMR(ssp->drcmr_rx) = 0; + DRCMR(ssp->drcmr_tx) = 0; pxa_free_dma(drv_data->tx_channel); pxa_free_dma(drv_data->rx_channel); } /* Release IRQ */ - irq = platform_get_irq(pdev, 0); - if (irq >= 0) - free_irq(irq, drv_data); + free_irq(ssp->irq, drv_data); + + /* Release SSP */ + ssp_free(ssp); /* Disconnect from the SPI framework */ spi_unregister_master(drv_data->master); @@ -1576,6 +1541,7 @@ static int suspend_devices(struct device *dev, void *pm_message) static int pxa2xx_spi_suspend(struct platform_device *pdev, pm_message_t state) { struct driver_data *drv_data = platform_get_drvdata(pdev); + struct ssp_device *ssp = drv_data->ssp; int status = 0; /* Check all childern for current power state */ @@ -1588,7 +1554,7 @@ static int pxa2xx_spi_suspend(struct platform_device *pdev, pm_message_t state) if (status != 0) return status; write_SSCR0(0, drv_data->ioaddr); - pxa_set_cken(drv_data->master_info->clock_enable, 0); + clk_disable(ssp->clk); return 0; } @@ -1596,10 +1562,11 @@ static int pxa2xx_spi_suspend(struct platform_device *pdev, pm_message_t state) static int pxa2xx_spi_resume(struct platform_device *pdev) { struct driver_data *drv_data = platform_get_drvdata(pdev); + struct ssp_device *ssp = drv_data->ssp; int status = 0; /* Enable the SSP clock */ - pxa_set_cken(drv_data->master_info->clock_enable, 1); + clk_disable(ssp->clk); /* Start the queue running */ status = start_queue(drv_data); diff --git a/drivers/spi/spi.c b/drivers/spi/spi.c index 93e9de46977..682a6a48fec 100644 --- a/drivers/spi/spi.c +++ b/drivers/spi/spi.c @@ -485,6 +485,15 @@ void spi_unregister_master(struct spi_master *master) } EXPORT_SYMBOL_GPL(spi_unregister_master); +static int __spi_master_match(struct device *dev, void *data) +{ + struct spi_master *m; + u16 *bus_num = data; + + m = container_of(dev, struct spi_master, dev); + return m->bus_num == *bus_num; +} + /** * spi_busnum_to_master - look up master associated with bus_num * @bus_num: the master's bus number @@ -499,17 +508,12 @@ struct spi_master *spi_busnum_to_master(u16 bus_num) { struct device *dev; struct spi_master *master = NULL; - struct spi_master *m; - - down(&spi_master_class.sem); - list_for_each_entry(dev, &spi_master_class.children, node) { - m = container_of(dev, struct spi_master, dev); - if (m->bus_num == bus_num) { - master = spi_master_get(m); - break; - } - } - up(&spi_master_class.sem); + + dev = class_find_device(&spi_master_class, &bus_num, + __spi_master_match); + if (dev) + master = container_of(dev, struct spi_master, dev); + /* reference got in class_find_device */ return master; } EXPORT_SYMBOL_GPL(spi_busnum_to_master); |