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 *); 93fedd55f1SJustin 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), 103fedd55f1SJustin 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 driver_t opal_i2c_driver = { 114fedd55f1SJustin Hibbits "iichb", 1154ffd72e3SWojciech Macek opal_i2c_methods, 1164ffd72e3SWojciech Macek sizeof(struct opal_i2c_softc), 1174ffd72e3SWojciech Macek }; 1184ffd72e3SWojciech Macek 1194ffd72e3SWojciech Macek static int 1204ffd72e3SWojciech Macek opal_i2c_probe(device_t dev) 1214ffd72e3SWojciech Macek { 1224ffd72e3SWojciech Macek 1234ffd72e3SWojciech Macek if (!(ofw_bus_is_compatible(dev, "ibm,opal-i2c"))) 1244ffd72e3SWojciech Macek return (ENXIO); 1254ffd72e3SWojciech Macek 1264ffd72e3SWojciech Macek device_set_desc(dev, "opal-i2c"); 1274ffd72e3SWojciech Macek 1284ffd72e3SWojciech Macek return (0); 1294ffd72e3SWojciech Macek } 1304ffd72e3SWojciech Macek 1314ffd72e3SWojciech Macek static int 1324ffd72e3SWojciech Macek opal_i2c_attach(device_t dev) 1334ffd72e3SWojciech Macek { 1344ffd72e3SWojciech Macek struct opal_i2c_softc *sc; 1354ffd72e3SWojciech Macek int len; 1364ffd72e3SWojciech Macek 1374ffd72e3SWojciech Macek sc = device_get_softc(dev); 1384ffd72e3SWojciech Macek sc->dev = dev; 1394ffd72e3SWojciech Macek 1404ffd72e3SWojciech Macek len = OF_getproplen(ofw_bus_get_node(dev), "ibm,opal-id"); 1414ffd72e3SWojciech Macek if (len <= 0) 1424ffd72e3SWojciech Macek return (EINVAL); 1434ffd72e3SWojciech Macek OF_getencprop(ofw_bus_get_node(dev), "ibm,opal-id", &sc->opal_id, len); 1444ffd72e3SWojciech Macek 1454ffd72e3SWojciech Macek if ((sc->iicbus = device_add_child(dev, "iicbus", -1)) == NULL) { 1464ffd72e3SWojciech Macek device_printf(dev, "could not allocate iicbus instance\n"); 1474ffd72e3SWojciech Macek return (EINVAL); 1484ffd72e3SWojciech Macek } 1494ffd72e3SWojciech Macek 1504ffd72e3SWojciech Macek I2C_LOCK_INIT(sc); 1514ffd72e3SWojciech Macek 1524ffd72e3SWojciech Macek return (bus_generic_attach(dev)); 1534ffd72e3SWojciech Macek } 1544ffd72e3SWojciech Macek 1554ffd72e3SWojciech Macek static int 1564ffd72e3SWojciech Macek opal_get_async_rc(struct opal_msg msg) 1574ffd72e3SWojciech Macek { 1584ffd72e3SWojciech Macek if (msg.msg_type != OPAL_MSG_ASYNC_COMP) 1594ffd72e3SWojciech Macek return OPAL_PARAMETER; 1604ffd72e3SWojciech Macek else 1614ffd72e3SWojciech Macek return htobe64(msg.params[1]); 1624ffd72e3SWojciech Macek } 1634ffd72e3SWojciech Macek 1644ffd72e3SWojciech Macek static int 1654ffd72e3SWojciech Macek i2c_opal_send_request(uint32_t bus_id, struct opal_i2c_request *req) 1664ffd72e3SWojciech Macek { 1674ffd72e3SWojciech Macek struct opal_msg msg; 1683395ab28SJustin Hibbits uint64_t token; 1693395ab28SJustin Hibbits int rc; 1704ffd72e3SWojciech Macek 171dac618a6SJustin Hibbits token = opal_alloc_async_token(); 1724ffd72e3SWojciech Macek 1734ffd72e3SWojciech Macek memset(&msg, 0, sizeof(msg)); 1744ffd72e3SWojciech Macek 1754ffd72e3SWojciech Macek rc = opal_call(OPAL_I2C_REQUEST, token, bus_id, 176dac618a6SJustin Hibbits vtophys(req)); 1774ffd72e3SWojciech Macek if (rc != OPAL_ASYNC_COMPLETION) 178dac618a6SJustin Hibbits goto out; 1794ffd72e3SWojciech Macek 180dac618a6SJustin Hibbits rc = opal_wait_completion(&msg, sizeof(msg), token); 1814ffd72e3SWojciech Macek 1824ffd72e3SWojciech Macek if (rc != OPAL_SUCCESS) 183dac618a6SJustin Hibbits goto out; 1844ffd72e3SWojciech Macek 1854ffd72e3SWojciech Macek rc = opal_get_async_rc(msg); 1864ffd72e3SWojciech Macek 187dac618a6SJustin Hibbits out: 188dac618a6SJustin Hibbits opal_free_async_token(token); 189dac618a6SJustin Hibbits 190dac618a6SJustin Hibbits return (rc); 1914ffd72e3SWojciech Macek } 1924ffd72e3SWojciech Macek 1934ffd72e3SWojciech Macek static int 1944ffd72e3SWojciech Macek opal_i2c_transfer(device_t dev, struct iic_msg *msgs, uint32_t nmsgs) 1954ffd72e3SWojciech Macek { 1964ffd72e3SWojciech Macek struct opal_i2c_softc *sc; 1974ffd72e3SWojciech Macek int i, err = 0; 1984ffd72e3SWojciech Macek struct opal_i2c_request req; 1994ffd72e3SWojciech Macek 2004ffd72e3SWojciech Macek sc = device_get_softc(dev); 2014ffd72e3SWojciech Macek 2024ffd72e3SWojciech Macek memset(&req, 0, sizeof(req)); 2034ffd72e3SWojciech Macek 2044ffd72e3SWojciech Macek I2C_LOCK(sc); 2054ffd72e3SWojciech Macek for (i = 0; i < nmsgs; i++) { 2064ffd72e3SWojciech Macek req.type = (msgs[i].flags & IIC_M_RD) ? 2074ffd72e3SWojciech Macek OPAL_I2C_RAW_READ : OPAL_I2C_RAW_WRITE; 2083395ab28SJustin Hibbits req.addr = htobe16(msgs[i].slave >> 1); 2093395ab28SJustin Hibbits req.size = htobe32(msgs[i].len); 2103395ab28SJustin Hibbits req.buffer_pa = htobe64(pmap_kextract((uint64_t)msgs[i].buf)); 2114ffd72e3SWojciech Macek 2124ffd72e3SWojciech Macek err = i2c_opal_send_request(sc->opal_id, &req); 2134ffd72e3SWojciech Macek } 2144ffd72e3SWojciech Macek I2C_UNLOCK(sc); 2154ffd72e3SWojciech Macek 2164ffd72e3SWojciech Macek return (err); 2174ffd72e3SWojciech Macek } 2184ffd72e3SWojciech Macek 2194ffd72e3SWojciech Macek static int 2204ffd72e3SWojciech Macek opal_i2c_callback(device_t dev, int index, caddr_t data) 2214ffd72e3SWojciech Macek { 2224ffd72e3SWojciech Macek int error = 0; 2234ffd72e3SWojciech Macek 2244ffd72e3SWojciech Macek switch (index) { 2254ffd72e3SWojciech Macek case IIC_REQUEST_BUS: 2264ffd72e3SWojciech Macek break; 2274ffd72e3SWojciech Macek 2284ffd72e3SWojciech Macek case IIC_RELEASE_BUS: 2294ffd72e3SWojciech Macek break; 2304ffd72e3SWojciech Macek 2314ffd72e3SWojciech Macek default: 2324ffd72e3SWojciech Macek error = EINVAL; 2334ffd72e3SWojciech Macek } 2344ffd72e3SWojciech Macek 2354ffd72e3SWojciech Macek return (error); 2364ffd72e3SWojciech Macek } 2374ffd72e3SWojciech Macek 238fedd55f1SJustin Hibbits static phandle_t 239fedd55f1SJustin Hibbits opal_i2c_get_node(device_t bus, device_t dev) 240fedd55f1SJustin Hibbits { 241fedd55f1SJustin Hibbits 242fedd55f1SJustin Hibbits /* Share controller node with iibus device. */ 243fedd55f1SJustin Hibbits return (ofw_bus_get_node(bus)); 244fedd55f1SJustin Hibbits } 245fedd55f1SJustin Hibbits 246*5edf159fSJohn Baldwin DRIVER_MODULE(opal_i2c, opal_i2cm, opal_i2c_driver, NULL, NULL); 247676ea8e1SJohn Baldwin DRIVER_MODULE(iicbus, opal_i2c, iicbus_driver, NULL, NULL); 2484ffd72e3SWojciech Macek MODULE_DEPEND(opal_i2c, iicbus, 1, 1, 1); 249