The PMU hardware supercedes the CUDA hardware on more modern Macs providing additional power management functionality which is required for more modern Mac OSs.
Signed-off-by: Mark Cave-Ayland mark.cave-ayland@ilande.co.uk --- drivers/adb_bus.h | 5 + drivers/build.xml | 1 + drivers/macio.c | 6 +- drivers/pci.c | 6 + drivers/pmu.c | 625 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ drivers/pmu.h | 17 ++ 6 files changed, 658 insertions(+), 2 deletions(-) create mode 100644 drivers/pmu.c create mode 100644 drivers/pmu.h
diff --git a/drivers/adb_bus.h b/drivers/adb_bus.h index 205b375..5a3b8c1 100644 --- a/drivers/adb_bus.h +++ b/drivers/adb_bus.h @@ -17,6 +17,9 @@ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA, 02110-1301 USA */
+#ifndef __ADB_BUS_H__ +#define __ADB_BUS_H__ + typedef struct adb_bus_t adb_bus_t; typedef struct adb_dev_t adb_dev_t;
@@ -102,3 +105,5 @@ do { printk("ADB - %s: " fmt, __func__ , ##args); } while (0) #else #define ADB_DPRINTF(fmt, args...) do { } while (0) #endif + +#endif diff --git a/drivers/build.xml b/drivers/build.xml index de84e38..8df074b 100644 --- a/drivers/build.xml +++ b/drivers/build.xml @@ -10,6 +10,7 @@ <object source="adb_kbd.c" condition="DRIVER_ADB"/> <object source="adb_mouse.c" condition="DRIVER_ADB"/> <object source="cuda.c" condition="DRIVER_ADB"/> + <object source="pmu.c" condition="DRIVER_ADB"/> <object source="floppy.c" condition="DRIVER_FLOPPY"/> <object source="iommu.c" condition="DRIVER_SBUS"/> <object source="sbus.c" condition="DRIVER_SBUS"/> diff --git a/drivers/macio.c b/drivers/macio.c index ac2d8b6..be6f9e4 100644 --- a/drivers/macio.c +++ b/drivers/macio.c @@ -18,6 +18,7 @@ #include "drivers/drivers.h" #include "macio.h" #include "cuda.h" +#include "pmu.h" #include "escc.h" #include "drivers/pci.h"
@@ -378,10 +379,11 @@ ob_macio_keylargo_init(const char *path, phys_addr_t addr) aliases = find_dev("/aliases"); set_property(aliases, "mac-io", path, strlen(path) + 1);
- cuda_init(path, addr); - if (has_pmu()) { macio_gpio_init(path); + pmu_init(path, addr); + } else { + cuda_init(path, addr); }
/* The NewWorld NVRAM is not located in the MacIO device */ diff --git a/drivers/pci.c b/drivers/pci.c index f9884ba..e674cfb 100644 --- a/drivers/pci.c +++ b/drivers/pci.c @@ -1894,6 +1894,12 @@ static phandle_t ob_pci_host_set_interrupt_map(phandle_t host) target_node = find_dev(buf); set_int_property(target_node, "interrupt-parent", dnode);
+ snprintf(buf, sizeof(buf), "%s/mac-io/via-pmu", path); + target_node = find_dev(buf); + if (target_node) { + set_int_property(target_node, "interrupt-parent", dnode); + } + snprintf(buf, sizeof(buf), "%s/mac-io/gpio/extint-gpio1", path); target_node = find_dev(buf); if (target_node) { diff --git a/drivers/pmu.c b/drivers/pmu.c new file mode 100644 index 0000000..6fc5363 --- /dev/null +++ b/drivers/pmu.c @@ -0,0 +1,625 @@ +/* + * 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, INSTALL_OPEN, 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_initialize (int *idx) +{ + phandle_t ph=get_cur_dev(); + int props[2]; + + push_str("via-pmu"); + fword("device-type"); + + set_int_property(ph, "#address-cells", 1); + set_int_property(ph, "#size-cells", 0); + set_property(ph, "compatible", "pmu", 4); + + props[0] = __cpu_to_be32(IO_PMU_OFFSET); + props[1] = __cpu_to_be32(IO_PMU_SIZE); + set_property(ph, "reg", (char *)&props, sizeof(props)); + + /* On newworld machines the PMU is on interrupt 0x19 */ + props[0] = 0x19; + props[1] = 1; + set_property(ph, "interrupts", (char *)props, sizeof(props)); + set_int_property(ph, "pmu-version", 0xd0330c); + + bind_func("pmu-reset-all", pmu_reset_all); + feval("['] pmu-reset-all to reset-all"); +} + +static void ob_pmu_open(int *idx) +{ + RET(-1); +} + +static void ob_pmu_close(int *idx) +{ +} + +NODE_METHODS(ob_pmu) = { + { NULL, ob_pmu_initialize }, + { "open", ob_pmu_open }, + { "close", ob_pmu_close }, +}; + +DECLARE_UNNAMED_NODE(rtc, INSTALL_OPEN, sizeof(int)); + +static void rtc_open(int *idx) +{ + RET(-1); +} + +/* + * 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 }, + { "get-time", rtc_get_time }, + { "set-time", rtc_set_time }, +}; + +static void rtc_init(char *path) +{ + phandle_t ph, aliases; + char buf[64]; + + snprintf(buf, sizeof(buf), "%s/rtc", path); + REGISTER_NAMED_NODE(rtc, buf); + + ph = find_dev(buf); + set_property(ph, "device_type", "rtc", 4); + set_property(ph, "compatible", "rtc,via-pmu", 12); + + aliases = find_dev("/aliases"); + set_property(aliases, "rtc", buf, strlen(buf) + 1); + device_end(); +} + +static void powermgt_init(char *path) +{ + phandle_t ph; + char buf[64]; + + /* 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 }; + + snprintf(buf, sizeof(buf), "%s/power-mgt", path); + REGISTER_NAMED_NODE(rtc, buf); // XXX ? + + ph = find_dev(buf); + set_property(ph, "device_type", "power-mgt", 10); + set_property(ph, "compatible", "via-pmu-99", 11); + set_property(ph, "registry-name", "extint-gpio1", 13); + set_property(ph, "prim-info", prim, sizeof(prim)); + device_end(); +} + +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; + } + + snprintf(buf, sizeof(buf), "%s/via-pmu", path); + REGISTER_NAMED_NODE(ob_pmu, buf); + + aliases = find_dev("/aliases"); + 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; + + device_end(); + bind_func("poweroff", pmu_poweroff); + + return pmu; +} diff --git a/drivers/pmu.h b/drivers/pmu.h new file mode 100644 index 0000000..4a736ed --- /dev/null +++ b/drivers/pmu.h @@ -0,0 +1,17 @@ +#ifndef __PMU_H__ +#define __PMU_H__ + +#include "adb_bus.h" + +typedef struct pmu_t { + phys_addr_t base; + adb_bus_t *adb_bus; +} pmu_t; + +pmu_t *pmu_init (const char *path, phys_addr_t base); + +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); + +#endif /* __PMU_H__ */