1*210f418fSSebastian Reichel // SPDX-License-Identifier: GPL-2.0
2*210f418fSSebastian Reichel /*
3*210f418fSSebastian Reichel * Rockchip RK806 Core (SPI) driver
4*210f418fSSebastian Reichel *
5*210f418fSSebastian Reichel * Copyright (c) 2021 Rockchip Electronics Co., Ltd.
6*210f418fSSebastian Reichel * Copyright (c) 2023 Collabora Ltd.
7*210f418fSSebastian Reichel *
8*210f418fSSebastian Reichel * Author: Xu Shengfei <xsf@rock-chips.com>
9*210f418fSSebastian Reichel * Author: Sebastian Reichel <sebastian.reichel@collabora.com>
10*210f418fSSebastian Reichel */
11*210f418fSSebastian Reichel
12*210f418fSSebastian Reichel #include <linux/interrupt.h>
13*210f418fSSebastian Reichel #include <linux/mfd/core.h>
14*210f418fSSebastian Reichel #include <linux/mfd/rk808.h>
15*210f418fSSebastian Reichel #include <linux/module.h>
16*210f418fSSebastian Reichel #include <linux/regmap.h>
17*210f418fSSebastian Reichel #include <linux/spi/spi.h>
18*210f418fSSebastian Reichel
19*210f418fSSebastian Reichel #define RK806_ADDR_SIZE 2
20*210f418fSSebastian Reichel #define RK806_CMD_WITH_SIZE(CMD, VALUE_BYTES) \
21*210f418fSSebastian Reichel (RK806_CMD_##CMD | RK806_CMD_CRC_DIS | (VALUE_BYTES - 1))
22*210f418fSSebastian Reichel
23*210f418fSSebastian Reichel static const struct regmap_range rk806_volatile_ranges[] = {
24*210f418fSSebastian Reichel regmap_reg_range(RK806_POWER_EN0, RK806_POWER_EN5),
25*210f418fSSebastian Reichel regmap_reg_range(RK806_DVS_START_CTRL, RK806_INT_MSK1),
26*210f418fSSebastian Reichel };
27*210f418fSSebastian Reichel
28*210f418fSSebastian Reichel static const struct regmap_access_table rk806_volatile_table = {
29*210f418fSSebastian Reichel .yes_ranges = rk806_volatile_ranges,
30*210f418fSSebastian Reichel .n_yes_ranges = ARRAY_SIZE(rk806_volatile_ranges),
31*210f418fSSebastian Reichel };
32*210f418fSSebastian Reichel
33*210f418fSSebastian Reichel static const struct regmap_config rk806_regmap_config_spi = {
34*210f418fSSebastian Reichel .reg_bits = 16,
35*210f418fSSebastian Reichel .val_bits = 8,
36*210f418fSSebastian Reichel .max_register = RK806_BUCK_RSERVE_REG5,
37*210f418fSSebastian Reichel .cache_type = REGCACHE_RBTREE,
38*210f418fSSebastian Reichel .volatile_table = &rk806_volatile_table,
39*210f418fSSebastian Reichel };
40*210f418fSSebastian Reichel
rk806_spi_bus_write(void * context,const void * vdata,size_t count)41*210f418fSSebastian Reichel static int rk806_spi_bus_write(void *context, const void *vdata, size_t count)
42*210f418fSSebastian Reichel {
43*210f418fSSebastian Reichel struct device *dev = context;
44*210f418fSSebastian Reichel struct spi_device *spi = to_spi_device(dev);
45*210f418fSSebastian Reichel struct spi_transfer xfer[2] = { 0 };
46*210f418fSSebastian Reichel /* data and thus count includes the register address */
47*210f418fSSebastian Reichel size_t val_size = count - RK806_ADDR_SIZE;
48*210f418fSSebastian Reichel char cmd;
49*210f418fSSebastian Reichel
50*210f418fSSebastian Reichel if (val_size < 1 || val_size > (RK806_CMD_LEN_MSK + 1))
51*210f418fSSebastian Reichel return -EINVAL;
52*210f418fSSebastian Reichel
53*210f418fSSebastian Reichel cmd = RK806_CMD_WITH_SIZE(WRITE, val_size);
54*210f418fSSebastian Reichel
55*210f418fSSebastian Reichel xfer[0].tx_buf = &cmd;
56*210f418fSSebastian Reichel xfer[0].len = sizeof(cmd);
57*210f418fSSebastian Reichel xfer[1].tx_buf = vdata;
58*210f418fSSebastian Reichel xfer[1].len = count;
59*210f418fSSebastian Reichel
60*210f418fSSebastian Reichel return spi_sync_transfer(spi, xfer, ARRAY_SIZE(xfer));
61*210f418fSSebastian Reichel }
62*210f418fSSebastian Reichel
rk806_spi_bus_read(void * context,const void * vreg,size_t reg_size,void * val,size_t val_size)63*210f418fSSebastian Reichel static int rk806_spi_bus_read(void *context, const void *vreg, size_t reg_size,
64*210f418fSSebastian Reichel void *val, size_t val_size)
65*210f418fSSebastian Reichel {
66*210f418fSSebastian Reichel struct device *dev = context;
67*210f418fSSebastian Reichel struct spi_device *spi = to_spi_device(dev);
68*210f418fSSebastian Reichel char txbuf[3] = { 0 };
69*210f418fSSebastian Reichel
70*210f418fSSebastian Reichel if (reg_size != RK806_ADDR_SIZE ||
71*210f418fSSebastian Reichel val_size < 1 || val_size > (RK806_CMD_LEN_MSK + 1))
72*210f418fSSebastian Reichel return -EINVAL;
73*210f418fSSebastian Reichel
74*210f418fSSebastian Reichel /* TX buffer contains command byte followed by two address bytes */
75*210f418fSSebastian Reichel txbuf[0] = RK806_CMD_WITH_SIZE(READ, val_size);
76*210f418fSSebastian Reichel memcpy(txbuf+1, vreg, reg_size);
77*210f418fSSebastian Reichel
78*210f418fSSebastian Reichel return spi_write_then_read(spi, txbuf, sizeof(txbuf), val, val_size);
79*210f418fSSebastian Reichel }
80*210f418fSSebastian Reichel
81*210f418fSSebastian Reichel static const struct regmap_bus rk806_regmap_bus_spi = {
82*210f418fSSebastian Reichel .write = rk806_spi_bus_write,
83*210f418fSSebastian Reichel .read = rk806_spi_bus_read,
84*210f418fSSebastian Reichel .reg_format_endian_default = REGMAP_ENDIAN_LITTLE,
85*210f418fSSebastian Reichel };
86*210f418fSSebastian Reichel
rk8xx_spi_probe(struct spi_device * spi)87*210f418fSSebastian Reichel static int rk8xx_spi_probe(struct spi_device *spi)
88*210f418fSSebastian Reichel {
89*210f418fSSebastian Reichel struct regmap *regmap;
90*210f418fSSebastian Reichel
91*210f418fSSebastian Reichel regmap = devm_regmap_init(&spi->dev, &rk806_regmap_bus_spi,
92*210f418fSSebastian Reichel &spi->dev, &rk806_regmap_config_spi);
93*210f418fSSebastian Reichel if (IS_ERR(regmap))
94*210f418fSSebastian Reichel return dev_err_probe(&spi->dev, PTR_ERR(regmap),
95*210f418fSSebastian Reichel "Failed to init regmap\n");
96*210f418fSSebastian Reichel
97*210f418fSSebastian Reichel return rk8xx_probe(&spi->dev, RK806_ID, spi->irq, regmap);
98*210f418fSSebastian Reichel }
99*210f418fSSebastian Reichel
100*210f418fSSebastian Reichel static const struct of_device_id rk8xx_spi_of_match[] = {
101*210f418fSSebastian Reichel { .compatible = "rockchip,rk806", },
102*210f418fSSebastian Reichel { }
103*210f418fSSebastian Reichel };
104*210f418fSSebastian Reichel MODULE_DEVICE_TABLE(of, rk8xx_spi_of_match);
105*210f418fSSebastian Reichel
106*210f418fSSebastian Reichel static const struct spi_device_id rk8xx_spi_id_table[] = {
107*210f418fSSebastian Reichel { "rk806", 0 },
108*210f418fSSebastian Reichel { }
109*210f418fSSebastian Reichel };
110*210f418fSSebastian Reichel MODULE_DEVICE_TABLE(spi, rk8xx_spi_id_table);
111*210f418fSSebastian Reichel
112*210f418fSSebastian Reichel static struct spi_driver rk8xx_spi_driver = {
113*210f418fSSebastian Reichel .driver = {
114*210f418fSSebastian Reichel .name = "rk8xx-spi",
115*210f418fSSebastian Reichel .of_match_table = rk8xx_spi_of_match,
116*210f418fSSebastian Reichel },
117*210f418fSSebastian Reichel .probe = rk8xx_spi_probe,
118*210f418fSSebastian Reichel .id_table = rk8xx_spi_id_table,
119*210f418fSSebastian Reichel };
120*210f418fSSebastian Reichel module_spi_driver(rk8xx_spi_driver);
121*210f418fSSebastian Reichel
122*210f418fSSebastian Reichel MODULE_AUTHOR("Xu Shengfei <xsf@rock-chips.com>");
123*210f418fSSebastian Reichel MODULE_DESCRIPTION("RK8xx SPI PMIC driver");
124*210f418fSSebastian Reichel MODULE_LICENSE("GPL");
125