LinuxPPC-Dev Archive on lore.kernel.org
 help / color / mirror / Atom feed
* Re: [PATCH] ucc_geth: Fix half-duplex operation for non-MII/RMII interfaces
From: Mark Huth @ 2009-06-25  5:11 UTC (permalink / raw)
  To: Anton Vorontsov; +Cc: linuxppc-dev, netdev, Li Yang, David Miller
In-Reply-To: <20090624174557.GA31479@oksana.dev.rtsoft.ru>

Anton Vorontsov wrote:
> Currently the half-duplex operation seems to not work reliably for
> RGMII/GMII PHY interfaces. It takes about 10 minutes to boot NFS
> rootfs using 10/half link, following symptoms were observed:
> 
>   ucc_geth: QE UCC Gigabit Ethernet Controller
>   ucc_geth: UCC1 at 0xe0082000 (irq = 32)
>   [...]
>   Sending DHCP and RARP requests .
>   PHY: mdio@e0082120:07 - Link is Up - 10/Half
>   ., OK
So why does the phy think this is a half-duplex network?
>   [...]
>   Looking up port of RPC 100003/2 on 10.0.0.2
>   Looking up port of RPC 100005/1 on 10.0.0.2
>   VFS: Mounted root (nfs filesystem) readonly on device 0:13.
>   Freeing unused kernel memory: 204k init
>   eth0: no IPv6 routers present
>   nfs: server 10.0.0.2 not responding, still trying
>   nfs: server 10.0.0.2 not responding, still trying
>   nfs: server 10.0.0.2 not responding, still trying
>   nfs: server 10.0.0.2 OK
>   nfs: server 10.0.0.2 OK
>   nfs: server 10.0.0.2 not responding, still trying
>   [... few minutes of OK/not responding flood ...]
> 
> The statistic shows that there are indeed some errors:
> 
>   # ethtool -S eth0 | grep -v ": 0"
>   NIC statistics:
>        tx-64-frames: 42
>        tx-65-127-frames: 9
>        tx-128-255-frames: 4768
>        rx-64-frames: 41
>        rx-65-127-frames: 260
>        rx-128-255-frames: 2679
>        tx-bytes-ok: 859634
>        tx-multicast-frames: 5
>        tx-broadcast-frames: 7
>        rx-frames: 8333
>        rx-bytes-ok: 8039364
>        rx-bytes-all: 8039364
>        stats-counter-mask: 4294901760
>        tx-single-collision: 324
>        tx-multiple-collision: 47
>        tx-late-collsion: 604
>        tx-aborted-frames: 604
The above two counters are the actual errors from a half-duplex ethernet 
configuration.  The size of the collision domain is limited so that the 
collisions from one end will reach the other end within the minimum 
frame length wire time.  Thus the collision will be detected within the 
first 64 bytes of the frame.  A late collision indicates a 
mis-configured network. The fact that everything seems to work when the 
MAC is placed into full-duplex mode hints that the network is really a 
full-duplex network.

Otherwise, if the network is really half-duplex, then presence of a 
full-duplex node will result in the other nodes seeing CRC/framing 
errors on receive, and possibly also late collisions, as the full-duplex 
node does not observe the CS or the CD( carrier sense and collision 
detect) part of CSMA/CD, because it doesn't care.

Putting a node in full-duplex will always make the nasty collision 
related errors go away, but it may not be a proper diagnosis of the problem.
>        tx-frames-ok: 4967
>        tx-256-511-frames: 3
>        tx-512-1023-frames: 79
>        tx-1024-1518-frames: 71
>        rx-256-511-frames: 37
>        rx-512-1023-frames: 73
>        rx-1024-1518-frames: 5243
> 
> According to current QEIWRM (Rev. 2 5/2009), FDX bit can be 0 for
> RGMII(10/100) modes, while MPC8568ERM (Rev. C 02/2007) spec says
> that cleared FDX bit is permitted for MII/RMII modes only.
> 
> The symptoms above were seen on MPC8569E-MDS boards, so QEIWRM is
> clearly wrong, and this patch completely cures the problems above.

Not so fast - RGMII and GMII refer to the interface between the MAC and 
the PHY.  While Gigabit physical links will always be full-duplex, phys 
that detect lower operational modes will indicate half-duplex where 
needed, and putting the MAC into full-duplex will make other nodes see 
errors.

As Andy indicated later, it may be necessary to alter the interface 
definition in those cases, depending on the particular hardware. 
Forcing full-duplex does not seem to be a general solution.

Mark Huth
MontaVista Software
> 
> Signed-off-by: Anton Vorontsov <avorontsov@ru.mvista.com>
> ---
>  drivers/net/ucc_geth.c |    8 ++++++--
>  1 files changed, 6 insertions(+), 2 deletions(-)
> 
> diff --git a/drivers/net/ucc_geth.c b/drivers/net/ucc_geth.c
> index 464df03..e618cf2 100644
> --- a/drivers/net/ucc_geth.c
> +++ b/drivers/net/ucc_geth.c
> @@ -1469,12 +1469,16 @@ static void adjust_link(struct net_device *dev)
>  	if (phydev->link) {
>  		u32 tempval = in_be32(&ug_regs->maccfg2);
>  		u32 upsmr = in_be32(&uf_regs->upsmr);
> +		phy_interface_t phyi = ugeth->phy_interface;
> +
>  		/* Now we make sure that we can be in full duplex mode.
>  		 * If not, we operate in half-duplex mode. */
>  		if (phydev->duplex != ugeth->oldduplex) {
>  			new_state = 1;
> -			if (!(phydev->duplex))
> -				tempval &= ~(MACCFG2_FDX);
> +			if (!phydev->duplex &&
> +					(phyi == PHY_INTERFACE_MODE_MII ||
> +					 phyi == PHY_INTERFACE_MODE_RMII))
> +				tempval &= ~MACCFG2_FDX;
>  			else
>  				tempval |= MACCFG2_FDX;
>  			ugeth->oldduplex = phydev->duplex;

^ permalink raw reply

* Re: How the kernel printk works before do console_setup.
From: Johnny Hung @ 2009-06-25  6:27 UTC (permalink / raw)
  To: Benjamin Herrenschmidt; +Cc: linuxppc-dev, linux-embedded, linux-kernel
In-Reply-To: <1245884203.21200.18.camel@pasglop>

2009/6/25 Benjamin Herrenschmidt <benh@kernel.crashing.org>:
>
>> Before the console is set up, the printk data is formatted
>> and put into the kernel log buffer, but not sent to any console.
>> Any messages printk'ed before that are buffered but do not
>> appear. =A0When the console is initialized, then all buffered
>> messages are sent to the console, and subsequent printks cause
>> the message to go to the log buffer, but then immediately
>> get sent from there to the console.
>>
>> Under certain conditions you can examine the log buffer of
>> a kernel that failed to initialize it's console, after a
>> warm reset of the machine, using the firmware memory dump
>> command.
>
> On ppc, we have tricks to display things earlier :-)
>
> We can initialize the serial ports way before console_setup() (and we do
> in most cases) and we use what we call the "udbg" console until the real
> one takes over. The "udbg" console is a very small layer which outputs
> via a provided "putc" routine. Platforms can provide their own here, we
> have a collection of standard ones for legacy UARTs (it should be
> automatically setup in that case by the code in legacy_serial), Apple
> ESCCs, etc... We even have compile time options that allow that stuff to
> be initialized before start_kernel...

Thank you. This is what I described and want to understand. The
arch/powerpc/kernel/legacy_serial.c
do find_legacy_serial_ports then take a default serial port by using
open firmware device tree
information. The find_legacy_serial_ports() called form setup_arch but
I don't know who call
setup_arch (setup_32.c)function. Can you give me a hint ? Thanks in advance=
d.

BRs, H. Johnny

^ permalink raw reply

* Re: How the kernel printk works before do console_setup.
From: Michael Ellerman @ 2009-06-25  6:37 UTC (permalink / raw)
  To: Johnny Hung; +Cc: linuxppc-dev, linux-kernel, linux-embedded
In-Reply-To: <cb9ecdfa0906242327j76d1e4fbib13bc7d9753aeacf@mail.gmail.com>

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

On Thu, 2009-06-25 at 14:27 +0800, Johnny Hung wrote:
> 2009/6/25 Benjamin Herrenschmidt <benh@kernel.crashing.org>:
> >
> >> Before the console is set up, the printk data is formatted
> >> and put into the kernel log buffer, but not sent to any console.
> >> Any messages printk'ed before that are buffered but do not
> >> appear.  When the console is initialized, then all buffered
> >> messages are sent to the console, and subsequent printks cause
> >> the message to go to the log buffer, but then immediately
> >> get sent from there to the console.
> >>
> >> Under certain conditions you can examine the log buffer of
> >> a kernel that failed to initialize it's console, after a
> >> warm reset of the machine, using the firmware memory dump
> >> command.
> >
> > On ppc, we have tricks to display things earlier :-)
> >
> > We can initialize the serial ports way before console_setup() (and we do
> > in most cases) and we use what we call the "udbg" console until the real
> > one takes over. The "udbg" console is a very small layer which outputs
> > via a provided "putc" routine. Platforms can provide their own here, we
> > have a collection of standard ones for legacy UARTs (it should be
> > automatically setup in that case by the code in legacy_serial), Apple
> > ESCCs, etc... We even have compile time options that allow that stuff to
> > be initialized before start_kernel...
> 
> Thank you. This is what I described and want to understand. The
> arch/powerpc/kernel/legacy_serial.c
> do find_legacy_serial_ports then take a default serial port by using
> open firmware device tree
> information. The find_legacy_serial_ports() called form setup_arch but
> I don't know who call
> setup_arch (setup_32.c)function. Can you give me a hint ? Thanks in advanced.

setup_arch() is called from start_kernel() in init/main.c

cheers

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

^ permalink raw reply

* Re: [PATCH] ucc_geth: Fix half-duplex operation for non-MII/RMII interfaces
From: Anton Vorontsov @ 2009-06-25  7:02 UTC (permalink / raw)
  To: Mark Huth; +Cc: linuxppc-dev, netdev, Li Yang, David Miller
In-Reply-To: <4A4306F2.3070909@mvista.com>

On Wed, Jun 24, 2009 at 10:11:14PM -0700, Mark Huth wrote:
> Anton Vorontsov wrote:
>> Currently the half-duplex operation seems to not work reliably for
>> RGMII/GMII PHY interfaces. It takes about 10 minutes to boot NFS
>> rootfs using 10/half link, following symptoms were observed:
>>
>>   ucc_geth: QE UCC Gigabit Ethernet Controller
>>   ucc_geth: UCC1 at 0xe0082000 (irq = 32)
>>   [...]
>>   Sending DHCP and RARP requests .
>>   PHY: mdio@e0082120:07 - Link is Up - 10/Half
>>   ., OK
> So why does the phy think this is a half-duplex network?

Because it's physical media now in half-duplex. At least that's
what PHY detects.

[...]
>>        tx-late-collsion: 604
>>        tx-aborted-frames: 604
> The above two counters are the actual errors from a half-duplex ethernet  
> configuration.  The size of the collision domain is limited so that the  
> collisions from one end will reach the other end within the minimum  
> frame length wire time.  Thus the collision will be detected within the  
> first 64 bytes of the frame.  A late collision indicates a  
> mis-configured network. The fact that everything seems to work when the  
> MAC is placed into full-duplex mode hints that the network is really a  
> full-duplex network.

No, it's half. Can be configured so on both sides, with or
without auto-negotiation. The "10/half" message comes from a
PHY layer, the PHY layer reports human readable values of
PHY's LPA/BMSR registers, not MAC's configuration.

Of course, it could be that the root cause of the problems
I observe is weird NIC on my host. Well, then QA team should
have used the same broken NIC on their hosts. :-)

I can easily test it by interconnecting two targets though.

> Otherwise, if the network is really half-duplex, then presence of a  
> full-duplex node will result in the other nodes seeing CRC/framing  
> errors on receive, and possibly also late collisions, as the full-duplex  
> node does not observe the CS or the CD( carrier sense and collision  
> detect) part of CSMA/CD, because it doesn't care.
>
> Putting a node in full-duplex will always make the nasty collision  
> related errors go away, but it may not be a proper diagnosis of the 
> problem.

>>        tx-frames-ok: 4967
>>        tx-256-511-frames: 3
>>        tx-512-1023-frames: 79
>>        tx-1024-1518-frames: 71
>>        rx-256-511-frames: 37
>>        rx-512-1023-frames: 73
>>        rx-1024-1518-frames: 5243
>>
>> According to current QEIWRM (Rev. 2 5/2009), FDX bit can be 0 for
>> RGMII(10/100) modes, while MPC8568ERM (Rev. C 02/2007) spec says
>> that cleared FDX bit is permitted for MII/RMII modes only.
>>
>> The symptoms above were seen on MPC8569E-MDS boards, so QEIWRM is
>> clearly wrong, and this patch completely cures the problems above.
>
> Not so fast - RGMII and GMII refer to the interface between the MAC and  
> the PHY.

