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