mirror of https://github.com/Qortal/Brooklyn
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
256 lines
6.5 KiB
256 lines
6.5 KiB
// SPDX-License-Identifier: GPL-2.0-only |
|
/* |
|
* dptf_power: DPTF platform power driver |
|
* Copyright (c) 2016, Intel Corporation. |
|
*/ |
|
|
|
#include <linux/kernel.h> |
|
#include <linux/module.h> |
|
#include <linux/acpi.h> |
|
#include <linux/platform_device.h> |
|
|
|
/* |
|
* Presentation of attributes which are defined for INT3407 and INT3532. |
|
* They are: |
|
* PMAX : Maximum platform power |
|
* PSRC : Platform power source |
|
* ARTG : Adapter rating |
|
* CTYP : Charger type |
|
* PROP : Rest of worst case platform Power |
|
* PBSS : Power Battery Steady State |
|
* RBHF : High Frequency Impedance |
|
* VBNL : Instantaneous No-Load Voltage |
|
* CMPP : Current Discharge Capability |
|
*/ |
|
#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 = dev_get_drvdata(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, "%d\n", (int)val);\ |
|
else \ |
|
return -EINVAL;\ |
|
} |
|
|
|
DPTF_POWER_SHOW(max_platform_power_mw, PMAX) |
|
DPTF_POWER_SHOW(platform_power_source, PSRC) |
|
DPTF_POWER_SHOW(adapter_rating_mw, ARTG) |
|
DPTF_POWER_SHOW(battery_steady_power_mw, PBSS) |
|
DPTF_POWER_SHOW(charger_type, CTYP) |
|
DPTF_POWER_SHOW(rest_of_platform_power_mw, PROP) |
|
DPTF_POWER_SHOW(max_steady_state_power_mw, PBSS) |
|
DPTF_POWER_SHOW(high_freq_impedance_mohm, RBHF) |
|
DPTF_POWER_SHOW(no_load_voltage_mv, VBNL) |
|
DPTF_POWER_SHOW(current_discharge_capbility_ma, CMPP); |
|
|
|
static DEVICE_ATTR_RO(max_platform_power_mw); |
|
static DEVICE_ATTR_RO(platform_power_source); |
|
static DEVICE_ATTR_RO(adapter_rating_mw); |
|
static DEVICE_ATTR_RO(battery_steady_power_mw); |
|
static DEVICE_ATTR_RO(charger_type); |
|
static DEVICE_ATTR_RO(rest_of_platform_power_mw); |
|
static DEVICE_ATTR_RO(max_steady_state_power_mw); |
|
static DEVICE_ATTR_RO(high_freq_impedance_mohm); |
|
static DEVICE_ATTR_RO(no_load_voltage_mv); |
|
static DEVICE_ATTR_RO(current_discharge_capbility_ma); |
|
|
|
static ssize_t prochot_confirm_store(struct device *dev, |
|
struct device_attribute *attr, |
|
const char *buf, size_t count) |
|
{ |
|
struct acpi_device *acpi_dev = dev_get_drvdata(dev); |
|
acpi_status status; |
|
int seq_no; |
|
|
|
if (kstrtouint(buf, 0, &seq_no) < 0) |
|
return -EINVAL; |
|
|
|
status = acpi_execute_simple_method(acpi_dev->handle, "PBOK", seq_no); |
|
if (ACPI_SUCCESS(status)) |
|
return count; |
|
|
|
return -EINVAL; |
|
} |
|
|
|
static DEVICE_ATTR_WO(prochot_confirm); |
|
|
|
static struct attribute *dptf_power_attrs[] = { |
|
&dev_attr_max_platform_power_mw.attr, |
|
&dev_attr_platform_power_source.attr, |
|
&dev_attr_adapter_rating_mw.attr, |
|
&dev_attr_battery_steady_power_mw.attr, |
|
&dev_attr_charger_type.attr, |
|
&dev_attr_rest_of_platform_power_mw.attr, |
|
&dev_attr_prochot_confirm.attr, |
|
NULL |
|
}; |
|
|
|
static const struct attribute_group dptf_power_attribute_group = { |
|
.attrs = dptf_power_attrs, |
|
.name = "dptf_power" |
|
}; |
|
|
|
static struct attribute *dptf_battery_attrs[] = { |
|
&dev_attr_max_platform_power_mw.attr, |
|
&dev_attr_max_steady_state_power_mw.attr, |
|
&dev_attr_high_freq_impedance_mohm.attr, |
|
&dev_attr_no_load_voltage_mv.attr, |
|
&dev_attr_current_discharge_capbility_ma.attr, |
|
NULL |
|
}; |
|
|
|
static const struct attribute_group dptf_battery_attribute_group = { |
|
.attrs = dptf_battery_attrs, |
|
.name = "dptf_battery" |
|
}; |
|
|
|
#define MAX_POWER_CHANGED 0x80 |
|
#define POWER_STATE_CHANGED 0x81 |
|
#define STEADY_STATE_POWER_CHANGED 0x83 |
|
#define POWER_PROP_CHANGE_EVENT 0x84 |
|
#define IMPEDANCE_CHANGED 0x85 |
|
#define VOLTAGE_CURRENT_CHANGED 0x86 |
|
|
|
static long long dptf_participant_type(acpi_handle handle) |
|
{ |
|
unsigned long long ptype; |
|
acpi_status status; |
|
|
|
status = acpi_evaluate_integer(handle, "PTYP", NULL, &ptype); |
|
if (ACPI_FAILURE(status)) |
|
return -ENODEV; |
|
|
|
return ptype; |
|
} |
|
|
|
static void dptf_power_notify(acpi_handle handle, u32 event, void *data) |
|
{ |
|
struct platform_device *pdev = data; |
|
char *attr; |
|
|
|
switch (event) { |
|
case POWER_STATE_CHANGED: |
|
attr = "platform_power_source"; |
|
break; |
|
case POWER_PROP_CHANGE_EVENT: |
|
attr = "rest_of_platform_power_mw"; |
|
break; |
|
case MAX_POWER_CHANGED: |
|
attr = "max_platform_power_mw"; |
|
break; |
|
case STEADY_STATE_POWER_CHANGED: |
|
attr = "max_steady_state_power_mw"; |
|
break; |
|
case IMPEDANCE_CHANGED: |
|
attr = "high_freq_impedance_mohm"; |
|
break; |
|
case VOLTAGE_CURRENT_CHANGED: |
|
attr = "no_load_voltage_mv"; |
|
break; |
|
default: |
|
dev_err(&pdev->dev, "Unsupported event [0x%x]\n", event); |
|
return; |
|
} |
|
|
|
/* |
|
* Notify that an attribute is changed, so that user space can read |
|
* again. |
|
*/ |
|
if (dptf_participant_type(handle) == 0x0CULL) |
|
sysfs_notify(&pdev->dev.kobj, "dptf_battery", attr); |
|
else |
|
sysfs_notify(&pdev->dev.kobj, "dptf_power", attr); |
|
} |
|
|
|
static int dptf_power_add(struct platform_device *pdev) |
|
{ |
|
const struct attribute_group *attr_group; |
|
struct acpi_device *acpi_dev; |
|
unsigned long long ptype; |
|
int result; |
|
|
|
acpi_dev = ACPI_COMPANION(&(pdev->dev)); |
|
if (!acpi_dev) |
|
return -ENODEV; |
|
|
|
ptype = dptf_participant_type(acpi_dev->handle); |
|
if (ptype == 0x11) |
|
attr_group = &dptf_power_attribute_group; |
|
else if (ptype == 0x0C) |
|
attr_group = &dptf_battery_attribute_group; |
|
else |
|
return -ENODEV; |
|
|
|
result = acpi_install_notify_handler(acpi_dev->handle, |
|
ACPI_DEVICE_NOTIFY, |
|
dptf_power_notify, |
|
(void *)pdev); |
|
if (result) |
|
return result; |
|
|
|
result = sysfs_create_group(&pdev->dev.kobj, |
|
attr_group); |
|
if (result) { |
|
acpi_remove_notify_handler(acpi_dev->handle, |
|
ACPI_DEVICE_NOTIFY, |
|
dptf_power_notify); |
|
return result; |
|
} |
|
|
|
platform_set_drvdata(pdev, acpi_dev); |
|
|
|
return 0; |
|
} |
|
|
|
static int dptf_power_remove(struct platform_device *pdev) |
|
{ |
|
struct acpi_device *acpi_dev = platform_get_drvdata(pdev); |
|
|
|
acpi_remove_notify_handler(acpi_dev->handle, |
|
ACPI_DEVICE_NOTIFY, |
|
dptf_power_notify); |
|
|
|
if (dptf_participant_type(acpi_dev->handle) == 0x0CULL) |
|
sysfs_remove_group(&pdev->dev.kobj, &dptf_battery_attribute_group); |
|
else |
|
sysfs_remove_group(&pdev->dev.kobj, &dptf_power_attribute_group); |
|
|
|
return 0; |
|
} |
|
|
|
static const struct acpi_device_id int3407_device_ids[] = { |
|
{"INT3407", 0}, |
|
{"INT3532", 0}, |
|
{"INTC1047", 0}, |
|
{"INTC1050", 0}, |
|
{"INTC1060", 0}, |
|
{"INTC1061", 0}, |
|
{"INTC1065", 0}, |
|
{"INTC1066", 0}, |
|
{"INTC10A4", 0}, |
|
{"INTC10A5", 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_power", |
|
.acpi_match_table = int3407_device_ids, |
|
}, |
|
}; |
|
|
|
module_platform_driver(dptf_power_driver); |
|
|
|
MODULE_AUTHOR("Srinivas Pandruvada <[email protected]>"); |
|
MODULE_LICENSE("GPL v2"); |
|
MODULE_DESCRIPTION("ACPI DPTF platform power driver");
|
|
|