From 6e05ffd34a07e56a6135619db0352c5da7a09d9a Mon Sep 17 00:00:00 2001 From: Jonathan Bell Date: Fri, 15 Sep 2023 17:33:03 +0100 Subject: [PATCH 0574/1085] drivers: rtc-rpi: add battery charge circuit control and readback Parse devicetree for a charger voltage and apply it. If nonzero and a valid voltage, the firmware will enable charging, otherwise the charger circuit is disabled. Add sysfs attributes to read back the supported charge voltage range, the measured battery voltage, and the charger setpoint. Signed-off-by: Jonathan Bell --- drivers/rtc/rtc-rpi.c | 106 ++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 103 insertions(+), 3 deletions(-) --- a/drivers/rtc/rtc-rpi.c +++ b/drivers/rtc/rtc-rpi.c @@ -19,11 +19,22 @@ struct rpi_rtc_data { struct rtc_device *rtc; struct rpi_firmware *fw; + u32 bbat_vchg_microvolts; }; #define RPI_FIRMWARE_GET_RTC_REG 0x00030087 #define RPI_FIRMWARE_SET_RTC_REG 0x00038087 -enum {RTC_TIME, RTC_ALARM, RTC_ALARM_PENDING, RTC_ALARM_ENABLE}; + +enum { + RTC_TIME, + RTC_ALARM, + RTC_ALARM_PENDING, + RTC_ALARM_ENABLE, + RTC_BBAT_CHG_VOLTS, + RTC_BBAT_CHG_VOLTS_MIN, + RTC_BBAT_CHG_VOLTS_MAX, + RTC_BBAT_VOLTS +}; static int rpi_rtc_read_time(struct device *dev, struct rtc_time *tm) { @@ -114,6 +125,83 @@ static const struct rtc_class_ops rpi_rt .alarm_irq_enable = rpi_rtc_alarm_irq_enable, }; +static int rpi_rtc_set_charge_voltage(struct device *dev) +{ + struct rpi_rtc_data *vrtc = dev_get_drvdata(dev); + u32 data[2] = {RTC_BBAT_CHG_VOLTS, vrtc->bbat_vchg_microvolts}; + int err; + + err = rpi_firmware_property(vrtc->fw, RPI_FIRMWARE_SET_RTC_REG, + &data, sizeof(data)); + + if (err) + dev_err(dev, "failed to set trickle charge voltage to %uuV: %d\n", + vrtc->bbat_vchg_microvolts, err); + else if (vrtc->bbat_vchg_microvolts) + dev_info(dev, "trickle charging enabled at %uuV\n", + vrtc->bbat_vchg_microvolts); + + return err; +} + +static ssize_t rpi_rtc_print_uint_reg(struct device *dev, char *buf, u32 reg) +{ + struct rpi_rtc_data *vrtc = dev_get_drvdata(dev->parent); + u32 data[2] = {reg, 0}; + int ret = 0; + + ret = rpi_firmware_property(vrtc->fw, RPI_FIRMWARE_GET_RTC_REG, + &data, sizeof(data)); + if (ret < 0) + return ret; + + return sprintf(buf, "%u\n", data[1]); +} + +static ssize_t charging_voltage_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + return rpi_rtc_print_uint_reg(dev, buf, RTC_BBAT_CHG_VOLTS); +} +static DEVICE_ATTR_RO(charging_voltage); + +static ssize_t charging_voltage_min_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + return rpi_rtc_print_uint_reg(dev, buf, RTC_BBAT_CHG_VOLTS_MIN); +} +static DEVICE_ATTR_RO(charging_voltage_min); + +static ssize_t charging_voltage_max_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + return rpi_rtc_print_uint_reg(dev, buf, RTC_BBAT_CHG_VOLTS_MAX); +} +static DEVICE_ATTR_RO(charging_voltage_max); + +static ssize_t battery_voltage_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + return rpi_rtc_print_uint_reg(dev, buf, RTC_BBAT_VOLTS); +} +static DEVICE_ATTR_RO(battery_voltage); + +static struct attribute *rpi_rtc_attrs[] = { + &dev_attr_charging_voltage.attr, + &dev_attr_charging_voltage_min.attr, + &dev_attr_charging_voltage_max.attr, + &dev_attr_battery_voltage.attr, + NULL +}; + +static const struct attribute_group rpi_rtc_sysfs_files = { + .attrs = rpi_rtc_attrs, +}; + static int rpi_rtc_probe(struct platform_device *pdev) { struct rpi_rtc_data *vrtc; @@ -151,10 +239,22 @@ static int rpi_rtc_probe(struct platform clear_bit(RTC_FEATURE_UPDATE_INTERRUPT, vrtc->rtc->features); vrtc->rtc->ops = &rpi_rtc_ops; - ret = devm_rtc_register_device(vrtc->rtc); + ret = rtc_add_group(vrtc->rtc, &rpi_rtc_sysfs_files); + if (ret) + return ret; rpi_rtc_alarm_clear_pending(dev); - return ret; + + /* + * Optionally enable trickle charging - if the property isn't + * present (or set to zero), trickle charging is disabled. + */ + of_property_read_u32(np, "trickle-charge-microvolt", + &vrtc->bbat_vchg_microvolts); + + rpi_rtc_set_charge_voltage(dev); + + return devm_rtc_register_device(vrtc->rtc); } static const struct of_device_id rpi_rtc_dt_match[] = {