mirror of
				https://github.com/torvalds/linux.git
				synced 2025-11-04 02:30:34 +02:00 
			
		
		
		
	spi: pxa2xx: Introduce __lpss_ssp_update_priv() helper
In a few places we repeat RMW IO operations on LPSS private registers. Let's introduce a helper to make the code better to read and maintain. Signed-off-by: Andy Shevchenko <andriy.shevchenko@linux.intel.com> Link: https://patch.msgid.link/20250116162109.263081-1-andriy.shevchenko@linux.intel.com Signed-off-by: Mark Brown <broonie@kernel.org>
This commit is contained in:
		
							parent
							
								
									9a8afbe567
								
							
						
					
					
						commit
						78b435c904
					
				
					 1 changed files with 40 additions and 48 deletions
				
			
		| 
						 | 
					@ -73,8 +73,9 @@ struct chip_data {
 | 
				
			||||||
#define LPSS_CAPS_CS_EN_MASK			(0xf << LPSS_CAPS_CS_EN_SHIFT)
 | 
					#define LPSS_CAPS_CS_EN_MASK			(0xf << LPSS_CAPS_CS_EN_SHIFT)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#define LPSS_PRIV_CLOCK_GATE 0x38
 | 
					#define LPSS_PRIV_CLOCK_GATE 0x38
 | 
				
			||||||
#define LPSS_PRIV_CLOCK_GATE_CLK_CTL_MASK 0x3
 | 
					#define LPSS_PRIV_CLOCK_GATE_CLK_CTL_MASK	0x3
 | 
				
			||||||
#define LPSS_PRIV_CLOCK_GATE_CLK_CTL_FORCE_ON 0x3
 | 
					#define LPSS_PRIV_CLOCK_GATE_CLK_CTL_FORCE_ON	0x3
 | 
				
			||||||
 | 
					#define LPSS_PRIV_CLOCK_GATE_CLK_CTL_FORCE_OFF	0x0
 | 
				
			||||||
 | 
					
 | 
				
			||||||
struct lpss_config {
 | 
					struct lpss_config {
 | 
				
			||||||
	/* LPSS offset from drv_data->ioaddr */
 | 
						/* LPSS offset from drv_data->ioaddr */
 | 
				
			||||||
| 
						 | 
					@ -321,6 +322,20 @@ static void __lpss_ssp_write_priv(struct driver_data *drv_data,
 | 
				
			||||||
	writel(value, drv_data->lpss_base + offset);
 | 
						writel(value, drv_data->lpss_base + offset);
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static bool __lpss_ssp_update_priv(struct driver_data *drv_data, unsigned int offset,
 | 
				
			||||||
 | 
									   u32 mask, u32 value)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						u32 new, curr;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						curr = __lpss_ssp_read_priv(drv_data, offset);
 | 
				
			||||||
 | 
						new = (curr & ~mask) | (value & mask);
 | 
				
			||||||
 | 
						if (new == curr)
 | 
				
			||||||
 | 
							return false;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						__lpss_ssp_write_priv(drv_data, offset, new);
 | 
				
			||||||
 | 
						return true;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/*
 | 
					/*
 | 
				
			||||||
 * lpss_ssp_setup - perform LPSS SSP specific setup
 | 
					 * lpss_ssp_setup - perform LPSS SSP specific setup
 | 
				
			||||||
 * @drv_data: pointer to the driver private data
 | 
					 * @drv_data: pointer to the driver private data
 | 
				
			||||||
| 
						 | 
					@ -337,21 +352,16 @@ static void lpss_ssp_setup(struct driver_data *drv_data)
 | 
				
			||||||
	drv_data->lpss_base = drv_data->ssp->mmio_base + config->offset;
 | 
						drv_data->lpss_base = drv_data->ssp->mmio_base + config->offset;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	/* Enable software chip select control */
 | 
						/* Enable software chip select control */
 | 
				
			||||||
	value = __lpss_ssp_read_priv(drv_data, config->reg_cs_ctrl);
 | 
						value = LPSS_CS_CONTROL_SW_MODE | LPSS_CS_CONTROL_CS_HIGH;
 | 
				
			||||||
	value &= ~(LPSS_CS_CONTROL_SW_MODE | LPSS_CS_CONTROL_CS_HIGH);
 | 
						__lpss_ssp_update_priv(drv_data, config->reg_cs_ctrl, value, value);
 | 
				
			||||||
	value |= LPSS_CS_CONTROL_SW_MODE | LPSS_CS_CONTROL_CS_HIGH;
 | 
					 | 
				
			||||||
	__lpss_ssp_write_priv(drv_data, config->reg_cs_ctrl, value);
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
	/* Enable multiblock DMA transfers */
 | 
						/* Enable multiblock DMA transfers */
 | 
				
			||||||
	if (drv_data->controller_info->enable_dma) {
 | 
						if (drv_data->controller_info->enable_dma) {
 | 
				
			||||||
		__lpss_ssp_write_priv(drv_data, config->reg_ssp, 1);
 | 
							__lpss_ssp_update_priv(drv_data, config->reg_ssp, BIT(0), BIT(0));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		if (config->reg_general >= 0) {
 | 
							if (config->reg_general >= 0) {
 | 
				
			||||||
			value = __lpss_ssp_read_priv(drv_data,
 | 
								value = LPSS_GENERAL_REG_RXTO_HOLDOFF_DISABLE;
 | 
				
			||||||
						     config->reg_general);
 | 
								__lpss_ssp_update_priv(drv_data, config->reg_general, value, value);
 | 
				
			||||||
			value |= LPSS_GENERAL_REG_RXTO_HOLDOFF_DISABLE;
 | 
					 | 
				
			||||||
			__lpss_ssp_write_priv(drv_data,
 | 
					 | 
				
			||||||
					      config->reg_general, value);
 | 
					 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
| 
						 | 
					@ -361,30 +371,19 @@ static void lpss_ssp_select_cs(struct spi_device *spi,
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
	struct driver_data *drv_data =
 | 
						struct driver_data *drv_data =
 | 
				
			||||||
		spi_controller_get_devdata(spi->controller);
 | 
							spi_controller_get_devdata(spi->controller);
 | 
				
			||||||
	u32 value, cs;
 | 
						u32 cs;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if (!config->cs_sel_mask)
 | 
						cs = spi_get_chipselect(spi, 0) << config->cs_sel_shift;
 | 
				
			||||||
 | 
						if (!__lpss_ssp_update_priv(drv_data, config->reg_cs_ctrl, config->cs_sel_mask, cs))
 | 
				
			||||||
		return;
 | 
							return;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	value = __lpss_ssp_read_priv(drv_data, config->reg_cs_ctrl);
 | 
						/*
 | 
				
			||||||
 | 
						 * When switching another chip select output active the output must be
 | 
				
			||||||
	cs = spi_get_chipselect(spi, 0);
 | 
						 * selected first and wait 2 ssp_clk cycles before changing state to
 | 
				
			||||||
	cs <<= config->cs_sel_shift;
 | 
						 * active. Otherwise a short glitch will occur on the previous chip
 | 
				
			||||||
	if (cs != (value & config->cs_sel_mask)) {
 | 
						 * select since output select is latched but state control is not.
 | 
				
			||||||
		/*
 | 
						 */
 | 
				
			||||||
		 * When switching another chip select output active the
 | 
						ndelay(1000000000 / (drv_data->controller->max_speed_hz / 2));
 | 
				
			||||||
		 * 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->controller->max_speed_hz / 2));
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
static void lpss_ssp_cs_control(struct spi_device *spi, bool enable)
 | 
					static void lpss_ssp_cs_control(struct spi_device *spi, bool enable)
 | 
				
			||||||
| 
						 | 
					@ -392,34 +391,27 @@ static void lpss_ssp_cs_control(struct spi_device *spi, bool enable)
 | 
				
			||||||
	struct driver_data *drv_data =
 | 
						struct driver_data *drv_data =
 | 
				
			||||||
		spi_controller_get_devdata(spi->controller);
 | 
							spi_controller_get_devdata(spi->controller);
 | 
				
			||||||
	const struct lpss_config *config;
 | 
						const struct lpss_config *config;
 | 
				
			||||||
	u32 value;
 | 
						u32 mask;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	config = lpss_get_config(drv_data);
 | 
						config = lpss_get_config(drv_data);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if (enable)
 | 
						if (enable)
 | 
				
			||||||
		lpss_ssp_select_cs(spi, config);
 | 
							lpss_ssp_select_cs(spi, config);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	value = __lpss_ssp_read_priv(drv_data, config->reg_cs_ctrl);
 | 
						mask = LPSS_CS_CONTROL_CS_HIGH;
 | 
				
			||||||
	if (enable)
 | 
						__lpss_ssp_update_priv(drv_data, config->reg_cs_ctrl, mask, enable ? mask : 0);
 | 
				
			||||||
		value &= ~LPSS_CS_CONTROL_CS_HIGH;
 | 
					 | 
				
			||||||
	else
 | 
					 | 
				
			||||||
		value |= LPSS_CS_CONTROL_CS_HIGH;
 | 
					 | 
				
			||||||
	__lpss_ssp_write_priv(drv_data, config->reg_cs_ctrl, value);
 | 
					 | 
				
			||||||
	if (config->cs_clk_stays_gated) {
 | 
						if (config->cs_clk_stays_gated) {
 | 
				
			||||||
		u32 clkgate;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
		/*
 | 
							/*
 | 
				
			||||||
		 * Changing CS alone when dynamic clock gating is on won't
 | 
							 * Changing CS alone when dynamic clock gating is on won't
 | 
				
			||||||
		 * actually flip CS at that time. This ruins SPI transfers
 | 
							 * actually flip CS at that time. This ruins SPI transfers
 | 
				
			||||||
		 * that specify delays, or have no data. Toggle the clock mode
 | 
							 * that specify delays, or have no data. Toggle the clock mode
 | 
				
			||||||
		 * to force on briefly to poke the CS pin to move.
 | 
							 * to force on briefly to poke the CS pin to move.
 | 
				
			||||||
		 */
 | 
							 */
 | 
				
			||||||
		clkgate = __lpss_ssp_read_priv(drv_data, LPSS_PRIV_CLOCK_GATE);
 | 
							mask = LPSS_PRIV_CLOCK_GATE_CLK_CTL_MASK;
 | 
				
			||||||
		value = (clkgate & ~LPSS_PRIV_CLOCK_GATE_CLK_CTL_MASK) |
 | 
							if (__lpss_ssp_update_priv(drv_data, LPSS_PRIV_CLOCK_GATE, mask,
 | 
				
			||||||
			LPSS_PRIV_CLOCK_GATE_CLK_CTL_FORCE_ON;
 | 
										   LPSS_PRIV_CLOCK_GATE_CLK_CTL_FORCE_ON))
 | 
				
			||||||
 | 
								__lpss_ssp_update_priv(drv_data, LPSS_PRIV_CLOCK_GATE, mask,
 | 
				
			||||||
		__lpss_ssp_write_priv(drv_data, LPSS_PRIV_CLOCK_GATE, value);
 | 
										       LPSS_PRIV_CLOCK_GATE_CLK_CTL_FORCE_OFF);
 | 
				
			||||||
		__lpss_ssp_write_priv(drv_data, LPSS_PRIV_CLOCK_GATE, clkgate);
 | 
					 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
		Loading…
	
		Reference in a new issue