diff options
Diffstat (limited to 'drivers/spi')
-rw-r--r-- | drivers/spi/Kconfig | 6 | ||||
-rw-r--r-- | drivers/spi/spi-omap2-mcspi.c | 51 | ||||
-rw-r--r-- | drivers/spi/spi-pl022.c | 143 | ||||
-rw-r--r-- | drivers/spi/spi-topcliff-pch.c | 13 | ||||
-rw-r--r-- | drivers/spi/spi.c | 2 |
5 files changed, 131 insertions, 84 deletions
diff --git a/drivers/spi/Kconfig b/drivers/spi/Kconfig index 8ba4510..7587796 100644 --- a/drivers/spi/Kconfig +++ b/drivers/spi/Kconfig @@ -346,14 +346,14 @@ config SPI_TI_SSP serial port. config SPI_TOPCLIFF_PCH - tristate "Intel EG20T PCH/OKI SEMICONDUCTOR ML7213 IOH SPI controller" + tristate "Intel EG20T PCH/LAPIS Semicon IOH(ML7213/ML7223/ML7831) SPI" depends on PCI help SPI driver for the Topcliff PCH (Platform Controller Hub) SPI bus used in some x86 embedded processors. - This driver also supports the ML7213, a companion chip for the - Atom E6xx series and compatible with the Intel EG20T PCH. + This driver also supports the ML7213/ML7223/ML7831, a companion chip + for the Atom E6xx series and compatible with the Intel EG20T PCH. config SPI_TXX9 tristate "Toshiba TXx9 SPI controller" diff --git a/drivers/spi/spi-omap2-mcspi.c b/drivers/spi/spi-omap2-mcspi.c index 322be7a..0b0dfb7 100644 --- a/drivers/spi/spi-omap2-mcspi.c +++ b/drivers/spi/spi-omap2-mcspi.c @@ -121,6 +121,7 @@ struct omap2_mcspi { /* SPI1 has 4 channels, while SPI2 has 2 */ struct omap2_mcspi_dma *dma_channels; struct device *dev; + struct workqueue_struct *wq; }; struct omap2_mcspi_cs { @@ -143,8 +144,6 @@ struct omap2_mcspi_regs { static struct omap2_mcspi_regs omap2_mcspi_ctx[OMAP2_MCSPI_MAX_CTRL]; -static struct workqueue_struct *omap2_mcspi_wq; - #define MOD_REG_BIT(val, mask, set) do { \ if (set) \ val |= mask; \ @@ -1043,7 +1042,7 @@ static int omap2_mcspi_transfer(struct spi_device *spi, struct spi_message *m) spin_lock_irqsave(&mcspi->lock, flags); list_add_tail(&m->queue, &mcspi->msg_queue); - queue_work(omap2_mcspi_wq, &mcspi->work); + queue_work(mcspi->wq, &mcspi->work); spin_unlock_irqrestore(&mcspi->lock, flags); return 0; @@ -1088,6 +1087,7 @@ static int __init omap2_mcspi_probe(struct platform_device *pdev) struct omap2_mcspi *mcspi; struct resource *r; int status = 0, i; + char wq_name[20]; master = spi_alloc_master(&pdev->dev, sizeof *mcspi); if (master == NULL) { @@ -1111,10 +1111,17 @@ static int __init omap2_mcspi_probe(struct platform_device *pdev) mcspi = spi_master_get_devdata(master); mcspi->master = master; + sprintf(wq_name, "omap2_mcspi/%d", master->bus_num); + mcspi->wq = alloc_workqueue(wq_name, WQ_MEM_RECLAIM, 1); + if (mcspi->wq == NULL) { + status = -ENOMEM; + goto free_master; + } + r = platform_get_resource(pdev, IORESOURCE_MEM, 0); if (r == NULL) { status = -ENODEV; - goto err1; + goto free_master; } r->start += pdata->regs_offset; @@ -1123,14 +1130,14 @@ static int __init omap2_mcspi_probe(struct platform_device *pdev) if (!request_mem_region(r->start, resource_size(r), dev_name(&pdev->dev))) { status = -EBUSY; - goto err1; + goto free_master; } mcspi->base = ioremap(r->start, resource_size(r)); if (!mcspi->base) { dev_dbg(&pdev->dev, "can't ioremap MCSPI\n"); status = -ENOMEM; - goto err2; + goto release_region; } mcspi->dev = &pdev->dev; @@ -1145,7 +1152,7 @@ static int __init omap2_mcspi_probe(struct platform_device *pdev) GFP_KERNEL); if (mcspi->dma_channels == NULL) - goto err2; + goto unmap_io; for (i = 0; i < master->num_chipselect; i++) { char dma_ch_name[14]; @@ -1175,25 +1182,33 @@ static int __init omap2_mcspi_probe(struct platform_device *pdev) mcspi->dma_channels[i].dma_tx_sync_dev = dma_res->start; } + if (status < 0) + goto dma_chnl_free; + pm_runtime_enable(&pdev->dev); if (status || omap2_mcspi_master_setup(mcspi) < 0) - goto err3; + goto disable_pm; status = spi_register_master(master); if (status < 0) - goto err4; + goto err_spi_register; return status; -err4: +err_spi_register: spi_master_put(master); -err3: +disable_pm: + pm_runtime_disable(&pdev->dev); +dma_chnl_free: kfree(mcspi->dma_channels); -err2: - release_mem_region(r->start, resource_size(r)); +unmap_io: iounmap(mcspi->base); -err1: +release_region: + release_mem_region(r->start, resource_size(r)); +free_master: + kfree(master); + platform_set_drvdata(pdev, NULL); return status; } @@ -1210,6 +1225,7 @@ static int __exit omap2_mcspi_remove(struct platform_device *pdev) dma_channels = mcspi->dma_channels; omap2_mcspi_disable_clocks(mcspi); + pm_runtime_disable(&pdev->dev); r = platform_get_resource(pdev, IORESOURCE_MEM, 0); release_mem_region(r->start, resource_size(r)); @@ -1217,6 +1233,8 @@ static int __exit omap2_mcspi_remove(struct platform_device *pdev) spi_unregister_master(master); iounmap(base); kfree(dma_channels); + destroy_workqueue(mcspi->wq); + platform_set_drvdata(pdev, NULL); return 0; } @@ -1275,10 +1293,6 @@ static struct platform_driver omap2_mcspi_driver = { static int __init omap2_mcspi_init(void) { - omap2_mcspi_wq = create_singlethread_workqueue( - omap2_mcspi_driver.driver.name); - if (omap2_mcspi_wq == NULL) - return -1; return platform_driver_probe(&omap2_mcspi_driver, omap2_mcspi_probe); } subsys_initcall(omap2_mcspi_init); @@ -1287,7 +1301,6 @@ static void __exit omap2_mcspi_exit(void) { platform_driver_unregister(&omap2_mcspi_driver); - destroy_workqueue(omap2_mcspi_wq); } module_exit(omap2_mcspi_exit); diff --git a/drivers/spi/spi-pl022.c b/drivers/spi/spi-pl022.c index 5559b22..f1f5efb 100644 --- a/drivers/spi/spi-pl022.c +++ b/drivers/spi/spi-pl022.c @@ -340,6 +340,10 @@ struct vendor_data { * @cur_msg: Pointer to current spi_message being processed * @cur_transfer: Pointer to current spi_transfer * @cur_chip: pointer to current clients chip(assigned from controller_state) + * @next_msg_cs_active: the next message in the queue has been examined + * and it was found that it uses the same chip select as the previous + * message, so we left it active after the previous transfer, and it's + * active already. * @tx: current position in TX buffer to be read * @tx_end: end position in TX buffer to be read * @rx: current position in RX buffer to be written @@ -373,6 +377,7 @@ struct pl022 { struct spi_message *cur_msg; struct spi_transfer *cur_transfer; struct chip_data *cur_chip; + bool next_msg_cs_active; void *tx; void *tx_end; void *rx; @@ -445,23 +450,9 @@ static void giveback(struct pl022 *pl022) struct spi_transfer *last_transfer; unsigned long flags; struct spi_message *msg; - void (*curr_cs_control) (u32 command); + pl022->next_msg_cs_active = false; - /* - * This local reference to the chip select function - * is needed because we set curr_chip to NULL - * as a step toward termininating the message. - */ - curr_cs_control = pl022->cur_chip->cs_control; - spin_lock_irqsave(&pl022->queue_lock, flags); - msg = pl022->cur_msg; - pl022->cur_msg = NULL; - pl022->cur_transfer = NULL; - pl022->cur_chip = NULL; - queue_work(pl022->workqueue, &pl022->pump_messages); - spin_unlock_irqrestore(&pl022->queue_lock, flags); - - last_transfer = list_entry(msg->transfers.prev, + last_transfer = list_entry(pl022->cur_msg->transfers.prev, struct spi_transfer, transfer_list); @@ -473,18 +464,13 @@ static void giveback(struct pl022 *pl022) */ udelay(last_transfer->delay_usecs); - /* - * Drop chip select UNLESS cs_change is true or we are returning - * a message with an error, or next message is for another chip - */ - if (!last_transfer->cs_change) - curr_cs_control(SSP_CHIP_DESELECT); - else { + if (!last_transfer->cs_change) { struct spi_message *next_msg; - /* Holding of cs was hinted, but we need to make sure - * the next message is for the same chip. Don't waste - * time with the following tests unless this was hinted. + /* + * cs_change was not set. We can keep the chip select + * enabled if there is message in the queue and it is + * for the same spi device. * * We cannot postpone this until pump_messages, because * after calling msg->complete (below) the driver that @@ -501,19 +487,29 @@ static void giveback(struct pl022 *pl022) struct spi_message, queue); spin_unlock_irqrestore(&pl022->queue_lock, flags); - /* see if the next and current messages point - * to the same chip + /* + * see if the next and current messages point + * to the same spi device. */ - if (next_msg && next_msg->spi != msg->spi) + if (next_msg && next_msg->spi != pl022->cur_msg->spi) next_msg = NULL; - if (!next_msg || msg->state == STATE_ERROR) - curr_cs_control(SSP_CHIP_DESELECT); + if (!next_msg || pl022->cur_msg->state == STATE_ERROR) + pl022->cur_chip->cs_control(SSP_CHIP_DESELECT); + else + pl022->next_msg_cs_active = true; } + + spin_lock_irqsave(&pl022->queue_lock, flags); + msg = pl022->cur_msg; + pl022->cur_msg = NULL; + pl022->cur_transfer = NULL; + pl022->cur_chip = NULL; + queue_work(pl022->workqueue, &pl022->pump_messages); + spin_unlock_irqrestore(&pl022->queue_lock, flags); + msg->state = NULL; if (msg->complete) msg->complete(msg->context); - /* This message is completed, so let's turn off the clocks & power */ - pm_runtime_put(&pl022->adev->dev); } /** @@ -1244,9 +1240,9 @@ static irqreturn_t pl022_interrupt_handler(int irq, void *dev_id) if ((pl022->tx == pl022->tx_end) && (flag == 0)) { flag = 1; - /* Disable Transmit interrupt */ - writew(readw(SSP_IMSC(pl022->virtbase)) & - (~SSP_IMSC_MASK_TXIM), + /* Disable Transmit interrupt, enable receive interrupt */ + writew((readw(SSP_IMSC(pl022->virtbase)) & + ~SSP_IMSC_MASK_TXIM) | SSP_IMSC_MASK_RXIM, SSP_IMSC(pl022->virtbase)); } @@ -1352,7 +1348,7 @@ static void pump_transfers(unsigned long data) */ udelay(previous->delay_usecs); - /* Drop chip select only if cs_change is requested */ + /* Reselect chip select only if cs_change was requested */ if (previous->cs_change) pl022->cur_chip->cs_control(SSP_CHIP_SELECT); } else { @@ -1379,15 +1375,22 @@ static void pump_transfers(unsigned long data) } err_config_dma: - writew(ENABLE_ALL_INTERRUPTS, SSP_IMSC(pl022->virtbase)); + /* enable all interrupts except RX */ + writew(ENABLE_ALL_INTERRUPTS & ~SSP_IMSC_MASK_RXIM, SSP_IMSC(pl022->virtbase)); } static void do_interrupt_dma_transfer(struct pl022 *pl022) { - u32 irqflags = ENABLE_ALL_INTERRUPTS; + /* + * Default is to enable all interrupts except RX - + * this will be enabled once TX is complete + */ + u32 irqflags = ENABLE_ALL_INTERRUPTS & ~SSP_IMSC_MASK_RXIM; + + /* Enable target chip, if not already active */ + if (!pl022->next_msg_cs_active) + pl022->cur_chip->cs_control(SSP_CHIP_SELECT); - /* Enable target chip */ - pl022->cur_chip->cs_control(SSP_CHIP_SELECT); if (set_up_next_transfer(pl022, pl022->cur_transfer)) { /* Error path */ pl022->cur_msg->state = STATE_ERROR; @@ -1442,7 +1445,8 @@ static void do_polling_transfer(struct pl022 *pl022) } else { /* STATE_START */ message->state = STATE_RUNNING; - pl022->cur_chip->cs_control(SSP_CHIP_SELECT); + if (!pl022->next_msg_cs_active) + pl022->cur_chip->cs_control(SSP_CHIP_SELECT); } /* Configuration Changing Per Transfer */ @@ -1504,14 +1508,28 @@ static void pump_messages(struct work_struct *work) struct pl022 *pl022 = container_of(work, struct pl022, pump_messages); unsigned long flags; + bool was_busy = false; /* Lock queue and check for queue work */ spin_lock_irqsave(&pl022->queue_lock, flags); if (list_empty(&pl022->queue) || !pl022->running) { + if (pl022->busy) { + /* nothing more to do - disable spi/ssp and power off */ + writew((readw(SSP_CR1(pl022->virtbase)) & + (~SSP_CR1_MASK_SSE)), SSP_CR1(pl022->virtbase)); + + if (pl022->master_info->autosuspend_delay > 0) { + pm_runtime_mark_last_busy(&pl022->adev->dev); + pm_runtime_put_autosuspend(&pl022->adev->dev); + } else { + pm_runtime_put(&pl022->adev->dev); + } + } pl022->busy = false; spin_unlock_irqrestore(&pl022->queue_lock, flags); return; } + /* Make sure we are not already running a message */ if (pl022->cur_msg) { spin_unlock_irqrestore(&pl022->queue_lock, flags); @@ -1522,7 +1540,10 @@ static void pump_messages(struct work_struct *work) list_entry(pl022->queue.next, struct spi_message, queue); list_del_init(&pl022->cur_msg->queue); - pl022->busy = true; + if (pl022->busy) + was_busy = true; + else + pl022->busy = true; spin_unlock_irqrestore(&pl022->queue_lock, flags); /* Initial message state */ @@ -1532,12 +1553,14 @@ static void pump_messages(struct work_struct *work) /* Setup the SPI using the per chip configuration */ pl022->cur_chip = spi_get_ctldata(pl022->cur_msg->spi); - /* - * We enable the core voltage and clocks here, then the clocks - * and core will be disabled when giveback() is called in each method - * (poll/interrupt/DMA) - */ - pm_runtime_get_sync(&pl022->adev->dev); + if (!was_busy) + /* + * We enable the core voltage and clocks here, then the clocks + * and core will be disabled when this workqueue is run again + * and there is no more work to be done. + */ + pm_runtime_get_sync(&pl022->adev->dev); + restore_state(pl022); flush(pl022); @@ -1582,6 +1605,7 @@ static int start_queue(struct pl022 *pl022) pl022->cur_msg = NULL; pl022->cur_transfer = NULL; pl022->cur_chip = NULL; + pl022->next_msg_cs_active = false; spin_unlock_irqrestore(&pl022->queue_lock, flags); queue_work(pl022->workqueue, &pl022->pump_messages); @@ -1881,7 +1905,7 @@ static int pl022_setup(struct spi_device *spi) { struct pl022_config_chip const *chip_info; struct chip_data *chip; - struct ssp_clock_params clk_freq = {0, }; + struct ssp_clock_params clk_freq = { .cpsdvsr = 0, .scr = 0}; int status = 0; struct pl022 *pl022 = spi_master_get_devdata(spi->master); unsigned int bits = spi->bits_per_word; @@ -2231,7 +2255,17 @@ pl022_probe(struct amba_device *adev, const struct amba_id *id) dev_dbg(dev, "probe succeeded\n"); /* let runtime pm put suspend */ - pm_runtime_put(dev); + if (platform_info->autosuspend_delay > 0) { + dev_info(&adev->dev, + "will use autosuspend for runtime pm, delay %dms\n", + platform_info->autosuspend_delay); + pm_runtime_set_autosuspend_delay(dev, + platform_info->autosuspend_delay); + pm_runtime_use_autosuspend(dev); + pm_runtime_put_autosuspend(dev); + } else { + pm_runtime_put(dev); + } return 0; err_spi_register: @@ -2305,11 +2339,6 @@ static int pl022_suspend(struct device *dev) return status; } - amba_vcore_enable(pl022->adev); - amba_pclk_enable(pl022->adev); - load_ssp_default_config(pl022); - amba_pclk_disable(pl022->adev); - amba_vcore_disable(pl022->adev); dev_dbg(dev, "suspended\n"); return 0; } @@ -2432,6 +2461,8 @@ static struct amba_id pl022_ids[] = { { 0, 0 }, }; +MODULE_DEVICE_TABLE(amba, pl022_ids); + static struct amba_driver pl022_driver = { .drv = { .name = "ssp-pl022", diff --git a/drivers/spi/spi-topcliff-pch.c b/drivers/spi/spi-topcliff-pch.c index 6a80749..7086583 100644 --- a/drivers/spi/spi-topcliff-pch.c +++ b/drivers/spi/spi-topcliff-pch.c @@ -1,7 +1,7 @@ /* * SPI bus driver for the Topcliff PCH used by Intel SoCs * - * Copyright (C) 2010 OKI SEMICONDUCTOR Co., LTD. + * Copyright (C) 2011 LAPIS Semiconductor Co., Ltd. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -95,16 +95,18 @@ #define PCH_CLOCK_HZ 50000000 #define PCH_MAX_SPBR 1023 -/* Definition for ML7213 by OKI SEMICONDUCTOR */ +/* Definition for ML7213/ML7223/ML7831 by LAPIS Semiconductor */ #define PCI_VENDOR_ID_ROHM 0x10DB #define PCI_DEVICE_ID_ML7213_SPI 0x802c #define PCI_DEVICE_ID_ML7223_SPI 0x800F +#define PCI_DEVICE_ID_ML7831_SPI 0x8816 /* * Set the number of SPI instance max * Intel EG20T PCH : 1ch - * OKI SEMICONDUCTOR ML7213 IOH : 2ch - * OKI SEMICONDUCTOR ML7223 IOH : 1ch + * LAPIS Semiconductor ML7213 IOH : 2ch + * LAPIS Semiconductor ML7223 IOH : 1ch + * LAPIS Semiconductor ML7831 IOH : 1ch */ #define PCH_SPI_MAX_DEV 2 @@ -218,6 +220,7 @@ static struct pci_device_id pch_spi_pcidev_id[] = { { PCI_VDEVICE(INTEL, PCI_DEVICE_ID_GE_SPI), 1, }, { PCI_VDEVICE(ROHM, PCI_DEVICE_ID_ML7213_SPI), 2, }, { PCI_VDEVICE(ROHM, PCI_DEVICE_ID_ML7223_SPI), 1, }, + { PCI_VDEVICE(ROHM, PCI_DEVICE_ID_ML7831_SPI), 1, }, { } }; @@ -1753,4 +1756,4 @@ MODULE_PARM_DESC(use_dma, "to use DMA for data transfers pass 1 else 0; default 1"); MODULE_LICENSE("GPL"); -MODULE_DESCRIPTION("Intel EG20T PCH/OKI SEMICONDUCTOR ML7xxx IOH SPI Driver"); +MODULE_DESCRIPTION("Intel EG20T PCH/LAPIS Semiconductor ML7xxx IOH SPI Driver"); diff --git a/drivers/spi/spi.c b/drivers/spi/spi.c index 77eae99..b2ccdea 100644 --- a/drivers/spi/spi.c +++ b/drivers/spi/spi.c @@ -319,7 +319,7 @@ struct spi_device *spi_alloc_device(struct spi_master *master) } spi->master = master; - spi->dev.parent = dev; + spi->dev.parent = &master->dev; spi->dev.bus = &spi_bus_type; spi->dev.release = spidev_release; device_initialize(&spi->dev); |