18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0+
28c2ecf20Sopenharmony_ci//
38c2ecf20Sopenharmony_ci// Driver for Amlogic Meson SPI flash controller (SPIFC)
48c2ecf20Sopenharmony_ci//
58c2ecf20Sopenharmony_ci// Copyright (C) 2014 Beniamino Galvani <b.galvani@gmail.com>
68c2ecf20Sopenharmony_ci//
78c2ecf20Sopenharmony_ci
88c2ecf20Sopenharmony_ci#include <linux/clk.h>
98c2ecf20Sopenharmony_ci#include <linux/delay.h>
108c2ecf20Sopenharmony_ci#include <linux/device.h>
118c2ecf20Sopenharmony_ci#include <linux/io.h>
128c2ecf20Sopenharmony_ci#include <linux/kernel.h>
138c2ecf20Sopenharmony_ci#include <linux/module.h>
148c2ecf20Sopenharmony_ci#include <linux/of.h>
158c2ecf20Sopenharmony_ci#include <linux/platform_device.h>
168c2ecf20Sopenharmony_ci#include <linux/pm_runtime.h>
178c2ecf20Sopenharmony_ci#include <linux/regmap.h>
188c2ecf20Sopenharmony_ci#include <linux/spi/spi.h>
198c2ecf20Sopenharmony_ci#include <linux/types.h>
208c2ecf20Sopenharmony_ci
218c2ecf20Sopenharmony_ci/* register map */
228c2ecf20Sopenharmony_ci#define REG_CMD			0x00
238c2ecf20Sopenharmony_ci#define REG_ADDR		0x04
248c2ecf20Sopenharmony_ci#define REG_CTRL		0x08
258c2ecf20Sopenharmony_ci#define REG_CTRL1		0x0c
268c2ecf20Sopenharmony_ci#define REG_STATUS		0x10
278c2ecf20Sopenharmony_ci#define REG_CTRL2		0x14
288c2ecf20Sopenharmony_ci#define REG_CLOCK		0x18
298c2ecf20Sopenharmony_ci#define REG_USER		0x1c
308c2ecf20Sopenharmony_ci#define REG_USER1		0x20
318c2ecf20Sopenharmony_ci#define REG_USER2		0x24
328c2ecf20Sopenharmony_ci#define REG_USER3		0x28
338c2ecf20Sopenharmony_ci#define REG_USER4		0x2c
348c2ecf20Sopenharmony_ci#define REG_SLAVE		0x30
358c2ecf20Sopenharmony_ci#define REG_SLAVE1		0x34
368c2ecf20Sopenharmony_ci#define REG_SLAVE2		0x38
378c2ecf20Sopenharmony_ci#define REG_SLAVE3		0x3c
388c2ecf20Sopenharmony_ci#define REG_C0			0x40
398c2ecf20Sopenharmony_ci#define REG_B8			0x60
408c2ecf20Sopenharmony_ci#define REG_MAX			0x7c
418c2ecf20Sopenharmony_ci
428c2ecf20Sopenharmony_ci/* register fields */
438c2ecf20Sopenharmony_ci#define CMD_USER		BIT(18)
448c2ecf20Sopenharmony_ci#define CTRL_ENABLE_AHB		BIT(17)
458c2ecf20Sopenharmony_ci#define CLOCK_SOURCE		BIT(31)
468c2ecf20Sopenharmony_ci#define CLOCK_DIV_SHIFT		12
478c2ecf20Sopenharmony_ci#define CLOCK_DIV_MASK		(0x3f << CLOCK_DIV_SHIFT)
488c2ecf20Sopenharmony_ci#define CLOCK_CNT_HIGH_SHIFT	6
498c2ecf20Sopenharmony_ci#define CLOCK_CNT_HIGH_MASK	(0x3f << CLOCK_CNT_HIGH_SHIFT)
508c2ecf20Sopenharmony_ci#define CLOCK_CNT_LOW_SHIFT	0
518c2ecf20Sopenharmony_ci#define CLOCK_CNT_LOW_MASK	(0x3f << CLOCK_CNT_LOW_SHIFT)
528c2ecf20Sopenharmony_ci#define USER_DIN_EN_MS		BIT(0)
538c2ecf20Sopenharmony_ci#define USER_CMP_MODE		BIT(2)
548c2ecf20Sopenharmony_ci#define USER_UC_DOUT_SEL	BIT(27)
558c2ecf20Sopenharmony_ci#define USER_UC_DIN_SEL		BIT(28)
568c2ecf20Sopenharmony_ci#define USER_UC_MASK		((BIT(5) - 1) << 27)
578c2ecf20Sopenharmony_ci#define USER1_BN_UC_DOUT_SHIFT	17
588c2ecf20Sopenharmony_ci#define USER1_BN_UC_DOUT_MASK	(0xff << 16)
598c2ecf20Sopenharmony_ci#define USER1_BN_UC_DIN_SHIFT	8
608c2ecf20Sopenharmony_ci#define USER1_BN_UC_DIN_MASK	(0xff << 8)
618c2ecf20Sopenharmony_ci#define USER4_CS_ACT		BIT(30)
628c2ecf20Sopenharmony_ci#define SLAVE_TRST_DONE		BIT(4)
638c2ecf20Sopenharmony_ci#define SLAVE_OP_MODE		BIT(30)
648c2ecf20Sopenharmony_ci#define SLAVE_SW_RST		BIT(31)
658c2ecf20Sopenharmony_ci
668c2ecf20Sopenharmony_ci#define SPIFC_BUFFER_SIZE	64
678c2ecf20Sopenharmony_ci
688c2ecf20Sopenharmony_ci/**
698c2ecf20Sopenharmony_ci * struct meson_spifc
708c2ecf20Sopenharmony_ci * @master:	the SPI master
718c2ecf20Sopenharmony_ci * @regmap:	regmap for device registers
728c2ecf20Sopenharmony_ci * @clk:	input clock of the built-in baud rate generator
738c2ecf20Sopenharmony_ci * @dev:	the device structure
748c2ecf20Sopenharmony_ci */
758c2ecf20Sopenharmony_cistruct meson_spifc {
768c2ecf20Sopenharmony_ci	struct spi_master *master;
778c2ecf20Sopenharmony_ci	struct regmap *regmap;
788c2ecf20Sopenharmony_ci	struct clk *clk;
798c2ecf20Sopenharmony_ci	struct device *dev;
808c2ecf20Sopenharmony_ci};
818c2ecf20Sopenharmony_ci
828c2ecf20Sopenharmony_cistatic const struct regmap_config spifc_regmap_config = {
838c2ecf20Sopenharmony_ci	.reg_bits = 32,
848c2ecf20Sopenharmony_ci	.val_bits = 32,
858c2ecf20Sopenharmony_ci	.reg_stride = 4,
868c2ecf20Sopenharmony_ci	.max_register = REG_MAX,
878c2ecf20Sopenharmony_ci};
888c2ecf20Sopenharmony_ci
898c2ecf20Sopenharmony_ci/**
908c2ecf20Sopenharmony_ci * meson_spifc_wait_ready() - wait for the current operation to terminate
918c2ecf20Sopenharmony_ci * @spifc:	the Meson SPI device
928c2ecf20Sopenharmony_ci * Return:	0 on success, a negative value on error
938c2ecf20Sopenharmony_ci */
948c2ecf20Sopenharmony_cistatic int meson_spifc_wait_ready(struct meson_spifc *spifc)
958c2ecf20Sopenharmony_ci{
968c2ecf20Sopenharmony_ci	unsigned long deadline = jiffies + msecs_to_jiffies(5);
978c2ecf20Sopenharmony_ci	u32 data;
988c2ecf20Sopenharmony_ci
998c2ecf20Sopenharmony_ci	do {
1008c2ecf20Sopenharmony_ci		regmap_read(spifc->regmap, REG_SLAVE, &data);
1018c2ecf20Sopenharmony_ci		if (data & SLAVE_TRST_DONE)
1028c2ecf20Sopenharmony_ci			return 0;
1038c2ecf20Sopenharmony_ci		cond_resched();
1048c2ecf20Sopenharmony_ci	} while (!time_after(jiffies, deadline));
1058c2ecf20Sopenharmony_ci
1068c2ecf20Sopenharmony_ci	return -ETIMEDOUT;
1078c2ecf20Sopenharmony_ci}
1088c2ecf20Sopenharmony_ci
1098c2ecf20Sopenharmony_ci/**
1108c2ecf20Sopenharmony_ci * meson_spifc_drain_buffer() - copy data from device buffer to memory
1118c2ecf20Sopenharmony_ci * @spifc:	the Meson SPI device
1128c2ecf20Sopenharmony_ci * @buf:	the destination buffer
1138c2ecf20Sopenharmony_ci * @len:	number of bytes to copy
1148c2ecf20Sopenharmony_ci */
1158c2ecf20Sopenharmony_cistatic void meson_spifc_drain_buffer(struct meson_spifc *spifc, u8 *buf,
1168c2ecf20Sopenharmony_ci				     int len)
1178c2ecf20Sopenharmony_ci{
1188c2ecf20Sopenharmony_ci	u32 data;
1198c2ecf20Sopenharmony_ci	int i = 0;
1208c2ecf20Sopenharmony_ci
1218c2ecf20Sopenharmony_ci	while (i < len) {
1228c2ecf20Sopenharmony_ci		regmap_read(spifc->regmap, REG_C0 + i, &data);
1238c2ecf20Sopenharmony_ci
1248c2ecf20Sopenharmony_ci		if (len - i >= 4) {
1258c2ecf20Sopenharmony_ci			*((u32 *)buf) = data;
1268c2ecf20Sopenharmony_ci			buf += 4;
1278c2ecf20Sopenharmony_ci		} else {
1288c2ecf20Sopenharmony_ci			memcpy(buf, &data, len - i);
1298c2ecf20Sopenharmony_ci			break;
1308c2ecf20Sopenharmony_ci		}
1318c2ecf20Sopenharmony_ci		i += 4;
1328c2ecf20Sopenharmony_ci	}
1338c2ecf20Sopenharmony_ci}
1348c2ecf20Sopenharmony_ci
1358c2ecf20Sopenharmony_ci/**
1368c2ecf20Sopenharmony_ci * meson_spifc_fill_buffer() - copy data from memory to device buffer
1378c2ecf20Sopenharmony_ci * @spifc:	the Meson SPI device
1388c2ecf20Sopenharmony_ci * @buf:	the source buffer
1398c2ecf20Sopenharmony_ci * @len:	number of bytes to copy
1408c2ecf20Sopenharmony_ci */
1418c2ecf20Sopenharmony_cistatic void meson_spifc_fill_buffer(struct meson_spifc *spifc, const u8 *buf,
1428c2ecf20Sopenharmony_ci				    int len)
1438c2ecf20Sopenharmony_ci{
1448c2ecf20Sopenharmony_ci	u32 data;
1458c2ecf20Sopenharmony_ci	int i = 0;
1468c2ecf20Sopenharmony_ci
1478c2ecf20Sopenharmony_ci	while (i < len) {
1488c2ecf20Sopenharmony_ci		if (len - i >= 4)
1498c2ecf20Sopenharmony_ci			data = *(u32 *)buf;
1508c2ecf20Sopenharmony_ci		else
1518c2ecf20Sopenharmony_ci			memcpy(&data, buf, len - i);
1528c2ecf20Sopenharmony_ci
1538c2ecf20Sopenharmony_ci		regmap_write(spifc->regmap, REG_C0 + i, data);
1548c2ecf20Sopenharmony_ci
1558c2ecf20Sopenharmony_ci		buf += 4;
1568c2ecf20Sopenharmony_ci		i += 4;
1578c2ecf20Sopenharmony_ci	}
1588c2ecf20Sopenharmony_ci}
1598c2ecf20Sopenharmony_ci
1608c2ecf20Sopenharmony_ci/**
1618c2ecf20Sopenharmony_ci * meson_spifc_setup_speed() - program the clock divider
1628c2ecf20Sopenharmony_ci * @spifc:	the Meson SPI device
1638c2ecf20Sopenharmony_ci * @speed:	desired speed in Hz
1648c2ecf20Sopenharmony_ci */
1658c2ecf20Sopenharmony_cistatic void meson_spifc_setup_speed(struct meson_spifc *spifc, u32 speed)
1668c2ecf20Sopenharmony_ci{
1678c2ecf20Sopenharmony_ci	unsigned long parent, value;
1688c2ecf20Sopenharmony_ci	int n;
1698c2ecf20Sopenharmony_ci
1708c2ecf20Sopenharmony_ci	parent = clk_get_rate(spifc->clk);
1718c2ecf20Sopenharmony_ci	n = max_t(int, parent / speed - 1, 1);
1728c2ecf20Sopenharmony_ci
1738c2ecf20Sopenharmony_ci	dev_dbg(spifc->dev, "parent %lu, speed %u, n %d\n", parent,
1748c2ecf20Sopenharmony_ci		speed, n);
1758c2ecf20Sopenharmony_ci
1768c2ecf20Sopenharmony_ci	value = (n << CLOCK_DIV_SHIFT) & CLOCK_DIV_MASK;
1778c2ecf20Sopenharmony_ci	value |= (n << CLOCK_CNT_LOW_SHIFT) & CLOCK_CNT_LOW_MASK;
1788c2ecf20Sopenharmony_ci	value |= (((n + 1) / 2 - 1) << CLOCK_CNT_HIGH_SHIFT) &
1798c2ecf20Sopenharmony_ci		CLOCK_CNT_HIGH_MASK;
1808c2ecf20Sopenharmony_ci
1818c2ecf20Sopenharmony_ci	regmap_write(spifc->regmap, REG_CLOCK, value);
1828c2ecf20Sopenharmony_ci}
1838c2ecf20Sopenharmony_ci
1848c2ecf20Sopenharmony_ci/**
1858c2ecf20Sopenharmony_ci * meson_spifc_txrx() - transfer a chunk of data
1868c2ecf20Sopenharmony_ci * @spifc:	the Meson SPI device
1878c2ecf20Sopenharmony_ci * @xfer:	the current SPI transfer
1888c2ecf20Sopenharmony_ci * @offset:	offset of the data to transfer
1898c2ecf20Sopenharmony_ci * @len:	length of the data to transfer
1908c2ecf20Sopenharmony_ci * @last_xfer:	whether this is the last transfer of the message
1918c2ecf20Sopenharmony_ci * @last_chunk:	whether this is the last chunk of the transfer
1928c2ecf20Sopenharmony_ci * Return:	0 on success, a negative value on error
1938c2ecf20Sopenharmony_ci */
1948c2ecf20Sopenharmony_cistatic int meson_spifc_txrx(struct meson_spifc *spifc,
1958c2ecf20Sopenharmony_ci			    struct spi_transfer *xfer,
1968c2ecf20Sopenharmony_ci			    int offset, int len, bool last_xfer,
1978c2ecf20Sopenharmony_ci			    bool last_chunk)
1988c2ecf20Sopenharmony_ci{
1998c2ecf20Sopenharmony_ci	bool keep_cs = true;
2008c2ecf20Sopenharmony_ci	int ret;
2018c2ecf20Sopenharmony_ci
2028c2ecf20Sopenharmony_ci	if (xfer->tx_buf)
2038c2ecf20Sopenharmony_ci		meson_spifc_fill_buffer(spifc, xfer->tx_buf + offset, len);
2048c2ecf20Sopenharmony_ci
2058c2ecf20Sopenharmony_ci	/* enable DOUT stage */
2068c2ecf20Sopenharmony_ci	regmap_update_bits(spifc->regmap, REG_USER, USER_UC_MASK,
2078c2ecf20Sopenharmony_ci			   USER_UC_DOUT_SEL);
2088c2ecf20Sopenharmony_ci	regmap_write(spifc->regmap, REG_USER1,
2098c2ecf20Sopenharmony_ci		     (8 * len - 1) << USER1_BN_UC_DOUT_SHIFT);
2108c2ecf20Sopenharmony_ci
2118c2ecf20Sopenharmony_ci	/* enable data input during DOUT */
2128c2ecf20Sopenharmony_ci	regmap_update_bits(spifc->regmap, REG_USER, USER_DIN_EN_MS,
2138c2ecf20Sopenharmony_ci			   USER_DIN_EN_MS);
2148c2ecf20Sopenharmony_ci
2158c2ecf20Sopenharmony_ci	if (last_chunk) {
2168c2ecf20Sopenharmony_ci		if (last_xfer)
2178c2ecf20Sopenharmony_ci			keep_cs = xfer->cs_change;
2188c2ecf20Sopenharmony_ci		else
2198c2ecf20Sopenharmony_ci			keep_cs = !xfer->cs_change;
2208c2ecf20Sopenharmony_ci	}
2218c2ecf20Sopenharmony_ci
2228c2ecf20Sopenharmony_ci	regmap_update_bits(spifc->regmap, REG_USER4, USER4_CS_ACT,
2238c2ecf20Sopenharmony_ci			   keep_cs ? USER4_CS_ACT : 0);
2248c2ecf20Sopenharmony_ci
2258c2ecf20Sopenharmony_ci	/* clear transition done bit */
2268c2ecf20Sopenharmony_ci	regmap_update_bits(spifc->regmap, REG_SLAVE, SLAVE_TRST_DONE, 0);
2278c2ecf20Sopenharmony_ci	/* start transfer */
2288c2ecf20Sopenharmony_ci	regmap_update_bits(spifc->regmap, REG_CMD, CMD_USER, CMD_USER);
2298c2ecf20Sopenharmony_ci
2308c2ecf20Sopenharmony_ci	ret = meson_spifc_wait_ready(spifc);
2318c2ecf20Sopenharmony_ci
2328c2ecf20Sopenharmony_ci	if (!ret && xfer->rx_buf)
2338c2ecf20Sopenharmony_ci		meson_spifc_drain_buffer(spifc, xfer->rx_buf + offset, len);
2348c2ecf20Sopenharmony_ci
2358c2ecf20Sopenharmony_ci	return ret;
2368c2ecf20Sopenharmony_ci}
2378c2ecf20Sopenharmony_ci
2388c2ecf20Sopenharmony_ci/**
2398c2ecf20Sopenharmony_ci * meson_spifc_transfer_one() - perform a single transfer
2408c2ecf20Sopenharmony_ci * @master:	the SPI master
2418c2ecf20Sopenharmony_ci * @spi:	the SPI device
2428c2ecf20Sopenharmony_ci * @xfer:	the current SPI transfer
2438c2ecf20Sopenharmony_ci * Return:	0 on success, a negative value on error
2448c2ecf20Sopenharmony_ci */
2458c2ecf20Sopenharmony_cistatic int meson_spifc_transfer_one(struct spi_master *master,
2468c2ecf20Sopenharmony_ci				    struct spi_device *spi,
2478c2ecf20Sopenharmony_ci				    struct spi_transfer *xfer)
2488c2ecf20Sopenharmony_ci{
2498c2ecf20Sopenharmony_ci	struct meson_spifc *spifc = spi_master_get_devdata(master);
2508c2ecf20Sopenharmony_ci	int len, done = 0, ret = 0;
2518c2ecf20Sopenharmony_ci
2528c2ecf20Sopenharmony_ci	meson_spifc_setup_speed(spifc, xfer->speed_hz);
2538c2ecf20Sopenharmony_ci
2548c2ecf20Sopenharmony_ci	regmap_update_bits(spifc->regmap, REG_CTRL, CTRL_ENABLE_AHB, 0);
2558c2ecf20Sopenharmony_ci
2568c2ecf20Sopenharmony_ci	while (done < xfer->len && !ret) {
2578c2ecf20Sopenharmony_ci		len = min_t(int, xfer->len - done, SPIFC_BUFFER_SIZE);
2588c2ecf20Sopenharmony_ci		ret = meson_spifc_txrx(spifc, xfer, done, len,
2598c2ecf20Sopenharmony_ci				       spi_transfer_is_last(master, xfer),
2608c2ecf20Sopenharmony_ci				       done + len >= xfer->len);
2618c2ecf20Sopenharmony_ci		done += len;
2628c2ecf20Sopenharmony_ci	}
2638c2ecf20Sopenharmony_ci
2648c2ecf20Sopenharmony_ci	regmap_update_bits(spifc->regmap, REG_CTRL, CTRL_ENABLE_AHB,
2658c2ecf20Sopenharmony_ci			   CTRL_ENABLE_AHB);
2668c2ecf20Sopenharmony_ci
2678c2ecf20Sopenharmony_ci	return ret;
2688c2ecf20Sopenharmony_ci}
2698c2ecf20Sopenharmony_ci
2708c2ecf20Sopenharmony_ci/**
2718c2ecf20Sopenharmony_ci * meson_spifc_hw_init() - reset and initialize the SPI controller
2728c2ecf20Sopenharmony_ci * @spifc:	the Meson SPI device
2738c2ecf20Sopenharmony_ci */
2748c2ecf20Sopenharmony_cistatic void meson_spifc_hw_init(struct meson_spifc *spifc)
2758c2ecf20Sopenharmony_ci{
2768c2ecf20Sopenharmony_ci	/* reset device */
2778c2ecf20Sopenharmony_ci	regmap_update_bits(spifc->regmap, REG_SLAVE, SLAVE_SW_RST,
2788c2ecf20Sopenharmony_ci			   SLAVE_SW_RST);
2798c2ecf20Sopenharmony_ci	/* disable compatible mode */
2808c2ecf20Sopenharmony_ci	regmap_update_bits(spifc->regmap, REG_USER, USER_CMP_MODE, 0);
2818c2ecf20Sopenharmony_ci	/* set master mode */
2828c2ecf20Sopenharmony_ci	regmap_update_bits(spifc->regmap, REG_SLAVE, SLAVE_OP_MODE, 0);
2838c2ecf20Sopenharmony_ci}
2848c2ecf20Sopenharmony_ci
2858c2ecf20Sopenharmony_cistatic int meson_spifc_probe(struct platform_device *pdev)
2868c2ecf20Sopenharmony_ci{
2878c2ecf20Sopenharmony_ci	struct spi_master *master;
2888c2ecf20Sopenharmony_ci	struct meson_spifc *spifc;
2898c2ecf20Sopenharmony_ci	void __iomem *base;
2908c2ecf20Sopenharmony_ci	unsigned int rate;
2918c2ecf20Sopenharmony_ci	int ret = 0;
2928c2ecf20Sopenharmony_ci
2938c2ecf20Sopenharmony_ci	master = spi_alloc_master(&pdev->dev, sizeof(struct meson_spifc));
2948c2ecf20Sopenharmony_ci	if (!master)
2958c2ecf20Sopenharmony_ci		return -ENOMEM;
2968c2ecf20Sopenharmony_ci
2978c2ecf20Sopenharmony_ci	platform_set_drvdata(pdev, master);
2988c2ecf20Sopenharmony_ci
2998c2ecf20Sopenharmony_ci	spifc = spi_master_get_devdata(master);
3008c2ecf20Sopenharmony_ci	spifc->dev = &pdev->dev;
3018c2ecf20Sopenharmony_ci
3028c2ecf20Sopenharmony_ci	base = devm_platform_ioremap_resource(pdev, 0);
3038c2ecf20Sopenharmony_ci	if (IS_ERR(base)) {
3048c2ecf20Sopenharmony_ci		ret = PTR_ERR(base);
3058c2ecf20Sopenharmony_ci		goto out_err;
3068c2ecf20Sopenharmony_ci	}
3078c2ecf20Sopenharmony_ci
3088c2ecf20Sopenharmony_ci	spifc->regmap = devm_regmap_init_mmio(spifc->dev, base,
3098c2ecf20Sopenharmony_ci					      &spifc_regmap_config);
3108c2ecf20Sopenharmony_ci	if (IS_ERR(spifc->regmap)) {
3118c2ecf20Sopenharmony_ci		ret = PTR_ERR(spifc->regmap);
3128c2ecf20Sopenharmony_ci		goto out_err;
3138c2ecf20Sopenharmony_ci	}
3148c2ecf20Sopenharmony_ci
3158c2ecf20Sopenharmony_ci	spifc->clk = devm_clk_get(spifc->dev, NULL);
3168c2ecf20Sopenharmony_ci	if (IS_ERR(spifc->clk)) {
3178c2ecf20Sopenharmony_ci		dev_err(spifc->dev, "missing clock\n");
3188c2ecf20Sopenharmony_ci		ret = PTR_ERR(spifc->clk);
3198c2ecf20Sopenharmony_ci		goto out_err;
3208c2ecf20Sopenharmony_ci	}
3218c2ecf20Sopenharmony_ci
3228c2ecf20Sopenharmony_ci	ret = clk_prepare_enable(spifc->clk);
3238c2ecf20Sopenharmony_ci	if (ret) {
3248c2ecf20Sopenharmony_ci		dev_err(spifc->dev, "can't prepare clock\n");
3258c2ecf20Sopenharmony_ci		goto out_err;
3268c2ecf20Sopenharmony_ci	}
3278c2ecf20Sopenharmony_ci
3288c2ecf20Sopenharmony_ci	rate = clk_get_rate(spifc->clk);
3298c2ecf20Sopenharmony_ci
3308c2ecf20Sopenharmony_ci	master->num_chipselect = 1;
3318c2ecf20Sopenharmony_ci	master->dev.of_node = pdev->dev.of_node;
3328c2ecf20Sopenharmony_ci	master->bits_per_word_mask = SPI_BPW_MASK(8);
3338c2ecf20Sopenharmony_ci	master->auto_runtime_pm = true;
3348c2ecf20Sopenharmony_ci	master->transfer_one = meson_spifc_transfer_one;
3358c2ecf20Sopenharmony_ci	master->min_speed_hz = rate >> 6;
3368c2ecf20Sopenharmony_ci	master->max_speed_hz = rate >> 1;
3378c2ecf20Sopenharmony_ci
3388c2ecf20Sopenharmony_ci	meson_spifc_hw_init(spifc);
3398c2ecf20Sopenharmony_ci
3408c2ecf20Sopenharmony_ci	pm_runtime_set_active(spifc->dev);
3418c2ecf20Sopenharmony_ci	pm_runtime_enable(spifc->dev);
3428c2ecf20Sopenharmony_ci
3438c2ecf20Sopenharmony_ci	ret = devm_spi_register_master(spifc->dev, master);
3448c2ecf20Sopenharmony_ci	if (ret) {
3458c2ecf20Sopenharmony_ci		dev_err(spifc->dev, "failed to register spi master\n");
3468c2ecf20Sopenharmony_ci		goto out_clk;
3478c2ecf20Sopenharmony_ci	}
3488c2ecf20Sopenharmony_ci
3498c2ecf20Sopenharmony_ci	return 0;
3508c2ecf20Sopenharmony_ciout_clk:
3518c2ecf20Sopenharmony_ci	clk_disable_unprepare(spifc->clk);
3528c2ecf20Sopenharmony_ci	pm_runtime_disable(spifc->dev);
3538c2ecf20Sopenharmony_ciout_err:
3548c2ecf20Sopenharmony_ci	spi_master_put(master);
3558c2ecf20Sopenharmony_ci	return ret;
3568c2ecf20Sopenharmony_ci}
3578c2ecf20Sopenharmony_ci
3588c2ecf20Sopenharmony_cistatic int meson_spifc_remove(struct platform_device *pdev)
3598c2ecf20Sopenharmony_ci{
3608c2ecf20Sopenharmony_ci	struct spi_master *master = platform_get_drvdata(pdev);
3618c2ecf20Sopenharmony_ci	struct meson_spifc *spifc = spi_master_get_devdata(master);
3628c2ecf20Sopenharmony_ci
3638c2ecf20Sopenharmony_ci	pm_runtime_get_sync(&pdev->dev);
3648c2ecf20Sopenharmony_ci	clk_disable_unprepare(spifc->clk);
3658c2ecf20Sopenharmony_ci	pm_runtime_disable(&pdev->dev);
3668c2ecf20Sopenharmony_ci
3678c2ecf20Sopenharmony_ci	return 0;
3688c2ecf20Sopenharmony_ci}
3698c2ecf20Sopenharmony_ci
3708c2ecf20Sopenharmony_ci#ifdef CONFIG_PM_SLEEP
3718c2ecf20Sopenharmony_cistatic int meson_spifc_suspend(struct device *dev)
3728c2ecf20Sopenharmony_ci{
3738c2ecf20Sopenharmony_ci	struct spi_master *master = dev_get_drvdata(dev);
3748c2ecf20Sopenharmony_ci	struct meson_spifc *spifc = spi_master_get_devdata(master);
3758c2ecf20Sopenharmony_ci	int ret;
3768c2ecf20Sopenharmony_ci
3778c2ecf20Sopenharmony_ci	ret = spi_master_suspend(master);
3788c2ecf20Sopenharmony_ci	if (ret)
3798c2ecf20Sopenharmony_ci		return ret;
3808c2ecf20Sopenharmony_ci
3818c2ecf20Sopenharmony_ci	if (!pm_runtime_suspended(dev))
3828c2ecf20Sopenharmony_ci		clk_disable_unprepare(spifc->clk);
3838c2ecf20Sopenharmony_ci
3848c2ecf20Sopenharmony_ci	return 0;
3858c2ecf20Sopenharmony_ci}
3868c2ecf20Sopenharmony_ci
3878c2ecf20Sopenharmony_cistatic int meson_spifc_resume(struct device *dev)
3888c2ecf20Sopenharmony_ci{
3898c2ecf20Sopenharmony_ci	struct spi_master *master = dev_get_drvdata(dev);
3908c2ecf20Sopenharmony_ci	struct meson_spifc *spifc = spi_master_get_devdata(master);
3918c2ecf20Sopenharmony_ci	int ret;
3928c2ecf20Sopenharmony_ci
3938c2ecf20Sopenharmony_ci	if (!pm_runtime_suspended(dev)) {
3948c2ecf20Sopenharmony_ci		ret = clk_prepare_enable(spifc->clk);
3958c2ecf20Sopenharmony_ci		if (ret)
3968c2ecf20Sopenharmony_ci			return ret;
3978c2ecf20Sopenharmony_ci	}
3988c2ecf20Sopenharmony_ci
3998c2ecf20Sopenharmony_ci	meson_spifc_hw_init(spifc);
4008c2ecf20Sopenharmony_ci
4018c2ecf20Sopenharmony_ci	ret = spi_master_resume(master);
4028c2ecf20Sopenharmony_ci	if (ret)
4038c2ecf20Sopenharmony_ci		clk_disable_unprepare(spifc->clk);
4048c2ecf20Sopenharmony_ci
4058c2ecf20Sopenharmony_ci	return ret;
4068c2ecf20Sopenharmony_ci}
4078c2ecf20Sopenharmony_ci#endif /* CONFIG_PM_SLEEP */
4088c2ecf20Sopenharmony_ci
4098c2ecf20Sopenharmony_ci#ifdef CONFIG_PM
4108c2ecf20Sopenharmony_cistatic int meson_spifc_runtime_suspend(struct device *dev)
4118c2ecf20Sopenharmony_ci{
4128c2ecf20Sopenharmony_ci	struct spi_master *master = dev_get_drvdata(dev);
4138c2ecf20Sopenharmony_ci	struct meson_spifc *spifc = spi_master_get_devdata(master);
4148c2ecf20Sopenharmony_ci
4158c2ecf20Sopenharmony_ci	clk_disable_unprepare(spifc->clk);
4168c2ecf20Sopenharmony_ci
4178c2ecf20Sopenharmony_ci	return 0;
4188c2ecf20Sopenharmony_ci}
4198c2ecf20Sopenharmony_ci
4208c2ecf20Sopenharmony_cistatic int meson_spifc_runtime_resume(struct device *dev)
4218c2ecf20Sopenharmony_ci{
4228c2ecf20Sopenharmony_ci	struct spi_master *master = dev_get_drvdata(dev);
4238c2ecf20Sopenharmony_ci	struct meson_spifc *spifc = spi_master_get_devdata(master);
4248c2ecf20Sopenharmony_ci
4258c2ecf20Sopenharmony_ci	return clk_prepare_enable(spifc->clk);
4268c2ecf20Sopenharmony_ci}
4278c2ecf20Sopenharmony_ci#endif /* CONFIG_PM */
4288c2ecf20Sopenharmony_ci
4298c2ecf20Sopenharmony_cistatic const struct dev_pm_ops meson_spifc_pm_ops = {
4308c2ecf20Sopenharmony_ci	SET_SYSTEM_SLEEP_PM_OPS(meson_spifc_suspend, meson_spifc_resume)
4318c2ecf20Sopenharmony_ci	SET_RUNTIME_PM_OPS(meson_spifc_runtime_suspend,
4328c2ecf20Sopenharmony_ci			   meson_spifc_runtime_resume,
4338c2ecf20Sopenharmony_ci			   NULL)
4348c2ecf20Sopenharmony_ci};
4358c2ecf20Sopenharmony_ci
4368c2ecf20Sopenharmony_cistatic const struct of_device_id meson_spifc_dt_match[] = {
4378c2ecf20Sopenharmony_ci	{ .compatible = "amlogic,meson6-spifc", },
4388c2ecf20Sopenharmony_ci	{ .compatible = "amlogic,meson-gxbb-spifc", },
4398c2ecf20Sopenharmony_ci	{ },
4408c2ecf20Sopenharmony_ci};
4418c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(of, meson_spifc_dt_match);
4428c2ecf20Sopenharmony_ci
4438c2ecf20Sopenharmony_cistatic struct platform_driver meson_spifc_driver = {
4448c2ecf20Sopenharmony_ci	.probe	= meson_spifc_probe,
4458c2ecf20Sopenharmony_ci	.remove	= meson_spifc_remove,
4468c2ecf20Sopenharmony_ci	.driver	= {
4478c2ecf20Sopenharmony_ci		.name		= "meson-spifc",
4488c2ecf20Sopenharmony_ci		.of_match_table	= of_match_ptr(meson_spifc_dt_match),
4498c2ecf20Sopenharmony_ci		.pm		= &meson_spifc_pm_ops,
4508c2ecf20Sopenharmony_ci	},
4518c2ecf20Sopenharmony_ci};
4528c2ecf20Sopenharmony_ci
4538c2ecf20Sopenharmony_cimodule_platform_driver(meson_spifc_driver);
4548c2ecf20Sopenharmony_ci
4558c2ecf20Sopenharmony_ciMODULE_AUTHOR("Beniamino Galvani <b.galvani@gmail.com>");
4568c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("Amlogic Meson SPIFC driver");
4578c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL v2");
458