Tue Oct 27 20:13:21 2020 UTC ()
Add additional debugging output.
Track more chip state in the softc and check the chip state via a timer.
This allows us to easily observe changes caused by external events
(e.g. disk removal or PSU failure).


(jdc)
diff -r1.3 -r1.4 src/sys/dev/i2c/pcagpio.c

cvs diff -r1.3 -r1.4 src/sys/dev/i2c/pcagpio.c (expand / switch to unified diff)

--- src/sys/dev/i2c/pcagpio.c 2020/02/02 06:43:14 1.3
+++ src/sys/dev/i2c/pcagpio.c 2020/10/27 20:13:21 1.4
@@ -1,14 +1,14 @@ @@ -1,14 +1,14 @@
1/* $NetBSD: pcagpio.c,v 1.3 2020/02/02 06:43:14 macallan Exp $ */ 1/* $NetBSD: pcagpio.c,v 1.4 2020/10/27 20:13:21 jdc Exp $ */
2 2
3/*- 3/*-
4 * Copyright (c) 2020 Michael Lorenz 4 * Copyright (c) 2020 Michael Lorenz
5 * All rights reserved. 5 * All rights reserved.
6 * 6 *
7 * Redistribution and use in source and binary forms, with or without 7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions 8 * modification, are permitted provided that the following conditions
9 * are met: 9 * are met:
10 * 1. Redistributions of source code must retain the above copyright 10 * 1. Redistributions of source code must retain the above copyright
11 * notice, this list of conditions and the following disclaimer. 11 * notice, this list of conditions and the following disclaimer.
12 * 2. Redistributions in binary form must reproduce the above copyright 12 * 2. Redistributions in binary form must reproduce the above copyright
13 * notice, this list of conditions and the following disclaimer in the 13 * notice, this list of conditions and the following disclaimer in the
14 * documentation and/or other materials provided with the distribution. 14 * documentation and/or other materials provided with the distribution.
@@ -21,118 +21,130 @@ @@ -21,118 +21,130 @@
21 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 21 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
22 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 22 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
23 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 23 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
24 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 24 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
25 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 25 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
26 * POSSIBILITY OF SUCH DAMAGE. 26 * POSSIBILITY OF SUCH DAMAGE.
27 */ 27 */
28 28
29/* 29/*
30 * a driver for Philips Semiconductor PCA9555 GPIO controllers 30 * a driver for Philips Semiconductor PCA9555 GPIO controllers
31 */ 31 */
32 32
33#include <sys/cdefs.h> 33#include <sys/cdefs.h>
34__KERNEL_RCSID(0, "$NetBSD: pcagpio.c,v 1.3 2020/02/02 06:43:14 macallan Exp $"); 34__KERNEL_RCSID(0, "$NetBSD: pcagpio.c,v 1.4 2020/10/27 20:13:21 jdc Exp $");
35 35
36#include <sys/param.h> 36#include <sys/param.h>
37#include <sys/systm.h> 37#include <sys/systm.h>
38#include <sys/device.h> 38#include <sys/device.h>
 39#ifdef PCAGPIO_DEBUG
 40#include <sys/kernel.h>
 41#endif
39#include <sys/conf.h> 42#include <sys/conf.h>
40#include <sys/bus.h> 43#include <sys/bus.h>
41 44
42#include <dev/i2c/i2cvar.h> 45#include <dev/i2c/i2cvar.h>
43#include <dev/led.h> 46#include <dev/led.h>
44 47
45#ifdef PCAGPIO_DEBUG 48#ifdef PCAGPIO_DEBUG
46#define DPRINTF printf 49#define DPRINTF printf
47#else 50#else
48#define DPRINTF if (0) printf 51#define DPRINTF if (0) printf
49#endif 52#endif
50 53
51/* commands */ 54/* commands */
52#define PCAGPIO_INPUT 0x00 /* line status */ 55#define PCAGPIO_INPUT 0x00 /* line status */
53#define PCAGPIO_OUTPUT 0x01 /* output status */ 56#define PCAGPIO_OUTPUT 0x01 /* output status */
54#define PCAGPIO_REVERT 0x02 /* revert input if set */ 57#define PCAGPIO_REVERT 0x02 /* revert input if set */
55#define PCAGPIO_CONFIG 0x03 /* input if set, output if not */ 58#define PCAGPIO_CONFIG 0x03 /* input if set, output if not */
56 59
57static int pcagpio_match(device_t, cfdata_t, void *); 60static int pcagpio_match(device_t, cfdata_t, void *);
58static void pcagpio_attach(device_t, device_t, void *); 61static void pcagpio_attach(device_t, device_t, void *);
 62static int pcagpio_detach(device_t, int);
 63#ifdef PCAGPIO_DEBUG
 64static void pcagpio_timeout(void *);
 65#endif
