LinuxPPC-Dev Archive on lore.kernel.org
 help / color / mirror / Atom feed
* Re: [PATCH 3/7] Update the [register,unregister]_memory routines
From: Nathan Fontenot @ 2010-07-13 15:46 UTC (permalink / raw)
  To: KAMEZAWA Hiroyuki; +Cc: linuxppc-dev, linux-kernel
In-Reply-To: <20100713152044.7ec8c9ae.kamezawa.hiroyu@jp.fujitsu.com>

On 07/13/2010 01:20 AM, KAMEZAWA Hiroyuki wrote:
> On Mon, 12 Jul 2010 10:44:10 -0500
> Nathan Fontenot <nfont@austin.ibm.com> wrote:
> 
>> This patch moves the register/unregister_memory routines to
>> avoid a forward declaration.  It also moves the sysfs file
>> creation and deletion for each directory into the register/
>> unregister routines to avoid duplicating it with these updates.
>>
>> Signed-off-by: Nathan Fontenot <nfont@austin.ibm.com>
>> ---
>>  drivers/base/memory.c |   93 +++++++++++++++++++++++++-------------------------
>>  1 file changed, 48 insertions(+), 45 deletions(-)
>>
>> Index: linux-2.6/drivers/base/memory.c
>> ===================================================================
>> --- linux-2.6.orig/drivers/base/memory.c	2010-07-09 14:23:17.000000000 -0500
>> +++ linux-2.6/drivers/base/memory.c	2010-07-09 14:23:20.000000000 -0500
>> @@ -87,31 +87,6 @@
>>  EXPORT_SYMBOL(unregister_memory_isolate_notifier);
>>  
>>  /*
>> - * register_memory - Setup a sysfs device for a memory block
>> - */
>> -static
>> -int register_memory(struct memory_block *memory, struct mem_section *section)
>> -{
>> -	int error;
>> -
>> -	memory->sysdev.cls = &memory_sysdev_class;
>> -	memory->sysdev.id = __section_nr(section);
>> -
>> -	error = sysdev_register(&memory->sysdev);
>> -	return error;
>> -}
>> -
>> -static void
>> -unregister_memory(struct memory_block *memory)
>> -{
>> -	BUG_ON(memory->sysdev.cls != &memory_sysdev_class);
>> -
>> -	/* drop the ref. we got in remove_memory_block() */
>> -	kobject_put(&memory->sysdev.kobj);
>> -	sysdev_unregister(&memory->sysdev);
>> -}
>> -
>> -/*
>>   * use this as the physical section index that this memsection
>>   * uses.
>>   */
>> @@ -346,6 +321,53 @@
>>  	sysdev_remove_file(&mem->sysdev, &attr_##attr_name)
>>  
>>  /*
>> + * register_memory - Setup a sysfs device for a memory block
>> + */
>> +static
>> +int register_memory(struct memory_block *memory, struct mem_section *section,
>> +		    int nid, enum mem_add_context context)
>> +{
>> +	int ret;
>> +
>> +	memory->sysdev.cls = &memory_sysdev_class;
>> +	memory->sysdev.id = __section_nr(section);
>> +

> Why not block-ID  but section-ID ?

Using the beginning section id as the id here makes the splitting of
memory_block's easier since we can assume that the id is unique.

> 
> -Kame
> 

^ permalink raw reply

* Re: [PATCH 1/7] Split the memory_block structure
From: Nathan Fontenot @ 2010-07-13 15:44 UTC (permalink / raw)
  To: KAMEZAWA Hiroyuki; +Cc: linuxppc-dev, linux-kernel
In-Reply-To: <20100713151855.3d56242d.kamezawa.hiroyu@jp.fujitsu.com>

Thanks for the review, answers below...

-Nathan

On 07/13/2010 01:18 AM, KAMEZAWA Hiroyuki wrote:
> 
> plz cc linux-mm in the next time...
> And please incudes updates for Documentation/memory-hotplug.txt.
> 

will do.
> 
> On Mon, 12 Jul 2010 10:42:06 -0500
> Nathan Fontenot <nfont@austin.ibm.com> wrote:
> 
>> This patch splits the memory_block struct into a memory_block
>> struct to cover each sysfs directory and a new memory_block_section
>> struct for each memory section covered by the sysfs directory.
>>
>> This also updates the routine handling memory_block creation
>> and manipulation to use these updated structures.
>>
> 
> Could you clarify the number of memory_block_section per memory_block ?

The default number of memory_block_sections per memory block is 1.  The
memory_block_size() routine (defined as __weak) sets the size of the
memory block, and thus the number of memory_block_sections.

The current view of memory in sysfs where each directory covers a single
memory section should still hold for everyone, unless a arch defines
their own memory_section_size routine to alter the behavior.

> 
> 
>> Signed -off-by: Nathan Fontenot <nfont@austin.ibm.com>
>> ---
>>  drivers/base/memory.c  |  228 +++++++++++++++++++++++++++++++++++--------------
>>  include/linux/memory.h |   11 +-
>>  2 files changed, 172 insertions(+), 67 deletions(-)
>>
>> Index: linux-2.6/drivers/base/memory.c
>> ===================================================================
>> --- linux-2.6.orig/drivers/base/memory.c	2010-07-08 11:27:21.000000000 -0500
>> +++ linux-2.6/drivers/base/memory.c	2010-07-09 14:23:09.000000000 -0500
>> @@ -28,6 +28,14 @@
>>  #include <asm/uaccess.h>
>>  
>>  #define MEMORY_CLASS_NAME	"memory"
>> +#define MIN_MEMORY_BLOCK_SIZE	(1 << SECTION_SIZE_BITS)
>> +
>> +static int sections_per_block;
>> +
> some default value, plz. Does this can be determined only by .config ?

The default is 1.  This is determined in the get_memory_block_size() which
is called from the memory sysfs init routine.

> 
> 
>> +static inline int base_memory_block_id(int section_nr)
>> +{
>> +	return (section_nr / sections_per_block) * sections_per_block;
>> +}
>>  
>>  static struct sysdev_class memory_sysdev_class = {
>>  	.name = MEMORY_CLASS_NAME,
>> @@ -94,10 +102,9 @@
>>  }
>>  
>>  static void
>> -unregister_memory(struct memory_block *memory, struct mem_section *section)
>> +unregister_memory(struct memory_block *memory)
>>  {
>>  	BUG_ON(memory->sysdev.cls != &memory_sysdev_class);
>> -	BUG_ON(memory->sysdev.id != __section_nr(section));
>>  
>>  	/* drop the ref. we got in remove_memory_block() */
>>  	kobject_put(&memory->sysdev.kobj);
>> @@ -123,13 +130,20 @@
>>  static ssize_t show_mem_removable(struct sys_device *dev,
>>  			struct sysdev_attribute *attr, char *buf)
>>  {
>> -	unsigned long start_pfn;
>> -	int ret;
>> -	struct memory_block *mem =
>> -		container_of(dev, struct memory_block, sysdev);
>> +	struct list_head *pos, *tmp;
>> +	struct memory_block *mem;
>> +	int ret = 1;
>> +
>> +	mem = container_of(dev, struct memory_block, sysdev);
>> +	list_for_each_safe(pos, tmp, &mem->sections) {
>> +		struct memory_block_section *mbs;
>> +		unsigned long start_pfn;
>> +
>> +		mbs = list_entry(pos, struct memory_block_section, next);
> 
> list_for_each_entry ?

I went with list_for_each_safe() here since I am not holding the mutex
while walking the list.  Perhaps this should be changed to take the
mutex and use list_for_each_entry().

> 
> 
> 
>> +		start_pfn = section_nr_to_pfn(mbs->phys_index);
>> +		ret &= is_mem_section_removable(start_pfn, PAGES_PER_SECTION);
>> +	}
> 
> Hmm, them, only when the whole memory block is removable, it's shown as
> removable. Right ?
> Does it meets ppc guy's requirements ?

Yes, and yes.

> 
>>  
>> -	start_pfn = section_nr_to_pfn(mem->phys_index);
>> -	ret = is_mem_section_removable(start_pfn, PAGES_PER_SECTION);
>>  	return sprintf(buf, "%d\n", ret);
>>  }
> 
> Hmm...can't you print removable information as bitmap, here ?
> overkill ?

We could print it as a bitmap, but I think it would be overkill.  The
memory add/remove routines work on a memory_block such that all
memory_block_sections in the memory_block are added/removed as a whole.
Given this, I figured we only needed to know if the entire memory_block
is removable.

> 
> 
>>  
>> @@ -182,16 +196,16 @@
>>   * OK to have direct references to sparsemem variables in here.
>>   */
>>  static int
>> -memory_block_action(struct memory_block *mem, unsigned long action)
>> +memory_block_action(struct memory_block_section *mbs, unsigned long action)
>>  {
>>  	int i;
>>  	unsigned long psection;
>>  	unsigned long start_pfn, start_paddr;
>>  	struct page *first_page;
>>  	int ret;
>> -	int old_state = mem->state;
>> ot-option-to-disable-memory-hotplug.patch
>> +	int old_state = mbs->state;
> 
> Where is this noise from ?

Yuck!  I'll take a look.  That shouldn't be there obviously.

> 
>>  
>> -	psection = mem->phys_index;
>> +	psection = mbs->phys_index;
>>  	first_page = pfn_to_page(psection << PFN_SECTION_SHIFT);
>>  
>>  	/*
>> @@ -217,18 +231,18 @@
>>  			ret = online_pages(start_pfn, PAGES_PER_SECTION);
>>  			break;
>>  		case MEM_OFFLINE:
>> -			mem->state = MEM_GOING_OFFLINE;
>> +			mbs->state = MEM_GOING_OFFLINE;
>>  			start_paddr = page_to_pfn(first_page) << PAGE_SHIFT;
>>  			ret = remove_memory(start_paddr,
>>  					    PAGES_PER_SECTION << PAGE_SHIFT);
>>  			if (ret) {
>> -				mem->state = old_state;
>> +				mbs->state = old_state;
>>  				break;
>>  			}
>>  			break;
>>  		default:
>>  			WARN(1, KERN_WARNING "%s(%p, %ld) unknown action: %ld\n",
>> -					__func__, mem, action, action);
>> +					__func__, mbs, action, action);
>>  			ret = -EINVAL;
>>  	}
>>  
>> @@ -238,19 +252,40 @@
>>  static int memory_block_change_state(struct memory_block *mem,
>>  		unsigned long to_state, unsigned long from_state_req)
>>  {
>> +	struct memory_block_section *mbs;
>> +	struct list_head *pos;
>>  	int ret = 0;
>> +
>>  	mutex_lock(&mem->state_mutex);
>>  
>> -	if (mem->state != from_state_req) {
>> -		ret = -EINVAL;
>> -		goto out;
>> +	list_for_each(pos, &mem->sections) {
>> +		mbs = list_entry(pos, struct memory_block_section, next);
>> +
> 
> list_for_each_entry() ?

That could be done here.

> 
>> +		if (mbs->state != from_state_req)
>> +			continue;
>> +
>> +		ret = memory_block_action(mbs, to_state);
>> +		if (ret)
>> +			break;
>> +	}
> 
> Then, all actions will be affect all memory sections under memory block ?
> (Hmm..maybe have to see following patches ?)

Correct.  Add/remove actions will work on a memory_block as a whole.

> 
> 
>> +
>> +	if (ret) {
>> +		list_for_each(pos, &mem->sections) {
>> +			mbs = list_entry(pos, struct memory_block_section,
>> +					 next);
>> +
> list_for_each_entry() ?

got it. :)

> 
>> +			if (mbs->state == from_state_req)
>> +				continue;
>> +
>> +			if (memory_block_action(mbs, to_state))
>> +				printk(KERN_ERR "Could not re-enable memory "
>> +				       "section %lx\n", mbs->phys_index);
>> +		}
>>  	}
>>  
>> -	ret = memory_block_action(mem, to_state);
>>  	if (!ret)
>>  		mem->state = to_state;
>>  
>> -out:
>>  	mutex_unlock(&mem->state_mutex);
>>  	return ret;
>>  }
>> @@ -260,20 +295,15 @@
>>  		struct sysdev_attribute *attr, const char *buf, size_t count)
>>  {
> 
> Hmm, store_mem_state() ? What diff option are you using ?

Yes, this is store_mem_state.

Patches were generated with quilt.

> 
> 
>>  	struct memory_block *mem;
>> -	unsigned int phys_section_nr;
>>  	int ret = -EINVAL;
>>  
>>  	mem = container_of(dev, struct memory_block, sysdev);
>> -	phys_section_nr = mem->phys_index;
>> -
>> -	if (!present_section_nr(phys_section_nr))
>> -		goto out;
>>  
>>  	if (!strncmp(buf, "online", min((int)count, 6)))
>>  		ret = memory_block_change_state(mem, MEM_ONLINE, MEM_OFFLINE);
>>  	else if(!strncmp(buf, "offline", min((int)count, 7)))
>>  		ret = memory_block_change_state(mem, MEM_OFFLINE, MEM_ONLINE);
>> -out:
>> +
>>  	if (ret)
>>  		return ret;
>>  	return count;
>> @@ -435,39 +465,6 @@
>>  	return 0;
>>  }
>>  
>> -static int add_memory_block(int nid, struct mem_section *section,
>> -			unsigned long state, enum mem_add_context context)
>> -{
>> -	struct memory_block *mem = kzalloc(sizeof(*mem), GFP_KERNEL);
>> -	unsigned long start_pfn;
>> -	int ret = 0;
>> -
>> -	if (!mem)
>> -		return -ENOMEM;
>> -
>> -	mem->phys_index = __section_nr(section);
>> -	mem->state = state;
>> -	mutex_init(&mem->state_mutex);
>> -	start_pfn = section_nr_to_pfn(mem->phys_index);
>> -	mem->phys_device = arch_get_memory_phys_device(start_pfn);
>> -
>> -	ret = register_memory(mem, section);
>> -	if (!ret)
>> -		ret = mem_create_simple_file(mem, phys_index);
>> -	if (!ret)
>> -		ret = mem_create_simple_file(mem, state);
>> -	if (!ret)
>> -		ret = mem_create_simple_file(mem, phys_device);
>> -	if (!ret)
>> -		ret = mem_create_simple_file(mem, removable);
>> -	if (!ret) {
>> -		if (context == HOTPLUG)
>> -			ret = register_mem_sect_under_node(mem, nid);
>> -	}
>> -
>> -	return ret;
>> -}
>> -
> 
> 
> please divide clean-up and logic-change patches into their own..

ok.
> 
> 
>>  /*
>>   * For now, we have a linear search to go find the appropriate
>>   * memory_block corresponding to a particular phys_index. If
>> @@ -482,12 +479,13 @@
>>  	struct sys_device *sysdev;
>>  	struct memory_block *mem;
>>  	char name[sizeof(MEMORY_CLASS_NAME) + 9 + 1];
>> +	int block_id = base_memory_block_id(__section_nr(section));
>>  
>>  	/*
>>  	 * This only works because we know that section == sysdev->id
>>  	 * slightly redundant with sysdev_register()
>>  	 */
>> -	sprintf(&name[0], "%s%d", MEMORY_CLASS_NAME, __section_nr(section));
>> +	sprintf(&name[0], "%s%d", MEMORY_CLASS_NAME, block_id);
> 
> Hmm. Then, the user has to calculate block-id in addtion to section-id.
> Can't we use memory block name as memory%d-%d(start-end) ?

I am not attached to a particular name for the directories.  I think
keeping the memory%d, where %d is the starting id makes the code that splits
the directory cleaner.

In a later patch I add a new file for each directory that has the ending
id in it so users can easily determine the start and end id's of the
memory block.

> 
> 
> 
>>  
>>  	kobj = kset_find_obj(&memory_sysdev_class.kset, name);
>>  	if (!kobj)
>> @@ -498,19 +496,97 @@
>>  
>>  	return mem;
>>  }
>> +static int add_mem_block_section(struct memory_block *mem,
>> +				 int section_nr, unsigned long state)
>> +{
>> +	struct memory_block_section *mbs;
>> +
>> +	mbs = kzalloc(sizeof(*mbs), GFP_KERNEL);
>> +	if (!mbs)
>> +		return -ENOMEM;
>> +
>> +	mbs->phys_index = section_nr;
>> +	mbs->state = state;
>> +
>> +	list_add(&mbs->next, &mem->sections);
>> +	return 0;
>> +}
>> +
>> +static int add_memory_block(int nid, struct mem_section *section,
>> +			unsigned long state, enum mem_add_context context)
>> +{
>> +	struct memory_block *mem;
>> +	int ret = 0;
>> +
>> +	mem = find_memory_block(section);
> 
> I guess you need to add changes to find_memory_block. section-ID != block-ID.

That is above, see the line

>> +	int block_id = base_memory_block_id(__section_nr(section));

> 
> 
>> +	if (!mem) {
>> +		unsigned long start_pfn;
>> +
>> +		mem = kzalloc(sizeof(*mem), GFP_KERNEL);
>> +		if (!mem)
>> +			return -ENOMEM;
>> +
>> +		mem->state = state;
>> +		mutex_init(&mem->state_mutex);
>> +		start_pfn = section_nr_to_pfn(__section_nr(section));
>> +		mem->phys_device = arch_get_memory_phys_device(start_pfn);
>> +		INIT_LIST_HEAD(&mem->sections);
> 
> I'm not sure this phys_device is properly set in any arch...but this changes in
> granule will not affect ?

I don't think so, hopefully someone will speak up if this causes an issue.

> 
>> +
>> +		ret = register_memory(mem, section);
>> +		if (!ret)
>> +			ret = mem_create_simple_file(mem, phys_index);
>> +		if (!ret)
>> +			ret = mem_create_simple_file(mem, state);
>> +		if (!ret)
>> +			ret = mem_create_simple_file(mem, phys_device);
>> +		if (!ret)
>> +			ret = mem_create_simple_file(mem, removable);
>> +		if (!ret) {
>> +			if (context == HOTPLUG)
>> +				ret = register_mem_sect_under_node(mem, nid);
>> +		}
>> +	} else {
>> +		kobject_put(&mem->sysdev.kobj);
>> +	}
>> +
>> +	if (!ret)
>> +		ret = add_mem_block_section(mem, __section_nr(section), state);
>> +
>> +	return ret;
>> +}
> 
> 
> 
> 
> 
>>  
>>  int remove_memory_block(unsigned long node_id, struct mem_section *section,
>>  		int phys_device)
>>  {
>>  	struct memory_block *mem;
>> +	struct memory_block_section *mbs;
>> +	struct list_head *pos, *tmp;
>> +	int section_nr = __section_nr(section);
>>  
>>  	mem = find_memory_block(section);
> 
> ditto.
> 
>> -	unregister_mem_sect_under_nodes(mem);
>> -	mem_remove_simple_file(mem, phys_index);
>> -	mem_remove_simple_file(mem, state);
>> -	mem_remove_simple_file(mem, phys_device);
>> -	mem_remove_simple_file(mem, removable);
>> -	unregister_memory(mem, section);
>> +	mutex_lock(&mem->state_mutex);
>> +
>> +	/* remove the specified section */
>> +	list_for_each_safe(pos, tmp, &mem->sections) {
>> +		mbs = list_entry(pos, struct memory_block_section, next);
>> +
> list_for_each_entry_safe ?

yep. :)

> 
>> +		if (mbs->phys_index == section_nr) {
>> +			list_del(&mbs->next);
>> +			kfree(mbs);
>> +		}
>> +	}
>> +
>> +	mutex_unlock(&mem->state_mutex);
>> +
>> +	if (list_empty(&mem->sections)) {
>> +		unregister_mem_sect_under_nodes(mem);
>> +		mem_remove_simple_file(mem, phys_index);
>> +		mem_remove_simple_file(mem, state);
>> +		mem_remove_simple_file(mem, phys_device);
>> +		mem_remove_simple_file(mem, removable);
>> +		unregister_memory(mem);
>> +		kfree(mem);
>> +	}
>>  
>>  	return 0;
>>  }
>> @@ -532,6 +608,24 @@
>>  	return remove_memory_block(0, section, 0);
>>  }
>>  
>> +u32 __weak memory_block_size(void)
>> +{
>> +	return MIN_MEMORY_BLOCK_SIZE;
>> +}
>> +
>> +static u32 get_memory_block_size(void)
>> +{
>> +	u32 blk_sz;
>> +
>> +	blk_sz = memory_block_size();
>> +
>> +	/* Validate blk_sz is a power of 2 and not less than section size */
>> +	if ((blk_sz & (blk_sz - 1)) || (blk_sz < MIN_MEMORY_BLOCK_SIZE))
>> +		blk_sz = MIN_MEMORY_BLOCK_SIZE;
>> +
>> +	return blk_sz;
>> +}
>> +
>>  /*
>>   * Initialize the sysfs support for memory devices...
>>   */
>> @@ -540,12 +634,16 @@
>>  	unsigned int i;
>>  	int ret;
>>  	int err;
>> +	int block_sz;
>>  
>>  	memory_sysdev_class.kset.uevent_ops = &memory_uevent_ops;
>>  	ret = sysdev_class_register(&memory_sysdev_class);
>>  	if (ret)
>>  		goto out;
>>  
>> +	block_sz = get_memory_block_size();
>> +	sections_per_block = block_sz / MIN_MEMORY_BLOCK_SIZE;
>> +
>>  	/*
>>  	 * Create entries for memory sections that were found
>>  	 * during boot and have been initialized
>> Index: linux-2.6/include/linux/memory.h
>> ===================================================================
>> --- linux-2.6.orig/include/linux/memory.h	2010-07-08 11:27:21.000000000 -0500
>> +++ linux-2.6/include/linux/memory.h	2010-07-09 14:22:44.000000000 -0500
>> @@ -19,9 +19,15 @@
>>  #include <linux/node.h>
>>  #include <linux/compiler.h>
>>  #include <linux/mutex.h>
>> +#include <linux/list.h>
>>  
>> -struct memory_block {
>> +struct memory_block_section {
>> +	unsigned long state;
>>  	unsigned long phys_index;
>> +	struct list_head next;
>> +};
>> +
>> +struct memory_block {
>>  	unsigned long state;
>>  	/*
>>  	 * This serializes all state change requests.  It isn't
>> @@ -34,6 +40,7 @@
>>  	void *hw;			/* optional pointer to fw/hw data */
>>  	int (*phys_callback)(struct memory_block *);
>>  	struct sys_device sysdev;
>> +	struct list_head sections;
>>  };
>>  
>>  int arch_get_memory_phys_device(unsigned long start_pfn);
>> @@ -113,7 +120,7 @@
>>  extern int remove_memory_block(unsigned long, struct mem_section *, int);
>>  extern int memory_notify(unsigned long val, void *v);
>>  extern int memory_isolate_notify(unsigned long val, void *v);
>> -extern struct memory_block *find_memory_block(unsigned long);
>> +extern struct memory_block *find_memory_block(struct mem_section *);
>>  extern int memory_is_hidden(struct mem_section *);
>>  #define CONFIG_MEM_BLOCK_SIZE	(PAGES_PER_SECTION<<PAGE_SHIFT)
>>  enum mem_add_context { BOOT, HOTPLUG };
>> --
>> To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
>> the body of a message to majordomo@vger.kernel.org
>> More majordomo info at  http://vger.kernel.org/majordomo-info.html
>> Please read the FAQ at  http://www.tux.org/lkml/
>>
> 

^ permalink raw reply

* Re: section .data..init_task
From: Sam Ravnborg @ 2010-07-13 15:33 UTC (permalink / raw)
  To: Sean MacLennan; +Cc: Denys Vlasenko, linuxppc-dev, Tim Abbott
In-Reply-To: <20100713112610.30b66318@lappy.seanm.ca>

On Tue, Jul 13, 2010 at 11:26:10AM -0400, Sean MacLennan wrote:
> On Tue, 13 Jul 2010 10:54:19 +0200
> Sam Ravnborg <sam@ravnborg.org> wrote:
> 
> > It looks like a missing AT() in the output section.
> > The following patch should also fix it.
> > 
> > Please test and let us know.
> > 
> > Thanks,
> > 	Sam
> 
> Applied the patch and it solves the problem. Thanks.

Thanks for the quick feedback!

	Sam

^ permalink raw reply

* Re: section .data..init_task
From: Sean MacLennan @ 2010-07-13 15:26 UTC (permalink / raw)
  To: Sam Ravnborg; +Cc: Denys Vlasenko, linuxppc-dev, Tim Abbott
In-Reply-To: <20100713085419.GA5826@merkur.ravnborg.org>

On Tue, 13 Jul 2010 10:54:19 +0200
Sam Ravnborg <sam@ravnborg.org> wrote:

> It looks like a missing AT() in the output section.
> The following patch should also fix it.
> 
> Please test and let us know.
> 
> Thanks,
> 	Sam

Applied the patch and it solves the problem. Thanks.

Cheers,
   Sean

^ permalink raw reply

* Re: 2.6.35-rc4 ppc crash when loading radeon modeset=1
From: Michel Dänzer @ 2010-07-13 14:59 UTC (permalink / raw)
  To: jjDaNiMoTh; +Cc: linuxppc-dev, xorg-driver-ati
In-Reply-To: <AANLkTilk4-gIiJPQb5aqZ5v4YvgzsddexnsmrFBER5HO@mail.gmail.com>

On Die, 2010-07-13 at 16:51 +0200, jjDaNiMoTh wrote:=20
> 2010/7/13 Michel D=C3=A4nzer <michel@daenzer.net>:
> > Does KMS work better with radeon.agpmode=3D1 (or 2 or -1)?
>=20
> with radeon.agpmode=3D-1, we could start X server (no black screen),
> with both radeon.modeset=3D{0,1}.

Note that radeon.agpmode is only effective with radeon.modeset=3D1,
otherwise you need to use Option "AGPMode" in xorg.conf (and vice
versa).


> In all cases, Xorg works fine, except when we try to load an OpenGL
> application (like glxgears), Xorg freeze, we could move only the
> mouse, we couldn't switch to a backup console.

Could be a GPU lockup again, possibly due to still using AGP 4x with
modeset=3D0.


