/* * Copyright (c) 2015, The Linux Foundation. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 and * only version 2 as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * */ #include #include #include #include #include #include #include #include #include "tsens.h" #define CAL_MDEGC 30000 #define CONFIG_ADDR 0x3640 /* CONFIG_ADDR bitmasks */ #define CONFIG 0x9b #define CONFIG_MASK 0xf #define CONFIG_SHIFT 0 #define STATUS_CNTL_8064 0x3660 #define CNTL_ADDR 0x3620 /* CNTL_ADDR bitmasks */ #define EN BIT(0) #define SW_RST BIT(1) #define SENSOR0_EN BIT(3) #define SLP_CLK_ENA BIT(26) #define MEASURE_PERIOD 1 #define SENSOR0_SHIFT 3 /* INT_STATUS_ADDR bitmasks */ #define MIN_STATUS_MASK BIT(0) #define LOWER_STATUS_CLR BIT(1) #define UPPER_STATUS_CLR BIT(2) #define MAX_STATUS_MASK BIT(3) #define THRESHOLD_ADDR 0x3624 /* THRESHOLD_ADDR bitmasks */ #define THRESHOLD_MAX_CODE 0xff #define THRESHOLD_MIN_CODE 0 #define THRESHOLD_MAX_LIMIT_SHIFT 24 #define THRESHOLD_MIN_LIMIT_SHIFT 16 #define THRESHOLD_UPPER_LIMIT_SHIFT 8 #define THRESHOLD_LOWER_LIMIT_SHIFT 0 #define THRESHOLD_MAX_LIMIT_MASK (THRESHOLD_MAX_CODE << \ THRESHOLD_MAX_LIMIT_SHIFT) #define THRESHOLD_MIN_LIMIT_MASK (THRESHOLD_MAX_CODE << \ THRESHOLD_MIN_LIMIT_SHIFT) #define THRESHOLD_UPPER_LIMIT_MASK (THRESHOLD_MAX_CODE << \ THRESHOLD_UPPER_LIMIT_SHIFT) #define THRESHOLD_LOWER_LIMIT_MASK (THRESHOLD_MAX_CODE << \ THRESHOLD_LOWER_LIMIT_SHIFT) /* Initial temperature threshold values */ #define LOWER_LIMIT_TH 0x9d /* 95C */ #define UPPER_LIMIT_TH 0xa6 /* 105C */ #define MIN_LIMIT_TH 0x0 #define MAX_LIMIT_TH 0xff #define S0_STATUS_ADDR 0x3628 #define STATUS_ADDR_OFFSET 2 #define SENSOR_STATUS_SIZE 4 #define INT_STATUS_ADDR 0x363c #define TRDY_MASK BIT(7) #define TIMEOUT_US 100 #define TSENS_EN BIT(0) #define TSENS_SW_RST BIT(1) #define TSENS_ADC_CLK_SEL BIT(2) #define SENSOR0_EN BIT(3) #define SENSOR1_EN BIT(4) #define SENSOR2_EN BIT(5) #define SENSOR3_EN BIT(6) #define SENSOR4_EN BIT(7) #define SENSORS_EN (SENSOR0_EN | SENSOR1_EN | \ SENSOR2_EN | SENSOR3_EN | SENSOR4_EN) #define TSENS_8064_SENSOR5_EN BIT(8) #define TSENS_8064_SENSOR6_EN BIT(9) #define TSENS_8064_SENSOR7_EN BIT(10) #define TSENS_8064_SENSOR8_EN BIT(11) #define TSENS_8064_SENSOR9_EN BIT(12) #define TSENS_8064_SENSOR10_EN BIT(13) #define TSENS_8064_SENSORS_EN (SENSORS_EN | \ TSENS_8064_SENSOR5_EN | \ TSENS_8064_SENSOR6_EN | \ TSENS_8064_SENSOR7_EN | \ TSENS_8064_SENSOR8_EN | \ TSENS_8064_SENSOR9_EN | \ TSENS_8064_SENSOR10_EN) #define TSENS_8064_SEQ_SENSORS 5 #define TSENS_8064_S4_S5_OFFSET 40 #define TSENS_FACTOR 1000 /* Trips: from very hot to very cold */ enum tsens_trip_type { TSENS_TRIP_STAGE3 = 0, TSENS_TRIP_STAGE2, TSENS_TRIP_STAGE1, TSENS_TRIP_STAGE0, TSENS_TRIP_NUM, }; u32 tsens_8064_slope[] = { 1176, 1176, 1154, 1176, 1111, 1132, 1132, 1199, 1132, 1199, 1132 }; /* Temperature on y axis and ADC-code on x-axis */ static inline int code_to_degC(u32 adc_code, const struct tsens_sensor *s) { int degcbeforefactor, degc; degcbeforefactor = (adc_code * s->slope) + s->offset; if (degcbeforefactor == 0) degc = degcbeforefactor; else if (degcbeforefactor > 0) degc = (degcbeforefactor + TSENS_FACTOR/2) / TSENS_FACTOR; else degc = (degcbeforefactor - TSENS_FACTOR/2) / TSENS_FACTOR; return degc; } static int degC_to_code(int degC, const struct tsens_sensor *s) { int code = ((degC * TSENS_FACTOR - s->offset) + (s->slope/2)) / s->slope; if (code > THRESHOLD_MAX_CODE) code = THRESHOLD_MAX_CODE; else if (code < THRESHOLD_MIN_CODE) code = THRESHOLD_MIN_CODE; return code; } static int suspend_ipq8064(struct tsens_device *tmdev) { int ret; unsigned int mask; struct regmap *map = tmdev->map; ret = regmap_read(map, THRESHOLD_ADDR, &tmdev->ctx.threshold); if (ret) return ret; ret = regmap_read(map, CNTL_ADDR, &tmdev->ctx.control); if (ret) return ret; mask = SLP_CLK_ENA | EN; ret = regmap_update_bits(map, CNTL_ADDR, mask, 0); if (ret) return ret; return 0; } static int resume_ipq8064(struct tsens_device *tmdev) { int ret; struct regmap *map = tmdev->map; ret = regmap_update_bits(map, CNTL_ADDR, SW_RST, SW_RST); if (ret) return ret; ret = regmap_update_bits(map, CONFIG_ADDR, CONFIG_MASK, CONFIG); if (ret) return ret; ret = regmap_write(map, THRESHOLD_ADDR, tmdev->ctx.threshold); if (ret) return ret; ret = regmap_write(map, CNTL_ADDR, tmdev->ctx.control); if (ret) return ret; return 0; } static void notify_uspace_tsens_fn(struct work_struct *work) { struct tsens_sensor *s = container_of(work, struct tsens_sensor, notify_work); if (!s || !s->tzd) { /* Do nothing. TSENS driver has not been registered yet */ return; } sysfs_notify(&s->tzd->device.kobj, NULL, "type"); } static void tsens_scheduler_fn(struct work_struct *work) { struct tsens_device *tmdev = container_of(work, struct tsens_device, tsens_work); unsigned int threshold, threshold_low, code, reg, sensor, mask; bool upper_th_x, lower_th_x; int ret; ret = regmap_read(tmdev->map, STATUS_CNTL_8064, ®); if (ret) return; reg = reg | LOWER_STATUS_CLR | UPPER_STATUS_CLR; ret = regmap_write(tmdev->map, STATUS_CNTL_8064, reg); if (ret) return; mask = (unsigned int)(~(LOWER_STATUS_CLR | UPPER_STATUS_CLR)); ret = regmap_read(tmdev->map, THRESHOLD_ADDR, &threshold); if (ret) return; threshold_low = (threshold & THRESHOLD_LOWER_LIMIT_MASK) >> THRESHOLD_LOWER_LIMIT_SHIFT; threshold = (threshold & THRESHOLD_UPPER_LIMIT_MASK) >> THRESHOLD_UPPER_LIMIT_SHIFT; ret = regmap_read(tmdev->map, STATUS_CNTL_8064, ®); if (ret) return; ret = regmap_read(tmdev->map, CNTL_ADDR, &sensor); if (ret) return; sensor &= (uint32_t) TSENS_8064_SENSORS_EN; sensor >>= SENSOR0_SHIFT; /* Constraint: There is only 1 interrupt control register for all * 11 temperature sensor. So monitoring more than 1 sensor based * on interrupts will yield inconsistent result. To overcome this * issue we will monitor only sensor 0 which is the master sensor. */ /* Skip if the sensor is disabled */ if (sensor & 1) { ret = regmap_read(tmdev->map, tmdev->sensor[0].status, &code); if (ret) return; upper_th_x = code >= threshold; lower_th_x = code <= threshold_low; if (upper_th_x) mask |= UPPER_STATUS_CLR; if (lower_th_x) mask |= LOWER_STATUS_CLR; if (upper_th_x || lower_th_x) { /* Notify user space */ schedule_work(&tmdev->sensor[0].notify_work); pr_debug("Trigger (%d degrees) for sensor %d\n", code_to_degC(code, &tmdev->sensor[0]), 0); } } regmap_write(tmdev->map, STATUS_CNTL_8064, reg & mask); /* force memory to sync */ mb(); } static irqreturn_t tsens_isr(int irq, void *data) { struct tsens_device *tmdev = data; schedule_work(&tmdev->tsens_work); return IRQ_HANDLED; } static void hw_init(struct tsens_device *tmdev) { int ret; unsigned int reg_cntl = 0, reg_cfg = 0, reg_thr = 0; unsigned int reg_status_cntl = 0; regmap_read(tmdev->map, CNTL_ADDR, ®_cntl); regmap_write(tmdev->map, CNTL_ADDR, reg_cntl | TSENS_SW_RST); reg_cntl |= SLP_CLK_ENA | (MEASURE_PERIOD << 18) | (((1 << tmdev->num_sensors) - 1) << SENSOR0_SHIFT); regmap_write(tmdev->map, CNTL_ADDR, reg_cntl); regmap_read(tmdev->map, STATUS_CNTL_8064, ®_status_cntl); reg_status_cntl |= LOWER_STATUS_CLR | UPPER_STATUS_CLR | MIN_STATUS_MASK | MAX_STATUS_MASK; regmap_write(tmdev->map, STATUS_CNTL_8064, reg_status_cntl); reg_cntl |= TSENS_EN; regmap_write(tmdev->map, CNTL_ADDR, reg_cntl); regmap_read(tmdev->map, CONFIG_ADDR, ®_cfg); reg_cfg = (reg_cfg & ~CONFIG_MASK) | (CONFIG << CONFIG_SHIFT); regmap_write(tmdev->map, CONFIG_ADDR, reg_cfg); reg_thr |= (LOWER_LIMIT_TH << THRESHOLD_LOWER_LIMIT_SHIFT) | (UPPER_LIMIT_TH << THRESHOLD_UPPER_LIMIT_SHIFT) | (MIN_LIMIT_TH << THRESHOLD_MIN_LIMIT_SHIFT) | (MAX_LIMIT_TH << THRESHOLD_MAX_LIMIT_SHIFT); regmap_write(tmdev->map, THRESHOLD_ADDR, reg_thr); INIT_WORK(&tmdev->tsens_work, tsens_scheduler_fn); ret = devm_request_irq(tmdev->dev, tmdev->tsens_irq, tsens_isr, IRQF_TRIGGER_RISING, "tsens_interrupt", tmdev); if (ret < 0) { pr_err("%s: request_irq FAIL: %d\n", __func__, ret); return; } } static int init_ipq8064(struct tsens_device *tmdev) { int ret, i; u32 reg_cntl, offset = 0; init_common(tmdev); if (!tmdev->map) return -ENODEV; /* * The status registers for each sensor are discontiguous * because some SoCs have 5 sensors while others have more * but the control registers stay in the same place, i.e * directly after the first 5 status registers. */ for (i = 0; i < tmdev->num_sensors; i++) { if (i >= TSENS_8064_SEQ_SENSORS) offset = TSENS_8064_S4_S5_OFFSET; tmdev->sensor[i].status = S0_STATUS_ADDR + offset + (i << STATUS_ADDR_OFFSET); tmdev->sensor[i].slope = tsens_8064_slope[i]; INIT_WORK(&tmdev->sensor[i].notify_work, notify_uspace_tsens_fn); } reg_cntl = SW_RST; ret = regmap_update_bits(tmdev->map, CNTL_ADDR, SW_RST, reg_cntl); if (ret) return ret; reg_cntl |= SLP_CLK_ENA | (MEASURE_PERIOD << 18); reg_cntl &= ~SW_RST; ret = regmap_update_bits(tmdev->map, CONFIG_ADDR, CONFIG_MASK, CONFIG); reg_cntl |= GENMASK(tmdev->num_sensors - 1, 0) << SENSOR0_SHIFT; ret = regmap_write(tmdev->map, CNTL_ADDR, reg_cntl); if (ret) return ret; reg_cntl |= EN; ret = regmap_write(tmdev->map, CNTL_ADDR, reg_cntl); if (ret) return ret; return 0; } static int calibrate_ipq8064(struct tsens_device *tmdev) { int i; char *data, *data_backup; ssize_t num_read = tmdev->num_sensors; struct tsens_sensor *s = tmdev->sensor; data = qfprom_read(tmdev->dev, "calib"); if (IS_ERR(data)) { pr_err("Calibration not found.\n"); return PTR_ERR(data); } data_backup = qfprom_read(tmdev->dev, "calib_backup"); if (IS_ERR(data_backup)) { pr_err("Backup calibration not found.\n"); return PTR_ERR(data_backup); } for (i = 0; i < num_read; i++) { s[i].calib_data = readb_relaxed(data + i); s[i].calib_data_backup = readb_relaxed(data_backup + i); if (s[i].calib_data_backup) s[i].calib_data = s[i].calib_data_backup; if (!s[i].calib_data) { pr_err("QFPROM TSENS calibration data not present\n"); return -ENODEV; } s[i].slope = tsens_8064_slope[i]; s[i].offset = CAL_MDEGC - (s[i].calib_data * s[i].slope); } hw_init(tmdev); return 0; } static int get_temp_ipq8064(struct tsens_device *tmdev, int id, int *temp) { int ret; u32 code, trdy; const struct tsens_sensor *s = &tmdev->sensor[id]; unsigned long timeout; timeout = jiffies + usecs_to_jiffies(TIMEOUT_US); do { ret = regmap_read(tmdev->map, INT_STATUS_ADDR, &trdy); if (ret) return ret; if (!(trdy & TRDY_MASK)) continue; ret = regmap_read(tmdev->map, s->status, &code); if (ret) return ret; *temp = code_to_degC(code, s); return 0; } while (time_before(jiffies, timeout)); return -ETIMEDOUT; } static int set_trip_temp_ipq8064(void *data, int trip, int temp) { unsigned int reg_th, reg_cntl; int ret, code, code_chk, hi_code, lo_code; const struct tsens_sensor *s = data; struct tsens_device *tmdev = s->tmdev; code_chk = code = degC_to_code(temp, s); if (code < THRESHOLD_MIN_CODE || code > THRESHOLD_MAX_CODE) return -EINVAL; ret = regmap_read(tmdev->map, STATUS_CNTL_8064, ®_cntl); if (ret) return ret; ret = regmap_read(tmdev->map, THRESHOLD_ADDR, ®_th); if (ret) return ret; hi_code = (reg_th & THRESHOLD_UPPER_LIMIT_MASK) >> THRESHOLD_UPPER_LIMIT_SHIFT; lo_code = (reg_th & THRESHOLD_LOWER_LIMIT_MASK) >> THRESHOLD_LOWER_LIMIT_SHIFT; switch (trip) { case TSENS_TRIP_STAGE3: code <<= THRESHOLD_MAX_LIMIT_SHIFT; reg_th &= ~THRESHOLD_MAX_LIMIT_MASK; break; case TSENS_TRIP_STAGE2: if (code_chk <= lo_code) return -EINVAL; code <<= THRESHOLD_UPPER_LIMIT_SHIFT; reg_th &= ~THRESHOLD_UPPER_LIMIT_MASK; break; case TSENS_TRIP_STAGE1: if (code_chk >= hi_code) return -EINVAL; code <<= THRESHOLD_LOWER_LIMIT_SHIFT; reg_th &= ~THRESHOLD_LOWER_LIMIT_MASK; break; case TSENS_TRIP_STAGE0: code <<= THRESHOLD_MIN_LIMIT_SHIFT; reg_th &= ~THRESHOLD_MIN_LIMIT_MASK; break; default: return -EINVAL; } ret = regmap_write(tmdev->map, THRESHOLD_ADDR, reg_th | code); if (ret) return ret; return 0; } static int set_trip_activate_ipq8064(void *data, int trip, enum thermal_trip_activation_mode mode) { unsigned int reg_cntl, mask, val; const struct tsens_sensor *s = data; struct tsens_device *tmdev = s->tmdev; int ret; if (!tmdev || trip < 0) return -EINVAL; ret = regmap_read(tmdev->map, STATUS_CNTL_8064, ®_cntl); if (ret) return ret; switch (trip) { case TSENS_TRIP_STAGE3: mask = MAX_STATUS_MASK; break; case TSENS_TRIP_STAGE2: mask = UPPER_STATUS_CLR; break; case TSENS_TRIP_STAGE1: mask = LOWER_STATUS_CLR; break; case TSENS_TRIP_STAGE0: mask = MIN_STATUS_MASK; break; default: return -EINVAL; } if (mode == THERMAL_TRIP_ACTIVATION_DISABLED) val = reg_cntl | mask; else val = reg_cntl & ~mask; ret = regmap_write(tmdev->map, STATUS_CNTL_8064, val); if (ret) return ret; /* force memory to sync */ mb(); return 0; } const struct tsens_ops ops_ipq8064 = { .init = init_ipq8064, .calibrate = calibrate_ipq8064, .get_temp = get_temp_ipq8064, .suspend = suspend_ipq8064, .resume = resume_ipq8064, .set_trip_temp = set_trip_temp_ipq8064, .set_trip_activate = set_trip_activate_ipq8064, }; const struct tsens_data data_ipq8064 = { .num_sensors = 11, .ops = &ops_ipq8064, };