1/*	$NetBSD: umcpmio_gpio.c,v 1.1 2025/11/29 18:39:14 brad Exp $	*/
2
3/*
4 * Copyright (c) 2024, 2025 Brad Spencer <brad@anduin.eldar.org>
5 *
6 * Permission to use, copy, modify, and distribute this software for any
7 * purpose with or without fee is hereby granted, provided that the above
8 * copyright notice and this permission notice appear in all copies.
9 *
10 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
11 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
12 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
13 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
14 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
15 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
16 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
17 */
18
19#include <sys/cdefs.h>
20__KERNEL_RCSID(0, "$NetBSD: umcpmio_gpio.c,v 1.1 2025/11/29 18:39:14 brad Exp $");
21
22#ifdef _KERNEL_OPT
23#include "opt_usb.h"
24#endif
25
26#include <sys/param.h>
27#include <sys/types.h>
28
29#include <sys/gpio.h>
30
31#include <dev/gpio/gpiovar.h>
32
33#include <dev/usb/umcpmio.h>
34#include <dev/usb/umcpmio_hid_reports.h>
35#include <dev/usb/umcpmio_transport.h>
36#include <dev/usb/umcpmio_gpio.h>
37#include <dev/usb/umcpmio_subr.h>
38
39#define UMCPMIO_DEBUG 1
40#ifdef UMCPMIO_DEBUG
41#define DPRINTF(x)	do { if (umcpmiodebug) printf x; } while (0)
42#define DPRINTFN(n, x)	do { if (umcpmiodebug > (n)) printf x; } while (0)
43extern int umcpmiodebug;
44#else
45#define DPRINTF(x)	__nothing
46#define DPRINTFN(n, x)	__nothing
47#endif
48
49/* Stuff required to deal with the gpio on the MCP2210 and
50 * MCP2221 / MCP2221A */
51
52/* The MCP2210 has more, but simpler, pins than the MCP2221
53 * / MCP2221A.
54 *
55 *
56 * The MCP2221 / MCP2221A does not have symetric behavior with
57 * respect to the get and puts of the SRAM.  This means that you
58 * more or less can't just take the response buffer from a SRAM
59 * get and use it directly as a SRAM put.  The MCP2210 is better
60 * in this respect.
61 */
62
63
64/* We call the dedicated function ALT3 everywhere */
65
66static uint32_t
67mcp2210_counter_gp_to_flags(uint8_t other_settings)
68{
69	uint32_t r = 0;
70
71	switch ((other_settings >> 1) & 0x07) {
72	case MCP2210_COUNTER_FALLING_EDGE:
73		r |= GPIO_PIN_ALT3;
74		break;
75	case MCP2210_COUNTER_RISING_EDGE:
76		r |= GPIO_PIN_ALT4;
77		break;
78	case MCP2210_COUNTER_LOW_PULSE:
79		r |= GPIO_PIN_ALT5;
80		break;
81	case MCP2210_COUNTER_HIGH_PULSE:
82		r |= GPIO_PIN_ALT6;
83		break;
84	case MCP2210_COUNTER_OFF:
85	default:
86		printf("mcp2210_counter_gp_to_flags: Unhandled flag on counter pin: 0x%02x\n", other_settings);
87	}
88
89	return r;
90}
91
92static uint32_t
93mcp2210_sram_gpio_to_flags(uint8_t *gp_settings, int pin)
94{
95	uint32_t r = 0;
96
97	switch (gp_settings[pin]) {
98	case MCP2210_PIN_IS_ALT0:
99		r |= GPIO_PIN_ALT0;
100		break;
101	case MCP2210_PIN_IS_DED:
102		if (pin == 6) {
103			r |= mcp2210_counter_gp_to_flags(gp_settings[11]);
104		} else {
105			r |= GPIO_PIN_ALT3;
106		}
107		break;
108	case MCP2210_PIN_IS_GPIO:
109	default:
110		if (pin < 8) {
111			if (gp_settings[9] & (1 << pin))
112				r |= GPIO_PIN_INPUT;
113			else
114				r |= GPIO_PIN_OUTPUT;
115		} else {
116			r |= GPIO_PIN_INPUT;
117		}
118		break;
119	}
120
121	return r;
122}
123
124static uint32_t
125mcp2221_sram_gpio_to_flags(uint8_t gp_setting)
126{
127	uint32_t r = 0;
128
129	switch (gp_setting & MCP2221_SRAM_PIN_TYPE_MASK) {
130	case MCP2221_SRAM_PIN_IS_DED:
131		r |= GPIO_PIN_ALT3;
132		break;
133	case MCP2221_SRAM_PIN_IS_ALT0:
134		r |= GPIO_PIN_ALT0;
135		break;
136	case MCP2221_SRAM_PIN_IS_ALT1:
137		r |= GPIO_PIN_ALT1;
138		break;
139	case MCP2221_SRAM_PIN_IS_ALT2:
140		r |= GPIO_PIN_ALT2;
141		break;
142	case MCP2221_SRAM_PIN_IS_GPIO:
143	default:
144		if ((gp_setting & MCP2221_SRAM_GPIO_TYPE_MASK) ==
145		    MCP2221_SRAM_GPIO_INPUT)
146			r |= GPIO_PIN_INPUT;
147		else
148			r |= GPIO_PIN_OUTPUT;
149		break;
150	}
151
152	return r;
153}
154
155static void
156mcp2221_set_gpio_dir_sram(struct mcp2221_set_sram_req *req, int pin, int flags)
157{
158	uint8_t *alter = NULL;
159	uint8_t *newvalue = NULL;
160
161	if (pin >= 0 && pin < MCP2221_NPINS) {
162		switch (pin) {
163		case 0:
164			alter = &req->alter_gpio_config;
165			newvalue = &req->gp0_settings;
166			break;
167		case 1:
168			alter = &req->alter_gpio_config;
169			newvalue = &req->gp1_settings;
170			break;
171		case 2:
172			alter = &req->alter_gpio_config;
173			newvalue = &req->gp2_settings;
174			break;
175		case 3:
176			alter = &req->alter_gpio_config;
177			newvalue = &req->gp3_settings;
178			break;
179		default:
180			break;
181		}
182
183		if (alter != NULL) {
184			*alter = MCP2221_SRAM_ALTER_GPIO;
185			if (flags & GPIO_PIN_INPUT)
186				*newvalue |= MCP2221_SRAM_GPIO_INPUT;
187			else
188				*newvalue &= ~MCP2221_SRAM_GPIO_INPUT;
189		}
190	}
191}
192
193static int
194mcp2210_get_gpio_sram(struct umcpmio_softc *sc,
195    struct mcp2210_get_gpio_sram_res *res)
196{
197	struct mcp2210_get_gpio_sram_req req;
198	int err = 0;
199
200	memset(&req, 0, MCP2210_REQ_BUFFER_SIZE);
201	req.cmd = MCP2210_CMD_GET_GPIO_SRAM;
202
203	KASSERT(mutex_owned(&sc->sc_action_mutex));
204
205	err = umcpmio_send_report(sc,
206	    (uint8_t *) & req, MCP2210_REQ_BUFFER_SIZE,
207	    (uint8_t *) res, sc->sc_cv_wait);
208
209	err = mcp2210_decode_errors(req.cmd, err, res->completion);
210
211	return err;
212}
213
214static int
215mcp2210_set_gpio_sram(struct umcpmio_softc *sc,
216    struct mcp2210_set_gpio_sram_req *req, struct mcp2210_set_gpio_sram_res *res)
217{
218	int err = 0;
219
220	req->cmd = MCP2210_CMD_SET_GPIO_SRAM;
221
222	KASSERT(mutex_owned(&sc->sc_action_mutex));
223
224	err = umcpmio_send_report(sc,
225	    (uint8_t *) req, MCP2210_REQ_BUFFER_SIZE,
226	    (uint8_t *) res, sc->sc_cv_wait);
227
228	err = mcp2210_decode_errors(req->cmd, err, res->completion);
229
230	return err;
231}
232
233static int
234mcp2210_get_gpio_dir_sram(struct umcpmio_softc *sc,
235    struct mcp2210_get_gpio_dir_res *res)
236{
237	struct mcp2210_get_gpio_dir_req req;
238	int err = 0;
239
240	memset(&req, 0, MCP2210_REQ_BUFFER_SIZE);
241	req.cmd = MCP2210_CMD_GET_GPIO_DIR_SRAM;
242
243	KASSERT(mutex_owned(&sc->sc_action_mutex));
244
245	err = umcpmio_send_report(sc,
246	    (uint8_t *) & req, MCP2210_REQ_BUFFER_SIZE,
247	    (uint8_t *) res, sc->sc_cv_wait);
248
249	err = mcp2210_decode_errors(req.cmd, err, res->completion);
250
251	return err;
252}
253
254static int
255mcp2210_get_gpio_value_sram(struct umcpmio_softc *sc,
256    struct mcp2210_get_gpio_value_res *res)
257{
258	struct mcp2210_get_gpio_value_req req;
259	int err = 0;
260
261	memset(&req, 0, MCP2210_REQ_BUFFER_SIZE);
262	req.cmd = MCP2210_CMD_GET_GPIO_VAL_SRAM;
263
264	KASSERT(mutex_owned(&sc->sc_action_mutex));
265
266	err = umcpmio_send_report(sc,
267	    (uint8_t *) & req, MCP2210_REQ_BUFFER_SIZE,
268	    (uint8_t *) res, sc->sc_cv_wait);
269
270	err = mcp2210_decode_errors(req.cmd, err, res->completion);
271
272	return err;
273}
274
275static int
276mcp2210_set_gpio_dir_sram(struct umcpmio_softc *sc,
277    struct mcp2210_set_gpio_dir_req *req, struct mcp2210_set_gpio_dir_res *res)
278{
279	int err = 0;
280
281	req->cmd = MCP2210_CMD_SET_GPIO_DIR_SRAM;
282
283	KASSERT(mutex_owned(&sc->sc_action_mutex));
284
285	err = umcpmio_send_report(sc,
286	    (uint8_t *) req, MCP2210_REQ_BUFFER_SIZE,
287	    (uint8_t *) res, sc->sc_cv_wait);
288
289	err = mcp2210_decode_errors(req->cmd, err, res->completion);
290
291	return err;
292}
293
294static int
295mcp2210_set_gpio_value_sram(struct umcpmio_softc *sc,
296    struct mcp2210_set_gpio_value_req *req, struct mcp2210_set_gpio_value_res *res)
297{
298	int err = 0;
299
300	req->cmd = MCP2210_CMD_SET_GPIO_VAL_SRAM;
301
302	KASSERT(mutex_owned(&sc->sc_action_mutex));
303
304	err = umcpmio_send_report(sc,
305	    (uint8_t *) req, MCP2210_REQ_BUFFER_SIZE,
306	    (uint8_t *) res, sc->sc_cv_wait);
307
308	err = mcp2210_decode_errors(req->cmd, err, res->completion);
309
310	return err;
311}
312
313static void
314mcp2221_set_gpio_designation_sram(struct mcp2221_set_sram_req *req, int pin,
315    int flags)
316{
317	uint8_t *alter = NULL;
318	uint8_t *newvalue = NULL;
319	uint32_t altmask =
320	GPIO_PIN_ALT0 | GPIO_PIN_ALT1 | GPIO_PIN_ALT2 | GPIO_PIN_ALT3;
321
322	if (pin >= 0 && pin < MCP2221_NPINS) {
323		switch (pin) {
324		case 0:
325			alter = &req->alter_gpio_config;
326			newvalue = &req->gp0_settings;
327			break;
328		case 1:
329			alter = &req->alter_gpio_config;
330			newvalue = &req->gp1_settings;
331			break;
332		case 2:
333			alter = &req->alter_gpio_config;
334			newvalue = &req->gp2_settings;
335			break;
336		case 3:
337			alter = &req->alter_gpio_config;
338			newvalue = &req->gp3_settings;
339			break;
340		default:
341			break;
342		}
343
344		if (alter != NULL) {
345			int nv = *newvalue;
346
347			*alter = MCP2221_SRAM_ALTER_GPIO;
348			nv &= 0xF8;
349
350			if (flags & (GPIO_PIN_OUTPUT | GPIO_PIN_INPUT)) {
351				nv |= MCP2221_SRAM_PIN_IS_GPIO;
352			} else {
353				switch (flags & altmask) {
354				case GPIO_PIN_ALT0:
355					nv |= MCP2221_SRAM_PIN_IS_ALT0;
356					break;
357				case GPIO_PIN_ALT1:
358					nv |= MCP2221_SRAM_PIN_IS_ALT1;
359					break;
360				case GPIO_PIN_ALT2:
361					nv |= MCP2221_SRAM_PIN_IS_ALT2;
362					break;
363					/* ALT3 will always be used as the
364					 * dedicated function specific to the
365					 * pin.  Not all of the pins will have
366					 * the alt functions below #3. */
367				case GPIO_PIN_ALT3:
368					nv |= MCP2221_SRAM_PIN_IS_DED;
369					break;
370				default:
371					break;
372				}
373			}
374			*newvalue = nv;
375		}
376	}
377}
378/*
379 * It is unfortunate that the GET and PUT requests are not symertric.  That is,
380 * the bits sort of line up but not quite between a GET and PUT.
381 */
382
383static struct umcpmio_mapping_put mcp2221_vref_puts[] = {
384	{
385		.tname = "4.096V",
386		.mask = 0x06 | 0x01,
387	},
388	{
389		.tname = "2.048V",
390		.mask = 0x04 | 0x01,
391	},
392	{
393		.tname = "1.024V",
394		.mask = 0x02 | 0x01,
395	},
396	{
397		.tname = "OFF",
398		.mask = 0x00 | 0x01,
399	},
400	{
401		.tname = "VDD",
402		.mask = 0x00,
403	}
404};
405
406void
407mcp2221_set_dac_vref(struct mcp2221_set_sram_req *req, char *newvref)
408{
409	int i;
410
411	for (i = 0; i < __arraycount(mcp2221_vref_puts); i++) {
412		if (strncmp(newvref, mcp2221_vref_puts[i].tname,
413		    MCP2221_VREF_NAME) == 0) {
414			break;
415		}
416	}
417
418	if (i == __arraycount(mcp2221_vref_puts))
419		return;
420
421	req->dac_voltage_reference |= mcp2221_vref_puts[i].mask |
422	    MCP2221_SRAM_CHANGE_DAC_VREF;
423}
424
425int
426mcp2221_set_dac_vref_one(struct umcpmio_softc *sc, char *newvref)
427{
428	struct mcp2221_set_sram_req req;
429	struct mcp2221_set_sram_res res;
430	int err = 0;
431
432	KASSERT(mutex_owned(&sc->sc_action_mutex));
433
434	memset(&req, 0, MCP2221_REQ_BUFFER_SIZE);
435	mcp2221_set_dac_vref(&req, newvref);
436	err = mcp2221_put_sram(sc, &req, &res);
437
438	return err;
439}
440
441void
442mcp2221_set_dac_value(struct mcp2221_set_sram_req *req, uint8_t newvalue)
443{
444	req->set_dac_output_value |= (newvalue & MCP2221_SRAM_DAC_VALUE_MASK) |
445	    MCP2221_SRAM_CHANGE_DAC_VREF;
446}
447
448int
449mcp2221_set_dac_value_one(struct umcpmio_softc *sc, uint8_t newvalue)
450{
451	struct mcp2221_set_sram_req req;
452	struct mcp2221_set_sram_res res;
453	int err = 0;
454
455	KASSERT(mutex_owned(&sc->sc_action_mutex));
456
457	memset(&req, 0, MCP2221_REQ_BUFFER_SIZE);
458	mcp2221_set_dac_value(&req, newvalue);
459	err = mcp2221_put_sram(sc, &req, &res);
460
461	return err;
462}
463
464void
465mcp2221_set_adc_vref(struct mcp2221_set_sram_req *req, char *newvref)
466{
467	int i;
468
469	for (i = 0; i < __arraycount(mcp2221_vref_puts); i++) {
470		if (strncmp(newvref, mcp2221_vref_puts[i].tname,
471		    MCP2221_VREF_NAME) == 0) {
472			break;
473		}
474	}
475
476	if (i == __arraycount(mcp2221_vref_puts))
477		return;
478
479	req->adc_voltage_reference |= mcp2221_vref_puts[i].mask |
480	    MCP2221_SRAM_CHANGE_ADC_VREF;
481}
482
483int
484mcp2221_set_adc_vref_one(struct umcpmio_softc *sc, char *newvref)
485{
486	struct mcp2221_set_sram_req req;
487	struct mcp2221_set_sram_res res;
488	int err = 0;
489
490	KASSERT(mutex_owned(&sc->sc_action_mutex));
491
492	memset(&req, 0, MCP2221_REQ_BUFFER_SIZE);
493	mcp2221_set_adc_vref(&req, newvref);
494	err = mcp2221_put_sram(sc, &req, &res);
495
496	return err;
497}
498
499static struct umcpmio_mapping_put mcp2221_dc_puts[] = {
500	{
501		.tname = "75%",
502		.mask = MCP2221_SRAM_GPIO_CLOCK_DC_75,
503	},
504	{
505		.tname = "50%",
506		.mask = MCP2221_SRAM_GPIO_CLOCK_DC_50,
507	},
508	{
509		.tname = "25%",
510		.mask = MCP2221_SRAM_GPIO_CLOCK_DC_25,
511	},
512	{
513		.tname = "0%",
514		.mask = MCP2221_SRAM_GPIO_CLOCK_DC_0,
515	}
516};
517
518void
519mcp2221_set_gpioclock_dc(struct mcp2221_set_sram_req *req, char *new_dc)
520{
521	int i;
522
523	for (i = 0; i < __arraycount(mcp2221_dc_puts); i++) {
524		if (strncmp(new_dc, mcp2221_dc_puts[i].tname,
525		    MCP2221_DC_NAME) == 0) {
526			break;
527		}
528	}
529
530	if (i == __arraycount(mcp2221_dc_puts))
531		return;
532
533	req->clock_output_divider |= mcp2221_dc_puts[i].mask;
534}
535
536int
537mcp2221_set_gpioclock_dc_one(struct umcpmio_softc *sc, char *new_dutycycle)
538{
539	struct mcp2221_get_sram_res current_sram_res;
540	struct mcp2221_set_sram_req req;
541	struct mcp2221_set_sram_res res;
542	int err = 0;
543
544	KASSERT(mutex_owned(&sc->sc_action_mutex));
545
546	err = mcp2221_get_sram(sc, &current_sram_res);
547	if (err)
548		goto out;
549
550	memset(&req, 0, MCP2221_REQ_BUFFER_SIZE);
551	mcp2221_set_gpioclock_dc(&req, new_dutycycle);
552	DPRINTF(("mcp2221_set_gpioclock_dc_one:"
553	    " req.clock_output_divider=%02x, current mask=%02x\n",
554	    req.clock_output_divider,
555	    (current_sram_res.clock_divider &
556	    MCP2221_SRAM_GPIO_CLOCK_CD_MASK)));
557	req.clock_output_divider |=
558	    (current_sram_res.clock_divider &
559	    MCP2221_SRAM_GPIO_CLOCK_CD_MASK) |
560	    MCP2221_SRAM_GPIO_CHANGE_DCCD;
561	DPRINTF(("mcp2221_set_gpioclock_dc_one:"
562	    " SET req.clock_output_divider=%02x\n",
563	    req.clock_output_divider));
564	err = mcp2221_put_sram(sc, &req, &res);
565out:
566	return err;
567}
568
569static struct umcpmio_mapping_put mcp2221_cd_puts[] = {
570	{
571		.tname = "375kHz",
572		.mask = MCP2221_SRAM_GPIO_CLOCK_CD_375KHZ,
573	},
574	{
575		.tname = "750kHz",
576		.mask = MCP2221_SRAM_GPIO_CLOCK_CD_750KHZ,
577	},
578	{
579		.tname = "1.5MHz",
580		.mask = MCP2221_SRAM_GPIO_CLOCK_CD_1P5MHZ,
581	},
582	{
583		.tname = "3MHz",
584		.mask = MCP2221_SRAM_GPIO_CLOCK_CD_3MHZ,
585	},
586	{
587		.tname = "6MHz",
588		.mask = MCP2221_SRAM_GPIO_CLOCK_CD_6MHZ,
589	},
590	{
591		.tname = "12MHz",
592		.mask = MCP2221_SRAM_GPIO_CLOCK_CD_12MHZ,
593	},
594	{
595		.tname = "24MHz",
596		.mask = MCP2221_SRAM_GPIO_CLOCK_CD_24MHZ,
597	}
598};
599
600void
601mcp2221_set_gpioclock_cd(struct mcp2221_set_sram_req *req, char *new_cd)
602{
603	int i;
604
605	for (i = 0; i < __arraycount(mcp2221_cd_puts); i++) {
606		if (strncmp(new_cd, mcp2221_cd_puts[i].tname,
607		    MCP2221_CD_NAME) == 0) {
608			break;
609		}
610	}
611
612	if (i == __arraycount(mcp2221_cd_puts))
613		return;
614
615	req->clock_output_divider |= mcp2221_cd_puts[i].mask;
616}
617
618int
619mcp2221_set_gpioclock_cd_one(struct umcpmio_softc *sc, char *new_clockdivider)
620{
621	struct mcp2221_get_sram_res current_sram_res;
622	struct mcp2221_set_sram_req req;
623	struct mcp2221_set_sram_res res;
624	int err = 0;
625
626	KASSERT(mutex_owned(&sc->sc_action_mutex));
627
628	err = mcp2221_get_sram(sc, &current_sram_res);
629	if (err)
630		goto out;
631
632	memset(&req, 0, MCP2221_REQ_BUFFER_SIZE);
633	mcp2221_set_gpioclock_cd(&req, new_clockdivider);
634	DPRINTF(("mcp2221_set_gpioclock_cd_one:"
635	    " req.clock_output_divider=%02x, current mask=%02x\n",
636	    req.clock_output_divider,
637	    (current_sram_res.clock_divider &
638	    MCP2221_SRAM_GPIO_CLOCK_CD_MASK)));
639	req.clock_output_divider |=
640	    (current_sram_res.clock_divider &
641	    MCP2221_SRAM_GPIO_CLOCK_DC_MASK) |
642	    MCP2221_SRAM_GPIO_CHANGE_DCCD;
643	DPRINTF(("mcp2221_set_gpioclock_cd_one:"
644	    " SET req.clock_output_divider=%02x\n",
645	    req.clock_output_divider));
646	err = mcp2221_put_sram(sc, &req, &res);
647out:
648	return err;
649}
650
651int
652mcp2221_get_gpio_cfg(struct umcpmio_softc *sc,
653    struct mcp2221_get_gpio_cfg_res *res)
654{
655	struct mcp2221_get_gpio_cfg_req req;
656	int err = 0;
657
658	memset(&req, 0, MCP2221_REQ_BUFFER_SIZE);
659	req.cmd = MCP2221_CMD_GET_GPIO_CFG;
660
661	KASSERT(mutex_owned(&sc->sc_action_mutex));
662
663	err = umcpmio_send_report(sc,
664	    (uint8_t *) & req, MCP2221_REQ_BUFFER_SIZE,
665	    (uint8_t *) res, sc->sc_cv_wait);
666
667	return err;
668}
669
670static int
671mcp2221_put_gpio_cfg(struct umcpmio_softc *sc,
672    struct mcp2221_set_gpio_cfg_req *req, struct mcp2221_set_gpio_cfg_res *res)
673{
674	int err = 0;
675
676	req->cmd = MCP2221_CMD_SET_GPIO_CFG;
677
678	KASSERT(mutex_owned(&sc->sc_action_mutex));
679
680	err = umcpmio_send_report(sc,
681	    (uint8_t *) req, MCP2221_REQ_BUFFER_SIZE,
682	    (uint8_t *) res, sc->sc_cv_wait);
683
684	return err;
685}
686
687static int
688mcp2210_get_gpio_value(struct umcpmio_softc *sc,
689    int pin)
690{
691	struct mcp2210_get_gpio_value_res res;
692	uint16_t v;
693	int err = 0;
694	int r = GPIO_PIN_LOW;
695
696
697	KASSERT(mutex_owned(&sc->sc_action_mutex));
698
699	err = mcp2210_get_gpio_value_sram(sc, &res);
700
701	if (!err) {
702		v = (res.pin_value_msb << 8) | res.pin_value_lsb;
703		if (v & (1 << pin))
704			r = GPIO_PIN_HIGH;
705	}
706	return r;
707}
708/* So... if the pin isn't set to GPIO, just call the output LOW */
709
710static int
711mcp2221_get_gpio_value(struct umcpmio_softc *sc,
712    int pin)
713{
714	struct mcp2221_get_gpio_cfg_res get_gpio_cfg_res;
715	int err = 0;
716	int r = GPIO_PIN_LOW;
717
718	KASSERT(mutex_owned(&sc->sc_action_mutex));
719
720	err = mcp2221_get_gpio_cfg(sc, &get_gpio_cfg_res);
721	if (err)
722		goto out;
723
724	if (get_gpio_cfg_res.cmd != MCP2221_CMD_GET_GPIO_CFG ||
725	    get_gpio_cfg_res.completion != MCP2221_CMD_COMPLETE_OK) {
726		device_printf(sc->sc_dev, "mcp2221_get_gpio_value:"
727		    " wrong command or error: %02x %02x\n",
728		    get_gpio_cfg_res.cmd,
729		    get_gpio_cfg_res.completion);
730		goto out;
731	}
732	switch (pin) {
733	case 0:
734		if (get_gpio_cfg_res.gp0_pin_value !=
735		    MCP2221_GPIO_CFG_VALUE_NOT_GPIO)
736			if (get_gpio_cfg_res.gp0_pin_value == 0x01)
737				r = GPIO_PIN_HIGH;
738		break;
739	case 1:
740		if (get_gpio_cfg_res.gp1_pin_value !=
741		    MCP2221_GPIO_CFG_VALUE_NOT_GPIO)
742			if (get_gpio_cfg_res.gp1_pin_value == 0x01)
743				r = GPIO_PIN_HIGH;
744		break;
745	case 2:
746		if (get_gpio_cfg_res.gp2_pin_value !=
747		    MCP2221_GPIO_CFG_VALUE_NOT_GPIO)
748			if (get_gpio_cfg_res.gp2_pin_value == 0x01)
749				r = GPIO_PIN_HIGH;
750		break;
751	case 3:
752		if (get_gpio_cfg_res.gp3_pin_value !=
753		    MCP2221_GPIO_CFG_VALUE_NOT_GPIO)
754			if (get_gpio_cfg_res.gp3_pin_value == 0x01)
755				r = GPIO_PIN_HIGH;
756		break;
757	default:
758		break;
759	}
760out:
761	return r;
762}
763
764static int
765mcp2210_set_gpio_value(struct umcpmio_softc *sc,
766    int pin, bool value)
767{
768	int err = 0;
769	struct mcp2210_get_gpio_value_res get_res;
770	struct mcp2210_set_gpio_value_req set_req;
771	struct mcp2210_set_gpio_value_res set_res;
772	uint16_t v;
773
774	KASSERT(mutex_owned(&sc->sc_action_mutex));
775
776	err = mcp2210_get_gpio_value_sram(sc, &get_res);
777	if (!err && get_res.completion != MCP2210_CMD_COMPLETE_OK)
778		err = EIO;
779
780	if (!err) {
781		v = (get_res.pin_value_msb << 8) | get_res.pin_value_lsb;
782
783		if (value)
784			v |= (1 << pin);
785		else
786			v &= ~(1 << pin);
787
788		set_req.pin_value_lsb = v & 0x00ff;
789		set_req.pin_value_msb = (v & 0xff00) >> 8;
790
791		err = mcp2210_set_gpio_value_sram(sc, &set_req, &set_res);
792	}
793	return err;
794}
795
796static void
797mcp2221_set_gpio_value(struct mcp2221_set_gpio_cfg_req *req,
798    int pin, bool value)
799{
800	uint8_t *alter = NULL;
801	uint8_t *newvalue = NULL;
802
803	if (pin < 0 || pin >= MCP2221_NPINS)
804		return;
805
806	switch (pin) {
807	case 0:
808		alter = &req->alter_gp0_value;
809		newvalue = &req->new_gp0_value;
810		break;
811	case 1:
812		alter = &req->alter_gp1_value;
813		newvalue = &req->new_gp1_value;
814		break;
815	case 2:
816		alter = &req->alter_gp2_value;
817		newvalue = &req->new_gp2_value;
818		break;
819	case 3:
820		alter = &req->alter_gp3_value;
821		newvalue = &req->new_gp3_value;
822		break;
823	default:
824		return;
825	}
826
827	*alter = MCP2221_GPIO_CFG_ALTER;
828	*newvalue = 0;
829	if (value)
830		*newvalue = 1;
831}
832
833static int
834mcp2221_set_gpio_value_one(struct umcpmio_softc *sc,
835    int pin, bool value)
836{
837	int err = 0;
838	struct mcp2221_set_gpio_cfg_req req;
839	struct mcp2221_set_gpio_cfg_res res;
840
841	KASSERT(mutex_owned(&sc->sc_action_mutex));
842
843	memset(&req, 0, MCP2221_REQ_BUFFER_SIZE);
844	mcp2221_set_gpio_value(&req, pin, value);
845	err = mcp2221_put_gpio_cfg(sc, &req, &res);
846	if (err)
847		goto out;
848	if (res.cmd != MCP2221_CMD_SET_GPIO_CFG ||
849	    res.completion != MCP2221_CMD_COMPLETE_OK) {
850		err = EIO;
851		device_printf(sc->sc_dev, "umcpmio_gpio_pin_write:"
852		    "  not the command desired, or error: %02x %02x\n",
853		    res.cmd,
854		    res.completion);
855	}
856out:
857	return err;
858}
859
860/* These are standard gpio reads and set calls */
861
862static int
863umcpmio_gpio_pin_read(void *arg, int pin)
864{
865	struct umcpmio_softc *sc = arg;
866	int r = GPIO_PIN_LOW;
867
868	mutex_enter(&sc->sc_action_mutex);
869
870	if (sc->sc_chipinfo->usb_id == USB_PRODUCT_MICROCHIP_MCP2210)
871		r = mcp2210_get_gpio_value(sc, pin);
872	if (sc->sc_chipinfo->usb_id == USB_PRODUCT_MICROCHIP_MCP2221)
873		r = mcp2221_get_gpio_value(sc, pin);
874
875	mutex_exit(&sc->sc_action_mutex);
876
877	return r;
878}
879
880static void
881umcpmio_gpio_pin_write(void *arg, int pin, int value)
882{
883	struct umcpmio_softc *sc = arg;
884
885	mutex_enter(&sc->sc_action_mutex);
886
887	if (sc->sc_chipinfo->usb_id == USB_PRODUCT_MICROCHIP_MCP2210)
888		mcp2210_set_gpio_value(sc, pin, value);
889	if (sc->sc_chipinfo->usb_id == USB_PRODUCT_MICROCHIP_MCP2221)
890		mcp2221_set_gpio_value_one(sc, pin, value);
891
892	mutex_exit(&sc->sc_action_mutex);
893}
894
895static int
896mcp2210_gpio_pin_ctl(void *arg, int pin, int flags)
897{
898	struct umcpmio_softc *sc = arg;
899	struct mcp2210_set_gpio_sram_req mcp2210_set_gpio_sram_req;
900	struct mcp2210_set_gpio_dir_req mcp2210_set_gpio_dir_sram_req;
901	struct mcp2210_set_gpio_sram_res mcp2210_set_gpio_sram_res;
902	struct mcp2210_set_gpio_dir_res mcp2210_set_gpio_dir_sram_res;
903	struct mcp2210_set_gpio_value_req mcp2210_set_gpio_value_sram_req;
904	struct mcp2210_set_gpio_value_res mcp2210_set_gpio_value_sram_res;
905
906	struct mcp2210_get_gpio_sram_res mcp2210_get_gpio_sram_res;
907	struct mcp2210_get_gpio_dir_res mcp2210_get_gpio_dir_sram_res;
908	struct mcp2210_get_gpio_value_res mcp2210_get_gpio_value_sram_res;
909
910	uint32_t altmask =
911	GPIO_PIN_ALT0 | GPIO_PIN_ALT3 | GPIO_PIN_ALT4 | GPIO_PIN_ALT5 | GPIO_PIN_ALT6;
912	int err = 0;
913	uint16_t vdir;
914
915	if (sc->sc_dying)
916		return 0;
917
918	KASSERT(mutex_owned(&sc->sc_action_mutex));
919
920	err = mcp2210_get_gpio_sram(sc, &mcp2210_get_gpio_sram_res);
921	if (err)
922		goto out;
923	err = mcp2210_get_gpio_dir_sram(sc, &mcp2210_get_gpio_dir_sram_res);
924	if (err)
925		goto out;
926	err = mcp2210_get_gpio_value_sram(sc, &mcp2210_get_gpio_value_sram_res);
927	if (err)
928		goto out;
929
930	umcpmio_dump_buffer(sc->sc_dumpbuffer,
931	    (uint8_t *) & mcp2210_get_gpio_sram_res, MCP2210_RES_BUFFER_SIZE,
932	    "mcp2210_gpio_pin_ctl get gpio sram res");
933	umcpmio_dump_buffer(sc->sc_dumpbuffer,
934	    (uint8_t *) & mcp2210_get_gpio_dir_sram_res, MCP2210_RES_BUFFER_SIZE,
935	    "mcp2210_gpio_pin_ctl get gpio dir sram res");
936
937	memset(&mcp2210_set_gpio_sram_req.cmd, 0, MCP2210_REQ_BUFFER_SIZE);
938	memcpy(&mcp2210_set_gpio_sram_req.gp0_designation,
939	    &mcp2210_get_gpio_sram_res.gp0_designation,
940	    &mcp2210_get_gpio_sram_res.nvram_protection -
941	    &mcp2210_get_gpio_sram_res.gp0_designation + 1);
942
943	memset(&mcp2210_set_gpio_dir_sram_req.cmd, 0, MCP2210_REQ_BUFFER_SIZE);
944	vdir = (mcp2210_get_gpio_dir_sram_res.pin_dir_msb << 8) |
945	    mcp2210_get_gpio_dir_sram_res.pin_dir_lsb;
946
947	uint8_t *b = (uint8_t *) & mcp2210_set_gpio_sram_req.cmd;
948	bool changed_sram = false;
949
950	if (flags & (GPIO_PIN_OUTPUT | GPIO_PIN_INPUT)) {
951		if (b[MCP2210_GPIO_SRAM_GP0 + pin] != MCP2210_PIN_IS_GPIO) {
952			b[MCP2210_GPIO_SRAM_GP0 + pin] = MCP2210_PIN_IS_GPIO;
953			changed_sram = true;
954		}
955		if (flags & GPIO_PIN_INPUT)
956			vdir |= (1 << pin);
957		else
958			vdir &= ~(1 << pin);
959	} else {
960		switch (flags & altmask) {
961		case GPIO_PIN_ALT0:
962			if (b[MCP2210_GPIO_SRAM_GP0 + pin] != MCP2210_PIN_IS_ALT0) {
963				b[MCP2210_GPIO_SRAM_GP0 + pin] = MCP2210_PIN_IS_ALT0;
964				changed_sram = true;
965			}
966			break;
967		case GPIO_PIN_ALT3:
968			if (b[MCP2210_GPIO_SRAM_GP0 + pin] != MCP2210_PIN_IS_DED) {
969				b[MCP2210_GPIO_SRAM_GP0 + pin] = MCP2210_PIN_IS_DED;
970				changed_sram = true;
971			}
972			if (pin == 6) {
973				if (mcp2210_counter_gp_to_flags(mcp2210_get_gpio_sram_res.other_settings) != GPIO_PIN_ALT3) {
974					mcp2210_set_gpio_sram_req.other_settings &= 0xf1;
975					mcp2210_set_gpio_sram_req.other_settings |= MCP2210_COUNTER_FALLING_EDGE << 1;
976					changed_sram = true;
977				}
978			}
979			break;
980		case GPIO_PIN_ALT4:
981			if (b[MCP2210_GPIO_SRAM_GP0 + pin] != MCP2210_PIN_IS_DED) {
982				b[MCP2210_GPIO_SRAM_GP0 + pin] = MCP2210_PIN_IS_DED;
983				changed_sram = true;
984			}
985			if (pin == 6) {
986				if (mcp2210_counter_gp_to_flags(mcp2210_get_gpio_sram_res.other_settings) != GPIO_PIN_ALT4) {
987					mcp2210_set_gpio_sram_req.other_settings &= 0xf1;
988					mcp2210_set_gpio_sram_req.other_settings |= MCP2210_COUNTER_RISING_EDGE << 1;
989					changed_sram = true;
990				}
991			}
992			break;
993		case GPIO_PIN_ALT5:
994			if (b[MCP2210_GPIO_SRAM_GP0 + pin] != MCP2210_PIN_IS_DED) {
995				b[MCP2210_GPIO_SRAM_GP0 + pin] = MCP2210_PIN_IS_DED;
996				changed_sram = true;
997			}
998			if (pin == 6) {
999				if (mcp2210_counter_gp_to_flags(mcp2210_get_gpio_sram_res.other_settings) != GPIO_PIN_ALT5) {
1000					mcp2210_set_gpio_sram_req.other_settings &= 0xf1;
1001					mcp2210_set_gpio_sram_req.other_settings |= MCP2210_COUNTER_LOW_PULSE << 1;
1002					changed_sram = true;
1003				}
1004			}
1005			break;
1006		case GPIO_PIN_ALT6:
1007			if (b[MCP2210_GPIO_SRAM_GP0 + pin] != MCP2210_PIN_IS_DED) {
1008				b[MCP2210_GPIO_SRAM_GP0 + pin] = MCP2210_PIN_IS_DED;
1009				changed_sram = true;
1010			}
1011			if (pin == 6) {
1012				if (mcp2210_counter_gp_to_flags(mcp2210_get_gpio_sram_res.other_settings) != GPIO_PIN_ALT6) {
1013					mcp2210_set_gpio_sram_req.other_settings &= 0xf1;
1014					mcp2210_set_gpio_sram_req.other_settings |= MCP2210_COUNTER_HIGH_PULSE << 1;
1015					changed_sram = true;
1016				}
1017			}
1018			break;
1019		default:
1020			break;
1021		}
1022	}
1023
1024	/* On the MCP-2210, if you change the purpose of the pin you have to
1025	 * write the current direction and value of the pins back to the chip.
1026	 * The reason for this is tha you can not change just one pins purpose
1027	 * without setting the direction and values of all pins to the default. */
1028
1029	if (changed_sram) {
1030		err = mcp2210_set_gpio_sram(sc, &mcp2210_set_gpio_sram_req,
1031		    &mcp2210_set_gpio_sram_res);
1032		if (err)
1033			goto out;
1034
1035		mcp2210_set_gpio_dir_sram_req.pin_dir_msb = (vdir >> 8) & 0xff;
1036		mcp2210_set_gpio_dir_sram_req.pin_dir_lsb = vdir & 0x00ff;
1037
1038		err = mcp2210_set_gpio_dir_sram(sc, &mcp2210_set_gpio_dir_sram_req,
1039		    &mcp2210_set_gpio_dir_sram_res);
1040		if (err)
1041			goto out;
1042
1043		mcp2210_set_gpio_value_sram_req.pin_value_msb = mcp2210_get_gpio_value_sram_res.pin_value_msb;
1044		mcp2210_set_gpio_value_sram_req.pin_value_lsb = mcp2210_get_gpio_value_sram_res.pin_value_lsb;
1045
1046		/* Further, if the pin is for OUTPUT, then we will want to set
1047		 * its value to the default, otherwise the default may never be
1048		 * reflected in the pin state, as the pin might have been a
1049		 * INPUT with the opposite value and just putting all of the
1050		 * values back in that case would do the wrong thing. */
1051
1052		if (flags & GPIO_PIN_OUTPUT && pin < 8) {
1053			if (mcp2210_set_gpio_sram_req.default_output_lsb & (1 << pin))
1054				mcp2210_set_gpio_value_sram_req.pin_value_lsb |= (1 << pin);
1055			else
1056				mcp2210_set_gpio_value_sram_req.pin_value_lsb &= ~(1 << pin);
1057		}
1058		err = mcp2210_set_gpio_value_sram(sc, &mcp2210_set_gpio_value_sram_req, &mcp2210_set_gpio_value_sram_res);
1059	} else {
1060		/* In this case, the pin purpose was not changed, so all that
1061		 * needs to happen is the direction needs to be updated.  This
1062		 * actually won't matter unless the pin is strickly a GPIO pin.
1063		 * ALT0, the CS purpose, is handled by the chip itself, ALT3 -
1064		 * ALT6 is for the event / interrupt counter.  So, we really
1065		 * only have to care if the pin switches from INPUT to OUTPUT,
1066		 * or vise versa. */
1067		if (flags & (GPIO_PIN_OUTPUT | GPIO_PIN_INPUT)) {
1068			mcp2210_set_gpio_dir_sram_req.pin_dir_msb = (vdir >> 8) & 0xff;
1069			mcp2210_set_gpio_dir_sram_req.pin_dir_lsb = vdir & 0x00ff;
1070
1071			err = mcp2210_set_gpio_dir_sram(sc, &mcp2210_set_gpio_dir_sram_req,
1072			    &mcp2210_set_gpio_dir_sram_res);
1073		}
1074	}
1075out:
1076	return err;
1077}
1078/*
1079 * Internal function that does the dirty work of setting a gpio
1080 * pin to its "type" for the MCP-2221 / MCP-2221A
1081 *
1082 * There are really two ways to do some of this, one is to set the pin
1083 * to input and output, or whatever, using SRAM calls, the other is to
1084 * use the GPIO config calls to set input and output and SRAM for
1085 * everything else.  This just uses SRAM for everything.
1086 */
1087
1088int
1089mcp2221_gpio_pin_ctl(void *arg, int pin, int flags)
1090{
1091	struct umcpmio_softc *sc = arg;
1092	struct mcp2221_set_sram_req set_sram_req;
1093	struct mcp2221_set_sram_res set_sram_res;
1094	struct mcp2221_get_sram_res current_sram_res;
1095	struct mcp2221_get_gpio_cfg_res current_gpio_cfg_res;
1096	int err = 0;
1097
1098	if (sc->sc_dying)
1099		return 0;
1100
1101	KASSERT(mutex_owned(&sc->sc_action_mutex));
1102
1103	err = mcp2221_get_sram(sc, &current_sram_res);
1104	if (err)
1105		goto out;
1106
1107	err = mcp2221_get_gpio_cfg(sc, &current_gpio_cfg_res);
1108	if (err)
1109		goto out;
1110
1111	/* You can't just set one pin, you must set all of them, so copy the
1112	 * current settings for the pin we are not messing with.
1113	 *
1114	 * And, yes, of course, if the MCP-2210 is ever supported with this
1115	 * driver, this sort of unrolling will need to be turned into something
1116	 * different, but for now, just unroll as there are only 4 pins to care
1117	 * about.
1118	 *
1119	 * */
1120
1121	memset(&set_sram_req, 0, MCP2221_REQ_BUFFER_SIZE);
1122	switch (pin) {
1123	case 0:
1124		set_sram_req.gp1_settings = current_sram_res.gp1_settings;
1125		set_sram_req.gp2_settings = current_sram_res.gp2_settings;
1126		set_sram_req.gp3_settings = current_sram_res.gp3_settings;
1127		break;
1128	case 1:
1129		set_sram_req.gp0_settings = current_sram_res.gp0_settings;
1130		set_sram_req.gp2_settings = current_sram_res.gp2_settings;
1131		set_sram_req.gp3_settings = current_sram_res.gp3_settings;
1132		break;
1133	case 2:
1134		set_sram_req.gp0_settings = current_sram_res.gp0_settings;
1135		set_sram_req.gp1_settings = current_sram_res.gp1_settings;
1136		set_sram_req.gp3_settings = current_sram_res.gp3_settings;
1137		break;
1138	case 3:
1139		set_sram_req.gp0_settings = current_sram_res.gp0_settings;
1140		set_sram_req.gp1_settings = current_sram_res.gp1_settings;
1141		set_sram_req.gp2_settings = current_sram_res.gp2_settings;
1142		break;
1143	}
1144	mcp2221_set_gpio_designation_sram(&set_sram_req, pin, flags);
1145	mcp2221_set_gpio_dir_sram(&set_sram_req, pin, flags);
1146
1147	/* This part is unfortunate...  if a pin is set to output, the value
1148	 * set on the pin is not mirrored by the chip into SRAM, but the chip
1149	 * will use the value from SRAM to set the value of the pin.  What this
1150	 * means is that we have to learn the value from the GPIO config and
1151	 * make sure it is set properly when updating SRAM. */
1152
1153	if (current_gpio_cfg_res.gp0_pin_dir == MCP2221_GPIO_CFG_DIR_OUTPUT) {
1154		if (current_gpio_cfg_res.gp0_pin_value == 1) {
1155			set_sram_req.gp0_settings |=
1156			    MCP2221_SRAM_GPIO_OUTPUT_HIGH;
1157		} else {
1158			set_sram_req.gp0_settings &=
1159			    ~MCP2221_SRAM_GPIO_OUTPUT_HIGH;
1160		}
1161	}
1162	if (current_gpio_cfg_res.gp1_pin_dir == MCP2221_GPIO_CFG_DIR_OUTPUT) {
1163		if (current_gpio_cfg_res.gp1_pin_value == 1) {
1164			set_sram_req.gp1_settings |=
1165			    MCP2221_SRAM_GPIO_OUTPUT_HIGH;
1166		} else {
1167			set_sram_req.gp1_settings &=
1168			    ~MCP2221_SRAM_GPIO_OUTPUT_HIGH;
1169		}
1170	}
1171	if (current_gpio_cfg_res.gp2_pin_dir == MCP2221_GPIO_CFG_DIR_OUTPUT) {
1172		if (current_gpio_cfg_res.gp2_pin_value == 1) {
1173			set_sram_req.gp2_settings |=
1174			    MCP2221_SRAM_GPIO_OUTPUT_HIGH;
1175		} else {
1176			set_sram_req.gp2_settings &=
1177			    ~MCP2221_SRAM_GPIO_OUTPUT_HIGH;
1178		}
1179	}
1180	if (current_gpio_cfg_res.gp3_pin_dir == MCP2221_GPIO_CFG_DIR_OUTPUT) {
1181		if (current_gpio_cfg_res.gp3_pin_value == 1) {
1182			set_sram_req.gp3_settings |=
1183			    MCP2221_SRAM_GPIO_OUTPUT_HIGH;
1184		} else {
1185			set_sram_req.gp3_settings &=
1186			    ~MCP2221_SRAM_GPIO_OUTPUT_HIGH;
1187		}
1188	}
1189	err = mcp2221_put_sram(sc, &set_sram_req, &set_sram_res);
1190	if (err)
1191		goto out;
1192
1193	umcpmio_dump_buffer(sc->sc_dumpbuffer,
1194	    (uint8_t *) & set_sram_res, MCP2221_RES_BUFFER_SIZE,
1195	    "mcp2221_gpio_pin_ctl set sram buffer copy");
1196	if (set_sram_res.cmd != MCP2221_CMD_SET_SRAM ||
1197	    set_sram_res.completion != MCP2221_CMD_COMPLETE_OK) {
1198		device_printf(sc->sc_dev, "mcp2221_gpio_pin_ctl:"
1199		    " not the command desired, or error: %02x %02x\n",
1200		    set_sram_res.cmd,
1201		    set_sram_res.completion);
1202		err = EIO;
1203		goto out;
1204	}
1205	sc->sc_gpio_pins[pin].pin_flags = flags;
1206	err = 0;
1207
1208out:
1209	return err;
1210}
1211
1212void
1213umcpmio_gpio_pin_ctl(void *arg, int pin, int flags)
1214{
1215	struct umcpmio_softc *sc = arg;
1216
1217	if (sc->sc_dying)
1218		return;
1219
1220	mutex_enter(&sc->sc_action_mutex);
1221	if (sc->sc_chipinfo->usb_id == USB_PRODUCT_MICROCHIP_MCP2210)
1222		mcp2210_gpio_pin_ctl(sc, pin, flags);
1223	if (sc->sc_chipinfo->usb_id == USB_PRODUCT_MICROCHIP_MCP2221)
1224		mcp2221_gpio_pin_ctl(sc, pin, flags);
1225	mutex_exit(&sc->sc_action_mutex);
1226}
1227
1228int
1229mcp2210_get_gp6_counter(struct umcpmio_softc *sc, int *counter,
1230    uint8_t reset)
1231{
1232	struct mcp2210_get_gp6_events_req req;
1233	struct mcp2210_get_gp6_events_res res;
1234	int err = 0;
1235
1236	KASSERT(mutex_owned(&sc->sc_action_mutex));
1237
1238	memset(&req, 0, MCP2210_REQ_BUFFER_SIZE);
1239	req.cmd = MCP2210_CMD_GET_GP6_EVENTS;
1240	req.reset_counter = reset;
1241
1242	umcpmio_dump_buffer(sc->sc_dumpbuffer,
1243	    (uint8_t *) & req, MCP2210_REQ_BUFFER_SIZE,
1244	    "mcp2210_get_gp6_counter req");
1245
1246	err = umcpmio_send_report(sc,
1247	    (uint8_t *) & req, MCP2210_REQ_BUFFER_SIZE,
1248	    (uint8_t *) & res, sc->sc_cv_wait);
1249
1250	umcpmio_dump_buffer(sc->sc_dumpbuffer,
1251	    (uint8_t *) & res, MCP2210_RES_BUFFER_SIZE,
1252	    "mcp2210_get_gp6_counter res");
1253
1254	err = mcp2210_decode_errors(req.cmd, err, res.completion);
1255	if (!err) {
1256		*counter = (res.counter_msb << 8) | res.counter_lsb;
1257	}
1258	return err;
1259}
1260
1261static int
1262umcpmio_extract_gpio_sram(struct umcpmio_softc *sc,
1263    uint8_t *extract)
1264{
1265	int err = 0;
1266	struct mcp2210_get_gpio_sram_res mcp2210_get_gpio_sram_res;
1267	struct mcp2210_get_gpio_dir_res mcp2210_get_gpio_dir_sram_res;
1268	struct mcp2221_get_sram_res mcp2221_get_sram_res;
1269
1270	if (sc->sc_chipinfo->usb_id == USB_PRODUCT_MICROCHIP_MCP2210) {
1271		mutex_enter(&sc->sc_action_mutex);
1272		err = mcp2210_get_gpio_sram(sc, &mcp2210_get_gpio_sram_res);
1273		mutex_exit(&sc->sc_action_mutex);
1274		if (err)
1275			return err;
1276
1277		umcpmio_dump_buffer(sc->sc_dumpbuffer,
1278		    (uint8_t *) & mcp2210_get_gpio_sram_res, MCP2210_RES_BUFFER_SIZE,
1279		    "umcpmio_extract_gpio_sram mcp2210 get gpio sram buffer copy");
1280
1281		extract[0] = mcp2210_get_gpio_sram_res.gp0_designation;
1282		extract[1] = mcp2210_get_gpio_sram_res.gp1_designation;
1283		extract[2] = mcp2210_get_gpio_sram_res.gp2_designation;
1284		extract[3] = mcp2210_get_gpio_sram_res.gp3_designation;
1285		extract[4] = mcp2210_get_gpio_sram_res.gp4_designation;
1286		extract[5] = mcp2210_get_gpio_sram_res.gp5_designation;
1287		extract[6] = mcp2210_get_gpio_sram_res.gp6_designation;
1288		extract[7] = mcp2210_get_gpio_sram_res.gp7_designation;
1289		extract[8] = mcp2210_get_gpio_sram_res.gp8_designation;
1290
1291		mutex_enter(&sc->sc_action_mutex);
1292		err = mcp2210_get_gpio_dir_sram(sc, &mcp2210_get_gpio_dir_sram_res);
1293		mutex_exit(&sc->sc_action_mutex);
1294		if (err)
1295			return err;
1296
1297		umcpmio_dump_buffer(sc->sc_dumpbuffer,
1298		    (uint8_t *) & mcp2210_get_gpio_dir_sram_res, MCP2210_RES_BUFFER_SIZE,
1299		    "umcpmio_extract_gpio_sram mcp2210 get gpio dir sram buffer copy");
1300
1301		extract[9] = mcp2210_get_gpio_dir_sram_res.pin_dir_lsb;
1302		extract[10] = mcp2210_get_gpio_dir_sram_res.pin_dir_msb;
1303		extract[11] = mcp2210_get_gpio_sram_res.other_settings;
1304	}
1305	if (sc->sc_chipinfo->usb_id == USB_PRODUCT_MICROCHIP_MCP2221) {
1306		mutex_enter(&sc->sc_action_mutex);
1307		err = mcp2221_get_sram(sc, &mcp2221_get_sram_res);
1308		mutex_exit(&sc->sc_action_mutex);
1309		if (err)
1310			return err;
1311
1312		umcpmio_dump_buffer(sc->sc_dumpbuffer,
1313		    (uint8_t *) & mcp2221_get_sram_res, MCP2221_RES_BUFFER_SIZE,
1314		    "umcpmio_extract_gpio_sram mcp2221 get sram buffer copy");
1315
1316		extract[0] = mcp2221_get_sram_res.gp0_settings;
1317		extract[1] = mcp2221_get_sram_res.gp1_settings;
1318		extract[2] = mcp2221_get_sram_res.gp2_settings;
1319		extract[3] = mcp2221_get_sram_res.gp3_settings;
1320	}
1321	return err;
1322}
1323
1324void
1325umcpmio_gpio_attach(struct umcpmio_softc *sc)
1326{
1327	int err;
1328	struct gpiobus_attach_args gba;
1329	uint8_t extract[UMCPMIO_MAX_GPIO_PINS + 3];
1330
1331	err = umcpmio_extract_gpio_sram(sc, extract);
1332	if (err) {
1333		aprint_error_dev(sc->sc_dev, "umcpmio_gpio_attach:"
1334		    " extract gpio from sram: err=%d\n",
1335		    err);
1336		return;
1337	}
1338
1339	/* The MCP2221 / MCP2221A has a pin that can have gpio interrupt
1340	 * ability, but there are problems with making use of it as the
1341	 * gpio framework runs with spin locks or hard interrupt level,
1342	 * and you can't call into the USB framework in that state.
1343	 *
1344	 * It is largely the same reason using the umcpmio gpio pins
1345	 * as attachments to gpiopps or gpioow doesn't work.  Spin
1346	 * locks are held there too.
1347	 */
1348	for (int c = 0; c < sc->sc_chipinfo->num_gpio_pins; c++){
1349		sc->sc_gpio_pins[c].pin_num = c;
1350		sc->sc_gpio_pins[c].pin_caps = sc->sc_chipinfo->gpio_pin_ability[c];
1351		if (sc->sc_chipinfo->usb_id == USB_PRODUCT_MICROCHIP_MCP2210) {
1352			sc->sc_gpio_pins[c].pin_flags =
1353			    mcp2210_sram_gpio_to_flags(extract, c);
1354		}
1355		if (sc->sc_chipinfo->usb_id == USB_PRODUCT_MICROCHIP_MCP2221) {
1356			sc->sc_gpio_pins[c].pin_flags =
1357			    mcp2221_sram_gpio_to_flags(extract[c]);
1358		}
1359		sc->sc_gpio_pins[c].pin_intrcaps = 0;
1360		strncpy(sc->sc_gpio_pins[c].pin_defname,
1361		    sc->sc_chipinfo->gpio_names[c],
1362		    strlen(sc->sc_chipinfo->gpio_names[c]) + 1);
1363	}
1364
1365	sc->sc_gpio_gc.gp_cookie = sc;
1366	sc->sc_gpio_gc.gp_pin_read = umcpmio_gpio_pin_read;
1367	sc->sc_gpio_gc.gp_pin_write = umcpmio_gpio_pin_write;
1368	sc->sc_gpio_gc.gp_pin_ctl = umcpmio_gpio_pin_ctl;
1369
1370	gba.gba_gc = &sc->sc_gpio_gc;
1371	gba.gba_pins = sc->sc_gpio_pins;
1372	gba.gba_npins = sc->sc_chipinfo->num_gpio_pins;
1373
1374	sc->sc_gpio_dev = config_found(sc->sc_dev, &gba, gpiobus_print,
1375	    CFARGS(.iattr = "gpiobus"));
1376}
1377