|
@@ -65,8 +65,6 @@ MODULE_ALIAS("platform:pxa2xx-spi");
|
|
#define LPSS_GENERAL_REG_RXTO_HOLDOFF_DISABLE BIT(24)
|
|
#define LPSS_GENERAL_REG_RXTO_HOLDOFF_DISABLE BIT(24)
|
|
#define LPSS_CS_CONTROL_SW_MODE BIT(0)
|
|
#define LPSS_CS_CONTROL_SW_MODE BIT(0)
|
|
#define LPSS_CS_CONTROL_CS_HIGH BIT(1)
|
|
#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_SHIFT 9
|
|
#define LPSS_CAPS_CS_EN_MASK (0xf << LPSS_CAPS_CS_EN_SHIFT)
|
|
#define LPSS_CAPS_CS_EN_MASK (0xf << LPSS_CAPS_CS_EN_SHIFT)
|
|
|
|
|
|
@@ -82,6 +80,10 @@ struct lpss_config {
|
|
u32 rx_threshold;
|
|
u32 rx_threshold;
|
|
u32 tx_threshold_lo;
|
|
u32 tx_threshold_lo;
|
|
u32 tx_threshold_hi;
|
|
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 */
|
|
/* 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_lo = 160,
|
|
.tx_threshold_hi = 224,
|
|
.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 */
|
|
{ /* LPSS_SPT_SSP */
|
|
.offset = 0x200,
|
|
.offset = 0x200,
|
|
.reg_general = -1,
|
|
.reg_general = -1,
|
|
@@ -125,6 +140,8 @@ static const struct lpss_config lpss_platforms[] = {
|
|
.rx_threshold = 1,
|
|
.rx_threshold = 1,
|
|
.tx_threshold_lo = 16,
|
|
.tx_threshold_lo = 16,
|
|
.tx_threshold_hi = 48,
|
|
.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) {
|
|
switch (drv_data->ssp_type) {
|
|
case LPSS_LPT_SSP:
|
|
case LPSS_LPT_SSP:
|
|
case LPSS_BYT_SSP:
|
|
case LPSS_BYT_SSP:
|
|
|
|
+ case LPSS_BSW_SSP:
|
|
case LPSS_SPT_SSP:
|
|
case LPSS_SPT_SSP:
|
|
case LPSS_BXT_SSP:
|
|
case LPSS_BXT_SSP:
|
|
return true;
|
|
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)
|
|
static void lpss_ssp_cs_control(struct driver_data *drv_data, bool enable)
|
|
{
|
|
{
|
|
const struct lpss_config *config;
|
|
const struct lpss_config *config;
|
|
- u32 value, cs;
|
|
|
|
|
|
+ u32 value;
|
|
|
|
|
|
config = lpss_get_config(drv_data);
|
|
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);
|
|
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;
|
|
value &= ~LPSS_CS_CONTROL_CS_HIGH;
|
|
- } else {
|
|
|
|
|
|
+ else
|
|
value |= LPSS_CS_CONTROL_CS_HIGH;
|
|
value |= LPSS_CS_CONTROL_CS_HIGH;
|
|
- }
|
|
|
|
__lpss_ssp_write_priv(drv_data, config->reg_cs_ctrl, value);
|
|
__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_transfer* last_transfer;
|
|
struct spi_message *msg;
|
|
struct spi_message *msg;
|
|
|
|
+ unsigned long timeout;
|
|
|
|
|
|
msg = drv_data->cur_msg;
|
|
msg = drv_data->cur_msg;
|
|
drv_data->cur_msg = NULL;
|
|
drv_data->cur_msg = NULL;
|
|
@@ -508,6 +540,12 @@ static void giveback(struct driver_data *drv_data)
|
|
if (last_transfer->delay_usecs)
|
|
if (last_transfer->delay_usecs)
|
|
udelay(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
|
|
/* Drop chip select UNLESS cs_change is true or we are returning
|
|
* a message with an error, or next message is for another chip
|
|
* 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)
|
|
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);
|
|
write_SSSR_CS(drv_data, drv_data->clear_sr);
|
|
reset_sccr1(drv_data);
|
|
reset_sccr1(drv_data);
|
|
if (!pxa25x_ssp_comp(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->tx_end = drv_data->tx + transfer->len;
|
|
drv_data->rx = transfer->rx_buf;
|
|
drv_data->rx = transfer->rx_buf;
|
|
drv_data->rx_end = drv_data->rx + transfer->len;
|
|
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->len = transfer->len;
|
|
drv_data->write = drv_data->tx ? chip->write : null_writer;
|
|
drv_data->write = drv_data->tx ? chip->write : null_writer;
|
|
drv_data->read = drv_data->rx ? chip->read : null_reader;
|
|
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");
|
|
"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;
|
|
message->state = RUNNING_STATE;
|
|
|
|
|
|
drv_data->dma_mapped = 0;
|
|
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);
|
|
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 (is_lpss_ssp(drv_data)) {
|
|
if ((pxa2xx_spi_read(drv_data, SSIRF) & 0xff)
|
|
if ((pxa2xx_spi_read(drv_data, SSIRF) & 0xff)
|
|
!= chip->lpss_rx_threshold)
|
|
!= chip->lpss_rx_threshold)
|
|
@@ -1166,6 +1202,7 @@ static int setup(struct spi_device *spi)
|
|
break;
|
|
break;
|
|
case LPSS_LPT_SSP:
|
|
case LPSS_LPT_SSP:
|
|
case LPSS_BYT_SSP:
|
|
case LPSS_BYT_SSP:
|
|
|
|
+ case LPSS_BSW_SSP:
|
|
case LPSS_SPT_SSP:
|
|
case LPSS_SPT_SSP:
|
|
case LPSS_BXT_SSP:
|
|
case LPSS_BXT_SSP:
|
|
config = lpss_get_config(drv_data);
|
|
config = lpss_get_config(drv_data);
|
|
@@ -1313,7 +1350,7 @@ static const struct acpi_device_id pxa2xx_spi_acpi_match[] = {
|
|
{ "INT3430", LPSS_LPT_SSP },
|
|
{ "INT3430", LPSS_LPT_SSP },
|
|
{ "INT3431", LPSS_LPT_SSP },
|
|
{ "INT3431", LPSS_LPT_SSP },
|
|
{ "80860F0E", LPSS_BYT_SSP },
|
|
{ "80860F0E", LPSS_BYT_SSP },
|
|
- { "8086228E", LPSS_BYT_SSP },
|
|
|
|
|
|
+ { "8086228E", LPSS_BSW_SSP },
|
|
{ },
|
|
{ },
|
|
};
|
|
};
|
|
MODULE_DEVICE_TABLE(acpi, pxa2xx_spi_acpi_match);
|
|
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 */
|
|
/* SPT-H */
|
|
{ PCI_VDEVICE(INTEL, 0xa129), LPSS_SPT_SSP },
|
|
{ PCI_VDEVICE(INTEL, 0xa129), LPSS_SPT_SSP },
|
|
{ PCI_VDEVICE(INTEL, 0xa12a), LPSS_SPT_SSP },
|
|
{ PCI_VDEVICE(INTEL, 0xa12a), LPSS_SPT_SSP },
|
|
- /* BXT */
|
|
|
|
|
|
+ /* BXT A-Step */
|
|
{ PCI_VDEVICE(INTEL, 0x0ac2), LPSS_BXT_SSP },
|
|
{ PCI_VDEVICE(INTEL, 0x0ac2), LPSS_BXT_SSP },
|
|
{ PCI_VDEVICE(INTEL, 0x0ac4), LPSS_BXT_SSP },
|
|
{ PCI_VDEVICE(INTEL, 0x0ac4), LPSS_BXT_SSP },
|
|
{ PCI_VDEVICE(INTEL, 0x0ac6), 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 */
|
|
/* APL */
|
|
{ PCI_VDEVICE(INTEL, 0x5ac2), LPSS_BXT_SSP },
|
|
{ PCI_VDEVICE(INTEL, 0x5ac2), LPSS_BXT_SSP },
|
|
{ PCI_VDEVICE(INTEL, 0x5ac4), LPSS_BXT_SSP },
|
|
{ PCI_VDEVICE(INTEL, 0x5ac4), LPSS_BXT_SSP },
|
|
@@ -1438,6 +1479,29 @@ pxa2xx_spi_init_pdata(struct platform_device *pdev)
|
|
}
|
|
}
|
|
#endif
|
|
#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)
|
|
static int pxa2xx_spi_probe(struct platform_device *pdev)
|
|
{
|
|
{
|
|
struct device *dev = &pdev->dev;
|
|
struct device *dev = &pdev->dev;
|
|
@@ -1490,6 +1554,7 @@ static int pxa2xx_spi_probe(struct platform_device *pdev)
|
|
master->setup = setup;
|
|
master->setup = setup;
|
|
master->transfer_one_message = pxa2xx_spi_transfer_one_message;
|
|
master->transfer_one_message = pxa2xx_spi_transfer_one_message;
|
|
master->unprepare_transfer_hardware = pxa2xx_spi_unprepare_transfer;
|
|
master->unprepare_transfer_hardware = pxa2xx_spi_unprepare_transfer;
|
|
|
|
+ master->fw_translate_cs = pxa2xx_spi_fw_translate_cs;
|
|
master->auto_runtime_pm = true;
|
|
master->auto_runtime_pm = true;
|
|
|
|
|
|
drv_data->ssp_type = ssp->type;
|
|
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_MASK;
|
|
tmp >>= LPSS_CAPS_CS_EN_SHIFT;
|
|
tmp >>= LPSS_CAPS_CS_EN_SHIFT;
|
|
platform_info->num_chipselect = ffz(tmp);
|
|
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;
|
|
master->num_chipselect = platform_info->num_chipselect;
|