LinuxPPC-Dev Archive on lore.kernel.org
 help / color / mirror / Atom feed
* Re: Xilinx BSP for linux 2.6
From: Ameet Patil @ 2006-07-07 15:55 UTC (permalink / raw)
  To: Ming Liu; +Cc: linuxppc-embedded
In-Reply-To: <BAY110-F2E51F50D2158DF44D3820B2740@phx.gbl>

Its the same link as before! I just updated the file!
http://www.cs.york.ac.uk/rtslab/demos/amos/xupv2pro/patches/linuxppc-2.6.17_sysace.patch

-Ameet

Ming Liu wrote:
> Dear Ameet,
> Could you please give me a copy of your new driver for SystemACE?
> Regards
> Ming
> 
> 
>> From: Ameet Patil <ammubhai@gmail.com>
>> To: Ming Liu <eemingliu@hotmail.com>
>> Subject: Re: Xilinx BSP for linux 2.6
>> Date: Fri, 07 Jul 2006 15:17:31 +0100
>>
>> Thats right! Sorry about the error! :-)
>>
>> Good luck with TEMAC!
>>
>> -Ameet
>>
>> Ming Liu wrote:
>> > Dear Ameet,
>> > OK. I found the problem. tts/0 should be a char device, not a block 
> one.
>> > so it should be c not b. I neglected that and just copied your 
> commands. :)
>> >
>> > Next, I will try to implement the Temac driver. If any information, I
>> > will let you know.
>> >
>> > Regards
>> > Ming
>> >
>> >
>> >> From: Ameet Patil <ammubhai@gmail.com>
>> >> To: Ming Liu <eemingliu@hotmail.com>
>> >> CC: linuxppc-embedded@ozlabs.org
>> >> Subject: Re: Xilinx BSP for linux 2.6
>> >> Date: Fri, 07 Jul 2006 13:42:42 +0100
>> >>
>> >> Hi Ming,
>> >>
>> >> Excellent News!
>> >>
>> >> Don't worry... the problem you are facing could be because of missing
>> >> device files in your /dev folder.
>> >>
>> >> Do as follows:
>> >> mount the CF card onto your PC and then goto the /dev folder on your
>> >> root filesystem in the card. Here run the following commands as root:
>> >> # mknod -m 660 console c 5 1
>> >> # mknod -m 660 xsa b 254 0
>> >> # mknod -m 660 xsa1 b 254 1
>> >> # mknod -m 660 xsa2 b 254 2
>> >> # mknod -m 660 xsa3 b 254 3
>> >> Now there should be a directory tts in your dev. If not create one
>> >> "mkdir tts". Then do this in the dev/tts folder,
>> >> # mknod -m 660 0 b 4 64
>> >>
>> >> > Also, when I compile the kernel, there is a error. That's in 
> adapter.c
>> >> > file, undefined reference to "XAssert". I checked the source code 
> and
>> >> I think you have the old patch. If you download the new one, the
>> >> compilation problem should not occur. But you resolved it yourself,...
>> >> which is good.
>> >>
>> >> Let me know if this works. I shall add this stuff in PART II of the
>> > article.
>> >>
>> >> -Ameet
>> >>
>> >> Ming Liu wrote:
>> >> > Dear Ameet,
>> >> > A good news is that now my 2.6 kernel is running in my ML403 board!
>> >> > According your guidance, first I made the kernel as simple as 
> possible
>> >> > and it works well. Then I included the patch for SystemACE and here 
> is
>> >> > the information shown on the hyper teminal:
>> >> >
>> >> >
>> >> > Linux/PPC load: console=ttyS0,9600 root=/dev/xsa3 [    0.000148]
>> >> > Console: colour dummy device 80x25
>> >> > [    0.000919] Dentry cache hash table entries: 16384 (order: 4, 
> 65536
>> >> > bytes)
>> >> > [    0.002277] Inode-cache hash table entries: 8192 (order: 3, 32768
>> > bytes)
>> >> > [    0.012802] Memory: 63104k available (1340k kernel code, 384k 
> data,
>> >> > 76k init,
>> >> > 0k highmem)
>> >> > [    0.230310] Mount-cache hash table entries: 512
>> >> > [    0.238305] VFS: Disk quotas dquot_6.5.1
>> >> > [    0.238516] Dquot-cache hash table entries: 1024 (order 0, 4096
>> > bytes)
>> >> > [    0.238817] JFFS2 version 2.2. (NAND) (C) 2001-2003 Red Hat, Inc.
>> >> > [    0.239435] JFS: nTxBlock = 493, nTxLock = 3944
>> >> > [    0.241883] Initializing Cryptographic API
>> >> > [    0.241940] io scheduler noop registered (default)
>> >> > [    0.322864] Serial: 8250/16550 driver $Revision: 1.90 $ 4 ports, 
> IRQ
>> >> > sharing
>> >> > disabled
>> >> > [    0.324759] serial8250.0: ttyS0 at MMIO 0x40401003 (irq = 1) is a
>> > 16550A
>> >> > [    1.653644] RAMDISK driver initialized: 16 RAM disks of 65536K 
> size
>> >> > 1024 bloc
>> >> > ksize
>> >> > [    1.745079] loop: loaded (max 8 devices)
>> >> > [    1.793392]  xsa: xsa1 xsa2 xsa3
>> >> > [    1.839262] mice: PS/2 mouse device common for all mice
>> >> > [    1.935059] VFS: Mounted root (ext2 filesystem) readonly.
>> >> > [    2.000037] Freeing unused kernel memory: 76k init
>> >> > [    2.075656] Warning: unable to open an initial console.
>> >> >
>> >> > I use xsa3 as the partition to store the file system. Now the file
>> >> > system is the old one for my 2.4 kernel generated by busybox. Then I
>> >> > think that's why the problem "unable to open an initial console" is
>> >> > generated. Do you have some suggestion on how to solve it? Thanks 
> for
>> >> > your opinion.
>> >> > Also, when I compile the kernel, there is a error. That's in 
> adapter.c
>> >> > file, undefined reference to "XAssert". I checked the source code 
> and
>> >> > found that the function of XAssert is defined in the file
>> >> > xbasic_types.c, not the xbasic_types.h. So it cannot be included 
> into
>> >> > adapter.c. And also in the makefile there is no dependency on
>> >> > xbasic_types.c. So I added the defination for this function in
>> >> > adapter.c, copying it from xbasic_types.c. It then worked well.
>> >> > Waiting for your guidance. You know, an expert's opinion is always
>> >> > useful for a novice, like me. :)
>> >> >
>> >> > Regards
>> >> > Ming
>> >> >
>> >> > _________________________________________________________________
>> >> > 免费下载 MSN Explorer:   http://explorer.msn.com/lccn/
>> >> >
>> >
>> > _________________________________________________________________
>> > 享用世界上最大的电子邮件系统― MSN Hotmail。  http://www.hotmail.com
>> >
> 
> _________________________________________________________________
> 免费下载 MSN Explorer:   http://explorer.msn.com/lccn 
> 

^ permalink raw reply

* [PATCH 06/12] aoa: pmf gpio: report if function calling fails
From: Johannes Berg @ 2006-07-07 16:52 UTC (permalink / raw)
  To: linuxppc-dev
In-Reply-To: <20060707165207.877010000@sipsolutions.net>

This patch makes the pmf GPIO layer in aoa report if calling
a platform function failed.

Signed-off-by: Benjamin Herrenschmidt <benh@kernel.crashing.org>
Signed-off-by: Johannes Berg <johannes@sipsolutions.net>

