xref: /netbsd-src/sys/dev/usb/umcpmio.c (revision 3bfaa97146632a7fff2425e11c47d4964bacef79)
1*3bfaa971Sbrad /*	$NetBSD: umcpmio.c,v 1.1 2024/12/16 16:37:38 brad Exp $	*/
2*3bfaa971Sbrad 
3*3bfaa971Sbrad /*
4*3bfaa971Sbrad  * Copyright (c) 2024 Brad Spencer <brad@anduin.eldar.org>
5*3bfaa971Sbrad  *
6*3bfaa971Sbrad  * Permission to use, copy, modify, and distribute this software for any
7*3bfaa971Sbrad  * purpose with or without fee is hereby granted, provided that the above
8*3bfaa971Sbrad  * copyright notice and this permission notice appear in all copies.
9*3bfaa971Sbrad  *
10*3bfaa971Sbrad  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
11*3bfaa971Sbrad  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
12*3bfaa971Sbrad  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
13*3bfaa971Sbrad  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
14*3bfaa971Sbrad  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
15*3bfaa971Sbrad  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
16*3bfaa971Sbrad  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
17*3bfaa971Sbrad  */
18*3bfaa971Sbrad 
19*3bfaa971Sbrad #include <sys/cdefs.h>
20*3bfaa971Sbrad __KERNEL_RCSID(0, "$NetBSD: umcpmio.c,v 1.1 2024/12/16 16:37:38 brad Exp $");
21*3bfaa971Sbrad 
22*3bfaa971Sbrad /*
23*3bfaa971Sbrad   Driver for the Microchip MCP2221 / MCP2221A USB multi-io chip
24*3bfaa971Sbrad */
25*3bfaa971Sbrad 
26*3bfaa971Sbrad #ifdef _KERNEL_OPT
27*3bfaa971Sbrad #include "opt_usb.h"
28*3bfaa971Sbrad #endif
29*3bfaa971Sbrad 
30*3bfaa971Sbrad #include <sys/param.h>
31*3bfaa971Sbrad #include <sys/systm.h>
32*3bfaa971Sbrad #include <sys/conf.h>
33*3bfaa971Sbrad #include <sys/kernel.h>
34*3bfaa971Sbrad #include <sys/kmem.h>
35*3bfaa971Sbrad #include <sys/device.h>
36*3bfaa971Sbrad #include <sys/sysctl.h>
37*3bfaa971Sbrad #include <sys/tty.h>
38*3bfaa971Sbrad #include <sys/file.h>
39*3bfaa971Sbrad #include <sys/vnode.h>
40*3bfaa971Sbrad #include <sys/kauth.h>
41*3bfaa971Sbrad #include <sys/lwp.h>
42*3bfaa971Sbrad 
43*3bfaa971Sbrad #include <sys/gpio.h>
44*3bfaa971Sbrad #include <dev/gpio/gpiovar.h>
45*3bfaa971Sbrad 
46*3bfaa971Sbrad #include <dev/i2c/i2cvar.h>
47*3bfaa971Sbrad 
48*3bfaa971Sbrad #include <dev/usb/usb.h>
49*3bfaa971Sbrad #include <dev/usb/usbhid.h>
50*3bfaa971Sbrad 
51*3bfaa971Sbrad #include <dev/usb/usbdi.h>
52*3bfaa971Sbrad #include <dev/usb/usbdi_util.h>
53*3bfaa971Sbrad #include <dev/usb/usbdevs.h>
54*3bfaa971Sbrad #include <dev/usb/uhidev.h>
55*3bfaa971Sbrad #include <dev/hid/hid.h>
56*3bfaa971Sbrad 
57*3bfaa971Sbrad #include <dev/usb/umcpmio.h>
58*3bfaa971Sbrad #include <dev/usb/umcpmio_subr.h>
59*3bfaa971Sbrad #include <dev/usb/umcpmio_hid_reports.h>
60*3bfaa971Sbrad #include <dev/usb/umcpmio_io.h>
61*3bfaa971Sbrad 
62*3bfaa971Sbrad int umcpmio_send_report(struct umcpmio_softc *, uint8_t *, size_t, uint8_t *, int);
63*3bfaa971Sbrad 
64*3bfaa971Sbrad static const struct usb_devno umcpmio_devs[] = {
65*3bfaa971Sbrad 	{ USB_VENDOR_MICROCHIP, USB_PRODUCT_MICROCHIP_MCP2221 },
66*3bfaa971Sbrad };
67*3bfaa971Sbrad #define umcpmio_lookup(v, p) usb_lookup(umcpmio_devs, v, p)
68*3bfaa971Sbrad 
69*3bfaa971Sbrad static int	umcpmio_match(device_t, cfdata_t, void *);
70*3bfaa971Sbrad static void	umcpmio_attach(device_t, device_t, void *);
71*3bfaa971Sbrad static int	umcpmio_detach(device_t, int);
72*3bfaa971Sbrad static int	umcpmio_activate(device_t, enum devact);
73*3bfaa971Sbrad static int 	umcpmio_verify_sysctl(SYSCTLFN_ARGS);
74*3bfaa971Sbrad static int	umcpmio_verify_dac_sysctl(SYSCTLFN_ARGS);
75*3bfaa971Sbrad static int	umcpmio_verify_adc_sysctl(SYSCTLFN_ARGS);
76*3bfaa971Sbrad static int	umcpmio_verify_gpioclock_dc_sysctl(SYSCTLFN_ARGS);
77*3bfaa971Sbrad static int	umcpmio_verify_gpioclock_cd_sysctl(SYSCTLFN_ARGS);
78*3bfaa971Sbrad 
79*3bfaa971Sbrad #define UMCPMIO_DEBUG 1
80*3bfaa971Sbrad #ifdef UMCPMIO_DEBUG
81*3bfaa971Sbrad #define DPRINTF(x)	if (umcpmiodebug) printf x
82*3bfaa971Sbrad #define DPRINTFN(n, x)	if (umcpmiodebug > (n)) printf x
83*3bfaa971Sbrad int	umcpmiodebug = 0;
84*3bfaa971Sbrad #else
85*3bfaa971Sbrad #define DPRINTF(x)	__nothing
86*3bfaa971Sbrad #define DPRINTFN(n,x)	__nothing
87*3bfaa971Sbrad #endif
88*3bfaa971Sbrad 
89*3bfaa971Sbrad 
90*3bfaa971Sbrad CFATTACH_DECL_NEW(umcpmio, sizeof(struct umcpmio_softc), umcpmio_match,
91*3bfaa971Sbrad     umcpmio_attach, umcpmio_detach, umcpmio_activate);
92*3bfaa971Sbrad 
93*3bfaa971Sbrad 
94*3bfaa971Sbrad #define WAITMS(ms) if (ms > 0) delay(ms * 1000)
95*3bfaa971Sbrad 
96*3bfaa971Sbrad extern struct cfdriver umcpmio_cd;
97*3bfaa971Sbrad 
98*3bfaa971Sbrad static dev_type_open(umcpmio_dev_open);
99*3bfaa971Sbrad static dev_type_read(umcpmio_dev_read);
100*3bfaa971Sbrad static dev_type_write(umcpmio_dev_write);
101*3bfaa971Sbrad static dev_type_close(umcpmio_dev_close);
102*3bfaa971Sbrad static dev_type_ioctl(umcpmio_dev_ioctl);
103*3bfaa971Sbrad const struct cdevsw umcpmio_cdevsw = {
104*3bfaa971Sbrad 	.d_open = umcpmio_dev_open,
105*3bfaa971Sbrad 	.d_close = umcpmio_dev_close,
106*3bfaa971Sbrad 	.d_read = umcpmio_dev_read,
107*3bfaa971Sbrad 	.d_write = umcpmio_dev_write,
108*3bfaa971Sbrad 	.d_ioctl = umcpmio_dev_ioctl,
109*3bfaa971Sbrad 	.d_stop = nostop,
110*3bfaa971Sbrad 	.d_tty = notty,
111*3bfaa971Sbrad 	.d_poll = nopoll,
112*3bfaa971Sbrad 	.d_mmap = nommap,
113*3bfaa971Sbrad 	.d_kqfilter = nokqfilter,
114*3bfaa971Sbrad 	.d_discard = nodiscard,
115*3bfaa971Sbrad 	.d_flag = D_OTHER
116*3bfaa971Sbrad };
117*3bfaa971Sbrad 
118*3bfaa971Sbrad 
119*3bfaa971Sbrad static const char umcpmio_valid_vrefs[] =
120*3bfaa971Sbrad     "4.096V, 2.048V, 1.024V, OFF, VDD";
121*3bfaa971Sbrad 
122*3bfaa971Sbrad static const char umcpmio_valid_dcs[] =
123*3bfaa971Sbrad     "75%, 50%, 25%, 0%";
124*3bfaa971Sbrad 
125*3bfaa971Sbrad static const char umcpmio_valid_cds[] =
126*3bfaa971Sbrad     "375kHz, 750kHz, 1.5MHz, 3MHz, 6MHz, 12MHz, 24MHz";
127*3bfaa971Sbrad 
128*3bfaa971Sbrad static void
129*3bfaa971Sbrad umcpmio_dump_buffer(bool enabled, uint8_t *buf, u_int len, const char *name)
130*3bfaa971Sbrad {
131*3bfaa971Sbrad 	if (enabled) {
132*3bfaa971Sbrad 		DPRINTF(("%s:",name));
133*3bfaa971Sbrad 		for(int i=0; i < len; i++) {
134*3bfaa971Sbrad 			DPRINTF((" %02x",buf[i]));
135*3bfaa971Sbrad 		}
136*3bfaa971Sbrad 		DPRINTF(("\n"));
137*3bfaa971Sbrad 	}
138*3bfaa971Sbrad }
139*3bfaa971Sbrad 
140*3bfaa971Sbrad /* Communication with the HID function requires sending a HID report request and
141*3bfaa971Sbrad  * then waiting for a response.
142*3bfaa971Sbrad  *
143*3bfaa971Sbrad  * The panic that occurs when trying to use the interrupt... i.e.
144*3bfaa971Sbrad  * attaching though this driver seems to be related to the fact that
145*3bfaa971Sbrad  * a spin lock is held and the USB stack wants to wait.
146*3bfaa971Sbrad  *
147*3bfaa971Sbrad  * The USB stack *IS* going to have to wait for the response from
148*3bfaa971Sbrad  * the device, somehow...
149*3bfaa971Sbrad  *
150*3bfaa971Sbrad  * It didn't seem possible to defer the uhidev_write to a thread.
151*3bfaa971Sbrad  * Attempts to yield() while spinning hard also did not work and
152*3bfaa971Sbrad  * not yield()ing didn't allow anything else to run.
153*3bfaa971Sbrad  *
154*3bfaa971Sbrad  */
155*3bfaa971Sbrad 
156*3bfaa971Sbrad /*
157*3bfaa971Sbrad  * This is the panic you will get:
158*3bfaa971Sbrad  *
159*3bfaa971Sbrad    panic: kernel diagnostic assertion "ci->ci_mtx_count == -1" failed: file "../../../../kern/kern_synch.c", line 762 mi_switch: cpu0: ci_mtx_count (-2) != -1 (block with spin-mutex held)
160*3bfaa971Sbrad 
161*3bfaa971Sbrad */
162*3bfaa971Sbrad 
163*3bfaa971Sbrad static void
164*3bfaa971Sbrad umcpmio_uhidev_intr(void *cookie, void *ibuf, u_int len)
165*3bfaa971Sbrad {
166*3bfaa971Sbrad 	struct umcpmio_softc *sc = cookie;
167*3bfaa971Sbrad 
168*3bfaa971Sbrad 	if (sc->sc_dying)
169*3bfaa971Sbrad 		return;
170*3bfaa971Sbrad 
171*3bfaa971Sbrad 	DPRINTFN(30,("umcpmio_uhidev_intr: len=%d\n",len));
172*3bfaa971Sbrad 
173*3bfaa971Sbrad 	mutex_enter(&sc->sc_res_mutex);
174*3bfaa971Sbrad 	switch(len) {
175*3bfaa971Sbrad 	case MCP2221_RES_BUFFER_SIZE:
176*3bfaa971Sbrad 		if (sc->sc_res_buffer != NULL) {
177*3bfaa971Sbrad 			memcpy(sc->sc_res_buffer, ibuf, MCP2221_RES_BUFFER_SIZE);
178*3bfaa971Sbrad 			sc->sc_res_ready = true;
179*3bfaa971Sbrad 			cv_signal(&sc->sc_res_cv);
180*3bfaa971Sbrad 		} else {
181*3bfaa971Sbrad 			int d=umcpmiodebug;
182*3bfaa971Sbrad 			device_printf(sc->sc_dev,"umcpmio_uhidev_intr: NULL sc_res_buffer: len=%d\n",len);
183*3bfaa971Sbrad 			umcpmiodebug=20;
184*3bfaa971Sbrad 			umcpmio_dump_buffer(true, (uint8_t *)ibuf, len, "umcpmio_uhidev_intr: ibuf");
185*3bfaa971Sbrad 			umcpmiodebug=d;
186*3bfaa971Sbrad 		}
187*3bfaa971Sbrad 
188*3bfaa971Sbrad 		break;
189*3bfaa971Sbrad 	default:
190*3bfaa971Sbrad 		device_printf(sc->sc_dev,"umcpmio_uhidev_intr: Unknown interrupt length: %d",len);
191*3bfaa971Sbrad 		break;
192*3bfaa971Sbrad 	}
193*3bfaa971Sbrad 	mutex_exit(&sc->sc_res_mutex);
194*3bfaa971Sbrad }
195*3bfaa971Sbrad 
196*3bfaa971Sbrad /* Send a HID report.  This needs to be called with the action mutex held */
197*3bfaa971Sbrad 
198*3bfaa971Sbrad int
199*3bfaa971Sbrad umcpmio_send_report(struct umcpmio_softc *sc, uint8_t *sendbuf,
200*3bfaa971Sbrad     size_t sendlen, uint8_t *resbuf, int timeout)
201*3bfaa971Sbrad {
202*3bfaa971Sbrad 	int err = 0;
203*3bfaa971Sbrad 	int err_count=0;
204*3bfaa971Sbrad 
205*3bfaa971Sbrad 	if (sc->sc_dying)
206*3bfaa971Sbrad 		return EIO;
207*3bfaa971Sbrad 
208*3bfaa971Sbrad 	KASSERT(mutex_owned(&sc->sc_action_mutex));
209*3bfaa971Sbrad 
210*3bfaa971Sbrad 	if (sc->sc_res_buffer != NULL) {
211*3bfaa971Sbrad 		device_printf(sc->sc_dev,"umcpmio_send_report: sc->sc_res_buffer is not NULL\n");
212*3bfaa971Sbrad 	}
213*3bfaa971Sbrad 	sc->sc_res_buffer = resbuf;
214*3bfaa971Sbrad 	sc->sc_res_ready = false;
215*3bfaa971Sbrad 
216*3bfaa971Sbrad 	err = uhidev_write(sc->sc_hdev, sendbuf, sendlen);
217*3bfaa971Sbrad 
218*3bfaa971Sbrad 	if (err) {
219*3bfaa971Sbrad 		DPRINTF(("umcpmio_send_report: uhidev_write errored with: err=%d\n", err));
220*3bfaa971Sbrad 		goto out;
221*3bfaa971Sbrad 	}
222*3bfaa971Sbrad 
223*3bfaa971Sbrad 	DPRINTFN(30,("umcpmio_send_report: about to wait on cv.  err=%d\n", err));
224*3bfaa971Sbrad 
225*3bfaa971Sbrad 	mutex_enter(&sc->sc_res_mutex);
226*3bfaa971Sbrad 	while (!sc->sc_res_ready) {
227*3bfaa971Sbrad 		DPRINTFN(20,("umcpmio_send_report: LOOP for response.  sc_res_ready=%d, err_count=%d, timeout=%d\n",sc->sc_res_ready, err_count, mstohz(timeout)));
228*3bfaa971Sbrad 
229*3bfaa971Sbrad 		err = cv_timedwait_sig(&sc->sc_res_cv, &sc->sc_res_mutex, mstohz(timeout));
230*3bfaa971Sbrad 
231*3bfaa971Sbrad 		/* We are only going to allow this to loop on an error,
232*3bfaa971Sbrad 		 * any error at all, so many times.
233*3bfaa971Sbrad 		 */
234*3bfaa971Sbrad 		if (err) {
235*3bfaa971Sbrad 			DPRINTF(("umcpmio_send_report: cv_timedwait_sig reported an error: err=%d, sc->sc_res_ready=%d\n",err,sc->sc_res_ready));
236*3bfaa971Sbrad 			err_count++;
237*3bfaa971Sbrad 		}
238*3bfaa971Sbrad 
239*3bfaa971Sbrad 		/* The CV was interrupted, but the buffer is ready so, clear the error
240*3bfaa971Sbrad 		 * and break out.
241*3bfaa971Sbrad 		 */
242*3bfaa971Sbrad 		if ((err == ERESTART) && (sc->sc_res_ready)) {
243*3bfaa971Sbrad 			DPRINTF(("umcpmio_send_report: ERESTART and buffer is ready\n"));
244*3bfaa971Sbrad 			err = 0;
245*3bfaa971Sbrad 			break;
246*3bfaa971Sbrad 		}
247*3bfaa971Sbrad 
248*3bfaa971Sbrad 		/* Too many times though the loop, just break out.  Turn
249*3bfaa971Sbrad 		 * a ERESTART (interruption) into a I/O error at this point.
250*3bfaa971Sbrad 		 */
251*3bfaa971Sbrad 		if (err_count > sc->sc_response_errcnt) {
252*3bfaa971Sbrad 			DPRINTF(("umcpmio_send_report: err_count exceeded: err=%d\n",err));
253*3bfaa971Sbrad 			if (err == ERESTART)
254*3bfaa971Sbrad 				err = EIO;
255*3bfaa971Sbrad 			break;
256*3bfaa971Sbrad 		}
257*3bfaa971Sbrad 
258*3bfaa971Sbrad 		/* This is a normal timeout, without interruption, try again */
259*3bfaa971Sbrad 		if (err == EWOULDBLOCK) {
260*3bfaa971Sbrad 			DPRINTF(("umcpmio_send_report: EWOULDBLOCK: err_count=%d\n",err_count));
261*3bfaa971Sbrad 			continue;
262*3bfaa971Sbrad 		}
263*3bfaa971Sbrad 
264*3bfaa971Sbrad 		/* The CV was interrupted and the buffer wasn't filled in, so try again */
265*3bfaa971Sbrad 		if ((err == ERESTART) && (!sc->sc_res_ready)) {
266*3bfaa971Sbrad 			DPRINTF(("umcpmio_send_report: ERESTART and buffer is NOT ready.  err_count=%d\n",err_count));
267*3bfaa971Sbrad 			continue;
268*3bfaa971Sbrad 		}
269*3bfaa971Sbrad 	}
270*3bfaa971Sbrad 
271*3bfaa971Sbrad 	sc->sc_res_buffer = NULL;
272*3bfaa971Sbrad 	sc->sc_res_ready = false;
273*3bfaa971Sbrad 	mutex_exit(&sc->sc_res_mutex);
274*3bfaa971Sbrad 
275*3bfaa971Sbrad 	/* Turn most errors into an I/O error */
276*3bfaa971Sbrad 	if (err &&
277*3bfaa971Sbrad 	    err != ERESTART)
278*3bfaa971Sbrad 		err = EIO;
279*3bfaa971Sbrad 
280*3bfaa971Sbrad  out:
281*3bfaa971Sbrad 	return err;
282*3bfaa971Sbrad }
283*3bfaa971Sbrad 
284*3bfaa971Sbrad /* These are standard gpio reads and set calls */
285*3bfaa971Sbrad 
286*3bfaa971Sbrad static int
287*3bfaa971Sbrad umcpmio_gpio_pin_read(void *arg, int pin)
288*3bfaa971Sbrad {
289*3bfaa971Sbrad 	struct umcpmio_softc *sc = arg;
290*3bfaa971Sbrad 	int r = GPIO_PIN_LOW;
291*3bfaa971Sbrad 
292*3bfaa971Sbrad 	r = umcpmio_get_gpio_value(sc, pin, true);
293*3bfaa971Sbrad 
294*3bfaa971Sbrad 	return(r);
295*3bfaa971Sbrad }
296*3bfaa971Sbrad 
297*3bfaa971Sbrad static void
298*3bfaa971Sbrad umcpmio_gpio_pin_write(void *arg, int pin, int value)
299*3bfaa971Sbrad {
300*3bfaa971Sbrad 	struct umcpmio_softc *sc = arg;
301*3bfaa971Sbrad 
302*3bfaa971Sbrad 	umcpmio_set_gpio_value_one(sc, pin, value, true);
303*3bfaa971Sbrad }
304*3bfaa971Sbrad 
305*3bfaa971Sbrad /* Internal function that does the dirty work of setting a gpio
306*3bfaa971Sbrad  * pin to its "type".
307*3bfaa971Sbrad  *
308*3bfaa971Sbrad  * There are really two ways to do some of this, one is to set the pin to input
309*3bfaa971Sbrad  * and output, or whatever, using SRAM calls, the other is to use the GPIO
310*3bfaa971Sbrad  * config calls to set input and output and SRAM for everything else.  This just
311*3bfaa971Sbrad  * uses SRAM for everything.
312*3bfaa971Sbrad  */
313*3bfaa971Sbrad 
314*3bfaa971Sbrad static int
315*3bfaa971Sbrad umcpmio_gpio_pin_ctlctl(void *arg, int pin, int flags, bool takemutex)
316*3bfaa971Sbrad {
317*3bfaa971Sbrad 	struct umcpmio_softc *sc = arg;
318*3bfaa971Sbrad 	struct mcp2221_set_sram_req set_sram_req;
319*3bfaa971Sbrad 	struct mcp2221_set_sram_res set_sram_res;
320*3bfaa971Sbrad 	struct mcp2221_get_sram_res current_sram_res;
321*3bfaa971Sbrad 	struct mcp2221_get_gpio_cfg_res current_gpio_cfg_res;
322*3bfaa971Sbrad 	int err = 0;
323*3bfaa971Sbrad 
324*3bfaa971Sbrad 	if (sc->sc_dying)
325*3bfaa971Sbrad 		return 0;
326*3bfaa971Sbrad 
327*3bfaa971Sbrad 	if (takemutex)
328*3bfaa971Sbrad 		mutex_enter(&sc->sc_action_mutex);
329*3bfaa971Sbrad 
330*3bfaa971Sbrad 	err = umcpmio_get_sram(sc, &current_sram_res, false);
331*3bfaa971Sbrad 	if (err)
332*3bfaa971Sbrad 		goto out;
333*3bfaa971Sbrad 
334*3bfaa971Sbrad 	err = umcpmio_get_gpio_cfg(sc, &current_gpio_cfg_res, false);
335*3bfaa971Sbrad 	if (err)
336*3bfaa971Sbrad 		goto out;
337*3bfaa971Sbrad 
338*3bfaa971Sbrad 	/* You can't just set one pin, you must set all of them, so copy the
339*3bfaa971Sbrad 	 * current settings for the pin we are not messing with.
340*3bfaa971Sbrad 	 *
341*3bfaa971Sbrad 	 * And, yes, of course, if the MCP-2210 is ever supported with this
342*3bfaa971Sbrad 	 * driver, this sort of unrolling will need to be turned into
343*3bfaa971Sbrad 	 * something different, but for now, just unroll as there are only
344*3bfaa971Sbrad 	 * 4 pins to care about.
345*3bfaa971Sbrad 	 *
346*3bfaa971Sbrad 	 */
347*3bfaa971Sbrad 
348*3bfaa971Sbrad 	memset(&set_sram_req, 0, MCP2221_REQ_BUFFER_SIZE);
349*3bfaa971Sbrad 	switch (pin) {
350*3bfaa971Sbrad 	case 0:
351*3bfaa971Sbrad 		set_sram_req.gp1_settings = current_sram_res.gp1_settings;
352*3bfaa971Sbrad 		set_sram_req.gp2_settings = current_sram_res.gp2_settings;
353*3bfaa971Sbrad 		set_sram_req.gp3_settings = current_sram_res.gp3_settings;
354*3bfaa971Sbrad 		break;
355*3bfaa971Sbrad 	case 1:
356*3bfaa971Sbrad 		set_sram_req.gp0_settings = current_sram_res.gp0_settings;
357*3bfaa971Sbrad 		set_sram_req.gp2_settings = current_sram_res.gp2_settings;
358*3bfaa971Sbrad 		set_sram_req.gp3_settings = current_sram_res.gp3_settings;
359*3bfaa971Sbrad 		break;
360*3bfaa971Sbrad 	case 2:
361*3bfaa971Sbrad 		set_sram_req.gp0_settings = current_sram_res.gp0_settings;
362*3bfaa971Sbrad 		set_sram_req.gp1_settings = current_sram_res.gp1_settings;
363*3bfaa971Sbrad 		set_sram_req.gp3_settings = current_sram_res.gp3_settings;
364*3bfaa971Sbrad 		break;
365*3bfaa971Sbrad 	case 3:
366*3bfaa971Sbrad 		set_sram_req.gp0_settings = current_sram_res.gp0_settings;
367*3bfaa971Sbrad 		set_sram_req.gp1_settings = current_sram_res.gp1_settings;
368*3bfaa971Sbrad 		set_sram_req.gp2_settings = current_sram_res.gp2_settings;
369*3bfaa971Sbrad 		break;
370*3bfaa971Sbrad 	}
371*3bfaa971Sbrad 	umcpmio_set_gpio_designation_sram(&set_sram_req, pin, flags);
372*3bfaa971Sbrad 	umcpmio_set_gpio_dir_sram(&set_sram_req, pin, flags);
373*3bfaa971Sbrad 
374*3bfaa971Sbrad 	/*
375*3bfaa971Sbrad 	* This part is unfortunate...  if a pin is set to output, the value set
376*3bfaa971Sbrad 	* on the pin is not mirrored by the chip into SRAM, but the chip will
377*3bfaa971Sbrad 	* use the value from SRAM to set the value of the pin.  What this means is
378*3bfaa971Sbrad 	* that we have to learn the value from the GPIO config and make sure it is
379*3bfaa971Sbrad 	* set properly when updating SRAM.
380*3bfaa971Sbrad 	*/
381*3bfaa971Sbrad 
382*3bfaa971Sbrad 	if (current_gpio_cfg_res.gp0_pin_dir == MCP2221_GPIO_CFG_DIR_OUTPUT) {
383*3bfaa971Sbrad 		if (current_gpio_cfg_res.gp0_pin_value == 1)
384*3bfaa971Sbrad 			set_sram_req.gp0_settings |= MCP2221_SRAM_GPIO_OUTPUT_HIGH;
385*3bfaa971Sbrad 		else
386*3bfaa971Sbrad 			set_sram_req.gp0_settings &= ~MCP2221_SRAM_GPIO_OUTPUT_HIGH;
387*3bfaa971Sbrad 	}
388*3bfaa971Sbrad 	if (current_gpio_cfg_res.gp1_pin_dir == MCP2221_GPIO_CFG_DIR_OUTPUT) {
389*3bfaa971Sbrad 		if (current_gpio_cfg_res.gp1_pin_value == 1)
390*3bfaa971Sbrad 			set_sram_req.gp1_settings |= MCP2221_SRAM_GPIO_OUTPUT_HIGH;
391*3bfaa971Sbrad 		else
392*3bfaa971Sbrad 			set_sram_req.gp1_settings &= ~MCP2221_SRAM_GPIO_OUTPUT_HIGH;
393*3bfaa971Sbrad 	}
394*3bfaa971Sbrad 	if (current_gpio_cfg_res.gp2_pin_dir == MCP2221_GPIO_CFG_DIR_OUTPUT) {
395*3bfaa971Sbrad 		if (current_gpio_cfg_res.gp2_pin_value == 1)
396*3bfaa971Sbrad 			set_sram_req.gp2_settings |= MCP2221_SRAM_GPIO_OUTPUT_HIGH;
397*3bfaa971Sbrad 		else
398*3bfaa971Sbrad 			set_sram_req.gp2_settings &= ~MCP2221_SRAM_GPIO_OUTPUT_HIGH;
399*3bfaa971Sbrad 	}
400*3bfaa971Sbrad 	if (current_gpio_cfg_res.gp3_pin_dir == MCP2221_GPIO_CFG_DIR_OUTPUT) {
401*3bfaa971Sbrad 		if (current_gpio_cfg_res.gp3_pin_value == 1)
402*3bfaa971Sbrad 			set_sram_req.gp3_settings |= MCP2221_SRAM_GPIO_OUTPUT_HIGH;
403*3bfaa971Sbrad 		else
404*3bfaa971Sbrad 			set_sram_req.gp3_settings &= ~MCP2221_SRAM_GPIO_OUTPUT_HIGH;
405*3bfaa971Sbrad 	}
406*3bfaa971Sbrad 
407*3bfaa971Sbrad 	err = umcpmio_put_sram(sc, &set_sram_req, &set_sram_res, false);
408*3bfaa971Sbrad 	if (! err) {
409*3bfaa971Sbrad 		umcpmio_dump_buffer(sc->sc_dumpbuffer, (uint8_t *)&set_sram_res, MCP2221_RES_BUFFER_SIZE, "umcpmio_gpio_pin_ctlctl set sram buffer copy");
410*3bfaa971Sbrad 		if (set_sram_res.cmd == MCP2221_CMD_SET_SRAM &&
411*3bfaa971Sbrad 		    set_sram_res.completion == MCP2221_CMD_COMPLETE_OK) {
412*3bfaa971Sbrad 			sc->sc_gpio_pins[pin].pin_flags = flags;
413*3bfaa971Sbrad 		} else {
414*3bfaa971Sbrad 			device_printf(sc->sc_dev, "umcpmio_gpio_pin_ctlctl: not the command desired, or error: %02x %02x\n",
415*3bfaa971Sbrad 			    set_sram_res.cmd,
416*3bfaa971Sbrad 			    set_sram_res.completion);
417*3bfaa971Sbrad 			err = EIO;
418*3bfaa971Sbrad 		}
419*3bfaa971Sbrad 	}
420*3bfaa971Sbrad 
421*3bfaa971Sbrad  out:
422*3bfaa971Sbrad 	if (takemutex)
423*3bfaa971Sbrad 		mutex_exit(&sc->sc_action_mutex);
424*3bfaa971Sbrad 
425*3bfaa971Sbrad 	return err;
426*3bfaa971Sbrad }
427*3bfaa971Sbrad 
428*3bfaa971Sbrad static void
429*3bfaa971Sbrad umcpmio_gpio_pin_ctl(void *arg, int pin, int flags)
430*3bfaa971Sbrad {
431*3bfaa971Sbrad 	struct umcpmio_softc *sc = arg;
432*3bfaa971Sbrad 
433*3bfaa971Sbrad 	if (sc->sc_dying)
434*3bfaa971Sbrad 		return;
435*3bfaa971Sbrad 
436*3bfaa971Sbrad 	umcpmio_gpio_pin_ctlctl(sc, pin, flags, true);
437*3bfaa971Sbrad }
438*3bfaa971Sbrad 
439*3bfaa971Sbrad /*
440*3bfaa971Sbrad   XXX -
441*3bfaa971Sbrad 
442*3bfaa971Sbrad   Since testing of gpio interrupts wasn't possible, this part probably is not
443*3bfaa971Sbrad   complete.  At the very least, there is a scheduled callout that needs to exist
444*3bfaa971Sbrad   to read the interrupt status.  The chip does not send anything on its own when
445*3bfaa971Sbrad   the interrupt happens.
446*3bfaa971Sbrad  */
447*3bfaa971Sbrad 
448*3bfaa971Sbrad 
449*3bfaa971Sbrad static void *
450*3bfaa971Sbrad umcpmio_gpio_intr_establish(void *vsc, int pin, int ipl, int irqmode,
451*3bfaa971Sbrad     int (*func)(void *), void *arg)
452*3bfaa971Sbrad {
453*3bfaa971Sbrad 	struct umcpmio_softc *sc = vsc;
454*3bfaa971Sbrad 	struct umcpmio_irq *irq = &sc->sc_gpio_irqs[0];
455*3bfaa971Sbrad 	struct mcp2221_set_sram_req set_sram_req;
456*3bfaa971Sbrad 	struct mcp2221_set_sram_res set_sram_res;
457*3bfaa971Sbrad 	struct mcp2221_get_sram_res current_sram_res;
458*3bfaa971Sbrad 	int err = 0;
459*3bfaa971Sbrad 
460*3bfaa971Sbrad 	if (sc->sc_dying)
461*3bfaa971Sbrad 		return(NULL);
462*3bfaa971Sbrad 
463*3bfaa971Sbrad 	irq->sc_gpio_irqfunc = func;
464*3bfaa971Sbrad 	irq->sc_gpio_irqarg = arg;
465*3bfaa971Sbrad 
466*3bfaa971Sbrad 	DPRINTF(("umcpmio_intr_establish: pin=%d, irqmode=%04x\n",pin,irqmode));
467*3bfaa971Sbrad 
468*3bfaa971Sbrad 	mutex_enter(&sc->sc_action_mutex);
469*3bfaa971Sbrad 
470*3bfaa971Sbrad 	err = umcpmio_get_sram(sc, &current_sram_res, false);
471*3bfaa971Sbrad 	if (err)
472*3bfaa971Sbrad 		goto out;
473*3bfaa971Sbrad 
474*3bfaa971Sbrad 	memset(&set_sram_req, 0, MCP2221_REQ_BUFFER_SIZE);
475*3bfaa971Sbrad 	set_sram_req.gp0_settings = current_sram_res.gp0_settings;
476*3bfaa971Sbrad 	set_sram_req.gp2_settings = current_sram_res.gp2_settings;
477*3bfaa971Sbrad 	set_sram_req.gp3_settings = current_sram_res.gp3_settings;
478*3bfaa971Sbrad 	umcpmio_set_gpio_irq_sram(&set_sram_req, irqmode);
479*3bfaa971Sbrad 	err = umcpmio_put_sram(sc, &set_sram_req, &set_sram_res, false);
480*3bfaa971Sbrad 	if (! err) {
481*3bfaa971Sbrad 		umcpmio_dump_buffer(sc->sc_dumpbuffer, (uint8_t *)&set_sram_res, MCP2221_RES_BUFFER_SIZE, "umcpmio_intr_establish set sram buffer copy");
482*3bfaa971Sbrad 		if (set_sram_res.cmd == MCP2221_CMD_SET_SRAM &&
483*3bfaa971Sbrad 		    set_sram_res.completion == MCP2221_CMD_COMPLETE_OK) {
484*3bfaa971Sbrad 			sc->sc_gpio_pins[1].pin_flags = GPIO_PIN_ALT2;
485*3bfaa971Sbrad 		} else {
486*3bfaa971Sbrad 			device_printf(sc->sc_dev, "umcpmio_intr_establish: not the command desired, or error: %02x %02x\n",
487*3bfaa971Sbrad 			    set_sram_res.cmd,
488*3bfaa971Sbrad 			    set_sram_res.completion);
489*3bfaa971Sbrad 		}
490*3bfaa971Sbrad 	} else {
491*3bfaa971Sbrad 		device_printf(sc->sc_dev, "umcpmio_intr_establish: set sram error: err=%d\n",
492*3bfaa971Sbrad 		    err);
493*3bfaa971Sbrad 	}
494*3bfaa971Sbrad 
495*3bfaa971Sbrad  out:
496*3bfaa971Sbrad 	mutex_exit(&sc->sc_action_mutex);
497*3bfaa971Sbrad 
498*3bfaa971Sbrad 	return(irq);
499*3bfaa971Sbrad }
500*3bfaa971Sbrad 
501*3bfaa971Sbrad static void
502*3bfaa971Sbrad umcpmio_gpio_intr_disestablish(void *vsc, void *ih)
503*3bfaa971Sbrad {
504*3bfaa971Sbrad 	struct umcpmio_softc *sc = vsc;
505*3bfaa971Sbrad 	struct mcp2221_set_sram_req set_sram_req;
506*3bfaa971Sbrad 	struct mcp2221_set_sram_res set_sram_res;
507*3bfaa971Sbrad 	struct mcp2221_get_sram_res current_sram_res;
508*3bfaa971Sbrad 	int err = 0;
509*3bfaa971Sbrad 
510*3bfaa971Sbrad 	if (sc->sc_dying)
511*3bfaa971Sbrad 		return;
512*3bfaa971Sbrad 
513*3bfaa971Sbrad 	DPRINTF(("umcpmio_intr_disestablish:\n"));
514*3bfaa971Sbrad 
515*3bfaa971Sbrad 	mutex_enter(&sc->sc_action_mutex);
516*3bfaa971Sbrad 
517*3bfaa971Sbrad 	err = umcpmio_get_sram(sc, &current_sram_res, false);
518*3bfaa971Sbrad 	if (err)
519*3bfaa971Sbrad 		goto out;
520*3bfaa971Sbrad 
521*3bfaa971Sbrad 	memset(&set_sram_req, 0, MCP2221_REQ_BUFFER_SIZE);
522*3bfaa971Sbrad 	set_sram_req.gp0_settings = current_sram_res.gp0_settings;
523*3bfaa971Sbrad 	set_sram_req.gp2_settings = current_sram_res.gp2_settings;
524*3bfaa971Sbrad 	set_sram_req.gp3_settings = current_sram_res.gp3_settings;
525*3bfaa971Sbrad 	umcpmio_set_gpio_irq_sram(&set_sram_req, 0);
526*3bfaa971Sbrad 	err = umcpmio_put_sram(sc, &set_sram_req, &set_sram_res, true);
527*3bfaa971Sbrad 	if (! err) {
528*3bfaa971Sbrad 		umcpmio_dump_buffer(sc->sc_dumpbuffer, (uint8_t *)&set_sram_res, MCP2221_RES_BUFFER_SIZE, "umcpmio_intr_disestablish set sram buffer copy");
529*3bfaa971Sbrad 		if (set_sram_res.cmd == MCP2221_CMD_SET_SRAM &&
530*3bfaa971Sbrad 		    set_sram_res.completion == MCP2221_CMD_COMPLETE_OK) {
531*3bfaa971Sbrad 			sc->sc_gpio_pins[1].pin_flags = GPIO_PIN_INPUT;
532*3bfaa971Sbrad 		} else {
533*3bfaa971Sbrad 			device_printf(sc->sc_dev, "umcpmio_intr_disestablish: not the command desired, or error: %02x %02x\n",
534*3bfaa971Sbrad 			    set_sram_res.cmd,
535*3bfaa971Sbrad 			    set_sram_res.completion);
536*3bfaa971Sbrad 		}
537*3bfaa971Sbrad 	} else {
538*3bfaa971Sbrad 		device_printf(sc->sc_dev, "umcpmio_intr_disestablish: set sram error: err=%d\n",
539*3bfaa971Sbrad 		    err);
540*3bfaa971Sbrad 	}
541*3bfaa971Sbrad  out:
542*3bfaa971Sbrad 	mutex_exit(&sc->sc_action_mutex);
543*3bfaa971Sbrad }
544*3bfaa971Sbrad 
545*3bfaa971Sbrad static bool
546*3bfaa971Sbrad umcpmio_gpio_intrstr(void *vsc, int pin, int irqmode, char *buf, size_t buflen)
547*3bfaa971Sbrad {
548*3bfaa971Sbrad 
549*3bfaa971Sbrad         if (pin < 0 || pin >= MCP2221_NPINS) {
550*3bfaa971Sbrad 		DPRINTF(("umcpmio_gpio_intrstr: pin %d less than zero or too big\n",pin));
551*3bfaa971Sbrad                 return (false);
552*3bfaa971Sbrad 	}
553*3bfaa971Sbrad 
554*3bfaa971Sbrad 	if (pin != 1) {
555*3bfaa971Sbrad 		DPRINTF(("umcpmio_gpio_intrstr: pin %d was not 1\n",pin));
556*3bfaa971Sbrad 		return (false);
557*3bfaa971Sbrad 	}
558*3bfaa971Sbrad 
559*3bfaa971Sbrad         snprintf(buf, buflen, "GPIO %d", pin);
560*3bfaa971Sbrad 
561*3bfaa971Sbrad         return (true);
562*3bfaa971Sbrad }
563*3bfaa971Sbrad 
564*3bfaa971Sbrad /* Clear status of the I2C engine */
565*3bfaa971Sbrad 
566*3bfaa971Sbrad static int
567*3bfaa971Sbrad umcpmio_i2c_clear(struct umcpmio_softc *sc, bool takemutex)
568*3bfaa971Sbrad {
569*3bfaa971Sbrad 	int err = 0;
570*3bfaa971Sbrad 	struct mcp2221_status_req status_req;
571*3bfaa971Sbrad 	struct mcp2221_status_res status_res;
572*3bfaa971Sbrad 
573*3bfaa971Sbrad 	memset(&status_req, 0, MCP2221_REQ_BUFFER_SIZE);
574*3bfaa971Sbrad 	status_req.cmd = MCP2221_CMD_STATUS;
575*3bfaa971Sbrad 	status_req.cancel_transfer = MCP2221_I2C_DO_CANCEL;
576*3bfaa971Sbrad 
577*3bfaa971Sbrad 	if (takemutex)
578*3bfaa971Sbrad 		mutex_enter(&sc->sc_action_mutex);
579*3bfaa971Sbrad 	err = umcpmio_send_report(sc, (uint8_t *)&status_req, MCP2221_REQ_BUFFER_SIZE, (uint8_t *)&status_res, sc->sc_cv_wait);
580*3bfaa971Sbrad 	if (takemutex)
581*3bfaa971Sbrad 		mutex_exit(&sc->sc_action_mutex);
582*3bfaa971Sbrad 
583*3bfaa971Sbrad 	if (! err) {
584*3bfaa971Sbrad 		umcpmio_dump_buffer(sc->sc_dumpbuffer, (uint8_t *)&status_res, MCP2221_RES_BUFFER_SIZE, "umcpmio_i2c_clear buffer copy");
585*3bfaa971Sbrad 		if (status_res.cmd == MCP2221_CMD_STATUS &&
586*3bfaa971Sbrad 		    status_res.completion == MCP2221_CMD_COMPLETE_OK) {
587*3bfaa971Sbrad 			umcpmio_dump_buffer(true, (uint8_t *)&status_res, MCP2221_RES_BUFFER_SIZE, "umcpmio_i2c_clear res buffer");
588*3bfaa971Sbrad 		} else {
589*3bfaa971Sbrad 			device_printf(sc->sc_dev, "umcpmio_i2c_clear: cmd exec: not the command desired, or error: %02x %02x\n",
590*3bfaa971Sbrad 			    status_res.cmd,
591*3bfaa971Sbrad 			    status_res.completion);
592*3bfaa971Sbrad 			err = EIO;
593*3bfaa971Sbrad 		}
594*3bfaa971Sbrad 	} else {
595*3bfaa971Sbrad 		device_printf(sc->sc_dev, "umcpmio_i2c_clear: request error: err=%d\n", err);
596*3bfaa971Sbrad 		err = EIO;
597*3bfaa971Sbrad 	}
598*3bfaa971Sbrad 
599*3bfaa971Sbrad 	return(err);
600*3bfaa971Sbrad }
601*3bfaa971Sbrad 
602*3bfaa971Sbrad /* There isn't much required to acquire or release the I2C bus, but the man
603*3bfaa971Sbrad  * pages says these are needed
604*3bfaa971Sbrad  */
605*3bfaa971Sbrad 
606*3bfaa971Sbrad static int
607*3bfaa971Sbrad umcpmio_acquire_bus(void *v, int flags)
608*3bfaa971Sbrad {
609*3bfaa971Sbrad 	return(0);
610*3bfaa971Sbrad }
611*3bfaa971Sbrad 
612*3bfaa971Sbrad static void
613*3bfaa971Sbrad umcpmio_release_bus(void *v, int flags)
614*3bfaa971Sbrad {
615*3bfaa971Sbrad 	return;
616*3bfaa971Sbrad }
617*3bfaa971Sbrad 
618*3bfaa971Sbrad /* The I2C write and I2C read functions mostly use an algorithm that Adafruit
619*3bfaa971Sbrad  * came up with in their Python based driver.  A lot of other people have used
620*3bfaa971Sbrad  * this same algorithm to good effect.  If changes are made to the I2C read and
621*3bfaa971Sbrad  * write functions, it is HIGHLY advisable that a MCP2221 or MCP2221A be on
622*3bfaa971Sbrad  * hand to test them.
623*3bfaa971Sbrad  */
624*3bfaa971Sbrad 
625*3bfaa971Sbrad 
626*3bfaa971Sbrad /* This is what is considered a fatal return from the engine. */
627*3bfaa971Sbrad 
628*3bfaa971Sbrad static bool
629*3bfaa971Sbrad umcpmio_i2c_fatal(uint8_t state)
630*3bfaa971Sbrad {
631*3bfaa971Sbrad 	int r = false;
632*3bfaa971Sbrad 
633*3bfaa971Sbrad 	if (state == MCP2221_ENGINE_ADDRNACK ||
634*3bfaa971Sbrad 	    state == MCP2221_ENGINE_STARTTIMEOUT ||
635*3bfaa971Sbrad 	    state == MCP2221_ENGINE_REPSTARTTIMEOUT ||
636*3bfaa971Sbrad 	    state == MCP2221_ENGINE_STOPTIMEOUT ||
637*3bfaa971Sbrad 	    state == MCP2221_ENGINE_READTIMEOUT ||
638*3bfaa971Sbrad 	    state == MCP2221_ENGINE_WRITETIMEOUT ||
639*3bfaa971Sbrad 	    state == MCP2221_ENGINE_ADDRTIMEOUT)
640*3bfaa971Sbrad 		r = true;
641*3bfaa971Sbrad 	return(r);
642*3bfaa971Sbrad }
643*3bfaa971Sbrad 
644*3bfaa971Sbrad static int
645*3bfaa971Sbrad umcpmio_i2c_write(struct umcpmio_softc *sc, i2c_op_t op, i2c_addr_t addr,
646*3bfaa971Sbrad     const void *cmdbuf, size_t cmdlen, void *databuf, size_t datalen, int flags)
647*3bfaa971Sbrad {
648*3bfaa971Sbrad 	struct mcp2221_i2c_req i2c_req;
649*3bfaa971Sbrad 	struct mcp2221_i2c_res i2c_res;
650*3bfaa971Sbrad 	struct mcp2221_status_res status_res;
651*3bfaa971Sbrad 	int remaining;
652*3bfaa971Sbrad 	int err = 0;
653*3bfaa971Sbrad 	uint8_t cmd;
654*3bfaa971Sbrad 	size_t totallen = 0;
655*3bfaa971Sbrad 	int wretry = sc->sc_retry_busy_write;
656*3bfaa971Sbrad 	int wsretry = sc->sc_retry_busy_write;
657*3bfaa971Sbrad 
658*3bfaa971Sbrad 	err = umcpmio_get_status(sc, &status_res, true);
659*3bfaa971Sbrad 	if (err)
660*3bfaa971Sbrad 		goto out;
661*3bfaa971Sbrad 	if (status_res.internal_i2c_state != 0) {
662*3bfaa971Sbrad 		DPRINTF(("umcpmio_i2c_write: internal state not zero, clearing. internal_i2c_state=%02x\n",status_res.internal_i2c_state));
663*3bfaa971Sbrad 		err = umcpmio_i2c_clear(sc, true);
664*3bfaa971Sbrad 	}
665*3bfaa971Sbrad 	if (err)
666*3bfaa971Sbrad 		goto out;
667*3bfaa971Sbrad 
668*3bfaa971Sbrad 	if (cmdbuf != NULL)
669*3bfaa971Sbrad 		totallen += cmdlen;
670*3bfaa971Sbrad 	if (databuf != NULL)
671*3bfaa971Sbrad 		totallen += datalen;
672*3bfaa971Sbrad 
673*3bfaa971Sbrad  again:
674*3bfaa971Sbrad 	memset(&i2c_req, 0, MCP2221_REQ_BUFFER_SIZE);
675*3bfaa971Sbrad 	cmd = MCP2221_I2C_WRITE_DATA_NS;
676*3bfaa971Sbrad 	if (I2C_OP_STOP_P(op))
677*3bfaa971Sbrad 		cmd = MCP2221_I2C_WRITE_DATA;
678*3bfaa971Sbrad 	i2c_req.cmd = cmd;
679*3bfaa971Sbrad 	i2c_req.lsblen = totallen;
680*3bfaa971Sbrad 	i2c_req.msblen = 0;
681*3bfaa971Sbrad 	i2c_req.slaveaddr = addr << 1;
682*3bfaa971Sbrad 
683*3bfaa971Sbrad 	remaining = 0;
684*3bfaa971Sbrad 	if (cmdbuf != NULL) {
685*3bfaa971Sbrad 		memcpy(&i2c_req.data[0], cmdbuf, cmdlen);
686*3bfaa971Sbrad 		remaining = cmdlen;
687*3bfaa971Sbrad 	}
688*3bfaa971Sbrad 	if (databuf != NULL)
689*3bfaa971Sbrad 		memcpy(&i2c_req.data[remaining], databuf, datalen);
690*3bfaa971Sbrad 
691*3bfaa971Sbrad 	DPRINTF(("umcpmio_i2c_write: I2C WRITE: cmd: %02x\n",cmd));
692*3bfaa971Sbrad 	umcpmio_dump_buffer(sc->sc_dumpbuffer, (uint8_t *)&i2c_req, MCP2221_REQ_BUFFER_SIZE, "umcpmio_i2c_write: write req buffer copy");
693*3bfaa971Sbrad 
694*3bfaa971Sbrad 	mutex_enter(&sc->sc_action_mutex);
695*3bfaa971Sbrad 	err = umcpmio_send_report(sc, (uint8_t *)&i2c_req, MCP2221_REQ_BUFFER_SIZE, (uint8_t *)&i2c_res, sc->sc_cv_wait);
696*3bfaa971Sbrad 	mutex_exit(&sc->sc_action_mutex);
697*3bfaa971Sbrad 	if (! err) {
698*3bfaa971Sbrad 		umcpmio_dump_buffer(sc->sc_dumpbuffer, (uint8_t *)&i2c_res, MCP2221_RES_BUFFER_SIZE, "umcpmio_i2c_write: write res buffer copy");
699*3bfaa971Sbrad 		if (i2c_res.cmd == cmd &&
700*3bfaa971Sbrad 		    i2c_res.completion == MCP2221_CMD_COMPLETE_OK) {
701*3bfaa971Sbrad 			/* Adafruit does a read back of the status at this
702*3bfaa971Sbrad 			 * point.  We choose not to do that.  That is done later
703*3bfaa971Sbrad 			 * anyway, and it seemed to be redundent.
704*3bfaa971Sbrad 			 */
705*3bfaa971Sbrad 		} else {
706*3bfaa971Sbrad 			if (i2c_res.cmd == cmd &&
707*3bfaa971Sbrad 			    i2c_res.completion == MCP2221_I2C_ENGINE_BUSY) {
708*3bfaa971Sbrad 				DPRINTF(("umcpmio_i2c_write: I2C engine busy\n"));
709*3bfaa971Sbrad 
710*3bfaa971Sbrad 				if (umcpmio_i2c_fatal(i2c_res.internal_i2c_state)) {
711*3bfaa971Sbrad 					err = EIO;
712*3bfaa971Sbrad 				} else {
713*3bfaa971Sbrad 					wretry--;
714*3bfaa971Sbrad 					if (wretry > 0) {
715*3bfaa971Sbrad 						WAITMS(sc->sc_busy_delay);
716*3bfaa971Sbrad 						goto again;
717*3bfaa971Sbrad 					} else {
718*3bfaa971Sbrad 						err = EBUSY;
719*3bfaa971Sbrad 					}
720*3bfaa971Sbrad 				}
721*3bfaa971Sbrad 			} else {
722*3bfaa971Sbrad 				device_printf(sc->sc_dev, "umcpmio_i2c_write:  not the command desired, or error: %02x %02x\n",
723*3bfaa971Sbrad 				    i2c_res.cmd,
724*3bfaa971Sbrad 				    i2c_res.completion);
725*3bfaa971Sbrad 				err = EIO;
726*3bfaa971Sbrad 			}
727*3bfaa971Sbrad 		}
728*3bfaa971Sbrad 	} else {
729*3bfaa971Sbrad 		device_printf(sc->sc_dev, "umcpmio_i2c_write request error: err=%d\n", err);
730*3bfaa971Sbrad 		err = EIO;
731*3bfaa971Sbrad 	}
732*3bfaa971Sbrad 
733*3bfaa971Sbrad 	if (! err) {
734*3bfaa971Sbrad 		while (wsretry > 0) {
735*3bfaa971Sbrad 			wsretry--;
736*3bfaa971Sbrad 
737*3bfaa971Sbrad 			DPRINTF(("umcpmio_i2c_write: checking status loop: wcretry=%d\n",wsretry));
738*3bfaa971Sbrad 
739*3bfaa971Sbrad 			err = umcpmio_get_status(sc, &status_res, true);
740*3bfaa971Sbrad 			if (! err) {
741*3bfaa971Sbrad 				umcpmio_dump_buffer(sc->sc_dumpbuffer, (uint8_t *)&status_res, MCP2221_RES_BUFFER_SIZE, "umcpmio_i2c_write post check status");
742*3bfaa971Sbrad 				/* Since there isn't any documentation on what
743*3bfaa971Sbrad 				 * some of the internal state means, it isn't
744*3bfaa971Sbrad 				 * clear that this is any different than than
745*3bfaa971Sbrad 				 * MCP2221_ENGINE_ADDRNACK in the other state
746*3bfaa971Sbrad 				 * register.
747*3bfaa971Sbrad 				 */
748*3bfaa971Sbrad 
749*3bfaa971Sbrad 				if (status_res.internal_i2c_state20 & MCP2221_ENGINE_T1_MASK_NACK) {
750*3bfaa971Sbrad 					DPRINTF(("umcpmio_i2c_write post check: engine internal state T1 says NACK\n"));
751*3bfaa971Sbrad 					err = EIO;
752*3bfaa971Sbrad 					break;
753*3bfaa971Sbrad 				}
754*3bfaa971Sbrad 				if (status_res.internal_i2c_state == 0) {
755*3bfaa971Sbrad 					DPRINTF(("umcpmio_i2c_write post check: engine internal state is ZERO\n"));
756*3bfaa971Sbrad 					err = 0;
757*3bfaa971Sbrad 					break;
758*3bfaa971Sbrad 				}
759*3bfaa971Sbrad 				if (status_res.internal_i2c_state == MCP2221_ENGINE_WRITINGNOSTOP &&
760*3bfaa971Sbrad 				    cmd == MCP2221_I2C_WRITE_DATA_NS) {
761*3bfaa971Sbrad 					DPRINTF(("umcpmio_i2c_write post check: engine internal state is WRITINGNOSTOP\n"));
762*3bfaa971Sbrad 					err = 0;
763*3bfaa971Sbrad 					break;
764*3bfaa971Sbrad 				}
765*3bfaa971Sbrad 				if (umcpmio_i2c_fatal(status_res.internal_i2c_state)) {
766*3bfaa971Sbrad 					DPRINTF(("umcpmio_i2c_write post check: engine internal state is fatal: %02x\n", status_res.internal_i2c_state));
767*3bfaa971Sbrad 					err = EIO;
768*3bfaa971Sbrad 					break;
769*3bfaa971Sbrad 				}
770*3bfaa971Sbrad 				WAITMS(sc->sc_busy_delay);
771*3bfaa971Sbrad 			} else {
772*3bfaa971Sbrad 				err = EIO;
773*3bfaa971Sbrad 				break;
774*3bfaa971Sbrad 			}
775*3bfaa971Sbrad 		}
776*3bfaa971Sbrad 	}
777*3bfaa971Sbrad 
778*3bfaa971Sbrad  out:
779*3bfaa971Sbrad 
780*3bfaa971Sbrad 	return(err);
781*3bfaa971Sbrad }
782*3bfaa971Sbrad 
783*3bfaa971Sbrad /* This one deviates a bit from Adafruit in that is supports a straight read and
784*3bfaa971Sbrad  * a write + read.  That is, write a register to read from and then do the read.
785*3bfaa971Sbrad  */
786*3bfaa971Sbrad 
787*3bfaa971Sbrad static int
788*3bfaa971Sbrad umcpmio_i2c_read(struct umcpmio_softc *sc, i2c_op_t op, i2c_addr_t addr,
789*3bfaa971Sbrad     const void *cmdbuf, size_t cmdlen, void *databuf, size_t datalen, int flags)
790*3bfaa971Sbrad {
791*3bfaa971Sbrad 	struct mcp2221_i2c_req i2c_req;
792*3bfaa971Sbrad 	struct mcp2221_i2c_res i2c_res;
793*3bfaa971Sbrad 	struct mcp2221_i2c_fetch_req i2c_fetch_req;
794*3bfaa971Sbrad 	struct mcp2221_i2c_fetch_res i2c_fetch_res;
795*3bfaa971Sbrad 	struct mcp2221_status_res status_res;
796*3bfaa971Sbrad 	int err = 0;
797*3bfaa971Sbrad 	uint8_t cmd;
798*3bfaa971Sbrad 	int rretry = sc->sc_retry_busy_read;
799*3bfaa971Sbrad 
800*3bfaa971Sbrad 	if (cmdbuf != NULL) {
801*3bfaa971Sbrad 		DPRINTF(("umcpmio_i2c_read: has a cmdbuf, doing write first: addr=%02x\n",addr));
802*3bfaa971Sbrad 		err = umcpmio_i2c_write(sc, I2C_OP_WRITE, addr, cmdbuf, cmdlen, NULL, 0, flags);
803*3bfaa971Sbrad 	}
804*3bfaa971Sbrad 	if (err)
805*3bfaa971Sbrad 		goto out;
806*3bfaa971Sbrad 
807*3bfaa971Sbrad 	err = umcpmio_get_status(sc, &status_res, true);
808*3bfaa971Sbrad 	if (err)
809*3bfaa971Sbrad 		goto out;
810*3bfaa971Sbrad 
811*3bfaa971Sbrad 	if (status_res.internal_i2c_state !=0 &&
812*3bfaa971Sbrad 	    status_res.internal_i2c_state != MCP2221_ENGINE_WRITINGNOSTOP) {
813*3bfaa971Sbrad 		DPRINTF(("umcpmio_i2c_read: internal state not zero and not WRITINGNOSTOP, clearing. internal_i2c_state=%02x\n",status_res.internal_i2c_state));
814*3bfaa971Sbrad 		err = umcpmio_i2c_clear(sc, true);
815*3bfaa971Sbrad 	}
816*3bfaa971Sbrad 	if (err)
817*3bfaa971Sbrad 		goto out;
818*3bfaa971Sbrad 
819*3bfaa971Sbrad 	memset(&i2c_req, 0, MCP2221_REQ_BUFFER_SIZE);
820*3bfaa971Sbrad 	if (cmdbuf == NULL &&
821*3bfaa971Sbrad 	    status_res.internal_i2c_state != MCP2221_ENGINE_WRITINGNOSTOP) {
822*3bfaa971Sbrad 		cmd = MCP2221_I2C_READ_DATA;
823*3bfaa971Sbrad 	} else {
824*3bfaa971Sbrad 		cmd = MCP2221_I2C_READ_DATA_RS;
825*3bfaa971Sbrad 	}
826*3bfaa971Sbrad 
827*3bfaa971Sbrad 	/* The chip apparently can't do a READ without a STOP operation.  Report that, and try
828*3bfaa971Sbrad 	 * treating it like a READ with a STOP.  This won't work for a lot of devices.
829*3bfaa971Sbrad 	 */
830*3bfaa971Sbrad 
831*3bfaa971Sbrad 	if (!I2C_OP_STOP_P(op) &&
832*3bfaa971Sbrad 	    sc->sc_reportreadnostop)
833*3bfaa971Sbrad 		device_printf(sc->sc_dev,"umcpmio_i2c_read: ************ called with READ without STOP ***************\n");
834*3bfaa971Sbrad 
835*3bfaa971Sbrad 	i2c_req.cmd = cmd;
836*3bfaa971Sbrad 	i2c_req.lsblen = datalen;
837*3bfaa971Sbrad 	i2c_req.msblen = 0;
838*3bfaa971Sbrad 	i2c_req.slaveaddr = (addr << 1) | 0x01;
839*3bfaa971Sbrad 
840*3bfaa971Sbrad 	DPRINTF(("umcpmio_i2c_read: I2C READ normal read: cmd=%02x, addr=%02x\n",cmd,addr));
841*3bfaa971Sbrad 
842*3bfaa971Sbrad 	umcpmio_dump_buffer(sc->sc_dumpbuffer, (uint8_t *)&i2c_req, MCP2221_RES_BUFFER_SIZE, "umcpmio_i2c_read normal read req buffer copy");
843*3bfaa971Sbrad 
844*3bfaa971Sbrad 	mutex_enter(&sc->sc_action_mutex);
845*3bfaa971Sbrad 	err = umcpmio_send_report(sc, (uint8_t *)&i2c_req, MCP2221_REQ_BUFFER_SIZE, (uint8_t *)&i2c_res, sc->sc_cv_wait);
846*3bfaa971Sbrad 	mutex_exit(&sc->sc_action_mutex);
847*3bfaa971Sbrad 
848*3bfaa971Sbrad 	if (! err) {
849*3bfaa971Sbrad 		umcpmio_dump_buffer(sc->sc_dumpbuffer, (uint8_t *)&i2c_res, MCP2221_RES_BUFFER_SIZE, "umcpmio_i2c_read read-request response buffer copy");
850*3bfaa971Sbrad 
851*3bfaa971Sbrad 		while (rretry > 0) {
852*3bfaa971Sbrad 			rretry--;
853*3bfaa971Sbrad 			DPRINTF(("umcpmio_i2c_read: fetch loop: rretry=%d\n",rretry));
854*3bfaa971Sbrad 			err = 0;
855*3bfaa971Sbrad 			memset(&i2c_fetch_req, 0, MCP2221_REQ_BUFFER_SIZE);
856*3bfaa971Sbrad 			i2c_fetch_req.cmd = MCP2221_CMD_I2C_FETCH_READ_DATA;
857*3bfaa971Sbrad 			mutex_enter(&sc->sc_action_mutex);
858*3bfaa971Sbrad 			err = umcpmio_send_report(sc, (uint8_t *)&i2c_fetch_req, MCP2221_REQ_BUFFER_SIZE, (uint8_t *)&i2c_fetch_res, sc->sc_cv_wait);
859*3bfaa971Sbrad 			mutex_exit(&sc->sc_action_mutex);
860*3bfaa971Sbrad 			umcpmio_dump_buffer(sc->sc_dumpbuffer, (uint8_t *)&i2c_fetch_req, MCP2221_RES_BUFFER_SIZE, "umcpmio_i2c_read fetch res buffer copy");
861*3bfaa971Sbrad 
862*3bfaa971Sbrad 			if (i2c_fetch_res.cmd == MCP2221_CMD_I2C_FETCH_READ_DATA) {
863*3bfaa971Sbrad 				if (i2c_fetch_res.completion == MCP2221_FETCH_READ_PARTIALDATA ||
864*3bfaa971Sbrad 				    i2c_fetch_res.fetchlen == MCP2221_FETCH_READERROR) {
865*3bfaa971Sbrad 					DPRINTF(("umcpmio_i2c_read: fetch loop: partial data or read error: completion=%02x,fetchlen=%02x\n",i2c_fetch_res.completion,i2c_fetch_res.fetchlen));
866*3bfaa971Sbrad 					WAITMS(sc->sc_busy_delay);
867*3bfaa971Sbrad 					err = EAGAIN;
868*3bfaa971Sbrad 					continue;
869*3bfaa971Sbrad 				}
870*3bfaa971Sbrad 				if (i2c_fetch_res.internal_i2c_state == MCP2221_ENGINE_ADDRNACK) {
871*3bfaa971Sbrad 					DPRINTF(("umcpmio_i2c_read: fetch loop: engine NACK\n"));
872*3bfaa971Sbrad 					err = EIO;
873*3bfaa971Sbrad 					break;
874*3bfaa971Sbrad 				}
875*3bfaa971Sbrad 				if (i2c_fetch_res.internal_i2c_state == 0 &&
876*3bfaa971Sbrad 				    i2c_fetch_res.fetchlen == 0) {
877*3bfaa971Sbrad 					DPRINTF(("umcpmio_i2c_read: fetch loop: internal state and fetch len are ZERO\n"));
878*3bfaa971Sbrad 					err = 0;
879*3bfaa971Sbrad 					break;
880*3bfaa971Sbrad 				}
881*3bfaa971Sbrad 				if (i2c_fetch_res.internal_i2c_state == MCP2221_ENGINE_READPARTIAL ||
882*3bfaa971Sbrad 				    i2c_fetch_res.internal_i2c_state == MCP2221_ENGINE_READCOMPLETE) {
883*3bfaa971Sbrad 					DPRINTF(("umcpmio_i2c_read: fetch loop: read partial or read complete: internal_i2c_state=%02x\n", i2c_fetch_res.internal_i2c_state));
884*3bfaa971Sbrad 					err = 0;
885*3bfaa971Sbrad 					break;
886*3bfaa971Sbrad 				}
887*3bfaa971Sbrad 			} else {
888*3bfaa971Sbrad 				device_printf(sc->sc_dev, "umcpmio_i2c_read: fetch2: not the command desired: %02x\n",
889*3bfaa971Sbrad 				    i2c_fetch_res.cmd);
890*3bfaa971Sbrad 				err = EIO;
891*3bfaa971Sbrad 				break;
892*3bfaa971Sbrad 			}
893*3bfaa971Sbrad 		}
894*3bfaa971Sbrad 		if (err == EAGAIN)
895*3bfaa971Sbrad 			err = ETIMEDOUT;
896*3bfaa971Sbrad 
897*3bfaa971Sbrad 		if (! err) {
898*3bfaa971Sbrad 			if (databuf != NULL &&
899*3bfaa971Sbrad 			    i2c_fetch_res.fetchlen != MCP2221_FETCH_READERROR) {
900*3bfaa971Sbrad 				int size = uimin(i2c_fetch_res.fetchlen, datalen);
901*3bfaa971Sbrad 				DPRINTF(("umcpmio_i2c_read: copy data: size=%d,fetchlen=%d\n",size, i2c_fetch_res.fetchlen));
902*3bfaa971Sbrad 				if (size > 0)
903*3bfaa971Sbrad 					memcpy(databuf, &i2c_fetch_res.data[0], size);
904*3bfaa971Sbrad 			} else {
905*3bfaa971Sbrad 				DPRINTF(("umcpmio_i2c_read: copy data: databuf is NULL\n"));
906*3bfaa971Sbrad 			}
907*3bfaa971Sbrad 		}
908*3bfaa971Sbrad 	} else {
909*3bfaa971Sbrad 		device_printf(sc->sc_dev, "umcpmio_i2c_read request error: cmd=%02x,err=%d\n", cmd, err);
910*3bfaa971Sbrad 		err = EIO;
911*3bfaa971Sbrad 	}
912*3bfaa971Sbrad  out:
913*3bfaa971Sbrad 
914*3bfaa971Sbrad 	return(err);
915*3bfaa971Sbrad }
916*3bfaa971Sbrad 
917*3bfaa971Sbrad static int
918*3bfaa971Sbrad umcpmio_i2c_exec(void *v, i2c_op_t op, i2c_addr_t addr, const void *cmdbuf,
919*3bfaa971Sbrad     size_t cmdlen, void *databuf, size_t datalen, int flags)
920*3bfaa971Sbrad {
921*3bfaa971Sbrad 	struct umcpmio_softc *sc = v;
922*3bfaa971Sbrad 	size_t totallen = 0;
923*3bfaa971Sbrad 	int err = 0;
924*3bfaa971Sbrad 
925*3bfaa971Sbrad 	if (addr > 0x7f)
926*3bfaa971Sbrad 		return (ENOTSUP);
927*3bfaa971Sbrad 
928*3bfaa971Sbrad 	if (cmdbuf != NULL)
929*3bfaa971Sbrad 		totallen += cmdlen;
930*3bfaa971Sbrad 	if (databuf != NULL)
931*3bfaa971Sbrad 		totallen += datalen;
932*3bfaa971Sbrad 
933*3bfaa971Sbrad 	/* There is a way to do a transfer that is larger than 60 bytes,
934*3bfaa971Sbrad 	 * but it requires that your break the transfer up into pieces and
935*3bfaa971Sbrad 	 * send them in 60 byte chunks.  We just won't support that right now.
936*3bfaa971Sbrad 	 * It would be somewhat unusual for there to be a transfer that big,
937*3bfaa971Sbrad 	 * unless you are trying to do block transfers and that isn't natively
938*3bfaa971Sbrad 	 * supported by the chip anyway...  so those have to be broken up and
939*3bfaa971Sbrad 	 * sent as bytes.
940*3bfaa971Sbrad 	 */
941*3bfaa971Sbrad 
942*3bfaa971Sbrad 	if (totallen > 60)
943*3bfaa971Sbrad 		return (ENOTSUP);
944*3bfaa971Sbrad 
945*3bfaa971Sbrad 	if (I2C_OP_WRITE_P(op)) {
946*3bfaa971Sbrad 		err = umcpmio_i2c_write(sc, op, addr, cmdbuf, cmdlen, databuf, datalen, flags);
947*3bfaa971Sbrad 
948*3bfaa971Sbrad 		DPRINTF(("umcpmio_exec: I2C WRITE: err=%d\n", err));
949*3bfaa971Sbrad 	} else {
950*3bfaa971Sbrad 		err = umcpmio_i2c_read(sc, op, addr, cmdbuf, cmdlen, databuf, datalen, flags);
951*3bfaa971Sbrad 
952*3bfaa971Sbrad 		DPRINTF(("umcpmio_exec: I2C READ: err=%d\n", err));
953*3bfaa971Sbrad 	}
954*3bfaa971Sbrad 
955*3bfaa971Sbrad 	return(err);
956*3bfaa971Sbrad }
957*3bfaa971Sbrad 
958*3bfaa971Sbrad /* Accessing the ADC and DAC part of the chip */
959*3bfaa971Sbrad 
960*3bfaa971Sbrad #define UMCPMIO_DEV_UNIT(m) (m & 0x80 ? (m & 0x7f) / 3 : m)
961*3bfaa971Sbrad #define UMCPMIO_DEV_WHAT(m) (m & 0x80 ? ((m & 0x7f) % 3) + 1: CONTROL_DEV)
962*3bfaa971Sbrad 
963*3bfaa971Sbrad static int
964*3bfaa971Sbrad umcpmio_dev_open(dev_t dev, int flags, int fmt, struct lwp *l)
965*3bfaa971Sbrad {
966*3bfaa971Sbrad 	struct umcpmio_softc *sc;
967*3bfaa971Sbrad 	int dunit;
968*3bfaa971Sbrad 	int pin = -1;
969*3bfaa971Sbrad 	int error = 0;
970*3bfaa971Sbrad 
971*3bfaa971Sbrad 	sc = device_lookup_private(&umcpmio_cd, UMCPMIO_DEV_UNIT(minor(dev)));
972*3bfaa971Sbrad 	if (!sc)
973*3bfaa971Sbrad 		return ENXIO;
974*3bfaa971Sbrad 
975*3bfaa971Sbrad 	dunit = UMCPMIO_DEV_WHAT(minor(dev));
976*3bfaa971Sbrad 
977*3bfaa971Sbrad 	if (sc->sc_dev_open[dunit]) {
978*3bfaa971Sbrad 		DPRINTF(("umcpmio_dev_open: dunit=%d BUSY\n",dunit));
979*3bfaa971Sbrad 		return EBUSY;
980*3bfaa971Sbrad 	}
981*3bfaa971Sbrad 
982*3bfaa971Sbrad 	/* The control device only allows for ioctl calls, so pretty much allow
983*3bfaa971Sbrad 	 * any sort of access.  For the ADC, you perform a strict O_RDONLY and
984*3bfaa971Sbrad 	 * for the DAC a strict O_WRONLY.  It is an error to try and do a O_RDWR
985*3bfaa971Sbrad 	 * It makes little sense to try and support select or poll.  The ADC and
986*3bfaa971Sbrad 	 * DAC are always available for use.
987*3bfaa971Sbrad 	 */
988*3bfaa971Sbrad 
989*3bfaa971Sbrad 	if (dunit != CONTROL_DEV &&
990*3bfaa971Sbrad 	    ((flags & FREAD) && (flags & FWRITE))) {
991*3bfaa971Sbrad 		DPRINTF(("umcpmio_dev_open: Not CONTROL device and trying to do READ and WRITE\n"));
992*3bfaa971Sbrad 		return EINVAL;
993*3bfaa971Sbrad 	}
994*3bfaa971Sbrad 
995*3bfaa971Sbrad 	/* Ya, this unrolling will also have to be changed if the MCP-2210 is
996*3bfaa971Sbrad 	 * supported.  There are currently only 4 pins, so don't worry too much
997*3bfaa971Sbrad 	 * about it.  The MCP-2210 has RAM, so there would be a fifth for it.
998*3bfaa971Sbrad 	 */
999*3bfaa971Sbrad 
1000*3bfaa971Sbrad 	mutex_enter(&sc->sc_action_mutex);
1001*3bfaa971Sbrad 	if (dunit != CONTROL_DEV) {
1002*3bfaa971Sbrad 		switch (dunit) {
1003*3bfaa971Sbrad 		case GP1_DEV:
1004*3bfaa971Sbrad 			pin = 1;
1005*3bfaa971Sbrad 			break;
1006*3bfaa971Sbrad 		case GP2_DEV:
1007*3bfaa971Sbrad 			pin = 2;
1008*3bfaa971Sbrad 			break;
1009*3bfaa971Sbrad 		case GP3_DEV:
1010*3bfaa971Sbrad 			pin = 3;
1011*3bfaa971Sbrad 			break;
1012*3bfaa971Sbrad 		default:
1013*3bfaa971Sbrad 			error = EINVAL;
1014*3bfaa971Sbrad 			break;
1015*3bfaa971Sbrad 		}
1016*3bfaa971Sbrad 		if (! error) {
1017*3bfaa971Sbrad 			/* XXX - we can probably do better here...  it doesn't
1018*3bfaa971Sbrad 			 * remember what the pin was set to and probably should.
1019*3bfaa971Sbrad 			 */
1020*3bfaa971Sbrad 			   if (flags & FREAD) {
1021*3bfaa971Sbrad 				error = umcpmio_gpio_pin_ctlctl(sc, pin, GPIO_PIN_ALT0, false);
1022*3bfaa971Sbrad 			} else {
1023*3bfaa971Sbrad 				if (pin == 1) {
1024*3bfaa971Sbrad 					error = EINVAL;
1025*3bfaa971Sbrad 				} else {
1026*3bfaa971Sbrad 					error = umcpmio_gpio_pin_ctlctl(sc, pin, GPIO_PIN_ALT1, false);
1027*3bfaa971Sbrad 				}
1028*3bfaa971Sbrad 			}
1029*3bfaa971Sbrad 		}
1030*3bfaa971Sbrad 	}
1031*3bfaa971Sbrad 	if (! error)
1032*3bfaa971Sbrad 		sc->sc_dev_open[dunit] = true;
1033*3bfaa971Sbrad 	mutex_exit(&sc->sc_action_mutex);
1034*3bfaa971Sbrad 
1035*3bfaa971Sbrad 	DPRINTF(("umcpmio_dev_open: Opened dunit=%d,pin=%d,error=%d\n",dunit,pin,error));
1036*3bfaa971Sbrad 
1037*3bfaa971Sbrad 	return error;
1038*3bfaa971Sbrad }
1039*3bfaa971Sbrad 
1040*3bfaa971Sbrad /* Read an ADC value */
1041*3bfaa971Sbrad 
1042*3bfaa971Sbrad static int
1043*3bfaa971Sbrad umcpmio_dev_read(dev_t dev, struct uio *uio, int flags)
1044*3bfaa971Sbrad {
1045*3bfaa971Sbrad 	struct umcpmio_softc *sc;
1046*3bfaa971Sbrad 	struct mcp2221_status_res status_res;
1047*3bfaa971Sbrad 	int dunit;
1048*3bfaa971Sbrad 	int error = 0;
1049*3bfaa971Sbrad 	uint8_t adc_lsb;
1050*3bfaa971Sbrad 	uint8_t adc_msb;
1051*3bfaa971Sbrad 	uint16_t buf;
1052*3bfaa971Sbrad 
1053*3bfaa971Sbrad 	if ((sc = device_lookup_private(&umcpmio_cd, UMCPMIO_DEV_UNIT(minor(dev)))) == NULL)
1054*3bfaa971Sbrad 		return ENXIO;
1055*3bfaa971Sbrad 
1056*3bfaa971Sbrad 	dunit = UMCPMIO_DEV_WHAT(minor(dev));
1057*3bfaa971Sbrad 
1058*3bfaa971Sbrad 	if (dunit != CONTROL_DEV) {
1059*3bfaa971Sbrad 		while (uio->uio_resid &&
1060*3bfaa971Sbrad 		    !sc->sc_dying) {
1061*3bfaa971Sbrad 			error = umcpmio_get_status(sc, &status_res, true);
1062*3bfaa971Sbrad 			if (! error) {
1063*3bfaa971Sbrad 				switch (dunit) {
1064*3bfaa971Sbrad 				case GP1_DEV:
1065*3bfaa971Sbrad 					adc_lsb = status_res.adc_channel0_lsb;
1066*3bfaa971Sbrad 					adc_msb = status_res.adc_channel0_msb;
1067*3bfaa971Sbrad 					break;
1068*3bfaa971Sbrad 				case GP2_DEV:
1069*3bfaa971Sbrad 					adc_lsb = status_res.adc_channel1_lsb;
1070*3bfaa971Sbrad 					adc_msb = status_res.adc_channel1_msb;
1071*3bfaa971Sbrad 					break;
1072*3bfaa971Sbrad 				case GP3_DEV:
1073*3bfaa971Sbrad 					adc_lsb = status_res.adc_channel2_lsb;
1074*3bfaa971Sbrad 					adc_msb = status_res.adc_channel2_msb;
1075*3bfaa971Sbrad 					break;
1076*3bfaa971Sbrad 				default:
1077*3bfaa971Sbrad 					error = EINVAL;
1078*3bfaa971Sbrad 					break;
1079*3bfaa971Sbrad 				}
1080*3bfaa971Sbrad 
1081*3bfaa971Sbrad 				if (! error) {
1082*3bfaa971Sbrad 					if (sc->sc_dying)
1083*3bfaa971Sbrad 						break;
1084*3bfaa971Sbrad 
1085*3bfaa971Sbrad 					buf = adc_msb << 8;
1086*3bfaa971Sbrad 					buf |= adc_lsb;
1087*3bfaa971Sbrad 					error = uiomove(&buf, 2, uio);
1088*3bfaa971Sbrad 				}
1089*3bfaa971Sbrad 			}
1090*3bfaa971Sbrad 		}
1091*3bfaa971Sbrad 	} else {
1092*3bfaa971Sbrad 		error = EINVAL;
1093*3bfaa971Sbrad 	}
1094*3bfaa971Sbrad 
1095*3bfaa971Sbrad 
1096*3bfaa971Sbrad 	return error;
1097*3bfaa971Sbrad }
1098*3bfaa971Sbrad 
1099*3bfaa971Sbrad /* Write to the DAC */
1100*3bfaa971Sbrad 
1101*3bfaa971Sbrad static int
1102*3bfaa971Sbrad umcpmio_dev_write(dev_t dev, struct uio *uio, int flags)
1103*3bfaa971Sbrad {
1104*3bfaa971Sbrad 	struct umcpmio_softc *sc;
1105*3bfaa971Sbrad 	int dunit;
1106*3bfaa971Sbrad 	int error = 0;
1107*3bfaa971Sbrad 
1108*3bfaa971Sbrad 	if ((sc = device_lookup_private(&umcpmio_cd, UMCPMIO_DEV_UNIT(minor(dev)))) == NULL)
1109*3bfaa971Sbrad 		return ENXIO;
1110*3bfaa971Sbrad 
1111*3bfaa971Sbrad 	dunit = UMCPMIO_DEV_WHAT(minor(dev));
1112*3bfaa971Sbrad 
1113*3bfaa971Sbrad 	if (dunit != CONTROL_DEV) {
1114*3bfaa971Sbrad 		while (uio->uio_resid &&
1115*3bfaa971Sbrad 		    !sc->sc_dying) {
1116*3bfaa971Sbrad 			uint8_t buf;
1117*3bfaa971Sbrad 
1118*3bfaa971Sbrad 			if ((error = uiomove(&buf, 1, uio)) != 0)
1119*3bfaa971Sbrad 				break;
1120*3bfaa971Sbrad 
1121*3bfaa971Sbrad 			if (sc->sc_dying)
1122*3bfaa971Sbrad 				break;
1123*3bfaa971Sbrad 
1124*3bfaa971Sbrad 			error = umcpmio_set_dac_value_one(sc, buf, true);
1125*3bfaa971Sbrad 			if (error)
1126*3bfaa971Sbrad 				break;
1127*3bfaa971Sbrad 		}
1128*3bfaa971Sbrad 	} else {
1129*3bfaa971Sbrad 		error = EINVAL;
1130*3bfaa971Sbrad 	}
1131*3bfaa971Sbrad 
1132*3bfaa971Sbrad 	return error;
1133*3bfaa971Sbrad }
1134*3bfaa971Sbrad 
1135*3bfaa971Sbrad /* Close everything up */
1136*3bfaa971Sbrad 
1137*3bfaa971Sbrad static int
1138*3bfaa971Sbrad umcpmio_dev_close(dev_t dev, int flags, int fmt, struct lwp *l)
1139*3bfaa971Sbrad {
1140*3bfaa971Sbrad 	struct umcpmio_softc *sc;
1141*3bfaa971Sbrad 	int dunit;
1142*3bfaa971Sbrad 	int pin;
1143*3bfaa971Sbrad 	int error = 0;
1144*3bfaa971Sbrad 
1145*3bfaa971Sbrad 	sc = device_lookup_private(&umcpmio_cd, UMCPMIO_DEV_UNIT(minor(dev)));
1146*3bfaa971Sbrad 	if (sc->sc_dying)
1147*3bfaa971Sbrad 		return EIO;
1148*3bfaa971Sbrad 
1149*3bfaa971Sbrad 	dunit = UMCPMIO_DEV_WHAT(minor(dev));
1150*3bfaa971Sbrad 
1151*3bfaa971Sbrad 	mutex_enter(&sc->sc_action_mutex);
1152*3bfaa971Sbrad 	if (dunit != CONTROL_DEV) {
1153*3bfaa971Sbrad 		switch (dunit) {
1154*3bfaa971Sbrad 		case GP1_DEV:
1155*3bfaa971Sbrad 			pin = 1;
1156*3bfaa971Sbrad 			break;
1157*3bfaa971Sbrad 		case GP2_DEV:
1158*3bfaa971Sbrad 			pin = 2;
1159*3bfaa971Sbrad 			break;
1160*3bfaa971Sbrad 		case GP3_DEV:
1161*3bfaa971Sbrad 			pin = 3;
1162*3bfaa971Sbrad 			break;
1163*3bfaa971Sbrad 		default:
1164*3bfaa971Sbrad 			error = EINVAL;
1165*3bfaa971Sbrad 			break;
1166*3bfaa971Sbrad 		}
1167*3bfaa971Sbrad 		if (! error) {
1168*3bfaa971Sbrad 			/* XXX - Ya, this really could be done better.  Probably should
1169*3bfaa971Sbrad 			 * read the sram config and maybe the gpio config and save out
1170*3bfaa971Sbrad 			 * what the pin was set to.
1171*3bfaa971Sbrad 			*/
1172*3bfaa971Sbrad 
1173*3bfaa971Sbrad 			error = umcpmio_gpio_pin_ctlctl(sc, pin, GPIO_PIN_INPUT, false);
1174*3bfaa971Sbrad 		}
1175*3bfaa971Sbrad 	}
1176*3bfaa971Sbrad 	sc->sc_dev_open[dunit] = false;
1177*3bfaa971Sbrad 	mutex_exit(&sc->sc_action_mutex);
1178*3bfaa971Sbrad 
1179*3bfaa971Sbrad 	return error;
1180*3bfaa971Sbrad }
1181*3bfaa971Sbrad 
1182*3bfaa971Sbrad static int
1183*3bfaa971Sbrad umcpmio_dev_ioctl(dev_t dev, u_long cmd, void *data, int flag, struct lwp *l)
1184*3bfaa971Sbrad {
1185*3bfaa971Sbrad 	struct umcpmio_softc *sc;
1186*3bfaa971Sbrad 	struct mcp2221_status_res get_status_res;
1187*3bfaa971Sbrad 	struct mcp2221_get_sram_res get_sram_res;
1188*3bfaa971Sbrad 	struct mcp2221_get_gpio_cfg_res get_gpio_cfg_res;
1189*3bfaa971Sbrad 	struct mcp2221_get_flash_res get_flash_res;
1190*3bfaa971Sbrad 	struct mcp2221_status_res *ioctl_get_status;
1191*3bfaa971Sbrad 	struct mcp2221_get_sram_res *ioctl_get_sram;
1192*3bfaa971Sbrad 	struct mcp2221_get_gpio_cfg_res *ioctl_get_gpio_cfg;
1193*3bfaa971Sbrad 	struct umcpmio_ioctl_get_flash *ioctl_get_flash;
1194*3bfaa971Sbrad 	struct umcpmio_ioctl_put_flash *ioctl_put_flash;
1195*3bfaa971Sbrad 	struct mcp2221_put_flash_req put_flash_req;
1196*3bfaa971Sbrad 	struct mcp2221_put_flash_res put_flash_res;
1197*3bfaa971Sbrad 	int dunit;
1198*3bfaa971Sbrad 	int error = 0;
1199*3bfaa971Sbrad 
1200*3bfaa971Sbrad 	sc = device_lookup_private(&umcpmio_cd, UMCPMIO_DEV_UNIT(minor(dev)));
1201*3bfaa971Sbrad 	if (sc->sc_dying)
1202*3bfaa971Sbrad 		return EIO;
1203*3bfaa971Sbrad 
1204*3bfaa971Sbrad 	dunit = UMCPMIO_DEV_WHAT(minor(dev));
1205*3bfaa971Sbrad 
1206*3bfaa971Sbrad 	if (dunit != CONTROL_DEV) {
1207*3bfaa971Sbrad 		/* It actually is fine to call ioctl with a unsupported cmd,
1208*3bfaa971Sbrad 		 * but be a little noisy if debug is enabled.
1209*3bfaa971Sbrad 		 */
1210*3bfaa971Sbrad 		DPRINTF(("umcpmio_dev_ioctl: dunit is not the CONTROL device: dunit=%d,cmd=%ld\n",dunit,cmd));
1211*3bfaa971Sbrad 		return EINVAL;
1212*3bfaa971Sbrad 	}
1213*3bfaa971Sbrad 
1214*3bfaa971Sbrad 	mutex_enter(&sc->sc_action_mutex);
1215*3bfaa971Sbrad 
1216*3bfaa971Sbrad 	switch (cmd) {
1217*3bfaa971Sbrad 		/* The GET calls use a shadow buffer for each type of call.  That
1218*3bfaa971Sbrad 		 * probably isn't actually needed and the memcpy could be avoided.
1219*3bfaa971Sbrad 		 * but...  it is only ever 64 bytes, so maybe not a big deal.
1220*3bfaa971Sbrad 		 */
1221*3bfaa971Sbrad 	case UMCPMIO_GET_STATUS:
1222*3bfaa971Sbrad 		ioctl_get_status = (struct mcp2221_status_res *)data;
1223*3bfaa971Sbrad 		error = umcpmio_get_status(sc, &get_status_res, false);
1224*3bfaa971Sbrad 		umcpmio_dump_buffer(sc->sc_dumpbuffer, (uint8_t *)&get_status_res, MCP2221_RES_BUFFER_SIZE, "umcpmio_dev_ioctl: UMCPMIO_GET_STATUS: get_status_res");
1225*3bfaa971Sbrad 		DPRINTF(("umcpmio_dev_ioctl: UMCPMIO_GET_STATUS: umcpmio_get_status error=%d\n",error));
1226*3bfaa971Sbrad 		if (! error)
1227*3bfaa971Sbrad 			memcpy(ioctl_get_status, &get_status_res, MCP2221_RES_BUFFER_SIZE);
1228*3bfaa971Sbrad 		break;
1229*3bfaa971Sbrad 
1230*3bfaa971Sbrad 	case UMCPMIO_GET_SRAM:
1231*3bfaa971Sbrad 		ioctl_get_sram = (struct mcp2221_get_sram_res *)data;
1232*3bfaa971Sbrad 		error = umcpmio_get_sram(sc, &get_sram_res, false);
1233*3bfaa971Sbrad 		umcpmio_dump_buffer(sc->sc_dumpbuffer, (uint8_t *)&get_sram_res, MCP2221_RES_BUFFER_SIZE, "umcpmio_dev_ioctl: UMCPMIO_GET_SRAM: get_sram_res");
1234*3bfaa971Sbrad 		DPRINTF(("umcpmio_dev_ioctl: UMCPMIO_GET_SRAM: umcpmio_get_sram error=%d\n",error));
1235*3bfaa971Sbrad 		if (! error)
1236*3bfaa971Sbrad 			memcpy(ioctl_get_sram, &get_sram_res, MCP2221_RES_BUFFER_SIZE);
1237*3bfaa971Sbrad 		break;
1238*3bfaa971Sbrad 
1239*3bfaa971Sbrad 	case UMCPMIO_GET_GP_CFG:
1240*3bfaa971Sbrad 		ioctl_get_gpio_cfg = (struct mcp2221_get_gpio_cfg_res *)data;
1241*3bfaa971Sbrad 		error = umcpmio_get_gpio_cfg(sc, &get_gpio_cfg_res, false);
1242*3bfaa971Sbrad 		umcpmio_dump_buffer(sc->sc_dumpbuffer, (uint8_t *)&get_gpio_cfg_res, MCP2221_RES_BUFFER_SIZE, "umcpmio_dev_ioctl: UMCPMIO_GET_GP_CFG: get_gpio_cfg_res");
1243*3bfaa971Sbrad 		DPRINTF(("umcpmio_dev_ioctl: UMCPMIO_GET_GP_CFG: umcpmio_get_gpio_cfg error=%d\n",error));
1244*3bfaa971Sbrad 		if (! error)
1245*3bfaa971Sbrad 			memcpy(ioctl_get_gpio_cfg, &get_gpio_cfg_res, MCP2221_RES_BUFFER_SIZE);
1246*3bfaa971Sbrad 		break;
1247*3bfaa971Sbrad 
1248*3bfaa971Sbrad 	case UMCPMIO_GET_FLASH:
1249*3bfaa971Sbrad 		ioctl_get_flash  = (struct umcpmio_ioctl_get_flash *)data;
1250*3bfaa971Sbrad 		error = umcpmio_get_flash(sc, ioctl_get_flash->subcode, &get_flash_res, false);
1251*3bfaa971Sbrad 		umcpmio_dump_buffer(sc->sc_dumpbuffer, (uint8_t *)&get_flash_res, MCP2221_RES_BUFFER_SIZE, "umcpmio_dev_ioctl: UMCPMIO_GET_FLASH: get_flash_res");
1252*3bfaa971Sbrad 		DPRINTF(("umcpmio_dev_ioctl: UMCPMIO_GET_FLASH: umcpmio_get_flash subcode=%d,error=%d\n",ioctl_get_flash->subcode,error));
1253*3bfaa971Sbrad 		if (! error)
1254*3bfaa971Sbrad 			memcpy(&ioctl_get_flash->get_flash_res, &get_flash_res, MCP2221_RES_BUFFER_SIZE);
1255*3bfaa971Sbrad 		break;
1256*3bfaa971Sbrad 
1257*3bfaa971Sbrad 	case UMCPMIO_PUT_FLASH:
1258*3bfaa971Sbrad 		/* We only allow the flash parts related to gpio to be changed.
1259*3bfaa971Sbrad 		 * Bounce any attempt to do something else.  Also use a shadow
1260*3bfaa971Sbrad 		 * buffer for the put, so we get to control just literally
1261*3bfaa971Sbrad 		 * everything about the write to flash.
1262*3bfaa971Sbrad 		 */
1263*3bfaa971Sbrad 		ioctl_put_flash  = (struct umcpmio_ioctl_put_flash *)data;
1264*3bfaa971Sbrad 		DPRINTF(("umcpmio_dev_ioctl: UMCPMIO_PUT_FLASH: umcpmio_put_flash subcode=%d\n",ioctl_put_flash->subcode));
1265*3bfaa971Sbrad 		if (ioctl_put_flash->subcode == MCP2221_FLASH_SUBCODE_GP) {
1266*3bfaa971Sbrad 			memset(&put_flash_req, 0, MCP2221_REQ_BUFFER_SIZE);
1267*3bfaa971Sbrad 			put_flash_req.subcode = ioctl_put_flash->subcode;
1268*3bfaa971Sbrad 			put_flash_req.u.gp.gp0_settings = ioctl_put_flash->put_flash_req.u.gp.gp0_settings;
1269*3bfaa971Sbrad 			put_flash_req.u.gp.gp1_settings = ioctl_put_flash->put_flash_req.u.gp.gp1_settings;
1270*3bfaa971Sbrad 			put_flash_req.u.gp.gp2_settings = ioctl_put_flash->put_flash_req.u.gp.gp2_settings;
1271*3bfaa971Sbrad 			put_flash_req.u.gp.gp3_settings = ioctl_put_flash->put_flash_req.u.gp.gp3_settings;
1272*3bfaa971Sbrad 			umcpmio_dump_buffer(sc->sc_dumpbuffer, (uint8_t *)&ioctl_put_flash->put_flash_req, MCP2221_REQ_BUFFER_SIZE, "umcpmio_dev_ioctl: UMCPMIO_PUT_FLASH: ioctl put_flash_req");
1273*3bfaa971Sbrad 			umcpmio_dump_buffer(sc->sc_dumpbuffer, (uint8_t *)&put_flash_req, MCP2221_REQ_BUFFER_SIZE, "umcpmio_dev_ioctl: UMCPMIO_PUT_FLASH: put_flash_req");
1274*3bfaa971Sbrad 			memset(&put_flash_res, 0, MCP2221_RES_BUFFER_SIZE);
1275*3bfaa971Sbrad 			error = umcpmio_put_flash(sc, &put_flash_req, &put_flash_res, false);
1276*3bfaa971Sbrad 			umcpmio_dump_buffer(sc->sc_dumpbuffer, (uint8_t *)&put_flash_res, MCP2221_RES_BUFFER_SIZE, "umcpmio_dev_ioctl: UMCPMIO_PUT_FLASH: put_flash_res");
1277*3bfaa971Sbrad 			memcpy(&ioctl_put_flash->put_flash_res, &put_flash_res, MCP2221_RES_BUFFER_SIZE);
1278*3bfaa971Sbrad 		} else {
1279*3bfaa971Sbrad 			error = EINVAL;
1280*3bfaa971Sbrad 		}
1281*3bfaa971Sbrad 		break;
1282*3bfaa971Sbrad 	default:
1283*3bfaa971Sbrad 		error = EINVAL;
1284*3bfaa971Sbrad 	}
1285*3bfaa971Sbrad 
1286*3bfaa971Sbrad 	mutex_exit(&sc->sc_action_mutex);
1287*3bfaa971Sbrad 
1288*3bfaa971Sbrad 	return error;
1289*3bfaa971Sbrad }
1290*3bfaa971Sbrad 
1291*3bfaa971Sbrad /* This is for sysctl variables that don't actually change the chip.  */
1292*3bfaa971Sbrad 
1293*3bfaa971Sbrad int
1294*3bfaa971Sbrad umcpmio_verify_sysctl(SYSCTLFN_ARGS)
1295*3bfaa971Sbrad {
1296*3bfaa971Sbrad 	int error, t;
1297*3bfaa971Sbrad 	struct sysctlnode node;
1298*3bfaa971Sbrad 
1299*3bfaa971Sbrad 	node = *rnode;
1300*3bfaa971Sbrad 	t = *(int *)rnode->sysctl_data;
1301*3bfaa971Sbrad 	node.sysctl_data = &t;
1302*3bfaa971Sbrad 	error = sysctl_lookup(SYSCTLFN_CALL(&node));
1303*3bfaa971Sbrad 	if (error || newp == NULL)
1304*3bfaa971Sbrad 		return error;
1305*3bfaa971Sbrad 
1306*3bfaa971Sbrad 	if (t < 0)
1307*3bfaa971Sbrad 		return EINVAL;
1308*3bfaa971Sbrad 
1309*3bfaa971Sbrad 	*(int *)rnode->sysctl_data = t;
1310*3bfaa971Sbrad 
1311*3bfaa971Sbrad 	return 0;
1312*3bfaa971Sbrad }
1313*3bfaa971Sbrad 
1314*3bfaa971Sbrad /* sysctl validation for stuff that interacts with the chip needs to happen in a
1315*3bfaa971Sbrad  * transaction.  The read of the current state and the update to new state can't
1316*3bfaa971Sbrad  * allow for someone to sneak in between the two.
1317*3bfaa971Sbrad  *
1318*3bfaa971Sbrad  * We use text for the values of a lot of these variables so you don't need the
1319*3bfaa971Sbrad  * datasheet in front of you.  You get to do that with umcpmioctl(8).
1320*3bfaa971Sbrad  */
1321*3bfaa971Sbrad 
1322*3bfaa971Sbrad static struct umcpmio_sysctl_name umcpmio_vref_names[] = {
1323*3bfaa971Sbrad 	{
1324*3bfaa971Sbrad 		.text = "4.096V",
1325*3bfaa971Sbrad 	},
1326*3bfaa971Sbrad 	{
1327*3bfaa971Sbrad 		.text = "2.048V",
1328*3bfaa971Sbrad 	},
1329*3bfaa971Sbrad 	{
1330*3bfaa971Sbrad 		.text = "1.024V",
1331*3bfaa971Sbrad 	},
1332*3bfaa971Sbrad 	{
1333*3bfaa971Sbrad 		.text = "OFF",
1334*3bfaa971Sbrad 	},
1335*3bfaa971Sbrad 	{
1336*3bfaa971Sbrad 		.text = "VDD",
1337*3bfaa971Sbrad 	}
1338*3bfaa971Sbrad };
1339*3bfaa971Sbrad 
1340*3bfaa971Sbrad int
1341*3bfaa971Sbrad umcpmio_verify_dac_sysctl(SYSCTLFN_ARGS)
1342*3bfaa971Sbrad {
1343*3bfaa971Sbrad 	char buf[UMCPMIO_VREF_NAME];
1344*3bfaa971Sbrad 	char cbuf[UMCPMIO_VREF_NAME];
1345*3bfaa971Sbrad 	struct umcpmio_softc *sc;
1346*3bfaa971Sbrad 	struct sysctlnode node;
1347*3bfaa971Sbrad 	int error = 0;
1348*3bfaa971Sbrad 	int vrm;
1349*3bfaa971Sbrad 	size_t i;
1350*3bfaa971Sbrad 	struct mcp2221_get_sram_res sram_res;
1351*3bfaa971Sbrad 
1352*3bfaa971Sbrad 	node = *rnode;
1353*3bfaa971Sbrad 	sc = node.sysctl_data;
1354*3bfaa971Sbrad 
1355*3bfaa971Sbrad 	mutex_enter(&sc->sc_action_mutex);
1356*3bfaa971Sbrad 
1357*3bfaa971Sbrad 	error = umcpmio_get_sram(sc, &sram_res, false);
1358*3bfaa971Sbrad 	if (error)
1359*3bfaa971Sbrad 		goto out;
1360*3bfaa971Sbrad 
1361*3bfaa971Sbrad 	umcpmio_dump_buffer(sc->sc_dumpbuffer, (uint8_t *)&sram_res, MCP2221_RES_BUFFER_SIZE, "umcpmio_verify_dac_sysctl SRAM res buffer");
1362*3bfaa971Sbrad 
1363*3bfaa971Sbrad 	if (sram_res.dac_reference_voltage & MCP2221_SRAM_DAC_IS_VRM) {
1364*3bfaa971Sbrad 		vrm = sram_res.dac_reference_voltage & MCP2221_SRAM_DAC_VRM_MASK;
1365*3bfaa971Sbrad 		switch (vrm) {
1366*3bfaa971Sbrad 		case MCP2221_SRAM_DAC_VRM_4096V:
1367*3bfaa971Sbrad 			strncpy(buf, "4.096V", UMCPMIO_VREF_NAME);
1368*3bfaa971Sbrad 			break;
1369*3bfaa971Sbrad 		case MCP2221_SRAM_DAC_VRM_2048V:
1370*3bfaa971Sbrad 			strncpy(buf, "2.048V", UMCPMIO_VREF_NAME);
1371*3bfaa971Sbrad 			break;
1372*3bfaa971Sbrad 		case MCP2221_SRAM_DAC_VRM_1024V:
1373*3bfaa971Sbrad 			strncpy(buf, "1.024V", UMCPMIO_VREF_NAME);
1374*3bfaa971Sbrad 			break;
1375*3bfaa971Sbrad 		case MCP2221_SRAM_DAC_VRM_OFF:
1376*3bfaa971Sbrad 		default:
1377*3bfaa971Sbrad 			strncpy(buf, "OFF", UMCPMIO_VREF_NAME);
1378*3bfaa971Sbrad 			break;
1379*3bfaa971Sbrad 		}
1380*3bfaa971Sbrad 	} else {
1381*3bfaa971Sbrad 		strncpy(buf, "VDD", UMCPMIO_VREF_NAME);
1382*3bfaa971Sbrad 	}
1383*3bfaa971Sbrad 	strncpy(cbuf, buf, UMCPMIO_VREF_NAME);
1384*3bfaa971Sbrad 	node.sysctl_data = buf;
1385*3bfaa971Sbrad 	error = sysctl_lookup(SYSCTLFN_CALL(&node));
1386*3bfaa971Sbrad 	if (error || newp == NULL)
1387*3bfaa971Sbrad 		goto out;
1388*3bfaa971Sbrad 
1389*3bfaa971Sbrad 	for (i = 0; i < __arraycount(umcpmio_vref_names); i++) {
1390*3bfaa971Sbrad 		if (strncmp(node.sysctl_data, umcpmio_vref_names[i].text,
1391*3bfaa971Sbrad 		    UMCPMIO_VREF_NAME) == 0) {
1392*3bfaa971Sbrad 			break;
1393*3bfaa971Sbrad 		}
1394*3bfaa971Sbrad 	}
1395*3bfaa971Sbrad 
1396*3bfaa971Sbrad 	if (i == __arraycount(umcpmio_vref_names))
1397*3bfaa971Sbrad 		error = EINVAL;
1398*3bfaa971Sbrad 
1399*3bfaa971Sbrad 	if (! error) {
1400*3bfaa971Sbrad 		if (strncmp(cbuf, buf, UMCPMIO_VREF_NAME) != 0) {
1401*3bfaa971Sbrad 			DPRINTF(("umcpmio_verify_dac_sysctl: setting DAC vref: %s\n",buf));
1402*3bfaa971Sbrad 			error = umcpmio_set_dac_vref_one(sc, buf, false);
1403*3bfaa971Sbrad 		}
1404*3bfaa971Sbrad 	}
1405*3bfaa971Sbrad 
1406*3bfaa971Sbrad  out:
1407*3bfaa971Sbrad 	mutex_exit(&sc->sc_action_mutex);
1408*3bfaa971Sbrad 	return error;
1409*3bfaa971Sbrad }
1410*3bfaa971Sbrad 
1411*3bfaa971Sbrad int
1412*3bfaa971Sbrad umcpmio_verify_adc_sysctl(SYSCTLFN_ARGS)
1413*3bfaa971Sbrad {
1414*3bfaa971Sbrad 	char buf[UMCPMIO_VREF_NAME];
1415*3bfaa971Sbrad 	char cbuf[UMCPMIO_VREF_NAME];
1416*3bfaa971Sbrad 	struct umcpmio_softc *sc;
1417*3bfaa971Sbrad 	struct sysctlnode node;
1418*3bfaa971Sbrad 	int error = 0;
1419*3bfaa971Sbrad 	int vrm;
1420*3bfaa971Sbrad 	size_t i;
1421*3bfaa971Sbrad 	struct mcp2221_get_sram_res sram_res;
1422*3bfaa971Sbrad 
1423*3bfaa971Sbrad 	node = *rnode;
1424*3bfaa971Sbrad 	sc = node.sysctl_data;
1425*3bfaa971Sbrad 
1426*3bfaa971Sbrad 	mutex_enter(&sc->sc_action_mutex);
1427*3bfaa971Sbrad 
1428*3bfaa971Sbrad 	error = umcpmio_get_sram(sc, &sram_res, false);
1429*3bfaa971Sbrad 	if (error)
1430*3bfaa971Sbrad 		goto out;
1431*3bfaa971Sbrad 
1432*3bfaa971Sbrad 	if (sram_res.irq_adc_reference_voltage & MCP2221_SRAM_ADC_IS_VRM) {
1433*3bfaa971Sbrad 		vrm = sram_res.irq_adc_reference_voltage & MCP2221_SRAM_ADC_VRM_MASK;
1434*3bfaa971Sbrad 		switch (vrm) {
1435*3bfaa971Sbrad 		case MCP2221_SRAM_ADC_VRM_4096V:
1436*3bfaa971Sbrad 			strncpy(buf, "4.096V", UMCPMIO_VREF_NAME);
1437*3bfaa971Sbrad 			break;
1438*3bfaa971Sbrad 		case MCP2221_SRAM_ADC_VRM_2048V:
1439*3bfaa971Sbrad 			strncpy(buf, "2.048V", UMCPMIO_VREF_NAME);
1440*3bfaa971Sbrad 			break;
1441*3bfaa971Sbrad 		case MCP2221_SRAM_ADC_VRM_1024V:
1442*3bfaa971Sbrad 			strncpy(buf, "1.024V", UMCPMIO_VREF_NAME);
1443*3bfaa971Sbrad 			break;
1444*3bfaa971Sbrad 		case MCP2221_SRAM_ADC_VRM_OFF:
1445*3bfaa971Sbrad 		default:
1446*3bfaa971Sbrad 			strncpy(buf, "OFF", UMCPMIO_VREF_NAME);
1447*3bfaa971Sbrad 			break;
1448*3bfaa971Sbrad 		}
1449*3bfaa971Sbrad 	} else {
1450*3bfaa971Sbrad 		strncpy(buf, "VDD", UMCPMIO_VREF_NAME);
1451*3bfaa971Sbrad 	}
1452*3bfaa971Sbrad 	strncpy(cbuf, buf, UMCPMIO_VREF_NAME);
1453*3bfaa971Sbrad 	node.sysctl_data = buf;
1454*3bfaa971Sbrad 	error = sysctl_lookup(SYSCTLFN_CALL(&node));
1455*3bfaa971Sbrad 	if (error || newp == NULL)
1456*3bfaa971Sbrad 		goto out;
1457*3bfaa971Sbrad 
1458*3bfaa971Sbrad 	for (i = 0; i < __arraycount(umcpmio_vref_names); i++) {
1459*3bfaa971Sbrad 		if (strncmp(node.sysctl_data, umcpmio_vref_names[i].text,
1460*3bfaa971Sbrad 		    UMCPMIO_VREF_NAME) == 0) {
1461*3bfaa971Sbrad 			break;
1462*3bfaa971Sbrad 		}
1463*3bfaa971Sbrad 	}
1464*3bfaa971Sbrad 
1465*3bfaa971Sbrad 	if (i == __arraycount(umcpmio_vref_names))
1466*3bfaa971Sbrad 		error = EINVAL;
1467*3bfaa971Sbrad 
1468*3bfaa971Sbrad 	if (! error) {
1469*3bfaa971Sbrad 		if (strncmp(cbuf, buf, UMCPMIO_VREF_NAME) != 0) {
1470*3bfaa971Sbrad 			DPRINTF(("umcpmio_verify_adc_sysctl: setting ADC vref: %s\n",buf));
1471*3bfaa971Sbrad 			error = umcpmio_set_adc_vref_one(sc, buf, false);
1472*3bfaa971Sbrad 		}
1473*3bfaa971Sbrad 	}
1474*3bfaa971Sbrad 
1475*3bfaa971Sbrad  out:
1476*3bfaa971Sbrad 	mutex_exit(&sc->sc_action_mutex);
1477*3bfaa971Sbrad 	return error;
1478*3bfaa971Sbrad }
1479*3bfaa971Sbrad 
1480*3bfaa971Sbrad static struct umcpmio_sysctl_name umcpmio_dc_names[] = {
1481*3bfaa971Sbrad 	{
1482*3bfaa971Sbrad 		.text = "75%",
1483*3bfaa971Sbrad 	},
1484*3bfaa971Sbrad 	{
1485*3bfaa971Sbrad 		.text = "50%",
1486*3bfaa971Sbrad 	},
1487*3bfaa971Sbrad 	{
1488*3bfaa971Sbrad 		.text = "25%",
1489*3bfaa971Sbrad 	},
1490*3bfaa971Sbrad 	{
1491*3bfaa971Sbrad 		.text = "0%",
1492*3bfaa971Sbrad 	}
1493*3bfaa971Sbrad };
1494*3bfaa971Sbrad 
1495*3bfaa971Sbrad static int
1496*3bfaa971Sbrad umcpmio_verify_gpioclock_dc_sysctl(SYSCTLFN_ARGS)
1497*3bfaa971Sbrad {
1498*3bfaa971Sbrad 	char buf[UMCPMIO_VREF_NAME];
1499*3bfaa971Sbrad 	char cbuf[UMCPMIO_VREF_NAME];
1500*3bfaa971Sbrad 	struct umcpmio_softc *sc;
1501*3bfaa971Sbrad 	struct sysctlnode node;
1502*3bfaa971Sbrad 	int error = 0;
1503*3bfaa971Sbrad 	uint8_t duty_cycle;
1504*3bfaa971Sbrad 	size_t i;
1505*3bfaa971Sbrad 	struct mcp2221_get_sram_res sram_res;
1506*3bfaa971Sbrad 
1507*3bfaa971Sbrad 	node = *rnode;
1508*3bfaa971Sbrad 	sc = node.sysctl_data;
1509*3bfaa971Sbrad 
1510*3bfaa971Sbrad 	mutex_enter(&sc->sc_action_mutex);
1511*3bfaa971Sbrad 
1512*3bfaa971Sbrad 	error = umcpmio_get_sram(sc, &sram_res, false);
1513*3bfaa971Sbrad 	if (error)
1514*3bfaa971Sbrad 		goto out;
1515*3bfaa971Sbrad 
1516*3bfaa971Sbrad 	duty_cycle = sram_res.clock_divider & MCP2221_SRAM_GPIO_CLOCK_DC_MASK;
1517*3bfaa971Sbrad 	DPRINTF(("umcpmio_verify_gpioclock_dc_sysctl: current duty cycle: %02x\n",duty_cycle));
1518*3bfaa971Sbrad 	switch (duty_cycle) {
1519*3bfaa971Sbrad 	case MCP2221_SRAM_GPIO_CLOCK_DC_75:
1520*3bfaa971Sbrad 		strncpy(buf, "75%", UMCPMIO_DC_NAME);
1521*3bfaa971Sbrad 		break;
1522*3bfaa971Sbrad 	case MCP2221_SRAM_GPIO_CLOCK_DC_50:
1523*3bfaa971Sbrad 		strncpy(buf, "50%", UMCPMIO_DC_NAME);
1524*3bfaa971Sbrad 		break;
1525*3bfaa971Sbrad 	case MCP2221_SRAM_GPIO_CLOCK_DC_25:
1526*3bfaa971Sbrad 		strncpy(buf, "25%", UMCPMIO_DC_NAME);
1527*3bfaa971Sbrad 		break;
1528*3bfaa971Sbrad 	case MCP2221_SRAM_GPIO_CLOCK_DC_0:
1529*3bfaa971Sbrad 	default:
1530*3bfaa971Sbrad 		strncpy(buf, "0%", UMCPMIO_DC_NAME);
1531*3bfaa971Sbrad 		break;
1532*3bfaa971Sbrad 	}
1533*3bfaa971Sbrad 	strncpy(cbuf, buf, UMCPMIO_VREF_NAME);
1534*3bfaa971Sbrad 	node.sysctl_data = buf;
1535*3bfaa971Sbrad 	error = sysctl_lookup(SYSCTLFN_CALL(&node));
1536*3bfaa971Sbrad 	if (error || newp == NULL)
1537*3bfaa971Sbrad 		goto out;
1538*3bfaa971Sbrad 
1539*3bfaa971Sbrad 	for (i = 0; i < __arraycount(umcpmio_dc_names); i++) {
1540*3bfaa971Sbrad 		if (strncmp(node.sysctl_data, umcpmio_dc_names[i].text,
1541*3bfaa971Sbrad 		    UMCPMIO_VREF_NAME) == 0) {
1542*3bfaa971Sbrad 			break;
1543*3bfaa971Sbrad 		}
1544*3bfaa971Sbrad 	}
1545*3bfaa971Sbrad 
1546*3bfaa971Sbrad 	if (i == __arraycount(umcpmio_dc_names))
1547*3bfaa971Sbrad 		error = EINVAL;
1548*3bfaa971Sbrad 
1549*3bfaa971Sbrad 	if (! error) {
1550*3bfaa971Sbrad 		if (strncmp(cbuf, buf, UMCPMIO_VREF_NAME) != 0) {
1551*3bfaa971Sbrad 			DPRINTF(("umcpmio_verify_gpioclock_dc_sysctl: setting GPIO clock duty cycle: %s\n",buf));
1552*3bfaa971Sbrad 			error = umcpmio_set_gpioclock_dc_one(sc, buf, false);
1553*3bfaa971Sbrad 		}
1554*3bfaa971Sbrad 	}
1555*3bfaa971Sbrad 
1556*3bfaa971Sbrad  out:
1557*3bfaa971Sbrad 	mutex_exit(&sc->sc_action_mutex);
1558*3bfaa971Sbrad 	return error;
1559*3bfaa971Sbrad }
1560*3bfaa971Sbrad 
1561*3bfaa971Sbrad 
1562*3bfaa971Sbrad static struct umcpmio_sysctl_name umcpmio_cd_names[] = {
1563*3bfaa971Sbrad 	{
1564*3bfaa971Sbrad 		.text = "375kHz",
1565*3bfaa971Sbrad 	},
1566*3bfaa971Sbrad 	{
1567*3bfaa971Sbrad 		.text = "750kHz",
1568*3bfaa971Sbrad 	},
1569*3bfaa971Sbrad 	{
1570*3bfaa971Sbrad 		.text = "1.5MHz",
1571*3bfaa971Sbrad 	},
1572*3bfaa971Sbrad 	{
1573*3bfaa971Sbrad 		.text = "3MHz",
1574*3bfaa971Sbrad 	},
1575*3bfaa971Sbrad 	{
1576*3bfaa971Sbrad 		.text = "6MHz",
1577*3bfaa971Sbrad 	},
1578*3bfaa971Sbrad 	{
1579*3bfaa971Sbrad 		.text = "12MHz",
1580*3bfaa971Sbrad 	},
1581*3bfaa971Sbrad 	{
1582*3bfaa971Sbrad 		.text = "24MHz",
1583*3bfaa971Sbrad 	}
1584*3bfaa971Sbrad };
1585*3bfaa971Sbrad 
1586*3bfaa971Sbrad static int
1587*3bfaa971Sbrad umcpmio_verify_gpioclock_cd_sysctl(SYSCTLFN_ARGS)
1588*3bfaa971Sbrad {
1589*3bfaa971Sbrad 	char buf[UMCPMIO_CD_NAME];
1590*3bfaa971Sbrad 	char cbuf[UMCPMIO_CD_NAME];
1591*3bfaa971Sbrad 	struct umcpmio_softc *sc;
1592*3bfaa971Sbrad 	struct sysctlnode node;
1593*3bfaa971Sbrad 	int error = 0;
1594*3bfaa971Sbrad 	uint8_t clock_divider;
1595*3bfaa971Sbrad 	size_t i;
1596*3bfaa971Sbrad 	struct mcp2221_get_sram_res sram_res;
1597*3bfaa971Sbrad 
1598*3bfaa971Sbrad 	node = *rnode;
1599*3bfaa971Sbrad 	sc = node.sysctl_data;
1600*3bfaa971Sbrad 
1601*3bfaa971Sbrad 	mutex_enter(&sc->sc_action_mutex);
1602*3bfaa971Sbrad 
1603*3bfaa971Sbrad 	error = umcpmio_get_sram(sc, &sram_res, false);
1604*3bfaa971Sbrad 	if (error)
1605*3bfaa971Sbrad 		goto out;
1606*3bfaa971Sbrad 
1607*3bfaa971Sbrad 	clock_divider = sram_res.clock_divider & MCP2221_SRAM_GPIO_CLOCK_CD_MASK;
1608*3bfaa971Sbrad 	DPRINTF(("umcpmio_verify_gpioclock_cd_sysctl: current clock divider: %02x\n",clock_divider));
1609*3bfaa971Sbrad 	switch (clock_divider) {
1610*3bfaa971Sbrad 
1611*3bfaa971Sbrad 	case MCP2221_SRAM_GPIO_CLOCK_CD_375KHZ:
1612*3bfaa971Sbrad 		strncpy(buf, "375kHz", UMCPMIO_CD_NAME);
1613*3bfaa971Sbrad 		break;
1614*3bfaa971Sbrad 	case MCP2221_SRAM_GPIO_CLOCK_CD_750KHZ:
1615*3bfaa971Sbrad 		strncpy(buf, "750kHz", UMCPMIO_CD_NAME);
1616*3bfaa971Sbrad 		break;
1617*3bfaa971Sbrad 	case MCP2221_SRAM_GPIO_CLOCK_CD_1P5MHZ:
1618*3bfaa971Sbrad 		strncpy(buf, "1.5MHz", UMCPMIO_CD_NAME);
1619*3bfaa971Sbrad 		break;
1620*3bfaa971Sbrad 	case MCP2221_SRAM_GPIO_CLOCK_CD_3MHZ:
1621*3bfaa971Sbrad 		strncpy(buf, "3MHz", UMCPMIO_CD_NAME);
1622*3bfaa971Sbrad 		break;
1623*3bfaa971Sbrad 	case MCP2221_SRAM_GPIO_CLOCK_CD_6MHZ:
1624*3bfaa971Sbrad 		strncpy(buf, "6MHz", UMCPMIO_CD_NAME);
1625*3bfaa971Sbrad 		break;
1626*3bfaa971Sbrad 	case MCP2221_SRAM_GPIO_CLOCK_CD_12MHZ:
1627*3bfaa971Sbrad 		strncpy(buf, "12MHz", UMCPMIO_CD_NAME);
1628*3bfaa971Sbrad 		break;
1629*3bfaa971Sbrad 	case MCP2221_SRAM_GPIO_CLOCK_CD_24MHZ:
1630*3bfaa971Sbrad 		strncpy(buf, "24MHz", UMCPMIO_CD_NAME);
1631*3bfaa971Sbrad 		break;
1632*3bfaa971Sbrad 	default:
1633*3bfaa971Sbrad 		strncpy(buf, "12MHz", UMCPMIO_CD_NAME);
1634*3bfaa971Sbrad 		break;
1635*3bfaa971Sbrad 	}
1636*3bfaa971Sbrad 	strncpy(cbuf, buf, UMCPMIO_CD_NAME);
1637*3bfaa971Sbrad 	node.sysctl_data = buf;
1638*3bfaa971Sbrad 	error = sysctl_lookup(SYSCTLFN_CALL(&node));
1639*3bfaa971Sbrad 	if (error || newp == NULL)
1640*3bfaa971Sbrad 		goto out;
1641*3bfaa971Sbrad 
1642*3bfaa971Sbrad 	for (i = 0; i < __arraycount(umcpmio_cd_names); i++) {
1643*3bfaa971Sbrad 		if (strncmp(node.sysctl_data, umcpmio_cd_names[i].text,
1644*3bfaa971Sbrad 		    UMCPMIO_CD_NAME) == 0) {
1645*3bfaa971Sbrad 			break;
1646*3bfaa971Sbrad 		}
1647*3bfaa971Sbrad 	}
1648*3bfaa971Sbrad 
1649*3bfaa971Sbrad 	if (i == __arraycount(umcpmio_cd_names))
1650*3bfaa971Sbrad 		error = EINVAL;
1651*3bfaa971Sbrad 
1652*3bfaa971Sbrad 	if (! error) {
1653*3bfaa971Sbrad 		if (strncmp(cbuf, buf, UMCPMIO_CD_NAME) != 0) {
1654*3bfaa971Sbrad 			DPRINTF(("umcpmio_verify_gpioclock_cd_sysctl: setting GPIO clock clock divider: %s\n",buf));
1655*3bfaa971Sbrad 			error = umcpmio_set_gpioclock_cd_one(sc, buf, false);
1656*3bfaa971Sbrad 		}
1657*3bfaa971Sbrad 	}
1658*3bfaa971Sbrad 
1659*3bfaa971Sbrad  out:
1660*3bfaa971Sbrad 	mutex_exit(&sc->sc_action_mutex);
1661*3bfaa971Sbrad 	return error;
1662*3bfaa971Sbrad }
1663*3bfaa971Sbrad 
1664*3bfaa971Sbrad static int
1665*3bfaa971Sbrad umcpmio_sysctl_init(struct umcpmio_softc *sc)
1666*3bfaa971Sbrad {
1667*3bfaa971Sbrad 	int error;
1668*3bfaa971Sbrad 	const struct sysctlnode *cnode;
1669*3bfaa971Sbrad 	int sysctlroot_num, i2c_num, adc_dac_num, adc_num, dac_num, gpio_num;
1670*3bfaa971Sbrad 
1671*3bfaa971Sbrad 	if ((error = sysctl_createv(&sc->sc_umcpmiolog, 0, NULL, &cnode,
1672*3bfaa971Sbrad 	    0, CTLTYPE_NODE, device_xname(sc->sc_dev),
1673*3bfaa971Sbrad 	    SYSCTL_DESCR("mcpmio controls"), NULL, 0, NULL, 0, CTL_HW,
1674*3bfaa971Sbrad 	    CTL_CREATE, CTL_EOL)) != 0)
1675*3bfaa971Sbrad 		return error;
1676*3bfaa971Sbrad 
1677*3bfaa971Sbrad 	sysctlroot_num = cnode->sysctl_num;
1678*3bfaa971Sbrad 
1679*3bfaa971Sbrad #ifdef UMCPMIO_DEBUG
1680*3bfaa971Sbrad 	if ((error = sysctl_createv(&sc->sc_umcpmiolog, 0, NULL, &cnode,
1681*3bfaa971Sbrad 	    CTLFLAG_READWRITE, CTLTYPE_INT, "debug",
1682*3bfaa971Sbrad 	    SYSCTL_DESCR("Debug level"), umcpmio_verify_sysctl, 0,
1683*3bfaa971Sbrad 	    &umcpmiodebug, 0, CTL_HW, sysctlroot_num, CTL_CREATE,
1684*3bfaa971Sbrad 	    CTL_EOL)) != 0)
1685*3bfaa971Sbrad 		return error;
1686*3bfaa971Sbrad 
1687*3bfaa971Sbrad 	if ((error = sysctl_createv(&sc->sc_umcpmiolog, 0, NULL, &cnode,
1688*3bfaa971Sbrad 	    CTLFLAG_READWRITE, CTLTYPE_BOOL, "dump_buffers",
1689*3bfaa971Sbrad 	    SYSCTL_DESCR("Dump buffer when debugging"), NULL, 0, &sc->sc_dumpbuffer,
1690*3bfaa971Sbrad 	    0, CTL_HW, sysctlroot_num, CTL_CREATE, CTL_EOL)) != 0)
1691*3bfaa971Sbrad 		return error;
1692*3bfaa971Sbrad #endif
1693*3bfaa971Sbrad 
1694*3bfaa971Sbrad 	if ((error = sysctl_createv(&sc->sc_umcpmiolog, 0, NULL, &cnode,
1695*3bfaa971Sbrad 	    CTLFLAG_READWRITE, CTLTYPE_INT, "response_wait",
1696*3bfaa971Sbrad 	    SYSCTL_DESCR("How long to wait in ms for a response for a HID report"),
1697*3bfaa971Sbrad 	    umcpmio_verify_sysctl, 0, &sc->sc_cv_wait, 0, CTL_HW,
1698*3bfaa971Sbrad 	    sysctlroot_num, CTL_CREATE, CTL_EOL)) != 0)
1699*3bfaa971Sbrad 		return error;
1700*3bfaa971Sbrad 
1701*3bfaa971Sbrad 	if ((error = sysctl_createv(&sc->sc_umcpmiolog, 0, NULL, &cnode,
1702*3bfaa971Sbrad 	    CTLFLAG_READWRITE, CTLTYPE_INT, "response_errcnt",
1703*3bfaa971Sbrad 	    SYSCTL_DESCR("How many errors to allow on a response"),
1704*3bfaa971Sbrad 	    umcpmio_verify_sysctl, 0, &sc->sc_response_errcnt, 0, CTL_HW,
1705*3bfaa971Sbrad 	    sysctlroot_num, CTL_CREATE, CTL_EOL)) != 0)
1706*3bfaa971Sbrad 		return error;
1707*3bfaa971Sbrad 
1708*3bfaa971Sbrad 	if ((error = sysctl_createv(&sc->sc_umcpmiolog, 0, NULL, &cnode,
1709*3bfaa971Sbrad 	    0, CTLTYPE_NODE, "i2c",
1710*3bfaa971Sbrad 	    SYSCTL_DESCR("I2C controls"), NULL, 0, NULL, 0, CTL_HW,
1711*3bfaa971Sbrad 	    sysctlroot_num, CTL_CREATE, CTL_EOL)) != 0)
1712*3bfaa971Sbrad 		return error;
1713*3bfaa971Sbrad 
1714*3bfaa971Sbrad 	i2c_num = cnode->sysctl_num;
1715*3bfaa971Sbrad 
1716*3bfaa971Sbrad 	if ((error = sysctl_createv(&sc->sc_umcpmiolog, 0, NULL, &cnode,
1717*3bfaa971Sbrad 	    0, CTLTYPE_NODE, "adcdac",
1718*3bfaa971Sbrad 	    SYSCTL_DESCR("ADC and DAC controls"), NULL, 0, NULL, 0, CTL_HW,
1719*3bfaa971Sbrad 	    sysctlroot_num, CTL_CREATE, CTL_EOL)) != 0)
1720*3bfaa971Sbrad 		return error;
1721*3bfaa971Sbrad 
1722*3bfaa971Sbrad 	adc_dac_num = cnode->sysctl_num;
1723*3bfaa971Sbrad 
1724*3bfaa971Sbrad 	if ((error = sysctl_createv(&sc->sc_umcpmiolog, 0, NULL, &cnode,
1725*3bfaa971Sbrad 	    0, CTLTYPE_NODE, "adc",
1726*3bfaa971Sbrad 	    SYSCTL_DESCR("ADC controls"), NULL, 0, NULL, 0, CTL_HW,
1727*3bfaa971Sbrad 	    sysctlroot_num, CTL_CREATE, CTL_EOL)) != 0)
1728*3bfaa971Sbrad 		return error;
1729*3bfaa971Sbrad 
1730*3bfaa971Sbrad 	adc_num = cnode->sysctl_num;
1731*3bfaa971Sbrad 
1732*3bfaa971Sbrad 	if ((error = sysctl_createv(&sc->sc_umcpmiolog, 0, NULL, &cnode,
1733*3bfaa971Sbrad 	    0, CTLTYPE_NODE, "dac",
1734*3bfaa971Sbrad 	    SYSCTL_DESCR("DAC controls"), NULL, 0, NULL, 0, CTL_HW,
1735*3bfaa971Sbrad 	    sysctlroot_num, CTL_CREATE, CTL_EOL)) != 0)
1736*3bfaa971Sbrad 		return error;
1737*3bfaa971Sbrad 
1738*3bfaa971Sbrad 	dac_num = cnode->sysctl_num;
1739*3bfaa971Sbrad 
1740*3bfaa971Sbrad 	if ((error = sysctl_createv(&sc->sc_umcpmiolog, 0, NULL, &cnode,
1741*3bfaa971Sbrad 	    0, CTLTYPE_NODE, "gpio",
1742*3bfaa971Sbrad 	    SYSCTL_DESCR("GPIO controls"), NULL, 0, NULL, 0, CTL_HW,
1743*3bfaa971Sbrad 	    sysctlroot_num, CTL_CREATE, CTL_EOL)) != 0)
1744*3bfaa971Sbrad 		return error;
1745*3bfaa971Sbrad 
1746*3bfaa971Sbrad 	gpio_num = cnode->sysctl_num;
1747*3bfaa971Sbrad 
1748*3bfaa971Sbrad 	/* I2C */
1749*3bfaa971Sbrad 	if ((error = sysctl_createv(&sc->sc_umcpmiolog, 0, NULL, &cnode,
1750*3bfaa971Sbrad 	    CTLFLAG_READWRITE, CTLTYPE_BOOL, "reportreadnostop",
1751*3bfaa971Sbrad 	    SYSCTL_DESCR("Report that a READ without STOP was attempted by a device"),
1752*3bfaa971Sbrad 	    NULL, 0, &sc->sc_reportreadnostop, 0, CTL_HW, sysctlroot_num, i2c_num,
1753*3bfaa971Sbrad 	    CTL_CREATE, CTL_EOL)) != 0)
1754*3bfaa971Sbrad 		return error;
1755*3bfaa971Sbrad 
1756*3bfaa971Sbrad 	if ((error = sysctl_createv(&sc->sc_umcpmiolog, 0, NULL, &cnode,
1757*3bfaa971Sbrad 	    CTLFLAG_READWRITE, CTLTYPE_INT, "busy_delay",
1758*3bfaa971Sbrad 	    SYSCTL_DESCR("How long to wait in ms when the I2C engine is busy"),
1759*3bfaa971Sbrad 	    umcpmio_verify_sysctl, 0, &sc->sc_busy_delay, 0, CTL_HW,
1760*3bfaa971Sbrad 	    sysctlroot_num, i2c_num, CTL_CREATE, CTL_EOL)) != 0)
1761*3bfaa971Sbrad 		return error;
1762*3bfaa971Sbrad 
1763*3bfaa971Sbrad 	if ((error = sysctl_createv(&sc->sc_umcpmiolog, 0, NULL, &cnode,
1764*3bfaa971Sbrad 	    CTLFLAG_READWRITE, CTLTYPE_INT, "retry_busy_read",
1765*3bfaa971Sbrad 	    SYSCTL_DESCR("How many times to retry a busy I2C read"),
1766*3bfaa971Sbrad 	    umcpmio_verify_sysctl, 0, &sc->sc_retry_busy_read, 0, CTL_HW,
1767*3bfaa971Sbrad 	    sysctlroot_num, i2c_num, CTL_CREATE, CTL_EOL)) != 0)
1768*3bfaa971Sbrad 		return error;
1769*3bfaa971Sbrad 
1770*3bfaa971Sbrad 	if ((error = sysctl_createv(&sc->sc_umcpmiolog, 0, NULL, &cnode,
1771*3bfaa971Sbrad 	    CTLFLAG_READWRITE, CTLTYPE_INT, "retry_busy_write",
1772*3bfaa971Sbrad 	    SYSCTL_DESCR("How many times to retry a busy I2C write"),
1773*3bfaa971Sbrad 	    umcpmio_verify_sysctl, 0, &sc->sc_retry_busy_write, 0, CTL_HW,
1774*3bfaa971Sbrad 	    sysctlroot_num, i2c_num, CTL_CREATE, CTL_EOL)) != 0)
1775*3bfaa971Sbrad 		return error;
1776*3bfaa971Sbrad 
1777*3bfaa971Sbrad 	/* GPIO */
1778*3bfaa971Sbrad 	if ((error = sysctl_createv(&sc->sc_umcpmiolog, 0, NULL, &cnode,
1779*3bfaa971Sbrad 	    CTLFLAG_READWRITE, CTLTYPE_INT, "irq_poll",
1780*3bfaa971Sbrad 	    SYSCTL_DESCR("How often to poll for a IRQ change"),
1781*3bfaa971Sbrad 	    umcpmio_verify_sysctl, 0, &sc->sc_irq_poll, 0, CTL_HW,
1782*3bfaa971Sbrad 	    sysctlroot_num, gpio_num, CTL_CREATE, CTL_EOL)) != 0)
1783*3bfaa971Sbrad 		return error;
1784*3bfaa971Sbrad 
1785*3bfaa971Sbrad 	if ((error = sysctl_createv(&sc->sc_umcpmiolog, 0, NULL, &cnode,
1786*3bfaa971Sbrad 	    CTLFLAG_READONLY, CTLTYPE_STRING, "clock_duty_cycles",
1787*3bfaa971Sbrad 	    SYSCTL_DESCR("Valid duty cycles for GPIO clock on GP1 ALT3 duty cycle"),
1788*3bfaa971Sbrad 	    0, 0, __UNCONST(umcpmio_valid_dcs),
1789*3bfaa971Sbrad 	    sizeof(umcpmio_valid_dcs) + 1, CTL_HW, sysctlroot_num, gpio_num, CTL_CREATE, CTL_EOL)) != 0)
1790*3bfaa971Sbrad 		return error;
1791*3bfaa971Sbrad 
1792*3bfaa971Sbrad 	if ((error = sysctl_createv(&sc->sc_umcpmiolog, 0, NULL, &cnode,
1793*3bfaa971Sbrad 	    CTLFLAG_READWRITE, CTLTYPE_STRING, "clock_duty_cycle",
1794*3bfaa971Sbrad 	    SYSCTL_DESCR("GPIO clock on GP1 ALT3 duty cycle"),
1795*3bfaa971Sbrad 	    umcpmio_verify_gpioclock_dc_sysctl, 0, (void *) sc,
1796*3bfaa971Sbrad 	    UMCPMIO_DC_NAME, CTL_HW, sysctlroot_num, gpio_num, CTL_CREATE, CTL_EOL)) != 0)
1797*3bfaa971Sbrad 		return error;
1798*3bfaa971Sbrad 
1799*3bfaa971Sbrad 	if ((error = sysctl_createv(&sc->sc_umcpmiolog, 0, NULL, &cnode,
1800*3bfaa971Sbrad 	    CTLFLAG_READONLY, CTLTYPE_STRING, "clock_dividers",
1801*3bfaa971Sbrad 	    SYSCTL_DESCR("Valid clock dividers for GPIO clock on GP1 with ALT3"),
1802*3bfaa971Sbrad 	    0, 0, __UNCONST(umcpmio_valid_cds),
1803*3bfaa971Sbrad 	    sizeof(umcpmio_valid_cds) + 1, CTL_HW, sysctlroot_num, gpio_num, CTL_CREATE, CTL_EOL)) != 0)
1804*3bfaa971Sbrad 		return error;
1805*3bfaa971Sbrad 
1806*3bfaa971Sbrad 	if ((error = sysctl_createv(&sc->sc_umcpmiolog, 0, NULL, &cnode,
1807*3bfaa971Sbrad 	    CTLFLAG_READWRITE, CTLTYPE_STRING, "clock_divider",
1808*3bfaa971Sbrad 	    SYSCTL_DESCR("GPIO clock on GP1 ALT3 clock divider"),
1809*3bfaa971Sbrad 	    umcpmio_verify_gpioclock_cd_sysctl, 0, (void *) sc,
1810*3bfaa971Sbrad 	    UMCPMIO_CD_NAME, CTL_HW, sysctlroot_num, gpio_num, CTL_CREATE, CTL_EOL)) != 0)
1811*3bfaa971Sbrad 		return error;
1812*3bfaa971Sbrad 
1813*3bfaa971Sbrad 	/* ADC and DAC */
1814*3bfaa971Sbrad 	if ((error = sysctl_createv(&sc->sc_umcpmiolog, 0, NULL, &cnode,
1815*3bfaa971Sbrad 	    CTLFLAG_READONLY, CTLTYPE_STRING, "vrefs",
1816*3bfaa971Sbrad 	    SYSCTL_DESCR("Valid vref values for ADC and DAC"),
1817*3bfaa971Sbrad 	    0, 0, __UNCONST(umcpmio_valid_vrefs),
1818*3bfaa971Sbrad 	    sizeof(umcpmio_valid_vrefs) + 1, CTL_HW, sysctlroot_num, adc_dac_num, CTL_CREATE, CTL_EOL)) != 0)
1819*3bfaa971Sbrad 		return error;
1820*3bfaa971Sbrad 
1821*3bfaa971Sbrad 	/* ADC */
1822*3bfaa971Sbrad 	if ((error = sysctl_createv(&sc->sc_umcpmiolog, 0, NULL, &cnode,
1823*3bfaa971Sbrad 	    CTLFLAG_READWRITE, CTLTYPE_STRING, "vref",
1824*3bfaa971Sbrad 	    SYSCTL_DESCR("ADC voltage reference"),
1825*3bfaa971Sbrad 	    umcpmio_verify_adc_sysctl, 0, (void *) sc,
1826*3bfaa971Sbrad 	    UMCPMIO_VREF_NAME, CTL_HW, sysctlroot_num, adc_num, CTL_CREATE, CTL_EOL)) != 0)
1827*3bfaa971Sbrad 		return error;
1828*3bfaa971Sbrad 
1829*3bfaa971Sbrad 	/* DAC */
1830*3bfaa971Sbrad 	if ((error = sysctl_createv(&sc->sc_umcpmiolog, 0, NULL, &cnode,
1831*3bfaa971Sbrad 	    CTLFLAG_READWRITE, CTLTYPE_STRING, "vref",
1832*3bfaa971Sbrad 	    SYSCTL_DESCR("DAC voltage reference"),
1833*3bfaa971Sbrad 	    umcpmio_verify_dac_sysctl, 0, (void *) sc,
1834*3bfaa971Sbrad 	    UMCPMIO_VREF_NAME, CTL_HW, sysctlroot_num, dac_num, CTL_CREATE, CTL_EOL)) != 0)
1835*3bfaa971Sbrad 		return error;
1836*3bfaa971Sbrad 
1837*3bfaa971Sbrad 	return 0;
1838*3bfaa971Sbrad }
1839*3bfaa971Sbrad 
1840*3bfaa971Sbrad static int
1841*3bfaa971Sbrad umcpmio_match(device_t parent, cfdata_t match, void *aux)
1842*3bfaa971Sbrad {
1843*3bfaa971Sbrad 	struct uhidev_attach_arg *uha = aux;
1844*3bfaa971Sbrad 
1845*3bfaa971Sbrad 	return umcpmio_lookup(uha->uiaa->uiaa_vendor, uha->uiaa->uiaa_product)
1846*3bfaa971Sbrad 	    != NULL ? UMATCH_VENDOR_PRODUCT : UMATCH_NONE;
1847*3bfaa971Sbrad }
1848*3bfaa971Sbrad 
1849*3bfaa971Sbrad 
1850*3bfaa971Sbrad /* This driver could be extended to support the MCP-2210 which is MCP's USB to
1851*3bfaa971Sbrad  * SPI / gpio chip.  It also appears to be a something like the PIC16F1455 used in
1852*3bfaa971Sbrad  * the MCP2221 / MCP2221A.  It is likely that a lot of this could use tables to
1853*3bfaa971Sbrad  * drive behavior.
1854*3bfaa971Sbrad  */
1855*3bfaa971Sbrad 
1856*3bfaa971Sbrad static void
1857*3bfaa971Sbrad umcpmio_attach(device_t parent, device_t self, void *aux)
1858*3bfaa971Sbrad {
1859*3bfaa971Sbrad 	struct umcpmio_softc *sc = device_private(self);
1860*3bfaa971Sbrad 	struct uhidev_attach_arg *uha = aux;
1861*3bfaa971Sbrad 	struct gpiobus_attach_args gba;
1862*3bfaa971Sbrad 	struct i2cbus_attach_args iba;
1863*3bfaa971Sbrad 	int err;
1864*3bfaa971Sbrad 	struct mcp2221_status_res status_res;
1865*3bfaa971Sbrad 
1866*3bfaa971Sbrad 	sc->sc_dev = self;
1867*3bfaa971Sbrad 	sc->sc_hdev = uha->parent;
1868*3bfaa971Sbrad 	sc->sc_udev = uha->uiaa->uiaa_device;
1869*3bfaa971Sbrad 
1870*3bfaa971Sbrad 	sc->sc_umcpmiolog = NULL;
1871*3bfaa971Sbrad 	sc->sc_dumpbuffer = false;
1872*3bfaa971Sbrad 
1873*3bfaa971Sbrad 	sc->sc_reportreadnostop = true;
1874*3bfaa971Sbrad 	sc->sc_cv_wait = 2500;
1875*3bfaa971Sbrad 	sc->sc_response_errcnt = 5;
1876*3bfaa971Sbrad 	sc->sc_busy_delay = 1;
1877*3bfaa971Sbrad 	sc->sc_retry_busy_read = 50;
1878*3bfaa971Sbrad 	sc->sc_retry_busy_write = 50;
1879*3bfaa971Sbrad 	sc->sc_irq_poll = 10;
1880*3bfaa971Sbrad 	sc->sc_dev_open[CONTROL_DEV] = sc->sc_dev_open[GP1_DEV] = sc->sc_dev_open[GP2_DEV] = sc->sc_dev_open[GP3_DEV] = false;
1881*3bfaa971Sbrad 
1882*3bfaa971Sbrad 	aprint_normal("\n");
1883*3bfaa971Sbrad 
1884*3bfaa971Sbrad 	if ((err = umcpmio_sysctl_init(sc)) != 0) {
1885*3bfaa971Sbrad 		aprint_error_dev(self, "Can't setup sysctl tree (%d)\n", err);
1886*3bfaa971Sbrad 		return;
1887*3bfaa971Sbrad 	}
1888*3bfaa971Sbrad 
1889*3bfaa971Sbrad 	mutex_init(&sc->sc_action_mutex, MUTEX_DEFAULT, IPL_NONE);
1890*3bfaa971Sbrad 	cv_init(&sc->sc_res_cv, "mcpres");
1891*3bfaa971Sbrad 	mutex_init(&sc->sc_res_mutex, MUTEX_DEFAULT, IPL_NONE);
1892*3bfaa971Sbrad 	sc->sc_res_buffer = NULL;
1893*3bfaa971Sbrad 	sc->sc_res_ready = false;
1894*3bfaa971Sbrad 
1895*3bfaa971Sbrad 	err = uhidev_open(sc->sc_hdev, &umcpmio_uhidev_intr, sc);
1896*3bfaa971Sbrad 
1897*3bfaa971Sbrad 	/* It is not clear that this should be needed, but it was noted
1898*3bfaa971Sbrad 	 * that the device would sometimes not be ready if this delay
1899*3bfaa971Sbrad 	 * was not present.  In fact, the attempts to set stuff a little
1900*3bfaa971Sbrad 	 * later would sometimes fail.
1901*3bfaa971Sbrad 	 */
1902*3bfaa971Sbrad 
1903*3bfaa971Sbrad 	delay(1000);
1904*3bfaa971Sbrad 
1905*3bfaa971Sbrad 	if (err)
1906*3bfaa971Sbrad 		aprint_error_dev(sc->sc_dev, "umcpmio_attach: open uhidev_open: err=%d\n",err);
1907*3bfaa971Sbrad 
1908*3bfaa971Sbrad 	if (!err)
1909*3bfaa971Sbrad 		err = umcpmio_get_status(sc, &status_res, true);
1910*3bfaa971Sbrad 
1911*3bfaa971Sbrad 	if (!err) {
1912*3bfaa971Sbrad 		aprint_normal_dev(sc->sc_dev, "Hardware revision: %d.%d, Firmware revision: %d.%d\n",
1913*3bfaa971Sbrad 		    status_res.mcp2221_hardware_rev_major,
1914*3bfaa971Sbrad 		    status_res.mcp2221_hardware_rev_minor,
1915*3bfaa971Sbrad 		    status_res.mcp2221_firmware_rev_major,
1916*3bfaa971Sbrad 		    status_res.mcp2221_firmware_rev_minor);
1917*3bfaa971Sbrad 
1918*3bfaa971Sbrad 		/* The datasheet suggests that it is possble for this to fail if the I2C port
1919*3bfaa971Sbrad 		 * is currently being used.  However...  since you just plugged in the chip,
1920*3bfaa971Sbrad 		 * the I2C port should not really be in use at that moment.  In any case, try
1921*3bfaa971Sbrad 		 * hard to set this and don't make it fatal if it did not get set.
1922*3bfaa971Sbrad 		 */
1923*3bfaa971Sbrad 		int i2cspeed=0;
1924*3bfaa971Sbrad 		while (! err && i2cspeed < 3) {
1925*3bfaa971Sbrad 			err = umcpmio_set_i2c_speed_one(sc, I2C_SPEED_SM, true);
1926*3bfaa971Sbrad 			if (err) {
1927*3bfaa971Sbrad 				aprint_error_dev(sc->sc_dev, "umcpmio_attach: set I2C speed: err=%d\n",
1928*3bfaa971Sbrad 				    err);
1929*3bfaa971Sbrad 				delay(300);
1930*3bfaa971Sbrad 			}
1931*3bfaa971Sbrad 			i2cspeed++;
1932*3bfaa971Sbrad 		}
1933*3bfaa971Sbrad 
1934*3bfaa971Sbrad 		struct mcp2221_get_sram_res get_sram_res;
1935*3bfaa971Sbrad 		err = umcpmio_get_sram(sc, &get_sram_res, true);
1936*3bfaa971Sbrad 
1937*3bfaa971Sbrad 		if (! err) {
1938*3bfaa971Sbrad 			umcpmio_dump_buffer(sc->sc_dumpbuffer, (uint8_t *)&get_sram_res, MCP2221_RES_BUFFER_SIZE, "umcpmio_attach get sram buffer copy");
1939*3bfaa971Sbrad 
1940*3bfaa971Sbrad 			/* There are only 4 pins right now, just unroll any loops */
1941*3bfaa971Sbrad 
1942*3bfaa971Sbrad 			sc->sc_gpio_pins[0].pin_num = 0;
1943*3bfaa971Sbrad 			sc->sc_gpio_pins[0].pin_caps = GPIO_PIN_INPUT | GPIO_PIN_OUTPUT | GPIO_PIN_ALT0 | GPIO_PIN_ALT3;
1944*3bfaa971Sbrad 			sc->sc_gpio_pins[0].pin_flags = umcpmio_sram_gpio_to_flags(get_sram_res.gp0_settings);
1945*3bfaa971Sbrad 			sc->sc_gpio_pins[0].pin_intrcaps = 0;
1946*3bfaa971Sbrad 			snprintf(sc->sc_gpio_pins[0].pin_defname, 4, "GP0");
1947*3bfaa971Sbrad 
1948*3bfaa971Sbrad 			sc->sc_gpio_pins[1].pin_num = 1;
1949*3bfaa971Sbrad 			sc->sc_gpio_pins[1].pin_caps = GPIO_PIN_INPUT | GPIO_PIN_OUTPUT | GPIO_PIN_ALT0 | GPIO_PIN_ALT1 | GPIO_PIN_ALT2 | GPIO_PIN_ALT3;
1950*3bfaa971Sbrad 			sc->sc_gpio_pins[1].pin_flags = umcpmio_sram_gpio_to_flags(get_sram_res.gp1_settings);
1951*3bfaa971Sbrad 			/* XXX - lets not advertise this right now... */
1952*3bfaa971Sbrad #if 0
1953*3bfaa971Sbrad 			sc->sc_gpio_pins[1].pin_intrcaps = GPIO_INTR_POS_EDGE | GPIO_INTR_NEG_EDGE | GPIO_INTR_DOUBLE_EDGE | GPIO_INTR_MPSAFE;
1954*3bfaa971Sbrad #endif
1955*3bfaa971Sbrad 			sc->sc_gpio_pins[1].pin_intrcaps = 0;
1956*3bfaa971Sbrad 			snprintf(sc->sc_gpio_pins[1].pin_defname, 4, "GP1");
1957*3bfaa971Sbrad 
1958*3bfaa971Sbrad 			sc->sc_gpio_pins[2].pin_num = 2;
1959*3bfaa971Sbrad 			sc->sc_gpio_pins[2].pin_caps = GPIO_PIN_INPUT | GPIO_PIN_OUTPUT | GPIO_PIN_ALT0 | GPIO_PIN_ALT1 | GPIO_PIN_ALT3;
1960*3bfaa971Sbrad 			sc->sc_gpio_pins[2].pin_flags = umcpmio_sram_gpio_to_flags(get_sram_res.gp2_settings);
1961*3bfaa971Sbrad 			sc->sc_gpio_pins[2].pin_intrcaps = 0;
1962*3bfaa971Sbrad 			snprintf(sc->sc_gpio_pins[2].pin_defname, 4, "GP2");
1963*3bfaa971Sbrad 
1964*3bfaa971Sbrad 			sc->sc_gpio_pins[3].pin_num = 3;
1965*3bfaa971Sbrad 			sc->sc_gpio_pins[3].pin_caps = GPIO_PIN_INPUT | GPIO_PIN_OUTPUT | GPIO_PIN_ALT0 | GPIO_PIN_ALT1 | GPIO_PIN_ALT3;
1966*3bfaa971Sbrad 			sc->sc_gpio_pins[3].pin_flags = umcpmio_sram_gpio_to_flags(get_sram_res.gp3_settings);
1967*3bfaa971Sbrad 			sc->sc_gpio_pins[3].pin_intrcaps = 0;
1968*3bfaa971Sbrad 			snprintf(sc->sc_gpio_pins[3].pin_defname, 4, "GP3");
1969*3bfaa971Sbrad 
1970*3bfaa971Sbrad 			sc->sc_gpio_gc.gp_cookie = sc;
1971*3bfaa971Sbrad 			sc->sc_gpio_gc.gp_pin_read = umcpmio_gpio_pin_read;
1972*3bfaa971Sbrad 			sc->sc_gpio_gc.gp_pin_write = umcpmio_gpio_pin_write;
1973*3bfaa971Sbrad 			sc->sc_gpio_gc.gp_pin_ctl = umcpmio_gpio_pin_ctl;
1974*3bfaa971Sbrad 
1975*3bfaa971Sbrad 			sc->sc_gpio_gc.gp_intr_establish = umcpmio_gpio_intr_establish;
1976*3bfaa971Sbrad 			sc->sc_gpio_gc.gp_intr_disestablish = umcpmio_gpio_intr_disestablish;
1977*3bfaa971Sbrad 			sc->sc_gpio_gc.gp_intr_str = umcpmio_gpio_intrstr;
1978*3bfaa971Sbrad 
1979*3bfaa971Sbrad 
1980*3bfaa971Sbrad 			gba.gba_gc = &sc->sc_gpio_gc;
1981*3bfaa971Sbrad 			gba.gba_pins = sc->sc_gpio_pins;
1982*3bfaa971Sbrad 			gba.gba_npins = MCP2221_NPINS;
1983*3bfaa971Sbrad 
1984*3bfaa971Sbrad 			sc->sc_gpio_dev = config_found(self, &gba, gpiobus_print, CFARGS(.iattr = "gpiobus"));
1985*3bfaa971Sbrad 
1986*3bfaa971Sbrad 			iic_tag_init(&sc->sc_i2c_tag);
1987*3bfaa971Sbrad 			sc->sc_i2c_tag.ic_cookie = sc;
1988*3bfaa971Sbrad 			sc->sc_i2c_tag.ic_acquire_bus = umcpmio_acquire_bus;
1989*3bfaa971Sbrad 			sc->sc_i2c_tag.ic_release_bus = umcpmio_release_bus;
1990*3bfaa971Sbrad 			sc->sc_i2c_tag.ic_exec = umcpmio_i2c_exec;
1991*3bfaa971Sbrad 
1992*3bfaa971Sbrad 			memset(&iba, 0, sizeof(iba));
1993*3bfaa971Sbrad 			iba.iba_tag = &sc->sc_i2c_tag;
1994*3bfaa971Sbrad 			sc->sc_i2c_dev = config_found(self, &iba, iicbus_print, CFARGS(.iattr = "i2cbus"));
1995*3bfaa971Sbrad 		} else {
1996*3bfaa971Sbrad 			aprint_error_dev(sc->sc_dev, "umcpmio_attach: get sram error: err=%d\n",
1997*3bfaa971Sbrad 			    err);
1998*3bfaa971Sbrad 		}
1999*3bfaa971Sbrad 	} else {
2000*3bfaa971Sbrad 		aprint_error_dev(sc->sc_dev, "umcpmio_attach: open uhidev_open: err=%d\n", err);
2001*3bfaa971Sbrad 	}
2002*3bfaa971Sbrad }
2003*3bfaa971Sbrad 
2004*3bfaa971Sbrad 
2005*3bfaa971Sbrad static int
2006*3bfaa971Sbrad umcpmio_detach(device_t self, int flags)
2007*3bfaa971Sbrad {
2008*3bfaa971Sbrad 	struct umcpmio_softc *sc = device_private(self);
2009*3bfaa971Sbrad 	int err;
2010*3bfaa971Sbrad 
2011*3bfaa971Sbrad 	DPRINTF(("umcpmio_detach: sc=%p flags=%d\n", sc, flags));
2012*3bfaa971Sbrad 
2013*3bfaa971Sbrad 	mutex_enter(&sc->sc_action_mutex);
2014*3bfaa971Sbrad 	sc->sc_dying = 1;
2015*3bfaa971Sbrad 
2016*3bfaa971Sbrad 	err = config_detach_children(self, flags);
2017*3bfaa971Sbrad 	if (err)
2018*3bfaa971Sbrad 		return err;
2019*3bfaa971Sbrad 
2020*3bfaa971Sbrad 	uhidev_close(sc->sc_hdev);
2021*3bfaa971Sbrad 
2022*3bfaa971Sbrad 	mutex_destroy(&sc->sc_res_mutex);
2023*3bfaa971Sbrad 	cv_destroy(&sc->sc_res_cv);
2024*3bfaa971Sbrad 
2025*3bfaa971Sbrad 	sysctl_teardown(&sc->sc_umcpmiolog);
2026*3bfaa971Sbrad 
2027*3bfaa971Sbrad 	mutex_exit(&sc->sc_action_mutex);
2028*3bfaa971Sbrad 	mutex_destroy(&sc->sc_action_mutex);
2029*3bfaa971Sbrad 
2030*3bfaa971Sbrad 	return 0;
2031*3bfaa971Sbrad }
2032*3bfaa971Sbrad 
2033*3bfaa971Sbrad static int
2034*3bfaa971Sbrad umcpmio_activate(device_t self, enum devact act)
2035*3bfaa971Sbrad {
2036*3bfaa971Sbrad 	struct umcpmio_softc *sc = device_private(self);
2037*3bfaa971Sbrad 
2038*3bfaa971Sbrad 	DPRINTFN(5,("umcpmio_activate: %d\n", act));
2039*3bfaa971Sbrad 
2040*3bfaa971Sbrad 	switch (act) {
2041*3bfaa971Sbrad 	case DVACT_DEACTIVATE:
2042*3bfaa971Sbrad 		sc->sc_dying = 1;
2043*3bfaa971Sbrad 		return 0;
2044*3bfaa971Sbrad 	default:
2045*3bfaa971Sbrad 		return EOPNOTSUPP;
2046*3bfaa971Sbrad 	}
2047*3bfaa971Sbrad }
2048