static int pmu_todr_get(todr_chip_handle_t tch, struct timeval *tvp) { struct pmu_softc *sc = tch->cookie; uint32_t sec; int count = 10; int ok = FALSE; uint8_t resp[16]; DPRINTF("pmu_todr_get\n"); while ((count > 0) && (!ok)) { pmu_send(sc, PMU_READ_RTC, 0, NULL, 16, resp); memcpy(&sec, &resp[1], 4); tvp->tv_sec = sec - DIFF19041970; ok = (sec > DIFF19041970) && (sec < 0xf0000000); if (!ok) aprint_error_dev(sc->sc_dev, "got garbage from rtc (%08x)\n", sec); count--; } if (count == 0) { aprint_error_dev(sc->sc_dev, "unable to get a sane time value\n"); tvp->tv_sec = 0; } DPRINTF("tod: %" PRIo64 "\n", tvp->tv_sec); tvp->tv_usec = 0; return 0; }
static void pmu_autopoll(void *cookie, int flag) { struct pmu_softc *sc = cookie; /* magical incantation to re-enable autopolling */ uint8_t cmd[] = {0, PMU_SET_POLL_MASK, (flag >> 8) & 0xff, flag & 0xff}; uint8_t resp[16]; if (sc->sc_autopoll == flag) return; if (flag) { pmu_send(sc, PMU_ADB_CMD, 4, cmd, 16, resp); } else { pmu_send(sc, PMU_ADB_POLL_OFF, 0, NULL, 16, resp); } sc->sc_autopoll = flag & 0xffff; }
void pmu_restart(void) { struct pmu_softc *sc; uint8_t resp[16]; if (pmu0 == NULL) return; sc = pmu0; if (pmu_send(sc, PMU_RESET_CPU, 0, NULL, 16, resp) >= 0) while (1); }
static int pmu_todr_set(todr_chip_handle_t tch, struct timeval *tvp) { struct pmu_softc *sc = tch->cookie; uint32_t sec; uint8_t resp[16]; sec = tvp->tv_sec + DIFF19041970; if (pmu_send(sc, PMU_SET_RTC, 4, (uint8_t *)&sec, 16, resp) >= 0) return 0; return -1; }
void pmu_modem(int on) { struct pmu_softc *sc; uint8_t resp[16], cmd[2] = {0, 0}; if (pmu0 == NULL) return; sc = pmu0; cmd[0] = PMU_POW0_MODEM | (on ? PMU_POW0_ON : 0); pmu_send(sc, PMU_POWER_CTRL0, 1, cmd, 16, resp); }
void pmu_poweroff(void) { struct pmu_softc *sc; uint8_t cmd[] = {'M', 'A', 'T', 'T'}; uint8_t resp[16]; if (pmu0 == NULL) return; sc = pmu0; if (pmu_send(sc, PMU_POWER_OFF, 4, cmd, 16, resp) >= 0) while (1); }
static void pmu_init(struct pmu_softc *sc) { uint8_t pmu_imask, resp[16]; pmu_imask = PMU_INT_PCEJECT | PMU_INT_SNDBRT | PMU_INT_ADB/* | PMU_INT_TICK*/; pmu_imask |= PMU_INT_BATTERY; pmu_imask |= PMU_INT_ENVIRONMENT; pmu_send(sc, PMU_SET_IMASK, 1, &pmu_imask, 16, resp); pmu_write_reg(sc, vIER, 0x90); /* enable VIA interrupts */ }
static int pmu_adb_handler(void *cookie, int len, uint8_t *data) { struct pmu_softc *sc = cookie; uint8_t resp[16]; if (sc->sc_adb_handler != NULL) { sc->sc_adb_handler(sc->sc_adb_cookie, len, data); /* * the PMU will turn off autopolling after each LISTEN so we * need to re-enable it here whenever we receive an ACK for a * LISTEN command */ if ((data[1] & 0x0c) == 0x08) { uint8_t cmd[] = {0, 0x86, (sc->sc_autopoll >> 8) & 0xff, sc->sc_autopoll & 0xff}; pmu_send(sc, PMU_ADB_CMD, 4, cmd, 16, resp); }
static int pmu_intr(void *arg) { struct pmu_softc *sc = arg; unsigned int len, i; uint8_t resp[16]; DPRINTF(":"); pmu_write_reg(sc, vIFR, 0x90); /* Clear 'em */ len = pmu_send(sc, PMU_INT_ACK, 0, NULL, 16, resp); if ((len < 1) || (resp[1] == 0)) goto done; #ifdef PMU_DEBUG { DPRINTF("intr: %02x", resp[0]); for (i = 1; i < len; i++) DPRINTF(" %02x", resp[i]); DPRINTF("\n"); } #endif if (resp[1] & PMU_INT_ADB) { pmu_adb_handler(sc, len - 1, &resp[1]); goto done; } if (resp[1] & PMU_INT_SNDBRT) { /* deal with the brightness / volume control buttons */ DPRINTF("brightness: %d volume %d\n", resp[2], resp[3]); sc->sc_brightness_wanted = resp[2]; sc->sc_volume_wanted = resp[3]; wakeup(&sc->sc_event); goto done; } if (resp[1] & PMU_INT_PCEJECT) { /* deal with PCMCIA eject buttons */ DPRINTF("card eject %d\n", resp[3]); sc->sc_pending_eject |= (resp[3] & 3); wakeup(&sc->sc_event); goto done; } if (resp[1] & PMU_INT_BATTERY) { /* deal with battery messages */ printf("battery:"); for (i = 2; i < len; i++) printf(" %02x", resp[i]); printf("\n"); goto done; } if (resp[1] & PMU_INT_ENVIRONMENT) { int closed; #ifdef PMU_VERBOSE /* deal with environment messages */ printf("environment:"); for (i = 2; i < len; i++) printf(" %02x", resp[i]); printf("\n"); #endif closed = (resp[2] & PMU_ENV_LID_CLOSED) != 0; if (closed != sc->sc_lid_closed) { sc->sc_lid_closed = closed; sysmon_pswitch_event(&sc->sc_lidswitch, closed ? PSWITCH_EVENT_PRESSED : PSWITCH_EVENT_RELEASED); } goto done; } if (resp[1] & PMU_INT_TICK) { /* don't bother */ goto done; } /* unknown interrupt code?! */ #ifdef PMU_DEBUG printf("pmu intr: %02x:", resp[1]); for (i = 2; i < len; i++) printf(" %02x", resp[i]); printf("\n"); #endif done: return 1; }
static void pmu_attach(device_t parent, device_t self, void *aux) { struct confargs *ca = aux; struct pmu_softc *sc = device_private(self); #if notyet struct i2cbus_attach_args iba; #endif uint32_t regs[16]; int irq = ca->ca_intr[0]; int node, extint_node, root_node; int nbat = 1, i, pmnode; int type = IST_EDGE; uint8_t cmd[2] = {2, 0}; uint8_t resp[16]; char name[256]; extint_node = of_getnode_byname(OF_parent(ca->ca_node), "extint-gpio1"); if (extint_node) { OF_getprop(extint_node, "interrupts", &irq, 4); type = IST_LEVEL; } aprint_normal(" irq %d: ", irq); sc->sc_dev = self; sc->sc_node = ca->ca_node; sc->sc_memt = ca->ca_tag; root_node = OF_finddevice("/"); sc->sc_error = 0; sc->sc_autopoll = 0; sc->sc_pending_eject = 0; sc->sc_brightness = sc->sc_brightness_wanted = 0x80; sc->sc_volume = sc->sc_volume_wanted = 0x80; sc->sc_flags = 0; sc->sc_callback = NULL; sc->sc_lid_closed = 0; if (bus_space_map(sc->sc_memt, ca->ca_reg[0] + ca->ca_baseaddr, ca->ca_reg[1], 0, &sc->sc_memh) != 0) { aprint_error_dev(self, "unable to map registers\n"); return; } sc->sc_ih = intr_establish(irq, type, IPL_TTY, pmu_intr, sc); pmu_init(sc); sc->sc_pmu_ops.cookie = sc; sc->sc_pmu_ops.do_command = pmu_send; sc->sc_pmu_ops.register_callback = pmu_register_callback; if (pmu0 == NULL) pmu0 = sc; pmu_send(sc, PMU_SYSTEM_READY, 1, cmd, 16, resp); /* check what kind of PMU we're talking to */ if (pmu_send(sc, PMU_GET_VERSION, 0, cmd, 16, resp) > 1) aprint_normal(" rev. %d", resp[1]); aprint_normal("\n"); node = OF_child(sc->sc_node); while (node != 0) { if (OF_getprop(node, "name", name, 256) == 0) goto next; if (strncmp(name, "pmu-i2c", 8) == 0) { aprint_normal_dev(self, "initializing IIC bus\n"); goto next; } if (strncmp(name, "adb", 4) == 0) { aprint_normal_dev(self, "initializing ADB\n"); sc->sc_adbops.cookie = sc; sc->sc_adbops.send = pmu_adb_send; sc->sc_adbops.poll = pmu_adb_poll; sc->sc_adbops.autopoll = pmu_autopoll; sc->sc_adbops.set_handler = pmu_adb_set_handler; #if NNADB > 0 config_found_ia(self, "adb_bus", &sc->sc_adbops, nadb_print); #endif goto next; } if (strncmp(name, "rtc", 4) == 0) { aprint_normal_dev(self, "initializing RTC\n"); sc->sc_todr.todr_gettime = pmu_todr_get; sc->sc_todr.todr_settime = pmu_todr_set; sc->sc_todr.cookie = sc; todr_attach(&sc->sc_todr); goto next; } if (strncmp(name, "battery", 8) == 0) goto next; aprint_normal_dev(self, "%s not configured\n", name); next: node = OF_peer(node); } if (OF_finddevice("/bandit/ohare") != -1) { aprint_normal_dev(self, "enabling ohare backlight control\n"); sc->sc_flags |= PMU_HAS_BACKLIGHT_CONTROL; cmd[0] = 0; cmd[1] = 0; memset(resp, 0, 6); if (pmu_send(sc, PMU_READ_BRIGHTNESS, 1, cmd, 16, resp) > 1) { sc->sc_brightness_wanted = resp[1]; pmu_update_brightness(sc); } } /* attach batteries */ if (of_compatible(root_node, has_legacy_battery) != -1) { pmu_attach_legacy_battery(sc); } else if (of_compatible(root_node, has_two_smart_batteries) != -1) { pmu_attach_smart_battery(sc, 0); pmu_attach_smart_battery(sc, 1); } else { /* check how many batteries we have */ pmnode = of_getnode_byname(ca->ca_node, "power-mgt"); if (pmnode == -1) goto bat_done; if (OF_getprop(pmnode, "prim-info", regs, sizeof(regs)) < 24) goto bat_done; nbat = regs[6] >> 16; for (i = 0; i < nbat; i++) pmu_attach_smart_battery(sc, i); } bat_done: #if notyet memset(&iba, 0, sizeof(iba)); iba.iba_tag = &sc->sc_i2c; sc->sc_i2c.ic_cookie = sc; sc->sc_i2c.ic_acquire_bus = pmu_i2c_acquire_bus; sc->sc_i2c.ic_release_bus = pmu_i2c_release_bus; sc->sc_i2c.ic_send_start = NULL; sc->sc_i2c.ic_send_stop = NULL; sc->sc_i2c.ic_initiate_xfer = NULL; sc->sc_i2c.ic_read_byte = NULL; sc->sc_i2c.ic_write_byte = NULL; sc->sc_i2c.ic_exec = pmu_i2c_exec; config_found_ia(sc->sc_dev, "i2cbus", &iba, iicbus_print); #endif if (kthread_create(PRI_NONE, 0, NULL, pmu_thread, sc, &sc->sc_thread, "%s", "pmu") != 0) { aprint_error_dev(self, "unable to create event kthread\n"); } sc->sc_lidswitch.smpsw_name = "Lid switch"; sc->sc_lidswitch.smpsw_type = PSWITCH_TYPE_LID; if (sysmon_pswitch_register(&sc->sc_lidswitch) != 0) aprint_error_dev(self, "unable to register lid switch with sysmon\n"); }
static int pmu_attach(device_t dev) { struct pmu_softc *sc; int i; uint8_t reg; uint8_t cmd[2] = {2, 0}; uint8_t resp[16]; phandle_t node,child; struct sysctl_ctx_list *ctx; struct sysctl_oid *tree; sc = device_get_softc(dev); sc->sc_dev = dev; sc->sc_memrid = 0; sc->sc_memr = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &sc->sc_memrid, RF_ACTIVE); mtx_init(&sc->sc_mutex,"pmu",NULL,MTX_DEF | MTX_RECURSE); if (sc->sc_memr == NULL) { device_printf(dev, "Could not alloc mem resource!\n"); return (ENXIO); } /* * Our interrupt is attached to a GPIO pin. Depending on probe order, * we may not have found it yet. If we haven't, it will find us, and * attach our interrupt then. */ pmu = dev; if (pmu_extint != NULL) { if (setup_pmu_intr(dev,pmu_extint) != 0) return (ENXIO); } sc->sc_autopoll = 0; sc->sc_batteries = 0; sc->adb_bus = NULL; sc->sc_leddev = NULL; /* Init PMU */ reg = PMU_INT_TICK | PMU_INT_ADB | PMU_INT_PCEJECT | PMU_INT_SNDBRT; reg |= PMU_INT_BATTERY; reg |= PMU_INT_ENVIRONMENT; pmu_send(sc, PMU_SET_IMASK, 1, ®, 16, resp); pmu_write_reg(sc, vIER, 0x90); /* make sure VIA interrupts are on */ pmu_send(sc, PMU_SYSTEM_READY, 1, cmd, 16, resp); pmu_send(sc, PMU_GET_VERSION, 1, cmd, 16, resp); /* Initialize child buses (ADB) */ node = ofw_bus_get_node(dev); for (child = OF_child(node); child != 0; child = OF_peer(child)) { char name[32]; memset(name, 0, sizeof(name)); OF_getprop(child, "name", name, sizeof(name)); if (bootverbose) device_printf(dev, "PMU child <%s>\n",name); if (strncmp(name, "adb", 4) == 0) { sc->adb_bus = device_add_child(dev,"adb",-1); } if (strncmp(name, "power-mgt", 9) == 0) { uint32_t prim_info[9]; if (OF_getprop(child, "prim-info", prim_info, sizeof(prim_info)) >= 7) sc->sc_batteries = (prim_info[6] >> 16) & 0xff; if (bootverbose && sc->sc_batteries > 0) device_printf(dev, "%d batteries detected\n", sc->sc_batteries); } }