Module Name:    src
Committed By:   macallan
Date:           Sun Feb  2 06:41:27 UTC 2020

Modified Files:
        src/sys/dev/i2c: files.i2c pcagpio.c

Log Message:
attach LEDs according to info passed as device properties


To generate a diff of this commit:
cvs rdiff -u -r1.109 -r1.110 src/sys/dev/i2c/files.i2c
cvs rdiff -u -r1.1 -r1.2 src/sys/dev/i2c/pcagpio.c

Please note that diffs are not public domain; they are subject to the
copyright notices on the relevant files.

Modified files:

Index: src/sys/dev/i2c/files.i2c
diff -u src/sys/dev/i2c/files.i2c:1.109 src/sys/dev/i2c/files.i2c:1.110
--- src/sys/dev/i2c/files.i2c:1.109	Wed Jan 29 05:27:05 2020
+++ src/sys/dev/i2c/files.i2c	Sun Feb  2 06:41:27 2020
@@ -1,4 +1,4 @@
-#	$NetBSD: files.i2c,v 1.109 2020/01/29 05:27:05 macallan Exp $
+#	$NetBSD: files.i2c,v 1.110 2020/02/02 06:41:27 macallan Exp $
 
 obsolete defflag	opt_i2cbus.h		I2C_SCAN
 define	i2cbus { }
@@ -387,6 +387,6 @@ attach	cwfg at iic
 file	dev/i2c/cwfg.c				cwfg
 
 # Philips PCA955x GPIO
-device	pcagpio
+device	pcagpio: leds
 attach	pcagpio at iic
 file	dev/i2c/pcagpio.c			pcagpio

Index: src/sys/dev/i2c/pcagpio.c
diff -u src/sys/dev/i2c/pcagpio.c:1.1 src/sys/dev/i2c/pcagpio.c:1.2
--- src/sys/dev/i2c/pcagpio.c:1.1	Wed Jan 29 05:27:05 2020
+++ src/sys/dev/i2c/pcagpio.c	Sun Feb  2 06:41:27 2020
@@ -1,7 +1,7 @@
-/* $NetBSD: pcagpio.c,v 1.1 2020/01/29 05:27:05 macallan Exp $ */
+/* $NetBSD: pcagpio.c,v 1.2 2020/02/02 06:41:27 macallan Exp $ */
 
 /*-
- * Copyright (c) 2018 Michael Lorenz
+ * Copyright (c) 2020 Michael Lorenz
  * All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
@@ -31,7 +31,7 @@
  */
 
 #include <sys/cdefs.h>
-__KERNEL_RCSID(0, "$NetBSD: pcagpio.c,v 1.1 2020/01/29 05:27:05 macallan Exp $");
+__KERNEL_RCSID(0, "$NetBSD: pcagpio.c,v 1.2 2020/02/02 06:41:27 macallan Exp $");
 
 #include <sys/param.h>
 #include <sys/systm.h>
