ausmbus_psc.c revision 1.17 1 /* $NetBSD: ausmbus_psc.c,v 1.17 2025/09/15 13:23:02 thorpej Exp $ */
2
3 /*-
4 * Copyright (c) 2006 Shigeyuki Fukushima.
5 * All rights reserved.
6 *
7 * Written by Shigeyuki Fukushima.
8 *
9 * Redistribution and use in source and binary forms, with or without
10 * modification, are permitted provided that the following conditions
11 * are met:
12 * 1. Redistributions of source code must retain the above copyright
13 * notice, this list of conditions and the following disclaimer.
14 * 2. Redistributions in binary form must reproduce the above
15 * copyright notice, this list of conditions and the following
16 * disclaimer in the documentation and/or other materials provided
17 * with the distribution.
18 * 3. The name of the author may not be used to endorse or promote
19 * products derived from this software without specific prior
20 * written permission.
21 *
22 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS
23 * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
24 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
25 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
26 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
27 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
28 * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
29 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
30 * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
31 * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
32 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
33 */
34
35 #include <sys/cdefs.h>
36 __KERNEL_RCSID(0, "$NetBSD: ausmbus_psc.c,v 1.17 2025/09/15 13:23:02 thorpej Exp $");
37
38 #include "locators.h"
39
40 #include <sys/param.h>
41 #include <sys/systm.h>
42 #include <sys/device.h>
43 #include <sys/errno.h>
44
45 #include <sys/bus.h>
46 #include <machine/cpu.h>
47
48 #include <mips/alchemy/dev/aupscreg.h>
49 #include <mips/alchemy/dev/aupscvar.h>
50 #include <mips/alchemy/dev/ausmbus_pscreg.h>
51
52 #include <dev/i2c/i2cvar.h>
53 #include <dev/i2c/i2c_bitbang.h>
54
55 struct ausmbus_softc {
56 device_t sc_dev;
57
58 /* protocol comoon fields */
59 struct aupsc_controller sc_ctrl;
60
61 /* protocol specific fields */
62 struct i2c_controller sc_i2c;
63 i2c_addr_t sc_smbus_slave_addr;
64 int sc_smbus_timeout;
65 };
66
67 #define ausmbus_reg_read(sc, reg) \
68 bus_space_read_4(sc->sc_ctrl.psc_bust, sc->sc_ctrl.psc_bush, reg)
69 #define ausmbus_reg_write(sc, reg, val) \
70 bus_space_write_4(sc->sc_ctrl.psc_bust, sc->sc_ctrl.psc_bush, reg, \
71 val); \
72 delay(100);
73
74 static int ausmbus_match(device_t, struct cfdata *, void *);
75 static void ausmbus_attach(device_t, device_t, void *);
76
77 CFATTACH_DECL_NEW(ausmbus, sizeof(struct ausmbus_softc),
78 ausmbus_match, ausmbus_attach, NULL, NULL);
79
80 /* functions for i2c_controller */
81 static int ausmbus_acquire_bus(void *, int);
82 static void ausmbus_release_bus(void *, int);
83 static int ausmbus_exec(void *cookie, i2c_op_t op, i2c_addr_t addr,
84 const void *cmd, size_t cmdlen, void *vbuf,
85 size_t buflen, int flags);
86
87 /* subroutine functions for i2c_controller */
88 static int ausmbus_quick_write(struct ausmbus_softc *);
89 static int ausmbus_quick_read(struct ausmbus_softc *);
90 static int ausmbus_receive_1(struct ausmbus_softc *, uint8_t *);
91 static int ausmbus_read_1(struct ausmbus_softc *, uint8_t, uint8_t *);
92 static int ausmbus_read_2(struct ausmbus_softc *, uint8_t, uint16_t *);
93 static int ausmbus_send_1(struct ausmbus_softc *, uint8_t);
94 static int ausmbus_write_1(struct ausmbus_softc *, uint8_t, uint8_t);
95 static int ausmbus_write_2(struct ausmbus_softc *, uint8_t, uint16_t);
96 static int ausmbus_wait_mastertx(struct ausmbus_softc *sc);
97 static int ausmbus_wait_masterrx(struct ausmbus_softc *sc);
98 static int ausmbus_initiate_xfer(void *, i2c_addr_t, int);
99 static int ausmbus_read_byte(void *arg, uint8_t *vp, int flags);
100 static int ausmbus_write_byte(void *arg, uint8_t v, int flags);
101
102
103 static int
104 ausmbus_match(device_t parent, struct cfdata *cf, void *aux)
105 {
106 struct aupsc_attach_args *aa = (struct aupsc_attach_args *)aux;
107
108 if (strcmp(aa->aupsc_name, cf->cf_name) != 0)
109 return 0;
110
111 return 1;
112 }
113
114 static void
115 ausmbus_attach(device_t parent, device_t self, void *aux)
116 {
117 struct ausmbus_softc *sc = device_private(self);
118 struct aupsc_attach_args *aa = (struct aupsc_attach_args *)aux;
119
120 aprint_normal(": Alchemy PSC SMBus protocol\n");
121
122 sc->sc_dev = self;
123
124 /* Initialize PSC */
125 sc->sc_ctrl = aa->aupsc_ctrl;
126
127 /* Initialize i2c_controller for SMBus */
128 iic_tag_init(&sc->sc_i2c);
129 sc->sc_i2c.ic_cookie = sc;
130 sc->sc_i2c.ic_acquire_bus = ausmbus_acquire_bus;
131 sc->sc_i2c.ic_release_bus = ausmbus_release_bus;
132 sc->sc_i2c.ic_exec = ausmbus_exec;
133 sc->sc_smbus_timeout = 10;
134
135 iicbus_attach(self, &sc->sc_i2c);
136 }
137
138 static int
139 ausmbus_acquire_bus(void *arg, int flags)
140 {
141 struct ausmbus_softc *sc = arg;
142 uint32_t v;
143
144 /* Select SMBus Protocol & Enable PSC */
145 sc->sc_ctrl.psc_enable(sc, AUPSC_SEL_SMBUS);
146 v = ausmbus_reg_read(sc, AUPSC_SMBSTAT);
147 if ((v & SMBUS_STAT_SR) == 0) {
148 /* PSC is not ready */
149 return -1;
150 }
151
152 /* Setup SMBus Configuration register */
153 v = SMBUS_CFG_DD; /* Disable DMA */
154 v |= SMBUS_CFG_RT_SET(SMBUS_CFG_RT_FIFO8); /* Rx FIFO 8data */
155 v |= SMBUS_CFG_TT_SET(SMBUS_CFG_TT_FIFO8); /* Tx FIFO 8data */
156 v |= SMBUS_CFG_DIV_SET(SMBUS_CFG_DIV8); /* pscn_mainclk/8 */
157 v &= ~SMBUS_CFG_SFM; /* Standard Mode */
158 ausmbus_reg_write(sc, AUPSC_SMBCFG, v);
159
160 /* Setup SMBus Protocol Timing register */
161 v = SMBUS_TMR_TH_SET(SMBUS_TMR_STD_TH)
162 | SMBUS_TMR_PS_SET(SMBUS_TMR_STD_PS)
163 | SMBUS_TMR_PU_SET(SMBUS_TMR_STD_PU)
164 | SMBUS_TMR_SH_SET(SMBUS_TMR_STD_SH)
165 | SMBUS_TMR_SU_SET(SMBUS_TMR_STD_SU)
166 | SMBUS_TMR_CL_SET(SMBUS_TMR_STD_CL)
167 | SMBUS_TMR_CH_SET(SMBUS_TMR_STD_CH);
168 ausmbus_reg_write(sc, AUPSC_SMBTMR, v);
169
170 /* Setup SMBus Mask register */
171 v = SMBUS_MSK_ALLMASK;
172 ausmbus_reg_write(sc, AUPSC_SMBMSK, v);
173
174 /* SMBus Enable */
175 v = ausmbus_reg_read(sc, AUPSC_SMBCFG);
176 v |= SMBUS_CFG_DE;
177 ausmbus_reg_write(sc, AUPSC_SMBCFG, v);
178 v = ausmbus_reg_read(sc, AUPSC_SMBSTAT);
179 if ((v & SMBUS_STAT_SR) == 0) {
180 /* SMBus is not ready */
181 return -1;
182 }
183
184 #ifdef AUSMBUS_PSC_DEBUG
185 aprint_normal("AuSMBus enabled.\n");
186 aprint_normal("AuSMBus smbconfig: 0x%08x\n",
187 ausmbus_reg_read(sc, AUPSC_SMBCFG));
188 aprint_normal("AuSMBus smbstatus: 0x%08x\n",
189 ausmbus_reg_read(sc, AUPSC_SMBSTAT));
190 aprint_normal("AuSMBus smbtmr : 0x%08x\n",
191 ausmbus_reg_read(sc, AUPSC_SMBTMR));
192 aprint_normal("AuSMBus smbmask : 0x%08x\n",
193 ausmbus_reg_read(sc, AUPSC_SMBMSK));
194 #endif
195
196 return 0;
197 }
198
199 static void
200 ausmbus_release_bus(void *arg, int flags)
201 {
202 struct ausmbus_softc *sc = arg;
203
204 ausmbus_reg_write(sc, AUPSC_SMBCFG, 0);
205 sc->sc_ctrl.psc_disable(sc);
206
207 return;
208 }
209
210 static int
211 ausmbus_exec(void *cookie, i2c_op_t op, i2c_addr_t addr, const void *vcmd,
212 size_t cmdlen, void *vbuf, size_t buflen, int flags)
213 {
214 struct ausmbus_softc *sc = (struct ausmbus_softc *)cookie;
215 const uint8_t *cmd = vcmd;
216
217 sc->sc_smbus_slave_addr = addr;
218
219 /* Receive byte */
220 if ((I2C_OP_READ_P(op)) && (cmdlen == 0) && (buflen == 1)) {
221 return ausmbus_receive_1(sc, (uint8_t *)vbuf);
222 }
223
224 /* Read byte */
225 if ((I2C_OP_READ_P(op)) && (cmdlen == 1) && (buflen == 1)) {
226 return ausmbus_read_1(sc, *cmd, (uint8_t *)vbuf);
227 }
228
229 /* Read word */
230 if ((I2C_OP_READ_P(op)) && (cmdlen == 1) && (buflen == 2)) {
231 return ausmbus_read_2(sc, *cmd, (uint16_t *)vbuf);
232 }
233
234 /* Read quick */
235 if ((I2C_OP_READ_P(op)) && (cmdlen == 0) && (buflen == 0)) {
236 return ausmbus_quick_read(sc);
237 }
238
239 /* Send byte */
240 if ((I2C_OP_WRITE_P(op)) && (cmdlen == 0) && (buflen == 1)) {
241 return ausmbus_send_1(sc, *((uint8_t *)vbuf));
242 }
243
244 /* Write byte */
245 if ((I2C_OP_WRITE_P(op)) && (cmdlen == 1) && (buflen == 1)) {
246 return ausmbus_write_1(sc, *cmd, *((uint8_t *)vbuf));
247 }
248
249 /* Write word */
250 if ((I2C_OP_WRITE_P(op)) && (cmdlen == 1) && (buflen == 2)) {
251 return ausmbus_write_2(sc, *cmd, *((uint16_t *)vbuf));
252 }
253
254 /* Write quick */
255 if ((I2C_OP_WRITE_P(op)) && (cmdlen == 0) && (buflen == 0)) {
256 return ausmbus_quick_write(sc);
257 }
258
259 /*
260 * XXX: TODO Please Support other protocols defined in SMBus 2.0
261 * - Process call
262 * - Block write/read
263 * - Clock write-block read process cal
264 * - SMBus host notify protocol
265 *
266 * - Read quick and write quick have not been tested!
267 */
268
269 return -1;
270 }
271
272 static int
273 ausmbus_receive_1(struct ausmbus_softc *sc, uint8_t *vp)
274 {
275 int error;
276
277 error = ausmbus_initiate_xfer(sc, sc->sc_smbus_slave_addr, I2C_F_READ);
278 if (error != 0) {
279 return error;
280 }
281 error = ausmbus_read_byte(sc, vp, I2C_F_STOP);
282 if (error != 0) {
283 return error;
284 }
285
286 return 0;
287 }
288
289 static int
290 ausmbus_read_1(struct ausmbus_softc *sc, uint8_t cmd, uint8_t *vp)
291 {
292 int error;
293
294 error = ausmbus_initiate_xfer(sc, sc->sc_smbus_slave_addr, I2C_F_WRITE);
295 if (error != 0) {
296 return error;
297 }
298
299 error = ausmbus_write_byte(sc, cmd, I2C_F_READ);
300 if (error != 0) {
301 return error;
302 }
303
304 error = ausmbus_initiate_xfer(sc, sc->sc_smbus_slave_addr, I2C_F_READ);
305 if (error != 0) {
306 return error;
307 }
308
309 error = ausmbus_read_byte(sc, vp, I2C_F_STOP);
310 if (error != 0) {
311 return error;
312 }
313
314 return 0;
315 }
316
317 static int
318 ausmbus_read_2(struct ausmbus_softc *sc, uint8_t cmd, uint16_t *vp)
319 {
320 int error;
321 uint8_t high, low;
322
323 error = ausmbus_initiate_xfer(sc, sc->sc_smbus_slave_addr, I2C_F_WRITE);
324 if (error != 0) {
325 return error;
326 }
327
328 error = ausmbus_write_byte(sc, cmd, I2C_F_READ);
329 if (error != 0) {
330 return error;
331 }
332
333 error = ausmbus_initiate_xfer(sc, sc->sc_smbus_slave_addr, I2C_F_READ);
334 if (error != 0) {
335 return error;
336 }
337
338 error = ausmbus_read_byte(sc, &low, 0);
339 if (error != 0) {
340 return error;
341 }
342
343 error = ausmbus_read_byte(sc, &high, I2C_F_STOP);
344 if (error != 0) {
345 return error;
346 }
347
348 *vp = (high << 8) | low;
349
350 return 0;
351 }
352
353 static int
354 ausmbus_send_1(struct ausmbus_softc *sc, uint8_t val)
355 {
356 int error;
357
358 error = ausmbus_initiate_xfer(sc, sc->sc_smbus_slave_addr, I2C_F_WRITE);
359 if (error != 0) {
360 return error;
361 }
362
363 error = ausmbus_write_byte(sc, val, I2C_F_STOP);
364 if (error != 0) {
365 return error;
366 }
367
368 return 0;
369 }
370
371 static int
372 ausmbus_write_1(struct ausmbus_softc *sc, uint8_t cmd, uint8_t val)
373 {
374 int error;
375
376 error = ausmbus_initiate_xfer(sc, sc->sc_smbus_slave_addr, I2C_F_WRITE);
377 if (error != 0) {
378 return error;
379 }
380
381 error = ausmbus_write_byte(sc, cmd, 0);
382 if (error != 0) {
383 return error;
384 }
385
386 error = ausmbus_write_byte(sc, val, I2C_F_STOP);
387 if (error != 0) {
388 return error;
389 }
390
391 return 0;
392 }
393
394 static int
395 ausmbus_write_2(struct ausmbus_softc *sc, uint8_t cmd, uint16_t val)
396 {
397 int error;
398 uint8_t high, low;
399
400 high = (val >> 8) & 0xff;
401 low = val & 0xff;
402
403 error = ausmbus_initiate_xfer(sc, sc->sc_smbus_slave_addr, I2C_F_WRITE);
404 if (error != 0) {
405 return error;
406 }
407
408 error = ausmbus_write_byte(sc, cmd, 0);
409 if (error != 0) {
410 return error;
411 }
412
413 error = ausmbus_write_byte(sc, low, 0);
414 if (error != 0) {
415 return error;
416 }
417
418 error = ausmbus_write_byte(sc, high, I2C_F_STOP);
419 if (error != 0) {
420 return error;
421 }
422
423 return 0;
424 }
425
426 /*
427 * XXX The quick_write() and quick_read() routines have not been tested!
428 */
429 static int
430 ausmbus_quick_write(struct ausmbus_softc *sc)
431 {
432 return ausmbus_initiate_xfer(sc, sc->sc_smbus_slave_addr,
433 I2C_F_STOP | I2C_F_WRITE);
434 }
435
436 static int
437 ausmbus_quick_read(struct ausmbus_softc *sc)
438 {
439 return ausmbus_initiate_xfer(sc, sc->sc_smbus_slave_addr,
440 I2C_F_STOP | I2C_F_READ);
441 }
442
443 static int
444 ausmbus_wait_mastertx(struct ausmbus_softc *sc)
445 {
446 uint32_t v;
447 int timeout;
448 int txerr = 0;
449
450 timeout = sc->sc_smbus_timeout;
451
452 do {
453 v = ausmbus_reg_read(sc, AUPSC_SMBEVNT);
454 #ifdef AUSMBUS_PSC_DEBUG
455 aprint_normal("AuSMBus: ausmbus_wait_mastertx(): psc_smbevnt=0x%08x\n", v);
456 #endif
457 if ((v & SMBUS_EVNT_TU) != 0)
458 break;
459 if ((v & SMBUS_EVNT_MD) != 0)
460 break;
461 if ((v & (SMBUS_EVNT_DN | SMBUS_EVNT_AN | SMBUS_EVNT_AL))
462 != 0) {
463 txerr = 1;
464 break;
465 }
466 timeout--;
467 delay(1);
468 } while (timeout > 0);
469
470 if (txerr != 0) {
471 ausmbus_reg_write(sc, AUPSC_SMBEVNT,
472 SMBUS_EVNT_DN | SMBUS_EVNT_AN | SMBUS_EVNT_AL);
473 #ifdef AUSMBUS_PSC_DEBUG
474 aprint_normal("AuSMBus: ausmbus_wait_mastertx(): Tx error\n");
475 #endif
476 return -1;
477 }
478
479 /* Reset Event TU (Tx Underflow) */
480 ausmbus_reg_write(sc, AUPSC_SMBEVNT, SMBUS_EVNT_TU | SMBUS_EVNT_MD);
481
482 #ifdef AUSMBUS_PSC_DEBUG
483 v = ausmbus_reg_read(sc, AUPSC_SMBEVNT);
484 aprint_normal("AuSMBus: ausmbus_wait_mastertx(): psc_smbevnt=0x%08x (reset)\n", v);
485 #endif
486 return 0;
487 }
488
489 static int
490 ausmbus_wait_masterrx(struct ausmbus_softc *sc)
491 {
492 uint32_t v;
493 int timeout;
494 timeout = sc->sc_smbus_timeout;
495
496 if (ausmbus_wait_mastertx(sc) != 0)
497 return -1;
498
499 do {
500 v = ausmbus_reg_read(sc, AUPSC_SMBSTAT);
501 #ifdef AUSMBUS_PSC_DEBUG
502 aprint_normal("AuSMBus: ausmbus_wait_masterrx(): psc_smbstat=0x%08x\n", v);
503 #endif
504 if ((v & SMBUS_STAT_RE) == 0)
505 break;
506 timeout--;
507 delay(1);
508 } while (timeout > 0);
509
510 return 0;
511 }
512
513 static int
514 ausmbus_initiate_xfer(void *arg, i2c_addr_t addr, int flags)
515 {
516 struct ausmbus_softc *sc = arg;
517 uint32_t v;
518
519 /* Tx/Rx Slave Address */
520 v = (addr << 1) & SMBUS_TXRX_ADDRDATA;
521 if ((flags & I2C_F_READ) != 0)
522 v |= 1;
523 if ((flags & I2C_F_STOP) != 0)
524 v |= SMBUS_TXRX_STP;
525 ausmbus_reg_write(sc, AUPSC_SMBTXRX, v);
526
527 /* Master Start */
528 ausmbus_reg_write(sc, AUPSC_SMBPCR, SMBUS_PCR_MS);
529
530 if (ausmbus_wait_mastertx(sc) != 0)
531 return -1;
532
533 return 0;
534 }
535
536 static int
537 ausmbus_read_byte(void *arg, uint8_t *vp, int flags)
538 {
539 struct ausmbus_softc *sc = arg;
540 uint32_t v;
541
542 if ((flags & I2C_F_STOP) != 0) {
543 ausmbus_reg_write(sc, AUPSC_SMBTXRX, SMBUS_TXRX_STP);
544 } else {
545 ausmbus_reg_write(sc, AUPSC_SMBTXRX, 0);
546 }
547
548 if (ausmbus_wait_masterrx(sc) != 0)
549 return -1;
550
551 v = ausmbus_reg_read(sc, AUPSC_SMBTXRX);
552 *vp = v & SMBUS_TXRX_ADDRDATA;
553
554 return 0;
555 }
556
557 static int
558 ausmbus_write_byte(void *arg, uint8_t v, int flags)
559 {
560 struct ausmbus_softc *sc = arg;
561
562 if ((flags & I2C_F_STOP) != 0) {
563 ausmbus_reg_write(sc, AUPSC_SMBTXRX, (v | SMBUS_TXRX_STP));
564 } else if ((flags & I2C_F_READ) != 0) {
565 ausmbus_reg_write(sc, AUPSC_SMBTXRX, (v | SMBUS_TXRX_RSR));
566 } else {
567 ausmbus_reg_write(sc, AUPSC_SMBTXRX, v);
568 }
569
570 if (ausmbus_wait_mastertx(sc) != 0)
571 return -1;
572
573 return 0;
574 }
575