|  | /* | 
|  | * (C) Copyright 2007 | 
|  | * Matthias Fuchs, esd gmbh, matthias.fuchs@esd-electronics.com. | 
|  | * | 
|  | * SPDX-License-Identifier:	GPL-2.0+ | 
|  | */ | 
|  |  | 
|  | #include <common.h> | 
|  | #include <asm/io.h> | 
|  | #include <spartan2.h> | 
|  | #include <spartan3.h> | 
|  | #include <command.h> | 
|  | #include "fpga.h" | 
|  | #include "pmc440.h" | 
|  |  | 
|  | DECLARE_GLOBAL_DATA_PTR; | 
|  |  | 
|  | #if defined(CONFIG_FPGA) | 
|  |  | 
|  | #define USE_SP_CODE | 
|  |  | 
|  | #ifdef USE_SP_CODE | 
|  | xilinx_spartan3_slave_parallel_fns pmc440_fpga_fns = { | 
|  | fpga_pre_config_fn, | 
|  | fpga_pgm_fn, | 
|  | fpga_init_fn, | 
|  | NULL, /* err */ | 
|  | fpga_done_fn, | 
|  | fpga_clk_fn, | 
|  | fpga_cs_fn, | 
|  | fpga_wr_fn, | 
|  | NULL, /* rdata */ | 
|  | fpga_wdata_fn, | 
|  | fpga_busy_fn, | 
|  | fpga_abort_fn, | 
|  | fpga_post_config_fn, | 
|  | }; | 
|  | #else | 
|  | xilinx_spartan3_slave_serial_fns pmc440_fpga_fns = { | 
|  | fpga_pre_config_fn, | 
|  | fpga_pgm_fn, | 
|  | fpga_clk_fn, | 
|  | fpga_init_fn, | 
|  | fpga_done_fn, | 
|  | fpga_wr_fn, | 
|  | fpga_post_config_fn, | 
|  | }; | 
|  | #endif | 
|  |  | 
|  | xilinx_spartan2_slave_serial_fns ngcc_fpga_fns = { | 
|  | ngcc_fpga_pre_config_fn, | 
|  | ngcc_fpga_pgm_fn, | 
|  | ngcc_fpga_clk_fn, | 
|  | ngcc_fpga_init_fn, | 
|  | ngcc_fpga_done_fn, | 
|  | ngcc_fpga_wr_fn, | 
|  | ngcc_fpga_post_config_fn | 
|  | }; | 
|  |  | 
|  | xilinx_desc fpga[CONFIG_FPGA_COUNT] = { | 
|  | XILINX_XC3S1200E_DESC( | 
|  | #ifdef USE_SP_CODE | 
|  | slave_parallel, | 
|  | #else | 
|  | slave_serial, | 
|  | #endif | 
|  | (void *)&pmc440_fpga_fns, | 
|  | 0), | 
|  | XILINX_XC2S200_DESC( | 
|  | slave_serial, | 
|  | (void *)&ngcc_fpga_fns, | 
|  | 0) | 
|  | }; | 
|  |  | 
|  |  | 
|  | /* | 
|  | * Set the active-low FPGA reset signal. | 
|  | */ | 
|  | void fpga_reset(int assert) | 
|  | { | 
|  | debug("%s:%d: RESET ", __FUNCTION__, __LINE__); | 
|  | if (assert) { | 
|  | out_be32((void*)GPIO1_OR, in_be32((void*)GPIO1_OR) & ~GPIO1_FPGA_DATA); | 
|  | debug("asserted\n"); | 
|  | } else { | 
|  | out_be32((void*)GPIO1_OR, in_be32((void*)GPIO1_OR) | GPIO1_FPGA_DATA); | 
|  | debug("deasserted\n"); | 
|  | } | 
|  | } | 
|  |  | 
|  |  | 
|  | /* | 
|  | * Initialize the SelectMap interface.  We assume that the mode and the | 
|  | * initial state of all of the port pins have already been set! | 
|  | */ | 
|  | void fpga_serialslave_init(void) | 
|  | { | 
|  | debug("%s:%d: Initialize serial slave interface\n", __FUNCTION__, | 
|  | __LINE__); | 
|  | fpga_pgm_fn(false, false, 0);	/* make sure program pin is inactive */ | 
|  | } | 
|  |  | 
|  |  | 
|  | /* | 
|  | * Set the FPGA's active-low SelectMap program line to the specified level | 
|  | */ | 
|  | int fpga_pgm_fn(int assert, int flush, int cookie) | 
|  | { | 
|  | debug("%s:%d: FPGA PROGRAM ", | 
|  | __FUNCTION__, __LINE__); | 
|  |  | 
|  | if (assert) { | 
|  | out_be32((void*)GPIO1_OR, in_be32((void*)GPIO1_OR) & ~GPIO1_FPGA_PRG); | 
|  | debug("asserted\n"); | 
|  | } else { | 
|  | out_be32((void*)GPIO1_OR, in_be32((void*)GPIO1_OR) | GPIO1_FPGA_PRG); | 
|  | debug("deasserted\n"); | 
|  | } | 
|  | return assert; | 
|  | } | 
|  |  | 
|  |  | 
|  | /* | 
|  | * Test the state of the active-low FPGA INIT line.  Return 1 on INIT | 
|  | * asserted (low). | 
|  | */ | 
|  | int fpga_init_fn(int cookie) | 
|  | { | 
|  | if (in_be32((void*)GPIO1_IR) & GPIO1_FPGA_INIT) | 
|  | return 0; | 
|  | else | 
|  | return 1; | 
|  | } | 
|  |  | 
|  | #ifdef USE_SP_CODE | 
|  | int fpga_abort_fn(int cookie) | 
|  | { | 
|  | return 0; | 
|  | } | 
|  |  | 
|  |  | 
|  | int fpga_cs_fn(int assert_cs, int flush, int cookie) | 
|  | { | 
|  | return assert_cs; | 
|  | } | 
|  |  | 
|  |  | 
|  | int fpga_busy_fn(int cookie) | 
|  | { | 
|  | return 1; | 
|  | } | 
|  | #endif | 
|  |  | 
|  |  | 
|  | /* | 
|  | * Test the state of the active-high FPGA DONE pin | 
|  | */ | 
|  | int fpga_done_fn(int cookie) | 
|  | { | 
|  | if (in_be32((void*)GPIO1_IR) & GPIO1_FPGA_DONE) | 
|  | return 1; | 
|  | else | 
|  | return 0; | 
|  | } | 
|  |  | 
|  |  | 
|  | /* | 
|  | * FPGA pre-configuration function. Just make sure that | 
|  | * FPGA reset is asserted to keep the FPGA from starting up after | 
|  | * configuration. | 
|  | */ | 
|  | int fpga_pre_config_fn(int cookie) | 
|  | { | 
|  | debug("%s:%d: FPGA pre-configuration\n", __FUNCTION__, __LINE__); | 
|  | fpga_reset(true); | 
|  |  | 
|  | /* release init# */ | 
|  | out_be32((void*)GPIO0_OR, in_be32((void*)GPIO0_OR) | GPIO0_FPGA_FORCEINIT); | 
|  | /* disable PLD IOs */ | 
|  | out_be32((void*)GPIO1_OR, in_be32((void*)GPIO1_OR) | GPIO1_IOEN_N); | 
|  | return 0; | 
|  | } | 
|  |  | 
|  |  | 
|  | /* | 
|  | * FPGA post configuration function. Blip the FPGA reset line and then see if | 
|  | * the FPGA appears to be running. | 
|  | */ | 
|  | int fpga_post_config_fn(int cookie) | 
|  | { | 
|  | pmc440_fpga_t *fpga = (pmc440_fpga_t *)FPGA_BA; | 
|  | int rc=0; | 
|  | char *s; | 
|  |  | 
|  | debug("%s:%d: FPGA post configuration\n", __FUNCTION__, __LINE__); | 
|  |  | 
|  | /* enable PLD0..7 pins */ | 
|  | out_be32((void*)GPIO1_OR, in_be32((void*)GPIO1_OR) & ~GPIO1_IOEN_N); | 
|  |  | 
|  | fpga_reset(true); | 
|  | udelay (100); | 
|  | fpga_reset(false); | 
|  | udelay (100); | 
|  |  | 
|  | FPGA_OUT32(&fpga->status, (gd->board_type << STATUS_HWREV_SHIFT) & STATUS_HWREV_MASK); | 
|  |  | 
|  | /* NGCC/CANDES only: enable ledlink */ | 
|  | if ((s = getenv("bd_type")) && | 
|  | ((!strcmp(s, "ngcc")) || (!strcmp(s, "candes")))) | 
|  | FPGA_SETBITS(&fpga->ctrla, 0x29f8c000); | 
|  |  | 
|  | return rc; | 
|  | } | 
|  |  | 
|  |  | 
|  | int fpga_clk_fn(int assert_clk, int flush, int cookie) | 
|  | { | 
|  | if (assert_clk) | 
|  | out_be32((void*)GPIO1_OR, in_be32((void*)GPIO1_OR) | GPIO1_FPGA_CLK); | 
|  | else | 
|  | out_be32((void*)GPIO1_OR, in_be32((void*)GPIO1_OR) & ~GPIO1_FPGA_CLK); | 
|  |  | 
|  | return assert_clk; | 
|  | } | 
|  |  | 
|  |  | 
|  | int fpga_wr_fn(int assert_write, int flush, int cookie) | 
|  | { | 
|  | if (assert_write) | 
|  | out_be32((void*)GPIO1_OR, in_be32((void*)GPIO1_OR) | GPIO1_FPGA_DATA); | 
|  | else | 
|  | out_be32((void*)GPIO1_OR, in_be32((void*)GPIO1_OR) & ~GPIO1_FPGA_DATA); | 
|  |  | 
|  | return assert_write; | 
|  | } | 
|  |  | 
|  | #ifdef USE_SP_CODE | 
|  | int fpga_wdata_fn(uchar data, int flush, int cookie) | 
|  | { | 
|  | uchar val = data; | 
|  | ulong or = in_be32((void*)GPIO1_OR); | 
|  | int i = 7; | 
|  | do { | 
|  | /* Write data */ | 
|  | if (val & 0x80) | 
|  | or = (or & ~GPIO1_FPGA_CLK) | GPIO1_FPGA_DATA; | 
|  | else | 
|  | or = or & ~(GPIO1_FPGA_CLK | GPIO1_FPGA_DATA); | 
|  |  | 
|  | out_be32((void*)GPIO1_OR, or); | 
|  |  | 
|  | /* Assert the clock */ | 
|  | or |= GPIO1_FPGA_CLK; | 
|  | out_be32((void*)GPIO1_OR, or); | 
|  | val <<= 1; | 
|  | i --; | 
|  | } while (i > 0); | 
|  |  | 
|  | /* Write last data bit (the 8th clock comes from the sp_load() code */ | 
|  | if (val & 0x80) | 
|  | or = (or & ~GPIO1_FPGA_CLK) | GPIO1_FPGA_DATA; | 
|  | else | 
|  | or = or & ~(GPIO1_FPGA_CLK | GPIO1_FPGA_DATA); | 
|  |  | 
|  | out_be32((void*)GPIO1_OR, or); | 
|  |  | 
|  | return 0; | 
|  | } | 
|  | #endif | 
|  |  | 
|  | #define NGCC_FPGA_PRG  CLOCK_EN | 
|  | #define NGCC_FPGA_DATA RESET_OUT | 
|  | #define NGCC_FPGA_DONE CLOCK_IN | 
|  | #define NGCC_FPGA_INIT IRIGB_R_IN | 
|  | #define NGCC_FPGA_CLK  CLOCK_OUT | 
|  |  | 
|  | void ngcc_fpga_serialslave_init(void) | 
|  | { | 
|  | debug("%s:%d: Initialize serial slave interface\n", | 
|  | __FUNCTION__, __LINE__); | 
|  |  | 
|  | /* make sure program pin is inactive */ | 
|  | ngcc_fpga_pgm_fn(false, false, 0); | 
|  | } | 
|  |  | 
|  | /* | 
|  | * Set the active-low FPGA reset signal. | 
|  | */ | 
|  | void ngcc_fpga_reset(int assert) | 
|  | { | 
|  | debug("%s:%d: RESET ", __FUNCTION__, __LINE__); | 
|  |  | 
|  | if (assert) { | 
|  | FPGA_CLRBITS(NGCC_CTRL_BASE, NGCC_CTRL_FPGARST_N); | 
|  | debug("asserted\n"); | 
|  | } else { | 
|  | FPGA_SETBITS(NGCC_CTRL_BASE, NGCC_CTRL_FPGARST_N); | 
|  | debug("deasserted\n"); | 
|  | } | 
|  | } | 
|  |  | 
|  |  | 
|  | /* | 
|  | * Set the FPGA's active-low SelectMap program line to the specified level | 
|  | */ | 
|  | int ngcc_fpga_pgm_fn(int assert, int flush, int cookie) | 
|  | { | 
|  | pmc440_fpga_t *fpga = (pmc440_fpga_t *)FPGA_BA; | 
|  |  | 
|  | debug("%s:%d: FPGA PROGRAM ", __FUNCTION__, __LINE__); | 
|  |  | 
|  | if (assert) { | 
|  | FPGA_CLRBITS(&fpga->ctrla, NGCC_FPGA_PRG); | 
|  | debug("asserted\n"); | 
|  | } else { | 
|  | FPGA_SETBITS(&fpga->ctrla, NGCC_FPGA_PRG); | 
|  | debug("deasserted\n"); | 
|  | } | 
|  |  | 
|  | return assert; | 
|  | } | 
|  |  | 
|  |  | 
|  | /* | 
|  | * Test the state of the active-low FPGA INIT line.  Return 1 on INIT | 
|  | * asserted (low). | 
|  | */ | 
|  | int ngcc_fpga_init_fn(int cookie) | 
|  | { | 
|  | pmc440_fpga_t *fpga = (pmc440_fpga_t *)FPGA_BA; | 
|  |  | 
|  | debug("%s:%d: INIT check... ", __FUNCTION__, __LINE__); | 
|  | if (FPGA_IN32(&fpga->status) & NGCC_FPGA_INIT) { | 
|  | debug("high\n"); | 
|  | return 0; | 
|  | } else { | 
|  | debug("low\n"); | 
|  | return 1; | 
|  | } | 
|  | } | 
|  |  | 
|  |  | 
|  | /* | 
|  | * Test the state of the active-high FPGA DONE pin | 
|  | */ | 
|  | int ngcc_fpga_done_fn(int cookie) | 
|  | { | 
|  | pmc440_fpga_t *fpga = (pmc440_fpga_t *)FPGA_BA; | 
|  |  | 
|  | debug("%s:%d: DONE check... ", __FUNCTION__, __LINE__); | 
|  | if (FPGA_IN32(&fpga->status) & NGCC_FPGA_DONE) { | 
|  | debug("DONE high\n"); | 
|  | return 1; | 
|  | } else { | 
|  | debug("low\n"); | 
|  | return 0; | 
|  | } | 
|  | } | 
|  |  | 
|  |  | 
|  | /* | 
|  | * FPGA pre-configuration function. | 
|  | */ | 
|  | int ngcc_fpga_pre_config_fn(int cookie) | 
|  | { | 
|  | pmc440_fpga_t *fpga = (pmc440_fpga_t *)FPGA_BA; | 
|  | debug("%s:%d: FPGA pre-configuration\n", __FUNCTION__, __LINE__); | 
|  |  | 
|  | ngcc_fpga_reset(true); | 
|  | FPGA_CLRBITS(&fpga->ctrla, 0xfffffe00); | 
|  |  | 
|  | ngcc_fpga_reset(true); | 
|  | return 0; | 
|  | } | 
|  |  | 
|  |  | 
|  | /* | 
|  | * FPGA post configuration function. Blip the FPGA reset line and then see if | 
|  | * the FPGA appears to be running. | 
|  | */ | 
|  | int ngcc_fpga_post_config_fn(int cookie) | 
|  | { | 
|  | pmc440_fpga_t *fpga = (pmc440_fpga_t *)FPGA_BA; | 
|  |  | 
|  | debug("%s:%d: NGCC FPGA post configuration\n", __FUNCTION__, __LINE__); | 
|  |  | 
|  | udelay (100); | 
|  | ngcc_fpga_reset(false); | 
|  |  | 
|  | FPGA_SETBITS(&fpga->ctrla, 0x29f8c000); | 
|  |  | 
|  | return 0; | 
|  | } | 
|  |  | 
|  |  | 
|  | int ngcc_fpga_clk_fn(int assert_clk, int flush, int cookie) | 
|  | { | 
|  | pmc440_fpga_t *fpga = (pmc440_fpga_t *)FPGA_BA; | 
|  |  | 
|  | if (assert_clk) | 
|  | FPGA_SETBITS(&fpga->ctrla, NGCC_FPGA_CLK); | 
|  | else | 
|  | FPGA_CLRBITS(&fpga->ctrla, NGCC_FPGA_CLK); | 
|  |  | 
|  | return assert_clk; | 
|  | } | 
|  |  | 
|  |  | 
|  | int ngcc_fpga_wr_fn(int assert_write, int flush, int cookie) | 
|  | { | 
|  | pmc440_fpga_t *fpga = (pmc440_fpga_t *)FPGA_BA; | 
|  |  | 
|  | if (assert_write) | 
|  | FPGA_SETBITS(&fpga->ctrla, NGCC_FPGA_DATA); | 
|  | else | 
|  | FPGA_CLRBITS(&fpga->ctrla, NGCC_FPGA_DATA); | 
|  |  | 
|  | return assert_write; | 
|  | } | 
|  |  | 
|  |  | 
|  | /* | 
|  | * Initialize the fpga.  Return 1 on success, 0 on failure. | 
|  | */ | 
|  | int pmc440_init_fpga(void) | 
|  | { | 
|  | char *s; | 
|  |  | 
|  | debug("%s:%d: Initialize FPGA interface\n", | 
|  | __FUNCTION__, __LINE__); | 
|  | fpga_init(); | 
|  |  | 
|  | fpga_serialslave_init (); | 
|  | debug("%s:%d: Adding fpga 0\n", __FUNCTION__, __LINE__); | 
|  | fpga_add (fpga_xilinx, &fpga[0]); | 
|  |  | 
|  | /* NGCC only */ | 
|  | if ((s = getenv("bd_type")) && !strcmp(s, "ngcc")) { | 
|  | ngcc_fpga_serialslave_init (); | 
|  | debug("%s:%d: Adding fpga 1\n", __FUNCTION__, __LINE__); | 
|  | fpga_add (fpga_xilinx, &fpga[1]); | 
|  | } | 
|  |  | 
|  | return 0; | 
|  | } | 
|  | #endif /* CONFIG_FPGA */ |