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/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, ®val, 1);
+ if (ret) {
+ printf("%s: error reading the BOCO @%#x !!\n",
+ __func__, reg);
+ return ret;
+ }
+ regval &= ~flags;
+ ret = i2c_write(BOCO_ADDR, reg, 1, ®val, 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, ®val, 1);
+ if (ret) {
+ printf("%s: error reading the BOCO @%#x !!\n",
+ __func__, reg);
+ return ret;
+ }
+ regval |= flags;
+ ret = i2c_write(BOCO_ADDR, reg, 1, ®val, 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;
+}
+