Correct.

> While Gigabit physical links will always be full-duplex, phys  
> that detect lower operational modes will indicate half-duplex where  
> needed, and putting the MAC into full-duplex will make other nodes see  
> errors.

D'oh!

[1358634.636147] eth1: Transmit error, Tx status register 82.
[1358634.636150] Probably a duplex mismatch.  See Documentation/networking/vortex.txt

It's on a host side.

> As Andy indicated later, it may be necessary to alter the interface  
> definition in those cases, depending on the particular hardware. Forcing 
> full-duplex does not seem to be a general solution.

Definitely. Though I'm out of ideas if it's NOT host-side issue.

Thanks!

-- 
Anton Vorontsov
email: cbouatmailru@gmail.com
irc://irc.freenode.net/bd2

^ permalink raw reply

* Re: [PATCH v2 1/2] ucc_geth: Fix half-duplex operation for non-MII/RMII interfaces
From: Anton Vorontsov @ 2009-06-25  7:08 UTC (permalink / raw)
  To: David Miller; +Cc: linuxppc-dev, Li Yang, Andy Fleming, netdev
In-Reply-To: <20090625010232.GA28590@oksana.dev.rtsoft.ru>

On Thu, Jun 25, 2009 at 05:02:32AM +0400, Anton Vorontsov wrote:
> Currently the half-duplex operation seems to not work reliably for
> RGMII PHY interfaces. It takes about 10 minutes to boot NFS rootfs
> using 10/half link, following symptoms were observed:

David, please ignore these patches, they just appear to
workaround the issue, but they don't fix the root cause (which
is unknown yet), and may cause other side to see the duplex
errors. Heh...

Thanks,

-- 
Anton Vorontsov
email: cbouatmailru@gmail.com
irc://irc.freenode.net/bd2

^ permalink raw reply

* Re: [PATCH v2 1/2] ucc_geth: Fix half-duplex operation for non-MII/RMII interfaces
From: David Miller @ 2009-06-25  8:07 UTC (permalink / raw)
  To: avorontsov; +Cc: linuxppc-dev, leoli, afleming, netdev
In-Reply-To: <20090625070813.GA25653@oksana.dev.rtsoft.ru>

From: Anton Vorontsov <avorontsov@ru.mvista.com>
Date: Thu, 25 Jun 2009 11:08:13 +0400

> On Thu, Jun 25, 2009 at 05:02:32AM +0400, Anton Vorontsov wrote:
>> Currently the half-duplex operation seems to not work reliably for
>> RGMII PHY interfaces. It takes about 10 minutes to boot NFS rootfs
>> using 10/half link, following symptoms were observed:
> 
> David, please ignore these patches, they just appear to
> workaround the issue, but they don't fix the root cause (which
> is unknown yet), and may cause other side to see the duplex
> errors. Heh...

Ok, thanks for the heads-up.

^ permalink raw reply

* Re: 85xx Address space query
From: kernel mailz @ 2009-06-25 10:51 UTC (permalink / raw)
  To: Scott Wood; +Cc: linuxppc-dev
In-Reply-To: <4A42AE1A.1090107@freescale.com>

So this means
when kernel gets interrupted by app which may be in PID=5 (say)
kernel translations for PID=0 remain valid ?
I am not able to follow Scott

-TZ

On Thu, Jun 25, 2009 at 4:22 AM, Scott Wood<scottwood@freescale.com> wrote:
> kernel mailz wrote:
>>
>> But If the app was running with PID=1, interrupt occurs, kernel code
>> gets executed in PID=1, how does the kernel handle this ? and goes
>> back to PID=0, since its translations are all in PID=0
>
> PID 0 is special, it's mappings are present regardless of the value of the
> PID register.
>
> -Scott
>

^ permalink raw reply

* Re: [PATCH] ucc_geth: Fix half-duplex operation for non-MII/RMII interfaces
From: Anton Vorontsov @ 2009-06-25 11:17 UTC (permalink / raw)
  To: Mark Huth; +Cc: linuxppc-dev, netdev, Li Yang, David Miller
In-Reply-To: <20090625070236.GA27711@oksana.dev.rtsoft.ru>

On Thu, Jun 25, 2009 at 11:02:36AM +0400, Anton Vorontsov wrote:
[...]
> Of course, it could be that the root cause of the problems
> I observe is weird NIC on my host. Well, then QA team should
> have used the same broken NIC on their hosts. :-)
> 
> I can easily test it by interconnecting two targets though.
[...]
> Definitely. Though I'm out of ideas if it's NOT host-side issue.

Two boards interconnected work fine. eTSEC + SKY2 NIC also work
OK. I'll test some more NICs, known to not work is 3Com
Corporation 3c905C-TX/TX-M [Tornado] (rev 74).


After some netperf tests, statistic on a gianfar board:

# ethtool -S eth0 | grep -v ": 0"
NIC statistics:
     rx-dropped-by-kernel: 2
     tx-rx-64-frames: 52
     tx-rx-65-127-frames: 20668
     tx-rx-128-255-frames: 10343
     tx-rx-256-511-frames: 113
     tx-rx-512-1023-frames: 147
     tx-rx-1024-1518-frames: 45965
     rx-bytes: 45075782
     rx-packets: 50300
     rx-carrier-sense-error: 1
     rx-fragmented-frames: 7600
     tx-byte-counter: 27401677
     tx-packets: 34618
     tx-broadcast-packets: 3
     tx-deferral-packets: 5
     tx-single-collision-packets: 4039
     tx-multiple-collision-packets: 1943
     tx-excessive-collision-packets: 30
     tx-total-collision: 785


Thanks for the ideas, Mark.

-- 
Anton Vorontsov
email: cbouatmailru@gmail.com
irc://irc.freenode.net/bd2

^ permalink raw reply

* Re: [PATCH v2] Fix RTAS watchdog driver temperature read functionality
From: Utz Bacher @ 2009-06-25 11:22 UTC (permalink / raw)
  To: Adrian Reber; +Cc: linuxppc-dev-bounces+adrian=lisas.de, linuxppc-dev
In-Reply-To: <20090611105216.GG8358@lisas.de>

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

