/*
 * Copyright (C) 2003-2009 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.
 */

/*
 * This is an implementation of the host controller of the
 * Intel 440LX chipset.
 */

#define DEBUG_SMRAM	0
#define DEBUG_CHIPSET	0

#include "config.h"

#include <assert.h>
#include <inttypes.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#include "glue.h"

#include "chip_intel_82443LX.h"

#define COMP_(x)	chip_intel_82443LX_ ## x

struct cpssp {
	/*
	 * Signals
	 */
	struct sig_host_bus *port_host_bus;
	struct sig_pci_bus *port_pci_bus;
	struct sig_agp_bus *port_agp_bus;
	struct sig_cs32 *port_mem_cs[8];

#define STATE
#define NAME		mem
#define NAME_(x)	mem_ ## x
#include "arch_intel_7180.c"
#undef NAME_
#undef NAME
#undef STATE
};

/* Host Bus Interface */

static void
mem_host_out_ior_info_flush(struct cpssp *cpssp, uint32_t port, unsigned int bs)
{
	sig_host_bus_ior_info_flush(cpssp->port_host_bus, cpssp, port, bs);
}

static void
mem_host_out_iow_info_flush(struct cpssp *cpssp, uint32_t port, unsigned int bs)
{
	sig_host_bus_iow_info_flush(cpssp->port_host_bus, cpssp, port, bs);
}

static int
mem_host_out_map_r_check(struct cpssp *cpssp, unsigned int state, uint32_t pa)
{
	return sig_host_bus_map_r_check(cpssp->port_host_bus, cpssp, state, pa);
}

static int
mem_host_out_map_w_check(struct cpssp *cpssp, unsigned int state, uint32_t pa)
{
	return sig_host_bus_map_w_check(cpssp->port_host_bus, cpssp, state, pa);
}

static void
mem_host_out_unmap(struct cpssp *cpssp, uint32_t addr, uint32_t len)
{
	sig_host_bus_unmap(cpssp->port_host_bus, cpssp, addr, len);
}

/* Memory Interface */

static int
mem_mem_out_read(
	struct cpssp *cpssp,
	unsigned int region,
	uint32_t addr,
	unsigned int bs,
	uint32_t *valp
)
{
	return sig_cs32_mr(cpssp->port_mem_cs[region], cpssp, addr, bs, valp);
}

static int
mem_mem_out_write(
	struct cpssp *cpssp,
	unsigned int region,
	uint32_t addr,
	unsigned int bs,
	uint32_t val
)
{
	return sig_cs32_mw(cpssp->port_mem_cs[region], cpssp, addr, bs, val);
}

static int
mem_mem_out_map_r(
	struct cpssp *cpssp,
	unsigned int region,
	uint32_t addr,
	int (**cfp)(void *, uint32_t, unsigned int, uint32_t *),
	void **csp,
	char **haddr_p
)
{
	return sig_cs32_map_r(cpssp->port_mem_cs[region], cpssp, addr,
			cfp, csp, haddr_p);
}

static int
mem_mem_out_map_w(
	struct cpssp *cpssp,
	unsigned int region,
	uint32_t addr,
	int (**cfp)(void *, uint32_t, unsigned int, uint32_t),
	void **csp,
	char **haddr_p
)
{
	return sig_cs32_map_w(cpssp->port_mem_cs[region], cpssp, addr, 
			cfp, csp, haddr_p);
}

/* PCI Bus Interface */

static void
mem_pci_out_main_addr_type(struct cpssp *cpssp, uint32_t addr, unsigned int type)
{
	sig_pci_bus_addr_type(cpssp->port_pci_bus, cpssp, addr, type);
}

static int
mem_pci_out_main_read_data(struct cpssp *cpssp, unsigned int bs, uint32_t *valp)
{
	return sig_pci_bus_read_data(cpssp->port_pci_bus, cpssp, bs, valp);
}

static int
mem_pci_out_main_write_data(struct cpssp *cpssp, unsigned int bs, uint32_t val)
{
	return sig_pci_bus_write_data(cpssp->port_pci_bus, cpssp, bs, val);
}

static int
mem_pci_out_main_c0r(struct cpssp *cpssp, uint32_t addr, unsigned int bs, uint32_t *valp)
{
	return sig_pci_bus_c0r(cpssp->port_pci_bus, cpssp, addr, bs, valp);
}

static int
mem_pci_out_main_c0w(struct cpssp *cpssp, uint32_t addr, unsigned int bs, uint32_t val)
{
	return sig_pci_bus_c0w(cpssp->port_pci_bus, cpssp, addr, bs, val);
}

static int
mem_pci_out_c1r(struct cpssp *cpssp, uint32_t addr, unsigned int bs, uint32_t *valp)
{
	return sig_pci_bus_c1r(cpssp->port_pci_bus, cpssp, addr, bs, valp);
}

