| /* Copyright (c) 2016-2017, 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) "TADC: %s: " fmt, __func__ |
| |
| #include <linux/iio/iio.h> |
| #include <linux/interrupt.h> |
| #include <linux/module.h> |
| #include <linux/of_irq.h> |
| #include <linux/platform_device.h> |
| #include <linux/regmap.h> |
| #include <linux/power_supply.h> |
| #include <linux/pmic-voter.h> |
| |
| #define USB_PRESENT_VOTER "USB_PRESENT_VOTER" |
| #define SLEEP_VOTER "SLEEP_VOTER" |
| #define SHUTDOWN_VOTER "SHUTDOWN_VOTER" |
| #define TADC_REVISION1_REG 0x00 |
| #define TADC_REVISION2_REG 0x01 |
| #define TADC_REVISION3_REG 0x02 |
| #define TADC_REVISION4_REG 0x03 |
| #define TADC_PERPH_TYPE_REG 0x04 |
| #define TADC_PERPH_SUBTYPE_REG 0x05 |
| |
| /* TADC register definitions */ |
| #define TADC_SW_CH_CONV_REG(chip) (chip->tadc_base + 0x06) |
| #define TADC_MBG_ERR_REG(chip) (chip->tadc_base + 0x07) |
| #define TADC_EN_CTL_REG(chip) (chip->tadc_base + 0x46) |
| #define TADC_CONV_REQ_REG(chip) (chip->tadc_base + 0x51) |
| #define TADC_HWTRIG_CONV_CH_EN_REG(chip) (chip->tadc_base + 0x52) |
| #define TADC_HW_SETTLE_DELAY_REG(chip) (chip->tadc_base + 0x53) |
| #define TADC_LONG_HW_SETTLE_DLY_EN_REG(chip) (chip->tadc_base + 0x54) |
| #define TADC_LONG_HW_SETTLE_DLY_REG(chip) (chip->tadc_base + 0x55) |
| #define TADC_ADC_BUF_CH_REG(chip) (chip->tadc_base + 0x56) |
| #define TADC_ADC_AAF_CH_REG(chip) (chip->tadc_base + 0x57) |
| #define TADC_ADC_DATA_RDBK_REG(chip) (chip->tadc_base + 0x58) |
| #define TADC_CH1_ADC_LO_REG(chip) (chip->tadc_base + 0x60) |
| #define TADC_CH1_ADC_HI_REG(chip) (chip->tadc_base + 0x61) |
| #define TADC_CH2_ADC_LO_REG(chip) (chip->tadc_base + 0x62) |
| #define TADC_CH2_ADC_HI_REG(chip) (chip->tadc_base + 0x63) |
| #define TADC_CH3_ADC_LO_REG(chip) (chip->tadc_base + 0x64) |
| #define TADC_CH3_ADC_HI_REG(chip) (chip->tadc_base + 0x65) |
| #define TADC_CH4_ADC_LO_REG(chip) (chip->tadc_base + 0x66) |
| #define TADC_CH4_ADC_HI_REG(chip) (chip->tadc_base + 0x67) |
| #define TADC_CH5_ADC_LO_REG(chip) (chip->tadc_base + 0x68) |
| #define TADC_CH5_ADC_HI_REG(chip) (chip->tadc_base + 0x69) |
| #define TADC_CH6_ADC_LO_REG(chip) (chip->tadc_base + 0x70) |
| #define TADC_CH6_ADC_HI_REG(chip) (chip->tadc_base + 0x71) |
| #define TADC_CH7_ADC_LO_REG(chip) (chip->tadc_base + 0x72) |
| #define TADC_CH7_ADC_HI_REG(chip) (chip->tadc_base + 0x73) |
| #define TADC_CH8_ADC_LO_REG(chip) (chip->tadc_base + 0x74) |
| #define TADC_CH8_ADC_HI_REG(chip) (chip->tadc_base + 0x75) |
| #define TADC_ADC_DIRECT_TST(chip) (chip->tadc_base + 0xE7) |
| |
| /* TADC_CMP register definitions */ |
| #define TADC_CMP_THR1_CMP_REG(chip) (chip->tadc_cmp_base + 0x51) |
| #define TADC_CMP_THR1_CH1_CMP_LO_REG(chip) (chip->tadc_cmp_base + 0x52) |
| #define TADC_CMP_THR1_CH1_CMP_HI_REG(chip) (chip->tadc_cmp_base + 0x53) |
| #define TADC_CMP_THR1_CH2_CMP_LO_REG(chip) (chip->tadc_cmp_base + 0x54) |
| #define TADC_CMP_THR1_CH2_CMP_HI_REG(chip) (chip->tadc_cmp_base + 0x55) |
| #define TADC_CMP_THR1_CH3_CMP_LO_REG(chip) (chip->tadc_cmp_base + 0x56) |
| #define TADC_CMP_THR1_CH3_CMP_HI_REG(chip) (chip->tadc_cmp_base + 0x57) |
| #define TADC_CMP_THR2_CMP_REG(chip) (chip->tadc_cmp_base + 0x67) |
| #define TADC_CMP_THR2_CH1_CMP_LO_REG(chip) (chip->tadc_cmp_base + 0x68) |
| #define TADC_CMP_THR2_CH1_CMP_HI_REG(chip) (chip->tadc_cmp_base + 0x69) |
| #define TADC_CMP_THR2_CH2_CMP_LO_REG(chip) (chip->tadc_cmp_base + 0x6A) |
| #define TADC_CMP_THR2_CH2_CMP_HI_REG(chip) (chip->tadc_cmp_base + 0x6B) |
| #define TADC_CMP_THR2_CH3_CMP_LO_REG(chip) (chip->tadc_cmp_base + 0x6C) |
| #define TADC_CMP_THR2_CH3_CMP_HI_REG(chip) (chip->tadc_cmp_base + 0x6D) |
| #define TADC_CMP_THR3_CMP_REG(chip) (chip->tadc_cmp_base + 0x7D) |
| #define TADC_CMP_THR3_CH1_CMP_LO_REG(chip) (chip->tadc_cmp_base + 0x7E) |
| #define TADC_CMP_THR3_CH1_CMP_HI_REG(chip) (chip->tadc_cmp_base + 0x7F) |
| #define TADC_CMP_THR3_CH2_CMP_LO_REG(chip) (chip->tadc_cmp_base + 0x80) |
| #define TADC_CMP_THR3_CH2_CMP_HI_REG(chip) (chip->tadc_cmp_base + 0x81) |
| #define TADC_CMP_THR3_CH3_CMP_LO_REG(chip) (chip->tadc_cmp_base + 0x82) |
| #define TADC_CMP_THR3_CH3_CMP_HI_REG(chip) (chip->tadc_cmp_base + 0x83) |
| #define TADC_CMP_THR4_CMP_REG(chip) (chip->tadc_cmp_base + 0x93) |
| #define TADC_CMP_THR4_CH1_CMP_LO_REG(chip) (chip->tadc_cmp_base + 0x94) |
| #define TADC_CMP_THR4_CH1_CMP_HI_REG(chip) (chip->tadc_cmp_base + 0x95) |
| #define TADC_CMP_THR1_CH1_HYST_REG(chip) (chip->tadc_cmp_base + 0xB0) |
| #define TADC_CMP_THR2_CH1_HYST_REG(chip) (chip->tadc_cmp_base + 0xB1) |
| #define TADC_CMP_THR3_CH1_HYST_REG(chip) (chip->tadc_cmp_base + 0xB2) |
| #define TADC_CMP_THR4_CH1_HYST_REG(chip) (chip->tadc_cmp_base + 0xB3) |
| |
| /* 10 bits of resolution */ |
| #define TADC_RESOLUTION 1024 |
| /* number of hardware channels */ |
| #define TADC_NUM_CH 8 |
| |
| enum tadc_chan_id { |
| TADC_THERM1 = 0, |
| TADC_THERM2, |
| TADC_DIE_TEMP, |
| TADC_BATT_I, |
| TADC_BATT_V, |
| TADC_INPUT_I, |
| TADC_INPUT_V, |
| TADC_OTG_I, |
| /* virtual channels */ |
| TADC_BATT_P, |
| TADC_INPUT_P, |
| TADC_THERM1_THR1, |
| TADC_THERM1_THR2, |
| TADC_THERM1_THR3, |
| TADC_THERM1_THR4, |
| TADC_THERM2_THR1, |
| TADC_THERM2_THR2, |
| TADC_THERM2_THR3, |
| TADC_DIE_TEMP_THR1, |
| TADC_DIE_TEMP_THR2, |
| TADC_DIE_TEMP_THR3, |
| TADC_CHAN_ID_MAX, |
| }; |
| |
| #define TADC_CHAN(_name, _type, _channel, _info_mask) \ |
| { \ |
| .type = _type, \ |
| .channel = _channel, \ |
| .info_mask_separate = _info_mask, \ |
| .extend_name = _name, \ |
| } |
| |
| #define TADC_THERM_CHAN(_name, _channel) \ |
| TADC_CHAN(_name, IIO_TEMP, _channel, \ |
| BIT(IIO_CHAN_INFO_RAW) | \ |
| BIT(IIO_CHAN_INFO_PROCESSED)) |
| |
| #define TADC_TEMP_CHAN(_name, _channel) \ |
| TADC_CHAN(_name, IIO_TEMP, _channel, \ |
| BIT(IIO_CHAN_INFO_RAW) | \ |
| BIT(IIO_CHAN_INFO_PROCESSED) | \ |
| BIT(IIO_CHAN_INFO_SCALE) | \ |
| BIT(IIO_CHAN_INFO_OFFSET)) |
| |
| #define TADC_CURRENT_CHAN(_name, _channel) \ |
| TADC_CHAN(_name, IIO_CURRENT, _channel, \ |
| BIT(IIO_CHAN_INFO_RAW) | \ |
| BIT(IIO_CHAN_INFO_PROCESSED) | \ |
| BIT(IIO_CHAN_INFO_SCALE)) |
| |
| |
| #define TADC_VOLTAGE_CHAN(_name, _channel) \ |
| TADC_CHAN(_name, IIO_VOLTAGE, _channel, \ |
| BIT(IIO_CHAN_INFO_RAW) | \ |
| BIT(IIO_CHAN_INFO_PROCESSED) | \ |
| BIT(IIO_CHAN_INFO_SCALE)) |
| |
| #define TADC_POWER_CHAN(_name, _channel) \ |
| TADC_CHAN(_name, IIO_POWER, _channel, \ |
| BIT(IIO_CHAN_INFO_PROCESSED)) |
| |
| static const struct iio_chan_spec tadc_iio_chans[] = { |
| [TADC_THERM1] = TADC_THERM_CHAN( |
| "batt", TADC_THERM1), |
| [TADC_THERM2] = TADC_THERM_CHAN( |
| "skin", TADC_THERM2), |
| [TADC_DIE_TEMP] = TADC_TEMP_CHAN( |
| "die", TADC_DIE_TEMP), |
| [TADC_BATT_I] = TADC_CURRENT_CHAN( |
| "batt", TADC_BATT_I), |
| [TADC_BATT_V] = TADC_VOLTAGE_CHAN( |
| "batt", TADC_BATT_V), |
| [TADC_INPUT_I] = TADC_CURRENT_CHAN( |
| "input", TADC_INPUT_I), |
| [TADC_INPUT_V] = TADC_VOLTAGE_CHAN( |
| "input", TADC_INPUT_V), |
| [TADC_OTG_I] = TADC_CURRENT_CHAN( |
| "otg", TADC_OTG_I), |
| [TADC_BATT_P] = TADC_POWER_CHAN( |
| "batt", TADC_BATT_P), |
| [TADC_INPUT_P] = TADC_POWER_CHAN( |
| "input", TADC_INPUT_P), |
| [TADC_THERM1_THR1] = TADC_THERM_CHAN( |
| "batt_warm", TADC_THERM1_THR1), |
| [TADC_THERM1_THR2] = TADC_THERM_CHAN( |
| "batt_cool", TADC_THERM1_THR2), |
| [TADC_THERM1_THR3] = TADC_THERM_CHAN( |
| "batt_cold", TADC_THERM1_THR3), |
| [TADC_THERM1_THR4] = TADC_THERM_CHAN( |
| "batt_hot", TADC_THERM1_THR4), |
| [TADC_THERM2_THR1] = TADC_THERM_CHAN( |
| "skin_lb", TADC_THERM2_THR1), |
| [TADC_THERM2_THR2] = TADC_THERM_CHAN( |
| "skin_ub", TADC_THERM2_THR2), |
| [TADC_THERM2_THR3] = TADC_THERM_CHAN( |
| "skin_rst", TADC_THERM2_THR3), |
| [TADC_DIE_TEMP_THR1] = TADC_THERM_CHAN( |
| "die_lb", TADC_DIE_TEMP_THR1), |
| [TADC_DIE_TEMP_THR2] = TADC_THERM_CHAN( |
| "die_ub", TADC_DIE_TEMP_THR2), |
| [TADC_DIE_TEMP_THR3] = TADC_THERM_CHAN( |
| "die_rst", TADC_DIE_TEMP_THR3), |
| }; |
| |
| struct tadc_therm_thr { |
| int addr_lo; |
| int addr_hi; |
| }; |
| |
| struct tadc_chan_data { |
| s32 scale; |
| s32 offset; |
| u32 rbias; |
| const struct tadc_pt *table; |
| size_t tablesize; |
| struct tadc_therm_thr thr[4]; |
| }; |
| |
| struct tadc_chip { |
| struct device *dev; |
| struct regmap *regmap; |
| u32 tadc_base; |
| u32 tadc_cmp_base; |
| struct tadc_chan_data chans[TADC_NUM_CH]; |
| struct completion eoc_complete; |
| struct mutex write_lock; |
| struct mutex conv_lock; |
| struct power_supply *usb_psy; |
| struct votable *tadc_disable_votable; |
| struct work_struct status_change_work; |
| struct notifier_block nb; |
| u8 hwtrig_conv; |
| }; |
| |
| struct tadc_pt { |
| s32 x; |
| s32 y; |
| }; |
| |
| /* |
| * Thermistor tables are generated by the B-parameter equation which is a |
| * simplifed version of the Steinhart-Hart equation. |
| * |
| * (1 / T) = (1 / T0) + (1 / B) * ln(R / R0) |
| * |
| * Where R0 is the resistance at temperature T0, and T0 is typically room |
| * temperature (25C). |
| */ |
| static const struct tadc_pt tadc_therm_3450b_68k[] = { |
| { 4151, 120000 }, |
| { 4648, 115000 }, |
| { 5220, 110000 }, |
| { 5880, 105000 }, |
| { 6644, 100000 }, |
| { 7533, 95000 }, |
| { 8571, 90000 }, |
| { 9786, 85000 }, |
| { 11216, 80000 }, |
| { 12906, 75000 }, |
| { 14910, 70000 }, |
| { 17300, 65000 }, |
| { 20163, 60000 }, |
| { 23609, 55000 }, |
| { 27780, 50000 }, |
| { 32855, 45000 }, |
| { 39065, 40000 }, |
| { 46712, 35000 }, |
| { 56185, 30000 }, |
| { 68000, 25000 }, |
| { 82837, 20000 }, |
| { 101604, 15000 }, |
| { 125525, 10000 }, |
| { 156261, 5000 }, |
| { 196090, 0 }, |
| { 248163, -5000 }, |
| { 316887, -10000 }, |
| { 408493, -15000 }, |
| { 531889, -20000 }, |
| { 699966, -25000 }, |
| { 931618, -30000 }, |
| { 1254910, -35000 }, |
| { 1712127, -40000 }, |
| }; |
| |
| static bool tadc_is_reg_locked(struct tadc_chip *chip, u16 reg) |
| { |
| if ((reg & 0xFF00) == chip->tadc_cmp_base) |
| return true; |
| |
| if (reg >= TADC_HWTRIG_CONV_CH_EN_REG(chip)) |
| return true; |
| |
| return false; |
| } |
| |
| static int tadc_read(struct tadc_chip *chip, u16 reg, u8 *val, size_t count) |
| { |
| int rc = 0; |
| |
| rc = regmap_bulk_read(chip->regmap, reg, val, count); |
| if (rc < 0) |
| pr_err("Couldn't read 0x%04x rc=%d\n", reg, rc); |
| |
| return rc; |
| } |
| |
| static int tadc_write(struct tadc_chip *chip, u16 reg, u8 data) |
| { |
| int rc = 0; |
| |
| mutex_lock(&chip->write_lock); |
| if (tadc_is_reg_locked(chip, reg)) { |
| rc = regmap_write(chip->regmap, (reg & 0xFF00) | 0xD0, 0xA5); |
| if (rc < 0) { |
| pr_err("Couldn't unlock secure register rc=%d\n", rc); |
| goto unlock; |
| } |
| } |
| |
| rc = regmap_write(chip->regmap, reg, data); |
| if (rc < 0) { |
| pr_err("Couldn't write 0x%02x to 0x%04x rc=%d\n", |
| data, reg, rc); |
| goto unlock; |
| } |
| |
| unlock: |
| mutex_unlock(&chip->write_lock); |
| return rc; |
| } |
| static int tadc_bulk_write(struct tadc_chip *chip, u16 reg, u8 *data, |
| size_t count) |
| { |
| int rc = 0, i; |
| |
| mutex_lock(&chip->write_lock); |
| for (i = 0; i < count; ++i, ++reg) { |
| if (tadc_is_reg_locked(chip, reg)) { |
| rc = regmap_write(chip->regmap, |
| (reg & 0xFF00) | 0xD0, 0xA5); |
| if (rc < 0) { |
| pr_err("Couldn't unlock secure register rc=%d\n", |
| rc); |
| goto unlock; |
| } |
| } |
| |
| rc = regmap_write(chip->regmap, reg, data[i]); |
| if (rc < 0) { |
| pr_err("Couldn't write 0x%02x to 0x%04x rc=%d\n", |
| data[i], reg, rc); |
| goto unlock; |
| } |
| } |
| |
| unlock: |
| mutex_unlock(&chip->write_lock); |
| return rc; |
| } |
| |
| static int tadc_masked_write(struct tadc_chip *chip, u16 reg, u8 mask, u8 data) |
| { |
| int rc = 0; |
| |
| mutex_lock(&chip->write_lock); |
| if (tadc_is_reg_locked(chip, reg)) { |
| rc = regmap_write(chip->regmap, (reg & 0xFF00) | 0xD0, 0xA5); |
| if (rc < 0) { |
| pr_err("Couldn't unlock secure register rc=%d\n", rc); |
| goto unlock; |
| } |
| } |
| |
| rc = regmap_update_bits(chip->regmap, reg, mask, data); |
| |
| unlock: |
| mutex_unlock(&chip->write_lock); |
| return rc; |
| } |
| |
| static int tadc_lerp(const struct tadc_pt *pts, size_t size, bool inv, |
| s32 input, s32 *output) |
| { |
| int i; |
| s64 temp; |
| bool ascending; |
| |
| if (pts == NULL) { |
| pr_err("Table is NULL\n"); |
| return -EINVAL; |
| } |
| |
| if (size < 1) { |
| pr_err("Table has no entries\n"); |
| return -ENOENT; |
| } |
| |
| if (size == 1) { |
| *output = inv ? pts[0].x : pts[0].y; |
| return 0; |
| } |
| |
| ascending = inv ? (pts[0].y < pts[1].y) : (pts[0].x < pts[1].x); |
| if (ascending ? (input <= (inv ? pts[0].y : pts[0].x)) : |
| (input >= (inv ? pts[0].y : pts[0].x))) { |
| *output = inv ? pts[0].x : pts[0].y; |
| return 0; |
| } |
| |
| if (ascending ? (input >= (inv ? pts[size - 1].y : pts[size - 1].x)) : |
| (input <= (inv ? pts[size - 1].y : pts[size - 1].x))) { |
| *output = inv ? pts[size - 1].x : pts[size - 1].y; |
| return 0; |
| } |
| |
| for (i = 1; i < size; i++) |
| if (ascending ? (input <= (inv ? pts[i].y : pts[i].x)) : |
| (input >= (inv ? pts[i].y : pts[i].x))) |
| break; |
| |
| if (inv) { |
| temp = (s64)(pts[i].x - pts[i - 1].x) * |
| (s64)(input - pts[i - 1].y); |
| temp = div_s64(temp, pts[i].y - pts[i - 1].y); |
| *output = temp + pts[i - 1].x; |
| } else { |
| temp = (s64)(pts[i].y - pts[i - 1].y) * |
| (s64)(input - pts[i - 1].x); |
| temp = div_s64(temp, pts[i].x - pts[i - 1].x); |
| *output = temp + pts[i - 1].y; |
| } |
| |
| return 0; |
| } |
| |
| /* |
| * Process the result of a thermistor reading. |
| * |
| * The voltage input to the ADC is a result of a voltage divider circuit. |
| * Vout = (Rtherm / (Rbias + Rtherm)) * Vbias |
| * |
| * The ADC value is based on the output voltage of the voltage divider, and the |
| * bias voltage. |
| * ADC = (Vin * 1024) / Vbias |
| * |
| * Combine these equations and solve for Rtherm |
| * Rtherm = (ADC * Rbias) / (1024 - ADC) |
| */ |
| static int tadc_get_processed_therm(const struct tadc_chan_data *chan_data, |
| s16 adc, s32 *result) |
| { |
| s32 rtherm; |
| |
| rtherm = div_s64((s64)adc * chan_data->rbias, TADC_RESOLUTION - adc); |
| return tadc_lerp(chan_data->table, chan_data->tablesize, false, rtherm, |
| result); |
| } |
| |
| static int tadc_get_raw_therm(const struct tadc_chan_data *chan_data, |
| int mdegc, int *result) |
| { |
| int rc; |
| s32 rtherm; |
| |
| rc = tadc_lerp(chan_data->table, chan_data->tablesize, true, mdegc, |
| &rtherm); |
| if (rc < 0) { |
| pr_err("Couldn't interpolate %d\n rc=%d", mdegc, rc); |
| return rc; |
| } |
| |
| *result = div64_s64((s64)rtherm * TADC_RESOLUTION, |
| (s64)chan_data->rbias + rtherm); |
| return 0; |
| } |
| |
| static int tadc_read_channel(struct tadc_chip *chip, u16 address, int *adc) |
| { |
| u8 val[2]; |
| int rc; |
| |
| rc = tadc_read(chip, address, val, ARRAY_SIZE(val)); |
| if (rc < 0) { |
| pr_err("Couldn't read channel rc=%d\n", rc); |
| return rc; |
| } |
| |
| /* the 10th bit is the sign bit for all channels */ |
| *adc = sign_extend32(val[0] | val[1] << BITS_PER_BYTE, 10); |
| return rc; |
| } |
| |
| static int tadc_write_channel(struct tadc_chip *chip, u16 address, int adc) |
| { |
| u8 val[2]; |
| int rc; |
| |
| /* the 10th bit is the sign bit for all channels */ |
| adc = sign_extend32(adc, 10); |
| val[0] = (u8)adc; |
| val[1] = (u8)(adc >> BITS_PER_BYTE); |
| rc = tadc_bulk_write(chip, address, val, 2); |
| if (rc < 0) { |
| pr_err("Couldn't write to channel rc=%d\n", rc); |
| return rc; |
| } |
| |
| return rc; |
| } |
| |
| #define CONVERSION_TIMEOUT_MS 100 |
| static int tadc_do_conversion(struct tadc_chip *chip, u8 channels, s16 *adc) |
| { |
| unsigned long timeout, timeleft; |
| u8 val[TADC_NUM_CH * 2]; |
| int rc = 0, i; |
| |
| mutex_lock(&chip->conv_lock); |
| rc = tadc_read(chip, TADC_MBG_ERR_REG(chip), val, 1); |
| if (rc < 0) { |
| pr_err("Couldn't read mbg error status rc=%d\n", rc); |
| goto unlock; |
| } |
| |
| reinit_completion(&chip->eoc_complete); |
| |
| if (get_effective_result(chip->tadc_disable_votable)) { |
| /* leave it back in completed state */ |
| complete_all(&chip->eoc_complete); |
| rc = -ENODATA; |
| goto unlock; |
| } |
| |
| if (val[0] != 0) { |
| tadc_write(chip, TADC_EN_CTL_REG(chip), 0); |
| tadc_write(chip, TADC_EN_CTL_REG(chip), 0x80); |
| } |
| |
| rc = tadc_write(chip, TADC_CONV_REQ_REG(chip), channels); |
| if (rc < 0) { |
| pr_err("Couldn't write conversion request rc=%d\n", rc); |
| goto unlock; |
| } |
| |
| timeout = msecs_to_jiffies(CONVERSION_TIMEOUT_MS); |
| timeleft = wait_for_completion_timeout(&chip->eoc_complete, timeout); |
| |
| if (timeleft == 0) { |
| rc = tadc_read(chip, TADC_SW_CH_CONV_REG(chip), val, 1); |
| if (rc < 0) { |
| pr_err("Couldn't read conversion status rc=%d\n", rc); |
| goto unlock; |
| } |
| |
| /* |
| * check one last time if the channel we are requesting |
| * has completed conversion |
| */ |
| if (val[0] != channels) { |
| rc = -ETIMEDOUT; |
| goto unlock; |
| } |
| } |
| |
| rc = tadc_read(chip, TADC_CH1_ADC_LO_REG(chip), val, ARRAY_SIZE(val)); |
| if (rc < 0) { |
| pr_err("Couldn't read adc channels rc=%d\n", rc); |
| goto unlock; |
| } |
| |
| for (i = 0; i < TADC_NUM_CH; i++) |
| adc[i] = (s16)(val[i * 2] | (u16)val[i * 2 + 1] << 8); |
| |
| pr_debug("Conversion time for channels 0x%x = %dms\n", channels, |
| jiffies_to_msecs(timeout - timeleft)); |
| |
| unlock: |
| mutex_unlock(&chip->conv_lock); |
| return rc; |
| } |
| |
| static int tadc_read_raw(struct iio_dev *indio_dev, |
| struct iio_chan_spec const *chan, int *val, int *val2, |
| long mask) |
| { |
| struct tadc_chip *chip = iio_priv(indio_dev); |
| struct tadc_chan_data *chan_data = NULL; |
| int rc, offset = 0, scale, scale2, scale_type; |
| s16 adc[TADC_NUM_CH]; |
| |
| switch (chan->channel) { |
| case TADC_THERM1_THR1: |
| case TADC_THERM1_THR2: |
| case TADC_THERM1_THR3: |
| case TADC_THERM1_THR4: |
| chan_data = &chip->chans[TADC_THERM1]; |
| break; |
| case TADC_THERM2_THR1: |
| case TADC_THERM2_THR2: |
| case TADC_THERM2_THR3: |
| chan_data = &chip->chans[TADC_THERM2]; |
| break; |
| case TADC_DIE_TEMP_THR1: |
| case TADC_DIE_TEMP_THR2: |
| case TADC_DIE_TEMP_THR3: |
| chan_data = &chip->chans[TADC_DIE_TEMP]; |
| break; |
| default: |
| if (chan->channel >= ARRAY_SIZE(chip->chans)) { |
| pr_err("Channel %d is out of bounds\n", chan->channel); |
| return -EINVAL; |
| } |
| |
| chan_data = &chip->chans[chan->channel]; |
| break; |
| } |
| |
| if (!chan_data) |
| return -EINVAL; |
| |
| switch (mask) { |
| case IIO_CHAN_INFO_RAW: |
| switch (chan->channel) { |
| case TADC_THERM1_THR1: |
| case TADC_THERM2_THR1: |
| case TADC_DIE_TEMP_THR1: |
| rc = tadc_read_channel(chip, |
| chan_data->thr[0].addr_lo, val); |
| break; |
| case TADC_THERM1_THR2: |
| case TADC_THERM2_THR2: |
| case TADC_DIE_TEMP_THR2: |
| rc = tadc_read_channel(chip, |
| chan_data->thr[1].addr_lo, val); |
| break; |
| case TADC_THERM1_THR3: |
| case TADC_THERM2_THR3: |
| case TADC_DIE_TEMP_THR3: |
| rc = tadc_read_channel(chip, |
| chan_data->thr[2].addr_lo, val); |
| break; |
| case TADC_THERM1_THR4: |
| rc = tadc_read_channel(chip, |
| chan_data->thr[3].addr_lo, val); |
| break; |
| default: |
| rc = tadc_do_conversion(chip, BIT(chan->channel), adc); |
| if (rc < 0) { |
| if (rc != -ENODATA) |
| pr_err("Couldn't read battery current and voltage channels rc=%d\n", |
| rc); |
| return rc; |
| } |
| *val = adc[chan->channel]; |
| break; |
| } |
| |
| if (rc < 0 && rc != -ENODATA) { |
| pr_err("Couldn't read channel %d\n", chan->channel); |
| return rc; |
| } |
| |
| return IIO_VAL_INT; |
| case IIO_CHAN_INFO_PROCESSED: |
| switch (chan->channel) { |
| case TADC_THERM1: |
| case TADC_THERM2: |
| case TADC_THERM1_THR1: |
| case TADC_THERM1_THR2: |
| case TADC_THERM1_THR3: |
| case TADC_THERM1_THR4: |
| case TADC_THERM2_THR1: |
| case TADC_THERM2_THR2: |
| case TADC_THERM2_THR3: |
| rc = tadc_read_raw(indio_dev, chan, val, NULL, |
| IIO_CHAN_INFO_RAW); |
| if (rc < 0) |
| return rc; |
| |
| rc = tadc_get_processed_therm(chan_data, *val, val); |
| if (rc < 0) { |
| pr_err("Couldn't process 0x%04x from channel %d rc=%d\n", |
| *val, chan->channel, rc); |
| return rc; |
| } |
| break; |
| case TADC_BATT_P: |
| rc = tadc_do_conversion(chip, |
| BIT(TADC_BATT_I) | BIT(TADC_BATT_V), adc); |
| if (rc < 0 && rc != -ENODATA) { |
| pr_err("Couldn't read battery current and voltage channels rc=%d\n", |
| rc); |
| return rc; |
| } |
| |
| *val = adc[TADC_BATT_I] * adc[TADC_BATT_V]; |
| break; |
| case TADC_INPUT_P: |
| rc = tadc_do_conversion(chip, |
| BIT(TADC_INPUT_I) | BIT(TADC_INPUT_V), adc); |
| if (rc < 0 && rc != -ENODATA) { |
| pr_err("Couldn't read input current and voltage channels rc=%d\n", |
| rc); |
| return rc; |
| } |
| |
| *val = adc[TADC_INPUT_I] * adc[TADC_INPUT_V]; |
| break; |
| default: |
| rc = tadc_read_raw(indio_dev, chan, val, NULL, |
| IIO_CHAN_INFO_RAW); |
| if (rc < 0) |
| return rc; |
| |
| /* offset is optional */ |
| rc = tadc_read_raw(indio_dev, chan, &offset, NULL, |
| IIO_CHAN_INFO_OFFSET); |
| if (rc < 0) |
| return rc; |
| |
| scale_type = tadc_read_raw(indio_dev, chan, |
| &scale, &scale2, IIO_CHAN_INFO_SCALE); |
| switch (scale_type) { |
| case IIO_VAL_INT: |
| *val = *val * scale + offset; |
| break; |
| case IIO_VAL_FRACTIONAL: |
| *val = div_s64((s64)*val * scale + offset, |
| scale2); |
| break; |
| default: |
| return -EINVAL; |
| } |
| break; |
| } |
| |
| return IIO_VAL_INT; |
| case IIO_CHAN_INFO_SCALE: |
| switch (chan->channel) { |
| case TADC_DIE_TEMP: |
| case TADC_DIE_TEMP_THR1: |
| case TADC_DIE_TEMP_THR2: |
| case TADC_DIE_TEMP_THR3: |
| *val = chan_data->scale; |
| return IIO_VAL_INT; |
| case TADC_BATT_I: |
| case TADC_BATT_V: |
| case TADC_INPUT_I: |
| case TADC_INPUT_V: |
| case TADC_OTG_I: |
| *val = chan_data->scale; |
| *val2 = TADC_RESOLUTION; |
| return IIO_VAL_FRACTIONAL; |
| } |
| |
| return -EINVAL; |
| case IIO_CHAN_INFO_OFFSET: |
| *val = chan_data->offset; |
| return IIO_VAL_INT; |
| } |
| |
| return -EINVAL; |
| } |
| |
| static int tadc_write_raw(struct iio_dev *indio_dev, |
| struct iio_chan_spec const *chan, int val, int val2, |
| long mask) |
| { |
| struct tadc_chip *chip = iio_priv(indio_dev); |
| const struct tadc_chan_data *chan_data; |
| int rc, raw; |
| s32 rem; |
| |
| switch (chan->channel) { |
| case TADC_THERM1_THR1: |
| case TADC_THERM1_THR2: |
| case TADC_THERM1_THR3: |
| case TADC_THERM1_THR4: |
| chan_data = &chip->chans[TADC_THERM1]; |
| break; |
| case TADC_THERM2_THR1: |
| case TADC_THERM2_THR2: |
| case TADC_THERM2_THR3: |
| chan_data = &chip->chans[TADC_THERM2]; |
| break; |
| case TADC_DIE_TEMP_THR1: |
| case TADC_DIE_TEMP_THR2: |
| case TADC_DIE_TEMP_THR3: |
| chan_data = &chip->chans[TADC_DIE_TEMP]; |
| break; |
| default: |
| if (chan->channel >= ARRAY_SIZE(chip->chans)) { |
| pr_err("Channel %d is out of bounds\n", chan->channel); |
| return -EINVAL; |
| } |
| |
| chan_data = &chip->chans[chan->channel]; |
| break; |
| } |
| |
| if (!chan_data) |
| return -EINVAL; |
| |
| switch (mask) { |
| case IIO_CHAN_INFO_PROCESSED: |
| switch (chan->channel) { |
| case TADC_THERM1_THR1: |
| case TADC_THERM1_THR2: |
| case TADC_THERM1_THR3: |
| case TADC_THERM1_THR4: |
| case TADC_THERM2_THR1: |
| case TADC_THERM2_THR2: |
| case TADC_THERM2_THR3: |
| rc = tadc_get_raw_therm(chan_data, val, &raw); |
| if (rc < 0) { |
| pr_err("Couldn't get raw value rc=%d\n", rc); |
| return rc; |
| } |
| break; |
| case TADC_DIE_TEMP_THR1: |
| case TADC_DIE_TEMP_THR2: |
| case TADC_DIE_TEMP_THR3: |
| /* DIV_ROUND_CLOSEST does not like negative numbers */ |
| raw = div_s64_rem(val - chan_data->offset, |
| chan_data->scale, &rem); |
| if (abs(rem) >= abs(chan_data->scale / 2)) |
| raw++; |
| break; |
| default: |
| return -EINVAL; |
| } |
| |
| rc = tadc_write_raw(indio_dev, chan, raw, 0, |
| IIO_CHAN_INFO_RAW); |
| if (rc < 0) { |
| pr_err("Couldn't write raw rc=%d\n", rc); |
| return rc; |
| } |
| |
| break; |
| case IIO_CHAN_INFO_RAW: |
| switch (chan->channel) { |
| case TADC_THERM1_THR1: |
| case TADC_THERM2_THR1: |
| case TADC_DIE_TEMP_THR1: |
| rc = tadc_write_channel(chip, |
| chan_data->thr[0].addr_lo, val); |
| break; |
| case TADC_THERM1_THR2: |
| case TADC_THERM2_THR2: |
| case TADC_DIE_TEMP_THR2: |
| rc = tadc_write_channel(chip, |
| chan_data->thr[1].addr_lo, val); |
| break; |
| case TADC_THERM1_THR3: |
| case TADC_THERM2_THR3: |
| case TADC_DIE_TEMP_THR3: |
| rc = tadc_write_channel(chip, |
| chan_data->thr[2].addr_lo, val); |
| break; |
| case TADC_THERM1_THR4: |
| rc = tadc_write_channel(chip, |
| chan_data->thr[3].addr_lo, val); |
| break; |
| default: |
| return -EINVAL; |
| } |
| |
| if (rc < 0) { |
| pr_err("Couldn't write channel %d\n", chan->channel); |
| return rc; |
| } |
| |
| break; |
| default: |
| return -EINVAL; |
| } |
| |
| return 0; |
| } |
| |
| static irqreturn_t handle_eoc(int irq, void *dev_id) |
| { |
| struct tadc_chip *chip = dev_id; |
| |
| complete_all(&chip->eoc_complete); |
| return IRQ_HANDLED; |
| } |
| |
| static int tadc_disable_vote_callback(struct votable *votable, |
| void *data, int disable, const char *client) |
| { |
| struct tadc_chip *chip = data; |
| int rc; |
| int timeout; |
| unsigned long timeleft; |
| |
| if (disable) { |
| timeout = msecs_to_jiffies(CONVERSION_TIMEOUT_MS); |
| timeleft = wait_for_completion_timeout(&chip->eoc_complete, |
| timeout); |
| if (timeleft == 0) |
| pr_err("Timed out waiting for eoc, disabling hw conversions regardless\n"); |
| |
| rc = tadc_read(chip, TADC_HWTRIG_CONV_CH_EN_REG(chip), |
| &chip->hwtrig_conv, 1); |
| if (rc < 0) { |
| pr_err("Couldn't save hw conversions rc=%d\n", rc); |
| return rc; |
| } |
| rc = tadc_write(chip, TADC_HWTRIG_CONV_CH_EN_REG(chip), 0x00); |
| if (rc < 0) { |
| pr_err("Couldn't disable hw conversions rc=%d\n", rc); |
| return rc; |
| } |
| rc = tadc_write(chip, TADC_ADC_DIRECT_TST(chip), 0x80); |
| if (rc < 0) { |
| pr_err("Couldn't enable direct test mode rc=%d\n", rc); |
| return rc; |
| } |
| } else { |
| rc = tadc_write(chip, TADC_ADC_DIRECT_TST(chip), 0x00); |
| if (rc < 0) { |
| pr_err("Couldn't disable direct test mode rc=%d\n", rc); |
| return rc; |
| } |
| rc = tadc_write(chip, TADC_HWTRIG_CONV_CH_EN_REG(chip), |
| chip->hwtrig_conv); |
| if (rc < 0) { |
| pr_err("Couldn't restore hw conversions rc=%d\n", rc); |
| return rc; |
| } |
| } |
| |
| pr_debug("client: %s disable: %d\n", client, disable); |
| return 0; |
| } |
| |
| static void status_change_work(struct work_struct *work) |
| { |
| struct tadc_chip *chip = container_of(work, |
| struct tadc_chip, status_change_work); |
| union power_supply_propval pval = {0, }; |
| int rc; |
| |
| if (!chip->usb_psy) |
| chip->usb_psy = power_supply_get_by_name("usb"); |
| |
| if (!chip->usb_psy) { |
| /* treat usb is not present */ |
| vote(chip->tadc_disable_votable, USB_PRESENT_VOTER, true, 0); |
| return; |
| } |
| |
| rc = power_supply_get_property(chip->usb_psy, |
| POWER_SUPPLY_PROP_PRESENT, &pval); |
| if (rc < 0) { |
| pr_err("Couldn't get present status rc=%d\n", rc); |
| /* treat usb is not present */ |
| vote(chip->tadc_disable_votable, USB_PRESENT_VOTER, true, 0); |
| return; |
| } |
| |
| /* disable if usb is not present */ |
| vote(chip->tadc_disable_votable, USB_PRESENT_VOTER, !pval.intval, 0); |
| } |
| |
| static int tadc_notifier_call(struct notifier_block *nb, |
| unsigned long ev, void *v) |
| { |
| struct power_supply *psy = v; |
| struct tadc_chip *chip = container_of(nb, struct tadc_chip, nb); |
| |
| if (ev != PSY_EVENT_PROP_CHANGED) |
| return NOTIFY_OK; |
| |
| if ((strcmp(psy->desc->name, "usb") == 0)) |
| schedule_work(&chip->status_change_work); |
| |
| return NOTIFY_OK; |
| } |
| |
| static int tadc_register_notifier(struct tadc_chip *chip) |
| { |
| int rc; |
| |
| chip->nb.notifier_call = tadc_notifier_call; |
| rc = power_supply_reg_notifier(&chip->nb); |
| if (rc < 0) { |
| pr_err("Couldn't register psy notifier rc = %d\n", rc); |
| return rc; |
| } |
| |
| return 0; |
| } |
| |
| static int tadc_suspend(struct device *dev) |
| { |
| struct tadc_chip *chip = dev_get_drvdata(dev); |
| |
| vote(chip->tadc_disable_votable, SLEEP_VOTER, true, 0); |
| return 0; |
| } |
| |
| static int tadc_resume(struct device *dev) |
| { |
| struct tadc_chip *chip = dev_get_drvdata(dev); |
| |
| vote(chip->tadc_disable_votable, SLEEP_VOTER, false, 0); |
| return 0; |
| } |
| |
| static int tadc_set_therm_table(struct tadc_chan_data *chan_data, u32 beta, |
| u32 rtherm) |
| { |
| if (beta == 3450 && rtherm == 68000) { |
| chan_data->table = tadc_therm_3450b_68k; |
| chan_data->tablesize = ARRAY_SIZE(tadc_therm_3450b_68k); |
| return 0; |
| } |
| |
| return -ENOENT; |
| } |
| |
| static int tadc_parse_dt(struct tadc_chip *chip) |
| { |
| struct device_node *child, *node; |
| struct tadc_chan_data *chan_data; |
| u32 chan_id, rtherm, beta; |
| int rc = 0; |
| |
| node = chip->dev->of_node; |
| for_each_available_child_of_node(node, child) { |
| rc = of_property_read_u32(child, "reg", &chan_id); |
| if (rc < 0) { |
| pr_err("Couldn't find channel for %s rc=%d", |
| child->name, rc); |
| return rc; |
| } |
| |
| if (chan_id > TADC_NUM_CH - 1) { |
| pr_err("Channel %d is out of range [0, %d]\n", |
| chan_id, TADC_NUM_CH - 1); |
| return -EINVAL; |
| } |
| |
| chan_data = &chip->chans[chan_id]; |
| if (chan_id == TADC_THERM1 || chan_id == TADC_THERM2) { |
| rc = of_property_read_u32(child, |
| "qcom,rbias", &chan_data->rbias); |
| if (rc < 0) { |
| pr_err("Couldn't read qcom,rbias rc=%d\n", rc); |
| return rc; |
| } |
| |
| rc = of_property_read_u32(child, |
| "qcom,beta-coefficient", &beta); |
| if (rc < 0) { |
| pr_err("Couldn't read qcom,beta-coefficient rc=%d\n", |
| rc); |
| return rc; |
| } |
| |
| rc = of_property_read_u32(child, |
| "qcom,rtherm-at-25degc", &rtherm); |
| if (rc < 0) { |
| pr_err("Couldn't read qcom,rtherm-at-25degc rc=%d\n", |
| rc); |
| return rc; |
| } |
| |
| rc = tadc_set_therm_table(chan_data, beta, rtherm); |
| if (rc < 0) { |
| pr_err("Couldn't set therm table rc=%d\n", rc); |
| return rc; |
| } |
| } else { |
| rc = of_property_read_s32(child, "qcom,scale", |
| &chan_data->scale); |
| if (rc < 0) { |
| pr_err("Couldn't read scale rc=%d\n", rc); |
| return rc; |
| } |
| |
| of_property_read_s32(child, "qcom,offset", |
| &chan_data->offset); |
| } |
| } |
| |
| return rc; |
| } |
| |
| static int tadc_init_hw(struct tadc_chip *chip) |
| { |
| int rc; |
| |
| chip->chans[TADC_THERM1].thr[0].addr_lo = |
| TADC_CMP_THR1_CH1_CMP_LO_REG(chip); |
| chip->chans[TADC_THERM1].thr[0].addr_hi = |
| TADC_CMP_THR1_CH1_CMP_HI_REG(chip); |
| chip->chans[TADC_THERM1].thr[1].addr_lo = |
| TADC_CMP_THR2_CH1_CMP_LO_REG(chip); |
| chip->chans[TADC_THERM1].thr[1].addr_hi = |
| TADC_CMP_THR2_CH1_CMP_HI_REG(chip); |
| chip->chans[TADC_THERM1].thr[2].addr_lo = |
| TADC_CMP_THR3_CH1_CMP_LO_REG(chip); |
| chip->chans[TADC_THERM1].thr[2].addr_hi = |
| TADC_CMP_THR3_CH1_CMP_HI_REG(chip); |
| chip->chans[TADC_THERM1].thr[3].addr_lo = |
| TADC_CMP_THR4_CH1_CMP_LO_REG(chip); |
| chip->chans[TADC_THERM1].thr[3].addr_hi = |
| TADC_CMP_THR4_CH1_CMP_HI_REG(chip); |
| |
| chip->chans[TADC_THERM2].thr[0].addr_lo = |
| TADC_CMP_THR1_CH2_CMP_LO_REG(chip); |
| chip->chans[TADC_THERM2].thr[0].addr_hi = |
| TADC_CMP_THR1_CH2_CMP_HI_REG(chip); |
| chip->chans[TADC_THERM2].thr[1].addr_lo = |
| TADC_CMP_THR2_CH2_CMP_LO_REG(chip); |
| chip->chans[TADC_THERM2].thr[1].addr_hi = |
| TADC_CMP_THR2_CH2_CMP_HI_REG(chip); |
| chip->chans[TADC_THERM2].thr[2].addr_lo = |
| TADC_CMP_THR3_CH2_CMP_LO_REG(chip); |
| chip->chans[TADC_THERM2].thr[2].addr_hi = |
| TADC_CMP_THR3_CH2_CMP_HI_REG(chip); |
| |
| chip->chans[TADC_DIE_TEMP].thr[0].addr_lo = |
| TADC_CMP_THR1_CH3_CMP_LO_REG(chip); |
| chip->chans[TADC_DIE_TEMP].thr[0].addr_hi = |
| TADC_CMP_THR1_CH3_CMP_HI_REG(chip); |
| chip->chans[TADC_DIE_TEMP].thr[1].addr_lo = |
| TADC_CMP_THR2_CH3_CMP_LO_REG(chip); |
| chip->chans[TADC_DIE_TEMP].thr[1].addr_hi = |
| TADC_CMP_THR2_CH3_CMP_HI_REG(chip); |
| chip->chans[TADC_DIE_TEMP].thr[2].addr_lo = |
| TADC_CMP_THR3_CH3_CMP_LO_REG(chip); |
| chip->chans[TADC_DIE_TEMP].thr[2].addr_hi = |
| TADC_CMP_THR3_CH3_CMP_HI_REG(chip); |
| |
| rc = tadc_write(chip, TADC_CMP_THR1_CMP_REG(chip), 0); |
| if (rc < 0) { |
| pr_err("Couldn't enable hardware triggers rc=%d\n", rc); |
| return rc; |
| } |
| |
| rc = tadc_write(chip, TADC_CMP_THR2_CMP_REG(chip), 0); |
| if (rc < 0) { |
| pr_err("Couldn't enable hardware triggers rc=%d\n", rc); |
| return rc; |
| } |
| |
| rc = tadc_write(chip, TADC_CMP_THR3_CMP_REG(chip), 0); |
| if (rc < 0) { |
| pr_err("Couldn't enable hardware triggers rc=%d\n", rc); |
| return rc; |
| } |
| |
| /* enable connector and die temp hardware triggers */ |
| rc = tadc_masked_write(chip, TADC_HWTRIG_CONV_CH_EN_REG(chip), |
| BIT(TADC_THERM2) | BIT(TADC_DIE_TEMP), |
| BIT(TADC_THERM2) | BIT(TADC_DIE_TEMP)); |
| if (rc < 0) { |
| pr_err("Couldn't enable hardware triggers rc=%d\n", rc); |
| return rc; |
| } |
| |
| /* save hw triggered conversion configuration */ |
| rc = tadc_read(chip, TADC_HWTRIG_CONV_CH_EN_REG(chip), |
| &chip->hwtrig_conv, 1); |
| if (rc < 0) { |
| pr_err("Couldn't save hw conversions rc=%d\n", rc); |
| return rc; |
| } |
| |
| return 0; |
| } |
| |
| static const struct iio_info tadc_info = { |
| .read_raw = &tadc_read_raw, |
| .write_raw = &tadc_write_raw, |
| .driver_module = THIS_MODULE, |
| }; |
| |
| static int tadc_probe(struct platform_device *pdev) |
| { |
| struct device_node *node = pdev->dev.of_node; |
| struct iio_dev *indio_dev; |
| struct tadc_chip *chip; |
| int rc, irq; |
| |
| indio_dev = devm_iio_device_alloc(&pdev->dev, sizeof(*chip)); |
| if (!indio_dev) |
| return -ENOMEM; |
| |
| chip = iio_priv(indio_dev); |
| chip->dev = &pdev->dev; |
| init_completion(&chip->eoc_complete); |
| |
| /* |
| * set the completion in "completed" state so disable of the tadc |
| * can progress |
| */ |
| complete_all(&chip->eoc_complete); |
| |
| rc = of_property_read_u32(node, "reg", &chip->tadc_base); |
| if (rc < 0) { |
| pr_err("Couldn't read base address rc=%d\n", rc); |
| return rc; |
| } |
| chip->tadc_cmp_base = chip->tadc_base + 0x100; |
| |
| mutex_init(&chip->write_lock); |
| mutex_init(&chip->conv_lock); |
| INIT_WORK(&chip->status_change_work, status_change_work); |
| chip->regmap = dev_get_regmap(chip->dev->parent, NULL); |
| if (!chip->regmap) { |
| pr_err("Couldn't get regmap\n"); |
| return -ENODEV; |
| } |
| |
| rc = tadc_parse_dt(chip); |
| if (rc < 0) { |
| pr_err("Couldn't parse device tree rc=%d\n", rc); |
| return rc; |
| } |
| |
| rc = tadc_init_hw(chip); |
| if (rc < 0) { |
| pr_err("Couldn't initialize hardware rc=%d\n", rc); |
| return rc; |
| } |
| |
| chip->tadc_disable_votable = create_votable("SMB_TADC_DISABLE", |
| VOTE_SET_ANY, |
| tadc_disable_vote_callback, |
| chip); |
| if (IS_ERR(chip->tadc_disable_votable)) { |
| rc = PTR_ERR(chip->tadc_disable_votable); |
| return rc; |
| } |
| /* assume usb is not present */ |
| vote(chip->tadc_disable_votable, USB_PRESENT_VOTER, true, 0); |
| vote(chip->tadc_disable_votable, SHUTDOWN_VOTER, false, 0); |
| vote(chip->tadc_disable_votable, SLEEP_VOTER, false, 0); |
| |
| rc = tadc_register_notifier(chip); |
| if (rc < 0) { |
| pr_err("Couldn't register notifier=%d\n", rc); |
| goto destroy_votable; |
| } |
| |
| irq = of_irq_get_byname(node, "eoc"); |
| if (irq < 0) { |
| pr_err("Couldn't get eoc irq rc=%d\n", irq); |
| goto destroy_votable; |
| } |
| |
| rc = devm_request_threaded_irq(chip->dev, irq, NULL, handle_eoc, |
| IRQF_ONESHOT, "eoc", chip); |
| if (rc < 0) { |
| pr_err("Couldn't request irq %d rc=%d\n", irq, rc); |
| goto destroy_votable; |
| } |
| |
| indio_dev->dev.parent = chip->dev; |
| indio_dev->name = pdev->name; |
| indio_dev->modes = INDIO_DIRECT_MODE; |
| indio_dev->info = &tadc_info; |
| indio_dev->channels = tadc_iio_chans; |
| indio_dev->num_channels = ARRAY_SIZE(tadc_iio_chans); |
| |
| rc = devm_iio_device_register(chip->dev, indio_dev); |
| if (rc < 0) { |
| pr_err("Couldn't register IIO device rc=%d\n", rc); |
| goto destroy_votable; |
| } |
| |
| platform_set_drvdata(pdev, chip); |
| return 0; |
| |
| destroy_votable: |
| destroy_votable(chip->tadc_disable_votable); |
| return rc; |
| } |
| |
| static int tadc_remove(struct platform_device *pdev) |
| { |
| struct tadc_chip *chip = platform_get_drvdata(pdev); |
| |
| destroy_votable(chip->tadc_disable_votable); |
| return 0; |
| } |
| |
| static void tadc_shutdown(struct platform_device *pdev) |
| { |
| struct tadc_chip *chip = platform_get_drvdata(pdev); |
| |
| vote(chip->tadc_disable_votable, SHUTDOWN_VOTER, true, 0); |
| } |
| |
| static const struct dev_pm_ops tadc_pm_ops = { |
| .resume = tadc_resume, |
| .suspend = tadc_suspend, |
| }; |
| |
| static const struct of_device_id tadc_match_table[] = { |
| { .compatible = "qcom,tadc" }, |
| { } |
| }; |
| MODULE_DEVICE_TABLE(of, tadc_match_table); |
| |
| static struct platform_driver tadc_driver = { |
| .driver = { |
| .name = "qcom-tadc", |
| .of_match_table = tadc_match_table, |
| .pm = &tadc_pm_ops, |
| }, |
| .probe = tadc_probe, |
| .remove = tadc_remove, |
| .shutdown = tadc_shutdown, |
| }; |
| module_platform_driver(tadc_driver); |
| |
| MODULE_DESCRIPTION("Qualcomm Technologies Inc. TADC driver"); |
| MODULE_LICENSE("GPL v2"); |