Adrian Reber wrote on 11.06.2009 12:52:17:
> 
> I forgot about this patch. Can this still be merged?
> 
> 
> On Thu, Mar 26, 2009 at 01:05:42PM +0100, Adrian Reber wrote:
> > Using the RTAS watchdog driver to read out the temperature crashes
> > on a PXCAB:
> > 
> > Unable to handle kernel paging request for data at address 0xfe347b50
> > Faulting instruction address: 0xc00000000001af64
> > Oops: Kernel access of bad area, sig: 11 [#1]
> > 
> > The wrong usage of "(void *)__pa(&temperature)" in rtas_call() is
> > removed by using the function rtas_get_sensor() which does the
> > right thing.
> > 
> > Signed-off-by: Adrian Reber <adrian@lisas.de>
> > ---
> > v2: use rtas_get_sensor(); typo fixed
> > ---
> >  drivers/watchdog/wdrtas.c |    8 +++-----
> >  1 files changed, 3 insertions(+), 5 deletions(-)
> > 
> > diff --git a/drivers/watchdog/wdrtas.c b/drivers/watchdog/wdrtas.c
> > index 5d3b1a8..a9f00dc 100644
> > --- a/drivers/watchdog/wdrtas.c
> > +++ b/drivers/watchdog/wdrtas.c
> > @@ -214,16 +214,14 @@ static void wdrtas_timer_keepalive(void)
> >   */
> >  static int wdrtas_get_temperature(void)
> >  {
> > -   long result;
> > +   int result;
> >     int temperature = 0;
> > 
> > -   result = rtas_call(wdrtas_token_get_sensor_state, 2, 2,
> > -            (void *)__pa(&temperature),
> > -            WDRTAS_THERMAL_SENSOR, 0);
> > +   result = rtas_get_sensor(WDRTAS_THERMAL_SENSOR, 0, &temperature);
> > 
> >     if (result < 0)
> >        printk(KERN_WARNING "wdrtas: reading the thermal sensor "
> > -             "faild: %li\n", result);
> > +             "failed: %i\n", result);
> >     else
> >        temperature = ((temperature * 9) / 5) + 32; /* fahrenheit */
> > 
> > -- 
> > 1.5.6.6

makes sense. Second version also looks good to me -- thanks!

Acked-by: Utz Bacher <utz.bacher@de.ibm.com>

Utz

:wq

[-- Attachment #2: Type: text/html, Size: 2852 bytes --]

^ permalink raw reply

* Re: [PATCH v2] Fix RTAS watchdog driver temperature read functionality
From: Benjamin Herrenschmidt @ 2009-06-25 11:28 UTC (permalink / raw)
  To: Utz Bacher; +Cc: linuxppc-dev
In-Reply-To: <OF3E26F577.90C4A946-ONC12575E0.003E09AB-C12575E0.003E67E9@de.ibm.com>

On Thu, 2009-06-25 at 13:22 +0200, Utz Bacher wrote:
> 
> Adrian Reber wrote on 11.06.2009 12:52:17: 
> > 
> > I forgot about this patch. Can this still be merged?

Probably, can you re-send it in proper format to the list so it gets
into patchwork (unless it's already there in which case I'll pick it up
anyway) ?

Cheers,
Ben.

> > 
> > On Thu, Mar 26, 2009 at 01:05:42PM +0100, Adrian Reber wrote:
> > > Using the RTAS watchdog driver to read out the temperature crashes
> > > on a PXCAB:
> > > 
> > > Unable to handle kernel paging request for data at address
> 0xfe347b50
> > > Faulting instruction address: 0xc00000000001af64
> > > Oops: Kernel access of bad area, sig: 11 [#1]
> > > 
> > > The wrong usage of "(void *)__pa(&temperature)" in rtas_call() is
> > > removed by using the function rtas_get_sensor() which does the
> > > right thing.
> > > 
> > > Signed-off-by: Adrian Reber <adrian@lisas.de>
> > > ---
> > > v2: use rtas_get_sensor(); typo fixed
> > > ---
> > >  drivers/watchdog/wdrtas.c |    8 +++-----
> > >  1 files changed, 3 insertions(+), 5 deletions(-)
> > > 
> > > diff --git a/drivers/watchdog/wdrtas.c b/drivers/watchdog/wdrtas.c
> > > index 5d3b1a8..a9f00dc 100644
> > > --- a/drivers/watchdog/wdrtas.c
> > > +++ b/drivers/watchdog/wdrtas.c
> > > @@ -214,16 +214,14 @@ static void wdrtas_timer_keepalive(void)
> > >   */
> > >  static int wdrtas_get_temperature(void)
> > >  {
> > > -   long result;
> > > +   int result;
> > >     int temperature = 0;
> > >  
> > > -   result = rtas_call(wdrtas_token_get_sensor_state, 2, 2,
> > > -            (void *)__pa(&temperature),
> > > -            WDRTAS_THERMAL_SENSOR, 0);
> > > +   result = rtas_get_sensor(WDRTAS_THERMAL_SENSOR, 0,
> &temperature);
> > >  
> > >     if (result < 0)
> > >        printk(KERN_WARNING "wdrtas: reading the thermal sensor "
> > > -             "faild: %li\n", result);
> > > +             "failed: %i\n", result);
> > >     else
> > >        temperature = ((temperature * 9) / 5) + 32; /* fahrenheit
> */
> > >  
> > > -- 
> > > 1.5.6.6
> 
> makes sense. Second version also looks good to me -- thanks! 
> 
> Acked-by: Utz Bacher <utz.bacher@de.ibm.com> 
> 
> Utz 
> 
> :wq

^ permalink raw reply

* Re: 85xx Address space query
From: Benjamin Herrenschmidt @ 2009-06-25 11:32 UTC (permalink / raw)
  To: Kumar Gala; +Cc: linuxppc-dev, kernel mailz
In-Reply-To: <D47FC871-1535-4DBF-8617-6EEA8C180484@kernel.crashing.org>


> > 1. User code executes ioctl
> > 2. interrupt goes to the kernel
> 
> On the interrupt the PR changes from 0 -> 1

The other way around actually :-)

> > 3. ioctl handler in driver gets invoked
> > The buffer pointer still contains 0x10000.
> >
> > How kernel code running in PR=0 accesses it and does the copy. I am  
> > not able to see a address space switch in the asm code of  
> > copy_tofrom_user.
> 
> There isn't a address space switch.  But address spaces exist at the  
> same time.  The user app is given 0..0xc000_0000 and the kernel uses  
> 0xc000_0000..0xffff_ffff.

You may want to add, to make things clearer, that the HW treats PID 0
specially. Translations in the TLB that have PID (we call it TID in the
TLB entry) 0 match any value of the PID register.

So kernel pages all have TID = 0, which means they are visible to all
processes, but have permissions set such that only the supervisor (ie.
PR = 0) can actually access them. MSR:PR is automatically switched to 0
by the processor when taking an interrupt, allowing the kernel thus to
access both its own pages and the pages of the current process.

Cheers,
Ben.

^ permalink raw reply

* Re: 85xx Address space query
From: Benjamin Herrenschmidt @ 2009-06-25 11:33 UTC (permalink / raw)
  To: kernel mailz; +Cc: linuxppc-dev
In-Reply-To: <abe8a1fd0906241046x2e3ee7d5h6a6c0231086a6f0e@mail.gmail.com>

On Wed, 2009-06-24 at 23:16 +0530, kernel mailz wrote:
> But If the app was running with PID=1, interrupt occurs, kernel code
> gets executed in PID=1, how does the kernel handle this ? and goes
> back to PID=0, since its translations are all in PID=0

PID 0 is a special case in HW. TLB entries with TID=0 will match any PID
value.

Cheers,
Ben.

^ permalink raw reply

* Direct MII connection between MPC8313 and VIRTEX FPGA
From: Frank Prepelica @ 2009-06-25 15:11 UTC (permalink / raw)
  To: linuxppc-dev

Hi all,

we removed the ethernet PHYs (MARVELL) from our customized board (based =
on MPC8313ERDB) and=20
connected the CPU Ethernet lines directly to a FPGA which is now =
emulating the PHY. But the ifconfig=20
command cannot find that eth device anymore. Does the emulated PHY needs =
to provide MDIO=20
information that the ETH device can be found again?

Any help is very appreciated

Best regards
Frank





Frank Prepelica
Software Design Engineer

Ubidyne GmbH
Lise-Meitner-Str.-14
89081 Ulm - Germany

Phone:=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0 +49 731 88 00 71 58
Fax:=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0 +49 731 88 00 71 99
Email:=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0 =
frank.prepelica@ubidyne.com
Homepage:=A0=A0=A0=A0=A0=A0 www.ubidyne.com
=A0
Registered office: Ulm
District court of Ulm: HRB 5295
Managing Directors:
Dipl. Ing. Ken Hawk
Dipl. Ing. Beat M=FCller
Dipl. Ing. Mike Levis

^ permalink raw reply

* Re: 85xx Address space query
From: Kumar Gala @ 2009-06-25 17:32 UTC (permalink / raw)
  To: kernel mailz; +Cc: Scott Wood, linuxppc-dev
In-Reply-To: <abe8a1fd0906250351y1aadc153m9f52f65a5860a2ca@mail.gmail.com>

That is correct.  The PID = 0 translations are always valid.

- k

On Jun 25, 2009, at 5:51 AM, kernel mailz wrote:

> So this means
> when kernel gets interrupted by app which may be in PID=5 (say)
> kernel translations for PID=0 remain valid ?
> I am not able to follow Scott
>
> -TZ
>
> On Thu, Jun 25, 2009 at 4:22 AM, Scott Wood<scottwood@freescale.com>  
> wrote:
>> kernel mailz wrote:
>>>
>>> But If the app was running with PID=1, interrupt occurs, kernel code
>>> gets executed in PID=1, how does the kernel handle this ? and goes
>>> back to PID=0, since its translations are all in PID=0
>>
>> PID 0 is special, it's mappings are present regardless of the value  
>> of the
>> PID register.
>>
>> -Scott
>>
> _______________________________________________
> Linuxppc-dev mailing list
> Linuxppc-dev@lists.ozlabs.org
> https://lists.ozlabs.org/listinfo/linuxppc-dev

^ permalink raw reply

* Re: 85xx Address space query
From: kernel mailz @ 2009-06-25 17:52 UTC (permalink / raw)
  To: Kumar Gala; +Cc: Scott Wood, linuxppc-dev
In-Reply-To: <4FA3B563-2881-4F9E-8B23-C36FB439BE2B@kernel.crashing.org>

If an aap has to be placed in AS=1 and it issues an ioctl, kernel
needs to be modified ?
I guess the PID=0 trick will work when AS is same

right ?



On 6/25/09, Kumar Gala <galak@kernel.crashing.org> wrote:
> That is correct.  The PID = 0 translations are always valid.
>
> - k
>
> On Jun 25, 2009, at 5:51 AM, kernel mailz wrote:
>
>> So this means
>> when kernel gets interrupted by app which may be in PID=5 (say)
>> kernel translations for PID=0 remain valid ?
>> I am not able to follow Scott
>>
>> -TZ
>>
>> On Thu, Jun 25, 2009 at 4:22 AM, Scott Wood<scottwood@freescale.com>
>> wrote:
>>> kernel mailz wrote:
>>>>
>>>> But If the app was running with PID=1, interrupt occurs, kernel code
>>>> gets executed in PID=1, how does the kernel handle this ? and goes
>>>> back to PID=0, since its translations are all in PID=0
>>>
>>> PID 0 is special, it's mappings are present regardless of the value
>>> of the
>>> PID register.
>>>
>>> -Scott
>>>
>> _______________________________________________
>> Linuxppc-dev mailing list
>> Linuxppc-dev@lists.ozlabs.org
>> https://lists.ozlabs.org/listinfo/linuxppc-dev
>
>

^ permalink raw reply

* Re: 85xx Address space query
From: Kumar Gala @ 2009-06-25 17:58 UTC (permalink / raw)
  To: kernel mailz; +Cc: Scott Wood, linuxppc-dev
In-Reply-To: <abe8a1fd0906251052k22ceabfcr378133af61d2ff71@mail.gmail.com>


On Jun 25, 2009, at 12:52 PM, kernel mailz wrote:

> If an aap has to be placed in AS=1 and it issues an ioctl, kernel
> needs to be modified ?

Correct, this would be a significant change to the kernel.

> I guess the PID=0 trick will work when AS is same
>
> right ?

correct.

- k

>
> On 6/25/09, Kumar Gala <galak@kernel.crashing.org> wrote:
>> That is correct.  The PID = 0 translations are always valid.
>>
>> - k
>>
>> On Jun 25, 2009, at 5:51 AM, kernel mailz wrote:
>>
>>> So this means
>>> when kernel gets interrupted by app which may be in PID=5 (say)
>>> kernel translations for PID=0 remain valid ?
>>> I am not able to follow Scott
>>>
>>> -TZ
>>>
>>> On Thu, Jun 25, 2009 at 4:22 AM, Scott Wood<scottwood@freescale.com>
>>> wrote:
>>>> kernel mailz wrote:
>>>>>
>>>>> But If the app was running with PID=1, interrupt occurs, kernel  
>>>>> code
>>>>> gets executed in PID=1, how does the kernel handle this ? and goes
>>>>> back to PID=0, since its translations are all in PID=0
>>>>
>>>> PID 0 is special, it's mappings are present regardless of the value
>>>> of the
>>>> PID register.
>>>>
>>>> -Scott
>>>>
>>> _______________________________________________
>>> Linuxppc-dev mailing list
>>> Linuxppc-dev@lists.ozlabs.org
>>> https://lists.ozlabs.org/listinfo/linuxppc-dev
>>
>>

^ permalink raw reply

* Slowing down the schedular, How?
From: kernel mailz @ 2009-06-25 18:38 UTC (permalink / raw)
  To: linuxppc-dev
In-Reply-To: <abe8a1fd0906251136t36d78428k90a610fdff987ab6@mail.gmail.com>

Hi,

I have a SMP linux running on 85xx poweprc. Say on Core 0 and Core 1
two different processes are running and on both the schedular runs.
Now for some special case, if one of my process issues a ioctl which
gets serviced by a kernel module, I wish to slow the schedular on that
core only. Otherwise the performance will get degraded.

Should I use highest priority tasklet, will it be sufficient or i need
to do something special

-TZ

^ permalink raw reply

* Re: Subject: [PATCH v6] spi: Add PPC4xx SPI driver
From: Steven A. Falco @ 2009-06-25 18:53 UTC (permalink / raw)
  To: David Brownell, Stefan Roese; +Cc: linuxppc-dev@ozlabs.org
In-Reply-To: <200906251833.n5PIX42Z017229@saf.cs.myharris.net>

Stefan suggested that I try to address the comments against the PPC4xx
SPI driver, so here goes...

A post of version 7 of the driver will follow this email, but I thought
it might make it easier on everyone to inline my comments here.  Hence,
this brief, introductory "top post".

> On Thursday 08 January 2009, Stefan Roese wrote:
>> This adds a SPI driver for the SPI controller found in the IBM/AMCC
>> 4xx PowerPC's.
> 
> 
>> +/*
>> + * The PPC4xx SPI controller has no FIFO so each sent/received byte will
>> + * generate an interrupt to the CPU. This can cause high CPU utilization.
>> + * This driver allows platforms to reduce the interrupt load on the CPU
>> + * during SPI transfers by setting max_speed_hz via the device tree.
> 
> Note that an alternate strategy is to use polling instead of IRQs,
> at least when the data rate is high enough that the IRQ processing
> is also slowing down the data transfers.
> 
> 
>> +/* bits in mode register - bit 0 ist MSb */
>> +/* data latched on leading edge of clock, else trailing edge */
>> +#define SPI_PPC4XX_MODE_SCP	(0x80 >> 3)
> 
> Or in short, SCP == CPHA.
> 

Actually, SCP = !CPHA.  I've added a comment to that effect in v7.

>> +/* port enabled */
>> +#define SPI_PPC4XX_MODE_SPE	(0x80 >> 4)
>> +/* MSB first, else LSB first */
>> +#define SPI_PPC4XX_MODE_RD	(0x80 >> 5)
>> +/* clock invert - idle clock = 1, active clock = 0; else reversed */
>> +#define SPI_PPC4XX_MODE_CI	(0x80 >> 6)
> 
> Or in short, CI == CPOL.
> 

Correct.  Comment added.

>> +/* loopback enable */
>> +#define SPI_PPC4XX_MODE_IL	(0x80 >> 7)
>> +/* bits in control register */
>> +/* starts a transfer when set */
>> +#define SPI_PPC4XX_CR_STR	(0x80 >> 7)
>> +/* bits in status register */
>> +/* port is busy with a transfer */
>> +#define SPI_PPC4XX_SR_BSY	(0x80 >> 6)
>> +/* RxD ready */
>> +#define SPI_PPC4XX_SR_RBR	(0x80 >> 7)
>> +
>> +/* the spi->mode bits understood by this driver: */
>> +#define MODEBITS	(SPI_CPHA | SPI_CPOL | SPI_CS_HIGH | SPI_LSB_FIRST)
>> +
>> +/* clock settings (SCP and CI) for various SPI modes */
>> +#define SPI_CLK_MODE0	SPI_PPC4XX_MODE_SCP
>> +#define SPI_CLK_MODE1	0
>> +#define SPI_CLK_MODE2	SPI_PPC4XX_MODE_CI
>> +#define SPI_CLK_MODE3	(SPI_PPC4XX_MODE_SCP | SPI_PPC4XX_MODE_CI)
> 
> Color me puzzled then.  Either the definitions of MODE0 and MODE1
> are swapped here ... or the comments above are wrong, and SCP should
> describe "falling" vs "rising" instead of "leading" vs "trailing".
> 
> 

The modes were wrong.  MODE0 and MODE1 were correct, but MODE2 and MODE3
were interchanged.  Fixed in v7, and verified on a logic analyzer.

>> +static int spi_ppc4xx_setupxfer(struct spi_device *spi, struct spi_transfer *t)
>> +{
>> +	struct ppc4xx_spi *hw = spi_master_get_devdata(spi->master);
>> +	struct spi_ppc4xx_cs *cs = spi->controller_state;
>> +	int scr;
>> +	u8 cdm = 0;
>> +	u32 speed;
>> +	u8 bits_per_word;
>> +
>> +	bits_per_word = (t) ? t->bits_per_word : spi->bits_per_word;
>> +	speed = (t) ? t->speed_hz : spi->max_speed_hz;
>> +
>> +	...
>> +
>> +	if (!speed || (speed > spi->max_speed_hz)) {
> 
> This is wrong.  Typical case is that t->speed_hz is zero,
> meaning "use spi->max_speed_hz" ... you treat that as an error.
> 

Correct.  I've fixed the logic for t->speed_hz and for t->bits_per_word.

>> +		dev_err(&spi->dev, "invalid speed_hz (%d)\n", speed);
>> +		return -EINVAL;
>> +	}
>> +
>> +	...
>> +}
>> +
>> +static int spi_ppc4xx_setup(struct spi_device *spi)
>> +{
>> +	int ret;
>> +	struct spi_ppc4xx_cs *cs = spi->controller_state;
>> +	int init = 0;
> 
> This "init" thing is still wrong.

Correct.  I've removed it entirely.

> 
> Consider:  board gets set up with one device.  Later,
> *WHILE BUSY TRANSFERRING DATA TO/FROM THAT DEVICE*
> some other device gets instantiated.  Then ...
> 
> 
>> +	if (cs == NULL) {
>> +		cs = kzalloc(sizeof *cs, GFP_KERNEL);
>> +		if (!cs)
>> +			return -ENOMEM;
>> +		spi->controller_state = cs;
>> +
>> +		/*
>> +		 * First time called, so let's init the SPI controller
>> +		 * at the end of this function
>> +		 */
>> +		init = 1;
> 
> "init" becomes true here, although the controller has
> already been initialized.  If it needs to be set up,
> do it in probe() before registering the spi_master.
> 
> 
>> +	}
>> +
>> +	...
>> +
>> +	/*
>> +	 * New configuration (mode, speed etc) will be written to the
>> +	 * controller in spi_ppc4xx_setupxfer(). Only call
>> +	 * spi_ppc4xx_setupxfer() directly upon first initialization.
>> +	 */
>> +	if (init) {
>> +		ret = spi_ppc4xx_setupxfer(spi, NULL);
> 
> ... so blam, you clobber the settings currently being used for
> the active transfer.  So this would be a bug.
> 
> If not ... this driver deserves a comment on exactly how
> unusual this driver is.  Specifically, that all spi_device
> children must be set up *in advance* so that standard calls
> like spi_new_device() don't work with it; and why.
> 
> I don't think I see anything in this driver which would
> prevent that from working, though.  Sure you've got to
> have a list of chipselect GPIOs in advance, but that's
> distinct from being unable to use spi_new_device().
> 
> 
>> +	...
>> +}
>> +
>> +static void spi_ppc4xx_chipsel(struct spi_device *spi, int value)
>> +{
>> +	struct ppc4xx_spi *hw = spi_master_get_devdata(spi->master);
>> +	unsigned int cspol = spi->mode & SPI_CS_HIGH ? 1 : 0;
>> +	unsigned int cs = spi->chip_select;
>> +
>> +	if (!hw->num_gpios)
>> +		return;
> 
> Hmm, num_gpios ... can never be zero, right?  You always
> set it up so that of_gpio_count() GPIOs can all be used
> as chipselects.  Looks like this check is not needed.
> 

I believe that it can be zero, in the case where there is a single
"always on" device.  But I have modified this test to use
hw->master->num_chipselect as per your next comment.  I've also added a
check for the special case of EEXIST, which on powerpc means "a hole in
the list of gpio's".  I actually use this for an AVR device, because it
won't tolerate transitions on its CS lead, so I manage its CS outside of
the driver.  I can explain further if you want the details of our
hardware ugliness. :-)

>> +
>> +	if (cs >= hw->num_gpios)
>> +		return;
> 
> I guess I don't see why you have num_gpio instead of just
> using the chipselect count, either.  (Assuming that the
> of_gpio_count routine isn't counting *all* gpios, instead
> of just ones allocated to this controller, which would be
> a different issue.)
> 
> 

Correct, and fixed in v7.

>> +
>> +	if (value != BITBANG_CS_INACTIVE && value != BITBANG_CS_ACTIVE)
>> +		return;
> 
> Strange, what other values could be passed??
> 
> 

I've removed this test - it is superfluous.

>> +
>> +	if (value == BITBANG_CS_INACTIVE)
>> +		cspol = !cspol;
>> +
>> +	gpio_set_value(hw->gpios[cs], cspol);
>> +}
>> +
>> +static irqreturn_t spi_ppc4xx_int(int irq, void *dev_id)
>> +{
>> +	struct ppc4xx_spi *hw;
>> +	u8 status;
>> +	u8 data;
>> +	unsigned int count;
>> +
>> +	hw = (struct ppc4xx_spi *)dev_id;
>> +
>> +	status = in_8(&hw->regs->sr);
>> +	if (!status)
>> +		return IRQ_NONE;
>> +
>> +	/* should never happen but check anyway */
>> +	if (status & SPI_PPC4XX_SR_BSY) {
> 
> Maybe "if (unlikely(...)) {" then.
> 
> 

I added unlikely() and a comment explaining why this test might be
needed.  I've not observed it myself, but the documentation seems to
allow for the possibility.

>> +		u8 lstatus;
>> +		int cnt = 0;
>> +
>> +		dev_dbg(hw->dev, "got interrupt but spi still busy?\n");
> 
> But ... *does* this ever happen?  If not, I'd strike this
> code.  And if it does, I'd add a comment explaining what's
> known about this hardware bug.  Without this, the IRQ handler
> would look pretty tight.
> 

See the comment in the v7 code for why this might be necessary.

>> +		do {
>> +			ndelay(10);
>> +			lstatus = in_8(&hw->regs->sr);
>> +		} while (++cnt < 100 && lstatus & SPI_PPC4XX_SR_BSY);
>> +
>> +		if (cnt >= 100) {
>> +			dev_err(hw->dev, "busywait: too many loops!\n");
>> +			complete(&hw->done);
>> +			return IRQ_HANDLED;
>> +		} else {
>> +			/* status is always 1 (RBR) here */
>> +			status = in_8(&hw->regs->sr);
>> +			dev_dbg(hw->dev, "loops %d status %x\n", cnt, status);
>> +		}
>> +	}
>> +
>> +	count = hw->count;
>> +	hw->count++;
>> +
>> +	if (status & SPI_PPC4XX_SR_RBR) {
>> +		/* Data Ready */
> 
> Most SPI hardware I've seen has at most a weak distinction
> between "tx done" and "rx done" IRQs ... there's little
> point to separate IRQs, since shifting out the last TX bit
> is what shifts in the last RX bit.
> 
> Unless this is unusual hardware you can probably just assume
> that there's RX data, and only read it if hw->rx is non-null.
> Or, if this is *not* set, report the odd behavior...
> 
> 

I've removed this test.  The docs clearly state that the RBR bit is the
cause of the interrupt, so there is no reason to test it.

>> +		data = in_8(&hw->regs->rxd);
>> +		if (hw->rx)
>> +			hw->rx[count] = data;
>> +	}
>> +
>> +	count++;
>> +
>> +	if (count < hw->len) {
>> +		data = hw->tx ? hw->tx[count] : 0;
>> +		out_8(&hw->regs->txd, data);
>> +		out_8(&hw->regs->cr, SPI_PPC4XX_CR_STR);
>> +	} else {
>> +		complete(&hw->done);
>> +	}
>> +
>> +	return IRQ_HANDLED;
>> +}
>> +
>> +		...
>> +
> 
>> +static int __init spi_ppc4xx_of_probe(struct of_device *op,
>> +				      const struct of_device_id *match)
>> +{
>> +	struct ppc4xx_spi *hw;
>> +	struct spi_master *master;
>> +	struct spi_bitbang *bbp;
>> +	struct resource resource;
>> +	struct device_node *np = op->node;
>> +	struct device *dev = &op->dev;
>> +	struct device_node *opbnp;
>> +	int ret;
>> +	const unsigned int *clk;
>> +
>> +	master = spi_alloc_master(dev, sizeof *hw);
>> +	if (master == NULL)
>> +		return -ENOMEM;
>> +	dev_set_drvdata(dev, master);
>> +	hw = spi_master_get_devdata(master);
>> +	memset(hw, 0, sizeof(struct ppc4xx_spi));
> 
> That memset is not required, it comes to you pre-zeroed.
> 

Removed.

>> +
>> +	hw->master = spi_master_get(master);
>> +	hw->dev = dev;
>> +
>> +	init_completion(&hw->done);
>> +
>> +	hw->num_gpios = of_gpio_count(np);
> 
> Worth a comment what's going on there.  I'm assuming this
> of_gpio_count() thing returns a count of GPIOs somehow
> associated with this specific device tree node, rather than
> say all GPIOs known to the device tree...
> 

I've beefed up the comments for this in v7.  Please let me know if you
want more.

>> +	if (hw->num_gpios) {
>> +		int i;
>> +
>> +		hw->gpios = kzalloc(sizeof(int) * hw->num_gpios,
>> +				    GFP_KERNEL);
>> +		if (!hw->gpios) {
>> +			ret = -ENOMEM;
>> +			goto free_master;
>> +		}
>> +
>> +		for (i = 0; i < hw->num_gpios; i++) {
>> +			int gpio = of_get_gpio(np, i);
>> +			if (gpio < 0) {
>> +				dev_err(dev, "Invalid gpio spec %d\n", i);
>> +				ret = gpio;
>> +				goto free_gpios;
>> +			}
>> +
>> +			ret = gpio_request(gpio, np->name);
>> +			if (ret < 0) {
>> +				dev_err(dev, "gpio %d already in use\n", i);
>> +				ret = gpio;
>> +				goto free_gpios;
>> +			}
>> +
>> +			gpio_direction_output(gpio, 0);
>> +			hw->gpios[i] = gpio;
>> +		}
>> +	}
> 
> Else ... error, if there are no chipselects?
> 
> I've got a patch pending to add a "no chipselect" spi->mode flag.
> If you expect to support that, please let me know.
> 
> 

I guess we should allow the case of a single device with no CS.  I don't
have such a configuration, but I think the way I've written the
spi_ppc4xx_chipsel() will handle that case.

>> +
>> +	/* Setup the state for the bitbang driver */
>> +	bbp = &hw->bitbang;
>> +	bbp->master = hw->master;
>> +	bbp->setup_transfer = spi_ppc4xx_setupxfer;
>> +	bbp->chipselect = spi_ppc4xx_chipsel;
>> +	bbp->txrx_bufs = spi_ppc4xx_txrx;
>> +	bbp->use_dma = 0;
>> +	bbp->master->setup = spi_ppc4xx_setup;
>> +	bbp->master->cleanup = spi_ppc4xx_cleanup;
>> +	/* only one SPI controller */
>> +	bbp->master->bus_num = 0;
> 
> Bad assumption.  There could be GPIO-based adapters,
> of course.  And aren't these the cores which can be
> found on higher end Xilinx FPGAs?  And, accordingly,
> integrated with any number of additional controllers?
> 
> If so, the restriction is just that this controller
> be bus #0.  If not ... the Kconfig should say more
> about *which* PPC4xx chips have this controller.
> (I suspect it's the latter.)
> 
> 

I've changed this to ...bus_num = -1.  The dynamic allocation works for
me.

>> +	if (bbp->master->num_chipselect == 0) {
> 
> You didn't set it, so of course it's still zeroed.
> 
> 

Removed this test.

>> +		/* this many pins in al GPIO controllers */
>> +		bbp->master->num_chipselect = hw->num_gpios;
> 
> So num_chipselect is alays num_gpios, and there's no point
> to having num_gpios since you could always use num_chipselect.
> 
> 

Correct.  Substituted num_chipselect for num_gpios in v7.

>> +	}
>> +
>> +	...
>> +}
> 
> 

^ permalink raw reply

* Subject: [PATCH v7] spi: Add PPC4xx SPI driver
From: Steven A. Falco @ 2009-06-25 19:12 UTC (permalink / raw)
  To: David Brownell, Stefan Roese, linuxppc-dev@ozlabs.org

This adds a SPI driver for the SPI controller found in the IBM/AMCC
4xx PowerPC's.

Signed-off-by: Stefan Roese <sr@denx.de>
Signed-off-by: Wolfgang Ocker <weo@reccoware.de>
Acked-by: Josh Boyer <jwboyer@linux.vnet.ibm.com>
Signed-off-by: Steven A. Falco <sfalco@harris.com>
---
Note, I have not removed any of the prior sign-offs, but feel free to
look this version over very closely, and let me know if you demand a
"retraction".  :-)

I've successfully used this driver on a custom PPC440EPx based board
with three devices on the spi bus, one of which uses the "chip-select
hole" feature.

Changes in v7:
- Additional comments as per David Brownell's review
- Corrected phase in mode 2 and 3
- Corrected speed and bits_per_word logic in spi_ppc4xx_setupxfer
- Removed extraneous tests as per David's review
- Added support for "holes" in the chip-select list
- Using dynamic bus allocation
- Corrected initialization logic

Changes in v6:
- Moved comment about high interrupt load to top of file and extended it
  by explaining that the 4xx SPI controller has no FIFOs.
- Added parameter checking to setup() routine.
- Removed comment about LSB
- Used of_gpio_count() instead creating own static implementation as
  suggested by Anton.

Changes in v5:
- Don't call setupxfer() from setup() so that the baudrate etc
  won't get changed while another transfer is active, as suggested
  by David Brownell.
- module_{init,exit} moved directly to the functions to which they
  apply.
- Use __func__ instead of __FUNCTION__.

Changes in v4:
- Added fixes suggested by Josh Boyer
- Changed compatible property from "ibm,spi" to "ibm,ppc4xx-spi"

Changes in v3:
- When the device is removed the GPIOs are released. The memory
  for the GPIO array is freed.

Changes in v2:
- Now the gpios property is correctly decoded and the
  resulting gpio numbers are used as the devices chip
  selects.

diff --git a/drivers/spi/Kconfig b/drivers/spi/Kconfig
index 83a185d..2f0a3df 100644
--- a/drivers/spi/Kconfig
+++ b/drivers/spi/Kconfig
@@ -171,6 +171,13 @@ config SPI_ORION
 	help
 	  This enables using the SPI master controller on the Orion chips.
 
+config SPI_PPC4xx
+	tristate "PPC4xx SPI Controller"
+	depends on 4xx && SPI_MASTER
+	select SPI_BITBANG
+	help
+	  This selects a driver for the PPC4xx SPI Controller.
+
 config SPI_PXA2XX
 	tristate "PXA2xx SSP SPI master"
 	depends on ARCH_PXA && EXPERIMENTAL
diff --git a/drivers/spi/Makefile b/drivers/spi/Makefile
index 5d04519..ed1d74f 100644
--- a/drivers/spi/Makefile
+++ b/drivers/spi/Makefile
@@ -23,8 +23,9 @@ obj-$(CONFIG_SPI_PXA2XX)		+= pxa2xx_spi.o
 obj-$(CONFIG_SPI_OMAP_UWIRE)		+= omap_uwire.o
 obj-$(CONFIG_SPI_OMAP24XX)		+= omap2_mcspi.o
 obj-$(CONFIG_SPI_ORION)			+= orion_spi.o
 obj-$(CONFIG_SPI_MPC52xx_PSC)		+= mpc52xx_psc_spi.o
 obj-$(CONFIG_SPI_MPC83xx)		+= spi_mpc83xx.o
+obj-$(CONFIG_SPI_PPC4xx)		+= spi_ppc4xx.o
 obj-$(CONFIG_SPI_S3C24XX_GPIO)		+= spi_s3c24xx_gpio.o
 obj-$(CONFIG_SPI_S3C24XX)		+= spi_s3c24xx.o
 obj-$(CONFIG_SPI_TXX9)			+= spi_txx9.o
diff --git a/drivers/spi/spi_ppc4xx.c b/drivers/spi/spi_ppc4xx.c
new file mode 100644
index 0000000..631bca2
--- /dev/null
+++ b/drivers/spi/spi_ppc4xx.c
@@ -0,0 +1,623 @@
+/*
+ * SPI_PPC4XX SPI controller driver.
+ *
+ * Copyright (C) 2007 Gary Jennejohn <garyj@denx.de>
+ * Copyright 2008 Stefan Roese <sr@denx.de>, DENX Software Engineering
+ * Copyright 2009 Harris Corporation, Steven A. Falco <sfalco@harris.com>
+ *
+ * Based in part on drivers/spi/spi_s3c24xx.c
+ *
+ * Copyright (c) 2006 Ben Dooks
+ * Copyright (c) 2006 Simtec Electronics
+ *	Ben Dooks <ben@simtec.co.uk>
+ *
+ * This program is free software; you can redistribute  it and/or modify it
+ * under the terms of the GNU General Public License version 2 as published
+ * by the Free Software Foundation.
+ */
+
+/*
+ * The PPC4xx SPI controller has no FIFO so each sent/received byte will
+ * generate an interrupt to the CPU. This can cause high CPU utilization.
+ * This driver allows platforms to reduce the interrupt load on the CPU
+ * during SPI transfers by setting max_speed_hz via the device tree.
+ */
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/sched.h>
+#include <linux/errno.h>
+#include <linux/wait.h>
+#include <linux/of_platform.h>
+#include <linux/of_spi.h>
+#include <linux/of_gpio.h>
+#include <linux/interrupt.h>
+#include <linux/delay.h>
+
+#include <linux/gpio.h>
+#include <linux/spi/spi.h>
+#include <linux/spi/spi_bitbang.h>
+
+#include <asm/io.h>
+#include <asm/dcr.h>
+#include <asm/dcr-regs.h>
+
+/* bits in mode register - bit 0 is MSb */
+
+/*
+ * SPI_PPC4XX_MODE_SCP = 0 means "data latched on trailing edge of clock"
+ * SPI_PPC4XX_MODE_SCP = 1 means "data latched on leading edge of clock"
+ * Note: This is the inverse of CPHA.
+ */
+#define SPI_PPC4XX_MODE_SCP	(0x80 >> 3)
+
+/* SPI_PPC4XX_MODE_SPE = 1 means "port enabled" */
+#define SPI_PPC4XX_MODE_SPE	(0x80 >> 4)
+
+/*
+ * SPI_PPC4XX_MODE_RD = 0 means "MSB first" - this is the normal mode
+ * SPI_PPC4XX_MODE_RD = 1 means "LSB first" - this is bit-reversed mode
+ * Note: This is identical to SPI_LSB_FIRST.
+ */
+#define SPI_PPC4XX_MODE_RD	(0x80 >> 5)
+
+/*
+ * SPI_PPC4XX_MODE_CI = 0 means "clock idles low"
+ * SPI_PPC4XX_MODE_CI = 1 means "clock idles high"
+ * Note: This is identical to CPOL.
+ */
+#define SPI_PPC4XX_MODE_CI	(0x80 >> 6)
+
+/*
+ * SPI_PPC4XX_MODE_IL = 0 means "loopback disable"
+ * SPI_PPC4XX_MODE_IL = 1 means "loopback enable"
+ */
+#define SPI_PPC4XX_MODE_IL	(0x80 >> 7)
+
+/* bits in control register */
+/* starts a transfer when set */
+#define SPI_PPC4XX_CR_STR	(0x80 >> 7)
+
+/* bits in status register */
+/* port is busy with a transfer */
+#define SPI_PPC4XX_SR_BSY	(0x80 >> 6)
+/* RxD ready */
+#define SPI_PPC4XX_SR_RBR	(0x80 >> 7)
+
+/* the spi->mode bits understood by this driver: */
+#define MODEBITS	(SPI_CPHA | SPI_CPOL | SPI_CS_HIGH | SPI_LSB_FIRST)
+
+/* clock settings (SCP and CI) for various SPI modes */
+#define SPI_CLK_MODE0	(SPI_PPC4XX_MODE_SCP | 0)
+#define SPI_CLK_MODE1	(0 | 0)
+#define SPI_CLK_MODE2	(SPI_PPC4XX_MODE_SCP | SPI_PPC4XX_MODE_CI)
+#define SPI_CLK_MODE3	(0 | SPI_PPC4XX_MODE_CI)
+
+#define DRIVER_NAME	"spi_ppc4xx_of"
+
+struct spi_ppc4xx_regs {
+	u8 mode;
+	u8 rxd;
+	u8 txd;
+	u8 cr;
+	u8 sr;
+	u8 dummy;
+	/*
+	 * Clock divisor modulus register
+	 * This uses the follwing formula:
+	 *    SCPClkOut = OPBCLK/(4(CDM + 1))
+	 * or
+	 *    CDM = (OPBCLK/4*SCPClkOut) - 1
+	 * bit 0 is the MSb!
+	 */
+	u8 cdm;
+};
+
+/* SPI Controller driver's private data. */
+struct ppc4xx_spi {
+	/* bitbang has to be first */
+	struct spi_bitbang bitbang;
+	struct completion done;
+
+	u64 mapbase;
+	u64 mapsize;
+	int irqnum;
+	/* need this to set the SPI clock */
+	unsigned int opb_freq;
+
+	/* for transfers */
+	int len;
+	int count;
+	/* data buffers */
+	const unsigned char *tx;
+	unsigned char *rx;
+
+	int *gpios;
+
+	struct spi_ppc4xx_regs __iomem *regs; /* pointer to the registers */
+	struct spi_master *master;
+	struct device *dev;
+};
+
+/* need this so we can set the clock in the chipselect routine */
+struct spi_ppc4xx_cs {
+	u8 mode;
+};
+
+static int spi_ppc4xx_txrx(struct spi_device *spi, struct spi_transfer *t)
+{
+	struct ppc4xx_spi *hw;
+	u8 data;
+
+	dev_dbg(&spi->dev, "txrx: tx %p, rx %p, len %d\n",
+		t->tx_buf, t->rx_buf, t->len);
+
+	hw = spi_master_get_devdata(spi->master);
+
+	hw->tx = t->tx_buf;
+	hw->rx = t->rx_buf;
+	hw->len = t->len;
+	hw->count = 0;
+
+	/* send the first byte */
+	data = hw->tx ? hw->tx[0] : 0;
+	out_8(&hw->regs->txd, data);
+	out_8(&hw->regs->cr, SPI_PPC4XX_CR_STR);
+	wait_for_completion(&hw->done);
+
+	return hw->count;
+}
+
+static int spi_ppc4xx_setupxfer(struct spi_device *spi, struct spi_transfer *t)
+{
+	struct ppc4xx_spi *hw = spi_master_get_devdata(spi->master);
+	struct spi_ppc4xx_cs *cs = spi->controller_state;
+	int scr;
+	u8 cdm = 0;
+	u32 speed;
+	u8 bits_per_word;
+
+ 	/* Start with the generic configuration for this device. */
+	bits_per_word = spi->bits_per_word;
+	speed = spi->max_speed_hz;
+
+	/*
+	 * Modify the configuration if the transfer overrides it.  Do not allow
+	 * the transfer to overwrite the generic configuration with zeros.
+	 */
+	if (t) {
+		if (t->bits_per_word)
+			bits_per_word = t->bits_per_word;
+
+		if (t->speed_hz)
+			speed = min(t->speed_hz, spi->max_speed_hz);
+	}
+
+	if (bits_per_word != 8) {
+		dev_err(&spi->dev, "invalid bits-per-word (%d)\n", bits_per_word);
+		return -EINVAL;
+	}
+
+	if (!speed || (speed > spi->max_speed_hz)) {
+		dev_err(&spi->dev, "invalid speed_hz (%d)\n", speed);
+		return -EINVAL;
+	}
+
+	/* Write new configration */
+	out_8(&hw->regs->mode, cs->mode);
+
+	/* Set the clock */
+	/* opb_freq was already divided by 4 */
+	scr = (hw->opb_freq / speed) - 1;
+	if (scr > 0)
+		cdm = min(scr, 0xff);
+
+	dev_dbg(&spi->dev, "setting pre-scaler to %d (hz %d)\n", cdm, speed);
+
+	if (in_8(&hw->regs->cdm) != cdm)
+		out_8(&hw->regs->cdm, cdm);
+
+	spin_lock(&hw->bitbang.lock);
+	if (!hw->bitbang.busy) {
+		hw->bitbang.chipselect(spi, BITBANG_CS_INACTIVE);
+		/* Need to ndelay here? */
+	}
+	spin_unlock(&hw->bitbang.lock);
+
+	return 0;
+}
+
+static int spi_ppc4xx_setup(struct spi_device *spi)
+{
+	struct spi_ppc4xx_cs *cs = spi->controller_state;
+
+	if (!spi->bits_per_word)
+		spi->bits_per_word = 8;
+
+	if (spi->bits_per_word != 8) {
+		dev_err(&spi->dev, "invalid bits-per-word (%d)\n",
+			spi->bits_per_word);
+		return -EINVAL;
+	}
+
+	if (!spi->max_speed_hz) {
+		dev_err(&spi->dev, "invalid max_speed_hz (must be non-zero)\n");
+		return -EINVAL;
+	}
+
+	if (spi->mode & ~MODEBITS) {
+		dev_dbg(&spi->dev, "setup: unsupported mode bits %x\n",
+			spi->mode & ~MODEBITS);
+		return -EINVAL;
+	}
+
+	if (cs == NULL) {
+		cs = kzalloc(sizeof *cs, GFP_KERNEL);
+		if (!cs)
+			return -ENOMEM;
+		spi->controller_state = cs;
+	}
+
+	/*
+	 * We set all bits of the SPI0_MODE register, so,
+	 * no need to read-modify-write
+	 */
+	cs->mode = SPI_PPC4XX_MODE_SPE;
+
+	switch (spi->mode & (SPI_CPHA | SPI_CPOL)) {
+	case SPI_MODE_0:
+		cs->mode |= SPI_CLK_MODE0;
+		break;
+	case SPI_MODE_1:
+		cs->mode |= SPI_CLK_MODE1;
+		break;
+	case SPI_MODE_2:
+		cs->mode |= SPI_CLK_MODE2;
+		break;
+	case SPI_MODE_3:
+		cs->mode |= SPI_CLK_MODE3;
+		break;
+	}
+
+	if (spi->mode & SPI_LSB_FIRST)
+		cs->mode |= SPI_PPC4XX_MODE_RD;
+
+	dev_dbg(&spi->dev, "%s: mode %d, %u bpw, %d hz\n",
+		__func__, spi->mode, spi->bits_per_word,
+		spi->max_speed_hz);
+
+	return 0;
+}
+
+static void spi_ppc4xx_chipsel(struct spi_device *spi, int value)
+{
+	struct ppc4xx_spi *hw = spi_master_get_devdata(spi->master);
+	unsigned int cs = spi->chip_select;
+	unsigned int cspol;
+
+	/*
+	 * If there are no chip selects at all, or if this is the special case of
+	 * a non-existent (dummy) chip select, do nothing.
+	 */
+
+	if (!hw->master->num_chipselect || hw->gpios[cs] == -EEXIST)
+		return;
+
+	cspol = spi->mode & SPI_CS_HIGH ? 1 : 0;
+	if (value == BITBANG_CS_INACTIVE)
+		cspol = !cspol;
+
+	gpio_set_value(hw->gpios[cs], cspol);
+}
+
+static irqreturn_t spi_ppc4xx_int(int irq, void *dev_id)
+{
+	struct ppc4xx_spi *hw;
+	u8 status;
+	u8 data;
+	unsigned int count;
+
+	hw = (struct ppc4xx_spi *)dev_id;
+
+	status = in_8(&hw->regs->sr);
+	if (!status)
+		return IRQ_NONE;
+
+	/*
+	 * BSY de-asserts one cycle after the transfer is complete.  The interrupt
+	 * is asserted after the transfer is complete.  The exact relationship is
+	 * not documented, hence this code.
+	 */
+
+	if (unlikely(status & SPI_PPC4XX_SR_BSY)) {
+		u8 lstatus;
+		int cnt = 0;
+
+		dev_dbg(hw->dev, "got interrupt but spi still busy?\n");
+		do {
+			ndelay(10);
+			lstatus = in_8(&hw->regs->sr);
+		} while (++cnt < 100 && lstatus & SPI_PPC4XX_SR_BSY);
+
+		if (cnt >= 100) {
+			dev_err(hw->dev, "busywait: too many loops!\n");
+			complete(&hw->done);
+			return IRQ_HANDLED;
+		} else {
+			/* status is always 1 (RBR) here */
+			status = in_8(&hw->regs->sr);
+			dev_dbg(hw->dev, "loops %d status %x\n", cnt, status);
+		}
+	}
+
+	count = hw->count;
+	hw->count++;
+
+	/* RBR triggered this interrupt.  Therefore, data must be ready. */
+	data = in_8(&hw->regs->rxd);
+	if (hw->rx)
+		hw->rx[count] = data;
+
+	count++;
+
+	if (count < hw->len) {
+		data = hw->tx ? hw->tx[count] : 0;
+		out_8(&hw->regs->txd, data);
+		out_8(&hw->regs->cr, SPI_PPC4XX_CR_STR);
+	} else {
+		complete(&hw->done);
+	}
+
+	return IRQ_HANDLED;
+}
+
+static void spi_ppc4xx_cleanup(struct spi_device *spi)
+{
+	kfree(spi->controller_state);
+}
+
+static void spi_ppc4xx_enable(struct ppc4xx_spi *hw)
+{
+	/*
+	 * On all 4xx PPC's the SPI bus is shared/multiplexed with
+	 * the 2nd I2C bus. We need to enable the the SPI bus before
+	 * using it.
+	 */
+
+	/* need to clear bit 14 to enable SPC */
+	dcri_clrset(SDR0, SDR0_PFC1, 0x80000000 >> 14, 0);
+}
+
+static void free_gpios(struct ppc4xx_spi *hw)
+{
+	if (hw->master->num_chipselect) {
+		int i;
+		for (i = 0; i < hw->master->num_chipselect; i++)
+			if (gpio_is_valid(hw->gpios[i]))
+				gpio_free(hw->gpios[i]);
+
+		kfree(hw->gpios);
+		hw->gpios = NULL;
+	}
+}
+
+/*
+ * of_device layer stuff...
+ */
+static int __init spi_ppc4xx_of_probe(struct of_device *op,
+				      const struct of_device_id *match)
+{
+	struct ppc4xx_spi *hw;
+	struct spi_master *master;
+	struct spi_bitbang *bbp;
+	struct resource resource;
+	struct device_node *np = op->node;
+	struct device *dev = &op->dev;
+	struct device_node *opbnp;
+	int ret;
+	int num_gpios;
+	const unsigned int *clk;
+
+	master = spi_alloc_master(dev, sizeof *hw);
+	if (master == NULL)
+		return -ENOMEM;
+	dev_set_drvdata(dev, master);
+	hw = spi_master_get_devdata(master);
+	hw->master = spi_master_get(master);
+	hw->dev = dev;
+
+	init_completion(&hw->done);
+
+	/*
+	 * A count of zero implies a single SPI device without any chip-select.
+	 * Note that of_gpio_count counts all gpios assigned to this spi master.
+	 * This includes both "null" gpio's and real ones.
+	 */
+	num_gpios = of_gpio_count(np);
+	if (num_gpios) {
+		int i;
+
+		hw->gpios = kzalloc(sizeof(int) * num_gpios, GFP_KERNEL);
+		if (!hw->gpios) {
+			ret = -ENOMEM;
+			goto free_master;
+		}
+
+		for (i = 0; i < num_gpios; i++) {
+			int gpio;
+			enum of_gpio_flags flags;
+		       
+			gpio = of_get_gpio_flags(np, i, &flags);
+			hw->gpios[i] = gpio;
+
+			if (gpio_is_valid(gpio)) {
+				/* Real CS - set the initial state. */
+				ret = gpio_request(gpio, np->name);
+				if (ret < 0) {
+					dev_err(dev, "can't request gpio #%d: %d\n",
+							i, ret);
+					goto free_gpios;
+				}
+
+				gpio_direction_output(gpio,
+						!!(flags & OF_GPIO_ACTIVE_LOW));
+			} else if (gpio == -EEXIST) {
+				; /* No CS, but that's OK. */
+			} else {
+				dev_err(dev, "invalid gpio #%d: %d\n", i, gpio);
+				ret = -EINVAL;
+				goto free_gpios;
+			}
+		}
+	}
+
+	/* Setup the state for the bitbang driver */
+	bbp = &hw->bitbang;
+	bbp->master = hw->master;
+	bbp->setup_transfer = spi_ppc4xx_setupxfer;
+	bbp->chipselect = spi_ppc4xx_chipsel;
+	bbp->txrx_bufs = spi_ppc4xx_txrx;
+	bbp->use_dma = 0;
+	bbp->master->setup = spi_ppc4xx_setup;
+	bbp->master->cleanup = spi_ppc4xx_cleanup;
+
+	/* Allocate bus num dynamically. */
+	bbp->master->bus_num = -1;
+
+	/* this many pins in all GPIO controllers */
+	bbp->master->num_chipselect = num_gpios;
+
+	/* Get the clock for the OPB */
+	opbnp = of_find_compatible_node(NULL, NULL, "ibm,opb");
+	if (opbnp == NULL) {
+		dev_err(dev, "OPB: cannot find node\n");
+		ret = -ENODEV;
+		goto free_gpios;
+	}
+	/* Get the clock (Hz) for the OPB */
+	clk = of_get_property(opbnp, "clock-frequency", NULL);
+	if (clk == NULL) {
+		dev_err(dev, "OPB: no clock-frequency property set\n");
+		of_node_put(opbnp);
+		ret = -ENODEV;
+		goto free_gpios;
+	}
+	hw->opb_freq = *clk;
+	hw->opb_freq >>= 2;
+	of_node_put(opbnp);
+
+	ret = of_address_to_resource(np, 0, &resource);
+	if (ret) {
+		dev_err(dev, "error while parsing device node resource\n");
+		goto free_gpios;
+	}
+	hw->mapbase = resource.start;
+	hw->mapsize = resource.end - resource.start + 1;
+
+	/* Sanity check */
+	if (hw->mapsize < sizeof(struct spi_ppc4xx_regs)) {
+		dev_err(dev, "too small to map registers\n");
+		ret = -EINVAL;
+		goto free_gpios;
+	}
+
+	/* Request IRQ */
+	hw->irqnum = irq_of_parse_and_map(np, 0);
+	ret = request_irq(hw->irqnum, spi_ppc4xx_int,
+			  IRQF_DISABLED, "spi_ppc4xx_of", (void *)hw);
+	if (ret) {
+		dev_err(dev, "unable to allocate interrupt\n");
+		goto free_gpios;
+	}
+
+	if (!request_mem_region(hw->mapbase, hw->mapsize, DRIVER_NAME)) {
+		dev_err(dev, "resource unavailable\n");
+		ret = -EBUSY;
+		goto request_mem_error;
+	}
+
+	hw->regs = ioremap(hw->mapbase, sizeof(struct spi_ppc4xx_regs));
+
+	if (!hw->regs) {
+		dev_err(dev, "unable to memory map registers\n");
+		ret = -ENXIO;
+		goto map_io_error;
+	}
+
+	spi_ppc4xx_enable(hw);
+
+	/* Finally register our spi controller */
+	dev->dma_mask = 0;
+	ret = spi_bitbang_start(bbp);
+	if (ret) {
+		dev_err(dev, "failed to register SPI master\n");
+		goto unmap_regs;
+	}
+
+	dev_info(dev, "driver initialized\n");
+	of_register_spi_devices(master, np);
+
+	return 0;
+
+unmap_regs:
+	iounmap(hw->regs);
+map_io_error:
+	release_mem_region(hw->mapbase, hw->mapsize);
+request_mem_error:
+	free_irq(hw->irqnum, hw);
+free_gpios:
+	free_gpios(hw);
+free_master:
+	dev_set_drvdata(dev, NULL);
+	spi_master_put(master);
+
+	dev_err(dev, "initialization failed\n");
+	return ret;
+}
+
+static int __exit spi_ppc4xx_of_remove(struct of_device *op)
+{
+	struct spi_master *master = dev_get_drvdata(&op->dev);
+	struct ppc4xx_spi *hw = spi_master_get_devdata(master);
+
+	spi_bitbang_stop(&hw->bitbang);
+	dev_set_drvdata(&op->dev, NULL);
+	release_mem_region(hw->mapbase, hw->mapsize);
+	free_irq(hw->irqnum, hw);
+	iounmap(hw->regs);
+	free_gpios(hw);
+	return 0;
+}
+
+static struct of_device_id spi_ppc4xx_of_match[] = {
+	{ .compatible = "ibm,ppc4xx-spi", },
+	{},
+};
+
+MODULE_DEVICE_TABLE(of, spi_ppc4xx_of_match);
+
+static struct of_platform_driver spi_ppc4xx_of_driver = {
+	.match_table = spi_ppc4xx_of_match,
+	.probe = spi_ppc4xx_of_probe,
+	.remove = __exit_p(spi_ppc4xx_of_remove),
+	.driver = {
+		.name = DRIVER_NAME,
+		.owner = THIS_MODULE,
+	},
+};
+
+static int __init spi_ppc4xx_init(void)
+{
+	return of_register_platform_driver(&spi_ppc4xx_of_driver);
+}
+module_init(spi_ppc4xx_init);
+
+static void __exit spi_ppc4xx_exit(void)
+{
+	of_unregister_platform_driver(&spi_ppc4xx_of_driver);
+}
+module_exit(spi_ppc4xx_exit);
+
+MODULE_AUTHOR("Gary Jennejohn & Stefan Roese");
+MODULE_DESCRIPTION("Simple PPC4xx SPI Driver");
+MODULE_LICENSE("GPL");

^ permalink raw reply related

* Re: 85xx Address space query
From: Scott Wood @ 2009-06-25 19:34 UTC (permalink / raw)
  To: kernel mailz; +Cc: linuxppc-dev
In-Reply-To: <abe8a1fd0906250351y1aadc153m9f52f65a5860a2ca@mail.gmail.com>

kernel mailz wrote:
> So this means
> when kernel gets interrupted by app which may be in PID=5 (say)
> kernel translations for PID=0 remain valid ?
> I am not able to follow Scott

Yes, exactly.  They're valid even in userspace, except to the extent that the 
kernel marks them supervisor-only.

-Scott

^ permalink raw reply

* Re: Direct MII connection between MPC8313 and VIRTEX FPGA
From: Grant Likely @ 2009-06-25 20:05 UTC (permalink / raw)
  To: Frank Prepelica; +Cc: linuxppc-dev
In-Reply-To: <29DC34A6B43468409F5A371CFE34E849D7ED46@ex01.ads.ubidyne.de>

On Thu, Jun 25, 2009 at 9:11 AM, Frank
Prepelica<Frank.Prepelica@ubidyne.com> wrote:
> Hi all,
>
> we removed the ethernet PHYs (MARVELL) from our customized board (based on MPC8313ERDB) and
> connected the CPU Ethernet lines directly to a FPGA which is now emulating the PHY. But the ifconfig
> command cannot find that eth device anymore. Does the emulated PHY needs to provide MDIO
> information that the ETH device can be found again?

No, you just need to make sure that the Ethernet driver is able to
support fixed speed MII links without a PHY.  It's trivial to do, but
not all drivers support it.  Search for "current-speed" in
drivers/net/fec_mpc52xx.c for an example.

g.

-- 
Grant Likely, B.Sc., P.Eng.
Secret Lab Technologies Ltd.

^ permalink raw reply

* Re: Subject: [PATCH v7] spi: Add PPC4xx SPI driver
From: David Brownell @ 2009-06-26  4:04 UTC (permalink / raw)
  To: Steven A. Falco; +Cc: linuxppc-dev@ozlabs.org, Stefan Roese
In-Reply-To: <4A43CC2C.1030409@harris.com>

On Thursday 25 June 2009, Steven A. Falco wrote:
> +       if (spi->mode & ~MODEBITS) {
> +               dev_dbg(&spi->dev, "setup: unsupported mode bits %x\n",
> +                       spi->mode & ~MODEBITS);
> +               return -EINVAL;
> +       }

This wasn't tested against 2.6.30-rc1 was it?

See the recent fixup patch I sent.  There's a spi_master->modebits
mask that should have been initialized, and which eliminates the
need for tests like that ...

> +       dev_dbg(&spi->dev, "%s: mode %d, %u bpw, %d hz\n",
> +               __func__, spi->mode, spi->bits_per_word,
> +               spi->max_speed_hz);
> +

... also the SPI core now provides a *standard* format debug
message for that stuff.  It also handles one more thing, which
I expect to see fixed in a v8 of this patch ... :)

- Dave

^ permalink raw reply

* [git pull] Please pull powerpc.git merge branch
From: Benjamin Herrenschmidt @ 2009-06-26  7:19 UTC (permalink / raw)
  To: Linus Torvalds; +Cc: linuxppc-dev list, Andrew Morton, Linux Kernel list

Hi Linus !

I would have hoped to get that stuff in before -rc1 but heh, I got sick
for a couple of days, a distracted by some other stuff for a couple more
and here we are, I missed it :-)

