1*9fdf0c62Smpi /* $OpenBSD: amliic.c,v 1.5 2021/10/24 17:52:26 mpi Exp $ */
2965b27e9Skettenis /*
3965b27e9Skettenis * Copyright (c) 2019 Mark Kettenis <kettenis@openbsd.org>
4965b27e9Skettenis *
5965b27e9Skettenis * Permission to use, copy, modify, and distribute this software for any
6965b27e9Skettenis * purpose with or without fee is hereby granted, provided that the above
7965b27e9Skettenis * copyright notice and this permission notice appear in all copies.
8965b27e9Skettenis *
9965b27e9Skettenis * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
10965b27e9Skettenis * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
11965b27e9Skettenis * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
12965b27e9Skettenis * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
13965b27e9Skettenis * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
14965b27e9Skettenis * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
15965b27e9Skettenis * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
16965b27e9Skettenis */
17965b27e9Skettenis
18965b27e9Skettenis #include <sys/param.h>
19965b27e9Skettenis #include <sys/systm.h>
20965b27e9Skettenis #include <sys/device.h>
21965b27e9Skettenis
22965b27e9Skettenis #include <machine/intr.h>
23965b27e9Skettenis #include <machine/bus.h>
24965b27e9Skettenis #include <machine/fdt.h>
25965b27e9Skettenis
26965b27e9Skettenis #define _I2C_PRIVATE
27965b27e9Skettenis #include <dev/i2c/i2cvar.h>
28965b27e9Skettenis
29965b27e9Skettenis #include <dev/ofw/openfirm.h>
30965b27e9Skettenis #include <dev/ofw/ofw_clock.h>
31965b27e9Skettenis #include <dev/ofw/ofw_pinctrl.h>
32965b27e9Skettenis #include <dev/ofw/fdt.h>
33965b27e9Skettenis
34965b27e9Skettenis /* Registers. */
35965b27e9Skettenis #define I2C_M_CONTROL 0x00
36965b27e9Skettenis #define I2C_M_CONTROL_QTR_CLK_DLY_SHIFT 12
37965b27e9Skettenis #define I2C_M_CONTROL_QTR_CLK_EXT_SHIFT 28
38965b27e9Skettenis #define I2C_M_CONTROL_ERROR (1 << 3)
39965b27e9Skettenis #define I2C_M_CONTROL_STATUS (1 << 2)
40965b27e9Skettenis #define I2C_M_CONTROL_START (1 << 0)
41965b27e9Skettenis #define I2C_M_SLAVE_ADDRESS 0x04
42965b27e9Skettenis #define I2C_M_TOKEN_LIST0 0x08
43965b27e9Skettenis #define I2C_M_TOKEN_LIST1 0x0c
44965b27e9Skettenis #define I2C_M_TOKEN_WDATA0 0x10
45965b27e9Skettenis #define I2C_M_TOKEN_WDATA1 0x14
46965b27e9Skettenis #define I2C_M_TOKEN_RDATA0 0x18
47965b27e9Skettenis #define I2C_M_TOKEN_RDATA1 0x1c
48965b27e9Skettenis
49965b27e9Skettenis /* Token definitions. */
50965b27e9Skettenis #define END 0x0
51965b27e9Skettenis #define START 0x1
52965b27e9Skettenis #define SLAVE_ADDR_WRITE 0x2
53965b27e9Skettenis #define SLAVE_ADDR_READ 0x3
54965b27e9Skettenis #define DATA 0x4
55965b27e9Skettenis #define DATA_LAST 0x5
56965b27e9Skettenis #define STOP 0x6
57965b27e9Skettenis
58965b27e9Skettenis #define HREAD4(sc, reg) \
59965b27e9Skettenis (bus_space_read_4((sc)->sc_iot, (sc)->sc_ioh, (reg)))
60965b27e9Skettenis #define HWRITE4(sc, reg, val) \
61965b27e9Skettenis bus_space_write_4((sc)->sc_iot, (sc)->sc_ioh, (reg), (val))
62965b27e9Skettenis #define HSET4(sc, reg, bits) \
63965b27e9Skettenis HWRITE4((sc), (reg), HREAD4((sc), (reg)) | (bits))
64965b27e9Skettenis #define HCLR4(sc, reg, bits) \
65965b27e9Skettenis HWRITE4((sc), (reg), HREAD4((sc), (reg)) & ~(bits))
66965b27e9Skettenis
67965b27e9Skettenis struct amliic_softc {
68965b27e9Skettenis struct device sc_dev;
69965b27e9Skettenis bus_space_tag_t sc_iot;
70965b27e9Skettenis bus_space_handle_t sc_ioh;
71965b27e9Skettenis
72965b27e9Skettenis int sc_node;
73965b27e9Skettenis struct i2c_controller sc_ic;
74965b27e9Skettenis };
75965b27e9Skettenis
76965b27e9Skettenis int amliic_match(struct device *, void *, void *);
77965b27e9Skettenis void amliic_attach(struct device *, struct device *, void *);
78965b27e9Skettenis
79*9fdf0c62Smpi const struct cfattach amliic_ca = {
80965b27e9Skettenis sizeof (struct amliic_softc), amliic_match, amliic_attach
81965b27e9Skettenis };
82965b27e9Skettenis
83965b27e9Skettenis struct cfdriver amliic_cd = {
84965b27e9Skettenis NULL, "amliic", DV_DULL
85965b27e9Skettenis };
86965b27e9Skettenis
87965b27e9Skettenis int amliic_acquire_bus(void *, int);
88965b27e9Skettenis void amliic_release_bus(void *, int);
89965b27e9Skettenis int amliic_exec(void *, i2c_op_t, i2c_addr_t, const void *, size_t,
90965b27e9Skettenis void *, size_t, int);
91965b27e9Skettenis
92965b27e9Skettenis void amliic_bus_scan(struct device *, struct i2cbus_attach_args *, void *);
93965b27e9Skettenis
94965b27e9Skettenis int
amliic_match(struct device * parent,void * match,void * aux)95965b27e9Skettenis amliic_match(struct device *parent, void *match, void *aux)
96965b27e9Skettenis {
97965b27e9Skettenis struct fdt_attach_args *faa = aux;
98965b27e9Skettenis
99965b27e9Skettenis return OF_is_compatible(faa->fa_node, "amlogic,meson-axg-i2c");
100965b27e9Skettenis }
101965b27e9Skettenis
102965b27e9Skettenis void
amliic_attach(struct device * parent,struct device * self,void * aux)103965b27e9Skettenis amliic_attach(struct device *parent, struct device *self, void *aux)
104965b27e9Skettenis {
105965b27e9Skettenis struct amliic_softc *sc = (struct amliic_softc *)self;
106965b27e9Skettenis struct fdt_attach_args *faa = aux;
107965b27e9Skettenis struct i2cbus_attach_args iba;
108965b27e9Skettenis uint32_t clock_speed, bus_speed;
109965b27e9Skettenis uint32_t div, divl, divh;
110965b27e9Skettenis
111965b27e9Skettenis if (faa->fa_nreg < 1) {
112965b27e9Skettenis printf(": no registers\n");
113965b27e9Skettenis return;
114965b27e9Skettenis }
115965b27e9Skettenis
116965b27e9Skettenis sc->sc_iot = faa->fa_iot;
117965b27e9Skettenis sc->sc_node = faa->fa_node;
118965b27e9Skettenis
119965b27e9Skettenis if (bus_space_map(sc->sc_iot, faa->fa_reg[0].addr,
120965b27e9Skettenis faa->fa_reg[0].size, 0, &sc->sc_ioh)) {
121965b27e9Skettenis printf(": can't map registers\n");
122965b27e9Skettenis return;
123965b27e9Skettenis }
124965b27e9Skettenis
125965b27e9Skettenis printf("\n");
126965b27e9Skettenis
127965b27e9Skettenis pinctrl_byname(sc->sc_node, "default");
128965b27e9Skettenis
129965b27e9Skettenis clock_enable_all(sc->sc_node);
130965b27e9Skettenis
131965b27e9Skettenis clock_speed = clock_get_frequency(sc->sc_node, NULL);
132965b27e9Skettenis bus_speed = OF_getpropint(sc->sc_node, "clock-frequency", 100000);
133965b27e9Skettenis
134965b27e9Skettenis div = clock_speed / bus_speed / 4;
135965b27e9Skettenis divl = div & 0x3ff;
136965b27e9Skettenis divh = div >> 10;
137965b27e9Skettenis HWRITE4(sc, I2C_M_CONTROL, divh << I2C_M_CONTROL_QTR_CLK_EXT_SHIFT |
138965b27e9Skettenis divl << I2C_M_CONTROL_QTR_CLK_DLY_SHIFT);
139965b27e9Skettenis
140965b27e9Skettenis sc->sc_ic.ic_cookie = sc;
141965b27e9Skettenis sc->sc_ic.ic_acquire_bus = amliic_acquire_bus;
142965b27e9Skettenis sc->sc_ic.ic_release_bus = amliic_release_bus;
143965b27e9Skettenis sc->sc_ic.ic_exec = amliic_exec;
144965b27e9Skettenis
145965b27e9Skettenis /* Configure its children */
146965b27e9Skettenis memset(&iba, 0, sizeof(iba));
147965b27e9Skettenis iba.iba_name = "iic";
148965b27e9Skettenis iba.iba_tag = &sc->sc_ic;
149965b27e9Skettenis iba.iba_bus_scan = amliic_bus_scan;
150965b27e9Skettenis iba.iba_bus_scan_arg = &sc->sc_node;
151965b27e9Skettenis
152965b27e9Skettenis config_found(&sc->sc_dev, &iba, iicbus_print);
153965b27e9Skettenis }
154965b27e9Skettenis
155965b27e9Skettenis int
amliic_acquire_bus(void * cookie,int flags)156965b27e9Skettenis amliic_acquire_bus(void *cookie, int flags)
157965b27e9Skettenis {
158965b27e9Skettenis return 0;
159965b27e9Skettenis }
160965b27e9Skettenis
161965b27e9Skettenis void
amliic_release_bus(void * cookie,int flags)162965b27e9Skettenis amliic_release_bus(void *cookie, int flags)
163965b27e9Skettenis {
164965b27e9Skettenis }
165965b27e9Skettenis
166965b27e9Skettenis int
amliic_exec(void * cookie,i2c_op_t op,i2c_addr_t addr,const void * cmd,size_t cmdlen,void * buf,size_t buflen,int flags)167965b27e9Skettenis amliic_exec(void *cookie, i2c_op_t op, i2c_addr_t addr, const void *cmd,
168965b27e9Skettenis size_t cmdlen, void *buf, size_t buflen, int flags)
169965b27e9Skettenis {
170965b27e9Skettenis struct amliic_softc *sc = cookie;
171965b27e9Skettenis uint64_t tokens = 0;
172965b27e9Skettenis uint64_t data = 0;
173965b27e9Skettenis uint32_t rdata;
17425591a85Skettenis uint32_t ctrl;
17525591a85Skettenis size_t pos, len;
176965b27e9Skettenis int i = 0, j = 0;
177965b27e9Skettenis int timo, k;
178965b27e9Skettenis
179965b27e9Skettenis #define SET_TOKEN(i, t) \
180965b27e9Skettenis tokens |= (((uint64_t)(t)) << ((i) * 4))
181965b27e9Skettenis #define SET_DATA(i, d) \
182965b27e9Skettenis data |= (((uint64_t)(d)) << ((i) * 8))
183965b27e9Skettenis
18425591a85Skettenis if (cmdlen > 8)
185965b27e9Skettenis return EINVAL;
186965b27e9Skettenis
187965b27e9Skettenis if (cmdlen > 0) {
188965b27e9Skettenis SET_TOKEN(i++, START);
189965b27e9Skettenis SET_TOKEN(i++, SLAVE_ADDR_WRITE);
190965b27e9Skettenis for (k = 0; k < cmdlen; k++) {
191965b27e9Skettenis SET_TOKEN(i++, DATA);
192965b27e9Skettenis SET_DATA(j++, ((uint8_t *)cmd)[k]);
193965b27e9Skettenis }
194965b27e9Skettenis }
195965b27e9Skettenis
196965b27e9Skettenis if (I2C_OP_READ_P(op)) {
197965b27e9Skettenis SET_TOKEN(i++, START);
198965b27e9Skettenis SET_TOKEN(i++, SLAVE_ADDR_READ);
19925591a85Skettenis } else if (cmdlen == 0) {
200965b27e9Skettenis SET_TOKEN(i++, START);
201965b27e9Skettenis SET_TOKEN(i++, SLAVE_ADDR_WRITE);
202965b27e9Skettenis }
20325591a85Skettenis
20425591a85Skettenis HWRITE4(sc, I2C_M_SLAVE_ADDRESS, addr << 1);
20525591a85Skettenis
20625591a85Skettenis pos = 0;
20725591a85Skettenis while (pos < buflen) {
20825591a85Skettenis len = MIN(buflen - pos, 8 - j);
20925591a85Skettenis
21025591a85Skettenis if (I2C_OP_READ_P(op)) {
21125591a85Skettenis for (k = 0; k < len; k++)
21225591a85Skettenis SET_TOKEN(i++, (pos == (buflen - 1)) ?
21325591a85Skettenis DATA_LAST : DATA);
21425591a85Skettenis } else {
21525591a85Skettenis for (k = 0; k < len; k++) {
216965b27e9Skettenis SET_TOKEN(i++, DATA);
21725591a85Skettenis SET_DATA(j++, ((uint8_t *)buf)[pos++]);
218965b27e9Skettenis }
219965b27e9Skettenis }
220965b27e9Skettenis
22125591a85Skettenis if (pos == buflen && I2C_OP_STOP_P(op))
222965b27e9Skettenis SET_TOKEN(i++, STOP);
223965b27e9Skettenis
224965b27e9Skettenis SET_TOKEN(i++, END);
225965b27e9Skettenis
22625591a85Skettenis /* Write slave address, tokens and data to hardware. */
227965b27e9Skettenis HWRITE4(sc, I2C_M_TOKEN_LIST0, tokens);
228965b27e9Skettenis HWRITE4(sc, I2C_M_TOKEN_LIST1, tokens >> 32);
229965b27e9Skettenis HWRITE4(sc, I2C_M_TOKEN_WDATA0, data);
230965b27e9Skettenis HWRITE4(sc, I2C_M_TOKEN_WDATA1, data >> 32);
231965b27e9Skettenis
232965b27e9Skettenis /* Start token list processing. */
233965b27e9Skettenis HSET4(sc, I2C_M_CONTROL, I2C_M_CONTROL_START);
234965b27e9Skettenis for (timo = 50000; timo > 0; timo--) {
23525591a85Skettenis ctrl = HREAD4(sc, I2C_M_CONTROL);
23625591a85Skettenis if ((ctrl & I2C_M_CONTROL_STATUS) == 0)
237965b27e9Skettenis break;
238965b27e9Skettenis delay(10);
239965b27e9Skettenis }
240965b27e9Skettenis HCLR4(sc, I2C_M_CONTROL, I2C_M_CONTROL_START);
24125591a85Skettenis if (ctrl & I2C_M_CONTROL_ERROR)
242965b27e9Skettenis return EIO;
24325591a85Skettenis if (timo == 0)
24425591a85Skettenis return ETIMEDOUT;
245965b27e9Skettenis
246965b27e9Skettenis if (I2C_OP_READ_P(op)) {
24725591a85Skettenis rdata = HREAD4(sc, I2C_M_TOKEN_RDATA0);
24825591a85Skettenis for (i = 0; i < len; i++) {
24925591a85Skettenis if (i == 4)
25025591a85Skettenis rdata = HREAD4(sc, I2C_M_TOKEN_RDATA1);
25125591a85Skettenis ((uint8_t *)buf)[pos++] = rdata;
252965b27e9Skettenis rdata >>= 8;
253965b27e9Skettenis }
254965b27e9Skettenis }
255965b27e9Skettenis
25625591a85Skettenis /* Reset tokens. */
25725591a85Skettenis tokens = 0;
25825591a85Skettenis data = 0;
25925591a85Skettenis i = j = 0;
26025591a85Skettenis }
26125591a85Skettenis
262965b27e9Skettenis return 0;
263965b27e9Skettenis }
264965b27e9Skettenis
265965b27e9Skettenis void
amliic_bus_scan(struct device * self,struct i2cbus_attach_args * iba,void * arg)266965b27e9Skettenis amliic_bus_scan(struct device *self, struct i2cbus_attach_args *iba, void *arg)
267965b27e9Skettenis {
268965b27e9Skettenis int iba_node = *(int *)arg;
269965b27e9Skettenis struct i2c_attach_args ia;
270c1762c82Spatrick char name[32], status[32];
271965b27e9Skettenis uint32_t reg[1];
272965b27e9Skettenis int node;
273965b27e9Skettenis
274965b27e9Skettenis for (node = OF_child(iba_node); node; node = OF_peer(node)) {
275965b27e9Skettenis memset(name, 0, sizeof(name));
276c1762c82Spatrick memset(status, 0, sizeof(status));
277965b27e9Skettenis memset(reg, 0, sizeof(reg));
278965b27e9Skettenis
279965b27e9Skettenis if (OF_getprop(node, "compatible", name, sizeof(name)) == -1)
280965b27e9Skettenis continue;
281965b27e9Skettenis if (name[0] == '\0')
282965b27e9Skettenis continue;
283965b27e9Skettenis
284c1762c82Spatrick if (OF_getprop(node, "status", status, sizeof(status)) > 0 &&
285c1762c82Spatrick strcmp(status, "disabled") == 0)
286c1762c82Spatrick continue;
287c1762c82Spatrick
288965b27e9Skettenis if (OF_getprop(node, "reg", ®, sizeof(reg)) != sizeof(reg))
289965b27e9Skettenis continue;
290965b27e9Skettenis
291965b27e9Skettenis memset(&ia, 0, sizeof(ia));
292965b27e9Skettenis ia.ia_tag = iba->iba_tag;
293965b27e9Skettenis ia.ia_addr = bemtoh32(®[0]);
294965b27e9Skettenis ia.ia_name = name;
295965b27e9Skettenis ia.ia_cookie = &node;
296965b27e9Skettenis config_found(self, &ia, iic_print);
297965b27e9Skettenis }
298965b27e9Skettenis }
299