> Same situations with glxgears in both modeset=3D0 and =3D1. In the log
> (Xorg.0.log) we have found:=20
>=20
> [.. other xorg log, no EE only WW]
> [    65.238] (II) RADEON(0): Panel infos found from DDC detailed: 1280x85=
4
> [    65.238] (II) RADEON(0): EDID vendor "APP", prod id 39968
> [    65.249] (II) RADEON(0): Output: DVI-0, Detected Monitor Type: 0
> [    65.249] Unhandled monitor type 0
> [    65.249] (II) RADEON(0): Output: S-video, Detected Monitor Type: 0
> [   137.813] [mi] EQ overflowing. The server is probably stuck in an
> infinite loop.
> [   137.813]
> Backtrace:
> [   137.814] 0: /usr/bin/X (xorg_backtrace+0x58) [0x100582cc]
> [   137.814] 1: /usr/bin/X (mieqEnqueue+0x1c8) [0x1004e5d8]
> [   137.814] 2: /usr/bin/X (xf86PostButtonEventP+0xf4) [0x10061be8]
> [   137.814] 3: /usr/bin/X (xf86PostButtonEvent+0xb4) [0x10061d2c]
> [   137.814] 4: /usr/lib/xorg/modules/input/evdev_drv.so
> (0xf380000+0x3d88) [0xf383d88]
> [   137.814] 5: /usr/bin/X (0x10000000+0x68784) [0x10068784]
> [   137.814] 6: /usr/bin/X (0x10000000+0x11a7e4) [0x1011a7e4]
> [   137.814] 7: (vdso) (__kernel_sigtramp32+0x0) [0x100344]
> [   137.814] 8: /usr/lib/xorg/modules/dri/r300_dri.so
> (0xf3f5000+0x48534) [0xf43d534]
> [   137.814] 9: /usr/lib/libdrm.so.2 (drmIoctl+0x40) [0xf8b8f64]
> [   137.814] 10: /usr/lib/libdrm.so.2 (drmCommandWrite+0x24) [0xf8bbe60]
> [   137.814] 11: /usr/lib/xorg/modules/dri/r300_dri.so
> (0xf3f5000+0x46944) [0xf43b944]
> [   137.814] 12: /usr/lib/xorg/modules/dri/r300_dri.so
> (0xf3f5000+0x64d8c) [0xf459d8c]
> [   137.814] 13: /usr/lib/xorg/modules/extensions/libglx.so
> (0xf930000+0x40f78) [0xf970f78]
> [   137.814] 14: /usr/lib/xorg/modules/extensions/libglx.so
> (0xf930000+0x44be4) [0xf974be4]
> [   137.814] 15: /usr/bin/X (0x10000000+0x34a24) [0x10034a24]
> [   137.815] 16: /usr/bin/X (0x10000000+0x18bc4) [0x10018bc4]
> [   137.815] 17: /lib/libc.so.6 (0xfb39000+0x1f544) [0xfb58544]
> [   137.815] 18: /lib/libc.so.6 (0xfb39000+0x1f6d0) [0xfb586d0]

What does the log file contain with modeset=3D1?


> Do we need to compile mesa, ati-dri, x.org and xf86-video-ati from git?

Shouldn't be necessary.


--=20
Earthling Michel D=C3=A4nzer           |                http://www.vmware.c=
om
Libre software enthusiast         |          Debian, X and DRI developer

^ permalink raw reply

* Re: 2.6.35-rc4 ppc crash when loading radeon modeset=1
From: jjDaNiMoTh @ 2010-07-13 14:51 UTC (permalink / raw)
  To: Michel Dänzer; +Cc: linuxppc-dev, xorg-driver-ati
In-Reply-To: <1279030767.515.15.camel@thor.local>

2010/7/13 Michel D=C3=A4nzer <michel@daenzer.net>:
[cut]
> Which framebuffer device (if any) is it trying to initialize otherwise?
> OFfb? The first paragraph above implies none, but then I'm not sure why
> the video=3D parameters would make any difference.
We tried and with 2.6.35-rc4 we could boot without video=3D. First good new=
s :)

[cut]
> Does KMS work better with radeon.agpmode=3D1 (or 2 or -1)?

with radeon.agpmode=3D-1, we could start X server (no black screen),
with both radeon.modeset=3D{0,1}. In all cases, Xorg works fine, except
when we try to load an OpenGL application (like glxgears), Xorg
freeze, we could move only the mouse, we couldn't switch to a backup
console. Same situations with glxgears in both modeset=3D0 and =3D1. In
the log (Xorg.0.log) we have found:

[.. other xorg log, no EE only WW]
[    65.238] (II) RADEON(0): Panel infos found from DDC detailed: 1280x854
[    65.238] (II) RADEON(0): EDID vendor "APP", prod id 39968
[    65.249] (II) RADEON(0): Output: DVI-0, Detected Monitor Type: 0
[    65.249] Unhandled monitor type 0
[    65.249] (II) RADEON(0): Output: S-video, Detected Monitor Type: 0
[   137.813] [mi] EQ overflowing. The server is probably stuck in an
infinite loop.
[   137.813]
Backtrace:
[   137.814] 0: /usr/bin/X (xorg_backtrace+0x58) [0x100582cc]
[   137.814] 1: /usr/bin/X (mieqEnqueue+0x1c8) [0x1004e5d8]
[   137.814] 2: /usr/bin/X (xf86PostButtonEventP+0xf4) [0x10061be8]
[   137.814] 3: /usr/bin/X (xf86PostButtonEvent+0xb4) [0x10061d2c]
[   137.814] 4: /usr/lib/xorg/modules/input/evdev_drv.so
(0xf380000+0x3d88) [0xf383d88]
[   137.814] 5: /usr/bin/X (0x10000000+0x68784) [0x10068784]
[   137.814] 6: /usr/bin/X (0x10000000+0x11a7e4) [0x1011a7e4]
[   137.814] 7: (vdso) (__kernel_sigtramp32+0x0) [0x100344]
[   137.814] 8: /usr/lib/xorg/modules/dri/r300_dri.so
(0xf3f5000+0x48534) [0xf43d534]
[   137.814] 9: /usr/lib/libdrm.so.2 (drmIoctl+0x40) [0xf8b8f64]
[   137.814] 10: /usr/lib/libdrm.so.2 (drmCommandWrite+0x24) [0xf8bbe60]
[   137.814] 11: /usr/lib/xorg/modules/dri/r300_dri.so
(0xf3f5000+0x46944) [0xf43b944]
[   137.814] 12: /usr/lib/xorg/modules/dri/r300_dri.so
(0xf3f5000+0x64d8c) [0xf459d8c]
[   137.814] 13: /usr/lib/xorg/modules/extensions/libglx.so
(0xf930000+0x40f78) [0xf970f78]
[   137.814] 14: /usr/lib/xorg/modules/extensions/libglx.so
(0xf930000+0x44be4) [0xf974be4]
[   137.814] 15: /usr/bin/X (0x10000000+0x34a24) [0x10034a24]
[   137.815] 16: /usr/bin/X (0x10000000+0x18bc4) [0x10018bc4]
[   137.815] 17: /lib/libc.so.6 (0xfb39000+0x1f544) [0xfb58544]
[   137.815] 18: /lib/libc.so.6 (0xfb39000+0x1f6d0) [0xfb586d0]

Do we need to compile mesa, ati-dri, x.org and xf86-video-ati from git?

Many thanks

^ permalink raw reply

* [PATCH] powerpc: Add vmcoreinfo symbols to allow makdumpfile to filter core files properly
From: Neil Horman @ 2010-07-13 13:46 UTC (permalink / raw)
  To: linux-kernel, kexec, vgoyal, hbabu, benh, paulus, linuxppc-dev; +Cc: nhorman

Hey all-
	About 2 years ago now, I sent this patch upstream to allow makedumpfile
to properly filter cores on ppc64:
http://www.mail-archive.com/kexec@lists.infradead.org/msg02426.html
It got acks from the kexec folks so I pulled it into RHEL, but I never checked
back here to make sure it ever made it in, which apparently it didn't.  It still
needs to be included, so I'm reposting it here, making sure to copy all the ppc
folks this time.  I've retested it on the latest linus kernel and it works fine,
allowing makedumpfile to find all the symbols it needs to properly strip a
vmcore on ppc64.

Neil

Signed-off-by: Neil Horman <nhorman@tuxdriver.com>


 machine_kexec.c |   12 ++++++++++++
 1 file changed, 12 insertions(+)


diff --git a/arch/powerpc/kernel/machine_kexec.c b/arch/powerpc/kernel/machine_kexec.c
index bb3d893..0df7031 100644
--- a/arch/powerpc/kernel/machine_kexec.c
+++ b/arch/powerpc/kernel/machine_kexec.c
@@ -45,6 +45,18 @@ void machine_kexec_cleanup(struct kimage *image)
 		ppc_md.machine_kexec_cleanup(image);
 }
 
