blob: d4f71c562f859d0fb265f56e0cdb5127809c2958 [file] [log] [blame]
Tom Rini10e47792018-05-06 17:58:06 -04001// SPDX-License-Identifier: GPL-2.0+
Simon Glass11cd6312016-03-11 22:07:13 -07002/*
3 * Copyright (C) 2016 Google, Inc
Simon Glass11cd6312016-03-11 22:07:13 -07004 */
5
Simon Glass11cd6312016-03-11 22:07:13 -07006#include <dm.h>
7#include <errno.h>
8#include <fdtdec.h>
Simon Glass0f2af882020-05-10 11:40:05 -06009#include <log.h>
Simon Glass11cd6312016-03-11 22:07:13 -070010#include <pch.h>
11#include <pci.h>
12#include <asm/cpu.h>
Simon Glass3ba929a2020-10-30 21:38:53 -060013#include <asm/global_data.h>
Simon Glass11cd6312016-03-11 22:07:13 -070014#include <asm/gpio.h>
15#include <asm/io.h>
16#include <asm/pci.h>
17#include <dm/pinctrl.h>
18
19DECLARE_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
32static 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
42static 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
52static 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
62static 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) {
Simon Glassaa0ed9d2017-01-16 07:03:42 -0700107 ulong iobase_addr;
Simon Glass11cd6312016-03-11 22:07:13 -0700108
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
153static 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__);
Michal Suchanekac12a2f2022-10-12 21:57:59 +0200162 ret = uclass_first_device_err(UCLASS_PCH, &pch);
Simon Glass11cd6312016-03-11 22:07:13 -0700163 if (ret)
164 return ret;
Simon Glass11cd6312016-03-11 22:07:13 -0700165
166 /*
167 * Get the memory/io base address to configure every pins.
168 * IOBASE is used to configure the mode/pads
169 * GPIOBASE is used to configure the direction and default value
170 */
171 ret = pch_get_gpio_base(pch, &gpiobase);
172 if (ret) {
173 debug("%s: invalid GPIOBASE address (%08x)\n", __func__,
174 gpiobase);
175 return -EINVAL;
176 }
177
178 /*
179 * Get the IOBASE, this is not mandatory as this is not
180 * supported by all the CPU
181 */
182 ret = pch_get_io_base(pch, &iobase);
183 if (ret && ret != -ENOSYS) {
184 debug("%s: invalid IOBASE address (%08x)\n", __func__, iobase);
185 return -EINVAL;
186 }
187
Simon Glassdd79d6e2017-01-17 16:52:55 -0700188 for (pin_node = fdt_first_subnode(gd->fdt_blob, dev_of_offset(dev));
Simon Glass11cd6312016-03-11 22:07:13 -0700189 pin_node > 0;
190 pin_node = fdt_next_subnode(gd->fdt_blob, pin_node)) {
191 /* Configure the pin */
192 ret = ich6_pinctrl_cfg_pin(gpiobase, iobase, pin_node);
193 if (ret != 0) {
194 debug("%s: invalid configuration for the pin %d\n",
195 __func__, pin_node);
196 return ret;
197 }
198 }
199 debug("%s: done\n", __func__);
200
201 return 0;
202}
203
204static const struct udevice_id ich6_pinctrl_match[] = {
205 { .compatible = "intel,x86-pinctrl", .data = X86_SYSCON_PINCONF },
206 { /* sentinel */ }
207};
208
209U_BOOT_DRIVER(ich6_pinctrl) = {
210 .name = "ich6_pinctrl",
211 .id = UCLASS_SYSCON,
212 .of_match = ich6_pinctrl_match,
213 .probe = ich6_pinctrl_probe,
214};