1/*- 2 * Copyright 2013-2015 John Wehle <john@feith.com> 3 * All rights reserved. 4 * 5 * Redistribution and use in source and binary forms, with or without 6 * modification, are permitted provided that the following conditions 7 * are met: 8 * 1. Redistributions of source code must retain the above copyright 9 * notice, this list of conditions and the following disclaimer. 10 * 2. Redistributions in binary form must reproduce the above copyright 11 * notice, this list of conditions and the following disclaimer in the 12 * documentation and/or other materials provided with the distribution. 13 * 14 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 15 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 16 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 17 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 18 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 19 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 20 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 21 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 22 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 23 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 24 * SUCH DAMAGE. 25 */ 26 27/* 28 * Amlogic aml8726 I2C driver. 29 * 30 * Currently this implementation doesn't take full advantage of the hardware. 31 */ 32 33#include <sys/cdefs.h> 34__FBSDID("$FreeBSD$"); 35 36#include <sys/param.h> 37#include <sys/systm.h> 38#include <sys/bus.h> 39#include <sys/conf.h> 40#include <sys/kernel.h> 41#include <sys/lock.h> 42#include <sys/mbuf.h> 43#include <sys/malloc.h> 44#include <sys/module.h> 45#include <sys/mutex.h> 46#include <sys/rman.h> 47 48#include <machine/bus.h> 49 50#include <dev/ofw/ofw_bus.h> 51#include <dev/ofw/ofw_bus_subr.h> 52 53#include <dev/iicbus/iiconf.h> 54#include <dev/iicbus/iicbus.h> 55 56#include "iicbb_if.h" 57 58 59struct aml8726_iic_softc { 60 device_t dev; 61 struct resource *res[1]; 62 struct mtx mtx; 63 device_t iicbb; 64}; 65 66static struct resource_spec aml8726_iic_spec[] = { 67 { SYS_RES_MEMORY, 0, RF_ACTIVE }, 68 { -1, 0 } 69}; 70 71#define AML_I2C_LOCK(sc) mtx_lock(&(sc)->mtx) 72#define AML_I2C_UNLOCK(sc) mtx_unlock(&(sc)->mtx) 73#define AML_I2C_LOCK_INIT(sc) \ 74 mtx_init(&(sc)->mtx, device_get_nameunit((sc)->dev), \ 75 "i2c", MTX_DEF) 76#define AML_I2C_LOCK_DESTROY(sc) mtx_destroy(&(sc)->mtx); 77 78#define AML_I2C_CTRL_REG 0 79#define AML_I2C_MANUAL_SDA_I (1 << 26) 80#define AML_I2C_MANUAL_SCL_I (1 << 25) 81#define AML_I2C_MANUAL_SDA_O (1 << 24) 82#define AML_I2C_MANUAL_SCL_O (1 << 23) 83#define AML_I2C_MANUAL_EN (1 << 22) 84 85#define CSR_WRITE_4(sc, reg, val) bus_write_4((sc)->res[0], reg, (val)) 86#define CSR_READ_4(sc, reg) bus_read_4((sc)->res[0], reg) 87 88static int 89aml8726_iic_probe(device_t dev) 90{ 91 92 if (!ofw_bus_status_okay(dev)) 93 return (ENXIO); 94 95 if (!ofw_bus_is_compatible(dev, "amlogic,meson6-i2c")) 96 return (ENXIO); 97 98 device_set_desc(dev, "Amlogic aml8726 I2C"); 99 100 return (BUS_PROBE_DEFAULT); 101} 102 103static int 104aml8726_iic_attach(device_t dev) 105{ 106 struct aml8726_iic_softc *sc = device_get_softc(dev); 107 int error; 108 109 sc->dev = dev; 110 111 if (bus_alloc_resources(dev, aml8726_iic_spec, sc->res)) { 112 device_printf(dev, "can not allocate resources for device\n"); 113 return (ENXIO); 114 } 115 116 AML_I2C_LOCK_INIT(sc); 117 118 sc->iicbb = device_add_child(dev, "iicbb", -1); 119 120 if (sc->iicbb == NULL) { 121 device_printf(dev, "could not add iicbb\n"); 122 error = ENXIO; 123 goto fail; 124 } 125 126 error = device_probe_and_attach(sc->iicbb); 127 128 if (error) { 129 device_printf(dev, "could not attach iicbb\n"); 130 goto fail; 131 } 132 133 return (0); 134 135fail: 136 AML_I2C_LOCK_DESTROY(sc); 137 bus_release_resources(dev, aml8726_iic_spec, sc->res); 138 139 return (error); 140} 141 142static int 143aml8726_iic_detach(device_t dev) 144{ 145 struct aml8726_iic_softc *sc = device_get_softc(dev); 146 device_t child; 147 148 /* 149 * Detach the children before recursively deleting 150 * in case a child has a pointer to a grandchild 151 * which is used by the child's detach routine. 152 * 153 * Remember the child before detaching so we can 154 * delete it (bus_generic_detach indirectly zeroes 155 * sc->child_dev). 156 */ 157 child = sc->iicbb; 158 bus_generic_detach(dev); 159 if (child) 160 device_delete_child(dev, child); 161 162 AML_I2C_LOCK_DESTROY(sc); 163 164 bus_release_resources(dev, aml8726_iic_spec, sc->res); 165 166 return (0); 167} 168 169static void 170aml8726_iic_child_detached(device_t dev, device_t child) 171{ 172 struct aml8726_iic_softc *sc = device_get_softc(dev); 173 174 if (child == sc->iicbb) 175 sc->iicbb = NULL; 176} 177 178static int 179aml8726_iic_callback(device_t dev, int index, caddr_t data) 180{ 181 182 return (0); 183} 184 185static int 186aml8726_iic_reset(device_t dev, u_char speed, u_char addr, u_char *oldaddr) 187{ 188 struct aml8726_iic_softc *sc = device_get_softc(dev); 189 190 AML_I2C_LOCK(sc); 191 192 CSR_WRITE_4(sc, AML_I2C_CTRL_REG, 193 (CSR_READ_4(sc, AML_I2C_CTRL_REG) | AML_I2C_MANUAL_SDA_O | 194 AML_I2C_MANUAL_SCL_O | AML_I2C_MANUAL_EN)); 195 196 AML_I2C_UNLOCK(sc); 197 198 /* Wait for 10 usec */ 199 DELAY(10); 200 201 return (IIC_ENOADDR); 202} 203 204static int 205aml8726_iic_getscl(device_t dev) 206{ 207 struct aml8726_iic_softc *sc = device_get_softc(dev); 208 209 return (CSR_READ_4(sc, AML_I2C_CTRL_REG) & AML_I2C_MANUAL_SCL_I); 210} 211 212static int 213aml8726_iic_getsda(device_t dev) 214{ 215 struct aml8726_iic_softc *sc = device_get_softc(dev); 216 217 return (CSR_READ_4(sc, AML_I2C_CTRL_REG) & AML_I2C_MANUAL_SDA_I); 218} 219 220static void 221aml8726_iic_setscl(device_t dev, int val) 222{ 223 struct aml8726_iic_softc *sc = device_get_softc(dev); 224 225 AML_I2C_LOCK(sc); 226 227 CSR_WRITE_4(sc, AML_I2C_CTRL_REG, ((CSR_READ_4(sc, AML_I2C_CTRL_REG) & 228 ~AML_I2C_MANUAL_SCL_O) | (val ? AML_I2C_MANUAL_SCL_O : 0) | 229 AML_I2C_MANUAL_EN)); 230 231 AML_I2C_UNLOCK(sc); 232} 233 234static void 235aml8726_iic_setsda(device_t dev, int val) 236{ 237 struct aml8726_iic_softc *sc = device_get_softc(dev); 238 239 AML_I2C_LOCK(sc); 240 241 CSR_WRITE_4(sc, AML_I2C_CTRL_REG, ((CSR_READ_4(sc, AML_I2C_CTRL_REG) & 242 ~AML_I2C_MANUAL_SDA_O) | (val ? AML_I2C_MANUAL_SDA_O : 0) | 243 AML_I2C_MANUAL_EN)); 244 245 AML_I2C_UNLOCK(sc); 246} 247 248static device_method_t aml8726_iic_methods[] = { 249 /* Device interface */ 250 DEVMETHOD(device_probe, aml8726_iic_probe), 251 DEVMETHOD(device_attach, aml8726_iic_attach), 252 DEVMETHOD(device_detach, aml8726_iic_detach), 253 254 /* bus interface */ 255 DEVMETHOD(bus_child_detached, aml8726_iic_child_detached), 256 DEVMETHOD(bus_print_child, bus_generic_print_child), 257 DEVMETHOD(bus_driver_added, bus_generic_driver_added), 258 259 /* IICBB interface */ 260 DEVMETHOD(iicbb_callback, aml8726_iic_callback), 261 DEVMETHOD(iicbb_reset, aml8726_iic_reset), 262 263 DEVMETHOD(iicbb_getscl, aml8726_iic_getscl), 264 DEVMETHOD(iicbb_getsda, aml8726_iic_getsda), 265 DEVMETHOD(iicbb_setscl, aml8726_iic_setscl), 266 DEVMETHOD(iicbb_setsda, aml8726_iic_setsda), 267 268 DEVMETHOD_END 269}; 270 271static driver_t aml8726_iic_driver = { 272 "aml8726_iic", 273 aml8726_iic_methods, 274 sizeof(struct aml8726_iic_softc), 275}; 276 277static devclass_t aml8726_iic_devclass; 278 279DRIVER_MODULE(aml8726_iic, simplebus, aml8726_iic_driver, 280 aml8726_iic_devclass, 0, 0); 281DRIVER_MODULE(iicbb, aml8726_iic, iicbb_driver, iicbb_devclass, 0, 0); 282MODULE_DEPEND(aml8726_iic, iicbb, IICBB_MINVER, IICBB_PREFVER, IICBB_MAXVER); 283MODULE_VERSION(aml8726_iic, 1); 284