From patchwork Thu Oct 18 09:07:02 2012 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Haojian Zhuang X-Patchwork-Id: 1608791 Return-Path: X-Original-To: patchwork-linux-arm@patchwork.kernel.org Delivered-To: patchwork-process-083081@patchwork2.kernel.org Received: from merlin.infradead.org (merlin.infradead.org [205.233.59.134]) by patchwork2.kernel.org (Postfix) with ESMTP id 804B0DFB34 for ; Thu, 18 Oct 2012 09:10:56 +0000 (UTC) Received: from localhost ([::1] helo=merlin.infradead.org) by merlin.infradead.org with esmtp (Exim 4.76 #1 (Red Hat Linux)) id 1TOm5d-0002IW-EQ; Thu, 18 Oct 2012 09:08:30 +0000 Received: from mail-da0-f49.google.com ([209.85.210.49]) by merlin.infradead.org with esmtps (Exim 4.76 #1 (Red Hat Linux)) id 1TOm4h-0001me-U3 for linux-arm-kernel@lists.infradead.org; Thu, 18 Oct 2012 09:07:34 +0000 Received: by mail-da0-f49.google.com with SMTP id q27so3509129daj.36 for ; Thu, 18 Oct 2012 02:07:31 -0700 (PDT) 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=aQkd5cOkbl3sTPg9YrANxE7SpR8F/lAI2g3nQa7SjMo=; b=qq9FEVZSBCFiEVIM/mRoLit0yLFo+i/ZRwJ++HM8NtqmI3hkftI08gnoATXyZL2GXB wJppxM8Cz/Ujwv+Tu3Uu+0Wez3KaFQTV+QmnJ9A0YYh07Dlvqv1ok0tddE9TLtMjKsIG f/wYCB2mwi1PL1LsWtzYusRWjVXVAr9IEo3C0KmtifpC1tiFmGJ33XQgOUpaMRaCFvzW ZdvZ9KsldeY6EX3/YxlzgLdKHr8p3+AAgkpc9ckv12vDK3UaBzX0ok0iBWClmLxHJ2us gnl7U1ku4Cw7v0lw0kj6pFt9a+58b5RQQV1YSoghwliOymKp2Y0aOWvThckF8Q85L8LH fCCw== Received: by 10.68.192.97 with SMTP id hf1mr2879573pbc.106.1350551251032; Thu, 18 Oct 2012 02:07:31 -0700 (PDT) Received: from localhost ([58.39.221.134]) by mx.google.com with ESMTPS id qv4sm12590170pbc.38.2012.10.18.02.07.26 (version=TLSv1/SSLv3 cipher=OTHER); Thu, 18 Oct 2012 02:07:29 -0700 (PDT) From: Haojian Zhuang To: linux-arm-kernel@lists.infradead.org, arnd@arndb.de, linus.walleij@linaro.org, tony@atomide.com, devicetree-discuss@lists.ozlabs.org Subject: [PATCH 08/10] pinctrl: single: support pinconf generic Date: Thu, 18 Oct 2012 17:07:02 +0800 Message-Id: <1350551224-12857-8-git-send-email-haojian.zhuang@gmail.com> X-Mailer: git-send-email 1.7.9.5 In-Reply-To: <1350551224-12857-1-git-send-email-haojian.zhuang@gmail.com> References: <1350551224-12857-1-git-send-email-haojian.zhuang@gmail.com> X-Spam-Note: CRM114 invocation failed X-Spam-Score: -2.7 (--) X-Spam-Report: SpamAssassin version 3.3.2 on merlin.infradead.org summary: Content analysis details: (-2.7 points) pts rule name description ---- ---------------------- -------------------------------------------------- -0.7 RCVD_IN_DNSWL_LOW RBL: Sender listed at http://www.dnswl.org/, low trust [209.85.210.49 listed in list.dnswl.org] 0.0 FREEMAIL_FROM Sender email is commonly abused enduser mail provider (haojian.zhuang[at]gmail.com) -0.0 SPF_PASS SPF: sender matches SPF record -1.9 BAYES_00 BODY: Bayes spam probability is 0 to 1% [score: 0.0000] -0.1 DKIM_VALID_AU Message has a valid DKIM or DK signature from author's domain 0.1 DKIM_SIGNED Message has a DKIM or DK signature, not necessarily valid -0.1 DKIM_VALID Message has at least one valid DKIM or DK signature Cc: Haojian Zhuang X-BeenThere: linux-arm-kernel@lists.infradead.org X-Mailman-Version: 2.1.14 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , MIME-Version: 1.0 Sender: linux-arm-kernel-bounces@lists.infradead.org Errors-To: linux-arm-kernel-bounces+patchwork-linux-arm=patchwork.kernel.org@lists.infradead.org Add pinconf generic support with POWER SOURCE, BIAS PULL. Signed-off-by: Haojian Zhuang --- drivers/pinctrl/Kconfig | 2 +- drivers/pinctrl/pinctrl-single.c | 286 ++++++++++++++++++++++++++++++++++++-- 2 files changed, 274 insertions(+), 14 deletions(-) diff --git a/drivers/pinctrl/Kconfig b/drivers/pinctrl/Kconfig index 54e3588..cc2ef20 100644 --- a/drivers/pinctrl/Kconfig +++ b/drivers/pinctrl/Kconfig @@ -106,7 +106,7 @@ config PINCTRL_SINGLE tristate "One-register-per-pin type device tree based pinctrl driver" depends on OF select PINMUX - select PINCONF + select GENERIC_PINCONF help This selects the device tree based generic pinctrl driver. diff --git a/drivers/pinctrl/pinctrl-single.c b/drivers/pinctrl/pinctrl-single.c index 02cd412..a396944 100644 --- a/drivers/pinctrl/pinctrl-single.c +++ b/drivers/pinctrl/pinctrl-single.c @@ -20,6 +20,7 @@ #include #include +#include #include #include @@ -27,6 +28,9 @@ #define DRIVER_NAME "pinctrl-single" #define PCS_MUX_NAME "pinctrl-single,pins" +#define PCS_BIAS_NAME "pinctrl-single,bias" +#define PCS_POWER_SOURCE_NAME "pinctrl-single,power-source" +#define PCS_SCHMITT_NAME "pinctrl-single,input-schmitt" #define PCS_REG_NAME_LEN ((sizeof(unsigned long) * 2) + 1) #define PCS_OFF_DISABLED ~0U #define PCS_MAX_GPIO_VALUES 3 @@ -137,6 +141,15 @@ struct pcs_name { * @foff: value to turn mux off * @fmax: max number of functions in fmask * @gmask: gpio control mask + * @bmask: mask of bias in pinconf + * @bshift: offset of bias in pinconf + * @bdis: bias disable value in pinconf + * @bpullup: bias pull up value in pinconf + * @bpulldown: bias pull down value in pinconf + * @ismask: mask of input schmitt in pinconf + * @isshift: offset of input schmitt in pinconf + * @psmask: mask of power source in pinconf + * @psshift: offset of power source in pinconf * @names: array of register names for pins * @pins: physical pins on the SoC * @pgtree: pingroup index radix tree @@ -164,6 +177,15 @@ struct pcs_device { unsigned foff; unsigned fmax; unsigned gmask; + unsigned bmask; + unsigned bshift; + unsigned bdis; + unsigned bpullup; + unsigned bpulldown; + unsigned ismask; + unsigned isshift; + unsigned psmask; + unsigned psshift; struct pcs_name *names; struct pcs_data pins; struct radix_tree_root pgtree; @@ -268,9 +290,14 @@ static int pcs_get_group_pins(struct pinctrl_dev *pctldev, static void pcs_pin_dbg_show(struct pinctrl_dev *pctldev, struct seq_file *s, - unsigned offset) + unsigned pin) { - seq_printf(s, " " DRIVER_NAME); + struct pcs_device *pcs; + unsigned offset; + + pcs = pinctrl_dev_get_drvdata(pctldev); + offset = pin * (pcs->width / BITS_PER_BYTE); + seq_printf(s, " register value:0x%x", pcs_readl(pcs->base + offset)); } static void pcs_dt_free_map(struct pinctrl_dev *pctldev, @@ -468,28 +495,163 @@ static struct pinmux_ops pcs_pinmux_ops = { .gpio_disable_free = pcs_disable_gpio, }; +static void pcs_free_pingroups(struct pcs_device *pcs); + static int pcs_pinconf_get(struct pinctrl_dev *pctldev, unsigned pin, unsigned long *config) { + struct pcs_device *pcs = pinctrl_dev_get_drvdata(pctldev); + enum pin_config_param param = pinconf_to_config_param(*config); + unsigned data; + u32 offset; + + offset = pin * (pcs->width / BITS_PER_BYTE); + data = pcs_readl(pcs->base + offset); + + switch (param) { + case PIN_CONFIG_POWER_SOURCE: + if (pcs->psmask == PCS_OFF_DISABLED + || pcs->psshift == PCS_OFF_DISABLED) + return -ENOTSUPP; + data &= pcs->psmask; + data = data >> pcs->psshift; + *config = data; + return 0; + break; + case PIN_CONFIG_BIAS_DISABLE: + if (pcs->bmask == PCS_OFF_DISABLED + || pcs->bshift == PCS_OFF_DISABLED + || pcs->bdis == PCS_OFF_DISABLED) + return -ENOTSUPP; + data &= pcs->bmask; + *config = 0; + if (data == pcs->bdis) + return 0; + else + return -EINVAL; + break; + case PIN_CONFIG_BIAS_PULL_UP: + if (pcs->bmask == PCS_OFF_DISABLED + || pcs->bshift == PCS_OFF_DISABLED + || pcs->bpullup == PCS_OFF_DISABLED) + return -ENOTSUPP; + data &= pcs->bmask; + *config = 0; + if (data == pcs->bpullup) + return 0; + else + return -EINVAL; + break; + case PIN_CONFIG_BIAS_PULL_DOWN: + if (pcs->bmask == PCS_OFF_DISABLED + || pcs->bshift == PCS_OFF_DISABLED + || pcs->bpulldown == PCS_OFF_DISABLED) + return -ENOTSUPP; + data &= pcs->bmask; + *config = 0; + if (data == pcs->bpulldown) + return 0; + else + return -EINVAL; + break; + default: + break; + } return -ENOTSUPP; } static int pcs_pinconf_set(struct pinctrl_dev *pctldev, unsigned pin, unsigned long config) { - return -ENOTSUPP; + struct pcs_device *pcs = pinctrl_dev_get_drvdata(pctldev); + enum pin_config_param config_param = pinconf_to_config_param(config); + unsigned ret, mask = ~0UL; + u32 offset, data; + + switch (config_param) { + case PIN_CONFIG_POWER_SOURCE: + if (pcs->psmask == PCS_OFF_DISABLED + || pcs->psshift == PCS_OFF_DISABLED) + return 0; + mask = pcs->psmask; + data = (pinconf_to_config_argument(config) << pcs->psshift) + & pcs->psmask; + break; + case PIN_CONFIG_BIAS_DISABLE: + if (pcs->bmask == PCS_OFF_DISABLED + || pcs->bshift == PCS_OFF_DISABLED) + return 0; + mask = pcs->bmask; + data = (pinconf_to_config_argument(config) << pcs->bshift) + & pcs->bmask; + break; + default: + return 0; + } + offset = pin * (pcs->width / BITS_PER_BYTE); + ret = pcs_readl(pcs->base + offset) & ~mask; + pcs_writel(ret | data, pcs->base + offset); + return 0; } static int pcs_pinconf_group_get(struct pinctrl_dev *pctldev, unsigned group, unsigned long *config) { - return -ENOTSUPP; + struct pcs_device *pcs = pinctrl_dev_get_drvdata(pctldev); + struct pcs_pingroup *pins; + + pins = radix_tree_lookup(&pcs->pgtree, group); + if (!pins) { + dev_err(pcs->dev, "%s could not find pingroup%i\n", + __func__, group); + return -EINVAL; + } + return pcs_pinconf_get(pctldev, pins->gpins[0], config); } static int pcs_pinconf_group_set(struct pinctrl_dev *pctldev, unsigned group, unsigned long config) { - return -ENOTSUPP; + struct pcs_device *pcs = pinctrl_dev_get_drvdata(pctldev); + enum pin_config_param config_param = pinconf_to_config_param(config); + struct pcs_pingroup *pins; + u32 offset, data; + unsigned ret, mask = ~0UL; + int i; + + switch (config_param) { + case PIN_CONFIG_POWER_SOURCE: + if (pcs->psmask == PCS_OFF_DISABLED + || pcs->psshift == PCS_OFF_DISABLED) + return 0; + mask = pcs->psmask; + data = (pinconf_to_config_argument(config) << pcs->psshift) + & pcs->psmask; + break; + case PIN_CONFIG_BIAS_DISABLE: + if (pcs->bmask == PCS_OFF_DISABLED + || pcs->bshift == PCS_OFF_DISABLED) + return 0; + mask = pcs->bmask; + data = (pinconf_to_config_argument(config) << pcs->bshift) + & pcs->bmask; + break; + default: + return 0; + } + + pins = radix_tree_lookup(&pcs->pgtree, group); + if (!pins) { + dev_err(pcs->dev, "%s could not find pingroup%i\n", + __func__, group); + return -EINVAL; + } + for (i = 0; i < pins->ngpins; i++) { + offset = pins->gpins[i] * (pcs->width / BITS_PER_BYTE); + ret = pcs_readl(pcs->base + offset) & ~mask; + pcs_writel(ret | data, pcs->base + offset); + } + return 0; } static void pcs_pinconf_dbg_show(struct pinctrl_dev *pctldev, @@ -503,6 +665,7 @@ static void pcs_pinconf_group_dbg_show(struct pinctrl_dev *pctldev, } static struct pinconf_ops pcs_pinconf_ops = { + .is_generic = true, .pin_config_get = pcs_pinconf_get, .pin_config_set = pcs_pinconf_set, .pin_config_group_get = pcs_pinconf_group_get, @@ -720,12 +883,16 @@ static int pcs_get_pin_by_offset(struct pcs_device *pcs, unsigned offset) static int pcs_parse_one_pinctrl_entry(struct pcs_device *pcs, struct device_node *np, struct pinctrl_map **map, + unsigned num_configs, const char **pgnames) { struct pcs_func_vals *vals; + struct pinctrl_map *p = *map; const __be32 *mux; int size, rows, *pins, index = 0, found = 0, res = -ENOMEM; struct pcs_function *function; + unsigned long *config; + u32 value; mux = of_get_property(np, PCS_MUX_NAME, &size); if ((!mux) || (size < sizeof(*mux) * 2)) { @@ -773,12 +940,42 @@ static int pcs_parse_one_pinctrl_entry(struct pcs_device *pcs, if (res < 0) goto free_function; - (*map)->type = PIN_MAP_TYPE_MUX_GROUP; - (*map)->data.mux.group = np->name; - (*map)->data.mux.function = np->name; + p->type = PIN_MAP_TYPE_MUX_GROUP; + p->data.mux.group = np->name; + p->data.mux.function = np->name; + + if (!num_configs) + return 0; + config = devm_kzalloc(pcs->dev, sizeof(*config) * num_configs, + GFP_KERNEL); + if (!config) { + res = -ENOMEM; + goto free_pingroup; + } + index = 0; + if (!of_property_read_u32(np, PCS_SCHMITT_NAME, &value)) + config[index++] = + pinconf_to_config_packed(PIN_CONFIG_INPUT_SCHMITT, + value & 0xffff); + if (!of_property_read_u32(np, PCS_BIAS_NAME, &value)) + config[index++] = + pinconf_to_config_packed(PIN_CONFIG_BIAS_DISABLE, + value & 0xffff); + if (!of_property_read_u32(np, PCS_POWER_SOURCE_NAME, &value)) + config[index++] = + pinconf_to_config_packed(PIN_CONFIG_POWER_SOURCE, + value & 0xffff); + p++; + p->type = PIN_MAP_TYPE_CONFIGS_GROUP; + p->data.configs.group_or_pin = np->name; + p->data.configs.configs = config; + p->data.configs.num_configs = num_configs; return 0; +free_pingroup: + pcs_free_pingroups(pcs); + free_function: pcs_remove_function(pcs, function); @@ -790,6 +987,29 @@ free_vals: return res; } + +static int pcs_dt_check_maps(struct device_node *np, unsigned *num_maps, + unsigned *num_configs) +{ + unsigned size; + + *num_maps = 0; + *num_configs = 0; + if (of_get_property(np, PCS_MUX_NAME, &size)) + (*num_maps)++; + if (of_get_property(np, PCS_SCHMITT_NAME, &size)) + (*num_configs)++; + if (of_get_property(np, PCS_BIAS_NAME, &size)) + (*num_configs)++; + if (of_get_property(np, PCS_POWER_SOURCE_NAME, &size)) + (*num_configs)++; + if (*num_configs) + (*num_maps)++; + if (!(*num_maps)) + return -EINVAL; + return 0; +} + /** * pcs_dt_node_to_map() - allocates and parses pinctrl maps * @pctldev: pinctrl instance @@ -803,29 +1023,32 @@ static int pcs_dt_node_to_map(struct pinctrl_dev *pctldev, { struct pcs_device *pcs; const char **pgnames; + unsigned num_configs; int ret; pcs = pinctrl_dev_get_drvdata(pctldev); - *map = devm_kzalloc(pcs->dev, sizeof(**map), GFP_KERNEL); + ret = pcs_dt_check_maps(np_config, num_maps, &num_configs); + if (ret) + return ret; + + *map = devm_kzalloc(pcs->dev, sizeof(**map) * (*num_maps), GFP_KERNEL); if (!map) return -ENOMEM; - *num_maps = 0; - pgnames = devm_kzalloc(pcs->dev, sizeof(*pgnames), GFP_KERNEL); if (!pgnames) { ret = -ENOMEM; goto free_map; } - ret = pcs_parse_one_pinctrl_entry(pcs, np_config, map, pgnames); + ret = pcs_parse_one_pinctrl_entry(pcs, np_config, map, + num_configs, pgnames); if (ret < 0) { dev_err(pcs->dev, "no pins entries for %s\n", np_config->name); goto free_pgnames; } - *num_maps = 1; return 0; @@ -1015,6 +1238,43 @@ static int __devinit pcs_probe(struct platform_device *pdev) if (ret) pcs->foff = PCS_OFF_DISABLED; + ret = of_property_read_u32(np, "pinctrl-single,power-source-mask", + &pcs->psmask); + if (ret) + pcs->psmask = PCS_OFF_DISABLED; + ret = of_property_read_u32(np, "pinctrl-single,power-source-shift", + &pcs->psshift); + if (ret) + pcs->psshift = PCS_OFF_DISABLED; + ret = of_property_read_u32(np, "pinctrl-single,bias-mask", + &pcs->bmask); + if (ret) + pcs->bmask = PCS_OFF_DISABLED; + ret = of_property_read_u32(np, "pinctrl-single,bias-shift", + &pcs->bshift); + if (ret) + pcs->bshift = PCS_OFF_DISABLED; + ret = of_property_read_u32(np, "pinctrl-single,bias-disable", + &pcs->bdis); + if (ret) + pcs->bdis = PCS_OFF_DISABLED; + ret = of_property_read_u32(np, "pinctrl-single,bias-pull-up", + &pcs->bpullup); + if (ret) + pcs->bpullup = PCS_OFF_DISABLED; + ret = of_property_read_u32(np, "pinctrl-single,bias-pull-down", + &pcs->bpulldown); + if (ret) + pcs->bpulldown = PCS_OFF_DISABLED; + ret = of_property_read_u32(np, "pinctrl-single,input-schmitt-mask", + &pcs->ismask); + if (ret) + pcs->ismask = PCS_OFF_DISABLED; + ret = of_property_read_u32(np, "pinctrl-single,input-schmitt-shift", + &pcs->isshift); + if (ret) + pcs->isshift = PCS_OFF_DISABLED; + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); if (!res) { dev_err(pcs->dev, "could not get resource\n");