blob: 64b3dda3345edb03ccf0ccd0170b975373339e3b [file] [log] [blame]
Lee Leahyb0005132015-05-12 18:19:47 -07001/*
2 * This file is part of the coreboot project.
3 *
4 * Copyright (C) 2014 Google Inc.
Lee Leahy1d14b3e2015-05-12 18:23:27 -07005 * Copyright (C) 2015 Intel Corporation.
Lee Leahyb0005132015-05-12 18:19:47 -07006 *
7 * This program is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation; version 2 of the License.
10 *
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
Lee Leahyb0005132015-05-12 18:19:47 -070015 */
16
17#include <stdint.h>
18#include <string.h>
19#include <arch/io.h>
Aaron Durbinaf030502015-08-08 01:11:32 -050020#include <console/console.h>
Lee Leahyb0005132015-05-12 18:19:47 -070021#include <device/device.h>
22#include <device/pci.h>
Lee Leahy1d14b3e2015-05-12 18:23:27 -070023#include <gpio.h>
24#include <soc/pcr.h>
Lee Leahyb0005132015-05-12 18:19:47 -070025#include <soc/iomap.h>
26#include <soc/pm.h>
27
Subrata Banikc1bc8172015-08-31 17:10:35 +053028static const int gpio_debug = 0;
Aaron Durbinffdf9012015-07-24 13:00:36 -050029
30/* There are 4 communities with 8 GPIO groups (GPP_[A:G] and GPD) */
31struct gpio_community {
32 int port_id;
33 /* Inclusive pads within the community. */
34 gpio_t min;
35 gpio_t max;
36};
37
38/* This is ordered to match ACPI and OS driver. */
39static const struct gpio_community communities[] = {
40 {
41 .port_id = PID_GPIOCOM0,
42 .min = GPP_A0,
43 .max = GPP_B23,
44 },
45 {
46 .port_id = PID_GPIOCOM1,
47 .min = GPP_C0,
48 .max = GPP_E23,
49 },
50 {
51 .port_id = PID_GPIOCOM3,
52 .min = GPP_F0,
53 .max = GPP_G7,
54 },
55 {
56 .port_id = PID_GPIOCOM2,
57 .min = GPD0,
58 .max = GPD11,
59 },
60};
61
Aaron Durbinaf030502015-08-08 01:11:32 -050062static const char *gpio_group_names[GPIO_NUM_GROUPS] = {
63 "GPP_A",
64 "GPP_B",
65 "GPP_C",
66 "GPP_D",
67 "GPP_E",
68 "GPP_F",
69 "GPP_G",
70 "GPD",
71};
72
73static inline size_t gpios_in_community(const struct gpio_community *comm)
74{
75 /* max is inclusive */
76 return comm->max - comm->min + 1;
77}
78
79static inline size_t groups_in_community(const struct gpio_community *comm)
80{
81 size_t n = gpios_in_community(comm) + GPIO_MAX_NUM_PER_GROUP - 1;
82 return n / GPIO_MAX_NUM_PER_GROUP;
83}
84
85static inline int gpio_index_gpd(gpio_t gpio)
86{
87 if (gpio >= GPD0 && gpio <= GPD11)
88 return 1;
89 return 0;
90}
91
Aaron Durbinffdf9012015-07-24 13:00:36 -050092static const struct gpio_community *gpio_get_community(gpio_t pad)
93{
94 size_t i;
95
96 for (i = 0; i < ARRAY_SIZE(communities); i++) {
97 const struct gpio_community *c = &communities[i];
98
99 if (pad >= c->min && pad <= c->max)
100 return c;
101 }
102
103 return NULL;
104}
105
Aaron Durbinaf030502015-08-08 01:11:32 -0500106static size_t community_clr_get_smi_sts(const struct gpio_community *comm,
107 uint32_t *sts)
108{
109 uint8_t *regs;
110 size_t i;
111 uint32_t *gpi_status_reg;
112 uint32_t *gpi_en_reg;
113 const size_t num_grps = groups_in_community(comm);
114
115 /* Not all groups can be routed to SMI. However, the registers
116 * read as 0. In order to simplify the logic read everything from
117 * each community. */
118 regs = pcr_port_regs(comm->port_id);
119 gpi_status_reg = (void *)&regs[GPI_SMI_STS_OFFSET];
120 gpi_en_reg = (void *)&regs[GPI_SMI_EN_OFFSET];
121 for (i = 0; i < num_grps; i++) {
122 sts[i] = read32(gpi_status_reg + i) & read32(gpi_en_reg + i);
123 /* Clear the enabled and set status bits. */
124 write32(gpi_status_reg + i, sts[i]);
125 }
126
127 return num_grps;
128}
129
130static void print_gpi_status(uint32_t status, const char *grp_name)
131{
132 int i;
133
134 if (!status)
135 return;
136
137 for (i = 31; i >= 0; i--) {
138 if (status & (1 << i))
139 printk(BIOS_DEBUG, "%s%d ", grp_name, i);
140 }
141}
142
143void gpi_clear_get_smi_status(struct gpi_status *sts)
144{
145 int i;
146 int do_print;
147 size_t sts_index = 0;
148
149 for (i = 0; i < ARRAY_SIZE(communities); i++) {
150 const struct gpio_community *comm = &communities[i];
151 sts_index += community_clr_get_smi_sts(comm,
152 &sts->grp[sts_index]);
153 }
154
155 do_print = 0;
156 for (i = 0; i < ARRAY_SIZE(sts->grp); i++) {
157 if (sts->grp[i] == 0)
158 continue;
159 do_print = 1;
160 break;
161 }
162
163 if (!do_print)
164 return;
165
166 printk(BIOS_DEBUG, "GPI_SMI_STS: ");
167 for (i = 0; i < ARRAY_SIZE(sts->grp); i++)
168 print_gpi_status(sts->grp[i], gpio_group_names[i]);
169 printk(BIOS_DEBUG, "\n");
170}
171
172int gpi_status_get(const struct gpi_status *sts, gpio_t gpi)
173{
174 const uint32_t *gpi_sts;
175
176 /* Check if valid gpi */
177 if (gpio_get_community(gpi) == NULL)
178 return 0;
179
180 /* If not in GPD group the index is a linear function based on
181 * GPI number and GPIO_MAX_NUM_PER_GROUP. */
182 if (gpio_index_gpd(gpi))
183 gpi_sts = &sts->grp[GPD];
184 else
185 gpi_sts = &sts->grp[gpi / GPIO_MAX_NUM_PER_GROUP];
186
187 return !!(*gpi_sts & (1 << (gpi % GPIO_MAX_NUM_PER_GROUP)));
188}
189
Aaron Durbin9a8dc372015-08-07 22:29:42 -0500190void gpio_route_gpe(uint16_t gpe0_route)
191{
192 int i;
193 uint32_t misc_cfg;
194 const uint32_t misc_cfg_reg_mask = GPE_DW_MASK;
195
196 misc_cfg = (uint32_t)gpe0_route << GPE_DW_SHIFT;
197 misc_cfg &= misc_cfg_reg_mask;
198
199 for (i = 0; i < ARRAY_SIZE(communities); i++) {
200 uint8_t *regs;
201 uint32_t reg;
202 const struct gpio_community *comm = &communities[i];
203
204 regs = pcr_port_regs(comm->port_id);
205
206 reg = read32(regs + MISCCFG_OFFSET);
207 reg &= ~misc_cfg_reg_mask;
208 reg |= misc_cfg;
209 write32(regs + MISCCFG_OFFSET, reg);
210 }
211}
212
Aaron Durbinffdf9012015-07-24 13:00:36 -0500213static void *gpio_dw_regs(gpio_t pad)
214{
215 const struct gpio_community *comm;
216 uint8_t *regs;
217 size_t pad_relative;
218
219 comm = gpio_get_community(pad);
220
221 if (comm == NULL)
222 return NULL;
223
224 regs = pcr_port_regs(comm->port_id);
225
226 pad_relative = pad - comm->min;
227
228 /* DW0 and DW1 regs are 4 bytes each. */
229 return &regs[PAD_CFG_DW_OFFSET + pad_relative * 8];
230}
231
232static void *gpio_hostsw_reg(gpio_t pad, size_t *bit)
233{
234 const struct gpio_community *comm;
235 uint8_t *regs;
236 size_t pad_relative;
237
238 comm = gpio_get_community(pad);
239
240 if (comm == NULL)
241 return NULL;
242
243 regs = pcr_port_regs(comm->port_id);
244
245 pad_relative = pad - comm->min;
246
247 /* Update the bit for this pad. */
248 *bit = (pad_relative % HOSTSW_OWN_PADS_PER);
249
250 /* HostSw regs are 4 bytes each. */
251 regs = &regs[HOSTSW_OWN_REG_OFFSET];
252 return &regs[(pad_relative / HOSTSW_OWN_PADS_PER) * 4];
253}
254
255static void gpio_handle_pad_mode(const struct pad_config *cfg)
256{
257 size_t bit;
258 uint32_t *hostsw_own_reg;
259 uint32_t reg;
260
261 bit = 0;
262 hostsw_own_reg = gpio_hostsw_reg(cfg->pad, &bit);
263
264 reg = read32(hostsw_own_reg);
265 reg &= ~(1U << bit);
266
267 if ((cfg->attrs & PAD_FIELD(HOSTSW, GPIO)) == PAD_FIELD(HOSTSW, GPIO))
268 reg |= (HOSTSW_GPIO << bit);
269 else
270 reg |= (HOSTSW_ACPI << bit);
271
272 write32(hostsw_own_reg, reg);
273}
274
Aaron Durbin13e2ed32015-08-09 14:20:41 -0500275static void gpi_enable_smi(gpio_t pad)
276{
277 const struct gpio_community *comm;
278 uint8_t *regs;
279 uint32_t *gpi_status_reg;
280 uint32_t *gpi_en_reg;
281 size_t group_offset;
282 uint32_t pad_mask;
283
284 comm = gpio_get_community(pad);
285
286 regs = pcr_port_regs(comm->port_id);
287 gpi_status_reg = (void *)&regs[GPI_SMI_STS_OFFSET];
288 gpi_en_reg = (void *)&regs[GPI_SMI_EN_OFFSET];
289
290 /* Offset of SMI STS/EN for this pad's group within the community. */
291 group_offset = (pad - comm->min) / GPIO_MAX_NUM_PER_GROUP;
292
293 /* Clear status then set enable. */
294 pad_mask = 1 << ((pad - comm->min) % GPIO_MAX_NUM_PER_GROUP);
295 write32(&gpi_status_reg[group_offset], pad_mask);
296 write32(&gpi_en_reg[group_offset],
297 read32(&gpi_en_reg[group_offset]) | pad_mask);
298}
299
Aaron Durbinffdf9012015-07-24 13:00:36 -0500300static void gpio_configure_pad(const struct pad_config *cfg)
301{
302 uint32_t *dw_regs;
303 uint32_t reg;
304 uint32_t termination;
Aaron Durbin13e2ed32015-08-09 14:20:41 -0500305 uint32_t dw0;
Aaron Durbinffdf9012015-07-24 13:00:36 -0500306 const uint32_t termination_mask = PAD_TERM_MASK << PAD_TERM_SHIFT;
307
308 dw_regs = gpio_dw_regs(cfg->pad);
309
310 if (dw_regs == NULL)
311 return;
312
Aaron Durbin13e2ed32015-08-09 14:20:41 -0500313 dw0 = cfg->dw0;
314
315 write32(&dw_regs[0], dw0);
Aaron Durbinffdf9012015-07-24 13:00:36 -0500316 reg = read32(&dw_regs[1]);
317 reg &= ~termination_mask;
318 termination = cfg->attrs;
319 termination &= termination_mask;
320 reg |= termination;
321 write32(&dw_regs[1], reg);
322
323 gpio_handle_pad_mode(cfg);
Aaron Durbin13e2ed32015-08-09 14:20:41 -0500324
325 if ((dw0 & PAD_FIELD(GPIROUTSMI, MASK)) == PAD_FIELD(GPIROUTSMI, YES))
326 gpi_enable_smi(cfg->pad);
Subrata Banikc1bc8172015-08-31 17:10:35 +0530327
328 if(gpio_debug)
329 printk(BIOS_DEBUG,
330 "Write Pad: Base(%p) - conf0 = %x conf1= %x pad # = %d\n",
331 &dw_regs[0], dw0, reg, cfg->pad);
Aaron Durbinffdf9012015-07-24 13:00:36 -0500332}
333
334void gpio_configure_pads(const struct pad_config *cfgs, size_t num)
335{
336 size_t i;
337
338 for (i = 0; i < num; i++)
339 gpio_configure_pad(&cfgs[i]);
340}
341
342void gpio_input_pulldown(gpio_t gpio)
343{
344 struct pad_config cfg = PAD_CFG_GPI(gpio, 5K_PD, DEEP);
345 gpio_configure_pad(&cfg);
346}
347
348void gpio_input_pullup(gpio_t gpio)
349{
350 struct pad_config cfg = PAD_CFG_GPI(gpio, 5K_PU, DEEP);
351 gpio_configure_pad(&cfg);
352}
353
354void gpio_input(gpio_t gpio)
355{
356 struct pad_config cfg = PAD_CFG_GPI(gpio, NONE, DEEP);
357 gpio_configure_pad(&cfg);
358}
359
360void gpio_output(gpio_t gpio, int value)
361{
362 struct pad_config cfg = PAD_CFG_GPO(gpio, value, DEEP);
363 gpio_configure_pad(&cfg);
364}
365
366int gpio_get(gpio_t gpio_num)
367{
368 uint32_t *dw_regs;
369 uint32_t reg;
370
371 dw_regs = gpio_dw_regs(gpio_num);
372
373 if (dw_regs == NULL)
374 return -1;
375
376 reg = read32(&dw_regs[0]);
377
378 return (reg >> GPIORXSTATE_SHIFT) & GPIORXSTATE_MASK;
379}
380
381void gpio_set(gpio_t gpio_num, int value)
382{
383 uint32_t *dw_regs;
384 uint32_t reg;
385
386 dw_regs = gpio_dw_regs(gpio_num);
387
388 if (dw_regs == NULL)
389 return;
390
391 reg = read32(&dw_regs[0]);
Aaron Durbin162aee92015-08-19 21:07:14 -0500392 reg &= ~PAD_FIELD(GPIOTXSTATE, MASK);
Aaron Durbinffdf9012015-07-24 13:00:36 -0500393 reg |= PAD_FIELD_VAL(GPIOTXSTATE, value);
394 write32(&dw_regs[0], reg);
395 /* GPIO port ids support posted write semantics. */
396}