18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci * SPI-Engine SPI controller driver
48c2ecf20Sopenharmony_ci * Copyright 2015 Analog Devices Inc.
58c2ecf20Sopenharmony_ci *  Author: Lars-Peter Clausen <lars@metafoo.de>
68c2ecf20Sopenharmony_ci */
78c2ecf20Sopenharmony_ci
88c2ecf20Sopenharmony_ci#include <linux/clk.h>
98c2ecf20Sopenharmony_ci#include <linux/interrupt.h>
108c2ecf20Sopenharmony_ci#include <linux/io.h>
118c2ecf20Sopenharmony_ci#include <linux/of.h>
128c2ecf20Sopenharmony_ci#include <linux/module.h>
138c2ecf20Sopenharmony_ci#include <linux/platform_device.h>
148c2ecf20Sopenharmony_ci#include <linux/spi/spi.h>
158c2ecf20Sopenharmony_ci
168c2ecf20Sopenharmony_ci#define SPI_ENGINE_VERSION_MAJOR(x)	((x >> 16) & 0xff)
178c2ecf20Sopenharmony_ci#define SPI_ENGINE_VERSION_MINOR(x)	((x >> 8) & 0xff)
188c2ecf20Sopenharmony_ci#define SPI_ENGINE_VERSION_PATCH(x)	(x & 0xff)
198c2ecf20Sopenharmony_ci
208c2ecf20Sopenharmony_ci#define SPI_ENGINE_REG_VERSION			0x00
218c2ecf20Sopenharmony_ci
228c2ecf20Sopenharmony_ci#define SPI_ENGINE_REG_RESET			0x40
238c2ecf20Sopenharmony_ci
248c2ecf20Sopenharmony_ci#define SPI_ENGINE_REG_INT_ENABLE		0x80
258c2ecf20Sopenharmony_ci#define SPI_ENGINE_REG_INT_PENDING		0x84
268c2ecf20Sopenharmony_ci#define SPI_ENGINE_REG_INT_SOURCE		0x88
278c2ecf20Sopenharmony_ci
288c2ecf20Sopenharmony_ci#define SPI_ENGINE_REG_SYNC_ID			0xc0
298c2ecf20Sopenharmony_ci
308c2ecf20Sopenharmony_ci#define SPI_ENGINE_REG_CMD_FIFO_ROOM		0xd0
318c2ecf20Sopenharmony_ci#define SPI_ENGINE_REG_SDO_FIFO_ROOM		0xd4
328c2ecf20Sopenharmony_ci#define SPI_ENGINE_REG_SDI_FIFO_LEVEL		0xd8
338c2ecf20Sopenharmony_ci
348c2ecf20Sopenharmony_ci#define SPI_ENGINE_REG_CMD_FIFO			0xe0
358c2ecf20Sopenharmony_ci#define SPI_ENGINE_REG_SDO_DATA_FIFO		0xe4
368c2ecf20Sopenharmony_ci#define SPI_ENGINE_REG_SDI_DATA_FIFO		0xe8
378c2ecf20Sopenharmony_ci#define SPI_ENGINE_REG_SDI_DATA_FIFO_PEEK	0xec
388c2ecf20Sopenharmony_ci
398c2ecf20Sopenharmony_ci#define SPI_ENGINE_INT_CMD_ALMOST_EMPTY		BIT(0)
408c2ecf20Sopenharmony_ci#define SPI_ENGINE_INT_SDO_ALMOST_EMPTY		BIT(1)
418c2ecf20Sopenharmony_ci#define SPI_ENGINE_INT_SDI_ALMOST_FULL		BIT(2)
428c2ecf20Sopenharmony_ci#define SPI_ENGINE_INT_SYNC			BIT(3)
438c2ecf20Sopenharmony_ci
448c2ecf20Sopenharmony_ci#define SPI_ENGINE_CONFIG_CPHA			BIT(0)
458c2ecf20Sopenharmony_ci#define SPI_ENGINE_CONFIG_CPOL			BIT(1)
468c2ecf20Sopenharmony_ci#define SPI_ENGINE_CONFIG_3WIRE			BIT(2)
478c2ecf20Sopenharmony_ci
488c2ecf20Sopenharmony_ci#define SPI_ENGINE_INST_TRANSFER		0x0
498c2ecf20Sopenharmony_ci#define SPI_ENGINE_INST_ASSERT			0x1
508c2ecf20Sopenharmony_ci#define SPI_ENGINE_INST_WRITE			0x2
518c2ecf20Sopenharmony_ci#define SPI_ENGINE_INST_MISC			0x3
528c2ecf20Sopenharmony_ci
538c2ecf20Sopenharmony_ci#define SPI_ENGINE_CMD_REG_CLK_DIV		0x0
548c2ecf20Sopenharmony_ci#define SPI_ENGINE_CMD_REG_CONFIG		0x1
558c2ecf20Sopenharmony_ci
568c2ecf20Sopenharmony_ci#define SPI_ENGINE_MISC_SYNC			0x0
578c2ecf20Sopenharmony_ci#define SPI_ENGINE_MISC_SLEEP			0x1
588c2ecf20Sopenharmony_ci
598c2ecf20Sopenharmony_ci#define SPI_ENGINE_TRANSFER_WRITE		0x1
608c2ecf20Sopenharmony_ci#define SPI_ENGINE_TRANSFER_READ		0x2
618c2ecf20Sopenharmony_ci
628c2ecf20Sopenharmony_ci#define SPI_ENGINE_CMD(inst, arg1, arg2) \
638c2ecf20Sopenharmony_ci	(((inst) << 12) | ((arg1) << 8) | (arg2))
648c2ecf20Sopenharmony_ci
658c2ecf20Sopenharmony_ci#define SPI_ENGINE_CMD_TRANSFER(flags, n) \
668c2ecf20Sopenharmony_ci	SPI_ENGINE_CMD(SPI_ENGINE_INST_TRANSFER, (flags), (n))
678c2ecf20Sopenharmony_ci#define SPI_ENGINE_CMD_ASSERT(delay, cs) \
688c2ecf20Sopenharmony_ci	SPI_ENGINE_CMD(SPI_ENGINE_INST_ASSERT, (delay), (cs))
698c2ecf20Sopenharmony_ci#define SPI_ENGINE_CMD_WRITE(reg, val) \
708c2ecf20Sopenharmony_ci	SPI_ENGINE_CMD(SPI_ENGINE_INST_WRITE, (reg), (val))
718c2ecf20Sopenharmony_ci#define SPI_ENGINE_CMD_SLEEP(delay) \
728c2ecf20Sopenharmony_ci	SPI_ENGINE_CMD(SPI_ENGINE_INST_MISC, SPI_ENGINE_MISC_SLEEP, (delay))
738c2ecf20Sopenharmony_ci#define SPI_ENGINE_CMD_SYNC(id) \
748c2ecf20Sopenharmony_ci	SPI_ENGINE_CMD(SPI_ENGINE_INST_MISC, SPI_ENGINE_MISC_SYNC, (id))
758c2ecf20Sopenharmony_ci
768c2ecf20Sopenharmony_cistruct spi_engine_program {
778c2ecf20Sopenharmony_ci	unsigned int length;
788c2ecf20Sopenharmony_ci	uint16_t instructions[];
798c2ecf20Sopenharmony_ci};
808c2ecf20Sopenharmony_ci
818c2ecf20Sopenharmony_cistruct spi_engine {
828c2ecf20Sopenharmony_ci	struct clk *clk;
838c2ecf20Sopenharmony_ci	struct clk *ref_clk;
848c2ecf20Sopenharmony_ci
858c2ecf20Sopenharmony_ci	spinlock_t lock;
868c2ecf20Sopenharmony_ci
878c2ecf20Sopenharmony_ci	void __iomem *base;
888c2ecf20Sopenharmony_ci
898c2ecf20Sopenharmony_ci	struct spi_message *msg;
908c2ecf20Sopenharmony_ci	struct spi_engine_program *p;
918c2ecf20Sopenharmony_ci	unsigned cmd_length;
928c2ecf20Sopenharmony_ci	const uint16_t *cmd_buf;
938c2ecf20Sopenharmony_ci
948c2ecf20Sopenharmony_ci	struct spi_transfer *tx_xfer;
958c2ecf20Sopenharmony_ci	unsigned int tx_length;
968c2ecf20Sopenharmony_ci	const uint8_t *tx_buf;
978c2ecf20Sopenharmony_ci
988c2ecf20Sopenharmony_ci	struct spi_transfer *rx_xfer;
998c2ecf20Sopenharmony_ci	unsigned int rx_length;
1008c2ecf20Sopenharmony_ci	uint8_t *rx_buf;
1018c2ecf20Sopenharmony_ci
1028c2ecf20Sopenharmony_ci	unsigned int sync_id;
1038c2ecf20Sopenharmony_ci	unsigned int completed_id;
1048c2ecf20Sopenharmony_ci
1058c2ecf20Sopenharmony_ci	unsigned int int_enable;
1068c2ecf20Sopenharmony_ci};
1078c2ecf20Sopenharmony_ci
1088c2ecf20Sopenharmony_cistatic void spi_engine_program_add_cmd(struct spi_engine_program *p,
1098c2ecf20Sopenharmony_ci	bool dry, uint16_t cmd)
1108c2ecf20Sopenharmony_ci{
1118c2ecf20Sopenharmony_ci	if (!dry)
1128c2ecf20Sopenharmony_ci		p->instructions[p->length] = cmd;
1138c2ecf20Sopenharmony_ci	p->length++;
1148c2ecf20Sopenharmony_ci}
1158c2ecf20Sopenharmony_ci
1168c2ecf20Sopenharmony_cistatic unsigned int spi_engine_get_config(struct spi_device *spi)
1178c2ecf20Sopenharmony_ci{
1188c2ecf20Sopenharmony_ci	unsigned int config = 0;
1198c2ecf20Sopenharmony_ci
1208c2ecf20Sopenharmony_ci	if (spi->mode & SPI_CPOL)
1218c2ecf20Sopenharmony_ci		config |= SPI_ENGINE_CONFIG_CPOL;
1228c2ecf20Sopenharmony_ci	if (spi->mode & SPI_CPHA)
1238c2ecf20Sopenharmony_ci		config |= SPI_ENGINE_CONFIG_CPHA;
1248c2ecf20Sopenharmony_ci	if (spi->mode & SPI_3WIRE)
1258c2ecf20Sopenharmony_ci		config |= SPI_ENGINE_CONFIG_3WIRE;
1268c2ecf20Sopenharmony_ci
1278c2ecf20Sopenharmony_ci	return config;
1288c2ecf20Sopenharmony_ci}
1298c2ecf20Sopenharmony_ci
1308c2ecf20Sopenharmony_cistatic unsigned int spi_engine_get_clk_div(struct spi_engine *spi_engine,
1318c2ecf20Sopenharmony_ci	struct spi_device *spi, struct spi_transfer *xfer)
1328c2ecf20Sopenharmony_ci{
1338c2ecf20Sopenharmony_ci	unsigned int clk_div;
1348c2ecf20Sopenharmony_ci
1358c2ecf20Sopenharmony_ci	clk_div = DIV_ROUND_UP(clk_get_rate(spi_engine->ref_clk),
1368c2ecf20Sopenharmony_ci		xfer->speed_hz * 2);
1378c2ecf20Sopenharmony_ci	if (clk_div > 255)
1388c2ecf20Sopenharmony_ci		clk_div = 255;
1398c2ecf20Sopenharmony_ci	else if (clk_div > 0)
1408c2ecf20Sopenharmony_ci		clk_div -= 1;
1418c2ecf20Sopenharmony_ci
1428c2ecf20Sopenharmony_ci	return clk_div;
1438c2ecf20Sopenharmony_ci}
1448c2ecf20Sopenharmony_ci
1458c2ecf20Sopenharmony_cistatic void spi_engine_gen_xfer(struct spi_engine_program *p, bool dry,
1468c2ecf20Sopenharmony_ci	struct spi_transfer *xfer)
1478c2ecf20Sopenharmony_ci{
1488c2ecf20Sopenharmony_ci	unsigned int len = xfer->len;
1498c2ecf20Sopenharmony_ci
1508c2ecf20Sopenharmony_ci	while (len) {
1518c2ecf20Sopenharmony_ci		unsigned int n = min(len, 256U);
1528c2ecf20Sopenharmony_ci		unsigned int flags = 0;
1538c2ecf20Sopenharmony_ci
1548c2ecf20Sopenharmony_ci		if (xfer->tx_buf)
1558c2ecf20Sopenharmony_ci			flags |= SPI_ENGINE_TRANSFER_WRITE;
1568c2ecf20Sopenharmony_ci		if (xfer->rx_buf)
1578c2ecf20Sopenharmony_ci			flags |= SPI_ENGINE_TRANSFER_READ;
1588c2ecf20Sopenharmony_ci
1598c2ecf20Sopenharmony_ci		spi_engine_program_add_cmd(p, dry,
1608c2ecf20Sopenharmony_ci			SPI_ENGINE_CMD_TRANSFER(flags, n - 1));
1618c2ecf20Sopenharmony_ci		len -= n;
1628c2ecf20Sopenharmony_ci	}
1638c2ecf20Sopenharmony_ci}
1648c2ecf20Sopenharmony_ci
1658c2ecf20Sopenharmony_cistatic void spi_engine_gen_sleep(struct spi_engine_program *p, bool dry,
1668c2ecf20Sopenharmony_ci	struct spi_engine *spi_engine, unsigned int clk_div,
1678c2ecf20Sopenharmony_ci	struct spi_transfer *xfer)
1688c2ecf20Sopenharmony_ci{
1698c2ecf20Sopenharmony_ci	unsigned int spi_clk = clk_get_rate(spi_engine->ref_clk);
1708c2ecf20Sopenharmony_ci	unsigned int t;
1718c2ecf20Sopenharmony_ci	int delay;
1728c2ecf20Sopenharmony_ci
1738c2ecf20Sopenharmony_ci	if (xfer->delay_usecs) {
1748c2ecf20Sopenharmony_ci		delay = xfer->delay_usecs;
1758c2ecf20Sopenharmony_ci	} else {
1768c2ecf20Sopenharmony_ci		delay = spi_delay_to_ns(&xfer->delay, xfer);
1778c2ecf20Sopenharmony_ci		if (delay < 0)
1788c2ecf20Sopenharmony_ci			return;
1798c2ecf20Sopenharmony_ci		delay /= 1000;
1808c2ecf20Sopenharmony_ci	}
1818c2ecf20Sopenharmony_ci
1828c2ecf20Sopenharmony_ci	if (delay == 0)
1838c2ecf20Sopenharmony_ci		return;
1848c2ecf20Sopenharmony_ci
1858c2ecf20Sopenharmony_ci	t = DIV_ROUND_UP(delay * spi_clk, (clk_div + 1) * 2);
1868c2ecf20Sopenharmony_ci	while (t) {
1878c2ecf20Sopenharmony_ci		unsigned int n = min(t, 256U);
1888c2ecf20Sopenharmony_ci
1898c2ecf20Sopenharmony_ci		spi_engine_program_add_cmd(p, dry, SPI_ENGINE_CMD_SLEEP(n - 1));
1908c2ecf20Sopenharmony_ci		t -= n;
1918c2ecf20Sopenharmony_ci	}
1928c2ecf20Sopenharmony_ci}
1938c2ecf20Sopenharmony_ci
1948c2ecf20Sopenharmony_cistatic void spi_engine_gen_cs(struct spi_engine_program *p, bool dry,
1958c2ecf20Sopenharmony_ci		struct spi_device *spi, bool assert)
1968c2ecf20Sopenharmony_ci{
1978c2ecf20Sopenharmony_ci	unsigned int mask = 0xff;
1988c2ecf20Sopenharmony_ci
1998c2ecf20Sopenharmony_ci	if (assert)
2008c2ecf20Sopenharmony_ci		mask ^= BIT(spi->chip_select);
2018c2ecf20Sopenharmony_ci
2028c2ecf20Sopenharmony_ci	spi_engine_program_add_cmd(p, dry, SPI_ENGINE_CMD_ASSERT(1, mask));
2038c2ecf20Sopenharmony_ci}
2048c2ecf20Sopenharmony_ci
2058c2ecf20Sopenharmony_cistatic int spi_engine_compile_message(struct spi_engine *spi_engine,
2068c2ecf20Sopenharmony_ci	struct spi_message *msg, bool dry, struct spi_engine_program *p)
2078c2ecf20Sopenharmony_ci{
2088c2ecf20Sopenharmony_ci	struct spi_device *spi = msg->spi;
2098c2ecf20Sopenharmony_ci	struct spi_transfer *xfer;
2108c2ecf20Sopenharmony_ci	int clk_div, new_clk_div;
2118c2ecf20Sopenharmony_ci	bool cs_change = true;
2128c2ecf20Sopenharmony_ci
2138c2ecf20Sopenharmony_ci	clk_div = -1;
2148c2ecf20Sopenharmony_ci
2158c2ecf20Sopenharmony_ci	spi_engine_program_add_cmd(p, dry,
2168c2ecf20Sopenharmony_ci		SPI_ENGINE_CMD_WRITE(SPI_ENGINE_CMD_REG_CONFIG,
2178c2ecf20Sopenharmony_ci			spi_engine_get_config(spi)));
2188c2ecf20Sopenharmony_ci
2198c2ecf20Sopenharmony_ci	list_for_each_entry(xfer, &msg->transfers, transfer_list) {
2208c2ecf20Sopenharmony_ci		new_clk_div = spi_engine_get_clk_div(spi_engine, spi, xfer);
2218c2ecf20Sopenharmony_ci		if (new_clk_div != clk_div) {
2228c2ecf20Sopenharmony_ci			clk_div = new_clk_div;
2238c2ecf20Sopenharmony_ci			spi_engine_program_add_cmd(p, dry,
2248c2ecf20Sopenharmony_ci				SPI_ENGINE_CMD_WRITE(SPI_ENGINE_CMD_REG_CLK_DIV,
2258c2ecf20Sopenharmony_ci					clk_div));
2268c2ecf20Sopenharmony_ci		}
2278c2ecf20Sopenharmony_ci
2288c2ecf20Sopenharmony_ci		if (cs_change)
2298c2ecf20Sopenharmony_ci			spi_engine_gen_cs(p, dry, spi, true);
2308c2ecf20Sopenharmony_ci
2318c2ecf20Sopenharmony_ci		spi_engine_gen_xfer(p, dry, xfer);
2328c2ecf20Sopenharmony_ci		spi_engine_gen_sleep(p, dry, spi_engine, clk_div, xfer);
2338c2ecf20Sopenharmony_ci
2348c2ecf20Sopenharmony_ci		cs_change = xfer->cs_change;
2358c2ecf20Sopenharmony_ci		if (list_is_last(&xfer->transfer_list, &msg->transfers))
2368c2ecf20Sopenharmony_ci			cs_change = !cs_change;
2378c2ecf20Sopenharmony_ci
2388c2ecf20Sopenharmony_ci		if (cs_change)
2398c2ecf20Sopenharmony_ci			spi_engine_gen_cs(p, dry, spi, false);
2408c2ecf20Sopenharmony_ci	}
2418c2ecf20Sopenharmony_ci
2428c2ecf20Sopenharmony_ci	return 0;
2438c2ecf20Sopenharmony_ci}
2448c2ecf20Sopenharmony_ci
2458c2ecf20Sopenharmony_cistatic void spi_engine_xfer_next(struct spi_engine *spi_engine,
2468c2ecf20Sopenharmony_ci	struct spi_transfer **_xfer)
2478c2ecf20Sopenharmony_ci{
2488c2ecf20Sopenharmony_ci	struct spi_message *msg = spi_engine->msg;
2498c2ecf20Sopenharmony_ci	struct spi_transfer *xfer = *_xfer;
2508c2ecf20Sopenharmony_ci
2518c2ecf20Sopenharmony_ci	if (!xfer) {
2528c2ecf20Sopenharmony_ci		xfer = list_first_entry(&msg->transfers,
2538c2ecf20Sopenharmony_ci			struct spi_transfer, transfer_list);
2548c2ecf20Sopenharmony_ci	} else if (list_is_last(&xfer->transfer_list, &msg->transfers)) {
2558c2ecf20Sopenharmony_ci		xfer = NULL;
2568c2ecf20Sopenharmony_ci	} else {
2578c2ecf20Sopenharmony_ci		xfer = list_next_entry(xfer, transfer_list);
2588c2ecf20Sopenharmony_ci	}
2598c2ecf20Sopenharmony_ci
2608c2ecf20Sopenharmony_ci	*_xfer = xfer;
2618c2ecf20Sopenharmony_ci}
2628c2ecf20Sopenharmony_ci
2638c2ecf20Sopenharmony_cistatic void spi_engine_tx_next(struct spi_engine *spi_engine)
2648c2ecf20Sopenharmony_ci{
2658c2ecf20Sopenharmony_ci	struct spi_transfer *xfer = spi_engine->tx_xfer;
2668c2ecf20Sopenharmony_ci
2678c2ecf20Sopenharmony_ci	do {
2688c2ecf20Sopenharmony_ci		spi_engine_xfer_next(spi_engine, &xfer);
2698c2ecf20Sopenharmony_ci	} while (xfer && !xfer->tx_buf);
2708c2ecf20Sopenharmony_ci
2718c2ecf20Sopenharmony_ci	spi_engine->tx_xfer = xfer;
2728c2ecf20Sopenharmony_ci	if (xfer) {
2738c2ecf20Sopenharmony_ci		spi_engine->tx_length = xfer->len;
2748c2ecf20Sopenharmony_ci		spi_engine->tx_buf = xfer->tx_buf;
2758c2ecf20Sopenharmony_ci	} else {
2768c2ecf20Sopenharmony_ci		spi_engine->tx_buf = NULL;
2778c2ecf20Sopenharmony_ci	}
2788c2ecf20Sopenharmony_ci}
2798c2ecf20Sopenharmony_ci
2808c2ecf20Sopenharmony_cistatic void spi_engine_rx_next(struct spi_engine *spi_engine)
2818c2ecf20Sopenharmony_ci{
2828c2ecf20Sopenharmony_ci	struct spi_transfer *xfer = spi_engine->rx_xfer;
2838c2ecf20Sopenharmony_ci
2848c2ecf20Sopenharmony_ci	do {
2858c2ecf20Sopenharmony_ci		spi_engine_xfer_next(spi_engine, &xfer);
2868c2ecf20Sopenharmony_ci	} while (xfer && !xfer->rx_buf);
2878c2ecf20Sopenharmony_ci
2888c2ecf20Sopenharmony_ci	spi_engine->rx_xfer = xfer;
2898c2ecf20Sopenharmony_ci	if (xfer) {
2908c2ecf20Sopenharmony_ci		spi_engine->rx_length = xfer->len;
2918c2ecf20Sopenharmony_ci		spi_engine->rx_buf = xfer->rx_buf;
2928c2ecf20Sopenharmony_ci	} else {
2938c2ecf20Sopenharmony_ci		spi_engine->rx_buf = NULL;
2948c2ecf20Sopenharmony_ci	}
2958c2ecf20Sopenharmony_ci}
2968c2ecf20Sopenharmony_ci
2978c2ecf20Sopenharmony_cistatic bool spi_engine_write_cmd_fifo(struct spi_engine *spi_engine)
2988c2ecf20Sopenharmony_ci{
2998c2ecf20Sopenharmony_ci	void __iomem *addr = spi_engine->base + SPI_ENGINE_REG_CMD_FIFO;
3008c2ecf20Sopenharmony_ci	unsigned int n, m, i;
3018c2ecf20Sopenharmony_ci	const uint16_t *buf;
3028c2ecf20Sopenharmony_ci
3038c2ecf20Sopenharmony_ci	n = readl_relaxed(spi_engine->base + SPI_ENGINE_REG_CMD_FIFO_ROOM);
3048c2ecf20Sopenharmony_ci	while (n && spi_engine->cmd_length) {
3058c2ecf20Sopenharmony_ci		m = min(n, spi_engine->cmd_length);
3068c2ecf20Sopenharmony_ci		buf = spi_engine->cmd_buf;
3078c2ecf20Sopenharmony_ci		for (i = 0; i < m; i++)
3088c2ecf20Sopenharmony_ci			writel_relaxed(buf[i], addr);
3098c2ecf20Sopenharmony_ci		spi_engine->cmd_buf += m;
3108c2ecf20Sopenharmony_ci		spi_engine->cmd_length -= m;
3118c2ecf20Sopenharmony_ci		n -= m;
3128c2ecf20Sopenharmony_ci	}
3138c2ecf20Sopenharmony_ci
3148c2ecf20Sopenharmony_ci	return spi_engine->cmd_length != 0;
3158c2ecf20Sopenharmony_ci}
3168c2ecf20Sopenharmony_ci
3178c2ecf20Sopenharmony_cistatic bool spi_engine_write_tx_fifo(struct spi_engine *spi_engine)
3188c2ecf20Sopenharmony_ci{
3198c2ecf20Sopenharmony_ci	void __iomem *addr = spi_engine->base + SPI_ENGINE_REG_SDO_DATA_FIFO;
3208c2ecf20Sopenharmony_ci	unsigned int n, m, i;
3218c2ecf20Sopenharmony_ci	const uint8_t *buf;
3228c2ecf20Sopenharmony_ci
3238c2ecf20Sopenharmony_ci	n = readl_relaxed(spi_engine->base + SPI_ENGINE_REG_SDO_FIFO_ROOM);
3248c2ecf20Sopenharmony_ci	while (n && spi_engine->tx_length) {
3258c2ecf20Sopenharmony_ci		m = min(n, spi_engine->tx_length);
3268c2ecf20Sopenharmony_ci		buf = spi_engine->tx_buf;
3278c2ecf20Sopenharmony_ci		for (i = 0; i < m; i++)
3288c2ecf20Sopenharmony_ci			writel_relaxed(buf[i], addr);
3298c2ecf20Sopenharmony_ci		spi_engine->tx_buf += m;
3308c2ecf20Sopenharmony_ci		spi_engine->tx_length -= m;
3318c2ecf20Sopenharmony_ci		n -= m;
3328c2ecf20Sopenharmony_ci		if (spi_engine->tx_length == 0)
3338c2ecf20Sopenharmony_ci			spi_engine_tx_next(spi_engine);
3348c2ecf20Sopenharmony_ci	}
3358c2ecf20Sopenharmony_ci
3368c2ecf20Sopenharmony_ci	return spi_engine->tx_length != 0;
3378c2ecf20Sopenharmony_ci}
3388c2ecf20Sopenharmony_ci
3398c2ecf20Sopenharmony_cistatic bool spi_engine_read_rx_fifo(struct spi_engine *spi_engine)
3408c2ecf20Sopenharmony_ci{
3418c2ecf20Sopenharmony_ci	void __iomem *addr = spi_engine->base + SPI_ENGINE_REG_SDI_DATA_FIFO;
3428c2ecf20Sopenharmony_ci	unsigned int n, m, i;
3438c2ecf20Sopenharmony_ci	uint8_t *buf;
3448c2ecf20Sopenharmony_ci
3458c2ecf20Sopenharmony_ci	n = readl_relaxed(spi_engine->base + SPI_ENGINE_REG_SDI_FIFO_LEVEL);
3468c2ecf20Sopenharmony_ci	while (n && spi_engine->rx_length) {
3478c2ecf20Sopenharmony_ci		m = min(n, spi_engine->rx_length);
3488c2ecf20Sopenharmony_ci		buf = spi_engine->rx_buf;
3498c2ecf20Sopenharmony_ci		for (i = 0; i < m; i++)
3508c2ecf20Sopenharmony_ci			buf[i] = readl_relaxed(addr);
3518c2ecf20Sopenharmony_ci		spi_engine->rx_buf += m;
3528c2ecf20Sopenharmony_ci		spi_engine->rx_length -= m;
3538c2ecf20Sopenharmony_ci		n -= m;
3548c2ecf20Sopenharmony_ci		if (spi_engine->rx_length == 0)
3558c2ecf20Sopenharmony_ci			spi_engine_rx_next(spi_engine);
3568c2ecf20Sopenharmony_ci	}
3578c2ecf20Sopenharmony_ci
3588c2ecf20Sopenharmony_ci	return spi_engine->rx_length != 0;
3598c2ecf20Sopenharmony_ci}
3608c2ecf20Sopenharmony_ci
3618c2ecf20Sopenharmony_cistatic irqreturn_t spi_engine_irq(int irq, void *devid)
3628c2ecf20Sopenharmony_ci{
3638c2ecf20Sopenharmony_ci	struct spi_master *master = devid;
3648c2ecf20Sopenharmony_ci	struct spi_engine *spi_engine = spi_master_get_devdata(master);
3658c2ecf20Sopenharmony_ci	unsigned int disable_int = 0;
3668c2ecf20Sopenharmony_ci	unsigned int pending;
3678c2ecf20Sopenharmony_ci
3688c2ecf20Sopenharmony_ci	pending = readl_relaxed(spi_engine->base + SPI_ENGINE_REG_INT_PENDING);
3698c2ecf20Sopenharmony_ci
3708c2ecf20Sopenharmony_ci	if (pending & SPI_ENGINE_INT_SYNC) {
3718c2ecf20Sopenharmony_ci		writel_relaxed(SPI_ENGINE_INT_SYNC,
3728c2ecf20Sopenharmony_ci			spi_engine->base + SPI_ENGINE_REG_INT_PENDING);
3738c2ecf20Sopenharmony_ci		spi_engine->completed_id = readl_relaxed(
3748c2ecf20Sopenharmony_ci			spi_engine->base + SPI_ENGINE_REG_SYNC_ID);
3758c2ecf20Sopenharmony_ci	}
3768c2ecf20Sopenharmony_ci
3778c2ecf20Sopenharmony_ci	spin_lock(&spi_engine->lock);
3788c2ecf20Sopenharmony_ci
3798c2ecf20Sopenharmony_ci	if (pending & SPI_ENGINE_INT_CMD_ALMOST_EMPTY) {
3808c2ecf20Sopenharmony_ci		if (!spi_engine_write_cmd_fifo(spi_engine))
3818c2ecf20Sopenharmony_ci			disable_int |= SPI_ENGINE_INT_CMD_ALMOST_EMPTY;
3828c2ecf20Sopenharmony_ci	}
3838c2ecf20Sopenharmony_ci
3848c2ecf20Sopenharmony_ci	if (pending & SPI_ENGINE_INT_SDO_ALMOST_EMPTY) {
3858c2ecf20Sopenharmony_ci		if (!spi_engine_write_tx_fifo(spi_engine))
3868c2ecf20Sopenharmony_ci			disable_int |= SPI_ENGINE_INT_SDO_ALMOST_EMPTY;
3878c2ecf20Sopenharmony_ci	}
3888c2ecf20Sopenharmony_ci
3898c2ecf20Sopenharmony_ci	if (pending & (SPI_ENGINE_INT_SDI_ALMOST_FULL | SPI_ENGINE_INT_SYNC)) {
3908c2ecf20Sopenharmony_ci		if (!spi_engine_read_rx_fifo(spi_engine))
3918c2ecf20Sopenharmony_ci			disable_int |= SPI_ENGINE_INT_SDI_ALMOST_FULL;
3928c2ecf20Sopenharmony_ci	}
3938c2ecf20Sopenharmony_ci
3948c2ecf20Sopenharmony_ci	if (pending & SPI_ENGINE_INT_SYNC) {
3958c2ecf20Sopenharmony_ci		if (spi_engine->msg &&
3968c2ecf20Sopenharmony_ci		    spi_engine->completed_id == spi_engine->sync_id) {
3978c2ecf20Sopenharmony_ci			struct spi_message *msg = spi_engine->msg;
3988c2ecf20Sopenharmony_ci
3998c2ecf20Sopenharmony_ci			kfree(spi_engine->p);
4008c2ecf20Sopenharmony_ci			msg->status = 0;
4018c2ecf20Sopenharmony_ci			msg->actual_length = msg->frame_length;
4028c2ecf20Sopenharmony_ci			spi_engine->msg = NULL;
4038c2ecf20Sopenharmony_ci			spi_finalize_current_message(master);
4048c2ecf20Sopenharmony_ci			disable_int |= SPI_ENGINE_INT_SYNC;
4058c2ecf20Sopenharmony_ci		}
4068c2ecf20Sopenharmony_ci	}
4078c2ecf20Sopenharmony_ci
4088c2ecf20Sopenharmony_ci	if (disable_int) {
4098c2ecf20Sopenharmony_ci		spi_engine->int_enable &= ~disable_int;
4108c2ecf20Sopenharmony_ci		writel_relaxed(spi_engine->int_enable,
4118c2ecf20Sopenharmony_ci			spi_engine->base + SPI_ENGINE_REG_INT_ENABLE);
4128c2ecf20Sopenharmony_ci	}
4138c2ecf20Sopenharmony_ci
4148c2ecf20Sopenharmony_ci	spin_unlock(&spi_engine->lock);
4158c2ecf20Sopenharmony_ci
4168c2ecf20Sopenharmony_ci	return IRQ_HANDLED;
4178c2ecf20Sopenharmony_ci}
4188c2ecf20Sopenharmony_ci
4198c2ecf20Sopenharmony_cistatic int spi_engine_transfer_one_message(struct spi_master *master,
4208c2ecf20Sopenharmony_ci	struct spi_message *msg)
4218c2ecf20Sopenharmony_ci{
4228c2ecf20Sopenharmony_ci	struct spi_engine_program p_dry, *p;
4238c2ecf20Sopenharmony_ci	struct spi_engine *spi_engine = spi_master_get_devdata(master);
4248c2ecf20Sopenharmony_ci	unsigned int int_enable = 0;
4258c2ecf20Sopenharmony_ci	unsigned long flags;
4268c2ecf20Sopenharmony_ci	size_t size;
4278c2ecf20Sopenharmony_ci
4288c2ecf20Sopenharmony_ci	p_dry.length = 0;
4298c2ecf20Sopenharmony_ci	spi_engine_compile_message(spi_engine, msg, true, &p_dry);
4308c2ecf20Sopenharmony_ci
4318c2ecf20Sopenharmony_ci	size = sizeof(*p->instructions) * (p_dry.length + 1);
4328c2ecf20Sopenharmony_ci	p = kzalloc(sizeof(*p) + size, GFP_KERNEL);
4338c2ecf20Sopenharmony_ci	if (!p)
4348c2ecf20Sopenharmony_ci		return -ENOMEM;
4358c2ecf20Sopenharmony_ci	spi_engine_compile_message(spi_engine, msg, false, p);
4368c2ecf20Sopenharmony_ci
4378c2ecf20Sopenharmony_ci	spin_lock_irqsave(&spi_engine->lock, flags);
4388c2ecf20Sopenharmony_ci	spi_engine->sync_id = (spi_engine->sync_id + 1) & 0xff;
4398c2ecf20Sopenharmony_ci	spi_engine_program_add_cmd(p, false,
4408c2ecf20Sopenharmony_ci		SPI_ENGINE_CMD_SYNC(spi_engine->sync_id));
4418c2ecf20Sopenharmony_ci
4428c2ecf20Sopenharmony_ci	spi_engine->msg = msg;
4438c2ecf20Sopenharmony_ci	spi_engine->p = p;
4448c2ecf20Sopenharmony_ci
4458c2ecf20Sopenharmony_ci	spi_engine->cmd_buf = p->instructions;
4468c2ecf20Sopenharmony_ci	spi_engine->cmd_length = p->length;
4478c2ecf20Sopenharmony_ci	if (spi_engine_write_cmd_fifo(spi_engine))
4488c2ecf20Sopenharmony_ci		int_enable |= SPI_ENGINE_INT_CMD_ALMOST_EMPTY;
4498c2ecf20Sopenharmony_ci
4508c2ecf20Sopenharmony_ci	spi_engine_tx_next(spi_engine);
4518c2ecf20Sopenharmony_ci	if (spi_engine_write_tx_fifo(spi_engine))
4528c2ecf20Sopenharmony_ci		int_enable |= SPI_ENGINE_INT_SDO_ALMOST_EMPTY;
4538c2ecf20Sopenharmony_ci
4548c2ecf20Sopenharmony_ci	spi_engine_rx_next(spi_engine);
4558c2ecf20Sopenharmony_ci	if (spi_engine->rx_length != 0)
4568c2ecf20Sopenharmony_ci		int_enable |= SPI_ENGINE_INT_SDI_ALMOST_FULL;
4578c2ecf20Sopenharmony_ci
4588c2ecf20Sopenharmony_ci	int_enable |= SPI_ENGINE_INT_SYNC;
4598c2ecf20Sopenharmony_ci
4608c2ecf20Sopenharmony_ci	writel_relaxed(int_enable,
4618c2ecf20Sopenharmony_ci		spi_engine->base + SPI_ENGINE_REG_INT_ENABLE);
4628c2ecf20Sopenharmony_ci	spi_engine->int_enable = int_enable;
4638c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&spi_engine->lock, flags);
4648c2ecf20Sopenharmony_ci
4658c2ecf20Sopenharmony_ci	return 0;
4668c2ecf20Sopenharmony_ci}
4678c2ecf20Sopenharmony_ci
4688c2ecf20Sopenharmony_cistatic int spi_engine_probe(struct platform_device *pdev)
4698c2ecf20Sopenharmony_ci{
4708c2ecf20Sopenharmony_ci	struct spi_engine *spi_engine;
4718c2ecf20Sopenharmony_ci	struct spi_master *master;
4728c2ecf20Sopenharmony_ci	unsigned int version;
4738c2ecf20Sopenharmony_ci	int irq;
4748c2ecf20Sopenharmony_ci	int ret;
4758c2ecf20Sopenharmony_ci
4768c2ecf20Sopenharmony_ci	irq = platform_get_irq(pdev, 0);
4778c2ecf20Sopenharmony_ci	if (irq <= 0)
4788c2ecf20Sopenharmony_ci		return -ENXIO;
4798c2ecf20Sopenharmony_ci
4808c2ecf20Sopenharmony_ci	spi_engine = devm_kzalloc(&pdev->dev, sizeof(*spi_engine), GFP_KERNEL);
4818c2ecf20Sopenharmony_ci	if (!spi_engine)
4828c2ecf20Sopenharmony_ci		return -ENOMEM;
4838c2ecf20Sopenharmony_ci
4848c2ecf20Sopenharmony_ci	master = spi_alloc_master(&pdev->dev, 0);
4858c2ecf20Sopenharmony_ci	if (!master)
4868c2ecf20Sopenharmony_ci		return -ENOMEM;
4878c2ecf20Sopenharmony_ci
4888c2ecf20Sopenharmony_ci	spi_master_set_devdata(master, spi_engine);
4898c2ecf20Sopenharmony_ci
4908c2ecf20Sopenharmony_ci	spin_lock_init(&spi_engine->lock);
4918c2ecf20Sopenharmony_ci
4928c2ecf20Sopenharmony_ci	spi_engine->clk = devm_clk_get(&pdev->dev, "s_axi_aclk");
4938c2ecf20Sopenharmony_ci	if (IS_ERR(spi_engine->clk)) {
4948c2ecf20Sopenharmony_ci		ret = PTR_ERR(spi_engine->clk);
4958c2ecf20Sopenharmony_ci		goto err_put_master;
4968c2ecf20Sopenharmony_ci	}
4978c2ecf20Sopenharmony_ci
4988c2ecf20Sopenharmony_ci	spi_engine->ref_clk = devm_clk_get(&pdev->dev, "spi_clk");
4998c2ecf20Sopenharmony_ci	if (IS_ERR(spi_engine->ref_clk)) {
5008c2ecf20Sopenharmony_ci		ret = PTR_ERR(spi_engine->ref_clk);
5018c2ecf20Sopenharmony_ci		goto err_put_master;
5028c2ecf20Sopenharmony_ci	}
5038c2ecf20Sopenharmony_ci
5048c2ecf20Sopenharmony_ci	ret = clk_prepare_enable(spi_engine->clk);
5058c2ecf20Sopenharmony_ci	if (ret)
5068c2ecf20Sopenharmony_ci		goto err_put_master;
5078c2ecf20Sopenharmony_ci
5088c2ecf20Sopenharmony_ci	ret = clk_prepare_enable(spi_engine->ref_clk);
5098c2ecf20Sopenharmony_ci	if (ret)
5108c2ecf20Sopenharmony_ci		goto err_clk_disable;
5118c2ecf20Sopenharmony_ci
5128c2ecf20Sopenharmony_ci	spi_engine->base = devm_platform_ioremap_resource(pdev, 0);
5138c2ecf20Sopenharmony_ci	if (IS_ERR(spi_engine->base)) {
5148c2ecf20Sopenharmony_ci		ret = PTR_ERR(spi_engine->base);
5158c2ecf20Sopenharmony_ci		goto err_ref_clk_disable;
5168c2ecf20Sopenharmony_ci	}
5178c2ecf20Sopenharmony_ci
5188c2ecf20Sopenharmony_ci	version = readl(spi_engine->base + SPI_ENGINE_REG_VERSION);
5198c2ecf20Sopenharmony_ci	if (SPI_ENGINE_VERSION_MAJOR(version) != 1) {
5208c2ecf20Sopenharmony_ci		dev_err(&pdev->dev, "Unsupported peripheral version %u.%u.%c\n",
5218c2ecf20Sopenharmony_ci			SPI_ENGINE_VERSION_MAJOR(version),
5228c2ecf20Sopenharmony_ci			SPI_ENGINE_VERSION_MINOR(version),
5238c2ecf20Sopenharmony_ci			SPI_ENGINE_VERSION_PATCH(version));
5248c2ecf20Sopenharmony_ci		ret = -ENODEV;
5258c2ecf20Sopenharmony_ci		goto err_ref_clk_disable;
5268c2ecf20Sopenharmony_ci	}
5278c2ecf20Sopenharmony_ci
5288c2ecf20Sopenharmony_ci	writel_relaxed(0x00, spi_engine->base + SPI_ENGINE_REG_RESET);
5298c2ecf20Sopenharmony_ci	writel_relaxed(0xff, spi_engine->base + SPI_ENGINE_REG_INT_PENDING);
5308c2ecf20Sopenharmony_ci	writel_relaxed(0x00, spi_engine->base + SPI_ENGINE_REG_INT_ENABLE);
5318c2ecf20Sopenharmony_ci
5328c2ecf20Sopenharmony_ci	ret = request_irq(irq, spi_engine_irq, 0, pdev->name, master);
5338c2ecf20Sopenharmony_ci	if (ret)
5348c2ecf20Sopenharmony_ci		goto err_ref_clk_disable;
5358c2ecf20Sopenharmony_ci
5368c2ecf20Sopenharmony_ci	master->dev.of_node = pdev->dev.of_node;
5378c2ecf20Sopenharmony_ci	master->mode_bits = SPI_CPOL | SPI_CPHA | SPI_3WIRE;
5388c2ecf20Sopenharmony_ci	master->bits_per_word_mask = SPI_BPW_MASK(8);
5398c2ecf20Sopenharmony_ci	master->max_speed_hz = clk_get_rate(spi_engine->ref_clk) / 2;
5408c2ecf20Sopenharmony_ci	master->transfer_one_message = spi_engine_transfer_one_message;
5418c2ecf20Sopenharmony_ci	master->num_chipselect = 8;
5428c2ecf20Sopenharmony_ci
5438c2ecf20Sopenharmony_ci	ret = spi_register_master(master);
5448c2ecf20Sopenharmony_ci	if (ret)
5458c2ecf20Sopenharmony_ci		goto err_free_irq;
5468c2ecf20Sopenharmony_ci
5478c2ecf20Sopenharmony_ci	platform_set_drvdata(pdev, master);
5488c2ecf20Sopenharmony_ci
5498c2ecf20Sopenharmony_ci	return 0;
5508c2ecf20Sopenharmony_cierr_free_irq:
5518c2ecf20Sopenharmony_ci	free_irq(irq, master);
5528c2ecf20Sopenharmony_cierr_ref_clk_disable:
5538c2ecf20Sopenharmony_ci	clk_disable_unprepare(spi_engine->ref_clk);
5548c2ecf20Sopenharmony_cierr_clk_disable:
5558c2ecf20Sopenharmony_ci	clk_disable_unprepare(spi_engine->clk);
5568c2ecf20Sopenharmony_cierr_put_master:
5578c2ecf20Sopenharmony_ci	spi_master_put(master);
5588c2ecf20Sopenharmony_ci	return ret;
5598c2ecf20Sopenharmony_ci}
5608c2ecf20Sopenharmony_ci
5618c2ecf20Sopenharmony_cistatic int spi_engine_remove(struct platform_device *pdev)
5628c2ecf20Sopenharmony_ci{
5638c2ecf20Sopenharmony_ci	struct spi_master *master = spi_master_get(platform_get_drvdata(pdev));
5648c2ecf20Sopenharmony_ci	struct spi_engine *spi_engine = spi_master_get_devdata(master);
5658c2ecf20Sopenharmony_ci	int irq = platform_get_irq(pdev, 0);
5668c2ecf20Sopenharmony_ci
5678c2ecf20Sopenharmony_ci	spi_unregister_master(master);
5688c2ecf20Sopenharmony_ci
5698c2ecf20Sopenharmony_ci	free_irq(irq, master);
5708c2ecf20Sopenharmony_ci
5718c2ecf20Sopenharmony_ci	spi_master_put(master);
5728c2ecf20Sopenharmony_ci
5738c2ecf20Sopenharmony_ci	writel_relaxed(0xff, spi_engine->base + SPI_ENGINE_REG_INT_PENDING);
5748c2ecf20Sopenharmony_ci	writel_relaxed(0x00, spi_engine->base + SPI_ENGINE_REG_INT_ENABLE);
5758c2ecf20Sopenharmony_ci	writel_relaxed(0x01, spi_engine->base + SPI_ENGINE_REG_RESET);
5768c2ecf20Sopenharmony_ci
5778c2ecf20Sopenharmony_ci	clk_disable_unprepare(spi_engine->ref_clk);
5788c2ecf20Sopenharmony_ci	clk_disable_unprepare(spi_engine->clk);
5798c2ecf20Sopenharmony_ci
5808c2ecf20Sopenharmony_ci	return 0;
5818c2ecf20Sopenharmony_ci}
5828c2ecf20Sopenharmony_ci
5838c2ecf20Sopenharmony_cistatic const struct of_device_id spi_engine_match_table[] = {
5848c2ecf20Sopenharmony_ci	{ .compatible = "adi,axi-spi-engine-1.00.a" },
5858c2ecf20Sopenharmony_ci	{ },
5868c2ecf20Sopenharmony_ci};
5878c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(of, spi_engine_match_table);
5888c2ecf20Sopenharmony_ci
5898c2ecf20Sopenharmony_cistatic struct platform_driver spi_engine_driver = {
5908c2ecf20Sopenharmony_ci	.probe = spi_engine_probe,
5918c2ecf20Sopenharmony_ci	.remove = spi_engine_remove,
5928c2ecf20Sopenharmony_ci	.driver = {
5938c2ecf20Sopenharmony_ci		.name = "spi-engine",
5948c2ecf20Sopenharmony_ci		.of_match_table = spi_engine_match_table,
5958c2ecf20Sopenharmony_ci	},
5968c2ecf20Sopenharmony_ci};
5978c2ecf20Sopenharmony_cimodule_platform_driver(spi_engine_driver);
5988c2ecf20Sopenharmony_ci
5998c2ecf20Sopenharmony_ciMODULE_AUTHOR("Lars-Peter Clausen <lars@metafoo.de>");
6008c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("Analog Devices SPI engine peripheral driver");
6018c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL");
602