Received: by 10.213.65.68 with SMTP id h4csp1225450imn; Wed, 14 Mar 2018 13:28:32 -0700 (PDT) X-Google-Smtp-Source: AG47ELsQyx3WAokgQ+6qzyKqzDYMut/Zx9kfTXIfOYwdVbxa3twsTJ8k35O/XK1iCJpCnXdfGEI9 X-Received: by 2002:a17:902:5389:: with SMTP id c9-v6mr614739pli.90.1521059312495; Wed, 14 Mar 2018 13:28:32 -0700 (PDT) ARC-Seal: i=1; a=rsa-sha256; t=1521059312; cv=none; d=google.com; s=arc-20160816; b=RPtF5Lyve9AFXbHrJEijjmdxWCGI6+EkKB5UnOUg6SEVrbzYgdywEKOgASbo/XvgyP nlUlEbfsPWLDgOSexMHNkmanKFn04rkdQgmCaa99DRczko7LldwP2vdh5nAuAblbMHjQ T4V32SsIerOUdwGRBHT52vsoiMS+cFa0otGOvqk7/0S1v9c0TdakSfAu1lCJnp608Odw X1JtimUqOBjPDTgrSJbGYWKqIbQQU2blt98iWVvd79KcsbcSD06GtRQK84s8dDHCjTPV opSp5Br77K5vLmdNhK1/ICmdbdiamHyUVUQnRBPJcGQ7efImZ7Yrnr61og1rtkBLV8d+ n8+w== ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=arc-20160816; h=list-id:precedence:sender:content-transfer-encoding:mime-version :user-agent:references:in-reply-to:message-id:cc:subject:date:to :from:arc-authentication-results; bh=4QAt1IjjOUwOQKYps7NBx+dzWqxCL2qKvE5ys3vp/qY=; b=oAu0j4cbpq1QWRz5evB/ervn2h7QjQw77p2owr3z8KoAA6u636WaMkII04yOTmtr32 zrZ/o7yQoLTsA71XQOjJPQVFSM0Q+edzK8AGfxeIwZVlPkYd7LqrCweowC8WkCc8pm1B iBUhAtsQxgP6XVpQfRK1n8dpR9PgKIkYKV25SXKCxrbbnuzOffS0xLbpsGOP+JTfr+eI aT/ZaFMHze3SLsD6oGi/rOI35hrmptpzC2KLT5DD+2R+ZoH+exfRAHoHzWgdxHOTxOei yl+k2sOYvA0Um8mJ8cSBQKe43MYhcLmInX3UbFHv7SwXaQxwRR5odPjd9Bi/JbrBWcAa 5Ecw== ARC-Authentication-Results: i=1; mx.google.com; spf=pass (google.com: best guess record for domain of linux-kernel-owner@vger.kernel.org designates 209.132.180.67 as permitted sender) smtp.mailfrom=linux-kernel-owner@vger.kernel.org Return-Path: Received: from vger.kernel.org (vger.kernel.org. [209.132.180.67]) by mx.google.com with ESMTP id g4si2584887pfh.98.2018.03.14.13.28.17; Wed, 14 Mar 2018 13:28:32 -0700 (PDT) Received-SPF: pass (google.com: best guess record for domain of linux-kernel-owner@vger.kernel.org designates 209.132.180.67 as permitted sender) client-ip=209.132.180.67; Authentication-Results: mx.google.com; spf=pass (google.com: best guess record for domain of linux-kernel-owner@vger.kernel.org designates 209.132.180.67 as permitted sender) smtp.mailfrom=linux-kernel-owner@vger.kernel.org Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1752243AbeCNUXT (ORCPT + 99 others); Wed, 14 Mar 2018 16:23:19 -0400 Received: from mx2.suse.de ([195.135.220.15]:38529 "EHLO mx2.suse.de" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1752099AbeCNUXN (ORCPT ); Wed, 14 Mar 2018 16:23:13 -0400 X-Virus-Scanned: by amavisd-new at test-mx.suse.de Received: from relay2.suse.de (charybdis-ext.suse.de [195.135.220.254]) by mx2.suse.de (Postfix) with ESMTP id 563EDAF73; Wed, 14 Mar 2018 20:23:12 +0000 (UTC) From: NeilBrown To: Greg Kroah-Hartman Date: Thu, 15 Mar 2018 07:22:35 +1100 Subject: [PATCH 02/13] staging: mt7621-pinctrl: ralink: add pinctrl driver Cc: devel@driverdev.osuosl.org, lkml , John Crispin Message-ID: <152105895506.22262.17783863404921290235.stgit@noble> In-Reply-To: <152105892255.22262.1902152685410223215.stgit@noble> References: <152105892255.22262.1902152685410223215.stgit@noble> User-Agent: StGit/0.17.1-dirty MIME-Version: 1.0 Content-Type: text/plain; charset="utf-8" Content-Transfer-Encoding: 7bit Sender: linux-kernel-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-kernel@vger.kernel.org From: John Crispin Signed-off-by: John Crispin Signed-off-by: NeilBrown --- drivers/staging/Kconfig | 2 drivers/staging/Makefile | 1 drivers/staging/mt7621-pinctrl/Kconfig | 4 drivers/staging/mt7621-pinctrl/Makefile | 3 drivers/staging/mt7621-pinctrl/TODO | 6 drivers/staging/mt7621-pinctrl/pinctrl-rt2880.c | 472 +++++++++++++++++++++++ 6 files changed, 488 insertions(+) create mode 100644 drivers/staging/mt7621-pinctrl/Kconfig create mode 100644 drivers/staging/mt7621-pinctrl/Makefile create mode 100644 drivers/staging/mt7621-pinctrl/TODO create mode 100644 drivers/staging/mt7621-pinctrl/pinctrl-rt2880.c diff --git a/drivers/staging/Kconfig b/drivers/staging/Kconfig index e95ab683331e..cdd04c8b4445 100644 --- a/drivers/staging/Kconfig +++ b/drivers/staging/Kconfig @@ -122,4 +122,6 @@ source "drivers/staging/vboxvideo/Kconfig" source "drivers/staging/pi433/Kconfig" +source "drivers/staging/mt7621-pinctrl/Kconfig" + endif # STAGING diff --git a/drivers/staging/Makefile b/drivers/staging/Makefile index 4e79a4ad6cf6..db90d85a80d3 100644 --- a/drivers/staging/Makefile +++ b/drivers/staging/Makefile @@ -53,3 +53,4 @@ obj-$(CONFIG_CRYPTO_DEV_CCREE) += ccree/ obj-$(CONFIG_DRM_VBOXVIDEO) += vboxvideo/ obj-$(CONFIG_PI433) += pi433/ obj-$(CONFIG_SOC_MT7621) += mt7621-pci/ +obj-$(CONFIG_SOC_MT7621) += mt7621-pinctrl/ diff --git a/drivers/staging/mt7621-pinctrl/Kconfig b/drivers/staging/mt7621-pinctrl/Kconfig new file mode 100644 index 000000000000..37cf9c3273be --- /dev/null +++ b/drivers/staging/mt7621-pinctrl/Kconfig @@ -0,0 +1,4 @@ +config PINCTRL_RT2880 + bool "RT2800 pinctrl driver for RALINK/Mediatek SOCs" + depends on RALINK + select PINMUX diff --git a/drivers/staging/mt7621-pinctrl/Makefile b/drivers/staging/mt7621-pinctrl/Makefile new file mode 100644 index 000000000000..856102137a1e --- /dev/null +++ b/drivers/staging/mt7621-pinctrl/Makefile @@ -0,0 +1,3 @@ +obj-$(CONFIG_PINCTRL_RT2880) += pinctrl-rt2880.o + +ccflags-y += -I$(srctree)/drivers/pinctrl diff --git a/drivers/staging/mt7621-pinctrl/TODO b/drivers/staging/mt7621-pinctrl/TODO new file mode 100644 index 000000000000..b2c235a16d5c --- /dev/null +++ b/drivers/staging/mt7621-pinctrl/TODO @@ -0,0 +1,6 @@ + +- general code review and cleanup +- should probably be always selected by 'config RALINK' +- ensure device-tree requirements are documented + +Cc: NeilBrown diff --git a/drivers/staging/mt7621-pinctrl/pinctrl-rt2880.c b/drivers/staging/mt7621-pinctrl/pinctrl-rt2880.c new file mode 100644 index 000000000000..3d2d1c2a006f --- /dev/null +++ b/drivers/staging/mt7621-pinctrl/pinctrl-rt2880.c @@ -0,0 +1,472 @@ +/* + * linux/drivers/pinctrl/pinctrl-rt2880.c + * + * 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 + * publishhed by the Free Software Foundation. + * + * Copyright (C) 2013 John Crispin + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include "core.h" + +#define SYSC_REG_GPIO_MODE 0x60 +#define SYSC_REG_GPIO_MODE2 0x64 + +struct rt2880_priv { + struct device *dev; + + struct pinctrl_pin_desc *pads; + struct pinctrl_desc *desc; + + struct rt2880_pmx_func **func; + int func_count; + + struct rt2880_pmx_group *groups; + const char **group_names; + int group_count; + + uint8_t *gpio; + int max_pins; +}; + +static int rt2880_get_group_count(struct pinctrl_dev *pctrldev) +{ + struct rt2880_priv *p = pinctrl_dev_get_drvdata(pctrldev); + + return p->group_count; +} + +static const char *rt2880_get_group_name(struct pinctrl_dev *pctrldev, + unsigned group) +{ + struct rt2880_priv *p = pinctrl_dev_get_drvdata(pctrldev); + + if (group >= p->group_count) + return NULL; + + return p->group_names[group]; +} + +static int rt2880_get_group_pins(struct pinctrl_dev *pctrldev, + unsigned group, + const unsigned **pins, + unsigned *num_pins) +{ + struct rt2880_priv *p = pinctrl_dev_get_drvdata(pctrldev); + + if (group >= p->group_count) + return -EINVAL; + + *pins = p->groups[group].func[0].pins; + *num_pins = p->groups[group].func[0].pin_count; + + return 0; +} + +static void rt2880_pinctrl_dt_free_map(struct pinctrl_dev *pctrldev, + struct pinctrl_map *map, unsigned num_maps) +{ + int i; + + for (i = 0; i < num_maps; i++) + if (map[i].type == PIN_MAP_TYPE_CONFIGS_PIN || + map[i].type == PIN_MAP_TYPE_CONFIGS_GROUP) + kfree(map[i].data.configs.configs); + kfree(map); +} + +static void rt2880_pinctrl_pin_dbg_show(struct pinctrl_dev *pctrldev, + struct seq_file *s, + unsigned offset) +{ + seq_printf(s, "ralink pio"); +} + +static void rt2880_pinctrl_dt_subnode_to_map(struct pinctrl_dev *pctrldev, + struct device_node *np, + struct pinctrl_map **map) +{ + const char *function; + int func = of_property_read_string(np, "ralink,function", &function); + int grps = of_property_count_strings(np, "ralink,group"); + int i; + + if (func || !grps) + return; + + for (i = 0; i < grps; i++) { + const char *group; + + of_property_read_string_index(np, "ralink,group", i, &group); + + (*map)->type = PIN_MAP_TYPE_MUX_GROUP; + (*map)->name = function; + (*map)->data.mux.group = group; + (*map)->data.mux.function = function; + (*map)++; + } +} + +static int rt2880_pinctrl_dt_node_to_map(struct pinctrl_dev *pctrldev, + struct device_node *np_config, + struct pinctrl_map **map, + unsigned *num_maps) +{ + int max_maps = 0; + struct pinctrl_map *tmp; + struct device_node *np; + + for_each_child_of_node(np_config, np) { + int ret = of_property_count_strings(np, "ralink,group"); + + if (ret >= 0) + max_maps += ret; + } + + if (!max_maps) + return max_maps; + + *map = kzalloc(max_maps * sizeof(struct pinctrl_map), GFP_KERNEL); + if (!*map) + return -ENOMEM; + + tmp = *map; + + for_each_child_of_node(np_config, np) + rt2880_pinctrl_dt_subnode_to_map(pctrldev, np, &tmp); + *num_maps = max_maps; + + return 0; +} + +static const struct pinctrl_ops rt2880_pctrl_ops = { + .get_groups_count = rt2880_get_group_count, + .get_group_name = rt2880_get_group_name, + .get_group_pins = rt2880_get_group_pins, + .pin_dbg_show = rt2880_pinctrl_pin_dbg_show, + .dt_node_to_map = rt2880_pinctrl_dt_node_to_map, + .dt_free_map = rt2880_pinctrl_dt_free_map, +}; + +static int rt2880_pmx_func_count(struct pinctrl_dev *pctrldev) +{ + struct rt2880_priv *p = pinctrl_dev_get_drvdata(pctrldev); + + return p->func_count; +} + +static const char *rt2880_pmx_func_name(struct pinctrl_dev *pctrldev, + unsigned func) +{ + struct rt2880_priv *p = pinctrl_dev_get_drvdata(pctrldev); + + return p->func[func]->name; +} + +static int rt2880_pmx_group_get_groups(struct pinctrl_dev *pctrldev, + unsigned func, + const char * const **groups, + unsigned * const num_groups) +{ + struct rt2880_priv *p = pinctrl_dev_get_drvdata(pctrldev); + + if (p->func[func]->group_count == 1) + *groups = &p->group_names[p->func[func]->groups[0]]; + else + *groups = p->group_names; + + *num_groups = p->func[func]->group_count; + + return 0; +} + +static int rt2880_pmx_group_enable(struct pinctrl_dev *pctrldev, + unsigned func, + unsigned group) +{ + struct rt2880_priv *p = pinctrl_dev_get_drvdata(pctrldev); + u32 mode = 0; + u32 reg = SYSC_REG_GPIO_MODE; + int i; + int shift; + + /* dont allow double use */ + if (p->groups[group].enabled) { + dev_err(p->dev, "%s is already enabled\n", p->groups[group].name); + return -EBUSY; + } + + p->groups[group].enabled = 1; + p->func[func]->enabled = 1; + + shift = p->groups[group].shift; + if (shift >= 32) { + shift -= 32; + reg = SYSC_REG_GPIO_MODE2; + } + mode = rt_sysc_r32(reg); + mode &= ~(p->groups[group].mask << shift); + + /* mark the pins as gpio */ + for (i = 0; i < p->groups[group].func[0].pin_count; i++) + p->gpio[p->groups[group].func[0].pins[i]] = 1; + + /* function 0 is gpio and needs special handling */ + if (func == 0) { + mode |= p->groups[group].gpio << shift; + } else { + for (i = 0; i < p->func[func]->pin_count; i++) + p->gpio[p->func[func]->pins[i]] = 0; + mode |= p->func[func]->value << shift; + } + rt_sysc_w32(mode, reg); + + return 0; +} + +static int rt2880_pmx_group_gpio_request_enable(struct pinctrl_dev *pctrldev, + struct pinctrl_gpio_range *range, + unsigned pin) +{ + struct rt2880_priv *p = pinctrl_dev_get_drvdata(pctrldev); + + if (!p->gpio[pin]) { + dev_err(p->dev, "pin %d is not set to gpio mux\n", pin); + return -EINVAL; + } + + return 0; +} + +static const struct pinmux_ops rt2880_pmx_group_ops = { + .get_functions_count = rt2880_pmx_func_count, + .get_function_name = rt2880_pmx_func_name, + .get_function_groups = rt2880_pmx_group_get_groups, + .set_mux = rt2880_pmx_group_enable, + .gpio_request_enable = rt2880_pmx_group_gpio_request_enable, +}; + +static struct pinctrl_desc rt2880_pctrl_desc = { + .owner = THIS_MODULE, + .name = "rt2880-pinmux", + .pctlops = &rt2880_pctrl_ops, + .pmxops = &rt2880_pmx_group_ops, +}; + +static struct rt2880_pmx_func gpio_func = { + .name = "gpio", +}; + +static int rt2880_pinmux_index(struct rt2880_priv *p) +{ + struct rt2880_pmx_func **f; + struct rt2880_pmx_group *mux = p->groups; + int i, j, c = 0; + + /* count the mux functions */ + while (mux->name) { + p->group_count++; + mux++; + } + + /* allocate the group names array needed by the gpio function */ + p->group_names = devm_kzalloc(p->dev, sizeof(char *) * p->group_count, GFP_KERNEL); + if (!p->group_names) + return -1; + + for (i = 0; i < p->group_count; i++) { + p->group_names[i] = p->groups[i].name; + p->func_count += p->groups[i].func_count; + } + + /* we have a dummy function[0] for gpio */ + p->func_count++; + + /* allocate our function and group mapping index buffers */ + f = p->func = devm_kzalloc(p->dev, sizeof(struct rt2880_pmx_func) * p->func_count, GFP_KERNEL); + gpio_func.groups = devm_kzalloc(p->dev, sizeof(int) * p->group_count, GFP_KERNEL); + if (!f || !gpio_func.groups) + return -1; + + /* add a backpointer to the function so it knows its group */ + gpio_func.group_count = p->group_count; + for (i = 0; i < gpio_func.group_count; i++) + gpio_func.groups[i] = i; + + f[c] = &gpio_func; + c++; + + /* add remaining functions */ + for (i = 0; i < p->group_count; i++) { + for (j = 0; j < p->groups[i].func_count; j++) { + f[c] = &p->groups[i].func[j]; + f[c]->groups = devm_kzalloc(p->dev, sizeof(int), GFP_KERNEL); + f[c]->groups[0] = i; + f[c]->group_count = 1; + c++; + } + } + return 0; +} + +static int rt2880_pinmux_pins(struct rt2880_priv *p) +{ + int i, j; + + /* loop over the functions and initialize the pins array. also work out the highest pin used */ + for (i = 0; i < p->func_count; i++) { + int pin; + + if (!p->func[i]->pin_count) + continue; + + p->func[i]->pins = devm_kzalloc(p->dev, sizeof(int) * p->func[i]->pin_count, GFP_KERNEL); + for (j = 0; j < p->func[i]->pin_count; j++) + p->func[i]->pins[j] = p->func[i]->pin_first + j; + + pin = p->func[i]->pin_first + p->func[i]->pin_count; + if (pin > p->max_pins) + p->max_pins = pin; + } + + /* the buffer that tells us which pins are gpio */ + p->gpio = devm_kzalloc(p->dev,sizeof(uint8_t) * p->max_pins, + GFP_KERNEL); + /* the pads needed to tell pinctrl about our pins */ + p->pads = devm_kzalloc(p->dev, + sizeof(struct pinctrl_pin_desc) * p->max_pins, + GFP_KERNEL); + if (!p->pads || !p->gpio ) { + dev_err(p->dev, "Failed to allocate gpio data\n"); + return -ENOMEM; + } + + memset(p->gpio, 1, sizeof(uint8_t) * p->max_pins); + for (i = 0; i < p->func_count; i++) { + if (!p->func[i]->pin_count) + continue; + + for (j = 0; j < p->func[i]->pin_count; j++) + p->gpio[p->func[i]->pins[j]] = 0; + } + + /* pin 0 is always a gpio */ + p->gpio[0] = 1; + + /* set the pads */ + for (i = 0; i < p->max_pins; i++) { + /* strlen("ioXY") + 1 = 5 */ + char *name = devm_kzalloc(p->dev, 5, GFP_KERNEL); + + if (!name) { + dev_err(p->dev, "Failed to allocate pad name\n"); + return -ENOMEM; + } + snprintf(name, 5, "io%d", i); + p->pads[i].number = i; + p->pads[i].name = name; + } + p->desc->pins = p->pads; + p->desc->npins = p->max_pins; + + return 0; +} + +static int rt2880_pinmux_probe(struct platform_device *pdev) +{ + struct rt2880_priv *p; + struct pinctrl_dev *dev; + struct device_node *np; + + if (!rt2880_pinmux_data) + return -ENOSYS; + + /* setup the private data */ + p = devm_kzalloc(&pdev->dev, sizeof(struct rt2880_priv), GFP_KERNEL); + if (!p) + return -ENOMEM; + + p->dev = &pdev->dev; + p->desc = &rt2880_pctrl_desc; + p->groups = rt2880_pinmux_data; + platform_set_drvdata(pdev, p); + + /* init the device */ + if (rt2880_pinmux_index(p)) { + dev_err(&pdev->dev, "failed to load index\n"); + return -EINVAL; + } + if (rt2880_pinmux_pins(p)) { + dev_err(&pdev->dev, "failed to load pins\n"); + return -EINVAL; + } + dev = pinctrl_register(p->desc, &pdev->dev, p); + if (IS_ERR(dev)) + return PTR_ERR(dev); + + /* finalize by adding gpio ranges for enables gpio controllers */ + for_each_compatible_node(np, NULL, "ralink,rt2880-gpio") { + const __be32 *ngpio, *gpiobase; + struct pinctrl_gpio_range *range; + char *name; + + if (!of_device_is_available(np)) + continue; + + ngpio = of_get_property(np, "ralink,num-gpios", NULL); + gpiobase = of_get_property(np, "ralink,gpio-base", NULL); + if (!ngpio || !gpiobase) { + dev_err(&pdev->dev, "failed to load chip info\n"); + return -EINVAL; + } + + range = devm_kzalloc(p->dev, sizeof(struct pinctrl_gpio_range) + 4, GFP_KERNEL); + range->name = name = (char *) &range[1]; + sprintf(name, "pio"); + range->npins = __be32_to_cpu(*ngpio); + range->base = __be32_to_cpu(*gpiobase); + range->pin_base = range->base; + pinctrl_add_gpio_range(dev, range); + } + + return 0; +} + +static const struct of_device_id rt2880_pinmux_match[] = { + { .compatible = "ralink,rt2880-pinmux" }, + {}, +}; +MODULE_DEVICE_TABLE(of, rt2880_pinmux_match); + +static struct platform_driver rt2880_pinmux_driver = { + .probe = rt2880_pinmux_probe, + .driver = { + .name = "rt2880-pinmux", + .owner = THIS_MODULE, + .of_match_table = rt2880_pinmux_match, + }, +}; + +int __init rt2880_pinmux_init(void) +{ + return platform_driver_register(&rt2880_pinmux_driver); +} + +core_initcall_sync(rt2880_pinmux_init);