diff options
Diffstat (limited to 'drivers/thermal/hisi_thermal.c')
| -rw-r--r-- | drivers/thermal/hisi_thermal.c | 714 |
1 files changed, 467 insertions, 247 deletions
diff --git a/drivers/thermal/hisi_thermal.c b/drivers/thermal/hisi_thermal.c index 9c3ce341eb97..4307161533a7 100644 --- a/drivers/thermal/hisi_thermal.c +++ b/drivers/thermal/hisi_thermal.c @@ -1,262 +1,490 @@ +// SPDX-License-Identifier: GPL-2.0-only /* - * Hisilicon thermal sensor driver + * HiSilicon thermal sensor driver * - * Copyright (c) 2014-2015 Hisilicon Limited. + * Copyright (c) 2014-2015 HiSilicon Limited. * Copyright (c) 2014-2015 Linaro Limited. * * Xinwei Kong <kong.kongxinwei@hisilicon.com> * Leo Yan <leo.yan@linaro.org> - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * This program is distributed "as is" WITHOUT ANY WARRANTY of any - * kind, whether express or implied; without even the implied warranty - * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. */ #include <linux/cpufreq.h> #include <linux/delay.h> #include <linux/interrupt.h> #include <linux/module.h> +#include <linux/of.h> #include <linux/platform_device.h> #include <linux/io.h> - -#include "thermal_core.h" - -#define TEMP0_TH (0x4) -#define TEMP0_RST_TH (0x8) -#define TEMP0_CFG (0xC) -#define TEMP0_EN (0x10) -#define TEMP0_INT_EN (0x14) -#define TEMP0_INT_CLR (0x18) -#define TEMP0_RST_MSK (0x1C) -#define TEMP0_VALUE (0x28) - -#define HISI_TEMP_BASE (-60) -#define HISI_TEMP_RESET (100000) - -#define HISI_MAX_SENSORS 4 +#include <linux/thermal.h> + +#define HI6220_TEMP0_LAG (0x0) +#define HI6220_TEMP0_TH (0x4) +#define HI6220_TEMP0_RST_TH (0x8) +#define HI6220_TEMP0_CFG (0xC) +#define HI6220_TEMP0_CFG_SS_MSK (0xF000) +#define HI6220_TEMP0_CFG_HDAK_MSK (0x30) +#define HI6220_TEMP0_EN (0x10) +#define HI6220_TEMP0_INT_EN (0x14) +#define HI6220_TEMP0_INT_CLR (0x18) +#define HI6220_TEMP0_RST_MSK (0x1C) +#define HI6220_TEMP0_VALUE (0x28) + +#define HI3660_OFFSET(chan) ((chan) * 0x40) +#define HI3660_TEMP(chan) (HI3660_OFFSET(chan) + 0x1C) +#define HI3660_TH(chan) (HI3660_OFFSET(chan) + 0x20) +#define HI3660_LAG(chan) (HI3660_OFFSET(chan) + 0x28) +#define HI3660_INT_EN(chan) (HI3660_OFFSET(chan) + 0x2C) +#define HI3660_INT_CLR(chan) (HI3660_OFFSET(chan) + 0x30) + +#define HI6220_TEMP_BASE (-60000) +#define HI6220_TEMP_RESET (100000) +#define HI6220_TEMP_STEP (785) +#define HI6220_TEMP_LAG (3500) + +#define HI3660_TEMP_BASE (-63780) +#define HI3660_TEMP_STEP (205) +#define HI3660_TEMP_LAG (4000) + +#define HI6220_CLUSTER0_SENSOR 2 +#define HI6220_CLUSTER1_SENSOR 1 + +#define HI3660_LITTLE_SENSOR 0 +#define HI3660_BIG_SENSOR 1 +#define HI3660_G3D_SENSOR 2 +#define HI3660_MODEM_SENSOR 3 + +struct hisi_thermal_data; struct hisi_thermal_sensor { - struct hisi_thermal_data *thermal; + struct hisi_thermal_data *data; struct thermal_zone_device *tzd; - - long sensor_temp; + const char *irq_name; uint32_t id; uint32_t thres_temp; }; +struct hisi_thermal_ops { + int (*get_temp)(struct hisi_thermal_sensor *sensor); + int (*enable_sensor)(struct hisi_thermal_sensor *sensor); + int (*disable_sensor)(struct hisi_thermal_sensor *sensor); + int (*irq_handler)(struct hisi_thermal_sensor *sensor); + int (*probe)(struct hisi_thermal_data *data); +}; + struct hisi_thermal_data { - struct mutex thermal_lock; /* protects register data */ + const struct hisi_thermal_ops *ops; + struct hisi_thermal_sensor *sensor; struct platform_device *pdev; struct clk *clk; - struct hisi_thermal_sensor sensors[HISI_MAX_SENSORS]; - - int irq, irq_bind_sensor; - bool irq_enabled; - void __iomem *regs; + int nr_sensors; }; -/* in millicelsius */ -static inline int _step_to_temp(int step) +/* + * The temperature computation on the tsensor is as follow: + * Unit: millidegree Celsius + * Step: 200/255 (0.7843) + * Temperature base: -60°C + * + * The register is programmed in temperature steps, every step is 785 + * millidegree and begins at -60 000 m°C + * + * The temperature from the steps: + * + * Temp = TempBase + (steps x 785) + * + * and the steps from the temperature: + * + * steps = (Temp - TempBase) / 785 + * + */ +static inline int hi6220_thermal_step_to_temp(int step) +{ + return HI6220_TEMP_BASE + (step * HI6220_TEMP_STEP); +} + +static inline int hi6220_thermal_temp_to_step(int temp) { - /* - * Every step equals (1 * 200) / 255 celsius, and finally - * need convert to millicelsius. - */ - return (HISI_TEMP_BASE * 1000 + (step * 200000 / 255)); + return DIV_ROUND_UP(temp - HI6220_TEMP_BASE, HI6220_TEMP_STEP); } -static inline long _temp_to_step(long temp) +/* + * for Hi3660, + * Step: 189/922 (0.205) + * Temperature base: -63.780°C + * + * The register is programmed in temperature steps, every step is 205 + * millidegree and begins at -63 780 m°C + */ +static inline int hi3660_thermal_step_to_temp(int step) { - return ((temp - HISI_TEMP_BASE * 1000) * 255) / 200000; + return HI3660_TEMP_BASE + step * HI3660_TEMP_STEP; } -static long hisi_thermal_get_sensor_temp(struct hisi_thermal_data *data, - struct hisi_thermal_sensor *sensor) +static inline int hi3660_thermal_temp_to_step(int temp) { - long val; + return DIV_ROUND_UP(temp - HI3660_TEMP_BASE, HI3660_TEMP_STEP); +} - mutex_lock(&data->thermal_lock); +/* + * The lag register contains 5 bits encoding the temperature in steps. + * + * Each time the temperature crosses the threshold boundary, an + * interrupt is raised. It could be when the temperature is going + * above the threshold or below. However, if the temperature is + * fluctuating around this value due to the load, we can receive + * several interrupts which may not desired. + * + * We can setup a temperature representing the delta between the + * threshold and the current temperature when the temperature is + * decreasing. + * + * For instance: the lag register is 5°C, the threshold is 65°C, when + * the temperature reaches 65°C an interrupt is raised and when the + * temperature decrease to 65°C - 5°C another interrupt is raised. + * + * A very short lag can lead to an interrupt storm, a long lag + * increase the latency to react to the temperature changes. In our + * case, that is not really a problem as we are polling the + * temperature. + * + * [0:4] : lag register + * + * The temperature is coded in steps, cf. HI6220_TEMP_STEP. + * + * Min : 0x00 : 0.0 °C + * Max : 0x1F : 24.3 °C + * + * The 'value' parameter is in milliCelsius. + */ +static inline void hi6220_thermal_set_lag(void __iomem *addr, int value) +{ + writel(DIV_ROUND_UP(value, HI6220_TEMP_STEP) & 0x1F, + addr + HI6220_TEMP0_LAG); +} - /* disable interrupt */ - writel(0x0, data->regs + TEMP0_INT_EN); - writel(0x1, data->regs + TEMP0_INT_CLR); +static inline void hi6220_thermal_alarm_clear(void __iomem *addr, int value) +{ + writel(value, addr + HI6220_TEMP0_INT_CLR); +} - /* disable module firstly */ - writel(0x0, data->regs + TEMP0_EN); +static inline void hi6220_thermal_alarm_enable(void __iomem *addr, int value) +{ + writel(value, addr + HI6220_TEMP0_INT_EN); +} - /* select sensor id */ - writel((sensor->id << 12), data->regs + TEMP0_CFG); +static inline void hi6220_thermal_alarm_set(void __iomem *addr, int temp) +{ + writel(hi6220_thermal_temp_to_step(temp) | 0x0FFFFFF00, + addr + HI6220_TEMP0_TH); +} - /* enable module */ - writel(0x1, data->regs + TEMP0_EN); +static inline void hi6220_thermal_reset_set(void __iomem *addr, int temp) +{ + writel(hi6220_thermal_temp_to_step(temp), addr + HI6220_TEMP0_RST_TH); +} - usleep_range(3000, 5000); +static inline void hi6220_thermal_reset_enable(void __iomem *addr, int value) +{ + writel(value, addr + HI6220_TEMP0_RST_MSK); +} - val = readl(data->regs + TEMP0_VALUE); - val = _step_to_temp(val); +static inline void hi6220_thermal_enable(void __iomem *addr, int value) +{ + writel(value, addr + HI6220_TEMP0_EN); +} - mutex_unlock(&data->thermal_lock); +static inline int hi6220_thermal_get_temperature(void __iomem *addr) +{ + return hi6220_thermal_step_to_temp(readl(addr + HI6220_TEMP0_VALUE)); +} - return val; +/* + * [0:6] lag register + * + * The temperature is coded in steps, cf. HI3660_TEMP_STEP. + * + * Min : 0x00 : 0.0 °C + * Max : 0x7F : 26.0 °C + * + */ +static inline void hi3660_thermal_set_lag(void __iomem *addr, + int id, int value) +{ + writel(DIV_ROUND_UP(value, HI3660_TEMP_STEP) & 0x7F, + addr + HI3660_LAG(id)); } -static void hisi_thermal_enable_bind_irq_sensor - (struct hisi_thermal_data *data) +static inline void hi3660_thermal_alarm_clear(void __iomem *addr, + int id, int value) { - struct hisi_thermal_sensor *sensor; + writel(value, addr + HI3660_INT_CLR(id)); +} - mutex_lock(&data->thermal_lock); +static inline void hi3660_thermal_alarm_enable(void __iomem *addr, + int id, int value) +{ + writel(value, addr + HI3660_INT_EN(id)); +} - sensor = &data->sensors[data->irq_bind_sensor]; +static inline void hi3660_thermal_alarm_set(void __iomem *addr, + int id, int value) +{ + writel(value, addr + HI3660_TH(id)); +} - /* setting the hdak time */ - writel(0x0, data->regs + TEMP0_CFG); +static inline int hi3660_thermal_get_temperature(void __iomem *addr, int id) +{ + return hi3660_thermal_step_to_temp(readl(addr + HI3660_TEMP(id))); +} + +/* + * Temperature configuration register - Sensor selection + * + * Bits [19:12] + * + * 0x0: local sensor (default) + * 0x1: remote sensor 1 (ACPU cluster 1) + * 0x2: remote sensor 2 (ACPU cluster 0) + * 0x3: remote sensor 3 (G3D) + */ +static inline void hi6220_thermal_sensor_select(void __iomem *addr, int sensor) +{ + writel((readl(addr + HI6220_TEMP0_CFG) & ~HI6220_TEMP0_CFG_SS_MSK) | + (sensor << 12), addr + HI6220_TEMP0_CFG); +} + +/* + * Temperature configuration register - Hdak conversion polling interval + * + * Bits [5:4] + * + * 0x0 : 0.768 ms + * 0x1 : 6.144 ms + * 0x2 : 49.152 ms + * 0x3 : 393.216 ms + */ +static inline void hi6220_thermal_hdak_set(void __iomem *addr, int value) +{ + writel((readl(addr + HI6220_TEMP0_CFG) & ~HI6220_TEMP0_CFG_HDAK_MSK) | + (value << 4), addr + HI6220_TEMP0_CFG); +} + +static int hi6220_thermal_irq_handler(struct hisi_thermal_sensor *sensor) +{ + struct hisi_thermal_data *data = sensor->data; + + hi6220_thermal_alarm_clear(data->regs, 1); + return 0; +} + +static int hi3660_thermal_irq_handler(struct hisi_thermal_sensor *sensor) +{ + struct hisi_thermal_data *data = sensor->data; + + hi3660_thermal_alarm_clear(data->regs, sensor->id, 1); + return 0; +} + +static int hi6220_thermal_get_temp(struct hisi_thermal_sensor *sensor) +{ + struct hisi_thermal_data *data = sensor->data; + + return hi6220_thermal_get_temperature(data->regs); +} + +static int hi3660_thermal_get_temp(struct hisi_thermal_sensor *sensor) +{ + struct hisi_thermal_data *data = sensor->data; + + return hi3660_thermal_get_temperature(data->regs, sensor->id); +} + +static int hi6220_thermal_disable_sensor(struct hisi_thermal_sensor *sensor) +{ + struct hisi_thermal_data *data = sensor->data; + + /* disable sensor module */ + hi6220_thermal_enable(data->regs, 0); + hi6220_thermal_alarm_enable(data->regs, 0); + hi6220_thermal_reset_enable(data->regs, 0); + + clk_disable_unprepare(data->clk); + + return 0; +} + +static int hi3660_thermal_disable_sensor(struct hisi_thermal_sensor *sensor) +{ + struct hisi_thermal_data *data = sensor->data; + + /* disable sensor module */ + hi3660_thermal_alarm_enable(data->regs, sensor->id, 0); + return 0; +} + +static int hi6220_thermal_enable_sensor(struct hisi_thermal_sensor *sensor) +{ + struct hisi_thermal_data *data = sensor->data; + int ret; + + /* enable clock for tsensor */ + ret = clk_prepare_enable(data->clk); + if (ret) + return ret; /* disable module firstly */ - writel(0x0, data->regs + TEMP0_RST_MSK); - writel(0x0, data->regs + TEMP0_EN); + hi6220_thermal_reset_enable(data->regs, 0); + hi6220_thermal_enable(data->regs, 0); /* select sensor id */ - writel((sensor->id << 12), data->regs + TEMP0_CFG); + hi6220_thermal_sensor_select(data->regs, sensor->id); + + /* setting the hdak time */ + hi6220_thermal_hdak_set(data->regs, 0); + + /* setting lag value between current temp and the threshold */ + hi6220_thermal_set_lag(data->regs, HI6220_TEMP_LAG); /* enable for interrupt */ - writel(_temp_to_step(sensor->thres_temp) | 0x0FFFFFF00, - data->regs + TEMP0_TH); + hi6220_thermal_alarm_set(data->regs, sensor->thres_temp); - writel(_temp_to_step(HISI_TEMP_RESET), data->regs + TEMP0_RST_TH); + hi6220_thermal_reset_set(data->regs, HI6220_TEMP_RESET); /* enable module */ - writel(0x1, data->regs + TEMP0_RST_MSK); - writel(0x1, data->regs + TEMP0_EN); + hi6220_thermal_reset_enable(data->regs, 1); + hi6220_thermal_enable(data->regs, 1); - writel(0x0, data->regs + TEMP0_INT_CLR); - writel(0x1, data->regs + TEMP0_INT_EN); + hi6220_thermal_alarm_clear(data->regs, 0); + hi6220_thermal_alarm_enable(data->regs, 1); - usleep_range(3000, 5000); - - mutex_unlock(&data->thermal_lock); + return 0; } -static void hisi_thermal_disable_sensor(struct hisi_thermal_data *data) +static int hi3660_thermal_enable_sensor(struct hisi_thermal_sensor *sensor) { - mutex_lock(&data->thermal_lock); + unsigned int value; + struct hisi_thermal_data *data = sensor->data; - /* disable sensor module */ - writel(0x0, data->regs + TEMP0_INT_EN); - writel(0x0, data->regs + TEMP0_RST_MSK); - writel(0x0, data->regs + TEMP0_EN); + /* disable interrupt */ + hi3660_thermal_alarm_enable(data->regs, sensor->id, 0); + + /* setting lag value between current temp and the threshold */ + hi3660_thermal_set_lag(data->regs, sensor->id, HI3660_TEMP_LAG); + + /* set interrupt threshold */ + value = hi3660_thermal_temp_to_step(sensor->thres_temp); + hi3660_thermal_alarm_set(data->regs, sensor->id, value); + + /* enable interrupt */ + hi3660_thermal_alarm_clear(data->regs, sensor->id, 1); + hi3660_thermal_alarm_enable(data->regs, sensor->id, 1); - mutex_unlock(&data->thermal_lock); + return 0; } -static int hisi_thermal_get_temp(void *_sensor, int *temp) +static int hi6220_thermal_probe(struct hisi_thermal_data *data) { - struct hisi_thermal_sensor *sensor = _sensor; - struct hisi_thermal_data *data = sensor->thermal; + struct platform_device *pdev = data->pdev; + struct device *dev = &pdev->dev; - int sensor_id = -1, i; - long max_temp = 0; + data->clk = devm_clk_get(dev, "thermal_clk"); + if (IS_ERR(data->clk)) + return dev_err_probe(dev, PTR_ERR(data->clk), "failed to get thermal clk\n"); - *temp = hisi_thermal_get_sensor_temp(data, sensor); + data->sensor = devm_kzalloc(dev, sizeof(*data->sensor), GFP_KERNEL); + if (!data->sensor) + return -ENOMEM; - sensor->sensor_temp = *temp; + data->sensor[0].id = HI6220_CLUSTER0_SENSOR; + data->sensor[0].irq_name = "tsensor_intr"; + data->sensor[0].data = data; + data->nr_sensors = 1; - for (i = 0; i < HISI_MAX_SENSORS; i++) { - if (!data->sensors[i].tzd) - continue; + return 0; +} - if (data->sensors[i].sensor_temp >= max_temp) { - max_temp = data->sensors[i].sensor_temp; - sensor_id = i; - } - } +static int hi3660_thermal_probe(struct hisi_thermal_data *data) +{ + struct platform_device *pdev = data->pdev; + struct device *dev = &pdev->dev; - /* If no sensor has been enabled, then skip to enable irq */ - if (sensor_id == -1) - return 0; + data->nr_sensors = 1; - mutex_lock(&data->thermal_lock); - data->irq_bind_sensor = sensor_id; - mutex_unlock(&data->thermal_lock); - - dev_dbg(&data->pdev->dev, "id=%d, irq=%d, temp=%d, thres=%d\n", - sensor->id, data->irq_enabled, *temp, sensor->thres_temp); - /* - * Bind irq to sensor for two cases: - * Reenable alarm IRQ if temperature below threshold; - * if irq has been enabled, always set it; - */ - if (data->irq_enabled) { - hisi_thermal_enable_bind_irq_sensor(data); - return 0; - } + data->sensor = devm_kcalloc(dev, data->nr_sensors, + sizeof(*data->sensor), GFP_KERNEL); + if (!data->sensor) + return -ENOMEM; - if (max_temp < sensor->thres_temp) { - data->irq_enabled = true; - hisi_thermal_enable_bind_irq_sensor(data); - enable_irq(data->irq); - } + data->sensor[0].id = HI3660_BIG_SENSOR; + data->sensor[0].irq_name = "tsensor_a73"; + data->sensor[0].data = data; return 0; } -static struct thermal_zone_of_device_ops hisi_of_thermal_ops = { - .get_temp = hisi_thermal_get_temp, -}; - -static irqreturn_t hisi_thermal_alarm_irq(int irq, void *dev) +static int hisi_thermal_get_temp(struct thermal_zone_device *tz, int *temp) { - struct hisi_thermal_data *data = dev; + struct hisi_thermal_sensor *sensor = thermal_zone_device_priv(tz); + struct hisi_thermal_data *data = sensor->data; - disable_irq_nosync(irq); - data->irq_enabled = false; + *temp = data->ops->get_temp(sensor); - return IRQ_WAKE_THREAD; + return 0; } +static const struct thermal_zone_device_ops hisi_of_thermal_ops = { + .get_temp = hisi_thermal_get_temp, +}; + static irqreturn_t hisi_thermal_alarm_irq_thread(int irq, void *dev) { - struct hisi_thermal_data *data = dev; - struct hisi_thermal_sensor *sensor; - int i; + struct hisi_thermal_sensor *sensor = dev; + struct hisi_thermal_data *data = sensor->data; + int temp = 0; - mutex_lock(&data->thermal_lock); - sensor = &data->sensors[data->irq_bind_sensor]; + data->ops->irq_handler(sensor); - dev_crit(&data->pdev->dev, "THERMAL ALARM: T > %d\n", - sensor->thres_temp / 1000); - mutex_unlock(&data->thermal_lock); + temp = data->ops->get_temp(sensor); - for (i = 0; i < HISI_MAX_SENSORS; i++) { - if (!data->sensors[i].tzd) - continue; + if (temp >= sensor->thres_temp) { + dev_crit(&data->pdev->dev, + "sensor <%d> THERMAL ALARM: %d > %d\n", + sensor->id, temp, sensor->thres_temp); - thermal_zone_device_update(data->sensors[i].tzd, + thermal_zone_device_update(sensor->tzd, THERMAL_EVENT_UNSPECIFIED); + + } else { + dev_crit(&data->pdev->dev, + "sensor <%d> THERMAL ALARM stopped: %d < %d\n", + sensor->id, temp, sensor->thres_temp); } return IRQ_HANDLED; } -static int hisi_thermal_register_sensor(struct platform_device *pdev, - struct hisi_thermal_data *data, - struct hisi_thermal_sensor *sensor, - int index) +static int hisi_trip_walk_cb(struct thermal_trip *trip, void *arg) { - int ret, i; - const struct thermal_trip *trip; + struct hisi_thermal_sensor *sensor = arg; - sensor->id = index; - sensor->thermal = data; + if (trip->type != THERMAL_TRIP_PASSIVE) + return 0; + + sensor->thres_temp = trip->temperature; + /* Return nonzero to terminate the search. */ + return 1; +} - sensor->tzd = devm_thermal_zone_of_sensor_register(&pdev->dev, - sensor->id, sensor, &hisi_of_thermal_ops); +static int hisi_thermal_register_sensor(struct platform_device *pdev, + struct hisi_thermal_sensor *sensor) +{ + int ret; + + sensor->tzd = devm_thermal_of_zone_register(&pdev->dev, + sensor->id, sensor, + &hisi_of_thermal_ops); if (IS_ERR(sensor->tzd)) { ret = PTR_ERR(sensor->tzd); sensor->tzd = NULL; @@ -265,20 +493,36 @@ static int hisi_thermal_register_sensor(struct platform_device *pdev, return ret; } - trip = of_thermal_get_trip_points(sensor->tzd); - - for (i = 0; i < of_thermal_get_ntrips(sensor->tzd); i++) { - if (trip[i].type == THERMAL_TRIP_PASSIVE) { - sensor->thres_temp = trip[i].temperature; - break; - } - } + thermal_zone_for_each_trip(sensor->tzd, hisi_trip_walk_cb, sensor); return 0; } +static const struct hisi_thermal_ops hi6220_ops = { + .get_temp = hi6220_thermal_get_temp, + .enable_sensor = hi6220_thermal_enable_sensor, + .disable_sensor = hi6220_thermal_disable_sensor, + .irq_handler = hi6220_thermal_irq_handler, + .probe = hi6220_thermal_probe, +}; + +static const struct hisi_thermal_ops hi3660_ops = { + .get_temp = hi3660_thermal_get_temp, + .enable_sensor = hi3660_thermal_enable_sensor, + .disable_sensor = hi3660_thermal_disable_sensor, + .irq_handler = hi3660_thermal_irq_handler, + .probe = hi3660_thermal_probe, +}; + static const struct of_device_id of_hisi_thermal_match[] = { - { .compatible = "hisilicon,tsensor" }, + { + .compatible = "hisilicon,tsensor", + .data = &hi6220_ops, + }, + { + .compatible = "hisilicon,hi3660-tsensor", + .data = &hi3660_ops, + }, { /* end */ } }; MODULE_DEVICE_TABLE(of, of_hisi_thermal_match); @@ -288,108 +532,89 @@ static void hisi_thermal_toggle_sensor(struct hisi_thermal_sensor *sensor, { struct thermal_zone_device *tzd = sensor->tzd; - tzd->ops->set_mode(tzd, - on ? THERMAL_DEVICE_ENABLED : THERMAL_DEVICE_DISABLED); + if (on) + thermal_zone_device_enable(tzd); + else + thermal_zone_device_disable(tzd); } static int hisi_thermal_probe(struct platform_device *pdev) { struct hisi_thermal_data *data; - struct resource *res; - int i; - int ret; + struct device *dev = &pdev->dev; + int i, ret; - data = devm_kzalloc(&pdev->dev, sizeof(*data), GFP_KERNEL); + data = devm_kzalloc(dev, sizeof(*data), GFP_KERNEL); if (!data) return -ENOMEM; - mutex_init(&data->thermal_lock); data->pdev = pdev; + platform_set_drvdata(pdev, data); + data->ops = of_device_get_match_data(dev); - res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - data->regs = devm_ioremap_resource(&pdev->dev, res); - if (IS_ERR(data->regs)) { - dev_err(&pdev->dev, "failed to get io address\n"); + data->regs = devm_platform_ioremap_resource(pdev, 0); + if (IS_ERR(data->regs)) return PTR_ERR(data->regs); - } - - data->irq = platform_get_irq(pdev, 0); - if (data->irq < 0) - return data->irq; - ret = devm_request_threaded_irq(&pdev->dev, data->irq, - hisi_thermal_alarm_irq, - hisi_thermal_alarm_irq_thread, - 0, "hisi_thermal", data); - if (ret < 0) { - dev_err(&pdev->dev, "failed to request alarm irq: %d\n", ret); + ret = data->ops->probe(data); + if (ret) return ret; - } - platform_set_drvdata(pdev, data); + for (i = 0; i < data->nr_sensors; i++) { + struct hisi_thermal_sensor *sensor = &data->sensor[i]; - data->clk = devm_clk_get(&pdev->dev, "thermal_clk"); - if (IS_ERR(data->clk)) { - ret = PTR_ERR(data->clk); - if (ret != -EPROBE_DEFER) - dev_err(&pdev->dev, - "failed to get thermal clk: %d\n", ret); - return ret; - } + ret = hisi_thermal_register_sensor(pdev, sensor); + if (ret) { + dev_err(dev, "failed to register thermal sensor: %d\n", + ret); + return ret; + } - /* enable clock for thermal */ - ret = clk_prepare_enable(data->clk); - if (ret) { - dev_err(&pdev->dev, "failed to enable thermal clk: %d\n", ret); - return ret; - } + ret = platform_get_irq(pdev, 0); + if (ret < 0) + return ret; + + ret = devm_request_threaded_irq(dev, ret, NULL, + hisi_thermal_alarm_irq_thread, + IRQF_ONESHOT, sensor->irq_name, + sensor); + if (ret < 0) { + dev_err(dev, "Failed to request alarm irq: %d\n", ret); + return ret; + } + + ret = data->ops->enable_sensor(sensor); + if (ret) { + dev_err(dev, "Failed to setup the sensor: %d\n", ret); + return ret; + } - hisi_thermal_enable_bind_irq_sensor(data); - irq_get_irqchip_state(data->irq, IRQCHIP_STATE_MASKED, - &data->irq_enabled); - - for (i = 0; i < HISI_MAX_SENSORS; ++i) { - ret = hisi_thermal_register_sensor(pdev, data, - &data->sensors[i], i); - if (ret) - dev_err(&pdev->dev, - "failed to register thermal sensor: %d\n", ret); - else - hisi_thermal_toggle_sensor(&data->sensors[i], true); + hisi_thermal_toggle_sensor(sensor, true); } return 0; } -static int hisi_thermal_remove(struct platform_device *pdev) +static void hisi_thermal_remove(struct platform_device *pdev) { struct hisi_thermal_data *data = platform_get_drvdata(pdev); int i; - for (i = 0; i < HISI_MAX_SENSORS; i++) { - struct hisi_thermal_sensor *sensor = &data->sensors[i]; - - if (!sensor->tzd) - continue; + for (i = 0; i < data->nr_sensors; i++) { + struct hisi_thermal_sensor *sensor = &data->sensor[i]; hisi_thermal_toggle_sensor(sensor, false); + data->ops->disable_sensor(sensor); } - - hisi_thermal_disable_sensor(data); - clk_disable_unprepare(data->clk); - - return 0; } -#ifdef CONFIG_PM_SLEEP static int hisi_thermal_suspend(struct device *dev) { struct hisi_thermal_data *data = dev_get_drvdata(dev); + int i; - hisi_thermal_disable_sensor(data); - data->irq_enabled = false; - - clk_disable_unprepare(data->clk); + for (i = 0; i < data->nr_sensors; i++) + data->ops->disable_sensor(&data->sensor[i]); return 0; } @@ -397,27 +622,22 @@ static int hisi_thermal_suspend(struct device *dev) static int hisi_thermal_resume(struct device *dev) { struct hisi_thermal_data *data = dev_get_drvdata(dev); - int ret; - - ret = clk_prepare_enable(data->clk); - if (ret) - return ret; + int i, ret = 0; - data->irq_enabled = true; - hisi_thermal_enable_bind_irq_sensor(data); + for (i = 0; i < data->nr_sensors; i++) + ret |= data->ops->enable_sensor(&data->sensor[i]); - return 0; + return ret; } -#endif -static SIMPLE_DEV_PM_OPS(hisi_thermal_pm_ops, +static DEFINE_SIMPLE_DEV_PM_OPS(hisi_thermal_pm_ops, hisi_thermal_suspend, hisi_thermal_resume); static struct platform_driver hisi_thermal_driver = { .driver = { .name = "hisi_thermal", - .pm = &hisi_thermal_pm_ops, - .of_match_table = of_hisi_thermal_match, + .pm = pm_sleep_ptr(&hisi_thermal_pm_ops), + .of_match_table = of_hisi_thermal_match, }, .probe = hisi_thermal_probe, .remove = hisi_thermal_remove, @@ -427,5 +647,5 @@ module_platform_driver(hisi_thermal_driver); MODULE_AUTHOR("Xinwei Kong <kong.kongxinwei@hisilicon.com>"); MODULE_AUTHOR("Leo Yan <leo.yan@linaro.org>"); -MODULE_DESCRIPTION("Hisilicon thermal driver"); +MODULE_DESCRIPTION("HiSilicon thermal driver"); MODULE_LICENSE("GPL v2"); |
