]> asedeno.scripts.mit.edu Git - linux.git/blobdiff - drivers/spi/spi-pxa2xx.c
Merge git://git.kernel.org/pub/scm/linux/kernel/git/netdev/net
[linux.git] / drivers / spi / spi-pxa2xx.c
index 9071333ebdd86fe290f441c21b4a1a37c7e40ff5..2e318158fca97686200ff2aa6b8169158911c204 100644 (file)
@@ -70,6 +70,10 @@ MODULE_ALIAS("platform:pxa2xx-spi");
 #define LPSS_CAPS_CS_EN_SHIFT                  9
 #define LPSS_CAPS_CS_EN_MASK                   (0xf << LPSS_CAPS_CS_EN_SHIFT)
 
+#define LPSS_PRIV_CLOCK_GATE 0x38
+#define LPSS_PRIV_CLOCK_GATE_CLK_CTL_MASK 0x3
+#define LPSS_PRIV_CLOCK_GATE_CLK_CTL_FORCE_ON 0x3
+
 struct lpss_config {
        /* LPSS offset from drv_data->ioaddr */
        unsigned offset;
@@ -86,6 +90,8 @@ struct lpss_config {
        unsigned cs_sel_shift;
        unsigned cs_sel_mask;
        unsigned cs_num;
+       /* Quirks */
+       unsigned cs_clk_stays_gated : 1;
 };
 
 /* Keep these sorted with enum pxa_ssp_type */
@@ -156,6 +162,7 @@ static const struct lpss_config lpss_platforms[] = {
                .tx_threshold_hi = 56,
                .cs_sel_shift = 8,
                .cs_sel_mask = 3 << 8,
+               .cs_clk_stays_gated = true,
        },
 };
 
@@ -383,6 +390,22 @@ static void lpss_ssp_cs_control(struct spi_device *spi, bool enable)
        else
                value |= LPSS_CS_CONTROL_CS_HIGH;
        __lpss_ssp_write_priv(drv_data, config->reg_cs_ctrl, value);
+       if (config->cs_clk_stays_gated) {
+               u32 clkgate;
+
+               /*
+                * Changing CS alone when dynamic clock gating is on won't
+                * actually flip CS at that time. This ruins SPI transfers
+                * that specify delays, or have no data. Toggle the clock mode
+                * to force on briefly to poke the CS pin to move.
+                */
+               clkgate = __lpss_ssp_read_priv(drv_data, LPSS_PRIV_CLOCK_GATE);
+               value = (clkgate & ~LPSS_PRIV_CLOCK_GATE_CLK_CTL_MASK) |
+                       LPSS_PRIV_CLOCK_GATE_CLK_CTL_FORCE_ON;
+
+               __lpss_ssp_write_priv(drv_data, LPSS_PRIV_CLOCK_GATE, value);
+               __lpss_ssp_write_priv(drv_data, LPSS_PRIV_CLOCK_GATE, clkgate);
+       }
 }
 
 static void cs_assert(struct spi_device *spi)
@@ -461,6 +484,16 @@ int pxa2xx_spi_flush(struct driver_data *drv_data)
        return limit;
 }
 
