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