Commit | Line | Data |
---|---|---|
a1867f85 ML |
1 | // SPDX-License-Identifier: GPL-2.0+ |
2 | /* | |
3 | * SPI driver for Renesas Synchronization Management Unit (SMU) devices. | |
4 | * | |
5 | * Copyright (C) 2021 Integrated Device Technology, Inc., a Renesas Company. | |
6 | */ | |
7 | ||
8 | #include <linux/init.h> | |
9 | #include <linux/kernel.h> | |
10 | #include <linux/mfd/core.h> | |
11 | #include <linux/mfd/rsmu.h> | |
12 | #include <linux/module.h> | |
13 | #include <linux/of.h> | |
14 | #include <linux/regmap.h> | |
15 | #include <linux/slab.h> | |
16 | #include <linux/spi/spi.h> | |
17 | ||
18 | #include "rsmu.h" | |
19 | ||
20 | #define RSMU_CM_PAGE_ADDR 0x7C | |
21 | #define RSMU_SABRE_PAGE_ADDR 0x7F | |
22 | #define RSMU_HIGHER_ADDR_MASK 0xFF80 | |
23 | #define RSMU_HIGHER_ADDR_SHIFT 7 | |
24 | #define RSMU_LOWER_ADDR_MASK 0x7F | |
25 | ||
26 | static int rsmu_read_device(struct rsmu_ddata *rsmu, u8 reg, u8 *buf, u16 bytes) | |
27 | { | |
28 | struct spi_device *client = to_spi_device(rsmu->dev); | |
29 | struct spi_transfer xfer = {0}; | |
30 | struct spi_message msg; | |
31 | u8 cmd[256] = {0}; | |
32 | u8 rsp[256] = {0}; | |
33 | int ret; | |
34 | ||
35 | cmd[0] = reg | 0x80; | |
36 | xfer.rx_buf = rsp; | |
37 | xfer.len = bytes + 1; | |
38 | xfer.tx_buf = cmd; | |
39 | xfer.bits_per_word = client->bits_per_word; | |
40 | xfer.speed_hz = client->max_speed_hz; | |
41 | ||
42 | spi_message_init(&msg); | |
43 | spi_message_add_tail(&xfer, &msg); | |
44 | ||
45 | /* | |
46 | * 4-wire SPI is a shift register, so for every byte you send, | |
47 | * you get one back at the same time. Example read from 0xC024, | |
48 | * which has value of 0x2D | |
49 | * | |
50 | * MOSI: | |
51 | * 7C 00 C0 #Set page register | |
52 | * A4 00 #MSB is set, so this is read command | |
53 | * MISO: | |
54 | * XX 2D #XX is a dummy byte from sending A4 and we | |
55 | * need to throw it away | |
56 | */ | |
57 | ret = spi_sync(client, &msg); | |
58 | if (ret >= 0) | |
59 | memcpy(buf, &rsp[1], xfer.len-1); | |
60 | ||
61 | return ret; | |
62 | } | |
63 | ||
64 | static int rsmu_write_device(struct rsmu_ddata *rsmu, u8 reg, u8 *buf, u16 bytes) | |
65 | { | |
66 | struct spi_device *client = to_spi_device(rsmu->dev); | |
67 | struct spi_transfer xfer = {0}; | |
68 | struct spi_message msg; | |
69 | u8 cmd[256] = {0}; | |
70 | ||
71 | cmd[0] = reg; | |
72 | memcpy(&cmd[1], buf, bytes); | |
73 | ||
74 | xfer.len = bytes + 1; | |
75 | xfer.tx_buf = cmd; | |
76 | xfer.bits_per_word = client->bits_per_word; | |
77 | xfer.speed_hz = client->max_speed_hz; | |
78 | spi_message_init(&msg); | |
79 | spi_message_add_tail(&xfer, &msg); | |
80 | ||
81 | return spi_sync(client, &msg); | |
82 | } | |
83 | ||
84 | /* | |
85 | * 1-byte (1B) offset addressing: | |
86 | * 16-bit register address: the lower 7 bits of the register address come | |
87 | * from the offset addr byte and the upper 9 bits come from the page register. | |
88 | */ | |
89 | static int rsmu_write_page_register(struct rsmu_ddata *rsmu, u16 reg) | |
90 | { | |
91 | u8 page_reg; | |
92 | u8 buf[2]; | |
93 | u16 bytes; | |
94 | u16 page; | |
95 | int err; | |
96 | ||
97 | switch (rsmu->type) { | |
98 | case RSMU_CM: | |
99 | page_reg = RSMU_CM_PAGE_ADDR; | |
100 | page = reg & RSMU_HIGHER_ADDR_MASK; | |
101 | buf[0] = (u8)(page & 0xff); | |
102 | buf[1] = (u8)((page >> 8) & 0xff); | |
103 | bytes = 2; | |
104 | break; | |
105 | case RSMU_SABRE: | |
106 | page_reg = RSMU_SABRE_PAGE_ADDR; | |
107 | page = reg >> RSMU_HIGHER_ADDR_SHIFT; | |
108 | buf[0] = (u8)(page & 0xff); | |
109 | bytes = 1; | |
110 | break; | |
111 | default: | |
112 | dev_err(rsmu->dev, "Unsupported RSMU device type: %d\n", rsmu->type); | |
113 | return -ENODEV; | |
114 | } | |
115 | ||
116 | /* Simply return if we are on the same page */ | |
117 | if (rsmu->page == page) | |
118 | return 0; | |
119 | ||
120 | err = rsmu_write_device(rsmu, page_reg, buf, bytes); | |
121 | if (err) | |
122 | dev_err(rsmu->dev, "Failed to set page offset 0x%x\n", page); | |
123 | else | |
124 | /* Remember the last page */ | |
125 | rsmu->page = page; | |
126 | ||
127 | return err; | |
128 | } | |
129 | ||
130 | static int rsmu_reg_read(void *context, unsigned int reg, unsigned int *val) | |
131 | { | |
132 | struct rsmu_ddata *rsmu = spi_get_drvdata((struct spi_device *)context); | |
133 | u8 addr = (u8)(reg & RSMU_LOWER_ADDR_MASK); | |
134 | int err; | |
135 | ||
136 | err = rsmu_write_page_register(rsmu, reg); | |
137 | if (err) | |
138 | return err; | |
139 | ||
140 | err = rsmu_read_device(rsmu, addr, (u8 *)val, 1); | |
141 | if (err) | |
142 | dev_err(rsmu->dev, "Failed to read offset address 0x%x\n", addr); | |
143 | ||
144 | return err; | |
145 | } | |
146 | ||
147 | static int rsmu_reg_write(void *context, unsigned int reg, unsigned int val) | |
148 | { | |
149 | struct rsmu_ddata *rsmu = spi_get_drvdata((struct spi_device *)context); | |
150 | u8 addr = (u8)(reg & RSMU_LOWER_ADDR_MASK); | |
151 | u8 data = (u8)val; | |
152 | int err; | |
153 | ||
154 | err = rsmu_write_page_register(rsmu, reg); | |
155 | if (err) | |
156 | return err; | |
157 | ||
158 | err = rsmu_write_device(rsmu, addr, &data, 1); | |
159 | if (err) | |
160 | dev_err(rsmu->dev, | |
161 | "Failed to write offset address 0x%x\n", addr); | |
162 | ||
163 | return err; | |
164 | } | |
165 | ||
166 | static const struct regmap_config rsmu_cm_regmap_config = { | |
167 | .reg_bits = 16, | |
168 | .val_bits = 8, | |
169 | .max_register = 0xD000, | |
170 | .reg_read = rsmu_reg_read, | |
171 | .reg_write = rsmu_reg_write, | |
172 | .cache_type = REGCACHE_NONE, | |
173 | }; | |
174 | ||
175 | static const struct regmap_config rsmu_sabre_regmap_config = { | |
176 | .reg_bits = 16, | |
177 | .val_bits = 8, | |
178 | .max_register = 0x400, | |
179 | .reg_read = rsmu_reg_read, | |
180 | .reg_write = rsmu_reg_write, | |
181 | .cache_type = REGCACHE_NONE, | |
182 | }; | |
183 | ||
184 | static int rsmu_spi_probe(struct spi_device *client) | |
185 | { | |
186 | const struct spi_device_id *id = spi_get_device_id(client); | |
187 | const struct regmap_config *cfg; | |
188 | struct rsmu_ddata *rsmu; | |
189 | int ret; | |
190 | ||
191 | rsmu = devm_kzalloc(&client->dev, sizeof(*rsmu), GFP_KERNEL); | |
192 | if (!rsmu) | |
193 | return -ENOMEM; | |
194 | ||
195 | spi_set_drvdata(client, rsmu); | |
196 | ||
197 | rsmu->dev = &client->dev; | |
198 | rsmu->type = (enum rsmu_type)id->driver_data; | |
199 | ||
200 | /* Initialize regmap */ | |
201 | switch (rsmu->type) { | |
202 | case RSMU_CM: | |
203 | cfg = &rsmu_cm_regmap_config; | |
204 | break; | |
205 | case RSMU_SABRE: | |
206 | cfg = &rsmu_sabre_regmap_config; | |
207 | break; | |
208 | default: | |
209 | dev_err(rsmu->dev, "Unsupported RSMU device type: %d\n", rsmu->type); | |
210 | return -ENODEV; | |
211 | } | |
212 | ||
213 | rsmu->regmap = devm_regmap_init(&client->dev, NULL, client, cfg); | |
214 | if (IS_ERR(rsmu->regmap)) { | |
215 | ret = PTR_ERR(rsmu->regmap); | |
216 | dev_err(rsmu->dev, "Failed to allocate register map: %d\n", ret); | |
217 | return ret; | |
218 | } | |
219 | ||
220 | return rsmu_core_init(rsmu); | |
221 | } | |
222 | ||
a0386bba | 223 | static void rsmu_spi_remove(struct spi_device *client) |
a1867f85 ML |
224 | { |
225 | struct rsmu_ddata *rsmu = spi_get_drvdata(client); | |
226 | ||
227 | rsmu_core_exit(rsmu); | |
a1867f85 ML |
228 | } |
229 | ||
230 | static const struct spi_device_id rsmu_spi_id[] = { | |
231 | { "8a34000", RSMU_CM }, | |
232 | { "8a34001", RSMU_CM }, | |
233 | { "82p33810", RSMU_SABRE }, | |
234 | { "82p33811", RSMU_SABRE }, | |
235 | {} | |
236 | }; | |
237 | MODULE_DEVICE_TABLE(spi, rsmu_spi_id); | |
238 | ||
239 | static const struct of_device_id rsmu_spi_of_match[] = { | |
240 | { .compatible = "idt,8a34000", .data = (void *)RSMU_CM }, | |
241 | { .compatible = "idt,8a34001", .data = (void *)RSMU_CM }, | |
242 | { .compatible = "idt,82p33810", .data = (void *)RSMU_SABRE }, | |
243 | { .compatible = "idt,82p33811", .data = (void *)RSMU_SABRE }, | |
244 | {} | |
245 | }; | |
246 | MODULE_DEVICE_TABLE(of, rsmu_spi_of_match); | |
247 | ||
248 | static struct spi_driver rsmu_spi_driver = { | |
249 | .driver = { | |
250 | .name = "rsmu-spi", | |
251 | .of_match_table = of_match_ptr(rsmu_spi_of_match), | |
252 | }, | |
253 | .probe = rsmu_spi_probe, | |
254 | .remove = rsmu_spi_remove, | |
255 | .id_table = rsmu_spi_id, | |
256 | }; | |
257 | ||
258 | static int __init rsmu_spi_init(void) | |
259 | { | |
260 | return spi_register_driver(&rsmu_spi_driver); | |
261 | } | |
262 | subsys_initcall(rsmu_spi_init); | |
263 | ||
264 | static void __exit rsmu_spi_exit(void) | |
265 | { | |
266 | spi_unregister_driver(&rsmu_spi_driver); | |
267 | } | |
268 | module_exit(rsmu_spi_exit); | |
269 | ||
270 | MODULE_DESCRIPTION("Renesas SMU SPI driver"); | |
271 | MODULE_LICENSE("GPL"); |