1/*- 2 * Copyright (C) 2008 MARVELL INTERNATIONAL LTD. 3 * All rights reserved. 4 * 5 * Developed by Semihalf. 6 * 7 * Redistribution and use in source and binary forms, with or without 8 * modification, are permitted provided that the following conditions 9 * are met: 10 * 1. Redistributions of source code must retain the above copyright 11 * notice, this list of conditions and the following disclaimer. 12 * 2. Redistributions in binary form must reproduce the above copyright 13 * notice, this list of conditions and the following disclaimer in the 14 * documentation and/or other materials provided with the distribution. 15 * 3. Neither the name of MARVELL nor the names of contributors 16 * may be used to endorse or promote products derived from this software 17 * without specific prior written permission. 18 * 19 * THIS SOFTWARE IS PROVIDED BY AUTHOR AND CONTRIBUTORS ``AS IS'' AND 20 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 21 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 22 * ARE DISCLAIMED. IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE 23 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 24 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 25 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 26 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 27 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 28 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 29 * SUCH DAMAGE. 30 */ 31 32/* 33 * Driver for the TWSI (aka I2C, aka IIC) bus controller found on Marvell 34 * and Allwinner SoCs. Supports master operation only, and works in polling mode. 35 * 36 * Calls to DELAY() are needed per Application Note AN-179 "TWSI Software 37 * Guidelines for Discovery(TM), Horizon (TM) and Feroceon(TM) Devices". 38 */ 39 40#include <sys/cdefs.h> 41__FBSDID("$FreeBSD$"); 42 43#include <sys/param.h> 44#include <sys/systm.h> 45#include <sys/bus.h> 46#include <sys/kernel.h> 47#include <sys/module.h> 48#include <sys/resource.h> 49 50#include <machine/_inttypes.h> 51#include <machine/bus.h> 52#include <machine/resource.h> 53 54#include <sys/rman.h> 55 56#include <sys/lock.h> 57#include <sys/mutex.h> 58 59#include <dev/iicbus/iiconf.h> 60#include <dev/iicbus/iicbus.h> 61#include <dev/ofw/ofw_bus.h> 62#include <dev/ofw/ofw_bus_subr.h> 63 64#ifdef EXT_RESOURCES 65#include <dev/extres/clk/clk.h> 66#endif 67 68#include <arm/mv/mvreg.h> 69#include <arm/mv/mvvar.h> 70#include <dev/iicbus/twsi/twsi.h> 71 72#include "iicbus_if.h" 73 74#define MV_TWSI_NAME "twsi" 75#define IICBUS_DEVNAME "iicbus" 76 77#define TWSI_ADDR 0x00 78#define TWSI_DATA 0x04 79#define TWSI_CNTR 0x08 80#define TWSI_XADDR 0x10 81#define TWSI_STAT 0x0c 82#define TWSI_BAUD_RATE 0x0c 83#define TWSI_SRST 0x1c 84 85#define TWSI_BAUD_RATE_RAW(C,M,N) ((C)/((10*(M+1))<<(N+1))) 86#define TWSI_BAUD_RATE_SLOW 50000 /* 50kHz */ 87#define TWSI_BAUD_RATE_FAST 100000 /* 100kHz */ 88 89#define TWSI_DEBUG 90#undef TWSI_DEBUG 91 92#ifdef TWSI_DEBUG 93#define debugf(fmt, args...) do { printf("%s(): ", __func__); printf(fmt,##args); } while (0) 94#else 95#define debugf(fmt, args...) 96#endif 97 98static phandle_t mv_twsi_get_node(device_t, device_t); 99static int mv_twsi_probe(device_t); 100static int mv_twsi_attach(device_t); 101 102static struct ofw_compat_data compat_data[] = { 103 { "mrvl,twsi", true }, 104 { "marvell,mv64xxx-i2c", true }, 105 { "marvell,mv78230-i2c", true }, 106 { NULL, false } 107}; 108 109static device_method_t mv_twsi_methods[] = { 110 /* device interface */ 111 DEVMETHOD(device_probe, mv_twsi_probe), 112 DEVMETHOD(device_attach, mv_twsi_attach), 113 114 /* ofw_bus interface */ 115 DEVMETHOD(ofw_bus_get_node, mv_twsi_get_node), 116 117 DEVMETHOD_END 118}; 119 120DEFINE_CLASS_1(twsi, mv_twsi_driver, mv_twsi_methods, 121 sizeof(struct twsi_softc), twsi_driver); 122 123static devclass_t mv_twsi_devclass; 124 125DRIVER_MODULE(twsi, simplebus, mv_twsi_driver, mv_twsi_devclass, 0, 0); 126DRIVER_MODULE(iicbus, twsi, iicbus_driver, iicbus_devclass, 0, 0); 127MODULE_DEPEND(twsi, iicbus, 1, 1, 1); 128SIMPLEBUS_PNP_INFO(compat_data); 129 130static phandle_t 131mv_twsi_get_node(device_t bus, device_t dev) 132{ 133 134 /* Used by ofw_iicbus. */ 135 return (ofw_bus_get_node(bus)); 136} 137 138static int 139mv_twsi_probe(device_t dev) 140{ 141 struct twsi_softc *sc; 142 143 sc = device_get_softc(dev); 144 if (!ofw_bus_status_okay(dev)) 145 return (ENXIO); 146 147 if (!ofw_bus_search_compatible(dev, compat_data)->ocd_data) 148 return (ENXIO); 149 150 device_set_desc(dev, "Marvell Integrated I2C Bus Controller"); 151 return (BUS_PROBE_DEFAULT); 152} 153 154#define ABSSUB(a,b) (((a) > (b)) ? (a) - (b) : (b) - (a)) 155static void 156mv_twsi_cal_baud_rate(struct twsi_softc *sc, const uint32_t target, 157 struct twsi_baud_rate *rate) 158{ 159 uint64_t clk; 160 uint32_t cur, diff, diff0; 161 int m, n, m0, n0; 162 163 /* Calculate baud rate. */ 164 m0 = n0 = 4; /* Default values on reset */ 165 diff0 = 0xffffffff; 166#ifdef __aarch64__ 167 clk_get_freq(sc->clk_core, &clk); 168#else 169 clk = get_tclk(); 170#endif 171 172 for (n = 0; n < 8; n++) { 173 for (m = 0; m < 16; m++) { 174 cur = TWSI_BAUD_RATE_RAW(clk,m,n); 175 diff = ABSSUB(target, cur); 176 if (diff < diff0) { 177 m0 = m; 178 n0 = n; 179 diff0 = diff; 180 } 181 } 182 } 183 rate->raw = TWSI_BAUD_RATE_RAW(clk, m0, n0); 184 rate->param = TWSI_BAUD_RATE_PARAM(m0, n0); 185 rate->m = m0; 186 rate->n = n0; 187} 188 189static int 190mv_twsi_attach(device_t dev) 191{ 192 struct twsi_softc *sc; 193#ifdef __aarch64__ 194 int error; 195#endif 196 197 sc = device_get_softc(dev); 198 sc->dev = dev; 199 200#ifdef __aarch64__ 201 /* Activate clock */ 202 error = clk_get_by_ofw_index(dev, 0, 0, &sc->clk_core); 203 if (error != 0) { 204 device_printf(dev, "could not find core clock\n"); 205 return (error); 206 } 207 error = clk_enable(sc->clk_core); 208 if (error != 0) { 209 device_printf(dev, "could not enable core clock\n"); 210 return (error); 211 } 212 213 if (clk_get_by_ofw_index(dev, 0, 1, &sc->clk_reg) == 0) { 214 error = clk_enable(sc->clk_reg); 215 if (error != 0) { 216 device_printf(dev, "could not enable core clock\n"); 217 return (error); 218 } 219 } 220#endif 221 222 mv_twsi_cal_baud_rate(sc, TWSI_BAUD_RATE_SLOW, &sc->baud_rate[IIC_SLOW]); 223 mv_twsi_cal_baud_rate(sc, TWSI_BAUD_RATE_FAST, &sc->baud_rate[IIC_FAST]); 224 if (bootverbose) 225 device_printf(dev, "calculated baud rates are:\n" 226 " %" PRIu32 " kHz (M=%d, N=%d) for slow,\n" 227 " %" PRIu32 " kHz (M=%d, N=%d) for fast.\n", 228 sc->baud_rate[IIC_SLOW].raw / 1000, 229 sc->baud_rate[IIC_SLOW].m, 230 sc->baud_rate[IIC_SLOW].n, 231 sc->baud_rate[IIC_FAST].raw / 1000, 232 sc->baud_rate[IIC_FAST].m, 233 sc->baud_rate[IIC_FAST].n); 234 235 sc->reg_data = TWSI_DATA; 236 sc->reg_slave_addr = TWSI_ADDR; 237 sc->reg_slave_ext_addr = TWSI_XADDR; 238 sc->reg_control = TWSI_CNTR; 239 sc->reg_status = TWSI_STAT; 240 sc->reg_baud_rate = TWSI_BAUD_RATE; 241 sc->reg_soft_reset = TWSI_SRST; 242 243 return (twsi_attach(dev)); 244} 245