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

#include "config.h"

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

#include "glue.h"

#define INCLUDE
#include "arch_rtc.c"
#undef INCLUDE

#include "chip_motorola_mc146818A.h"

struct cpssp {
	/*
	 * Config
	 */

	/*
	 * Signals
	 */
	struct sig_std_logic *port_int;

	/*
	 * State
	 */
	unsigned int state_power;

#define STATE

#define NAME            chip_motorola_mc146818A_rtc
#define NAME_(x)        chip_motorola_mc146818A_rtc_ ## x
#define SNAME           "chip_motorola_mc146818A_rtc"
#include "arch_rtc.c"
#undef SNAME
#undef NAME_
#undef NAME

#undef STATE
};

static void
chip_motorola_mc146818A_rtc_irq_set(struct cpssp *cpssp, unsigned int val)
{
	sig_std_logic_or_set(cpssp->port_int, cpssp, val);
}

#define BEHAVIOR

#define NAME            chip_motorola_mc146818A_rtc
#define NAME_(x)        chip_motorola_mc146818A_rtc_ ## x
#define SNAME           "chip_motorola_mc146818A_rtc"
#include "arch_rtc.c"
#undef SNAME
#undef NAME_
#undef NAME

#undef BEHAVIOR

static void
chip_motorola_mc146818A_power_set(void *_css, unsigned int val)
{
	struct cpssp *cpssp = (struct cpssp *) _css;

	cpssp->state_power = val;
}

static void
chip_motorola_mc146818A_n_reset_set(void *_css, unsigned int val)
{
	struct cpssp *cpssp = (struct cpssp *) _css;

	chip_motorola_mc146818A_rtc_reset(cpssp);
}

static int
chip_motorola_mc146818A_inb(void *_css, uint32_t port, uint8_t *valp)
{
	struct cpssp *cpssp = (struct cpssp *) _css;

	chip_motorola_mc146818A_rtc_inb(cpssp, valp, port & 1);

	return 0;
}

static int
chip_motorola_mc146818A_outb(void *_css, uint32_t port, uint8_t val)
{
	struct cpssp *cpssp = (struct cpssp *) _css;

	chip_motorola_mc146818A_rtc_outb(cpssp, val, port & 1);

	return 0;
}

void *
chip_motorola_mc146818A_create(
	const char *name,
	const char *rtc_start,
	const char *cmos,
	struct sig_manage *port_manage,
	struct sig_std_logic *port_power,
	struct sig_std_logic *port_reset_hash_,
	struct sig_cs *port_cs,
	struct sig_isa_bus *port_bus,
	struct sig_std_logic *port_int
)
{
	static const struct sig_std_logic_funcs power_funcs = {
		.boolean_or_set = chip_motorola_mc146818A_power_set,
	};
	static const struct sig_std_logic_funcs reset_hash__funcs = {
		.boolean_or_set = chip_motorola_mc146818A_n_reset_set,
	};
	static const struct sig_cs_funcs cs_funcs = {
		.readb = chip_motorola_mc146818A_inb,
		.writeb = chip_motorola_mc146818A_outb,
	};
	struct cpssp *cpssp;

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

	system_name_push(name);

	chip_motorola_mc146818A_rtc_create(cpssp, "rtc", rtc_start, cmos);
	chip_motorola_mc146818A_rtc_init(cpssp);

	/* Out */
	cpssp->port_int = port_int;

	/* Call */
	sig_cs_connect(port_cs, cpssp, &cs_funcs);

	/* In */
	cpssp->state_power = 0;
	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();

	return cpssp;
}

void
chip_motorola_mc146818A_destroy(void *_cpssp)
{
	struct cpssp *cpssp = _cpssp;

	chip_motorola_mc146818A_rtc_destroy(cpssp);

	shm_free(cpssp);
}

void
chip_motorola_mc146818A_suspend(void *_cpssp, FILE *fp)
{
	struct cpssp *cpssp = _cpssp;
	
	generic_suspend(cpssp, sizeof(*cpssp), fp);
}

void
chip_motorola_mc146818A_resume(void *_cpssp, FILE *fp)
{
	struct cpssp *cpssp = _cpssp;
	
	void *savemedia = cpssp->chip_motorola_mc146818A_rtc.media; 
	
	generic_resume(cpssp, sizeof(*cpssp), fp);
	
	cpssp->chip_motorola_mc146818A_rtc.media = savemedia;
}