+void arch_crash_save_vmcoreinfo(void)
+{
+
+#ifdef CONFIG_NEED_MULTIPLE_NODES
+	VMCOREINFO_SYMBOL(node_data);
+	VMCOREINFO_LENGTH(node_data, MAX_NUMNODES);
+#endif
+#ifndef CONFIG_NEED_MULTIPLE_NODES
+	VMCOREINFO_SYMBOL(contig_page_data);
+#endif
+}
+
 /*
  * Do not allocate memory (or fail in any way) in machine_kexec().
  * We are past the point of no return, committed to rebooting now.

^ permalink raw reply related

* Re: 2.6.35-rc4 ppc crash when loading radeon modeset=1
From: Michel Dänzer @ 2010-07-13 14:19 UTC (permalink / raw)
  To: jjDaNiMoTh; +Cc: linuxppc-dev, xorg-driver-ati
In-Reply-To: <AANLkTikdlwl6VxdbkHP0TveQ3SPO0w4RD-onoG3QPyCO@mail.gmail.com>

On Die, 2010-07-13 at 16:03 +0200, jjDaNiMoTh wrote:=20
>=20
> When trying new 2.6.35-rc4, our kernel team (ArchLinuxPPC) has tried
> to setup KMS acceleration for radeon based machine.
> We have removed radeonfb, and all others framebuffer driver, and added
> fbcon and KMS enabled by default for radeon driver.
>=20
> With a clean start, the screen freeze, when the control pass from
> yaboot to kernel.
>=20
> If we start with video=3Dfbcon (or video=3Dradeondrmfb), we could reach
> the loading modules point, [...]

Which framebuffer device (if any) is it trying to initialize otherwise?
OFfb? The first paragraph above implies none, but then I'm not sure why
the video=3D parameters would make any difference.


> Jul 13 15:31:39 jim kernel: [drm] Initialized drm 1.1.0 20060810
> Jul 13 15:31:39 jim kernel: [drm] radeon kernel modesetting enabled.
> Jul 13 15:31:39 jim kernel: [drm] initializing kernel modesetting
> (RV350 0x1002:0x4E50).
> Jul 13 15:31:39 jim kernel: [drm] register mmio base: 0xB0000000
> Jul 13 15:31:39 jim kernel: [drm] register mmio size: 65536
> Jul 13 15:31:39 jim kernel: [drm] Using generic clock info
> Jul 13 15:31:39 jim kernel: agpgart-uninorth 0000:00:0b.0: putting AGP
> V2 device into 4x mode
> Jul 13 15:31:39 jim kernel: radeon 0000:00:10.0: putting AGP V2 device
> into 4x mode
> Jul 13 15:31:39 jim kernel: radeon 0000:00:10.0: GTT: 256M 0x00000000
> - 0x0FFFFFFF
> Jul 13 15:31:39 jim kernel: [drm] Generation 2 PCI interface, using
> max accessible memory
> Jul 13 15:31:39 jim kernel: radeon 0000:00:10.0: VRAM: 64M 0xB8000000
> - 0xBBFFFFFF (64M used)
> Jul 13 15:31:39 jim kernel: [drm] radeon: irq initialized.
> Jul 13 15:31:39 jim kernel: [drm] Detected VRAM RAM=3D64M, BAR=3D128M
> Jul 13 15:31:39 jim kernel: [drm] RAM width 128bits DDR
> Jul 13 15:31:39 jim kernel: [TTM] Zone  kernel: Available graphics
> memory: 384990 kiB.
> Jul 13 15:31:39 jim kernel: [TTM] Zone highmem: Available graphics
> memory: 516062 kiB.
> Jul 13 15:31:39 jim kernel: [TTM] Initializing pool allocator.
> Jul 13 15:31:39 jim kernel: [drm] radeon: 64M of VRAM memory ready
> Jul 13 15:31:39 jim kernel: [drm] radeon: 256M of GTT memory ready.
> Jul 13 15:31:39 jim kernel: [drm] radeon: 1 quad pipes, 1 Z pipes initial=
ized.
> Jul 13 15:31:39 jim kernel: [drm] Loading R300 Microcode
> Jul 13 15:31:39 jim kernel: [drm] radeon: ring at 0x0000000000000000
> Jul 13 15:31:39 jim kernel: [drm] ring test succeeded in 3 usecs
> Jul 13 15:31:39 jim kernel: [drm] radeon: ib pool ready.

So far, so good.

> Jul 13 15:31:40 jim kernel: GPU lockup (waiting for 0x00000001 last
> fence id 0x00000000)

The GPU locks up, and things go downhill from there...

Does KMS work better with radeon.agpmode=3D1 (or 2 or -1)?


--=20
Earthling Michel D=C3=A4nzer           |                http://www.vmware.c=
om
Libre software enthusiast         |          Debian, X and DRI developer

^ permalink raw reply

* 2.6.35-rc4 ppc crash when loading radeon modeset=1
From: jjDaNiMoTh @ 2010-07-13 14:03 UTC (permalink / raw)
  To: xorg-driver-ati; +Cc: linuxppc-dev

Hello to all.

Sorry if these aren't right place, please point me to the right
direction if you can :)

When trying new 2.6.35-rc4, our kernel team (ArchLinuxPPC) has tried
to setup KMS acceleration for radeon based machine.
We have removed radeonfb, and all others framebuffer driver, and added
fbcon and KMS enabled by default for radeon driver.

With a clean start, the screen freeze, when the control pass from
yaboot to kernel.

If we start with video=fbcon (or video=radeondrmfb), we could reach
the loading modules point, but after the loading of radeon, the screen
goes black, without any log information.

Loading kernel with video=fbcon radeon.modeset=0 allow us to reach the
end of init stage, and we could load X.org. In this case, acceleration
is disabled.

If we log out and do the following command:
modprobe -r radeon drm
modprobe drm debug=1
modprobe radeon modeset=1

The screen goes black, but at next boot we have found the logs. Any hint?

System information:

Xorg server: 1.8.1
xf86-video-ati 6.13.0
ati-dri 7.8.1
mesa 7.8.1
linux 2.6.35-rc4-00131-ge467e10

There are logs (most relevant part):
[...]
Jul 13 15:29:50 jim kernel: Caused by (from SRR1=149030): Transfer
error ack signal
Jul 13 15:29:50 jim kernel: Machine check in kernel mode.
Jul 13 15:29:50 jim kernel: Caused by (from SRR1=149030): Transfer
error ack signal
Jul 13 15:29:50 jim kernel: Machine check in kernel mode.
Jul 13 15:29:50 jim kernel: Caused by (from SRR1=149030): Transfer
error ack signal
Jul 13 15:29:50 jim kernel: Machine check in kernel mode.
Jul 13 15:29:50 jim kernel: Caused by (from SRR1=149030): Transfer
error ack signal
[....]
Jul 13 15:31:28 jim kernel: [drm] Module unloaded
Jul 13 15:31:39 jim kernel: [drm] Initialized drm 1.1.0 20060810
Jul 13 15:31:39 jim kernel: [drm] radeon kernel modesetting enabled.
Jul 13 15:31:39 jim kernel: [drm] initializing kernel modesetting
(RV350 0x1002:0x4E50).
Jul 13 15:31:39 jim kernel: [drm] register mmio base: 0xB0000000
Jul 13 15:31:39 jim kernel: [drm] register mmio size: 65536
Jul 13 15:31:39 jim kernel: [drm] Using generic clock info
Jul 13 15:31:39 jim kernel: agpgart-uninorth 0000:00:0b.0: putting AGP
V2 device into 4x mode
Jul 13 15:31:39 jim kernel: radeon 0000:00:10.0: putting AGP V2 device
into 4x mode
Jul 13 15:31:39 jim kernel: radeon 0000:00:10.0: GTT: 256M 0x00000000
- 0x0FFFFFFF
Jul 13 15:31:39 jim kernel: [drm] Generation 2 PCI interface, using
max accessible memory
Jul 13 15:31:39 jim kernel: radeon 0000:00:10.0: VRAM: 64M 0xB8000000
- 0xBBFFFFFF (64M used)
Jul 13 15:31:39 jim kernel: [drm] radeon: irq initialized.
Jul 13 15:31:39 jim kernel: [drm] Detected VRAM RAM=64M, BAR=128M
Jul 13 15:31:39 jim kernel: [drm] RAM width 128bits DDR
Jul 13 15:31:39 jim kernel: [TTM] Zone  kernel: Available graphics
memory: 384990 kiB.
Jul 13 15:31:39 jim kernel: [TTM] Zone highmem: Available graphics
memory: 516062 kiB.
Jul 13 15:31:39 jim kernel: [TTM] Initializing pool allocator.
Jul 13 15:31:39 jim kernel: [drm] radeon: 64M of VRAM memory ready
Jul 13 15:31:39 jim kernel: [drm] radeon: 256M of GTT memory ready.
Jul 13 15:31:39 jim kernel: [drm] radeon: 1 quad pipes, 1 Z pipes initialized.
Jul 13 15:31:39 jim kernel: [drm] Loading R300 Microcode
Jul 13 15:31:39 jim kernel: [drm] radeon: ring at 0x0000000000000000
Jul 13 15:31:39 jim kernel: [drm] ring test succeeded in 3 usecs
Jul 13 15:31:39 jim kernel: [drm] radeon: ib pool ready.
Jul 13 15:31:40 jim kernel: GPU lockup (waiting for 0x00000001 last
fence id 0x00000000)
Jul 13 15:31:40 jim kernel: NIP: f260fda4 LR: f260fda4 CTR: 00000001
Jul 13 15:31:40 jim kernel: REGS: ef3a3b90 TRAP: 0700   Not tainted
(2.6.35-rc4-NAT-00131-ge467e10)
Jul 13 15:31:40 jim kernel: MSR: 00029032 <EE,ME,CE,IR,DR>  CR:
22822484  XER: 20000000
Jul 13 15:31:40 jim kernel: TASK = eedb9ac0[2066] 'modprobe' THREAD: ef3a2000
Jul 13 15:31:40 jim kernel: GPR00: f260fda4 ef3a3c40 eedb9ac0 00000040
416d5d8c ffffffff c04db984 416d5d09
Jul 13 15:31:40 jim kernel: GPR08: 416d5d8c 00000001 00000000 0000000a
22822482 100238a8 00000000 00000000
Jul 13 15:31:40 jim kernel: GPR16: 00000000 0000007d c04a0000 f26a1d54
00000001 00000000 00000000 ef3a2000
Jul 13 15:31:40 jim kernel: GPR24: c005f328 ef3a3c54 00000000 eed386cc
ef3a3c48 00000000 eed38000 ef085d40
Jul 13 15:31:40 jim kernel: NIP [f260fda4]
radeon_fence_wait+0x28c/0x2f4 [radeon]
Jul 13 15:31:40 jim kernel: LR [f260fda4] radeon_fence_wait+0x28c/0x2f4 [radeon]
Jul 13 15:31:40 jim kernel: Call Trace:
Jul 13 15:31:40 jim kernel: [ef3a3c40] [f260fda4]
radeon_fence_wait+0x28c/0x2f4 [radeon] (unreliable)
Jul 13 15:31:40 jim kernel: [ef3a3cb0] [f2638234]
r100_ib_test+0x158/0x280 [radeon]
Jul 13 15:31:40 jim kernel: [ef3a3ce0] [f26383a4]
r100_ib_init+0x28/0xc8 [radeon]
Jul 13 15:31:40 jim kernel: [ef3a3cf0] [f263f65c]
r300_startup+0xd4/0x1e4 [radeon]
Jul 13 15:31:40 jim kernel: [ef3a3d00] [f263fb3c] r300_init+0x150/0x334 [radeon]
Jul 13 15:31:40 jim kernel: [ef3a3d10] [f25fc514]
radeon_device_init+0x2b0/0x418 [radeon]
Jul 13 15:31:40 jim kernel: [ef3a3d30] [f25fdc0c]
radeon_driver_load_kms+0xa4/0x1f4 [radeon]
Jul 13 15:31:40 jim kernel: [ef3a3d60] [f2444c4c] drm_get_dev+0x284/0x43c [drm]
Jul 13 15:31:40 jim kernel: [ef3a3d90] [f268d75c]
radeon_pci_probe+0x18/0x28 [radeon]
Jul 13 15:31:40 jim kernel: [ef3a3da0] [c01f6f20] pci_device_probe+0x80/0xa4
Jul 13 15:31:40 jim kernel: [ef3a3dc0] [c025cf2c] driver_probe_device+0xc0/0x208
Jul 13 15:31:40 jim kernel: [ef3a3de0] [c025d130] __driver_attach+0xbc/0xc0
Jul 13 15:31:40 jim kernel: [ef3a3e00] [c025bd28] bus_for_each_dev+0x64/0xa0
Jul 13 15:31:40 jim kernel: [ef3a3e30] [c025cb58] driver_attach+0x24/0x34
Jul 13 15:31:40 jim kernel: [ef3a3e40] [c025c618] bus_add_driver+0xd8/0x308
Jul 13 15:31:40 jim kernel: [ef3a3e70] [c025d418] driver_register+0x88/0x154
Jul 13 15:31:40 jim kernel: [ef3a3e90] [c01f7200]
__pci_register_driver+0x4c/0xdc
Jul 13 15:31:40 jim kernel: [ef3a3eb0] [f243ee50] drm_init+0x120/0x134 [drm]
Jul 13 15:31:40 jim kernel: [ef3a3ed0] [f26c00e4]
radeon_init+0xe4/0x128 [radeon]
Jul 13 15:31:40 jim kernel: [ef3a3ef0] [c0003ff4] do_one_initcall+0x3c/0x1d8
Jul 13 15:31:40 jim kernel: [ef3a3f20] [c007b434] sys_init_module+0xdc/0x1e0
Jul 13 15:31:40 jim kernel: [ef3a3f40] [c0017f84] ret_from_syscall+0x0/0x40
Jul 13 15:31:40 jim kernel: --- Exception: c01 at 0xff62b58
Jul 13 15:31:40 jim kernel: LR = 0x10002c7c
Jul 13 15:31:40 jim kernel: Instruction dump:
Jul 13 15:31:40 jim kernel: 4bffffc4 813e0a18 7fc3f378 80090014
7c0903a6 4e800421 2f830000 419eff78
Jul 13 15:31:40 jim kernel: 809f0010 7e639b78 7ec5b378 4807dc59
<0fe00000> 9a9e16a8 7fc3f378 4bfecd41
Jul 13 15:31:40 jim kernel: radeon 0000:00:10.0: GPU reset succeed
Jul 13 15:31:40 jim kernel: [drm] radeon: 1 quad pipes, 1 Z pipes initialized.
Jul 13 15:31:40 jim kernel: [drm] radeon: ring at 0x0000000000000000
Jul 13 15:31:41 jim kernel: Oops: Kernel access of bad area, sig: 11 [#1]
Jul 13 15:31:41 jim kernel: PREEMPT PowerMac
Jul 13 15:31:41 jim kernel: Modules linked in: radeon(+) ttm
drm_kms_helper drm i2c_algo_bit hid_apple appletouch usbhid arc4 ecb
b43 mac80211 cfg80211 ohci_hcd snd_seq_oss snd_seq_midi_event snd_seq
snd_seq_device therm_adt746x snd_pcm_oss snd_aoa_codec_tas
snd_mixer_oss snd_aoa_fabric_layout snd_aoa snd_aoa_i2sbus snd_pcm
snd_timer snd_page_alloc snd ehci_hcd ohci1394 ssb ams input_polldev
yenta_socket soundcore pcmcia usbcore ide_cd_mod pcmcia_rsrc ieee1394
cpufreq_userspace snd_aoa_soundbus i2c_powermac pcmcia_core evdev
cdrom uninorth_agp sungem sungem_phy [last unloaded: i2c_algo_bit]
Jul 13 15:31:41 jim kernel: NIP: f2482248 LR: f25fcb88 CTR: 00000000
Jul 13 15:31:41 jim kernel: REGS: ef3a3b50 TRAP: 0300   Tainted: G
   W    (2.6.35-rc4-NAT-00131-ge467e10)
Jul 13 15:31:41 jim kernel: MSR: 00009032 <EE,ME,IR,DR>  CR: 22822484
XER: 20000000
Jul 13 15:31:41 jim kernel: DAR: 00000000, DSISR: 40000000
Jul 13 15:31:41 jim kernel: TASK = eedb9ac0[2066] 'modprobe' THREAD: ef3a2000
Jul 13 15:31:41 jim kernel: GPR00: f25fcb88 ef3a3c00 eedb9ac0 ef2c8c00
416d67bb ffffffff c04db97e 416d66f1
Jul 13 15:31:41 jim kernel: GPR08: 416d67bb 00000030 f28e002c eed39670
22822482 100238a8 00000000 00000000
Jul 13 15:31:41 jim kernel: GPR16: 00000000 0000007d c04a0000 f26a1d54
00000001 00000000 00000000 ef3a2000
Jul 13 15:31:41 jim kernel: GPR24: c005f328 ef3a3c54 f2484054 f2483878
ef2c8c00 ef2c8ea4 ef2c8e98 fffffffc
Jul 13 15:31:41 jim kernel: NIP [f2482248]
drm_helper_resume_force_mode+0x38/0x16c [drm_kms_helper]
Jul 13 15:31:41 jim kernel: LR [f25fcb88] radeon_gpu_reset+0x98/0x104 [radeon]
Jul 13 15:31:41 jim kernel: Call Trace:
Jul 13 15:31:41 jim kernel: [ef3a3c00] [ffffffea] 0xffffffea (unreliable)
Jul 13 15:31:41 jim kernel: [ef3a3c30] [f25fcb88]
radeon_gpu_reset+0x98/0x104 [radeon]
Jul 13 15:31:41 jim kernel: [ef3a3c40] [f260fdb4]
radeon_fence_wait+0x29c/0x2f4 [radeon]
Jul 13 15:31:41 jim kernel: [ef3a3cb0] [f2638234]
r100_ib_test+0x158/0x280 [radeon]
Jul 13 15:31:41 jim kernel: [ef3a3ce0] [f26383a4]
r100_ib_init+0x28/0xc8 [radeon]
Jul 13 15:31:41 jim kernel: [ef3a3cf0] [f263f65c]
r300_startup+0xd4/0x1e4 [radeon]
Jul 13 15:31:41 jim kernel: [ef3a3d00] [f263fb3c] r300_init+0x150/0x334 [radeon]
Jul 13 15:31:41 jim kernel: [ef3a3d10] [f25fc514]
radeon_device_init+0x2b0/0x418 [radeon]
Jul 13 15:31:41 jim kernel: [ef3a3d30] [f25fdc0c]
radeon_driver_load_kms+0xa4/0x1f4 [radeon]
Jul 13 15:31:41 jim kernel: [ef3a3d60] [f2444c4c] drm_get_dev+0x284/0x43c [drm]
Jul 13 15:31:41 jim kernel: [ef3a3d90] [f268d75c]
radeon_pci_probe+0x18/0x28 [radeon]
Jul 13 15:31:41 jim kernel: [ef3a3da0] [c01f6f20] pci_device_probe+0x80/0xa4
Jul 13 15:31:41 jim kernel: [ef3a3dc0] [c025cf2c] driver_probe_device+0xc0/0x208
Jul 13 15:31:41 jim kernel: [ef3a3de0] [c025d130] __driver_attach+0xbc/0xc0
Jul 13 15:31:41 jim kernel: [ef3a3e00] [c025bd28] bus_for_each_dev+0x64/0xa0
Jul 13 15:31:41 jim kernel: [ef3a3e30] [c025cb58] driver_attach+0x24/0x34
Jul 13 15:31:41 jim kernel: [ef3a3e40] [c025c618] bus_add_driver+0xd8/0x308
Jul 13 15:31:41 jim kernel: [ef3a3e70] [c025d418] driver_register+0x88/0x154
Jul 13 15:31:41 jim kernel: [ef3a3e90] [c01f7200]
__pci_register_driver+0x4c/0xdc
Jul 13 15:31:41 jim kernel: [ef3a3eb0] [f243ee50] drm_init+0x120/0x134 [drm]
Jul 13 15:31:41 jim kernel: [ef3a3ed0] [f26c00e4]
radeon_init+0xe4/0x128 [radeon]
Jul 13 15:31:41 jim kernel: [ef3a3ef0] [c0003ff4] do_one_initcall+0x3c/0x1d8
Jul 13 15:31:41 jim kernel: [ef3a3f20] [c007b434] sys_init_module+0xdc/0x1e0
Jul 13 15:31:41 jim kernel: [ef3a3f40] [c0017f84] ret_from_syscall+0x0/0x40
Jul 13 15:31:41 jim kernel: --- Exception: c01 at 0xff62b58
Jul 13 15:31:41 jim kernel: LR = 0x10002c7c
Jul 13 15:31:41 jim kernel: Instruction dump:
Jul 13 15:31:41 jim kernel: bf010010 7c7d1b78 3f60f248 90010034
3f40f248 3b7b382c 7c7c1b78 3bc30298
Jul 13 15:31:41 jim kernel: 3b5a4054 3b7b004c 87fd02a4 3bfffffc
<813f0004> 2f890000 419e0008 7c004a2c
Jul 13 15:31:41 jim kernel: ---[ end trace 9928f19443a4dfb8 ]---
Jul 13 15:31:48 jim kernel: Oops: Kernel access of bad area, sig: 11 [#2]
Jul 13 15:31:48 jim kernel: PREEMPT PowerMac
Jul 13 15:31:48 jim kernel: Modules linked in: radeon(+) ttm
drm_kms_helper drm i2c_algo_bit hid_apple appletouch usbhid arc4 ecb
b43 mac80211 cfg80211 ohci_hcd snd_seq_oss snd_seq_midi_event snd_seq
snd_seq_device therm_adt746x snd_pcm_oss snd_aoa_codec_tas
snd_mixer_oss snd_aoa_fabric_layout snd_aoa snd_aoa_i2sbus snd_pcm
snd_timer snd_page_alloc snd ehci_hcd ohci1394 ssb ams input_polldev
yenta_socket soundcore pcmcia usbcore ide_cd_mod pcmcia_rsrc ieee1394
cpufreq_userspace snd_aoa_soundbus i2c_powermac pcmcia_core evdev
cdrom uninorth_agp sungem sungem_phy [last unloaded: i2c_algo_bit]
Jul 13 15:31:48 jim kernel: NIP: c0382df4 LR: c0382de0 CTR: 00000000
Jul 13 15:31:48 jim kernel: REGS: eed7dd80 TRAP: 0300   Tainted: G
 D W    (2.6.35-rc4-NAT-00131-ge467e10)
Jul 13 15:31:48 jim kernel: MSR: 00001032 <ME,IR,DR>  CR: 24000424
XER: 20000000
Jul 13 15:31:48 jim kernel: DAR: 00000000, DSISR: 42000000
Jul 13 15:31:48 jim kernel: TASK = efb46820[2095] 'X' THREAD: eed7c000
Jul 13 15:31:48 jim kernel: GPR00: ffffffff eed7de30 efb46820 ef2c8e3c
eed7de38 eed7c000 eed7de44 0000082f
Jul 13 15:31:48 jim kernel: GPR08: 0000e200 00000000 0000007f c0382fc8
24000422 101d3ea8 101cbed4 00000000
Jul 13 15:31:48 jim kernel: GPR16: 10478758 00000000 00000001 101d3c0c
00000000 101cb8c8 ef2c8c00 00009032
Jul 13 15:31:48 jim kernel: GPR24: ef2c8dc0 ef2c8e40 eed7de38 efb46820
c04d0000 00009032 eed7c000 ef2c8e3c
Jul 13 15:31:48 jim kernel: NIP [c0382df4] __mutex_lock_slowpath+0xa0/0x274
Jul 13 15:31:48 jim kernel: LR [c0382de0] __mutex_lock_slowpath+0x8c/0x274
Jul 13 15:31:48 jim kernel: Call Trace:
Jul 13 15:31:48 jim kernel: [eed7de30] [c0382dd0]
__mutex_lock_slowpath+0x7c/0x274 (unreliable)
Jul 13 15:31:48 jim kernel: [eed7de70] [c0382fe0] mutex_lock+0x18/0x34
Jul 13 15:31:48 jim kernel: [eed7de80] [f244d010] drm_fb_release+0x28/0xac [drm]
Jul 13 15:31:48 jim kernel: [eed7dea0] [f243fab8] drm_release+0x674/0x770 [drm]
Jul 13 15:31:48 jim kernel: [eed7dee0] [c00fc410] fput+0x118/0x264
Jul 13 15:31:48 jim kernel: [eed7df00] [c00f87d0] filp_close+0x6c/0x98
Jul 13 15:31:48 jim kernel: [eed7df20] [c00f88ac] sys_close+0xb0/0x12c
Jul 13 15:31:48 jim kernel: [eed7df40] [c0017f84] ret_from_syscall+0x0/0x40
Jul 13 15:31:48 jim kernel: --- Exception: c01 at 0xfec3730
Jul 13 15:31:48 jim kernel: LR = 0xf8b9340
Jul 13 15:31:48 jim kernel: Instruction dump:
Jul 13 15:31:48 jim kernel: 7f44d378 3b3f0004 4bcee849 80bb0004
7fe3fb78 7f44d378 4bcee9e5 813f0008
Jul 13 15:31:48 jim kernel: 3800ffff 93210008 935f0008 9121000c
<93490000> 93610010 7d20f828 7c00f92d
Jul 13 15:31:48 jim kernel: ---[ end trace 9928f19443a4dfb9 ]---
Jul 13 15:31:48 jim kernel: note: X[2095] exited with preempt_count 2
Jul 13 15:31:48 jim kernel: Modules linked in: radeon(+) ttm
drm_kms_helper drm i2c_algo_bit hid_apple appletouch usbhid arc4 ecb
b43 mac80211 cfg80211 ohci_hcd snd_seq_oss snd_seq_midi_event snd_seq
snd_seq_device therm_adt746x snd_pcm_oss snd_aoa_codec_tas
snd_mixer_oss snd_aoa_fabric_layout snd_aoa snd_aoa_i2sbus snd_pcm
snd_timer snd_page_alloc snd ehci_hcd ohci1394 ssb ams input_polldev
yenta_socket soundcore pcmcia usbcore ide_cd_mod pcmcia_rsrc ieee1394
cpufreq_userspace snd_aoa_soundbus i2c_powermac pcmcia_core evdev
cdrom uninorth_agp sungem sungem_phy [last unloaded: i2c_algo_bit]
Jul 13 15:31:48 jim kernel: Call Trace:
Jul 13 15:31:48 jim kernel: [eed7da50] [c000a6e8]
show_stack+0x50/0x158 (unreliable)
Jul 13 15:31:48 jim kernel: [eed7da90] [c00369bc] __schedule_bug+0x64/0x68
Jul 13 15:31:48 jim kernel: [eed7daa0] [c03816a0] schedule+0x360/0x428
Jul 13 15:31:48 jim kernel: [eed7dae0] [c0382068] schedule_timeout+0x1c0/0x304
Jul 13 15:31:48 jim kernel: [eed7db30] [c0381b7c] wait_for_common+0xd4/0x1c8
Jul 13 15:31:48 jim kernel: [eed7db70] [c005a0b8] flush_cpu_workqueue+0xdc/0x110
Jul 13 15:31:48 jim kernel: [eed7dbb0] [c022fc7c] tty_ldisc_release+0x2c/0x84
Jul 13 15:31:48 jim kernel: [eed7dbd0] [c0228374] tty_release+0x428/0x5b0
Jul 13 15:31:48 jim kernel: [eed7dc70] [c00fc410] fput+0x118/0x264
Jul 13 15:31:48 jim kernel: [eed7dc90] [c00f87d0] filp_close+0x6c/0x98
Jul 13 15:31:48 jim kernel: [eed7dcb0] [c0042958] put_files_struct+0x12c/0x158
Jul 13 15:31:48 jim kernel: [eed7dce0] [c0042b7c] do_exit+0x118/0x708
Jul 13 15:31:48 jim kernel: [eed7dd30] [c0015560] die+0x100/0x2c0
Jul 13 15:31:48 jim kernel: [eed7dd60] [c001f350] bad_page_fault+0x90/0xc8
Jul 13 15:31:48 jim kernel: [eed7dd70] [c001843c] handle_page_fault+0x7c/0x80
Jul 13 15:31:48 jim kernel: --- Exception: 300 at
__mutex_lock_slowpath+0xa0/0x274
Jul 13 15:31:48 jim kernel: LR = __mutex_lock_slowpath+0x8c/0x274
Jul 13 15:31:48 jim kernel: [eed7de30] [c0382dd0]
__mutex_lock_slowpath+0x7c/0x274 (unreliable)
Jul 13 15:31:48 jim kernel: [eed7de70] [c0382fe0] mutex_lock+0x18/0x34
Jul 13 15:31:48 jim kernel: [eed7de80] [f244d010] drm_fb_release+0x28/0xac [drm]
Jul 13 15:31:48 jim kernel: [eed7dea0] [f243fab8] drm_release+0x674/0x770 [drm]
Jul 13 15:31:48 jim kernel: [eed7dee0] [c00fc410] fput+0x118/0x264
Jul 13 15:31:48 jim kernel: [eed7df00] [c00f87d0] filp_close+0x6c/0x98
Jul 13 15:31:48 jim kernel: [eed7df20] [c00f88ac] sys_close+0xb0/0x12c
Jul 13 15:31:48 jim kernel: [eed7df40] [c0017f84] ret_from_syscall+0x0/0x40
Jul 13 15:31:48 jim kernel: --- Exception: c01 at 0xfec3730
Jul 13 15:31:48 jim kernel: LR = 0xf8b9340
Jul 13 15:33:09 jim kernel: Using PowerMac machine description
[Next boot..]


Many thanks

^ permalink raw reply

* Re: [PATCH 1/7] Split the memory_block structure
From: Brian King @ 2010-07-13 14:00 UTC (permalink / raw)
  To: Nathan Fontenot; +Cc: linuxppc-dev, linux-kernel
In-Reply-To: <4C3B37CE.50802@austin.ibm.com>

On 07/12/2010 10:42 AM, Nathan Fontenot wrote:
> @@ -123,13 +130,20 @@
>  static ssize_t show_mem_removable(struct sys_device *dev,
>  			struct sysdev_attribute *attr, char *buf)
>  {
> -	unsigned long start_pfn;
> -	int ret;
> -	struct memory_block *mem =
> -		container_of(dev, struct memory_block, sysdev);
> +	struct list_head *pos, *tmp;
> +	struct memory_block *mem;
> +	int ret = 1;
> +
> +	mem = container_of(dev, struct memory_block, sysdev);
> +	list_for_each_safe(pos, tmp, &mem->sections) {
> +		struct memory_block_section *mbs;
> +		unsigned long start_pfn;
> +
> +		mbs = list_entry(pos, struct memory_block_section, next);
> +		start_pfn = section_nr_to_pfn(mbs->phys_index);
> +		ret &= is_mem_section_removable(start_pfn, PAGES_PER_SECTION);
> +	}

I don't see you deleting anyting from the list in this loop. Why do you need
to use list_for_each_safe? That won't protect you if someone else is messing
with the list.

> 
> -	start_pfn = section_nr_to_pfn(mem->phys_index);
> -	ret = is_mem_section_removable(start_pfn, PAGES_PER_SECTION);
>  	return sprintf(buf, "%d\n", ret);
>  }
> 


> @@ -238,19 +252,40 @@
>  static int memory_block_change_state(struct memory_block *mem,
>  		unsigned long to_state, unsigned long from_state_req)
>  {
> +	struct memory_block_section *mbs;
> +	struct list_head *pos;
>  	int ret = 0;
> +
>  	mutex_lock(&mem->state_mutex);
> 
> -	if (mem->state != from_state_req) {
> -		ret = -EINVAL;
> -		goto out;
> +	list_for_each(pos, &mem->sections) {
> +		mbs = list_entry(pos, struct memory_block_section, next);
> +
> +		if (mbs->state != from_state_req)
> +			continue;
> +
> +		ret = memory_block_action(mbs, to_state);
> +		if (ret)
> +			break;
> +	}

Would it be better here to loop through all the sections and ensure they
are in the proper state first before starting to change the state of any
of them? Then you could easily return -EINVAL if one or more is in
the incorrect state and wouldn't need to the code below.

> +	if (ret) {
> +		list_for_each(pos, &mem->sections) {
> +			mbs = list_entry(pos, struct memory_block_section,
> +					 next);
> +
> +			if (mbs->state == from_state_req)
> +				continue;
> +
> +			if (memory_block_action(mbs, to_state))
> +				printk(KERN_ERR "Could not re-enable memory "
> +				       "section %lx\n", mbs->phys_index);
> +		}
>  	}
> 
> -	ret = memory_block_action(mem, to_state);
>  	if (!ret)
>  		mem->state = to_state;
> 
> -out:
>  	mutex_unlock(&mem->state_mutex);
>  	return ret;
>  }


> @@ -498,19 +496,97 @@
> 
>  	return mem;
>  }
> +static int add_mem_block_section(struct memory_block *mem,
> +				 int section_nr, unsigned long state)
> +{
> +	struct memory_block_section *mbs;
> +
> +	mbs = kzalloc(sizeof(*mbs), GFP_KERNEL);
> +	if (!mbs)
> +		return -ENOMEM;
> +
> +	mbs->phys_index = section_nr;
> +	mbs->state = state;
> +
> +	list_add(&mbs->next, &mem->sections);

I don't think there is sufficient protection for this list. Don't we
need to be holding a lock of some sort when adding/deleting/iterating
through this list? 

> +	return 0;
> +}

-- 
Brian King
Linux on Power Virtualization
IBM Linux Technology Center

^ permalink raw reply

* High process latencies due to MPC5200 FEC hard- soft-irq processing
From: Wolfgang Grandegger @ 2010-07-13 13:29 UTC (permalink / raw)
  To: Netdev; +Cc: linuxppc-dev@ozlabs.org, LKML

Hello,

we realized, that multiple ping floods (ping -f) can cause very large
high-priority process latencies (up to a many seconds) on a MPC5200
PowerPC system with FEC NAPI support. The latencies are measured with

  # cyclictest -p 80 -n

The problem is that processing of the ICMP pakets in the Hard-Irq and
Soft-IRQ context can last for a long time without returning to the
scheduler. Reducing MAX_SOFTIRQ_RESTART from 10 to 2 helps - the latency
goes down to 35 ms with 2 "ping -f" - but it's not a configurable
parameter, even if it somehow depends on the CPU power. And using the
-rt patches seems overkill to me. Any other ideas or comments on how to
get rid of such high process latencies?

Wolfgang.

^ permalink raw reply

* Re: [PATCH] powerpc: reduce the size of the defconfigs
From: Martyn Welch @ 2010-07-13 10:35 UTC (permalink / raw)
  To: Uwe Kleine-König
  Cc: Olof Johansson, Stephen Rothwell, Linus, ppc-dev, LKML
In-Reply-To: <20100713073137.GC26442@pengutronix.de>

Uwe Kleine-König wrote:
> On Tue, Jul 13, 2010 at 05:09:45PM +1000, Stephen Rothwell wrote:
>   
>> This uses Uwe's script (modified by Olof Johansson to speed it up
>> somewhat) to reduce the size of all the powerpc defconfigs.  The resulting
>>     
> IMHO we should add the script to the source, too.  And if it's only for
> me to see Olof's optimisation. :-)
>
>   

As the person who introduced the gef_* PPC defconfigs to the kernel (I
understood this to be best practice), I'm happy to go along with
whatever - as long as I can build a bootable kernel from the mainline
kernel. I think I understand Linus' wish not to see all the defconfig
churn (I get fed up diffing defconfigs, just to read through loads of
additions and removals of undef'ed entires from just the few configs I'm
interested in).

I assume I'm not alone among those attempting to add board support to
the mainline kernel in not being able to keep up with the LKML mailing
list and therefore have missed most of this discussion (think I've
managed to read some of it now). I would very much appreciate some
documentation / guidance on how to use this script - preferably provided
as a comment in the head of the script or/and some pointers in
"Documentation/".

Martyn

>> files have been verified to produce the same .config files by generating
>> them before and after this patch and comparing the results.
>>
>> Signed-off-by: Stephen Rothwell <sfr@canb.auug.org.au>
>> ---
>>  100 files changed, 51 insertions(+), 133815 deletions(-)
>>     
> Nice.
>
> Best regards
> Uwe
>
>   


-- 
Martyn Welch (Principal Software Engineer)   |   Registered in England and
GE Intelligent Platforms                     |   Wales (3828642) at 100
T +44(0)127322748                            |   Barbirolli Square, Manchester,
E martyn.welch@ge.com                        |   M2 3AB  VAT:GB 927559189

^ permalink raw reply

* [
From: Sam Ravnborg @ 2010-07-13  9:50 UTC (permalink / raw)
  To: Sean MacLennan, Benjamin Herrenschmidt
  Cc: Denys Vlasenko, linuxppc-dev, Michal Marek, Tim Abbott
In-Reply-To: <20100712203435.08a3e90f@lappy.seanm.ca>

>From 851e645a7eee68380caaf026eb6d3be118876370 Mon Sep 17 00:00:00 2001
From: Sam Ravnborg <sam@ravnborg.org>
Date: Tue, 13 Jul 2010 11:39:42 +0200
Subject: [PATCH] vmlinux.lds: fix .data..init_task output section (fix popwerpc boot)

The .data..init_task output section was missing
a load offset causing a popwerpc target to fail to boot.

Sean MacLennan tracked it down to the definition of
INIT_TASK_DATA_SECTION().

There are only two users of INIT_TASK_DATA_SECTION()
in the kernel today: cris and popwerpc.
cris do not support relocatable kernels and is thus not
impacted by this change.

Fix INIT_TASK_DATA_SECTION() to specify load offset like
all other output sections.

Reported-by: Sean MacLennan <smaclennan@pikatech.com>
Signed-off-by: Sam Ravnborg <sam@ravnborg.org>
---

On the assumption that Sean reports that it fixes
the warnings/boot issue here is a real patch.

Ben - will you take it via the popwerpc tree
or shall I ask Michal to take it via kbuild?

	Sam

 include/asm-generic/vmlinux.lds.h |    2 +-
 1 files changed, 1 insertions(+), 1 deletions(-)

diff --git a/include/asm-generic/vmlinux.lds.h b/include/asm-generic/vmlinux.lds.h
index 48c5299..cdfff74 100644
--- a/include/asm-generic/vmlinux.lds.h
+++ b/include/asm-generic/vmlinux.lds.h
@@ -435,7 +435,7 @@
  */
 #define INIT_TASK_DATA_SECTION(align)					\
 	. = ALIGN(align);						\
