Commit | Line | Data |
---|---|---|
e20db70d AH |
1 | // SPDX-License-Identifier: GPL-2.0+ |
2 | /* | |
3 | * Copyright 2018-2020 NXP. | |
4 | */ | |
5 | ||
755a7397 | 6 | #include <dt-bindings/firmware/imx/rsrc.h> |
e20db70d AH |
7 | #include <linux/err.h> |
8 | #include <linux/firmware/imx/sci.h> | |
e20db70d AH |
9 | #include <linux/module.h> |
10 | #include <linux/of.h> | |
11 | #include <linux/of_device.h> | |
12 | #include <linux/platform_device.h> | |
13 | #include <linux/slab.h> | |
14 | #include <linux/thermal.h> | |
15 | ||
16 | #include "thermal_core.h" | |
d2bc4dd9 | 17 | #include "thermal_hwmon.h" |
e20db70d AH |
18 | |
19 | #define IMX_SC_MISC_FUNC_GET_TEMP 13 | |
20 | ||
21 | static struct imx_sc_ipc *thermal_ipc_handle; | |
22 | ||
23 | struct imx_sc_sensor { | |
24 | struct thermal_zone_device *tzd; | |
25 | u32 resource_id; | |
26 | }; | |
27 | ||
28 | struct req_get_temp { | |
29 | u16 resource_id; | |
30 | u8 type; | |
1fd213f3 | 31 | } __packed __aligned(4); |
e20db70d AH |
32 | |
33 | struct resp_get_temp { | |
968ea0df AH |
34 | s16 celsius; |
35 | s8 tenths; | |
1fd213f3 | 36 | } __packed __aligned(4); |
e20db70d AH |
37 | |
38 | struct imx_sc_msg_misc_get_temp { | |
39 | struct imx_sc_rpc_msg hdr; | |
40 | union { | |
41 | struct req_get_temp req; | |
42 | struct resp_get_temp resp; | |
43 | } data; | |
1fd213f3 | 44 | } __packed __aligned(4); |
e20db70d | 45 | |
32fb9a8a | 46 | static int imx_sc_thermal_get_temp(struct thermal_zone_device *tz, int *temp) |
e20db70d AH |
47 | { |
48 | struct imx_sc_msg_misc_get_temp msg; | |
49 | struct imx_sc_rpc_msg *hdr = &msg.hdr; | |
32fb9a8a | 50 | struct imx_sc_sensor *sensor = tz->devdata; |
e20db70d AH |
51 | int ret; |
52 | ||
53 | msg.data.req.resource_id = sensor->resource_id; | |
54 | msg.data.req.type = IMX_SC_C_TEMP; | |
55 | ||
56 | hdr->ver = IMX_SC_RPC_VERSION; | |
57 | hdr->svc = IMX_SC_RPC_SVC_MISC; | |
58 | hdr->func = IMX_SC_MISC_FUNC_GET_TEMP; | |
59 | hdr->size = 2; | |
60 | ||
61 | ret = imx_scu_call_rpc(thermal_ipc_handle, &msg, true); | |
62 | if (ret) { | |
63 | dev_err(&sensor->tzd->device, "read temp sensor %d failed, ret %d\n", | |
64 | sensor->resource_id, ret); | |
65 | return ret; | |
66 | } | |
67 | ||
68 | *temp = msg.data.resp.celsius * 1000 + msg.data.resp.tenths * 100; | |
69 | ||
70 | return 0; | |
71 | } | |
72 | ||
32fb9a8a | 73 | static const struct thermal_zone_device_ops imx_sc_thermal_ops = { |
e20db70d AH |
74 | .get_temp = imx_sc_thermal_get_temp, |
75 | }; | |
76 | ||
77 | static int imx_sc_thermal_probe(struct platform_device *pdev) | |
78 | { | |
e20db70d | 79 | struct imx_sc_sensor *sensor; |
31fd4b9d DL |
80 | const int *resource_id; |
81 | int i, ret; | |
e20db70d AH |
82 | |
83 | ret = imx_scu_get_handle(&thermal_ipc_handle); | |
84 | if (ret) | |
85 | return ret; | |
86 | ||
31fd4b9d DL |
87 | resource_id = of_device_get_match_data(&pdev->dev); |
88 | if (!resource_id) | |
89 | return -EINVAL; | |
e20db70d | 90 | |
31fd4b9d | 91 | for (i = 0; resource_id[i] > 0; i++) { |
e20db70d | 92 | |
e20db70d | 93 | sensor = devm_kzalloc(&pdev->dev, sizeof(*sensor), GFP_KERNEL); |
31fd4b9d DL |
94 | if (!sensor) |
95 | return -ENOMEM; | |
e20db70d | 96 | |
31fd4b9d | 97 | sensor->resource_id = resource_id[i]; |
e20db70d | 98 | |
31fd4b9d DL |
99 | sensor->tzd = devm_thermal_of_zone_register(&pdev->dev, sensor->resource_id, |
100 | sensor, &imx_sc_thermal_ops); | |
e20db70d | 101 | if (IS_ERR(sensor->tzd)) { |
31fd4b9d DL |
102 | /* |
103 | * Save the error value before freeing the | |
104 | * sensor pointer, otherwise we endup with a | |
105 | * use-after-free error | |
106 | */ | |
e20db70d | 107 | ret = PTR_ERR(sensor->tzd); |
31fd4b9d DL |
108 | |
109 | devm_kfree(&pdev->dev, sensor); | |
110 | ||
111 | /* | |
112 | * The thermal framework notifies us there is | |
113 | * no thermal zone description for such a | |
114 | * sensor id | |
115 | */ | |
116 | if (ret == -ENODEV) | |
117 | continue; | |
118 | ||
119 | dev_err(&pdev->dev, "failed to register thermal zone\n"); | |
120 | return ret; | |
e20db70d | 121 | } |
d2bc4dd9 AH |
122 | |
123 | if (devm_thermal_add_hwmon_sysfs(sensor->tzd)) | |
124 | dev_warn(&pdev->dev, "failed to add hwmon sysfs attributes\n"); | |
e20db70d AH |
125 | } |
126 | ||
31fd4b9d | 127 | return 0; |
e20db70d AH |
128 | } |
129 | ||
130 | static int imx_sc_thermal_remove(struct platform_device *pdev) | |
131 | { | |
132 | return 0; | |
133 | } | |
134 | ||
31fd4b9d DL |
135 | static int imx_sc_sensors[] = { IMX_SC_R_SYSTEM, IMX_SC_R_PMIC_0, -1 }; |
136 | ||
e20db70d | 137 | static const struct of_device_id imx_sc_thermal_table[] = { |
31fd4b9d | 138 | { .compatible = "fsl,imx-sc-thermal", .data = imx_sc_sensors }, |
e20db70d AH |
139 | {} |
140 | }; | |
141 | MODULE_DEVICE_TABLE(of, imx_sc_thermal_table); | |
142 | ||
143 | static struct platform_driver imx_sc_thermal_driver = { | |
144 | .probe = imx_sc_thermal_probe, | |
145 | .remove = imx_sc_thermal_remove, | |
146 | .driver = { | |
147 | .name = "imx-sc-thermal", | |
148 | .of_match_table = imx_sc_thermal_table, | |
149 | }, | |
150 | }; | |
151 | module_platform_driver(imx_sc_thermal_driver); | |
152 | ||
153 | MODULE_AUTHOR("Anson Huang <Anson.Huang@nxp.com>"); | |
154 | MODULE_DESCRIPTION("Thermal driver for NXP i.MX SoCs with system controller"); | |
155 | MODULE_LICENSE("GPL v2"); |