1 /* 2 * (C) Copyright 2016 - Beniamino Galvani <b.galvani@gmail.com> 3 * 4 * SPDX-License-Identifier: GPL-2.0+ 5 */ 6 7 #include <common.h> 8 #include <dm/device.h> 9 #include <dm/pinctrl.h> 10 #include <fdt_support.h> 11 #include <linux/err.h> 12 #include <linux/io.h> 13 #include <linux/sizes.h> 14 15 #include "pinctrl-meson.h" 16 17 DECLARE_GLOBAL_DATA_PTR; 18 19 static const char *meson_pinctrl_dummy_name = "_dummy"; 20 21 static int meson_pinctrl_get_groups_count(struct udevice *dev) 22 { 23 struct meson_pinctrl *priv = dev_get_priv(dev); 24 25 return priv->data->num_groups; 26 } 27 28 static const char *meson_pinctrl_get_group_name(struct udevice *dev, 29 unsigned selector) 30 { 31 struct meson_pinctrl *priv = dev_get_priv(dev); 32 33 if (!priv->data->groups[selector].name) 34 return meson_pinctrl_dummy_name; 35 36 return priv->data->groups[selector].name; 37 } 38 39 static int meson_pinmux_get_functions_count(struct udevice *dev) 40 { 41 struct meson_pinctrl *priv = dev_get_priv(dev); 42 43 return priv->data->num_funcs; 44 } 45 46 static const char *meson_pinmux_get_function_name(struct udevice *dev, 47 unsigned selector) 48 { 49 struct meson_pinctrl *priv = dev_get_priv(dev); 50 51 return priv->data->funcs[selector].name; 52 } 53 54 static void meson_pinmux_disable_other_groups(struct meson_pinctrl *priv, 55 unsigned int pin, int sel_group) 56 { 57 struct meson_pmx_group *group; 58 void __iomem *addr; 59 int i, j; 60 61 for (i = 0; i < priv->data->num_groups; i++) { 62 group = &priv->data->groups[i]; 63 if (group->is_gpio || i == sel_group) 64 continue; 65 66 for (j = 0; j < group->num_pins; j++) { 67 if (group->pins[j] == pin) { 68 /* We have found a group using the pin */ 69 debug("pinmux: disabling %s\n", group->name); 70 addr = priv->reg_mux + group->reg * 4; 71 writel(readl(addr) & ~BIT(group->bit), addr); 72 } 73 } 74 } 75 } 76 77 static int meson_pinmux_group_set(struct udevice *dev, 78 unsigned group_selector, 79 unsigned func_selector) 80 { 81 struct meson_pinctrl *priv = dev_get_priv(dev); 82 const struct meson_pmx_group *group; 83 const struct meson_pmx_func *func; 84 void __iomem *addr; 85 int i; 86 87 group = &priv->data->groups[group_selector]; 88 func = &priv->data->funcs[func_selector]; 89 90 debug("pinmux: set group %s func %s\n", group->name, func->name); 91 92 /* 93 * Disable groups using the same pins. 94 * The selected group is not disabled to avoid glitches. 95 */ 96 for (i = 0; i < group->num_pins; i++) { 97 meson_pinmux_disable_other_groups(priv, 98 group->pins[i], 99 group_selector); 100 } 101 102 /* Function 0 (GPIO) doesn't need any additional setting */ 103 if (func_selector) { 104 addr = priv->reg_mux + group->reg * 4; 105 writel(readl(addr) | BIT(group->bit), addr); 106 } 107 108 return 0; 109 } 110 111 const struct pinctrl_ops meson_pinctrl_ops = { 112 .get_groups_count = meson_pinctrl_get_groups_count, 113 .get_group_name = meson_pinctrl_get_group_name, 114 .get_functions_count = meson_pinmux_get_functions_count, 115 .get_function_name = meson_pinmux_get_function_name, 116 .pinmux_group_set = meson_pinmux_group_set, 117 .set_state = pinctrl_generic_set_state, 118 }; 119 120 static fdt_addr_t parse_address(int offset, const char *name, int na, int ns) 121 { 122 int index, len = 0; 123 const fdt32_t *reg; 124 125 index = fdt_stringlist_search(gd->fdt_blob, offset, "reg-names", name); 126 if (index < 0) 127 return FDT_ADDR_T_NONE; 128 129 reg = fdt_getprop(gd->fdt_blob, offset, "reg", &len); 130 if (!reg || (len <= (index * sizeof(fdt32_t) * (na + ns)))) 131 return FDT_ADDR_T_NONE; 132 133 reg += index * (na + ns); 134 135 return fdt_translate_address((void *)gd->fdt_blob, offset, reg); 136 } 137 138 int meson_pinctrl_probe(struct udevice *dev) 139 { 140 struct meson_pinctrl *priv = dev_get_priv(dev); 141 fdt_addr_t addr; 142 int node, gpio = -1, len; 143 int na, ns; 144 145 na = fdt_address_cells(gd->fdt_blob, dev->parent->of_offset); 146 if (na < 1) { 147 debug("bad #address-cells\n"); 148 return -EINVAL; 149 } 150 151 ns = fdt_size_cells(gd->fdt_blob, dev->parent->of_offset); 152 if (ns < 1) { 153 debug("bad #size-cells\n"); 154 return -EINVAL; 155 } 156 157 fdt_for_each_subnode(node, gd->fdt_blob, dev->of_offset) { 158 if (fdt_getprop(gd->fdt_blob, node, "gpio-controller", &len)) { 159 gpio = node; 160 break; 161 } 162 } 163 164 if (!gpio) { 165 debug("gpio node not found\n"); 166 return -EINVAL; 167 } 168 169 addr = parse_address(gpio, "mux", na, ns); 170 if (addr == FDT_ADDR_T_NONE) { 171 debug("mux not found\n"); 172 return -EINVAL; 173 } 174 175 priv->reg_mux = (void __iomem *)addr; 176 priv->data = (struct meson_pinctrl_data *)dev_get_driver_data(dev); 177 178 return 0; 179 } 180