blob: 7f03fd040769922fa37b77e8c4156a8344756604 [file] [log] [blame]
/*
* Copyright 2012 Google Inc.
*
* See file CREDITS for list of people who contributed to this
* project.
*
* 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; either version 2 of
* the License, or (at your option) any later version.
*
* 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., 59 Temple Place, Suite 330, Boston,
* MA 02111-1307 USA
*/
/*
* Danger Will Robinson! Bank 0 (GPIOs 0-31) seems to be fairly stable. Most
* ICH versions have more, but decoding the matrix that describes them is
* absurdly complex and constantly changing. We'll provide Bank 1 and Bank 2,
* but they will ONLY work for certain unspecified chipsets because the offset
* from GPIOBASE changes randomly. Even then, many GPIOs are unimplemented or
* reserved or subject to arcane restrictions.
*/
#include <assert.h>
#include <stdint.h>
#include "base/container_of.h"
#include "base/io.h"
#include "base/xalloc.h"
#include "drivers/bus/pci/pci.h"
#include "drivers/gpio/gpio.h"
#include "drivers/gpio/pch.h"
/* Functions for manipulating GPIO regs. */
static uint32_t pch_gpiobase(void)
{
static const uint32_t dev = PCI_DEV(0, 0x1f, 0);
static const uint8_t pci_cfg_gpiobase = 0x48;
static uint32_t base = ~(uint32_t)0;
if (base != ~(uint32_t)0)
return base;
base = pci_read_config32(dev, pci_cfg_gpiobase);
// Drop the IO space bit + baytrail EN bit (also safe on PantherPoint)
base &= ~0x3;
return base;
}
static int pch_gpio_set(unsigned bank, unsigned bit, uint8_t *bases, int val)
{
uint16_t addr = pch_gpiobase() + bases[bank];
uint32_t reg = inl(addr);
reg = (reg & ~(1 << bit)) | ((val & 1) << bit);
outl(reg, addr);
return 0;
}
static int pch_gpio_get(unsigned bank, unsigned bit, uint8_t *bases)
{
uint32_t reg = inl(pch_gpiobase() + bases[bank]);
return (reg >> bit) & 1;
}
/* Interface functions for manipulating a GPIO. */
static int pch_gpio_get_value(GpioOps *me)
{
assert(me);
PchGpio *gpio = container_of(me, PchGpio, ops);
if (!gpio->dir_set) {
if (pch_gpio_set(gpio->bank, gpio->bit,
gpio->cfg->io_start, 1) < 0)
return -1;
gpio->dir_set = 1;
}
return pch_gpio_get(gpio->bank, gpio->bit, gpio->cfg->lvl_start);
}
static int pch_gpio_set_value(GpioOps *me, unsigned value)
{
assert(me);
PchGpio *gpio = container_of(me, PchGpio, ops);
if (!gpio->dir_set) {
if (pch_gpio_set(gpio->bank, gpio->bit,
gpio->cfg->io_start, 0) < 0)
return -1;
gpio->dir_set = 1;
}
return pch_gpio_set(gpio->bank, gpio->bit,
gpio->cfg->lvl_start, value);
}
static int pch_gpio_use(PchGpio *me, unsigned use)
{
assert(me);
return pch_gpio_set(me->bank, me->bit, me->cfg->use_start, use);
}
/* Functions to allocate and set up a GPIO structure. */
static PchGpio *new_pch_gpio(PchGpioCfg *cfg, unsigned bank, unsigned bit)
{
die_if(bank >= cfg->num_banks || bit >= 32,
"GPIO parameters (%d, %d) out of bounds.\n", bank, bit);
PchGpio *gpio = xzalloc(sizeof(*gpio));
gpio->cfg = cfg;
gpio->use = &pch_gpio_use;
gpio->bank = bank;
gpio->bit = bit;
return gpio;
}
PchGpio *new_pch_gpio_input(PchGpioCfg *cfg, unsigned bank, unsigned bit)
{
PchGpio *gpio = new_pch_gpio(cfg, bank, bit);
gpio->ops.get = &pch_gpio_get_value;
return gpio;
}
PchGpio *new_pch_gpio_output(PchGpioCfg *cfg, unsigned bank, unsigned bit)
{
PchGpio *gpio = new_pch_gpio(cfg, bank, bit);
gpio->ops.set = &pch_gpio_set_value;
return gpio;
}