Commit | Line | Data |
---|---|---|
2025cf9e | 1 | // SPDX-License-Identifier: GPL-2.0-only |
6256ebd5 SP |
2 | /* |
3 | * dptf_power: DPTF platform power driver | |
4 | * Copyright (c) 2016, Intel Corporation. | |
6256ebd5 SP |
5 | */ |
6 | ||
7 | #include <linux/kernel.h> | |
8 | #include <linux/module.h> | |
9 | #include <linux/acpi.h> | |
10 | #include <linux/platform_device.h> | |
11 | ||
12 | /* | |
7b52b200 SP |
13 | * Presentation of attributes which are defined for INT3407 and INT3532. |
14 | * They are: | |
6256ebd5 SP |
15 | * PMAX : Maximum platform powe |
16 | * PSRC : Platform power source | |
17 | * ARTG : Adapter rating | |
18 | * CTYP : Charger type | |
19 | * PBSS : Battery steady power | |
668ce99e | 20 | * PROP : Rest of worst case platform Power |
7b52b200 SP |
21 | * PBSS : Power Battery Steady State |
22 | * PBSS : Power Battery Steady State | |
23 | * RBHF : High Frequency Impedance | |
24 | * VBNL : Instantaneous No-Load Voltage | |
25 | * CMPP : Current Discharge Capability | |
6256ebd5 SP |
26 | */ |
27 | #define DPTF_POWER_SHOW(name, object) \ | |
28 | static ssize_t name##_show(struct device *dev,\ | |
29 | struct device_attribute *attr,\ | |
30 | char *buf)\ | |
31 | {\ | |
b0f65b91 | 32 | struct acpi_device *acpi_dev = dev_get_drvdata(dev);\ |
6256ebd5 SP |
33 | unsigned long long val;\ |
34 | acpi_status status;\ | |
35 | \ | |
36 | status = acpi_evaluate_integer(acpi_dev->handle, #object,\ | |
37 | NULL, &val);\ | |
38 | if (ACPI_SUCCESS(status))\ | |
39 | return sprintf(buf, "%d\n", (int)val);\ | |
40 | else \ | |
41 | return -EINVAL;\ | |
42 | } | |
43 | ||
44 | DPTF_POWER_SHOW(max_platform_power_mw, PMAX) | |
45 | DPTF_POWER_SHOW(platform_power_source, PSRC) | |
46 | DPTF_POWER_SHOW(adapter_rating_mw, ARTG) | |
47 | DPTF_POWER_SHOW(battery_steady_power_mw, PBSS) | |
48 | DPTF_POWER_SHOW(charger_type, CTYP) | |
668ce99e | 49 | DPTF_POWER_SHOW(rest_of_platform_power_mw, PROP) |
7b52b200 SP |
50 | DPTF_POWER_SHOW(max_steady_state_power_mw, PBSS) |
51 | DPTF_POWER_SHOW(high_freq_impedance_mohm, RBHF) | |
52 | DPTF_POWER_SHOW(no_load_voltage_mv, VBNL) | |
53 | DPTF_POWER_SHOW(current_discharge_capbility_ma, CMPP); | |
6256ebd5 SP |
54 | |
55 | static DEVICE_ATTR_RO(max_platform_power_mw); | |
56 | static DEVICE_ATTR_RO(platform_power_source); | |
57 | static DEVICE_ATTR_RO(adapter_rating_mw); | |
58 | static DEVICE_ATTR_RO(battery_steady_power_mw); | |
59 | static DEVICE_ATTR_RO(charger_type); | |
668ce99e | 60 | static DEVICE_ATTR_RO(rest_of_platform_power_mw); |
7b52b200 SP |
61 | static DEVICE_ATTR_RO(max_steady_state_power_mw); |
62 | static DEVICE_ATTR_RO(high_freq_impedance_mohm); | |
63 | static DEVICE_ATTR_RO(no_load_voltage_mv); | |
64 | static DEVICE_ATTR_RO(current_discharge_capbility_ma); | |
668ce99e SP |
65 | |
66 | static ssize_t prochot_confirm_store(struct device *dev, | |
67 | struct device_attribute *attr, | |
68 | const char *buf, size_t count) | |
69 | { | |
70 | struct acpi_device *acpi_dev = dev_get_drvdata(dev); | |
71 | acpi_status status; | |
72 | int seq_no; | |
73 | ||
74 | if (kstrtouint(buf, 0, &seq_no) < 0) | |
75 | return -EINVAL; | |
76 | ||
77 | status = acpi_execute_simple_method(acpi_dev->handle, "PBOK", seq_no); | |
78 | if (ACPI_SUCCESS(status)) | |
79 | return count; | |
80 | ||
81 | return -EINVAL; | |
82 | } | |
83 | ||
84 | static DEVICE_ATTR_WO(prochot_confirm); | |
6256ebd5 SP |
85 | |
86 | static struct attribute *dptf_power_attrs[] = { | |
87 | &dev_attr_max_platform_power_mw.attr, | |
88 | &dev_attr_platform_power_source.attr, | |
89 | &dev_attr_adapter_rating_mw.attr, | |
90 | &dev_attr_battery_steady_power_mw.attr, | |
91 | &dev_attr_charger_type.attr, | |
668ce99e SP |
92 | &dev_attr_rest_of_platform_power_mw.attr, |
93 | &dev_attr_prochot_confirm.attr, | |
6256ebd5 SP |
94 | NULL |
95 | }; | |
96 | ||
9e4de6a8 | 97 | static const struct attribute_group dptf_power_attribute_group = { |
6256ebd5 SP |
98 | .attrs = dptf_power_attrs, |
99 | .name = "dptf_power" | |
100 | }; | |
101 | ||
7b52b200 SP |
102 | static struct attribute *dptf_battery_attrs[] = { |
103 | &dev_attr_max_platform_power_mw.attr, | |
104 | &dev_attr_max_steady_state_power_mw.attr, | |
105 | &dev_attr_high_freq_impedance_mohm.attr, | |
106 | &dev_attr_no_load_voltage_mv.attr, | |
107 | &dev_attr_current_discharge_capbility_ma.attr, | |
108 | NULL | |
109 | }; | |
110 | ||
111 | static const struct attribute_group dptf_battery_attribute_group = { | |
112 | .attrs = dptf_battery_attrs, | |
113 | .name = "dptf_battery" | |
114 | }; | |
115 | ||
116 | #define MAX_POWER_CHANGED 0x80 | |
668ce99e | 117 | #define POWER_STATE_CHANGED 0x81 |
7b52b200 | 118 | #define STEADY_STATE_POWER_CHANGED 0x83 |
668ce99e | 119 | #define POWER_PROP_CHANGE_EVENT 0x84 |
7b52b200 SP |
120 | #define IMPEDANCED_CHNGED 0x85 |
121 | #define VOLTAGE_CURRENT_CHANGED 0x86 | |
122 | ||
123 | static long long dptf_participant_type(acpi_handle handle) | |
124 | { | |
125 | unsigned long long ptype; | |
126 | acpi_status status; | |
127 | ||
128 | status = acpi_evaluate_integer(handle, "PTYP", NULL, &ptype); | |
129 | if (ACPI_FAILURE(status)) | |
130 | return -ENODEV; | |
131 | ||
132 | return ptype; | |
133 | } | |
668ce99e SP |
134 | |
135 | static void dptf_power_notify(acpi_handle handle, u32 event, void *data) | |
136 | { | |
137 | struct platform_device *pdev = data; | |
138 | char *attr; | |
139 | ||
140 | switch (event) { | |
141 | case POWER_STATE_CHANGED: | |
142 | attr = "platform_power_source"; | |
143 | break; | |
144 | case POWER_PROP_CHANGE_EVENT: | |
145 | attr = "rest_of_platform_power_mw"; | |
146 | break; | |
7b52b200 SP |
147 | case MAX_POWER_CHANGED: |
148 | attr = "max_platform_power_mw"; | |
149 | break; | |
150 | case STEADY_STATE_POWER_CHANGED: | |
151 | attr = "max_steady_state_power_mw"; | |
152 | break; | |
153 | case VOLTAGE_CURRENT_CHANGED: | |
154 | attr = "no_load_voltage_mv"; | |
155 | break; | |
668ce99e SP |
156 | default: |
157 | dev_err(&pdev->dev, "Unsupported event [0x%x]\n", event); | |
158 | return; | |
159 | } | |
160 | ||
161 | /* | |
162 | * Notify that an attribute is changed, so that user space can read | |
163 | * again. | |
164 | */ | |
7b52b200 SP |
165 | if (dptf_participant_type(handle) == 0x0CULL) |
166 | sysfs_notify(&pdev->dev.kobj, "dptf_battery", attr); | |
167 | else | |
168 | sysfs_notify(&pdev->dev.kobj, "dptf_power", attr); | |
668ce99e SP |
169 | } |
170 | ||
6256ebd5 SP |
171 | static int dptf_power_add(struct platform_device *pdev) |
172 | { | |
7b52b200 | 173 | const struct attribute_group *attr_group; |
6256ebd5 | 174 | struct acpi_device *acpi_dev; |
6256ebd5 SP |
175 | unsigned long long ptype; |
176 | int result; | |
177 | ||
178 | acpi_dev = ACPI_COMPANION(&(pdev->dev)); | |
179 | if (!acpi_dev) | |
180 | return -ENODEV; | |
181 | ||
7b52b200 SP |
182 | ptype = dptf_participant_type(acpi_dev->handle); |
183 | if (ptype == 0x11) | |
184 | attr_group = &dptf_power_attribute_group; | |
185 | else if (ptype == 0x0C) | |
186 | attr_group = &dptf_battery_attribute_group; | |
187 | else | |
6256ebd5 SP |
188 | return -ENODEV; |
189 | ||
668ce99e SP |
190 | result = acpi_install_notify_handler(acpi_dev->handle, |
191 | ACPI_DEVICE_NOTIFY, | |
192 | dptf_power_notify, | |
193 | (void *)pdev); | |
194 | if (result) | |
195 | return result; | |
196 | ||
6256ebd5 | 197 | result = sysfs_create_group(&pdev->dev.kobj, |
7b52b200 | 198 | attr_group); |
668ce99e SP |
199 | if (result) { |
200 | acpi_remove_notify_handler(acpi_dev->handle, | |
201 | ACPI_DEVICE_NOTIFY, | |
202 | dptf_power_notify); | |
6256ebd5 | 203 | return result; |
668ce99e | 204 | } |
6256ebd5 SP |
205 | |
206 | platform_set_drvdata(pdev, acpi_dev); | |
207 | ||
208 | return 0; | |
209 | } | |
210 | ||
211 | static int dptf_power_remove(struct platform_device *pdev) | |
212 | { | |
668ce99e | 213 | struct acpi_device *acpi_dev = platform_get_drvdata(pdev); |
6256ebd5 | 214 | |
668ce99e SP |
215 | acpi_remove_notify_handler(acpi_dev->handle, |
216 | ACPI_DEVICE_NOTIFY, | |
217 | dptf_power_notify); | |
7b52b200 SP |
218 | |
219 | if (dptf_participant_type(acpi_dev->handle) == 0x0CULL) | |
220 | sysfs_remove_group(&pdev->dev.kobj, &dptf_battery_attribute_group); | |
221 | else | |
222 | sysfs_remove_group(&pdev->dev.kobj, &dptf_power_attribute_group); | |
6256ebd5 SP |
223 | |
224 | return 0; | |
225 | } | |
226 | ||
227 | static const struct acpi_device_id int3407_device_ids[] = { | |
228 | {"INT3407", 0}, | |
7b52b200 | 229 | {"INT3532", 0}, |
b62c770f | 230 | {"INTC1047", 0}, |
1e05daca | 231 | {"INTC1050", 0}, |
38748bcb SP |
232 | {"INTC1060", 0}, |
233 | {"INTC1061", 0}, | |
6256ebd5 SP |
234 | {"", 0}, |
235 | }; | |
236 | MODULE_DEVICE_TABLE(acpi, int3407_device_ids); | |
237 | ||
238 | static struct platform_driver dptf_power_driver = { | |
239 | .probe = dptf_power_add, | |
240 | .remove = dptf_power_remove, | |
241 | .driver = { | |
ff44fe3e | 242 | .name = "dptf_power", |
6256ebd5 SP |
243 | .acpi_match_table = int3407_device_ids, |
244 | }, | |
245 | }; | |
246 | ||
247 | module_platform_driver(dptf_power_driver); | |
248 | ||
249 | MODULE_AUTHOR("Srinivas Pandruvada <srinivas.pandruvada@linux.intel.com>"); | |
250 | MODULE_LICENSE("GPL v2"); | |
251 | MODULE_DESCRIPTION("ACPI DPTF platform power driver"); |