]>
Commit | Line | Data |
---|---|---|
16020011 CLG |
1 | /* |
2 | * ARM Aspeed I2C controller | |
3 | * | |
4 | * Copyright (C) 2016 IBM Corp. | |
5 | * | |
6 | * This program is free software; you can redistribute it and/or | |
7 | * modify it under the terms of the GNU General Public License | |
8 | * as published by the Free Software Foundation; either version 2 | |
9 | * of the License, or (at your option) any later version. | |
10 | * | |
11 | * This program is distributed in the hope that it will be useful, | |
12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | |
13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
14 | * GNU General Public License for more details. | |
15 | * | |
16 | * You should have received a copy of the GNU General Public License | |
17 | * along with this program; if not, see <http://www.gnu.org/licenses/>. | |
18 | * | |
19 | */ | |
20 | ||
21 | #include "qemu/osdep.h" | |
22 | #include "hw/sysbus.h" | |
23 | #include "qemu/log.h" | |
0b8fa32f | 24 | #include "qemu/module.h" |
16020011 CLG |
25 | #include "hw/i2c/aspeed_i2c.h" |
26 | ||
27 | /* I2C Global Register */ | |
28 | ||
29 | #define I2C_CTRL_STATUS 0x00 /* Device Interrupt Status */ | |
30 | #define I2C_CTRL_ASSIGN 0x08 /* Device Interrupt Target | |
31 | Assignment */ | |
32 | ||
33 | /* I2C Device (Bus) Register */ | |
34 | ||
35 | #define I2CD_FUN_CTRL_REG 0x00 /* I2CD Function Control */ | |
36 | #define I2CD_BUFF_SEL_MASK (0x7 << 20) | |
37 | #define I2CD_BUFF_SEL(x) (x << 20) | |
38 | #define I2CD_M_SDA_LOCK_EN (0x1 << 16) | |
39 | #define I2CD_MULTI_MASTER_DIS (0x1 << 15) | |
40 | #define I2CD_M_SCL_DRIVE_EN (0x1 << 14) | |
41 | #define I2CD_MSB_STS (0x1 << 9) | |
42 | #define I2CD_SDA_DRIVE_1T_EN (0x1 << 8) | |
43 | #define I2CD_M_SDA_DRIVE_1T_EN (0x1 << 7) | |
44 | #define I2CD_M_HIGH_SPEED_EN (0x1 << 6) | |
45 | #define I2CD_DEF_ADDR_EN (0x1 << 5) | |
46 | #define I2CD_DEF_ALERT_EN (0x1 << 4) | |
47 | #define I2CD_DEF_ARP_EN (0x1 << 3) | |
48 | #define I2CD_DEF_GCALL_EN (0x1 << 2) | |
49 | #define I2CD_SLAVE_EN (0x1 << 1) | |
50 | #define I2CD_MASTER_EN (0x1) | |
51 | ||
52 | #define I2CD_AC_TIMING_REG1 0x04 /* Clock and AC Timing Control #1 */ | |
53 | #define I2CD_AC_TIMING_REG2 0x08 /* Clock and AC Timing Control #1 */ | |
54 | #define I2CD_INTR_CTRL_REG 0x0c /* I2CD Interrupt Control */ | |
55 | #define I2CD_INTR_STS_REG 0x10 /* I2CD Interrupt Status */ | |
5540cb97 CLG |
56 | |
57 | #define I2CD_INTR_SLAVE_ADDR_MATCH (0x1 << 31) /* 0: addr1 1: addr2 */ | |
58 | #define I2CD_INTR_SLAVE_ADDR_RX_PENDING (0x1 << 30) | |
59 | /* bits[19-16] Reserved */ | |
60 | ||
61 | /* All bits below are cleared by writing 1 */ | |
62 | #define I2CD_INTR_SLAVE_INACTIVE_TIMEOUT (0x1 << 15) | |
16020011 CLG |
63 | #define I2CD_INTR_SDA_DL_TIMEOUT (0x1 << 14) |
64 | #define I2CD_INTR_BUS_RECOVER_DONE (0x1 << 13) | |
65 | #define I2CD_INTR_SMBUS_ALERT (0x1 << 12) /* Bus [0-3] only */ | |
66 | #define I2CD_INTR_SMBUS_ARP_ADDR (0x1 << 11) /* Removed */ | |
67 | #define I2CD_INTR_SMBUS_DEV_ALERT_ADDR (0x1 << 10) /* Removed */ | |
68 | #define I2CD_INTR_SMBUS_DEF_ADDR (0x1 << 9) /* Removed */ | |
69 | #define I2CD_INTR_GCALL_ADDR (0x1 << 8) /* Removed */ | |
5540cb97 | 70 | #define I2CD_INTR_SLAVE_ADDR_RX_MATCH (0x1 << 7) /* use RX_DONE */ |
16020011 CLG |
71 | #define I2CD_INTR_SCL_TIMEOUT (0x1 << 6) |
72 | #define I2CD_INTR_ABNORMAL (0x1 << 5) | |
73 | #define I2CD_INTR_NORMAL_STOP (0x1 << 4) | |
74 | #define I2CD_INTR_ARBIT_LOSS (0x1 << 3) | |
75 | #define I2CD_INTR_RX_DONE (0x1 << 2) | |
76 | #define I2CD_INTR_TX_NAK (0x1 << 1) | |
77 | #define I2CD_INTR_TX_ACK (0x1 << 0) | |
78 | ||
79 | #define I2CD_CMD_REG 0x14 /* I2CD Command/Status */ | |
80 | #define I2CD_SDA_OE (0x1 << 28) | |
81 | #define I2CD_SDA_O (0x1 << 27) | |
82 | #define I2CD_SCL_OE (0x1 << 26) | |
83 | #define I2CD_SCL_O (0x1 << 25) | |
84 | #define I2CD_TX_TIMING (0x1 << 24) | |
85 | #define I2CD_TX_STATUS (0x1 << 23) | |
86 | ||
87 | #define I2CD_TX_STATE_SHIFT 19 /* Tx State Machine */ | |
88 | #define I2CD_TX_STATE_MASK 0xf | |
89 | #define I2CD_IDLE 0x0 | |
90 | #define I2CD_MACTIVE 0x8 | |
91 | #define I2CD_MSTART 0x9 | |
92 | #define I2CD_MSTARTR 0xa | |
93 | #define I2CD_MSTOP 0xb | |
94 | #define I2CD_MTXD 0xc | |
95 | #define I2CD_MRXACK 0xd | |
96 | #define I2CD_MRXD 0xe | |
97 | #define I2CD_MTXACK 0xf | |
98 | #define I2CD_SWAIT 0x1 | |
99 | #define I2CD_SRXD 0x4 | |
100 | #define I2CD_STXACK 0x5 | |
101 | #define I2CD_STXD 0x6 | |
102 | #define I2CD_SRXACK 0x7 | |
103 | #define I2CD_RECOVER 0x3 | |
104 | ||
105 | #define I2CD_SCL_LINE_STS (0x1 << 18) | |
106 | #define I2CD_SDA_LINE_STS (0x1 << 17) | |
107 | #define I2CD_BUS_BUSY_STS (0x1 << 16) | |
108 | #define I2CD_SDA_OE_OUT_DIR (0x1 << 15) | |
109 | #define I2CD_SDA_O_OUT_DIR (0x1 << 14) | |
110 | #define I2CD_SCL_OE_OUT_DIR (0x1 << 13) | |
111 | #define I2CD_SCL_O_OUT_DIR (0x1 << 12) | |
112 | #define I2CD_BUS_RECOVER_CMD_EN (0x1 << 11) | |
113 | #define I2CD_S_ALT_EN (0x1 << 10) | |
114 | #define I2CD_RX_DMA_ENABLE (0x1 << 9) | |
115 | #define I2CD_TX_DMA_ENABLE (0x1 << 8) | |
116 | ||
117 | /* Command Bit */ | |
118 | #define I2CD_M_STOP_CMD (0x1 << 5) | |
119 | #define I2CD_M_S_RX_CMD_LAST (0x1 << 4) | |
120 | #define I2CD_M_RX_CMD (0x1 << 3) | |
121 | #define I2CD_S_TX_CMD (0x1 << 2) | |
122 | #define I2CD_M_TX_CMD (0x1 << 1) | |
123 | #define I2CD_M_START_CMD (0x1) | |
124 | ||
125 | #define I2CD_DEV_ADDR_REG 0x18 /* Slave Device Address */ | |
126 | #define I2CD_BUF_CTRL_REG 0x1c /* Pool Buffer Control */ | |
127 | #define I2CD_BYTE_BUF_REG 0x20 /* Transmit/Receive Byte Buffer */ | |
128 | #define I2CD_BYTE_BUF_TX_SHIFT 0 | |
129 | #define I2CD_BYTE_BUF_TX_MASK 0xff | |
130 | #define I2CD_BYTE_BUF_RX_SHIFT 8 | |
131 | #define I2CD_BYTE_BUF_RX_MASK 0xff | |
132 | ||
133 | ||
134 | static inline bool aspeed_i2c_bus_is_master(AspeedI2CBus *bus) | |
135 | { | |
136 | return bus->ctrl & I2CD_MASTER_EN; | |
137 | } | |
138 | ||
139 | static inline bool aspeed_i2c_bus_is_enabled(AspeedI2CBus *bus) | |
140 | { | |
141 | return bus->ctrl & (I2CD_MASTER_EN | I2CD_SLAVE_EN); | |
142 | } | |
143 | ||
144 | static inline void aspeed_i2c_bus_raise_interrupt(AspeedI2CBus *bus) | |
145 | { | |
146 | bus->intr_status &= bus->intr_ctrl; | |
147 | if (bus->intr_status) { | |
148 | bus->controller->intr_status |= 1 << bus->id; | |
149 | qemu_irq_raise(bus->controller->irq); | |
150 | } | |
151 | } | |
152 | ||
153 | static uint64_t aspeed_i2c_bus_read(void *opaque, hwaddr offset, | |
154 | unsigned size) | |
155 | { | |
156 | AspeedI2CBus *bus = opaque; | |
157 | ||
158 | switch (offset) { | |
159 | case I2CD_FUN_CTRL_REG: | |
160 | return bus->ctrl; | |
161 | case I2CD_AC_TIMING_REG1: | |
162 | return bus->timing[0]; | |
163 | case I2CD_AC_TIMING_REG2: | |
164 | return bus->timing[1]; | |
165 | case I2CD_INTR_CTRL_REG: | |
166 | return bus->intr_ctrl; | |
167 | case I2CD_INTR_STS_REG: | |
168 | return bus->intr_status; | |
169 | case I2CD_BYTE_BUF_REG: | |
170 | return bus->buf; | |
171 | case I2CD_CMD_REG: | |
172 | return bus->cmd | (i2c_bus_busy(bus->bus) << 16); | |
173 | default: | |
174 | qemu_log_mask(LOG_GUEST_ERROR, | |
175 | "%s: Bad offset 0x%" HWADDR_PRIx "\n", __func__, offset); | |
176 | return -1; | |
177 | } | |
178 | } | |
179 | ||
4960f084 CLG |
180 | static void aspeed_i2c_set_state(AspeedI2CBus *bus, uint8_t state) |
181 | { | |
182 | bus->cmd &= ~(I2CD_TX_STATE_MASK << I2CD_TX_STATE_SHIFT); | |
183 | bus->cmd |= (state & I2CD_TX_STATE_MASK) << I2CD_TX_STATE_SHIFT; | |
184 | } | |
185 | ||
186 | static uint8_t aspeed_i2c_get_state(AspeedI2CBus *bus) | |
187 | { | |
188 | return (bus->cmd >> I2CD_TX_STATE_SHIFT) & I2CD_TX_STATE_MASK; | |
189 | } | |
190 | ||
7bd9c60d GR |
191 | static void aspeed_i2c_handle_rx_cmd(AspeedI2CBus *bus) |
192 | { | |
bc15cde0 | 193 | uint8_t ret; |
7bd9c60d GR |
194 | |
195 | aspeed_i2c_set_state(bus, I2CD_MRXD); | |
196 | ret = i2c_recv(bus->bus); | |
bc15cde0 | 197 | bus->intr_status |= I2CD_INTR_RX_DONE; |
7bd9c60d GR |
198 | bus->buf = (ret & I2CD_BYTE_BUF_RX_MASK) << I2CD_BYTE_BUF_RX_SHIFT; |
199 | if (bus->cmd & I2CD_M_S_RX_CMD_LAST) { | |
200 | i2c_nack(bus->bus); | |
201 | } | |
202 | bus->cmd &= ~(I2CD_M_RX_CMD | I2CD_M_S_RX_CMD_LAST); | |
203 | aspeed_i2c_set_state(bus, I2CD_MACTIVE); | |
204 | } | |
205 | ||
4960f084 CLG |
206 | /* |
207 | * The state machine needs some refinement. It is only used to track | |
208 | * invalid STOP commands for the moment. | |
209 | */ | |
16020011 CLG |
210 | static void aspeed_i2c_bus_handle_cmd(AspeedI2CBus *bus, uint64_t value) |
211 | { | |
ddabca75 | 212 | bus->cmd &= ~0xFFFF; |
16020011 | 213 | bus->cmd |= value & 0xFFFF; |
16020011 CLG |
214 | |
215 | if (bus->cmd & I2CD_M_START_CMD) { | |
4960f084 CLG |
216 | uint8_t state = aspeed_i2c_get_state(bus) & I2CD_MACTIVE ? |
217 | I2CD_MSTARTR : I2CD_MSTART; | |
218 | ||
219 | aspeed_i2c_set_state(bus, state); | |
220 | ||
16020011 CLG |
221 | if (i2c_start_transfer(bus->bus, extract32(bus->buf, 1, 7), |
222 | extract32(bus->buf, 0, 1))) { | |
223 | bus->intr_status |= I2CD_INTR_TX_NAK; | |
224 | } else { | |
225 | bus->intr_status |= I2CD_INTR_TX_ACK; | |
226 | } | |
227 | ||
ddabca75 CLG |
228 | /* START command is also a TX command, as the slave address is |
229 | * sent on the bus */ | |
230 | bus->cmd &= ~(I2CD_M_START_CMD | I2CD_M_TX_CMD); | |
231 | ||
232 | /* No slave found */ | |
233 | if (!i2c_bus_busy(bus->bus)) { | |
234 | return; | |
235 | } | |
4960f084 | 236 | aspeed_i2c_set_state(bus, I2CD_MACTIVE); |
ddabca75 CLG |
237 | } |
238 | ||
239 | if (bus->cmd & I2CD_M_TX_CMD) { | |
4960f084 | 240 | aspeed_i2c_set_state(bus, I2CD_MTXD); |
16020011 | 241 | if (i2c_send(bus->bus, bus->buf)) { |
4960f084 | 242 | bus->intr_status |= (I2CD_INTR_TX_NAK); |
16020011 CLG |
243 | i2c_end_transfer(bus->bus); |
244 | } else { | |
245 | bus->intr_status |= I2CD_INTR_TX_ACK; | |
246 | } | |
ddabca75 | 247 | bus->cmd &= ~I2CD_M_TX_CMD; |
4960f084 | 248 | aspeed_i2c_set_state(bus, I2CD_MACTIVE); |
ddabca75 | 249 | } |
16020011 | 250 | |
bb626e5b GR |
251 | if ((bus->cmd & (I2CD_M_RX_CMD | I2CD_M_S_RX_CMD_LAST)) && |
252 | !(bus->intr_status & I2CD_INTR_RX_DONE)) { | |
7bd9c60d | 253 | aspeed_i2c_handle_rx_cmd(bus); |
16020011 CLG |
254 | } |
255 | ||
d0efdc16 | 256 | if (bus->cmd & I2CD_M_STOP_CMD) { |
4960f084 CLG |
257 | if (!(aspeed_i2c_get_state(bus) & I2CD_MACTIVE)) { |
258 | qemu_log_mask(LOG_GUEST_ERROR, "%s: abnormal stop\n", __func__); | |
16020011 CLG |
259 | bus->intr_status |= I2CD_INTR_ABNORMAL; |
260 | } else { | |
4960f084 | 261 | aspeed_i2c_set_state(bus, I2CD_MSTOP); |
16020011 CLG |
262 | i2c_end_transfer(bus->bus); |
263 | bus->intr_status |= I2CD_INTR_NORMAL_STOP; | |
264 | } | |
ddabca75 | 265 | bus->cmd &= ~I2CD_M_STOP_CMD; |
4960f084 | 266 | aspeed_i2c_set_state(bus, I2CD_IDLE); |
16020011 | 267 | } |
16020011 CLG |
268 | } |
269 | ||
270 | static void aspeed_i2c_bus_write(void *opaque, hwaddr offset, | |
271 | uint64_t value, unsigned size) | |
272 | { | |
273 | AspeedI2CBus *bus = opaque; | |
bb626e5b | 274 | bool handle_rx; |
16020011 CLG |
275 | |
276 | switch (offset) { | |
277 | case I2CD_FUN_CTRL_REG: | |
278 | if (value & I2CD_SLAVE_EN) { | |
279 | qemu_log_mask(LOG_UNIMP, "%s: slave mode not implemented\n", | |
280 | __func__); | |
281 | break; | |
282 | } | |
283 | bus->ctrl = value & 0x0071C3FF; | |
284 | break; | |
285 | case I2CD_AC_TIMING_REG1: | |
286 | bus->timing[0] = value & 0xFFFFF0F; | |
287 | break; | |
288 | case I2CD_AC_TIMING_REG2: | |
289 | bus->timing[1] = value & 0x7; | |
290 | break; | |
291 | case I2CD_INTR_CTRL_REG: | |
292 | bus->intr_ctrl = value & 0x7FFF; | |
293 | break; | |
294 | case I2CD_INTR_STS_REG: | |
bb626e5b GR |
295 | handle_rx = (bus->intr_status & I2CD_INTR_RX_DONE) && |
296 | (value & I2CD_INTR_RX_DONE); | |
16020011 | 297 | bus->intr_status &= ~(value & 0x7FFF); |
5540cb97 CLG |
298 | if (!bus->intr_status) { |
299 | bus->controller->intr_status &= ~(1 << bus->id); | |
300 | qemu_irq_lower(bus->controller->irq); | |
301 | } | |
bb626e5b GR |
302 | if (handle_rx && (bus->cmd & (I2CD_M_RX_CMD | I2CD_M_S_RX_CMD_LAST))) { |
303 | aspeed_i2c_handle_rx_cmd(bus); | |
304 | aspeed_i2c_bus_raise_interrupt(bus); | |
305 | } | |
16020011 CLG |
306 | break; |
307 | case I2CD_DEV_ADDR_REG: | |
308 | qemu_log_mask(LOG_UNIMP, "%s: slave mode not implemented\n", | |
309 | __func__); | |
310 | break; | |
311 | case I2CD_BYTE_BUF_REG: | |
312 | bus->buf = (value & I2CD_BYTE_BUF_TX_MASK) << I2CD_BYTE_BUF_TX_SHIFT; | |
313 | break; | |
314 | case I2CD_CMD_REG: | |
315 | if (!aspeed_i2c_bus_is_enabled(bus)) { | |
316 | break; | |
317 | } | |
318 | ||
319 | if (!aspeed_i2c_bus_is_master(bus)) { | |
320 | qemu_log_mask(LOG_UNIMP, "%s: slave mode not implemented\n", | |
321 | __func__); | |
322 | break; | |
323 | } | |
324 | ||
325 | aspeed_i2c_bus_handle_cmd(bus, value); | |
ddabca75 | 326 | aspeed_i2c_bus_raise_interrupt(bus); |
16020011 CLG |
327 | break; |
328 | ||
329 | default: | |
330 | qemu_log_mask(LOG_GUEST_ERROR, "%s: Bad offset 0x%" HWADDR_PRIx "\n", | |
331 | __func__, offset); | |
332 | } | |
333 | } | |
334 | ||
335 | static uint64_t aspeed_i2c_ctrl_read(void *opaque, hwaddr offset, | |
336 | unsigned size) | |
337 | { | |
338 | AspeedI2CState *s = opaque; | |
339 | ||
340 | switch (offset) { | |
341 | case I2C_CTRL_STATUS: | |
342 | return s->intr_status; | |
343 | default: | |
344 | qemu_log_mask(LOG_GUEST_ERROR, "%s: Bad offset 0x%" HWADDR_PRIx "\n", | |
345 | __func__, offset); | |
346 | break; | |
347 | } | |
348 | ||
349 | return -1; | |
350 | } | |
351 | ||
352 | static void aspeed_i2c_ctrl_write(void *opaque, hwaddr offset, | |
353 | uint64_t value, unsigned size) | |
354 | { | |
355 | switch (offset) { | |
356 | case I2C_CTRL_STATUS: | |
357 | default: | |
358 | qemu_log_mask(LOG_GUEST_ERROR, "%s: Bad offset 0x%" HWADDR_PRIx "\n", | |
359 | __func__, offset); | |
360 | break; | |
361 | } | |
362 | } | |
363 | ||
364 | static const MemoryRegionOps aspeed_i2c_bus_ops = { | |
365 | .read = aspeed_i2c_bus_read, | |
366 | .write = aspeed_i2c_bus_write, | |
367 | .endianness = DEVICE_LITTLE_ENDIAN, | |
368 | }; | |
369 | ||
370 | static const MemoryRegionOps aspeed_i2c_ctrl_ops = { | |
371 | .read = aspeed_i2c_ctrl_read, | |
372 | .write = aspeed_i2c_ctrl_write, | |
373 | .endianness = DEVICE_LITTLE_ENDIAN, | |
374 | }; | |
375 | ||
376 | static const VMStateDescription aspeed_i2c_bus_vmstate = { | |
377 | .name = TYPE_ASPEED_I2C, | |
378 | .version_id = 1, | |
379 | .minimum_version_id = 1, | |
380 | .fields = (VMStateField[]) { | |
381 | VMSTATE_UINT8(id, AspeedI2CBus), | |
382 | VMSTATE_UINT32(ctrl, AspeedI2CBus), | |
383 | VMSTATE_UINT32_ARRAY(timing, AspeedI2CBus, 2), | |
384 | VMSTATE_UINT32(intr_ctrl, AspeedI2CBus), | |
385 | VMSTATE_UINT32(intr_status, AspeedI2CBus), | |
386 | VMSTATE_UINT32(cmd, AspeedI2CBus), | |
387 | VMSTATE_UINT32(buf, AspeedI2CBus), | |
388 | VMSTATE_END_OF_LIST() | |
389 | } | |
390 | }; | |
391 | ||
392 | static const VMStateDescription aspeed_i2c_vmstate = { | |
393 | .name = TYPE_ASPEED_I2C, | |
394 | .version_id = 1, | |
395 | .minimum_version_id = 1, | |
396 | .fields = (VMStateField[]) { | |
397 | VMSTATE_UINT32(intr_status, AspeedI2CState), | |
398 | VMSTATE_STRUCT_ARRAY(busses, AspeedI2CState, | |
399 | ASPEED_I2C_NR_BUSSES, 1, aspeed_i2c_bus_vmstate, | |
400 | AspeedI2CBus), | |
401 | VMSTATE_END_OF_LIST() | |
402 | } | |
403 | }; | |
404 | ||
405 | static void aspeed_i2c_reset(DeviceState *dev) | |
406 | { | |
407 | int i; | |
408 | AspeedI2CState *s = ASPEED_I2C(dev); | |
409 | ||
410 | s->intr_status = 0; | |
411 | ||
412 | for (i = 0; i < ASPEED_I2C_NR_BUSSES; i++) { | |
413 | s->busses[i].intr_ctrl = 0; | |
414 | s->busses[i].intr_status = 0; | |
415 | s->busses[i].cmd = 0; | |
416 | s->busses[i].buf = 0; | |
417 | i2c_end_transfer(s->busses[i].bus); | |
418 | } | |
419 | } | |
420 | ||
421 | /* | |
422 | * Address Definitions | |
423 | * | |
424 | * 0x000 ... 0x03F: Global Register | |
425 | * 0x040 ... 0x07F: Device 1 | |
426 | * 0x080 ... 0x0BF: Device 2 | |
427 | * 0x0C0 ... 0x0FF: Device 3 | |
428 | * 0x100 ... 0x13F: Device 4 | |
429 | * 0x140 ... 0x17F: Device 5 | |
430 | * 0x180 ... 0x1BF: Device 6 | |
431 | * 0x1C0 ... 0x1FF: Device 7 | |
432 | * 0x200 ... 0x2FF: Buffer Pool (unused in linux driver) | |
433 | * 0x300 ... 0x33F: Device 8 | |
434 | * 0x340 ... 0x37F: Device 9 | |
435 | * 0x380 ... 0x3BF: Device 10 | |
436 | * 0x3C0 ... 0x3FF: Device 11 | |
437 | * 0x400 ... 0x43F: Device 12 | |
438 | * 0x440 ... 0x47F: Device 13 | |
439 | * 0x480 ... 0x4BF: Device 14 | |
440 | * 0x800 ... 0xFFF: Buffer Pool (unused in linux driver) | |
441 | */ | |
442 | static void aspeed_i2c_realize(DeviceState *dev, Error **errp) | |
443 | { | |
444 | int i; | |
445 | SysBusDevice *sbd = SYS_BUS_DEVICE(dev); | |
446 | AspeedI2CState *s = ASPEED_I2C(dev); | |
447 | ||
448 | sysbus_init_irq(sbd, &s->irq); | |
449 | memory_region_init_io(&s->iomem, OBJECT(s), &aspeed_i2c_ctrl_ops, s, | |
450 | "aspeed.i2c", 0x1000); | |
451 | sysbus_init_mmio(sbd, &s->iomem); | |
452 | ||
453 | for (i = 0; i < ASPEED_I2C_NR_BUSSES; i++) { | |
454 | char name[16]; | |
455 | int offset = i < 7 ? 1 : 5; | |
456 | snprintf(name, sizeof(name), "aspeed.i2c.%d", i); | |
457 | s->busses[i].controller = s; | |
458 | s->busses[i].id = i; | |
459 | s->busses[i].bus = i2c_init_bus(dev, name); | |
460 | memory_region_init_io(&s->busses[i].mr, OBJECT(dev), | |
461 | &aspeed_i2c_bus_ops, &s->busses[i], name, 0x40); | |
462 | memory_region_add_subregion(&s->iomem, 0x40 * (i + offset), | |
463 | &s->busses[i].mr); | |
464 | } | |
465 | } | |
466 | ||
467 | static void aspeed_i2c_class_init(ObjectClass *klass, void *data) | |
468 | { | |
469 | DeviceClass *dc = DEVICE_CLASS(klass); | |
470 | ||
471 | dc->vmsd = &aspeed_i2c_vmstate; | |
472 | dc->reset = aspeed_i2c_reset; | |
473 | dc->realize = aspeed_i2c_realize; | |
474 | dc->desc = "Aspeed I2C Controller"; | |
475 | } | |
476 | ||
477 | static const TypeInfo aspeed_i2c_info = { | |
478 | .name = TYPE_ASPEED_I2C, | |
479 | .parent = TYPE_SYS_BUS_DEVICE, | |
480 | .instance_size = sizeof(AspeedI2CState), | |
481 | .class_init = aspeed_i2c_class_init, | |
482 | }; | |
483 | ||
484 | static void aspeed_i2c_register_types(void) | |
485 | { | |
486 | type_register_static(&aspeed_i2c_info); | |
487 | } | |
488 | ||
489 | type_init(aspeed_i2c_register_types) | |
490 | ||
491 | ||
492 | I2CBus *aspeed_i2c_get_bus(DeviceState *dev, int busnr) | |
493 | { | |
494 | AspeedI2CState *s = ASPEED_I2C(dev); | |
495 | I2CBus *bus = NULL; | |
496 | ||
497 | if (busnr >= 0 && busnr < ASPEED_I2C_NR_BUSSES) { | |
498 | bus = s->busses[busnr].bus; | |
499 | } | |
500 | ||
501 | return bus; | |
502 | } |