static int
mem_pci_out_c1w(struct cpssp *cpssp, uint32_t addr, unsigned int bs, uint32_t val)
{
	return sig_pci_bus_c1w(cpssp->port_pci_bus, cpssp, addr, bs, val);
}

static int
mem_pci_out_ior(struct cpssp *cpssp, uint32_t addr, unsigned int bs, uint32_t *valp)
{
	return sig_pci_bus_ior(cpssp->port_pci_bus, cpssp, addr, bs, valp);
}

static int
mem_pci_out_iow(struct cpssp *cpssp, uint32_t addr, unsigned int bs, uint32_t val)
{
	return sig_pci_bus_iow(cpssp->port_pci_bus, cpssp, addr, bs, val);
}

static int
mem_pci_out_ior_info(
	struct cpssp *cpssp,
	uint32_t port,
	unsigned int bs,
	int (**cf)(void *, uint32_t, unsigned int, uint32_t *),
        void **cs
)
{
	return sig_pci_bus_ior_info(cpssp->port_pci_bus, cpssp,
			port, bs, cf, cs);
}

static int
mem_pci_out_iow_info(
	struct cpssp *cpssp,
	uint32_t port,
	unsigned int bs,
	int (**cf)(void *, uint32_t, unsigned int, uint32_t),
        void **cs
)
{
	return sig_pci_bus_iow_info(cpssp->port_pci_bus, cpssp,
			port, bs, cf, cs);
}

static int
mem_pci_out_mr(struct cpssp *cpssp, uint32_t addr, unsigned int bs, uint32_t *valp)
{
	return sig_pci_bus_mr(cpssp->port_pci_bus, cpssp, addr, bs, valp);
}

static int
mem_pci_out_mw(struct cpssp *cpssp, uint32_t addr, unsigned int bs, uint32_t val)
{
	return sig_pci_bus_mw(cpssp->port_pci_bus, cpssp, addr, bs, val);
}

static int
mem_pci_out_map_r_check(struct cpssp *cpssp, uint32_t addr)
{
	return sig_pci_bus_map_r_check(cpssp->port_pci_bus, cpssp, addr);
}

static int
mem_pci_out_map_r(
	struct cpssp *cpssp,
	uint32_t addr,
	int (**cfp)(void *, uint32_t, unsigned int, uint32_t *),
	void **csp,
	char **haddr_p
)
{
	return sig_pci_bus_map_r(cpssp->port_pci_bus, cpssp, addr,
			cfp, csp, haddr_p);
}

static int
mem_pci_out_map_w_check(struct cpssp *cpssp, uint32_t addr)
{
	return sig_pci_bus_map_w_check(cpssp->port_pci_bus, cpssp, addr);
}

static int
mem_pci_out_map_w(
	struct cpssp *cpssp,
	uint32_t addr,
	int (**cfp)(void *, uint32_t, unsigned int, uint32_t),
	void **csp,
	char **haddr_p
)
{
	return sig_pci_bus_map_w(cpssp->port_pci_bus, cpssp, addr,
			cfp, csp, haddr_p);
}

static void
mem_pci_out_unmap(struct cpssp *cpssp, uint32_t addr, uint32_t len)
{
	sig_pci_bus_unmap(cpssp->port_pci_bus, cpssp, addr, len);
}

static int
mem_pci_out_inta_addr(struct cpssp *cpssp)
{
	return sig_pci_bus_inta_addr(cpssp->port_pci_bus, cpssp);
}

static int
mem_pci_out_inta_data(struct cpssp *cpssp, uint8_t *valp)
{
	return sig_pci_bus_inta_data(cpssp->port_pci_bus, cpssp, valp);
}

#define BEHAVIOR
#define NAME		mem
#define NAME_(x)	mem_ ## x
#include "arch_intel_7180.c"
#undef NAME_
#undef NAME
#undef BEHAVIOR

