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