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

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.com


Signed-off-by: default avatarMark Brown <broonie@kernel.org>
parent f27bff47
Loading
Loading
Loading
Loading
+50 −79
Original line number Diff line number Diff line
@@ -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,36 +267,13 @@ 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;

		/* 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) {
	if (!spicc->xfer_remain) {
		/* Disable all IRQs */
		writel(0, spicc->base + SPICC_INTREG);

@@ -321,23 +282,11 @@ static irqreturn_t meson_spicc_irq(int irq, void *data)
		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);
	meson_spicc_setup_burst(spicc);

		/* 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_setup_burst(spicc, burst_len);
	meson_spicc_reset_fifo(spicc);

	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,
};