diff options
Diffstat (limited to 'src/soc/intel/broadwell/gpio.c')
-rw-r--r-- | src/soc/intel/broadwell/gpio.c | 199 |
1 files changed, 199 insertions, 0 deletions
diff --git a/src/soc/intel/broadwell/gpio.c b/src/soc/intel/broadwell/gpio.c new file mode 100644 index 0000000000..92699d28bb --- /dev/null +++ b/src/soc/intel/broadwell/gpio.c @@ -0,0 +1,199 @@ +/* + * This file is part of the coreboot project. + * + * Copyright (C) 2014 Google Inc. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include <stdint.h> +#include <string.h> +#include <arch/io.h> +#include <device/device.h> +#include <device/pci.h> +#include <broadwell/gpio.h> +#include <broadwell/iomap.h> +#include <broadwell/pm.h> + +/* + * This function will return a number that indicates which PIRQ + * this GPIO maps to. If this is not a PIRQ capable GPIO then + * it will return -1. The GPIO to PIRQ mapping is not linear. + */ +static int gpio_to_pirq(int gpio) +{ + switch (gpio) { + case 8: return 0; /* PIRQI */ + case 9: return 1; /* PIRQJ */ + case 10: return 2; /* PIRQK */ + case 13: return 3; /* PIRQL */ + case 14: return 4; /* PIRQM */ + case 45: return 5; /* PIRQN */ + case 46: return 6; /* PIRQO */ + case 47: return 7; /* PIRQP */ + case 48: return 8; /* PIRQQ */ + case 49: return 9; /* PIRQR */ + case 50: return 10; /* PIRQS */ + case 51: return 11; /* PIRQT */ + case 52: return 12; /* PIRQU */ + case 53: return 13; /* PIRQV */ + case 54: return 14; /* PIRQW */ + case 55: return 15; /* PIRQX */ + default: return -1; + }; +} + +void init_one_gpio(int gpio_num, struct gpio_config *config) +{ + u32 owner, route, irqen, reset; + int set, bit; + + if (gpio_num > MAX_GPIO_NUMBER || !config) + return; + + outl(config->conf0, GPIO_BASE_ADDRESS + GPIO_CONFIG0(gpio_num)); + outl(config->conf1, GPIO_BASE_ADDRESS + GPIO_CONFIG1(gpio_num)); + + /* Determine set and bit based on GPIO number */ + set = gpio_num >> 5; + bit = gpio_num % 32; + + /* Save settings from current GPIO config */ + owner = inl(GPIO_BASE_ADDRESS + GPIO_OWNER(set)); + route = inl(GPIO_BASE_ADDRESS + GPIO_ROUTE(set)); + irqen = inl(GPIO_BASE_ADDRESS + GPIO_IRQ_IE(set)); + reset = inl(GPIO_BASE_ADDRESS + GPIO_RESET(set)); + + owner |= config->owner << bit; + route |= config->route << bit; + irqen |= config->irqen << bit; + reset |= config->reset << bit; + + outl(owner, GPIO_BASE_ADDRESS + GPIO_OWNER(set)); + outl(route, GPIO_BASE_ADDRESS + GPIO_ROUTE(set)); + outl(irqen, GPIO_BASE_ADDRESS + GPIO_IRQ_IE(set)); + outl(reset, GPIO_BASE_ADDRESS + GPIO_RESET(set)); + + if (set == 0) { + u32 blink = inl(GPIO_BASE_ADDRESS + GPIO_BLINK); + blink |= config->blink << bit; + outl(blink, GPIO_BASE_ADDRESS + GPIO_BLINK); + } + + /* PIRQ to IO-APIC map */ + if (config->pirq == GPIO_PIRQ_APIC_ROUTE) { + u32 pirq2apic = inl(GPIO_BASE_ADDRESS + GPIO_PIRQ_APIC_EN); + set = gpio_to_pirq(gpio_num); + if (set >= 0) { + pirq2apic |= 1 << set; + outl(pirq2apic, GPIO_BASE_ADDRESS + GPIO_PIRQ_APIC_EN); + } + } +} + +void init_gpios(const struct gpio_config config[]) +{ + const struct gpio_config *entry; + u32 owner[3] = {0}; + u32 route[3] = {0}; + u32 irqen[3] = {0}; + u32 reset[3] = {0}; + u32 blink = 0; + u16 pirq2apic = 0; + int set, bit, gpio = 0; + + for (entry = config; entry->conf0 != GPIO_LIST_END; entry++, gpio++) { + if (gpio > MAX_GPIO_NUMBER) + break; + + /* Setup Configuration registers 1 and 2 */ + outl(entry->conf0, GPIO_BASE_ADDRESS + GPIO_CONFIG0(gpio)); + outl(entry->conf1, GPIO_BASE_ADDRESS + GPIO_CONFIG1(gpio)); + + /* Determine set and bit based on GPIO number */ + set = gpio >> 5; + bit = gpio % 32; + + /* Apply settings to set specific bits */ + owner[set] |= entry->owner << bit; + route[set] |= entry->route << bit; + irqen[set] |= entry->irqen << bit; + reset[set] |= entry->reset << bit; + + if (set == 0) + blink |= entry->blink << bit; + + /* PIRQ to IO-APIC map */ + if (entry->pirq == GPIO_PIRQ_APIC_ROUTE) { + set = gpio_to_pirq(gpio); + if (set >= 0) + pirq2apic |= 1 << set; + } + } + + for (set = 0; set <= 2; set++) { + outl(owner[set], GPIO_BASE_ADDRESS + GPIO_OWNER(set)); + outl(route[set], GPIO_BASE_ADDRESS + GPIO_ROUTE(set)); + outl(irqen[set], GPIO_BASE_ADDRESS + GPIO_IRQ_IE(set)); + outl(reset[set], GPIO_BASE_ADDRESS + GPIO_RESET(set)); + } + + outl(blink, GPIO_BASE_ADDRESS + GPIO_BLINK); + outl(pirq2apic, GPIO_BASE_ADDRESS + GPIO_PIRQ_APIC_EN); +} + +int get_gpio(int gpio_num) +{ + if (gpio_num > MAX_GPIO_NUMBER) + return 0; + + return !!(inl(GPIO_BASE_ADDRESS + GPIO_CONFIG0(gpio_num)) & GPI_LEVEL); +} + +/* + * get a number comprised of multiple GPIO values. gpio_num_array points to + * the array of gpio pin numbers to scan, terminated by -1. + */ +unsigned get_gpios(const int *gpio_num_array) +{ + int gpio; + unsigned bitmask = 1; + unsigned vector = 0; + + while (bitmask && + ((gpio = *gpio_num_array++) != -1)) { + if (get_gpio(gpio)) + vector |= bitmask; + bitmask <<= 1; + } + return vector; +} + +void set_gpio(int gpio_num, int value) +{ + u32 conf0; + + if (gpio_num > MAX_GPIO_NUMBER) + return; + + conf0 = inl(GPIO_BASE_ADDRESS + GPIO_CONFIG0(gpio_num)); + conf0 &= ~GPO_LEVEL_MASK; + conf0 |= value << GPO_LEVEL_SHIFT; + outl(conf0, GPIO_BASE_ADDRESS + GPIO_CONFIG0(gpio_num)); +} + +int gpio_is_native(int gpio_num) +{ + return !(inl(GPIO_BASE_ADDRESS + GPIO_CONFIG0(gpio_num)) & 1); +} |