File Index
Files and directories
Sign in
FILE

sfctemp.c

File preview with syntax highlighting.

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