11.1Sbrad/*	$NetBSD: umcpmio_gpio.c,v 1.1 2025/11/29 18:39:14 brad Exp $	*/
21.1Sbrad
31.1Sbrad/*
41.1Sbrad * Copyright (c) 2024, 2025 Brad Spencer <brad@anduin.eldar.org>
51.1Sbrad *
61.1Sbrad * Permission to use, copy, modify, and distribute this software for any
71.1Sbrad * purpose with or without fee is hereby granted, provided that the above
81.1Sbrad * copyright notice and this permission notice appear in all copies.
91.1Sbrad *
101.1Sbrad * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
111.1Sbrad * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
121.1Sbrad * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
131.1Sbrad * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
141.1Sbrad * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
151.1Sbrad * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
161.1Sbrad * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
171.1Sbrad */
181.1Sbrad
191.1Sbrad#include <sys/cdefs.h>
201.1Sbrad__KERNEL_RCSID(0, "$NetBSD: umcpmio_gpio.c,v 1.1 2025/11/29 18:39:14 brad Exp $");
211.1Sbrad
221.1Sbrad#ifdef _KERNEL_OPT
231.1Sbrad#include "opt_usb.h"
241.1Sbrad#endif
251.1Sbrad
261.1Sbrad#include <sys/param.h>
271.1Sbrad#include <sys/types.h>
281.1Sbrad
291.1Sbrad#include <sys/gpio.h>
301.1Sbrad
311.1Sbrad#include <dev/gpio/gpiovar.h>
321.1Sbrad
331.1Sbrad#include <dev/usb/umcpmio.h>
341.1Sbrad#include <dev/usb/umcpmio_hid_reports.h>
351.1Sbrad#include <dev/usb/umcpmio_transport.h>
361.1Sbrad#include <dev/usb/umcpmio_gpio.h>
371.1Sbrad#include <dev/usb/umcpmio_subr.h>
381.1Sbrad
391.1Sbrad#define UMCPMIO_DEBUG 1
401.1Sbrad#ifdef UMCPMIO_DEBUG
411.1Sbrad#define DPRINTF(x)	do { if (umcpmiodebug) printf x; } while (0)
421.1Sbrad#define DPRINTFN(n, x)	do { if (umcpmiodebug > (n)) printf x; } while (0)
431.1Sbradextern int umcpmiodebug;
441.1Sbrad#else
451.1Sbrad#define DPRINTF(x)	__nothing
461.1Sbrad#define DPRINTFN(n, x)	__nothing
471.1Sbrad#endif
481.1Sbrad
491.1Sbrad/* Stuff required to deal with the gpio on the MCP2210 and
501.1Sbrad * MCP2221 / MCP2221A */
511.1Sbrad
521.1Sbrad/* The MCP2210 has more, but simpler, pins than the MCP2221
531.1Sbrad * / MCP2221A.
541.1Sbrad *
551.1Sbrad *
561.1Sbrad * The MCP2221 / MCP2221A does not have symetric behavior with
571.1Sbrad * respect to the get and puts of the SRAM.  This means that you
581.1Sbrad * more or less can't just take the response buffer from a SRAM
591.1Sbrad * get and use it directly as a SRAM put.  The MCP2210 is better
601.1Sbrad * in this respect.
611.1Sbrad */
621.1Sbrad
631.1Sbrad
641.1Sbrad/* We call the dedicated function ALT3 everywhere */
651.1Sbrad
661.1Sbradstatic uint32_t
671.1Sbradmcp2210_counter_gp_to_flags(uint8_t other_settings)
681.1Sbrad{
691.1Sbrad	uint32_t r = 0;
701.1Sbrad
711.1Sbrad	switch ((other_settings >> 1) & 0x07) {
721.1Sbrad	case MCP2210_COUNTER_FALLING_EDGE:
731.1Sbrad		r |= GPIO_PIN_ALT3;
741.1Sbrad		break;
751.1Sbrad	case MCP2210_COUNTER_RISING_EDGE:
761.1Sbrad		r |= GPIO_PIN_ALT4;
771.1Sbrad		break;
781.1Sbrad	case MCP2210_COUNTER_LOW_PULSE:
791.1Sbrad		r |= GPIO_PIN_ALT5;
801.1Sbrad		break;
811.1Sbrad	case MCP2210_COUNTER_HIGH_PULSE:
821.1Sbrad		r |= GPIO_PIN_ALT6;
831.1Sbrad		break;
841.1Sbrad	case MCP2210_COUNTER_OFF:
851.1Sbrad	default:
861.1Sbrad		printf("mcp2210_counter_gp_to_flags: Unhandled flag on counter pin: 0x%02x\n", other_settings);
871.1Sbrad	}
881.1Sbrad
891.1Sbrad	return r;
901.1Sbrad}
911.1Sbrad
921.1Sbradstatic uint32_t
931.1Sbradmcp2210_sram_gpio_to_flags(uint8_t *gp_settings, int pin)
941.1Sbrad{
951.1Sbrad	uint32_t r = 0;
961.1Sbrad
971.1Sbrad	switch (gp_settings[pin]) {
981.1Sbrad	case MCP2210_PIN_IS_ALT0:
991.1Sbrad		r |= GPIO_PIN_ALT0;
1001.1Sbrad		break;
1011.1Sbrad	case MCP2210_PIN_IS_DED:
1021.1Sbrad		if (pin == 6) {
1031.1Sbrad			r |= mcp2210_counter_gp_to_flags(gp_settings[11]);
1041.1Sbrad		} else {
1051.1Sbrad			r |= GPIO_PIN_ALT3;
1061.1Sbrad		}
1071.1Sbrad		break;
1081.1Sbrad	case MCP2210_PIN_IS_GPIO:
1091.1Sbrad	default:
1101.1Sbrad		if (pin < 8) {
1111.1Sbrad			if (gp_settings[9] & (1 << pin))
1121.1Sbrad				r |= GPIO_PIN_INPUT;
1131.1Sbrad			else
1141.1Sbrad				r |= GPIO_PIN_OUTPUT;
1151.1Sbrad		} else {
1161.1Sbrad			r |= GPIO_PIN_INPUT;
1171.1Sbrad		}
1181.1Sbrad		break;
1191.1Sbrad	}
1201.1Sbrad
1211.1Sbrad	return r;
1221.1Sbrad}
1231.1Sbrad
1241.1Sbradstatic uint32_t
1251.1Sbradmcp2221_sram_gpio_to_flags(uint8_t gp_setting)
1261.1Sbrad{
1271.1Sbrad	uint32_t r = 0;
1281.1Sbrad
1291.1Sbrad	switch (gp_setting & MCP2221_SRAM_PIN_TYPE_MASK) {
1301.1Sbrad	case MCP2221_SRAM_PIN_IS_DED:
1311.1Sbrad		r |= GPIO_PIN_ALT3;
1321.1Sbrad		break;
1331.1Sbrad	case MCP2221_SRAM_PIN_IS_ALT0:
1341.1Sbrad		r |= GPIO_PIN_ALT0;
1351.1Sbrad		break;
1361.1Sbrad	case MCP2221_SRAM_PIN_IS_ALT1:
1371.1Sbrad		r |= GPIO_PIN_ALT1;
1381.1Sbrad		break;
1391.1Sbrad	case MCP2221_SRAM_PIN_IS_ALT2:
1401.1Sbrad		r |= GPIO_PIN_ALT2;
1411.1Sbrad		break;
1421.1Sbrad	case MCP2221_SRAM_PIN_IS_GPIO:
1431.1Sbrad	default:
1441.1Sbrad		if ((gp_setting & MCP2221_SRAM_GPIO_TYPE_MASK) ==
1451.1Sbrad		    MCP2221_SRAM_GPIO_INPUT)
1461.1Sbrad			r |= GPIO_PIN_INPUT;
1471.1Sbrad		else
1481.1Sbrad			r |= GPIO_PIN_OUTPUT;
1491.1Sbrad		break;
1501.1Sbrad	}
1511.1Sbrad
1521.1Sbrad	return r;
1531.1Sbrad}
1541.1Sbrad
1551.1Sbradstatic void
1561.1Sbradmcp2221_set_gpio_dir_sram(struct mcp2221_set_sram_req *req, int pin, int flags)
1571.1Sbrad{
1581.1Sbrad	uint8_t *alter = NULL;
1591.1Sbrad	uint8_t *newvalue = NULL;
1601.1Sbrad
1611.1Sbrad	if (pin >= 0 && pin < MCP2221_NPINS) {
1621.1Sbrad		switch (pin) {
1631.1Sbrad		case 0:
1641.1Sbrad			alter = &req->alter_gpio_config;
1651.1Sbrad			newvalue = &req->gp0_settings;
1661.1Sbrad			break;
1671.1Sbrad		case 1:
1681.1Sbrad			alter = &req->alter_gpio_config;
1691.1Sbrad			newvalue = &req->gp1_settings;
1701.1Sbrad			break;
1711.1Sbrad		case 2:
1721.1Sbrad			alter = &req->alter_gpio_config;
1731.1Sbrad			newvalue = &req->gp2_settings;
1741.1Sbrad			break;
1751.1Sbrad		case 3:
1761.1Sbrad			alter = &req->alter_gpio_config;
1771.1Sbrad			newvalue = &req->gp3_settings;
1781.1Sbrad			break;
1791.1Sbrad		default:
1801.1Sbrad			break;
1811.1Sbrad		}
1821.1Sbrad
1831.1Sbrad		if (alter != NULL) {
1841.1Sbrad			*alter = MCP2221_SRAM_ALTER_GPIO;
1851.1Sbrad			if (flags & GPIO_PIN_INPUT)
1861.1Sbrad				*newvalue |= MCP2221_SRAM_GPIO_INPUT;
1871.1Sbrad			else
1881.1Sbrad				*newvalue &= ~MCP2221_SRAM_GPIO_INPUT;
1891.1Sbrad		}
1901.1Sbrad	}
1911.1Sbrad}
1921.1Sbrad
1931.1Sbradstatic int
1941.1Sbradmcp2210_get_gpio_sram(struct umcpmio_softc *sc,
1951.1Sbrad    struct mcp2210_get_gpio_sram_res *res)
1961.1Sbrad{
1971.1Sbrad	struct mcp2210_get_gpio_sram_req req;
1981.1Sbrad	int err = 0;
1991.1Sbrad
2001.1Sbrad	memset(&req, 0, MCP2210_REQ_BUFFER_SIZE);
2011.1Sbrad	req.cmd = MCP2210_CMD_GET_GPIO_SRAM;
2021.1Sbrad
2031.1Sbrad	KASSERT(mutex_owned(&sc->sc_action_mutex));
2041.1Sbrad
2051.1Sbrad	err = umcpmio_send_report(sc,
2061.1Sbrad	    (uint8_t *) & req, MCP2210_REQ_BUFFER_SIZE,
2071.1Sbrad	    (uint8_t *) res, sc->sc_cv_wait);
2081.1Sbrad
2091.1Sbrad	err = mcp2210_decode_errors(req.cmd, err, res->completion);
2101.1Sbrad
2111.1Sbrad	return err;
2121.1Sbrad}
2131.1Sbrad
2141.1Sbradstatic int
2151.1Sbradmcp2210_set_gpio_sram(struct umcpmio_softc *sc,
2161.1Sbrad    struct mcp2210_set_gpio_sram_req *req, struct mcp2210_set_gpio_sram_res *res)
2171.1Sbrad{
2181.1Sbrad	int err = 0;
2191.1Sbrad
2201.1Sbrad	req->cmd = MCP2210_CMD_SET_GPIO_SRAM;
2211.1Sbrad
2221.1Sbrad	KASSERT(mutex_owned(&sc->sc_action_mutex));
2231.1Sbrad
2241.1Sbrad	err = umcpmio_send_report(sc,
2251.1Sbrad	    (uint8_t *) req, MCP2210_REQ_BUFFER_SIZE,
2261.1Sbrad	    (uint8_t *) res, sc->sc_cv_wait);
2271.1Sbrad
2281.1Sbrad	err = mcp2210_decode_errors(req->cmd, err, res->completion);
2291.1Sbrad
2301.1Sbrad	return err;
2311.1Sbrad}
2321.1Sbrad
2331.1Sbradstatic int
2341.1Sbradmcp2210_get_gpio_dir_sram(struct umcpmio_softc *sc,
2351.1Sbrad    struct mcp2210_get_gpio_dir_res *res)
2361.1Sbrad{
2371.1Sbrad	struct mcp2210_get_gpio_dir_req req;
2381.1Sbrad	int err = 0;
2391.1Sbrad
2401.1Sbrad	memset(&req, 0, MCP2210_REQ_BUFFER_SIZE);
2411.1Sbrad	req.cmd = MCP2210_CMD_GET_GPIO_DIR_SRAM;
2421.1Sbrad
2431.1Sbrad	KASSERT(mutex_owned(&sc->sc_action_mutex));
2441.1Sbrad
2451.1Sbrad	err = umcpmio_send_report(sc,
2461.1Sbrad	    (uint8_t *) & req, MCP2210_REQ_BUFFER_SIZE,
2471.1Sbrad	    (uint8_t *) res, sc->sc_cv_wait);
2481.1Sbrad
2491.1Sbrad	err = mcp2210_decode_errors(req.cmd, err, res->completion);
2501.1Sbrad
2511.1Sbrad	return err;
2521.1Sbrad}
2531.1Sbrad
2541.1Sbradstatic int
2551.1Sbradmcp2210_get_gpio_value_sram(struct umcpmio_softc *sc,
2561.1Sbrad    struct mcp2210_get_gpio_value_res *res)
2571.1Sbrad{
2581.1Sbrad	struct mcp2210_get_gpio_value_req req;
2591.1Sbrad	int err = 0;
2601.1Sbrad
2611.1Sbrad	memset(&req, 0, MCP2210_REQ_BUFFER_SIZE);
2621.1Sbrad	req.cmd = MCP2210_CMD_GET_GPIO_VAL_SRAM;
2631.1Sbrad
2641.1Sbrad	KASSERT(mutex_owned(&sc->sc_action_mutex));
2651.1Sbrad
2661.1Sbrad	err = umcpmio_send_report(sc,
2671.1Sbrad	    (uint8_t *) & req, MCP2210_REQ_BUFFER_SIZE,
2681.1Sbrad	    (uint8_t *) res, sc->sc_cv_wait);
2691.1Sbrad
2701.1Sbrad	err = mcp2210_decode_errors(req.cmd, err, res->completion);
2711.1Sbrad
2721.1Sbrad	return err;
2731.1Sbrad}
2741.1Sbrad
2751.1Sbradstatic int
2761.1Sbradmcp2210_set_gpio_dir_sram(struct umcpmio_softc *sc,
2771.1Sbrad    struct mcp2210_set_gpio_dir_req *req, struct mcp2210_set_gpio_dir_res *res)
2781.1Sbrad{
2791.1Sbrad	int err = 0;
2801.1Sbrad
2811.1Sbrad	req->cmd = MCP2210_CMD_SET_GPIO_DIR_SRAM;
2821.1Sbrad
2831.1Sbrad	KASSERT(mutex_owned(&sc->sc_action_mutex));
2841.1Sbrad
2851.1Sbrad	err = umcpmio_send_report(sc,
2861.1Sbrad	    (uint8_t *) req, MCP2210_REQ_BUFFER_SIZE,
2871.1Sbrad	    (uint8_t *) res, sc->sc_cv_wait);
2881.1Sbrad
2891.1Sbrad	err = mcp2210_decode_errors(req->cmd, err, res->completion);
2901.1Sbrad
2911.1Sbrad	return err;
2921.1Sbrad}
2931.1Sbrad
2941.1Sbradstatic int
2951.1Sbradmcp2210_set_gpio_value_sram(struct umcpmio_softc *sc,
2961.1Sbrad    struct mcp2210_set_gpio_value_req *req, struct mcp2210_set_gpio_value_res *res)
2971.1Sbrad{
2981.1Sbrad	int err = 0;
2991.1Sbrad
3001.1Sbrad	req->cmd = MCP2210_CMD_SET_GPIO_VAL_SRAM;
3011.1Sbrad
3021.1Sbrad	KASSERT(mutex_owned(&sc->sc_action_mutex));
3031.1Sbrad
3041.1Sbrad	err = umcpmio_send_report(sc,
3051.1Sbrad	    (uint8_t *) req, MCP2210_REQ_BUFFER_SIZE,
3061.1Sbrad	    (uint8_t *) res, sc->sc_cv_wait);
3071.1Sbrad
3081.1Sbrad	err = mcp2210_decode_errors(req->cmd, err, res->completion);
3091.1Sbrad
3101.1Sbrad	return err;
3111.1Sbrad}
3121.1Sbrad
3131.1Sbradstatic void
3141.1Sbradmcp2221_set_gpio_designation_sram(struct mcp2221_set_sram_req *req, int pin,
3151.1Sbrad    int flags)
3161.1Sbrad{
3171.1Sbrad	uint8_t *alter = NULL;
3181.1Sbrad	uint8_t *newvalue = NULL;
3191.1Sbrad	uint32_t altmask =
3201.1Sbrad	GPIO_PIN_ALT0 | GPIO_PIN_ALT1 | GPIO_PIN_ALT2 | GPIO_PIN_ALT3;
3211.1Sbrad
3221.1Sbrad	if (pin >= 0 && pin < MCP2221_NPINS) {
3231.1Sbrad		switch (pin) {
3241.1Sbrad		case 0:
3251.1Sbrad			alter = &req->alter_gpio_config;
3261.1Sbrad			newvalue = &req->gp0_settings;
3271.1Sbrad			break;
3281.1Sbrad		case 1:
3291.1Sbrad			alter = &req->alter_gpio_config;
3301.1Sbrad			newvalue = &req->gp1_settings;
3311.1Sbrad			break;
3321.1Sbrad		case 2:
3331.1Sbrad			alter = &req->alter_gpio_config;
3341.1Sbrad			newvalue = &req->gp2_settings;
3351.1Sbrad			break;
3361.1Sbrad		case 3:
3371.1Sbrad			alter = &req->alter_gpio_config;
3381.1Sbrad			newvalue = &req->gp3_settings;
3391.1Sbrad			break;
3401.1Sbrad		default:
3411.1Sbrad			break;
3421.1Sbrad		}
3431.1Sbrad
3441.1Sbrad		if (alter != NULL) {
3451.1Sbrad			int nv = *newvalue;
3461.1Sbrad
3471.1Sbrad			*alter = MCP2221_SRAM_ALTER_GPIO;
3481.1Sbrad			nv &= 0xF8;
3491.1Sbrad
3501.1Sbrad			if (flags & (GPIO_PIN_OUTPUT | GPIO_PIN_INPUT)) {
3511.1Sbrad				nv |= MCP2221_SRAM_PIN_IS_GPIO;
3521.1Sbrad			} else {
3531.1Sbrad				switch (flags & altmask) {
3541.1Sbrad				case GPIO_PIN_ALT0:
3551.1Sbrad					nv |= MCP2221_SRAM_PIN_IS_ALT0;
3561.1Sbrad					break;
3571.1Sbrad				case GPIO_PIN_ALT1:
3581.1Sbrad					nv |= MCP2221_SRAM_PIN_IS_ALT1;
3591.1Sbrad					break;
3601.1Sbrad				case GPIO_PIN_ALT2:
3611.1Sbrad					nv |= MCP2221_SRAM_PIN_IS_ALT2;
3621.1Sbrad					break;
3631.1Sbrad					/* ALT3 will always be used as the
3641.1Sbrad					 * dedicated function specific to the
3651.1Sbrad					 * pin.  Not all of the pins will have
3661.1Sbrad					 * the alt functions below #3. */
3671.1Sbrad				case GPIO_PIN_ALT3:
3681.1Sbrad					nv |= MCP2221_SRAM_PIN_IS_DED;
3691.1Sbrad					break;
3701.1Sbrad				default:
3711.1Sbrad					break;
3721.1Sbrad				}
3731.1Sbrad			}
3741.1Sbrad			*newvalue = nv;
3751.1Sbrad		}
3761.1Sbrad	}
3771.1Sbrad}
3781.1Sbrad/*
3791.1Sbrad * It is unfortunate that the GET and PUT requests are not symertric.  That is,
3801.1Sbrad * the bits sort of line up but not quite between a GET and PUT.
3811.1Sbrad */
3821.1Sbrad
3831.1Sbradstatic struct umcpmio_mapping_put mcp2221_vref_puts[] = {
3841.1Sbrad	{
3851.1Sbrad		.tname = "4.096V",
3861.1Sbrad		.mask = 0x06 | 0x01,
3871.1Sbrad	},
3881.1Sbrad	{
3891.1Sbrad		.tname = "2.048V",
3901.1Sbrad		.mask = 0x04 | 0x01,
3911.1Sbrad	},
3921.1Sbrad	{
3931.1Sbrad		.tname = "1.024V",
3941.1Sbrad		.mask = 0x02 | 0x01,
3951.1Sbrad	},
3961.1Sbrad	{
3971.1Sbrad		.tname = "OFF",
3981.1Sbrad		.mask = 0x00 | 0x01,
3991.1Sbrad	},
4001.1Sbrad	{
4011.1Sbrad		.tname = "VDD",
4021.1Sbrad		.mask = 0x00,
4031.1Sbrad	}
4041.1Sbrad};
4051.1Sbrad
4061.1Sbradvoid
4071.1Sbradmcp2221_set_dac_vref(struct mcp2221_set_sram_req *req, char *newvref)
4081.1Sbrad{
4091.1Sbrad	int i;
4101.1Sbrad
4111.1Sbrad	for (i = 0; i < __arraycount(mcp2221_vref_puts); i++) {
4121.1Sbrad		if (strncmp(newvref, mcp2221_vref_puts[i].tname,
4131.1Sbrad		    MCP2221_VREF_NAME) == 0) {
4141.1Sbrad			break;
4151.1Sbrad		}
4161.1Sbrad	}
4171.1Sbrad
4181.1Sbrad	if (i == __arraycount(mcp2221_vref_puts))
4191.1Sbrad		return;
4201.1Sbrad
4211.1Sbrad	req->dac_voltage_reference |= mcp2221_vref_puts[i].mask |
4221.1Sbrad	    MCP2221_SRAM_CHANGE_DAC_VREF;
4231.1Sbrad}
4241.1Sbrad
4251.1Sbradint
4261.1Sbradmcp2221_set_dac_vref_one(struct umcpmio_softc *sc, char *newvref)
4271.1Sbrad{
4281.1Sbrad	struct mcp2221_set_sram_req req;
4291.1Sbrad	struct mcp2221_set_sram_res res;
4301.1Sbrad	int err = 0;
4311.1Sbrad
4321.1Sbrad	KASSERT(mutex_owned(&sc->sc_action_mutex));
4331.1Sbrad
4341.1Sbrad	memset(&req, 0, MCP2221_REQ_BUFFER_SIZE);
4351.1Sbrad	mcp2221_set_dac_vref(&req, newvref);
4361.1Sbrad	err = mcp2221_put_sram(sc, &req, &res);
4371.1Sbrad
4381.1Sbrad	return err;
4391.1Sbrad}
4401.1Sbrad
4411.1Sbradvoid
4421.1Sbradmcp2221_set_dac_value(struct mcp2221_set_sram_req *req, uint8_t newvalue)
4431.1Sbrad{
4441.1Sbrad	req->set_dac_output_value |= (newvalue & MCP2221_SRAM_DAC_VALUE_MASK) |
4451.1Sbrad	    MCP2221_SRAM_CHANGE_DAC_VREF;
4461.1Sbrad}
4471.1Sbrad
4481.1Sbradint
4491.1Sbradmcp2221_set_dac_value_one(struct umcpmio_softc *sc, uint8_t newvalue)
4501.1Sbrad{
4511.1Sbrad	struct mcp2221_set_sram_req req;
4521.1Sbrad	struct mcp2221_set_sram_res res;
4531.1Sbrad	int err = 0;
4541.1Sbrad
4551.1Sbrad	KASSERT(mutex_owned(&sc->sc_action_mutex));
4561.1Sbrad
4571.1Sbrad	memset(&req, 0, MCP2221_REQ_BUFFER_SIZE);
4581.1Sbrad	mcp2221_set_dac_value(&req, newvalue);
4591.1Sbrad	err = mcp2221_put_sram(sc, &req, &res);
4601.1Sbrad
4611.1Sbrad	return err;
4621.1Sbrad}
4631.1Sbrad
4641.1Sbradvoid
4651.1Sbradmcp2221_set_adc_vref(struct mcp2221_set_sram_req *req, char *newvref)
4661.1Sbrad{
4671.1Sbrad	int i;
4681.1Sbrad
4691.1Sbrad	for (i = 0; i < __arraycount(mcp2221_vref_puts); i++) {
4701.1Sbrad		if (strncmp(newvref, mcp2221_vref_puts[i].tname,
4711.1Sbrad		    MCP2221_VREF_NAME) == 0) {
4721.1Sbrad			break;
4731.1Sbrad		}
4741.1Sbrad	}
4751.1Sbrad
4761.1Sbrad	if (i == __arraycount(mcp2221_vref_puts))
4771.1Sbrad		return;
4781.1Sbrad
4791.1Sbrad	req->adc_voltage_reference |= mcp2221_vref_puts[i].mask |
4801.1Sbrad	    MCP2221_SRAM_CHANGE_ADC_VREF;
4811.1Sbrad}
4821.1Sbrad
4831.1Sbradint
4841.1Sbradmcp2221_set_adc_vref_one(struct umcpmio_softc *sc, char *newvref)
4851.1Sbrad{
4861.1Sbrad	struct mcp2221_set_sram_req req;
4871.1Sbrad	struct mcp2221_set_sram_res res;
4881.1Sbrad	int err = 0;
4891.1Sbrad
4901.1Sbrad	KASSERT(mutex_owned(&sc->sc_action_mutex));
4911.1Sbrad
4921.1Sbrad	memset(&req, 0, MCP2221_REQ_BUFFER_SIZE);
4931.1Sbrad	mcp2221_set_adc_vref(&req, newvref);
4941.1Sbrad	err = mcp2221_put_sram(sc, &req, &res);
4951.1Sbrad
4961.1Sbrad	return err;
4971.1Sbrad}
4981.1Sbrad
4991.1Sbradstatic struct umcpmio_mapping_put mcp2221_dc_puts[] = {
5001.1Sbrad	{
5011.1Sbrad		.tname = "75%",
5021.1Sbrad		.mask = MCP2221_SRAM_GPIO_CLOCK_DC_75,
5031.1Sbrad	},
5041.1Sbrad	{
5051.1Sbrad		.tname = "50%",
5061.1Sbrad		.mask = MCP2221_SRAM_GPIO_CLOCK_DC_50,
5071.1Sbrad	},
5081.1Sbrad	{
5091.1Sbrad		.tname = "25%",
5101.1Sbrad		.mask = MCP2221_SRAM_GPIO_CLOCK_DC_25,
5111.1Sbrad	},
5121.1Sbrad	{
5131.1Sbrad		.tname = "0%",
5141.1Sbrad		.mask = MCP2221_SRAM_GPIO_CLOCK_DC_0,
5151.1Sbrad	}
5161.1Sbrad};
5171.1Sbrad
5181.1Sbradvoid
5191.1Sbradmcp2221_set_gpioclock_dc(struct mcp2221_set_sram_req *req, char *new_dc)
5201.1Sbrad{
5211.1Sbrad	int i;
5221.1Sbrad
5231.1Sbrad	for (i = 0; i < __arraycount(mcp2221_dc_puts); i++) {
5241.1Sbrad		if (strncmp(new_dc, mcp2221_dc_puts[i].tname,
5251.1Sbrad		    MCP2221_DC_NAME) == 0) {
5261.1Sbrad			break;
5271.1Sbrad		}
5281.1Sbrad	}
5291.1Sbrad
5301.1Sbrad	if (i == __arraycount(mcp2221_dc_puts))
5311.1Sbrad		return;
5321.1Sbrad
5331.1Sbrad	req->clock_output_divider |= mcp2221_dc_puts[i].mask;
5341.1Sbrad}
5351.1Sbrad
5361.1Sbradint
5371.1Sbradmcp2221_set_gpioclock_dc_one(struct umcpmio_softc *sc, char *new_dutycycle)
5381.1Sbrad{
5391.1Sbrad	struct mcp2221_get_sram_res current_sram_res;
5401.1Sbrad	struct mcp2221_set_sram_req req;
5411.1Sbrad	struct mcp2221_set_sram_res res;
5421.1Sbrad	int err = 0;
5431.1Sbrad
5441.1Sbrad	KASSERT(mutex_owned(&sc->sc_action_mutex));
5451.1Sbrad
5461.1Sbrad	err = mcp2221_get_sram(sc, &current_sram_res);
5471.1Sbrad	if (err)
5481.1Sbrad		goto out;
5491.1Sbrad
5501.1Sbrad	memset(&req, 0, MCP2221_REQ_BUFFER_SIZE);
5511.1Sbrad	mcp2221_set_gpioclock_dc(&req, new_dutycycle);
5521.1Sbrad	DPRINTF(("mcp2221_set_gpioclock_dc_one:"
5531.1Sbrad	    " req.clock_output_divider=%02x, current mask=%02x\n",
5541.1Sbrad	    req.clock_output_divider,
5551.1Sbrad	    (current_sram_res.clock_divider &
5561.1Sbrad	    MCP2221_SRAM_GPIO_CLOCK_CD_MASK)));
5571.1Sbrad	req.clock_output_divider |=
5581.1Sbrad	    (current_sram_res.clock_divider &
5591.1Sbrad	    MCP2221_SRAM_GPIO_CLOCK_CD_MASK) |
5601.1Sbrad	    MCP2221_SRAM_GPIO_CHANGE_DCCD;
5611.1Sbrad	DPRINTF(("mcp2221_set_gpioclock_dc_one:"
5621.1Sbrad	    " SET req.clock_output_divider=%02x\n",
5631.1Sbrad	    req.clock_output_divider));
5641.1Sbrad	err = mcp2221_put_sram(sc, &req, &res);
5651.1Sbradout:
5661.1Sbrad	return err;
5671.1Sbrad}
5681.1Sbrad
5691.1Sbradstatic struct umcpmio_mapping_put mcp2221_cd_puts[] = {
5701.1Sbrad	{
5711.1Sbrad		.tname = "375kHz",
5721.1Sbrad		.mask = MCP2221_SRAM_GPIO_CLOCK_CD_375KHZ,
5731.1Sbrad	},
5741.1Sbrad	{
5751.1Sbrad		.tname = "750kHz",
5761.1Sbrad		.mask = MCP2221_SRAM_GPIO_CLOCK_CD_750KHZ,
5771.1Sbrad	},
5781.1Sbrad	{
5791.1Sbrad		.tname = "1.5MHz",
5801.1Sbrad		.mask = MCP2221_SRAM_GPIO_CLOCK_CD_1P5MHZ,
5811.1Sbrad	},
5821.1Sbrad	{
5831.1Sbrad		.tname = "3MHz",
5841.1Sbrad		.mask = MCP2221_SRAM_GPIO_CLOCK_CD_3MHZ,
5851.1Sbrad	},
5861.1Sbrad	{
5871.1Sbrad		.tname = "6MHz",
5881.1Sbrad		.mask = MCP2221_SRAM_GPIO_CLOCK_CD_6MHZ,
5891.1Sbrad	},
5901.1Sbrad	{
5911.1Sbrad		.tname = "12MHz",
5921.1Sbrad		.mask = MCP2221_SRAM_GPIO_CLOCK_CD_12MHZ,
5931.1Sbrad	},
5941.1Sbrad	{
5951.1Sbrad		.tname = "24MHz",
5961.1Sbrad		.mask = MCP2221_SRAM_GPIO_CLOCK_CD_24MHZ,
5971.1Sbrad	}
5981.1Sbrad};
5991.1Sbrad
6001.1Sbradvoid
6011.1Sbradmcp2221_set_gpioclock_cd(struct mcp2221_set_sram_req *req, char *new_cd)
6021.1Sbrad{
6031.1Sbrad	int i;
6041.1Sbrad
6051.1Sbrad	for (i = 0; i < __arraycount(mcp2221_cd_puts); i++) {
6061.1Sbrad		if (strncmp(new_cd, mcp2221_cd_puts[i].tname,
6071.1Sbrad		    MCP2221_CD_NAME) == 0) {
6081.1Sbrad			break;
6091.1Sbrad		}
6101.1Sbrad	}
6111.1Sbrad
6121.1Sbrad	if (i == __arraycount(mcp2221_cd_puts))
6131.1Sbrad		return;
6141.1Sbrad
6151.1Sbrad	req->clock_output_divider |= mcp2221_cd_puts[i].mask;
6161.1Sbrad}
6171.1Sbrad
6181.1Sbradint
6191.1Sbradmcp2221_set_gpioclock_cd_one(struct umcpmio_softc *sc, char *new_clockdivider)
6201.1Sbrad{
6211.1Sbrad	struct mcp2221_get_sram_res current_sram_res;
6221.1Sbrad	struct mcp2221_set_sram_req req;
6231.1Sbrad	struct mcp2221_set_sram_res res;
6241.1Sbrad	int err = 0;
6251.1Sbrad
6261.1Sbrad	KASSERT(mutex_owned(&sc->sc_action_mutex));
6271.1Sbrad
6281.1Sbrad	err = mcp2221_get_sram(sc, &current_sram_res);
6291.1Sbrad	if (err)
6301.1Sbrad		goto out;
6311.1Sbrad
6321.1Sbrad	memset(&req, 0, MCP2221_REQ_BUFFER_SIZE);
6331.1Sbrad	mcp2221_set_gpioclock_cd(&req, new_clockdivider);
6341.1Sbrad	DPRINTF(("mcp2221_set_gpioclock_cd_one:"
6351.1Sbrad	    " req.clock_output_divider=%02x, current mask=%02x\n",
6361.1Sbrad	    req.clock_output_divider,
6371.1Sbrad	    (current_sram_res.clock_divider &
6381.1Sbrad	    MCP2221_SRAM_GPIO_CLOCK_CD_MASK)));
6391.1Sbrad	req.clock_output_divider |=
6401.1Sbrad	    (current_sram_res.clock_divider &
6411.1Sbrad	    MCP2221_SRAM_GPIO_CLOCK_DC_MASK) |
6421.1Sbrad	    MCP2221_SRAM_GPIO_CHANGE_DCCD;
6431.1Sbrad	DPRINTF(("mcp2221_set_gpioclock_cd_one:"
6441.1Sbrad	    " SET req.clock_output_divider=%02x\n",
6451.1Sbrad	    req.clock_output_divider));
6461.1Sbrad	err = mcp2221_put_sram(sc, &req, &res);
6471.1Sbradout:
6481.1Sbrad	return err;
6491.1Sbrad}
6501.1Sbrad
6511.1Sbradint
6521.1Sbradmcp2221_get_gpio_cfg(struct umcpmio_softc *sc,
6531.1Sbrad    struct mcp2221_get_gpio_cfg_res *res)
6541.1Sbrad{
6551.1Sbrad	struct mcp2221_get_gpio_cfg_req req;
6561.1Sbrad	int err = 0;
6571.1Sbrad
6581.1Sbrad	memset(&req, 0, MCP2221_REQ_BUFFER_SIZE);
6591.1Sbrad	req.cmd = MCP2221_CMD_GET_GPIO_CFG;
6601.1Sbrad
6611.1Sbrad	KASSERT(mutex_owned(&sc->sc_action_mutex));
6621.1Sbrad
6631.1Sbrad	err = umcpmio_send_report(sc,
6641.1Sbrad	    (uint8_t *) & req, MCP2221_REQ_BUFFER_SIZE,
6651.1Sbrad	    (uint8_t *) res, sc->sc_cv_wait);
6661.1Sbrad
6671.1Sbrad	return err;
6681.1Sbrad}
6691.1Sbrad
6701.1Sbradstatic int
6711.1Sbradmcp2221_put_gpio_cfg(struct umcpmio_softc *sc,
6721.1Sbrad    struct mcp2221_set_gpio_cfg_req *req, struct mcp2221_set_gpio_cfg_res *res)
6731.1Sbrad{
6741.1Sbrad	int err = 0;
6751.1Sbrad
6761.1Sbrad	req->cmd = MCP2221_CMD_SET_GPIO_CFG;
6771.1Sbrad
6781.1Sbrad	KASSERT(mutex_owned(&sc->sc_action_mutex));
6791.1Sbrad
6801.1Sbrad	err = umcpmio_send_report(sc,
6811.1Sbrad	    (uint8_t *) req, MCP2221_REQ_BUFFER_SIZE,
6821.1Sbrad	    (uint8_t *) res, sc->sc_cv_wait);
6831.1Sbrad
6841.1Sbrad	return err;
6851.1Sbrad}
6861.1Sbrad
6871.1Sbradstatic int
6881.1Sbradmcp2210_get_gpio_value(struct umcpmio_softc *sc,
6891.1Sbrad    int pin)
6901.1Sbrad{
6911.1Sbrad	struct mcp2210_get_gpio_value_res res;
6921.1Sbrad	uint16_t v;
6931.1Sbrad	int err = 0;
6941.1Sbrad	int r = GPIO_PIN_LOW;
6951.1Sbrad
6961.1Sbrad
6971.1Sbrad	KASSERT(mutex_owned(&sc->sc_action_mutex));
6981.1Sbrad
6991.1Sbrad	err = mcp2210_get_gpio_value_sram(sc, &res);
7001.1Sbrad
7011.1Sbrad	if (!err) {
7021.1Sbrad		v = (res.pin_value_msb << 8) | res.pin_value_lsb;
7031.1Sbrad		if (v & (1 << pin))
7041.1Sbrad			r = GPIO_PIN_HIGH;
7051.1Sbrad	}
7061.1Sbrad	return r;
7071.1Sbrad}
7081.1Sbrad/* So... if the pin isn't set to GPIO, just call the output LOW */
7091.1Sbrad
7101.1Sbradstatic int
7111.1Sbradmcp2221_get_gpio_value(struct umcpmio_softc *sc,
7121.1Sbrad    int pin)
7131.1Sbrad{
7141.1Sbrad	struct mcp2221_get_gpio_cfg_res get_gpio_cfg_res;
7151.1Sbrad	int err = 0;
7161.1Sbrad	int r = GPIO_PIN_LOW;
7171.1Sbrad
7181.1Sbrad	KASSERT(mutex_owned(&sc->sc_action_mutex));
7191.1Sbrad
7201.1Sbrad	err = mcp2221_get_gpio_cfg(sc, &get_gpio_cfg_res);
7211.1Sbrad	if (err)
7221.1Sbrad		goto out;
7231.1Sbrad
7241.1Sbrad	if (get_gpio_cfg_res.cmd != MCP2221_CMD_GET_GPIO_CFG ||
7251.1Sbrad	    get_gpio_cfg_res.completion != MCP2221_CMD_COMPLETE_OK) {
7261.1Sbrad		device_printf(sc->sc_dev, "mcp2221_get_gpio_value:"
7271.1Sbrad		    " wrong command or error: %02x %02x\n",
7281.1Sbrad		    get_gpio_cfg_res.cmd,
7291.1Sbrad		    get_gpio_cfg_res.completion);
7301.1Sbrad		goto out;
7311.1Sbrad	}
7321.1Sbrad	switch (pin) {
7331.1Sbrad	case 0:
7341.1Sbrad		if (get_gpio_cfg_res.gp0_pin_value !=
7351.1Sbrad		    MCP2221_GPIO_CFG_VALUE_NOT_GPIO)
7361.1Sbrad			if (get_gpio_cfg_res.gp0_pin_value == 0x01)
7371.1Sbrad				r = GPIO_PIN_HIGH;
7381.1Sbrad		break;
7391.1Sbrad	case 1:
7401.1Sbrad		if (get_gpio_cfg_res.gp1_pin_value !=
7411.1Sbrad		    MCP2221_GPIO_CFG_VALUE_NOT_GPIO)
7421.1Sbrad			if (get_gpio_cfg_res.gp1_pin_value == 0x01)
7431.1Sbrad				r = GPIO_PIN_HIGH;
7441.1Sbrad		break;
7451.1Sbrad	case 2:
7461.1Sbrad		if (get_gpio_cfg_res.gp2_pin_value !=
7471.1Sbrad		    MCP2221_GPIO_CFG_VALUE_NOT_GPIO)
7481.1Sbrad			if (get_gpio_cfg_res.gp2_pin_value == 0x01)
7491.1Sbrad				r = GPIO_PIN_HIGH;
7501.1Sbrad		break;
7511.1Sbrad	case 3:
7521.1Sbrad		if (get_gpio_cfg_res.gp3_pin_value !=
7531.1Sbrad		    MCP2221_GPIO_CFG_VALUE_NOT_GPIO)
7541.1Sbrad			if (get_gpio_cfg_res.gp3_pin_value == 0x01)
7551.1Sbrad				r = GPIO_PIN_HIGH;
7561.1Sbrad		break;
7571.1Sbrad	default:
7581.1Sbrad		break;
7591.1Sbrad	}
7601.1Sbradout:
7611.1Sbrad	return r;
7621.1Sbrad}
7631.1Sbrad
7641.1Sbradstatic int
7651.1Sbradmcp2210_set_gpio_value(struct umcpmio_softc *sc,
7661.1Sbrad    int pin, bool value)
7671.1Sbrad{
7681.1Sbrad	int err = 0;
7691.1Sbrad	struct mcp2210_get_gpio_value_res get_res;
7701.1Sbrad	struct mcp2210_set_gpio_value_req set_req;
7711.1Sbrad	struct mcp2210_set_gpio_value_res set_res;
7721.1Sbrad	uint16_t v;
7731.1Sbrad
7741.1Sbrad	KASSERT(mutex_owned(&sc->sc_action_mutex));
7751.1Sbrad
7761.1Sbrad	err = mcp2210_get_gpio_value_sram(sc, &get_res);
7771.1Sbrad	if (!err && get_res.completion != MCP2210_CMD_COMPLETE_OK)
7781.1Sbrad		err = EIO;
7791.1Sbrad
7801.1Sbrad	if (!err) {
7811.1Sbrad		v = (get_res.pin_value_msb << 8) | get_res.pin_value_lsb;
7821.1Sbrad
7831.1Sbrad		if (value)
7841.1Sbrad			v |= (1 << pin);
7851.1Sbrad		else
7861.1Sbrad			v &= ~(1 << pin);
7871.1Sbrad
7881.1Sbrad		set_req.pin_value_lsb = v & 0x00ff;
7891.1Sbrad		set_req.pin_value_msb = (v & 0xff00) >> 8;
7901.1Sbrad
7911.1Sbrad		err = mcp2210_set_gpio_value_sram(sc, &set_req, &set_res);
7921.1Sbrad	}
7931.1Sbrad	return err;
7941.1Sbrad}
7951.1Sbrad
7961.1Sbradstatic void
7971.1Sbradmcp2221_set_gpio_value(struct mcp2221_set_gpio_cfg_req *req,
7981.1Sbrad    int pin, bool value)
7991.1Sbrad{
8001.1Sbrad	uint8_t *alter = NULL;
8011.1Sbrad	uint8_t *newvalue = NULL;
8021.1Sbrad
8031.1Sbrad	if (pin < 0 || pin >= MCP2221_NPINS)
8041.1Sbrad		return;
8051.1Sbrad
8061.1Sbrad	switch (pin) {
8071.1Sbrad	case 0:
8081.1Sbrad		alter = &req->alter_gp0_value;
8091.1Sbrad		newvalue = &req->new_gp0_value;
8101.1Sbrad		break;
8111.1Sbrad	case 1:
8121.1Sbrad		alter = &req->alter_gp1_value;
8131.1Sbrad		newvalue = &req->new_gp1_value;
8141.1Sbrad		break;
8151.1Sbrad	case 2:
8161.1Sbrad		alter = &req->alter_gp2_value;
8171.1Sbrad		newvalue = &req->new_gp2_value;
8181.1Sbrad		break;
8191.1Sbrad	case 3:
8201.1Sbrad		alter = &req->alter_gp3_value;
8211.1Sbrad		newvalue = &req->new_gp3_value;
8221.1Sbrad		break;
8231.1Sbrad	default:
8241.1Sbrad		return;
8251.1Sbrad	}
8261.1Sbrad
8271.1Sbrad	*alter = MCP2221_GPIO_CFG_ALTER;
8281.1Sbrad	*newvalue = 0;
8291.1Sbrad	if (value)
8301.1Sbrad		*newvalue = 1;
8311.1Sbrad}
8321.1Sbrad
8331.1Sbradstatic int
8341.1Sbradmcp2221_set_gpio_value_one(struct umcpmio_softc *sc,
8351.1Sbrad    int pin, bool value)
8361.1Sbrad{
8371.1Sbrad	int err = 0;
8381.1Sbrad	struct mcp2221_set_gpio_cfg_req req;
8391.1Sbrad	struct mcp2221_set_gpio_cfg_res res;
8401.1Sbrad
8411.1Sbrad	KASSERT(mutex_owned(&sc->sc_action_mutex));
8421.1Sbrad
8431.1Sbrad	memset(&req, 0, MCP2221_REQ_BUFFER_SIZE);
8441.1Sbrad	mcp2221_set_gpio_value(&req, pin, value);
8451.1Sbrad	err = mcp2221_put_gpio_cfg(sc, &req, &res);
8461.1Sbrad	if (err)
8471.1Sbrad		goto out;
8481.1Sbrad	if (res.cmd != MCP2221_CMD_SET_GPIO_CFG ||
8491.1Sbrad	    res.completion != MCP2221_CMD_COMPLETE_OK) {
8501.1Sbrad		err = EIO;
8511.1Sbrad		device_printf(sc->sc_dev, "umcpmio_gpio_pin_write:"
8521.1Sbrad		    "  not the command desired, or error: %02x %02x\n",
8531.1Sbrad		    res.cmd,
8541.1Sbrad		    res.completion);
8551.1Sbrad	}
8561.1Sbradout:
8571.1Sbrad	return err;
8581.1Sbrad}
8591.1Sbrad
8601.1Sbrad/* These are standard gpio reads and set calls */
8611.1Sbrad
8621.1Sbradstatic int
8631.1Sbradumcpmio_gpio_pin_read(void *arg, int pin)
8641.1Sbrad{
8651.1Sbrad	struct umcpmio_softc *sc = arg;
8661.1Sbrad	int r = GPIO_PIN_LOW;
8671.1Sbrad
8681.1Sbrad	mutex_enter(&sc->sc_action_mutex);
8691.1Sbrad
8701.1Sbrad	if (sc->sc_chipinfo->usb_id == USB_PRODUCT_MICROCHIP_MCP2210)
8711.1Sbrad		r = mcp2210_get_gpio_value(sc, pin);
8721.1Sbrad	if (sc->sc_chipinfo->usb_id == USB_PRODUCT_MICROCHIP_MCP2221)
8731.1Sbrad		r = mcp2221_get_gpio_value(sc, pin);
8741.1Sbrad
8751.1Sbrad	mutex_exit(&sc->sc_action_mutex);
8761.1Sbrad
8771.1Sbrad	return r;
8781.1Sbrad}
8791.1Sbrad
8801.1Sbradstatic void
8811.1Sbradumcpmio_gpio_pin_write(void *arg, int pin, int value)
8821.1Sbrad{
8831.1Sbrad	struct umcpmio_softc *sc = arg;
8841.1Sbrad
8851.1Sbrad	mutex_enter(&sc->sc_action_mutex);
8861.1Sbrad
8871.1Sbrad	if (sc->sc_chipinfo->usb_id == USB_PRODUCT_MICROCHIP_MCP2210)
8881.1Sbrad		mcp2210_set_gpio_value(sc, pin, value);
8891.1Sbrad	if (sc->sc_chipinfo->usb_id == USB_PRODUCT_MICROCHIP_MCP2221)
8901.1Sbrad		mcp2221_set_gpio_value_one(sc, pin, value);
8911.1Sbrad
8921.1Sbrad	mutex_exit(&sc->sc_action_mutex);
8931.1Sbrad}
8941.1Sbrad
8951.1Sbradstatic int
8961.1Sbradmcp2210_gpio_pin_ctl(void *arg, int pin, int flags)
8971.1Sbrad{
8981.1Sbrad	struct umcpmio_softc *sc = arg;
8991.1Sbrad	struct mcp2210_set_gpio_sram_req mcp2210_set_gpio_sram_req;
9001.1Sbrad	struct mcp2210_set_gpio_dir_req mcp2210_set_gpio_dir_sram_req;
9011.1Sbrad	struct mcp2210_set_gpio_sram_res mcp2210_set_gpio_sram_res;
9021.1Sbrad	struct mcp2210_set_gpio_dir_res mcp2210_set_gpio_dir_sram_res;
9031.1Sbrad	struct mcp2210_set_gpio_value_req mcp2210_set_gpio_value_sram_req;
9041.1Sbrad	struct mcp2210_set_gpio_value_res mcp2210_set_gpio_value_sram_res;
9051.1Sbrad
9061.1Sbrad	struct mcp2210_get_gpio_sram_res mcp2210_get_gpio_sram_res;
9071.1Sbrad	struct mcp2210_get_gpio_dir_res mcp2210_get_gpio_dir_sram_res;
9081.1Sbrad	struct mcp2210_get_gpio_value_res mcp2210_get_gpio_value_sram_res;
9091.1Sbrad
9101.1Sbrad	uint32_t altmask =
9111.1Sbrad	GPIO_PIN_ALT0 | GPIO_PIN_ALT3 | GPIO_PIN_ALT4 | GPIO_PIN_ALT5 | GPIO_PIN_ALT6;
9121.1Sbrad	int err = 0;
9131.1Sbrad	uint16_t vdir;
9141.1Sbrad
9151.1Sbrad	if (sc->sc_dying)
9161.1Sbrad		return 0;
9171.1Sbrad
9181.1Sbrad	KASSERT(mutex_owned(&sc->sc_action_mutex));
9191.1Sbrad
9201.1Sbrad	err = mcp2210_get_gpio_sram(sc, &mcp2210_get_gpio_sram_res);
9211.1Sbrad	if (err)
9221.1Sbrad		goto out;
9231.1Sbrad	err = mcp2210_get_gpio_dir_sram(sc, &mcp2210_get_gpio_dir_sram_res);
9241.1Sbrad	if (err)
9251.1Sbrad		goto out;
9261.1Sbrad	err = mcp2210_get_gpio_value_sram(sc, &mcp2210_get_gpio_value_sram_res);
9271.1Sbrad	if (err)
9281.1Sbrad		goto out;
9291.1Sbrad
9301.1Sbrad	umcpmio_dump_buffer(sc->sc_dumpbuffer,
9311.1Sbrad	    (uint8_t *) & mcp2210_get_gpio_sram_res, MCP2210_RES_BUFFER_SIZE,
9321.1Sbrad	    "mcp2210_gpio_pin_ctl get gpio sram res");
9331.1Sbrad	umcpmio_dump_buffer(sc->sc_dumpbuffer,
9341.1Sbrad	    (uint8_t *) & mcp2210_get_gpio_dir_sram_res, MCP2210_RES_BUFFER_SIZE,
9351.1Sbrad	    "mcp2210_gpio_pin_ctl get gpio dir sram res");
9361.1Sbrad
9371.1Sbrad	memset(&mcp2210_set_gpio_sram_req.cmd, 0, MCP2210_REQ_BUFFER_SIZE);
9381.1Sbrad	memcpy(&mcp2210_set_gpio_sram_req.gp0_designation,
9391.1Sbrad	    &mcp2210_get_gpio_sram_res.gp0_designation,
9401.1Sbrad	    &mcp2210_get_gpio_sram_res.nvram_protection -
9411.1Sbrad	    &mcp2210_get_gpio_sram_res.gp0_designation + 1);
9421.1Sbrad
9431.1Sbrad	memset(&mcp2210_set_gpio_dir_sram_req.cmd, 0, MCP2210_REQ_BUFFER_SIZE);
9441.1Sbrad	vdir = (mcp2210_get_gpio_dir_sram_res.pin_dir_msb << 8) |
9451.1Sbrad	    mcp2210_get_gpio_dir_sram_res.pin_dir_lsb;
9461.1Sbrad
9471.1Sbrad	uint8_t *b = (uint8_t *) & mcp2210_set_gpio_sram_req.cmd;
9481.1Sbrad	bool changed_sram = false;
9491.1Sbrad
9501.1Sbrad	if (flags & (GPIO_PIN_OUTPUT | GPIO_PIN_INPUT)) {
9511.1Sbrad		if (b[MCP2210_GPIO_SRAM_GP0 + pin] != MCP2210_PIN_IS_GPIO) {
9521.1Sbrad			b[MCP2210_GPIO_SRAM_GP0 + pin] = MCP2210_PIN_IS_GPIO;
9531.1Sbrad			changed_sram = true;
9541.1Sbrad		}
9551.1Sbrad		if (flags & GPIO_PIN_INPUT)
9561.1Sbrad			vdir |= (1 << pin);
9571.1Sbrad		else
9581.1Sbrad			vdir &= ~(1 << pin);
9591.1Sbrad	} else {
9601.1Sbrad		switch (flags & altmask) {
9611.1Sbrad		case GPIO_PIN_ALT0:
9621.1Sbrad			if (b[MCP2210_GPIO_SRAM_GP0 + pin] != MCP2210_PIN_IS_ALT0) {
9631.1Sbrad				b[MCP2210_GPIO_SRAM_GP0 + pin] = MCP2210_PIN_IS_ALT0;
9641.1Sbrad				changed_sram = true;
9651.1Sbrad			}
9661.1Sbrad			break;
9671.1Sbrad		case GPIO_PIN_ALT3:
9681.1Sbrad			if (b[MCP2210_GPIO_SRAM_GP0 + pin] != MCP2210_PIN_IS_DED) {
9691.1Sbrad				b[MCP2210_GPIO_SRAM_GP0 + pin] = MCP2210_PIN_IS_DED;
9701.1Sbrad				changed_sram = true;
9711.1Sbrad			}
9721.1Sbrad			if (pin == 6) {
9731.1Sbrad				if (mcp2210_counter_gp_to_flags(mcp2210_get_gpio_sram_res.other_settings) != GPIO_PIN_ALT3) {
9741.1Sbrad					mcp2210_set_gpio_sram_req.other_settings &= 0xf1;
9751.1Sbrad					mcp2210_set_gpio_sram_req.other_settings |= MCP2210_COUNTER_FALLING_EDGE << 1;
9761.1Sbrad					changed_sram = true;
9771.1Sbrad				}
9781.1Sbrad			}
9791.1Sbrad			break;
9801.1Sbrad		case GPIO_PIN_ALT4:
9811.1Sbrad			if (b[MCP2210_GPIO_SRAM_GP0 + pin] != MCP2210_PIN_IS_DED) {
9821.1Sbrad				b[MCP2210_GPIO_SRAM_GP0 + pin] = MCP2210_PIN_IS_DED;
9831.1Sbrad				changed_sram = true;
9841.1Sbrad			}
9851.1Sbrad			if (pin == 6) {
9861.1Sbrad				if (mcp2210_counter_gp_to_flags(mcp2210_get_gpio_sram_res.other_settings) != GPIO_PIN_ALT4) {
9871.1Sbrad					mcp2210_set_gpio_sram_req.other_settings &= 0xf1;
9881.1Sbrad					mcp2210_set_gpio_sram_req.other_settings |= MCP2210_COUNTER_RISING_EDGE << 1;
9891.1Sbrad					changed_sram = true;
9901.1Sbrad				}
9911.1Sbrad			}
9921.1Sbrad			break;
9931.1Sbrad		case GPIO_PIN_ALT5:
9941.1Sbrad			if (b[MCP2210_GPIO_SRAM_GP0 + pin] != MCP2210_PIN_IS_DED) {
9951.1Sbrad				b[MCP2210_GPIO_SRAM_GP0 + pin] = MCP2210_PIN_IS_DED;
9961.1Sbrad				changed_sram = true;
9971.1Sbrad			}
9981.1Sbrad			if (pin == 6) {
9991.1Sbrad				if (mcp2210_counter_gp_to_flags(mcp2210_get_gpio_sram_res.other_settings) != GPIO_PIN_ALT5) {
10001.1Sbrad					mcp2210_set_gpio_sram_req.other_settings &= 0xf1;
10011.1Sbrad					mcp2210_set_gpio_sram_req.other_settings |= MCP2210_COUNTER_LOW_PULSE << 1;
10021.1Sbrad					changed_sram = true;
10031.1Sbrad				}
10041.1Sbrad			}
10051.1Sbrad			break;
10061.1Sbrad		case GPIO_PIN_ALT6:
10071.1Sbrad			if (b[MCP2210_GPIO_SRAM_GP0 + pin] != MCP2210_PIN_IS_DED) {
10081.1Sbrad				b[MCP2210_GPIO_SRAM_GP0 + pin] = MCP2210_PIN_IS_DED;
10091.1Sbrad				changed_sram = true;
10101.1Sbrad			}
10111.1Sbrad			if (pin == 6) {
10121.1Sbrad				if (mcp2210_counter_gp_to_flags(mcp2210_get_gpio_sram_res.other_settings) != GPIO_PIN_ALT6) {
10131.1Sbrad					mcp2210_set_gpio_sram_req.other_settings &= 0xf1;
10141.1Sbrad					mcp2210_set_gpio_sram_req.other_settings |= MCP2210_COUNTER_HIGH_PULSE << 1;
10151.1Sbrad					changed_sram = true;
10161.1Sbrad				}
10171.1Sbrad			}
10181.1Sbrad			break;
10191.1Sbrad		default:
10201.1Sbrad			break;
10211.1Sbrad		}
10221.1Sbrad	}
10231.1Sbrad
10241.1Sbrad	/* On the MCP-2210, if you change the purpose of the pin you have to
10251.1Sbrad	 * write the current direction and value of the pins back to the chip.
10261.1Sbrad	 * The reason for this is tha you can not change just one pins purpose
10271.1Sbrad	 * without setting the direction and values of all pins to the default. */
10281.1Sbrad
10291.1Sbrad	if (changed_sram) {
10301.1Sbrad		err = mcp2210_set_gpio_sram(sc, &mcp2210_set_gpio_sram_req,
10311.1Sbrad		    &mcp2210_set_gpio_sram_res);
10321.1Sbrad		if (err)
10331.1Sbrad			goto out;
10341.1Sbrad
10351.1Sbrad		mcp2210_set_gpio_dir_sram_req.pin_dir_msb = (vdir >> 8) & 0xff;
10361.1Sbrad		mcp2210_set_gpio_dir_sram_req.pin_dir_lsb = vdir & 0x00ff;
10371.1Sbrad
10381.1Sbrad		err = mcp2210_set_gpio_dir_sram(sc, &mcp2210_set_gpio_dir_sram_req,
10391.1Sbrad		    &mcp2210_set_gpio_dir_sram_res);
10401.1Sbrad		if (err)
10411.1Sbrad			goto out;
10421.1Sbrad
10431.1Sbrad		mcp2210_set_gpio_value_sram_req.pin_value_msb = mcp2210_get_gpio_value_sram_res.pin_value_msb;
10441.1Sbrad		mcp2210_set_gpio_value_sram_req.pin_value_lsb = mcp2210_get_gpio_value_sram_res.pin_value_lsb;
10451.1Sbrad
10461.1Sbrad		/* Further, if the pin is for OUTPUT, then we will want to set
10471.1Sbrad		 * its value to the default, otherwise the default may never be
10481.1Sbrad		 * reflected in the pin state, as the pin might have been a
10491.1Sbrad		 * INPUT with the opposite value and just putting all of the
10501.1Sbrad		 * values back in that case would do the wrong thing. */
10511.1Sbrad
10521.1Sbrad		if (flags & GPIO_PIN_OUTPUT && pin < 8) {
10531.1Sbrad			if (mcp2210_set_gpio_sram_req.default_output_lsb & (1 << pin))
10541.1Sbrad				mcp2210_set_gpio_value_sram_req.pin_value_lsb |= (1 << pin);
10551.1Sbrad			else
10561.1Sbrad				mcp2210_set_gpio_value_sram_req.pin_value_lsb &= ~(1 << pin);
10571.1Sbrad		}
10581.1Sbrad		err = mcp2210_set_gpio_value_sram(sc, &mcp2210_set_gpio_value_sram_req, &mcp2210_set_gpio_value_sram_res);
10591.1Sbrad	} else {
10601.1Sbrad		/* In this case, the pin purpose was not changed, so all that
10611.1Sbrad		 * needs to happen is the direction needs to be updated.  This
10621.1Sbrad		 * actually won't matter unless the pin is strickly a GPIO pin.
10631.1Sbrad		 * ALT0, the CS purpose, is handled by the chip itself, ALT3 -
10641.1Sbrad		 * ALT6 is for the event / interrupt counter.  So, we really
10651.1Sbrad		 * only have to care if the pin switches from INPUT to OUTPUT,
10661.1Sbrad		 * or vise versa. */
10671.1Sbrad		if (flags & (GPIO_PIN_OUTPUT | GPIO_PIN_INPUT)) {
10681.1Sbrad			mcp2210_set_gpio_dir_sram_req.pin_dir_msb = (vdir >> 8) & 0xff;
10691.1Sbrad			mcp2210_set_gpio_dir_sram_req.pin_dir_lsb = vdir & 0x00ff;
10701.1Sbrad
10711.1Sbrad			err = mcp2210_set_gpio_dir_sram(sc, &mcp2210_set_gpio_dir_sram_req,
10721.1Sbrad			    &mcp2210_set_gpio_dir_sram_res);
10731.1Sbrad		}
10741.1Sbrad	}
10751.1Sbradout:
10761.1Sbrad	return err;
10771.1Sbrad}
10781.1Sbrad/*
10791.1Sbrad * Internal function that does the dirty work of setting a gpio
10801.1Sbrad * pin to its "type" for the MCP-2221 / MCP-2221A
10811.1Sbrad *
10821.1Sbrad * There are really two ways to do some of this, one is to set the pin
10831.1Sbrad * to input and output, or whatever, using SRAM calls, the other is to
10841.1Sbrad * use the GPIO config calls to set input and output and SRAM for
10851.1Sbrad * everything else.  This just uses SRAM for everything.
10861.1Sbrad */
10871.1Sbrad
10881.1Sbradint
10891.1Sbradmcp2221_gpio_pin_ctl(void *arg, int pin, int flags)
10901.1Sbrad{
10911.1Sbrad	struct umcpmio_softc *sc = arg;
10921.1Sbrad	struct mcp2221_set_sram_req set_sram_req;
10931.1Sbrad	struct mcp2221_set_sram_res set_sram_res;
10941.1Sbrad	struct mcp2221_get_sram_res current_sram_res;
10951.1Sbrad	struct mcp2221_get_gpio_cfg_res current_gpio_cfg_res;
10961.1Sbrad	int err = 0;
10971.1Sbrad
10981.1Sbrad	if (sc->sc_dying)
10991.1Sbrad		return 0;
11001.1Sbrad
11011.1Sbrad	KASSERT(mutex_owned(&sc->sc_action_mutex));
11021.1Sbrad
11031.1Sbrad	err = mcp2221_get_sram(sc, &current_sram_res);
11041.1Sbrad	if (err)
11051.1Sbrad		goto out;
11061.1Sbrad
11071.1Sbrad	err = mcp2221_get_gpio_cfg(sc, &current_gpio_cfg_res);
11081.1Sbrad	if (err)
11091.1Sbrad		goto out;
11101.1Sbrad
11111.1Sbrad	/* You can't just set one pin, you must set all of them, so copy the
11121.1Sbrad	 * current settings for the pin we are not messing with.
11131.1Sbrad	 *
11141.1Sbrad	 * And, yes, of course, if the MCP-2210 is ever supported with this
11151.1Sbrad	 * driver, this sort of unrolling will need to be turned into something
11161.1Sbrad	 * different, but for now, just unroll as there are only 4 pins to care
11171.1Sbrad	 * about.
11181.1Sbrad	 *
11191.1Sbrad	 * */
11201.1Sbrad
11211.1Sbrad	memset(&set_sram_req, 0, MCP2221_REQ_BUFFER_SIZE);
11221.1Sbrad	switch (pin) {
11231.1Sbrad	case 0:
11241.1Sbrad		set_sram_req.gp1_settings = current_sram_res.gp1_settings;
11251.1Sbrad		set_sram_req.gp2_settings = current_sram_res.gp2_settings;
11261.1Sbrad		set_sram_req.gp3_settings = current_sram_res.gp3_settings;
11271.1Sbrad		break;
11281.1Sbrad	case 1:
11291.1Sbrad		set_sram_req.gp0_settings = current_sram_res.gp0_settings;
11301.1Sbrad		set_sram_req.gp2_settings = current_sram_res.gp2_settings;
11311.1Sbrad		set_sram_req.gp3_settings = current_sram_res.gp3_settings;
11321.1Sbrad		break;
11331.1Sbrad	case 2:
11341.1Sbrad		set_sram_req.gp0_settings = current_sram_res.gp0_settings;
11351.1Sbrad		set_sram_req.gp1_settings = current_sram_res.gp1_settings;
11361.1Sbrad		set_sram_req.gp3_settings = current_sram_res.gp3_settings;
11371.1Sbrad		break;
11381.1Sbrad	case 3:
11391.1Sbrad		set_sram_req.gp0_settings = current_sram_res.gp0_settings;
11401.1Sbrad		set_sram_req.gp1_settings = current_sram_res.gp1_settings;
11411.1Sbrad		set_sram_req.gp2_settings = current_sram_res.gp2_settings;
11421.1Sbrad		break;
11431.1Sbrad	}
11441.1Sbrad	mcp2221_set_gpio_designation_sram(&set_sram_req, pin, flags);
11451.1Sbrad	mcp2221_set_gpio_dir_sram(&set_sram_req, pin, flags);
11461.1Sbrad
11471.1Sbrad	/* This part is unfortunate...  if a pin is set to output, the value
11481.1Sbrad	 * set on the pin is not mirrored by the chip into SRAM, but the chip
11491.1Sbrad	 * will use the value from SRAM to set the value of the pin.  What this
11501.1Sbrad	 * means is that we have to learn the value from the GPIO config and
11511.1Sbrad	 * make sure it is set properly when updating SRAM. */
11521.1Sbrad
11531.1Sbrad	if (current_gpio_cfg_res.gp0_pin_dir == MCP2221_GPIO_CFG_DIR_OUTPUT) {
11541.1Sbrad		if (current_gpio_cfg_res.gp0_pin_value == 1) {
11551.1Sbrad			set_sram_req.gp0_settings |=
11561.1Sbrad			    MCP2221_SRAM_GPIO_OUTPUT_HIGH;
11571.1Sbrad		} else {
11581.1Sbrad			set_sram_req.gp0_settings &=
11591.1Sbrad			    ~MCP2221_SRAM_GPIO_OUTPUT_HIGH;
11601.1Sbrad		}
11611.1Sbrad	}
11621.1Sbrad	if (current_gpio_cfg_res.gp1_pin_dir == MCP2221_GPIO_CFG_DIR_OUTPUT) {
11631.1Sbrad		if (current_gpio_cfg_res.gp1_pin_value == 1) {
11641.1Sbrad			set_sram_req.gp1_settings |=
11651.1Sbrad			    MCP2221_SRAM_GPIO_OUTPUT_HIGH;
11661.1Sbrad		} else {
11671.1Sbrad			set_sram_req.gp1_settings &=
11681.1Sbrad			    ~MCP2221_SRAM_GPIO_OUTPUT_HIGH;
11691.1Sbrad		}
11701.1Sbrad	}
11711.1Sbrad	if (current_gpio_cfg_res.gp2_pin_dir == MCP2221_GPIO_CFG_DIR_OUTPUT) {
11721.1Sbrad		if (current_gpio_cfg_res.gp2_pin_value == 1) {
11731.1Sbrad			set_sram_req.gp2_settings |=
11741.1Sbrad			    MCP2221_SRAM_GPIO_OUTPUT_HIGH;
11751.1Sbrad		} else {
11761.1Sbrad			set_sram_req.gp2_settings &=
11771.1Sbrad			    ~MCP2221_SRAM_GPIO_OUTPUT_HIGH;
11781.1Sbrad		}
11791.1Sbrad	}
11801.1Sbrad	if (current_gpio_cfg_res.gp3_pin_dir == MCP2221_GPIO_CFG_DIR_OUTPUT) {
11811.1Sbrad		if (current_gpio_cfg_res.gp3_pin_value == 1) {
11821.1Sbrad			set_sram_req.gp3_settings |=
11831.1Sbrad			    MCP2221_SRAM_GPIO_OUTPUT_HIGH;
11841.1Sbrad		} else {
11851.1Sbrad			set_sram_req.gp3_settings &=
11861.1Sbrad			    ~MCP2221_SRAM_GPIO_OUTPUT_HIGH;
11871.1Sbrad		}
11881.1Sbrad	}
11891.1Sbrad	err = mcp2221_put_sram(sc, &set_sram_req, &set_sram_res);
11901.1Sbrad	if (err)
11911.1Sbrad		goto out;
11921.1Sbrad
11931.1Sbrad	umcpmio_dump_buffer(sc->sc_dumpbuffer,
11941.1Sbrad	    (uint8_t *) & set_sram_res, MCP2221_RES_BUFFER_SIZE,
11951.1Sbrad	    "mcp2221_gpio_pin_ctl set sram buffer copy");
11961.1Sbrad	if (set_sram_res.cmd != MCP2221_CMD_SET_SRAM ||
11971.1Sbrad	    set_sram_res.completion != MCP2221_CMD_COMPLETE_OK) {
11981.1Sbrad		device_printf(sc->sc_dev, "mcp2221_gpio_pin_ctl:"
11991.1Sbrad		    " not the command desired, or error: %02x %02x\n",
12001.1Sbrad		    set_sram_res.cmd,
12011.1Sbrad		    set_sram_res.completion);
12021.1Sbrad		err = EIO;
12031.1Sbrad		goto out;
12041.1Sbrad	}
12051.1Sbrad	sc->sc_gpio_pins[pin].pin_flags = flags;
12061.1Sbrad	err = 0;
12071.1Sbrad
12081.1Sbradout:
12091.1Sbrad	return err;
12101.1Sbrad}
12111.1Sbrad
12121.1Sbradvoid
12131.1Sbradumcpmio_gpio_pin_ctl(void *arg, int pin, int flags)
12141.1Sbrad{
12151.1Sbrad	struct umcpmio_softc *sc = arg;
12161.1Sbrad
12171.1Sbrad	if (sc->sc_dying)
12181.1Sbrad		return;
12191.1Sbrad
12201.1Sbrad	mutex_enter(&sc->sc_action_mutex);
12211.1Sbrad	if (sc->sc_chipinfo->usb_id == USB_PRODUCT_MICROCHIP_MCP2210)
12221.1Sbrad		mcp2210_gpio_pin_ctl(sc, pin, flags);
12231.1Sbrad	if (sc->sc_chipinfo->usb_id == USB_PRODUCT_MICROCHIP_MCP2221)
12241.1Sbrad		mcp2221_gpio_pin_ctl(sc, pin, flags);
12251.1Sbrad	mutex_exit(&sc->sc_action_mutex);
12261.1Sbrad}
12271.1Sbrad
12281.1Sbradint
12291.1Sbradmcp2210_get_gp6_counter(struct umcpmio_softc *sc, int *counter,
12301.1Sbrad    uint8_t reset)
12311.1Sbrad{
12321.1Sbrad	struct mcp2210_get_gp6_events_req req;
12331.1Sbrad	struct mcp2210_get_gp6_events_res res;
12341.1Sbrad	int err = 0;
12351.1Sbrad
12361.1Sbrad	KASSERT(mutex_owned(&sc->sc_action_mutex));
12371.1Sbrad
12381.1Sbrad	memset(&req, 0, MCP2210_REQ_BUFFER_SIZE);
12391.1Sbrad	req.cmd = MCP2210_CMD_GET_GP6_EVENTS;
12401.1Sbrad	req.reset_counter = reset;
12411.1Sbrad
12421.1Sbrad	umcpmio_dump_buffer(sc->sc_dumpbuffer,
12431.1Sbrad	    (uint8_t *) & req, MCP2210_REQ_BUFFER_SIZE,
12441.1Sbrad	    "mcp2210_get_gp6_counter req");
12451.1Sbrad
12461.1Sbrad	err = umcpmio_send_report(sc,
12471.1Sbrad	    (uint8_t *) & req, MCP2210_REQ_BUFFER_SIZE,
12481.1Sbrad	    (uint8_t *) & res, sc->sc_cv_wait);
12491.1Sbrad
12501.1Sbrad	umcpmio_dump_buffer(sc->sc_dumpbuffer,
12511.1Sbrad	    (uint8_t *) & res, MCP2210_RES_BUFFER_SIZE,
12521.1Sbrad	    "mcp2210_get_gp6_counter res");
12531.1Sbrad
12541.1Sbrad	err = mcp2210_decode_errors(req.cmd, err, res.completion);
12551.1Sbrad	if (!err) {
12561.1Sbrad		*counter = (res.counter_msb << 8) | res.counter_lsb;
12571.1Sbrad	}
12581.1Sbrad	return err;
12591.1Sbrad}
12601.1Sbrad
12611.1Sbradstatic int
12621.1Sbradumcpmio_extract_gpio_sram(struct umcpmio_softc *sc,
12631.1Sbrad    uint8_t *extract)
12641.1Sbrad{
12651.1Sbrad	int err = 0;
12661.1Sbrad	struct mcp2210_get_gpio_sram_res mcp2210_get_gpio_sram_res;
12671.1Sbrad	struct mcp2210_get_gpio_dir_res mcp2210_get_gpio_dir_sram_res;
12681.1Sbrad	struct mcp2221_get_sram_res mcp2221_get_sram_res;
12691.1Sbrad
12701.1Sbrad	if (sc->sc_chipinfo->usb_id == USB_PRODUCT_MICROCHIP_MCP2210) {
12711.1Sbrad		mutex_enter(&sc->sc_action_mutex);
12721.1Sbrad		err = mcp2210_get_gpio_sram(sc, &mcp2210_get_gpio_sram_res);
12731.1Sbrad		mutex_exit(&sc->sc_action_mutex);
12741.1Sbrad		if (err)
12751.1Sbrad			return err;
12761.1Sbrad
12771.1Sbrad		umcpmio_dump_buffer(sc->sc_dumpbuffer,
12781.1Sbrad		    (uint8_t *) & mcp2210_get_gpio_sram_res, MCP2210_RES_BUFFER_SIZE,
12791.1Sbrad		    "umcpmio_extract_gpio_sram mcp2210 get gpio sram buffer copy");
12801.1Sbrad
12811.1Sbrad		extract[0] = mcp2210_get_gpio_sram_res.gp0_designation;
12821.1Sbrad		extract[1] = mcp2210_get_gpio_sram_res.gp1_designation;
12831.1Sbrad		extract[2] = mcp2210_get_gpio_sram_res.gp2_designation;
12841.1Sbrad		extract[3] = mcp2210_get_gpio_sram_res.gp3_designation;
12851.1Sbrad		extract[4] = mcp2210_get_gpio_sram_res.gp4_designation;
12861.1Sbrad		extract[5] = mcp2210_get_gpio_sram_res.gp5_designation;
12871.1Sbrad		extract[6] = mcp2210_get_gpio_sram_res.gp6_designation;
12881.1Sbrad		extract[7] = mcp2210_get_gpio_sram_res.gp7_designation;
12891.1Sbrad		extract[8] = mcp2210_get_gpio_sram_res.gp8_designation;
12901.1Sbrad
12911.1Sbrad		mutex_enter(&sc->sc_action_mutex);
12921.1Sbrad		err = mcp2210_get_gpio_dir_sram(sc, &mcp2210_get_gpio_dir_sram_res);
12931.1Sbrad		mutex_exit(&sc->sc_action_mutex);
12941.1Sbrad		if (err)
12951.1Sbrad			return err;
12961.1Sbrad
12971.1Sbrad		umcpmio_dump_buffer(sc->sc_dumpbuffer,
12981.1Sbrad		    (uint8_t *) & mcp2210_get_gpio_dir_sram_res, MCP2210_RES_BUFFER_SIZE,
12991.1Sbrad		    "umcpmio_extract_gpio_sram mcp2210 get gpio dir sram buffer copy");
13001.1Sbrad
13011.1Sbrad		extract[9] = mcp2210_get_gpio_dir_sram_res.pin_dir_lsb;
13021.1Sbrad		extract[10] = mcp2210_get_gpio_dir_sram_res.pin_dir_msb;
13031.1Sbrad		extract[11] = mcp2210_get_gpio_sram_res.other_settings;
13041.1Sbrad	}
13051.1Sbrad	if (sc->sc_chipinfo->usb_id == USB_PRODUCT_MICROCHIP_MCP2221) {
13061.1Sbrad		mutex_enter(&sc->sc_action_mutex);
13071.1Sbrad		err = mcp2221_get_sram(sc, &mcp2221_get_sram_res);
13081.1Sbrad		mutex_exit(&sc->sc_action_mutex);
13091.1Sbrad		if (err)
13101.1Sbrad			return err;
13111.1Sbrad
13121.1Sbrad		umcpmio_dump_buffer(sc->sc_dumpbuffer,
13131.1Sbrad		    (uint8_t *) & mcp2221_get_sram_res, MCP2221_RES_BUFFER_SIZE,
13141.1Sbrad		    "umcpmio_extract_gpio_sram mcp2221 get sram buffer copy");
13151.1Sbrad
13161.1Sbrad		extract[0] = mcp2221_get_sram_res.gp0_settings;
13171.1Sbrad		extract[1] = mcp2221_get_sram_res.gp1_settings;
13181.1Sbrad		extract[2] = mcp2221_get_sram_res.gp2_settings;
13191.1Sbrad		extract[3] = mcp2221_get_sram_res.gp3_settings;
13201.1Sbrad	}
13211.1Sbrad	return err;
13221.1Sbrad}
13231.1Sbrad
13241.1Sbradvoid
13251.1Sbradumcpmio_gpio_attach(struct umcpmio_softc *sc)
13261.1Sbrad{
13271.1Sbrad	int err;
13281.1Sbrad	struct gpiobus_attach_args gba;
13291.1Sbrad	uint8_t extract[UMCPMIO_MAX_GPIO_PINS + 3];
13301.1Sbrad
13311.1Sbrad	err = umcpmio_extract_gpio_sram(sc, extract);
13321.1Sbrad	if (err) {
13331.1Sbrad		aprint_error_dev(sc->sc_dev, "umcpmio_gpio_attach:"
13341.1Sbrad		    " extract gpio from sram: err=%d\n",
13351.1Sbrad		    err);
13361.1Sbrad		return;
13371.1Sbrad	}
13381.1Sbrad
13391.1Sbrad	/* The MCP2221 / MCP2221A has a pin that can have gpio interrupt
13401.1Sbrad	 * ability, but there are problems with making use of it as the
13411.1Sbrad	 * gpio framework runs with spin locks or hard interrupt level,
13421.1Sbrad	 * and you can't call into the USB framework in that state.
13431.1Sbrad	 *
13441.1Sbrad	 * It is largely the same reason using the umcpmio gpio pins
13451.1Sbrad	 * as attachments to gpiopps or gpioow doesn't work.  Spin
13461.1Sbrad	 * locks are held there too.
13471.1Sbrad	 */
13481.1Sbrad	for (int c = 0; c < sc->sc_chipinfo->num_gpio_pins; c++){
13491.1Sbrad		sc->sc_gpio_pins[c].pin_num = c;
13501.1Sbrad		sc->sc_gpio_pins[c].pin_caps = sc->sc_chipinfo->gpio_pin_ability[c];
13511.1Sbrad		if (sc->sc_chipinfo->usb_id == USB_PRODUCT_MICROCHIP_MCP2210) {
13521.1Sbrad			sc->sc_gpio_pins[c].pin_flags =
13531.1Sbrad			    mcp2210_sram_gpio_to_flags(extract, c);
13541.1Sbrad		}
13551.1Sbrad		if (sc->sc_chipinfo->usb_id == USB_PRODUCT_MICROCHIP_MCP2221) {
13561.1Sbrad			sc->sc_gpio_pins[c].pin_flags =
13571.1Sbrad			    mcp2221_sram_gpio_to_flags(extract[c]);
13581.1Sbrad		}
13591.1Sbrad		sc->sc_gpio_pins[c].pin_intrcaps = 0;
13601.1Sbrad		strncpy(sc->sc_gpio_pins[c].pin_defname,
13611.1Sbrad		    sc->sc_chipinfo->gpio_names[c],
13621.1Sbrad		    strlen(sc->sc_chipinfo->gpio_names[c]) + 1);
13631.1Sbrad	}
13641.1Sbrad
13651.1Sbrad	sc->sc_gpio_gc.gp_cookie = sc;
13661.1Sbrad	sc->sc_gpio_gc.gp_pin_read = umcpmio_gpio_pin_read;
13671.1Sbrad	sc->sc_gpio_gc.gp_pin_write = umcpmio_gpio_pin_write;
13681.1Sbrad	sc->sc_gpio_gc.gp_pin_ctl = umcpmio_gpio_pin_ctl;
13691.1Sbrad
13701.1Sbrad	gba.gba_gc = &sc->sc_gpio_gc;
13711.1Sbrad	gba.gba_pins = sc->sc_gpio_pins;
13721.1Sbrad	gba.gba_npins = sc->sc_chipinfo->num_gpio_pins;
13731.1Sbrad
13741.1Sbrad	sc->sc_gpio_dev = config_found(sc->sc_dev, &gba, gpiobus_print,
13751.1Sbrad	    CFARGS(.iattr = "gpiobus"));
13761.1Sbrad}
1377