| /* Copyright (c) 2012-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. |
| * |
| */ |
| |
| #define pr_fmt(fmt) "%s: " fmt, __func__ |
| |
| #include <linux/module.h> |
| #include <linux/platform_device.h> |
| #include <linux/thermal.h> |
| #include <linux/interrupt.h> |
| #include <linux/workqueue.h> |
| #include <linux/delay.h> |
| #include <linux/kernel.h> |
| #include <linux/io.h> |
| #include <linux/slab.h> |
| #include <linux/msm_tsens.h> |
| #include <linux/err.h> |
| #include <linux/of.h> |
| |
| #include <mach/msm_iomap.h> |
| |
| #define TSENS_DRIVER_NAME "msm-tsens" |
| /* TSENS register info */ |
| #define TSENS_UPPER_LOWER_INTERRUPT_CTRL(n) ((n) + 0x1000) |
| #define TSENS_INTERRUPT_EN BIT(0) |
| |
| #define TSENS_S0_UPPER_LOWER_STATUS_CTRL_ADDR(n) ((n) + 0x1004) |
| #define TSENS_UPPER_STATUS_CLR BIT(21) |
| #define TSENS_LOWER_STATUS_CLR BIT(20) |
| #define TSENS_UPPER_THRESHOLD_MASK 0xffc00 |
| #define TSENS_LOWER_THRESHOLD_MASK 0x3ff |
| #define TSENS_UPPER_THRESHOLD_SHIFT 10 |
| |
| #define TSENS_S0_STATUS_ADDR(n) ((n) + 0x1030) |
| #define TSENS_SN_ADDR_OFFSET 0x4 |
| #define TSENS_SN_STATUS_TEMP_MASK 0x3ff |
| #define TSENS_SN_STATUS_LOWER_STATUS BIT(11) |
| #define TSENS_SN_STATUS_UPPER_STATUS BIT(12) |
| #define TSENS_STATUS_ADDR_OFFSET 2 |
| |
| #define TSENS_TRDY_ADDR(n) ((n) + 0x105c) |
| #define TSENS_TRDY_MASK BIT(0) |
| |
| #define TSENS_CTRL_ADDR(n) (n) |
| #define TSENS_EN BIT(0) |
| #define TSENS_SW_RST BIT(1) |
| #define TSENS_ADC_CLK_SEL BIT(2) |
| #define TSENS_SENSOR0_SHIFT 3 |
| #define TSENS_62_5_MS_MEAS_PERIOD 1 |
| #define TSENS_312_5_MS_MEAS_PERIOD 2 |
| #define TSENS_MEAS_PERIOD_SHIFT 18 |
| |
| #define TSENS_SN_MIN_MAX_STATUS_CTRL(n) ((n) + 4) |
| #define TSENS_GLOBAL_CONFIG(n) ((n) + 0x34) |
| #define TSENS_S0_MAIN_CONFIG(n) ((n) + 0x38) |
| #define TSENS_SN_REMOTE_CONFIG(n) ((n) + 0x3c) |
| |
| #define TSENS_EEPROM(n) ((n) + 0xd0) |
| #define TSENS_EEPROM_REDUNDANCY_SEL(n) ((n) + 0x444) |
| #define TSENS_EEPROM_BACKUP_REGION(n) ((n) + 0x440) |
| |
| #define TSENS_MAIN_CALIB_ADDR_RANGE 6 |
| #define TSENS_BACKUP_CALIB_ADDR_RANGE 4 |
| |
| #define TSENS_EEPROM_8X26_1(n) ((n) + 0x1c0) |
| #define TSENS_EEPROM_8X26_2(n) ((n) + 0x444) |
| #define TSENS_8X26_MAIN_CALIB_ADDR_RANGE 4 |
| |
| #define TSENS_EEPROM_8X10_1(n) ((n) + 0x1a4) |
| #define TSENS_EEPROM_8X10_1_OFFSET 8 |
| #define TSENS_EEPROM_8X10_2(n) ((n) + 0x1a8) |
| #define TSENS_EEPROM_8X10_SPARE_1(n) ((n) + 0xd8) |
| #define TSENS_EEPROM_8X10_SPARE_2(n) ((n) + 0xdc) |
| |
| /* TSENS calibration Mask data */ |
| #define TSENS_BASE1_MASK 0xff |
| #define TSENS0_POINT1_MASK 0x3f00 |
| #define TSENS1_POINT1_MASK 0xfc000 |
| #define TSENS2_POINT1_MASK 0x3f00000 |
| #define TSENS3_POINT1_MASK 0xfc000000 |
| #define TSENS4_POINT1_MASK 0x3f |
| #define TSENS5_POINT1_MASK 0xfc0 |
| #define TSENS6_POINT1_MASK 0x3f000 |
| #define TSENS7_POINT1_MASK 0xfc0000 |
| #define TSENS8_POINT1_MASK 0x3f000000 |
| #define TSENS8_POINT1_MASK_BACKUP 0x3f |
| #define TSENS9_POINT1_MASK 0x3f |
| #define TSENS9_POINT1_MASK_BACKUP 0xfc0 |
| #define TSENS10_POINT1_MASK 0xfc0 |
| #define TSENS10_POINT1_MASK_BACKUP 0x3f000 |
| #define TSENS_CAL_SEL_0_1 0xc0000000 |
| #define TSENS_CAL_SEL_2 0x40000000 |
| #define TSENS_CAL_SEL_SHIFT 30 |
| #define TSENS_CAL_SEL_SHIFT_2 28 |
| #define TSENS_ONE_POINT_CALIB 0x1 |
| #define TSENS_ONE_POINT_CALIB_OPTION_2 0x2 |
| #define TSENS_TWO_POINT_CALIB 0x3 |
| |
| #define TSENS0_POINT1_SHIFT 8 |
| #define TSENS1_POINT1_SHIFT 14 |
| #define TSENS2_POINT1_SHIFT 20 |
| #define TSENS3_POINT1_SHIFT 26 |
| #define TSENS5_POINT1_SHIFT 6 |
| #define TSENS6_POINT1_SHIFT 12 |
| #define TSENS7_POINT1_SHIFT 18 |
| #define TSENS8_POINT1_SHIFT 24 |
| #define TSENS9_POINT1_BACKUP_SHIFT 6 |
| #define TSENS10_POINT1_SHIFT 6 |
| #define TSENS10_POINT1_BACKUP_SHIFT 12 |
| |
| #define TSENS_POINT2_BASE_SHIFT 12 |
| #define TSENS_POINT2_BASE_BACKUP_SHIFT 18 |
| #define TSENS0_POINT2_SHIFT 20 |
| #define TSENS0_POINT2_BACKUP_SHIFT 26 |
| #define TSENS1_POINT2_SHIFT 26 |
| #define TSENS2_POINT2_BACKUP_SHIFT 6 |
| #define TSENS3_POINT2_SHIFT 6 |
| #define TSENS3_POINT2_BACKUP_SHIFT 12 |
| #define TSENS4_POINT2_SHIFT 12 |
| #define TSENS4_POINT2_BACKUP_SHIFT 18 |
| #define TSENS5_POINT2_SHIFT 18 |
| #define TSENS5_POINT2_BACKUP_SHIFT 24 |
| #define TSENS6_POINT2_SHIFT 24 |
| #define TSENS7_POINT2_BACKUP_SHIFT 6 |
| #define TSENS8_POINT2_SHIFT 6 |
| #define TSENS8_POINT2_BACKUP_SHIFT 12 |
| #define TSENS9_POINT2_SHIFT 12 |
| #define TSENS9_POINT2_BACKUP_SHIFT 18 |
| #define TSENS10_POINT2_SHIFT 18 |
| #define TSENS10_POINT2_BACKUP_SHIFT 24 |
| |
| #define TSENS_BASE2_MASK 0xff000 |
| #define TSENS_BASE2_BACKUP_MASK 0xfc0000 |
| #define TSENS0_POINT2_MASK 0x3f00000 |
| #define TSENS0_POINT2_BACKUP_MASK 0xfc000000 |
| #define TSENS1_POINT2_MASK 0xfc000000 |
| #define TSENS1_POINT2_BACKUP_MASK 0x3f |
| #define TSENS2_POINT2_MASK 0x3f |
| #define TSENS2_POINT2_BACKUP_MASK 0xfc0 |
| #define TSENS3_POINT2_MASK 0xfc0 |
| #define TSENS3_POINT2_BACKUP_MASK 0x3f000 |
| #define TSENS4_POINT2_MASK 0x3f000 |
| #define TSENS4_POINT2_BACKUP_MASK 0xfc0000 |
| #define TSENS5_POINT2_MASK 0xfc0000 |
| #define TSENS5_POINT2_BACKUP_MASK 0x3f000000 |
| #define TSENS6_POINT2_MASK 0x3f000000 |
| #define TSENS6_POINT2_BACKUP_MASK 0x3f |
| #define TSENS7_POINT2_MASK 0x3f |
| #define TSENS7_POINT2_BACKUP_MASK 0xfc0 |
| #define TSENS8_POINT2_MASK 0xfc0 |
| #define TSENS8_POINT2_BACKUP_MASK 0x3f000 |
| #define TSENS9_POINT2_MASK 0x3f000 |
| #define TSENS9_POINT2_BACKUP_MASK 0xfc0000 |
| #define TSENS10_POINT2_MASK 0xfc0000 |
| #define TSENS10_POINT2_BACKUP_MASK 0x3f000000 |
| |
| #define TSENS_8X26_BASE0_MASK 0x1fe000 |
| #define TSENS0_8X26_POINT1_MASK 0x7e00000 |
| #define TSENS1_8X26_POINT1_MASK 0x3f |
| #define TSENS2_8X26_POINT1_MASK 0xfc0 |
| #define TSENS3_8X26_POINT1_MASK 0x3f000 |
| #define TSENS4_8X26_POINT1_MASK 0xfc0000 |
| #define TSENS5_8X26_POINT1_MASK 0x3f000000 |
| #define TSENS6_8X26_POINT1_MASK 0x3f00000 |
| #define TSENS_8X26_TSENS_CAL_SEL 0xe0000000 |
| #define TSENS_8X26_BASE1_MASK 0xff |
| #define TSENS0_8X26_POINT2_MASK 0x3f00 |
| #define TSENS1_8X26_POINT2_MASK 0xfc000 |
| #define TSENS2_8X26_POINT2_MASK 0x3f00000 |
| #define TSENS3_8X26_POINT2_MASK 0xfc000000 |
| #define TSENS4_8X26_POINT2_MASK 0x3f00000 |
| #define TSENS5_8X26_POINT2_MASK 0xfc000000 |
| #define TSENS6_8X26_POINT2_MASK 0x7e0000 |
| |
| #define TSENS_8X26_CAL_SEL_SHIFT 29 |
| #define TSENS_8X26_BASE0_SHIFT 13 |
| #define TSENS0_8X26_POINT1_SHIFT 21 |
| #define TSENS2_8X26_POINT1_SHIFT 6 |
| #define TSENS3_8X26_POINT1_SHIFT 12 |
| #define TSENS4_8X26_POINT1_SHIFT 18 |
| #define TSENS5_8X26_POINT1_SHIFT 24 |
| #define TSENS6_8X26_POINT1_SHIFT 20 |
| |
| #define TSENS0_8X26_POINT2_SHIFT 8 |
| #define TSENS1_8X26_POINT2_SHIFT 14 |
| #define TSENS2_8X26_POINT2_SHIFT 20 |
| #define TSENS3_8X26_POINT2_SHIFT 26 |
| #define TSENS4_8X26_POINT2_SHIFT 20 |
| #define TSENS5_8X26_POINT2_SHIFT 26 |
| #define TSENS6_8X26_POINT2_SHIFT 17 |
| |
| #define TSENS_8X10_CAL_SEL_SHIFT 28 |
| #define TSENS_8X10_BASE1_SHIFT 8 |
| #define TSENS0_8X10_POINT1_SHIFT 16 |
| #define TSENS0_8X10_POINT2_SHIFT 22 |
| #define TSENS1_8X10_POINT2_SHIFT 6 |
| #define TSENS_8X10_BASE0_MASK 0xff |
| #define TSENS_8X10_BASE1_MASK 0xff00 |
| #define TSENS0_8X10_POINT1_MASK 0x3f0000 |
| #define TSENS0_8X10_POINT2_MASK 0xfc00000 |
| #define TSENS_8X10_TSENS_CAL_SEL 0x70000000 |
| #define TSENS1_8X10_POINT1_MASK 0x3f |
| #define TSENS1_8X10_POINT2_MASK 0xfc0 |
| #define TSENS_8X10_REDUN_SEL_MASK 0x6000000 |
| #define TSENS_8X10_REDUN_SEL_SHIFT 25 |
| |
| #define TSENS_BIT_APPEND 0x3 |
| #define TSENS_CAL_DEGC_POINT1 30 |
| #define TSENS_CAL_DEGC_POINT2 120 |
| #define TSENS_SLOPE_FACTOR 1000 |
| |
| /* TSENS register data */ |
| #define TSENS_TRDY_RDY_MIN_TIME 2000 |
| #define TSENS_TRDY_RDY_MAX_TIME 2100 |
| #define TSENS_THRESHOLD_MAX_CODE 0x3ff |
| #define TSENS_THRESHOLD_MIN_CODE 0x0 |
| |
| #define TSENS_GLOBAL_INIT_DATA 0x302f16c |
| #define TSENS_S0_MAIN_CFG_INIT_DATA 0x1c3 |
| #define TSENS_SN_MIN_MAX_STATUS_CTRL_DATA 0x3ffc00 |
| #define TSENS_SN_REMOTE_CFG_DATA 0x11c3 |
| |
| #define TSENS_QFPROM_BACKUP_SEL 0x3 |
| #define TSENS_QFPROM_BACKUP_REDUN_SEL 0xe0000000 |
| #define TSENS_QFPROM_BACKUP_REDUN_SHIFT 29 |
| |
| enum tsens_calib_fuse_map_type { |
| TSENS_CALIB_FUSE_MAP_8974 = 0, |
| TSENS_CALIB_FUSE_MAP_8X26, |
| TSENS_CALIB_FUSE_MAP_8X10, |
| TSENS_CALIB_FUSE_MAP_NUM, |
| }; |
| |
| /* Trips: warm and cool */ |
| enum tsens_trip_type { |
| TSENS_TRIP_WARM = 0, |
| TSENS_TRIP_COOL, |
| TSENS_TRIP_NUM, |
| }; |
| |
| struct tsens_tm_device_sensor { |
| struct thermal_zone_device *tz_dev; |
| enum thermal_device_mode mode; |
| /* Physical HW sensor number */ |
| unsigned int sensor_hw_num; |
| /* Software index. This is keep track of the HW/SW |
| * sensor_ID mapping */ |
| unsigned int sensor_sw_id; |
| struct work_struct work; |
| int offset; |
| int calib_data_point1; |
| int calib_data_point2; |
| uint32_t slope_mul_tsens_factor; |
| }; |
| |
| struct tsens_tm_device { |
| struct platform_device *pdev; |
| struct workqueue_struct *tsens_wq; |
| bool prev_reading_avail; |
| bool calibration_less_mode; |
| bool tsens_local_init; |
| int tsens_factor; |
| uint32_t tsens_num_sensor; |
| int tsens_irq; |
| void *tsens_addr; |
| void *tsens_calib_addr; |
| int tsens_len; |
| int calib_len; |
| struct resource *res_tsens_mem; |
| struct resource *res_calib_mem; |
| struct work_struct tsens_work; |
| uint32_t calib_mode; |
| struct tsens_tm_device_sensor sensor[0]; |
| }; |
| |
| struct tsens_tm_device *tmdev; |
| |
| int tsens_get_sw_id_mapping(int sensor_hw_num, int *sensor_sw_idx) |
| { |
| int i = 0; |
| bool id_found = false; |
| |
| while (i < tmdev->tsens_num_sensor && !id_found) { |
| if (sensor_hw_num == tmdev->sensor[i].sensor_hw_num) { |
| *sensor_sw_idx = tmdev->sensor[i].sensor_sw_id; |
| id_found = true; |
| } |
| i++; |
| } |
| |
| if (!id_found) |
| return -EINVAL; |
| |
| return 0; |
| } |
| EXPORT_SYMBOL(tsens_get_sw_id_mapping); |
| |
| int tsens_get_hw_id_mapping(int sensor_sw_id, int *sensor_hw_num) |
| { |
| int i = 0; |
| bool id_found = false; |
| |
| while (i < tmdev->tsens_num_sensor && !id_found) { |
| if (sensor_sw_id == tmdev->sensor[i].sensor_sw_id) { |
| *sensor_hw_num = tmdev->sensor[i].sensor_hw_num; |
| id_found = true; |
| } |
| i++; |
| } |
| |
| if (!id_found) |
| return -EINVAL; |
| |
| return 0; |
| } |
| EXPORT_SYMBOL(tsens_get_hw_id_mapping); |
| |
| static int tsens_tz_code_to_degc(int adc_code, int sensor_sw_id) |
| { |
| int degc, num, den, idx; |
| |
| idx = sensor_sw_id; |
| num = ((adc_code * tmdev->tsens_factor) - |
| tmdev->sensor[idx].offset); |
| den = (int) tmdev->sensor[idx].slope_mul_tsens_factor; |
| |
| if (num > 0) |
| degc = ((num + (den/2))/den); |
| else if (num < 0) |
| degc = ((num - (den/2))/den); |
| else |
| degc = num/den; |
| |
| pr_debug("raw_code:0x%x, sensor_num:%d, degc:%d\n", |
| adc_code, idx, degc); |
| return degc; |
| } |
| |
| static int tsens_tz_degc_to_code(int degc, int idx) |
| { |
| int code = ((degc * tmdev->sensor[idx].slope_mul_tsens_factor) |
| + tmdev->sensor[idx].offset)/tmdev->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; |
| pr_debug("raw_code:0x%x, sensor_num:%d, degc:%d\n", |
| code, idx, degc); |
| return code; |
| } |
| |
| static void msm_tsens_get_temp(int sensor_hw_num, unsigned long *temp) |
| { |
| unsigned int code, sensor_addr; |
| int sensor_sw_id = -EINVAL, rc = 0; |
| |
| if (!tmdev->prev_reading_avail) { |
| while (!(readl_relaxed(TSENS_TRDY_ADDR(tmdev->tsens_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(tmdev->tsens_addr); |
| code = readl_relaxed(sensor_addr + |
| (sensor_hw_num << TSENS_STATUS_ADDR_OFFSET)); |
| /* Obtain SW index to map the corresponding thermal zone's |
| * offset and slope for code to degc conversion. */ |
| rc = tsens_get_sw_id_mapping(sensor_hw_num, &sensor_sw_id); |
| if (rc < 0) { |
| pr_err("tsens mapping index not found\n"); |
| return; |
| } |
| |
| *temp = tsens_tz_code_to_degc((code & TSENS_SN_STATUS_TEMP_MASK), |
| sensor_sw_id); |
| } |
| |
| 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; |
| |
| msm_tsens_get_temp(tm_sensor->sensor_hw_num, temp); |
| |
| return 0; |
| } |
| |
| int tsens_get_temp(struct tsens_device *device, unsigned long *temp) |
| { |
| if (!tmdev) |
| return -ENODEV; |
| |
| msm_tsens_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; |
| } |
| |
| 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_WARM: |
| *type = THERMAL_TRIP_CONFIGURABLE_HI; |
| break; |
| case TSENS_TRIP_COOL: |
| *type = THERMAL_TRIP_CONFIGURABLE_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, 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; |
| |
| reg_cntl = readl_relaxed((TSENS_S0_UPPER_LOWER_STATUS_CTRL_ADDR |
| (tmdev->tsens_addr) + |
| (tm_sensor->sensor_hw_num * |
| TSENS_SN_ADDR_OFFSET))); |
| switch (trip) { |
| case TSENS_TRIP_WARM: |
| code = (reg_cntl & TSENS_UPPER_THRESHOLD_MASK) |
| >> TSENS_UPPER_THRESHOLD_SHIFT; |
| mask = TSENS_UPPER_STATUS_CLR; |
| |
| if (!(reg_cntl & TSENS_LOWER_STATUS_CLR)) |
| lo_code = (reg_cntl & TSENS_LOWER_THRESHOLD_MASK); |
| break; |
| case TSENS_TRIP_COOL: |
| code = (reg_cntl & TSENS_LOWER_THRESHOLD_MASK); |
| mask = TSENS_LOWER_STATUS_CLR; |
| |
| if (!(reg_cntl & TSENS_UPPER_STATUS_CLR)) |
| hi_code = (reg_cntl & TSENS_UPPER_THRESHOLD_MASK) |
| >> TSENS_UPPER_THRESHOLD_SHIFT; |
| break; |
| default: |
| return -EINVAL; |
| } |
| |
| if (mode == THERMAL_TRIP_ACTIVATION_DISABLED) |
| writel_relaxed(reg_cntl | mask, |
| (TSENS_S0_UPPER_LOWER_STATUS_CTRL_ADDR |
| (tmdev->tsens_addr) + |
| (tm_sensor->sensor_hw_num * TSENS_SN_ADDR_OFFSET))); |
| else { |
| if (code < lo_code || code > hi_code) { |
| pr_err("%s with invalid code %x\n", __func__, code); |
| return -EINVAL; |
| } |
| writel_relaxed(reg_cntl & ~mask, |
| (TSENS_S0_UPPER_LOWER_STATUS_CTRL_ADDR(tmdev->tsens_addr) + |
| (tm_sensor->sensor_hw_num * TSENS_SN_ADDR_OFFSET))); |
| } |
| 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; |
| int sensor_sw_id = -EINVAL, rc = 0; |
| |
| if (!tm_sensor || trip < 0 || !temp) |
| return -EINVAL; |
| |
| reg = readl_relaxed(TSENS_S0_UPPER_LOWER_STATUS_CTRL_ADDR |
| (tmdev->tsens_addr) + |
| (tm_sensor->sensor_hw_num * TSENS_SN_ADDR_OFFSET)); |
| switch (trip) { |
| case TSENS_TRIP_WARM: |
| reg = (reg & TSENS_UPPER_THRESHOLD_MASK) >> |
| TSENS_UPPER_THRESHOLD_SHIFT; |
| break; |
| case TSENS_TRIP_COOL: |
| reg = (reg & TSENS_LOWER_THRESHOLD_MASK); |
| break; |
| default: |
| return -EINVAL; |
| } |
| |
| rc = tsens_get_sw_id_mapping(tm_sensor->sensor_hw_num, &sensor_sw_id); |
| if (rc < 0) { |
| pr_err("tsens mapping index not found\n"); |
| return rc; |
| } |
| *temp = tsens_tz_code_to_degc(reg, sensor_sw_id); |
| |
| return 0; |
| } |
| |
| static int tsens_tz_notify(struct thermal_zone_device *thermal, |
| int count, enum thermal_trip_type type) |
| { |
| /* Critical temperature threshold are enabled and will |
| * shutdown the device once critical thresholds are crossed. */ |
| pr_debug("%s debug\n", __func__); |
| 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_cntl; |
| int code, hi_code, lo_code, code_err_chk, sensor_sw_id = 0, rc = 0; |
| |
| if (!tm_sensor || trip < 0) |
| return -EINVAL; |
| |
| rc = tsens_get_sw_id_mapping(tm_sensor->sensor_hw_num, &sensor_sw_id); |
| if (rc < 0) { |
| pr_err("tsens mapping index not found\n"); |
| return rc; |
| } |
| code_err_chk = code = tsens_tz_degc_to_code(temp, sensor_sw_id); |
| if (!tm_sensor || trip < 0) |
| return -EINVAL; |
| |
| lo_code = TSENS_THRESHOLD_MIN_CODE; |
| hi_code = TSENS_THRESHOLD_MAX_CODE; |
| |
| reg_cntl = readl_relaxed(TSENS_S0_UPPER_LOWER_STATUS_CTRL_ADDR |
| (tmdev->tsens_addr) + (tm_sensor->sensor_hw_num * |
| TSENS_SN_ADDR_OFFSET)); |
| switch (trip) { |
| case TSENS_TRIP_WARM: |
| code <<= TSENS_UPPER_THRESHOLD_SHIFT; |
| reg_cntl &= ~TSENS_UPPER_THRESHOLD_MASK; |
| if (!(reg_cntl & TSENS_LOWER_STATUS_CLR)) |
| lo_code = (reg_cntl & TSENS_LOWER_THRESHOLD_MASK); |
| break; |
| case TSENS_TRIP_COOL: |
| reg_cntl &= ~TSENS_LOWER_THRESHOLD_MASK; |
| if (!(reg_cntl & TSENS_UPPER_STATUS_CLR)) |
| hi_code = (reg_cntl & TSENS_UPPER_THRESHOLD_MASK) |
| >> TSENS_UPPER_THRESHOLD_SHIFT; |
| break; |
| default: |
| return -EINVAL; |
| } |
| |
| if (code_err_chk < lo_code || code_err_chk > hi_code) |
| return -EINVAL; |
| |
| writel_relaxed(reg_cntl | code, (TSENS_S0_UPPER_LOWER_STATUS_CTRL_ADDR |
| (tmdev->tsens_addr) + |
| (tm_sensor->sensor_hw_num * |
| TSENS_SN_ADDR_OFFSET))); |
| mb(); |
| return 0; |
| } |
| |
| static struct thermal_zone_device_ops tsens_thermal_zone_ops = { |
| .get_temp = tsens_tz_get_temp, |
| .get_mode = tsens_tz_get_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, |
| .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 i, status, threshold; |
| unsigned int sensor_status_addr, sensor_status_ctrl_addr; |
| int sensor_sw_id = -EINVAL, rc = 0; |
| |
| sensor_status_addr = |
| (unsigned int)TSENS_S0_STATUS_ADDR(tmdev->tsens_addr); |
| sensor_status_ctrl_addr = |
| (unsigned int)TSENS_S0_UPPER_LOWER_STATUS_CTRL_ADDR |
| (tmdev->tsens_addr); |
| for (i = 0; i < tm->tsens_num_sensor; i++) { |
| bool upper_thr = false, lower_thr = false; |
| uint32_t addr_offset; |
| |
| addr_offset = tm->sensor[i].sensor_hw_num * |
| TSENS_SN_ADDR_OFFSET; |
| status = readl_relaxed(sensor_status_addr + addr_offset); |
| threshold = readl_relaxed(sensor_status_ctrl_addr + |
| addr_offset); |
| if (status & TSENS_SN_STATUS_UPPER_STATUS) { |
| writel_relaxed(threshold | TSENS_UPPER_STATUS_CLR, |
| TSENS_S0_UPPER_LOWER_STATUS_CTRL_ADDR( |
| tmdev->tsens_addr + addr_offset)); |
| upper_thr = true; |
| } |
| if (status & TSENS_SN_STATUS_LOWER_STATUS) { |
| writel_relaxed(threshold | TSENS_LOWER_STATUS_CLR, |
| TSENS_S0_UPPER_LOWER_STATUS_CTRL_ADDR( |
| tmdev->tsens_addr + addr_offset)); |
| lower_thr = true; |
| } |
| if (upper_thr || lower_thr) { |
| unsigned long temp; |
| enum thermal_trip_type trip = |
| THERMAL_TRIP_CONFIGURABLE_LOW; |
| |
| if (upper_thr) |
| trip = THERMAL_TRIP_CONFIGURABLE_HI; |
| tsens_tz_get_temp(tm->sensor[i].tz_dev, &temp); |
| thermal_sensor_trip(tm->sensor[i].tz_dev, trip, temp); |
| |
| /* Notify user space */ |
| queue_work(tm->tsens_wq, &tm->sensor[i].work); |
| rc = tsens_get_sw_id_mapping( |
| tm->sensor[i].sensor_hw_num, |
| &sensor_sw_id); |
| if (rc < 0) |
| pr_err("tsens mapping index not found\n"); |
| pr_debug("sensor:%d trigger temp (%d degC)\n", |
| tm->sensor[i].sensor_hw_num, |
| tsens_tz_code_to_degc((status & |
| TSENS_SN_STATUS_TEMP_MASK), |
| sensor_sw_id)); |
| } |
| } |
| mb(); |
| } |
| |
| static irqreturn_t tsens_isr(int irq, void *data) |
| { |
| queue_work(tmdev->tsens_wq, &tmdev->tsens_work); |
| |
| return IRQ_HANDLED; |
| } |
| |
| static void tsens_hw_init(void) |
| { |
| unsigned int reg_cntl = 0, sensor_en = 0; |
| unsigned int i; |
| |
| if (tmdev->tsens_local_init) { |
| writel_relaxed(reg_cntl, TSENS_CTRL_ADDR(tmdev->tsens_addr)); |
| writel_relaxed(reg_cntl | TSENS_SW_RST, |
| TSENS_CTRL_ADDR(tmdev->tsens_addr)); |
| reg_cntl |= (TSENS_62_5_MS_MEAS_PERIOD << |
| TSENS_MEAS_PERIOD_SHIFT); |
| for (i = 0; i < tmdev->tsens_num_sensor; i++) |
| sensor_en |= (1 << tmdev->sensor[i].sensor_hw_num); |
| sensor_en <<= TSENS_SENSOR0_SHIFT; |
| reg_cntl |= (sensor_en | TSENS_EN); |
| writel_relaxed(reg_cntl, TSENS_CTRL_ADDR(tmdev->tsens_addr)); |
| writel_relaxed(TSENS_GLOBAL_INIT_DATA, |
| TSENS_GLOBAL_CONFIG(tmdev->tsens_addr)); |
| writel_relaxed(TSENS_S0_MAIN_CFG_INIT_DATA, |
| TSENS_S0_MAIN_CONFIG(tmdev->tsens_addr)); |
| for (i = 0; i < tmdev->tsens_num_sensor; i++) { |
| writel_relaxed(TSENS_SN_MIN_MAX_STATUS_CTRL_DATA, |
| TSENS_SN_MIN_MAX_STATUS_CTRL(tmdev->tsens_addr) |
| + (tmdev->sensor[i].sensor_hw_num * |
| TSENS_SN_ADDR_OFFSET)); |
| writel_relaxed(TSENS_SN_REMOTE_CFG_DATA, |
| TSENS_SN_REMOTE_CONFIG(tmdev->tsens_addr) |
| + (tmdev->sensor[i].sensor_hw_num * |
| TSENS_SN_ADDR_OFFSET)); |
| } |
| pr_debug("Local TSENS control initialization\n"); |
| } |
| writel_relaxed(TSENS_INTERRUPT_EN, |
| TSENS_UPPER_LOWER_INTERRUPT_CTRL(tmdev->tsens_addr)); |
| } |
| |
| static int tsens_calib_8x10_sensors(void) |
| { |
| int i, tsens_base0_data = 0, tsens0_point1 = 0, tsens1_point1 = 0; |
| int tsens0_point2 = 0, tsens1_point2 = 0; |
| int tsens_base1_data = 0, tsens_calibration_mode = 0; |
| uint32_t calib_data[2], calib_redun_sel; |
| uint32_t calib_tsens_point1_data[2], calib_tsens_point2_data[2]; |
| |
| if (tmdev->calibration_less_mode) |
| goto calibration_less_mode; |
| |
| calib_redun_sel = readl_relaxed( |
| TSENS_EEPROM_8X10_2(tmdev->tsens_calib_addr)); |
| calib_redun_sel = calib_redun_sel & TSENS_8X10_REDUN_SEL_MASK; |
| calib_redun_sel >>= TSENS_8X10_REDUN_SEL_SHIFT; |
| pr_debug("calib_redun_sel:%x\n", calib_redun_sel); |
| |
| if (calib_redun_sel == TSENS_QFPROM_BACKUP_SEL) { |
| calib_data[0] = readl_relaxed( |
| TSENS_EEPROM_8X10_SPARE_1(tmdev->tsens_calib_addr)); |
| calib_data[1] = readl_relaxed( |
| TSENS_EEPROM_8X10_SPARE_2(tmdev->tsens_calib_addr)); |
| } else { |
| calib_data[0] = readl_relaxed( |
| TSENS_EEPROM_8X10_1(tmdev->tsens_calib_addr)); |
| calib_data[1] = readl_relaxed( |
| (TSENS_EEPROM_8X10_1(tmdev->tsens_calib_addr) + |
| TSENS_EEPROM_8X10_1_OFFSET)); |
| } |
| |
| tsens_calibration_mode = (calib_data[0] & TSENS_8X10_TSENS_CAL_SEL) |
| >> TSENS_8X10_CAL_SEL_SHIFT; |
| pr_debug("calib mode scheme:%x\n", tsens_calibration_mode); |
| |
| if ((tsens_calibration_mode == TSENS_TWO_POINT_CALIB) || |
| (tsens_calibration_mode == TSENS_ONE_POINT_CALIB_OPTION_2)) { |
| tsens_base0_data = (calib_data[0] & TSENS_8X10_BASE0_MASK); |
| tsens0_point1 = (calib_data[0] & TSENS0_8X10_POINT1_MASK) >> |
| TSENS0_8X10_POINT1_SHIFT; |
| tsens1_point1 = calib_data[1] & TSENS1_8X10_POINT1_MASK; |
| } else |
| goto calibration_less_mode; |
| |
| if (tsens_calibration_mode == TSENS_TWO_POINT_CALIB) { |
| tsens_base1_data = (calib_data[0] & TSENS_8X10_BASE1_MASK) >> |
| TSENS_8X10_BASE1_SHIFT; |
| tsens0_point2 = (calib_data[0] & TSENS0_8X10_POINT2_MASK) >> |
| TSENS0_8X10_POINT2_SHIFT; |
| tsens1_point2 = (calib_data[1] & TSENS1_8X10_POINT2_MASK) >> |
| TSENS1_8X10_POINT2_SHIFT; |
| } |
| |
| if (tsens_calibration_mode == 0) { |
| calibration_less_mode: |
| pr_debug("TSENS is calibrationless mode\n"); |
| for (i = 0; i < tmdev->tsens_num_sensor; i++) |
| calib_tsens_point2_data[i] = 780; |
| calib_tsens_point1_data[0] = 595; |
| calib_tsens_point1_data[1] = 629; |
| goto compute_intercept_slope; |
| } |
| |
| if ((tsens_calibration_mode == TSENS_ONE_POINT_CALIB_OPTION_2) || |
| (tsens_calibration_mode == TSENS_TWO_POINT_CALIB)) { |
| calib_tsens_point1_data[0] = |
| ((((tsens_base0_data) + tsens0_point1) << 2) | |
| TSENS_BIT_APPEND); |
| calib_tsens_point1_data[1] = |
| ((((tsens_base0_data) + tsens1_point1) << 2) | |
| TSENS_BIT_APPEND); |
| } |
| |
| if (tsens_calibration_mode == TSENS_TWO_POINT_CALIB) { |
| pr_debug("two point calibration calculation\n"); |
| calib_tsens_point2_data[0] = |
| (((tsens_base1_data + tsens0_point2) << 2) | |
| TSENS_BIT_APPEND); |
| calib_tsens_point2_data[1] = |
| (((tsens_base1_data + tsens1_point2) << 2) | |
| TSENS_BIT_APPEND); |
| } |
| |
| compute_intercept_slope: |
| for (i = 0; i < tmdev->tsens_num_sensor; i++) { |
| int32_t num = 0, den = 0; |
| tmdev->sensor[i].calib_data_point2 = calib_tsens_point2_data[i]; |
| tmdev->sensor[i].calib_data_point1 = calib_tsens_point1_data[i]; |
| pr_debug("sensor:%d - calib_data_point1:0x%x, calib_data_point2:0x%x\n", |
| i, tmdev->sensor[i].calib_data_point1, |
| tmdev->sensor[i].calib_data_point2); |
| if (tsens_calibration_mode == TSENS_TWO_POINT_CALIB) { |
| /* slope (m) = adc_code2 - adc_code1 (y2 - y1)/ |
| temp_120_degc - temp_30_degc (x2 - x1) */ |
| num = tmdev->sensor[i].calib_data_point2 - |
| tmdev->sensor[i].calib_data_point1; |
| num *= tmdev->tsens_factor; |
| den = TSENS_CAL_DEGC_POINT2 - TSENS_CAL_DEGC_POINT1; |
| tmdev->sensor[i].slope_mul_tsens_factor = num/den; |
| } |
| tmdev->sensor[i].offset = (tmdev->sensor[i].calib_data_point1 * |
| tmdev->tsens_factor) - (TSENS_CAL_DEGC_POINT1 * |
| tmdev->sensor[i].slope_mul_tsens_factor); |
| INIT_WORK(&tmdev->sensor[i].work, notify_uspace_tsens_fn); |
| tmdev->prev_reading_avail = false; |
| } |
| |
| return 0; |
| } |
| |
| static int tsens_calib_8x26_sensors(void) |
| { |
| int i, tsens_base0_data = 0, tsens0_point1 = 0, tsens1_point1 = 0; |
| int tsens2_point1 = 0, tsens3_point1 = 0, tsens4_point1 = 0; |
| int tsens5_point1 = 0, tsens6_point1 = 0, tsens6_point2 = 0; |
| int tsens0_point2 = 0, tsens1_point2 = 0, tsens2_point2 = 0; |
| int tsens3_point2 = 0, tsens4_point2 = 0, tsens5_point2 = 0; |
| int tsens_base1_data = 0, tsens_calibration_mode = 0; |
| uint32_t calib_data[6]; |
| uint32_t calib_tsens_point1_data[7], calib_tsens_point2_data[7]; |
| |
| if (tmdev->calibration_less_mode) |
| goto calibration_less_mode; |
| |
| for (i = 0; i < TSENS_8X26_MAIN_CALIB_ADDR_RANGE; i++) |
| calib_data[i] = readl_relaxed( |
| (TSENS_EEPROM_8X26_1(tmdev->tsens_calib_addr)) |
| + (i * TSENS_SN_ADDR_OFFSET)); |
| calib_data[4] = readl_relaxed( |
| (TSENS_EEPROM_8X26_2(tmdev->tsens_calib_addr))); |
| calib_data[5] = readl_relaxed( |
| (TSENS_EEPROM_8X26_2(tmdev->tsens_calib_addr)) + 0x8); |
| |
| tsens_calibration_mode = (calib_data[5] & TSENS_8X26_TSENS_CAL_SEL) |
| >> TSENS_8X26_CAL_SEL_SHIFT; |
| pr_debug("calib mode scheme:%x\n", tsens_calibration_mode); |
| |
| if ((tsens_calibration_mode == TSENS_TWO_POINT_CALIB) || |
| (tsens_calibration_mode == TSENS_ONE_POINT_CALIB_OPTION_2)) { |
| tsens_base0_data = (calib_data[0] & TSENS_8X26_BASE0_MASK) |
| >> TSENS_8X26_BASE0_SHIFT; |
| tsens0_point1 = (calib_data[0] & TSENS0_8X26_POINT1_MASK) >> |
| TSENS0_8X26_POINT1_SHIFT; |
| tsens1_point1 = calib_data[1] & TSENS1_8X26_POINT1_MASK; |
| tsens2_point1 = (calib_data[1] & TSENS2_8X26_POINT1_MASK) >> |
| TSENS2_8X26_POINT1_SHIFT; |
| tsens3_point1 = (calib_data[1] & TSENS3_8X26_POINT1_MASK) >> |
| TSENS3_8X26_POINT1_SHIFT; |
| tsens4_point1 = (calib_data[1] & TSENS4_8X26_POINT1_MASK) >> |
| TSENS4_8X26_POINT1_SHIFT; |
| tsens5_point1 = (calib_data[1] & TSENS5_8X26_POINT1_MASK) >> |
| TSENS5_8X26_POINT1_SHIFT; |
| tsens6_point1 = (calib_data[2] & TSENS6_8X26_POINT1_MASK) >> |
| TSENS6_8X26_POINT1_SHIFT; |
| } else |
| goto calibration_less_mode; |
| |
| if (tsens_calibration_mode == TSENS_TWO_POINT_CALIB) { |
| tsens_base1_data = (calib_data[3] & TSENS_8X26_BASE1_MASK); |
| tsens0_point2 = (calib_data[3] & TSENS0_8X26_POINT2_MASK) >> |
| TSENS0_8X26_POINT2_SHIFT; |
| tsens1_point2 = (calib_data[3] & TSENS1_8X26_POINT2_MASK) >> |
| TSENS1_8X26_POINT2_SHIFT; |
| tsens2_point2 = (calib_data[3] & TSENS2_8X26_POINT2_MASK) >> |
| TSENS2_8X26_POINT2_SHIFT; |
| tsens3_point2 = (calib_data[3] & TSENS3_8X26_POINT2_MASK) >> |
| TSENS3_8X26_POINT2_SHIFT; |
| tsens4_point2 = (calib_data[4] & TSENS4_8X26_POINT2_MASK) >> |
| TSENS4_8X26_POINT2_SHIFT; |
| tsens5_point2 = (calib_data[4] & TSENS5_8X26_POINT2_MASK) >> |
| TSENS5_8X26_POINT2_SHIFT; |
| tsens6_point2 = (calib_data[5] & TSENS6_8X26_POINT2_MASK) >> |
| TSENS6_8X26_POINT2_SHIFT; |
| } |
| |
| if (tsens_calibration_mode == 0) { |
| calibration_less_mode: |
| pr_debug("TSENS is calibrationless mode\n"); |
| for (i = 0; i < tmdev->tsens_num_sensor; i++) |
| calib_tsens_point2_data[i] = 780; |
| calib_tsens_point1_data[0] = 595; |
| calib_tsens_point1_data[1] = 625; |
| calib_tsens_point1_data[2] = 553; |
| calib_tsens_point1_data[3] = 578; |
| calib_tsens_point1_data[4] = 505; |
| calib_tsens_point1_data[5] = 509; |
| calib_tsens_point1_data[6] = 507; |
| goto compute_intercept_slope; |
| } |
| |
| if ((tsens_calibration_mode == TSENS_ONE_POINT_CALIB_OPTION_2) || |
| (tsens_calibration_mode == TSENS_TWO_POINT_CALIB)) { |
| calib_tsens_point1_data[0] = |
| ((((tsens_base0_data) + tsens0_point1) << 2) | |
| TSENS_BIT_APPEND); |
| calib_tsens_point1_data[1] = |
| ((((tsens_base0_data) + tsens1_point1) << 2) | |
| TSENS_BIT_APPEND); |
| calib_tsens_point1_data[2] = |
| ((((tsens_base0_data) + tsens2_point1) << 2) | |
| TSENS_BIT_APPEND); |
| calib_tsens_point1_data[3] = |
| ((((tsens_base0_data) + tsens3_point1) << 2) | |
| TSENS_BIT_APPEND); |
| calib_tsens_point1_data[4] = |
| ((((tsens_base0_data) + tsens4_point1) << 2) | |
| TSENS_BIT_APPEND); |
| calib_tsens_point1_data[5] = |
| ((((tsens_base0_data) + tsens5_point1) << 2) | |
| TSENS_BIT_APPEND); |
| calib_tsens_point1_data[6] = |
| ((((tsens_base0_data) + tsens6_point1) << 2) | |
| TSENS_BIT_APPEND); |
| } |
| |
| if (tsens_calibration_mode == TSENS_TWO_POINT_CALIB) { |
| pr_debug("two point calibration calculation\n"); |
| calib_tsens_point2_data[0] = |
| (((tsens_base1_data + tsens0_point2) << 2) | |
| TSENS_BIT_APPEND); |
| calib_tsens_point2_data[1] = |
| (((tsens_base1_data + tsens1_point2) << 2) | |
| TSENS_BIT_APPEND); |
| calib_tsens_point2_data[2] = |
| (((tsens_base1_data + tsens2_point2) << 2) | |
| TSENS_BIT_APPEND); |
| calib_tsens_point2_data[3] = |
| (((tsens_base1_data + tsens3_point2) << 2) | |
| TSENS_BIT_APPEND); |
| calib_tsens_point2_data[4] = |
| (((tsens_base1_data + tsens4_point2) << 2) | |
| TSENS_BIT_APPEND); |
| calib_tsens_point2_data[5] = |
| (((tsens_base1_data + tsens5_point2) << 2) | |
| TSENS_BIT_APPEND); |
| calib_tsens_point2_data[6] = |
| (((tsens_base1_data + tsens6_point2) << 2) | |
| TSENS_BIT_APPEND); |
| } |
| |
| compute_intercept_slope: |
| for (i = 0; i < tmdev->tsens_num_sensor; i++) { |
| int32_t num = 0, den = 0; |
| tmdev->sensor[i].calib_data_point2 = calib_tsens_point2_data[i]; |
| tmdev->sensor[i].calib_data_point1 = calib_tsens_point1_data[i]; |
| pr_debug("sensor:%d - calib_data_point1:0x%x, calib_data_point2:0x%x\n", |
| i, tmdev->sensor[i].calib_data_point1, |
| tmdev->sensor[i].calib_data_point2); |
| if (tsens_calibration_mode == TSENS_TWO_POINT_CALIB) { |
| /* slope (m) = adc_code2 - adc_code1 (y2 - y1)/ |
| temp_120_degc - temp_30_degc (x2 - x1) */ |
| num = tmdev->sensor[i].calib_data_point2 - |
| tmdev->sensor[i].calib_data_point1; |
| num *= tmdev->tsens_factor; |
| den = TSENS_CAL_DEGC_POINT2 - TSENS_CAL_DEGC_POINT1; |
| tmdev->sensor[i].slope_mul_tsens_factor = num/den; |
| } |
| tmdev->sensor[i].offset = (tmdev->sensor[i].calib_data_point1 * |
| tmdev->tsens_factor) - (TSENS_CAL_DEGC_POINT1 * |
| tmdev->sensor[i].slope_mul_tsens_factor); |
| INIT_WORK(&tmdev->sensor[i].work, notify_uspace_tsens_fn); |
| tmdev->prev_reading_avail = false; |
| } |
| |
| return 0; |
| } |
| |
| static int tsens_calib_8974_sensors(void) |
| { |
| int i, tsens_base1_data = 0, tsens0_point1 = 0, tsens1_point1 = 0; |
| int tsens2_point1 = 0, tsens3_point1 = 0, tsens4_point1 = 0; |
| int tsens5_point1 = 0, tsens6_point1 = 0, tsens7_point1 = 0; |
| int tsens8_point1 = 0, tsens9_point1 = 0, tsens10_point1 = 0; |
| int tsens0_point2 = 0, tsens1_point2 = 0, tsens2_point2 = 0; |
| int tsens3_point2 = 0, tsens4_point2 = 0, tsens5_point2 = 0; |
| int tsens6_point2 = 0, tsens7_point2 = 0, tsens8_point2 = 0; |
| int tsens9_point2 = 0, tsens10_point2 = 0; |
| int tsens_base2_data = 0, tsens_calibration_mode = 0, temp = 0; |
| uint32_t calib_data[6], calib_redun_sel, calib_data_backup[4]; |
| uint32_t calib_tsens_point1_data[11], calib_tsens_point2_data[11]; |
| |
| if (tmdev->calibration_less_mode) |
| goto calibration_less_mode; |
| |
| calib_redun_sel = readl_relaxed( |
| TSENS_EEPROM_REDUNDANCY_SEL(tmdev->tsens_calib_addr)); |
| calib_redun_sel = calib_redun_sel & TSENS_QFPROM_BACKUP_REDUN_SEL; |
| calib_redun_sel >>= TSENS_QFPROM_BACKUP_REDUN_SHIFT; |
| pr_debug("calib_redun_sel:%x\n", calib_redun_sel); |
| |
| for (i = 0; i < TSENS_MAIN_CALIB_ADDR_RANGE; i++) { |
| calib_data[i] = readl_relaxed( |
| (TSENS_EEPROM(tmdev->tsens_calib_addr)) |
| + (i * TSENS_SN_ADDR_OFFSET)); |
| pr_debug("calib raw data row%d:0x%x\n", i, calib_data[i]); |
| } |
| |
| if (calib_redun_sel == TSENS_QFPROM_BACKUP_SEL) { |
| tsens_calibration_mode = (calib_data[4] & TSENS_CAL_SEL_0_1) |
| >> TSENS_CAL_SEL_SHIFT; |
| temp = (calib_data[5] & TSENS_CAL_SEL_2) |
| >> TSENS_CAL_SEL_SHIFT_2; |
| tsens_calibration_mode |= temp; |
| pr_debug("backup calib mode:%x\n", calib_redun_sel); |
| |
| for (i = 0; i < TSENS_BACKUP_CALIB_ADDR_RANGE; i++) |
| calib_data_backup[i] = readl_relaxed( |
| (TSENS_EEPROM_BACKUP_REGION( |
| tmdev->tsens_calib_addr)) |
| + (i * TSENS_SN_ADDR_OFFSET)); |
| |
| if ((tsens_calibration_mode == TSENS_ONE_POINT_CALIB) |
| || (tsens_calibration_mode == |
| TSENS_TWO_POINT_CALIB) || |
| (tsens_calibration_mode == |
| TSENS_ONE_POINT_CALIB_OPTION_2)) { |
| tsens_base1_data = (calib_data_backup[0] & |
| TSENS_BASE1_MASK); |
| tsens0_point1 = (calib_data_backup[0] & |
| TSENS0_POINT1_MASK) >> |
| TSENS0_POINT1_SHIFT; |
| tsens1_point1 = (calib_data_backup[0] & |
| TSENS1_POINT1_MASK) >> TSENS1_POINT1_SHIFT; |
| tsens2_point1 = (calib_data_backup[0] & |
| TSENS2_POINT1_MASK) >> TSENS2_POINT1_SHIFT; |
| tsens3_point1 = (calib_data_backup[0] & |
| TSENS3_POINT1_MASK) >> TSENS3_POINT1_SHIFT; |
| tsens4_point1 = (calib_data_backup[1] & |
| TSENS4_POINT1_MASK); |
| tsens5_point1 = (calib_data_backup[1] & |
| TSENS5_POINT1_MASK) >> TSENS5_POINT1_SHIFT; |
| tsens6_point1 = (calib_data_backup[1] & |
| TSENS6_POINT1_MASK) >> TSENS6_POINT1_SHIFT; |
| tsens7_point1 = (calib_data_backup[1] & |
| TSENS7_POINT1_MASK) >> TSENS7_POINT1_SHIFT; |
| tsens8_point1 = (calib_data_backup[2] & |
| TSENS8_POINT1_MASK_BACKUP) >> |
| TSENS8_POINT1_SHIFT; |
| tsens9_point1 = (calib_data_backup[2] & |
| TSENS9_POINT1_MASK_BACKUP) >> |
| TSENS9_POINT1_BACKUP_SHIFT; |
| tsens10_point1 = (calib_data_backup[2] & |
| TSENS10_POINT1_MASK_BACKUP) >> |
| TSENS10_POINT1_BACKUP_SHIFT; |
| } else |
| goto calibration_less_mode; |
| |
| if (tsens_calibration_mode == TSENS_TWO_POINT_CALIB) { |
| tsens_base2_data = (calib_data_backup[2] & |
| TSENS_BASE2_BACKUP_MASK) >> |
| TSENS_POINT2_BASE_BACKUP_SHIFT; |
| tsens0_point2 = (calib_data_backup[2] & |
| TSENS0_POINT2_BACKUP_MASK) >> |
| TSENS0_POINT2_BACKUP_SHIFT; |
| tsens1_point2 = (calib_data_backup[3] & |
| TSENS1_POINT2_BACKUP_MASK); |
| tsens2_point2 = (calib_data_backup[3] & |
| TSENS2_POINT2_BACKUP_MASK) >> |
| TSENS2_POINT2_BACKUP_SHIFT; |
| tsens3_point2 = (calib_data_backup[3] & |
| TSENS3_POINT2_BACKUP_MASK) >> |
| TSENS3_POINT2_BACKUP_SHIFT; |
| tsens4_point2 = (calib_data_backup[3] & |
| TSENS4_POINT2_BACKUP_MASK) >> |
| TSENS4_POINT2_BACKUP_SHIFT; |
| tsens5_point2 = (calib_data[4] & |
| TSENS5_POINT2_BACKUP_MASK) >> |
| TSENS5_POINT2_BACKUP_SHIFT; |
| tsens6_point2 = (calib_data[5] & |
| TSENS6_POINT2_BACKUP_MASK); |
| tsens7_point2 = (calib_data[5] & |
| TSENS7_POINT2_BACKUP_MASK) >> |
| TSENS7_POINT2_BACKUP_SHIFT; |
| tsens8_point2 = (calib_data[5] & |
| TSENS8_POINT2_BACKUP_MASK) >> |
| TSENS8_POINT2_BACKUP_SHIFT; |
| tsens9_point2 = (calib_data[5] & |
| TSENS9_POINT2_BACKUP_MASK) >> |
| TSENS9_POINT2_BACKUP_SHIFT; |
| tsens10_point2 = (calib_data[5] & |
| TSENS10_POINT2_BACKUP_MASK) |
| >> TSENS10_POINT2_BACKUP_SHIFT; |
| } |
| } else { |
| tsens_calibration_mode = (calib_data[1] & TSENS_CAL_SEL_0_1) |
| >> TSENS_CAL_SEL_SHIFT; |
| temp = (calib_data[3] & TSENS_CAL_SEL_2) |
| >> TSENS_CAL_SEL_SHIFT_2; |
| tsens_calibration_mode |= temp; |
| pr_debug("calib mode scheme:%x\n", tsens_calibration_mode); |
| if ((tsens_calibration_mode == TSENS_ONE_POINT_CALIB) || |
| (tsens_calibration_mode == |
| TSENS_ONE_POINT_CALIB_OPTION_2) || |
| (tsens_calibration_mode == TSENS_TWO_POINT_CALIB)) { |
| tsens_base1_data = (calib_data[0] & TSENS_BASE1_MASK); |
| tsens0_point1 = (calib_data[0] & TSENS0_POINT1_MASK) >> |
| TSENS0_POINT1_SHIFT; |
| tsens1_point1 = (calib_data[0] & TSENS1_POINT1_MASK) >> |
| TSENS1_POINT1_SHIFT; |
| tsens2_point1 = (calib_data[0] & TSENS2_POINT1_MASK) >> |
| TSENS2_POINT1_SHIFT; |
| tsens3_point1 = (calib_data[0] & TSENS3_POINT1_MASK) >> |
| TSENS3_POINT1_SHIFT; |
| tsens4_point1 = (calib_data[1] & TSENS4_POINT1_MASK); |
| tsens5_point1 = (calib_data[1] & TSENS5_POINT1_MASK) >> |
| TSENS5_POINT1_SHIFT; |
| tsens6_point1 = (calib_data[1] & TSENS6_POINT1_MASK) >> |
| TSENS6_POINT1_SHIFT; |
| tsens7_point1 = (calib_data[1] & TSENS7_POINT1_MASK) >> |
| TSENS7_POINT1_SHIFT; |
| tsens8_point1 = (calib_data[1] & TSENS8_POINT1_MASK) >> |
| TSENS8_POINT1_SHIFT; |
| tsens9_point1 = (calib_data[2] & TSENS9_POINT1_MASK); |
| tsens10_point1 = (calib_data[2] & TSENS10_POINT1_MASK) |
| >> TSENS10_POINT1_SHIFT; |
| } else |
| goto calibration_less_mode; |
| |
| if (tsens_calibration_mode == TSENS_TWO_POINT_CALIB) { |
| tsens_base2_data = (calib_data[2] & TSENS_BASE2_MASK) >> |
| TSENS_POINT2_BASE_SHIFT; |
| tsens0_point2 = (calib_data[2] & TSENS0_POINT2_MASK) >> |
| TSENS0_POINT2_SHIFT; |
| tsens1_point2 = (calib_data[2] & TSENS1_POINT2_MASK) >> |
| TSENS1_POINT2_SHIFT; |
| tsens2_point2 = (calib_data[3] & TSENS2_POINT2_MASK); |
| tsens3_point2 = (calib_data[3] & TSENS3_POINT2_MASK) >> |
| TSENS3_POINT2_SHIFT; |
| tsens4_point2 = (calib_data[3] & TSENS4_POINT2_MASK) >> |
| TSENS4_POINT2_SHIFT; |
| tsens5_point2 = (calib_data[3] & TSENS5_POINT2_MASK) >> |
| TSENS5_POINT2_SHIFT; |
| tsens6_point2 = (calib_data[3] & TSENS6_POINT2_MASK) >> |
| TSENS6_POINT2_SHIFT; |
| tsens7_point2 = (calib_data[4] & TSENS7_POINT2_MASK); |
| tsens8_point2 = (calib_data[4] & TSENS8_POINT2_MASK) >> |
| TSENS8_POINT2_SHIFT; |
| tsens9_point2 = (calib_data[4] & TSENS9_POINT2_MASK) >> |
| TSENS9_POINT2_SHIFT; |
| tsens10_point2 = (calib_data[4] & TSENS10_POINT2_MASK) |
| >> TSENS10_POINT2_SHIFT; |
| } |
| |
| if (tsens_calibration_mode == 0) { |
| calibration_less_mode: |
| pr_debug("TSENS is calibrationless mode\n"); |
| for (i = 0; i < tmdev->tsens_num_sensor; i++) |
| calib_tsens_point2_data[i] = 780; |
| calib_tsens_point1_data[0] = 502; |
| calib_tsens_point1_data[1] = 509; |
| calib_tsens_point1_data[2] = 503; |
| calib_tsens_point1_data[3] = 509; |
| calib_tsens_point1_data[4] = 505; |
| calib_tsens_point1_data[5] = 509; |
| calib_tsens_point1_data[6] = 507; |
| calib_tsens_point1_data[7] = 510; |
| calib_tsens_point1_data[8] = 508; |
| calib_tsens_point1_data[9] = 509; |
| calib_tsens_point1_data[10] = 508; |
| goto compute_intercept_slope; |
| } |
| } |
| |
| if (tsens_calibration_mode == TSENS_ONE_POINT_CALIB) { |
| calib_tsens_point1_data[0] = |
| (((tsens_base1_data) << 2) | TSENS_BIT_APPEND) |
| + tsens0_point1; |
| calib_tsens_point1_data[1] = |
| (((tsens_base1_data) << 2) | TSENS_BIT_APPEND) |
| + tsens1_point1; |
| calib_tsens_point1_data[2] = |
| (((tsens_base1_data) << 2) | TSENS_BIT_APPEND) |
| + tsens2_point1; |
| calib_tsens_point1_data[3] = |
| (((tsens_base1_data) << 2) | TSENS_BIT_APPEND) |
| + tsens3_point1; |
| calib_tsens_point1_data[4] = |
| (((tsens_base1_data) << 2) | TSENS_BIT_APPEND) |
| + tsens4_point1; |
| calib_tsens_point1_data[5] = |
| (((tsens_base1_data) << 2) | TSENS_BIT_APPEND) |
| + tsens5_point1; |
| calib_tsens_point1_data[6] = |
| (((tsens_base1_data) << 2) | TSENS_BIT_APPEND) |
| + tsens6_point1; |
| calib_tsens_point1_data[7] = |
| (((tsens_base1_data) << 2) | TSENS_BIT_APPEND) |
| + tsens7_point1; |
| calib_tsens_point1_data[8] = |
| (((tsens_base1_data) << 2) | TSENS_BIT_APPEND) |
| + tsens8_point1; |
| calib_tsens_point1_data[9] = |
| (((tsens_base1_data) << 2) | TSENS_BIT_APPEND) |
| + tsens9_point1; |
| calib_tsens_point1_data[10] = |
| (((tsens_base1_data) << 2) | TSENS_BIT_APPEND) |
| + tsens10_point1; |
| } |
| |
| if ((tsens_calibration_mode == TSENS_ONE_POINT_CALIB_OPTION_2) || |
| (tsens_calibration_mode == TSENS_TWO_POINT_CALIB)) { |
| pr_debug("one point calibration calculation\n"); |
| calib_tsens_point1_data[0] = |
| ((((tsens_base1_data) + tsens0_point1) << 2) | |
| TSENS_BIT_APPEND); |
| calib_tsens_point1_data[1] = |
| ((((tsens_base1_data) + tsens1_point1) << 2) | |
| TSENS_BIT_APPEND); |
| calib_tsens_point1_data[2] = |
| ((((tsens_base1_data) + tsens2_point1) << 2) | |
| TSENS_BIT_APPEND); |
| calib_tsens_point1_data[3] = |
| ((((tsens_base1_data) + tsens3_point1) << 2) | |
| TSENS_BIT_APPEND); |
| calib_tsens_point1_data[4] = |
| ((((tsens_base1_data) + tsens4_point1) << 2) | |
| TSENS_BIT_APPEND); |
| calib_tsens_point1_data[5] = |
| ((((tsens_base1_data) + tsens5_point1) << 2) | |
| TSENS_BIT_APPEND); |
| calib_tsens_point1_data[6] = |
| ((((tsens_base1_data) + tsens6_point1) << 2) | |
| TSENS_BIT_APPEND); |
| calib_tsens_point1_data[7] = |
| ((((tsens_base1_data) + tsens7_point1) << 2) | |
| TSENS_BIT_APPEND); |
| calib_tsens_point1_data[8] = |
| ((((tsens_base1_data) + tsens8_point1) << 2) | |
| TSENS_BIT_APPEND); |
| calib_tsens_point1_data[9] = |
| ((((tsens_base1_data) + tsens9_point1) << 2) | |
| TSENS_BIT_APPEND); |
| calib_tsens_point1_data[10] = |
| ((((tsens_base1_data) + tsens10_point1) << 2) | |
| TSENS_BIT_APPEND); |
| } |
| |
| if (tsens_calibration_mode == TSENS_TWO_POINT_CALIB) { |
| pr_debug("two point calibration calculation\n"); |
| calib_tsens_point2_data[0] = |
| (((tsens_base2_data + tsens0_point2) << 2) | |
| TSENS_BIT_APPEND); |
| calib_tsens_point2_data[1] = |
| (((tsens_base2_data + tsens1_point2) << 2) | |
| TSENS_BIT_APPEND); |
| calib_tsens_point2_data[2] = |
| (((tsens_base2_data + tsens2_point2) << 2) | |
| TSENS_BIT_APPEND); |
| calib_tsens_point2_data[3] = |
| (((tsens_base2_data + tsens3_point2) << 2) | |
| TSENS_BIT_APPEND); |
| calib_tsens_point2_data[4] = |
| (((tsens_base2_data + tsens4_point2) << 2) | |
| TSENS_BIT_APPEND); |
| calib_tsens_point2_data[5] = |
| (((tsens_base2_data + tsens5_point2) << 2) | |
| TSENS_BIT_APPEND); |
| calib_tsens_point2_data[6] = |
| (((tsens_base2_data + tsens6_point2) << 2) | |
| TSENS_BIT_APPEND); |
| calib_tsens_point2_data[7] = |
| (((tsens_base2_data + tsens7_point2) << 2) | |
| TSENS_BIT_APPEND); |
| calib_tsens_point2_data[8] = |
| (((tsens_base2_data + tsens8_point2) << 2) | |
| TSENS_BIT_APPEND); |
| calib_tsens_point2_data[9] = |
| (((tsens_base2_data + tsens9_point2) << 2) | |
| TSENS_BIT_APPEND); |
| calib_tsens_point2_data[10] = |
| (((tsens_base2_data + tsens10_point2) << 2) | |
| TSENS_BIT_APPEND); |
| } |
| |
| compute_intercept_slope: |
| for (i = 0; i < tmdev->tsens_num_sensor; i++) { |
| int32_t num = 0, den = 0; |
| tmdev->sensor[i].calib_data_point2 = calib_tsens_point2_data[i]; |
| tmdev->sensor[i].calib_data_point1 = calib_tsens_point1_data[i]; |
| pr_debug("sensor:%d - calib_data_point1:0x%x, calib_data_point2:0x%x\n", |
| i, tmdev->sensor[i].calib_data_point1, |
| tmdev->sensor[i].calib_data_point2); |
| if (tsens_calibration_mode == TSENS_TWO_POINT_CALIB) { |
| /* slope (m) = adc_code2 - adc_code1 (y2 - y1)/ |
| temp_120_degc - temp_30_degc (x2 - x1) */ |
| num = tmdev->sensor[i].calib_data_point2 - |
| tmdev->sensor[i].calib_data_point1; |
| num *= tmdev->tsens_factor; |
| den = TSENS_CAL_DEGC_POINT2 - TSENS_CAL_DEGC_POINT1; |
| tmdev->sensor[i].slope_mul_tsens_factor = num/den; |
| } |
| tmdev->sensor[i].offset = (tmdev->sensor[i].calib_data_point1 * |
| tmdev->tsens_factor) - (TSENS_CAL_DEGC_POINT1 * |
| tmdev->sensor[i].slope_mul_tsens_factor); |
| pr_debug("offset:%d\n", tmdev->sensor[i].offset); |
| INIT_WORK(&tmdev->sensor[i].work, notify_uspace_tsens_fn); |
| tmdev->prev_reading_avail = false; |
| } |
| |
| return 0; |
| } |
| |
| static int tsens_calib_sensors(void) |
| { |
| int rc = 0; |
| |
| if (!tmdev) |
| return -ENODEV; |
| |
| if (tmdev->calib_mode == TSENS_CALIB_FUSE_MAP_8974) |
| rc = tsens_calib_8974_sensors(); |
| else if (tmdev->calib_mode == TSENS_CALIB_FUSE_MAP_8X26) |
| rc = tsens_calib_8x26_sensors(); |
| else if (tmdev->calib_mode == TSENS_CALIB_FUSE_MAP_8X10) |
| rc = tsens_calib_8x10_sensors(); |
| else |
| rc = -ENODEV; |
| |
| return rc; |
| } |
| |
| static int get_device_tree_data(struct platform_device *pdev) |
| { |
| struct device_node *of_node = pdev->dev.of_node; |
| struct resource *res_mem = NULL; |
| u32 *tsens_slope_data; |
| u32 *sensor_id; |
| u32 rc = 0, i, tsens_num_sensors, calib_type; |
| const char *tsens_calib_mode; |
| |
| rc = of_property_read_u32(of_node, |
| "qcom,sensors", &tsens_num_sensors); |
| if (rc) { |
| dev_err(&pdev->dev, "missing sensor number\n"); |
| return -ENODEV; |
| } |
| |
| tsens_slope_data = devm_kzalloc(&pdev->dev, |
| tsens_num_sensors * sizeof(u32), GFP_KERNEL); |
| if (!tsens_slope_data) { |
| dev_err(&pdev->dev, "can not allocate slope data\n"); |
| return -ENOMEM; |
| } |
| |
| rc = of_property_read_u32_array(of_node, |
| "qcom,slope", tsens_slope_data, tsens_num_sensors); |
| if (rc) { |
| dev_err(&pdev->dev, "invalid or missing property: tsens-slope\n"); |
| return rc; |
| }; |
| |
| rc = of_property_read_string(of_node, |
| "qcom,calib-mode", &tsens_calib_mode); |
| if (rc) { |
| dev_err(&pdev->dev, "missing calib-mode\n"); |
| return -ENODEV; |
| } |
| |
| if (!strncmp(tsens_calib_mode, "fuse_map1", 9)) |
| calib_type = TSENS_CALIB_FUSE_MAP_8974; |
| else if (!strncmp(tsens_calib_mode, "fuse_map2", 9)) |
| calib_type = TSENS_CALIB_FUSE_MAP_8X26; |
| else if (!strncmp(tsens_calib_mode, "fuse_map3", 9)) |
| calib_type = TSENS_CALIB_FUSE_MAP_8X10; |
| else { |
| pr_err("%s: Invalid calibration property\n", __func__); |
| return -EINVAL; |
| } |
| |
| tmdev = devm_kzalloc(&pdev->dev, |
| sizeof(struct tsens_tm_device) + |
| tsens_num_sensors * |
| sizeof(struct tsens_tm_device_sensor), |
| GFP_KERNEL); |
| if (tmdev == NULL) { |
| pr_err("%s: kzalloc() failed.\n", __func__); |
| return -ENOMEM; |
| } |
| |
| for (i = 0; i < tsens_num_sensors; i++) |
| tmdev->sensor[i].slope_mul_tsens_factor = tsens_slope_data[i]; |
| tmdev->tsens_factor = TSENS_SLOPE_FACTOR; |
| tmdev->tsens_num_sensor = tsens_num_sensors; |
| tmdev->calibration_less_mode = of_property_read_bool(of_node, |
| "qcom,calibration-less-mode"); |
| tmdev->calib_mode = calib_type; |
| tmdev->tsens_local_init = of_property_read_bool(of_node, |
| "qcom,tsens-local-init"); |
| |
| sensor_id = devm_kzalloc(&pdev->dev, |
| tsens_num_sensors * sizeof(u32), GFP_KERNEL); |
| if (!sensor_id) { |
| dev_err(&pdev->dev, "can not allocate sensor id\n"); |
| return -ENOMEM; |
| } |
| |
| rc = of_property_read_u32_array(of_node, |
| "qcom,sensor-id", sensor_id, tsens_num_sensors); |
| if (rc) { |
| pr_debug("Default sensor id mapping\n"); |
| for (i = 0; i < tsens_num_sensors; i++) { |
| tmdev->sensor[i].sensor_hw_num = i; |
| tmdev->sensor[i].sensor_sw_id = i; |
| } |
| } else { |
| pr_debug("Use specified sensor id mapping\n"); |
| for (i = 0; i < tsens_num_sensors; i++) { |
| tmdev->sensor[i].sensor_hw_num = sensor_id[i]; |
| tmdev->sensor[i].sensor_sw_id = i; |
| } |
| } |
| |
| tmdev->tsens_irq = platform_get_irq(pdev, 0); |
| if (tmdev->tsens_irq < 0) { |
| pr_err("Invalid get irq\n"); |
| rc = tmdev->tsens_irq; |
| goto fail_tmdev; |
| } |
| |
| /* TSENS register region */ |
| tmdev->res_tsens_mem = platform_get_resource_byname(pdev, |
| IORESOURCE_MEM, "tsens_physical"); |
| if (!tmdev->res_tsens_mem) { |
| pr_err("Could not get tsens physical address resource\n"); |
| rc = -EINVAL; |
| goto fail_tmdev; |
| } |
| |
| tmdev->tsens_len = tmdev->res_tsens_mem->end - |
| tmdev->res_tsens_mem->start + 1; |
| |
| res_mem = request_mem_region(tmdev->res_tsens_mem->start, |
| tmdev->tsens_len, tmdev->res_tsens_mem->name); |
| if (!res_mem) { |
| pr_err("Request tsens physical memory region failed\n"); |
| rc = -EINVAL; |
| goto fail_tmdev; |
| } |
| |
| tmdev->tsens_addr = ioremap(res_mem->start, tmdev->tsens_len); |
| if (!tmdev->tsens_addr) { |
| pr_err("Failed to IO map TSENS registers.\n"); |
| rc = -EINVAL; |
| goto fail_unmap_tsens_region; |
| } |
| |
| /* TSENS calibration region */ |
| tmdev->res_calib_mem = platform_get_resource_byname(pdev, |
| IORESOURCE_MEM, "tsens_eeprom_physical"); |
| if (!tmdev->res_calib_mem) { |
| pr_err("Could not get qfprom physical address resource\n"); |
| rc = -EINVAL; |
| goto fail_unmap_tsens; |
| } |
| |
| tmdev->calib_len = tmdev->res_calib_mem->end - |
| tmdev->res_calib_mem->start + 1; |
| |
| res_mem = request_mem_region(tmdev->res_calib_mem->start, |
| tmdev->calib_len, tmdev->res_calib_mem->name); |
| if (!res_mem) { |
| pr_err("Request calibration memory region failed\n"); |
| rc = -EINVAL; |
| goto fail_unmap_tsens; |
| } |
| |
| tmdev->tsens_calib_addr = ioremap(res_mem->start, |
| tmdev->calib_len); |
| if (!tmdev->tsens_calib_addr) { |
| pr_err("Failed to IO map EEPROM registers.\n"); |
| rc = -EINVAL; |
| goto fail_unmap_calib_region; |
| } |
| |
| return 0; |
| |
| fail_unmap_calib_region: |
| if (tmdev->res_calib_mem) |
| release_mem_region(tmdev->res_calib_mem->start, |
| tmdev->calib_len); |
| fail_unmap_tsens: |
| if (tmdev->tsens_addr) |
| iounmap(tmdev->tsens_addr); |
| fail_unmap_tsens_region: |
| if (tmdev->res_tsens_mem) |
| release_mem_region(tmdev->res_tsens_mem->start, |
| tmdev->tsens_len); |
| fail_tmdev: |
| tmdev = NULL; |
| return rc; |
| } |
| |
| static int __devinit tsens_tm_probe(struct platform_device *pdev) |
| { |
| int rc; |
| |
| if (tmdev) { |
| pr_err("TSENS device already in use\n"); |
| return -EBUSY; |
| } |
| |
| if (pdev->dev.of_node) { |
| rc = get_device_tree_data(pdev); |
| if (rc) { |
| pr_err("Error reading TSENS DT\n"); |
| return rc; |
| } |
| } else |
| return -ENODEV; |
| |
| tmdev->pdev = pdev; |
| tmdev->tsens_wq = alloc_workqueue("tsens_wq", WQ_HIGHPRI, 0); |
| if (!tmdev->tsens_wq) { |
| rc = -ENOMEM; |
| goto fail; |
| } |
| |
| rc = tsens_calib_sensors(); |
| if (rc < 0) { |
| pr_err("Calibration failed\n"); |
| goto fail; |
| } |
| |
| tsens_hw_init(); |
| |
| tmdev->prev_reading_avail = true; |
| |
| platform_set_drvdata(pdev, tmdev); |
| |
| return 0; |
| fail: |
| if (tmdev->tsens_wq) |
| destroy_workqueue(tmdev->tsens_wq); |
| if (tmdev->tsens_calib_addr) |
| iounmap(tmdev->tsens_calib_addr); |
| if (tmdev->res_calib_mem) |
| release_mem_region(tmdev->res_calib_mem->start, |
| tmdev->calib_len); |
| if (tmdev->tsens_addr) |
| iounmap(tmdev->tsens_addr); |
| if (tmdev->res_tsens_mem) |
| release_mem_region(tmdev->res_tsens_mem->start, |
| tmdev->tsens_len); |
| tmdev = NULL; |
| |
| return rc; |
| } |
| |
| static int __devinit _tsens_register_thermal(void) |
| { |
| struct platform_device *pdev; |
| int rc, i; |
| |
| if (!tmdev) { |
| pr_err("%s: TSENS early init not done\n", __func__); |
| return -ENODEV; |
| } |
| |
| pdev = tmdev->pdev; |
| |
| for (i = 0; i < tmdev->tsens_num_sensor; i++) { |
| char name[18]; |
| snprintf(name, sizeof(name), "tsens_tz_sensor%d", |
| tmdev->sensor[i].sensor_hw_num); |
| tmdev->sensor[i].mode = THERMAL_DEVICE_ENABLED; |
| 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(tmdev->tsens_irq, 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; |
| } else { |
| enable_irq_wake(tmdev->tsens_irq); |
| } |
| platform_set_drvdata(pdev, tmdev); |
| |
| INIT_WORK(&tmdev->tsens_work, tsens_scheduler_fn); |
| |
| return 0; |
| fail: |
| if (tmdev->tsens_calib_addr) |
| iounmap(tmdev->tsens_calib_addr); |
| if (tmdev->res_calib_mem) |
| release_mem_region(tmdev->res_calib_mem->start, |
| tmdev->calib_len); |
| if (tmdev->tsens_addr) |
| iounmap(tmdev->tsens_addr); |
| if (tmdev->res_tsens_mem) |
| release_mem_region(tmdev->res_tsens_mem->start, |
| tmdev->tsens_len); |
| return rc; |
| } |
| |
| static int __devexit tsens_tm_remove(struct platform_device *pdev) |
| { |
| struct tsens_tm_device *tmdev = platform_get_drvdata(pdev); |
| int i; |
| |
| for (i = 0; i < tmdev->tsens_num_sensor; i++) |
| thermal_zone_device_unregister(tmdev->sensor[i].tz_dev); |
| if (tmdev->tsens_calib_addr) |
| iounmap(tmdev->tsens_calib_addr); |
| if (tmdev->res_calib_mem) |
| release_mem_region(tmdev->res_calib_mem->start, |
| tmdev->calib_len); |
| if (tmdev->tsens_addr) |
| iounmap(tmdev->tsens_addr); |
| if (tmdev->res_tsens_mem) |
| release_mem_region(tmdev->res_tsens_mem->start, |
| tmdev->tsens_len); |
| free_irq(tmdev->tsens_irq, tmdev); |
| destroy_workqueue(tmdev->tsens_wq); |
| platform_set_drvdata(pdev, NULL); |
| |
| return 0; |
| } |
| |
| static struct of_device_id tsens_match[] = { |
| { .compatible = "qcom,msm-tsens", |
| }, |
| {} |
| }; |
| |
| static struct platform_driver tsens_tm_driver = { |
| .probe = tsens_tm_probe, |
| .remove = tsens_tm_remove, |
| .driver = { |
| .name = "msm-tsens", |
| .owner = THIS_MODULE, |
| .of_match_table = tsens_match, |
| }, |
| }; |
| |
| int __init tsens_tm_init_driver(void) |
| { |
| return platform_driver_register(&tsens_tm_driver); |
| } |
| |
| static int __init tsens_thermal_register(void) |
| { |
| return _tsens_register_thermal(); |
| } |
| module_init(tsens_thermal_register); |
| |
| static void __exit _tsens_tm_remove(void) |
| { |
| platform_driver_unregister(&tsens_tm_driver); |
| } |
| module_exit(_tsens_tm_remove); |
| |
| MODULE_ALIAS("platform:" TSENS_DRIVER_NAME); |
| MODULE_LICENSE("GPL v2"); |