Fabio Estevam | 2dfef65 | 2018-07-28 16:17:51 -0300 | [diff] [blame] | 1 | // SPDX-License-Identifier: GPL-2.0 |
| 2 | // |
| 3 | // Copyright 2016 Freescale Semiconductor, Inc. |
Jia Hongtao | 4352844 | 2016-06-30 11:08:38 +0800 | [diff] [blame] | 4 | |
Anson Huang | 5190404 | 2019-07-30 10:21:22 +0800 | [diff] [blame] | 5 | #include <linux/clk.h> |
Jia Hongtao | 4352844 | 2016-06-30 11:08:38 +0800 | [diff] [blame] | 6 | #include <linux/err.h> |
| 7 | #include <linux/io.h> |
Anson Huang | ce68eec | 2020-03-11 13:07:32 +0800 | [diff] [blame] | 8 | #include <linux/module.h> |
Jia Hongtao | 4352844 | 2016-06-30 11:08:38 +0800 | [diff] [blame] | 9 | #include <linux/of.h> |
Anson Huang | ce68eec | 2020-03-11 13:07:32 +0800 | [diff] [blame] | 10 | #include <linux/platform_device.h> |
Andrey Smirnov | 4316237 | 2019-12-10 08:41:49 -0800 | [diff] [blame] | 11 | #include <linux/regmap.h> |
| 12 | #include <linux/sizes.h> |
Jia Hongtao | 4352844 | 2016-06-30 11:08:38 +0800 | [diff] [blame] | 13 | #include <linux/thermal.h> |
Yuantian Tang | 47fa116 | 2020-05-26 14:02:12 +0800 | [diff] [blame] | 14 | #include <linux/units.h> |
Jia Hongtao | 4352844 | 2016-06-30 11:08:38 +0800 | [diff] [blame] | 15 | |
| 16 | #include "thermal_core.h" |
Andrey Smirnov | fd84330 | 2019-12-10 08:41:53 -0800 | [diff] [blame] | 17 | #include "thermal_hwmon.h" |
Jia Hongtao | 4352844 | 2016-06-30 11:08:38 +0800 | [diff] [blame] | 18 | |
Yuantian Tang | 9809797 | 2019-10-11 10:05:34 +0800 | [diff] [blame] | 19 | #define SITES_MAX 16 |
| 20 | #define TMR_DISABLE 0x0 |
| 21 | #define TMR_ME 0x80000000 |
| 22 | #define TMR_ALPF 0x0c000000 |
| 23 | #define TMR_ALPF_V2 0x03000000 |
| 24 | #define TMTMIR_DEFAULT 0x0000000f |
| 25 | #define TIER_DISABLE 0x0 |
| 26 | #define TEUMR0_V2 0x51009c00 |
Yuantian Tang | 47fa116 | 2020-05-26 14:02:12 +0800 | [diff] [blame] | 27 | #define TMSARA_V2 0xe |
Yuantian Tang | 9809797 | 2019-10-11 10:05:34 +0800 | [diff] [blame] | 28 | #define TMU_VER1 0x1 |
| 29 | #define TMU_VER2 0x2 |
Jia Hongtao | 4352844 | 2016-06-30 11:08:38 +0800 | [diff] [blame] | 30 | |
Andrey Smirnov | 4316237 | 2019-12-10 08:41:49 -0800 | [diff] [blame] | 31 | #define REGS_TMR 0x000 /* Mode Register */ |
| 32 | #define TMR_DISABLE 0x0 |
| 33 | #define TMR_ME 0x80000000 |
| 34 | #define TMR_ALPF 0x0c000000 |
Andrey Smirnov | 45038e0 | 2019-12-10 08:41:50 -0800 | [diff] [blame] | 35 | #define TMR_MSITE_ALL GENMASK(15, 0) |
Jia Hongtao | 4352844 | 2016-06-30 11:08:38 +0800 | [diff] [blame] | 36 | |
Andrey Smirnov | 4316237 | 2019-12-10 08:41:49 -0800 | [diff] [blame] | 37 | #define REGS_TMTMIR 0x008 /* Temperature measurement interval Register */ |
| 38 | #define TMTMIR_DEFAULT 0x0000000f |
Yuantian Tang | 9809797 | 2019-10-11 10:05:34 +0800 | [diff] [blame] | 39 | |
Andrey Smirnov | 4316237 | 2019-12-10 08:41:49 -0800 | [diff] [blame] | 40 | #define REGS_V2_TMSR 0x008 /* monitor site register */ |
| 41 | |
| 42 | #define REGS_V2_TMTMIR 0x00c /* Temperature measurement interval Register */ |
| 43 | |
| 44 | #define REGS_TIER 0x020 /* Interrupt Enable Register */ |
| 45 | #define TIER_DISABLE 0x0 |
| 46 | |
| 47 | |
| 48 | #define REGS_TTCFGR 0x080 /* Temperature Configuration Register */ |
| 49 | #define REGS_TSCFGR 0x084 /* Sensor Configuration Register */ |
| 50 | |
| 51 | #define REGS_TRITSR(n) (0x100 + 16 * (n)) /* Immediate Temperature |
| 52 | * Site Register |
| 53 | */ |
Andrey Smirnov | 36564d7 | 2019-12-10 08:41:51 -0800 | [diff] [blame] | 54 | #define TRITSR_V BIT(31) |
Yuantian Tang | 47fa116 | 2020-05-26 14:02:12 +0800 | [diff] [blame] | 55 | #define REGS_V2_TMSAR(n) (0x304 + 16 * (n)) /* TMU monitoring |
| 56 | * site adjustment register |
| 57 | */ |
Andrey Smirnov | 4316237 | 2019-12-10 08:41:49 -0800 | [diff] [blame] | 58 | #define REGS_TTRnCR(n) (0xf10 + 4 * (n)) /* Temperature Range n |
| 59 | * Control Register |
| 60 | */ |
| 61 | #define REGS_IPBRR(n) (0xbf8 + 4 * (n)) /* IP Block Revision |
| 62 | * Register n |
| 63 | */ |
| 64 | #define REGS_V2_TEUMR(n) (0xf00 + 4 * (n)) |
Jia Hongtao | 4352844 | 2016-06-30 11:08:38 +0800 | [diff] [blame] | 65 | |
| 66 | /* |
| 67 | * Thermal zone data |
| 68 | */ |
Yuantian Tang | 7797ff4 | 2019-01-18 13:39:40 +0800 | [diff] [blame] | 69 | struct qoriq_sensor { |
Yuantian Tang | 7797ff4 | 2019-01-18 13:39:40 +0800 | [diff] [blame] | 70 | int id; |
| 71 | }; |
| 72 | |
Jia Hongtao | 4352844 | 2016-06-30 11:08:38 +0800 | [diff] [blame] | 73 | struct qoriq_tmu_data { |
Yuantian Tang | 9809797 | 2019-10-11 10:05:34 +0800 | [diff] [blame] | 74 | int ver; |
Andrey Smirnov | 4316237 | 2019-12-10 08:41:49 -0800 | [diff] [blame] | 75 | struct regmap *regmap; |
Anson Huang | 5190404 | 2019-07-30 10:21:22 +0800 | [diff] [blame] | 76 | struct clk *clk; |
Andrey Smirnov | b319da1 | 2019-12-10 08:41:45 -0800 | [diff] [blame] | 77 | struct qoriq_sensor sensor[SITES_MAX]; |
Jia Hongtao | 4352844 | 2016-06-30 11:08:38 +0800 | [diff] [blame] | 78 | }; |
| 79 | |
Andrey Smirnov | b319da1 | 2019-12-10 08:41:45 -0800 | [diff] [blame] | 80 | static struct qoriq_tmu_data *qoriq_sensor_to_data(struct qoriq_sensor *s) |
| 81 | { |
| 82 | return container_of(s, struct qoriq_tmu_data, sensor[s->id]); |
| 83 | } |
| 84 | |
Jia Hongtao | 4352844 | 2016-06-30 11:08:38 +0800 | [diff] [blame] | 85 | static int tmu_get_temp(void *p, int *temp) |
| 86 | { |
Yuantian Tang | 7797ff4 | 2019-01-18 13:39:40 +0800 | [diff] [blame] | 87 | struct qoriq_sensor *qsensor = p; |
Andrey Smirnov | b319da1 | 2019-12-10 08:41:45 -0800 | [diff] [blame] | 88 | struct qoriq_tmu_data *qdata = qoriq_sensor_to_data(qsensor); |
Jia Hongtao | 4352844 | 2016-06-30 11:08:38 +0800 | [diff] [blame] | 89 | u32 val; |
Andrey Smirnov | 36564d7 | 2019-12-10 08:41:51 -0800 | [diff] [blame] | 90 | /* |
| 91 | * REGS_TRITSR(id) has the following layout: |
| 92 | * |
Yuantian Tang | 47fa116 | 2020-05-26 14:02:12 +0800 | [diff] [blame] | 93 | * For TMU Rev1: |
Andrey Smirnov | 36564d7 | 2019-12-10 08:41:51 -0800 | [diff] [blame] | 94 | * 31 ... 7 6 5 4 3 2 1 0 |
| 95 | * V TEMP |
| 96 | * |
| 97 | * Where V bit signifies if the measurement is ready and is |
| 98 | * within sensor range. TEMP is an 8 bit value representing |
Yuantian Tang | 47fa116 | 2020-05-26 14:02:12 +0800 | [diff] [blame] | 99 | * temperature in Celsius. |
| 100 | |
| 101 | * For TMU Rev2: |
| 102 | * 31 ... 8 7 6 5 4 3 2 1 0 |
| 103 | * V TEMP |
| 104 | * |
| 105 | * Where V bit signifies if the measurement is ready and is |
| 106 | * within sensor range. TEMP is an 9 bit value representing |
| 107 | * temperature in KelVin. |
Andrey Smirnov | 36564d7 | 2019-12-10 08:41:51 -0800 | [diff] [blame] | 108 | */ |
| 109 | if (regmap_read_poll_timeout(qdata->regmap, |
| 110 | REGS_TRITSR(qsensor->id), |
| 111 | val, |
| 112 | val & TRITSR_V, |
| 113 | USEC_PER_MSEC, |
| 114 | 10 * USEC_PER_MSEC)) |
| 115 | return -ENODATA; |
Jia Hongtao | 4352844 | 2016-06-30 11:08:38 +0800 | [diff] [blame] | 116 | |
Yuantian Tang | 47fa116 | 2020-05-26 14:02:12 +0800 | [diff] [blame] | 117 | if (qdata->ver == TMU_VER1) |
| 118 | *temp = (val & GENMASK(7, 0)) * MILLIDEGREE_PER_DEGREE; |
| 119 | else |
| 120 | *temp = kelvin_to_millicelsius(val & GENMASK(8, 0)); |
Jia Hongtao | 4352844 | 2016-06-30 11:08:38 +0800 | [diff] [blame] | 121 | |
| 122 | return 0; |
| 123 | } |
| 124 | |
Yuantian Tang | 7797ff4 | 2019-01-18 13:39:40 +0800 | [diff] [blame] | 125 | static const struct thermal_zone_of_device_ops tmu_tz_ops = { |
| 126 | .get_temp = tmu_get_temp, |
| 127 | }; |
| 128 | |
Andrey Smirnov | 0303662 | 2019-12-10 08:41:46 -0800 | [diff] [blame] | 129 | static int qoriq_tmu_register_tmu_zone(struct device *dev, |
| 130 | struct qoriq_tmu_data *qdata) |
Jia Hongtao | 4352844 | 2016-06-30 11:08:38 +0800 | [diff] [blame] | 131 | { |
Andrey Smirnov | 45038e0 | 2019-12-10 08:41:50 -0800 | [diff] [blame] | 132 | int id; |
| 133 | |
| 134 | if (qdata->ver == TMU_VER1) { |
| 135 | regmap_write(qdata->regmap, REGS_TMR, |
| 136 | TMR_MSITE_ALL | TMR_ME | TMR_ALPF); |
| 137 | } else { |
| 138 | regmap_write(qdata->regmap, REGS_V2_TMSR, TMR_MSITE_ALL); |
| 139 | regmap_write(qdata->regmap, REGS_TMR, TMR_ME | TMR_ALPF_V2); |
| 140 | } |
Jia Hongtao | 4352844 | 2016-06-30 11:08:38 +0800 | [diff] [blame] | 141 | |
Yuantian Tang | 7797ff4 | 2019-01-18 13:39:40 +0800 | [diff] [blame] | 142 | for (id = 0; id < SITES_MAX; id++) { |
Andrey Smirnov | 11ef00f | 2019-12-10 08:41:43 -0800 | [diff] [blame] | 143 | struct thermal_zone_device *tzd; |
Andrey Smirnov | b319da1 | 2019-12-10 08:41:45 -0800 | [diff] [blame] | 144 | struct qoriq_sensor *sensor = &qdata->sensor[id]; |
Andrey Smirnov | 11ef00f | 2019-12-10 08:41:43 -0800 | [diff] [blame] | 145 | int ret; |
| 146 | |
Andrey Smirnov | d6fb056 | 2019-12-10 08:41:44 -0800 | [diff] [blame] | 147 | sensor->id = id; |
Andrey Smirnov | 11ef00f | 2019-12-10 08:41:43 -0800 | [diff] [blame] | 148 | |
Andrey Smirnov | 0303662 | 2019-12-10 08:41:46 -0800 | [diff] [blame] | 149 | tzd = devm_thermal_zone_of_sensor_register(dev, id, |
Andrey Smirnov | d6fb056 | 2019-12-10 08:41:44 -0800 | [diff] [blame] | 150 | sensor, |
Andrey Smirnov | 11ef00f | 2019-12-10 08:41:43 -0800 | [diff] [blame] | 151 | &tmu_tz_ops); |
| 152 | ret = PTR_ERR_OR_ZERO(tzd); |
| 153 | if (ret) { |
| 154 | if (ret == -ENODEV) |
Yuantian Tang | 7797ff4 | 2019-01-18 13:39:40 +0800 | [diff] [blame] | 155 | continue; |
Yuantian Tang | 7797ff4 | 2019-01-18 13:39:40 +0800 | [diff] [blame] | 156 | |
Andrey Smirnov | 45038e0 | 2019-12-10 08:41:50 -0800 | [diff] [blame] | 157 | regmap_write(qdata->regmap, REGS_TMR, TMR_DISABLE); |
| 158 | return ret; |
Yuantian Tang | 9809797 | 2019-10-11 10:05:34 +0800 | [diff] [blame] | 159 | } |
Andrey Smirnov | fd84330 | 2019-12-10 08:41:53 -0800 | [diff] [blame] | 160 | |
| 161 | if (devm_thermal_add_hwmon_sysfs(tzd)) |
| 162 | dev_warn(dev, |
| 163 | "Failed to add hwmon sysfs attributes\n"); |
| 164 | |
Yuantian Tang | 9809797 | 2019-10-11 10:05:34 +0800 | [diff] [blame] | 165 | } |
Jia Hongtao | 4352844 | 2016-06-30 11:08:38 +0800 | [diff] [blame] | 166 | |
Yuantian Tang | 7797ff4 | 2019-01-18 13:39:40 +0800 | [diff] [blame] | 167 | return 0; |
Jia Hongtao | 4352844 | 2016-06-30 11:08:38 +0800 | [diff] [blame] | 168 | } |
| 169 | |
Andrey Smirnov | 8e1cda3 | 2019-12-10 08:41:47 -0800 | [diff] [blame] | 170 | static int qoriq_tmu_calibration(struct device *dev, |
| 171 | struct qoriq_tmu_data *data) |
Jia Hongtao | 4352844 | 2016-06-30 11:08:38 +0800 | [diff] [blame] | 172 | { |
| 173 | int i, val, len; |
| 174 | u32 range[4]; |
| 175 | const u32 *calibration; |
Andrey Smirnov | 8e1cda3 | 2019-12-10 08:41:47 -0800 | [diff] [blame] | 176 | struct device_node *np = dev->of_node; |
Jia Hongtao | 4352844 | 2016-06-30 11:08:38 +0800 | [diff] [blame] | 177 | |
Yuantian Tang | 9809797 | 2019-10-11 10:05:34 +0800 | [diff] [blame] | 178 | len = of_property_count_u32_elems(np, "fsl,tmu-range"); |
| 179 | if (len < 0 || len > 4) { |
Andrey Smirnov | 8e1cda3 | 2019-12-10 08:41:47 -0800 | [diff] [blame] | 180 | dev_err(dev, "invalid range data.\n"); |
Yuantian Tang | 9809797 | 2019-10-11 10:05:34 +0800 | [diff] [blame] | 181 | return len; |
| 182 | } |
| 183 | |
| 184 | val = of_property_read_u32_array(np, "fsl,tmu-range", range, len); |
| 185 | if (val != 0) { |
Andrey Smirnov | 8e1cda3 | 2019-12-10 08:41:47 -0800 | [diff] [blame] | 186 | dev_err(dev, "failed to read range data.\n"); |
Yuantian Tang | 9809797 | 2019-10-11 10:05:34 +0800 | [diff] [blame] | 187 | return val; |
Jia Hongtao | 4352844 | 2016-06-30 11:08:38 +0800 | [diff] [blame] | 188 | } |
| 189 | |
| 190 | /* Init temperature range registers */ |
Yuantian Tang | 9809797 | 2019-10-11 10:05:34 +0800 | [diff] [blame] | 191 | for (i = 0; i < len; i++) |
Andrey Smirnov | 4316237 | 2019-12-10 08:41:49 -0800 | [diff] [blame] | 192 | regmap_write(data->regmap, REGS_TTRnCR(i), range[i]); |
Jia Hongtao | 4352844 | 2016-06-30 11:08:38 +0800 | [diff] [blame] | 193 | |
| 194 | calibration = of_get_property(np, "fsl,tmu-calibration", &len); |
| 195 | if (calibration == NULL || len % 8) { |
Andrey Smirnov | 8e1cda3 | 2019-12-10 08:41:47 -0800 | [diff] [blame] | 196 | dev_err(dev, "invalid calibration data.\n"); |
Jia Hongtao | 4352844 | 2016-06-30 11:08:38 +0800 | [diff] [blame] | 197 | return -ENODEV; |
| 198 | } |
| 199 | |
| 200 | for (i = 0; i < len; i += 8, calibration += 2) { |
| 201 | val = of_read_number(calibration, 1); |
Andrey Smirnov | 4316237 | 2019-12-10 08:41:49 -0800 | [diff] [blame] | 202 | regmap_write(data->regmap, REGS_TTCFGR, val); |
Jia Hongtao | 4352844 | 2016-06-30 11:08:38 +0800 | [diff] [blame] | 203 | val = of_read_number(calibration + 1, 1); |
Andrey Smirnov | 4316237 | 2019-12-10 08:41:49 -0800 | [diff] [blame] | 204 | regmap_write(data->regmap, REGS_TSCFGR, val); |
Jia Hongtao | 4352844 | 2016-06-30 11:08:38 +0800 | [diff] [blame] | 205 | } |
| 206 | |
| 207 | return 0; |
| 208 | } |
| 209 | |
| 210 | static void qoriq_tmu_init_device(struct qoriq_tmu_data *data) |
| 211 | { |
Yuantian Tang | 47fa116 | 2020-05-26 14:02:12 +0800 | [diff] [blame] | 212 | int i; |
| 213 | |
Jia Hongtao | 4352844 | 2016-06-30 11:08:38 +0800 | [diff] [blame] | 214 | /* Disable interrupt, using polling instead */ |
Andrey Smirnov | 4316237 | 2019-12-10 08:41:49 -0800 | [diff] [blame] | 215 | regmap_write(data->regmap, REGS_TIER, TIER_DISABLE); |
Jia Hongtao | 4352844 | 2016-06-30 11:08:38 +0800 | [diff] [blame] | 216 | |
| 217 | /* Set update_interval */ |
Andrey Smirnov | 4316237 | 2019-12-10 08:41:49 -0800 | [diff] [blame] | 218 | |
Yuantian Tang | 9809797 | 2019-10-11 10:05:34 +0800 | [diff] [blame] | 219 | if (data->ver == TMU_VER1) { |
Andrey Smirnov | 4316237 | 2019-12-10 08:41:49 -0800 | [diff] [blame] | 220 | regmap_write(data->regmap, REGS_TMTMIR, TMTMIR_DEFAULT); |
Yuantian Tang | 9809797 | 2019-10-11 10:05:34 +0800 | [diff] [blame] | 221 | } else { |
Andrey Smirnov | 4316237 | 2019-12-10 08:41:49 -0800 | [diff] [blame] | 222 | regmap_write(data->regmap, REGS_V2_TMTMIR, TMTMIR_DEFAULT); |
| 223 | regmap_write(data->regmap, REGS_V2_TEUMR(0), TEUMR0_V2); |
Yuantian Tang | 47fa116 | 2020-05-26 14:02:12 +0800 | [diff] [blame] | 224 | for (i = 0; i < SITES_MAX; i++) |
| 225 | regmap_write(data->regmap, REGS_V2_TMSAR(i), TMSARA_V2); |
Yuantian Tang | 9809797 | 2019-10-11 10:05:34 +0800 | [diff] [blame] | 226 | } |
Jia Hongtao | 4352844 | 2016-06-30 11:08:38 +0800 | [diff] [blame] | 227 | |
| 228 | /* Disable monitoring */ |
Andrey Smirnov | 4316237 | 2019-12-10 08:41:49 -0800 | [diff] [blame] | 229 | regmap_write(data->regmap, REGS_TMR, TMR_DISABLE); |
Jia Hongtao | 4352844 | 2016-06-30 11:08:38 +0800 | [diff] [blame] | 230 | } |
| 231 | |
Andrey Smirnov | 4316237 | 2019-12-10 08:41:49 -0800 | [diff] [blame] | 232 | static const struct regmap_range qoriq_yes_ranges[] = { |
| 233 | regmap_reg_range(REGS_TMR, REGS_TSCFGR), |
| 234 | regmap_reg_range(REGS_TTRnCR(0), REGS_TTRnCR(3)), |
| 235 | regmap_reg_range(REGS_V2_TEUMR(0), REGS_V2_TEUMR(2)), |
Yuantian Tang | 47fa116 | 2020-05-26 14:02:12 +0800 | [diff] [blame] | 236 | regmap_reg_range(REGS_V2_TMSAR(0), REGS_V2_TMSAR(15)), |
Andrey Smirnov | 4316237 | 2019-12-10 08:41:49 -0800 | [diff] [blame] | 237 | regmap_reg_range(REGS_IPBRR(0), REGS_IPBRR(1)), |
| 238 | /* Read only registers below */ |
| 239 | regmap_reg_range(REGS_TRITSR(0), REGS_TRITSR(15)), |
| 240 | }; |
| 241 | |
| 242 | static const struct regmap_access_table qoriq_wr_table = { |
| 243 | .yes_ranges = qoriq_yes_ranges, |
| 244 | .n_yes_ranges = ARRAY_SIZE(qoriq_yes_ranges) - 1, |
| 245 | }; |
| 246 | |
| 247 | static const struct regmap_access_table qoriq_rd_table = { |
| 248 | .yes_ranges = qoriq_yes_ranges, |
| 249 | .n_yes_ranges = ARRAY_SIZE(qoriq_yes_ranges), |
| 250 | }; |
| 251 | |
Anson Huang | 85f0b61 | 2020-03-11 13:07:31 +0800 | [diff] [blame] | 252 | static void qoriq_tmu_action(void *p) |
| 253 | { |
| 254 | struct qoriq_tmu_data *data = p; |
| 255 | |
| 256 | regmap_write(data->regmap, REGS_TMR, TMR_DISABLE); |
| 257 | clk_disable_unprepare(data->clk); |
| 258 | } |
| 259 | |
Jia Hongtao | 4352844 | 2016-06-30 11:08:38 +0800 | [diff] [blame] | 260 | static int qoriq_tmu_probe(struct platform_device *pdev) |
| 261 | { |
| 262 | int ret; |
Yuantian Tang | 9809797 | 2019-10-11 10:05:34 +0800 | [diff] [blame] | 263 | u32 ver; |
Jia Hongtao | 4352844 | 2016-06-30 11:08:38 +0800 | [diff] [blame] | 264 | struct qoriq_tmu_data *data; |
| 265 | struct device_node *np = pdev->dev.of_node; |
Andrey Smirnov | e167dc4 | 2019-12-10 08:41:42 -0800 | [diff] [blame] | 266 | struct device *dev = &pdev->dev; |
Andrey Smirnov | 4316237 | 2019-12-10 08:41:49 -0800 | [diff] [blame] | 267 | const bool little_endian = of_property_read_bool(np, "little-endian"); |
| 268 | const enum regmap_endian format_endian = |
| 269 | little_endian ? REGMAP_ENDIAN_LITTLE : REGMAP_ENDIAN_BIG; |
| 270 | const struct regmap_config regmap_config = { |
| 271 | .reg_bits = 32, |
| 272 | .val_bits = 32, |
| 273 | .reg_stride = 4, |
| 274 | .rd_table = &qoriq_rd_table, |
| 275 | .wr_table = &qoriq_wr_table, |
| 276 | .val_format_endian = format_endian, |
| 277 | .max_register = SZ_4K, |
| 278 | }; |
| 279 | void __iomem *base; |
Jia Hongtao | 4352844 | 2016-06-30 11:08:38 +0800 | [diff] [blame] | 280 | |
Andrey Smirnov | e167dc4 | 2019-12-10 08:41:42 -0800 | [diff] [blame] | 281 | data = devm_kzalloc(dev, sizeof(struct qoriq_tmu_data), |
Jia Hongtao | 4352844 | 2016-06-30 11:08:38 +0800 | [diff] [blame] | 282 | GFP_KERNEL); |
| 283 | if (!data) |
| 284 | return -ENOMEM; |
| 285 | |
Andrey Smirnov | 4316237 | 2019-12-10 08:41:49 -0800 | [diff] [blame] | 286 | base = devm_platform_ioremap_resource(pdev, 0); |
| 287 | ret = PTR_ERR_OR_ZERO(base); |
| 288 | if (ret) { |
Andrey Smirnov | e167dc4 | 2019-12-10 08:41:42 -0800 | [diff] [blame] | 289 | dev_err(dev, "Failed to get memory region\n"); |
Andrey Smirnov | 4316237 | 2019-12-10 08:41:49 -0800 | [diff] [blame] | 290 | return ret; |
| 291 | } |
| 292 | |
| 293 | data->regmap = devm_regmap_init_mmio(dev, base, ®map_config); |
| 294 | ret = PTR_ERR_OR_ZERO(data->regmap); |
| 295 | if (ret) { |
| 296 | dev_err(dev, "Failed to init regmap (%d)\n", ret); |
| 297 | return ret; |
Jia Hongtao | 4352844 | 2016-06-30 11:08:38 +0800 | [diff] [blame] | 298 | } |
| 299 | |
Andrey Smirnov | e167dc4 | 2019-12-10 08:41:42 -0800 | [diff] [blame] | 300 | data->clk = devm_clk_get_optional(dev, NULL); |
Anson Huang | 5190404 | 2019-07-30 10:21:22 +0800 | [diff] [blame] | 301 | if (IS_ERR(data->clk)) |
| 302 | return PTR_ERR(data->clk); |
| 303 | |
| 304 | ret = clk_prepare_enable(data->clk); |
| 305 | if (ret) { |
Andrey Smirnov | e167dc4 | 2019-12-10 08:41:42 -0800 | [diff] [blame] | 306 | dev_err(dev, "Failed to enable clock\n"); |
Anson Huang | 5190404 | 2019-07-30 10:21:22 +0800 | [diff] [blame] | 307 | return ret; |
| 308 | } |
| 309 | |
Anson Huang | 85f0b61 | 2020-03-11 13:07:31 +0800 | [diff] [blame] | 310 | ret = devm_add_action_or_reset(dev, qoriq_tmu_action, data); |
| 311 | if (ret) |
| 312 | return ret; |
| 313 | |
Yuantian Tang | 9809797 | 2019-10-11 10:05:34 +0800 | [diff] [blame] | 314 | /* version register offset at: 0xbf8 on both v1 and v2 */ |
Andrey Smirnov | 4316237 | 2019-12-10 08:41:49 -0800 | [diff] [blame] | 315 | ret = regmap_read(data->regmap, REGS_IPBRR(0), &ver); |
| 316 | if (ret) { |
| 317 | dev_err(&pdev->dev, "Failed to read IP block version\n"); |
| 318 | return ret; |
| 319 | } |
Yuantian Tang | 9809797 | 2019-10-11 10:05:34 +0800 | [diff] [blame] | 320 | data->ver = (ver >> 8) & 0xff; |
Yuantian Tang | 9809797 | 2019-10-11 10:05:34 +0800 | [diff] [blame] | 321 | |
Jia Hongtao | 4352844 | 2016-06-30 11:08:38 +0800 | [diff] [blame] | 322 | qoriq_tmu_init_device(data); /* TMU initialization */ |
| 323 | |
Andrey Smirnov | 8e1cda3 | 2019-12-10 08:41:47 -0800 | [diff] [blame] | 324 | ret = qoriq_tmu_calibration(dev, data); /* TMU calibration */ |
Jia Hongtao | 4352844 | 2016-06-30 11:08:38 +0800 | [diff] [blame] | 325 | if (ret < 0) |
Anson Huang | 85f0b61 | 2020-03-11 13:07:31 +0800 | [diff] [blame] | 326 | return ret; |
Jia Hongtao | 4352844 | 2016-06-30 11:08:38 +0800 | [diff] [blame] | 327 | |
Andrey Smirnov | 0303662 | 2019-12-10 08:41:46 -0800 | [diff] [blame] | 328 | ret = qoriq_tmu_register_tmu_zone(dev, data); |
Yuantian Tang | 7797ff4 | 2019-01-18 13:39:40 +0800 | [diff] [blame] | 329 | if (ret < 0) { |
Andrey Smirnov | e167dc4 | 2019-12-10 08:41:42 -0800 | [diff] [blame] | 330 | dev_err(dev, "Failed to register sensors\n"); |
Anson Huang | 85f0b61 | 2020-03-11 13:07:31 +0800 | [diff] [blame] | 331 | return ret; |
Jia Hongtao | 4352844 | 2016-06-30 11:08:38 +0800 | [diff] [blame] | 332 | } |
| 333 | |
Andrey Smirnov | 8e1cda3 | 2019-12-10 08:41:47 -0800 | [diff] [blame] | 334 | platform_set_drvdata(pdev, data); |
| 335 | |
Jia Hongtao | 4352844 | 2016-06-30 11:08:38 +0800 | [diff] [blame] | 336 | return 0; |
Jia Hongtao | 4352844 | 2016-06-30 11:08:38 +0800 | [diff] [blame] | 337 | } |
| 338 | |
Anson Huang | aea5919 | 2019-07-30 10:21:25 +0800 | [diff] [blame] | 339 | static int __maybe_unused qoriq_tmu_suspend(struct device *dev) |
Jia Hongtao | 4352844 | 2016-06-30 11:08:38 +0800 | [diff] [blame] | 340 | { |
Jia Hongtao | 4352844 | 2016-06-30 11:08:38 +0800 | [diff] [blame] | 341 | struct qoriq_tmu_data *data = dev_get_drvdata(dev); |
Andrey Smirnov | 4316237 | 2019-12-10 08:41:49 -0800 | [diff] [blame] | 342 | int ret; |
Jia Hongtao | 4352844 | 2016-06-30 11:08:38 +0800 | [diff] [blame] | 343 | |
Andrey Smirnov | 4316237 | 2019-12-10 08:41:49 -0800 | [diff] [blame] | 344 | ret = regmap_update_bits(data->regmap, REGS_TMR, TMR_ME, 0); |
| 345 | if (ret) |
| 346 | return ret; |
Jia Hongtao | 4352844 | 2016-06-30 11:08:38 +0800 | [diff] [blame] | 347 | |
Anson Huang | 5190404 | 2019-07-30 10:21:22 +0800 | [diff] [blame] | 348 | clk_disable_unprepare(data->clk); |
| 349 | |
Jia Hongtao | 4352844 | 2016-06-30 11:08:38 +0800 | [diff] [blame] | 350 | return 0; |
| 351 | } |
| 352 | |
Anson Huang | aea5919 | 2019-07-30 10:21:25 +0800 | [diff] [blame] | 353 | static int __maybe_unused qoriq_tmu_resume(struct device *dev) |
Jia Hongtao | 4352844 | 2016-06-30 11:08:38 +0800 | [diff] [blame] | 354 | { |
Anson Huang | 5190404 | 2019-07-30 10:21:22 +0800 | [diff] [blame] | 355 | int ret; |
Jia Hongtao | 4352844 | 2016-06-30 11:08:38 +0800 | [diff] [blame] | 356 | struct qoriq_tmu_data *data = dev_get_drvdata(dev); |
| 357 | |
Anson Huang | 5190404 | 2019-07-30 10:21:22 +0800 | [diff] [blame] | 358 | ret = clk_prepare_enable(data->clk); |
| 359 | if (ret) |
| 360 | return ret; |
| 361 | |
Jia Hongtao | 4352844 | 2016-06-30 11:08:38 +0800 | [diff] [blame] | 362 | /* Enable monitoring */ |
Andrey Smirnov | 4316237 | 2019-12-10 08:41:49 -0800 | [diff] [blame] | 363 | return regmap_update_bits(data->regmap, REGS_TMR, TMR_ME, TMR_ME); |
Jia Hongtao | 4352844 | 2016-06-30 11:08:38 +0800 | [diff] [blame] | 364 | } |
Jia Hongtao | 4352844 | 2016-06-30 11:08:38 +0800 | [diff] [blame] | 365 | |
| 366 | static SIMPLE_DEV_PM_OPS(qoriq_tmu_pm_ops, |
| 367 | qoriq_tmu_suspend, qoriq_tmu_resume); |
| 368 | |
| 369 | static const struct of_device_id qoriq_tmu_match[] = { |
| 370 | { .compatible = "fsl,qoriq-tmu", }, |
Anson Huang | 6017e2a | 2018-08-30 10:14:46 +0800 | [diff] [blame] | 371 | { .compatible = "fsl,imx8mq-tmu", }, |
Jia Hongtao | 4352844 | 2016-06-30 11:08:38 +0800 | [diff] [blame] | 372 | {}, |
| 373 | }; |
| 374 | MODULE_DEVICE_TABLE(of, qoriq_tmu_match); |
| 375 | |
| 376 | static struct platform_driver qoriq_tmu = { |
| 377 | .driver = { |
| 378 | .name = "qoriq_thermal", |
| 379 | .pm = &qoriq_tmu_pm_ops, |
| 380 | .of_match_table = qoriq_tmu_match, |
| 381 | }, |
| 382 | .probe = qoriq_tmu_probe, |
Jia Hongtao | 4352844 | 2016-06-30 11:08:38 +0800 | [diff] [blame] | 383 | }; |
| 384 | module_platform_driver(qoriq_tmu); |
| 385 | |
| 386 | MODULE_AUTHOR("Jia Hongtao <hongtao.jia@nxp.com>"); |
| 387 | MODULE_DESCRIPTION("QorIQ Thermal Monitoring Unit driver"); |
| 388 | MODULE_LICENSE("GPL v2"); |