1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29#include <common.h>
30#include <dm.h>
31#include <errno.h>
32#include <fdtdec.h>
33#include <log.h>
34#include <pch.h>
35#include <pci.h>
36#include <asm/cpu.h>
37#include <asm/global_data.h>
38#include <asm/gpio.h>
39#include <asm/io.h>
40#include <asm/pci.h>
41
42DECLARE_GLOBAL_DATA_PTR;
43
44#define GPIO_PER_BANK 32
45
46struct ich6_bank_priv {
47
48 uint16_t use_sel;
49 uint16_t io_sel;
50 uint16_t lvl;
51 u32 lvl_write_cache;
52 bool use_lvl_write_cache;
53};
54
55#define GPIO_USESEL_OFFSET(x) (x)
56#define GPIO_IOSEL_OFFSET(x) (x + 4)
57#define GPIO_LVL_OFFSET(x) (x + 8)
58
59static int _ich6_gpio_set_value(struct ich6_bank_priv *bank, unsigned offset,
60 int value)
61{
62 u32 val;
63
64 if (bank->use_lvl_write_cache)
65 val = bank->lvl_write_cache;
66 else
67 val = inl(bank->lvl);
68
69 if (value)
70 val |= (1UL << offset);
71 else
72 val &= ~(1UL << offset);
73 outl(val, bank->lvl);
74 if (bank->use_lvl_write_cache)
75 bank->lvl_write_cache = val;
76
77 return 0;
78}
79
80static int _ich6_gpio_set_direction(uint16_t base, unsigned offset, int dir)
81{
82 u32 val;
83
84 if (!dir) {
85 val = inl(base);
86 val |= (1UL << offset);
87 outl(val, base);
88 } else {
89 val = inl(base);
90 val &= ~(1UL << offset);
91 outl(val, base);
92 }
93
94 return 0;
95}
96
97static int gpio_ich6_of_to_plat(struct udevice *dev)
98{
99 struct ich6_bank_plat *plat = dev_get_plat(dev);
100 u32 gpiobase;
101 int offset;
102 int ret;
103
104 ret = pch_get_gpio_base(dev->parent, &gpiobase);
105 if (ret)
106 return ret;
107
108 offset = fdtdec_get_int(gd->fdt_blob, dev_of_offset(dev), "reg", -1);
109 if (offset == -1) {
110 debug("%s: Invalid register offset %d\n", __func__, offset);
111 return -EINVAL;
112 }
113 plat->offset = offset;
114 plat->base_addr = gpiobase + offset;
115 plat->bank_name = fdt_getprop(gd->fdt_blob, dev_of_offset(dev),
116 "bank-name", NULL);
117
118 return 0;
119}
120
121static int ich6_gpio_probe(struct udevice *dev)
122{
123 struct ich6_bank_plat *plat = dev_get_plat(dev);
124 struct gpio_dev_priv *uc_priv = dev_get_uclass_priv(dev);
125 struct ich6_bank_priv *bank = dev_get_priv(dev);
126 const void *prop;
127
128 uc_priv->gpio_count = GPIO_PER_BANK;
129 uc_priv->bank_name = plat->bank_name;
130 bank->use_sel = plat->base_addr;
131 bank->io_sel = plat->base_addr + 4;
132 bank->lvl = plat->base_addr + 8;
133
134 prop = fdt_getprop(gd->fdt_blob, dev_of_offset(dev),
135 "use-lvl-write-cache", NULL);
136 if (prop)
137 bank->use_lvl_write_cache = true;
138 else
139 bank->use_lvl_write_cache = false;
140 bank->lvl_write_cache = 0;
141
142 return 0;
143}
144
145static int ich6_gpio_request(struct udevice *dev, unsigned offset,
146 const char *label)
147{
148 struct ich6_bank_priv *bank = dev_get_priv(dev);
149 u32 tmplong;
150
151
152
153
154
155
156 tmplong = inl(bank->use_sel);
157 if (!(tmplong & (1UL << offset))) {
158 debug("%s: gpio %d is reserved for internal use\n", __func__,
159 offset);
160 return -EPERM;
161 }
162
163 return 0;
164}
165
166static int ich6_gpio_direction_input(struct udevice *dev, unsigned offset)
167{
168 struct ich6_bank_priv *bank = dev_get_priv(dev);
169
170 return _ich6_gpio_set_direction(bank->io_sel, offset, 0);
171}
172
173static int ich6_gpio_direction_output(struct udevice *dev, unsigned offset,
174 int value)
175{
176 int ret;
177 struct ich6_bank_priv *bank = dev_get_priv(dev);
178
179 ret = _ich6_gpio_set_direction(bank->io_sel, offset, 1);
180 if (ret)
181 return ret;
182
183 return _ich6_gpio_set_value(bank, offset, value);
184}
185
186static int ich6_gpio_get_value(struct udevice *dev, unsigned offset)
187{
188 struct ich6_bank_priv *bank = dev_get_priv(dev);
189 u32 tmplong;
190 int r;
191
192 tmplong = inl(bank->lvl);
193 if (bank->use_lvl_write_cache)
194 tmplong |= bank->lvl_write_cache;
195 r = (tmplong & (1UL << offset)) ? 1 : 0;
196 return r;
197}
198
199static int ich6_gpio_set_value(struct udevice *dev, unsigned offset,
200 int value)
201{
202 struct ich6_bank_priv *bank = dev_get_priv(dev);
203 return _ich6_gpio_set_value(bank, offset, value);
204}
205
206static int ich6_gpio_get_function(struct udevice *dev, unsigned offset)
207{
208 struct ich6_bank_priv *bank = dev_get_priv(dev);
209 u32 mask = 1UL << offset;
210
211 if (!(inl(bank->use_sel) & mask))
212 return GPIOF_FUNC;
213 if (inl(bank->io_sel) & mask)
214 return GPIOF_INPUT;
215 else
216 return GPIOF_OUTPUT;
217}
218
219static const struct dm_gpio_ops gpio_ich6_ops = {
220 .request = ich6_gpio_request,
221 .direction_input = ich6_gpio_direction_input,
222 .direction_output = ich6_gpio_direction_output,
223 .get_value = ich6_gpio_get_value,
224 .set_value = ich6_gpio_set_value,
225 .get_function = ich6_gpio_get_function,
226};
227
228static const struct udevice_id intel_ich6_gpio_ids[] = {
229 { .compatible = "intel,ich6-gpio" },
230 { }
231};
232
233U_BOOT_DRIVER(gpio_ich6) = {
234 .name = "gpio_ich6",
235 .id = UCLASS_GPIO,
236 .of_match = intel_ich6_gpio_ids,
237 .ops = &gpio_ich6_ops,
238 .of_to_plat = gpio_ich6_of_to_plat,
239 .probe = ich6_gpio_probe,
240 .priv_auto = sizeof(struct ich6_bank_priv),
241 .plat_auto = sizeof(struct ich6_bank_plat),
242};
243