On 19/09/2022 05:49, Yinbo Zhu wrote: > This patch adds the support for loongson2k thermal sensor controller, > which can support maximum 3 sensors. > > It's based on thermal of framework: > - Trip points defined in device tree. > - Cpufreq as cooling device registered in loongson2k cpufreq driver. > - Pwm fan as cooling device registered in hwmon pwm-fan driver. > > Signed-off-by: zhanghongchen <zhanghongchen@xxxxxxxxxxx> > Signed-off-by: Yinbo Zhu <zhuyinbo@xxxxxxxxxxx> > --- > drivers/thermal/Kconfig | 10 ++ > drivers/thermal/Makefile | 1 + > drivers/thermal/ls2k_thermal.c | 244 +++++++++++++++++++++++++++++++++ > 3 files changed, 255 insertions(+) > create mode 100644 drivers/thermal/ls2k_thermal.c > > diff --git a/drivers/thermal/Kconfig b/drivers/thermal/Kconfig > index e052dae614eb..04f5c044bc94 100644 > --- a/drivers/thermal/Kconfig > +++ b/drivers/thermal/Kconfig > @@ -504,4 +504,14 @@ config KHADAS_MCU_FAN_THERMAL > If you say yes here you get support for the FAN controlled > by the Microcontroller found on the Khadas VIM boards. > > +config LOONGSON2K_THERMAL > + tristate "Loongson 2K SOC series thermal driver" > + depends on OF > + default m Why should it be module by default on x86 or ARM? See how other drivers do it. > + help > + Support for Thermal driver found on Loongson 2K SOC series platforms. > + It supports one critical trip point and one passive trip point. The > + cpufreq and the pwm fan is used as the cooling device to throttle CPUs > + when the passive trip is crossed. > + > endif (...) > + > +static int ls2k_thermal_probe(struct platform_device *pdev) > +{ > + struct device *dev = &pdev->dev; > + struct resource *res; > + struct ls2k_thermal_data *data; > + int ret; > + > + data = devm_kzalloc(dev, sizeof(*data), GFP_KERNEL); > + if (!data) > + return -ENOMEM; > + > + data->pdev = pdev; > + platform_set_drvdata(pdev, data); > + > + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); > + data->regs = devm_ioremap(dev, res->start, resource_size(res)); There is a helper combining these two. > + if (IS_ERR(data->regs)) { > + dev_err(dev, "failed to get io address\n"); I think error msg can be skipped in such case. Core should print it. > + return PTR_ERR(data->regs); > + } > + > + /* get irq */ > + data->irq = platform_get_irq(pdev, 0); > + if (data->irq < 0) > + return data->irq; > + > + /* get id */ > + if (of_property_read_u32(dev->of_node, "id", &data->id)) { > + dev_err(dev, "not found id property!\n"); > + data->id = LS2K_SOC_DEFAULT_SENSOR; > + } > + > + if (data->id > LS2K_SOC_MAX_SENSOR_NUM) { > + dev_err(dev, "sensor id error,must be in <0 ~ %d>\n", > + LS2K_SOC_MAX_SENSOR_NUM); > + return -EINVAL; > + } > + > + writeb(0xff, data->regs + LS2K_TSENSOR_STATUS); > + > + ls2k_tsensor_set(data, 0, 0, false); > + > + data->tzd = devm_thermal_zone_of_sensor_register(&pdev->dev, > + data->id, data, > + &ls2k_of_thermal_ops); > + if (IS_ERR(data->tzd)) { > + ret = PTR_ERR(data->tzd); > + data->tzd = NULL; > + dev_err(&pdev->dev, "failed to register %d\n", ret); > + return ret; > + } > + > + ret = devm_request_threaded_irq(dev, data->irq, > + ls2k_thermal_alarm_irq, ls2k_thermal_irq_thread, > + IRQF_ONESHOT, "ls2k_thermal", data); > + if (ret < 0) { > + dev_err(dev, "failed to request alarm irq: %d\n", ret); > + return ret; > + } > + > + /* > + * Thermal_zone doesn't enable hwmon as default, > + * enable it here > + */ > + data->tzd->tzp->no_hwmon = false; > + ret = thermal_add_hwmon_sysfs(data->tzd); > + if (ret) { > + dev_err(dev, "failed to add hwmon sysfs interface %d\n", ret); > + return ret; > + } > + > + return 0; > +} > + > +int ls2k_thermal_remove(struct platform_device *pdev) > +{ > + struct ls2k_thermal_data *data = platform_get_drvdata(pdev); > + int reg_off = data->id * 2; > + > + /* disable interrupt */ > + writew(0, data->regs + LS2K_TSENSOR_CTRL_LO + reg_off); > + writew(0, data->regs + LS2K_TSENSOR_CTRL_HI + reg_off); > + > + return 0; > +} > + > +static const struct of_device_id of_ls2k_thermal_match[] = { > + { .compatible = "loongson,2k-tsensor",}, > + { /* end */ } > +}; > +MODULE_DEVICE_TABLE(of, of_ls2k_thermal_match); > + > +#ifdef CONFIG_PM_SLEEP > +static int ls2k_thermal_suspend(struct device *dev) > +{ > + struct ls2k_thermal_data *data = dev_get_drvdata(dev); > + int reg_off = data->id * 2; > + > + data->ctrl_low_val = readw(data->regs + LS2K_TSENSOR_CTRL_LO + reg_off); > + data->ctrl_hi_val = readw(data->regs + LS2K_TSENSOR_CTRL_HI + reg_off); > + > + writew(0, data->regs + LS2K_TSENSOR_CTRL_LO + reg_off); > + writew(0, data->regs + LS2K_TSENSOR_CTRL_HI + reg_off); > + > + return 0; > +} > + > +static int ls2k_thermal_resume(struct device *dev) > +{ > + struct ls2k_thermal_data *data = dev_get_drvdata(dev); > + int reg_off = data->id * 2; > + > + writew(data->ctrl_low_val, data->regs + LS2K_TSENSOR_CTRL_LO + reg_off); > + writew(data->ctrl_hi_val, data->regs + LS2K_TSENSOR_CTRL_HI + reg_off); > + > + return 0; > +} > + > +static SIMPLE_DEV_PM_OPS(ls2k_thermal_pm_ops, > + ls2k_thermal_suspend, ls2k_thermal_resume); > +#endif > + > +static struct platform_driver ls2k_thermal_driver = { > + .driver = { > + .name = "ls2k_thermal", > +#ifdef CONFIG_PM_SLEEP pm_ptr() (and use same approach as its users - no need for ifdefs, DEFINE_SIMPLE_DEV_PM_OPS) > + .pm = &ls2k_thermal_pm_ops, > +#endif > + .of_match_table = of_ls2k_thermal_match, > + }, > + .probe = ls2k_thermal_probe, > + .remove = ls2k_thermal_remove, > +}; > +module_platform_driver(ls2k_thermal_driver); Best regards, Krzysztof