iicbus.c revision 282674
138774Snsouch/*- 293023Snsouch * Copyright (c) 1998, 2001 Nicolas Souchu 338774Snsouch * All rights reserved. 438774Snsouch * 538774Snsouch * Redistribution and use in source and binary forms, with or without 638774Snsouch * modification, are permitted provided that the following conditions 738774Snsouch * are met: 838774Snsouch * 1. Redistributions of source code must retain the above copyright 938774Snsouch * notice, this list of conditions and the following disclaimer. 1038774Snsouch * 2. Redistributions in binary form must reproduce the above copyright 1138774Snsouch * notice, this list of conditions and the following disclaimer in the 1238774Snsouch * documentation and/or other materials provided with the distribution. 1338774Snsouch * 1438774Snsouch * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 1538774Snsouch * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 1638774Snsouch * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 1738774Snsouch * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 1838774Snsouch * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 1938774Snsouch * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 2038774Snsouch * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 2138774Snsouch * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 2238774Snsouch * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 2338774Snsouch * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 2438774Snsouch * SUCH DAMAGE. 2538774Snsouch */ 2638774Snsouch 27119418Sobrien#include <sys/cdefs.h> 28119418Sobrien__FBSDID("$FreeBSD: head/sys/dev/iicbus/iicbus.c 282674 2015-05-09 03:05:44Z loos $"); 29119418Sobrien 3038774Snsouch/* 3138774Snsouch * Autoconfiguration and support routines for the Philips serial I2C bus 3238774Snsouch */ 3338774Snsouch 3438774Snsouch#include <sys/param.h> 3538774Snsouch#include <sys/systm.h> 3638774Snsouch#include <sys/kernel.h> 37181304Sjhb#include <sys/lock.h> 38167856Simp#include <sys/malloc.h> 3938774Snsouch#include <sys/module.h> 40181304Sjhb#include <sys/mutex.h> 41282674Sloos#include <sys/rman.h> 42274641Sian#include <sys/sysctl.h> 4338774Snsouch#include <sys/bus.h> 4438774Snsouch 4538774Snsouch#include <dev/iicbus/iiconf.h> 4638774Snsouch#include <dev/iicbus/iicbus.h> 4738774Snsouch 4838774Snsouch#include "iicbus_if.h" 4938774Snsouch 50129152Sjoerg/* See comments below for why auto-scanning is a bad idea. */ 51129152Sjoerg#define SCAN_IICBUS 0 52129152Sjoerg 5340782Snsouchstatic int 5440782Snsouchiicbus_probe(device_t dev) 5540782Snsouch{ 56160372Simp 5742442Snsouch device_set_desc(dev, "Philips I2C bus"); 58186833Snwhitehorn 59186833Snwhitehorn /* Allow other subclasses to override this driver. */ 60187457Snwhitehorn return (BUS_PROBE_GENERIC); 6140782Snsouch} 6240782Snsouch 63129152Sjoerg#if SCAN_IICBUS 6440782Snsouchstatic int 6540782Snsouchiic_probe_device(device_t dev, u_char addr) 6640782Snsouch{ 6740782Snsouch int count; 6840782Snsouch char byte; 6940782Snsouch 7040782Snsouch if ((addr & 1) == 0) { 7140782Snsouch /* is device writable? */ 7240782Snsouch if (!iicbus_start(dev, (u_char)addr, 0)) { 7340782Snsouch iicbus_stop(dev); 7440782Snsouch return (1); 7540782Snsouch } 7640782Snsouch } else { 7740782Snsouch /* is device readable? */ 7840782Snsouch if (!iicbus_block_read(dev, (u_char)addr, &byte, 1, &count)) 7940782Snsouch return (1); 8040782Snsouch } 8140782Snsouch 8240782Snsouch return (0); 8340782Snsouch} 8442442Snsouch#endif 8540782Snsouch 8638774Snsouch/* 8740782Snsouch * We add all the devices which we know about. 8840782Snsouch * The generic attach routine will attach them if they are alive. 8938774Snsouch */ 9038774Snsouchstatic int 9140782Snsouchiicbus_attach(device_t dev) 9238774Snsouch{ 93129152Sjoerg#if SCAN_IICBUS 94129152Sjoerg unsigned char addr; 95129152Sjoerg#endif 96167856Simp struct iicbus_softc *sc = IICBUS_SOFTC(dev); 97228257Sadrian int strict; 98129152Sjoerg 99167856Simp sc->dev = dev; 100181304Sjhb mtx_init(&sc->lock, "iicbus", NULL, MTX_DEF); 101274641Sian iicbus_init_frequency(dev, 0); 10240782Snsouch iicbus_reset(dev, IIC_FASTEST, 0, NULL); 103228257Sadrian if (resource_int_value(device_get_name(dev), 104228257Sadrian device_get_unit(dev), "strict", &strict) == 0) 105228257Sadrian sc->strict = strict; 106228257Sadrian else 107228257Sadrian sc->strict = 1; 10838774Snsouch 10942442Snsouch /* device probing is meaningless since the bus is supposed to be 11042442Snsouch * hot-plug. Moreover, some I2C chips do not appreciate random 11142442Snsouch * accesses like stop after start to fast, reads for less than 11242442Snsouch * x bytes... 11342442Snsouch */ 114129152Sjoerg#if SCAN_IICBUS 11540782Snsouch printf("Probing for devices on iicbus%d:", device_get_unit(dev)); 11638774Snsouch 11740782Snsouch /* probe any devices */ 118129152Sjoerg for (addr = 16; addr < 240; addr++) { 11940782Snsouch if (iic_probe_device(dev, (u_char)addr)) { 12040782Snsouch printf(" <%x>", addr); 12140782Snsouch } 12240782Snsouch } 12340782Snsouch printf("\n"); 12442442Snsouch#endif 125181304Sjhb bus_generic_probe(dev); 126167856Simp bus_enumerate_hinted_children(dev); 12738774Snsouch bus_generic_attach(dev); 12838774Snsouch return (0); 12938774Snsouch} 13093023Snsouch 13193023Snsouchstatic int 13293023Snsouchiicbus_detach(device_t dev) 13393023Snsouch{ 134181304Sjhb struct iicbus_softc *sc = IICBUS_SOFTC(dev); 135160372Simp 13693023Snsouch iicbus_reset(dev, IIC_FASTEST, 0, NULL); 13793023Snsouch bus_generic_detach(dev); 138181304Sjhb mtx_destroy(&sc->lock); 13993023Snsouch return (0); 14093023Snsouch} 14193023Snsouch 14293023Snsouchstatic int 143167856Simpiicbus_print_child(device_t dev, device_t child) 14493023Snsouch{ 145167856Simp struct iicbus_ivar *devi = IICBUS_IVAR(child); 146167856Simp int retval = 0; 147160372Simp 148167856Simp retval += bus_print_child_header(dev, child); 149167856Simp if (devi->addr != 0) 150167856Simp retval += printf(" at addr %#x", devi->addr); 151282674Sloos resource_list_print_type(&devi->rl, "irq", SYS_RES_IRQ, "%ld"); 152167856Simp retval += bus_print_child_footer(dev, child); 153167856Simp 154167856Simp return (retval); 155167856Simp} 156167856Simp 157167856Simpstatic void 158167856Simpiicbus_probe_nomatch(device_t bus, device_t child) 159167856Simp{ 160167856Simp struct iicbus_ivar *devi = IICBUS_IVAR(child); 161167856Simp 162282674Sloos device_printf(bus, "<unknown card> at addr %#x", devi->addr); 163282674Sloos resource_list_print_type(&devi->rl, "irq", SYS_RES_IRQ, "%ld"); 164282674Sloos printf("\n"); 165167856Simp} 166167856Simp 167167856Simpstatic int 168167856Simpiicbus_child_location_str(device_t bus, device_t child, char *buf, 169167856Simp size_t buflen) 170167856Simp{ 171167856Simp struct iicbus_ivar *devi = IICBUS_IVAR(child); 172167856Simp 173167856Simp snprintf(buf, buflen, "addr=%#x", devi->addr); 17493023Snsouch return (0); 17593023Snsouch} 17693023Snsouch 177167856Simpstatic int 178167856Simpiicbus_child_pnpinfo_str(device_t bus, device_t child, char *buf, 179167856Simp size_t buflen) 180167856Simp{ 181167856Simp *buf = '\0'; 182167856Simp return (0); 183167856Simp} 184167856Simp 185167856Simpstatic int 186188461Simpiicbus_read_ivar(device_t bus, device_t child, int which, uintptr_t *result) 187167856Simp{ 188167856Simp struct iicbus_ivar *devi = IICBUS_IVAR(child); 189167856Simp 190167856Simp switch (which) { 191167856Simp default: 192167856Simp return (EINVAL); 193167856Simp case IICBUS_IVAR_ADDR: 194209800Snwhitehorn *result = devi->addr; 195167856Simp break; 196167856Simp } 197167856Simp return (0); 198167856Simp} 199167856Simp 200167856Simpstatic device_t 201212413Savgiicbus_add_child(device_t dev, u_int order, const char *name, int unit) 202167856Simp{ 203167856Simp device_t child; 204167856Simp struct iicbus_ivar *devi; 205167856Simp 206167856Simp child = device_add_child_ordered(dev, order, name, unit); 207167856Simp if (child == NULL) 208167856Simp return (child); 209167856Simp devi = malloc(sizeof(struct iicbus_ivar), M_DEVBUF, M_NOWAIT | M_ZERO); 210167856Simp if (devi == NULL) { 211167856Simp device_delete_child(dev, child); 212167856Simp return (0); 213167856Simp } 214282674Sloos resource_list_init(&devi->rl); 215167856Simp device_set_ivars(child, devi); 216167856Simp return (child); 217167856Simp} 218167856Simp 219167856Simpstatic void 220167856Simpiicbus_hinted_child(device_t bus, const char *dname, int dunit) 221167856Simp{ 222167856Simp device_t child; 223282674Sloos int irq; 224167856Simp struct iicbus_ivar *devi; 225167856Simp 226167856Simp child = BUS_ADD_CHILD(bus, 0, dname, dunit); 227167856Simp devi = IICBUS_IVAR(child); 228167856Simp resource_int_value(dname, dunit, "addr", &devi->addr); 229282674Sloos if (resource_int_value(dname, dunit, "irq", &irq) == 0) { 230282674Sloos if (bus_set_resource(child, SYS_RES_IRQ, 0, irq, 1) != 0) 231282674Sloos device_printf(bus, 232282674Sloos "warning: bus_set_resource() failed\n"); 233282674Sloos } 234167856Simp} 235167856Simp 236282674Sloosstatic int 237282674Sloosiicbus_set_resource(device_t dev, device_t child, int type, int rid, 238282674Sloos u_long start, u_long count) 239282674Sloos{ 240282674Sloos struct iicbus_ivar *devi; 241282674Sloos struct resource_list_entry *rle; 242282674Sloos 243282674Sloos devi = IICBUS_IVAR(child); 244282674Sloos rle = resource_list_add(&devi->rl, type, rid, start, 245282674Sloos start + count - 1, count); 246282674Sloos if (rle == NULL) 247282674Sloos return (ENXIO); 248282674Sloos 249282674Sloos return (0); 250282674Sloos} 251282674Sloos 252282674Sloosstatic struct resource * 253282674Sloosiicbus_alloc_resource(device_t bus, device_t child, int type, int *rid, 254282674Sloos u_long start, u_long end, u_long count, u_int flags) 255282674Sloos{ 256282674Sloos struct resource_list *rl; 257282674Sloos struct resource_list_entry *rle; 258282674Sloos 259282674Sloos /* Only IRQ resources are supported. */ 260282674Sloos if (type != SYS_RES_IRQ) 261282674Sloos return (NULL); 262282674Sloos 263282674Sloos /* 264282674Sloos * Request for the default allocation with a given rid: use resource 265282674Sloos * list stored in the local device info. 266282674Sloos */ 267282674Sloos if ((start == 0UL) && (end == ~0UL)) { 268282674Sloos rl = BUS_GET_RESOURCE_LIST(bus, child); 269282674Sloos if (rl == NULL) 270282674Sloos return (NULL); 271282674Sloos rle = resource_list_find(rl, type, *rid); 272282674Sloos if (rle == NULL) { 273282674Sloos if (bootverbose) 274282674Sloos device_printf(bus, "no default resources for " 275282674Sloos "rid = %d, type = %d\n", *rid, type); 276282674Sloos return (NULL); 277282674Sloos } 278282674Sloos start = rle->start; 279282674Sloos end = rle->end; 280282674Sloos count = rle->count; 281282674Sloos } 282282674Sloos 283282674Sloos return (bus_generic_alloc_resource(bus, child, type, rid, start, end, 284282674Sloos count, flags)); 285282674Sloos} 286282674Sloos 287282674Sloosstatic struct resource_list * 288282674Sloosiicbus_get_resource_list(device_t bus __unused, device_t child) 289282674Sloos{ 290282674Sloos struct iicbus_ivar *devi; 291282674Sloos 292282674Sloos devi = IICBUS_IVAR(child); 293282674Sloos return (&devi->rl); 294282674Sloos} 295282674Sloos 29638774Snsouchint 29738774Snsouchiicbus_generic_intr(device_t dev, int event, char *buf) 29838774Snsouch{ 299160372Simp 30038774Snsouch return (0); 30138774Snsouch} 30238774Snsouch 30340782Snsouchint 30440782Snsouchiicbus_null_callback(device_t dev, int index, caddr_t data) 30540782Snsouch{ 306160372Simp 30740782Snsouch return (0); 30840782Snsouch} 30940782Snsouch 31040782Snsouchint 31140782Snsouchiicbus_null_repeated_start(device_t dev, u_char addr) 31240782Snsouch{ 313160372Simp 31440782Snsouch return (IIC_ENOTSUPP); 31540782Snsouch} 31640782Snsouch 317274641Sianvoid 318274641Sianiicbus_init_frequency(device_t dev, u_int bus_freq) 319274641Sian{ 320274641Sian struct iicbus_softc *sc = IICBUS_SOFTC(dev); 321274641Sian 322274641Sian /* 323274641Sian * If a bus frequency value was passed in, use it. Otherwise initialize 324274641Sian * it first to the standard i2c 100KHz frequency, then override that 325274641Sian * from a hint if one exists. 326274641Sian */ 327274641Sian if (bus_freq > 0) 328274641Sian sc->bus_freq = bus_freq; 329274641Sian else { 330274641Sian sc->bus_freq = 100000; 331274641Sian resource_int_value(device_get_name(dev), device_get_unit(dev), 332274641Sian "frequency", (int *)&sc->bus_freq); 333274641Sian } 334274641Sian /* 335274641Sian * Set up the sysctl that allows the bus frequency to be changed. 336274641Sian * It is flagged as a tunable so that the user can set the value in 337274641Sian * loader(8), and that will override any other setting from any source. 338274641Sian * The sysctl tunable/value is the one most directly controlled by the 339274641Sian * user and thus the one that always takes precedence. 340274641Sian */ 341274641Sian SYSCTL_ADD_UINT(device_get_sysctl_ctx(dev), 342274641Sian SYSCTL_CHILDREN(device_get_sysctl_tree(dev)), 343274641Sian OID_AUTO, "frequency", CTLFLAG_RW | CTLFLAG_TUN, &sc->bus_freq, 344274641Sian sc->bus_freq, "Bus frequency in Hz"); 345274641Sian} 346274641Sian 347274641Sianstatic u_int 348274641Sianiicbus_get_frequency(device_t dev, u_char speed) 349274641Sian{ 350274641Sian struct iicbus_softc *sc = IICBUS_SOFTC(dev); 351274641Sian 352274641Sian /* 353274641Sian * If the frequency has not been configured for the bus, or the request 354274641Sian * is specifically for SLOW speed, use the standard 100KHz rate, else 355274641Sian * use the configured bus speed. 356274641Sian */ 357274641Sian if (sc->bus_freq == 0 || speed == IIC_SLOW) 358274641Sian return (100000); 359274641Sian return (sc->bus_freq); 360274641Sian} 361274641Sian 362167856Simpstatic device_method_t iicbus_methods[] = { 363282668Sloos /* device interface */ 364282668Sloos DEVMETHOD(device_probe, iicbus_probe), 365282668Sloos DEVMETHOD(device_attach, iicbus_attach), 366282668Sloos DEVMETHOD(device_detach, iicbus_detach), 367167856Simp 368282668Sloos /* bus interface */ 369282674Sloos DEVMETHOD(bus_setup_intr, bus_generic_setup_intr), 370282674Sloos DEVMETHOD(bus_teardown_intr, bus_generic_teardown_intr), 371282674Sloos DEVMETHOD(bus_release_resource, bus_generic_release_resource), 372282674Sloos DEVMETHOD(bus_activate_resource, bus_generic_activate_resource), 373282674Sloos DEVMETHOD(bus_deactivate_resource, bus_generic_deactivate_resource), 374282674Sloos DEVMETHOD(bus_adjust_resource, bus_generic_adjust_resource), 375282674Sloos DEVMETHOD(bus_get_resource, bus_generic_rl_get_resource), 376282674Sloos DEVMETHOD(bus_alloc_resource, iicbus_alloc_resource), 377282674Sloos DEVMETHOD(bus_get_resource_list, iicbus_get_resource_list), 378282674Sloos DEVMETHOD(bus_set_resource, iicbus_set_resource), 379282668Sloos DEVMETHOD(bus_add_child, iicbus_add_child), 380282668Sloos DEVMETHOD(bus_print_child, iicbus_print_child), 381167856Simp DEVMETHOD(bus_probe_nomatch, iicbus_probe_nomatch), 382167856Simp DEVMETHOD(bus_read_ivar, iicbus_read_ivar), 383167856Simp DEVMETHOD(bus_child_pnpinfo_str, iicbus_child_pnpinfo_str), 384167856Simp DEVMETHOD(bus_child_location_str, iicbus_child_location_str), 385167856Simp DEVMETHOD(bus_hinted_child, iicbus_hinted_child), 386167856Simp 387167856Simp /* iicbus interface */ 388167856Simp DEVMETHOD(iicbus_transfer, iicbus_transfer), 389274641Sian DEVMETHOD(iicbus_get_frequency, iicbus_get_frequency), 390167856Simp 391227843Smarius DEVMETHOD_END 392167856Simp}; 393167856Simp 394167856Simpdriver_t iicbus_driver = { 395167856Simp "iicbus", 396167856Simp iicbus_methods, 397167856Simp sizeof(struct iicbus_softc), 398167856Simp}; 399167856Simp 400167856Simpdevclass_t iicbus_devclass; 401167856Simp 40293023SnsouchMODULE_VERSION(iicbus, IICBUS_MODVER); 403187261SnwhitehornDRIVER_MODULE(iicbus, iichb, iicbus_driver, iicbus_devclass, 0, 0); 404