void *
COMP_(create)(
	const char *name,
	struct sig_manage *port_manage,
	struct sig_std_logic *port_power,
	struct sig_std_logic *port_reset_hash_,
	struct sig_host_bus *port_host_bus,
	struct sig_cs32 *port_mem_cs0,
	struct sig_cs32 *port_mem_cs1,
	struct sig_cs32 *port_mem_cs2,
	struct sig_cs32 *port_mem_cs3,
	struct sig_cs32 *port_mem_cs4,
	struct sig_cs32 *port_mem_cs5,
	struct sig_cs32 *port_mem_cs6,
	struct sig_cs32 *port_mem_cs7,
	struct sig_mem_bus *port_mem,
	struct sig_pci_bus *port_pci_bus,
	struct sig_agp_bus *port_agp_bus
)
{
	static const struct sig_std_logic_funcs power_funcs = {
		.boolean_or_set = mem_power_set,
	};
	static const struct sig_std_logic_funcs reset_hash__funcs = {
		.boolean_or_set = mem_n_reset_set,
	};
	static const struct sig_host_bus_funcs host_bus_funcs = {
		.addr_type =    mem_host_bus_addr_type,
		.read_data =    mem_host_bus_read_data,
		.write_data =   mem_host_bus_write_data,

		.ior =	  mem_host_bus_ior,
		.iow =	  mem_host_bus_iow,

		.ior_info =     mem_host_bus_ior_info,
		.iow_info =     mem_host_bus_iow_info,

		.mr =	   mem_host_bus_mr,
		.mw =	   mem_host_bus_mw,
		.map_r_check =  mem_host_bus_map_r_check,
		.map_r =	mem_host_bus_map_r,
		.map_w_check =  mem_host_bus_map_w_check,
		.map_w =	mem_host_bus_map_w,
		.map_x_check =  mem_host_bus_map_r_check, /* ! */
		.map_x =	mem_host_bus_map_r, /* ! */

		.inta_addr =    mem_host_bus_inta_addr,
		.inta_data =    mem_host_bus_inta_data,

#if 0   /* Not used yet. */
		.unmap =	mem_host_bus_unmap,
#endif
	};
	static const struct sig_cs32_funcs mem_cs0_funcs = {
		.unmap =	mem_memX_unmap,
	};
	static const struct sig_cs32_funcs mem_cs1_funcs = {
		.unmap =	mem_memX_unmap,
	};
	static const struct sig_cs32_funcs mem_cs2_funcs = {
		.unmap =	mem_memX_unmap,
	};
	static const struct sig_cs32_funcs mem_cs3_funcs = {
		.unmap =	mem_memX_unmap,
	};
	static const struct sig_cs32_funcs mem_cs4_funcs = {
		.unmap =	mem_memX_unmap,
	};
	static const struct sig_cs32_funcs mem_cs5_funcs = {
		.unmap =	mem_memX_unmap,
	};
	static const struct sig_cs32_funcs mem_cs6_funcs = {
		.unmap =	mem_memX_unmap,
	};
	static const struct sig_cs32_funcs mem_cs7_funcs = {
		.unmap =	mem_memX_unmap,
	};
	static const struct sig_pci_bus_funcs pci_bus_funcs = {
		.mr =	   mem_pci_bus_mr,
		.mw =	   mem_pci_bus_mw,
		.map_r_check =  mem_pci_bus_map_r_check,
		.map_r =	mem_pci_bus_map_r,
		.map_w_check =  mem_pci_bus_map_w_check,
		.map_w =	mem_pci_bus_map_w,

#if 0   /* Not used yet. */
		.ack =	  mem_pci_bus_ack,
#endif

		.unmap =	mem_pci_bus_unmap,
	};
	struct cpssp *cpssp;

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

	/* Out */
	/* Call */
	cpssp->port_host_bus = port_host_bus;
	sig_host_bus_connect(port_host_bus, cpssp, &host_bus_funcs);

	cpssp->port_mem_cs[0] = port_mem_cs0;
	sig_cs32_connect(port_mem_cs0, cpssp, &mem_cs0_funcs);

	cpssp->port_mem_cs[1] = port_mem_cs1;
	sig_cs32_connect(port_mem_cs1, cpssp, &mem_cs1_funcs);

	cpssp->port_mem_cs[2] = port_mem_cs2;
	sig_cs32_connect(port_mem_cs2, cpssp, &mem_cs2_funcs);

	cpssp->port_mem_cs[3] = port_mem_cs3;
	sig_cs32_connect(port_mem_cs3, cpssp, &mem_cs3_funcs);

	cpssp->port_mem_cs[4] = port_mem_cs4;
	sig_cs32_connect(port_mem_cs4, cpssp, &mem_cs4_funcs);

	cpssp->port_mem_cs[5] = port_mem_cs5;
	sig_cs32_connect(port_mem_cs5, cpssp, &mem_cs5_funcs);

	cpssp->port_mem_cs[6] = port_mem_cs6;
	sig_cs32_connect(port_mem_cs6, cpssp, &mem_cs6_funcs);

	cpssp->port_mem_cs[7] = port_mem_cs7;
	sig_cs32_connect(port_mem_cs7, cpssp, &mem_cs7_funcs);

	cpssp->port_pci_bus = port_pci_bus;
	sig_pci_bus_connect(port_pci_bus, cpssp, &pci_bus_funcs);

	cpssp->port_agp_bus = port_agp_bus;
	/* FIXME */

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

	sig_std_logic_connect_in(port_reset_hash_, cpssp, &reset_hash__funcs);

	return cpssp;
}

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

	shm_free(cpssp);
}

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

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

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

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