xref: /freebsd/sys/powerpc/powernv/opal_i2c.c (revision 4ffd72e34cb3f45ede886c9e30b2a5f705b33132)
1*4ffd72e3SWojciech Macek /*-
2*4ffd72e3SWojciech Macek  * Copyright (c) 2017-2018 QCM Technologies.
3*4ffd72e3SWojciech Macek  * Copyright (c) 2017-2018 Semihalf.
4*4ffd72e3SWojciech Macek  * All rights reserved.
5*4ffd72e3SWojciech Macek  *
6*4ffd72e3SWojciech Macek  * Redistribution and use in source and binary forms, with or without
7*4ffd72e3SWojciech Macek  * modification, are permitted provided that the following conditions
8*4ffd72e3SWojciech Macek  * are met:
9*4ffd72e3SWojciech Macek  * 1. Redistributions of source code must retain the above copyright
10*4ffd72e3SWojciech Macek  *    notice, this list of conditions and the following disclaimer.
11*4ffd72e3SWojciech Macek  * 2. Redistributions in binary form must reproduce the above copyright
12*4ffd72e3SWojciech Macek  *    notice, this list of conditions and the following disclaimer in the
13*4ffd72e3SWojciech Macek  *    documentation and/or other materials provided with the distribution.
14*4ffd72e3SWojciech Macek  *
15*4ffd72e3SWojciech Macek  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
16*4ffd72e3SWojciech Macek  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
17*4ffd72e3SWojciech Macek  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
18*4ffd72e3SWojciech Macek  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
19*4ffd72e3SWojciech Macek  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
20*4ffd72e3SWojciech Macek  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
21*4ffd72e3SWojciech Macek  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
22*4ffd72e3SWojciech Macek  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
23*4ffd72e3SWojciech Macek  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
24*4ffd72e3SWojciech Macek  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
25*4ffd72e3SWojciech Macek  * SUCH DAMAGE.
26*4ffd72e3SWojciech Macek  *
27*4ffd72e3SWojciech Macek  * $FreeBSD$
28*4ffd72e3SWojciech Macek  */
29*4ffd72e3SWojciech Macek 
30*4ffd72e3SWojciech Macek #include "opt_platform.h"
31*4ffd72e3SWojciech Macek 
32*4ffd72e3SWojciech Macek #include <sys/cdefs.h>
33*4ffd72e3SWojciech Macek __FBSDID("$FreeBSD$");
34*4ffd72e3SWojciech Macek 
35*4ffd72e3SWojciech Macek #include <sys/param.h>
36*4ffd72e3SWojciech Macek #include <sys/endian.h>
37*4ffd72e3SWojciech Macek #include <sys/systm.h>
38*4ffd72e3SWojciech Macek #include <sys/bus.h>
39*4ffd72e3SWojciech Macek #include <sys/conf.h>
40*4ffd72e3SWojciech Macek #include <sys/kernel.h>
41*4ffd72e3SWojciech Macek #include <sys/lock.h>
42*4ffd72e3SWojciech Macek #include <sys/mbuf.h>
43*4ffd72e3SWojciech Macek #include <sys/malloc.h>
44*4ffd72e3SWojciech Macek #include <sys/module.h>
45*4ffd72e3SWojciech Macek #include <sys/mutex.h>
46*4ffd72e3SWojciech Macek #include <sys/rman.h>
47*4ffd72e3SWojciech Macek #include <machine/bus.h>
48*4ffd72e3SWojciech Macek 
49*4ffd72e3SWojciech Macek #include <vm/vm.h>
50*4ffd72e3SWojciech Macek #include <vm/pmap.h>
51*4ffd72e3SWojciech Macek 
52*4ffd72e3SWojciech Macek #include <dev/iicbus/iiconf.h>
53*4ffd72e3SWojciech Macek #include <dev/iicbus/iicbus.h>
54*4ffd72e3SWojciech Macek #include "iicbus_if.h"
55*4ffd72e3SWojciech Macek 
56*4ffd72e3SWojciech Macek #include "opal.h"
57*4ffd72e3SWojciech Macek 
58*4ffd72e3SWojciech Macek #ifdef FDT
59*4ffd72e3SWojciech Macek #include <dev/ofw/ofw_bus.h>
60*4ffd72e3SWojciech Macek #include <dev/ofw/ofw_bus_subr.h>
61*4ffd72e3SWojciech Macek #endif
62*4ffd72e3SWojciech Macek 
63*4ffd72e3SWojciech Macek struct opal_i2c_softc
64*4ffd72e3SWojciech Macek {
65*4ffd72e3SWojciech Macek 	device_t dev;
66*4ffd72e3SWojciech Macek 	device_t iicbus;
67*4ffd72e3SWojciech Macek 	uint32_t opal_id;
68*4ffd72e3SWojciech Macek 	struct mtx sc_mtx;
69*4ffd72e3SWojciech Macek };
70*4ffd72e3SWojciech Macek 
71*4ffd72e3SWojciech Macek /* OPAL I2C request */
72*4ffd72e3SWojciech Macek struct opal_i2c_request {
73*4ffd72e3SWojciech Macek 	uint8_t type;
74*4ffd72e3SWojciech Macek #define OPAL_I2C_RAW_READ	0
75*4ffd72e3SWojciech Macek #define OPAL_I2C_RAW_WRITE	1
76*4ffd72e3SWojciech Macek #define OPAL_I2C_SM_READ	2
77*4ffd72e3SWojciech Macek #define OPAL_I2C_SM_WRITE	3
78*4ffd72e3SWojciech Macek 	uint8_t flags;
79*4ffd72e3SWojciech Macek 	uint8_t	subaddr_sz;		/* Max 4 */
80*4ffd72e3SWojciech Macek 	uint8_t reserved;
81*4ffd72e3SWojciech Macek 	uint16_t addr;			/* 7 or 10 bit address */
82*4ffd72e3SWojciech Macek 	uint16_t reserved2;
83*4ffd72e3SWojciech Macek 	uint32_t subaddr;		/* Sub-address if any */
84*4ffd72e3SWojciech Macek 	uint32_t size;			/* Data size */
85*4ffd72e3SWojciech Macek 	uint64_t buffer_pa;		/* Buffer real address */
86*4ffd72e3SWojciech Macek };
87*4ffd72e3SWojciech Macek 
88*4ffd72e3SWojciech Macek static int opal_i2c_attach(device_t);
89*4ffd72e3SWojciech Macek static int opal_i2c_callback(device_t, int, caddr_t);
90*4ffd72e3SWojciech Macek static int opal_i2c_probe(device_t);
91*4ffd72e3SWojciech Macek static int opal_i2c_transfer(device_t, struct iic_msg *, uint32_t);
92*4ffd72e3SWojciech Macek static int i2c_opal_send_request(uint32_t, struct opal_i2c_request *);
93*4ffd72e3SWojciech Macek 
94*4ffd72e3SWojciech Macek static device_method_t opal_i2c_methods[] = {
95*4ffd72e3SWojciech Macek 	/* Device interface */
96*4ffd72e3SWojciech Macek 	DEVMETHOD(device_probe,		opal_i2c_probe),
97*4ffd72e3SWojciech Macek 	DEVMETHOD(device_attach,	opal_i2c_attach),
98*4ffd72e3SWojciech Macek 
99*4ffd72e3SWojciech Macek 	/* iicbus interface */
100*4ffd72e3SWojciech Macek 	DEVMETHOD(iicbus_callback,	opal_i2c_callback),
101*4ffd72e3SWojciech Macek 	DEVMETHOD(iicbus_transfer,	opal_i2c_transfer),
102*4ffd72e3SWojciech Macek 	DEVMETHOD_END
103*4ffd72e3SWojciech Macek };
104*4ffd72e3SWojciech Macek 
105*4ffd72e3SWojciech Macek #define	I2C_LOCK(_sc)		mtx_lock(&(_sc)->sc_mtx)
106*4ffd72e3SWojciech Macek #define	I2C_UNLOCK(_sc)		mtx_unlock(&(_sc)->sc_mtx)
107*4ffd72e3SWojciech Macek #define	I2C_LOCK_INIT(_sc) \
108*4ffd72e3SWojciech Macek 	mtx_init(&_sc->sc_mtx, device_get_nameunit(_sc->dev), \
109*4ffd72e3SWojciech Macek 	    "i2c", MTX_DEF)
110*4ffd72e3SWojciech Macek 
111*4ffd72e3SWojciech Macek static devclass_t opal_i2c_devclass;
112*4ffd72e3SWojciech Macek 
113*4ffd72e3SWojciech Macek static driver_t opal_i2c_driver = {
114*4ffd72e3SWojciech Macek 	"i2c",
115*4ffd72e3SWojciech Macek 	opal_i2c_methods,
116*4ffd72e3SWojciech Macek 	sizeof(struct opal_i2c_softc),
117*4ffd72e3SWojciech Macek };
118*4ffd72e3SWojciech Macek 
119*4ffd72e3SWojciech Macek static int
120*4ffd72e3SWojciech Macek opal_i2c_probe(device_t dev)
121*4ffd72e3SWojciech Macek {
122*4ffd72e3SWojciech Macek 
123*4ffd72e3SWojciech Macek 	if (!(ofw_bus_is_compatible(dev, "ibm,opal-i2c")))
124*4ffd72e3SWojciech Macek 		return (ENXIO);
125*4ffd72e3SWojciech Macek 
126*4ffd72e3SWojciech Macek 	device_set_desc(dev, "opal-i2c");
127*4ffd72e3SWojciech Macek 
128*4ffd72e3SWojciech Macek 	return (0);
129*4ffd72e3SWojciech Macek }
130*4ffd72e3SWojciech Macek 
131*4ffd72e3SWojciech Macek static int
132*4ffd72e3SWojciech Macek opal_i2c_attach(device_t dev)
133*4ffd72e3SWojciech Macek {
134*4ffd72e3SWojciech Macek 	struct opal_i2c_softc *sc;
135*4ffd72e3SWojciech Macek 	int len;
136*4ffd72e3SWojciech Macek 
137*4ffd72e3SWojciech Macek 	sc = device_get_softc(dev);
138*4ffd72e3SWojciech Macek 	sc->dev = dev;
139*4ffd72e3SWojciech Macek 
140*4ffd72e3SWojciech Macek 	len = OF_getproplen(ofw_bus_get_node(dev), "ibm,opal-id");
141*4ffd72e3SWojciech Macek 	if (len <= 0)
142*4ffd72e3SWojciech Macek 		return (EINVAL);
143*4ffd72e3SWojciech Macek 	OF_getencprop(ofw_bus_get_node(dev), "ibm,opal-id", &sc->opal_id, len);
144*4ffd72e3SWojciech Macek 
145*4ffd72e3SWojciech Macek 	if ((sc->iicbus = device_add_child(dev, "iicbus", -1)) == NULL) {
146*4ffd72e3SWojciech Macek 		device_printf(dev, "could not allocate iicbus instance\n");
147*4ffd72e3SWojciech Macek 		return (EINVAL);
148*4ffd72e3SWojciech Macek 	}
149*4ffd72e3SWojciech Macek 
150*4ffd72e3SWojciech Macek 	I2C_LOCK_INIT(sc);
151*4ffd72e3SWojciech Macek 
152*4ffd72e3SWojciech Macek 	return (bus_generic_attach(dev));
153*4ffd72e3SWojciech Macek }
154*4ffd72e3SWojciech Macek 
155*4ffd72e3SWojciech Macek static int
156*4ffd72e3SWojciech Macek opal_get_async_rc(struct opal_msg msg)
157*4ffd72e3SWojciech Macek {
158*4ffd72e3SWojciech Macek 	if (msg.msg_type != OPAL_MSG_ASYNC_COMP)
159*4ffd72e3SWojciech Macek 		return OPAL_PARAMETER;
160*4ffd72e3SWojciech Macek 	else
161*4ffd72e3SWojciech Macek 		return htobe64(msg.params[1]);
162*4ffd72e3SWojciech Macek }
163*4ffd72e3SWojciech Macek 
164*4ffd72e3SWojciech Macek static int
165*4ffd72e3SWojciech Macek i2c_opal_send_request(uint32_t bus_id, struct opal_i2c_request *req)
166*4ffd72e3SWojciech Macek {
167*4ffd72e3SWojciech Macek 	struct opal_msg msg;
168*4ffd72e3SWojciech Macek 	int token, rc;
169*4ffd72e3SWojciech Macek 
170*4ffd72e3SWojciech Macek 	/*
171*4ffd72e3SWojciech Macek 	 * XXX:
172*4ffd72e3SWojciech Macek 	 * Async tokens should be managed globally. Since there is
173*4ffd72e3SWojciech Macek 	 * only one place now, use hardcoded value.
174*4ffd72e3SWojciech Macek 	 */
175*4ffd72e3SWojciech Macek 	token = 0x112233;
176*4ffd72e3SWojciech Macek 
177*4ffd72e3SWojciech Macek 	memset(&msg, 0, sizeof(msg));
178*4ffd72e3SWojciech Macek 
179*4ffd72e3SWojciech Macek 	rc = opal_call(OPAL_I2C_REQUEST, token, bus_id,
180*4ffd72e3SWojciech Macek 	    pmap_kextract((uint64_t)req));
181*4ffd72e3SWojciech Macek 	if (rc != OPAL_ASYNC_COMPLETION)
182*4ffd72e3SWojciech Macek 		return (rc);
183*4ffd72e3SWojciech Macek 
184*4ffd72e3SWojciech Macek 	do {
185*4ffd72e3SWojciech Macek 		rc = opal_call(OPAL_CHECK_ASYNC_COMPLETION,
186*4ffd72e3SWojciech Macek 		    pmap_kextract((uint64_t)&msg), sizeof(msg), token);
187*4ffd72e3SWojciech Macek 	} while (rc == OPAL_BUSY);
188*4ffd72e3SWojciech Macek 
189*4ffd72e3SWojciech Macek 	if (rc != OPAL_SUCCESS)
190*4ffd72e3SWojciech Macek 		return (rc);
191*4ffd72e3SWojciech Macek 
192*4ffd72e3SWojciech Macek 	rc = opal_get_async_rc(msg);
193*4ffd72e3SWojciech Macek 
194*4ffd72e3SWojciech Macek 	return rc;
195*4ffd72e3SWojciech Macek }
196*4ffd72e3SWojciech Macek 
197*4ffd72e3SWojciech Macek static int
198*4ffd72e3SWojciech Macek opal_i2c_transfer(device_t dev, struct iic_msg *msgs, uint32_t nmsgs)
199*4ffd72e3SWojciech Macek {
200*4ffd72e3SWojciech Macek 	struct opal_i2c_softc *sc;
201*4ffd72e3SWojciech Macek 	int i, err = 0;
202*4ffd72e3SWojciech Macek 	struct opal_i2c_request req;
203*4ffd72e3SWojciech Macek 
204*4ffd72e3SWojciech Macek 	sc = device_get_softc(dev);
205*4ffd72e3SWojciech Macek 
206*4ffd72e3SWojciech Macek 	memset(&req, 0, sizeof(req));
207*4ffd72e3SWojciech Macek 
208*4ffd72e3SWojciech Macek 	/* XXX: Currently OPAL can parse only 1 message */
209*4ffd72e3SWojciech Macek 	if (nmsgs > 1) {
210*4ffd72e3SWojciech Macek 		device_printf(dev,
211*4ffd72e3SWojciech Macek 		    "trying to parse %d messages, while only 1 is supported\n", nmsgs);
212*4ffd72e3SWojciech Macek 		return (ENOMEM);
213*4ffd72e3SWojciech Macek 	}
214*4ffd72e3SWojciech Macek 
215*4ffd72e3SWojciech Macek 	I2C_LOCK(sc);
216*4ffd72e3SWojciech Macek 	for (i = 0; i < nmsgs; i++) {
217*4ffd72e3SWojciech Macek 		req.type = (msgs[i].flags & IIC_M_RD) ?
218*4ffd72e3SWojciech Macek 		    OPAL_I2C_RAW_READ : OPAL_I2C_RAW_WRITE;
219*4ffd72e3SWojciech Macek 		req.addr = htobe16(msgs[0].slave);
220*4ffd72e3SWojciech Macek 		req.size = htobe32(msgs[0].len);
221*4ffd72e3SWojciech Macek 		req.buffer_pa = htobe64(pmap_kextract((uint64_t)msgs[0].buf));
222*4ffd72e3SWojciech Macek 
223*4ffd72e3SWojciech Macek 		err = i2c_opal_send_request(sc->opal_id, &req);
224*4ffd72e3SWojciech Macek 	}
225*4ffd72e3SWojciech Macek 	I2C_UNLOCK(sc);
226*4ffd72e3SWojciech Macek 
227*4ffd72e3SWojciech Macek 	return (err);
228*4ffd72e3SWojciech Macek }
229*4ffd72e3SWojciech Macek 
230*4ffd72e3SWojciech Macek static int
231*4ffd72e3SWojciech Macek opal_i2c_callback(device_t dev, int index, caddr_t data)
232*4ffd72e3SWojciech Macek {
233*4ffd72e3SWojciech Macek 	int error = 0;
234*4ffd72e3SWojciech Macek 
235*4ffd72e3SWojciech Macek 	switch (index) {
236*4ffd72e3SWojciech Macek 	case IIC_REQUEST_BUS:
237*4ffd72e3SWojciech Macek 		break;
238*4ffd72e3SWojciech Macek 
239*4ffd72e3SWojciech Macek 	case IIC_RELEASE_BUS:
240*4ffd72e3SWojciech Macek 		break;
241*4ffd72e3SWojciech Macek 
242*4ffd72e3SWojciech Macek 	default:
243*4ffd72e3SWojciech Macek 		error = EINVAL;
244*4ffd72e3SWojciech Macek 	}
245*4ffd72e3SWojciech Macek 
246*4ffd72e3SWojciech Macek 	return (error);
247*4ffd72e3SWojciech Macek }
248*4ffd72e3SWojciech Macek 
249*4ffd72e3SWojciech Macek DRIVER_MODULE(opal_i2c, opal_i2cm, opal_i2c_driver, opal_i2c_devclass, NULL,
250*4ffd72e3SWojciech Macek     NULL);
251*4ffd72e3SWojciech Macek DRIVER_MODULE(iicbus, opal_i2c, iicbus_driver, iicbus_devclass, NULL, NULL);
252*4ffd72e3SWojciech Macek MODULE_DEPEND(opal_i2c, iicbus, 1, 1, 1);
253