| /* | |
| * Pinctrl driver for the Wondermedia SoC's | |
| * | |
| * Copyright (c) 2013 Tony Prisk <linux@prisktech.co.nz> | |
| * | |
| * This program is free software; you can redistribute it and/or modify it | |
| * under the terms and conditions of the GNU General Public License, | |
| * version 2, as published by the Free Software Foundation. | |
| * | |
| * This program is distributed in the hope it will be useful, but WITHOUT | |
| * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or | |
| * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for | |
| * more details. | |
| */ | |
| #include <linux/err.h> | |
| #include <linux/gpio/driver.h> | |
| #include <linux/interrupt.h> | |
| #include <linux/io.h> | |
| #include <linux/irq.h> | |
| #include <linux/module.h> | |
| #include <linux/of.h> | |
| #include <linux/of_irq.h> | |
| #include <linux/pinctrl/consumer.h> | |
| #include <linux/pinctrl/machine.h> | |
| #include <linux/pinctrl/pinconf.h> | |
| #include <linux/pinctrl/pinconf-generic.h> | |
| #include <linux/pinctrl/pinctrl.h> | |
| #include <linux/pinctrl/pinmux.h> | |
| #include <linux/platform_device.h> | |
| #include <linux/slab.h> | |
| #include "pinctrl-wmt.h" | |
| static inline void wmt_setbits(struct wmt_pinctrl_data *data, u32 reg, | |
| u32 mask) | |
| { | |
| u32 val; | |
| val = readl_relaxed(data->base + reg); | |
| val |= mask; | |
| writel_relaxed(val, data->base + reg); | |
| } | |
| static inline void wmt_clearbits(struct wmt_pinctrl_data *data, u32 reg, | |
| u32 mask) | |
| { | |
| u32 val; | |
| val = readl_relaxed(data->base + reg); | |
| val &= ~mask; | |
| writel_relaxed(val, data->base + reg); | |
| } | |
| enum wmt_func_sel { | |
| WMT_FSEL_GPIO_IN = 0, | |
| WMT_FSEL_GPIO_OUT = 1, | |
| WMT_FSEL_ALT = 2, | |
| WMT_FSEL_COUNT = 3, | |
| }; | |
| static const char * const wmt_functions[WMT_FSEL_COUNT] = { | |
| [WMT_FSEL_GPIO_IN] = "gpio_in", | |
| [WMT_FSEL_GPIO_OUT] = "gpio_out", | |
| [WMT_FSEL_ALT] = "alt", | |
| }; | |
| static int wmt_pmx_get_functions_count(struct pinctrl_dev *pctldev) | |
| { | |
| return WMT_FSEL_COUNT; | |
| } | |
| static const char *wmt_pmx_get_function_name(struct pinctrl_dev *pctldev, | |
| unsigned selector) | |
| { | |
| return wmt_functions[selector]; | |
| } | |
| static int wmt_pmx_get_function_groups(struct pinctrl_dev *pctldev, | |
| unsigned selector, | |
| const char * const **groups, | |
| unsigned * const num_groups) | |
| { | |
| struct wmt_pinctrl_data *data = pinctrl_dev_get_drvdata(pctldev); | |
| /* every pin does every function */ | |
| *groups = data->groups; | |
| *num_groups = data->ngroups; | |
| return 0; | |
| } | |
| static int wmt_set_pinmux(struct wmt_pinctrl_data *data, unsigned func, | |
| unsigned pin) | |
| { | |
| u32 bank = WMT_BANK_FROM_PIN(pin); | |
| u32 bit = WMT_BIT_FROM_PIN(pin); | |
| u32 reg_en = data->banks[bank].reg_en; | |
| u32 reg_dir = data->banks[bank].reg_dir; | |
| if (reg_dir == NO_REG) { | |
| dev_err(data->dev, "pin:%d no direction register defined\n", | |
| pin); | |
| return -EINVAL; | |
| } | |
| /* | |
| * If reg_en == NO_REG, we assume it is a dedicated GPIO and cannot be | |
| * disabled (as on VT8500) and that no alternate function is available. | |
| */ | |
| switch (func) { | |
| case WMT_FSEL_GPIO_IN: | |
| if (reg_en != NO_REG) | |
| wmt_setbits(data, reg_en, BIT(bit)); | |
| wmt_clearbits(data, reg_dir, BIT(bit)); | |
| break; | |
| case WMT_FSEL_GPIO_OUT: | |
| if (reg_en != NO_REG) | |
| wmt_setbits(data, reg_en, BIT(bit)); | |
| wmt_setbits(data, reg_dir, BIT(bit)); | |
| break; | |
| case WMT_FSEL_ALT: | |
| if (reg_en == NO_REG) { | |
| dev_err(data->dev, "pin:%d no alt function available\n", | |
| pin); | |
| return -EINVAL; | |
| } | |
| wmt_clearbits(data, reg_en, BIT(bit)); | |
| } | |
| return 0; | |
| } | |
| static int wmt_pmx_set_mux(struct pinctrl_dev *pctldev, | |
| unsigned func_selector, | |
| unsigned group_selector) | |
| { | |
| struct wmt_pinctrl_data *data = pinctrl_dev_get_drvdata(pctldev); | |
| u32 pinnum = data->pins[group_selector].number; | |
| return wmt_set_pinmux(data, func_selector, pinnum); | |
| } | |
| static void wmt_pmx_gpio_disable_free(struct pinctrl_dev *pctldev, | |
| struct pinctrl_gpio_range *range, | |
| unsigned offset) | |
| { | |
| struct wmt_pinctrl_data *data = pinctrl_dev_get_drvdata(pctldev); | |
| /* disable by setting GPIO_IN */ | |
| wmt_set_pinmux(data, WMT_FSEL_GPIO_IN, offset); | |
| } | |
| static int wmt_pmx_gpio_set_direction(struct pinctrl_dev *pctldev, | |
| struct pinctrl_gpio_range *range, | |
| unsigned offset, | |
| bool input) | |
| { | |
| struct wmt_pinctrl_data *data = pinctrl_dev_get_drvdata(pctldev); | |
| wmt_set_pinmux(data, (input ? WMT_FSEL_GPIO_IN : WMT_FSEL_GPIO_OUT), | |
| offset); | |
| return 0; | |
| } | |
| static struct pinmux_ops wmt_pinmux_ops = { | |
| .get_functions_count = wmt_pmx_get_functions_count, | |
| .get_function_name = wmt_pmx_get_function_name, | |
| .get_function_groups = wmt_pmx_get_function_groups, | |
| .set_mux = wmt_pmx_set_mux, | |
| .gpio_disable_free = wmt_pmx_gpio_disable_free, | |
| .gpio_set_direction = wmt_pmx_gpio_set_direction, | |
| }; | |
| static int wmt_get_groups_count(struct pinctrl_dev *pctldev) | |
| { | |
| struct wmt_pinctrl_data *data = pinctrl_dev_get_drvdata(pctldev); | |
| return data->ngroups; | |
| } | |
| static const char *wmt_get_group_name(struct pinctrl_dev *pctldev, | |
| unsigned selector) | |
| { | |
| struct wmt_pinctrl_data *data = pinctrl_dev_get_drvdata(pctldev); | |
| return data->groups[selector]; | |
| } | |
| static int wmt_get_group_pins(struct pinctrl_dev *pctldev, | |
| unsigned selector, | |
| const unsigned **pins, | |
| unsigned *num_pins) | |
| { | |
| struct wmt_pinctrl_data *data = pinctrl_dev_get_drvdata(pctldev); | |
| *pins = &data->pins[selector].number; | |
| *num_pins = 1; | |
| return 0; | |
| } | |
| static int wmt_pctl_find_group_by_pin(struct wmt_pinctrl_data *data, u32 pin) | |
| { | |
| int i; | |
| for (i = 0; i < data->npins; i++) { | |
| if (data->pins[i].number == pin) | |
| return i; | |
| } | |
| return -EINVAL; | |
| } | |
| static int wmt_pctl_dt_node_to_map_func(struct wmt_pinctrl_data *data, | |
| struct device_node *np, | |
| u32 pin, u32 fnum, | |
| struct pinctrl_map **maps) | |
| { | |
| int group; | |
| struct pinctrl_map *map = *maps; | |
| if (fnum >= ARRAY_SIZE(wmt_functions)) { | |
| dev_err(data->dev, "invalid wm,function %d\n", fnum); | |
| return -EINVAL; | |
| } | |
| group = wmt_pctl_find_group_by_pin(data, pin); | |
| if (group < 0) { | |
| dev_err(data->dev, "unable to match pin %d to group\n", pin); | |
| return group; | |
| } | |
| map->type = PIN_MAP_TYPE_MUX_GROUP; | |
| map->data.mux.group = data->groups[group]; | |
| map->data.mux.function = wmt_functions[fnum]; | |
| (*maps)++; | |
| return 0; | |
| } | |
| static int wmt_pctl_dt_node_to_map_pull(struct wmt_pinctrl_data *data, | |
| struct device_node *np, | |
| u32 pin, u32 pull, | |
| struct pinctrl_map **maps) | |
| { | |
| int group; | |
| unsigned long *configs; | |
| struct pinctrl_map *map = *maps; | |
| if (pull > 2) { | |
| dev_err(data->dev, "invalid wm,pull %d\n", pull); | |
| return -EINVAL; | |
| } | |
| group = wmt_pctl_find_group_by_pin(data, pin); | |
| if (group < 0) { | |
| dev_err(data->dev, "unable to match pin %d to group\n", pin); | |
| return group; | |
| } | |
| configs = kzalloc(sizeof(*configs), GFP_KERNEL); | |
| if (!configs) | |
| return -ENOMEM; | |
| switch (pull) { | |
| case 0: | |
| configs[0] = PIN_CONFIG_BIAS_DISABLE; | |
| break; | |
| case 1: | |
| configs[0] = PIN_CONFIG_BIAS_PULL_DOWN; | |
| break; | |
| case 2: | |
| configs[0] = PIN_CONFIG_BIAS_PULL_UP; | |
| break; | |
| default: | |
| configs[0] = PIN_CONFIG_BIAS_DISABLE; | |
| dev_err(data->dev, "invalid pull state %d - disabling\n", pull); | |
| } | |
| map->type = PIN_MAP_TYPE_CONFIGS_PIN; | |
| map->data.configs.group_or_pin = data->groups[group]; | |
| map->data.configs.configs = configs; | |
| map->data.configs.num_configs = 1; | |
| (*maps)++; | |
| return 0; | |
| } | |
| static void wmt_pctl_dt_free_map(struct pinctrl_dev *pctldev, | |
| struct pinctrl_map *maps, | |
| unsigned num_maps) | |
| { | |
| int i; | |
| for (i = 0; i < num_maps; i++) | |
| if (maps[i].type == PIN_MAP_TYPE_CONFIGS_PIN) | |
| kfree(maps[i].data.configs.configs); | |
| kfree(maps); | |
| } | |
| static int wmt_pctl_dt_node_to_map(struct pinctrl_dev *pctldev, | |
| struct device_node *np, | |
| struct pinctrl_map **map, | |
| unsigned *num_maps) | |
| { | |
| struct pinctrl_map *maps, *cur_map; | |
| struct property *pins, *funcs, *pulls; | |
| u32 pin, func, pull; | |
| int num_pins, num_funcs, num_pulls, maps_per_pin; | |
| int i, err; | |
| struct wmt_pinctrl_data *data = pinctrl_dev_get_drvdata(pctldev); | |
| pins = of_find_property(np, "wm,pins", NULL); | |
| if (!pins) { | |
| dev_err(data->dev, "missing wmt,pins property\n"); | |
| return -EINVAL; | |
| } | |
| funcs = of_find_property(np, "wm,function", NULL); | |
| pulls = of_find_property(np, "wm,pull", NULL); | |
| if (!funcs && !pulls) { | |
| dev_err(data->dev, "neither wm,function nor wm,pull specified\n"); | |
| return -EINVAL; | |
| } | |
| /* | |
| * The following lines calculate how many values are defined for each | |
| * of the properties. | |
| */ | |
| num_pins = pins->length / sizeof(u32); | |
| num_funcs = funcs ? (funcs->length / sizeof(u32)) : 0; | |
| num_pulls = pulls ? (pulls->length / sizeof(u32)) : 0; | |
| if (num_funcs > 1 && num_funcs != num_pins) { | |
| dev_err(data->dev, "wm,function must have 1 or %d entries\n", | |
| num_pins); | |
| return -EINVAL; | |
| } | |
| if (num_pulls > 1 && num_pulls != num_pins) { | |
| dev_err(data->dev, "wm,pull must have 1 or %d entries\n", | |
| num_pins); | |
| return -EINVAL; | |
| } | |
| maps_per_pin = 0; | |
| if (num_funcs) | |
| maps_per_pin++; | |
| if (num_pulls) | |
| maps_per_pin++; | |
| cur_map = maps = kzalloc(num_pins * maps_per_pin * sizeof(*maps), | |
| GFP_KERNEL); | |
| if (!maps) | |
| return -ENOMEM; | |
| for (i = 0; i < num_pins; i++) { | |
| err = of_property_read_u32_index(np, "wm,pins", i, &pin); | |
| if (err) | |
| goto fail; | |
| if (pin >= (data->nbanks * 32)) { | |
| dev_err(data->dev, "invalid wm,pins value\n"); | |
| err = -EINVAL; | |
| goto fail; | |
| } | |
| if (num_funcs) { | |
| err = of_property_read_u32_index(np, "wm,function", | |
| (num_funcs > 1 ? i : 0), &func); | |
| if (err) | |
| goto fail; | |
| err = wmt_pctl_dt_node_to_map_func(data, np, pin, func, | |
| &cur_map); | |
| if (err) | |
| goto fail; | |
| } | |
| if (num_pulls) { | |
| err = of_property_read_u32_index(np, "wm,pull", | |
| (num_pulls > 1 ? i : 0), &pull); | |
| if (err) | |
| goto fail; | |
| err = wmt_pctl_dt_node_to_map_pull(data, np, pin, pull, | |
| &cur_map); | |
| if (err) | |
| goto fail; | |
| } | |
| } | |
| *map = maps; | |
| *num_maps = num_pins * maps_per_pin; | |
| return 0; | |
| /* | |
| * The fail path removes any maps that have been allocated. The fail path is | |
| * only called from code after maps has been kzalloc'd. It is also safe to | |
| * pass 'num_pins * maps_per_pin' as the map count even though we probably | |
| * failed before all the mappings were read as all maps are allocated at once, | |
| * and configs are only allocated for .type = PIN_MAP_TYPE_CONFIGS_PIN - there | |
| * is no failpath where a config can be allocated without .type being set. | |
| */ | |
| fail: | |
| wmt_pctl_dt_free_map(pctldev, maps, num_pins * maps_per_pin); | |
| return err; | |
| } | |
| static struct pinctrl_ops wmt_pctl_ops = { | |
| .get_groups_count = wmt_get_groups_count, | |
| .get_group_name = wmt_get_group_name, | |
| .get_group_pins = wmt_get_group_pins, | |
| .dt_node_to_map = wmt_pctl_dt_node_to_map, | |
| .dt_free_map = wmt_pctl_dt_free_map, | |
| }; | |
| static int wmt_pinconf_get(struct pinctrl_dev *pctldev, unsigned pin, | |
| unsigned long *config) | |
| { | |
| return -ENOTSUPP; | |
| } | |
| static int wmt_pinconf_set(struct pinctrl_dev *pctldev, unsigned pin, | |
| unsigned long *configs, unsigned num_configs) | |
| { | |
| struct wmt_pinctrl_data *data = pinctrl_dev_get_drvdata(pctldev); | |
| enum pin_config_param param; | |
| u16 arg; | |
| u32 bank = WMT_BANK_FROM_PIN(pin); | |
| u32 bit = WMT_BIT_FROM_PIN(pin); | |
| u32 reg_pull_en = data->banks[bank].reg_pull_en; | |
| u32 reg_pull_cfg = data->banks[bank].reg_pull_cfg; | |
| int i; | |
| if ((reg_pull_en == NO_REG) || (reg_pull_cfg == NO_REG)) { | |
| dev_err(data->dev, "bias functions not supported on pin %d\n", | |
| pin); | |
| return -EINVAL; | |
| } | |
| for (i = 0; i < num_configs; i++) { | |
| param = pinconf_to_config_param(configs[i]); | |
| arg = pinconf_to_config_argument(configs[i]); | |
| if ((param == PIN_CONFIG_BIAS_PULL_DOWN) || | |
| (param == PIN_CONFIG_BIAS_PULL_UP)) { | |
| if (arg == 0) | |
| param = PIN_CONFIG_BIAS_DISABLE; | |
| } | |
| switch (param) { | |
| case PIN_CONFIG_BIAS_DISABLE: | |
| wmt_clearbits(data, reg_pull_en, BIT(bit)); | |
| break; | |
| case PIN_CONFIG_BIAS_PULL_DOWN: | |
| wmt_clearbits(data, reg_pull_cfg, BIT(bit)); | |
| wmt_setbits(data, reg_pull_en, BIT(bit)); | |
| break; | |
| case PIN_CONFIG_BIAS_PULL_UP: | |
| wmt_setbits(data, reg_pull_cfg, BIT(bit)); | |
| wmt_setbits(data, reg_pull_en, BIT(bit)); | |
| break; | |
| default: | |
| dev_err(data->dev, "unknown pinconf param\n"); | |
| return -EINVAL; | |
| } | |
| } /* for each config */ | |
| return 0; | |
| } | |
| static struct pinconf_ops wmt_pinconf_ops = { | |
| .pin_config_get = wmt_pinconf_get, | |
| .pin_config_set = wmt_pinconf_set, | |
| }; | |
| static struct pinctrl_desc wmt_desc = { | |
| .owner = THIS_MODULE, | |
| .name = "pinctrl-wmt", | |
| .pctlops = &wmt_pctl_ops, | |
| .pmxops = &wmt_pinmux_ops, | |
| .confops = &wmt_pinconf_ops, | |
| }; | |
| static int wmt_gpio_get_direction(struct gpio_chip *chip, unsigned offset) | |
| { | |
| struct wmt_pinctrl_data *data = gpiochip_get_data(chip); | |
| u32 bank = WMT_BANK_FROM_PIN(offset); | |
| u32 bit = WMT_BIT_FROM_PIN(offset); | |
| u32 reg_dir = data->banks[bank].reg_dir; | |
| u32 val; | |
| val = readl_relaxed(data->base + reg_dir); | |
| if (val & BIT(bit)) | |
| return GPIOF_DIR_OUT; | |
| else | |
| return GPIOF_DIR_IN; | |
| } | |
| static int wmt_gpio_get_value(struct gpio_chip *chip, unsigned offset) | |
| { | |
| struct wmt_pinctrl_data *data = gpiochip_get_data(chip); | |
| u32 bank = WMT_BANK_FROM_PIN(offset); | |
| u32 bit = WMT_BIT_FROM_PIN(offset); | |
| u32 reg_data_in = data->banks[bank].reg_data_in; | |
| if (reg_data_in == NO_REG) { | |
| dev_err(data->dev, "no data in register defined\n"); | |
| return -EINVAL; | |
| } | |
| return !!(readl_relaxed(data->base + reg_data_in) & BIT(bit)); | |
| } | |
| static void wmt_gpio_set_value(struct gpio_chip *chip, unsigned offset, | |
| int val) | |
| { | |
| struct wmt_pinctrl_data *data = gpiochip_get_data(chip); | |
| u32 bank = WMT_BANK_FROM_PIN(offset); | |
| u32 bit = WMT_BIT_FROM_PIN(offset); | |
| u32 reg_data_out = data->banks[bank].reg_data_out; | |
| if (reg_data_out == NO_REG) { | |
| dev_err(data->dev, "no data out register defined\n"); | |
| return; | |
| } | |
| if (val) | |
| wmt_setbits(data, reg_data_out, BIT(bit)); | |
| else | |
| wmt_clearbits(data, reg_data_out, BIT(bit)); | |
| } | |
| static int wmt_gpio_direction_input(struct gpio_chip *chip, unsigned offset) | |
| { | |
| return pinctrl_gpio_direction_input(chip->base + offset); | |
| } | |
| static int wmt_gpio_direction_output(struct gpio_chip *chip, unsigned offset, | |
| int value) | |
| { | |
| wmt_gpio_set_value(chip, offset, value); | |
| return pinctrl_gpio_direction_output(chip->base + offset); | |
| } | |
| static struct gpio_chip wmt_gpio_chip = { | |
| .label = "gpio-wmt", | |
| .owner = THIS_MODULE, | |
| .request = gpiochip_generic_request, | |
| .free = gpiochip_generic_free, | |
| .get_direction = wmt_gpio_get_direction, | |
| .direction_input = wmt_gpio_direction_input, | |
| .direction_output = wmt_gpio_direction_output, | |
| .get = wmt_gpio_get_value, | |
| .set = wmt_gpio_set_value, | |
| .can_sleep = false, | |
| }; | |
| int wmt_pinctrl_probe(struct platform_device *pdev, | |
| struct wmt_pinctrl_data *data) | |
| { | |
| int err; | |
| struct resource *res; | |
| res = platform_get_resource(pdev, IORESOURCE_MEM, 0); | |
| data->base = devm_ioremap_resource(&pdev->dev, res); | |
| if (IS_ERR(data->base)) | |
| return PTR_ERR(data->base); | |
| wmt_desc.pins = data->pins; | |
| wmt_desc.npins = data->npins; | |
| data->gpio_chip = wmt_gpio_chip; | |
| data->gpio_chip.parent = &pdev->dev; | |
| data->gpio_chip.of_node = pdev->dev.of_node; | |
| data->gpio_chip.ngpio = data->nbanks * 32; | |
| platform_set_drvdata(pdev, data); | |
| data->dev = &pdev->dev; | |
| data->pctl_dev = pinctrl_register(&wmt_desc, &pdev->dev, data); | |
| if (IS_ERR(data->pctl_dev)) { | |
| dev_err(&pdev->dev, "Failed to register pinctrl\n"); | |
| return PTR_ERR(data->pctl_dev); | |
| } | |
| err = gpiochip_add_data(&data->gpio_chip, data); | |
| if (err) { | |
| dev_err(&pdev->dev, "could not add GPIO chip\n"); | |
| goto fail_gpio; | |
| } | |
| err = gpiochip_add_pin_range(&data->gpio_chip, dev_name(data->dev), | |
| 0, 0, data->nbanks * 32); | |
| if (err) | |
| goto fail_range; | |
| dev_info(&pdev->dev, "Pin controller initialized\n"); | |
| return 0; | |
| fail_range: | |
| gpiochip_remove(&data->gpio_chip); | |
| fail_gpio: | |
| pinctrl_unregister(data->pctl_dev); | |
| return err; | |
| } | |
| int wmt_pinctrl_remove(struct platform_device *pdev) | |
| { | |
| struct wmt_pinctrl_data *data = platform_get_drvdata(pdev); | |
| gpiochip_remove(&data->gpio_chip); | |
| pinctrl_unregister(data->pctl_dev); | |
| return 0; | |
| } |