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