| /* Copyright (c) 2013-2015, The Linux Foundation. All rights reserved. |
| * |
| * This program is free software; you can redistribute it and/or modify |
| * it under the terms of the GNU General Public License version 2 and |
| * only version 2 as published by the Free Software Foundation. |
| * |
| * This program is distributed in the hope that 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/irqdomain.h> |
| #include <linux/io.h> |
| #include <linux/kernel.h> |
| #include <linux/module.h> |
| #include <linux/of.h> |
| #include <linux/pinctrl/consumer.h> |
| #include <linux/pinctrl/pinctrl.h> |
| #include <linux/pinctrl/pinmux.h> |
| #include <linux/pinctrl/machine.h> |
| #include <linux/platform_device.h> |
| #include <linux/slab.h> |
| #include "core.h" |
| #include "pinconf.h" |
| #include "pinctrl-msm.h" |
| |
| /** |
| * struct msm_pinctrl_dd: represents the pinctrol driver data. |
| * @base: virtual base of TLMM. |
| * @irq: interrupt number for TLMM summary interrupt. |
| * @num_pins: Number of total pins present on TLMM. |
| * @msm_pindesc: list of descriptors for each pin. |
| * @num_pintypes: number of pintypes on TLMM. |
| * @msm_pintype: points to the representation of all pin types supported. |
| * @pctl: pin controller instance managed by the driver. |
| * @pctl_dev: pin controller descriptor registered with the pinctrl subsystem. |
| * @pin_grps: list of pin groups available to the driver. |
| * @num_grps: number of groups. |
| * @pmx_funcs:list of pin functions available to the driver |
| * @num_funcs: number of functions. |
| * @dev: pin contol device. |
| */ |
| struct msm_pinctrl_dd { |
| void __iomem *base; |
| int irq; |
| unsigned int num_pins; |
| struct msm_pindesc *msm_pindesc; |
| unsigned int num_pintypes; |
| struct msm_pintype_info *msm_pintype; |
| struct pinctrl_desc pctl; |
| struct pinctrl_dev *pctl_dev; |
| struct msm_pin_grps *pin_grps; |
| unsigned int num_grps; |
| struct msm_pmx_funcs *pmx_funcs; |
| unsigned int num_funcs; |
| struct device *dev; |
| }; |
| |
| /** |
| * struct msm_irq_of_info: represents of init data for tlmm interrupt |
| * controllers |
| * @compat: compat string for tlmm interrup controller instance. |
| * @irq_init: irq chip initialization callback. |
| */ |
| struct msm_irq_of_info { |
| const char *compat; |
| int (*irq_init)(struct device_node *np, struct irq_chip *ic); |
| }; |
| |
| static int msm_pmx_functions_count(struct pinctrl_dev *pctldev) |
| { |
| struct msm_pinctrl_dd *dd; |
| |
| dd = pinctrl_dev_get_drvdata(pctldev); |
| return dd->num_funcs; |
| } |
| |
| static const char *msm_pmx_get_fname(struct pinctrl_dev *pctldev, |
| unsigned selector) |
| { |
| struct msm_pinctrl_dd *dd; |
| |
| dd = pinctrl_dev_get_drvdata(pctldev); |
| return dd->pmx_funcs[selector].name; |
| } |
| |
| static int msm_pmx_get_groups(struct pinctrl_dev *pctldev, |
| unsigned selector, const char * const **groups, |
| unsigned * const num_groups) |
| { |
| struct msm_pinctrl_dd *dd; |
| |
| dd = pinctrl_dev_get_drvdata(pctldev); |
| *groups = dd->pmx_funcs[selector].gps; |
| *num_groups = dd->pmx_funcs[selector].num_grps; |
| return 0; |
| } |
| |
| static void msm_pmx_prg_fn(struct pinctrl_dev *pctldev, unsigned selector, |
| unsigned group, bool enable) |
| { |
| struct msm_pinctrl_dd *dd; |
| const unsigned int *pins; |
| struct msm_pindesc *pindesc; |
| struct msm_pintype_info *pinfo; |
| unsigned int pin, cnt, func; |
| |
| dd = pinctrl_dev_get_drvdata(pctldev); |
| pins = dd->pin_grps[group].pins; |
| pindesc = dd->msm_pindesc; |
| |
| /* |
| * for each pin in the pin group selected, program the correspoding |
| * pin function number in the config register. |
| */ |
| for (cnt = 0; cnt < dd->pin_grps[group].num_pins; cnt++) { |
| pin = pins[cnt]; |
| pinfo = pindesc[pin].pin_info; |
| pin = pin - pinfo->pin_start; |
| func = dd->pin_grps[group].func; |
| pinfo->prg_func(pin, func, enable, pinfo); |
| } |
| } |
| |
| static int msm_pmx_enable(struct pinctrl_dev *pctldev, unsigned selector, |
| unsigned group) |
| { |
| msm_pmx_prg_fn(pctldev, selector, group, true); |
| return 0; |
| } |
| |
| static void msm_pmx_disable(struct pinctrl_dev *pctldev, |
| unsigned selector, unsigned group) |
| { |
| msm_pmx_prg_fn(pctldev, selector, group, false); |
| } |
| |
| /* Enable gpio function for a pin */ |
| static int msm_pmx_gpio_request(struct pinctrl_dev *pctldev, |
| struct pinctrl_gpio_range *grange, |
| unsigned pin) |
| { |
| struct msm_pinctrl_dd *dd; |
| struct msm_pindesc *pindesc; |
| struct msm_pintype_info *pinfo; |
| |
| dd = pinctrl_dev_get_drvdata(pctldev); |
| pindesc = dd->msm_pindesc; |
| pinfo = pindesc[pin].pin_info; |
| /* All TLMM versions use function 0 for gpio function */ |
| pinfo->prg_func(pin, 0, true, pinfo); |
| return 0; |
| } |
| |
| static struct pinmux_ops msm_pmxops = { |
| .get_functions_count = msm_pmx_functions_count, |
| .get_function_name = msm_pmx_get_fname, |
| .get_function_groups = msm_pmx_get_groups, |
| .enable = msm_pmx_enable, |
| .disable = msm_pmx_disable, |
| .gpio_request_enable = msm_pmx_gpio_request, |
| }; |
| |
| static int msm_pconf_prg(struct pinctrl_dev *pctldev, unsigned int pin, |
| unsigned long *config, bool rw) |
| { |
| struct msm_pinctrl_dd *dd; |
| struct msm_pindesc *pindesc; |
| struct msm_pintype_info *pinfo; |
| |
| dd = pinctrl_dev_get_drvdata(pctldev); |
| pindesc = dd->msm_pindesc; |
| pinfo = pindesc[pin].pin_info; |
| pin = pin - pinfo->pin_start; |
| return pinfo->prg_cfg(pin, config, rw, pinfo); |
| } |
| |
| static int msm_pconf_set(struct pinctrl_dev *pctldev, unsigned int pin, |
| unsigned long config) |
| { |
| return msm_pconf_prg(pctldev, pin, &config, true); |
| } |
| |
| static int msm_pconf_get(struct pinctrl_dev *pctldev, unsigned int pin, |
| unsigned long *config) |
| { |
| return msm_pconf_prg(pctldev, pin, config, false); |
| } |
| |
| static int msm_pconf_group_set(struct pinctrl_dev *pctldev, |
| unsigned group, unsigned long config) |
| { |
| struct msm_pinctrl_dd *dd; |
| const unsigned int *pins; |
| unsigned int cnt; |
| |
| dd = pinctrl_dev_get_drvdata(pctldev); |
| pins = dd->pin_grps[group].pins; |
| |
| for (cnt = 0; cnt < dd->pin_grps[group].num_pins; cnt++) |
| msm_pconf_set(pctldev, pins[cnt], config); |
| |
| return 0; |
| } |
| |
| static int msm_pconf_group_get(struct pinctrl_dev *pctldev, |
| unsigned int group, unsigned long *config) |
| { |
| struct msm_pinctrl_dd *dd; |
| const unsigned int *pins; |
| |
| dd = pinctrl_dev_get_drvdata(pctldev); |
| pins = dd->pin_grps[group].pins; |
| msm_pconf_get(pctldev, pins[0], config); |
| return 0; |
| } |
| |
| static struct pinconf_ops msm_pconfops = { |
| .pin_config_get = msm_pconf_get, |
| .pin_config_set = msm_pconf_set, |
| .pin_config_group_get = msm_pconf_group_get, |
| .pin_config_group_set = msm_pconf_group_set, |
| }; |
| |
| static int msm_get_grps_count(struct pinctrl_dev *pctldev) |
| { |
| struct msm_pinctrl_dd *dd; |
| |
| dd = pinctrl_dev_get_drvdata(pctldev); |
| return dd->num_grps; |
| } |
| |
| static const char *msm_get_grps_name(struct pinctrl_dev *pctldev, |
| unsigned selector) |
| { |
| struct msm_pinctrl_dd *dd; |
| |
| dd = pinctrl_dev_get_drvdata(pctldev); |
| return dd->pin_grps[selector].name; |
| } |
| |
| static int msm_get_grps_pins(struct pinctrl_dev *pctldev, |
| unsigned selector, const unsigned **pins, unsigned *num_pins) |
| { |
| struct msm_pinctrl_dd *dd; |
| |
| dd = pinctrl_dev_get_drvdata(pctldev); |
| *pins = dd->pin_grps[selector].pins; |
| *num_pins = dd->pin_grps[selector].num_pins; |
| return 0; |
| } |
| |
| static struct msm_pintype_info *msm_pgrp_to_pintype(struct device_node *nd, |
| struct msm_pinctrl_dd *dd) |
| { |
| struct device_node *ptype_nd; |
| struct msm_pintype_info *pinfo = NULL; |
| int idx = 0; |
| |
| /*Extract pin type node from parent node */ |
| ptype_nd = of_parse_phandle(nd, "qcom,pins", 0); |
| /* find the pin type info for this pin type node */ |
| for (idx = 0; idx < dd->num_pintypes; idx++) { |
| pinfo = &dd->msm_pintype[idx]; |
| if (ptype_nd == pinfo->node) { |
| of_node_put(ptype_nd); |
| break; |
| } |
| } |
| return pinfo; |
| } |
| |
| /* create pinctrl_map entries by parsing device tree nodes */ |
| static int msm_dt_node_to_map(struct pinctrl_dev *pctldev, |
| struct device_node *cfg_np, struct pinctrl_map **maps, |
| unsigned *nmaps) |
| { |
| struct msm_pinctrl_dd *dd; |
| struct device_node *parent; |
| struct msm_pindesc *pindesc; |
| struct msm_pintype_info *pinfo; |
| struct pinctrl_map *map; |
| const char *grp_name; |
| char *fn_name; |
| u32 val; |
| unsigned long *cfg; |
| unsigned int fn_name_len = 0; |
| int cfg_cnt = 0, map_cnt = 0, func_cnt = 0, ret = 0; |
| |
| dd = pinctrl_dev_get_drvdata(pctldev); |
| pindesc = dd->msm_pindesc; |
| /* get parent node of config node */ |
| parent = of_get_parent(cfg_np); |
| /* |
| * parent node contains pin grouping |
| * get pin type from pin grouping |
| */ |
| pinfo = msm_pgrp_to_pintype(parent, dd); |
| /* check if there is a function associated with the parent pin group */ |
| if (of_find_property(parent, "qcom,pin-func", NULL)) |
| func_cnt++; |
| /* get pin configs */ |
| ret = pinconf_generic_parse_dt_config(cfg_np, &cfg, &cfg_cnt); |
| if (ret) { |
| dev_err(dd->dev, "properties incorrect\n"); |
| return ret; |
| } |
| |
| map_cnt = cfg_cnt + func_cnt; |
| |
| /* Allocate memory for pin-map entries */ |
| map = kzalloc(sizeof(*map) * map_cnt, GFP_KERNEL); |
| if (!map) |
| return -ENOMEM; |
| *nmaps = 0; |
| |
| /* Get group name from node */ |
| of_property_read_string(parent, "label", &grp_name); |
| /* create the config map entry */ |
| map[*nmaps].data.configs.group_or_pin = grp_name; |
| map[*nmaps].data.configs.configs = cfg; |
| map[*nmaps].data.configs.num_configs = cfg_cnt; |
| map[*nmaps].type = PIN_MAP_TYPE_CONFIGS_GROUP; |
| *nmaps += 1; |
| |
| /* If there is no function specified in device tree return */ |
| if (func_cnt == 0) { |
| *maps = map; |
| goto no_func; |
| } |
| /* Get function mapping */ |
| of_property_read_u32(parent, "qcom,pin-func", &val); |
| |
| fn_name_len = strlen(grp_name) + strlen("-func") + 1; |
| fn_name = kzalloc(fn_name_len, GFP_KERNEL); |
| if (!fn_name) { |
| ret = -ENOMEM; |
| goto func_err; |
| } |
| snprintf(fn_name, fn_name_len, "%s-func", grp_name); |
| map[*nmaps].data.mux.group = grp_name; |
| map[*nmaps].data.mux.function = fn_name; |
| map[*nmaps].type = PIN_MAP_TYPE_MUX_GROUP; |
| *nmaps += 1; |
| *maps = map; |
| of_node_put(parent); |
| return 0; |
| |
| func_err: |
| kfree(cfg); |
| kfree(map); |
| no_func: |
| of_node_put(parent); |
| return ret; |
| } |
| |
| /* free the memory allocated to hold the pin-map table */ |
| static void msm_dt_free_map(struct pinctrl_dev *pctldev, |
| struct pinctrl_map *map, unsigned num_maps) |
| { |
| int idx; |
| |
| for (idx = 0; idx < num_maps; idx++) { |
| if (map[idx].type == PIN_MAP_TYPE_CONFIGS_GROUP) |
| kfree(map[idx].data.configs.configs); |
| else if (map[idx].type == PIN_MAP_TYPE_MUX_GROUP) |
| kfree(map[idx].data.mux.function); |
| }; |
| |
| kfree(map); |
| } |
| |
| static struct pinctrl_ops msm_pctrlops = { |
| .get_groups_count = msm_get_grps_count, |
| .get_group_name = msm_get_grps_name, |
| .get_group_pins = msm_get_grps_pins, |
| .dt_node_to_map = msm_dt_node_to_map, |
| .dt_free_map = msm_dt_free_map, |
| }; |
| |
| static int msm_pinctrl_request_gpio(struct gpio_chip *gc, unsigned offset) |
| { |
| return pinctrl_request_gpio(gc->base + offset); |
| } |
| |
| static void msm_pinctrl_free_gpio(struct gpio_chip *gc, unsigned offset) |
| { |
| pinctrl_free_gpio(gc->base + offset); |
| } |
| |
| static int msm_of_get_pin(struct device_node *np, int index, |
| struct msm_pinctrl_dd *dd, uint *pin) |
| { |
| struct of_phandle_args pargs; |
| struct msm_pintype_info *pinfo, *pintype; |
| int num_pintypes; |
| int ret, i; |
| |
| ret = of_parse_phandle_with_args(np, "qcom,pins", "#qcom,pin-cells", |
| index, &pargs); |
| if (ret) |
| return ret; |
| pintype = dd->msm_pintype; |
| num_pintypes = dd->num_pintypes; |
| for (i = 0; i < num_pintypes; i++) { |
| pinfo = &pintype[i]; |
| /* Find the matching pin type node */ |
| if (pargs.np != pinfo->node) |
| continue; |
| /* Check if arg specified is in valid range for pin type */ |
| if (pargs.args[0] > pinfo->num_pins) { |
| ret = -EINVAL; |
| dev_err(dd->dev, "Invalid pin number for type %s\n", |
| pinfo->name); |
| goto out; |
| } |
| /* |
| * Pin number = index within pin type + start of pin numbers |
| * for this pin type |
| */ |
| *pin = pargs.args[0] + pinfo->pin_start; |
| } |
| out: |
| of_node_put(pargs.np); |
| return ret; |
| } |
| |
| static int msm_pinctrl_dt_parse_pins(struct device_node *dev_node, |
| struct msm_pinctrl_dd *dd) |
| { |
| struct device *dev; |
| struct device_node *pgrp_np; |
| struct msm_pin_grps *pin_grps, *curr_grp; |
| struct msm_pmx_funcs *pmx_funcs, *curr_func; |
| char *func_name; |
| const char *grp_name; |
| int ret, i, grp_index = 0, func_index = 0; |
| uint pin = 0, *pins, num_grps = 0, num_pins = 0, len = 0; |
| uint num_funcs = 0; |
| u32 func = 0; |
| |
| dev = dd->dev; |
| for_each_child_of_node(dev_node, pgrp_np) { |
| if (!of_find_property(pgrp_np, "qcom,pins", NULL)) |
| continue; |
| if (of_find_property(pgrp_np, "qcom,pin-func", NULL)) |
| num_funcs++; |
| num_grps++; |
| } |
| |
| pin_grps = (struct msm_pin_grps *)devm_kzalloc(dd->dev, |
| sizeof(*pin_grps) * num_grps, |
| GFP_KERNEL); |
| if (!pin_grps) { |
| dev_err(dev, "Failed to allocate grp desc\n"); |
| return -ENOMEM; |
| } |
| pmx_funcs = (struct msm_pmx_funcs *)devm_kzalloc(dd->dev, |
| sizeof(*pmx_funcs) * num_funcs, |
| GFP_KERNEL); |
| if (!pmx_funcs) { |
| dev_err(dev, "Failed to allocate grp desc\n"); |
| return -ENOMEM; |
| } |
| /* |
| * Iterate over all child nodes, and for nodes containing pin lists |
| * populate corresponding pin group, and if provided, corresponding |
| * function |
| */ |
| for_each_child_of_node(dev_node, pgrp_np) { |
| if (!of_find_property(pgrp_np, "qcom,pins", NULL)) |
| continue; |
| curr_grp = pin_grps + grp_index; |
| /* Get group name from label*/ |
| ret = of_property_read_string(pgrp_np, "label", &grp_name); |
| if (ret) { |
| dev_err(dev, "Unable to allocate group name\n"); |
| return ret; |
| } |
| ret = of_property_read_u32(pgrp_np, "qcom,num-grp-pins", |
| &num_pins); |
| if (ret) { |
| dev_err(dev, "pin count not specified for groups %s\n", |
| grp_name); |
| return ret; |
| } |
| pins = devm_kzalloc(dd->dev, sizeof(unsigned int) * num_pins, |
| GFP_KERNEL); |
| if (!pins) { |
| dev_err(dev, "Unable to allocte pins for %s\n", |
| grp_name); |
| return -ENOMEM; |
| } |
| for (i = 0; i < num_pins; i++) { |
| ret = msm_of_get_pin(pgrp_np, i, dd, &pin); |
| if (ret) { |
| dev_err(dev, "Pin grp %s does not have pins\n", |
| grp_name); |
| return ret; |
| } |
| pins[i] = pin; |
| } |
| curr_grp->pins = pins; |
| curr_grp->num_pins = num_pins; |
| curr_grp->name = grp_name; |
| grp_index++; |
| /* Check if func specified */ |
| if (!of_find_property(pgrp_np, "qcom,pin-func", NULL)) |
| continue; |
| curr_func = pmx_funcs + func_index; |
| len = strlen(grp_name) + strlen("-func") + 1; |
| func_name = devm_kzalloc(dev, len, GFP_KERNEL); |
| if (!func_name) { |
| dev_err(dev, "Cannot allocate func name for grp %s", |
| grp_name); |
| return -ENOMEM; |
| } |
| snprintf(func_name, len, "%s%s", grp_name, "-func"); |
| curr_func->name = func_name; |
| curr_func->gps = devm_kzalloc(dev, sizeof(char *), GFP_KERNEL); |
| if (!curr_func->gps) { |
| dev_err(dev, "failed to alloc memory for group list "); |
| return -ENOMEM; |
| } |
| of_property_read_u32(pgrp_np, "qcom,pin-func", &func); |
| curr_grp->func = func; |
| curr_func->gps[0] = grp_name; |
| curr_func->num_grps = 1; |
| func_index++; |
| } |
| dd->pin_grps = pin_grps; |
| dd->num_grps = num_grps; |
| dd->pmx_funcs = pmx_funcs; |
| dd->num_funcs = num_funcs; |
| return 0; |
| } |
| |
| static void msm_populate_pindesc(struct msm_pintype_info *pinfo, |
| struct msm_pindesc *msm_pindesc) |
| { |
| int i; |
| struct msm_pindesc *pindesc; |
| |
| for (i = 0; i < pinfo->num_pins; i++) { |
| pindesc = &msm_pindesc[i + pinfo->pin_start]; |
| pindesc->pin_info = pinfo; |
| snprintf(pindesc->name, sizeof(pindesc->name), |
| "%s-%d", pinfo->name, i); |
| } |
| } |
| |
| static bool msm_pintype_supports_gpio(struct msm_pintype_info *pinfo) |
| { |
| struct device_node *pt_node; |
| |
| if (!pinfo->node) |
| return false; |
| |
| for_each_child_of_node(pinfo->node, pt_node) { |
| if (of_find_property(pt_node, "gpio-controller", NULL)) { |
| pinfo->gc.of_node = pt_node; |
| pinfo->supports_gpio = true; |
| return true; |
| } |
| } |
| return false; |
| } |
| |
| static bool msm_pintype_supports_irq(struct msm_pintype_info *pinfo) |
| { |
| struct device_node *pt_node; |
| |
| if (!pinfo->node) |
| return false; |
| |
| for_each_child_of_node(pinfo->node, pt_node) { |
| if (of_find_property(pt_node, "interrupt-controller", NULL)) { |
| pinfo->irq_chip->node = pt_node; |
| return true; |
| } |
| } |
| return false; |
| } |
| |
| static int msm_pinctrl_dt_parse_pintype(struct device_node *dev_node, |
| struct msm_pinctrl_dd *dd) |
| { |
| struct device_node *pt_node; |
| struct msm_pindesc *msm_pindesc; |
| struct msm_pintype_info *pintype, *pinfo; |
| u32 num_pins, pinfo_entries, curr_pins; |
| int i, ret; |
| uint total_pins = 0; |
| |
| pinfo = dd->msm_pintype; |
| pinfo_entries = dd->num_pintypes; |
| curr_pins = 0; |
| |
| for_each_child_of_node(dev_node, pt_node) { |
| for (i = 0; i < pinfo_entries; i++) { |
| pintype = &pinfo[i]; |
| if (strcmp(pt_node->name, pintype->name)) |
| continue; |
| of_node_get(pt_node); |
| pintype->node = pt_node; |
| /* determine number of pins of given pin type */ |
| ret = of_property_read_u32(pt_node, "qcom,num-pins", |
| &num_pins); |
| if (ret) { |
| dev_err(dd->dev, "num pins not specified\n"); |
| goto fail; |
| } |
| /* determine pin number range for given pin type */ |
| pintype->num_pins = num_pins; |
| pintype->pin_start = curr_pins; |
| pintype->pin_end = curr_pins + num_pins; |
| pintype->set_reg_base(dd->base, pintype); |
| total_pins += num_pins; |
| curr_pins += num_pins; |
| } |
| } |
| dd->msm_pindesc = devm_kzalloc(dd->dev, |
| sizeof(struct msm_pindesc) * |
| total_pins, GFP_KERNEL); |
| if (!dd->msm_pindesc) { |
| dev_err(dd->dev, "Unable to allocate msm pindesc"); |
| goto fail; |
| } |
| |
| dd->num_pins = total_pins; |
| msm_pindesc = dd->msm_pindesc; |
| /* |
| * Populate pin descriptor based on each pin type present in Device |
| * tree and supported by the driver |
| */ |
| for (i = 0; i < pinfo_entries; i++) { |
| pintype = &pinfo[i]; |
| /* If entry not in device tree, skip */ |
| if (!pintype->node) |
| continue; |
| msm_populate_pindesc(pintype, msm_pindesc); |
| } |
| return 0; |
| fail: |
| for (i = 0; i < pinfo_entries; i++) { |
| pintype = &pinfo[i]; |
| if (pintype->node) |
| of_node_put(pintype->node); |
| } |
| return -ENOMEM; |
| } |
| |
| |
| static void msm_pinctrl_cleanup_dd(struct msm_pinctrl_dd *dd) |
| { |
| int i; |
| struct msm_pintype_info *pintype; |
| |
| pintype = dd->msm_pintype; |
| for (i = 0; i < dd->num_pintypes; i++) { |
| if (pintype->node) |
| of_node_put(dd->msm_pintype[i].node); |
| } |
| } |
| |
| static int msm_pinctrl_get_drvdata(struct msm_pinctrl_dd *dd, |
| struct platform_device *pdev) |
| { |
| int ret; |
| struct device_node *node = pdev->dev.of_node; |
| |
| ret = msm_pinctrl_dt_parse_pintype(node, dd); |
| if (ret) |
| goto out; |
| |
| ret = msm_pinctrl_dt_parse_pins(node, dd); |
| if (ret) |
| msm_pinctrl_cleanup_dd(dd); |
| out: |
| return ret; |
| } |
| |
| static int msm_register_pinctrl(struct msm_pinctrl_dd *dd) |
| { |
| int i; |
| struct pinctrl_pin_desc *pindesc; |
| struct msm_pintype_info *pinfo, *pintype; |
| struct pinctrl_desc *ctrl_desc = &dd->pctl; |
| |
| ctrl_desc->name = "msm-pinctrl"; |
| ctrl_desc->owner = THIS_MODULE; |
| ctrl_desc->pmxops = &msm_pmxops; |
| ctrl_desc->confops = &msm_pconfops; |
| ctrl_desc->pctlops = &msm_pctrlops; |
| |
| pindesc = devm_kzalloc(dd->dev, sizeof(*pindesc) * dd->num_pins, |
| GFP_KERNEL); |
| if (!pindesc) { |
| dev_err(dd->dev, "Failed to allocate pinctrl pin desc\n"); |
| return -ENOMEM; |
| } |
| |
| for (i = 0; i < dd->num_pins; i++) { |
| pindesc[i].number = i; |
| pindesc[i].name = dd->msm_pindesc[i].name; |
| } |
| ctrl_desc->pins = pindesc; |
| ctrl_desc->npins = dd->num_pins; |
| dd->pctl_dev = pinctrl_register(ctrl_desc, dd->dev, dd); |
| if (!dd->pctl_dev) { |
| dev_err(dd->dev, "could not register pinctrl driver\n"); |
| return -EINVAL; |
| } |
| |
| pinfo = dd->msm_pintype; |
| for (i = 0; i < dd->num_pintypes; i++) { |
| pintype = &pinfo[i]; |
| if (!pintype->supports_gpio) |
| continue; |
| pintype->grange.name = pintype->name; |
| pintype->grange.id = i; |
| pintype->grange.pin_base = pintype->pin_start; |
| pintype->grange.base = pintype->gc.base; |
| pintype->grange.npins = pintype->gc.ngpio; |
| pintype->grange.gc = &pintype->gc; |
| pinctrl_add_gpio_range(dd->pctl_dev, &pintype->grange); |
| } |
| return 0; |
| } |
| |
| static void msm_register_gpiochip(struct msm_pinctrl_dd *dd) |
| { |
| |
| struct gpio_chip *gc; |
| struct msm_pintype_info *pintype, *pinfo; |
| int i, ret = 0; |
| |
| pinfo = dd->msm_pintype; |
| for (i = 0; i < dd->num_pintypes; i++) { |
| pintype = &pinfo[i]; |
| if (!msm_pintype_supports_gpio(pintype)) |
| continue; |
| gc = &pintype->gc; |
| gc->request = msm_pinctrl_request_gpio; |
| gc->free = msm_pinctrl_free_gpio; |
| gc->dev = dd->dev; |
| gc->ngpio = pintype->num_pins; |
| gc->base = 0; |
| ret = gpiochip_add(gc); |
| if (ret) { |
| dev_err(dd->dev, "failed to register gpio chip\n"); |
| pinfo->supports_gpio = false; |
| } |
| } |
| } |
| |
| static int msm_register_irqchip(struct msm_pinctrl_dd *dd) |
| { |
| struct msm_pintype_info *pintype, *pinfo; |
| int i, ret = 0; |
| |
| pinfo = dd->msm_pintype; |
| for (i = 0; i < dd->num_pintypes; i++) { |
| pintype = &pinfo[i]; |
| if (!msm_pintype_supports_irq(pintype)) |
| continue; |
| ret = pintype->init_irq(dd->irq, pintype, dd->dev); |
| return ret; |
| } |
| return 0; |
| } |
| |
| int msm_pinctrl_probe(struct platform_device *pdev, |
| struct msm_tlmm_desc *tlmm_info) |
| { |
| struct msm_pinctrl_dd *dd; |
| struct device *dev = &pdev->dev; |
| int ret; |
| |
| dd = devm_kzalloc(dev, sizeof(*dd), GFP_KERNEL); |
| if (!dd) { |
| dev_err(dev, "Alloction failed for driver data\n"); |
| return -ENOMEM; |
| } |
| dd->dev = dev; |
| dd->msm_pintype = tlmm_info->pintypes; |
| dd->base = tlmm_info->base; |
| dd->irq = tlmm_info->irq; |
| dd->num_pintypes = tlmm_info->num_pintypes; |
| ret = msm_pinctrl_get_drvdata(dd, pdev); |
| if (ret) { |
| dev_err(&pdev->dev, "driver data not available\n"); |
| return ret; |
| } |
| msm_register_gpiochip(dd); |
| ret = msm_register_pinctrl(dd); |
| if (ret) { |
| msm_pinctrl_cleanup_dd(dd); |
| return ret; |
| } |
| msm_register_irqchip(dd); |
| platform_set_drvdata(pdev, dd); |
| return 0; |
| } |
| EXPORT_SYMBOL(msm_pinctrl_probe); |
| |
| #ifdef CONFIG_USE_PINCTRL_IRQ |
| struct irq_chip mpm_tlmm_irq_extn = { |
| .irq_eoi = NULL, |
| .irq_mask = NULL, |
| .irq_unmask = NULL, |
| .irq_retrigger = NULL, |
| .irq_set_type = NULL, |
| .irq_set_wake = NULL, |
| .irq_disable = NULL, |
| }; |
| |
| struct msm_irq_of_info msm_tlmm_irq[] = { |
| #ifdef CONFIG_PINCTRL_MSM_TLMM |
| { |
| .compat = "qcom,msm-tlmm-gp", |
| .irq_init = msm_tlmm_of_gp_irq_init, |
| }, |
| #endif |
| }; |
| |
| int __init msm_tlmm_of_irq_init(struct device_node *controller, |
| struct device_node *parent) |
| { |
| int rc, i; |
| const char *compat; |
| |
| rc = of_property_read_string(controller, "compatible", &compat); |
| if (rc) |
| return rc; |
| |
| for (i = 0; i < ARRAY_SIZE(msm_tlmm_irq); i++) { |
| struct msm_irq_of_info *tlmm_info = &msm_tlmm_irq[i]; |
| |
| if (!of_compat_cmp(tlmm_info->compat, compat, strlen(compat))) |
| return tlmm_info->irq_init(controller, |
| &mpm_tlmm_irq_extn); |
| |
| } |
| return -EIO; |
| } |
| #endif |