682 lines
21 KiB
C
682 lines
21 KiB
C
/*
|
|
* 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;
|
|
}
|