]>
Commit | Line | Data |
---|---|---|
65ca801b BZ |
1 | /* |
2 | * PPC4xx I2C controller emulation | |
3 | * | |
4 | * Copyright (c) 2007 Jocelyn Mayer | |
7709dbf1 | 5 | * Copyright (c) 2012 François Revol |
39aeba6c | 6 | * Copyright (c) 2016-2018 BALATON Zoltan |
65ca801b BZ |
7 | * |
8 | * Permission is hereby granted, free of charge, to any person obtaining a copy | |
9 | * of this software and associated documentation files (the "Software"), to deal | |
10 | * in the Software without restriction, including without limitation the rights | |
11 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | |
12 | * copies of the Software, and to permit persons to whom the Software is | |
13 | * furnished to do so, subject to the following conditions: | |
14 | * | |
15 | * The above copyright notice and this permission notice shall be included in | |
16 | * all copies or substantial portions of the Software. | |
17 | * | |
18 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | |
19 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | |
20 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL | |
21 | * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | |
22 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | |
23 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN | |
24 | * THE SOFTWARE. | |
25 | */ | |
26 | ||
27 | #include "qemu/osdep.h" | |
65ca801b | 28 | #include "qemu-common.h" |
7709dbf1 | 29 | #include "qemu/log.h" |
65ca801b BZ |
30 | #include "cpu.h" |
31 | #include "hw/hw.h" | |
3b09bb0f | 32 | #include "hw/i2c/ppc4xx_i2c.h" |
ef9173a5 | 33 | #include "bitbang_i2c.h" |
65ca801b | 34 | |
42a907e8 | 35 | #define PPC4xx_I2C_MEM_SIZE 18 |
65ca801b | 36 | |
afb6e204 BZ |
37 | enum { |
38 | IIC_MDBUF = 0, | |
39 | /* IIC_SDBUF = 2, */ | |
40 | IIC_LMADR = 4, | |
41 | IIC_HMADR, | |
42 | IIC_CNTL, | |
43 | IIC_MDCNTL, | |
44 | IIC_STS, | |
45 | IIC_EXTSTS, | |
46 | IIC_LSADR, | |
47 | IIC_HSADR, | |
48 | IIC_CLKDIV, | |
49 | IIC_INTRMSK, | |
50 | IIC_XFRCNT, | |
51 | IIC_XTCNTLSS, | |
52 | IIC_DIRECTCNTL | |
53 | /* IIC_INTR */ | |
54 | }; | |
55 | ||
7709dbf1 BZ |
56 | #define IIC_CNTL_PT (1 << 0) |
57 | #define IIC_CNTL_READ (1 << 1) | |
58 | #define IIC_CNTL_CHT (1 << 2) | |
59 | #define IIC_CNTL_RPST (1 << 3) | |
afb6e204 BZ |
60 | #define IIC_CNTL_AMD (1 << 6) |
61 | #define IIC_CNTL_HMT (1 << 7) | |
62 | ||
63 | #define IIC_MDCNTL_EINT (1 << 2) | |
64 | #define IIC_MDCNTL_ESM (1 << 3) | |
65 | #define IIC_MDCNTL_FMDB (1 << 6) | |
7709dbf1 BZ |
66 | |
67 | #define IIC_STS_PT (1 << 0) | |
afb6e204 | 68 | #define IIC_STS_IRQA (1 << 1) |
7709dbf1 | 69 | #define IIC_STS_ERR (1 << 2) |
afb6e204 | 70 | #define IIC_STS_MDBF (1 << 4) |
7709dbf1 BZ |
71 | #define IIC_STS_MDBS (1 << 5) |
72 | ||
73 | #define IIC_EXTSTS_XFRA (1 << 0) | |
afb6e204 BZ |
74 | #define IIC_EXTSTS_BCS_FREE (4 << 4) |
75 | #define IIC_EXTSTS_BCS_BUSY (5 << 4) | |
76 | ||
77 | #define IIC_INTRMSK_EIMTC (1 << 0) | |
78 | #define IIC_INTRMSK_EITA (1 << 1) | |
79 | #define IIC_INTRMSK_EIIC (1 << 2) | |
80 | #define IIC_INTRMSK_EIHE (1 << 3) | |
7709dbf1 BZ |
81 | |
82 | #define IIC_XTCNTLSS_SRST (1 << 0) | |
83 | ||
ef9173a5 BZ |
84 | #define IIC_DIRECTCNTL_SDAC (1 << 3) |
85 | #define IIC_DIRECTCNTL_SCLC (1 << 2) | |
86 | #define IIC_DIRECTCNTL_MSDA (1 << 1) | |
87 | #define IIC_DIRECTCNTL_MSCL (1 << 0) | |
88 | ||
7709dbf1 BZ |
89 | static void ppc4xx_i2c_reset(DeviceState *s) |
90 | { | |
91 | PPC4xxI2CState *i2c = PPC4xx_I2C(s); | |
92 | ||
afb6e204 BZ |
93 | i2c->mdidx = -1; |
94 | memset(i2c->mdata, 0, ARRAY_SIZE(i2c->mdata)); | |
95 | /* [hl][ms]addr are not affected by reset */ | |
7709dbf1 BZ |
96 | i2c->cntl = 0; |
97 | i2c->mdcntl = 0; | |
98 | i2c->sts = 0; | |
afb6e204 | 99 | i2c->extsts = IIC_EXTSTS_BCS_FREE; |
7709dbf1 BZ |
100 | i2c->clkdiv = 0; |
101 | i2c->intrmsk = 0; | |
102 | i2c->xfrcnt = 0; | |
103 | i2c->xtcntlss = 0; | |
afb6e204 | 104 | i2c->directcntl = 0xf; /* all non-reserved bits set */ |
7709dbf1 | 105 | } |
65ca801b | 106 | |
3b09bb0f | 107 | static uint64_t ppc4xx_i2c_readb(void *opaque, hwaddr addr, unsigned int size) |
65ca801b | 108 | { |
3b09bb0f BZ |
109 | PPC4xxI2CState *i2c = PPC4xx_I2C(opaque); |
110 | uint64_t ret; | |
afb6e204 | 111 | int i; |
65ca801b | 112 | |
65ca801b | 113 | switch (addr) { |
afb6e204 BZ |
114 | case IIC_MDBUF: |
115 | if (i2c->mdidx < 0) { | |
7709dbf1 | 116 | ret = 0xff; |
afb6e204 BZ |
117 | break; |
118 | } | |
119 | ret = i2c->mdata[0]; | |
120 | if (i2c->mdidx == 3) { | |
121 | i2c->sts &= ~IIC_STS_MDBF; | |
122 | } else if (i2c->mdidx == 0) { | |
123 | i2c->sts &= ~IIC_STS_MDBS; | |
124 | } | |
125 | for (i = 0; i < i2c->mdidx; i++) { | |
126 | i2c->mdata[i] = i2c->mdata[i + 1]; | |
127 | } | |
128 | if (i2c->mdidx >= 0) { | |
129 | i2c->mdidx--; | |
7709dbf1 | 130 | } |
65ca801b | 131 | break; |
afb6e204 | 132 | case IIC_LMADR: |
65ca801b BZ |
133 | ret = i2c->lmadr; |
134 | break; | |
afb6e204 | 135 | case IIC_HMADR: |
65ca801b BZ |
136 | ret = i2c->hmadr; |
137 | break; | |
afb6e204 | 138 | case IIC_CNTL: |
65ca801b BZ |
139 | ret = i2c->cntl; |
140 | break; | |
afb6e204 | 141 | case IIC_MDCNTL: |
65ca801b BZ |
142 | ret = i2c->mdcntl; |
143 | break; | |
afb6e204 | 144 | case IIC_STS: |
65ca801b BZ |
145 | ret = i2c->sts; |
146 | break; | |
afb6e204 BZ |
147 | case IIC_EXTSTS: |
148 | ret = i2c_bus_busy(i2c->bus) ? | |
149 | IIC_EXTSTS_BCS_BUSY : IIC_EXTSTS_BCS_FREE; | |
65ca801b | 150 | break; |
afb6e204 | 151 | case IIC_LSADR: |
65ca801b BZ |
152 | ret = i2c->lsadr; |
153 | break; | |
afb6e204 | 154 | case IIC_HSADR: |
65ca801b BZ |
155 | ret = i2c->hsadr; |
156 | break; | |
afb6e204 | 157 | case IIC_CLKDIV: |
65ca801b BZ |
158 | ret = i2c->clkdiv; |
159 | break; | |
afb6e204 | 160 | case IIC_INTRMSK: |
65ca801b BZ |
161 | ret = i2c->intrmsk; |
162 | break; | |
afb6e204 | 163 | case IIC_XFRCNT: |
65ca801b BZ |
164 | ret = i2c->xfrcnt; |
165 | break; | |
afb6e204 | 166 | case IIC_XTCNTLSS: |
65ca801b BZ |
167 | ret = i2c->xtcntlss; |
168 | break; | |
afb6e204 | 169 | case IIC_DIRECTCNTL: |
65ca801b BZ |
170 | ret = i2c->directcntl; |
171 | break; | |
172 | default: | |
42a907e8 BZ |
173 | if (addr < PPC4xx_I2C_MEM_SIZE) { |
174 | qemu_log_mask(LOG_UNIMP, "%s: Unimplemented register 0x%" | |
175 | HWADDR_PRIx "\n", __func__, addr); | |
176 | } else { | |
177 | qemu_log_mask(LOG_GUEST_ERROR, "%s: Bad address 0x%" | |
178 | HWADDR_PRIx "\n", __func__, addr); | |
179 | } | |
7709dbf1 | 180 | ret = 0; |
65ca801b BZ |
181 | break; |
182 | } | |
65ca801b BZ |
183 | return ret; |
184 | } | |
185 | ||
3b09bb0f BZ |
186 | static void ppc4xx_i2c_writeb(void *opaque, hwaddr addr, uint64_t value, |
187 | unsigned int size) | |
65ca801b | 188 | { |
3b09bb0f | 189 | PPC4xxI2CState *i2c = opaque; |
7709dbf1 | 190 | |
65ca801b | 191 | switch (addr) { |
afb6e204 BZ |
192 | case IIC_MDBUF: |
193 | if (i2c->mdidx >= 3) { | |
194 | break; | |
7709dbf1 | 195 | } |
afb6e204 BZ |
196 | i2c->mdata[++i2c->mdidx] = value; |
197 | if (i2c->mdidx == 3) { | |
198 | i2c->sts |= IIC_STS_MDBF; | |
199 | } else if (i2c->mdidx == 0) { | |
200 | i2c->sts |= IIC_STS_MDBS; | |
7709dbf1 | 201 | } |
65ca801b | 202 | break; |
afb6e204 | 203 | case IIC_LMADR: |
65ca801b BZ |
204 | i2c->lmadr = value; |
205 | break; | |
afb6e204 | 206 | case IIC_HMADR: |
65ca801b BZ |
207 | i2c->hmadr = value; |
208 | break; | |
afb6e204 BZ |
209 | case IIC_CNTL: |
210 | i2c->cntl = value & ~IIC_CNTL_PT; | |
211 | if (value & IIC_CNTL_AMD) { | |
212 | qemu_log_mask(LOG_UNIMP, "%s: only 7 bit addresses supported\n", | |
213 | __func__); | |
214 | } | |
215 | if (value & IIC_CNTL_HMT && i2c_bus_busy(i2c->bus)) { | |
216 | i2c_end_transfer(i2c->bus); | |
217 | if (i2c->mdcntl & IIC_MDCNTL_EINT && | |
218 | i2c->intrmsk & IIC_INTRMSK_EIHE) { | |
219 | i2c->sts |= IIC_STS_IRQA; | |
220 | qemu_irq_raise(i2c->irq); | |
221 | } | |
222 | } else if (value & IIC_CNTL_PT) { | |
223 | int recv = (value & IIC_CNTL_READ) >> 1; | |
224 | int tct = value >> 4 & 3; | |
225 | int i; | |
226 | ||
227 | if (recv && (i2c->lmadr >> 1) >= 0x50 && (i2c->lmadr >> 1) < 0x58) { | |
228 | /* smbus emulation does not like multi byte reads w/o restart */ | |
229 | value |= IIC_CNTL_RPST; | |
230 | } | |
231 | ||
232 | for (i = 0; i <= tct; i++) { | |
233 | if (!i2c_bus_busy(i2c->bus)) { | |
234 | i2c->extsts = IIC_EXTSTS_BCS_FREE; | |
235 | if (i2c_start_transfer(i2c->bus, i2c->lmadr >> 1, recv)) { | |
236 | i2c->sts |= IIC_STS_ERR; | |
237 | i2c->extsts |= IIC_EXTSTS_XFRA; | |
238 | break; | |
239 | } else { | |
240 | i2c->sts &= ~IIC_STS_ERR; | |
241 | } | |
7709dbf1 | 242 | } |
afb6e204 BZ |
243 | if (!(i2c->sts & IIC_STS_ERR) && |
244 | i2c_send_recv(i2c->bus, &i2c->mdata[i], !recv)) { | |
7709dbf1 BZ |
245 | i2c->sts |= IIC_STS_ERR; |
246 | i2c->extsts |= IIC_EXTSTS_XFRA; | |
afb6e204 BZ |
247 | break; |
248 | } | |
249 | if (value & IIC_CNTL_RPST || !(value & IIC_CNTL_CHT)) { | |
250 | i2c_end_transfer(i2c->bus); | |
7709dbf1 | 251 | } |
afb6e204 BZ |
252 | } |
253 | i2c->xfrcnt = i; | |
254 | i2c->mdidx = i - 1; | |
255 | if (recv && i2c->mdidx >= 0) { | |
256 | i2c->sts |= IIC_STS_MDBS; | |
257 | } | |
258 | if (recv && i2c->mdidx == 3) { | |
259 | i2c->sts |= IIC_STS_MDBF; | |
260 | } | |
261 | if (i && i2c->mdcntl & IIC_MDCNTL_EINT && | |
262 | i2c->intrmsk & IIC_INTRMSK_EIMTC) { | |
263 | i2c->sts |= IIC_STS_IRQA; | |
264 | qemu_irq_raise(i2c->irq); | |
7709dbf1 BZ |
265 | } |
266 | } | |
65ca801b | 267 | break; |
afb6e204 BZ |
268 | case IIC_MDCNTL: |
269 | i2c->mdcntl = value & 0x3d; | |
270 | if (value & IIC_MDCNTL_ESM) { | |
271 | qemu_log_mask(LOG_UNIMP, "%s: slave mode not implemented\n", | |
272 | __func__); | |
273 | } | |
274 | if (value & IIC_MDCNTL_FMDB) { | |
275 | i2c->mdidx = -1; | |
276 | memset(i2c->mdata, 0, ARRAY_SIZE(i2c->mdata)); | |
277 | i2c->sts &= ~(IIC_STS_MDBF | IIC_STS_MDBS); | |
278 | } | |
65ca801b | 279 | break; |
afb6e204 BZ |
280 | case IIC_STS: |
281 | i2c->sts &= ~(value & 0x0a); | |
282 | if (value & IIC_STS_IRQA && i2c->mdcntl & IIC_MDCNTL_EINT) { | |
283 | qemu_irq_lower(i2c->irq); | |
284 | } | |
65ca801b | 285 | break; |
afb6e204 | 286 | case IIC_EXTSTS: |
42a907e8 | 287 | i2c->extsts &= ~(value & 0x8f); |
65ca801b | 288 | break; |
afb6e204 | 289 | case IIC_LSADR: |
65ca801b BZ |
290 | i2c->lsadr = value; |
291 | break; | |
afb6e204 | 292 | case IIC_HSADR: |
65ca801b BZ |
293 | i2c->hsadr = value; |
294 | break; | |
afb6e204 | 295 | case IIC_CLKDIV: |
65ca801b BZ |
296 | i2c->clkdiv = value; |
297 | break; | |
afb6e204 | 298 | case IIC_INTRMSK: |
65ca801b BZ |
299 | i2c->intrmsk = value; |
300 | break; | |
afb6e204 | 301 | case IIC_XFRCNT: |
65ca801b BZ |
302 | i2c->xfrcnt = value & 0x77; |
303 | break; | |
afb6e204 BZ |
304 | case IIC_XTCNTLSS: |
305 | i2c->xtcntlss &= ~(value & 0xf0); | |
7709dbf1 BZ |
306 | if (value & IIC_XTCNTLSS_SRST) { |
307 | /* Is it actually a full reset? U-Boot sets some regs before */ | |
308 | ppc4xx_i2c_reset(DEVICE(i2c)); | |
309 | break; | |
310 | } | |
65ca801b | 311 | break; |
afb6e204 | 312 | case IIC_DIRECTCNTL: |
ef9173a5 BZ |
313 | i2c->directcntl = value & (IIC_DIRECTCNTL_SDAC & IIC_DIRECTCNTL_SCLC); |
314 | i2c->directcntl |= (value & IIC_DIRECTCNTL_SCLC ? 1 : 0); | |
315 | bitbang_i2c_set(i2c->bitbang, BITBANG_I2C_SCL, | |
316 | i2c->directcntl & IIC_DIRECTCNTL_MSCL); | |
317 | i2c->directcntl |= bitbang_i2c_set(i2c->bitbang, BITBANG_I2C_SDA, | |
318 | (value & IIC_DIRECTCNTL_SDAC) != 0) << 1; | |
65ca801b | 319 | break; |
7709dbf1 | 320 | default: |
42a907e8 BZ |
321 | if (addr < PPC4xx_I2C_MEM_SIZE) { |
322 | qemu_log_mask(LOG_UNIMP, "%s: Unimplemented register 0x%" | |
323 | HWADDR_PRIx "\n", __func__, addr); | |
324 | } else { | |
325 | qemu_log_mask(LOG_GUEST_ERROR, "%s: Bad address 0x%" | |
326 | HWADDR_PRIx "\n", __func__, addr); | |
327 | } | |
7709dbf1 | 328 | break; |
65ca801b BZ |
329 | } |
330 | } | |
331 | ||
3b09bb0f BZ |
332 | static const MemoryRegionOps ppc4xx_i2c_ops = { |
333 | .read = ppc4xx_i2c_readb, | |
334 | .write = ppc4xx_i2c_writeb, | |
335 | .valid.min_access_size = 1, | |
336 | .valid.max_access_size = 4, | |
337 | .impl.min_access_size = 1, | |
338 | .impl.max_access_size = 1, | |
65ca801b BZ |
339 | .endianness = DEVICE_NATIVE_ENDIAN, |
340 | }; | |
341 | ||
3b09bb0f | 342 | static void ppc4xx_i2c_init(Object *o) |
65ca801b | 343 | { |
3b09bb0f | 344 | PPC4xxI2CState *s = PPC4xx_I2C(o); |
65ca801b | 345 | |
3b09bb0f BZ |
346 | memory_region_init_io(&s->iomem, OBJECT(s), &ppc4xx_i2c_ops, s, |
347 | TYPE_PPC4xx_I2C, PPC4xx_I2C_MEM_SIZE); | |
348 | sysbus_init_mmio(SYS_BUS_DEVICE(s), &s->iomem); | |
349 | sysbus_init_irq(SYS_BUS_DEVICE(s), &s->irq); | |
350 | s->bus = i2c_init_bus(DEVICE(s), "i2c"); | |
ef9173a5 | 351 | s->bitbang = bitbang_i2c_init(s->bus); |
65ca801b | 352 | } |
3b09bb0f BZ |
353 | |
354 | static void ppc4xx_i2c_class_init(ObjectClass *klass, void *data) | |
355 | { | |
356 | DeviceClass *dc = DEVICE_CLASS(klass); | |
357 | ||
358 | dc->reset = ppc4xx_i2c_reset; | |
359 | } | |
360 | ||
361 | static const TypeInfo ppc4xx_i2c_type_info = { | |
362 | .name = TYPE_PPC4xx_I2C, | |
363 | .parent = TYPE_SYS_BUS_DEVICE, | |
364 | .instance_size = sizeof(PPC4xxI2CState), | |
365 | .instance_init = ppc4xx_i2c_init, | |
366 | .class_init = ppc4xx_i2c_class_init, | |
367 | }; | |
368 | ||
369 | static void ppc4xx_i2c_register_types(void) | |
370 | { | |
371 | type_register_static(&ppc4xx_i2c_type_info); | |
372 | } | |
373 | ||
374 | type_init(ppc4xx_i2c_register_types) |