0001
0002
0003
0004
0005
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
0052 #define AVS_TMON_TEMP_SLOPE 487
0053 #define AVS_TMON_TEMP_OFFSET 410040
0054
0055
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
0069 u32 enable_offs;
0070 u32 enable_mask;
0071
0072
0073 u32 reg_offs;
0074 u32 reg_msk;
0075 int reg_shift;
0076 };
0077
0078 static struct avs_tmon_trip avs_tmon_trips[] = {
0079
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
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
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
0116 const struct brcmstb_thermal_params *temp_params;
0117 };
0118
0119
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
0131
0132
0133
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;
0143
0144 if (temp >= offset)
0145 return 0;
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
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
0248 if (intr >= high)
0249 avs_tmon_trip_enable(priv, TMON_TRIP_TYPE_HIGH, 0);
0250
0251 if (intr <= low)
0252 avs_tmon_trip_enable(priv, TMON_TRIP_TYPE_LOW, 0);
0253
0254
0255
0256
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
0271
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
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");