arm/km: enable BOCO2 FPGA download support

This adds a first support of the FPGA download for a PCIe FPGA based
on the BOCO2 CPLD.

This takes place in 3 steps, all done accessing the SPICTRL reg of the
BOCO2:
1) start the FPGA config with an access to the FPGA_PROG bit
2) later in the boot sequence, wait for the FPGA_DONE bit to toggle to 1
   for the end of the FPGA configuration (with a timeout)
3) reset the FPGA
4) finally remove the access to its config EEPROM from the FPGA so that
   the CPU can update the FPGA configuration when the kernel is running

The boards with a PCIe FPGA but without BOCO2 still are supported.

The config option name is CONFIG_KM_FPGA_CONFIG

Signed-off-by: Valentin Longchamp <valentin.longchamp@keymile.com>
Signed-off-by: Holger Brunck <holger.brunck@keymile.com>
cc: Gerlando Falauto <gerlando.falauto@keymile.com>
cc: Prafulla Wadaskar <prafulla@marvell.com>
diff --git a/board/keymile/km_arm/Makefile b/board/keymile/km_arm/Makefile
index aa51255..13d485a 100644
--- a/board/keymile/km_arm/Makefile
+++ b/board/keymile/km_arm/Makefile
@@ -31,6 +31,10 @@
 
 COBJS	:= $(BOARD).o ../common/common.o ../common/ivm.o
 
+ifdef CONFIG_KM_FPGA_CONFIG
+COBJS	+= fpga_config.o
+endif
+
 SRCS	:= $(SOBJS:.o=.S) $(COBJS:.o=.c)
 OBJS	:= $(addprefix $(obj),$(COBJS))
 SOBJS	:= $(addprefix $(obj),$(SOBJS))
