/* * Device driver for the via-pmu on Apple Powermacs. * * The VIA (versatile interface adapter) interfaces to the PMU, * a 6805 microprocessor core whose primary function is to control * battery charging and system power on the PowerBook 3400 and 2400. * The PMU also controls the ADB (Apple Desktop Bus) which connects * to the keyboard and mouse, as well as the non-volatile RAM * and the RTC (real time clock) chip. * * Copyright (C) 1998 Paul Mackerras and Fabio Riccardi. * Copyright (C) 2001-2002 Benjamin Herrenschmidt * Copyright (C) 2006-2007 Johannes Berg * */ #include "config.h" #include "libopenbios/bindings.h" #include "drivers/drivers.h" #include "libc/byteorder.h" #include "libc/vsprintf.h" #include "macio.h" #include "pmu.h" #undef DEBUG_PMU #ifdef DEBUG_PMU #define PMU_DPRINTF(fmt, args...) \ do { printk("PMU - %s: " fmt, __func__ , ##args); } while (0) #else #define PMU_DPRINTF(fmt, args...) do { } while (0) #endif #define IO_PMU_OFFSET 0x00016000 #define IO_PMU_SIZE 0x00002000 /* VIA registers - spaced 0x200 bytes apart */ #define RS 0x200 /* skip between registers */ #define B 0 /* B-side data */ #define A RS /* A-side data */ #define DIRB (2*RS) /* B-side direction (1=output) */ #define DIRA (3*RS) /* A-side direction (1=output) */ #define T1CL (4*RS) /* Timer 1 ctr/latch (low 8 bits) */ #define T1CH (5*RS) /* Timer 1 counter (high 8 bits) */ #define T1LL (6*RS) /* Timer 1 latch (low 8 bits) */ #define T1LH (7*RS) /* Timer 1 latch (high 8 bits) */ #define T2CL (8*RS) /* Timer 2 ctr/latch (low 8 bits) */ #define T2CH (9*RS) /* Timer 2 counter (high 8 bits) */ #define SR (10*RS) /* Shift register */ #define ACR (11*RS) /* Auxiliary control register */ #define PCR (12*RS) /* Peripheral control register */ #define IFR (13*RS) /* Interrupt flag register */ #define IER (14*RS) /* Interrupt enable register */ #define ANH (15*RS) /* A-side data, no handshake */ /* Bits in B data register: all active low */ #define TACK 0x08 /* Transfer request (input) */ #define TREQ 0x10 /* Transfer acknowledge (output) */ /* Bits in ACR */ #define SR_CTRL 0x1c /* Shift register control bits */ #define SR_EXT 0x0c /* Shift on external clock */ #define SR_OUT 0x10 /* Shift out if 1 */ /* Bits in IFR and IER */ #define IER_SET 0x80 /* set bits in IER */ #define IER_CLR 0 /* clear bits in IER */ #define SR_INT 0x04 /* Shift register full/empty */ /* * This table indicates for each PMU opcode: * - the number of data bytes to be sent with the command, or -1 * if a length byte should be sent, * - the number of response bytes which the PMU will return, or * -1 if it will send a length byte. */ static const int8_t pmu_data_len[256][2] = { /* 0 1 2 3 4 5 6 7 */ /*00*/ {-1, 0},{-1, 0},{-1, 0},{-1, 0},{-1, 0},{-1, 0},{-1, 0},{-1, 0}, /*08*/ {-1,-1},{-1,-1},{-1,-1},{-1,-1},{-1,-1},{-1,-1},{-1,-1},{-1,-1}, /*10*/ { 1, 0},{ 1, 0},{-1, 0},{-1, 0},{-1, 0},{-1, 0},{-1, 0},{-1, 0}, /*18*/ { 0, 1},{ 0, 1},{-1,-1},{-1,-1},{-1,-1},{-1,-1},{-1,-1},{ 0, 0}, /*20*/ {-1, 0},{ 0, 0},{ 2, 0},{ 1, 0},{ 1, 0},{-1, 0},{-1, 0},{-1, 0}, /*28*/ { 0,-1},{ 0,-1},{-1,-1},{-1,-1},{-1,-1},{-1,-1},{-1,-1},{ 0,-1}, /*30*/ { 4, 0},{20, 0},{-1, 0},{ 3, 0},{-1, 0},{-1, 0},{-1, 0},{-1, 0}, /*38*/ { 0, 4},{ 0,20},{ 2,-1},{ 2, 1},{ 3,-1},{-1,-1},{-1,-1},{ 4, 0}, /*40*/ { 1, 0},{ 1, 0},{-1, 0},{-1, 0},{-1, 0},{-1, 0},{-1, 0},{-1, 0}, /*48*/ { 0, 1},{ 0, 1},{-1,-1},{ 1, 0},{ 1, 0},{-1,-1},{-1,-1},{-1,-1}, /*50*/ { 1, 0},{ 0, 0},{ 2, 0},{ 2, 0},{-1, 0},{ 1, 0},{ 3, 0},{ 1, 0}, /*58*/ { 0, 1},{ 1, 0},{ 0, 2},{ 0, 2},{ 0,-1},{-1,-1},{-1,-1},{-1,-1}, /*60*/ { 2, 0},{-1, 0},{-1, 0},{-1, 0},{-1, 0},{-1, 0},{-1, 0},{-1, 0}, /*68*/ { 0, 3},{ 0, 3},{ 0, 2},{ 0, 8},{ 0,-1},{ 0,-1},{-1,-1},{-1,-1}, /*70*/ { 1, 0},{ 1, 0},{ 1, 0},{-1, 0},{-1, 0},{-1, 0},{-1, 0},{-1, 0}, /*78*/ { 0,-1},{ 0,-1},{-1,-1},{-1,-1},{-1,-1},{ 5, 1},{ 4, 1},{ 4, 1}, /*80*/ { 4, 0},{-1, 0},{ 0, 0},{-1, 0},{-1, 0},{-1, 0},{-1, 0},{-1, 0}, /*88*/ { 0, 5},{-1,-1},{-1,-1},{-1,-1},{-1,-1},{-1,-1},{-1,-1},{-1,-1}, /*90*/ { 1, 0},{ 2, 0},{-1, 0},{-1, 0},{-1, 0},{-1, 0},{-1, 0},{-1, 0}, /*98*/ { 0, 1},{ 0, 1},{-1,-1},{-1,-1},{-1,-1},{-1,-1},{-1,-1},{-1,-1}, /*a0*/ { 2, 0},{ 2, 0},{ 2, 0},{ 4, 0},{-1, 0},{ 0, 0},{-1, 0},{-1, 0}, /*a8*/ { 1, 1},{ 1, 0},{ 3, 0},{ 2, 0},{-1,-1},{-1,-1},{-1,-1},{-1,-1}, /*b0*/ {-1, 0},{-1, 0},{-1, 0},{-1, 0},{-1, 0},{-1, 0},{-1, 0},{-1, 0}, /*b8*/ {-1,-1},{-1,-1},{-1,-1},{-1,-1},{-1,-1},{-1,-1},{-1,-1},{-1,-1}, /*c0*/ {-1, 0},{-1, 0},{-1, 0},{-1, 0},{-1, 0},{-1, 0},{-1, 0},{-1, 0}, /*c8*/ {-1,-1},{-1,-1},{-1,-1},{-1,-1},{-1,-1},{-1,-1},{-1,-1},{-1,-1}, /*d0*/ { 0, 0},{-1, 0},{-1, 0},{-1, 0},{-1, 0},{-1, 0},{-1, 0},{-1, 0}, /*d8*/ { 1, 1},{ 1, 1},{-1,-1},{-1,-1},{ 0, 1},{ 0,-1},{-1,-1},{-1,-1}, /*e0*/ {-1, 0},{ 4, 0},{ 0, 1},{-1, 0},{-1, 0},{ 4, 0},{-1, 0},{-1, 0}, /*e8*/ { 3,-1},{-1,-1},{ 0, 1},{-1,-1},{ 0,-1},{-1,-1},{-1,-1},{ 0, 0}, /*f0*/ {-1, 0},{-1, 0},{-1, 0},{-1, 0},{-1, 0},{-1, 0},{-1, 0},{-1, 0}, /*f8*/ {-1,-1},{-1,-1},{-1,-1},{-1,-1},{-1,-1},{-1,-1},{-1,-1},{-1,-1}, }; /* * PMU commands */ #define PMU_POWER_CTRL0 0x10 /* control power of some devices */ #define PMU_POWER_CTRL 0x11 /* control power of some devices */ #define PMU_ADB_CMD 0x20 /* send ADB packet */ #define PMU_ADB_POLL_OFF 0x21 /* disable ADB auto-poll */ #define PMU_WRITE_NVRAM 0x33 /* write non-volatile RAM */ #define PMU_READ_NVRAM 0x3b /* read non-volatile RAM */ #define PMU_SET_RTC 0x30 /* set real-time clock */ #define PMU_READ_RTC 0x38 /* read real-time clock */ #define PMU_SET_VOLBUTTON 0x40 /* set volume up/down position */ #define PMU_BACKLIGHT_BRIGHT 0x41 /* set backlight brightness */ #define PMU_GET_VOLBUTTON 0x48 /* get volume up/down position */ #define PMU_PCEJECT 0x4c /* eject PC-card from slot */ #define PMU_BATTERY_STATE 0x6b /* report battery state etc. */ #define PMU_SMART_BATTERY_STATE 0x6f /* report battery state (new way) */ #define PMU_SET_INTR_MASK 0x70 /* set PMU interrupt mask */ #define PMU_INT_ACK 0x78 /* read interrupt bits */ #define PMU_SHUTDOWN 0x7e /* turn power off */ #define PMU_CPU_SPEED 0x7d /* control CPU speed on some models */ #define PMU_SLEEP 0x7f /* put CPU to sleep */ #define PMU_POWER_EVENTS 0x8f /* Send power-event commands to PMU */ #define PMU_I2C_CMD 0x9a /* I2C operations */ #define PMU_RESET 0xd0 /* reset CPU */ #define PMU_GET_BRIGHTBUTTON 0xd9 /* report brightness up/down pos */ #define PMU_GET_COVER 0xdc /* report cover open/closed */ #define PMU_SYSTEM_READY 0xdf /* tell PMU we are awake */ #define PMU_GET_VERSION 0xea /* read the PMU version */ /* Bits to use with the PMU_POWER_CTRL0 command */ #define PMU_POW0_ON 0x80 /* OR this to power ON the device */ #define PMU_POW0_OFF 0x00 /* leave bit 7 to 0 to power it OFF */ #define PMU_POW0_HARD_DRIVE 0x04 /* Hard drive power (on wallstreet/lombard ?) */ /* Bits to use with the PMU_POWER_CTRL command */ #define PMU_POW_ON 0x80 /* OR this to power ON the device */ #define PMU_POW_OFF 0x00 /* leave bit 7 to 0 to power it OFF */ #define PMU_POW_BACKLIGHT 0x01 /* backlight power */ #define PMU_POW_CHARGER 0x02 /* battery charger power */ #define PMU_POW_IRLED 0x04 /* IR led power (on wallstreet) */ #define PMU_POW_MEDIABAY 0x08 /* media bay power (wallstreet/lombard ?) */ /* Bits in PMU interrupt and interrupt mask bytes */ #define PMU_INT_PCEJECT 0x04 /* PC-card eject buttons */ #define PMU_INT_SNDBRT 0x08 /* sound/brightness up/down buttons */ #define PMU_INT_ADB 0x10 /* ADB autopoll or reply data */ #define PMU_INT_BATTERY 0x20 /* Battery state change */ #define PMU_INT_ENVIRONMENT 0x40 /* Environment interrupts */ #define PMU_INT_TICK 0x80 /* 1-second tick interrupt */ /* Other bits in PMU interrupt valid when PMU_INT_ADB is set */ #define PMU_INT_ADB_AUTO 0x04 /* ADB autopoll, when PMU_INT_ADB */ #define PMU_INT_WAITING_CHARGER 0x01 /* ??? */ #define PMU_INT_AUTO_SRQ_POLL 0x02 /* ??? */ /* Bits in the environement message (either obtained via PMU_GET_COVER, * or via PMU_INT_ENVIRONMENT on core99 */ #define PMU_ENV_LID_CLOSED 0x01 /* The lid is closed */ /* I2C related definitions */ #define PMU_I2C_MODE_SIMPLE 0 #define PMU_I2C_MODE_STDSUB 1 #define PMU_I2C_MODE_COMBINED 2 #define PMU_I2C_BUS_STATUS 0 #define PMU_I2C_BUS_SYSCLK 1 #define PMU_I2C_BUS_POWER 2 #define PMU_I2C_STATUS_OK 0 #define PMU_I2C_STATUS_DATAREAD 1 #define PMU_I2C_STATUS_BUSY 0xfe /* PMU PMU_POWER_EVENTS commands */ enum { PMU_PWR_GET_POWERUP_EVENTS = 0x00, PMU_PWR_SET_POWERUP_EVENTS = 0x01, PMU_PWR_CLR_POWERUP_EVENTS = 0x02, PMU_PWR_GET_WAKEUP_EVENTS = 0x03, PMU_PWR_SET_WAKEUP_EVENTS = 0x04, PMU_PWR_CLR_WAKEUP_EVENTS = 0x05, }; /* Power events wakeup bits */ enum { PMU_PWR_WAKEUP_KEY = 0x01, /* Wake on key press */ PMU_PWR_WAKEUP_AC_INSERT = 0x02, /* Wake on AC adapter plug */ PMU_PWR_WAKEUP_AC_CHANGE = 0x04, PMU_PWR_WAKEUP_LID_OPEN = 0x08, PMU_PWR_WAKEUP_RING = 0x10, }; static uint8_t pmu_readb(pmu_t *dev, int reg) { return *(volatile uint8_t *)(dev->base + reg); asm volatile("eieio" : : : "memory"); } static void pmu_writeb(pmu_t *dev, int reg, uint8_t val) { *(volatile uint8_t *)(dev->base + reg) = val; asm volatile("eieio" : : : "memory"); } static void pmu_handshake(pmu_t *dev) { pmu_writeb(dev, B, pmu_readb(dev, B) & ~TREQ); while ((pmu_readb(dev, B) & TACK) != 0); pmu_writeb(dev, B, pmu_readb(dev, B) | TREQ); while ((pmu_readb(dev, B) & TACK) == 0); } static void pmu_send_byte(pmu_t *dev, uint8_t val) { pmu_writeb(dev, ACR, pmu_readb(dev, ACR) | SR_OUT | SR_EXT); pmu_writeb(dev, SR, val); pmu_handshake(dev); } static uint8_t pmu_recv_byte(pmu_t *dev) { pmu_writeb(dev, ACR, (pmu_readb(dev, ACR) & ~SR_OUT) | SR_EXT); pmu_readb(dev, SR); pmu_handshake(dev); return pmu_readb(dev, SR); } int pmu_request(pmu_t *dev, uint8_t cmd, uint8_t in_len, uint8_t *in_data, uint8_t *out_len, uint8_t *out_data) { int i, l, out_sz; uint8_t d; /* Check command data size */ l = pmu_data_len[cmd][0]; if (l >= 0 && in_len != l) { printk("PMU: Error, request %02x wants %d args, got %d\n", cmd, l, in_len); return -1; } /* Make sure PMU is idle */ while ((pmu_readb(dev, B) & TACK) == 0); /* Send command */ pmu_send_byte(dev, cmd); /* Optionally send data length */ if (l < 0) { pmu_send_byte(dev, in_len); /* Send data */ } for (i = 0; i < in_len; i++) { pmu_send_byte(dev, in_data[i]); } /* Check response size */ l = pmu_data_len[cmd][1]; if (l < 0) { l = pmu_recv_byte(dev); } if (out_len) { out_sz = *out_len; *out_len = 0; } else { out_sz = 0; } if (l > out_sz) { printk("PMU: Error, request %02x returns %d bytes" ", room for %d\n", cmd, l, out_sz); } for (i = 0; i < l; i++) { d = pmu_recv_byte(dev); if (i < out_sz) { out_data[i] = d; (*out_len)++; } } return 0; } #define MAX_REQ_SIZE 128 #ifdef CONFIG_DRIVER_ADB static int pmu_adb_req(void *host, const uint8_t *snd_buf, int len, uint8_t *rcv_buf) { uint8_t buffer[MAX_REQ_SIZE], *pos, olen; int rc; PMU_DPRINTF("pmu_adb_req: len=%d: %02x %02x %02x...\n", len, snd_buf[0], snd_buf[1], snd_buf[2]); if (len >= (MAX_REQ_SIZE - 1)) { printk("pmu_adb_req: too big ! (%d)\n", len); return -1; } buffer[0] = snd_buf[0]; buffer[1] = 0; /* We don't do autopoll */ buffer[2] = len - 1; if (len > 1) { memcpy(&buffer[3], &snd_buf[1], len - 1); } rc = pmu_request(host, PMU_ADB_CMD, len + 2, buffer, NULL, NULL); if (rc) { printk("PMU adb request failure %d\n", rc); return 0; } olen = MAX_REQ_SIZE; rc = pmu_request(host, PMU_INT_ACK, 0, NULL, &olen, buffer); if (rc) { printk("PMU intack request failure %d\n", rc); return 0; } PMU_DPRINTF("pmu_resp=%d int=0x%02x\n", olen, buffer[0]); if (olen <= 2) { return 0; } else { pos = &buffer[3]; olen -= 3; PMU_DPRINTF("ADB resp: 0x%02x 0x%02x\n", buffer[3], buffer[4]); } memcpy(rcv_buf, pos, olen); return olen; } #endif DECLARE_UNNAMED_NODE(ob_pmu, 0, sizeof(int)); static pmu_t *main_pmu; static void pmu_reset_all(void) { pmu_request(main_pmu, PMU_RESET, 0, NULL, NULL, NULL); } static void pmu_poweroff(void) { uint8_t params[] = "MATT"; pmu_request(main_pmu, PMU_SHUTDOWN, 4, params, NULL, NULL); } static void ob_pmu_open(int *idx) { RET(-1); } static void ob_pmu_close(int *idx) { } NODE_METHODS(ob_pmu) = { { "open", ob_pmu_open }, { "close", ob_pmu_close }, }; DECLARE_UNNAMED_NODE(rtc, 0, sizeof(int)); static void rtc_open(int *idx) { RET(-1); } static void rtc_close(int *idx) { } /* * get-time ( -- second minute hour day month year ) * */ static const int days_month[12] = { 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 }; static const int days_month_leap[12] = { 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 }; static inline int is_leap(int year) { return ((year % 4 == 0) && (year % 100 != 0)) || (year % 400 == 0); } static void rtc_get_time(int *idx) { uint8_t obuf[4], olen; ucell second, minute, hour, day, month, year; uint32_t now; int current; const int *days; olen = 4; pmu_request(main_pmu, PMU_READ_RTC, 0, NULL, &olen, obuf); /* seconds since 01/01/1904 */ now = (obuf[0] << 24) + (obuf[1] << 16) + (obuf[2] << 8) + obuf[3]; second = now % 60; now /= 60; minute = now % 60; now /= 60; hour = now % 24; now /= 24; year = now * 100 / 36525; now -= year * 36525 / 100; year += 1904; days = is_leap(year) ? days_month_leap : days_month; current = 0; month = 0; while (month < 12) { if (now <= current + days[month]) { break; } current += days[month]; month++; } month++; day = now - current; PUSH(second); PUSH(minute); PUSH(hour); PUSH(day); PUSH(month); PUSH(year); } /* * set-time ( second minute hour day month year -- ) * */ static void rtc_set_time(int *idx) { uint8_t ibuf[4]; ucell second, minute, hour, day, month, year; const int *days; uint32_t now; unsigned int nb_days; int i; year = POP(); month = POP(); day = POP(); hour = POP(); minute = POP(); second = POP(); days = is_leap(year) ? days_month_leap : days_month; nb_days = (year - 1904) * 36525 / 100 + day; for (i = 0; i < month - 1; i++) { nb_days += days[i]; } now = (((nb_days * 24) + hour) * 60 + minute) * 60 + second; ibuf[0] = now >> 24; ibuf[1] = now >> 16; ibuf[2] = now >> 8; ibuf[3] = now; pmu_request(main_pmu, PMU_SET_RTC, 4, ibuf, NULL, NULL); } NODE_METHODS(rtc) = { { "open", rtc_open }, { "close", rtc_close }, { "get-time", rtc_get_time }, { "set-time", rtc_set_time }, }; static void rtc_init(char *path) { phandle_t aliases; char buf[128]; push_str(path); fword("find-device"); fword("new-device"); push_str("rtc"); fword("device-name"); push_str("rtc"); fword("device-type"); push_str("rtc,via-pmu"); fword("encode-string"); push_str("compatible"); fword("property"); BIND_NODE_METHODS(get_cur_dev(), rtc); fword("finish-device"); aliases = find_dev("/aliases"); snprintf(buf, sizeof(buf), "%s/rtc", path); set_property(aliases, "rtc", buf, strlen(buf) + 1); } static void powermgt_init(char *path) { phandle_t ph; push_str(path); fword("find-device"); fword("new-device"); push_str("power-mgt"); fword("device-name"); push_str("power-mgt"); fword("device-type"); push_str("via-pmu-99"); fword("encode-string"); push_str("compatible"); fword("property"); push_str("extint-gpio1"); fword("encode-string"); push_str("registry-name"); fword("property"); /* This is a bunch of magic "Feature" bits for which we only have * partial definitions from Darwin. These are taken from a * PowerMac3,1 device-tree. They are also identical in a * PowerMac5,1 "Cube". Note that more recent machines such as * the MacMini (PowerMac10,1) do not have this property, however * MacOS 9 seems to require it (it hangs during boot otherwise). */ const char prim[] = { 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0x2c, 0x00, 0x03, 0x0d, 0x40, /* Public PM features */ /* 0x00000001 : Wake timer supported */ /* 0x00000004 : Processor cycling supported */ /* 0x00000100 : Can wake on modem ring */ /* 0x00000200 : Has monitor dimming support */ /* 0x00000400 : Can program startup timer */ /* 0x00002000 : Supports wake on LAN */ /* 0x00004000 : Can wake on LID/case open */ /* 0x00008000 : Can power off PCI on sleep */ /* 0x00010000 : Supports deep sleep */ 0x00, 0x01, 0xe7, 0x05, /* Private PM features */ /* 0x00000400 : Supports ICT control */ /* 0x00001000 : Supports Idle2 in hardware */ /* 0x00002000 : Open case prevents sleep */ 0x00, 0x00, 0x34, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* # of batteries supported */ 0x26, 0x0d, 0x46, 0x00, 0x02, 0x78, 0x78, 0x3c, 0x00 }; ph = get_cur_dev(); BIND_NODE_METHODS(ph, rtc); set_property(ph, "prim-info", prim, sizeof(prim)); fword("finish-device"); } pmu_t *pmu_init(const char *path, phys_addr_t base) { pmu_t *pmu; char buf[64]; phandle_t aliases; base += IO_PMU_OFFSET; PMU_DPRINTF(" base=" FMT_plx "\n", base); pmu = malloc(sizeof(pmu_t)); if (pmu == NULL) { return NULL; } fword("new-device"); push_str("via-pmu"); fword("device-name"); push_str("via-pmu"); fword("device-type"); push_str("pmu"); fword("encode-string"); push_str("compatible"); fword("property"); PUSH(1); fword("encode-int"); push_str("#address-cells"); fword("property"); PUSH(0); fword("encode-int"); push_str("#size-cells"); fword("property"); PUSH(IO_PMU_OFFSET); fword("encode-int"); PUSH(IO_PMU_SIZE); fword("encode-int"); fword("encode+"); push_str("reg"); fword("property"); /* On newworld machines the PMU is on interrupt 0x19 */ PUSH(0x19); fword("encode-int"); PUSH(1); fword("encode-int"); fword("encode+"); push_str("interrupts"); fword("property"); PUSH(0xd0330c); fword("encode-int"); push_str("pmu-version"); fword("property"); BIND_NODE_METHODS(get_cur_dev(), ob_pmu); aliases = find_dev("/aliases"); snprintf(buf, sizeof(buf), "%s/via-pmu", path); set_property(aliases, "via-pmu", buf, strlen(buf) + 1); pmu->base = base; #ifdef CONFIG_DRIVER_ADB if (has_adb()) { pmu->adb_bus = adb_bus_new(pmu, &pmu_adb_req); adb_bus_init(buf, pmu->adb_bus); } #endif rtc_init(buf); powermgt_init(buf); main_pmu = pmu; fword("finish-device"); bind_func("pmu-power-off", pmu_poweroff); feval("['] pmu-power-off to power-off"); bind_func("pmu-reset-all", pmu_reset_all); feval("['] pmu-reset-all to reset-all"); return pmu; }