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…
Add table
Reference in a new issue