162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0+
262306a36Sopenharmony_ci//
362306a36Sopenharmony_ci// Driver for Amlogic Meson SPI flash controller (SPIFC)
462306a36Sopenharmony_ci//
562306a36Sopenharmony_ci// Copyright (C) 2014 Beniamino Galvani <b.galvani@gmail.com>
662306a36Sopenharmony_ci//
762306a36Sopenharmony_ci
862306a36Sopenharmony_ci#include <linux/clk.h>
962306a36Sopenharmony_ci#include <linux/delay.h>
1062306a36Sopenharmony_ci#include <linux/device.h>
1162306a36Sopenharmony_ci#include <linux/io.h>
1262306a36Sopenharmony_ci#include <linux/kernel.h>
1362306a36Sopenharmony_ci#include <linux/module.h>
1462306a36Sopenharmony_ci#include <linux/of.h>
1562306a36Sopenharmony_ci#include <linux/platform_device.h>
1662306a36Sopenharmony_ci#include <linux/pm_runtime.h>
1762306a36Sopenharmony_ci#include <linux/regmap.h>
1862306a36Sopenharmony_ci#include <linux/spi/spi.h>
1962306a36Sopenharmony_ci#include <linux/types.h>
2062306a36Sopenharmony_ci
2162306a36Sopenharmony_ci/* register map */
2262306a36Sopenharmony_ci#define REG_CMD			0x00
2362306a36Sopenharmony_ci#define REG_ADDR		0x04
2462306a36Sopenharmony_ci#define REG_CTRL		0x08
2562306a36Sopenharmony_ci#define REG_CTRL1		0x0c
2662306a36Sopenharmony_ci#define REG_STATUS		0x10
2762306a36Sopenharmony_ci#define REG_CTRL2		0x14
2862306a36Sopenharmony_ci#define REG_CLOCK		0x18
2962306a36Sopenharmony_ci#define REG_USER		0x1c
3062306a36Sopenharmony_ci#define REG_USER1		0x20
3162306a36Sopenharmony_ci#define REG_USER2		0x24
3262306a36Sopenharmony_ci#define REG_USER3		0x28
3362306a36Sopenharmony_ci#define REG_USER4		0x2c
3462306a36Sopenharmony_ci#define REG_SLAVE		0x30
3562306a36Sopenharmony_ci#define REG_SLAVE1		0x34
3662306a36Sopenharmony_ci#define REG_SLAVE2		0x38
3762306a36Sopenharmony_ci#define REG_SLAVE3		0x3c
3862306a36Sopenharmony_ci#define REG_C0			0x40
3962306a36Sopenharmony_ci#define REG_B8			0x60
4062306a36Sopenharmony_ci#define REG_MAX			0x7c
4162306a36Sopenharmony_ci
4262306a36Sopenharmony_ci/* register fields */
4362306a36Sopenharmony_ci#define CMD_USER		BIT(18)
4462306a36Sopenharmony_ci#define CTRL_ENABLE_AHB		BIT(17)
4562306a36Sopenharmony_ci#define CLOCK_SOURCE		BIT(31)
4662306a36Sopenharmony_ci#define CLOCK_DIV_SHIFT		12
4762306a36Sopenharmony_ci#define CLOCK_DIV_MASK		(0x3f << CLOCK_DIV_SHIFT)
4862306a36Sopenharmony_ci#define CLOCK_CNT_HIGH_SHIFT	6
4962306a36Sopenharmony_ci#define CLOCK_CNT_HIGH_MASK	(0x3f << CLOCK_CNT_HIGH_SHIFT)
5062306a36Sopenharmony_ci#define CLOCK_CNT_LOW_SHIFT	0
5162306a36Sopenharmony_ci#define CLOCK_CNT_LOW_MASK	(0x3f << CLOCK_CNT_LOW_SHIFT)
5262306a36Sopenharmony_ci#define USER_DIN_EN_MS		BIT(0)
5362306a36Sopenharmony_ci#define USER_CMP_MODE		BIT(2)
5462306a36Sopenharmony_ci#define USER_UC_DOUT_SEL	BIT(27)
5562306a36Sopenharmony_ci#define USER_UC_DIN_SEL		BIT(28)
5662306a36Sopenharmony_ci#define USER_UC_MASK		((BIT(5) - 1) << 27)
5762306a36Sopenharmony_ci#define USER1_BN_UC_DOUT_SHIFT	17
5862306a36Sopenharmony_ci#define USER1_BN_UC_DOUT_MASK	(0xff << 16)
5962306a36Sopenharmony_ci#define USER1_BN_UC_DIN_SHIFT	8
6062306a36Sopenharmony_ci#define USER1_BN_UC_DIN_MASK	(0xff << 8)
6162306a36Sopenharmony_ci#define USER4_CS_ACT		BIT(30)
6262306a36Sopenharmony_ci#define SLAVE_TRST_DONE		BIT(4)
6362306a36Sopenharmony_ci#define SLAVE_OP_MODE		BIT(30)
6462306a36Sopenharmony_ci#define SLAVE_SW_RST		BIT(31)
6562306a36Sopenharmony_ci
6662306a36Sopenharmony_ci#define SPIFC_BUFFER_SIZE	64
6762306a36Sopenharmony_ci
6862306a36Sopenharmony_ci/**
6962306a36Sopenharmony_ci * struct meson_spifc
7062306a36Sopenharmony_ci * @master:	the SPI master
7162306a36Sopenharmony_ci * @regmap:	regmap for device registers
7262306a36Sopenharmony_ci * @clk:	input clock of the built-in baud rate generator
7362306a36Sopenharmony_ci * @dev:	the device structure
7462306a36Sopenharmony_ci */
7562306a36Sopenharmony_cistruct meson_spifc {
7662306a36Sopenharmony_ci	struct spi_master *master;
7762306a36Sopenharmony_ci	struct regmap *regmap;
7862306a36Sopenharmony_ci	struct clk *clk;
7962306a36Sopenharmony_ci	struct device *dev;
8062306a36Sopenharmony_ci};
8162306a36Sopenharmony_ci
8262306a36Sopenharmony_cistatic const struct regmap_config spifc_regmap_config = {
8362306a36Sopenharmony_ci	.reg_bits = 32,
8462306a36Sopenharmony_ci	.val_bits = 32,
8562306a36Sopenharmony_ci	.reg_stride = 4,
8662306a36Sopenharmony_ci	.max_register = REG_MAX,
8762306a36Sopenharmony_ci};
8862306a36Sopenharmony_ci
8962306a36Sopenharmony_ci/**
9062306a36Sopenharmony_ci * meson_spifc_wait_ready() - wait for the current operation to terminate
9162306a36Sopenharmony_ci * @spifc:	the Meson SPI device
9262306a36Sopenharmony_ci * Return:	0 on success, a negative value on error
9362306a36Sopenharmony_ci */
9462306a36Sopenharmony_cistatic int meson_spifc_wait_ready(struct meson_spifc *spifc)
9562306a36Sopenharmony_ci{
9662306a36Sopenharmony_ci	unsigned long deadline = jiffies + msecs_to_jiffies(5);
9762306a36Sopenharmony_ci	u32 data;
9862306a36Sopenharmony_ci
9962306a36Sopenharmony_ci	do {
10062306a36Sopenharmony_ci		regmap_read(spifc->regmap, REG_SLAVE, &data);
10162306a36Sopenharmony_ci		if (data & SLAVE_TRST_DONE)
10262306a36Sopenharmony_ci			return 0;
10362306a36Sopenharmony_ci		cond_resched();
10462306a36Sopenharmony_ci	} while (!time_after(jiffies, deadline));
10562306a36Sopenharmony_ci
10662306a36Sopenharmony_ci	return -ETIMEDOUT;
10762306a36Sopenharmony_ci}
10862306a36Sopenharmony_ci
10962306a36Sopenharmony_ci/**
11062306a36Sopenharmony_ci * meson_spifc_drain_buffer() - copy data from device buffer to memory
11162306a36Sopenharmony_ci * @spifc:	the Meson SPI device
11262306a36Sopenharmony_ci * @buf:	the destination buffer
11362306a36Sopenharmony_ci * @len:	number of bytes to copy
11462306a36Sopenharmony_ci */
11562306a36Sopenharmony_cistatic void meson_spifc_drain_buffer(struct meson_spifc *spifc, u8 *buf,
11662306a36Sopenharmony_ci				     int len)
11762306a36Sopenharmony_ci{
11862306a36Sopenharmony_ci	u32 data;
11962306a36Sopenharmony_ci	int i = 0;
12062306a36Sopenharmony_ci
12162306a36Sopenharmony_ci	while (i < len) {
12262306a36Sopenharmony_ci		regmap_read(spifc->regmap, REG_C0 + i, &data);
12362306a36Sopenharmony_ci
12462306a36Sopenharmony_ci		if (len - i >= 4) {
12562306a36Sopenharmony_ci			*((u32 *)buf) = data;
12662306a36Sopenharmony_ci			buf += 4;
12762306a36Sopenharmony_ci		} else {
12862306a36Sopenharmony_ci			memcpy(buf, &data, len - i);
12962306a36Sopenharmony_ci			break;
13062306a36Sopenharmony_ci		}
13162306a36Sopenharmony_ci		i += 4;
13262306a36Sopenharmony_ci	}
13362306a36Sopenharmony_ci}
13462306a36Sopenharmony_ci
13562306a36Sopenharmony_ci/**
13662306a36Sopenharmony_ci * meson_spifc_fill_buffer() - copy data from memory to device buffer
13762306a36Sopenharmony_ci * @spifc:	the Meson SPI device
13862306a36Sopenharmony_ci * @buf:	the source buffer
13962306a36Sopenharmony_ci * @len:	number of bytes to copy
14062306a36Sopenharmony_ci */
14162306a36Sopenharmony_cistatic void meson_spifc_fill_buffer(struct meson_spifc *spifc, const u8 *buf,
14262306a36Sopenharmony_ci				    int len)
14362306a36Sopenharmony_ci{
14462306a36Sopenharmony_ci	u32 data;
14562306a36Sopenharmony_ci	int i = 0;
14662306a36Sopenharmony_ci
14762306a36Sopenharmony_ci	while (i < len) {
14862306a36Sopenharmony_ci		if (len - i >= 4)
14962306a36Sopenharmony_ci			data = *(u32 *)buf;
15062306a36Sopenharmony_ci		else
15162306a36Sopenharmony_ci			memcpy(&data, buf, len - i);
15262306a36Sopenharmony_ci
15362306a36Sopenharmony_ci		regmap_write(spifc->regmap, REG_C0 + i, data);
15462306a36Sopenharmony_ci
15562306a36Sopenharmony_ci		buf += 4;
15662306a36Sopenharmony_ci		i += 4;
15762306a36Sopenharmony_ci	}
15862306a36Sopenharmony_ci}
15962306a36Sopenharmony_ci
16062306a36Sopenharmony_ci/**
16162306a36Sopenharmony_ci * meson_spifc_setup_speed() - program the clock divider
16262306a36Sopenharmony_ci * @spifc:	the Meson SPI device
16362306a36Sopenharmony_ci * @speed:	desired speed in Hz
16462306a36Sopenharmony_ci */
16562306a36Sopenharmony_cistatic void meson_spifc_setup_speed(struct meson_spifc *spifc, u32 speed)
16662306a36Sopenharmony_ci{
16762306a36Sopenharmony_ci	unsigned long parent, value;
16862306a36Sopenharmony_ci	int n;
16962306a36Sopenharmony_ci
17062306a36Sopenharmony_ci	parent = clk_get_rate(spifc->clk);
17162306a36Sopenharmony_ci	n = max_t(int, parent / speed - 1, 1);
17262306a36Sopenharmony_ci
17362306a36Sopenharmony_ci	dev_dbg(spifc->dev, "parent %lu, speed %u, n %d\n", parent,
17462306a36Sopenharmony_ci		speed, n);
17562306a36Sopenharmony_ci
17662306a36Sopenharmony_ci	value = (n << CLOCK_DIV_SHIFT) & CLOCK_DIV_MASK;
17762306a36Sopenharmony_ci	value |= (n << CLOCK_CNT_LOW_SHIFT) & CLOCK_CNT_LOW_MASK;
17862306a36Sopenharmony_ci	value |= (((n + 1) / 2 - 1) << CLOCK_CNT_HIGH_SHIFT) &
17962306a36Sopenharmony_ci		CLOCK_CNT_HIGH_MASK;
18062306a36Sopenharmony_ci
18162306a36Sopenharmony_ci	regmap_write(spifc->regmap, REG_CLOCK, value);
18262306a36Sopenharmony_ci}
18362306a36Sopenharmony_ci
18462306a36Sopenharmony_ci/**
18562306a36Sopenharmony_ci * meson_spifc_txrx() - transfer a chunk of data
18662306a36Sopenharmony_ci * @spifc:	the Meson SPI device
18762306a36Sopenharmony_ci * @xfer:	the current SPI transfer
18862306a36Sopenharmony_ci * @offset:	offset of the data to transfer
18962306a36Sopenharmony_ci * @len:	length of the data to transfer
19062306a36Sopenharmony_ci * @last_xfer:	whether this is the last transfer of the message
19162306a36Sopenharmony_ci * @last_chunk:	whether this is the last chunk of the transfer
19262306a36Sopenharmony_ci * Return:	0 on success, a negative value on error
19362306a36Sopenharmony_ci */
19462306a36Sopenharmony_cistatic int meson_spifc_txrx(struct meson_spifc *spifc,
19562306a36Sopenharmony_ci			    struct spi_transfer *xfer,
19662306a36Sopenharmony_ci			    int offset, int len, bool last_xfer,
19762306a36Sopenharmony_ci			    bool last_chunk)
19862306a36Sopenharmony_ci{
19962306a36Sopenharmony_ci	bool keep_cs = true;
20062306a36Sopenharmony_ci	int ret;
20162306a36Sopenharmony_ci
20262306a36Sopenharmony_ci	if (xfer->tx_buf)
20362306a36Sopenharmony_ci		meson_spifc_fill_buffer(spifc, xfer->tx_buf + offset, len);
20462306a36Sopenharmony_ci
20562306a36Sopenharmony_ci	/* enable DOUT stage */
20662306a36Sopenharmony_ci	regmap_update_bits(spifc->regmap, REG_USER, USER_UC_MASK,
20762306a36Sopenharmony_ci			   USER_UC_DOUT_SEL);
20862306a36Sopenharmony_ci	regmap_write(spifc->regmap, REG_USER1,
20962306a36Sopenharmony_ci		     (8 * len - 1) << USER1_BN_UC_DOUT_SHIFT);
21062306a36Sopenharmony_ci
21162306a36Sopenharmony_ci	/* enable data input during DOUT */
21262306a36Sopenharmony_ci	regmap_update_bits(spifc->regmap, REG_USER, USER_DIN_EN_MS,
21362306a36Sopenharmony_ci			   USER_DIN_EN_MS);
21462306a36Sopenharmony_ci
21562306a36Sopenharmony_ci	if (last_chunk) {
21662306a36Sopenharmony_ci		if (last_xfer)
21762306a36Sopenharmony_ci			keep_cs = xfer->cs_change;
21862306a36Sopenharmony_ci		else
21962306a36Sopenharmony_ci			keep_cs = !xfer->cs_change;
22062306a36Sopenharmony_ci	}
22162306a36Sopenharmony_ci
22262306a36Sopenharmony_ci	regmap_update_bits(spifc->regmap, REG_USER4, USER4_CS_ACT,
22362306a36Sopenharmony_ci			   keep_cs ? USER4_CS_ACT : 0);
22462306a36Sopenharmony_ci
22562306a36Sopenharmony_ci	/* clear transition done bit */
22662306a36Sopenharmony_ci	regmap_update_bits(spifc->regmap, REG_SLAVE, SLAVE_TRST_DONE, 0);
22762306a36Sopenharmony_ci	/* start transfer */
22862306a36Sopenharmony_ci	regmap_update_bits(spifc->regmap, REG_CMD, CMD_USER, CMD_USER);
22962306a36Sopenharmony_ci
23062306a36Sopenharmony_ci	ret = meson_spifc_wait_ready(spifc);
23162306a36Sopenharmony_ci
23262306a36Sopenharmony_ci	if (!ret && xfer->rx_buf)
23362306a36Sopenharmony_ci		meson_spifc_drain_buffer(spifc, xfer->rx_buf + offset, len);
23462306a36Sopenharmony_ci
23562306a36Sopenharmony_ci	return ret;
23662306a36Sopenharmony_ci}
23762306a36Sopenharmony_ci
23862306a36Sopenharmony_ci/**
23962306a36Sopenharmony_ci * meson_spifc_transfer_one() - perform a single transfer
24062306a36Sopenharmony_ci * @master:	the SPI master
24162306a36Sopenharmony_ci * @spi:	the SPI device
24262306a36Sopenharmony_ci * @xfer:	the current SPI transfer
24362306a36Sopenharmony_ci * Return:	0 on success, a negative value on error
24462306a36Sopenharmony_ci */
24562306a36Sopenharmony_cistatic int meson_spifc_transfer_one(struct spi_master *master,
24662306a36Sopenharmony_ci				    struct spi_device *spi,
24762306a36Sopenharmony_ci				    struct spi_transfer *xfer)
24862306a36Sopenharmony_ci{
24962306a36Sopenharmony_ci	struct meson_spifc *spifc = spi_master_get_devdata(master);
25062306a36Sopenharmony_ci	int len, done = 0, ret = 0;
25162306a36Sopenharmony_ci
25262306a36Sopenharmony_ci	meson_spifc_setup_speed(spifc, xfer->speed_hz);
25362306a36Sopenharmony_ci
25462306a36Sopenharmony_ci	regmap_update_bits(spifc->regmap, REG_CTRL, CTRL_ENABLE_AHB, 0);
25562306a36Sopenharmony_ci
25662306a36Sopenharmony_ci	while (done < xfer->len && !ret) {
25762306a36Sopenharmony_ci		len = min_t(int, xfer->len - done, SPIFC_BUFFER_SIZE);
25862306a36Sopenharmony_ci		ret = meson_spifc_txrx(spifc, xfer, done, len,
25962306a36Sopenharmony_ci				       spi_transfer_is_last(master, xfer),
26062306a36Sopenharmony_ci				       done + len >= xfer->len);
26162306a36Sopenharmony_ci		done += len;
26262306a36Sopenharmony_ci	}
26362306a36Sopenharmony_ci
26462306a36Sopenharmony_ci	regmap_update_bits(spifc->regmap, REG_CTRL, CTRL_ENABLE_AHB,
26562306a36Sopenharmony_ci			   CTRL_ENABLE_AHB);
26662306a36Sopenharmony_ci
26762306a36Sopenharmony_ci	return ret;
26862306a36Sopenharmony_ci}
26962306a36Sopenharmony_ci
27062306a36Sopenharmony_ci/**
27162306a36Sopenharmony_ci * meson_spifc_hw_init() - reset and initialize the SPI controller
27262306a36Sopenharmony_ci * @spifc:	the Meson SPI device
27362306a36Sopenharmony_ci */
27462306a36Sopenharmony_cistatic void meson_spifc_hw_init(struct meson_spifc *spifc)
27562306a36Sopenharmony_ci{
27662306a36Sopenharmony_ci	/* reset device */
27762306a36Sopenharmony_ci	regmap_update_bits(spifc->regmap, REG_SLAVE, SLAVE_SW_RST,
27862306a36Sopenharmony_ci			   SLAVE_SW_RST);
27962306a36Sopenharmony_ci	/* disable compatible mode */
28062306a36Sopenharmony_ci	regmap_update_bits(spifc->regmap, REG_USER, USER_CMP_MODE, 0);
28162306a36Sopenharmony_ci	/* set master mode */
28262306a36Sopenharmony_ci	regmap_update_bits(spifc->regmap, REG_SLAVE, SLAVE_OP_MODE, 0);
28362306a36Sopenharmony_ci}
28462306a36Sopenharmony_ci
28562306a36Sopenharmony_cistatic int meson_spifc_probe(struct platform_device *pdev)
28662306a36Sopenharmony_ci{
28762306a36Sopenharmony_ci	struct spi_master *master;
28862306a36Sopenharmony_ci	struct meson_spifc *spifc;
28962306a36Sopenharmony_ci	void __iomem *base;
29062306a36Sopenharmony_ci	unsigned int rate;
29162306a36Sopenharmony_ci	int ret = 0;
29262306a36Sopenharmony_ci
29362306a36Sopenharmony_ci	master = spi_alloc_master(&pdev->dev, sizeof(struct meson_spifc));
29462306a36Sopenharmony_ci	if (!master)
29562306a36Sopenharmony_ci		return -ENOMEM;
29662306a36Sopenharmony_ci
29762306a36Sopenharmony_ci	platform_set_drvdata(pdev, master);
29862306a36Sopenharmony_ci
29962306a36Sopenharmony_ci	spifc = spi_master_get_devdata(master);
30062306a36Sopenharmony_ci	spifc->dev = &pdev->dev;
30162306a36Sopenharmony_ci
30262306a36Sopenharmony_ci	base = devm_platform_ioremap_resource(pdev, 0);
30362306a36Sopenharmony_ci	if (IS_ERR(base)) {
30462306a36Sopenharmony_ci		ret = PTR_ERR(base);
30562306a36Sopenharmony_ci		goto out_err;
30662306a36Sopenharmony_ci	}
30762306a36Sopenharmony_ci
30862306a36Sopenharmony_ci	spifc->regmap = devm_regmap_init_mmio(spifc->dev, base,
30962306a36Sopenharmony_ci					      &spifc_regmap_config);
31062306a36Sopenharmony_ci	if (IS_ERR(spifc->regmap)) {
31162306a36Sopenharmony_ci		ret = PTR_ERR(spifc->regmap);
31262306a36Sopenharmony_ci		goto out_err;
31362306a36Sopenharmony_ci	}
31462306a36Sopenharmony_ci
31562306a36Sopenharmony_ci	spifc->clk = devm_clk_get(spifc->dev, NULL);
31662306a36Sopenharmony_ci	if (IS_ERR(spifc->clk)) {
31762306a36Sopenharmony_ci		dev_err(spifc->dev, "missing clock\n");
31862306a36Sopenharmony_ci		ret = PTR_ERR(spifc->clk);
31962306a36Sopenharmony_ci		goto out_err;
32062306a36Sopenharmony_ci	}
32162306a36Sopenharmony_ci
32262306a36Sopenharmony_ci	ret = clk_prepare_enable(spifc->clk);
32362306a36Sopenharmony_ci	if (ret) {
32462306a36Sopenharmony_ci		dev_err(spifc->dev, "can't prepare clock\n");
32562306a36Sopenharmony_ci		goto out_err;
32662306a36Sopenharmony_ci	}
32762306a36Sopenharmony_ci
32862306a36Sopenharmony_ci	rate = clk_get_rate(spifc->clk);
32962306a36Sopenharmony_ci
33062306a36Sopenharmony_ci	master->num_chipselect = 1;
33162306a36Sopenharmony_ci	master->dev.of_node = pdev->dev.of_node;
33262306a36Sopenharmony_ci	master->bits_per_word_mask = SPI_BPW_MASK(8);
33362306a36Sopenharmony_ci	master->auto_runtime_pm = true;
33462306a36Sopenharmony_ci	master->transfer_one = meson_spifc_transfer_one;
33562306a36Sopenharmony_ci	master->min_speed_hz = rate >> 6;
33662306a36Sopenharmony_ci	master->max_speed_hz = rate >> 1;
33762306a36Sopenharmony_ci
33862306a36Sopenharmony_ci	meson_spifc_hw_init(spifc);
33962306a36Sopenharmony_ci
34062306a36Sopenharmony_ci	pm_runtime_set_active(spifc->dev);
34162306a36Sopenharmony_ci	pm_runtime_enable(spifc->dev);
34262306a36Sopenharmony_ci
34362306a36Sopenharmony_ci	ret = devm_spi_register_master(spifc->dev, master);
34462306a36Sopenharmony_ci	if (ret) {
34562306a36Sopenharmony_ci		dev_err(spifc->dev, "failed to register spi master\n");
34662306a36Sopenharmony_ci		goto out_clk;
34762306a36Sopenharmony_ci	}
34862306a36Sopenharmony_ci
34962306a36Sopenharmony_ci	return 0;
35062306a36Sopenharmony_ciout_clk:
35162306a36Sopenharmony_ci	clk_disable_unprepare(spifc->clk);
35262306a36Sopenharmony_ci	pm_runtime_disable(spifc->dev);
35362306a36Sopenharmony_ciout_err:
35462306a36Sopenharmony_ci	spi_master_put(master);
35562306a36Sopenharmony_ci	return ret;
35662306a36Sopenharmony_ci}
35762306a36Sopenharmony_ci
35862306a36Sopenharmony_cistatic void meson_spifc_remove(struct platform_device *pdev)
35962306a36Sopenharmony_ci{
36062306a36Sopenharmony_ci	struct spi_master *master = platform_get_drvdata(pdev);
36162306a36Sopenharmony_ci	struct meson_spifc *spifc = spi_master_get_devdata(master);
36262306a36Sopenharmony_ci
36362306a36Sopenharmony_ci	pm_runtime_get_sync(&pdev->dev);
36462306a36Sopenharmony_ci	clk_disable_unprepare(spifc->clk);
36562306a36Sopenharmony_ci	pm_runtime_disable(&pdev->dev);
36662306a36Sopenharmony_ci}
36762306a36Sopenharmony_ci
36862306a36Sopenharmony_ci#ifdef CONFIG_PM_SLEEP
36962306a36Sopenharmony_cistatic int meson_spifc_suspend(struct device *dev)
37062306a36Sopenharmony_ci{
37162306a36Sopenharmony_ci	struct spi_master *master = dev_get_drvdata(dev);
37262306a36Sopenharmony_ci	struct meson_spifc *spifc = spi_master_get_devdata(master);
37362306a36Sopenharmony_ci	int ret;
37462306a36Sopenharmony_ci
37562306a36Sopenharmony_ci	ret = spi_master_suspend(master);
37662306a36Sopenharmony_ci	if (ret)
37762306a36Sopenharmony_ci		return ret;
37862306a36Sopenharmony_ci
37962306a36Sopenharmony_ci	if (!pm_runtime_suspended(dev))
38062306a36Sopenharmony_ci		clk_disable_unprepare(spifc->clk);
38162306a36Sopenharmony_ci
38262306a36Sopenharmony_ci	return 0;
38362306a36Sopenharmony_ci}
38462306a36Sopenharmony_ci
38562306a36Sopenharmony_cistatic int meson_spifc_resume(struct device *dev)
38662306a36Sopenharmony_ci{
38762306a36Sopenharmony_ci	struct spi_master *master = dev_get_drvdata(dev);
38862306a36Sopenharmony_ci	struct meson_spifc *spifc = spi_master_get_devdata(master);
38962306a36Sopenharmony_ci	int ret;
39062306a36Sopenharmony_ci
39162306a36Sopenharmony_ci	if (!pm_runtime_suspended(dev)) {
39262306a36Sopenharmony_ci		ret = clk_prepare_enable(spifc->clk);
39362306a36Sopenharmony_ci		if (ret)
39462306a36Sopenharmony_ci			return ret;
39562306a36Sopenharmony_ci	}
39662306a36Sopenharmony_ci
39762306a36Sopenharmony_ci	meson_spifc_hw_init(spifc);
39862306a36Sopenharmony_ci
39962306a36Sopenharmony_ci	ret = spi_master_resume(master);
40062306a36Sopenharmony_ci	if (ret)
40162306a36Sopenharmony_ci		clk_disable_unprepare(spifc->clk);
40262306a36Sopenharmony_ci
40362306a36Sopenharmony_ci	return ret;
40462306a36Sopenharmony_ci}
40562306a36Sopenharmony_ci#endif /* CONFIG_PM_SLEEP */
40662306a36Sopenharmony_ci
40762306a36Sopenharmony_ci#ifdef CONFIG_PM
40862306a36Sopenharmony_cistatic int meson_spifc_runtime_suspend(struct device *dev)
40962306a36Sopenharmony_ci{
41062306a36Sopenharmony_ci	struct spi_master *master = dev_get_drvdata(dev);
41162306a36Sopenharmony_ci	struct meson_spifc *spifc = spi_master_get_devdata(master);
41262306a36Sopenharmony_ci
41362306a36Sopenharmony_ci	clk_disable_unprepare(spifc->clk);
41462306a36Sopenharmony_ci
41562306a36Sopenharmony_ci	return 0;
41662306a36Sopenharmony_ci}
41762306a36Sopenharmony_ci
41862306a36Sopenharmony_cistatic int meson_spifc_runtime_resume(struct device *dev)
41962306a36Sopenharmony_ci{
42062306a36Sopenharmony_ci	struct spi_master *master = dev_get_drvdata(dev);
42162306a36Sopenharmony_ci	struct meson_spifc *spifc = spi_master_get_devdata(master);
42262306a36Sopenharmony_ci
42362306a36Sopenharmony_ci	return clk_prepare_enable(spifc->clk);
42462306a36Sopenharmony_ci}
42562306a36Sopenharmony_ci#endif /* CONFIG_PM */
42662306a36Sopenharmony_ci
42762306a36Sopenharmony_cistatic const struct dev_pm_ops meson_spifc_pm_ops = {
42862306a36Sopenharmony_ci	SET_SYSTEM_SLEEP_PM_OPS(meson_spifc_suspend, meson_spifc_resume)
42962306a36Sopenharmony_ci	SET_RUNTIME_PM_OPS(meson_spifc_runtime_suspend,
43062306a36Sopenharmony_ci			   meson_spifc_runtime_resume,
43162306a36Sopenharmony_ci			   NULL)
43262306a36Sopenharmony_ci};
43362306a36Sopenharmony_ci
43462306a36Sopenharmony_cistatic const struct of_device_id meson_spifc_dt_match[] = {
43562306a36Sopenharmony_ci	{ .compatible = "amlogic,meson6-spifc", },
43662306a36Sopenharmony_ci	{ .compatible = "amlogic,meson-gxbb-spifc", },
43762306a36Sopenharmony_ci	{ },
43862306a36Sopenharmony_ci};
43962306a36Sopenharmony_ciMODULE_DEVICE_TABLE(of, meson_spifc_dt_match);
44062306a36Sopenharmony_ci
44162306a36Sopenharmony_cistatic struct platform_driver meson_spifc_driver = {
44262306a36Sopenharmony_ci	.probe	= meson_spifc_probe,
44362306a36Sopenharmony_ci	.remove_new = meson_spifc_remove,
44462306a36Sopenharmony_ci	.driver	= {
44562306a36Sopenharmony_ci		.name		= "meson-spifc",
44662306a36Sopenharmony_ci		.of_match_table	= of_match_ptr(meson_spifc_dt_match),
44762306a36Sopenharmony_ci		.pm		= &meson_spifc_pm_ops,
44862306a36Sopenharmony_ci	},
44962306a36Sopenharmony_ci};
45062306a36Sopenharmony_ci
45162306a36Sopenharmony_cimodule_platform_driver(meson_spifc_driver);
45262306a36Sopenharmony_ci
45362306a36Sopenharmony_ciMODULE_AUTHOR("Beniamino Galvani <b.galvani@gmail.com>");
45462306a36Sopenharmony_ciMODULE_DESCRIPTION("Amlogic Meson SPIFC driver");
45562306a36Sopenharmony_ciMODULE_LICENSE("GPL v2");
456