spi: pxa2xx: Add output control for multiple Intel LPSS chip selects
authorJarkko Nikula <jarkko.nikula@linux.intel.com>
Wed, 28 Oct 2015 13:13:40 +0000 (15:13 +0200)
committerMark Brown <broonie@kernel.org>
Fri, 30 Oct 2015 02:18:05 +0000 (11:18 +0900)
Intel LPSS SPI host controllers in upcoming Intel platforms can have up
to 4 chip selects per port. Extend chip select control in
lpss_ssp_cs_control() by adding a code that selects the active chip
select output prior to changing the state. Detection for number of
enabled chip select signals will be added by another patch.

Signed-off-by: Jarkko Nikula <jarkko.nikula@linux.intel.com>
Signed-off-by: Mark Brown <broonie@kernel.org>
drivers/spi/spi-pxa2xx.c

index 9060aee5a7b1cdfcf44ae01a2b3bfab64947f660..040f6bb566a15a9f5f2c9a577dc98842f1d04737 100644 (file)
@@ -64,6 +64,8 @@ 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)
 
 struct lpss_config {
        /* LPSS offset from drv_data->ioaddr */
@@ -271,15 +273,34 @@ static void lpss_ssp_setup(struct driver_data *drv_data)
 static void lpss_ssp_cs_control(struct driver_data *drv_data, bool enable)
 {
        const struct lpss_config *config;
-       u32 value;
+       u32 value, cs;
 
        config = lpss_get_config(drv_data);
 
        value = __lpss_ssp_read_priv(drv_data, config->reg_cs_ctrl);
-       if (enable)
+       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));
+               }
                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);
 }