/*
 * This file is part of the flashrom project.
 *
 * Copyright (C) 2009 Uwe Hermann <uwe@hermann-uwe.de>
 *
 * 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
 */

#include <stdlib.h>
#include <string.h>
#include <fcntl.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <errno.h>
#include "flash.h"

#define BIOS_ROM_ADDR		0x04
#define BIOS_ROM_DATA		0x08
#define INT_STATUS		0x0e
#define SELECT_REG_WINDOW	0x800

#define PCI_IO_BASE_ADDRESS	0x10

#define PCI_VENDOR_ID_3COM	0x10b7

uint32_t io_base_addr;
struct pci_access *pacc;
struct pci_filter filter;

#if defined(__FreeBSD__) || defined(__DragonFly__)
int io_fd;
#endif

#define OK 0
#define NT 1	/* Not tested */

struct pci_dev *gdev;

static struct nic_status {
	uint16_t device_id;
	int status;
	const char *device_name;
} nics[] = {
	{0x3114, NT, "Silicon Image, Inc. SiI 3114 [SATALink/SATARaid] Serial ATA Controller"},

	{},
};

struct pci_dev *sata_sil_validate(struct pci_dev *dev)
{
	int i = 0;
	uint32_t addr = -1;

	for (i = 0; nics[i].device_name != NULL; i++) {
		if (dev->device_id != nics[i].device_id)
			continue;

//		addr = pci_read_long(dev, PCI_IO_BASE_ADDRESS) & ~0x03;

		printf("Found NIC \"3COM %s\" (%04x:%04x), addr = 0x%x\n",
		       nics[i].device_name, 0x1095,
		       nics[i].device_id, addr);

		if (nics[i].status == NT) {
			printf("===\nThis NIC is UNTESTED. Please email a "
			       "report including the 'flashrom -p sata_sil'\n"
			       "output to flashrom@coreboot.org if it works "
			       "for you. Thank you for your help!\n===\n");
		}

		return dev;
	}

	return dev;
}

int sata_sil_init(void)
{
	struct pci_dev *dev;
	char *msg = NULL;

#if defined (__sun) && (defined(__i386) || defined(__amd64))
	if (sysi86(SI86V86, V86SC_IOPL, PS_IOPL) != 0) {
#elif defined(__FreeBSD__) || defined (__DragonFly__)
	if ((io_fd = open("/dev/io", O_RDWR)) < 0) {
#else
	if (iopl(3) != 0) {
#endif
		fprintf(stderr, "ERROR: Could not get IO privileges (%s).\n"
			"You need to be root.\n", strerror(errno));
		exit(1);
	}

	pacc = pci_alloc();     /* Get the pci_access structure */
	pci_init(pacc);         /* Initialize the PCI library */
	pci_scan_bus(pacc);     /* We want to get the list of devices */

	if (nic_pcidev != NULL) {
		pci_filter_init(pacc, &filter);
		
		if ((msg = pci_filter_parse_slot(&filter, nic_pcidev))) {
			fprintf(stderr, "Error: %s\n", msg);
			exit(1);
		}
	}

	if (!filter.vendor && !filter.device) {
		pci_filter_init(pacc, &filter);
		filter.vendor = 0x1095;
	}

	dev = pci_dev_find_filter(filter);

	if (dev && (dev->vendor_id == 0x1095))
		gdev = sata_sil_validate(dev);
	else {
		fprintf(stderr, "Error: No supported SATA cards found.\n");
		exit(1);
	}

	/*
	 * The lowest 16 bytes of the I/O mapped register space of (most) 3COM
	 * cards form a 'register window' into one of multiple (usually 8)
	 * register banks. For 3C90xB/3C90xC we need register window/bank 0.
	 */
//	OUTW(SELECT_REG_WINDOW + 0, io_base_addr + INT_STATUS);

	return 0;
}

int sata_sil_shutdown(void)
{
	free(nic_pcidev);
	pci_cleanup(pacc);
	return 0;
}

void *sata_sil_map(const char *descr, unsigned long phys_addr, size_t len)
{
	return 0;
}

void sata_sil_unmap(void *virt_addr, size_t len)
{
}

void sata_sil_chip_writeb(uint8_t val, volatile void *addr)
{
	uint32_t tmp = (1 << 25) | (0 << 24) | ((uint32_t) addr & 0xffffff);

	while (pci_read_long(gdev, 0x90) & (1 << 25)) ;

	pci_write_long(gdev, 0x90, tmp);

	while (pci_read_long(gdev, 0x90) & (1 << 25)) ;

	pci_write_byte(gdev, 0x94, val);
}

void sata_sil_chip_writew(uint16_t val, volatile void *addr)
{
}

void sata_sil_chip_writel(uint32_t val, volatile void *addr)
{
}

uint8_t sata_sil_chip_readb(const volatile void *addr)
{
	uint32_t tmp = (1 << 25) | (1 << 24) | ((uint32_t) addr & 0xffffff);

	while (pci_read_long(gdev, 0x90) & (1 << 25)) ;

	pci_write_long(gdev, 0x90, tmp);

	while (pci_read_long(gdev, 0x90) & (1 << 25)) ;

	return pci_read_byte(gdev, 0x94);
}

uint16_t sata_sil_chip_readw(const volatile void *addr)
{
	return 0xffff;
}

uint32_t sata_sil_chip_readl(const volatile void *addr)
{
	return 0xffffffff;
}

