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
23static 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
28static 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
33static 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
41static 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
63static 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
81static 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
87static 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
100static const struct of_device_id rk8xx_spi_of_match[] = {
101 { .compatible = "rockchip,rk806", },
102 { }
103};
104MODULE_DEVICE_TABLE(of, rk8xx_spi_of_match);
105
106static const struct spi_device_id rk8xx_spi_id_table[] = {
107 { "rk806", 0 },
108 { }
109};
110MODULE_DEVICE_TABLE(spi, rk8xx_spi_id_table);
111
112static 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};
120module_spi_driver(rk8xx_spi_driver);
121
122MODULE_AUTHOR("Xu Shengfei <xsf@rock-chips.com>");
123MODULE_DESCRIPTION("RK8xx SPI PMIC driver");
124MODULE_LICENSE("GPL");
125

source code of linux/drivers/mfd/rk8xx-spi.c