Return-Path: Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1754854AbbHGGwd (ORCPT ); Fri, 7 Aug 2015 02:52:33 -0400 Received: from mail-bn1on0115.outbound.protection.outlook.com ([157.56.110.115]:52691 "EHLO na01-bn1-obe.outbound.protection.outlook.com" rhost-flags-OK-OK-OK-FAIL) by vger.kernel.org with ESMTP id S1753851AbbHGGwb (ORCPT ); Fri, 7 Aug 2015 02:52:31 -0400 Authentication-Results: spf=fail (sender IP is 192.88.158.2) smtp.mailfrom=freescale.com; lists.linuxfoundation.org; dkim=none (message not signed) header.d=none; Date: Fri, 7 Aug 2015 13:41:59 +0800 From: Peter Chen To: Baolin Wang CC: , , , , , , , , Subject: Re: [PATCH 1/2] gadget: Introduce the usb charger framework Message-ID: <20150807054157.GD13200@shlinux2> References: <2ba023cbaa1dd96fd5e31e2f01cc0a599a3a5e55.1438844454.git.baolin.wang@linaro.org> MIME-Version: 1.0 Content-Type: text/plain; charset="us-ascii" Content-Disposition: inline In-Reply-To: <2ba023cbaa1dd96fd5e31e2f01cc0a599a3a5e55.1438844454.git.baolin.wang@linaro.org> User-Agent: Mutt/1.5.21 (2010-09-15) X-EOPAttributedMessage: 0 X-Microsoft-Exchange-Diagnostics: 1;BY2FFO11FD048;1:xwUoo/2WZA0I/EfCt2lHgilITbqPpxXXJ0xAwk0eHOhFwOpAuPsfgyikfTQBGduY3XU4QPFN8uS+JlF8ExABc2KRcx5A/hPY+fPxq1vQjAjsnksBzUsPcD0S1Xd+ZSk5Gs6OA06I8naWt1ZlB5ItwDHkqmvGhhVY+6aDqHkyugscvDP6WBwVYeLw+xcrgl5faM3IFV/42qHlHKkYKGDW/gFX7FTKMgo+lx6+2AIv6u8L3bRxVNWk+vnXszC/Zb4PamA82CYGr+yefL/iSF1AsHgdxJWiwwU6A5eJ+rYPXNJyFd9CwXHMxydWAnpS3eiI X-Forefront-Antispam-Report: CIP:192.88.158.2;CTRY:US;IPV:NLI;EFV:NLI;SFV:NSPM;SFS:(10019020)(6009001)(2980300002)(339900001)(199003)(43544003)(24454002)(189002)(47776003)(6806004)(97756001)(77156002)(46406003)(87936001)(62966003)(33716001)(97736004)(23726002)(5001860100001)(110136002)(92566002)(4001350100001)(50986999)(5001830100001)(4001540100001)(104016003)(33656002)(5001960100002)(50466002)(54356999)(76176999)(83506001)(2950100001)(64706001)(86362001)(85426001)(15975445007)(5001920100001)(81156007)(68736005)(77096005)(46102003)(105606002)(19580395003)(69596002)(5890100001)(19580405001)(189998001)(106466001)(2004002)(21314002);DIR:OUT;SFP:1102;SCL:1;SRVR:BY2PR03MB489;H:az84smr01.freescale.net;FPR:;SPF:Fail;PTR:InfoDomainNonexistent;A:1;MX:1;LANG:en; X-Microsoft-Exchange-Diagnostics: 1;BY2PR03MB489;2:8tPGlhJXZ/gDtPv1m2bsn9doqvKtlWj4DPHLFwStgp+uf2enRkg4ywEXyfC/eQ36IhQ2u0UYGe8fECvbYlX7Jy6Mv/wzJhYOrmdE1BBo4rfy7fDiFXuGgFePspns/xSJwQOwn3tWsLPO2zAxEldCmTG7QYp23U9+QIpUTjYaFeI=;3:OX9Dq2rg9ismd0XokQahrE+V16FD2vttHKFaiDI36Y33gAvn3S5ZEZOZpGHJrl5G4Jzrk+OicSz+dhIQCdzFWBMDJwXf53E13gGKHnMeZgFKg2zU9jSHXP6Bi1EYjEH19E86dvUIHBgIaArZmfXqL3GsqEKxvmkmi9iKk4Zuik4klgRX6NGXAZwOl5Wh+2XHonnebPghWDFhUGQaHqOAI8fP4yrq5IP8rPE8bDgmDZVr6Q8NBeY+d1rQzDPza3+7OghZuuc9AEYmt9+gW40HIg==;25:mvmuP4bRWgG8p/s8zfC+D2M222wn9McNEFtRYSk0Grlj7k3+NGInLCf1DqjLfA6GL2sZ5WkZUcNUk59W7xjPmQys5m/N8ynYHtTkhx7S+9Ly5QUL8g9ao+1Glaqiozu3bpRlP2TQYjA5XrjCZRU3zzk+Mix/dGM5T129BDZIPMsWIkv4MUqQebYsY/X1H78yF2SnJGUsJzJCD+xJO/FuOsSgKhZbHwK2iFRVFr0SGa0B+L228L824rnNmFbQturmp+7Bti3v5iK3OdJFuPypcg== X-Microsoft-Antispam: UriScan:;BCL:0;PCL:0;RULEID:(42134001)(42139001);SRVR:BY2PR03MB489;UriScan:;BCL:0;PCL:0;RULEID:(42134001)(42139001);SRVR:BY2PR03MB254; X-Microsoft-Exchange-Diagnostics: 1;BY2PR03MB489;20:YiBN4OBam2EzzbUHnYQ3YOUDWbz9vEs4A7rxhnES1WhG3NlWK+Bg5WkwDImeaTO0xbxVOVJmKVuGRQM2FVBbgv3SKmA/k6iOQqsR9v6EqRQkpFVRnSr8FIBn8/CUIIBYFie8LPa+ATTkrCJ4hOEKjMNWiUlqCRA3YNdle4NJrJW0gzArTdt9Hma8Oo7degRxuWBUjTSb5Xyx79tmnDj8vVeZLMVKDpTbzU30SjS8Nfu3xy2o4eXB3SfBBznZO1xrHCVZV09fqqi3U3cliuvXPqX2BSFhI5i8nZaClydoMca5XrUgqQzlN8YrZdtLfVR03sD8DrK1MhGmyU/weKhbkEp3500o7QGkZDc/qpNQ2Vw=;4:9UJifsRXLaub+2Hy3NB9BwO/S2wk1JxeC2Ex66sr7vSmx7lf9DhrqCd7E43ewk8PNc4WscEBf2kDnNkC51zXVPp6Ev2/axKWvqLLnj62383Z4AO1gyxXt5Enizy39DQCCXLk34XtC4gD9mXbQT3VcA1NUqzMYOgc8LKs7pfWjtby+FzamTC2tjcnd7XGwiUcOX8TQlCnXQ3mPEGV66uBmu8IitwFaLd5HAnXOQ7APj78U4UJxLaDBvXJe6MdM9TQ3YWQMJSs6z3iNd63N6Y0ZQ4NG0sVnxCAi5Nnam3GMZ8= X-Microsoft-Antispam-PRVS: X-Exchange-Antispam-Report-Test: UriScan:; X-Exchange-Antispam-Report-CFA-Test: BCL:0;PCL:0;RULEID:(601004)(5005006)(3002001);SRVR:BY2PR03MB489;BCL:0;PCL:0;RULEID:;SRVR:BY2PR03MB489; X-Forefront-PRVS: 066153096A X-Microsoft-Exchange-Diagnostics: =?us-ascii?Q?1;BY2PR03MB489;23:AM2++1a7wa1n8lcvkVl+PT4K46Rv82iFtO6vAyfL2e?= =?us-ascii?Q?VimiQ3kKJi3m5oUGyD/9+Ud9l++aTC8hGNN+wtpySiJOJu4V3C/hX7yEqpDH?= =?us-ascii?Q?MttpPFASRcoJ0x96Y+GzeNxUN5KG88Y3JFbYh/qG3AVlzkFOZoE+TRp8Tnq/?= =?us-ascii?Q?zLdZ79PO7tZwTdnL0oAOGxgZ4HDEG/xSjL3tCQ6j9PgbWUJ/iDrJnnF7mcF6?= =?us-ascii?Q?ozKZ4TdEJEwvfCWrknQuds8sNv4iKY492pZ3cyEIq3/INKJ5Rai5XzrwMjMN?= =?us-ascii?Q?NkBKBR4lYXS4jJemZZUboxL0u0jzq2VMPMRNApD8AoYvn6M49GRAtLoBz6ro?= =?us-ascii?Q?3NGOTubohVmd6vs4/7jnUrCrs4AvAdyMOslJJJIUmFoF4isoQ7zSMkH9Mpev?= =?us-ascii?Q?JNPbuyZdejMZNjs/R/cv8a3lpby3m5VCJhx77XtJZPFkSiqks3ugEABz/lOv?= =?us-ascii?Q?aJ3a+iv0OHzX4jJCA9f5A69CfKLCqv7JYEw7CHgAj4aLvdw+OZ4SKEJzbA4k?= =?us-ascii?Q?hrxRSqLr7RhwYt9oh6D5tcTmOkYvHJjQx+U1J8hjWaavco6bJF6mUVfua800?= =?us-ascii?Q?59oCmpYSuEN7ZedVzp/RGYNU+6Hqiythz+bCyBiYskp2oezrYZSjf4Gb0YfT?= =?us-ascii?Q?SvdNJrTDE2I4GeygNOUUa1hgVWuAcR0VSa+laFTF5B3oy2h/fkJhwjPdHhWp?= =?us-ascii?Q?QjGkvmrxDPZ/4gaxmE+22uegzLSPbiiXRLtcNy0My78VMewX1DSBgXcFA/l3?= =?us-ascii?Q?aK0pSBmBKj7ZAcsUIwFKUGw45y6BHow/h0H0BtlKaNguowCE6Uoc0GhdqyxX?= =?us-ascii?Q?zUHQHnhBuLFZDUfdgGC6taK+hcEHV4rJN/bDExpkVcX9UlwojjlGZsho4mTI?= =?us-ascii?Q?2hr3RWkAuUeq2DkRRprxfQp22PFZ5raFduKVwwHcCfb2TCaLcyMbplg7aziD?= =?us-ascii?Q?LqobM4cAgBreUjrY4QADHRswlr6Xd03Ox2VQTNonTOk1tnM+wKsmUTRckV1t?= =?us-ascii?Q?DWyvYwNSn6YUPaHFxG+h6sFiMGpu7UpZHhUI2pWj7qFKkzQ4jMSxVUqTKdHU?= =?us-ascii?Q?+fa1p75Xz+3mLorSwoJM9Q7cpIQZ6t/fZU55uEKVtmSPm6NfvSS1bZcQaFas?= =?us-ascii?Q?3skSm1Zfw/BH1pfhHhCSWf+IlEwdgJPH/2kmOr/fi0f36pci0Nt2Z5siEaIg?= =?us-ascii?Q?+hvtcpX9gjN6qDuUoBcofW1FXn9C2SQlblMMLuv383El/aJLbav+3U+gv/b0?= =?us-ascii?Q?+zk9T9vMK45iK+t06zU8TT2Wpxkbx/GHh3gGCsjuR/0TjzK6336qcaOq1WEg?= =?us-ascii?Q?AajWrIFgl6/SX0lSy3vR9aW6l0LAK8WLFPIYVFL3Q1?= X-Microsoft-Exchange-Diagnostics: 1;BY2PR03MB489;5:TqLV6p8KJSSNvvuKKAgxKqadl6g+nnyNoTISaeVe2kmP9QmrypUr02mpYUMd3pyH0KJlhwgaowFPxgixVxq63/8NAm4BXTuT2rnOktMnpGswJOL3RWqbkJHUzLhJXZgsttbc+2IackIvUHKLkbQoCg==;24:cLlNKA5pPu+6kQHey0F2wq/dORp8G8n6UBwfmIiIfOaHPrRdDvIy+FGJW4mIny44EybaceA1KrBAaPlgDPgtdsE4LJsTt55/W027hQOxYug=;20:R6YnHf+oO69Ek1w86AIJ1tkkFkS0j2gANJgdayCnn2XOlwv5rQWVzJml97uBXoLBBrCMcs7z+817MlCj+ax4vA== X-MS-Exchange-CrossTenant-OriginalArrivalTime: 07 Aug 2015 06:52:23.9608 (UTC) X-MS-Exchange-CrossTenant-Id: 710a03f5-10f6-4d38-9ff4-a80b81da590d X-MS-Exchange-CrossTenant-OriginalAttributedTenantConnectingIp: TenantId=710a03f5-10f6-4d38-9ff4-a80b81da590d;Ip=[192.88.158.2];Helo=[az84smr01.freescale.net] X-MS-Exchange-CrossTenant-FromEntityHeader: HybridOnPrem X-MS-Exchange-Transport-CrossTenantHeadersStamped: BY2PR03MB489 X-Microsoft-Exchange-Diagnostics: 1;BY2PR03MB254;2:j89BLod8C8WdYKPCWlhRGbMxOwIk9pSLbG7VxsQVHOJqZvv5Lr84Q+mB0tUtPPWcg3s4ff3Lehg+p72VxVTFfSLtzjW58Ib09d9AYQQ5ZbC547/DtkYek2VF3UvvSg7p0RWLTUUFNiN6qWffaCrXHF1NmrEcQvskqLbbUjtCcP0=;3:3jFngVRCms3wShHcoNLJWatT7911J02Y3901ppzPb7FHPeFDYp4iC/4XtzwEwY6pqh1Xs9vZtgSm5akyyZZp7xHfSULrFOFPX6FuvHR+PjYIz0/7Tjj4rglJPv+jRy81OL06t/w+Ku02SybxFmwUrD5v7iVvmUI1HForFqnPDuHOu8JLWFZF9ryc7lssptUBMuNqhxuHeCdVZiK5FkS+CNmYte+TWSowzU9fcSKyAa2S29DU0i5ISh+KC3Hz8IpE2qqYYNjB3Qn4vpiVJMqGVQ==;25:E8uAPnEnF0f6aRmozYSbwUobFeJFi4taCmVt1b4xzPWzFiFwXrBhIU04mY5mID/9htCh8eJIKr1uuL3ignMu4ZgytHZiJ4Ab/1/R1br3HUqQQqhJLnWo21dUud8aC/gjV2zw3cnlsYpBvYIx6nQs0F7wivZ0QqBxzVi+ZfCERYz7cMgsD5drmiyA5gbBLYEjGQkRIvH1PQcI56bvx2EAI5v4eveCWFT9va7dae/wb0YtxHsYE1AC0YOfjEHXX/aRzZoVGipYB4yrqyd/54HCyQ==;23:K9SRZ3gWP4axf8ImFEYBpQEY/CZ5cAxTBgOmv5IJkczQ/tbdl+ZlwxUrlZderfozjoqhkrCtS5z7MK+duCwV0hRgvIkcgJ/98ME0VZtxUwUj4QpEInqHy7t0VLFLnNe8GS4Wm7LyQXDI0nLkrXtCkxbfXI5AstphyQykVl1Ky7C9A+oU0PAb7wV+Yefb2cSR X-OriginatorOrg: freescale.com Sender: linux-kernel-owner@vger.kernel.org List-ID: X-Mailing-List: linux-kernel@vger.kernel.org Content-Length: 21199 Lines: 713 On Thu, Aug 06, 2015 at 03:03:48PM +0800, Baolin Wang wrote: > 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. > > 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. > > Signed-off-by: Baolin Wang > --- > drivers/usb/gadget/charger.c | 547 +++++++++++++++++++++++++++++++++++++++ > include/linux/usb/usb_charger.h | 101 ++++++++ > 2 files changed, 648 insertions(+) > create mode 100644 drivers/usb/gadget/charger.c > create mode 100644 include/linux/usb/usb_charger.h > > diff --git a/drivers/usb/gadget/charger.c b/drivers/usb/gadget/charger.c > new file mode 100644 > index 0000000..3ca0180 > --- /dev/null > +++ b/drivers/usb/gadget/charger.c > @@ -0,0 +1,547 @@ > +/* > + * usb charger.c -- USB charger driver > + * > + * This program is free software; you can redistribute it and/or modify > + * it under the terms of the GNU General Public License as published by > + * the Free Software Foundation; either version 2 of the License, or > + * (at your option) any later version. > + */ > + > +#include > +#include > +#include > +#include > +#include > +#include > +#include > +#include > +#include > +#include > +#include > +#include > +#include > +#include > +#include > + > +#define DEFAULT_CUR_PROTECT (50) > +#define DEFAULT_SDP_CUR_LIMIT (500 - DEFAULT_CUR_PROTECT) > +#define DEFAULT_DCP_CUR_LIMIT (1500 - DEFAULT_CUR_PROTECT) > +#define DEFAULT_CDP_CUR_LIMIT (1500 - DEFAULT_CUR_PROTECT) > +#define DEFAULT_ACA_CUR_LIMIT (1500 - DEFAULT_CUR_PROTECT) > + > +static LIST_HEAD(usb_charger_list); > +static DEFINE_MUTEX(usb_charger_list_lock); > + > +/* > + * usb_charger_find_by_name - Get the usb charger device by name. > + * @name - usb charger device name. > + * > + * notes: when this function walks the list and returns a charger > + * it's dropped the lock which means that something else could come > + * along and delete the charger before we dereference the pointer. > + * It's very unlikely but it's a possibility so you should take care > + * of it. > + * Thus when you get the usb charger by name, you should call > + * put_usb_charger() to derease the reference count of the usb charger. > + * > + * return the instance of usb charger device. > + */ > +struct usb_charger *usb_charger_find_by_name(char *name) > +{ > + struct usb_charger *uchger; > + > + if (!name) > + return ERR_PTR(-EINVAL); > + > + mutex_lock(&usb_charger_list_lock); > + list_for_each_entry(uchger, &usb_charger_list, entry) { > + if (!strcmp(uchger->name, name)) { > + get_usb_charger(uchger); > + mutex_unlock(&usb_charger_list_lock); > + return uchger; > + } > + } > + mutex_unlock(&usb_charger_list_lock); > + > + return NULL; > +} > + > +/* > + * usb_charger_register_notify() - Register a notifiee to get notified by > + * any attach status changes from the usb charger type detection. > + * @uchger - the usb charger device which is monitored. > + * @nb - a notifier block to be registered. > + */ > +void usb_charger_register_notify(struct usb_charger *uchger, > + struct notifier_block *nb) > +{ > + unsigned long flags; > + > + spin_lock_irqsave(&uchger->lock, flags); > + raw_notifier_chain_register(&uchger->uchger_nh, nb); > + spin_unlock_irqrestore(&uchger->lock, flags); > +} > + > +/* > + * 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. > + */ > +void usb_charger_unregister_notify(struct usb_charger *uchger, > + struct notifier_block *nb) > +{ > + unsigned long flags; > + > + spin_lock_irqsave(&uchger->lock, flags); > + raw_notifier_chain_unregister(&uchger->uchger_nh, nb); > + spin_unlock_irqrestore(&uchger->lock, flags); > +} > + > +/* > + * usb_charger_register_extcon_notifier() - Register a notifiee of the usb > + * charger to get notified by any attach status changes from > + * the extcon device. > + * @uchger - the usb charger device. > + * @edev - the extcon device. > + * @extcon_id - extcon id. > + */ > +int usb_charger_register_extcon_notifier(struct usb_charger *uchger, > + struct extcon_dev *edev, > + unsigned int extcon_id) > +{ > + if (!uchger || !edev) > + return -EINVAL; > + > + return extcon_register_notifier(edev, extcon_id, &uchger->extcon_nb.nb); > +} > + > +/* > + * usb_charger_unregister_extcon_notifier() - Unregister a notifiee of the > + * usb charger from the extcon device. > + * @uchger - the usb charger device. > + * @edev - the extcon device. > + * @extcon_id - extcon id. > + */ > +int usb_charger_unregister_extcon_notifier(struct usb_charger *uchger, > + struct extcon_dev *edev, > + unsigned int extcon_id) > +{ > + if (!uchger || !edev) > + return -EINVAL; > + > + return extcon_unregister_notifier(edev, extcon_id, &uchger->extcon_nb.nb); > +} > + > +/* > + * usb_charger_register_gadget_notifier() - Register a notifiee of the usb > + * charger to get notified by any attach status changes from > + * the usb gadget device. > + * @uchger - the usb charger device. > + */ > +int usb_charger_register_gadget_notifier(struct usb_charger *uchger) > +{ > + struct usb_gadget *ugadget = uchger->gadget; > + > + return usb_gadget_register_notify(ugadget, &uchger->gadget_nb); > +} usb_gadget_register_notify is defined at your 2nd patch, it will cause 'git bisect' problem. > + > +/* > + * usb_charger_unregister_gadget_notifier() - Unregister a notifiee of the usb > + * charger from the usb gadget device. > + * @uchger - the usb charger device. > + */ > +int usb_charger_unregister_gadget_notifier(struct usb_charger *uchger) > +{ > + struct usb_gadget *ugadget = uchger->gadget; > + > + return usb_gadget_unregister_notify(ugadget, &uchger->gadget_nb); > +} > + > +/* > + * usb_charger_set_cur_limit() - Set the current limitation. > + * @uchger - the usb charger device. > + * @xxx_cur - the current limit by different charger type. > + * > + */ > +int usb_charger_set_cur_limit(struct usb_charger *uchger, > + struct usb_charger_cur_limit *cur_limit_set) > +{ > + if (!uchger || !cur_limit_set) > + return -EINVAL; > + > + 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; > + return 0; > +} > + > +/* > + * usb_charger_get_cur_limit() - Get the current limitation by different usb > + * charger type. > + * @uchger - the usb charger device. > + * @type - the usb charger type. > + * > + * return the current limitation to set. > + */ > +static unsigned int > +usb_charger_get_cur_limit(struct usb_charger *uchger) > +{ > + enum usb_charger_type uchger_type = uchger->type; > + 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_detect_type() - Get the usb charger type by the callback which is > + * implemented by user. > + * @uchger - the usb charger device. > + * > + * return the usb charger type. > + */ > +enum usb_charger_type > +usb_charger_detect_type(struct usb_charger *uchger) > +{ > + if (uchger->gadget && uchger->gadget->ops > + && uchger->gadget->ops->get_charger_type) > + uchger->type = > + uchger->gadget->ops->get_charger_type(uchger->gadget); > + else > + uchger->type = UNKNOWN_TYPE; > + > + return uchger->type; > +} > + > +/* > + * usb_charger_notifier_others() - It will notify other device registered on > + * usb charger. > + * @uchger - the usb charger device. > + * > + */ > +static void > +usb_charger_notify_others(struct usb_charger *uchger, > + enum usb_charger_state state) > +{ > + unsigned long flags; > + > + spin_lock_irqsave(&uchger->lock, flags); it is better to use mutex_lock instead of disabling interrupt since checking charger type may cause sleeping. > + uchger->state = state; > + > + switch (state) { > + case USB_CHARGER_PRESENT: > + usb_charger_detect_type(uchger); > + raw_notifier_call_chain(&uchger->uchger_nh, > + usb_charger_get_cur_limit(uchger), > + uchger); > + break; > + case USB_CHARGER_REMOVE: > + uchger->type = UNKNOWN_TYPE; > + raw_notifier_call_chain(&uchger->uchger_nh, 0, uchger); > + break; > + default: > + dev_warn(&uchger->dev, "Unknown USB charger state: %d\n", > + state); > + break; > + } > + spin_unlock_irqrestore(&uchger->lock, flags); > +} > + > +/* > + * usb_charger_plug_by_extcon() - The notifier call function which is registered > + * on the extcon device. > + * @nb - thr notifier block that notified by extcon device. > + * @state - the extcon device state changed. > + * @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 (!uchger) > + 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. > + * @data - here specify a usb charger device. > + * > + */ > +static int > +usb_charger_plug_by_gadget(struct notifier_block *nb, > + unsigned long state, void *data) > +{ > + struct usb_gadget *gadget = (struct usb_gadget *)data; > + struct usb_charger *uchger = gadget->uchger; > + enum usb_charger_state uchger_state; > + > + if (!uchger) > + return NOTIFY_BAD; > + > + /* Report event to power to setting the current limitation > + * for this usb charger when one usb charger state is changed > + * with detecting by usb gadget state. > + */ > + if (uchger->old_gadget_state != state) { > + uchger->old_gadget_state = state; > + > + if (state >= USB_STATE_ATTACHED) > + uchger_state = USB_CHARGER_PRESENT; > + else if (state == USB_STATE_NOTATTACHED) > + uchger_state = USB_CHARGER_REMOVE; > + else > + uchger_state = USB_CHARGER_DEFAULT; > + > + usb_charger_notify_others(uchger, uchger_state); > + } > + > + return NOTIFY_OK; > +} > + > +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); > + > + if (!atomic_dec_and_test(&uchger->count)) { > + dev_err(dev, "The usb charger is still in use\n"); > + return; > + } > + > + kfree(uchger->name); > + kfree(uchger); > +} > + > +/* > + * usb_charger_unregister() - Unregister a usb charger device. > + * @uchger - the usb charger device. > + * > + */ > +int usb_charger_unregister(struct usb_charger *uchger) > +{ > + if (!uchger) > + return -EINVAL; > + > + mutex_lock(&usb_charger_list_lock); > + list_del(&uchger->entry); > + mutex_unlock(&usb_charger_list_lock); > + > + 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); > +} > + > +/* > + * usb_charger_register() - Register a new usb charger device. > + * @uchger - the new usb charger device. > + * > + */ > +int usb_charger_register(struct device *dev, struct usb_charger *uchger) > +{ > + static atomic_t uchger_no = ATOMIC_INIT(-1); > + struct usb_charger *tmp; > + int ret; > + > + if (!uchger) { > + dev_err(dev, "no device provided for charger\n"); > + return -EINVAL; > + } > + > + uchger->dev.parent = dev; > + uchger->dev.release = usb_charger_release; > + dev_set_name(&uchger->dev, "usb-chger%lu", > + (unsigned long)atomic_inc_return(&uchger_no)); For the name of usb-charger, which not using "usb-charger.x" directly? > + > + ret = device_register(&uchger->dev); > + if (ret) { > + put_device(&uchger->dev); > + return ret; > + } > + > + dev_set_drvdata(&uchger->dev, uchger); > + > + mutex_lock(&usb_charger_list_lock); > + list_for_each_entry(tmp, &usb_charger_list, entry) { > + if (!(strcmp(tmp->name, uchger->name))) { > + mutex_unlock(&usb_charger_list_lock); > + ret = -EEXIST; > + goto out; > + } > + } > + list_add_tail(&uchger->entry, &usb_charger_list); > + mutex_unlock(&usb_charger_list_lock); > + > + return 0; > + > +out: > + dev_err(dev, "Failed to register usb charger (%s)\n", > + uchger->name); > + device_unregister(&uchger->dev); > + put_device(&uchger->dev); > + 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; > +} > + > +int usb_charger_init(struct usb_gadget *ugadget) > +{ > + struct usb_charger *uchger; > + struct extcon_dev *edev; > + char buf[100]; > + char *str; > + int ret; > + > + if (!ugadget) > + return -EINVAL; > + > + uchger = devm_kzalloc(&ugadget->dev, sizeof(struct usb_charger), > + GFP_KERNEL); > + if (!uchger) > + return -ENOMEM; > + > + sprintf(buf, "usb-charger.%s", ugadget->name); > + str = devm_kzalloc(&ugadget->dev, sizeof(char) * (strlen(buf) + 1), > + GFP_KERNEL); > + if (!str) > + return -ENOMEM; > + > + strcpy(str, buf); > + uchger->name = str; > + uchger->type = UNKNOWN_TYPE; > + uchger->state = USB_CHARGER_DEFAULT; > + 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; > + > + atomic_set(&uchger->count, 1); > + spin_lock_init(&uchger->lock); > + INIT_LIST_HEAD(&uchger->entry); > + 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; > + usb_charger_register_extcon_notifier(uchger, edev, EXTCON_USB); > + } > + > + /* register a notifier on a usb gadget device */ > + uchger->gadget = ugadget; > + ugadget->uchger = uchger; > + uchger->old_gadget_state = ugadget->state; > + uchger->gadget_nb.notifier_call = usb_charger_plug_by_gadget; > + usb_charger_register_gadget_notifier(uchger); > + > + ret = usb_charger_register(&ugadget->dev, uchger); > + if (ret) > + goto reg_fail; > + > + return 0; > + > +reg_fail: > + if (uchger->extcon_dev) > + usb_charger_unregister_extcon_notifier(uchger, > + uchger->extcon_dev, EXTCON_USB); > + > + usb_charger_unregister_gadget_notifier(uchger); > + return ret; > +} > + > +int usb_charger_exit(struct usb_gadget *ugadget) > +{ > + struct usb_charger *uchger = ugadget->uchger; > + > + if (!uchger) > + return -EINVAL; > + > + if (uchger->extcon_dev) > + usb_charger_unregister_extcon_notifier(uchger, > + uchger->extcon_dev, EXTCON_USB); > + > + usb_charger_unregister_gadget_notifier(uchger); > + return usb_charger_unregister(uchger); > +} > + > +MODULE_AUTHOR("Baolin Wang "); > +MODULE_DESCRIPTION("USB charger driver"); > diff --git a/include/linux/usb/usb_charger.h b/include/linux/usb/usb_charger.h > new file mode 100644 > index 0000000..da4d3c9 > --- /dev/null > +++ b/include/linux/usb/usb_charger.h > @@ -0,0 +1,101 @@ > +#ifndef __LINUX_USB_CHARGER_H__ > +#define __LINUX_USB_CHARGER_H__ > + > +#include > +#include > +#include > +#include > + > +/* 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, > +}; > + > +/* 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 { > + /* Internal data. Please do not set. */ > + const char *name; > + struct device dev; > + struct raw_notifier_head uchger_nh; > + struct list_head entry; > + spinlock_t lock; > + enum usb_charger_type type; > + enum usb_charger_state state; > + atomic_t count; > + > + /* 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; > + struct notifier_block gadget_nb; > + > + /* Current limitation */ > + struct usb_charger_cur_limit cur_limit; > +}; > + > +extern struct usb_charger *usb_charger_find_by_name(char *name); > + > +extern void usb_charger_register_notify(struct usb_charger *uchger, > + struct notifier_block *nb); > +extern void usb_charger_unregister_notify(struct usb_charger *uchger, > + struct notifier_block *nb); > + > +extern int usb_charger_register_extcon_notifier(struct usb_charger *uchger, > + struct extcon_dev *edev, > + unsigned int extcon_id); > +extern int usb_charger_unregister_extcon_notifier(struct usb_charger *uchger, > + struct extcon_dev *edev, > + unsigned int extcon_id); > + > +extern int usb_charger_register_gadget_notifier(struct usb_charger *uchger); > +extern int usb_charger_unregister_gadget_notifier(struct usb_charger *uchger); > + > +extern int usb_charger_set_cur_limit(struct usb_charger *uchger, > + struct usb_charger_cur_limit *cur_limit_set); > +extern enum usb_charger_type usb_charger_detect_type(struct usb_charger *uchger); > + > +extern int usb_charger_init(struct usb_gadget *ugadget); > +extern int usb_charger_exit(struct usb_gadget *ugadget); > + > +static inline void get_usb_charger(struct usb_charger *uchger) > +{ > + atomic_inc(&uchger->count); > +} > + > +static inline void put_usb_charger(struct usb_charger *uchger) > +{ > + atomic_dec(&uchger->count); > +} > + > +#endif /* __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 -- Best Regards, Peter Chen -- To unsubscribe from this list: send the line "unsubscribe linux-kernel" in the body of a message to majordomo@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html Please read the FAQ at http://www.tux.org/lkml/