patch-2.4.5 linux/arch/ppc/kernel/feature.c
Next file: linux/arch/ppc/kernel/find_name.c
Previous file: linux/arch/ppc/kernel/error_log.h
Back to the patch index
Back to the overall index
- Lines: 888
- Date:
Fri May 25 12:41:46 2001
- Orig file:
v2.4.4/linux/arch/ppc/kernel/feature.c
- Orig date:
Mon Jan 22 15:41:15 2001
diff -u --recursive --new-file v2.4.4/linux/arch/ppc/kernel/feature.c linux/arch/ppc/kernel/feature.c
@@ -1,18 +1,17 @@
/*
+ * BK Id: SCCS/s.feature.c 1.10 05/17/01 18:14:21 cort
+ */
+/*
* arch/ppc/kernel/feature.c
*
* Copyright (C) 1996 Paul Mackerras (paulus@cs.anu.edu.au)
- * Ben. Herrenschmidt (bh40@calva.net)
+ * Ben. Herrenschmidt (benh@kernel.crashing.org)
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version
* 2 of the License, or (at your option) any later version.
*
- * BenH: Changed implementation to work on multiple registers
- * polarity is also taken into account. Removed delay (now
- * responsibility of the caller). Added spinlocks.
- *
*/
#include <linux/config.h>
#include <linux/types.h>
@@ -29,11 +28,12 @@
#include <asm/io.h>
#include <asm/prom.h>
#include <asm/feature.h>
+#include <asm/dbdma.h>
+#include <asm/machdep.h>
#undef DEBUG_FEATURE
#define MAX_FEATURE_CONTROLLERS 2
-#define MAX_FEATURE_OFFSET 0x100
#define FREG(c,r) (&(((c)->reg)[(r)>>2]))
/* Keylargo reg. access. */
@@ -56,9 +56,7 @@
unsigned int mask; /* bit mask */
} fbit;
-/* I don't have an OHare machine to test with, so I left those as they
- * were. Someone with such a machine chould check out what OF says and
- * try too see if they match the heathrow ones and should be changed too
+/* Those features concern for OHare-based PowerBooks (2400, 3400, 3500)
*/
static fbit feature_bits_ohare_pbook[] = {
{0x38,0,0}, /* FEATURE_null */
@@ -159,11 +157,12 @@
/*
* Those bits are from a 1999 G3 PowerBook, with a paddington chip.
- * Mostly the same as the heathrow.
+ * Mostly the same as the heathrow. They are used on both PowerBooks
+ * and desktop machines using the paddington chip
*/
static fbit feature_bits_paddington[] = {
{0x38,0,0}, /* FEATURE_null */
- {0x38,0,0}, /* FEATURE_Serial_reset */
+ {0x38,0,PADD_RESET_SCC}, /* FEATURE_Serial_reset */
{0x38,0,HRW_SCC_ENABLE}, /* FEATURE_Serial_enable */
{0x38,0,HRW_SCCA_IO}, /* FEATURE_Serial_IO_A */
{0x38,0,HRW_SCCB_IO}, /* FEATURE_Serial_IO_B */
@@ -192,6 +191,7 @@
};
/* Those bits are for Core99 machines (iBook,G4,iMacSL/DV,Pismo,...).
+ * Note: Different sets may be needed for iBook, especially for sound
*/
static fbit feature_bits_keylargo[] = {
{0x38,0,0}, /* FEATURE_null */
@@ -201,13 +201,13 @@
{0x38,0,KL0_SCC_B_INTF_ENABLE}, /* FEATURE_Serial_IO_B */
{0x38,0,0}, /* FEATURE_SWIM3_enable */
{0x38,0,0}, /* FEATURE_MESH_enable */
- {0x3c,0,0}, /* FEATURE_IDE0_enable */
+ {0x3c,0,KL1_EIDE0_ENABLE}, /* FEATURE_IDE0_enable */
{0x3c,1,KL1_EIDE0_RESET_N}, /* FEATURE_IDE0_reset */
{0x38,0,0}, /* FEATURE_IOBUS_enable */
- {0x34,1,0x00000200}, /* FEATURE_Mediabay_reset */
- {0x34,1,0x00000400}, /* FEATURE_Mediabay_power */
+ {0x34,1,KL_MBCR_MB0_DEV_RESET}, /* FEATURE_Mediabay_reset */
+ {0x34,1,KL_MBCR_MB0_DEV_POWER}, /* FEATURE_Mediabay_power */
{0x38,0,0}, /* FEATURE_Mediabay_PCI_enable */
- {0x3c,0,0x0}, /* FEATURE_IDE1_enable */
+ {0x3c,0,KL1_EIDE1_ENABLE}, /* FEATURE_IDE1_enable */
{0x3c,1,KL1_EIDE1_RESET_N}, /* FEATURE_IDE1_reset */
{0x38,0,0}, /* FEATURE_Mediabay_floppy_enable */
{0x38,0,0}, /* FEATURE_BMac_reset */
@@ -216,10 +216,10 @@
{0x38,0,0}, /* FEATURE_Slow_SCC_PCLK */
{0x38,0,0}, /* FEATURE_Sound_Power */
{0x38,0,0}, /* FEATURE_Sound_CLK_Enable */
- {0x38,0,0}, /* FEATURE_IDE2_enable */
+ {0x3c,0,KL1_UIDE_ENABLE}, /* FEATURE_IDE2_enable */
{0x3c,1,KL1_UIDE_RESET_N}, /* FEATURE_IDE2_reset */
- {0x34,0,KL_MBCR_MBDEV_ENABLE}, /* FEATURE_Mediabay_IDE_switch */
- {0x34,0,0x00000100}, /* FEATURE_Mediabay_content */
+ {0x34,0,KL_MBCR_MB0_DEV_ENABLE},/* FEATURE_Mediabay_IDE_switch */
+ {0x34,0,KL_MBCR_MB0_ENABLE}, /* FEATURE_Mediabay_content */
{0x40,1,KL2_AIRPORT_RESET_N}, /* FEATURE_Airport_reset */
};
@@ -238,6 +238,8 @@
static struct feature_controller*
feature_lookup_controller(struct device_node *device);
+static void uninorth_init(void);
+static void keylargo_init(void);
#ifdef CONFIG_PMAC_PBOOK
static void heathrow_prepare_for_sleep(struct feature_controller* ctrler);
static void heathrow_wakeup(struct feature_controller* ctrler);
@@ -245,32 +247,80 @@
static void core99_wake_up(struct feature_controller* ctrler);
#endif /* CONFIG_PMAC_PBOOK */
-static void keylargo_init(void);
-static void uninorth_init(void);
-
/* static variables */
static struct feature_controller controllers[MAX_FEATURE_CONTROLLERS];
static int controller_count = 0;
/* Core99 stuffs */
-static volatile u32* uninorth_base = NULL;
-static volatile u32* keylargo_base = NULL;
+/*static*/ volatile u32* uninorth_base;
+static volatile u32* keylargo_base;
+static struct feature_controller* keylargo;
static int uninorth_rev;
static int keylargo_rev;
+static u32 board_features;
+static u8 airport_pwr_regs[5];
+static int airport_pwr_state;
+static struct device_node* airport_dev;
+
+#define FTR_NEED_OPENPIC_TWEAK 0x00000001
+#define FTR_CAN_NAP 0x00000002
+#define FTR_HAS_FW_POWER 0x00000004
+#define FTR_CAN_SLEEP 0x00000008
+
+static struct board_features_t {
+ char* compatible;
+ u32 features;
+} board_features_datas[] __initdata =
+{
+ { "AAPL,PowerMac G3", 0 }, /* Beige G3 */
+ { "iMac,1", 0 }, /* First iMac (gossamer) */
+ { "PowerMac1,1", 0 }, /* B&W G3 / Yikes */
+ { "PowerMac1,2", 0 }, /* B&W G3 / Yikes */
+ { "PowerMac2,1", FTR_CAN_SLEEP }, /* r128 based iMac */
+ { "PowerMac2,2", FTR_HAS_FW_POWER|FTR_CAN_SLEEP }, /* Summer 2000 iMac */
+ { "PowerMac4,1", FTR_CAN_SLEEP }, /* iMac "Flower Power" */
+ { "PowerMac3,1", FTR_NEED_OPENPIC_TWEAK }, /* Sawtooth (G4) */
+ { "PowerMac3,2", 0 }, /* G4/Dual G4 */
+ { "PowerMac3,3", FTR_NEED_OPENPIC_TWEAK }, /* G4/Dual G4 */
+ { "PowerMac5,1", 0 }, /* Cube */
+ { "AAPL,3400/2400", FTR_CAN_SLEEP }, /* 2400/3400 PowerBook */
+ { "AAPL,3500", FTR_CAN_SLEEP }, /* 3500 PowerBook (G3) */
+ { "AAPL,PowerBook1998", FTR_CAN_SLEEP }, /* Wallstreet PowerBook */
+ { "PowerBook1,1", FTR_CAN_SLEEP }, /* 101 (Lombard) PowerBook */
+ { "PowerBook2,1", FTR_CAN_SLEEP }, /* iBook */
+ { "PowerBook2,2", FTR_CAN_SLEEP /*| FTR_CAN_NAP*/ }, /* iBook FireWire */
+ { "PowerBook3,1", FTR_CAN_SLEEP|FTR_CAN_NAP| /* PowerBook 2000 (Pismo) */
+ FTR_HAS_FW_POWER },
+ { "PowerBook3,2", FTR_CAN_NAP|FTR_CAN_SLEEP }, /* PowerBook Titanium */
+ { "PowerBook4,1", FTR_CAN_NAP|FTR_CAN_SLEEP }, /* New polycarbonate iBook */
+ { NULL, 0 }
+};
+
+extern unsigned long powersave_nap;
-/*
- * WARNING ! This function is called early in setup_arch, neither the IO base
- * nor the udelay calibration have been done yet
- */
void
feature_init(void)
{
struct device_node *np;
u32 *rev;
-
+ int i;
+
if (_machine != _MACH_Pmac)
return;
+ /* Figure out motherboard type & options */
+ for(i=0;board_features_datas[i].compatible;i++) {
+ if (machine_is_compatible(board_features_datas[i].compatible)) {
+ board_features = board_features_datas[i].features;
+ break;
+ }
+ }
+
+ /* Set default value of powersave_nap on machines that support it */
+ if (board_features & FTR_CAN_NAP)
+ powersave_nap = 1;
+
+ /* Track those poor mac-io's */
np = find_devices("mac-io");
while (np != NULL) {
/* KeyLargo contains several (5 ?) FCR registers in mac-io,
@@ -280,13 +330,25 @@
struct feature_controller* ctrler =
feature_add_controller(np, feature_bits_keylargo);
if (ctrler) {
+ keylargo = ctrler;
keylargo_base = ctrler->reg;
rev = (u32 *)get_property(ctrler->device, "revision-id", NULL);
if (rev)
keylargo_rev = *rev;
+
+ rev = (u32 *)get_property(ctrler->device, "device-id", NULL);
+ if (rev && (*rev) == 0x0025)
+ keylargo_rev |= KL_PANGEA_REV;
}
} else if (device_is_compatible(np, "paddington")) {
feature_add_controller(np, feature_bits_paddington);
+ /* We disable the modem power bit on Yikes as it can
+ * do bad things (it's the nvram power)
+ */
+ if (machine_is_compatible("PowerMac1,1") ||
+ machine_is_compatible("PowerMac1,2")) {
+ feature_bits_paddington[FEATURE_Modem_power].mask = 0;
+ }
} else if (machine_is_compatible("AAPL,PowerBook1998")) {
feature_add_controller(np, feature_bits_wallstreet);
} else {
@@ -311,11 +373,12 @@
}
}
- /* Handle core99 Uni-N */
+ /* Locate core99 Uni-N */
np = find_devices("uni-n");
if (np && np->n_addrs > 0) {
uninorth_base = ioremap(np->addrs[0].address, 0x1000);
uninorth_rev = in_be32(UN_REG(UNI_N_VERSION));
+ printk("Uninorth at 0x%08x\n", np->addrs[0].address);
}
if (uninorth_base && keylargo_base)
printk("Uni-N revision: %d, KeyLargo revision: %d\n",
@@ -360,8 +423,12 @@
return NULL;
}
+ /* We remap the entire mac-io here. Normally, this will just
+ * give us back our already existing BAT mapping
+ */
controller->reg = (volatile u32 *)ioremap(
- controller_device->addrs[0].address, MAX_FEATURE_OFFSET);
+ controller_device->addrs[0].address,
+ controller_device->addrs[0].size);
if (bits == NULL) {
printk(KERN_INFO "Twiddling the magic ohare bits\n");
@@ -495,6 +562,12 @@
return bit->polarity ? (value == 0) : (value == bit->mask);
}
+int
+feature_can_sleep(void)
+{
+ return ((board_features & FTR_CAN_SLEEP) != 0);
+}
+
/*
* Core99 functions
*
@@ -506,53 +579,220 @@
void
feature_set_gmac_power(struct device_node* device, int power)
{
- if (!uninorth_base)
+ unsigned long flags;
+
+ if (!uninorth_base || !keylargo)
return;
+
+ /* TODO: Handle save/restore of PCI config space here
+ */
+
+ spin_lock_irqsave(&keylargo->lock, flags);
if (power)
UN_BIS(UNI_N_CLOCK_CNTL, UNI_N_CLOCK_CNTL_GMAC);
else
UN_BIC(UNI_N_CLOCK_CNTL, UNI_N_CLOCK_CNTL_GMAC);
+ spin_unlock_irqrestore(&keylargo->lock, flags);
udelay(20);
}
void
-feature_set_gmac_phy_reset(struct device_node* device, int reset)
+feature_gmac_phy_reset(struct device_node* device)
{
- if (!keylargo_base)
+ unsigned long flags;
+
+ if (!keylargo_base || !keylargo)
return;
- out_8((volatile u8 *)KL_FCR(KL_GPIO_ETH_PHY_RESET), reset);
+
+ spin_lock_irqsave(&keylargo->lock, flags);
+ out_8((volatile u8 *)KL_FCR(KL_GPIO_ETH_PHY_RESET),
+ KEYLARGO_GPIO_OUTPUT_ENABLE);
(void)in_8((volatile u8 *)KL_FCR(KL_GPIO_ETH_PHY_RESET));
+ spin_unlock_irqrestore(&keylargo->lock, flags);
+ mdelay(10);
+ spin_lock_irqsave(&keylargo->lock, flags);
+ out_8((volatile u8 *)KL_FCR(KL_GPIO_ETH_PHY_RESET),
+ KEYLARGO_GPIO_OUTPUT_ENABLE | KEYLARGO_GPIO_OUTOUT_DATA);
+ (void)in_8((volatile u8 *)KL_FCR(KL_GPIO_ETH_PHY_RESET));
+ spin_unlock_irqrestore(&keylargo->lock, flags);
+ mdelay(10);
}
/* Pass the node of the correct controller, please */
void
feature_set_usb_power(struct device_node* device, int power)
{
+ char* prop;
+ int number;
+ u32 reg;
+
+ unsigned long flags;
+
+ if (!keylargo_base || !keylargo)
+ return;
+
+ prop = (char *)get_property(device, "AAPL,clock-id", NULL);
+ if (!prop)
+ return;
+ if (strncmp(prop, "usb0u048", strlen("usb0u048")) == 0)
+ number = 0;
+ else if (strncmp(prop, "usb1u148", strlen("usb1u148")) == 0)
+ number = 2;
+ else
+ return;
+
+ spin_lock_irqsave(&keylargo->lock, flags);
+ if (power) {
+ /* Turn ON */
+
+ if (number == 0) {
+ KL_BIC(KEYLARGO_FCR0, (KL0_USB0_PAD_SUSPEND0 | KL0_USB0_PAD_SUSPEND1));
+ mdelay(1);
+ KL_BIS(KEYLARGO_FCR0, KL0_USB0_CELL_ENABLE);
+ } else {
+ KL_BIC(KEYLARGO_FCR0, (KL0_USB1_PAD_SUSPEND0 | KL0_USB1_PAD_SUSPEND1));
+ mdelay(1);
+ KL_BIS(KEYLARGO_FCR0, KL0_USB1_CELL_ENABLE);
+ }
+ reg = KL_IN(KEYLARGO_FCR4);
+ reg &= ~(KL4_SET_PORT_ENABLE(number) | KL4_SET_PORT_RESUME(number) |
+ KL4_SET_PORT_CONNECT(number) | KL4_SET_PORT_DISCONNECT(number));
+ reg &= ~(KL4_SET_PORT_ENABLE(number+1) | KL4_SET_PORT_RESUME(number+1) |
+ KL4_SET_PORT_CONNECT(number+1) | KL4_SET_PORT_DISCONNECT(number+1));
+ KL_OUT(KEYLARGO_FCR4, reg);
+ (void)KL_IN(KEYLARGO_FCR4);
+ udelay(10);
+ } else {
+ /* Turn OFF */
+
+ reg = KL_IN(KEYLARGO_FCR4);
+ reg |= KL4_SET_PORT_ENABLE(number) | KL4_SET_PORT_RESUME(number) |
+ KL4_SET_PORT_CONNECT(number) | KL4_SET_PORT_DISCONNECT(number);
+ reg |= KL4_SET_PORT_ENABLE(number+1) | KL4_SET_PORT_RESUME(number+1) |
+ KL4_SET_PORT_CONNECT(number+1) | KL4_SET_PORT_DISCONNECT(number+1);
+ KL_OUT(KEYLARGO_FCR4, reg);
+ (void)KL_IN(KEYLARGO_FCR4);
+ udelay(1);
+ if (number == 0) {
+ KL_BIC(KEYLARGO_FCR0, KL0_USB0_CELL_ENABLE);
+ (void)KL_IN(KEYLARGO_FCR0);
+ udelay(1);
+ KL_BIS(KEYLARGO_FCR0, (KL0_USB0_PAD_SUSPEND0 | KL0_USB0_PAD_SUSPEND1));
+ (void)KL_IN(KEYLARGO_FCR0);
+ } else {
+ KL_BIC(KEYLARGO_FCR0, KL0_USB1_CELL_ENABLE);
+ (void)KL_IN(KEYLARGO_FCR0);
+ udelay(1);
+ KL_BIS(KEYLARGO_FCR0, (KL0_USB1_PAD_SUSPEND0 | KL0_USB1_PAD_SUSPEND1));
+ (void)KL_IN(KEYLARGO_FCR0);
+ }
+ udelay(1);
+ }
+ spin_unlock_irqrestore(&keylargo->lock, flags);
}
-/* Not yet implemented */
void
feature_set_firewire_power(struct device_node* device, int power)
{
+ unsigned long flags;
+
+ /* TODO: Handle save/restore of PCI config space here
+ */
+
if (!uninorth_base)
return;
+ spin_lock_irqsave(&keylargo->lock, flags);
if (power)
UN_BIS(UNI_N_CLOCK_CNTL, UNI_N_CLOCK_CNTL_FW);
else
UN_BIC(UNI_N_CLOCK_CNTL, UNI_N_CLOCK_CNTL_FW);
udelay(20);
+ spin_unlock_irqrestore(&keylargo->lock, flags);
+}
+
+/* Warning: will kill the PHY.. */
+void
+feature_set_firewire_cable_power(struct device_node* device, int power)
+{
+ unsigned long flags;
+ u8 gpioValue = power ? 0 : 4;
+
+ if (!keylargo_base || !(board_features & FTR_HAS_FW_POWER))
+ return;
+ spin_lock_irqsave(&keylargo->lock, flags);
+ out_8((volatile u8 *)KL_FCR(KL_GPIO_FW_CABLE_POWER), gpioValue);
+ (void)in_8((volatile u8 *)KL_FCR(KL_GPIO_FW_CABLE_POWER));
+ spin_unlock_irqrestore(&keylargo->lock, flags);
}
#ifdef CONFIG_SMP
void
-feature_core99_kick_cpu1(void)
+feature_core99_kick_cpu(int cpu_nr)
{
- out_8((volatile u8 *)KL_FCR(KL_GPIO_KICK_CPU1), KL_GPIO_KICK_CPU1_UP);
+#if 1 /* New way */
+ const int reset_lines[] = { KL_GPIO_RESET_CPU0,
+ KL_GPIO_RESET_CPU1,
+ KL_GPIO_RESET_CPU2,
+ KL_GPIO_RESET_CPU3 };
+ volatile u8* reset_io;
+
+ if (!keylargo_base || cpu_nr > 3)
+ return;
+ reset_io = (volatile u8*)KL_FCR(reset_lines[cpu_nr]);
+ out_8(reset_io, KEYLARGO_GPIO_OUTPUT_ENABLE);
+ udelay(1);
+ out_8(reset_io, KEYLARGO_GPIO_OUTPUT_ENABLE | KEYLARGO_GPIO_OUTOUT_DATA);
+#else
+ out_8((volatile u8 *)KL_FCR(KL_GPIO_EXTINT_CPU1), KL_GPIO_EXTINT_CPU1_ASSERT);
udelay(1);
- out_8((volatile u8 *)KL_FCR(KL_GPIO_KICK_CPU1), KL_GPIO_KICK_CPU1_DOWN);
+ out_8((volatile u8 *)KL_FCR(KL_GPIO_EXTINT_CPU1), KL_GPIO_EXTINT_CPU1_RELEASE);
+#endif
}
#endif /* CONFIG_SMP */
+void
+feature_set_airport_power(struct device_node* device, int power)
+{
+ if (!keylargo_base || !airport_dev || airport_dev != device)
+ return;
+ if (airport_pwr_state == power)
+ return;
+ if (power) {
+ /* Some if this is from Darwin code, some is from tracing of
+ * MacOS driver with Macsbug. Some real bit definitions would
+ * really help here...
+ */
+ out_8((volatile u8*)KL_FCR(KL_GPIO_AIRPORT_0), airport_pwr_regs[0]);
+ out_8((volatile u8*)KL_FCR(KL_GPIO_AIRPORT_1), airport_pwr_regs[1]);
+ out_8((volatile u8*)KL_FCR(KL_GPIO_AIRPORT_2), airport_pwr_regs[2]);
+ out_8((volatile u8*)KL_FCR(KL_GPIO_AIRPORT_3), airport_pwr_regs[3]);
+ out_8((volatile u8*)KL_FCR(KL_GPIO_AIRPORT_4), airport_pwr_regs[4]);
+ udelay(20);
+ out_8((volatile u8*)KL_FCR(KL_GPIO_AIRPORT_4), 5);
+ udelay(20);
+ out_8((volatile u8*)KL_FCR(KL_GPIO_AIRPORT_4), 4);
+ mdelay(20);
+ KL_BIC(KEYLARGO_FCR2, KL2_AIRPORT_RESET_N);
+ udelay(10);
+ out_8((volatile u8*)KL_FCR(0x1a3e0), 0x41);
+ udelay(10);
+ KL_BIS(KEYLARGO_FCR2, KL2_AIRPORT_RESET_N);
+ udelay(10);
+ } else {
+ airport_pwr_regs[0] = in_8((volatile u8*)KL_FCR(KL_GPIO_AIRPORT_0));
+ airport_pwr_regs[1] = in_8((volatile u8*)KL_FCR(KL_GPIO_AIRPORT_1));
+ airport_pwr_regs[2] = in_8((volatile u8*)KL_FCR(KL_GPIO_AIRPORT_2));
+ airport_pwr_regs[3] = in_8((volatile u8*)KL_FCR(KL_GPIO_AIRPORT_3));
+ airport_pwr_regs[4] = in_8((volatile u8*)KL_FCR(KL_GPIO_AIRPORT_4));
+ out_8((volatile u8*)KL_FCR(KL_GPIO_AIRPORT_0), 0);
+ out_8((volatile u8*)KL_FCR(KL_GPIO_AIRPORT_1), 0);
+ out_8((volatile u8*)KL_FCR(KL_GPIO_AIRPORT_2), 0);
+ out_8((volatile u8*)KL_FCR(KL_GPIO_AIRPORT_3), 0);
+ out_8((volatile u8*)KL_FCR(KL_GPIO_AIRPORT_4), 0);
+ }
+ airport_pwr_state = power;
+}
+
/* Initialize the Core99 UniNorth host bridge and memory controller
*/
static void
@@ -563,51 +803,106 @@
/* Set the arbitrer QAck delay according to what Apple does
*/
- actrl = in_be32(UN_REG(UNI_N_ARB_CTRL)) & ~UNI_N_ARB_CTRL_QACK_DELAY_MASK;
- actrl |= ((uninorth_rev < 3) ? UNI_N_ARB_CTRL_QACK_DELAY105 : UNI_N_ARB_CTRL_QACK_DELAY)
- << UNI_N_ARB_CTRL_QACK_DELAY_SHIFT;
- UN_OUT(UNI_N_ARB_CTRL, actrl);
+ if (uninorth_rev < 0x10) {
+ actrl = UN_IN(UNI_N_ARB_CTRL) & ~UNI_N_ARB_CTRL_QACK_DELAY_MASK;
+ actrl |= ((uninorth_rev < 3) ? UNI_N_ARB_CTRL_QACK_DELAY105 :
+ UNI_N_ARB_CTRL_QACK_DELAY) << UNI_N_ARB_CTRL_QACK_DELAY_SHIFT;
+ UN_OUT(UNI_N_ARB_CTRL, actrl);
+ }
- /*
- * Turns OFF the gmac clock. The gmac driver will turn
- * it back ON when the interface is enabled. This save
- * power on portables.
- *
- * Note: We could also try to turn OFF the PHY. Since this
- * has to be done by both the gmac driver and this code,
- * I'll probably end-up moving some of this out of the
- * modular gmac driver into a non-modular stub containing
- * some basic PHY management and power management stuffs
+ /* Enable GMAC for now for PCI probing. It will be disabled
+ * later on after PCI probe
*/
gmac = find_devices("ethernet");
-
while(gmac) {
if (device_is_compatible(gmac, "gmac"))
break;
gmac = gmac->next;
}
if (gmac)
- feature_set_gmac_power(gmac, 0);
+ feature_set_gmac_power(gmac, 1);
- /* Kludge (enable FW before PCI probe) */
+ /* Enable FW before PCI probe. Will be disabled later on
+ */
fw = find_devices("firewire");
if (fw && device_is_compatible(fw, "pci106b,18"))
feature_set_firewire_power(fw, 1);
}
-/* Initialize the Core99 KeyLargo ASIC. Currently, we just make sure
- * OpenPIC is enabled
+/* Initialize the Core99 KeyLargo ASIC.
*/
static void
keylargo_init(void)
{
+ struct device_node* np;
+
KL_BIS(KEYLARGO_FCR2, KL2_MPIC_ENABLE);
+
+ /* Lookup for an airport card, and disable it if found
+ * to save power (will be re-enabled by driver if used)
+ */
+ np = find_devices("radio");
+ if (np && np->parent == keylargo->device)
+ airport_dev = np;
+
+ if (airport_dev) {
+ airport_pwr_state = 1;
+ feature_set_airport_power(airport_dev, 0);
+ }
+
}
#ifdef CONFIG_PMAC_PBOOK
+void
+feature_prepare_for_sleep(void)
+{
+ /* We assume gatwick is second */
+ struct feature_controller* ctrler = &controllers[0];
+
+ if (controller_count > 1 &&
+ device_is_compatible(ctrler->device, "gatwick"))
+ ctrler = &controllers[1];
+
+ if (ctrler->bits == feature_bits_heathrow ||
+ ctrler->bits == feature_bits_paddington) {
+ heathrow_prepare_for_sleep(ctrler);
+ return;
+ }
+ if (ctrler->bits == feature_bits_keylargo) {
+ core99_prepare_for_sleep(ctrler);
+ return;
+ }
+}
+
+
+void
+feature_wake_up(void)
+{
+ struct feature_controller* ctrler = &controllers[0];
+
+ if (controller_count > 1 &&
+ device_is_compatible(ctrler->device, "gatwick"))
+ ctrler = &controllers[1];
+
+ if (ctrler->bits == feature_bits_heathrow ||
+ ctrler->bits == feature_bits_paddington) {
+ heathrow_wakeup(ctrler);
+ return;
+ }
+ if (ctrler->bits == feature_bits_keylargo) {
+ core99_wake_up(ctrler);
+ return;
+ }
+}
static u32 save_fcr[5];
static u32 save_mbcr;
+static u32 save_gpio_levels[2];
+static u8 save_gpio_extint[KEYLARGO_GPIO_EXTINT_CNT];
+static u8 save_gpio_normal[KEYLARGO_GPIO_CNT];
+static u32 save_unin_clock_ctl;
+static struct dbdma_regs save_dbdma[13];
+
static void
heathrow_prepare_for_sleep(struct feature_controller* ctrler)
@@ -631,60 +926,226 @@
}
static void
-core99_prepare_for_sleep(struct feature_controller* ctrler)
+turn_off_keylargo(void)
{
- /* Not yet implemented */
+ u32 temp;
+
+ /* For now, suspending the USB ref cause the machine to die on
+ * wakeup -- BenH
+ */
+#if 0
+ mdelay(1);
+ KL_BIS(KEYLARGO_FCR0, KL0_USB_REF_SUSPEND);
+ (void)KL_IN(KEYLARGO_FCR0);
+ mdelay(1500);
+#endif
+
+ KL_BIC(KEYLARGO_FCR0, KL0_SCCA_ENABLE | KL0_SCCB_ENABLE |
+ KL0_SCC_CELL_ENABLE |
+ KL0_IRDA_ENABLE | KL0_IRDA_CLK32_ENABLE |
+ KL0_IRDA_CLK19_ENABLE);
+
+ (void)KL_IN(KEYLARGO_FCR0); udelay(10);
+ KL_BIS(KEYLARGO_MBCR, KL_MBCR_MB0_DEV_ENABLE);
+ (void)KL_IN(KEYLARGO_MBCR); udelay(10);
+
+ KL_BIC(KEYLARGO_FCR1,
+ KL1_AUDIO_SEL_22MCLK | KL1_AUDIO_CLK_ENABLE_BIT |
+ KL1_AUDIO_CLK_OUT_ENABLE | KL1_AUDIO_CELL_ENABLE |
+ KL1_I2S0_CELL_ENABLE | KL1_I2S0_CLK_ENABLE_BIT |
+ KL1_I2S0_ENABLE | KL1_I2S1_CELL_ENABLE |
+ KL1_I2S1_CLK_ENABLE_BIT | KL1_I2S1_ENABLE |
+ KL1_EIDE0_ENABLE | KL1_EIDE0_RESET_N |
+ KL1_EIDE1_ENABLE | KL1_EIDE1_RESET_N |
+ KL1_UIDE_ENABLE);
+ (void)KL_IN(KEYLARGO_FCR1); udelay(10);
+
+ KL_BIS(KEYLARGO_FCR2, KL2_MODEM_POWER_N);
+ udelay(10);
+ KL_BIC(KEYLARGO_FCR2, KL2_IOBUS_ENABLE);
+ udelay(10);
+ temp = KL_IN(KEYLARGO_FCR3);
+ if (keylargo_rev >= 2)
+ temp |= (KL3_SHUTDOWN_PLL2X | KL3_SHUTDOWN_PLL_TOTAL);
+
+ temp |= KL3_SHUTDOWN_PLLKW6 | KL3_SHUTDOWN_PLLKW4 |
+ KL3_SHUTDOWN_PLLKW35 | KL3_SHUTDOWN_PLLKW12;
+ temp &= ~(KL3_CLK66_ENABLE | KL3_CLK49_ENABLE | KL3_CLK45_ENABLE
+ | KL3_CLK31_ENABLE | KL3_TIMER_CLK18_ENABLE | KL3_I2S1_CLK18_ENABLE
+ | KL3_I2S0_CLK18_ENABLE | KL3_VIA_CLK16_ENABLE);
+ KL_OUT(KEYLARGO_FCR3, temp);
+ (void)KL_IN(KEYLARGO_FCR3); udelay(10);
}
static void
-core99_wake_up(struct feature_controller* ctrler)
+turn_off_pangea(void)
{
- /* Not yet implemented */
+ u32 temp;
+
+ KL_BIC(KEYLARGO_FCR0, KL0_SCCA_ENABLE | KL0_SCCB_ENABLE |
+ KL0_SCC_CELL_ENABLE |
+ KL0_USB0_CELL_ENABLE | KL0_USB1_CELL_ENABLE);
+
+ (void)KL_IN(KEYLARGO_FCR0); udelay(10);
+ KL_BIS(KEYLARGO_MBCR, KL_MBCR_MB0_DEV_ENABLE);
+ (void)KL_IN(KEYLARGO_MBCR); udelay(10);
+
+ KL_BIC(KEYLARGO_FCR1,
+ KL1_AUDIO_SEL_22MCLK | KL1_AUDIO_CLK_ENABLE_BIT |
+ KL1_AUDIO_CLK_OUT_ENABLE | KL1_AUDIO_CELL_ENABLE |
+ KL1_I2S0_CELL_ENABLE | KL1_I2S0_CLK_ENABLE_BIT |
+ KL1_I2S0_ENABLE | KL1_I2S1_CELL_ENABLE |
+ KL1_I2S1_CLK_ENABLE_BIT | KL1_I2S1_ENABLE |
+ KL1_UIDE_ENABLE);
+ (void)KL_IN(KEYLARGO_FCR1); udelay(10);
+
+ KL_BIS(KEYLARGO_FCR2, KL2_MODEM_POWER_N);
+ udelay(10);
+ temp = KL_IN(KEYLARGO_FCR3);
+ temp |= KL3_SHUTDOWN_PLLKW6 | KL3_SHUTDOWN_PLLKW4 |
+ KL3_SHUTDOWN_PLLKW35;
+ temp &= ~(KL3_CLK49_ENABLE | KL3_CLK45_ENABLE
+ | KL3_CLK31_ENABLE | KL3_TIMER_CLK18_ENABLE | KL3_I2S1_CLK18_ENABLE
+ | KL3_I2S0_CLK18_ENABLE | KL3_VIA_CLK16_ENABLE);
+ KL_OUT(KEYLARGO_FCR3, temp);
+ (void)KL_IN(KEYLARGO_FCR3); udelay(10);
}
-void
-feature_prepare_for_sleep(void)
+static void
+core99_prepare_for_sleep(struct feature_controller* ctrler)
{
- /* We assume gatwick is second */
- struct feature_controller* ctrler = &controllers[0];
+ int i;
+ u8* base8;
+
+ /*
+ * Save various bits of KeyLargo
+ */
- if (!ctrler)
- return;
- if (controller_count > 1 &&
- device_is_compatible(ctrler->device, "gatwick"))
- ctrler = &controllers[1];
+ /* We power off the wireless slot in case it was not done
+ * by the driver. We don't power it on automatically however
+ */
+ feature_set_airport_power(airport_dev, 0);
- if (ctrler->bits == feature_bits_heathrow ||
- ctrler->bits == feature_bits_paddington) {
- heathrow_prepare_for_sleep(ctrler);
- return;
+ /* We power off the FW cable. Should be done by the driver... */
+ feature_set_firewire_cable_power(NULL, 0);
+
+ /* Save the state of the various GPIOs */
+ save_gpio_levels[0] = KL_IN(KEYLARGO_GPIO_LEVELS0);
+ save_gpio_levels[1] = KL_IN(KEYLARGO_GPIO_LEVELS1);
+ base8 = (u8 *)KL_FCR(KEYLARGO_GPIO_EXTINT_0);
+ for (i=0; i<KEYLARGO_GPIO_EXTINT_CNT; i++)
+ save_gpio_extint[i] = in_8(base8+i);
+ base8 = (u8 *)KL_FCR(KEYLARGO_GPIO_0);
+ for (i=0; i<KEYLARGO_GPIO_CNT; i++)
+ save_gpio_normal[i] = in_8(base8+i);
+
+ /* Save the FCRs */
+ save_mbcr = KL_IN(KEYLARGO_MBCR);
+ save_fcr[0] = KL_IN(KEYLARGO_FCR0);
+ save_fcr[1] = KL_IN(KEYLARGO_FCR1);
+ save_fcr[2] = KL_IN(KEYLARGO_FCR2);
+ save_fcr[3] = KL_IN(KEYLARGO_FCR3);
+ save_fcr[4] = KL_IN(KEYLARGO_FCR4);
+
+ /* Save state & config of DBDMA channels */
+ for (i=0; i<13; i++) {
+ volatile struct dbdma_regs* chan = (volatile struct dbdma_regs*)
+ (keylargo_base + ((0x8000+i*0x100)>>2));
+ save_dbdma[i].cmdptr_hi = in_le32(&chan->cmdptr_hi);
+ save_dbdma[i].cmdptr = in_le32(&chan->cmdptr);
+ save_dbdma[i].intr_sel = in_le32(&chan->intr_sel);
+ save_dbdma[i].br_sel = in_le32(&chan->br_sel);
+ save_dbdma[i].wait_sel = in_le32(&chan->wait_sel);
}
- if (ctrler->bits == feature_bits_keylargo) {
- core99_prepare_for_sleep(ctrler);
- return;
+
+ /*
+ * Turn off as much as we can
+ */
+ if (keylargo_rev & KL_PANGEA_REV)
+ turn_off_pangea();
+ else
+ turn_off_keylargo();
+
+ /*
+ * Put the host bridge to sleep
+ */
+
+ save_unin_clock_ctl = UN_IN(UNI_N_CLOCK_CNTL);
+ UN_OUT(UNI_N_CLOCK_CNTL, save_unin_clock_ctl &
+ ~(UNI_N_CLOCK_CNTL_GMAC|UNI_N_CLOCK_CNTL_FW/*|UNI_N_CLOCK_CNTL_PCI*/));
+ udelay(100);
+ UN_OUT(UNI_N_HWINIT_STATE, UNI_N_HWINIT_STATE_SLEEPING);
+ UN_OUT(UNI_N_POWER_MGT, UNI_N_POWER_MGT_SLEEP);
+
+ /*
+ * FIXME: A bit of black magic with OpenPIC (don't ask me why)
+ */
+ if (board_features & FTR_NEED_OPENPIC_TWEAK) {
+ KL_BIS(0x506e0, 0x00400000);
+ KL_BIS(0x506e0, 0x80000000);
}
}
-void
-feature_wake_up(void)
+static void
+core99_wake_up(struct feature_controller* ctrler)
{
- struct feature_controller* ctrler = &controllers[0];
+ int i;
+ u8* base8;
- if (!ctrler)
- return;
- if (controller_count > 1 &&
- device_is_compatible(ctrler->device, "gatwick"))
- ctrler = &controllers[1];
+ /*
+ * Wakeup the host bridge
+ */
+ UN_OUT(UNI_N_POWER_MGT, UNI_N_POWER_MGT_NORMAL);
+ udelay(10);
+ UN_OUT(UNI_N_HWINIT_STATE, UNI_N_HWINIT_STATE_RUNNING);
+ udelay(10);
- if (ctrler->bits == feature_bits_heathrow ||
- ctrler->bits == feature_bits_paddington) {
- heathrow_wakeup(ctrler);
- return;
+ /*
+ * Restore KeyLargo
+ */
+
+ KL_OUT(KEYLARGO_MBCR, save_mbcr);
+ (void)KL_IN(KEYLARGO_MBCR); udelay(10);
+ KL_OUT(KEYLARGO_FCR0, save_fcr[0]);
+ (void)KL_IN(KEYLARGO_FCR0); udelay(10);
+ KL_OUT(KEYLARGO_FCR1, save_fcr[1]);
+ (void)KL_IN(KEYLARGO_FCR1); udelay(10);
+ KL_OUT(KEYLARGO_FCR2, save_fcr[2]);
+ (void)KL_IN(KEYLARGO_FCR2); udelay(10);
+ KL_OUT(KEYLARGO_FCR3, save_fcr[3]);
+ (void)KL_IN(KEYLARGO_FCR3); udelay(10);
+ KL_OUT(KEYLARGO_FCR4, save_fcr[4]);
+ (void)KL_IN(KEYLARGO_FCR4); udelay(10);
+
+ for (i=0; i<13; i++) {
+ volatile struct dbdma_regs* chan = (volatile struct dbdma_regs*)
+ (keylargo_base + ((0x8000+i*0x100)>>2));
+ out_le32(&chan->control, (ACTIVE|DEAD|WAKE|FLUSH|PAUSE|RUN)<<16);
+ while (in_le32(&chan->status) & ACTIVE)
+ mb();
+ out_le32(&chan->cmdptr_hi, save_dbdma[i].cmdptr_hi);
+ out_le32(&chan->cmdptr, save_dbdma[i].cmdptr);
+ out_le32(&chan->intr_sel, save_dbdma[i].intr_sel);
+ out_le32(&chan->br_sel, save_dbdma[i].br_sel);
+ out_le32(&chan->wait_sel, save_dbdma[i].wait_sel);
}
- if (ctrler->bits == feature_bits_keylargo) {
- core99_wake_up(ctrler);
- return;
+
+ KL_OUT(KEYLARGO_GPIO_LEVELS0, save_gpio_levels[0]);
+ KL_OUT(KEYLARGO_GPIO_LEVELS1, save_gpio_levels[1]);
+ base8 = (u8 *)KL_FCR(KEYLARGO_GPIO_EXTINT_0);
+ for (i=0; i<KEYLARGO_GPIO_EXTINT_CNT; i++)
+ out_8(base8+i, save_gpio_extint[i]);
+ base8 = (u8 *)KL_FCR(KEYLARGO_GPIO_0);
+ for (i=0; i<KEYLARGO_GPIO_CNT; i++)
+ out_8(base8+i, save_gpio_normal[i]);
+
+ /* FIXME more black magic with OpenPIC ... */
+ if (board_features & FTR_NEED_OPENPIC_TWEAK) {
+ KL_BIC(0x506e0, 0x00400000);
+ KL_BIC(0x506e0, 0x80000000);
}
-}
+ UN_OUT(UNI_N_CLOCK_CNTL, save_unin_clock_ctl);
+ udelay(100);
+}
#endif /* CONFIG_PMAC_PBOOK */
FUNET's LINUX-ADM group, linux-adm@nic.funet.fi
TCL-scripts by Sam Shen (who was at: slshen@lbl.gov)