Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

platform/x86/intel: bytcrc_pwrsrc: Optionally register a power_supply dev

On some Android tablets with Crystal Cove PMIC the DSDT lacks an ACPI AC
device to indicate whether a charger is plugged in or not.

Add support for registering a "crystal_cove_pwrsrc" power_supply class
device to indicate charger online status. This is made conditional on
a "linux,register-pwrsrc-power_supply" boolean device-property to avoid
registering a duplicate power_supply class device on devices where this
is already handled by an ACPI AC device.

Note the "linux,register-pwrsrc-power_supply" property is only used on
x86/ACPI (non devicetree) devs and the devicetree-bindings maintainers
have requested properties like these to not be added to the devicetree
bindings, so the new property is deliberately not added to any bindings.

Reviewed-by: Andy Shevchenko <andy@kernel.org>
Signed-off-by: Hans de Goede <hdegoede@redhat.com>
Link: https://lore.kernel.org/r/20241204193442.65374-2-hdegoede@redhat.com
Reviewed-by: Ilpo Järvinen <ilpo.jarvinen@linux.intel.com>
Signed-off-by: Ilpo Järvinen <ilpo.jarvinen@linux.intel.com>

authored by

Hans de Goede and committed by
Ilpo Järvinen
0130ec83 c0f1bfc1

+77 -2
+77 -2
drivers/platform/x86/intel/bytcrc_pwrsrc.c
··· 8 8 * Copyright (C) 2013 Intel Corporation 9 9 */ 10 10 11 + #include <linux/array_size.h> 12 + #include <linux/bits.h> 11 13 #include <linux/debugfs.h> 14 + #include <linux/interrupt.h> 12 15 #include <linux/mfd/intel_soc_pmic.h> 13 16 #include <linux/module.h> 14 17 #include <linux/platform_device.h> 18 + #include <linux/power_supply.h> 19 + #include <linux/property.h> 15 20 #include <linux/regmap.h> 16 21 22 + #define CRYSTALCOVE_PWRSRC_IRQ 0x03 17 23 #define CRYSTALCOVE_SPWRSRC_REG 0x1E 24 + #define CRYSTALCOVE_SPWRSRC_USB BIT(0) 25 + #define CRYSTALCOVE_SPWRSRC_DC BIT(1) 26 + #define CRYSTALCOVE_SPWRSRC_BATTERY BIT(2) 18 27 #define CRYSTALCOVE_RESETSRC0_REG 0x20 19 28 #define CRYSTALCOVE_RESETSRC1_REG 0x21 20 29 #define CRYSTALCOVE_WAKESRC_REG 0x22 ··· 31 22 struct crc_pwrsrc_data { 32 23 struct regmap *regmap; 33 24 struct dentry *debug_dentry; 25 + struct power_supply *psy; 34 26 unsigned int resetsrc0; 35 27 unsigned int resetsrc1; 36 28 unsigned int wakesrc; ··· 128 118 return regmap_write(data->regmap, reg, *val); 129 119 } 130 120 121 + static irqreturn_t crc_pwrsrc_irq_handler(int irq, void *_data) 122 + { 123 + struct crc_pwrsrc_data *data = _data; 124 + unsigned int irq_mask; 125 + 126 + if (regmap_read(data->regmap, CRYSTALCOVE_PWRSRC_IRQ, &irq_mask)) 127 + return IRQ_NONE; 128 + 129 + regmap_write(data->regmap, CRYSTALCOVE_PWRSRC_IRQ, irq_mask); 130 + 131 + power_supply_changed(data->psy); 132 + return IRQ_HANDLED; 133 + } 134 + 135 + static int crc_pwrsrc_psy_get_property(struct power_supply *psy, 136 + enum power_supply_property psp, 137 + union power_supply_propval *val) 138 + { 139 + struct crc_pwrsrc_data *data = power_supply_get_drvdata(psy); 140 + unsigned int pwrsrc; 141 + int ret; 142 + 143 + if (psp != POWER_SUPPLY_PROP_ONLINE) 144 + return -EINVAL; 145 + 146 + ret = regmap_read(data->regmap, CRYSTALCOVE_SPWRSRC_REG, &pwrsrc); 147 + if (ret) 148 + return ret; 149 + 150 + val->intval = !!(pwrsrc & (CRYSTALCOVE_SPWRSRC_USB | 151 + CRYSTALCOVE_SPWRSRC_DC)); 152 + return 0; 153 + } 154 + 155 + static const enum power_supply_property crc_pwrsrc_psy_props[] = { 156 + POWER_SUPPLY_PROP_ONLINE, 157 + }; 158 + 159 + static const struct power_supply_desc crc_pwrsrc_psy_desc = { 160 + .name = "crystal_cove_pwrsrc", 161 + .type = POWER_SUPPLY_TYPE_MAINS, 162 + .properties = crc_pwrsrc_psy_props, 163 + .num_properties = ARRAY_SIZE(crc_pwrsrc_psy_props), 164 + .get_property = crc_pwrsrc_psy_get_property, 165 + }; 166 + 131 167 static int crc_pwrsrc_probe(struct platform_device *pdev) 132 168 { 133 169 struct intel_soc_pmic *pmic = dev_get_drvdata(pdev->dev.parent); 170 + struct device *dev = &pdev->dev; 134 171 struct crc_pwrsrc_data *data; 135 - int ret; 172 + int irq, ret; 136 173 137 - data = devm_kzalloc(&pdev->dev, sizeof(*data), GFP_KERNEL); 174 + data = devm_kzalloc(dev, sizeof(*data), GFP_KERNEL); 138 175 if (!data) 139 176 return -ENOMEM; 140 177 ··· 205 148 ret = crc_pwrsrc_read_and_clear(data, CRYSTALCOVE_WAKESRC_REG, &data->wakesrc); 206 149 if (ret) 207 150 return ret; 151 + 152 + if (device_property_read_bool(dev->parent, "linux,register-pwrsrc-power_supply")) { 153 + struct power_supply_config psy_cfg = { .drv_data = data }; 154 + 155 + irq = platform_get_irq(pdev, 0); 156 + if (irq < 0) 157 + return irq; 158 + 159 + data->psy = devm_power_supply_register(dev, &crc_pwrsrc_psy_desc, &psy_cfg); 160 + if (IS_ERR(data->psy)) 161 + return dev_err_probe(dev, PTR_ERR(data->psy), "registering power-supply\n"); 162 + 163 + ret = devm_request_threaded_irq(dev, irq, NULL, 164 + crc_pwrsrc_irq_handler, 165 + IRQF_ONESHOT, KBUILD_MODNAME, data); 166 + if (ret) 167 + return dev_err_probe(dev, ret, "requesting IRQ\n"); 168 + } 208 169 209 170 data->debug_dentry = debugfs_create_dir(KBUILD_MODNAME, NULL); 210 171 debugfs_create_file("pwrsrc", 0444, data->debug_dentry, data, &pwrsrc_fops);