123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619 |
- // SPDX-License-Identifier: GPL-2.0-only
- /*
- * Copyright (c) 2018-2020 The Linux Foundation. All rights reserved.
- * Copyright (c) 2022-2023, Qualcomm Innovation Center, Inc. All rights reserved.
- */
- #include <linux/alarmtimer.h>
- #include <linux/cdev.h>
- #include <linux/device.h>
- #include <linux/interrupt.h>
- #include <linux/power_supply.h>
- #include <linux/regmap.h>
- #include <linux/rtc.h>
- #include <linux/iio/consumer.h>
- #include <uapi/linux/qg.h>
- #include "qg-sdam.h"
- #include "qg-core.h"
- #include "qg-reg.h"
- #include "qg-defs.h"
- #include "qg-iio.h"
- #include "qg-util.h"
- static inline bool is_sticky_register(u32 addr)
- {
- if ((addr & 0xFF) == QG_STATUS2_REG)
- return true;
- return false;
- }
- int qg_read(struct qpnp_qg *chip, u32 addr, u8 *val, int len)
- {
- int rc, i;
- u32 dummy = 0;
- rc = regmap_bulk_read(chip->regmap, addr, val, len);
- if (rc < 0) {
- pr_err("Failed regmap_read for address %04x rc=%d\n", addr, rc);
- return rc;
- }
- if (is_sticky_register(addr)) {
- /* write to the sticky register to clear it */
- rc = regmap_write(chip->regmap, addr, dummy);
- if (rc < 0) {
- pr_err("Failed regmap_write for %04x rc=%d\n",
- addr, rc);
- return rc;
- }
- }
- if (*chip->debug_mask & QG_DEBUG_BUS_READ) {
- pr_info("length %d addr=%04x\n", len, addr);
- for (i = 0; i < len; i++)
- pr_info("val[%d]: %02x\n", i, val[i]);
- }
- return 0;
- }
- int qg_write(struct qpnp_qg *chip, u32 addr, u8 *val, int len)
- {
- int rc, i;
- mutex_lock(&chip->bus_lock);
- if (len > 1)
- rc = regmap_bulk_write(chip->regmap, addr, val, len);
- else
- rc = regmap_write(chip->regmap, addr, *val);
- if (rc < 0) {
- pr_err("Failed regmap_write for address %04x rc=%d\n",
- addr, rc);
- goto out;
- }
- if (*chip->debug_mask & QG_DEBUG_BUS_WRITE) {
- pr_info("length %d addr=%04x\n", len, addr);
- for (i = 0; i < len; i++)
- pr_info("val[%d]: %02x\n", i, val[i]);
- }
- out:
- mutex_unlock(&chip->bus_lock);
- return rc;
- }
- int qg_masked_write(struct qpnp_qg *chip, int addr, u32 mask, u32 val)
- {
- int rc;
- mutex_lock(&chip->bus_lock);
- rc = regmap_update_bits(chip->regmap, addr, mask, val);
- if (rc < 0) {
- pr_err("Failed regmap_update_bits for address %04x rc=%d\n",
- addr, rc);
- goto out;
- }
- if (*chip->debug_mask & QG_DEBUG_BUS_WRITE)
- pr_info("addr=%04x mask: %02x val: %02x\n", addr, mask, val);
- out:
- mutex_unlock(&chip->bus_lock);
- return rc;
- }
- int qg_read_raw_data(struct qpnp_qg *chip, int addr, u32 *data)
- {
- int rc;
- u8 reg[2] = {0};
- rc = qg_read(chip, chip->qg_base + addr, ®[0], 2);
- if (rc < 0) {
- pr_err("Failed to read QG addr %d rc=%d\n", addr, rc);
- return rc;
- }
- *data = reg[0] | (reg[1] << 8);
- return rc;
- }
- s64 qg_iraw_to_ua(struct qpnp_qg *chip, int iraw)
- {
- if (chip->qg_subtype == QG_ADC_IBAT_5A)
- return div_s64(152588LL * (s64)iraw, 1000);
- else
- return div_s64(305176LL * (s64)iraw, 1000);
- }
- int get_fifo_length(struct qpnp_qg *chip, u32 *fifo_length, bool rt)
- {
- int rc;
- u8 reg = 0;
- u32 addr;
- addr = rt ? QG_STATUS3_REG : QG_S2_NORMAL_MEAS_CTL2_REG;
- rc = qg_read(chip, chip->qg_base + addr, ®, 1);
- if (rc < 0) {
- pr_err("Failed to read FIFO length rc=%d\n", rc);
- return rc;
- }
- if (rt) {
- *fifo_length = reg & COUNT_FIFO_RT_MASK;
- } else {
- *fifo_length = (reg & FIFO_LENGTH_MASK) >> FIFO_LENGTH_SHIFT;
- *fifo_length += 1;
- }
- return rc;
- }
- int get_sample_count(struct qpnp_qg *chip, u32 *sample_count)
- {
- int rc;
- u8 reg = 0;
- rc = qg_read(chip, chip->qg_base + QG_S2_NORMAL_MEAS_CTL2_REG,
- ®, 1);
- if (rc < 0) {
- pr_err("Failed to read FIFO sample count rc=%d\n", rc);
- return rc;
- }
- *sample_count = 1 << ((reg & NUM_OF_ACCUM_MASK) + 1);
- return rc;
- }
- #define QG_CLK_RATE 32000
- #define QG_ACTUAL_CLK_RATE 32764
- int get_sample_interval(struct qpnp_qg *chip, u32 *sample_interval)
- {
- int rc;
- u8 reg = 0;
- rc = qg_read(chip, chip->qg_base + QG_S2_NORMAL_MEAS_CTL3_REG,
- ®, 1);
- if (rc < 0) {
- pr_err("Failed to read FIFO sample interval rc=%d\n", rc);
- return rc;
- }
- *sample_interval = reg * 10;
- if (chip->wa_flags & QG_CLK_ADJUST_WA) {
- *sample_interval = DIV_ROUND_CLOSEST(
- *sample_interval * QG_CLK_RATE, QG_ACTUAL_CLK_RATE);
- }
- return rc;
- }
- int get_rtc_time(unsigned long *rtc_time)
- {
- struct rtc_time tm;
- struct rtc_device *rtc;
- int rc;
- rtc = rtc_class_open(CONFIG_RTC_HCTOSYS_DEVICE);
- if (rtc == NULL) {
- pr_err("Failed to open rtc device (%s)\n",
- CONFIG_RTC_HCTOSYS_DEVICE);
- return -EINVAL;
- }
- rc = rtc_read_time(rtc, &tm);
- if (rc) {
- pr_err("Failed to read rtc time (%s) : %d\n",
- CONFIG_RTC_HCTOSYS_DEVICE, rc);
- goto close_time;
- }
- rc = rtc_valid_tm(&tm);
- if (rc) {
- pr_err("Invalid RTC time (%s): %d\n",
- CONFIG_RTC_HCTOSYS_DEVICE, rc);
- goto close_time;
- }
- *rtc_time = rtc_tm_to_time64(&tm);
- close_time:
- rtc_class_close(rtc);
- return rc;
- }
- int get_fifo_done_time(struct qpnp_qg *chip, bool rt, int *time_ms)
- {
- int rc, length = 0;
- u32 sample_count = 0, sample_interval = 0, acc_count = 0;
- rc = get_fifo_length(chip, &length, rt ? true : false);
- if (rc < 0)
- return rc;
- rc = get_sample_count(chip, &sample_count);
- if (rc < 0)
- return rc;
- rc = get_sample_interval(chip, &sample_interval);
- if (rc < 0)
- return rc;
- *time_ms = length * sample_count * sample_interval;
- if (rt) {
- rc = qg_read(chip, chip->qg_base + QG_ACCUM_CNT_RT_REG,
- (u8 *)&acc_count, 1);
- if (rc < 0)
- return rc;
- *time_ms += ((sample_count - acc_count) * sample_interval);
- }
- return 0;
- }
- static bool is_usb_available(struct qpnp_qg *chip)
- {
- if (chip->usb_psy)
- return true;
- chip->usb_psy = power_supply_get_by_name("usb");
- if (!chip->usb_psy)
- return false;
- return true;
- }
- static bool is_dc_available(struct qpnp_qg *chip)
- {
- if (chip->dc_psy)
- return true;
- chip->dc_psy = power_supply_get_by_name("dc");
- if (!chip->dc_psy)
- return false;
- return true;
- }
- bool is_usb_present(struct qpnp_qg *chip)
- {
- union power_supply_propval pval = {0, };
- if (is_usb_available(chip))
- power_supply_get_property(chip->usb_psy,
- POWER_SUPPLY_PROP_PRESENT, &pval);
- return pval.intval ? true : false;
- }
- bool is_dc_present(struct qpnp_qg *chip)
- {
- union power_supply_propval pval = {0, };
- if (is_dc_available(chip))
- power_supply_get_property(chip->dc_psy,
- POWER_SUPPLY_PROP_PRESENT, &pval);
- return pval.intval ? true : false;
- }
- bool is_input_present(struct qpnp_qg *chip)
- {
- return is_usb_present(chip) || is_dc_present(chip);
- }
- bool is_parallel_available(struct qpnp_qg *chip)
- {
- if (is_chan_valid(chip, PARALLEL_CHARGING_ENABLED))
- return true;
- return false;
- }
- bool is_cp_available(struct qpnp_qg *chip)
- {
- if (chip->cp_psy)
- return true;
- chip->cp_psy = power_supply_get_by_name("charge_pump_master");
- if (!chip->cp_psy)
- return false;
- return true;
- }
- bool is_parallel_enabled(struct qpnp_qg *chip)
- {
- int val = 0;
- if (is_parallel_available(chip))
- qg_read_iio_chan(chip, PARALLEL_CHARGING_ENABLED, &val);
- else if (is_cp_available(chip))
- qg_read_iio_chan(chip, CP_CHARGING_ENABLED, &val);
- return val ? true : false;
- }
- int qg_write_monotonic_soc(struct qpnp_qg *chip, int msoc)
- {
- u8 reg = 0;
- int rc;
- reg = (msoc * 255) / 100;
- rc = qg_write(chip, chip->qg_base + QG_SOC_MONOTONIC_REG,
- ®, 1);
- if (rc < 0)
- pr_err("Failed to update QG_SOC_MONOTINIC reg rc=%d\n", rc);
- return rc;
- }
- int qg_get_battery_temp(struct qpnp_qg *chip, int *temp)
- {
- int rc = 0;
- if (chip->battery_missing) {
- *temp = 250;
- return 0;
- }
- rc = iio_read_channel_processed(chip->batt_therm_chan, temp);
- if (rc < 0) {
- pr_err("Failed reading BAT_TEMP over ADC rc=%d\n", rc);
- return rc;
- }
- pr_debug("batt_temp = %d\n", *temp);
- return 0;
- }
- int qg_get_battery_current(struct qpnp_qg *chip, int *ibat_ua)
- {
- int rc = 0, last_ibat = 0;
- if (chip->battery_missing) {
- *ibat_ua = 0;
- return 0;
- }
- if (chip->qg_mode == QG_V_MODE) {
- *ibat_ua = chip->qg_v_ibat;
- return 0;
- }
- /* hold data */
- rc = qg_masked_write(chip, chip->qg_base + QG_DATA_CTL2_REG,
- BURST_AVG_HOLD_FOR_READ_BIT,
- BURST_AVG_HOLD_FOR_READ_BIT);
- if (rc < 0) {
- pr_err("Failed to hold burst-avg data rc=%d\n", rc);
- goto release;
- }
- rc = qg_read(chip, chip->qg_base + QG_LAST_BURST_AVG_I_DATA0_REG,
- (u8 *)&last_ibat, 2);
- if (rc < 0) {
- pr_err("Failed to read LAST_BURST_AVG_I reg, rc=%d\n", rc);
- goto release;
- }
- last_ibat = sign_extend32(last_ibat, 15);
- *ibat_ua = qg_iraw_to_ua(chip, last_ibat);
- release:
- /* release */
- qg_masked_write(chip, chip->qg_base + QG_DATA_CTL2_REG,
- BURST_AVG_HOLD_FOR_READ_BIT, 0);
- return rc;
- }
- int qg_get_battery_voltage(struct qpnp_qg *chip, int *vbat_uv)
- {
- int rc = 0;
- u64 last_vbat = 0;
- if (chip->battery_missing) {
- *vbat_uv = 3700000;
- return 0;
- }
- rc = qg_read(chip, chip->qg_base + QG_LAST_ADC_V_DATA0_REG,
- (u8 *)&last_vbat, 2);
- if (rc < 0) {
- pr_err("Failed to read LAST_ADV_V reg, rc=%d\n", rc);
- return rc;
- }
- *vbat_uv = V_RAW_TO_UV(last_vbat);
- return rc;
- }
- int qg_get_vbat_avg(struct qpnp_qg *chip, int *vbat_uv)
- {
- int rc = 0;
- u64 last_vbat = 0;
- rc = qg_read(chip, chip->qg_base + QG_S2_NORMAL_AVG_V_DATA0_REG,
- (u8 *)&last_vbat, 2);
- if (rc < 0) {
- pr_err("Failed to read S2_NORMAL_AVG_V reg, rc=%d\n", rc);
- return rc;
- }
- *vbat_uv = V_RAW_TO_UV(last_vbat);
- return 0;
- }
- int qg_get_ibat_avg(struct qpnp_qg *chip, int *ibat_ua)
- {
- int rc = 0;
- int last_ibat = 0;
- rc = qg_read(chip, chip->qg_base + QG_S2_NORMAL_AVG_I_DATA0_REG,
- (u8 *)&last_ibat, 2);
- if (rc < 0) {
- pr_err("Failed to read S2_NORMAL_AVG_I reg, rc=%d\n", rc);
- return rc;
- }
- if (last_ibat == FIFO_I_RESET_VAL) {
- /* First FIFO is not complete, read instantaneous IBAT */
- rc = qg_get_battery_current(chip, ibat_ua);
- if (rc < 0)
- pr_err("Failed to read inst. IBAT rc=%d\n", rc);
- return rc;
- }
- last_ibat = sign_extend32(last_ibat, 15);
- *ibat_ua = qg_iraw_to_ua(chip, last_ibat);
- return 0;
- }
- bool is_chan_valid(struct qpnp_qg *chip,
- enum qg_ext_iio_channels chan)
- {
- int rc;
- if (IS_ERR(chip->ext_iio_chans[chan]))
- return false;
- if (!chip->ext_iio_chans[chan]) {
- chip->ext_iio_chans[chan] = devm_iio_channel_get(chip->dev,
- qg_ext_iio_chan_name[chan]);
- if (IS_ERR(chip->ext_iio_chans[chan])) {
- rc = PTR_ERR(chip->ext_iio_chans[chan]);
- if (rc == -EPROBE_DEFER)
- chip->ext_iio_chans[chan] = NULL;
- pr_err("Failed to get IIO channel %s, rc=%d\n",
- qg_ext_iio_chan_name[chan], rc);
- return false;
- }
- }
- return true;
- }
- int qg_read_iio_chan(struct qpnp_qg *chip,
- enum qg_ext_iio_channels chan, int *val)
- {
- int rc;
- if (is_chan_valid(chip, chan)) {
- rc = iio_read_channel_processed(
- chip->ext_iio_chans[chan], val);
- return (rc < 0) ? rc : 0;
- }
- return -EINVAL;
- }
- int qg_write_iio_chan(struct qpnp_qg *chip,
- enum qg_ext_iio_channels chan, int val)
- {
- if (is_chan_valid(chip, chan))
- return iio_write_channel_raw(chip->ext_iio_chans[chan],
- val);
- return -EINVAL;
- }
- int qg_read_int_iio_chan(struct iio_channel *iio_chan_list, int chan_id,
- int *val)
- {
- int rc;
- do {
- if (iio_chan_list->channel->channel == chan_id) {
- rc = iio_read_channel_processed(iio_chan_list,
- val);
- return (rc < 0) ? rc : 0;
- }
- } while (iio_chan_list++);
- return -ENOENT;
- }
- int qg_read_range_data_from_node(struct device_node *node,
- const char *prop_str, struct range_data *ranges,
- int max_threshold, u32 max_value)
- {
- int rc = 0, i, length, per_tuple_length, tuples;
- if (!node || !prop_str || !ranges) {
- pr_err("Invalid parameters passed\n");
- return -EINVAL;
- }
- rc = of_property_count_elems_of_size(node, prop_str, sizeof(u32));
- if (rc < 0) {
- pr_err("Count %s failed, rc=%d\n", prop_str, rc);
- return rc;
- }
- length = rc;
- per_tuple_length = sizeof(struct range_data) / sizeof(u32);
- if (length % per_tuple_length) {
- pr_err("%s length (%d) should be multiple of %d\n",
- prop_str, length, per_tuple_length);
- return -EINVAL;
- }
- tuples = length / per_tuple_length;
- if (tuples > MAX_STEP_CHG_ENTRIES) {
- pr_err("too many entries(%d), only %d allowed\n",
- tuples, MAX_STEP_CHG_ENTRIES);
- return -EINVAL;
- }
- rc = of_property_read_u32_array(node, prop_str,
- (u32 *)ranges, length);
- if (rc) {
- pr_err("Read %s failed, rc=%d\n", prop_str, rc);
- return rc;
- }
- for (i = 0; i < tuples; i++) {
- if (ranges[i].low_threshold >
- ranges[i].high_threshold) {
- pr_err("%s thresholds should be in ascendant ranges\n",
- prop_str);
- rc = -EINVAL;
- goto clean;
- }
- if (i != 0) {
- if (ranges[i - 1].high_threshold >
- ranges[i].low_threshold) {
- pr_err("%s thresholds should be in ascendant ranges\n",
- prop_str);
- rc = -EINVAL;
- goto clean;
- }
- }
- if (ranges[i].low_threshold > max_threshold)
- ranges[i].low_threshold = max_threshold;
- if (ranges[i].high_threshold > max_threshold)
- ranges[i].high_threshold = max_threshold;
- if (ranges[i].value > max_value)
- ranges[i].value = max_value;
- }
- return rc;
- clean:
- memset(ranges, 0, tuples * sizeof(struct range_data));
- return rc;
- }
|