1 // SPDX-License-Identifier: GPL-2.0+ 2 /* 3 * Fast-charge control for Apple "MFi" devices 4 * 5 * Copyright (C) 2019 Bastien Nocera <hadess@hadess.net> 6 */ 7 8 /* Standard include files */ 9 #include <linux/module.h> 10 #include <linux/power_supply.h> 11 #include <linux/slab.h> 12 #include <linux/usb.h> 13 14 MODULE_AUTHOR("Bastien Nocera <hadess@hadess.net>"); 15 MODULE_DESCRIPTION("Fast-charge control for Apple \"MFi\" devices"); 16 MODULE_LICENSE("GPL"); 17 18 #define TRICKLE_CURRENT_MA 0 19 #define FAST_CURRENT_MA 2500 20 21 #define APPLE_VENDOR_ID 0x05ac /* Apple */ 22 23 /* The product ID is defined as starting with 0x12nn, as per the 24 * "Choosing an Apple Device USB Configuration" section in 25 * release R9 (2012) of the "MFi Accessory Hardware Specification" 26 * 27 * To distinguish an Apple device, a USB host can check the device 28 * descriptor of attached USB devices for the following fields: 29 * ■ Vendor ID: 0x05AC 30 * ■ Product ID: 0x12nn 31 * 32 * Those checks will be done in .match() and .probe(). 33 */ 34 35 static const struct usb_device_id mfi_fc_id_table[] = { 36 { .idVendor = APPLE_VENDOR_ID, 37 .match_flags = USB_DEVICE_ID_MATCH_VENDOR }, 38 {}, 39 }; 40 41 MODULE_DEVICE_TABLE(usb, mfi_fc_id_table); 42 43 /* Driver-local specific stuff */ 44 struct mfi_device { 45 struct usb_device *udev; 46 struct power_supply *battery; 47 struct power_supply_desc battery_desc; 48 int charge_type; 49 }; 50 51 static int apple_mfi_fc_set_charge_type(struct mfi_device *mfi, 52 const union power_supply_propval *val) 53 { 54 int current_ma; 55 int retval; 56 __u8 request_type; 57 58 if (mfi->charge_type == val->intval) { 59 dev_dbg(&mfi->udev->dev, "charge type %d already set\n", 60 mfi->charge_type); 61 return 0; 62 } 63 64 switch (val->intval) { 65 case POWER_SUPPLY_CHARGE_TYPE_TRICKLE: 66 current_ma = TRICKLE_CURRENT_MA; 67 break; 68 case POWER_SUPPLY_CHARGE_TYPE_FAST: 69 current_ma = FAST_CURRENT_MA; 70 break; 71 default: 72 return -EINVAL; 73 } 74 75 request_type = USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE; 76 retval = usb_control_msg(mfi->udev, usb_sndctrlpipe(mfi->udev, 0), 77 0x40, /* Vendor‐defined power request */ 78 request_type, 79 current_ma, /* wValue, current offset */ 80 current_ma, /* wIndex, current offset */ 81 NULL, 0, USB_CTRL_GET_TIMEOUT); 82 if (retval) { 83 dev_dbg(&mfi->udev->dev, "retval = %d\n", retval); 84 return retval; 85 } 86 87 mfi->charge_type = val->intval; 88 89 return 0; 90 } 91 92 static int apple_mfi_fc_get_property(struct power_supply *psy, 93 enum power_supply_property psp, 94 union power_supply_propval *val) 95 { 96 struct mfi_device *mfi = power_supply_get_drvdata(psy); 97 98 dev_dbg(&mfi->udev->dev, "prop: %d\n", psp); 99 100 switch (psp) { 101 case POWER_SUPPLY_PROP_CHARGE_TYPE: 102 val->intval = mfi->charge_type; 103 break; 104 case POWER_SUPPLY_PROP_SCOPE: 105 val->intval = POWER_SUPPLY_SCOPE_DEVICE; 106 break; 107 default: 108 return -ENODATA; 109 } 110 111 return 0; 112 } 113 114 static int apple_mfi_fc_set_property(struct power_supply *psy, 115 enum power_supply_property psp, 116 const union power_supply_propval *val) 117 { 118 struct mfi_device *mfi = power_supply_get_drvdata(psy); 119 int ret; 120 121 dev_dbg(&mfi->udev->dev, "prop: %d\n", psp); 122 123 ret = pm_runtime_get_sync(&mfi->udev->dev); 124 if (ret < 0) { 125 pm_runtime_put_noidle(&mfi->udev->dev); 126 return ret; 127 } 128 129 switch (psp) { 130 case POWER_SUPPLY_PROP_CHARGE_TYPE: 131 ret = apple_mfi_fc_set_charge_type(mfi, val); 132 break; 133 default: 134 ret = -EINVAL; 135 } 136 137 pm_runtime_mark_last_busy(&mfi->udev->dev); 138 pm_runtime_put_autosuspend(&mfi->udev->dev); 139 140 return ret; 141 } 142 143 static int apple_mfi_fc_property_is_writeable(struct power_supply *psy, 144 enum power_supply_property psp) 145 { 146 switch (psp) { 147 case POWER_SUPPLY_PROP_CHARGE_TYPE: 148 return 1; 149 default: 150 return 0; 151 } 152 } 153 154 static enum power_supply_property apple_mfi_fc_properties[] = { 155 POWER_SUPPLY_PROP_CHARGE_TYPE, 156 POWER_SUPPLY_PROP_SCOPE 157 }; 158 159 static const struct power_supply_desc apple_mfi_fc_desc = { 160 .name = "apple_mfi_fastcharge", 161 .type = POWER_SUPPLY_TYPE_BATTERY, 162 .properties = apple_mfi_fc_properties, 163 .num_properties = ARRAY_SIZE(apple_mfi_fc_properties), 164 .get_property = apple_mfi_fc_get_property, 165 .set_property = apple_mfi_fc_set_property, 166 .property_is_writeable = apple_mfi_fc_property_is_writeable 167 }; 168 169 static bool mfi_fc_match(struct usb_device *udev) 170 { 171 int idProduct; 172 173 idProduct = le16_to_cpu(udev->descriptor.idProduct); 174 /* See comment above mfi_fc_id_table[] */ 175 return (idProduct >= 0x1200 && idProduct <= 0x12ff); 176 } 177 178 static int mfi_fc_probe(struct usb_device *udev) 179 { 180 struct power_supply_config battery_cfg = {}; 181 struct mfi_device *mfi = NULL; 182 char *battery_name; 183 int err; 184 185 if (!mfi_fc_match(udev)) 186 return -ENODEV; 187 188 mfi = kzalloc(sizeof(struct mfi_device), GFP_KERNEL); 189 if (!mfi) 190 return -ENOMEM; 191 192 battery_name = kasprintf(GFP_KERNEL, "apple_mfi_fastcharge_%d-%d", 193 udev->bus->busnum, udev->devnum); 194 if (!battery_name) { 195 err = -ENOMEM; 196 goto err_free_mfi; 197 } 198 199 mfi->battery_desc = apple_mfi_fc_desc; 200 mfi->battery_desc.name = battery_name; 201 202 battery_cfg.drv_data = mfi; 203 204 mfi->charge_type = POWER_SUPPLY_CHARGE_TYPE_TRICKLE; 205 mfi->battery = power_supply_register(&udev->dev, 206 &mfi->battery_desc, 207 &battery_cfg); 208 if (IS_ERR(mfi->battery)) { 209 dev_err(&udev->dev, "Can't register battery\n"); 210 err = PTR_ERR(mfi->battery); 211 goto err_free_name; 212 } 213 214 mfi->udev = usb_get_dev(udev); 215 dev_set_drvdata(&udev->dev, mfi); 216 217 return 0; 218 219 err_free_name: 220 kfree(battery_name); 221 err_free_mfi: 222 kfree(mfi); 223 return err; 224 } 225 226 static void mfi_fc_disconnect(struct usb_device *udev) 227 { 228 struct mfi_device *mfi; 229 230 mfi = dev_get_drvdata(&udev->dev); 231 if (mfi->battery) 232 power_supply_unregister(mfi->battery); 233 kfree(mfi->battery_desc.name); 234 dev_set_drvdata(&udev->dev, NULL); 235 usb_put_dev(mfi->udev); 236 kfree(mfi); 237 } 238 239 static struct usb_device_driver mfi_fc_driver = { 240 .name = "apple-mfi-fastcharge", 241 .probe = mfi_fc_probe, 242 .disconnect = mfi_fc_disconnect, 243 .id_table = mfi_fc_id_table, 244 .match = mfi_fc_match, 245 .generic_subclass = 1, 246 }; 247 248 static int __init mfi_fc_driver_init(void) 249 { 250 return usb_register_device_driver(&mfi_fc_driver, THIS_MODULE); 251 } 252 253 static void __exit mfi_fc_driver_exit(void) 254 { 255 usb_deregister_device_driver(&mfi_fc_driver); 256 } 257 258 module_init(mfi_fc_driver_init); 259 module_exit(mfi_fc_driver_exit); 260