summaryrefslogtreecommitdiffstatshomepage
path: root/src/devices/machine/spg290_i2c.cpp
blob: 41eaaf584374655681091cbf1d94c05de008bb43 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
// license:BSD-3-Clause
// copyright-holders:Sandro Ronco
/*****************************************************************************

        SPG290 I2C

*****************************************************************************/

#include "emu.h"
#include "spg290_i2c.h"


DEFINE_DEVICE_TYPE(SPG290_I2C, spg290_i2c_device, "spg290_i2c", "SPG290 I2C")


spg290_i2c_device::spg290_i2c_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock)
	: device_t(mconfig, SPG290_I2C, tag, owner, clock)
	, m_irq_cb(*this)
	, m_i2c_read_cb(*this, 0)
	, m_i2c_write_cb(*this)
{
}

void spg290_i2c_device::device_start()
{
	m_i2c_timer = timer_alloc(FUNC(spg290_i2c_device::i2c_update), this);

	save_item(NAME(m_config));
	save_item(NAME(m_irq_control));
	save_item(NAME(m_clock_conf));
	save_item(NAME(m_id));
	save_item(NAME(m_port_addr));
	save_item(NAME(m_wdata));
	save_item(NAME(m_rdata));
}

void spg290_i2c_device::device_reset()
{
	m_config = 0;
	m_irq_control = 0;
	m_clock_conf = 0;
	m_id = 0;
	m_port_addr = 0;
	m_wdata = 0;
	m_rdata = 0;

	m_i2c_timer->adjust(attotime::never);

	m_irq_cb(CLEAR_LINE);
}

TIMER_CALLBACK_MEMBER(spg290_i2c_device::i2c_update)
{
	if (m_config & 0x40)
		m_rdata = m_i2c_read_cb(m_port_addr);
	else
		m_i2c_write_cb(m_port_addr, m_wdata);

	// I2C ack
	m_config |= (m_config & 7) << 3;

	// I2C IRQ
	if (m_irq_control & 0x02)
	{
		m_irq_control |= 0x01;
		m_irq_cb(ASSERT_LINE);
	}
}

uint32_t spg290_i2c_device::read(offs_t offset, uint32_t mem_mask)
{
	switch (offset << 2)
	{
	case 0x20:      // I2C configuration
		return m_config;
	case 0x24:      // Interrupt status
		return m_irq_control;
	case 0x28:      // Clock setting
		return m_clock_conf;
	case 0x2c:      // ID
		return m_id;
	case 0x30:      // Port address
		return m_port_addr;
	case 0x34:      // Write data
		return m_wdata;
	case 0x38:      // Read data
		return m_rdata;
	default:
		logerror("[%s] %s: unknown read %x\n", tag(), machine().describe_context(), offset << 2);
	}

	return 0;
}

void spg290_i2c_device::write(offs_t offset, uint32_t data, uint32_t mem_mask)
{
	switch (offset << 2)
	{
	case 0x20:      // I2C configuration
	{
		COMBINE_DATA(&m_config);

		const auto i2c_clk = attotime::from_hz(clock() / 4 / ((m_clock_conf & 0x3ff) + 1));

		if (m_config & 0x0001)       // Start 8
			m_i2c_timer->adjust(i2c_clk * (2 + 9 * 4));
		else if (m_config & 0x0002)  // Start 16
			m_i2c_timer->adjust(i2c_clk * (2 + 9 * 5));
		else if (m_config & 0x0004)  // Start N
			m_i2c_timer->adjust(i2c_clk * (2 + 9 * 4), 0, i2c_clk * (2 + 9 * 4));
		else if (m_config & 0x0100)  // Stop N
			m_i2c_timer->adjust(attotime::never);

		break;
	}
	case 0x24:      // Interrupt status
		COMBINE_DATA(&m_irq_control);

		if (ACCESSING_BITS_0_7)
			m_irq_control &= ~(data & 0x01);  // IRQ ack

		if (!(m_irq_control & 0x01))
			m_irq_cb(CLEAR_LINE);

		break;
	case 0x28:      // Clock setting
		COMBINE_DATA(&m_clock_conf);
		break;
	case 0x2c:      // ID
		COMBINE_DATA(&m_id);
		break;
	case 0x30:      // Port address
		COMBINE_DATA(&m_port_addr);
		break;
	case 0x34:      // Write data
		COMBINE_DATA(&m_wdata);
		break;
	case 0x38:      // Read data
		COMBINE_DATA(&m_rdata);
		break;
	default:
		logerror("[%s] %s: unknown write %x = %x\n", tag(), machine().describe_context(), offset << 2, data);
	}
}