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); +}