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