1*eb7eaf8dSmpi /* $OpenBSD: mgiic.c,v 1.4 2021/10/24 17:05:03 mpi Exp $ */
24438e468Sderaadt /*
3539dbbeaSderaadt * Copyright (c) 2008 Theo de Raadt <deraadt@openbsd.org>
44438e468Sderaadt *
54438e468Sderaadt * Permission to use, copy, modify, and distribute this software for any
64438e468Sderaadt * purpose with or without fee is hereby granted, provided that the above
74438e468Sderaadt * copyright notice and this permission notice appear in all copies.
84438e468Sderaadt *
94438e468Sderaadt * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
104438e468Sderaadt * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
114438e468Sderaadt * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
124438e468Sderaadt * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
134438e468Sderaadt * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
144438e468Sderaadt * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
154438e468Sderaadt * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
164438e468Sderaadt */
174438e468Sderaadt
184438e468Sderaadt #include <sys/param.h>
194438e468Sderaadt #include <sys/device.h>
204438e468Sderaadt #include <sys/errno.h>
214438e468Sderaadt #include <sys/malloc.h>
224438e468Sderaadt #include <sys/systm.h>
234438e468Sderaadt #include <sys/rwlock.h>
244438e468Sderaadt
254438e468Sderaadt #include <uvm/uvm_extern.h>
264438e468Sderaadt
274438e468Sderaadt #include <machine/bus.h>
284438e468Sderaadt #include <machine/autoconf.h>
294438e468Sderaadt #include <machine/openfirm.h>
304438e468Sderaadt
314438e468Sderaadt #include <dev/i2c/i2cvar.h>
324438e468Sderaadt #include <sparc64/dev/ofwi2cvar.h>
334438e468Sderaadt
34539dbbeaSderaadt #define MGSLAVEADDR 0x00
35539dbbeaSderaadt #define MGSLAVEXADDR 0x08
36539dbbeaSderaadt #define MGDATA 0x10
37539dbbeaSderaadt #define MGCONTROL 0x18
38539dbbeaSderaadt #define MGCONTROL_IEN 0x80
39539dbbeaSderaadt #define MGCONTROL_ENAB 0x40
40539dbbeaSderaadt #define MGCONTROL_STA 0x20
41539dbbeaSderaadt #define MGCONTROL_STP 0x10
42539dbbeaSderaadt #define MGCONTROL_IFLG 0x08
43539dbbeaSderaadt #define MGCONTROL_AAK 0x04
44539dbbeaSderaadt #define MGSTATUS 0x20
45539dbbeaSderaadt #define MGSTATUS_BUSERR 0x00
46539dbbeaSderaadt #define MGSTATUS_STARTSENT 0x08
47539dbbeaSderaadt #define MGSTATUS_REPEATSTART 0x10
48539dbbeaSderaadt #define MGSTATUS_ADDR_W_ACKR 0x18
49539dbbeaSderaadt #define MGSTATUS_ADDR_W_NOACKR 0x20
50539dbbeaSderaadt #define MGSTATUS_MDATA_ACKR 0x28
51539dbbeaSderaadt #define MGSTATUS_MDATA_NOACKR 0x30
52539dbbeaSderaadt #define MGSTATUS_ARBLOST 0x38
53539dbbeaSderaadt #define MGSTATUS_ADDR_R_ACKR 0x40
54539dbbeaSderaadt #define MGSTATUS_ADDR_R_NOACKR 0x48
55539dbbeaSderaadt #define MGSTATUS_MDATA_ACKT 0x50
56539dbbeaSderaadt #define MGSTATUS_MDATA_NOACKT 0x58
57539dbbeaSderaadt #define MGSTATUS_SADDR_W_ACKT 0x60
58539dbbeaSderaadt #define MGSTATUS_ARBLOST_SLW_ACKT 0x68
59539dbbeaSderaadt #define MGSTATUS_GC_TACK 0x70
60539dbbeaSderaadt #define MGSTATUS_ARBLOST_GC_ACKT 0x78
61539dbbeaSderaadt #define MGSTATUS_IDLE 0xf8
62539dbbeaSderaadt #define MGCLOCKCONTROL 0x28
63539dbbeaSderaadt #define MGSOFTRESET 0x30
644438e468Sderaadt
654438e468Sderaadt struct mgiic_softc {
664438e468Sderaadt struct device sc_dev;
674438e468Sderaadt
684438e468Sderaadt bus_space_tag_t sc_bt;
694438e468Sderaadt bus_space_handle_t sc_regh;
704438e468Sderaadt
714438e468Sderaadt
724438e468Sderaadt int sc_poll;
734438e468Sderaadt
744438e468Sderaadt struct i2c_controller sc_i2c;
754438e468Sderaadt struct rwlock sc_lock;
764438e468Sderaadt };
774438e468Sderaadt
784438e468Sderaadt int mgiic_match(struct device *, void *, void *);
794438e468Sderaadt void mgiic_attach(struct device *, struct device *, void *);
804438e468Sderaadt
814438e468Sderaadt struct cfdriver mgiic_cd = {
824438e468Sderaadt NULL, "mgiic", DV_DULL
834438e468Sderaadt };
844438e468Sderaadt
85*eb7eaf8dSmpi const struct cfattach mgiic_ca = {
864438e468Sderaadt sizeof(struct mgiic_softc), mgiic_match, mgiic_attach
874438e468Sderaadt };
884438e468Sderaadt
894438e468Sderaadt int mgiic_i2c_acquire_bus(void *, int);
904438e468Sderaadt void mgiic_i2c_release_bus(void *, int);
914438e468Sderaadt int mgiic_i2c_exec(void *, i2c_op_t, i2c_addr_t, const void *,
924438e468Sderaadt size_t, void *, size_t, int);
934438e468Sderaadt
94539dbbeaSderaadt int mgiic_xmit(struct mgiic_softc *, u_int8_t, const u_int8_t *,
95539dbbeaSderaadt size_t);
964438e468Sderaadt int mgiic_recv(struct mgiic_softc *, u_int8_t, u_int8_t *, size_t);
974438e468Sderaadt volatile u_int8_t mgiic_read(struct mgiic_softc *, bus_size_t);
984438e468Sderaadt volatile void mgiic_write(struct mgiic_softc *, bus_size_t, u_int8_t);
994438e468Sderaadt volatile void mgiic_control(struct mgiic_softc *, u_int8_t, u_int8_t);
1004438e468Sderaadt int mgiic_poll(struct mgiic_softc *);
1014438e468Sderaadt
1024438e468Sderaadt int
mgiic_match(struct device * parent,void * match,void * aux)1034438e468Sderaadt mgiic_match(struct device *parent, void *match, void *aux)
1044438e468Sderaadt {
1054438e468Sderaadt struct mainbus_attach_args *ma = aux;
1064438e468Sderaadt char compat[32];
1074438e468Sderaadt
1084438e468Sderaadt if (strcmp(ma->ma_name, "i2c") != 0)
1094438e468Sderaadt return (0);
1104438e468Sderaadt if (OF_getprop(ma->ma_node, "compatible", compat, sizeof(compat)) == -1)
1114438e468Sderaadt return (0);
1124438e468Sderaadt if (strcmp(compat, "fire-i2c") == 0)
1134438e468Sderaadt return (1);
1144438e468Sderaadt return (0);
1154438e468Sderaadt }
1164438e468Sderaadt
1174438e468Sderaadt void
mgiic_attach(struct device * parent,struct device * self,void * aux)1184438e468Sderaadt mgiic_attach(struct device *parent, struct device *self, void *aux)
1194438e468Sderaadt {
1204438e468Sderaadt struct mgiic_softc *sc = (struct mgiic_softc *)self;
1214438e468Sderaadt struct mainbus_attach_args *ma = aux;
1224438e468Sderaadt struct i2cbus_attach_args iba;
1234438e468Sderaadt
1244438e468Sderaadt sc->sc_bt = ma->ma_bustag;
1254438e468Sderaadt
1264438e468Sderaadt if (bus_space_map(sc->sc_bt, ma->ma_reg[0].ur_paddr,
1274438e468Sderaadt ma->ma_reg[0].ur_len, 0, &sc->sc_regh)) {
1284438e468Sderaadt printf(": failed to map preg\n");
1294438e468Sderaadt return;
1304438e468Sderaadt }
1314438e468Sderaadt
1324438e468Sderaadt rw_init(&sc->sc_lock, "iiclk");
1334438e468Sderaadt sc->sc_i2c.ic_cookie = sc;
1344438e468Sderaadt sc->sc_i2c.ic_acquire_bus = mgiic_i2c_acquire_bus;
1354438e468Sderaadt sc->sc_i2c.ic_release_bus = mgiic_i2c_release_bus;
1364438e468Sderaadt sc->sc_i2c.ic_exec = mgiic_i2c_exec;
1374438e468Sderaadt
138539dbbeaSderaadt printf("\n");
139539dbbeaSderaadt
1404438e468Sderaadt bzero(&iba, sizeof(iba));
1414438e468Sderaadt iba.iba_name = "iic";
1424438e468Sderaadt iba.iba_tag = &sc->sc_i2c;
1434438e468Sderaadt iba.iba_bus_scan = ofwiic_scan;
1444438e468Sderaadt iba.iba_bus_scan_arg = &ma->ma_node;
1454438e468Sderaadt config_found(&sc->sc_dev, &iba, iicbus_print);
1464438e468Sderaadt }
1474438e468Sderaadt
1484438e468Sderaadt int
mgiic_i2c_acquire_bus(void * arg,int flags)1494438e468Sderaadt mgiic_i2c_acquire_bus(void *arg, int flags)
1504438e468Sderaadt {
1514438e468Sderaadt struct mgiic_softc *sc = arg;
1524438e468Sderaadt
1534438e468Sderaadt if (cold || sc->sc_poll || (flags & I2C_F_POLL))
1544438e468Sderaadt return (0);
1554438e468Sderaadt
1564438e468Sderaadt return (rw_enter(&sc->sc_lock, RW_WRITE | RW_INTR));
1574438e468Sderaadt }
1584438e468Sderaadt
1594438e468Sderaadt void
mgiic_i2c_release_bus(void * arg,int flags)1604438e468Sderaadt mgiic_i2c_release_bus(void *arg, int flags)
1614438e468Sderaadt {
1624438e468Sderaadt struct mgiic_softc *sc = arg;
1634438e468Sderaadt
1644438e468Sderaadt if (cold || sc->sc_poll || (flags & I2C_F_POLL))
1654438e468Sderaadt return;
1664438e468Sderaadt
1674438e468Sderaadt rw_exit(&sc->sc_lock);
1684438e468Sderaadt }
1694438e468Sderaadt
1704438e468Sderaadt int
mgiic_i2c_exec(void * arg,i2c_op_t op,i2c_addr_t addr,const void * cmdbuf,size_t cmdlen,void * buf,size_t len,int flags)1714438e468Sderaadt mgiic_i2c_exec(void *arg, i2c_op_t op, i2c_addr_t addr,
1724438e468Sderaadt const void *cmdbuf, size_t cmdlen, void *buf, size_t len, int flags)
1734438e468Sderaadt {
1744438e468Sderaadt struct mgiic_softc *sc = arg;
175539dbbeaSderaadt int ret = 0;
1764438e468Sderaadt
1774438e468Sderaadt if (addr & ~0x7f)
1784438e468Sderaadt return (1);
1794438e468Sderaadt
1804438e468Sderaadt if (cold || sc->sc_poll)
1814438e468Sderaadt flags |= I2C_F_POLL;
1824438e468Sderaadt
183022afbd7Sjsg if (cmdlen > 0) {
184539dbbeaSderaadt ret = mgiic_xmit(sc, addr & 0x7f, cmdbuf, cmdlen);
185539dbbeaSderaadt if (ret != 0)
186539dbbeaSderaadt goto done;
187022afbd7Sjsg }
1884438e468Sderaadt
1894438e468Sderaadt if (len > 0) {
1904438e468Sderaadt if (I2C_OP_WRITE_P(op))
1914438e468Sderaadt ret = mgiic_xmit(sc, addr & 0x7f, buf, len);
1924438e468Sderaadt else
1934438e468Sderaadt ret = mgiic_recv(sc, addr & 0x7f, buf, len);
1944438e468Sderaadt }
195539dbbeaSderaadt done:
1964438e468Sderaadt printf("e%d\n", ret);
1974438e468Sderaadt return (ret);
1984438e468Sderaadt }
1994438e468Sderaadt
2004438e468Sderaadt int
mgiic_xmit(struct mgiic_softc * sc,u_int8_t addr,const u_int8_t * buf,size_t len)2014438e468Sderaadt mgiic_xmit(struct mgiic_softc *sc, u_int8_t addr, const u_int8_t *buf,
2024438e468Sderaadt size_t len)
2034438e468Sderaadt {
204539dbbeaSderaadt int err = 1, i = 0;
2054438e468Sderaadt
206539dbbeaSderaadt top:
207539dbbeaSderaadt printf("xmit s%02x STA ", mgiic_read(sc, MGSTATUS));
208539dbbeaSderaadt mgiic_control(sc, MGCONTROL_STA, MGCONTROL_IFLG);
2094438e468Sderaadt
2104438e468Sderaadt if (mgiic_poll(sc))
2114438e468Sderaadt goto bail;
212539dbbeaSderaadt printf("s%02x ", mgiic_read(sc, MGSTATUS));
213539dbbeaSderaadt if (mgiic_read(sc, MGSTATUS) != MGSTATUS_STARTSENT)
2144438e468Sderaadt goto bail;
2154438e468Sderaadt
216539dbbeaSderaadt mgiic_write(sc, MGDATA, addr << 1);
217539dbbeaSderaadt printf("a%02x ", addr << 1);
218539dbbeaSderaadt mgiic_control(sc, 0, MGCONTROL_IFLG);
2194438e468Sderaadt
220539dbbeaSderaadt while (i < len) {
2214438e468Sderaadt if (mgiic_poll(sc))
2224438e468Sderaadt goto bail;
223539dbbeaSderaadt printf("s%02x ", mgiic_read(sc, MGSTATUS));
224539dbbeaSderaadt switch (mgiic_read(sc, MGSTATUS)) {
225539dbbeaSderaadt case MGSTATUS_ADDR_W_ACKR:
226539dbbeaSderaadt case MGSTATUS_MDATA_ACKR:
227539dbbeaSderaadt mgiic_write(sc, MGDATA, buf[i]);
2284438e468Sderaadt printf("w%02x ", buf[i]);
229539dbbeaSderaadt i++;
230539dbbeaSderaadt mgiic_control(sc, 0, MGCONTROL_IFLG);
2314438e468Sderaadt break;
232539dbbeaSderaadt case MGSTATUS_ADDR_W_NOACKR:
233539dbbeaSderaadt case MGSTATUS_MDATA_NOACKR:
234539dbbeaSderaadt mgiic_write(sc, MGDATA, buf[i]);
235539dbbeaSderaadt printf("w%02x ", buf[i]);
236539dbbeaSderaadt mgiic_control(sc, 0, MGCONTROL_IFLG);
237539dbbeaSderaadt break;
238539dbbeaSderaadt case MGSTATUS_BUSERR:
239539dbbeaSderaadt mgiic_control(sc, MGCONTROL_STP, MGCONTROL_IFLG);
240539dbbeaSderaadt i = 0;
241539dbbeaSderaadt if (mgiic_poll(sc))
242539dbbeaSderaadt goto bail;
243539dbbeaSderaadt goto top;
244539dbbeaSderaadt case MGSTATUS_IDLE:
2454438e468Sderaadt default:
246539dbbeaSderaadt err = 1;
2474438e468Sderaadt goto bail;
2484438e468Sderaadt }
2494438e468Sderaadt }
250539dbbeaSderaadt printf("OK ");
2514438e468Sderaadt err = 0;
2524438e468Sderaadt bail:
253539dbbeaSderaadt if (err)
254539dbbeaSderaadt printf("BAIL STP s%02x\n", mgiic_read(sc, MGSTATUS));
255539dbbeaSderaadt mgiic_control(sc, MGCONTROL_STP, MGCONTROL_IFLG);
256539dbbeaSderaadt while (mgiic_read(sc, MGSTATUS) != MGSTATUS_IDLE)
2574438e468Sderaadt ;
258539dbbeaSderaadt printf("s%02x\n", mgiic_read(sc, MGSTATUS));
2594438e468Sderaadt return (err);
2604438e468Sderaadt }
2614438e468Sderaadt
2624438e468Sderaadt int
mgiic_recv(struct mgiic_softc * sc,u_int8_t addr,u_int8_t * buf,size_t len)2634438e468Sderaadt mgiic_recv(struct mgiic_softc *sc, u_int8_t addr, u_int8_t *buf, size_t len)
2644438e468Sderaadt {
265539dbbeaSderaadt int err = 1, i = 0;
2664438e468Sderaadt
267539dbbeaSderaadt printf("recv s%02x ", mgiic_read(sc, MGSTATUS));
268539dbbeaSderaadt mgiic_control(sc, MGCONTROL_STA, MGCONTROL_IFLG);
2694438e468Sderaadt if (mgiic_poll(sc))
2704438e468Sderaadt goto bail;
271539dbbeaSderaadt
272539dbbeaSderaadt printf("s%02x ", mgiic_read(sc, MGSTATUS));
273539dbbeaSderaadt if (mgiic_read(sc, MGSTATUS) != MGSTATUS_STARTSENT)
2744438e468Sderaadt goto bail;
2754438e468Sderaadt
276539dbbeaSderaadt re_address:
277539dbbeaSderaadt mgiic_write(sc, MGDATA, (addr << 1) | 0x01);
278539dbbeaSderaadt printf("a%02x ", (addr << 1) | 0x01);
279539dbbeaSderaadt mgiic_control(sc, 0, MGCONTROL_IFLG);
2804438e468Sderaadt
281539dbbeaSderaadt while (i < len) {
2824438e468Sderaadt if (mgiic_poll(sc))
2834438e468Sderaadt goto bail;
284539dbbeaSderaadt printf("s%02x ", mgiic_read(sc, MGSTATUS));
285539dbbeaSderaadt switch (mgiic_read(sc, MGSTATUS)) {
286539dbbeaSderaadt case MGSTATUS_ADDR_R_ACKR:
287539dbbeaSderaadt if (len - i > 1)
288539dbbeaSderaadt mgiic_control(sc, MGCONTROL_AAK, MGCONTROL_IFLG);
289539dbbeaSderaadt else
290539dbbeaSderaadt mgiic_control(sc, 0, MGCONTROL_IFLG);
2914438e468Sderaadt break;
292539dbbeaSderaadt case MGSTATUS_ADDR_R_NOACKR:
293539dbbeaSderaadt mgiic_control(sc, MGCONTROL_STA, MGCONTROL_IFLG);
2944438e468Sderaadt break;
295539dbbeaSderaadt case MGSTATUS_REPEATSTART:
296539dbbeaSderaadt goto re_address;
297539dbbeaSderaadt case MGSTATUS_MDATA_ACKT:
298539dbbeaSderaadt buf[i] = mgiic_read(sc, MGDATA);
2994438e468Sderaadt printf("r%02x ", buf[i]);
3004438e468Sderaadt i++;
301539dbbeaSderaadt if (len - i > 1)
302539dbbeaSderaadt mgiic_control(sc, MGCONTROL_AAK, MGCONTROL_IFLG);
303539dbbeaSderaadt else
304539dbbeaSderaadt mgiic_control(sc, 0, MGCONTROL_IFLG|MGCONTROL_AAK);
305539dbbeaSderaadt break;
306539dbbeaSderaadt case MGSTATUS_MDATA_NOACKT:
307539dbbeaSderaadt buf[i] = mgiic_read(sc, MGDATA);
308539dbbeaSderaadt printf("r%02x ", buf[i]);
309539dbbeaSderaadt i++;
310539dbbeaSderaadt if (len == i) {
311539dbbeaSderaadt printf("DONE ");
312539dbbeaSderaadt err = 0;
313539dbbeaSderaadt goto bail;
314539dbbeaSderaadt }
315539dbbeaSderaadt printf("SHORT ");
316539dbbeaSderaadt goto bail;
3174438e468Sderaadt break;
3184438e468Sderaadt default:
319539dbbeaSderaadt printf("BAD");
3204438e468Sderaadt goto bail;
3214438e468Sderaadt }
3224438e468Sderaadt }
323539dbbeaSderaadt printf("OK ");
3244438e468Sderaadt err = 0;
3254438e468Sderaadt bail:
326539dbbeaSderaadt if (err)
327539dbbeaSderaadt printf("BAIL STP s%02x\n", mgiic_read(sc, MGSTATUS));
328539dbbeaSderaadt mgiic_control(sc, MGCONTROL_STP, MGCONTROL_IFLG | MGCONTROL_AAK);
329539dbbeaSderaadt while (mgiic_read(sc, MGSTATUS) != MGSTATUS_IDLE)
3304438e468Sderaadt ;
331539dbbeaSderaadt printf("s%02x\n", mgiic_read(sc, MGSTATUS));
3324438e468Sderaadt return (err);
3334438e468Sderaadt }
3344438e468Sderaadt
3354438e468Sderaadt volatile u_int8_t
mgiic_read(struct mgiic_softc * sc,bus_size_t r)3364438e468Sderaadt mgiic_read(struct mgiic_softc *sc, bus_size_t r)
3374438e468Sderaadt {
3384438e468Sderaadt bus_space_barrier(sc->sc_bt, sc->sc_regh, r, 8,
3394438e468Sderaadt BUS_SPACE_BARRIER_READ);
3404438e468Sderaadt return (bus_space_read_8(sc->sc_bt, sc->sc_regh, r)) & 0xff;
3414438e468Sderaadt }
3424438e468Sderaadt
3434438e468Sderaadt volatile void
mgiic_write(struct mgiic_softc * sc,bus_size_t r,u_int8_t v)3444438e468Sderaadt mgiic_write(struct mgiic_softc *sc, bus_size_t r, u_int8_t v)
3454438e468Sderaadt {
3464438e468Sderaadt u_int64_t val = v;
3474438e468Sderaadt
3484438e468Sderaadt bus_space_write_8(sc->sc_bt, sc->sc_regh, r, val);
3494438e468Sderaadt bus_space_barrier(sc->sc_bt, sc->sc_regh, r, 8,
3504438e468Sderaadt BUS_SPACE_BARRIER_WRITE);
3514438e468Sderaadt }
3524438e468Sderaadt
3534438e468Sderaadt volatile void
mgiic_control(struct mgiic_softc * sc,u_int8_t on,u_int8_t off)3544438e468Sderaadt mgiic_control(struct mgiic_softc *sc, u_int8_t on, u_int8_t off)
3554438e468Sderaadt {
3564438e468Sderaadt u_int8_t val;
3574438e468Sderaadt
358539dbbeaSderaadt val = (mgiic_read(sc, MGCONTROL) | on) & ~off;
359539dbbeaSderaadt mgiic_write(sc, MGCONTROL, val);
3604438e468Sderaadt }
3614438e468Sderaadt
3624438e468Sderaadt int
mgiic_poll(struct mgiic_softc * sc)3634438e468Sderaadt mgiic_poll(struct mgiic_softc *sc)
3644438e468Sderaadt {
3654438e468Sderaadt int i;
3664438e468Sderaadt
3674438e468Sderaadt for (i = 0; i < 1000; i++) {
368539dbbeaSderaadt if (mgiic_read(sc, MGCONTROL) & MGCONTROL_IFLG)
3694438e468Sderaadt return (0);
3704438e468Sderaadt delay(100);
3714438e468Sderaadt }
3724438e468Sderaadt return (1);
3734438e468Sderaadt }
374