Re: [PATCH v5] power_supply: Add support for Richtek rt9455 battery charger
From: Sebastian Reichel <sre@kernel.org>
Date: 2015-05-24 11:02:32
Also in:
linux-pm, lkml
Hi Anda-Maria, On Fri, May 08, 2015 at 03:57:49PM +0300, Anda-Maria Nicolae wrote:
Based on the datasheet found here: http://www.richtek.com/download_ds.jsp?p=RT9455 Signed-off-by: Anda-Maria Nicolae <redacted> --- Updates from v4 version: - replaced depends on REGMAP_I2C with select REGMAP_I2C - got the latest version of vendor_prefixes file; prev patch contained an older one - added explanation in rt9455_charger.txt about what boost-output-voltage represents .../devicetree/bindings/power/rt9455_charger.txt | 46 +
Please move this into its own patch, see Documentation/devicetree/bindings/submitting-patches.txt
.../devicetree/bindings/vendor-prefixes.txt | 1 +
Please also move this into its own patch
quoted hunk ↗ jump to hunk
drivers/power/Kconfig | 7 + drivers/power/Makefile | 1 + drivers/power/rt9455_charger.c | 1821 ++++++++++++++++++++ 5 files changed, 1876 insertions(+) create mode 100644 Documentation/devicetree/bindings/power/rt9455_charger.txt create mode 100644 drivers/power/rt9455_charger.cdiff --git a/Documentation/devicetree/bindings/power/rt9455_charger.txt b/Documentation/devicetree/bindings/power/rt9455_charger.txt new file mode 100644 index 0000000..a87c0f5 --- /dev/null +++ b/Documentation/devicetree/bindings/power/rt9455_charger.txt@@ -0,0 +1,46 @@ +Binding for Richtek rt9455 battery charger + +Required properties: +- compatible: Should contain one of the following: + * "richtek,rt9455" + +- reg: integer, i2c address of the device. +- richtek,output-charge-current: integer, output current from the charger to the + battery, in uA. +- richtek,end-of-charge-percentage: integer, percent of the output charge current. + When the current in constant-voltage phase drops + below output_charge_current x end-of-charge-percentage, + charge is terminated. +- richtek,battery-regulation-voltage: integer, maximum battery voltage in uV. +- richtek,boost-output-voltage: integer, maximum voltage provided to consumer + devices, when the charger is in boost mode. + +Optional properties: +- richtek,min-input-voltage-regulation: integer, input voltage level in uA, used to + decrease voltage level when the over current + of the input power source occurs. + This prevents input voltage drop due to insufficient + current provided by the power source. +- richtek,avg-input-current-regulation: integer, input current value drained by the + charger from the power source. + +Example: + +rt9455@22 { + compatible = "richtek,rt9455"; + reg = <0x22>; + + interrupt-parent = <&gpio1>; + interrupts = <0 1>; + + interrupt-gpio = <&gpio1 0 1>; + reset-gpio = <&gpio1 1 1>;
Neither the gpios, nor the interrupts are specified as required/optional properties. Also the reset gpio is not referenced in the sourcecode at all.
+ richtek,output-charge-current = <500000>;
+ richtek,end-of-charge-percentage = <10>;
+ richtek,battery-regulation-voltage = <4200000>;
+ richtek,boost-output-voltage = <5050000>;
+
+ richtek,min-input-voltage-regulation = <4500000>;
+ richtek,avg-input-current-regulation = <500000>;
+};
[...]
+/*
+ * Before setting the charger into boost mode, boost output voltage is
+ * set. This is needed because boost output voltage may differ from battery
+ * regulation voltage. F_VOREG bits represent either battery regulation voltage
+ * or boost output voltage, depending on the mode the charger is. Both battery
+ * regulation voltage and boost output voltage are read from DT/ACPI during
+ * probe.
+ */
+static int rt9455_set_boost_voltage_before_boost_mode(struct rt9455_info *info)
+{
+ struct device *dev = &info->client->dev;
+ int curr_boost_voltage, ret;
+
+ ret = rt9455_get_field_val(info, F_VOREG,
+ rt9455_boost_voltage_values,
+ ARRAY_SIZE(rt9455_boost_voltage_values),
+ &curr_boost_voltage);
+ if (ret) {
+ dev_err(dev, "Failed to read boost output voltage value\n");
+ return ret;
+ }
+
+ if (curr_boost_voltage != info->boost_voltage) {
+ ret = rt9455_set_field_val(info, F_VOREG,
+ rt9455_boost_voltage_values,
+ ARRAY_SIZE(rt9455_boost_voltage_values),
+ info->boost_voltage);
+ if (ret) {
+ dev_err(dev, "Failed to set boost output voltage value\n");
+ return ret;
+ }
+ }
+
+ return 0;
+}The register comparision looks over-engineered to me. Just write the new value into the register.
+/*
+ * Before setting the charger into charge mode, battery regulation voltage is
+ * set. This is needed because boost output voltage may differ from battery
+ * regulation voltage. F_VOREG bits represent either battery regulation voltage
+ * or boost output voltage, depending on the mode the charger is. Both battery
+ * regulation voltage and boost output voltage are read from DT/ACPI during
+ * probe.
+ */
+static int rt9455_set_voreg_before_charge_mode(struct rt9455_info *info)
+{
+ struct device *dev = &info->client->dev;
+ int curr_voreg, ret;
+
+ ret = rt9455_get_field_val(info, F_VOREG,
+ rt9455_voreg_values,
+ ARRAY_SIZE(rt9455_voreg_values),
+ &curr_voreg);
+ if (ret) {
+ dev_err(dev, "Failed to read VOREG value\n");
+ return ret;
+ }
+
+ if (curr_voreg != info->voreg) {
+ ret = rt9455_set_field_val(info, F_VOREG,
+ rt9455_voreg_values,
+ ARRAY_SIZE(rt9455_voreg_values),
+ info->voreg);
+ if (ret) {
+ dev_err(dev, "Failed to set VOREG value\n");
+ return ret;
+ }
+ }
+
+ return 0;
+}see last comment
[...]
+static int rt9455_setup_irq(struct rt9455_info *info)
+{
+ struct device *dev = &info->client->dev;
+ int ret;
+
+ /* Obtain IRQ GPIO */
+ info->gpiod_irq = devm_gpiod_get_index(dev, RT9455_IRQ_NAME, 0);
+ if (IS_ERR(info->gpiod_irq)) {
+ dev_err(dev, "Failed to get IRQ GPIO\n");
+ return PTR_ERR(info->gpiod_irq);
+ }
+
+ /* Configure IRQ GPIO */
+ ret = gpiod_direction_input(info->gpiod_irq);
+ if (ret) {
+ dev_err(dev, "Failed to set IRQ GPIO direction err = %d\n",
+ ret);
+ return ret;
+ }
+
+ /* Map the pin to an IRQ */
+ ret = gpiod_to_irq(info->gpiod_irq);
+ if (ret < 0) {
+ dev_err(dev, "Failed to associate GPIO with an IRQ err = %d\n",
+ ret);
+ return ret;
+ }
+
+ dev_dbg(dev, "GPIO resource, no:%d irq:%d\n",
+ desc_to_gpio(info->gpiod_irq), ret);
+ info->client->irq = ret;
+
+ info->gpio_irq = desc_to_gpio(info->gpiod_irq);info->gpio_irq is never referenced. Drop it.
+ return 0; +}
There is no usage of info->gpiod_irq except for irq conversion, so you can just drop the whole gpio usage and reference the gpio as irq in the DT specification: interrupts-parent = <&gpio42>; interrupts = <23 IRQ_TYPE_LEVEL_HIGH>; This way the irq will be assigned to client->irq automatically. For ACPI the same will be supported once this patchset is merged: https://lkml.org/lkml/2015/4/28/455
[...]
+static int rt9455_probe(struct i2c_client *client,
+ const struct i2c_device_id *id)
+{
+ struct i2c_adapter *adapter = to_i2c_adapter(client->dev.parent);
+ struct device *dev = &client->dev;
+ struct rt9455_info *info;
+ struct power_supply_config rt9455_charger_config = {};
+ /*
+ * Mandatory device-specific data values. Also, VOREG and boost output
+ * voltage are mandatory values, but they are stored in rt9455_info
+ * structure.
+ */
+ u32 ichrg, ieoc_percentage;
+ /* Optional device-specific data values. */
+ u32 mivr = -1, iaicr = -1;
+ int i, ret;
+
+ if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA)) {
+ dev_err(dev, "No support for SMBUS_BYTE_DATA\n");
+ return -ENODEV;
+ }
+ info = devm_kzalloc(dev, sizeof(*info), GFP_KERNEL);
+ if (!info)
+ return -ENOMEM;
+
+ info->client = client;
+ i2c_set_clientdata(client, info);
+
+ info->regmap = devm_regmap_init_i2c(client,
+ &rt9455_regmap_config);
+ if (IS_ERR(info->regmap)) {
+ dev_err(dev, "Failed to initialize register map\n");
+ return -EINVAL;
+ }
+
+ for (i = 0; i < F_MAX_FIELDS; i++) {
+ info->regmap_fields[i] =
+ devm_regmap_field_alloc(dev, info->regmap,
+ rt9455_reg_fields[i]);
+ if (IS_ERR(info->regmap_fields[i])) {
+ dev_err(dev,
+ "Failed to allocate regmap field = %d\n", i);
+ return PTR_ERR(info->regmap_fields[i]);
+ }
+ }
+
+ ret = rt9455_discover_charger(info, &ichrg, &ieoc_percentage,
+ &mivr, &iaicr);
+ if (ret) {
+ dev_err(dev, "Failed to discover charger\n");
+ return ret;
+ }
+
+#if IS_ENABLED(CONFIG_USB_PHY)
+ info->usb_phy = devm_usb_get_phy(dev, USB_PHY_TYPE_USB2);
+ if (IS_ERR(info->usb_phy)) {
+ dev_err(dev, "Failed to get USB transceiver\n");
+ } else {
+ info->nb.notifier_call = rt9455_usb_event;
+ ret = usb_register_notifier(info->usb_phy, &info->nb);
+ if (ret) {
+ dev_err(dev, "Failed to register USB notifier\n");
+ /*
+ * If usb_register_notifier() fails, set notifier_call
+ * to NULL, to avoid calling usb_unregister_notifier().
+ */
+ info->nb.notifier_call = NULL;
+ }
+ }
+#endif
+
+ INIT_DEFERRABLE_WORK(&info->pwr_rdy_work, rt9455_pwr_rdy_work_callback);
+ INIT_DEFERRABLE_WORK(&info->max_charging_time_work,
+ rt9455_max_charging_time_work_callback);
+ INIT_DEFERRABLE_WORK(&info->batt_presence_work,
+ rt9455_batt_presence_work_callback);
+
+ rt9455_charger_config.of_node = dev->of_node;
+ rt9455_charger_config.drv_data = info;
+ rt9455_charger_config.supplied_to = rt9455_charger_supplied_to;
+ rt9455_charger_config.num_supplicants =
+ ARRAY_SIZE(rt9455_charger_supplied_to);
+ ret = devm_request_threaded_irq(dev, client->irq, NULL,
+ rt9455_irq_handler_thread,
+ IRQF_TRIGGER_LOW | IRQF_ONESHOT,
+ RT9455_DRIVER_NAME, info);
+ if (ret) {
+ dev_err(dev, "Failed to register IRQ handler\n");
+ goto put_usb_notifier;
+ }
+
+ ret = rt9455_hw_init(info, ichrg, ieoc_percentage, mivr, iaicr);
+ if (ret) {
+ dev_err(dev, "Failed to set charger to its default values\n");
+ goto put_usb_notifier;
+ }
+
+ info->charger = power_supply_register(dev, &rt9455_charger_desc,
+ &rt9455_charger_config);
+ if (IS_ERR(info->charger)) {
+ dev_err(dev, "Failed to register charger\n");
+ ret = PTR_ERR(info->charger);
+ goto put_usb_notifier;
+ }You can use devm_power_supply_register() and remove the power_supply_unregister() call from rt9455_remove().
+
+ return 0;
+
+put_usb_notifier:
+#if IS_ENABLED(CONFIG_USB_PHY)
+ if (info->nb.notifier_call) {
+ usb_unregister_notifier(info->usb_phy, &info->nb);
+ info->nb.notifier_call = NULL;
+ }
+#endif
+ return ret;
+}
[...]-- Sebastian
Attachments
- signature.asc [application/pgp-signature] 819 bytes