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 return -ENOMEM; 189 190 battery_cfg.drv_data = mfi; 191 192 mfi->charge_type = POWER_SUPPLY_CHARGE_TYPE_TRICKLE; 193 mfi->battery = power_supply_register(&udev->dev, 194 &apple_mfi_fc_desc, 195 &battery_cfg); 196 if (IS_ERR(mfi->battery)) { 197 dev_err(&udev->dev, "Can't register battery\n"); 198 err = PTR_ERR(mfi->battery); 199 kfree(mfi); 200 return err; 201 } 202 203 mfi->udev = usb_get_dev(udev); 204 dev_set_drvdata(&udev->dev, mfi); 205 206 return 0; 207 } 208 209 static void mfi_fc_disconnect(struct usb_device *udev) 210 { 211 struct mfi_device *mfi; 212 213 mfi = dev_get_drvdata(&udev->dev); 214 if (mfi->battery) 215 power_supply_unregister(mfi->battery); 216 dev_set_drvdata(&udev->dev, NULL); 217 usb_put_dev(mfi->udev); 218 kfree(mfi); 219 } 220 221 static struct usb_device_driver mfi_fc_driver = { 222 .name = "apple-mfi-fastcharge", 223 .probe = mfi_fc_probe, 224 .disconnect = mfi_fc_disconnect, 225 .id_table = mfi_fc_id_table, 226 .match = mfi_fc_match, 227 .generic_subclass = 1, 228 }; 229 230 static int __init mfi_fc_driver_init(void) 231 { 232 return usb_register_device_driver(&mfi_fc_driver, THIS_MODULE); 233 } 234 235 static void __exit mfi_fc_driver_exit(void) 236 { 237 usb_deregister_device_driver(&mfi_fc_driver); 238 } 239 240 module_init(mfi_fc_driver_init); 241 module_exit(mfi_fc_driver_exit); 242