/* 
 | 
 * (C) Copyright 2012 
 | 
 * Valentin Lontgchamp, Keymile AG, valentin.longchamp@keymile.com 
 | 
 * 
 | 
 * SPDX-License-Identifier:    GPL-2.0+ 
 | 
 */ 
 | 
  
 | 
#include <common.h> 
 | 
#include <i2c.h> 
 | 
#include <linux/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; 
 | 
} 
 | 
  
 | 
#if defined(KM_PCIE_RESET_MPP7) 
 | 
  
 | 
#define KM_PEX_RST_GPIO_PIN    7 
 | 
int fpga_reset(void) 
 | 
{ 
 | 
    if (!check_boco2()) { 
 | 
        /* we do not have BOCO2, this is not really used */ 
 | 
        return 0; 
 | 
    } 
 | 
  
 | 
    printf("PCIe reset through GPIO7: "); 
 | 
    /* apply PCIe reset via GPIO */ 
 | 
    kw_gpio_set_valid(KM_PEX_RST_GPIO_PIN, 1); 
 | 
    kw_gpio_direction_output(KM_PEX_RST_GPIO_PIN, 1); 
 | 
    kw_gpio_set_value(KM_PEX_RST_GPIO_PIN, 0); 
 | 
    udelay(1000*10); 
 | 
    kw_gpio_set_value(KM_PEX_RST_GPIO_PIN, 1); 
 | 
  
 | 
    printf(" done\n"); 
 | 
  
 | 
    return 0; 
 | 
} 
 | 
  
 | 
#else 
 | 
  
 | 
#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; 
 | 
} 
 | 
#endif 
 | 
  
 | 
/* 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; 
 | 
} 
 |