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: Neil Armstrong <narmstrong@baylibre.com>
Link: https://lore.kernel.org/r/20200312133131.26430-8-narmstrong@baylibre.com
Signed-off-by: Mark Brown <broonie@kernel.org>
This commit is contained in:
Neil Armstrong 2020-03-12 14:31:29 +01:00 committed by Mark Brown
parent f27bff479e
commit 0eb707ac7d
No known key found for this signature in database
GPG Key ID: 24D68B725D5487D0
1 changed files with 50 additions and 79 deletions

View File

@ -141,12 +141,10 @@
#define writel_bits_relaxed(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 {
unsigned int max_speed_hz;
unsigned int min_speed_hz;
unsigned int fifo_size;
bool has_oen;
bool has_enhance_clk_div;
};
@ -166,8 +164,6 @@ struct meson_spicc_device {
unsigned long tx_remain;
unsigned long rx_remain;
unsigned long xfer_remain;
bool is_burst_end;
bool is_last_burst;
};
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)
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));
}
@ -246,34 +242,22 @@ static inline void meson_spicc_tx(struct meson_spicc_device *spicc)
spicc->base + SPICC_TXDATA);
}
static inline u32 meson_spicc_setup_rx_irq(struct meson_spicc_device *spicc,
u32 irq_ctrl)
static inline void meson_spicc_setup_burst(struct meson_spicc_device *spicc)
{
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)
{
unsigned int burst_len = min_t(unsigned int,
spicc->xfer_remain /
spicc->bytes_per_word,
spicc->data->fifo_size);
/* Setup Xfer variables */
spicc->tx_remain = burst_len;
spicc->rx_remain = burst_len;
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 */
writel_bits_relaxed(SPICC_BURSTLENGTH_MASK,
FIELD_PREP(SPICC_BURSTLENGTH_MASK,
burst_len),
burst_len - 1),
spicc->base + SPICC_CONREG);
/* Fill TX FIFO */
@ -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)
{
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 */
meson_spicc_rx(spicc);
/* Enable TC interrupt since we transferred everything */
if (!spicc->tx_remain && !spicc->rx_remain) {
spicc->is_burst_end = true;
if (!spicc->xfer_remain) {
/* Disable all IRQs */
writel(0, spicc->base + SPICC_INTREG);
/* Enable TC interrupt */
ctrl |= SPICC_TC_EN;
spi_finalize_current_transfer(spicc->master);
/* Reload IRQ status */
stat = readl_relaxed(spicc->base + SPICC_STATREG) & ctrl;
return IRQ_HANDLED;
}
/* Check transfer complete */
if ((stat & SPICC_TC) && spicc->is_burst_end) {
unsigned int burst_len;
/* Setup burst */
meson_spicc_setup_burst(spicc);
/* 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;
}
burst_len = min_t(unsigned int,
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 */
ctrl = meson_spicc_setup_rx_irq(spicc, ctrl);
/* Reconfigure interrupts */
writel(ctrl, spicc->base + SPICC_INTREG);
/* Start burst */
writel_bits_relaxed(SPICC_XCH, SPICC_XCH, spicc->base + SPICC_CONREG);
return IRQ_HANDLED;
}
@ -405,6 +354,28 @@ static void meson_spicc_setup_xfer(struct meson_spicc_device *spicc,
clk_set_rate(spicc->clk, xfer->speed_hz);
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,
@ -412,8 +383,6 @@ static int meson_spicc_transfer_one(struct spi_master *master,
struct spi_transfer *xfer)
{
struct meson_spicc_device *spicc = spi_master_get_devdata(master);
unsigned int burst_len;
u32 irq = 0;
/* Store current transfer */
spicc->xfer = xfer;
@ -427,22 +396,22 @@ static int meson_spicc_transfer_one(struct spi_master *master,
spicc->bytes_per_word =
DIV_ROUND_UP(spicc->xfer->bits_per_word, 8);
if (xfer->len % spicc->bytes_per_word)
return -EINVAL;
/* Setup transfer parameters */
meson_spicc_setup_xfer(spicc, xfer);
burst_len = min_t(unsigned int,
spicc->xfer_remain / spicc->bytes_per_word,
SPICC_BURST_MAX);
meson_spicc_reset_fifo(spicc);
meson_spicc_setup_burst(spicc, burst_len);
irq = meson_spicc_setup_rx_irq(spicc, irq);
/* Setup burst */
meson_spicc_setup_burst(spicc);
/* Start burst */
writel_bits_relaxed(SPICC_XCH, SPICC_XCH, spicc->base + SPICC_CONREG);
/* Enable interrupts */
writel_relaxed(irq, spicc->base + SPICC_INTREG);
writel_relaxed(SPICC_TC_EN, spicc->base + SPICC_INTREG);
return 1;
}
@ -499,7 +468,7 @@ static int meson_spicc_prepare_message(struct spi_master *master,
/* Setup no wait cycles by default */
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;
}
@ -797,11 +766,13 @@ static int meson_spicc_remove(struct platform_device *pdev)
static const struct meson_spicc_data meson_spicc_gx_data = {
.max_speed_hz = 30000000,
.min_speed_hz = 325000,
.fifo_size = 16,
};
static const struct meson_spicc_data meson_spicc_axg_data = {
.max_speed_hz = 80000000,
.min_speed_hz = 325000,
.fifo_size = 16,
.has_oen = true,
.has_enhance_clk_div = true,
};