]>
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 AH |
45 | |
46 | static int imx_sc_thermal_get_temp(void *data, int *temp) | |
47 | { | |
48 | struct imx_sc_msg_misc_get_temp msg; | |
49 | struct imx_sc_rpc_msg *hdr = &msg.hdr; | |
50 | struct imx_sc_sensor *sensor = data; | |
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 | ||
73 | static const struct thermal_zone_of_device_ops imx_sc_thermal_ops = { | |
74 | .get_temp = imx_sc_thermal_get_temp, | |
75 | }; | |
76 | ||
77 | static int imx_sc_thermal_probe(struct platform_device *pdev) | |
78 | { | |
79 | struct device_node *np, *child, *sensor_np; | |
80 | struct imx_sc_sensor *sensor; | |
81 | int ret; | |
82 | ||
83 | ret = imx_scu_get_handle(&thermal_ipc_handle); | |
84 | if (ret) | |
85 | return ret; | |
86 | ||
87 | np = of_find_node_by_name(NULL, "thermal-zones"); | |
88 | if (!np) | |
89 | return -ENODEV; | |
90 | ||
91 | sensor_np = of_node_get(pdev->dev.of_node); | |
92 | ||
93 | for_each_available_child_of_node(np, child) { | |
94 | sensor = devm_kzalloc(&pdev->dev, sizeof(*sensor), GFP_KERNEL); | |
95 | if (!sensor) { | |
96 | of_node_put(sensor_np); | |
97 | return -ENOMEM; | |
98 | } | |
99 | ||
100 | ret = thermal_zone_of_get_sensor_id(child, | |
101 | sensor_np, | |
102 | &sensor->resource_id); | |
103 | if (ret < 0) { | |
104 | dev_err(&pdev->dev, | |
105 | "failed to get valid sensor resource id: %d\n", | |
106 | ret); | |
107 | break; | |
108 | } | |
109 | ||
110 | sensor->tzd = devm_thermal_zone_of_sensor_register(&pdev->dev, | |
111 | sensor->resource_id, | |
112 | sensor, | |
113 | &imx_sc_thermal_ops); | |
114 | if (IS_ERR(sensor->tzd)) { | |
115 | dev_err(&pdev->dev, "failed to register thermal zone\n"); | |
116 | ret = PTR_ERR(sensor->tzd); | |
117 | break; | |
118 | } | |
d2bc4dd9 AH |
119 | |
120 | if (devm_thermal_add_hwmon_sysfs(sensor->tzd)) | |
121 | dev_warn(&pdev->dev, "failed to add hwmon sysfs attributes\n"); | |
e20db70d AH |
122 | } |
123 | ||
124 | of_node_put(sensor_np); | |
125 | ||
126 | return ret; | |
127 | } | |
128 | ||
129 | static int imx_sc_thermal_remove(struct platform_device *pdev) | |
130 | { | |
131 | return 0; | |
132 | } | |
133 | ||
134 | static const struct of_device_id imx_sc_thermal_table[] = { | |
135 | { .compatible = "fsl,imx-sc-thermal", }, | |
136 | {} | |
137 | }; | |
138 | MODULE_DEVICE_TABLE(of, imx_sc_thermal_table); | |
139 | ||
140 | static struct platform_driver imx_sc_thermal_driver = { | |
141 | .probe = imx_sc_thermal_probe, | |
142 | .remove = imx_sc_thermal_remove, | |
143 | .driver = { | |
144 | .name = "imx-sc-thermal", | |
145 | .of_match_table = imx_sc_thermal_table, | |
146 | }, | |
147 | }; | |
148 | module_platform_driver(imx_sc_thermal_driver); | |
149 | ||
150 | MODULE_AUTHOR("Anson Huang <Anson.Huang@nxp.com>"); | |
151 | MODULE_DESCRIPTION("Thermal driver for NXP i.MX SoCs with system controller"); | |
152 | MODULE_LICENSE("GPL v2"); |