diff --git a/arch/arm/boot/dts/nxp/imx/Makefile b/arch/arm/boot/dts/nxp/imx/Makefile index 02758c69b279a..bd811e38c9525 100644 --- a/arch/arm/boot/dts/nxp/imx/Makefile +++ b/arch/arm/boot/dts/nxp/imx/Makefile @@ -1,4 +1,7 @@ # SPDX-License-Identifier: GPL-2.0 + +DTC_FLAGS_imx6ul-ts7250v3 := -@ + dtb-$(CONFIG_SOC_IMX1) += \ imx1-ads.dtb \ imx1-apf9328.dtb @@ -332,7 +335,6 @@ dtb-$(CONFIG_SOC_IMX6UL) += \ imx6ul-ts4100-8.dtb \ imx6ul-ts4100-16.dtb \ imx6ul-ts7250v3-reva.dtb \ - imx6ul-ts7250v3.dtb \ imx6ul-ts7553v2.dtb \ imx6ul-ts7100-1.dtb \ imx6ul-ts7100-3.dtb \ @@ -375,6 +377,13 @@ dtb-$(CONFIG_SOC_IMX6UL) += \ imx6ull-tqma6ull2l-mba6ulx.dtb \ imx6ulz-14x14-evk.dtb \ imx6ulz-bsh-smm-m2.dtb + +imx6ul-ts7250v3-silo104-i2c-gpio-dtbs := imx6ul-ts7250v3.dtb imx6ul-ts7250v3-silo104-i2c-gpio.dtbo +dtb-$(CONFIG_SOC_IMX6UL) += imx6ul-ts7250v3.dtb +dtb-$(CONFIG_SOC_IMX6UL) += imx6ul-ts7250v3-silo104-i2c-gpio.dtbo + +DTC_FLAGS_imx6ul-ts7250v3-silo104-i2c-gpio := -@ + dtb-$(CONFIG_SOC_IMX7D) += \ imx7d-cl-som-imx7.dtb \ imx7d-colibri-aster.dtb \ diff --git a/arch/arm/boot/dts/nxp/imx/imx6ul-ts7250v3-silo104-i2c-gpio.dtso b/arch/arm/boot/dts/nxp/imx/imx6ul-ts7250v3-silo104-i2c-gpio.dtso new file mode 100644 index 0000000000000..1321390feae21 --- /dev/null +++ b/arch/arm/boot/dts/nxp/imx/imx6ul-ts7250v3-silo104-i2c-gpio.dtso @@ -0,0 +1,170 @@ +/dts-v1/; +/plugin/; + +#include +#include + +/ { + fragment@0 { + target-path = "/"; + __overlay__ { + i2c5_gpio: i2c-104 { + compatible = "i2c-gpio"; + + /* FPGA bank1 pins: SCL=13, SDA=8 */ + scl-gpios = <&fpga_bank1 13 (GPIO_ACTIVE_HIGH | GPIO_OPEN_DRAIN)>; + sda-gpios = <&fpga_bank1 8 (GPIO_ACTIVE_HIGH | GPIO_OPEN_DRAIN)>; + + #address-cells = <1>; + #size-cells = <0>; + }; + }; + }; + + fragment@1 { + target-path = "/aliases"; + __overlay__ { + i2c5 = &i2c5_gpio; + }; + }; + + /* Add the wizard device tree under the i2c5_gpio bus */ + fragment@2 { + target = <&i2c5_gpio>; + __overlay__ { + wizard: wizard@68 { + compatible = "technologic,wizard"; + reg = <0x68>; + + silo_wizard_adc: silo-wizard-adc { + compatible = "technologic,wizard-adc"; + #io-channel-cells = <1>; + }; + + wizard_irq: silo-interrupt-controller { + compatible = "technologic,wizard-irq"; + interrupt-parent = <&fpga_intc>; + interrupts = <17 IRQ_TYPE_LEVEL_HIGH>; + + #interrupt-cells = <1>; + interrupt-controller; + }; + + wizard_silo: silo { + compatible = "technologic,wizard-silo"; + interrupt-parent = <&fpga_intc>; + interrupts = <17 IRQ_TYPE_LEVEL_HIGH>; + }; + + wizard_temp: temperature-sensor { + compatible = "technologic,wizard-temp"; + #io-channel-cells = <0>; + }; + + wizard_reset: reset-controller { + compatible = "technologic,wizard-reset"; + #reset-cells = <1>; + }; + }; + }; + }; + + /* ADC voltage-divider helpers for Silo104, channels 0..7 */ + fragment@3 { + target-path = "/"; + __overlay__ { + + /* iio-rescale to reflect 1:1 scaling */ + scap_junc_2: an-scap-junc-2 { + compatible = "iio-rescale"; + io-channels = <&silo_wizard_adc 0>; + io-channel-names = "parent"; + label = "silo-scap-junc-2"; + + numerator = <1>; + denominator = <1>; + type = "voltage"; + + #io-channel-cells = <0>; + }; + + silo_5v2mv_rail: an-silo-5v2mv-rail { + compatible = "voltage-divider"; + io-channels = <&silo_wizard_adc 1>; + label = "silo-5v2mv-rail"; + io-channel-names = "parent"; + output-ohms = <75000>; + full-ohms = <150000>; + #io-channel-cells = <0>; + }; + + silo_scap_plus: an-scap-plus { + compatible = "voltage-divider"; + io-channels = <&silo_wizard_adc 2>; + io-channel-names = "parent"; + label = "an-scap-plus"; + output-ohms = <75000>; + full-ohms = <150000>; + #io-channel-cells = <0>; + }; + + /* iio-rescale to reflect 1:1 scaling */ + scap_junc_1: an-scap-junc-1 { + compatible = "iio-rescale"; + io-channels = <&silo_wizard_adc 3>; + io-channel-names = "parent"; + label = "silo-scap-junc-1"; + + numerator = <1>; + denominator = <1>; + type = "voltage"; + + #io-channel-cells = <0>; + }; + + scap_vchrg_mv: an-scap-vchrg-mv { + compatible = "voltage-divider"; + io-channels = <&silo_wizard_adc 4>; + label = "scap-vchrg-mv"; + io-channel-names = "parent"; + output-ohms = <75000>; + full-ohms = <150000>; + #io-channel-cells = <0>; + }; + + silo_reg_5v: an-silo-reg-5v { + compatible = "voltage-divider"; + io-channels = <&silo_wizard_adc 5>; + label = "silo-reg-5v"; + output-ohms = <75000>; + full-ohms = <150000>; + #io-channel-cells = <0>; + }; + + silo_vin: an-silo-vin { + compatible = "voltage-divider"; + io-channels = <&silo_wizard_adc 6>; + label = "silo-vin"; + output-ohms = <4990>; + full-ohms = <79990>; + #io-channel-cells = <0>; + }; + + /* Total system current draw */ + silo_sbc_current: an-silo-sbc-current { + compatible = "current-sense-amplifier"; + io-channels = <&silo_wizard_adc 7>; + label = "system-current"; + #io-channel-cells = <0>; + + /* Effective shunt: R43 || R44 = 50 mOhm */ + sense-resistor-micro-ohms = <50000>; /* 50 milliohm */ + + /* VSENSE->VOUT gain: 0.004 * (8.66k || 6.04k) ~= 14.233 V/V */ + sense-gain-mult = <14233>; + sense-gain-div = <1000>; + }; + }; + }; +}; + diff --git a/arch/arm/boot/dts/nxp/imx/imx6ul-ts7250v3.dtsi b/arch/arm/boot/dts/nxp/imx/imx6ul-ts7250v3.dtsi index 3d113bd5e26ce..f066dd55ff54a 100644 --- a/arch/arm/boot/dts/nxp/imx/imx6ul-ts7250v3.dtsi +++ b/arch/arm/boot/dts/nxp/imx/imx6ul-ts7250v3.dtsi @@ -158,7 +158,6 @@ wizard: wizard@10 { compatible = "technologic,wizard"; reg = <0x10>; - status = "disabled"; wizard_adc: wizard_adc { compatible = "technologic,wizard-adc"; diff --git a/arch/arm/configs/tsimx6ul_defconfig b/arch/arm/configs/tsimx6ul_defconfig index 40fab3d662bab..2245743c9dd95 100644 --- a/arch/arm/configs/tsimx6ul_defconfig +++ b/arch/arm/configs/tsimx6ul_defconfig @@ -686,6 +686,8 @@ CONFIG_POWER_RESET=y CONFIG_POWER_RESET_TS_WIZARD=y CONFIG_POWER_RESET_SYSCON=y CONFIG_POWER_RESET_SYSCON_POWEROFF=y +CONFIG_POWER_SUPPLY=y +CONFIG_TSSILO_SUPERCAPS=y CONFIG_SENSORS_IIO_HWMON=y CONFIG_THERMAL=y CONFIG_THERMAL_STATISTICS=y diff --git a/arch/arm/configs/tsimx6ul_minimal_defconfig b/arch/arm/configs/tsimx6ul_minimal_defconfig index a617c752ec43d..b8c54c848065a 100644 --- a/arch/arm/configs/tsimx6ul_minimal_defconfig +++ b/arch/arm/configs/tsimx6ul_minimal_defconfig @@ -214,6 +214,8 @@ CONFIG_POWER_RESET=y CONFIG_POWER_RESET_TS_WIZARD=y CONFIG_POWER_RESET_SYSCON=y CONFIG_POWER_RESET_SYSCON_POWEROFF=y +CONFIG_POWER_SUPPLY=y +CONFIG_TSSILO_SUPERCAPS=y CONFIG_SENSORS_IIO_HWMON=y CONFIG_THERMAL=y CONFIG_THERMAL_STATISTICS=y diff --git a/drivers/iio/adc/tswizard_adc.c b/drivers/iio/adc/tswizard_adc.c index 4cbed7874f8a0..736594878298d 100644 --- a/drivers/iio/adc/tswizard_adc.c +++ b/drivers/iio/adc/tswizard_adc.c @@ -142,23 +142,24 @@ static int ts_wizard_adc_probe(struct platform_device *pdev) indio_dev->num_channels = adc->channel_count; indio_dev->channels = tswizard_channels; - indio_dev->name = dev_name(&pdev->dev); + indio_dev->name = (pdev->dev.of_node && pdev->dev.of_node->name) ? pdev->dev.of_node->name: dev_name(&pdev->dev); indio_dev->dev.of_node = pdev->dev.of_node; indio_dev->info = &ts_adc_info; return devm_iio_device_register(&pdev->dev, indio_dev); } -static const struct of_device_id tswizard_of_match[] = { +static const struct of_device_id tswizard_adc_of_match[] = { { .compatible = "technologic,tswizard-adc", }, + { .compatible = "technologic,wizard-adc", }, { } }; -MODULE_DEVICE_TABLE(of, tsadc_of_match); +MODULE_DEVICE_TABLE(of, tswizard_adc_of_match); static struct platform_driver tsadc_driver = { .driver = { .name = "tswizard-adc", - .of_match_table = tswizard_of_match, + .of_match_table = tswizard_adc_of_match, }, .probe = ts_wizard_adc_probe, }; diff --git a/drivers/mfd/tswizard-core.c b/drivers/mfd/tswizard-core.c index d54c9b7df551f..d9a92c7ca415c 100644 --- a/drivers/mfd/tswizard-core.c +++ b/drivers/mfd/tswizard-core.c @@ -10,30 +10,61 @@ #include #define MODEL_TS_7250_V3 0x7250 +#define MODEL_TS_SILO_104 0x5104 static struct mfd_cell tswizard_devs[] = { { .name = "tswizard-reset", - .id = -1, + .of_compatible = "technologic,wizard-reset", + .id = PLATFORM_DEVID_AUTO, }, { .name = "tswizard-temp", .of_compatible = "technologic,wizard-temp", - .id = -1, + .id = PLATFORM_DEVID_AUTO, }, { .name = "tswizard-adc", .of_compatible = "technologic,wizard-adc", - .id = -1, + .id = PLATFORM_DEVID_AUTO, } }; +static struct mfd_cell silo104_devs[] = { + { + .name = "tswizard-irq", + .of_compatible = "technologic,wizard-irq", + .id = PLATFORM_DEVID_AUTO, + }, + { + .name = "tswizard-reset", + .of_compatible = "technologic,wizard-reset", + .id = PLATFORM_DEVID_AUTO, + }, + { + .name = "tswizard-silo", + .of_compatible = "technologic,wizard-silo", + .id = PLATFORM_DEVID_NONE, + }, + { + .name = "tswizard-temp", + .of_compatible = "technologic,wizard-temp", + .id = PLATFORM_DEVID_AUTO, + }, + { + .name = "tswizard-adc", + .of_compatible = "technologic,wizard-adc", + .id = PLATFORM_DEVID_AUTO, + }, +}; + static const struct regmap_range ts_wizard_read_regs[] = { regmap_reg_range(0, 3), /* model/version/advertisements */ regmap_reg_range(16, 16), /* flags */ regmap_reg_range(24, 24), /* inputs */ regmap_reg_range(32, 32), /* reboot_reason */ regmap_reg_range(34, 37), /* serial */ + regmap_reg_range(WIZ_SILO_BASE, WIZ_SILO_BASE +16), /* silo regs (64-80)*/ regmap_reg_range(128, 160), /* ADCs+temp */ }; @@ -41,6 +72,10 @@ static const struct regmap_range ts_wizard_write_regs[] = { regmap_reg_range(8, 8), /* cmds */ regmap_reg_range(16, 16), /* flags */ regmap_reg_range(34, 37), /* serial */ + regmap_reg_range(WIZ_SILO_BASE +2, WIZ_SILO_BASE +2), /* silo control (66)*/ + regmap_reg_range(WIZ_SILO_BASE +4, WIZ_SILO_BASE +4), /* silo req chg current (68) */ + regmap_reg_range(WIZ_SILO_BASE +9, WIZ_SILO_BASE +9), /* CRITICAL_PCT (base+9) */ + regmap_reg_range(WIZ_SILO_BASE +12, WIZ_SILO_BASE +13), /* startup current (76), min pwr on pct (77) */ }; const struct regmap_access_table ts_wizard_read_register_set = { @@ -64,7 +99,7 @@ const struct regmap_config ts_wizard_i2c_regmap = { .rd_table = &ts_wizard_read_register_set, .volatile_table = &ts_wizard_read_register_set, - .disable_locking = true, + .disable_locking = false, .cache_type = REGCACHE_NONE, }; EXPORT_SYMBOL_GPL(ts_wizard_i2c_regmap); @@ -253,7 +288,7 @@ static int ts_wizard_i2c_probe(struct i2c_client *client) { struct ts_wizard *wiz; struct device *dev = &client->dev; - int err = 0, i; + int err = 0, i, j; uint32_t model, revision, features; wiz = devm_kzalloc(dev, sizeof(struct ts_wizard), @@ -297,15 +332,24 @@ static int ts_wizard_i2c_probe(struct i2c_client *client) dev_warn(dev, "error creating sysfs entries\n"); } + if (model == MODEL_TS_SILO_104) { + err = mfd_add_devices(dev, PLATFORM_DEVID_AUTO, silo104_devs, + ARRAY_SIZE(silo104_devs), NULL, 0, NULL); + if (err) { + dev_err(dev, "Failed to add SILO104 devices: %d\n", err); + } + return err; + } + /* Set up and register the platform devices. */ for (i = 0; i < ARRAY_SIZE(tswizard_devs); i++) { tswizard_devs[i].platform_data = wiz; tswizard_devs[i].pdata_size = sizeof(struct ts_wizard); } - return mfd_add_devices(dev, 0, tswizard_devs, + return mfd_add_devices(dev, PLATFORM_DEVID_AUTO, tswizard_devs, ARRAY_SIZE(tswizard_devs), NULL, 0, NULL); -} +}; static const struct i2c_device_id ts_wizard_i2c_id[] = { { "tswizard", 0 }, diff --git a/drivers/power/supply/Kconfig b/drivers/power/supply/Kconfig index a61bb1283e197..46578e6203c4f 100644 --- a/drivers/power/supply/Kconfig +++ b/drivers/power/supply/Kconfig @@ -952,4 +952,13 @@ config CHARGER_QCOM_SMB2 adds support for the SMB2 switch mode battery charger found in PMI8998 and related PMICs. +config TSSILO_SUPERCAPS + tristate "embeddedTS SILO supercaps driver" + depends on I2C + select REGMAP_I2C + help + Say Y here to enable the embeddedTS TS-SILO supercapacitor driver. + This adds support for the TS-SILO supercapacitor unexpected shutdown + protection device. + endif # POWER_SUPPLY diff --git a/drivers/power/supply/Makefile b/drivers/power/supply/Makefile index a8a9fa6de1e9a..bab9dba91424d 100644 --- a/drivers/power/supply/Makefile +++ b/drivers/power/supply/Makefile @@ -111,3 +111,5 @@ obj-$(CONFIG_BATTERY_SURFACE) += surface_battery.o obj-$(CONFIG_CHARGER_SURFACE) += surface_charger.o obj-$(CONFIG_BATTERY_UG3105) += ug3105_battery.o obj-$(CONFIG_CHARGER_QCOM_SMB2) += qcom_pmi8998_charger.o +obj-$(CONFIG_TSSILO_SUPERCAPS) += tssilo_supercaps.o + diff --git a/drivers/power/supply/tssilo_supercaps.c b/drivers/power/supply/tssilo_supercaps.c new file mode 100644 index 0000000000000..8ee6009e28bef --- /dev/null +++ b/drivers/power/supply/tssilo_supercaps.c @@ -0,0 +1,367 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Supercaps driver for the embeddedTS SILO controller + * Copyright (C) 2024-2025 Technologic Systems, Inc. dba embeddedTS + */ + +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#include +#include +#include + +#define SILO_RESERVED0 (WIZ_SILO_BASE + 0) +#define SILO_STATUS (WIZ_SILO_BASE + 1) +#define SILO_CONTROL (WIZ_SILO_BASE + 2) + +#define SILO_REQUESTED_CHG_CURRENT_MA (WIZ_SILO_BASE + 4) +#define SILO_MAX_SUPPORTED_CHRG_CURRENT_MA (WIZ_SILO_BASE + 5) + +#define SILO_PCT_CHARGED (WIZ_SILO_BASE + 8) +#define SILO_CRITICAL_PCT (WIZ_SILO_BASE + 9) + +#define SILO_STARTUP_REQUESTED_CHG_CURRENT_MA (WIZ_SILO_BASE + 12) +#define SILO_MIN_PWR_ON_PCT (WIZ_SILO_BASE + 13) + +#define SILO_STATUS_CHARGING BIT(0) +#define SILO_STATUS_PWR_FAIL BIT(15) +#define SILO_STATUS_MODE_MASK 0x3E +#define SILO_STATUS_MODE_SHIFT 1 + +#define SILO_STATUS_MODE_DISABLED 0 +#define SILO_STATUS_MODE_CHARGING 1 +#define SILO_STATUS_MODE_FULL 2 +#define SILO_STATUS_MODE_DISCHARGING 3 + +#define SILO_CONTROL_CHRG_EN BIT(0) +#define SILO_CONTROL_PWRUP BIT(1) + +struct silo_data { + struct regmap *regmap; + struct power_supply *psy; +}; + +static int get_pct_charged(struct silo_data *data) +{ + int ret; + unsigned int val; + + ret = regmap_read(data->regmap, SILO_PCT_CHARGED, &val); + if (ret) { + dev_err(&data->psy->dev, "%s failed from regmap_read (rc=%d)\n", __func__, ret); + return ret; + } + return val; +} + +static ssize_t startup_charge_current_ma_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + struct silo_data *data = dev_get_drvdata(dev); + int ret, val; + + ret = regmap_read(data->regmap, SILO_STARTUP_REQUESTED_CHG_CURRENT_MA, &val); + if (ret) + return ret; + return sprintf(buf, "%d\n", val); +} + +static ssize_t startup_charge_current_ma_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + struct silo_data *data = dev_get_drvdata(dev); + unsigned int max; + int ret; + int val; + + ret = kstrtoint(buf, 10, &val); + if (ret) + return ret; + + ret = regmap_read(data->regmap, SILO_MAX_SUPPORTED_CHRG_CURRENT_MA, &max); + if (ret) + return ret; + if (val < 0 || val > max) + return -EINVAL; + + ret = regmap_write(data->regmap, SILO_STARTUP_REQUESTED_CHG_CURRENT_MA, val); + if (ret) + return ret; + + power_supply_changed(data->psy); + + return count; +} + +static ssize_t min_power_on_pct_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + struct silo_data *data = dev_get_drvdata(dev); + unsigned int val; + int ret; + + ret = regmap_read(data->regmap, SILO_MIN_PWR_ON_PCT, &val); + if (ret) + return ret; + + return sprintf(buf, "%d\n", val); +} + +static ssize_t min_power_on_pct_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + struct silo_data *data = dev_get_drvdata(dev); + unsigned int val; + int ret; + + ret = kstrtoint(buf, 10, &val); + if (ret) + return ret; + + if (val > 100) + return -EINVAL; + + ret = regmap_write(data->regmap, SILO_MIN_PWR_ON_PCT, val); + if (ret) + return ret; + + power_supply_changed(data->psy); + + return count; +} + +static DEVICE_ATTR_RW(startup_charge_current_ma); +static DEVICE_ATTR_RW(min_power_on_pct); + +/* + * Currently these are our properties that do not map to any standard + * power supply properties. + */ +static struct attribute *silo_attrs[] = { + &dev_attr_startup_charge_current_ma.attr, + &dev_attr_min_power_on_pct.attr, + NULL, +}; + +static const struct attribute_group silo_attr_group = { + .attrs = silo_attrs, +}; + +static int silo_property_is_writable(struct power_supply *psy, + enum power_supply_property psp) +{ + switch (psp) { + case POWER_SUPPLY_PROP_CHARGE_BEHAVIOUR: + case POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT: + case POWER_SUPPLY_PROP_CAPACITY_ALERT_MIN: + return 1; + default: + break; + } + return 0; +} + +static enum power_supply_property silo_props[] = { + POWER_SUPPLY_PROP_ONLINE, + POWER_SUPPLY_PROP_STATUS, + POWER_SUPPLY_PROP_CAPACITY, + POWER_SUPPLY_PROP_CHARGE_BEHAVIOUR, + POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT, + POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT_MAX, + POWER_SUPPLY_PROP_CAPACITY_ALERT_MIN +}; + +static int silo_get_property(struct power_supply *psy, + enum power_supply_property psp, + union power_supply_propval *val) +{ + unsigned int reg; + int ret; + + struct silo_data *data = power_supply_get_drvdata(psy); + + switch (psp) { + case POWER_SUPPLY_PROP_CHARGE_BEHAVIOUR: + ret = regmap_test_bits(data->regmap, SILO_CONTROL, SILO_CONTROL_CHRG_EN); + if (ret < 0) + return ret; + val->intval = (ret ? POWER_SUPPLY_CHARGE_BEHAVIOUR_AUTO : POWER_SUPPLY_CHARGE_BEHAVIOUR_INHIBIT_CHARGE); + return 0; + case POWER_SUPPLY_PROP_ONLINE: + ret = regmap_read(data->regmap, SILO_STATUS, ®); + val->intval = !(reg & SILO_STATUS_PWR_FAIL); + return ret; + case POWER_SUPPLY_PROP_CAPACITY: + ret = get_pct_charged(data); + if (ret < 0) + return ret; + val->intval = ret; + return 0; + case POWER_SUPPLY_PROP_CAPACITY_ALERT_MIN: + return regmap_read(data->regmap, SILO_CRITICAL_PCT, &val->intval); + case POWER_SUPPLY_PROP_STATUS: + ret = regmap_read(data->regmap, SILO_STATUS, ®); + reg = (reg & SILO_STATUS_MODE_MASK) >> SILO_STATUS_MODE_SHIFT; + + switch (reg) { + case SILO_STATUS_MODE_DISABLED: + val->intval = POWER_SUPPLY_STATUS_NOT_CHARGING; + break; + case SILO_STATUS_MODE_CHARGING: + val->intval = POWER_SUPPLY_STATUS_CHARGING; + break; + case SILO_STATUS_MODE_FULL: + val->intval = POWER_SUPPLY_STATUS_FULL; + break; + case SILO_STATUS_MODE_DISCHARGING: + val->intval = POWER_SUPPLY_STATUS_DISCHARGING; + break; + default: + val->intval = POWER_SUPPLY_STATUS_UNKNOWN; + break; + } + return 0; + case POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT: + return regmap_read(data->regmap, SILO_REQUESTED_CHG_CURRENT_MA, &val->intval); + case POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT_MAX: + return regmap_read(data->regmap, SILO_MAX_SUPPORTED_CHRG_CURRENT_MA, &val->intval); + default: + return -EINVAL; + } + return -ENODATA; +} + +static int silo_set_property(struct power_supply *psy, + enum power_supply_property psp, + const union power_supply_propval *val) +{ + struct silo_data *data = power_supply_get_drvdata(psy); + unsigned int value; + int ret; + + if (psp == POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT) { + ret = regmap_read(data->regmap, SILO_MAX_SUPPORTED_CHRG_CURRENT_MA, &value); + if (ret) + return ret; + if (val->intval < 0 || val->intval > value) + return -EINVAL; + ret = regmap_write(data->regmap, SILO_REQUESTED_CHG_CURRENT_MA, val->intval); + } else if (psp == POWER_SUPPLY_PROP_CAPACITY_ALERT_MIN) { + ret = regmap_write(data->regmap, SILO_CRITICAL_PCT, val->intval); + } else if (psp == POWER_SUPPLY_PROP_CHARGE_BEHAVIOUR) { + value = ((val->intval == POWER_SUPPLY_CHARGE_BEHAVIOUR_AUTO) ? SILO_CONTROL_CHRG_EN : 0); + ret = regmap_update_bits(data->regmap, SILO_CONTROL, + SILO_CONTROL_CHRG_EN, value); + } else { + return -EINVAL; + } + + if (!ret) + power_supply_changed(data->psy); + + return ret; +} + +static const struct power_supply_desc silo_desc = { + .name = "silo", + .type = POWER_SUPPLY_TYPE_UPS, + .properties = silo_props, + .num_properties = ARRAY_SIZE(silo_props), + .get_property = silo_get_property, + .set_property = silo_set_property, + .property_is_writeable = silo_property_is_writable, + .no_thermal = true, +}; + +static irqreturn_t silo_irq_handler(int irq, void *dev_id) +{ + struct silo_data *data = dev_id; + power_supply_changed(data->psy); + return IRQ_HANDLED; +} + +static int ts_silo_probe(struct platform_device *pdev) +{ + struct ts_wizard *wizard = dev_get_drvdata(pdev->dev.parent); + struct device *dev = &pdev->dev; + struct silo_data *data; + struct power_supply_config psy_cfg = {}; + int ret; + int irq; + unsigned int version; + + data = devm_kzalloc(dev, sizeof(*data), GFP_KERNEL); + data->regmap = wizard->regmap; + platform_set_drvdata(pdev, data); + + irq = platform_get_irq(pdev, 0); + if (irq < 0) + return irq; + + psy_cfg.drv_data = data; + data->psy = devm_power_supply_register(dev, &silo_desc, &psy_cfg); + if (IS_ERR(data->psy)) { + dev_err(dev, "devm_power_supply_register failed (rc=%pe)", data->psy); + return PTR_ERR(data->psy); + } + + ret = sysfs_create_group(&dev->kobj, &silo_attr_group); + if (ret) { + dev_err(dev, "sysfs_create_group failed (rc=%d)\n", ret); + return ret; + } + + ret = devm_request_threaded_irq(dev, irq, + NULL, silo_irq_handler, + IRQF_ONESHOT, dev_name(dev), data); + if (ret) + return ret; + + ret = regmap_read(data->regmap, SILO_RESERVED0, &version); + if (ret < 0) + return ret; + dev_info(dev, "TS-SILO version %d\n", version); + if (version < 2) + dev_warn(dev, "POWER_FAIL ignored without a Wizard interrupt controller.\n"); + return 0; +} + +static int ts_silo_remove(struct platform_device *pdev) +{ + sysfs_remove_group(&pdev->dev.kobj, &silo_attr_group); + return 0; +} + +static const struct of_device_id silo_of_match[] = { + { .compatible = "technologic,wizard-silo", }, + {} +}; +MODULE_DEVICE_TABLE(of, silo_of_match); + +static struct platform_driver silo_driver = { + .driver = { + .name = "silo", + .of_match_table = silo_of_match, + }, + .probe = ts_silo_probe, + .remove = ts_silo_remove, +}; + +module_platform_driver(silo_driver); + +MODULE_DESCRIPTION("embeddedTS SILO supercaps driver"); +MODULE_AUTHOR("Lionel D. Hummel "); +MODULE_LICENSE("GPL"); diff --git a/include/linux/mfd/ts_wizard.h b/include/linux/mfd/ts_wizard.h index 6ce7b173249b2..123a1f93a968e 100644 --- a/include/linux/mfd/ts_wizard.h +++ b/include/linux/mfd/ts_wizard.h @@ -23,9 +23,12 @@ struct ts_wizard { #define WIZ_SERIAL1 35 #define WIZ_SERIAL2 36 #define WIZ_SERIAL_CTRL 37 +#define WIZ_SILO_BASE 64 #define WIZ_ADC_BASE 128 #define WIZ_ADC_LAST 159 #define WIZ_TEMPERATURE 160 +#define WIZ_CURRENT 161 +#define WIZ_IRQCHIP_BASE 512 enum gen_flags_t { FLG_FORCE_USB_CON = (1 << 4), @@ -39,6 +42,10 @@ enum gen_inputs_t { }; enum wiz_features_t { + WIZ_FEAT_CT = BIT(6), // Channel Table visible + WIZ_FEAT_SILO = BIT(5), + WIZ_FEAT_BOOT_MODE = BIT(4), + WIZ_FEAT_RBTR = BIT(3), // TBI on i.MX93 WIZ_FEAT_SN = (1 << 2), WIZ_FEAT_FWUPD = (1 << 1), WIZ_FEAT_RSTC = (1 << 0), @@ -57,6 +64,8 @@ enum reboot_reasons_t { }; enum wiz_cmds_t { + I2C_CMD_RESERVED3 = BIT(3), + I2C_CMD_RESERVED2 = BIT(2), I2C_REBOOT = (1 << 0), I2C_HALT = (1 << 1), };