1*3bfaa971Sbrad /* $NetBSD: umcpmio.c,v 1.1 2024/12/16 16:37:38 brad Exp $ */ 2*3bfaa971Sbrad 3*3bfaa971Sbrad /* 4*3bfaa971Sbrad * Copyright (c) 2024 Brad Spencer <brad@anduin.eldar.org> 5*3bfaa971Sbrad * 6*3bfaa971Sbrad * Permission to use, copy, modify, and distribute this software for any 7*3bfaa971Sbrad * purpose with or without fee is hereby granted, provided that the above 8*3bfaa971Sbrad * copyright notice and this permission notice appear in all copies. 9*3bfaa971Sbrad * 10*3bfaa971Sbrad * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 11*3bfaa971Sbrad * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 12*3bfaa971Sbrad * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 13*3bfaa971Sbrad * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 14*3bfaa971Sbrad * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 15*3bfaa971Sbrad * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 16*3bfaa971Sbrad * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 17*3bfaa971Sbrad */ 18*3bfaa971Sbrad 19*3bfaa971Sbrad #include <sys/cdefs.h> 20*3bfaa971Sbrad __KERNEL_RCSID(0, "$NetBSD: umcpmio.c,v 1.1 2024/12/16 16:37:38 brad Exp $"); 21*3bfaa971Sbrad 22*3bfaa971Sbrad /* 23*3bfaa971Sbrad Driver for the Microchip MCP2221 / MCP2221A USB multi-io chip 24*3bfaa971Sbrad */ 25*3bfaa971Sbrad 26*3bfaa971Sbrad #ifdef _KERNEL_OPT 27*3bfaa971Sbrad #include "opt_usb.h" 28*3bfaa971Sbrad #endif 29*3bfaa971Sbrad 30*3bfaa971Sbrad #include <sys/param.h> 31*3bfaa971Sbrad #include <sys/systm.h> 32*3bfaa971Sbrad #include <sys/conf.h> 33*3bfaa971Sbrad #include <sys/kernel.h> 34*3bfaa971Sbrad #include <sys/kmem.h> 35*3bfaa971Sbrad #include <sys/device.h> 36*3bfaa971Sbrad #include <sys/sysctl.h> 37*3bfaa971Sbrad #include <sys/tty.h> 38*3bfaa971Sbrad #include <sys/file.h> 39*3bfaa971Sbrad #include <sys/vnode.h> 40*3bfaa971Sbrad #include <sys/kauth.h> 41*3bfaa971Sbrad #include <sys/lwp.h> 42*3bfaa971Sbrad 43*3bfaa971Sbrad #include <sys/gpio.h> 44*3bfaa971Sbrad #include <dev/gpio/gpiovar.h> 45*3bfaa971Sbrad 46*3bfaa971Sbrad #include <dev/i2c/i2cvar.h> 47*3bfaa971Sbrad 48*3bfaa971Sbrad #include <dev/usb/usb.h> 49*3bfaa971Sbrad #include <dev/usb/usbhid.h> 50*3bfaa971Sbrad 51*3bfaa971Sbrad #include <dev/usb/usbdi.h> 52*3bfaa971Sbrad #include <dev/usb/usbdi_util.h> 53*3bfaa971Sbrad #include <dev/usb/usbdevs.h> 54*3bfaa971Sbrad #include <dev/usb/uhidev.h> 55*3bfaa971Sbrad #include <dev/hid/hid.h> 56*3bfaa971Sbrad 57*3bfaa971Sbrad #include <dev/usb/umcpmio.h> 58*3bfaa971Sbrad #include <dev/usb/umcpmio_subr.h> 59*3bfaa971Sbrad #include <dev/usb/umcpmio_hid_reports.h> 60*3bfaa971Sbrad #include <dev/usb/umcpmio_io.h> 61*3bfaa971Sbrad 62*3bfaa971Sbrad int umcpmio_send_report(struct umcpmio_softc *, uint8_t *, size_t, uint8_t *, int); 63*3bfaa971Sbrad 64*3bfaa971Sbrad static const struct usb_devno umcpmio_devs[] = { 65*3bfaa971Sbrad { USB_VENDOR_MICROCHIP, USB_PRODUCT_MICROCHIP_MCP2221 }, 66*3bfaa971Sbrad }; 67*3bfaa971Sbrad #define umcpmio_lookup(v, p) usb_lookup(umcpmio_devs, v, p) 68*3bfaa971Sbrad 69*3bfaa971Sbrad static int umcpmio_match(device_t, cfdata_t, void *); 70*3bfaa971Sbrad static void umcpmio_attach(device_t, device_t, void *); 71*3bfaa971Sbrad static int umcpmio_detach(device_t, int); 72*3bfaa971Sbrad static int umcpmio_activate(device_t, enum devact); 73*3bfaa971Sbrad static int umcpmio_verify_sysctl(SYSCTLFN_ARGS); 74*3bfaa971Sbrad static int umcpmio_verify_dac_sysctl(SYSCTLFN_ARGS); 75*3bfaa971Sbrad static int umcpmio_verify_adc_sysctl(SYSCTLFN_ARGS); 76*3bfaa971Sbrad static int umcpmio_verify_gpioclock_dc_sysctl(SYSCTLFN_ARGS); 77*3bfaa971Sbrad static int umcpmio_verify_gpioclock_cd_sysctl(SYSCTLFN_ARGS); 78*3bfaa971Sbrad 79*3bfaa971Sbrad #define UMCPMIO_DEBUG 1 80*3bfaa971Sbrad #ifdef UMCPMIO_DEBUG 81*3bfaa971Sbrad #define DPRINTF(x) if (umcpmiodebug) printf x 82*3bfaa971Sbrad #define DPRINTFN(n, x) if (umcpmiodebug > (n)) printf x 83*3bfaa971Sbrad int umcpmiodebug = 0; 84*3bfaa971Sbrad #else 85*3bfaa971Sbrad #define DPRINTF(x) __nothing 86*3bfaa971Sbrad #define DPRINTFN(n,x) __nothing 87*3bfaa971Sbrad #endif 88*3bfaa971Sbrad 89*3bfaa971Sbrad 90*3bfaa971Sbrad CFATTACH_DECL_NEW(umcpmio, sizeof(struct umcpmio_softc), umcpmio_match, 91*3bfaa971Sbrad umcpmio_attach, umcpmio_detach, umcpmio_activate); 92*3bfaa971Sbrad 93*3bfaa971Sbrad 94*3bfaa971Sbrad #define WAITMS(ms) if (ms > 0) delay(ms * 1000) 95*3bfaa971Sbrad 96*3bfaa971Sbrad extern struct cfdriver umcpmio_cd; 97*3bfaa971Sbrad 98*3bfaa971Sbrad static dev_type_open(umcpmio_dev_open); 99*3bfaa971Sbrad static dev_type_read(umcpmio_dev_read); 100*3bfaa971Sbrad static dev_type_write(umcpmio_dev_write); 101*3bfaa971Sbrad static dev_type_close(umcpmio_dev_close); 102*3bfaa971Sbrad static dev_type_ioctl(umcpmio_dev_ioctl); 103*3bfaa971Sbrad const struct cdevsw umcpmio_cdevsw = { 104*3bfaa971Sbrad .d_open = umcpmio_dev_open, 105*3bfaa971Sbrad .d_close = umcpmio_dev_close, 106*3bfaa971Sbrad .d_read = umcpmio_dev_read, 107*3bfaa971Sbrad .d_write = umcpmio_dev_write, 108*3bfaa971Sbrad .d_ioctl = umcpmio_dev_ioctl, 109*3bfaa971Sbrad .d_stop = nostop, 110*3bfaa971Sbrad .d_tty = notty, 111*3bfaa971Sbrad .d_poll = nopoll, 112*3bfaa971Sbrad .d_mmap = nommap, 113*3bfaa971Sbrad .d_kqfilter = nokqfilter, 114*3bfaa971Sbrad .d_discard = nodiscard, 115*3bfaa971Sbrad .d_flag = D_OTHER 116*3bfaa971Sbrad }; 117*3bfaa971Sbrad 118*3bfaa971Sbrad 119*3bfaa971Sbrad static const char umcpmio_valid_vrefs[] = 120*3bfaa971Sbrad "4.096V, 2.048V, 1.024V, OFF, VDD"; 121*3bfaa971Sbrad 122*3bfaa971Sbrad static const char umcpmio_valid_dcs[] = 123*3bfaa971Sbrad "75%, 50%, 25%, 0%"; 124*3bfaa971Sbrad 125*3bfaa971Sbrad static const char umcpmio_valid_cds[] = 126*3bfaa971Sbrad "375kHz, 750kHz, 1.5MHz, 3MHz, 6MHz, 12MHz, 24MHz"; 127*3bfaa971Sbrad 128*3bfaa971Sbrad static void 129*3bfaa971Sbrad umcpmio_dump_buffer(bool enabled, uint8_t *buf, u_int len, const char *name) 130*3bfaa971Sbrad { 131*3bfaa971Sbrad if (enabled) { 132*3bfaa971Sbrad DPRINTF(("%s:",name)); 133*3bfaa971Sbrad for(int i=0; i < len; i++) { 134*3bfaa971Sbrad DPRINTF((" %02x",buf[i])); 135*3bfaa971Sbrad } 136*3bfaa971Sbrad DPRINTF(("\n")); 137*3bfaa971Sbrad } 138*3bfaa971Sbrad } 139*3bfaa971Sbrad 140*3bfaa971Sbrad /* Communication with the HID function requires sending a HID report request and 141*3bfaa971Sbrad * then waiting for a response. 142*3bfaa971Sbrad * 143*3bfaa971Sbrad * The panic that occurs when trying to use the interrupt... i.e. 144*3bfaa971Sbrad * attaching though this driver seems to be related to the fact that 145*3bfaa971Sbrad * a spin lock is held and the USB stack wants to wait. 146*3bfaa971Sbrad * 147*3bfaa971Sbrad * The USB stack *IS* going to have to wait for the response from 148*3bfaa971Sbrad * the device, somehow... 149*3bfaa971Sbrad * 150*3bfaa971Sbrad * It didn't seem possible to defer the uhidev_write to a thread. 151*3bfaa971Sbrad * Attempts to yield() while spinning hard also did not work and 152*3bfaa971Sbrad * not yield()ing didn't allow anything else to run. 153*3bfaa971Sbrad * 154*3bfaa971Sbrad */ 155*3bfaa971Sbrad 156*3bfaa971Sbrad /* 157*3bfaa971Sbrad * This is the panic you will get: 158*3bfaa971Sbrad * 159*3bfaa971Sbrad panic: kernel diagnostic assertion "ci->ci_mtx_count == -1" failed: file "../../../../kern/kern_synch.c", line 762 mi_switch: cpu0: ci_mtx_count (-2) != -1 (block with spin-mutex held) 160*3bfaa971Sbrad 161*3bfaa971Sbrad */ 162*3bfaa971Sbrad 163*3bfaa971Sbrad static void 164*3bfaa971Sbrad umcpmio_uhidev_intr(void *cookie, void *ibuf, u_int len) 165*3bfaa971Sbrad { 166*3bfaa971Sbrad struct umcpmio_softc *sc = cookie; 167*3bfaa971Sbrad 168*3bfaa971Sbrad if (sc->sc_dying) 169*3bfaa971Sbrad return; 170*3bfaa971Sbrad 171*3bfaa971Sbrad DPRINTFN(30,("umcpmio_uhidev_intr: len=%d\n",len)); 172*3bfaa971Sbrad 173*3bfaa971Sbrad mutex_enter(&sc->sc_res_mutex); 174*3bfaa971Sbrad switch(len) { 175*3bfaa971Sbrad case MCP2221_RES_BUFFER_SIZE: 176*3bfaa971Sbrad if (sc->sc_res_buffer != NULL) { 177*3bfaa971Sbrad memcpy(sc->sc_res_buffer, ibuf, MCP2221_RES_BUFFER_SIZE); 178*3bfaa971Sbrad sc->sc_res_ready = true; 179*3bfaa971Sbrad cv_signal(&sc->sc_res_cv); 180*3bfaa971Sbrad } else { 181*3bfaa971Sbrad int d=umcpmiodebug; 182*3bfaa971Sbrad device_printf(sc->sc_dev,"umcpmio_uhidev_intr: NULL sc_res_buffer: len=%d\n",len); 183*3bfaa971Sbrad umcpmiodebug=20; 184*3bfaa971Sbrad umcpmio_dump_buffer(true, (uint8_t *)ibuf, len, "umcpmio_uhidev_intr: ibuf"); 185*3bfaa971Sbrad umcpmiodebug=d; 186*3bfaa971Sbrad } 187*3bfaa971Sbrad 188*3bfaa971Sbrad break; 189*3bfaa971Sbrad default: 190*3bfaa971Sbrad device_printf(sc->sc_dev,"umcpmio_uhidev_intr: Unknown interrupt length: %d",len); 191*3bfaa971Sbrad break; 192*3bfaa971Sbrad } 193*3bfaa971Sbrad mutex_exit(&sc->sc_res_mutex); 194*3bfaa971Sbrad } 195*3bfaa971Sbrad 196*3bfaa971Sbrad /* Send a HID report. This needs to be called with the action mutex held */ 197*3bfaa971Sbrad 198*3bfaa971Sbrad int 199*3bfaa971Sbrad umcpmio_send_report(struct umcpmio_softc *sc, uint8_t *sendbuf, 200*3bfaa971Sbrad size_t sendlen, uint8_t *resbuf, int timeout) 201*3bfaa971Sbrad { 202*3bfaa971Sbrad int err = 0; 203*3bfaa971Sbrad int err_count=0; 204*3bfaa971Sbrad 205*3bfaa971Sbrad if (sc->sc_dying) 206*3bfaa971Sbrad return EIO; 207*3bfaa971Sbrad 208*3bfaa971Sbrad KASSERT(mutex_owned(&sc->sc_action_mutex)); 209*3bfaa971Sbrad 210*3bfaa971Sbrad if (sc->sc_res_buffer != NULL) { 211*3bfaa971Sbrad device_printf(sc->sc_dev,"umcpmio_send_report: sc->sc_res_buffer is not NULL\n"); 212*3bfaa971Sbrad } 213*3bfaa971Sbrad sc->sc_res_buffer = resbuf; 214*3bfaa971Sbrad sc->sc_res_ready = false; 215*3bfaa971Sbrad 216*3bfaa971Sbrad err = uhidev_write(sc->sc_hdev, sendbuf, sendlen); 217*3bfaa971Sbrad 218*3bfaa971Sbrad if (err) { 219*3bfaa971Sbrad DPRINTF(("umcpmio_send_report: uhidev_write errored with: err=%d\n", err)); 220*3bfaa971Sbrad goto out; 221*3bfaa971Sbrad } 222*3bfaa971Sbrad 223*3bfaa971Sbrad DPRINTFN(30,("umcpmio_send_report: about to wait on cv. err=%d\n", err)); 224*3bfaa971Sbrad 225*3bfaa971Sbrad mutex_enter(&sc->sc_res_mutex); 226*3bfaa971Sbrad while (!sc->sc_res_ready) { 227*3bfaa971Sbrad DPRINTFN(20,("umcpmio_send_report: LOOP for response. sc_res_ready=%d, err_count=%d, timeout=%d\n",sc->sc_res_ready, err_count, mstohz(timeout))); 228*3bfaa971Sbrad 229*3bfaa971Sbrad err = cv_timedwait_sig(&sc->sc_res_cv, &sc->sc_res_mutex, mstohz(timeout)); 230*3bfaa971Sbrad 231*3bfaa971Sbrad /* We are only going to allow this to loop on an error, 232*3bfaa971Sbrad * any error at all, so many times. 233*3bfaa971Sbrad */ 234*3bfaa971Sbrad if (err) { 235*3bfaa971Sbrad DPRINTF(("umcpmio_send_report: cv_timedwait_sig reported an error: err=%d, sc->sc_res_ready=%d\n",err,sc->sc_res_ready)); 236*3bfaa971Sbrad err_count++; 237*3bfaa971Sbrad } 238*3bfaa971Sbrad 239*3bfaa971Sbrad /* The CV was interrupted, but the buffer is ready so, clear the error 240*3bfaa971Sbrad * and break out. 241*3bfaa971Sbrad */ 242*3bfaa971Sbrad if ((err == ERESTART) && (sc->sc_res_ready)) { 243*3bfaa971Sbrad DPRINTF(("umcpmio_send_report: ERESTART and buffer is ready\n")); 244*3bfaa971Sbrad err = 0; 245*3bfaa971Sbrad break; 246*3bfaa971Sbrad } 247*3bfaa971Sbrad 248*3bfaa971Sbrad /* Too many times though the loop, just break out. Turn 249*3bfaa971Sbrad * a ERESTART (interruption) into a I/O error at this point. 250*3bfaa971Sbrad */ 251*3bfaa971Sbrad if (err_count > sc->sc_response_errcnt) { 252*3bfaa971Sbrad DPRINTF(("umcpmio_send_report: err_count exceeded: err=%d\n",err)); 253*3bfaa971Sbrad if (err == ERESTART) 254*3bfaa971Sbrad err = EIO; 255*3bfaa971Sbrad break; 256*3bfaa971Sbrad } 257*3bfaa971Sbrad 258*3bfaa971Sbrad /* This is a normal timeout, without interruption, try again */ 259*3bfaa971Sbrad if (err == EWOULDBLOCK) { 260*3bfaa971Sbrad DPRINTF(("umcpmio_send_report: EWOULDBLOCK: err_count=%d\n",err_count)); 261*3bfaa971Sbrad continue; 262*3bfaa971Sbrad } 263*3bfaa971Sbrad 264*3bfaa971Sbrad /* The CV was interrupted and the buffer wasn't filled in, so try again */ 265*3bfaa971Sbrad if ((err == ERESTART) && (!sc->sc_res_ready)) { 266*3bfaa971Sbrad DPRINTF(("umcpmio_send_report: ERESTART and buffer is NOT ready. err_count=%d\n",err_count)); 267*3bfaa971Sbrad continue; 268*3bfaa971Sbrad } 269*3bfaa971Sbrad } 270*3bfaa971Sbrad 271*3bfaa971Sbrad sc->sc_res_buffer = NULL; 272*3bfaa971Sbrad sc->sc_res_ready = false; 273*3bfaa971Sbrad mutex_exit(&sc->sc_res_mutex); 274*3bfaa971Sbrad 275*3bfaa971Sbrad /* Turn most errors into an I/O error */ 276*3bfaa971Sbrad if (err && 277*3bfaa971Sbrad err != ERESTART) 278*3bfaa971Sbrad err = EIO; 279*3bfaa971Sbrad 280*3bfaa971Sbrad out: 281*3bfaa971Sbrad return err; 282*3bfaa971Sbrad } 283*3bfaa971Sbrad 284*3bfaa971Sbrad /* These are standard gpio reads and set calls */ 285*3bfaa971Sbrad 286*3bfaa971Sbrad static int 287*3bfaa971Sbrad umcpmio_gpio_pin_read(void *arg, int pin) 288*3bfaa971Sbrad { 289*3bfaa971Sbrad struct umcpmio_softc *sc = arg; 290*3bfaa971Sbrad int r = GPIO_PIN_LOW; 291*3bfaa971Sbrad 292*3bfaa971Sbrad r = umcpmio_get_gpio_value(sc, pin, true); 293*3bfaa971Sbrad 294*3bfaa971Sbrad return(r); 295*3bfaa971Sbrad } 296*3bfaa971Sbrad 297*3bfaa971Sbrad static void 298*3bfaa971Sbrad umcpmio_gpio_pin_write(void *arg, int pin, int value) 299*3bfaa971Sbrad { 300*3bfaa971Sbrad struct umcpmio_softc *sc = arg; 301*3bfaa971Sbrad 302*3bfaa971Sbrad umcpmio_set_gpio_value_one(sc, pin, value, true); 303*3bfaa971Sbrad } 304*3bfaa971Sbrad 305*3bfaa971Sbrad /* Internal function that does the dirty work of setting a gpio 306*3bfaa971Sbrad * pin to its "type". 307*3bfaa971Sbrad * 308*3bfaa971Sbrad * There are really two ways to do some of this, one is to set the pin to input 309*3bfaa971Sbrad * and output, or whatever, using SRAM calls, the other is to use the GPIO 310*3bfaa971Sbrad * config calls to set input and output and SRAM for everything else. This just 311*3bfaa971Sbrad * uses SRAM for everything. 312*3bfaa971Sbrad */ 313*3bfaa971Sbrad 314*3bfaa971Sbrad static int 315*3bfaa971Sbrad umcpmio_gpio_pin_ctlctl(void *arg, int pin, int flags, bool takemutex) 316*3bfaa971Sbrad { 317*3bfaa971Sbrad struct umcpmio_softc *sc = arg; 318*3bfaa971Sbrad struct mcp2221_set_sram_req set_sram_req; 319*3bfaa971Sbrad struct mcp2221_set_sram_res set_sram_res; 320*3bfaa971Sbrad struct mcp2221_get_sram_res current_sram_res; 321*3bfaa971Sbrad struct mcp2221_get_gpio_cfg_res current_gpio_cfg_res; 322*3bfaa971Sbrad int err = 0; 323*3bfaa971Sbrad 324*3bfaa971Sbrad if (sc->sc_dying) 325*3bfaa971Sbrad return 0; 326*3bfaa971Sbrad 327*3bfaa971Sbrad if (takemutex) 328*3bfaa971Sbrad mutex_enter(&sc->sc_action_mutex); 329*3bfaa971Sbrad 330*3bfaa971Sbrad err = umcpmio_get_sram(sc, ¤t_sram_res, false); 331*3bfaa971Sbrad if (err) 332*3bfaa971Sbrad goto out; 333*3bfaa971Sbrad 334*3bfaa971Sbrad err = umcpmio_get_gpio_cfg(sc, ¤t_gpio_cfg_res, false); 335*3bfaa971Sbrad if (err) 336*3bfaa971Sbrad goto out; 337*3bfaa971Sbrad 338*3bfaa971Sbrad /* You can't just set one pin, you must set all of them, so copy the 339*3bfaa971Sbrad * current settings for the pin we are not messing with. 340*3bfaa971Sbrad * 341*3bfaa971Sbrad * And, yes, of course, if the MCP-2210 is ever supported with this 342*3bfaa971Sbrad * driver, this sort of unrolling will need to be turned into 343*3bfaa971Sbrad * something different, but for now, just unroll as there are only 344*3bfaa971Sbrad * 4 pins to care about. 345*3bfaa971Sbrad * 346*3bfaa971Sbrad */ 347*3bfaa971Sbrad 348*3bfaa971Sbrad memset(&set_sram_req, 0, MCP2221_REQ_BUFFER_SIZE); 349*3bfaa971Sbrad switch (pin) { 350*3bfaa971Sbrad case 0: 351*3bfaa971Sbrad set_sram_req.gp1_settings = current_sram_res.gp1_settings; 352*3bfaa971Sbrad set_sram_req.gp2_settings = current_sram_res.gp2_settings; 353*3bfaa971Sbrad set_sram_req.gp3_settings = current_sram_res.gp3_settings; 354*3bfaa971Sbrad break; 355*3bfaa971Sbrad case 1: 356*3bfaa971Sbrad set_sram_req.gp0_settings = current_sram_res.gp0_settings; 357*3bfaa971Sbrad set_sram_req.gp2_settings = current_sram_res.gp2_settings; 358*3bfaa971Sbrad set_sram_req.gp3_settings = current_sram_res.gp3_settings; 359*3bfaa971Sbrad break; 360*3bfaa971Sbrad case 2: 361*3bfaa971Sbrad set_sram_req.gp0_settings = current_sram_res.gp0_settings; 362*3bfaa971Sbrad set_sram_req.gp1_settings = current_sram_res.gp1_settings; 363*3bfaa971Sbrad set_sram_req.gp3_settings = current_sram_res.gp3_settings; 364*3bfaa971Sbrad break; 365*3bfaa971Sbrad case 3: 366*3bfaa971Sbrad set_sram_req.gp0_settings = current_sram_res.gp0_settings; 367*3bfaa971Sbrad set_sram_req.gp1_settings = current_sram_res.gp1_settings; 368*3bfaa971Sbrad set_sram_req.gp2_settings = current_sram_res.gp2_settings; 369*3bfaa971Sbrad break; 370*3bfaa971Sbrad } 371*3bfaa971Sbrad umcpmio_set_gpio_designation_sram(&set_sram_req, pin, flags); 372*3bfaa971Sbrad umcpmio_set_gpio_dir_sram(&set_sram_req, pin, flags); 373*3bfaa971Sbrad 374*3bfaa971Sbrad /* 375*3bfaa971Sbrad * This part is unfortunate... if a pin is set to output, the value set 376*3bfaa971Sbrad * on the pin is not mirrored by the chip into SRAM, but the chip will 377*3bfaa971Sbrad * use the value from SRAM to set the value of the pin. What this means is 378*3bfaa971Sbrad * that we have to learn the value from the GPIO config and make sure it is 379*3bfaa971Sbrad * set properly when updating SRAM. 380*3bfaa971Sbrad */ 381*3bfaa971Sbrad 382*3bfaa971Sbrad if (current_gpio_cfg_res.gp0_pin_dir == MCP2221_GPIO_CFG_DIR_OUTPUT) { 383*3bfaa971Sbrad if (current_gpio_cfg_res.gp0_pin_value == 1) 384*3bfaa971Sbrad set_sram_req.gp0_settings |= MCP2221_SRAM_GPIO_OUTPUT_HIGH; 385*3bfaa971Sbrad else 386*3bfaa971Sbrad set_sram_req.gp0_settings &= ~MCP2221_SRAM_GPIO_OUTPUT_HIGH; 387*3bfaa971Sbrad } 388*3bfaa971Sbrad if (current_gpio_cfg_res.gp1_pin_dir == MCP2221_GPIO_CFG_DIR_OUTPUT) { 389*3bfaa971Sbrad if (current_gpio_cfg_res.gp1_pin_value == 1) 390*3bfaa971Sbrad set_sram_req.gp1_settings |= MCP2221_SRAM_GPIO_OUTPUT_HIGH; 391*3bfaa971Sbrad else 392*3bfaa971Sbrad set_sram_req.gp1_settings &= ~MCP2221_SRAM_GPIO_OUTPUT_HIGH; 393*3bfaa971Sbrad } 394*3bfaa971Sbrad if (current_gpio_cfg_res.gp2_pin_dir == MCP2221_GPIO_CFG_DIR_OUTPUT) { 395*3bfaa971Sbrad if (current_gpio_cfg_res.gp2_pin_value == 1) 396*3bfaa971Sbrad set_sram_req.gp2_settings |= MCP2221_SRAM_GPIO_OUTPUT_HIGH; 397*3bfaa971Sbrad else 398*3bfaa971Sbrad set_sram_req.gp2_settings &= ~MCP2221_SRAM_GPIO_OUTPUT_HIGH; 399*3bfaa971Sbrad } 400*3bfaa971Sbrad if (current_gpio_cfg_res.gp3_pin_dir == MCP2221_GPIO_CFG_DIR_OUTPUT) { 401*3bfaa971Sbrad if (current_gpio_cfg_res.gp3_pin_value == 1) 402*3bfaa971Sbrad set_sram_req.gp3_settings |= MCP2221_SRAM_GPIO_OUTPUT_HIGH; 403*3bfaa971Sbrad else 404*3bfaa971Sbrad set_sram_req.gp3_settings &= ~MCP2221_SRAM_GPIO_OUTPUT_HIGH; 405*3bfaa971Sbrad } 406*3bfaa971Sbrad 407*3bfaa971Sbrad err = umcpmio_put_sram(sc, &set_sram_req, &set_sram_res, false); 408*3bfaa971Sbrad if (! err) { 409*3bfaa971Sbrad umcpmio_dump_buffer(sc->sc_dumpbuffer, (uint8_t *)&set_sram_res, MCP2221_RES_BUFFER_SIZE, "umcpmio_gpio_pin_ctlctl set sram buffer copy"); 410*3bfaa971Sbrad if (set_sram_res.cmd == MCP2221_CMD_SET_SRAM && 411*3bfaa971Sbrad set_sram_res.completion == MCP2221_CMD_COMPLETE_OK) { 412*3bfaa971Sbrad sc->sc_gpio_pins[pin].pin_flags = flags; 413*3bfaa971Sbrad } else { 414*3bfaa971Sbrad device_printf(sc->sc_dev, "umcpmio_gpio_pin_ctlctl: not the command desired, or error: %02x %02x\n", 415*3bfaa971Sbrad set_sram_res.cmd, 416*3bfaa971Sbrad set_sram_res.completion); 417*3bfaa971Sbrad err = EIO; 418*3bfaa971Sbrad } 419*3bfaa971Sbrad } 420*3bfaa971Sbrad 421*3bfaa971Sbrad out: 422*3bfaa971Sbrad if (takemutex) 423*3bfaa971Sbrad mutex_exit(&sc->sc_action_mutex); 424*3bfaa971Sbrad 425*3bfaa971Sbrad return err; 426*3bfaa971Sbrad } 427*3bfaa971Sbrad 428*3bfaa971Sbrad static void 429*3bfaa971Sbrad umcpmio_gpio_pin_ctl(void *arg, int pin, int flags) 430*3bfaa971Sbrad { 431*3bfaa971Sbrad struct umcpmio_softc *sc = arg; 432*3bfaa971Sbrad 433*3bfaa971Sbrad if (sc->sc_dying) 434*3bfaa971Sbrad return; 435*3bfaa971Sbrad 436*3bfaa971Sbrad umcpmio_gpio_pin_ctlctl(sc, pin, flags, true); 437*3bfaa971Sbrad } 438*3bfaa971Sbrad 439*3bfaa971Sbrad /* 440*3bfaa971Sbrad XXX - 441*3bfaa971Sbrad 442*3bfaa971Sbrad Since testing of gpio interrupts wasn't possible, this part probably is not 443*3bfaa971Sbrad complete. At the very least, there is a scheduled callout that needs to exist 444*3bfaa971Sbrad to read the interrupt status. The chip does not send anything on its own when 445*3bfaa971Sbrad the interrupt happens. 446*3bfaa971Sbrad */ 447*3bfaa971Sbrad 448*3bfaa971Sbrad 449*3bfaa971Sbrad static void * 450*3bfaa971Sbrad umcpmio_gpio_intr_establish(void *vsc, int pin, int ipl, int irqmode, 451*3bfaa971Sbrad int (*func)(void *), void *arg) 452*3bfaa971Sbrad { 453*3bfaa971Sbrad struct umcpmio_softc *sc = vsc; 454*3bfaa971Sbrad struct umcpmio_irq *irq = &sc->sc_gpio_irqs[0]; 455*3bfaa971Sbrad struct mcp2221_set_sram_req set_sram_req; 456*3bfaa971Sbrad struct mcp2221_set_sram_res set_sram_res; 457*3bfaa971Sbrad struct mcp2221_get_sram_res current_sram_res; 458*3bfaa971Sbrad int err = 0; 459*3bfaa971Sbrad 460*3bfaa971Sbrad if (sc->sc_dying) 461*3bfaa971Sbrad return(NULL); 462*3bfaa971Sbrad 463*3bfaa971Sbrad irq->sc_gpio_irqfunc = func; 464*3bfaa971Sbrad irq->sc_gpio_irqarg = arg; 465*3bfaa971Sbrad 466*3bfaa971Sbrad DPRINTF(("umcpmio_intr_establish: pin=%d, irqmode=%04x\n",pin,irqmode)); 467*3bfaa971Sbrad 468*3bfaa971Sbrad mutex_enter(&sc->sc_action_mutex); 469*3bfaa971Sbrad 470*3bfaa971Sbrad err = umcpmio_get_sram(sc, ¤t_sram_res, false); 471*3bfaa971Sbrad if (err) 472*3bfaa971Sbrad goto out; 473*3bfaa971Sbrad 474*3bfaa971Sbrad memset(&set_sram_req, 0, MCP2221_REQ_BUFFER_SIZE); 475*3bfaa971Sbrad set_sram_req.gp0_settings = current_sram_res.gp0_settings; 476*3bfaa971Sbrad set_sram_req.gp2_settings = current_sram_res.gp2_settings; 477*3bfaa971Sbrad set_sram_req.gp3_settings = current_sram_res.gp3_settings; 478*3bfaa971Sbrad umcpmio_set_gpio_irq_sram(&set_sram_req, irqmode); 479*3bfaa971Sbrad err = umcpmio_put_sram(sc, &set_sram_req, &set_sram_res, false); 480*3bfaa971Sbrad if (! err) { 481*3bfaa971Sbrad umcpmio_dump_buffer(sc->sc_dumpbuffer, (uint8_t *)&set_sram_res, MCP2221_RES_BUFFER_SIZE, "umcpmio_intr_establish set sram buffer copy"); 482*3bfaa971Sbrad if (set_sram_res.cmd == MCP2221_CMD_SET_SRAM && 483*3bfaa971Sbrad set_sram_res.completion == MCP2221_CMD_COMPLETE_OK) { 484*3bfaa971Sbrad sc->sc_gpio_pins[1].pin_flags = GPIO_PIN_ALT2; 485*3bfaa971Sbrad } else { 486*3bfaa971Sbrad device_printf(sc->sc_dev, "umcpmio_intr_establish: not the command desired, or error: %02x %02x\n", 487*3bfaa971Sbrad set_sram_res.cmd, 488*3bfaa971Sbrad set_sram_res.completion); 489*3bfaa971Sbrad } 490*3bfaa971Sbrad } else { 491*3bfaa971Sbrad device_printf(sc->sc_dev, "umcpmio_intr_establish: set sram error: err=%d\n", 492*3bfaa971Sbrad err); 493*3bfaa971Sbrad } 494*3bfaa971Sbrad 495*3bfaa971Sbrad out: 496*3bfaa971Sbrad mutex_exit(&sc->sc_action_mutex); 497*3bfaa971Sbrad 498*3bfaa971Sbrad return(irq); 499*3bfaa971Sbrad } 500*3bfaa971Sbrad 501*3bfaa971Sbrad static void 502*3bfaa971Sbrad umcpmio_gpio_intr_disestablish(void *vsc, void *ih) 503*3bfaa971Sbrad { 504*3bfaa971Sbrad struct umcpmio_softc *sc = vsc; 505*3bfaa971Sbrad struct mcp2221_set_sram_req set_sram_req; 506*3bfaa971Sbrad struct mcp2221_set_sram_res set_sram_res; 507*3bfaa971Sbrad struct mcp2221_get_sram_res current_sram_res; 508*3bfaa971Sbrad int err = 0; 509*3bfaa971Sbrad 510*3bfaa971Sbrad if (sc->sc_dying) 511*3bfaa971Sbrad return; 512*3bfaa971Sbrad 513*3bfaa971Sbrad DPRINTF(("umcpmio_intr_disestablish:\n")); 514*3bfaa971Sbrad 515*3bfaa971Sbrad mutex_enter(&sc->sc_action_mutex); 516*3bfaa971Sbrad 517*3bfaa971Sbrad err = umcpmio_get_sram(sc, ¤t_sram_res, false); 518*3bfaa971Sbrad if (err) 519*3bfaa971Sbrad goto out; 520*3bfaa971Sbrad 521*3bfaa971Sbrad memset(&set_sram_req, 0, MCP2221_REQ_BUFFER_SIZE); 522*3bfaa971Sbrad set_sram_req.gp0_settings = current_sram_res.gp0_settings; 523*3bfaa971Sbrad set_sram_req.gp2_settings = current_sram_res.gp2_settings; 524*3bfaa971Sbrad set_sram_req.gp3_settings = current_sram_res.gp3_settings; 525*3bfaa971Sbrad umcpmio_set_gpio_irq_sram(&set_sram_req, 0); 526*3bfaa971Sbrad err = umcpmio_put_sram(sc, &set_sram_req, &set_sram_res, true); 527*3bfaa971Sbrad if (! err) { 528*3bfaa971Sbrad umcpmio_dump_buffer(sc->sc_dumpbuffer, (uint8_t *)&set_sram_res, MCP2221_RES_BUFFER_SIZE, "umcpmio_intr_disestablish set sram buffer copy"); 529*3bfaa971Sbrad if (set_sram_res.cmd == MCP2221_CMD_SET_SRAM && 530*3bfaa971Sbrad set_sram_res.completion == MCP2221_CMD_COMPLETE_OK) { 531*3bfaa971Sbrad sc->sc_gpio_pins[1].pin_flags = GPIO_PIN_INPUT; 532*3bfaa971Sbrad } else { 533*3bfaa971Sbrad device_printf(sc->sc_dev, "umcpmio_intr_disestablish: not the command desired, or error: %02x %02x\n", 534*3bfaa971Sbrad set_sram_res.cmd, 535*3bfaa971Sbrad set_sram_res.completion); 536*3bfaa971Sbrad } 537*3bfaa971Sbrad } else { 538*3bfaa971Sbrad device_printf(sc->sc_dev, "umcpmio_intr_disestablish: set sram error: err=%d\n", 539*3bfaa971Sbrad err); 540*3bfaa971Sbrad } 541*3bfaa971Sbrad out: 542*3bfaa971Sbrad mutex_exit(&sc->sc_action_mutex); 543*3bfaa971Sbrad } 544*3bfaa971Sbrad 545*3bfaa971Sbrad static bool 546*3bfaa971Sbrad umcpmio_gpio_intrstr(void *vsc, int pin, int irqmode, char *buf, size_t buflen) 547*3bfaa971Sbrad { 548*3bfaa971Sbrad 549*3bfaa971Sbrad if (pin < 0 || pin >= MCP2221_NPINS) { 550*3bfaa971Sbrad DPRINTF(("umcpmio_gpio_intrstr: pin %d less than zero or too big\n",pin)); 551*3bfaa971Sbrad return (false); 552*3bfaa971Sbrad } 553*3bfaa971Sbrad 554*3bfaa971Sbrad if (pin != 1) { 555*3bfaa971Sbrad DPRINTF(("umcpmio_gpio_intrstr: pin %d was not 1\n",pin)); 556*3bfaa971Sbrad return (false); 557*3bfaa971Sbrad } 558*3bfaa971Sbrad 559*3bfaa971Sbrad snprintf(buf, buflen, "GPIO %d", pin); 560*3bfaa971Sbrad 561*3bfaa971Sbrad return (true); 562*3bfaa971Sbrad } 563*3bfaa971Sbrad 564*3bfaa971Sbrad /* Clear status of the I2C engine */ 565*3bfaa971Sbrad 566*3bfaa971Sbrad static int 567*3bfaa971Sbrad umcpmio_i2c_clear(struct umcpmio_softc *sc, bool takemutex) 568*3bfaa971Sbrad { 569*3bfaa971Sbrad int err = 0; 570*3bfaa971Sbrad struct mcp2221_status_req status_req; 571*3bfaa971Sbrad struct mcp2221_status_res status_res; 572*3bfaa971Sbrad 573*3bfaa971Sbrad memset(&status_req, 0, MCP2221_REQ_BUFFER_SIZE); 574*3bfaa971Sbrad status_req.cmd = MCP2221_CMD_STATUS; 575*3bfaa971Sbrad status_req.cancel_transfer = MCP2221_I2C_DO_CANCEL; 576*3bfaa971Sbrad 577*3bfaa971Sbrad if (takemutex) 578*3bfaa971Sbrad mutex_enter(&sc->sc_action_mutex); 579*3bfaa971Sbrad err = umcpmio_send_report(sc, (uint8_t *)&status_req, MCP2221_REQ_BUFFER_SIZE, (uint8_t *)&status_res, sc->sc_cv_wait); 580*3bfaa971Sbrad if (takemutex) 581*3bfaa971Sbrad mutex_exit(&sc->sc_action_mutex); 582*3bfaa971Sbrad 583*3bfaa971Sbrad if (! err) { 584*3bfaa971Sbrad umcpmio_dump_buffer(sc->sc_dumpbuffer, (uint8_t *)&status_res, MCP2221_RES_BUFFER_SIZE, "umcpmio_i2c_clear buffer copy"); 585*3bfaa971Sbrad if (status_res.cmd == MCP2221_CMD_STATUS && 586*3bfaa971Sbrad status_res.completion == MCP2221_CMD_COMPLETE_OK) { 587*3bfaa971Sbrad umcpmio_dump_buffer(true, (uint8_t *)&status_res, MCP2221_RES_BUFFER_SIZE, "umcpmio_i2c_clear res buffer"); 588*3bfaa971Sbrad } else { 589*3bfaa971Sbrad device_printf(sc->sc_dev, "umcpmio_i2c_clear: cmd exec: not the command desired, or error: %02x %02x\n", 590*3bfaa971Sbrad status_res.cmd, 591*3bfaa971Sbrad status_res.completion); 592*3bfaa971Sbrad err = EIO; 593*3bfaa971Sbrad } 594*3bfaa971Sbrad } else { 595*3bfaa971Sbrad device_printf(sc->sc_dev, "umcpmio_i2c_clear: request error: err=%d\n", err); 596*3bfaa971Sbrad err = EIO; 597*3bfaa971Sbrad } 598*3bfaa971Sbrad 599*3bfaa971Sbrad return(err); 600*3bfaa971Sbrad } 601*3bfaa971Sbrad 602*3bfaa971Sbrad /* There isn't much required to acquire or release the I2C bus, but the man 603*3bfaa971Sbrad * pages says these are needed 604*3bfaa971Sbrad */ 605*3bfaa971Sbrad 606*3bfaa971Sbrad static int 607*3bfaa971Sbrad umcpmio_acquire_bus(void *v, int flags) 608*3bfaa971Sbrad { 609*3bfaa971Sbrad return(0); 610*3bfaa971Sbrad } 611*3bfaa971Sbrad 612*3bfaa971Sbrad static void 613*3bfaa971Sbrad umcpmio_release_bus(void *v, int flags) 614*3bfaa971Sbrad { 615*3bfaa971Sbrad return; 616*3bfaa971Sbrad } 617*3bfaa971Sbrad 618*3bfaa971Sbrad /* The I2C write and I2C read functions mostly use an algorithm that Adafruit 619*3bfaa971Sbrad * came up with in their Python based driver. A lot of other people have used 620*3bfaa971Sbrad * this same algorithm to good effect. If changes are made to the I2C read and 621*3bfaa971Sbrad * write functions, it is HIGHLY advisable that a MCP2221 or MCP2221A be on 622*3bfaa971Sbrad * hand to test them. 623*3bfaa971Sbrad */ 624*3bfaa971Sbrad 625*3bfaa971Sbrad 626*3bfaa971Sbrad /* This is what is considered a fatal return from the engine. */ 627*3bfaa971Sbrad 628*3bfaa971Sbrad static bool 629*3bfaa971Sbrad umcpmio_i2c_fatal(uint8_t state) 630*3bfaa971Sbrad { 631*3bfaa971Sbrad int r = false; 632*3bfaa971Sbrad 633*3bfaa971Sbrad if (state == MCP2221_ENGINE_ADDRNACK || 634*3bfaa971Sbrad state == MCP2221_ENGINE_STARTTIMEOUT || 635*3bfaa971Sbrad state == MCP2221_ENGINE_REPSTARTTIMEOUT || 636*3bfaa971Sbrad state == MCP2221_ENGINE_STOPTIMEOUT || 637*3bfaa971Sbrad state == MCP2221_ENGINE_READTIMEOUT || 638*3bfaa971Sbrad state == MCP2221_ENGINE_WRITETIMEOUT || 639*3bfaa971Sbrad state == MCP2221_ENGINE_ADDRTIMEOUT) 640*3bfaa971Sbrad r = true; 641*3bfaa971Sbrad return(r); 642*3bfaa971Sbrad } 643*3bfaa971Sbrad 644*3bfaa971Sbrad static int 645*3bfaa971Sbrad umcpmio_i2c_write(struct umcpmio_softc *sc, i2c_op_t op, i2c_addr_t addr, 646*3bfaa971Sbrad const void *cmdbuf, size_t cmdlen, void *databuf, size_t datalen, int flags) 647*3bfaa971Sbrad { 648*3bfaa971Sbrad struct mcp2221_i2c_req i2c_req; 649*3bfaa971Sbrad struct mcp2221_i2c_res i2c_res; 650*3bfaa971Sbrad struct mcp2221_status_res status_res; 651*3bfaa971Sbrad int remaining; 652*3bfaa971Sbrad int err = 0; 653*3bfaa971Sbrad uint8_t cmd; 654*3bfaa971Sbrad size_t totallen = 0; 655*3bfaa971Sbrad int wretry = sc->sc_retry_busy_write; 656*3bfaa971Sbrad int wsretry = sc->sc_retry_busy_write; 657*3bfaa971Sbrad 658*3bfaa971Sbrad err = umcpmio_get_status(sc, &status_res, true); 659*3bfaa971Sbrad if (err) 660*3bfaa971Sbrad goto out; 661*3bfaa971Sbrad if (status_res.internal_i2c_state != 0) { 662*3bfaa971Sbrad DPRINTF(("umcpmio_i2c_write: internal state not zero, clearing. internal_i2c_state=%02x\n",status_res.internal_i2c_state)); 663*3bfaa971Sbrad err = umcpmio_i2c_clear(sc, true); 664*3bfaa971Sbrad } 665*3bfaa971Sbrad if (err) 666*3bfaa971Sbrad goto out; 667*3bfaa971Sbrad 668*3bfaa971Sbrad if (cmdbuf != NULL) 669*3bfaa971Sbrad totallen += cmdlen; 670*3bfaa971Sbrad if (databuf != NULL) 671*3bfaa971Sbrad totallen += datalen; 672*3bfaa971Sbrad 673*3bfaa971Sbrad again: 674*3bfaa971Sbrad memset(&i2c_req, 0, MCP2221_REQ_BUFFER_SIZE); 675*3bfaa971Sbrad cmd = MCP2221_I2C_WRITE_DATA_NS; 676*3bfaa971Sbrad if (I2C_OP_STOP_P(op)) 677*3bfaa971Sbrad cmd = MCP2221_I2C_WRITE_DATA; 678*3bfaa971Sbrad i2c_req.cmd = cmd; 679*3bfaa971Sbrad i2c_req.lsblen = totallen; 680*3bfaa971Sbrad i2c_req.msblen = 0; 681*3bfaa971Sbrad i2c_req.slaveaddr = addr << 1; 682*3bfaa971Sbrad 683*3bfaa971Sbrad remaining = 0; 684*3bfaa971Sbrad if (cmdbuf != NULL) { 685*3bfaa971Sbrad memcpy(&i2c_req.data[0], cmdbuf, cmdlen); 686*3bfaa971Sbrad remaining = cmdlen; 687*3bfaa971Sbrad } 688*3bfaa971Sbrad if (databuf != NULL) 689*3bfaa971Sbrad memcpy(&i2c_req.data[remaining], databuf, datalen); 690*3bfaa971Sbrad 691*3bfaa971Sbrad DPRINTF(("umcpmio_i2c_write: I2C WRITE: cmd: %02x\n",cmd)); 692*3bfaa971Sbrad umcpmio_dump_buffer(sc->sc_dumpbuffer, (uint8_t *)&i2c_req, MCP2221_REQ_BUFFER_SIZE, "umcpmio_i2c_write: write req buffer copy"); 693*3bfaa971Sbrad 694*3bfaa971Sbrad mutex_enter(&sc->sc_action_mutex); 695*3bfaa971Sbrad err = umcpmio_send_report(sc, (uint8_t *)&i2c_req, MCP2221_REQ_BUFFER_SIZE, (uint8_t *)&i2c_res, sc->sc_cv_wait); 696*3bfaa971Sbrad mutex_exit(&sc->sc_action_mutex); 697*3bfaa971Sbrad if (! err) { 698*3bfaa971Sbrad umcpmio_dump_buffer(sc->sc_dumpbuffer, (uint8_t *)&i2c_res, MCP2221_RES_BUFFER_SIZE, "umcpmio_i2c_write: write res buffer copy"); 699*3bfaa971Sbrad if (i2c_res.cmd == cmd && 700*3bfaa971Sbrad i2c_res.completion == MCP2221_CMD_COMPLETE_OK) { 701*3bfaa971Sbrad /* Adafruit does a read back of the status at this 702*3bfaa971Sbrad * point. We choose not to do that. That is done later 703*3bfaa971Sbrad * anyway, and it seemed to be redundent. 704*3bfaa971Sbrad */ 705*3bfaa971Sbrad } else { 706*3bfaa971Sbrad if (i2c_res.cmd == cmd && 707*3bfaa971Sbrad i2c_res.completion == MCP2221_I2C_ENGINE_BUSY) { 708*3bfaa971Sbrad DPRINTF(("umcpmio_i2c_write: I2C engine busy\n")); 709*3bfaa971Sbrad 710*3bfaa971Sbrad if (umcpmio_i2c_fatal(i2c_res.internal_i2c_state)) { 711*3bfaa971Sbrad err = EIO; 712*3bfaa971Sbrad } else { 713*3bfaa971Sbrad wretry--; 714*3bfaa971Sbrad if (wretry > 0) { 715*3bfaa971Sbrad WAITMS(sc->sc_busy_delay); 716*3bfaa971Sbrad goto again; 717*3bfaa971Sbrad } else { 718*3bfaa971Sbrad err = EBUSY; 719*3bfaa971Sbrad } 720*3bfaa971Sbrad } 721*3bfaa971Sbrad } else { 722*3bfaa971Sbrad device_printf(sc->sc_dev, "umcpmio_i2c_write: not the command desired, or error: %02x %02x\n", 723*3bfaa971Sbrad i2c_res.cmd, 724*3bfaa971Sbrad i2c_res.completion); 725*3bfaa971Sbrad err = EIO; 726*3bfaa971Sbrad } 727*3bfaa971Sbrad } 728*3bfaa971Sbrad } else { 729*3bfaa971Sbrad device_printf(sc->sc_dev, "umcpmio_i2c_write request error: err=%d\n", err); 730*3bfaa971Sbrad err = EIO; 731*3bfaa971Sbrad } 732*3bfaa971Sbrad 733*3bfaa971Sbrad if (! err) { 734*3bfaa971Sbrad while (wsretry > 0) { 735*3bfaa971Sbrad wsretry--; 736*3bfaa971Sbrad 737*3bfaa971Sbrad DPRINTF(("umcpmio_i2c_write: checking status loop: wcretry=%d\n",wsretry)); 738*3bfaa971Sbrad 739*3bfaa971Sbrad err = umcpmio_get_status(sc, &status_res, true); 740*3bfaa971Sbrad if (! err) { 741*3bfaa971Sbrad umcpmio_dump_buffer(sc->sc_dumpbuffer, (uint8_t *)&status_res, MCP2221_RES_BUFFER_SIZE, "umcpmio_i2c_write post check status"); 742*3bfaa971Sbrad /* Since there isn't any documentation on what 743*3bfaa971Sbrad * some of the internal state means, it isn't 744*3bfaa971Sbrad * clear that this is any different than than 745*3bfaa971Sbrad * MCP2221_ENGINE_ADDRNACK in the other state 746*3bfaa971Sbrad * register. 747*3bfaa971Sbrad */ 748*3bfaa971Sbrad 749*3bfaa971Sbrad if (status_res.internal_i2c_state20 & MCP2221_ENGINE_T1_MASK_NACK) { 750*3bfaa971Sbrad DPRINTF(("umcpmio_i2c_write post check: engine internal state T1 says NACK\n")); 751*3bfaa971Sbrad err = EIO; 752*3bfaa971Sbrad break; 753*3bfaa971Sbrad } 754*3bfaa971Sbrad if (status_res.internal_i2c_state == 0) { 755*3bfaa971Sbrad DPRINTF(("umcpmio_i2c_write post check: engine internal state is ZERO\n")); 756*3bfaa971Sbrad err = 0; 757*3bfaa971Sbrad break; 758*3bfaa971Sbrad } 759*3bfaa971Sbrad if (status_res.internal_i2c_state == MCP2221_ENGINE_WRITINGNOSTOP && 760*3bfaa971Sbrad cmd == MCP2221_I2C_WRITE_DATA_NS) { 761*3bfaa971Sbrad DPRINTF(("umcpmio_i2c_write post check: engine internal state is WRITINGNOSTOP\n")); 762*3bfaa971Sbrad err = 0; 763*3bfaa971Sbrad break; 764*3bfaa971Sbrad } 765*3bfaa971Sbrad if (umcpmio_i2c_fatal(status_res.internal_i2c_state)) { 766*3bfaa971Sbrad DPRINTF(("umcpmio_i2c_write post check: engine internal state is fatal: %02x\n", status_res.internal_i2c_state)); 767*3bfaa971Sbrad err = EIO; 768*3bfaa971Sbrad break; 769*3bfaa971Sbrad } 770*3bfaa971Sbrad WAITMS(sc->sc_busy_delay); 771*3bfaa971Sbrad } else { 772*3bfaa971Sbrad err = EIO; 773*3bfaa971Sbrad break; 774*3bfaa971Sbrad } 775*3bfaa971Sbrad } 776*3bfaa971Sbrad } 777*3bfaa971Sbrad 778*3bfaa971Sbrad out: 779*3bfaa971Sbrad 780*3bfaa971Sbrad return(err); 781*3bfaa971Sbrad } 782*3bfaa971Sbrad 783*3bfaa971Sbrad /* This one deviates a bit from Adafruit in that is supports a straight read and 784*3bfaa971Sbrad * a write + read. That is, write a register to read from and then do the read. 785*3bfaa971Sbrad */ 786*3bfaa971Sbrad 787*3bfaa971Sbrad static int 788*3bfaa971Sbrad umcpmio_i2c_read(struct umcpmio_softc *sc, i2c_op_t op, i2c_addr_t addr, 789*3bfaa971Sbrad const void *cmdbuf, size_t cmdlen, void *databuf, size_t datalen, int flags) 790*3bfaa971Sbrad { 791*3bfaa971Sbrad struct mcp2221_i2c_req i2c_req; 792*3bfaa971Sbrad struct mcp2221_i2c_res i2c_res; 793*3bfaa971Sbrad struct mcp2221_i2c_fetch_req i2c_fetch_req; 794*3bfaa971Sbrad struct mcp2221_i2c_fetch_res i2c_fetch_res; 795*3bfaa971Sbrad struct mcp2221_status_res status_res; 796*3bfaa971Sbrad int err = 0; 797*3bfaa971Sbrad uint8_t cmd; 798*3bfaa971Sbrad int rretry = sc->sc_retry_busy_read; 799*3bfaa971Sbrad 800*3bfaa971Sbrad if (cmdbuf != NULL) { 801*3bfaa971Sbrad DPRINTF(("umcpmio_i2c_read: has a cmdbuf, doing write first: addr=%02x\n",addr)); 802*3bfaa971Sbrad err = umcpmio_i2c_write(sc, I2C_OP_WRITE, addr, cmdbuf, cmdlen, NULL, 0, flags); 803*3bfaa971Sbrad } 804*3bfaa971Sbrad if (err) 805*3bfaa971Sbrad goto out; 806*3bfaa971Sbrad 807*3bfaa971Sbrad err = umcpmio_get_status(sc, &status_res, true); 808*3bfaa971Sbrad if (err) 809*3bfaa971Sbrad goto out; 810*3bfaa971Sbrad 811*3bfaa971Sbrad if (status_res.internal_i2c_state !=0 && 812*3bfaa971Sbrad status_res.internal_i2c_state != MCP2221_ENGINE_WRITINGNOSTOP) { 813*3bfaa971Sbrad DPRINTF(("umcpmio_i2c_read: internal state not zero and not WRITINGNOSTOP, clearing. internal_i2c_state=%02x\n",status_res.internal_i2c_state)); 814*3bfaa971Sbrad err = umcpmio_i2c_clear(sc, true); 815*3bfaa971Sbrad } 816*3bfaa971Sbrad if (err) 817*3bfaa971Sbrad goto out; 818*3bfaa971Sbrad 819*3bfaa971Sbrad memset(&i2c_req, 0, MCP2221_REQ_BUFFER_SIZE); 820*3bfaa971Sbrad if (cmdbuf == NULL && 821*3bfaa971Sbrad status_res.internal_i2c_state != MCP2221_ENGINE_WRITINGNOSTOP) { 822*3bfaa971Sbrad cmd = MCP2221_I2C_READ_DATA; 823*3bfaa971Sbrad } else { 824*3bfaa971Sbrad cmd = MCP2221_I2C_READ_DATA_RS; 825*3bfaa971Sbrad } 826*3bfaa971Sbrad 827*3bfaa971Sbrad /* The chip apparently can't do a READ without a STOP operation. Report that, and try 828*3bfaa971Sbrad * treating it like a READ with a STOP. This won't work for a lot of devices. 829*3bfaa971Sbrad */ 830*3bfaa971Sbrad 831*3bfaa971Sbrad if (!I2C_OP_STOP_P(op) && 832*3bfaa971Sbrad sc->sc_reportreadnostop) 833*3bfaa971Sbrad device_printf(sc->sc_dev,"umcpmio_i2c_read: ************ called with READ without STOP ***************\n"); 834*3bfaa971Sbrad 835*3bfaa971Sbrad i2c_req.cmd = cmd; 836*3bfaa971Sbrad i2c_req.lsblen = datalen; 837*3bfaa971Sbrad i2c_req.msblen = 0; 838*3bfaa971Sbrad i2c_req.slaveaddr = (addr << 1) | 0x01; 839*3bfaa971Sbrad 840*3bfaa971Sbrad DPRINTF(("umcpmio_i2c_read: I2C READ normal read: cmd=%02x, addr=%02x\n",cmd,addr)); 841*3bfaa971Sbrad 842*3bfaa971Sbrad umcpmio_dump_buffer(sc->sc_dumpbuffer, (uint8_t *)&i2c_req, MCP2221_RES_BUFFER_SIZE, "umcpmio_i2c_read normal read req buffer copy"); 843*3bfaa971Sbrad 844*3bfaa971Sbrad mutex_enter(&sc->sc_action_mutex); 845*3bfaa971Sbrad err = umcpmio_send_report(sc, (uint8_t *)&i2c_req, MCP2221_REQ_BUFFER_SIZE, (uint8_t *)&i2c_res, sc->sc_cv_wait); 846*3bfaa971Sbrad mutex_exit(&sc->sc_action_mutex); 847*3bfaa971Sbrad 848*3bfaa971Sbrad if (! err) { 849*3bfaa971Sbrad umcpmio_dump_buffer(sc->sc_dumpbuffer, (uint8_t *)&i2c_res, MCP2221_RES_BUFFER_SIZE, "umcpmio_i2c_read read-request response buffer copy"); 850*3bfaa971Sbrad 851*3bfaa971Sbrad while (rretry > 0) { 852*3bfaa971Sbrad rretry--; 853*3bfaa971Sbrad DPRINTF(("umcpmio_i2c_read: fetch loop: rretry=%d\n",rretry)); 854*3bfaa971Sbrad err = 0; 855*3bfaa971Sbrad memset(&i2c_fetch_req, 0, MCP2221_REQ_BUFFER_SIZE); 856*3bfaa971Sbrad i2c_fetch_req.cmd = MCP2221_CMD_I2C_FETCH_READ_DATA; 857*3bfaa971Sbrad mutex_enter(&sc->sc_action_mutex); 858*3bfaa971Sbrad err = umcpmio_send_report(sc, (uint8_t *)&i2c_fetch_req, MCP2221_REQ_BUFFER_SIZE, (uint8_t *)&i2c_fetch_res, sc->sc_cv_wait); 859*3bfaa971Sbrad mutex_exit(&sc->sc_action_mutex); 860*3bfaa971Sbrad umcpmio_dump_buffer(sc->sc_dumpbuffer, (uint8_t *)&i2c_fetch_req, MCP2221_RES_BUFFER_SIZE, "umcpmio_i2c_read fetch res buffer copy"); 861*3bfaa971Sbrad 862*3bfaa971Sbrad if (i2c_fetch_res.cmd == MCP2221_CMD_I2C_FETCH_READ_DATA) { 863*3bfaa971Sbrad if (i2c_fetch_res.completion == MCP2221_FETCH_READ_PARTIALDATA || 864*3bfaa971Sbrad i2c_fetch_res.fetchlen == MCP2221_FETCH_READERROR) { 865*3bfaa971Sbrad DPRINTF(("umcpmio_i2c_read: fetch loop: partial data or read error: completion=%02x,fetchlen=%02x\n",i2c_fetch_res.completion,i2c_fetch_res.fetchlen)); 866*3bfaa971Sbrad WAITMS(sc->sc_busy_delay); 867*3bfaa971Sbrad err = EAGAIN; 868*3bfaa971Sbrad continue; 869*3bfaa971Sbrad } 870*3bfaa971Sbrad if (i2c_fetch_res.internal_i2c_state == MCP2221_ENGINE_ADDRNACK) { 871*3bfaa971Sbrad DPRINTF(("umcpmio_i2c_read: fetch loop: engine NACK\n")); 872*3bfaa971Sbrad err = EIO; 873*3bfaa971Sbrad break; 874*3bfaa971Sbrad } 875*3bfaa971Sbrad if (i2c_fetch_res.internal_i2c_state == 0 && 876*3bfaa971Sbrad i2c_fetch_res.fetchlen == 0) { 877*3bfaa971Sbrad DPRINTF(("umcpmio_i2c_read: fetch loop: internal state and fetch len are ZERO\n")); 878*3bfaa971Sbrad err = 0; 879*3bfaa971Sbrad break; 880*3bfaa971Sbrad } 881*3bfaa971Sbrad if (i2c_fetch_res.internal_i2c_state == MCP2221_ENGINE_READPARTIAL || 882*3bfaa971Sbrad i2c_fetch_res.internal_i2c_state == MCP2221_ENGINE_READCOMPLETE) { 883*3bfaa971Sbrad DPRINTF(("umcpmio_i2c_read: fetch loop: read partial or read complete: internal_i2c_state=%02x\n", i2c_fetch_res.internal_i2c_state)); 884*3bfaa971Sbrad err = 0; 885*3bfaa971Sbrad break; 886*3bfaa971Sbrad } 887*3bfaa971Sbrad } else { 888*3bfaa971Sbrad device_printf(sc->sc_dev, "umcpmio_i2c_read: fetch2: not the command desired: %02x\n", 889*3bfaa971Sbrad i2c_fetch_res.cmd); 890*3bfaa971Sbrad err = EIO; 891*3bfaa971Sbrad break; 892*3bfaa971Sbrad } 893*3bfaa971Sbrad } 894*3bfaa971Sbrad if (err == EAGAIN) 895*3bfaa971Sbrad err = ETIMEDOUT; 896*3bfaa971Sbrad 897*3bfaa971Sbrad if (! err) { 898*3bfaa971Sbrad if (databuf != NULL && 899*3bfaa971Sbrad i2c_fetch_res.fetchlen != MCP2221_FETCH_READERROR) { 900*3bfaa971Sbrad int size = uimin(i2c_fetch_res.fetchlen, datalen); 901*3bfaa971Sbrad DPRINTF(("umcpmio_i2c_read: copy data: size=%d,fetchlen=%d\n",size, i2c_fetch_res.fetchlen)); 902*3bfaa971Sbrad if (size > 0) 903*3bfaa971Sbrad memcpy(databuf, &i2c_fetch_res.data[0], size); 904*3bfaa971Sbrad } else { 905*3bfaa971Sbrad DPRINTF(("umcpmio_i2c_read: copy data: databuf is NULL\n")); 906*3bfaa971Sbrad } 907*3bfaa971Sbrad } 908*3bfaa971Sbrad } else { 909*3bfaa971Sbrad device_printf(sc->sc_dev, "umcpmio_i2c_read request error: cmd=%02x,err=%d\n", cmd, err); 910*3bfaa971Sbrad err = EIO; 911*3bfaa971Sbrad } 912*3bfaa971Sbrad out: 913*3bfaa971Sbrad 914*3bfaa971Sbrad return(err); 915*3bfaa971Sbrad } 916*3bfaa971Sbrad 917*3bfaa971Sbrad static int 918*3bfaa971Sbrad umcpmio_i2c_exec(void *v, i2c_op_t op, i2c_addr_t addr, const void *cmdbuf, 919*3bfaa971Sbrad size_t cmdlen, void *databuf, size_t datalen, int flags) 920*3bfaa971Sbrad { 921*3bfaa971Sbrad struct umcpmio_softc *sc = v; 922*3bfaa971Sbrad size_t totallen = 0; 923*3bfaa971Sbrad int err = 0; 924*3bfaa971Sbrad 925*3bfaa971Sbrad if (addr > 0x7f) 926*3bfaa971Sbrad return (ENOTSUP); 927*3bfaa971Sbrad 928*3bfaa971Sbrad if (cmdbuf != NULL) 929*3bfaa971Sbrad totallen += cmdlen; 930*3bfaa971Sbrad if (databuf != NULL) 931*3bfaa971Sbrad totallen += datalen; 932*3bfaa971Sbrad 933*3bfaa971Sbrad /* There is a way to do a transfer that is larger than 60 bytes, 934*3bfaa971Sbrad * but it requires that your break the transfer up into pieces and 935*3bfaa971Sbrad * send them in 60 byte chunks. We just won't support that right now. 936*3bfaa971Sbrad * It would be somewhat unusual for there to be a transfer that big, 937*3bfaa971Sbrad * unless you are trying to do block transfers and that isn't natively 938*3bfaa971Sbrad * supported by the chip anyway... so those have to be broken up and 939*3bfaa971Sbrad * sent as bytes. 940*3bfaa971Sbrad */ 941*3bfaa971Sbrad 942*3bfaa971Sbrad if (totallen > 60) 943*3bfaa971Sbrad return (ENOTSUP); 944*3bfaa971Sbrad 945*3bfaa971Sbrad if (I2C_OP_WRITE_P(op)) { 946*3bfaa971Sbrad err = umcpmio_i2c_write(sc, op, addr, cmdbuf, cmdlen, databuf, datalen, flags); 947*3bfaa971Sbrad 948*3bfaa971Sbrad DPRINTF(("umcpmio_exec: I2C WRITE: err=%d\n", err)); 949*3bfaa971Sbrad } else { 950*3bfaa971Sbrad err = umcpmio_i2c_read(sc, op, addr, cmdbuf, cmdlen, databuf, datalen, flags); 951*3bfaa971Sbrad 952*3bfaa971Sbrad DPRINTF(("umcpmio_exec: I2C READ: err=%d\n", err)); 953*3bfaa971Sbrad } 954*3bfaa971Sbrad 955*3bfaa971Sbrad return(err); 956*3bfaa971Sbrad } 957*3bfaa971Sbrad 958*3bfaa971Sbrad /* Accessing the ADC and DAC part of the chip */ 959*3bfaa971Sbrad 960*3bfaa971Sbrad #define UMCPMIO_DEV_UNIT(m) (m & 0x80 ? (m & 0x7f) / 3 : m) 961*3bfaa971Sbrad #define UMCPMIO_DEV_WHAT(m) (m & 0x80 ? ((m & 0x7f) % 3) + 1: CONTROL_DEV) 962*3bfaa971Sbrad 963*3bfaa971Sbrad static int 964*3bfaa971Sbrad umcpmio_dev_open(dev_t dev, int flags, int fmt, struct lwp *l) 965*3bfaa971Sbrad { 966*3bfaa971Sbrad struct umcpmio_softc *sc; 967*3bfaa971Sbrad int dunit; 968*3bfaa971Sbrad int pin = -1; 969*3bfaa971Sbrad int error = 0; 970*3bfaa971Sbrad 971*3bfaa971Sbrad sc = device_lookup_private(&umcpmio_cd, UMCPMIO_DEV_UNIT(minor(dev))); 972*3bfaa971Sbrad if (!sc) 973*3bfaa971Sbrad return ENXIO; 974*3bfaa971Sbrad 975*3bfaa971Sbrad dunit = UMCPMIO_DEV_WHAT(minor(dev)); 976*3bfaa971Sbrad 977*3bfaa971Sbrad if (sc->sc_dev_open[dunit]) { 978*3bfaa971Sbrad DPRINTF(("umcpmio_dev_open: dunit=%d BUSY\n",dunit)); 979*3bfaa971Sbrad return EBUSY; 980*3bfaa971Sbrad } 981*3bfaa971Sbrad 982*3bfaa971Sbrad /* The control device only allows for ioctl calls, so pretty much allow 983*3bfaa971Sbrad * any sort of access. For the ADC, you perform a strict O_RDONLY and 984*3bfaa971Sbrad * for the DAC a strict O_WRONLY. It is an error to try and do a O_RDWR 985*3bfaa971Sbrad * It makes little sense to try and support select or poll. The ADC and 986*3bfaa971Sbrad * DAC are always available for use. 987*3bfaa971Sbrad */ 988*3bfaa971Sbrad 989*3bfaa971Sbrad if (dunit != CONTROL_DEV && 990*3bfaa971Sbrad ((flags & FREAD) && (flags & FWRITE))) { 991*3bfaa971Sbrad DPRINTF(("umcpmio_dev_open: Not CONTROL device and trying to do READ and WRITE\n")); 992*3bfaa971Sbrad return EINVAL; 993*3bfaa971Sbrad } 994*3bfaa971Sbrad 995*3bfaa971Sbrad /* Ya, this unrolling will also have to be changed if the MCP-2210 is 996*3bfaa971Sbrad * supported. There are currently only 4 pins, so don't worry too much 997*3bfaa971Sbrad * about it. The MCP-2210 has RAM, so there would be a fifth for it. 998*3bfaa971Sbrad */ 999*3bfaa971Sbrad 1000*3bfaa971Sbrad mutex_enter(&sc->sc_action_mutex); 1001*3bfaa971Sbrad if (dunit != CONTROL_DEV) { 1002*3bfaa971Sbrad switch (dunit) { 1003*3bfaa971Sbrad case GP1_DEV: 1004*3bfaa971Sbrad pin = 1; 1005*3bfaa971Sbrad break; 1006*3bfaa971Sbrad case GP2_DEV: 1007*3bfaa971Sbrad pin = 2; 1008*3bfaa971Sbrad break; 1009*3bfaa971Sbrad case GP3_DEV: 1010*3bfaa971Sbrad pin = 3; 1011*3bfaa971Sbrad break; 1012*3bfaa971Sbrad default: 1013*3bfaa971Sbrad error = EINVAL; 1014*3bfaa971Sbrad break; 1015*3bfaa971Sbrad } 1016*3bfaa971Sbrad if (! error) { 1017*3bfaa971Sbrad /* XXX - we can probably do better here... it doesn't 1018*3bfaa971Sbrad * remember what the pin was set to and probably should. 1019*3bfaa971Sbrad */ 1020*3bfaa971Sbrad if (flags & FREAD) { 1021*3bfaa971Sbrad error = umcpmio_gpio_pin_ctlctl(sc, pin, GPIO_PIN_ALT0, false); 1022*3bfaa971Sbrad } else { 1023*3bfaa971Sbrad if (pin == 1) { 1024*3bfaa971Sbrad error = EINVAL; 1025*3bfaa971Sbrad } else { 1026*3bfaa971Sbrad error = umcpmio_gpio_pin_ctlctl(sc, pin, GPIO_PIN_ALT1, false); 1027*3bfaa971Sbrad } 1028*3bfaa971Sbrad } 1029*3bfaa971Sbrad } 1030*3bfaa971Sbrad } 1031*3bfaa971Sbrad if (! error) 1032*3bfaa971Sbrad sc->sc_dev_open[dunit] = true; 1033*3bfaa971Sbrad mutex_exit(&sc->sc_action_mutex); 1034*3bfaa971Sbrad 1035*3bfaa971Sbrad DPRINTF(("umcpmio_dev_open: Opened dunit=%d,pin=%d,error=%d\n",dunit,pin,error)); 1036*3bfaa971Sbrad 1037*3bfaa971Sbrad return error; 1038*3bfaa971Sbrad } 1039*3bfaa971Sbrad 1040*3bfaa971Sbrad /* Read an ADC value */ 1041*3bfaa971Sbrad 1042*3bfaa971Sbrad static int 1043*3bfaa971Sbrad umcpmio_dev_read(dev_t dev, struct uio *uio, int flags) 1044*3bfaa971Sbrad { 1045*3bfaa971Sbrad struct umcpmio_softc *sc; 1046*3bfaa971Sbrad struct mcp2221_status_res status_res; 1047*3bfaa971Sbrad int dunit; 1048*3bfaa971Sbrad int error = 0; 1049*3bfaa971Sbrad uint8_t adc_lsb; 1050*3bfaa971Sbrad uint8_t adc_msb; 1051*3bfaa971Sbrad uint16_t buf; 1052*3bfaa971Sbrad 1053*3bfaa971Sbrad if ((sc = device_lookup_private(&umcpmio_cd, UMCPMIO_DEV_UNIT(minor(dev)))) == NULL) 1054*3bfaa971Sbrad return ENXIO; 1055*3bfaa971Sbrad 1056*3bfaa971Sbrad dunit = UMCPMIO_DEV_WHAT(minor(dev)); 1057*3bfaa971Sbrad 1058*3bfaa971Sbrad if (dunit != CONTROL_DEV) { 1059*3bfaa971Sbrad while (uio->uio_resid && 1060*3bfaa971Sbrad !sc->sc_dying) { 1061*3bfaa971Sbrad error = umcpmio_get_status(sc, &status_res, true); 1062*3bfaa971Sbrad if (! error) { 1063*3bfaa971Sbrad switch (dunit) { 1064*3bfaa971Sbrad case GP1_DEV: 1065*3bfaa971Sbrad adc_lsb = status_res.adc_channel0_lsb; 1066*3bfaa971Sbrad adc_msb = status_res.adc_channel0_msb; 1067*3bfaa971Sbrad break; 1068*3bfaa971Sbrad case GP2_DEV: 1069*3bfaa971Sbrad adc_lsb = status_res.adc_channel1_lsb; 1070*3bfaa971Sbrad adc_msb = status_res.adc_channel1_msb; 1071*3bfaa971Sbrad break; 1072*3bfaa971Sbrad case GP3_DEV: 1073*3bfaa971Sbrad adc_lsb = status_res.adc_channel2_lsb; 1074*3bfaa971Sbrad adc_msb = status_res.adc_channel2_msb; 1075*3bfaa971Sbrad break; 1076*3bfaa971Sbrad default: 1077*3bfaa971Sbrad error = EINVAL; 1078*3bfaa971Sbrad break; 1079*3bfaa971Sbrad } 1080*3bfaa971Sbrad 1081*3bfaa971Sbrad if (! error) { 1082*3bfaa971Sbrad if (sc->sc_dying) 1083*3bfaa971Sbrad break; 1084*3bfaa971Sbrad 1085*3bfaa971Sbrad buf = adc_msb << 8; 1086*3bfaa971Sbrad buf |= adc_lsb; 1087*3bfaa971Sbrad error = uiomove(&buf, 2, uio); 1088*3bfaa971Sbrad } 1089*3bfaa971Sbrad } 1090*3bfaa971Sbrad } 1091*3bfaa971Sbrad } else { 1092*3bfaa971Sbrad error = EINVAL; 1093*3bfaa971Sbrad } 1094*3bfaa971Sbrad 1095*3bfaa971Sbrad 1096*3bfaa971Sbrad return error; 1097*3bfaa971Sbrad } 1098*3bfaa971Sbrad 1099*3bfaa971Sbrad /* Write to the DAC */ 1100*3bfaa971Sbrad 1101*3bfaa971Sbrad static int 1102*3bfaa971Sbrad umcpmio_dev_write(dev_t dev, struct uio *uio, int flags) 1103*3bfaa971Sbrad { 1104*3bfaa971Sbrad struct umcpmio_softc *sc; 1105*3bfaa971Sbrad int dunit; 1106*3bfaa971Sbrad int error = 0; 1107*3bfaa971Sbrad 1108*3bfaa971Sbrad if ((sc = device_lookup_private(&umcpmio_cd, UMCPMIO_DEV_UNIT(minor(dev)))) == NULL) 1109*3bfaa971Sbrad return ENXIO; 1110*3bfaa971Sbrad 1111*3bfaa971Sbrad dunit = UMCPMIO_DEV_WHAT(minor(dev)); 1112*3bfaa971Sbrad 1113*3bfaa971Sbrad if (dunit != CONTROL_DEV) { 1114*3bfaa971Sbrad while (uio->uio_resid && 1115*3bfaa971Sbrad !sc->sc_dying) { 1116*3bfaa971Sbrad uint8_t buf; 1117*3bfaa971Sbrad 1118*3bfaa971Sbrad if ((error = uiomove(&buf, 1, uio)) != 0) 1119*3bfaa971Sbrad break; 1120*3bfaa971Sbrad 1121*3bfaa971Sbrad if (sc->sc_dying) 1122*3bfaa971Sbrad break; 1123*3bfaa971Sbrad 1124*3bfaa971Sbrad error = umcpmio_set_dac_value_one(sc, buf, true); 1125*3bfaa971Sbrad if (error) 1126*3bfaa971Sbrad break; 1127*3bfaa971Sbrad } 1128*3bfaa971Sbrad } else { 1129*3bfaa971Sbrad error = EINVAL; 1130*3bfaa971Sbrad } 1131*3bfaa971Sbrad 1132*3bfaa971Sbrad return error; 1133*3bfaa971Sbrad } 1134*3bfaa971Sbrad 1135*3bfaa971Sbrad /* Close everything up */ 1136*3bfaa971Sbrad 1137*3bfaa971Sbrad static int 1138*3bfaa971Sbrad umcpmio_dev_close(dev_t dev, int flags, int fmt, struct lwp *l) 1139*3bfaa971Sbrad { 1140*3bfaa971Sbrad struct umcpmio_softc *sc; 1141*3bfaa971Sbrad int dunit; 1142*3bfaa971Sbrad int pin; 1143*3bfaa971Sbrad int error = 0; 1144*3bfaa971Sbrad 1145*3bfaa971Sbrad sc = device_lookup_private(&umcpmio_cd, UMCPMIO_DEV_UNIT(minor(dev))); 1146*3bfaa971Sbrad if (sc->sc_dying) 1147*3bfaa971Sbrad return EIO; 1148*3bfaa971Sbrad 1149*3bfaa971Sbrad dunit = UMCPMIO_DEV_WHAT(minor(dev)); 1150*3bfaa971Sbrad 1151*3bfaa971Sbrad mutex_enter(&sc->sc_action_mutex); 1152*3bfaa971Sbrad if (dunit != CONTROL_DEV) { 1153*3bfaa971Sbrad switch (dunit) { 1154*3bfaa971Sbrad case GP1_DEV: 1155*3bfaa971Sbrad pin = 1; 1156*3bfaa971Sbrad break; 1157*3bfaa971Sbrad case GP2_DEV: 1158*3bfaa971Sbrad pin = 2; 1159*3bfaa971Sbrad break; 1160*3bfaa971Sbrad case GP3_DEV: 1161*3bfaa971Sbrad pin = 3; 1162*3bfaa971Sbrad break; 1163*3bfaa971Sbrad default: 1164*3bfaa971Sbrad error = EINVAL; 1165*3bfaa971Sbrad break; 1166*3bfaa971Sbrad } 1167*3bfaa971Sbrad if (! error) { 1168*3bfaa971Sbrad /* XXX - Ya, this really could be done better. Probably should 1169*3bfaa971Sbrad * read the sram config and maybe the gpio config and save out 1170*3bfaa971Sbrad * what the pin was set to. 1171*3bfaa971Sbrad */ 1172*3bfaa971Sbrad 1173*3bfaa971Sbrad error = umcpmio_gpio_pin_ctlctl(sc, pin, GPIO_PIN_INPUT, false); 1174*3bfaa971Sbrad } 1175*3bfaa971Sbrad } 1176*3bfaa971Sbrad sc->sc_dev_open[dunit] = false; 1177*3bfaa971Sbrad mutex_exit(&sc->sc_action_mutex); 1178*3bfaa971Sbrad 1179*3bfaa971Sbrad return error; 1180*3bfaa971Sbrad } 1181*3bfaa971Sbrad 1182*3bfaa971Sbrad static int 1183*3bfaa971Sbrad umcpmio_dev_ioctl(dev_t dev, u_long cmd, void *data, int flag, struct lwp *l) 1184*3bfaa971Sbrad { 1185*3bfaa971Sbrad struct umcpmio_softc *sc; 1186*3bfaa971Sbrad struct mcp2221_status_res get_status_res; 1187*3bfaa971Sbrad struct mcp2221_get_sram_res get_sram_res; 1188*3bfaa971Sbrad struct mcp2221_get_gpio_cfg_res get_gpio_cfg_res; 1189*3bfaa971Sbrad struct mcp2221_get_flash_res get_flash_res; 1190*3bfaa971Sbrad struct mcp2221_status_res *ioctl_get_status; 1191*3bfaa971Sbrad struct mcp2221_get_sram_res *ioctl_get_sram; 1192*3bfaa971Sbrad struct mcp2221_get_gpio_cfg_res *ioctl_get_gpio_cfg; 1193*3bfaa971Sbrad struct umcpmio_ioctl_get_flash *ioctl_get_flash; 1194*3bfaa971Sbrad struct umcpmio_ioctl_put_flash *ioctl_put_flash; 1195*3bfaa971Sbrad struct mcp2221_put_flash_req put_flash_req; 1196*3bfaa971Sbrad struct mcp2221_put_flash_res put_flash_res; 1197*3bfaa971Sbrad int dunit; 1198*3bfaa971Sbrad int error = 0; 1199*3bfaa971Sbrad 1200*3bfaa971Sbrad sc = device_lookup_private(&umcpmio_cd, UMCPMIO_DEV_UNIT(minor(dev))); 1201*3bfaa971Sbrad if (sc->sc_dying) 1202*3bfaa971Sbrad return EIO; 1203*3bfaa971Sbrad 1204*3bfaa971Sbrad dunit = UMCPMIO_DEV_WHAT(minor(dev)); 1205*3bfaa971Sbrad 1206*3bfaa971Sbrad if (dunit != CONTROL_DEV) { 1207*3bfaa971Sbrad /* It actually is fine to call ioctl with a unsupported cmd, 1208*3bfaa971Sbrad * but be a little noisy if debug is enabled. 1209*3bfaa971Sbrad */ 1210*3bfaa971Sbrad DPRINTF(("umcpmio_dev_ioctl: dunit is not the CONTROL device: dunit=%d,cmd=%ld\n",dunit,cmd)); 1211*3bfaa971Sbrad return EINVAL; 1212*3bfaa971Sbrad } 1213*3bfaa971Sbrad 1214*3bfaa971Sbrad mutex_enter(&sc->sc_action_mutex); 1215*3bfaa971Sbrad 1216*3bfaa971Sbrad switch (cmd) { 1217*3bfaa971Sbrad /* The GET calls use a shadow buffer for each type of call. That 1218*3bfaa971Sbrad * probably isn't actually needed and the memcpy could be avoided. 1219*3bfaa971Sbrad * but... it is only ever 64 bytes, so maybe not a big deal. 1220*3bfaa971Sbrad */ 1221*3bfaa971Sbrad case UMCPMIO_GET_STATUS: 1222*3bfaa971Sbrad ioctl_get_status = (struct mcp2221_status_res *)data; 1223*3bfaa971Sbrad error = umcpmio_get_status(sc, &get_status_res, false); 1224*3bfaa971Sbrad umcpmio_dump_buffer(sc->sc_dumpbuffer, (uint8_t *)&get_status_res, MCP2221_RES_BUFFER_SIZE, "umcpmio_dev_ioctl: UMCPMIO_GET_STATUS: get_status_res"); 1225*3bfaa971Sbrad DPRINTF(("umcpmio_dev_ioctl: UMCPMIO_GET_STATUS: umcpmio_get_status error=%d\n",error)); 1226*3bfaa971Sbrad if (! error) 1227*3bfaa971Sbrad memcpy(ioctl_get_status, &get_status_res, MCP2221_RES_BUFFER_SIZE); 1228*3bfaa971Sbrad break; 1229*3bfaa971Sbrad 1230*3bfaa971Sbrad case UMCPMIO_GET_SRAM: 1231*3bfaa971Sbrad ioctl_get_sram = (struct mcp2221_get_sram_res *)data; 1232*3bfaa971Sbrad error = umcpmio_get_sram(sc, &get_sram_res, false); 1233*3bfaa971Sbrad umcpmio_dump_buffer(sc->sc_dumpbuffer, (uint8_t *)&get_sram_res, MCP2221_RES_BUFFER_SIZE, "umcpmio_dev_ioctl: UMCPMIO_GET_SRAM: get_sram_res"); 1234*3bfaa971Sbrad DPRINTF(("umcpmio_dev_ioctl: UMCPMIO_GET_SRAM: umcpmio_get_sram error=%d\n",error)); 1235*3bfaa971Sbrad if (! error) 1236*3bfaa971Sbrad memcpy(ioctl_get_sram, &get_sram_res, MCP2221_RES_BUFFER_SIZE); 1237*3bfaa971Sbrad break; 1238*3bfaa971Sbrad 1239*3bfaa971Sbrad case UMCPMIO_GET_GP_CFG: 1240*3bfaa971Sbrad ioctl_get_gpio_cfg = (struct mcp2221_get_gpio_cfg_res *)data; 1241*3bfaa971Sbrad error = umcpmio_get_gpio_cfg(sc, &get_gpio_cfg_res, false); 1242*3bfaa971Sbrad umcpmio_dump_buffer(sc->sc_dumpbuffer, (uint8_t *)&get_gpio_cfg_res, MCP2221_RES_BUFFER_SIZE, "umcpmio_dev_ioctl: UMCPMIO_GET_GP_CFG: get_gpio_cfg_res"); 1243*3bfaa971Sbrad DPRINTF(("umcpmio_dev_ioctl: UMCPMIO_GET_GP_CFG: umcpmio_get_gpio_cfg error=%d\n",error)); 1244*3bfaa971Sbrad if (! error) 1245*3bfaa971Sbrad memcpy(ioctl_get_gpio_cfg, &get_gpio_cfg_res, MCP2221_RES_BUFFER_SIZE); 1246*3bfaa971Sbrad break; 1247*3bfaa971Sbrad 1248*3bfaa971Sbrad case UMCPMIO_GET_FLASH: 1249*3bfaa971Sbrad ioctl_get_flash = (struct umcpmio_ioctl_get_flash *)data; 1250*3bfaa971Sbrad error = umcpmio_get_flash(sc, ioctl_get_flash->subcode, &get_flash_res, false); 1251*3bfaa971Sbrad umcpmio_dump_buffer(sc->sc_dumpbuffer, (uint8_t *)&get_flash_res, MCP2221_RES_BUFFER_SIZE, "umcpmio_dev_ioctl: UMCPMIO_GET_FLASH: get_flash_res"); 1252*3bfaa971Sbrad DPRINTF(("umcpmio_dev_ioctl: UMCPMIO_GET_FLASH: umcpmio_get_flash subcode=%d,error=%d\n",ioctl_get_flash->subcode,error)); 1253*3bfaa971Sbrad if (! error) 1254*3bfaa971Sbrad memcpy(&ioctl_get_flash->get_flash_res, &get_flash_res, MCP2221_RES_BUFFER_SIZE); 1255*3bfaa971Sbrad break; 1256*3bfaa971Sbrad 1257*3bfaa971Sbrad case UMCPMIO_PUT_FLASH: 1258*3bfaa971Sbrad /* We only allow the flash parts related to gpio to be changed. 1259*3bfaa971Sbrad * Bounce any attempt to do something else. Also use a shadow 1260*3bfaa971Sbrad * buffer for the put, so we get to control just literally 1261*3bfaa971Sbrad * everything about the write to flash. 1262*3bfaa971Sbrad */ 1263*3bfaa971Sbrad ioctl_put_flash = (struct umcpmio_ioctl_put_flash *)data; 1264*3bfaa971Sbrad DPRINTF(("umcpmio_dev_ioctl: UMCPMIO_PUT_FLASH: umcpmio_put_flash subcode=%d\n",ioctl_put_flash->subcode)); 1265*3bfaa971Sbrad if (ioctl_put_flash->subcode == MCP2221_FLASH_SUBCODE_GP) { 1266*3bfaa971Sbrad memset(&put_flash_req, 0, MCP2221_REQ_BUFFER_SIZE); 1267*3bfaa971Sbrad put_flash_req.subcode = ioctl_put_flash->subcode; 1268*3bfaa971Sbrad put_flash_req.u.gp.gp0_settings = ioctl_put_flash->put_flash_req.u.gp.gp0_settings; 1269*3bfaa971Sbrad put_flash_req.u.gp.gp1_settings = ioctl_put_flash->put_flash_req.u.gp.gp1_settings; 1270*3bfaa971Sbrad put_flash_req.u.gp.gp2_settings = ioctl_put_flash->put_flash_req.u.gp.gp2_settings; 1271*3bfaa971Sbrad put_flash_req.u.gp.gp3_settings = ioctl_put_flash->put_flash_req.u.gp.gp3_settings; 1272*3bfaa971Sbrad umcpmio_dump_buffer(sc->sc_dumpbuffer, (uint8_t *)&ioctl_put_flash->put_flash_req, MCP2221_REQ_BUFFER_SIZE, "umcpmio_dev_ioctl: UMCPMIO_PUT_FLASH: ioctl put_flash_req"); 1273*3bfaa971Sbrad umcpmio_dump_buffer(sc->sc_dumpbuffer, (uint8_t *)&put_flash_req, MCP2221_REQ_BUFFER_SIZE, "umcpmio_dev_ioctl: UMCPMIO_PUT_FLASH: put_flash_req"); 1274*3bfaa971Sbrad memset(&put_flash_res, 0, MCP2221_RES_BUFFER_SIZE); 1275*3bfaa971Sbrad error = umcpmio_put_flash(sc, &put_flash_req, &put_flash_res, false); 1276*3bfaa971Sbrad umcpmio_dump_buffer(sc->sc_dumpbuffer, (uint8_t *)&put_flash_res, MCP2221_RES_BUFFER_SIZE, "umcpmio_dev_ioctl: UMCPMIO_PUT_FLASH: put_flash_res"); 1277*3bfaa971Sbrad memcpy(&ioctl_put_flash->put_flash_res, &put_flash_res, MCP2221_RES_BUFFER_SIZE); 1278*3bfaa971Sbrad } else { 1279*3bfaa971Sbrad error = EINVAL; 1280*3bfaa971Sbrad } 1281*3bfaa971Sbrad break; 1282*3bfaa971Sbrad default: 1283*3bfaa971Sbrad error = EINVAL; 1284*3bfaa971Sbrad } 1285*3bfaa971Sbrad 1286*3bfaa971Sbrad mutex_exit(&sc->sc_action_mutex); 1287*3bfaa971Sbrad 1288*3bfaa971Sbrad return error; 1289*3bfaa971Sbrad } 1290*3bfaa971Sbrad 1291*3bfaa971Sbrad /* This is for sysctl variables that don't actually change the chip. */ 1292*3bfaa971Sbrad 1293*3bfaa971Sbrad int 1294*3bfaa971Sbrad umcpmio_verify_sysctl(SYSCTLFN_ARGS) 1295*3bfaa971Sbrad { 1296*3bfaa971Sbrad int error, t; 1297*3bfaa971Sbrad struct sysctlnode node; 1298*3bfaa971Sbrad 1299*3bfaa971Sbrad node = *rnode; 1300*3bfaa971Sbrad t = *(int *)rnode->sysctl_data; 1301*3bfaa971Sbrad node.sysctl_data = &t; 1302*3bfaa971Sbrad error = sysctl_lookup(SYSCTLFN_CALL(&node)); 1303*3bfaa971Sbrad if (error || newp == NULL) 1304*3bfaa971Sbrad return error; 1305*3bfaa971Sbrad 1306*3bfaa971Sbrad if (t < 0) 1307*3bfaa971Sbrad return EINVAL; 1308*3bfaa971Sbrad 1309*3bfaa971Sbrad *(int *)rnode->sysctl_data = t; 1310*3bfaa971Sbrad 1311*3bfaa971Sbrad return 0; 1312*3bfaa971Sbrad } 1313*3bfaa971Sbrad 1314*3bfaa971Sbrad /* sysctl validation for stuff that interacts with the chip needs to happen in a 1315*3bfaa971Sbrad * transaction. The read of the current state and the update to new state can't 1316*3bfaa971Sbrad * allow for someone to sneak in between the two. 1317*3bfaa971Sbrad * 1318*3bfaa971Sbrad * We use text for the values of a lot of these variables so you don't need the 1319*3bfaa971Sbrad * datasheet in front of you. You get to do that with umcpmioctl(8). 1320*3bfaa971Sbrad */ 1321*3bfaa971Sbrad 1322*3bfaa971Sbrad static struct umcpmio_sysctl_name umcpmio_vref_names[] = { 1323*3bfaa971Sbrad { 1324*3bfaa971Sbrad .text = "4.096V", 1325*3bfaa971Sbrad }, 1326*3bfaa971Sbrad { 1327*3bfaa971Sbrad .text = "2.048V", 1328*3bfaa971Sbrad }, 1329*3bfaa971Sbrad { 1330*3bfaa971Sbrad .text = "1.024V", 1331*3bfaa971Sbrad }, 1332*3bfaa971Sbrad { 1333*3bfaa971Sbrad .text = "OFF", 1334*3bfaa971Sbrad }, 1335*3bfaa971Sbrad { 1336*3bfaa971Sbrad .text = "VDD", 1337*3bfaa971Sbrad } 1338*3bfaa971Sbrad }; 1339*3bfaa971Sbrad 1340*3bfaa971Sbrad int 1341*3bfaa971Sbrad umcpmio_verify_dac_sysctl(SYSCTLFN_ARGS) 1342*3bfaa971Sbrad { 1343*3bfaa971Sbrad char buf[UMCPMIO_VREF_NAME]; 1344*3bfaa971Sbrad char cbuf[UMCPMIO_VREF_NAME]; 1345*3bfaa971Sbrad struct umcpmio_softc *sc; 1346*3bfaa971Sbrad struct sysctlnode node; 1347*3bfaa971Sbrad int error = 0; 1348*3bfaa971Sbrad int vrm; 1349*3bfaa971Sbrad size_t i; 1350*3bfaa971Sbrad struct mcp2221_get_sram_res sram_res; 1351*3bfaa971Sbrad 1352*3bfaa971Sbrad node = *rnode; 1353*3bfaa971Sbrad sc = node.sysctl_data; 1354*3bfaa971Sbrad 1355*3bfaa971Sbrad mutex_enter(&sc->sc_action_mutex); 1356*3bfaa971Sbrad 1357*3bfaa971Sbrad error = umcpmio_get_sram(sc, &sram_res, false); 1358*3bfaa971Sbrad if (error) 1359*3bfaa971Sbrad goto out; 1360*3bfaa971Sbrad 1361*3bfaa971Sbrad umcpmio_dump_buffer(sc->sc_dumpbuffer, (uint8_t *)&sram_res, MCP2221_RES_BUFFER_SIZE, "umcpmio_verify_dac_sysctl SRAM res buffer"); 1362*3bfaa971Sbrad 1363*3bfaa971Sbrad if (sram_res.dac_reference_voltage & MCP2221_SRAM_DAC_IS_VRM) { 1364*3bfaa971Sbrad vrm = sram_res.dac_reference_voltage & MCP2221_SRAM_DAC_VRM_MASK; 1365*3bfaa971Sbrad switch (vrm) { 1366*3bfaa971Sbrad case MCP2221_SRAM_DAC_VRM_4096V: 1367*3bfaa971Sbrad strncpy(buf, "4.096V", UMCPMIO_VREF_NAME); 1368*3bfaa971Sbrad break; 1369*3bfaa971Sbrad case MCP2221_SRAM_DAC_VRM_2048V: 1370*3bfaa971Sbrad strncpy(buf, "2.048V", UMCPMIO_VREF_NAME); 1371*3bfaa971Sbrad break; 1372*3bfaa971Sbrad case MCP2221_SRAM_DAC_VRM_1024V: 1373*3bfaa971Sbrad strncpy(buf, "1.024V", UMCPMIO_VREF_NAME); 1374*3bfaa971Sbrad break; 1375*3bfaa971Sbrad case MCP2221_SRAM_DAC_VRM_OFF: 1376*3bfaa971Sbrad default: 1377*3bfaa971Sbrad strncpy(buf, "OFF", UMCPMIO_VREF_NAME); 1378*3bfaa971Sbrad break; 1379*3bfaa971Sbrad } 1380*3bfaa971Sbrad } else { 1381*3bfaa971Sbrad strncpy(buf, "VDD", UMCPMIO_VREF_NAME); 1382*3bfaa971Sbrad } 1383*3bfaa971Sbrad strncpy(cbuf, buf, UMCPMIO_VREF_NAME); 1384*3bfaa971Sbrad node.sysctl_data = buf; 1385*3bfaa971Sbrad error = sysctl_lookup(SYSCTLFN_CALL(&node)); 1386*3bfaa971Sbrad if (error || newp == NULL) 1387*3bfaa971Sbrad goto out; 1388*3bfaa971Sbrad 1389*3bfaa971Sbrad for (i = 0; i < __arraycount(umcpmio_vref_names); i++) { 1390*3bfaa971Sbrad if (strncmp(node.sysctl_data, umcpmio_vref_names[i].text, 1391*3bfaa971Sbrad UMCPMIO_VREF_NAME) == 0) { 1392*3bfaa971Sbrad break; 1393*3bfaa971Sbrad } 1394*3bfaa971Sbrad } 1395*3bfaa971Sbrad 1396*3bfaa971Sbrad if (i == __arraycount(umcpmio_vref_names)) 1397*3bfaa971Sbrad error = EINVAL; 1398*3bfaa971Sbrad 1399*3bfaa971Sbrad if (! error) { 1400*3bfaa971Sbrad if (strncmp(cbuf, buf, UMCPMIO_VREF_NAME) != 0) { 1401*3bfaa971Sbrad DPRINTF(("umcpmio_verify_dac_sysctl: setting DAC vref: %s\n",buf)); 1402*3bfaa971Sbrad error = umcpmio_set_dac_vref_one(sc, buf, false); 1403*3bfaa971Sbrad } 1404*3bfaa971Sbrad } 1405*3bfaa971Sbrad 1406*3bfaa971Sbrad out: 1407*3bfaa971Sbrad mutex_exit(&sc->sc_action_mutex); 1408*3bfaa971Sbrad return error; 1409*3bfaa971Sbrad } 1410*3bfaa971Sbrad 1411*3bfaa971Sbrad int 1412*3bfaa971Sbrad umcpmio_verify_adc_sysctl(SYSCTLFN_ARGS) 1413*3bfaa971Sbrad { 1414*3bfaa971Sbrad char buf[UMCPMIO_VREF_NAME]; 1415*3bfaa971Sbrad char cbuf[UMCPMIO_VREF_NAME]; 1416*3bfaa971Sbrad struct umcpmio_softc *sc; 1417*3bfaa971Sbrad struct sysctlnode node; 1418*3bfaa971Sbrad int error = 0; 1419*3bfaa971Sbrad int vrm; 1420*3bfaa971Sbrad size_t i; 1421*3bfaa971Sbrad struct mcp2221_get_sram_res sram_res; 1422*3bfaa971Sbrad 1423*3bfaa971Sbrad node = *rnode; 1424*3bfaa971Sbrad sc = node.sysctl_data; 1425*3bfaa971Sbrad 1426*3bfaa971Sbrad mutex_enter(&sc->sc_action_mutex); 1427*3bfaa971Sbrad 1428*3bfaa971Sbrad error = umcpmio_get_sram(sc, &sram_res, false); 1429*3bfaa971Sbrad if (error) 1430*3bfaa971Sbrad goto out; 1431*3bfaa971Sbrad 1432*3bfaa971Sbrad if (sram_res.irq_adc_reference_voltage & MCP2221_SRAM_ADC_IS_VRM) { 1433*3bfaa971Sbrad vrm = sram_res.irq_adc_reference_voltage & MCP2221_SRAM_ADC_VRM_MASK; 1434*3bfaa971Sbrad switch (vrm) { 1435*3bfaa971Sbrad case MCP2221_SRAM_ADC_VRM_4096V: 1436*3bfaa971Sbrad strncpy(buf, "4.096V", UMCPMIO_VREF_NAME); 1437*3bfaa971Sbrad break; 1438*3bfaa971Sbrad case MCP2221_SRAM_ADC_VRM_2048V: 1439*3bfaa971Sbrad strncpy(buf, "2.048V", UMCPMIO_VREF_NAME); 1440*3bfaa971Sbrad break; 1441*3bfaa971Sbrad case MCP2221_SRAM_ADC_VRM_1024V: 1442*3bfaa971Sbrad strncpy(buf, "1.024V", UMCPMIO_VREF_NAME); 1443*3bfaa971Sbrad break; 1444*3bfaa971Sbrad case MCP2221_SRAM_ADC_VRM_OFF: 1445*3bfaa971Sbrad default: 1446*3bfaa971Sbrad strncpy(buf, "OFF", UMCPMIO_VREF_NAME); 1447*3bfaa971Sbrad break; 1448*3bfaa971Sbrad } 1449*3bfaa971Sbrad } else { 1450*3bfaa971Sbrad strncpy(buf, "VDD", UMCPMIO_VREF_NAME); 1451*3bfaa971Sbrad } 1452*3bfaa971Sbrad strncpy(cbuf, buf, UMCPMIO_VREF_NAME); 1453*3bfaa971Sbrad node.sysctl_data = buf; 1454*3bfaa971Sbrad error = sysctl_lookup(SYSCTLFN_CALL(&node)); 1455*3bfaa971Sbrad if (error || newp == NULL) 1456*3bfaa971Sbrad goto out; 1457*3bfaa971Sbrad 1458*3bfaa971Sbrad for (i = 0; i < __arraycount(umcpmio_vref_names); i++) { 1459*3bfaa971Sbrad if (strncmp(node.sysctl_data, umcpmio_vref_names[i].text, 1460*3bfaa971Sbrad UMCPMIO_VREF_NAME) == 0) { 1461*3bfaa971Sbrad break; 1462*3bfaa971Sbrad } 1463*3bfaa971Sbrad } 1464*3bfaa971Sbrad 1465*3bfaa971Sbrad if (i == __arraycount(umcpmio_vref_names)) 1466*3bfaa971Sbrad error = EINVAL; 1467*3bfaa971Sbrad 1468*3bfaa971Sbrad if (! error) { 1469*3bfaa971Sbrad if (strncmp(cbuf, buf, UMCPMIO_VREF_NAME) != 0) { 1470*3bfaa971Sbrad DPRINTF(("umcpmio_verify_adc_sysctl: setting ADC vref: %s\n",buf)); 1471*3bfaa971Sbrad error = umcpmio_set_adc_vref_one(sc, buf, false); 1472*3bfaa971Sbrad } 1473*3bfaa971Sbrad } 1474*3bfaa971Sbrad 1475*3bfaa971Sbrad out: 1476*3bfaa971Sbrad mutex_exit(&sc->sc_action_mutex); 1477*3bfaa971Sbrad return error; 1478*3bfaa971Sbrad } 1479*3bfaa971Sbrad 1480*3bfaa971Sbrad static struct umcpmio_sysctl_name umcpmio_dc_names[] = { 1481*3bfaa971Sbrad { 1482*3bfaa971Sbrad .text = "75%", 1483*3bfaa971Sbrad }, 1484*3bfaa971Sbrad { 1485*3bfaa971Sbrad .text = "50%", 1486*3bfaa971Sbrad }, 1487*3bfaa971Sbrad { 1488*3bfaa971Sbrad .text = "25%", 1489*3bfaa971Sbrad }, 1490*3bfaa971Sbrad { 1491*3bfaa971Sbrad .text = "0%", 1492*3bfaa971Sbrad } 1493*3bfaa971Sbrad }; 1494*3bfaa971Sbrad 1495*3bfaa971Sbrad static int 1496*3bfaa971Sbrad umcpmio_verify_gpioclock_dc_sysctl(SYSCTLFN_ARGS) 1497*3bfaa971Sbrad { 1498*3bfaa971Sbrad char buf[UMCPMIO_VREF_NAME]; 1499*3bfaa971Sbrad char cbuf[UMCPMIO_VREF_NAME]; 1500*3bfaa971Sbrad struct umcpmio_softc *sc; 1501*3bfaa971Sbrad struct sysctlnode node; 1502*3bfaa971Sbrad int error = 0; 1503*3bfaa971Sbrad uint8_t duty_cycle; 1504*3bfaa971Sbrad size_t i; 1505*3bfaa971Sbrad struct mcp2221_get_sram_res sram_res; 1506*3bfaa971Sbrad 1507*3bfaa971Sbrad node = *rnode; 1508*3bfaa971Sbrad sc = node.sysctl_data; 1509*3bfaa971Sbrad 1510*3bfaa971Sbrad mutex_enter(&sc->sc_action_mutex); 1511*3bfaa971Sbrad 1512*3bfaa971Sbrad error = umcpmio_get_sram(sc, &sram_res, false); 1513*3bfaa971Sbrad if (error) 1514*3bfaa971Sbrad goto out; 1515*3bfaa971Sbrad 1516*3bfaa971Sbrad duty_cycle = sram_res.clock_divider & MCP2221_SRAM_GPIO_CLOCK_DC_MASK; 1517*3bfaa971Sbrad DPRINTF(("umcpmio_verify_gpioclock_dc_sysctl: current duty cycle: %02x\n",duty_cycle)); 1518*3bfaa971Sbrad switch (duty_cycle) { 1519*3bfaa971Sbrad case MCP2221_SRAM_GPIO_CLOCK_DC_75: 1520*3bfaa971Sbrad strncpy(buf, "75%", UMCPMIO_DC_NAME); 1521*3bfaa971Sbrad break; 1522*3bfaa971Sbrad case MCP2221_SRAM_GPIO_CLOCK_DC_50: 1523*3bfaa971Sbrad strncpy(buf, "50%", UMCPMIO_DC_NAME); 1524*3bfaa971Sbrad break; 1525*3bfaa971Sbrad case MCP2221_SRAM_GPIO_CLOCK_DC_25: 1526*3bfaa971Sbrad strncpy(buf, "25%", UMCPMIO_DC_NAME); 1527*3bfaa971Sbrad break; 1528*3bfaa971Sbrad case MCP2221_SRAM_GPIO_CLOCK_DC_0: 1529*3bfaa971Sbrad default: 1530*3bfaa971Sbrad strncpy(buf, "0%", UMCPMIO_DC_NAME); 1531*3bfaa971Sbrad break; 1532*3bfaa971Sbrad } 1533*3bfaa971Sbrad strncpy(cbuf, buf, UMCPMIO_VREF_NAME); 1534*3bfaa971Sbrad node.sysctl_data = buf; 1535*3bfaa971Sbrad error = sysctl_lookup(SYSCTLFN_CALL(&node)); 1536*3bfaa971Sbrad if (error || newp == NULL) 1537*3bfaa971Sbrad goto out; 1538*3bfaa971Sbrad 1539*3bfaa971Sbrad for (i = 0; i < __arraycount(umcpmio_dc_names); i++) { 1540*3bfaa971Sbrad if (strncmp(node.sysctl_data, umcpmio_dc_names[i].text, 1541*3bfaa971Sbrad UMCPMIO_VREF_NAME) == 0) { 1542*3bfaa971Sbrad break; 1543*3bfaa971Sbrad } 1544*3bfaa971Sbrad } 1545*3bfaa971Sbrad 1546*3bfaa971Sbrad if (i == __arraycount(umcpmio_dc_names)) 1547*3bfaa971Sbrad error = EINVAL; 1548*3bfaa971Sbrad 1549*3bfaa971Sbrad if (! error) { 1550*3bfaa971Sbrad if (strncmp(cbuf, buf, UMCPMIO_VREF_NAME) != 0) { 1551*3bfaa971Sbrad DPRINTF(("umcpmio_verify_gpioclock_dc_sysctl: setting GPIO clock duty cycle: %s\n",buf)); 1552*3bfaa971Sbrad error = umcpmio_set_gpioclock_dc_one(sc, buf, false); 1553*3bfaa971Sbrad } 1554*3bfaa971Sbrad } 1555*3bfaa971Sbrad 1556*3bfaa971Sbrad out: 1557*3bfaa971Sbrad mutex_exit(&sc->sc_action_mutex); 1558*3bfaa971Sbrad return error; 1559*3bfaa971Sbrad } 1560*3bfaa971Sbrad 1561*3bfaa971Sbrad 1562*3bfaa971Sbrad static struct umcpmio_sysctl_name umcpmio_cd_names[] = { 1563*3bfaa971Sbrad { 1564*3bfaa971Sbrad .text = "375kHz", 1565*3bfaa971Sbrad }, 1566*3bfaa971Sbrad { 1567*3bfaa971Sbrad .text = "750kHz", 1568*3bfaa971Sbrad }, 1569*3bfaa971Sbrad { 1570*3bfaa971Sbrad .text = "1.5MHz", 1571*3bfaa971Sbrad }, 1572*3bfaa971Sbrad { 1573*3bfaa971Sbrad .text = "3MHz", 1574*3bfaa971Sbrad }, 1575*3bfaa971Sbrad { 1576*3bfaa971Sbrad .text = "6MHz", 1577*3bfaa971Sbrad }, 1578*3bfaa971Sbrad { 1579*3bfaa971Sbrad .text = "12MHz", 1580*3bfaa971Sbrad }, 1581*3bfaa971Sbrad { 1582*3bfaa971Sbrad .text = "24MHz", 1583*3bfaa971Sbrad } 1584*3bfaa971Sbrad }; 1585*3bfaa971Sbrad 1586*3bfaa971Sbrad static int 1587*3bfaa971Sbrad umcpmio_verify_gpioclock_cd_sysctl(SYSCTLFN_ARGS) 1588*3bfaa971Sbrad { 1589*3bfaa971Sbrad char buf[UMCPMIO_CD_NAME]; 1590*3bfaa971Sbrad char cbuf[UMCPMIO_CD_NAME]; 1591*3bfaa971Sbrad struct umcpmio_softc *sc; 1592*3bfaa971Sbrad struct sysctlnode node; 1593*3bfaa971Sbrad int error = 0; 1594*3bfaa971Sbrad uint8_t clock_divider; 1595*3bfaa971Sbrad size_t i; 1596*3bfaa971Sbrad struct mcp2221_get_sram_res sram_res; 1597*3bfaa971Sbrad 1598*3bfaa971Sbrad node = *rnode; 1599*3bfaa971Sbrad sc = node.sysctl_data; 1600*3bfaa971Sbrad 1601*3bfaa971Sbrad mutex_enter(&sc->sc_action_mutex); 1602*3bfaa971Sbrad 1603*3bfaa971Sbrad error = umcpmio_get_sram(sc, &sram_res, false); 1604*3bfaa971Sbrad if (error) 1605*3bfaa971Sbrad goto out; 1606*3bfaa971Sbrad 1607*3bfaa971Sbrad clock_divider = sram_res.clock_divider & MCP2221_SRAM_GPIO_CLOCK_CD_MASK; 1608*3bfaa971Sbrad DPRINTF(("umcpmio_verify_gpioclock_cd_sysctl: current clock divider: %02x\n",clock_divider)); 1609*3bfaa971Sbrad switch (clock_divider) { 1610*3bfaa971Sbrad 1611*3bfaa971Sbrad case MCP2221_SRAM_GPIO_CLOCK_CD_375KHZ: 1612*3bfaa971Sbrad strncpy(buf, "375kHz", UMCPMIO_CD_NAME); 1613*3bfaa971Sbrad break; 1614*3bfaa971Sbrad case MCP2221_SRAM_GPIO_CLOCK_CD_750KHZ: 1615*3bfaa971Sbrad strncpy(buf, "750kHz", UMCPMIO_CD_NAME); 1616*3bfaa971Sbrad break; 1617*3bfaa971Sbrad case MCP2221_SRAM_GPIO_CLOCK_CD_1P5MHZ: 1618*3bfaa971Sbrad strncpy(buf, "1.5MHz", UMCPMIO_CD_NAME); 1619*3bfaa971Sbrad break; 1620*3bfaa971Sbrad case MCP2221_SRAM_GPIO_CLOCK_CD_3MHZ: 1621*3bfaa971Sbrad strncpy(buf, "3MHz", UMCPMIO_CD_NAME); 1622*3bfaa971Sbrad break; 1623*3bfaa971Sbrad case MCP2221_SRAM_GPIO_CLOCK_CD_6MHZ: 1624*3bfaa971Sbrad strncpy(buf, "6MHz", UMCPMIO_CD_NAME); 1625*3bfaa971Sbrad break; 1626*3bfaa971Sbrad case MCP2221_SRAM_GPIO_CLOCK_CD_12MHZ: 1627*3bfaa971Sbrad strncpy(buf, "12MHz", UMCPMIO_CD_NAME); 1628*3bfaa971Sbrad break; 1629*3bfaa971Sbrad case MCP2221_SRAM_GPIO_CLOCK_CD_24MHZ: 1630*3bfaa971Sbrad strncpy(buf, "24MHz", UMCPMIO_CD_NAME); 1631*3bfaa971Sbrad break; 1632*3bfaa971Sbrad default: 1633*3bfaa971Sbrad strncpy(buf, "12MHz", UMCPMIO_CD_NAME); 1634*3bfaa971Sbrad break; 1635*3bfaa971Sbrad } 1636*3bfaa971Sbrad strncpy(cbuf, buf, UMCPMIO_CD_NAME); 1637*3bfaa971Sbrad node.sysctl_data = buf; 1638*3bfaa971Sbrad error = sysctl_lookup(SYSCTLFN_CALL(&node)); 1639*3bfaa971Sbrad if (error || newp == NULL) 1640*3bfaa971Sbrad goto out; 1641*3bfaa971Sbrad 1642*3bfaa971Sbrad for (i = 0; i < __arraycount(umcpmio_cd_names); i++) { 1643*3bfaa971Sbrad if (strncmp(node.sysctl_data, umcpmio_cd_names[i].text, 1644*3bfaa971Sbrad UMCPMIO_CD_NAME) == 0) { 1645*3bfaa971Sbrad break; 1646*3bfaa971Sbrad } 1647*3bfaa971Sbrad } 1648*3bfaa971Sbrad 1649*3bfaa971Sbrad if (i == __arraycount(umcpmio_cd_names)) 1650*3bfaa971Sbrad error = EINVAL; 1651*3bfaa971Sbrad 1652*3bfaa971Sbrad if (! error) { 1653*3bfaa971Sbrad if (strncmp(cbuf, buf, UMCPMIO_CD_NAME) != 0) { 1654*3bfaa971Sbrad DPRINTF(("umcpmio_verify_gpioclock_cd_sysctl: setting GPIO clock clock divider: %s\n",buf)); 1655*3bfaa971Sbrad error = umcpmio_set_gpioclock_cd_one(sc, buf, false); 1656*3bfaa971Sbrad } 1657*3bfaa971Sbrad } 1658*3bfaa971Sbrad 1659*3bfaa971Sbrad out: 1660*3bfaa971Sbrad mutex_exit(&sc->sc_action_mutex); 1661*3bfaa971Sbrad return error; 1662*3bfaa971Sbrad } 1663*3bfaa971Sbrad 1664*3bfaa971Sbrad static int 1665*3bfaa971Sbrad umcpmio_sysctl_init(struct umcpmio_softc *sc) 1666*3bfaa971Sbrad { 1667*3bfaa971Sbrad int error; 1668*3bfaa971Sbrad const struct sysctlnode *cnode; 1669*3bfaa971Sbrad int sysctlroot_num, i2c_num, adc_dac_num, adc_num, dac_num, gpio_num; 1670*3bfaa971Sbrad 1671*3bfaa971Sbrad if ((error = sysctl_createv(&sc->sc_umcpmiolog, 0, NULL, &cnode, 1672*3bfaa971Sbrad 0, CTLTYPE_NODE, device_xname(sc->sc_dev), 1673*3bfaa971Sbrad SYSCTL_DESCR("mcpmio controls"), NULL, 0, NULL, 0, CTL_HW, 1674*3bfaa971Sbrad CTL_CREATE, CTL_EOL)) != 0) 1675*3bfaa971Sbrad return error; 1676*3bfaa971Sbrad 1677*3bfaa971Sbrad sysctlroot_num = cnode->sysctl_num; 1678*3bfaa971Sbrad 1679*3bfaa971Sbrad #ifdef UMCPMIO_DEBUG 1680*3bfaa971Sbrad if ((error = sysctl_createv(&sc->sc_umcpmiolog, 0, NULL, &cnode, 1681*3bfaa971Sbrad CTLFLAG_READWRITE, CTLTYPE_INT, "debug", 1682*3bfaa971Sbrad SYSCTL_DESCR("Debug level"), umcpmio_verify_sysctl, 0, 1683*3bfaa971Sbrad &umcpmiodebug, 0, CTL_HW, sysctlroot_num, CTL_CREATE, 1684*3bfaa971Sbrad CTL_EOL)) != 0) 1685*3bfaa971Sbrad return error; 1686*3bfaa971Sbrad 1687*3bfaa971Sbrad if ((error = sysctl_createv(&sc->sc_umcpmiolog, 0, NULL, &cnode, 1688*3bfaa971Sbrad CTLFLAG_READWRITE, CTLTYPE_BOOL, "dump_buffers", 1689*3bfaa971Sbrad SYSCTL_DESCR("Dump buffer when debugging"), NULL, 0, &sc->sc_dumpbuffer, 1690*3bfaa971Sbrad 0, CTL_HW, sysctlroot_num, CTL_CREATE, CTL_EOL)) != 0) 1691*3bfaa971Sbrad return error; 1692*3bfaa971Sbrad #endif 1693*3bfaa971Sbrad 1694*3bfaa971Sbrad if ((error = sysctl_createv(&sc->sc_umcpmiolog, 0, NULL, &cnode, 1695*3bfaa971Sbrad CTLFLAG_READWRITE, CTLTYPE_INT, "response_wait", 1696*3bfaa971Sbrad SYSCTL_DESCR("How long to wait in ms for a response for a HID report"), 1697*3bfaa971Sbrad umcpmio_verify_sysctl, 0, &sc->sc_cv_wait, 0, CTL_HW, 1698*3bfaa971Sbrad sysctlroot_num, CTL_CREATE, CTL_EOL)) != 0) 1699*3bfaa971Sbrad return error; 1700*3bfaa971Sbrad 1701*3bfaa971Sbrad if ((error = sysctl_createv(&sc->sc_umcpmiolog, 0, NULL, &cnode, 1702*3bfaa971Sbrad CTLFLAG_READWRITE, CTLTYPE_INT, "response_errcnt", 1703*3bfaa971Sbrad SYSCTL_DESCR("How many errors to allow on a response"), 1704*3bfaa971Sbrad umcpmio_verify_sysctl, 0, &sc->sc_response_errcnt, 0, CTL_HW, 1705*3bfaa971Sbrad sysctlroot_num, CTL_CREATE, CTL_EOL)) != 0) 1706*3bfaa971Sbrad return error; 1707*3bfaa971Sbrad 1708*3bfaa971Sbrad if ((error = sysctl_createv(&sc->sc_umcpmiolog, 0, NULL, &cnode, 1709*3bfaa971Sbrad 0, CTLTYPE_NODE, "i2c", 1710*3bfaa971Sbrad SYSCTL_DESCR("I2C controls"), NULL, 0, NULL, 0, CTL_HW, 1711*3bfaa971Sbrad sysctlroot_num, CTL_CREATE, CTL_EOL)) != 0) 1712*3bfaa971Sbrad return error; 1713*3bfaa971Sbrad 1714*3bfaa971Sbrad i2c_num = cnode->sysctl_num; 1715*3bfaa971Sbrad 1716*3bfaa971Sbrad if ((error = sysctl_createv(&sc->sc_umcpmiolog, 0, NULL, &cnode, 1717*3bfaa971Sbrad 0, CTLTYPE_NODE, "adcdac", 1718*3bfaa971Sbrad SYSCTL_DESCR("ADC and DAC controls"), NULL, 0, NULL, 0, CTL_HW, 1719*3bfaa971Sbrad sysctlroot_num, CTL_CREATE, CTL_EOL)) != 0) 1720*3bfaa971Sbrad return error; 1721*3bfaa971Sbrad 1722*3bfaa971Sbrad adc_dac_num = cnode->sysctl_num; 1723*3bfaa971Sbrad 1724*3bfaa971Sbrad if ((error = sysctl_createv(&sc->sc_umcpmiolog, 0, NULL, &cnode, 1725*3bfaa971Sbrad 0, CTLTYPE_NODE, "adc", 1726*3bfaa971Sbrad SYSCTL_DESCR("ADC controls"), NULL, 0, NULL, 0, CTL_HW, 1727*3bfaa971Sbrad sysctlroot_num, CTL_CREATE, CTL_EOL)) != 0) 1728*3bfaa971Sbrad return error; 1729*3bfaa971Sbrad 1730*3bfaa971Sbrad adc_num = cnode->sysctl_num; 1731*3bfaa971Sbrad 1732*3bfaa971Sbrad if ((error = sysctl_createv(&sc->sc_umcpmiolog, 0, NULL, &cnode, 1733*3bfaa971Sbrad 0, CTLTYPE_NODE, "dac", 1734*3bfaa971Sbrad SYSCTL_DESCR("DAC controls"), NULL, 0, NULL, 0, CTL_HW, 1735*3bfaa971Sbrad sysctlroot_num, CTL_CREATE, CTL_EOL)) != 0) 1736*3bfaa971Sbrad return error; 1737*3bfaa971Sbrad 1738*3bfaa971Sbrad dac_num = cnode->sysctl_num; 1739*3bfaa971Sbrad 1740*3bfaa971Sbrad if ((error = sysctl_createv(&sc->sc_umcpmiolog, 0, NULL, &cnode, 1741*3bfaa971Sbrad 0, CTLTYPE_NODE, "gpio", 1742*3bfaa971Sbrad SYSCTL_DESCR("GPIO controls"), NULL, 0, NULL, 0, CTL_HW, 1743*3bfaa971Sbrad sysctlroot_num, CTL_CREATE, CTL_EOL)) != 0) 1744*3bfaa971Sbrad return error; 1745*3bfaa971Sbrad 1746*3bfaa971Sbrad gpio_num = cnode->sysctl_num; 1747*3bfaa971Sbrad 1748*3bfaa971Sbrad /* I2C */ 1749*3bfaa971Sbrad if ((error = sysctl_createv(&sc->sc_umcpmiolog, 0, NULL, &cnode, 1750*3bfaa971Sbrad CTLFLAG_READWRITE, CTLTYPE_BOOL, "reportreadnostop", 1751*3bfaa971Sbrad SYSCTL_DESCR("Report that a READ without STOP was attempted by a device"), 1752*3bfaa971Sbrad NULL, 0, &sc->sc_reportreadnostop, 0, CTL_HW, sysctlroot_num, i2c_num, 1753*3bfaa971Sbrad CTL_CREATE, CTL_EOL)) != 0) 1754*3bfaa971Sbrad return error; 1755*3bfaa971Sbrad 1756*3bfaa971Sbrad if ((error = sysctl_createv(&sc->sc_umcpmiolog, 0, NULL, &cnode, 1757*3bfaa971Sbrad CTLFLAG_READWRITE, CTLTYPE_INT, "busy_delay", 1758*3bfaa971Sbrad SYSCTL_DESCR("How long to wait in ms when the I2C engine is busy"), 1759*3bfaa971Sbrad umcpmio_verify_sysctl, 0, &sc->sc_busy_delay, 0, CTL_HW, 1760*3bfaa971Sbrad sysctlroot_num, i2c_num, CTL_CREATE, CTL_EOL)) != 0) 1761*3bfaa971Sbrad return error; 1762*3bfaa971Sbrad 1763*3bfaa971Sbrad if ((error = sysctl_createv(&sc->sc_umcpmiolog, 0, NULL, &cnode, 1764*3bfaa971Sbrad CTLFLAG_READWRITE, CTLTYPE_INT, "retry_busy_read", 1765*3bfaa971Sbrad SYSCTL_DESCR("How many times to retry a busy I2C read"), 1766*3bfaa971Sbrad umcpmio_verify_sysctl, 0, &sc->sc_retry_busy_read, 0, CTL_HW, 1767*3bfaa971Sbrad sysctlroot_num, i2c_num, CTL_CREATE, CTL_EOL)) != 0) 1768*3bfaa971Sbrad return error; 1769*3bfaa971Sbrad 1770*3bfaa971Sbrad if ((error = sysctl_createv(&sc->sc_umcpmiolog, 0, NULL, &cnode, 1771*3bfaa971Sbrad CTLFLAG_READWRITE, CTLTYPE_INT, "retry_busy_write", 1772*3bfaa971Sbrad SYSCTL_DESCR("How many times to retry a busy I2C write"), 1773*3bfaa971Sbrad umcpmio_verify_sysctl, 0, &sc->sc_retry_busy_write, 0, CTL_HW, 1774*3bfaa971Sbrad sysctlroot_num, i2c_num, CTL_CREATE, CTL_EOL)) != 0) 1775*3bfaa971Sbrad return error; 1776*3bfaa971Sbrad 1777*3bfaa971Sbrad /* GPIO */ 1778*3bfaa971Sbrad if ((error = sysctl_createv(&sc->sc_umcpmiolog, 0, NULL, &cnode, 1779*3bfaa971Sbrad CTLFLAG_READWRITE, CTLTYPE_INT, "irq_poll", 1780*3bfaa971Sbrad SYSCTL_DESCR("How often to poll for a IRQ change"), 1781*3bfaa971Sbrad umcpmio_verify_sysctl, 0, &sc->sc_irq_poll, 0, CTL_HW, 1782*3bfaa971Sbrad sysctlroot_num, gpio_num, CTL_CREATE, CTL_EOL)) != 0) 1783*3bfaa971Sbrad return error; 1784*3bfaa971Sbrad 1785*3bfaa971Sbrad if ((error = sysctl_createv(&sc->sc_umcpmiolog, 0, NULL, &cnode, 1786*3bfaa971Sbrad CTLFLAG_READONLY, CTLTYPE_STRING, "clock_duty_cycles", 1787*3bfaa971Sbrad SYSCTL_DESCR("Valid duty cycles for GPIO clock on GP1 ALT3 duty cycle"), 1788*3bfaa971Sbrad 0, 0, __UNCONST(umcpmio_valid_dcs), 1789*3bfaa971Sbrad sizeof(umcpmio_valid_dcs) + 1, CTL_HW, sysctlroot_num, gpio_num, CTL_CREATE, CTL_EOL)) != 0) 1790*3bfaa971Sbrad return error; 1791*3bfaa971Sbrad 1792*3bfaa971Sbrad if ((error = sysctl_createv(&sc->sc_umcpmiolog, 0, NULL, &cnode, 1793*3bfaa971Sbrad CTLFLAG_READWRITE, CTLTYPE_STRING, "clock_duty_cycle", 1794*3bfaa971Sbrad SYSCTL_DESCR("GPIO clock on GP1 ALT3 duty cycle"), 1795*3bfaa971Sbrad umcpmio_verify_gpioclock_dc_sysctl, 0, (void *) sc, 1796*3bfaa971Sbrad UMCPMIO_DC_NAME, CTL_HW, sysctlroot_num, gpio_num, CTL_CREATE, CTL_EOL)) != 0) 1797*3bfaa971Sbrad return error; 1798*3bfaa971Sbrad 1799*3bfaa971Sbrad if ((error = sysctl_createv(&sc->sc_umcpmiolog, 0, NULL, &cnode, 1800*3bfaa971Sbrad CTLFLAG_READONLY, CTLTYPE_STRING, "clock_dividers", 1801*3bfaa971Sbrad SYSCTL_DESCR("Valid clock dividers for GPIO clock on GP1 with ALT3"), 1802*3bfaa971Sbrad 0, 0, __UNCONST(umcpmio_valid_cds), 1803*3bfaa971Sbrad sizeof(umcpmio_valid_cds) + 1, CTL_HW, sysctlroot_num, gpio_num, CTL_CREATE, CTL_EOL)) != 0) 1804*3bfaa971Sbrad return error; 1805*3bfaa971Sbrad 1806*3bfaa971Sbrad if ((error = sysctl_createv(&sc->sc_umcpmiolog, 0, NULL, &cnode, 1807*3bfaa971Sbrad CTLFLAG_READWRITE, CTLTYPE_STRING, "clock_divider", 1808*3bfaa971Sbrad SYSCTL_DESCR("GPIO clock on GP1 ALT3 clock divider"), 1809*3bfaa971Sbrad umcpmio_verify_gpioclock_cd_sysctl, 0, (void *) sc, 1810*3bfaa971Sbrad UMCPMIO_CD_NAME, CTL_HW, sysctlroot_num, gpio_num, CTL_CREATE, CTL_EOL)) != 0) 1811*3bfaa971Sbrad return error; 1812*3bfaa971Sbrad 1813*3bfaa971Sbrad /* ADC and DAC */ 1814*3bfaa971Sbrad if ((error = sysctl_createv(&sc->sc_umcpmiolog, 0, NULL, &cnode, 1815*3bfaa971Sbrad CTLFLAG_READONLY, CTLTYPE_STRING, "vrefs", 1816*3bfaa971Sbrad SYSCTL_DESCR("Valid vref values for ADC and DAC"), 1817*3bfaa971Sbrad 0, 0, __UNCONST(umcpmio_valid_vrefs), 1818*3bfaa971Sbrad sizeof(umcpmio_valid_vrefs) + 1, CTL_HW, sysctlroot_num, adc_dac_num, CTL_CREATE, CTL_EOL)) != 0) 1819*3bfaa971Sbrad return error; 1820*3bfaa971Sbrad 1821*3bfaa971Sbrad /* ADC */ 1822*3bfaa971Sbrad if ((error = sysctl_createv(&sc->sc_umcpmiolog, 0, NULL, &cnode, 1823*3bfaa971Sbrad CTLFLAG_READWRITE, CTLTYPE_STRING, "vref", 1824*3bfaa971Sbrad SYSCTL_DESCR("ADC voltage reference"), 1825*3bfaa971Sbrad umcpmio_verify_adc_sysctl, 0, (void *) sc, 1826*3bfaa971Sbrad UMCPMIO_VREF_NAME, CTL_HW, sysctlroot_num, adc_num, CTL_CREATE, CTL_EOL)) != 0) 1827*3bfaa971Sbrad return error; 1828*3bfaa971Sbrad 1829*3bfaa971Sbrad /* DAC */ 1830*3bfaa971Sbrad if ((error = sysctl_createv(&sc->sc_umcpmiolog, 0, NULL, &cnode, 1831*3bfaa971Sbrad CTLFLAG_READWRITE, CTLTYPE_STRING, "vref", 1832*3bfaa971Sbrad SYSCTL_DESCR("DAC voltage reference"), 1833*3bfaa971Sbrad umcpmio_verify_dac_sysctl, 0, (void *) sc, 1834*3bfaa971Sbrad UMCPMIO_VREF_NAME, CTL_HW, sysctlroot_num, dac_num, CTL_CREATE, CTL_EOL)) != 0) 1835*3bfaa971Sbrad return error; 1836*3bfaa971Sbrad 1837*3bfaa971Sbrad return 0; 1838*3bfaa971Sbrad } 1839*3bfaa971Sbrad 1840*3bfaa971Sbrad static int 1841*3bfaa971Sbrad umcpmio_match(device_t parent, cfdata_t match, void *aux) 1842*3bfaa971Sbrad { 1843*3bfaa971Sbrad struct uhidev_attach_arg *uha = aux; 1844*3bfaa971Sbrad 1845*3bfaa971Sbrad return umcpmio_lookup(uha->uiaa->uiaa_vendor, uha->uiaa->uiaa_product) 1846*3bfaa971Sbrad != NULL ? UMATCH_VENDOR_PRODUCT : UMATCH_NONE; 1847*3bfaa971Sbrad } 1848*3bfaa971Sbrad 1849*3bfaa971Sbrad 1850*3bfaa971Sbrad /* This driver could be extended to support the MCP-2210 which is MCP's USB to 1851*3bfaa971Sbrad * SPI / gpio chip. It also appears to be a something like the PIC16F1455 used in 1852*3bfaa971Sbrad * the MCP2221 / MCP2221A. It is likely that a lot of this could use tables to 1853*3bfaa971Sbrad * drive behavior. 1854*3bfaa971Sbrad */ 1855*3bfaa971Sbrad 1856*3bfaa971Sbrad static void 1857*3bfaa971Sbrad umcpmio_attach(device_t parent, device_t self, void *aux) 1858*3bfaa971Sbrad { 1859*3bfaa971Sbrad struct umcpmio_softc *sc = device_private(self); 1860*3bfaa971Sbrad struct uhidev_attach_arg *uha = aux; 1861*3bfaa971Sbrad struct gpiobus_attach_args gba; 1862*3bfaa971Sbrad struct i2cbus_attach_args iba; 1863*3bfaa971Sbrad int err; 1864*3bfaa971Sbrad struct mcp2221_status_res status_res; 1865*3bfaa971Sbrad 1866*3bfaa971Sbrad sc->sc_dev = self; 1867*3bfaa971Sbrad sc->sc_hdev = uha->parent; 1868*3bfaa971Sbrad sc->sc_udev = uha->uiaa->uiaa_device; 1869*3bfaa971Sbrad 1870*3bfaa971Sbrad sc->sc_umcpmiolog = NULL; 1871*3bfaa971Sbrad sc->sc_dumpbuffer = false; 1872*3bfaa971Sbrad 1873*3bfaa971Sbrad sc->sc_reportreadnostop = true; 1874*3bfaa971Sbrad sc->sc_cv_wait = 2500; 1875*3bfaa971Sbrad sc->sc_response_errcnt = 5; 1876*3bfaa971Sbrad sc->sc_busy_delay = 1; 1877*3bfaa971Sbrad sc->sc_retry_busy_read = 50; 1878*3bfaa971Sbrad sc->sc_retry_busy_write = 50; 1879*3bfaa971Sbrad sc->sc_irq_poll = 10; 1880*3bfaa971Sbrad sc->sc_dev_open[CONTROL_DEV] = sc->sc_dev_open[GP1_DEV] = sc->sc_dev_open[GP2_DEV] = sc->sc_dev_open[GP3_DEV] = false; 1881*3bfaa971Sbrad 1882*3bfaa971Sbrad aprint_normal("\n"); 1883*3bfaa971Sbrad 1884*3bfaa971Sbrad if ((err = umcpmio_sysctl_init(sc)) != 0) { 1885*3bfaa971Sbrad aprint_error_dev(self, "Can't setup sysctl tree (%d)\n", err); 1886*3bfaa971Sbrad return; 1887*3bfaa971Sbrad } 1888*3bfaa971Sbrad 1889*3bfaa971Sbrad mutex_init(&sc->sc_action_mutex, MUTEX_DEFAULT, IPL_NONE); 1890*3bfaa971Sbrad cv_init(&sc->sc_res_cv, "mcpres"); 1891*3bfaa971Sbrad mutex_init(&sc->sc_res_mutex, MUTEX_DEFAULT, IPL_NONE); 1892*3bfaa971Sbrad sc->sc_res_buffer = NULL; 1893*3bfaa971Sbrad sc->sc_res_ready = false; 1894*3bfaa971Sbrad 1895*3bfaa971Sbrad err = uhidev_open(sc->sc_hdev, &umcpmio_uhidev_intr, sc); 1896*3bfaa971Sbrad 1897*3bfaa971Sbrad /* It is not clear that this should be needed, but it was noted 1898*3bfaa971Sbrad * that the device would sometimes not be ready if this delay 1899*3bfaa971Sbrad * was not present. In fact, the attempts to set stuff a little 1900*3bfaa971Sbrad * later would sometimes fail. 1901*3bfaa971Sbrad */ 1902*3bfaa971Sbrad 1903*3bfaa971Sbrad delay(1000); 1904*3bfaa971Sbrad 1905*3bfaa971Sbrad if (err) 1906*3bfaa971Sbrad aprint_error_dev(sc->sc_dev, "umcpmio_attach: open uhidev_open: err=%d\n",err); 1907*3bfaa971Sbrad 1908*3bfaa971Sbrad if (!err) 1909*3bfaa971Sbrad err = umcpmio_get_status(sc, &status_res, true); 1910*3bfaa971Sbrad 1911*3bfaa971Sbrad if (!err) { 1912*3bfaa971Sbrad aprint_normal_dev(sc->sc_dev, "Hardware revision: %d.%d, Firmware revision: %d.%d\n", 1913*3bfaa971Sbrad status_res.mcp2221_hardware_rev_major, 1914*3bfaa971Sbrad status_res.mcp2221_hardware_rev_minor, 1915*3bfaa971Sbrad status_res.mcp2221_firmware_rev_major, 1916*3bfaa971Sbrad status_res.mcp2221_firmware_rev_minor); 1917*3bfaa971Sbrad 1918*3bfaa971Sbrad /* The datasheet suggests that it is possble for this to fail if the I2C port 1919*3bfaa971Sbrad * is currently being used. However... since you just plugged in the chip, 1920*3bfaa971Sbrad * the I2C port should not really be in use at that moment. In any case, try 1921*3bfaa971Sbrad * hard to set this and don't make it fatal if it did not get set. 1922*3bfaa971Sbrad */ 1923*3bfaa971Sbrad int i2cspeed=0; 1924*3bfaa971Sbrad while (! err && i2cspeed < 3) { 1925*3bfaa971Sbrad err = umcpmio_set_i2c_speed_one(sc, I2C_SPEED_SM, true); 1926*3bfaa971Sbrad if (err) { 1927*3bfaa971Sbrad aprint_error_dev(sc->sc_dev, "umcpmio_attach: set I2C speed: err=%d\n", 1928*3bfaa971Sbrad err); 1929*3bfaa971Sbrad delay(300); 1930*3bfaa971Sbrad } 1931*3bfaa971Sbrad i2cspeed++; 1932*3bfaa971Sbrad } 1933*3bfaa971Sbrad 1934*3bfaa971Sbrad struct mcp2221_get_sram_res get_sram_res; 1935*3bfaa971Sbrad err = umcpmio_get_sram(sc, &get_sram_res, true); 1936*3bfaa971Sbrad 1937*3bfaa971Sbrad if (! err) { 1938*3bfaa971Sbrad umcpmio_dump_buffer(sc->sc_dumpbuffer, (uint8_t *)&get_sram_res, MCP2221_RES_BUFFER_SIZE, "umcpmio_attach get sram buffer copy"); 1939*3bfaa971Sbrad 1940*3bfaa971Sbrad /* There are only 4 pins right now, just unroll any loops */ 1941*3bfaa971Sbrad 1942*3bfaa971Sbrad sc->sc_gpio_pins[0].pin_num = 0; 1943*3bfaa971Sbrad sc->sc_gpio_pins[0].pin_caps = GPIO_PIN_INPUT | GPIO_PIN_OUTPUT | GPIO_PIN_ALT0 | GPIO_PIN_ALT3; 1944*3bfaa971Sbrad sc->sc_gpio_pins[0].pin_flags = umcpmio_sram_gpio_to_flags(get_sram_res.gp0_settings); 1945*3bfaa971Sbrad sc->sc_gpio_pins[0].pin_intrcaps = 0; 1946*3bfaa971Sbrad snprintf(sc->sc_gpio_pins[0].pin_defname, 4, "GP0"); 1947*3bfaa971Sbrad 1948*3bfaa971Sbrad sc->sc_gpio_pins[1].pin_num = 1; 1949*3bfaa971Sbrad sc->sc_gpio_pins[1].pin_caps = GPIO_PIN_INPUT | GPIO_PIN_OUTPUT | GPIO_PIN_ALT0 | GPIO_PIN_ALT1 | GPIO_PIN_ALT2 | GPIO_PIN_ALT3; 1950*3bfaa971Sbrad sc->sc_gpio_pins[1].pin_flags = umcpmio_sram_gpio_to_flags(get_sram_res.gp1_settings); 1951*3bfaa971Sbrad /* XXX - lets not advertise this right now... */ 1952*3bfaa971Sbrad #if 0 1953*3bfaa971Sbrad sc->sc_gpio_pins[1].pin_intrcaps = GPIO_INTR_POS_EDGE | GPIO_INTR_NEG_EDGE | GPIO_INTR_DOUBLE_EDGE | GPIO_INTR_MPSAFE; 1954*3bfaa971Sbrad #endif 1955*3bfaa971Sbrad sc->sc_gpio_pins[1].pin_intrcaps = 0; 1956*3bfaa971Sbrad snprintf(sc->sc_gpio_pins[1].pin_defname, 4, "GP1"); 1957*3bfaa971Sbrad 1958*3bfaa971Sbrad sc->sc_gpio_pins[2].pin_num = 2; 1959*3bfaa971Sbrad sc->sc_gpio_pins[2].pin_caps = GPIO_PIN_INPUT | GPIO_PIN_OUTPUT | GPIO_PIN_ALT0 | GPIO_PIN_ALT1 | GPIO_PIN_ALT3; 1960*3bfaa971Sbrad sc->sc_gpio_pins[2].pin_flags = umcpmio_sram_gpio_to_flags(get_sram_res.gp2_settings); 1961*3bfaa971Sbrad sc->sc_gpio_pins[2].pin_intrcaps = 0; 1962*3bfaa971Sbrad snprintf(sc->sc_gpio_pins[2].pin_defname, 4, "GP2"); 1963*3bfaa971Sbrad 1964*3bfaa971Sbrad sc->sc_gpio_pins[3].pin_num = 3; 1965*3bfaa971Sbrad sc->sc_gpio_pins[3].pin_caps = GPIO_PIN_INPUT | GPIO_PIN_OUTPUT | GPIO_PIN_ALT0 | GPIO_PIN_ALT1 | GPIO_PIN_ALT3; 1966*3bfaa971Sbrad sc->sc_gpio_pins[3].pin_flags = umcpmio_sram_gpio_to_flags(get_sram_res.gp3_settings); 1967*3bfaa971Sbrad sc->sc_gpio_pins[3].pin_intrcaps = 0; 1968*3bfaa971Sbrad snprintf(sc->sc_gpio_pins[3].pin_defname, 4, "GP3"); 1969*3bfaa971Sbrad 1970*3bfaa971Sbrad sc->sc_gpio_gc.gp_cookie = sc; 1971*3bfaa971Sbrad sc->sc_gpio_gc.gp_pin_read = umcpmio_gpio_pin_read; 1972*3bfaa971Sbrad sc->sc_gpio_gc.gp_pin_write = umcpmio_gpio_pin_write; 1973*3bfaa971Sbrad sc->sc_gpio_gc.gp_pin_ctl = umcpmio_gpio_pin_ctl; 1974*3bfaa971Sbrad 1975*3bfaa971Sbrad sc->sc_gpio_gc.gp_intr_establish = umcpmio_gpio_intr_establish; 1976*3bfaa971Sbrad sc->sc_gpio_gc.gp_intr_disestablish = umcpmio_gpio_intr_disestablish; 1977*3bfaa971Sbrad sc->sc_gpio_gc.gp_intr_str = umcpmio_gpio_intrstr; 1978*3bfaa971Sbrad 1979*3bfaa971Sbrad 1980*3bfaa971Sbrad gba.gba_gc = &sc->sc_gpio_gc; 1981*3bfaa971Sbrad gba.gba_pins = sc->sc_gpio_pins; 1982*3bfaa971Sbrad gba.gba_npins = MCP2221_NPINS; 1983*3bfaa971Sbrad 1984*3bfaa971Sbrad sc->sc_gpio_dev = config_found(self, &gba, gpiobus_print, CFARGS(.iattr = "gpiobus")); 1985*3bfaa971Sbrad 1986*3bfaa971Sbrad iic_tag_init(&sc->sc_i2c_tag); 1987*3bfaa971Sbrad sc->sc_i2c_tag.ic_cookie = sc; 1988*3bfaa971Sbrad sc->sc_i2c_tag.ic_acquire_bus = umcpmio_acquire_bus; 1989*3bfaa971Sbrad sc->sc_i2c_tag.ic_release_bus = umcpmio_release_bus; 1990*3bfaa971Sbrad sc->sc_i2c_tag.ic_exec = umcpmio_i2c_exec; 1991*3bfaa971Sbrad 1992*3bfaa971Sbrad memset(&iba, 0, sizeof(iba)); 1993*3bfaa971Sbrad iba.iba_tag = &sc->sc_i2c_tag; 1994*3bfaa971Sbrad sc->sc_i2c_dev = config_found(self, &iba, iicbus_print, CFARGS(.iattr = "i2cbus")); 1995*3bfaa971Sbrad } else { 1996*3bfaa971Sbrad aprint_error_dev(sc->sc_dev, "umcpmio_attach: get sram error: err=%d\n", 1997*3bfaa971Sbrad err); 1998*3bfaa971Sbrad } 1999*3bfaa971Sbrad } else { 2000*3bfaa971Sbrad aprint_error_dev(sc->sc_dev, "umcpmio_attach: open uhidev_open: err=%d\n", err); 2001*3bfaa971Sbrad } 2002*3bfaa971Sbrad } 2003*3bfaa971Sbrad 2004*3bfaa971Sbrad 2005*3bfaa971Sbrad static int 2006*3bfaa971Sbrad umcpmio_detach(device_t self, int flags) 2007*3bfaa971Sbrad { 2008*3bfaa971Sbrad struct umcpmio_softc *sc = device_private(self); 2009*3bfaa971Sbrad int err; 2010*3bfaa971Sbrad 2011*3bfaa971Sbrad DPRINTF(("umcpmio_detach: sc=%p flags=%d\n", sc, flags)); 2012*3bfaa971Sbrad 2013*3bfaa971Sbrad mutex_enter(&sc->sc_action_mutex); 2014*3bfaa971Sbrad sc->sc_dying = 1; 2015*3bfaa971Sbrad 2016*3bfaa971Sbrad err = config_detach_children(self, flags); 2017*3bfaa971Sbrad if (err) 2018*3bfaa971Sbrad return err; 2019*3bfaa971Sbrad 2020*3bfaa971Sbrad uhidev_close(sc->sc_hdev); 2021*3bfaa971Sbrad 2022*3bfaa971Sbrad mutex_destroy(&sc->sc_res_mutex); 2023*3bfaa971Sbrad cv_destroy(&sc->sc_res_cv); 2024*3bfaa971Sbrad 2025*3bfaa971Sbrad sysctl_teardown(&sc->sc_umcpmiolog); 2026*3bfaa971Sbrad 2027*3bfaa971Sbrad mutex_exit(&sc->sc_action_mutex); 2028*3bfaa971Sbrad mutex_destroy(&sc->sc_action_mutex); 2029*3bfaa971Sbrad 2030*3bfaa971Sbrad return 0; 2031*3bfaa971Sbrad } 2032*3bfaa971Sbrad 2033*3bfaa971Sbrad static int 2034*3bfaa971Sbrad umcpmio_activate(device_t self, enum devact act) 2035*3bfaa971Sbrad { 2036*3bfaa971Sbrad struct umcpmio_softc *sc = device_private(self); 2037*3bfaa971Sbrad 2038*3bfaa971Sbrad DPRINTFN(5,("umcpmio_activate: %d\n", act)); 2039*3bfaa971Sbrad 2040*3bfaa971Sbrad switch (act) { 2041*3bfaa971Sbrad case DVACT_DEACTIVATE: 2042*3bfaa971Sbrad sc->sc_dying = 1; 2043*3bfaa971Sbrad return 0; 2044*3bfaa971Sbrad default: 2045*3bfaa971Sbrad return EOPNOTSUPP; 2046*3bfaa971Sbrad } 2047*3bfaa971Sbrad } 2048