static int query_adc(int mux) { uint8_t buf[2]; uint32_t mux_masked, result = 0; uint64_t startTime; mux_masked = mux & 0xF; result = pmu_get_reg(PMU_ADC_REG); if (mux == 3) { mux_masked |= 0x20; pmu_write_reg(PMU_MUXSEL_REG, mux_masked, 0); udelay(80000); } pmu_write_reg(PMU_MUXSEL_REG, mux_masked | 0x10, 0); startTime = timer_get_system_microtime(); do { udelay(1000); if (has_elapsed(startTime, 50000)) return -1; result = pmu_get_reg(PMU_ADC_REG); } while (!(result & 0x20)); pmu_get_regs(PMU_ADCVAL_REG, buf, 2); pmu_write_reg(PMU_MUXSEL_REG, 0, 0); return (buf[1] << 4) | (buf[0] & 0xF); }
void pmu_write_oocshdwn(int data) { uint8_t poweroffData[] = {0xC0, 0xFF, 0xBF, 0xFF, 0xAE, 0xFF}; uint8_t buffer[sizeof(poweroffData) + 1]; pmu_get_reg(PMU_UNK2_REG); buffer[0] = PMU_UNK1_REG; memcpy(&buffer[1], poweroffData, sizeof(poweroffData)); i2c_tx(PMU_I2C_BUS, PMU_SETADDR, buffer, sizeof(buffer)); if (data == 1) { uint8_t result, reg; // sub_5FF0D99C(); // Something to do with gas gauge. for (reg = PMU_UNKREG_START; reg <= PMU_UNKREG_END; reg++) { result = pmu_get_reg(reg); if (!((result & 0xE0) <= 0x5F || (result & 2) == 0)) pmu_write_reg(reg, result & 0xFD, FALSE); } } pmu_write_reg(PMU_OOCSHDWN_REG, data, FALSE); while(TRUE) { udelay(100000); } }
static int pmu_send(void *cookie, int cmd, int length, uint8_t *in_msg, int rlen, uint8_t *out_msg) { struct pmu_softc *sc = cookie; int i, rcv_len = -1, s; uint8_t out_len, intreg; DPRINTF("pmu_send: "); s = splhigh(); intreg = pmu_read_reg(sc, vIER); intreg &= 0x10; pmu_write_reg(sc, vIER, intreg); /* wait idle */ do {} while (pmu_intr_state(sc)); sc->sc_error = 0; /* send command */ pmu_send_byte(sc, cmd); /* send length if necessary */ if (pm_send_cmd_type[cmd] < 0) { pmu_send_byte(sc, length); } for (i = 0; i < length; i++) { pmu_send_byte(sc, in_msg[i]); DPRINTF(" next "); } DPRINTF("done sending\n"); /* see if there's data to read */ rcv_len = pm_receive_cmd_type[cmd]; if (rcv_len == 0) goto done; /* read command */ if (rcv_len == 1) { pmu_read_byte(sc, out_msg); goto done; } else out_msg[0] = cmd; if (rcv_len < 0) { pmu_read_byte(sc, &out_len); rcv_len = out_len + 1; } for (i = 1; i < min(rcv_len, rlen); i++) pmu_read_byte(sc, &out_msg[i]); done: DPRINTF("\n"); pmu_write_reg(sc, vIER, (intreg == 0) ? 0 : 0x90); splx(s); return rcv_len; }
int pmu_write_unk(uint8_t regidx, int flag1, int flag2) { uint8_t registers = PMU_UNKREG_START + regidx; uint8_t recv_buff = 0; uint8_t data = 0; int result; if (regidx > PMU_UNKREG_END - PMU_UNKREG_START) return -1; result = i2c_rx(PMU_I2C_BUS, PMU_GETADDR, (void*)®isters, 1, (void*)&recv_buff, 1); if (result != I2CNoError) return result; recv_buff &= 0x1D; if (!flag1) { data = recv_buff | 0x60; } else { data = recv_buff; if (flag2) data |= 2; } pmu_write_reg(registers, data, 1); return 0; }
error_t pmu_setup_gpio(int _idx, int _dir, int _pol) { uint8_t reg = PMU_GPIO + _idx; uint8_t val; if (_idx >= PMU_GPIO_COUNT) return EINVAL; val = pmu_get_reg(reg); val &= 0x1D; if(!_dir) // Input { val |= 0x40; } else // Output { if(_pol) val |= 2; // High else val &=~ 2; // Low } pmu_write_reg(reg, val, 1); return SUCCESS; }
int pmu_write_regs(const PMURegisterData* regs, int num) { int i; for(i = 0; i < num; i++) { pmu_write_reg(regs[i].reg, regs[i].data, 1); } return 0; }
static void pmu_ack_on(struct pmu_softc *sc) { uint8_t reg; reg = pmu_read_reg(sc, vBufB); reg |= vPB4; pmu_write_reg(sc, vBufB, reg); }
int pmu_set_gpmem_reg(int reg, uint8_t data) { if(pmu_write_reg(reg + 0x67, data, TRUE) == 0) { GPMemCache[reg] = data; GPMemCachedPresent |= (0x1 << reg); return 0; } return -1; }
static void pmu_out(struct pmu_softc *sc) { uint8_t reg; reg = pmu_read_reg(sc, vACR); reg |= vSR_OUT; reg |= 0x0c; pmu_write_reg(sc, vACR, reg); }
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 */ }
int pmu_set_gpmem_reg(int reg, uint8_t data) { if (reg > PMU_MAXREG) return -1; // If the data isn't cached, // Or if the cached data differs than what we write, write it. if ((GPMemCachedPresent & (0x1 << reg)) == 0 || GPMemCache[reg] != data) { GPMemCache[reg] = data; GPMemCachedPresent |= (0x1 << reg); pmu_write_reg(reg ^ 0x80, data, FALSE); } return 0; }
int query_adc(int flags, uint32_t* result) { // clear the done bit if it is set pmu_get_reg(PMU_ADCSTS); // set up flags if (flags == 0x3) { pmu_write_reg(PMU_ADCCON, flags | 0x20, FALSE); udelay(80000); } pmu_write_reg(PMU_ADCCON, flags | 0x10, FALSE); // wait until done uint64_t startTime = timer_get_system_microtime(); while (!(pmu_get_reg(PMU_ADCSTS) & 0x2)) { if (has_elapsed(startTime, 40000)) { return -1; } } uint8_t out[2]; pmu_get_regs(PMU_ADCOUT1, out, 2); *result = (out[1] << 2) | (out[0] & 0x3); return 0; }
static inline int pmu_send_byte(struct pmu_softc *sc, uint8_t data) { pmu_out(sc); pmu_write_reg(sc, vSR, data); pmu_ack_off(sc); /* wait for intr to come up */ /* XXX should add a timeout and bail if it expires */ do {} while (pmu_intr_state(sc) == 0); pmu_ack_on(sc); do {} while (pmu_intr_state(sc)); pmu_ack_on(sc); DPRINTF(" %02x>", data); return 0; }
void pmu_write_oocshdwn(int data) { uint8_t registers[1]; uint8_t discardData[5]; uint8_t poweroffData[] = {7, 0xD1, 0xFF, 0xF0}; registers[0] = 1; i2c_rx(PMU_I2C_BUS, PMU_GETADDR, registers, sizeof(registers), discardData, 3); i2c_tx(PMU_I2C_BUS, PMU_SETADDR, poweroffData, sizeof(poweroffData)); pmu_write_reg(PMU_OOCSHDWN, data, FALSE); // Wait for the hardware to shut down EnterCriticalSection(); while(TRUE) { udelay(100000); } }
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 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); } }
error_t pmu_setup_ldo(int _idx, uint16_t _v, uint8_t _enable_gates, uint8_t _enable) { uint8_t val; if(_idx >= PMU_LDO_COUNT) return EINVAL; if(pmu_ldo[_idx].base_voltage && !_v) return EINVAL; if(pmu_ldo[_idx].gate_mask) { val = pmu_get_reg(PMU_LDO_GATES); val &=~ pmu_ldo[_idx].gate_mask; if(_enable && _enable_gates) val |= pmu_ldo[_idx].gate_mask; CHAIN_FAIL(pmu_write_reg(PMU_LDO_GATES, val, 1)); } if(pmu_ldo[_idx].base_voltage) { uint8_t steps = (_v - pmu_ldo[_idx].base_voltage) / pmu_ldo[_idx].step_size; if(steps > pmu_ldo[_idx].step_count) { bufferPrintf("pmu: Power too great for %d: %d > %d!\n", _idx, steps, pmu_ldo[_idx].step_count); return EINVAL; } val = pmu_get_reg(PMU_LDO_V + _idx); val &=~ pmu_ldo[_idx].step_mask; val |= (steps & pmu_ldo[_idx].step_mask); CHAIN_FAIL(pmu_write_reg(PMU_LDO_V + _idx, val, 1)); } val = pmu_get_reg(pmu_ldo[_idx].reg); val &=~ pmu_ldo[_idx].mask; if(_enable) val |= pmu_ldo[_idx].mask; if(_idx == 0x16) { // This is some haxxy dependancy code. // Basically, pins 4 and 5 from 0x16 // rely on pin 3 being enabled. // // (Actually it's broken on disable, // but I'll fix it when it needs fixing.) // -- Ricky26 if(!(pmu_ldo[_idx].mask & 8)) { if(val & 0x30) val |= 8; else val &=~ 8; } } CHAIN_FAIL(pmu_write_reg(pmu_ldo[_idx].reg, val, 1)); return SUCCESS; }