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