[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