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