diff options
author | Linus Torvalds <torvalds@linux-foundation.org> | 2016-03-15 21:07:33 -0700 |
---|---|---|
committer | Linus Torvalds <torvalds@linux-foundation.org> | 2016-03-15 21:07:33 -0700 |
commit | ff280e3639548fc8c366f6e4bd471e715ac590c7 (patch) | |
tree | 91c960af9702419a9e28f6bdc2f9cdb3921deb40 /drivers/spi/spi-pxa2xx.c | |
parent | 5ca5446ec5ba5e79a6f271cd026bb153d6850fcc (diff) | |
parent | c508709bcffb644afbf5e5016fc7c90bf80c30ff (diff) |
Merge tag 'spi-v4.6' of git://git.kernel.org/pub/scm/linux/kernel/git/broonie/spi
Pull spi updates from Mark Brown:
"Not the biggest set of changes for SPI but a bit of a pickup in
activity on the core:
- Support for memory mapped read from flash devices via a SPI
controller.
- The beginnings of a message rewriting framework in the core which
should in time allow us to support transforming messages to work
around the limits of controllers or optimise the performance for
controllers transparently to calling drivers.
- Updates to the PXA2xx, the main functional change being to improve
the ACPI support.
- A new driver for the Analog Devices AXI SPI engine"
* tag 'spi-v4.6' of git://git.kernel.org/pub/scm/linux/kernel/git/broonie/spi: (66 commits)
spi: Add gfp parameter to kernel-doc to fix build warning
spi: Fix htmldocs build error due struct spi_replaced_transfers
spi: rockchip: covert rsd_nsecs to u32 type
spi: rockchip: header file cleanup
spi: xilinx: Add devicetree binding for spi-xilinx
spi: respect the maximum segment size of DMA device
spi: rockchip: check requesting dma channel with EPROBE_DEFER
spi: rockchip: migrate to dmaengine_terminate_async
spi: rockchip: check return value of dmaengine_prep_slave_sg
spi: core: Fix deadlock when sending messages
spi/rockchip: fix endian mode for 16-bit transfers
spi/rockchip: Make sure spi clk is on in rockchip_spi_set_cs
spi: pxa2xx: Use newer more explicit DMAengine terminate API
spi: pxa2xx: Add support for Intel Broxton B-Step
spi: lp-8841: return correct error code from probe
spi: imx: drop bogus tests for rx/tx bufs in DMA transfer
spi: imx: set MX51_ECSPI_CTRL_SMC bit in setup function
spi: imx: make some register defines simpler
spi: imx: remove unnecessary bit clearing in mx51_ecspi_config
spi: imx: add support for all SPI word width for DMA
...
Diffstat (limited to 'drivers/spi/spi-pxa2xx.c')
-rw-r--r-- | drivers/spi/spi-pxa2xx.c | 151 |
1 files changed, 109 insertions, 42 deletions
diff --git a/drivers/spi/spi-pxa2xx.c b/drivers/spi/spi-pxa2xx.c index ab9914ad8365..85e59a406a4c 100644 --- a/drivers/spi/spi-pxa2xx.c +++ b/drivers/spi/spi-pxa2xx.c @@ -65,8 +65,6 @@ MODULE_ALIAS("platform:pxa2xx-spi"); #define LPSS_GENERAL_REG_RXTO_HOLDOFF_DISABLE BIT(24) #define LPSS_CS_CONTROL_SW_MODE BIT(0) #define LPSS_CS_CONTROL_CS_HIGH BIT(1) -#define LPSS_CS_CONTROL_CS_SEL_SHIFT 8 -#define LPSS_CS_CONTROL_CS_SEL_MASK (3 << LPSS_CS_CONTROL_CS_SEL_SHIFT) #define LPSS_CAPS_CS_EN_SHIFT 9 #define LPSS_CAPS_CS_EN_MASK (0xf << LPSS_CAPS_CS_EN_SHIFT) @@ -82,6 +80,10 @@ struct lpss_config { u32 rx_threshold; u32 tx_threshold_lo; u32 tx_threshold_hi; + /* Chip select control */ + unsigned cs_sel_shift; + unsigned cs_sel_mask; + unsigned cs_num; }; /* Keep these sorted with enum pxa_ssp_type */ @@ -106,6 +108,19 @@ static const struct lpss_config lpss_platforms[] = { .tx_threshold_lo = 160, .tx_threshold_hi = 224, }, + { /* LPSS_BSW_SSP */ + .offset = 0x400, + .reg_general = 0x08, + .reg_ssp = 0x0c, + .reg_cs_ctrl = 0x18, + .reg_capabilities = -1, + .rx_threshold = 64, + .tx_threshold_lo = 160, + .tx_threshold_hi = 224, + .cs_sel_shift = 2, + .cs_sel_mask = 1 << 2, + .cs_num = 2, + }, { /* LPSS_SPT_SSP */ .offset = 0x200, .reg_general = -1, @@ -125,6 +140,8 @@ static const struct lpss_config lpss_platforms[] = { .rx_threshold = 1, .tx_threshold_lo = 16, .tx_threshold_hi = 48, + .cs_sel_shift = 8, + .cs_sel_mask = 3 << 8, }, }; @@ -139,6 +156,7 @@ static bool is_lpss_ssp(const struct driver_data *drv_data) switch (drv_data->ssp_type) { case LPSS_LPT_SSP: case LPSS_BYT_SSP: + case LPSS_BSW_SSP: case LPSS_SPT_SSP: case LPSS_BXT_SSP: return true; @@ -288,37 +306,50 @@ static void lpss_ssp_setup(struct driver_data *drv_data) } } +static void lpss_ssp_select_cs(struct driver_data *drv_data, + const struct lpss_config *config) +{ + u32 value, cs; + + if (!config->cs_sel_mask) + return; + + value = __lpss_ssp_read_priv(drv_data, config->reg_cs_ctrl); + + cs = drv_data->cur_msg->spi->chip_select; + cs <<= config->cs_sel_shift; + if (cs != (value & config->cs_sel_mask)) { + /* + * When switching another chip select output active the + * output must be selected first and wait 2 ssp_clk cycles + * before changing state to active. Otherwise a short + * glitch will occur on the previous chip select since + * output select is latched but state control is not. + */ + value &= ~config->cs_sel_mask; + value |= cs; + __lpss_ssp_write_priv(drv_data, + config->reg_cs_ctrl, value); + ndelay(1000000000 / + (drv_data->master->max_speed_hz / 2)); + } +} + static void lpss_ssp_cs_control(struct driver_data *drv_data, bool enable) { const struct lpss_config *config; - u32 value, cs; + u32 value; config = lpss_get_config(drv_data); + if (enable) + lpss_ssp_select_cs(drv_data, config); + value = __lpss_ssp_read_priv(drv_data, config->reg_cs_ctrl); - if (enable) { - cs = drv_data->cur_msg->spi->chip_select; - cs <<= LPSS_CS_CONTROL_CS_SEL_SHIFT; - if (cs != (value & LPSS_CS_CONTROL_CS_SEL_MASK)) { - /* - * When switching another chip select output active - * the output must be selected first and wait 2 ssp_clk - * cycles before changing state to active. Otherwise - * a short glitch will occur on the previous chip - * select since output select is latched but state - * control is not. - */ - value &= ~LPSS_CS_CONTROL_CS_SEL_MASK; - value |= cs; - __lpss_ssp_write_priv(drv_data, - config->reg_cs_ctrl, value); - ndelay(1000000000 / - (drv_data->master->max_speed_hz / 2)); - } + if (enable) value &= ~LPSS_CS_CONTROL_CS_HIGH; - } else { + else value |= LPSS_CS_CONTROL_CS_HIGH; - } __lpss_ssp_write_priv(drv_data, config->reg_cs_ctrl, value); } @@ -496,6 +527,7 @@ static void giveback(struct driver_data *drv_data) { struct spi_transfer* last_transfer; struct spi_message *msg; + unsigned long timeout; msg = drv_data->cur_msg; drv_data->cur_msg = NULL; @@ -508,6 +540,12 @@ static void giveback(struct driver_data *drv_data) if (last_transfer->delay_usecs) udelay(last_transfer->delay_usecs); + /* Wait until SSP becomes idle before deasserting the CS */ + timeout = jiffies + msecs_to_jiffies(10); + while (pxa2xx_spi_read(drv_data, SSSR) & SSSR_BSY && + !time_after(jiffies, timeout)) + cpu_relax(); + /* Drop chip select UNLESS cs_change is true or we are returning * a message with an error, or next message is for another chip */ @@ -572,7 +610,7 @@ static void int_error_stop(struct driver_data *drv_data, const char* msg) static void int_transfer_complete(struct driver_data *drv_data) { - /* Stop SSP */ + /* Clear and disable interrupts */ write_SSSR_CS(drv_data, drv_data->clear_sr); reset_sccr1(drv_data); if (!pxa25x_ssp_comp(drv_data)) @@ -957,8 +995,6 @@ static void pump_transfers(unsigned long data) drv_data->tx_end = drv_data->tx + transfer->len; drv_data->rx = transfer->rx_buf; drv_data->rx_end = drv_data->rx + transfer->len; - drv_data->rx_dma = transfer->rx_dma; - drv_data->tx_dma = transfer->tx_dma; drv_data->len = transfer->len; drv_data->write = drv_data->tx ? chip->write : null_writer; drv_data->read = drv_data->rx ? chip->read : null_reader; @@ -1001,19 +1037,6 @@ static void pump_transfers(unsigned long data) "pump_transfers: DMA burst size reduced to match bits_per_word\n"); } - /* NOTE: PXA25x_SSP _could_ use external clocking ... */ - cr0 = pxa2xx_configure_sscr0(drv_data, clk_div, bits); - if (!pxa25x_ssp_comp(drv_data)) - dev_dbg(&message->spi->dev, "%u Hz actual, %s\n", - drv_data->master->max_speed_hz - / (1 + ((cr0 & SSCR0_SCR(0xfff)) >> 8)), - chip->enable_dma ? "DMA" : "PIO"); - else - dev_dbg(&message->spi->dev, "%u Hz actual, %s\n", - drv_data->master->max_speed_hz / 2 - / (1 + ((cr0 & SSCR0_SCR(0x0ff)) >> 8)), - chip->enable_dma ? "DMA" : "PIO"); - message->state = RUNNING_STATE; drv_data->dma_mapped = 0; @@ -1040,6 +1063,19 @@ static void pump_transfers(unsigned long data) write_SSSR_CS(drv_data, drv_data->clear_sr); } + /* NOTE: PXA25x_SSP _could_ use external clocking ... */ + cr0 = pxa2xx_configure_sscr0(drv_data, clk_div, bits); + if (!pxa25x_ssp_comp(drv_data)) + dev_dbg(&message->spi->dev, "%u Hz actual, %s\n", + drv_data->master->max_speed_hz + / (1 + ((cr0 & SSCR0_SCR(0xfff)) >> 8)), + drv_data->dma_mapped ? "DMA" : "PIO"); + else + dev_dbg(&message->spi->dev, "%u Hz actual, %s\n", + drv_data->master->max_speed_hz / 2 + / (1 + ((cr0 & SSCR0_SCR(0x0ff)) >> 8)), + drv_data->dma_mapped ? "DMA" : "PIO"); + if (is_lpss_ssp(drv_data)) { if ((pxa2xx_spi_read(drv_data, SSIRF) & 0xff) != chip->lpss_rx_threshold) @@ -1166,6 +1202,7 @@ static int setup(struct spi_device *spi) break; case LPSS_LPT_SSP: case LPSS_BYT_SSP: + case LPSS_BSW_SSP: case LPSS_SPT_SSP: case LPSS_BXT_SSP: config = lpss_get_config(drv_data); @@ -1313,7 +1350,7 @@ static const struct acpi_device_id pxa2xx_spi_acpi_match[] = { { "INT3430", LPSS_LPT_SSP }, { "INT3431", LPSS_LPT_SSP }, { "80860F0E", LPSS_BYT_SSP }, - { "8086228E", LPSS_BYT_SSP }, + { "8086228E", LPSS_BSW_SSP }, { }, }; MODULE_DEVICE_TABLE(acpi, pxa2xx_spi_acpi_match); @@ -1347,10 +1384,14 @@ static const struct pci_device_id pxa2xx_spi_pci_compound_match[] = { /* SPT-H */ { PCI_VDEVICE(INTEL, 0xa129), LPSS_SPT_SSP }, { PCI_VDEVICE(INTEL, 0xa12a), LPSS_SPT_SSP }, - /* BXT */ + /* BXT A-Step */ { PCI_VDEVICE(INTEL, 0x0ac2), LPSS_BXT_SSP }, { PCI_VDEVICE(INTEL, 0x0ac4), LPSS_BXT_SSP }, { PCI_VDEVICE(INTEL, 0x0ac6), LPSS_BXT_SSP }, + /* BXT B-Step */ + { PCI_VDEVICE(INTEL, 0x1ac2), LPSS_BXT_SSP }, + { PCI_VDEVICE(INTEL, 0x1ac4), LPSS_BXT_SSP }, + { PCI_VDEVICE(INTEL, 0x1ac6), LPSS_BXT_SSP }, /* APL */ { PCI_VDEVICE(INTEL, 0x5ac2), LPSS_BXT_SSP }, { PCI_VDEVICE(INTEL, 0x5ac4), LPSS_BXT_SSP }, @@ -1438,6 +1479,29 @@ pxa2xx_spi_init_pdata(struct platform_device *pdev) } #endif +static int pxa2xx_spi_fw_translate_cs(struct spi_master *master, unsigned cs) +{ + struct driver_data *drv_data = spi_master_get_devdata(master); + + if (has_acpi_companion(&drv_data->pdev->dev)) { + switch (drv_data->ssp_type) { + /* + * For Atoms the ACPI DeviceSelection used by the Windows + * driver starts from 1 instead of 0 so translate it here + * to match what Linux expects. + */ + case LPSS_BYT_SSP: + case LPSS_BSW_SSP: + return cs - 1; + + default: + break; + } + } + + return cs; +} + static int pxa2xx_spi_probe(struct platform_device *pdev) { struct device *dev = &pdev->dev; @@ -1490,6 +1554,7 @@ static int pxa2xx_spi_probe(struct platform_device *pdev) master->setup = setup; master->transfer_one_message = pxa2xx_spi_transfer_one_message; master->unprepare_transfer_hardware = pxa2xx_spi_unprepare_transfer; + master->fw_translate_cs = pxa2xx_spi_fw_translate_cs; master->auto_runtime_pm = true; drv_data->ssp_type = ssp->type; @@ -1576,6 +1641,8 @@ static int pxa2xx_spi_probe(struct platform_device *pdev) tmp &= LPSS_CAPS_CS_EN_MASK; tmp >>= LPSS_CAPS_CS_EN_SHIFT; platform_info->num_chipselect = ffz(tmp); + } else if (config->cs_num) { + platform_info->num_chipselect = config->cs_num; } } master->num_chipselect = platform_info->num_chipselect; |