18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci * Synaptics AS370 SoC Hardware Monitoring Driver
48c2ecf20Sopenharmony_ci *
58c2ecf20Sopenharmony_ci * Copyright (C) 2018 Synaptics Incorporated
68c2ecf20Sopenharmony_ci * Author: Jisheng Zhang <jszhang@kernel.org>
78c2ecf20Sopenharmony_ci */
88c2ecf20Sopenharmony_ci
98c2ecf20Sopenharmony_ci#include <linux/bitops.h>
108c2ecf20Sopenharmony_ci#include <linux/hwmon.h>
118c2ecf20Sopenharmony_ci#include <linux/init.h>
128c2ecf20Sopenharmony_ci#include <linux/io.h>
138c2ecf20Sopenharmony_ci#include <linux/module.h>
148c2ecf20Sopenharmony_ci#include <linux/of_device.h>
158c2ecf20Sopenharmony_ci
168c2ecf20Sopenharmony_ci#define CTRL		0x0
178c2ecf20Sopenharmony_ci#define  PD		BIT(0)
188c2ecf20Sopenharmony_ci#define  EN		BIT(1)
198c2ecf20Sopenharmony_ci#define  T_SEL		BIT(2)
208c2ecf20Sopenharmony_ci#define  V_SEL		BIT(3)
218c2ecf20Sopenharmony_ci#define  NMOS_SEL	BIT(8)
228c2ecf20Sopenharmony_ci#define  PMOS_SEL	BIT(9)
238c2ecf20Sopenharmony_ci#define STS		0x4
248c2ecf20Sopenharmony_ci#define  BN_MASK	GENMASK(11, 0)
258c2ecf20Sopenharmony_ci#define  EOC		BIT(12)
268c2ecf20Sopenharmony_ci
278c2ecf20Sopenharmony_cistruct as370_hwmon {
288c2ecf20Sopenharmony_ci	void __iomem *base;
298c2ecf20Sopenharmony_ci};
308c2ecf20Sopenharmony_ci
318c2ecf20Sopenharmony_cistatic void init_pvt(struct as370_hwmon *hwmon)
328c2ecf20Sopenharmony_ci{
338c2ecf20Sopenharmony_ci	u32 val;
348c2ecf20Sopenharmony_ci	void __iomem *addr = hwmon->base + CTRL;
358c2ecf20Sopenharmony_ci
368c2ecf20Sopenharmony_ci	val = PD;
378c2ecf20Sopenharmony_ci	writel_relaxed(val, addr);
388c2ecf20Sopenharmony_ci	val |= T_SEL;
398c2ecf20Sopenharmony_ci	writel_relaxed(val, addr);
408c2ecf20Sopenharmony_ci	val |= EN;
418c2ecf20Sopenharmony_ci	writel_relaxed(val, addr);
428c2ecf20Sopenharmony_ci	val &= ~PD;
438c2ecf20Sopenharmony_ci	writel_relaxed(val, addr);
448c2ecf20Sopenharmony_ci}
458c2ecf20Sopenharmony_ci
468c2ecf20Sopenharmony_cistatic int as370_hwmon_read(struct device *dev, enum hwmon_sensor_types type,
478c2ecf20Sopenharmony_ci			    u32 attr, int channel, long *temp)
488c2ecf20Sopenharmony_ci{
498c2ecf20Sopenharmony_ci	int val;
508c2ecf20Sopenharmony_ci	struct as370_hwmon *hwmon = dev_get_drvdata(dev);
518c2ecf20Sopenharmony_ci
528c2ecf20Sopenharmony_ci	switch (attr) {
538c2ecf20Sopenharmony_ci	case hwmon_temp_input:
548c2ecf20Sopenharmony_ci		val = readl_relaxed(hwmon->base + STS) & BN_MASK;
558c2ecf20Sopenharmony_ci		*temp = DIV_ROUND_CLOSEST(val * 251802, 4096) - 85525;
568c2ecf20Sopenharmony_ci		break;
578c2ecf20Sopenharmony_ci	default:
588c2ecf20Sopenharmony_ci		return -EOPNOTSUPP;
598c2ecf20Sopenharmony_ci	}
608c2ecf20Sopenharmony_ci
618c2ecf20Sopenharmony_ci	return 0;
628c2ecf20Sopenharmony_ci}
638c2ecf20Sopenharmony_ci
648c2ecf20Sopenharmony_cistatic umode_t
658c2ecf20Sopenharmony_cias370_hwmon_is_visible(const void *data, enum hwmon_sensor_types type,
668c2ecf20Sopenharmony_ci		       u32 attr, int channel)
678c2ecf20Sopenharmony_ci{
688c2ecf20Sopenharmony_ci	if (type != hwmon_temp)
698c2ecf20Sopenharmony_ci		return 0;
708c2ecf20Sopenharmony_ci
718c2ecf20Sopenharmony_ci	switch (attr) {
728c2ecf20Sopenharmony_ci	case hwmon_temp_input:
738c2ecf20Sopenharmony_ci		return 0444;
748c2ecf20Sopenharmony_ci	default:
758c2ecf20Sopenharmony_ci		return 0;
768c2ecf20Sopenharmony_ci	}
778c2ecf20Sopenharmony_ci}
788c2ecf20Sopenharmony_ci
798c2ecf20Sopenharmony_cistatic const u32 as370_hwmon_temp_config[] = {
808c2ecf20Sopenharmony_ci	HWMON_T_INPUT,
818c2ecf20Sopenharmony_ci	0
828c2ecf20Sopenharmony_ci};
838c2ecf20Sopenharmony_ci
848c2ecf20Sopenharmony_cistatic const struct hwmon_channel_info as370_hwmon_temp = {
858c2ecf20Sopenharmony_ci	.type = hwmon_temp,
868c2ecf20Sopenharmony_ci	.config = as370_hwmon_temp_config,
878c2ecf20Sopenharmony_ci};
888c2ecf20Sopenharmony_ci
898c2ecf20Sopenharmony_cistatic const struct hwmon_channel_info *as370_hwmon_info[] = {
908c2ecf20Sopenharmony_ci	&as370_hwmon_temp,
918c2ecf20Sopenharmony_ci	NULL
928c2ecf20Sopenharmony_ci};
938c2ecf20Sopenharmony_ci
948c2ecf20Sopenharmony_cistatic const struct hwmon_ops as370_hwmon_ops = {
958c2ecf20Sopenharmony_ci	.is_visible = as370_hwmon_is_visible,
968c2ecf20Sopenharmony_ci	.read = as370_hwmon_read,
978c2ecf20Sopenharmony_ci};
988c2ecf20Sopenharmony_ci
998c2ecf20Sopenharmony_cistatic const struct hwmon_chip_info as370_chip_info = {
1008c2ecf20Sopenharmony_ci	.ops = &as370_hwmon_ops,
1018c2ecf20Sopenharmony_ci	.info = as370_hwmon_info,
1028c2ecf20Sopenharmony_ci};
1038c2ecf20Sopenharmony_ci
1048c2ecf20Sopenharmony_cistatic int as370_hwmon_probe(struct platform_device *pdev)
1058c2ecf20Sopenharmony_ci{
1068c2ecf20Sopenharmony_ci	struct device *hwmon_dev;
1078c2ecf20Sopenharmony_ci	struct as370_hwmon *hwmon;
1088c2ecf20Sopenharmony_ci	struct device *dev = &pdev->dev;
1098c2ecf20Sopenharmony_ci
1108c2ecf20Sopenharmony_ci	hwmon = devm_kzalloc(dev, sizeof(*hwmon), GFP_KERNEL);
1118c2ecf20Sopenharmony_ci	if (!hwmon)
1128c2ecf20Sopenharmony_ci		return -ENOMEM;
1138c2ecf20Sopenharmony_ci
1148c2ecf20Sopenharmony_ci	hwmon->base = devm_platform_ioremap_resource(pdev, 0);
1158c2ecf20Sopenharmony_ci	if (IS_ERR(hwmon->base))
1168c2ecf20Sopenharmony_ci		return PTR_ERR(hwmon->base);
1178c2ecf20Sopenharmony_ci
1188c2ecf20Sopenharmony_ci	init_pvt(hwmon);
1198c2ecf20Sopenharmony_ci
1208c2ecf20Sopenharmony_ci	hwmon_dev = devm_hwmon_device_register_with_info(dev,
1218c2ecf20Sopenharmony_ci							 "as370",
1228c2ecf20Sopenharmony_ci							 hwmon,
1238c2ecf20Sopenharmony_ci							 &as370_chip_info,
1248c2ecf20Sopenharmony_ci							 NULL);
1258c2ecf20Sopenharmony_ci	return PTR_ERR_OR_ZERO(hwmon_dev);
1268c2ecf20Sopenharmony_ci}
1278c2ecf20Sopenharmony_ci
1288c2ecf20Sopenharmony_cistatic const struct of_device_id as370_hwmon_match[] = {
1298c2ecf20Sopenharmony_ci	{ .compatible = "syna,as370-hwmon" },
1308c2ecf20Sopenharmony_ci	{},
1318c2ecf20Sopenharmony_ci};
1328c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(of, as370_hwmon_match);
1338c2ecf20Sopenharmony_ci
1348c2ecf20Sopenharmony_cistatic struct platform_driver as370_hwmon_driver = {
1358c2ecf20Sopenharmony_ci	.probe = as370_hwmon_probe,
1368c2ecf20Sopenharmony_ci	.driver = {
1378c2ecf20Sopenharmony_ci		.name = "as370-hwmon",
1388c2ecf20Sopenharmony_ci		.of_match_table = as370_hwmon_match,
1398c2ecf20Sopenharmony_ci	},
1408c2ecf20Sopenharmony_ci};
1418c2ecf20Sopenharmony_cimodule_platform_driver(as370_hwmon_driver);
1428c2ecf20Sopenharmony_ci
1438c2ecf20Sopenharmony_ciMODULE_AUTHOR("Jisheng Zhang<jszhang@kernel.org>");
1448c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("Synaptics AS370 SoC hardware monitor");
1458c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL v2");
146