/* $NetBSD: umcpmio_subr.c,v 1.3 2025/03/25 20:38:27 riastradh Exp $ */
/*
* Copyright (c) 2024 Brad Spencer <
[email protected]>
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
#include <sys/cdefs.h>
__KERNEL_RCSID(0, "$NetBSD: umcpmio_subr.c,v 1.3 2025/03/25 20:38:27 riastradh Exp $");
#ifdef _KERNEL_OPT
#include "opt_usb.h"
#endif
#include <sys/param.h>
#include <sys/types.h>
#include <sys/conf.h>
#include <sys/device.h>
#include <sys/file.h>
#include <sys/kauth.h>
#include <sys/kernel.h>
#include <sys/kmem.h>
#include <sys/lwp.h>
#include <sys/sysctl.h>
#include <sys/systm.h>
#include <sys/tty.h>
#include <sys/vnode.h>
#include <dev/hid/hid.h>
#include <dev/usb/uhidev.h>
#include <dev/usb/usb.h>
#include <dev/usb/usbdevs.h>
#include <dev/usb/usbdi.h>
#include <dev/usb/usbdi_util.h>
#include <dev/usb/usbhid.h>
#include <dev/usb/umcpmio.h>
#include <dev/usb/umcpmio_subr.h>
#include <dev/usb/umcpmio_hid_reports.h>
int umcpmio_send_report(struct umcpmio_softc *, uint8_t *, size_t, uint8_t *,
int);
#define UMCPMIO_DEBUG 1
#ifdef UMCPMIO_DEBUG
#define DPRINTF(x) do { if (umcpmiodebug) printf x; } while (0)
#define DPRINTFN(n, x) do { if (umcpmiodebug > (n)) printf x; } while (0)
extern int umcpmiodebug;
#else
#define DPRINTF(x) __nothing
#define DPRINTFN(n,x) __nothing
#endif
/* Handy functions that do a bunch of things for the main driver code */
int
umcpmio_get_status(struct umcpmio_softc *sc,
struct mcp2221_status_res *res, bool takemutex)
{
struct mcp2221_status_req req;
int err = 0;
memset(&req, 0, MCP2221_REQ_BUFFER_SIZE);
req.cmd = MCP2221_CMD_STATUS;
if (takemutex)
mutex_enter(&sc->sc_action_mutex);
err = umcpmio_send_report(sc,
(uint8_t *)&req, MCP2221_REQ_BUFFER_SIZE,
(uint8_t *)res, sc->sc_cv_wait);
if (takemutex)
mutex_exit(&sc->sc_action_mutex);
return err;
}
void
umcpmio_set_i2c_speed(struct mcp2221_status_req *req, int flags)
{
int i2cbaud = MCP2221_DEFAULT_I2C_SPEED;
if (flags & I2C_F_SPEED)
i2cbaud = 400000;
req->set_i2c_speed = MCP2221_I2C_SET_SPEED;
if (i2cbaud <= 0)
i2cbaud = MCP2221_DEFAULT_I2C_SPEED;
/*
* Everyone and their brother seems to store the I2C divider like this,
* so do likewise
*/
req->i2c_clock_divider = (MCP2221_INTERNAL_CLOCK / i2cbaud) - 3;
}
int
umcpmio_put_status(struct umcpmio_softc *sc,
struct mcp2221_status_req *req, struct mcp2221_status_res *res,
bool takemutex)
{
int err = 0;
req->cmd = MCP2221_CMD_STATUS;
if (takemutex)
mutex_enter(&sc->sc_action_mutex);
err = umcpmio_send_report(sc,
(uint8_t *)req, MCP2221_REQ_BUFFER_SIZE,
(uint8_t *)res, sc->sc_cv_wait);
if (takemutex)
mutex_exit(&sc->sc_action_mutex);
return err;
}
int
umcpmio_set_i2c_speed_one(struct umcpmio_softc *sc,
int flags, bool takemutex)
{
int err = 0;
struct mcp2221_status_req req;
struct mcp2221_status_res res;
memset(&req, 0, MCP2221_REQ_BUFFER_SIZE);
umcpmio_set_i2c_speed(&req, flags);
err = umcpmio_put_status(sc, &req, &res, takemutex);
if (err)
goto out;
if (res.set_i2c_speed == MCP2221_I2C_SPEED_BUSY)
err = EBUSY;
out:
return err;
}
int
umcpmio_get_sram(struct umcpmio_softc *sc,
struct mcp2221_get_sram_res *res, bool takemutex)
{
struct mcp2221_get_sram_req req;
int err = 0;
memset(&req, 0, MCP2221_REQ_BUFFER_SIZE);
req.cmd = MCP2221_CMD_GET_SRAM;
if (takemutex)
mutex_enter(&sc->sc_action_mutex);
err = umcpmio_send_report(sc,
(uint8_t *)&req, MCP2221_REQ_BUFFER_SIZE,
(uint8_t *)res, sc->sc_cv_wait);
if (takemutex)
mutex_exit(&sc->sc_action_mutex);
return err;
}
int
umcpmio_put_sram(struct umcpmio_softc *sc,
struct mcp2221_set_sram_req *req, struct mcp2221_set_sram_res *res,
bool takemutex)
{
int err = 0;
req->cmd = MCP2221_CMD_SET_SRAM;
if (takemutex)
mutex_enter(&sc->sc_action_mutex);
err = umcpmio_send_report(sc,
(uint8_t *)req, MCP2221_REQ_BUFFER_SIZE,
(uint8_t *)res, sc->sc_cv_wait);
if (takemutex)
mutex_exit(&sc->sc_action_mutex);
return err;
}
/* We call the dedicated function ALT3 everywhere */
uint32_t
umcpmio_sram_gpio_to_flags(uint8_t gp_setting)
{
uint32_t r = 0;
switch (gp_setting & MCP2221_SRAM_PIN_TYPE_MASK) {
case MCP2221_SRAM_PIN_IS_DED:
r |= GPIO_PIN_ALT3;
break;
case MCP2221_SRAM_PIN_IS_ALT0:
r |= GPIO_PIN_ALT0;
break;
case MCP2221_SRAM_PIN_IS_ALT1:
r |= GPIO_PIN_ALT1;
break;
case MCP2221_SRAM_PIN_IS_ALT2:
r |= GPIO_PIN_ALT2;
break;
case MCP2221_SRAM_PIN_IS_GPIO:
default:
if ((gp_setting & MCP2221_SRAM_GPIO_TYPE_MASK) ==
MCP2221_SRAM_GPIO_INPUT)
r |= GPIO_PIN_INPUT;
else
r |= GPIO_PIN_OUTPUT;
break;
}
return r;
}
void
umcpmio_set_gpio_value_sram(struct mcp2221_set_sram_req *req, int pin,
bool value)
{
uint8_t *alter = NULL;
uint8_t *newvalue = NULL;
if (pin >= 0 && pin < MCP2221_NPINS) {
switch (pin) {
case 0:
alter = &req->alter_gpio_config;
newvalue = &req->gp0_settings;
break;
case 1:
alter = &req->alter_gpio_config;
newvalue = &req->gp1_settings;
break;
case 2:
alter = &req->alter_gpio_config;
newvalue = &req->gp2_settings;
break;
case 3:
alter = &req->alter_gpio_config;
newvalue = &req->gp3_settings;
break;
default:
break;
}
if (alter != NULL) {
*alter = MCP2221_SRAM_ALTER_GPIO;
if (value)
*newvalue |= MCP2221_SRAM_GPIO_HIGH;
else
*newvalue &= ~MCP2221_SRAM_GPIO_HIGH;
}
}
}
void
umcpmio_set_gpio_dir_sram(struct mcp2221_set_sram_req *req, int pin, int flags)
{
uint8_t *alter = NULL;
uint8_t *newvalue = NULL;
if (pin >= 0 && pin < MCP2221_NPINS) {
switch (pin) {
case 0:
alter = &req->alter_gpio_config;
newvalue = &req->gp0_settings;
break;
case 1:
alter = &req->alter_gpio_config;
newvalue = &req->gp1_settings;
break;
case 2:
alter = &req->alter_gpio_config;
newvalue = &req->gp2_settings;
break;
case 3:
alter = &req->alter_gpio_config;
newvalue = &req->gp3_settings;
break;
default:
break;
}
if (alter != NULL) {
*alter = MCP2221_SRAM_ALTER_GPIO;
if (flags & GPIO_PIN_INPUT)
*newvalue |= MCP2221_SRAM_GPIO_INPUT;
else
*newvalue &= ~MCP2221_SRAM_GPIO_INPUT;
}
}
}
void
umcpmio_set_gpio_designation_sram(struct mcp2221_set_sram_req *req, int pin,
int flags)
{
uint8_t *alter = NULL;
uint8_t *newvalue = NULL;
uint32_t altmask =
GPIO_PIN_ALT0 | GPIO_PIN_ALT1 | GPIO_PIN_ALT2 | GPIO_PIN_ALT3;
if (pin >= 0 && pin < MCP2221_NPINS) {
switch (pin) {
case 0:
alter = &req->alter_gpio_config;
newvalue = &req->gp0_settings;
break;
case 1:
alter = &req->alter_gpio_config;
newvalue = &req->gp1_settings;
break;
case 2:
alter = &req->alter_gpio_config;
newvalue = &req->gp2_settings;
break;
case 3:
alter = &req->alter_gpio_config;
newvalue = &req->gp3_settings;
break;
default:
break;
}
if (alter != NULL) {
int nv = *newvalue;
*alter = MCP2221_SRAM_ALTER_GPIO;
nv &= 0xF8;
if (flags & (GPIO_PIN_OUTPUT|GPIO_PIN_INPUT)) {
nv |= MCP2221_SRAM_PIN_IS_GPIO;
} else {
switch (flags & altmask) {
case GPIO_PIN_ALT0:
nv |= MCP2221_SRAM_PIN_IS_ALT0;
break;
case GPIO_PIN_ALT1:
nv |= MCP2221_SRAM_PIN_IS_ALT1;
break;
case GPIO_PIN_ALT2:
nv |= MCP2221_SRAM_PIN_IS_ALT2;
break;
/*
* ALT3 will always be used as
* the dedicated function
* specific to the pin. Not
* all of the pins will have
* the alt functions below #3.
*/
case GPIO_PIN_ALT3:
nv |= MCP2221_SRAM_PIN_IS_DED;
break;
default:
break;
}
}
*newvalue = nv;
}
}
}
void
umcpmio_set_gpio_irq_sram(struct mcp2221_set_sram_req *req, int irqmode)
{
req->alter_gpio_config = MCP2221_SRAM_ALTER_GPIO;
if (irqmode & (GPIO_INTR_POS_EDGE | GPIO_INTR_DOUBLE_EDGE)) {
req->irq_config |= MCP2221_SRAM_ALTER_IRQ |
MCP2221_SRAM_ALTER_POS_EDGE |
MCP2221_SRAM_ENABLE_POS_EDGE |
MCP2221_SRAM_CLEAR_IRQ;
}
if (irqmode & (GPIO_INTR_NEG_EDGE | GPIO_INTR_DOUBLE_EDGE)) {
req->irq_config |= MCP2221_SRAM_ALTER_IRQ |
MCP2221_SRAM_ALTER_NEG_EDGE |
MCP2221_SRAM_ENABLE_NEG_EDGE |
MCP2221_SRAM_CLEAR_IRQ;
}
if (req->irq_config != 0) {
req->gp1_settings = MCP2221_SRAM_PIN_IS_ALT2;
} else {
req->irq_config = MCP2221_SRAM_ALTER_IRQ |
MCP2221_SRAM_CLEAR_IRQ;
req->gp1_settings = MCP2221_SRAM_PIN_IS_GPIO |
MCP2221_SRAM_GPIO_INPUT;
}
}
/*
* It is unfortunate that the GET and PUT requests are not symertric. That is,
* the bits sort of line up but not quite between a GET and PUT.
*/
static struct umcpmio_mapping_put umcpmio_vref_puts[] = {
{
.tname = "4.096V",
.mask = 0x06 | 0x01,
},
{
.tname = "2.048V",
.mask = 0x04 | 0x01,
},
{
.tname = "1.024V",
.mask = 0x02 | 0x01,
},
{
.tname = "OFF",
.mask = 0x00 | 0x01,
},
{
.tname = "VDD",
.mask = 0x00,
}
};
void
umcpmio_set_dac_vref(struct mcp2221_set_sram_req *req, char *newvref)
{
int i;
for (i = 0; i < __arraycount(umcpmio_vref_puts); i++) {
if (strncmp(newvref, umcpmio_vref_puts[i].tname,
UMCPMIO_VREF_NAME) == 0) {
break;
}
}
if (i == __arraycount(umcpmio_vref_puts))
return;
req->dac_voltage_reference |= umcpmio_vref_puts[i].mask |
MCP2221_SRAM_CHANGE_DAC_VREF;
}
int
umcpmio_set_dac_vref_one(struct umcpmio_softc *sc, char *newvref,
bool takemutex)
{
struct mcp2221_set_sram_req req;
struct mcp2221_set_sram_res res;
int err = 0;
memset(&req, 0, MCP2221_REQ_BUFFER_SIZE);
umcpmio_set_dac_vref(&req, newvref);
err = umcpmio_put_sram(sc, &req, &res, takemutex);
return err;
}
void
umcpmio_set_dac_value(struct mcp2221_set_sram_req *req, uint8_t newvalue)
{
req->set_dac_output_value |= (newvalue & MCP2221_SRAM_DAC_VALUE_MASK) |
MCP2221_SRAM_CHANGE_DAC_VREF;
}
int
umcpmio_set_dac_value_one(struct umcpmio_softc *sc, uint8_t newvalue,
bool takemutex)
{
struct mcp2221_set_sram_req req;
struct mcp2221_set_sram_res res;
int err = 0;
memset(&req, 0, MCP2221_REQ_BUFFER_SIZE);
umcpmio_set_dac_value(&req, newvalue);
err = umcpmio_put_sram(sc, &req, &res, takemutex);
return err;
}
void
umcpmio_set_adc_vref(struct mcp2221_set_sram_req *req, char *newvref)
{
int i;
for (i = 0; i < __arraycount(umcpmio_vref_puts); i++) {
if (strncmp(newvref, umcpmio_vref_puts[i].tname,
UMCPMIO_VREF_NAME) == 0) {
break;
}
}
if (i == __arraycount(umcpmio_vref_puts))
return;
req->adc_voltage_reference |= umcpmio_vref_puts[i].mask |
MCP2221_SRAM_CHANGE_ADC_VREF;
}
int
umcpmio_set_adc_vref_one(struct umcpmio_softc *sc, char *newvref,
bool takemutex)
{
struct mcp2221_set_sram_req req;
struct mcp2221_set_sram_res res;
int err = 0;
memset(&req, 0, MCP2221_REQ_BUFFER_SIZE);
umcpmio_set_adc_vref(&req, newvref);
err = umcpmio_put_sram(sc, &req, &res, takemutex);
return err;
}
static struct umcpmio_mapping_put umcpmio_dc_puts[] = {
{
.tname = "75%",
.mask = MCP2221_SRAM_GPIO_CLOCK_DC_75,
},
{
.tname = "50%",
.mask = MCP2221_SRAM_GPIO_CLOCK_DC_50,
},
{
.tname = "25%",
.mask = MCP2221_SRAM_GPIO_CLOCK_DC_25,
},
{
.tname = "0%",
.mask = MCP2221_SRAM_GPIO_CLOCK_DC_0,
}
};
void
umcpmio_set_gpioclock_dc(struct mcp2221_set_sram_req *req, char *new_dc)
{
int i;
for (i = 0; i < __arraycount(umcpmio_dc_puts); i++) {
if (strncmp(new_dc, umcpmio_dc_puts[i].tname,
UMCPMIO_VREF_NAME) == 0) {
break;
}
}
if (i == __arraycount(umcpmio_dc_puts))
return;
req->clock_output_divider |= umcpmio_dc_puts[i].mask;
}
int
umcpmio_set_gpioclock_dc_one(struct umcpmio_softc *sc, char *new_dutycycle,
bool takemutex)
{
struct mcp2221_get_sram_res current_sram_res;
struct mcp2221_set_sram_req req;
struct mcp2221_set_sram_res res;
int err = 0;
err = umcpmio_get_sram(sc, ¤t_sram_res, takemutex);
if (err)
goto out;
memset(&req, 0, MCP2221_REQ_BUFFER_SIZE);
umcpmio_set_gpioclock_dc(&req, new_dutycycle);
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)));
req.clock_output_divider |=
(current_sram_res.clock_divider &
MCP2221_SRAM_GPIO_CLOCK_CD_MASK) |
MCP2221_SRAM_GPIO_CHANGE_DCCD;
DPRINTF(("umcpmio_set_gpioclock_dc_one:"
" SET req.clock_output_divider=%02x\n",
req.clock_output_divider));
err = umcpmio_put_sram(sc, &req, &res, takemutex);
out:
return err;
}
static struct umcpmio_mapping_put umcpmio_cd_puts[] = {
{
.tname = "375kHz",
.mask = MCP2221_SRAM_GPIO_CLOCK_CD_375KHZ,
},
{
.tname = "750kHz",
.mask = MCP2221_SRAM_GPIO_CLOCK_CD_750KHZ,
},
{
.tname = "1.5MHz",
.mask = MCP2221_SRAM_GPIO_CLOCK_CD_1P5MHZ,
},
{
.tname = "3MHz",
.mask = MCP2221_SRAM_GPIO_CLOCK_CD_3MHZ,
},
{
.tname = "6MHz",
.mask = MCP2221_SRAM_GPIO_CLOCK_CD_6MHZ,
},
{
.tname = "12MHz",
.mask = MCP2221_SRAM_GPIO_CLOCK_CD_12MHZ,
},
{
.tname = "24MHz",
.mask = MCP2221_SRAM_GPIO_CLOCK_CD_24MHZ,
}
};
void
umcpmio_set_gpioclock_cd(struct mcp2221_set_sram_req *req, char *new_cd)
{
int i;
for (i = 0; i < __arraycount(umcpmio_cd_puts); i++) {
if (strncmp(new_cd, umcpmio_cd_puts[i].tname,
UMCPMIO_CD_NAME) == 0) {
break;
}
}
if (i == __arraycount(umcpmio_cd_puts))
return;
req->clock_output_divider |= umcpmio_cd_puts[i].mask;
}
int
umcpmio_set_gpioclock_cd_one(struct umcpmio_softc *sc, char *new_clockdivider,
bool takemutex)
{
struct mcp2221_get_sram_res current_sram_res;
struct mcp2221_set_sram_req req;
struct mcp2221_set_sram_res res;
int err = 0;
err = umcpmio_get_sram(sc, ¤t_sram_res, takemutex);
if (err)
goto out;
memset(&req, 0, MCP2221_REQ_BUFFER_SIZE);
umcpmio_set_gpioclock_cd(&req, new_clockdivider);
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)));
req.clock_output_divider |=
(current_sram_res.clock_divider &
MCP2221_SRAM_GPIO_CLOCK_DC_MASK) |
MCP2221_SRAM_GPIO_CHANGE_DCCD;
DPRINTF(("umcpmio_set_gpioclock_cd_one:"
" SET req.clock_output_divider=%02x\n",
req.clock_output_divider));
err = umcpmio_put_sram(sc, &req, &res, takemutex);
out:
return err;
}
int
umcpmio_get_gpio_cfg(struct umcpmio_softc *sc,
struct mcp2221_get_gpio_cfg_res *res, bool takemutex)
{
struct mcp2221_get_gpio_cfg_req req;
int err = 0;
memset(&req, 0, MCP2221_REQ_BUFFER_SIZE);
req.cmd = MCP2221_CMD_GET_GPIO_CFG;
if (takemutex)
mutex_enter(&sc->sc_action_mutex);
err = umcpmio_send_report(sc,
(uint8_t *)&req, MCP2221_REQ_BUFFER_SIZE,
(uint8_t *)res, sc->sc_cv_wait);
if (takemutex)
mutex_exit(&sc->sc_action_mutex);
return err;
}
int
umcpmio_put_gpio_cfg(struct umcpmio_softc *sc,
struct mcp2221_set_gpio_cfg_req *req, struct mcp2221_set_gpio_cfg_res *res,
bool takemutex)
{
int err = 0;
req->cmd = MCP2221_CMD_SET_GPIO_CFG;
if (takemutex)
mutex_enter(&sc->sc_action_mutex);
err = umcpmio_send_report(sc,
(uint8_t *)req, MCP2221_REQ_BUFFER_SIZE,
(uint8_t *)res, sc->sc_cv_wait);
if (takemutex)
mutex_exit(&sc->sc_action_mutex);
return err;
}
/* So... if the pin isn't set to GPIO, just call the output LOW */
int
umcpmio_get_gpio_value(struct umcpmio_softc *sc,
int pin, bool takemutex)
{
struct mcp2221_get_gpio_cfg_res get_gpio_cfg_res;
int err = 0;
int r = GPIO_PIN_LOW;
err = umcpmio_get_gpio_cfg(sc, &get_gpio_cfg_res, takemutex);
if (err)
goto out;
if (get_gpio_cfg_res.cmd != MCP2221_CMD_GET_GPIO_CFG ||
get_gpio_cfg_res.completion != MCP2221_CMD_COMPLETE_OK) {
device_printf(sc->sc_dev, "umcpmio_get_gpio_value:"
" wrong command or error: %02x %02x\n",
get_gpio_cfg_res.cmd,
get_gpio_cfg_res.completion);
goto out;
}
switch (pin) {
case 0:
if (get_gpio_cfg_res.gp0_pin_value !=
MCP2221_GPIO_CFG_VALUE_NOT_GPIO)
if (get_gpio_cfg_res.gp0_pin_value == 0x01)
r = GPIO_PIN_HIGH;
break;
case 1:
if (get_gpio_cfg_res.gp1_pin_value !=
MCP2221_GPIO_CFG_VALUE_NOT_GPIO)
if (get_gpio_cfg_res.gp1_pin_value == 0x01)
r = GPIO_PIN_HIGH;
break;
case 2:
if (get_gpio_cfg_res.gp2_pin_value !=
MCP2221_GPIO_CFG_VALUE_NOT_GPIO)
if (get_gpio_cfg_res.gp2_pin_value == 0x01)
r = GPIO_PIN_HIGH;
break;
case 3:
if (get_gpio_cfg_res.gp3_pin_value !=
MCP2221_GPIO_CFG_VALUE_NOT_GPIO)
if (get_gpio_cfg_res.gp3_pin_value == 0x01)
r = GPIO_PIN_HIGH;
break;
default:
break;
}
out:
return r;
}
void
umcpmio_set_gpio_value(struct mcp2221_set_gpio_cfg_req *req,
int pin, bool value)
{
uint8_t *alter = NULL;
uint8_t *newvalue = NULL;
if (pin < 0 || pin >= MCP2221_NPINS)
return;
switch (pin) {
case 0:
alter = &req->alter_gp0_value;
newvalue = &req->new_gp0_value;
break;
case 1:
alter = &req->alter_gp1_value;
newvalue = &req->new_gp1_value;
break;
case 2:
alter = &req->alter_gp2_value;
newvalue = &req->new_gp2_value;
break;
case 3:
alter = &req->alter_gp3_value;
newvalue = &req->new_gp3_value;
break;
default:
return;
}
*alter = MCP2221_GPIO_CFG_ALTER;
*newvalue = 0;
if (value)
*newvalue = 1;
}
int
umcpmio_set_gpio_value_one(struct umcpmio_softc *sc,
int pin, bool value, bool takemutex)
{
int err = 0;
struct mcp2221_set_gpio_cfg_req req;
struct mcp2221_set_gpio_cfg_res res;
memset(&req, 0, MCP2221_REQ_BUFFER_SIZE);
umcpmio_set_gpio_value(&req, pin, value);
err = umcpmio_put_gpio_cfg(sc, &req, &res, takemutex);
if (err)
goto out;
if (res.cmd != MCP2221_CMD_SET_GPIO_CFG ||
res.completion != MCP2221_CMD_COMPLETE_OK) {
err = EIO;
device_printf(sc->sc_dev, "umcpmio_gpio_pin_write:"
" not the command desired, or error: %02x %02x\n",
res.cmd,
res.completion);
}
out:
return err;
}
int
umcpmio_get_flash(struct umcpmio_softc *sc, uint8_t subcode,
struct mcp2221_get_flash_res *res, bool takemutex)
{
struct mcp2221_get_flash_req req;
int err = 0;
memset(&req, 0, MCP2221_REQ_BUFFER_SIZE);
req.cmd = MCP2221_CMD_GET_FLASH;
if (subcode < MCP2221_FLASH_SUBCODE_CS ||
subcode > MCP2221_FLASH_SUBCODE_CHIPSN)
return EINVAL;
req.subcode = subcode;
if (takemutex)
mutex_enter(&sc->sc_action_mutex);
err = umcpmio_send_report(sc,
(uint8_t *)&req, MCP2221_REQ_BUFFER_SIZE,
(uint8_t *)res, sc->sc_cv_wait);
if (takemutex)
mutex_exit(&sc->sc_action_mutex);
return err;
}
int
umcpmio_put_flash(struct umcpmio_softc *sc, struct mcp2221_put_flash_req *req,
struct mcp2221_put_flash_res *res, bool takemutex)
{
int err = 0;
req->cmd = MCP2221_CMD_SET_FLASH;
if (req->subcode < MCP2221_FLASH_SUBCODE_CS ||
req->subcode > MCP2221_FLASH_SUBCODE_CHIPSN) {
DPRINTF(("umcpmio_put_flash: subcode out of range:"
" subcode=%d\n",
req->subcode));
return EINVAL;
}
if (takemutex)
mutex_enter(&sc->sc_action_mutex);
err = umcpmio_send_report(sc,
(uint8_t *)req, MCP2221_REQ_BUFFER_SIZE,
(uint8_t *)res, sc->sc_cv_wait);
if (takemutex)
mutex_exit(&sc->sc_action_mutex);
return err;
}