diff --git a/board/keymile/km_arm/fpga_config.c b/board/keymile/km_arm/fpga_config.c
new file mode 100644
index 0000000..4356b9a
--- /dev/null
+++ b/board/keymile/km_arm/fpga_config.c
@@ -0,0 +1,212 @@
+/*
+ * (C) Copyright 2012
+ * Valentin Lontgchamp, Keymile AG, valentin.longchamp@keymile.com
+ *
+ * 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., 51 Franklin Street, Fifth Floor, Boston,
+ * MA 02110-1301 USA
+ */
+
+#include <common.h>
+#include <i2c.h>
+#include <asm/errno.h>
+
+/* GPIO Pin from kirkwood connected to PROGRAM_B pin of the xilinx FPGA */
+#define KM_XLX_PROGRAM_B_PIN    39
+
+#define BOCO_ADDR	0x10
+
+#define ID_REG		0x00
+#define BOCO2_ID	0x5b
+
+static int check_boco2(void)
+{
+	int ret;
+	u8 id;
+
+	ret = i2c_read(BOCO_ADDR, ID_REG, 1, &id, 1);
+	if (ret) {
+		printf("%s: error reading the BOCO id !!\n", __func__);
+		return ret;
+	}
+
+	return (id == BOCO2_ID);
+}
+
+static int boco_clear_bits(u8 reg, u8 flags)
+{
+	int ret;
+	u8 regval;
+
+	/* give access to the EEPROM from FPGA */
+	ret = i2c_read(BOCO_ADDR, reg, 1, &regval, 1);
+	if (ret) {
+		printf("%s: error reading the BOCO @%#x !!\n",
+			__func__, reg);
+		return ret;
+	}
+	regval &= ~flags;
+	ret = i2c_write(BOCO_ADDR, reg, 1, &regval, 1);
+	if (ret) {
+		printf("%s: error writing the BOCO @%#x !!\n",
+			__func__, reg);
+		return ret;
+	}
+
+	return 0;
+}
+
+static int boco_set_bits(u8 reg, u8 flags)
+{
+	int ret;
+	u8 regval;
+
+	/* give access to the EEPROM from FPGA */
+	ret = i2c_read(BOCO_ADDR, reg, 1, &regval, 1);
+	if (ret) {
+		printf("%s: error reading the BOCO @%#x !!\n",
+			__func__, reg);
+		return ret;
+	}
+	regval |= flags;
+	ret = i2c_write(BOCO_ADDR, reg, 1, &regval, 1);
+	if (ret) {
+		printf("%s: error writing the BOCO @%#x !!\n",
+			__func__, reg);
+		return ret;
+	}
+
+	return 0;
+}
+
+#define SPI_REG		0x06
+#define CFG_EEPROM	0x02
+#define FPGA_PROG	0x04
+#define FPGA_DONE	0x20
+
+int trigger_fpga_config(void)
+{
+	int ret = 0;
+
+	if (check_boco2()) {
+		/* we have a BOCO2, this has to be triggered here */
+
+		/* make sure the FPGA_can access the EEPROM */
+		ret = boco_clear_bits(SPI_REG, CFG_EEPROM);
+		if (ret)
+			return ret;
+
+		/* trigger the config start */
+		ret = boco_clear_bits(SPI_REG, FPGA_PROG);
+		if (ret)
+			return ret;
+
+		/* small delay for the pulse */
+		udelay(10);
+
+		/* up signal for pulse end */
+		ret = boco_set_bits(SPI_REG, FPGA_PROG);
+		if (ret)
+			return ret;
+
+	} else {
+		/* we do it the old way, with the gpio pin */
+		kw_gpio_set_valid(KM_XLX_PROGRAM_B_PIN, 1);
+		kw_gpio_direction_output(KM_XLX_PROGRAM_B_PIN, 0);
+		/* small delay for the pulse */
+		udelay(10);
+		kw_gpio_direction_input(KM_XLX_PROGRAM_B_PIN);
+	}
+
+	return 0;
+}
+
+int wait_for_fpga_config(void)
+{
+	int ret = 0;
+	u8 spictrl;
+	u32 timeout = 20000;
+
+	if (!check_boco2()) {
+		/* we do not have BOCO2, this is not really used */
+		return 0;
+	}
+
+	printf("PCIe FPGA config:");
+	do {
+		ret = i2c_read(BOCO_ADDR, SPI_REG, 1, &spictrl, 1);
+		if (ret) {
+			printf("%s: error reading the BOCO spictrl !!\n",
+				__func__);
+			return ret;
+		}
+		if (timeout-- == 0) {
+			printf(" FPGA_DONE timeout\n");
+			return -EFAULT;
+		}
+		udelay(10);
+	} while (!(spictrl & FPGA_DONE));
+
+	printf(" done\n");
+
+	return 0;
+}
+
+#define PRST1		0x4
+#define BRIDGE_RST	0x4
+
+int fpga_reset(void)
+{
+	int ret = 0;
+
+	if (!check_boco2()) {
+		/* we do not have BOCO2, this is not really used */
+		return 0;
+	}
+
+	ret = boco_clear_bits(PRST1, BRIDGE_RST);
+	if (ret)
+		return ret;
+
+	/* small delay for the pulse */
+	udelay(10);
+
+	ret = boco_set_bits(PRST1, BRIDGE_RST);
+	if (ret)
+		return ret;
+
+	return 0;
+}
+
+/* the FPGA was configured, we configure the BOCO2 so that the EEPROM
+ * is available from the Bobcat SPI bus */
+int toggle_eeprom_spi_bus(void)
+{
+	int ret = 0;
+
+	if (!check_boco2()) {
+		/* we do not have BOCO2, this is not really used */
+		return 0;
+	}
+
+	ret = boco_set_bits(SPI_REG, CFG_EEPROM);
+	if (ret)
+		return ret;
+
+	return 0;
+}
+
diff --git a/board/keymile/km_arm/km_arm.c b/board/keymile/km_arm/km_arm.c
index daab27b..c8da823 100644
--- a/board/keymile/km_arm/km_arm.c
+++ b/board/keymile/km_arm/km_arm.c
@@ -268,12 +268,6 @@
 	kw_gpio_set_valid(KM_KIRKWOOD_ENV_WP, 38);
 	kw_gpio_direction_output(KM_KIRKWOOD_ENV_WP, 1);
 #endif
-#if defined(CONFIG_KM_RECONFIG_XLX)
-	/* trigger the reconfiguration of the xilinx fpga */
-	kw_gpio_set_valid(KM_XLX_PROGRAM_B_PIN, 1);
-	kw_gpio_direction_output(KM_XLX_PROGRAM_B_PIN, 0);
-	kw_gpio_direction_input(KM_XLX_PROGRAM_B_PIN);
-#endif
 	return 0;
 }
 
@@ -282,6 +276,21 @@
 	/* address of boot parameters */
 	gd->bd->bi_boot_params = kw_sdram_bar(0) + 0x100;
 
+#if defined(CONFIG_KM_FPGA_CONFIG)
+	trigger_fpga_config();
+#endif
+
+	return 0;
+}
+
+int board_late_init(void)
+{
+#if defined(CONFIG_KM_FPGA_CONFIG)
+	wait_for_fpga_config();
+	fpga_reset();
+	toggle_eeprom_spi_bus();
+#endif
+
 	return 0;
 }