Anyway, here are a bunch of relatively small ppc bits, almost all are
bug fixes, mostly regressions, with the notable exception of one patch
which I decided was worth taking a chance, which is the addition of
lockdep/irqtrace support for ppc32 which has been around for 2 or 3
weeks and seems to be working well (it already found a couple of
bugs !).


The following changes since commit 987fed3bf6982f2627d4fa242caa9026ef61132a:
  Linus Torvalds (1):
        Merge branch 'drm-fixes' of git://git.kernel.org/.../airlied/drm-2.6

are available in the git repository at:

  git://git.kernel.org/pub/scm/linux/kernel/git/benh/powerpc.git merge

Adrian Reber (1):
      powerpc/rtas: Fix watchdog driver temperature read functionality

Anton Vorontsov (1):
      powerpc/85xx: Make eSDHC 1-bit only transfer mode default for MPC8569E-MDS

Benjamin Herrenschmidt (13):
      powerpc/mpic: Fix mapping of "DCR" based MPIC variants
      powerpc/pmac: Fix issues with PowerMac "PowerSurge" SMP
      powerpc/mm: Make k(un)map_atomic out of line
      powerpc/pmac: Fix DMA ops for MacIO devices
      powerpc: Map more memory early on 601 processors
      powerpc: Add irqtrace support for 32-bit powerpc
      powerpc/rtas: Turn rtas lock into a raw spinlock
      powerpc: Use one common impl. of RTAS timebase sync and use raw spinlock
      powerpc/pasemi: Use raw spinlock in SMP TB sync
      powerpc/of: Fix usage of dev_set_name() in of_device_alloc()
      powerpc/440: Fix warning early debug code
      powerpc/mm: Fix potential access to freed pages when using hugetlbfs
      Merge commit 'kumar/next' into merge

