162306a36Sopenharmony_ci/*
262306a36Sopenharmony_ci * Driver for Amlogic Meson SPI communication controller (SPICC)
362306a36Sopenharmony_ci *
462306a36Sopenharmony_ci * Copyright (C) BayLibre, SAS
562306a36Sopenharmony_ci * Author: Neil Armstrong <narmstrong@baylibre.com>
662306a36Sopenharmony_ci *
762306a36Sopenharmony_ci * SPDX-License-Identifier: GPL-2.0+
862306a36Sopenharmony_ci */
962306a36Sopenharmony_ci
1062306a36Sopenharmony_ci#include <linux/bitfield.h>
1162306a36Sopenharmony_ci#include <linux/clk.h>
1262306a36Sopenharmony_ci#include <linux/clk-provider.h>
1362306a36Sopenharmony_ci#include <linux/device.h>
1462306a36Sopenharmony_ci#include <linux/io.h>
1562306a36Sopenharmony_ci#include <linux/kernel.h>
1662306a36Sopenharmony_ci#include <linux/module.h>
1762306a36Sopenharmony_ci#include <linux/of.h>
1862306a36Sopenharmony_ci#include <linux/platform_device.h>
1962306a36Sopenharmony_ci#include <linux/spi/spi.h>
2062306a36Sopenharmony_ci#include <linux/types.h>
2162306a36Sopenharmony_ci#include <linux/interrupt.h>
2262306a36Sopenharmony_ci#include <linux/reset.h>
2362306a36Sopenharmony_ci#include <linux/pinctrl/consumer.h>
2462306a36Sopenharmony_ci
2562306a36Sopenharmony_ci/*
2662306a36Sopenharmony_ci * The Meson SPICC controller could support DMA based transfers, but is not
2762306a36Sopenharmony_ci * implemented by the vendor code, and while having the registers documentation
2862306a36Sopenharmony_ci * it has never worked on the GXL Hardware.
2962306a36Sopenharmony_ci * The PIO mode is the only mode implemented, and due to badly designed HW :
3062306a36Sopenharmony_ci * - all transfers are cutted in 16 words burst because the FIFO hangs on
3162306a36Sopenharmony_ci *   TX underflow, and there is no TX "Half-Empty" interrupt, so we go by
3262306a36Sopenharmony_ci *   FIFO max size chunk only
3362306a36Sopenharmony_ci * - CS management is dumb, and goes UP between every burst, so is really a
3462306a36Sopenharmony_ci *   "Data Valid" signal than a Chip Select, GPIO link should be used instead
3562306a36Sopenharmony_ci *   to have a CS go down over the full transfer
3662306a36Sopenharmony_ci */
3762306a36Sopenharmony_ci
3862306a36Sopenharmony_ci#define SPICC_MAX_BURST	128
3962306a36Sopenharmony_ci
4062306a36Sopenharmony_ci/* Register Map */
4162306a36Sopenharmony_ci#define SPICC_RXDATA	0x00
4262306a36Sopenharmony_ci
4362306a36Sopenharmony_ci#define SPICC_TXDATA	0x04
4462306a36Sopenharmony_ci
4562306a36Sopenharmony_ci#define SPICC_CONREG	0x08
4662306a36Sopenharmony_ci#define SPICC_ENABLE		BIT(0)
4762306a36Sopenharmony_ci#define SPICC_MODE_MASTER	BIT(1)
4862306a36Sopenharmony_ci#define SPICC_XCH		BIT(2)
4962306a36Sopenharmony_ci#define SPICC_SMC		BIT(3)
5062306a36Sopenharmony_ci#define SPICC_POL		BIT(4)
5162306a36Sopenharmony_ci#define SPICC_PHA		BIT(5)
5262306a36Sopenharmony_ci#define SPICC_SSCTL		BIT(6)
5362306a36Sopenharmony_ci#define SPICC_SSPOL		BIT(7)
5462306a36Sopenharmony_ci#define SPICC_DRCTL_MASK	GENMASK(9, 8)
5562306a36Sopenharmony_ci#define SPICC_DRCTL_IGNORE	0
5662306a36Sopenharmony_ci#define SPICC_DRCTL_FALLING	1
5762306a36Sopenharmony_ci#define SPICC_DRCTL_LOWLEVEL	2
5862306a36Sopenharmony_ci#define SPICC_CS_MASK		GENMASK(13, 12)
5962306a36Sopenharmony_ci#define SPICC_DATARATE_MASK	GENMASK(18, 16)
6062306a36Sopenharmony_ci#define SPICC_DATARATE_DIV4	0
6162306a36Sopenharmony_ci#define SPICC_DATARATE_DIV8	1
6262306a36Sopenharmony_ci#define SPICC_DATARATE_DIV16	2
6362306a36Sopenharmony_ci#define SPICC_DATARATE_DIV32	3
6462306a36Sopenharmony_ci#define SPICC_BITLENGTH_MASK	GENMASK(24, 19)
6562306a36Sopenharmony_ci#define SPICC_BURSTLENGTH_MASK	GENMASK(31, 25)
6662306a36Sopenharmony_ci
6762306a36Sopenharmony_ci#define SPICC_INTREG	0x0c
6862306a36Sopenharmony_ci#define SPICC_TE_EN	BIT(0) /* TX FIFO Empty Interrupt */
6962306a36Sopenharmony_ci#define SPICC_TH_EN	BIT(1) /* TX FIFO Half-Full Interrupt */
7062306a36Sopenharmony_ci#define SPICC_TF_EN	BIT(2) /* TX FIFO Full Interrupt */
7162306a36Sopenharmony_ci#define SPICC_RR_EN	BIT(3) /* RX FIFO Ready Interrupt */
7262306a36Sopenharmony_ci#define SPICC_RH_EN	BIT(4) /* RX FIFO Half-Full Interrupt */
7362306a36Sopenharmony_ci#define SPICC_RF_EN	BIT(5) /* RX FIFO Full Interrupt */
7462306a36Sopenharmony_ci#define SPICC_RO_EN	BIT(6) /* RX FIFO Overflow Interrupt */
7562306a36Sopenharmony_ci#define SPICC_TC_EN	BIT(7) /* Transfert Complete Interrupt */
7662306a36Sopenharmony_ci
7762306a36Sopenharmony_ci#define SPICC_DMAREG	0x10
7862306a36Sopenharmony_ci#define SPICC_DMA_ENABLE		BIT(0)
7962306a36Sopenharmony_ci#define SPICC_TXFIFO_THRESHOLD_MASK	GENMASK(5, 1)
8062306a36Sopenharmony_ci#define SPICC_RXFIFO_THRESHOLD_MASK	GENMASK(10, 6)
8162306a36Sopenharmony_ci#define SPICC_READ_BURST_MASK		GENMASK(14, 11)
8262306a36Sopenharmony_ci#define SPICC_WRITE_BURST_MASK		GENMASK(18, 15)
8362306a36Sopenharmony_ci#define SPICC_DMA_URGENT		BIT(19)
8462306a36Sopenharmony_ci#define SPICC_DMA_THREADID_MASK		GENMASK(25, 20)
8562306a36Sopenharmony_ci#define SPICC_DMA_BURSTNUM_MASK		GENMASK(31, 26)
8662306a36Sopenharmony_ci
8762306a36Sopenharmony_ci#define SPICC_STATREG	0x14
8862306a36Sopenharmony_ci#define SPICC_TE	BIT(0) /* TX FIFO Empty Interrupt */
8962306a36Sopenharmony_ci#define SPICC_TH	BIT(1) /* TX FIFO Half-Full Interrupt */
9062306a36Sopenharmony_ci#define SPICC_TF	BIT(2) /* TX FIFO Full Interrupt */
9162306a36Sopenharmony_ci#define SPICC_RR	BIT(3) /* RX FIFO Ready Interrupt */
9262306a36Sopenharmony_ci#define SPICC_RH	BIT(4) /* RX FIFO Half-Full Interrupt */
9362306a36Sopenharmony_ci#define SPICC_RF	BIT(5) /* RX FIFO Full Interrupt */
9462306a36Sopenharmony_ci#define SPICC_RO	BIT(6) /* RX FIFO Overflow Interrupt */
9562306a36Sopenharmony_ci#define SPICC_TC	BIT(7) /* Transfert Complete Interrupt */
9662306a36Sopenharmony_ci
9762306a36Sopenharmony_ci#define SPICC_PERIODREG	0x18
9862306a36Sopenharmony_ci#define SPICC_PERIOD	GENMASK(14, 0)	/* Wait cycles */
9962306a36Sopenharmony_ci
10062306a36Sopenharmony_ci#define SPICC_TESTREG	0x1c
10162306a36Sopenharmony_ci#define SPICC_TXCNT_MASK	GENMASK(4, 0)	/* TX FIFO Counter */
10262306a36Sopenharmony_ci#define SPICC_RXCNT_MASK	GENMASK(9, 5)	/* RX FIFO Counter */
10362306a36Sopenharmony_ci#define SPICC_SMSTATUS_MASK	GENMASK(12, 10)	/* State Machine Status */
10462306a36Sopenharmony_ci#define SPICC_LBC_RO		BIT(13)	/* Loop Back Control Read-Only */
10562306a36Sopenharmony_ci#define SPICC_LBC_W1		BIT(14) /* Loop Back Control Write-Only */
10662306a36Sopenharmony_ci#define SPICC_SWAP_RO		BIT(14) /* RX FIFO Data Swap Read-Only */
10762306a36Sopenharmony_ci#define SPICC_SWAP_W1		BIT(15) /* RX FIFO Data Swap Write-Only */
10862306a36Sopenharmony_ci#define SPICC_DLYCTL_RO_MASK	GENMASK(20, 15) /* Delay Control Read-Only */
10962306a36Sopenharmony_ci#define SPICC_MO_DELAY_MASK	GENMASK(17, 16) /* Master Output Delay */
11062306a36Sopenharmony_ci#define SPICC_MO_NO_DELAY	0
11162306a36Sopenharmony_ci#define SPICC_MO_DELAY_1_CYCLE	1
11262306a36Sopenharmony_ci#define SPICC_MO_DELAY_2_CYCLE	2
11362306a36Sopenharmony_ci#define SPICC_MO_DELAY_3_CYCLE	3
11462306a36Sopenharmony_ci#define SPICC_MI_DELAY_MASK	GENMASK(19, 18) /* Master Input Delay */
11562306a36Sopenharmony_ci#define SPICC_MI_NO_DELAY	0
11662306a36Sopenharmony_ci#define SPICC_MI_DELAY_1_CYCLE	1
11762306a36Sopenharmony_ci#define SPICC_MI_DELAY_2_CYCLE	2
11862306a36Sopenharmony_ci#define SPICC_MI_DELAY_3_CYCLE	3
11962306a36Sopenharmony_ci#define SPICC_MI_CAP_DELAY_MASK	GENMASK(21, 20) /* Master Capture Delay */
12062306a36Sopenharmony_ci#define SPICC_CAP_AHEAD_2_CYCLE	0
12162306a36Sopenharmony_ci#define SPICC_CAP_AHEAD_1_CYCLE	1
12262306a36Sopenharmony_ci#define SPICC_CAP_NO_DELAY	2
12362306a36Sopenharmony_ci#define SPICC_CAP_DELAY_1_CYCLE	3
12462306a36Sopenharmony_ci#define SPICC_FIFORST_RO_MASK	GENMASK(22, 21) /* FIFO Softreset Read-Only */
12562306a36Sopenharmony_ci#define SPICC_FIFORST_W1_MASK	GENMASK(23, 22) /* FIFO Softreset Write-Only */
12662306a36Sopenharmony_ci
12762306a36Sopenharmony_ci#define SPICC_DRADDR	0x20	/* Read Address of DMA */
12862306a36Sopenharmony_ci
12962306a36Sopenharmony_ci#define SPICC_DWADDR	0x24	/* Write Address of DMA */
13062306a36Sopenharmony_ci
13162306a36Sopenharmony_ci#define SPICC_ENH_CTL0	0x38	/* Enhanced Feature */
13262306a36Sopenharmony_ci#define SPICC_ENH_CLK_CS_DELAY_MASK	GENMASK(15, 0)
13362306a36Sopenharmony_ci#define SPICC_ENH_DATARATE_MASK		GENMASK(23, 16)
13462306a36Sopenharmony_ci#define SPICC_ENH_DATARATE_EN		BIT(24)
13562306a36Sopenharmony_ci#define SPICC_ENH_MOSI_OEN		BIT(25)
13662306a36Sopenharmony_ci#define SPICC_ENH_CLK_OEN		BIT(26)
13762306a36Sopenharmony_ci#define SPICC_ENH_CS_OEN		BIT(27)
13862306a36Sopenharmony_ci#define SPICC_ENH_CLK_CS_DELAY_EN	BIT(28)
13962306a36Sopenharmony_ci#define SPICC_ENH_MAIN_CLK_AO		BIT(29)
14062306a36Sopenharmony_ci
14162306a36Sopenharmony_ci#define writel_bits_relaxed(mask, val, addr) \
14262306a36Sopenharmony_ci	writel_relaxed((readl_relaxed(addr) & ~(mask)) | (val), addr)
14362306a36Sopenharmony_ci
14462306a36Sopenharmony_cistruct meson_spicc_data {
14562306a36Sopenharmony_ci	unsigned int			max_speed_hz;
14662306a36Sopenharmony_ci	unsigned int			min_speed_hz;
14762306a36Sopenharmony_ci	unsigned int			fifo_size;
14862306a36Sopenharmony_ci	bool				has_oen;
14962306a36Sopenharmony_ci	bool				has_enhance_clk_div;
15062306a36Sopenharmony_ci	bool				has_pclk;
15162306a36Sopenharmony_ci};
15262306a36Sopenharmony_ci
15362306a36Sopenharmony_cistruct meson_spicc_device {
15462306a36Sopenharmony_ci	struct spi_master		*master;
15562306a36Sopenharmony_ci	struct platform_device		*pdev;
15662306a36Sopenharmony_ci	void __iomem			*base;
15762306a36Sopenharmony_ci	struct clk			*core;
15862306a36Sopenharmony_ci	struct clk			*pclk;
15962306a36Sopenharmony_ci	struct clk_divider		pow2_div;
16062306a36Sopenharmony_ci	struct clk			*clk;
16162306a36Sopenharmony_ci	struct spi_message		*message;
16262306a36Sopenharmony_ci	struct spi_transfer		*xfer;
16362306a36Sopenharmony_ci	struct completion		done;
16462306a36Sopenharmony_ci	const struct meson_spicc_data	*data;
16562306a36Sopenharmony_ci	u8				*tx_buf;
16662306a36Sopenharmony_ci	u8				*rx_buf;
16762306a36Sopenharmony_ci	unsigned int			bytes_per_word;
16862306a36Sopenharmony_ci	unsigned long			tx_remain;
16962306a36Sopenharmony_ci	unsigned long			rx_remain;
17062306a36Sopenharmony_ci	unsigned long			xfer_remain;
17162306a36Sopenharmony_ci	struct pinctrl			*pinctrl;
17262306a36Sopenharmony_ci	struct pinctrl_state		*pins_idle_high;
17362306a36Sopenharmony_ci	struct pinctrl_state		*pins_idle_low;
17462306a36Sopenharmony_ci};
17562306a36Sopenharmony_ci
17662306a36Sopenharmony_ci#define pow2_clk_to_spicc(_div) container_of(_div, struct meson_spicc_device, pow2_div)
17762306a36Sopenharmony_ci
17862306a36Sopenharmony_cistatic void meson_spicc_oen_enable(struct meson_spicc_device *spicc)
17962306a36Sopenharmony_ci{
18062306a36Sopenharmony_ci	u32 conf;
18162306a36Sopenharmony_ci
18262306a36Sopenharmony_ci	if (!spicc->data->has_oen) {
18362306a36Sopenharmony_ci		/* Try to get pinctrl states for idle high/low */
18462306a36Sopenharmony_ci		spicc->pins_idle_high = pinctrl_lookup_state(spicc->pinctrl,
18562306a36Sopenharmony_ci							     "idle-high");
18662306a36Sopenharmony_ci		if (IS_ERR(spicc->pins_idle_high)) {
18762306a36Sopenharmony_ci			dev_warn(&spicc->pdev->dev, "can't get idle-high pinctrl\n");
18862306a36Sopenharmony_ci			spicc->pins_idle_high = NULL;
18962306a36Sopenharmony_ci		}
19062306a36Sopenharmony_ci		spicc->pins_idle_low = pinctrl_lookup_state(spicc->pinctrl,
19162306a36Sopenharmony_ci							     "idle-low");
19262306a36Sopenharmony_ci		if (IS_ERR(spicc->pins_idle_low)) {
19362306a36Sopenharmony_ci			dev_warn(&spicc->pdev->dev, "can't get idle-low pinctrl\n");
19462306a36Sopenharmony_ci			spicc->pins_idle_low = NULL;
19562306a36Sopenharmony_ci		}
19662306a36Sopenharmony_ci		return;
19762306a36Sopenharmony_ci	}
19862306a36Sopenharmony_ci
19962306a36Sopenharmony_ci	conf = readl_relaxed(spicc->base + SPICC_ENH_CTL0) |
20062306a36Sopenharmony_ci		SPICC_ENH_MOSI_OEN | SPICC_ENH_CLK_OEN | SPICC_ENH_CS_OEN;
20162306a36Sopenharmony_ci
20262306a36Sopenharmony_ci	writel_relaxed(conf, spicc->base + SPICC_ENH_CTL0);
20362306a36Sopenharmony_ci}
20462306a36Sopenharmony_ci
20562306a36Sopenharmony_cistatic inline bool meson_spicc_txfull(struct meson_spicc_device *spicc)
20662306a36Sopenharmony_ci{
20762306a36Sopenharmony_ci	return !!FIELD_GET(SPICC_TF,
20862306a36Sopenharmony_ci			   readl_relaxed(spicc->base + SPICC_STATREG));
20962306a36Sopenharmony_ci}
21062306a36Sopenharmony_ci
21162306a36Sopenharmony_cistatic inline bool meson_spicc_rxready(struct meson_spicc_device *spicc)
21262306a36Sopenharmony_ci{
21362306a36Sopenharmony_ci	return FIELD_GET(SPICC_RH | SPICC_RR | SPICC_RF,
21462306a36Sopenharmony_ci			 readl_relaxed(spicc->base + SPICC_STATREG));
21562306a36Sopenharmony_ci}
21662306a36Sopenharmony_ci
21762306a36Sopenharmony_cistatic inline u32 meson_spicc_pull_data(struct meson_spicc_device *spicc)
21862306a36Sopenharmony_ci{
21962306a36Sopenharmony_ci	unsigned int bytes = spicc->bytes_per_word;
22062306a36Sopenharmony_ci	unsigned int byte_shift = 0;
22162306a36Sopenharmony_ci	u32 data = 0;
22262306a36Sopenharmony_ci	u8 byte;
22362306a36Sopenharmony_ci
22462306a36Sopenharmony_ci	while (bytes--) {
22562306a36Sopenharmony_ci		byte = *spicc->tx_buf++;
22662306a36Sopenharmony_ci		data |= (byte & 0xff) << byte_shift;
22762306a36Sopenharmony_ci		byte_shift += 8;
22862306a36Sopenharmony_ci	}
22962306a36Sopenharmony_ci
23062306a36Sopenharmony_ci	spicc->tx_remain--;
23162306a36Sopenharmony_ci	return data;
23262306a36Sopenharmony_ci}
23362306a36Sopenharmony_ci
23462306a36Sopenharmony_cistatic inline void meson_spicc_push_data(struct meson_spicc_device *spicc,
23562306a36Sopenharmony_ci					 u32 data)
23662306a36Sopenharmony_ci{
23762306a36Sopenharmony_ci	unsigned int bytes = spicc->bytes_per_word;
23862306a36Sopenharmony_ci	unsigned int byte_shift = 0;
23962306a36Sopenharmony_ci	u8 byte;
24062306a36Sopenharmony_ci
24162306a36Sopenharmony_ci	while (bytes--) {
24262306a36Sopenharmony_ci		byte = (data >> byte_shift) & 0xff;
24362306a36Sopenharmony_ci		*spicc->rx_buf++ = byte;
24462306a36Sopenharmony_ci		byte_shift += 8;
24562306a36Sopenharmony_ci	}
24662306a36Sopenharmony_ci
24762306a36Sopenharmony_ci	spicc->rx_remain--;
24862306a36Sopenharmony_ci}
24962306a36Sopenharmony_ci
25062306a36Sopenharmony_cistatic inline void meson_spicc_rx(struct meson_spicc_device *spicc)
25162306a36Sopenharmony_ci{
25262306a36Sopenharmony_ci	/* Empty RX FIFO */
25362306a36Sopenharmony_ci	while (spicc->rx_remain &&
25462306a36Sopenharmony_ci	       meson_spicc_rxready(spicc))
25562306a36Sopenharmony_ci		meson_spicc_push_data(spicc,
25662306a36Sopenharmony_ci				readl_relaxed(spicc->base + SPICC_RXDATA));
25762306a36Sopenharmony_ci}
25862306a36Sopenharmony_ci
25962306a36Sopenharmony_cistatic inline void meson_spicc_tx(struct meson_spicc_device *spicc)
26062306a36Sopenharmony_ci{
26162306a36Sopenharmony_ci	/* Fill Up TX FIFO */
26262306a36Sopenharmony_ci	while (spicc->tx_remain &&
26362306a36Sopenharmony_ci	       !meson_spicc_txfull(spicc))
26462306a36Sopenharmony_ci		writel_relaxed(meson_spicc_pull_data(spicc),
26562306a36Sopenharmony_ci			       spicc->base + SPICC_TXDATA);
26662306a36Sopenharmony_ci}
26762306a36Sopenharmony_ci
26862306a36Sopenharmony_cistatic inline void meson_spicc_setup_burst(struct meson_spicc_device *spicc)
26962306a36Sopenharmony_ci{
27062306a36Sopenharmony_ci
27162306a36Sopenharmony_ci	unsigned int burst_len = min_t(unsigned int,
27262306a36Sopenharmony_ci				       spicc->xfer_remain /
27362306a36Sopenharmony_ci				       spicc->bytes_per_word,
27462306a36Sopenharmony_ci				       spicc->data->fifo_size);
27562306a36Sopenharmony_ci	/* Setup Xfer variables */
27662306a36Sopenharmony_ci	spicc->tx_remain = burst_len;
27762306a36Sopenharmony_ci	spicc->rx_remain = burst_len;
27862306a36Sopenharmony_ci	spicc->xfer_remain -= burst_len * spicc->bytes_per_word;
27962306a36Sopenharmony_ci
28062306a36Sopenharmony_ci	/* Setup burst length */
28162306a36Sopenharmony_ci	writel_bits_relaxed(SPICC_BURSTLENGTH_MASK,
28262306a36Sopenharmony_ci			FIELD_PREP(SPICC_BURSTLENGTH_MASK,
28362306a36Sopenharmony_ci				burst_len - 1),
28462306a36Sopenharmony_ci			spicc->base + SPICC_CONREG);
28562306a36Sopenharmony_ci
28662306a36Sopenharmony_ci	/* Fill TX FIFO */
28762306a36Sopenharmony_ci	meson_spicc_tx(spicc);
28862306a36Sopenharmony_ci}
28962306a36Sopenharmony_ci
29062306a36Sopenharmony_cistatic irqreturn_t meson_spicc_irq(int irq, void *data)
29162306a36Sopenharmony_ci{
29262306a36Sopenharmony_ci	struct meson_spicc_device *spicc = (void *) data;
29362306a36Sopenharmony_ci
29462306a36Sopenharmony_ci	writel_bits_relaxed(SPICC_TC, SPICC_TC, spicc->base + SPICC_STATREG);
29562306a36Sopenharmony_ci
29662306a36Sopenharmony_ci	/* Empty RX FIFO */
29762306a36Sopenharmony_ci	meson_spicc_rx(spicc);
29862306a36Sopenharmony_ci
29962306a36Sopenharmony_ci	if (!spicc->xfer_remain) {
30062306a36Sopenharmony_ci		/* Disable all IRQs */
30162306a36Sopenharmony_ci		writel(0, spicc->base + SPICC_INTREG);
30262306a36Sopenharmony_ci
30362306a36Sopenharmony_ci		complete(&spicc->done);
30462306a36Sopenharmony_ci
30562306a36Sopenharmony_ci		return IRQ_HANDLED;
30662306a36Sopenharmony_ci	}
30762306a36Sopenharmony_ci
30862306a36Sopenharmony_ci	/* Setup burst */
30962306a36Sopenharmony_ci	meson_spicc_setup_burst(spicc);
31062306a36Sopenharmony_ci
31162306a36Sopenharmony_ci	/* Start burst */
31262306a36Sopenharmony_ci	writel_bits_relaxed(SPICC_XCH, SPICC_XCH, spicc->base + SPICC_CONREG);
31362306a36Sopenharmony_ci
31462306a36Sopenharmony_ci	return IRQ_HANDLED;
31562306a36Sopenharmony_ci}
31662306a36Sopenharmony_ci
31762306a36Sopenharmony_cistatic void meson_spicc_auto_io_delay(struct meson_spicc_device *spicc)
31862306a36Sopenharmony_ci{
31962306a36Sopenharmony_ci	u32 div, hz;
32062306a36Sopenharmony_ci	u32 mi_delay, cap_delay;
32162306a36Sopenharmony_ci	u32 conf;
32262306a36Sopenharmony_ci
32362306a36Sopenharmony_ci	if (spicc->data->has_enhance_clk_div) {
32462306a36Sopenharmony_ci		div = FIELD_GET(SPICC_ENH_DATARATE_MASK,
32562306a36Sopenharmony_ci				readl_relaxed(spicc->base + SPICC_ENH_CTL0));
32662306a36Sopenharmony_ci		div++;
32762306a36Sopenharmony_ci		div <<= 1;
32862306a36Sopenharmony_ci	} else {
32962306a36Sopenharmony_ci		div = FIELD_GET(SPICC_DATARATE_MASK,
33062306a36Sopenharmony_ci				readl_relaxed(spicc->base + SPICC_CONREG));
33162306a36Sopenharmony_ci		div += 2;
33262306a36Sopenharmony_ci		div = 1 << div;
33362306a36Sopenharmony_ci	}
33462306a36Sopenharmony_ci
33562306a36Sopenharmony_ci	mi_delay = SPICC_MI_NO_DELAY;
33662306a36Sopenharmony_ci	cap_delay = SPICC_CAP_AHEAD_2_CYCLE;
33762306a36Sopenharmony_ci	hz = clk_get_rate(spicc->clk);
33862306a36Sopenharmony_ci
33962306a36Sopenharmony_ci	if (hz >= 100000000)
34062306a36Sopenharmony_ci		cap_delay = SPICC_CAP_DELAY_1_CYCLE;
34162306a36Sopenharmony_ci	else if (hz >= 80000000)
34262306a36Sopenharmony_ci		cap_delay = SPICC_CAP_NO_DELAY;
34362306a36Sopenharmony_ci	else if (hz >= 40000000)
34462306a36Sopenharmony_ci		cap_delay = SPICC_CAP_AHEAD_1_CYCLE;
34562306a36Sopenharmony_ci	else if (div >= 16)
34662306a36Sopenharmony_ci		mi_delay = SPICC_MI_DELAY_3_CYCLE;
34762306a36Sopenharmony_ci	else if (div >= 8)
34862306a36Sopenharmony_ci		mi_delay = SPICC_MI_DELAY_2_CYCLE;
34962306a36Sopenharmony_ci	else if (div >= 6)
35062306a36Sopenharmony_ci		mi_delay = SPICC_MI_DELAY_1_CYCLE;
35162306a36Sopenharmony_ci
35262306a36Sopenharmony_ci	conf = readl_relaxed(spicc->base + SPICC_TESTREG);
35362306a36Sopenharmony_ci	conf &= ~(SPICC_MO_DELAY_MASK | SPICC_MI_DELAY_MASK
35462306a36Sopenharmony_ci		  | SPICC_MI_CAP_DELAY_MASK);
35562306a36Sopenharmony_ci	conf |= FIELD_PREP(SPICC_MI_DELAY_MASK, mi_delay);
35662306a36Sopenharmony_ci	conf |= FIELD_PREP(SPICC_MI_CAP_DELAY_MASK, cap_delay);
35762306a36Sopenharmony_ci	writel_relaxed(conf, spicc->base + SPICC_TESTREG);
35862306a36Sopenharmony_ci}
35962306a36Sopenharmony_ci
36062306a36Sopenharmony_cistatic void meson_spicc_setup_xfer(struct meson_spicc_device *spicc,
36162306a36Sopenharmony_ci				   struct spi_transfer *xfer)
36262306a36Sopenharmony_ci{
36362306a36Sopenharmony_ci	u32 conf, conf_orig;
36462306a36Sopenharmony_ci
36562306a36Sopenharmony_ci	/* Read original configuration */
36662306a36Sopenharmony_ci	conf = conf_orig = readl_relaxed(spicc->base + SPICC_CONREG);
36762306a36Sopenharmony_ci
36862306a36Sopenharmony_ci	/* Setup word width */
36962306a36Sopenharmony_ci	conf &= ~SPICC_BITLENGTH_MASK;
37062306a36Sopenharmony_ci	conf |= FIELD_PREP(SPICC_BITLENGTH_MASK,
37162306a36Sopenharmony_ci			   (spicc->bytes_per_word << 3) - 1);
37262306a36Sopenharmony_ci
37362306a36Sopenharmony_ci	/* Ignore if unchanged */
37462306a36Sopenharmony_ci	if (conf != conf_orig)
37562306a36Sopenharmony_ci		writel_relaxed(conf, spicc->base + SPICC_CONREG);
37662306a36Sopenharmony_ci
37762306a36Sopenharmony_ci	clk_set_rate(spicc->clk, xfer->speed_hz);
37862306a36Sopenharmony_ci
37962306a36Sopenharmony_ci	meson_spicc_auto_io_delay(spicc);
38062306a36Sopenharmony_ci
38162306a36Sopenharmony_ci	writel_relaxed(0, spicc->base + SPICC_DMAREG);
38262306a36Sopenharmony_ci}
38362306a36Sopenharmony_ci
38462306a36Sopenharmony_cistatic void meson_spicc_reset_fifo(struct meson_spicc_device *spicc)
38562306a36Sopenharmony_ci{
38662306a36Sopenharmony_ci	if (spicc->data->has_oen)
38762306a36Sopenharmony_ci		writel_bits_relaxed(SPICC_ENH_MAIN_CLK_AO,
38862306a36Sopenharmony_ci				    SPICC_ENH_MAIN_CLK_AO,
38962306a36Sopenharmony_ci				    spicc->base + SPICC_ENH_CTL0);
39062306a36Sopenharmony_ci
39162306a36Sopenharmony_ci	writel_bits_relaxed(SPICC_FIFORST_W1_MASK, SPICC_FIFORST_W1_MASK,
39262306a36Sopenharmony_ci			    spicc->base + SPICC_TESTREG);
39362306a36Sopenharmony_ci
39462306a36Sopenharmony_ci	while (meson_spicc_rxready(spicc))
39562306a36Sopenharmony_ci		readl_relaxed(spicc->base + SPICC_RXDATA);
39662306a36Sopenharmony_ci
39762306a36Sopenharmony_ci	if (spicc->data->has_oen)
39862306a36Sopenharmony_ci		writel_bits_relaxed(SPICC_ENH_MAIN_CLK_AO, 0,
39962306a36Sopenharmony_ci				    spicc->base + SPICC_ENH_CTL0);
40062306a36Sopenharmony_ci}
40162306a36Sopenharmony_ci
40262306a36Sopenharmony_cistatic int meson_spicc_transfer_one(struct spi_master *master,
40362306a36Sopenharmony_ci				    struct spi_device *spi,
40462306a36Sopenharmony_ci				    struct spi_transfer *xfer)
40562306a36Sopenharmony_ci{
40662306a36Sopenharmony_ci	struct meson_spicc_device *spicc = spi_master_get_devdata(master);
40762306a36Sopenharmony_ci	uint64_t timeout;
40862306a36Sopenharmony_ci
40962306a36Sopenharmony_ci	/* Store current transfer */
41062306a36Sopenharmony_ci	spicc->xfer = xfer;
41162306a36Sopenharmony_ci
41262306a36Sopenharmony_ci	/* Setup transfer parameters */
41362306a36Sopenharmony_ci	spicc->tx_buf = (u8 *)xfer->tx_buf;
41462306a36Sopenharmony_ci	spicc->rx_buf = (u8 *)xfer->rx_buf;
41562306a36Sopenharmony_ci	spicc->xfer_remain = xfer->len;
41662306a36Sopenharmony_ci
41762306a36Sopenharmony_ci	/* Pre-calculate word size */
41862306a36Sopenharmony_ci	spicc->bytes_per_word =
41962306a36Sopenharmony_ci	   DIV_ROUND_UP(spicc->xfer->bits_per_word, 8);
42062306a36Sopenharmony_ci
42162306a36Sopenharmony_ci	if (xfer->len % spicc->bytes_per_word)
42262306a36Sopenharmony_ci		return -EINVAL;
42362306a36Sopenharmony_ci
42462306a36Sopenharmony_ci	/* Setup transfer parameters */
42562306a36Sopenharmony_ci	meson_spicc_setup_xfer(spicc, xfer);
42662306a36Sopenharmony_ci
42762306a36Sopenharmony_ci	meson_spicc_reset_fifo(spicc);
42862306a36Sopenharmony_ci
42962306a36Sopenharmony_ci	/* Setup burst */
43062306a36Sopenharmony_ci	meson_spicc_setup_burst(spicc);
43162306a36Sopenharmony_ci
43262306a36Sopenharmony_ci	/* Setup wait for completion */
43362306a36Sopenharmony_ci	reinit_completion(&spicc->done);
43462306a36Sopenharmony_ci
43562306a36Sopenharmony_ci	/* For each byte we wait for 8 cycles of the SPI clock */
43662306a36Sopenharmony_ci	timeout = 8LL * MSEC_PER_SEC * xfer->len;
43762306a36Sopenharmony_ci	do_div(timeout, xfer->speed_hz);
43862306a36Sopenharmony_ci
43962306a36Sopenharmony_ci	/* Add 10us delay between each fifo bursts */
44062306a36Sopenharmony_ci	timeout += ((xfer->len >> 4) * 10) / MSEC_PER_SEC;
44162306a36Sopenharmony_ci
44262306a36Sopenharmony_ci	/* Increase it twice and add 200 ms tolerance */
44362306a36Sopenharmony_ci	timeout += timeout + 200;
44462306a36Sopenharmony_ci
44562306a36Sopenharmony_ci	/* Start burst */
44662306a36Sopenharmony_ci	writel_bits_relaxed(SPICC_XCH, SPICC_XCH, spicc->base + SPICC_CONREG);
44762306a36Sopenharmony_ci
44862306a36Sopenharmony_ci	/* Enable interrupts */
44962306a36Sopenharmony_ci	writel_relaxed(SPICC_TC_EN, spicc->base + SPICC_INTREG);
45062306a36Sopenharmony_ci
45162306a36Sopenharmony_ci	if (!wait_for_completion_timeout(&spicc->done, msecs_to_jiffies(timeout)))
45262306a36Sopenharmony_ci		return -ETIMEDOUT;
45362306a36Sopenharmony_ci
45462306a36Sopenharmony_ci	return 0;
45562306a36Sopenharmony_ci}
45662306a36Sopenharmony_ci
45762306a36Sopenharmony_cistatic int meson_spicc_prepare_message(struct spi_master *master,
45862306a36Sopenharmony_ci				       struct spi_message *message)
45962306a36Sopenharmony_ci{
46062306a36Sopenharmony_ci	struct meson_spicc_device *spicc = spi_master_get_devdata(master);
46162306a36Sopenharmony_ci	struct spi_device *spi = message->spi;
46262306a36Sopenharmony_ci	u32 conf = readl_relaxed(spicc->base + SPICC_CONREG) & SPICC_DATARATE_MASK;
46362306a36Sopenharmony_ci
46462306a36Sopenharmony_ci	/* Store current message */
46562306a36Sopenharmony_ci	spicc->message = message;
46662306a36Sopenharmony_ci
46762306a36Sopenharmony_ci	/* Enable Master */
46862306a36Sopenharmony_ci	conf |= SPICC_ENABLE;
46962306a36Sopenharmony_ci	conf |= SPICC_MODE_MASTER;
47062306a36Sopenharmony_ci
47162306a36Sopenharmony_ci	/* SMC = 0 */
47262306a36Sopenharmony_ci
47362306a36Sopenharmony_ci	/* Setup transfer mode */
47462306a36Sopenharmony_ci	if (spi->mode & SPI_CPOL)
47562306a36Sopenharmony_ci		conf |= SPICC_POL;
47662306a36Sopenharmony_ci	else
47762306a36Sopenharmony_ci		conf &= ~SPICC_POL;
47862306a36Sopenharmony_ci
47962306a36Sopenharmony_ci	if (!spicc->data->has_oen) {
48062306a36Sopenharmony_ci		if (spi->mode & SPI_CPOL) {
48162306a36Sopenharmony_ci			if (spicc->pins_idle_high)
48262306a36Sopenharmony_ci				pinctrl_select_state(spicc->pinctrl, spicc->pins_idle_high);
48362306a36Sopenharmony_ci		} else {
48462306a36Sopenharmony_ci			if (spicc->pins_idle_low)
48562306a36Sopenharmony_ci				pinctrl_select_state(spicc->pinctrl, spicc->pins_idle_low);
48662306a36Sopenharmony_ci		}
48762306a36Sopenharmony_ci	}
48862306a36Sopenharmony_ci
48962306a36Sopenharmony_ci	if (spi->mode & SPI_CPHA)
49062306a36Sopenharmony_ci		conf |= SPICC_PHA;
49162306a36Sopenharmony_ci	else
49262306a36Sopenharmony_ci		conf &= ~SPICC_PHA;
49362306a36Sopenharmony_ci
49462306a36Sopenharmony_ci	/* SSCTL = 0 */
49562306a36Sopenharmony_ci
49662306a36Sopenharmony_ci	if (spi->mode & SPI_CS_HIGH)
49762306a36Sopenharmony_ci		conf |= SPICC_SSPOL;
49862306a36Sopenharmony_ci	else
49962306a36Sopenharmony_ci		conf &= ~SPICC_SSPOL;
50062306a36Sopenharmony_ci
50162306a36Sopenharmony_ci	if (spi->mode & SPI_READY)
50262306a36Sopenharmony_ci		conf |= FIELD_PREP(SPICC_DRCTL_MASK, SPICC_DRCTL_LOWLEVEL);
50362306a36Sopenharmony_ci	else
50462306a36Sopenharmony_ci		conf |= FIELD_PREP(SPICC_DRCTL_MASK, SPICC_DRCTL_IGNORE);
50562306a36Sopenharmony_ci
50662306a36Sopenharmony_ci	/* Select CS */
50762306a36Sopenharmony_ci	conf |= FIELD_PREP(SPICC_CS_MASK, spi_get_chipselect(spi, 0));
50862306a36Sopenharmony_ci
50962306a36Sopenharmony_ci	/* Default 8bit word */
51062306a36Sopenharmony_ci	conf |= FIELD_PREP(SPICC_BITLENGTH_MASK, 8 - 1);
51162306a36Sopenharmony_ci
51262306a36Sopenharmony_ci	writel_relaxed(conf, spicc->base + SPICC_CONREG);
51362306a36Sopenharmony_ci
51462306a36Sopenharmony_ci	/* Setup no wait cycles by default */
51562306a36Sopenharmony_ci	writel_relaxed(0, spicc->base + SPICC_PERIODREG);
51662306a36Sopenharmony_ci
51762306a36Sopenharmony_ci	writel_bits_relaxed(SPICC_LBC_W1, 0, spicc->base + SPICC_TESTREG);
51862306a36Sopenharmony_ci
51962306a36Sopenharmony_ci	return 0;
52062306a36Sopenharmony_ci}
52162306a36Sopenharmony_ci
52262306a36Sopenharmony_cistatic int meson_spicc_unprepare_transfer(struct spi_master *master)
52362306a36Sopenharmony_ci{
52462306a36Sopenharmony_ci	struct meson_spicc_device *spicc = spi_master_get_devdata(master);
52562306a36Sopenharmony_ci	u32 conf = readl_relaxed(spicc->base + SPICC_CONREG) & SPICC_DATARATE_MASK;
52662306a36Sopenharmony_ci
52762306a36Sopenharmony_ci	/* Disable all IRQs */
52862306a36Sopenharmony_ci	writel(0, spicc->base + SPICC_INTREG);
52962306a36Sopenharmony_ci
53062306a36Sopenharmony_ci	device_reset_optional(&spicc->pdev->dev);
53162306a36Sopenharmony_ci
53262306a36Sopenharmony_ci	/* Set default configuration, keeping datarate field */
53362306a36Sopenharmony_ci	writel_relaxed(conf, spicc->base + SPICC_CONREG);
53462306a36Sopenharmony_ci
53562306a36Sopenharmony_ci	if (!spicc->data->has_oen)
53662306a36Sopenharmony_ci		pinctrl_select_default_state(&spicc->pdev->dev);
53762306a36Sopenharmony_ci
53862306a36Sopenharmony_ci	return 0;
53962306a36Sopenharmony_ci}
54062306a36Sopenharmony_ci
54162306a36Sopenharmony_cistatic int meson_spicc_setup(struct spi_device *spi)
54262306a36Sopenharmony_ci{
54362306a36Sopenharmony_ci	if (!spi->controller_state)
54462306a36Sopenharmony_ci		spi->controller_state = spi_master_get_devdata(spi->master);
54562306a36Sopenharmony_ci
54662306a36Sopenharmony_ci	return 0;
54762306a36Sopenharmony_ci}
54862306a36Sopenharmony_ci
54962306a36Sopenharmony_cistatic void meson_spicc_cleanup(struct spi_device *spi)
55062306a36Sopenharmony_ci{
55162306a36Sopenharmony_ci	spi->controller_state = NULL;
55262306a36Sopenharmony_ci}
55362306a36Sopenharmony_ci
55462306a36Sopenharmony_ci/*
55562306a36Sopenharmony_ci * The Clock Mux
55662306a36Sopenharmony_ci *            x-----------------x   x------------x    x------\
55762306a36Sopenharmony_ci *        |---| pow2 fixed div  |---| pow2 div   |----|      |
55862306a36Sopenharmony_ci *        |   x-----------------x   x------------x    |      |
55962306a36Sopenharmony_ci * src ---|                                           | mux  |-- out
56062306a36Sopenharmony_ci *        |   x-----------------x   x------------x    |      |
56162306a36Sopenharmony_ci *        |---| enh fixed div   |---| enh div    |0---|      |
56262306a36Sopenharmony_ci *            x-----------------x   x------------x    x------/
56362306a36Sopenharmony_ci *
56462306a36Sopenharmony_ci * Clk path for GX series:
56562306a36Sopenharmony_ci *    src -> pow2 fixed div -> pow2 div -> out
56662306a36Sopenharmony_ci *
56762306a36Sopenharmony_ci * Clk path for AXG series:
56862306a36Sopenharmony_ci *    src -> pow2 fixed div -> pow2 div -> mux -> out
56962306a36Sopenharmony_ci *    src -> enh fixed div -> enh div -> mux -> out
57062306a36Sopenharmony_ci *
57162306a36Sopenharmony_ci * Clk path for G12A series:
57262306a36Sopenharmony_ci *    pclk -> pow2 fixed div -> pow2 div -> mux -> out
57362306a36Sopenharmony_ci *    pclk -> enh fixed div -> enh div -> mux -> out
57462306a36Sopenharmony_ci *
57562306a36Sopenharmony_ci * The pow2 divider is tied to the controller HW state, and the
57662306a36Sopenharmony_ci * divider is only valid when the controller is initialized.
57762306a36Sopenharmony_ci *
57862306a36Sopenharmony_ci * A set of clock ops is added to make sure we don't read/set this
57962306a36Sopenharmony_ci * clock rate while the controller is in an unknown state.
58062306a36Sopenharmony_ci */
58162306a36Sopenharmony_ci
58262306a36Sopenharmony_cistatic unsigned long meson_spicc_pow2_recalc_rate(struct clk_hw *hw,
58362306a36Sopenharmony_ci						  unsigned long parent_rate)
58462306a36Sopenharmony_ci{
58562306a36Sopenharmony_ci	struct clk_divider *divider = to_clk_divider(hw);
58662306a36Sopenharmony_ci	struct meson_spicc_device *spicc = pow2_clk_to_spicc(divider);
58762306a36Sopenharmony_ci
58862306a36Sopenharmony_ci	if (!spicc->master->cur_msg)
58962306a36Sopenharmony_ci		return 0;
59062306a36Sopenharmony_ci
59162306a36Sopenharmony_ci	return clk_divider_ops.recalc_rate(hw, parent_rate);
59262306a36Sopenharmony_ci}
59362306a36Sopenharmony_ci
59462306a36Sopenharmony_cistatic int meson_spicc_pow2_determine_rate(struct clk_hw *hw,
59562306a36Sopenharmony_ci					   struct clk_rate_request *req)
59662306a36Sopenharmony_ci{
59762306a36Sopenharmony_ci	struct clk_divider *divider = to_clk_divider(hw);
59862306a36Sopenharmony_ci	struct meson_spicc_device *spicc = pow2_clk_to_spicc(divider);
59962306a36Sopenharmony_ci
60062306a36Sopenharmony_ci	if (!spicc->master->cur_msg)
60162306a36Sopenharmony_ci		return -EINVAL;
60262306a36Sopenharmony_ci
60362306a36Sopenharmony_ci	return clk_divider_ops.determine_rate(hw, req);
60462306a36Sopenharmony_ci}
60562306a36Sopenharmony_ci
60662306a36Sopenharmony_cistatic int meson_spicc_pow2_set_rate(struct clk_hw *hw, unsigned long rate,
60762306a36Sopenharmony_ci				     unsigned long parent_rate)
60862306a36Sopenharmony_ci{
60962306a36Sopenharmony_ci	struct clk_divider *divider = to_clk_divider(hw);
61062306a36Sopenharmony_ci	struct meson_spicc_device *spicc = pow2_clk_to_spicc(divider);
61162306a36Sopenharmony_ci
61262306a36Sopenharmony_ci	if (!spicc->master->cur_msg)
61362306a36Sopenharmony_ci		return -EINVAL;
61462306a36Sopenharmony_ci
61562306a36Sopenharmony_ci	return clk_divider_ops.set_rate(hw, rate, parent_rate);
61662306a36Sopenharmony_ci}
61762306a36Sopenharmony_ci
61862306a36Sopenharmony_cistatic const struct clk_ops meson_spicc_pow2_clk_ops = {
61962306a36Sopenharmony_ci	.recalc_rate = meson_spicc_pow2_recalc_rate,
62062306a36Sopenharmony_ci	.determine_rate = meson_spicc_pow2_determine_rate,
62162306a36Sopenharmony_ci	.set_rate = meson_spicc_pow2_set_rate,
62262306a36Sopenharmony_ci};
62362306a36Sopenharmony_ci
62462306a36Sopenharmony_cistatic int meson_spicc_pow2_clk_init(struct meson_spicc_device *spicc)
62562306a36Sopenharmony_ci{
62662306a36Sopenharmony_ci	struct device *dev = &spicc->pdev->dev;
62762306a36Sopenharmony_ci	struct clk_fixed_factor *pow2_fixed_div;
62862306a36Sopenharmony_ci	struct clk_init_data init;
62962306a36Sopenharmony_ci	struct clk *clk;
63062306a36Sopenharmony_ci	struct clk_parent_data parent_data[2];
63162306a36Sopenharmony_ci	char name[64];
63262306a36Sopenharmony_ci
63362306a36Sopenharmony_ci	memset(&init, 0, sizeof(init));
63462306a36Sopenharmony_ci	memset(&parent_data, 0, sizeof(parent_data));
63562306a36Sopenharmony_ci
63662306a36Sopenharmony_ci	init.parent_data = parent_data;
63762306a36Sopenharmony_ci
63862306a36Sopenharmony_ci	/* algorithm for pow2 div: rate = freq / 4 / (2 ^ N) */
63962306a36Sopenharmony_ci
64062306a36Sopenharmony_ci	pow2_fixed_div = devm_kzalloc(dev, sizeof(*pow2_fixed_div), GFP_KERNEL);
64162306a36Sopenharmony_ci	if (!pow2_fixed_div)
64262306a36Sopenharmony_ci		return -ENOMEM;
64362306a36Sopenharmony_ci
64462306a36Sopenharmony_ci	snprintf(name, sizeof(name), "%s#pow2_fixed_div", dev_name(dev));
64562306a36Sopenharmony_ci	init.name = name;
64662306a36Sopenharmony_ci	init.ops = &clk_fixed_factor_ops;
64762306a36Sopenharmony_ci	init.flags = 0;
64862306a36Sopenharmony_ci	if (spicc->data->has_pclk)
64962306a36Sopenharmony_ci		parent_data[0].hw = __clk_get_hw(spicc->pclk);
65062306a36Sopenharmony_ci	else
65162306a36Sopenharmony_ci		parent_data[0].hw = __clk_get_hw(spicc->core);
65262306a36Sopenharmony_ci	init.num_parents = 1;
65362306a36Sopenharmony_ci
65462306a36Sopenharmony_ci	pow2_fixed_div->mult = 1,
65562306a36Sopenharmony_ci	pow2_fixed_div->div = 4,
65662306a36Sopenharmony_ci	pow2_fixed_div->hw.init = &init;
65762306a36Sopenharmony_ci
65862306a36Sopenharmony_ci	clk = devm_clk_register(dev, &pow2_fixed_div->hw);
65962306a36Sopenharmony_ci	if (WARN_ON(IS_ERR(clk)))
66062306a36Sopenharmony_ci		return PTR_ERR(clk);
66162306a36Sopenharmony_ci
66262306a36Sopenharmony_ci	snprintf(name, sizeof(name), "%s#pow2_div", dev_name(dev));
66362306a36Sopenharmony_ci	init.name = name;
66462306a36Sopenharmony_ci	init.ops = &meson_spicc_pow2_clk_ops;
66562306a36Sopenharmony_ci	/*
66662306a36Sopenharmony_ci	 * Set NOCACHE here to make sure we read the actual HW value
66762306a36Sopenharmony_ci	 * since we reset the HW after each transfer.
66862306a36Sopenharmony_ci	 */
66962306a36Sopenharmony_ci	init.flags = CLK_SET_RATE_PARENT | CLK_GET_RATE_NOCACHE;
67062306a36Sopenharmony_ci	parent_data[0].hw = &pow2_fixed_div->hw;
67162306a36Sopenharmony_ci	init.num_parents = 1;
67262306a36Sopenharmony_ci
67362306a36Sopenharmony_ci	spicc->pow2_div.shift = 16,
67462306a36Sopenharmony_ci	spicc->pow2_div.width = 3,
67562306a36Sopenharmony_ci	spicc->pow2_div.flags = CLK_DIVIDER_POWER_OF_TWO,
67662306a36Sopenharmony_ci	spicc->pow2_div.reg = spicc->base + SPICC_CONREG;
67762306a36Sopenharmony_ci	spicc->pow2_div.hw.init = &init;
67862306a36Sopenharmony_ci
67962306a36Sopenharmony_ci	spicc->clk = devm_clk_register(dev, &spicc->pow2_div.hw);
68062306a36Sopenharmony_ci	if (WARN_ON(IS_ERR(spicc->clk)))
68162306a36Sopenharmony_ci		return PTR_ERR(spicc->clk);
68262306a36Sopenharmony_ci
68362306a36Sopenharmony_ci	return 0;
68462306a36Sopenharmony_ci}
68562306a36Sopenharmony_ci
68662306a36Sopenharmony_cistatic int meson_spicc_enh_clk_init(struct meson_spicc_device *spicc)
68762306a36Sopenharmony_ci{
68862306a36Sopenharmony_ci	struct device *dev = &spicc->pdev->dev;
68962306a36Sopenharmony_ci	struct clk_fixed_factor *enh_fixed_div;
69062306a36Sopenharmony_ci	struct clk_divider *enh_div;
69162306a36Sopenharmony_ci	struct clk_mux *mux;
69262306a36Sopenharmony_ci	struct clk_init_data init;
69362306a36Sopenharmony_ci	struct clk *clk;
69462306a36Sopenharmony_ci	struct clk_parent_data parent_data[2];
69562306a36Sopenharmony_ci	char name[64];
69662306a36Sopenharmony_ci
69762306a36Sopenharmony_ci	memset(&init, 0, sizeof(init));
69862306a36Sopenharmony_ci	memset(&parent_data, 0, sizeof(parent_data));
69962306a36Sopenharmony_ci
70062306a36Sopenharmony_ci	init.parent_data = parent_data;
70162306a36Sopenharmony_ci
70262306a36Sopenharmony_ci	/* algorithm for enh div: rate = freq / 2 / (N + 1) */
70362306a36Sopenharmony_ci
70462306a36Sopenharmony_ci	enh_fixed_div = devm_kzalloc(dev, sizeof(*enh_fixed_div), GFP_KERNEL);
70562306a36Sopenharmony_ci	if (!enh_fixed_div)
70662306a36Sopenharmony_ci		return -ENOMEM;
70762306a36Sopenharmony_ci
70862306a36Sopenharmony_ci	snprintf(name, sizeof(name), "%s#enh_fixed_div", dev_name(dev));
70962306a36Sopenharmony_ci	init.name = name;
71062306a36Sopenharmony_ci	init.ops = &clk_fixed_factor_ops;
71162306a36Sopenharmony_ci	init.flags = 0;
71262306a36Sopenharmony_ci	if (spicc->data->has_pclk)
71362306a36Sopenharmony_ci		parent_data[0].hw = __clk_get_hw(spicc->pclk);
71462306a36Sopenharmony_ci	else
71562306a36Sopenharmony_ci		parent_data[0].hw = __clk_get_hw(spicc->core);
71662306a36Sopenharmony_ci	init.num_parents = 1;
71762306a36Sopenharmony_ci
71862306a36Sopenharmony_ci	enh_fixed_div->mult = 1,
71962306a36Sopenharmony_ci	enh_fixed_div->div = 2,
72062306a36Sopenharmony_ci	enh_fixed_div->hw.init = &init;
72162306a36Sopenharmony_ci
72262306a36Sopenharmony_ci	clk = devm_clk_register(dev, &enh_fixed_div->hw);
72362306a36Sopenharmony_ci	if (WARN_ON(IS_ERR(clk)))
72462306a36Sopenharmony_ci		return PTR_ERR(clk);
72562306a36Sopenharmony_ci
72662306a36Sopenharmony_ci	enh_div = devm_kzalloc(dev, sizeof(*enh_div), GFP_KERNEL);
72762306a36Sopenharmony_ci	if (!enh_div)
72862306a36Sopenharmony_ci		return -ENOMEM;
72962306a36Sopenharmony_ci
73062306a36Sopenharmony_ci	snprintf(name, sizeof(name), "%s#enh_div", dev_name(dev));
73162306a36Sopenharmony_ci	init.name = name;
73262306a36Sopenharmony_ci	init.ops = &clk_divider_ops;
73362306a36Sopenharmony_ci	init.flags = CLK_SET_RATE_PARENT;
73462306a36Sopenharmony_ci	parent_data[0].hw = &enh_fixed_div->hw;
73562306a36Sopenharmony_ci	init.num_parents = 1;
73662306a36Sopenharmony_ci
73762306a36Sopenharmony_ci	enh_div->shift	= 16,
73862306a36Sopenharmony_ci	enh_div->width	= 8,
73962306a36Sopenharmony_ci	enh_div->reg = spicc->base + SPICC_ENH_CTL0;
74062306a36Sopenharmony_ci	enh_div->hw.init = &init;
74162306a36Sopenharmony_ci
74262306a36Sopenharmony_ci	clk = devm_clk_register(dev, &enh_div->hw);
74362306a36Sopenharmony_ci	if (WARN_ON(IS_ERR(clk)))
74462306a36Sopenharmony_ci		return PTR_ERR(clk);
74562306a36Sopenharmony_ci
74662306a36Sopenharmony_ci	mux = devm_kzalloc(dev, sizeof(*mux), GFP_KERNEL);
74762306a36Sopenharmony_ci	if (!mux)
74862306a36Sopenharmony_ci		return -ENOMEM;
74962306a36Sopenharmony_ci
75062306a36Sopenharmony_ci	snprintf(name, sizeof(name), "%s#sel", dev_name(dev));
75162306a36Sopenharmony_ci	init.name = name;
75262306a36Sopenharmony_ci	init.ops = &clk_mux_ops;
75362306a36Sopenharmony_ci	parent_data[0].hw = &spicc->pow2_div.hw;
75462306a36Sopenharmony_ci	parent_data[1].hw = &enh_div->hw;
75562306a36Sopenharmony_ci	init.num_parents = 2;
75662306a36Sopenharmony_ci	init.flags = CLK_SET_RATE_PARENT;
75762306a36Sopenharmony_ci
75862306a36Sopenharmony_ci	mux->mask = 0x1,
75962306a36Sopenharmony_ci	mux->shift = 24,
76062306a36Sopenharmony_ci	mux->reg = spicc->base + SPICC_ENH_CTL0;
76162306a36Sopenharmony_ci	mux->hw.init = &init;
76262306a36Sopenharmony_ci
76362306a36Sopenharmony_ci	spicc->clk = devm_clk_register(dev, &mux->hw);
76462306a36Sopenharmony_ci	if (WARN_ON(IS_ERR(spicc->clk)))
76562306a36Sopenharmony_ci		return PTR_ERR(spicc->clk);
76662306a36Sopenharmony_ci
76762306a36Sopenharmony_ci	return 0;
76862306a36Sopenharmony_ci}
76962306a36Sopenharmony_ci
77062306a36Sopenharmony_cistatic int meson_spicc_probe(struct platform_device *pdev)
77162306a36Sopenharmony_ci{
77262306a36Sopenharmony_ci	struct spi_master *master;
77362306a36Sopenharmony_ci	struct meson_spicc_device *spicc;
77462306a36Sopenharmony_ci	int ret, irq;
77562306a36Sopenharmony_ci
77662306a36Sopenharmony_ci	master = spi_alloc_master(&pdev->dev, sizeof(*spicc));
77762306a36Sopenharmony_ci	if (!master) {
77862306a36Sopenharmony_ci		dev_err(&pdev->dev, "master allocation failed\n");
77962306a36Sopenharmony_ci		return -ENOMEM;
78062306a36Sopenharmony_ci	}
78162306a36Sopenharmony_ci	spicc = spi_master_get_devdata(master);
78262306a36Sopenharmony_ci	spicc->master = master;
78362306a36Sopenharmony_ci
78462306a36Sopenharmony_ci	spicc->data = of_device_get_match_data(&pdev->dev);
78562306a36Sopenharmony_ci	if (!spicc->data) {
78662306a36Sopenharmony_ci		dev_err(&pdev->dev, "failed to get match data\n");
78762306a36Sopenharmony_ci		ret = -EINVAL;
78862306a36Sopenharmony_ci		goto out_master;
78962306a36Sopenharmony_ci	}
79062306a36Sopenharmony_ci
79162306a36Sopenharmony_ci	spicc->pdev = pdev;
79262306a36Sopenharmony_ci	platform_set_drvdata(pdev, spicc);
79362306a36Sopenharmony_ci
79462306a36Sopenharmony_ci	init_completion(&spicc->done);
79562306a36Sopenharmony_ci
79662306a36Sopenharmony_ci	spicc->base = devm_platform_ioremap_resource(pdev, 0);
79762306a36Sopenharmony_ci	if (IS_ERR(spicc->base)) {
79862306a36Sopenharmony_ci		dev_err(&pdev->dev, "io resource mapping failed\n");
79962306a36Sopenharmony_ci		ret = PTR_ERR(spicc->base);
80062306a36Sopenharmony_ci		goto out_master;
80162306a36Sopenharmony_ci	}
80262306a36Sopenharmony_ci
80362306a36Sopenharmony_ci	/* Set master mode and enable controller */
80462306a36Sopenharmony_ci	writel_relaxed(SPICC_ENABLE | SPICC_MODE_MASTER,
80562306a36Sopenharmony_ci		       spicc->base + SPICC_CONREG);
80662306a36Sopenharmony_ci
80762306a36Sopenharmony_ci	/* Disable all IRQs */
80862306a36Sopenharmony_ci	writel_relaxed(0, spicc->base + SPICC_INTREG);
80962306a36Sopenharmony_ci
81062306a36Sopenharmony_ci	irq = platform_get_irq(pdev, 0);
81162306a36Sopenharmony_ci	if (irq < 0) {
81262306a36Sopenharmony_ci		ret = irq;
81362306a36Sopenharmony_ci		goto out_master;
81462306a36Sopenharmony_ci	}
81562306a36Sopenharmony_ci
81662306a36Sopenharmony_ci	ret = devm_request_irq(&pdev->dev, irq, meson_spicc_irq,
81762306a36Sopenharmony_ci			       0, NULL, spicc);
81862306a36Sopenharmony_ci	if (ret) {
81962306a36Sopenharmony_ci		dev_err(&pdev->dev, "irq request failed\n");
82062306a36Sopenharmony_ci		goto out_master;
82162306a36Sopenharmony_ci	}
82262306a36Sopenharmony_ci
82362306a36Sopenharmony_ci	spicc->core = devm_clk_get(&pdev->dev, "core");
82462306a36Sopenharmony_ci	if (IS_ERR(spicc->core)) {
82562306a36Sopenharmony_ci		dev_err(&pdev->dev, "core clock request failed\n");
82662306a36Sopenharmony_ci		ret = PTR_ERR(spicc->core);
82762306a36Sopenharmony_ci		goto out_master;
82862306a36Sopenharmony_ci	}
82962306a36Sopenharmony_ci
83062306a36Sopenharmony_ci	if (spicc->data->has_pclk) {
83162306a36Sopenharmony_ci		spicc->pclk = devm_clk_get(&pdev->dev, "pclk");
83262306a36Sopenharmony_ci		if (IS_ERR(spicc->pclk)) {
83362306a36Sopenharmony_ci			dev_err(&pdev->dev, "pclk clock request failed\n");
83462306a36Sopenharmony_ci			ret = PTR_ERR(spicc->pclk);
83562306a36Sopenharmony_ci			goto out_master;
83662306a36Sopenharmony_ci		}
83762306a36Sopenharmony_ci	}
83862306a36Sopenharmony_ci
83962306a36Sopenharmony_ci	ret = clk_prepare_enable(spicc->core);
84062306a36Sopenharmony_ci	if (ret) {
84162306a36Sopenharmony_ci		dev_err(&pdev->dev, "core clock enable failed\n");
84262306a36Sopenharmony_ci		goto out_master;
84362306a36Sopenharmony_ci	}
84462306a36Sopenharmony_ci
84562306a36Sopenharmony_ci	ret = clk_prepare_enable(spicc->pclk);
84662306a36Sopenharmony_ci	if (ret) {
84762306a36Sopenharmony_ci		dev_err(&pdev->dev, "pclk clock enable failed\n");
84862306a36Sopenharmony_ci		goto out_core_clk;
84962306a36Sopenharmony_ci	}
85062306a36Sopenharmony_ci
85162306a36Sopenharmony_ci	spicc->pinctrl = devm_pinctrl_get(&pdev->dev);
85262306a36Sopenharmony_ci	if (IS_ERR(spicc->pinctrl)) {
85362306a36Sopenharmony_ci		ret = PTR_ERR(spicc->pinctrl);
85462306a36Sopenharmony_ci		goto out_clk;
85562306a36Sopenharmony_ci	}
85662306a36Sopenharmony_ci
85762306a36Sopenharmony_ci	device_reset_optional(&pdev->dev);
85862306a36Sopenharmony_ci
85962306a36Sopenharmony_ci	master->num_chipselect = 4;
86062306a36Sopenharmony_ci	master->dev.of_node = pdev->dev.of_node;
86162306a36Sopenharmony_ci	master->mode_bits = SPI_CPHA | SPI_CPOL | SPI_CS_HIGH;
86262306a36Sopenharmony_ci	master->bits_per_word_mask = SPI_BPW_MASK(32) |
86362306a36Sopenharmony_ci				     SPI_BPW_MASK(24) |
86462306a36Sopenharmony_ci				     SPI_BPW_MASK(16) |
86562306a36Sopenharmony_ci				     SPI_BPW_MASK(8);
86662306a36Sopenharmony_ci	master->flags = (SPI_CONTROLLER_MUST_RX | SPI_CONTROLLER_MUST_TX);
86762306a36Sopenharmony_ci	master->min_speed_hz = spicc->data->min_speed_hz;
86862306a36Sopenharmony_ci	master->max_speed_hz = spicc->data->max_speed_hz;
86962306a36Sopenharmony_ci	master->setup = meson_spicc_setup;
87062306a36Sopenharmony_ci	master->cleanup = meson_spicc_cleanup;
87162306a36Sopenharmony_ci	master->prepare_message = meson_spicc_prepare_message;
87262306a36Sopenharmony_ci	master->unprepare_transfer_hardware = meson_spicc_unprepare_transfer;
87362306a36Sopenharmony_ci	master->transfer_one = meson_spicc_transfer_one;
87462306a36Sopenharmony_ci	master->use_gpio_descriptors = true;
87562306a36Sopenharmony_ci
87662306a36Sopenharmony_ci	meson_spicc_oen_enable(spicc);
87762306a36Sopenharmony_ci
87862306a36Sopenharmony_ci	ret = meson_spicc_pow2_clk_init(spicc);
87962306a36Sopenharmony_ci	if (ret) {
88062306a36Sopenharmony_ci		dev_err(&pdev->dev, "pow2 clock registration failed\n");
88162306a36Sopenharmony_ci		goto out_clk;
88262306a36Sopenharmony_ci	}
88362306a36Sopenharmony_ci
88462306a36Sopenharmony_ci	if (spicc->data->has_enhance_clk_div) {
88562306a36Sopenharmony_ci		ret = meson_spicc_enh_clk_init(spicc);
88662306a36Sopenharmony_ci		if (ret) {
88762306a36Sopenharmony_ci			dev_err(&pdev->dev, "clock registration failed\n");
88862306a36Sopenharmony_ci			goto out_clk;
88962306a36Sopenharmony_ci		}
89062306a36Sopenharmony_ci	}
89162306a36Sopenharmony_ci
89262306a36Sopenharmony_ci	ret = devm_spi_register_master(&pdev->dev, master);
89362306a36Sopenharmony_ci	if (ret) {
89462306a36Sopenharmony_ci		dev_err(&pdev->dev, "spi master registration failed\n");
89562306a36Sopenharmony_ci		goto out_clk;
89662306a36Sopenharmony_ci	}
89762306a36Sopenharmony_ci
89862306a36Sopenharmony_ci	return 0;
89962306a36Sopenharmony_ci
90062306a36Sopenharmony_ciout_clk:
90162306a36Sopenharmony_ci	clk_disable_unprepare(spicc->pclk);
90262306a36Sopenharmony_ci
90362306a36Sopenharmony_ciout_core_clk:
90462306a36Sopenharmony_ci	clk_disable_unprepare(spicc->core);
90562306a36Sopenharmony_ci
90662306a36Sopenharmony_ciout_master:
90762306a36Sopenharmony_ci	spi_master_put(master);
90862306a36Sopenharmony_ci
90962306a36Sopenharmony_ci	return ret;
91062306a36Sopenharmony_ci}
91162306a36Sopenharmony_ci
91262306a36Sopenharmony_cistatic void meson_spicc_remove(struct platform_device *pdev)
91362306a36Sopenharmony_ci{
91462306a36Sopenharmony_ci	struct meson_spicc_device *spicc = platform_get_drvdata(pdev);
91562306a36Sopenharmony_ci
91662306a36Sopenharmony_ci	/* Disable SPI */
91762306a36Sopenharmony_ci	writel(0, spicc->base + SPICC_CONREG);
91862306a36Sopenharmony_ci
91962306a36Sopenharmony_ci	clk_disable_unprepare(spicc->core);
92062306a36Sopenharmony_ci	clk_disable_unprepare(spicc->pclk);
92162306a36Sopenharmony_ci
92262306a36Sopenharmony_ci	spi_master_put(spicc->master);
92362306a36Sopenharmony_ci}
92462306a36Sopenharmony_ci
92562306a36Sopenharmony_cistatic const struct meson_spicc_data meson_spicc_gx_data = {
92662306a36Sopenharmony_ci	.max_speed_hz		= 30000000,
92762306a36Sopenharmony_ci	.min_speed_hz		= 325000,
92862306a36Sopenharmony_ci	.fifo_size		= 16,
92962306a36Sopenharmony_ci};
93062306a36Sopenharmony_ci
93162306a36Sopenharmony_cistatic const struct meson_spicc_data meson_spicc_axg_data = {
93262306a36Sopenharmony_ci	.max_speed_hz		= 80000000,
93362306a36Sopenharmony_ci	.min_speed_hz		= 325000,
93462306a36Sopenharmony_ci	.fifo_size		= 16,
93562306a36Sopenharmony_ci	.has_oen		= true,
93662306a36Sopenharmony_ci	.has_enhance_clk_div	= true,
93762306a36Sopenharmony_ci};
93862306a36Sopenharmony_ci
93962306a36Sopenharmony_cistatic const struct meson_spicc_data meson_spicc_g12a_data = {
94062306a36Sopenharmony_ci	.max_speed_hz		= 166666666,
94162306a36Sopenharmony_ci	.min_speed_hz		= 50000,
94262306a36Sopenharmony_ci	.fifo_size		= 15,
94362306a36Sopenharmony_ci	.has_oen		= true,
94462306a36Sopenharmony_ci	.has_enhance_clk_div	= true,
94562306a36Sopenharmony_ci	.has_pclk		= true,
94662306a36Sopenharmony_ci};
94762306a36Sopenharmony_ci
94862306a36Sopenharmony_cistatic const struct of_device_id meson_spicc_of_match[] = {
94962306a36Sopenharmony_ci	{
95062306a36Sopenharmony_ci		.compatible	= "amlogic,meson-gx-spicc",
95162306a36Sopenharmony_ci		.data		= &meson_spicc_gx_data,
95262306a36Sopenharmony_ci	},
95362306a36Sopenharmony_ci	{
95462306a36Sopenharmony_ci		.compatible = "amlogic,meson-axg-spicc",
95562306a36Sopenharmony_ci		.data		= &meson_spicc_axg_data,
95662306a36Sopenharmony_ci	},
95762306a36Sopenharmony_ci	{
95862306a36Sopenharmony_ci		.compatible = "amlogic,meson-g12a-spicc",
95962306a36Sopenharmony_ci		.data		= &meson_spicc_g12a_data,
96062306a36Sopenharmony_ci	},
96162306a36Sopenharmony_ci	{ /* sentinel */ }
96262306a36Sopenharmony_ci};
96362306a36Sopenharmony_ciMODULE_DEVICE_TABLE(of, meson_spicc_of_match);
96462306a36Sopenharmony_ci
96562306a36Sopenharmony_cistatic struct platform_driver meson_spicc_driver = {
96662306a36Sopenharmony_ci	.probe   = meson_spicc_probe,
96762306a36Sopenharmony_ci	.remove_new = meson_spicc_remove,
96862306a36Sopenharmony_ci	.driver  = {
96962306a36Sopenharmony_ci		.name = "meson-spicc",
97062306a36Sopenharmony_ci		.of_match_table = of_match_ptr(meson_spicc_of_match),
97162306a36Sopenharmony_ci	},
97262306a36Sopenharmony_ci};
97362306a36Sopenharmony_ci
97462306a36Sopenharmony_cimodule_platform_driver(meson_spicc_driver);
97562306a36Sopenharmony_ci
97662306a36Sopenharmony_ciMODULE_DESCRIPTION("Meson SPI Communication Controller driver");
97762306a36Sopenharmony_ciMODULE_AUTHOR("Neil Armstrong <narmstrong@baylibre.com>");
97862306a36Sopenharmony_ciMODULE_LICENSE("GPL");
979