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
145
146
147
|
// 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, const XTAL &clock)
: device_t(mconfig, SPG290_I2C, tag, owner, clock)
, m_irq_cb(*this)
, m_i2c_read_cb(*this)
, m_i2c_write_cb(*this)
{
}
void spg290_i2c_device::device_start()
{
m_irq_cb.resolve_safe();
m_i2c_read_cb.resolve_safe(0);
m_i2c_write_cb.resolve_safe();
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);
}
}
|