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_ */

Reply via email to