Commit 0eb707ac authored by Neil Armstrong's avatar Neil Armstrong Committed by Mark Brown

spi: meson-spicc: adapt burst handling for G12A support

The G12A SPICC controller variant has a different FIFO size and doesn't
handle the RX Half interrupt the same way as GXL & AXG variants.

Thus simplify the burst management and take in account a variable FIFO
size.
Signed-off-by: default avatarNeil Armstrong <narmstrong@baylibre.com>
Link: https://lore.kernel.org/r/20200312133131.26430-8-narmstrong@baylibre.comSigned-off-by: default avatarMark Brown <broonie@kernel.org>
parent f27bff47
...@@ -141,12 +141,10 @@ ...@@ -141,12 +141,10 @@
#define writel_bits_relaxed(mask, val, addr) \ #define writel_bits_relaxed(mask, val, addr) \
writel_relaxed((readl_relaxed(addr) & ~(mask)) | (val), addr) writel_relaxed((readl_relaxed(addr) & ~(mask)) | (val), addr)
#define SPICC_BURST_MAX 16
#define SPICC_FIFO_HALF 10
struct meson_spicc_data { struct meson_spicc_data {
unsigned int max_speed_hz; unsigned int max_speed_hz;
unsigned int min_speed_hz; unsigned int min_speed_hz;
unsigned int fifo_size;
bool has_oen; bool has_oen;
bool has_enhance_clk_div; bool has_enhance_clk_div;
}; };
...@@ -166,8 +164,6 @@ struct meson_spicc_device { ...@@ -166,8 +164,6 @@ struct meson_spicc_device {
unsigned long tx_remain; unsigned long tx_remain;
unsigned long rx_remain; unsigned long rx_remain;
unsigned long xfer_remain; unsigned long xfer_remain;
bool is_burst_end;
bool is_last_burst;
}; };
static void meson_spicc_oen_enable(struct meson_spicc_device *spicc) static void meson_spicc_oen_enable(struct meson_spicc_device *spicc)
...@@ -191,7 +187,7 @@ static inline bool meson_spicc_txfull(struct meson_spicc_device *spicc) ...@@ -191,7 +187,7 @@ static inline bool meson_spicc_txfull(struct meson_spicc_device *spicc)
static inline bool meson_spicc_rxready(struct meson_spicc_device *spicc) static inline bool meson_spicc_rxready(struct meson_spicc_device *spicc)
{ {
return FIELD_GET(SPICC_RH | SPICC_RR | SPICC_RF_EN, return FIELD_GET(SPICC_RH | SPICC_RR | SPICC_RF,
readl_relaxed(spicc->base + SPICC_STATREG)); readl_relaxed(spicc->base + SPICC_STATREG));
} }
...@@ -246,34 +242,22 @@ static inline void meson_spicc_tx(struct meson_spicc_device *spicc) ...@@ -246,34 +242,22 @@ static inline void meson_spicc_tx(struct meson_spicc_device *spicc)
spicc->base + SPICC_TXDATA); spicc->base + SPICC_TXDATA);
} }
static inline u32 meson_spicc_setup_rx_irq(struct meson_spicc_device *spicc, static inline void meson_spicc_setup_burst(struct meson_spicc_device *spicc)
u32 irq_ctrl)
{ {
if (spicc->rx_remain > SPICC_FIFO_HALF)
irq_ctrl |= SPICC_RH_EN;
else
irq_ctrl |= SPICC_RR_EN;
return irq_ctrl;
}
static inline void meson_spicc_setup_burst(struct meson_spicc_device *spicc, unsigned int burst_len = min_t(unsigned int,
unsigned int burst_len) spicc->xfer_remain /
{ spicc->bytes_per_word,
spicc->data->fifo_size);
/* Setup Xfer variables */ /* Setup Xfer variables */
spicc->tx_remain = burst_len; spicc->tx_remain = burst_len;
spicc->rx_remain = burst_len; spicc->rx_remain = burst_len;
spicc->xfer_remain -= burst_len * spicc->bytes_per_word; spicc->xfer_remain -= burst_len * spicc->bytes_per_word;
spicc->is_burst_end = false;
if (burst_len < SPICC_BURST_MAX || !spicc->xfer_remain)
spicc->is_last_burst = true;
else
spicc->is_last_burst = false;
/* Setup burst length */ /* Setup burst length */
writel_bits_relaxed(SPICC_BURSTLENGTH_MASK, writel_bits_relaxed(SPICC_BURSTLENGTH_MASK,
FIELD_PREP(SPICC_BURSTLENGTH_MASK, FIELD_PREP(SPICC_BURSTLENGTH_MASK,
burst_len), burst_len - 1),
spicc->base + SPICC_CONREG); spicc->base + SPICC_CONREG);
/* Fill TX FIFO */ /* Fill TX FIFO */
...@@ -283,61 +267,26 @@ static inline void meson_spicc_setup_burst(struct meson_spicc_device *spicc, ...@@ -283,61 +267,26 @@ static inline void meson_spicc_setup_burst(struct meson_spicc_device *spicc,
static irqreturn_t meson_spicc_irq(int irq, void *data) static irqreturn_t meson_spicc_irq(int irq, void *data)
{ {
struct meson_spicc_device *spicc = (void *) data; struct meson_spicc_device *spicc = (void *) data;
u32 ctrl = readl_relaxed(spicc->base + SPICC_INTREG);
u32 stat = readl_relaxed(spicc->base + SPICC_STATREG) & ctrl;
ctrl &= ~(SPICC_RH_EN | SPICC_RR_EN); writel_bits_relaxed(SPICC_TC, SPICC_TC, spicc->base + SPICC_STATREG);
/* Empty RX FIFO */ /* Empty RX FIFO */
meson_spicc_rx(spicc); meson_spicc_rx(spicc);
/* Enable TC interrupt since we transferred everything */ if (!spicc->xfer_remain) {
if (!spicc->tx_remain && !spicc->rx_remain) { /* Disable all IRQs */
spicc->is_burst_end = true; writel(0, spicc->base + SPICC_INTREG);
/* Enable TC interrupt */
ctrl |= SPICC_TC_EN;
/* Reload IRQ status */
stat = readl_relaxed(spicc->base + SPICC_STATREG) & ctrl;
}
/* Check transfer complete */
if ((stat & SPICC_TC) && spicc->is_burst_end) {
unsigned int burst_len;
/* Clear TC bit */
writel_relaxed(SPICC_TC, spicc->base + SPICC_STATREG);
/* Disable TC interrupt */
ctrl &= ~SPICC_TC_EN;
if (spicc->is_last_burst) {
/* Disable all IRQs */
writel(0, spicc->base + SPICC_INTREG);
spi_finalize_current_transfer(spicc->master);
return IRQ_HANDLED; spi_finalize_current_transfer(spicc->master);
}
burst_len = min_t(unsigned int, return IRQ_HANDLED;
spicc->xfer_remain / spicc->bytes_per_word,
SPICC_BURST_MAX);
/* Setup burst */
meson_spicc_setup_burst(spicc, burst_len);
/* Restart burst */
writel_bits_relaxed(SPICC_XCH, SPICC_XCH,
spicc->base + SPICC_CONREG);
} }
/* Setup RX interrupt trigger */ /* Setup burst */
ctrl = meson_spicc_setup_rx_irq(spicc, ctrl); meson_spicc_setup_burst(spicc);
/* Reconfigure interrupts */ /* Start burst */
writel(ctrl, spicc->base + SPICC_INTREG); writel_bits_relaxed(SPICC_XCH, SPICC_XCH, spicc->base + SPICC_CONREG);
return IRQ_HANDLED; return IRQ_HANDLED;
} }
...@@ -405,6 +354,28 @@ static void meson_spicc_setup_xfer(struct meson_spicc_device *spicc, ...@@ -405,6 +354,28 @@ static void meson_spicc_setup_xfer(struct meson_spicc_device *spicc,
clk_set_rate(spicc->clk, xfer->speed_hz); clk_set_rate(spicc->clk, xfer->speed_hz);
meson_spicc_auto_io_delay(spicc); meson_spicc_auto_io_delay(spicc);
writel_relaxed(0, spicc->base + SPICC_DMAREG);
}
static void meson_spicc_reset_fifo(struct meson_spicc_device *spicc)
{
u32 data;
if (spicc->data->has_oen)
writel_bits_relaxed(SPICC_ENH_MAIN_CLK_AO,
SPICC_ENH_MAIN_CLK_AO,
spicc->base + SPICC_ENH_CTL0);
writel_bits_relaxed(SPICC_FIFORST_W1_MASK, SPICC_FIFORST_W1_MASK,
spicc->base + SPICC_TESTREG);
while (meson_spicc_rxready(spicc))
data = readl_relaxed(spicc->base + SPICC_RXDATA);
if (spicc->data->has_oen)
writel_bits_relaxed(SPICC_ENH_MAIN_CLK_AO, 0,
spicc->base + SPICC_ENH_CTL0);
} }
static int meson_spicc_transfer_one(struct spi_master *master, static int meson_spicc_transfer_one(struct spi_master *master,
...@@ -412,8 +383,6 @@ static int meson_spicc_transfer_one(struct spi_master *master, ...@@ -412,8 +383,6 @@ static int meson_spicc_transfer_one(struct spi_master *master,
struct spi_transfer *xfer) struct spi_transfer *xfer)
{ {
struct meson_spicc_device *spicc = spi_master_get_devdata(master); struct meson_spicc_device *spicc = spi_master_get_devdata(master);
unsigned int burst_len;
u32 irq = 0;
/* Store current transfer */ /* Store current transfer */
spicc->xfer = xfer; spicc->xfer = xfer;
...@@ -427,22 +396,22 @@ static int meson_spicc_transfer_one(struct spi_master *master, ...@@ -427,22 +396,22 @@ static int meson_spicc_transfer_one(struct spi_master *master,
spicc->bytes_per_word = spicc->bytes_per_word =
DIV_ROUND_UP(spicc->xfer->bits_per_word, 8); DIV_ROUND_UP(spicc->xfer->bits_per_word, 8);
if (xfer->len % spicc->bytes_per_word)
return -EINVAL;
/* Setup transfer parameters */ /* Setup transfer parameters */
meson_spicc_setup_xfer(spicc, xfer); meson_spicc_setup_xfer(spicc, xfer);
burst_len = min_t(unsigned int, meson_spicc_reset_fifo(spicc);
spicc->xfer_remain / spicc->bytes_per_word,
SPICC_BURST_MAX);
meson_spicc_setup_burst(spicc, burst_len);
irq = meson_spicc_setup_rx_irq(spicc, irq); /* Setup burst */
meson_spicc_setup_burst(spicc);
/* Start burst */ /* Start burst */
writel_bits_relaxed(SPICC_XCH, SPICC_XCH, spicc->base + SPICC_CONREG); writel_bits_relaxed(SPICC_XCH, SPICC_XCH, spicc->base + SPICC_CONREG);
/* Enable interrupts */ /* Enable interrupts */
writel_relaxed(irq, spicc->base + SPICC_INTREG); writel_relaxed(SPICC_TC_EN, spicc->base + SPICC_INTREG);
return 1; return 1;
} }
...@@ -499,7 +468,7 @@ static int meson_spicc_prepare_message(struct spi_master *master, ...@@ -499,7 +468,7 @@ static int meson_spicc_prepare_message(struct spi_master *master,
/* Setup no wait cycles by default */ /* Setup no wait cycles by default */
writel_relaxed(0, spicc->base + SPICC_PERIODREG); writel_relaxed(0, spicc->base + SPICC_PERIODREG);
writel_bits_relaxed(BIT(24), BIT(24), spicc->base + SPICC_TESTREG); writel_bits_relaxed(SPICC_LBC_W1, 0, spicc->base + SPICC_TESTREG);
return 0; return 0;
} }
...@@ -797,11 +766,13 @@ static int meson_spicc_remove(struct platform_device *pdev) ...@@ -797,11 +766,13 @@ static int meson_spicc_remove(struct platform_device *pdev)
static const struct meson_spicc_data meson_spicc_gx_data = { static const struct meson_spicc_data meson_spicc_gx_data = {
.max_speed_hz = 30000000, .max_speed_hz = 30000000,
.min_speed_hz = 325000, .min_speed_hz = 325000,
.fifo_size = 16,
}; };
static const struct meson_spicc_data meson_spicc_axg_data = { static const struct meson_spicc_data meson_spicc_axg_data = {
.max_speed_hz = 80000000, .max_speed_hz = 80000000,
.min_speed_hz = 325000, .min_speed_hz = 325000,
.fifo_size = 16,
.has_oen = true, .has_oen = true,
.has_enhance_clk_div = true, .has_enhance_clk_div = true,
}; };
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment