OSDN Git Service

Merge series "spi: spidev: Fix messages in spidev" from Oleksandr Suvorov <oleksandr...
[tomoyo/tomoyo-test1.git] / drivers / spi / spi-pxa2xx.c
index aa93bbf..73d2a65 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,
        },
 };
 
@@ -388,6 +395,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)