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 int charge_type; 48 }; 49 50 static int apple_mfi_fc_set_charge_type(struct mfi_device *mfi, 51 const union power_supply_propval *val) 52 { 53 int current_ma; 54 int retval; 55 __u8 request_type; 56 57 if (mfi->charge_type == val->intval) { 58 dev_dbg(&mfi->udev->dev, "charge type %d already set\n", 59 mfi->charge_type); 60 return 0; 61 } 62 63 switch (val->intval) { 64 case POWER_SUPPLY_CHARGE_TYPE_TRICKLE: 65 current_ma = TRICKLE_CURRENT_MA; 66 break; 67 case POWER_SUPPLY_CHARGE_TYPE_FAST: 68 current_ma = FAST_CURRENT_MA; 69 break; 70 default: 71 return -EINVAL; 72 } 73 74 request_type = USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE; 75 retval = usb_control_msg(mfi->udev, usb_sndctrlpipe(mfi->udev, 0), 76 0x40, /* Vendor‐defined power request */ 77 request_type, 78 current_ma, /* wValue, current offset */ 79 current_ma, /* wIndex, current offset */ 80 NULL, 0, USB_CTRL_GET_TIMEOUT); 81 if (retval) { 82 dev_dbg(&mfi->udev->dev, "retval = %d\n", retval); 83 return retval; 84 } 85 86 mfi->charge_type = val->intval; 87 88 return 0; 89 } 90 91 static int apple_mfi_fc_get_property(struct power_supply *psy, 92 enum power_supply_property psp, 93 union power_supply_propval *val) 94 { 95 struct mfi_device *mfi = power_supply_get_drvdata(psy); 96 97 dev_dbg(&mfi->udev->dev, "prop: %d\n", psp); 98 99 switch (psp) { 100 case POWER_SUPPLY_PROP_CHARGE_TYPE: 101 val->intval = mfi->charge_type; 102 break; 103 case POWER_SUPPLY_PROP_SCOPE: 104 val->intval = POWER_SUPPLY_SCOPE_DEVICE; 105 break; 106 default: 107 return -ENODATA; 108 } 109 110 return 0; 111 } 112 113 static int apple_mfi_fc_set_property(struct power_supply *psy, 114 enum power_supply_property psp, 115 const union power_supply_propval *val) 116 { 117 struct mfi_device *mfi = power_supply_get_drvdata(psy); 118 int ret; 119 120 dev_dbg(&mfi->udev->dev, "prop: %d\n", psp); 121 122 ret = pm_runtime_get_sync(&mfi->udev->dev); 123 if (ret < 0) { 124 pm_runtime_put_noidle(&mfi->udev->dev); 125 return ret; 126 } 127 128 switch (psp) { 129 case POWER_SUPPLY_PROP_CHARGE_TYPE: 130 ret = apple_mfi_fc_set_charge_type(mfi, val); 131 break; 132 default: 133 ret = -EINVAL; 134 } 135 136 pm_runtime_mark_last_busy(&mfi->udev->dev); 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 int err; 182 183 if (!mfi_fc_match(udev)) 184 return -ENODEV; 185 186 mfi = kzalloc(sizeof(struct mfi_device), GFP_KERNEL); 187 if (!mfi) { 188 err = -ENOMEM; 189 goto error; 190 } 191 192 battery_cfg.drv_data = mfi; 193 194 mfi->charge_type = POWER_SUPPLY_CHARGE_TYPE_TRICKLE; 195 mfi->battery = power_supply_register(&udev->dev, 196 &apple_mfi_fc_desc, 197 &battery_cfg); 198 if (IS_ERR(mfi->battery)) { 199 dev_err(&udev->dev, "Can't register battery\n"); 200 err = PTR_ERR(mfi->battery); 201 goto error; 202 } 203 204 mfi->udev = usb_get_dev(udev); 205 dev_set_drvdata(&udev->dev, mfi); 206 207 return 0; 208 209 error: 210 kfree(mfi); 211 return err; 212 } 213 214 static void mfi_fc_disconnect(struct usb_device *udev) 215 { 216 struct mfi_device *mfi; 217 218 mfi = dev_get_drvdata(&udev->dev); 219 if (mfi->battery) 220 power_supply_unregister(mfi->battery); 221 dev_set_drvdata(&udev->dev, NULL); 222 usb_put_dev(mfi->udev); 223 kfree(mfi); 224 } 225 226 static struct usb_device_driver mfi_fc_driver = { 227 .name = "apple-mfi-fastcharge", 228 .probe = mfi_fc_probe, 229 .disconnect = mfi_fc_disconnect, 230 .id_table = mfi_fc_id_table, 231 .match = mfi_fc_match, 232 .generic_subclass = 1, 233 }; 234 235 static int __init mfi_fc_driver_init(void) 236 { 237 return usb_register_device_driver(&mfi_fc_driver, THIS_MODULE); 238 } 239 240 static void __exit mfi_fc_driver_exit(void) 241 { 242 usb_deregister_device_driver(&mfi_fc_driver); 243 } 244 245 module_init(mfi_fc_driver_init); 246 module_exit(mfi_fc_driver_exit); 247