+static void pxa2xx_spi_off(struct driver_data *drv_data)
+{
+       /* On MMP, disabling SSE seems to corrupt the rx fifo */
+       if (drv_data->ssp_type == MMP2_SSP)
+               return;
+
+       pxa2xx_spi_write(drv_data, SSCR0,
+                        pxa2xx_spi_read(drv_data, SSCR0) & ~SSCR0_SSE);
+}
+
 static int null_writer(struct driver_data *drv_data)
 {
        u8 n_bytes = drv_data->n_bytes;
@@ -587,8 +620,7 @@ static void int_error_stop(struct driver_data *drv_data, const char* msg)
        if (!pxa25x_ssp_comp(drv_data))
                pxa2xx_spi_write(drv_data, SSTO, 0);
        pxa2xx_spi_flush(drv_data);
-       pxa2xx_spi_write(drv_data, SSCR0,
-                        pxa2xx_spi_read(drv_data, SSCR0) & ~SSCR0_SSE);
+       pxa2xx_spi_off(drv_data);
 
        dev_err(&drv_data->pdev->dev, "%s\n", msg);
 
@@ -686,8 +718,7 @@ static irqreturn_t interrupt_transfer(struct driver_data *drv_data)
 
 static void handle_bad_msg(struct driver_data *drv_data)
 {
-       pxa2xx_spi_write(drv_data, SSCR0,
-                        pxa2xx_spi_read(drv_data, SSCR0) & ~SSCR0_SSE);
+       pxa2xx_spi_off(drv_data);
        pxa2xx_spi_write(drv_data, SSCR1,
                         pxa2xx_spi_read(drv_data, SSCR1) & ~drv_data->int_cr1);
        if (!pxa25x_ssp_comp(drv_data))
@@ -1062,7 +1093,8 @@ static int pxa2xx_spi_transfer_one(struct spi_controller *controller,
            || (pxa2xx_spi_read(drv_data, SSCR1) & change_mask)
            != (cr1 & change_mask)) {
                /* stop the SSP, and update the other bits */
-               pxa2xx_spi_write(drv_data, SSCR0, cr0 & ~SSCR0_SSE);
+               if (drv_data->ssp_type != MMP2_SSP)
+                       pxa2xx_spi_write(drv_data, SSCR0, cr0 & ~SSCR0_SSE);
                if (!pxa25x_ssp_comp(drv_data))
                        pxa2xx_spi_write(drv_data, SSTO, chip->timeout);
                /* first set CR1 without interrupt and service enables */
@@ -1118,8 +1150,7 @@ static int pxa2xx_spi_slave_abort(struct spi_controller *controller)
        if (!pxa25x_ssp_comp(drv_data))
                pxa2xx_spi_write(drv_data, SSTO, 0);
        pxa2xx_spi_flush(drv_data);
-       pxa2xx_spi_write(drv_data, SSCR0,
-                        pxa2xx_spi_read(drv_data, SSCR0) & ~SSCR0_SSE);
+       pxa2xx_spi_off(drv_data);
 
        dev_dbg(&drv_data->pdev->dev, "transfer aborted\n");
 
@@ -1135,8 +1166,7 @@ static void pxa2xx_spi_handle_err(struct spi_controller *controller,
        struct driver_data *drv_data = spi_controller_get_devdata(controller);
 
        /* Disable the SSP */
-       pxa2xx_spi_write(drv_data, SSCR0,
-                        pxa2xx_spi_read(drv_data, SSCR0) & ~SSCR0_SSE);
+       pxa2xx_spi_off(drv_data);
        /* Clear and disable interrupts and service requests */
        write_SSSR_CS(drv_data, drv_data->clear_sr);
        pxa2xx_spi_write(drv_data, SSCR1,
@@ -1161,8 +1191,7 @@ static int pxa2xx_spi_unprepare_transfer(struct spi_controller *controller)
        struct driver_data *drv_data = spi_controller_get_devdata(controller);
 
        /* Disable the SSP now */
-       pxa2xx_spi_write(drv_data, SSCR0,
-                        pxa2xx_spi_read(drv_data, SSCR0) & ~SSCR0_SSE);
+       pxa2xx_spi_off(drv_data);
 
        return 0;
 }
@@ -1423,6 +1452,9 @@ static const struct pci_device_id pxa2xx_spi_pci_compound_match[] = {
        /* KBL-H */
        { PCI_VDEVICE(INTEL, 0xa2a9), LPSS_SPT_SSP },
        { PCI_VDEVICE(INTEL, 0xa2aa), LPSS_SPT_SSP },
+       /* CML-V */
+       { PCI_VDEVICE(INTEL, 0xa3a9), LPSS_SPT_SSP },
+       { PCI_VDEVICE(INTEL, 0xa3aa), LPSS_SPT_SSP },
        /* BXT A-Step */
        { PCI_VDEVICE(INTEL, 0x0ac2), LPSS_BXT_SSP },
        { PCI_VDEVICE(INTEL, 0x0ac4), LPSS_BXT_SSP },