Gerhard Pircher (1):
      powerpc/amigaone: Limit ISA I/O range to 4k in the device tree

Huang Weiyi (1):
      powerpc/85xx: remove duplicated #include

Jon Smirl (1):
      powerpc: Have git ignore generated files from dtc compile

Kumar Gala (6):
      powerpc/cpm1: Remove IMAP_ADDR
      powerpc/85xx: Stop using ppc_md.init on socrates
      powerpc/85xx: Fix issue found by lockdep trace in smp_85xx_kick_cpu
      powerpc: Refactor device tree binding
      powerpc: Fix output from show_regs
      powerpc: Fix mpic alloc warning

Michael Ellerman (1):
      powerpc: Swiotlb breaks pseries

Randy Vinson (1):
      powerpc/85xx: Fix FSL RapidIO probing on MDS boards

Sean MacLennan (1):
      powerpc/warp: Platform fix for i2c change

Sonny Rao (2):
      powerpc/BSR: add 4096 byte BSR size
      powerpc/BSR: Fix BSR to allow mmap of small BSR on 64k kernel

Timur Tabi (1):
      powerpc/qe: add polling timeout to qe_issue_cmd()

 Documentation/powerpc/booting-without-of.txt     | 1168 +---------------------
 Documentation/powerpc/dts-bindings/4xx/emac.txt  |  148 +++
 Documentation/powerpc/dts-bindings/gpio/gpio.txt |   50 +
 Documentation/powerpc/dts-bindings/gpio/mdio.txt |   19 +
 Documentation/powerpc/dts-bindings/marvell.txt   |  521 ++++++++++
 Documentation/powerpc/dts-bindings/phy.txt       |   25 +
 Documentation/powerpc/dts-bindings/spi-bus.txt   |   57 ++
 Documentation/powerpc/dts-bindings/usb-ehci.txt  |   25 +
 Documentation/powerpc/dts-bindings/xilinx.txt    |  295 ++++++
 arch/powerpc/Kconfig                             |    1 -
 arch/powerpc/boot/.gitignore                     |   10 +
 arch/powerpc/boot/dts/amigaone.dts               |    4 +-
 arch/powerpc/boot/dts/mpc8569mds.dts             |    1 +
 arch/powerpc/include/asm/cpm1.h                  |    2 -
 arch/powerpc/include/asm/dma-mapping.h           |   24 +-
 arch/powerpc/include/asm/highmem.h               |   57 +-
 arch/powerpc/include/asm/hw_irq.h                |   20 +-
 arch/powerpc/include/asm/pte-hash64-64k.h        |    3 +-
 arch/powerpc/include/asm/rtas.h                  |    5 +-
 arch/powerpc/kernel/entry_32.S                   |  127 +++-
 arch/powerpc/kernel/head_32.S                    |   17 +-
 arch/powerpc/kernel/of_device.c                  |    2 +-
 arch/powerpc/kernel/process.c                    |    2 +-
 arch/powerpc/kernel/rtas.c                       |   69 ++-
 arch/powerpc/kernel/setup_32.c                   |    2 +
 arch/powerpc/kernel/smp.c                        |    3 +-
 arch/powerpc/kernel/udbg_16550.c                 |    2 +-
 arch/powerpc/mm/Makefile                         |    1 +
 arch/powerpc/mm/highmem.c                        |   77 ++
 arch/powerpc/platforms/44x/warp.c                |   44 +-
 arch/powerpc/platforms/85xx/mpc85xx_mds.c        |    1 +
 arch/powerpc/platforms/85xx/smp.c                |    9 +-
 arch/powerpc/platforms/85xx/socrates.c           |    6 +-
 arch/powerpc/platforms/85xx/xes_mpc85xx.c        |    1 -
 arch/powerpc/platforms/cell/smp.c                |   30 +-
 arch/powerpc/platforms/chrp/smp.c                |   33 +-
 arch/powerpc/platforms/pasemi/setup.c            |   15 +-
 arch/powerpc/platforms/powermac/setup.c          |   41 +-
 arch/powerpc/platforms/powermac/smp.c            |  166 ++--
 arch/powerpc/platforms/pseries/smp.c             |   30 +-
 arch/powerpc/sysdev/mpic.c                       |   34 +-
 arch/powerpc/sysdev/qe_lib/qe.c                  |    9 +-
 drivers/char/bsr.c                               |   42 +-
 drivers/macintosh/macio_asic.c                   |   11 +
 drivers/watchdog/wdrtas.c                        |    8 +-
 scripts/dtc/.gitignore                           |    5 +
 46 files changed, 1691 insertions(+), 1531 deletions(-)
 create mode 100644 Documentation/powerpc/dts-bindings/4xx/emac.txt
 create mode 100644 Documentation/powerpc/dts-bindings/gpio/gpio.txt
 create mode 100644 Documentation/powerpc/dts-bindings/gpio/mdio.txt
 create mode 100644 Documentation/powerpc/dts-bindings/marvell.txt
 create mode 100644 Documentation/powerpc/dts-bindings/phy.txt
 create mode 100644 Documentation/powerpc/dts-bindings/spi-bus.txt
 create mode 100644 Documentation/powerpc/dts-bindings/usb-ehci.txt
 create mode 100644 Documentation/powerpc/dts-bindings/xilinx.txt
 create mode 100644 arch/powerpc/mm/highmem.c
 create mode 100644 scripts/dtc/.gitignore

