broadcom/cygnus: Enable gpio on romstage

BUG=chrome-os-partner:35936
BRANCH=broadcom-firmware
TEST=When enable configuration CYGNUS_GPIO_TEST,
print on console:

Start gpio test...
[gpio_crmu] gpio:0 set input
[gpio_crmu] gpio:0 set pullup:0
GPIO get 170=0
gpio request enable pin=64 offset=0x1c
....
GPIO set 69=1
[gpio_asiu] gpio:45 set, value:0
GPIO set 69=0
Gpio test completed...

Additional test:
Measure on GPIO 69 to confirm the voltage
change.
Apply voltage on AON_GPIO0 to check reading.

Change-Id: I634cffccc7f0fa198317a92e3f1670ae6828892e
Signed-off-by: Patrick Georgi <pgeorgi@chromium.org>
Original-Commit-Id: 4784e5d88b9f7acb70bd6f6b12b1096f092f34b8
Original-Signed-off-by: Icarus Chau <ichau@broadcom.com>
Original-Reviewed-on: https://chrome-internal-review.googlesource.com/204537
Original-Reviewed-by: Daisuke Nojiri <dnojiri@google.com>
Original-Change-Id: Ia4f833be80d7844c2d014c9ffcf587d385e7766c
Original-Reviewed-on: https://chromium-review.googlesource.com/263495
Original-Reviewed-by: Daisuke Nojiri <dnojiri@chromium.org>
Original-Commit-Queue: Daisuke Nojiri <dnojiri@chromium.org>
Original-Trybot-Ready: Daisuke Nojiri <dnojiri@chromium.org>
Original-Tested-by: Daisuke Nojiri <dnojiri@chromium.org>
Reviewed-on: http://review.coreboot.org/9907
Tested-by: build bot (Jenkins)
Reviewed-by: Stefan Reinauer <stefan.reinauer@coreboot.org>
diff --git a/src/soc/broadcom/cygnus/gpio.c b/src/soc/broadcom/cygnus/gpio.c
new file mode 100644
index 0000000..9c4ef24
--- /dev/null
+++ b/src/soc/broadcom/cygnus/gpio.c
@@ -0,0 +1,501 @@
+/*
+* Copyright (C) 2015 Broadcom Corporation
+*
+* 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.
+*
+* This program is distributed "as is" WITHOUT ANY WARRANTY of any
+* kind, whether express or implied; without even the implied warranty
+* of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+* GNU General Public License for more details.
+*/
+
+#include <arch/io.h>
+#include <console/console.h>
+#include <gpio.h>
+#include <stdlib.h>
+#include <delay.h>
+
+#define dev_dbg(chip, fmt, args...) printk(BIOS_DEBUG, "[%s] " fmt, \
+	chip->label, args)
+
+#define CYGNUS_GPIO_DATA_IN_OFFSET   0x00
+#define CYGNUS_GPIO_DATA_OUT_OFFSET  0x04
+#define CYGNUS_GPIO_OUT_EN_OFFSET    0x08
+#define CYGNUS_GPIO_IN_TYPE_OFFSET   0x0c
+#define CYGNUS_GPIO_INT_DE_OFFSET    0x10
+#define CYGNUS_GPIO_INT_EDGE_OFFSET  0x14
+#define CYGNUS_GPIO_INT_MSK_OFFSET   0x18
+#define CYGNUS_GPIO_INT_STAT_OFFSET  0x1c
+#define CYGNUS_GPIO_INT_MSTAT_OFFSET 0x20
+#define CYGNUS_GPIO_INT_CLR_OFFSET   0x24
+#define CYGNUS_GPIO_PAD_RES_OFFSET   0x34
+#define CYGNUS_GPIO_RES_EN_OFFSET    0x38
+
+/* drive strength control for ASIU GPIO */
+#define CYGNUS_GPIO_ASIU_DRV0_CTRL_OFFSET 0x58
+
+/* drive strength control for CCM/CRMU (AON) GPIO */
+#define CYGNUS_GPIO_DRV0_CTRL_OFFSET  0x00
+
+#define GPIO_BANK_SIZE 0x200
+#define NGPIOS_PER_BANK 32
+#define GPIO_BANK(pin) ((pin) / NGPIOS_PER_BANK)
+
+#define CYGNUS_GPIO_REG(pin, reg) (GPIO_BANK(pin) * GPIO_BANK_SIZE + (reg))
+#define CYGNUS_GPIO_SHIFT(pin) ((pin) % NGPIOS_PER_BANK)
+
+#define GPIO_DRV_STRENGTH_BIT_SHIFT  20
+#define GPIO_DRV_STRENGTH_BITS       3
+#define GPIO_DRV_STRENGTH_BIT_MASK   ((1 << GPIO_DRV_STRENGTH_BITS) - 1)
+
+/*
+ * Cygnus GPIO core
+ *
+ * @base: I/O register base for Cygnus GPIO controller
+ * @io_ctrl: I/O register base for certain type of Cygnus GPIO controller that
+ * has the PINCONF support implemented outside of the GPIO block
+ * @num_banks: number of GPIO banks, each bank supports up to 32 GPIOs
+ * @pinmux_is_supported: flag to indicate this GPIO controller contains pins
+ * that can be individually muxed to GPIO
+ * @pctl_priv: pointer to pinctrl handle
+ */
+struct cygnus_gpio {
+	void		*base;
+	void		*io_ctrl;
+	const char	*label;
+	int			gpio_base;
+	u16			ngpio;
+	unsigned	num_banks;
+	int			pinmux_is_supported;
+	void		*pctl_priv;
+};
+
+/*
+ * GPIO cores table
+ *
+ * Cygnus has 3 gpio cores. The tables contains descriptors of those cores.
+ */
+struct cygnus_gpio cygnus_gpio_table[] = {
+	{
+		.base = (void *)0x03024800,
+		.io_ctrl = (void *)0x03024008,
+		.label = "gpio_crmu",
+		.gpio_base = 170,
+		.ngpio = 6,
+	},
+	{
+		.base = (void *)0x1800a000,
+		.io_ctrl = (void *)0x0301d164,
+		.label = "gpio_ccm",
+		.gpio_base = 0,
+		.ngpio = 24,
+	},
+	{
+		.base = (void *)0x180a5000,
+		.label = "gpio_asiu",
+		.gpio_base = 24,
+		.ngpio = 146,
+		.pinmux_is_supported = 1
+	}
+};
+
+/*
+ * Map a GPIO in the local gpio_chip pin space to a pin in the Cygnus IOMUX
+ * pinctrl pin space
+ */
+struct cygnus_gpio_pin_range {
+	unsigned offset;
+	unsigned pin_base;
+	unsigned num_pins;
+};
+
+#define CYGNUS_PINRANGE(o, p, n) { .offset = o, .pin_base = p, .num_pins = n }
+
+/*
+ * Pin mapping table for mapping local GPIO pins to Cygnus IOMUX pinctrl pins.
+ * This is for ASIU gpio. The offset is based on ASIU gpios.
+ */
+static const struct cygnus_gpio_pin_range cygnus_gpio_pintable[] = {
+	CYGNUS_PINRANGE(0, 42, 1),
+	CYGNUS_PINRANGE(1, 44, 3),
+	CYGNUS_PINRANGE(4, 48, 1),
+	CYGNUS_PINRANGE(5, 50, 3),
+	CYGNUS_PINRANGE(8, 126, 1),
+	CYGNUS_PINRANGE(9, 155, 1),
+	CYGNUS_PINRANGE(10, 152, 1),
+	CYGNUS_PINRANGE(11, 154, 1),
+	CYGNUS_PINRANGE(12, 153, 1),
+	CYGNUS_PINRANGE(13, 127, 3),
+	CYGNUS_PINRANGE(16, 140, 1),
+	CYGNUS_PINRANGE(17, 145, 7),
+	CYGNUS_PINRANGE(24, 130, 10),
+	CYGNUS_PINRANGE(34, 141, 4),
+	CYGNUS_PINRANGE(38, 54, 1),
+	CYGNUS_PINRANGE(39, 56, 3),
+	CYGNUS_PINRANGE(42, 60, 3),
+	CYGNUS_PINRANGE(45, 64, 3),
+	CYGNUS_PINRANGE(48, 68, 2),
+	CYGNUS_PINRANGE(50, 84, 6),
+	CYGNUS_PINRANGE(56, 94, 6),
+	CYGNUS_PINRANGE(62, 72, 1),
+	CYGNUS_PINRANGE(63, 70, 1),
+	CYGNUS_PINRANGE(64, 80, 1),
+	CYGNUS_PINRANGE(65, 74, 3),
+	CYGNUS_PINRANGE(68, 78, 1),
+	CYGNUS_PINRANGE(69, 82, 1),
+	CYGNUS_PINRANGE(70, 156, 17),
+	CYGNUS_PINRANGE(87, 104, 12),
+	CYGNUS_PINRANGE(99, 102, 2),
+	CYGNUS_PINRANGE(101, 90, 4),
+	CYGNUS_PINRANGE(105, 116, 10),
+	CYGNUS_PINRANGE(123, 11, 1),
+	CYGNUS_PINRANGE(124, 38, 4),
+	CYGNUS_PINRANGE(128, 43, 1),
+	CYGNUS_PINRANGE(129, 47, 1),
+	CYGNUS_PINRANGE(130, 49, 1),
+	CYGNUS_PINRANGE(131, 53, 1),
+	CYGNUS_PINRANGE(132, 55, 1),
+	CYGNUS_PINRANGE(133, 59, 1),
+	CYGNUS_PINRANGE(134, 63, 1),
+	CYGNUS_PINRANGE(135, 67, 1),
+	CYGNUS_PINRANGE(136, 71, 1),
+	CYGNUS_PINRANGE(137, 73, 1),
+	CYGNUS_PINRANGE(138, 77, 1),
+	CYGNUS_PINRANGE(139, 79, 1),
+	CYGNUS_PINRANGE(140, 81, 1),
+	CYGNUS_PINRANGE(141, 83, 1),
+	CYGNUS_PINRANGE(142, 10, 1)
+};
+
+static unsigned cygnus_gpio_to_pin(unsigned gpio)
+{
+	int i;
+
+	for (i = 0; i < ARRAY_SIZE(cygnus_gpio_pintable); i++) {
+		const struct cygnus_gpio_pin_range *range = cygnus_gpio_pintable
+			+ i;
+
+		if ((gpio < range->offset) ||
+			(gpio >= (range->offset + range->num_pins)))
+			continue;
+
+		return range->pin_base + (gpio - range->offset);
+	}
+	return -1;
+}
+
+static struct cygnus_gpio *cygnus_get_gpio_core(unsigned gpio,
+	unsigned *gpio_offset)
+{
+	int i;
+
+	for (i = 0; i < ARRAY_SIZE(cygnus_gpio_table); i++) {
+		struct cygnus_gpio *chip = cygnus_gpio_table + i;
+
+		if ((gpio < chip->gpio_base) ||
+			(gpio >= (chip->gpio_base + chip->ngpio)))
+			continue;
+
+		*gpio_offset = gpio - chip->gpio_base;
+		return chip;
+	}
+
+	return NULL;
+}
+
+static u32 cygnus_readl(struct cygnus_gpio *chip, unsigned int offset)
+{
+	return read32(chip->base + offset);
+}
+
+static void cygnus_writel(struct cygnus_gpio *chip, unsigned int offset,
+			  u32 val)
+{
+	write32(chip->base + offset, val);
+}
+
+/**
+ *  cygnus_set_bit - set or clear one bit (corresponding to the GPIO pin) in a
+ *  Cygnus GPIO register
+ *
+ *  @cygnus_gpio: Cygnus GPIO device
+ *  @reg: register offset
+ *  @gpio: GPIO pin
+ *  @set: set or clear. 1 - set; 0 -clear
+ */
+static void cygnus_set_bit(struct cygnus_gpio *chip, unsigned int reg,
+			   unsigned gpio, int set)
+{
+	unsigned int offset = CYGNUS_GPIO_REG(gpio, reg);
+	unsigned int shift = CYGNUS_GPIO_SHIFT(gpio);
+	u32 val;
+
+	val = cygnus_readl(chip, offset);
+	if (set)
+		val |= BIT(shift);
+	else
+		val &= ~BIT(shift);
+	cygnus_writel(chip, offset, val);
+}
+
+static int cygnus_get_bit(struct cygnus_gpio *chip, unsigned int reg,
+			  unsigned gpio)
+{
+	unsigned int offset = CYGNUS_GPIO_REG(gpio, reg);
+	unsigned int shift = CYGNUS_GPIO_SHIFT(gpio);
+	u32 val;
+
+	val = cygnus_readl(chip, offset) & BIT(shift);
+	if (val)
+		return 1;
+	else
+		return 0;
+}
+
+/*
+ * Request the Cygnus IOMUX pinmux controller to mux individual pins to GPIO
+ */
+static int cygnus_gpio_request(struct cygnus_gpio *chip, unsigned offset)
+{
+	/* not all Cygnus GPIO pins can be muxed individually */
+	if (!chip->pinmux_is_supported || (chip->pctl_priv == NULL))
+		return 0;
+
+	return cygnus_gpio_request_enable(chip->pctl_priv,
+		cygnus_gpio_to_pin(offset));
+}
+
+static void cygnus_gpio_free(struct cygnus_gpio *chip, unsigned offset)
+{
+	if (!chip->pinmux_is_supported || (chip->pctl_priv == NULL))
+		return;
+
+	cygnus_gpio_disable_free(chip->pctl_priv, cygnus_gpio_to_pin(offset));
+}
+
+static int cygnus_gpio_direction_input(struct cygnus_gpio *chip, unsigned gpio)
+{
+	cygnus_set_bit(chip, CYGNUS_GPIO_OUT_EN_OFFSET, gpio, 0);
+
+	dev_dbg(chip, "gpio:%u set input\n", gpio);
+
+	return 0;
+}
+
+static int cygnus_gpio_direction_output(struct cygnus_gpio *chip, unsigned gpio,
+					int value)
+{
+	cygnus_set_bit(chip, CYGNUS_GPIO_OUT_EN_OFFSET, gpio, 1);
+	cygnus_set_bit(chip, CYGNUS_GPIO_DATA_OUT_OFFSET, gpio, value);
+
+	dev_dbg(chip, "gpio:%u set output, value:%d\n", gpio, value);
+
+	return 0;
+}
+
+static void cygnus_gpio_set(struct cygnus_gpio *chip, unsigned gpio, int value)
+{
+	cygnus_set_bit(chip, CYGNUS_GPIO_DATA_OUT_OFFSET, gpio, value);
+
+	dev_dbg(chip, "gpio:%u set, value:%d\n", gpio, value);
+}
+
+static int cygnus_gpio_get(struct cygnus_gpio *chip, unsigned gpio)
+{
+	return cygnus_get_bit(chip, CYGNUS_GPIO_DATA_IN_OFFSET, gpio);
+}
+
+static int cygnus_gpio_set_pull(struct cygnus_gpio *chip, unsigned gpio,
+				int disable, int pull_up)
+{
+	if (disable) {
+		cygnus_set_bit(chip, CYGNUS_GPIO_RES_EN_OFFSET, gpio, 0);
+	} else {
+		cygnus_set_bit(chip, CYGNUS_GPIO_PAD_RES_OFFSET, gpio, pull_up);
+		cygnus_set_bit(chip, CYGNUS_GPIO_RES_EN_OFFSET, gpio, 1);
+	}
+
+	dev_dbg(chip, "gpio:%u set pullup:%d\n", gpio, pull_up);
+
+	return 0;
+}
+
+#define CYGNUS_GPIO_TEST_AON_GPIO0	170
+#define CYGNUS_GPIO_TEST_SPI2_MISO	69
+#define CYGNUS_GPIO_TEST_DELAY_S	3
+
+static void cygnus_gpio_test(void)
+{
+	gpio_t gpio_in;
+	gpio_t gpio_out;
+	unsigned val;
+
+	printk(BIOS_INFO, "Start gpio test...\n");
+
+	gpio_in = CYGNUS_GPIO_TEST_AON_GPIO0;  /* AON_GPIO0 */
+	gpio_input(gpio_in);
+	gpio_input_pulldown(gpio_in);
+	printk(BIOS_INFO, "GPIO get %d=%d\n", gpio_in, gpio_get(gpio_in));
+
+	gpio_in = CYGNUS_GPIO_TEST_SPI2_MISO;  /* SPI2_MISO */
+	gpio_input(gpio_in);
+	gpio_input_pullup(gpio_in);
+	printk(BIOS_INFO, "GPIO get %d=%d\n", gpio_in, gpio_get(gpio_in));
+	val = 0;
+	gpio_out = CYGNUS_GPIO_TEST_SPI2_MISO;
+
+	gpio_output(gpio_out, val);
+	printk(BIOS_INFO, "GPIO set %d=%d\n", gpio_out, val);
+	delay(CYGNUS_GPIO_TEST_DELAY_S);
+
+	val = 1;
+	gpio_set(gpio_out, val);
+	printk(BIOS_INFO, "GPIO set %d=%d\n", gpio_out, val);
+	delay(CYGNUS_GPIO_TEST_DELAY_S);
+
+	val = 0;
+	gpio_set(gpio_out, val);
+	printk(BIOS_INFO, "GPIO set %d=%d\n", gpio_out, val);
+	delay(CYGNUS_GPIO_TEST_DELAY_S);
+
+	val = 1;
+	gpio_set(gpio_out, val);
+	printk(BIOS_INFO, "GPIO set %d=%d\n", gpio_out, val);
+	delay(CYGNUS_GPIO_TEST_DELAY_S);
+
+	val = 0;
+	gpio_set(gpio_out, val);
+	printk(BIOS_INFO, "GPIO set %d=%d\n", gpio_out, val);
+	delay(CYGNUS_GPIO_TEST_DELAY_S);
+
+	gpio_free(CYGNUS_GPIO_TEST_AON_GPIO0);
+	gpio_free(CYGNUS_GPIO_TEST_SPI2_MISO);
+	printk(BIOS_INFO, "Gpio test completed...\n");
+}
+
+void gpio_init(void)
+{
+	int i;
+
+	printk(BIOS_INFO, "Setting up the gpio...\n");
+
+	for (i = 0; i < ARRAY_SIZE(cygnus_gpio_table); i++) {
+		struct cygnus_gpio *chip = cygnus_gpio_table + i;
+
+		chip->num_banks = (chip->ngpio+NGPIOS_PER_BANK - 1)
+			/ NGPIOS_PER_BANK;
+		if (chip->pinmux_is_supported)
+			chip->pctl_priv = cygnus_pinmux_init();
+	}
+
+	if (IS_ENABLED(CONFIG_CYGNUS_GPIO_TEST))
+		cygnus_gpio_test();
+}
+
+void gpio_free(gpio_t gpio)
+{
+	struct cygnus_gpio *chip;
+	unsigned gpio_num;
+
+	chip = cygnus_get_gpio_core(gpio, &gpio_num);
+	if (chip == NULL) {
+		dev_dbg(chip, "unable to find chip for gpio %d", gpio);
+		return;
+	}
+
+	cygnus_gpio_free(chip, gpio_num);
+}
+
+void gpio_input(gpio_t gpio)
+{
+	struct cygnus_gpio *chip;
+	unsigned gpio_num;
+
+	chip = cygnus_get_gpio_core(gpio, &gpio_num);
+	if (chip == NULL) {
+		dev_dbg(chip, "unable to find chip for gpio %d", gpio);
+		return;
+	}
+
+	if (cygnus_gpio_request(chip, gpio_num) != 0) {
+		printk(BIOS_ERR, "Cannot mux GPIO %d\n", gpio);
+		return;
+	}
+	cygnus_gpio_direction_input(chip, gpio_num);
+}
+
+void gpio_input_pulldown(gpio_t gpio)
+{
+	struct cygnus_gpio *chip;
+	unsigned gpio_num;
+
+	chip = cygnus_get_gpio_core(gpio, &gpio_num);
+	if (chip == NULL) {
+		dev_dbg(chip, "unable to find chip for gpio %d", gpio);
+		return;
+	}
+
+	cygnus_gpio_set_pull(chip, gpio_num, 0, 0);
+}
+
+void gpio_input_pullup(gpio_t gpio)
+{
+	struct cygnus_gpio *chip;
+	unsigned gpio_num;
+
+	chip = cygnus_get_gpio_core(gpio, &gpio_num);
+	if (chip == NULL) {
+		dev_dbg(chip, "unable to find chip for gpio %d", gpio);
+		return;
+	}
+
+	cygnus_gpio_set_pull(chip, gpio_num, 0, 1);
+}
+
+int gpio_get(gpio_t gpio)
+{
+	struct cygnus_gpio *chip;
+	unsigned gpio_num;
+
+	chip = cygnus_get_gpio_core(gpio, &gpio_num);
+	if (chip == NULL) {
+		dev_dbg(chip, "unable to find chip for gpio %d", gpio);
+		return -1;
+	}
+
+	return cygnus_gpio_get(chip, gpio_num);
+}
+
+void gpio_set(gpio_t gpio, int value)
+{
+	struct cygnus_gpio *chip;
+	unsigned gpio_num;
+
+	chip = cygnus_get_gpio_core(gpio, &gpio_num);
+	if (chip == NULL) {
+		dev_dbg(chip, "unable to find chip for gpio %d", gpio);
+		return;
+	}
+
+	cygnus_gpio_set(chip, gpio_num, value);
+}
+
+void gpio_output(gpio_t gpio, int value)
+{
+	struct cygnus_gpio *chip;
+	unsigned gpio_num;
+
+	chip = cygnus_get_gpio_core(gpio, &gpio_num);
+	if (chip == NULL) {
+		dev_dbg(chip, "unable to find chip for gpio %d", gpio);
+		return;
+	}
+
+	if (cygnus_gpio_request(chip, gpio_num) != 0) {
+		printk(BIOS_ERR, "Cannot mux GPIO %d\n", gpio);
+		return;
+	}
+	cygnus_gpio_direction_output(chip, gpio_num, value);
+}