diff options
Diffstat (limited to 'drivers/spi/spi-pxa2xx.c')
| -rw-r--r-- | drivers/spi/spi-pxa2xx.c | 23 | 
1 files changed, 23 insertions, 0 deletions
| diff --git a/drivers/spi/spi-pxa2xx.c b/drivers/spi/spi-pxa2xx.c index 4c7a71f0fb3e..2e318158fca9 100644 --- a/drivers/spi/spi-pxa2xx.c +++ b/drivers/spi/spi-pxa2xx.c @@ -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) |