brintos

brintos / linux-shallow public Read only

0
0
Text · 7.5 KiB · fb1da93 Raw
332 lines · c
1// SPDX-License-Identifier: GPL-2.02/*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 has20 * powered up. Trst(min 100ns)21 * 0:reset  1:de-assert22 */23#define SFCTEMP_RSTN	BIT(0)24 25/*26 * TempSensor analog core power down. The analog core will be powered up27 * Tpu(min 50us) after PD is de-asserted. RSTN should be held low until the28 * analog core is powered up.29 * 0:power up  1:power down30 */31#define SFCTEMP_PD	BIT(1)32 33/*34 * TempSensor start conversion enable.35 * 0:disable  1:enable36 */37#define SFCTEMP_RUN	BIT(2)38 39/*40 * TempSensor conversion value output.41 * Temp(C)=DOUT*Y/4094 - K42 */43#define SFCTEMP_DOUT_POS	1644#define SFCTEMP_DOUT_MSK	GENMASK(27, 16)45 46/* DOUT to Celcius conversion constants */47#define SFCTEMP_Y1000	237500L48#define SFCTEMP_Z	4094L49#define SFCTEMP_K1000	81100L50 51struct 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 62static void sfctemp_power_up(struct sfctemp *sfctemp)63{64	/* make sure we're powered down first */65	writel(SFCTEMP_PD, sfctemp->regs);66	udelay(1);67 68	writel(0, sfctemp->regs);69	/* wait t_pu(50us) + t_rst(100ns) */70	usleep_range(60, 200);71 72	/* de-assert reset */73	writel(SFCTEMP_RSTN, sfctemp->regs);74	udelay(1); /* wait t_su(500ps) */75}76 77static void sfctemp_power_down(struct sfctemp *sfctemp)78{79	writel(SFCTEMP_PD, sfctemp->regs);80}81 82static void sfctemp_run(struct sfctemp *sfctemp)83{84	writel(SFCTEMP_RSTN | SFCTEMP_RUN, sfctemp->regs);85	udelay(1);86}87 88static void sfctemp_stop(struct sfctemp *sfctemp)89{90	writel(SFCTEMP_RSTN, sfctemp->regs);91}92 93static 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(sfctemp->clk_bus);102	if (ret)103		goto err;104	ret = reset_control_deassert(sfctemp->rst_bus);105	if (ret)106		goto err_disable_bus;107 108	ret = clk_prepare_enable(sfctemp->clk_sense);109	if (ret)110		goto err_assert_bus;111	ret = reset_control_deassert(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;118done:119	mutex_unlock(&sfctemp->lock);120	return ret;121 122err_disable_sense:123	clk_disable_unprepare(sfctemp->clk_sense);124err_assert_bus:125	reset_control_assert(sfctemp->rst_bus);126err_disable_bus:127	clk_disable_unprepare(sfctemp->clk_bus);128err:129	mutex_unlock(&sfctemp->lock);130	return ret;131}132 133static 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(sfctemp->rst_sense);142	clk_disable_unprepare(sfctemp->clk_sense);143	reset_control_assert(sfctemp->rst_bus);144	clk_disable_unprepare(sfctemp->clk_bus);145	sfctemp->enabled = false;146done:147	mutex_unlock(&sfctemp->lock);148	return 0;149}150 151static void sfctemp_disable_action(void *data)152{153	sfctemp_disable(data);154}155 156static 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(sfctemp->regs) & SFCTEMP_DOUT_MSK) >> SFCTEMP_DOUT_POS)168		* SFCTEMP_Y1000 / SFCTEMP_Z - SFCTEMP_K1000;169 170	ret = 0;171out:172	mutex_unlock(&sfctemp->lock);173	return ret;174}175 176static 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 194static 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 215static 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 237static 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	NULL241};242 243static const struct hwmon_ops sfctemp_hwmon_ops = {244	.is_visible = sfctemp_is_visible,245	.read = sfctemp_read,246	.write = sfctemp_write,247};248 249static const struct hwmon_chip_info sfctemp_chip_info = {250	.ops = &sfctemp_hwmon_ops,251	.info = sfctemp_info,252};253 254static 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, sizeof(*sfctemp), GFP_KERNEL);262	if (!sfctemp)263		return -ENOMEM;264 265	dev_set_drvdata(dev, sfctemp);266	mutex_init(&sfctemp->lock);267 268	sfctemp->regs = devm_platform_ioremap_resource(pdev, 0);269	if (IS_ERR(sfctemp->regs))270		return PTR_ERR(sfctemp->regs);271 272	sfctemp->clk_sense = devm_clk_get(dev, "sense");273	if (IS_ERR(sfctemp->clk_sense))274		return dev_err_probe(dev, PTR_ERR(sfctemp->clk_sense),275				     "error getting sense clock\n");276 277	sfctemp->clk_bus = devm_clk_get(dev, "bus");278	if (IS_ERR(sfctemp->clk_bus))279		return dev_err_probe(dev, PTR_ERR(sfctemp->clk_bus),280				     "error getting bus clock\n");281 282	sfctemp->rst_sense = devm_reset_control_get_exclusive(dev, "sense");283	if (IS_ERR(sfctemp->rst_sense))284		return dev_err_probe(dev, PTR_ERR(sfctemp->rst_sense),285				     "error getting sense reset\n");286 287	sfctemp->rst_bus = devm_reset_control_get_exclusive(dev, "bus");288	if (IS_ERR(sfctemp->rst_bus))289		return dev_err_probe(dev, PTR_ERR(sfctemp->rst_bus),290				     "error getting busreset\n");291 292	ret = reset_control_assert(sfctemp->rst_sense);293	if (ret)294		return dev_err_probe(dev, ret, "error asserting sense reset\n");295 296	ret = reset_control_assert(sfctemp->rst_bus);297	if (ret)298		return dev_err_probe(dev, ret, "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, ret, "error enabling temperature sensor\n");307 308	hwmon_dev = devm_hwmon_device_register_with_info(dev, "sfctemp", sfctemp,309							 &sfctemp_chip_info, NULL);310	return PTR_ERR_OR_ZERO(hwmon_dev);311}312 313static const struct of_device_id sfctemp_of_match[] = {314	{ .compatible = "starfive,jh7100-temp" },315	{ .compatible = "starfive,jh7110-temp" },316	{ /* sentinel */ }317};318MODULE_DEVICE_TABLE(of, sfctemp_of_match);319 320static struct platform_driver sfctemp_driver = {321	.probe  = sfctemp_probe,322	.driver = {323		.name = "sfctemp",324		.of_match_table = sfctemp_of_match,325	},326};327module_platform_driver(sfctemp_driver);328 329MODULE_AUTHOR("Emil Renner Berthing");330MODULE_DESCRIPTION("StarFive JH71x0 temperature sensor driver");331MODULE_LICENSE("GPL");332