diff --git a/Documentation/devicetree/bindings/spi/fsl-spi.txt b/Documentation/devicetree/bindings/spi/fsl-spi.txt index b032dd76e9d2..a2331372068c 100644 --- a/Documentation/devicetree/bindings/spi/fsl-spi.txt +++ b/Documentation/devicetree/bindings/spi/fsl-spi.txt @@ -42,6 +42,10 @@ Required properties: - interrupts : should contain eSPI interrupt, the device has one interrupt. - fsl,espi-num-chipselects : the number of the chipselect signals. +Optional properties: +- fsl,csbef: chip select assertion time in bits before frame starts +- fsl,csaft: chip select negation time in bits after frame ends + Example: spi@110000 { #address-cells = <1>; @@ -51,4 +55,6 @@ Example: interrupts = <53 0x2>; interrupt-parent = <&mpic>; fsl,espi-num-chipselects = <4>; + fsl,csbef = <1>; + fsl,csaft = <1>; }; diff --git a/Documentation/devicetree/bindings/spi/spi-dw.txt b/Documentation/devicetree/bindings/spi/spi-dw.txt new file mode 100644 index 000000000000..7b63ed601990 --- /dev/null +++ b/Documentation/devicetree/bindings/spi/spi-dw.txt @@ -0,0 +1,24 @@ +Synopsys DesignWare SPI master + +Required properties: +- compatible: should be "snps,designware-spi" +- #address-cells: see spi-bus.txt +- #size-cells: see spi-bus.txt +- reg: address and length of the spi master registers +- interrupts: should contain one interrupt +- clocks: spi clock phandle +- num-cs: see spi-bus.txt + +Optional properties: +- cs-gpios: see spi-bus.txt + +Example: + +spi: spi@4020a000 { + compatible = "snps,designware-spi"; + interrupts = <11 1>; + reg = <0x4020a000 0x1000>; + clocks = <&pclk>; + num-cs = <2>; + cs-gpios = <&banka 0 0>; +}; diff --git a/drivers/spi/spi-dw-mmio.c b/drivers/spi/spi-dw-mmio.c index 1492f5ee9aaa..a5cba14ac3d2 100644 --- a/drivers/spi/spi-dw-mmio.c +++ b/drivers/spi/spi-dw-mmio.c @@ -16,6 +16,7 @@ #include #include #include +#include #include "spi-dw.h" @@ -70,6 +71,27 @@ static int dw_spi_mmio_probe(struct platform_device *pdev) dws->num_cs = 4; dws->max_freq = clk_get_rate(dwsmmio->clk); + if (pdev->dev.of_node) { + int i; + + for (i = 0; i < dws->num_cs; i++) { + int cs_gpio = of_get_named_gpio(pdev->dev.of_node, + "cs-gpios", i); + + if (cs_gpio == -EPROBE_DEFER) { + ret = cs_gpio; + goto out; + } + + if (gpio_is_valid(cs_gpio)) { + ret = devm_gpio_request(&pdev->dev, cs_gpio, + dev_name(&pdev->dev)); + if (ret) + goto out; + } + } + } + ret = dw_spi_add_host(&pdev->dev, dws); if (ret) goto out; diff --git a/drivers/spi/spi-dw.c b/drivers/spi/spi-dw.c index 712ac5629cd4..29f33143b795 100644 --- a/drivers/spi/spi-dw.c +++ b/drivers/spi/spi-dw.c @@ -24,6 +24,7 @@ #include #include #include +#include #include "spi-dw.h" @@ -36,12 +37,6 @@ #define DONE_STATE ((void *)2) #define ERROR_STATE ((void *)-1) -#define QUEUE_RUNNING 0 -#define QUEUE_STOPPED 1 - -#define MRST_SPI_DEASSERT 0 -#define MRST_SPI_ASSERT 1 - /* Slave spi_dev related */ struct chip_data { u16 cr0; @@ -263,28 +258,22 @@ static int map_dma_buffers(struct dw_spi *dws) static void giveback(struct dw_spi *dws) { struct spi_transfer *last_transfer; - unsigned long flags; struct spi_message *msg; - spin_lock_irqsave(&dws->lock, flags); msg = dws->cur_msg; dws->cur_msg = NULL; dws->cur_transfer = NULL; dws->prev_chip = dws->cur_chip; dws->cur_chip = NULL; dws->dma_mapped = 0; - queue_work(dws->workqueue, &dws->pump_messages); - spin_unlock_irqrestore(&dws->lock, flags); last_transfer = list_last_entry(&msg->transfers, struct spi_transfer, transfer_list); - if (!last_transfer->cs_change && dws->cs_control) - dws->cs_control(MRST_SPI_DEASSERT); + if (!last_transfer->cs_change) + spi_chip_sel(dws, dws->cur_msg->spi, 0); - msg->state = NULL; - if (msg->complete) - msg->complete(msg->context); + spi_finalize_current_message(dws->master); } static void int_error_stop(struct dw_spi *dws, const char *msg) @@ -502,7 +491,7 @@ static void pump_transfers(unsigned long data) dw_writew(dws, DW_SPI_CTRL0, cr0); spi_set_clk(dws, clk_div ? clk_div : chip->clk_div); - spi_chip_sel(dws, spi->chip_select); + spi_chip_sel(dws, spi, 1); /* Set the interrupt mask, for poll mode just disable all int */ spi_mask_intr(dws, 0xff); @@ -529,30 +518,12 @@ early_exit: return; } -static void pump_messages(struct work_struct *work) +static int dw_spi_transfer_one_message(struct spi_master *master, + struct spi_message *msg) { - struct dw_spi *dws = - container_of(work, struct dw_spi, pump_messages); - unsigned long flags; - - /* Lock queue and check for queue work */ - spin_lock_irqsave(&dws->lock, flags); - if (list_empty(&dws->queue) || dws->run == QUEUE_STOPPED) { - dws->busy = 0; - spin_unlock_irqrestore(&dws->lock, flags); - return; - } - - /* Make sure we are not already running a message */ - if (dws->cur_msg) { - spin_unlock_irqrestore(&dws->lock, flags); - return; - } - - /* Extract head of queue */ - dws->cur_msg = list_entry(dws->queue.next, struct spi_message, queue); - list_del_init(&dws->cur_msg->queue); + struct dw_spi *dws = spi_master_get_devdata(master); + dws->cur_msg = msg; /* Initial message state*/ dws->cur_msg->state = START_STATE; dws->cur_transfer = list_entry(dws->cur_msg->transfers.next, @@ -560,46 +531,9 @@ static void pump_messages(struct work_struct *work) transfer_list); dws->cur_chip = spi_get_ctldata(dws->cur_msg->spi); - /* Mark as busy and launch transfers */ + /* Launch transfers */ tasklet_schedule(&dws->pump_transfers); - dws->busy = 1; - spin_unlock_irqrestore(&dws->lock, flags); -} - -/* spi_device use this to queue in their spi_msg */ -static int dw_spi_transfer(struct spi_device *spi, struct spi_message *msg) -{ - struct dw_spi *dws = spi_master_get_devdata(spi->master); - unsigned long flags; - - spin_lock_irqsave(&dws->lock, flags); - - if (dws->run == QUEUE_STOPPED) { - spin_unlock_irqrestore(&dws->lock, flags); - return -ESHUTDOWN; - } - - msg->actual_length = 0; - msg->status = -EINPROGRESS; - msg->state = START_STATE; - - list_add_tail(&msg->queue, &dws->queue); - - if (dws->run == QUEUE_RUNNING && !dws->busy) { - - if (dws->cur_transfer || dws->cur_msg) - queue_work(dws->workqueue, - &dws->pump_messages); - else { - /* If no other data transaction in air, just go */ - spin_unlock_irqrestore(&dws->lock, flags); - pump_messages(&dws->pump_messages); - return 0; - } - } - - spin_unlock_irqrestore(&dws->lock, flags); return 0; } @@ -608,6 +542,7 @@ static int dw_spi_setup(struct spi_device *spi) { struct dw_spi_chip *chip_info = NULL; struct chip_data *chip; + int ret; /* Only alloc on first setup */ chip = spi_get_ctldata(spi); @@ -661,81 +596,13 @@ static int dw_spi_setup(struct spi_device *spi) | (spi->mode << SPI_MODE_OFFSET) | (chip->tmode << SPI_TMOD_OFFSET); - return 0; -} - -static int init_queue(struct dw_spi *dws) -{ - INIT_LIST_HEAD(&dws->queue); - spin_lock_init(&dws->lock); - - dws->run = QUEUE_STOPPED; - dws->busy = 0; - - tasklet_init(&dws->pump_transfers, - pump_transfers, (unsigned long)dws); - - INIT_WORK(&dws->pump_messages, pump_messages); - dws->workqueue = create_singlethread_workqueue( - dev_name(dws->master->dev.parent)); - if (dws->workqueue == NULL) - return -EBUSY; - - return 0; -} - -static int start_queue(struct dw_spi *dws) -{ - unsigned long flags; - - spin_lock_irqsave(&dws->lock, flags); - - if (dws->run == QUEUE_RUNNING || dws->busy) { - spin_unlock_irqrestore(&dws->lock, flags); - return -EBUSY; + if (gpio_is_valid(spi->cs_gpio)) { + ret = gpio_direction_output(spi->cs_gpio, + !(spi->mode & SPI_CS_HIGH)); + if (ret) + return ret; } - dws->run = QUEUE_RUNNING; - dws->cur_msg = NULL; - dws->cur_transfer = NULL; - dws->cur_chip = NULL; - dws->prev_chip = NULL; - spin_unlock_irqrestore(&dws->lock, flags); - - queue_work(dws->workqueue, &dws->pump_messages); - - return 0; -} - -static int stop_queue(struct dw_spi *dws) -{ - unsigned long flags; - unsigned limit = 50; - int status = 0; - - spin_lock_irqsave(&dws->lock, flags); - dws->run = QUEUE_STOPPED; - while ((!list_empty(&dws->queue) || dws->busy) && limit--) { - spin_unlock_irqrestore(&dws->lock, flags); - msleep(10); - spin_lock_irqsave(&dws->lock, flags); - } - - if (!list_empty(&dws->queue) || dws->busy) - status = -EBUSY; - spin_unlock_irqrestore(&dws->lock, flags); - - return status; -} - -static int destroy_queue(struct dw_spi *dws) -{ - int status; - - status = stop_queue(dws); - if (status != 0) - return status; - destroy_workqueue(dws->workqueue); return 0; } @@ -794,7 +661,7 @@ int dw_spi_add_host(struct device *dev, struct dw_spi *dws) master->bus_num = dws->bus_num; master->num_chipselect = dws->num_cs; master->setup = dw_spi_setup; - master->transfer = dw_spi_transfer; + master->transfer_one_message = dw_spi_transfer_one_message; master->max_speed_hz = dws->max_freq; /* Basic HW init */ @@ -808,33 +675,21 @@ int dw_spi_add_host(struct device *dev, struct dw_spi *dws) } } - /* Initial and start queue */ - ret = init_queue(dws); - if (ret) { - dev_err(&master->dev, "problem initializing queue\n"); - goto err_diable_hw; - } - ret = start_queue(dws); - if (ret) { - dev_err(&master->dev, "problem starting queue\n"); - goto err_diable_hw; - } + tasklet_init(&dws->pump_transfers, pump_transfers, (unsigned long)dws); spi_master_set_devdata(master, dws); ret = devm_spi_register_master(dev, master); if (ret) { dev_err(&master->dev, "problem registering spi master\n"); - goto err_queue_alloc; + goto err_dma_exit; } mrst_spi_debugfs_init(dws); return 0; -err_queue_alloc: - destroy_queue(dws); +err_dma_exit: if (dws->dma_ops && dws->dma_ops->dma_exit) dws->dma_ops->dma_exit(dws); -err_diable_hw: spi_enable_chip(dws, 0); err_free_master: spi_master_put(master); @@ -844,18 +699,10 @@ EXPORT_SYMBOL_GPL(dw_spi_add_host); void dw_spi_remove_host(struct dw_spi *dws) { - int status = 0; - if (!dws) return; mrst_spi_debugfs_remove(dws); - /* Remove the queue */ - status = destroy_queue(dws); - if (status != 0) - dev_err(&dws->master->dev, - "dw_spi_remove: workqueue will not complete, message memory not freed\n"); - if (dws->dma_ops && dws->dma_ops->dma_exit) dws->dma_ops->dma_exit(dws); spi_enable_chip(dws, 0); @@ -868,7 +715,7 @@ int dw_spi_suspend_host(struct dw_spi *dws) { int ret = 0; - ret = stop_queue(dws); + ret = spi_master_suspend(dws->master); if (ret) return ret; spi_enable_chip(dws, 0); @@ -882,7 +729,7 @@ int dw_spi_resume_host(struct dw_spi *dws) int ret; spi_hw_init(dws); - ret = start_queue(dws); + ret = spi_master_resume(dws->master); if (ret) dev_err(&dws->master->dev, "fail to start queue (%d)\n", ret); return ret; diff --git a/drivers/spi/spi-dw.h b/drivers/spi/spi-dw.h index 587643dae11e..6d2acad34f64 100644 --- a/drivers/spi/spi-dw.h +++ b/drivers/spi/spi-dw.h @@ -3,6 +3,7 @@ #include #include +#include /* Register offsets */ #define DW_SPI_CTRL0 0x00 @@ -104,14 +105,6 @@ struct dw_spi { u16 bus_num; u16 num_cs; /* supported slave numbers */ - /* Driver message queue */ - struct workqueue_struct *workqueue; - struct work_struct pump_messages; - spinlock_t lock; - struct list_head queue; - int busy; - int run; - /* Message Transfer pump */ struct tasklet_struct pump_transfers; @@ -186,15 +179,20 @@ static inline void spi_set_clk(struct dw_spi *dws, u16 div) dw_writel(dws, DW_SPI_BAUDR, div); } -static inline void spi_chip_sel(struct dw_spi *dws, u16 cs) +static inline void spi_chip_sel(struct dw_spi *dws, struct spi_device *spi, + int active) { - if (cs > dws->num_cs) - return; + u16 cs = spi->chip_select; + int gpio_val = active ? (spi->mode & SPI_CS_HIGH) : + !(spi->mode & SPI_CS_HIGH); if (dws->cs_control) - dws->cs_control(1); + dws->cs_control(active); + if (gpio_is_valid(spi->cs_gpio)) + gpio_set_value(spi->cs_gpio, gpio_val); - dw_writel(dws, DW_SPI_SER, 1 << cs); + if (active) + dw_writel(dws, DW_SPI_SER, 1 << cs); } /* Disable IRQ bits */ diff --git a/drivers/spi/spi-fsl-dspi.c b/drivers/spi/spi-fsl-dspi.c index d565eeee3bd8..5021ddf03f60 100644 --- a/drivers/spi/spi-fsl-dspi.c +++ b/drivers/spi/spi-fsl-dspi.c @@ -406,7 +406,7 @@ static irqreturn_t dspi_interrupt(int irq, void *dev_id) return IRQ_HANDLED; } -static struct of_device_id fsl_dspi_dt_ids[] = { +static const struct of_device_id fsl_dspi_dt_ids[] = { { .compatible = "fsl,vf610-dspi", .data = NULL, }, { /* sentinel */ } }; diff --git a/drivers/spi/spi-fsl-espi.c b/drivers/spi/spi-fsl-espi.c index e767f5831b9c..8ebd724e4c59 100644 --- a/drivers/spi/spi-fsl-espi.c +++ b/drivers/spi/spi-fsl-espi.c @@ -348,7 +348,7 @@ static void fsl_espi_cmd_trans(struct spi_message *m, } espi_trans->tx_buf = local_buf; - espi_trans->rx_buf = local_buf + espi_trans->n_tx; + espi_trans->rx_buf = local_buf; fsl_espi_do_trans(m, espi_trans); espi_trans->actual_length = espi_trans->len; @@ -397,7 +397,7 @@ static void fsl_espi_rw_trans(struct spi_message *m, espi_trans->n_rx = trans_len; espi_trans->len = trans_len + n_tx; espi_trans->tx_buf = local_buf; - espi_trans->rx_buf = local_buf + n_tx; + espi_trans->rx_buf = local_buf; fsl_espi_do_trans(m, espi_trans); memcpy(rx_buf + pos, espi_trans->rx_buf + n_tx, trans_len); @@ -458,7 +458,7 @@ static int fsl_espi_setup(struct spi_device *spi) return -EINVAL; if (!cs) { - cs = kzalloc(sizeof *cs, GFP_KERNEL); + cs = devm_kzalloc(&spi->dev, sizeof(*cs), GFP_KERNEL); if (!cs) return -ENOMEM; spi->controller_state = cs; @@ -586,8 +586,10 @@ static struct spi_master * fsl_espi_probe(struct device *dev, struct spi_master *master; struct mpc8xxx_spi *mpc8xxx_spi; struct fsl_espi_reg *reg_base; - u32 regval; - int i, ret = 0; + struct device_node *nc; + const __be32 *prop; + u32 regval, csmode; + int i, len, ret = 0; master = spi_alloc_master(dev, sizeof(struct mpc8xxx_spi)); if (!master) { @@ -634,8 +636,32 @@ static struct spi_master * fsl_espi_probe(struct device *dev, mpc8xxx_spi_write_reg(®_base->event, 0xffffffff); /* Init eSPI CS mode register */ - for (i = 0; i < pdata->max_chipselect; i++) - mpc8xxx_spi_write_reg(®_base->csmode[i], CSMODE_INIT_VAL); + for_each_available_child_of_node(master->dev.of_node, nc) { + /* get chip select */ + prop = of_get_property(nc, "reg", &len); + if (!prop || len < sizeof(*prop)) + continue; + i = be32_to_cpup(prop); + if (i < 0 || i >= pdata->max_chipselect) + continue; + + csmode = CSMODE_INIT_VAL; + /* check if CSBEF is set in device tree */ + prop = of_get_property(nc, "fsl,csbef", &len); + if (prop && len >= sizeof(*prop)) { + csmode &= ~(CSMODE_BEF(0xf)); + csmode |= CSMODE_BEF(be32_to_cpup(prop)); + } + /* check if CSAFT is set in device tree */ + prop = of_get_property(nc, "fsl,csaft", &len); + if (prop && len >= sizeof(*prop)) { + csmode &= ~(CSMODE_AFT(0xf)); + csmode |= CSMODE_AFT(be32_to_cpup(prop)); + } + mpc8xxx_spi_write_reg(®_base->csmode[i], csmode); + + dev_info(dev, "cs=%d, init_csmode=0x%x\n", i, csmode); + } /* Enable SPI interface */ regval = pdata->initial_spmode | SPMODE_INIT_VAL | SPMODE_ENABLE; diff --git a/drivers/spi/spi-fsl-lib.c b/drivers/spi/spi-fsl-lib.c index e5d45fca3551..95212ea96c8d 100644 --- a/drivers/spi/spi-fsl-lib.c +++ b/drivers/spi/spi-fsl-lib.c @@ -99,11 +99,6 @@ int mpc8xxx_spi_transfer(struct spi_device *spi, return 0; } -void mpc8xxx_spi_cleanup(struct spi_device *spi) -{ - kfree(spi->controller_state); -} - const char *mpc8xxx_spi_strmode(unsigned int flags) { if (flags & SPI_QE_CPU_MODE) { @@ -134,7 +129,6 @@ int mpc8xxx_spi_probe(struct device *dev, struct resource *mem, | SPI_LSB_FIRST | SPI_LOOP; master->transfer = mpc8xxx_spi_transfer; - master->cleanup = mpc8xxx_spi_cleanup; master->dev.of_node = dev->of_node; mpc8xxx_spi = spi_master_get_devdata(master); diff --git a/drivers/spi/spi-fsl-lib.h b/drivers/spi/spi-fsl-lib.h index 52db6936778e..2fcbfd01d109 100644 --- a/drivers/spi/spi-fsl-lib.h +++ b/drivers/spi/spi-fsl-lib.h @@ -124,7 +124,6 @@ extern struct mpc8xxx_spi_probe_info *to_of_pinfo( extern int mpc8xxx_spi_bufs(struct mpc8xxx_spi *mspi, struct spi_transfer *t, unsigned int len); extern int mpc8xxx_spi_transfer(struct spi_device *spi, struct spi_message *m); -extern void mpc8xxx_spi_cleanup(struct spi_device *spi); extern const char *mpc8xxx_spi_strmode(unsigned int flags); extern int mpc8xxx_spi_probe(struct device *dev, struct resource *mem, unsigned int irq); diff --git a/drivers/spi/spi-fsl-spi.c b/drivers/spi/spi-fsl-spi.c index b3e7775034db..98ccd231bf00 100644 --- a/drivers/spi/spi-fsl-spi.c +++ b/drivers/spi/spi-fsl-spi.c @@ -431,7 +431,7 @@ static int fsl_spi_setup(struct spi_device *spi) return -EINVAL; if (!cs) { - cs = kzalloc(sizeof *cs, GFP_KERNEL); + cs = devm_kzalloc(&spi->dev, sizeof(*cs), GFP_KERNEL); if (!cs) return -ENOMEM; spi->controller_state = cs; diff --git a/drivers/spi/spi-gpio.c b/drivers/spi/spi-gpio.c index 09823076df88..9f595535cf27 100644 --- a/drivers/spi/spi-gpio.c +++ b/drivers/spi/spi-gpio.c @@ -340,7 +340,7 @@ done: } #ifdef CONFIG_OF -static struct of_device_id spi_gpio_dt_ids[] = { +static const struct of_device_id spi_gpio_dt_ids[] = { { .compatible = "spi-gpio" }, {} }; diff --git a/drivers/spi/spi-qup.c b/drivers/spi/spi-qup.c index 943f973a59aa..fc1de86d3c8a 100644 --- a/drivers/spi/spi-qup.c +++ b/drivers/spi/spi-qup.c @@ -749,7 +749,7 @@ static int spi_qup_remove(struct platform_device *pdev) return 0; } -static struct of_device_id spi_qup_dt_match[] = { +static const struct of_device_id spi_qup_dt_match[] = { { .compatible = "qcom,spi-qup-v2.1.1", }, { .compatible = "qcom,spi-qup-v2.2.1", }, { } diff --git a/drivers/spi/spi-tegra114.c b/drivers/spi/spi-tegra114.c index 400649595505..e4a85ada861d 100644 --- a/drivers/spi/spi-tegra114.c +++ b/drivers/spi/spi-tegra114.c @@ -1012,7 +1012,7 @@ static irqreturn_t tegra_spi_isr(int irq, void *context_data) return IRQ_WAKE_THREAD; } -static struct of_device_id tegra_spi_of_match[] = { +static const struct of_device_id tegra_spi_of_match[] = { { .compatible = "nvidia,tegra114-spi", }, {} }; diff --git a/drivers/spi/spi-tegra20-sflash.c b/drivers/spi/spi-tegra20-sflash.c index 47869ea636e1..3548ce25c08f 100644 --- a/drivers/spi/spi-tegra20-sflash.c +++ b/drivers/spi/spi-tegra20-sflash.c @@ -419,7 +419,7 @@ static irqreturn_t tegra_sflash_isr(int irq, void *context_data) return handle_cpu_based_xfer(tsd); } -static struct of_device_id tegra_sflash_of_match[] = { +static const struct of_device_id tegra_sflash_of_match[] = { { .compatible = "nvidia,tegra20-sflash", }, {} }; diff --git a/drivers/spi/spi-tegra20-slink.c b/drivers/spi/spi-tegra20-slink.c index e3c1b93e45d1..0b9e32e9f493 100644 --- a/drivers/spi/spi-tegra20-slink.c +++ b/drivers/spi/spi-tegra20-slink.c @@ -1001,7 +1001,7 @@ static const struct tegra_slink_chip_data tegra20_spi_cdata = { .cs_hold_time = false, }; -static struct of_device_id tegra_slink_of_match[] = { +static const struct of_device_id tegra_slink_of_match[] = { { .compatible = "nvidia,tegra30-slink", .data = &tegra30_spi_cdata, }, { .compatible = "nvidia,tegra20-slink", .data = &tegra20_spi_cdata, }, {}