patch-2.2.17 linux/arch/ppc/kernel/feature.c

Next file: linux/arch/ppc/kernel/gemini_setup.c
Previous file: linux/arch/ppc/kernel/chrp_time.c
Back to the patch index
Back to the overall index

diff -u --recursive --new-file v2.2.16/arch/ppc/kernel/feature.c linux/arch/ppc/kernel/feature.c
@@ -8,85 +8,182 @@
  *  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/types.h>
 #include <linux/init.h>
 #include <linux/delay.h>
 #include <linux/kernel.h>
 #include <linux/sched.h>
+#include <asm/spinlock.h>
 #include <asm/errno.h>
 #include <asm/ohare.h>
+#include <asm/heathrow.h>
 #include <asm/io.h>
 #include <asm/prom.h>
 #include <asm/feature.h>
 
-#define MAX_FEATURE_REGS		2
 #undef DEBUG_FEATURE
 
-static u32 feature_bits_pbook[] = {
-	0,			/* FEATURE_null */
-	OH_SCC_RESET,		/* FEATURE_Serial_reset */
-	OH_SCC_ENABLE,		/* FEATURE_Serial_enable */
-	OH_SCCA_IO,		/* FEATURE_Serial_IO_A */
-	OH_SCCB_IO,		/* FEATURE_Serial_IO_B */
-	OH_FLOPPY_ENABLE,	/* FEATURE_SWIM3_enable */
-	OH_MESH_ENABLE,		/* FEATURE_MESH_enable */
-	OH_IDE_ENABLE,		/* FEATURE_IDE_enable */
-	OH_VIA_ENABLE,		/* FEATURE_VIA_enable */
-	OH_IDECD_POWER,		/* FEATURE_CD_power */
-	OH_BAY_RESET,		/* FEATURE_Mediabay_reset */
-	OH_BAY_ENABLE,		/* FEATURE_Mediabay_enable */
-	OH_BAY_PCI_ENABLE,	/* FEATURE_Mediabay_PCI_enable */
-	OH_BAY_IDE_ENABLE,	/* FEATURE_Mediabay_IDE_enable */
-	OH_BAY_FLOPPY_ENABLE,	/* FEATURE_Mediabay_floppy_enable */
-	0,			/* FEATURE_BMac_reset */
-	0,			/* FEATURE_BMac_IO_enable */
-	0,			/* FEATURE_Modem_Reset -> guess... */
-	OH_IDE_POWER,		/* FEATURE_IDE_DiskPower -> guess... */
-	OH_IDE_RESET		/* FEATURE_IDE_Reset (0 based) -> guess... */
+#define MAX_FEATURE_CONTROLLERS		2
+#define MAX_FEATURE_OFFSET		0x50
+#define FREG(c,r)			(&(((c)->reg)[(r)>>2]))
+
+typedef struct feature_bit {
+	int		reg;		/* reg. offset from mac-io base */
+	unsigned int	polarity;	/* 0 = normal, 1 = inverse */
+	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
+ */
+static fbit feature_bits_ohare_pbook[] = {
+	{0x38,0,0},			/* FEATURE_null */
+	{0x38,0,OH_SCC_RESET},		/* FEATURE_Serial_reset */
+	{0x38,0,OH_SCC_ENABLE},		/* FEATURE_Serial_enable */
+	{0x38,0,OH_SCCA_IO},		/* FEATURE_Serial_IO_A */
+	{0x38,0,OH_SCCB_IO},		/* FEATURE_Serial_IO_B */
+	{0x38,0,OH_FLOPPY_ENABLE},	/* FEATURE_SWIM3_enable */
+	{0x38,0,OH_MESH_ENABLE},	/* FEATURE_MESH_enable */
+	{0x38,0,OH_IDE0_ENABLE},	/* FEATURE_IDE0_enable */
+	{0x38,1,OH_IDE0_RESET_N},	/* FEATURE_IDE0_reset */
+	{0x38,0,OH_IOBUS_ENABLE},	/* FEATURE_IOBUS_enable */
+	{0x38,1,OH_BAY_RESET_N},	/* FEATURE_Mediabay_reset */
+	{0x38,1,OH_BAY_POWER_N},	/* FEATURE_Mediabay_power */
+	{0x38,0,OH_BAY_PCI_ENABLE},	/* FEATURE_Mediabay_PCI_enable */
+	{0x38,0,OH_BAY_IDE_ENABLE},	/* FEATURE_IDE1_enable */
+	{0x38,1,OH_IDE1_RESET_N},	/* FEATURE_IDE1_reset */
+	{0x38,0,OH_BAY_FLOPPY_ENABLE},	/* FEATURE_Mediabay_floppy_enable */
+	{0x38,0,0},			/* FEATURE_BMac_reset */
+	{0x38,0,0},			/* FEATURE_BMac_IO_enable */
+	{0x38,0,0},			/* FEATURE_Modem_power */
+	{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 */
+	{0x38,0,0},			/* FEATURE_IDE2_reset */
+	{0x38,0,0},			/* FEATURE_Mediabay_IDE_switch */
+	{0x38,0,0},			/* FEATURE_Mediabay_content */
 };
 
-/* assume these are the same as the ohare until proven otherwise */
-static u32 feature_bits_heathrow[] = {
-	0,			/* FEATURE_null */
-	OH_SCC_RESET,		/* FEATURE_Serial_reset */
-	OH_SCC_ENABLE,		/* FEATURE_Serial_enable */
-	OH_SCCA_IO,		/* FEATURE_Serial_IO_A */
-	OH_SCCB_IO,		/* FEATURE_Serial_IO_B */
-	OH_FLOPPY_ENABLE,	/* FEATURE_SWIM3_enable */
-	OH_MESH_ENABLE,		/* FEATURE_MESH_enable */
-	OH_IDE_ENABLE,		/* FEATURE_IDE_enable */
-	OH_VIA_ENABLE,		/* FEATURE_VIA_enable */
-	OH_IDECD_POWER,		/* FEATURE_CD_power */
-	OH_BAY_RESET,		/* FEATURE_Mediabay_reset */
-	OH_BAY_ENABLE,		/* FEATURE_Mediabay_enable */
-	OH_BAY_PCI_ENABLE,	/* FEATURE_Mediabay_PCI_enable */
-	OH_BAY_IDE_ENABLE,	/* FEATURE_Mediabay_IDE_enable */
-	OH_BAY_FLOPPY_ENABLE,	/* FEATURE_Mediabay_floppy_enable */
-	0x80000000,		/* FEATURE_BMac_reset */
-	0x60000000,		/* FEATURE_BMac_IO_enable */
-	0x02000000,		/* FEATURE_Modem_Reset -> guess...*/
-	OH_IDE_POWER,		/* FEATURE_IDE_DiskPower -> guess... */
-	OH_IDE_RESET		/* FEATURE_IDE_Reset (0 based) -> guess... */
+/* Those bits are from a PowerBook. It's possible that desktop machines
+ * based on heathrow need a different definition or some bits removed
+ */
+static fbit feature_bits_heathrow[] = {
+	{0x38,0,0},			/* FEATURE_null */
+	{0x38,0,HRW_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 */
+	{0x38,0,HRW_SWIM_ENABLE},	/* FEATURE_SWIM3_enable */
+	{0x38,0,HRW_MESH_ENABLE},	/* FEATURE_MESH_enable */
+	{0x38,0,HRW_IDE0_ENABLE},	/* FEATURE_IDE0_enable */
+	{0x38,1,HRW_IDE0_RESET_N},	/* FEATURE_IDE0_reset */
+	{0x38,0,HRW_IOBUS_ENABLE},	/* FEATURE_IOBUS_enable */
+	{0x38,1,HRW_BAY_RESET_N},	/* FEATURE_Mediabay_reset */
+	{0x38,1,HRW_BAY_POWER_N},	/* FEATURE_Mediabay_power */
+	{0x38,0,HRW_BAY_PCI_ENABLE},	/* FEATURE_Mediabay_PCI_enable */
+	{0x38,0,HRW_BAY_IDE_ENABLE},	/* FEATURE_IDE1_enable */
+	{0x38,1,HRW_IDE1_RESET_N},	/* FEATURE_IDE1_reset */
+	{0x38,0,HRW_BAY_FLOPPY_ENABLE},	/* FEATURE_Mediabay_floppy_enable */
+	{0x38,0,HRW_BMAC_RESET},	/* FEATURE_BMac_reset */
+	{0x38,0,HRW_BMAC_IO_ENABLE},	/* FEATURE_BMac_IO_enable */
+	{0x38,1,HRW_MODEM_POWER_N},	/* FEATURE_Modem_power */
+	{0x38,0,HRW_SLOW_SCC_PCLK},	/* FEATURE_Slow_SCC_PCLK */
+	{0x38,1,HRW_SOUND_POWER_N},	/* FEATURE_Sound_Power */
+	{0x38,0,HRW_SOUND_CLK_ENABLE},	/* FEATURE_Sound_CLK_Enable */
+	{0x38,0,0},			/* FEATURE_IDE2_enable */
+	{0x38,0,0},			/* FEATURE_IDE2_reset */
+	{0x38,0,0},			/* FEATURE_Mediabay_IDE_switch */
+	{0x38,0,0},			/* FEATURE_Mediabay_content */
+};
+
+/*
+ * Those bits are from a 1999 G3 PowerBook, with a paddington chip.
+ * Mostly the same as the heathrow.
+ */
+static fbit feature_bits_paddington[] = {
+	{0x38,0,0},			/* FEATURE_null */
+	{0x38,0,0},			/* 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 */
+	{0x38,0,HRW_SWIM_ENABLE},	/* FEATURE_SWIM3_enable */
+	{0x38,0,HRW_MESH_ENABLE},	/* FEATURE_MESH_enable */
+	{0x38,0,HRW_IDE0_ENABLE},	/* FEATURE_IDE0_enable */
+	{0x38,1,HRW_IDE0_RESET_N},	/* FEATURE_IDE0_reset */
+	{0x38,0,HRW_IOBUS_ENABLE},	/* FEATURE_IOBUS_enable */
+	{0x38,1,HRW_BAY_RESET_N},	/* FEATURE_Mediabay_reset */
+	{0x38,1,HRW_BAY_POWER_N},	/* FEATURE_Mediabay_power */
+	{0x38,0,HRW_BAY_PCI_ENABLE},	/* FEATURE_Mediabay_PCI_enable */
+	{0x38,0,HRW_BAY_IDE_ENABLE},	/* FEATURE_IDE1_enable */
+	{0x38,1,HRW_IDE1_RESET_N},	/* FEATURE_IDE1_reset */
+	{0x38,0,HRW_BAY_FLOPPY_ENABLE},	/* FEATURE_Mediabay_floppy_enable */
+	{0x38,0,HRW_BMAC_RESET},	/* FEATURE_BMac_reset */
+	{0x38,0,HRW_BMAC_IO_ENABLE},	/* FEATURE_BMac_IO_enable */
+	{0x38,1,PADD_MODEM_POWER_N},	/* FEATURE_Modem_power */
+	{0x38,0,HRW_SLOW_SCC_PCLK},	/* FEATURE_Slow_SCC_PCLK */
+	{0x38,1,HRW_SOUND_POWER_N},	/* FEATURE_Sound_Power */
+	{0x38,0,HRW_SOUND_CLK_ENABLE},	/* FEATURE_Sound_CLK_Enable */
+	{0x38,0,0},			/* FEATURE_IDE2_enable */
+	{0x38,0,0},			/* FEATURE_IDE2_reset */
+	{0x38,0,0},			/* FEATURE_Mediabay_IDE_switch */
+	{0x38,0,0},			/* FEATURE_Mediabay_content */
+};
+
+/* Those bits are for Core99 machines (iBook,G4,iMacSL/DV,Pismo,...).
+ */
+static fbit feature_bits_keylargo[] = {
+	{0x38,0,0},			/* FEATURE_null */
+	{0x38,0,0},			/* FEATURE_Serial_reset */
+	{0x38,0,0x00000054},		/* FEATURE_Serial_enable */
+	{0x38,0,0},			/* FEATURE_Serial_IO_A */
+	{0x38,0,0},			/* FEATURE_Serial_IO_B */
+	{0x38,0,0},			/* FEATURE_SWIM3_enable */
+	{0x38,0,0},			/* FEATURE_MESH_enable */
+	{0x3c,0,0},			/* FEATURE_IDE0_enable */
+ 	{0x3c,1,0x01000000},		/* FEATURE_IDE0_reset */
+	{0x38,0,0},			/* FEATURE_IOBUS_enable */
+	{0x34,1,0x00000200},		/* FEATURE_Mediabay_reset */
+	{0x34,1,0x00000400},		/* FEATURE_Mediabay_power */
+	{0x38,0,0},			/* FEATURE_Mediabay_PCI_enable */
+	{0x3c,0,0x0},			/* FEATURE_IDE1_enable */
+	{0x3c,1,0x08000000},		/* FEATURE_IDE1_reset */
+	{0x38,0,0},			/* FEATURE_Mediabay_floppy_enable */
+	{0x38,0,0},			/* FEATURE_BMac_reset */
+	{0x38,0,0},			/* FEATURE_BMac_IO_enable */
+	{0x40,1,0x02000000},		/* FEATURE_Modem_power */
+	{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,1,0x40000000},		/* FEATURE_IDE2_reset */
+	{0x34,0,0x00001000},		/* FEATURE_Mediabay_IDE_switch */
+	{0x34,0,0x00000100},		/* FEATURE_Mediabay_content */
 };
 
 /* definition of a feature controller object */
-struct feature_controller
-{
-	u32*			bits;
+struct feature_controller {
+	fbit*			bits;
 	volatile u32*		reg;
 	struct device_node*	device;
+	spinlock_t		lock;
 };
 
 /* static functions */
 static void
-feature_add_controller(struct device_node *controller_device, u32* bits);
+feature_add_controller(struct device_node *controller_device, fbit* bits);
 
-static int
+static struct feature_controller*
 feature_lookup_controller(struct device_node *device);
 
 /* static varialbles */
-static struct feature_controller	controllers[MAX_FEATURE_REGS];
+static struct feature_controller	controllers[MAX_FEATURE_CONTROLLERS];
 static int				controller_count = 0;
 
 
@@ -96,18 +193,25 @@
 	struct device_node *np;
 
 	np = find_devices("mac-io");
-	while (np != NULL)
-	{
-		feature_add_controller(np, feature_bits_heathrow);
+	while (np != NULL) {
+		/* KeyLargo contains several (5 ?) FCR registers in mac-io,
+		 * plus some gpio's which could eventually be handled here.
+		 */
+		if (device_is_compatible(np, "Keylargo")) {
+			feature_add_controller(np, feature_bits_keylargo);
+		} else if (device_is_compatible(np, "paddington")) {
+			feature_add_controller(np, feature_bits_paddington);
+		} else {
+			feature_add_controller(np, feature_bits_heathrow);
+		}
 		np = np->next;
 	}
 	if (controller_count == 0)
 	{
 		np = find_devices("ohare");
-		if (np)
-		{
+		if (np) {
 			if (find_devices("via-pmu") != NULL)
-				feature_add_controller(np, feature_bits_pbook);
+				feature_add_controller(np, feature_bits_ohare_pbook);
 			else
 				/* else not sure; maybe this is a Starmax? */
 				feature_add_controller(np, NULL);
@@ -116,17 +220,26 @@
 
 	if (controller_count)
 		printk(KERN_INFO "Registered %d feature controller(s)\n", controller_count);
+
+#ifdef CONFIG_PMAC_PBOOK
+#ifdef CONFIG_DMASOUND_MODULE
+	/* On PowerBooks, we disable the sound chip when dmasound is a module */
+	if (controller_count && find_devices("via-pmu") != NULL) {
+		feature_clear(controllers[0].device, FEATURE_Sound_power);
+		feature_clear(controllers[0].device, FEATURE_Sound_CLK_enable);
+	}
+#endif	
+#endif
 }
 
 static void
-feature_add_controller(struct device_node *controller_device, u32* bits)
+feature_add_controller(struct device_node *controller_device, fbit* bits)
 {
 	struct feature_controller*	controller;
 	
-	if (controller_count >= MAX_FEATURE_REGS)
-	{
+	if (controller_count >= MAX_FEATURE_CONTROLLERS) {
 		printk(KERN_INFO "Feature controller %s skipped(MAX:%d)\n",
-			controller_device->full_name, MAX_FEATURE_REGS);
+			controller_device->full_name, MAX_FEATURE_CONTROLLERS);
 		return;
 	}
 	controller = &controllers[controller_count];
@@ -140,30 +253,32 @@
 	}
 
 	controller->reg		= (volatile u32 *)ioremap(
-		controller_device->addrs[0].address + OHARE_FEATURE_REG, 4);
+		controller_device->addrs[0].address, MAX_FEATURE_OFFSET);
 
 	if (bits == NULL) {
 		printk(KERN_INFO "Twiddling the magic ohare bits\n");
-		out_le32(controller->reg, STARMAX_FEATURES);
+		out_le32(FREG(controller,OHARE_FEATURE_REG), STARMAX_FEATURES);
 		return;
 	}
 
+	spin_lock_init(&controller->lock);
+	
 	controller_count++;
 }
 
-static int
+static struct feature_controller*
 feature_lookup_controller(struct device_node *device)
 {
 	int	i;
 	
 	if (device == NULL)
-		return -EINVAL;
+		return NULL;
 		
 	while(device)
 	{
 		for (i=0; i<controller_count; i++)
 			if (device == controllers[i].device)
-				return i;
+				return &controllers[i];
 		device = device->parent;
 	}
 
@@ -172,35 +287,38 @@
 		device->name);
 #endif
 	
-	return -ENODEV;
+	return NULL;
 }
 
 int
 feature_set(struct device_node* device, enum system_feature f)
 {
-	int		controller;
-	unsigned long	flags;
+	struct feature_controller*	controller;
+	unsigned long			flags;
+	unsigned long			value;
+	fbit*				bit;
 
 	if (f >= FEATURE_last)
 		return -EINVAL;	
 
 	controller = feature_lookup_controller(device);
-	if (controller < 0)
-		return controller;
+	if (!controller)
+		return -ENODEV;
+	bit = &controller->bits[f];
+	if (!bit->mask)
+		return -EINVAL;
 	
 #ifdef DEBUG_FEATURE
 	printk("feature: <%s> setting feature %d in controller @0x%x\n",
-		device->name, (int)f, (unsigned int)controllers[controller].reg);
+		device->name, (int)f, (unsigned int)controller->reg);
 #endif
 
-	save_flags(flags);
-	cli();
-	out_le32( controllers[controller].reg,
-		 in_le32(controllers[controller].reg) |
-		 controllers[controller].bits[f]);
-	(void)in_le32(controllers[controller].reg);
-	restore_flags(flags);
-	udelay(10);
+	spin_lock_irqsave(&controller->lock, flags);
+	value = in_le32(FREG(controller, bit->reg));
+	value = bit->polarity ? (value & ~bit->mask) : (value | bit->mask);
+	out_le32(FREG(controller, bit->reg), value);
+	(void)in_le32(FREG(controller, bit->reg));
+	spin_unlock_irqrestore(&controller->lock, flags);
 	
 	return 0;
 }
@@ -208,29 +326,32 @@
 int
 feature_clear(struct device_node* device, enum system_feature f)
 {
-	int		controller;
-	unsigned long	flags;
+	struct feature_controller*	controller;
+	unsigned long			flags;
+	unsigned long			value;
+	fbit*				bit;
 
 	if (f >= FEATURE_last)
 		return -EINVAL;	
 
 	controller = feature_lookup_controller(device);
-	if (controller < 0)
-		return controller;
+	if (!controller)
+		return -ENODEV;
+	bit = &controller->bits[f];
+	if (!bit->mask)
+		return -EINVAL;
 	
 #ifdef DEBUG_FEATURE
 	printk("feature: <%s> clearing feature %d in controller @0x%x\n",
-		device->name, (int)f, (unsigned int)controllers[controller].reg);
+		device->name, (int)f, (unsigned int)controller->reg);
 #endif
 
-	save_flags(flags);
-	cli();
-	out_le32( controllers[controller].reg,
-		 in_le32(controllers[controller].reg) &
-		 ~(controllers[controller].bits[f]));
-	(void)in_le32(controllers[controller].reg);
-	restore_flags(flags);
-	udelay(10);
+	spin_lock_irqsave(&controller->lock, flags);
+	value = in_le32(FREG(controller, bit->reg));
+	value = bit->polarity ? (value | bit->mask) : (value & ~bit->mask);
+	out_le32(FREG(controller, bit->reg), value);
+	(void)in_le32(FREG(controller, bit->reg));
+	spin_unlock_irqrestore(&controller->lock, flags);
 	
 	return 0;
 }
@@ -238,16 +359,29 @@
 int
 feature_test(struct device_node* device, enum system_feature f)
 {
-	int		controller;
+	struct feature_controller*	controller;
+	unsigned long			value;
+	fbit*				bit;
 
 	if (f >= FEATURE_last)
 		return -EINVAL;	
 
 	controller = feature_lookup_controller(device);
-	if (controller < 0)
-		return controller;
+	if (!controller)
+		return -ENODEV;
+	bit = &controller->bits[f];
+	if (!bit->mask)
+		return -EINVAL;
 	
-	return (in_le32(controllers[controller].reg) &
-		controllers[controller].bits[f]) != 0;
+#ifdef DEBUG_FEATURE
+	printk("feature: <%s> clearing feature %d in controller @0x%x\n",
+		device->name, (int)f, (unsigned int)controller->reg);
+#endif
+	/* If one feature contains several bits, all of them must be set
+	 * for value to be true, or all of them must be 0 if polarity is
+	 * inverse
+	 */
+	value = (in_le32(FREG(controller, bit->reg)) & bit->mask);
+	return bit->polarity ? (value == 0) : (value == bit->mask);
 }
 

FUNET's LINUX-ADM group, linux-adm@nic.funet.fi
TCL-scripts by Sam Shen (who was at: slshen@lbl.gov)