1 | // SPDX-License-Identifier: GPL-2.0 |
2 | /* |
3 | * Copyright (C) 2021 Emil Renner Berthing <kernel@esmil.dk> |
4 | * Copyright (C) 2021 Samin Guo <samin.guo@starfivetech.com> |
5 | */ |
6 | |
7 | #include <linux/bits.h> |
8 | #include <linux/clk.h> |
9 | #include <linux/delay.h> |
10 | #include <linux/hwmon.h> |
11 | #include <linux/io.h> |
12 | #include <linux/module.h> |
13 | #include <linux/mutex.h> |
14 | #include <linux/of.h> |
15 | #include <linux/platform_device.h> |
16 | #include <linux/reset.h> |
17 | |
18 | /* |
19 | * TempSensor reset. The RSTN can be de-asserted once the analog core has |
20 | * powered up. Trst(min 100ns) |
21 | * 0:reset 1:de-assert |
22 | */ |
23 | #define SFCTEMP_RSTN BIT(0) |
24 | |
25 | /* |
26 | * TempSensor analog core power down. The analog core will be powered up |
27 | * Tpu(min 50us) after PD is de-asserted. RSTN should be held low until the |
28 | * analog core is powered up. |
29 | * 0:power up 1:power down |
30 | */ |
31 | #define SFCTEMP_PD BIT(1) |
32 | |
33 | /* |
34 | * TempSensor start conversion enable. |
35 | * 0:disable 1:enable |
36 | */ |
37 | #define SFCTEMP_RUN BIT(2) |
38 | |
39 | /* |
40 | * TempSensor conversion value output. |
41 | * Temp(C)=DOUT*Y/4094 - K |
42 | */ |
43 | #define SFCTEMP_DOUT_POS 16 |
44 | #define SFCTEMP_DOUT_MSK GENMASK(27, 16) |
45 | |
46 | /* DOUT to Celcius conversion constants */ |
47 | #define SFCTEMP_Y1000 237500L |
48 | #define SFCTEMP_Z 4094L |
49 | #define SFCTEMP_K1000 81100L |
50 | |
51 | struct sfctemp { |
52 | /* serialize access to hardware register and enabled below */ |
53 | struct mutex lock; |
54 | void __iomem *regs; |
55 | struct clk *clk_sense; |
56 | struct clk *clk_bus; |
57 | struct reset_control *rst_sense; |
58 | struct reset_control *rst_bus; |
59 | bool enabled; |
60 | }; |
61 | |
62 | static void sfctemp_power_up(struct sfctemp *sfctemp) |
63 | { |
64 | /* make sure we're powered down first */ |
65 | writel(SFCTEMP_PD, addr: sfctemp->regs); |
66 | udelay(1); |
67 | |
68 | writel(val: 0, addr: sfctemp->regs); |
69 | /* wait t_pu(50us) + t_rst(100ns) */ |
70 | usleep_range(min: 60, max: 200); |
71 | |
72 | /* de-assert reset */ |
73 | writel(SFCTEMP_RSTN, addr: sfctemp->regs); |
74 | udelay(1); /* wait t_su(500ps) */ |
75 | } |
76 | |
77 | static void sfctemp_power_down(struct sfctemp *sfctemp) |
78 | { |
79 | writel(SFCTEMP_PD, addr: sfctemp->regs); |
80 | } |
81 | |
82 | static void sfctemp_run(struct sfctemp *sfctemp) |
83 | { |
84 | writel(SFCTEMP_RSTN | SFCTEMP_RUN, addr: sfctemp->regs); |
85 | udelay(1); |
86 | } |
87 | |
88 | static void sfctemp_stop(struct sfctemp *sfctemp) |
89 | { |
90 | writel(SFCTEMP_RSTN, addr: sfctemp->regs); |
91 | } |
92 | |
93 | static int sfctemp_enable(struct sfctemp *sfctemp) |
94 | { |
95 | int ret = 0; |
96 | |
97 | mutex_lock(&sfctemp->lock); |
98 | if (sfctemp->enabled) |
99 | goto done; |
100 | |
101 | ret = clk_prepare_enable(clk: sfctemp->clk_bus); |
102 | if (ret) |
103 | goto err; |
104 | ret = reset_control_deassert(rstc: sfctemp->rst_bus); |
105 | if (ret) |
106 | goto err_disable_bus; |
107 | |
108 | ret = clk_prepare_enable(clk: sfctemp->clk_sense); |
109 | if (ret) |
110 | goto err_assert_bus; |
111 | ret = reset_control_deassert(rstc: sfctemp->rst_sense); |
112 | if (ret) |
113 | goto err_disable_sense; |
114 | |
115 | sfctemp_power_up(sfctemp); |
116 | sfctemp_run(sfctemp); |
117 | sfctemp->enabled = true; |
118 | done: |
119 | mutex_unlock(lock: &sfctemp->lock); |
120 | return ret; |
121 | |
122 | err_disable_sense: |
123 | clk_disable_unprepare(clk: sfctemp->clk_sense); |
124 | err_assert_bus: |
125 | reset_control_assert(rstc: sfctemp->rst_bus); |
126 | err_disable_bus: |
127 | clk_disable_unprepare(clk: sfctemp->clk_bus); |
128 | err: |
129 | mutex_unlock(lock: &sfctemp->lock); |
130 | return ret; |
131 | } |
132 | |
133 | static int sfctemp_disable(struct sfctemp *sfctemp) |
134 | { |
135 | mutex_lock(&sfctemp->lock); |
136 | if (!sfctemp->enabled) |
137 | goto done; |
138 | |
139 | sfctemp_stop(sfctemp); |
140 | sfctemp_power_down(sfctemp); |
141 | reset_control_assert(rstc: sfctemp->rst_sense); |
142 | clk_disable_unprepare(clk: sfctemp->clk_sense); |
143 | reset_control_assert(rstc: sfctemp->rst_bus); |
144 | clk_disable_unprepare(clk: sfctemp->clk_bus); |
145 | sfctemp->enabled = false; |
146 | done: |
147 | mutex_unlock(lock: &sfctemp->lock); |
148 | return 0; |
149 | } |
150 | |
151 | static void sfctemp_disable_action(void *data) |
152 | { |
153 | sfctemp_disable(sfctemp: data); |
154 | } |
155 | |
156 | static int sfctemp_convert(struct sfctemp *sfctemp, long *val) |
157 | { |
158 | int ret; |
159 | |
160 | mutex_lock(&sfctemp->lock); |
161 | if (!sfctemp->enabled) { |
162 | ret = -ENODATA; |
163 | goto out; |
164 | } |
165 | |
166 | /* calculate temperature in milli Celcius */ |
167 | *val = (long)((readl(addr: sfctemp->regs) & SFCTEMP_DOUT_MSK) >> SFCTEMP_DOUT_POS) |
168 | * SFCTEMP_Y1000 / SFCTEMP_Z - SFCTEMP_K1000; |
169 | |
170 | ret = 0; |
171 | out: |
172 | mutex_unlock(lock: &sfctemp->lock); |
173 | return ret; |
174 | } |
175 | |
176 | static umode_t sfctemp_is_visible(const void *data, enum hwmon_sensor_types type, |
177 | u32 attr, int channel) |
178 | { |
179 | switch (type) { |
180 | case hwmon_temp: |
181 | switch (attr) { |
182 | case hwmon_temp_enable: |
183 | return 0644; |
184 | case hwmon_temp_input: |
185 | return 0444; |
186 | default: |
187 | return 0; |
188 | } |
189 | default: |
190 | return 0; |
191 | } |
192 | } |
193 | |
194 | static int sfctemp_read(struct device *dev, enum hwmon_sensor_types type, |
195 | u32 attr, int channel, long *val) |
196 | { |
197 | struct sfctemp *sfctemp = dev_get_drvdata(dev); |
198 | |
199 | switch (type) { |
200 | case hwmon_temp: |
201 | switch (attr) { |
202 | case hwmon_temp_enable: |
203 | *val = sfctemp->enabled; |
204 | return 0; |
205 | case hwmon_temp_input: |
206 | return sfctemp_convert(sfctemp, val); |
207 | default: |
208 | return -EINVAL; |
209 | } |
210 | default: |
211 | return -EINVAL; |
212 | } |
213 | } |
214 | |
215 | static int sfctemp_write(struct device *dev, enum hwmon_sensor_types type, |
216 | u32 attr, int channel, long val) |
217 | { |
218 | struct sfctemp *sfctemp = dev_get_drvdata(dev); |
219 | |
220 | switch (type) { |
221 | case hwmon_temp: |
222 | switch (attr) { |
223 | case hwmon_temp_enable: |
224 | if (val == 0) |
225 | return sfctemp_disable(sfctemp); |
226 | if (val == 1) |
227 | return sfctemp_enable(sfctemp); |
228 | return -EINVAL; |
229 | default: |
230 | return -EINVAL; |
231 | } |
232 | default: |
233 | return -EINVAL; |
234 | } |
235 | } |
236 | |
237 | static const struct hwmon_channel_info *sfctemp_info[] = { |
238 | HWMON_CHANNEL_INFO(chip, HWMON_C_REGISTER_TZ), |
239 | HWMON_CHANNEL_INFO(temp, HWMON_T_ENABLE | HWMON_T_INPUT), |
240 | NULL |
241 | }; |
242 | |
243 | static const struct hwmon_ops sfctemp_hwmon_ops = { |
244 | .is_visible = sfctemp_is_visible, |
245 | .read = sfctemp_read, |
246 | .write = sfctemp_write, |
247 | }; |
248 | |
249 | static const struct hwmon_chip_info sfctemp_chip_info = { |
250 | .ops = &sfctemp_hwmon_ops, |
251 | .info = sfctemp_info, |
252 | }; |
253 | |
254 | static int sfctemp_probe(struct platform_device *pdev) |
255 | { |
256 | struct device *dev = &pdev->dev; |
257 | struct device *hwmon_dev; |
258 | struct sfctemp *sfctemp; |
259 | int ret; |
260 | |
261 | sfctemp = devm_kzalloc(dev, size: sizeof(*sfctemp), GFP_KERNEL); |
262 | if (!sfctemp) |
263 | return -ENOMEM; |
264 | |
265 | dev_set_drvdata(dev, data: sfctemp); |
266 | mutex_init(&sfctemp->lock); |
267 | |
268 | sfctemp->regs = devm_platform_ioremap_resource(pdev, index: 0); |
269 | if (IS_ERR(ptr: sfctemp->regs)) |
270 | return PTR_ERR(ptr: sfctemp->regs); |
271 | |
272 | sfctemp->clk_sense = devm_clk_get(dev, id: "sense" ); |
273 | if (IS_ERR(ptr: sfctemp->clk_sense)) |
274 | return dev_err_probe(dev, err: PTR_ERR(ptr: sfctemp->clk_sense), |
275 | fmt: "error getting sense clock\n" ); |
276 | |
277 | sfctemp->clk_bus = devm_clk_get(dev, id: "bus" ); |
278 | if (IS_ERR(ptr: sfctemp->clk_bus)) |
279 | return dev_err_probe(dev, err: PTR_ERR(ptr: sfctemp->clk_bus), |
280 | fmt: "error getting bus clock\n" ); |
281 | |
282 | sfctemp->rst_sense = devm_reset_control_get_exclusive(dev, id: "sense" ); |
283 | if (IS_ERR(ptr: sfctemp->rst_sense)) |
284 | return dev_err_probe(dev, err: PTR_ERR(ptr: sfctemp->rst_sense), |
285 | fmt: "error getting sense reset\n" ); |
286 | |
287 | sfctemp->rst_bus = devm_reset_control_get_exclusive(dev, id: "bus" ); |
288 | if (IS_ERR(ptr: sfctemp->rst_bus)) |
289 | return dev_err_probe(dev, err: PTR_ERR(ptr: sfctemp->rst_bus), |
290 | fmt: "error getting busreset\n" ); |
291 | |
292 | ret = reset_control_assert(rstc: sfctemp->rst_sense); |
293 | if (ret) |
294 | return dev_err_probe(dev, err: ret, fmt: "error asserting sense reset\n" ); |
295 | |
296 | ret = reset_control_assert(rstc: sfctemp->rst_bus); |
297 | if (ret) |
298 | return dev_err_probe(dev, err: ret, fmt: "error asserting bus reset\n" ); |
299 | |
300 | ret = devm_add_action(dev, sfctemp_disable_action, sfctemp); |
301 | if (ret) |
302 | return ret; |
303 | |
304 | ret = sfctemp_enable(sfctemp); |
305 | if (ret) |
306 | return dev_err_probe(dev, err: ret, fmt: "error enabling temperature sensor\n" ); |
307 | |
308 | hwmon_dev = devm_hwmon_device_register_with_info(dev, name: "sfctemp" , drvdata: sfctemp, |
309 | info: &sfctemp_chip_info, NULL); |
310 | return PTR_ERR_OR_ZERO(ptr: hwmon_dev); |
311 | } |
312 | |
313 | static const struct of_device_id sfctemp_of_match[] = { |
314 | { .compatible = "starfive,jh7100-temp" }, |
315 | { .compatible = "starfive,jh7110-temp" }, |
316 | { /* sentinel */ } |
317 | }; |
318 | MODULE_DEVICE_TABLE(of, sfctemp_of_match); |
319 | |
320 | static struct platform_driver sfctemp_driver = { |
321 | .probe = sfctemp_probe, |
322 | .driver = { |
323 | .name = "sfctemp" , |
324 | .of_match_table = sfctemp_of_match, |
325 | }, |
326 | }; |
327 | module_platform_driver(sfctemp_driver); |
328 | |
329 | MODULE_AUTHOR("Emil Renner Berthing" ); |
330 | MODULE_DESCRIPTION("StarFive JH71x0 temperature sensor driver" ); |
331 | MODULE_LICENSE("GPL" ); |
332 | |