^ permalink raw reply

* Re: ALSA fixes for non-coherent ppc32 again
From: Gerhard Pircher @ 2009-06-26 13:14 UTC (permalink / raw)
  To: Takashi Iwai; +Cc: linuxppc-dev
In-Reply-To: <s5hr5xa9d5q.wl%tiwai@suse.de>


-------- Original-Nachricht --------
> Datum: Wed, 24 Jun 2009 11:47:13 +0200
> Von: Takashi Iwai <tiwai@suse.de>
> An: "Gerhard Pircher" <gerhard_pircher@gmx.net>
> CC: benh@kernel.crashing.org, linuxppc-dev@ozlabs.org
> Betreff: Re: ALSA fixes for non-coherent ppc32 again

> At Wed, 24 Jun 2009 10:46:01 +0200,
> Gerhard Pircher wrote:
> > 
> > 
> > -------- Original-Nachricht --------
> > > Datum: Tue, 23 Jun 2009 23:42:24 +0200
> > > Von: "Gerhard Pircher" <gerhard_pircher@gmx.net>
> > > An: benh@kernel.crashing.org, tiwai@suse.de
> > > CC: linuxppc-dev@ozlabs.org
> > > Betreff: Re: ALSA fixes for non-coherent ppc32 again
> > 
> > > Okay, that's wrong. I somehow messed up the .config file. It doesn't
> > > compile, too.
> > I got it to compile now and it seems to work fine. I overlooked a typo
> > in sound/core/Makefile first (ifndef CONFIG_SND_NONCOHERNT_DMA.)
> 
> Gah, thanks, fixed now on the git tree too.

