/*
 * Copyright (C) 2003-2013 FAUmachine Team <info@faumachine.org>.
 * This program is free software. You can redistribute it and/or modify it
 * under the terms of the GNU General Public License, either version 2 of
 * the License, or (at your option) any later version. See COPYING.
 */

#define DEBUG	1

/* FIXME merge changes here with PCI version, split into different arch files */
#define __CARDBUS__	1

#include "config.h"

#include <assert.h>
#include <errno.h>
#include <fcntl.h>
#include <inttypes.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>

#include "qemu/bswap.h"
#include "glue.h"
#include "system.h"

#define CHIP		chip_intel_82557_cardbus
#define CHIP_(x)	chip_intel_82557_cardbus_ ## x

#include "chip_intel_82557_cardbus.h"

struct cpssp {
	unsigned int state_power;
	struct sig_pci_bus *port_bus;
	struct sig_std_logic *port_int;
	struct sig_cs *port_romcs;
	struct sig_isa_bus *port_rombus;
	struct sig_mdi *port_mdi;
	int eeprom_do;

#define STATE

#define NAME		eeprom
#define NAME_(x)	eeprom_ ## x
#define SNAME		"eeprom"
#include "arch_gen_eeprom.c"
#undef SNAME
#undef NAME_
#undef NAME

#define NAME		mac
#define NAME_(x)	mac_ ## x
#define SNAME		"mac"
#define __CARDBUS__	1
#include "arch_intel_8255x.c"
#undef __CARDBUS__
#undef SNAME
#undef NAME_
#undef NAME

#undef STATE
};

static /*forward*/ void
eeprom_do_set(struct cpssp *cpssp, unsigned int val);

static void
mac_phy_read(struct cpssp *cpssp, unsigned int reg, uint16_t *valp)
{
	sig_mdi_read(cpssp->port_mdi, cpssp, reg, valp);
}

static void
mac_phy_write(struct cpssp *cpssp, unsigned int reg, uint16_t val)
{
	sig_mdi_write(cpssp->port_mdi, cpssp, reg, val);
}

static void
mac_send(struct cpssp *cpssp, const void *buf, unsigned int buflen)
{
	sig_mdi_send(cpssp->port_mdi, cpssp, buf, buflen);
}

static void
mac_rom_read(struct cpssp *cpssp, uint16_t addr, uint8_t *valp)
{
	sig_cs_readb(cpssp->port_romcs, cpssp, addr, valp);
}

#define BEHAVIOR

#define NAME		eeprom
#define NAME_(x)	eeprom_ ## x
#define SNAME		"eeprom"
#include "arch_gen_eeprom.c"
#undef SNAME
#undef NAME_
#undef NAME

#define NAME		mac
#define NAME_(x)	mac_ ## x
#define SNAME		"mac"
#define __CARDBUS__	1
#include "arch_intel_8255x.c"
#undef __CARDBUS__
#undef SNAME
#undef NAME_
#undef NAME

#undef BEHAVIOR

static void
CHIP_(power_set)(void *_cpssp, unsigned int val)
{
	struct cpssp *cpssp = (struct cpssp *) _cpssp;

	cpssp->state_power = val;

	if (val) {
		mac_hardware_reset(cpssp);
	}
}

static void
CHIP_(n_reset_set)(void *_cpssp, unsigned int n_val)
{
	struct cpssp *cpssp = (struct cpssp *) _cpssp;

	if (n_val) {
		mac_hardware_reset(cpssp);
	}
}

void *
CHIP_(create)(
	const char *name,
	const char *mac,
	struct sig_manage *port_manage,
	struct sig_std_logic *port_power,
	struct sig_std_logic *port_reset_hash_,
	struct sig_pci_idsel *port_idsel,
	struct sig_pci_bus *port_bus,
	struct sig_std_logic *port_int,
	struct sig_cs *port_romcs,
	struct sig_isa_bus *port_rombus,
	struct sig_mdi *port_mdi
)
{
	static char mac_default[] = "00:D0:B7:00:00:01";
	static const struct sig_std_logic_funcs power_funcs = {
		.boolean_or_set = CHIP_(power_set),
	};
	static const struct sig_std_logic_funcs reset_hash__funcs = {
		.boolean_or_set = CHIP_(n_reset_set),
	};
	static const struct sig_pci_idsel_funcs idsel_funcs = {
		.c0r = mac_c0r,
		.c0w = mac_c0w,
	};
	static const struct sig_pci_bus_funcs bus_funcs = {
		.ior = mac_ior,
		.iow = mac_iow,

		.mr = mac_mr,
		.mw = mac_mw,
		.map_r = mac_map_r,
		.map_w = mac_map_w,
	};
	static const struct sig_mdi_funcs mdi_funcs = {
		.recv = mac_recv,
	};
	struct cpssp *cpssp;

	if (mac == NULL) {
		mac = "00:00:00:00:00:00";
	}
	if (strcmp(mac, "00:00:00:00:00:00") == 0) {
		mac = mac_default;
	}

	cpssp = shm_alloc(sizeof(*cpssp));
	assert(cpssp);

	system_name_push(name);

	eeprom_create(cpssp);
	mac_create(cpssp, name, mac, port_manage, port_int, port_mdi);

	/* Call */
	sig_pci_idsel_connect(port_idsel, cpssp, &idsel_funcs);

	cpssp->port_bus = port_bus;
	sig_pci_bus_connect(port_bus, cpssp, &bus_funcs);

	cpssp->port_mdi = port_mdi;
	sig_mdi_connect(port_mdi, cpssp, &mdi_funcs);

	/* Out */
	cpssp->port_int = port_int;
	sig_std_logic_connect_out(port_int, cpssp, SIG_STD_LOGIC_L);

	cpssp->port_romcs = port_romcs;
	cpssp->port_rombus = port_rombus;

	/* In */
	sig_std_logic_connect_in(port_power, cpssp, &power_funcs);

	sig_std_logic_connect_in(port_reset_hash_, cpssp, &reset_hash__funcs);

	system_name_pop();

	if (mac == mac_default) {
		mac_default[sizeof(mac_default) - 2]++;
	}

	return cpssp;
}

void
CHIP_(destroy)(void *_cpssp)
{
	struct cpssp *cpssp = _cpssp;

	eeprom_destroy(cpssp);
	mac_destroy(cpssp);

	shm_free(cpssp);
}

void
CHIP_(suspend)(void *_cpssp, FILE *fp)
{
	struct cpssp *cpssp = _cpssp;

	generic_suspend(cpssp, sizeof(*cpssp), fp);
}

void
CHIP_(resume)(void *_cpssp, FILE *fp)
{
	struct cpssp *cpssp = _cpssp;

	generic_resume(cpssp, sizeof(*cpssp), fp);
}
