forked from Qortal/Brooklyn
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
215 lines
5.3 KiB
215 lines
5.3 KiB
// SPDX-License-Identifier: GPL-2.0+ |
|
/* |
|
* Copyright (C) 2016 Google, Inc |
|
*/ |
|
|
|
#include <common.h> |
|
#include <dm.h> |
|
#include <errno.h> |
|
#include <fdtdec.h> |
|
#include <pch.h> |
|
#include <pci.h> |
|
#include <asm/cpu.h> |
|
#include <asm/gpio.h> |
|
#include <asm/io.h> |
|
#include <asm/pci.h> |
|
#include <dm/pinctrl.h> |
|
|
|
DECLARE_GLOBAL_DATA_PTR; |
|
|
|
#define GPIO_USESEL_OFFSET(x) (x) |
|
#define GPIO_IOSEL_OFFSET(x) (x + 4) |
|
#define GPIO_LVL_OFFSET(x) ((x) ? (x) + 8 : 0xc) |
|
#define GPI_INV 0x2c |
|
|
|
#define IOPAD_MODE_MASK 0x7 |
|
#define IOPAD_PULL_ASSIGN_SHIFT 7 |
|
#define IOPAD_PULL_ASSIGN_MASK (0x3 << IOPAD_PULL_ASSIGN_SHIFT) |
|
#define IOPAD_PULL_STRENGTH_SHIFT 9 |
|
#define IOPAD_PULL_STRENGTH_MASK (0x3 << IOPAD_PULL_STRENGTH_SHIFT) |
|
|
|
static int ich6_pinctrl_set_value(uint16_t base, unsigned offset, int value) |
|
{ |
|
if (value) |
|
setio_32(base, 1UL << offset); |
|
else |
|
clrio_32(base, 1UL << offset); |
|
|
|
return 0; |
|
} |
|
|
|
static int ich6_pinctrl_set_function(uint16_t base, unsigned offset, int func) |
|
{ |
|
if (func) |
|
setio_32(base, 1UL << offset); |
|
else |
|
clrio_32(base, 1UL << offset); |
|
|
|
return 0; |
|
} |
|
|
|
static int ich6_pinctrl_set_direction(uint16_t base, unsigned offset, int dir) |
|
{ |
|
if (!dir) |
|
setio_32(base, 1UL << offset); |
|
else |
|
clrio_32(base, 1UL << offset); |
|
|
|
return 0; |
|
} |
|
|
|
static int ich6_pinctrl_cfg_pin(s32 gpiobase, s32 iobase, int pin_node) |
|
{ |
|
bool is_gpio, invert; |
|
u32 gpio_offset[2]; |
|
int pad_offset; |
|
int dir, val; |
|
int ret; |
|
|
|
/* |
|
* GPIO node is not mandatory, so we only do the pinmuxing if the |
|
* node exists. |
|
*/ |
|
ret = fdtdec_get_int_array(gd->fdt_blob, pin_node, "gpio-offset", |
|
gpio_offset, 2); |
|
if (!ret) { |
|
/* Do we want to force the GPIO mode? */ |
|
is_gpio = fdtdec_get_bool(gd->fdt_blob, pin_node, "mode-gpio"); |
|
if (is_gpio) |
|
ich6_pinctrl_set_function(GPIO_USESEL_OFFSET(gpiobase) + |
|
gpio_offset[0], gpio_offset[1], |
|
1); |
|
|
|
dir = fdtdec_get_int(gd->fdt_blob, pin_node, "direction", -1); |
|
if (dir != -1) |
|
ich6_pinctrl_set_direction(GPIO_IOSEL_OFFSET(gpiobase) + |
|
gpio_offset[0], gpio_offset[1], |
|
dir); |
|
|
|
val = fdtdec_get_int(gd->fdt_blob, pin_node, "output-value", |
|
-1); |
|
if (val != -1) |
|
ich6_pinctrl_set_value(GPIO_LVL_OFFSET(gpiobase) + |
|
gpio_offset[0], gpio_offset[1], |
|
val); |
|
|
|
invert = fdtdec_get_bool(gd->fdt_blob, pin_node, "invert"); |
|
if (invert) |
|
setio_32(gpiobase + GPI_INV, 1 << gpio_offset[1]); |
|
debug("gpio %#x bit %d, is_gpio %d, dir %d, val %d, invert %d\n", |
|
gpio_offset[0], gpio_offset[1], is_gpio, dir, val, |
|
invert); |
|
} |
|
|
|
/* if iobase is present, let's configure the pad */ |
|
if (iobase != -1) { |
|
ulong iobase_addr; |
|
|
|
/* |
|
* The offset for the same pin for the IOBASE and GPIOBASE are |
|
* different, so instead of maintaining a lookup table, |
|
* the device tree should provide directly the correct |
|
* value for both mapping. |
|
*/ |
|
pad_offset = fdtdec_get_int(gd->fdt_blob, pin_node, |
|
"pad-offset", -1); |
|
if (pad_offset == -1) |
|
return 0; |
|
|
|
/* compute the absolute pad address */ |
|
iobase_addr = iobase + pad_offset; |
|
|
|
/* |
|
* Do we need to set a specific function mode? |
|
* If someone put also 'mode-gpio', this option will |
|
* be just ignored by the controller |
|
*/ |
|
val = fdtdec_get_int(gd->fdt_blob, pin_node, "mode-func", -1); |
|
if (val != -1) |
|
clrsetbits_le32(iobase_addr, IOPAD_MODE_MASK, val); |
|
|
|
/* Configure the pull-up/down if needed */ |
|
val = fdtdec_get_int(gd->fdt_blob, pin_node, "pull-assign", -1); |
|
if (val != -1) |
|
clrsetbits_le32(iobase_addr, |
|
IOPAD_PULL_ASSIGN_MASK, |
|
val << IOPAD_PULL_ASSIGN_SHIFT); |
|
|
|
val = fdtdec_get_int(gd->fdt_blob, pin_node, "pull-strength", |
|
-1); |
|
if (val != -1) |
|
clrsetbits_le32(iobase_addr, |
|
IOPAD_PULL_STRENGTH_MASK, |
|
val << IOPAD_PULL_STRENGTH_SHIFT); |
|
|
|
debug("%s: pad cfg [0x%x]: %08x\n", __func__, pad_offset, |
|
readl(iobase_addr)); |
|
} |
|
|
|
return 0; |
|
} |
|
|
|
static int ich6_pinctrl_probe(struct udevice *dev) |
|
{ |
|
struct udevice *pch; |
|
int pin_node; |
|
int ret; |
|
u32 gpiobase; |
|
u32 iobase = -1; |
|
|
|
debug("%s: start\n", __func__); |
|
ret = uclass_first_device(UCLASS_PCH, &pch); |
|
if (ret) |
|
return ret; |
|
if (!pch) |
|
return -ENODEV; |
|
|
|
/* |
|
* Get the memory/io base address to configure every pins. |
|
* IOBASE is used to configure the mode/pads |
|
* GPIOBASE is used to configure the direction and default value |
|
*/ |
|
ret = pch_get_gpio_base(pch, &gpiobase); |
|
if (ret) { |
|
debug("%s: invalid GPIOBASE address (%08x)\n", __func__, |
|
gpiobase); |
|
return -EINVAL; |
|
} |
|
|
|
/* |
|
* Get the IOBASE, this is not mandatory as this is not |
|
* supported by all the CPU |
|
*/ |
|
ret = pch_get_io_base(pch, &iobase); |
|
if (ret && ret != -ENOSYS) { |
|
debug("%s: invalid IOBASE address (%08x)\n", __func__, iobase); |
|
return -EINVAL; |
|
} |
|
|
|
for (pin_node = fdt_first_subnode(gd->fdt_blob, dev_of_offset(dev)); |
|
pin_node > 0; |
|
pin_node = fdt_next_subnode(gd->fdt_blob, pin_node)) { |
|
/* Configure the pin */ |
|
ret = ich6_pinctrl_cfg_pin(gpiobase, iobase, pin_node); |
|
if (ret != 0) { |
|
debug("%s: invalid configuration for the pin %d\n", |
|
__func__, pin_node); |
|
return ret; |
|
} |
|
} |
|
debug("%s: done\n", __func__); |
|
|
|
return 0; |
|
} |
|
|
|
static const struct udevice_id ich6_pinctrl_match[] = { |
|
{ .compatible = "intel,x86-pinctrl", .data = X86_SYSCON_PINCONF }, |
|
{ /* sentinel */ } |
|
}; |
|
|
|
U_BOOT_DRIVER(ich6_pinctrl) = { |
|
.name = "ich6_pinctrl", |
|
.id = UCLASS_SYSCON, |
|
.of_match = ich6_pinctrl_match, |
|
.probe = ich6_pinctrl_probe, |
|
};
|
|
|