-	.data..init_task : {						\
+	.data..init_task :  AT(ADDR(.data..init_task) - LOAD_OFFSET) {	\
 		INIT_TASK_DATA(align)					\
 	}
 
-- 
1.6.0.6

^ permalink raw reply related

* Re: section .data..init_task
From: Sam Ravnborg @ 2010-07-13  8:54 UTC (permalink / raw)
  To: Sean MacLennan; +Cc: Denys Vlasenko, linuxppc-dev, Tim Abbott
In-Reply-To: <20100712203435.08a3e90f@lappy.seanm.ca>

On Mon, Jul 12, 2010 at 08:34:35PM -0400, Sean MacLennan wrote:
> On Mon, 28 Jun 2010 00:59:00 -0400
> Sean MacLennan <smaclennan@pikatech.com> wrote:
> 
> > Anybody else seeing these messages?
> > 
> > ppc_4xxFP-ld: .tmp_vmlinux1: section .data..init_task lma 0xc0374000
> > overlaps previous sections ppc_4xxFP-ld: .tmp_vmlinux2:
> > section .data..init_task lma 0xc03a2000 overlaps previous sections
> > ppc_4xxFP-ld: vmlinux: section .data..init_task lma 0xc03a2000
> > overlaps previous sections
> > 
> > Or does anybody know what they mean? They started showing up in
> > 2.6.35.
> > 
> > Very easy to reproduce, so don't hesitate to ask for more info.
> 
> I had a bit of time, so I tracked this down. This patch seems to be
> the culprit: http://lkml.org/lkml/2010/2/19/366
> 
> Specifically, this code:
> 
>  	/* The initial task and kernel stack */
> -	.data.init_task : AT(ADDR(.data.init_task) - LOAD_OFFSET) {
> -		INIT_TASK_DATA(THREAD_SIZE)
> -	}
> +	INIT_TASK_DATA_SECTION(THREAD_SIZE)
> 
> If I change it back to:
> 
> 	/* The initial task and kernel stack */
> 	.data..init_task : AT(ADDR(.data..init_task) - LOAD_OFFSET) {
> 		INIT_TASK_DATA(THREAD_SIZE)
> 	}
> 
> not only do the warnings go away, but the kernel now boots again!

It looks like a missing AT() in the output section.
The following patch should also fix it.

Please test and let us know.

Thanks,
	Sam


diff --git a/include/asm-generic/vmlinux.lds.h b/include/asm-generic/vmlinux.lds.h
index 48c5299..3c4bf03 100644
--- a/include/asm-generic/vmlinux.lds.h
+++ b/include/asm-generic/vmlinux.lds.h
@@ -435,7 +435,7 @@
  */
 #define INIT_TASK_DATA_SECTION(align)					\
 	. = ALIGN(align);						\
-	.data..init_task : {						\
+	.data..init_task : AT(ADDR(.data..init_task) - LOAD_OFFSET) {	\
 		INIT_TASK_DATA(align)					\
 	}
 

^ permalink raw reply related

* optimized script [Was: ARM defconfig files]
From: Uwe Kleine-König @ 2010-07-13  8:07 UTC (permalink / raw)
  To: Linus Torvalds
  Cc: Stephen Rothwell, Daniel Walker, Russell King - ARM Linux,
	Nicolas Pitre, Kevin Hilman, linux-arm-msm,
	Linux Kernel Mailing List, Eric Miao, Olof Johansson, linux-omap,
	linuxppc-dev, linux-arm-kernel
In-Reply-To: <20100713070741.GB26442@pengutronix.de>

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

Hello,

On Tue, Jul 13, 2010 at 09:07:41AM +0200, Uwe Kleine-König wrote:
> Hi
> 
> On Mon, Jul 12, 2010 at 01:50:47PM -0600, Grant Likely wrote:
> > On Mon, Jul 12, 2010 at 1:34 PM, Linus Torvalds
> > <torvalds@linux-foundation.org> wrote:
> > > On Mon, Jul 12, 2010 at 12:17 PM, Nicolas Pitre <nico@fluxnic.net> wrote:
> > >> I think Uwe could provide his script and add it to the kernel tree.
> > >> Then all architectures could benefit from it.  Having the defconfig
> > >> files contain only those options which are different from the defaults
> > >> is certainly more readable, even on x86.
> > >
> > > Quite possible. But maintainers would need to be on the lookout of
> > > people actually using the script, and refusing to apply patches that
> > > re-introduce the whole big thing.
> > 
> > I can (partially) speak for powerpc.  If ARM uses this approach, then
> > I think we can do the same.  After the defconfigs are trimmed, I
> > certainly won't pick up any more full defconfigs.
> I just restarted my script on the powerpc defconfigs basing on rc5, I
> assume they complete in a few days time.
So Stephen was faster than me.  I don't know yet how he optimised my
script, meanwhile I put some efforts into it, too by just checking lines
that match "^(# )?CONFIG_".

Find it attached.

I will start to reduce the remaining configs (i.e. all but arm and
powerpc).

Best regards
Uwe

-- 
Pengutronix e.K.                           | Uwe Kleine-König            |
Industrial Linux Solutions                 | http://www.pengutronix.de/  |

[-- Attachment #2: reduce_defconfig --]
[-- Type: text/plain, Size: 1899 bytes --]

#! /usr/bin/env python
# vim: set fileencoding=utf-8 :
# Copyright (C) 2010 by Uwe Kleine-König <u.kleine-koenig@pengutronix.de>

import getopt
import re
import os
import subprocess
import sys

# This prevents including a timestamp in the .config which makes comparing a
# bit easier.
os.environ['KCONFIG_NOTIMESTAMP'] = 'Yes, please'

re_interesting = re.compile(r'^(# )?CONFIG_')

opts, args = getopt.getopt(sys.argv[1:], '', ['arch=', 'src='])

src = ''
arch = 'arm'

for o, a in opts:
    if o == '--arch':
        arch = a
    elif o == '--src':
        src = a

configdir = os.path.join(src, 'arch', arch, 'configs')

def all_defconfigs():
    lc = len(configdir)
    for root, dirs, files in os.walk(configdir):
        root = root[lc + 1:]
        for f in filter(lambda s: s.endswith('_defconfig'), files):
            yield os.path.join(root, f)

if not args:
    args = all_defconfigs()

for target in args:
    defconfig_src = os.path.join(configdir, target)

    subprocess.check_call(['make', '-s', 'ARCH=%s' % arch, target])
    origconfig = list(open('.config'))
    config = list(origconfig)
    config_size = os.stat('.config').st_size

    i = 0

    while i < len(config):
        mo = re_interesting.match(config[i])
        if mo:
            defconfig = open(defconfig_src, 'w')
            defconfig.writelines(config[:i])
            defconfig.writelines(config[i + 1:])
            defconfig.close()
            subprocess.check_call(['make', '-s', 'ARCH=%s' % arch, target])
            if os.stat('.config').st_size == config_size and list(open('.config')) == origconfig:
                print '-%s' % config[i][:-1]
                del config[i]
            else:
                print ' %s' % config[i][:-1]
                i += 1
        else:
            del config[i]

    defconfig = open(defconfig_src, 'w')
    defconfig.writelines(config)
    defconfig.close()

^ permalink raw reply

* Re: [PATCH] powerpc: reduce the size of the defconfigs
From: Uwe Kleine-König @ 2010-07-13  7:31 UTC (permalink / raw)
  To: Stephen Rothwell; +Cc: Olof Johansson, ppc-dev, Linus, LKML
In-Reply-To: <20100713170945.26d95641.sfr@canb.auug.org.au>

On Tue, Jul 13, 2010 at 05:09:45PM +1000, Stephen Rothwell wrote:
> This uses Uwe's script (modified by Olof Johansson to speed it up
> somewhat) to reduce the size of all the powerpc defconfigs.  The resulting
IMHO we should add the script to the source, too.  And if it's only for
me to see Olof's optimisation. :-)

> files have been verified to produce the same .config files by generating
> them before and after this patch and comparing the results.
> 
> Signed-off-by: Stephen Rothwell <sfr@canb.auug.org.au>
> ---
>  100 files changed, 51 insertions(+), 133815 deletions(-)
Nice.

Best regards
Uwe

-- 
Pengutronix e.K.                           | Uwe Kleine-König            |
Industrial Linux Solutions                 | http://www.pengutronix.de/  |

^ permalink raw reply

* [PATCH] powerpc: reduce the size of the defconfigs
From: Stephen Rothwell @ 2010-07-13  7:09 UTC (permalink / raw)
  To: Linus; +Cc: Olof Johansson, ppc-dev, LKML, "Uwe Kleine-König"

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

This uses Uwe's script (modified by Olof Johansson to speed it up
somewhat) to reduce the size of all the powerpc defconfigs.  The resulting
files have been verified to produce the same .config files by generating
them before and after this patch and comparing the results.

Signed-off-by: Stephen Rothwell <sfr@canb.auug.org.au>
---
 arch/powerpc/configs/40x/acadia_defconfig         | 1002 ----------
 arch/powerpc/configs/40x/ep405_defconfig          | 1211 ------------
 arch/powerpc/configs/40x/hcu4_defconfig           | 1064 ----------
 arch/powerpc/configs/40x/kilauea_defconfig        | 1197 -----------
 arch/powerpc/configs/40x/makalu_defconfig         | 1005 ----------
 arch/powerpc/configs/40x/virtex_defconfig         | 1105 -----------
 arch/powerpc/configs/40x/walnut_defconfig         | 1089 ----------
 arch/powerpc/configs/44x/arches_defconfig         | 1059 ----------
 arch/powerpc/configs/44x/bamboo_defconfig         | 1020 ----------
 arch/powerpc/configs/44x/canyonlands_defconfig    | 1263 ------------
 arch/powerpc/configs/44x/ebony_defconfig          | 1103 -----------
 arch/powerpc/configs/44x/eiger_defconfig          | 1177 -----------
 arch/powerpc/configs/44x/icon_defconfig           | 1335 -------------
 arch/powerpc/configs/44x/iss476-smp_defconfig     |  938 ---------
 arch/powerpc/configs/44x/katmai_defconfig         | 1088 ----------
 arch/powerpc/configs/44x/rainier_defconfig        | 1090 ----------
 arch/powerpc/configs/44x/redwood_defconfig        | 1168 -----------
 arch/powerpc/configs/44x/sam440ep_defconfig       | 1319 -------------
 arch/powerpc/configs/44x/sequoia_defconfig        | 1111 -----------
 arch/powerpc/configs/44x/taishan_defconfig        | 1097 -----------
 arch/powerpc/configs/44x/virtex5_defconfig        | 1111 -----------
 arch/powerpc/configs/44x/warp_defconfig           | 1389 -------------
 arch/powerpc/configs/52xx/cm5200_defconfig        | 1232 ------------
 arch/powerpc/configs/52xx/lite5200b_defconfig     | 1257 ------------
 arch/powerpc/configs/52xx/motionpro_defconfig     | 1265 ------------
 arch/powerpc/configs/52xx/pcm030_defconfig        | 1219 ------------
 arch/powerpc/configs/52xx/tqm5200_defconfig       | 1367 -------------
 arch/powerpc/configs/83xx/asp8347_defconfig       | 1432 --------------
 arch/powerpc/configs/83xx/kmeter1_defconfig       |  927 ---------
 arch/powerpc/configs/83xx/mpc8313_rdb_defconfig   | 1729 ----------------
 arch/powerpc/configs/83xx/mpc8315_rdb_defconfig   | 1798 -----------------
 arch/powerpc/configs/83xx/mpc832x_mds_defconfig   | 1328 -------------
 arch/powerpc/configs/83xx/mpc832x_rdb_defconfig   | 1475 --------------
 arch/powerpc/configs/83xx/mpc834x_itx_defconfig   | 1567 ---------------
 arch/powerpc/configs/83xx/mpc834x_itxgp_defconfig | 1453 --------------
 arch/powerpc/configs/83xx/mpc834x_mds_defconfig   | 1262 ------------
 arch/powerpc/configs/83xx/mpc836x_mds_defconfig   | 1403 -------------
 arch/powerpc/configs/83xx/mpc836x_rdk_defconfig   | 1304 ------------
 arch/powerpc/configs/83xx/mpc837x_mds_defconfig   | 1333 -------------
 arch/powerpc/configs/83xx/mpc837x_rdb_defconfig   | 1471 --------------
 arch/powerpc/configs/83xx/sbc834x_defconfig       | 1398 -------------
 arch/powerpc/configs/85xx/ksi8560_defconfig       | 1119 -----------
 arch/powerpc/configs/85xx/mpc8540_ads_defconfig   |  992 ----------
 arch/powerpc/configs/85xx/mpc8560_ads_defconfig   | 1139 -----------
 arch/powerpc/configs/85xx/mpc85xx_cds_defconfig   | 1155 -----------
 arch/powerpc/configs/85xx/sbc8548_defconfig       | 1001 ----------
 arch/powerpc/configs/85xx/sbc8560_defconfig       | 1029 ----------
 arch/powerpc/configs/85xx/socrates_defconfig      | 1645 ----------------
 arch/powerpc/configs/85xx/stx_gp3_defconfig       | 1528 --------------
 arch/powerpc/configs/85xx/tqm8540_defconfig       | 1318 -------------
 arch/powerpc/configs/85xx/tqm8541_defconfig       | 1364 -------------
 arch/powerpc/configs/85xx/tqm8548_defconfig       | 1355 +-------------
 arch/powerpc/configs/85xx/tqm8555_defconfig       | 1364 -------------
 arch/powerpc/configs/85xx/tqm8560_defconfig       | 1364 -------------
 arch/powerpc/configs/85xx/xes_mpc85xx_defconfig   | 1786 -----------------
 arch/powerpc/configs/86xx/gef_ppc9a_defconfig     | 1736 ----------------
 arch/powerpc/configs/86xx/gef_sbc310_defconfig    | 1625 ---------------
 arch/powerpc/configs/86xx/gef_sbc610_defconfig    | 1819 -----------------
 arch/powerpc/configs/86xx/mpc8610_hpcd_defconfig  | 1634 ---------------
 arch/powerpc/configs/86xx/mpc8641_hpcn_defconfig  | 1640 ----------------
 arch/powerpc/configs/86xx/sbc8641d_defconfig      | 1432 --------------
 arch/powerpc/configs/adder875_defconfig           |  913 ---------
 arch/powerpc/configs/amigaone_defconfig           | 1490 --------------
 arch/powerpc/configs/c2k_defconfig                | 1608 ---------------
 arch/powerpc/configs/cell_defconfig               | 1314 +------------
 arch/powerpc/configs/celleb_defconfig             | 1195 +-----------
 arch/powerpc/configs/chrp32_defconfig             | 1469 --------------
 arch/powerpc/configs/ep8248e_defconfig            | 1118 -----------
 arch/powerpc/configs/ep88xc_defconfig             |  858 --------
 arch/powerpc/configs/g5_defconfig                 | 1549 ---------------
 arch/powerpc/configs/gamecube_defconfig           |  945 ---------
 arch/powerpc/configs/holly_defconfig              |  879 ---------
 arch/powerpc/configs/iseries_defconfig            | 1056 ----------
 arch/powerpc/configs/linkstation_defconfig        | 1782 -----------------
 arch/powerpc/configs/maple_defconfig              | 1371 -------------
 arch/powerpc/configs/mgcoge_defconfig             | 1158 -----------
 arch/powerpc/configs/mgsuvd_defconfig             |  937 ---------
 arch/powerpc/configs/mpc512x_defconfig            | 1559 ---------------
 arch/powerpc/configs/mpc5200_defconfig            | 1849 -----------------
 arch/powerpc/configs/mpc7448_hpc2_defconfig       | 1194 -----------
 arch/powerpc/configs/mpc8272_ads_defconfig        | 1185 -----------
 arch/powerpc/configs/mpc83xx_defconfig            | 1662 ----------------
 arch/powerpc/configs/mpc85xx_defconfig            | 1704 ----------------
 arch/powerpc/configs/mpc85xx_smp_defconfig        | 1708 ----------------
 arch/powerpc/configs/mpc866_ads_defconfig         |  950 ---------
 arch/powerpc/configs/mpc86xx_defconfig            | 1680 ----------------
 arch/powerpc/configs/mpc885_ads_defconfig         |  863 --------
 arch/powerpc/configs/pasemi_defconfig             | 1931 +------------------
 arch/powerpc/configs/pmac32_defconfig             | 1884 +------------------
 arch/powerpc/configs/ppc40x_defconfig             | 1332 -------------
 arch/powerpc/configs/ppc44x_defconfig             | 1471 --------------
 arch/powerpc/configs/ppc64_defconfig              | 1724 +----------------
 arch/powerpc/configs/ppc64e_defconfig             | 1803 -----------------
 arch/powerpc/configs/ppc6xx_defconfig             | 2186 +--------------------
 arch/powerpc/configs/pq2fads_defconfig            | 1304 ------------
 arch/powerpc/configs/prpmc2800_defconfig          | 1674 ----------------
 arch/powerpc/configs/ps3_defconfig                | 1334 -------------
 arch/powerpc/configs/pseries_defconfig            | 1454 +--------------
 arch/powerpc/configs/storcenter_defconfig         | 1305 ------------
 arch/powerpc/configs/wii_defconfig                | 1262 ------------
 100 files changed, 51 insertions(+), 133815 deletions(-)

The patch is available at
http://ozlabs.org/~sfr/powerpc-reduce-the-size-of-the-defconfigs.patch.bz2 .

-- 
Cheers,
Stephen Rothwell                    sfr@canb.auug.org.au
http://www.canb.auug.org.au/~sfr/

[-- Attachment #2: Type: application/pgp-signature, Size: 490 bytes --]

^ permalink raw reply

* Re: [PATCH 4/7] Allow sysfs memory directories to be split
From: KAMEZAWA Hiroyuki @ 2010-07-13  6:28 UTC (permalink / raw)
  To: Nathan Fontenot; +Cc: linuxppc-dev, linux-kernel
In-Reply-To: <4C3B3895.3040209@austin.ibm.com>

On Mon, 12 Jul 2010 10:45:25 -0500
Nathan Fontenot <nfont@austin.ibm.com> wrote:

> This patch introduces the new 'split' file in each memory sysfs
> directory and the associated routines needed to handle splitting
> a directory.
> 
> Signed-off-by; Nathan Fontenot <nfont@austin.ibm.com>
> ---

pleae check diff option...


>  drivers/base/memory.c |   99 +++++++++++++++++++++++++++++++++++++++++++++++++-
>  1 file changed, 98 insertions(+), 1 deletion(-)
> 
> Index: linux-2.6/drivers/base/memory.c
> ===================================================================
> --- linux-2.6.orig/drivers/base/memory.c	2010-07-09 14:23:20.000000000 -0500
> +++ linux-2.6/drivers/base/memory.c	2010-07-09 14:38:09.000000000 -0500
> @@ -32,6 +32,9 @@
>  
>  static int sections_per_block;
>  
> +static int register_memory(struct memory_block *, struct mem_section *,
> +			   int, enum mem_add_context);
> +
>  static inline int base_memory_block_id(int section_nr)
>  {
>  	return (section_nr / sections_per_block) * sections_per_block;
> @@ -309,11 +312,100 @@
>  	return sprintf(buf, "%d\n", mem->phys_device);
>  }
>  
> +static void update_memory_block_phys_indexes(struct memory_block *mem)
> +{
> +	struct list_head *pos;
> +	struct memory_block_section *mbs;
> +	unsigned long min_index = 0xffffffff;
> +	unsigned long max_index = 0;
> +
> +	list_for_each(pos, &mem->sections) {
> +		mbs = list_entry(pos, struct memory_block_section, next);
> +
> +		if (mbs->phys_index < min_index)
> +			min_index = mbs->phys_index;
> +
> +		if (mbs->phys_index > max_index)
> +			max_index = mbs->phys_index;
> +	}
> +
> +	mem->start_phys_index = min_index;
> +	mem->end_phys_index = max_index;
> +}
> +
> +static ssize_t
> +store_mem_split_block(struct sys_device *dev, struct sysdev_attribute *attr,
> +		      const char *buf, size_t count)
> +{
> +	struct memory_block *mem, *new_mem_blk;
> +	struct memory_block_section *mbs;
> +	struct list_head *pos, *tmp;
> +	struct mem_section *section;
> +	int min_scn_nr = 0;
> +	int max_scn_nr = 0;
> +	int total_scns = 0;
> +	int new_blk_min, new_blk_total;
> +	int ret = -EINVAL;
> +
> +	mem = container_of(dev, struct memory_block, sysdev);
> +
> +	if (list_is_singular(&mem->sections))
> +		return -EINVAL;

What this means ?


> +
> +	mutex_lock(&mem->state_mutex);
> +
> +	list_for_each(pos, &mem->sections) {
> +		mbs = list_entry(pos, struct memory_block_section, next);
> +
> +		total_scns++;
> +
> +		if (min_scn_nr > mbs->phys_index)
> +			min_scn_nr = mbs->phys_index;
> +
> +		if (max_scn_nr < mbs->phys_index)
> +			max_scn_nr = mbs->phys_index;
> +	}
> +
> +	new_mem_blk = kzalloc(sizeof(*new_mem_blk), GFP_KERNEL);
> +	if (!new_mem_blk)
> +		return -ENOMEM;
> +
> +	mutex_init(&new_mem_blk->state_mutex);
> +	INIT_LIST_HEAD(&new_mem_blk->sections);
> +	new_mem_blk->state = mem->state;
> +
> +	mutex_lock(&new_mem_blk->state_mutex);
> +
> +	new_blk_total = total_scns / 2;
> +	new_blk_min = max_scn_nr - new_blk_total + 1;
> +
> +	section = __nr_to_section(new_blk_min);
> +	ret = register_memory(new_mem_blk, section, 0, HOTPLUG);
> +
'nid' is always 0 ?

And for what purpose this interface is ? Does this split memory block into 2 pieces
of the same size ?? sounds __very__ strange interface to me.

If this is necessary, I hope move the whole things to configfs rather than
something tricky.

Bye.
-Kame

^ permalink raw reply

* Re: [PATCH 3/7] Update the [register,unregister]_memory routines
From: KAMEZAWA Hiroyuki @ 2010-07-13  6:20 UTC (permalink / raw)
  To: Nathan Fontenot; +Cc: linuxppc-dev, linux-kernel
In-Reply-To: <4C3B384A.4000902@austin.ibm.com>

On Mon, 12 Jul 2010 10:44:10 -0500
Nathan Fontenot <nfont@austin.ibm.com> wrote:

> This patch moves the register/unregister_memory routines to
> avoid a forward declaration.  It also moves the sysfs file
> creation and deletion for each directory into the register/
> unregister routines to avoid duplicating it with these updates.
> 
> Signed-off-by: Nathan Fontenot <nfont@austin.ibm.com>
> ---
>  drivers/base/memory.c |   93 +++++++++++++++++++++++++-------------------------
>  1 file changed, 48 insertions(+), 45 deletions(-)
> 
> Index: linux-2.6/drivers/base/memory.c
> ===================================================================
> --- linux-2.6.orig/drivers/base/memory.c	2010-07-09 14:23:17.000000000 -0500
> +++ linux-2.6/drivers/base/memory.c	2010-07-09 14:23:20.000000000 -0500
> @@ -87,31 +87,6 @@
>  EXPORT_SYMBOL(unregister_memory_isolate_notifier);
>  
>  /*
> - * register_memory - Setup a sysfs device for a memory block
> - */
> -static
> -int register_memory(struct memory_block *memory, struct mem_section *section)
> -{
> -	int error;
> -
> -	memory->sysdev.cls = &memory_sysdev_class;
> -	memory->sysdev.id = __section_nr(section);
> -
> -	error = sysdev_register(&memory->sysdev);
> -	return error;
> -}
> -
> -static void
> -unregister_memory(struct memory_block *memory)
> -{
> -	BUG_ON(memory->sysdev.cls != &memory_sysdev_class);
> -
> -	/* drop the ref. we got in remove_memory_block() */
> -	kobject_put(&memory->sysdev.kobj);
> -	sysdev_unregister(&memory->sysdev);
> -}
> -
> -/*
>   * use this as the physical section index that this memsection
>   * uses.
>   */
> @@ -346,6 +321,53 @@
>  	sysdev_remove_file(&mem->sysdev, &attr_##attr_name)
>  
>  /*
> + * register_memory - Setup a sysfs device for a memory block
> + */
> +static
> +int register_memory(struct memory_block *memory, struct mem_section *section,
> +		    int nid, enum mem_add_context context)
> +{
> +	int ret;
> +
> +	memory->sysdev.cls = &memory_sysdev_class;
> +	memory->sysdev.id = __section_nr(section);
> +
Why not block-ID  but section-ID ?

-Kame

^ permalink raw reply

* Re: [PATCH 1/7] Split the memory_block structure
From: KAMEZAWA Hiroyuki @ 2010-07-13  6:18 UTC (permalink / raw)
  To: Nathan Fontenot; +Cc: linuxppc-dev, linux-kernel
In-Reply-To: <4C3B37CE.50802@austin.ibm.com>


plz cc linux-mm in the next time...
And please incudes updates for Documentation/memory-hotplug.txt.


On Mon, 12 Jul 2010 10:42:06 -0500
Nathan Fontenot <nfont@austin.ibm.com> wrote:

> This patch splits the memory_block struct into a memory_block
> struct to cover each sysfs directory and a new memory_block_section
> struct for each memory section covered by the sysfs directory.
> 
> This also updates the routine handling memory_block creation
> and manipulation to use these updated structures.
> 

Could you clarify the number of memory_block_section per memory_block ?


> Signed -off-by: Nathan Fontenot <nfont@austin.ibm.com>
> ---
>  drivers/base/memory.c  |  228 +++++++++++++++++++++++++++++++++++--------------
>  include/linux/memory.h |   11 +-
>  2 files changed, 172 insertions(+), 67 deletions(-)
> 
> Index: linux-2.6/drivers/base/memory.c
> ===================================================================
> --- linux-2.6.orig/drivers/base/memory.c	2010-07-08 11:27:21.000000000 -0500
> +++ linux-2.6/drivers/base/memory.c	2010-07-09 14:23:09.000000000 -0500
> @@ -28,6 +28,14 @@
>  #include <asm/uaccess.h>
>  
>  #define MEMORY_CLASS_NAME	"memory"
> +#define MIN_MEMORY_BLOCK_SIZE	(1 << SECTION_SIZE_BITS)
> +
> +static int sections_per_block;
> +
some default value, plz. Does this can be determined only by .config ?


> +static inline int base_memory_block_id(int section_nr)
> +{
> +	return (section_nr / sections_per_block) * sections_per_block;
> +}
>  
>  static struct sysdev_class memory_sysdev_class = {
>  	.name = MEMORY_CLASS_NAME,
> @@ -94,10 +102,9 @@
>  }
>  
>  static void
> -unregister_memory(struct memory_block *memory, struct mem_section *section)
> +unregister_memory(struct memory_block *memory)
>  {
>  	BUG_ON(memory->sysdev.cls != &memory_sysdev_class);
> -	BUG_ON(memory->sysdev.id != __section_nr(section));
>  
>  	/* drop the ref. we got in remove_memory_block() */
>  	kobject_put(&memory->sysdev.kobj);
> @@ -123,13 +130,20 @@
>  static ssize_t show_mem_removable(struct sys_device *dev,
>  			struct sysdev_attribute *attr, char *buf)
>  {
> -	unsigned long start_pfn;
> -	int ret;
> -	struct memory_block *mem =
> -		container_of(dev, struct memory_block, sysdev);
> +	struct list_head *pos, *tmp;
> +	struct memory_block *mem;
> +	int ret = 1;
> +
> +	mem = container_of(dev, struct memory_block, sysdev);
> +	list_for_each_safe(pos, tmp, &mem->sections) {
> +		struct memory_block_section *mbs;
> +		unsigned long start_pfn;
> +
> +		mbs = list_entry(pos, struct memory_block_section, next);

list_for_each_entry ?



> +		start_pfn = section_nr_to_pfn(mbs->phys_index);
> +		ret &= is_mem_section_removable(start_pfn, PAGES_PER_SECTION);
> +	}

Hmm, them, only when the whole memory block is removable, it's shown as
removable. Right ?
Does it meets ppc guy's requirements ?

>  
> -	start_pfn = section_nr_to_pfn(mem->phys_index);
> -	ret = is_mem_section_removable(start_pfn, PAGES_PER_SECTION);
>  	return sprintf(buf, "%d\n", ret);
>  }

Hmm...can't you print removable information as bitmap, here ?
overkill ?


>  
> @@ -182,16 +196,16 @@
>   * OK to have direct references to sparsemem variables in here.
>   */
>  static int
> -memory_block_action(struct memory_block *mem, unsigned long action)
> +memory_block_action(struct memory_block_section *mbs, unsigned long action)
>  {
>  	int i;
>  	unsigned long psection;
>  	unsigned long start_pfn, start_paddr;
>  	struct page *first_page;
>  	int ret;
> -	int old_state = mem->state;
> ot-option-to-disable-memory-hotplug.patch
> +	int old_state = mbs->state;

Where is this noise from ?

>  
> -	psection = mem->phys_index;
> +	psection = mbs->phys_index;
>  	first_page = pfn_to_page(psection << PFN_SECTION_SHIFT);
>  
>  	/*
> @@ -217,18 +231,18 @@
>  			ret = online_pages(start_pfn, PAGES_PER_SECTION);
>  			break;
>  		case MEM_OFFLINE:
> -			mem->state = MEM_GOING_OFFLINE;
> +			mbs->state = MEM_GOING_OFFLINE;
>  			start_paddr = page_to_pfn(first_page) << PAGE_SHIFT;
>  			ret = remove_memory(start_paddr,
>  					    PAGES_PER_SECTION << PAGE_SHIFT);
>  			if (ret) {
> -				mem->state = old_state;
> +				mbs->state = old_state;
>  				break;
>  			}
>  			break;
>  		default:
>  			WARN(1, KERN_WARNING "%s(%p, %ld) unknown action: %ld\n",
> -					__func__, mem, action, action);
> +					__func__, mbs, action, action);
>  			ret = -EINVAL;
>  	}
>  
> @@ -238,19 +252,40 @@
>  static int memory_block_change_state(struct memory_block *mem,
>  		unsigned long to_state, unsigned long from_state_req)
>  {
> +	struct memory_block_section *mbs;
> +	struct list_head *pos;
>  	int ret = 0;
> +
>  	mutex_lock(&mem->state_mutex);
>  
> -	if (mem->state != from_state_req) {
> -		ret = -EINVAL;
> -		goto out;
> +	list_for_each(pos, &mem->sections) {
> +		mbs = list_entry(pos, struct memory_block_section, next);
> +

list_for_each_entry() ?

> +		if (mbs->state != from_state_req)
> +			continue;
> +
> +		ret = memory_block_action(mbs, to_state);
> +		if (ret)
> +			break;
> +	}

Then, all actions will be affect all memory sections under memory block ?
(Hmm..maybe have to see following patches ?)


> +
> +	if (ret) {
> +		list_for_each(pos, &mem->sections) {
> +			mbs = list_entry(pos, struct memory_block_section,
> +					 next);
> +
list_for_each_entry() ?

> +			if (mbs->state == from_state_req)
> +				continue;
> +
> +			if (memory_block_action(mbs, to_state))
> +				printk(KERN_ERR "Could not re-enable memory "
> +				       "section %lx\n", mbs->phys_index);
> +		}
>  	}
>  
> -	ret = memory_block_action(mem, to_state);
>  	if (!ret)
>  		mem->state = to_state;
>  
> -out:
>  	mutex_unlock(&mem->state_mutex);
>  	return ret;
>  }
> @@ -260,20 +295,15 @@
>  		struct sysdev_attribute *attr, const char *buf, size_t count)
>  {

Hmm, store_mem_state() ? What diff option are you using ? 


>  	struct memory_block *mem;
> -	unsigned int phys_section_nr;
>  	int ret = -EINVAL;
>  
>  	mem = container_of(dev, struct memory_block, sysdev);
> -	phys_section_nr = mem->phys_index;
> -
> -	if (!present_section_nr(phys_section_nr))
> -		goto out;
>  
>  	if (!strncmp(buf, "online", min((int)count, 6)))
>  		ret = memory_block_change_state(mem, MEM_ONLINE, MEM_OFFLINE);
>  	else if(!strncmp(buf, "offline", min((int)count, 7)))
>  		ret = memory_block_change_state(mem, MEM_OFFLINE, MEM_ONLINE);
> -out:
> +
>  	if (ret)
>  		return ret;
>  	return count;
> @@ -435,39 +465,6 @@
>  	return 0;
>  }
>  
> -static int add_memory_block(int nid, struct mem_section *section,
> -			unsigned long state, enum mem_add_context context)
> -{
> -	struct memory_block *mem = kzalloc(sizeof(*mem), GFP_KERNEL);
> -	unsigned long start_pfn;
> -	int ret = 0;
> -
> -	if (!mem)
> -		return -ENOMEM;
> -
> -	mem->phys_index = __section_nr(section);
> -	mem->state = state;
> -	mutex_init(&mem->state_mutex);
> -	start_pfn = section_nr_to_pfn(mem->phys_index);
> -	mem->phys_device = arch_get_memory_phys_device(start_pfn);
> -
> -	ret = register_memory(mem, section);
> -	if (!ret)
> -		ret = mem_create_simple_file(mem, phys_index);
> -	if (!ret)
> -		ret = mem_create_simple_file(mem, state);
> -	if (!ret)
> -		ret = mem_create_simple_file(mem, phys_device);
> -	if (!ret)
> -		ret = mem_create_simple_file(mem, removable);
> -	if (!ret) {
> -		if (context == HOTPLUG)
> -			ret = register_mem_sect_under_node(mem, nid);
> -	}
> -
> -	return ret;
> -}
> -


please divide clean-up and logic-change patches into their own..


>  /*
>   * For now, we have a linear search to go find the appropriate
>   * memory_block corresponding to a particular phys_index. If
> @@ -482,12 +479,13 @@
>  	struct sys_device *sysdev;
>  	struct memory_block *mem;
>  	char name[sizeof(MEMORY_CLASS_NAME) + 9 + 1];
> +	int block_id = base_memory_block_id(__section_nr(section));
>  
>  	/*
>  	 * This only works because we know that section == sysdev->id
>  	 * slightly redundant with sysdev_register()
>  	 */
> -	sprintf(&name[0], "%s%d", MEMORY_CLASS_NAME, __section_nr(section));
> +	sprintf(&name[0], "%s%d", MEMORY_CLASS_NAME, block_id);

Hmm. Then, the user has to calculate block-id in addtion to section-id.
Can't we use memory block name as memory%d-%d(start-end) ?



>  
>  	kobj = kset_find_obj(&memory_sysdev_class.kset, name);
>  	if (!kobj)
> @@ -498,19 +496,97 @@
>  
>  	return mem;
>  }
> +static int add_mem_block_section(struct memory_block *mem,
> +				 int section_nr, unsigned long state)
> +{
> +	struct memory_block_section *mbs;
> +
> +	mbs = kzalloc(sizeof(*mbs), GFP_KERNEL);
> +	if (!mbs)
> +		return -ENOMEM;
> +
> +	mbs->phys_index = section_nr;
> +	mbs->state = state;
> +
> +	list_add(&mbs->next, &mem->sections);
> +	return 0;
> +}
> +
> +static int add_memory_block(int nid, struct mem_section *section,
> +			unsigned long state, enum mem_add_context context)
> +{
> +	struct memory_block *mem;
> +	int ret = 0;
> +
> +	mem = find_memory_block(section);

I guess you need to add changes to find_memory_block. section-ID != block-ID.


> +	if (!mem) {
> +		unsigned long start_pfn;
> +
> +		mem = kzalloc(sizeof(*mem), GFP_KERNEL);
> +		if (!mem)
> +			return -ENOMEM;
> +
> +		mem->state = state;
> +		mutex_init(&mem->state_mutex);
> +		start_pfn = section_nr_to_pfn(__section_nr(section));
> +		mem->phys_device = arch_get_memory_phys_device(start_pfn);
> +		INIT_LIST_HEAD(&mem->sections);

I'm not sure this phys_device is properly set in any arch...but this changes in
granule will not affect ?

> +
> +		ret = register_memory(mem, section);
> +		if (!ret)
> +			ret = mem_create_simple_file(mem, phys_index);
> +		if (!ret)
> +			ret = mem_create_simple_file(mem, state);
> +		if (!ret)
> +			ret = mem_create_simple_file(mem, phys_device);
> +		if (!ret)
> +			ret = mem_create_simple_file(mem, removable);
> +		if (!ret) {
> +			if (context == HOTPLUG)
> +				ret = register_mem_sect_under_node(mem, nid);
> +		}
> +	} else {
> +		kobject_put(&mem->sysdev.kobj);
> +	}
> +
> +	if (!ret)
> +		ret = add_mem_block_section(mem, __section_nr(section), state);
> +
> +	return ret;
> +}





>  
>  int remove_memory_block(unsigned long node_id, struct mem_section *section,
>  		int phys_device)
>  {
>  	struct memory_block *mem;
> +	struct memory_block_section *mbs;
> +	struct list_head *pos, *tmp;
> +	int section_nr = __section_nr(section);
>  
>  	mem = find_memory_block(section);

ditto.

> -	unregister_mem_sect_under_nodes(mem);
> -	mem_remove_simple_file(mem, phys_index);
> -	mem_remove_simple_file(mem, state);
> -	mem_remove_simple_file(mem, phys_device);
> -	mem_remove_simple_file(mem, removable);
> -	unregister_memory(mem, section);
> +	mutex_lock(&mem->state_mutex);
> +
> +	/* remove the specified section */
> +	list_for_each_safe(pos, tmp, &mem->sections) {
> +		mbs = list_entry(pos, struct memory_block_section, next);
> +
list_for_each_entry_safe ?

> +		if (mbs->phys_index == section_nr) {
> +			list_del(&mbs->next);
> +			kfree(mbs);
> +		}
> +	}
> +
> +	mutex_unlock(&mem->state_mutex);
> +
> +	if (list_empty(&mem->sections)) {
> +		unregister_mem_sect_under_nodes(mem);
> +		mem_remove_simple_file(mem, phys_index);
> +		mem_remove_simple_file(mem, state);
> +		mem_remove_simple_file(mem, phys_device);
> +		mem_remove_simple_file(mem, removable);
> +		unregister_memory(mem);
> +		kfree(mem);
> +	}
>  
>  	return 0;
>  }
> @@ -532,6 +608,24 @@
>  	return remove_memory_block(0, section, 0);
>  }
>  
> +u32 __weak memory_block_size(void)
> +{
> +	return MIN_MEMORY_BLOCK_SIZE;
> +}
> +
> +static u32 get_memory_block_size(void)
> +{
> +	u32 blk_sz;
> +
> +	blk_sz = memory_block_size();
> +
> +	/* Validate blk_sz is a power of 2 and not less than section size */
> +	if ((blk_sz & (blk_sz - 1)) || (blk_sz < MIN_MEMORY_BLOCK_SIZE))
> +		blk_sz = MIN_MEMORY_BLOCK_SIZE;
> +
> +	return blk_sz;
> +}
> +
>  /*
>   * Initialize the sysfs support for memory devices...
>   */
> @@ -540,12 +634,16 @@
>  	unsigned int i;
>  	int ret;
>  	int err;
> +	int block_sz;
>  
>  	memory_sysdev_class.kset.uevent_ops = &memory_uevent_ops;
>  	ret = sysdev_class_register(&memory_sysdev_class);
>  	if (ret)
>  		goto out;
>  
> +	block_sz = get_memory_block_size();
> +	sections_per_block = block_sz / MIN_MEMORY_BLOCK_SIZE;
> +
>  	/*
>  	 * Create entries for memory sections that were found
>  	 * during boot and have been initialized
> Index: linux-2.6/include/linux/memory.h
> ===================================================================
> --- linux-2.6.orig/include/linux/memory.h	2010-07-08 11:27:21.000000000 -0500
> +++ linux-2.6/include/linux/memory.h	2010-07-09 14:22:44.000000000 -0500
> @@ -19,9 +19,15 @@
>  #include <linux/node.h>
>  #include <linux/compiler.h>
>  #include <linux/mutex.h>
> +#include <linux/list.h>
>  
> -struct memory_block {
> +struct memory_block_section {
> +	unsigned long state;
>  	unsigned long phys_index;
> +	struct list_head next;
> +};
> +
> +struct memory_block {
>  	unsigned long state;
>  	/*
>  	 * This serializes all state change requests.  It isn't
> @@ -34,6 +40,7 @@
>  	void *hw;			/* optional pointer to fw/hw data */
>  	int (*phys_callback)(struct memory_block *);
>  	struct sys_device sysdev;
> +	struct list_head sections;
>  };
>  
>  int arch_get_memory_phys_device(unsigned long start_pfn);
> @@ -113,7 +120,7 @@
>  extern int remove_memory_block(unsigned long, struct mem_section *, int);
>  extern int memory_notify(unsigned long val, void *v);
>  extern int memory_isolate_notify(unsigned long val, void *v);
> -extern struct memory_block *find_memory_block(unsigned long);
> +extern struct memory_block *find_memory_block(struct mem_section *);
>  extern int memory_is_hidden(struct mem_section *);
>  #define CONFIG_MEM_BLOCK_SIZE	(PAGES_PER_SECTION<<PAGE_SHIFT)
>  enum mem_add_context { BOOT, HOTPLUG };
> --
> To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
> the body of a message to majordomo@vger.kernel.org
> More majordomo info at  http://vger.kernel.org/majordomo-info.html
> Please read the FAQ at  http://www.tux.org/lkml/
> 

^ permalink raw reply

* Re: [PATCH 0/7] De-couple sysfs memory directories from memory sections
From: Nathan Fontenot @ 2010-07-13  2:28 UTC (permalink / raw)
  To: 7eggert; +Cc: Bodo Eggert, linuxppc-dev, linux-kernel
In-Reply-To: <20100712194958.1A32B2D6595@mail-in-10.arcor-online.net>

On 07/12/2010 02:30 PM, Bodo Eggert wrote:
> Nathan Fontenot <nfont@austin.ibm.com> wrote:
> 
>> The file 'split' allows for splitting the
>> directory in two, with each new directory covering half as many
>> memory sections as the previous directory.
> 
> Just some random thoughts:
> 1) Why is it needed/helpful?

This is needed if someone needed to perform an action (add/remove) a
single memory section. The 'split' option allows users to isolate
a memory section  so these operations could be performed.

> 2) If it is needed, why not take an int to split after n entries?

The idea of being able to split a directory came out of a previous
discussion on how to resolve the issue this patch set is trying to solve.
I included the split functionality in this patch set since it
was suggested.  I will leave the decision of whether or not this
functionality is needed up to the community.

-Nathan 

^ permalink raw reply

* [PATCH] kbuild: Enable building defconfigs from Kconfig files
From: Stephen Rothwell @ 2010-07-13  1:43 UTC (permalink / raw)
  To: Linus, Michal Marek; +Cc: Catalin Marinas, Andrew Morton, ppc-dev, LKML

After this change, doing a "make xxx_defconfig" will check first for
a file called arch/<arch>/configs/Kconfig.xxx and use that to generate
the .config (effectively starting from an allnoconfig).  If that file
doesn't exist, it will use arch/<ARCH>/configs/xxx_defconfig as now.

Signed-off-by: Stephen Rothwell <sfr@canb.auug.org.au>
---
 scripts/kconfig/Makefile |   14 +++++++++++++-
 1 files changed, 13 insertions(+), 1 deletions(-)

Hi Linus,

Is this more the direction you want to take?

There are still 2 main problems with is approach:

	- there are some config options that are globally and
unconditionally enabled that some platforms may not want.  The only way
currently to turn them off is to reproduce the config entry with the
different default.  I am not sure if we need a wa to turn them off or to
just change them to being neede to be selected by those that do want them.
	- we have no way to select options that are neither bool or
tristate to suitable values.  Again the only way to do that currently is
to reproduce the config entry with a different default value.

I am currently working towards using this to recreate the PowerPC
defconfigs, but it is a slow process as they have some much stuff enabled
in them and some of it is probably actually not relevant.

This process is made easier by the recent commit "kbuild: Warn on
selecting symbols with unmet direct dependencies" that is in the kbuild
tree (and linux-next).

diff --git a/scripts/kconfig/Makefile b/scripts/kconfig/Makefile
index 7ea649d..1ab8f45 100644
--- a/scripts/kconfig/Makefile
+++ b/scripts/kconfig/Makefile
@@ -117,9 +117,21 @@ else
 	$(Q)$< -D arch/$(SRCARCH)/configs/$(KBUILD_DEFCONFIG) $(Kconfig)
 endif
 
-%_defconfig: $(obj)/conf
+configs_dir := $(srctree)/arch/$(SRCARCH)/configs
+# We check a level of sub directories because arch/powerpc
+# has its defconfig files arranged that way
+old_defconfigs := $(patsubst $(configs_dir)/%,%,\
+	$(wildcard $(configs_dir)/*_defconfig) \
+	$(wildcard $(configs_dir)/*/*_defconfig))
+defconfigs := $(patsubst $(configs_dir)/Kconfig.%,%_defconfig,\
+	$(wildcard $(configs_dir)/Kconfig.*))
+
+$(old_defconfigs): %_defconfig: $(obj)/conf
 	$(Q)$< -D arch/$(SRCARCH)/configs/$@ $(Kconfig)
 
+$(defconfigs): %_defconfig: $(obj)/conf
+	$(Q)$< -n arch/$(SRCARCH)/configs/Kconfig.$*
+
 # Help text used by make help
 help:
 	@echo  '  config	  - Update current config utilising a line-oriented program'
-- 
1.7.1

-- 
Cheers,
Stephen Rothwell                    sfr@canb.auug.org.au
http://www.canb.auug.org.au/~sfr/

^ permalink raw reply related

* Re: section .data..init_task
From: Sean MacLennan @ 2010-07-13  0:34 UTC (permalink / raw)
  To: Denys Vlasenko, linuxppc-dev, Tim Abbott, Sam Ravnborg
In-Reply-To: <20100628005900.28d4a8e0@lappy.seanm.ca>

On Mon, 28 Jun 2010 00:59:00 -0400
Sean MacLennan <smaclennan@pikatech.com> wrote:

> Anybody else seeing these messages?
> 
> ppc_4xxFP-ld: .tmp_vmlinux1: section .data..init_task lma 0xc0374000
> overlaps previous sections ppc_4xxFP-ld: .tmp_vmlinux2:
> section .data..init_task lma 0xc03a2000 overlaps previous sections
> ppc_4xxFP-ld: vmlinux: section .data..init_task lma 0xc03a2000
> overlaps previous sections
> 
> Or does anybody know what they mean? They started showing up in
> 2.6.35.
> 
> Very easy to reproduce, so don't hesitate to ask for more info.

I had a bit of time, so I tracked this down. This patch seems to be
the culprit: http://lkml.org/lkml/2010/2/19/366

Specifically, this code:

 	/* The initial task and kernel stack */
-	.data.init_task : AT(ADDR(.data.init_task) - LOAD_OFFSET) {
-		INIT_TASK_DATA(THREAD_SIZE)
-	}
+	INIT_TASK_DATA_SECTION(THREAD_SIZE)

If I change it back to:

	/* The initial task and kernel stack */
	.data..init_task : AT(ADDR(.data..init_task) - LOAD_OFFSET) {
		INIT_TASK_DATA(THREAD_SIZE)
	}

not only do the warnings go away, but the kernel now boots again!

Cheers,
   Sean

^ permalink raw reply

* Re: [PATCH 1/9 v1.01] Add Synopsys DesignWare HS USB OTG Controller driver.
From: David Brownell @ 2010-07-12 23:55 UTC (permalink / raw)
  To: linux-usb, Fushen Chen
  Cc: linuxppc-dev, chuck, gregkh, Mark Miesfeld, Fushen Chen
In-Reply-To: <12789766042434-git-send-email-fchen@apm.com>

Please remove all the changes not related to
this Synopsis IP ... and make the OTG functionality
key on the generic OTG symbol, not a DW-specific one.

^ permalink raw reply

* [PATCH 8/9 v1.01] Add Synopsys DesignWare HS USB OTG Controller driver.
From: Fushen Chen @ 2010-07-12 23:16 UTC (permalink / raw)
  To: linux-usb; +Cc: linuxppc-dev, chuck, gregkh, Mark Miesfeld, Fushen Chen
In-Reply-To: <12789766063456-git-send-email-fchen@apm.com>

Implements the DWC OTG Peripheral Controller Driver (PCD)
Interrupt Service routine.

Signed-off-by: Fushen Chen <fchen@apm.com>
Signed-off-by: Mark Miesfeld <mmiesfeld@apm.com>
---
 drivers/usb/otg/dwc_otg_pcd_intr.c | 2273 ++++++++++++++++++++++++++++++++++++
 1 files changed, 2273 insertions(+), 0 deletions(-)
 create mode 100644 drivers/usb/otg/dwc_otg_pcd_intr.c

diff --git a/drivers/usb/otg/dwc_otg_pcd_intr.c b/drivers/usb/otg/dwc_otg_pcd_intr.c
new file mode 100644
index 0000000..487e44c
--- /dev/null
+++ b/drivers/usb/otg/dwc_otg_pcd_intr.c
@@ -0,0 +1,2273 @@
+/*
+ * DesignWare HS OTG controller driver
+ *
+ * Author: Mark Miesfeld <mmiesfeld@apm.com>
+ *
+ * Based on versions provided by AMCC and Synopsis which are:
+ *	Copyright (C) 2009-2010 AppliedMicro(www.apm.com)
+ * Modified by Stefan Roese <sr@denx.de>, DENX Software Engineering
+ *
+ * Synopsys HS OTG Linux Software Driver and documentation (hereinafter,
+ * "Software") is an Unsupported proprietary work of Synopsys, Inc. unless
+ * otherwise expressly agreed to in writing between Synopsys and you.
+ *
+ * The Software IS NOT an item of Licensed Software or Licensed Product under
+ * any End User Software License Agreement or Agreement for Licensed Product
+ * with Synopsys or any supplement thereto. You are permitted to use and
+ * redistribute this Software in source and binary forms, with or without
+ * modification, provided that redistributions of source code must retain this
+ * notice. You may not view, use, disclose, copy or distribute this file or
+ * any information contained herein except pursuant to this license grant from
+ * Synopsys. If you do not agree with this notice, including the disclaimer
+ * below, then you are not authorized to use the Software.
+ *
+ * THIS SOFTWARE IS BEING DISTRIBUTED BY SYNOPSYS SOLELY ON AN "AS IS" BASIS
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE HEREBY DISCLAIMED. IN NO EVENT SHALL SYNOPSYS BE LIABLE FOR ANY DIRECT,
+ * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
+ * DAMAGE.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+ * or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include <linux/interrupt.h>
+#include <linux/dma-mapping.h>
+
+#include "dwc_otg_driver.h"
+#include "dwc_otg_pcd.h"
+
+/**
+ * This function returns pointer to in ep struct with number num
+ */
+static struct pcd_ep *get_in_ep(struct dwc_pcd *pcd, u32 num)
+{
+	u32 i;
+	int num_in_eps = GET_CORE_IF(pcd)->dev_if->num_in_eps;
+
+	if (num == 0) {
+		return &pcd->ep0;
+	} else {
+		for (i = 0; i < num_in_eps; ++i) {
+			if (pcd->in_ep[i].dwc_ep.num == num)
+				return &pcd->in_ep[i];
+		}
+	}
+	return 0;
+}
+
+/**
+ * This function returns pointer to out ep struct with number num
+ */
+static struct pcd_ep *get_out_ep(struct dwc_pcd *pcd, u32 num)
+{
+	u32 i;
+	int num_out_eps = GET_CORE_IF(pcd)->dev_if->num_out_eps;
+
+	if (num == 0) {
+		return &pcd->ep0;
+	} else {
+		for (i = 0; i < num_out_eps; ++i) {
+			if (pcd->out_ep[i].dwc_ep.num == num)
+				return &pcd->out_ep[i];
+		}
+	}
+	return 0;
+}
+
+/**
+ * This functions gets a pointer to an EP from the wIndex address
+ * value of the control request.
+ */
+static struct pcd_ep *get_ep_by_addr(struct dwc_pcd *pcd, u16 index)
+{
+	struct pcd_ep *ep;
+
+	if (!(index & USB_ENDPOINT_NUMBER_MASK))
+		return &pcd->ep0;
+
+	list_for_each_entry(ep, &pcd->gadget.ep_list, ep.ep_list) {
+		u8 bEndpointAddress;
+
+		if (!ep->desc)
+			continue;
+
+		bEndpointAddress = ep->desc->bEndpointAddress;
+		if ((index ^ bEndpointAddress) & USB_DIR_IN)
+			continue;
+
+		if ((index & 0x0f) == (bEndpointAddress & 0x0f))
+			return ep;
+	}
+	return NULL;
+}
+
+/**
+ * This function checks the EP request queue, if the queue is not
+ * empty the next request is started.
+ */
+void start_next_request(struct pcd_ep *ep)
+{
+	struct pcd_request *req = NULL;
+
+	if (!list_empty(&ep->queue)) {
+		req = list_entry(ep->queue.next, struct pcd_request, queue);
+
+		/* Setup and start the Transfer */
+		ep->dwc_ep.start_xfer_buff = req->req.buf;
+		ep->dwc_ep.xfer_buff = req->req.buf;
+		ep->dwc_ep.xfer_len = req->req.length;
+		ep->dwc_ep.xfer_count = 0;
+		ep->dwc_ep.dma_addr = req->req.dma;
+		ep->dwc_ep.sent_zlp = 0;
+		ep->dwc_ep.total_len = ep->dwc_ep.xfer_len;
+
+		/*
+		 * Added-sr: 2007-07-26
+		 *
+		 * When a new transfer will be started, mark this
+		 * endpoint as active. This way it will be blocked
+		 * for further transfers, until the current transfer
+		 * is finished.
+		 */
+		if (dwc_has_feature(GET_CORE_IF(ep->pcd), DWC_LIMITED_XFER))
+			ep->dwc_ep.active = 1;
+
+		dwc_otg_ep_start_transfer(GET_CORE_IF(ep->pcd), &ep->dwc_ep);
+	}
+}
+
+/**
+ * This function handles the SOF Interrupts. At this time the SOF
+ * Interrupt is disabled.
+ */
+static int dwc_otg_pcd_handle_sof_intr(struct dwc_pcd *pcd)
+{
+	struct core_if *core_if = GET_CORE_IF(pcd);
+	union gintsts_data gintsts;
+
+	/* Clear interrupt */
+	gintsts.d32 = 0;
+	gintsts.b.sofintr = 1;
+	dwc_write_reg32(&core_if->core_global_regs->gintsts, gintsts.d32);
+	return 1;
+}
+
+/**
+ * This function reads the 8 bytes of the setup packet from the Rx FIFO into the
+ * destination buffer.  It is called from the Rx Status Queue Level (RxStsQLvl)
+ * interrupt routine when a SETUP packet has been received in Slave mode.
+ */
+static void dwc_otg_read_setup_packet(struct core_if *core_if, u32 *dest)
+{
+	dest[0] = dwc_read_datafifo32(core_if->data_fifo[0]);
+	dest[1] = dwc_read_datafifo32(core_if->data_fifo[0]);
+}
+/**
+ * This function handles the Rx Status Queue Level Interrupt, which
+ * indicates that there is a least one packet in the Rx FIFO.  The
+ * packets are moved from the FIFO to memory, where they will be
+ * processed when the Endpoint Interrupt Register indicates Transfer
+ * Complete or SETUP Phase Done.
+ *
+ * Repeat the following until the Rx Status Queue is empty:
+ *	 -# Read the Receive Status Pop Register (GRXSTSP) to get Packet
+ *		info
+ *	 -# If Receive FIFO is empty then skip to step Clear the interrupt
+ *		and exit
+ *	 -# If SETUP Packet call dwc_otg_read_setup_packet to copy the
+ *		SETUP data to the buffer
+ *	 -# If OUT Data Packet call dwc_otg_read_packet to copy the data
+ *		to the destination buffer
+ */
+static int dwc_otg_pcd_handle_rx_status_q_level_intr(struct dwc_pcd *pcd)
+{
+	struct core_if *core_if = GET_CORE_IF(pcd);
+	struct core_global_regs *global_regs = core_if->core_global_regs;
+	union gintmsk_data gintmask = {.d32 = 0};
+	union device_grxsts_data status;
+	struct pcd_ep *ep;
+	union gintsts_data gintsts;
+
+	/* Disable the Rx Status Queue Level interrupt */
+	gintmask.b.rxstsqlvl = 1;
+	dwc_modify_reg32(&global_regs->gintmsk, gintmask.d32, 0);
+
+	/* Get the Status from the top of the FIFO */
+	status.d32 = dwc_read_reg32(&global_regs->grxstsp);
+
+	/* Get pointer to EP structure */
+	ep = get_out_ep(pcd, status.b.epnum);
+
+	switch (status.b.pktsts) {
+	case DWC_DSTS_GOUT_NAK:
+		break;
+	case DWC_STS_DATA_UPDT:
+		if (status.b.bcnt && ep->dwc_ep.xfer_buff) {
+			dwc_otg_read_packet(core_if, ep->dwc_ep.xfer_buff,
+						status.b.bcnt);
+			ep->dwc_ep.xfer_count += status.b.bcnt;
+			ep->dwc_ep.xfer_buff += status.b.bcnt;
+		}
+		break;
+	case DWC_STS_XFER_COMP:
+		break;
+	case DWC_DSTS_SETUP_COMP:
+		break;
+	case DWC_DSTS_SETUP_UPDT:
+		dwc_otg_read_setup_packet(core_if, pcd->setup_pkt->d32);
+		ep->dwc_ep.xfer_count += status.b.bcnt;
+		break;
+	default:
+		break;
+	}
+
+	/* Enable the Rx Status Queue Level interrupt */
+	dwc_modify_reg32(&global_regs->gintmsk, 0, gintmask.d32);
+
+	/* Clear interrupt */
+	gintsts.d32 = 0;
+	gintsts.b.rxstsqlvl = 1;
+	dwc_write_reg32(&global_regs->gintsts, gintsts.d32);
+
+	return 1;
+}
+
+/**
+ * This function examines the Device IN Token Learning Queue to
+ * determine the EP number of the last IN token received.  This
+ * implementation is for the Mass Storage device where there are only
+ * 2 IN EPs (Control-IN and BULK-IN).
+ *
+ * The EP numbers for the first six IN Tokens are in DTKNQR1 and there
+ * are 8 EP Numbers in each of the other possible DTKNQ Registers.
+ */
+static int get_ep_of_last_in_token(struct core_if *core_if)
+{
+	struct device_global_regs *regs = core_if->dev_if->dev_global_regs;
+	const u32 TOKEN_Q_DEPTH = core_if->hwcfg2.b.dev_token_q_depth;
+
+	/* Number of Token Queue Registers */
+	const int DTKNQ_REG_CNT = (TOKEN_Q_DEPTH + 7) / 8;
+	union dtknq1_data dtknqr1;
+	u32 in_tkn_epnums[4];
+	int ndx;
+	u32 i;
+	u32 *addr = &regs->dtknqr1;
+	int epnum = 0;
+
+	/* Read the DTKNQ Registers */
+	for (i = 0; i <= DTKNQ_REG_CNT; i++) {
+		in_tkn_epnums[i] = dwc_read_reg32(addr);
+
+		if (addr == &regs->dvbusdis)
+			addr = &regs->dtknqr3_dthrctl;
+		else
+			++addr;
+	}
+
+	/* Copy the DTKNQR1 data to the bit field. */
+	dtknqr1.d32 = in_tkn_epnums[0];
+
+	/* Get the EP numbers */
+	in_tkn_epnums[0] = dtknqr1.b.epnums0_5;
+	ndx = dtknqr1.b.intknwptr - 1;
+
+	if (ndx == -1) {
+		/*
+		 * Calculate the max queue position.
+		 */
+		int cnt = TOKEN_Q_DEPTH;
+
+		if (TOKEN_Q_DEPTH <= 6)
+			cnt = TOKEN_Q_DEPTH - 1;
+		else if (TOKEN_Q_DEPTH <= 14)
+			cnt = TOKEN_Q_DEPTH - 7;
+		else if (TOKEN_Q_DEPTH <= 22)
+			cnt = TOKEN_Q_DEPTH - 15;
+		else
+			cnt = TOKEN_Q_DEPTH - 23;
+
+		epnum = (in_tkn_epnums[DTKNQ_REG_CNT - 1] >> (cnt * 4)) & 0xF;
+	} else {
+		if (ndx <= 5) {
+			epnum = (in_tkn_epnums[0] >> (ndx * 4)) & 0xF;
+		} else if (ndx <= 13) {
+			ndx -= 6;
+			epnum = (in_tkn_epnums[1] >> (ndx * 4)) & 0xF;
+		} else if (ndx <= 21) {
+			ndx -= 14;
+			epnum = (in_tkn_epnums[2] >> (ndx * 4)) & 0xF;
+		} else if (ndx <= 29) {
+			ndx -= 22;
+			epnum = (in_tkn_epnums[3] >> (ndx * 4)) & 0xF;
+		}
+	}
+
+	return epnum;
+}
+
+static inline int count_dwords(struct pcd_ep *ep, u32 len)
+{
+	if (len > ep->dwc_ep.maxpacket)
+		len = ep->dwc_ep.maxpacket;
+	return (len + 3) / 4;
+}
+
+/**
+ * This function writes a packet into the Tx FIFO associated with the EP.  For
+ * non-periodic EPs the non-periodic Tx FIFO is written.  For periodic EPs the
+ * periodic Tx FIFO associated with the EP is written with all packets for the
+ * next micro-frame.
+ *
+ * The buffer is padded to DWORD on a per packet basis in
+ * slave/dma mode if the MPS is not DWORD aligned.  The last packet, if
+ * short, is also padded to a multiple of DWORD.
+ *
+ * ep->xfer_buff always starts DWORD aligned in memory and is a
+ * multiple of DWORD in length
+ *
+ * ep->xfer_len can be any number of bytes
+ *
+ * ep->xfer_count is a multiple of ep->maxpacket until the last packet
+ *
+ * FIFO access is DWORD
+ */
+static void dwc_otg_ep_write_packet(struct core_if *core_if, struct dwc_ep *ep,
+				int dma)
+{
+	u32 i;
+	u32 byte_count;
+	u32 dword_count;
+	u32 *fifo;
+	u32 *data_buff = (u32 *) ep->xfer_buff;
+
+	if (ep->xfer_count >= ep->xfer_len)
+		return;
+
+	/* Find the byte length of the packet either short packet or MPS */
+	if ((ep->xfer_len - ep->xfer_count) < ep->maxpacket)
+		byte_count = ep->xfer_len - ep->xfer_count;
+	else
+		byte_count = ep->maxpacket;
+
+	/*
+	 * Find the DWORD length, padded by extra bytes as neccessary if MPS
+	 * is not a multiple of DWORD
+	 */
+	dword_count = (byte_count + 3) / 4;
+
+	fifo = core_if->data_fifo[ep->num];
+
+	if (!dma)
+		for (i = 0; i < dword_count; i++, data_buff++)
+			dwc_write_datafifo32(fifo, *data_buff);
+
+	ep->xfer_count += byte_count;
+	ep->xfer_buff += byte_count;
+	ep->dma_addr += byte_count;
+}
+
+/**
+ * This interrupt occurs when the non-periodic Tx FIFO is half-empty.
+ * The active request is checked for the next packet to be loaded into
+ * the non-periodic Tx FIFO.
+ */
+static int dwc_otg_pcd_handle_np_tx_fifo_empty_intr(struct dwc_pcd *pcd)
+{
+	struct core_if *core_if = GET_CORE_IF(pcd);
+	struct core_global_regs *global_regs = core_if->core_global_regs;
+	union gnptxsts_data txstatus = {.d32 = 0 };
+	union gintsts_data gintsts = {.d32 = 0};
+	int epnum = 0;
+	struct pcd_ep *ep;
+	u32 len;
+	int dwords;
+
+	/* Get the epnum from the IN Token Learning Queue. */
+	epnum = get_ep_of_last_in_token(core_if);
+	ep = get_in_ep(pcd, epnum);
+
+	txstatus.d32 = dwc_read_reg32(&global_regs->gnptxsts);
+
+	/*
+	 * While there is space in the queue, space in the FIFO, and data to
+	 * tranfer, write packets to the Tx FIFO
+	 */
+	len = ep->dwc_ep.xfer_len - ep->dwc_ep.xfer_count;
+	dwords = count_dwords(ep, len);
+	while (txstatus.b.nptxqspcavail > 0 &&
+			txstatus.b.nptxfspcavail > dwords &&
+			ep->dwc_ep.xfer_count < ep->dwc_ep.xfer_len) {
+		/*
+		 * Added-sr: 2007-07-26
+		 *
+		 * When a new transfer will be started, mark this
+		 * endpoint as active. This way it will be blocked
+		 * for further transfers, until the current transfer
+		 * is finished.
+		 */
+		if (dwc_has_feature(core_if, DWC_LIMITED_XFER))
+			ep->dwc_ep.active = 1;
+
+		dwc_otg_ep_write_packet(core_if, &ep->dwc_ep, 0);
+		len = ep->dwc_ep.xfer_len - ep->dwc_ep.xfer_count;
+		dwords = count_dwords(ep, len);
+		txstatus.d32 = dwc_read_reg32(&global_regs->gnptxsts);
+	}
+
+	/* Clear nptxfempty interrupt */
+	gintsts.b.nptxfempty = 1;
+	dwc_write_reg32(&global_regs->gintsts, gintsts.d32);
+
+	/* Re-enable tx-fifo empty interrupt, if packets are stil pending */
+	if (len)
+		dwc_modify_reg32(&global_regs->gintmsk, 0, gintsts.d32);
+	return 1;
+}
+
+/**
+ * This function is called when dedicated Tx FIFO Empty interrupt occurs.
+ * The active request is checked for the next packet to be loaded into
+ * apropriate Tx FIFO.
+ */
+static int write_empty_tx_fifo(struct dwc_pcd *pcd, u32 epnum)
+{
+	struct core_if *core_if = GET_CORE_IF(pcd);
+	struct device_in_ep_regs *regs;
+	union dtxfsts_data txstatus = {.d32 = 0};
+	struct pcd_ep *ep;
+	u32 len;
+	int dwords;
+	union diepint_data diepint;
+
+	ep = get_in_ep(pcd, epnum);
+	regs = core_if->dev_if->in_ep_regs[epnum];
+
+	txstatus.d32 = dwc_read_reg32(&regs->dtxfsts);
+
+	/*
+	 * While there is space in the queue, space in the FIFO and data to
+	 * tranfer, write packets to the Tx FIFO
+	 */
+	len = ep->dwc_ep.xfer_len - ep->dwc_ep.xfer_count;
+	dwords = count_dwords(ep, len);
+	while (txstatus.b.txfspcavail > dwords && ep->dwc_ep.xfer_count <
+			ep->dwc_ep.xfer_len && ep->dwc_ep.xfer_len != 0) {
+		dwc_otg_ep_write_packet(core_if, &ep->dwc_ep, 0);
+		len = ep->dwc_ep.xfer_len - ep->dwc_ep.xfer_count;
+		dwords = count_dwords(ep, len);
+		txstatus.d32 = dwc_read_reg32(&regs->dtxfsts);
+	}
+	/* Clear emptyintr */
+	diepint.b.emptyintr = 1;
+	dwc_write_reg32(in_ep_int_reg(pcd, epnum), diepint.d32);
+	return 1;
+}
+
+/**
+ * This function is called when the Device is disconnected.  It stops any active
+ * requests and informs the Gadget driver of the disconnect.
+ */
+void dwc_otg_pcd_stop(struct dwc_pcd *pcd)
+{
+	int i, num_in_eps, num_out_eps;
+	struct pcd_ep *ep;
+	union gintmsk_data intr_mask = {.d32 = 0};
+
+	num_in_eps = GET_CORE_IF(pcd)->dev_if->num_in_eps;
+	num_out_eps = GET_CORE_IF(pcd)->dev_if->num_out_eps;
+
+	/* Don't disconnect drivers more than once */
+	if (pcd->ep0state == EP0_DISCONNECT)
+		return;
+	pcd->ep0state = EP0_DISCONNECT;
+
+	/* Reset the OTG state. */
+	dwc_otg_pcd_update_otg(pcd, 1);
+
+	/* Disable the NP Tx Fifo Empty Interrupt. */
+	intr_mask.b.nptxfempty = 1;
+	dwc_modify_reg32(&GET_CORE_IF(pcd)->core_global_regs->gintmsk,
+				intr_mask.d32, 0);
+
+	/* Flush the FIFOs */
+	dwc_otg_flush_tx_fifo(GET_CORE_IF(pcd), 0);
+	dwc_otg_flush_rx_fifo(GET_CORE_IF(pcd));
+
+	/* Prevent new request submissions, kill any outstanding requests  */
+	ep = &pcd->ep0;
+	request_nuke(ep);
+
+	/* Prevent new request submissions, kill any outstanding requests  */
+	for (i = 0; i < num_in_eps; i++)
+		request_nuke((struct pcd_ep *) &pcd->in_ep[i]);
+
+	/* Prevent new request submissions, kill any outstanding requests  */
+	for (i = 0; i < num_out_eps; i++)
+		request_nuke((struct pcd_ep *) &pcd->out_ep[i]);
+
+	/* Report disconnect; the driver is already quiesced */
+	if (pcd->driver && pcd->driver->disconnect) {
+		spin_unlock(&pcd->lock);
+		pcd->driver->disconnect(&pcd->gadget);
+		spin_lock(&pcd->lock);
+	}
+}
+
+/**
+ * This interrupt indicates that ...
+ */
+static int dwc_otg_pcd_handle_i2c_intr(struct dwc_pcd *pcd)
+{
+	union gintmsk_data intr_mask = {.d32 = 0};
+	union gintsts_data gintsts;
+
+	printk(KERN_INFO "Interrupt handler not implemented for i2cintr\n");
+
+	/* Turn off and clean the interrupt */
+	intr_mask.b.i2cintr = 1;
+	dwc_modify_reg32(&GET_CORE_IF(pcd)->core_global_regs->gintmsk,
+				intr_mask.d32, 0);
+
+	gintsts.d32 = 0;
+	gintsts.b.i2cintr = 1;
+	dwc_write_reg32(&GET_CORE_IF(pcd)->core_global_regs->gintsts,
+				gintsts.d32);
+
+	return 1;
+}
+
+/**
+ * This interrupt indicates that ...
+ */
+static int dwc_otg_pcd_handle_early_suspend_intr(struct dwc_pcd *pcd)
+{
+	union gintmsk_data intr_mask = {.d32 = 0};
+	union gintsts_data gintsts;
+
+	printk(KERN_INFO "Early Suspend Detected\n");
+
+	/* Turn off and clean the interrupt */
+	intr_mask.b.erlysuspend = 1;
+	dwc_modify_reg32(&GET_CORE_IF(pcd)->core_global_regs->gintmsk,
+				intr_mask.d32, 0);
+
+	gintsts.d32 = 0;
+	gintsts.b.erlysuspend = 1;
+	dwc_write_reg32(&GET_CORE_IF(pcd)->core_global_regs->gintsts,
+				gintsts.d32);
+
+	return 1;
+}
+
+/**
+ * This function configures EPO to receive SETUP packets.
+ *
+ * Program the following fields in the endpoint specific registers for Control
+ * OUT EP 0, in order to receive a setup packet:
+ *
+ * - DOEPTSIZ0.Packet Count = 3 (To receive up to 3 back to back setup packets)
+ *
+ * - DOEPTSIZE0.Transfer Size = 24 Bytes (To receive up to 3 back to back setup
+ *   packets)
+ *
+ * In DMA mode, DOEPDMA0 Register with a memory address to store any setup
+ * packets received
+ */
+static void ep0_out_start(struct core_if *core_if, struct dwc_pcd *pcd)
+{
+	struct device_if *dev_if = core_if->dev_if;
+	union deptsiz0_data doeptsize0 = {.d32 = 0};
+
+	doeptsize0.b.supcnt = 3;
+	doeptsize0.b.pktcnt = 1;
+	doeptsize0.b.xfersize = 8 * 3;
+	dwc_write_reg32(&dev_if->out_ep_regs[0]->doeptsiz, doeptsize0.d32);
+
+	if (core_if->dma_enable) {
+		union depctl_data doepctl = {.d32 = 0};
+
+		dwc_write_reg32(&dev_if->out_ep_regs[0]->doepdma,
+				pcd->setup_pkt_dma_handle);
+
+		doepctl.b.epena = 1;
+		doepctl.b.usbactep = 1;
+		dwc_write_reg32(out_ep_ctl_reg(pcd, 0), doepctl.d32);
+	}
+}
+
+/**
+ * This interrupt occurs when a USB Reset is detected.  When the USB Reset
+ * Interrupt occurs the device state is set to DEFAULT and the EP0 state is set
+ * to IDLE.
+ *
+ * Set the NAK bit for all OUT endpoints (DOEPCTLn.SNAK = 1)
+ *
+ * Unmask the following interrupt bits:
+ *  - DAINTMSK.INEP0 = 1 (Control 0 IN endpoint)
+ *  - DAINTMSK.OUTEP0 = 1 (Control 0 OUT endpoint)
+ *  - DOEPMSK.SETUP = 1
+ *  - DOEPMSK.XferCompl = 1
+ *  - DIEPMSK.XferCompl = 1
+ *  - DIEPMSK.TimeOut = 1
+ *
+ * Program the following fields in the endpoint specific registers for Control
+ * OUT EP 0, in order to receive a setup packet
+ *  - DOEPTSIZ0.Packet Count = 3 (To receive up to 3 back to back setup packets)
+ *  - DOEPTSIZE0.Transfer Size = 24 Bytes (To receive up to 3 back to back setup
+ *    packets)
+ *
+ *  - In DMA mode, DOEPDMA0 Register with a memory address to store any setup
+ *    packets received
+ *
+ * At this point, all the required initialization, except for enabling
+ * the control 0 OUT endpoint is done, for receiving SETUP packets.
+ *
+ * Note that the bits in the Device IN endpoint mask register (diepmsk) are laid
+ * out exactly the same as the Device IN endpoint interrupt register (diepint.)
+ * Likewise for Device OUT endpoint mask / interrupt registers (doepmsk /
+ * doepint.)
+ */
+static int dwc_otg_pcd_handle_usb_reset_intr(struct dwc_pcd *pcd)
+{
+	struct core_if *core_if = GET_CORE_IF(pcd);
+	struct device_if *dev_if = core_if->dev_if;
+	union depctl_data doepctl = {.d32 = 0};
+	union daint_data daintmsk = {.d32 = 0};
+	union doepint_data doepmsk = {.d32 = 0};
+	union diepint_data diepmsk = {.d32 = 0};
+	union dcfg_data dcfg = {.d32 = 0};
+	union grstctl_data resetctl = {.d32 = 0};
+	union dctl_data dctl = {.d32 = 0};
+	u32 i;
+	union gintsts_data gintsts = {.d32 = 0 };
+
+	printk(KERN_INFO "USB RESET\n");
+
+	/* reset the HNP settings */
+	dwc_otg_pcd_update_otg(pcd, 1);
+
+	/* Clear the Remote Wakeup Signalling */
+	dctl.b.rmtwkupsig = 1;
+	dwc_modify_reg32(dev_ctl_reg(pcd), dctl.d32, 0);
+
+	/* Set NAK for all OUT EPs */
+	doepctl.b.snak = 1;
+	for (i = 0; i <= dev_if->num_out_eps; i++)
+		dwc_write_reg32(out_ep_ctl_reg(pcd, i), doepctl.d32);
+
+	/* Flush the NP Tx FIFO */
+	dwc_otg_flush_tx_fifo(core_if, 0);
+
+	/* Flush the Learning Queue */
+	resetctl.b.intknqflsh = 1;
+	dwc_write_reg32(&core_if->core_global_regs->grstctl, resetctl.d32);
+
+	daintmsk.b.inep0 = 1;
+	daintmsk.b.outep0 = 1;
+	dwc_write_reg32(&dev_if->dev_global_regs->daintmsk, daintmsk.d32);
+
+	doepmsk.b.setup = 1;
+	doepmsk.b.xfercompl = 1;
+	doepmsk.b.ahberr = 1;
+	doepmsk.b.epdisabled = 1;
+	dwc_write_reg32(&dev_if->dev_global_regs->doepmsk, doepmsk.d32);
+
+	diepmsk.b.xfercompl = 1;
+	diepmsk.b.timeout = 1;
+	diepmsk.b.epdisabled = 1;
+	diepmsk.b.ahberr = 1;
+	diepmsk.b.intknepmis = 1;
+	dwc_write_reg32(&dev_if->dev_global_regs->diepmsk, diepmsk.d32);
+
+	/* Reset Device Address */
+	dcfg.d32 = dwc_read_reg32(&dev_if->dev_global_regs->dcfg);
+	dcfg.b.devaddr = 0;
+	dwc_write_reg32(&dev_if->dev_global_regs->dcfg, dcfg.d32);
+
+	/* setup EP0 to receive SETUP packets */
+	ep0_out_start(core_if, pcd);
+
+	/* Clear interrupt */
+	gintsts.d32 = 0;
+	gintsts.b.usbreset = 1;
+	dwc_write_reg32(&core_if->core_global_regs->gintsts, gintsts.d32);
+
+	return 1;
+}
+
+/**
+ * Get the device speed from the device status register and convert it
+ * to USB speed constant.
+ */
+static int get_device_speed(struct dwc_pcd *pcd)
+{
+	union dsts_data dsts;
+	enum usb_device_speed speed = USB_SPEED_UNKNOWN;
+
+	dsts.d32 = dwc_read_reg32(dev_sts_reg(pcd));
+
+	switch (dsts.b.enumspd) {
+	case DWC_DSTS_ENUMSPD_HS_PHY_30MHZ_OR_60MHZ:
+		speed = USB_SPEED_HIGH;
+		break;
+	case DWC_DSTS_ENUMSPD_FS_PHY_30MHZ_OR_60MHZ:
+	case DWC_DSTS_ENUMSPD_FS_PHY_48MHZ:
+		speed = USB_SPEED_FULL;
+		break;
+	case DWC_DSTS_ENUMSPD_LS_PHY_6MHZ:
+		speed = USB_SPEED_LOW;
+		break;
+	}
+	return speed;
+}
+
+/**
+ * This function enables EP0 OUT to receive SETUP packets and configures EP0
+ * IN for transmitting packets.  It is normally called when the "Enumeration
+ * Done" interrupt occurs.
+ */
+static void dwc_otg_ep0_activate(struct core_if *core_if, struct dwc_ep *ep)
+{
+	struct device_if *dev_if = core_if->dev_if;
+	union dsts_data dsts;
+	union depctl_data diepctl;
+	union depctl_data doepctl;
+	union dctl_data dctl = {.d32 = 0};
+
+	/* Read the Device Status and Endpoint 0 Control registers */
+	dsts.d32 = dwc_read_reg32(&dev_if->dev_global_regs->dsts);
+	diepctl.d32 = dwc_read_reg32(&dev_if->in_ep_regs[0]->diepctl);
+	doepctl.d32 = dwc_read_reg32(&dev_if->out_ep_regs[0]->doepctl);
+
+	/* Set the MPS of the IN EP based on the enumeration speed */
+	switch (dsts.b.enumspd) {
+	case DWC_DSTS_ENUMSPD_HS_PHY_30MHZ_OR_60MHZ:
+	case DWC_DSTS_ENUMSPD_FS_PHY_30MHZ_OR_60MHZ:
+	case DWC_DSTS_ENUMSPD_FS_PHY_48MHZ:
+		diepctl.b.mps = DWC_DEP0CTL_MPS_64;
+		break;
+	case DWC_DSTS_ENUMSPD_LS_PHY_6MHZ:
+		diepctl.b.mps = DWC_DEP0CTL_MPS_8;
+		break;
+	}
+	dwc_write_reg32(&dev_if->in_ep_regs[0]->diepctl, diepctl.d32);
+
+	/* Enable OUT EP for receive */
+	doepctl.b.epena = 1;
+	dwc_write_reg32(&dev_if->out_ep_regs[0]->doepctl, doepctl.d32);
+
+	dctl.b.cgnpinnak = 1;
+	dwc_modify_reg32(&dev_if->dev_global_regs->dctl, dctl.d32, dctl.d32);
+}
+
+/**
+ * Read the device status register and set the device speed in the
+ * data structure.
+ * Set up EP0 to receive SETUP packets by calling dwc_ep0_activate.
+ */
+static int dwc_otg_pcd_handle_enum_done_intr(struct dwc_pcd *pcd)
+{
+	struct pcd_ep *ep0 = &pcd->ep0;
+	union gintsts_data gintsts;
+	union gusbcfg_data gusbcfg;
+	struct core_if *core_if = GET_CORE_IF(pcd);
+	struct core_global_regs *global_regs = core_if->core_global_regs;
+	u32 gsnpsid = global_regs->gsnpsid;
+	u8 utmi16b, utmi8b;
+
+	if (gsnpsid >= (u32)0x4f54260a) {
+		utmi16b = 5;
+		utmi8b = 9;
+	} else {
+		utmi16b = 4;
+		utmi8b = 8;
+	}
+	dwc_otg_ep0_activate(GET_CORE_IF(pcd), &ep0->dwc_ep);
+
+	pcd->ep0state = EP0_IDLE;
+	ep0->stopped = 0;
+	pcd->gadget.speed = get_device_speed(pcd);
+
+	gusbcfg.d32 = dwc_read_reg32(&global_regs->gusbcfg);
+
+	/* Set USB turnaround time based on device speed and PHY interface. */
+	if (pcd->gadget.speed == USB_SPEED_HIGH) {
+		switch (core_if->hwcfg2.b.hs_phy_type) {
+		case DWC_HWCFG2_HS_PHY_TYPE_ULPI:
+			gusbcfg.b.usbtrdtim = 9;
+			break;
+		case DWC_HWCFG2_HS_PHY_TYPE_UTMI:
+			if (core_if->hwcfg4.b.utmi_phy_data_width == 0)
+				gusbcfg.b.usbtrdtim = utmi8b;
+			else if (core_if->hwcfg4.b.utmi_phy_data_width == 1)
+				gusbcfg.b.usbtrdtim = utmi16b;
+			else if (core_if->core_params->phy_utmi_width == 8)
+				gusbcfg.b.usbtrdtim = utmi8b;
+			else
+				gusbcfg.b.usbtrdtim = utmi16b;
+			break;
+		case DWC_HWCFG2_HS_PHY_TYPE_UTMI_ULPI:
+			if (gusbcfg.b.ulpi_utmi_sel == 1) {
+				gusbcfg.b.usbtrdtim = 9;
+			} else {
+				if (core_if->core_params->phy_utmi_width == 16)
+					gusbcfg.b.usbtrdtim = utmi16b;
+				else
+					gusbcfg.b.usbtrdtim = utmi8b;
+			}
+			break;
+		}
+	} else {
+		/* Full or low speed */
+		gusbcfg.b.usbtrdtim = 9;
+	}
+	dwc_write_reg32(&global_regs->gusbcfg, gusbcfg.d32);
+
+	/* Clear interrupt */
+	gintsts.d32 = 0;
+	gintsts.b.enumdone = 1;
+	dwc_write_reg32(&GET_CORE_IF(pcd)->core_global_regs->gintsts,
+			 gintsts.d32);
+
+	return 1;
+}
+
+/**
+ * This interrupt indicates that the ISO OUT Packet was dropped due to
+ * Rx FIFO full or Rx Status Queue Full.  If this interrupt occurs
+ * read all the data from the Rx FIFO.
+ */
+static int dwc_otg_pcd_handle_isoc_out_packet_dropped_intr(struct dwc_pcd *pcd)
+{
+	union gintmsk_data intr_mask = {.d32 = 0};
+	union gintsts_data gintsts;
+
+	printk(KERN_INFO "Interrupt Handler not implemented for ISOC Out "
+			"Dropped\n");
+
+	/* Turn off and clear the interrupt */
+	intr_mask.b.isooutdrop = 1;
+	dwc_modify_reg32(&GET_CORE_IF(pcd)->core_global_regs->gintmsk,
+				intr_mask.d32, 0);
+
+	gintsts.d32 = 0;
+	gintsts.b.isooutdrop = 1;
+	dwc_write_reg32(&GET_CORE_IF(pcd)->core_global_regs->gintsts,
+			 gintsts.d32);
+
+	return 1;
+}
+
+/**
+ * This interrupt indicates the end of the portion of the micro-frame
+ * for periodic transactions.  If there is a periodic transaction for
+ * the next frame, load the packets into the EP periodic Tx FIFO.
+ */
+static int dwc_otg_pcd_handle_end_periodic_frame_intr(struct dwc_pcd *pcd)
+{
+	union gintmsk_data intr_mask = {.d32 = 0};
+	union gintsts_data gintsts;
+
+	printk(KERN_INFO "Interrupt handler not implemented for End of "
+		"Periodic Portion of Micro-Frame Interrupt");
+
+	/* Turn off and clear the interrupt */
+	intr_mask.b.eopframe = 1;
+	dwc_modify_reg32(&GET_CORE_IF(pcd)->core_global_regs->gintmsk,
+				intr_mask.d32, 0);
+
+	gintsts.d32 = 0;
+	gintsts.b.eopframe = 1;
+	dwc_write_reg32(&GET_CORE_IF(pcd)->core_global_regs->gintsts,
+				gintsts.d32);
+
+	return 1;
+}
+
+/**
+ * This interrupt indicates that EP of the packet on the top of the
+ * non-periodic Tx FIFO does not match EP of the IN Token received.
+ *
+ * The "Device IN Token Queue" Registers are read to determine the
+ * order the IN Tokens have been received.  The non-periodic Tx FIFO is flushed,
+ * so it can be reloaded in the order seen in the IN Token Queue.
+ */
+static int dwc_otg_pcd_handle_ep_mismatch_intr(struct core_if *core_if)
+{
+	union gintmsk_data intr_mask = {.d32 = 0};
+	union gintsts_data gintsts;
+
+	printk(KERN_INFO "Interrupt handler not implemented for End Point "
+				"Mismatch\n");
+
+	/* Turn off and clear the interrupt */
+	intr_mask.b.epmismatch = 1;
+	dwc_modify_reg32(&core_if->core_global_regs->gintmsk,
+				intr_mask.d32, 0);
+
+	gintsts.d32 = 0;
+	gintsts.b.epmismatch = 1;
+	dwc_write_reg32(&core_if->core_global_regs->gintsts, gintsts.d32);
+	return 1;
+}
+
+/**
+ * This funcion stalls EP0.
+ */
+static void ep0_do_stall(struct dwc_pcd *pcd, const int val)
+{
+	struct pcd_ep *ep0 = &pcd->ep0;
+	struct usb_ctrlrequest *ctrl = &pcd->setup_pkt->req;
+
+	printk(KERN_WARNING "req %02x.%02x protocol STALL; err %d\n",
+		ctrl->bRequestType, ctrl->bRequest, val);
+
+	ep0->dwc_ep.is_in = 1;
+	dwc_otg_ep_set_stall(pcd->otg_dev->core_if, &ep0->dwc_ep);
+
+	pcd->ep0.stopped = 1;
+	pcd->ep0state = EP0_IDLE;
+	ep0_out_start(GET_CORE_IF(pcd), pcd);
+}
+
+/**
+ * This functions delegates the setup command to the gadget driver.
+ */
+static void do_gadget_setup(struct dwc_pcd *pcd,
+				   struct usb_ctrlrequest *ctrl)
+{
+	int ret = 0;
+
+	if (pcd->driver && pcd->driver->setup) {
+		spin_unlock(&pcd->lock);
+		ret = pcd->driver->setup(&pcd->gadget, ctrl);
+		spin_lock(&pcd->lock);
+
+		if (ret < 0)
+			ep0_do_stall(pcd, ret);
+
+		/** This is a g_file_storage gadget driver specific
+		 * workaround: a DELAYED_STATUS result from the fsg_setup
+		 * routine will result in the gadget queueing a EP0 IN status
+		 * phase for a two-stage control transfer.
+		 *
+		 * Exactly the same as a SET_CONFIGURATION/SET_INTERFACE except
+		 * that this is a class specific request.  Need a generic way to
+		 * know when the gadget driver will queue the status phase.
+		 *
+		 * Can we assume when we call the gadget driver setup() function
+		 * that it will always queue and require the following flag?
+		 * Need to look into this.
+		 */
+		if (ret == 256 + 999)
+			pcd->request_config = 1;
+	}
+}
+
+/**
+ * This function starts the Zero-Length Packet for the IN status phase
+ * of a 2 stage control transfer.
+ */
+static void do_setup_in_status_phase(struct dwc_pcd *pcd)
+{
+	struct pcd_ep *ep0 = &pcd->ep0;
+
+	if (pcd->ep0state == EP0_STALL)
+		return;
+
+	pcd->ep0state = EP0_STATUS;
+
+	ep0->dwc_ep.xfer_len = 0;
+	ep0->dwc_ep.xfer_count = 0;
+	ep0->dwc_ep.is_in = 1;
+	ep0->dwc_ep.dma_addr = pcd->setup_pkt_dma_handle;
+	dwc_otg_ep0_start_transfer(GET_CORE_IF(pcd), &ep0->dwc_ep);
+
+	/* Prepare for more SETUP Packets */
+	ep0_out_start(GET_CORE_IF(pcd), pcd);
+}
+
+/**
+ * This function starts the Zero-Length Packet for the OUT status phase
+ * of a 2 stage control transfer.
+ */
+static void do_setup_out_status_phase(struct dwc_pcd *pcd)
+{
+	struct pcd_ep *ep0 = &pcd->ep0;
+
+	if (pcd->ep0state == EP0_STALL)
+		return;
+	pcd->ep0state = EP0_STATUS;
+
+	ep0->dwc_ep.xfer_len = 0;
+	ep0->dwc_ep.xfer_count = 0;
+	ep0->dwc_ep.is_in = 0;
+	ep0->dwc_ep.dma_addr = pcd->setup_pkt_dma_handle;
+	dwc_otg_ep0_start_transfer(GET_CORE_IF(pcd), &ep0->dwc_ep);
+
+	/* Prepare for more SETUP Packets */
+	ep0_out_start(GET_CORE_IF(pcd), pcd);
+}
+
+/**
+ * Clear the EP halt (STALL) and if pending requests start the
+ * transfer.
+ */
+static void pcd_clear_halt(struct dwc_pcd *pcd, struct pcd_ep *ep)
+{
+	struct core_if *core_if = GET_CORE_IF(pcd);
+
+	if (!ep->dwc_ep.stall_clear_flag)
+		dwc_otg_ep_clear_stall(core_if, &ep->dwc_ep);
+
+	/* Reactive the EP */
+	dwc_otg_ep_activate(core_if, &ep->dwc_ep);
+
+	if (ep->stopped) {
+		ep->stopped = 0;
+		/* If there is a request in the EP queue start it */
+
+		/*
+		 * start_next_request(), outside of interrupt context at some
+		 * time after the current time, after a clear-halt setup packet.
+		 * Still need to implement ep mismatch in the future if a gadget
+		 * ever uses more than one endpoint at once
+		 */
+		if (core_if->dma_enable) {
+			ep->queue_sof = 1;
+			tasklet_schedule(pcd->start_xfer_tasklet);
+		} else {
+			/*
+			 * Added-sr: 2007-07-26
+			 *
+			 * To re-enable this endpoint it's important to
+			 * set this next_ep number. Otherwise the endpoint
+			 * will not get active again after stalling.
+			 */
+			if (dwc_has_feature(core_if, DWC_LIMITED_XFER))
+				start_next_request(ep);
+		}
+	}
+
+	/* Start Control Status Phase */
+	do_setup_in_status_phase(pcd);
+}
+
+/**
+ * This function is called when the SET_FEATURE TEST_MODE Setup packet is sent
+ * from the host.  The Device Control register is written with the Test Mode
+ * bits set to the specified Test Mode.  This is done as a tasklet so that the
+ * "Status" phase of the control transfer completes before transmitting the TEST
+ * packets.
+ *
+ */
+static void do_test_mode(unsigned long data)
+{
+	union dctl_data dctl;
+	struct dwc_pcd *pcd = (struct dwc_pcd *) data;
+	int test_mode = pcd->test_mode;
+
+	dctl.d32 = dwc_read_reg32(dev_ctl_reg(pcd));
+	switch (test_mode) {
+	case 1:		/* TEST_J */
+		dctl.b.tstctl = 1;
+		break;
+	case 2:		/* TEST_K */
+		dctl.b.tstctl = 2;
+		break;
+	case 3:		/* TEST_SE0_NAK */
+		dctl.b.tstctl = 3;
+		break;
+	case 4:		/* TEST_PACKET */
+		dctl.b.tstctl = 4;
+		break;
+	case 5:		/* TEST_FORCE_ENABLE */
+		dctl.b.tstctl = 5;
+		break;
+	}
+	dwc_write_reg32(dev_ctl_reg(pcd), dctl.d32);
+}
+
+/**
+ * This function process the SET_FEATURE Setup Commands.
+ */
+static void do_set_feature(struct dwc_pcd *pcd)
+{
+	struct core_if *core_if = GET_CORE_IF(pcd);
+	struct core_global_regs *regs = core_if->core_global_regs;
+	struct usb_ctrlrequest ctrl = pcd->setup_pkt->req;
+	struct pcd_ep *ep = NULL;
+	int otg_cap = core_if->core_params->otg_cap;
+	union gotgctl_data gotgctl = {.d32 = 0};
+
+	switch (ctrl.bRequestType & USB_RECIP_MASK) {
+	case USB_RECIP_DEVICE:
+		switch (__le16_to_cpu(ctrl.wValue)) {
+		case USB_DEVICE_REMOTE_WAKEUP:
+			pcd->remote_wakeup_enable = 1;
+			break;
+		case USB_DEVICE_TEST_MODE:
+			/*
+			 * Setup the Test Mode tasklet to do the Test
+			 * Packet generation after the SETUP Status
+			 * phase has completed.
+			 */
+
+			pcd->test_mode_tasklet.next = 0;
+			pcd->test_mode_tasklet.state = 0;
+			atomic_set(&pcd->test_mode_tasklet.count, 0);
+
+			pcd->test_mode_tasklet.func = do_test_mode;
+			pcd->test_mode_tasklet.data = (unsigned long)pcd;
+			pcd->test_mode = __le16_to_cpu(ctrl.wIndex) >> 8;
+			tasklet_schedule(&pcd->test_mode_tasklet);
+
+			break;
+		case USB_DEVICE_B_HNP_ENABLE:
+			/* dev may initiate HNP */
+			if (otg_cap == DWC_OTG_CAP_PARAM_HNP_SRP_CAPABLE) {
+				pcd->b_hnp_enable = 1;
+				dwc_otg_pcd_update_otg(pcd, 0);
+				/*
+				 * gotgctl.devhnpen cleared by a
+				 * USB Reset?
+				 */
+				gotgctl.b.devhnpen = 1;
+				gotgctl.b.hnpreq = 1;
+				dwc_write_reg32(&regs->gotgctl, gotgctl.d32);
+			} else {
+				ep0_do_stall(pcd, -EOPNOTSUPP);
+			}
+			break;
+		case USB_DEVICE_A_HNP_SUPPORT:
+			/* RH port supports HNP */
+			if (otg_cap == DWC_OTG_CAP_PARAM_HNP_SRP_CAPABLE) {
+				pcd->a_hnp_support = 1;
+				dwc_otg_pcd_update_otg(pcd, 0);
+			} else {
+				ep0_do_stall(pcd, -EOPNOTSUPP);
+			}
+			break;
+		case USB_DEVICE_A_ALT_HNP_SUPPORT:
+			/* other RH port does */
+			if (otg_cap == DWC_OTG_CAP_PARAM_HNP_SRP_CAPABLE) {
+				pcd->a_alt_hnp_support = 1;
+				dwc_otg_pcd_update_otg(pcd, 0);
+			} else {
+				ep0_do_stall(pcd, -EOPNOTSUPP);
+			}
+			break;
+		}
+		do_setup_in_status_phase(pcd);
+		break;
+	case USB_RECIP_INTERFACE:
+		do_gadget_setup(pcd, &ctrl);
+		break;
+	case USB_RECIP_ENDPOINT:
+		if (__le16_to_cpu(ctrl.wValue) == USB_ENDPOINT_HALT) {
+			ep = get_ep_by_addr(pcd, __le16_to_cpu(ctrl.wIndex));
+
+			if (ep == 0) {
+				ep0_do_stall(pcd, -EOPNOTSUPP);
+				return;
+			}
+
+			ep->stopped = 1;
+			dwc_otg_ep_set_stall(core_if, &ep->dwc_ep);
+		}
+		do_setup_in_status_phase(pcd);
+		break;
+	}
+}
+
+/**
+ * This function process the CLEAR_FEATURE Setup Commands.
+ */
+static void do_clear_feature(struct dwc_pcd *pcd)
+{
+	struct usb_ctrlrequest ctrl = pcd->setup_pkt->req;
+	struct pcd_ep *ep = NULL;
+
+	switch (ctrl.bRequestType & USB_RECIP_MASK) {
+	case USB_RECIP_DEVICE:
+		switch (__le16_to_cpu(ctrl.wValue)) {
+		case USB_DEVICE_REMOTE_WAKEUP:
+			pcd->remote_wakeup_enable = 0;
+			break;
+		case USB_DEVICE_TEST_MODE:
+			/* Add CLEAR_FEATURE for TEST modes. */
+			break;
+		}
+		do_setup_in_status_phase(pcd);
+		break;
+	case USB_RECIP_ENDPOINT:
+		ep = get_ep_by_addr(pcd, __le16_to_cpu(ctrl.wIndex));
+		if (ep == 0) {
+			ep0_do_stall(pcd, -EOPNOTSUPP);
+			return;
+		}
+
+		pcd_clear_halt(pcd, ep);
+		break;
+	}
+}
+
+/**
+ * This function processes SETUP commands.  In Linux, the USB Command processing
+ * is done in two places - the first being the PCD and the second in the Gadget
+ * Driver (for example, the File-Backed Storage Gadget Driver).
+ *
+ * GET_STATUS: Command is processed as defined in chapter 9 of the USB 2.0
+ * Specification chapter 9
+ *
+ * CLEAR_FEATURE: The Device and Endpoint requests are the ENDPOINT_HALT feature
+ * is procesed, all others the interface requests are ignored.
+ *
+ * SET_FEATURE: The Device and Endpoint requests are processed by the PCD.
+ * Interface requests are passed to the Gadget Driver.
+ *
+ * SET_ADDRESS: PCD, Program the DCFG reg, with device address received
+ *
+ * GET_DESCRIPTOR: Gadget Driver, Return the requested descriptor
+ *
+ * SET_DESCRIPTOR: Gadget Driver, Optional - not implemented by any of the
+ * existing Gadget Drivers.
+ *
+ * SET_CONFIGURATION: Gadget Driver, Disable all EPs and enable EPs for new
+ * configuration.
+ *
+ * GET_CONFIGURATION: Gadget Driver, Return the current configuration
+ *
+ * SET_INTERFACE: Gadget Driver, Disable all EPs and enable EPs for new
+ * configuration.
+ *
+ * GET_INTERFACE: Gadget Driver, Return the current interface.
+ *
+ * SYNC_FRAME:  Display debug message.
+ *
+ * When the SETUP Phase Done interrupt occurs, the PCD SETUP commands are
+ * processed by pcd_setup. Calling the Function Driver's setup function from
+ * pcd_setup processes the gadget SETUP commands.
+ */
+static void pcd_setup(struct dwc_pcd *pcd)
+{
+	struct core_if *core_if = GET_CORE_IF(pcd);
+	struct device_if *dev_if = core_if->dev_if;
+	struct usb_ctrlrequest ctrl = pcd->setup_pkt->req;
+	struct pcd_ep *ep;
+	struct pcd_ep *ep0 = &pcd->ep0;
+	u16 *status = pcd->status_buf;
+	union deptsiz0_data doeptsize0 = {.d32 = 0};
+
+	doeptsize0.d32 = dwc_read_reg32(&dev_if->out_ep_regs[0]->doeptsiz);
+
+	/* handle > 1 setup packet , assert error for now */
+	if (core_if->dma_enable && (doeptsize0.b.supcnt < 2))
+		printk(KERN_ERR "\n\n	 CANNOT handle > 1 setup packet in "
+				"DMA mode\n\n");
+
+	/* Clean up the request queue */
+	request_nuke(ep0);
+	ep0->stopped = 0;
+
+	if (ctrl.bRequestType & USB_DIR_IN) {
+		ep0->dwc_ep.is_in = 1;
+		pcd->ep0state = EP0_IN_DATA_PHASE;
+	} else {
+		ep0->dwc_ep.is_in = 0;
+		pcd->ep0state = EP0_OUT_DATA_PHASE;
+	}
+
+	if ((ctrl.bRequestType & USB_TYPE_MASK) != USB_TYPE_STANDARD) {
+		/*
+		 * Handle non-standard (class/vendor) requests in the gadget
+		 * driver
+		 */
+		do_gadget_setup(pcd, &ctrl);
+		return;
+	}
+
+	switch (ctrl.bRequest) {
+	case USB_REQ_GET_STATUS:
+		switch (ctrl.bRequestType & USB_RECIP_MASK) {
+		case USB_RECIP_DEVICE:
+			*status = 0x1;		/* Self powered */
+			*status |= pcd->remote_wakeup_enable << 1;
+			break;
+		case USB_RECIP_INTERFACE:
+			*status = 0;
+			break;
+		case USB_RECIP_ENDPOINT:
+			ep = get_ep_by_addr(pcd, __le16_to_cpu(ctrl.wIndex));
+			if (ep == 0 || __le16_to_cpu(ctrl.wLength) > 2) {
+				ep0_do_stall(pcd, -EOPNOTSUPP);
+				return;
+			}
+			*status = ep->stopped;
+			break;
+		}
+
+		*status = __cpu_to_le16(*status);
+
+		pcd->ep0_pending = 1;
+		ep0->dwc_ep.start_xfer_buff = (u8 *) status;
+		ep0->dwc_ep.xfer_buff = (u8 *) status;
+		ep0->dwc_ep.dma_addr = pcd->status_buf_dma_handle;
+		ep0->dwc_ep.xfer_len = 2;
+		ep0->dwc_ep.xfer_count = 0;
+		ep0->dwc_ep.total_len = ep0->dwc_ep.xfer_len;
+		dwc_otg_ep0_start_transfer(GET_CORE_IF(pcd), &ep0->dwc_ep);
+		break;
+	case USB_REQ_CLEAR_FEATURE:
+		do_clear_feature(pcd);
+		break;
+	case USB_REQ_SET_FEATURE:
+		do_set_feature(pcd);
+		break;
+	case USB_REQ_SET_ADDRESS:
+		if (ctrl.bRequestType == USB_RECIP_DEVICE) {
+			union dcfg_data dcfg = {.d32 = 0};
+
+			dcfg.b.devaddr = __le16_to_cpu(ctrl.wValue);
+			dwc_modify_reg32(&dev_if->dev_global_regs->dcfg, 0,
+						dcfg.d32);
+			do_setup_in_status_phase(pcd);
+			return;
+		}
+		break;
+	case USB_REQ_SET_INTERFACE:
+	case USB_REQ_SET_CONFIGURATION:
+		pcd->request_config = 1;	/* Configuration changed */
+		do_gadget_setup(pcd, &ctrl);
+		break;
+	case USB_REQ_SYNCH_FRAME:
+		do_gadget_setup(pcd, &ctrl);
+		break;
+	default:
+		/* Call the Gadget Driver's setup functions */
+		do_gadget_setup(pcd, &ctrl);
+		break;
+	}
+}
+
+/**
+ * This function completes the ep0 control transfer.
+ */
+static int ep0_complete_request(struct pcd_ep *ep)
+{
+	struct core_if *core_if = GET_CORE_IF(ep->pcd);
+	struct device_if *dev_if = core_if->dev_if;
+	struct device_in_ep_regs *in_regs = dev_if->in_ep_regs[ep->dwc_ep.num];
+	union deptsiz0_data deptsiz;
+	struct pcd_request *req;
+	int is_last = 0;
+	struct dwc_pcd *pcd = ep->pcd;
+
+	if (pcd->ep0_pending && list_empty(&ep->queue)) {
+		if (ep->dwc_ep.is_in)
+			do_setup_out_status_phase(pcd);
+		else
+			do_setup_in_status_phase(pcd);
+
+		pcd->ep0_pending = 0;
+		pcd->ep0state = EP0_STATUS;
+		return 1;
+	}
+
+	if (list_empty(&ep->queue))
+		return 0;
+
+	req = list_entry(ep->queue.next, struct pcd_request, queue);
+
+	if (pcd->ep0state == EP0_STATUS) {
+		is_last = 1;
+	} else if (ep->dwc_ep.is_in) {
+		deptsiz.d32 = dwc_read_reg32(&in_regs->dieptsiz);
+
+		if (deptsiz.b.xfersize == 0) {
+			req->req.actual = ep->dwc_ep.xfer_count;
+			do_setup_out_status_phase(pcd);
+		}
+	} else {
+		/* This is ep0-OUT */
+		req->req.actual = ep->dwc_ep.xfer_count;
+		do_setup_in_status_phase(pcd);
+	}
+
+	/* Complete the request */
+	if (is_last) {
+		request_done(ep, req, 0);
+		ep->dwc_ep.start_xfer_buff = 0;
+		ep->dwc_ep.xfer_buff = 0;
+		ep->dwc_ep.xfer_len = 0;
+		return 1;
+	}
+	return 0;
+}
+
+/**
+ * This function completes the request for the EP.  If there are additional
+ * requests for the EP in the queue they will be started.
+ */
+static void complete_ep(struct pcd_ep *ep)
+{
+	struct core_if *core_if = GET_CORE_IF(ep->pcd);
+	struct device_if *dev_if = core_if->dev_if;
+	struct device_in_ep_regs *in_ep_regs =
+		dev_if->in_ep_regs[ep->dwc_ep.num];
+	union deptsiz_data deptsiz;
+	struct pcd_request *req = NULL;
+	int is_last = 0;
+
+	/* Get any pending requests */
+	if (!list_empty(&ep->queue))
+		req = list_entry(ep->queue.next, struct pcd_request, queue);
+
+	if (ep->dwc_ep.is_in) {
+		deptsiz.d32 = dwc_read_reg32(&in_ep_regs->dieptsiz);
+
+		if (core_if->dma_enable  && !deptsiz.b.xfersize)
+			ep->dwc_ep.xfer_count = ep->dwc_ep.xfer_len;
+
+		if (deptsiz.b.xfersize == 0 && deptsiz.b.pktcnt == 0 &&
+				ep->dwc_ep.xfer_count == ep->dwc_ep.xfer_len)
+			is_last = 1;
+		else
+			printk(KERN_WARNING "Incomplete transfer (%s-%s "
+				"[siz=%d pkt=%d])\n", ep->ep.name,
+				ep->dwc_ep.is_in ? "IN" : "OUT",
+				deptsiz.b.xfersize, deptsiz.b.pktcnt);
+	} else {
+		struct device_out_ep_regs *out_ep_regs =
+		    dev_if->out_ep_regs[ep->dwc_ep.num];
+		deptsiz.d32 = dwc_read_reg32(&out_ep_regs->doeptsiz);
+		is_last = 1;
+	}
+
+	/* Complete the request */
+	if (is_last) {
+		/*
+		 * Added-sr: 2007-07-26
+		 *
+		 * Since the 405EZ (Ultra) only support 2047 bytes as
+		 * max transfer size, we have to split up bigger transfers
+		 * into multiple transfers of 1024 bytes sized messages.
+		 * I happens often, that transfers of 4096 bytes are
+		 * required (zero-gadget, file_storage-gadget).
+		 */
+		if ((dwc_has_feature(core_if, DWC_LIMITED_XFER)) &&
+				ep->dwc_ep.bytes_pending) {
+			struct device_in_ep_regs *in_regs =
+				core_if->dev_if->in_ep_regs[ep->dwc_ep.num];
+			union gintmsk_data intr_mask = { .d32 = 0};
+
+			ep->dwc_ep.xfer_len = ep->dwc_ep.bytes_pending;
+			if (ep->dwc_ep.xfer_len > MAX_XFER_LEN) {
+				ep->dwc_ep.bytes_pending = ep->dwc_ep.xfer_len -
+					MAX_XFER_LEN;
+				ep->dwc_ep.xfer_len = MAX_XFER_LEN;
+			} else {
+				ep->dwc_ep.bytes_pending = 0;
+			}
+
+			/*
+			 * Restart the current transfer with the next "chunk"
+			 * of data.
+			 */
+			ep->dwc_ep.xfer_count = 0;
+
+			deptsiz.d32 = dwc_read_reg32(&(in_regs->dieptsiz));
+			deptsiz.b.xfersize = ep->dwc_ep.xfer_len;
+			deptsiz.b.pktcnt = (ep->dwc_ep.xfer_len - 1 +
+				ep->dwc_ep.maxpacket) / ep->dwc_ep.maxpacket;
+			dwc_write_reg32(&in_regs->dieptsiz, deptsiz.d32);
+
+			intr_mask.b.nptxfempty = 1;
+			dwc_modify_reg32(&core_if->core_global_regs->gintsts,
+						intr_mask.d32, 0);
+			dwc_modify_reg32(&core_if->core_global_regs->gintmsk,
+						intr_mask.d32, intr_mask.d32);
+
+			/*
+			 * Just return here if message was not completely
+			 * transferred.
+			 */
+			return;
+		}
+		if (core_if->dma_enable)
+			req->req.actual = ep->dwc_ep.xfer_len -
+				deptsiz.b.xfersize;
+		else
+			req->req.actual = ep->dwc_ep.xfer_count;
+
+		request_done(ep, req, 0);
+		ep->dwc_ep.start_xfer_buff = 0;
+		ep->dwc_ep.xfer_buff = 0;
+		ep->dwc_ep.xfer_len = 0;
+
+		/* If there is a request in the queue start it. */
+		start_next_request(ep);
+	}
+}
+
+/**
+ * This function continues control IN transfers started by
+ * dwc_otg_ep0_start_transfer, when the transfer does not fit in a
+ * single packet.  NOTE: The DIEPCTL0/DOEPCTL0 registers only have one
+ * bit for the packet count.
+ */
+static void dwc_otg_ep0_continue_transfer(struct core_if *c_if,
+	struct dwc_ep *ep)
+{
+	union depctl_data depctl;
+	union deptsiz0_data deptsiz;
+	union gintmsk_data intr_mask = {.d32 = 0};
+	struct device_if *d_if = c_if->dev_if;
+	struct core_global_regs *glbl_regs = c_if->core_global_regs;
+
+	if (ep->is_in) {
+		struct device_in_ep_regs *in_regs = d_if->in_ep_regs[0];
+		union gnptxsts_data tx_status = {.d32 = 0};
+
+		tx_status.d32 = dwc_read_reg32(&glbl_regs->gnptxsts);
+
+		depctl.d32 = dwc_read_reg32(&in_regs->diepctl);
+		deptsiz.d32 = dwc_read_reg32(&in_regs->dieptsiz);
+
+		/*
+		 * Program the transfer size and packet count as follows:
+		 *   xfersize = N * maxpacket + short_packet
+		 *   pktcnt = N + (short_packet exist ? 1 : 0)
+		 */
+		if (ep->total_len - ep->xfer_count > ep->maxpacket)
+			deptsiz.b.xfersize = ep->maxpacket;
+		else
+			deptsiz.b.xfersize = ep->total_len - ep->xfer_count;
+
+		deptsiz.b.pktcnt = 1;
+		ep->xfer_len += deptsiz.b.xfersize;
+		dwc_write_reg32(&in_regs->dieptsiz, deptsiz.d32);
+
+		/* Write the DMA register */
+		if (c_if->hwcfg2.b.architecture == DWC_INT_DMA_ARCH)
+			dwc_write_reg32(&in_regs->diepdma, ep->dma_addr);
+
+		/* EP enable, IN data in FIFO */
+		depctl.b.cnak = 1;
+		depctl.b.epena = 1;
+		dwc_write_reg32(&in_regs->diepctl, depctl.d32);
+
+		/*
+		 * Enable the Non-Periodic Tx FIFO empty interrupt, the
+		 * data will be written into the fifo by the ISR.
+		 */
+		if (!c_if->dma_enable) {
+			/* First clear it from GINTSTS */
+			intr_mask.b.nptxfempty = 1;
+			dwc_write_reg32(&glbl_regs->gintsts, intr_mask.d32);
+
+			/* To avoid spurious NPTxFEmp intr */
+			dwc_modify_reg32(&glbl_regs->gintmsk, intr_mask.d32, 0);
+		}
+	}
+}
+
+/**
+ * This function handles EP0 Control transfers.
+ *
+ * The state of the control tranfers are tracked in ep0state
+ */
+static void handle_ep0(struct dwc_pcd *pcd)
+{
+	struct core_if *core_if = GET_CORE_IF(pcd);
+	struct pcd_ep *ep0 = &pcd->ep0;
+
+	switch (pcd->ep0state) {
+	case EP0_DISCONNECT:
+		break;
+	case EP0_IDLE:
+		pcd->request_config = 0;
+		pcd_setup(pcd);
+		break;
+	case EP0_IN_DATA_PHASE:
+		if (core_if->dma_enable)
+			/*
+			 * For EP0 we can only program 1 packet at a time so we
+			 * need to do the calculations after each complete.
+			 * Call write_packet to make the calculations, as in
+			 * slave mode, and use those values to determine if we
+			 * can complete.
+			 */
+			dwc_otg_ep_write_packet(core_if, &ep0->dwc_ep, 1);
+		else
+			dwc_otg_ep_write_packet(core_if, &ep0->dwc_ep, 0);
+
+		if (ep0->dwc_ep.xfer_count < ep0->dwc_ep.total_len)
+			dwc_otg_ep0_continue_transfer(core_if, &ep0->dwc_ep);
+		else
+			ep0_complete_request(ep0);
+		break;
+	case EP0_OUT_DATA_PHASE:
+		ep0_complete_request(ep0);
+		break;
+	case EP0_STATUS:
+		ep0_complete_request(ep0);
+		pcd->ep0state = EP0_IDLE;
+		ep0->stopped = 1;
+		ep0->dwc_ep.is_in = 0;		/* OUT for next SETUP */
+
+		/* Prepare for more SETUP Packets */
+		if (core_if->dma_enable) {
+			ep0_out_start(core_if, pcd);
+		} else {
+			int i;
+			union depctl_data diepctl;
+
+			diepctl.d32 = dwc_read_reg32(in_ep_ctl_reg(pcd, 0));
+			if (pcd->ep0.queue_sof) {
+				pcd->ep0.queue_sof = 0;
+				start_next_request(&pcd->ep0);
+			}
+
+			diepctl.d32 = dwc_read_reg32(in_ep_ctl_reg(pcd, 0));
+			if (pcd->ep0.queue_sof) {
+				pcd->ep0.queue_sof = 0;
+				start_next_request(&pcd->ep0);
+			}
+
+			for (i = 0; i < core_if->dev_if->num_in_eps; i++) {
+				diepctl.d32 =
+					dwc_read_reg32(in_ep_ctl_reg(pcd, i));
+
+				if (pcd->in_ep[i].queue_sof) {
+					pcd->in_ep[i].queue_sof = 0;
+					start_next_request(&pcd->in_ep[i]);
+				}
+			}
+		}
+		break;
+	case EP0_STALL:
+		printk(KERN_ERR "EP0 STALLed, should not get here "
+				"handle_ep0()\n");
+		break;
+	}
+}
+
+/**
+ * Restart transfer
+ */
+static void restart_transfer(struct dwc_pcd *pcd, const u32 ep_num)
+{
+	struct core_if *core_if = GET_CORE_IF(pcd);
+	struct device_if *dev_if = core_if->dev_if;
+	union deptsiz_data dieptsiz = {.d32 = 0};
+	struct pcd_ep *ep;
+
+	dieptsiz.d32 = dwc_read_reg32(&dev_if->in_ep_regs[ep_num]->dieptsiz);
+	ep = get_in_ep(pcd, ep_num);
+
+	/*
+	 * If pktcnt is not 0, and xfersize is 0, and there is a buffer,
+	 * resend the last packet.
+	 */
+	if (dieptsiz.b.pktcnt && !dieptsiz.b.xfersize &&
+			ep->dwc_ep.start_xfer_buff) {
+		if (ep->dwc_ep.xfer_len <= ep->dwc_ep.maxpacket) {
+			ep->dwc_ep.xfer_count = 0;
+			ep->dwc_ep.xfer_buff = ep->dwc_ep.start_xfer_buff;
+		} else {
+			ep->dwc_ep.xfer_count -= ep->dwc_ep.maxpacket;
+
+			/* convert packet size to dwords. */
+			ep->dwc_ep.xfer_buff -= ep->dwc_ep.maxpacket;
+		}
+		ep->stopped = 0;
+
+		if (!ep_num)
+			dwc_otg_ep0_start_transfer(core_if, &ep->dwc_ep);
+		else
+			dwc_otg_ep_start_transfer(core_if, &ep->dwc_ep);
+	}
+}
+
+/**
+ * Handle the IN EP Transfer Complete interrupt.
+ *
+ * If dedicated fifos are enabled, then the Tx FIFO empty interrupt for the EP
+ * is disabled.  Otherwise the NP Tx FIFO empty interrupt is  disabled.
+ */
+static void handle_in_ep_xfr_complete_intr(struct dwc_pcd *pcd,
+			struct pcd_ep *ep, u32 num)
+{
+	struct core_if *c_if = GET_CORE_IF(pcd);
+	struct device_if *d_if = c_if->dev_if;
+	struct dwc_ep *dwc_ep = &ep->dwc_ep;
+	union diepint_data epint = {.d32 = 0};
+
+	if (c_if->en_multiple_tx_fifo) {
+		u32 fifoemptymsk = 0x1 << dwc_ep->num;
+		dwc_modify_reg32(&d_if->dev_global_regs->dtknqr4_fifoemptymsk,
+					fifoemptymsk, 0);
+	} else {
+		union gintmsk_data intr_mask = {.d32 = 0};
+		intr_mask.b.nptxfempty = 1;
+		dwc_modify_reg32(&c_if->core_global_regs->gintmsk,
+					intr_mask.d32, 0);
+	}
+
+	/* Clear the interrupt, then complete the transfer */
+	epint.b.xfercompl = 1;
+	dwc_write_reg32(&d_if->in_ep_regs[num]->diepint, epint.d32);
+
+	if (!num)
+		handle_ep0(pcd);
+	else
+		complete_ep(ep);
+}
+
+/**
+ * Handle the IN EP disable interrupt.
+ */
+static void handle_in_ep_disable_intr(struct dwc_pcd *pcd,
+				const u32 ep_num)
+{
+	struct core_if *core_if = GET_CORE_IF(pcd);
+	struct device_if *dev_if = core_if->dev_if;
+	union deptsiz_data dieptsiz = {.d32 = 0};
+	union dctl_data dctl = {.d32 = 0};
+	struct pcd_ep *ep;
+	struct dwc_ep *dwc_ep;
+	union diepint_data diepint = {.d32 = 0};
+
+	ep = get_in_ep(pcd, ep_num);
+	dwc_ep = &ep->dwc_ep;
+
+	dieptsiz.d32 = dwc_read_reg32(&dev_if->in_ep_regs[ep_num]->dieptsiz);
+
+	if (ep->stopped) {
+		/* Flush the Tx FIFO */
+		dwc_otg_flush_tx_fifo(core_if, dwc_ep->tx_fifo_num);
+
+		/* Clear the Global IN NP NAK */
+		dctl.d32 = 0;
+		dctl.b.cgnpinnak = 1;
+		dwc_modify_reg32(dev_ctl_reg(pcd), dctl.d32, 0);
+
+		if (dieptsiz.b.pktcnt || dieptsiz.b.xfersize)
+			restart_transfer(pcd, ep_num);
+	} else {
+		if (dieptsiz.b.pktcnt || dieptsiz.b.xfersize)
+			restart_transfer(pcd, ep_num);
+	}
+	/* Clear epdisabled */
+	diepint.b.epdisabled = 1;
+	dwc_write_reg32(in_ep_int_reg(pcd, ep_num), diepint.d32);
+
+}
+
+/**
+ * Handler for the IN EP timeout handshake interrupt.
+ */
+static void handle_in_ep_timeout_intr(struct dwc_pcd *pcd, const u32 ep_num)
+{
+	struct core_if *core_if = GET_CORE_IF(pcd);
+	struct pcd_ep *ep;
+	union dctl_data dctl = {.d32 = 0};
+	union gintmsk_data intr_mask = {.d32 = 0};
+	union diepint_data diepint = {.d32 = 0};
+
+	ep = get_in_ep(pcd, ep_num);
+
+	/* Disable the NP Tx Fifo Empty Interrrupt */
+	if (!core_if->dma_enable) {
+		intr_mask.b.nptxfempty = 1;
+		dwc_modify_reg32(&core_if->core_global_regs->gintmsk,
+					intr_mask.d32, 0);
+	}
+
+	/* Non-periodic EP */
+	/* Enable the Global IN NAK Effective Interrupt */
+	intr_mask.b.ginnakeff = 1;
+	dwc_modify_reg32(&core_if->core_global_regs->gintmsk, 0, intr_mask.d32);
+
+	/* Set Global IN NAK */
+	dctl.b.sgnpinnak = 1;
+	dwc_modify_reg32(dev_ctl_reg(pcd), dctl.d32, dctl.d32);
+	ep->stopped = 1;
+
+	/* Clear timeout */
+    diepint.b.timeout = 1;
+    dwc_write_reg32(in_ep_int_reg(pcd, ep_num), diepint.d32);
+}
+
+/**
+ * Handles the IN Token received with TxF Empty interrupt.
+ *
+ * For the 405EZ, only start the next transfer, when currently no other transfer
+ * is active on this endpoint.
+ *
+ * Note that the bits in the Device IN endpoint mask register are laid out
+ * exactly the same as the Device IN endpoint interrupt register.
+ */
+static void handle_in_ep_tx_fifo_empty_intr(struct dwc_pcd *pcd,
+			struct pcd_ep *ep, u32 num)
+{
+	union diepint_data diepint = {.d32 = 0};
+
+	if (!ep->stopped && num) {
+		union diepint_data diepmsk = {.d32 = 0};
+		diepmsk.b.intktxfemp = 1;
+		dwc_modify_reg32(dev_diepmsk_reg(pcd), diepmsk.d32, 0);
+
+		if (dwc_has_feature(GET_CORE_IF(pcd), DWC_LIMITED_XFER)) {
+			if (!ep->dwc_ep.active)
+				start_next_request(ep);
+		} else {
+			start_next_request(ep);
+		}
+	}
+	/* Clear intktxfemp */
+    diepint.b.intktxfemp = 1;
+	dwc_write_reg32(in_ep_int_reg(pcd, num), diepint.d32);
+}
+
+static void handle_in_ep_nak_effective_intr(struct dwc_pcd *pcd,
+			struct pcd_ep *ep, u32 num)
+{
+	union depctl_data diepctl = {.d32 = 0};
+	union diepint_data diepint = {.d32 = 0};
+
+	/* Periodic EP */
+	if (ep->disabling) {
+		diepctl.d32 = 0;
+		diepctl.b.snak = 1;
+		diepctl.b.epdis = 1;
+		dwc_modify_reg32(in_ep_ctl_reg(pcd, num), diepctl.d32,
+					diepctl.d32);
+	}
+	/* Clear inepnakeff */
+	diepint.b.inepnakeff = 1;
+	dwc_write_reg32(in_ep_int_reg(pcd, num), diepint.d32);
+
+}
+
+/**
+ * This function returns the Device IN EP Interrupt register
+ */
+static inline u32 dwc_otg_read_diep_intr(struct core_if *core_if,
+						struct dwc_ep *ep)
+{
+	struct device_if *dev_if = core_if->dev_if;
+	u32 v, msk, emp;
+	msk = dwc_read_reg32(&dev_if->dev_global_regs->diepmsk);
+	emp = dwc_read_reg32(&dev_if->dev_global_regs->dtknqr4_fifoemptymsk);
+	msk |= ((emp >> ep->num) & 0x1) << 7;
+	v = dwc_read_reg32(&dev_if->in_ep_regs[ep->num]->diepint) & msk;
+	return v;
+}
+
+/**
+ * This function reads the Device All Endpoints Interrupt register and
+ * returns the IN endpoint interrupt bits.
+ */
+static inline u32 dwc_otg_read_dev_all_in_ep_intr(struct core_if *_if)
+{
+	u32 v;
+	v = dwc_read_reg32(&_if->dev_if->dev_global_regs->daint) &
+		dwc_read_reg32(&_if->dev_if->dev_global_regs->daintmsk);
+	return v & 0xffff;
+}
+
+/**
+ * This interrupt indicates that an IN EP has a pending Interrupt.
+ * The sequence for handling the IN EP interrupt is shown below:
+ *
+ * - Read the Device All Endpoint Interrupt register
+ * - Repeat the following for each IN EP interrupt bit set (from LSB to MSB).
+ *
+ * - Read the Device Endpoint Interrupt (DIEPINTn) register
+ * - If "Transfer Complete" call the request complete function
+ * - If "Endpoint Disabled" complete the EP disable procedure.
+ * - If "AHB Error Interrupt" log error
+ * - If "Time-out Handshake" log error
+ * - If "IN Token Received when TxFIFO Empty" write packet to Tx FIFO.
+ * - If "IN Token EP Mismatch" (disable, this is handled by EP Mismatch
+ *   Interrupt)
+ */
+static int dwc_otg_pcd_handle_in_ep_intr(struct dwc_pcd *pcd)
+{
+	struct core_if *core_if = GET_CORE_IF(pcd);
+	union diepint_data diepint = {.d32 = 0};
+	u32 ep_intr;
+	u32 epnum = 0;
+	struct pcd_ep *ep;
+	struct dwc_ep *dwc_ep;
+
+	/* Read in the device interrupt bits */
+	ep_intr = dwc_otg_read_dev_all_in_ep_intr(core_if);
+
+	/* Service the Device IN interrupts for each endpoint */
+	while (ep_intr) {
+		if (ep_intr & 0x1) {
+			union diepint_data c_diepint;
+
+			/* Get EP pointer */
+			ep = get_in_ep(pcd, epnum);
+			dwc_ep = &ep->dwc_ep;
+
+			diepint.d32 = dwc_otg_read_diep_intr(core_if, dwc_ep);
+
+			/* Transfer complete */
+			if (diepint.b.xfercompl)
+				handle_in_ep_xfr_complete_intr(pcd, ep, epnum);
+
+			/* Endpoint disable */
+			if (diepint.b.epdisabled)
+				handle_in_ep_disable_intr(pcd, epnum);
+
+			/* AHB Error */
+			if (diepint.b.ahberr) {
+				/* Clear ahberr */
+				c_diepint.d32 = 0;
+				c_diepint.b.ahberr = 1;
+				dwc_write_reg32(in_ep_int_reg(pcd, epnum),
+					c_diepint.d32);
+			}
+
+			/* TimeOUT Handshake (non-ISOC IN EPs) */
+			if (diepint.b.timeout)
+				handle_in_ep_timeout_intr(pcd, epnum);
+
+			/* IN Token received with TxF Empty */
+			if (diepint.b.intktxfemp)
+				handle_in_ep_tx_fifo_empty_intr(pcd, ep, epnum);
+
+			/* IN Token Received with EP mismatch */
+			if (diepint.b.intknepmis) {
+				/* Clear intknepmis */
+				c_diepint.d32 = 0;
+				c_diepint.b.intknepmis = 1;
+				dwc_write_reg32(in_ep_int_reg(pcd, epnum),
+					c_diepint.d32);
+			}
+
+			/* IN Endpoint NAK Effective */
+			if (diepint.b.inepnakeff)
+				handle_in_ep_nak_effective_intr(pcd, ep, epnum);
+
+			/* IN EP Tx FIFO Empty Intr */
+			if (diepint.b.emptyintr)
+				write_empty_tx_fifo(pcd, epnum);
+		}
+		epnum++;
+		ep_intr >>= 1;
+	}
+	return 1;
+}
+
+/**
+ * This function reads the Device All Endpoints Interrupt register and
+ * returns the OUT endpoint interrupt bits.
+ */
+static inline u32 dwc_otg_read_dev_all_out_ep_intr(struct core_if *_if)
+{
+	u32 v;
+	v = dwc_read_reg32(&_if->dev_if->dev_global_regs->daint) &
+		dwc_read_reg32(&_if->dev_if->dev_global_regs->daintmsk);
+	return (v & 0xffff0000) >> 16;
+}
+
+/**
+ * This function returns the Device OUT EP Interrupt register
+ */
+static inline u32 dwc_otg_read_doep_intr(struct core_if *core_if,
+						struct dwc_ep *ep)
+{
+	struct device_if *dev_if = core_if->dev_if;
+	u32 v;
+	v = dwc_read_reg32(&dev_if->out_ep_regs[ep->num]->doepint) &
+			dwc_read_reg32(&dev_if->dev_global_regs->doepmsk);
+	return v;
+}
+
+/**
+ * This interrupt indicates that an OUT EP has a pending Interrupt.
+ * The sequence for handling the OUT EP interrupt is shown below:
+ *
+ * - Read the Device All Endpoint Interrupt register.
+ * - Repeat the following for each OUT EP interrupt bit set (from LSB to MSB).
+ *
+ * - Read the Device Endpoint Interrupt (DOEPINTn) register
+ * - If "Transfer Complete" call the request complete function
+ * - If "Endpoint Disabled" complete the EP disable procedure.
+ * - If "AHB Error Interrupt" log error
+ * - If "Setup Phase Done" process Setup Packet (See Standard USB Command
+ *   Processing)
+ */
+static int dwc_otg_pcd_handle_out_ep_intr(struct dwc_pcd *pcd)
+{
+	struct core_if *core_if = GET_CORE_IF(pcd);
+	u32 ep_intr;
+	union doepint_data doepint = {.d32 = 0};
+	u32 epnum = 0;
+	struct dwc_ep *dwc_ep;
+
+	/* Read in the device interrupt bits */
+	ep_intr = dwc_otg_read_dev_all_out_ep_intr(core_if);
+	while (ep_intr) {
+		if (ep_intr & 0x1) {
+			union doepint_data c_doepint;
+
+			dwc_ep = &((get_out_ep(pcd, epnum))->dwc_ep);
+			doepint.d32 = dwc_otg_read_doep_intr(core_if, dwc_ep);
+
+			/* Transfer complete */
+			if (doepint.b.xfercompl) {
+				/* Clear xfercompl */
+				c_doepint.d32 = 0;
+				c_doepint.b.xfercompl = 1;
+				dwc_write_reg32(out_ep_int_reg(pcd, epnum),
+						c_doepint.d32);
+				if (epnum == 0)
+					handle_ep0(pcd);
+				else
+					complete_ep(get_out_ep(pcd, epnum));
+			}
+
+			/* Endpoint disable */
+			if (doepint.b.epdisabled) {
+				/* Clear epdisabled */
+				c_doepint.d32 = 0;
+				c_doepint.b.epdisabled = 1;
+				dwc_write_reg32(out_ep_int_reg(pcd, epnum),
+						c_doepint.d32);
+			}
+
+			/* AHB Error */
+			if (doepint.b.ahberr) {
+				c_doepint.d32 = 0;
+				c_doepint.b.ahberr = 1;
+				dwc_write_reg32(out_ep_int_reg(pcd, epnum),
+						c_doepint.d32);
+			}
+
+		    /* Setup Phase Done (control EPs) */
+		    if (doepint.b.setup) {
+				c_doepint.d32 = 0;
+				c_doepint.b.setup = 1;
+				dwc_write_reg32(out_ep_int_reg(pcd, epnum),
+						c_doepint.d32);
+				handle_ep0(pcd);
+			}
+		}
+		epnum++;
+		ep_intr >>= 1;
+	}
+	return 1;
+}
+
+/**
+ * Incomplete ISO IN Transfer Interrupt.  This interrupt indicates one of the
+ * following conditions occurred while transmitting an ISOC transaction.
+ *
+ * - Corrupted IN Token for ISOC EP.
+ * - Packet not complete in FIFO.
+ *
+ * The follow actions should be taken:
+ * - Determine the EP
+ * - Set incomplete flag in dwc_ep structure
+ *  - Disable EP.  When "Endpoint Disabled" interrupt is received Flush FIFO
+ */
+static int dwc_otg_pcd_handle_incomplete_isoc_in_intr(struct dwc_pcd *pcd)
+{
+	union gintmsk_data intr_mask = {.d32 = 0};
+	union gintsts_data gintsts = {.d32 = 0};
+
+	printk(KERN_INFO "Interrupt handler not implemented for IN ISOC "
+				"Incomplete\n");
+
+	/* Turn off and clear the interrupt */
+	intr_mask.b.incomplisoin = 1;
+	dwc_modify_reg32(&GET_CORE_IF(pcd)->core_global_regs->gintmsk,
+				intr_mask.d32, 0);
+
+	gintsts.b.incomplisoin = 1;
+	dwc_write_reg32(&GET_CORE_IF(pcd)->core_global_regs->gintsts,
+				gintsts.d32);
+	return 1;
+}
+
+/**
+ * Incomplete ISO OUT Transfer Interrupt.  This interrupt indicates that the
+ * core has dropped an ISO OUT packet.  The following conditions can be the
+ * cause:
+ *
+ * - FIFO Full, the entire packet would not fit in the FIFO.
+ * - CRC Error
+ * - Corrupted Token
+ *
+ * The follow actions should be taken:
+ * - Determine the EP
+ * - Set incomplete flag in dwc_ep structure
+ * - Read any data from the FIFO
+ * - Disable EP.  When "Endpoint Disabled" interrupt is received re-enable EP.
+ */
+static int dwc_otg_pcd_handle_incomplete_isoc_out_intr(struct dwc_pcd *pcd)
+{
+	union gintmsk_data intr_mask = {.d32 = 0};
+	union gintsts_data gintsts = {.d32 = 0};
+
+	printk(KERN_INFO "Interrupt handler not implemented for OUT ISOC "
+				"Incomplete\n");
+
+	/* Turn off and clear the interrupt */
+	intr_mask.b.incomplisoout = 1;
+	dwc_modify_reg32(&GET_CORE_IF(pcd)->core_global_regs->gintmsk,
+				intr_mask.d32, 0);
+
+	gintsts.b.incomplisoout = 1;
+	dwc_write_reg32(&GET_CORE_IF(pcd)->core_global_regs->gintsts,
+				gintsts.d32);
+	return 1;
+}
+
+/**
+ * This function handles the Global IN NAK Effective interrupt.
+ */
+static int dwc_otg_pcd_handle_in_nak_effective(struct dwc_pcd *pcd)
+{
+	struct device_if *dev_if = GET_CORE_IF(pcd)->dev_if;
+	union depctl_data diepctl = {.d32 = 0};
+	union depctl_data diepctl_rd = {.d32 = 0};
+	union gintmsk_data intr_mask = {.d32 = 0};
+	union gintsts_data gintsts = {.d32 = 0};
+	u32 i;
+
+	/* Disable all active IN EPs */
+	diepctl.b.epdis = 1;
+	diepctl.b.snak = 1;
+	for (i = 0; i <= dev_if->num_in_eps; i++) {
+		diepctl_rd.d32 = dwc_read_reg32(in_ep_ctl_reg(pcd, i));
+		if (diepctl_rd.b.epena)
+			dwc_write_reg32(in_ep_ctl_reg(pcd, i), diepctl.d32);
+	}
+
+	/* Disable the Global IN NAK Effective Interrupt */
+	intr_mask.b.ginnakeff = 1;
+	dwc_modify_reg32(&GET_CORE_IF(pcd)->core_global_regs->gintmsk,
+				intr_mask.d32, 0);
+
+	/* Clear interrupt */
+	gintsts.b.ginnakeff = 1;
+	dwc_write_reg32(&GET_CORE_IF(pcd)->core_global_regs->gintsts,
+			gintsts.d32);
+	return 1;
+}
+
+/**
+ * This function handles the Global OUT NAK Effective interrupt.
+ */
+static int dwc_otg_pcd_handle_out_nak_effective(struct dwc_pcd *pcd)
+{
+	union gintmsk_data intr_mask = {.d32 = 0};
+	union gintsts_data gintsts = {.d32 = 0};
+
+	printk(KERN_INFO "Interrupt handler not implemented for Global IN "
+			"NAK Effective\n");
+
+	/* Turn off and clear the interrupt */
+	intr_mask.b.goutnakeff = 1;
+	dwc_modify_reg32(&GET_CORE_IF(pcd)->core_global_regs->gintmsk,
+				intr_mask.d32, 0);
+
+	/* Clear goutnakeff */
+	gintsts.b.goutnakeff = 1;
+	dwc_write_reg32(&GET_CORE_IF(pcd)->core_global_regs->gintsts,
+				gintsts.d32);
+	return 1;
+}
+
+/**
+ * PCD interrupt handler.
+ *
+ * The PCD handles the device interrupts.  Many conditions can cause a
+ * device interrupt. When an interrupt occurs, the device interrupt
+ * service routine determines the cause of the interrupt and
+ * dispatches handling to the appropriate function. These interrupt
+ * handling functions are described below.
+ *
+ * All interrupt registers are processed from LSB to MSB.
+ *
+ */
+int dwc_otg_pcd_handle_intr(struct dwc_pcd *pcd)
+{
+	struct core_if *core_if = GET_CORE_IF(pcd);
+
+	union gintsts_data gintr_status;
+	int ret = 0;
+
+	if (dwc_otg_is_device_mode(core_if)) {
+		spin_lock(&pcd->lock);
+
+		gintr_status.d32 = dwc_otg_read_core_intr(core_if);
+		if (!gintr_status.d32) {
+			spin_unlock(&pcd->lock);
+			return 0;
+		}
+
+		if (gintr_status.b.sofintr)
+			ret |= dwc_otg_pcd_handle_sof_intr(pcd);
+		if (gintr_status.b.rxstsqlvl)
+			ret |= dwc_otg_pcd_handle_rx_status_q_level_intr(pcd);
+		if (gintr_status.b.nptxfempty)
+			ret |= dwc_otg_pcd_handle_np_tx_fifo_empty_intr(pcd);
+		if (gintr_status.b.ginnakeff)
+			ret |= dwc_otg_pcd_handle_in_nak_effective(pcd);
+		if (gintr_status.b.goutnakeff)
+			ret |= dwc_otg_pcd_handle_out_nak_effective(pcd);
+		if (gintr_status.b.i2cintr)
+			ret |= dwc_otg_pcd_handle_i2c_intr(pcd);
+		if (gintr_status.b.erlysuspend)
+			ret |= dwc_otg_pcd_handle_early_suspend_intr(pcd);
+		if (gintr_status.b.usbreset)
+			ret |= dwc_otg_pcd_handle_usb_reset_intr(pcd);
+		if (gintr_status.b.enumdone)
+			ret |= dwc_otg_pcd_handle_enum_done_intr(pcd);
+		if (gintr_status.b.isooutdrop)
+			ret |=
+			dwc_otg_pcd_handle_isoc_out_packet_dropped_intr(pcd);
+		if (gintr_status.b.eopframe)
+			ret |= dwc_otg_pcd_handle_end_periodic_frame_intr(pcd);
+		if (gintr_status.b.epmismatch)
+			ret |= dwc_otg_pcd_handle_ep_mismatch_intr(core_if);
+		if (gintr_status.b.inepint)
+			ret |= dwc_otg_pcd_handle_in_ep_intr(pcd);
+		if (gintr_status.b.outepintr)
+			ret |= dwc_otg_pcd_handle_out_ep_intr(pcd);
+		if (gintr_status.b.incomplisoin)
+			ret |= dwc_otg_pcd_handle_incomplete_isoc_in_intr(pcd);
+		if (gintr_status.b.incomplisoout)
+			ret |= dwc_otg_pcd_handle_incomplete_isoc_out_intr(pcd);
+
+		spin_unlock(&pcd->lock);
+	}
+	return ret;
+}
-- 
1.7.1

^ permalink raw reply related


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