[OpenBIOS] [PATCH 4/5] ppc: add PMU driver
Mark Cave-Ayland
mark.cave-ayland at ilande.co.uk
Tue Jun 12 18:02:11 CEST 2018
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 at 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__ */
--
2.11.0
More information about the OpenBIOS
mailing list