diff options
Diffstat (limited to 'board/keymile/km_arm/fpga_config.c')
-rw-r--r-- | board/keymile/km_arm/fpga_config.c | 256 |
1 files changed, 256 insertions, 0 deletions
diff --git a/board/keymile/km_arm/fpga_config.c b/board/keymile/km_arm/fpga_config.c new file mode 100644 index 0000000..fcc5fe6 --- /dev/null +++ b/board/keymile/km_arm/fpga_config.c @@ -0,0 +1,256 @@ +/* + * (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_INIT_B 0x10 +#define FPGA_DONE 0x20 + +static int fpga_done(void) +{ + int ret = 0; + u8 regval; + + /* this is only supported with the boco2 design */ + if (!check_boco2()) + return 0; + + ret = i2c_read(BOCO_ADDR, SPI_REG, 1, ®val, 1); + if (ret) { + printf("%s: error reading the BOCO @%#x !!\n", + __func__, SPI_REG); + return 0; + } + + return regval & FPGA_DONE ? 1 : 0; +} + +int skip; + +int trigger_fpga_config(void) +{ + int ret = 0; + + /* if the FPGA is already configured, we do not want to + * reconfigure it */ + skip = 0; + if (fpga_done()) { + printf("PCIe FPGA config: skipped\n"); + skip = 1; + return 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 | FPGA_INIT_B); + 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; + + /* finally, raise INIT_B to remove the config delay */ + ret = boco_set_bits(SPI_REG, FPGA_INIT_B); + 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 (skip) + return 0; + + 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 PCIE_RST 0x10 +#define TRAFFIC_RST 0x04 + +int fpga_reset(void) +{ + int ret = 0; + u8 resets; + + if (!check_boco2()) { + /* we do not have BOCO2, this is not really used */ + return 0; + } + + /* if we have skipped, we only want to reset the PCIe part */ + resets = skip ? PCIE_RST : PCIE_RST | TRAFFIC_RST; + + ret = boco_clear_bits(PRST1, resets); + if (ret) + return ret; + + /* small delay for the pulse */ + udelay(10); + + ret = boco_set_bits(PRST1, resets); + 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; +} + |