From patchwork Sun Dec 2 15:01:03 2012 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Ming Lei X-Patchwork-Id: 1830881 Return-Path: X-Original-To: patchwork-linux-pm@patchwork.kernel.org Delivered-To: patchwork-process-083081@patchwork1.kernel.org Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by patchwork1.kernel.org (Postfix) with ESMTP id 040E9402E1 for ; Sun, 2 Dec 2012 15:01:56 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1752017Ab2LBPBw (ORCPT ); Sun, 2 Dec 2012 10:01:52 -0500 Received: from mail-pb0-f46.google.com ([209.85.160.46]:56131 "EHLO mail-pb0-f46.google.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1751553Ab2LBPBv (ORCPT ); Sun, 2 Dec 2012 10:01:51 -0500 Received: by mail-pb0-f46.google.com with SMTP id wy7so1445313pbc.19 for ; Sun, 02 Dec 2012 07:01:50 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20120113; h=from:to:cc:subject:date:message-id:x-mailer:in-reply-to:references; bh=OzQ0ix6HkVXPO2snHWT9Qw/9C3jILfA3GeZK9jIo27E=; b=m9UIl+ek9nq2AWVDjtrkTKpOHL9JhbnZs8cJ745qkSKEW6D+C9y3cYgm+mSvW/zITA kn97v1evXqV4mmEStILqyArQLRnh67PttQzTVRNfsngi1DnOmlsKnX6oRZUAHxSswPxY PT//TNX0dTB99v9j1Nt6cRb5C08HOhWeAMIH6o9Crxb4nRdVCBAiNIdPR4C6TUbl5tiO ZlxujUgPEbhRmfIzthjTFf4Vda9wpWyp4k+5uIGsG3h53ATZUVVHLAl4G02sEWi8c6hW WMVKGt6Xf/MHZjm+BN4RijKlZ7E8J9MDqeHjC5E2u+5rzq+4N1UF+YfwELxIfnhGt8nn 41ag== Received: by 10.68.189.5 with SMTP id ge5mr21694912pbc.1.1354460510671; Sun, 02 Dec 2012 07:01:50 -0800 (PST) Received: from localhost ([183.37.207.193]) by mx.google.com with ESMTPS id hs2sm6444102pbc.22.2012.12.02.07.01.36 (version=TLSv1/SSLv3 cipher=OTHER); Sun, 02 Dec 2012 07:01:49 -0800 (PST) From: Ming Lei To: Alan Stern , Greg Kroah-Hartman Cc: Lan Tianyu , Sarah Sharp , "Rafael J. Wysocki" , linux-pm@vger.kernel.org, Oliver Neukum , linux-omap@vger.kernel.org, linux-usb@vger.kernel.org, Ming Lei , Andy Green , Roger Quadros , Felipe Balbi Subject: [RFC PATCH 1/5] Device Power: introduce power controller Date: Sun, 2 Dec 2012 23:01:03 +0800 Message-Id: <1354460467-28006-2-git-send-email-tom.leiming@gmail.com> X-Mailer: git-send-email 1.7.9.5 In-Reply-To: <1354460467-28006-1-git-send-email-tom.leiming@gmail.com> References: <1354460467-28006-1-git-send-email-tom.leiming@gmail.com> Sender: linux-pm-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-pm@vger.kernel.org Power controller is an abstract on simple power on/off switch. One power controller can bind to more than one device, which provides power logically, for example, we can think one usb port in hub provides power to the usb device attached to the port, even though the power is supplied actually by other ways, eg. the usb hub is a self-power device. From hardware view, more than one device can share one power domain, and power controller can power on if one of these devices need to provide power, and power off if all these devices don't need to provide power. Cc: "Rafael J. Wysocki" Cc: Andy Green Cc: Roger Quadros Cc: Alan Stern Cc: Felipe Balbi Signed-off-by: Ming Lei --- drivers/base/power/Makefile | 1 + drivers/base/power/power_controller.c | 110 +++++++++++++++++++++++++++++++++ include/linux/power_controller.h | 48 ++++++++++++++ kernel/power/Kconfig | 6 ++ 4 files changed, 165 insertions(+) create mode 100644 drivers/base/power/power_controller.c create mode 100644 include/linux/power_controller.h diff --git a/drivers/base/power/Makefile b/drivers/base/power/Makefile index 2e58ebb..c08bfc9 100644 --- a/drivers/base/power/Makefile +++ b/drivers/base/power/Makefile @@ -5,5 +5,6 @@ obj-$(CONFIG_PM_TRACE_RTC) += trace.o obj-$(CONFIG_PM_OPP) += opp.o obj-$(CONFIG_PM_GENERIC_DOMAINS) += domain.o domain_governor.o obj-$(CONFIG_HAVE_CLK) += clock_ops.o +obj-$(CONFIG_POWER_CONTROLLER) += power_controller.o ccflags-$(CONFIG_DEBUG_DRIVER) := -DDEBUG diff --git a/drivers/base/power/power_controller.c b/drivers/base/power/power_controller.c new file mode 100644 index 0000000..d7671fb --- /dev/null +++ b/drivers/base/power/power_controller.c @@ -0,0 +1,110 @@ +#include +#include +#include +#include +#include +#include + +static DEFINE_MUTEX(pc_lock); + +static void pc_devm_release(struct device *dev, void *res) +{ +} + +static int pc_devm_match(struct device *dev, void *res, void *match_data) +{ + struct pc_dev_data *data = res; + + return (data->magic == (unsigned long)pc_devm_release); +} + +static struct pc_dev_data *dev_pc_data(struct device *dev) +{ + struct pc_dev_data *data; + + data = devres_find(dev, pc_devm_release, pc_devm_match, NULL); + return data; +} + +static int pc_add_devm_data(struct device *dev, struct power_controller *pc, + void *dev_data, int dev_data_size) +{ + struct pc_dev_data *data; + int ret = 0; + + mutex_lock(&pc_lock); + + /* each device should only have one power controller resource */ + data = dev_pc_data(dev); + if (data) { + ret = 1; + goto exit; + } + + data = devres_alloc(pc_devm_release, sizeof(struct pc_dev_data) + + dev_data_size, GFP_KERNEL); + if (!data) { + ret = -ENOMEM; + goto exit; + } + + data->magic = (unsigned long)pc_devm_release; + data->pc = pc; + data->dev_data = &data[1]; + memcpy(data->dev_data, dev_data, dev_data_size); + devres_add(dev, data); + +exit: + mutex_unlock(&pc_lock); + return ret; +} + +static struct power_controller *dev_pc(struct device *dev) +{ + struct pc_dev_data *data = dev_pc_data(dev); + + if (data) + return data->pc; + return NULL; +} + +struct pc_dev_data *dev_pc_get_data(struct device *dev) +{ + struct pc_dev_data *data = dev_pc_data(dev); + return data; +} +EXPORT_SYMBOL(dev_pc_get_data); + +int dev_pc_bind(struct power_controller *pc, struct device *dev, + void *dev_data, int dev_data_size) +{ + return pc_add_devm_data(dev, pc, dev_data, dev_data_size); +} +EXPORT_SYMBOL(dev_pc_bind); + +void dev_pc_unbind(struct power_controller *pc, struct device *dev) +{ +} +EXPORT_SYMBOL(dev_pc_unbind); + +void dev_pc_power_on(struct device *dev) +{ + struct power_controller *pc = dev_pc(dev); + + if (!pc) return; + + if (atomic_inc_return(&pc->count) == 1) + pc->power_on(pc, dev); +} +EXPORT_SYMBOL(dev_pc_power_on); + +void dev_pc_power_off(struct device *dev) +{ + struct power_controller *pc = dev_pc(dev); + + if (!pc) return; + + if (!atomic_dec_return(&pc->count)) + pc->power_off(pc, dev); +} +EXPORT_SYMBOL(dev_pc_power_off); diff --git a/include/linux/power_controller.h b/include/linux/power_controller.h new file mode 100644 index 0000000..772f6d7 --- /dev/null +++ b/include/linux/power_controller.h @@ -0,0 +1,48 @@ +#ifndef _LINUX_POWER_CONTROLLER_H +#define _LINUX_POWER_CONTROLLER_H + +#include + +/* + * One power controller provides simple power on and power off. + * + * One power controller can bind to more than one device, which + * provides power logically, for example, we can think one usb port + * in hub provides power to the usb device attached to the port, even + * though the power is supplied actually by other ways, eg. the usb + * hub is a self-power device. From hardware view, more than one + * device can share one power domain, and power controller can power + * on if one of these devices need to provide power, and power off if + * all these devices don't need to provide power. + * + * The abstract is introduced to hide the implementation details of + * power controller, and only let platform code handle the details. + */ +struct power_controller { + atomic_t count; /* Number of users with power "on" */ + char *name; + void (*power_off)(struct power_controller *pc, struct device *dev); + void (*power_on)(struct power_controller *pc, struct device *dev); +}; + +struct pc_dev_data { + unsigned long magic; + struct power_controller *pc; + void *dev_data; +}; + +#ifdef CONFIG_POWER_CONTROLLER +extern struct pc_dev_data *dev_pc_get_data(struct device *dev); +extern int dev_pc_bind(struct power_controller *pc, struct device *dev, void *dev_data, int dev_data_size); +extern void dev_pc_unbind(struct power_controller *pc, struct device *dev); +extern void dev_pc_power_on(struct device *dev); +extern void dev_pc_power_off(struct device *dev); +#else +static inline struct pc_dev_data *dev_pc_get_data(struct device *dev){return NULL;} +static inline int dev_pc_bind(struct power_controller *pc, struct device *dev, void *dev_data, + int dev_data_size){return 0;} +static inline void dev_pc_unbind(struct power_controller *pc, struct device *dev){} +static inline void dev_pc_power_on(struct device *dev){} +static inline void dev_pc_power_off(struct device *dev){} +#endif +#endif /* _LINUX_POWER_CONTROLLER_H */ diff --git a/kernel/power/Kconfig b/kernel/power/Kconfig index 5dfdc9e..51803a9 100644 --- a/kernel/power/Kconfig +++ b/kernel/power/Kconfig @@ -255,6 +255,12 @@ config PM_OPP implementations a ready to use framework to manage OPPs. For more information, read +config POWER_CONTROLLER + bool "Power Controller" + ---help--- + Power Controller is an abstract on power switch which can be + shared by more than more devices. + config PM_CLK def_bool y depends on PM && HAVE_CLK