From patchwork Wed Oct 31 23:04:15 2012 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Haojian Zhuang X-Patchwork-Id: 1682311 Return-Path: X-Original-To: patchwork-linux-arm@patchwork.kernel.org Delivered-To: patchwork-process-083081@patchwork1.kernel.org Received: from merlin.infradead.org (merlin.infradead.org [205.233.59.134]) by patchwork1.kernel.org (Postfix) with ESMTP id B8E274005F for ; Wed, 31 Oct 2012 23:08:48 +0000 (UTC) Received: from localhost ([::1] helo=merlin.infradead.org) by merlin.infradead.org with esmtp (Exim 4.76 #1 (Red Hat Linux)) id 1TThLi-0000FU-GW; Wed, 31 Oct 2012 23:05:26 +0000 Received: from mail-pb0-f49.google.com ([209.85.160.49]) by merlin.infradead.org with esmtps (Exim 4.76 #1 (Red Hat Linux)) id 1TThKw-0008U1-Do for linux-arm-kernel@lists.infradead.org; Wed, 31 Oct 2012 23:04:39 +0000 Received: by mail-pb0-f49.google.com with SMTP id xa7so1220810pbc.36 for ; Wed, 31 Oct 2012 16:04:35 -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=nqUt3rLbc0kEhky98BlE0soaJ3awG4SPaHholoXucEo=; b=kQZrNKlVvEqHftlypiEc1Lz8RHBweHgokDwYMsHHMGEEruBf6y1kLZW7FIKYyRDmup rcA/PCdzSqkP4wOAznf8FEsgoYJa//yyWRFRghlnls+dFruxuXvztsoGkB6d8EW95XFs Vi4JJCMGfbz8CpdiYbQ+a2D0zHMGfnWXj+zD+AXVIABt/+PDqVu0oPjOUXsnfQcqZG7t 4nbIGj/LUW+JdZa2NXYsCm3/T144Dh3ce5a8yT8VDxvb2IgNWgzcFbPZwybHF/HK2d9P 9ZzjSJVN+6AUvcriIlYWmlbdxRHoj5Mv587IuCadxoVNMxIH4cWP1uwsiXFqM64wS/cm xXPQ== Received: by 10.68.135.42 with SMTP id pp10mr8509655pbb.159.1351724675895; Wed, 31 Oct 2012 16:04:35 -0700 (PDT) Received: from localhost ([61.172.13.185]) by mx.google.com with ESMTPS id ku7sm1172572pbc.0.2012.10.31.16.04.32 (version=TLSv1/SSLv3 cipher=OTHER); Wed, 31 Oct 2012 16:04:34 -0700 (PDT) From: Haojian Zhuang To: swarren@wwwdotorg.org, linux-arm-kernel@lists.infradead.org, arnd@arndb.de, tony@atomide.com, devicetree-discuss@lists.org, linus.walleij@linaro.org Subject: [PATCH v3 3/9] pinctrl: single: support pinconf generic Date: Thu, 1 Nov 2012 07:04:15 +0800 Message-Id: <1351724661-29050-4-git-send-email-haojian.zhuang@gmail.com> X-Mailer: git-send-email 1.7.9.5 In-Reply-To: <1351724661-29050-1-git-send-email-haojian.zhuang@gmail.com> References: <1351724661-29050-1-git-send-email-haojian.zhuang@gmail.com> X-CRM114-Version: 20100106-BlameMichelson ( TRE 0.8.0 (BSD) ) MR-646709E3 X-CRM114-CacheID: sfid-20121031_190438_689909_F51DF301 X-CRM114-Status: GOOD ( 25.60 ) 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.160.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 | 1 + drivers/pinctrl/pinctrl-single.c | 276 ++++++++++++++++++++++++++++++++++++-- 2 files changed, 266 insertions(+), 11 deletions(-) diff --git a/drivers/pinctrl/Kconfig b/drivers/pinctrl/Kconfig index 7bf914d..e9f2d2d 100644 --- a/drivers/pinctrl/Kconfig +++ b/drivers/pinctrl/Kconfig @@ -139,6 +139,7 @@ config PINCTRL_SINGLE 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 a7c5fdd..3b97b65 100644 --- a/drivers/pinctrl/pinctrl-single.c +++ b/drivers/pinctrl/pinctrl-single.c @@ -20,6 +20,7 @@ #include #include +#include #include #include @@ -28,6 +29,9 @@ #define DRIVER_NAME "pinctrl-single" #define PCS_MUX_PINS_NAME "pinctrl-single,pins" #define PCS_MUX_BITS_NAME "pinctrl-single,bits" +#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 @@ -130,6 +134,15 @@ struct pcs_name { * @fshift: function register shift * @foff: value to turn mux off * @fmax: max number of functions in fmask + * @bmask: bias mask in pinconf + * @bshift: bias register shift + * @bdis: bias disable value in pinconf + * @bpullup: bias pull up value in pinconf + * @bpulldown: bias pull down value in pinconf + * @ismask: input schmitt mask in pinconf + * @isshift: input schmitt register shift + * @psmask: power source mask in pinconf + * @psshift: power source register shift * @names: array of register names for pins * @pins: physical pins on the SoC * @pgtree: pingroup index radix tree @@ -156,6 +169,15 @@ struct pcs_device { unsigned fshift; unsigned foff; unsigned fmax; + unsigned bmask; + unsigned bshift; + unsigned bdis; + unsigned bpullup; + unsigned bpulldown; + unsigned ismask; + unsigned isshift; + unsigned psmask; + unsigned psshift; bool bits_per_mux; struct pcs_name *names; struct pcs_data pins; @@ -452,28 +474,163 @@ static struct pinmux_ops pcs_pinmux_ops = { .gpio_request_enable = pcs_request_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->read(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->read(pcs->base + offset) & ~mask; + pcs->write(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->read(pcs->base + offset) & ~mask; + pcs->write(ret | data, pcs->base + offset); + } + return 0; } static void pcs_pinconf_dbg_show(struct pinctrl_dev *pctldev, @@ -487,6 +644,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, @@ -688,6 +846,7 @@ static int pcs_get_pin_by_offset(struct pcs_device *pcs, unsigned offset) * @pcs: pinctrl driver instance * @np: device node of the mux entry * @map: map entry + * @num_configs: number of pin configurations * @pgnames: pingroup names * * Note that this binding currently supports only sets of one register + value. @@ -704,12 +863,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, params, rows, *pins, index = 0, found = 0, res = -ENOMEM; struct pcs_function *function; + unsigned long *config; + u32 value; if (pcs->bits_per_mux) { params = 3; @@ -772,12 +935,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); @@ -789,6 +982,30 @@ 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_PINS_NAME, &size) + || of_get_property(np, PCS_MUX_BITS_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 @@ -802,29 +1019,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; @@ -1009,6 +1229,40 @@ static int __devinit pcs_probe(struct platform_device *pdev) pcs->bits_per_mux = of_property_read_bool(np, "pinctrl-single,bit-per-mux"); + ret = of_property_read_u32(np, "pinctrl-single,power-source-mask", + &pcs->psmask); + if (ret) { + pcs->psmask = PCS_OFF_DISABLED; + pcs->psshift = PCS_OFF_DISABLED; + } else + pcs->psshift = ffs(pcs->psmask) - 1; + ret = of_property_read_u32(np, "pinctrl-single,bias-mask", + &pcs->bmask); + if (ret) { + pcs->bmask = PCS_OFF_DISABLED; + pcs->bshift = PCS_OFF_DISABLED; + } else + pcs->bshift = ffs(pcs->bmask) - 1; + 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; + pcs->isshift = PCS_OFF_DISABLED; + } else + pcs->isshift = ffs(pcs->ismask) - 1; + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); if (!res) { dev_err(pcs->dev, "could not get resource\n");