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 284ffd72e3SWojciech Macek #include "opt_platform.h" 294ffd72e3SWojciech Macek 304ffd72e3SWojciech Macek #include <sys/param.h> 314ffd72e3SWojciech Macek #include <sys/endian.h> 324ffd72e3SWojciech Macek #include <sys/systm.h> 334ffd72e3SWojciech Macek #include <sys/bus.h> 344ffd72e3SWojciech Macek #include <sys/conf.h> 354ffd72e3SWojciech Macek #include <sys/kernel.h> 364ffd72e3SWojciech Macek #include <sys/lock.h> 374ffd72e3SWojciech Macek #include <sys/mbuf.h> 384ffd72e3SWojciech Macek #include <sys/malloc.h> 394ffd72e3SWojciech Macek #include <sys/module.h> 404ffd72e3SWojciech Macek #include <sys/mutex.h> 414ffd72e3SWojciech Macek #include <sys/rman.h> 424ffd72e3SWojciech Macek #include <machine/bus.h> 434ffd72e3SWojciech Macek 444ffd72e3SWojciech Macek #include <vm/vm.h> 454ffd72e3SWojciech Macek #include <vm/pmap.h> 464ffd72e3SWojciech Macek 474ffd72e3SWojciech Macek #include <dev/iicbus/iiconf.h> 484ffd72e3SWojciech Macek #include <dev/iicbus/iicbus.h> 494ffd72e3SWojciech Macek #include "iicbus_if.h" 504ffd72e3SWojciech Macek 514ffd72e3SWojciech Macek #include "opal.h" 524ffd72e3SWojciech Macek 534ffd72e3SWojciech Macek #ifdef FDT 544ffd72e3SWojciech Macek #include <dev/ofw/ofw_bus.h> 554ffd72e3SWojciech Macek #include <dev/ofw/ofw_bus_subr.h> 564ffd72e3SWojciech Macek #endif 574ffd72e3SWojciech Macek 584ffd72e3SWojciech Macek struct opal_i2c_softc 594ffd72e3SWojciech Macek { 604ffd72e3SWojciech Macek device_t dev; 614ffd72e3SWojciech Macek device_t iicbus; 624ffd72e3SWojciech Macek uint32_t opal_id; 634ffd72e3SWojciech Macek struct mtx sc_mtx; 644ffd72e3SWojciech Macek }; 654ffd72e3SWojciech Macek 664ffd72e3SWojciech Macek /* OPAL I2C request */ 674ffd72e3SWojciech Macek struct opal_i2c_request { 684ffd72e3SWojciech Macek uint8_t type; 694ffd72e3SWojciech Macek #define OPAL_I2C_RAW_READ 0 704ffd72e3SWojciech Macek #define OPAL_I2C_RAW_WRITE 1 714ffd72e3SWojciech Macek #define OPAL_I2C_SM_READ 2 724ffd72e3SWojciech Macek #define OPAL_I2C_SM_WRITE 3 734ffd72e3SWojciech Macek uint8_t flags; 744ffd72e3SWojciech Macek uint8_t subaddr_sz; /* Max 4 */ 754ffd72e3SWojciech Macek uint8_t reserved; 764ffd72e3SWojciech Macek uint16_t addr; /* 7 or 10 bit address */ 774ffd72e3SWojciech Macek uint16_t reserved2; 784ffd72e3SWojciech Macek uint32_t subaddr; /* Sub-address if any */ 794ffd72e3SWojciech Macek uint32_t size; /* Data size */ 804ffd72e3SWojciech Macek uint64_t buffer_pa; /* Buffer real address */ 814ffd72e3SWojciech Macek }; 824ffd72e3SWojciech Macek 834ffd72e3SWojciech Macek static int opal_i2c_attach(device_t); 844ffd72e3SWojciech Macek static int opal_i2c_callback(device_t, int, caddr_t); 854ffd72e3SWojciech Macek static int opal_i2c_probe(device_t); 864ffd72e3SWojciech Macek static int opal_i2c_transfer(device_t, struct iic_msg *, uint32_t); 874ffd72e3SWojciech Macek static int i2c_opal_send_request(uint32_t, struct opal_i2c_request *); 88fedd55f1SJustin Hibbits static phandle_t opal_i2c_get_node(device_t bus, device_t dev); 894ffd72e3SWojciech Macek 904ffd72e3SWojciech Macek static device_method_t opal_i2c_methods[] = { 914ffd72e3SWojciech Macek /* Device interface */ 924ffd72e3SWojciech Macek DEVMETHOD(device_probe, opal_i2c_probe), 934ffd72e3SWojciech Macek DEVMETHOD(device_attach, opal_i2c_attach), 944ffd72e3SWojciech Macek 954ffd72e3SWojciech Macek /* iicbus interface */ 964ffd72e3SWojciech Macek DEVMETHOD(iicbus_callback, opal_i2c_callback), 974ffd72e3SWojciech Macek DEVMETHOD(iicbus_transfer, opal_i2c_transfer), 98fedd55f1SJustin Hibbits DEVMETHOD(ofw_bus_get_node, opal_i2c_get_node), 994ffd72e3SWojciech Macek DEVMETHOD_END 1004ffd72e3SWojciech Macek }; 1014ffd72e3SWojciech Macek 1024ffd72e3SWojciech Macek #define I2C_LOCK(_sc) mtx_lock(&(_sc)->sc_mtx) 1034ffd72e3SWojciech Macek #define I2C_UNLOCK(_sc) mtx_unlock(&(_sc)->sc_mtx) 1044ffd72e3SWojciech Macek #define I2C_LOCK_INIT(_sc) \ 1054ffd72e3SWojciech Macek mtx_init(&_sc->sc_mtx, device_get_nameunit(_sc->dev), \ 1064ffd72e3SWojciech Macek "i2c", MTX_DEF) 1074ffd72e3SWojciech Macek 1084ffd72e3SWojciech Macek static driver_t opal_i2c_driver = { 109fedd55f1SJustin Hibbits "iichb", 1104ffd72e3SWojciech Macek opal_i2c_methods, 1114ffd72e3SWojciech Macek sizeof(struct opal_i2c_softc), 1124ffd72e3SWojciech Macek }; 1134ffd72e3SWojciech Macek 1144ffd72e3SWojciech Macek static int 1154ffd72e3SWojciech Macek opal_i2c_probe(device_t dev) 1164ffd72e3SWojciech Macek { 1174ffd72e3SWojciech Macek 1184ffd72e3SWojciech Macek if (!(ofw_bus_is_compatible(dev, "ibm,opal-i2c"))) 1194ffd72e3SWojciech Macek return (ENXIO); 1204ffd72e3SWojciech Macek 1214ffd72e3SWojciech Macek device_set_desc(dev, "opal-i2c"); 1224ffd72e3SWojciech Macek 1234ffd72e3SWojciech Macek return (0); 1244ffd72e3SWojciech Macek } 1254ffd72e3SWojciech Macek 1264ffd72e3SWojciech Macek static int 1274ffd72e3SWojciech Macek opal_i2c_attach(device_t dev) 1284ffd72e3SWojciech Macek { 1294ffd72e3SWojciech Macek struct opal_i2c_softc *sc; 1304ffd72e3SWojciech Macek int len; 1314ffd72e3SWojciech Macek 1324ffd72e3SWojciech Macek sc = device_get_softc(dev); 1334ffd72e3SWojciech Macek sc->dev = dev; 1344ffd72e3SWojciech Macek 1354ffd72e3SWojciech Macek len = OF_getproplen(ofw_bus_get_node(dev), "ibm,opal-id"); 1364ffd72e3SWojciech Macek if (len <= 0) 1374ffd72e3SWojciech Macek return (EINVAL); 1384ffd72e3SWojciech Macek OF_getencprop(ofw_bus_get_node(dev), "ibm,opal-id", &sc->opal_id, len); 1394ffd72e3SWojciech Macek 1404ffd72e3SWojciech Macek if ((sc->iicbus = device_add_child(dev, "iicbus", -1)) == NULL) { 1414ffd72e3SWojciech Macek device_printf(dev, "could not allocate iicbus instance\n"); 1424ffd72e3SWojciech Macek return (EINVAL); 1434ffd72e3SWojciech Macek } 1444ffd72e3SWojciech Macek 1454ffd72e3SWojciech Macek I2C_LOCK_INIT(sc); 1464ffd72e3SWojciech Macek 147*18250ec6SJohn Baldwin bus_attach_children(dev); 148*18250ec6SJohn Baldwin return (0); 1494ffd72e3SWojciech Macek } 1504ffd72e3SWojciech Macek 1514ffd72e3SWojciech Macek static int 1524ffd72e3SWojciech Macek opal_get_async_rc(struct opal_msg msg) 1534ffd72e3SWojciech Macek { 1544ffd72e3SWojciech Macek if (msg.msg_type != OPAL_MSG_ASYNC_COMP) 1554ffd72e3SWojciech Macek return OPAL_PARAMETER; 1564ffd72e3SWojciech Macek else 1574ffd72e3SWojciech Macek return htobe64(msg.params[1]); 1584ffd72e3SWojciech Macek } 1594ffd72e3SWojciech Macek 1604ffd72e3SWojciech Macek static int 1614ffd72e3SWojciech Macek i2c_opal_send_request(uint32_t bus_id, struct opal_i2c_request *req) 1624ffd72e3SWojciech Macek { 1634ffd72e3SWojciech Macek struct opal_msg msg; 1643395ab28SJustin Hibbits uint64_t token; 1653395ab28SJustin Hibbits int rc; 1664ffd72e3SWojciech Macek 167dac618a6SJustin Hibbits token = opal_alloc_async_token(); 1684ffd72e3SWojciech Macek 1694ffd72e3SWojciech Macek memset(&msg, 0, sizeof(msg)); 1704ffd72e3SWojciech Macek 1714ffd72e3SWojciech Macek rc = opal_call(OPAL_I2C_REQUEST, token, bus_id, 172dac618a6SJustin Hibbits vtophys(req)); 1734ffd72e3SWojciech Macek if (rc != OPAL_ASYNC_COMPLETION) 174dac618a6SJustin Hibbits goto out; 1754ffd72e3SWojciech Macek 176dac618a6SJustin Hibbits rc = opal_wait_completion(&msg, sizeof(msg), token); 1774ffd72e3SWojciech Macek 1784ffd72e3SWojciech Macek if (rc != OPAL_SUCCESS) 179dac618a6SJustin Hibbits goto out; 1804ffd72e3SWojciech Macek 1814ffd72e3SWojciech Macek rc = opal_get_async_rc(msg); 1824ffd72e3SWojciech Macek 183dac618a6SJustin Hibbits out: 184dac618a6SJustin Hibbits opal_free_async_token(token); 185dac618a6SJustin Hibbits 186dac618a6SJustin Hibbits return (rc); 1874ffd72e3SWojciech Macek } 1884ffd72e3SWojciech Macek 1894ffd72e3SWojciech Macek static int 1904ffd72e3SWojciech Macek opal_i2c_transfer(device_t dev, struct iic_msg *msgs, uint32_t nmsgs) 1914ffd72e3SWojciech Macek { 1924ffd72e3SWojciech Macek struct opal_i2c_softc *sc; 1934ffd72e3SWojciech Macek int i, err = 0; 1944ffd72e3SWojciech Macek struct opal_i2c_request req; 1954ffd72e3SWojciech Macek 1964ffd72e3SWojciech Macek sc = device_get_softc(dev); 1974ffd72e3SWojciech Macek 1984ffd72e3SWojciech Macek memset(&req, 0, sizeof(req)); 1994ffd72e3SWojciech Macek 2004ffd72e3SWojciech Macek I2C_LOCK(sc); 2014ffd72e3SWojciech Macek for (i = 0; i < nmsgs; i++) { 2024ffd72e3SWojciech Macek req.type = (msgs[i].flags & IIC_M_RD) ? 2034ffd72e3SWojciech Macek OPAL_I2C_RAW_READ : OPAL_I2C_RAW_WRITE; 2043395ab28SJustin Hibbits req.addr = htobe16(msgs[i].slave >> 1); 2053395ab28SJustin Hibbits req.size = htobe32(msgs[i].len); 2063395ab28SJustin Hibbits req.buffer_pa = htobe64(pmap_kextract((uint64_t)msgs[i].buf)); 2074ffd72e3SWojciech Macek 2084ffd72e3SWojciech Macek err = i2c_opal_send_request(sc->opal_id, &req); 2094ffd72e3SWojciech Macek } 2104ffd72e3SWojciech Macek I2C_UNLOCK(sc); 2114ffd72e3SWojciech Macek 2124ffd72e3SWojciech Macek return (err); 2134ffd72e3SWojciech Macek } 2144ffd72e3SWojciech Macek 2154ffd72e3SWojciech Macek static int 2164ffd72e3SWojciech Macek opal_i2c_callback(device_t dev, int index, caddr_t data) 2174ffd72e3SWojciech Macek { 2184ffd72e3SWojciech Macek int error = 0; 2194ffd72e3SWojciech Macek 2204ffd72e3SWojciech Macek switch (index) { 2214ffd72e3SWojciech Macek case IIC_REQUEST_BUS: 2224ffd72e3SWojciech Macek break; 2234ffd72e3SWojciech Macek 2244ffd72e3SWojciech Macek case IIC_RELEASE_BUS: 2254ffd72e3SWojciech Macek break; 2264ffd72e3SWojciech Macek 2274ffd72e3SWojciech Macek default: 2284ffd72e3SWojciech Macek error = EINVAL; 2294ffd72e3SWojciech Macek } 2304ffd72e3SWojciech Macek 2314ffd72e3SWojciech Macek return (error); 2324ffd72e3SWojciech Macek } 2334ffd72e3SWojciech Macek 234fedd55f1SJustin Hibbits static phandle_t 235fedd55f1SJustin Hibbits opal_i2c_get_node(device_t bus, device_t dev) 236fedd55f1SJustin Hibbits { 237fedd55f1SJustin Hibbits 238fedd55f1SJustin Hibbits /* Share controller node with iibus device. */ 239fedd55f1SJustin Hibbits return (ofw_bus_get_node(bus)); 240fedd55f1SJustin Hibbits } 241fedd55f1SJustin Hibbits 2425edf159fSJohn Baldwin DRIVER_MODULE(opal_i2c, opal_i2cm, opal_i2c_driver, NULL, NULL); 243676ea8e1SJohn Baldwin DRIVER_MODULE(iicbus, opal_i2c, iicbus_driver, NULL, NULL); 2444ffd72e3SWojciech Macek MODULE_DEPEND(opal_i2c, iicbus, 1, 1, 1); 245