Module Name: src Committed By: thorpej Date: Fri May 14 06:53:14 UTC 2021
Modified Files: src/sys/arch/sparc64/dev [thorpej-i2c-spi-conf]: pcfiic_ebus.c src/sys/dev/ic [thorpej-i2c-spi-conf]: pcf8584.c pcf8584var.h Log Message: Update the pcfiic driver for the new i2c device enumeration scheme. Push the multiple-channels complexity up into the ebus front-end, as that specific to a particular Sun model of controller. Now the BBC-type controllers get multiple I2C bus instances attached to them. To generate a diff of this commit: cvs rdiff -u -r1.7 -r1.7.6.1 src/sys/arch/sparc64/dev/pcfiic_ebus.c cvs rdiff -u -r1.18 -r1.18.2.1 src/sys/dev/ic/pcf8584.c cvs rdiff -u -r1.6 -r1.6.12.1 src/sys/dev/ic/pcf8584var.h Please note that diffs are not public domain; they are subject to the copyright notices on the relevant files.
Modified files: Index: src/sys/arch/sparc64/dev/pcfiic_ebus.c diff -u src/sys/arch/sparc64/dev/pcfiic_ebus.c:1.7 src/sys/arch/sparc64/dev/pcfiic_ebus.c:1.7.6.1 --- src/sys/arch/sparc64/dev/pcfiic_ebus.c:1.7 Fri Oct 23 15:18:10 2020 +++ src/sys/arch/sparc64/dev/pcfiic_ebus.c Fri May 14 06:53:14 2021 @@ -1,7 +1,36 @@ -/* $NetBSD: pcfiic_ebus.c,v 1.7 2020/10/23 15:18:10 jdc Exp $ */ +/* $NetBSD: pcfiic_ebus.c,v 1.7.6.1 2021/05/14 06:53:14 thorpej Exp $ */ /* $OpenBSD: pcfiic_ebus.c,v 1.13 2008/06/08 03:07:40 deraadt Exp $ */ /* + * Copyright (c) 2021 The NetBSD Foundation, Inc. + * All rights reserved. + * + * This code is derived from software contributed to The NetBSD Foundation + * by Jason R. Thorpe. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +/* * Copyright (c) 2006 David Gwynne <d...@openbsd.org> * * Permission to use, copy, modify, and distribute this software for any @@ -18,7 +47,7 @@ */ #include <sys/cdefs.h> -__KERNEL_RCSID(0, "$NetBSD: pcfiic_ebus.c,v 1.7 2020/10/23 15:18:10 jdc Exp $"); +__KERNEL_RCSID(0, "$NetBSD: pcfiic_ebus.c,v 1.7.6.1 2021/05/14 06:53:14 thorpej Exp $"); /* * Device specific driver for the EBus i2c devices found on some sun4u @@ -30,7 +59,8 @@ __KERNEL_RCSID(0, "$NetBSD: pcfiic_ebus. #include <sys/systm.h> #include <sys/device.h> #include <sys/kernel.h> -#include <sys/rwlock.h> +#include <sys/kmem.h> +#include <sys/mutex.h> #include <sys/bus.h> #include <machine/openfirm.h> @@ -44,20 +74,119 @@ __KERNEL_RCSID(0, "$NetBSD: pcfiic_ebus. #include <dev/ic/pcf8584var.h> #include <dev/ic/pcf8584reg.h> -int pcfiic_ebus_match(device_t, struct cfdata *, void *); -void pcfiic_ebus_attach(device_t, device_t, void *); - struct pcfiic_ebus_softc { struct pcfiic_softc esc_sc; - int esc_node; + kmutex_t esc_ctrl_lock; + bus_space_handle_t esc_ioh; /* for channel selection */ + void *esc_ih; }; -CFATTACH_DECL_NEW(pcfiic, sizeof(struct pcfiic_ebus_softc), - pcfiic_ebus_match, pcfiic_ebus_attach, NULL, NULL); +static void +bbc_select_channel(struct pcfiic_ebus_softc *esc, uint8_t channel) +{ + bus_space_write_1(esc->esc_sc.sc_iot, esc->esc_ioh, 0, channel); + bus_space_barrier(esc->esc_sc.sc_iot, esc->esc_ioh, 0, 1, + BUS_SPACE_BARRIER_WRITE); +} + +static int +bbc_acquire_bus(void *v, int flags) +{ + struct pcfiic_channel *ch = v; + struct pcfiic_ebus_softc *esc = container_of(ch->ch_sc, + struct pcfiic_ebus_softc, esc_sc); + + if (flags & I2C_F_POLL) { + if (! mutex_tryenter(&esc->esc_ctrl_lock)) { + return EBUSY; + } + } else { + mutex_enter(&esc->esc_ctrl_lock); + } + + bbc_select_channel(esc, (uint8_t)ch->ch_channel); + return 0; +} + +static void +bbc_release_bus(void *v, int flags) +{ + struct pcfiic_channel *ch = v; + struct pcfiic_ebus_softc *esc = container_of(ch->ch_sc, + struct pcfiic_ebus_softc, esc_sc); -int + mutex_exit(&esc->esc_ctrl_lock); +} + +static void +bbc_initialize_channels(struct pcfiic_ebus_softc *esc) +{ + struct pcfiic_softc *sc = &esc->esc_sc; + struct pcfiic_channel *ch; + devhandle_t devhandle = device_handle(sc->sc_dev); + unsigned int busmap = 0; + int node = devhandle_to_of(devhandle); + uint32_t reg[2]; + uint32_t channel; + int i, nchannels; + + /* + * Two physical I2C busses share a single controller. The + * devices are not distinct, so it's not easy to treat it + * it as a mux. + * + * The locking order is: + * + * iic bus mutex -> ctrl_lock + * + * ctrl_lock is taken in bbc_acquire_bus. + */ + mutex_init(&esc->esc_ctrl_lock, MUTEX_DEFAULT, IPL_NONE); + sc->sc_acquire_bus = bbc_acquire_bus; + sc->sc_release_bus = bbc_release_bus; + + /* + * The Sun device tree has all devices, no matter the + * channel, as direct children of this node. Figure + * out which channel numbers are listed, count them, + * and then populate the channel structures. + */ + for (node = OF_child(node); node != 0; node = OF_peer(node)) { + if (OF_getprop(node, "reg", reg, sizeof(reg)) != sizeof(reg)) { + continue; + } + + /* Channel is in the first cell. */ + channel = be32toh(reg[0]); + KASSERT(channel < 32); + + busmap |= __BIT(channel); + } + + nchannels = popcount(busmap); + if (nchannels == 0) { + /* No child devices. */ + return; + } + + ch = kmem_alloc(nchannels * sizeof(*ch), KM_SLEEP); + for (i = 0; i < nchannels; i++) { + channel = ffs(busmap); + KASSERT(channel != 0); + channel--; /* ffs() returns 0 if no bits set. */ + busmap &= ~__BIT(channel); + + ch[i].ch_channel = channel; + ch[i].ch_devhandle = devhandle; + } + + sc->sc_channels = ch; + sc->sc_nchannels = nchannels; +} + +static int pcfiic_ebus_match(device_t parent, struct cfdata *match, void *aux) { struct ebus_attach_args *ea = aux; @@ -82,15 +211,15 @@ pcfiic_ebus_match(device_t parent, struc return (0); } -void +static void pcfiic_ebus_attach(device_t parent, device_t self, void *aux) { struct pcfiic_ebus_softc *esc = device_private(self); struct pcfiic_softc *sc = &esc->esc_sc; struct ebus_attach_args *ea = aux; char compat[32]; - u_int64_t addr; - u_int8_t clock = PCF8584_CLK_12 | PCF8584_SCL_90; + uint32_t addr[2]; + uint8_t clock = PCF8584_CLK_12 | PCF8584_SCL_90; int swapregs = 0; if (ea->ea_nreg < 1 || ea->ea_nreg > 2) { @@ -121,15 +250,15 @@ pcfiic_ebus_attach(device_t parent, devi } if (OF_getprop(ea->ea_node, "own-address", &addr, sizeof(addr)) == -1) { - addr = 0xaa; - } else if (addr == 0x00 || addr > 0xff) { + addr[0] = 0; + addr[1] = 0x55 << 1; + } else if (addr[1] == 0x00 || addr[1] > 0xff) { printf(": invalid address on I2C bus"); return; } - if (bus_space_map(ea->ea_bustag, - EBUS_ADDR_FROM_REG(&ea->ea_reg[0]), - ea->ea_reg[0].size, 0, &sc->sc_ioh) == 0) { + if (bus_space_map(ea->ea_bustag, EBUS_ADDR_FROM_REG(&ea->ea_reg[0]), + ea->ea_reg[0].size, 0, &sc->sc_ioh) == 0) { sc->sc_iot = ea->ea_bustag; } else { printf(": can't map register space\n"); @@ -140,14 +269,14 @@ pcfiic_ebus_attach(device_t parent, devi /* * Second register only occurs on BBC-based machines, * and is likely not prom mapped - */ - if (bus_space_map(sc->sc_iot, EBUS_ADDR_FROM_REG(&ea->ea_reg[1]), - ea->ea_reg[1].size, 0, &sc->sc_ioh2) != 0) { + */ + if (bus_space_map(sc->sc_iot, + EBUS_ADDR_FROM_REG(&ea->ea_reg[1]), + ea->ea_reg[1].size, 0, &esc->esc_ioh) != 0) { printf(": can't map 2nd register space\n"); return; } - sc->sc_master = 1; - printf(": iic mux present"); + bbc_initialize_channels(esc); } if (ea->ea_nintr >= 1) @@ -156,9 +285,11 @@ pcfiic_ebus_attach(device_t parent, devi else esc->esc_ih = NULL; - if (esc->esc_ih == NULL) sc->sc_poll = 1; - pcfiic_attach(sc, (i2c_addr_t)(addr >> 1), clock, swapregs); + pcfiic_attach(sc, (i2c_addr_t)(addr[1] >> 1), clock, swapregs); } + +CFATTACH_DECL_NEW(pcfiic, sizeof(struct pcfiic_ebus_softc), + pcfiic_ebus_match, pcfiic_ebus_attach, NULL, NULL); Index: src/sys/dev/ic/pcf8584.c diff -u src/sys/dev/ic/pcf8584.c:1.18 src/sys/dev/ic/pcf8584.c:1.18.2.1 --- src/sys/dev/ic/pcf8584.c:1.18 Sat Apr 24 23:36:55 2021 +++ src/sys/dev/ic/pcf8584.c Fri May 14 06:53:14 2021 @@ -1,7 +1,36 @@ -/* $NetBSD: pcf8584.c,v 1.18 2021/04/24 23:36:55 thorpej Exp $ */ +/* $NetBSD: pcf8584.c,v 1.18.2.1 2021/05/14 06:53:14 thorpej Exp $ */ /* $OpenBSD: pcf8584.c,v 1.9 2007/10/20 18:46:21 kettenis Exp $ */ /* + * Copyright (c) 2021 The NetBSD Foundation, Inc. + * All rights reserved. + * + * This code is derived from software contributed to The NetBSD Foundation + * by Jason R. Thorpe. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +/* * Copyright (c) 2006 David Gwynne <d...@openbsd.org> * * Permission to use, copy, modify, and distribute this software for any @@ -20,7 +49,7 @@ #include <sys/param.h> #include <sys/systm.h> #include <sys/device.h> -#include <sys/malloc.h> +#include <sys/kmem.h> #include <sys/kernel.h> #include <sys/proc.h> #include <sys/bus.h> @@ -30,6 +59,8 @@ #include <dev/ic/pcf8584var.h> #include <dev/ic/pcf8584reg.h> +#include "locators.h" + /* Internal registers */ #define PCF8584_S0 0x00 #define PCF8584_S1 0x01 @@ -37,8 +68,6 @@ #define PCF8584_S3 0x03 void pcfiic_init(struct pcfiic_softc *); -int pcfiic_i2c_acquire_bus(void *, int); -void pcfiic_i2c_release_bus(void *, int); int pcfiic_i2c_exec(void *, i2c_op_t, i2c_addr_t, const void *, size_t, void *, size_t, int); @@ -49,7 +78,6 @@ int pcfiic_recv(struct pcfiic_softc *, u_int8_t pcfiic_read(struct pcfiic_softc *, bus_size_t); void pcfiic_write(struct pcfiic_softc *, bus_size_t, u_int8_t); -void pcfiic_choose_bus(struct pcfiic_softc *, u_int8_t); int pcfiic_wait_BBN(struct pcfiic_softc *); int pcfiic_wait_pin(struct pcfiic_softc *, volatile u_int8_t *); @@ -71,10 +99,12 @@ pcfiic_init(struct pcfiic_softc *sc) } void -pcfiic_attach(struct pcfiic_softc *sc, i2c_addr_t addr, u_int8_t clock, +pcfiic_attach(struct pcfiic_softc *sc, i2c_addr_t addr, uint8_t clock, int swapregs) { - struct i2cbus_attach_args iba; + struct i2cbus_attach_args iba; + struct pcfiic_channel *ch; + int i; if (swapregs) { sc->sc_regmap[PCF8584_S1] = PCF8584_S0; @@ -90,16 +120,40 @@ pcfiic_attach(struct pcfiic_softc *sc, i printf("\n"); - if (sc->sc_master) - pcfiic_choose_bus(sc, 0); + if (sc->sc_channels == NULL) { + KASSERT(sc->sc_nchannels == 0); + ch = kmem_alloc(sizeof(*sc->sc_channels), KM_SLEEP); + ch->ch_channel = 0; + ch->ch_devhandle = device_handle(sc->sc_dev); - iic_tag_init(&sc->sc_i2c); - sc->sc_i2c.ic_cookie = sc; - sc->sc_i2c.ic_exec = pcfiic_i2c_exec; - - bzero(&iba, sizeof(iba)); - iba.iba_tag = &sc->sc_i2c; - config_found(sc->sc_dev, &iba, iicbus_print, CFARG_EOL); + sc->sc_channels = ch; + sc->sc_nchannels = 1; + } else { + KASSERT(sc->sc_nchannels != 0); + } + + for (i = 0; i < sc->sc_nchannels; i++) { + int locs[I2CBUSCF_NLOCS]; + + ch = &sc->sc_channels[i]; + ch->ch_sc = sc; + iic_tag_init(&ch->ch_i2c); + ch->ch_i2c.ic_cookie = ch; + ch->ch_i2c.ic_exec = pcfiic_i2c_exec; + ch->ch_i2c.ic_acquire_bus = sc->sc_acquire_bus; + ch->ch_i2c.ic_release_bus = sc->sc_release_bus; + + locs[I2CBUSCF_BUS] = ch->ch_i2c.ic_channel; + + memset(&iba, 0, sizeof(iba)); + iba.iba_tag = &ch->ch_i2c; + config_found(sc->sc_dev, &iba, + sc->sc_nchannels == 1 ? iicbus_print : iicbus_print_multi, + CFARG_SUBMATCH, config_stdsubmatch, + CFARG_LOCATORS, locs, + CFARG_DEVHANDLE, ch->ch_devhandle, + CFARG_EOL); + } } int @@ -123,9 +177,6 @@ pcfiic_i2c_exec(void *arg, i2c_op_t op, if (sc->sc_poll) flags |= I2C_F_POLL; - if (sc->sc_master) - pcfiic_choose_bus(sc, addr >> 7); - /* * If we are writing, write address, cmdbuf, buf. * If we are reading, write address, cmdbuf, then read address, buf. @@ -224,14 +275,6 @@ pcfiic_write(struct pcfiic_softc *sc, bu (void)bus_space_read_1(sc->sc_iot, sc->sc_ioh, PCF8584_S1); } -void -pcfiic_choose_bus(struct pcfiic_softc *sc, u_int8_t bus) -{ - bus_space_write_1(sc->sc_iot, sc->sc_ioh2, 0, bus); - bus_space_barrier(sc->sc_iot, sc->sc_ioh2, 0, 1, - BUS_SPACE_BARRIER_WRITE); -} - int pcfiic_wait_BBN(struct pcfiic_softc *sc) { Index: src/sys/dev/ic/pcf8584var.h diff -u src/sys/dev/ic/pcf8584var.h:1.6 src/sys/dev/ic/pcf8584var.h:1.6.12.1 --- src/sys/dev/ic/pcf8584var.h:1.6 Sun Dec 22 23:23:32 2019 +++ src/sys/dev/ic/pcf8584var.h Fri May 14 06:53:14 2021 @@ -1,4 +1,4 @@ -/* $NetBSD: pcf8584var.h,v 1.6 2019/12/22 23:23:32 thorpej Exp $ */ +/* $NetBSD: pcf8584var.h,v 1.6.12.1 2021/05/14 06:53:14 thorpej Exp $ */ /* $OpenBSD: pcf8584var.h,v 1.5 2007/10/20 18:46:21 kettenis Exp $ */ /* @@ -17,21 +17,41 @@ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ +#ifndef _DEV_IC_PCF8584VAR_H_ +#define _DEV_IC_PCF8584VAR_H_ + +struct pcfiic_channel { + struct i2c_controller ch_i2c; + struct pcfiic_softc *ch_sc; + devhandle_t ch_devhandle; + int ch_channel; +}; + struct pcfiic_softc { device_t sc_dev; bus_space_tag_t sc_iot; bus_space_handle_t sc_ioh; - bus_space_handle_t sc_ioh2; - int sc_master; - u_int8_t sc_addr; - u_int8_t sc_clock; - u_int8_t sc_regmap[2]; + uint8_t sc_addr; + uint8_t sc_clock; + uint8_t sc_regmap[2]; int sc_poll; - struct i2c_controller sc_i2c; + /* + * Some Sun clones of the this i2c controller support + * multiple channels. The specific attachment will + * initialize these fields for controllers that support + * this. If not, the core driver will assume a single + * channel. + */ + struct pcfiic_channel *sc_channels; + int sc_nchannels; + int (*sc_acquire_bus)(void *, int); + void (*sc_release_bus)(void *, int); }; -void pcfiic_attach(struct pcfiic_softc *, i2c_addr_t, u_int8_t, int); +void pcfiic_attach(struct pcfiic_softc *, i2c_addr_t, uint8_t, int); int pcfiic_intr(void *); + +#endif /* _DEV_IC_PCF8584VAR_H_ */