Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-2.0-only
0002 /*
0003  * Broadcom STB AVS TMON thermal sensor driver
0004  *
0005  * Copyright (c) 2015-2017 Broadcom
0006  */
0007 
0008 #define DRV_NAME    "brcmstb_thermal"
0009 
0010 #define pr_fmt(fmt) DRV_NAME ": " fmt
0011 
0012 #include <linux/bitops.h>
0013 #include <linux/device.h>
0014 #include <linux/err.h>
0015 #include <linux/io.h>
0016 #include <linux/irqreturn.h>
0017 #include <linux/interrupt.h>
0018 #include <linux/kernel.h>
0019 #include <linux/module.h>
0020 #include <linux/platform_device.h>
0021 #include <linux/of_device.h>
0022 #include <linux/thermal.h>
0023 
0024 #define AVS_TMON_STATUS         0x00
0025  #define AVS_TMON_STATUS_valid_msk  BIT(11)
0026  #define AVS_TMON_STATUS_data_msk   GENMASK(10, 1)
0027  #define AVS_TMON_STATUS_data_shift 1
0028 
0029 #define AVS_TMON_EN_OVERTEMP_RESET  0x04
0030  #define AVS_TMON_EN_OVERTEMP_RESET_msk BIT(0)
0031 
0032 #define AVS_TMON_RESET_THRESH       0x08
0033  #define AVS_TMON_RESET_THRESH_msk  GENMASK(10, 1)
0034  #define AVS_TMON_RESET_THRESH_shift    1
0035 
0036 #define AVS_TMON_INT_IDLE_TIME      0x10
0037 
0038 #define AVS_TMON_EN_TEMP_INT_SRCS   0x14
0039  #define AVS_TMON_EN_TEMP_INT_SRCS_high BIT(1)
0040  #define AVS_TMON_EN_TEMP_INT_SRCS_low  BIT(0)
0041 
0042 #define AVS_TMON_INT_THRESH     0x18
0043  #define AVS_TMON_INT_THRESH_high_msk   GENMASK(26, 17)
0044  #define AVS_TMON_INT_THRESH_high_shift 17
0045  #define AVS_TMON_INT_THRESH_low_msk    GENMASK(10, 1)
0046  #define AVS_TMON_INT_THRESH_low_shift  1
0047 
0048 #define AVS_TMON_TEMP_INT_CODE      0x1c
0049 #define AVS_TMON_TP_TEST_ENABLE     0x20
0050 
0051 /* Default coefficients */
0052 #define AVS_TMON_TEMP_SLOPE     487
0053 #define AVS_TMON_TEMP_OFFSET        410040
0054 
0055 /* HW related temperature constants */
0056 #define AVS_TMON_TEMP_MAX       0x3ff
0057 #define AVS_TMON_TEMP_MIN       -88161
0058 #define AVS_TMON_TEMP_MASK      AVS_TMON_TEMP_MAX
0059 
0060 enum avs_tmon_trip_type {
0061     TMON_TRIP_TYPE_LOW = 0,
0062     TMON_TRIP_TYPE_HIGH,
0063     TMON_TRIP_TYPE_RESET,
0064     TMON_TRIP_TYPE_MAX,
0065 };
0066 
0067 struct avs_tmon_trip {
0068     /* HW bit to enable the trip */
0069     u32 enable_offs;
0070     u32 enable_mask;
0071 
0072     /* HW field to read the trip temperature */
0073     u32 reg_offs;
0074     u32 reg_msk;
0075     int reg_shift;
0076 };
0077 
0078 static struct avs_tmon_trip avs_tmon_trips[] = {
0079     /* Trips when temperature is below threshold */
0080     [TMON_TRIP_TYPE_LOW] = {
0081         .enable_offs    = AVS_TMON_EN_TEMP_INT_SRCS,
0082         .enable_mask    = AVS_TMON_EN_TEMP_INT_SRCS_low,
0083         .reg_offs   = AVS_TMON_INT_THRESH,
0084         .reg_msk    = AVS_TMON_INT_THRESH_low_msk,
0085         .reg_shift  = AVS_TMON_INT_THRESH_low_shift,
0086     },
0087     /* Trips when temperature is above threshold */
0088     [TMON_TRIP_TYPE_HIGH] = {
0089         .enable_offs    = AVS_TMON_EN_TEMP_INT_SRCS,
0090         .enable_mask    = AVS_TMON_EN_TEMP_INT_SRCS_high,
0091         .reg_offs   = AVS_TMON_INT_THRESH,
0092         .reg_msk    = AVS_TMON_INT_THRESH_high_msk,
0093         .reg_shift  = AVS_TMON_INT_THRESH_high_shift,
0094     },
0095     /* Automatically resets chip when above threshold */
0096     [TMON_TRIP_TYPE_RESET] = {
0097         .enable_offs    = AVS_TMON_EN_OVERTEMP_RESET,
0098         .enable_mask    = AVS_TMON_EN_OVERTEMP_RESET_msk,
0099         .reg_offs   = AVS_TMON_RESET_THRESH,
0100         .reg_msk    = AVS_TMON_RESET_THRESH_msk,
0101         .reg_shift  = AVS_TMON_RESET_THRESH_shift,
0102     },
0103 };
0104 
0105 struct brcmstb_thermal_params {
0106     unsigned int offset;
0107     unsigned int mult;
0108     const struct thermal_zone_of_device_ops *of_ops;
0109 };
0110 
0111 struct brcmstb_thermal_priv {
0112     void __iomem *tmon_base;
0113     struct device *dev;
0114     struct thermal_zone_device *thermal;
0115     /* Process specific thermal parameters used for calculations */
0116     const struct brcmstb_thermal_params *temp_params;
0117 };
0118 
0119 /* Convert a HW code to a temperature reading (millidegree celsius) */
0120 static inline int avs_tmon_code_to_temp(struct brcmstb_thermal_priv *priv,
0121                     u32 code)
0122 {
0123     int offset = priv->temp_params->offset;
0124     int mult = priv->temp_params->mult;
0125 
0126     return (offset - (int)((code & AVS_TMON_TEMP_MASK) * mult));
0127 }
0128 
0129 /*
0130  * Convert a temperature value (millidegree celsius) to a HW code
0131  *
0132  * @temp: temperature to convert
0133  * @low: if true, round toward the low side
0134  */
0135 static inline u32 avs_tmon_temp_to_code(struct brcmstb_thermal_priv *priv,
0136                     int temp, bool low)
0137 {
0138     int offset = priv->temp_params->offset;
0139     int mult = priv->temp_params->mult;
0140 
0141     if (temp < AVS_TMON_TEMP_MIN)
0142         return AVS_TMON_TEMP_MAX;   /* Maximum code value */
0143 
0144     if (temp >= offset)
0145         return 0;   /* Minimum code value */
0146 
0147     if (low)
0148         return (u32)(DIV_ROUND_UP(offset - temp, mult));
0149     else
0150         return (u32)((offset - temp) / mult);
0151 }
0152 
0153 static int brcmstb_get_temp(void *data, int *temp)
0154 {
0155     struct brcmstb_thermal_priv *priv = data;
0156     u32 val;
0157     long t;
0158 
0159     val = __raw_readl(priv->tmon_base + AVS_TMON_STATUS);
0160 
0161     if (!(val & AVS_TMON_STATUS_valid_msk)) {
0162         dev_err(priv->dev, "reading not valid\n");
0163         return -EIO;
0164     }
0165 
0166     val = (val & AVS_TMON_STATUS_data_msk) >> AVS_TMON_STATUS_data_shift;
0167 
0168     t = avs_tmon_code_to_temp(priv, val);
0169     if (t < 0)
0170         *temp = 0;
0171     else
0172         *temp = t;
0173 
0174     return 0;
0175 }
0176 
0177 static void avs_tmon_trip_enable(struct brcmstb_thermal_priv *priv,
0178                  enum avs_tmon_trip_type type, int en)
0179 {
0180     struct avs_tmon_trip *trip = &avs_tmon_trips[type];
0181     u32 val = __raw_readl(priv->tmon_base + trip->enable_offs);
0182 
0183     dev_dbg(priv->dev, "%sable trip, type %d\n", en ? "en" : "dis", type);
0184 
0185     if (en)
0186         val |= trip->enable_mask;
0187     else
0188         val &= ~trip->enable_mask;
0189 
0190     __raw_writel(val, priv->tmon_base + trip->enable_offs);
0191 }
0192 
0193 static int avs_tmon_get_trip_temp(struct brcmstb_thermal_priv *priv,
0194                   enum avs_tmon_trip_type type)
0195 {
0196     struct avs_tmon_trip *trip = &avs_tmon_trips[type];
0197     u32 val = __raw_readl(priv->tmon_base + trip->reg_offs);
0198 
0199     val &= trip->reg_msk;
0200     val >>= trip->reg_shift;
0201 
0202     return avs_tmon_code_to_temp(priv, val);
0203 }
0204 
0205 static void avs_tmon_set_trip_temp(struct brcmstb_thermal_priv *priv,
0206                    enum avs_tmon_trip_type type,
0207                    int temp)
0208 {
0209     struct avs_tmon_trip *trip = &avs_tmon_trips[type];
0210     u32 val, orig;
0211 
0212     dev_dbg(priv->dev, "set temp %d to %d\n", type, temp);
0213 
0214     /* round toward low temp for the low interrupt */
0215     val = avs_tmon_temp_to_code(priv, temp,
0216                     type == TMON_TRIP_TYPE_LOW);
0217 
0218     val <<= trip->reg_shift;
0219     val &= trip->reg_msk;
0220 
0221     orig = __raw_readl(priv->tmon_base + trip->reg_offs);
0222     orig &= ~trip->reg_msk;
0223     orig |= val;
0224     __raw_writel(orig, priv->tmon_base + trip->reg_offs);
0225 }
0226 
0227 static int avs_tmon_get_intr_temp(struct brcmstb_thermal_priv *priv)
0228 {
0229     u32 val;
0230 
0231     val = __raw_readl(priv->tmon_base + AVS_TMON_TEMP_INT_CODE);
0232     return avs_tmon_code_to_temp(priv, val);
0233 }
0234 
0235 static irqreturn_t brcmstb_tmon_irq_thread(int irq, void *data)
0236 {
0237     struct brcmstb_thermal_priv *priv = data;
0238     int low, high, intr;
0239 
0240     low = avs_tmon_get_trip_temp(priv, TMON_TRIP_TYPE_LOW);
0241     high = avs_tmon_get_trip_temp(priv, TMON_TRIP_TYPE_HIGH);
0242     intr = avs_tmon_get_intr_temp(priv);
0243 
0244     dev_dbg(priv->dev, "low/intr/high: %d/%d/%d\n",
0245             low, intr, high);
0246 
0247     /* Disable high-temp until next threshold shift */
0248     if (intr >= high)
0249         avs_tmon_trip_enable(priv, TMON_TRIP_TYPE_HIGH, 0);
0250     /* Disable low-temp until next threshold shift */
0251     if (intr <= low)
0252         avs_tmon_trip_enable(priv, TMON_TRIP_TYPE_LOW, 0);
0253 
0254     /*
0255      * Notify using the interrupt temperature, in case the temperature
0256      * changes before it can next be read out
0257      */
0258     thermal_zone_device_update(priv->thermal, intr);
0259 
0260     return IRQ_HANDLED;
0261 }
0262 
0263 static int brcmstb_set_trips(void *data, int low, int high)
0264 {
0265     struct brcmstb_thermal_priv *priv = data;
0266 
0267     dev_dbg(priv->dev, "set trips %d <--> %d\n", low, high);
0268 
0269     /*
0270      * Disable low-temp if "low" is too small. As per thermal framework
0271      * API, we use -INT_MAX rather than INT_MIN.
0272      */
0273     if (low <= -INT_MAX) {
0274         avs_tmon_trip_enable(priv, TMON_TRIP_TYPE_LOW, 0);
0275     } else {
0276         avs_tmon_set_trip_temp(priv, TMON_TRIP_TYPE_LOW, low);
0277         avs_tmon_trip_enable(priv, TMON_TRIP_TYPE_LOW, 1);
0278     }
0279 
0280     /* Disable high-temp if "high" is too big. */
0281     if (high == INT_MAX) {
0282         avs_tmon_trip_enable(priv, TMON_TRIP_TYPE_HIGH, 0);
0283     } else {
0284         avs_tmon_set_trip_temp(priv, TMON_TRIP_TYPE_HIGH, high);
0285         avs_tmon_trip_enable(priv, TMON_TRIP_TYPE_HIGH, 1);
0286     }
0287 
0288     return 0;
0289 }
0290 
0291 static const struct thermal_zone_of_device_ops brcmstb_16nm_of_ops = {
0292     .get_temp   = brcmstb_get_temp,
0293 };
0294 
0295 static const struct brcmstb_thermal_params brcmstb_16nm_params = {
0296     .offset = 457829,
0297     .mult   = 557,
0298     .of_ops = &brcmstb_16nm_of_ops,
0299 };
0300 
0301 static const struct thermal_zone_of_device_ops brcmstb_28nm_of_ops = {
0302     .get_temp   = brcmstb_get_temp,
0303     .set_trips  = brcmstb_set_trips,
0304 };
0305 
0306 static const struct brcmstb_thermal_params brcmstb_28nm_params = {
0307     .offset = 410040,
0308     .mult   = 487,
0309     .of_ops = &brcmstb_28nm_of_ops,
0310 };
0311 
0312 static const struct of_device_id brcmstb_thermal_id_table[] = {
0313     { .compatible = "brcm,avs-tmon-bcm7216", .data = &brcmstb_16nm_params },
0314     { .compatible = "brcm,avs-tmon", .data = &brcmstb_28nm_params },
0315     {},
0316 };
0317 MODULE_DEVICE_TABLE(of, brcmstb_thermal_id_table);
0318 
0319 static int brcmstb_thermal_probe(struct platform_device *pdev)
0320 {
0321     const struct thermal_zone_of_device_ops *of_ops;
0322     struct thermal_zone_device *thermal;
0323     struct brcmstb_thermal_priv *priv;
0324     struct resource *res;
0325     int irq, ret;
0326 
0327     priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL);
0328     if (!priv)
0329         return -ENOMEM;
0330 
0331     priv->temp_params = of_device_get_match_data(&pdev->dev);
0332     if (!priv->temp_params)
0333         return -EINVAL;
0334 
0335     res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
0336     priv->tmon_base = devm_ioremap_resource(&pdev->dev, res);
0337     if (IS_ERR(priv->tmon_base))
0338         return PTR_ERR(priv->tmon_base);
0339 
0340     priv->dev = &pdev->dev;
0341     platform_set_drvdata(pdev, priv);
0342     of_ops = priv->temp_params->of_ops;
0343 
0344     thermal = devm_thermal_zone_of_sensor_register(&pdev->dev, 0, priv,
0345                                of_ops);
0346     if (IS_ERR(thermal)) {
0347         ret = PTR_ERR(thermal);
0348         dev_err(&pdev->dev, "could not register sensor: %d\n", ret);
0349         return ret;
0350     }
0351 
0352     priv->thermal = thermal;
0353 
0354     irq = platform_get_irq_optional(pdev, 0);
0355     if (irq >= 0) {
0356         ret = devm_request_threaded_irq(&pdev->dev, irq, NULL,
0357                         brcmstb_tmon_irq_thread,
0358                         IRQF_ONESHOT,
0359                         DRV_NAME, priv);
0360         if (ret < 0) {
0361             dev_err(&pdev->dev, "could not request IRQ: %d\n", ret);
0362             return ret;
0363         }
0364     }
0365 
0366     dev_info(&pdev->dev, "registered AVS TMON of-sensor driver\n");
0367 
0368     return 0;
0369 }
0370 
0371 static struct platform_driver brcmstb_thermal_driver = {
0372     .probe = brcmstb_thermal_probe,
0373     .driver = {
0374         .name = DRV_NAME,
0375         .of_match_table = brcmstb_thermal_id_table,
0376     },
0377 };
0378 module_platform_driver(brcmstb_thermal_driver);
0379 
0380 MODULE_LICENSE("GPL v2");
0381 MODULE_AUTHOR("Brian Norris");
0382 MODULE_DESCRIPTION("Broadcom STB AVS TMON thermal driver");