1 /*
2  * Copyright (C) 2015 Masahiro Yamada <yamada.masahiro@socionext.com>
3  *
4  * SPDX-License-Identifier:	GPL-2.0+
5  */
6 
7 #include <common.h>
8 #include <mapmem.h>
9 #include <linux/io.h>
10 #include <linux/err.h>
11 #include <dm/device.h>
12 #include <dm/pinctrl.h>
13 
14 #include "pinctrl-uniphier.h"
15 
16 DECLARE_GLOBAL_DATA_PTR;
17 
18 static int uniphier_pinctrl_get_groups_count(struct udevice *dev)
19 {
20 	struct uniphier_pinctrl_priv *priv = dev_get_priv(dev);
21 
22 	return priv->socdata->groups_count;
23 }
24 
25 static const char *uniphier_pinctrl_get_group_name(struct udevice *dev,
26 						   unsigned selector)
27 {
28 	struct uniphier_pinctrl_priv *priv = dev_get_priv(dev);
29 
30 	return priv->socdata->groups[selector].name;
31 }
32 
33 static int uniphier_pinmux_get_functions_count(struct udevice *dev)
34 {
35 	struct uniphier_pinctrl_priv *priv = dev_get_priv(dev);
36 
37 	return priv->socdata->functions_count;
38 }
39 
40 static const char *uniphier_pinmux_get_function_name(struct udevice *dev,
41 						     unsigned selector)
42 {
43 	struct uniphier_pinctrl_priv *priv = dev_get_priv(dev);
44 
45 	return priv->socdata->functions[selector];
46 }
47 
48 static void uniphier_pinconf_input_enable(struct udevice *dev, unsigned pin)
49 {
50 	struct uniphier_pinctrl_priv *priv = dev_get_priv(dev);
51 	int pins_count = priv->socdata->pins_count;
52 	const struct uniphier_pinctrl_pin *pins = priv->socdata->pins;
53 	int i;
54 
55 	for (i = 0; i < pins_count; i++) {
56 		if (pins[i].number == pin) {
57 			unsigned int iectrl;
58 			u32 tmp;
59 
60 			iectrl = uniphier_pin_get_iectrl(pins[i].data);
61 			tmp = readl(priv->base + UNIPHIER_PINCTRL_IECTRL);
62 			tmp |= 1 << iectrl;
63 			writel(tmp, priv->base + UNIPHIER_PINCTRL_IECTRL);
64 		}
65 	}
66 }
67 
68 static void uniphier_pinmux_set_one(struct udevice *dev, unsigned pin,
69 				    unsigned muxval)
70 {
71 	struct uniphier_pinctrl_priv *priv = dev_get_priv(dev);
72 	unsigned mux_bits = priv->socdata->mux_bits;
73 	unsigned reg_stride = priv->socdata->reg_stride;
74 	unsigned reg, reg_end, shift, mask;
75 	u32 tmp;
76 
77 	reg = UNIPHIER_PINCTRL_PINMUX_BASE + pin * mux_bits / 32 * reg_stride;
78 	reg_end = reg + reg_stride;
79 	shift = pin * mux_bits % 32;
80 	mask = (1U << mux_bits) - 1;
81 
82 	/*
83 	 * If reg_stride is greater than 4, the MSB of each pinsel shall be
84 	 * stored in the offset+4.
85 	 */
86 	for (; reg < reg_end; reg += 4) {
87 		tmp = readl(priv->base + reg);
88 		tmp &= ~(mask << shift);
89 		tmp |= (mask & muxval) << shift;
90 		writel(tmp, priv->base + reg);
91 
92 		muxval >>= mux_bits;
93 	}
94 
95 	if (priv->socdata->load_pinctrl)
96 		writel(1, priv->base + UNIPHIER_PINCTRL_LOAD_PINMUX);
97 
98 	/* some pins need input-enabling */
99 	uniphier_pinconf_input_enable(dev, pin);
100 }
101 
102 static int uniphier_pinmux_group_set(struct udevice *dev,
103 				     unsigned group_selector,
104 				     unsigned func_selector)
105 {
106 	struct uniphier_pinctrl_priv *priv = dev_get_priv(dev);
107 	const struct uniphier_pinctrl_group *grp =
108 					&priv->socdata->groups[group_selector];
109 	int i;
110 
111 	for (i = 0; i < grp->num_pins; i++)
112 		uniphier_pinmux_set_one(dev, grp->pins[i], grp->muxvals[i]);
113 
114 	return 0;
115 }
116 
117 const struct pinctrl_ops uniphier_pinctrl_ops = {
118 	.get_groups_count = uniphier_pinctrl_get_groups_count,
119 	.get_group_name = uniphier_pinctrl_get_group_name,
120 	.get_functions_count = uniphier_pinmux_get_functions_count,
121 	.get_function_name = uniphier_pinmux_get_function_name,
122 	.pinmux_group_set = uniphier_pinmux_group_set,
123 	.set_state = pinctrl_generic_set_state,
124 };
125 
126 int uniphier_pinctrl_probe(struct udevice *dev,
127 			   struct uniphier_pinctrl_socdata *socdata)
128 {
129 	struct uniphier_pinctrl_priv *priv = dev_get_priv(dev);
130 	fdt_addr_t addr;
131 	fdt_size_t size;
132 
133 	addr = fdtdec_get_addr_size(gd->fdt_blob, dev->of_offset, "reg",
134 				    &size);
135 	if (addr == FDT_ADDR_T_NONE)
136 		return -EINVAL;
137 
138 	priv->base = map_sysmem(addr, size);
139 	if (!priv->base)
140 		return -ENOMEM;
141 
142 	priv->socdata = socdata;
143 
144 	return 0;
145 }
146 
147 int uniphier_pinctrl_remove(struct udevice *dev)
148 {
149 	struct uniphier_pinctrl_priv *priv = dev_get_priv(dev);
150 
151 	unmap_sysmem(priv->base);
152 
153 	return 0;
154 }
155