Re: [PATCH 2/3] thermal: artpec8-tmu: Add tmu driver for artpec8

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



Hello Sang Min,

On Thu, Mar 10, 2022 at 7:43 AM Sang Min Kim <hypmean.kim@xxxxxxxxxxx> wrote:
>
>
> Add tmu driver to handle thermal management for artpec8 SoC.
>
> This driver is derived from the tmu of exynos and additionally supports
> the thermal zone of multiple remote sensors of artpec8.
>
> Signed-off-by: sangmin kim <hypmean.kim@xxxxxxxxxxx>
> ---

Any reason for adding a new TMU driver here? Can this SoC use the
existing exynos_tmu driver?
In case IP is similar and there are few differences in terms of
registers offset etc,
you can use a compatible name to differentiate the same.


>  drivers/thermal/samsung/artpec8_tmu.c | 754 ++++++++++++++++++++++++++++++++++
>  1 file changed, 754 insertions(+)
>  create mode 100644 drivers/thermal/samsung/artpec8_tmu.c
>
> diff --git a/drivers/thermal/samsung/artpec8_tmu.c b/drivers/thermal/samsung/artpec8_tmu.c
> new file mode 100644
> index 0000000..d973827
> --- /dev/null
> +++ b/drivers/thermal/samsung/artpec8_tmu.c
> @@ -0,0 +1,754 @@
> +// SPDX-License-Identifier: GPL-2.0-or-later
> +/*
> + *  artpec8_tmu.c - Samsung TMU (Thermal Management Unit)
> + *
> + *  Copyright (C) 2014 Samsung Electronics
> + *  Bartlomiej Zolnierkiewicz <b.zolnierkie@xxxxxxxxxxx>
> + *  Lukasz Majewski <l.majewski@xxxxxxxxxxx>
> + *
> + *  Copyright (C) 2011 Samsung Electronics
> + *  Donggeun Kim <dg77.kim@xxxxxxxxxxx>
> + *  Amit Daniel Kachhap <amit.kachhap@xxxxxxxxxx>
> + */
> +
> +#include <linux/clk.h>
> +#include <linux/io.h>
> +#include <linux/interrupt.h>
> +#include <linux/kernel.h>
> +#include <linux/module.h>
> +#include <linux/of_device.h>
> +#include <linux/of_address.h>
> +#include <linux/of_irq.h>
> +#include <linux/platform_device.h>
> +
> +#include <dt-bindings/thermal/thermal_exynos.h>
> +
> +#include "../thermal_core.h"
> +
> +#define ARTPEC8_TMU_REG_TRIMINFO        0x0
> +#define ARTPEC8_TMU_REG_TRIMINFO1        0x4
> +#define ARTPEC8_TMU_REG_TRIMINFO2        0x8
> +#define ARTPEC8_TMU_REG_TRIMINFO3        0xC
> +#define ARTPEC8_TMU_REG_TRIMINFO4        0x10
> +#define ARTPEC8_TMU_REG_TRIMINFO5        0x14
> +#define ARTPEC8_TMU_REG_CONTROL                0x20
> +#define ARTPEC8_TMU_REG_CONTROL1        0x24
> +#define ARTPEC8_TMU_REG_STATUS                0x28
> +
> +#define ARTPEC8_TMU_REG_AVG_CONTROL        0x38
> +#define ARTPEC8_TMU_REG_TMU_TRIM0        0x3C
> +
> +#define ARTPEC8_TMU_REG_EMUL_CON        0x160
> +#define NUM_PROBE_OFFSET                16
> +
> +#define ARTPEC8_FIRST_POINT_TRIM        25
> +#define ARTPEC8_SECOND_POINT_TRIM        105
> +
> +#define ARTPEC8_EMUL_EN                        1
> +#define ARTPEC8_TIME_OFFSET                16
> +#define ARTPEC8_EMUL_NEXT_TIME                (0x4e20 << ARTPEC8_TIME_OFFSET)
> +
> +#define ARTPEC8_TMU_TEMP_MASK                0x1ff
> +#define ARTPEC8_CALIB_SEL_SHIFT                23
> +
> +#define ARTPEC8_EMUL_DATA_SHIFT                7
> +
> +#define ARTPEC8_T_BUF_VREF_SEL_SHIFT        18
> +#define ARTPEC8_T_BUF_SLOPE_SEL_SHIFT        18
> +#define ARTPEC8_INTEN_TRIPPING_SHIFT        7
> +#define ARTPEC8_INTEN_CLOCKDOWN_SHIFT        8
> +#define ARTPEC8_TRIMINFO_105_SHIFT        9
> +#define ARTPEC8_INTEN_FALL0_SHIFT        16
> +#define ARTPEC8_TMU_REF_VOLTAGE_SHIFT        24
> +#define ARTPEC8_TMU_REF_VOLTAGE_MASK        0x1f
> +#define ARTPEC8_TMU_BUF_SLOPE_SEL_SHIFT        8
> +#define ARTPEC8_TMU_BUF_SLOPE_SEL_MASK        0xf
> +
> +#define ARTPEC8_TMU_CONTROL_CORE_EN        1
> +#define ARTPEC8_TMU_CONTROL_AUTO_MODE        2
> +#define ARTPEC8_TMU_CONTROL_TRIP_EN        (1 << 12)
> +#define ARTPEC8_LPI_MODE_EN                (1 << 10)
> +
> +#define ARTPEC8_TRIM0_BGR_I_SHIFT        20
> +#define ARTPEC8_TRIM0_VREF_SHIFT        12
> +#define ARTPEC8_TRIM0_VBE_I_SHIFT        8
> +
> +
> +#define INTPEND_RISE_MASK                0xff
> +#define INTPEND_FALL_MASK                0xff0000
> +#define ARTPEC8_TRIM0_MASK                0xf
> +#define ARTPEC8_TRIM2_MASK                0x7
> +
> +#define ARTPEC8_TRIMINFO_TRIM0_SHIFT        18
> +
> +#define MCELSIUS                        1000
> +
> +#define LOW_TEMP_WEIGHT                        9203
> +#define HIGH_TEMP_WEIGHT                9745
> +#define TEMP_WEIGHT                        10000
> +
> +struct sensor_offset {
> +        u32 trim_offset;
> +        u32 temp_offset;
> +        u32 temp_reg_shift;
> +        u32 rise_offset;
> +        u32 fall_offset;
> +        u32 past_offset;
> +        u32 inten;
> +        u32 intpend;
> +};
> +
> +#define SENSOR(_tr, _te, _sh, _ri, _fa, _pa, _en, _pend)        \
> +        {                                                        \
> +                .trim_offset        = _tr,                                \
> +                .temp_offset        = _te,                                \
> +                .temp_reg_shift        = _sh,                                \
> +                .rise_offset        = _ri,                                \
> +                .fall_offset        = _fa,                                \
> +                .past_offset        = _pa,                                \
> +                .inten                = _en,                                \
> +                .intpend        = _pend,                        \
> +        }
> +
> +static const struct sensor_offset artpec8_sensors[] = {
> +        SENSOR(0x0,        0x40,        0,  0x50,        0x60,        0x70,        0x110,        0x118),
> +        SENSOR(0x4,        0x40,        9,  0x170,        0x180,        0x90,        0x120,        0x128),
> +        SENSOR(0x8,        0x44,        0,  0x190,        0x1a0,        0xb0,        0x130,        0x138),
> +        SENSOR(0xc,        0x44,        9,  0x1b0,        0x1c0,        0xd0,        0x140,        0x148),
> +        SENSOR(0x10,        0x44,        18, 0x1d0,        0x1e0,        0xf0,        0x150,        0x158),
> +        SENSOR(0x14,        0x48,        0,  0x1f0,        0x200,        0x250,        0x310,        0x318),
> +};
> +
> +/**
> + * struct artpec8_sensor: A structure to hold the private data of the sensor
> + * @tmudev: The tmu device which this sensor is connected.
> + * @tzd: Thermal zonde device pointer to register this sensor.
> + * @id: Identifier of the one instance of the thermal sensor.
> + * @ntrip: Number of threshols for this sensor.
> + * @triminfo_25: OTP information to trim temperature sensor error for 25C
> + * @triminfo_105: OTP information to trim temperature sensor error for 105C
> + * @trim_offset: Offset of triminfo register.
> + * @temp_offset: Offset of current temperature. The temperature values of
> + *                2 to 3 remote sensors are stored in this register.
> + * @temp_reg_shift: start location of each tempt in temp_off
> + * @rise_offset: Offset of rising threshold level 6 and 7.
> + * @fall_offset: Offset of falling thershold level 6 and 7.
> + * @past_offset: Offset of Past temperature 0,1.
> + * @inten: Offset of interrupt enable sfr.
> + * @intpend: Offset of interrupt pending sfr.
> + */
> +struct artpec8_sensor {
> +        struct artpec8_tmu_data *tmudev;
> +        struct thermal_zone_device *tzd;
> +        int id;
> +        unsigned int ntrip;
> +        u16 triminfo_25;
> +        u16 triminfo_105;
> +        u32 trim_offset;
> +        u32 temp_offset;
> +        u32 temp_reg_shift;
> +        u32 rise_offset;
> +        u32 fall_offset;
> +        u32 past_offset;
> +        u32 inten;
> +        u32 intpend;
> +};
> +
> +/**
> + * struct artpec8_tmu_data : A structure to hold the private data of the TMU
> +        driver
> + * @id: identifier of the one instance of the TMU controller.
> + * @base: base address of the single instance of the TMU controller.
> + * @irq: irq number of the TMU controller.
> + * @irq_work: pointer to the irq work structure.
> + * @lock: lock to implement synchronization.
> + * @clk: pointer to the clock structure.
> + * @cal_type: calibration type for temperature
> + * @nr_remote: number of remote temperature sensors.
> + * @sensor: variable size member including the information of each sensor
> + */
> +struct artpec8_tmu_data {
> +        int id;
> +        void __iomem *base;
> +        int irq;
> +        struct work_struct irq_work;
> +        struct mutex lock;
> +        struct clk *clk;
> +        u32 cal_type;
> +        unsigned int nr_remote;
> +
> +        struct artpec8_sensor sensor[0];
> +};
> +
> +static u16 temp_to_code(struct artpec8_sensor *sensor, int temp)
> +{
> +        int code;
> +        int weight;
> +
> +        if (sensor->tmudev->cal_type == TYPE_ONE_POINT_TRIMMING)
> +                return temp + sensor->triminfo_25 - ARTPEC8_FIRST_POINT_TRIM;
> +
> +        if (temp > ARTPEC8_FIRST_POINT_TRIM)
> +                weight = HIGH_TEMP_WEIGHT;
> +        else
> +                weight = LOW_TEMP_WEIGHT;
> +
> +        code = DIV_ROUND_CLOSEST((temp - ARTPEC8_FIRST_POINT_TRIM) *
> +                (sensor->triminfo_105 - sensor->triminfo_25) * TEMP_WEIGHT,
> +                (ARTPEC8_SECOND_POINT_TRIM - ARTPEC8_FIRST_POINT_TRIM) *
> +                weight);
> +        code += sensor->triminfo_25;
> +
> +        return (u16)code;
> +}
> +
> +static int code_to_temp(struct artpec8_sensor *sensor, u16 code)
> +{
> +        int temp;
> +        int weight;
> +
> +        if (sensor->tmudev->cal_type == TYPE_ONE_POINT_TRIMMING)
> +                return code - sensor->triminfo_25 + ARTPEC8_FIRST_POINT_TRIM;
> +
> +        if (code > sensor->triminfo_25)
> +                weight = HIGH_TEMP_WEIGHT;
> +        else
> +                weight = LOW_TEMP_WEIGHT;
> +
> +        temp = DIV_ROUND_CLOSEST((code - sensor->triminfo_25) *
> +                (ARTPEC8_SECOND_POINT_TRIM - ARTPEC8_FIRST_POINT_TRIM) * weight,
> +                (sensor->triminfo_105 - sensor->triminfo_25) * TEMP_WEIGHT);
> +        temp += ARTPEC8_FIRST_POINT_TRIM;
> +
> +        return temp;
> +}
> +
> +static void artpec8_tmu_set_trip_temp(struct artpec8_tmu_data *data,
> +                int trip, int temp, int remote)
> +{
> +        unsigned int reg_off, bit_off;
> +        u32 th;
> +        struct artpec8_sensor *sensor;
> +        unsigned int temp_rise;
> +
> +        sensor = &data->sensor[remote];
> +        temp_rise = sensor->rise_offset;
> +
> +        reg_off = ((7 - trip) / 2) * 4;
> +        bit_off = ((8 - trip) % 2);
> +
> +        th = readl(data->base + temp_rise + reg_off);
> +        th &= ~(ARTPEC8_TMU_TEMP_MASK << (16 * bit_off));
> +        th |= temp_to_code(sensor, temp) << (16 * bit_off);
> +        writel(th, data->base + temp_rise + reg_off);
> +}
> +
> +static void artpec8_tmu_set_trip_hyst(struct artpec8_tmu_data *data,
> +                int trip, int temp, int hyst, int remote)
> +{
> +        unsigned int reg_off, bit_off;
> +        u32 th;
> +        struct artpec8_sensor *sensor;
> +        unsigned int temp_fall;
> +
> +        sensor = &data->sensor[remote];
> +        temp_fall = sensor->fall_offset;
> +
> +        reg_off = ((7 - trip) / 2) * 4;
> +        bit_off = ((8 - trip) % 2);
> +
> +        th = readl(data->base + temp_fall + reg_off);
> +        th &= ~(ARTPEC8_TMU_TEMP_MASK << (16 * bit_off));
> +        th |= temp_to_code(sensor, temp - hyst) << (16 * bit_off);
> +        writel(th, data->base + temp_fall + reg_off);
> +}
> +
> +static void artpec8_enable_interrupt(struct artpec8_tmu_data *data,
> +                int sensor_idx)
> +{
> +        int i;
> +        unsigned int interrupt_en = 0;
> +        struct thermal_zone_device *tz = data->sensor[sensor_idx].tzd;
> +
> +        for (i = 0; i < data->sensor->ntrip; i++) {
> +                if (!of_thermal_is_trip_valid(tz, i))
> +                        continue;
> +                interrupt_en |= (1 << i);
> +        }
> +        writel(interrupt_en, data->base + data->sensor[sensor_idx].inten);
> +}
> +
> +static void artpec8_tmu_control(struct platform_device *pdev, bool on)
> +{
> +        struct artpec8_tmu_data *data = platform_get_drvdata(pdev);
> +        unsigned int con, con1, i;
> +        unsigned int vref;
> +        unsigned int slope;
> +        unsigned int trim0, trim0_bgr, trim0_vref, trim0_vbe, avg_mode;
> +
> +        vref = (readl(data->base + ARTPEC8_TMU_REG_TRIMINFO) >>
> +                ARTPEC8_T_BUF_VREF_SEL_SHIFT) &
> +                ARTPEC8_TMU_REF_VOLTAGE_MASK;
> +        slope = (readl(data->base + ARTPEC8_TMU_REG_TRIMINFO1) >>
> +                ARTPEC8_T_BUF_SLOPE_SEL_SHIFT) &
> +                ARTPEC8_TMU_BUF_SLOPE_SEL_MASK;
> +        con = (vref << ARTPEC8_TMU_REF_VOLTAGE_SHIFT) |
> +                (slope << ARTPEC8_TMU_BUF_SLOPE_SEL_SHIFT);
> +
> +        if (on) {
> +                for (i = 0; i < data->nr_remote; i++)
> +                        artpec8_enable_interrupt(data, i);
> +                con |= (ARTPEC8_TMU_CONTROL_CORE_EN |
> +                        ARTPEC8_TMU_CONTROL_AUTO_MODE);
> +        } else {
> +                con &= ~(ARTPEC8_TMU_CONTROL_CORE_EN);
> +        }
> +
> +        trim0_bgr = readl(data->base + ARTPEC8_TMU_REG_TRIMINFO3) >>
> +                ARTPEC8_TRIMINFO_TRIM0_SHIFT;
> +        trim0_bgr &= ARTPEC8_TRIM0_MASK;
> +        trim0_vref = readl(data->base + ARTPEC8_TMU_REG_TRIMINFO4) >>
> +                ARTPEC8_TRIMINFO_TRIM0_SHIFT;
> +        trim0_vref &= ARTPEC8_TRIM0_MASK;
> +        trim0_vbe = readl(data->base + ARTPEC8_TMU_REG_TRIMINFO5) >>
> +                ARTPEC8_TRIMINFO_TRIM0_SHIFT;
> +        trim0_vbe &= ARTPEC8_TRIM0_MASK;
> +        trim0 = trim0_bgr << ARTPEC8_TRIM0_BGR_I_SHIFT |
> +                trim0_vref << ARTPEC8_TRIM0_VREF_SHIFT |
> +                trim0_vbe << ARTPEC8_TRIM0_VBE_I_SHIFT;
> +
> +        con1 = (data->nr_remote << NUM_PROBE_OFFSET) | ARTPEC8_LPI_MODE_EN;
> +
> +        while (!readb(data->base + ARTPEC8_TMU_REG_STATUS))
> +                pr_debug("TMU busy waiting\n");
> +
> +
> +        avg_mode = readl(data->base + ARTPEC8_TMU_REG_AVG_CONTROL);
> +        avg_mode &= ~ARTPEC8_TRIM2_MASK;
> +        avg_mode |= (readl(data->base + ARTPEC8_TMU_REG_TRIMINFO2) >>
> +                ARTPEC8_TRIMINFO_TRIM0_SHIFT) & ARTPEC8_TRIM2_MASK;
> +
> +        writel(avg_mode, data->base + ARTPEC8_TMU_REG_AVG_CONTROL);
> +        writel(trim0, data->base + ARTPEC8_TMU_REG_TMU_TRIM0);
> +        writel(con1, data->base + ARTPEC8_TMU_REG_CONTROL1);
> +        writel(con, data->base + ARTPEC8_TMU_REG_CONTROL);
> +}
> +
> +static void artpec8_tmu_clear_irqs(struct artpec8_tmu_data *data, int i)
> +{
> +        u32 intp = readl(data->base + data->sensor[i].intpend);
> +
> +        writel(intp, data->base + data->sensor[i].intpend);
> +}
> +
> +static int artpec8_map_dt_data(struct platform_device *pdev)
> +{
> +        struct artpec8_tmu_data *data = platform_get_drvdata(pdev);
> +        struct device *dev;
> +        struct resource res;
> +
> +        if (!data || !pdev->dev.of_node)
> +                return -ENODEV;
> +
> +        dev = &pdev->dev;
> +        data->id = of_alias_get_id(dev->of_node, "tmuctrl");
> +        if (data->id < 0)
> +                data->id = 0;
> +
> +        data->irq = irq_of_parse_and_map(dev->of_node, 0);
> +        if (data->irq <= 0) {
> +                dev_warn(dev, "failed to get IRQ\n");
> +                return -ENODEV;
> +        }
> +
> +        if (of_address_to_resource(dev->of_node, 0, &res)) {
> +                dev_warn(dev, "failed to get Resource 0\n");
> +                return -ENODEV;
> +        }
> +
> +        data->base = devm_ioremap(dev, res.start, resource_size(&res));
> +        if (!data->base) {
> +                dev_warn(dev, "Failed to ioremap memory\n");
> +                return -EADDRNOTAVAIL;
> +        }
> +
> +        of_property_read_u32(dev->of_node, "remote_sensors",
> +                        &data->nr_remote);
> +
> +        data->cal_type = readl(data->base + ARTPEC8_TMU_REG_TRIMINFO) >>
> +                ARTPEC8_CALIB_SEL_SHIFT;
> +
> +        return 0;
> +}
> +
> +static void artpec8_tmu_work(struct work_struct *work)
> +{
> +        int i;
> +        u32 inten, intpend, rise, fall;
> +        struct artpec8_sensor *sensor;
> +        struct artpec8_tmu_data *data = container_of(work,
> +                        struct artpec8_tmu_data, irq_work);
> +
> +        for (i = 0; i < data->nr_remote; i++)
> +                thermal_zone_device_update(data->sensor[i].tzd,
> +                                THERMAL_EVENT_UNSPECIFIED);
> +
> +        mutex_lock(&data->lock);
> +        for (i = 0; i < data->nr_remote; i++) {
> +                sensor = &data->sensor[i];
> +                intpend = readl(data->base + sensor->intpend);
> +
> +                if (intpend) {
> +                        fall = intpend & INTPEND_FALL_MASK;
> +                        rise = intpend & INTPEND_RISE_MASK;
> +
> +                        if (fall) {
> +                                inten = readl(data->base + sensor->inten) &
> +                                        (~fall);
> +                                inten |= fall >> ARTPEC8_INTEN_FALL0_SHIFT;
> +                                writel(inten, data->base + sensor->inten);
> +                        }
> +
> +                        if (rise) {
> +                                inten = readl(data->base + sensor->inten) &
> +                                        (~rise);
> +                                inten |= (rise << ARTPEC8_INTEN_FALL0_SHIFT) |
> +                                        min_t(u32, rise << 1, BIT(sensor->ntrip - 1)) |
> +                                        (rise >> 1);
> +                                writel(inten, data->base + sensor->inten);
> +                        }
> +                        writel(intpend, data->base + sensor->intpend);
> +                }
> +        }
> +        mutex_unlock(&data->lock);
> +}
> +
> +static int artpec8_get_temp(void *p, int *temp)
> +{
> +        struct artpec8_sensor *sensor = p;
> +        struct artpec8_tmu_data *data;
> +        bool enabled, valid;
> +        u16 value;
> +        int ret = 0;
> +
> +        if (!sensor)
> +                return -EINVAL;
> +
> +        data = sensor->tmudev;
> +        if (!data)
> +                return -EINVAL;
> +
> +        enabled = readl(data->base + ARTPEC8_TMU_REG_CONTROL) & 0x1;
> +        valid = readl(data->base + ARTPEC8_TMU_REG_STATUS) & 0xf0;
> +        if (!enabled || !valid)
> +                return -EAGAIN;
> +
> +        mutex_lock(&data->lock);
> +
> +        value = (readl(data->base + sensor->temp_offset) >>
> +                sensor->temp_reg_shift) & ARTPEC8_TMU_TEMP_MASK;
> +        *temp = code_to_temp(sensor, value) * MCELSIUS;
> +
> +        mutex_unlock(&data->lock);
> +
> +        return ret;
> +}
> +
> +static int artpec8_tmu_set_emulation(void *p, int temp)
> +{
> +        struct artpec8_sensor *sensor = p;
> +        struct artpec8_tmu_data *data = sensor->tmudev;
> +        u32 temp_code, econ;
> +
> +        temp /= MCELSIUS;
> +
> +        mutex_lock(&data->lock);
> +
> +        temp_code = temp_to_code(sensor, temp);
> +        econ = ARTPEC8_EMUL_NEXT_TIME | temp_code << ARTPEC8_EMUL_DATA_SHIFT |
> +                ARTPEC8_EMUL_EN;
> +        writel(econ, data->base + ARTPEC8_TMU_REG_EMUL_CON);
> +
> +        mutex_unlock(&data->lock);
> +
> +        return 0;
> +}
> +
> +static const struct thermal_zone_of_device_ops artpec8_ops = {
> +        .get_temp = artpec8_get_temp,
> +        .set_emul_temp = artpec8_tmu_set_emulation,
> +};
> +
> +static void samsung_tmu_control(struct platform_device *pdev, bool on)
> +{
> +        struct artpec8_tmu_data *data = platform_get_drvdata(pdev);
> +
> +        mutex_lock(&data->lock);
> +        artpec8_tmu_control(pdev, on);
> +        mutex_unlock(&data->lock);
> +}
> +
> +static int artpec8_map_sensor_data(struct artpec8_tmu_data *data,
> +                int sensor_idx, struct artpec8_sensor *sensor)
> +{
> +        int id = sensor_idx;
> +
> +        sensor->id = id;
> +        sensor->tmudev = data;
> +        sensor->trim_offset = artpec8_sensors[id].trim_offset;
> +        sensor->temp_offset = artpec8_sensors[id].temp_offset;
> +        sensor->temp_reg_shift = artpec8_sensors[id].temp_reg_shift;
> +        sensor->rise_offset = artpec8_sensors[id].rise_offset;
> +        sensor->fall_offset = artpec8_sensors[id].fall_offset;
> +        sensor->past_offset = artpec8_sensors[id].past_offset;
> +        sensor->inten = artpec8_sensors[id].inten;
> +        sensor->intpend = artpec8_sensors[id].intpend;
> +        sensor->triminfo_25 = readl(data->base + sensor->trim_offset) &
> +                ARTPEC8_TMU_TEMP_MASK;
> +        sensor->triminfo_105 = (readl(data->base + sensor->trim_offset) >>
> +                ARTPEC8_TRIMINFO_105_SHIFT) & ARTPEC8_TMU_TEMP_MASK;
> +
> +        return 0;
> +}
> +
> +static int artpec8_register_tzd(struct platform_device *pdev)
> +{
> +        struct artpec8_tmu_data *data = platform_get_drvdata(pdev);
> +        struct artpec8_sensor *sensor;
> +        struct device *dev = &pdev->dev;
> +        int sensor_idx, ret = 0;
> +        struct thermal_zone_device *tzd;
> +        const struct thermal_trip *trips;
> +
> +        for (sensor_idx = 0; sensor_idx < data->nr_remote; sensor_idx++) {
> +                sensor = &data->sensor[sensor_idx];
> +
> +                ret = artpec8_map_sensor_data(data, sensor_idx, sensor);
> +                if (ret)
> +                        break;
> +
> +                tzd = devm_thermal_zone_of_sensor_register(dev,
> +                                sensor_idx, sensor, &artpec8_ops);
> +                if (IS_ERR(tzd))
> +                        continue;
> +
> +                sensor->tzd = tzd;
> +                trips = of_thermal_get_trip_points(tzd);
> +                if (!trips) {
> +                        dev_warn(dev,
> +                                "Cannot get trip points from device tree!\n");
> +                        ret = -ENODEV;
> +                        break;
> +                }
> +                sensor->ntrip = of_thermal_get_ntrips(tzd);
> +        }
> +
> +        return ret;
> +}
> +
> +static int artpec8_sensor_initialize(struct artpec8_tmu_data *data,
> +                int sensor_idx)
> +{
> +        struct thermal_zone_device *tzd;
> +        struct artpec8_sensor *sensor;
> +        int ret = 0, trip, temp, hyst;
> +
> +        sensor = &data->sensor[sensor_idx];
> +        if (!sensor)
> +                return -EINVAL;
> +
> +        tzd = sensor->tzd;
> +        for (trip = 0; trip < sensor->ntrip; trip++) {
> +                ret = tzd->ops->get_trip_temp(tzd, trip, &temp);
> +                if (ret)
> +                        break;
> +
> +                temp /= MCELSIUS;
> +                artpec8_tmu_set_trip_temp(data, trip, temp, sensor_idx);
> +
> +                ret = tzd->ops->get_trip_hyst(tzd, trip, &hyst);
> +                if (ret)
> +                        break;
> +
> +                hyst /= MCELSIUS;
> +                artpec8_tmu_set_trip_hyst(data, trip, temp, hyst, sensor_idx);
> +        }
> +        artpec8_tmu_clear_irqs(data, sensor_idx);
> +
> +        return ret;
> +}
> +
> +static int artpec8_tmu_initialize(struct platform_device *pdev)
> +{
> +        struct artpec8_tmu_data *data = platform_get_drvdata(pdev);
> +        int ret = 0, sensor_idx;
> +
> +        mutex_lock(&data->lock);
> +
> +        for (sensor_idx = 0; sensor_idx < data->nr_remote; sensor_idx++) {
> +                if (!readb(data->base + ARTPEC8_TMU_REG_STATUS)) {
> +                        ret = -EBUSY;
> +                        break;
> +                }
> +
> +                ret = artpec8_sensor_initialize(data, sensor_idx);
> +                if (ret)
> +                        break;
> +        }
> +
> +        mutex_unlock(&data->lock);
> +
> +        return ret;
> +}
> +
> +static irqreturn_t artpec8_tmu_irq(int irq, void *id)
> +{
> +        struct artpec8_tmu_data *data = id;
> +
> +        disable_irq_nosync(irq);
> +        schedule_work(&data->irq_work);
> +        enable_irq(data->irq);
> +
> +        return IRQ_HANDLED;
> +}
> +
> +static const struct of_device_id artpec8_tmu_match[] = {
> +        { .compatible = "axis,artpec8-tmu", },
> +        {},
> +};
> +MODULE_DEVICE_TABLE(of, artpec8_tmu_match);
> +
> +static int artpec8_tmu_probe(struct platform_device *pdev)
> +{
> +        int ret, sensor_idx, nr_remote;
> +        struct device *dev;
> +        const struct of_device_id *id;
> +        struct artpec8_tmu_data *data;
> +
> +        if (pdev->dev.of_node)
> +                dev = &pdev->dev;
> +        else
> +                dev = pdev->dev.parent;
> +
> +        id = of_match_node(artpec8_tmu_match, dev->of_node);
> +        if (id) {
> +                data = (struct artpec8_tmu_data *)id->data;
> +        } else {
> +                dev_warn(dev, "dev id error\n");
> +                return -EINVAL;
> +        }
> +
> +        of_property_read_u32(dev->of_node, "remote_sensors", &nr_remote);
> +        data = devm_kzalloc(dev, sizeof(struct artpec8_tmu_data) +
> +                        (sizeof(struct artpec8_sensor) * nr_remote),
> +                        GFP_KERNEL);
> +        if (!data)
> +                return -ENOMEM;
> +
> +        platform_set_drvdata(pdev, data);
> +        mutex_init(&data->lock);
> +
> +        ret = artpec8_map_dt_data(pdev);
> +        if (ret)
> +                return ret;
> +
> +        ret = artpec8_register_tzd(pdev);
> +        if (ret)
> +                return -EINVAL;
> +
> +        ret = artpec8_tmu_initialize(pdev);
> +        if (ret) {
> +                dev_warn(dev, "Failed to initialize TMU\n");
> +                goto err_thermal;
> +        }
> +
> +        INIT_WORK(&data->irq_work, artpec8_tmu_work);
> +
> +        data->clk = devm_clk_get(dev, "tmu_apbif");
> +        if (IS_ERR(data->clk)) {
> +                dev_warn(dev, "Failed to get clock\n");
> +                ret = PTR_ERR(data->clk);
> +                return -ENODEV;
> +        }
> +
> +        ret = clk_prepare(data->clk);
> +        if (ret) {
> +                dev_err(dev, "Failed to prepare clock\n");
> +                goto err_clk;
> +        }
> +
> +        ret = devm_request_irq(dev, data->irq, artpec8_tmu_irq,
> +                IRQF_TRIGGER_RISING | IRQF_SHARED, dev_name(dev), data);
> +        if (ret) {
> +                dev_warn(dev, "Failed to request irq: %d\n", data->irq);
> +                goto err_thermal;
> +        }
> +
> +        samsung_tmu_control(pdev, true);
> +
> +        return 0;
> +
> +err_thermal:
> +        for (sensor_idx = 0; sensor_idx < data->nr_remote; sensor_idx++)
> +                thermal_zone_of_sensor_unregister(dev,
> +                                data->sensor[sensor_idx].tzd);
> +err_clk:
> +        clk_unprepare(data->clk);
> +
> +        return ret;
> +}
> +
> +#ifdef CONFIG_PM_SLEEP
> +static int artpec8_tmu_suspend(struct device *dev)
> +{
> +        samsung_tmu_control(to_platform_device(dev), false);
> +
> +        return 0;
> +}
> +
> +static int artpec8_tmu_resume(struct device *dev)
> +{
> +        samsung_tmu_control(to_platform_device(dev), true);
> +
> +        return 0;
> +}
> +
> +static SIMPLE_DEV_PM_OPS(artpec8_tmu_pm,
> +                         artpec8_tmu_suspend, artpec8_tmu_resume);
> +#define ARTPEC8_TMU_PM        (&artpec8_tmu_pm)
> +#else
> +#define ARTPEC8_TMU_PM        NULL
> +#endif
> +
> +static int artpec8_tmu_remove(struct platform_device *pdev)
> +{
> +        struct artpec8_tmu_data *data = platform_get_drvdata(pdev);
> +        struct device *dev = &pdev->dev;
> +        int i;
> +
> +        for (i = 0; i < data->nr_remote; i++)
> +                thermal_zone_of_sensor_unregister(dev,
> +                                data->sensor[i].tzd);
> +        samsung_tmu_control(pdev, false);
> +
> +        clk_unprepare(data->clk);
> +
> +        return 0;
> +}
> +
> +static struct platform_driver artpec8_tmu_driver = {
> +        .driver = {
> +                .name   = "artpec8-tmu",
> +                .pm     = ARTPEC8_TMU_PM,
> +                .of_match_table = artpec8_tmu_match,
> +        },
> +        .probe = artpec8_tmu_probe,
> +        .remove        = artpec8_tmu_remove,
> +};
> +
> +module_platform_driver(artpec8_tmu_driver);
> +
> +MODULE_DESCRIPTION("ARTPEC8 TMU Driver");
> +MODULE_AUTHOR("Sangmin Kim <hypmean.kim@xxxxxxxxxxx>");
> +MODULE_LICENSE("GPL");
> +MODULE_ALIAS("platform:artpec8-tmu");
> --
> 2.9.5
>
>



-- 
Regards,
Alim



[Index of Archives]     [Linux SoC Development]     [Linux Rockchip Development]     [Linux for Synopsys ARC Processors]    
  • [Linux on Unisoc (RDA Micro) SoCs]     [Linux Actions SoC]     [Linux USB Development]     [Video for Linux]     [Linux Audio Users]     [Linux SCSI]     [Yosemite News]

  •   Powered by Linux