On Thu, Jun 23, 2016 at 5:42 AM, Srinivas Pandruvada <srinivas.pandruvada@xxxxxxxxxxxxxxx> wrote: > This driver adds support for Dynamic Platform and Thermal Framework > (DPTF) Platform Power Participant device support. > This participant is responsible for exposing platform telemetry such as > platform Power, battery Information such as state of Charge, estimated > maximum sustainable power (PMax), SMART battery spec information. > > This driver is implemented as a platform driver for INT3407 and presented > as power_supply device. Since this has common features with the ACPI > battery, existing interface provide by battery_common driver are reused > to present as a battery power supply device. > > Since this driver will also register a battery power supply device,this > driver will exit if CONFIG_ACPI_BATTERY is defined. > > There are two types of objects in INT3407: > - Same as ACPI Battery objects: _BST and _BIX. These are exposed as > standard power supply attributes. > - Specific to INT3407, which are related to platform power > There are some objects, for which it doesn't make sense to enhance > power_supply class and add attributes there. They are represented as > sysfs attribute under acpi device. The below part of the changelog should really go to a doc under Documentation/. > Here the bid for INT3407 is TPWR > in the following example. > /sys/class/power_supply/TPWR > ├── alarm > ├── capacity > ├── capacity_level > ├── cycle_count > ├── device -> ../../../INT3407:00 > │ ├── dptf_power > │ │ ├── adapter_rating > │ │ ├── battery_steady_power > │ │ ├── charger_type > │ │ ├── max_platform_power > │ │ ├── platform_power_source > │ │ ├── power_sampling_period > > For example representing AC/adapter properties as a power_supply > properties will not make sense for a battery device. In some case > when there is an existing property, the meaning is different. > For example charger_type has a equivalent power_supply property, > which has different symantics than INT3407. > > ACPI methods description used in this driver: > PSOC: Platform Battery State Of Charge as a percentage. > PMAX: Maximum platform power that can be supported by the battery in mW. > PSRC: System charge source, > 0x00 = DC > 0x01 = AC > 0x02 = USB > 0x03 = Wireless Charger > ARTG: Adapter rating in mW (Maximum Adapter power) Must be 0 if no AC > Adaptor is plugged in. > CTYP: Charger Type, > Traditional : 0x01 > Hybrid: 0x02 > NVDC: 0x03 > PBSS: Returns max sustained power for battery in milliWatts. > DPSP: Sets the polling interval in 10ths of seconds. A value of 0 tells > the driver to use event notification for PMAX and PBSS > > Signed-off-by: Srinivas Pandruvada <srinivas.pandruvada@xxxxxxxxxxxxxxx> > --- > drivers/acpi/Kconfig | 17 +++++ > drivers/acpi/Makefile | 1 + > drivers/acpi/dptf_power.c | 184 ++++++++++++++++++++++++++++++++++++++++++++++ > 3 files changed, 202 insertions(+) > create mode 100644 drivers/acpi/dptf_power.c > > diff --git a/drivers/acpi/Kconfig b/drivers/acpi/Kconfig > index 27ae351..03a34b4 100644 > --- a/drivers/acpi/Kconfig > +++ b/drivers/acpi/Kconfig > @@ -526,4 +526,21 @@ config XPOWER_PMIC_OPREGION > > endif > > +config DPTF_POWER > + tristate "DPTF Platform Power Participant" > + depends on X86 > + select ACPI_BATTERY_COMMON > + select POWER_SUPPLY > + default y > + help > + This driver adds support for Dynamic Platform and Thermal Framework > + (DPTF) Platform Power Participant device support. > + This participant is responsible for exposing platform telemetry such > + as platform Power, battery Information such as state of Charge, > + estimated maximum sustainable power (PMax), SMART battery spec > + information. > + > + To compile this driver as a module, choose M here: > + the module will be called dptf_power. > + > endif # ACPI > diff --git a/drivers/acpi/Makefile b/drivers/acpi/Makefile > index 950bf8e..1c76715 100644 > --- a/drivers/acpi/Makefile > +++ b/drivers/acpi/Makefile > @@ -75,6 +75,7 @@ obj-$(CONFIG_ACPI) += acpi_memhotplug.o > obj-$(CONFIG_ACPI_HOTPLUG_IOAPIC) += ioapic.o > obj-$(CONFIG_ACPI_BATTERY_COMMON) += battery_common.o > obj-$(CONFIG_ACPI_BATTERY) += battery.o > +obj-$(CONFIG_DPTF_POWER) += dptf_power.o Now that I think about it, maybe it would be better to put that into drivers/platform/x86? > obj-$(CONFIG_ACPI_SBS) += sbshc.o > obj-$(CONFIG_ACPI_SBS) += sbs.o > obj-$(CONFIG_ACPI_HED) += hed.o > diff --git a/drivers/acpi/dptf_power.c b/drivers/acpi/dptf_power.c > new file mode 100644 > index 0000000..013b7f3 > --- /dev/null > +++ b/drivers/acpi/dptf_power.c > @@ -0,0 +1,184 @@ > +/* > + * dptf_power: DPTF platform power driver > + * Copyright (c) 2016, Intel Corporation. > + * > + * This program is free software; you can redistribute it and/or modify it > + * under the terms and conditions of the GNU General Public License, > + * version 2, as published by the Free Software Foundation. > + * > + * This program is distributed in the hope it will be useful, but WITHOUT > + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or > + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for > + * more details. > + * > + */ > + > +#include <linux/kernel.h> > +#include <linux/module.h> > +#include <linux/acpi.h> > +#include <linux/platform_device.h> > +#include "battery.h" > + > +#define DPTF_POWER_SHOW(name, object) \ > +static ssize_t name##_show(struct device *dev,\ > + struct device_attribute *attr,\ > + char *buf)\ > +{\ > + struct acpi_device *acpi_dev = to_acpi_device(dev);\ > + unsigned long long val;\ > + acpi_status status;\ > +\ > + status = acpi_evaluate_integer(acpi_dev->handle, #object,\ > + NULL, &val);\ > + if (ACPI_SUCCESS(status))\ > + return sprintf(buf, "%llu\n", val);\ > + else\ > + return -EINVAL;\ > +} > + > +DPTF_POWER_SHOW(max_platform_power, PMAX) > +DPTF_POWER_SHOW(platform_power_source, PSRC) > +DPTF_POWER_SHOW(adapter_rating, ARTG) > +DPTF_POWER_SHOW(charger_type, CTYP) > +DPTF_POWER_SHOW(battery_steady_power, PBSS) > +DPTF_POWER_SHOW(power_sampling_period, DPSP) > + > + > +static DEVICE_ATTR_RO(max_platform_power); > +static DEVICE_ATTR_RO(platform_power_source); > +static DEVICE_ATTR_RO(adapter_rating); > +static DEVICE_ATTR_RO(battery_steady_power); > +static DEVICE_ATTR_RO(power_sampling_period); > +static DEVICE_ATTR_RO(charger_type); > + > +static struct attribute *dptf_power_attrs[] = { > + &dev_attr_max_platform_power.attr, > + &dev_attr_platform_power_source.attr, > + &dev_attr_adapter_rating.attr, > + &dev_attr_charger_type.attr, > + &dev_attr_battery_steady_power.attr, > + &dev_attr_power_sampling_period.attr, > + NULL > +}; > + > +static struct attribute_group dptf_power_attribute_group = { > + .attrs = dptf_power_attrs, > + .name = "dptf_power" > +}; > + > +struct platform_device *dptf_power_pdev; > + > +static void dptf_power_notify(acpi_handle handle, u32 event, void *data) > +{ > + struct acpi_device *device = data; > + > + acpi_battery_common_notify(device, event); > +} > + > +static int dptf_power_init(struct platform_device *pdev) > +{ > + struct acpi_device *acpi_dev; > + acpi_status status; > + unsigned long long ptype; > + int result; > + > + acpi_dev = ACPI_COMPANION(&(pdev->dev)); > + if (!acpi_dev) > + return -ENODEV; Since acpi_battery is an ACPI driver, it will bind to acpi_dev directly, so I guess this should check if there's a driver bound to acpi_dev and bail out if so, shouldn't it? > + > + status = acpi_evaluate_integer(acpi_dev->handle, "PTYP", NULL, &ptype); > + if (ACPI_FAILURE(status)) > + return -ENODEV; > + > + if (ptype != 0x11) > + return -ENODEV; > + > + > + result = acpi_battery_common_add(acpi_dev); > + if (result) > + return result; > + > + result = sysfs_create_group(&acpi_dev->dev.kobj, > + &dptf_power_attribute_group); > + if (result) > + goto error_remove_battery; > + > + result = acpi_install_notify_handler(acpi_dev->handle, > + ACPI_DEVICE_NOTIFY, > + dptf_power_notify, > + (void *)acpi_dev); > + if (result) > + goto error_remove_sysfs; > + > + platform_set_drvdata(pdev, acpi_dev); > + > + return 0; > + > +error_remove_sysfs: > + sysfs_remove_group(&acpi_dev->dev.kobj, &dptf_power_attribute_group); > +error_remove_battery: > + acpi_battery_common_remove(acpi_dev); > + > + return result; > +} > + > +static void dptf_power_init_work_fn(struct work_struct *work) > +{ > + if (!battery_registered) > + dptf_power_init(dptf_power_pdev); > +} > + > +static DECLARE_DELAYED_WORK(dptf_power_init_work, dptf_power_init_work_fn); > + > +static int dptf_power_add(struct platform_device *pdev) > +{ > + int ret = 0; > + > +#if IS_ENABLED(CONFIG_ACPI_BATTERY) > + /* Wait for battery driver to initialize and register for 100ms */ Do we really need to wait? It looks like acpi_battery would be a fallback driver for this one, so why wait? > + dptf_power_pdev = pdev; > + schedule_delayed_work(&dptf_power_init_work, msecs_to_jiffies(100)); > +#else > + ret = dptf_power_init(pdev); > +#endif > + return ret; > +} > + > +static int dptf_power_remove(struct platform_device *pdev) > +{ > + struct acpi_device *acpi_dev = platform_get_drvdata(pdev); > + > +#if defined(CONFIG_ACPI_BATTERY) > + cancel_delayed_work_sync(&dptf_power_init_work); > +#endif > + if (!acpi_dev) > + return 0; > + > + acpi_remove_notify_handler(acpi_dev->handle, ACPI_DEVICE_NOTIFY, > + dptf_power_notify); > + sysfs_remove_group(&acpi_dev->dev.kobj, &dptf_power_attribute_group); > + acpi_battery_common_remove(acpi_dev); > + > + return 0; > +} > + > +static const struct acpi_device_id int3407_device_ids[] = { > + {"INT3407", 0}, > + {"", 0}, > +}; > +MODULE_DEVICE_TABLE(acpi, int3407_device_ids); > + > +static struct platform_driver dptf_power_driver = { > + .probe = dptf_power_add, > + .remove = dptf_power_remove, > + .driver = { > + .name = "DPTF Platform Power", > + .acpi_match_table = int3407_device_ids, > + }, > +}; > + > +module_platform_driver(dptf_power_driver); > + > +MODULE_AUTHOR("Srinivas Pandruvada <srinivas.pandruvada@xxxxxxxxxxxxxxx>"); > +MODULE_LICENSE("GPL v2"); > +MODULE_DESCRIPTION("ACPI DPTF platform power driver"); > -- -- To unsubscribe from this list: send the line "unsubscribe linux-acpi" in the body of a message to majordomo@xxxxxxxxxxxxxxx More majordomo info at http://vger.kernel.org/majordomo-info.html