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_put_autosuspend(&mfi->udev->dev); 138 139 return ret; 140 } 141 142 static int apple_mfi_fc_property_is_writeable(struct power_supply *psy, 143 enum power_supply_property psp) 144 { 145 switch (psp) { 146 case POWER_SUPPLY_PROP_CHARGE_TYPE: 147 return 1; 148 default: 149 return 0; 150 } 151 } 152 153 static enum power_supply_property apple_mfi_fc_properties[] = { 154 POWER_SUPPLY_PROP_CHARGE_TYPE, 155 POWER_SUPPLY_PROP_SCOPE 156 }; 157 158 static const struct power_supply_desc apple_mfi_fc_desc = { 159 .name = "apple_mfi_fastcharge", 160 .type = POWER_SUPPLY_TYPE_BATTERY, 161 .properties = apple_mfi_fc_properties, 162 .num_properties = ARRAY_SIZE(apple_mfi_fc_properties), 163 .get_property = apple_mfi_fc_get_property, 164 .set_property = apple_mfi_fc_set_property, 165 .property_is_writeable = apple_mfi_fc_property_is_writeable 166 }; 167 168 static bool mfi_fc_match(struct usb_device *udev) 169 { 170 int idProduct; 171 172 idProduct = le16_to_cpu(udev->descriptor.idProduct); 173 /* See comment above mfi_fc_id_table[] */ 174 return (idProduct >= 0x1200 && idProduct <= 0x12ff); 175 } 176 177 static int mfi_fc_probe(struct usb_device *udev) 178 { 179 struct power_supply_config battery_cfg = {}; 180 struct mfi_device *mfi = NULL; 181 char *battery_name; 182 int err; 183 184 if (!mfi_fc_match(udev)) 185 return -ENODEV; 186 187 mfi = kzalloc(sizeof(struct mfi_device), GFP_KERNEL); 188 if (!mfi) 189 return -ENOMEM; 190 191 battery_name = kasprintf(GFP_KERNEL, "apple_mfi_fastcharge_%d-%d", 192 udev->bus->busnum, udev->devnum); 193 if (!battery_name) { 194 err = -ENOMEM; 195 goto err_free_mfi; 196 } 197 198 mfi->battery_desc = apple_mfi_fc_desc; 199 mfi->battery_desc.name = battery_name; 200 201 battery_cfg.drv_data = mfi; 202 203 mfi->charge_type = POWER_SUPPLY_CHARGE_TYPE_TRICKLE; 204 mfi->battery = power_supply_register(&udev->dev, 205 &mfi->battery_desc, 206 &battery_cfg); 207 if (IS_ERR(mfi->battery)) { 208 dev_err(&udev->dev, "Can't register battery\n"); 209 err = PTR_ERR(mfi->battery); 210 goto err_free_name; 211 } 212 213 mfi->udev = usb_get_dev(udev); 214 dev_set_drvdata(&udev->dev, mfi); 215 216 return 0; 217 218 err_free_name: 219 kfree(battery_name); 220 err_free_mfi: 221 kfree(mfi); 222 return err; 223 } 224 225 static void mfi_fc_disconnect(struct usb_device *udev) 226 { 227 struct mfi_device *mfi; 228 229 mfi = dev_get_drvdata(&udev->dev); 230 if (mfi->battery) 231 power_supply_unregister(mfi->battery); 232 kfree(mfi->battery_desc.name); 233 dev_set_drvdata(&udev->dev, NULL); 234 usb_put_dev(mfi->udev); 235 kfree(mfi); 236 } 237 238 static struct usb_device_driver mfi_fc_driver = { 239 .name = "apple-mfi-fastcharge", 240 .probe = mfi_fc_probe, 241 .disconnect = mfi_fc_disconnect, 242 .id_table = mfi_fc_id_table, 243 .match = mfi_fc_match, 244 .generic_subclass = 1, 245 }; 246 247 static int __init mfi_fc_driver_init(void) 248 { 249 return usb_register_device_driver(&mfi_fc_driver, THIS_MODULE); 250 } 251 252 static void __exit mfi_fc_driver_exit(void) 253 { 254 usb_deregister_device_driver(&mfi_fc_driver); 255 } 256 257 module_init(mfi_fc_driver_init); 258 module_exit(mfi_fc_driver_exit); 259