xref: /openbmc/u-boot/arch/x86/lib/pinctrl_ich6.c (revision c0e032e0)
1 /*
2  * Copyright (C) 2016 Google, Inc
3  *
4  * SPDX-License-Identifier:	GPL-2.0+
5  */
6 
7 #include <common.h>
8 #include <dm.h>
9 #include <errno.h>
10 #include <fdtdec.h>
11 #include <pch.h>
12 #include <pci.h>
13 #include <asm/cpu.h>
14 #include <asm/gpio.h>
15 #include <asm/io.h>
16 #include <asm/pci.h>
17 #include <dm/pinctrl.h>
18 
19 DECLARE_GLOBAL_DATA_PTR;
20 
21 #define GPIO_USESEL_OFFSET(x)	(x)
22 #define GPIO_IOSEL_OFFSET(x)	(x + 4)
23 #define GPIO_LVL_OFFSET(x)	((x) ? (x) + 8 : 0xc)
24 #define GPI_INV			0x2c
25 
26 #define IOPAD_MODE_MASK			0x7
27 #define IOPAD_PULL_ASSIGN_SHIFT		7
28 #define IOPAD_PULL_ASSIGN_MASK		(0x3 << IOPAD_PULL_ASSIGN_SHIFT)
29 #define IOPAD_PULL_STRENGTH_SHIFT	9
30 #define IOPAD_PULL_STRENGTH_MASK	(0x3 << IOPAD_PULL_STRENGTH_SHIFT)
31 
32 static int ich6_pinctrl_set_value(uint16_t base, unsigned offset, int value)
33 {
34 	if (value)
35 		setio_32(base, 1UL << offset);
36 	else
37 		clrio_32(base, 1UL << offset);
38 
39 	return 0;
40 }
41 
42 static int ich6_pinctrl_set_function(uint16_t base, unsigned offset, int func)
43 {
44 	if (func)
45 		setio_32(base, 1UL << offset);
46 	else
47 		clrio_32(base, 1UL << offset);
48 
49 	return 0;
50 }
51 
52 static int ich6_pinctrl_set_direction(uint16_t base, unsigned offset, int dir)
53 {
54 	if (!dir)
55 		setio_32(base, 1UL << offset);
56 	else
57 		clrio_32(base, 1UL << offset);
58 
59 	return 0;
60 }
61 
62 static int ich6_pinctrl_cfg_pin(s32 gpiobase, s32 iobase, int pin_node)
63 {
64 	bool is_gpio, invert;
65 	u32 gpio_offset[2];
66 	int pad_offset;
67 	int dir, val;
68 	int ret;
69 
70 	/*
71 	 * GPIO node is not mandatory, so we only do the pinmuxing if the
72 	 * node exists.
73 	 */
74 	ret = fdtdec_get_int_array(gd->fdt_blob, pin_node, "gpio-offset",
75 				   gpio_offset, 2);
76 	if (!ret) {
77 		/* Do we want to force the GPIO mode? */
78 		is_gpio = fdtdec_get_bool(gd->fdt_blob, pin_node, "mode-gpio");
79 		if (is_gpio)
80 			ich6_pinctrl_set_function(GPIO_USESEL_OFFSET(gpiobase) +
81 						gpio_offset[0], gpio_offset[1],
82 						1);
83 
84 		dir = fdtdec_get_int(gd->fdt_blob, pin_node, "direction", -1);
85 		if (dir != -1)
86 			ich6_pinctrl_set_direction(GPIO_IOSEL_OFFSET(gpiobase) +
87 						 gpio_offset[0], gpio_offset[1],
88 						 dir);
89 
90 		val = fdtdec_get_int(gd->fdt_blob, pin_node, "output-value",
91 				     -1);
92 		if (val != -1)
93 			ich6_pinctrl_set_value(GPIO_LVL_OFFSET(gpiobase) +
94 					     gpio_offset[0], gpio_offset[1],
95 					     val);
96 
97 		invert = fdtdec_get_bool(gd->fdt_blob, pin_node, "invert");
98 		if (invert)
99 			setio_32(gpiobase + GPI_INV, 1 << gpio_offset[1]);
100 		debug("gpio %#x bit %d, is_gpio %d, dir %d, val %d, invert %d\n",
101 		      gpio_offset[0], gpio_offset[1], is_gpio, dir, val,
102 		      invert);
103 	}
104 
105 	/* if iobase is present, let's configure the pad */
106 	if (iobase != -1) {
107 		ulong iobase_addr;
108 
109 		/*
110 		 * The offset for the same pin for the IOBASE and GPIOBASE are
111 		 * different, so instead of maintaining a lookup table,
112 		 * the device tree should provide directly the correct
113 		 * value for both mapping.
114 		 */
115 		pad_offset = fdtdec_get_int(gd->fdt_blob, pin_node,
116 					    "pad-offset", -1);
117 		if (pad_offset == -1)
118 			return 0;
119 
120 		/* compute the absolute pad address */
121 		iobase_addr = iobase + pad_offset;
122 
123 		/*
124 		 * Do we need to set a specific function mode?
125 		 * If someone put also 'mode-gpio', this option will
126 		 * be just ignored by the controller
127 		 */
128 		val = fdtdec_get_int(gd->fdt_blob, pin_node, "mode-func", -1);
129 		if (val != -1)
130 			clrsetbits_le32(iobase_addr, IOPAD_MODE_MASK, val);
131 
132 		/* Configure the pull-up/down if needed */
133 		val = fdtdec_get_int(gd->fdt_blob, pin_node, "pull-assign", -1);
134 		if (val != -1)
135 			clrsetbits_le32(iobase_addr,
136 					IOPAD_PULL_ASSIGN_MASK,
137 					val << IOPAD_PULL_ASSIGN_SHIFT);
138 
139 		val = fdtdec_get_int(gd->fdt_blob, pin_node, "pull-strength",
140 				     -1);
141 		if (val != -1)
142 			clrsetbits_le32(iobase_addr,
143 					IOPAD_PULL_STRENGTH_MASK,
144 					val << IOPAD_PULL_STRENGTH_SHIFT);
145 
146 		debug("%s: pad cfg [0x%x]: %08x\n", __func__, pad_offset,
147 		      readl(iobase_addr));
148 	}
149 
150 	return 0;
151 }
152 
153 static int ich6_pinctrl_probe(struct udevice *dev)
154 {
155 	struct udevice *pch;
156 	int pin_node;
157 	int ret;
158 	u32 gpiobase;
159 	u32 iobase = -1;
160 
161 	debug("%s: start\n", __func__);
162 	ret = uclass_first_device(UCLASS_PCH, &pch);
163 	if (ret)
164 		return ret;
165 	if (!pch)
166 		return -ENODEV;
167 
168 	/*
169 	 * Get the memory/io base address to configure every pins.
170 	 * IOBASE is used to configure the mode/pads
171 	 * GPIOBASE is used to configure the direction and default value
172 	 */
173 	ret = pch_get_gpio_base(pch, &gpiobase);
174 	if (ret) {
175 		debug("%s: invalid GPIOBASE address (%08x)\n", __func__,
176 		      gpiobase);
177 		return -EINVAL;
178 	}
179 
180 	/*
181 	 * Get the IOBASE, this is not mandatory as this is not
182 	 * supported by all the CPU
183 	 */
184 	ret = pch_get_io_base(pch, &iobase);
185 	if (ret && ret != -ENOSYS) {
186 		debug("%s: invalid IOBASE address (%08x)\n", __func__, iobase);
187 		return -EINVAL;
188 	}
189 
190 	for (pin_node = fdt_first_subnode(gd->fdt_blob, dev_of_offset(dev));
191 	     pin_node > 0;
192 	     pin_node = fdt_next_subnode(gd->fdt_blob, pin_node)) {
193 		/* Configure the pin */
194 		ret = ich6_pinctrl_cfg_pin(gpiobase, iobase, pin_node);
195 		if (ret != 0) {
196 			debug("%s: invalid configuration for the pin %d\n",
197 			      __func__, pin_node);
198 			return ret;
199 		}
200 	}
201 	debug("%s: done\n", __func__);
202 
203 	return 0;
204 }
205 
206 static const struct udevice_id ich6_pinctrl_match[] = {
207 	{ .compatible = "intel,x86-pinctrl", .data = X86_SYSCON_PINCONF },
208 	{ /* sentinel */ }
209 };
210 
211 U_BOOT_DRIVER(ich6_pinctrl) = {
212 	.name = "ich6_pinctrl",
213 	.id = UCLASS_SYSCON,
214 	.of_match = ich6_pinctrl_match,
215 	.probe = ich6_pinctrl_probe,
216 };
217