--- linux-2.6-fetch.orig/sound/aoa/core/snd-aoa-gpio-pmf.c	2006-07-07 12:15:39.657697157 +0200
+++ linux-2.6-fetch/sound/aoa/core/snd-aoa-gpio-pmf.c	2006-07-07 12:15:54.207697157 +0200
@@ -14,9 +14,13 @@
 static void pmf_gpio_set_##name(struct gpio_runtime *rt, int on)\
 {								\
 	struct pmf_args args = { .count = 1, .u[0].v = !on };	\
-								\
+	int rc;							\
+							\
 	if (unlikely(!rt)) return;				\
-	pmf_call_function(rt->node, #name "-mute", &args);	\
+	rc = pmf_call_function(rt->node, #name "-mute", &args);	\
+	if (rc)							\
+		printk(KERN_WARNING "pmf_gpio_set_" #name	\
+		" failed, rc: %d\n", rc);			\
 	rt->implementation_private &= ~(1<<bit);		\
 	rt->implementation_private |= (!!on << bit);		\
 }								\
@@ -33,9 +37,13 @@ PMF_GPIO(lineout, 2);
 static void pmf_gpio_set_hw_reset(struct gpio_runtime *rt, int on)
 {
 	struct pmf_args args = { .count = 1, .u[0].v = !!on };
+	int rc;
 
 	if (unlikely(!rt)) return;
-	pmf_call_function(rt->node, "hw-reset", &args);
+	rc = pmf_call_function(rt->node, "hw-reset", &args);
+	if (rc)
+		printk(KERN_WARNING "pmf_gpio_set_hw_reset"
+		       " failed, rc: %d\n", rc);
 }
 
 static void pmf_gpio_all_amps_off(struct gpio_runtime *rt)

--

^ permalink raw reply

* [PATCH 10/12] aoa: tas: surface DRC control again
From: Johannes Berg @ 2006-07-07 16:52 UTC (permalink / raw)
  To: linuxppc-dev
In-Reply-To: <20060707165207.877010000@sipsolutions.net>

This patch makes the DRC control visible again for TAS chips.

Signed-off-by: Johannes Berg <johannes@sipsolutions.net>

--- linux-2.6-fetch.orig/sound/aoa/codecs/snd-aoa-codec-tas.c	2006-07-07 15:23:20.146178326 +0200
+++ linux-2.6-fetch/sound/aoa/codecs/snd-aoa-codec-tas.c	2006-07-07 15:25:15.316178326 +0200
@@ -342,6 +342,90 @@ static struct snd_kcontrol_new n##_contr
 MIXER_CONTROL(pcm1, "PCM", 0);
 MIXER_CONTROL(monitor, "Monitor", 2);
 
+static int tas_snd_drc_range_info(struct snd_kcontrol *kcontrol,
+	struct snd_ctl_elem_info *uinfo)
+{
+	uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
+	uinfo->count = 1;
+	uinfo->value.integer.min = 0;
+	uinfo->value.integer.max = TAS3004_DRC_MAX;
+	return 0;
+}
+
+static int tas_snd_drc_range_get(struct snd_kcontrol *kcontrol,
+	struct snd_ctl_elem_value *ucontrol)
+{
+	struct tas *tas = snd_kcontrol_chip(kcontrol);
+
+	ucontrol->value.integer.value[0] = tas->drc_range;
+	return 0;
+}
+
+static int tas_snd_drc_range_put(struct snd_kcontrol *kcontrol,
+	struct snd_ctl_elem_value *ucontrol)
+{
+	struct tas *tas = snd_kcontrol_chip(kcontrol);
+
+	if (tas->drc_range == ucontrol->value.integer.value[0])
+		return 0;
+
+	tas->drc_range = ucontrol->value.integer.value[0];
+	if (tas->hw_enabled)
+		tas3004_set_drc(tas);
+	return 1;
+}
+
+static struct snd_kcontrol_new drc_range_control = {
+	.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+	.name = "DRC Range",
+	.access = SNDRV_CTL_ELEM_ACCESS_READWRITE,
+	.info = tas_snd_drc_range_info,
+	.get = tas_snd_drc_range_get,
+	.put = tas_snd_drc_range_put,
+};
+
+static int tas_snd_drc_switch_info(struct snd_kcontrol *kcontrol,
+	struct snd_ctl_elem_info *uinfo)
+{
+	uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN;
+	uinfo->count = 1;
+	uinfo->value.integer.min = 0;
+	uinfo->value.integer.max = 1;
+	return 0;
+}
+
+static int tas_snd_drc_switch_get(struct snd_kcontrol *kcontrol,
+	struct snd_ctl_elem_value *ucontrol)
+{
+	struct tas *tas = snd_kcontrol_chip(kcontrol);
+
+	ucontrol->value.integer.value[0] = tas->drc_enabled;
+	return 0;
+}
+
+static int tas_snd_drc_switch_put(struct snd_kcontrol *kcontrol,
+	struct snd_ctl_elem_value *ucontrol)
+{
+	struct tas *tas = snd_kcontrol_chip(kcontrol);
+
+	if (tas->drc_enabled == ucontrol->value.integer.value[0])
+		return 0;
+
+	tas->drc_enabled = ucontrol->value.integer.value[0];
+	if (tas->hw_enabled)
+		tas3004_set_drc(tas);
+	return 1;
+}
+
+static struct snd_kcontrol_new drc_switch_control = {
+	.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+	.name = "DRC Range Switch",
+	.access = SNDRV_CTL_ELEM_ACCESS_READWRITE,
+	.info = tas_snd_drc_switch_info,
+	.get = tas_snd_drc_switch_get,
+	.put = tas_snd_drc_switch_put,
+};
+
 static int tas_snd_capture_source_info(struct snd_kcontrol *kcontrol,
 	struct snd_ctl_elem_info *uinfo)
 {
@@ -590,6 +674,14 @@ static int tas_init_codec(struct aoa_cod
 	if (err)
 		goto error;
 
+	err = aoa_snd_ctl_add(snd_ctl_new1(&drc_range_control, tas));
+	if (err)
+		goto error;
+
+	err = aoa_snd_ctl_add(snd_ctl_new1(&drc_switch_control, tas));
+	if (err)
+		goto error;
+
 	return 0;
  error:
 	tas->codec.soundbus_dev->detach_codec(tas->codec.soundbus_dev, tas);
@@ -623,7 +715,8 @@ static int tas_create(struct i2c_adapter
 	tas->i2c.driver = &tas_driver;
 	tas->i2c.adapter = adapter;
 	tas->i2c.addr = addr;
-	tas->drc_range = TAS3004_DRC_MAX;
+	/* seems that half is a saner default */
+	tas->drc_range = TAS3004_DRC_MAX / 2;
 	strlcpy(tas->i2c.name, "tas audio codec", I2C_NAME_SIZE-1);
 
 	if (i2c_attach_client(&tas->i2c)) {

--

^ permalink raw reply

* [PATCH 08/12] aoa: tas: change PCM1 name to PCM
From: Johannes Berg @ 2006-07-07 16:52 UTC (permalink / raw)
  To: linuxppc-dev
In-Reply-To: <20060707165207.877010000@sipsolutions.net>

This patch changes the PCM1 control name to PCM to make it play
nice with the softvol plugin (which will then go away if it
sees a proper PCM slider)

Signed-off-by: Johannes Berg <johannes@sipsolutions.net>

--- linux-2.6-fetch.orig/sound/aoa/codecs/snd-aoa-codec-tas.c	2006-07-07 12:15:39.307697157 +0200
+++ linux-2.6-fetch/sound/aoa/codecs/snd-aoa-codec-tas.c	2006-07-07 12:15:55.907697157 +0200
@@ -309,7 +309,7 @@ static struct snd_kcontrol_new n##_contr
 	.private_value = idx,				\
 }
 
-MIXER_CONTROL(pcm1, "PCM1", 0);
+MIXER_CONTROL(pcm1, "PCM", 0);
 MIXER_CONTROL(monitor, "Monitor", 2);
 
 static int tas_snd_capture_source_info(struct snd_kcontrol *kcontrol,

--

^ permalink raw reply

* [PATCH 09/12] aoa: tas: fix initialisation/reset
From: Johannes Berg @ 2006-07-07 16:52 UTC (permalink / raw)
  To: linuxppc-dev
In-Reply-To: <20060707165207.877010000@sipsolutions.net>

This patch fixes the initialisation and reset of the tas codec.
The tas will often reset if the i2s clocks go away so it needs
to be completely re-initialised when clocks come back.

Also, this patch adds some code for DRC that will be exploited
later to add a DRC control again, fixing a regression over
snd-powermac.

Signed-off-by: Benjamin Herrenschmidt <benh@kernel.crashing.org>
Signed-off-by: Johannes Berg <johannes@sipsolutions.net>

--- linux-2.6-fetch.orig/sound/aoa/codecs/snd-aoa-codec-tas.c	2006-07-07 15:19:58.526178326 +0200
+++ linux-2.6-fetch/sound/aoa/codecs/snd-aoa-codec-tas.c	2006-07-07 15:23:20.146178326 +0200
@@ -75,19 +75,24 @@ MODULE_DESCRIPTION("tas codec driver for
 #include "../aoa.h"
 #include "../soundbus/soundbus.h"
 
-
 #define PFX "snd-aoa-codec-tas: "
 
+
 struct tas {
 	struct aoa_codec	codec;
 	struct i2c_client	i2c;
-	u32			muted_l:1, muted_r:1,
-				controls_created:1;
+	u32			mute_l:1, mute_r:1 ,
+				controls_created:1 ,
+				drc_enabled:1,
+				hw_enabled:1;
 	u8			cached_volume_l, cached_volume_r;
 	u8			mixer_l[3], mixer_r[3];
 	u8			acr;
+	int			drc_range;
 };
 
+static int tas_reset_init(struct tas *tas);
+
 static struct tas *codec_to_tas(struct aoa_codec *codec)
 {
 	return container_of(codec, struct tas, codec);
@@ -101,6 +106,28 @@ static inline int tas_write_reg(struct t
 		return i2c_smbus_write_i2c_block_data(&tas->i2c, reg, len, data);
 }
 
+static void tas3004_set_drc(struct tas *tas)
+{
+	unsigned char val[6];
+
+	if (tas->drc_enabled)
+		val[0] = 0x50; /* 3:1 above threshold */
+	else
+		val[0] = 0x51; /* disabled */
+	val[1] = 0x02; /* 1:1 below threshold */
+	if (tas->drc_range > 0xef)
+		val[2] = 0xef;
+	else if (tas->drc_range < 0)
+		val[2] = 0x00;
+	else
+		val[2] = tas->drc_range;
+	val[3] = 0xb0;
+	val[4] = 0x60;
+	val[5] = 0xa0;
+
+	tas_write_reg(tas, TAS_REG_DRC, 6, val);
+}
+
 static void tas_set_volume(struct tas *tas)
 {
 	u8 block[6];
@@ -113,8 +140,8 @@ static void tas_set_volume(struct tas *t
 	if (left > 177) left = 177;
 	if (right > 177) right = 177;
 
-	if (tas->muted_l) left = 0;
-	if (tas->muted_r) right = 0;
+	if (tas->mute_l) left = 0;
+	if (tas->mute_r) right = 0;
 
 	/* analysing the volume and mixer tables shows
 	 * that they are similar enough when we shift
@@ -202,7 +229,8 @@ static int tas_snd_vol_put(struct snd_kc
 
 	tas->cached_volume_l = ucontrol->value.integer.value[0];
 	tas->cached_volume_r = ucontrol->value.integer.value[1];
-	tas_set_volume(tas);
+	if (tas->hw_enabled)
+		tas_set_volume(tas);
 	return 1;
 }
 
@@ -230,8 +258,8 @@ static int tas_snd_mute_get(struct snd_k
 {
 	struct tas *tas = snd_kcontrol_chip(kcontrol);
 
-	ucontrol->value.integer.value[0] = !tas->muted_l;
-	ucontrol->value.integer.value[1] = !tas->muted_r;
+	ucontrol->value.integer.value[0] = !tas->mute_l;
+	ucontrol->value.integer.value[1] = !tas->mute_r;
 	return 0;
 }
 
@@ -240,13 +268,14 @@ static int tas_snd_mute_put(struct snd_k
 {
 	struct tas *tas = snd_kcontrol_chip(kcontrol);
 
-	if (tas->muted_l == !ucontrol->value.integer.value[0]
-	 && tas->muted_r == !ucontrol->value.integer.value[1])
+	if (tas->mute_l == !ucontrol->value.integer.value[0]
+	 && tas->mute_r == !ucontrol->value.integer.value[1])
 		return 0;
 
-	tas->muted_l = !ucontrol->value.integer.value[0];
-	tas->muted_r = !ucontrol->value.integer.value[1];
-	tas_set_volume(tas);
+	tas->mute_l = !ucontrol->value.integer.value[0];
+	tas->mute_r = !ucontrol->value.integer.value[1];
+	if (tas->hw_enabled)
+		tas_set_volume(tas);
 	return 1;
 }
 
@@ -294,7 +323,8 @@ static int tas_snd_mixer_put(struct snd_
 	tas->mixer_l[idx] = ucontrol->value.integer.value[0];
 	tas->mixer_r[idx] = ucontrol->value.integer.value[1];
 
-	tas_set_mixer(tas);
+	if (tas->hw_enabled)
+		tas_set_mixer(tas);
 	return 1;
 }
 
@@ -346,7 +376,8 @@ static int tas_snd_capture_source_put(st
 		tas->acr |= TAS_ACR_INPUT_B;
 	if (oldacr == tas->acr)
 		return 0;
-	tas_write_reg(tas, TAS_REG_ACR, 1, &tas->acr);
+	if (tas->hw_enabled)
+		tas_write_reg(tas, TAS_REG_ACR, 1, &tas->acr);
 	return 1;
 }
 
@@ -399,26 +430,66 @@ static int tas_usable(struct codec_info_
 static int tas_reset_init(struct tas *tas)
 {
 	u8 tmp;
+
+	tas->codec.gpio->methods->all_amps_off(tas->codec.gpio);
+	msleep(5);
 	tas->codec.gpio->methods->set_hw_reset(tas->codec.gpio, 0);
-	msleep(1);
+	msleep(5);
 	tas->codec.gpio->methods->set_hw_reset(tas->codec.gpio, 1);
-	msleep(1);
+	msleep(20);
 	tas->codec.gpio->methods->set_hw_reset(tas->codec.gpio, 0);
-	msleep(1);
-
-	tas->acr &= ~TAS_ACR_ANALOG_PDOWN;
-	tas->acr |= TAS_ACR_B_MONAUREAL | TAS_ACR_B_MON_SEL_RIGHT;
-	if (tas_write_reg(tas, TAS_REG_ACR, 1, &tas->acr))
-		return -ENODEV;
+	msleep(10);
+	tas->codec.gpio->methods->all_amps_restore(tas->codec.gpio);
 
 	tmp = TAS_MCS_SCLK64 | TAS_MCS_SPORT_MODE_I2S | TAS_MCS_SPORT_WL_24BIT;
 	if (tas_write_reg(tas, TAS_REG_MCS, 1, &tmp))
 		return -ENODEV;
 
+	tas->acr |= TAS_ACR_ANALOG_PDOWN | TAS_ACR_B_MONAUREAL |
+		TAS_ACR_B_MON_SEL_RIGHT;
+	if (tas_write_reg(tas, TAS_REG_ACR, 1, &tas->acr))
+		return -ENODEV;
+
 	tmp = 0;
 	if (tas_write_reg(tas, TAS_REG_MCS2, 1, &tmp))
 		return -ENODEV;
 
+	tas3004_set_drc(tas);
+
+	/* Set treble & bass to 0dB */
+	tmp = 114;
+	tas_write_reg(tas, TAS_REG_TREBLE, 1, &tmp);
+	tas_write_reg(tas, TAS_REG_BASS, 1, &tmp);
+
+	tas->acr &= ~TAS_ACR_ANALOG_PDOWN;
+	if (tas_write_reg(tas, TAS_REG_ACR, 1, &tas->acr))
+		return -ENODEV;
+
+	return 0;
+}
+
+static int tas_switch_clock(struct codec_info_item *cii, enum clock_switch clock)
+{
+	struct tas *tas = cii->codec_data;
+
+	switch(clock) {
+	case CLOCK_SWITCH_PREPARE_SLAVE:
+		/* Clocks are going away, mute mute mute */
+		tas->codec.gpio->methods->all_amps_off(tas->codec.gpio);
+		tas->hw_enabled = 0;
+		break;
+	case CLOCK_SWITCH_SLAVE:
+		/* Clocks are back, re-init the codec */
+		tas_reset_init(tas);
+		tas_set_volume(tas);
+		tas_set_mixer(tas);
+		tas->hw_enabled = 1;
+		tas->codec.gpio->methods->all_amps_restore(tas->codec.gpio);
+		break;
+	default:
+		/* doesn't happen as of now */
+		return -EINVAL;
+	}
 	return 0;
 }
 
@@ -427,6 +498,7 @@ static int tas_reset_init(struct tas *ta
  * our i2c device is suspended, and then take note of that! */
 static int tas_suspend(struct tas *tas)
 {
+	tas->hw_enabled = 0;
 	tas->acr |= TAS_ACR_ANALOG_PDOWN;
 	tas_write_reg(tas, TAS_REG_ACR, 1, &tas->acr);
 	return 0;
@@ -438,6 +510,7 @@ static int tas_resume(struct tas *tas)
 	tas_reset_init(tas);
 	tas_set_volume(tas);
 	tas_set_mixer(tas);
+	tas->hw_enabled = 1;
 	return 0;
 }
 
@@ -463,6 +536,7 @@ static struct codec_info tas_codec_info 
 	.bus_factor = 64,
 	.owner = THIS_MODULE,
 	.usable = tas_usable,
+	.switch_clock = tas_switch_clock,
 #ifdef CONFIG_PM
 	.suspend = _tas_suspend,
 	.resume = _tas_resume,
@@ -483,6 +557,7 @@ static int tas_init_codec(struct aoa_cod
 		printk(KERN_ERR PFX "tas failed to initialise\n");
 		return -ENXIO;
 	}
+	tas->hw_enabled = 1;
 
 	if (tas->codec.soundbus_dev->attach_codec(tas->codec.soundbus_dev,
 						   aoa_get_card(),
@@ -548,6 +623,7 @@ static int tas_create(struct i2c_adapter
 	tas->i2c.driver = &tas_driver;
 	tas->i2c.adapter = adapter;
 	tas->i2c.addr = addr;
+	tas->drc_range = TAS3004_DRC_MAX;
 	strlcpy(tas->i2c.name, "tas audio codec", I2C_NAME_SIZE-1);
 
 	if (i2c_attach_client(&tas->i2c)) {
@@ -564,7 +640,9 @@ static int tas_create(struct i2c_adapter
 	if (aoa_codec_register(&tas->codec)) {
 		goto detach;
 	}
-	printk(KERN_DEBUG "snd-aoa-codec-tas: created and attached tas instance\n");
+	printk(KERN_DEBUG
+	       "snd-aoa-codec-tas: tas found, addr 0x%02x on %s\n",
+	       addr, node->full_name);
 	return 0;
  detach:
 	i2c_detach_client(&tas->i2c);
--- linux-2.6-fetch.orig/sound/aoa/codecs/snd-aoa-codec-tas.h	2006-07-07 15:19:58.666178326 +0200
+++ linux-2.6-fetch/sound/aoa/codecs/snd-aoa-codec-tas.h	2006-07-07 15:20:03.546178326 +0200
@@ -44,4 +44,12 @@
 #define TAS_REG_LEFT_BIQUAD6	0x10
 #define TAS_REG_RIGHT_BIQUAD6	0x19
 
+#define TAS_REG_LEFT_LOUDNESS		0x21
+#define TAS_REG_RIGHT_LOUDNESS		0x22
+#define TAS_REG_LEFT_LOUDNESS_GAIN	0x23
+#define TAS_REG_RIGHT_LOUDNESS_GAIN	0x24
+
+#define TAS3001_DRC_MAX		0x5f
+#define TAS3004_DRC_MAX		0xef
+
 #endif /* __SND_AOA_CODECTASH */

--

^ permalink raw reply

* [PATCH 01/12] Use proper irq mapping interface for snd-aoa-i2sbus.
From: Johannes Berg @ 2006-07-07 16:52 UTC (permalink / raw)
  To: linuxppc-dev
In-Reply-To: <20060707165207.877010000@sipsolutions.net>

Signed-off-by: Andreas Schwab <schwab@suse.de>
Acked-by: Benjamin Herrenschmidt <benh@kernel.crashing.org>
Signed-off-by: Johannes Berg <johannes@sipsolutions.net>

---
 sound/aoa/soundbus/i2sbus/i2sbus-core.c |    9 +++------
 1 file changed, 3 insertions(+), 6 deletions(-)

--- linux-2.6-fetch.orig/sound/aoa/soundbus/i2sbus/i2sbus-core.c	2006-07-07 12:15:45.857697157 +0200
+++ linux-2.6-fetch/sound/aoa/soundbus/i2sbus/i2sbus-core.c	2006-07-07 12:15:48.607697157 +0200
@@ -129,9 +129,6 @@ static int i2sbus_add_dev(struct macio_d
 	if (strncmp(np->name, "i2s-", 4))
 		return 0;
 
-	if (macio_irq_count(macio) != 3)
-		return 0;
-
 	dev = kzalloc(sizeof(struct i2sbus_dev), GFP_KERNEL);
 	if (!dev)
 		return 0;
@@ -183,10 +180,10 @@ static int i2sbus_add_dev(struct macio_d
 		snprintf(dev->rnames[i], sizeof(dev->rnames[i]), rnames[i], np->name);
 	}
 	for (i=0;i<3;i++) {
-		if (request_irq(macio_irq(macio, i), ints[i], 0,
-				dev->rnames[i], dev))
+		int irq = irq_of_parse_and_map(np, i);
+		if (request_irq(irq, ints[i], 0, dev->rnames[i], dev))
 			goto err;
-		dev->interrupts[i] = macio_irq(macio, i);
+		dev->interrupts[i] = irq;
 	}
 
 	for (i=0;i<3;i++) {

--

^ permalink raw reply

* [PATCH 11/12] aoa: layout fabric: add missing module aliases
From: Johannes Berg @ 2006-07-07 16:52 UTC (permalink / raw)
  To: linuxppc-dev
In-Reply-To: <20060707165207.877010000@sipsolutions.net>

The layout fabric gained support for all IDs when I extracted those from the
OSX description file. But apparently I had forgotten to add them all as
module aliases so the module will also load. This patch adds them.

Signed-off-by: Johannes Berg <johannes@sipsolutions.net>

--- linux-2.6-fetch.orig/sound/aoa/fabrics/snd-aoa-fabric-layout.c	2006-07-07 18:10:26.510223603 +0200
+++ linux-2.6-fetch/sound/aoa/fabrics/snd-aoa-fabric-layout.c	2006-07-07 18:12:09.690223603 +0200
@@ -77,24 +77,39 @@ struct layout {
 	int pcmid;
 };
 
+MODULE_ALIAS("sound-layout-36");
 MODULE_ALIAS("sound-layout-41");
 MODULE_ALIAS("sound-layout-45");
+MODULE_ALIAS("sound-layout-47");
+MODULE_ALIAS("sound-layout-48");
+MODULE_ALIAS("sound-layout-49");
+MODULE_ALIAS("sound-layout-50");
 MODULE_ALIAS("sound-layout-51");
+MODULE_ALIAS("sound-layout-56");
+MODULE_ALIAS("sound-layout-57");
 MODULE_ALIAS("sound-layout-58");
 MODULE_ALIAS("sound-layout-60");
 MODULE_ALIAS("sound-layout-61");
+MODULE_ALIAS("sound-layout-62");
 MODULE_ALIAS("sound-layout-64");
 MODULE_ALIAS("sound-layout-65");
+MODULE_ALIAS("sound-layout-66");
+MODULE_ALIAS("sound-layout-67");
 MODULE_ALIAS("sound-layout-68");
 MODULE_ALIAS("sound-layout-69");
 MODULE_ALIAS("sound-layout-70");
 MODULE_ALIAS("sound-layout-72");
+MODULE_ALIAS("sound-layout-76");
 MODULE_ALIAS("sound-layout-80");
 MODULE_ALIAS("sound-layout-82");
 MODULE_ALIAS("sound-layout-84");
 MODULE_ALIAS("sound-layout-86");
+MODULE_ALIAS("sound-layout-90");
 MODULE_ALIAS("sound-layout-92");
+MODULE_ALIAS("sound-layout-94");
 MODULE_ALIAS("sound-layout-96");
+MODULE_ALIAS("sound-layout-98");
+MODULE_ALIAS("sound-layout-100");
 
 /* onyx with all but microphone connected */
 static struct codec_connection onyx_connections_nomic[] = {

--

^ permalink raw reply

* [PATCH 04/12] aoa: fix when all is built into the kernel
From: Johannes Berg @ 2006-07-07 16:52 UTC (permalink / raw)
  To: linuxppc-dev
In-Reply-To: <20060707165207.877010000@sipsolutions.net>

This patch fixes initialisation issues when all of aoa is built
into the kernel by re-ordering the link order in the Makefile
and making the soundbus use subsys_initcall so it is initialised
earlier.

Signed-off-by: Benjamin Herrenschmidt <benh@kernel.crashing.org>
Signed-off-by: Johannes Berg <johannes@sipsolutions.net>

--- linux-2.6-fetch.orig/sound/aoa/Makefile	2006-07-07 12:15:40.067697157 +0200
+++ linux-2.6-fetch/sound/aoa/Makefile	2006-07-07 12:15:52.407697157 +0200
@@ -1,4 +1,4 @@
 obj-$(CONFIG_SND_AOA) += core/
-obj-$(CONFIG_SND_AOA) += codecs/
-obj-$(CONFIG_SND_AOA) += fabrics/
 obj-$(CONFIG_SND_AOA_SOUNDBUS) += soundbus/
+obj-$(CONFIG_SND_AOA) += fabrics/
+obj-$(CONFIG_SND_AOA) += codecs/
--- linux-2.6-fetch.orig/sound/aoa/soundbus/core.c	2006-07-07 12:15:40.137697157 +0200
+++ linux-2.6-fetch/sound/aoa/soundbus/core.c	2006-07-07 12:15:52.407697157 +0200
@@ -194,16 +194,6 @@ static struct bus_type soundbus_bus_type
 	.dev_attrs	= soundbus_dev_attrs,
 };
 
-static int __init soundbus_init(void)
-{
-	return bus_register(&soundbus_bus_type);
-}
-
-static void __exit soundbus_exit(void)
-{
-	bus_unregister(&soundbus_bus_type);
-}
-
 int soundbus_add_one(struct soundbus_dev *dev)
 {
 	static int devcount;
@@ -246,5 +236,15 @@ void soundbus_unregister_driver(struct s
 }
 EXPORT_SYMBOL_GPL(soundbus_unregister_driver);
 
-module_init(soundbus_init);
+static int __init soundbus_init(void)
+{
+	return bus_register(&soundbus_bus_type);
+}
+
+static void __exit soundbus_exit(void)
+{
+	bus_unregister(&soundbus_bus_type);
+}
+
+subsys_initcall(soundbus_init);
 module_exit(soundbus_exit);

--

^ permalink raw reply

* [PATCH 07/12] aoa fabric layout: clean up messages
From: Johannes Berg @ 2006-07-07 16:52 UTC (permalink / raw)
  To: linuxppc-dev
In-Reply-To: <20060707165207.877010000@sipsolutions.net>

This patch cleans up the printk's in the layout fabric and also
makes it display which type of GPIO access it is going to use.

Signed-off-by: Benjamin Herrenschmidt <benh@kernel.crashing.org>
Signed-off-by: Johannes Berg <johannes@sipsolutions.net>

--- linux-2.6-fetch.orig/sound/aoa/fabrics/snd-aoa-fabric-layout.c	2006-07-07 12:15:39.497697157 +0200
+++ linux-2.6-fetch/sound/aoa/fabrics/snd-aoa-fabric-layout.c	2006-07-07 12:15:55.057697157 +0200
@@ -950,11 +950,12 @@ static int aoa_fabric_layout_probe(struc
 	layout_id = (unsigned int *) get_property(sound, "layout-id", NULL);
 	if (!layout_id)
 		goto outnodev;
-	printk(KERN_INFO "snd-aoa-fabric-layout: found bus with layout %d ", *layout_id);
+	printk(KERN_INFO "snd-aoa-fabric-layout: found bus with layout %d\n",
+	       *layout_id);
 
 	layout = find_layout_by_id(*layout_id);
 	if (!layout) {
-		printk("(no idea how to handle)\n");
+		printk(KERN_ERR "snd-aoa-fabric-layout: unknown layout\n");
 		goto outnodev;
 	}
 
@@ -972,15 +973,17 @@ static int aoa_fabric_layout_probe(struc
 	case 51: /* PowerBook5,4 */
 	case 58: /* Mac Mini */
 		ldev->gpio.methods = ftr_gpio_methods;
+		printk(KERN_DEBUG
+		       "snd-aoa-fabric-layout: Using direct GPIOs\n");
 		break;
 	default:
 		ldev->gpio.methods = pmf_gpio_methods;
+		printk(KERN_DEBUG
+		       "snd-aoa-fabric-layout: Using PMF GPIOs\n");
 	}
 	ldev->selfptr_headphone.ptr = ldev;
 	ldev->selfptr_lineout.ptr = ldev;
 	sdev->ofdev.dev.driver_data = ldev;
-
-	printk("(using)\n");
 	list_add(&ldev->list, &layouts_list);
 	layouts_list_items++;
 

--

^ permalink raw reply

* [PATCH 02/12] aoa: i2sbus: move module parameter declaration up
From: Johannes Berg @ 2006-07-07 16:52 UTC (permalink / raw)
  To: linuxppc-dev
In-Reply-To: <20060707165207.877010000@sipsolutions.net>

This patch moves the i2sbus 'force' module parameter declaration
to the top of the file.

Signed-off-by: Benjamin Herrenschmidt <benh@kernel.crashing.org>
Signed-off-by: Johannes Berg <johannes@sipsolutions.net>

--- linux-2.6-fetch.orig/sound/aoa/soundbus/i2sbus/i2sbus-core.c	2006-07-07 12:15:48.607697157 +0200
+++ linux-2.6-fetch/sound/aoa/soundbus/i2sbus/i2sbus-core.c	2006-07-07 12:15:50.007697157 +0200
@@ -24,6 +24,11 @@ MODULE_DESCRIPTION("Apple Soundbus: I2S 
  * string that macio puts into the relevant device */
 MODULE_ALIAS("of:Ni2sTi2sC");
 
+static int force;
+module_param(force, int, 0444);
+MODULE_PARM_DESC(force, "Force loading i2sbus even when"
+			" no layout-id property is present");
+
 static struct of_device_id i2sbus_match[] = {
 	{ .name = "i2s" },
 	{ }
@@ -101,11 +106,6 @@ static irqreturn_t i2sbus_bus_intr(int i
 	return IRQ_HANDLED;
 }
 
-static int force;
-module_param(force, int, 0444);
-MODULE_PARM_DESC(force, "Force loading i2sbus even when"
-			" no layout-id property is present");
-
 /* FIXME: look at device node refcounting */
 static int i2sbus_add_dev(struct macio_dev *macio,
 			  struct i2sbus_control *control,

--

^ permalink raw reply

* [PATCH 12/12] aoa: tas: add missing bass/treble controls
From: Johannes Berg @ 2006-07-07 16:52 UTC (permalink / raw)
  To: linuxppc-dev
In-Reply-To: <20060707165207.877010000@sipsolutions.net>

This patch adds the bass/treble controls to snd-aoa that snd-powermac always
had for tas3004 based machines.

Signed-off-by: Johannes Berg <johannes@sipsolutions.net>

--- /dev/null	1970-01-01 00:00:00.000000000 +0000
+++ linux-2.6-fetch/sound/aoa/codecs/snd-aoa-codec-tas-basstreble.h	2006-07-07 18:15:40.800223603 +0200
@@ -0,0 +1,134 @@
+/*
+ * This file is only included exactly once!
+ *
+ * The tables here are derived from the tas3004 datasheet,
+ * modulo typo corrections and some smoothing...
+ */
+
+#define TAS3004_TREBLE_MIN	0
+#define TAS3004_TREBLE_MAX	72
+#define TAS3004_BASS_MIN	0
+#define TAS3004_BASS_MAX	72
+#define TAS3004_TREBLE_ZERO	36
+#define TAS3004_BASS_ZERO	36
+
+static u8 tas3004_treble_table[] = {
+	150, /* -18 dB */
+	149,
+	148,
+	147,
+	146,
+	145,
+	144,
+	143,
+	142,
+	141,
+	140,
+	139,
+	138,
+	137,
+	136,
+	135,
+	134,
+	133,
+	132,
+	131,
+	130,
+	129,
+	128,
+	127,
+	126,
+	125,
+	124,
+	123,
+	122,
+	121,
+	120,
+	119,
+	118,
+	117,
+	116,
+	115,
+	114, /* 0 dB */
+	113,
+	112,
+	111,
+	109,
+	108,
+	107,
+	105,
+	104,
+	103,
+	101,
+	99,
+	98,
+	96,
+	93,
+	91,
+	89,
+	86,
+	83,
+	81,
+	77,
+	74,
+	71,
+	67,
+	63,
+	59,
+	54,
+	49,
+	44,
+	38,
+	32,
+	26,
+	19,
+	10,
+	4,
+	2,
+	1, /* +18 dB */
+};
+
+static inline u8 tas3004_treble(int idx)
+{
+	return tas3004_treble_table[idx];
+}
+
+/* I only save the difference here to the treble table
+ * so that the binary is smaller...
+ * I have also ignored completely differences of
+ * +/- 1
+ */
+static s8 tas3004_bass_diff_to_treble[] = {
+	2, /* 7 dB, offset 50 */
+	2,
+	2,
+	2,
+	2,
+	1,
+	2,
+	2,
+	2,
+	3,
+	4,
+	4,
+	5,
+	6,
+	7,
+	8,
+	9,
+	10,
+	11,
+	14,
+	13,
+	8,
+	1, /* 18 dB */
+};
+
+static inline u8 tas3004_bass(int idx)
+{
+	u8 result = tas3004_treble_table[idx];
+
+	if (idx >= 50)
+		result += tas3004_bass_diff_to_treble[idx-50];
+	return result;
+}
--- linux-2.6-fetch.orig/sound/aoa/codecs/snd-aoa-codec-tas.c	2006-07-07 18:10:09.470223603 +0200
+++ linux-2.6-fetch/sound/aoa/codecs/snd-aoa-codec-tas.c	2006-07-07 18:25:07.330223603 +0200
@@ -72,6 +72,7 @@ MODULE_DESCRIPTION("tas codec driver for
 
 #include "snd-aoa-codec-tas.h"
 #include "snd-aoa-codec-tas-gain-table.h"
+#include "snd-aoa-codec-tas-basstreble.h"
 #include "../aoa.h"
 #include "../soundbus/soundbus.h"
 
@@ -87,6 +88,7 @@ struct tas {
 				hw_enabled:1;
 	u8			cached_volume_l, cached_volume_r;
 	u8			mixer_l[3], mixer_r[3];
+	u8			bass, treble;
 	u8			acr;
 	int			drc_range;
 };
@@ -128,6 +130,22 @@ static void tas3004_set_drc(struct tas *
 	tas_write_reg(tas, TAS_REG_DRC, 6, val);
 }
 
+static void tas_set_treble(struct tas *tas)
+{
+	u8 tmp;
+
+	tmp = tas3004_treble(tas->treble);
+	tas_write_reg(tas, TAS_REG_TREBLE, 1, &tmp);
+}
+
+static void tas_set_bass(struct tas *tas)
+{
+	u8 tmp;
+
+	tmp = tas3004_bass(tas->bass);
+	tas_write_reg(tas, TAS_REG_BASS, 1, &tmp);
+}
+
 static void tas_set_volume(struct tas *tas)
 {
 	u8 block[6];
@@ -485,6 +503,89 @@ static struct snd_kcontrol_new capture_s
 	.put = tas_snd_capture_source_put,
 };
 
+static int tas_snd_treble_info(struct snd_kcontrol *kcontrol,
+	struct snd_ctl_elem_info *uinfo)
+{
+	uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
+	uinfo->count = 1;
+	uinfo->value.integer.min = TAS3004_TREBLE_MIN;
+	uinfo->value.integer.max = TAS3004_TREBLE_MAX;
+	return 0;
+}
+
+static int tas_snd_treble_get(struct snd_kcontrol *kcontrol,
+	struct snd_ctl_elem_value *ucontrol)
+{
+	struct tas *tas = snd_kcontrol_chip(kcontrol);
+
+	ucontrol->value.integer.value[0] = tas->treble;
+	return 0;
+}
+
+static int tas_snd_treble_put(struct snd_kcontrol *kcontrol,
+	struct snd_ctl_elem_value *ucontrol)
+{
+	struct tas *tas = snd_kcontrol_chip(kcontrol);
+
+	if (tas->treble == ucontrol->value.integer.value[0])
+		return 0;
+
+	tas->treble = ucontrol->value.integer.value[0];
+	if (tas->hw_enabled)
+		tas_set_treble(tas);
+	return 1;
+}
+
+static struct snd_kcontrol_new treble_control = {
+	.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+	.name = "Treble",
+	.access = SNDRV_CTL_ELEM_ACCESS_READWRITE,
+	.info = tas_snd_treble_info,
+	.get = tas_snd_treble_get,
+	.put = tas_snd_treble_put,
+};
+
+static int tas_snd_bass_info(struct snd_kcontrol *kcontrol,
+	struct snd_ctl_elem_info *uinfo)
+{
+	uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
+	uinfo->count = 1;
+	uinfo->value.integer.min = TAS3004_BASS_MIN;
+	uinfo->value.integer.max = TAS3004_BASS_MAX;
+	return 0;
+}
+
+static int tas_snd_bass_get(struct snd_kcontrol *kcontrol,
+	struct snd_ctl_elem_value *ucontrol)
+{
+	struct tas *tas = snd_kcontrol_chip(kcontrol);
+
+	ucontrol->value.integer.value[0] = tas->bass;
+	return 0;
+}
+
+static int tas_snd_bass_put(struct snd_kcontrol *kcontrol,
+	struct snd_ctl_elem_value *ucontrol)
+{
+	struct tas *tas = snd_kcontrol_chip(kcontrol);
+
+	if (tas->bass == ucontrol->value.integer.value[0])
+		return 0;
+
+	tas->bass = ucontrol->value.integer.value[0];
+	if (tas->hw_enabled)
+		tas_set_bass(tas);
+	return 1;
+}
+
+static struct snd_kcontrol_new bass_control = {
+	.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+	.name = "Bass",
+	.access = SNDRV_CTL_ELEM_ACCESS_READWRITE,
+	.info = tas_snd_bass_info,
+	.get = tas_snd_bass_get,
+	.put = tas_snd_bass_put,
+};
 
 static struct transfer_info tas_transfers[] = {
 	{
@@ -541,9 +642,10 @@ static int tas_reset_init(struct tas *ta
 	tas3004_set_drc(tas);
 
 	/* Set treble & bass to 0dB */
-	tmp = 114;
-	tas_write_reg(tas, TAS_REG_TREBLE, 1, &tmp);
-	tas_write_reg(tas, TAS_REG_BASS, 1, &tmp);
+	tas->treble = TAS3004_TREBLE_ZERO;
+	tas->bass = TAS3004_BASS_ZERO;
+	tas_set_treble(tas);
+	tas_set_bass(tas);
 
 	tas->acr &= ~TAS_ACR_ANALOG_PDOWN;
 	if (tas_write_reg(tas, TAS_REG_ACR, 1, &tas->acr))
@@ -682,6 +784,14 @@ static int tas_init_codec(struct aoa_cod
 	if (err)
 		goto error;
 
+	err = aoa_snd_ctl_add(snd_ctl_new1(&treble_control, tas));
+	if (err)
+		goto error;
+
+	err = aoa_snd_ctl_add(snd_ctl_new1(&bass_control, tas));
+	if (err)
+		goto error;
+
 	return 0;
  error:
 	tas->codec.soundbus_dev->detach_codec(tas->codec.soundbus_dev, tas);

--

^ permalink raw reply

* [PATCH 03/12] aoa: i2sbus: fix for PowerMac7,2 and 7,3
From: Johannes Berg @ 2006-07-07 16:52 UTC (permalink / raw)
  To: linuxppc-dev
In-Reply-To: <20060707165207.877010000@sipsolutions.net>

This patch cleans up the resource handling in i2sbus and adds
workarounds for the broken device trees on the PowerMac7,2
and 7,3.
Some of this code will later move again when macio_asic is
going to export all the sub-nodes too.

Signed-off-by: Benjamin Herrenschmidt <benh@kernel.crashing.org>
Signed-off-by: Johannes Berg <johannes@sipsolutions.net>

--- linux-2.6-fetch.orig/sound/aoa/soundbus/i2sbus/i2sbus-core.c	2006-07-07 12:15:50.007697157 +0200
+++ linux-2.6-fetch/sound/aoa/soundbus/i2sbus/i2sbus-core.c	2006-07-07 12:15:51.277697157 +0200
@@ -7,13 +7,16 @@
  */
 
 #include <linux/module.h>
-#include <asm/macio.h>
-#include <asm/dbdma.h>
 #include <linux/pci.h>
 #include <linux/interrupt.h>
+#include <linux/dma-mapping.h>
+
 #include <sound/driver.h>
 #include <sound/core.h>
-#include <linux/dma-mapping.h>
+
+#include <asm/macio.h>
+#include <asm/dbdma.h>
+
 #include "../soundbus.h"
 #include "i2sbus.h"
 
@@ -78,12 +81,12 @@ static void i2sbus_release_dev(struct de
  	if (i2sdev->intfregs) iounmap(i2sdev->intfregs);
  	if (i2sdev->out.dbdma) iounmap(i2sdev->out.dbdma);
  	if (i2sdev->in.dbdma) iounmap(i2sdev->in.dbdma);
-	for (i=0;i<3;i++)
+	for (i = aoa_resource_i2smmio; i <= aoa_resource_rxdbdma; i++)
 		if (i2sdev->allocated_resource[i])
 			release_and_free_resource(i2sdev->allocated_resource[i]);
 	free_dbdma_descriptor_ring(i2sdev, &i2sdev->out.dbdma_ring);
 	free_dbdma_descriptor_ring(i2sdev, &i2sdev->in.dbdma_ring);
-	for (i=0;i<3;i++)
+	for (i = aoa_resource_i2smmio; i <= aoa_resource_rxdbdma; i++)
 		free_irq(i2sdev->interrupts[i], i2sdev);
 	i2sbus_control_remove_dev(i2sdev->control, i2sdev);
 	mutex_destroy(&i2sdev->lock);
@@ -106,6 +109,50 @@ static irqreturn_t i2sbus_bus_intr(int i
 	return IRQ_HANDLED;
 }
 
+
+/*
+ * XXX FIXME: We test the layout_id's here to get the proper way of
+ * mapping in various registers, thanks to bugs in Apple device-trees.
+ * We could instead key off the machine model and the name of the i2s
+ * node (i2s-a). This we'll do when we move it all to macio_asic.c
+ * and have that export items for each sub-node too.
+ */
+static int i2sbus_get_and_fixup_rsrc(struct device_node *np, int index,
+				     int layout, struct resource *res)
+{
+	struct device_node *parent;
+	int pindex, rc = -ENXIO;
+	u32 *reg;
+
+	/* Machines with layout 76 and 36 (K2 based) have a weird device
+	 * tree what we need to special case.
+	 * Normal machines just fetch the resource from the i2s-X node.
+	 * Darwin further divides normal machines into old and new layouts
+	 * with a subtely different code path but that doesn't seem necessary
+	 * in practice, they just bloated it. In addition, even on our K2
+	 * case the i2s-modem node, if we ever want to handle it, uses the
+	 * normal layout
+	 */
+	if (layout != 76 && layout != 36)
+		return of_address_to_resource(np, index, res);
+
+	parent = of_get_parent(np);
+	pindex = (index == aoa_resource_i2smmio) ? 0 : 1;
+	rc = of_address_to_resource(parent, pindex, res);
+	if (rc)
+		goto bail;
+	reg = (u32 *)get_property(np, "reg", NULL);
+	if (reg == NULL) {
+		rc = -ENXIO;
+		goto bail;
+	}
+	res->start += reg[index * 2];
+	res->end = res->start + reg[index * 2 + 1] - 1;
+ bail:
+	of_node_put(parent);
+	return rc;
+}
+
 /* FIXME: look at device node refcounting */
 static int i2sbus_add_dev(struct macio_dev *macio,
 			  struct i2sbus_control *control,
@@ -113,7 +160,8 @@ static int i2sbus_add_dev(struct macio_d
 {
 	struct i2sbus_dev *dev;
 	struct device_node *child = NULL, *sound = NULL;
-	int i;
+	struct resource *r;
+	int i, layout = 0, rlen;
 	static const char *rnames[] = { "i2sbus: %s (control)",
 					"i2sbus: %s (tx)",
 					"i2sbus: %s (rx)" };
@@ -144,8 +192,9 @@ static int i2sbus_add_dev(struct macio_d
 		u32 *layout_id;
 		layout_id = (u32*) get_property(sound, "layout-id", NULL);
 		if (layout_id) {
+			layout = *layout_id;
 			snprintf(dev->sound.modalias, 32,
-				 "sound-layout-%d", *layout_id);
+				 "sound-layout-%d", layout);
 			force = 1;
 		}
 	}
@@ -175,23 +224,32 @@ static int i2sbus_add_dev(struct macio_d
 	dev->bus_number = np->name[4] - 'a';
 	INIT_LIST_HEAD(&dev->sound.codec_list);
 
-	for (i=0;i<3;i++) {
+	for (i = aoa_resource_i2smmio; i <= aoa_resource_rxdbdma; i++) {
 		dev->interrupts[i] = -1;
-		snprintf(dev->rnames[i], sizeof(dev->rnames[i]), rnames[i], np->name);
+		snprintf(dev->rnames[i], sizeof(dev->rnames[i]),
+			 rnames[i], np->name);
 	}
-	for (i=0;i<3;i++) {
+	for (i = aoa_resource_i2smmio; i <= aoa_resource_rxdbdma; i++) {
 		int irq = irq_of_parse_and_map(np, i);
 		if (request_irq(irq, ints[i], 0, dev->rnames[i], dev))
 			goto err;
 		dev->interrupts[i] = irq;
 	}
 
-	for (i=0;i<3;i++) {
-		if (of_address_to_resource(np, i, &dev->resources[i]))
+
+	/* Resource handling is problematic as some device-trees contain
+	 * useless crap (ugh ugh ugh). We work around that here by calling
+	 * specific functions for calculating the appropriate resources.
+	 *
+	 * This will all be moved to macio_asic.c at one point
+	 */
+	for (i = aoa_resource_i2smmio; i <= aoa_resource_rxdbdma; i++) {
+		if (i2sbus_get_and_fixup_rsrc(np,i,layout,&dev->resources[i]))
 			goto err;
-		/* if only we could use our resource dev->resources[i]...
+		/* If only we could use our resource dev->resources[i]...
 		 * but request_resource doesn't know about parents and
-		 * contained resources... */
+		 * contained resources...
+		 */
 		dev->allocated_resource[i] = 
 			request_mem_region(dev->resources[i].start,
 					   dev->resources[i].end -
@@ -202,13 +260,25 @@ static int i2sbus_add_dev(struct macio_d
 			goto err;
 		}
 	}
-	/* should do sanity checking here about length of them */
-	dev->intfregs = ioremap(dev->resources[0].start,
-				dev->resources[0].end-dev->resources[0].start+1);
-	dev->out.dbdma = ioremap(dev->resources[1].start,
-			 	 dev->resources[1].end-dev->resources[1].start+1);
-	dev->in.dbdma = ioremap(dev->resources[2].start,
-				dev->resources[2].end-dev->resources[2].start+1);
+
+	r = &dev->resources[aoa_resource_i2smmio];
+	rlen = r->end - r->start + 1;
+	if (rlen < sizeof(struct i2s_interface_regs))
+		goto err;
+	dev->intfregs = ioremap(r->start, rlen);
+
+	r = &dev->resources[aoa_resource_txdbdma];
+	rlen = r->end - r->start + 1;
+	if (rlen < sizeof(struct dbdma_regs))
+		goto err;
+	dev->out.dbdma = ioremap(r->start, rlen);
+
+	r = &dev->resources[aoa_resource_rxdbdma];
+	rlen = r->end - r->start + 1;
+	if (rlen < sizeof(struct dbdma_regs))
+		goto err;
+	dev->in.dbdma = ioremap(r->start, rlen);
+
 	if (!dev->intfregs || !dev->out.dbdma || !dev->in.dbdma)
 		goto err;
 
--- linux-2.6-fetch.orig/sound/aoa/soundbus/i2sbus/i2sbus.h	2006-07-07 12:15:40.337697157 +0200
+++ linux-2.6-fetch/sound/aoa/soundbus/i2sbus/i2sbus.h	2006-07-07 12:15:51.277697157 +0200
@@ -45,6 +45,12 @@ struct pcm_info {
 	volatile struct dbdma_regs __iomem *dbdma;
 };
 
+enum {
+	aoa_resource_i2smmio = 0,
+	aoa_resource_txdbdma,
+	aoa_resource_rxdbdma,
+};
+
 struct i2sbus_dev {
 	struct soundbus_dev sound;
 	struct macio_dev *macio;

--

^ permalink raw reply

* [PATCH 00/12] aoa: fix regressions over snd-powermac
From: Johannes Berg @ 2006-07-07 16:52 UTC (permalink / raw)
  To: linuxppc-dev

This patch series fixes the remaining regressions over snd-powermac that I
am aware of. Most of the important patches are from BenH, thanks!

Most notably:
 * works on PowerMac7,2/7,3 now
 * adds DRC,bass and treble controls to tas3004-based machines

I've picked up Andreas' patch as the first patch in this series because the
rest of the series depends on it and it isn't in any tree yet.

Please merge for 2.6.18.

johannes

^ permalink raw reply

* [PATCH 05/12] aoa: i2sbus: revamp control layer
From: Johannes Berg @ 2006-07-07 16:52 UTC (permalink / raw)
  To: linuxppc-dev
In-Reply-To: <20060707165207.877010000@sipsolutions.net>

This patch revamps the i2sbus control layer by using the macio/keylargo
functions instead of directly mapping.

Signed-off-by: Benjamin Herrenschmidt <benh@kernel.crashing.org>
Signed-off-by: Johannes Berg <johannes@sipsolutions.net>

--- linux-2.6-fetch.orig/sound/aoa/soundbus/i2sbus/i2sbus.h	2006-07-07 12:15:51.277697157 +0200
+++ linux-2.6-fetch/sound/aoa/soundbus/i2sbus/i2sbus.h	2006-07-07 12:15:53.277697157 +0200
@@ -7,20 +7,22 @@
  */
 #ifndef __I2SBUS_H
 #define __I2SBUS_H
-#include <asm/dbdma.h>
 #include <linux/interrupt.h>
-#include <sound/pcm.h>
 #include <linux/spinlock.h>
 #include <linux/mutex.h>
+
+#include <sound/pcm.h>
+
 #include <asm/prom.h>
+#include <asm/pmac_feature.h>
+#include <asm/dbdma.h>
+
 #include "i2sbus-interface.h"
-#include "i2sbus-control.h"
 #include "../soundbus.h"
 
 struct i2sbus_control {
-	volatile struct i2s_control_regs __iomem *controlregs;
-	struct resource rsrc;
 	struct list_head list;
+	struct macio_chip *macio;
 };
 
 #define MAX_DBDMA_COMMANDS	32
--- linux-2.6-fetch.orig/sound/aoa/soundbus/i2sbus/i2sbus-control.c	2006-07-07 12:15:39.837697157 +0200
+++ linux-2.6-fetch/sound/aoa/soundbus/i2sbus/i2sbus-control.c	2006-07-07 12:15:53.277697157 +0200
@@ -6,12 +6,16 @@
  * GPL v2, can be found in COPYING.
  */
 
-#include <asm/io.h>
+#include <linux/kernel.h>
 #include <linux/delay.h>
+
+#include <asm/io.h>
 #include <asm/prom.h>
 #include <asm/macio.h>
 #include <asm/pmac_feature.h>
 #include <asm/pmac_pfunc.h>
+#include <asm/keylargo.h>
+
 #include "i2sbus.h"
 
 int i2sbus_control_init(struct macio_dev* dev, struct i2sbus_control **c)
@@ -22,26 +26,12 @@ int i2sbus_control_init(struct macio_dev
 
 	INIT_LIST_HEAD(&(*c)->list);
 
-	if (of_address_to_resource(dev->ofdev.node, 0, &(*c)->rsrc))
-		goto err;
-	/* we really should be using feature calls instead of mapping
-	 * these registers. It's safe for now since no one else is
-	 * touching them... */
-	(*c)->controlregs = ioremap((*c)->rsrc.start,
-				    sizeof(struct i2s_control_regs));
-	if (!(*c)->controlregs)
-		goto err;
-
+	(*c)->macio = dev->bus->chip;
 	return 0;
- err:
-	kfree(*c);
-	*c = NULL;
-	return -ENODEV;
 }
 
 void i2sbus_control_destroy(struct i2sbus_control *c)
 {
-	iounmap(c->controlregs);
 	kfree(c);
 }
 
@@ -93,19 +83,22 @@ int i2sbus_control_enable(struct i2sbus_
 			  struct i2sbus_dev *i2sdev)
 {
 	struct pmf_args args = { .count = 0 };
-	int cc;
+	struct macio_chip *macio = c->macio;
 
 	if (i2sdev->enable)
 		return pmf_call_one(i2sdev->enable, &args);
 
+	if (macio == NULL || macio->base == NULL)
+		return -ENODEV;
+
 	switch (i2sdev->bus_number) {
 	case 0:
-		cc = in_le32(&c->controlregs->cell_control);
-		out_le32(&c->controlregs->cell_control, cc | CTRL_CLOCK_INTF_0_ENABLE);
+		/* these need to be locked or done through
+		 * newly created feature calls! */
+		MACIO_BIS(KEYLARGO_FCR1, KL1_I2S0_ENABLE);
 		break;
 	case 1:
-		cc = in_le32(&c->controlregs->cell_control);
-		out_le32(&c->controlregs->cell_control, cc | CTRL_CLOCK_INTF_1_ENABLE);
+		MACIO_BIS(KEYLARGO_FCR1, KL1_I2S1_ENABLE);
 		break;
 	default:
 		return -ENODEV;
@@ -118,7 +111,7 @@ int i2sbus_control_cell(struct i2sbus_co
 			int enable)
 {
 	struct pmf_args args = { .count = 0 };
-	int cc;
+	struct macio_chip *macio = c->macio;
 
 	switch (enable) {
 	case 0:
@@ -133,18 +126,22 @@ int i2sbus_control_cell(struct i2sbus_co
 		printk(KERN_ERR "i2sbus: INVALID CELL ENABLE VALUE\n");
 		return -ENODEV;
 	}
+
+	if (macio == NULL || macio->base == NULL)
+		return -ENODEV;
+
 	switch (i2sdev->bus_number) {
 	case 0:
-		cc = in_le32(&c->controlregs->cell_control);
-		cc &= ~CTRL_CLOCK_CELL_0_ENABLE;
-		cc |= enable * CTRL_CLOCK_CELL_0_ENABLE;
-		out_le32(&c->controlregs->cell_control, cc);
+		if (enable)
+			MACIO_BIS(KEYLARGO_FCR1, KL1_I2S0_CELL_ENABLE);
+		else
+			MACIO_BIC(KEYLARGO_FCR1, KL1_I2S0_CELL_ENABLE);
 		break;
 	case 1:
-		cc = in_le32(&c->controlregs->cell_control);
-		cc &= ~CTRL_CLOCK_CELL_1_ENABLE;
-		cc |= enable * CTRL_CLOCK_CELL_1_ENABLE;
-		out_le32(&c->controlregs->cell_control, cc);
+		if (enable)
+			MACIO_BIS(KEYLARGO_FCR1, KL1_I2S1_CELL_ENABLE);
+		else
+			MACIO_BIC(KEYLARGO_FCR1, KL1_I2S1_CELL_ENABLE);
 		break;
 	default:
 		return -ENODEV;
@@ -157,7 +154,7 @@ int i2sbus_control_clock(struct i2sbus_c
 			 int enable)
 {
 	struct pmf_args args = { .count = 0 };
-	int cc;
+	struct macio_chip *macio = c->macio;
 
 	switch (enable) {
 	case 0:
@@ -172,18 +169,22 @@ int i2sbus_control_clock(struct i2sbus_c
 		printk(KERN_ERR "i2sbus: INVALID CLOCK ENABLE VALUE\n");
 		return -ENODEV;
 	}
+
+	if (macio == NULL || macio->base == NULL)
+		return -ENODEV;
+
 	switch (i2sdev->bus_number) {
 	case 0:
-		cc = in_le32(&c->controlregs->cell_control);
-		cc &= ~CTRL_CLOCK_CLOCK_0_ENABLE;
-		cc |= enable * CTRL_CLOCK_CLOCK_0_ENABLE;
-		out_le32(&c->controlregs->cell_control, cc);
+		if (enable)
+			MACIO_BIS(KEYLARGO_FCR1, KL1_I2S0_CLK_ENABLE_BIT);
+		else
+			MACIO_BIC(KEYLARGO_FCR1, KL1_I2S0_CLK_ENABLE_BIT);
 		break;
 	case 1:
-		cc = in_le32(&c->controlregs->cell_control);
-		cc &= ~CTRL_CLOCK_CLOCK_1_ENABLE;
-		cc |= enable * CTRL_CLOCK_CLOCK_1_ENABLE;
-		out_le32(&c->controlregs->cell_control, cc);
+		if (enable)
+			MACIO_BIS(KEYLARGO_FCR1, KL1_I2S1_CLK_ENABLE_BIT);
+		else
+			MACIO_BIC(KEYLARGO_FCR1, KL1_I2S1_CLK_ENABLE_BIT);
 		break;
 	default:
 		return -ENODEV;
--- linux-2.6-fetch.orig/sound/aoa/soundbus/i2sbus/i2sbus-control.h	2006-07-07 12:15:39.887697157 +0200
+++ /dev/null	1970-01-01 00:00:00.000000000 +0000
@@ -1,37 +0,0 @@
-/*
- * i2sbus driver -- bus register definitions
- *
- * Copyright 2006 Johannes Berg <johannes@sipsolutions.net>
- *
- * GPL v2, can be found in COPYING.
- */
-#ifndef __I2SBUS_CONTROLREGS_H
-#define __I2SBUS_CONTROLREGS_H
-
-/* i2s control registers, at least what we know about them */
-
-#define __PAD(m,n) u8 __pad##m[n]
-#define _PAD(line, n) __PAD(line, n)
-#define PAD(n) _PAD(__LINE__, (n))
-struct i2s_control_regs {
-	PAD(0x38);
-	__le32 fcr0;		/* 0x38 (unknown) */
-	__le32 cell_control;	/* 0x3c (fcr1) */
-	__le32 fcr2;		/* 0x40 (unknown) */
-	__le32 fcr3;		/* 0x44 (fcr3) */
-	__le32 clock_control;	/* 0x48 (unknown) */
-	PAD(4);
-	/* total size: 0x50 bytes */
-}  __attribute__((__packed__));
-
-#define CTRL_CLOCK_CELL_0_ENABLE	(1<<10)
-#define CTRL_CLOCK_CLOCK_0_ENABLE	(1<<12)
-#define CTRL_CLOCK_SWRESET_0		(1<<11)
-#define CTRL_CLOCK_INTF_0_ENABLE	(1<<13)
-
-#define CTRL_CLOCK_CELL_1_ENABLE	(1<<17)
-#define CTRL_CLOCK_CLOCK_1_ENABLE	(1<<18)
-#define CTRL_CLOCK_SWRESET_1		(1<<19)
-#define CTRL_CLOCK_INTF_1_ENABLE	(1<<20)
-
-#endif /* __I2SBUS_CONTROLREGS_H */

--

^ permalink raw reply

* snd-aoa: Add sound-layout-36 alias
From: Andreas Schwab @ 2006-07-07 17:18 UTC (permalink / raw)
  To: Johannes Berg; +Cc: linuxppc-dev list, alsa-devel

Signed-off-by: Andreas Schwab <schwab@suse.de>

---
 sound/aoa/fabrics/snd-aoa-fabric-layout.c |    1 +
 1 file changed, 1 insertion(+)

Index: linux-2.6.17-git13/sound/aoa/fabrics/snd-aoa-fabric-layout.c
===================================================================
--- linux-2.6.17-git13.orig/sound/aoa/fabrics/snd-aoa-fabric-layout.c	2006-06-29 00:59:52.000000000 +0200
+++ linux-2.6.17-git13/sound/aoa/fabrics/snd-aoa-fabric-layout.c	2006-06-29 01:06:02.000000000 +0200
@@ -77,6 +77,7 @@ struct layout {
 	int pcmid;
 };
 
+MODULE_ALIAS("sound-layout-36");
 MODULE_ALIAS("sound-layout-41");
 MODULE_ALIAS("sound-layout-45");
 MODULE_ALIAS("sound-layout-51");

Andreas.

-- 
Andreas Schwab, SuSE Labs, schwab@suse.de
SuSE Linux Products GmbH, Maxfeldstraße 5, 90409 Nürnberg, Germany
PGP key fingerprint = 58CA 54C7 6D53 942B 1756  01D3 44D5 214B 8276 4ED5
"And now for something completely different."

^ permalink raw reply

* Re: [Alsa-devel] snd-aoa: Add sound-layout-36 alias
From: Johannes Berg @ 2006-07-07 17:22 UTC (permalink / raw)
  To: Andreas Schwab; +Cc: linuxppc-dev list, alsa-devel
In-Reply-To: <jed5chjpuj.fsf@sykes.suse.de>

[-- Attachment #1: Type: text/plain, Size: 206 bytes --]

On Fri, 2006-07-07 at 19:18 +0200, Andreas Schwab wrote:
>  
> +MODULE_ALIAS("sound-layout-36");

Heh. I just sent a patch (in my series) that adds all the ones that were
still missing :)

johannes

[-- Attachment #2: This is a digitally signed message part --]
[-- Type: application/pgp-signature, Size: 828 bytes --]

^ permalink raw reply

* Re: G5 troubles booting powerpc-git (July 6)
From: Andrew Morton @ 2006-07-07 18:04 UTC (permalink / raw)
  To: Benjamin Herrenschmidt; +Cc: linuxppc-dev
In-Reply-To: <1152272710.9862.74.camel@localhost.localdomain>

On Fri, 07 Jul 2006 21:45:10 +1000
Benjamin Herrenschmidt <benh@kernel.crashing.org> wrote:

> On Fri, 2006-07-07 at 02:11 -0700, Andrew Morton wrote:
> > On Fri, 07 Jul 2006 18:57:47 +1000
> > Benjamin Herrenschmidt <benh@kernel.crashing.org> wrote:
> > 
> > > 
> > > In the meantime, try on your quad using a g5_defconfig
> > 
> > I tried it with your recently-sent patch applied.  It still dies in
> > smu_sensors_init().
> 
> Do you have lockdep ? 

Nope.

^ permalink raw reply

* Re: Linux v2.6.18-rc1
From: Steve Fox @ 2006-07-07 15:41 UTC (permalink / raw)
  To: linuxppc-dev; +Cc: linux-kernel
In-Reply-To: <Pine.LNX.4.64.0607052115210.12404@g5.osdl.org>

We've got a ppc64 machine that won't boot with this due to an IDE error.

[snip]
Freeing unused kernel memory: 256k freed
 running (1:2) /init autobench_args: ABAT:1152213829

creating device nodes .hda: lost interrupt
hda: lost interrupt
hda: lost interrupt
hda: lost interrupt
hda: lost interrupt
hda: lost interrupt
hda: lost interrupt
hda: lost interrupt
hda: lost interrupt

-- 

Steve Fox
IBM Linux Technology Center

^ permalink raw reply

* MPC5200 boot giving "request_module: runaway loop modprobe binfmt-4c46" errors
From: gturnock @ 2006-07-07 22:15 UTC (permalink / raw)
  To: linuxppc-embedded

[-- Attachment #1: Type: text/html, Size: 16545 bytes --]

^ permalink raw reply

* [PATCH maple] Fix new interrupt code (MPIC endianness)
From: Segher Boessenkool @ 2006-07-08  0:37 UTC (permalink / raw)
  To: linuxppc-dev; +Cc: Paul Mackerras

All U3/U4 based systems are big-endian, not all express it in their
device trees.

Signed-off-by: Segher Boessenkool <segher@kernel.crashing.org>
Acked-by: Benjamin Herrenschmidt <benh@kernel.crashing.org>

---
Index: linux-powerpc/arch/powerpc/platforms/maple/setup.c
===================================================================
--- linux-powerpc.orig/arch/powerpc/platforms/maple/setup.c	2006-07-03 19:56:28.469294976 +0200
+++ linux-powerpc/arch/powerpc/platforms/maple/setup.c	2006-07-03 20:05:01.121380920 +0200
@@ -259,6 +259,8 @@
 
 	/* XXX Maple specific bits */
 	flags |= MPIC_BROKEN_U3 | MPIC_WANTS_RESET;
+	/* All U3/U4 are big-endian, older SLOF firmware doesn't encode this */
+	flags |= MPIC_BIG_ENDIAN;
 
 	/* Setup the openpic driver. More device-tree junks, we hard code no
 	 * ISUs for now. I'll have to revisit some stuffs with the folks doing

^ permalink raw reply

* [PATCH maple] Fix new interrupt code (MPIC detection)
From: Segher Boessenkool @ 2006-07-08  0:37 UTC (permalink / raw)
  To: linuxppc-dev; +Cc: Paul Mackerras

As the code comment already says, the Maple device-tree is incorrect here;
make the Linux code detect the correct thing, too.

Signed-off-by: Segher Boessenkool <segher@kernel.crashing.org>
Acked-by: Benjamin Herrenschmidt <benh@kernel.crashing.org>

---
Index: linux-powerpc/arch/powerpc/platforms/maple/setup.c
===================================================================
--- linux-powerpc.orig/arch/powerpc/platforms/maple/setup.c	2006-07-03 19:38:29.597405696 +0200
+++ linux-powerpc/arch/powerpc/platforms/maple/setup.c	2006-07-03 19:56:28.469294976 +0200
@@ -221,10 +221,17 @@
 	 * in Maple device-tree where the type of the controller is
 	 * open-pic and not interrupt-controller
 	 */
-	for_each_node_by_type(np, "open-pic") {
-		mpic_node = np;
-		break;
-	}
+
+	for_each_node_by_type(np, "interrupt-controller")
+		if (device_is_compatible(np, "open-pic")) {
+			mpic_node = np;
+			break;
+		}
+	if (mpic_node == NULL)
+		for_each_node_by_type(np, "open-pic") {
+			mpic_node = np;
+			break;
+		}
 	if (mpic_node == NULL) {
 		printk(KERN_ERR
 		       "Failed to locate the MPIC interrupt controller\n");

^ permalink raw reply

* help? baffled trying to figure out where time is being spent
From: Chris Friesen @ 2006-07-08  2:05 UTC (permalink / raw)
  To: linuxppc-dev

Hi guys,

I'm looking at the following instrumented code.  This is part of an 
instrumented version of scheduler_tick(), running on what is essentially 
a "maple" board.  Dual 970fx, 4GB of memory.  Modified 2.6.10.

Shortly after initial boot, on cpu1, it seems like somehow there is a 
huge time gap between where "c" gets assigned and where "d" gets 
assigned in the code below.

The time gap between "c" and "d" is about 4.2 seconds.
The time gap between "aj" and "bj" is about 6.17 seconds.

The next time through the loop, the printk() triggers with a complaint 
that it took 6381 ticks between scheduler_tick() calls.

I'm baffled as to where the gap is coming from.  Any ideas?

Interrupts are for certain disabled while this code runs.  The printk 
statement is not being reached, so the "sched_delta" check is failing.

The gethrtime() function simply reads the tbr on ppc64, so it maps to a 
single instruction.



b=gethrtime();
	sched_delta = schedtime - __get_cpu_var(last_sched_tick);
c=gethrtime();
aj=jiffies;
	if (sched_delta > SCHED_INTERVAL_THRESH) {
		/* disable exception history if specified */
		if (disable_hist_on_event)
			disable_history_buffer = 1;
		printk(KERN_WARNING "cpu%d: jiffies: %lu, hrtime: %llu, %lu ticks 
between scheduler_tick() calls\n",
			smp_processor_id(), schedtime, (unsigned long long) gethrtime(), 
sched_delta);
	}
d=gethrtime();
bj=jiffies;



Chris

^ permalink raw reply

* [PATCH] powerpc: Slight rework of new irq handling (rfc)
From: Benjamin Herrenschmidt @ 2006-07-08  8:08 UTC (permalink / raw)
  To: linuxppc-dev list

This patch slightly reworks the new irq code to fix a small design
error. I removed the passing of the trigger to the map() calls entirely.
Mapping a linux virtual irq to a physical irq does only just that.
Setting the trigger is a different action which has a different call.
The changes are:

  - I no longer call host->ops->map() for an already mapped irq, I just
return the virtual number that was already mapped. It was called before
to give an opportunity to change the trigger, but that was causing
issues as that could happen while the interrupt was in use by a device,
and because of the trigger change, map would potentially muck around
with things in a racy way. That was causing much burden on a given's
controller implementation of map() to get it right. This is much simpler
now. map() is only called on the initial mapping of an irq, meaning that
you know that this irq is _not_ being used. You can initialize the
hardware if you want (though you don't have to).

 - Controllers that can handle different type of triggers
(level/edge/etc...) now implement the standard irq_chip->set_type() call
as defined by the generic code. That means that you can use the standard
set_irq_type() to configure an irq line manually if you wish or (though
I don't like that interface), pass explicit trigger flags to
request_irq() as defined by the generic kernel interfaces. Also, using
those interfaces guarantees that your controller set_type callback is
called with the descriptor lock held, thus providing locking against
activity on the same interrupt (including mask/unmask/etc...)
automatically. A result is that, for example, MPIC's own map()
implementation calls irq_set_type(NONE) to configure the hardware to the
default triggers.

 - To allow the above, the actual irq_map is new filled before map()
callback is called.

 - The irq_create_of_mapping() (also used by irq_of_parse_and_map())
function for mapping interrupts from the device-tree now also call the
separate set_irq_type(), and only does so if there is a change in the
trigger type.

 - While I was at it, I changed pci_read_irq_line() (which is the helper
I would expect most archs to use in their pcibios_fixup() to get the PCI
interrupt routing from the device tree) to also handle a fallback when
the DT mapping fails consisting of reading the PCI_INTERRUPT_LINE from
the device, mapping that through the default controller, and setting the
trigger to level low. That default behaviour works for several platforms
that don't have a proper interrupt tree like Pegasos. 

The patch hasn't yet been tested that much, which is why I'm not yet
submitting it officially for upstream, I will do so tomorrow or monday.
In the meantime, comments welcome.

Segher: Your patches #1 and #2 are still needed.

Index: linux-irq-work/arch/powerpc/kernel/irq.c
===================================================================
--- linux-irq-work.orig/arch/powerpc/kernel/irq.c	2006-07-07 17:49:20.000000000 +1000
+++ linux-irq-work/arch/powerpc/kernel/irq.c	2006-07-08 09:36:20.000000000 +1000
@@ -391,15 +391,14 @@
 			irq_map[i].host = host;
 			smp_wmb();
 
-			/* Clear some flags */
-			get_irq_desc(i)->status
-				&= ~(IRQ_NOREQUEST | IRQ_LEVEL);
+			/* Clear norequest flags */
+			get_irq_desc(i)->status &= ~IRQ_NOREQUEST;
 
 			/* Legacy flags are left to default at this point,
 			 * one can then use irq_create_mapping() to
 			 * explicitely change them
 			 */
-			ops->map(host, i, i, 0);
+			ops->map(host, i, i);
 		}
 		break;
 	case IRQ_HOST_MAP_LINEAR:
@@ -457,13 +456,11 @@
 }
 
 unsigned int irq_create_mapping(struct irq_host *host,
-				irq_hw_number_t hwirq,
-				unsigned int flags)
+				irq_hw_number_t hwirq)
 {
 	unsigned int virq, hint;
 
-	pr_debug("irq: irq_create_mapping(0x%p, 0x%lx, 0x%x)\n",
-		 host, hwirq, flags);
+	pr_debug("irq: irq_create_mapping(0x%p, 0x%lx)\n", host, hwirq);
 
 	/* Look for default host if nececssary */
 	if (host == NULL)
@@ -482,7 +479,6 @@
 	virq = irq_find_mapping(host, hwirq);
 	if (virq != IRQ_NONE) {
 		pr_debug("irq: -> existing mapping on virq %d\n", virq);
-		host->ops->map(host, virq, hwirq, flags);
 		return virq;
 	}
 
@@ -504,18 +500,18 @@
 	}
 	pr_debug("irq: -> obtained virq %d\n", virq);
 
-	/* Clear some flags */
-	get_irq_desc(virq)->status &= ~(IRQ_NOREQUEST | IRQ_LEVEL);
+	/* Clear IRQ_NOREQUEST flag */
+	get_irq_desc(virq)->status &= ~IRQ_NOREQUEST;
 
 	/* map it */
-	if (host->ops->map(host, virq, hwirq, flags)) {
+	smp_wmb();
+	irq_map[virq].hwirq = hwirq;
+	smp_mb();
+	if (host->ops->map(host, virq, hwirq)) {
 		pr_debug("irq: -> mapping failed, freeing\n");
 		irq_free_virt(virq, 1);
 		return NO_IRQ;
 	}
-	smp_wmb();
-	irq_map[virq].hwirq = hwirq;
-	smp_mb();
 	return virq;
 }
 EXPORT_SYMBOL_GPL(irq_create_mapping);
@@ -525,7 +521,8 @@
 {
 	struct irq_host *host;
 	irq_hw_number_t hwirq;
-	unsigned int flags = IRQ_TYPE_NONE;
+	unsigned int type = IRQ_TYPE_NONE;
+	unsigned int virq;
 
 	if (controller == NULL)
 		host = irq_default_host;
@@ -539,11 +536,20 @@
 		hwirq = intspec[0];
 	else {
 		if (host->ops->xlate(host, controller, intspec, intsize,
-				     &hwirq, &flags))
+				     &hwirq, &type))
 			return NO_IRQ;
 	}
 
-	return irq_create_mapping(host, hwirq, flags);
+	/* Create mapping */
+	virq = irq_create_mapping(host, hwirq);
+	if (virq == NO_IRQ)
+		return virq;
+
+	/* Set type if specified and different than the current one */
+	if (type != IRQ_TYPE_NONE &&
+	    type != (get_irq_desc(virq)->status & IRQF_TRIGGER_MASK))
+		set_irq_type(virq, type);
+	return virq;
 }
 EXPORT_SYMBOL_GPL(irq_create_of_mapping);
 
Index: linux-irq-work/arch/powerpc/platforms/powermac/pci.c
===================================================================
--- linux-irq-work.orig/arch/powerpc/platforms/powermac/pci.c	2006-07-07 17:49:20.000000000 +1000
+++ linux-irq-work/arch/powerpc/platforms/powermac/pci.c	2006-07-08 09:36:20.000000000 +1000
@@ -46,7 +46,6 @@
 static struct pci_controller *u3_agp;
 static struct pci_controller *u4_pcie;
 static struct pci_controller *u3_ht;
-#define has_second_ohare 0
 #else
 static int has_second_ohare;
 #endif /* CONFIG_PPC64 */
@@ -993,6 +992,7 @@
 		/* Read interrupt from the device-tree */
 		pci_read_irq_line(dev);
 
+#ifdef CONFIG_PPC32
 		/* Fixup interrupt for the modem/ethernet combo controller.
 		 * on machines with a second ohare chip.
 		 * The number in the device tree (27) is bogus (correct for
@@ -1002,8 +1002,11 @@
 		 */
 		if (has_second_ohare &&
 		    dev->vendor == PCI_VENDOR_ID_DEC &&
-		    dev->device == PCI_DEVICE_ID_DEC_TULIP_PLUS)
-			dev->irq = irq_create_mapping(NULL, 60, 0);
+		    dev->device == PCI_DEVICE_ID_DEC_TULIP_PLUS) {
+			dev->irq = irq_create_mapping(NULL, 60);
+			set_irq_type(dev->irq, IRQ_TYPE_LEVEL_LOW);
+		}
+#endif /* CONFIG_PPC32 */
 	}
 }
 
Index: linux-irq-work/arch/powerpc/platforms/powermac/pic.c
===================================================================
--- linux-irq-work.orig/arch/powerpc/platforms/powermac/pic.c	2006-07-07 17:49:20.000000000 +1000
+++ linux-irq-work/arch/powerpc/platforms/powermac/pic.c	2006-07-08 09:37:18.000000000 +1000
@@ -291,7 +291,7 @@
 }
 
 static int pmac_pic_host_map(struct irq_host *h, unsigned int virq,
-			     irq_hw_number_t hw, unsigned int flags)
+			     irq_hw_number_t hw)
 {
 	struct irq_desc *desc = get_irq_desc(virq);
 	int level;
@@ -318,6 +318,7 @@
 			       unsigned int *out_flags)
 
 {
+	*out_flags = IRQ_TYPE_NONE;
 	*out_hwirq = *intspec;
 	return 0;
 }
@@ -434,7 +435,7 @@
 
 	printk(KERN_INFO "irq: System has %d possible interrupts\n", max_irqs);
 #ifdef CONFIG_XMON
-	setup_irq(irq_create_mapping(NULL, 20, 0), &xmon_action);
+	setup_irq(irq_create_mapping(NULL, 20), &xmon_action);
 #endif
 }
 #endif /* CONFIG_PPC32 */
@@ -579,9 +580,10 @@
 		flags |= OF_IMAP_OLDWORLD_MAC;
 	if (get_property(of_chosen, "linux,bootx", NULL) != NULL)
 		flags |= OF_IMAP_NO_PHANDLE;
-	of_irq_map_init(flags);
 #endif /* CONFIG_PPC_32 */
 
+	of_irq_map_init(flags);
+
 	/* We first try to detect Apple's new Core99 chipset, since mac-io
 	 * is quite different on those machines and contains an IBM MPIC2.
 	 */
Index: linux-irq-work/arch/powerpc/sysdev/mpic.c
===================================================================
--- linux-irq-work.orig/arch/powerpc/sysdev/mpic.c	2006-07-07 17:49:20.000000000 +1000
+++ linux-irq-work/arch/powerpc/sysdev/mpic.c	2006-07-08 09:36:20.000000000 +1000
@@ -337,6 +337,17 @@
 	}
 }
 
+#else /* CONFIG_MPIC_BROKEN_U3 */
+
+static inline int mpic_is_ht_interrupt(struct mpic *mpic, unsigned int source)
+{
+	return 0;
+}
+
+static void __init mpic_scan_ht_pics(struct mpic *mpic)
+{
+}
+
 #endif /* CONFIG_MPIC_BROKEN_U3 */
 
 
@@ -405,11 +416,9 @@
 	unsigned int loops = 100000;
 	struct mpic *mpic = mpic_from_irq(irq);
 	unsigned int src = mpic_irq_to_hw(irq);
-	unsigned long flags;
 
 	DBG("%p: %s: enable_irq: %d (src %d)\n", mpic, mpic->name, irq, src);
 
-	spin_lock_irqsave(&mpic_lock, flags);
 	mpic_irq_write(src, MPIC_IRQ_VECTOR_PRI,
 		       mpic_irq_read(src, MPIC_IRQ_VECTOR_PRI) &
 		       ~MPIC_VECPRI_MASK);
@@ -420,7 +429,6 @@
 			break;
 		}
 	} while(mpic_irq_read(src, MPIC_IRQ_VECTOR_PRI) & MPIC_VECPRI_MASK);
-	spin_unlock_irqrestore(&mpic_lock, flags);
 }
 
 static void mpic_mask_irq(unsigned int irq)
@@ -428,11 +436,9 @@
 	unsigned int loops = 100000;
 	struct mpic *mpic = mpic_from_irq(irq);
 	unsigned int src = mpic_irq_to_hw(irq);
-	unsigned long flags;
 
 	DBG("%s: disable_irq: %d (src %d)\n", mpic->name, irq, src);
 
-	spin_lock_irqsave(&mpic_lock, flags);
 	mpic_irq_write(src, MPIC_IRQ_VECTOR_PRI,
 		       mpic_irq_read(src, MPIC_IRQ_VECTOR_PRI) |
 		       MPIC_VECPRI_MASK);
@@ -444,7 +450,6 @@
 			break;
 		}
 	} while(!(mpic_irq_read(src, MPIC_IRQ_VECTOR_PRI) & MPIC_VECPRI_MASK));
-	spin_unlock_irqrestore(&mpic_lock, flags);
 }
 
 static void mpic_end_irq(unsigned int irq)
@@ -512,8 +517,7 @@
 		mpic_ht_end_irq(mpic, src);
 	mpic_eoi(mpic);
 }
-
-#endif /* CONFIG_MPIC_BROKEN_U3 */
+#endif /* !CONFIG_MPIC_BROKEN_U3 */
 
 #ifdef CONFIG_SMP
 
@@ -560,47 +564,74 @@
 		       mpic_physmask(cpus_addr(tmp)[0]));	
 }
 
-static unsigned int mpic_flags_to_vecpri(unsigned int flags, int *level)
+static unsigned int mpic_type_to_vecpri(unsigned int type)
 {
-	unsigned int vecpri;
-
 	/* Now convert sense value */
-	switch(flags & IRQ_TYPE_SENSE_MASK) {
+	switch(type & IRQ_TYPE_SENSE_MASK) {
 	case IRQ_TYPE_EDGE_RISING:
-		vecpri = MPIC_VECPRI_SENSE_EDGE |
-			MPIC_VECPRI_POLARITY_POSITIVE;
-		*level = 0;
-		break;
+		return MPIC_VECPRI_SENSE_EDGE | MPIC_VECPRI_POLARITY_POSITIVE;
 	case IRQ_TYPE_EDGE_FALLING:
-		vecpri = MPIC_VECPRI_SENSE_EDGE |
-			MPIC_VECPRI_POLARITY_NEGATIVE;
-		*level = 0;
-		break;
+	case IRQ_TYPE_EDGE_BOTH:
+		return MPIC_VECPRI_SENSE_EDGE | MPIC_VECPRI_POLARITY_NEGATIVE;
 	case IRQ_TYPE_LEVEL_HIGH:
-		vecpri = MPIC_VECPRI_SENSE_LEVEL |
-			MPIC_VECPRI_POLARITY_POSITIVE;
-		*level = 1;
-		break;
+		return MPIC_VECPRI_SENSE_LEVEL | MPIC_VECPRI_POLARITY_POSITIVE;
 	case IRQ_TYPE_LEVEL_LOW:
 	default:
-		vecpri = MPIC_VECPRI_SENSE_LEVEL |
-			MPIC_VECPRI_POLARITY_NEGATIVE;
-		*level = 1;
+		return MPIC_VECPRI_SENSE_LEVEL | MPIC_VECPRI_POLARITY_NEGATIVE;
 	}
-	return vecpri;
+}
+
+static int mpic_set_irq_type(unsigned int virq, unsigned int flow_type)
+{
+	struct mpic *mpic = mpic_from_irq(virq);
+	unsigned int src = mpic_irq_to_hw(virq);
+	struct irq_desc *desc = get_irq_desc(virq);
+	unsigned int vecpri, vold, vnew;
+
+	pr_debug("mpic: set_irq_type(mpic:@%p,virq:%d,src:%d,type:0x%x)\n",
+		 mpic, virq, src, flow_type);
+
+	if (src >= mpic->irq_count)
+		return -EINVAL;
+
+	if (flow_type == IRQ_TYPE_NONE)
+		if (mpic->senses && src < mpic->senses_count)
+			flow_type = mpic->senses[src];
+	if (flow_type == IRQ_TYPE_NONE)
+		flow_type = IRQ_TYPE_LEVEL_LOW;
+
+	desc->status &= ~(IRQ_TYPE_SENSE_MASK | IRQ_LEVEL);
+	desc->status |= flow_type & IRQ_TYPE_SENSE_MASK;
+	if (flow_type & (IRQ_TYPE_LEVEL_HIGH | IRQ_TYPE_LEVEL_LOW))
+		desc->status |= IRQ_LEVEL;
+
+	if (mpic_is_ht_interrupt(mpic, src))
+		vecpri = MPIC_VECPRI_POLARITY_POSITIVE |
+			MPIC_VECPRI_SENSE_EDGE;
+	else
+		vecpri = mpic_type_to_vecpri(flow_type);
+
+	vold = mpic_irq_read(src, MPIC_IRQ_VECTOR_PRI);
+	vnew = vold & ~(MPIC_VECPRI_POLARITY_MASK | MPIC_VECPRI_SENSE_MASK);
+	vnew |= vecpri;
+	if (vold != vnew)
+		mpic_irq_write(src, MPIC_IRQ_VECTOR_PRI, vnew);
+
+	return 0;
 }
 
 static struct irq_chip mpic_irq_chip = {
-	.mask	= mpic_mask_irq,
-	.unmask	= mpic_unmask_irq,
-	.eoi	= mpic_end_irq,
+	.mask		= mpic_mask_irq,
+	.unmask		= mpic_unmask_irq,
+	.eoi		= mpic_end_irq,
+	.set_type	= mpic_set_irq_type,
 };
 
 #ifdef CONFIG_SMP
 static struct irq_chip mpic_ipi_chip = {
-	.mask	= mpic_mask_ipi,
-	.unmask	= mpic_unmask_ipi,
-	.eoi	= mpic_end_ipi,
+	.mask		= mpic_mask_ipi,
+	.unmask		= mpic_unmask_ipi,
+	.eoi		= mpic_end_ipi,
 };
 #endif /* CONFIG_SMP */
 
@@ -611,6 +642,7 @@
 	.mask		= mpic_mask_irq,
 	.unmask		= mpic_unmask_ht_irq,
 	.eoi		= mpic_end_ht_irq,
+	.set_type	= mpic_set_irq_type,
 };
 #endif /* CONFIG_MPIC_BROKEN_U3 */
 
@@ -624,18 +656,12 @@
 }
 
 static int mpic_host_map(struct irq_host *h, unsigned int virq,
-			 irq_hw_number_t hw, unsigned int flags)
+			 irq_hw_number_t hw)
 {
-	struct irq_desc *desc = get_irq_desc(virq);
-	struct irq_chip *chip;
 	struct mpic *mpic = h->host_data;
-	u32 v, vecpri = MPIC_VECPRI_SENSE_LEVEL |
-		MPIC_VECPRI_POLARITY_NEGATIVE;
-	int level;
-	unsigned long iflags;
+	struct irq_chip *chip;
 
-	pr_debug("mpic: map virq %d, hwirq 0x%lx, flags: 0x%x\n",
-		 virq, hw, flags);
+	pr_debug("mpic: map virq %d, hwirq 0x%lx\n", virq, hw);
 
 	if (hw == MPIC_VEC_SPURRIOUS)
 		return -EINVAL;
@@ -654,44 +680,23 @@
 	if (hw >= mpic->irq_count)
 		return -EINVAL;
 
-	/* If no sense provided, check default sense array */
-	if (((flags & IRQ_TYPE_SENSE_MASK) == IRQ_TYPE_NONE) &&
-	    mpic->senses && hw < mpic->senses_count)
-		flags |= mpic->senses[hw];
-
-	vecpri = mpic_flags_to_vecpri(flags, &level);
-	if (level)
-		desc->status |= IRQ_LEVEL;
+	/* Default chip */
 	chip = &mpic->hc_irq;
 
 #ifdef CONFIG_MPIC_BROKEN_U3
 	/* Check for HT interrupts, override vecpri */
-	if (mpic_is_ht_interrupt(mpic, hw)) {
-		vecpri &= ~(MPIC_VECPRI_SENSE_MASK |
-			    MPIC_VECPRI_POLARITY_MASK);
-		vecpri |= MPIC_VECPRI_POLARITY_POSITIVE;
+	if (mpic_is_ht_interrupt(mpic, hw))
 		chip = &mpic->hc_ht_irq;
-	}
-#endif
-
-	/* Reconfigure irq. We must preserve the mask bit as we can be called
-	 * while the interrupt is still active (This may change in the future
-	 * but for now, it is the case).
-	 */
-	spin_lock_irqsave(&mpic_lock, iflags);
-	v = mpic_irq_read(hw, MPIC_IRQ_VECTOR_PRI);
-	vecpri = (v &
-		~(MPIC_VECPRI_POLARITY_MASK | MPIC_VECPRI_SENSE_MASK)) |
-		vecpri;
-	if (vecpri != v)
-		mpic_irq_write(hw, MPIC_IRQ_VECTOR_PRI, vecpri);
-	spin_unlock_irqrestore(&mpic_lock, iflags);
+#endif /* CONFIG_MPIC_BROKEN_U3 */
 
-	pr_debug("mpic: mapping as IRQ, vecpri = 0x%08x (was 0x%08x)\n",
-		 vecpri, v);
+	pr_debug("mpic: mapping to irq chip @%p\n", chip);
 
 	set_irq_chip_data(virq, mpic);
 	set_irq_chip_and_handler(virq, chip, handle_fasteoi_irq);
+
+	/* Set default irq type */
+	set_irq_type(virq, IRQ_TYPE_NONE);
+
 	return 0;
 }
 
@@ -906,41 +911,16 @@
 	if (mpic->irq_count == 0)
 		mpic->irq_count = mpic->num_sources;
 
-#ifdef CONFIG_MPIC_BROKEN_U3
 	/* Do the HT PIC fixups on U3 broken mpic */
 	DBG("MPIC flags: %x\n", mpic->flags);
 	if ((mpic->flags & MPIC_BROKEN_U3) && (mpic->flags & MPIC_PRIMARY))
  		mpic_scan_ht_pics(mpic);
-#endif /* CONFIG_MPIC_BROKEN_U3 */
 
 	for (i = 0; i < mpic->num_sources; i++) {
 		/* start with vector = source number, and masked */
-		u32 vecpri = MPIC_VECPRI_MASK | i | (8 << MPIC_VECPRI_PRIORITY_SHIFT);
-		int level = 1;
+		u32 vecpri = MPIC_VECPRI_MASK | i |
+			(8 << MPIC_VECPRI_PRIORITY_SHIFT);
 		
-		/* do senses munging */
-		if (mpic->senses && i < mpic->senses_count)
-			vecpri |= mpic_flags_to_vecpri(mpic->senses[i],
-						       &level);
-		else
-			vecpri |= MPIC_VECPRI_SENSE_LEVEL;
-
-		/* deal with broken U3 */
-		if (mpic->flags & MPIC_BROKEN_U3) {
-#ifdef CONFIG_MPIC_BROKEN_U3
-			if (mpic_is_ht_interrupt(mpic, i)) {
-				vecpri &= ~(MPIC_VECPRI_SENSE_MASK |
-					    MPIC_VECPRI_POLARITY_MASK);
-				vecpri |= MPIC_VECPRI_POLARITY_POSITIVE;
-			}
-#else
-			printk(KERN_ERR "mpic: BROKEN_U3 set, but CONFIG doesn't match\n");
-#endif
-		}
-
-		DBG("setup source %d, vecpri: %08x, level: %d\n", i, vecpri,
-		    (level != 0));
-
 		/* init hw */
 		mpic_irq_write(i, MPIC_IRQ_VECTOR_PRI, vecpri);
 		mpic_irq_write(i, MPIC_IRQ_DESTINATION,
@@ -1154,7 +1134,7 @@
 
 	for (i = 0; i < 4; i++) {
 		unsigned int vipi = irq_create_mapping(mpic->irqhost,
-						       MPIC_VEC_IPI_0 + i, 0);
+						       MPIC_VEC_IPI_0 + i);
 		if (vipi == NO_IRQ) {
 			printk(KERN_ERR "Failed to map IPI %d\n", i);
 			break;
Index: linux-irq-work/drivers/macintosh/macio_asic.c
===================================================================
--- linux-irq-work.orig/drivers/macintosh/macio_asic.c	2006-07-07 17:49:20.000000000 +1000
+++ linux-irq-work/drivers/macintosh/macio_asic.c	2006-07-08 09:36:20.000000000 +1000
@@ -330,7 +330,7 @@
 {
 	unsigned int irq;
 
-	irq = irq_create_mapping(NULL, line, 0);
+	irq = irq_create_mapping(NULL, line);
 	if (irq != NO_IRQ) {
 		dev->interrupt[index].start = irq;
 		dev->interrupt[index].flags = IORESOURCE_IRQ;
Index: linux-irq-work/include/asm-powerpc/irq.h
===================================================================
--- linux-irq-work.orig/include/asm-powerpc/irq.h	2006-07-07 17:49:20.000000000 +1000
+++ linux-irq-work/include/asm-powerpc/irq.h	2006-07-08 09:36:20.000000000 +1000
@@ -83,25 +83,24 @@
 	int (*match)(struct irq_host *h, struct device_node *node);
 
 	/* Create or update a mapping between a virtual irq number and a hw
-	 * irq number. This can be called several times for the same mapping
-	 * but with different flags, though unmap shall always be called
-	 * before the virq->hw mapping is changed.
+	 * irq number. This is called only once for a given mapping.
 	 */
-	int (*map)(struct irq_host *h, unsigned int virq,
-		   irq_hw_number_t hw, unsigned int flags);
+	int (*map)(struct irq_host *h, unsigned int virq, irq_hw_number_t hw);
 
 	/* Dispose of such a mapping */
 	void (*unmap)(struct irq_host *h, unsigned int virq);
 
 	/* Translate device-tree interrupt specifier from raw format coming
 	 * from the firmware to a irq_hw_number_t (interrupt line number) and
-	 * trigger flags that can be passed to irq_create_mapping().
-	 * If no translation is provided, raw format is assumed to be one cell
-	 * for interrupt line and default sense.
+	 * type (sense) that can be passed to set_irq_type(). In the absence
+	 * of this callback, irq_create_of_mapping() and irq_of_parse_and_map()
+	 * will return the hw number in the first cell and IRQ_TYPE_NONE for
+	 * the type (which amount to keeping whatever default value the
+	 * interrupt controller has for that line)
 	 */
 	int (*xlate)(struct irq_host *h, struct device_node *ctrler,
 		     u32 *intspec, unsigned int intsize,
-		     irq_hw_number_t *out_hwirq, unsigned int *out_flags);
+		     irq_hw_number_t *out_hwirq, unsigned int *out_type);
 };
 
 struct irq_host {
@@ -193,25 +192,14 @@
  * irq_create_mapping - Map a hardware interrupt into linux virq space
  * @host: host owning this hardware interrupt or NULL for default host
  * @hwirq: hardware irq number in that host space
- * @flags: flags passed to the controller. contains the trigger type among
- *         others. Use IRQ_TYPE_* defined in include/linux/irq.h
  *
  * Only one mapping per hardware interrupt is permitted. Returns a linux
- * virq number. The flags can be used to provide sense information to the
- * controller (typically extracted from the device-tree). If no information
- * is passed, the controller defaults will apply (for example, xics can only
- * do edge so flags are irrelevant for some pseries specific irqs).
- *
- * The device-tree generally contains the trigger info in an encoding that is
- * specific to a given type of controller. In that case, you can directly use
- * host->ops->trigger_xlate() to translate that.
- *
- * It is recommended that new PICs that don't have existing OF bindings chose
- * to use a representation of triggers identical to linux.
+ * virq number.
+ * If the sense/trigger is to be specified, set_irq_type() should be called
+ * on the number returned from that call.
  */
 extern unsigned int irq_create_mapping(struct irq_host *host,
-				       irq_hw_number_t hwirq,
-				       unsigned int flags);
+				       irq_hw_number_t hwirq);
 
 
 /***
@@ -295,7 +283,7 @@
  *
  * This function is identical to irq_create_mapping except that it takes
  * as input informations straight from the device-tree (typically the results
- * of the of_irq_map_*() functions
+ * of the of_irq_map_*() functions.
  */
 extern unsigned int irq_create_of_mapping(struct device_node *controller,
 					  u32 *intspec, unsigned int intsize);
Index: linux-irq-work/arch/powerpc/kernel/ibmebus.c
===================================================================
--- linux-irq-work.orig/arch/powerpc/kernel/ibmebus.c	2006-07-04 09:37:03.000000000 +1000
+++ linux-irq-work/arch/powerpc/kernel/ibmebus.c	2006-07-08 11:05:35.000000000 +1000
@@ -323,7 +323,7 @@
 			unsigned long irq_flags, const char * devname,
 			void *dev_id)
 {
-	unsigned int irq = irq_create_mapping(NULL, ist, 0);
+	unsigned int irq = irq_create_mapping(NULL, ist);
 	
 	if (irq == NO_IRQ)
 		return -EINVAL;
Index: linux-irq-work/arch/powerpc/platforms/cell/interrupt.c
===================================================================
--- linux-irq-work.orig/arch/powerpc/platforms/cell/interrupt.c	2006-07-04 09:37:03.000000000 +1000
+++ linux-irq-work/arch/powerpc/platforms/cell/interrupt.c	2006-07-08 09:41:01.000000000 +1000
@@ -159,7 +159,7 @@
 		if (iic_hosts[node] == NULL)
 			continue;
 		virq = irq_create_mapping(iic_hosts[node],
-					  iic_ipi_to_irq(ipi), 0);
+					  iic_ipi_to_irq(ipi));
 		if (virq == NO_IRQ) {
 			printk(KERN_ERR
 			       "iic: failed to map IPI %s on node %d\n",
@@ -197,7 +197,7 @@
 }
 
 static int iic_host_map(struct irq_host *h, unsigned int virq,
-			irq_hw_number_t hw, unsigned int flags)
+			irq_hw_number_t hw)
 {
 	if (hw < IIC_IRQ_IPI0)
 		set_irq_chip_and_handler(virq, &iic_chip, handle_fasteoi_irq);
Index: linux-irq-work/arch/powerpc/platforms/cell/spider-pic.c
===================================================================
--- linux-irq-work.orig/arch/powerpc/platforms/cell/spider-pic.c	2006-07-04 09:37:03.000000000 +1000
+++ linux-irq-work/arch/powerpc/platforms/cell/spider-pic.c	2006-07-08 11:09:28.000000000 +1000
@@ -85,9 +85,6 @@
 	struct spider_pic *pic = spider_virq_to_pic(virq);
 	void __iomem *cfg = spider_get_irq_config(pic, irq_map[virq].hwirq);
 
-	/* We use no locking as we should be covered by the descriptor lock
-	 * for access to invidual source configuration registers
-	 */
 	out_be32(cfg, in_be32(cfg) | 0x30000000u);
 }
 
@@ -96,9 +93,6 @@
 	struct spider_pic *pic = spider_virq_to_pic(virq);
 	void __iomem *cfg = spider_get_irq_config(pic, irq_map[virq].hwirq);
 
-	/* We use no locking as we should be covered by the descriptor lock
-	 * for access to invidual source configuration registers
-	 */
 	out_be32(cfg, in_be32(cfg) & ~0x30000000u);
 }
 
@@ -120,26 +114,14 @@
 	out_be32(pic->regs + TIR_EDC, 0x100 | (src & 0xf));
 }
 
-static struct irq_chip spider_pic = {
-	.typename = " SPIDER   ",
-	.unmask = spider_unmask_irq,
-	.mask = spider_mask_irq,
-	.ack = spider_ack_irq,
-};
-
-static int spider_host_match(struct irq_host *h, struct device_node *node)
-{
-	struct spider_pic *pic = h->host_data;
-	return node == pic->of_node;
-}
-
-static int spider_host_map(struct irq_host *h, unsigned int virq,
-			irq_hw_number_t hw, unsigned int flags)
+static int spider_set_irq_type(unsigned int virq, unsigned int type)
 {
-	unsigned int sense = flags & IRQ_TYPE_SENSE_MASK;
-	struct spider_pic *pic = h->host_data;
+	unsigned int sense = type & IRQ_TYPE_SENSE_MASK;
+	struct spider_pic *pic = spider_virq_to_pic(virq);
+	unsigned int hw = irq_map[virq].hwirq;
 	void __iomem *cfg = spider_get_irq_config(pic, hw);
-	int level = 0;
+	struct irq_desc *desc = get_irq_desc(virq);
+	u32 old_mask;
 	u32 ic;
 
 	/* Note that only level high is supported for most interrupts */
@@ -157,29 +139,57 @@
 		break;
 	case IRQ_TYPE_LEVEL_LOW:
 		ic = 0x0;
-		level = 1;
 		break;
 	case IRQ_TYPE_LEVEL_HIGH:
 	case IRQ_TYPE_NONE:
 		ic = 0x1;
-		level = 1;
 		break;
 	default:
 		return -EINVAL;
 	}
 
+	/* Update irq_desc */
+	desc->status &= ~(IRQ_TYPE_SENSE_MASK | IRQ_LEVEL);
+	desc->status |= type & IRQ_TYPE_SENSE_MASK;
+	if (type & (IRQ_TYPE_LEVEL_HIGH | IRQ_TYPE_LEVEL_LOW))
+		desc->status |= IRQ_LEVEL;
+
 	/* Configure the source. One gross hack that was there before and
 	 * that I've kept around is the priority to the BE which I set to
 	 * be the same as the interrupt source number. I don't know wether
 	 * that's supposed to make any kind of sense however, we'll have to
 	 * decide that, but for now, I'm not changing the behaviour.
 	 */
-	out_be32(cfg, (ic << 24) | (0x7 << 16) | (pic->node_id << 4) | 0xe);
+	old_mask = in_be32(cfg) & 0x30000000u;
+	out_be32(cfg, old_mask | (ic << 24) | (0x7 << 16) |
+		 (pic->node_id << 4) | 0xe);
 	out_be32(cfg + 4, (0x2 << 16) | (hw & 0xff));
 
-	if (level)
-		get_irq_desc(virq)->status |= IRQ_LEVEL;
+	return 0;
+}
+
+static struct irq_chip spider_pic = {
+	.typename = " SPIDER   ",
+	.unmask = spider_unmask_irq,
+	.mask = spider_mask_irq,
+	.ack = spider_ack_irq,
+	.set_type = spider_set_irq_type,
+};
+
+static int spider_host_match(struct irq_host *h, struct device_node *node)
+{
+	struct spider_pic *pic = h->host_data;
+	return node == pic->of_node;
+}
+
+static int spider_host_map(struct irq_host *h, unsigned int virq,
+			irq_hw_number_t hw)
+{
 	set_irq_chip_and_handler(virq, &spider_pic, handle_level_irq);
+
+	/* Set default irq type */
+	set_irq_type(virq, IRQ_TYPE_NONE);
+
 	return 0;
 }
 
@@ -283,7 +293,7 @@
 	if (iic_host == NULL)
 		return NO_IRQ;
 	/* Manufacture an IIC interrupt number of class 2 */
-	virq = irq_create_mapping(iic_host, 0x20 | unit, 0);
+	virq = irq_create_mapping(iic_host, 0x20 | unit);
 	if (virq == NO_IRQ)
 		printk(KERN_ERR "spider_pic: failed to map cascade !");
 	return virq;
Index: linux-irq-work/arch/powerpc/platforms/cell/spu_base.c
===================================================================
--- linux-irq-work.orig/arch/powerpc/platforms/cell/spu_base.c	2006-07-04 09:37:03.000000000 +1000
+++ linux-irq-work/arch/powerpc/platforms/cell/spu_base.c	2006-07-08 11:05:01.000000000 +1000
@@ -583,9 +583,9 @@
 	spu->isrc = isrc = tmp[0];
 
 	/* Now map interrupts of all 3 classes */
-	spu->irqs[0] = irq_create_mapping(host, 0x00 | isrc, 0);
-	spu->irqs[1] = irq_create_mapping(host, 0x10 | isrc, 0);
-	spu->irqs[2] = irq_create_mapping(host, 0x20 | isrc, 0);
+	spu->irqs[0] = irq_create_mapping(host, 0x00 | isrc);
+	spu->irqs[1] = irq_create_mapping(host, 0x10 | isrc);
+	spu->irqs[2] = irq_create_mapping(host, 0x20 | isrc);
 
 	/* Right now, we only fail if class 2 failed */
 	return spu->irqs[2] == NO_IRQ ? -EINVAL : 0;
Index: linux-irq-work/arch/powerpc/platforms/iseries/irq.c
===================================================================
--- linux-irq-work.orig/arch/powerpc/platforms/iseries/irq.c	2006-07-04 09:37:03.000000000 +1000
+++ linux-irq-work/arch/powerpc/platforms/iseries/irq.c	2006-07-08 11:06:10.000000000 +1000
@@ -300,7 +300,7 @@
 	realirq = (((((sub_bus << 8) + (bus - 1)) << 3) + (idsel - 1)) << 3)
 		+ function;
 
-	return irq_create_mapping(NULL, realirq, IRQ_TYPE_NONE);
+	return irq_create_mapping(NULL, realirq);
 }
 
 #endif /* CONFIG_PCI */
@@ -341,7 +341,7 @@
 }
 
 static int iseries_irq_host_map(struct irq_host *h, unsigned int virq,
-				irq_hw_number_t hw, unsigned int flags)
+				irq_hw_number_t hw)
 {
 	set_irq_chip_and_handler(virq, &iseries_pic, handle_fasteoi_irq);
 
Index: linux-irq-work/arch/powerpc/platforms/pseries/ras.c
===================================================================
--- linux-irq-work.orig/arch/powerpc/platforms/pseries/ras.c	2006-07-04 09:37:03.000000000 +1000
+++ linux-irq-work/arch/powerpc/platforms/pseries/ras.c	2006-07-08 11:06:34.000000000 +1000
@@ -93,8 +93,7 @@
 		for (i = 0; i < opicplen; i++) {
 			if (count > 15)
 				break;
-			virqs[count] = irq_create_mapping(NULL, *(opicprop++),
-							 IRQ_TYPE_NONE);
+			virqs[count] = irq_create_mapping(NULL, *(opicprop++));
 			if (virqs[count] == NO_IRQ)
 				printk(KERN_ERR "Unable to allocate interrupt "
 				       "number for %s\n", np->full_name);
Index: linux-irq-work/arch/powerpc/platforms/pseries/xics.c
===================================================================
--- linux-irq-work.orig/arch/powerpc/platforms/pseries/xics.c	2006-07-04 09:37:03.000000000 +1000
+++ linux-irq-work/arch/powerpc/platforms/pseries/xics.c	2006-07-08 14:28:49.000000000 +1000
@@ -502,16 +502,9 @@
 }
 
 static int xics_host_map_direct(struct irq_host *h, unsigned int virq,
-				irq_hw_number_t hw, unsigned int flags)
+				irq_hw_number_t hw)
 {
-	unsigned int sense = flags & IRQ_TYPE_SENSE_MASK;
-
-	pr_debug("xics: map_direct virq %d, hwirq 0x%lx, flags: 0x%x\n",
-		 virq, hw, flags);
-
-	if (sense && sense != IRQ_TYPE_LEVEL_LOW)
-		printk(KERN_WARNING "xics: using unsupported sense 0x%x"
-		       " for irq %d (h: 0x%lx)\n", flags, virq, hw);
+	pr_debug("xics: map_direct virq %d, hwirq 0x%lx\n", virq, hw);
 
 	get_irq_desc(virq)->status |= IRQ_LEVEL;
 	set_irq_chip_and_handler(virq, &xics_pic_direct, handle_fasteoi_irq);
@@ -519,16 +512,9 @@
 }
 
 static int xics_host_map_lpar(struct irq_host *h, unsigned int virq,
-			      irq_hw_number_t hw, unsigned int flags)
+			      irq_hw_number_t hw)
 {
-	unsigned int sense = flags & IRQ_TYPE_SENSE_MASK;
-
-	pr_debug("xics: map_lpar virq %d, hwirq 0x%lx, flags: 0x%x\n",
-		 virq, hw, flags);
-
-	if (sense && sense != IRQ_TYPE_LEVEL_LOW)
-		printk(KERN_WARNING "xics: using unsupported sense 0x%x"
-		       " for irq %d (h: 0x%lx)\n", flags, virq, hw);
+	pr_debug("xics: map_direct virq %d, hwirq 0x%lx\n", virq, hw);
 
 	get_irq_desc(virq)->status |= IRQ_LEVEL;
 	set_irq_chip_and_handler(virq, &xics_pic_lpar, handle_fasteoi_irq);
@@ -757,7 +743,7 @@
 {
 	unsigned int ipi;
 
-	ipi = irq_create_mapping(xics_host, XICS_IPI, 0);
+	ipi = irq_create_mapping(xics_host, XICS_IPI);
 	BUG_ON(ipi == NO_IRQ);
 
 	/*
@@ -795,7 +781,7 @@
 		return;
 	desc = get_irq_desc(ipi);
 	if (desc->chip && desc->chip->eoi)
-		desc->chip->eoi(XICS_IPI);
+		desc->chip->eoi(ipi);
 
 	/*
 	 * Some machines need to have at least one cpu in the GIQ,
Index: linux-irq-work/arch/powerpc/sysdev/i8259.c
===================================================================
--- linux-irq-work.orig/arch/powerpc/sysdev/i8259.c	2006-07-04 09:37:03.000000000 +1000
+++ linux-irq-work/arch/powerpc/sysdev/i8259.c	2006-07-08 09:39:38.000000000 +1000
@@ -169,7 +169,7 @@
 }
 
 static int i8259_host_map(struct irq_host *h, unsigned int virq,
-			  irq_hw_number_t hw, unsigned int flags)
+			  irq_hw_number_t hw)
 {
 	pr_debug("i8259_host_map(%d, 0x%lx)\n", virq, hw);
 
@@ -177,7 +177,7 @@
 	if (hw == 2)
 		get_irq_desc(virq)->status |= IRQ_NOREQUEST;
 
-	/* We use the level stuff only for now, we might want to
+	/* We use the level handler only for now, we might want to
 	 * be more cautious here but that works for now
 	 */
 	get_irq_desc(virq)->status |= IRQ_LEVEL;
Index: linux-irq-work/drivers/char/hvsi.c
===================================================================
--- linux-irq-work.orig/drivers/char/hvsi.c	2006-07-04 09:37:03.000000000 +1000
+++ linux-irq-work/drivers/char/hvsi.c	2006-07-08 11:07:11.000000000 +1000
@@ -1299,7 +1299,7 @@
 		hp->inbuf_end = hp->inbuf;
 		hp->state = HVSI_CLOSED;
 		hp->vtermno = *vtermno;
-		hp->virq = irq_create_mapping(NULL, irq[0], 0);
+		hp->virq = irq_create_mapping(NULL, irq[0]);
 		if (hp->virq == NO_IRQ) {
 			printk(KERN_ERR "%s: couldn't create irq mapping for 0x%x\n",
 				__FUNCTION__, irq[0]);
Index: linux-irq-work/arch/powerpc/kernel/pci_64.c
===================================================================
--- linux-irq-work.orig/arch/powerpc/kernel/pci_64.c	2006-07-04 09:37:03.000000000 +1000
+++ linux-irq-work/arch/powerpc/kernel/pci_64.c	2006-07-08 15:31:31.000000000 +1000
@@ -21,13 +21,13 @@
 #include <linux/mm.h>
 #include <linux/list.h>
 #include <linux/syscalls.h>
+#include <linux/irq.h>
 
 #include <asm/processor.h>
 #include <asm/io.h>
 #include <asm/prom.h>
 #include <asm/pci-bridge.h>
 #include <asm/byteorder.h>
-#include <asm/irq.h>
 #include <asm/machdep.h>
 #include <asm/ppc-pci.h>
 
@@ -1289,15 +1289,31 @@
 
 	DBG("Try to map irq for %s...\n", pci_name(pci_dev));
 
+	/* Try to get a mapping from the device-tree */
 	if (of_irq_map_pci(pci_dev, &oirq)) {
-		DBG(" -> failed !\n");
-		return -1;
-	}
+		u8 line;
 
-	DBG(" -> got one, spec %d cells (0x%08x...) on %s\n",
-	    oirq.size, oirq.specifier[0], oirq.controller->full_name);
+		/* If that fails, lets fallback to what is in the config
+		 * space and map that through the default controller. We
+		 * also set the type to level low since that's what PCI
+		 * interrupts are. If your platform does differently, then
+		 * either provide a proper interrupt tree or don't use this
+		 * function.
+		 */
+		if (pci_read_config_byte(pci_dev, PCI_INTERRUPT_LINE, &line))
+			return -1;
+		DBG(" -> failed ! Using irq line %d from PCI config\n", line);
+
+		virq = irq_create_mapping(NULL, line);
+		if (virq != NO_IRQ)
+			set_irq_type(virq, IRQ_TYPE_LEVEL_LOW);
+	} else {
+		DBG(" -> got one, spec %d cells (0x%08x...) on %s\n",
+		    oirq.size, oirq.specifier[0], oirq.controller->full_name);
 
-	virq = irq_create_of_mapping(oirq.controller, oirq.specifier, oirq.size);
+		virq = irq_create_of_mapping(oirq.controller, oirq.specifier,
+					     oirq.size);
+	}
 	if(virq == NO_IRQ) {
 		DBG(" -> failed to map !\n");
 		return -1;
Index: linux-irq-work/arch/powerpc/kernel/pci_32.c
===================================================================
--- linux-irq-work.orig/arch/powerpc/kernel/pci_32.c	2006-07-04 09:37:03.000000000 +1000
+++ linux-irq-work/arch/powerpc/kernel/pci_32.c	2006-07-08 16:05:49.000000000 +1000
@@ -11,6 +11,7 @@
 #include <linux/sched.h>
 #include <linux/errno.h>
 #include <linux/bootmem.h>
+#include <linux/irq.h>
 
 #include <asm/processor.h>
 #include <asm/io.h>
@@ -18,7 +19,6 @@
 #include <asm/sections.h>
 #include <asm/pci-bridge.h>
 #include <asm/byteorder.h>
-#include <asm/irq.h>
 #include <asm/uaccess.h>
 #include <asm/machdep.h>
 
@@ -1420,15 +1420,31 @@
 
 	DBG("Try to map irq for %s...\n", pci_name(pci_dev));
 
+	/* Try to get a mapping from the device-tree */
 	if (of_irq_map_pci(pci_dev, &oirq)) {
-		DBG(" -> failed !\n");
-		return -1;
-	}
+		u8 line;
 
-	DBG(" -> got one, spec %d cells (0x%08x...) on %s\n",
-	    oirq.size, oirq.specifier[0], oirq.controller->full_name);
+		/* If that fails, lets fallback to what is in the config
+		 * space and map that through the default controller. We
+		 * also set the type to level low since that's what PCI
+		 * interrupts are. If your platform does differently, then
+		 * either provide a proper interrupt tree or don't use this
+		 * function.
+		 */
+		if (pci_read_config_byte(pci_dev, PCI_INTERRUPT_LINE, &line))
+			return -1;
+		DBG(" -> failed ! Using irq line %d from PCI config\n", line);
 
-	virq = irq_create_of_mapping(oirq.controller, oirq.specifier, oirq.size);
+		virq = irq_create_mapping(NULL, line);
+		if (virq != NO_IRQ)
+			set_irq_type(virq, IRQ_TYPE_LEVEL_LOW);
+	} else {
+		DBG(" -> got one, spec %d cells (0x%08x...) on %s\n",
+		    oirq.size, oirq.specifier[0], oirq.controller->full_name);
+
+		virq = irq_create_of_mapping(oirq.controller, oirq.specifier,
+					     oirq.size);
+	}
 	if(virq == NO_IRQ) {
 		DBG(" -> failed to map !\n");
 		return -1;

^ permalink raw reply

* [PATCH 0/6] Sizing zones and holes in an architecture independent manner V8
From: Mel Gorman @ 2006-07-08 11:10 UTC (permalink / raw)
  To: akpm
  Cc: davej, tony.luck, linux-mm, Mel Gorman, ak, bob.picco,
	linux-kernel, linuxppc-dev

This is V8 of the patchset to size zones and memory holes in an
architecture-independent manner.  The notable addition in this release is
accounting for mem_map as a memory hole as it is not reclaimable and the
optional account of the kernel image as a memory hole. This is to match the
existing behavior of x86_64.

Changelog since V7
o Rebase to 2.6.17-mm6
o Account for mem_map as a memory hole
o Adjust mem_map when arch independent zone-sizing is used and PFN 0 is in
  a memory hole not accounted for by ARCH_PFN_OFFSET

Changelog since V6
o MAX_ACTIVE_REGIONS is really maximum active regions, not MAX_ACTIVE_REGIONS-1
o MAX_ACTIVE_REGIONS is 256 unless the architecture specifically asks for
  a different number or MAX_NUMNODES is >= 32
o nr_nodemap_entries tracks the number of entries rather than terminating with
  end_pfn == 0
o Add number of documentation-related comments. Functions exposed by headers
  may potentially be picked up by kerneldoc
o Changed misleading zone_present_pages_in_node() name to
  zone_spanned_pages_in_node()
o Be a bit more verbose to help debugging when things go wrong.
o On x86_64, end_pfn_map now gets updated properly or ACPI tables get "lost"
o Signoffs added to patches 1 and 5 by Bob Picco related to contributions,
  fixes and reviews

Changelog since V5
o Add a missing #include to mm/mem_init.c
o Drop the verbose debugging part of the set
o Report active range registration when loglevel is set for KERN_DEBUG

Changelog since V4
o Rebase to 2.6.17-rc3-mm1
o Calculate holes on x86 with SRAT correctly

Changelog since V3
o Rebase to 2.6.17-rc2
o Allow the active regions to be cleared. Needed by x86_64 when it decides
  the SRAT table is bad half way through the registering of active regions
o Fix for flatmem x86_64 machines booting

Changelog since V2
o Fix a bug where holes in lower zones get double counted
o Catch the case where a new range is registered that is within an range
o Catch the case where a zone boundary is within a hole
o Use the EFI map for registering ranges on x86_64+numa
o On IA64+NUMA, add the active ranges before rounding for granules
o On x86_64, remove e820_hole_size and e820_bootmem_free and use
  arch-independent equivalents
o On x86_64, remove the map walk in e820_end_of_ram()
o Rename memory_present_with_active_regions, name ambiguous
o Add absent_pages_in_range() for arches to call

Changelog since V1
o Correctly convert virtual and physical addresses to PFNs on ia64
o Correctly convert physical addresses to PFN on older ppc 
o When add_active_range() is called with overlapping pfn ranges, merge them
o When a zone boundary occurs within a memory hole, account correctly
o Minor whitespace damage cleanup
o Debugging patch temporarily included

At a basic level, architectures define structures to record where active
ranges of page frames are located. Once located, the code to calculate
zone sizes and holes in each architecture is very similar.  Some of this
zone and hole sizing code is difficult to read for no good reason. This
set of patches eliminates the similar-looking architecture-specific code.

The patches introduce a mechanism where architectures register where the
active ranges of page frames are with add_active_range(). When all areas
have been discovered, free_area_init_nodes() is called to initialise
the pgdat and zones. The zone sizes and holes are then calculated in an
architecture independent manner.

Patch 1 introduces the mechanism for registering and initialising PFN ranges
Patch 2 changes ppc to use the mechanism - 134 arch-specific LOC removed
Patch 3 changes x86 to use the mechanism - 142 arch-specific LOC removed
Patch 4 changes x86_64 to use the mechanism - 78 arch-specific LOC removed
Patch 5 changes ia64 to use the mechanism - 57 arch-specific LOC removed
Patch 6 accounts for mem_map as a memory hole as the pages are not reclaimable.
	It adjusts the watermarks slightly

The patches have been successfully boot tested by me and verified that the
zones are the correct size on

o x86, flatmem with 1.5GiB of RAM
o x86, NUMAQ
o x86 with SRAT CONFIG_NUMA=n
o PPC64, NUMA
o PPC64, CONFIG_NUMA=n
o PPC64, CONFIG_64BIT=N
o x86_64, NUMA with SRAT
o x86_64, NUMA with broken SRAT that falls back to k8topology discovery
o x86_64, CONFIG_NUMA=n
o x86_64, CONFIG_64=n
o x86_64, CONFIG_64=n, CONFIG_NUMA=n
o x86_64, ACPI_NUMA, ACPI_MEMORY_HOTPLUG && !SPARSEMEM to trigger the
  hotadd path without sparsemem fun in srat.c (SRAT broken on test machine and
  I'm pretty sure the machine does not support physical memory hotadd anyway
  so test may not have been effective other than being a compile test.)
o ia64 (Itanium 2)
o ia64 (Itanium 2), CONFIG_64=N

Tony Luck has successfully tested for ia64 on Itanium with tiger_defconfig,
gensparse_defconfig and defconfig. Bob Picco has also tested and debugged
on IA64. Jack Steiner successfully boot tested on a mammoth SGI IA64-based
machine. These were on patches against 2.6.17-rc1 and release 3 of these
patches but there have been no ia64-changes since release 3.

There are differences in the zone sizes for x86_64 as the arch-specific code
for x86_64 accounts the kernel image and the starting mem_maps as memory
holes but the architecture-independent code accounts the memory as present.

The big benefit of this set of patches is the reduction of 411 lines of
architecture-specific code, some of which is very hairy. There should be
a greater net reduction when other architectures use the same mechanisms
for zone and hole sizing but I lack the hardware to test on.

Additional credit;
	Dave Hansen for the initial suggestion and comments on early patches
	Andy Whitcroft for reviewing early versions and catching numerous
		errors
	Tony Luck for testing and debugging on IA64
	Bob Picco for fixing bugs related to pfn registration, reviewing a
		number of patch revisions, providing a number of suggestions
		on future direction and testing heavily
	Jack Steiner and Robin Holt for testing on IA64 and clarifying
		issues related to memory holes
	Yasunori for testing on IA64
	Andi Kleen for reviewing and feeding back about x86_64
	Christian Kujau for providing valuable information related to ACPI
		problems on x86_64 and testing potential fixes
-- 
-- 
Mel Gorman
Part-time Phd Student                          Linux Technology Center
University of Limerick                         IBM Dublin Software Lab

^ permalink raw reply

* [PATCH 1/6] Introduce mechanism for registering active regions of memory
From: Mel Gorman @ 2006-07-08 11:11 UTC (permalink / raw)
  To: akpm
  Cc: davej, tony.luck, linuxppc-dev, Mel Gorman, linux-kernel,
	bob.picco, ak, linux-mm
In-Reply-To: <20060708111042.28664.14732.sendpatchset@skynet.skynet.ie>


This patch defines the structure to represent an active range of page
frames within a node in an architecture independent manner. Architectures
are expected to register active ranges of PFNs using add_active_range(nid,
start_pfn, end_pfn) and call free_area_init_nodes() passing the PFNs of
the end of each zone.


 include/linux/mm.h     |   45 +++
 include/linux/mmzone.h |   10 
 mm/page_alloc.c        |  557 ++++++++++++++++++++++++++++++++++++++++++--
 3 files changed, 586 insertions(+), 26 deletions(-)

Signed-off-by: Mel Gorman <mel@csn.ul.ie>
Signed-off-by: Bob Picco <bob.picco@hp.com>

diff -rup -X /usr/src/patchset-0.6/bin//dontdiff linux-2.6.17-mm6-clean/include/linux/mm.h linux-2.6.17-mm6-101-add_free_area_init_nodes/include/linux/mm.h
--- linux-2.6.17-mm6-clean/include/linux/mm.h	2006-07-05 14:31:17.000000000 +0100
+++ linux-2.6.17-mm6-101-add_free_area_init_nodes/include/linux/mm.h	2006-07-06 11:04:22.000000000 +0100
@@ -960,6 +960,51 @@ extern void free_area_init(unsigned long
 extern void free_area_init_node(int nid, pg_data_t *pgdat,
 	unsigned long * zones_size, unsigned long zone_start_pfn, 
 	unsigned long *zholes_size);
+#ifdef CONFIG_ARCH_POPULATES_NODE_MAP
+/*
+ * With CONFIG_ARCH_POPULATES_NODE_MAP set, an architecture may initialise its
+ * zones, allocate the backing mem_map and account for memory holes in a more
+ * architecture independent manner. This is a substitute for creating the
+ * zone_sizes[] and zholes_size[] arrays and passing them to
+ * free_area_init_node()
+ *
+ * An architecture is expected to register range of page frames backed by
+ * physical memory with add_active_range() before calling
+ * free_area_init_nodes() passing in the PFN each zone ends at. At a basic
+ * usage, an architecture is expected to do something like
+ *
+ * for_each_valid_physical_page_range()
+ * 	add_active_range(node_id, start_pfn, end_pfn)
+ * free_area_init_nodes(max_dma, max_dma32, max_normal_pfn, max_highmem_pfn);
+ *
+ * If the architecture guarantees that there are no holes in the ranges
+ * registered with add_active_range(), free_bootmem_active_regions()
+ * will call free_bootmem_node() for each registered physical page range.
+ * Similarly sparse_memory_present_with_active_regions() calls
+ * memory_present() for each range when SPARSEMEM is enabled.
+ *
+ * See mm/page_alloc.c for more information on each function exposed by
+ * CONFIG_ARCH_POPULATES_NODE_MAP
+ */
+extern void free_area_init_nodes(unsigned long max_dma_pfn,
+					unsigned long max_dma32_pfn,
+					unsigned long max_low_pfn,
+					unsigned long max_high_pfn);
+extern void add_active_range(unsigned int nid, unsigned long start_pfn,
+					unsigned long end_pfn);
+extern void shrink_active_range(unsigned int nid, unsigned long old_end_pfn,
+						unsigned long new_end_pfn);
+extern void remove_all_active_ranges(void);
+extern unsigned long absent_pages_in_range(unsigned long start_pfn,
+						unsigned long end_pfn);
+extern void get_pfn_range_for_nid(unsigned int nid,
+			unsigned long *start_pfn, unsigned long *end_pfn);
+extern unsigned long find_min_pfn_with_active_regions(void);
+extern unsigned long find_max_pfn_with_active_regions(void);
+extern void free_bootmem_with_active_regions(int nid,
+						unsigned long max_low_pfn);
+extern void sparse_memory_present_with_active_regions(int nid);
+#endif /* CONFIG_ARCH_POPULATES_NODE_MAP */
 extern void memmap_init_zone(unsigned long, int, unsigned long, unsigned long);
 extern void setup_per_zone_pages_min(void);
 extern void mem_init(void);
diff -rup -X /usr/src/patchset-0.6/bin//dontdiff linux-2.6.17-mm6-clean/include/linux/mmzone.h linux-2.6.17-mm6-101-add_free_area_init_nodes/include/linux/mmzone.h
--- linux-2.6.17-mm6-clean/include/linux/mmzone.h	2006-07-05 14:31:17.000000000 +0100
+++ linux-2.6.17-mm6-101-add_free_area_init_nodes/include/linux/mmzone.h	2006-07-06 11:04:22.000000000 +0100
@@ -293,6 +293,13 @@ struct zonelist {
 	struct zone *zones[MAX_NUMNODES * MAX_NR_ZONES + 1]; // NULL delimited
 };
 
+#ifdef CONFIG_ARCH_POPULATES_NODE_MAP
+struct node_active_region {
+	unsigned long start_pfn;
+	unsigned long end_pfn;
+	int nid;
+};
+#endif /* CONFIG_ARCH_POPULATES_NODE_MAP */
 
 /*
  * The pg_data_t structure is used in machines with CONFIG_DISCONTIGMEM
@@ -493,7 +500,8 @@ extern struct zone *next_zone(struct zon
 
 #endif
 
-#ifndef CONFIG_HAVE_ARCH_EARLY_PFN_TO_NID
+#if !defined(CONFIG_HAVE_ARCH_EARLY_PFN_TO_NID) && \
+	!defined(CONFIG_ARCH_POPULATES_NODE_MAP)
 #define early_pfn_to_nid(nid)  (0UL)
 #endif
 
diff -rup -X /usr/src/patchset-0.6/bin//dontdiff linux-2.6.17-mm6-clean/mm/page_alloc.c linux-2.6.17-mm6-101-add_free_area_init_nodes/mm/page_alloc.c
--- linux-2.6.17-mm6-clean/mm/page_alloc.c	2006-07-05 14:31:18.000000000 +0100
+++ linux-2.6.17-mm6-101-add_free_area_init_nodes/mm/page_alloc.c	2006-07-06 21:44:44.000000000 +0100
@@ -37,6 +37,8 @@
 #include <linux/vmalloc.h>
 #include <linux/mempolicy.h>
 #include <linux/stop_machine.h>
+#include <linux/sort.h>
+#include <linux/pfn.h>
 
 #include <asm/tlbflush.h>
 #include <asm/div64.h>
@@ -86,6 +88,33 @@ int min_free_kbytes = 1024;
 unsigned long __meminitdata nr_kernel_pages;
 unsigned long __meminitdata nr_all_pages;
 
+#ifdef CONFIG_ARCH_POPULATES_NODE_MAP
+  /*
+   * MAX_ACTIVE_REGIONS determines the maxmimum number of distinct
+   * ranges of memory (RAM) that may be registered with add_active_range().
+   * Ranges passed to add_active_range() will be merged if possible
+   * so the number of times add_active_range() can be called is
+   * related to the number of nodes and the number of holes
+   */
+  #ifdef CONFIG_MAX_ACTIVE_REGIONS
+    /* Allow an architecture to set MAX_ACTIVE_REGIONS to save memory */
+    #define MAX_ACTIVE_REGIONS CONFIG_MAX_ACTIVE_REGIONS
+  #else
+    #if MAX_NUMNODES >= 32
+      /* If there can be many nodes, allow up to 50 holes per node */
+      #define MAX_ACTIVE_REGIONS (MAX_NUMNODES*50)
+    #else
+      /* By default, allow up to 256 distinct regions */
+      #define MAX_ACTIVE_REGIONS 256
+    #endif
+  #endif
+
+  struct node_active_region __initdata early_node_map[MAX_ACTIVE_REGIONS];
+  int __initdata nr_nodemap_entries;
+  unsigned long __initdata arch_zone_lowest_possible_pfn[MAX_NR_ZONES];
+  unsigned long __initdata arch_zone_highest_possible_pfn[MAX_NR_ZONES];
+#endif /* CONFIG_ARCH_POPULATES_NODE_MAP */
+
 #ifdef CONFIG_DEBUG_VM
 static int page_outside_zone_boundaries(struct zone *zone, struct page *page)
 {
@@ -1728,25 +1757,6 @@ static inline unsigned long wait_table_b
 
 #define LONG_ALIGN(x) (((x)+(sizeof(long))-1)&~((sizeof(long))-1))
 
-static void __init calculate_zone_totalpages(struct pglist_data *pgdat,
-		unsigned long *zones_size, unsigned long *zholes_size)
-{
-	unsigned long realtotalpages, totalpages = 0;
-	int i;
-
-	for (i = 0; i < MAX_NR_ZONES; i++)
-		totalpages += zones_size[i];
-	pgdat->node_spanned_pages = totalpages;
-
-	realtotalpages = totalpages;
-	if (zholes_size)
-		for (i = 0; i < MAX_NR_ZONES; i++)
-			realtotalpages -= zholes_size[i];
-	pgdat->node_present_pages = realtotalpages;
-	printk(KERN_DEBUG "On node %d totalpages: %lu\n", pgdat->node_id, realtotalpages);
-}
-
-
 /*
  * Initially all pages are reserved - free ones are freed
  * up by free_all_bootmem() once the early boot process is
@@ -2064,6 +2074,272 @@ __meminit int init_currently_empty_zone(
 	return 0;
 }
 
+#ifdef CONFIG_ARCH_POPULATES_NODE_MAP
+/*
+ * Basic iterator support. Return the first range of PFNs for a node
+ * Note: nid == MAX_NUMNODES returns first region regardless of node
+ */
+static int __init first_active_region_index_in_nid(int nid)
+{
+	int i;
+
+	for (i = 0; i < nr_nodemap_entries; i++)
+		if (nid == MAX_NUMNODES || early_node_map[i].nid == nid)
+			return i;
+
+	return -1;
+}
+
+/*
+ * Basic iterator support. Return the next active range of PFNs for a node
+ * Note: nid == MAX_NUMNODES returns next region regardles of node
+ */
+static int __init next_active_region_index_in_nid(int index, int nid)
+{
+	for (index = index + 1; index < nr_nodemap_entries; index++)
+		if (nid == MAX_NUMNODES || early_node_map[index].nid == nid)
+			return index;
+
+	return -1;
+}
+
+#ifndef CONFIG_HAVE_ARCH_EARLY_PFN_TO_NID
+/*
+ * Required by SPARSEMEM. Given a PFN, return what node the PFN is on.
+ * Architectures may implement their own version but if add_active_range()
+ * was used and there are no special requirements, this is a convenient
+ * alternative
+ */
+int __init early_pfn_to_nid(unsigned long pfn)
+{
+	int i;
+
+	for (i = 0; i < nr_nodemap_entries; i++) {
+		unsigned long start_pfn = early_node_map[i].start_pfn;
+		unsigned long end_pfn = early_node_map[i].end_pfn;
+
+		if (start_pfn <= pfn && pfn < end_pfn)
+			return early_node_map[i].nid;
+	}
+
+	return 0;
+}
+#endif /* CONFIG_HAVE_ARCH_EARLY_PFN_TO_NID */
+
+/* Basic iterator support to walk early_node_map[] */
+#define for_each_active_range_index_in_nid(i, nid) \
+	for (i = first_active_region_index_in_nid(nid); i != -1; \
+				i = next_active_region_index_in_nid(i, nid))
+
+/**
+ * free_bootmem_with_active_regions - Call free_bootmem_node for each active range
+ * @nid: The node to free memory on. If MAX_NUMNODES, all nodes are freed
+ * @max_low_pfn: The highest PFN that till be passed to free_bootmem_node
+ *
+ * If an architecture guarantees that all ranges registered with
+ * add_active_ranges() contain no holes and may be freed, this
+ * this function may be used instead of calling free_bootmem() manually.
+ */
+void __init free_bootmem_with_active_regions(int nid,
+						unsigned long max_low_pfn)
+{
+	int i;
+
+	for_each_active_range_index_in_nid(i, nid) {
+		unsigned long size_pages = 0;
+		unsigned long end_pfn = early_node_map[i].end_pfn;
+
+		if (early_node_map[i].start_pfn >= max_low_pfn)
+			continue;
+
+		if (end_pfn > max_low_pfn)
+			end_pfn = max_low_pfn;
+
+		size_pages = end_pfn - early_node_map[i].start_pfn;
+		free_bootmem_node(NODE_DATA(early_node_map[i].nid),
+				PFN_PHYS(early_node_map[i].start_pfn),
+				size_pages << PAGE_SHIFT);
+	}
+}
+
+/**
+ * sparse_memory_present_with_active_regions - Call memory_present for each active range
+ * @nid: The node to call memory_present for. If MAX_NUMNODES, all nodes will be used
+ *
+ * If an architecture guarantees that all ranges registered with
+ * add_active_ranges() contain no holes and may be freed, this
+ * this function may be used instead of calling memory_present() manually.
+ */
+void __init sparse_memory_present_with_active_regions(int nid)
+{
+	int i;
+
+	for_each_active_range_index_in_nid(i, nid)
+		memory_present(early_node_map[i].nid,
+				early_node_map[i].start_pfn,
+				early_node_map[i].end_pfn);
+}
+
+/**
+ * get_pfn_range_for_nid - Return the start and end page frames for a node
+ * @nid: The nid to return the range for. If MAX_NUMNODES, the min and max PFN are returned
+ * @start_pfn: Passed by reference. On return, it will have the node start_pfn
+ * @end_pfn: Passed by reference. On return, it will have the node end_pfn
+ *
+ * It returns the start and end page frame of a node based on information
+ * provided by an arch calling add_active_range(). If called for a node
+ * with no available memory, a warning is printed and the start and end
+ * PFNs will be 0
+ */
+void __init get_pfn_range_for_nid(unsigned int nid,
+			unsigned long *start_pfn, unsigned long *end_pfn)
+{
+	int i;
+	*start_pfn = -1UL;
+	*end_pfn = 0;
+
+	for_each_active_range_index_in_nid(i, nid) {
+		*start_pfn = min(*start_pfn, early_node_map[i].start_pfn);
+		*end_pfn = max(*end_pfn, early_node_map[i].end_pfn);
+	}
+
+	if (*start_pfn == -1UL) {
+		printk(KERN_WARNING "Node %u active with no memory\n", nid);
+		*start_pfn = 0;
+	}
+}
+
+/*
+ * Return the number of pages a zone spans in a node, including holes
+ * present_pages = zone_spanned_pages_in_node() - zone_absent_pages_in_node()
+ */
+unsigned long __init zone_spanned_pages_in_node(int nid,
+					unsigned long zone_type,
+					unsigned long *ignored)
+{
+	unsigned long node_start_pfn, node_end_pfn;
+	unsigned long zone_start_pfn, zone_end_pfn;
+
+	/* Get the start and end of the node and zone */
+	get_pfn_range_for_nid(nid, &node_start_pfn, &node_end_pfn);
+	zone_start_pfn = arch_zone_lowest_possible_pfn[zone_type];
+	zone_end_pfn = arch_zone_highest_possible_pfn[zone_type];
+
+	/* Check that this node has pages within the zone's required range */
+	if (zone_end_pfn < node_start_pfn || zone_start_pfn > node_end_pfn)
+		return 0;
+
+	/* Move the zone boundaries inside the node if necessary */
+	zone_end_pfn = min(zone_end_pfn, node_end_pfn);
+	zone_start_pfn = max(zone_start_pfn, node_start_pfn);
+
+	/* Return the spanned pages */
+	return zone_end_pfn - zone_start_pfn;
+}
+
+/*
+ * Return the number of holes in a range on a node. If nid is MAX_NUMNODES,
+ * then all holes in the requested range will be accounted for
+ */
+unsigned long __init __absent_pages_in_range(int nid,
+				unsigned long range_start_pfn,
+				unsigned long range_end_pfn)
+{
+	int i = 0;
+	unsigned long prev_end_pfn = 0, hole_pages = 0;
+	unsigned long start_pfn;
+
+	/* Find the end_pfn of the first active range of pfns in the node */
+	i = first_active_region_index_in_nid(nid);
+	if (i == -1)
+		return 0;
+
+	prev_end_pfn = early_node_map[i].start_pfn;
+
+	/* Find all holes for the zone within the node */
+	for (; i != -1; i = next_active_region_index_in_nid(i, nid)) {
+
+		/* No need to continue if prev_end_pfn is outside the zone */
+		if (prev_end_pfn >= range_end_pfn)
+			break;
+
+		/* Make sure the end of the zone is not within the hole */
+		start_pfn = min(early_node_map[i].start_pfn, range_end_pfn);
+		prev_end_pfn = max(prev_end_pfn, range_start_pfn);
+
+		/* Update the hole size cound and move on */
+		if (start_pfn > range_start_pfn) {
+			BUG_ON(prev_end_pfn > start_pfn);
+			hole_pages += start_pfn - prev_end_pfn;
+		}
+		prev_end_pfn = early_node_map[i].end_pfn;
+	}
+
+	return hole_pages;
+}
+
+/**
+ * absent_pages_in_range - Return number of page frames in holes within a range
+ * @start_pfn: The start PFN to start searching for holes
+ * @end_pfn: The end PFN to stop searching for holes
+ *
+ * It returns the number of pages frames in memory holes within a range
+ */
+unsigned long __init absent_pages_in_range(unsigned long start_pfn,
+							unsigned long end_pfn)
+{
+	return __absent_pages_in_range(MAX_NUMNODES, start_pfn, end_pfn);
+}
+
+/* Return the number of page frames in holes in a zone on a node */
+unsigned long __init zone_absent_pages_in_node(int nid,
+					unsigned long zone_type,
+					unsigned long *ignored)
+{
+	return __absent_pages_in_range(nid,
+				arch_zone_lowest_possible_pfn[zone_type],
+				arch_zone_highest_possible_pfn[zone_type]);
+}
+#else
+static inline unsigned long zone_spanned_pages_in_node(int nid,
+					unsigned long zone_type,
+					unsigned long *zones_size)
+{
+	return zones_size[zone_type];
+}
+
+static inline unsigned long zone_absent_pages_in_node(int nid,
+						unsigned long zone_type,
+						unsigned long *zholes_size)
+{
+	if (!zholes_size)
+		return 0;
+
+	return zholes_size[zone_type];
+}
+#endif
+
+static void __init calculate_node_totalpages(struct pglist_data *pgdat,
+		unsigned long *zones_size, unsigned long *zholes_size)
+{
+	unsigned long realtotalpages, totalpages = 0;
+	int i;
+
+	for (i = 0; i < MAX_NR_ZONES; i++)
+		totalpages += zone_spanned_pages_in_node(pgdat->node_id, i,
+								zones_size);
+	pgdat->node_spanned_pages = totalpages;
+
+	realtotalpages = totalpages;
+	for (i = 0; i < MAX_NR_ZONES; i++)
+		realtotalpages -=
+			zone_absent_pages_in_node(pgdat->node_id, i,
+								zholes_size);
+	pgdat->node_present_pages = realtotalpages;
+	printk(KERN_DEBUG "On node %d totalpages: %lu\n", pgdat->node_id,
+							realtotalpages);
+}
+
 /*
  * Set up the zone data structures:
  *   - mark all pages reserved
@@ -2087,10 +2363,9 @@ static void __meminit free_area_init_cor
 		struct zone *zone = pgdat->node_zones + j;
 		unsigned long size, realsize;
 
-		realsize = size = zones_size[j];
-		if (zholes_size)
-			realsize -= zholes_size[j];
-
+		size = zone_spanned_pages_in_node(nid, j, zones_size);
+		realsize = size - zone_absent_pages_in_node(nid, j,
+								zholes_size);
 		if (j < ZONE_HIGHMEM)
 			nr_kernel_pages += realsize;
 		nr_all_pages += realsize;
@@ -2159,8 +2434,13 @@ static void __init alloc_node_mem_map(st
 	/*
 	 * With no DISCONTIG, the global mem_map is just set as node 0's
 	 */
-	if (pgdat == NODE_DATA(0))
+	if (pgdat == NODE_DATA(0)) {
 		mem_map = NODE_DATA(0)->node_mem_map;
+#ifdef CONFIG_ARCH_POPULATES_NODE_MAP
+		if (page_to_pfn(mem_map) != pgdat->node_start_pfn)
+			mem_map -= pgdat->node_start_pfn;
+#endif /* CONFIG_ARCH_POPULATES_NODE_MAP */
+	}
 #endif
 #endif /* CONFIG_FLAT_NODE_MEM_MAP */
 }
@@ -2171,13 +2451,240 @@ void __meminit free_area_init_node(int n
 {
 	pgdat->node_id = nid;
 	pgdat->node_start_pfn = node_start_pfn;
-	calculate_zone_totalpages(pgdat, zones_size, zholes_size);
+	calculate_node_totalpages(pgdat, zones_size, zholes_size);
 
 	alloc_node_mem_map(pgdat);
 
 	free_area_init_core(pgdat, zones_size, zholes_size);
 }
 
+#ifdef CONFIG_ARCH_POPULATES_NODE_MAP
+/**
+ * add_active_range - Register a range of PFNs backed by physical memory
+ * @nid: The node ID the range resides on
+ * @start_pfn: The start PFN of the available physical memory
+ * @end_pfn: The end PFN of the available physical memory
+ *
+ * These ranges are stored in an early_node_map[] and later used by
+ * free_area_init_nodes() to calculate zone sizes and holes. If the
+ * range spans a memory hole, it is up to the architecture to ensure
+ * the memory is not freed by the bootmem allocator. If possible
+ * the range being registered will be merged with existing ranges.
+ */
+void __init add_active_range(unsigned int nid, unsigned long start_pfn,
+						unsigned long end_pfn)
+{
+	int i;
+
+	printk(KERN_DEBUG "Entering add_active_range(%d, %lu, %lu) "
+			  "%d entries of %d used\n",
+			  nid, start_pfn, end_pfn,
+			  nr_nodemap_entries, MAX_ACTIVE_REGIONS);
+
+	/* Merge with existing active regions if possible */
+	for (i = 0; i < nr_nodemap_entries; i++) {
+		if (early_node_map[i].nid != nid)
+			continue;
+
+		/* Skip if an existing region covers this new one */
+		if (start_pfn >= early_node_map[i].start_pfn &&
+				end_pfn <= early_node_map[i].end_pfn)
+			return;
+
+		/* Merge forward if suitable */
+		if (start_pfn <= early_node_map[i].end_pfn &&
+				end_pfn > early_node_map[i].end_pfn) {
+			early_node_map[i].end_pfn = end_pfn;
+			return;
+		}
+
+		/* Merge backward if suitable */
+		if (start_pfn < early_node_map[i].end_pfn &&
+				end_pfn >= early_node_map[i].start_pfn) {
+			early_node_map[i].start_pfn = start_pfn;
+			return;
+		}
+	}
+
+	/* Check that early_node_map is large enough */
+	if (i >= MAX_ACTIVE_REGIONS) {
+		printk(KERN_CRIT "More than %d memory regions, truncating\n",
+							MAX_ACTIVE_REGIONS);
+		return;
+	}
+
+	early_node_map[i].nid = nid;
+	early_node_map[i].start_pfn = start_pfn;
+	early_node_map[i].end_pfn = end_pfn;
+	nr_nodemap_entries = i + 1;
+}
+
+/**
+ * shrink_active_range - Shrink an existing registered range of PFNs
+ * @nid: The node id the range is on that should be shrunk
+ * @old_end_pfn: The old end PFN of the range
+ * @new_end_pfn: The new PFN of the range
+ *
+ * i386 with NUMA use alloc_remap() to store a node_mem_map on a local node.
+ * The map is kept at the end physical page range that has already been
+ * registered with add_active_range(). This function allows an arch to shrink
+ * an existing registered range.
+ */
+void __init shrink_active_range(unsigned int nid, unsigned long old_end_pfn,
+						unsigned long new_end_pfn)
+{
+	int i;
+
+	/* Find the old active region end and shrink */
+	for_each_active_range_index_in_nid(i, nid)
+		if (early_node_map[i].end_pfn == old_end_pfn) {
+			early_node_map[i].end_pfn = new_end_pfn;
+			break;
+		}
+}
+
+/**
+ * remove_all_active_ranges - Remove all currently registered regions
+ * During discovery, it may be found that a table like SRAT is invalid
+ * and an alternative discovery method must be used. This function removes
+ * all currently registered regions.
+ */
+void __init remove_all_active_ranges()
+{
+	memset(early_node_map, 0, sizeof(early_node_map));
+	nr_nodemap_entries = 0;
+}
+
+/* Compare two active node_active_regions */
+static int __init cmp_node_active_region(const void *a, const void *b)
+{
+	struct node_active_region *arange = (struct node_active_region *)a;
+	struct node_active_region *brange = (struct node_active_region *)b;
+
+	/* Done this way to avoid overflows */
+	if (arange->start_pfn > brange->start_pfn)
+		return 1;
+	if (arange->start_pfn < brange->start_pfn)
+		return -1;
+
+	return 0;
+}
+
+/* sort the node_map by start_pfn */
+static void __init sort_node_map(void)
+{
+	sort(early_node_map, (size_t)nr_nodemap_entries,
+			sizeof(struct node_active_region),
+			cmp_node_active_region, NULL);
+}
+
+/* Find the lowest pfn for a node. This depends on a sorted early_node_map */
+unsigned long __init find_min_pfn_for_node(unsigned long nid)
+{
+	int i;
+
+	/* Assuming a sorted map, the first range found has the starting pfn */
+	for_each_active_range_index_in_nid(i, nid)
+		return early_node_map[i].start_pfn;
+
+	printk(KERN_WARNING "Could not find start_pfn for node %lu\n", nid);
+	return 0;
+}
+
+/**
+ * find_min_pfn_with_active_regions - Find the minimum PFN registered
+ *
+ * It returns the minimum PFN based on information provided via
+ * add_active_range()
+ */
+unsigned long __init find_min_pfn_with_active_regions(void)
+{
+	return find_min_pfn_for_node(MAX_NUMNODES);
+}
+
+/**
+ * find_max_pfn_with_active_regions - Find the maximum PFN registered
+ *
+ * It returns the maximum PFN based on information provided via
+ * add_active_range()
+ */
+unsigned long __init find_max_pfn_with_active_regions(void)
+{
+	int i;
+	unsigned long max_pfn = 0;
+
+	for (i = 0; i < nr_nodemap_entries; i++)
+		max_pfn = max(max_pfn, early_node_map[i].end_pfn);
+
+	return max_pfn;
+}
+
+/**
+ * free_area_init_nodes - Initialise all pg_data_t and zone data
+ * @arch_max_dma_pfn: The maximum PFN usable for ZONE_DMA
+ * @arch_max_dma32_pfn: The maximum PFN usable for ZONE_DMA32
+ * @arch_max_low_pfn: The maximum PFN usable for ZONE_NORMAL
+ * @arch_max_high_pfn: The maximum PFN usable for ZONE_HIGHMEM
+ *
+ * This will call free_area_init_node() for each active node in the system.
+ * Using the page ranges provided by add_active_range(), the size of each
+ * zone in each node and their holes is calculated. If the maximum PFN
+ * between two adjacent zones match, it is assumed that the zone is empty.
+ * For example, if arch_max_dma_pfn == arch_max_dma32_pfn, it is assumed
+ * that arch_max_dma32_pfn has no pages. It is also assumed that a zone
+ * starts where the previous one ended. For example, ZONE_DMA32 starts
+ * at arch_max_dma_pfn.
+ */
+void __init free_area_init_nodes(unsigned long arch_max_dma_pfn,
+				unsigned long arch_max_dma32_pfn,
+				unsigned long arch_max_low_pfn,
+				unsigned long arch_max_high_pfn)
+{
+	unsigned long nid;
+	int i;
+
+	/* Record where the zone boundaries are */
+	memset(arch_zone_lowest_possible_pfn, 0,
+				sizeof(arch_zone_lowest_possible_pfn));
+	memset(arch_zone_highest_possible_pfn, 0,
+				sizeof(arch_zone_highest_possible_pfn));
+	arch_zone_lowest_possible_pfn[ZONE_DMA] =
+					find_min_pfn_with_active_regions();
+	arch_zone_highest_possible_pfn[ZONE_DMA] = arch_max_dma_pfn;
+	arch_zone_highest_possible_pfn[ZONE_DMA32] = arch_max_dma32_pfn;
+	arch_zone_highest_possible_pfn[ZONE_NORMAL] = arch_max_low_pfn;
+	arch_zone_highest_possible_pfn[ZONE_HIGHMEM] = arch_max_high_pfn;
+	for (i = 1; i < MAX_NR_ZONES; i++)
+		arch_zone_lowest_possible_pfn[i] =
+			arch_zone_highest_possible_pfn[i-1];
+
+	/* Regions in the early_node_map can be in any order */
+	sort_node_map();
+
+	/* Print out the zone ranges */
+	printk("Zone PFN ranges:\n");
+	for (i = 0; i < MAX_NR_ZONES; i++)
+		printk("  %-8s %8lu -> %8lu\n",
+				zone_names[i],
+				arch_zone_lowest_possible_pfn[i],
+				arch_zone_highest_possible_pfn[i]);
+
+	/* Print out the early_node_map[] */
+	printk("early_node_map[%d] active PFN ranges\n", nr_nodemap_entries);
+	for (i = 0; i < nr_nodemap_entries; i++)
+		printk("  %3d: %8lu -> %8lu\n", early_node_map[i].nid,
+						early_node_map[i].start_pfn,
+						early_node_map[i].end_pfn);
+
+	/* Initialise every node */
+	for_each_online_node(nid) {
+		pg_data_t *pgdat = NODE_DATA(nid);
+		free_area_init_node(nid, pgdat, NULL,
+				find_min_pfn_for_node(nid), NULL);
+	}
+}
+#endif /* CONFIG_ARCH_POPULATES_NODE_MAP */
+
 #ifndef CONFIG_NEED_MULTIPLE_NODES
 static bootmem_data_t contig_bootmem_data;
 struct pglist_data contig_page_data = { .bdata = &contig_bootmem_data };

^ permalink raw reply


This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox