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 powe 16 * PSRC : Platform power source 17 * ARTG : Adapter rating 18 * CTYP : Charger type 19 * PBSS : Battery steady power 20 * PROP : Rest of worst case platform Power 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 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 {\ 32 struct acpi_device *acpi_dev = dev_get_drvdata(dev);\ 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) 49 DPTF_POWER_SHOW(rest_of_platform_power_mw, PROP) 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); 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); 60 static DEVICE_ATTR_RO(rest_of_platform_power_mw); 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); 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); 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, 92 &dev_attr_rest_of_platform_power_mw.attr, 93 &dev_attr_prochot_confirm.attr, 94 NULL 95 }; 96 97 static const struct attribute_group dptf_power_attribute_group = { 98 .attrs = dptf_power_attrs, 99 .name = "dptf_power" 100 }; 101 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 117 #define POWER_STATE_CHANGED 0x81 118 #define STEADY_STATE_POWER_CHANGED 0x83 119 #define POWER_PROP_CHANGE_EVENT 0x84 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 } 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; 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; 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 */ 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); 169 } 170 171 static int dptf_power_add(struct platform_device *pdev) 172 { 173 const struct attribute_group *attr_group; 174 struct acpi_device *acpi_dev; 175 unsigned long long ptype; 176 int result; 177 178 acpi_dev = ACPI_COMPANION(&(pdev->dev)); 179 if (!acpi_dev) 180 return -ENODEV; 181 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 188 return -ENODEV; 189 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 197 result = sysfs_create_group(&pdev->dev.kobj, 198 attr_group); 199 if (result) { 200 acpi_remove_notify_handler(acpi_dev->handle, 201 ACPI_DEVICE_NOTIFY, 202 dptf_power_notify); 203 return result; 204 } 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 { 213 struct acpi_device *acpi_dev = platform_get_drvdata(pdev); 214 215 acpi_remove_notify_handler(acpi_dev->handle, 216 ACPI_DEVICE_NOTIFY, 217 dptf_power_notify); 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); 223 224 return 0; 225 } 226 227 static const struct acpi_device_id int3407_device_ids[] = { 228 {"INT3407", 0}, 229 {"INT3532", 0}, 230 {"INTC1047", 0}, 231 {"INTC1050", 0}, 232 {"INTC1060", 0}, 233 {"INTC1061", 0}, 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 = { 242 .name = "dptf_power", 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"); 252