14ffd72e3SWojciech Macek /*- 24ffd72e3SWojciech Macek * Copyright (c) 2017-2018 QCM Technologies. 34ffd72e3SWojciech Macek * Copyright (c) 2017-2018 Semihalf. 44ffd72e3SWojciech Macek * All rights reserved. 54ffd72e3SWojciech Macek * 64ffd72e3SWojciech Macek * Redistribution and use in source and binary forms, with or without 74ffd72e3SWojciech Macek * modification, are permitted provided that the following conditions 84ffd72e3SWojciech Macek * are met: 94ffd72e3SWojciech Macek * 1. Redistributions of source code must retain the above copyright 104ffd72e3SWojciech Macek * notice, this list of conditions and the following disclaimer. 114ffd72e3SWojciech Macek * 2. Redistributions in binary form must reproduce the above copyright 124ffd72e3SWojciech Macek * notice, this list of conditions and the following disclaimer in the 134ffd72e3SWojciech Macek * documentation and/or other materials provided with the distribution. 144ffd72e3SWojciech Macek * 154ffd72e3SWojciech Macek * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 164ffd72e3SWojciech Macek * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 174ffd72e3SWojciech Macek * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 184ffd72e3SWojciech Macek * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 194ffd72e3SWojciech Macek * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 204ffd72e3SWojciech Macek * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 214ffd72e3SWojciech Macek * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 224ffd72e3SWojciech Macek * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 234ffd72e3SWojciech Macek * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 244ffd72e3SWojciech Macek * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 254ffd72e3SWojciech Macek * SUCH DAMAGE. 264ffd72e3SWojciech Macek * 274ffd72e3SWojciech Macek * $FreeBSD$ 284ffd72e3SWojciech Macek */ 294ffd72e3SWojciech Macek 304ffd72e3SWojciech Macek #include "opt_platform.h" 314ffd72e3SWojciech Macek 324ffd72e3SWojciech Macek #include <sys/cdefs.h> 334ffd72e3SWojciech Macek __FBSDID("$FreeBSD$"); 344ffd72e3SWojciech Macek 354ffd72e3SWojciech Macek #include <sys/param.h> 364ffd72e3SWojciech Macek #include <sys/endian.h> 374ffd72e3SWojciech Macek #include <sys/systm.h> 384ffd72e3SWojciech Macek #include <sys/bus.h> 394ffd72e3SWojciech Macek #include <sys/conf.h> 404ffd72e3SWojciech Macek #include <sys/kernel.h> 414ffd72e3SWojciech Macek #include <sys/lock.h> 424ffd72e3SWojciech Macek #include <sys/mbuf.h> 434ffd72e3SWojciech Macek #include <sys/malloc.h> 444ffd72e3SWojciech Macek #include <sys/module.h> 454ffd72e3SWojciech Macek #include <sys/mutex.h> 464ffd72e3SWojciech Macek #include <sys/rman.h> 474ffd72e3SWojciech Macek #include <machine/bus.h> 484ffd72e3SWojciech Macek 494ffd72e3SWojciech Macek #include <vm/vm.h> 504ffd72e3SWojciech Macek #include <vm/pmap.h> 514ffd72e3SWojciech Macek 524ffd72e3SWojciech Macek #include <dev/iicbus/iiconf.h> 534ffd72e3SWojciech Macek #include <dev/iicbus/iicbus.h> 544ffd72e3SWojciech Macek #include "iicbus_if.h" 554ffd72e3SWojciech Macek 564ffd72e3SWojciech Macek #include "opal.h" 574ffd72e3SWojciech Macek 584ffd72e3SWojciech Macek #ifdef FDT 594ffd72e3SWojciech Macek #include <dev/ofw/ofw_bus.h> 604ffd72e3SWojciech Macek #include <dev/ofw/ofw_bus_subr.h> 614ffd72e3SWojciech Macek #endif 624ffd72e3SWojciech Macek 634ffd72e3SWojciech Macek struct opal_i2c_softc 644ffd72e3SWojciech Macek { 654ffd72e3SWojciech Macek device_t dev; 664ffd72e3SWojciech Macek device_t iicbus; 674ffd72e3SWojciech Macek uint32_t opal_id; 684ffd72e3SWojciech Macek struct mtx sc_mtx; 694ffd72e3SWojciech Macek }; 704ffd72e3SWojciech Macek 714ffd72e3SWojciech Macek /* OPAL I2C request */ 724ffd72e3SWojciech Macek struct opal_i2c_request { 734ffd72e3SWojciech Macek uint8_t type; 744ffd72e3SWojciech Macek #define OPAL_I2C_RAW_READ 0 754ffd72e3SWojciech Macek #define OPAL_I2C_RAW_WRITE 1 764ffd72e3SWojciech Macek #define OPAL_I2C_SM_READ 2 774ffd72e3SWojciech Macek #define OPAL_I2C_SM_WRITE 3 784ffd72e3SWojciech Macek uint8_t flags; 794ffd72e3SWojciech Macek uint8_t subaddr_sz; /* Max 4 */ 804ffd72e3SWojciech Macek uint8_t reserved; 814ffd72e3SWojciech Macek uint16_t addr; /* 7 or 10 bit address */ 824ffd72e3SWojciech Macek uint16_t reserved2; 834ffd72e3SWojciech Macek uint32_t subaddr; /* Sub-address if any */ 844ffd72e3SWojciech Macek uint32_t size; /* Data size */ 854ffd72e3SWojciech Macek uint64_t buffer_pa; /* Buffer real address */ 864ffd72e3SWojciech Macek }; 874ffd72e3SWojciech Macek 884ffd72e3SWojciech Macek static int opal_i2c_attach(device_t); 894ffd72e3SWojciech Macek static int opal_i2c_callback(device_t, int, caddr_t); 904ffd72e3SWojciech Macek static int opal_i2c_probe(device_t); 914ffd72e3SWojciech Macek static int opal_i2c_transfer(device_t, struct iic_msg *, uint32_t); 924ffd72e3SWojciech Macek static int i2c_opal_send_request(uint32_t, struct opal_i2c_request *); 93*fedd55f1SJustin Hibbits static phandle_t opal_i2c_get_node(device_t bus, device_t dev); 944ffd72e3SWojciech Macek 954ffd72e3SWojciech Macek static device_method_t opal_i2c_methods[] = { 964ffd72e3SWojciech Macek /* Device interface */ 974ffd72e3SWojciech Macek DEVMETHOD(device_probe, opal_i2c_probe), 984ffd72e3SWojciech Macek DEVMETHOD(device_attach, opal_i2c_attach), 994ffd72e3SWojciech Macek 1004ffd72e3SWojciech Macek /* iicbus interface */ 1014ffd72e3SWojciech Macek DEVMETHOD(iicbus_callback, opal_i2c_callback), 1024ffd72e3SWojciech Macek DEVMETHOD(iicbus_transfer, opal_i2c_transfer), 103*fedd55f1SJustin Hibbits DEVMETHOD(ofw_bus_get_node, opal_i2c_get_node), 1044ffd72e3SWojciech Macek DEVMETHOD_END 1054ffd72e3SWojciech Macek }; 1064ffd72e3SWojciech Macek 1074ffd72e3SWojciech Macek #define I2C_LOCK(_sc) mtx_lock(&(_sc)->sc_mtx) 1084ffd72e3SWojciech Macek #define I2C_UNLOCK(_sc) mtx_unlock(&(_sc)->sc_mtx) 1094ffd72e3SWojciech Macek #define I2C_LOCK_INIT(_sc) \ 1104ffd72e3SWojciech Macek mtx_init(&_sc->sc_mtx, device_get_nameunit(_sc->dev), \ 1114ffd72e3SWojciech Macek "i2c", MTX_DEF) 1124ffd72e3SWojciech Macek 1134ffd72e3SWojciech Macek static devclass_t opal_i2c_devclass; 1144ffd72e3SWojciech Macek 1154ffd72e3SWojciech Macek static driver_t opal_i2c_driver = { 116*fedd55f1SJustin Hibbits "iichb", 1174ffd72e3SWojciech Macek opal_i2c_methods, 1184ffd72e3SWojciech Macek sizeof(struct opal_i2c_softc), 1194ffd72e3SWojciech Macek }; 1204ffd72e3SWojciech Macek 1214ffd72e3SWojciech Macek static int 1224ffd72e3SWojciech Macek opal_i2c_probe(device_t dev) 1234ffd72e3SWojciech Macek { 1244ffd72e3SWojciech Macek 1254ffd72e3SWojciech Macek if (!(ofw_bus_is_compatible(dev, "ibm,opal-i2c"))) 1264ffd72e3SWojciech Macek return (ENXIO); 1274ffd72e3SWojciech Macek 1284ffd72e3SWojciech Macek device_set_desc(dev, "opal-i2c"); 1294ffd72e3SWojciech Macek 1304ffd72e3SWojciech Macek return (0); 1314ffd72e3SWojciech Macek } 1324ffd72e3SWojciech Macek 1334ffd72e3SWojciech Macek static int 1344ffd72e3SWojciech Macek opal_i2c_attach(device_t dev) 1354ffd72e3SWojciech Macek { 1364ffd72e3SWojciech Macek struct opal_i2c_softc *sc; 1374ffd72e3SWojciech Macek int len; 1384ffd72e3SWojciech Macek 1394ffd72e3SWojciech Macek sc = device_get_softc(dev); 1404ffd72e3SWojciech Macek sc->dev = dev; 1414ffd72e3SWojciech Macek 1424ffd72e3SWojciech Macek len = OF_getproplen(ofw_bus_get_node(dev), "ibm,opal-id"); 1434ffd72e3SWojciech Macek if (len <= 0) 1444ffd72e3SWojciech Macek return (EINVAL); 1454ffd72e3SWojciech Macek OF_getencprop(ofw_bus_get_node(dev), "ibm,opal-id", &sc->opal_id, len); 1464ffd72e3SWojciech Macek 1474ffd72e3SWojciech Macek if ((sc->iicbus = device_add_child(dev, "iicbus", -1)) == NULL) { 1484ffd72e3SWojciech Macek device_printf(dev, "could not allocate iicbus instance\n"); 1494ffd72e3SWojciech Macek return (EINVAL); 1504ffd72e3SWojciech Macek } 1514ffd72e3SWojciech Macek 1524ffd72e3SWojciech Macek I2C_LOCK_INIT(sc); 1534ffd72e3SWojciech Macek 1544ffd72e3SWojciech Macek return (bus_generic_attach(dev)); 1554ffd72e3SWojciech Macek } 1564ffd72e3SWojciech Macek 1574ffd72e3SWojciech Macek static int 1584ffd72e3SWojciech Macek opal_get_async_rc(struct opal_msg msg) 1594ffd72e3SWojciech Macek { 1604ffd72e3SWojciech Macek if (msg.msg_type != OPAL_MSG_ASYNC_COMP) 1614ffd72e3SWojciech Macek return OPAL_PARAMETER; 1624ffd72e3SWojciech Macek else 1634ffd72e3SWojciech Macek return htobe64(msg.params[1]); 1644ffd72e3SWojciech Macek } 1654ffd72e3SWojciech Macek 1664ffd72e3SWojciech Macek static int 1674ffd72e3SWojciech Macek i2c_opal_send_request(uint32_t bus_id, struct opal_i2c_request *req) 1684ffd72e3SWojciech Macek { 1694ffd72e3SWojciech Macek struct opal_msg msg; 1704ffd72e3SWojciech Macek int token, rc; 1714ffd72e3SWojciech Macek 1724ffd72e3SWojciech Macek /* 1734ffd72e3SWojciech Macek * XXX: 1744ffd72e3SWojciech Macek * Async tokens should be managed globally. Since there is 1754ffd72e3SWojciech Macek * only one place now, use hardcoded value. 1764ffd72e3SWojciech Macek */ 1774ffd72e3SWojciech Macek token = 0x112233; 1784ffd72e3SWojciech Macek 1794ffd72e3SWojciech Macek memset(&msg, 0, sizeof(msg)); 1804ffd72e3SWojciech Macek 1814ffd72e3SWojciech Macek rc = opal_call(OPAL_I2C_REQUEST, token, bus_id, 1824ffd72e3SWojciech Macek pmap_kextract((uint64_t)req)); 1834ffd72e3SWojciech Macek if (rc != OPAL_ASYNC_COMPLETION) 1844ffd72e3SWojciech Macek return (rc); 1854ffd72e3SWojciech Macek 1864ffd72e3SWojciech Macek do { 1874ffd72e3SWojciech Macek rc = opal_call(OPAL_CHECK_ASYNC_COMPLETION, 1884ffd72e3SWojciech Macek pmap_kextract((uint64_t)&msg), sizeof(msg), token); 1894ffd72e3SWojciech Macek } while (rc == OPAL_BUSY); 1904ffd72e3SWojciech Macek 1914ffd72e3SWojciech Macek if (rc != OPAL_SUCCESS) 1924ffd72e3SWojciech Macek return (rc); 1934ffd72e3SWojciech Macek 1944ffd72e3SWojciech Macek rc = opal_get_async_rc(msg); 1954ffd72e3SWojciech Macek 1964ffd72e3SWojciech Macek return rc; 1974ffd72e3SWojciech Macek } 1984ffd72e3SWojciech Macek 1994ffd72e3SWojciech Macek static int 2004ffd72e3SWojciech Macek opal_i2c_transfer(device_t dev, struct iic_msg *msgs, uint32_t nmsgs) 2014ffd72e3SWojciech Macek { 2024ffd72e3SWojciech Macek struct opal_i2c_softc *sc; 2034ffd72e3SWojciech Macek int i, err = 0; 2044ffd72e3SWojciech Macek struct opal_i2c_request req; 2054ffd72e3SWojciech Macek 2064ffd72e3SWojciech Macek sc = device_get_softc(dev); 2074ffd72e3SWojciech Macek 2084ffd72e3SWojciech Macek memset(&req, 0, sizeof(req)); 2094ffd72e3SWojciech Macek 2104ffd72e3SWojciech Macek /* XXX: Currently OPAL can parse only 1 message */ 2114ffd72e3SWojciech Macek if (nmsgs > 1) { 2124ffd72e3SWojciech Macek device_printf(dev, 2134ffd72e3SWojciech Macek "trying to parse %d messages, while only 1 is supported\n", nmsgs); 2144ffd72e3SWojciech Macek return (ENOMEM); 2154ffd72e3SWojciech Macek } 2164ffd72e3SWojciech Macek 2174ffd72e3SWojciech Macek I2C_LOCK(sc); 2184ffd72e3SWojciech Macek for (i = 0; i < nmsgs; i++) { 2194ffd72e3SWojciech Macek req.type = (msgs[i].flags & IIC_M_RD) ? 2204ffd72e3SWojciech Macek OPAL_I2C_RAW_READ : OPAL_I2C_RAW_WRITE; 2214ffd72e3SWojciech Macek req.addr = htobe16(msgs[0].slave); 2224ffd72e3SWojciech Macek req.size = htobe32(msgs[0].len); 2234ffd72e3SWojciech Macek req.buffer_pa = htobe64(pmap_kextract((uint64_t)msgs[0].buf)); 2244ffd72e3SWojciech Macek 2254ffd72e3SWojciech Macek err = i2c_opal_send_request(sc->opal_id, &req); 2264ffd72e3SWojciech Macek } 2274ffd72e3SWojciech Macek I2C_UNLOCK(sc); 2284ffd72e3SWojciech Macek 2294ffd72e3SWojciech Macek return (err); 2304ffd72e3SWojciech Macek } 2314ffd72e3SWojciech Macek 2324ffd72e3SWojciech Macek static int 2334ffd72e3SWojciech Macek opal_i2c_callback(device_t dev, int index, caddr_t data) 2344ffd72e3SWojciech Macek { 2354ffd72e3SWojciech Macek int error = 0; 2364ffd72e3SWojciech Macek 2374ffd72e3SWojciech Macek switch (index) { 2384ffd72e3SWojciech Macek case IIC_REQUEST_BUS: 2394ffd72e3SWojciech Macek break; 2404ffd72e3SWojciech Macek 2414ffd72e3SWojciech Macek case IIC_RELEASE_BUS: 2424ffd72e3SWojciech Macek break; 2434ffd72e3SWojciech Macek 2444ffd72e3SWojciech Macek default: 2454ffd72e3SWojciech Macek error = EINVAL; 2464ffd72e3SWojciech Macek } 2474ffd72e3SWojciech Macek 2484ffd72e3SWojciech Macek return (error); 2494ffd72e3SWojciech Macek } 2504ffd72e3SWojciech Macek 251*fedd55f1SJustin Hibbits static phandle_t 252*fedd55f1SJustin Hibbits opal_i2c_get_node(device_t bus, device_t dev) 253*fedd55f1SJustin Hibbits { 254*fedd55f1SJustin Hibbits 255*fedd55f1SJustin Hibbits /* Share controller node with iibus device. */ 256*fedd55f1SJustin Hibbits return (ofw_bus_get_node(bus)); 257*fedd55f1SJustin Hibbits } 258*fedd55f1SJustin Hibbits 2594ffd72e3SWojciech Macek DRIVER_MODULE(opal_i2c, opal_i2cm, opal_i2c_driver, opal_i2c_devclass, NULL, 2604ffd72e3SWojciech Macek NULL); 2614ffd72e3SWojciech Macek DRIVER_MODULE(iicbus, opal_i2c, iicbus_driver, iicbus_devclass, NULL, NULL); 2624ffd72e3SWojciech Macek MODULE_DEPEND(opal_i2c, iicbus, 1, 1, 1); 263