1 // SPDX-License-Identifier: GPL-2.0+
3 * Fast-charge control for Apple "MFi" devices
5 * Copyright (C) 2019 Bastien Nocera <hadess@hadess.net>
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>
14 MODULE_AUTHOR("Bastien Nocera <hadess@hadess.net>");
15 MODULE_DESCRIPTION("Fast-charge control for Apple \"MFi\" devices");
16 MODULE_LICENSE("GPL");
18 #define TRICKLE_CURRENT_MA 0
19 #define FAST_CURRENT_MA 2500
21 #define APPLE_VENDOR_ID 0x05ac /* Apple */
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"
27 * To distinguish an Apple device, a USB host can check the device
28 * descriptor of attached USB devices for the following fields:
30 * ■ Product ID: 0x12nn
32 * Those checks will be done in .match() and .probe().
35 static const struct usb_device_id mfi_fc_id_table
[] = {
36 { .idVendor
= APPLE_VENDOR_ID
,
37 .match_flags
= USB_DEVICE_ID_MATCH_VENDOR
},
41 MODULE_DEVICE_TABLE(usb
, mfi_fc_id_table
);
43 /* Driver-local specific stuff */
45 struct usb_device
*udev
;
46 struct power_supply
*battery
;
50 static int apple_mfi_fc_set_charge_type(struct mfi_device
*mfi
,
51 const union power_supply_propval
*val
)
57 if (mfi
->charge_type
== val
->intval
) {
58 dev_dbg(&mfi
->udev
->dev
, "charge type %d already set\n",
63 switch (val
->intval
) {
64 case POWER_SUPPLY_CHARGE_TYPE_TRICKLE
:
65 current_ma
= TRICKLE_CURRENT_MA
;
67 case POWER_SUPPLY_CHARGE_TYPE_FAST
:
68 current_ma
= FAST_CURRENT_MA
;
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 */
78 current_ma
, /* wValue, current offset */
79 current_ma
, /* wIndex, current offset */
80 NULL
, 0, USB_CTRL_GET_TIMEOUT
);
82 dev_dbg(&mfi
->udev
->dev
, "retval = %d\n", retval
);
86 mfi
->charge_type
= val
->intval
;
91 static int apple_mfi_fc_get_property(struct power_supply
*psy
,
92 enum power_supply_property psp
,
93 union power_supply_propval
*val
)
95 struct mfi_device
*mfi
= power_supply_get_drvdata(psy
);
97 dev_dbg(&mfi
->udev
->dev
, "prop: %d\n", psp
);
100 case POWER_SUPPLY_PROP_CHARGE_TYPE
:
101 val
->intval
= mfi
->charge_type
;
103 case POWER_SUPPLY_PROP_SCOPE
:
104 val
->intval
= POWER_SUPPLY_SCOPE_DEVICE
;
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
)
117 struct mfi_device
*mfi
= power_supply_get_drvdata(psy
);
120 dev_dbg(&mfi
->udev
->dev
, "prop: %d\n", psp
);
122 ret
= pm_runtime_get_sync(&mfi
->udev
->dev
);
124 pm_runtime_put_noidle(&mfi
->udev
->dev
);
129 case POWER_SUPPLY_PROP_CHARGE_TYPE
:
130 ret
= apple_mfi_fc_set_charge_type(mfi
, val
);
136 pm_runtime_mark_last_busy(&mfi
->udev
->dev
);
137 pm_runtime_put_autosuspend(&mfi
->udev
->dev
);
142 static int apple_mfi_fc_property_is_writeable(struct power_supply
*psy
,
143 enum power_supply_property psp
)
146 case POWER_SUPPLY_PROP_CHARGE_TYPE
:
153 static enum power_supply_property apple_mfi_fc_properties
[] = {
154 POWER_SUPPLY_PROP_CHARGE_TYPE
,
155 POWER_SUPPLY_PROP_SCOPE
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
168 static bool mfi_fc_match(struct usb_device
*udev
)
172 idProduct
= le16_to_cpu(udev
->descriptor
.idProduct
);
173 /* See comment above mfi_fc_id_table[] */
174 return (idProduct
>= 0x1200 && idProduct
<= 0x12ff);
177 static int mfi_fc_probe(struct usb_device
*udev
)
179 struct power_supply_config battery_cfg
= {};
180 struct mfi_device
*mfi
= NULL
;
183 if (!mfi_fc_match(udev
))
186 mfi
= kzalloc(sizeof(struct mfi_device
), GFP_KERNEL
);
190 battery_cfg
.drv_data
= mfi
;
192 mfi
->charge_type
= POWER_SUPPLY_CHARGE_TYPE_TRICKLE
;
193 mfi
->battery
= power_supply_register(&udev
->dev
,
196 if (IS_ERR(mfi
->battery
)) {
197 dev_err(&udev
->dev
, "Can't register battery\n");
198 err
= PTR_ERR(mfi
->battery
);
203 mfi
->udev
= usb_get_dev(udev
);
204 dev_set_drvdata(&udev
->dev
, mfi
);
209 static void mfi_fc_disconnect(struct usb_device
*udev
)
211 struct mfi_device
*mfi
;
213 mfi
= dev_get_drvdata(&udev
->dev
);
215 power_supply_unregister(mfi
->battery
);
216 dev_set_drvdata(&udev
->dev
, NULL
);
217 usb_put_dev(mfi
->udev
);
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,
230 static int __init
mfi_fc_driver_init(void)
232 return usb_register_device_driver(&mfi_fc_driver
, THIS_MODULE
);
235 static void __exit
mfi_fc_driver_exit(void)
237 usb_deregister_device_driver(&mfi_fc_driver
);
240 module_init(mfi_fc_driver_init
);
241 module_exit(mfi_fc_driver_exit
);