struct lvts_data { conststruct lvts_ctrl_data *lvts_ctrl; const u32 *conn_cmd; const u32 *init_cmd; int num_lvts_ctrl; int num_conn_cmd; int num_init_cmd; int temp_factor; int temp_offset; int gt_calib_bit_offset; unsignedint def_calibration;
};
struct lvts_sensor { struct thermal_zone_device *tz; void __iomem *msr; void __iomem *base; int id; int dt_id; int low_thresh; int high_thresh;
};
struct lvts_ctrl { struct lvts_sensor sensors[LVTS_SENSOR_MAX]; conststruct lvts_data *lvts_data;
u32 calibration[LVTS_SENSOR_MAX];
u8 valid_sensor_mask; int mode; void __iomem *base; int low_thresh; int high_thresh;
};
/* * Measurement registers: * * LVTS_MSR[0-3] / LVTS_IMMD[0-3] * * Bits: * * 32-17: Unused * 16 : Valid temperature * 15-0 : Raw temperature
*/
rc = readl_poll_timeout(msr, value, value & BIT(16),
LVTS_MSR_READ_WAIT_US, LVTS_MSR_READ_TIMEOUT_US);
/* * As the thermal zone temperature will read before the * hardware sensor is fully initialized, we have to check the * validity of the temperature returned when reading the * measurement register. The thermal controller will set the * valid bit temperature only when it is totally initialized. * * Otherwise, we may end up with garbage values out of the * functionning temperature and directly jump to a system * shutdown.
*/ if (rc) return -EAGAIN;
lvts_for_each_valid_sensor(i, lvts_ctrl) { if (lvts_ctrl->sensors[i].high_thresh == lvts_ctrl->high_thresh
&& lvts_ctrl->sensors[i].low_thresh == lvts_ctrl->low_thresh) { /* * The minimum threshold needs to be configured in the * OFFSETL register to get working interrupts, but we * don't actually want to generate interrupts when * crossing it.
*/ if (lvts_ctrl->low_thresh == -INT_MAX) {
value &= ~low_offset_inten_masks[i];
value |= high_offset_inten_masks[i];
} else {
value |= low_offset_inten_masks[i] | high_offset_inten_masks[i];
}
} else {
value &= ~(low_offset_inten_masks[i] | high_offset_inten_masks[i]);
}
}
writel(value, LVTS_MONINT(lvts_ctrl->base));
}
staticbool lvts_should_update_thresh(struct lvts_ctrl *lvts_ctrl, int high)
{ int i;
/* * Low offset temperature threshold * * LVTS_OFFSETL * * Bits: * * 14-0 : Raw temperature for threshold
*/
pr_debug("%s: Setting low limit temperature interrupt: %d\n",
thermal_zone_device_type(tz), low);
writel(raw_low, LVTS_OFFSETL(base));
/* * High offset temperature threshold * * LVTS_OFFSETH * * Bits: * * 14-0 : Raw temperature for threshold
*/
pr_debug("%s: Setting high limit temperature interrupt: %d\n",
thermal_zone_device_type(tz), high);
writel(raw_high, LVTS_OFFSETH(base));
/* * Interrupt monitoring status * * LVTS_MONINTST * * Bits: * * 31 : Interrupt for stage 3 * 30 : Interrupt for stage 2 * 29 : Interrupt for state 1 * 28 : Interrupt using filter on sensor 3 * * 27 : Interrupt using immediate on sensor 3 * 26 : Interrupt normal to hot on sensor 3 * 25 : Interrupt high offset on sensor 3 * 24 : Interrupt low offset on sensor 3 * * 23 : Interrupt hot threshold on sensor 3 * 22 : Interrupt cold threshold on sensor 3 * 21 : Interrupt using filter on sensor 2 * 20 : Interrupt using filter on sensor 1 * * 19 : Interrupt using filter on sensor 0 * 18 : Interrupt using immediate on sensor 2 * 17 : Interrupt using immediate on sensor 1 * 16 : Interrupt using immediate on sensor 0 * * 15 : Interrupt device access timeout interrupt * 14 : Interrupt normal to hot on sensor 2 * 13 : Interrupt high offset interrupt on sensor 2 * 12 : Interrupt low offset interrupt on sensor 2 * * 11 : Interrupt hot threshold on sensor 2 * 10 : Interrupt cold threshold on sensor 2 * 9 : Interrupt normal to hot on sensor 1 * 8 : Interrupt high offset interrupt on sensor 1 * * 7 : Interrupt low offset interrupt on sensor 1 * 6 : Interrupt hot threshold on sensor 1 * 5 : Interrupt cold threshold on sensor 1 * 4 : Interrupt normal to hot on sensor 0 * * 3 : Interrupt high offset interrupt on sensor 0 * 2 : Interrupt low offset interrupt on sensor 0 * 1 : Interrupt hot threshold on sensor 0 * 0 : Interrupt cold threshold on sensor 0 * * We are interested in the sensor(s) responsible of the * interrupt event. We update the thermal framework with the * thermal zone associated with the sensor. The framework will * take care of the rest whatever the kind of interrupt, we * are only interested in which sensor raised the interrupt. * * sensor 3 interrupt: 0001 1111 1100 0000 0000 0000 0000 0000 * => 0x1FC00000 * sensor 2 interrupt: 0000 0000 0010 0100 0111 1100 0000 0000 * => 0x00247C00 * sensor 1 interrupt: 0000 0000 0001 0010 0000 0011 1110 0000 * => 0X001203E0 * sensor 0 interrupt: 0000 0000 0000 1001 0000 0000 0001 1111 * => 0x0009001F
*/
value = readl(LVTS_MONINTSTS(lvts_ctrl->base));
/* * Let's figure out which sensors raised the interrupt * * NOTE: the masks array must be ordered with the index * corresponding to the sensor id eg. index=0, mask for * sensor0.
*/ for (i = 0; i < ARRAY_SIZE(masks); i++) {
/* * Write back to clear the interrupt status (W1C)
*/
writel(value, LVTS_MONINTSTS(lvts_ctrl->base));
return iret;
}
/* * Temperature interrupt handler. Even if the driver supports more * interrupt modes, we use the interrupt when the temperature crosses * the hot threshold the way up and the way down (modulo the * hysteresis). * * Each thermal domain has a couple of interrupts, one for hardware * reset and another one for all the thermal events happening on the * different sensors. * * The interrupt is configured for thermal events when crossing the * hot temperature limit. At each interrupt, we check in every * controller if there is an interrupt pending.
*/ static irqreturn_t lvts_irq_handler(int irq, void *data)
{ struct lvts_domain *lvts_td = data;
irqreturn_t aux, iret = IRQ_NONE; int i;
for (i = 0; i < lvts_td->num_lvts_ctrl; i++) {
aux = lvts_ctrl_irq_handler(&lvts_td->lvts_ctrl[i]); if (aux != IRQ_HANDLED) continue;
/* * At this point, we don't know which id matches which * sensor. Let's set arbitrally the id from the index.
*/
lvts_sensor[i].id = i;
/* * The thermal zone registration will set the trip * point interrupt in the thermal controller * register. But this one will be reset in the * initialization after. So we need to post pone the * thermal zone creation after the controller is * setup. For this reason, we store the device tree * node id from the data in the sensor structure
*/
lvts_sensor[i].dt_id = dt_id;
/* * We assign the base address of the thermal * controller as a back pointer. So it will be * accessible from the different thermal framework ops * as we pass the lvts_sensor pointer as thermal zone * private data.
*/
lvts_sensor[i].base = lvts_ctrl->base;
/* * Each sensor has its own register address to read from.
*/
lvts_sensor[i].msr = lvts_ctrl_data->mode == LVTS_MSR_IMMEDIATE_MODE ?
imm_regs[i] : msr_regs[i];
/* A zero value for gt means that device has invalid efuse data */
gt = (((u32 *)efuse_calibration)[0] >> lvts_ctrl->lvts_data->gt_calib_bit_offset) & 0xff;
if (gt) {
lvts_ctrl->calibration[i] =
(efuse_calibration[sensor->cal_offsets[0]] << 0) +
(efuse_calibration[sensor->cal_offsets[1]] << 8) +
(efuse_calibration[sensor->cal_offsets[2]] << 16);
} elseif (lvts_ctrl->lvts_data->def_calibration) {
lvts_ctrl->calibration[i] = lvts_ctrl->lvts_data->def_calibration;
} else {
dev_err(dev, "efuse contains invalid calibration data and no default given.\n"); return -ENODATA;
}
}
return 0;
}
/* * The efuse bytes stream can be split into different chunk of * nvmems. This function reads and concatenate those into a single * buffer so it can be read sequentially when initializing the * calibration data.
*/ staticint lvts_calibration_read(struct device *dev, struct lvts_domain *lvts_td, conststruct lvts_data *lvts_data)
{ struct device_node *np = dev_of_node(dev); struct nvmem_cell *cell; struct property *prop; constchar *cell_name;
/* * The golden temp information is contained in the first 32-bit * word of efuse data at a specific bit offset.
*/
gt = (((u32 *)calib)[0] >> lvts_data->gt_calib_bit_offset) & 0xff;
/* A zero value for gt means that device has invalid efuse data */ if (gt && gt < LVTS_GOLDEN_TEMP_MAX)
golden_temp = gt;
staticvoid lvts_ctrl_monitor_enable(struct device *dev, struct lvts_ctrl *lvts_ctrl, bool enable)
{ /* * Bitmaps to enable each sensor on filtered mode in the MONCTL0 * register.
*/ staticconst u8 sensor_filt_bitmap[] = { BIT(0), BIT(1), BIT(2), BIT(3) };
u32 sensor_map = 0; int i;
if (lvts_ctrl->mode != LVTS_MSR_FILTERED_MODE) return;
if (enable) {
lvts_for_each_valid_sensor(i, lvts_ctrl)
sensor_map |= sensor_filt_bitmap[i];
}
/* * Bits: * 9: Single point access flow * 0-3: Enable sensing point 0-3
*/
writel(sensor_map | BIT(9), LVTS_MONCTL0(lvts_ctrl->base));
}
/* * At this point the configuration register is the only place in the * driver where we write multiple values. Per hardware constraint, * each write in the configuration register must be separated by a * delay of 2 us.
*/ staticvoid lvts_write_config(struct lvts_ctrl *lvts_ctrl, const u32 *cmds, int nr_cmds)
{ int i;
/* * Configuration register
*/ for (i = 0; i < nr_cmds; i++) {
writel(cmds[i], LVTS_CONFIG(lvts_ctrl->base));
usleep_range(2, 4);
}
}
staticint lvts_irq_init(struct lvts_ctrl *lvts_ctrl)
{ /* * LVTS_PROTCTL : Thermal Protection Sensor Selection * * Bits: * * 19-18 : Sensor to base the protection on * 17-16 : Strategy: * 00 : Average of 4 sensors * 01 : Max of 4 sensors * 10 : Selected sensor with bits 19-18 * 11 : Reserved
*/
/* * LVTS_PROTTA : Stage 1 temperature threshold * LVTS_PROTTB : Stage 2 temperature threshold * LVTS_PROTTC : Stage 3 temperature threshold * * Bits: * * 14-0: Raw temperature threshold * * writel(0x0, LVTS_PROTTA(lvts_ctrl->base)); * writel(0x0, LVTS_PROTTB(lvts_ctrl->base)); * writel(0x0, LVTS_PROTTC(lvts_ctrl->base));
*/
/* * LVTS_MONINT : Interrupt configuration register * * The LVTS_MONINT register layout is the same as the LVTS_MONINTSTS * register, except we set the bits to enable the interrupt.
*/
writel(0, LVTS_MONINT(lvts_ctrl->base));
return 0;
}
staticint lvts_domain_reset(struct device *dev, struct reset_control *reset)
{ int ret;
ret = reset_control_assert(reset); if (ret) return ret;
return reset_control_deassert(reset);
}
/* * Enable or disable the clocks of a specified thermal controller
*/ staticint lvts_ctrl_set_enable(struct lvts_ctrl *lvts_ctrl, int enable)
{ /* * LVTS_CLKEN : Internal LVTS clock * * Bits: * * 0 : enable / disable clock
*/
writel(enable, LVTS_CLKEN(lvts_ctrl->base));
/* * LVTS_ID : Get ID and status of the thermal controller * * Bits: * * 0-5 : thermal controller id * 7 : thermal controller connection is valid
*/
id = readl(LVTS_ID(lvts_ctrl->base)); if (!(id & BIT(7))) return -EIO;
/* * LVTS_EDATA0X : Efuse calibration reference value for sensor X * * Bits: * * 20-0 : Efuse value for normalization data
*/ for (i = 0; i < LVTS_SENSOR_MAX; i++)
writel(lvts_ctrl->calibration[i], lvts_edata[i]);
/* * LVTS_TSSEL : Sensing point index numbering * * Bits: * * 31-24: ADC Sense 3 * 23-16: ADC Sense 2 * 15-8 : ADC Sense 1 * 7-0 : ADC Sense 0
*/
value = LVTS_TSSEL_CONF;
writel(value, LVTS_TSSEL(lvts_ctrl->base));
/* * LVTS_CALSCALE : ADC voltage round
*/
value = 0x300;
value = LVTS_CALSCALE_CONF;
/* * LVTS_MSRCTL0 : Sensor filtering strategy * * Filters: * * 000 : One sample * 001 : Avg 2 samples * 010 : 4 samples, drop min and max, avg 2 samples * 011 : 6 samples, drop min and max, avg 4 samples * 100 : 10 samples, drop min and max, avg 8 samples * 101 : 18 samples, drop min and max, avg 16 samples * * Bits: * * 0-2 : Sensor0 filter * 3-5 : Sensor1 filter * 6-8 : Sensor2 filter * 9-11 : Sensor3 filter
*/
value = LVTS_HW_FILTER << 9 | LVTS_HW_FILTER << 6 |
LVTS_HW_FILTER << 3 | LVTS_HW_FILTER;
writel(value, LVTS_MSRCTL0(lvts_ctrl->base));
/* * LVTS_MONCTL1 : Period unit and group interval configuration * * The clock source of LVTS thermal controller is 26MHz. * * The period unit is a time base for all the interval delays * specified in the registers. By default we use 12. The time * conversion is done by multiplying by 256 and 1/26.10^6 * * An interval delay multiplied by the period unit gives the * duration in seconds. * * - Filter interval delay is a delay between two samples of * the same sensor. * * - Sensor interval delay is a delay between two samples of * different sensors. * * - Group interval delay is a delay between different rounds. * * For example: * If Period unit = C, filter delay = 1, sensor delay = 2, group delay = 1, * and two sensors, TS1 and TS2, are in a LVTS thermal controller * and then * Period unit time = C * 1/26M * 256 = 12 * 38.46ns * 256 = 118.149us * Filter interval delay = 1 * Period unit = 118.149us * Sensor interval delay = 2 * Period unit = 236.298us * Group interval delay = 1 * Period unit = 118.149us * * TS1 TS1 ... TS1 TS2 TS2 ... TS2 TS1... * <--> Filter interval delay * <--> Sensor interval delay * <--> Group interval delay * Bits: * 29 - 20 : Group interval * 16 - 13 : Send a single interrupt when crossing the hot threshold (1) * or an interrupt everytime the hot threshold is crossed (0) * 9 - 0 : Period unit *
*/
value = LVTS_GROUP_INTERVAL << 20 | LVTS_PERIOD_UNIT;
writel(value, LVTS_MONCTL1(lvts_ctrl->base));
/* * LVTS_MONCTL2 : Filtering and sensor interval * * Bits: * * 25-16 : Interval unit in PERIOD_UNIT between sample on * the same sensor, filter interval * 9-0 : Interval unit in PERIOD_UNIT between each sensor *
*/
value = LVTS_FILTER_INTERVAL << 16 | LVTS_SENSOR_INTERVAL;
writel(value, LVTS_MONCTL2(lvts_ctrl->base));
return lvts_irq_init(lvts_ctrl);
}
staticint lvts_ctrl_start(struct device *dev, struct lvts_ctrl *lvts_ctrl)
{ struct lvts_sensor *lvts_sensors = lvts_ctrl->sensors; struct thermal_zone_device *tz;
u32 sensor_map = 0; int i; /* * Bitmaps to enable each sensor on immediate and filtered modes, as * described in MSRCTL1 and MONCTL0 registers below, respectively.
*/
u32 sensor_imm_bitmap[] = { BIT(4), BIT(5), BIT(6), BIT(9) };
u32 sensor_filt_bitmap[] = { BIT(0), BIT(1), BIT(2), BIT(3) };
tz = devm_thermal_of_zone_register(dev, dt_id, &lvts_sensors[i],
&lvts_ops); if (IS_ERR(tz)) { /* * This thermal zone is not described in the * device tree. It is not an error from the * thermal OF code POV, we just continue.
*/ if (PTR_ERR(tz) == -ENODEV) continue;
return PTR_ERR(tz);
}
devm_thermal_add_hwmon_sysfs(dev, tz);
/* * The thermal zone pointer will be needed in the * interrupt handler, we store it in the sensor * structure. The thermal domain structure will be * passed to the interrupt handler private data as the * interrupt is shared for all the controller * belonging to the thermal domain.
*/
lvts_sensors[i].tz = tz;
/* * This sensor was correctly associated with a thermal * zone, let's set the corresponding bit in the sensor * map, so we can enable the temperature monitoring in * the hardware thermal controller.
*/
sensor_map |= sensor_bitmap[i];
}
/* * The initialization of the thermal zones give us * which sensor point to enable. If any thermal zone * was not described in the device tree, it won't be * enabled here in the sensor map.
*/ if (lvts_ctrl->mode == LVTS_MSR_IMMEDIATE_MODE) { /* * LVTS_MSRCTL1 : Measurement control * * Bits: * * 9: Ignore MSRCTL0 config and do immediate measurement on sensor3 * 6: Ignore MSRCTL0 config and do immediate measurement on sensor2 * 5: Ignore MSRCTL0 config and do immediate measurement on sensor1 * 4: Ignore MSRCTL0 config and do immediate measurement on sensor0 * * That configuration will ignore the filtering and the delays * introduced in MONCTL1 and MONCTL2
*/
writel(sensor_map, LVTS_MSRCTL1(lvts_ctrl->base));
} else { /* * Bits: * 9: Single point access flow * 0-3: Enable sensing point 0-3
*/
writel(sensor_map | BIT(9), LVTS_MONCTL0(lvts_ctrl->base));
}
return 0;
}
staticint lvts_domain_init(struct device *dev, struct lvts_domain *lvts_td, conststruct lvts_data *lvts_data)
{ struct lvts_ctrl *lvts_ctrl; int i, ret;
ret = lvts_ctrl_init(dev, lvts_td, lvts_data); if (ret) return ret;
ret = lvts_domain_reset(dev, lvts_td->reset); if (ret) {
dev_dbg(dev, "Failed to reset domain"); return ret;
}
for (i = 0; i < lvts_td->num_lvts_ctrl; i++) {
lvts_ctrl = &lvts_td->lvts_ctrl[i];
/* * Initialization steps: * * - Enable the clock * - Connect to the LVTS * - Initialize the LVTS * - Prepare the calibration data * - Select monitored sensors * [ Configure sampling ] * [ Configure the interrupt ] * - Start measurement
*/
ret = lvts_ctrl_set_enable(lvts_ctrl, true); if (ret) {
dev_dbg(dev, "Failed to enable LVTS clock"); return ret;
}
ret = lvts_ctrl_connect(dev, lvts_ctrl); if (ret) {
dev_dbg(dev, "Failed to connect to LVTS controller"); return ret;
}
ret = lvts_ctrl_initialize(dev, lvts_ctrl); if (ret) {
dev_dbg(dev, "Failed to initialize controller"); return ret;
}
ret = lvts_ctrl_calibrate(dev, lvts_ctrl); if (ret) {
dev_dbg(dev, "Failed to calibrate controller"); return ret;
}
ret = lvts_ctrl_configure(dev, lvts_ctrl); if (ret) {
dev_dbg(dev, "Failed to configure controller"); return ret;
}
ret = lvts_ctrl_start(dev, lvts_ctrl); if (ret) {
dev_dbg(dev, "Failed to start controller"); return ret;
}
}
lvts_td = devm_kzalloc(dev, sizeof(*lvts_td), GFP_KERNEL); if (!lvts_td) return -ENOMEM;
lvts_data = of_device_get_match_data(dev); if (!lvts_data) return -ENODEV;
lvts_td->clk = devm_clk_get_enabled(dev, NULL); if (IS_ERR(lvts_td->clk)) return dev_err_probe(dev, PTR_ERR(lvts_td->clk), "Failed to retrieve clock\n");
res = platform_get_mem_or_io(pdev, 0); if (!res) return dev_err_probe(dev, (-ENXIO), "No IO resource\n");
lvts_td->base = devm_platform_get_and_ioremap_resource(pdev, 0, &res); if (IS_ERR(lvts_td->base)) return dev_err_probe(dev, PTR_ERR(lvts_td->base), "Failed to map io resource\n");
lvts_td->reset = devm_reset_control_get_by_index(dev, 0); if (IS_ERR(lvts_td->reset)) return dev_err_probe(dev, PTR_ERR(lvts_td->reset), "Failed to get reset control\n");
irq = platform_get_irq(pdev, 0); if (irq < 0) return irq;
golden_temp_offset = lvts_data->temp_offset;
ret = lvts_domain_init(dev, lvts_td, lvts_data); if (ret) return dev_err_probe(dev, ret, "Failed to initialize the lvts domain\n");
/* * At this point the LVTS is initialized and enabled. We can * safely enable the interrupt.
*/
ret = devm_request_threaded_irq(dev, irq, NULL, lvts_irq_handler,
IRQF_ONESHOT, dev_name(dev), lvts_td); if (ret) return dev_err_probe(dev, ret, "Failed to request interrupt\n");
platform_set_drvdata(pdev, lvts_td);
return 0;
}
staticvoid lvts_remove(struct platform_device *pdev)
{ struct lvts_domain *lvts_td; int i;
lvts_td = platform_get_drvdata(pdev);
for (i = 0; i < lvts_td->num_lvts_ctrl; i++)
lvts_ctrl_set_enable(&lvts_td->lvts_ctrl[i], false);
}
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.