/* Copyright (c) 2011-2013, 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. * */ /* * Qualcomm MSM8960 TSENS driver * */ #include #include #include #include #include #include #include #include #include #include #include #include /* 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, }; /* MSM8960 TSENS register info */ #define TSENS_CAL_DEGC 30 #define TSENS_MAIN_SENSOR 0 #define TSENS_8960_QFPROM_ADDR0 (MSM_QFPROM_BASE + 0x00000404) #define TSENS_8960_QFPROM_SPARE_ADDR0 (MSM_QFPROM_BASE + 0x00000414) #define TSENS_8960_CONFIG 0x9b #define TSENS_8960_CONFIG_SHIFT 0 #define TSENS_8960_CONFIG_MASK (0xf << TSENS_8960_CONFIG_SHIFT) #define TSENS_CNTL_ADDR (MSM_CLK_CTL_BASE + 0x00003620) #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_STATUS_CNTL_OFFSET 8 #define TSENS_MIN_STATUS_MASK BIT((tsens_status_cntl_start)) #define TSENS_LOWER_STATUS_CLR BIT((tsens_status_cntl_start + 1)) #define TSENS_UPPER_STATUS_CLR BIT((tsens_status_cntl_start + 2)) #define TSENS_MAX_STATUS_MASK BIT((tsens_status_cntl_start + 3)) #define TSENS_MEASURE_PERIOD 1 #define TSENS_8960_SLP_CLK_ENA BIT(26) #define TSENS_THRESHOLD_ADDR (MSM_CLK_CTL_BASE + 0x00003624) #define TSENS_THRESHOLD_MAX_CODE 0xff #define TSENS_THRESHOLD_MIN_CODE 0 #define TSENS_THRESHOLD_MAX_LIMIT_SHIFT 24 #define TSENS_THRESHOLD_MIN_LIMIT_SHIFT 16 #define TSENS_THRESHOLD_UPPER_LIMIT_SHIFT 8 #define TSENS_THRESHOLD_LOWER_LIMIT_SHIFT 0 #define TSENS_THRESHOLD_MAX_LIMIT_MASK (TSENS_THRESHOLD_MAX_CODE << \ TSENS_THRESHOLD_MAX_LIMIT_SHIFT) #define TSENS_THRESHOLD_MIN_LIMIT_MASK (TSENS_THRESHOLD_MAX_CODE << \ TSENS_THRESHOLD_MIN_LIMIT_SHIFT) #define TSENS_THRESHOLD_UPPER_LIMIT_MASK (TSENS_THRESHOLD_MAX_CODE << \ TSENS_THRESHOLD_UPPER_LIMIT_SHIFT) #define TSENS_THRESHOLD_LOWER_LIMIT_MASK (TSENS_THRESHOLD_MAX_CODE << \ TSENS_THRESHOLD_LOWER_LIMIT_SHIFT) /* Initial temperature threshold values */ #define TSENS_LOWER_LIMIT_TH 0x50 #define TSENS_UPPER_LIMIT_TH 0xdf #define TSENS_MIN_LIMIT_TH 0x0 #define TSENS_MAX_LIMIT_TH 0xff #define TSENS_S0_STATUS_ADDR (MSM_CLK_CTL_BASE + 0x00003628) #define TSENS_STATUS_ADDR_OFFSET 2 #define TSENS_SENSOR_STATUS_SIZE 4 #define TSENS_INT_STATUS_ADDR (MSM_CLK_CTL_BASE + 0x0000363c) #define TSENS_LOWER_INT_MASK BIT(1) #define TSENS_UPPER_INT_MASK BIT(2) #define TSENS_MAX_INT_MASK BIT(3) #define TSENS_TRDY_MASK BIT(7) #define TSENS_8960_CONFIG_ADDR (MSM_CLK_CTL_BASE + 0x00003640) #define TSENS_TRDY_RDY_MIN_TIME 1000 #define TSENS_TRDY_RDY_MAX_TIME 1100 #define TSENS_SENSOR_SHIFT 16 #define TSENS_RED_SHIFT 8 #define TSENS_8960_QFPROM_SHIFT 4 #define TSENS_SENSOR_QFPROM_SHIFT 2 #define TSENS_SENSOR0_SHIFT 3 #define TSENS_MASK1 1 #define TSENS_8660_QFPROM_ADDR (MSM_QFPROM_BASE + 0x000000bc) #define TSENS_8660_QFPROM_RED_TEMP_SENSOR0_SHIFT 24 #define TSENS_8660_QFPROM_TEMP_SENSOR0_SHIFT 16 #define TSENS_8660_QFPROM_TEMP_SENSOR0_MASK (255 \ << TSENS_8660_QFPROM_TEMP_SENSOR0_SHIFT) #define TSENS_8660_CONFIG 01 #define TSENS_8660_CONFIG_SHIFT 28 #define TSENS_8660_CONFIG_MASK (3 << TSENS_8660_CONFIG_SHIFT) #define TSENS_8660_SLP_CLK_ENA BIT(24) #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_STATUS_CNTL (MSM_CLK_CTL_BASE + 0x00003660) #define TSENS_8064_S5_STATUS_ADDR (MSM_CLK_CTL_BASE + 0x00003664) #define TSENS_8064_SEQ_SENSORS 5 #define TSENS_8064_S4_S5_OFFSET 40 #define TSENS_CNTL_RESUME_MASK 0xfffffff9 #define TSENS_8960_SENSOR_MASK 0xf8 #define TSENS_8064_SENSOR_MASK 0x3ff8 static int tsens_status_cntl_start; struct tsens_tm_device_sensor { struct thermal_zone_device *tz_dev; enum thermal_device_mode mode; unsigned int sensor_num; struct work_struct work; int offset; int calib_data; int calib_data_backup; uint32_t slope_mul_tsens_factor; }; struct tsens_tm_device { bool prev_reading_avail; int tsens_factor; uint32_t tsens_num_sensor; enum platform_type hw_type; int pm_tsens_thr_data; int pm_tsens_cntl; struct work_struct tsens_work; struct tsens_tm_device_sensor sensor[0]; }; struct tsens_tm_device *tmdev; /* Temperature on y axis and ADC-code on x-axis */ static int tsens_tz_code_to_degC(int adc_code, int sensor_num) { int degcbeforefactor, degc; degcbeforefactor = (adc_code * tmdev->sensor[sensor_num].slope_mul_tsens_factor + tmdev->sensor[sensor_num].offset); if (degcbeforefactor == 0) degc = degcbeforefactor; else if (degcbeforefactor > 0) degc = (degcbeforefactor + tmdev->tsens_factor/2) / tmdev->tsens_factor; else degc = (degcbeforefactor - tmdev->tsens_factor/2) / tmdev->tsens_factor; return degc; } static int tsens_tz_degC_to_code(int degC, int sensor_num) { int code = (degC * tmdev->tsens_factor - tmdev->sensor[sensor_num].offset + tmdev->sensor[sensor_num].slope_mul_tsens_factor/2) / tmdev->sensor[sensor_num].slope_mul_tsens_factor; if (code > TSENS_THRESHOLD_MAX_CODE) code = TSENS_THRESHOLD_MAX_CODE; else if (code < TSENS_THRESHOLD_MIN_CODE) code = TSENS_THRESHOLD_MIN_CODE; return code; } static void tsens8960_get_temp(int sensor_num, unsigned long *temp) { unsigned int code, offset = 0, sensor_addr; if (!tmdev->prev_reading_avail) { while (!(readl_relaxed(TSENS_INT_STATUS_ADDR) & TSENS_TRDY_MASK)) usleep_range(TSENS_TRDY_RDY_MIN_TIME, TSENS_TRDY_RDY_MAX_TIME); tmdev->prev_reading_avail = true; } sensor_addr = (unsigned int)TSENS_S0_STATUS_ADDR; if (tmdev->hw_type == APQ_8064 && sensor_num >= TSENS_8064_SEQ_SENSORS) offset = TSENS_8064_S4_S5_OFFSET; code = readl_relaxed(sensor_addr + offset + (sensor_num << TSENS_STATUS_ADDR_OFFSET)); *temp = tsens_tz_code_to_degC(code, sensor_num); } static int tsens_tz_get_temp(struct thermal_zone_device *thermal, unsigned long *temp) { struct tsens_tm_device_sensor *tm_sensor = thermal->devdata; if (!tm_sensor || tm_sensor->mode != THERMAL_DEVICE_ENABLED || !temp) return -EINVAL; tsens8960_get_temp(tm_sensor->sensor_num, temp); return 0; } int tsens_get_temp(struct tsens_device *device, unsigned long *temp) { if (!tmdev) return -ENODEV; tsens8960_get_temp(device->sensor_num, temp); return 0; } EXPORT_SYMBOL(tsens_get_temp); int tsens_get_max_sensor_num(uint32_t *tsens_num_sensors) { if (!tmdev) return -ENODEV; *tsens_num_sensors = tmdev->tsens_num_sensor; return 0; } EXPORT_SYMBOL(tsens_get_max_sensor_num); static int tsens_tz_get_mode(struct thermal_zone_device *thermal, enum thermal_device_mode *mode) { struct tsens_tm_device_sensor *tm_sensor = thermal->devdata; if (!tm_sensor || !mode) return -EINVAL; *mode = tm_sensor->mode; return 0; } /* Function to enable the mode. * If the main sensor is disabled all the sensors are disable and * the clock is disabled. * If the main sensor is not enabled and sub sensor is enabled * returns with an error stating the main sensor is not enabled. */ static int tsens_tz_set_mode(struct thermal_zone_device *thermal, enum thermal_device_mode mode) { struct tsens_tm_device_sensor *tm_sensor = thermal->devdata; unsigned int reg, mask, i; if (!tm_sensor) return -EINVAL; if (mode != tm_sensor->mode) { reg = readl_relaxed(TSENS_CNTL_ADDR); mask = 1 << (tm_sensor->sensor_num + TSENS_SENSOR0_SHIFT); if (mode == THERMAL_DEVICE_ENABLED) { if ((mask != SENSOR0_EN) && !(reg & SENSOR0_EN)) { pr_info("Main sensor not enabled\n"); return -EINVAL; } writel_relaxed(reg | TSENS_SW_RST, TSENS_CNTL_ADDR); if (tmdev->hw_type == MSM_8960 || tmdev->hw_type == MDM_9615 || tmdev->hw_type == APQ_8064) reg |= mask | TSENS_8960_SLP_CLK_ENA | TSENS_EN; else reg |= mask | TSENS_8660_SLP_CLK_ENA | TSENS_EN; tmdev->prev_reading_avail = false; } else { reg &= ~mask; if (!(reg & SENSOR0_EN)) { if (tmdev->hw_type == APQ_8064) reg &= ~(TSENS_8064_SENSORS_EN | TSENS_8960_SLP_CLK_ENA | TSENS_EN); else if (tmdev->hw_type == MSM_8960 || tmdev->hw_type == MDM_9615) reg &= ~(SENSORS_EN | TSENS_8960_SLP_CLK_ENA | TSENS_EN); else reg &= ~(SENSORS_EN | TSENS_8660_SLP_CLK_ENA | TSENS_EN); for (i = 1; i < tmdev->tsens_num_sensor; i++) tmdev->sensor[i].mode = mode; } } writel_relaxed(reg, TSENS_CNTL_ADDR); } tm_sensor->mode = mode; return 0; } static int tsens_tz_get_trip_type(struct thermal_zone_device *thermal, int trip, enum thermal_trip_type *type) { struct tsens_tm_device_sensor *tm_sensor = thermal->devdata; if (!tm_sensor || trip < 0 || !type) return -EINVAL; switch (trip) { case TSENS_TRIP_STAGE3: *type = THERMAL_TRIP_CRITICAL; break; case TSENS_TRIP_STAGE2: *type = THERMAL_TRIP_CONFIGURABLE_HI; break; case TSENS_TRIP_STAGE1: *type = THERMAL_TRIP_CONFIGURABLE_LOW; break; case TSENS_TRIP_STAGE0: *type = THERMAL_TRIP_CRITICAL_LOW; break; default: return -EINVAL; } return 0; } static int tsens_tz_activate_trip_type(struct thermal_zone_device *thermal, int trip, enum thermal_trip_activation_mode mode) { struct tsens_tm_device_sensor *tm_sensor = thermal->devdata; unsigned int reg_cntl, reg_th, code, hi_code, lo_code, mask; if (!tm_sensor || trip < 0) return -EINVAL; lo_code = TSENS_THRESHOLD_MIN_CODE; hi_code = TSENS_THRESHOLD_MAX_CODE; if (tmdev->hw_type == APQ_8064) reg_cntl = readl_relaxed(TSENS_8064_STATUS_CNTL); else reg_cntl = readl_relaxed(TSENS_CNTL_ADDR); reg_th = readl_relaxed(TSENS_THRESHOLD_ADDR); switch (trip) { case TSENS_TRIP_STAGE3: code = (reg_th & TSENS_THRESHOLD_MAX_LIMIT_MASK) >> TSENS_THRESHOLD_MAX_LIMIT_SHIFT; mask = TSENS_MAX_STATUS_MASK; if (!(reg_cntl & TSENS_UPPER_STATUS_CLR)) lo_code = (reg_th & TSENS_THRESHOLD_UPPER_LIMIT_MASK) >> TSENS_THRESHOLD_UPPER_LIMIT_SHIFT; else if (!(reg_cntl & TSENS_LOWER_STATUS_CLR)) lo_code = (reg_th & TSENS_THRESHOLD_LOWER_LIMIT_MASK) >> TSENS_THRESHOLD_LOWER_LIMIT_SHIFT; else if (!(reg_cntl & TSENS_MIN_STATUS_MASK)) lo_code = (reg_th & TSENS_THRESHOLD_MIN_LIMIT_MASK) >> TSENS_THRESHOLD_MIN_LIMIT_SHIFT; break; case TSENS_TRIP_STAGE2: code = (reg_th & TSENS_THRESHOLD_UPPER_LIMIT_MASK) >> TSENS_THRESHOLD_UPPER_LIMIT_SHIFT; mask = TSENS_UPPER_STATUS_CLR; if (!(reg_cntl & TSENS_MAX_STATUS_MASK)) hi_code = (reg_th & TSENS_THRESHOLD_MAX_LIMIT_MASK) >> TSENS_THRESHOLD_MAX_LIMIT_SHIFT; if (!(reg_cntl & TSENS_LOWER_STATUS_CLR)) lo_code = (reg_th & TSENS_THRESHOLD_LOWER_LIMIT_MASK) >> TSENS_THRESHOLD_LOWER_LIMIT_SHIFT; else if (!(reg_cntl & TSENS_MIN_STATUS_MASK)) lo_code = (reg_th & TSENS_THRESHOLD_MIN_LIMIT_MASK) >> TSENS_THRESHOLD_MIN_LIMIT_SHIFT; break; case TSENS_TRIP_STAGE1: code = (reg_th & TSENS_THRESHOLD_LOWER_LIMIT_MASK) >> TSENS_THRESHOLD_LOWER_LIMIT_SHIFT; mask = TSENS_LOWER_STATUS_CLR; if (!(reg_cntl & TSENS_MIN_STATUS_MASK)) lo_code = (reg_th & TSENS_THRESHOLD_MIN_LIMIT_MASK) >> TSENS_THRESHOLD_MIN_LIMIT_SHIFT; if (!(reg_cntl & TSENS_UPPER_STATUS_CLR)) hi_code = (reg_th & TSENS_THRESHOLD_UPPER_LIMIT_MASK) >> TSENS_THRESHOLD_UPPER_LIMIT_SHIFT; else if (!(reg_cntl & TSENS_MAX_STATUS_MASK)) hi_code = (reg_th & TSENS_THRESHOLD_MAX_LIMIT_MASK) >> TSENS_THRESHOLD_MAX_LIMIT_SHIFT; break; case TSENS_TRIP_STAGE0: code = (reg_th & TSENS_THRESHOLD_MIN_LIMIT_MASK) >> TSENS_THRESHOLD_MIN_LIMIT_SHIFT; mask = TSENS_MIN_STATUS_MASK; if (!(reg_cntl & TSENS_LOWER_STATUS_CLR)) hi_code = (reg_th & TSENS_THRESHOLD_LOWER_LIMIT_MASK) >> TSENS_THRESHOLD_LOWER_LIMIT_SHIFT; else if (!(reg_cntl & TSENS_UPPER_STATUS_CLR)) hi_code = (reg_th & TSENS_THRESHOLD_UPPER_LIMIT_MASK) >> TSENS_THRESHOLD_UPPER_LIMIT_SHIFT; else if (!(reg_cntl & TSENS_MAX_STATUS_MASK)) hi_code = (reg_th & TSENS_THRESHOLD_MAX_LIMIT_MASK) >> TSENS_THRESHOLD_MAX_LIMIT_SHIFT; break; default: return -EINVAL; } if (mode == THERMAL_TRIP_ACTIVATION_DISABLED) { if (tmdev->hw_type == APQ_8064) writel_relaxed(reg_cntl | mask, TSENS_8064_STATUS_CNTL); else writel_relaxed(reg_cntl | mask, TSENS_CNTL_ADDR); } else { if (code < lo_code || code > hi_code) { pr_info("%s with invalid code %x\n", __func__, code); return -EINVAL; } if (tmdev->hw_type == APQ_8064) writel_relaxed(reg_cntl & ~mask, TSENS_8064_STATUS_CNTL); else writel_relaxed(reg_cntl & ~mask, TSENS_CNTL_ADDR); } mb(); return 0; } static int tsens_tz_get_trip_temp(struct thermal_zone_device *thermal, int trip, unsigned long *temp) { struct tsens_tm_device_sensor *tm_sensor = thermal->devdata; unsigned int reg; if (!tm_sensor || trip < 0 || !temp) return -EINVAL; reg = readl_relaxed(TSENS_THRESHOLD_ADDR); switch (trip) { case TSENS_TRIP_STAGE3: reg = (reg & TSENS_THRESHOLD_MAX_LIMIT_MASK) >> TSENS_THRESHOLD_MAX_LIMIT_SHIFT; break; case TSENS_TRIP_STAGE2: reg = (reg & TSENS_THRESHOLD_UPPER_LIMIT_MASK) >> TSENS_THRESHOLD_UPPER_LIMIT_SHIFT; break; case TSENS_TRIP_STAGE1: reg = (reg & TSENS_THRESHOLD_LOWER_LIMIT_MASK) >> TSENS_THRESHOLD_LOWER_LIMIT_SHIFT; break; case TSENS_TRIP_STAGE0: reg = (reg & TSENS_THRESHOLD_MIN_LIMIT_MASK) >> TSENS_THRESHOLD_MIN_LIMIT_SHIFT; break; default: return -EINVAL; } *temp = tsens_tz_code_to_degC(reg, tm_sensor->sensor_num); return 0; } static int tsens_tz_get_crit_temp(struct thermal_zone_device *thermal, unsigned long *temp) { return tsens_tz_get_trip_temp(thermal, TSENS_TRIP_STAGE3, temp); } static int tsens_tz_notify(struct thermal_zone_device *thermal, int count, enum thermal_trip_type type) { /* TSENS driver does not shutdown the device. All Thermal notification are sent to the thermal daemon to take appropriate action */ return 1; } static int tsens_tz_set_trip_temp(struct thermal_zone_device *thermal, int trip, long temp) { struct tsens_tm_device_sensor *tm_sensor = thermal->devdata; unsigned int reg_th, reg_cntl; int code, hi_code, lo_code, code_err_chk; code_err_chk = code = tsens_tz_degC_to_code(temp, tm_sensor->sensor_num); if (!tm_sensor || trip < 0) return -EINVAL; lo_code = TSENS_THRESHOLD_MIN_CODE; hi_code = TSENS_THRESHOLD_MAX_CODE; if (tmdev->hw_type == APQ_8064) reg_cntl = readl_relaxed(TSENS_8064_STATUS_CNTL); else reg_cntl = readl_relaxed(TSENS_CNTL_ADDR); reg_th = readl_relaxed(TSENS_THRESHOLD_ADDR); switch (trip) { case TSENS_TRIP_STAGE3: code <<= TSENS_THRESHOLD_MAX_LIMIT_SHIFT; reg_th &= ~TSENS_THRESHOLD_MAX_LIMIT_MASK; if (!(reg_cntl & TSENS_UPPER_STATUS_CLR)) lo_code = (reg_th & TSENS_THRESHOLD_UPPER_LIMIT_MASK) >> TSENS_THRESHOLD_UPPER_LIMIT_SHIFT; else if (!(reg_cntl & TSENS_LOWER_STATUS_CLR)) lo_code = (reg_th & TSENS_THRESHOLD_LOWER_LIMIT_MASK) >> TSENS_THRESHOLD_LOWER_LIMIT_SHIFT; else if (!(reg_cntl & TSENS_MIN_STATUS_MASK)) lo_code = (reg_th & TSENS_THRESHOLD_MIN_LIMIT_MASK) >> TSENS_THRESHOLD_MIN_LIMIT_SHIFT; break; case TSENS_TRIP_STAGE2: code <<= TSENS_THRESHOLD_UPPER_LIMIT_SHIFT; reg_th &= ~TSENS_THRESHOLD_UPPER_LIMIT_MASK; if (!(reg_cntl & TSENS_MAX_STATUS_MASK)) hi_code = (reg_th & TSENS_THRESHOLD_MAX_LIMIT_MASK) >> TSENS_THRESHOLD_MAX_LIMIT_SHIFT; if (!(reg_cntl & TSENS_LOWER_STATUS_CLR)) lo_code = (reg_th & TSENS_THRESHOLD_LOWER_LIMIT_MASK) >> TSENS_THRESHOLD_LOWER_LIMIT_SHIFT; else if (!(reg_cntl & TSENS_MIN_STATUS_MASK)) lo_code = (reg_th & TSENS_THRESHOLD_MIN_LIMIT_MASK) >> TSENS_THRESHOLD_MIN_LIMIT_SHIFT; break; case TSENS_TRIP_STAGE1: code <<= TSENS_THRESHOLD_LOWER_LIMIT_SHIFT; reg_th &= ~TSENS_THRESHOLD_LOWER_LIMIT_MASK; if (!(reg_cntl & TSENS_MIN_STATUS_MASK)) lo_code = (reg_th & TSENS_THRESHOLD_MIN_LIMIT_MASK) >> TSENS_THRESHOLD_MIN_LIMIT_SHIFT; if (!(reg_cntl & TSENS_UPPER_STATUS_CLR)) hi_code = (reg_th & TSENS_THRESHOLD_UPPER_LIMIT_MASK) >> TSENS_THRESHOLD_UPPER_LIMIT_SHIFT; else if (!(reg_cntl & TSENS_MAX_STATUS_MASK)) hi_code = (reg_th & TSENS_THRESHOLD_MAX_LIMIT_MASK) >> TSENS_THRESHOLD_MAX_LIMIT_SHIFT; break; case TSENS_TRIP_STAGE0: code <<= TSENS_THRESHOLD_MIN_LIMIT_SHIFT; reg_th &= ~TSENS_THRESHOLD_MIN_LIMIT_MASK; if (!(reg_cntl & TSENS_LOWER_STATUS_CLR)) hi_code = (reg_th & TSENS_THRESHOLD_LOWER_LIMIT_MASK) >> TSENS_THRESHOLD_LOWER_LIMIT_SHIFT; else if (!(reg_cntl & TSENS_UPPER_STATUS_CLR)) hi_code = (reg_th & TSENS_THRESHOLD_UPPER_LIMIT_MASK) >> TSENS_THRESHOLD_UPPER_LIMIT_SHIFT; else if (!(reg_cntl & TSENS_MAX_STATUS_MASK)) hi_code = (reg_th & TSENS_THRESHOLD_MAX_LIMIT_MASK) >> TSENS_THRESHOLD_MAX_LIMIT_SHIFT; break; default: return -EINVAL; } if (code_err_chk < lo_code || code_err_chk > hi_code) return -EINVAL; writel_relaxed(reg_th | code, TSENS_THRESHOLD_ADDR); return 0; } static struct thermal_zone_device_ops tsens_thermal_zone_ops = { .get_temp = tsens_tz_get_temp, .get_mode = tsens_tz_get_mode, .set_mode = tsens_tz_set_mode, .get_trip_type = tsens_tz_get_trip_type, .activate_trip_type = tsens_tz_activate_trip_type, .get_trip_temp = tsens_tz_get_trip_temp, .set_trip_temp = tsens_tz_set_trip_temp, .get_crit_temp = tsens_tz_get_crit_temp, .notify = tsens_tz_notify, }; static void notify_uspace_tsens_fn(struct work_struct *work) { struct tsens_tm_device_sensor *tm = container_of(work, struct tsens_tm_device_sensor, work); sysfs_notify(&tm->tz_dev->device.kobj, NULL, "type"); } static void tsens_scheduler_fn(struct work_struct *work) { struct tsens_tm_device *tm = container_of(work, struct tsens_tm_device, tsens_work); unsigned int threshold, threshold_low, i, code, reg, sensor, mask; unsigned int sensor_addr; bool upper_th_x, lower_th_x; int adc_code; if (tmdev->hw_type == APQ_8064) { reg = readl_relaxed(TSENS_8064_STATUS_CNTL); writel_relaxed(reg | TSENS_LOWER_STATUS_CLR | TSENS_UPPER_STATUS_CLR, TSENS_8064_STATUS_CNTL); } else { reg = readl_relaxed(TSENS_CNTL_ADDR); writel_relaxed(reg | TSENS_LOWER_STATUS_CLR | TSENS_UPPER_STATUS_CLR, TSENS_CNTL_ADDR); } mask = ~(TSENS_LOWER_STATUS_CLR | TSENS_UPPER_STATUS_CLR); threshold = readl_relaxed(TSENS_THRESHOLD_ADDR); threshold_low = (threshold & TSENS_THRESHOLD_LOWER_LIMIT_MASK) >> TSENS_THRESHOLD_LOWER_LIMIT_SHIFT; threshold = (threshold & TSENS_THRESHOLD_UPPER_LIMIT_MASK) >> TSENS_THRESHOLD_UPPER_LIMIT_SHIFT; sensor = readl_relaxed(TSENS_CNTL_ADDR); if (tmdev->hw_type == APQ_8064) { reg = readl_relaxed(TSENS_8064_STATUS_CNTL); sensor &= (uint32_t) TSENS_8064_SENSORS_EN; } else { reg = sensor; sensor &= (uint32_t) SENSORS_EN; } sensor >>= TSENS_SENSOR0_SHIFT; sensor_addr = (unsigned int)TSENS_S0_STATUS_ADDR; for (i = 0; i < tmdev->tsens_num_sensor; i++) { if (i == TSENS_8064_SEQ_SENSORS) sensor_addr += TSENS_8064_S4_S5_OFFSET; if (sensor & TSENS_MASK1) { code = readl_relaxed(sensor_addr); upper_th_x = code >= threshold; lower_th_x = code <= threshold_low; if (upper_th_x) mask |= TSENS_UPPER_STATUS_CLR; if (lower_th_x) mask |= TSENS_LOWER_STATUS_CLR; if (upper_th_x || lower_th_x) { /* Notify user space */ schedule_work(&tm->sensor[i].work); adc_code = readl_relaxed(sensor_addr); pr_debug("Trigger (%d degrees) for sensor %d\n", tsens_tz_code_to_degC(adc_code, i), i); } } sensor >>= 1; sensor_addr += TSENS_SENSOR_STATUS_SIZE; } if (tmdev->hw_type == APQ_8064) writel_relaxed(reg & mask, TSENS_8064_STATUS_CNTL); else writel_relaxed(reg & mask, TSENS_CNTL_ADDR); mb(); } static irqreturn_t tsens_isr(int irq, void *data) { schedule_work(&tmdev->tsens_work); return IRQ_HANDLED; } #ifdef CONFIG_PM static int tsens_suspend(struct device *dev) { int i = 0; tmdev->pm_tsens_thr_data = readl_relaxed(TSENS_THRESHOLD_ADDR); tmdev->pm_tsens_cntl = readl_relaxed(TSENS_CNTL_ADDR); writel_relaxed(tmdev->pm_tsens_cntl & ~(TSENS_8960_SLP_CLK_ENA | TSENS_EN), TSENS_CNTL_ADDR); tmdev->prev_reading_avail = 0; for (i = 0; i < tmdev->tsens_num_sensor; i++) tmdev->sensor[i].mode = THERMAL_DEVICE_DISABLED; disable_irq_nosync(TSENS_UPPER_LOWER_INT); mb(); return 0; } static int tsens_resume(struct device *dev) { unsigned int reg_cntl = 0, reg_cfg = 0, reg_sensor_mask = 0; unsigned int reg_status_cntl = 0, reg_thr_data = 0, i = 0; reg_cntl = readl_relaxed(TSENS_CNTL_ADDR); writel_relaxed(reg_cntl | TSENS_SW_RST, TSENS_CNTL_ADDR); if (tmdev->hw_type == MSM_8960 || tmdev->hw_type == MDM_9615) { reg_cntl |= TSENS_8960_SLP_CLK_ENA | (TSENS_MEASURE_PERIOD << 18) | TSENS_MIN_STATUS_MASK | TSENS_MAX_STATUS_MASK | SENSORS_EN; writel_relaxed(reg_cntl, TSENS_CNTL_ADDR); } else if (tmdev->hw_type == APQ_8064) { reg_cntl |= TSENS_8960_SLP_CLK_ENA | (TSENS_MEASURE_PERIOD << 18) | (((1 << tmdev->tsens_num_sensor) - 1) << TSENS_SENSOR0_SHIFT); writel_relaxed(reg_cntl, TSENS_CNTL_ADDR); reg_status_cntl = readl_relaxed(TSENS_8064_STATUS_CNTL); reg_status_cntl |= TSENS_MIN_STATUS_MASK | TSENS_MAX_STATUS_MASK; writel_relaxed(reg_status_cntl, TSENS_8064_STATUS_CNTL); } reg_cfg = readl_relaxed(TSENS_8960_CONFIG_ADDR); reg_cfg = (reg_cfg & ~TSENS_8960_CONFIG_MASK) | (TSENS_8960_CONFIG << TSENS_8960_CONFIG_SHIFT); writel_relaxed(reg_cfg, TSENS_8960_CONFIG_ADDR); writel_relaxed((tmdev->pm_tsens_cntl & TSENS_CNTL_RESUME_MASK), TSENS_CNTL_ADDR); reg_cntl = readl_relaxed(TSENS_CNTL_ADDR); writel_relaxed(tmdev->pm_tsens_thr_data, TSENS_THRESHOLD_ADDR); reg_thr_data = readl_relaxed(TSENS_THRESHOLD_ADDR); if (tmdev->hw_type == MSM_8960 || tmdev->hw_type == MDM_9615) reg_sensor_mask = ((reg_cntl & TSENS_8960_SENSOR_MASK) >> TSENS_SENSOR0_SHIFT); else { reg_sensor_mask = ((reg_cntl & TSENS_8064_SENSOR_MASK) >> TSENS_SENSOR0_SHIFT); } for (i = 0; i < tmdev->tsens_num_sensor; i++) { if (reg_sensor_mask & TSENS_MASK1) tmdev->sensor[i].mode = THERMAL_DEVICE_ENABLED; reg_sensor_mask >>= 1; } enable_irq(TSENS_UPPER_LOWER_INT); mb(); return 0; } static const struct dev_pm_ops tsens_pm_ops = { .suspend = tsens_suspend, .resume = tsens_resume, }; #endif static void tsens_disable_mode(void) { unsigned int reg_cntl = 0; reg_cntl = readl_relaxed(TSENS_CNTL_ADDR); if (tmdev->hw_type == MSM_8960 || tmdev->hw_type == MDM_9615 || tmdev->hw_type == APQ_8064) writel_relaxed(reg_cntl & ~((((1 << tmdev->tsens_num_sensor) - 1) << TSENS_SENSOR0_SHIFT) | TSENS_8960_SLP_CLK_ENA | TSENS_EN), TSENS_CNTL_ADDR); else if (tmdev->hw_type == MSM_8660) writel_relaxed(reg_cntl & ~((((1 << tmdev->tsens_num_sensor) - 1) << TSENS_SENSOR0_SHIFT) | TSENS_8660_SLP_CLK_ENA | TSENS_EN), TSENS_CNTL_ADDR); } static void tsens_hw_init(void) { unsigned int reg_cntl = 0, reg_cfg = 0, reg_thr = 0; unsigned int reg_status_cntl = 0; reg_cntl = readl_relaxed(TSENS_CNTL_ADDR); writel_relaxed(reg_cntl | TSENS_SW_RST, TSENS_CNTL_ADDR); if (tmdev->hw_type == MSM_8960 || tmdev->hw_type == MDM_9615) { reg_cntl |= TSENS_8960_SLP_CLK_ENA | (TSENS_MEASURE_PERIOD << 18) | TSENS_LOWER_STATUS_CLR | TSENS_UPPER_STATUS_CLR | TSENS_MIN_STATUS_MASK | TSENS_MAX_STATUS_MASK | SENSORS_EN; writel_relaxed(reg_cntl, TSENS_CNTL_ADDR); reg_cntl |= TSENS_EN; writel_relaxed(reg_cntl, TSENS_CNTL_ADDR); reg_cfg = readl_relaxed(TSENS_8960_CONFIG_ADDR); reg_cfg = (reg_cfg & ~TSENS_8960_CONFIG_MASK) | (TSENS_8960_CONFIG << TSENS_8960_CONFIG_SHIFT); writel_relaxed(reg_cfg, TSENS_8960_CONFIG_ADDR); } else if (tmdev->hw_type == MSM_8660) { reg_cntl |= TSENS_8660_SLP_CLK_ENA | TSENS_EN | (TSENS_MEASURE_PERIOD << 16) | TSENS_LOWER_STATUS_CLR | TSENS_UPPER_STATUS_CLR | TSENS_MIN_STATUS_MASK | TSENS_MAX_STATUS_MASK | (((1 << tmdev->tsens_num_sensor) - 1) << TSENS_SENSOR0_SHIFT); /* set TSENS_CONFIG bits (bits 29:28 of TSENS_CNTL) to '01'; this setting found to be optimal. */ reg_cntl = (reg_cntl & ~TSENS_8660_CONFIG_MASK) | (TSENS_8660_CONFIG << TSENS_8660_CONFIG_SHIFT); writel_relaxed(reg_cntl, TSENS_CNTL_ADDR); } else if (tmdev->hw_type == APQ_8064) { reg_cntl |= TSENS_8960_SLP_CLK_ENA | (TSENS_MEASURE_PERIOD << 18) | (((1 << tmdev->tsens_num_sensor) - 1) << TSENS_SENSOR0_SHIFT); writel_relaxed(reg_cntl, TSENS_CNTL_ADDR); reg_status_cntl = readl_relaxed(TSENS_8064_STATUS_CNTL); reg_status_cntl |= TSENS_LOWER_STATUS_CLR | TSENS_UPPER_STATUS_CLR | TSENS_MIN_STATUS_MASK | TSENS_MAX_STATUS_MASK; writel_relaxed(reg_status_cntl, TSENS_8064_STATUS_CNTL); reg_cntl |= TSENS_EN; writel_relaxed(reg_cntl, TSENS_CNTL_ADDR); reg_cfg = readl_relaxed(TSENS_8960_CONFIG_ADDR); reg_cfg = (reg_cfg & ~TSENS_8960_CONFIG_MASK) | (TSENS_8960_CONFIG << TSENS_8960_CONFIG_SHIFT); writel_relaxed(reg_cfg, TSENS_8960_CONFIG_ADDR); } reg_thr |= (TSENS_LOWER_LIMIT_TH << TSENS_THRESHOLD_LOWER_LIMIT_SHIFT) | (TSENS_UPPER_LIMIT_TH << TSENS_THRESHOLD_UPPER_LIMIT_SHIFT) | (TSENS_MIN_LIMIT_TH << TSENS_THRESHOLD_MIN_LIMIT_SHIFT) | (TSENS_MAX_LIMIT_TH << TSENS_THRESHOLD_MAX_LIMIT_SHIFT); writel_relaxed(reg_thr, TSENS_THRESHOLD_ADDR); } static int tsens_calib_sensors8660(void) { uint32_t *main_sensor_addr, sensor_shift, red_sensor_shift; uint32_t sensor_mask, red_sensor_mask; main_sensor_addr = TSENS_8660_QFPROM_ADDR; sensor_shift = TSENS_SENSOR_SHIFT; red_sensor_shift = sensor_shift + TSENS_RED_SHIFT; sensor_mask = TSENS_THRESHOLD_MAX_CODE << sensor_shift; red_sensor_mask = TSENS_THRESHOLD_MAX_CODE << red_sensor_shift; tmdev->sensor[TSENS_MAIN_SENSOR].calib_data = (readl_relaxed(main_sensor_addr) & sensor_mask) >> sensor_shift; tmdev->sensor[TSENS_MAIN_SENSOR].calib_data_backup = (readl_relaxed(main_sensor_addr) & red_sensor_mask) >> red_sensor_shift; if (tmdev->sensor[TSENS_MAIN_SENSOR].calib_data_backup) tmdev->sensor[TSENS_MAIN_SENSOR].calib_data = tmdev->sensor[TSENS_MAIN_SENSOR].calib_data_backup; if (!tmdev->sensor[TSENS_MAIN_SENSOR].calib_data) { pr_err("QFPROM TSENS calibration data not present\n"); return -ENODEV; } tmdev->sensor[TSENS_MAIN_SENSOR].offset = tmdev->tsens_factor * TSENS_CAL_DEGC - tmdev->sensor[TSENS_MAIN_SENSOR].slope_mul_tsens_factor * tmdev->sensor[TSENS_MAIN_SENSOR].calib_data; tmdev->prev_reading_avail = false; INIT_WORK(&tmdev->sensor[TSENS_MAIN_SENSOR].work, notify_uspace_tsens_fn); return 0; } static int tsens_calib_sensors8960(void) { uint32_t i; uint8_t *main_sensor_addr, *backup_sensor_addr; for (i = 0; i < tmdev->tsens_num_sensor; i++) { main_sensor_addr = TSENS_8960_QFPROM_ADDR0 + i; backup_sensor_addr = TSENS_8960_QFPROM_SPARE_ADDR0 + i; tmdev->sensor[i].calib_data = readb_relaxed(main_sensor_addr); tmdev->sensor[i].calib_data_backup = readb_relaxed(backup_sensor_addr); if (tmdev->sensor[i].calib_data_backup) tmdev->sensor[i].calib_data = tmdev->sensor[i].calib_data_backup; if (!tmdev->sensor[i].calib_data) { pr_err("QFPROM TSENS calibration data not present\n"); return -ENODEV; } tmdev->sensor[i].offset = (TSENS_CAL_DEGC * tmdev->tsens_factor) - (tmdev->sensor[i].calib_data * tmdev->sensor[i].slope_mul_tsens_factor); tmdev->prev_reading_avail = false; INIT_WORK(&tmdev->sensor[i].work, notify_uspace_tsens_fn); } return 0; } static int tsens_calib_sensors(void) { int rc = -ENODEV; if (tmdev->hw_type == MSM_8660) rc = tsens_calib_sensors8660(); else if (tmdev->hw_type == MSM_8960 || tmdev->hw_type == MDM_9615 || tmdev->hw_type == APQ_8064) rc = tsens_calib_sensors8960(); return rc; } int msm_tsens_early_init(struct tsens_platform_data *pdata) { int rc = 0, i; if (!pdata) { pr_err("No TSENS Platform data\n"); return -EINVAL; } tmdev = kzalloc(sizeof(struct tsens_tm_device) + pdata->tsens_num_sensor * sizeof(struct tsens_tm_device_sensor), GFP_ATOMIC); if (tmdev == NULL) { pr_err("%s: kzalloc() failed.\n", __func__); return -ENOMEM; } for (i = 0; i < pdata->tsens_num_sensor; i++) tmdev->sensor[i].slope_mul_tsens_factor = pdata->slope[i]; tmdev->tsens_factor = pdata->tsens_factor; tmdev->tsens_num_sensor = pdata->tsens_num_sensor; tmdev->hw_type = pdata->hw_type; rc = tsens_calib_sensors(); if (rc < 0) { kfree(tmdev); tmdev = NULL; return rc; } if (tmdev->hw_type == APQ_8064) tsens_status_cntl_start = 0; else tsens_status_cntl_start = TSENS_STATUS_CNTL_OFFSET; tsens_hw_init(); pr_debug("msm_tsens_early_init: done\n"); return rc; } static int __devinit tsens_tm_probe(struct platform_device *pdev) { int rc, i; if (!tmdev) { pr_info("%s : TSENS early init not done.\n", __func__); return -EFAULT; } for (i = 0; i < tmdev->tsens_num_sensor; i++) { char name[18]; snprintf(name, sizeof(name), "tsens_tz_sensor%d", i); tmdev->sensor[i].mode = THERMAL_DEVICE_ENABLED; tmdev->sensor[i].sensor_num = i; tmdev->sensor[i].tz_dev = thermal_zone_device_register(name, TSENS_TRIP_NUM, &tmdev->sensor[i], &tsens_thermal_zone_ops, 0, 0, 0, 0); if (IS_ERR(tmdev->sensor[i].tz_dev)) { pr_err("%s: thermal_zone_device_register() failed.\n", __func__); rc = -ENODEV; goto fail; } } rc = request_irq(TSENS_UPPER_LOWER_INT, tsens_isr, IRQF_TRIGGER_RISING, "tsens_interrupt", tmdev); if (rc < 0) { pr_err("%s: request_irq FAIL: %d\n", __func__, rc); for (i = 0; i < tmdev->tsens_num_sensor; i++) thermal_zone_device_unregister(tmdev->sensor[i].tz_dev); goto fail; } INIT_WORK(&tmdev->tsens_work, tsens_scheduler_fn); pr_debug("%s: OK\n", __func__); mb(); return 0; fail: tsens_disable_mode(); kfree(tmdev); tmdev = NULL; mb(); return rc; } static int __devexit tsens_tm_remove(struct platform_device *pdev) { int i; tsens_disable_mode(); mb(); free_irq(TSENS_UPPER_LOWER_INT, tmdev); for (i = 0; i < tmdev->tsens_num_sensor; i++) thermal_zone_device_unregister(tmdev->sensor[i].tz_dev); kfree(tmdev); tmdev = NULL; return 0; } static struct platform_driver tsens_tm_driver = { .probe = tsens_tm_probe, .remove = tsens_tm_remove, .driver = { .name = "tsens8960-tm", .owner = THIS_MODULE, #ifdef CONFIG_PM .pm = &tsens_pm_ops, #endif }, }; static int __init _tsens_tm_init(void) { return platform_driver_register(&tsens_tm_driver); } module_init(_tsens_tm_init); static void __exit _tsens_tm_remove(void) { platform_driver_unregister(&tsens_tm_driver); } module_exit(_tsens_tm_remove); MODULE_LICENSE("GPL v2"); MODULE_DESCRIPTION("MSM8960 Temperature Sensor driver"); MODULE_VERSION("1.0"); MODULE_ALIAS("platform:tsens8960-tm");