59 66
60/* we can only pass one cookie to led_attach() but we need several values... */ 67/* we can only pass one cookie to led_attach() but we need several values... */
61struct pcagpio_led { 68struct pcagpio_led {
62 void *cookie; 69 void *cookie;
63 uint32_t mask, v_on, v_off; 70 uint32_t mask, v_on, v_off;
64}; 71};
65 72
66struct pcagpio_softc { 73struct pcagpio_softc {
67 device_t sc_dev; 74 device_t sc_dev;
68 i2c_tag_t sc_i2c; 75 i2c_tag_t sc_i2c;
69 i2c_addr_t sc_addr; 76 i2c_addr_t sc_addr;
70 77
71 int sc_is_16bit; 78 int sc_is_16bit;
72 uint32_t sc_state; 79 uint32_t sc_state;
73 struct pcagpio_led sc_leds[16]; 80 struct pcagpio_led sc_leds[16];
74 int sc_nleds; 81 int sc_nleds;
 82
 83#ifdef PCAGPIO_DEBUG
 84 uint32_t sc_dir, sc_in;
 85 callout_t sc_timer;
 86#endif
75}; 87};
76 88
77 89
78static void pcagpio_writereg(struct pcagpio_softc *, int, uint32_t); 90static void pcagpio_writereg(struct pcagpio_softc *, int, uint32_t);
79static uint32_t pcagpio_readreg(struct pcagpio_softc *, int); 91static uint32_t pcagpio_readreg(struct pcagpio_softc *, int);
80static void pcagpio_attach_led( 92static void pcagpio_attach_led(
81 struct pcagpio_softc *, char *, int, int, int); 93 struct pcagpio_softc *, char *, int, int, int);
82static int pcagpio_get(void *); 94static int pcagpio_get(void *);
83static void pcagpio_set(void *, int); 95static void pcagpio_set(void *, int);
84 96
85CFATTACH_DECL_NEW(pcagpio, sizeof(struct pcagpio_softc), 97CFATTACH_DECL_NEW(pcagpio, sizeof(struct pcagpio_softc),
86 pcagpio_match, pcagpio_attach, NULL, NULL); 98 pcagpio_match, pcagpio_attach, pcagpio_detach, NULL);
87 99
88static const struct device_compatible_entry compat_data[] = { 100static const struct device_compatible_entry compat_data[] = {
89 { "i2c-pca9555", 1 }, 101 { "i2c-pca9555", 1 },
90 { "pca9555", 1 }, 102 { "pca9555", 1 },
91 { "i2c-pca9556", 0 }, 103 { "i2c-pca9556", 0 },
92 { "pca9556", 0 }, 104 { "pca9556", 0 },
93 { NULL, 0 } 105 { NULL, 0 }
94}; 106};
95 107
96static int 108static int
97pcagpio_match(device_t parent, cfdata_t match, void *aux) 109pcagpio_match(device_t parent, cfdata_t match, void *aux)
98{ 110{
99 struct i2c_attach_args *ia = aux; 111 struct i2c_attach_args *ia = aux;
100 int match_result; 112 int match_result;
101 113
102 if (iic_use_direct_match(ia, match, compat_data, &match_result)) 114 if (iic_use_direct_match(ia, match, compat_data, &match_result))
103 return match_result; 115 return match_result;
104 116
105 return 0; 117 return 0;
106} 118}
107 119
108#ifdef PCAGPIO_DEBUG 120#ifdef PCAGPIO_DEBUG
109static void 121static void
110printdir(uint32_t val, uint32_t mask, char letter) 122printdir(char* name, uint32_t val, uint32_t mask, char letter)
111{ 123{
112 char flags[17], bits[17]; 124 char flags[17], bits[17];
113 uint32_t bit = 0x8000; 125 uint32_t bit = 0x8000;
114 int i; 126 int i;
115 127
116 val &= mask; 128 val &= mask;
117 for (i = 0; i < 16; i++) { 129 for (i = 0; i < 16; i++) {
118 flags[i] = (mask & bit) ? letter : '-'; 130 flags[i] = (mask & bit) ? letter : '-';
119 bits[i] = (val & bit) ? 'X' : ' '; 131 bits[i] = (val & bit) ? 'X' : ' ';
120 bit = bit >> 1; 132 bit = bit >> 1;
121 } 133 }
122 flags[16] = 0; 134 flags[16] = 0;
123 bits[16] = 0; 135 bits[16] = 0;
124 printf("dir: %s\n", flags); 136 printf("%s: dir: %s\n", name, flags);
125 printf("lvl: %s\n", bits); 137 printf("%s: lvl: %s\n", name, bits);
126}  138}
127#endif 139#endif
128 140
129static void 141static void
130pcagpio_attach(device_t parent, device_t self, void *aux) 142pcagpio_attach(device_t parent, device_t self, void *aux)
131{ 143{
132 struct pcagpio_softc *sc = device_private(self); 144 struct pcagpio_softc *sc = device_private(self);
133 struct i2c_attach_args *ia = aux; 145 struct i2c_attach_args *ia = aux;
134 const struct device_compatible_entry *dce; 146 const struct device_compatible_entry *dce;
135 prop_dictionary_t dict = device_properties(self); 147 prop_dictionary_t dict = device_properties(self);
136 prop_array_t pins; 148 prop_array_t pins;
137 prop_dictionary_t pin; 149 prop_dictionary_t pin;
138 150
@@ -141,36 +153,41 @@ pcagpio_attach(device_t parent, device_t @@ -141,36 +153,41 @@ pcagpio_attach(device_t parent, device_t
141 sc->sc_addr = ia->ia_addr; 153 sc->sc_addr = ia->ia_addr;
142 sc->sc_nleds = 0; 154 sc->sc_nleds = 0;
143 155
144 aprint_naive("\n"); 156 aprint_naive("\n");
145 sc->sc_is_16bit = 0; 157 sc->sc_is_16bit = 0;
146 if (iic_compatible_match(ia, compat_data, &dce)) 158 if (iic_compatible_match(ia, compat_data, &dce))
147 sc->sc_is_16bit = dce->data; 159 sc->sc_is_16bit = dce->data;
148 160
149 aprint_normal(": %s\n", sc->sc_is_16bit ? "PCA9555" : "PCA9556"); 161 aprint_normal(": %s\n", sc->sc_is_16bit ? "PCA9555" : "PCA9556");
150 162
151 sc->sc_state = pcagpio_readreg(sc, PCAGPIO_OUTPUT); 163 sc->sc_state = pcagpio_readreg(sc, PCAGPIO_OUTPUT);
152 164
153#ifdef PCAGPIO_DEBUG 165#ifdef PCAGPIO_DEBUG
154 uint32_t dir, in, out; 166 uint32_t in, out;
155 dir = pcagpio_readreg(sc, PCAGPIO_CONFIG); 167 sc->sc_dir = pcagpio_readreg(sc, PCAGPIO_CONFIG);
156 in = pcagpio_readreg(sc, PCAGPIO_INPUT); 168 sc->sc_in = pcagpio_readreg(sc, PCAGPIO_INPUT);
 169 in = sc-> sc_in;
157 out = sc->sc_state; 170 out = sc->sc_state;
158 171
159 out &= ~dir; 172 out &= ~sc->sc_dir;
160 in &= dir; 173 in &= sc->sc_dir;
161  174
162 printdir(in, dir, 'I'); 175 printdir(sc->sc_dev->dv_xname, in, sc->sc_dir, 'I');
163 printdir(out, ~dir, 'O'); 176 printdir(sc->sc_dev->dv_xname, out, ~sc->sc_dir, 'O');
 177
 178 callout_init(&sc->sc_timer, CALLOUT_MPSAFE);
 179 callout_reset(&sc->sc_timer, hz*20, pcagpio_timeout, sc);
 180
164#endif 181#endif
165 182
166 pins = prop_dictionary_get(dict, "pins"); 183 pins = prop_dictionary_get(dict, "pins");
167 if (pins != NULL) { 184 if (pins != NULL) {
168 int i, num, def; 185 int i, num, def;
169 char name[32]; 186 char name[32];
170 const char *nptr; 187 const char *nptr;
171 bool ok = TRUE, act; 188 bool ok = TRUE, act;
172 189
173 for (i = 0; i < prop_array_count(pins); i++) { 190 for (i = 0; i < prop_array_count(pins); i++) {
174 nptr = NULL; 191 nptr = NULL;
175 pin = prop_array_get(pins, i); 192 pin = prop_array_get(pins, i);
176 ok &= prop_dictionary_get_cstring_nocopy(pin, "name", 193 ok &= prop_dictionary_get_cstring_nocopy(pin, "name",
@@ -179,26 +196,69 @@ pcagpio_attach(device_t parent, device_t @@ -179,26 +196,69 @@ pcagpio_attach(device_t parent, device_t
179 ok &= prop_dictionary_get_uint32(pin, "pin", &num); 196 ok &= prop_dictionary_get_uint32(pin, "pin", &num);
180 ok &= prop_dictionary_get_bool( 197 ok &= prop_dictionary_get_bool(
181 pin, "active_high", &act); 198 pin, "active_high", &act);
182 /* optional default state */ 199 /* optional default state */
183 def = -1; 200 def = -1;
184 prop_dictionary_get_int32(pin, "default_state", &def); 201 prop_dictionary_get_int32(pin, "default_state", &def);
185 if (ok) {  202 if (ok) {
186 pcagpio_attach_led(sc, name, num, act, def); 203 pcagpio_attach_led(sc, name, num, act, def);
187 } 204 }
188 } 205 }
189 } 206 }
190} 207}
191 208
 209static int
 210pcagpio_detach(device_t self, int flags)
 211{
 212#ifdef PCAGPIO_DEBUG
 213 struct pcagpio_softc *sc = device_private(self);
 214
 215 callout_halt(&sc->sc_timer, NULL);
 216 callout_destroy(&sc->sc_timer);
 217#endif
 218
 219 return 0;
 220}
 221
 222#ifdef PCAGPIO_DEBUG
 223static void
 224pcagpio_timeout(void *v)
 225{
 226 struct pcagpio_softc *sc = v;
 227 uint32_t out, dir, in, o_out, o_in;
 228
 229 out = pcagpio_readreg(sc, PCAGPIO_OUTPUT);
 230 dir = pcagpio_readreg(sc, PCAGPIO_CONFIG);
 231 in = pcagpio_readreg(sc, PCAGPIO_INPUT);
 232 if (out != sc->sc_state || dir != sc->sc_dir || in != sc->sc_in) {
 233 aprint_normal_dev(sc->sc_dev, "status change\n");
 234 o_out = sc->sc_state;
 235 o_in = sc->sc_in;
 236 o_out &= ~sc->sc_dir;
 237 o_in &= sc->sc_dir;
 238 printdir(sc->sc_dev->dv_xname, o_in, sc->sc_dir, 'I');
 239 printdir(sc->sc_dev->dv_xname, o_out, ~sc->sc_dir, 'O');
 240 sc->sc_state = out;
 241 sc->sc_dir = dir;
 242 sc->sc_in = in;
 243 out &= ~sc->sc_dir;
 244 in &= sc->sc_dir;
 245 printdir(sc->sc_dev->dv_xname, in, sc->sc_dir, 'I');
 246 printdir(sc->sc_dev->dv_xname, out, ~sc->sc_dir, 'O');
 247 }
 248 callout_reset(&sc->sc_timer, hz*60, pcagpio_timeout, sc);
 249}
 250#endif
 251
192static void 252static void
193pcagpio_writereg(struct pcagpio_softc *sc, int reg, uint32_t val) 253pcagpio_writereg(struct pcagpio_softc *sc, int reg, uint32_t val)
194{ 254{
195 uint8_t cmd; 255 uint8_t cmd;
196 256
197 iic_acquire_bus(sc->sc_i2c, 0); 257 iic_acquire_bus(sc->sc_i2c, 0);
198 if (sc->sc_is_16bit) { 258 if (sc->sc_is_16bit) {
199 uint16_t creg; 259 uint16_t creg;
200 cmd = reg << 1; 260 cmd = reg << 1;
201 creg = htole16(val); 261 creg = htole16(val);
202 iic_exec(sc->sc_i2c, I2C_OP_WRITE_WITH_STOP, 262 iic_exec(sc->sc_i2c, I2C_OP_WRITE_WITH_STOP,
203 sc->sc_addr, &cmd, 1, &creg, 2, 0); 263 sc->sc_addr, &cmd, 1, &creg, 2, 0);
204 } else { 264 } else {