* Re: Apparent kernel bug with GDB on ppc405
From: Josh Boyer @ 2007-10-27 1:27 UTC (permalink / raw)
To: benh; +Cc: gdb, Matt Mackall, linuxppc-embedded, Daniel Jacobowitz
In-Reply-To: <1193448983.18243.16.camel@pasglop>
On Sat, 2007-10-27 at 11:36 +1000, Benjamin Herrenschmidt wrote:
> On Fri, 2007-10-26 at 15:41 -0500, Josh Boyer wrote:
> > On Fri, 26 Oct 2007 11:51:22 +1000
> > Benjamin Herrenschmidt <benh@kernel.crashing.org> wrote:
> >
> > >
> > > On Wed, 2007-10-24 at 18:41 -0400, Daniel Jacobowitz wrote:
> > > > On Wed, Oct 24, 2007 at 05:32:50PM -0500, Matt Mackall wrote:
> > > > > Not completely implausible, but a) why isn't this seen on basically
> > > > > every machine with software TLB? b) why does -local- GDB, which is
> > > > > presumably doing much less work than gdbserver + network stack, not fail?
> > > >
> > > > You said it yourself. Local gdb does more work -> blows through more
> > > > TLB entries.
> > > >
> > > > I can't answer you about the other half, but I'm pretty sure TLB
> > > > invalidation is already supposed to be happening... somewhere.
> > >
> > > Yes. do_wp_page() -> ptep_clear_flush() -> flush_tlb_page()
> >
> > Aren't there cases in do_wp_page that don't call ptep_clear_flush?
> > Seems anonymous pages, and possibly shared writeable pages skip that
> > step if reuse is true.
>
> Nah, if that was broken, everybody would be in bad shape. I think I know
> what's up, see my other email.
Yeah, I figured that out after I sent this anyway. In those cases it
should likely get flushed from ptep_set_access_flags.
josh
^ permalink raw reply
* Re: Apparent kernel bug with GDB on ppc405
From: Benjamin Herrenschmidt @ 2007-10-27 1:36 UTC (permalink / raw)
To: Josh Boyer; +Cc: gdb, Matt Mackall, linuxppc-embedded, Daniel Jacobowitz
In-Reply-To: <20071026154126.28082129@weaponx.rchland.ibm.com>
On Fri, 2007-10-26 at 15:41 -0500, Josh Boyer wrote:
> On Fri, 26 Oct 2007 11:51:22 +1000
> Benjamin Herrenschmidt <benh@kernel.crashing.org> wrote:
>
> >
> > On Wed, 2007-10-24 at 18:41 -0400, Daniel Jacobowitz wrote:
> > > On Wed, Oct 24, 2007 at 05:32:50PM -0500, Matt Mackall wrote:
> > > > Not completely implausible, but a) why isn't this seen on basically
> > > > every machine with software TLB? b) why does -local- GDB, which is
> > > > presumably doing much less work than gdbserver + network stack, not fail?
> > >
> > > You said it yourself. Local gdb does more work -> blows through more
> > > TLB entries.
> > >
> > > I can't answer you about the other half, but I'm pretty sure TLB
> > > invalidation is already supposed to be happening... somewhere.
> >
> > Yes. do_wp_page() -> ptep_clear_flush() -> flush_tlb_page()
>
> Aren't there cases in do_wp_page that don't call ptep_clear_flush?
> Seems anonymous pages, and possibly shared writeable pages skip that
> step if reuse is true.
Nah, if that was broken, everybody would be in bad shape. I think I know
what's up, see my other email.
Cheers,
Ben.
^ permalink raw reply
* Re: Apparent kernel bug with GDB on ppc405
From: Benjamin Herrenschmidt @ 2007-10-27 1:30 UTC (permalink / raw)
To: Matt Mackall; +Cc: gdb, linuxppc-embedded
In-Reply-To: <20071026144134.GW19691@waste.org>
On Fri, 2007-10-26 at 09:41 -0500, Matt Mackall wrote:
> On Fri, Oct 26, 2007 at 01:23:25PM +1000, Benjamin Herrenschmidt
> wrote:
> >
> > > This is actually 405. Does that have the same issue?
> >
> > hrm... I don't remember :-) There -is- something fishy about the
> icache
> > on 405 but I don't remember for sure. Try sticking an iccci in there
> and
> > tell us if that helps.
>
> I did. I stuck an iccci -and- a dccci loop in. I threw in the tlbia
> when I ran out of other ideas. And the tlbia + existing
> flush_icache_range appears to be sufficient (tested on a non-trivial
> app).
>
> According to my docs, the 405fx's icache is virtually
> indexed/physically mapped, while the dcache is phys/phys.
I think there is a bug in the 40x/44x support at this stage, it only
doing invalidations for the current PID. That means that a process
trying to invalidate TLB entries of another address space will fail to
do so ... oops.
That can be fixed by adding a pid argument to to _tlbie, and using it in
the assembly. In the 40x case, probably by switching SPRN_PID to that
and then back, in the 44x case, by using that value instead of SPRN_PID
for the MMUCR register.
It's part of a patche I have around taking dirt that rework signficantly
the 44x (though not the 40x yet) TLB handling, cleaning things and
fixing bugs (such as this one, I remember it now). I need to spend a bit
of time to clean it up, remove some experimental stuff from it, and
submit it.
I'll try to do that early next week, and maybe come up with a fix for
that specific bug today or tomorrow.
Ben.
^ permalink raw reply
* Re: [PATCH 0/2] PowerPC: Add 44x NDFC device-tree aware support
From: Thomas Gleixner @ 2007-10-26 21:37 UTC (permalink / raw)
To: Valentine Barshak; +Cc: linuxppc-dev, sr, linux-mtd
In-Reply-To: <20071026163058.GA11354@ru.mvista.com>
On Fri, 26 Oct 2007, Valentine Barshak wrote:
> The major difference is that the original implements each chip connected NDFC banks as a
> separate MTD device. Here I try to have one MTD device spread on all chips found.
> However, the chips should have equal ID's and sizes, but I've never seen several different
> chips attached to single ndfc.
Bamboo has 2 different nand chips. And I'm aware of another board
which has a 2k-page onboard NAND and sockets for SmartMedia /
PictureXd cards, which will simply break with your design.
Restricting stuff for no good reason is never a good idea.
mtdconcat can build you a big one if you want, so why adding
restrictions to a driver ?
> I'm adding ndfc_of as a separate file, since some other changes
> have also been made (e.g. all i/o is made with ndfc_readl/writel inline functions).
This should be done in the original ndfc driver and not in a separate
incarnation.
> The original version didn't handle driver removal well (it never calls del_mtd...),it's
> corrected here.
Why not fixing the original driver ?
> Any comments are greatly appreciated.
NACK.
Please fix the existing driver and convert it to deal with OF and fix
the other short comings as well.
Duplicate code is not going anywhere near drivers/mtd/nand
tglx
^ permalink raw reply
* Re: reg adjust_total_lowmem
From: Scott Wood @ 2007-10-26 21:27 UTC (permalink / raw)
To: Kumar Gala; +Cc: linuxppc-dev list, Surya Ravikiran
In-Reply-To: <Pine.LNX.4.64.0710261615380.23925@blarg.am.freescale.net>
Kumar Gala wrote:
> On Fri, 26 Oct 2007, Scott Wood wrote:
>
>> Scott Wood wrote:
>>> On Fri, Oct 26, 2007 at 10:04:19AM -0500, Kumar Gala wrote:
>>>> The reason you have 192M is that lowmem is the total amount of memory
>>>> that can be covered by up to three CAM entries. In the case of setting
>>>> mem=252M that max that three CAM entries can cover is 192M (64 +64+64).
>>>> You should be able to access the other 60M via HIGHMEM.
>>> Why doesn't it just use a 256M mapping, and not access the last 4M?
>
> This has some possibility, not sure what the threshold should be. Do we
> just always map 768M of lowmem regardless of how much memory we have?
That's probably the simplest way. We'll need to fix VMALLOC_START as well.
>> Not to mention, why highmem and not just normal TLB0 mappings for the extra
>> pages?
>
> Because we do not handle recursive misses in the TLB handlers. We expect
> any load/store that occurs in the TLB handlers to not have a TLB fault
> associated with (and thus all of lowmem must be pinned).
OK. I guess that means no page debugging... :-P
-Scott
^ permalink raw reply
* Re: reg adjust_total_lowmem
From: Kumar Gala @ 2007-10-26 21:17 UTC (permalink / raw)
To: Scott Wood; +Cc: linuxppc-dev list, Surya Ravikiran
In-Reply-To: <47225327.2070103@freescale.com>
On Fri, 26 Oct 2007, Scott Wood wrote:
> Scott Wood wrote:
> > On Fri, Oct 26, 2007 at 10:04:19AM -0500, Kumar Gala wrote:
> > > The reason you have 192M is that lowmem is the total amount of memory
> > > that can be covered by up to three CAM entries. In the case of setting
> > > mem=252M that max that three CAM entries can cover is 192M (64 +64+64).
> > > You should be able to access the other 60M via HIGHMEM.
> >
> > Why doesn't it just use a 256M mapping, and not access the last 4M?
This has some possibility, not sure what the threshold should be. Do we
just always map 768M of lowmem regardless of how much memory we have?
> Not to mention, why highmem and not just normal TLB0 mappings for the extra
> pages?
Because we do not handle recursive misses in the TLB handlers. We expect
any load/store that occurs in the TLB handlers to not have a TLB fault
associated with (and thus all of lowmem must be pinned).
- k
^ permalink raw reply
* Re: reg adjust_total_lowmem
From: Scott Wood @ 2007-10-26 20:50 UTC (permalink / raw)
To: Kumar Gala; +Cc: linuxppc-dev list, Surya Ravikiran
In-Reply-To: <20071026194005.GA4571@loki.buserror.net>
Scott Wood wrote:
> On Fri, Oct 26, 2007 at 10:04:19AM -0500, Kumar Gala wrote:
>> The reason you have 192M is that lowmem is the total amount of memory
>> that can be covered by up to three CAM entries. In the case of
>> setting mem=252M that max that three CAM entries can cover is 192M (64
>> +64+64). You should be able to access the other 60M via HIGHMEM.
>
> Why doesn't it just use a 256M mapping, and not access the last 4M?
Not to mention, why highmem and not just normal TLB0 mappings for the
extra pages?
-Scott
^ permalink raw reply
* [PATCH] [Powerpc] fix switch_slb handling of 1T ESID values
From: Will Schmidt @ 2007-10-26 20:46 UTC (permalink / raw)
To: linuxppc-dev; +Cc: paulus
[Powerpc] fix switch_slb handling of 1T ESID values
Now that we have 1TB segment size support, we need to be using the
GET_ESID_1T macro when comparing ESID values for pc,stack, and
unmapped_base within switch_slb() when we're on a CPU that supports it.
This also happens to fix a duplicate-slb-entry inspired machine-check
exception I was seeing when trying to run java on a power6 partition.
Tested on power6 and power5.
Signed-Off-By: Will Schmidt <will_schmidt@vnet.ibm.com>
---
There is a similar bit of code in stab.c switch_stab(). Should this change also be made there?
---
arch/powerpc/mm/slb.c | 19 ++++++++++++++-----
1 files changed, 14 insertions(+), 5 deletions(-)
diff --git a/arch/powerpc/mm/slb.c b/arch/powerpc/mm/slb.c
index bbd2c51..0c527d7 100644
--- a/arch/powerpc/mm/slb.c
+++ b/arch/powerpc/mm/slb.c
@@ -193,16 +193,25 @@ void switch_slb(struct task_struct *tsk, struct mm_struct *mm)
return;
slb_allocate(pc);
- if (GET_ESID(pc) == GET_ESID(stack))
- return;
+ if (cpu_has_feature(CPU_FTR_1T_SEGMENT)) {
+ if (GET_ESID_1T(pc) == GET_ESID_1T(stack))
+ return;
+ } else
+ if (GET_ESID(pc) == GET_ESID(stack))
+ return;
if (is_kernel_addr(stack))
return;
slb_allocate(stack);
- if ((GET_ESID(pc) == GET_ESID(unmapped_base))
- || (GET_ESID(stack) == GET_ESID(unmapped_base)))
- return;
+ if (cpu_has_feature(CPU_FTR_1T_SEGMENT)) {
+ if ((GET_ESID_1T(pc) == GET_ESID_1T(unmapped_base))
+ || (GET_ESID_1T(stack) == GET_ESID_1T(unmapped_base)))
+ return;
+ } else
+ if ((GET_ESID(pc) == GET_ESID(unmapped_base))
+ || (GET_ESID(stack) == GET_ESID(unmapped_base)))
+ return;
if (is_kernel_addr(unmapped_base))
return;
^ permalink raw reply related
* Re: Apparent kernel bug with GDB on ppc405
From: Josh Boyer @ 2007-10-26 20:41 UTC (permalink / raw)
To: benh; +Cc: gdb, Matt Mackall, linuxppc-embedded, Daniel Jacobowitz
In-Reply-To: <1193363482.7018.41.camel@pasglop>
On Fri, 26 Oct 2007 11:51:22 +1000
Benjamin Herrenschmidt <benh@kernel.crashing.org> wrote:
>
> On Wed, 2007-10-24 at 18:41 -0400, Daniel Jacobowitz wrote:
> > On Wed, Oct 24, 2007 at 05:32:50PM -0500, Matt Mackall wrote:
> > > Not completely implausible, but a) why isn't this seen on basically
> > > every machine with software TLB? b) why does -local- GDB, which is
> > > presumably doing much less work than gdbserver + network stack, not fail?
> >
> > You said it yourself. Local gdb does more work -> blows through more
> > TLB entries.
> >
> > I can't answer you about the other half, but I'm pretty sure TLB
> > invalidation is already supposed to be happening... somewhere.
>
> Yes. do_wp_page() -> ptep_clear_flush() -> flush_tlb_page()
Aren't there cases in do_wp_page that don't call ptep_clear_flush?
Seems anonymous pages, and possibly shared writeable pages skip that
step if reuse is true.
josh
^ permalink raw reply
* Re: [PATCH] fix appletouch geyser 1 breakage
From: Johannes Berg @ 2007-10-26 20:31 UTC (permalink / raw)
To: Dmitry Torokhov; +Cc: linuxppc-dev list, Benjamin Berg, Anton Ekblad
In-Reply-To: <d120d5000710260905k4c6391b2j7c3d8528cc8198b0@mail.gmail.com>
[-- Attachment #1: Type: text/plain, Size: 107 bytes --]
> Johannes, and what is product ID for your touchpad?
It's 0x20e, listed as 'fountain'
johannes
[-- Attachment #2: This is a digitally signed message part --]
[-- Type: application/pgp-signature, Size: 828 bytes --]
^ permalink raw reply
* Re: problems to boot 2.6.23 kernel on XILINX ppc with 8Mbytes of RAM
From: Grant Likely @ 2007-10-26 19:54 UTC (permalink / raw)
To: Magnus Hjorth; +Cc: Linuxppc-embedded
In-Reply-To: <47203EF9.1070906@omnisys.se>
On 10/25/07, Magnus Hjorth <mh@omnisys.se> wrote:
> Hello Manu,
>
> Could you try the attached patch to arch/ppc/mm/4xx_mmu.c and see if it
> solves your problem?
Good catch, thanks!
Send you're signed off by line, and I'll for Josh to pick it up.
Reviewed-by: Grant Likely <grant.likely@secretlab.ca>
Cheers,
g.
--
Grant Likely, B.Sc., P.Eng.
Secret Lab Technologies Ltd.
grant.likely@secretlab.ca
(403) 399-0195
^ permalink raw reply
* Re: reg adjust_total_lowmem
From: Scott Wood @ 2007-10-26 19:40 UTC (permalink / raw)
To: Kumar Gala; +Cc: linuxppc-dev list, Surya Ravikiran
In-Reply-To: <524E69ED-899B-452F-B593-44499C5181A3@kernel.crashing.org>
On Fri, Oct 26, 2007 at 10:04:19AM -0500, Kumar Gala wrote:
>
> On Oct 25, 2007, at 10:51 PM, Surya Ravikiran wrote:
>
> > Hi,
> >
> > I am trying to bootup a Linux kernel, on a FS eval board with 256M.
> > I pass the kernel argument mem=252M, and see that the kernel boots up
> > fine, but with much less memory, ~192M (the closes 64M multiple), and
> > I browsed through the code to see that the adjust_total_lowmem
> > function does not add up the residual memory to the total memory.
> > I am trying to understand why I cannot do this reserving of higher end
> > of the RAM during boot up,
> > I would appreciate your comments.
>
> The reason you have 192M is that lowmem is the total amount of memory
> that can be covered by up to three CAM entries. In the case of
> setting mem=252M that max that three CAM entries can cover is 192M (64
> +64+64). You should be able to access the other 60M via HIGHMEM.
Why doesn't it just use a 256M mapping, and not access the last 4M?
-Scott
^ permalink raw reply
* RE: Porting to /arch/powerpc
From: Joakim Tjernlund @ 2007-10-26 19:03 UTC (permalink / raw)
To: 'Scott Wood', Li Yang; +Cc: linuxppc-embedded
In-Reply-To: <47222D86.1000502@freescale.com>
> -----Original Message-----
> From: Scott Wood [mailto:scottwood@freescale.com]
> Sent: den 26 oktober 2007 20:10
> To: Joakim Tjernlund
> Cc: 'Rune Torgersen'; linuxppc-embedded@ozlabs.org
> Subject: Re: Porting to /arch/powerpc
>
> Joakim Tjernlund wrote:
> >> The fs_enet driver doesn't currently support phy-less
> >> directly, but you
> >> may be able to do something with the fixed phy driver.
> >
> > How is PHY less support supposed to be impl.? I would like
> > to do the same for ucc_geth. I once sent a patch that made
> > the PHY optional, but it never made into the driver.
>
> Basically, the driver needs to assume the link is up if there's no
> phy-handle in the device tree, rather than error out.
>
> -Scott
Right, that was what I did, but I think Leo didn't want it at the time.
Leo?
Jocke
^ permalink raw reply
* Re: [PATCH 0/2] PowerPC: Add 44x NDFC device-tree aware support
From: Josh Boyer @ 2007-10-26 18:50 UTC (permalink / raw)
To: Valentine Barshak; +Cc: linuxppc-dev, tglx, sr, linux-mtd
In-Reply-To: <20071026163058.GA11354@ru.mvista.com>
On Fri, 26 Oct 2007 20:30:58 +0400
Valentine Barshak <vbarshak@ru.mvista.com> wrote:
> I've worked in parallel with Stefan Roese on the new OF NDFC support.
> This version (as well as Stefan's) is based on the original NDFC driver by Thomas Gleixner.
> The major difference is that the original implements each chip connected NDFC banks as a
> separate MTD device. Here I try to have one MTD device spread on all chips found.
> However, the chips should have equal ID's and sizes, but I've never seen several different
> chips attached to single ndfc.
Hm. Why did you do that? It adds restrictions to the driver that
really don't need to be present. If people want a single MTD device
across multiple chips, they can use mtdconcat.
Remember, just because you've never seen something, doesn't mean it
doesn't exist. Bamboo even has two different sized NAND chips
hooked to the NDFC, IIRC.
josh
^ permalink raw reply
* Re: problems to boot 2.6.23 kernel on XILINX ppc with 8Mbytes of RAM
From: manu @ 2007-10-26 18:43 UTC (permalink / raw)
To: Linuxppc-embedded
In-Reply-To: <47203EF9.1070906@omnisys.se>
Hello Magnus,
your patch solved the problem.
Thank you.
Manu
Magnus Hjorth a écrit :
> Hello Manu,
>
> Could you try the attached patch to arch/ppc/mm/4xx_mmu.c and see if
> it solves your problem?
>
> Best regards,
> Magnus
>
>
> manu wrote:
>> Hello,
>> I work on a custom board based on a virtex 2 pro FPGA and 8Mbytes of
>> SDRAM.
>> Untill now I used a 2.4.31 linux ppc kernel with John Williams patches
>> and uclinux distribution and everything worked perfectly.
>> I've decided to move to the latest 2.6 version from kernel.org
>> (2.6.23.1) with an initramfs containing a busybox.
>> My complete zImage including the initramfs has a size of 900Kbytes.
>> I made some tests with a ML300 board and I managed to get a shell
>> easily.
>> When I migrated to the custom board, I had the "Now booting the kernel"
>> message and then nothing.
>> When I trace the code running on the ppc with the debugger, execution
>> seems to be stuck in some early initialization code.
>> I managed to reproduce the problem on the ML300 using "mem=8m" parameter
>> on the bootline.
>> With "mem=16m" the kernel boots correctly.
>> I'm really surprised by the amount of RAM required to boot the kernel.
>> Is there a way to make it boot with only 8Mbytes of RAM ?
>> Thanks for your help.
>>
>> Manu
>> _______________________________________________
>> Linuxppc-embedded mailing list
>> Linuxppc-embedded@ozlabs.org
>> https://ozlabs.org/mailman/listinfo/linuxppc-embedded
>>
>
>
> ------------------------------------------------------------------------
>
> --- 4xx_mmu_old.c 2007-10-25 08:54:46.000000000 +0200
> +++ 4xx_mmu.c 2007-10-25 08:55:57.000000000 +0200
> @@ -105,7 +105,7 @@
> return s;
> }
>
> - while (s <= (total_lowmem - LARGE_PAGE_SIZE_16M)) {
> + while (s + LARGE_PAGE_SIZE_16M <= total_lowmem) {
> pmd_t *pmdp;
> unsigned long val = p | _PMD_SIZE_16M | _PAGE_HWEXEC | _PAGE_HWWRITE;
>
> @@ -120,7 +120,7 @@
> s += LARGE_PAGE_SIZE_16M;
> }
>
> - while (s <= (total_lowmem - LARGE_PAGE_SIZE_4M)) {
> + while (s + LARGE_PAGE_SIZE_4M <= total_lowmem) {
> pmd_t *pmdp;
> unsigned long val = p | _PMD_SIZE_4M | _PAGE_HWEXEC | _PAGE_HWWRITE;
>
>
^ permalink raw reply
* Re: [PATCH] [POWERPC] Fix CONFIG_SMP=n build break
From: Arnd Bergmann @ 2007-10-26 18:19 UTC (permalink / raw)
To: linuxppc-dev; +Cc: Olof Johansson, paulus, linux-kernel
In-Reply-To: <20071026165510.GA2323@lixom.net>
On Friday 26 October 2007, Olof Johansson wrote:
> Fix two build errors on powerpc allyesconfig + CONFIG_SMP=3Dn:
>
> arch/powerpc/platforms/built-in.o: In function `cpu_affinity_set':
> arch/powerpc/platforms/cell/spu_priv1_mmio.c:78: undefined reference to
> `.iic_get_target_id' arch/powerpc/platforms/built-in.o: In function
> `iic_init_IRQ':
> arch/powerpc/platforms/cell/interrupt.c:397: undefined reference to
> `.iic_setup_cpu'
Thanks for reporting this.
> --- a/arch/powerpc/platforms/cell/spu_priv1_mmio.c
> +++ b/arch/powerpc/platforms/cell/spu_priv1_mmio.c
> @@ -75,9 +75,11 @@ static u64 int_stat_get(struct spu *spu, int class)
> =A0
> =A0static void cpu_affinity_set(struct spu *spu, int cpu)
> =A0{
> +#ifdef CONFIG_SMP
> =A0=A0=A0=A0=A0=A0=A0=A0u64 target =3D iic_get_target_id(cpu);
> =A0=A0=A0=A0=A0=A0=A0=A0u64 route =3D target << 48 | target << 32 | targe=
t << 16;
> =A0=A0=A0=A0=A0=A0=A0=A0out_be64(&spu->priv1->int_route_RW, route);
> +#endif
> =A0}
I think here it would be better to move iic_get_target_id out of
CONFIG_SMP as well. We might want to kexec from an SMP kernel into
a UP kernel, and in that case, cpu_affinity_set() should better
reset the routing to CPU 0.
Arnd <><
^ permalink raw reply
* Re: Porting to /arch/powerpc
From: Scott Wood @ 2007-10-26 18:10 UTC (permalink / raw)
To: Joakim Tjernlund; +Cc: linuxppc-embedded
In-Reply-To: <020001c81756$0fc78760$5267a8c0@Jocke>
Joakim Tjernlund wrote:
>> The fs_enet driver doesn't currently support phy-less
>> directly, but you
>> may be able to do something with the fixed phy driver.
>
> How is PHY less support supposed to be impl.? I would like
> to do the same for ucc_geth. I once sent a patch that made
> the PHY optional, but it never made into the driver.
Basically, the driver needs to assume the link is up if there's no
phy-handle in the device tree, rather than error out.
-Scott
^ permalink raw reply
* [PATCH] Allow sharing of CMOS clock setup.
From: Randy Vinson @ 2007-10-26 17:45 UTC (permalink / raw)
To: linuxppc-dev@ozlabs.org, kumar Gala
>From 61da8cf1f92043925ea20ffafafaf0874d761b0e Mon Sep 17 00:00:00 2001
From: Randy Vinson <rvinson@mvista.com>
Date: Wed, 24 Oct 2007 17:36:59 -0700
Subject: [PATCH] Allow sharing of CMOS clock setup.
Move the CMOS RTC clock setup code from arch/powerpc/platforms/fsl_uli1575.c
to arch/powerpc/sysdev/rtc_cmos_setup.c so it can be used by more platforms.
Signed-off-by: Randy Vinson <rvinson@mvista.com>
---
arch/powerpc/platforms/fsl_uli1575.c | 14 --------------
arch/powerpc/sysdev/rtc_cmos_setup.c | 13 +++++++++++++
2 files changed, 13 insertions(+), 14 deletions(-)
diff --git a/arch/powerpc/platforms/fsl_uli1575.c b/arch/powerpc/platforms/fsl_uli1575.c
index afc9141..171d04f 100644
--- a/arch/powerpc/platforms/fsl_uli1575.c
+++ b/arch/powerpc/platforms/fsl_uli1575.c
@@ -13,7 +13,6 @@
#include <linux/kernel.h>
#include <linux/pci.h>
#include <linux/interrupt.h>
-#include <linux/mc146818rtc.h>
#include <asm/system.h>
#include <asm/pci-bridge.h>
@@ -155,19 +154,6 @@ static void __devinit quirk_final_uli1575(struct pci_dev *dev)
outb(0xfa, 0x4d0);
outb(0x1e, 0x4d1);
-
- /* setup RTC */
- CMOS_WRITE(RTC_SET, RTC_CONTROL);
- CMOS_WRITE(RTC_24H, RTC_CONTROL);
-
- /* ensure month, date, and week alarm fields are ignored */
- CMOS_WRITE(0, RTC_VALID);
-
- outb_p(0x7c, 0x72);
- outb_p(RTC_ALARM_DONT_CARE, 0x73);
-
- outb_p(0x7d, 0x72);
- outb_p(RTC_ALARM_DONT_CARE, 0x73);
}
/* SATA */
diff --git a/arch/powerpc/sysdev/rtc_cmos_setup.c b/arch/powerpc/sysdev/rtc_cmos_setup.c
index 0c9ac7e..4779b0b 100644
--- a/arch/powerpc/sysdev/rtc_cmos_setup.c
+++ b/arch/powerpc/sysdev/rtc_cmos_setup.c
@@ -53,6 +53,19 @@ static int __init add_rtc(void)
if (IS_ERR(pd))
return PTR_ERR(pd);
+ /* setup RTC */
+ CMOS_WRITE(RTC_SET, RTC_CONTROL);
+ CMOS_WRITE(RTC_24H, RTC_CONTROL);
+
+ /* ensure month, date, and week alarm fields are ignored */
+ CMOS_WRITE(0, RTC_VALID);
+
+ outb_p(0x7c, 0x72);
+ outb_p(RTC_ALARM_DONT_CARE, 0x73);
+
+ outb_p(0x7d, 0x72);
+ outb_p(RTC_ALARM_DONT_CARE, 0x73);
+
return 0;
}
fs_initcall(add_rtc);
--
1.5.3.rc2.22.g69a9b
^ permalink raw reply related
* [PATCH] PowerPC: 44x device-tree aware NDFC bindings
From: Valentine Barshak @ 2007-10-26 17:47 UTC (permalink / raw)
To: linuxppc-dev; +Cc: tglx, sr, linux-mtd
In-Reply-To: <20071026164130.GA11812@ru.mvista.com>
PowerPC 44x NAND Flash Controller (NDFC) bindings
Signed-off-by: Valentine Barshak <vbarshak@ru.mvista.com>
---
Documentation/powerpc/booting-without-of.txt | 43 +++++++++++++++++++++++++++
1 files changed, 43 insertions(+)
--- linux-2.6.orig/Documentation/powerpc/booting-without-of.txt 2007-10-26 19:01:43.000000000 +0400
+++ linux-2.6/Documentation/powerpc/booting-without-of.txt 2007-10-26 21:43:33.000000000 +0400
@@ -52,6 +52,7 @@ Table of Contents
i) Freescale QUICC Engine module (QE)
j) CFI or JEDEC memory-mapped NOR flash
k) Global Utilities Block
+ l) 44x NanD Flash Controller (NDFC)
VII - Specifying interrupt information for devices
1) interrupts property
@@ -2242,6 +2243,48 @@ platforms are moved over to use the flat
available.
For Axon: 0x0000012a
+ l) 44x NanD Flash Controller (NDFC)
+
+ Required properties:
+ - compatible : should be "ibm,ndfc".
+ - reg : should contain at address and length of the NDFC registers
+ - bank-width : NAND chip bus width. Should be 1 for 8-bit NAND or
+ 2 for 16-bit NAND
+ - bank-map : The first 4 bits of this property indicate which of the
+ 4 NDFC banks have chips attached.
+ - #address-cells, #size-cells : Must be present if the flash has
+ sub-nodes representing partitions (see below). In this case
+ both #address-cells and #size-cells must be equal to 1.
+
+ NDFC can have partition nodes, which are described the same way
+ as for the CFI or JEDEC memory-mapped NOR flash.
+
+ Example (Sequoia 440EPx):
+ NDFC is relocatable within EBC and should have EBC as a parent node.
+ Here we have NDFC on EBC CS3 bank:
+
+ ndfc@0,0 {
+ compatible = "ibm,ndfc-440epx", "ibm,ndfc";
+ reg = <3 000000 2000>;
+ bank-width = <1>;
+ bank-mask = <8>;
+ #address-cells = <1>;
+ #size-cells = <1>;
+ partition@0 {
+ label = "u-boot-nand";
+ reg = <0 0080000>;
+ };
+ partition@80000 {
+ label = "kernel-nand";
+ reg = <0080000 0180000>;
+ };
+ partition@200000 {
+ label = "filesystem";
+ reg = <0200000 1e00000>;
+ };
+ };
+
+
More devices will be defined as this spec matures.
VII - Specifying interrupt information for devices
^ permalink raw reply
* Re: [PATCH 2/2] PowerPC: NDFC entry for PowerPC 440EPx Sequoia DTS
From: Sergei Shtylyov @ 2007-10-26 16:54 UTC (permalink / raw)
To: Valentine Barshak; +Cc: linuxppc-dev, tglx, sr, linux-mtd
In-Reply-To: <20071026164130.GA11812@ru.mvista.com>
Hello.
Valentine Barshak wrote:
> NDFC (NAND Flash Controller) DTS entry for PowerPC 440EPx Sequoia board.
> The NDFC is relocatable in EBC space.
> Signed-off-by: Valentine Barshak <vbarshak@ru.mvista.com>
You need to documents the layout in the
Documentation/powerpc/booting-without-of.txt file
WBR, Sergei
^ permalink raw reply
* [PATCH] [POWERPC] Fix CONFIG_SMP=n build breaks
From: Olof Johansson @ 2007-10-26 16:55 UTC (permalink / raw)
To: paulus; +Cc: linuxppc-dev, linux-kernel, arnd
Fix two build errors on powerpc allyesconfig + CONFIG_SMP=n:
arch/powerpc/platforms/built-in.o: In function `cpu_affinity_set':
arch/powerpc/platforms/cell/spu_priv1_mmio.c:78: undefined reference to `.iic_get_target_id'
arch/powerpc/platforms/built-in.o: In function `iic_init_IRQ':
arch/powerpc/platforms/cell/interrupt.c:397: undefined reference to `.iic_setup_cpu'
Signed-off-by: Olof Johansson <olof@lixom.net>
diff --git a/arch/powerpc/platforms/cell/interrupt.c b/arch/powerpc/platforms/cell/interrupt.c
index 151fd8b..222678f 100644
--- a/arch/powerpc/platforms/cell/interrupt.c
+++ b/arch/powerpc/platforms/cell/interrupt.c
@@ -158,6 +158,11 @@ static unsigned int iic_get_irq(void)
return virq;
}
+void iic_setup_cpu(void)
+{
+ out_be64(&__get_cpu_var(iic).regs->prio, 0xff);
+}
+
#ifdef CONFIG_SMP
/* Use the highest interrupt priorities for IPI */
@@ -166,11 +171,6 @@ static inline int iic_ipi_to_irq(int ipi)
return IIC_IRQ_TYPE_IPI + 0xf - ipi;
}
-void iic_setup_cpu(void)
-{
- out_be64(&__get_cpu_var(iic).regs->prio, 0xff);
-}
-
void iic_cause_IPI(int cpu, int mesg)
{
out_be64(&per_cpu(iic, cpu).regs->generate, (0xf - mesg) << 4);
diff --git a/arch/powerpc/platforms/cell/spu_priv1_mmio.c b/arch/powerpc/platforms/cell/spu_priv1_mmio.c
index 67fa724..e5e08ac 100644
--- a/arch/powerpc/platforms/cell/spu_priv1_mmio.c
+++ b/arch/powerpc/platforms/cell/spu_priv1_mmio.c
@@ -75,9 +75,11 @@ static u64 int_stat_get(struct spu *spu, int class)
static void cpu_affinity_set(struct spu *spu, int cpu)
{
+#ifdef CONFIG_SMP
u64 target = iic_get_target_id(cpu);
u64 route = target << 48 | target << 32 | target << 16;
out_be64(&spu->priv1->int_route_RW, route);
+#endif
}
static u64 mfc_dar_get(struct spu *spu)
^ permalink raw reply related
* [PATCH 2/2] PowerPC: NDFC entry for PowerPC 440EPx Sequoia DTS
From: Valentine Barshak @ 2007-10-26 16:41 UTC (permalink / raw)
To: linuxppc-dev; +Cc: tglx, sr, linux-mtd
In-Reply-To: <20071026163058.GA11354@ru.mvista.com>
NDFC (NAND Flash Controller) DTS entry for PowerPC 440EPx Sequoia board.
The NDFC is relocatable in EBC space.
Signed-off-by: Valentine Barshak <vbarshak@ru.mvista.com>
---
arch/powerpc/boot/dts/sequoia.dts | 20 ++++++++++++++++++++
1 files changed, 20 insertions(+)
diff -pruN linux-2.6.orig/arch/powerpc/boot/dts/sequoia.dts linux-2.6.bld/arch/powerpc/boot/dts/sequoia.dts
--- linux-2.6.orig/arch/powerpc/boot/dts/sequoia.dts 2007-10-22 18:33:07.000000000 +0400
+++ linux-2.6.bld/arch/powerpc/boot/dts/sequoia.dts 2007-10-22 22:19:42.000000000 +0400
POB0: opb {
compatible = "ibm,opb-440epx", "ibm,opb";
#address-cells = <1>;
@@ -141,6 +148,26 @@
interrupts = <5 1>;
interrupt-parent = <&UIC1>;
+ ndfc@0,0 {
+ compatible = "ibm,ndfc-440epx", "ibm,ndfc";
+ reg = <3 000000 2000>;
+ bank-width = <1>;
+ bank-mask = <8>;
+ #address-cells = <1>;
+ #size-cells = <1>;
+ partition@0 {
+ label = "u-boot-nand";
+ reg = <0 0080000>;
+ };
+ partition@80000 {
+ label = "kernel-nand";
+ reg = <0080000 0180000>;
+ };
+ partition@200000 {
+ label = "filesystem";
+ reg = <0200000 1e00000>;
+ };
+ };
nor_flash@0,0 {
compatible = "amd,s29gl256n", "cfi-flash";
bank-width = <2>;
^ permalink raw reply
* [PATCH 1/2] PowerPC: Add 44x NDFC device-tree aware support
From: Valentine Barshak @ 2007-10-26 16:39 UTC (permalink / raw)
To: linuxppc-dev; +Cc: tglx, sr, linux-mtd
In-Reply-To: <20071026163058.GA11354@ru.mvista.com>
This adds device-tree aware PowerPC 44x NDFC (NAND Flash Controller) driver.
The code is based on the original ndfc.c driver by Thomas Gleixner.
The major difference is that here we try to handle all chips found as one mtd
device instead of having a separate one on each chip.
The partition handling code is based on the physmap_of one.
The the first 4 bits of the "bank-mask" property show which of the 4 NDFC
banks have chips attached. The "bank-width" property is 1 for 8-bit flash
and 2 for a 16-bit one.
Signed-off-by: Thomas Gleixner <tglx@linutronix.de>
Signed-off-by: Valentine Barshak <vbarshak@ru.mvista.com>
---
drivers/mtd/nand/Kconfig | 7
drivers/mtd/nand/Makefile | 1
drivers/mtd/nand/ndfc_of.c | 449 +++++++++++++++++++++++++++++++++++++++++++++
include/linux/mtd/ndfc.h | 4
4 files changed, 461 insertions(+)
diff -pruN linux-2.6.orig/drivers/mtd/nand/Kconfig linux-2.6/drivers/mtd/nand/Kconfig
--- linux-2.6.orig/drivers/mtd/nand/Kconfig 2007-10-25 19:20:05.000000000 +0400
+++ linux-2.6/drivers/mtd/nand/Kconfig 2007-10-26 16:16:20.000000000 +0400
@@ -158,6 +158,13 @@ config MTD_NAND_NDFC
help
NDFC Nand Flash Controllers are integrated in IBM/AMCC's 4xx SoCs
+config MTD_NAND_NDFC_OF
+ tristate "NDFC OF Nand Flash Controller"
+ depends on 44x
+ select MTD_NAND_ECC_SMC
+ help
+ NDFC OF Nand Flash Controllers are integrated in PowerPC44x SoCs
+
config MTD_NAND_S3C2410_CLKSTOP
bool "S3C2410 NAND IDLE clock stop"
depends on MTD_NAND_S3C2410
diff -pruN linux-2.6.orig/drivers/mtd/nand/Makefile linux-2.6/drivers/mtd/nand/Makefile
--- linux-2.6.orig/drivers/mtd/nand/Makefile 2007-10-25 19:20:05.000000000 +0400
+++ linux-2.6/drivers/mtd/nand/Makefile 2007-10-26 16:16:20.000000000 +0400
@@ -24,6 +24,7 @@ obj-$(CONFIG_MTD_NAND_TS7250) += ts7250
obj-$(CONFIG_MTD_NAND_NANDSIM) += nandsim.o
obj-$(CONFIG_MTD_NAND_CS553X) += cs553x_nand.o
obj-$(CONFIG_MTD_NAND_NDFC) += ndfc.o
+obj-$(CONFIG_MTD_NAND_NDFC_OF) += ndfc_of.o
obj-$(CONFIG_MTD_NAND_AT91) += at91_nand.o
obj-$(CONFIG_MTD_NAND_CM_X270) += cmx270_nand.o
obj-$(CONFIG_MTD_NAND_BASLER_EXCITE) += excite_nandflash.o
diff -pruN linux-2.6.orig/drivers/mtd/nand/ndfc_of.c linux-2.6/drivers/mtd/nand/ndfc_of.c
--- linux-2.6.orig/drivers/mtd/nand/ndfc_of.c 1970-01-01 03:00:00.000000000 +0300
+++ linux-2.6/drivers/mtd/nand/ndfc_of.c 2007-10-26 17:28:57.000000000 +0400
@@ -0,0 +1,449 @@
+/*
+ * PowerPC 44x NDFC (NanD Flash Controller) driver
+ * with OF device tree support.
+ *
+ * Based on the original ndfc driver by Thomas Gleixner
+ *
+ * Copyright 2006 IBM
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.
+ *
+ */
+#include <linux/module.h>
+#include <linux/mtd/nand.h>
+#include <linux/mtd/nand_ecc.h>
+#include <linux/mtd/partitions.h>
+#include <linux/mtd/ndfc.h>
+#include <linux/mtd/mtd.h>
+#include <linux/of.h>
+#include <linux/of_platform.h>
+
+#include <asm/io.h>
+
+
+struct of_ndfc {
+ __iomem void *base;
+ struct resource *res;
+ unsigned bank_width;
+ unsigned chip_cnt;
+ unsigned char chip_map[NDFC_MAX_BANKS];
+ struct nand_hw_control control;
+ struct nand_chip chip;
+ struct mtd_info mtd;
+#ifdef CONFIG_MTD_PARTITIONS
+ struct mtd_partition *parts;
+#endif
+};
+
+static inline u32 ndfc_raw_readl(struct of_ndfc *ndfc, u32 off)
+{
+ return __raw_readl(ndfc->base + off);
+}
+
+static inline void ndfc_raw_writel(struct of_ndfc *ndfc, u32 off, u32 val)
+{
+ __raw_writel(val, ndfc->base + off);
+}
+
+static inline void ndfc_writel(struct of_ndfc *ndfc, u32 off, u32 val)
+{
+ writel(val, ndfc->base + off);
+}
+
+static void ndfc_select_chip(struct mtd_info *mtd, int chip)
+{
+ struct nand_chip *this = mtd->priv;
+ struct of_ndfc *ndfc = this->priv;
+ uint32_t ccr;
+
+ ccr = ndfc_raw_readl(ndfc, NDFC_CCR);
+ if ((chip >= 0) && (chip < ndfc->chip_cnt)) {
+ ccr &= ~NDFC_CCR_BS_MASK;
+ ccr |= NDFC_CCR_BS(ndfc->chip_map[chip]);
+ } else
+ ccr |= NDFC_CCR_RESET_CE;
+ ndfc_raw_writel(ndfc, NDFC_CCR, ccr);
+}
+
+static void ndfc_hwcontrol(struct mtd_info *mtd, int cmd, unsigned int ctrl)
+{
+ struct nand_chip *this = mtd->priv;
+ struct of_ndfc *ndfc = this->priv;
+
+ if (cmd == NAND_CMD_NONE)
+ return;
+
+ if (ctrl & NAND_CLE)
+ ndfc_writel(ndfc, NDFC_CMD, cmd & 0xff);
+ else
+ ndfc_writel(ndfc, NDFC_ALE, cmd & 0xff);
+}
+
+static int ndfc_ready(struct mtd_info *mtd)
+{
+ struct nand_chip *this = mtd->priv;
+ struct of_ndfc *ndfc = this->priv;
+
+ return ndfc_raw_readl(ndfc, NDFC_STAT) & NDFC_STAT_IS_READY;
+}
+
+static void ndfc_enable_hwecc(struct mtd_info *mtd, int mode)
+{
+ uint32_t ccr;
+ struct nand_chip *this = mtd->priv;
+ struct of_ndfc *ndfc = this->priv;
+
+ ccr = ndfc_raw_readl(ndfc, NDFC_CCR);
+ ccr |= NDFC_CCR_RESET_ECC;
+ ndfc_raw_writel(ndfc, NDFC_CCR, ccr);
+ wmb();
+}
+
+
+static int ndfc_calculate_ecc(struct mtd_info *mtd,
+ const u_char *dat, u_char *ecc_code)
+{
+ uint32_t ecc;
+ struct nand_chip *this = mtd->priv;
+ struct of_ndfc *ndfc = this->priv;
+ uint8_t *p = (uint8_t *)&ecc;
+
+ wmb();
+ ecc = ndfc_raw_readl(ndfc, NDFC_ECC);
+ ecc_code[0] = p[1];
+ ecc_code[1] = p[2];
+ ecc_code[2] = p[3];
+
+ return 0;
+}
+
+
+/*
+ * Speedups for buffer read/write/verify
+ *
+ * NDFC allows 32bit read/write of data. So we can speed up the buffer
+ * functions. No further checking, as nand_base will always read/write
+ * page aligned.
+ */
+static void ndfc_read_buf(struct mtd_info *mtd, uint8_t *buf, int len)
+{
+ struct nand_chip *this = mtd->priv;
+ struct of_ndfc *ndfc = this->priv;
+ uint32_t *p = (uint32_t *) buf;
+
+ for(;len > 0; len -= 4)
+ *p++ = ndfc_raw_readl(ndfc, NDFC_DATA);
+}
+
+static void ndfc_write_buf(struct mtd_info *mtd, const uint8_t *buf, int len)
+{
+ struct nand_chip *this = mtd->priv;
+ struct of_ndfc *ndfc = this->priv;
+ uint32_t *p = (uint32_t *) buf;
+
+ for(;len > 0; len -= 4)
+ ndfc_raw_writel(ndfc, NDFC_DATA, *p++);
+}
+
+static int ndfc_verify_buf(struct mtd_info *mtd, const uint8_t *buf, int len)
+{
+ struct nand_chip *this = mtd->priv;
+ struct of_ndfc *ndfc = this->priv;
+ uint32_t *p = (uint32_t *) buf;
+
+ for(;len > 0; len -= 4)
+ if (*p++ != ndfc_raw_readl(ndfc, NDFC_DATA))
+ return -EFAULT;
+ return 0;
+}
+
+
+
+static void ndfc_chip_init(struct nand_chip *chip,
+ struct of_ndfc *ndfc)
+{
+ chip->IO_ADDR_R = ndfc->base + NDFC_DATA;
+ chip->IO_ADDR_W = ndfc->base + NDFC_DATA;
+ chip->cmd_ctrl = ndfc_hwcontrol;
+ chip->dev_ready = ndfc_ready;
+ chip->select_chip = ndfc_select_chip;
+ chip->chip_delay = 50;
+ chip->priv = ndfc;
+ if (ndfc->bank_width == 2)
+ chip->options |= NAND_BUSWIDTH_16;
+ chip->controller = &ndfc->control;
+ chip->read_buf = ndfc_read_buf;
+ chip->write_buf = ndfc_write_buf;
+ chip->verify_buf = ndfc_verify_buf;
+ chip->ecc.correct = nand_correct_data;
+ chip->ecc.hwctl = ndfc_enable_hwecc;
+ chip->ecc.calculate = ndfc_calculate_ecc;
+ chip->ecc.mode = NAND_ECC_HW;
+ chip->ecc.size = 256;
+ chip->ecc.bytes = 3;
+ ndfc->mtd.priv = chip;
+ ndfc->mtd.owner = THIS_MODULE;
+}
+
+
+#ifdef CONFIG_MTD_PARTITIONS
+#define OF_FLASH_PARTS(ndfc) ((ndfc)->parts)
+
+static int __devinit parse_partitions(struct of_ndfc *ndfc,
+ struct of_device *dev)
+{
+ const char *partname;
+ static const char *part_probe_types[]
+ = { "cmdlinepart", "RedBoot", NULL };
+ struct device_node *dp = dev->node, *pp;
+ int nr_parts, i;
+
+ /* First look for RedBoot table or partitions on the command
+ * line, these take precedence over device tree information */
+ nr_parts = parse_mtd_partitions(&ndfc->mtd, part_probe_types,
+ &ndfc->parts, 0);
+ if (nr_parts > 0) {
+ add_mtd_partitions(&ndfc->mtd, ndfc->parts, nr_parts);
+ return 0;
+ }
+
+ /* First count the subnodes */
+ nr_parts = 0;
+ for (pp = dp->child; pp; pp = pp->sibling)
+ nr_parts++;
+
+ if (nr_parts == 0)
+ return 0;
+
+ ndfc->parts = kzalloc(nr_parts * sizeof(*ndfc->parts),
+ GFP_KERNEL);
+ if (!ndfc->parts)
+ return -ENOMEM;
+
+ for (pp = dp->child, i = 0; pp; pp = pp->sibling, i++) {
+ const u32 *reg;
+ int len;
+
+ reg = of_get_property(pp, "reg", &len);
+ if (!reg || (len != 2*sizeof(u32))) {
+ dev_err(&dev->dev, "Invalid 'reg' on %s\n",
+ dp->full_name);
+ kfree(ndfc->parts);
+ ndfc->parts = NULL;
+ return -EINVAL;
+ }
+ ndfc->parts[i].offset = reg[0];
+ ndfc->parts[i].size = reg[1];
+
+ partname = of_get_property(pp, "label", &len);
+ if (!partname)
+ partname = of_get_property(pp, "name", &len);
+ ndfc->parts[i].name = (char *)partname;
+
+ if (of_get_property(pp, "read-only", &len))
+ ndfc->parts[i].mask_flags = MTD_WRITEABLE;
+ }
+
+ return nr_parts;
+}
+#else /* MTD_PARTITIONS */
+#define OF_FLASH_PARTS(ndfc) (0)
+#define parse_partitions(ndfc, dev) (0)
+#endif /* MTD_PARTITIONS */
+
+
+static int of_ndfc_remove(struct of_device *dev)
+{
+ struct of_ndfc *ndfc;
+
+ ndfc = dev_get_drvdata(&dev->dev);
+ if (!ndfc)
+ return 0;
+
+ if (OF_FLASH_PARTS(ndfc)) {
+ del_mtd_partitions(&ndfc->mtd);
+ kfree(OF_FLASH_PARTS(ndfc));
+ } else {
+ del_mtd_device(&ndfc->mtd);
+ }
+ nand_release(&ndfc->mtd);
+
+ dev_set_drvdata(&dev->dev, NULL);
+
+ if (ndfc->base)
+ iounmap(ndfc->base);
+
+ if (ndfc->res) {
+ release_resource(ndfc->res);
+ kfree(ndfc->res);
+ }
+
+ kfree(ndfc);
+
+ return 0;
+}
+
+
+static int __devinit ndfc_map_banks(struct of_ndfc *ndfc, const u32 *mask)
+{
+ unsigned cnt, i, tmp;
+ uint32_t bcr;
+
+ if (!ndfc || !mask)
+ return -EINVAL;
+
+ /* Disable all banks */
+ for (cnt = 0; cnt < NDFC_MAX_BANKS; cnt++) {
+ ndfc_raw_writel(ndfc, NDFC_BCFG0 + (cnt << 2), 0);
+ }
+
+ /* Enable bank and set default RE/WE/CE timings */
+ bcr = NDFC_BxCFG_EN | NDFC_BxCFG_RR(2) | NDFC_BxCFG_RWH(2) |
+ NDFC_BxCFG_RWP(2) | NDFC_BxCFG_CRW(2);
+ if (ndfc->bank_width == 2)
+ bcr |= NDFC_BxCFG_SZ_16BIT;
+
+ cnt = 0;
+ tmp = *mask;
+ while ((i = ffs(tmp)) && (cnt < NDFC_MAX_BANKS)) {
+ i--;
+ tmp &= ~(1 << i);
+ ndfc->chip_map[cnt++] = i;
+ ndfc_raw_writel(ndfc, NDFC_BCFG0 + (i << 2), bcr);
+ }
+ ndfc->chip_cnt = cnt;
+ return cnt;
+}
+
+
+static int __devinit of_ndfc_probe(struct of_device *dev,
+ const struct of_device_id *match)
+{
+ struct device_node *dp = dev->node;
+ struct resource res;
+ struct of_ndfc *ndfc;
+ const u32 *prop;
+ resource_size_t rlen;
+ int err;
+
+ err = -ENXIO;
+ if (of_address_to_resource(dp, 0, &res)) {
+ dev_err(&dev->dev, "can't get IO address from device tree\n");
+ goto err_out;
+ }
+
+ dev_dbg(&dev->dev, "regs: %.8llx-%.8llx\n",
+ (unsigned long long)res.start, (unsigned long long)res.end);
+
+ ndfc = kzalloc(sizeof(struct of_ndfc), GFP_KERNEL);
+ if (!ndfc) {
+ err = -ENOMEM;
+ goto err_out;
+ }
+
+ rlen = res.end - res.start + 1;
+ ndfc->res = request_mem_region(res.start, rlen, dev->dev.bus_id);
+ if (!ndfc->res) {
+ err = -EBUSY;
+ goto err_free_out;
+ }
+
+ ndfc->base = ioremap(res.start, rlen);
+ if (!ndfc->base) {
+ err = -ENXIO;
+ goto err_rel_out;
+ }
+
+ spin_lock_init(&ndfc->control.lock);
+ init_waitqueue_head(&ndfc->control.wq);
+
+ prop = of_get_property(dp, "bank-width", NULL);
+ ndfc->bank_width = ((prop) && (*prop) == 2) ? 2 : 1;
+
+ prop = of_get_property(dp, "bank-mask", NULL);
+ err = ndfc_map_banks(ndfc, prop);
+ if (err <= 0) {
+ dev_err(&dev->dev, "no banks found\n");
+ err = -ENODEV;
+ goto err_unmap_out;
+ }
+
+ ndfc_chip_init(&ndfc->chip, ndfc);
+ dev_set_drvdata(&dev->dev, ndfc);
+
+ dev_info(&dev->dev, "NDFC driver initialized. Chip-Rev: 0x%08x\n",
+ ndfc_raw_readl(ndfc, NDFC_REVID));
+
+ err = nand_scan_ident(&ndfc->mtd, ndfc->chip_cnt);
+ if (err)
+ goto err_dat_out;
+
+ if ((ndfc->mtd.writesize != 2048) && (ndfc->mtd.writesize != 512)) {
+ dev_err(&dev->dev, "unexpected NAND flash writesize %d",
+ ndfc->mtd.writesize);
+ goto err_dat_out;
+ }
+
+ err = nand_scan_tail(&ndfc->mtd);
+ if (err)
+ goto err_dat_out;
+
+ err = parse_partitions(ndfc, dev);
+ if (err < 0)
+ goto err_dat_out;
+
+ if (err > 0)
+ add_mtd_partitions(&ndfc->mtd, OF_FLASH_PARTS(ndfc), err);
+ else
+ add_mtd_device(&ndfc->mtd);
+
+ return 0;
+
+err_dat_out:
+ dev_set_drvdata(&dev->dev, NULL);
+err_unmap_out:
+ iounmap(ndfc->base);
+err_rel_out:
+ release_resource(ndfc->res);
+ kfree(ndfc->res);
+err_free_out:
+ kfree(ndfc);
+err_out:
+ return err;
+}
+
+static struct of_device_id of_ndfc_match[] = {
+ {
+ .compatible = "ibm,ndfc",
+ },
+ { },
+};
+MODULE_DEVICE_TABLE(of, of_ndfc_match);
+
+static struct of_platform_driver of_ndfc_driver = {
+ .name = "of-ndfc",
+ .match_table = of_ndfc_match,
+ .probe = of_ndfc_probe,
+ .remove = of_ndfc_remove,
+};
+
+static int __init of_ndfc_init(void)
+{
+ return of_register_platform_driver(&of_ndfc_driver);
+}
+
+static void __exit of_ndfc_exit(void)
+{
+ of_unregister_platform_driver(&of_ndfc_driver);
+}
+
+module_init(of_ndfc_init);
+module_exit(of_ndfc_exit);
+
+MODULE_LICENSE("GPL");
+
+
+MODULE_DESCRIPTION("OF driver for NDFC");
diff -pruN linux-2.6.orig/include/linux/mtd/ndfc.h linux-2.6/include/linux/mtd/ndfc.h
--- linux-2.6.orig/include/linux/mtd/ndfc.h 2007-10-25 19:20:42.000000000 +0400
+++ linux-2.6/include/linux/mtd/ndfc.h 2007-10-26 16:19:42.000000000 +0400
@@ -52,6 +52,10 @@
#define NDFC_BxCFG_SZ_MASK 0x08000000 /* Bank Size */
#define NDFC_BxCFG_SZ_8BIT 0x00000000 /* 8bit */
#define NDFC_BxCFG_SZ_16BIT 0x08000000 /* 16bit */
+#define NDFC_BxCFG_RR(x) (((x) & 0x7) << 0)
+#define NDFC_BxCFG_RWH(x) (((x) & 0x7) << 4)
+#define NDFC_BxCFG_RWP(x) (((x) & 0x7) << 8)
+#define NDFC_BxCFG_CRW(x) (((x) & 0x7) << 12)
#define NDFC_MAX_BANKS 4
^ permalink raw reply
* [PATCH 0/2] PowerPC: Add 44x NDFC device-tree aware support
From: Valentine Barshak @ 2007-10-26 16:30 UTC (permalink / raw)
To: linuxppc-dev; +Cc: tglx, sr, linux-mtd
I've worked in parallel with Stefan Roese on the new OF NDFC support.
This version (as well as Stefan's) is based on the original NDFC driver by Thomas Gleixner.
The major difference is that the original implements each chip connected NDFC banks as a
separate MTD device. Here I try to have one MTD device spread on all chips found.
However, the chips should have equal ID's and sizes, but I've never seen several different
chips attached to single ndfc. I'm adding ndfc_of as a separate file, since some other changes
have also been made (e.g. all i/o is made with ndfc_readl/writel inline functions).
The original version didn't handle driver removal well (it never calls del_mtd...),it's
corrected here. I decided not to add ecc pos and oob layout to DTS, since we should be OK with
the default ones with NDFC.
This stuff has been tested on PowerPC 440EPx Sequoia board.
Any comments are greatly appreciated.
Thanks,
Valentine.
^ permalink raw reply
* [PATCH v4.3] FEC - fast ethernet controller for mpc52xx
From: Domen Puncer @ 2007-10-26 16:07 UTC (permalink / raw)
To: Jeff Garzik; +Cc: linuxppc-dev, netdev
In-Reply-To: <20071026141828.GA14900@xyzzy.farnsworth.org>
On 26/10/07 07:18 -0700, Dale Farnsworth wrote:
> On Fri, Oct 26, 2007 at 01:59:09PM +0200, Domen Puncer wrote:
> > +static irqreturn_t mpc52xx_fec_tx_interrupt(int irq, void *dev_id)
> > +{
> > + struct net_device *dev = dev_id;
> > + struct mpc52xx_fec_priv *priv = netdev_priv(dev);
> > +
> > + spin_lock(&priv->lock);
> > +
> > + while (bcom_buffer_done(priv->tx_dmatsk)) {
> > + struct sk_buff *skb;
> > + struct bcom_fec_bd *bd;
> > + skb = bcom_retrieve_buffer(priv->tx_dmatsk, NULL,
> > + (struct bcom_bd **)&bd);
> > + /* Here (and in rx routines) would be a good place for
> > + * dma_unmap_single(), but bcom doesn't return bcom_bd of the
> > + * finished transfer, and _unmap is empty on this platfrom.
> > + */
>
> Oops, you forgot to remove the above comment. :)
Argh!
Repost w/o the comment.
Sorry for receiving all this almost-spam.
>
> Otherwise,
> Acked-by: Dale Farnsworth <dale@farnsworth.org>
>
> Domen, thanks for all your work on this. It's good to see it finally go in.
>
> -Dale
--- again, use your scisors here ;-) ---
Driver for ethernet on mpc5200/mpc5200b SoCs (FEC).
Signed-off-by: Domen Puncer <domen.puncer@telargo.com>
Acked-by: Dale Farnsworth <dale@farnsworth.org>
---
drivers/net/Kconfig | 24
drivers/net/Makefile | 4
drivers/net/fec_mpc52xx.c | 1112 ++++++++++++++++++++++++++++++++++++++++++
drivers/net/fec_mpc52xx.h | 313 +++++++++++
drivers/net/fec_mpc52xx_phy.c | 198 +++++++
5 files changed, 1651 insertions(+)
Index: linux.git/drivers/net/Kconfig
===================================================================
--- linux.git.orig/drivers/net/Kconfig
+++ linux.git/drivers/net/Kconfig
@@ -1880,6 +1880,30 @@ config FEC2
Say Y here if you want to use the second built-in 10/100 Fast
ethernet controller on some Motorola ColdFire processors.
+config FEC_MPC52xx
+ tristate "MPC52xx FEC driver"
+ depends on PPC_MPC52xx
+ select PPC_BESTCOMM
+ select PPC_BESTCOMM_FEC
+ select CRC32
+ select PHYLIB
+ ---help---
+ This option enables support for the MPC5200's on-chip
+ Fast Ethernet Controller
+ If compiled as module, it will be called 'fec_mpc52xx.ko'.
+
+config FEC_MPC52xx_MDIO
+ bool "MPC52xx FEC MDIO bus driver"
+ depends on FEC_MPC52xx
+ default y
+ ---help---
+ The MPC5200's FEC can connect to the Ethernet either with
+ an external MII PHY chip or 10 Mbps 7-wire interface
+ (Motorola? industry standard).
+ If your board uses an external PHY connected to FEC, enable this.
+ If not sure, enable.
+ If compiled as module, it will be called 'fec_mpc52xx_phy.ko'.
+
config NE_H8300
tristate "NE2000 compatible support for H8/300"
depends on H8300
Index: linux.git/drivers/net/Makefile
===================================================================
--- linux.git.orig/drivers/net/Makefile
+++ linux.git/drivers/net/Makefile
@@ -96,6 +96,10 @@ obj-$(CONFIG_SHAPER) += shaper.o
obj-$(CONFIG_HP100) += hp100.o
obj-$(CONFIG_SMC9194) += smc9194.o
obj-$(CONFIG_FEC) += fec.o
+obj-$(CONFIG_FEC_MPC52xx) += fec_mpc52xx.o
+ifeq ($(CONFIG_FEC_MPC52xx_MDIO),y)
+ obj-$(CONFIG_FEC_MPC52xx) += fec_mpc52xx_phy.o
+endif
obj-$(CONFIG_68360_ENET) += 68360enet.o
obj-$(CONFIG_WD80x3) += wd.o 8390.o
obj-$(CONFIG_EL2) += 3c503.o 8390.o
Index: linux.git/drivers/net/fec_mpc52xx.c
===================================================================
--- /dev/null
+++ linux.git/drivers/net/fec_mpc52xx.c
@@ -0,0 +1,1112 @@
+/*
+ * Driver for the MPC5200 Fast Ethernet Controller
+ *
+ * Originally written by Dale Farnsworth <dfarnsworth@mvista.com> and
+ * now maintained by Sylvain Munaut <tnt@246tNt.com>
+ *
+ * Copyright (C) 2007 Domen Puncer, Telargo, Inc.
+ * Copyright (C) 2007 Sylvain Munaut <tnt@246tNt.com>
+ * Copyright (C) 2003-2004 MontaVista, Software, Inc.
+ *
+ * This file is licensed under the terms of the GNU General Public License
+ * version 2. This program is licensed "as is" without any warranty of any
+ * kind, whether express or implied.
+ *
+ */
+
+#include <linux/module.h>
+
+#include <linux/kernel.h>
+#include <linux/types.h>
+#include <linux/spinlock.h>
+#include <linux/errno.h>
+#include <linux/init.h>
+#include <linux/crc32.h>
+#include <linux/hardirq.h>
+#include <linux/delay.h>
+#include <linux/of_device.h>
+#include <linux/of_platform.h>
+
+#include <linux/netdevice.h>
+#include <linux/etherdevice.h>
+#include <linux/ethtool.h>
+#include <linux/skbuff.h>
+
+#include <asm/io.h>
+#include <asm/delay.h>
+#include <asm/mpc52xx.h>
+
+#include <sysdev/bestcomm/bestcomm.h>
+#include <sysdev/bestcomm/fec.h>
+
+#include "fec_mpc52xx.h"
+
+#define DRIVER_NAME "mpc52xx-fec"
+
+static irqreturn_t mpc52xx_fec_interrupt(int, void *);
+static irqreturn_t mpc52xx_fec_rx_interrupt(int, void *);
+static irqreturn_t mpc52xx_fec_tx_interrupt(int, void *);
+static void mpc52xx_fec_stop(struct net_device *dev);
+static void mpc52xx_fec_start(struct net_device *dev);
+static void mpc52xx_fec_reset(struct net_device *dev);
+
+static u8 mpc52xx_fec_mac_addr[6];
+module_param_array_named(mac, mpc52xx_fec_mac_addr, byte, NULL, 0);
+MODULE_PARM_DESC(mac, "six hex digits, ie. 0x1,0x2,0xc0,0x01,0xba,0xbe");
+
+#define MPC52xx_MESSAGES_DEFAULT ( NETIF_MSG_DRV | NETIF_MSG_PROBE | \
+ NETIF_MSG_LINK | NETIF_MSG_IFDOWN | NETIF_MSG_IFDOWN )
+static int debug = -1; /* the above default */
+module_param(debug, int, 0);
+MODULE_PARM_DESC(debug, "debugging messages level");
+
+static void mpc52xx_fec_tx_timeout(struct net_device *dev)
+{
+ dev_warn(&dev->dev, "transmit timed out\n");
+
+ mpc52xx_fec_reset(dev);
+
+ dev->stats.tx_errors++;
+
+ netif_wake_queue(dev);
+}
+
+static void mpc52xx_fec_set_paddr(struct net_device *dev, u8 *mac)
+{
+ struct mpc52xx_fec_priv *priv = netdev_priv(dev);
+ struct mpc52xx_fec __iomem *fec = priv->fec;
+
+ out_be32(&fec->paddr1, *(u32 *)(&mac[0]));
+ out_be32(&fec->paddr2, (*(u16 *)(&mac[4]) << 16) | FEC_PADDR2_TYPE);
+}
+
+static void mpc52xx_fec_get_paddr(struct net_device *dev, u8 *mac)
+{
+ struct mpc52xx_fec_priv *priv = netdev_priv(dev);
+ struct mpc52xx_fec __iomem *fec = priv->fec;
+
+ *(u32 *)(&mac[0]) = in_be32(&fec->paddr1);
+ *(u16 *)(&mac[4]) = in_be32(&fec->paddr2) >> 16;
+}
+
+static int mpc52xx_fec_set_mac_address(struct net_device *dev, void *addr)
+{
+ struct sockaddr *sock = addr;
+
+ memcpy(dev->dev_addr, sock->sa_data, dev->addr_len);
+
+ mpc52xx_fec_set_paddr(dev, sock->sa_data);
+ return 0;
+}
+
+static void mpc52xx_fec_free_rx_buffers(struct net_device *dev, struct bcom_task *s)
+{
+ while (!bcom_queue_empty(s)) {
+ struct bcom_fec_bd *bd;
+ struct sk_buff *skb;
+
+ skb = bcom_retrieve_buffer(s, NULL, (struct bcom_bd **)&bd);
+ dma_unmap_single(&dev->dev, bd->skb_pa, skb->len, DMA_FROM_DEVICE);
+ kfree_skb(skb);
+ }
+}
+
+static int mpc52xx_fec_alloc_rx_buffers(struct net_device *dev, struct bcom_task *rxtsk)
+{
+ while (!bcom_queue_full(rxtsk)) {
+ struct sk_buff *skb;
+ struct bcom_fec_bd *bd;
+
+ skb = dev_alloc_skb(FEC_RX_BUFFER_SIZE);
+ if (skb == NULL)
+ return -EAGAIN;
+
+ /* zero out the initial receive buffers to aid debugging */
+ memset(skb->data, 0, FEC_RX_BUFFER_SIZE);
+
+ bd = (struct bcom_fec_bd *)bcom_prepare_next_buffer(rxtsk);
+
+ bd->status = FEC_RX_BUFFER_SIZE;
+ bd->skb_pa = dma_map_single(&dev->dev, skb->data,
+ FEC_RX_BUFFER_SIZE, DMA_FROM_DEVICE);
+
+ bcom_submit_next_buffer(rxtsk, skb);
+ }
+
+ return 0;
+}
+
+/* based on generic_adjust_link from fs_enet-main.c */
+static void mpc52xx_fec_adjust_link(struct net_device *dev)
+{
+ struct mpc52xx_fec_priv *priv = netdev_priv(dev);
+ struct phy_device *phydev = priv->phydev;
+ int new_state = 0;
+
+ if (phydev->link != PHY_DOWN) {
+ if (phydev->duplex != priv->duplex) {
+ struct mpc52xx_fec __iomem *fec = priv->fec;
+ u32 rcntrl;
+ u32 tcntrl;
+
+ new_state = 1;
+ priv->duplex = phydev->duplex;
+
+ rcntrl = in_be32(&fec->r_cntrl);
+ tcntrl = in_be32(&fec->x_cntrl);
+
+ rcntrl &= ~FEC_RCNTRL_DRT;
+ tcntrl &= ~FEC_TCNTRL_FDEN;
+ if (phydev->duplex == DUPLEX_FULL)
+ tcntrl |= FEC_TCNTRL_FDEN; /* FD enable */
+ else
+ rcntrl |= FEC_RCNTRL_DRT; /* disable Rx on Tx (HD) */
+
+ out_be32(&fec->r_cntrl, rcntrl);
+ out_be32(&fec->x_cntrl, tcntrl);
+ }
+
+ if (phydev->speed != priv->speed) {
+ new_state = 1;
+ priv->speed = phydev->speed;
+ }
+
+ if (priv->link == PHY_DOWN) {
+ new_state = 1;
+ priv->link = phydev->link;
+ netif_schedule(dev);
+ netif_carrier_on(dev);
+ netif_start_queue(dev);
+ }
+
+ } else if (priv->link) {
+ new_state = 1;
+ priv->link = PHY_DOWN;
+ priv->speed = 0;
+ priv->duplex = -1;
+ netif_stop_queue(dev);
+ netif_carrier_off(dev);
+ }
+
+ if (new_state && netif_msg_link(priv))
+ phy_print_status(phydev);
+}
+
+static int mpc52xx_fec_init_phy(struct net_device *dev)
+{
+ struct mpc52xx_fec_priv *priv = netdev_priv(dev);
+ struct phy_device *phydev;
+ char phy_id[BUS_ID_SIZE];
+
+ snprintf(phy_id, BUS_ID_SIZE, PHY_ID_FMT,
+ (unsigned int)dev->base_addr, priv->phy_addr);
+
+ priv->link = PHY_DOWN;
+ priv->speed = 0;
+ priv->duplex = -1;
+
+ phydev = phy_connect(dev, phy_id, &mpc52xx_fec_adjust_link, 0, PHY_INTERFACE_MODE_MII);
+ if (IS_ERR(phydev)) {
+ dev_err(&dev->dev, "phy_connect failed\n");
+ return PTR_ERR(phydev);
+ }
+ dev_info(&dev->dev, "attached phy %i to driver %s\n",
+ phydev->addr, phydev->drv->name);
+
+ priv->phydev = phydev;
+
+ return 0;
+}
+
+static int mpc52xx_fec_phy_start(struct net_device *dev)
+{
+ struct mpc52xx_fec_priv *priv = netdev_priv(dev);
+ int err;
+
+ if (!priv->has_phy)
+ return 0;
+
+ err = mpc52xx_fec_init_phy(dev);
+ if (err) {
+ dev_err(&dev->dev, "mpc52xx_fec_init_phy failed\n");
+ return err;
+ }
+
+ /* reset phy - this also wakes it from PDOWN */
+ phy_write(priv->phydev, MII_BMCR, BMCR_RESET);
+ phy_start(priv->phydev);
+
+ return 0;
+}
+
+static void mpc52xx_fec_phy_stop(struct net_device *dev)
+{
+ struct mpc52xx_fec_priv *priv = netdev_priv(dev);
+
+ if (!priv->has_phy)
+ return;
+
+ phy_disconnect(priv->phydev);
+ /* power down phy */
+ phy_stop(priv->phydev);
+ phy_write(priv->phydev, MII_BMCR, BMCR_PDOWN);
+}
+
+static int mpc52xx_fec_phy_mii_ioctl(struct mpc52xx_fec_priv *priv,
+ struct mii_ioctl_data *mii_data, int cmd)
+{
+ if (!priv->has_phy)
+ return -ENOTSUPP;
+
+ return phy_mii_ioctl(priv->phydev, mii_data, cmd);
+}
+
+static void mpc52xx_fec_phy_hw_init(struct mpc52xx_fec_priv *priv)
+{
+ struct mpc52xx_fec __iomem *fec = priv->fec;
+
+ if (!priv->has_phy)
+ return;
+
+ out_be32(&fec->mii_speed, priv->phy_speed);
+}
+
+static int mpc52xx_fec_open(struct net_device *dev)
+{
+ struct mpc52xx_fec_priv *priv = netdev_priv(dev);
+ int err = -EBUSY;
+
+ if (request_irq(dev->irq, &mpc52xx_fec_interrupt, IRQF_SHARED,
+ DRIVER_NAME "_ctrl", dev)) {
+ dev_err(&dev->dev, "ctrl interrupt request failed\n");
+ goto out;
+ }
+ if (request_irq(priv->r_irq, &mpc52xx_fec_rx_interrupt, 0,
+ DRIVER_NAME "_rx", dev)) {
+ dev_err(&dev->dev, "rx interrupt request failed\n");
+ goto free_ctrl_irq;
+ }
+ if (request_irq(priv->t_irq, &mpc52xx_fec_tx_interrupt, 0,
+ DRIVER_NAME "_tx", dev)) {
+ dev_err(&dev->dev, "tx interrupt request failed\n");
+ goto free_2irqs;
+ }
+
+ bcom_fec_rx_reset(priv->rx_dmatsk);
+ bcom_fec_tx_reset(priv->tx_dmatsk);
+
+ err = mpc52xx_fec_alloc_rx_buffers(dev, priv->rx_dmatsk);
+ if (err) {
+ dev_err(&dev->dev, "mpc52xx_fec_alloc_rx_buffers failed\n");
+ goto free_irqs;
+ }
+
+ err = mpc52xx_fec_phy_start(dev);
+ if (err)
+ goto free_skbs;
+
+ bcom_enable(priv->rx_dmatsk);
+ bcom_enable(priv->tx_dmatsk);
+
+ mpc52xx_fec_start(dev);
+
+ netif_start_queue(dev);
+
+ return 0;
+
+ free_skbs:
+ mpc52xx_fec_free_rx_buffers(dev, priv->rx_dmatsk);
+
+ free_irqs:
+ free_irq(priv->t_irq, dev);
+ free_2irqs:
+ free_irq(priv->r_irq, dev);
+ free_ctrl_irq:
+ free_irq(dev->irq, dev);
+ out:
+
+ return err;
+}
+
+static int mpc52xx_fec_close(struct net_device *dev)
+{
+ struct mpc52xx_fec_priv *priv = netdev_priv(dev);
+
+ netif_stop_queue(dev);
+
+ mpc52xx_fec_stop(dev);
+
+ mpc52xx_fec_free_rx_buffers(dev, priv->rx_dmatsk);
+
+ free_irq(dev->irq, dev);
+ free_irq(priv->r_irq, dev);
+ free_irq(priv->t_irq, dev);
+
+ mpc52xx_fec_phy_stop(dev);
+
+ return 0;
+}
+
+/* This will only be invoked if your driver is _not_ in XOFF state.
+ * What this means is that you need not check it, and that this
+ * invariant will hold if you make sure that the netif_*_queue()
+ * calls are done at the proper times.
+ */
+static int mpc52xx_fec_hard_start_xmit(struct sk_buff *skb, struct net_device *dev)
+{
+ struct mpc52xx_fec_priv *priv = netdev_priv(dev);
+ struct bcom_fec_bd *bd;
+
+ if (bcom_queue_full(priv->tx_dmatsk)) {
+ if (net_ratelimit())
+ dev_err(&dev->dev, "transmit queue overrun\n");
+ return 1;
+ }
+
+ spin_lock_irq(&priv->lock);
+ dev->trans_start = jiffies;
+
+ bd = (struct bcom_fec_bd *)
+ bcom_prepare_next_buffer(priv->tx_dmatsk);
+
+ bd->status = skb->len | BCOM_FEC_TX_BD_TFD | BCOM_FEC_TX_BD_TC;
+ bd->skb_pa = dma_map_single(&dev->dev, skb->data, skb->len, DMA_TO_DEVICE);
+
+ bcom_submit_next_buffer(priv->tx_dmatsk, skb);
+
+ if (bcom_queue_full(priv->tx_dmatsk)) {
+ netif_stop_queue(dev);
+ }
+
+ spin_unlock_irq(&priv->lock);
+
+ return 0;
+}
+
+/* This handles BestComm transmit task interrupts
+ */
+static irqreturn_t mpc52xx_fec_tx_interrupt(int irq, void *dev_id)
+{
+ struct net_device *dev = dev_id;
+ struct mpc52xx_fec_priv *priv = netdev_priv(dev);
+
+ spin_lock(&priv->lock);
+
+ while (bcom_buffer_done(priv->tx_dmatsk)) {
+ struct sk_buff *skb;
+ struct bcom_fec_bd *bd;
+ skb = bcom_retrieve_buffer(priv->tx_dmatsk, NULL,
+ (struct bcom_bd **)&bd);
+ dma_unmap_single(&dev->dev, bd->skb_pa, skb->len, DMA_TO_DEVICE);
+
+ dev_kfree_skb_irq(skb);
+ }
+
+ netif_wake_queue(dev);
+
+ spin_unlock(&priv->lock);
+
+ return IRQ_HANDLED;
+}
+
+static irqreturn_t mpc52xx_fec_rx_interrupt(int irq, void *dev_id)
+{
+ struct net_device *dev = dev_id;
+ struct mpc52xx_fec_priv *priv = netdev_priv(dev);
+
+ while (bcom_buffer_done(priv->rx_dmatsk)) {
+ struct sk_buff *skb;
+ struct sk_buff *rskb;
+ struct bcom_fec_bd *bd;
+ u32 status;
+
+ rskb = bcom_retrieve_buffer(priv->rx_dmatsk, &status,
+ (struct bcom_bd **)&bd);
+ dma_unmap_single(&dev->dev, bd->skb_pa, skb->len, DMA_FROM_DEVICE);
+
+ /* Test for errors in received frame */
+ if (status & BCOM_FEC_RX_BD_ERRORS) {
+ /* Drop packet and reuse the buffer */
+ bd = (struct bcom_fec_bd *)
+ bcom_prepare_next_buffer(priv->rx_dmatsk);
+
+ bd->status = FEC_RX_BUFFER_SIZE;
+ bd->skb_pa = dma_map_single(&dev->dev, rskb->data,
+ FEC_RX_BUFFER_SIZE, DMA_FROM_DEVICE);
+
+ bcom_submit_next_buffer(priv->rx_dmatsk, rskb);
+
+ dev->stats.rx_dropped++;
+
+ continue;
+ }
+
+ /* skbs are allocated on open, so now we allocate a new one,
+ * and remove the old (with the packet) */
+ skb = dev_alloc_skb(FEC_RX_BUFFER_SIZE);
+ if (skb) {
+ /* Process the received skb */
+ int length = status & BCOM_FEC_RX_BD_LEN_MASK;
+
+ skb_put(rskb, length - 4); /* length without CRC32 */
+
+ rskb->dev = dev;
+ rskb->protocol = eth_type_trans(rskb, dev);
+
+ netif_rx(rskb);
+ dev->last_rx = jiffies;
+ } else {
+ /* Can't get a new one : reuse the same & drop pkt */
+ dev_notice(&dev->dev, "Memory squeeze, dropping packet.\n");
+ dev->stats.rx_dropped++;
+
+ skb = rskb;
+ }
+
+ bd = (struct bcom_fec_bd *)
+ bcom_prepare_next_buffer(priv->rx_dmatsk);
+
+ bd->status = FEC_RX_BUFFER_SIZE;
+ bd->skb_pa = dma_map_single(&dev->dev, rskb->data,
+ FEC_RX_BUFFER_SIZE, DMA_FROM_DEVICE);
+
+ bcom_submit_next_buffer(priv->rx_dmatsk, skb);
+ }
+
+ return IRQ_HANDLED;
+}
+
+static irqreturn_t mpc52xx_fec_interrupt(int irq, void *dev_id)
+{
+ struct net_device *dev = dev_id;
+ struct mpc52xx_fec_priv *priv = netdev_priv(dev);
+ struct mpc52xx_fec __iomem *fec = priv->fec;
+ u32 ievent;
+
+ ievent = in_be32(&fec->ievent);
+
+ ievent &= ~FEC_IEVENT_MII; /* mii is handled separately */
+ if (!ievent)
+ return IRQ_NONE;
+
+ out_be32(&fec->ievent, ievent); /* clear pending events */
+
+ if (ievent & ~(FEC_IEVENT_RFIFO_ERROR | FEC_IEVENT_XFIFO_ERROR)) {
+ if (ievent & ~FEC_IEVENT_TFINT)
+ dev_dbg(&dev->dev, "ievent: %08x\n", ievent);
+ return IRQ_HANDLED;
+ }
+
+ if (net_ratelimit() && (ievent & FEC_IEVENT_RFIFO_ERROR))
+ dev_warn(&dev->dev, "FEC_IEVENT_RFIFO_ERROR\n");
+ if (net_ratelimit() && (ievent & FEC_IEVENT_XFIFO_ERROR))
+ dev_warn(&dev->dev, "FEC_IEVENT_XFIFO_ERROR\n");
+
+ mpc52xx_fec_reset(dev);
+
+ netif_wake_queue(dev);
+ return IRQ_HANDLED;
+}
+
+/*
+ * Get the current statistics.
+ * This may be called with the card open or closed.
+ */
+static struct net_device_stats *mpc52xx_fec_get_stats(struct net_device *dev)
+{
+ struct mpc52xx_fec_priv *priv = netdev_priv(dev);
+ struct net_device_stats *stats = &dev->stats;
+ struct mpc52xx_fec __iomem *fec = priv->fec;
+
+ stats->rx_bytes = in_be32(&fec->rmon_r_octets);
+ stats->rx_packets = in_be32(&fec->rmon_r_packets);
+ stats->rx_errors = in_be32(&fec->rmon_r_crc_align) +
+ in_be32(&fec->rmon_r_undersize) +
+ in_be32(&fec->rmon_r_oversize) +
+ in_be32(&fec->rmon_r_frag) +
+ in_be32(&fec->rmon_r_jab);
+
+ stats->tx_bytes = in_be32(&fec->rmon_t_octets);
+ stats->tx_packets = in_be32(&fec->rmon_t_packets);
+ stats->tx_errors = in_be32(&fec->rmon_t_crc_align) +
+ in_be32(&fec->rmon_t_undersize) +
+ in_be32(&fec->rmon_t_oversize) +
+ in_be32(&fec->rmon_t_frag) +
+ in_be32(&fec->rmon_t_jab);
+
+ stats->multicast = in_be32(&fec->rmon_r_mc_pkt);
+ stats->collisions = in_be32(&fec->rmon_t_col);
+
+ /* detailed rx_errors: */
+ stats->rx_length_errors = in_be32(&fec->rmon_r_undersize)
+ + in_be32(&fec->rmon_r_oversize)
+ + in_be32(&fec->rmon_r_frag)
+ + in_be32(&fec->rmon_r_jab);
+ stats->rx_over_errors = in_be32(&fec->r_macerr);
+ stats->rx_crc_errors = in_be32(&fec->ieee_r_crc);
+ stats->rx_frame_errors = in_be32(&fec->ieee_r_align);
+ stats->rx_fifo_errors = in_be32(&fec->rmon_r_drop);
+ stats->rx_missed_errors = in_be32(&fec->rmon_r_drop);
+
+ /* detailed tx_errors: */
+ stats->tx_aborted_errors = 0;
+ stats->tx_carrier_errors = in_be32(&fec->ieee_t_cserr);
+ stats->tx_fifo_errors = in_be32(&fec->rmon_t_drop);
+ stats->tx_heartbeat_errors = in_be32(&fec->ieee_t_sqe);
+ stats->tx_window_errors = in_be32(&fec->ieee_t_lcol);
+
+ return stats;
+}
+
+/*
+ * Read MIB counters in order to reset them,
+ * then zero all the stats fields in memory
+ */
+static void mpc52xx_fec_reset_stats(struct net_device *dev)
+{
+ struct mpc52xx_fec_priv *priv = netdev_priv(dev);
+ struct mpc52xx_fec __iomem *fec = priv->fec;
+
+ out_be32(&fec->mib_control, FEC_MIB_DISABLE);
+ memset_io(&fec->rmon_t_drop, 0, (__force u32)&fec->reserved10 -
+ (__force u32)&fec->rmon_t_drop);
+ out_be32(&fec->mib_control, 0);
+
+ memset(&dev->stats, 0, sizeof(dev->stats));
+}
+
+/*
+ * Set or clear the multicast filter for this adaptor.
+ */
+static void mpc52xx_fec_set_multicast_list(struct net_device *dev)
+{
+ struct mpc52xx_fec_priv *priv = netdev_priv(dev);
+ struct mpc52xx_fec __iomem *fec = priv->fec;
+ u32 rx_control;
+
+ rx_control = in_be32(&fec->r_cntrl);
+
+ if (dev->flags & IFF_PROMISC) {
+ rx_control |= FEC_RCNTRL_PROM;
+ out_be32(&fec->r_cntrl, rx_control);
+ } else {
+ rx_control &= ~FEC_RCNTRL_PROM;
+ out_be32(&fec->r_cntrl, rx_control);
+
+ if (dev->flags & IFF_ALLMULTI) {
+ out_be32(&fec->gaddr1, 0xffffffff);
+ out_be32(&fec->gaddr2, 0xffffffff);
+ } else {
+ u32 crc;
+ int i;
+ struct dev_mc_list *dmi;
+ u32 gaddr1 = 0x00000000;
+ u32 gaddr2 = 0x00000000;
+
+ dmi = dev->mc_list;
+ for (i=0; i<dev->mc_count; i++) {
+ crc = ether_crc_le(6, dmi->dmi_addr) >> 26;
+ if (crc >= 32)
+ gaddr1 |= 1 << (crc-32);
+ else
+ gaddr2 |= 1 << crc;
+ dmi = dmi->next;
+ }
+ out_be32(&fec->gaddr1, gaddr1);
+ out_be32(&fec->gaddr2, gaddr2);
+ }
+ }
+}
+
+/**
+ * mpc52xx_fec_hw_init
+ * @dev: network device
+ *
+ * Setup various hardware setting, only needed once on start
+ */
+static void mpc52xx_fec_hw_init(struct net_device *dev)
+{
+ struct mpc52xx_fec_priv *priv = netdev_priv(dev);
+ struct mpc52xx_fec __iomem *fec = priv->fec;
+ int i;
+
+ /* Whack a reset. We should wait for this. */
+ out_be32(&fec->ecntrl, FEC_ECNTRL_RESET);
+ for (i = 0; i < FEC_RESET_DELAY; ++i) {
+ if ((in_be32(&fec->ecntrl) & FEC_ECNTRL_RESET) == 0)
+ break;
+ udelay(1);
+ }
+ if (i == FEC_RESET_DELAY)
+ dev_err(&dev->dev, "FEC Reset timeout!\n");
+
+ /* set pause to 0x20 frames */
+ out_be32(&fec->op_pause, FEC_OP_PAUSE_OPCODE | 0x20);
+
+ /* high service request will be deasserted when there's < 7 bytes in fifo
+ * low service request will be deasserted when there's < 4*7 bytes in fifo
+ */
+ out_be32(&fec->rfifo_cntrl, FEC_FIFO_CNTRL_FRAME | FEC_FIFO_CNTRL_LTG_7);
+ out_be32(&fec->tfifo_cntrl, FEC_FIFO_CNTRL_FRAME | FEC_FIFO_CNTRL_LTG_7);
+
+ /* alarm when <= x bytes in FIFO */
+ out_be32(&fec->rfifo_alarm, 0x0000030c);
+ out_be32(&fec->tfifo_alarm, 0x00000100);
+
+ /* begin transmittion when 256 bytes are in FIFO (or EOF or FIFO full) */
+ out_be32(&fec->x_wmrk, FEC_FIFO_WMRK_256B);
+
+ /* enable crc generation */
+ out_be32(&fec->xmit_fsm, FEC_XMIT_FSM_APPEND_CRC | FEC_XMIT_FSM_ENABLE_CRC);
+ out_be32(&fec->iaddr1, 0x00000000); /* No individual filter */
+ out_be32(&fec->iaddr2, 0x00000000); /* No individual filter */
+
+ /* set phy speed.
+ * this can't be done in phy driver, since it needs to be called
+ * before fec stuff (even on resume) */
+ mpc52xx_fec_phy_hw_init(priv);
+}
+
+/**
+ * mpc52xx_fec_start
+ * @dev: network device
+ *
+ * This function is called to start or restart the FEC during a link
+ * change. This happens on fifo errors or when switching between half
+ * and full duplex.
+ */
+static void mpc52xx_fec_start(struct net_device *dev)
+{
+ struct mpc52xx_fec_priv *priv = netdev_priv(dev);
+ struct mpc52xx_fec __iomem *fec = priv->fec;
+ u32 rcntrl;
+ u32 tcntrl;
+ u32 tmp;
+
+ /* clear sticky error bits */
+ tmp = FEC_FIFO_STATUS_ERR | FEC_FIFO_STATUS_UF | FEC_FIFO_STATUS_OF;
+ out_be32(&fec->rfifo_status, in_be32(&fec->rfifo_status) & tmp);
+ out_be32(&fec->tfifo_status, in_be32(&fec->tfifo_status) & tmp);
+
+ /* FIFOs will reset on mpc52xx_fec_enable */
+ out_be32(&fec->reset_cntrl, FEC_RESET_CNTRL_ENABLE_IS_RESET);
+
+ /* Set station address. */
+ mpc52xx_fec_set_paddr(dev, dev->dev_addr);
+
+ mpc52xx_fec_set_multicast_list(dev);
+
+ /* set max frame len, enable flow control, select mii mode */
+ rcntrl = FEC_RX_BUFFER_SIZE << 16; /* max frame length */
+ rcntrl |= FEC_RCNTRL_FCE;
+
+ if (priv->has_phy)
+ rcntrl |= FEC_RCNTRL_MII_MODE;
+
+ if (priv->duplex == DUPLEX_FULL)
+ tcntrl = FEC_TCNTRL_FDEN; /* FD enable */
+ else {
+ rcntrl |= FEC_RCNTRL_DRT; /* disable Rx on Tx (HD) */
+ tcntrl = 0;
+ }
+ out_be32(&fec->r_cntrl, rcntrl);
+ out_be32(&fec->x_cntrl, tcntrl);
+
+ /* Clear any outstanding interrupt. */
+ out_be32(&fec->ievent, 0xffffffff);
+
+ /* Enable interrupts we wish to service. */
+ out_be32(&fec->imask, FEC_IMASK_ENABLE);
+
+ /* And last, enable the transmit and receive processing. */
+ out_be32(&fec->ecntrl, FEC_ECNTRL_ETHER_EN);
+ out_be32(&fec->r_des_active, 0x01000000);
+}
+
+/**
+ * mpc52xx_fec_stop
+ * @dev: network device
+ *
+ * stop all activity on fec and empty dma buffers
+ */
+static void mpc52xx_fec_stop(struct net_device *dev)
+{
+ struct mpc52xx_fec_priv *priv = netdev_priv(dev);
+ struct mpc52xx_fec __iomem *fec = priv->fec;
+ unsigned long timeout;
+
+ /* disable all interrupts */
+ out_be32(&fec->imask, 0);
+
+ /* Disable the rx task. */
+ bcom_disable(priv->rx_dmatsk);
+
+ /* Wait for tx queue to drain, but only if we're in process context */
+ if (!in_interrupt()) {
+ timeout = jiffies + msecs_to_jiffies(2000);
+ while (time_before(jiffies, timeout) &&
+ !bcom_queue_empty(priv->tx_dmatsk))
+ msleep(100);
+
+ if (time_after_eq(jiffies, timeout))
+ dev_err(&dev->dev, "queues didn't drain\n");
+#if 1
+ if (time_after_eq(jiffies, timeout)) {
+ dev_err(&dev->dev, " tx: index: %i, outdex: %i\n",
+ priv->tx_dmatsk->index,
+ priv->tx_dmatsk->outdex);
+ dev_err(&dev->dev, " rx: index: %i, outdex: %i\n",
+ priv->rx_dmatsk->index,
+ priv->rx_dmatsk->outdex);
+ }
+#endif
+ }
+
+ bcom_disable(priv->tx_dmatsk);
+
+ /* Stop FEC */
+ out_be32(&fec->ecntrl, in_be32(&fec->ecntrl) & ~FEC_ECNTRL_ETHER_EN);
+
+ return;
+}
+
+/* reset fec and bestcomm tasks */
+static void mpc52xx_fec_reset(struct net_device *dev)
+{
+ struct mpc52xx_fec_priv *priv = netdev_priv(dev);
+ struct mpc52xx_fec __iomem *fec = priv->fec;
+
+ mpc52xx_fec_stop(dev);
+
+ out_be32(&fec->rfifo_status, in_be32(&fec->rfifo_status));
+ out_be32(&fec->reset_cntrl, FEC_RESET_CNTRL_RESET_FIFO);
+
+ mpc52xx_fec_free_rx_buffers(dev, priv->rx_dmatsk);
+
+ mpc52xx_fec_hw_init(dev);
+
+ phy_stop(priv->phydev);
+ phy_write(priv->phydev, MII_BMCR, BMCR_RESET);
+ phy_start(priv->phydev);
+
+ bcom_fec_rx_reset(priv->rx_dmatsk);
+ bcom_fec_tx_reset(priv->tx_dmatsk);
+
+ mpc52xx_fec_alloc_rx_buffers(dev, priv->rx_dmatsk);
+
+ bcom_enable(priv->rx_dmatsk);
+ bcom_enable(priv->tx_dmatsk);
+
+ mpc52xx_fec_start(dev);
+}
+
+
+/* ethtool interface */
+static void mpc52xx_fec_get_drvinfo(struct net_device *dev,
+ struct ethtool_drvinfo *info)
+{
+ strcpy(info->driver, DRIVER_NAME);
+}
+
+static int mpc52xx_fec_get_settings(struct net_device *dev, struct ethtool_cmd *cmd)
+{
+ struct mpc52xx_fec_priv *priv = netdev_priv(dev);
+ return phy_ethtool_gset(priv->phydev, cmd);
+}
+
+static int mpc52xx_fec_set_settings(struct net_device *dev, struct ethtool_cmd *cmd)
+{
+ struct mpc52xx_fec_priv *priv = netdev_priv(dev);
+ return phy_ethtool_sset(priv->phydev, cmd);
+}
+
+static u32 mpc52xx_fec_get_msglevel(struct net_device *dev)
+{
+ struct mpc52xx_fec_priv *priv = netdev_priv(dev);
+ return priv->msg_enable;
+}
+
+static void mpc52xx_fec_set_msglevel(struct net_device *dev, u32 level)
+{
+ struct mpc52xx_fec_priv *priv = netdev_priv(dev);
+ priv->msg_enable = level;
+}
+
+static const struct ethtool_ops mpc52xx_fec_ethtool_ops = {
+ .get_drvinfo = mpc52xx_fec_get_drvinfo,
+ .get_settings = mpc52xx_fec_get_settings,
+ .set_settings = mpc52xx_fec_set_settings,
+ .get_link = ethtool_op_get_link,
+ .get_msglevel = mpc52xx_fec_get_msglevel,
+ .set_msglevel = mpc52xx_fec_set_msglevel,
+};
+
+
+static int mpc52xx_fec_ioctl(struct net_device *dev, struct ifreq *rq, int cmd)
+{
+ struct mpc52xx_fec_priv *priv = netdev_priv(dev);
+
+ return mpc52xx_fec_phy_mii_ioctl(priv, if_mii(rq), cmd);
+}
+
+/* ======================================================================== */
+/* OF Driver */
+/* ======================================================================== */
+
+static int __devinit
+mpc52xx_fec_probe(struct of_device *op, const struct of_device_id *match)
+{
+ int rv;
+ struct net_device *ndev;
+ struct mpc52xx_fec_priv *priv = NULL;
+ struct resource mem;
+ const phandle *ph;
+
+ phys_addr_t rx_fifo;
+ phys_addr_t tx_fifo;
+
+ /* Get the ether ndev & it's private zone */
+ ndev = alloc_etherdev(sizeof(struct mpc52xx_fec_priv));
+ if (!ndev)
+ return -ENOMEM;
+
+ priv = netdev_priv(ndev);
+
+ /* Reserve FEC control zone */
+ rv = of_address_to_resource(op->node, 0, &mem);
+ if (rv) {
+ printk(KERN_ERR DRIVER_NAME ": "
+ "Error while parsing device node resource\n" );
+ return rv;
+ }
+ if ((mem.end - mem.start + 1) != sizeof(struct mpc52xx_fec)) {
+ printk(KERN_ERR DRIVER_NAME
+ " - invalid resource size (%lx != %x), check mpc52xx_devices.c\n",
+ (unsigned long)(mem.end - mem.start + 1), sizeof(struct mpc52xx_fec));
+ return -EINVAL;
+ }
+
+ if (!request_mem_region(mem.start, sizeof(struct mpc52xx_fec), DRIVER_NAME))
+ return -EBUSY;
+
+ /* Init ether ndev with what we have */
+ ndev->open = mpc52xx_fec_open;
+ ndev->stop = mpc52xx_fec_close;
+ ndev->hard_start_xmit = mpc52xx_fec_hard_start_xmit;
+ ndev->do_ioctl = mpc52xx_fec_ioctl;
+ ndev->ethtool_ops = &mpc52xx_fec_ethtool_ops;
+ ndev->get_stats = mpc52xx_fec_get_stats;
+ ndev->set_mac_address = mpc52xx_fec_set_mac_address;
+ ndev->set_multicast_list = mpc52xx_fec_set_multicast_list;
+ ndev->tx_timeout = mpc52xx_fec_tx_timeout;
+ ndev->watchdog_timeo = FEC_WATCHDOG_TIMEOUT;
+ ndev->base_addr = mem.start;
+
+ priv->t_irq = priv->r_irq = ndev->irq = NO_IRQ; /* IRQ are free for now */
+
+ spin_lock_init(&priv->lock);
+
+ /* ioremap the zones */
+ priv->fec = ioremap(mem.start, sizeof(struct mpc52xx_fec));
+
+ if (!priv->fec) {
+ rv = -ENOMEM;
+ goto probe_error;
+ }
+
+ /* Bestcomm init */
+ rx_fifo = ndev->base_addr + offsetof(struct mpc52xx_fec, rfifo_data);
+ tx_fifo = ndev->base_addr + offsetof(struct mpc52xx_fec, tfifo_data);
+
+ priv->rx_dmatsk = bcom_fec_rx_init(FEC_RX_NUM_BD, rx_fifo, FEC_RX_BUFFER_SIZE);
+ priv->tx_dmatsk = bcom_fec_tx_init(FEC_TX_NUM_BD, tx_fifo);
+
+ if (!priv->rx_dmatsk || !priv->tx_dmatsk) {
+ printk(KERN_ERR DRIVER_NAME ": Can not init SDMA tasks\n" );
+ rv = -ENOMEM;
+ goto probe_error;
+ }
+
+ /* Get the IRQ we need one by one */
+ /* Control */
+ ndev->irq = irq_of_parse_and_map(op->node, 0);
+
+ /* RX */
+ priv->r_irq = bcom_get_task_irq(priv->rx_dmatsk);
+
+ /* TX */
+ priv->t_irq = bcom_get_task_irq(priv->tx_dmatsk);
+
+ /* MAC address init */
+ if (!is_zero_ether_addr(mpc52xx_fec_mac_addr))
+ memcpy(ndev->dev_addr, mpc52xx_fec_mac_addr, 6);
+ else
+ mpc52xx_fec_get_paddr(ndev, ndev->dev_addr);
+
+ priv->msg_enable = netif_msg_init(debug, MPC52xx_MESSAGES_DEFAULT);
+ priv->duplex = DUPLEX_FULL;
+
+ /* is the phy present in device tree? */
+ ph = of_get_property(op->node, "phy-handle", NULL);
+ if (ph) {
+ const unsigned int *prop;
+ struct device_node *phy_dn;
+ priv->has_phy = 1;
+
+ phy_dn = of_find_node_by_phandle(*ph);
+ prop = of_get_property(phy_dn, "reg", NULL);
+ priv->phy_addr = *prop;
+
+ of_node_put(phy_dn);
+
+ /* Phy speed */
+ priv->phy_speed = ((mpc52xx_find_ipb_freq(op->node) >> 20) / 5) << 1;
+ } else {
+ dev_info(&ndev->dev, "can't find \"phy-handle\" in device"
+ " tree, using 7-wire mode\n");
+ }
+
+ /* Hardware init */
+ mpc52xx_fec_hw_init(ndev);
+
+ mpc52xx_fec_reset_stats(ndev);
+
+ /* Register the new network device */
+ rv = register_netdev(ndev);
+ if (rv < 0)
+ goto probe_error;
+
+ /* We're done ! */
+ dev_set_drvdata(&op->dev, ndev);
+
+ return 0;
+
+
+ /* Error handling - free everything that might be allocated */
+probe_error:
+
+ irq_dispose_mapping(ndev->irq);
+
+ if (priv->rx_dmatsk)
+ bcom_fec_rx_release(priv->rx_dmatsk);
+ if (priv->tx_dmatsk)
+ bcom_fec_tx_release(priv->tx_dmatsk);
+
+ if (priv->fec)
+ iounmap(priv->fec);
+
+ release_mem_region(mem.start, sizeof(struct mpc52xx_fec));
+
+ free_netdev(ndev);
+
+ return rv;
+}
+
+static int
+mpc52xx_fec_remove(struct of_device *op)
+{
+ struct net_device *ndev;
+ struct mpc52xx_fec_priv *priv;
+
+ ndev = dev_get_drvdata(&op->dev);
+ priv = netdev_priv(ndev);
+
+ unregister_netdev(ndev);
+
+ irq_dispose_mapping(ndev->irq);
+
+ bcom_fec_rx_release(priv->rx_dmatsk);
+ bcom_fec_tx_release(priv->tx_dmatsk);
+
+ iounmap(priv->fec);
+
+ release_mem_region(ndev->base_addr, sizeof(struct mpc52xx_fec));
+
+ free_netdev(ndev);
+
+ dev_set_drvdata(&op->dev, NULL);
+ return 0;
+}
+
+#ifdef CONFIG_PM
+static int mpc52xx_fec_of_suspend(struct of_device *op, pm_message_t state)
+{
+ struct net_device *dev = dev_get_drvdata(&op->dev);
+
+ if (netif_running(dev))
+ mpc52xx_fec_close(dev);
+
+ return 0;
+}
+
+static int mpc52xx_fec_of_resume(struct of_device *op)
+{
+ struct net_device *dev = dev_get_drvdata(&op->dev);
+
+ mpc52xx_fec_hw_init(dev);
+ mpc52xx_fec_reset_stats(dev);
+
+ if (netif_running(dev))
+ mpc52xx_fec_open(dev);
+
+ return 0;
+}
+#endif
+
+static struct of_device_id mpc52xx_fec_match[] = {
+ {
+ .type = "network",
+ .compatible = "mpc5200-fec",
+ },
+ { }
+};
+
+MODULE_DEVICE_TABLE(of, mpc52xx_fec_match);
+
+static struct of_platform_driver mpc52xx_fec_driver = {
+ .owner = THIS_MODULE,
+ .name = DRIVER_NAME,
+ .match_table = mpc52xx_fec_match,
+ .probe = mpc52xx_fec_probe,
+ .remove = mpc52xx_fec_remove,
+#ifdef CONFIG_PM
+ .suspend = mpc52xx_fec_of_suspend,
+ .resume = mpc52xx_fec_of_resume,
+#endif
+};
+
+
+/* ======================================================================== */
+/* Module */
+/* ======================================================================== */
+
+static int __init
+mpc52xx_fec_init(void)
+{
+#ifdef CONFIG_FEC_MPC52xx_MDIO
+ int ret;
+ ret = of_register_platform_driver(&mpc52xx_fec_mdio_driver);
+ if (ret) {
+ printk(KERN_ERR DRIVER_NAME ": failed to register mdio driver\n");
+ return ret;
+ }
+#endif
+ return of_register_platform_driver(&mpc52xx_fec_driver);
+}
+
+static void __exit
+mpc52xx_fec_exit(void)
+{
+ of_unregister_platform_driver(&mpc52xx_fec_driver);
+#ifdef CONFIG_FEC_MPC52xx_MDIO
+ of_unregister_platform_driver(&mpc52xx_fec_mdio_driver);
+#endif
+}
+
+
+module_init(mpc52xx_fec_init);
+module_exit(mpc52xx_fec_exit);
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Dale Farnsworth");
+MODULE_DESCRIPTION("Ethernet driver for the Freescale MPC52xx FEC");
Index: linux.git/drivers/net/fec_mpc52xx.h
===================================================================
--- /dev/null
+++ linux.git/drivers/net/fec_mpc52xx.h
@@ -0,0 +1,313 @@
+/*
+ * drivers/drivers/net/fec_mpc52xx/fec.h
+ *
+ * Driver for the MPC5200 Fast Ethernet Controller
+ *
+ * Author: Dale Farnsworth <dfarnsworth@mvista.com>
+ *
+ * 2003-2004 (c) MontaVista, Software, Inc. This file is licensed under
+ * the terms of the GNU General Public License version 2. This program
+ * is licensed "as is" without any warranty of any kind, whether express
+ * or implied.
+ */
+
+#ifndef __DRIVERS_NET_MPC52XX_FEC_H__
+#define __DRIVERS_NET_MPC52XX_FEC_H__
+
+#include <linux/phy.h>
+
+/* Tunable constant */
+/* FEC_RX_BUFFER_SIZE includes 4 bytes for CRC32 */
+#define FEC_RX_BUFFER_SIZE 1522 /* max receive packet size */
+#define FEC_RX_NUM_BD 256
+#define FEC_TX_NUM_BD 64
+
+#define FEC_RESET_DELAY 50 /* uS */
+
+#define FEC_WATCHDOG_TIMEOUT ((400*HZ)/1000)
+
+struct mpc52xx_fec_priv {
+ int duplex;
+ int r_irq;
+ int t_irq;
+ struct mpc52xx_fec __iomem *fec;
+ struct bcom_task *rx_dmatsk;
+ struct bcom_task *tx_dmatsk;
+ spinlock_t lock;
+ int msg_enable;
+
+ int has_phy;
+ unsigned int phy_speed;
+ unsigned int phy_addr;
+ struct phy_device *phydev;
+ enum phy_state link;
+ int speed;
+};
+
+
+/* ======================================================================== */
+/* Hardware register sets & bits */
+/* ======================================================================== */
+
+struct mpc52xx_fec {
+ u32 fec_id; /* FEC + 0x000 */
+ u32 ievent; /* FEC + 0x004 */
+ u32 imask; /* FEC + 0x008 */
+
+ u32 reserved0[1]; /* FEC + 0x00C */
+ u32 r_des_active; /* FEC + 0x010 */
+ u32 x_des_active; /* FEC + 0x014 */
+ u32 r_des_active_cl; /* FEC + 0x018 */
+ u32 x_des_active_cl; /* FEC + 0x01C */
+ u32 ivent_set; /* FEC + 0x020 */
+ u32 ecntrl; /* FEC + 0x024 */
+
+ u32 reserved1[6]; /* FEC + 0x028-03C */
+ u32 mii_data; /* FEC + 0x040 */
+ u32 mii_speed; /* FEC + 0x044 */
+ u32 mii_status; /* FEC + 0x048 */
+
+ u32 reserved2[5]; /* FEC + 0x04C-05C */
+ u32 mib_data; /* FEC + 0x060 */
+ u32 mib_control; /* FEC + 0x064 */
+
+ u32 reserved3[6]; /* FEC + 0x068-7C */
+ u32 r_activate; /* FEC + 0x080 */
+ u32 r_cntrl; /* FEC + 0x084 */
+ u32 r_hash; /* FEC + 0x088 */
+ u32 r_data; /* FEC + 0x08C */
+ u32 ar_done; /* FEC + 0x090 */
+ u32 r_test; /* FEC + 0x094 */
+ u32 r_mib; /* FEC + 0x098 */
+ u32 r_da_low; /* FEC + 0x09C */
+ u32 r_da_high; /* FEC + 0x0A0 */
+
+ u32 reserved4[7]; /* FEC + 0x0A4-0BC */
+ u32 x_activate; /* FEC + 0x0C0 */
+ u32 x_cntrl; /* FEC + 0x0C4 */
+ u32 backoff; /* FEC + 0x0C8 */
+ u32 x_data; /* FEC + 0x0CC */
+ u32 x_status; /* FEC + 0x0D0 */
+ u32 x_mib; /* FEC + 0x0D4 */
+ u32 x_test; /* FEC + 0x0D8 */
+ u32 fdxfc_da1; /* FEC + 0x0DC */
+ u32 fdxfc_da2; /* FEC + 0x0E0 */
+ u32 paddr1; /* FEC + 0x0E4 */
+ u32 paddr2; /* FEC + 0x0E8 */
+ u32 op_pause; /* FEC + 0x0EC */
+
+ u32 reserved5[4]; /* FEC + 0x0F0-0FC */
+ u32 instr_reg; /* FEC + 0x100 */
+ u32 context_reg; /* FEC + 0x104 */
+ u32 test_cntrl; /* FEC + 0x108 */
+ u32 acc_reg; /* FEC + 0x10C */
+ u32 ones; /* FEC + 0x110 */
+ u32 zeros; /* FEC + 0x114 */
+ u32 iaddr1; /* FEC + 0x118 */
+ u32 iaddr2; /* FEC + 0x11C */
+ u32 gaddr1; /* FEC + 0x120 */
+ u32 gaddr2; /* FEC + 0x124 */
+ u32 random; /* FEC + 0x128 */
+ u32 rand1; /* FEC + 0x12C */
+ u32 tmp; /* FEC + 0x130 */
+
+ u32 reserved6[3]; /* FEC + 0x134-13C */
+ u32 fifo_id; /* FEC + 0x140 */
+ u32 x_wmrk; /* FEC + 0x144 */
+ u32 fcntrl; /* FEC + 0x148 */
+ u32 r_bound; /* FEC + 0x14C */
+ u32 r_fstart; /* FEC + 0x150 */
+ u32 r_count; /* FEC + 0x154 */
+ u32 r_lag; /* FEC + 0x158 */
+ u32 r_read; /* FEC + 0x15C */
+ u32 r_write; /* FEC + 0x160 */
+ u32 x_count; /* FEC + 0x164 */
+ u32 x_lag; /* FEC + 0x168 */
+ u32 x_retry; /* FEC + 0x16C */
+ u32 x_write; /* FEC + 0x170 */
+ u32 x_read; /* FEC + 0x174 */
+
+ u32 reserved7[2]; /* FEC + 0x178-17C */
+ u32 fm_cntrl; /* FEC + 0x180 */
+ u32 rfifo_data; /* FEC + 0x184 */
+ u32 rfifo_status; /* FEC + 0x188 */
+ u32 rfifo_cntrl; /* FEC + 0x18C */
+ u32 rfifo_lrf_ptr; /* FEC + 0x190 */
+ u32 rfifo_lwf_ptr; /* FEC + 0x194 */
+ u32 rfifo_alarm; /* FEC + 0x198 */
+ u32 rfifo_rdptr; /* FEC + 0x19C */
+ u32 rfifo_wrptr; /* FEC + 0x1A0 */
+ u32 tfifo_data; /* FEC + 0x1A4 */
+ u32 tfifo_status; /* FEC + 0x1A8 */
+ u32 tfifo_cntrl; /* FEC + 0x1AC */
+ u32 tfifo_lrf_ptr; /* FEC + 0x1B0 */
+ u32 tfifo_lwf_ptr; /* FEC + 0x1B4 */
+ u32 tfifo_alarm; /* FEC + 0x1B8 */
+ u32 tfifo_rdptr; /* FEC + 0x1BC */
+ u32 tfifo_wrptr; /* FEC + 0x1C0 */
+
+ u32 reset_cntrl; /* FEC + 0x1C4 */
+ u32 xmit_fsm; /* FEC + 0x1C8 */
+
+ u32 reserved8[3]; /* FEC + 0x1CC-1D4 */
+ u32 rdes_data0; /* FEC + 0x1D8 */
+ u32 rdes_data1; /* FEC + 0x1DC */
+ u32 r_length; /* FEC + 0x1E0 */
+ u32 x_length; /* FEC + 0x1E4 */
+ u32 x_addr; /* FEC + 0x1E8 */
+ u32 cdes_data; /* FEC + 0x1EC */
+ u32 status; /* FEC + 0x1F0 */
+ u32 dma_control; /* FEC + 0x1F4 */
+ u32 des_cmnd; /* FEC + 0x1F8 */
+ u32 data; /* FEC + 0x1FC */
+
+ u32 rmon_t_drop; /* FEC + 0x200 */
+ u32 rmon_t_packets; /* FEC + 0x204 */
+ u32 rmon_t_bc_pkt; /* FEC + 0x208 */
+ u32 rmon_t_mc_pkt; /* FEC + 0x20C */
+ u32 rmon_t_crc_align; /* FEC + 0x210 */
+ u32 rmon_t_undersize; /* FEC + 0x214 */
+ u32 rmon_t_oversize; /* FEC + 0x218 */
+ u32 rmon_t_frag; /* FEC + 0x21C */
+ u32 rmon_t_jab; /* FEC + 0x220 */
+ u32 rmon_t_col; /* FEC + 0x224 */
+ u32 rmon_t_p64; /* FEC + 0x228 */
+ u32 rmon_t_p65to127; /* FEC + 0x22C */
+ u32 rmon_t_p128to255; /* FEC + 0x230 */
+ u32 rmon_t_p256to511; /* FEC + 0x234 */
+ u32 rmon_t_p512to1023; /* FEC + 0x238 */
+ u32 rmon_t_p1024to2047; /* FEC + 0x23C */
+ u32 rmon_t_p_gte2048; /* FEC + 0x240 */
+ u32 rmon_t_octets; /* FEC + 0x244 */
+ u32 ieee_t_drop; /* FEC + 0x248 */
+ u32 ieee_t_frame_ok; /* FEC + 0x24C */
+ u32 ieee_t_1col; /* FEC + 0x250 */
+ u32 ieee_t_mcol; /* FEC + 0x254 */
+ u32 ieee_t_def; /* FEC + 0x258 */
+ u32 ieee_t_lcol; /* FEC + 0x25C */
+ u32 ieee_t_excol; /* FEC + 0x260 */
+ u32 ieee_t_macerr; /* FEC + 0x264 */
+ u32 ieee_t_cserr; /* FEC + 0x268 */
+ u32 ieee_t_sqe; /* FEC + 0x26C */
+ u32 t_fdxfc; /* FEC + 0x270 */
+ u32 ieee_t_octets_ok; /* FEC + 0x274 */
+
+ u32 reserved9[2]; /* FEC + 0x278-27C */
+ u32 rmon_r_drop; /* FEC + 0x280 */
+ u32 rmon_r_packets; /* FEC + 0x284 */
+ u32 rmon_r_bc_pkt; /* FEC + 0x288 */
+ u32 rmon_r_mc_pkt; /* FEC + 0x28C */
+ u32 rmon_r_crc_align; /* FEC + 0x290 */
+ u32 rmon_r_undersize; /* FEC + 0x294 */
+ u32 rmon_r_oversize; /* FEC + 0x298 */
+ u32 rmon_r_frag; /* FEC + 0x29C */
+ u32 rmon_r_jab; /* FEC + 0x2A0 */
+
+ u32 rmon_r_resvd_0; /* FEC + 0x2A4 */
+
+ u32 rmon_r_p64; /* FEC + 0x2A8 */
+ u32 rmon_r_p65to127; /* FEC + 0x2AC */
+ u32 rmon_r_p128to255; /* FEC + 0x2B0 */
+ u32 rmon_r_p256to511; /* FEC + 0x2B4 */
+ u32 rmon_r_p512to1023; /* FEC + 0x2B8 */
+ u32 rmon_r_p1024to2047; /* FEC + 0x2BC */
+ u32 rmon_r_p_gte2048; /* FEC + 0x2C0 */
+ u32 rmon_r_octets; /* FEC + 0x2C4 */
+ u32 ieee_r_drop; /* FEC + 0x2C8 */
+ u32 ieee_r_frame_ok; /* FEC + 0x2CC */
+ u32 ieee_r_crc; /* FEC + 0x2D0 */
+ u32 ieee_r_align; /* FEC + 0x2D4 */
+ u32 r_macerr; /* FEC + 0x2D8 */
+ u32 r_fdxfc; /* FEC + 0x2DC */
+ u32 ieee_r_octets_ok; /* FEC + 0x2E0 */
+
+ u32 reserved10[7]; /* FEC + 0x2E4-2FC */
+
+ u32 reserved11[64]; /* FEC + 0x300-3FF */
+};
+
+#define FEC_MIB_DISABLE 0x80000000
+
+#define FEC_IEVENT_HBERR 0x80000000
+#define FEC_IEVENT_BABR 0x40000000
+#define FEC_IEVENT_BABT 0x20000000
+#define FEC_IEVENT_GRA 0x10000000
+#define FEC_IEVENT_TFINT 0x08000000
+#define FEC_IEVENT_MII 0x00800000
+#define FEC_IEVENT_LATE_COL 0x00200000
+#define FEC_IEVENT_COL_RETRY_LIM 0x00100000
+#define FEC_IEVENT_XFIFO_UN 0x00080000
+#define FEC_IEVENT_XFIFO_ERROR 0x00040000
+#define FEC_IEVENT_RFIFO_ERROR 0x00020000
+
+#define FEC_IMASK_HBERR 0x80000000
+#define FEC_IMASK_BABR 0x40000000
+#define FEC_IMASK_BABT 0x20000000
+#define FEC_IMASK_GRA 0x10000000
+#define FEC_IMASK_MII 0x00800000
+#define FEC_IMASK_LATE_COL 0x00200000
+#define FEC_IMASK_COL_RETRY_LIM 0x00100000
+#define FEC_IMASK_XFIFO_UN 0x00080000
+#define FEC_IMASK_XFIFO_ERROR 0x00040000
+#define FEC_IMASK_RFIFO_ERROR 0x00020000
+
+/* all but MII, which is enabled separately */
+#define FEC_IMASK_ENABLE (FEC_IMASK_HBERR | FEC_IMASK_BABR | \
+ FEC_IMASK_BABT | FEC_IMASK_GRA | FEC_IMASK_LATE_COL | \
+ FEC_IMASK_COL_RETRY_LIM | FEC_IMASK_XFIFO_UN | \
+ FEC_IMASK_XFIFO_ERROR | FEC_IMASK_RFIFO_ERROR)
+
+#define FEC_RCNTRL_MAX_FL_SHIFT 16
+#define FEC_RCNTRL_LOOP 0x01
+#define FEC_RCNTRL_DRT 0x02
+#define FEC_RCNTRL_MII_MODE 0x04
+#define FEC_RCNTRL_PROM 0x08
+#define FEC_RCNTRL_BC_REJ 0x10
+#define FEC_RCNTRL_FCE 0x20
+
+#define FEC_TCNTRL_GTS 0x00000001
+#define FEC_TCNTRL_HBC 0x00000002
+#define FEC_TCNTRL_FDEN 0x00000004
+#define FEC_TCNTRL_TFC_PAUSE 0x00000008
+#define FEC_TCNTRL_RFC_PAUSE 0x00000010
+
+#define FEC_ECNTRL_RESET 0x00000001
+#define FEC_ECNTRL_ETHER_EN 0x00000002
+
+#define FEC_MII_DATA_ST 0x40000000 /* Start frame */
+#define FEC_MII_DATA_OP_RD 0x20000000 /* Perform read */
+#define FEC_MII_DATA_OP_WR 0x10000000 /* Perform write */
+#define FEC_MII_DATA_PA_MSK 0x0f800000 /* PHY Address mask */
+#define FEC_MII_DATA_RA_MSK 0x007c0000 /* PHY Register mask */
+#define FEC_MII_DATA_TA 0x00020000 /* Turnaround */
+#define FEC_MII_DATA_DATAMSK 0x0000ffff /* PHY data mask */
+
+#define FEC_MII_READ_FRAME (FEC_MII_DATA_ST | FEC_MII_DATA_OP_RD | FEC_MII_DATA_TA)
+#define FEC_MII_WRITE_FRAME (FEC_MII_DATA_ST | FEC_MII_DATA_OP_WR | FEC_MII_DATA_TA)
+
+#define FEC_MII_DATA_RA_SHIFT 0x12 /* MII reg addr bits */
+#define FEC_MII_DATA_PA_SHIFT 0x17 /* MII PHY addr bits */
+
+#define FEC_PADDR2_TYPE 0x8808
+
+#define FEC_OP_PAUSE_OPCODE 0x00010000
+
+#define FEC_FIFO_WMRK_256B 0x3
+
+#define FEC_FIFO_STATUS_ERR 0x00400000
+#define FEC_FIFO_STATUS_UF 0x00200000
+#define FEC_FIFO_STATUS_OF 0x00100000
+
+#define FEC_FIFO_CNTRL_FRAME 0x08000000
+#define FEC_FIFO_CNTRL_LTG_7 0x07000000
+
+#define FEC_RESET_CNTRL_RESET_FIFO 0x02000000
+#define FEC_RESET_CNTRL_ENABLE_IS_RESET 0x01000000
+
+#define FEC_XMIT_FSM_APPEND_CRC 0x02000000
+#define FEC_XMIT_FSM_ENABLE_CRC 0x01000000
+
+
+extern struct of_platform_driver mpc52xx_fec_mdio_driver;
+
+#endif /* __DRIVERS_NET_MPC52XX_FEC_H__ */
Index: linux.git/drivers/net/fec_mpc52xx_phy.c
===================================================================
--- /dev/null
+++ linux.git/drivers/net/fec_mpc52xx_phy.c
@@ -0,0 +1,198 @@
+/*
+ * Driver for the MPC5200 Fast Ethernet Controller - MDIO bus driver
+ *
+ * Copyright (C) 2007 Domen Puncer, Telargo, Inc.
+ *
+ * This file is licensed under the terms of the GNU General Public License
+ * version 2. This program is licensed "as is" without any warranty of any
+ * kind, whether express or implied.
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/netdevice.h>
+#include <linux/phy.h>
+#include <linux/of_platform.h>
+#include <asm/io.h>
+#include <asm/mpc52xx.h>
+#include "fec_mpc52xx.h"
+
+struct mpc52xx_fec_mdio_priv {
+ struct mpc52xx_fec __iomem *regs;
+};
+
+static int mpc52xx_fec_mdio_read(struct mii_bus *bus, int phy_id, int reg)
+{
+ struct mpc52xx_fec_mdio_priv *priv = bus->priv;
+ struct mpc52xx_fec __iomem *fec;
+ int tries = 100;
+ u32 request = FEC_MII_READ_FRAME;
+
+ fec = priv->regs;
+ out_be32(&fec->ievent, FEC_IEVENT_MII);
+
+ request |= (phy_id << FEC_MII_DATA_PA_SHIFT) & FEC_MII_DATA_PA_MSK;
+ request |= (reg << FEC_MII_DATA_RA_SHIFT) & FEC_MII_DATA_RA_MSK;
+
+ out_be32(&priv->regs->mii_data, request);
+
+ /* wait for it to finish, this takes about 23 us on lite5200b */
+ while (!(in_be32(&fec->ievent) & FEC_IEVENT_MII) && --tries)
+ udelay(5);
+
+ if (tries == 0)
+ return -ETIMEDOUT;
+
+ return in_be32(&priv->regs->mii_data) & FEC_MII_DATA_DATAMSK;
+}
+
+static int mpc52xx_fec_mdio_write(struct mii_bus *bus, int phy_id, int reg, u16 data)
+{
+ struct mpc52xx_fec_mdio_priv *priv = bus->priv;
+ struct mpc52xx_fec __iomem *fec;
+ u32 value = data;
+ int tries = 100;
+
+ fec = priv->regs;
+ out_be32(&fec->ievent, FEC_IEVENT_MII);
+
+ value |= FEC_MII_WRITE_FRAME;
+ value |= (phy_id << FEC_MII_DATA_PA_SHIFT) & FEC_MII_DATA_PA_MSK;
+ value |= (reg << FEC_MII_DATA_RA_SHIFT) & FEC_MII_DATA_RA_MSK;
+
+ out_be32(&priv->regs->mii_data, value);
+
+ /* wait for request to finish */
+ while (!(in_be32(&fec->ievent) & FEC_IEVENT_MII) && --tries)
+ udelay(5);
+
+ if (tries == 0)
+ return -ETIMEDOUT;
+
+ return 0;
+}
+
+static int mpc52xx_fec_mdio_probe(struct of_device *of, const struct of_device_id *match)
+{
+ struct device *dev = &of->dev;
+ struct device_node *np = of->node;
+ struct device_node *child = NULL;
+ struct mii_bus *bus;
+ struct mpc52xx_fec_mdio_priv *priv;
+ struct resource res = {};
+ int err;
+ int i;
+
+ bus = kzalloc(sizeof(*bus), GFP_KERNEL);
+ if (bus == NULL)
+ return -ENOMEM;
+ priv = kzalloc(sizeof(*priv), GFP_KERNEL);
+ if (priv == NULL) {
+ err = -ENOMEM;
+ goto out_free;
+ }
+
+ bus->name = "mpc52xx MII bus";
+ bus->read = mpc52xx_fec_mdio_read;
+ bus->write = mpc52xx_fec_mdio_write;
+
+ /* setup irqs */
+ bus->irq = kmalloc(sizeof(bus->irq[0]) * PHY_MAX_ADDR, GFP_KERNEL);
+ if (bus->irq == NULL) {
+ err = -ENOMEM;
+ goto out_free;
+ }
+ for (i=0; i<PHY_MAX_ADDR; i++)
+ bus->irq[i] = PHY_POLL;
+
+ while ((child = of_get_next_child(np, child)) != NULL) {
+ int irq = irq_of_parse_and_map(child, 0);
+ if (irq != NO_IRQ) {
+ const u32 *id = of_get_property(child, "reg", NULL);
+ bus->irq[*id] = irq;
+ }
+ }
+
+ /* setup registers */
+ err = of_address_to_resource(np, 0, &res);
+ if (err)
+ goto out_free;
+ priv->regs = ioremap(res.start, res.end - res.start + 1);
+ if (priv->regs == NULL) {
+ err = -ENOMEM;
+ goto out_free;
+ }
+
+ bus->id = res.start;
+ bus->priv = priv;
+
+ bus->dev = dev;
+ dev_set_drvdata(dev, bus);
+
+ /* set MII speed */
+ out_be32(&priv->regs->mii_speed, ((mpc52xx_find_ipb_freq(of->node) >> 20) / 5) << 1);
+
+ /* enable MII interrupt */
+ out_be32(&priv->regs->imask, in_be32(&priv->regs->imask) | FEC_IMASK_MII);
+
+ err = mdiobus_register(bus);
+ if (err)
+ goto out_unmap;
+
+ return 0;
+
+ out_unmap:
+ iounmap(priv->regs);
+ out_free:
+ for (i=0; i<PHY_MAX_ADDR; i++)
+ if (bus->irq[i] != PHY_POLL)
+ irq_dispose_mapping(bus->irq[i]);
+ kfree(bus->irq);
+ kfree(priv);
+ kfree(bus);
+
+ return err;
+}
+
+static int mpc52xx_fec_mdio_remove(struct of_device *of)
+{
+ struct device *dev = &of->dev;
+ struct mii_bus *bus = dev_get_drvdata(dev);
+ struct mpc52xx_fec_mdio_priv *priv = bus->priv;
+ int i;
+
+ mdiobus_unregister(bus);
+ dev_set_drvdata(dev, NULL);
+
+ iounmap(priv->regs);
+ for (i=0; i<PHY_MAX_ADDR; i++)
+ if (bus->irq[i])
+ irq_dispose_mapping(bus->irq[i]);
+ kfree(priv);
+ kfree(bus->irq);
+ kfree(bus);
+
+ return 0;
+}
+
+
+static struct of_device_id mpc52xx_fec_mdio_match[] = {
+ {
+ .type = "mdio",
+ .compatible = "mpc5200b-fec-phy",
+ },
+ {},
+};
+
+struct of_platform_driver mpc52xx_fec_mdio_driver = {
+ .name = "mpc5200b-fec-phy",
+ .probe = mpc52xx_fec_mdio_probe,
+ .remove = mpc52xx_fec_mdio_remove,
+ .match_table = mpc52xx_fec_mdio_match,
+};
+
+/* let fec driver call it, since this has to be registered before it */
+EXPORT_SYMBOL_GPL(mpc52xx_fec_mdio_driver);
+
+
+MODULE_LICENSE("Dual BSD/GPL");
--
Domen Puncer | Research & Development
.............................................................................................
Telargo d.o.o. | Zagrebška cesta 20 | 2000 Maribor | Slovenia
.............................................................................................
www.telargo.com
^ permalink raw reply
page: next (older) | prev (newer) | latest
- recent:[subjects (threaded)|topics (new)|topics (active)]
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox