Commit | Line | Data |
---|---|---|
a94cb7ee RM |
1 | /* |
2 | * Copyright (C) 2017 Rafał Miłecki <rafal@milecki.pl> | |
3 | * | |
4 | * This program is free software; you can redistribute it and/or modify | |
5 | * it under the terms of the GNU General Public License version 2 as | |
6 | * published by the Free Software Foundation. | |
7 | */ | |
8 | ||
9 | #include <linux/module.h> | |
10 | #include <linux/of_address.h> | |
11 | #include <linux/platform_device.h> | |
12 | #include <linux/thermal.h> | |
13 | ||
14 | #define PVTMON_CONTROL0 0x00 | |
15 | #define PVTMON_CONTROL0_SEL_MASK 0x0000000e | |
16 | #define PVTMON_CONTROL0_SEL_TEMP_MONITOR 0x00000000 | |
17 | #define PVTMON_CONTROL0_SEL_TEST_MODE 0x0000000e | |
18 | #define PVTMON_STATUS 0x08 | |
19 | ||
20 | struct ns_thermal { | |
21 | struct thermal_zone_device *tz; | |
22 | void __iomem *pvtmon; | |
23 | }; | |
24 | ||
25 | static int ns_thermal_get_temp(void *data, int *temp) | |
26 | { | |
27 | struct ns_thermal *ns_thermal = data; | |
28 | int offset = thermal_zone_get_offset(ns_thermal->tz); | |
29 | int slope = thermal_zone_get_slope(ns_thermal->tz); | |
30 | u32 val; | |
31 | ||
32 | val = readl(ns_thermal->pvtmon + PVTMON_CONTROL0); | |
33 | if ((val & PVTMON_CONTROL0_SEL_MASK) != PVTMON_CONTROL0_SEL_TEMP_MONITOR) { | |
34 | /* Clear current mode selection */ | |
35 | val &= ~PVTMON_CONTROL0_SEL_MASK; | |
36 | ||
37 | /* Set temp monitor mode (it's the default actually) */ | |
38 | val |= PVTMON_CONTROL0_SEL_TEMP_MONITOR; | |
39 | ||
40 | writel(val, ns_thermal->pvtmon + PVTMON_CONTROL0); | |
41 | } | |
42 | ||
43 | val = readl(ns_thermal->pvtmon + PVTMON_STATUS); | |
44 | *temp = slope * val + offset; | |
45 | ||
46 | return 0; | |
47 | } | |
48 | ||
49 | static const struct thermal_zone_of_device_ops ns_thermal_ops = { | |
50 | .get_temp = ns_thermal_get_temp, | |
51 | }; | |
52 | ||
53 | static int ns_thermal_probe(struct platform_device *pdev) | |
54 | { | |
55 | struct device *dev = &pdev->dev; | |
56 | struct ns_thermal *ns_thermal; | |
57 | ||
58 | ns_thermal = devm_kzalloc(dev, sizeof(*ns_thermal), GFP_KERNEL); | |
59 | if (!ns_thermal) | |
60 | return -ENOMEM; | |
61 | ||
62 | ns_thermal->pvtmon = of_iomap(dev_of_node(dev), 0); | |
63 | if (WARN_ON(!ns_thermal->pvtmon)) | |
64 | return -ENOENT; | |
65 | ||
66 | ns_thermal->tz = devm_thermal_zone_of_sensor_register(dev, 0, | |
67 | ns_thermal, | |
68 | &ns_thermal_ops); | |
69 | if (IS_ERR(ns_thermal->tz)) { | |
70 | iounmap(ns_thermal->pvtmon); | |
71 | return PTR_ERR(ns_thermal->tz); | |
72 | } | |
73 | ||
74 | platform_set_drvdata(pdev, ns_thermal); | |
75 | ||
76 | return 0; | |
77 | } | |
78 | ||
79 | static int ns_thermal_remove(struct platform_device *pdev) | |
80 | { | |
81 | struct ns_thermal *ns_thermal = platform_get_drvdata(pdev); | |
82 | ||
83 | iounmap(ns_thermal->pvtmon); | |
84 | ||
85 | return 0; | |
86 | } | |
87 | ||
88 | static const struct of_device_id ns_thermal_of_match[] = { | |
89 | { .compatible = "brcm,ns-thermal", }, | |
90 | {}, | |
91 | }; | |
92 | MODULE_DEVICE_TABLE(of, ns_thermal_of_match); | |
93 | ||
94 | static struct platform_driver ns_thermal_driver = { | |
95 | .probe = ns_thermal_probe, | |
96 | .remove = ns_thermal_remove, | |
97 | .driver = { | |
98 | .name = "ns-thermal", | |
99 | .of_match_table = ns_thermal_of_match, | |
100 | }, | |
101 | }; | |
102 | module_platform_driver(ns_thermal_driver); | |
103 | ||
cb9b323b | 104 | MODULE_AUTHOR("Rafał Miłecki <rafal@milecki.pl>"); |
a94cb7ee RM |
105 | MODULE_DESCRIPTION("Northstar thermal driver"); |
106 | MODULE_LICENSE("GPL v2"); |