Message ID | 6c594cc66fd06b575b04cc8bb0fe0374d0501d4d.1459494744.git.baolin.wang@linaro.org (mailing list archive) |
---|---|
State | Not Applicable, archived |
Headers | show |
On Fri, Apr 01, 2016 at 03:21:49PM +0800, Baolin Wang wrote: > + > +int devm_usb_charger_register(struct device *dev, > + struct usb_charger *uchger) > +{ > + struct usb_charger **ptr; > + int ret; > + > + ptr = devres_alloc(devm_uchger_dev_unreg, sizeof(*ptr), GFP_KERNEL); > + if (!ptr) > + return -ENOMEM; > + > + ret = usb_charger_register(dev, uchger); > + if (ret) { > + devres_free(ptr); > + return ret; > + } > + > + *ptr = uchger; > + devres_add(dev, ptr); > + > + return 0; > +} > +EXPORT_SYMBOL_GPL(devm_usb_charger_register); When the above API is expected to call? Can we use the USB charger without USB gadget? > + > +int usb_charger_init(struct usb_gadget *ugadget) > +{ > + struct usb_charger *uchger; > + struct extcon_dev *edev; > + struct power_supply *psy; > + int ret; > + > + uchger = kzalloc(sizeof(struct usb_charger), GFP_KERNEL); > + if (!uchger) > + return -ENOMEM; > + > + uchger->type = UNKNOWN_TYPE; > + uchger->state = USB_CHARGER_DEFAULT; > + uchger->id = -1; > + > + if (ugadget->speed >= USB_SPEED_SUPER) > + uchger->cur_limit.sdp_cur_limit = DEFAULT_SDP_CUR_LIMIT_SS; > + else > + uchger->cur_limit.sdp_cur_limit = DEFAULT_SDP_CUR_LIMIT; We still haven't known bus speed here, it is better do it after setting configuration has finished. Peter > + uchger->cur_limit.dcp_cur_limit = DEFAULT_DCP_CUR_LIMIT; > + uchger->cur_limit.cdp_cur_limit = DEFAULT_CDP_CUR_LIMIT; > + uchger->cur_limit.aca_cur_limit = DEFAULT_ACA_CUR_LIMIT; > + > + mutex_init(&uchger->lock); > + RAW_INIT_NOTIFIER_HEAD(&uchger->uchger_nh); > + > + /* register a notifier on a extcon device if it is exsited */ > + edev = extcon_get_edev_by_phandle(ugadget->dev.parent, 0); > + if (!IS_ERR_OR_NULL(edev)) { > + uchger->extcon_dev = edev; > + uchger->extcon_nb.nb.notifier_call = usb_charger_plug_by_extcon; > + uchger->extcon_nb.uchger = uchger; > + extcon_register_notifier(edev, EXTCON_USB, > + &uchger->extcon_nb.nb); > + } > + > + /* to check if the usb charger is link to a power supply */ > + psy = devm_power_supply_get_by_phandle(ugadget->dev.parent, > + "power-supplies"); > + if (!IS_ERR_OR_NULL(psy)) > + uchger->psy = psy; > + else > + uchger->psy = NULL; > + > + /* register a notifier on a usb gadget device */ > + uchger->gadget = ugadget; > + uchger->old_gadget_state = ugadget->state; > + > + /* register a new usb charger */ > + ret = usb_charger_register(&ugadget->dev, uchger); > + if (ret) > + goto fail; > + > + return 0; > + > +fail: > + if (uchger->extcon_dev) > + extcon_unregister_notifier(uchger->extcon_dev, > + EXTCON_USB, &uchger->extcon_nb.nb); > + > + kfree(uchger); > + return ret; > +} > + > +int usb_charger_exit(struct usb_gadget *ugadget) > +{ > + return 0; > +} > + > +static int __init usb_charger_class_init(void) > +{ > + usb_charger_class = class_create(THIS_MODULE, "usb_charger"); > + if (IS_ERR(usb_charger_class)) { > + pr_err("couldn't create class\n"); > + return PTR_ERR(usb_charger_class); > + } > + > + return 0; > +} > + > +static void __exit usb_charger_class_exit(void) > +{ > + class_destroy(usb_charger_class); > +} > +subsys_initcall(usb_charger_class_init); > +module_exit(usb_charger_class_exit); > + > +MODULE_AUTHOR("Baolin Wang <baolin.wang@linaro.org>"); > +MODULE_DESCRIPTION("USB charger driver"); > +MODULE_LICENSE("GPL v2"); > diff --git a/include/linux/usb/charger.h b/include/linux/usb/charger.h > new file mode 100644 > index 0000000..1bf1d55 > --- /dev/null > +++ b/include/linux/usb/charger.h > @@ -0,0 +1,171 @@ > +#ifndef __LINUX_USB_CHARGER_H__ > +#define __LINUX_USB_CHARGER_H__ > + > +#include <uapi/linux/usb/ch9.h> > +#include <uapi/linux/usb/charger.h> > + > +/* Current limitation by charger type */ > +struct usb_charger_cur_limit { > + unsigned int sdp_cur_limit; > + unsigned int dcp_cur_limit; > + unsigned int cdp_cur_limit; > + unsigned int aca_cur_limit; > +}; > + > +struct usb_charger_nb { > + struct notifier_block nb; > + struct usb_charger *uchger; > +}; > + > +struct usb_charger { > + struct device dev; > + struct raw_notifier_head uchger_nh; > + /* protect the notifier head and charger */ > + struct mutex lock; > + int id; > + enum usb_charger_type type; > + enum usb_charger_state state; > + > + /* for supporting extcon usb gpio */ > + struct extcon_dev *extcon_dev; > + struct usb_charger_nb extcon_nb; > + > + /* for supporting usb gadget */ > + struct usb_gadget *gadget; > + enum usb_device_state old_gadget_state; > + > + /* for supporting power supply */ > + struct power_supply *psy; > + > + /* user can get charger type by implementing this callback */ > + enum usb_charger_type (*get_charger_type)(struct usb_charger *); > + /* > + * charger detection method can be implemented if you need to > + * manually detect the charger type. > + */ > + enum usb_charger_type (*charger_detect)(struct usb_charger *); > + > + /* current limitation */ > + struct usb_charger_cur_limit cur_limit; > +}; > + > +#ifdef CONFIG_USB_CHARGER > +extern struct usb_charger *usb_charger_find_by_name(const char *name); > + > +extern struct usb_charger *usb_charger_get(struct usb_charger *uchger); > +extern void usb_charger_put(struct usb_charger *uchger); > + > +extern int usb_charger_register_notify(struct usb_charger *uchger, > + struct notifier_block *nb); > +extern int usb_charger_unregister_notify(struct usb_charger *uchger, > + struct notifier_block *nb); > + > +extern int usb_charger_set_cur_limit(struct usb_charger *uchger, > + struct usb_charger_cur_limit *cur_limit_set); > +extern int usb_charger_set_cur_limit_by_type(struct usb_charger *uchger, > + enum usb_charger_type type, > + unsigned int cur_limit); > +extern unsigned int usb_charger_get_current(struct usb_charger *uchger); > + > +extern int usb_charger_plug_by_gadget(struct usb_gadget *gadget, > + unsigned long state); > +extern enum usb_charger_type usb_charger_get_type(struct usb_charger *uchger); > +extern int usb_charger_detect_type(struct usb_charger *uchger); > + > +extern void devm_usb_charger_unregister(struct device *dev, > + struct usb_charger *uchger); > +extern int devm_usb_charger_register(struct device *dev, > + struct usb_charger *uchger); > + > +extern int usb_charger_init(struct usb_gadget *ugadget); > +extern int usb_charger_exit(struct usb_gadget *ugadget); > +#else > +static inline struct usb_charger *usb_charger_find_by_name(const char *name) > +{ > + return ERR_PTR(-ENODEV); > +} > + > +static inline struct usb_charger *usb_charger_get(struct usb_charger *uchger) > +{ > + return NULL; > +} > + > +static inline void usb_charger_put(struct usb_charger *uchger) > +{ > +} > + > +static inline int > +usb_charger_register_notify(struct usb_charger *uchger, > + struct notifier_block *nb) > +{ > + return 0; > +} > + > +static inline int > +usb_charger_unregister_notify(struct usb_charger *uchger, > + struct notifier_block *nb) > +{ > + return 0; > +} > + > +static inline int > +usb_charger_set_cur_limit(struct usb_charger *uchger, > + struct usb_charger_cur_limit *cur_limit_set) > +{ > + return 0; > +} > + > +static inline int > +usb_charger_set_cur_limit_by_type(struct usb_charger *uchger, > + enum usb_charger_type type, > + unsigned int cur_limit) > +{ > + return 0; > +} > + > +static inline unsigned int > +usb_charger_get_current(struct usb_charger *uchger) > +{ > + return 0; > +} > + > +static inline enum usb_charger_type > +usb_charger_get_type(struct usb_charger *uchger) > +{ > + return UNKNOWN_TYPE; > +} > + > +static inline int usb_charger_detect_type(struct usb_charger *uchger) > +{ > + return 0; > +} > + > +static inline int > +usb_charger_plug_by_gadget(struct usb_gadget *gadget, unsigned long state) > +{ > + return 0; > +} > + > +static inline void devm_usb_charger_unregister(struct device *dev, > + struct usb_charger *uchger) > +{ > +} > + > +static inline int devm_usb_charger_register(struct device *dev, > + struct usb_charger *uchger) > +{ > + return 0; > +} > + > +static inline int usb_charger_init(struct usb_gadget *ugadget) > +{ > + return 0; > +} > + > +static inline int usb_charger_exit(struct usb_gadget *ugadget) > +{ > + return 0; > +} > +#endif > + > +#endif /* __LINUX_USB_CHARGER_H__ */ > diff --git a/include/uapi/linux/usb/charger.h b/include/uapi/linux/usb/charger.h > new file mode 100644 > index 0000000..3c56ec4 > --- /dev/null > +++ b/include/uapi/linux/usb/charger.h > @@ -0,0 +1,31 @@ > +/* > + * This file defines the USB charger type and state that are needed for > + * USB device APIs. > + */ > + > +#ifndef _UAPI__LINUX_USB_CHARGER_H > +#define _UAPI__LINUX_USB_CHARGER_H > + > +/* > + * USB charger type: > + * SDP (Standard Downstream Port) > + * DCP (Dedicated Charging Port) > + * CDP (Charging Downstream Port) > + * ACA (Accessory Charger Adapters) > + */ > +enum usb_charger_type { > + UNKNOWN_TYPE, > + SDP_TYPE, > + DCP_TYPE, > + CDP_TYPE, > + ACA_TYPE, > +}; > + > +/* USB charger state */ > +enum usb_charger_state { > + USB_CHARGER_DEFAULT, > + USB_CHARGER_PRESENT, > + USB_CHARGER_REMOVE, > +}; > + > +#endif /* _UAPI__LINUX_USB_CHARGER_H */ > -- > 1.7.9.5 > > -- > To unsubscribe from this list: send the line "unsubscribe linux-usb" in > the body of a message to majordomo@vger.kernel.org > More majordomo info at http://vger.kernel.org/majordomo-info.html
On 5 April 2016 at 15:56, Peter Chen <hzpeterchen@gmail.com> wrote: > On Fri, Apr 01, 2016 at 03:21:49PM +0800, Baolin Wang wrote: >> + >> +int devm_usb_charger_register(struct device *dev, >> + struct usb_charger *uchger) >> +{ >> + struct usb_charger **ptr; >> + int ret; >> + >> + ptr = devres_alloc(devm_uchger_dev_unreg, sizeof(*ptr), GFP_KERNEL); >> + if (!ptr) >> + return -ENOMEM; >> + >> + ret = usb_charger_register(dev, uchger); >> + if (ret) { >> + devres_free(ptr); >> + return ret; >> + } >> + >> + *ptr = uchger; >> + devres_add(dev, ptr); >> + >> + return 0; >> +} >> +EXPORT_SYMBOL_GPL(devm_usb_charger_register); > > When the above API is expected to call? Can we use the USB charger > without USB gadget? I think this is open for user to do their specific initialization for usb charger. That depends on how to initialize your usb charger structure. > >> + >> +int usb_charger_init(struct usb_gadget *ugadget) >> +{ >> + struct usb_charger *uchger; >> + struct extcon_dev *edev; >> + struct power_supply *psy; >> + int ret; >> + >> + uchger = kzalloc(sizeof(struct usb_charger), GFP_KERNEL); >> + if (!uchger) >> + return -ENOMEM; >> + >> + uchger->type = UNKNOWN_TYPE; >> + uchger->state = USB_CHARGER_DEFAULT; >> + uchger->id = -1; >> + >> + if (ugadget->speed >= USB_SPEED_SUPER) >> + uchger->cur_limit.sdp_cur_limit = DEFAULT_SDP_CUR_LIMIT_SS; >> + else >> + uchger->cur_limit.sdp_cur_limit = DEFAULT_SDP_CUR_LIMIT; > > We still haven't known bus speed here, it is better do it after > setting configuration has finished. OK. Make sense.
On Fri, Apr 01, 2016 at 03:21:49PM +0800, Baolin Wang wrote: + > +static struct attribute *usb_charger_attrs[] = { > + &dev_attr_sdp_current.attr, > + &dev_attr_dcp_current.attr, > + &dev_attr_cdp_current.attr, > + &dev_attr_aca_current.attr, > + &dev_attr_charger_type.attr, > + &dev_attr_charger_state.attr, > + NULL > +}; The user may only care about current limit, type and state, why they need to care what type's current limit, it is the usb charger framework handles, the framework judge the current according to charger type and USB state (connect/configured/suspended). Peter > +ATTRIBUTE_GROUPS(usb_charger); > + > +/* > + * usb_charger_find_by_name() - Get the usb charger device by name. > + * @name - usb charger device name. > + * > + * return the instance of usb charger device, the device must be > + * released with usb_charger_put(). > + */ > +struct usb_charger *usb_charger_find_by_name(const char *name) > +{ > + struct class_dev_iter iter; > + struct device *dev; > + > + if (WARN(!name, "can't work with NULL name")) > + return ERR_PTR(-EINVAL); > + > + /* Iterate all usb charger devices in the class */ > + class_dev_iter_init(&iter, usb_charger_class, NULL, NULL); > + while ((dev = class_dev_iter_next(&iter))) { > + if (!strcmp(dev_name(dev), name)) > + break; > + } > + class_dev_iter_exit(&iter); > + > + if (WARN(!dev, "can't find usb charger device")) > + return ERR_PTR(-ENODEV); > + > + return dev_to_uchger(dev); > +} > +EXPORT_SYMBOL_GPL(usb_charger_find_by_name); > + > +/* > + * usb_charger_get() - Reference a usb charger. > + * @uchger - usb charger > + */ > +struct usb_charger *usb_charger_get(struct usb_charger *uchger) > +{ > + return (uchger && get_device(&uchger->dev)) ? uchger : NULL; > +} > +EXPORT_SYMBOL_GPL(usb_charger_get); > + > +/* > + * usb_charger_put() - Dereference a usb charger. > + * @uchger - charger to release > + */ > +void usb_charger_put(struct usb_charger *uchger) > +{ > + if (uchger) > + put_device(&uchger->dev); > +} > +EXPORT_SYMBOL_GPL(usb_charger_put); > + > +/* > + * usb_charger_get_type() - get the usb charger type with lock protection. > + * @uchger - usb charger device > + * > + * Users can get the charger type by this safe API, rather than using the > + * usb_charger structure directly. > + */ > +enum usb_charger_type usb_charger_get_type(struct usb_charger *uchger) > +{ > + enum usb_charger_type type; > + > + mutex_lock(&uchger->lock); > + type = uchger->type; > + mutex_unlock(&uchger->lock); > + > + return type; > +} > +EXPORT_SYMBOL_GPL(usb_charger_get_type); > + > +/* > + * usb_charger_detect_type() - detect the charger type manually. > + * @uchger - usb charger device > + * > + * Note: You should ensure you need to detect the charger type manually on your > + * platform. > + * You should call it at the right gadget state to avoid affecting gadget > + * enumeration. > + */ > +int usb_charger_detect_type(struct usb_charger *uchger) > +{ > + enum usb_charger_type type; > + > + if (WARN(!uchger->charger_detect, > + "charger detection method should not be NULL")) > + return -EINVAL; > + > + type = uchger->charger_detect(uchger); > + > + mutex_lock(&uchger->lock); > + uchger->type = type; > + mutex_unlock(&uchger->lock); > + > + return 0; > +} > +EXPORT_SYMBOL_GPL(usb_charger_detect_type); > + > +/* > + * usb_charger_get_type_by_others() - Get the usb charger type by the callback > + * which is implemented by users. > + * @uchger - the usb charger device. > + * > + * Note: This function is just used for getting the charger type, not for > + * detecting charger type which might affect the DP/DM line when gadget is on > + * enumeration state. > + */ > +static enum usb_charger_type > +usb_charger_get_type_by_others(struct usb_charger *uchger) > +{ > + if (uchger->type != UNKNOWN_TYPE) > + return uchger->type; > + > + if (uchger->psy) { > + union power_supply_propval val; > + > + power_supply_get_property(uchger->psy, > + POWER_SUPPLY_PROP_CHARGE_TYPE, > + &val); > + switch (val.intval) { > + case POWER_SUPPLY_TYPE_USB: > + uchger->type = SDP_TYPE; > + break; > + case POWER_SUPPLY_TYPE_USB_DCP: > + uchger->type = DCP_TYPE; > + break; > + case POWER_SUPPLY_TYPE_USB_CDP: > + uchger->type = CDP_TYPE; > + break; > + case POWER_SUPPLY_TYPE_USB_ACA: > + uchger->type = ACA_TYPE; > + break; > + default: > + uchger->type = UNKNOWN_TYPE; > + break; > + } > + } else if (uchger->get_charger_type) { > + uchger->type = uchger->get_charger_type(uchger); > + } else { > + uchger->type = UNKNOWN_TYPE; > + } > + > + return uchger->type; > +} > + > +/* > + * usb_charger_set_cur_limit_by_type() - Set the current limitation > + * by charger type. > + * @uchger - the usb charger device. > + * @type - the usb charger type. > + * @cur_limit - the current limitation. > + */ > +int usb_charger_set_cur_limit_by_type(struct usb_charger *uchger, > + enum usb_charger_type type, > + unsigned int cur_limit) > +{ > + if (WARN(!uchger, "charger can not be NULL")) > + return -EINVAL; > + > + mutex_lock(&uchger->lock); > + switch (type) { > + case SDP_TYPE: > + uchger->cur_limit.sdp_cur_limit = cur_limit; > + break; > + case DCP_TYPE: > + uchger->cur_limit.dcp_cur_limit = cur_limit; > + break; > + case CDP_TYPE: > + uchger->cur_limit.cdp_cur_limit = cur_limit; > + break; > + case ACA_TYPE: > + uchger->cur_limit.aca_cur_limit = cur_limit; > + break; > + default: > + mutex_unlock(&uchger->lock); > + return -EINVAL; > + } > + > + mutex_unlock(&uchger->lock); > + return 0; > +} > +EXPORT_SYMBOL_GPL(usb_charger_set_cur_limit_by_type); > + > +/* > + * usb_charger_set_cur_limit() - Set the current limitation. > + * @uchger - the usb charger device. > + * @cur_limit_set - the current limitation. > + */ > +int usb_charger_set_cur_limit(struct usb_charger *uchger, > + struct usb_charger_cur_limit *cur_limit_set) > +{ > + if (WARN(!uchger || !cur_limit_set, "charger or setting can't be NULL")) > + return -EINVAL; > + > + mutex_lock(&uchger->lock); > + uchger->cur_limit.sdp_cur_limit = cur_limit_set->sdp_cur_limit; > + uchger->cur_limit.dcp_cur_limit = cur_limit_set->dcp_cur_limit; > + uchger->cur_limit.cdp_cur_limit = cur_limit_set->cdp_cur_limit; > + uchger->cur_limit.aca_cur_limit = cur_limit_set->aca_cur_limit; > + mutex_unlock(&uchger->lock); > + > + return 0; > +} > +EXPORT_SYMBOL_GPL(usb_charger_set_cur_limit); > + > +/* > + * usb_charger_get_cur_limit() - Get the current limitation by > + * different usb charger type. > + * @uchger - the usb charger device. > + * > + * return the current limitation to set. > + */ > +static unsigned int > +usb_charger_get_cur_limit(struct usb_charger *uchger) > +{ > + enum usb_charger_type uchger_type = > + usb_charger_get_type_by_others(uchger); > + unsigned int cur_limit; > + > + switch (uchger_type) { > + case SDP_TYPE: > + cur_limit = uchger->cur_limit.sdp_cur_limit; > + break; > + case DCP_TYPE: > + cur_limit = uchger->cur_limit.dcp_cur_limit; > + break; > + case CDP_TYPE: > + cur_limit = uchger->cur_limit.cdp_cur_limit; > + break; > + case ACA_TYPE: > + cur_limit = uchger->cur_limit.aca_cur_limit; > + break; > + default: > + return 0; > + } > + > + return cur_limit; > +} > + > +/* > + * usb_charger_get_current() - Get the charger current with lock protection. > + * @uchger - the usb charger device. > + * > + * Users should get the charger current by this safe API. > + */ > +unsigned int usb_charger_get_current(struct usb_charger *uchger) > +{ > + unsigned int cur; > + > + mutex_lock(&uchger->lock); > + cur = usb_charger_get_cur_limit(uchger); > + mutex_unlock(&uchger->lock); > + > + return cur; > +} > +EXPORT_SYMBOL_GPL(usb_charger_get_current); > + > +/* > + * usb_charger_register_notify() - Register a notifiee to get notified by > + * any attach status changes from the usb charger detection. > + * @uchger - the usb charger device which is monitored. > + * @nb - a notifier block to be registered. > + */ > +int usb_charger_register_notify(struct usb_charger *uchger, > + struct notifier_block *nb) > +{ > + int ret; > + > + if (WARN(!uchger || !nb, "charger or nb can not be NULL")) > + return -EINVAL; > + > + mutex_lock(&uchger->lock); > + ret = raw_notifier_chain_register(&uchger->uchger_nh, nb); > + > + /* Generate an initial notify so users start in the right state */ > + if (!ret) { > + usb_charger_get_type_by_others(uchger); > + raw_notifier_call_chain(&uchger->uchger_nh, > + usb_charger_get_cur_limit(uchger), > + uchger); > + } > + mutex_unlock(&uchger->lock); > + > + return ret; > +} > +EXPORT_SYMBOL_GPL(usb_charger_register_notify); > + > +/* > + * usb_charger_unregister_notify() - Unregister a notifiee from the usb charger. > + * @uchger - the usb charger device which is monitored. > + * @nb - a notifier block to be unregistered. > + */ > +int usb_charger_unregister_notify(struct usb_charger *uchger, > + struct notifier_block *nb) > +{ > + int ret; > + > + if (WARN(!uchger || !nb, "charger or nb can not be NULL")) > + return -EINVAL; > + > + mutex_lock(&uchger->lock); > + ret = raw_notifier_chain_unregister(&uchger->uchger_nh, nb); > + mutex_unlock(&uchger->lock); > + > + return ret; > +} > +EXPORT_SYMBOL_GPL(usb_charger_unregister_notify); > + > +/* > + * usb_charger_notifier_others() - It will notify other device registered > + * on usb charger when the usb charger state is changed. > + * @uchger - the usb charger device. > + * @state - the state of the usb charger. > + */ > +static void > +usb_charger_notify_others(struct usb_charger *uchger, > + enum usb_charger_state state) > +{ > + char uchger_state[50] = { 0 }; > + char *envp[] = { uchger_state, NULL }; > + > + mutex_lock(&uchger->lock); > + uchger->state = state; > + > + switch (state) { > + case USB_CHARGER_PRESENT: > + usb_charger_get_type_by_others(uchger); > + raw_notifier_call_chain(&uchger->uchger_nh, > + usb_charger_get_cur_limit(uchger), > + uchger); > + snprintf(uchger_state, ARRAY_SIZE(uchger_state), > + "USB_CHARGER_STATE=%s", "USB_CHARGER_PRESENT"); > + break; > + case USB_CHARGER_REMOVE: > + uchger->type = UNKNOWN_TYPE; > + raw_notifier_call_chain(&uchger->uchger_nh, 0, uchger); > + snprintf(uchger_state, ARRAY_SIZE(uchger_state), > + "USB_CHARGER_STATE=%s", "USB_CHARGER_REMOVE"); > + break; > + default: > + dev_warn(&uchger->dev, "Unknown USB charger state: %d\n", > + state); > + mutex_unlock(&uchger->lock); > + return; > + } > + > + kobject_uevent_env(&uchger->dev.kobj, KOBJ_CHANGE, envp); > + mutex_unlock(&uchger->lock); > +} > + > +/* > + * usb_charger_plug_by_extcon() - The notifier call function which is registered > + * on the extcon device. > + * @nb - the notifier block that notified by extcon device. > + * @state - the extcon device state. > + * @data - here specify a extcon device. > + * > + * return the notify flag. > + */ > +static int > +usb_charger_plug_by_extcon(struct notifier_block *nb, > + unsigned long state, void *data) > +{ > + struct usb_charger_nb *extcon_nb = > + container_of(nb, struct usb_charger_nb, nb); > + struct usb_charger *uchger = extcon_nb->uchger; > + enum usb_charger_state uchger_state; > + > + if (WARN(!uchger, "charger can not be NULL")) > + return NOTIFY_BAD; > + > + /* > + * Report event to power to setting the current limitation > + * for this usb charger when one usb charger is added or removed > + * with detecting by extcon device. > + */ > + if (state) > + uchger_state = USB_CHARGER_PRESENT; > + else > + uchger_state = USB_CHARGER_REMOVE; > + > + usb_charger_notify_others(uchger, uchger_state); > + > + return NOTIFY_OK; > +} > + > +/* > + * usb_charger_plug_by_gadget() - Set the usb charger current limitation > + * according to the usb gadget device state. > + * @gadget - the usb gadget device. > + * @state - the usb gadget state. > + */ > +int usb_charger_plug_by_gadget(struct usb_gadget *gadget, > + unsigned long state) > +{ > + return 0; > +} > +EXPORT_SYMBOL_GPL(usb_charger_plug_by_gadget); > + > +static int devm_uchger_dev_match(struct device *dev, void *res, void *data) > +{ > + struct usb_charger **r = res; > + > + if (WARN_ON(!r || !*r)) > + return 0; > + > + return *r == data; > +} > + > +static void usb_charger_release(struct device *dev) > +{ > + struct usb_charger *uchger = dev_get_drvdata(dev); > + > + kfree(uchger); > +} > + > +/* > + * usb_charger_unregister() - Unregister a usb charger device. > + * @uchger - the usb charger to be unregistered. > + */ > +static int usb_charger_unregister(struct usb_charger *uchger) > +{ > + if (WARN(!uchger, "charger can not be NULL")) > + return -EINVAL; > + > + device_unregister(&uchger->dev); > + return 0; > +} > + > +static void devm_uchger_dev_unreg(struct device *dev, void *res) > +{ > + usb_charger_unregister(*(struct usb_charger **)res); > +} > + > +void devm_usb_charger_unregister(struct device *dev, > + struct usb_charger *uchger) > +{ > + devres_release(dev, devm_uchger_dev_unreg, > + devm_uchger_dev_match, uchger); > +} > +EXPORT_SYMBOL_GPL(devm_usb_charger_unregister); > + > +/* > + * usb_charger_register() - Register a new usb charger device > + * which is created by the usb charger framework. > + * @parent - the parent device of the new usb charger. > + * @uchger - the new usb charger device. > + */ > +static int usb_charger_register(struct device *parent, > + struct usb_charger *uchger) > +{ > + int ret; > + > + if (WARN(!uchger, "charger can not be NULL")) > + return -EINVAL; > + > + uchger->dev.parent = parent; > + uchger->dev.release = usb_charger_release; > + uchger->dev.class = usb_charger_class; > + uchger->dev.groups = usb_charger_groups; > + > + ret = ida_simple_get(&usb_charger_ida, 0, 0, GFP_KERNEL); > + if (ret < 0) > + goto fail_ida; > + > + uchger->id = ret; > + dev_set_name(&uchger->dev, "usb-charger.%d", uchger->id); > + dev_set_drvdata(&uchger->dev, uchger); > + > + ret = device_register(&uchger->dev); > + if (ret) > + goto fail_register; > + > + return 0; > + > +fail_register: > + put_device(&uchger->dev); > + ida_simple_remove(&usb_charger_ida, uchger->id); > + uchger->id = -1; > +fail_ida: > + dev_err(parent, "Failed to register usb charger: %d\n", ret); > + return ret; > +} > + > +int devm_usb_charger_register(struct device *dev, > + struct usb_charger *uchger) > +{ > + struct usb_charger **ptr; > + int ret; > + > + ptr = devres_alloc(devm_uchger_dev_unreg, sizeof(*ptr), GFP_KERNEL); > + if (!ptr) > + return -ENOMEM; > + > + ret = usb_charger_register(dev, uchger); > + if (ret) { > + devres_free(ptr); > + return ret; > + } > + > + *ptr = uchger; > + devres_add(dev, ptr); > + > + return 0; > +} > +EXPORT_SYMBOL_GPL(devm_usb_charger_register); > + > +int usb_charger_init(struct usb_gadget *ugadget) > +{ > + struct usb_charger *uchger; > + struct extcon_dev *edev; > + struct power_supply *psy; > + int ret; > + > + uchger = kzalloc(sizeof(struct usb_charger), GFP_KERNEL); > + if (!uchger) > + return -ENOMEM; > + > + uchger->type = UNKNOWN_TYPE; > + uchger->state = USB_CHARGER_DEFAULT; > + uchger->id = -1; > + > + if (ugadget->speed >= USB_SPEED_SUPER) > + uchger->cur_limit.sdp_cur_limit = DEFAULT_SDP_CUR_LIMIT_SS; > + else > + uchger->cur_limit.sdp_cur_limit = DEFAULT_SDP_CUR_LIMIT; > + uchger->cur_limit.dcp_cur_limit = DEFAULT_DCP_CUR_LIMIT; > + uchger->cur_limit.cdp_cur_limit = DEFAULT_CDP_CUR_LIMIT; > + uchger->cur_limit.aca_cur_limit = DEFAULT_ACA_CUR_LIMIT; > + > + mutex_init(&uchger->lock); > + RAW_INIT_NOTIFIER_HEAD(&uchger->uchger_nh); > + > + /* register a notifier on a extcon device if it is exsited */ > + edev = extcon_get_edev_by_phandle(ugadget->dev.parent, 0); > + if (!IS_ERR_OR_NULL(edev)) { > + uchger->extcon_dev = edev; > + uchger->extcon_nb.nb.notifier_call = usb_charger_plug_by_extcon; > + uchger->extcon_nb.uchger = uchger; > + extcon_register_notifier(edev, EXTCON_USB, > + &uchger->extcon_nb.nb); > + } > + > + /* to check if the usb charger is link to a power supply */ > + psy = devm_power_supply_get_by_phandle(ugadget->dev.parent, > + "power-supplies"); > + if (!IS_ERR_OR_NULL(psy)) > + uchger->psy = psy; > + else > + uchger->psy = NULL; > + > + /* register a notifier on a usb gadget device */ > + uchger->gadget = ugadget; > + uchger->old_gadget_state = ugadget->state; > + > + /* register a new usb charger */ > + ret = usb_charger_register(&ugadget->dev, uchger); > + if (ret) > + goto fail; > + > + return 0; > + > +fail: > + if (uchger->extcon_dev) > + extcon_unregister_notifier(uchger->extcon_dev, > + EXTCON_USB, &uchger->extcon_nb.nb); > + > + kfree(uchger); > + return ret; > +} > + > +int usb_charger_exit(struct usb_gadget *ugadget) > +{ > + return 0; > +} > + > +static int __init usb_charger_class_init(void) > +{ > + usb_charger_class = class_create(THIS_MODULE, "usb_charger"); > + if (IS_ERR(usb_charger_class)) { > + pr_err("couldn't create class\n"); > + return PTR_ERR(usb_charger_class); > + } > + > + return 0; > +} > + > +static void __exit usb_charger_class_exit(void) > +{ > + class_destroy(usb_charger_class); > +} > +subsys_initcall(usb_charger_class_init); > +module_exit(usb_charger_class_exit); > + > +MODULE_AUTHOR("Baolin Wang <baolin.wang@linaro.org>"); > +MODULE_DESCRIPTION("USB charger driver"); > +MODULE_LICENSE("GPL v2"); > diff --git a/include/linux/usb/charger.h b/include/linux/usb/charger.h > new file mode 100644 > index 0000000..1bf1d55 > --- /dev/null > +++ b/include/linux/usb/charger.h > @@ -0,0 +1,171 @@ > +#ifndef __LINUX_USB_CHARGER_H__ > +#define __LINUX_USB_CHARGER_H__ > + > +#include <uapi/linux/usb/ch9.h> > +#include <uapi/linux/usb/charger.h> > + > +/* Current limitation by charger type */ > +struct usb_charger_cur_limit { > + unsigned int sdp_cur_limit; > + unsigned int dcp_cur_limit; > + unsigned int cdp_cur_limit; > + unsigned int aca_cur_limit; > +}; > + > +struct usb_charger_nb { > + struct notifier_block nb; > + struct usb_charger *uchger; > +}; > + > +struct usb_charger { > + struct device dev; > + struct raw_notifier_head uchger_nh; > + /* protect the notifier head and charger */ > + struct mutex lock; > + int id; > + enum usb_charger_type type; > + enum usb_charger_state state; > + > + /* for supporting extcon usb gpio */ > + struct extcon_dev *extcon_dev; > + struct usb_charger_nb extcon_nb; > + > + /* for supporting usb gadget */ > + struct usb_gadget *gadget; > + enum usb_device_state old_gadget_state; > + > + /* for supporting power supply */ > + struct power_supply *psy; > + > + /* user can get charger type by implementing this callback */ > + enum usb_charger_type (*get_charger_type)(struct usb_charger *); > + /* > + * charger detection method can be implemented if you need to > + * manually detect the charger type. > + */ > + enum usb_charger_type (*charger_detect)(struct usb_charger *); > + > + /* current limitation */ > + struct usb_charger_cur_limit cur_limit; > +}; > + > +#ifdef CONFIG_USB_CHARGER > +extern struct usb_charger *usb_charger_find_by_name(const char *name); > + > +extern struct usb_charger *usb_charger_get(struct usb_charger *uchger); > +extern void usb_charger_put(struct usb_charger *uchger); > + > +extern int usb_charger_register_notify(struct usb_charger *uchger, > + struct notifier_block *nb); > +extern int usb_charger_unregister_notify(struct usb_charger *uchger, > + struct notifier_block *nb); > + > +extern int usb_charger_set_cur_limit(struct usb_charger *uchger, > + struct usb_charger_cur_limit *cur_limit_set); > +extern int usb_charger_set_cur_limit_by_type(struct usb_charger *uchger, > + enum usb_charger_type type, > + unsigned int cur_limit); > +extern unsigned int usb_charger_get_current(struct usb_charger *uchger); > + > +extern int usb_charger_plug_by_gadget(struct usb_gadget *gadget, > + unsigned long state); > +extern enum usb_charger_type usb_charger_get_type(struct usb_charger *uchger); > +extern int usb_charger_detect_type(struct usb_charger *uchger); > + > +extern void devm_usb_charger_unregister(struct device *dev, > + struct usb_charger *uchger); > +extern int devm_usb_charger_register(struct device *dev, > + struct usb_charger *uchger); > + > +extern int usb_charger_init(struct usb_gadget *ugadget); > +extern int usb_charger_exit(struct usb_gadget *ugadget); > +#else > +static inline struct usb_charger *usb_charger_find_by_name(const char *name) > +{ > + return ERR_PTR(-ENODEV); > +} > + > +static inline struct usb_charger *usb_charger_get(struct usb_charger *uchger) > +{ > + return NULL; > +} > + > +static inline void usb_charger_put(struct usb_charger *uchger) > +{ > +} > + > +static inline int > +usb_charger_register_notify(struct usb_charger *uchger, > + struct notifier_block *nb) > +{ > + return 0; > +} > + > +static inline int > +usb_charger_unregister_notify(struct usb_charger *uchger, > + struct notifier_block *nb) > +{ > + return 0; > +} > + > +static inline int > +usb_charger_set_cur_limit(struct usb_charger *uchger, > + struct usb_charger_cur_limit *cur_limit_set) > +{ > + return 0; > +} > + > +static inline int > +usb_charger_set_cur_limit_by_type(struct usb_charger *uchger, > + enum usb_charger_type type, > + unsigned int cur_limit) > +{ > + return 0; > +} > + > +static inline unsigned int > +usb_charger_get_current(struct usb_charger *uchger) > +{ > + return 0; > +} > + > +static inline enum usb_charger_type > +usb_charger_get_type(struct usb_charger *uchger) > +{ > + return UNKNOWN_TYPE; > +} > + > +static inline int usb_charger_detect_type(struct usb_charger *uchger) > +{ > + return 0; > +} > + > +static inline int > +usb_charger_plug_by_gadget(struct usb_gadget *gadget, unsigned long state) > +{ > + return 0; > +} > + > +static inline void devm_usb_charger_unregister(struct device *dev, > + struct usb_charger *uchger) > +{ > +} > + > +static inline int devm_usb_charger_register(struct device *dev, > + struct usb_charger *uchger) > +{ > + return 0; > +} > + > +static inline int usb_charger_init(struct usb_gadget *ugadget) > +{ > + return 0; > +} > + > +static inline int usb_charger_exit(struct usb_gadget *ugadget) > +{ > + return 0; > +} > +#endif > + > +#endif /* __LINUX_USB_CHARGER_H__ */ > diff --git a/include/uapi/linux/usb/charger.h b/include/uapi/linux/usb/charger.h > new file mode 100644 > index 0000000..3c56ec4 > --- /dev/null > +++ b/include/uapi/linux/usb/charger.h > @@ -0,0 +1,31 @@ > +/* > + * This file defines the USB charger type and state that are needed for > + * USB device APIs. > + */ > + > +#ifndef _UAPI__LINUX_USB_CHARGER_H > +#define _UAPI__LINUX_USB_CHARGER_H > + > +/* > + * USB charger type: > + * SDP (Standard Downstream Port) > + * DCP (Dedicated Charging Port) > + * CDP (Charging Downstream Port) > + * ACA (Accessory Charger Adapters) > + */ > +enum usb_charger_type { > + UNKNOWN_TYPE, > + SDP_TYPE, > + DCP_TYPE, > + CDP_TYPE, > + ACA_TYPE, > +}; > + > +/* USB charger state */ > +enum usb_charger_state { > + USB_CHARGER_DEFAULT, > + USB_CHARGER_PRESENT, > + USB_CHARGER_REMOVE, > +}; > + > +#endif /* _UAPI__LINUX_USB_CHARGER_H */ > -- > 1.7.9.5 > > -- > To unsubscribe from this list: send the line "unsubscribe linux-usb" in > the body of a message to majordomo@vger.kernel.org > More majordomo info at http://vger.kernel.org/majordomo-info.html
Peter Chen <hzpeterchen@gmail.com> writes: > On Fri, Apr 01, 2016 at 03:21:49PM +0800, Baolin Wang wrote: > + >> +static struct attribute *usb_charger_attrs[] = { >> + &dev_attr_sdp_current.attr, >> + &dev_attr_dcp_current.attr, >> + &dev_attr_cdp_current.attr, >> + &dev_attr_aca_current.attr, >> + &dev_attr_charger_type.attr, >> + &dev_attr_charger_state.attr, >> + NULL >> +}; > > The user may only care about current limit, type and state, why they > need to care what type's current limit, it is the usb charger > framework handles, the framework judge the current according to > charger type and USB state (connect/configured/suspended). it might be useful if we want to know that $this charger doesn't really give us as much current as it advertises.
On Wed, Apr 06, 2016 at 10:38:23AM +0300, Felipe Balbi wrote: > Peter Chen <hzpeterchen@gmail.com> writes: > > On Fri, Apr 01, 2016 at 03:21:49PM +0800, Baolin Wang wrote: > > + > >> +static struct attribute *usb_charger_attrs[] = { > >> + &dev_attr_sdp_current.attr, > >> + &dev_attr_dcp_current.attr, > >> + &dev_attr_cdp_current.attr, > >> + &dev_attr_aca_current.attr, > >> + &dev_attr_charger_type.attr, > >> + &dev_attr_charger_state.attr, > >> + NULL > >> +}; > > > > The user may only care about current limit, type and state, why they > > need to care what type's current limit, it is the usb charger > > framework handles, the framework judge the current according to > > charger type and USB state (connect/configured/suspended). > > it might be useful if we want to know that $this charger doesn't really > give us as much current as it advertises. > As my understanding, the current limit is dynamic value, it should report the value the charger supports now, eg, it connects SDP, but the host is suspended now, then the value should be 2mA.
Peter Chen <hzpeterchen@gmail.com> writes: > On Wed, Apr 06, 2016 at 10:38:23AM +0300, Felipe Balbi wrote: >> Peter Chen <hzpeterchen@gmail.com> writes: >> > On Fri, Apr 01, 2016 at 03:21:49PM +0800, Baolin Wang wrote: >> > + >> >> +static struct attribute *usb_charger_attrs[] = { >> >> + &dev_attr_sdp_current.attr, >> >> + &dev_attr_dcp_current.attr, >> >> + &dev_attr_cdp_current.attr, >> >> + &dev_attr_aca_current.attr, >> >> + &dev_attr_charger_type.attr, >> >> + &dev_attr_charger_state.attr, >> >> + NULL >> >> +}; >> > >> > The user may only care about current limit, type and state, why they >> > need to care what type's current limit, it is the usb charger >> > framework handles, the framework judge the current according to >> > charger type and USB state (connect/configured/suspended). >> >> it might be useful if we want to know that $this charger doesn't really >> give us as much current as it advertises. >> > > As my understanding, the current limit is dynamic value, it should > report the value the charger supports now, eg, it connects SDP, but > the host is suspended now, then the value should be 2mA. yes, and that's the limit. Now consider we connect to DCP or CDP and limit is 2000mA but we're charging at 1000mA ;-)
On Wed, Apr 06, 2016 at 11:05:26AM +0300, Felipe Balbi wrote: > Peter Chen <hzpeterchen@gmail.com> writes: > > On Wed, Apr 06, 2016 at 10:38:23AM +0300, Felipe Balbi wrote: > >> Peter Chen <hzpeterchen@gmail.com> writes: > >> > On Fri, Apr 01, 2016 at 03:21:49PM +0800, Baolin Wang wrote: > >> > + > >> >> +static struct attribute *usb_charger_attrs[] = { > >> >> + &dev_attr_sdp_current.attr, > >> >> + &dev_attr_dcp_current.attr, > >> >> + &dev_attr_cdp_current.attr, > >> >> + &dev_attr_aca_current.attr, > >> >> + &dev_attr_charger_type.attr, > >> >> + &dev_attr_charger_state.attr, > >> >> + NULL > >> >> +}; > >> > > >> > The user may only care about current limit, type and state, why they > >> > need to care what type's current limit, it is the usb charger > >> > framework handles, the framework judge the current according to > >> > charger type and USB state (connect/configured/suspended). > >> > >> it might be useful if we want to know that $this charger doesn't really > >> give us as much current as it advertises. > >> > > > > As my understanding, the current limit is dynamic value, it should > > report the value the charger supports now, eg, it connects SDP, but > > the host is suspended now, then the value should be 2mA. > > yes, and that's the limit. Now consider we connect to DCP or CDP and > limit is 2000mA but we're charging at 1000mA ;-) > Does the user need to know the $this charger limit? Don't they only care about the current charging value? I have a USB cable which can show charging current value, it changes from time to time, when it connects to host pc, it shows 430mA; when it connects to dedicated charger, it shows 1000mA.
On Wed, Apr 06, 2016 at 11:05:26AM +0300, Felipe Balbi wrote: > Peter Chen <hzpeterchen@gmail.com> writes: > > On Wed, Apr 06, 2016 at 10:38:23AM +0300, Felipe Balbi wrote: > >> Peter Chen <hzpeterchen@gmail.com> writes: > >> > On Fri, Apr 01, 2016 at 03:21:49PM +0800, Baolin Wang wrote: > >> > + > >> >> +static struct attribute *usb_charger_attrs[] = { > >> >> + &dev_attr_sdp_current.attr, > >> >> + &dev_attr_dcp_current.attr, > >> >> + &dev_attr_cdp_current.attr, > >> >> + &dev_attr_aca_current.attr, > >> >> + &dev_attr_charger_type.attr, > >> >> + &dev_attr_charger_state.attr, > >> >> + NULL > >> >> +}; > >> > > >> > The user may only care about current limit, type and state, why they > >> > need to care what type's current limit, it is the usb charger > >> > framework handles, the framework judge the current according to > >> > charger type and USB state (connect/configured/suspended). > >> > >> it might be useful if we want to know that $this charger doesn't really > >> give us as much current as it advertises. > >> > > > > As my understanding, the current limit is dynamic value, it should > > report the value the charger supports now, eg, it connects SDP, but > > the host is suspended now, then the value should be 2mA. > > yes, and that's the limit. Now consider we connect to DCP or CDP and > limit is 2000mA but we're charging at 1000mA ;-) > The user doesn't need to know the value which spec designs.
Hi > -----Original Message----- > From: linux-usb-owner@vger.kernel.org [mailto:linux-usb- > owner@vger.kernel.org] On Behalf Of Baolin Wang > Sent: Friday, April 01, 2016 3:22 PM > To: balbi@kernel.org; gregkh@linuxfoundation.org; sre@kernel.org; > dbaryshkov@gmail.com; dwmw2@infradead.org > Cc: peter.chen@freescale.com; stern@rowland.harvard.edu; > r.baldyga@samsung.com; yoshihiro.shimoda.uh@renesas.com; > lee.jones@linaro.org; broonie@kernel.org; > ckeepax@opensource.wolfsonmicro.com; patches@opensource.wolfsonmicro.com; > baolin.wang@linaro.org; linux-pm@vger.kernel.org; linux- > usb@vger.kernel.org; device-mainlining@lists.linuxfoundation.org; linux- > kernel@vger.kernel.org > Subject: [PATCH v9 1/4] gadget: Introduce the usb charger framework > ... > +/* > + * usb_charger_get_type() - get the usb charger type with lock protection. > + * @uchger - usb charger device > + * > + * Users can get the charger type by this safe API, rather than using > +the > + * usb_charger structure directly. > + */ > +enum usb_charger_type usb_charger_get_type(struct usb_charger *uchger) > +{ > + enum usb_charger_type type; > + > + mutex_lock(&uchger->lock); > + type = uchger->type; > + mutex_unlock(&uchger->lock); > + > + return type; > +} > +EXPORT_SYMBOL_GPL(usb_charger_get_type); > + > +/* > + * usb_charger_detect_type() - detect the charger type manually. > + * @uchger - usb charger device > + * > + * Note: You should ensure you need to detect the charger type manually > +on your > + * platform. > + * You should call it at the right gadget state to avoid affecting > +gadget > + * enumeration. > + */ > +int usb_charger_detect_type(struct usb_charger *uchger) { > + enum usb_charger_type type; > + > + if (WARN(!uchger->charger_detect, > + "charger detection method should not be NULL")) > + return -EINVAL; > + > + type = uchger->charger_detect(uchger); > + > + mutex_lock(&uchger->lock); > + uchger->type = type; > + mutex_unlock(&uchger->lock); > + > + return 0; > +} > +EXPORT_SYMBOL_GPL(usb_charger_detect_type); > + > +/* > + * usb_charger_get_type_by_others() - Get the usb charger type by the > +callback > + * which is implemented by users. > + * @uchger - the usb charger device. > + * > + * Note: This function is just used for getting the charger type, not > +for > + * detecting charger type which might affect the DP/DM line when gadget > +is on > + * enumeration state. > + */ > +static enum usb_charger_type > +usb_charger_get_type_by_others(struct usb_charger *uchger) { > + if (uchger->type != UNKNOWN_TYPE) > + return uchger->type; > + > + if (uchger->psy) { > + union power_supply_propval val; > + > + power_supply_get_property(uchger->psy, > + POWER_SUPPLY_PROP_CHARGE_TYPE, > + &val); > + switch (val.intval) { > + case POWER_SUPPLY_TYPE_USB: > + uchger->type = SDP_TYPE; > + break; > + case POWER_SUPPLY_TYPE_USB_DCP: > + uchger->type = DCP_TYPE; > + break; > + case POWER_SUPPLY_TYPE_USB_CDP: > + uchger->type = CDP_TYPE; > + break; > + case POWER_SUPPLY_TYPE_USB_ACA: > + uchger->type = ACA_TYPE; > + break; > + default: > + uchger->type = UNKNOWN_TYPE; > + break; > + } > + } else if (uchger->get_charger_type) { > + uchger->type = uchger->get_charger_type(uchger); > + } else { > + uchger->type = UNKNOWN_TYPE; > + } > + > + return uchger->type; > +} > + I think we may don't need this usb_charger_get_type_by_others(). "uchger->type" is set in one place is enough, that is: by uchger->charger_detect() in usb_charger_detect_type(), then usb_charger_get_type_by_others() is replaced by usb_charger_get_type(). uchger->charger_detect() can have diff implementations no matter what kind of mechanism is used, for your PMIC case, you can just directly get the type value by power_supply_get_property(); with that, we can have one central place to set uchger->type. After uchger->type is set, charger type detection is no need to be involved until charger type changes. Then next question is where is to call usb_charger_detect_type(), We need make sure it finished before usb gadget connect. Ideal is with your framework, diff users only need implement uchger->charger_detect(). :) Li Jun > -- > To unsubscribe from this list: send the line "unsubscribe linux-usb" in > the body of a message to majordomo@vger.kernel.org More majordomo info at > http://vger.kernel.org/majordomo-info.html -- To unsubscribe from this list: send the line "unsubscribe linux-pm" in the body of a message to majordomo@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html
Hi, Peter Chen <hzpeterchen@gmail.com> writes: > On Wed, Apr 06, 2016 at 11:05:26AM +0300, Felipe Balbi wrote: >> Peter Chen <hzpeterchen@gmail.com> writes: >> > On Wed, Apr 06, 2016 at 10:38:23AM +0300, Felipe Balbi wrote: >> >> Peter Chen <hzpeterchen@gmail.com> writes: >> >> > On Fri, Apr 01, 2016 at 03:21:49PM +0800, Baolin Wang wrote: >> >> > + >> >> >> +static struct attribute *usb_charger_attrs[] = { >> >> >> + &dev_attr_sdp_current.attr, >> >> >> + &dev_attr_dcp_current.attr, >> >> >> + &dev_attr_cdp_current.attr, >> >> >> + &dev_attr_aca_current.attr, >> >> >> + &dev_attr_charger_type.attr, >> >> >> + &dev_attr_charger_state.attr, >> >> >> + NULL >> >> >> +}; >> >> > >> >> > The user may only care about current limit, type and state, why they >> >> > need to care what type's current limit, it is the usb charger >> >> > framework handles, the framework judge the current according to >> >> > charger type and USB state (connect/configured/suspended). >> >> >> >> it might be useful if we want to know that $this charger doesn't really >> >> give us as much current as it advertises. >> >> >> > >> > As my understanding, the current limit is dynamic value, it should >> > report the value the charger supports now, eg, it connects SDP, but >> > the host is suspended now, then the value should be 2mA. >> >> yes, and that's the limit. Now consider we connect to DCP or CDP and >> limit is 2000mA but we're charging at 1000mA ;-) >> > > Does the user need to know the $this charger limit? Don't they only > care about the current charging value? I have a USB cable which can Why not ? UI might want to change the color of the battery charging icon if we're charging @ 2000mA or @ 1000mA to give some visual feedback as to "how fast" battery is supposed to be charged. > show charging current value, it changes from time to time, when it > connects to host pc, it shows 430mA; when it connects to dedicated > charger, it shows 1000mA. good for you, now what does that have to do with $subject ?
Hi, Peter Chen <hzpeterchen@gmail.com> writes: > On Wed, Apr 06, 2016 at 11:05:26AM +0300, Felipe Balbi wrote: >> Peter Chen <hzpeterchen@gmail.com> writes: >> > On Wed, Apr 06, 2016 at 10:38:23AM +0300, Felipe Balbi wrote: >> >> Peter Chen <hzpeterchen@gmail.com> writes: >> >> > On Fri, Apr 01, 2016 at 03:21:49PM +0800, Baolin Wang wrote: >> >> > + >> >> >> +static struct attribute *usb_charger_attrs[] = { >> >> >> + &dev_attr_sdp_current.attr, >> >> >> + &dev_attr_dcp_current.attr, >> >> >> + &dev_attr_cdp_current.attr, >> >> >> + &dev_attr_aca_current.attr, >> >> >> + &dev_attr_charger_type.attr, >> >> >> + &dev_attr_charger_state.attr, >> >> >> + NULL >> >> >> +}; >> >> > >> >> > The user may only care about current limit, type and state, why they >> >> > need to care what type's current limit, it is the usb charger >> >> > framework handles, the framework judge the current according to >> >> > charger type and USB state (connect/configured/suspended). >> >> >> >> it might be useful if we want to know that $this charger doesn't really >> >> give us as much current as it advertises. >> >> >> > >> > As my understanding, the current limit is dynamic value, it should >> > report the value the charger supports now, eg, it connects SDP, but >> > the host is suspended now, then the value should be 2mA. >> >> yes, and that's the limit. Now consider we connect to DCP or CDP and >> limit is 2000mA but we're charging at 1000mA ;-) >> > > The user doesn't need to know the value which spec designs. because... ?
On 6 April 2016 at 16:26, Jun Li <jun.li@nxp.com> wrote: > Hi > >> + */ >> +static enum usb_charger_type >> +usb_charger_get_type_by_others(struct usb_charger *uchger) { >> + if (uchger->type != UNKNOWN_TYPE) >> + return uchger->type; >> + >> + if (uchger->psy) { >> + union power_supply_propval val; >> + >> + power_supply_get_property(uchger->psy, >> + POWER_SUPPLY_PROP_CHARGE_TYPE, >> + &val); >> + switch (val.intval) { >> + case POWER_SUPPLY_TYPE_USB: >> + uchger->type = SDP_TYPE; >> + break; >> + case POWER_SUPPLY_TYPE_USB_DCP: >> + uchger->type = DCP_TYPE; >> + break; >> + case POWER_SUPPLY_TYPE_USB_CDP: >> + uchger->type = CDP_TYPE; >> + break; >> + case POWER_SUPPLY_TYPE_USB_ACA: >> + uchger->type = ACA_TYPE; >> + break; >> + default: >> + uchger->type = UNKNOWN_TYPE; >> + break; >> + } >> + } else if (uchger->get_charger_type) { >> + uchger->type = uchger->get_charger_type(uchger); >> + } else { >> + uchger->type = UNKNOWN_TYPE; >> + } >> + >> + return uchger->type; >> +} >> + > > I think we may don't need this usb_charger_get_type_by_others(). > "uchger->type" is set in one place is enough, that is: by > uchger->charger_detect() in usb_charger_detect_type(), then > usb_charger_get_type_by_others() is replaced by usb_charger_get_type(). > > uchger->charger_detect() can have diff implementations no matter > what kind of mechanism is used, for your PMIC case, you can just > directly get the type value by power_supply_get_property(); > with that, we can have one central place to set uchger->type. > After uchger->type is set, charger type detection is no need to be > involved until charger type changes. > > Then next question is where is to call usb_charger_detect_type(), > We need make sure it finished before usb gadget connect. Yeah, that's the point: where? It is hard for usb charger framework to control, which will make it more complicated. The 'usb_charger_detect_type()' is used for detecting the charger type manually on user's platform, and user should call it at the right time to avoid affecting gadget enumeration. Otherwise user can implement some callbacks showed in 'usb_charger_get_type_by_others()' function to get charger type. I think this is controllable and simple for the usb charger framework. > > Ideal is with your framework, diff users only need implement > uchger->charger_detect(). :) >
SGkNCg0KPiAtLS0tLU9yaWdpbmFsIE1lc3NhZ2UtLS0tLQ0KPiBGcm9tOiBCYW9saW4gV2FuZyBb bWFpbHRvOmJhb2xpbi53YW5nQGxpbmFyby5vcmddDQo+IFNlbnQ6IFdlZG5lc2RheSwgQXByaWwg MDYsIDIwMTYgNzozMSBQTQ0KPiBUbzogSnVuIExpIDxqdW4ubGlAbnhwLmNvbT4NCj4gQ2M6IGJh bGJpQGtlcm5lbC5vcmc7IGdyZWdraEBsaW51eGZvdW5kYXRpb24ub3JnOyBzcmVAa2VybmVsLm9y ZzsNCj4gZGJhcnlzaGtvdkBnbWFpbC5jb207IGR3bXcyQGluZnJhZGVhZC5vcmc7IHBldGVyLmNo ZW5AZnJlZXNjYWxlLmNvbTsNCj4gc3Rlcm5Acm93bGFuZC5oYXJ2YXJkLmVkdTsgci5iYWxkeWdh QHNhbXN1bmcuY29tOw0KPiB5b3NoaWhpcm8uc2hpbW9kYS51aEByZW5lc2FzLmNvbTsgbGVlLmpv bmVzQGxpbmFyby5vcmc7IGJyb29uaWVAa2VybmVsLm9yZzsNCj4gY2tlZXBheEBvcGVuc291cmNl LndvbGZzb25taWNyby5jb207IHBhdGNoZXNAb3BlbnNvdXJjZS53b2xmc29ubWljcm8uY29tOw0K PiBsaW51eC1wbUB2Z2VyLmtlcm5lbC5vcmc7IGxpbnV4LXVzYkB2Z2VyLmtlcm5lbC5vcmc7IGRl dmljZS0NCj4gbWFpbmxpbmluZ0BsaXN0cy5saW51eGZvdW5kYXRpb24ub3JnOyBsaW51eC1rZXJu ZWxAdmdlci5rZXJuZWwub3JnDQo+IFN1YmplY3Q6IFJlOiBbUEFUQ0ggdjkgMS80XSBnYWRnZXQ6 IEludHJvZHVjZSB0aGUgdXNiIGNoYXJnZXIgZnJhbWV3b3JrDQo+IA0KPiBPbiA2IEFwcmlsIDIw MTYgYXQgMTY6MjYsIEp1biBMaSA8anVuLmxpQG54cC5jb20+IHdyb3RlOg0KPiA+IEhpDQo+ID4N Cj4gPj4gKyAqLw0KPiA+PiArc3RhdGljIGVudW0gdXNiX2NoYXJnZXJfdHlwZQ0KPiA+PiArdXNi X2NoYXJnZXJfZ2V0X3R5cGVfYnlfb3RoZXJzKHN0cnVjdCB1c2JfY2hhcmdlciAqdWNoZ2VyKSB7 DQo+ID4+ICsgICAgIGlmICh1Y2hnZXItPnR5cGUgIT0gVU5LTk9XTl9UWVBFKQ0KPiA+PiArICAg ICAgICAgICAgIHJldHVybiB1Y2hnZXItPnR5cGU7DQo+ID4+ICsNCj4gPj4gKyAgICAgaWYgKHVj aGdlci0+cHN5KSB7DQo+ID4+ICsgICAgICAgICAgICAgdW5pb24gcG93ZXJfc3VwcGx5X3Byb3B2 YWwgdmFsOw0KPiA+PiArDQo+ID4+ICsgICAgICAgICAgICAgcG93ZXJfc3VwcGx5X2dldF9wcm9w ZXJ0eSh1Y2hnZXItPnBzeSwNCj4gPj4gKyAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAg ICAgICAgIFBPV0VSX1NVUFBMWV9QUk9QX0NIQVJHRV9UWVBFLA0KPiA+PiArICAgICAgICAgICAg ICAgICAgICAgICAgICAgICAgICAgICAgICAgJnZhbCk7DQo+ID4+ICsgICAgICAgICAgICAgc3dp dGNoICh2YWwuaW50dmFsKSB7DQo+ID4+ICsgICAgICAgICAgICAgY2FzZSBQT1dFUl9TVVBQTFlf VFlQRV9VU0I6DQo+ID4+ICsgICAgICAgICAgICAgICAgICAgICB1Y2hnZXItPnR5cGUgPSBTRFBf VFlQRTsNCj4gPj4gKyAgICAgICAgICAgICAgICAgICAgIGJyZWFrOw0KPiA+PiArICAgICAgICAg ICAgIGNhc2UgUE9XRVJfU1VQUExZX1RZUEVfVVNCX0RDUDoNCj4gPj4gKyAgICAgICAgICAgICAg ICAgICAgIHVjaGdlci0+dHlwZSA9IERDUF9UWVBFOw0KPiA+PiArICAgICAgICAgICAgICAgICAg ICAgYnJlYWs7DQo+ID4+ICsgICAgICAgICAgICAgY2FzZSBQT1dFUl9TVVBQTFlfVFlQRV9VU0Jf Q0RQOg0KPiA+PiArICAgICAgICAgICAgICAgICAgICAgdWNoZ2VyLT50eXBlID0gQ0RQX1RZUEU7 DQo+ID4+ICsgICAgICAgICAgICAgICAgICAgICBicmVhazsNCj4gPj4gKyAgICAgICAgICAgICBj YXNlIFBPV0VSX1NVUFBMWV9UWVBFX1VTQl9BQ0E6DQo+ID4+ICsgICAgICAgICAgICAgICAgICAg ICB1Y2hnZXItPnR5cGUgPSBBQ0FfVFlQRTsNCj4gPj4gKyAgICAgICAgICAgICAgICAgICAgIGJy ZWFrOw0KPiA+PiArICAgICAgICAgICAgIGRlZmF1bHQ6DQo+ID4+ICsgICAgICAgICAgICAgICAg ICAgICB1Y2hnZXItPnR5cGUgPSBVTktOT1dOX1RZUEU7DQo+ID4+ICsgICAgICAgICAgICAgICAg ICAgICBicmVhazsNCj4gPj4gKyAgICAgICAgICAgICB9DQo+ID4+ICsgICAgIH0gZWxzZSBpZiAo dWNoZ2VyLT5nZXRfY2hhcmdlcl90eXBlKSB7DQo+ID4+ICsgICAgICAgICAgICAgdWNoZ2VyLT50 eXBlID0gdWNoZ2VyLT5nZXRfY2hhcmdlcl90eXBlKHVjaGdlcik7DQo+ID4+ICsgICAgIH0gZWxz ZSB7DQo+ID4+ICsgICAgICAgICAgICAgdWNoZ2VyLT50eXBlID0gVU5LTk9XTl9UWVBFOw0KPiA+ PiArICAgICB9DQo+ID4+ICsNCj4gPj4gKyAgICAgcmV0dXJuIHVjaGdlci0+dHlwZTsNCj4gPj4g K30NCj4gPj4gKw0KPiA+DQo+ID4gSSB0aGluayB3ZSBtYXkgZG9uJ3QgbmVlZCB0aGlzIHVzYl9j aGFyZ2VyX2dldF90eXBlX2J5X290aGVycygpLg0KPiA+ICJ1Y2hnZXItPnR5cGUiIGlzIHNldCBp biBvbmUgcGxhY2UgaXMgZW5vdWdoLCB0aGF0IGlzOiBieQ0KPiA+IHVjaGdlci0+Y2hhcmdlcl9k ZXRlY3QoKSBpbiB1c2JfY2hhcmdlcl9kZXRlY3RfdHlwZSgpLCB0aGVuDQo+ID4gdXNiX2NoYXJn ZXJfZ2V0X3R5cGVfYnlfb3RoZXJzKCkgaXMgcmVwbGFjZWQgYnkgdXNiX2NoYXJnZXJfZ2V0X3R5 cGUoKS4NCj4gPg0KPiA+IHVjaGdlci0+Y2hhcmdlcl9kZXRlY3QoKSBjYW4gaGF2ZSBkaWZmIGlt cGxlbWVudGF0aW9ucyBubyBtYXR0ZXINCj4gPiB3aGF0IGtpbmQgb2YgbWVjaGFuaXNtIGlzIHVz ZWQsIGZvciB5b3VyIFBNSUMgY2FzZSwgeW91IGNhbiBqdXN0DQo+ID4gZGlyZWN0bHkgZ2V0IHRo ZSB0eXBlIHZhbHVlIGJ5IHBvd2VyX3N1cHBseV9nZXRfcHJvcGVydHkoKTsgd2l0aCB0aGF0LA0K PiA+IHdlIGNhbiBoYXZlIG9uZSBjZW50cmFsIHBsYWNlIHRvIHNldCB1Y2hnZXItPnR5cGUuDQo+ ID4gQWZ0ZXIgdWNoZ2VyLT50eXBlIGlzIHNldCwgY2hhcmdlciB0eXBlIGRldGVjdGlvbiBpcyBu byBuZWVkIHRvIGJlDQo+ID4gaW52b2x2ZWQgdW50aWwgY2hhcmdlciB0eXBlIGNoYW5nZXMuDQo+ ID4NCj4gPiBUaGVuIG5leHQgcXVlc3Rpb24gaXMgd2hlcmUgaXMgdG8gY2FsbCB1c2JfY2hhcmdl cl9kZXRlY3RfdHlwZSgpLCBXZQ0KPiA+IG5lZWQgbWFrZSBzdXJlIGl0IGZpbmlzaGVkIGJlZm9y ZSB1c2IgZ2FkZ2V0IGNvbm5lY3QuDQo+IA0KPiBZZWFoLCB0aGF0J3MgdGhlIHBvaW50OiB3aGVy ZT8gSXQgaXMgaGFyZCBmb3IgdXNiIGNoYXJnZXIgZnJhbWV3b3JrIHRvDQo+IGNvbnRyb2wsIHdo aWNoIHdpbGwgbWFrZSBpdCBtb3JlIGNvbXBsaWNhdGVkLiBUaGUNCj4gJ3VzYl9jaGFyZ2VyX2Rl dGVjdF90eXBlKCknIGlzIHVzZWQgZm9yIGRldGVjdGluZyB0aGUgY2hhcmdlciB0eXBlDQo+IG1h bnVhbGx5IG9uIHVzZXIncyBwbGF0Zm9ybSwgYW5kIHVzZXIgc2hvdWxkIGNhbGwgaXQgYXQgdGhl IHJpZ2h0IHRpbWUgdG8NCj4gYXZvaWQgYWZmZWN0aW5nIGdhZGdldCBlbnVtZXJhdGlvbi4gT3Ro ZXJ3aXNlIHVzZXIgY2FuIGltcGxlbWVudCBzb21lDQo+IGNhbGxiYWNrcyBzaG93ZWQgaW4gJ3Vz Yl9jaGFyZ2VyX2dldF90eXBlX2J5X290aGVycygpJyBmdW5jdGlvbiB0byBnZXQNCj4gY2hhcmdl ciB0eXBlLiBJIHRoaW5rIHRoaXMgaXMgY29udHJvbGxhYmxlIGFuZCBzaW1wbGUgZm9yIHRoZSB1 c2IgY2hhcmdlcg0KPiBmcmFtZXdvcmsuDQoNCkFzIG15IHN1Z2dlc3RlZCwgQ2FuIHRoaXMgZGV0 ZWN0aW9uIGJlIGluIHVzYl91ZGNfdmJ1c19oYW5kbGVyKCksIGFuZA0KYmVmb3JlIHVzYl91ZGNf Y29ubmVjdF9jb250cm9sKCk/IFdlIHNob3VsZCBiZSBhYmxlIHRvIGZpbmQgc29tZSBjZW50cmFs DQpwbGFjZSB3aGVyZSBpdCBpcyBiZXR3ZWVuIHZidXMgZGV0ZWN0aW9uIGFuZCBnYWRnZXQgY29u bmVjdC4gDQoNCj4gDQo+ID4NCj4gPiBJZGVhbCBpcyB3aXRoIHlvdXIgZnJhbWV3b3JrLCBkaWZm IHVzZXJzIG9ubHkgbmVlZCBpbXBsZW1lbnQNCj4gPiB1Y2hnZXItPmNoYXJnZXJfZGV0ZWN0KCku IDopDQo+ID4NCj4gDQo+IC0tDQo+IEJhb2xpbi53YW5nDQo+IEJlc3QgUmVnYXJkcw0K -- To unsubscribe from this list: send the line "unsubscribe linux-pm" in the body of a message to majordomo@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html
On Wed, Apr 06, 2016 at 01:25:06PM +0300, Felipe Balbi wrote: > > Hi, > > Peter Chen <hzpeterchen@gmail.com> writes: > > On Wed, Apr 06, 2016 at 11:05:26AM +0300, Felipe Balbi wrote: > >> Peter Chen <hzpeterchen@gmail.com> writes: > >> > On Wed, Apr 06, 2016 at 10:38:23AM +0300, Felipe Balbi wrote: > >> >> Peter Chen <hzpeterchen@gmail.com> writes: > >> >> > On Fri, Apr 01, 2016 at 03:21:49PM +0800, Baolin Wang wrote: > >> >> > + > >> >> >> +static struct attribute *usb_charger_attrs[] = { > >> >> >> + &dev_attr_sdp_current.attr, > >> >> >> + &dev_attr_dcp_current.attr, > >> >> >> + &dev_attr_cdp_current.attr, > >> >> >> + &dev_attr_aca_current.attr, > >> >> >> + &dev_attr_charger_type.attr, > >> >> >> + &dev_attr_charger_state.attr, > >> >> >> + NULL > >> >> >> +}; > >> >> > > >> >> > The user may only care about current limit, type and state, why they > >> >> > need to care what type's current limit, it is the usb charger > >> >> > framework handles, the framework judge the current according to > >> >> > charger type and USB state (connect/configured/suspended). > >> >> > >> >> it might be useful if we want to know that $this charger doesn't really > >> >> give us as much current as it advertises. > >> >> > >> > > >> > As my understanding, the current limit is dynamic value, it should > >> > report the value the charger supports now, eg, it connects SDP, but > >> > the host is suspended now, then the value should be 2mA. > >> > >> yes, and that's the limit. Now consider we connect to DCP or CDP and > >> limit is 2000mA but we're charging at 1000mA ;-) > >> > > > > Does the user need to know the $this charger limit? Don't they only > > care about the current charging value? I have a USB cable which can > > Why not ? UI might want to change the color of the battery charging icon > if we're charging @ 2000mA or @ 1000mA to give some visual feedback as > to "how fast" battery is supposed to be charged. > > > show charging current value, it changes from time to time, when it > > connects to host pc, it shows 430mA; when it connects to dedicated > > charger, it shows 1000mA. > > good for you, now what does that have to do with $subject ? > +static struct attribute *usb_charger_attrs[] = { + &dev_attr_sdp_current.attr, + &dev_attr_dcp_current.attr, + &dev_attr_cdp_current.attr, + &dev_attr_aca_current.attr, + &dev_attr_charger_type.attr, + &dev_attr_charger_state.attr, + NULL +}; Ok, even the users are interesting in current limit, we still have no necessary to know all kinds of chargers limit and current value, since we already have charger type user interface, the framework can show limit according to charger type. I think below user interfaces are enough, who do you think? +static struct attribute *usb_charger_attrs[] = { + &dev_attr_current.attr, + &dev_attr_current_limit.attr, + &dev_attr_charger_type.attr, + &dev_attr_charger_state.attr, + NULL +};
Hi, Peter Chen <hzpeterchen@gmail.com> writes: > On Wed, Apr 06, 2016 at 01:25:06PM +0300, Felipe Balbi wrote: >> >> Hi, >> >> Peter Chen <hzpeterchen@gmail.com> writes: >> > On Wed, Apr 06, 2016 at 11:05:26AM +0300, Felipe Balbi wrote: >> >> Peter Chen <hzpeterchen@gmail.com> writes: >> >> > On Wed, Apr 06, 2016 at 10:38:23AM +0300, Felipe Balbi wrote: >> >> >> Peter Chen <hzpeterchen@gmail.com> writes: >> >> >> > On Fri, Apr 01, 2016 at 03:21:49PM +0800, Baolin Wang wrote: >> >> >> > + >> >> >> >> +static struct attribute *usb_charger_attrs[] = { >> >> >> >> + &dev_attr_sdp_current.attr, >> >> >> >> + &dev_attr_dcp_current.attr, >> >> >> >> + &dev_attr_cdp_current.attr, >> >> >> >> + &dev_attr_aca_current.attr, >> >> >> >> + &dev_attr_charger_type.attr, >> >> >> >> + &dev_attr_charger_state.attr, >> >> >> >> + NULL >> >> >> >> +}; >> >> >> > >> >> >> > The user may only care about current limit, type and state, why they >> >> >> > need to care what type's current limit, it is the usb charger >> >> >> > framework handles, the framework judge the current according to >> >> >> > charger type and USB state (connect/configured/suspended). >> >> >> >> >> >> it might be useful if we want to know that $this charger doesn't really >> >> >> give us as much current as it advertises. >> >> >> >> >> > >> >> > As my understanding, the current limit is dynamic value, it should >> >> > report the value the charger supports now, eg, it connects SDP, but >> >> > the host is suspended now, then the value should be 2mA. >> >> >> >> yes, and that's the limit. Now consider we connect to DCP or CDP and >> >> limit is 2000mA but we're charging at 1000mA ;-) >> >> >> > >> > Does the user need to know the $this charger limit? Don't they only >> > care about the current charging value? I have a USB cable which can >> >> Why not ? UI might want to change the color of the battery charging icon >> if we're charging @ 2000mA or @ 1000mA to give some visual feedback as >> to "how fast" battery is supposed to be charged. >> >> > show charging current value, it changes from time to time, when it >> > connects to host pc, it shows 430mA; when it connects to dedicated >> > charger, it shows 1000mA. >> >> good for you, now what does that have to do with $subject ? >> > > +static struct attribute *usb_charger_attrs[] = { > + &dev_attr_sdp_current.attr, > + &dev_attr_dcp_current.attr, > + &dev_attr_cdp_current.attr, > + &dev_attr_aca_current.attr, > + &dev_attr_charger_type.attr, > + &dev_attr_charger_state.attr, > + NULL > +}; > > Ok, even the users are interesting in current limit, we still have no > necessary to know all kinds of chargers limit and current value, since > we already have charger type user interface, the framework can show > limit according to charger type. Oh, now I get your comment and I totally agree. We already *know* the detected charger type, there's no point in showing them all. > I think below user interfaces are enough, who do you think? > > +static struct attribute *usb_charger_attrs[] = { > + &dev_attr_current.attr, > + &dev_attr_current_limit.attr, > + &dev_attr_charger_type.attr, > + &dev_attr_charger_state.attr, > + NULL > +}; agreed, const though.
On 7 April 2016 at 12:56, Felipe Balbi <balbi@kernel.org> wrote: > > Hi, > > Peter Chen <hzpeterchen@gmail.com> writes: >> On Wed, Apr 06, 2016 at 01:25:06PM +0300, Felipe Balbi wrote: >>> >>> Hi, >>> >>> Peter Chen <hzpeterchen@gmail.com> writes: >>> > On Wed, Apr 06, 2016 at 11:05:26AM +0300, Felipe Balbi wrote: >>> >> Peter Chen <hzpeterchen@gmail.com> writes: >>> >> > On Wed, Apr 06, 2016 at 10:38:23AM +0300, Felipe Balbi wrote: >>> >> >> Peter Chen <hzpeterchen@gmail.com> writes: >>> >> >> > On Fri, Apr 01, 2016 at 03:21:49PM +0800, Baolin Wang wrote: >>> >> >> > + >>> >> >> >> +static struct attribute *usb_charger_attrs[] = { >>> >> >> >> + &dev_attr_sdp_current.attr, >>> >> >> >> + &dev_attr_dcp_current.attr, >>> >> >> >> + &dev_attr_cdp_current.attr, >>> >> >> >> + &dev_attr_aca_current.attr, >>> >> >> >> + &dev_attr_charger_type.attr, >>> >> >> >> + &dev_attr_charger_state.attr, >>> >> >> >> + NULL >>> >> >> >> +}; >>> >> >> > >>> >> >> > The user may only care about current limit, type and state, why they >>> >> >> > need to care what type's current limit, it is the usb charger >>> >> >> > framework handles, the framework judge the current according to >>> >> >> > charger type and USB state (connect/configured/suspended). >>> >> >> >>> >> >> it might be useful if we want to know that $this charger doesn't really >>> >> >> give us as much current as it advertises. >>> >> >> >>> >> > >>> >> > As my understanding, the current limit is dynamic value, it should >>> >> > report the value the charger supports now, eg, it connects SDP, but >>> >> > the host is suspended now, then the value should be 2mA. >>> >> >>> >> yes, and that's the limit. Now consider we connect to DCP or CDP and >>> >> limit is 2000mA but we're charging at 1000mA ;-) >>> >> >>> > >>> > Does the user need to know the $this charger limit? Don't they only >>> > care about the current charging value? I have a USB cable which can >>> >>> Why not ? UI might want to change the color of the battery charging icon >>> if we're charging @ 2000mA or @ 1000mA to give some visual feedback as >>> to "how fast" battery is supposed to be charged. >>> >>> > show charging current value, it changes from time to time, when it >>> > connects to host pc, it shows 430mA; when it connects to dedicated >>> > charger, it shows 1000mA. >>> >>> good for you, now what does that have to do with $subject ? >>> >> >> +static struct attribute *usb_charger_attrs[] = { >> + &dev_attr_sdp_current.attr, >> + &dev_attr_dcp_current.attr, >> + &dev_attr_cdp_current.attr, >> + &dev_attr_aca_current.attr, >> + &dev_attr_charger_type.attr, >> + &dev_attr_charger_state.attr, >> + NULL >> +}; >> >> Ok, even the users are interesting in current limit, we still have no >> necessary to know all kinds of chargers limit and current value, since >> we already have charger type user interface, the framework can show >> limit according to charger type. > > Oh, now I get your comment and I totally agree. We already *know* the > detected charger type, there's no point in showing them all. > >> I think below user interfaces are enough, who do you think? >> >> +static struct attribute *usb_charger_attrs[] = { >> + &dev_attr_current.attr, >> + &dev_attr_current_limit.attr, >> + &dev_attr_charger_type.attr, >> + &dev_attr_charger_state.attr, >> + NULL >> +}; > > agreed, const though. OK. Thanks for Felipe, Peter and Jun's suggestion. I'll modify it in next version. > > -- > balbi
diff --git a/drivers/usb/gadget/Kconfig b/drivers/usb/gadget/Kconfig index af5d922..82a5b3c 100644 --- a/drivers/usb/gadget/Kconfig +++ b/drivers/usb/gadget/Kconfig @@ -133,6 +133,13 @@ config U_SERIAL_CONSOLE help It supports the serial gadget can be used as a console. +config USB_CHARGER + bool "USB charger support" + help + The usb charger driver based on the usb gadget that makes an + enhancement to a power driver which can set the current limitation + when the usb charger is added or removed. + source "drivers/usb/gadget/udc/Kconfig" # diff --git a/drivers/usb/gadget/udc/Makefile b/drivers/usb/gadget/udc/Makefile index dfee534..0e9564c 100644 --- a/drivers/usb/gadget/udc/Makefile +++ b/drivers/usb/gadget/udc/Makefile @@ -2,6 +2,7 @@ # USB peripheral controller drivers # obj-$(CONFIG_USB_GADGET) += udc-core.o +obj-$(CONFIG_USB_CHARGER) += charger.o obj-$(CONFIG_USB_DUMMY_HCD) += dummy_hcd.o obj-$(CONFIG_USB_NET2272) += net2272.o obj-$(CONFIG_USB_NET2280) += net2280.o diff --git a/drivers/usb/gadget/udc/charger.c b/drivers/usb/gadget/udc/charger.c new file mode 100644 index 0000000..251f42b --- /dev/null +++ b/drivers/usb/gadget/udc/charger.c @@ -0,0 +1,758 @@ +/* + * charger.c -- USB charger driver + * + * Copyright (C) 2015 Linaro Ltd. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#include <linux/device.h> +#include <linux/err.h> +#include <linux/extcon.h> +#include <linux/export.h> +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/slab.h> +#include <linux/usb.h> +#include <linux/usb/ch9.h> +#include <linux/usb/gadget.h> +#include <linux/usb/charger.h> +#include <linux/power_supply.h> + +#define DEFAULT_SDP_CUR_LIMIT 500 +#define DEFAULT_SDP_CUR_LIMIT_SS 900 +#define DEFAULT_DCP_CUR_LIMIT 1500 +#define DEFAULT_CDP_CUR_LIMIT 1500 +#define DEFAULT_ACA_CUR_LIMIT 1500 + +static DEFINE_IDA(usb_charger_ida); +struct class *usb_charger_class; + +static struct usb_charger *dev_to_uchger(struct device *dev) +{ + return container_of(dev, struct usb_charger, dev); +} + +/* + * sdp_current_show() - Show the SDP type charger current limit. + */ +static ssize_t sdp_current_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + struct usb_charger *uchger = dev_to_uchger(dev); + + return sprintf(buf, "%d\n", uchger->cur_limit.sdp_cur_limit); +} +static DEVICE_ATTR_RO(sdp_current); + +/* + * dcp_current_show() - Show the DCP type charger current limit. + */ +static ssize_t dcp_current_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + struct usb_charger *uchger = dev_to_uchger(dev); + + return sprintf(buf, "%d\n", uchger->cur_limit.dcp_cur_limit); +} +static DEVICE_ATTR_RO(dcp_current); + +/* + * cdp_current_show() - Show the CDP type charger current limit. + */ +static ssize_t cdp_current_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + struct usb_charger *uchger = dev_to_uchger(dev); + + return sprintf(buf, "%d\n", uchger->cur_limit.cdp_cur_limit); +} +static DEVICE_ATTR_RO(cdp_current); + +/* + * aca_current_show() - Show the ACA type charger current limit. + */ +static ssize_t aca_current_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + struct usb_charger *uchger = dev_to_uchger(dev); + + return sprintf(buf, "%d\n", uchger->cur_limit.aca_cur_limit); +} +static DEVICE_ATTR_RO(aca_current); + +/* + * charger_type_show() - Show the charger type. + * + * It can be SDP/DCP/CDP/ACA type, else for unknown type. + */ +static ssize_t charger_type_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + struct usb_charger *uchger = dev_to_uchger(dev); + int cnt; + + switch (uchger->type) { + case SDP_TYPE: + cnt = sprintf(buf, "%s\n", "SDP"); + break; + case DCP_TYPE: + cnt = sprintf(buf, "%s\n", "DCP"); + break; + case CDP_TYPE: + cnt = sprintf(buf, "%s\n", "CDP"); + break; + case ACA_TYPE: + cnt = sprintf(buf, "%s\n", "ACA"); + break; + default: + cnt = sprintf(buf, "%s\n", "UNKNOWN"); + break; + } + + return cnt; +} +static DEVICE_ATTR_RO(charger_type); + +/* + * charger_state_show() - Show the charger state. + * + * Charger state can be present or removed. + */ +static ssize_t charger_state_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + struct usb_charger *uchger = dev_to_uchger(dev); + int cnt; + + switch (uchger->state) { + case USB_CHARGER_PRESENT: + cnt = sprintf(buf, "%s\n", "PRESENT"); + break; + case USB_CHARGER_REMOVE: + cnt = sprintf(buf, "%s\n", "REMOVE"); + break; + default: + cnt = sprintf(buf, "%s\n", "UNKNOWN"); + break; + } + + return cnt; +} +static DEVICE_ATTR_RO(charger_state); + +static struct attribute *usb_charger_attrs[] = { + &dev_attr_sdp_current.attr, + &dev_attr_dcp_current.attr, + &dev_attr_cdp_current.attr, + &dev_attr_aca_current.attr, + &dev_attr_charger_type.attr, + &dev_attr_charger_state.attr, + NULL +}; +ATTRIBUTE_GROUPS(usb_charger); + +/* + * usb_charger_find_by_name() - Get the usb charger device by name. + * @name - usb charger device name. + * + * return the instance of usb charger device, the device must be + * released with usb_charger_put(). + */ +struct usb_charger *usb_charger_find_by_name(const char *name) +{ + struct class_dev_iter iter; + struct device *dev; + + if (WARN(!name, "can't work with NULL name")) + return ERR_PTR(-EINVAL); + + /* Iterate all usb charger devices in the class */ + class_dev_iter_init(&iter, usb_charger_class, NULL, NULL); + while ((dev = class_dev_iter_next(&iter))) { + if (!strcmp(dev_name(dev), name)) + break; + } + class_dev_iter_exit(&iter); + + if (WARN(!dev, "can't find usb charger device")) + return ERR_PTR(-ENODEV); + + return dev_to_uchger(dev); +} +EXPORT_SYMBOL_GPL(usb_charger_find_by_name); + +/* + * usb_charger_get() - Reference a usb charger. + * @uchger - usb charger + */ +struct usb_charger *usb_charger_get(struct usb_charger *uchger) +{ + return (uchger && get_device(&uchger->dev)) ? uchger : NULL; +} +EXPORT_SYMBOL_GPL(usb_charger_get); + +/* + * usb_charger_put() - Dereference a usb charger. + * @uchger - charger to release + */ +void usb_charger_put(struct usb_charger *uchger) +{ + if (uchger) + put_device(&uchger->dev); +} +EXPORT_SYMBOL_GPL(usb_charger_put); + +/* + * usb_charger_get_type() - get the usb charger type with lock protection. + * @uchger - usb charger device + * + * Users can get the charger type by this safe API, rather than using the + * usb_charger structure directly. + */ +enum usb_charger_type usb_charger_get_type(struct usb_charger *uchger) +{ + enum usb_charger_type type; + + mutex_lock(&uchger->lock); + type = uchger->type; + mutex_unlock(&uchger->lock); + + return type; +} +EXPORT_SYMBOL_GPL(usb_charger_get_type); + +/* + * usb_charger_detect_type() - detect the charger type manually. + * @uchger - usb charger device + * + * Note: You should ensure you need to detect the charger type manually on your + * platform. + * You should call it at the right gadget state to avoid affecting gadget + * enumeration. + */ +int usb_charger_detect_type(struct usb_charger *uchger) +{ + enum usb_charger_type type; + + if (WARN(!uchger->charger_detect, + "charger detection method should not be NULL")) + return -EINVAL; + + type = uchger->charger_detect(uchger); + + mutex_lock(&uchger->lock); + uchger->type = type; + mutex_unlock(&uchger->lock); + + return 0; +} +EXPORT_SYMBOL_GPL(usb_charger_detect_type); + +/* + * usb_charger_get_type_by_others() - Get the usb charger type by the callback + * which is implemented by users. + * @uchger - the usb charger device. + * + * Note: This function is just used for getting the charger type, not for + * detecting charger type which might affect the DP/DM line when gadget is on + * enumeration state. + */ +static enum usb_charger_type +usb_charger_get_type_by_others(struct usb_charger *uchger) +{ + if (uchger->type != UNKNOWN_TYPE) + return uchger->type; + + if (uchger->psy) { + union power_supply_propval val; + + power_supply_get_property(uchger->psy, + POWER_SUPPLY_PROP_CHARGE_TYPE, + &val); + switch (val.intval) { + case POWER_SUPPLY_TYPE_USB: + uchger->type = SDP_TYPE; + break; + case POWER_SUPPLY_TYPE_USB_DCP: + uchger->type = DCP_TYPE; + break; + case POWER_SUPPLY_TYPE_USB_CDP: + uchger->type = CDP_TYPE; + break; + case POWER_SUPPLY_TYPE_USB_ACA: + uchger->type = ACA_TYPE; + break; + default: + uchger->type = UNKNOWN_TYPE; + break; + } + } else if (uchger->get_charger_type) { + uchger->type = uchger->get_charger_type(uchger); + } else { + uchger->type = UNKNOWN_TYPE; + } + + return uchger->type; +} + +/* + * usb_charger_set_cur_limit_by_type() - Set the current limitation + * by charger type. + * @uchger - the usb charger device. + * @type - the usb charger type. + * @cur_limit - the current limitation. + */ +int usb_charger_set_cur_limit_by_type(struct usb_charger *uchger, + enum usb_charger_type type, + unsigned int cur_limit) +{ + if (WARN(!uchger, "charger can not be NULL")) + return -EINVAL; + + mutex_lock(&uchger->lock); + switch (type) { + case SDP_TYPE: + uchger->cur_limit.sdp_cur_limit = cur_limit; + break; + case DCP_TYPE: + uchger->cur_limit.dcp_cur_limit = cur_limit; + break; + case CDP_TYPE: + uchger->cur_limit.cdp_cur_limit = cur_limit; + break; + case ACA_TYPE: + uchger->cur_limit.aca_cur_limit = cur_limit; + break; + default: + mutex_unlock(&uchger->lock); + return -EINVAL; + } + + mutex_unlock(&uchger->lock); + return 0; +} +EXPORT_SYMBOL_GPL(usb_charger_set_cur_limit_by_type); + +/* + * usb_charger_set_cur_limit() - Set the current limitation. + * @uchger - the usb charger device. + * @cur_limit_set - the current limitation. + */ +int usb_charger_set_cur_limit(struct usb_charger *uchger, + struct usb_charger_cur_limit *cur_limit_set) +{ + if (WARN(!uchger || !cur_limit_set, "charger or setting can't be NULL")) + return -EINVAL; + + mutex_lock(&uchger->lock); + uchger->cur_limit.sdp_cur_limit = cur_limit_set->sdp_cur_limit; + uchger->cur_limit.dcp_cur_limit = cur_limit_set->dcp_cur_limit; + uchger->cur_limit.cdp_cur_limit = cur_limit_set->cdp_cur_limit; + uchger->cur_limit.aca_cur_limit = cur_limit_set->aca_cur_limit; + mutex_unlock(&uchger->lock); + + return 0; +} +EXPORT_SYMBOL_GPL(usb_charger_set_cur_limit); + +/* + * usb_charger_get_cur_limit() - Get the current limitation by + * different usb charger type. + * @uchger - the usb charger device. + * + * return the current limitation to set. + */ +static unsigned int +usb_charger_get_cur_limit(struct usb_charger *uchger) +{ + enum usb_charger_type uchger_type = + usb_charger_get_type_by_others(uchger); + unsigned int cur_limit; + + switch (uchger_type) { + case SDP_TYPE: + cur_limit = uchger->cur_limit.sdp_cur_limit; + break; + case DCP_TYPE: + cur_limit = uchger->cur_limit.dcp_cur_limit; + break; + case CDP_TYPE: + cur_limit = uchger->cur_limit.cdp_cur_limit; + break; + case ACA_TYPE: + cur_limit = uchger->cur_limit.aca_cur_limit; + break; + default: + return 0; + } + + return cur_limit; +} + +/* + * usb_charger_get_current() - Get the charger current with lock protection. + * @uchger - the usb charger device. + * + * Users should get the charger current by this safe API. + */ +unsigned int usb_charger_get_current(struct usb_charger *uchger) +{ + unsigned int cur; + + mutex_lock(&uchger->lock); + cur = usb_charger_get_cur_limit(uchger); + mutex_unlock(&uchger->lock); + + return cur; +} +EXPORT_SYMBOL_GPL(usb_charger_get_current); + +/* + * usb_charger_register_notify() - Register a notifiee to get notified by + * any attach status changes from the usb charger detection. + * @uchger - the usb charger device which is monitored. + * @nb - a notifier block to be registered. + */ +int usb_charger_register_notify(struct usb_charger *uchger, + struct notifier_block *nb) +{ + int ret; + + if (WARN(!uchger || !nb, "charger or nb can not be NULL")) + return -EINVAL; + + mutex_lock(&uchger->lock); + ret = raw_notifier_chain_register(&uchger->uchger_nh, nb); + + /* Generate an initial notify so users start in the right state */ + if (!ret) { + usb_charger_get_type_by_others(uchger); + raw_notifier_call_chain(&uchger->uchger_nh, + usb_charger_get_cur_limit(uchger), + uchger); + } + mutex_unlock(&uchger->lock); + + return ret; +} +EXPORT_SYMBOL_GPL(usb_charger_register_notify); + +/* + * usb_charger_unregister_notify() - Unregister a notifiee from the usb charger. + * @uchger - the usb charger device which is monitored. + * @nb - a notifier block to be unregistered. + */ +int usb_charger_unregister_notify(struct usb_charger *uchger, + struct notifier_block *nb) +{ + int ret; + + if (WARN(!uchger || !nb, "charger or nb can not be NULL")) + return -EINVAL; + + mutex_lock(&uchger->lock); + ret = raw_notifier_chain_unregister(&uchger->uchger_nh, nb); + mutex_unlock(&uchger->lock); + + return ret; +} +EXPORT_SYMBOL_GPL(usb_charger_unregister_notify); + +/* + * usb_charger_notifier_others() - It will notify other device registered + * on usb charger when the usb charger state is changed. + * @uchger - the usb charger device. + * @state - the state of the usb charger. + */ +static void +usb_charger_notify_others(struct usb_charger *uchger, + enum usb_charger_state state) +{ + char uchger_state[50] = { 0 }; + char *envp[] = { uchger_state, NULL }; + + mutex_lock(&uchger->lock); + uchger->state = state; + + switch (state) { + case USB_CHARGER_PRESENT: + usb_charger_get_type_by_others(uchger); + raw_notifier_call_chain(&uchger->uchger_nh, + usb_charger_get_cur_limit(uchger), + uchger); + snprintf(uchger_state, ARRAY_SIZE(uchger_state), + "USB_CHARGER_STATE=%s", "USB_CHARGER_PRESENT"); + break; + case USB_CHARGER_REMOVE: + uchger->type = UNKNOWN_TYPE; + raw_notifier_call_chain(&uchger->uchger_nh, 0, uchger); + snprintf(uchger_state, ARRAY_SIZE(uchger_state), + "USB_CHARGER_STATE=%s", "USB_CHARGER_REMOVE"); + break; + default: + dev_warn(&uchger->dev, "Unknown USB charger state: %d\n", + state); + mutex_unlock(&uchger->lock); + return; + } + + kobject_uevent_env(&uchger->dev.kobj, KOBJ_CHANGE, envp); + mutex_unlock(&uchger->lock); +} + +/* + * usb_charger_plug_by_extcon() - The notifier call function which is registered + * on the extcon device. + * @nb - the notifier block that notified by extcon device. + * @state - the extcon device state. + * @data - here specify a extcon device. + * + * return the notify flag. + */ +static int +usb_charger_plug_by_extcon(struct notifier_block *nb, + unsigned long state, void *data) +{ + struct usb_charger_nb *extcon_nb = + container_of(nb, struct usb_charger_nb, nb); + struct usb_charger *uchger = extcon_nb->uchger; + enum usb_charger_state uchger_state; + + if (WARN(!uchger, "charger can not be NULL")) + return NOTIFY_BAD; + + /* + * Report event to power to setting the current limitation + * for this usb charger when one usb charger is added or removed + * with detecting by extcon device. + */ + if (state) + uchger_state = USB_CHARGER_PRESENT; + else + uchger_state = USB_CHARGER_REMOVE; + + usb_charger_notify_others(uchger, uchger_state); + + return NOTIFY_OK; +} + +/* + * usb_charger_plug_by_gadget() - Set the usb charger current limitation + * according to the usb gadget device state. + * @gadget - the usb gadget device. + * @state - the usb gadget state. + */ +int usb_charger_plug_by_gadget(struct usb_gadget *gadget, + unsigned long state) +{ + return 0; +} +EXPORT_SYMBOL_GPL(usb_charger_plug_by_gadget); + +static int devm_uchger_dev_match(struct device *dev, void *res, void *data) +{ + struct usb_charger **r = res; + + if (WARN_ON(!r || !*r)) + return 0; + + return *r == data; +} + +static void usb_charger_release(struct device *dev) +{ + struct usb_charger *uchger = dev_get_drvdata(dev); + + kfree(uchger); +} + +/* + * usb_charger_unregister() - Unregister a usb charger device. + * @uchger - the usb charger to be unregistered. + */ +static int usb_charger_unregister(struct usb_charger *uchger) +{ + if (WARN(!uchger, "charger can not be NULL")) + return -EINVAL; + + device_unregister(&uchger->dev); + return 0; +} + +static void devm_uchger_dev_unreg(struct device *dev, void *res) +{ + usb_charger_unregister(*(struct usb_charger **)res); +} + +void devm_usb_charger_unregister(struct device *dev, + struct usb_charger *uchger) +{ + devres_release(dev, devm_uchger_dev_unreg, + devm_uchger_dev_match, uchger); +} +EXPORT_SYMBOL_GPL(devm_usb_charger_unregister); + +/* + * usb_charger_register() - Register a new usb charger device + * which is created by the usb charger framework. + * @parent - the parent device of the new usb charger. + * @uchger - the new usb charger device. + */ +static int usb_charger_register(struct device *parent, + struct usb_charger *uchger) +{ + int ret; + + if (WARN(!uchger, "charger can not be NULL")) + return -EINVAL; + + uchger->dev.parent = parent; + uchger->dev.release = usb_charger_release; + uchger->dev.class = usb_charger_class; + uchger->dev.groups = usb_charger_groups; + + ret = ida_simple_get(&usb_charger_ida, 0, 0, GFP_KERNEL); + if (ret < 0) + goto fail_ida; + + uchger->id = ret; + dev_set_name(&uchger->dev, "usb-charger.%d", uchger->id); + dev_set_drvdata(&uchger->dev, uchger); + + ret = device_register(&uchger->dev); + if (ret) + goto fail_register; + + return 0; + +fail_register: + put_device(&uchger->dev); + ida_simple_remove(&usb_charger_ida, uchger->id); + uchger->id = -1; +fail_ida: + dev_err(parent, "Failed to register usb charger: %d\n", ret); + return ret; +} + +int devm_usb_charger_register(struct device *dev, + struct usb_charger *uchger) +{ + struct usb_charger **ptr; + int ret; + + ptr = devres_alloc(devm_uchger_dev_unreg, sizeof(*ptr), GFP_KERNEL); + if (!ptr) + return -ENOMEM; + + ret = usb_charger_register(dev, uchger); + if (ret) { + devres_free(ptr); + return ret; + } + + *ptr = uchger; + devres_add(dev, ptr); + + return 0; +} +EXPORT_SYMBOL_GPL(devm_usb_charger_register); + +int usb_charger_init(struct usb_gadget *ugadget) +{ + struct usb_charger *uchger; + struct extcon_dev *edev; + struct power_supply *psy; + int ret; + + uchger = kzalloc(sizeof(struct usb_charger), GFP_KERNEL); + if (!uchger) + return -ENOMEM; + + uchger->type = UNKNOWN_TYPE; + uchger->state = USB_CHARGER_DEFAULT; + uchger->id = -1; + + if (ugadget->speed >= USB_SPEED_SUPER) + uchger->cur_limit.sdp_cur_limit = DEFAULT_SDP_CUR_LIMIT_SS; + else + uchger->cur_limit.sdp_cur_limit = DEFAULT_SDP_CUR_LIMIT; + uchger->cur_limit.dcp_cur_limit = DEFAULT_DCP_CUR_LIMIT; + uchger->cur_limit.cdp_cur_limit = DEFAULT_CDP_CUR_LIMIT; + uchger->cur_limit.aca_cur_limit = DEFAULT_ACA_CUR_LIMIT; + + mutex_init(&uchger->lock); + RAW_INIT_NOTIFIER_HEAD(&uchger->uchger_nh); + + /* register a notifier on a extcon device if it is exsited */ + edev = extcon_get_edev_by_phandle(ugadget->dev.parent, 0); + if (!IS_ERR_OR_NULL(edev)) { + uchger->extcon_dev = edev; + uchger->extcon_nb.nb.notifier_call = usb_charger_plug_by_extcon; + uchger->extcon_nb.uchger = uchger; + extcon_register_notifier(edev, EXTCON_USB, + &uchger->extcon_nb.nb); + } + + /* to check if the usb charger is link to a power supply */ + psy = devm_power_supply_get_by_phandle(ugadget->dev.parent, + "power-supplies"); + if (!IS_ERR_OR_NULL(psy)) + uchger->psy = psy; + else + uchger->psy = NULL; + + /* register a notifier on a usb gadget device */ + uchger->gadget = ugadget; + uchger->old_gadget_state = ugadget->state; + + /* register a new usb charger */ + ret = usb_charger_register(&ugadget->dev, uchger); + if (ret) + goto fail; + + return 0; + +fail: + if (uchger->extcon_dev) + extcon_unregister_notifier(uchger->extcon_dev, + EXTCON_USB, &uchger->extcon_nb.nb); + + kfree(uchger); + return ret; +} + +int usb_charger_exit(struct usb_gadget *ugadget) +{ + return 0; +} + +static int __init usb_charger_class_init(void) +{ + usb_charger_class = class_create(THIS_MODULE, "usb_charger"); + if (IS_ERR(usb_charger_class)) { + pr_err("couldn't create class\n"); + return PTR_ERR(usb_charger_class); + } + + return 0; +} + +static void __exit usb_charger_class_exit(void) +{ + class_destroy(usb_charger_class); +} +subsys_initcall(usb_charger_class_init); +module_exit(usb_charger_class_exit); + +MODULE_AUTHOR("Baolin Wang <baolin.wang@linaro.org>"); +MODULE_DESCRIPTION("USB charger driver"); +MODULE_LICENSE("GPL v2"); diff --git a/include/linux/usb/charger.h b/include/linux/usb/charger.h new file mode 100644 index 0000000..1bf1d55 --- /dev/null +++ b/include/linux/usb/charger.h @@ -0,0 +1,171 @@ +#ifndef __LINUX_USB_CHARGER_H__ +#define __LINUX_USB_CHARGER_H__ + +#include <uapi/linux/usb/ch9.h> +#include <uapi/linux/usb/charger.h> + +/* Current limitation by charger type */ +struct usb_charger_cur_limit { + unsigned int sdp_cur_limit; + unsigned int dcp_cur_limit; + unsigned int cdp_cur_limit; + unsigned int aca_cur_limit; +}; + +struct usb_charger_nb { + struct notifier_block nb; + struct usb_charger *uchger; +}; + +struct usb_charger { + struct device dev; + struct raw_notifier_head uchger_nh; + /* protect the notifier head and charger */ + struct mutex lock; + int id; + enum usb_charger_type type; + enum usb_charger_state state; + + /* for supporting extcon usb gpio */ + struct extcon_dev *extcon_dev; + struct usb_charger_nb extcon_nb; + + /* for supporting usb gadget */ + struct usb_gadget *gadget; + enum usb_device_state old_gadget_state; + + /* for supporting power supply */ + struct power_supply *psy; + + /* user can get charger type by implementing this callback */ + enum usb_charger_type (*get_charger_type)(struct usb_charger *); + /* + * charger detection method can be implemented if you need to + * manually detect the charger type. + */ + enum usb_charger_type (*charger_detect)(struct usb_charger *); + + /* current limitation */ + struct usb_charger_cur_limit cur_limit; +}; + +#ifdef CONFIG_USB_CHARGER +extern struct usb_charger *usb_charger_find_by_name(const char *name); + +extern struct usb_charger *usb_charger_get(struct usb_charger *uchger); +extern void usb_charger_put(struct usb_charger *uchger); + +extern int usb_charger_register_notify(struct usb_charger *uchger, + struct notifier_block *nb); +extern int usb_charger_unregister_notify(struct usb_charger *uchger, + struct notifier_block *nb); + +extern int usb_charger_set_cur_limit(struct usb_charger *uchger, + struct usb_charger_cur_limit *cur_limit_set); +extern int usb_charger_set_cur_limit_by_type(struct usb_charger *uchger, + enum usb_charger_type type, + unsigned int cur_limit); +extern unsigned int usb_charger_get_current(struct usb_charger *uchger); + +extern int usb_charger_plug_by_gadget(struct usb_gadget *gadget, + unsigned long state); +extern enum usb_charger_type usb_charger_get_type(struct usb_charger *uchger); +extern int usb_charger_detect_type(struct usb_charger *uchger); + +extern void devm_usb_charger_unregister(struct device *dev, + struct usb_charger *uchger); +extern int devm_usb_charger_register(struct device *dev, + struct usb_charger *uchger); + +extern int usb_charger_init(struct usb_gadget *ugadget); +extern int usb_charger_exit(struct usb_gadget *ugadget); +#else +static inline struct usb_charger *usb_charger_find_by_name(const char *name) +{ + return ERR_PTR(-ENODEV); +} + +static inline struct usb_charger *usb_charger_get(struct usb_charger *uchger) +{ + return NULL; +} + +static inline void usb_charger_put(struct usb_charger *uchger) +{ +} + +static inline int +usb_charger_register_notify(struct usb_charger *uchger, + struct notifier_block *nb) +{ + return 0; +} + +static inline int +usb_charger_unregister_notify(struct usb_charger *uchger, + struct notifier_block *nb) +{ + return 0; +} + +static inline int +usb_charger_set_cur_limit(struct usb_charger *uchger, + struct usb_charger_cur_limit *cur_limit_set) +{ + return 0; +} + +static inline int +usb_charger_set_cur_limit_by_type(struct usb_charger *uchger, + enum usb_charger_type type, + unsigned int cur_limit) +{ + return 0; +} + +static inline unsigned int +usb_charger_get_current(struct usb_charger *uchger) +{ + return 0; +} + +static inline enum usb_charger_type +usb_charger_get_type(struct usb_charger *uchger) +{ + return UNKNOWN_TYPE; +} + +static inline int usb_charger_detect_type(struct usb_charger *uchger) +{ + return 0; +} + +static inline int +usb_charger_plug_by_gadget(struct usb_gadget *gadget, unsigned long state) +{ + return 0; +} + +static inline void devm_usb_charger_unregister(struct device *dev, + struct usb_charger *uchger) +{ +} + +static inline int devm_usb_charger_register(struct device *dev, + struct usb_charger *uchger) +{ + return 0; +} + +static inline int usb_charger_init(struct usb_gadget *ugadget) +{ + return 0; +} + +static inline int usb_charger_exit(struct usb_gadget *ugadget) +{ + return 0; +} +#endif + +#endif /* __LINUX_USB_CHARGER_H__ */ diff --git a/include/uapi/linux/usb/charger.h b/include/uapi/linux/usb/charger.h new file mode 100644 index 0000000..3c56ec4 --- /dev/null +++ b/include/uapi/linux/usb/charger.h @@ -0,0 +1,31 @@ +/* + * This file defines the USB charger type and state that are needed for + * USB device APIs. + */ + +#ifndef _UAPI__LINUX_USB_CHARGER_H +#define _UAPI__LINUX_USB_CHARGER_H + +/* + * USB charger type: + * SDP (Standard Downstream Port) + * DCP (Dedicated Charging Port) + * CDP (Charging Downstream Port) + * ACA (Accessory Charger Adapters) + */ +enum usb_charger_type { + UNKNOWN_TYPE, + SDP_TYPE, + DCP_TYPE, + CDP_TYPE, + ACA_TYPE, +}; + +/* USB charger state */ +enum usb_charger_state { + USB_CHARGER_DEFAULT, + USB_CHARGER_PRESENT, + USB_CHARGER_REMOVE, +}; + +#endif /* _UAPI__LINUX_USB_CHARGER_H */
This patch introduces the usb charger driver based on usb gadget that makes an enhancement to a power driver. It works well in practice but that requires a system with suitable hardware. The basic conception of the usb charger is that, when one usb charger is added or removed by reporting from the usb gadget state change or the extcon device state change, the usb charger will report to power user to set the current limitation. The usb charger will register notifiees on the usb gadget or the extcon device to get notified the usb charger state. It also supplies the notification mechanism to userspace When the usb charger state is changed. Power user will register a notifiee on the usb charger to get notified by status changes from the usb charger. It will report to power user to set the current limitation when detecting the usb charger is added or removed from extcon device state or usb gadget state. This patch doesn't yet integrate with the gadget code, so some functions which rely on the 'gadget' are not completed, that will be implemented in the following patches. Signed-off-by: Baolin Wang <baolin.wang@linaro.org> --- drivers/usb/gadget/Kconfig | 7 + drivers/usb/gadget/udc/Makefile | 1 + drivers/usb/gadget/udc/charger.c | 758 ++++++++++++++++++++++++++++++++++++++ include/linux/usb/charger.h | 171 +++++++++ include/uapi/linux/usb/charger.h | 31 ++ 5 files changed, 968 insertions(+) create mode 100644 drivers/usb/gadget/udc/charger.c create mode 100644 include/linux/usb/charger.h create mode 100644 include/uapi/linux/usb/charger.h