18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci * Copyright (C) 2017 Rafał Miłecki <rafal@milecki.pl>
48c2ecf20Sopenharmony_ci */
58c2ecf20Sopenharmony_ci
68c2ecf20Sopenharmony_ci#include <linux/module.h>
78c2ecf20Sopenharmony_ci#include <linux/of_address.h>
88c2ecf20Sopenharmony_ci#include <linux/platform_device.h>
98c2ecf20Sopenharmony_ci#include <linux/thermal.h>
108c2ecf20Sopenharmony_ci
118c2ecf20Sopenharmony_ci#define PVTMON_CONTROL0					0x00
128c2ecf20Sopenharmony_ci#define PVTMON_CONTROL0_SEL_MASK			0x0000000e
138c2ecf20Sopenharmony_ci#define PVTMON_CONTROL0_SEL_TEMP_MONITOR		0x00000000
148c2ecf20Sopenharmony_ci#define PVTMON_CONTROL0_SEL_TEST_MODE			0x0000000e
158c2ecf20Sopenharmony_ci#define PVTMON_STATUS					0x08
168c2ecf20Sopenharmony_ci
178c2ecf20Sopenharmony_cistruct ns_thermal {
188c2ecf20Sopenharmony_ci	struct thermal_zone_device *tz;
198c2ecf20Sopenharmony_ci	void __iomem *pvtmon;
208c2ecf20Sopenharmony_ci};
218c2ecf20Sopenharmony_ci
228c2ecf20Sopenharmony_cistatic int ns_thermal_get_temp(void *data, int *temp)
238c2ecf20Sopenharmony_ci{
248c2ecf20Sopenharmony_ci	struct ns_thermal *ns_thermal = data;
258c2ecf20Sopenharmony_ci	int offset = thermal_zone_get_offset(ns_thermal->tz);
268c2ecf20Sopenharmony_ci	int slope = thermal_zone_get_slope(ns_thermal->tz);
278c2ecf20Sopenharmony_ci	u32 val;
288c2ecf20Sopenharmony_ci
298c2ecf20Sopenharmony_ci	val = readl(ns_thermal->pvtmon + PVTMON_CONTROL0);
308c2ecf20Sopenharmony_ci	if ((val & PVTMON_CONTROL0_SEL_MASK) != PVTMON_CONTROL0_SEL_TEMP_MONITOR) {
318c2ecf20Sopenharmony_ci		/* Clear current mode selection */
328c2ecf20Sopenharmony_ci		val &= ~PVTMON_CONTROL0_SEL_MASK;
338c2ecf20Sopenharmony_ci
348c2ecf20Sopenharmony_ci		/* Set temp monitor mode (it's the default actually) */
358c2ecf20Sopenharmony_ci		val |= PVTMON_CONTROL0_SEL_TEMP_MONITOR;
368c2ecf20Sopenharmony_ci
378c2ecf20Sopenharmony_ci		writel(val, ns_thermal->pvtmon + PVTMON_CONTROL0);
388c2ecf20Sopenharmony_ci	}
398c2ecf20Sopenharmony_ci
408c2ecf20Sopenharmony_ci	val = readl(ns_thermal->pvtmon + PVTMON_STATUS);
418c2ecf20Sopenharmony_ci	*temp = slope * val + offset;
428c2ecf20Sopenharmony_ci
438c2ecf20Sopenharmony_ci	return 0;
448c2ecf20Sopenharmony_ci}
458c2ecf20Sopenharmony_ci
468c2ecf20Sopenharmony_cistatic const struct thermal_zone_of_device_ops ns_thermal_ops = {
478c2ecf20Sopenharmony_ci	.get_temp = ns_thermal_get_temp,
488c2ecf20Sopenharmony_ci};
498c2ecf20Sopenharmony_ci
508c2ecf20Sopenharmony_cistatic int ns_thermal_probe(struct platform_device *pdev)
518c2ecf20Sopenharmony_ci{
528c2ecf20Sopenharmony_ci	struct device *dev = &pdev->dev;
538c2ecf20Sopenharmony_ci	struct ns_thermal *ns_thermal;
548c2ecf20Sopenharmony_ci
558c2ecf20Sopenharmony_ci	ns_thermal = devm_kzalloc(dev, sizeof(*ns_thermal), GFP_KERNEL);
568c2ecf20Sopenharmony_ci	if (!ns_thermal)
578c2ecf20Sopenharmony_ci		return -ENOMEM;
588c2ecf20Sopenharmony_ci
598c2ecf20Sopenharmony_ci	ns_thermal->pvtmon = of_iomap(dev_of_node(dev), 0);
608c2ecf20Sopenharmony_ci	if (WARN_ON(!ns_thermal->pvtmon))
618c2ecf20Sopenharmony_ci		return -ENOENT;
628c2ecf20Sopenharmony_ci
638c2ecf20Sopenharmony_ci	ns_thermal->tz = devm_thermal_zone_of_sensor_register(dev, 0,
648c2ecf20Sopenharmony_ci							      ns_thermal,
658c2ecf20Sopenharmony_ci							      &ns_thermal_ops);
668c2ecf20Sopenharmony_ci	if (IS_ERR(ns_thermal->tz)) {
678c2ecf20Sopenharmony_ci		iounmap(ns_thermal->pvtmon);
688c2ecf20Sopenharmony_ci		return PTR_ERR(ns_thermal->tz);
698c2ecf20Sopenharmony_ci	}
708c2ecf20Sopenharmony_ci
718c2ecf20Sopenharmony_ci	platform_set_drvdata(pdev, ns_thermal);
728c2ecf20Sopenharmony_ci
738c2ecf20Sopenharmony_ci	return 0;
748c2ecf20Sopenharmony_ci}
758c2ecf20Sopenharmony_ci
768c2ecf20Sopenharmony_cistatic int ns_thermal_remove(struct platform_device *pdev)
778c2ecf20Sopenharmony_ci{
788c2ecf20Sopenharmony_ci	struct ns_thermal *ns_thermal = platform_get_drvdata(pdev);
798c2ecf20Sopenharmony_ci
808c2ecf20Sopenharmony_ci	iounmap(ns_thermal->pvtmon);
818c2ecf20Sopenharmony_ci
828c2ecf20Sopenharmony_ci	return 0;
838c2ecf20Sopenharmony_ci}
848c2ecf20Sopenharmony_ci
858c2ecf20Sopenharmony_cistatic const struct of_device_id ns_thermal_of_match[] = {
868c2ecf20Sopenharmony_ci	{ .compatible = "brcm,ns-thermal", },
878c2ecf20Sopenharmony_ci	{},
888c2ecf20Sopenharmony_ci};
898c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(of, ns_thermal_of_match);
908c2ecf20Sopenharmony_ci
918c2ecf20Sopenharmony_cistatic struct platform_driver ns_thermal_driver = {
928c2ecf20Sopenharmony_ci	.probe		= ns_thermal_probe,
938c2ecf20Sopenharmony_ci	.remove		= ns_thermal_remove,
948c2ecf20Sopenharmony_ci	.driver = {
958c2ecf20Sopenharmony_ci		.name = "ns-thermal",
968c2ecf20Sopenharmony_ci		.of_match_table = ns_thermal_of_match,
978c2ecf20Sopenharmony_ci	},
988c2ecf20Sopenharmony_ci};
998c2ecf20Sopenharmony_cimodule_platform_driver(ns_thermal_driver);
1008c2ecf20Sopenharmony_ci
1018c2ecf20Sopenharmony_ciMODULE_AUTHOR("Rafał Miłecki <rafal@milecki.pl>");
1028c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("Northstar thermal driver");
1038c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL v2");
104