Home | History | Annotate | Line # | Download | only in usb
umcpmio_subr.c revision 1.1
      1 /*	$NetBSD: umcpmio_subr.c,v 1.1 2024/12/16 16:37:38 brad Exp $	*/
      2 
      3 /*
      4  * Copyright (c) 2024 Brad Spencer <brad (at) 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_subr.c,v 1.1 2024/12/16 16:37:38 brad Exp $");
     21 
     22 #ifdef _KERNEL_OPT
     23 #include "opt_usb.h"
     24 #endif
     25 
     26 #include <sys/param.h>
     27 #include <sys/systm.h>
     28 #include <sys/conf.h>
     29 #include <sys/kernel.h>
     30 #include <sys/kmem.h>
     31 #include <sys/device.h>
     32 #include <sys/sysctl.h>
     33 #include <sys/tty.h>
     34 #include <sys/file.h>
     35 #include <sys/vnode.h>
     36 #include <sys/kauth.h>
     37 #include <sys/lwp.h>
     38 
     39 #include <dev/usb/usb.h>
     40 #include <dev/usb/usbhid.h>
     41 
     42 #include <dev/usb/usbdi.h>
     43 #include <dev/usb/usbdi_util.h>
     44 #include <dev/usb/usbdevs.h>
     45 #include <dev/usb/uhidev.h>
     46 #include <dev/hid/hid.h>
     47 
     48 #include <dev/usb/umcpmio.h>
     49 #include <dev/usb/umcpmio_subr.h>
     50 #include <dev/usb/umcpmio_hid_reports.h>
     51 
     52 int umcpmio_send_report(struct umcpmio_softc *, uint8_t *, size_t, uint8_t *, int);
     53 
     54 #define UMCPMIO_DEBUG 1
     55 #ifdef UMCPMIO_DEBUG
     56 #define DPRINTF(x)	if (umcpmiodebug) printf x
     57 #define DPRINTFN(n, x)	if (umcpmiodebug > (n)) printf x
     58 extern int	umcpmiodebug;
     59 #else
     60 #define DPRINTF(x)	__nothing
     61 #define DPRINTFN(n,x)	__nothing
     62 #endif
     63 
     64 /* Handy functions that do a bunch of things for the main driver code */
     65 
     66 int
     67 umcpmio_get_status(struct umcpmio_softc *sc,
     68     struct mcp2221_status_res *res, bool takemutex)
     69 {
     70 	struct mcp2221_status_req req;
     71 	int err = 0;
     72 
     73 	memset(&req, 0, MCP2221_REQ_BUFFER_SIZE);
     74 	req.cmd = MCP2221_CMD_STATUS;
     75 
     76 	if (takemutex)
     77 		mutex_enter(&sc->sc_action_mutex);
     78 	err = umcpmio_send_report(sc, (uint8_t *)&req, MCP2221_REQ_BUFFER_SIZE, (uint8_t *)res, sc->sc_cv_wait);
     79 	if (takemutex)
     80 		mutex_exit(&sc->sc_action_mutex);
     81 
     82 	return(err);
     83 }
     84 
     85 void
     86 umcpmio_set_i2c_speed(struct mcp2221_status_req *req,
     87     int flags)
     88 {
     89 	int i2cbaud = MCP2221_DEFAULT_I2C_SPEED;
     90 
     91 	if (flags & I2C_F_SPEED)
     92 		i2cbaud = 400000;
     93 
     94 	req->set_i2c_speed = MCP2221_I2C_SET_SPEED;
     95 	if (i2cbaud <= 0)
     96 		i2cbaud = MCP2221_DEFAULT_I2C_SPEED;
     97 
     98 	/* Everyone and their brother seems to store the I2C divider like this,
     99 	 * so do likewise */
    100 
    101 	req->i2c_clock_divider = (MCP2221_INTERNAL_CLOCK / i2cbaud) - 3;
    102 }
    103 
    104 int
    105 umcpmio_put_status(struct umcpmio_softc *sc,
    106     struct mcp2221_status_req *req, struct mcp2221_status_res *res,
    107     bool takemutex)
    108 {
    109 	int err = 0;
    110 
    111 	req->cmd = MCP2221_CMD_STATUS;
    112 
    113 	if (takemutex)
    114 		mutex_enter(&sc->sc_action_mutex);
    115 	err = umcpmio_send_report(sc, (uint8_t *)req, MCP2221_REQ_BUFFER_SIZE, (uint8_t *)res, sc->sc_cv_wait);
    116 	if (takemutex)
    117 		mutex_exit(&sc->sc_action_mutex);
    118 
    119 	return(err);
    120 }
    121 
    122 int
    123 umcpmio_set_i2c_speed_one(struct umcpmio_softc *sc,
    124     int flags, bool takemutex)
    125 {
    126 	int err = 0;
    127 	struct mcp2221_status_req req;
    128 	struct mcp2221_status_res res;
    129 
    130 	memset(&req, 0, MCP2221_REQ_BUFFER_SIZE);
    131 	umcpmio_set_i2c_speed(&req, flags);
    132 	err = umcpmio_put_status(sc, &req, &res, takemutex);
    133 	if (! err) {
    134 		if (res.set_i2c_speed == MCP2221_I2C_SPEED_BUSY)
    135 			err = EBUSY;
    136 	}
    137 
    138 	return(err);
    139 }
    140 
    141 int
    142 umcpmio_get_sram(struct umcpmio_softc *sc,
    143     struct mcp2221_get_sram_res *res, bool takemutex)
    144 {
    145 	struct mcp2221_get_sram_req req;
    146 	int err = 0;
    147 
    148 	memset(&req, 0, MCP2221_REQ_BUFFER_SIZE);
    149 	req.cmd = MCP2221_CMD_GET_SRAM;
    150 
    151 	if (takemutex)
    152 		mutex_enter(&sc->sc_action_mutex);
    153 	err = umcpmio_send_report(sc, (uint8_t *)&req, MCP2221_REQ_BUFFER_SIZE, (uint8_t *)res, sc->sc_cv_wait);
    154 	if (takemutex)
    155 		mutex_exit(&sc->sc_action_mutex);
    156 
    157 	return(err);
    158 }
    159 
    160 int
    161 umcpmio_put_sram(struct umcpmio_softc *sc,
    162     struct mcp2221_set_sram_req *req, struct mcp2221_set_sram_res *res,
    163     bool takemutex)
    164 {
    165 	int err = 0;
    166 
    167 	req->cmd = MCP2221_CMD_SET_SRAM;
    168 
    169 	if (takemutex)
    170 		mutex_enter(&sc->sc_action_mutex);
    171 	err = umcpmio_send_report(sc, (uint8_t *)req, MCP2221_REQ_BUFFER_SIZE, (uint8_t *)res, sc->sc_cv_wait);
    172 	if (takemutex)
    173 		mutex_exit(&sc->sc_action_mutex);
    174 
    175 	return(err);
    176 }
    177 
    178 /* We call the dedicated function ALT3 everywhere */
    179 
    180 uint32_t
    181 umcpmio_sram_gpio_to_flags(uint8_t gp_setting)
    182 {
    183 	uint32_t r = 0;
    184 
    185 	switch (gp_setting & MCP2221_SRAM_PIN_TYPE_MASK) {
    186 	case MCP2221_SRAM_PIN_IS_DED:
    187 		r |= GPIO_PIN_ALT3;
    188 		break;
    189 	case MCP2221_SRAM_PIN_IS_ALT0:
    190 		r |= GPIO_PIN_ALT0;
    191 		break;
    192 	case MCP2221_SRAM_PIN_IS_ALT1:
    193 		r |= GPIO_PIN_ALT1;
    194 		break;
    195 	case MCP2221_SRAM_PIN_IS_ALT2:
    196 		r |= GPIO_PIN_ALT2;
    197 		break;
    198 	case MCP2221_SRAM_PIN_IS_GPIO:
    199 	default:
    200 		if ((gp_setting & MCP2221_SRAM_GPIO_TYPE_MASK) == MCP2221_SRAM_GPIO_INPUT)
    201 			r |= GPIO_PIN_INPUT;
    202 		else
    203 			r |= GPIO_PIN_OUTPUT;
    204 		break;
    205 	}
    206 
    207 	return(r);
    208 }
    209 
    210 void
    211 umcpmio_set_gpio_value_sram(struct mcp2221_set_sram_req *req, int pin, bool value)
    212 {
    213 	uint8_t *alter = NULL;
    214 	uint8_t *newvalue = NULL;
    215 
    216 	if (pin >=0 && pin < MCP2221_NPINS) {
    217 		switch (pin) {
    218 		case 0:
    219 			alter = &req->alter_gpio_config;
    220 			newvalue = &req->gp0_settings;
    221 			break;
    222 		case 1:
    223 			alter = &req->alter_gpio_config;
    224 			newvalue = &req->gp1_settings;
    225 			break;
    226 		case 2:
    227 			alter = &req->alter_gpio_config;
    228 			newvalue = &req->gp2_settings;
    229 			break;
    230 		case 3:
    231 			alter = &req->alter_gpio_config;
    232 			newvalue = &req->gp3_settings;
    233 			break;
    234 		default:
    235 			break;
    236 		}
    237 
    238 		if (alter != NULL) {
    239 			*alter = MCP2221_SRAM_ALTER_GPIO;
    240 			if (value)
    241 				*newvalue |= MCP2221_SRAM_GPIO_HIGH;
    242 			else
    243 				*newvalue &= ~MCP2221_SRAM_GPIO_HIGH;
    244 		}
    245 	}
    246 }
    247 
    248 void
    249 umcpmio_set_gpio_dir_sram(struct mcp2221_set_sram_req *req, int pin, int flags)
    250 {
    251 	uint8_t *alter = NULL;
    252 	uint8_t *newvalue = NULL;
    253 
    254 	if (pin >=0 && pin < MCP2221_NPINS) {
    255 		switch (pin) {
    256 		case 0:
    257 			alter = &req->alter_gpio_config;
    258 			newvalue = &req->gp0_settings;
    259 			break;
    260 		case 1:
    261 			alter = &req->alter_gpio_config;
    262 			newvalue = &req->gp1_settings;
    263 			break;
    264 		case 2:
    265 			alter = &req->alter_gpio_config;
    266 			newvalue = &req->gp2_settings;
    267 			break;
    268 		case 3:
    269 			alter = &req->alter_gpio_config;
    270 			newvalue = &req->gp3_settings;
    271 			break;
    272 		default:
    273 			break;
    274 		}
    275 
    276 		if (alter != NULL) {
    277 			*alter = MCP2221_SRAM_ALTER_GPIO;
    278 			if (flags & GPIO_PIN_INPUT)
    279 				*newvalue |= MCP2221_SRAM_GPIO_INPUT;
    280 			else
    281 				*newvalue &= ~MCP2221_SRAM_GPIO_INPUT;
    282 		}
    283 	}
    284 }
    285 
    286 void
    287 umcpmio_set_gpio_designation_sram(struct mcp2221_set_sram_req *req, int pin, int flags)
    288 {
    289 	uint8_t *alter = NULL;
    290 	uint8_t *newvalue = NULL;
    291 	uint32_t altmask = GPIO_PIN_ALT0 | GPIO_PIN_ALT1 | GPIO_PIN_ALT2 | GPIO_PIN_ALT3;
    292 
    293 	if (pin >=0 && pin < MCP2221_NPINS) {
    294 		switch (pin) {
    295 		case 0:
    296 			alter = &req->alter_gpio_config;
    297 			newvalue = &req->gp0_settings;
    298 			break;
    299 		case 1:
    300 			alter = &req->alter_gpio_config;
    301 			newvalue = &req->gp1_settings;
    302 			break;
    303 		case 2:
    304 			alter = &req->alter_gpio_config;
    305 			newvalue = &req->gp2_settings;
    306 			break;
    307 		case 3:
    308 			alter = &req->alter_gpio_config;
    309 			newvalue = &req->gp3_settings;
    310 			break;
    311 		default:
    312 			break;
    313 		}
    314 
    315 		if (alter != NULL) {
    316 			int nv = *newvalue;
    317 
    318 			*alter = MCP2221_SRAM_ALTER_GPIO;
    319 			nv &= 0xF8;
    320 
    321 			if (flags & (GPIO_PIN_OUTPUT|GPIO_PIN_INPUT)) {
    322 				nv |= MCP2221_SRAM_PIN_IS_GPIO;
    323 			} else {
    324 				switch (flags & altmask) {
    325 				case GPIO_PIN_ALT0:
    326 					nv |= MCP2221_SRAM_PIN_IS_ALT0;
    327 					break;
    328 				case GPIO_PIN_ALT1:
    329 					nv |= MCP2221_SRAM_PIN_IS_ALT1;
    330 					break;
    331 				case GPIO_PIN_ALT2:
    332 					nv |= MCP2221_SRAM_PIN_IS_ALT2;
    333 					break;
    334 					/* ALT3 will always be used as the dedicated function specific to the pin.
    335 					 * Not all of the pins will have the alt functions below #3.
    336 					 */
    337 				case GPIO_PIN_ALT3:
    338 					nv |= MCP2221_SRAM_PIN_IS_DED;
    339 					break;
    340 				default:
    341 					break;
    342 				}
    343 			}
    344 			*newvalue = nv;
    345 		}
    346 	}
    347 }
    348 
    349 void
    350 umcpmio_set_gpio_irq_sram(struct mcp2221_set_sram_req *req, int irqmode)
    351 {
    352 	req->alter_gpio_config = MCP2221_SRAM_ALTER_GPIO;
    353 
    354 	if (irqmode & (GPIO_INTR_POS_EDGE | GPIO_INTR_DOUBLE_EDGE)) {
    355 		req->irq_config |= MCP2221_SRAM_ALTER_IRQ | MCP2221_SRAM_ALTER_POS_EDGE | MCP2221_SRAM_ENABLE_POS_EDGE | MCP2221_SRAM_CLEAR_IRQ;
    356 	}
    357 	if (irqmode & (GPIO_INTR_NEG_EDGE | GPIO_INTR_DOUBLE_EDGE)) {
    358 		req->irq_config |= MCP2221_SRAM_ALTER_IRQ | MCP2221_SRAM_ALTER_NEG_EDGE | MCP2221_SRAM_ENABLE_NEG_EDGE | MCP2221_SRAM_CLEAR_IRQ;
    359 	}
    360 
    361 	if (req->irq_config != 0) {
    362 		req->gp1_settings = MCP2221_SRAM_PIN_IS_ALT2;
    363 	} else {
    364 		req->irq_config = MCP2221_SRAM_ALTER_IRQ | MCP2221_SRAM_CLEAR_IRQ;
    365 		req->gp1_settings = MCP2221_SRAM_PIN_IS_GPIO | MCP2221_SRAM_GPIO_INPUT;
    366 	}
    367 }
    368 
    369 /* It is unfortunate that the GET and PUT requests are not symertric.  That is,
    370  * the bits sort of line up but not quite between a GET and PUT. */
    371 
    372 static struct umcpmio_mapping_put umcpmio_vref_puts[] = {
    373 	{
    374 		.tname = "4.096V",
    375 		.mask = 0x06 | 0x01,
    376 	},
    377 	{
    378 		.tname = "2.048V",
    379 		.mask = 0x04 | 0x01,
    380 	},
    381 	{
    382 		.tname = "1.024V",
    383 		.mask = 0x02 | 0x01,
    384 	},
    385 	{
    386 		.tname = "OFF",
    387 		.mask = 0x00 | 0x01,
    388 	},
    389 	{
    390 		.tname = "VDD",
    391 		.mask = 0x00,
    392 	}
    393 };
    394 
    395 void
    396 umcpmio_set_dac_vref(struct mcp2221_set_sram_req *req, char *newvref)
    397 {
    398 	int i;
    399 
    400 	for (i = 0; i < __arraycount(umcpmio_vref_puts); i++) {
    401 		if (strncmp(newvref, umcpmio_vref_puts[i].tname,
    402 		    UMCPMIO_VREF_NAME) == 0) {
    403 			break;
    404 		}
    405 	}
    406 
    407 	if (i == __arraycount(umcpmio_vref_puts))
    408 		return;
    409 
    410 	req->dac_voltage_reference |= umcpmio_vref_puts[i].mask | MCP2221_SRAM_CHANGE_DAC_VREF;
    411 }
    412 
    413 int
    414 umcpmio_set_dac_vref_one(struct umcpmio_softc *sc, char *newvref, bool takemutex)
    415 {
    416 	struct mcp2221_set_sram_req req;
    417 	struct mcp2221_set_sram_res res;
    418 	int err = 0;
    419 
    420 	memset(&req, 0, MCP2221_REQ_BUFFER_SIZE);
    421 	umcpmio_set_dac_vref(&req, newvref);
    422 	err = umcpmio_put_sram(sc, &req, &res, takemutex);
    423 
    424 	return err;
    425 }
    426 
    427 void
    428 umcpmio_set_dac_value(struct mcp2221_set_sram_req *req, uint8_t newvalue)
    429 {
    430 	req->set_dac_output_value |= (newvalue & MCP2221_SRAM_DAC_VALUE_MASK) | MCP2221_SRAM_CHANGE_DAC_VREF;
    431 }
    432 
    433 int
    434 umcpmio_set_dac_value_one(struct umcpmio_softc *sc, uint8_t newvalue, bool takemutex)
    435 {
    436 	struct mcp2221_set_sram_req req;
    437 	struct mcp2221_set_sram_res res;
    438 	int err = 0;
    439 
    440 	memset(&req, 0, MCP2221_REQ_BUFFER_SIZE);
    441 	umcpmio_set_dac_value(&req, newvalue);
    442 	err = umcpmio_put_sram(sc, &req, &res, takemutex);
    443 
    444 	return err;
    445 }
    446 
    447 void
    448 umcpmio_set_adc_vref(struct mcp2221_set_sram_req *req, char *newvref)
    449 {
    450 	int i;
    451 
    452 	for (i = 0; i < __arraycount(umcpmio_vref_puts); i++) {
    453 		if (strncmp(newvref, umcpmio_vref_puts[i].tname,
    454 		    UMCPMIO_VREF_NAME) == 0) {
    455 			break;
    456 		}
    457 	}
    458 
    459 	if (i == __arraycount(umcpmio_vref_puts))
    460 		return;
    461 
    462 	req->adc_voltage_reference |= umcpmio_vref_puts[i].mask | MCP2221_SRAM_CHANGE_ADC_VREF;
    463 }
    464 
    465 int
    466 umcpmio_set_adc_vref_one(struct umcpmio_softc *sc, char *newvref, bool takemutex)
    467 {
    468 	struct mcp2221_set_sram_req req;
    469 	struct mcp2221_set_sram_res res;
    470 	int err = 0;
    471 
    472 	memset(&req, 0, MCP2221_REQ_BUFFER_SIZE);
    473 	umcpmio_set_adc_vref(&req, newvref);
    474 	err = umcpmio_put_sram(sc, &req, &res, takemutex);
    475 
    476 	return err;
    477 }
    478 
    479 static struct umcpmio_mapping_put umcpmio_dc_puts[] = {
    480 	{
    481 		.tname = "75%",
    482 		.mask = MCP2221_SRAM_GPIO_CLOCK_DC_75,
    483 	},
    484 	{
    485 		.tname = "50%",
    486 		.mask = MCP2221_SRAM_GPIO_CLOCK_DC_50,
    487 	},
    488 	{
    489 		.tname = "25%",
    490 		.mask = MCP2221_SRAM_GPIO_CLOCK_DC_25,
    491 	},
    492 	{
    493 		.tname = "0%",
    494 		.mask = MCP2221_SRAM_GPIO_CLOCK_DC_0,
    495 	}
    496 };
    497 
    498 void
    499 umcpmio_set_gpioclock_dc(struct mcp2221_set_sram_req *req, char *new_dc)
    500 {
    501 	int i;
    502 
    503 	for (i = 0; i < __arraycount(umcpmio_dc_puts); i++) {
    504 		if (strncmp(new_dc, umcpmio_dc_puts[i].tname,
    505 		    UMCPMIO_VREF_NAME) == 0) {
    506 			break;
    507 		}
    508 	}
    509 
    510 	if (i == __arraycount(umcpmio_dc_puts))
    511 		return;
    512 
    513 	req->clock_output_divider |= umcpmio_dc_puts[i].mask;
    514 }
    515 
    516 int
    517 umcpmio_set_gpioclock_dc_one(struct umcpmio_softc *sc, char *new_dutycycle, bool takemutex)
    518 {
    519 	struct mcp2221_get_sram_res current_sram_res;
    520 	struct mcp2221_set_sram_req req;
    521 	struct mcp2221_set_sram_res res;
    522 	int err = 0;
    523 
    524 	err = umcpmio_get_sram(sc, &current_sram_res, takemutex);
    525 	if (! err) {
    526 		memset(&req, 0, MCP2221_REQ_BUFFER_SIZE);
    527 		umcpmio_set_gpioclock_dc(&req, new_dutycycle);
    528 		DPRINTF(("umcpmio_set_gpioclock_dc_one: req.clock_output_divider=%02x,current mask=%02x\n",req.clock_output_divider,current_sram_res.clock_divider & MCP2221_SRAM_GPIO_CLOCK_CD_MASK));
    529 		req.clock_output_divider |= (current_sram_res.clock_divider & MCP2221_SRAM_GPIO_CLOCK_CD_MASK) | MCP2221_SRAM_GPIO_CHANGE_DCCD;
    530 		DPRINTF(("umcpmio_set_gpioclock_dc_one: SET req.clock_output_divider=%02x\n",req.clock_output_divider));
    531 		err = umcpmio_put_sram(sc, &req, &res, takemutex);
    532 	}
    533 
    534 	return err;
    535 }
    536 
    537 static struct umcpmio_mapping_put umcpmio_cd_puts[] = {
    538 	{
    539 		.tname = "375kHz",
    540 		.mask = MCP2221_SRAM_GPIO_CLOCK_CD_375KHZ,
    541 	},
    542 	{
    543 		.tname = "750kHz",
    544 		.mask = MCP2221_SRAM_GPIO_CLOCK_CD_750KHZ,
    545 	},
    546 	{
    547 		.tname = "1.5MHz",
    548 		.mask = MCP2221_SRAM_GPIO_CLOCK_CD_1P5MHZ,
    549 	},
    550 	{
    551 		.tname = "3MHz",
    552 		.mask = MCP2221_SRAM_GPIO_CLOCK_CD_3MHZ,
    553 	},
    554 	{
    555 		.tname = "6MHz",
    556 		.mask = MCP2221_SRAM_GPIO_CLOCK_CD_6MHZ,
    557 	},
    558 	{
    559 		.tname = "12MHz",
    560 		.mask = MCP2221_SRAM_GPIO_CLOCK_CD_12MHZ,
    561 	},
    562 	{
    563 		.tname = "24MHz",
    564 		.mask = MCP2221_SRAM_GPIO_CLOCK_CD_24MHZ,
    565 	}
    566 };
    567 
    568 void
    569 umcpmio_set_gpioclock_cd(struct mcp2221_set_sram_req *req, char *new_cd)
    570 {
    571 	int i;
    572 
    573 	for (i = 0; i < __arraycount(umcpmio_cd_puts); i++) {
    574 		if (strncmp(new_cd, umcpmio_cd_puts[i].tname,
    575 		    UMCPMIO_CD_NAME) == 0) {
    576 			break;
    577 		}
    578 	}
    579 
    580 	if (i == __arraycount(umcpmio_cd_puts))
    581 		return;
    582 
    583 	req->clock_output_divider |= umcpmio_cd_puts[i].mask;
    584 }
    585 
    586 int
    587 umcpmio_set_gpioclock_cd_one(struct umcpmio_softc *sc, char *new_clockdivider, bool takemutex)
    588 {
    589 	struct mcp2221_get_sram_res current_sram_res;
    590 	struct mcp2221_set_sram_req req;
    591 	struct mcp2221_set_sram_res res;
    592 	int err = 0;
    593 
    594 	err = umcpmio_get_sram(sc, &current_sram_res, takemutex);
    595 	if (! err) {
    596 		memset(&req, 0, MCP2221_REQ_BUFFER_SIZE);
    597 		umcpmio_set_gpioclock_cd(&req, new_clockdivider);
    598 		DPRINTF(("umcpmio_set_gpioclock_cd_one: req.clock_output_divider=%02x,current mask=%02x\n",req.clock_output_divider,current_sram_res.clock_divider & MCP2221_SRAM_GPIO_CLOCK_CD_MASK));
    599 		req.clock_output_divider |= (current_sram_res.clock_divider & MCP2221_SRAM_GPIO_CLOCK_DC_MASK) | MCP2221_SRAM_GPIO_CHANGE_DCCD;
    600 		DPRINTF(("umcpmio_set_gpioclock_cd_one: SET req.clock_output_divider=%02x\n",req.clock_output_divider));
    601 		err = umcpmio_put_sram(sc, &req, &res, takemutex);
    602 	}
    603 
    604 	return err;
    605 }
    606 
    607 int
    608 umcpmio_get_gpio_cfg(struct umcpmio_softc *sc,
    609     struct mcp2221_get_gpio_cfg_res *res, bool takemutex)
    610 {
    611 	struct mcp2221_get_gpio_cfg_req req;
    612 	int err = 0;
    613 
    614 	memset(&req, 0, MCP2221_REQ_BUFFER_SIZE);
    615 	req.cmd = MCP2221_CMD_GET_GPIO_CFG;
    616 
    617 	if (takemutex)
    618 		mutex_enter(&sc->sc_action_mutex);
    619 	err = umcpmio_send_report(sc, (uint8_t *)&req, MCP2221_REQ_BUFFER_SIZE, (uint8_t *)res, sc->sc_cv_wait);
    620 	if (takemutex)
    621 		mutex_exit(&sc->sc_action_mutex);
    622 
    623 	return(err);
    624 }
    625 
    626 int
    627 umcpmio_put_gpio_cfg(struct umcpmio_softc *sc,
    628     struct mcp2221_set_gpio_cfg_req *req, struct mcp2221_set_gpio_cfg_res *res,
    629     bool takemutex)
    630 {
    631 	int err = 0;
    632 
    633 	req->cmd = MCP2221_CMD_SET_GPIO_CFG;
    634 
    635 	if (takemutex)
    636 		mutex_enter(&sc->sc_action_mutex);
    637 	err = umcpmio_send_report(sc, (uint8_t *)req, MCP2221_REQ_BUFFER_SIZE, (uint8_t *)res, sc->sc_cv_wait);
    638 	if (takemutex)
    639 		mutex_exit(&sc->sc_action_mutex);
    640 
    641 	return(err);
    642 }
    643 
    644 /* So... if the pin isn't set to GPIO, just call the output LOW */
    645 
    646 int
    647 umcpmio_get_gpio_value(struct umcpmio_softc *sc,
    648     int pin, bool takemutex)
    649 {
    650 	struct mcp2221_get_gpio_cfg_res get_gpio_cfg_res;
    651 	int err = 0;
    652 	int r = GPIO_PIN_LOW;
    653 
    654 	err = umcpmio_get_gpio_cfg(sc, &get_gpio_cfg_res, takemutex);
    655 	if (! err) {
    656 		if (get_gpio_cfg_res.cmd == MCP2221_CMD_GET_GPIO_CFG &&
    657 		    get_gpio_cfg_res.completion == MCP2221_CMD_COMPLETE_OK) {
    658 			switch (pin) {
    659 			case 0:
    660 				if (get_gpio_cfg_res.gp0_pin_value != MCP2221_GPIO_CFG_VALUE_NOT_GPIO)
    661 					if (get_gpio_cfg_res.gp0_pin_value == 0x01)
    662 						r = GPIO_PIN_HIGH;
    663 				break;
    664 			case 1:
    665 				if (get_gpio_cfg_res.gp1_pin_value != MCP2221_GPIO_CFG_VALUE_NOT_GPIO)
    666 					if (get_gpio_cfg_res.gp1_pin_value == 0x01)
    667 						r = GPIO_PIN_HIGH;
    668 				break;
    669 			case 2:
    670 				if (get_gpio_cfg_res.gp2_pin_value != MCP2221_GPIO_CFG_VALUE_NOT_GPIO)
    671 					if (get_gpio_cfg_res.gp2_pin_value == 0x01)
    672 						r = GPIO_PIN_HIGH;
    673 				break;
    674 			case 3:
    675 				if (get_gpio_cfg_res.gp3_pin_value != MCP2221_GPIO_CFG_VALUE_NOT_GPIO)
    676 					if (get_gpio_cfg_res.gp3_pin_value == 0x01)
    677 						r = GPIO_PIN_HIGH;
    678 				break;
    679 			default:
    680 				break;
    681 			}
    682 		} else {
    683 			device_printf(sc->sc_dev, "umcpmio_get_gpio_value: wrong command or error: %02x %02x\n",
    684 			    get_gpio_cfg_res.cmd,
    685 			    get_gpio_cfg_res.completion);
    686 		}
    687 	}
    688 
    689 	return(r);
    690 }
    691 
    692 void
    693 umcpmio_set_gpio_value(struct mcp2221_set_gpio_cfg_req *req,
    694     int pin, bool value)
    695 {
    696 	uint8_t *alter = NULL;
    697 	uint8_t *newvalue = NULL;
    698 
    699 	if (pin >=0 && pin < MCP2221_NPINS) {
    700 		switch (pin) {
    701 		case 0:
    702 			alter = &req->alter_gp0_value;
    703 			newvalue = &req->new_gp0_value;
    704 			break;
    705 		case 1:
    706 			alter = &req->alter_gp1_value;
    707 			newvalue = &req->new_gp1_value;
    708 			break;
    709 		case 2:
    710 			alter = &req->alter_gp2_value;
    711 			newvalue = &req->new_gp2_value;
    712 			break;
    713 		case 3:
    714 			alter = &req->alter_gp3_value;
    715 			newvalue = &req->new_gp3_value;
    716 			break;
    717 		default:
    718 			break;
    719 		}
    720 
    721 		if (alter != NULL) {
    722 			*alter = MCP2221_GPIO_CFG_ALTER;
    723 			*newvalue = 0;
    724 			if (value)
    725 				*newvalue = 1;
    726 		}
    727 	}
    728 }
    729 
    730 int
    731 umcpmio_set_gpio_value_one(struct umcpmio_softc *sc,
    732     int pin, bool value, bool takemutex)
    733 {
    734 	int err = 0;
    735 	struct mcp2221_set_gpio_cfg_req req;
    736 	struct mcp2221_set_gpio_cfg_res res;
    737 
    738 	memset(&req, 0, MCP2221_REQ_BUFFER_SIZE);
    739 	umcpmio_set_gpio_value(&req, pin, value);
    740 	err = umcpmio_put_gpio_cfg(sc, &req, &res, takemutex);
    741 	if (! err) {
    742 		if (res.cmd == MCP2221_CMD_SET_GPIO_CFG &&
    743 		    res.completion == MCP2221_CMD_COMPLETE_OK) {
    744 		} else {
    745 			err = EIO;
    746 			device_printf(sc->sc_dev, "umcpmio_gpio_pin_write:  not the command desired, or error: %02x %02x\n",
    747 			    res.cmd,
    748 			    res.completion);
    749 		}
    750 	}
    751 
    752 	return(err);
    753 }
    754 
    755 int
    756 umcpmio_get_flash(struct umcpmio_softc *sc, uint8_t subcode,
    757     struct mcp2221_get_flash_res *res, bool takemutex)
    758 {
    759 	struct mcp2221_get_flash_req req;
    760 	int err = 0;
    761 
    762 	memset(&req, 0, MCP2221_REQ_BUFFER_SIZE);
    763 	req.cmd = MCP2221_CMD_GET_FLASH;
    764 
    765 	if (subcode < MCP2221_FLASH_SUBCODE_CS ||
    766 	    subcode > MCP2221_FLASH_SUBCODE_CHIPSN)
    767 		return(EINVAL);
    768 
    769 	req.subcode = subcode;
    770 
    771 	if (takemutex)
    772 		mutex_enter(&sc->sc_action_mutex);
    773 	err = umcpmio_send_report(sc, (uint8_t *)&req, MCP2221_REQ_BUFFER_SIZE, (uint8_t *)res, sc->sc_cv_wait);
    774 	if (takemutex)
    775 		mutex_exit(&sc->sc_action_mutex);
    776 
    777 	return(err);
    778 }
    779 
    780 int
    781 umcpmio_put_flash(struct umcpmio_softc *sc, struct mcp2221_put_flash_req *req,
    782     struct mcp2221_put_flash_res *res, bool takemutex)
    783 {
    784 	int err = 0;
    785 
    786 	req->cmd = MCP2221_CMD_SET_FLASH;
    787 
    788 	if (req->subcode < MCP2221_FLASH_SUBCODE_CS ||
    789 	    req->subcode > MCP2221_FLASH_SUBCODE_CHIPSN) {
    790 		DPRINTF(("umcpmio_put_flash: subcode out of range: subcode=%d\n",req->subcode));
    791 		return(EINVAL);
    792 	}
    793 
    794 	if (takemutex)
    795 		mutex_enter(&sc->sc_action_mutex);
    796 	err = umcpmio_send_report(sc, (uint8_t *)req, MCP2221_REQ_BUFFER_SIZE, (uint8_t *)res, sc->sc_cv_wait);
    797 	if (takemutex)
    798 		mutex_exit(&sc->sc_action_mutex);
    799 
    800 	return(err);
    801 }
    802 
    803