525 lines
16 KiB
C
525 lines
16 KiB
C
//
|
|
// Copyright (C) 2013-2014 Michael Geszkiewicz
|
|
//
|
|
// 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 St, Fifth Floor, Boston, MA 02110-1301 USA
|
|
//
|
|
|
|
#if MESAFLASH_IO
|
|
#ifdef __linux__
|
|
#include <linux/parport.h>
|
|
#include <sys/ioctl.h>
|
|
#include <sys/io.h>
|
|
#endif
|
|
#include <stdio.h>
|
|
#include <stddef.h>
|
|
#include <string.h>
|
|
#include <errno.h>
|
|
#include <fcntl.h>
|
|
#include <unistd.h>
|
|
#include <stdlib.h>
|
|
#include <sys/stat.h>
|
|
#include "types.h"
|
|
#include "common.h"
|
|
#include "eeprom.h"
|
|
#include "eeprom_local.h"
|
|
#include "bitfile.h"
|
|
#include "epp_boards.h"
|
|
|
|
extern board_t boards[MAX_BOARDS];
|
|
extern int boards_count;
|
|
extern u8 boot_block[BOOT_BLOCK_SIZE];
|
|
static u8 file_buffer[SECTOR_SIZE];
|
|
|
|
static inline u8 epp_read_status(board_t *board) {
|
|
u8 data = inb(board->base_lo + EPP_STATUS_OFFSET);
|
|
// printf("read status 0x%02X\n", data);
|
|
return data;
|
|
}
|
|
|
|
static inline void epp_write_status(board_t *board, u8 status_byte) {
|
|
outb(status_byte, board->base_lo + EPP_STATUS_OFFSET);
|
|
// printf("wrote status 0x%02X\n", status_byte);
|
|
}
|
|
|
|
static inline void epp_write_control(board_t *board, u8 control_byte) {
|
|
outb(control_byte, board->base_lo + EPP_CONTROL_OFFSET);
|
|
// printf("wrote control 0x%02X\n", control_byte);
|
|
}
|
|
|
|
static inline int epp_check_for_timeout(board_t *board) {
|
|
return epp_read_status(board) & 0x01;
|
|
}
|
|
|
|
static int epp_clear_timeout(board_t *board) {
|
|
u8 status;
|
|
|
|
if (!epp_check_for_timeout(board)) {
|
|
return 1;
|
|
}
|
|
|
|
/* To clear timeout some chips require double read */
|
|
epp_read_status(board);
|
|
|
|
// read in the actual status register
|
|
status = epp_read_status(board);
|
|
|
|
epp_write_status(board, status | 0x01); // Some reset by writing 1
|
|
epp_write_status(board, status & 0xFE); // Others by writing 0
|
|
|
|
if (epp_check_for_timeout(board)) {
|
|
printf("failed to clear EPP Timeout!\n");
|
|
return 0; // fail
|
|
}
|
|
return 1; // success
|
|
}
|
|
|
|
inline void epp_addr8(board_t *board, u8 addr) {
|
|
outb(addr, board->base_lo + EPP_ADDRESS_OFFSET);
|
|
// printf("selected address 0x%02X\n", addr);
|
|
}
|
|
|
|
static inline void epp_addr16(board_t *board, u16 addr) {
|
|
outb((addr & 0x00FF), board->base_lo + EPP_ADDRESS_OFFSET);
|
|
outb((addr >> 8), board->base_lo + EPP_ADDRESS_OFFSET);
|
|
// printf("selected address 0x%04X\n", addr);
|
|
}
|
|
|
|
inline u8 epp_read8(board_t *board) {
|
|
u8 data = inb(board->base_lo + EPP_DATA_OFFSET);
|
|
// printf("read data 0x%02X\n", data);
|
|
return data;
|
|
}
|
|
|
|
static inline u32 epp_read32(board_t *board) {
|
|
u32 data;
|
|
|
|
if (board->epp_wide) {
|
|
data = inl(board->base_lo + EPP_DATA_OFFSET);
|
|
// printf("read data 0x%08X\n", data);
|
|
} else {
|
|
u8 a, b, c, d;
|
|
a = epp_read8(board);
|
|
b = epp_read8(board);
|
|
c = epp_read8(board);
|
|
d = epp_read8(board);
|
|
data = a + (b << 8) + (c << 16) + (d << 24);
|
|
}
|
|
|
|
return data;
|
|
}
|
|
|
|
inline void epp_write8(board_t *board, u8 data) {
|
|
outb(data, board->base_lo + EPP_DATA_OFFSET);
|
|
//printf("wrote data 0x%02X\n", data);
|
|
}
|
|
|
|
static inline void epp_write32(board_t *board, u32 data) {
|
|
if (board->epp_wide) {
|
|
outl(data, board->base_lo + EPP_DATA_OFFSET);
|
|
// printf("wrote data 0x%08X\n", data);
|
|
} else {
|
|
epp_write8(board, (data) & 0xFF);
|
|
epp_write8(board, (data >> 8) & 0xFF);
|
|
epp_write8(board, (data >> 16) & 0xFF);
|
|
epp_write8(board, (data >> 24) & 0xFF);
|
|
}
|
|
}
|
|
|
|
int epp_read(llio_t *self, u32 addr, void *buffer, int size) {
|
|
int bytes_remaining = size;
|
|
board_t *board = self->board;
|
|
|
|
epp_addr16(board, addr | EPP_ADDR_AUTOINCREMENT);
|
|
|
|
for (; bytes_remaining > 3; bytes_remaining -= 4) {
|
|
*((u32*)buffer) = epp_read32(board);
|
|
buffer += 4;
|
|
}
|
|
|
|
for ( ; bytes_remaining > 0; bytes_remaining --) {
|
|
*((u8*)buffer) = epp_read8(board);
|
|
buffer ++;
|
|
}
|
|
|
|
if (epp_check_for_timeout(board)) {
|
|
printf("EPP timeout on data cycle of read(addr=0x%04x, size=%d)\n", addr, size);
|
|
// self->needs_reset = 1;
|
|
epp_clear_timeout(board);
|
|
return 0; // fail
|
|
}
|
|
|
|
return 1; // success
|
|
}
|
|
|
|
int epp_write(llio_t *self, u32 addr, void *buffer, int size) {
|
|
int bytes_remaining = size;
|
|
board_t *board = self->board;
|
|
|
|
epp_addr16(board, addr | EPP_ADDR_AUTOINCREMENT);
|
|
|
|
for (; bytes_remaining > 3; bytes_remaining -= 4) {
|
|
epp_write32(board, *((u32*)buffer));
|
|
buffer += 4;
|
|
}
|
|
|
|
for ( ; bytes_remaining > 0; bytes_remaining --) {
|
|
epp_write8(board, *((u8*)buffer));
|
|
buffer ++;
|
|
}
|
|
|
|
if (epp_check_for_timeout(board)) {
|
|
printf("EPP timeout on data cycle of write(addr=0x%04x, size=%d)\n", addr, size);
|
|
// self->needs_reset = 1;
|
|
epp_clear_timeout(board);
|
|
return 0;
|
|
}
|
|
|
|
return 1;
|
|
}
|
|
|
|
int epp_program_fpga(llio_t *self, char *bitfile_name) {
|
|
board_t *board = self->board;
|
|
int bindex, bytesread;
|
|
char part_name[32];
|
|
struct stat file_stat;
|
|
FILE *fp;
|
|
|
|
if (stat(bitfile_name, &file_stat) != 0) {
|
|
printf("Can't find file %s\n", bitfile_name);
|
|
return -1;
|
|
}
|
|
fp = fopen(bitfile_name, "rb");
|
|
if (fp == NULL) {
|
|
printf("Can't open file %s: %s\n", bitfile_name, strerror(errno));
|
|
return -1;
|
|
}
|
|
if (print_bitfile_header(fp, (char*) &part_name, board->llio.verbose) == -1) {
|
|
fclose(fp);
|
|
return -1;
|
|
}
|
|
//
|
|
// send the firmware
|
|
//
|
|
|
|
// select the CPLD's data address
|
|
epp_addr8(board, 0);
|
|
|
|
printf("Programming FPGA...\n");
|
|
printf(" |");
|
|
fflush(stdout);
|
|
// program the FPGA
|
|
while (!feof(fp)) {
|
|
bytesread = fread(&file_buffer, 1, 8192, fp);
|
|
bindex = 0;
|
|
while (bindex < bytesread) {
|
|
epp_write8(board, bitfile_reverse_bits(file_buffer[bindex]));
|
|
bindex++;
|
|
}
|
|
printf("W");
|
|
fflush(stdout);
|
|
}
|
|
|
|
printf("\n");
|
|
fclose(fp);
|
|
|
|
// see if it worked
|
|
if (epp_check_for_timeout(board)) {
|
|
printf("EPP Timeout while sending firmware!\n");
|
|
return -EIO;
|
|
}
|
|
|
|
printf("Board FPGA programmed successfully.\n");
|
|
return 0;
|
|
}
|
|
|
|
// return 0 if the board has been reset, -errno if not
|
|
int epp_reset(llio_t *self) {
|
|
board_t *board = self->board;
|
|
u8 byte;
|
|
|
|
|
|
//
|
|
// this resets the FPGA *only* if it's currently configured with the
|
|
// HostMot2 or GPIO firmware
|
|
//
|
|
|
|
epp_addr16(board, 0x7F7F);
|
|
epp_write8(board, 0x5A);
|
|
epp_addr16(board, 0x7F7F);
|
|
epp_write8(board, 0x5A);
|
|
|
|
|
|
//
|
|
// this code resets the FPGA *only* if the CPLD is in charge of the
|
|
// parallel port
|
|
//
|
|
|
|
// select the control register
|
|
epp_addr8(board, 1);
|
|
|
|
// bring the Spartan3's PROG_B line low for 1 us (the specs require 300-500 ns or longer)
|
|
epp_write8(board, 0x00);
|
|
sleep_ns(1000);
|
|
|
|
// bring the Spartan3's PROG_B line high and wait for 2 ms before sending firmware (required by spec)
|
|
epp_write8(board, 0x01);
|
|
sleep_ns(2 * 1000 * 1000);
|
|
|
|
// make sure the FPGA is not asserting its /DONE bit
|
|
byte = epp_read8(board);
|
|
if ((byte & 0x01) != 0) {
|
|
printf("/DONE is not low after CPLD reset!\n");
|
|
return -EIO;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////////
|
|
|
|
int epp_boards_init(board_access_t *access) {
|
|
(void)access;
|
|
#ifdef __linux__
|
|
if (seteuid(0) != 0) {
|
|
printf("You need root privileges (or setuid root) to access EPP hardware\n");
|
|
return -1;
|
|
}
|
|
iopl(3);
|
|
#endif
|
|
|
|
return 0;
|
|
}
|
|
|
|
void epp_boards_cleanup(board_access_t *access) {
|
|
(void)access;
|
|
}
|
|
|
|
static int epp_board_open(board_t *board) {
|
|
if (board->mode == BOARD_MODE_CPLD) {
|
|
u8 byte;
|
|
|
|
epp_addr8(board, 0);
|
|
byte = epp_read8(board);
|
|
if (byte & 0x01) {
|
|
board->llio.fpga_part_number = "3s400tq144";
|
|
} else {
|
|
board->llio.fpga_part_number = "3s200tq144";
|
|
}
|
|
}
|
|
if (board->flash != BOARD_FLASH_NONE) {
|
|
eeprom_init(&(board->llio));
|
|
board->flash_id = read_flash_id(&(board->llio));
|
|
if (board->fallback_support == 1) {
|
|
eeprom_prepare_boot_block(board->flash_id);
|
|
board->flash_start_address = eeprom_calc_user_space(board->flash_id);
|
|
} else {
|
|
board->flash_start_address = 0;
|
|
}
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
static int epp_board_close(board_t *board) {
|
|
if (board->flash != BOARD_FLASH_NONE) {
|
|
eeprom_cleanup(&(board->llio));
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
void epp_boards_scan(board_access_t *access) {
|
|
#ifdef __linux__
|
|
board_t *board = &boards[boards_count];
|
|
u16 epp_addr = 0, epp_hi_addr = 0;
|
|
u32 hm2_cookie, eppio_cookie;
|
|
|
|
board_init_struct(board);
|
|
|
|
if (access->address == 0) {
|
|
access->dev_addr = "0x378";
|
|
access->dev_hi_addr = "0x778";
|
|
epp_addr = 0x378;
|
|
epp_hi_addr = 0x778;
|
|
} else {
|
|
if (strncmp(access->dev_addr, "0x", 2) == 0) {
|
|
access->dev_addr[0] = '0';
|
|
access->dev_addr[1] = '0';
|
|
epp_addr = strtol(access->dev_addr, NULL, 16);
|
|
} else {
|
|
epp_addr = strtol(access->dev_addr, NULL, 10);
|
|
}
|
|
if (epp_addr == 0) {
|
|
return;
|
|
}
|
|
|
|
// Parse the base_hi address.
|
|
if (access->dev_hi_addr != NULL) {
|
|
if (strncmp(access->dev_hi_addr, "0x", 2) == 0) {
|
|
access->dev_hi_addr[0] = '0';
|
|
access->dev_hi_addr[1] = '0';
|
|
epp_hi_addr = strtol(access->dev_hi_addr, NULL, 16);
|
|
} else {
|
|
epp_hi_addr = strtol(access->dev_hi_addr, NULL, 10);
|
|
}
|
|
}
|
|
}
|
|
|
|
if (epp_hi_addr == 0) {
|
|
epp_hi_addr = epp_addr + 0x400;
|
|
}
|
|
|
|
board->base_lo = epp_addr;
|
|
board->base_hi = epp_hi_addr;
|
|
|
|
// set up the parport for EPP
|
|
if(board->base_hi) {
|
|
outb(0x94, board->base_hi + ECP_CONTROL_HIGH_OFFSET); // select EPP mode in ECR
|
|
}
|
|
|
|
// select the device and tell it to make itself ready for io
|
|
epp_write_control(board, 0x04); // set control lines and input mode
|
|
epp_clear_timeout(board);
|
|
|
|
board->epp_wide = 1;
|
|
epp_read(&(board->llio), 0, &eppio_cookie, 4);
|
|
epp_read(&(board->llio), HM2_COOKIE_REG, &hm2_cookie, 4);
|
|
|
|
// Check for a wide mode failure and re-read
|
|
if ((hm2_cookie & 0x000000FF) == (HM2_COOKIE & 0x000000FF)) {
|
|
board->epp_wide = 0;
|
|
epp_read(&(board->llio), 0, &eppio_cookie, 4);
|
|
epp_read(&(board->llio), HM2_COOKIE_REG, &hm2_cookie, 4);
|
|
// printf("epp wide is broken!\n");
|
|
}
|
|
|
|
if (hm2_cookie == HM2_COOKIE) {
|
|
u32 idrom_addr;
|
|
char board_name[8];
|
|
u32 *ptr = (u32 *) &board_name;
|
|
|
|
epp_addr16(board, HM2_IDROM_ADDR | EPP_ADDR_AUTOINCREMENT);
|
|
idrom_addr = epp_read32(board);
|
|
epp_addr16(board, (idrom_addr + offsetof(hm2_idrom_desc_t, board_name)) | EPP_ADDR_AUTOINCREMENT);
|
|
*ptr++ = epp_read32(board);
|
|
*ptr = epp_read32(board);
|
|
|
|
if (strncmp(board_name, "MESA7I90", 8) == 0) {
|
|
board->type = BOARD_EPP;
|
|
board->mode = BOARD_MODE_FPGA;
|
|
strncpy(board->dev_addr, access->dev_addr, 16);
|
|
strncpy(board->llio.board_name, "7I90HD", 16);
|
|
board->llio.num_ioport_connectors = 3;
|
|
board->llio.pins_per_connector = 24;
|
|
board->llio.ioport_connector_name[0] = "P1";
|
|
board->llio.ioport_connector_name[1] = "P2";
|
|
board->llio.ioport_connector_name[2] = "P3";
|
|
board->llio.fpga_part_number = "6slx9tqg144";
|
|
board->llio.num_leds = 2;
|
|
board->llio.read = &epp_read;
|
|
board->llio.write = &epp_write;
|
|
board->llio.write_flash = &local_write_flash;
|
|
board->llio.verify_flash = &local_verify_flash;
|
|
|
|
board->open = &epp_board_open;
|
|
board->close = &epp_board_close;
|
|
board->print_info = &epp_print_info;
|
|
board->flash = BOARD_FLASH_HM2;
|
|
board->fallback_support = 1;
|
|
board->llio.verbose = access->verbose;
|
|
|
|
boards_count++;
|
|
} else if (strncmp(board_name, "MESA7I43", 8) == 0) {
|
|
board->type = BOARD_EPP;
|
|
board->mode = BOARD_MODE_FPGA;
|
|
strncpy(board->dev_addr, access->dev_addr, 16);
|
|
strncpy(board->llio.board_name, "7I43", 16);
|
|
board->llio.num_ioport_connectors = 2;
|
|
board->llio.pins_per_connector = 24;
|
|
board->llio.ioport_connector_name[0] = "P4";
|
|
board->llio.ioport_connector_name[1] = "P3";
|
|
board->llio.num_leds = 2;
|
|
board->llio.read = &epp_read;
|
|
board->llio.write = &epp_write;
|
|
board->llio.program_fpga = &epp_program_fpga;
|
|
board->llio.reset = &epp_reset;
|
|
|
|
board->open = &epp_board_open;
|
|
board->close = &epp_board_close;
|
|
board->print_info = &epp_print_info;
|
|
board->flash = BOARD_FLASH_HM2;
|
|
board->llio.verbose = access->verbose;
|
|
|
|
boards_count++;
|
|
}
|
|
} else if (eppio_cookie == HM2_COOKIE) {
|
|
board->type = BOARD_EPP;
|
|
board->mode = BOARD_MODE_FPGA;
|
|
strncpy(board->dev_addr, access->dev_addr, 16);
|
|
strncpy(board->llio.board_name, "7I43", 16);
|
|
board->llio.num_ioport_connectors = 2;
|
|
board->llio.pins_per_connector = 24;
|
|
board->llio.ioport_connector_name[0] = "P4";
|
|
board->llio.ioport_connector_name[1] = "P3";
|
|
board->llio.num_leds = 2;
|
|
board->llio.read = &epp_read;
|
|
board->llio.write = &epp_write;
|
|
board->llio.program_fpga = &epp_program_fpga;
|
|
board->llio.write_flash = &local_write_flash;
|
|
board->llio.verify_flash = &local_verify_flash;
|
|
|
|
board->open = &epp_board_open;
|
|
board->close = &epp_board_close;
|
|
board->print_info = &epp_print_info;
|
|
board->flash = BOARD_FLASH_EPP;
|
|
board->llio.verbose = access->verbose;
|
|
|
|
boards_count++;
|
|
} else {
|
|
board->type = BOARD_EPP;
|
|
board->mode = BOARD_MODE_CPLD;
|
|
strncpy(board->dev_addr, access->dev_addr, 16);
|
|
strncpy(board->llio.board_name, "7I43", 16);
|
|
board->llio.num_ioport_connectors = 2;
|
|
board->llio.pins_per_connector = 24;
|
|
board->llio.ioport_connector_name[0] = "P4";
|
|
board->llio.ioport_connector_name[1] = "P3";
|
|
board->llio.num_leds = 2;
|
|
board->llio.program_fpga = &epp_program_fpga;
|
|
board->llio.reset = &epp_reset;
|
|
|
|
board->open = &epp_board_open;
|
|
board->close = &epp_board_close;
|
|
board->print_info = &epp_print_info;
|
|
board->llio.verbose = access->verbose;
|
|
|
|
boards_count++;
|
|
}
|
|
#endif
|
|
}
|
|
|
|
void epp_print_info(board_t *board) {
|
|
printf("\nEPP device %s at 0x%04X:0x%04X\n", board->llio.board_name, board->base_lo, board->base_hi);
|
|
if (board->llio.verbose == 0) {
|
|
return;
|
|
}
|
|
printf("Communication:\n");
|
|
if (board->mode == BOARD_MODE_CPLD) {
|
|
printf(" controlled by CPLD\n");
|
|
} else if (board->mode == BOARD_MODE_FPGA) {
|
|
printf(" controlled by FPGA\n");
|
|
}
|
|
|
|
show_board_info(board);
|
|
}
|
|
#endif
|