The Kconfig option still needs to be fixed, otherwise SND_COHERENT_DMA
isn't selected for my "PPC32 && NOT_COHERENT_CACHE" machine.

config SND_NONCOHERENT_DMA
        def_bool n  <-- should be "y"
        depends on (PPC32 && NOT_COHERENT_CACHE)...

BTW: Can you put me on CC: please, if you bring up this topic on
linux-arch or so?

Gerhard

-- 
GRATIS für alle GMX-Mitglieder: Die maxdome Movie-FLAT!
Jetzt freischalten unter http://portal.gmx.net/de/go/maxdome01

^ permalink raw reply

* [PATCH] Remove 'SBC8240 Wind River' Device Driver Code
From: Subrata Modak @ 2009-06-26 14:25 UTC (permalink / raw)
  To: Scott Wood, David Woodhouse
  Cc: carolyn.j.smith, Stephen Rothwell, Adrian Bunk, Jim Cromie,
	linux-kernel, Linuxppc-dev, Sachin P Sant, linux-next,
	Subrata Modak, Alexander Beregalov, Balbir Singh

[-- Warning: decoded text below may be mangled, UTF-8 assumed --]
[-- Attachment #1: Type: text/plain, Size: 10601 bytes --]

Hi David/Scott,

Today's next tree(20090626) produced the following build error:

CC [M]  drivers/mtd/maps/sbc8240.o
drivers/mtd/maps/sbc8240.c:31:1: warning: "DEBUG" redefined
In file included from drivers/mtd/maps/sbc8240.c:23:
include/linux/mtd/mtd.h:333:1: warning: this is the location of the previous definition
drivers/mtd/maps/sbc8240.c: In function 'init_sbc8240_mtd':
drivers/mtd/maps/sbc8240.c:172: warning: passing argument 1 of 'simple_map_init' from incompatible pointer type
drivers/mtd/maps/sbc8240.c:177: error: 'struct mtd_info' has no member named 'module'
make[3]: *** [drivers/mtd/maps/sbc8240.o] Error 1
make[2]: *** [drivers/mtd/maps] Error 2
make[1]: *** [drivers/mtd] Error 2
make: *** [drivers] Error 2

I remember reporting this log back in April, when you suggested in removing it:
http://lkml.org/lkml/2009/4/21/476,

>On Tue, 2009-04-21 at 15:00 -0500, Scott Wood wrote:
>Subrata Modak wrote:
> > This is a very old one. Doesn´t seem to go away. Reported this earlier
> > on 14th April:
> > http://lkml.org/lkml/2009/4/14/483,
> > 
> > CC [M]  drivers/mtd/maps/sbc8240.o
> > drivers/mtd/maps/sbc8240.c:31:1: warning: "DEBUG" redefined
> > In file included from drivers/mtd/maps/sbc8240.c:23:
> > include/linux/mtd/mtd.h:339:1: warning: this is the location of the
> > previous definition
> > drivers/mtd/maps/sbc8240.c: In function ‘init_sbc8240_mtd’:
> > drivers/mtd/maps/sbc8240.c:172: error: ‘sbc8240_mtd’ undeclared (first
> > use in this function)
> > drivers/mtd/maps/sbc8240.c:172: error: (Each undeclared identifier is
> > reported only once
> > drivers/mtd/maps/sbc8240.c:172: error: for each function it appears in.)
> > drivers/mtd/maps/sbc8240.c: In function ‘cleanup_sbc8240_mtd’:
> > drivers/mtd/maps/sbc8240.c:233: error: ‘sbc8240_mtd’ undeclared (first
> > use in this function)
> 
> This looks like an arch/ppc orphan.  It's not enabled by any defconfig, 
> and it doesn't look like it does anything that physmap_of can't do.
> 
> I'd just remove it.
> 
> -Scott

I tried to gather some more info about this driver from the link
mentioned in Kconfig:
http://www.windriver.com/products/sbc8240/,
without much success.

If there are no issues, can you please apply this patch to remove it ?

To: David Woodhouse <dwmw2@infradead.org>,
To: Scott Wood <scottwood@freescale.com>,
Cc: Jim Cromie <jim.cromie@gmail.com>,
Cc: carolyn.j.smith@exgate.tek.com,
Cc: Adrian Bunk <bunk@kernel.org>,
Cc: Sachin P Sant <sachinp@linux.vnet.ibm.com>,
Cc: Balbir Singh <balbir@linux.vnet.ibm.com>,
Cc: Stephen Rothwell <sfr@canb.auug.org.au>,
Cc: linux-kernel <linux-kernel@vger.kernel.org>,
Cc: Linuxppc-dev <Linuxppc-dev@ozlabs.org>,
Cc: linux-next <linux-next@vger.kernel.org>,
Cc: Alexander Beregalov <a.beregalov@gmail.com>


Signed-off-by: Subrata Modak <subrata@linux.vnet.ibm.com>
Tested-on-PPC64-by: Subrata Modak <subrata@linux.vnet.ibm.com>
---

diff -uprN a/drivers/mtd/maps/Kconfig b/drivers/mtd/maps/Kconfig
--- a/drivers/mtd/maps/Kconfig	2009-06-26 07:36:23.000000000 -0500
+++ b/drivers/mtd/maps/Kconfig	2009-06-26 07:39:34.000000000 -0500
@@ -284,13 +284,6 @@ config MTD_L440GX
 
 	  BE VERY CAREFUL.
 
-config MTD_SBC8240
-	tristate "Flash device on SBC8240"
-	depends on MTD_JEDECPROBE && 8260
-	help
-          Flash access on the SBC8240 board from Wind River.  See
-          <http://www.windriver.com/products/sbc8240/>
-
 config MTD_TQM8XXL
 	tristate "CFI Flash device mapped on TQM8XXL"
 	depends on MTD_CFI && TQM8xxL
diff -uprN a/drivers/mtd/maps/Makefile b/drivers/mtd/maps/Makefile
--- a/drivers/mtd/maps/Makefile	2009-06-26 07:36:23.000000000 -0500
+++ b/drivers/mtd/maps/Makefile	2009-06-26 07:40:03.000000000 -0500
@@ -50,7 +50,6 @@ obj-$(CONFIG_MTD_UCLINUX)	+= uclinux.o
 obj-$(CONFIG_MTD_NETtel)	+= nettel.o
 obj-$(CONFIG_MTD_SCB2_FLASH)	+= scb2_flash.o
 obj-$(CONFIG_MTD_H720X)		+= h720x-flash.o
-obj-$(CONFIG_MTD_SBC8240)	+= sbc8240.o
 obj-$(CONFIG_MTD_IXP4XX)	+= ixp4xx.o
 obj-$(CONFIG_MTD_IXP2000)	+= ixp2000.o
 obj-$(CONFIG_MTD_WRSBC8260)	+= wr_sbc82xx_flash.o
diff -uprN a/drivers/mtd/maps/sbc8240.c b/drivers/mtd/maps/sbc8240.c
--- a/drivers/mtd/maps/sbc8240.c	2009-06-26 07:36:23.000000000 -0500
+++ b/drivers/mtd/maps/sbc8240.c	1969-12-31 18:00:00.000000000 -0600
@@ -1,250 +0,0 @@
-/*
- * Handle mapping of the flash memory access routines on the SBC8240 board.
- *
- * Carolyn Smith, Tektronix, Inc.
- *
- * This code is GPLed
- */
-
-/*
- * The SBC8240 has 2 flash banks.
- * Bank 0 is a 512 KiB AMD AM29F040B; 8 x 64 KiB sectors.
- * It contains the U-Boot code (7 sectors) and the environment (1 sector).
- * Bank 1 is 4 x 1 MiB AMD AM29LV800BT; 15 x 64 KiB sectors, 1 x 32 KiB sector,
- * 2 x 8 KiB sectors, 1 x 16 KiB sectors.
- * Both parts are JEDEC compatible.
- */
-
-#include <linux/module.h>
-#include <linux/types.h>
-#include <linux/kernel.h>
-#include <asm/io.h>
-
-#include <linux/mtd/mtd.h>
-#include <linux/mtd/map.h>
-#include <linux/mtd/cfi.h>
-
-#ifdef CONFIG_MTD_PARTITIONS
-#include <linux/mtd/partitions.h>
-#endif
-
-#define	DEBUG
-
-#ifdef	DEBUG
-# define debugk(fmt,args...)	printk(fmt ,##args)
-#else
-# define debugk(fmt,args...)
-#endif
-
-
-#define WINDOW_ADDR0	0xFFF00000		/* 512 KiB */
-#define WINDOW_SIZE0	0x00080000
-#define BUSWIDTH0	1
-
-#define WINDOW_ADDR1	0xFF000000		/* 4 MiB */
-#define WINDOW_SIZE1	0x00400000
-#define BUSWIDTH1	8
-
-#define MSG_PREFIX "sbc8240:"	/* prefix for our printk()'s */
-#define MTDID	   "sbc8240-%d"	/* for mtdparts= partitioning */
-
-
-static struct map_info sbc8240_map[2] = {
-	{
-		.name           = "sbc8240 Flash Bank #0",
-		.size           = WINDOW_SIZE0,
-		.bankwidth       = BUSWIDTH0,
-	},
-	{
-		.name           = "sbc8240 Flash Bank #1",
-		.size           = WINDOW_SIZE1,
-		.bankwidth       = BUSWIDTH1,
-	}
-};
-
-#define NUM_FLASH_BANKS	ARRAY_SIZE(sbc8240_map)
-
-/*
- * The following defines the partition layout of SBC8240 boards.
- *
- * See include/linux/mtd/partitions.h for definition of the
- * mtd_partition structure.
- *
- * The *_max_flash_size is the maximum possible mapped flash size
- * which is not necessarily the actual flash size. It must correspond
- * to the value specified in the mapping definition defined by the
- * "struct map_desc *_io_desc" for the corresponding machine.
- */
-
-#ifdef CONFIG_MTD_PARTITIONS
-
-static struct mtd_partition sbc8240_uboot_partitions [] = {
-	/* Bank 0 */
-	{
-		.name =	"U-boot",			/* U-Boot Firmware	*/
-		.offset =	0,
-		.size =	0x00070000,			/*  7 x 64 KiB sectors 	*/
-		.mask_flags = MTD_WRITEABLE,		/*  force read-only	*/
-	},
-	{
-		.name =	"environment",			/* U-Boot environment	*/
-		.offset =	0x00070000,
-		.size =	0x00010000,			/*  1 x 64 KiB sector	*/
-	},
-};
-
-static struct mtd_partition sbc8240_fs_partitions [] = {
-	{
-		.name =	"jffs",				/* JFFS  filesystem	*/
-		.offset =	0,
-		.size =	0x003C0000,			/*  4 * 15 * 64KiB	*/
-	},
-	{
-		.name =	"tmp32",
-		.offset =	0x003C0000,
-		.size =	0x00020000,			/*  4 * 32KiB		*/
-	},
-	{
-		.name =	"tmp8a",
-		.offset =	0x003E0000,
-		.size =	0x00008000,			/*  4 * 8KiB		*/
-	},
-	{
-		.name =	"tmp8b",
-		.offset =	0x003E8000,
-		.size =	0x00008000,			/*  4 * 8KiB		*/
-	},
-	{
-		.name =	"tmp16",
-		.offset =	0x003F0000,
-		.size =	0x00010000,			/*  4 * 16KiB		*/
-	}
-};
-
-/* trivial struct to describe partition information */
-struct mtd_part_def
-{
-	int nums;
-	unsigned char *type;
-	struct mtd_partition* mtd_part;
-};
-
-static struct mtd_info *sbc8240_mtd[NUM_FLASH_BANKS];
-static struct mtd_part_def sbc8240_part_banks[NUM_FLASH_BANKS];
-
-
-#endif	/* CONFIG_MTD_PARTITIONS */
-
-
-static int __init init_sbc8240_mtd (void)
-{
-	static struct _cjs {
-		u_long addr;
-		u_long size;
-	} pt[NUM_FLASH_BANKS] = {
-		{
-			.addr = WINDOW_ADDR0,
-			.size = WINDOW_SIZE0
-		},
-		{
-			.addr = WINDOW_ADDR1,
-			.size = WINDOW_SIZE1
-		},
-	};
-
-	int devicesfound = 0;
-	int i,j;
-
-	for (i = 0; i < NUM_FLASH_BANKS; i++) {
-		printk (KERN_NOTICE MSG_PREFIX
-			"Probing 0x%08lx at 0x%08lx\n", pt[i].size, pt[i].addr);
-
-		sbc8240_map[i].map_priv_1 =
-			(unsigned long) ioremap (pt[i].addr, pt[i].size);
-		if (!sbc8240_map[i].map_priv_1) {
-			printk (MSG_PREFIX "failed to ioremap\n");
-			for (j = 0; j < i; j++) {
-				iounmap((void *) sbc8240_map[j].map_priv_1);
-				sbc8240_map[j].map_priv_1 = 0;
-			}
-			return -EIO;
-		}
-		simple_map_init(&sbc8240_mtd[i]);
-
-		sbc8240_mtd[i] = do_map_probe("jedec_probe", &sbc8240_map[i]);
-
-		if (sbc8240_mtd[i]) {
-			sbc8240_mtd[i]->module = THIS_MODULE;
-			devicesfound++;
-		} else {
-			if (sbc8240_map[i].map_priv_1) {
-				iounmap((void *) sbc8240_map[i].map_priv_1);
-				sbc8240_map[i].map_priv_1 = 0;
-			}
-		}
-	}
-
-	if (!devicesfound) {
-		printk(KERN_NOTICE MSG_PREFIX
-		       "No suppported flash chips found!\n");
-		return -ENXIO;
-	}
-
-#ifdef CONFIG_MTD_PARTITIONS
-	sbc8240_part_banks[0].mtd_part   = sbc8240_uboot_partitions;
-	sbc8240_part_banks[0].type       = "static image";
-	sbc8240_part_banks[0].nums       = ARRAY_SIZE(sbc8240_uboot_partitions);
-	sbc8240_part_banks[1].mtd_part   = sbc8240_fs_partitions;
-	sbc8240_part_banks[1].type       = "static file system";
-	sbc8240_part_banks[1].nums       = ARRAY_SIZE(sbc8240_fs_partitions);
-
-	for (i = 0; i < NUM_FLASH_BANKS; i++) {
-
-		if (!sbc8240_mtd[i]) continue;
-		if (sbc8240_part_banks[i].nums == 0) {
-			printk (KERN_NOTICE MSG_PREFIX
-				"No partition info available, registering whole device\n");
-			add_mtd_device(sbc8240_mtd[i]);
-		} else {
-			printk (KERN_NOTICE MSG_PREFIX
-				"Using %s partition definition\n", sbc8240_part_banks[i].mtd_part->name);
-			add_mtd_partitions (sbc8240_mtd[i],
-					    sbc8240_part_banks[i].mtd_part,
-					    sbc8240_part_banks[i].nums);
-		}
-	}
-#else
-	printk(KERN_NOTICE MSG_PREFIX
-	       "Registering %d flash banks at once\n", devicesfound);
-
-	for (i = 0; i < devicesfound; i++) {
-		add_mtd_device(sbc8240_mtd[i]);
-	}
-#endif	/* CONFIG_MTD_PARTITIONS */
-
-	return devicesfound == 0 ? -ENXIO : 0;
-}
-
-static void __exit cleanup_sbc8240_mtd (void)
-{
-	int i;
-
-	for (i = 0; i < NUM_FLASH_BANKS; i++) {
-		if (sbc8240_mtd[i]) {
-			del_mtd_device (sbc8240_mtd[i]);
-			map_destroy (sbc8240_mtd[i]);
-		}
-		if (sbc8240_map[i].map_priv_1) {
-			iounmap ((void *) sbc8240_map[i].map_priv_1);
-			sbc8240_map[i].map_priv_1 = 0;
-		}
-	}
-}
-
-module_init (init_sbc8240_mtd);
-module_exit (cleanup_sbc8240_mtd);
-
-MODULE_LICENSE ("GPL");
-MODULE_AUTHOR ("Carolyn Smith <carolyn.smith@tektronix.com>");
-MODULE_DESCRIPTION ("MTD map driver for SBC8240 boards");
-

---
Regards--
Subrata

^ permalink raw reply


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