#include <sys/cdefs.h>
__KERNEL_RCSID(0, "$NetBSD: umcpmio_gpio.c,v 1.2 2025/12/12 17:49:35 andvar Exp $");
#ifdef _KERNEL_OPT
#include "opt_usb.h"
#endif
#include <sys/param.h>
#include <sys/types.h>
#include <sys/gpio.h>
#include <dev/gpio/gpiovar.h>
#include <dev/usb/umcpmio.h>
#include <dev/usb/umcpmio_hid_reports.h>
#include <dev/usb/umcpmio_transport.h>
#include <dev/usb/umcpmio_gpio.h>
#include <dev/usb/umcpmio_subr.h>
#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
static uint32_t
mcp2210_counter_gp_to_flags(uint8_t other_settings)
{
uint32_t r = 0;
switch ((other_settings >> 1) & 0x07) {
case MCP2210_COUNTER_FALLING_EDGE:
r |= GPIO_PIN_ALT3;
break;
case MCP2210_COUNTER_RISING_EDGE:
r |= GPIO_PIN_ALT4;
break;
case MCP2210_COUNTER_LOW_PULSE:
r |= GPIO_PIN_ALT5;
break;
case MCP2210_COUNTER_HIGH_PULSE:
r |= GPIO_PIN_ALT6;
break;
case MCP2210_COUNTER_OFF:
default:
printf("mcp2210_counter_gp_to_flags: Unhandled flag on counter pin: 0x%02x\n", other_settings);
}
return r;
}
static uint32_t
mcp2210_sram_gpio_to_flags(uint8_t *gp_settings, int pin)
{
uint32_t r = 0;
switch (gp_settings[pin]) {
case MCP2210_PIN_IS_ALT0:
r |= GPIO_PIN_ALT0;
break;
case MCP2210_PIN_IS_DED:
if (pin == 6) {
r |= mcp2210_counter_gp_to_flags(gp_settings[11]);
} else {
r |= GPIO_PIN_ALT3;
}
break;
case MCP2210_PIN_IS_GPIO:
default:
if (pin < 8) {
if (gp_settings[9] & (1 << pin))
r |= GPIO_PIN_INPUT;
else
r |= GPIO_PIN_OUTPUT;
} else {
r |= GPIO_PIN_INPUT;
}
break;
}
return r;
}
static uint32_t
mcp2221_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;
}
static void
mcp2221_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;
}
}
}
static int
mcp2210_get_gpio_sram(struct umcpmio_softc *sc,
struct mcp2210_get_gpio_sram_res *res)
{
struct mcp2210_get_gpio_sram_req req;
int err = 0;
memset(&req, 0, MCP2210_REQ_BUFFER_SIZE);
req.cmd = MCP2210_CMD_GET_GPIO_SRAM;
KASSERT(mutex_owned(&sc->sc_action_mutex));
err = umcpmio_send_report(sc,
(uint8_t *) & req, MCP2210_REQ_BUFFER_SIZE,
(uint8_t *) res, sc->sc_cv_wait);
err = mcp2210_decode_errors(req.cmd, err, res->completion);
return err;
}
static int
mcp2210_set_gpio_sram(struct umcpmio_softc *sc,
struct mcp2210_set_gpio_sram_req *req, struct mcp2210_set_gpio_sram_res *res)
{
int err = 0;
req->cmd = MCP2210_CMD_SET_GPIO_SRAM;
KASSERT(mutex_owned(&sc->sc_action_mutex));
err = umcpmio_send_report(sc,
(uint8_t *) req, MCP2210_REQ_BUFFER_SIZE,
(uint8_t *) res, sc->sc_cv_wait);
err = mcp2210_decode_errors(req->cmd, err, res->completion);
return err;
}
static int
mcp2210_get_gpio_dir_sram(struct umcpmio_softc *sc,
struct mcp2210_get_gpio_dir_res *res)
{
struct mcp2210_get_gpio_dir_req req;
int err = 0;
memset(&req, 0, MCP2210_REQ_BUFFER_SIZE);
req.cmd = MCP2210_CMD_GET_GPIO_DIR_SRAM;
KASSERT(mutex_owned(&sc->sc_action_mutex));
err = umcpmio_send_report(sc,
(uint8_t *) & req, MCP2210_REQ_BUFFER_SIZE,
(uint8_t *) res, sc->sc_cv_wait);
err = mcp2210_decode_errors(req.cmd, err, res->completion);
return err;
}
static int
mcp2210_get_gpio_value_sram(struct umcpmio_softc *sc,
struct mcp2210_get_gpio_value_res *res)
{
struct mcp2210_get_gpio_value_req req;
int err = 0;
memset(&req, 0, MCP2210_REQ_BUFFER_SIZE);
req.cmd = MCP2210_CMD_GET_GPIO_VAL_SRAM;
KASSERT(mutex_owned(&sc->sc_action_mutex));
err = umcpmio_send_report(sc,
(uint8_t *) & req, MCP2210_REQ_BUFFER_SIZE,
(uint8_t *) res, sc->sc_cv_wait);
err = mcp2210_decode_errors(req.cmd, err, res->completion);
return err;
}
static int
mcp2210_set_gpio_dir_sram(struct umcpmio_softc *sc,
struct mcp2210_set_gpio_dir_req *req, struct mcp2210_set_gpio_dir_res *res)
{
int err = 0;
req->cmd = MCP2210_CMD_SET_GPIO_DIR_SRAM;
KASSERT(mutex_owned(&sc->sc_action_mutex));
err = umcpmio_send_report(sc,
(uint8_t *) req, MCP2210_REQ_BUFFER_SIZE,
(uint8_t *) res, sc->sc_cv_wait);
err = mcp2210_decode_errors(req->cmd, err, res->completion);
return err;
}
static int
mcp2210_set_gpio_value_sram(struct umcpmio_softc *sc,
struct mcp2210_set_gpio_value_req *req, struct mcp2210_set_gpio_value_res *res)
{
int err = 0;
req->cmd = MCP2210_CMD_SET_GPIO_VAL_SRAM;
KASSERT(mutex_owned(&sc->sc_action_mutex));
err = umcpmio_send_report(sc,
(uint8_t *) req, MCP2210_REQ_BUFFER_SIZE,
(uint8_t *) res, sc->sc_cv_wait);
err = mcp2210_decode_errors(req->cmd, err, res->completion);
return err;
}
static void
mcp2221_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;
case GPIO_PIN_ALT3:
nv |= MCP2221_SRAM_PIN_IS_DED;
break;
default:
break;
}
}
*newvalue = nv;
}
}
}
static struct umcpmio_mapping_put mcp2221_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
mcp2221_set_dac_vref(struct mcp2221_set_sram_req *req, char *newvref)
{
int i;
for (i = 0; i < __arraycount(mcp2221_vref_puts); i++) {
if (strncmp(newvref, mcp2221_vref_puts[i].tname,
MCP2221_VREF_NAME) == 0) {
break;
}
}
if (i == __arraycount(mcp2221_vref_puts))
return;
req->dac_voltage_reference |= mcp2221_vref_puts[i].mask |
MCP2221_SRAM_CHANGE_DAC_VREF;
}
int
mcp2221_set_dac_vref_one(struct umcpmio_softc *sc, char *newvref)
{
struct mcp2221_set_sram_req req;
struct mcp2221_set_sram_res res;
int err = 0;
KASSERT(mutex_owned(&sc->sc_action_mutex));
memset(&req, 0, MCP2221_REQ_BUFFER_SIZE);
mcp2221_set_dac_vref(&req, newvref);
err = mcp2221_put_sram(sc, &req, &res);
return err;
}
void
mcp2221_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
mcp2221_set_dac_value_one(struct umcpmio_softc *sc, uint8_t newvalue)
{
struct mcp2221_set_sram_req req;
struct mcp2221_set_sram_res res;
int err = 0;
KASSERT(mutex_owned(&sc->sc_action_mutex));
memset(&req, 0, MCP2221_REQ_BUFFER_SIZE);
mcp2221_set_dac_value(&req, newvalue);
err = mcp2221_put_sram(sc, &req, &res);
return err;
}
void
mcp2221_set_adc_vref(struct mcp2221_set_sram_req *req, char *newvref)
{
int i;
for (i = 0; i < __arraycount(mcp2221_vref_puts); i++) {
if (strncmp(newvref, mcp2221_vref_puts[i].tname,
MCP2221_VREF_NAME) == 0) {
break;
}
}
if (i == __arraycount(mcp2221_vref_puts))
return;
req->adc_voltage_reference |= mcp2221_vref_puts[i].mask |
MCP2221_SRAM_CHANGE_ADC_VREF;
}
int
mcp2221_set_adc_vref_one(struct umcpmio_softc *sc, char *newvref)
{
struct mcp2221_set_sram_req req;
struct mcp2221_set_sram_res res;
int err = 0;
KASSERT(mutex_owned(&sc->sc_action_mutex));
memset(&req, 0, MCP2221_REQ_BUFFER_SIZE);
mcp2221_set_adc_vref(&req, newvref);
err = mcp2221_put_sram(sc, &req, &res);
return err;
}
static struct umcpmio_mapping_put mcp2221_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
mcp2221_set_gpioclock_dc(struct mcp2221_set_sram_req *req, char *new_dc)
{
int i;
for (i = 0; i < __arraycount(mcp2221_dc_puts); i++) {
if (strncmp(new_dc, mcp2221_dc_puts[i].tname,
MCP2221_DC_NAME) == 0) {
break;
}
}
if (i == __arraycount(mcp2221_dc_puts))
return;
req->clock_output_divider |= mcp2221_dc_puts[i].mask;
}
int
mcp2221_set_gpioclock_dc_one(struct umcpmio_softc *sc, char *new_dutycycle)
{
struct mcp2221_get_sram_res current_sram_res;
struct mcp2221_set_sram_req req;
struct mcp2221_set_sram_res res;
int err = 0;
KASSERT(mutex_owned(&sc->sc_action_mutex));
err = mcp2221_get_sram(sc, ¤t_sram_res);
if (err)
goto out;
memset(&req, 0, MCP2221_REQ_BUFFER_SIZE);
mcp2221_set_gpioclock_dc(&req, new_dutycycle);
DPRINTF(("mcp2221_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(("mcp2221_set_gpioclock_dc_one:"
" SET req.clock_output_divider=%02x\n",
req.clock_output_divider));
err = mcp2221_put_sram(sc, &req, &res);
out:
return err;
}
static struct umcpmio_mapping_put mcp2221_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
mcp2221_set_gpioclock_cd(struct mcp2221_set_sram_req *req, char *new_cd)
{
int i;
for (i = 0; i < __arraycount(mcp2221_cd_puts); i++) {
if (strncmp(new_cd, mcp2221_cd_puts[i].tname,
MCP2221_CD_NAME) == 0) {
break;
}
}
if (i == __arraycount(mcp2221_cd_puts))
return;
req->clock_output_divider |= mcp2221_cd_puts[i].mask;
}
int
mcp2221_set_gpioclock_cd_one(struct umcpmio_softc *sc, char *new_clockdivider)
{
struct mcp2221_get_sram_res current_sram_res;
struct mcp2221_set_sram_req req;
struct mcp2221_set_sram_res res;
int err = 0;
KASSERT(mutex_owned(&sc->sc_action_mutex));
err = mcp2221_get_sram(sc, ¤t_sram_res);
if (err)
goto out;
memset(&req, 0, MCP2221_REQ_BUFFER_SIZE);
mcp2221_set_gpioclock_cd(&req, new_clockdivider);
DPRINTF(("mcp2221_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(("mcp2221_set_gpioclock_cd_one:"
" SET req.clock_output_divider=%02x\n",
req.clock_output_divider));
err = mcp2221_put_sram(sc, &req, &res);
out:
return err;
}
int
mcp2221_get_gpio_cfg(struct umcpmio_softc *sc,
struct mcp2221_get_gpio_cfg_res *res)
{
struct mcp2221_get_gpio_cfg_req req;
int err = 0;
memset(&req, 0, MCP2221_REQ_BUFFER_SIZE);
req.cmd = MCP2221_CMD_GET_GPIO_CFG;
KASSERT(mutex_owned(&sc->sc_action_mutex));
err = umcpmio_send_report(sc,
(uint8_t *) & req, MCP2221_REQ_BUFFER_SIZE,
(uint8_t *) res, sc->sc_cv_wait);
return err;
}
static int
mcp2221_put_gpio_cfg(struct umcpmio_softc *sc,
struct mcp2221_set_gpio_cfg_req *req, struct mcp2221_set_gpio_cfg_res *res)
{
int err = 0;
req->cmd = MCP2221_CMD_SET_GPIO_CFG;
KASSERT(mutex_owned(&sc->sc_action_mutex));
err = umcpmio_send_report(sc,
(uint8_t *) req, MCP2221_REQ_BUFFER_SIZE,
(uint8_t *) res, sc->sc_cv_wait);
return err;
}
static int
mcp2210_get_gpio_value(struct umcpmio_softc *sc,
int pin)
{
struct mcp2210_get_gpio_value_res res;
uint16_t v;
int err = 0;
int r = GPIO_PIN_LOW;
KASSERT(mutex_owned(&sc->sc_action_mutex));
err = mcp2210_get_gpio_value_sram(sc, &res);
if (!err) {
v = (res.pin_value_msb << 8) | res.pin_value_lsb;
if (v & (1 << pin))
r = GPIO_PIN_HIGH;
}
return r;
}
static int
mcp2221_get_gpio_value(struct umcpmio_softc *sc,
int pin)
{
struct mcp2221_get_gpio_cfg_res get_gpio_cfg_res;
int err = 0;
int r = GPIO_PIN_LOW;
KASSERT(mutex_owned(&sc->sc_action_mutex));
err = mcp2221_get_gpio_cfg(sc, &get_gpio_cfg_res);
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, "mcp2221_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;
}
static int
mcp2210_set_gpio_value(struct umcpmio_softc *sc,
int pin, bool value)
{
int err = 0;
struct mcp2210_get_gpio_value_res get_res;
struct mcp2210_set_gpio_value_req set_req;
struct mcp2210_set_gpio_value_res set_res;
uint16_t v;
KASSERT(mutex_owned(&sc->sc_action_mutex));
err = mcp2210_get_gpio_value_sram(sc, &get_res);
if (!err && get_res.completion != MCP2210_CMD_COMPLETE_OK)
err = EIO;
if (!err) {
v = (get_res.pin_value_msb << 8) | get_res.pin_value_lsb;
if (value)
v |= (1 << pin);
else
v &= ~(1 << pin);
set_req.pin_value_lsb = v & 0x00ff;
set_req.pin_value_msb = (v & 0xff00) >> 8;
err = mcp2210_set_gpio_value_sram(sc, &set_req, &set_res);
}
return err;
}
static void
mcp2221_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;
}
static int
mcp2221_set_gpio_value_one(struct umcpmio_softc *sc,
int pin, bool value)
{
int err = 0;
struct mcp2221_set_gpio_cfg_req req;
struct mcp2221_set_gpio_cfg_res res;
KASSERT(mutex_owned(&sc->sc_action_mutex));
memset(&req, 0, MCP2221_REQ_BUFFER_SIZE);
mcp2221_set_gpio_value(&req, pin, value);
err = mcp2221_put_gpio_cfg(sc, &req, &res);
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;
}
static int
umcpmio_gpio_pin_read(void *arg, int pin)
{
struct umcpmio_softc *sc = arg;
int r = GPIO_PIN_LOW;
mutex_enter(&sc->sc_action_mutex);
if (sc->sc_chipinfo->usb_id == USB_PRODUCT_MICROCHIP_MCP2210)
r = mcp2210_get_gpio_value(sc, pin);
if (sc->sc_chipinfo->usb_id == USB_PRODUCT_MICROCHIP_MCP2221)
r = mcp2221_get_gpio_value(sc, pin);
mutex_exit(&sc->sc_action_mutex);
return r;
}
static void
umcpmio_gpio_pin_write(void *arg, int pin, int value)
{
struct umcpmio_softc *sc = arg;
mutex_enter(&sc->sc_action_mutex);
if (sc->sc_chipinfo->usb_id == USB_PRODUCT_MICROCHIP_MCP2210)
mcp2210_set_gpio_value(sc, pin, value);
if (sc->sc_chipinfo->usb_id == USB_PRODUCT_MICROCHIP_MCP2221)
mcp2221_set_gpio_value_one(sc, pin, value);
mutex_exit(&sc->sc_action_mutex);
}
static int
mcp2210_gpio_pin_ctl(void *arg, int pin, int flags)
{
struct umcpmio_softc *sc = arg;
struct mcp2210_set_gpio_sram_req mcp2210_set_gpio_sram_req;
struct mcp2210_set_gpio_dir_req mcp2210_set_gpio_dir_sram_req;
struct mcp2210_set_gpio_sram_res mcp2210_set_gpio_sram_res;
struct mcp2210_set_gpio_dir_res mcp2210_set_gpio_dir_sram_res;
struct mcp2210_set_gpio_value_req mcp2210_set_gpio_value_sram_req;
struct mcp2210_set_gpio_value_res mcp2210_set_gpio_value_sram_res;
struct mcp2210_get_gpio_sram_res mcp2210_get_gpio_sram_res;
struct mcp2210_get_gpio_dir_res mcp2210_get_gpio_dir_sram_res;
struct mcp2210_get_gpio_value_res mcp2210_get_gpio_value_sram_res;
uint32_t altmask =
GPIO_PIN_ALT0 | GPIO_PIN_ALT3 | GPIO_PIN_ALT4 | GPIO_PIN_ALT5 | GPIO_PIN_ALT6;
int err = 0;
uint16_t vdir;
if (sc->sc_dying)
return 0;
KASSERT(mutex_owned(&sc->sc_action_mutex));
err = mcp2210_get_gpio_sram(sc, &mcp2210_get_gpio_sram_res);
if (err)
goto out;
err = mcp2210_get_gpio_dir_sram(sc, &mcp2210_get_gpio_dir_sram_res);
if (err)
goto out;
err = mcp2210_get_gpio_value_sram(sc, &mcp2210_get_gpio_value_sram_res);
if (err)
goto out;
umcpmio_dump_buffer(sc->sc_dumpbuffer,
(uint8_t *) & mcp2210_get_gpio_sram_res, MCP2210_RES_BUFFER_SIZE,
"mcp2210_gpio_pin_ctl get gpio sram res");
umcpmio_dump_buffer(sc->sc_dumpbuffer,
(uint8_t *) & mcp2210_get_gpio_dir_sram_res, MCP2210_RES_BUFFER_SIZE,
"mcp2210_gpio_pin_ctl get gpio dir sram res");
memset(&mcp2210_set_gpio_sram_req.cmd, 0, MCP2210_REQ_BUFFER_SIZE);
memcpy(&mcp2210_set_gpio_sram_req.gp0_designation,
&mcp2210_get_gpio_sram_res.gp0_designation,
&mcp2210_get_gpio_sram_res.nvram_protection -
&mcp2210_get_gpio_sram_res.gp0_designation + 1);
memset(&mcp2210_set_gpio_dir_sram_req.cmd, 0, MCP2210_REQ_BUFFER_SIZE);
vdir = (mcp2210_get_gpio_dir_sram_res.pin_dir_msb << 8) |
mcp2210_get_gpio_dir_sram_res.pin_dir_lsb;
uint8_t *b = (uint8_t *) & mcp2210_set_gpio_sram_req.cmd;
bool changed_sram = false;
if (flags & (GPIO_PIN_OUTPUT | GPIO_PIN_INPUT)) {
if (b[MCP2210_GPIO_SRAM_GP0 + pin] != MCP2210_PIN_IS_GPIO) {
b[MCP2210_GPIO_SRAM_GP0 + pin] = MCP2210_PIN_IS_GPIO;
changed_sram = true;
}
if (flags & GPIO_PIN_INPUT)
vdir |= (1 << pin);
else
vdir &= ~(1 << pin);
} else {
switch (flags & altmask) {
case GPIO_PIN_ALT0:
if (b[MCP2210_GPIO_SRAM_GP0 + pin] != MCP2210_PIN_IS_ALT0) {
b[MCP2210_GPIO_SRAM_GP0 + pin] = MCP2210_PIN_IS_ALT0;
changed_sram = true;
}
break;
case GPIO_PIN_ALT3:
if (b[MCP2210_GPIO_SRAM_GP0 + pin] != MCP2210_PIN_IS_DED) {
b[MCP2210_GPIO_SRAM_GP0 + pin] = MCP2210_PIN_IS_DED;
changed_sram = true;
}
if (pin == 6) {
if (mcp2210_counter_gp_to_flags(mcp2210_get_gpio_sram_res.other_settings) != GPIO_PIN_ALT3) {
mcp2210_set_gpio_sram_req.other_settings &= 0xf1;
mcp2210_set_gpio_sram_req.other_settings |= MCP2210_COUNTER_FALLING_EDGE << 1;
changed_sram = true;
}
}
break;
case GPIO_PIN_ALT4:
if (b[MCP2210_GPIO_SRAM_GP0 + pin] != MCP2210_PIN_IS_DED) {
b[MCP2210_GPIO_SRAM_GP0 + pin] = MCP2210_PIN_IS_DED;
changed_sram = true;
}
if (pin == 6) {
if (mcp2210_counter_gp_to_flags(mcp2210_get_gpio_sram_res.other_settings) != GPIO_PIN_ALT4) {
mcp2210_set_gpio_sram_req.other_settings &= 0xf1;
mcp2210_set_gpio_sram_req.other_settings |= MCP2210_COUNTER_RISING_EDGE << 1;
changed_sram = true;
}
}
break;
case GPIO_PIN_ALT5:
if (b[MCP2210_GPIO_SRAM_GP0 + pin] != MCP2210_PIN_IS_DED) {
b[MCP2210_GPIO_SRAM_GP0 + pin] = MCP2210_PIN_IS_DED;
changed_sram = true;
}
if (pin == 6) {
if (mcp2210_counter_gp_to_flags(mcp2210_get_gpio_sram_res.other_settings) != GPIO_PIN_ALT5) {
mcp2210_set_gpio_sram_req.other_settings &= 0xf1;
mcp2210_set_gpio_sram_req.other_settings |= MCP2210_COUNTER_LOW_PULSE << 1;
changed_sram = true;
}
}
break;
case GPIO_PIN_ALT6:
if (b[MCP2210_GPIO_SRAM_GP0 + pin] != MCP2210_PIN_IS_DED) {
b[MCP2210_GPIO_SRAM_GP0 + pin] = MCP2210_PIN_IS_DED;
changed_sram = true;
}
if (pin == 6) {
if (mcp2210_counter_gp_to_flags(mcp2210_get_gpio_sram_res.other_settings) != GPIO_PIN_ALT6) {
mcp2210_set_gpio_sram_req.other_settings &= 0xf1;
mcp2210_set_gpio_sram_req.other_settings |= MCP2210_COUNTER_HIGH_PULSE << 1;
changed_sram = true;
}
}
break;
default:
break;
}
}
if (changed_sram) {
err = mcp2210_set_gpio_sram(sc, &mcp2210_set_gpio_sram_req,
&mcp2210_set_gpio_sram_res);
if (err)
goto out;
mcp2210_set_gpio_dir_sram_req.pin_dir_msb = (vdir >> 8) & 0xff;
mcp2210_set_gpio_dir_sram_req.pin_dir_lsb = vdir & 0x00ff;
err = mcp2210_set_gpio_dir_sram(sc, &mcp2210_set_gpio_dir_sram_req,
&mcp2210_set_gpio_dir_sram_res);
if (err)
goto out;
mcp2210_set_gpio_value_sram_req.pin_value_msb = mcp2210_get_gpio_value_sram_res.pin_value_msb;
mcp2210_set_gpio_value_sram_req.pin_value_lsb = mcp2210_get_gpio_value_sram_res.pin_value_lsb;
if (flags & GPIO_PIN_OUTPUT && pin < 8) {
if (mcp2210_set_gpio_sram_req.default_output_lsb & (1 << pin))
mcp2210_set_gpio_value_sram_req.pin_value_lsb |= (1 << pin);
else
mcp2210_set_gpio_value_sram_req.pin_value_lsb &= ~(1 << pin);
}
err = mcp2210_set_gpio_value_sram(sc, &mcp2210_set_gpio_value_sram_req, &mcp2210_set_gpio_value_sram_res);
} else {
if (flags & (GPIO_PIN_OUTPUT | GPIO_PIN_INPUT)) {
mcp2210_set_gpio_dir_sram_req.pin_dir_msb = (vdir >> 8) & 0xff;
mcp2210_set_gpio_dir_sram_req.pin_dir_lsb = vdir & 0x00ff;
err = mcp2210_set_gpio_dir_sram(sc, &mcp2210_set_gpio_dir_sram_req,
&mcp2210_set_gpio_dir_sram_res);
}
}
out:
return err;
}
int
mcp2221_gpio_pin_ctl(void *arg, int pin, int flags)
{
struct umcpmio_softc *sc = arg;
struct mcp2221_set_sram_req set_sram_req;
struct mcp2221_set_sram_res set_sram_res;
struct mcp2221_get_sram_res current_sram_res;
struct mcp2221_get_gpio_cfg_res current_gpio_cfg_res;
int err = 0;
if (sc->sc_dying)
return 0;
KASSERT(mutex_owned(&sc->sc_action_mutex));
err = mcp2221_get_sram(sc, ¤t_sram_res);
if (err)
goto out;
err = mcp2221_get_gpio_cfg(sc, ¤t_gpio_cfg_res);
if (err)
goto out;
memset(&set_sram_req, 0, MCP2221_REQ_BUFFER_SIZE);
switch (pin) {
case 0:
set_sram_req.gp1_settings = current_sram_res.gp1_settings;
set_sram_req.gp2_settings = current_sram_res.gp2_settings;
set_sram_req.gp3_settings = current_sram_res.gp3_settings;
break;
case 1:
set_sram_req.gp0_settings = current_sram_res.gp0_settings;
set_sram_req.gp2_settings = current_sram_res.gp2_settings;
set_sram_req.gp3_settings = current_sram_res.gp3_settings;
break;
case 2:
set_sram_req.gp0_settings = current_sram_res.gp0_settings;
set_sram_req.gp1_settings = current_sram_res.gp1_settings;
set_sram_req.gp3_settings = current_sram_res.gp3_settings;
break;
case 3:
set_sram_req.gp0_settings = current_sram_res.gp0_settings;
set_sram_req.gp1_settings = current_sram_res.gp1_settings;
set_sram_req.gp2_settings = current_sram_res.gp2_settings;
break;
}
mcp2221_set_gpio_designation_sram(&set_sram_req, pin, flags);
mcp2221_set_gpio_dir_sram(&set_sram_req, pin, flags);
if (current_gpio_cfg_res.gp0_pin_dir == MCP2221_GPIO_CFG_DIR_OUTPUT) {
if (current_gpio_cfg_res.gp0_pin_value == 1) {
set_sram_req.gp0_settings |=
MCP2221_SRAM_GPIO_OUTPUT_HIGH;
} else {
set_sram_req.gp0_settings &=
~MCP2221_SRAM_GPIO_OUTPUT_HIGH;
}
}
if (current_gpio_cfg_res.gp1_pin_dir == MCP2221_GPIO_CFG_DIR_OUTPUT) {
if (current_gpio_cfg_res.gp1_pin_value == 1) {
set_sram_req.gp1_settings |=
MCP2221_SRAM_GPIO_OUTPUT_HIGH;
} else {
set_sram_req.gp1_settings &=
~MCP2221_SRAM_GPIO_OUTPUT_HIGH;
}
}
if (current_gpio_cfg_res.gp2_pin_dir == MCP2221_GPIO_CFG_DIR_OUTPUT) {
if (current_gpio_cfg_res.gp2_pin_value == 1) {
set_sram_req.gp2_settings |=
MCP2221_SRAM_GPIO_OUTPUT_HIGH;
} else {
set_sram_req.gp2_settings &=
~MCP2221_SRAM_GPIO_OUTPUT_HIGH;
}
}
if (current_gpio_cfg_res.gp3_pin_dir == MCP2221_GPIO_CFG_DIR_OUTPUT) {
if (current_gpio_cfg_res.gp3_pin_value == 1) {
set_sram_req.gp3_settings |=
MCP2221_SRAM_GPIO_OUTPUT_HIGH;
} else {
set_sram_req.gp3_settings &=
~MCP2221_SRAM_GPIO_OUTPUT_HIGH;
}
}
err = mcp2221_put_sram(sc, &set_sram_req, &set_sram_res);
if (err)
goto out;
umcpmio_dump_buffer(sc->sc_dumpbuffer,
(uint8_t *) & set_sram_res, MCP2221_RES_BUFFER_SIZE,
"mcp2221_gpio_pin_ctl set sram buffer copy");
if (set_sram_res.cmd != MCP2221_CMD_SET_SRAM ||
set_sram_res.completion != MCP2221_CMD_COMPLETE_OK) {
device_printf(sc->sc_dev, "mcp2221_gpio_pin_ctl:"
" not the command desired, or error: %02x %02x\n",
set_sram_res.cmd,
set_sram_res.completion);
err = EIO;
goto out;
}
sc->sc_gpio_pins[pin].pin_flags = flags;
err = 0;
out:
return err;
}
void
umcpmio_gpio_pin_ctl(void *arg, int pin, int flags)
{
struct umcpmio_softc *sc = arg;
if (sc->sc_dying)
return;
mutex_enter(&sc->sc_action_mutex);
if (sc->sc_chipinfo->usb_id == USB_PRODUCT_MICROCHIP_MCP2210)
mcp2210_gpio_pin_ctl(sc, pin, flags);
if (sc->sc_chipinfo->usb_id == USB_PRODUCT_MICROCHIP_MCP2221)
mcp2221_gpio_pin_ctl(sc, pin, flags);
mutex_exit(&sc->sc_action_mutex);
}
int
mcp2210_get_gp6_counter(struct umcpmio_softc *sc, int *counter,
uint8_t reset)
{
struct mcp2210_get_gp6_events_req req;
struct mcp2210_get_gp6_events_res res;
int err = 0;
KASSERT(mutex_owned(&sc->sc_action_mutex));
memset(&req, 0, MCP2210_REQ_BUFFER_SIZE);
req.cmd = MCP2210_CMD_GET_GP6_EVENTS;
req.reset_counter = reset;
umcpmio_dump_buffer(sc->sc_dumpbuffer,
(uint8_t *) & req, MCP2210_REQ_BUFFER_SIZE,
"mcp2210_get_gp6_counter req");
err = umcpmio_send_report(sc,
(uint8_t *) & req, MCP2210_REQ_BUFFER_SIZE,
(uint8_t *) & res, sc->sc_cv_wait);
umcpmio_dump_buffer(sc->sc_dumpbuffer,
(uint8_t *) & res, MCP2210_RES_BUFFER_SIZE,
"mcp2210_get_gp6_counter res");
err = mcp2210_decode_errors(req.cmd, err, res.completion);
if (!err) {
*counter = (res.counter_msb << 8) | res.counter_lsb;
}
return err;
}
static int
umcpmio_extract_gpio_sram(struct umcpmio_softc *sc,
uint8_t *extract)
{
int err = 0;
struct mcp2210_get_gpio_sram_res mcp2210_get_gpio_sram_res;
struct mcp2210_get_gpio_dir_res mcp2210_get_gpio_dir_sram_res;
struct mcp2221_get_sram_res mcp2221_get_sram_res;
if (sc->sc_chipinfo->usb_id == USB_PRODUCT_MICROCHIP_MCP2210) {
mutex_enter(&sc->sc_action_mutex);
err = mcp2210_get_gpio_sram(sc, &mcp2210_get_gpio_sram_res);
mutex_exit(&sc->sc_action_mutex);
if (err)
return err;
umcpmio_dump_buffer(sc->sc_dumpbuffer,
(uint8_t *) & mcp2210_get_gpio_sram_res, MCP2210_RES_BUFFER_SIZE,
"umcpmio_extract_gpio_sram mcp2210 get gpio sram buffer copy");
extract[0] = mcp2210_get_gpio_sram_res.gp0_designation;
extract[1] = mcp2210_get_gpio_sram_res.gp1_designation;
extract[2] = mcp2210_get_gpio_sram_res.gp2_designation;
extract[3] = mcp2210_get_gpio_sram_res.gp3_designation;
extract[4] = mcp2210_get_gpio_sram_res.gp4_designation;
extract[5] = mcp2210_get_gpio_sram_res.gp5_designation;
extract[6] = mcp2210_get_gpio_sram_res.gp6_designation;
extract[7] = mcp2210_get_gpio_sram_res.gp7_designation;
extract[8] = mcp2210_get_gpio_sram_res.gp8_designation;
mutex_enter(&sc->sc_action_mutex);
err = mcp2210_get_gpio_dir_sram(sc, &mcp2210_get_gpio_dir_sram_res);
mutex_exit(&sc->sc_action_mutex);
if (err)
return err;
umcpmio_dump_buffer(sc->sc_dumpbuffer,
(uint8_t *) & mcp2210_get_gpio_dir_sram_res, MCP2210_RES_BUFFER_SIZE,
"umcpmio_extract_gpio_sram mcp2210 get gpio dir sram buffer copy");
extract[9] = mcp2210_get_gpio_dir_sram_res.pin_dir_lsb;
extract[10] = mcp2210_get_gpio_dir_sram_res.pin_dir_msb;
extract[11] = mcp2210_get_gpio_sram_res.other_settings;
}
if (sc->sc_chipinfo->usb_id == USB_PRODUCT_MICROCHIP_MCP2221) {
mutex_enter(&sc->sc_action_mutex);
err = mcp2221_get_sram(sc, &mcp2221_get_sram_res);
mutex_exit(&sc->sc_action_mutex);
if (err)
return err;
umcpmio_dump_buffer(sc->sc_dumpbuffer,
(uint8_t *) & mcp2221_get_sram_res, MCP2221_RES_BUFFER_SIZE,
"umcpmio_extract_gpio_sram mcp2221 get sram buffer copy");
extract[0] = mcp2221_get_sram_res.gp0_settings;
extract[1] = mcp2221_get_sram_res.gp1_settings;
extract[2] = mcp2221_get_sram_res.gp2_settings;
extract[3] = mcp2221_get_sram_res.gp3_settings;
}
return err;
}
void
umcpmio_gpio_attach(struct umcpmio_softc *sc)
{
int err;
struct gpiobus_attach_args gba;
uint8_t extract[UMCPMIO_MAX_GPIO_PINS + 3];
err = umcpmio_extract_gpio_sram(sc, extract);
if (err) {
aprint_error_dev(sc->sc_dev, "umcpmio_gpio_attach:"
" extract gpio from sram: err=%d\n",
err);
return;
}
for (int c = 0; c < sc->sc_chipinfo->num_gpio_pins; c++){
sc->sc_gpio_pins[c].pin_num = c;
sc->sc_gpio_pins[c].pin_caps = sc->sc_chipinfo->gpio_pin_ability[c];
if (sc->sc_chipinfo->usb_id == USB_PRODUCT_MICROCHIP_MCP2210) {
sc->sc_gpio_pins[c].pin_flags =
mcp2210_sram_gpio_to_flags(extract, c);
}
if (sc->sc_chipinfo->usb_id == USB_PRODUCT_MICROCHIP_MCP2221) {
sc->sc_gpio_pins[c].pin_flags =
mcp2221_sram_gpio_to_flags(extract[c]);
}
sc->sc_gpio_pins[c].pin_intrcaps = 0;
strncpy(sc->sc_gpio_pins[c].pin_defname,
sc->sc_chipinfo->gpio_names[c],
strlen(sc->sc_chipinfo->gpio_names[c]) + 1);
}
sc->sc_gpio_gc.gp_cookie = sc;
sc->sc_gpio_gc.gp_pin_read = umcpmio_gpio_pin_read;
sc->sc_gpio_gc.gp_pin_write = umcpmio_gpio_pin_write;
sc->sc_gpio_gc.gp_pin_ctl = umcpmio_gpio_pin_ctl;
gba.gba_gc = &sc->sc_gpio_gc;
gba.gba_pins = sc->sc_gpio_pins;
gba.gba_npins = sc->sc_chipinfo->num_gpio_pins;
sc->sc_gpio_dev = config_found(sc->sc_dev, &gba, gpiobus_print,
CFARGS(.iattr = "gpiobus"));
}