@@ -40,6 +40,13 @@ __KERNEL_RCSID(0, "$NetBSD: pcagpio.c,v 
 #include <sys/bus.h>
 
 #include <dev/i2c/i2cvar.h>
+#include <dev/led.h>
+
+#ifdef PCAGPIO_DEBUG
+#define DPRINTF printf
+#else
+#define DPRINTF if (0) printf
+#endif
 
 /* commands */
 #define PCAGPIO_INPUT	0x00	/* line status */
@@ -50,17 +57,29 @@ __KERNEL_RCSID(0, "$NetBSD: pcagpio.c,v 
 static int	pcagpio_match(device_t, cfdata_t, void *);
 static void	pcagpio_attach(device_t, device_t, void *);
 
+/* we can only pass one cookie to led_attach() but we need several values... */
+struct pcagpio_led {
+	void *cookie;
+	uint32_t mask, v_on, v_off;
+};
+
 struct pcagpio_softc {
 	device_t	sc_dev;
 	i2c_tag_t	sc_i2c;
 	i2c_addr_t	sc_addr;
 
 	int		sc_is_16bit;
+	uint32_t	sc_state;
+	struct pcagpio_led sc_leds[16];
+	int		sc_nleds;
 };
 
 
 static void 	pcagpio_writereg(struct pcagpio_softc *, int, uint32_t);
 static uint32_t pcagpio_readreg(struct pcagpio_softc *, int);
+static void	pcagpio_attach_led(struct pcagpio_softc *, char *, int, int, int);
+static int	pcagpio_get(void *);
+static void	pcagpio_set(void *, int);
 
 CFATTACH_DECL_NEW(pcagpio, sizeof(struct pcagpio_softc),
     pcagpio_match, pcagpio_attach, NULL, NULL);
@@ -85,6 +104,7 @@ pcagpio_match(device_t parent, cfdata_t 
 	return 0;
 }
 
+#ifdef PCAGPIO_DEBUG
 static void
 printdir(uint32_t val, uint32_t mask, char letter)
 {
@@ -103,6 +123,7 @@ printdir(uint32_t val, uint32_t mask, ch
 	printf("dir: %s\n", flags);
 	printf("lvl: %s\n", bits);
 }	
+#endif
 
 static void
 pcagpio_attach(device_t parent, device_t self, void *aux)
@@ -110,11 +131,14 @@ pcagpio_attach(device_t parent, device_t
 	struct pcagpio_softc *sc = device_private(self);
 	struct i2c_attach_args *ia = aux;
 	const struct device_compatible_entry *dce;
-	uint32_t dir, in, out;
+	prop_dictionary_t dict = device_properties(self);
+	prop_array_t pins;
+	prop_dictionary_t pin;
 
 	sc->sc_dev = self;
 	sc->sc_i2c = ia->ia_tag;
 	sc->sc_addr = ia->ia_addr;
+	sc->sc_nleds = 0;
 
 	aprint_naive("\n");
 	sc->sc_is_16bit = 0;
@@ -123,17 +147,43 @@ pcagpio_attach(device_t parent, device_t
 
 	aprint_normal(": %s\n", sc->sc_is_16bit ? "PCA9555" : "PCA9556");
 
-	if (sc->sc_addr == 0x38) pcagpio_writereg(sc, 1, 0xff & ~0x10);
-	
-	dir = pcagpio_readreg(sc, 3);
-	in = pcagpio_readreg(sc, 0);
-	out = pcagpio_readreg(sc, 1);
+	sc->sc_state = pcagpio_readreg(sc, PCAGPIO_OUTPUT);
+
+#ifdef PCAGPIO_DEBUG
+	uint32_t dir, in, out;
+	dir = pcagpio_readreg(sc, PCAGPIO_CONFIG);
+	in = pcagpio_readreg(sc, PCAGPIO_INPUT);
+	out = sc->sc_state;
 
 	out &= ~dir;
 	in &= dir;
 	
 	printdir(in, dir, 'I');
 	printdir(out, ~dir, 'O');
+#endif
+
+	pins = prop_dictionary_get(dict, "pins");
+	if (pins != NULL) {
+		int i, num, def;
+		char name[32];
+		const char *nptr;
+		bool ok = TRUE, act;
+
+		for (i = 0; i < prop_array_count(pins); i++) {
+			nptr = NULL;
+			pin = prop_array_get(pins, i);
+			ok &= prop_dictionary_get_cstring_nocopy(pin, "name", &nptr);
+			strncpy(name, nptr, 31);
+			ok &= prop_dictionary_get_uint32(pin, "pin", &num);
+			ok &= prop_dictionary_get_bool(pin, "active_high", &act);
+			/* optional default state */
+			def = -1;
+			prop_dictionary_get_int32(pin, "default_state", &def);
+			if (ok) {		
+				pcagpio_attach_led(sc, name, num, act, def);
+			}
+		}
+	}
 }
 
 static void
@@ -155,6 +205,7 @@ pcagpio_writereg(struct pcagpio_softc *s
 		iic_exec(sc->sc_i2c, I2C_OP_WRITE_WITH_STOP,
 		    sc->sc_addr, &cmd, 1, &creg, 1, 0);
 	}
+	if (reg == PCAGPIO_OUTPUT) sc->sc_state = val;
 	iic_release_bus(sc->sc_i2c, 0);
 }		
 
@@ -180,3 +231,43 @@ static uint32_t pcagpio_readreg(struct p
 	iic_release_bus(sc->sc_i2c, 0);
 	return ret;
 }
+
+static void
+pcagpio_attach_led(struct pcagpio_softc *sc, char *n, int pin, int act, int def)
+{
+	struct pcagpio_led *l;
+
+	l = &sc->sc_leds[sc->sc_nleds];
+	l->cookie = sc;
+	l->mask = 1 << pin;
+	l->v_on = act ? l->mask : 0;
+	l->v_off = act ? 0 : l->mask;
+	led_attach(n, l, pcagpio_get, pcagpio_set);
+	if (def != -1) pcagpio_set(l, def);
+	DPRINTF("%s: %04x %04x %04x def %d\n", __func__, l->mask, l->v_on, l->v_off, def);
+	sc->sc_nleds++;
+}
+
+static int
+pcagpio_get(void *cookie)
+{
+	struct pcagpio_led *l = cookie;
+	struct pcagpio_softc *sc = l->cookie;
+
+	return ((sc->sc_state & l->mask) == l->v_on);
+}
+
+static void
+pcagpio_set(void *cookie, int val)
+{
+	struct pcagpio_led *l = cookie;
+	struct pcagpio_softc *sc = l->cookie;
+	uint32_t newstate;	
+
+	newstate = sc->sc_state & ~l->mask;
+	newstate |= val ? l->v_on : l->v_off;
+	DPRINTF("%s: %04x -> %04x, %04x %04x %04x\n", __func__,
+	    sc->sc_state, newstate, l->mask, l->v_on, l->v_off);
+	if (newstate != sc->sc_state)
+		pcagpio_writereg(sc, PCAGPIO_OUTPUT, newstate);
+}

Reply via email to