LinuxPPC-Dev Archive on lore.kernel.org
 help / color / mirror / Atom feed
* Re: MPC8245 reset register
From: Reeve Yang @ 2006-09-06  0:50 UTC (permalink / raw)
  To: linuxppc-embedded
In-Reply-To: <53107f6e0609051706i67eac762y114ad03bf2065548@mail.gmail.com>

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

LoL, my bad not understanding the humor.

This becomes an interesting topic. I looked at different CPUs, it seems
everyone uses different method to reset itself, and there is no uniformed or
easy for 603e. I searched on google but didn't see anyone have similiar
concern. Actually lots of boxes/systems using MPC8245, why nobody cares
about it? :)

To use watchdog timeout, or gpio port to assert reset line on CPU are not
flexible enough. If using watchdog, I have to enable watchdog and reduce the
timeout length (if it's too long). If using some GPIO device, I'll have to
rely on i2c bus or whatever io interface to write data. Acutally our system
has RESET by a GPIO(PCA9556) port.

Interesting enough, I resolved the problem by writing a data to an invalid
address with hoping for a machine check exception (in fact this is what
u-boot does). Would it be good to make it as a stardard "restart" function
in mpc10x_common.c? If it's acceptable I could send out my patch.

- Reeve

On 9/6/06, Jon Scully <jonscully@gmail.com> wrote:
>
> On 9/5/06, Reeve Yang <yang.reeve@gmail.com> wrote:
> >  I'm kind of curious what's the proper way to reset the
> > 8245 CPU? For anyone who doesn't know MPC8245, which is 603e core.
>
> You could starve the watchdog (assuming SWE=1 in SYPCR).  If you own
> the hardware design, you could add an addressable WO latch (FPGA) that
> asserts reset for the right number of clock cycles (what I would
> normally provide or ask for in a design -- but *only* during
> development).  Otherwise... If this is for development purposes,
> consider using JTAG (Boundary Scan) to control /SRESET.
>
> (My reference to RST was supposed to be humorous -- as in, remember
> the good old days when you could do that in S/W?! ('RST 7' in Z80 &
> 8085)  Sorry for my bad humor.)
> _______________________________________________
> Linuxppc-embedded mailing list
> Linuxppc-embedded@ozlabs.org
> https://ozlabs.org/mailman/listinfo/linuxppc-embedded
>

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

^ permalink raw reply

* RE: Wireless Linux(802.11) for Embedded PowerPC
From: Chun Chung Lo @ 2006-09-06  1:30 UTC (permalink / raw)
  To: wei.li4, linuxppc-embedded


Hi,

I have also done a such project last year. I used a MPC860SR processor
card, plus an extra tailor-made carrier board (just extend the PCMCIA
interface from the processor card). The design is simple, I just
followed the reference design suggested by Freescale MPC860 user manual.
You can get this manual on Freescale's website.

Then I used a Intersil Prism2 compatible PCMCIA WLAN card (may be a
Compact Flash WLAN card is also OK, but I have never tried this) and
plug into the PCMCIA socket of the carrier board.=0D

In the whole project, I used ELDK 3.0 as my software development
platform (U-Boot, Linux, binutils... etc). And I used linux-wlan as the
WLAN card driver.=0D

But one more thing is I have built 2 of such systems and only tried the
normal WLAN functions (infrastructure, ad-hoc mode) but never AP mode
since the driver and my WLAN card does not support.

Here are some references:

1. WLAN card driver: linux-wlan -- http://www.linux-wlan.org/
2. Freescale MPC860 User Manual: --
http://www.freescale.com/files/netcomm/doc/ref_manual/MPC860UM.pdf

Thanks for your attention.

Best regards,
Lo Chun Chung

-----Original Message-----
From: linuxppc-embedded-bounces+cclo=3Dastri.org@ozlabs.org
[mailto:linuxppc-embedded-bounces+cclo=3Dastri.org@ozlabs.org] On Behalf
Of wei.li4@elf.mcgill.ca
Sent: Wednesday, 06 September, 2006 4:04 AM
To: linuxppc-embedded@ozlabs.org
Subject: Wireless Linux(802.11) for Embedded PowerPC


Hi All,

Where is the best access point for this topic? Did anyone work on this=0D
with MPC8xx? Thanks.

Wei
_______________________________________________
Linuxppc-embedded mailing list
Linuxppc-embedded@ozlabs.org
https://ozlabs.org/mailman/listinfo/linuxppc-embedded

This message (including any attachments) is for the named addressee(s)'s=
 use only. It may contain
sensitive, confidential, private proprietary or legally privileged=
 information intended for a
specific individual and purpose, and is protected by law. If you are not=
 the intended recipient,
please immediately delete it and all copies of it from your system, destroy=
 any hard copies of it
and notify the sender. Any use, disclosure, copying, or distribution of=
 this message and/or any
attachments is strictly prohibited.

^ permalink raw reply

* Re: pci error recovery procedure
From: Zhang, Yanmin @ 2006-09-06  1:26 UTC (permalink / raw)
  To: Linas Vepstas
  Cc: Yanmin Zhang, LKML, Rajesh Shah, linuxppc-dev, linux-pci maillist
In-Reply-To: <20060905190115.GE7139@austin.ibm.com>

On Wed, 2006-09-06 at 03:01, Linas Vepstas wrote:
> On Tue, Sep 05, 2006 at 10:32:08AM +0800, Zhang, Yanmin wrote:
> > Is it the exclusive reason to have multi-steps?
> 
> I don't understand the question. A previous email explained the reason
> to have mutiple steps.
The question is against Ben's comments. Pls. don't delete his comments
in your reply. 

> 
> > 1) Here link reset and hard reset are hardware operations, not the
> > link_reset and slot_reset callback in pci_error_handlers.
> 
> I don't understand the comment.
I wanted to clarify that we need differentiate link/hard reset from
callback link_reset and slot_reset when discussing the API.

> 
> > 2) Callback error_detected will notify drivers there is PCI errors. Drivers
> > shouldn't do any I/O in error_detected.
> 
> It shouldn't matter. If it is truly important for a particular platform
> to make sure that there is no i/o, then the low-level i/o routines
> could be modified to drop any accidentally issued i/o on the floor.
> This doesn't require a change to either the API or the policy.
> > 3) If both the link and slot are reset after all error_detected are called,
> > the device should go back to initial status and all DMA should be stopped
> > automatically. Why does the driver still need a chance to stop DMA? 
> 
> As explained previously, not all drivers may want to have a full
> electrical device reset.
I need repeat my idea.
1) My new pci_error_handlers doesn't always choose to reset slot. It
still depends on the return value of error_detected.
2) As a matter of fact, most cases of specific device's error_detected callback
will choose to return PCI_ERS_RESULT_NEED_RESET. Like what you did for
e100/e1000/ipr.

> 
> > The
> > error_detected of the drivers in the latest kernel who support err handlers
> > always returns PCI_ERS_RESULT_NEED_RESET. They are typical examples.
> 
> Just because the current drivers do it this way does not mean that this is
> the best way to do things.
If it's not the best way, why did you choose to reset slot for e1000/e100/ipr
error handlers? They are typical widely-used devices. To make it easier to
add error handlers?


>  A full reset is time-consuming. Some drivers
> may want to implement a faster and quicker reset.
> 
> --linas

^ permalink raw reply

* Re: [patch 0/5] [v2] powerpc: PA Semi PWRficient patches
From: Olof Johansson @ 2006-09-06  1:41 UTC (permalink / raw)
  To: Michael Ellerman; +Cc: linuxppc-dev, paulus, anton
In-Reply-To: <1157501868.3661.1.camel@localhost.localdomain>

On Wed, Sep 06, 2006 at 10:17:48AM +1000, Michael Ellerman wrote:
> On Tue, 2006-09-05 at 18:43 -0500, Olof Johansson wrote:
> > Hi,
> > 
> > The following series of patches introduces basic support for PA Semi's
> > PA6T core, and the base platform support for PWRficient PA6T-1682M.
> 
> I have a spare 'o' and 'e' over here if you want 'em?
> 
> ;)

Keep them, I'm sure IBM is working on some new opcodes. They're
so rare, it's better to spend them there! :-)


-Olof

^ permalink raw reply

* Re: pci error recovery procedure
From: Zhang, Yanmin @ 2006-09-06  1:35 UTC (permalink / raw)
  To: Linas Vepstas
  Cc: Yanmin Zhang, LKML, Rajesh Shah, linuxppc-dev, linux-pci maillist
In-Reply-To: <20060905185020.GD7139@austin.ibm.com>

On Wed, 2006-09-06 at 02:50, Linas Vepstas wrote:
> On Mon, Sep 04, 2006 at 07:03:12PM +1000, Benjamin Herrenschmidt wrote:
> > 
> > > As you know, all functions of a device share the same bus number and 5 bit dev number.
> > > They just have different 3 bit function number. We could deduce if functions are in the same
> > > device (slot).
> > 
> > Until you have a P2P bridge ...
> 
> And this is not theoretical: for example, the matrox graphics cards:
> 
> 0000:c8:01.0 PCI bridge: Hint Corp HB6 Universal PCI-PCI bridge (non-transparent mode) (rev 13)
> 0000:c9:00.0 VGA compatible controller: Matrox Graphics, Inc. MGA G400 AGP (rev 85)
> 
> Now, I could have sworn there was another device behind this bridge, 
> some serial or joystick controller or something, although this
> particular card doesn't seem to have it.
Thanks. My comments above in this email is just to try to find a method
to judge if 2 or more functions belongs to the same device. If it's
not right, it still doesn't hurt the new API pci_error_handlers.

> 
> ------
> It's not clear to me what hardware may show up in the future. 
> For example, someone may build a 32x PCI-E card that will act 
> as a bridge to a drawer with half-a-dozen ordinary PCI-X slots 
> in it. This is perhaps a bit hypothetical, but changing the API 
> will make it harder to implement eror recovery for such a system.
I agree that it's difficult to predict the future. At least the new API
could process the example.

> FWIW, there is at least one pSeries system in the lab which has
> several hundred PCI slots attached to it, although I've never 
> done testing on it. Hmm. Maybe its time I did ... 
> 
> --linas

^ permalink raw reply

* Re: pci error recovery procedure
From: Zhang, Yanmin @ 2006-09-06  2:04 UTC (permalink / raw)
  To: Linas Vepstas
  Cc: linuxppc-dev, linux-pci maillist, Yanmin Zhang, LKML, Rajesh Shah
In-Reply-To: <20060905191739.GF7139@austin.ibm.com>

On Wed, 2006-09-06 at 03:17, Linas Vepstas wrote:
> On Mon, Sep 04, 2006 at 01:47:30PM +0800, Zhang, Yanmin wrote:
> > > 
> > > Again, consider the multi-function cards. On pSeries, I can  only enable 
> > > DMA on a per-slot basis, not a per-function basis. So if one driver
> > > enables DMA before some other driver has reset appropriately, everything
> > > breaks.
> > Does here 'reset' mean hardware slot reset? 
> 
> I should have said: If one driver of a multi-function card enables DMA before 
> another driver has stabilized its harware, then everything breaks.
What's another driver's hardware? A function of the previous multi-function
card? Or a function of another device?

Ok. now, I copy what you said before below for more discussion.
> If we enabled both DMA and MMIO at the same time, there are mnay cases
> where the card will immediately trap again -- for example, if its
> DMA'ing to some crazy address. Thus, typically, one wants DMA disabled 
> until after the card reset.  Withouth the mmio_enabled() reset, there
> is no way of doing this.
Did you asume the card reset is executed by callback mmio_enabled?


> Again, consider the multi-function cards. On pSeries, I can  only enable 
> DMA on a er-slot basis, not a per-function basis. So if one driver
> enables DMA before some other driver has reset appropriately, everything
> breaks.
What does 'I' above stand for? The platform error recovery procedure
or the error callbacks of drivers? I guess it means platform, that is,
only platform enables DMA for the whole slot. But why does the last sentence
become driver enables DMA? As you know, driver binds device function instead of
slot. Could driver enable DMA for a function?


> 
> > Then, if the slot is always reset, there will be no the problem. 
> 
> But that assumes that a hardware #RST will always be done. The API
> was designed to get away from this requirement.
> 
> > If mmio_enabled is not used currently, I think we could delete it firstly. Later on,
> > if a platform really need it, we could add it, so we could keep the simplied codes.
> 
> It would be very difficult to add it later. And it would be especially
> silly, given that someone would find this discussion in the mailing list 
> archives.
You stick to keep mmio_enabled which is not used currently, but if there will be
a new platform who uses a more fine-grained steps to recover pci/pci-e, would
you say 'it would be very difficut' and refuse add new callbacks?

> 
> > Thanks. Now I understand why you specified mmio_enabled and slot_reset. They are just
> > to map to pSeries platform hardware operation steps. I know little about pSeries hardware,
> 
> The hardware was designed that way because the hardware engineers
> thought that this is what the device driver writers would need. 
> Thay are there to map to actual recovery steps that actual device
> drivers might want to do.
It doesn't prevent software from merging some steps. And, we want
to implement pci/pci-e error recovery for more platforms instead of just
pSeries.

> 
> > but is it possible to merge such hardware steps from software point of view?
> 
> The previous email explained why this would be a bad idea. 
Obviously, such conclusion is too early.

> 
> > > The platform. By "electrical reset", I mean "dropping the #RST pin low
> > > for 200mS". Only the platform can do this.
> > Thanks for your explanation. I assume after the electrical reset, all device
> > functions of the device slot will go back to the initial status before
> > attaching their drivers.
> 
> Maybe. Depends on what yur BIOS does. On pSeries, I also need to
> set up the adress BARs
> 
> > I found a problem of e1000 driver when testing its error handlers. After the NIC is resumed,
> > its RX/TX packets numbers are crazy.
> 
> Hmm. There is a patch to prevent this from happening. I thought
> it was applied a long time ago. e1000_update_stats() should include the
> lines:
> 
>    if (pdev->error_state && pdev->error_state != pci_channel_io_normal)
>       return;
> 
> which is enough to prevent crazy stats on my machine.
Thanks a lot!

Yanmin

^ permalink raw reply

* Re: On the transfer of data from the uart.c driver to the tty layer...
From: Dan Malek @ 2006-09-06  2:28 UTC (permalink / raw)
  To: Alejandro C; +Cc: linuxppc-dev
In-Reply-To: <BAY121-F225DA006048FC317F8E97BDB300@phx.gbl>


On Sep 5, 2006, at 10:56 AM, Alejandro C wrote:

> I'm working on a the uart.c driver to add support for SCCs in HDLC  
> mode.

Write a driver that does HDLC, please don't "update"
(complicate) the existing uart.c driver by adding
these features.  The uart.c is a UART driver, not
an SCC driver to be modified to your wishes.
Add another, configurable driver to do this.

> I've come across is that the receive buffer in the struct  
> tty_struct, where
> the tty layer collects the data from the driver
> (tty_struct.tty_flip_buffer.char_buf), is only 1024 bytes

Write a driver specific to your requirements that
has the proper sized buffers and management.

> (2*TTY_FLIPBUF_SIZE). The easiest at this stage would be to make those
> buffers larger, but I don't know what sort of impact this might  
> have... Any
> tips?

Yes, don't do this.  Those buffers are allocated from
a fairly small pool specific to the needs of the uart
driver.  Making these larger may quickly cause the
CPM drivers to run out of space.

> Any ideas on how to solve it in another fashion?

Write a driver specific to your requirements with the
proper buffer management for these sizes.

Thanks.

	-- Dan

^ permalink raw reply

* Re: [PATCH] kdump : Support kernels having 64k page size.
From: Michael Ellerman @ 2006-09-06  2:59 UTC (permalink / raw)
  To: Sachin P. Sant; +Cc: linuxppc-dev
In-Reply-To: <44FE15B0.3030909@in.ibm.com>

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

On Wed, 2006-09-06 at 05:56 +0530, Sachin P. Sant wrote:
> The following  kernel patch [ along with a patch to kexec tools posted 
> seperately ]
> is required to generate proper core files using kdump on ppc64.

Hi Sachin,

Can you provide some more explanation? It's not clear to me why this is
necessary.

thanks

-- 
Michael Ellerman
OzLabs, IBM Australia Development Lab

wwweb: http://michael.ellerman.id.au
phone: +61 2 6212 1183 (tie line 70 21183)

We do not inherit the earth from our ancestors,
we borrow it from our children. - S.M.A.R.T Person

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

^ permalink raw reply

* Re: common flatdevtree code
From: Paul Mackerras @ 2006-09-06  3:52 UTC (permalink / raw)
  To: Mark A. Greer; +Cc: linuxppc-dev, hollisb
In-Reply-To: <20060831184020.GA13494@mag.az.mvista.com>

Mark A. Greer writes:

> I'm doing some fairly massive rework to my patches so it'll take
> another day or two (plus 4 day weekend for me).  In the meantime,
> this is what I've done to your code.  :)

Any further progress on this?  I need an ft library for the prep
bootwrapper changes I'm planning.

I don't like the approach of doing a malloc for the whole blob every
time you make a property larger though.  I'm going to be adding quite
a few nodes and properties, potentially, and with a simple-minded
malloc (such as you get in a bootwrapper) it's likely to chew through
a lot of memory unnecessarily.  I would rather be able to start with
an existing blob and pull it apart once, add stuff to it, then put it
back together, doing a small number of mallocs in the process.

> I still plan on changing it a bit to use ft_next in a few more routines
> (e.g., ft_dump_blob).  ft_next has a clumsy interface but I like the fact

The interface might look nicer if we had a structure for the tag,
name, value and length.  Then there would only be one parameter
instead of the four you have at the moment.

Paul.

^ permalink raw reply

* Re: [PATCH] kdump : Support kernels having 64k page size.
From: Haren Myneni @ 2006-09-06  6:13 UTC (permalink / raw)
  To: michael; +Cc: linuxppc-dev, Sachin P. Sant
In-Reply-To: <1157511577.3661.7.camel@localhost.localdomain>

Michael Ellerman wrote:

>On Wed, 2006-09-06 at 05:56 +0530, Sachin P. Sant wrote:
>  
>
>>The following  kernel patch [ along with a patch to kexec tools posted 
>>seperately ]
>>is required to generate proper core files using kdump on ppc64.
>>    
>>
>
>Hi Sachin,
>
>Can you provide some more explanation? It's not clear to me why this is
>necessary.
>
>thanks
>
>  
>
At present we are doing the backup of 32K. Thus created one ELF PT_LOAD 
segment for this region.
But, in the case of 64K page size, second segments starts at 32K and the 
first one is not page aligned.  __ioremap() (crash_dump.c) getting 
failed if pfn = 0 which is the case for the second PT_LOAD segment.  We 
did not hit this issue for 4K page size because the the first page (32K 
backup) is copied to second kernel memory and thus referencing with the 
second kernel pfn.

Here the fix is,  backup regions size is max(PAGE_SIZE,  32K)  so that 
at least one page will be part of backup ELF segment.  Drawback here is, 
we will end up 32K more for backup for 64K page size.

It can also be fixed in copy_oldmem_page() (crash_dump.c), but first 
PT_LOAD segment is not page aligned:

if (pfn > 0)
    vaddr = __ioremap(pfn << PAGE_SHIFT, PAGE_SIZE, 0);
else
    vaddr = __va(pfn << PAGE_SHIFT);


Thanks
Haren





>------------------------------------------------------------------------
>
>_______________________________________________
>Linuxppc-dev mailing list
>Linuxppc-dev@ozlabs.org
>https://ozlabs.org/mailman/listinfo/linuxppc-dev
>

^ permalink raw reply

* Re: MAC driver issue
From: David H. Lynch Jr. @ 2006-09-06  6:25 UTC (permalink / raw)
  To: linuxppc-embedded
In-Reply-To: <44FD3640.30208@cambridgebroadband.com>

Alex Zeffertt wrote:
> Hi David,
>
> I think in order to get any help from this list you need to be more 
> specific
> about how this problem relates to PowerPCs.
    The relationship is weak - the driver is for a NIC in an embedded 
PPC 405.
    I was just hoping that someone here might have experienced a similar 
problem and have an idea what the answer might be -
    rather than join another list - I am sure there must be something 
like a network drivers list


>
> You also need to say exactly what "never gets handed off to the ARP 
> protocol"
> means.

    I mean I put a printk into the ARP protocol handler and in the 
working case I get to the prink, and in the failing case I don't.

    I have tried inspecting  netif_rx - but  it just queues up received 
packets - and in both my cases returns success.

    I am basically tryng to figure out what is it that the inbound 
network stack is upset about - somewhere it must have decided to reject
    my data, if I knew what it did not like, I would have a clue what to 
fix.  

> FWIW, in my experience the hardware independent parts of the 
> networking stack are
> very stable and the problem is almost always with the drivers, or with 
> the IP
> configuration (e.g. two interfaces on the same subnet).
    I have no doubt it is with the driver.  I am somewhat fortunate in 
this instance that I have a nearly identical setup - this is an FPGA 
based system
    I can swap the FPGA firmware, get an almost identical kernel with a 
slightly different NIC, and everything works - same cables, same IP's,
    Same switch, The only things different are the NIC and its driver. 
Even the Linux kernels are identical - except the NIC driver.
   
    BUT so is the data received and passed on to the kernel (outside 
random differences in the padding of the ARP packet)
    One works the other doesn't.


   


But since everything looks perfect, I have no clue where to start looking.
>
> Alex
>
> David H. Lynch Jr. wrote:
>> I am trying to get a network driver working.
>>
>> It sends - I can capture the output with Etherreal and it looks good.
>> It receives - I can dump the received packets when the come in and 
>> they look ok to me.
>>
>>
>> BUT,
>>     If I try to ping another host, first Linux sends and ARP 
>> broadcast, the appropriate client sends an ARP response.
>>     Etherreal is happy with both.
>>     My driver gets the response - The driver says the packet lenght 
>> is 64 bytes, which includes something like 14 bytes of padding at the 
>> end.
>>     But the actual packet looks perfect exactly like what etherreal 
>> says it should (and what I get when I capture a received ARP packet 
>> in another driver).
>>     At the end of the recive interrupt the skb is passed to Linux 
>> with the netif_rc(ndev) function. This returns SUCCESS.
>>
>>     However, the paket never gets handed of to the ARP protocol - I 
>> put some debugging in there and I never see it, while if I switch to 
>> a different  NIC
>>     driver nearly the identical ARP packet gets processed by arp 
>> inside Linux.
>>
>>     I have tried to chase this down, but I can't  follow what is 
>> going on inside of all the /net/core/dev.c etc.
>>
>>     Has anyone seen something similar to this ?
>>
>>     Does anyone have a clue where I can find some info  on trying to 
>> follow something through netif_rx to see where things are going off 
>> the rails ?
>>
>>
>>
>


-- 
Dave Lynch 					  	    DLA Systems
Software Development:  				         Embedded Linux
717.627.3770 	       dhlii@dlasys.net 	  http://www.dlasys.net
fax: 1.253.369.9244 			           Cell: 1.717.587.7774
Over 25 years' experience in platforms, languages, and technologies too numerous to list.

"Any intelligent fool can make things bigger and more complex... It takes a touch of genius - and a lot of courage to move in the opposite direction."
Albert Einstein

^ permalink raw reply

* RE: [PATCH 4/4] Add MPC8349E MDS device tree source file toarch/powerpc/boot/dts
From: Jiang Bo-r61859 @ 2006-09-06  7:36 UTC (permalink / raw)
  To: Phillips Kim-R1AAHA, linuxppc-dev
In-Reply-To: <20060825115947.0d9ad52f.kim.phillips@freescale.com>

=20

>-----Original Message-----
>Add MPC8349E MDS device tree source file to=20
>arch/powerpc/boot/dts, aptly indicating new school sense=20
>values definition.
>
> arch/powerpc/boot/dts/mpc8349emds.dts |  328=20
>+++++++++++++++++++++++++++++++++
> 1 files changed, 328 insertions(+), 0 deletions(-)
>+			device_type =3D "serial";
>+		pci@8500 {
>+			interrupt-map-mask =3D <f800 0 0 7>;
>+			interrupt-map =3D <
>+
>+					/* IDSEL 0x11 */
>+					 8800 0 0 1 700 14 0
>+					 8800 0 0 2 700 15 0
>+					 8800 0 0 3 700 16 0
>+					 8800 0 0 4 700 17 0
snip
>+
>+					/* IDSEL 0x18 */
>+					 b000 0 0 1 700 15 0
>+					 b000 0 0 2 700 16 0
>+					 b000 0 0 3 700 17 0
>+					 b000 0 0 4 700 14 0>;
snip

If it is about IDSEL 0x18, b000 should be c000

>+
>+		pci@8600 {
>+			interrupt-map-mask =3D <f800 0 0 7>;
>+			interrupt-map =3D <
>+
snip
>+					/* IDSEL 0x18 */
>+					 b000 0 0 1 700 15 0
>+					 b000 0 0 2 700 16 0
>+					 b000 0 0 3 700 17 0
>+					 b000 0 0 4 700 14 0>;
Same problem.
>_______________________________________________
>Linuxppc-dev mailing list
>Linuxppc-dev@ozlabs.org
>https://ozlabs.org/mailman/listinfo/linuxppc-dev
>

^ permalink raw reply

* MPC5200 SPI interface.
From: Txema Lopez @ 2006-09-06  7:30 UTC (permalink / raw)
  To: linuxppc-embedded

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

Hi all,
We are developping a Lite5200B based custom board and we are planning to 
connect some SPI peripherals. But we are not sure about what MPC5200 SPI 
interface choose.  Does anyone know what are the 
advantages/disadvantages of use the MPC5200 SPI dedicated interface or  
an PSC (PSC3) as the SPI interface?. The linux kernel includes both 
interfaces but we can not see in the documentation anything about this 
aspect. Any kind of information will be welcomed.

Best Regards,
José María López.

[-- Attachment #2: tlopez.vcf --]
[-- Type: text/x-vcard, Size: 324 bytes --]

begin:vcard
fn:Jose Maria Lopez
n:Lopez;Jose Maria
org:Fagor Automation S. Coop.
adr:;;San Andres 19. Apdo. 144;Arrasate-Mondragon;;20500;Spain
email;internet:tlopez@aotek.es
title:Sotware engineer
tel;work:(34) 943719200
tel;fax:(34) 943791712 
x-mozilla-html:FALSE
url:http://www.fagorautomation.es
version:2.1
end:vcard


^ permalink raw reply

* [POWERPC] merge iSeries i/o operations with the rest
From: Stephen Rothwell @ 2006-09-06  8:24 UTC (permalink / raw)
  To: paulus; +Cc: ppc-dev
In-Reply-To: <20060905120817.e52857ee.sfr@canb.auug.org.au>

The low level i/o operations are now handled in iSeries by taking a
trap when we access the (inaccessible) io memory region and calling the
Hypervisor from the trap code.

The memset_io/memcpy_(to,from)io routines just do a firmware feature
check as it will compile out in the non combined build case and in the
combined build, the extra check is a relatively small overhead.

The remainder of the routines are not used in the drivers for any
currently supported hardware on legacy iSeries (they used to BUG or were
not even implemented), so just use the eeh versions.

Signed-off-by: Stephen Rothwell <sfr@canb.auug.org.au>
---
 arch/powerpc/mm/fault.c                  |    8 +
 arch/powerpc/platforms/iseries/pci.c     |  170 +++++++++++++++++++++---------
 include/asm-powerpc/io.h                 |   78 +++++---------
 include/asm-powerpc/iseries/iseries_io.h |   27 -----
 include/asm-powerpc/ppc-pci.h            |   11 ++
 5 files changed, 169 insertions(+), 125 deletions(-)

This version does not change the low level inline assembler operations at
all (and so emulates more instructions in th trap handler).

Thus there are no changes to the generated code unles you compile for
legacy iSeries where I have booted this.

-- 
Cheers,
Stephen Rothwell                    sfr@canb.auug.org.au

diff --git a/arch/powerpc/mm/fault.c b/arch/powerpc/mm/fault.c
index 78a0d59..e8add2b 100644
--- a/arch/powerpc/mm/fault.c
+++ b/arch/powerpc/mm/fault.c
@@ -38,6 +38,8 @@ #include <asm/uaccess.h>
 #include <asm/tlbflush.h>
 #include <asm/kdebug.h>
 #include <asm/siginfo.h>
+#include <asm/firmware.h>
+#include <asm/ppc-pci.h>
 
 #ifdef CONFIG_KPROBES
 ATOMIC_NOTIFIER_HEAD(notify_page_fault_chain);
@@ -185,8 +187,12 @@ #endif /* CONFIG_4xx || CONFIG_BOOKE */
 	}
 
 	/* On a kernel SLB miss we can only check for a valid exception entry */
-	if (!user_mode(regs) && (address >= TASK_SIZE))
+	if (!user_mode(regs) && (address >= TASK_SIZE)) {
+		if (firmware_has_feature(FW_FEATURE_ISERIES) &&
+				iseries_handle_io_fault(regs, address))
+			return 0;
 		return SIGSEGV;
+	}
 
 #if !(defined(CONFIG_4xx) || defined(CONFIG_BOOKE))
   	if (error_code & DSISR_DABRMATCH) {
diff --git a/arch/powerpc/platforms/iseries/pci.c b/arch/powerpc/platforms/iseries/pci.c
index f4d427a..0c94046 100644
--- a/arch/powerpc/platforms/iseries/pci.c
+++ b/arch/powerpc/platforms/iseries/pci.c
@@ -26,6 +26,7 @@ #include <linux/init.h>
 #include <linux/module.h>
 #include <linux/ide.h>
 #include <linux/pci.h>
+#include <linux/ptrace.h>
 
 #include <asm/io.h>
 #include <asm/irq.h>
@@ -38,6 +39,7 @@ #include <asm/abs_addr.h>
 #include <asm/iseries/hv_call_xm.h>
 #include <asm/iseries/mf.h>
 #include <asm/iseries/iommu.h>
+#include <asm/iseries/iseries_io.h>
 
 #include <asm/ppc-pci.h>
 
@@ -270,46 +272,6 @@ void pcibios_fixup_resources(struct pci_
 }
 
 /*
- * I/0 Memory copy MUST use mmio commands on iSeries
- * To do; For performance, include the hv call directly
- */
-void iSeries_memset_io(volatile void __iomem *dest, char c, size_t Count)
-{
-	u8 ByteValue = c;
-	long NumberOfBytes = Count;
-
-	while (NumberOfBytes > 0) {
-		iSeries_Write_Byte(ByteValue, dest++);
-		-- NumberOfBytes;
-	}
-}
-EXPORT_SYMBOL(iSeries_memset_io);
-
-void iSeries_memcpy_toio(volatile void __iomem *dest, void *source, size_t count)
-{
-	char *src = source;
-	long NumberOfBytes = count;
-
-	while (NumberOfBytes > 0) {
-		iSeries_Write_Byte(*src++, dest++);
-		-- NumberOfBytes;
-	}
-}
-EXPORT_SYMBOL(iSeries_memcpy_toio);
-
-void iSeries_memcpy_fromio(void *dest, const volatile void __iomem *src, size_t count)
-{
-	char *dst = dest;
-	long NumberOfBytes = count;
-
-	while (NumberOfBytes > 0) {
-		*dst++ = iSeries_Read_Byte(src++);
-		-- NumberOfBytes;
-	}
-}
-EXPORT_SYMBOL(iSeries_memcpy_fromio);
-
-/*
  * Look down the chain to find the matching Device Device
  */
 static struct device_node *find_Device_Node(int bus, int devfn)
@@ -491,7 +453,7 @@ static inline struct device_node *xlate_
  * iSeries_Read_Word = Read Word  (16 bit)
  * iSeries_Read_Long = Read Long  (32 bit)
  */
-u8 iSeries_Read_Byte(const volatile void __iomem *IoAddress)
+static u8 iSeries_Read_Byte(const volatile void __iomem *IoAddress)
 {
 	u64 BarOffset;
 	u64 dsa;
@@ -518,9 +480,8 @@ u8 iSeries_Read_Byte(const volatile void
 
 	return (u8)ret.value;
 }
-EXPORT_SYMBOL(iSeries_Read_Byte);
 
-u16 iSeries_Read_Word(const volatile void __iomem *IoAddress)
+static u16 iSeries_Read_Word(const volatile void __iomem *IoAddress)
 {
 	u64 BarOffset;
 	u64 dsa;
@@ -548,9 +509,8 @@ u16 iSeries_Read_Word(const volatile voi
 
 	return swab16((u16)ret.value);
 }
-EXPORT_SYMBOL(iSeries_Read_Word);
 
-u32 iSeries_Read_Long(const volatile void __iomem *IoAddress)
+static u32 iSeries_Read_Long(const volatile void __iomem *IoAddress)
 {
 	u64 BarOffset;
 	u64 dsa;
@@ -578,7 +538,6 @@ u32 iSeries_Read_Long(const volatile voi
 
 	return swab32((u32)ret.value);
 }
-EXPORT_SYMBOL(iSeries_Read_Long);
 
 /*
  * Write MM I/O Instructions for the iSeries
@@ -587,7 +546,7 @@ EXPORT_SYMBOL(iSeries_Read_Long);
  * iSeries_Write_Word = Write Word(16 bit)
  * iSeries_Write_Long = Write Long(32 bit)
  */
-void iSeries_Write_Byte(u8 data, volatile void __iomem *IoAddress)
+static void iSeries_Write_Byte(u8 data, volatile void __iomem *IoAddress)
 {
 	u64 BarOffset;
 	u64 dsa;
@@ -612,9 +571,8 @@ void iSeries_Write_Byte(u8 data, volatil
 		rc = HvCall4(HvCallPciBarStore8, dsa, BarOffset, data, 0);
 	} while (CheckReturnCode("WWB", DevNode, &retry, rc) != 0);
 }
-EXPORT_SYMBOL(iSeries_Write_Byte);
 
-void iSeries_Write_Word(u16 data, volatile void __iomem *IoAddress)
+static void iSeries_Write_Word(u16 data, volatile void __iomem *IoAddress)
 {
 	u64 BarOffset;
 	u64 dsa;
@@ -639,9 +597,8 @@ void iSeries_Write_Word(u16 data, volati
 		rc = HvCall4(HvCallPciBarStore16, dsa, BarOffset, swab16(data), 0);
 	} while (CheckReturnCode("WWW", DevNode, &retry, rc) != 0);
 }
-EXPORT_SYMBOL(iSeries_Write_Word);
 
-void iSeries_Write_Long(u32 data, volatile void __iomem *IoAddress)
+static void iSeries_Write_Long(u32 data, volatile void __iomem *IoAddress)
 {
 	u64 BarOffset;
 	u64 dsa;
@@ -666,4 +623,113 @@ void iSeries_Write_Long(u32 data, volati
 		rc = HvCall4(HvCallPciBarStore32, dsa, BarOffset, swab32(data), 0);
 	} while (CheckReturnCode("WWL", DevNode, &retry, rc) != 0);
 }
-EXPORT_SYMBOL(iSeries_Write_Long);
+
+/*
+ * I/0 Memory copy MUST use mmio commands on iSeries
+ * To do; For performance, include the hv call directly
+ */
+void iSeries_memset_io(volatile void __iomem *dest, char c, size_t Count)
+{
+	u8 ByteValue = c;
+	long NumberOfBytes = Count;
+
+	while (NumberOfBytes > 0) {
+		iSeries_Write_Byte(ByteValue, dest++);
+		-- NumberOfBytes;
+	}
+}
+EXPORT_SYMBOL(iSeries_memset_io);
+
+void iSeries_memcpy_toio(volatile void __iomem *dest, const void *source,
+		size_t count)
+{
+	const char *src = source;
+	long NumberOfBytes = count;
+
+	while (NumberOfBytes > 0) {
+		iSeries_Write_Byte(*src++, dest++);
+		-- NumberOfBytes;
+	}
+}
+EXPORT_SYMBOL(iSeries_memcpy_toio);
+
+void iSeries_memcpy_fromio(void *dest, const volatile void __iomem *src, size_t count)
+{
+	char *dst = dest;
+	long NumberOfBytes = count;
+
+	while (NumberOfBytes > 0) {
+		*dst++ = iSeries_Read_Byte(src++);
+		-- NumberOfBytes;
+	}
+}
+EXPORT_SYMBOL(iSeries_memcpy_fromio);
+
+int iseries_handle_io_fault(struct pt_regs *regs, unsigned long address)
+{
+	u32 instr;
+	int reg;
+	int ra;
+	int op;
+	int eop;
+	void __iomem *addr = (void __iomem *)address;
+
+	/* is the address in out i/o range? */
+	if ((address < BASE_IO_MEMORY) || (address >= max_io_memory))
+		return 0;
+	/* we are only called for kernel mode faults */
+	instr = *(u32 *)regs->nip;
+	op = (instr >> 26) & 0x3f;
+	reg = (instr >> 21) & 0x1f;
+	ra = (instr >> 16) & 0x1f;
+	switch (op) {
+	case 31:
+		eop = (instr >> 1) & 0x3ff;
+
+		switch (eop) {
+		case 87:	/* lbzx */
+		case 119:	/* lbzux */
+			regs->gpr[reg] = iSeries_Read_Byte(addr);
+			if (eop & 0x20)
+				regs->gpr[ra] = address;
+			break;
+		case 215:	/* stbx */
+		case 247:	/* stbux */
+			iSeries_Write_Byte(regs->gpr[reg], addr);
+			if (eop & 0x20)
+				regs->gpr[ra] = address;
+			break;
+		case 790:	/* lhbrx */
+			regs->gpr[reg] = iSeries_Read_Word(addr);
+			break;
+		case 918:	/* sthbrx */
+			iSeries_Write_Word(regs->gpr[reg], addr);
+			break;
+		case 534:	/* lwbrx */
+			regs->gpr[reg] = iSeries_Read_Long(addr);
+			break;
+		case 662:	/* stwbrx */
+			iSeries_Write_Long(regs->gpr[reg], addr);
+			break;
+		default:
+			return 0;
+		}
+		break;
+	case 34:	/* lbz */
+	case 35:	/* lbzu */
+		regs->gpr[reg] = iSeries_Read_Byte(addr);
+		if (op & 1)
+			regs->gpr[ra] = address;
+		break;
+	case 38:	/* stb */
+	case 39:	/* stbu */
+		iSeries_Write_Byte(regs->gpr[reg], addr);
+		if (op & 1)
+			regs->gpr[ra] = address;
+		break;
+	default:
+		return 0;
+	}
+	regs->nip += 4;
+	return 1;
+}
diff --git a/include/asm-powerpc/io.h b/include/asm-powerpc/io.h
index 36c4c34..7762898 100644
--- a/include/asm-powerpc/io.h
+++ b/include/asm-powerpc/io.h
@@ -19,11 +19,10 @@ #else
 #include <linux/compiler.h>
 #include <asm/page.h>
 #include <asm/byteorder.h>
-#ifdef CONFIG_PPC_ISERIES 
 #include <asm/iseries/iseries_io.h>
-#endif  
 #include <asm/synch.h>
 #include <asm/delay.h>
+#include <asm/firmware.h>
 
 #include <asm-generic/iomap.h>
 
@@ -41,44 +40,10 @@ #define SLOW_DOWN_IO
 extern unsigned long isa_io_base;
 extern unsigned long pci_io_base;
 
-#ifdef CONFIG_PPC_ISERIES
-/* __raw_* accessors aren't supported on iSeries */
-#define __raw_readb(addr)	{ BUG(); 0; }
-#define __raw_readw(addr)       { BUG(); 0; }
-#define __raw_readl(addr)       { BUG(); 0; }
-#define __raw_readq(addr)       { BUG(); 0; }
-#define __raw_writeb(v, addr)   { BUG(); 0; }
-#define __raw_writew(v, addr)   { BUG(); 0; }
-#define __raw_writel(v, addr)   { BUG(); 0; }
-#define __raw_writeq(v, addr)   { BUG(); 0; }
-#define readb(addr)		iSeries_Read_Byte(addr)
-#define readw(addr)		iSeries_Read_Word(addr)
-#define readl(addr)		iSeries_Read_Long(addr)
-#define writeb(data, addr)	iSeries_Write_Byte((data),(addr))
-#define writew(data, addr)	iSeries_Write_Word((data),(addr))
-#define writel(data, addr)	iSeries_Write_Long((data),(addr))
-#define memset_io(a,b,c)	iSeries_memset_io((a),(b),(c))
-#define memcpy_fromio(a,b,c)	iSeries_memcpy_fromio((a), (b), (c))
-#define memcpy_toio(a,b,c)	iSeries_memcpy_toio((a), (b), (c))
-
-#define inb(addr)		readb(((void __iomem *)(long)(addr)))
-#define inw(addr)		readw(((void __iomem *)(long)(addr)))
-#define inl(addr)		readl(((void __iomem *)(long)(addr)))
-#define outb(data,addr)		writeb(data,((void __iomem *)(long)(addr)))
-#define outw(data,addr)		writew(data,((void __iomem *)(long)(addr)))
-#define outl(data,addr)		writel(data,((void __iomem *)(long)(addr)))
 /*
- * The *_ns versions below don't do byte-swapping.
- * Neither do the standard versions now, these are just here
- * for older code.
+ * __raw_* accessors aren't supported on iSeries so
+ * these used to BUG() on iSeries, but that is not really necessary.
  */
-#define insb(port, buf, ns)	_insb((u8 __iomem *)((port)+pci_io_base), (buf), (ns))
-#define insw(port, buf, ns)	_insw_ns((u8 __iomem *)((port)+pci_io_base), (buf), (ns))
-#define insl(port, buf, nl)	_insl_ns((u8 __iomem *)((port)+pci_io_base), (buf), (nl))
-#define insw_ns(port, buf, ns)	_insw_ns((u16 __iomem *)((port)+pci_io_base), (buf), (ns))
-#define insl_ns(port, buf, nl)	_insl_ns((u32 __iomem *)((port)+pci_io_base), (buf), (nl))
-#else
-
 static inline unsigned char __raw_readb(const volatile void __iomem *addr)
 {
 	return *(volatile unsigned char __force *)addr;
@@ -111,6 +76,7 @@ static inline void __raw_writeq(unsigned
 {
 	*(volatile unsigned long __force *)addr = v;
 }
+
 #define readb(addr)		eeh_readb(addr)
 #define readw(addr)		eeh_readw(addr)
 #define readl(addr)		eeh_readl(addr)
@@ -119,9 +85,6 @@ #define writeb(data, addr)	eeh_writeb((d
 #define writew(data, addr)	eeh_writew((data), (addr))
 #define writel(data, addr)	eeh_writel((data), (addr))
 #define writeq(data, addr)	eeh_writeq((data), (addr))
-#define memset_io(a,b,c)	eeh_memset_io((a),(b),(c))
-#define memcpy_fromio(a,b,c)	eeh_memcpy_fromio((a),(b),(c))
-#define memcpy_toio(a,b,c)	eeh_memcpy_toio((a),(b),(c))
 #define inb(port)		eeh_inb((unsigned long)port)
 #define outb(val, port)		eeh_outb(val, (unsigned long)port)
 #define inw(port)		eeh_inw((unsigned long)port)
@@ -140,8 +103,6 @@ #define insl(port, buf, nl)	eeh_insl_ns(
 #define insw_ns(port, buf, ns)	eeh_insw_ns((port), (buf), (ns))
 #define insl_ns(port, buf, nl)	eeh_insl_ns((port), (buf), (nl))
 
-#endif
-
 #define outsb(port, buf, ns)  _outsb((u8 __iomem *)((port)+pci_io_base), (buf), (ns))
 #define outsw(port, buf, ns)  _outsw_ns((u16 __iomem *)((port)+pci_io_base), (buf), (ns))
 #define outsl(port, buf, nl)  _outsl_ns((u32 __iomem *)((port)+pci_io_base), (buf), (nl))
@@ -399,9 +360,34 @@ static inline void out_be64(volatile uns
 	__asm__ __volatile__("std%U0%X0 %1,%0; sync" : "=m" (*addr) : "r" (val));
 }
 
-#ifndef CONFIG_PPC_ISERIES 
 #include <asm/eeh.h>
-#endif
+
+static inline void memset_io(volatile void __iomem *addr, int c,
+                                 unsigned long n)
+{
+	if (firmware_has_feature(FW_FEATURE_ISERIES))
+		iSeries_memset_io(addr, c, n);
+	else
+		eeh_memset_io(addr, c, n);
+}
+
+static inline void memcpy_fromio(void *dest, const volatile void __iomem *src,
+                                 unsigned long n)
+{
+	if (firmware_has_feature(FW_FEATURE_ISERIES))
+		iSeries_memcpy_fromio(dest, src, n);
+	else
+		eeh_memcpy_fromio(dest, src, n);
+}
+
+static inline void memcpy_toio(volatile void __iomem *dest, const void *src,
+                                 unsigned long n)
+{
+	if (firmware_has_feature(FW_FEATURE_ISERIES))
+		iSeries_memcpy_toio(dest, src, n);
+	else
+		eeh_memcpy_toio(dest, src, n);
+}
 
 /**
  *	check_signature		-	find BIOS signatures
@@ -417,7 +403,6 @@ static inline int check_signature(const 
 	const unsigned char *signature, int length)
 {
 	int retval = 0;
-#ifndef CONFIG_PPC_ISERIES 
 	do {
 		if (readb(io_addr) != *signature)
 			goto out;
@@ -427,7 +412,6 @@ #ifndef CONFIG_PPC_ISERIES 
 	} while (length);
 	retval = 1;
 out:
-#endif
 	return retval;
 }
 
diff --git a/include/asm-powerpc/iseries/iseries_io.h b/include/asm-powerpc/iseries/iseries_io.h
index f29009b..1b61c51 100644
--- a/include/asm-powerpc/iseries/iseries_io.h
+++ b/include/asm-powerpc/iseries/iseries_io.h
@@ -1,9 +1,5 @@
 #ifndef _ASM_POWERPC_ISERIES_ISERIES_IO_H
 #define _ASM_POWERPC_ISERIES_ISERIES_IO_H
-
-
-#ifdef CONFIG_PPC_ISERIES
-#include <linux/types.h>
 /*
  * Created by Allan Trautman on Thu Dec 28 2000.
  *
@@ -30,31 +26,12 @@ #include <linux/types.h>
  *   Created December 28, 2000
  * End Change Activity
  */
-
-#ifdef CONFIG_PCI
-extern u8   iSeries_Read_Byte(const volatile void __iomem * IoAddress);
-extern u16  iSeries_Read_Word(const volatile void __iomem * IoAddress);
-extern u32  iSeries_Read_Long(const volatile void __iomem * IoAddress);
-extern void iSeries_Write_Byte(u8  IoData, volatile void __iomem * IoAddress);
-extern void iSeries_Write_Word(u16 IoData, volatile void __iomem * IoAddress);
-extern void iSeries_Write_Long(u32 IoData, volatile void __iomem * IoAddress);
+#include <linux/types.h>
 
 extern void iSeries_memset_io(volatile void __iomem *dest, char x, size_t n);
-extern void iSeries_memcpy_toio(volatile void __iomem *dest, void *source,
+extern void iSeries_memcpy_toio(volatile void __iomem *dest, const void *source,
 		size_t n);
 extern void iSeries_memcpy_fromio(void *dest,
 		const volatile void __iomem *source, size_t n);
-#else
-static inline u8 iSeries_Read_Byte(const volatile void __iomem *IoAddress)
-{
-	return 0xff;
-}
-
-static inline void iSeries_Write_Byte(u8 IoData,
-		volatile void __iomem *IoAddress)
-{
-}
-#endif	/* CONFIG_PCI */
 
-#endif /* CONFIG_PPC_ISERIES */
 #endif /* _ASM_POWERPC_ISERIES_ISERIES_IO_H */
diff --git a/include/asm-powerpc/ppc-pci.h b/include/asm-powerpc/ppc-pci.h
index cf79bc7..db153ab 100644
--- a/include/asm-powerpc/ppc-pci.h
+++ b/include/asm-powerpc/ppc-pci.h
@@ -51,6 +51,17 @@ extern void pSeries_irq_bus_setup(struct
 
 extern unsigned long pci_probe_only;
 
+/* From platforms/iseries/pci.c */
+#if defined(CONFIG_PPC_ISERIES) && defined(CONFIG_PCI)
+extern int iseries_handle_io_fault(struct pt_regs *regs, unsigned long address);
+#else
+static inline int iseries_handle_io_fault(struct pt_regs *regs,
+		unsigned long address)
+{
+	return 0;
+}
+#endif
+
 /* ---- EEH internal-use-only related routines ---- */
 #ifdef CONFIG_EEH
 
-- 
1.4.1.1

^ permalink raw reply related

* Re: MAC driver issue
From: Alex Zeffertt @ 2006-09-06  8:25 UTC (permalink / raw)
  To: David H. Lynch Jr.; +Cc: linuxppc-embedded
In-Reply-To: <44FE69F2.40409@dlasys.net>

>> FWIW, in my experience the hardware independent parts of the 
>> networking stack are
>> very stable and the problem is almost always with the drivers, or with 
>> the IP
>> configuration (e.g. two interfaces on the same subnet).

>     I have no doubt it is with the driver.  I am somewhat fortunate in 
> this instance that I have a nearly identical setup - this is an FPGA 
> based system
>     I can swap the FPGA firmware, get an almost identical kernel with a 
> slightly different NIC, and everything works - same cables, same IP's,
>     Same switch, The only things different are the NIC and its driver. 
> Even the Linux kernels are identical - except the NIC driver.
>    
>     BUT so is the data received and passed on to the kernel (outside 
> random differences in the padding of the ARP packet)
>     One works the other doesn't.
> 

Well ethernet device drivers contain multiple arp supporting methods,
e.g. header_cache, header_cache_update, hard_header_parse, etc etc.
Generally driver writers don't need to concern themselves about these
as they are assigned to generic handlers by ether_setup().  However,
your problematic driver may do something different.

Given this problem appears to be driver specific rather than PPC
specific your best bet is to try and contact the author.  BTW, I don't
think you've said which driver you are using, a key piece of info....

Alex

^ permalink raw reply

* __init() and module_init() serialization of modules?
From: Parav Pandit @ 2006-09-06  8:43 UTC (permalink / raw)
  To: linuxppc-embedded

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

Hi,
   
  My doubt is more related to Linux kernel rather than PPC, but I feel that many will be interested to know this. Please help me to understand a silly doubt.
   
  When we have drivers as kernel modules, it can be loaded using insmod or modprobe.
  We use the depmod utility to generate the module dependancy and so concerned module gets loaded first.
  This is fine.
   
  Now when in comes to in-build module (part of kernel image), we obviously don't run the modprobe and/or depmod because they are no more modules.
   
  So how does - during compilation time, tool chain can identify which module to load first so that do_initcalls() function can take care if it.
   
  e.g. Module A and Module B.
  Both are inbuild, and A uses the functionality of B. So module_init() of B should get called first.
  How this is achieved for in-build modules?
  __init macro takes care of it for freeing that memory after those calls, but how the sequencing is done?
   
  Regards,
  Parav Pandit
   

 		
---------------------------------
Stay in the know. Pulse on the new Yahoo.com.  Check it out. 

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

^ permalink raw reply

* RE: PPC405 system slow boot
From: Clint Thomas @ 2006-09-06 13:28 UTC (permalink / raw)
  To: linuxppc-embedded

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

Actually, we figured out what was going on. Apparently we left one of
the interrupt lines disconnected from the CPU in the FPGA design. Thanks
for all the help guys, I really do appreciate it.

Clint Thomas

________________________________

From: Clint Thomas 
Sent: Friday, September 01, 2006 6:58 PM
To: linuxppc-embedded@ozlabs.org
Cc: 'Peter Ryser '; Jason Lamb
Subject: RE: PPC405 system slow boot


The xparameters.h file is generated by the Xilinx EDK for our FPGA, so I
don't see how there could be a mismatch. Using Chipscope, we were able
to find that the interrupt controller is triggered on kernel
initialization, but after the kernel has finished loading, the system
moves to a snail's pace at login. 
 
Does Linux use a different set of code to handle the UART, INTC, etc.
after the kernel is loaded? The system appears to work perfectly up
until after the kernel is done loading.

________________________________

From: Peter Ryser [mailto:peter.ryser@xilinx.com] 
Sent: Monday, August 28, 2006 8:45 PM
To: Clint Thomas
Cc: linuxppc-embedded@ozlabs.org
Subject: Re: PPC405 system slow boot


Clint,

check the interrupt sub-system of your design. What you describe
typically happens when the PPC does not get any interrupts from the
UART. It's most likely a mismatch between your hardware and the
xparameters.h.

- Peter


Clint Thomas wrote: 

	Hey guys,
	
	I've run through the loops to try and figure what could be wrong
with this system. The board in question is modeled after the Xilinx
ML300 board. It uses a Xilinx System ACE chip to load a FPGA / Kernel
image from compact flash. Originally, I was trying to use the
CompactFlash as the root file system, but because of issues in either
the design or software, this would only work if SysAce was in polled I/O
mode. To circumvent this, I built my root filesystem into an initrd
image and built a single ELF file with the Kernel and RFS, then strapped
that to the FPGA bit file to make a single FPGA/Kernel/RFS SysAce file.
	
	Upon decompression, the Linux kernel boots quickly and loads all
of the device drivers. However when it gets to the prompt, it starts
slowing down. Output and input to and from the board becomes very very
slow (it displays 2 characters roughly every 20 seconds). Originally I
believed this to be the CPU still polling SystemAce, so I disabled the
Linux System ACE drivers to remove that as a possibility, however after
doing this, the problem still persists, even with the RFS in ram! Has
anybody encountered a similar situation to this before, with possible
insight towards a solution? Thank you for your time.
	 
	Clinton Thomas
	cthomas@soneticom.com
	 
	
________________________________


	_______________________________________________
	Linuxppc-embedded mailing list
	Linuxppc-embedded@ozlabs.org
	https://ozlabs.org/mailman/listinfo/linuxppc-embedded


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

^ permalink raw reply

* Re: [2.6.19 PATCH 1/7] ehea: interface to network stack
From: Jan-Bernd Themann @ 2006-09-06 12:53 UTC (permalink / raw)
  To: Francois Romieu
  Cc: Jan-Bernd Themann, netdev, linux-kernel, Thomas Klein, linux-ppc,
	Christoph Raisch, Marcus Eder
In-Reply-To: <20060905185832.GA15311@electric-eye.fr.zoreil.com>

Hi,

ok, I admit this solution looks a bit nicer. We changed it in a similar way.

Jan-Bernd

On Tuesday 05 September 2006 20:58, Francois Romieu wrote:
> Thomas Klein <osstklei@de.ibm.com> :
> [...]
> > Somehow I don't get your point concerning the usage of 'k'. We need another
> > iterator as the for loops using 'k' use 'i' as their terminating condition.
> 
> Something like the code below perhaps (with more local variables maybe):
> 
> static int ehea_reg_interrupts(struct net_device *dev)
> {
> 	struct ehea_port *port = netdev_priv(dev);
> 	struct ehea_port_res *pr;
> 	int i, ret;
> 
> 	for (i = 0; i < port->num_def_qps; i++) {
> 		pr = &port->port_res[i];
> 		snprintf(pr->int_recv_name, EHEA_IRQ_NAME_SIZE - 1
> 			 , "%s-recv%d", dev->name, i);
> 		ret = ibmebus_request_irq(NULL, pr->recv_eq->attr.ist1,
> 					  ehea_recv_irq_handler, SA_INTERRUPT,
> 					  pr->int_recv_name, pr);
> 		if (ret) {
> 			ehea_error("failed registering irq for ehea_recv_int:"
> 				   "port_res_nr:%d, ist=%X", i,
> 				   pr->recv_eq->attr.ist1);
> 			goto err_free_irq_recv_eq_0;
> 		}
> 		if (netif_msg_ifup(port))
> 			ehea_info("irq_handle 0x%X for funct ehea_recv_int %d "
> 				  "registered", pr->recv_eq->attr.ist1, i);
> 	}
> 
> 	snprintf(port->int_aff_name, EHEA_IRQ_NAME_SIZE - 1,
> 		 "%s-aff", dev->name);
> 	ret = ibmebus_request_irq(NULL, port->qp_eq->attr.ist1,
> 				  ehea_qp_aff_irq_handler,
> 				  SA_INTERRUPT, port->int_aff_name, port);
> 	if (ret) {
> 		ehea_error("failed registering irq for qp_aff_irq_handler:"
> 			   " ist=%X", port->qp_eq->attr.ist1);
> 		goto err_free_irq_recv_eq_0;
> 	}
> 	if (netif_msg_ifup(port))
> 		ehea_info("irq_handle 0x%X for function qp_aff_irq_handler "
> 			  "registered", port->qp_eq->attr.ist1);
> 
> 	for (i = 0; i < port->num_def_qps + port->num_add_tx_qps; i++) {
> 		pr = &port->port_res[i];
> 		snprintf(pr->int_send_name, EHEA_IRQ_NAME_SIZE - 1,
> 			 "%s-send%d", dev->name, i);
> 		ret = ibmebus_request_irq(NULL, pr->send_eq->attr.ist1,
> 					  ehea_send_irq_handler, SA_INTERRUPT,
> 					  pr->int_send_name, pr);
> 		if (ret) {
> 			ehea_error("failed registering irq for ehea_send"
> 				   " port_res_nr:%d, ist=%X", i,
> 				   pr->send_eq->attr.ist1);
> 			goto err_free_irq_send_eq_1;
> 		}
> 		if (netif_msg_ifup(port))
> 			ehea_info("irq_handle 0x%X for function ehea_send_int "
> 				  "%d registered", pr->send_eq->attr.ist1, i);
> 	}
> out:
> 	return ret;
> 
> err_free_irq_send_eq_1:
> 	// Post-dec works with unsigned int too.
> 	while (i-- > 0) {
> 		u32 ist = port->port_res[i].send_eq->attr.ist1;
> 		ibmebus_free_irq(NULL, ist, &port->port_res[i]);
> 	}
> 	ibmebus_free_irq(NULL, port->qp_eq->attr.ist1, port);
> 	i = port->num_def_qps;
> err_free_irq_recv_eq_0:
> 	while (i-- > 0) {
> 		u32 ist = port->port_res[i].recv_eq->attr.ist1;
> 		ibmebus_free_irq(NULL, ist, &port->port_res[k]);
> 	}
> 	goto out;
> }
> 

^ permalink raw reply

* Re: [4/5] powerpc: PA Semi PWRficient platform support
From: Segher Boessenkool @ 2006-09-06 13:38 UTC (permalink / raw)
  To: Olof Johansson; +Cc: linuxppc-dev, paulus, anton
In-Reply-To: <20060905122956.2cebd36d@localhost.localdomain>

> +#define PA_PXP_CFA(bus, devfn, off) (((bus) << 20) | ((devfn) <<  
> 12) | (off))

> +	hose->cfg_data = ioremap(0xe0000000, 0x1000000);

Shouldn't that length be 0x10000000 then?  Or can't you have
more than 0x10 busses?

> +	bus_range = (int *) get_property(dev, "bus-range", &len);
> +	if (bus_range == NULL || len < 2 * sizeof(int)) {
> +		printk(KERN_WARNING "Can't get bus-range for %s, assume bus 0\n",
> +		dev->full_name);
> +	}

Just kill all this "bus-range" stuff?  Or do you really need it?

> +	np = of_find_node_by_type(np, "open-pic");
> +	if (!np) {
> +		printk(KERN_ERR "No interrupt controller in device tree.\n");
> +		return;
> +	}

"device_type" for the MPIC should be "interrupt-controller", "open-pic"
should be in the "compatible" property instead.

> +	/* Find address list in /platform-open-pic */

Erm, can't you get this info from the open-pic node itself?
I know maple does it like this but that should be fixed ;-)

> +	mpic = mpic_alloc(mpic_node, openpic_addr, MPIC_PRIMARY, 0, 0,
> +			  " PAS-OPIC  ");
> +	BUG_ON(mpic == NULL);
> +
> +	mpic_assign_isu(mpic, 0, openpic_addr + 0x10000);
> +	mpic_init(mpic);

Very nice, I'll convert maple to do the same (unless someone beats
me to it, heh).


Segher

^ permalink raw reply

* [PATCH] Make function of pm_power_off consistent with x86
From: Corey Minyard @ 2006-09-06 14:02 UTC (permalink / raw)
  To: linuxppc-dev; +Cc: jbarnett


Allow the pm_power_off function variable in PPC to work as an override.
This makes the function consistent with the other architectures and it
allows generic poweroff operations (like those provided in IPMI
systems) to work properly on PPC.

Signed-off-by: Corey Minyard <minyard@acm.org>
Cc: Joseph Barnett <jbarnett@motorola.com>

Index: linux-2.6.17/arch/ppc/kernel/setup.c
===================================================================
--- linux-2.6.17.orig/arch/ppc/kernel/setup.c
+++ linux-2.6.17/arch/ppc/kernel/setup.c
@@ -128,11 +128,8 @@ void machine_restart(char *cmd)
 	ppc_md.restart(cmd);
 }
 
-void machine_power_off(void)
+static void ppc_generic_power_off(void)
 {
-#ifdef CONFIG_NVRAM
-	nvram_sync();
-#endif
 	ppc_md.power_off();
 }
 
@@ -144,7 +141,17 @@ void machine_halt(void)
 	ppc_md.halt();
 }
 
-void (*pm_power_off)(void) = machine_power_off;
+void (*pm_power_off)(void) = ppc_generic_power_off;
+
+void machine_power_off(void)
+{
+#ifdef CONFIG_NVRAM
+	nvram_sync();
+#endif
+	if (pm_power_off)
+		pm_power_off();
+	ppc_generic_power_off();
+}
 
 #ifdef CONFIG_TAU
 extern u32 cpu_temp(unsigned long cpu);

^ permalink raw reply

* [2.6.19 PATCH 0/7] ehea: IBM eHEA Ethernet Device Driver
From: Jan-Bernd Themann @ 2006-09-06 13:29 UTC (permalink / raw)
  To: netdev
  Cc: Thomas Klein, Jan-Bernd Themann, linux-kernel, linux-ppc,
	Christoph Raisch, Marcus Eder

Hi,

this is the latest version of the IBM eHEA Ethernet Device Driver.
We integrated the comment we got from Francois Romieu.

Thanks,
Jan-Bernd

Signed-off-by: Jan-Bernd Themann <themann@de.ibm.com>=20
Changelog-by: =A0Jan-Bernd Themann <themann@de.ibm.com>

Differences to patch set http://www.spinics.net/lists/netdev/msg13972.html

Changelog:
=2D replace dev_alloc_skb by netdev_alloc_skb
=2D goto target naming rework
=2D minor coding style issues


 drivers/net/Kconfig             |    9=20
 drivers/net/Makefile            |    1=20
 drivers/net/ehea/Makefile       |    6=20
 drivers/net/ehea/ehea.h         |  443 ++++++
 drivers/net/ehea/ehea_ethtool.c |  294 ++++
 drivers/net/ehea/ehea_hcall.h   |   51=20
 drivers/net/ehea/ehea_hw.h      |  290 ++++
 drivers/net/ehea/ehea_main.c    | 2686 +++++++++++++++++++++++++++++++++++=
+++++
 drivers/net/ehea/ehea_phyp.c    |  705 ++++++++++
 drivers/net/ehea/ehea_phyp.h    |  454 ++++++
 drivers/net/ehea/ehea_qmr.c     |  604 ++++++++
 drivers/net/ehea/ehea_qmr.h     |  362 +++++
 12 files changed, 5905 insertions(+)

^ permalink raw reply

* [2.6.19 PATCH 1/7] ehea: interface to network stack
From: Jan-Bernd Themann @ 2006-09-06 13:30 UTC (permalink / raw)
  To: netdev
  Cc: Thomas Klein, Jan-Bernd Themann, linux-kernel, linux-ppc,
	Christoph Raisch, Marcus Eder

Signed-off-by: Jan-Bernd Themann <themann@de.ibm.com> 


 drivers/net/ehea/ehea_main.c | 2686 +++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 2686 insertions(+)



--- linux-2.6.18-rc6-orig/drivers/net/ehea/ehea_main.c	1970-01-01 01:00:00.000000000 +0100
+++ kernel/drivers/net/ehea/ehea_main.c	2006-09-06 15:53:42.000000000 +0200
@@ -0,0 +1,2686 @@
+/*
+ *  linux/drivers/net/ehea/ehea_main.c
+ *
+ *  eHEA ethernet device driver for IBM eServer System p
+ *
+ *  (C) Copyright IBM Corp. 2006
+ *
+ *  Authors:
+ *       Christoph Raisch <raisch@de.ibm.com>
+ *       Jan-Bernd Themann <themann@de.ibm.com>
+ *       Thomas Klein <tklein@de.ibm.com>
+ *
+ *
+ * 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, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.	 See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include <linux/in.h>
+#include <linux/ip.h>
+#include <linux/tcp.h>
+#include <linux/udp.h>
+#include <linux/if.h>
+#include <linux/list.h>
+#include <linux/if_ether.h>
+#include <net/ip.h>
+
+#include "ehea.h"
+#include "ehea_qmr.h"
+#include "ehea_phyp.h"
+
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Christoph Raisch <raisch@de.ibm.com>");
+MODULE_DESCRIPTION("IBM eServer HEA Driver");
+MODULE_VERSION(DRV_VERSION);
+
+
+static int msg_level = -1;
+static int rq1_entries = EHEA_DEF_ENTRIES_RQ1;
+static int rq2_entries = EHEA_DEF_ENTRIES_RQ2;
+static int rq3_entries = EHEA_DEF_ENTRIES_RQ3;
+static int sq_entries = EHEA_DEF_ENTRIES_SQ;
+
+module_param(msg_level, int, 0);
+module_param(rq1_entries, int, 0);
+module_param(rq2_entries, int, 0);
+module_param(rq3_entries, int, 0);
+module_param(sq_entries, int, 0);
+
+MODULE_PARM_DESC(msg_level, "msg_level");
+MODULE_PARM_DESC(rq3_entries, "Number of entries for Receive Queue 3 "
+		 "[2^x - 1], x = [6..14]. Default = "
+		 __MODULE_STRING(EHEA_DEF_ENTRIES_RQ3) ")");
+MODULE_PARM_DESC(rq2_entries, "Number of entries for Receive Queue 2 "
+		 "[2^x - 1], x = [6..14]. Default = "
+		 __MODULE_STRING(EHEA_DEF_ENTRIES_RQ2) ")");
+MODULE_PARM_DESC(rq1_entries, "Number of entries for Receive Queue 1 "
+		 "[2^x - 1], x = [6..14]. Default = "
+		 __MODULE_STRING(EHEA_DEF_ENTRIES_RQ1) ")");
+MODULE_PARM_DESC(sq_entries, " Number of entries for the Send Queue  "
+		 "[2^x - 1], x = [6..14]. Default = "
+		 __MODULE_STRING(EHEA_DEF_ENTRIES_SQ) ")");
+
+void ehea_dump(void *adr, int len, char *msg) {
+	int x;
+	unsigned char *deb = adr;
+	for (x = 0; x < len; x += 16) {
+		printk(DRV_NAME "%s adr=%p ofs=%04x %016lx %016lx\n", msg,
+			  deb, x, *((u64*)&deb[0]), *((u64*)&deb[8]));
+		deb += 16;
+	}
+}
+
+static struct net_device_stats *ehea_get_stats(struct net_device *dev)
+{
+	struct ehea_port *port = netdev_priv(dev);
+	struct net_device_stats *stats = &port->stats;
+	struct hcp_ehea_port_cb2 *cb2;
+	u64 hret, rx_packets;
+	int i;
+
+	memset(stats, 0, sizeof(*stats));
+
+	cb2 = kzalloc(H_CB_ALIGNMENT, GFP_KERNEL);
+	if (!cb2) {
+		ehea_error("no mem for cb2");
+		goto out;
+	}
+
+	hret = ehea_h_query_ehea_port(port->adapter->handle,
+				      port->logical_port_id,
+				      H_PORT_CB2, H_PORT_CB2_ALL, cb2);
+	if (hret != H_SUCCESS) {
+		ehea_error("query_ehea_port failed");
+		goto out_herr;
+	}
+
+	if (netif_msg_hw(port))
+		ehea_dump(cb2, sizeof(*cb2), "net_device_stats");
+
+	rx_packets = 0;
+	for (i = 0; i < port->num_def_qps; i++)
+		rx_packets += port->port_res[i].rx_packets;
+
+	stats->tx_packets = cb2->txucp + cb2->txmcp + cb2->txbcp;
+	stats->multicast = cb2->rxmcp;
+	stats->rx_errors = cb2->rxuerr;
+	stats->rx_bytes = cb2->rxo;
+	stats->tx_bytes = cb2->txo;
+	stats->rx_packets = rx_packets;
+
+out_herr:
+	kfree(cb2);
+out:
+	return stats;
+}
+
+static void ehea_refill_rq1(struct ehea_port_res *pr, int index, int nr_of_wqes)
+{
+	struct sk_buff **skb_arr_rq1 = pr->rq1_skba.arr;
+	struct net_device *dev = pr->port->netdev;
+	int max_index_mask = pr->rq1_skba.len - 1;
+	int i;
+
+	if (!nr_of_wqes)
+		return;
+
+	for (i = 0; i < nr_of_wqes; i++) {
+		if (!skb_arr_rq1[index]) {
+			skb_arr_rq1[index] = netdev_alloc_skb(dev,
+							      EHEA_L_PKT_SIZE);
+			if (!skb_arr_rq1[index]) {
+				ehea_error("%s: no mem for skb/%d wqes filled",
+					   dev->name, i);
+				break;
+			}
+		}
+		index--;
+		index &= max_index_mask;
+	}
+	/* Ring doorbell */
+	ehea_update_rq1a(pr->qp, i);
+}
+
+static int ehea_init_fill_rq1(struct ehea_port_res *pr, int nr_rq1a)
+{
+	int ret = 0;
+	struct sk_buff **skb_arr_rq1 = pr->rq1_skba.arr;
+	struct net_device *dev = pr->port->netdev;
+	int i;
+
+	for (i = 0; i < pr->rq1_skba.len; i++) {
+		skb_arr_rq1[i] = netdev_alloc_skb(dev, EHEA_L_PKT_SIZE);
+		if (!skb_arr_rq1[i]) {
+			ehea_error("%s: no mem for skb/%d wqes filled",
+				   dev->name, i);
+			ret = -ENOMEM;
+			goto out;
+		}
+	}
+	/* Ring doorbell */
+	ehea_update_rq1a(pr->qp, nr_rq1a);
+out:
+	return ret;
+}
+
+static int ehea_refill_rq_def(struct ehea_port_res *pr,
+			      struct ehea_q_skb_arr *q_skba, int rq_nr,
+			      int num_wqes, int wqe_type, int packet_size)
+{
+	struct net_device *dev = pr->port->netdev;
+	struct ehea_qp *qp = pr->qp;
+	struct sk_buff **skb_arr = q_skba->arr;
+	struct ehea_rwqe *rwqe;
+	int i, index, max_index_mask, fill_wqes;
+	int ret = 0;
+
+	fill_wqes = q_skba->os_skbs + num_wqes;
+
+	if (!fill_wqes)
+		return ret;
+
+	index = q_skba->index;
+	max_index_mask = q_skba->len - 1;
+	for (i = 0; i < fill_wqes; i++) {
+		struct sk_buff *skb = netdev_alloc_skb(dev, packet_size);
+		if (!skb) {
+			ehea_error("%s: no mem for skb/%d wqes filled",
+				   dev->name, i);
+			q_skba->os_skbs = fill_wqes - i;
+			ret = -ENOMEM;
+			break;
+		}
+		skb_reserve(skb, NET_IP_ALIGN);
+
+		skb_arr[index] = skb;
+
+		rwqe = ehea_get_next_rwqe(qp, rq_nr);
+		rwqe->wr_id = EHEA_BMASK_SET(EHEA_WR_ID_TYPE, wqe_type)
+		            | EHEA_BMASK_SET(EHEA_WR_ID_INDEX, index);
+		rwqe->sg_list[0].l_key = pr->recv_mr.lkey;
+		rwqe->sg_list[0].vaddr = (u64)skb->data;
+		rwqe->sg_list[0].len = packet_size;
+		rwqe->data_segments = 1;
+
+		index++;
+		index &= max_index_mask;
+	}
+	q_skba->index = index;
+
+	/* Ring doorbell */
+	iosync();
+	if (rq_nr == 2)
+		ehea_update_rq2a(pr->qp, i);
+	else
+		ehea_update_rq3a(pr->qp, i);
+
+	return ret;
+}
+
+
+static int ehea_refill_rq2(struct ehea_port_res *pr, int nr_of_wqes)
+{
+	return ehea_refill_rq_def(pr, &pr->rq2_skba, 2,
+				  nr_of_wqes, EHEA_RWQE2_TYPE,
+				  EHEA_RQ2_PKT_SIZE + NET_IP_ALIGN);
+}
+
+
+static int ehea_refill_rq3(struct ehea_port_res *pr, int nr_of_wqes)
+{
+	return ehea_refill_rq_def(pr, &pr->rq3_skba, 3,
+				  nr_of_wqes, EHEA_RWQE3_TYPE,
+				  EHEA_MAX_PACKET_SIZE + NET_IP_ALIGN);
+}
+
+static inline int ehea_check_cqe(struct ehea_cqe *cqe, int *rq_num)
+{
+	*rq_num = (cqe->type & EHEA_CQE_TYPE_RQ) >> 5;
+	if ((cqe->status & EHEA_CQE_STAT_ERR_MASK) == 0)
+		return 0;
+	if (((cqe->status & EHEA_CQE_STAT_ERR_TCP) != 0) &&
+	    (cqe->header_length == 0))
+		return 0;
+	return -EINVAL;
+}
+
+static inline void ehea_fill_skb(struct net_device *dev,
+				 struct sk_buff *skb, struct ehea_cqe *cqe)
+{
+	int length = cqe->num_bytes_transfered - 4;	/*remove CRC */
+
+	skb_put(skb, length);
+	skb->ip_summed = CHECKSUM_UNNECESSARY;
+	skb->protocol = eth_type_trans(skb, dev);
+}
+
+static inline struct sk_buff *get_skb_by_index(struct sk_buff **skb_array,
+					       int arr_len,
+					       struct ehea_cqe *cqe)
+{
+	int skb_index = EHEA_BMASK_GET(EHEA_WR_ID_INDEX, cqe->wr_id);
+	struct sk_buff *skb;
+	void *pref;
+	int x;
+
+	x = skb_index + 1;
+	x &= (arr_len - 1);
+
+	pref = skb_array[x];
+	prefetchw(pref);
+	prefetchw(pref + EHEA_CACHE_LINE);
+
+	pref = (skb_array[x]->data);
+	prefetch(pref);
+	prefetch(pref + EHEA_CACHE_LINE);
+	prefetch(pref + EHEA_CACHE_LINE * 2);
+	prefetch(pref + EHEA_CACHE_LINE * 3);
+	skb = skb_array[skb_index];
+	skb_array[skb_index] = NULL;
+	return skb;
+}
+
+static inline struct sk_buff *get_skb_by_index_ll(struct sk_buff **skb_array,
+						  int arr_len, int wqe_index)
+{
+	struct sk_buff *skb;
+	void *pref;
+	int x;
+
+	x = wqe_index + 1;
+	x &= (arr_len - 1);
+
+	pref = skb_array[x];
+	prefetchw(pref);
+	prefetchw(pref + EHEA_CACHE_LINE);
+
+	pref = (skb_array[x]->data);
+	prefetchw(pref);
+	prefetchw(pref + EHEA_CACHE_LINE);
+
+	skb = skb_array[wqe_index];
+	skb_array[wqe_index] = NULL;
+	return skb;
+}
+
+static int ehea_treat_poll_error(struct ehea_port_res *pr, int rq,
+				 struct ehea_cqe *cqe, int *processed_rq2,
+				 int *processed_rq3)
+{
+	struct sk_buff *skb;
+
+	if (netif_msg_rx_err(pr->port)) {
+		ehea_error("CQE Error for QP %d", pr->qp->init_attr.qp_nr);
+		ehea_dump(cqe, sizeof(*cqe), "CQE");
+	}
+
+	if (rq == 2) {
+		*processed_rq2 += 1;
+		skb = get_skb_by_index(pr->rq2_skba.arr, pr->rq2_skba.len, cqe);
+		dev_kfree_skb(skb);
+	} else if (rq == 3) {
+		*processed_rq3 += 1;
+		skb = get_skb_by_index(pr->rq3_skba.arr, pr->rq3_skba.len, cqe);
+		dev_kfree_skb(skb);
+	}
+
+	if (cqe->status & EHEA_CQE_STAT_FAT_ERR_MASK) {
+		ehea_error("Critical receive error. Resetting port.");
+		queue_work(pr->port->adapter->ehea_wq, &pr->port->reset_task);
+		return 1;
+	}
+
+	return 0;
+}
+
+static int ehea_poll(struct net_device *dev, int *budget)
+{
+	struct ehea_port *port = netdev_priv(dev);
+	struct ehea_port_res *pr = &port->port_res[0];
+	struct ehea_qp *qp = pr->qp;
+	struct ehea_cqe *cqe;
+	struct sk_buff *skb;
+	struct sk_buff **skb_arr_rq1 = pr->rq1_skba.arr;
+	struct sk_buff **skb_arr_rq2 = pr->rq2_skba.arr;
+	struct sk_buff **skb_arr_rq3 = pr->rq3_skba.arr;
+	int skb_arr_rq1_len = pr->rq1_skba.len;
+	int skb_arr_rq2_len = pr->rq2_skba.len;
+	int skb_arr_rq3_len = pr->rq3_skba.len;
+	int processed, processed_rq1, processed_rq2, processed_rq3;
+	int wqe_index, last_wqe_index, rq, intreq, my_quota, port_reset;
+
+	processed = processed_rq1 = processed_rq2 = processed_rq3 = 0;
+	last_wqe_index = 0;
+	my_quota = min(*budget, dev->quota);
+	my_quota = min(my_quota, EHEA_POLL_MAX_RWQE);
+
+	/* rq0 is low latency RQ */
+	cqe = ehea_poll_rq1(qp, &wqe_index);
+	while ((my_quota > 0) && cqe) {
+		ehea_inc_rq1(qp);
+		processed_rq1++;
+		processed++;
+		my_quota--;
+		if (netif_msg_rx_status(port))
+			ehea_dump(cqe, sizeof(*cqe), "CQE");
+
+		last_wqe_index = wqe_index;
+		rmb();
+		if (!ehea_check_cqe(cqe, &rq)) {
+			if (rq == 1) {	/* LL RQ1 */
+				skb = get_skb_by_index_ll(skb_arr_rq1,
+							  skb_arr_rq1_len,
+							  wqe_index);
+				if (unlikely(!skb)) {
+					if (netif_msg_rx_err(port))
+						ehea_error("LL rq1: skb=NULL");
+					skb = netdev_alloc_skb(dev,
+							       EHEA_L_PKT_SIZE);
+					if (!skb)
+						break;
+				}
+				memcpy(skb->data, ((char*)cqe) + 64,
+				       cqe->num_bytes_transfered - 4);
+				ehea_fill_skb(dev, skb, cqe);
+			} else if (rq == 2) {  /* RQ2 */
+				skb = get_skb_by_index(skb_arr_rq2,
+						       skb_arr_rq2_len, cqe);
+				if (unlikely(!skb)) {
+					if (netif_msg_rx_err(port))
+						ehea_error("rq2: skb=NULL");
+					break;
+				}
+				ehea_fill_skb(dev, skb, cqe);
+				processed_rq2++;
+			} else {  /* RQ3 */
+				skb = get_skb_by_index(skb_arr_rq3,
+						       skb_arr_rq3_len, cqe);
+				if (unlikely(!skb)) {
+					if (netif_msg_rx_err(port))
+						ehea_error("rq3: skb=NULL");
+					break;
+				}
+				ehea_fill_skb(dev, skb, cqe);
+				processed_rq3++;
+			}
+
+			if (cqe->status & EHEA_CQE_VLAN_TAG_XTRACT)
+				vlan_hwaccel_receive_skb(skb, port->vgrp,
+							 cqe->vlan_tag);
+			else
+				netif_receive_skb(skb);
+
+		} else { /* Error occured */
+			pr->p_state.poll_receive_errors++;
+			port_reset = ehea_treat_poll_error(pr, rq, cqe,
+							   &processed_rq2,
+							   &processed_rq3);
+			if (port_reset)
+				break;
+		}
+		cqe = ehea_poll_rq1(qp, &wqe_index);
+	}
+
+	dev->quota -= processed;
+	*budget -= processed;
+
+	pr->p_state.ehea_poll += 1;
+	pr->rx_packets += processed;
+
+	ehea_refill_rq1(pr, last_wqe_index, processed_rq1);
+	ehea_refill_rq2(pr, processed_rq2);
+	ehea_refill_rq3(pr, processed_rq3);
+
+	intreq = ((pr->p_state.ehea_poll & 0xF) == 0xF);
+
+	if (!cqe || intreq) {
+		netif_rx_complete(dev);
+		ehea_reset_cq_ep(pr->recv_cq);
+		ehea_reset_cq_n1(pr->recv_cq);
+		cqe = hw_qeit_get_valid(&qp->hw_rqueue1);
+		if (!cqe || intreq)
+			return 0;
+		if (!netif_rx_reschedule(dev, my_quota))
+			return 0;
+	}
+	return 1;
+}
+
+void free_sent_skbs(struct ehea_cqe *cqe, struct ehea_port_res *pr)
+{
+	struct sk_buff *skb;
+	int index, max_index_mask, i;
+
+	index = EHEA_BMASK_GET(EHEA_WR_ID_INDEX, cqe->wr_id);
+	max_index_mask = pr->sq_skba.len - 1;
+	for (i = 0; i < EHEA_BMASK_GET(EHEA_WR_ID_REFILL, cqe->wr_id); i++) {
+		skb = pr->sq_skba.arr[index];
+		if (likely(skb)) {
+			dev_kfree_skb(skb);
+			pr->sq_skba.arr[index] = NULL;
+		} else {
+			ehea_error("skb=NULL, wr_id=%lX, loop=%d, index=%d",
+				   cqe->wr_id, i, index);
+		}
+		index--;
+		index &= max_index_mask;
+	}
+}
+
+#define MAX_SENDCOMP_QUOTA 400
+void ehea_send_irq_tasklet(unsigned long data)
+{
+	struct ehea_port_res *pr = (struct ehea_port_res*)data;
+	struct ehea_cq *send_cq = pr->send_cq;
+	struct ehea_cqe *cqe;
+	int quota = MAX_SENDCOMP_QUOTA;
+	int cqe_counter = 0;
+	int swqe_av = 0;
+	unsigned long flags;
+
+	do {
+		cqe = ehea_poll_cq(send_cq);
+		if (!cqe) {
+			ehea_reset_cq_ep(send_cq);
+			ehea_reset_cq_n1(send_cq);
+			cqe = ehea_poll_cq(send_cq);
+			if (!cqe)
+				break;
+		}
+		cqe_counter++;
+		rmb();
+		if (cqe->status & EHEA_CQE_STAT_ERR_MASK) {
+			ehea_error("Send Completion Error: Resetting port");
+			if (netif_msg_tx_err(pr->port))
+				ehea_dump(cqe, sizeof(*cqe), "Send CQE");
+			queue_work(pr->port->adapter->ehea_wq,
+				   &pr->port->reset_task);
+			break;
+		}
+
+		if (netif_msg_tx_done(pr->port))
+			ehea_dump(cqe, sizeof(*cqe), "CQE");
+
+		if (likely(EHEA_BMASK_GET(EHEA_WR_ID_TYPE, cqe->wr_id)
+			   == EHEA_SWQE2_TYPE))
+			free_sent_skbs(cqe, pr);
+
+		swqe_av += EHEA_BMASK_GET(EHEA_WR_ID_REFILL, cqe->wr_id);
+		quota--;
+	} while (quota > 0);
+
+	ehea_update_feca(send_cq, cqe_counter);
+	atomic_add(swqe_av, &pr->swqe_avail);
+
+	spin_lock_irqsave(&pr->netif_queue, flags);
+	if (pr->queue_stopped && (atomic_read(&pr->swqe_avail)
+				  >= pr->swqe_refill_th)) {
+		netif_wake_queue(pr->port->netdev);
+		pr->queue_stopped = 0;
+	}
+	spin_unlock_irqrestore(&pr->netif_queue, flags);
+
+	if (unlikely(cqe))
+		tasklet_hi_schedule(&pr->send_comp_task);
+}
+
+static irqreturn_t ehea_send_irq_handler(int irq, void *param,
+					 struct pt_regs *regs)
+{
+	struct ehea_port_res *pr = param;
+	tasklet_hi_schedule(&pr->send_comp_task);
+	return IRQ_HANDLED;
+}
+
+static irqreturn_t ehea_recv_irq_handler(int irq, void *param, 
+					 struct pt_regs *regs)
+{
+	struct ehea_port_res *pr = param;
+	struct ehea_port *port = pr->port;
+	netif_rx_schedule(port->netdev);
+	return IRQ_HANDLED;
+}
+
+static irqreturn_t ehea_qp_aff_irq_handler(int irq, void *param, 
+					   struct pt_regs *regs)
+{
+	struct ehea_port *port = param;
+	struct ehea_eqe *eqe;
+	u32 qp_token;
+
+	eqe = ehea_poll_eq(port->qp_eq);
+	ehea_debug("eqe=%p", eqe);
+	while (eqe) {
+		ehea_debug("*eqe=%lx", *(u64*)eqe);
+		eqe = ehea_poll_eq(port->qp_eq);
+		qp_token = EHEA_BMASK_GET(EHEA_EQE_QP_TOKEN, eqe->entry);
+		ehea_debug("next eqe=%p", eqe);
+	}
+
+	return IRQ_HANDLED;
+}
+
+static struct ehea_port *ehea_get_port(struct ehea_adapter *adapter,
+				       int logical_port)
+{
+	int i;
+
+	for (i = 0; i < adapter->num_ports; i++)
+		if (adapter->port[i]->logical_port_id == logical_port)
+			return adapter->port[i];
+	return NULL;
+}
+
+int ehea_sense_port_attr(struct ehea_port *port)
+{
+	int ret;
+	u64 hret;
+	struct hcp_ehea_port_cb0 *cb0;
+
+	cb0 = kzalloc(H_CB_ALIGNMENT, GFP_KERNEL);
+	if (!cb0) {
+		ehea_error("no mem for cb0");
+		ret = -ENOMEM;
+		goto out;
+	}
+
+	hret = ehea_h_query_ehea_port(port->adapter->handle,
+				      port->logical_port_id, H_PORT_CB0,
+				      EHEA_BMASK_SET(H_PORT_CB0_ALL, 0xFFFF),
+				      cb0);
+	if (hret != H_SUCCESS) {
+		ret = -EIO;
+		goto out_free;
+	}
+
+	/* MAC address */
+	port->mac_addr = cb0->port_mac_addr << 16;
+
+	if (!is_valid_ether_addr((u8*)&port->mac_addr)) {
+		ret = -EADDRNOTAVAIL;
+		goto out_free;
+	}
+
+	/* Port speed */
+	switch (cb0->port_speed) {
+	case H_SPEED_10M_H:
+		port->port_speed = EHEA_SPEED_10M;
+		port->full_duplex = 0;
+		break;
+	case H_SPEED_10M_F:
+		port->port_speed = EHEA_SPEED_10M;
+		port->full_duplex = 1;
+		break;
+	case H_SPEED_100M_H:
+		port->port_speed = EHEA_SPEED_100M;
+		port->full_duplex = 0;
+		break;
+	case H_SPEED_100M_F:
+		port->port_speed = EHEA_SPEED_100M;
+		port->full_duplex = 1;
+		break;
+	case H_SPEED_1G_F:
+		port->port_speed = EHEA_SPEED_1G;
+		port->full_duplex = 1;
+		break;
+	case H_SPEED_10G_F:
+		port->port_speed = EHEA_SPEED_10G;
+		port->full_duplex = 1;
+		break;
+	default:
+		port->port_speed = 0;
+		port->full_duplex = 0;
+		break;
+	}
+
+	/* Number of default QPs */
+	port->num_def_qps = cb0->num_default_qps;
+
+	if (!port->num_def_qps) {
+		ret = -EINVAL;
+		goto out_free;
+	}
+
+	if (port->num_def_qps >= EHEA_NUM_TX_QP)
+		port->num_add_tx_qps = 0;
+	else
+		port->num_add_tx_qps = EHEA_NUM_TX_QP - port->num_def_qps;
+
+	ret = 0;
+out_free:
+	if (ret || netif_msg_probe(port))
+		ehea_dump(cb0, sizeof(*cb0), "ehea_sense_port_attr");
+	kfree(cb0);
+out:
+	return ret;
+}
+
+int ehea_set_portspeed(struct ehea_port *port, u32 port_speed)
+{
+	struct hcp_ehea_port_cb4 *cb4;
+	u64 hret;
+	int ret = 0;
+
+	cb4 = kzalloc(H_CB_ALIGNMENT, GFP_KERNEL);
+	if (!cb4) {
+		ehea_error("no mem for cb4");
+		ret = -ENOMEM;
+		goto out;
+	}
+
+	cb4->port_speed = port_speed;
+
+	netif_carrier_off(port->netdev);
+
+	hret = ehea_h_modify_ehea_port(port->adapter->handle,
+				       port->logical_port_id,
+				       H_PORT_CB4, H_PORT_CB4_SPEED, cb4);
+	if (hret == H_SUCCESS) {
+		port->autoneg = port_speed == EHEA_SPEED_AUTONEG ? 1 : 0;
+
+		hret = ehea_h_query_ehea_port(port->adapter->handle,
+					      port->logical_port_id,
+					      H_PORT_CB4, H_PORT_CB4_SPEED,
+					      cb4);
+		if (hret == H_SUCCESS) {
+			switch (cb4->port_speed) {
+			case H_SPEED_10M_H:
+				port->port_speed = EHEA_SPEED_10M;
+				port->full_duplex = 0;
+				break;
+			case H_SPEED_10M_F:
+				port->port_speed = EHEA_SPEED_10M;
+				port->full_duplex = 1;
+				break;
+			case H_SPEED_100M_H:
+				port->port_speed = EHEA_SPEED_100M;
+				port->full_duplex = 0;
+				break;
+			case H_SPEED_100M_F:
+				port->port_speed = EHEA_SPEED_100M;
+				port->full_duplex = 1;
+				break;
+			case H_SPEED_1G_F:
+				port->port_speed = EHEA_SPEED_1G;
+				port->full_duplex = 1;
+				break;
+			case H_SPEED_10G_F:
+				port->port_speed = EHEA_SPEED_10G;
+				port->full_duplex = 1;
+				break;
+			default:
+				port->port_speed = 0;
+				port->full_duplex = 0;
+				break;
+			}
+		} else {
+			ehea_error("Failed sensing port speed");
+			ret = -EIO;
+		}
+	} else {
+		if (hret == H_AUTHORITY) {
+			ehea_info("Hypervisor denied setting port speed. Either"
+				  " this partition is not authorized to set "
+				  "port speed or another partition has modified"
+				  " port speed first.");
+			ret = -EPERM;
+		} else {
+			ret = -EIO;
+			ehea_error("Failed setting port speed");
+		}
+	}
+	netif_carrier_on(port->netdev);
+	kfree(cb4);
+out:
+	return ret;
+}
+
+static void ehea_parse_eqe(struct ehea_adapter *adapter, u64 eqe)
+{
+	int ret;
+	u8 ec;
+	u8 portnum;
+	struct ehea_port *port;
+
+	ec = EHEA_BMASK_GET(NEQE_EVENT_CODE, eqe);
+
+	switch (ec) {
+	case EHEA_EC_PORTSTATE_CHG:	/* port state change */
+		portnum = EHEA_BMASK_GET(NEQE_PORTNUM, eqe);
+		port = ehea_get_port(adapter, portnum);
+
+		if (!port) {
+			ehea_error("unknown portnum %x", portnum);
+			break;
+		}
+
+		if (EHEA_BMASK_GET(NEQE_PORT_UP, eqe)) {
+			if (!netif_carrier_ok(port->netdev)) {
+				ret = ehea_sense_port_attr(
+					adapter->port[portnum]);
+				if (ret) {
+					ehea_error("failed resensing port "
+						   "attributes");
+					break;
+				}
+
+				if (netif_msg_link(port))
+					ehea_info("%s: Logical port up: %dMbps "
+						  "%s Duplex",
+						  port->netdev->name,
+						  port->port_speed,
+						  port->full_duplex ==
+						  1 ? "Full" : "Half");
+
+				netif_carrier_on(port->netdev);
+				netif_wake_queue(port->netdev);
+			}
+		} else
+			if (netif_carrier_ok(port->netdev)) {
+				if (netif_msg_link(port))
+					ehea_info("%s: Logical port down",
+						  port->netdev->name);
+				netif_carrier_off(port->netdev);
+				netif_stop_queue(port->netdev);
+			}
+
+		if (EHEA_BMASK_GET(NEQE_EXTSWITCH_PORT_UP, eqe)) {
+			if (netif_msg_link(port))
+				ehea_info("%s: Physical port up",
+					  port->netdev->name);
+		} else {
+			if (netif_msg_link(port))
+				ehea_info("%s: Physical port down",
+					  port->netdev->name);
+		}
+
+		if (EHEA_BMASK_GET(NEQE_EXTSWITCH_PRIMARY, eqe))
+			ehea_info("Externel switch port is primary port");
+		else
+			ehea_info("Externel switch port is backup port");
+
+		break;
+	case EHEA_EC_ADAPTER_MALFUNC:
+		ehea_error("Adapter malfunction");
+		break;
+	case EHEA_EC_PORT_MALFUNC:
+		ehea_info("Port malfunction");
+		break;
+	default:
+		ehea_error("unknown event code %x", ec);
+		break;
+	}
+}
+
+static void ehea_neq_tasklet(unsigned long data)
+{
+	struct ehea_adapter *adapter = (struct ehea_adapter*)data;
+	struct ehea_eqe *eqe;
+	u64 event_mask;
+
+	eqe = ehea_poll_eq(adapter->neq);
+	ehea_debug("eqe=%p", eqe);
+
+	while (eqe) {
+		ehea_debug("*eqe=%lx", eqe->entry);
+		ehea_parse_eqe(adapter, eqe->entry);
+		eqe = ehea_poll_eq(adapter->neq);
+		ehea_debug("next eqe=%p", eqe);
+	}
+
+	event_mask = EHEA_BMASK_SET(NELR_PORTSTATE_CHG, 1)
+		   | EHEA_BMASK_SET(NELR_ADAPTER_MALFUNC, 1)
+		   | EHEA_BMASK_SET(NELR_PORT_MALFUNC, 1);
+
+	ehea_h_reset_events(adapter->handle,
+			    adapter->neq->fw_handle, event_mask);
+}
+
+static irqreturn_t ehea_interrupt_neq(int irq, void *param, 
+				      struct pt_regs *regs)
+{
+	struct ehea_adapter *adapter = param;
+	tasklet_hi_schedule(&adapter->neq_tasklet);
+	return IRQ_HANDLED;
+}
+
+
+static int ehea_fill_port_res(struct ehea_port_res *pr)
+{
+	int ret;
+	struct ehea_qp_init_attr *init_attr = &pr->qp->init_attr;
+
+	ret = ehea_init_fill_rq1(pr, init_attr->act_nr_rwqes_rq1
+				     - init_attr->act_nr_rwqes_rq2
+				     - init_attr->act_nr_rwqes_rq3 - 1);
+
+	ret |= ehea_refill_rq2(pr, init_attr->act_nr_rwqes_rq2 - 1);
+
+	ret |= ehea_refill_rq3(pr, init_attr->act_nr_rwqes_rq3 - 1);
+
+	return ret;
+}
+
+static int ehea_reg_interrupts(struct net_device *dev)
+{
+	struct ehea_port *port = netdev_priv(dev);
+	struct ehea_port_res *pr;
+	int i, ret;
+
+	for (i = 0; i < port->num_def_qps; i++) {
+		pr = &port->port_res[i];
+		snprintf(pr->int_recv_name, EHEA_IRQ_NAME_SIZE - 1
+			 , "%s-recv%d", dev->name, i);
+		ret = ibmebus_request_irq(NULL, pr->recv_eq->attr.ist1,
+					  ehea_recv_irq_handler,
+					  SA_INTERRUPT, pr->int_recv_name, pr);
+		if (ret) {
+			ehea_error("failed registering irq for ehea_recv_int:"
+				   "port_res_nr:%d, ist=%X", i,
+				   pr->recv_eq->attr.ist1);
+			goto out_free_seq;
+		}
+		if (netif_msg_ifup(port))
+			ehea_info("irq_handle 0x%X for funct ehea_recv_int %d "
+				  "registered", pr->recv_eq->attr.ist1, i);
+	}
+
+	snprintf(port->int_aff_name, EHEA_IRQ_NAME_SIZE - 1, "%s-aff",
+		 dev->name);
+
+	ret = ibmebus_request_irq(NULL, port->qp_eq->attr.ist1,
+				  ehea_qp_aff_irq_handler,
+				  SA_INTERRUPT, port->int_aff_name, port);
+	if (ret) {
+		ehea_error("failed registering irq for qp_aff_irq_handler:"
+			   "ist=%X", port->qp_eq->attr.ist1);
+		goto out_free_qpeq;
+	}
+
+	if (netif_msg_ifup(port))
+		ehea_info("irq_handle 0x%X for function qp_aff_irq_handler "
+			  "registered", port->qp_eq->attr.ist1);
+
+	for (i = 0; i < port->num_def_qps + port->num_add_tx_qps; i++) {
+		pr = &port->port_res[i];
+		snprintf(pr->int_send_name, EHEA_IRQ_NAME_SIZE - 1,
+			 "%s-send%d", dev->name, i);
+		ret = ibmebus_request_irq(NULL, pr->send_eq->attr.ist1,
+					  ehea_send_irq_handler,
+					  SA_INTERRUPT, pr->int_send_name,
+					  pr);
+		if (ret) {
+			ehea_error("failed registering irq for ehea_send "
+				   "port_res_nr:%d, ist=%X", i,
+				   pr->send_eq->attr.ist1);
+			goto out_free_req;
+		}
+		if (netif_msg_ifup(port))
+			ehea_info("irq_handle 0x%X for function ehea_send_int "
+				  "%d registered", pr->send_eq->attr.ist1, i);
+	}
+out:
+	return ret;
+
+out_free_req:
+	while (--i >= 0) {
+		u32 ist = port->port_res[i].send_eq->attr.ist1;
+		ibmebus_free_irq(NULL, ist, &port->port_res[i]);
+	}
+out_free_qpeq:
+	ibmebus_free_irq(NULL, port->qp_eq->attr.ist1, port);
+	i = port->num_def_qps;
+out_free_seq:
+	while (--i >= 0) {
+		u32 ist = port->port_res[i].recv_eq->attr.ist1;
+		ibmebus_free_irq(NULL, ist, &port->port_res[i]);
+	}
+	goto out;
+}
+
+static void ehea_free_interrupts(struct net_device *dev)
+{
+	struct ehea_port *port = netdev_priv(dev);
+	struct ehea_port_res *pr;
+	int i;
+
+	/* send */
+	for (i = 0; i < port->num_def_qps + port->num_add_tx_qps; i++) {
+		pr = &port->port_res[i];
+		ibmebus_free_irq(NULL, pr->send_eq->attr.ist1, pr);
+		if (netif_msg_intr(port))
+			ehea_info("free send irq for res %d with handle 0x%X",
+				  i, pr->send_eq->attr.ist1);
+	}
+
+	/* receive */
+	for (i = 0; i < port->num_def_qps; i++) {
+		pr = &port->port_res[i];
+		ibmebus_free_irq(NULL, pr->recv_eq->attr.ist1, pr);
+		if (netif_msg_intr(port))
+			ehea_info("free recv irq for res %d with handle 0x%X",
+				  i, pr->recv_eq->attr.ist1);
+	}
+
+	/* associated events */
+	ibmebus_free_irq(NULL, port->qp_eq->attr.ist1, port);
+	if (netif_msg_intr(port))
+		ehea_info("associated event interrupt for handle 0x%X freed",
+			  port->qp_eq->attr.ist1);
+}
+
+static int ehea_configure_port(struct ehea_port *port)
+{
+	int ret, i;
+	u64 hret, mask;
+	struct hcp_ehea_port_cb0 *cb0;
+
+	ret = -ENOMEM;
+	cb0 = kzalloc(H_CB_ALIGNMENT, GFP_KERNEL);
+	if (!cb0)
+		goto out;
+
+	cb0->port_rc = EHEA_BMASK_SET(PXLY_RC_VALID, 1)
+		     | EHEA_BMASK_SET(PXLY_RC_IP_CHKSUM, 1)
+		     | EHEA_BMASK_SET(PXLY_RC_TCP_UDP_CHKSUM, 1)
+		     | EHEA_BMASK_SET(PXLY_RC_VLAN_XTRACT, 1)
+		     | EHEA_BMASK_SET(PXLY_RC_VLAN_TAG_FILTER,
+				      PXLY_RC_VLAN_FILTER)
+		     | EHEA_BMASK_SET(PXLY_RC_JUMBO_FRAME, 1);
+
+	for (i = 0; i < port->num_def_qps; i++)
+		cb0->default_qpn_arr[i] = port->port_res[i].qp->init_attr.qp_nr;
+
+	if (netif_msg_ifup(port))
+		ehea_dump(cb0, sizeof(*cb0), "ehea_configure_port");
+
+	mask = EHEA_BMASK_SET(H_PORT_CB0_PRC, 1)
+	     | EHEA_BMASK_SET(H_PORT_CB0_DEFQPNARRAY, 1);
+
+	hret = ehea_h_modify_ehea_port(port->adapter->handle,
+				       port->logical_port_id,
+				       H_PORT_CB0, mask, cb0);
+	ret = -EIO;
+	if (hret != H_SUCCESS)
+		goto out_free;
+
+	ret = 0;
+
+out_free:
+	kfree(cb0);
+out:
+	return ret;
+}
+
+static int ehea_gen_smrs(struct ehea_port_res *pr)
+{
+	u64 hret;
+	struct ehea_adapter *adapter = pr->port->adapter;
+
+	hret = ehea_h_register_smr(adapter->handle, adapter->mr.handle,
+				   adapter->mr.vaddr, EHEA_MR_ACC_CTRL,
+				   adapter->pd, &pr->send_mr);
+	if (hret != H_SUCCESS)
+		goto out;
+
+	hret = ehea_h_register_smr(adapter->handle, adapter->mr.handle,
+				   adapter->mr.vaddr, EHEA_MR_ACC_CTRL,
+				   adapter->pd, &pr->recv_mr);
+	if (hret != H_SUCCESS)
+		goto out_freeres;
+
+	return 0;
+
+out_freeres:
+	hret = ehea_h_free_resource(adapter->handle, pr->send_mr.handle);
+	if (hret != H_SUCCESS)
+		ehea_error("failed freeing SMR");
+out:
+	return -EIO;
+}
+
+static int ehea_rem_smrs(struct ehea_port_res *pr)
+{
+	struct ehea_adapter *adapter = pr->port->adapter;
+	int ret = 0;
+	u64 hret;
+
+	hret = ehea_h_free_resource(adapter->handle, pr->send_mr.handle);
+	if (hret != H_SUCCESS) {
+		ret = -EIO;
+		ehea_error("failed freeing send SMR for pr=%p", pr);
+	}
+
+	hret = ehea_h_free_resource(adapter->handle, pr->recv_mr.handle);
+	if (hret != H_SUCCESS) {
+		ret = -EIO;
+		ehea_error("failed freeing recv SMR for pr=%p", pr);
+	}
+
+	return ret;
+}
+
+static int ehea_init_q_skba(struct ehea_q_skb_arr *q_skba, int max_q_entries)
+{
+	int arr_size = sizeof(void*) * max_q_entries;
+
+	q_skba->arr = vmalloc(arr_size);
+	if (!q_skba->arr)
+		return -ENOMEM;
+
+	memset(q_skba->arr, 0, arr_size);
+
+	q_skba->len = max_q_entries;
+	q_skba->index = 0;
+	q_skba->os_skbs = 0;
+
+	return 0;
+}
+
+static int ehea_init_port_res(struct ehea_port *port, struct ehea_port_res *pr,
+			      struct port_res_cfg *pr_cfg, int queue_token)
+{
+	struct ehea_adapter *adapter = port->adapter;
+	enum ehea_eq_type eq_type = EHEA_EQ;
+	struct ehea_qp_init_attr *init_attr = NULL;
+	int ret = -EIO;
+
+	memset(pr, 0, sizeof(struct ehea_port_res));
+
+	pr->port = port;
+	spin_lock_init(&pr->send_lock);
+	spin_lock_init(&pr->recv_lock);
+	spin_lock_init(&pr->xmit_lock);
+	spin_lock_init(&pr->netif_queue);
+
+	pr->recv_eq = ehea_create_eq(adapter, eq_type, EHEA_MAX_ENTRIES_EQ, 0);
+	if (!pr->recv_eq) {
+		ehea_error("create_eq failed (recv_eq)");
+		goto out_free;
+	}
+
+	pr->send_eq = ehea_create_eq(adapter, eq_type, EHEA_MAX_ENTRIES_EQ, 0);
+	if (!pr->send_eq) {
+		ehea_error("create_eq failed (send_eq)");
+		goto out_free;
+	}
+
+	pr->recv_cq = ehea_create_cq(adapter, pr_cfg->max_entries_rcq,
+				     pr->recv_eq->fw_handle,
+				     port->logical_port_id);
+	if (!pr->recv_cq) {
+		ehea_error("create_cq failed (cq_recv)");
+		goto out_free;
+	}
+
+	pr->send_cq = ehea_create_cq(adapter, pr_cfg->max_entries_scq,
+				     pr->send_eq->fw_handle,
+				     port->logical_port_id);
+	if (!pr->send_cq) {
+		ehea_error("create_cq failed (cq_send)");
+		goto out_free;
+	}
+
+	if (netif_msg_ifup(port))
+		ehea_info("Send CQ: act_nr_cqes=%d, Recv CQ: act_nr_cqes=%d",
+			  pr->send_cq->attr.act_nr_of_cqes,
+			  pr->recv_cq->attr.act_nr_of_cqes);
+
+	init_attr = kzalloc(sizeof(*init_attr), GFP_KERNEL);
+	if (!init_attr) {
+		ret = -ENOMEM;
+		ehea_error("no mem for ehea_qp_init_attr");
+		goto out_free;
+	}
+
+	init_attr->low_lat_rq1 = 1;
+	init_attr->signalingtype = 1;	/* generate CQE if specified in WQE */
+	init_attr->rq_count = 3;
+	init_attr->qp_token = queue_token;
+	init_attr->max_nr_send_wqes = pr_cfg->max_entries_sq;
+	init_attr->max_nr_rwqes_rq1 = pr_cfg->max_entries_rq1;
+	init_attr->max_nr_rwqes_rq2 = pr_cfg->max_entries_rq2;
+	init_attr->max_nr_rwqes_rq3 = pr_cfg->max_entries_rq3;
+	init_attr->wqe_size_enc_sq = EHEA_SG_SQ;
+	init_attr->wqe_size_enc_rq1 = EHEA_SG_RQ1;
+	init_attr->wqe_size_enc_rq2 = EHEA_SG_RQ2;
+	init_attr->wqe_size_enc_rq3 = EHEA_SG_RQ3;
+	init_attr->rq2_threshold = EHEA_RQ2_THRESHOLD;
+	init_attr->rq3_threshold = EHEA_RQ3_THRESHOLD;
+	init_attr->port_nr = port->logical_port_id;
+	init_attr->send_cq_handle = pr->send_cq->fw_handle;
+	init_attr->recv_cq_handle = pr->recv_cq->fw_handle;
+	init_attr->aff_eq_handle = port->qp_eq->fw_handle;
+
+	pr->qp = ehea_create_qp(adapter, adapter->pd, init_attr);
+	if (!pr->qp) {
+		ehea_error("create_qp failed");
+		ret = -EIO;
+		goto out_free;
+	}
+
+	if (netif_msg_ifup(port))
+		ehea_info("QP: qp_nr=%d\n act_nr_snd_wqe=%d\n nr_rwqe_rq1=%d\n "
+			  "nr_rwqe_rq2=%d\n nr_rwqe_rq3=%d", init_attr->qp_nr,
+			  init_attr->act_nr_send_wqes,
+			  init_attr->act_nr_rwqes_rq1,
+			  init_attr->act_nr_rwqes_rq2,
+			  init_attr->act_nr_rwqes_rq3);
+
+	ret = ehea_init_q_skba(&pr->sq_skba, init_attr->act_nr_send_wqes + 1);
+	ret |= ehea_init_q_skba(&pr->rq1_skba, init_attr->act_nr_rwqes_rq1 + 1);
+	ret |= ehea_init_q_skba(&pr->rq2_skba, init_attr->act_nr_rwqes_rq2 + 1);
+	ret |= ehea_init_q_skba(&pr->rq3_skba, init_attr->act_nr_rwqes_rq3 + 1);
+	if (ret)
+		goto out_free;
+
+	pr->swqe_refill_th = init_attr->act_nr_send_wqes / 10;
+	if (ehea_gen_smrs(pr) != 0) {
+		ret = -EIO;
+		goto out_free;
+	}
+	tasklet_init(&pr->send_comp_task, ehea_send_irq_tasklet,
+		     (unsigned long)pr);
+	atomic_set(&pr->swqe_avail, init_attr->act_nr_send_wqes - 1);
+
+	kfree(init_attr);
+	ret = 0;
+	goto out;
+
+out_free:
+	kfree(init_attr);
+	vfree(pr->sq_skba.arr);
+	vfree(pr->rq1_skba.arr);
+	vfree(pr->rq2_skba.arr);
+	vfree(pr->rq3_skba.arr);
+	ehea_destroy_qp(pr->qp);
+	ehea_destroy_cq(pr->send_cq);
+	ehea_destroy_cq(pr->recv_cq);
+	ehea_destroy_eq(pr->send_eq);
+	ehea_destroy_eq(pr->recv_eq);
+out:
+	return ret;
+}
+
+static int ehea_clean_portres(struct ehea_port *port, struct ehea_port_res *pr)
+{
+	int ret, i;
+
+	ret = ehea_destroy_qp(pr->qp);
+
+	if (!ret) {
+		ehea_destroy_cq(pr->send_cq);
+		ehea_destroy_cq(pr->recv_cq);
+		ehea_destroy_eq(pr->send_eq);
+		ehea_destroy_eq(pr->recv_eq);
+
+		for (i = 0; i < pr->rq1_skba.len; i++)
+			if (pr->rq1_skba.arr[i])
+				dev_kfree_skb(pr->rq1_skba.arr[i]);
+
+		for (i = 0; i < pr->rq2_skba.len; i++)
+			if (pr->rq2_skba.arr[i])
+				dev_kfree_skb(pr->rq2_skba.arr[i]);
+
+		for (i = 0; i < pr->rq3_skba.len; i++)
+			if (pr->rq3_skba.arr[i])
+				dev_kfree_skb(pr->rq3_skba.arr[i]);
+
+		for (i = 0; i < pr->sq_skba.len; i++)
+			if (pr->sq_skba.arr[i])
+				dev_kfree_skb(pr->sq_skba.arr[i]);
+
+		vfree(pr->rq1_skba.arr);
+		vfree(pr->rq2_skba.arr);
+		vfree(pr->rq3_skba.arr);
+		vfree(pr->sq_skba.arr);
+		ret = ehea_rem_smrs(pr);
+	}
+	return ret;
+}
+
+/*
+ * The write_* functions store information in swqe which is used by
+ * the hardware to calculate the ip/tcp/udp checksum
+ */
+
+static inline void write_ip_start_end(struct ehea_swqe *swqe,
+				      const struct sk_buff *skb)
+{
+	swqe->ip_start = (u8)(((u64)skb->nh.iph) - ((u64)skb->data));
+	swqe->ip_end = (u8)(swqe->ip_start + skb->nh.iph->ihl * 4 - 1);
+}
+
+static inline void write_tcp_offset_end(struct ehea_swqe *swqe,
+					const struct sk_buff *skb)
+{
+	swqe->tcp_offset =
+		(u8)(swqe->ip_end + 1 + offsetof(struct tcphdr, check));
+
+	swqe->tcp_end = (u16)skb->len - 1;
+}
+
+static inline void write_udp_offset_end(struct ehea_swqe *swqe,
+					const struct sk_buff *skb)
+{
+	swqe->tcp_offset =
+		(u8)(swqe->ip_end + 1 + offsetof(struct udphdr, check));
+
+	swqe->tcp_end = (u16)skb->len - 1;
+}
+
+
+static void write_swqe2_TSO(struct sk_buff *skb,
+			    struct ehea_swqe *swqe, u32 lkey)
+{
+	struct ehea_vsgentry *sg1entry = &swqe->u.immdata_desc.sg_entry;
+	u8 *imm_data = &swqe->u.immdata_desc.immediate_data[0];
+	int skb_data_size = skb->len - skb->data_len;
+	int headersize;
+	u64 tmp_addr;
+
+	/* Packet is TCP with TSO enabled */
+	swqe->tx_control |= EHEA_SWQE_TSO;
+	swqe->mss = skb_shinfo(skb)->gso_size;
+	/* copy only eth/ip/tcp headers to immediate data and
+	 * the rest of skb->data to sg1entry
+	 */
+	headersize = ETH_HLEN + (skb->nh.iph->ihl * 4) + (skb->h.th->doff * 4);
+
+	skb_data_size = skb->len - skb->data_len;
+
+	if (skb_data_size >= headersize) {
+		/* copy immediate data */
+		memcpy(imm_data, skb->data, headersize);
+		swqe->immediate_data_length = headersize;
+
+		if (skb_data_size > headersize) {
+			/* set sg1entry data */
+			sg1entry->l_key = lkey;
+			sg1entry->len = skb_data_size - headersize;
+
+			tmp_addr = (u64)(skb->data + headersize);
+			sg1entry->vaddr = tmp_addr;
+			swqe->descriptors++;
+		}
+	} else
+		ehea_error("cannot handle fragmented headers");
+}
+
+static void write_swqe2_nonTSO(struct sk_buff *skb,
+			       struct ehea_swqe *swqe, u32 lkey)
+{
+	int skb_data_size = skb->len - skb->data_len;
+	u8 *imm_data = &swqe->u.immdata_desc.immediate_data[0];
+	struct ehea_vsgentry *sg1entry = &swqe->u.immdata_desc.sg_entry;
+	u64 tmp_addr;
+
+	/* Packet is any nonTSO type
+	 *
+	 * Copy as much as possible skb->data to immediate data and
+	 * the rest to sg1entry
+	 */
+	if (skb_data_size >= SWQE2_MAX_IMM) {
+		/* copy immediate data */
+		memcpy(imm_data, skb->data, SWQE2_MAX_IMM);
+
+		swqe->immediate_data_length = SWQE2_MAX_IMM;
+
+		if (skb_data_size > SWQE2_MAX_IMM) {
+			/* copy sg1entry data */
+			sg1entry->l_key = lkey;
+			sg1entry->len = skb_data_size - SWQE2_MAX_IMM;
+			tmp_addr = (u64)(skb->data + SWQE2_MAX_IMM);
+			sg1entry->vaddr = tmp_addr;
+			swqe->descriptors++;
+		}
+	} else {
+		memcpy(imm_data, skb->data, skb_data_size);
+		swqe->immediate_data_length = skb_data_size;
+	}
+}
+
+static inline void write_swqe2_data(struct sk_buff *skb, struct net_device *dev,
+				    struct ehea_swqe *swqe, u32 lkey)
+{
+	struct ehea_vsgentry *sg_list, *sg1entry, *sgentry;
+	skb_frag_t *frag;
+	int nfrags, sg1entry_contains_frag_data, i;
+	u64 tmp_addr;
+
+	nfrags = skb_shinfo(skb)->nr_frags;
+	sg1entry = &swqe->u.immdata_desc.sg_entry;
+	sg_list = (struct ehea_vsgentry*)&swqe->u.immdata_desc.sg_list;
+	swqe->descriptors = 0;
+	sg1entry_contains_frag_data = 0;
+
+	if ((dev->features & NETIF_F_TSO) && skb_shinfo(skb)->gso_size)
+		write_swqe2_TSO(skb, swqe, lkey);
+	else
+		write_swqe2_nonTSO(skb, swqe, lkey);
+
+	/* write descriptors */
+	if (nfrags > 0) {
+		if (swqe->descriptors == 0) {
+			/* sg1entry not yet used */
+			frag = &skb_shinfo(skb)->frags[0];
+
+			/* copy sg1entry data */
+			sg1entry->l_key = lkey;
+			sg1entry->len = frag->size;
+			tmp_addr =  (u64)(page_address(frag->page)
+					  + frag->page_offset);
+			sg1entry->vaddr = tmp_addr;
+			swqe->descriptors++;
+			sg1entry_contains_frag_data = 1;
+		}
+
+		for (i = sg1entry_contains_frag_data; i < nfrags; i++) {
+
+			frag = &skb_shinfo(skb)->frags[i];
+			sgentry = &sg_list[i - sg1entry_contains_frag_data];
+
+			sgentry->l_key = lkey;
+			sgentry->len = frag->size;
+
+			tmp_addr = (u64)(page_address(frag->page)
+					 + frag->page_offset);
+			sgentry->vaddr = tmp_addr;
+			swqe->descriptors++;
+		}
+	}
+}
+
+static int ehea_broadcast_reg_helper(struct ehea_port *port, u32 hcallid)
+{
+	int ret = 0;
+	u64 hret;
+	u8 reg_type;
+
+	/* De/Register untagged packets */
+	reg_type = EHEA_BCMC_BROADCAST | EHEA_BCMC_UNTAGGED;
+	hret = ehea_h_reg_dereg_bcmc(port->adapter->handle,
+				     port->logical_port_id,
+				     reg_type, port->mac_addr, 0, hcallid);
+	if (hret != H_SUCCESS) {
+		ehea_error("reg_dereg_bcmc failed (tagged)");
+		ret = -EIO;
+		goto out_herr;
+	}
+
+	/* De/Register VLAN packets */
+	reg_type = EHEA_BCMC_BROADCAST | EHEA_BCMC_VLANID_ALL;
+	hret = ehea_h_reg_dereg_bcmc(port->adapter->handle,
+				     port->logical_port_id,
+				     reg_type, port->mac_addr, 0, hcallid);
+	if (hret != H_SUCCESS) {
+		ehea_error("reg_dereg_bcmc failed (vlan)");
+		ret = -EIO;
+	}
+out_herr:
+	return ret;
+}
+
+static int ehea_set_mac_addr(struct net_device *dev, void *sa)
+{
+	struct ehea_port *port = netdev_priv(dev);
+	struct sockaddr *mac_addr = sa;
+	struct hcp_ehea_port_cb0 *cb0;
+	int ret;
+	u64 hret;
+
+	if (!is_valid_ether_addr(mac_addr->sa_data)) {
+		ret = -EADDRNOTAVAIL;
+		goto out;
+	}
+
+	cb0 = kzalloc(H_CB_ALIGNMENT, GFP_KERNEL);
+	if (!cb0) {
+		ehea_error("no mem for cb0");
+		ret = -ENOMEM;
+		goto out;
+	}
+
+	memcpy(&(cb0->port_mac_addr), &(mac_addr->sa_data[0]), ETH_ALEN);
+
+	cb0->port_mac_addr = cb0->port_mac_addr >> 16;
+
+	hret = ehea_h_modify_ehea_port(port->adapter->handle,
+				       port->logical_port_id, H_PORT_CB0,
+				       EHEA_BMASK_SET(H_PORT_CB0_MAC, 1), cb0);
+	if (hret != H_SUCCESS) {
+		ret = -EIO;
+		goto out_free;
+	}
+
+	memcpy(dev->dev_addr, mac_addr->sa_data, dev->addr_len);
+
+	/* Deregister old MAC in pHYP */
+	ret = ehea_broadcast_reg_helper(port, H_DEREG_BCMC);
+	if (ret)
+		goto out_free;
+
+	port->mac_addr = cb0->port_mac_addr << 16;
+
+	/* Register new MAC in pHYP */
+	ret = ehea_broadcast_reg_helper(port, H_REG_BCMC);
+	if (ret)
+		goto out_free;
+
+	ret = 0;
+out_free:
+	kfree(cb0);
+out:
+	return ret;
+}
+
+static void ehea_promiscuous_error(u64 hret, int enable)
+{
+	ehea_info("Hypervisor denied %sabling promiscuous mode.%s",
+		  enable == 1 ? "en" : "dis",
+		  hret != H_AUTHORITY ? "" : " Another partition owning a "
+		  "logical port on the same physical port might have altered "
+		  "promiscuous mode first.");
+}
+
+static void ehea_promiscuous(struct net_device *dev, int enable)
+{
+	struct ehea_port *port = netdev_priv(dev);
+	struct hcp_ehea_port_cb5 *cb5;
+	struct hcp_ehea_port_cb7 *cb7;
+	u64 hret;
+
+	if ((enable && port->promisc) || (!enable && !port->promisc))
+		return;
+
+	cb5 = kzalloc(H_CB_ALIGNMENT, GFP_KERNEL);
+	cb7 = kzalloc(H_CB_ALIGNMENT, GFP_KERNEL);
+	if (!cb5 || !cb7) {
+		ehea_error("no mem for cb5/cb7");
+		goto out;
+	}
+
+	/* Modify Pxs_RCU in CB5 */
+	hret = ehea_h_query_ehea_port(port->adapter->handle,
+				      port->logical_port_id,
+				      H_PORT_CB5, H_PORT_CB5_RCU, cb5);
+	if (hret) {
+		ehea_promiscuous_error(hret, enable);
+		goto out;
+	}
+
+	if (enable)
+		cb5->rcu = EHEA_BMASK_SET(PXS_RCU, 0xFFFF);
+	else
+		cb5->rcu = EHEA_BMASK_SET(PXS_RCU, 0);
+
+	hret = ehea_h_modify_ehea_port(port->adapter->handle,
+				       port->logical_port_id,
+				       H_PORT_CB5, H_PORT_CB5_RCU, cb5);
+	if (hret) {
+		ehea_promiscuous_error(hret, enable);
+		goto out;
+	}
+
+	/* Modify Pxs_DUCQPN in CB7 */
+	cb7->def_uc_qpn = enable == 1 ? port->port_res[0].qp->fw_handle : 0;
+
+	hret = ehea_h_modify_ehea_port(port->adapter->handle,
+				       port->logical_port_id,
+				       H_PORT_CB7, H_PORT_CB7_DUCQPN, cb7);
+	if (hret) {
+		/* Rollback */
+		if (enable)
+			cb5->rcu = EHEA_BMASK_SET(PXS_RCU, 0);
+		else
+			cb5->rcu = EHEA_BMASK_SET(PXS_RCU, 0xFFFF);
+
+		ehea_h_modify_ehea_port(port->adapter->handle,
+					port->logical_port_id,
+					H_PORT_CB5, H_PORT_CB5_RCU, cb5);
+
+		ehea_promiscuous_error(hret, enable);
+		goto out;
+	}
+
+	port->promisc = enable;
+out:
+	kfree(cb5);
+	kfree(cb7);
+	return;
+}
+
+static u64 ehea_multicast_reg_helper(struct ehea_port *port, u64 mc_mac_addr,
+				     u32 hcallid)
+{
+	u64 hret;
+	u8 reg_type;
+
+	reg_type = EHEA_BCMC_SCOPE_ALL | EHEA_BCMC_MULTICAST
+		 | EHEA_BCMC_UNTAGGED;
+
+	hret = ehea_h_reg_dereg_bcmc(port->adapter->handle,
+				     port->logical_port_id,
+				     reg_type, mc_mac_addr, 0, hcallid);
+	if (hret)
+		goto out;
+
+	reg_type = EHEA_BCMC_SCOPE_ALL | EHEA_BCMC_MULTICAST
+		 | EHEA_BCMC_VLANID_ALL;
+
+	hret = ehea_h_reg_dereg_bcmc(port->adapter->handle,
+				     port->logical_port_id,
+				     reg_type, mc_mac_addr, 0, hcallid);
+out:
+	return hret;
+}
+
+static int ehea_drop_multicast_list(struct net_device *dev)
+{
+	struct ehea_port *port = netdev_priv(dev);
+	struct ehea_mc_list *mc_entry = port->mc_list;
+	struct list_head *pos;
+	struct list_head *temp;
+	int ret = 0;
+	u64 hret;
+
+	list_for_each_safe(pos, temp, &(port->mc_list->list)) {
+		mc_entry = list_entry(pos, struct ehea_mc_list, list);
+
+		hret = ehea_multicast_reg_helper(port, mc_entry->macaddr,
+						 H_DEREG_BCMC);
+		if (hret) {
+			ehea_error("failed deregistering mcast MAC");
+			ret = -EIO;
+		}
+
+		list_del(pos);
+		kfree(mc_entry);
+	}
+	return ret;
+}
+
+static void ehea_allmulti(struct net_device *dev, int enable)
+{
+	struct ehea_port *port = netdev_priv(dev);
+	u64 hret;
+
+	if (!port->allmulti) {
+		if (enable) {
+			/* Enable ALLMULTI */
+			ehea_drop_multicast_list(dev);
+			hret = ehea_multicast_reg_helper(port, 0, H_REG_BCMC);
+			if (!hret)
+				port->allmulti = 1;
+			else
+				ehea_error("failed enabling IFF_ALLMULTI");
+		}
+	} else
+		if (!enable) {
+			/* Disable ALLMULTI */
+			hret = ehea_multicast_reg_helper(port, 0, H_DEREG_BCMC);
+			if (!hret)
+				port->allmulti = 0;
+			else
+				ehea_error("failed disabling IFF_ALLMULTI");
+		}
+}
+
+static void ehea_add_multicast_entry(struct ehea_port* port, u8* mc_mac_addr)
+{
+	struct ehea_mc_list *ehea_mcl_entry;
+	u64 hret;
+
+	ehea_mcl_entry = kzalloc(sizeof(*ehea_mcl_entry), GFP_KERNEL);
+	if (!ehea_mcl_entry) {
+		ehea_error("no mem for mcl_entry");
+		return;
+	}
+
+	INIT_LIST_HEAD(&ehea_mcl_entry->list);
+
+	memcpy(&ehea_mcl_entry->macaddr, mc_mac_addr, ETH_ALEN);
+
+	hret = ehea_multicast_reg_helper(port, ehea_mcl_entry->macaddr,
+					 H_REG_BCMC);
+	if (!hret)
+		list_add(&ehea_mcl_entry->list, &port->mc_list->list);
+	else {
+		ehea_error("failed registering mcast MAC");
+		kfree(ehea_mcl_entry);
+	}
+}
+
+static void ehea_set_multicast_list(struct net_device *dev)
+{
+	struct ehea_port *port = netdev_priv(dev);
+	struct dev_mc_list *k_mcl_entry;
+	int ret, i;
+
+	if (dev->flags & IFF_PROMISC) {
+		ehea_promiscuous(dev, 1);
+		return;
+	}
+	ehea_promiscuous(dev, 0);
+
+	if (dev->flags & IFF_ALLMULTI) {
+		ehea_allmulti(dev, 1);
+		return;
+	}
+	ehea_allmulti(dev, 0);
+
+	if (dev->mc_count) {
+		ret = ehea_drop_multicast_list(dev);
+		if (ret) {
+			/* Dropping the current multicast list failed.
+			 * Enabling ALL_MULTI is the best we can do.
+			 */
+			ehea_allmulti(dev, 1);
+		}
+
+		if (dev->mc_count > port->adapter->max_mc_mac) {
+			ehea_info("Mcast registration limit reached (0x%lx). "
+				  "Use ALLMULTI!",
+				  port->adapter->max_mc_mac);
+			goto out;
+		}
+
+		for (i = 0, k_mcl_entry = dev->mc_list;
+		     i < dev->mc_count;
+		     i++, k_mcl_entry = k_mcl_entry->next) {
+			ehea_add_multicast_entry(port, k_mcl_entry->dmi_addr);
+		}
+	}
+out:
+	return;
+}
+
+static int ehea_change_mtu(struct net_device *dev, int new_mtu)
+{
+	if ((new_mtu < 68) || (new_mtu > EHEA_MAX_PACKET_SIZE))
+		return -EINVAL;
+	dev->mtu = new_mtu;
+	return 0;
+}
+
+static void ehea_xmit2(struct sk_buff *skb, struct net_device *dev,
+		       struct ehea_swqe *swqe, u32 lkey)
+{
+	if (skb->protocol == htons(ETH_P_IP)) {
+		/* IPv4 */
+		swqe->tx_control |= EHEA_SWQE_CRC
+				 | EHEA_SWQE_IP_CHECKSUM
+				 | EHEA_SWQE_TCP_CHECKSUM
+				 | EHEA_SWQE_IMM_DATA_PRESENT
+				 | EHEA_SWQE_DESCRIPTORS_PRESENT;
+
+		write_ip_start_end(swqe, skb);
+
+		if (skb->nh.iph->protocol == IPPROTO_UDP) {
+			if ((skb->nh.iph->frag_off & IP_MF) ||
+			    (skb->nh.iph->frag_off & IP_OFFSET))
+				/* IP fragment, so don't change cs */
+				swqe->tx_control &= ~EHEA_SWQE_TCP_CHECKSUM;
+			else
+				write_udp_offset_end(swqe, skb);
+
+		} else if (skb->nh.iph->protocol == IPPROTO_TCP) {
+			write_tcp_offset_end(swqe, skb);
+		}
+
+		/* icmp (big data) and ip segmentation packets (all other ip
+		   packets) do not require any special handling */
+
+	} else {
+		/* Other Ethernet Protocol */
+		swqe->tx_control |= EHEA_SWQE_CRC
+				 | EHEA_SWQE_IMM_DATA_PRESENT
+				 | EHEA_SWQE_DESCRIPTORS_PRESENT;
+	}
+
+	write_swqe2_data(skb, dev, swqe, lkey);
+}
+
+static void ehea_xmit3(struct sk_buff *skb, struct net_device *dev,
+		       struct ehea_swqe *swqe)
+{
+	int nfrags = skb_shinfo(skb)->nr_frags;
+	u8 *imm_data = &swqe->u.immdata_nodesc.immediate_data[0];
+	skb_frag_t *frag;
+	int i;
+
+	if (skb->protocol == htons(ETH_P_IP)) {
+		/* IPv4 */
+		write_ip_start_end(swqe, skb);
+
+		if (skb->nh.iph->protocol == IPPROTO_TCP) {
+			swqe->tx_control |= EHEA_SWQE_CRC
+					 | EHEA_SWQE_IP_CHECKSUM
+					 | EHEA_SWQE_TCP_CHECKSUM
+					 | EHEA_SWQE_IMM_DATA_PRESENT;
+
+			write_tcp_offset_end(swqe, skb);
+
+		} else if (skb->nh.iph->protocol == IPPROTO_UDP) {
+			if ((skb->nh.iph->frag_off & IP_MF) ||
+			    (skb->nh.iph->frag_off & IP_OFFSET))
+				/* IP fragment, so don't change cs */
+				swqe->tx_control |= EHEA_SWQE_CRC
+						 | EHEA_SWQE_IMM_DATA_PRESENT;
+			else {
+				swqe->tx_control |= EHEA_SWQE_CRC
+						 | EHEA_SWQE_IP_CHECKSUM
+						 | EHEA_SWQE_TCP_CHECKSUM
+						 | EHEA_SWQE_IMM_DATA_PRESENT;
+
+				write_udp_offset_end(swqe, skb);
+			}
+		} else {
+			/* icmp (big data) and
+			   ip segmentation packets (all other ip packets) */
+			swqe->tx_control |= EHEA_SWQE_CRC
+					 | EHEA_SWQE_IP_CHECKSUM
+					 | EHEA_SWQE_IMM_DATA_PRESENT;
+		}
+	} else {
+		/* Other Ethernet Protocol */
+		swqe->tx_control |= EHEA_SWQE_CRC | EHEA_SWQE_IMM_DATA_PRESENT;
+	}
+	/* copy (immediate) data */
+	if (nfrags == 0) {
+		/* data is in a single piece */
+		memcpy(imm_data, skb->data, skb->len);
+	} else {
+		/* first copy data from the skb->data buffer ... */
+		memcpy(imm_data, skb->data, skb->len - skb->data_len);
+		imm_data += skb->len - skb->data_len;
+
+		/* ... then copy data from the fragments */
+		for (i = 0; i < nfrags; i++) {
+			frag = &skb_shinfo(skb)->frags[i];
+			memcpy(imm_data,
+			       page_address(frag->page) + frag->page_offset,
+			       frag->size);
+			imm_data += frag->size;
+		}
+	}
+	swqe->immediate_data_length = skb->len;
+	dev_kfree_skb(skb);
+}
+
+static int ehea_start_xmit(struct sk_buff *skb, struct net_device *dev)
+{
+	struct ehea_port *port = netdev_priv(dev);
+	struct ehea_swqe *swqe;
+	unsigned long flags;
+	u32 lkey;
+	int swqe_index;
+	struct ehea_port_res *pr = &port->port_res[0];
+
+	spin_lock(&pr->xmit_lock);
+
+	swqe = ehea_get_swqe(pr->qp, &swqe_index);
+	memset(swqe, 0, SWQE_HEADER_SIZE);
+	atomic_dec(&pr->swqe_avail);
+
+	if (skb->len <= SWQE3_MAX_IMM) {
+		u32 sig_iv = port->sig_comp_iv;
+		u32 swqe_num = pr->swqe_id_counter;
+		ehea_xmit3(skb, dev, swqe);
+		swqe->wr_id = EHEA_BMASK_SET(EHEA_WR_ID_TYPE, EHEA_SWQE3_TYPE)
+			| EHEA_BMASK_SET(EHEA_WR_ID_COUNT, swqe_num);
+		if (pr->swqe_ll_count >= (sig_iv - 1)) {
+			swqe->wr_id |= EHEA_BMASK_SET(EHEA_WR_ID_REFILL,
+						      sig_iv);
+			swqe->tx_control |= EHEA_SWQE_SIGNALLED_COMPLETION;
+			pr->swqe_ll_count = 0;
+		} else
+			pr->swqe_ll_count += 1;
+	} else {
+		swqe->wr_id =
+			EHEA_BMASK_SET(EHEA_WR_ID_TYPE, EHEA_SWQE2_TYPE)
+		      | EHEA_BMASK_SET(EHEA_WR_ID_COUNT, pr->swqe_id_counter)
+		      | EHEA_BMASK_SET(EHEA_WR_ID_INDEX, pr->sq_skba.index);
+		pr->sq_skba.arr[pr->sq_skba.index] = skb;
+
+		pr->sq_skba.index++;
+		pr->sq_skba.index &= (pr->sq_skba.len - 1);
+
+		lkey = pr->send_mr.lkey;
+		ehea_xmit2(skb, dev, swqe, lkey);
+
+		if (pr->swqe_count >= (EHEA_SIG_IV_LONG - 1)) {
+			swqe->wr_id |= EHEA_BMASK_SET(EHEA_WR_ID_REFILL,
+						      EHEA_SIG_IV_LONG);
+			swqe->tx_control |= EHEA_SWQE_SIGNALLED_COMPLETION;
+			pr->swqe_count = 0;
+		} else
+			pr->swqe_count += 1;
+	}
+	pr->swqe_id_counter += 1;
+
+	if (port->vgrp && vlan_tx_tag_present(skb)) {
+		swqe->tx_control |= EHEA_SWQE_VLAN_INSERT;
+		swqe->vlan_tag = vlan_tx_tag_get(skb);
+	}
+
+	if (netif_msg_tx_queued(port)) {
+		ehea_info("post swqe on QP %d", pr->qp->init_attr.qp_nr);
+		ehea_dump(swqe, sizeof(*swqe), "swqe");
+	}
+
+	ehea_post_swqe(pr->qp, swqe);
+	pr->tx_packets++;
+
+	if (unlikely(atomic_read(&pr->swqe_avail) <= 1)) {
+		spin_lock_irqsave(&pr->netif_queue, flags);
+		if (unlikely(atomic_read(&pr->swqe_avail) <= 1)) {
+			netif_stop_queue(dev);
+			pr->queue_stopped = 1;
+		}
+		spin_unlock_irqrestore(&pr->netif_queue, flags);
+	}
+	dev->trans_start = jiffies;
+	spin_unlock(&pr->xmit_lock);
+
+	return NETDEV_TX_OK;
+}
+
+static void ehea_vlan_rx_register(struct net_device *dev,
+				  struct vlan_group *grp)
+{
+	struct ehea_port *port = netdev_priv(dev);
+	struct ehea_adapter *adapter = port->adapter;
+	struct hcp_ehea_port_cb1 *cb1;
+	u64 hret;
+
+	port->vgrp = grp;
+
+	cb1 = kzalloc(H_CB_ALIGNMENT, GFP_KERNEL);
+	if (!cb1) {
+		ehea_error("no mem for cb1");
+		goto out;
+	}
+
+	if (grp)
+		memset(cb1->vlan_filter, 0, sizeof(cb1->vlan_filter));
+	else
+		memset(cb1->vlan_filter, 0xFF, sizeof(cb1->vlan_filter));
+
+	hret = ehea_h_modify_ehea_port(adapter->handle, port->logical_port_id,
+				       H_PORT_CB1, H_PORT_CB1_ALL, cb1);
+	if (hret != H_SUCCESS)
+		ehea_error("modify_ehea_port failed");
+
+	kfree(cb1);
+out:
+	return;
+}
+
+static void ehea_vlan_rx_add_vid(struct net_device *dev, unsigned short vid)
+{
+	struct ehea_port *port = netdev_priv(dev);
+	struct ehea_adapter *adapter = port->adapter;
+	struct hcp_ehea_port_cb1 *cb1;
+	int index;
+	u64 hret;
+
+	cb1 = kzalloc(H_CB_ALIGNMENT, GFP_KERNEL);
+	if (!cb1) {
+		ehea_error("no mem for cb1");
+		goto out;
+	}
+
+	hret = ehea_h_query_ehea_port(adapter->handle, port->logical_port_id,
+				      H_PORT_CB1, H_PORT_CB1_ALL, cb1);
+	if (hret != H_SUCCESS) {
+		ehea_error("query_ehea_port failed");
+		goto out;
+	}
+
+	index = (vid / 64);
+	cb1->vlan_filter[index] |= ((u64)(1 << (vid & 0x3F)));
+
+	hret = ehea_h_modify_ehea_port(adapter->handle, port->logical_port_id,
+				       H_PORT_CB1, H_PORT_CB1_ALL, cb1);
+	if (hret != H_SUCCESS)
+		ehea_error("modify_ehea_port failed");
+out:
+	kfree(cb1);
+	return;
+}
+
+static void ehea_vlan_rx_kill_vid(struct net_device *dev, unsigned short vid)
+{
+	struct ehea_port *port = netdev_priv(dev);
+	struct ehea_adapter *adapter = port->adapter;
+	struct hcp_ehea_port_cb1 *cb1;
+	int index;
+	u64 hret;
+
+	if (port->vgrp)
+		port->vgrp->vlan_devices[vid] = NULL;
+
+	cb1 = kzalloc(H_CB_ALIGNMENT, GFP_KERNEL);
+	if (!cb1) {
+		ehea_error("no mem for cb1");
+		goto out;
+	}
+
+	hret = ehea_h_query_ehea_port(adapter->handle, port->logical_port_id,
+				      H_PORT_CB1, H_PORT_CB1_ALL, cb1);
+	if (hret != H_SUCCESS) {
+		ehea_error("query_ehea_port failed");
+		goto out;
+	}
+
+	index = (vid / 64);
+	cb1->vlan_filter[index] &= ~((u64)(1 << (vid & 0x3F)));
+
+	hret = ehea_h_modify_ehea_port(adapter->handle, port->logical_port_id,
+				       H_PORT_CB1, H_PORT_CB1_ALL, cb1);
+	if (hret != H_SUCCESS)
+		ehea_error("modify_ehea_port failed");
+out:
+	kfree(cb1);
+	return;
+}
+
+int ehea_activate_qp(struct ehea_adapter *adapter, struct ehea_qp *qp)
+{
+	int ret = -EIO;
+	u64 hret;
+	u16 dummy16 = 0;
+	u64 dummy64 = 0;
+	struct hcp_modify_qp_cb0* cb0;
+
+	cb0 = kzalloc(H_CB_ALIGNMENT, GFP_KERNEL);
+	if (!cb0) {
+		ret = -ENOMEM;
+		goto out;
+	}
+
+	hret = ehea_h_query_ehea_qp(adapter->handle, 0, qp->fw_handle,
+				    EHEA_BMASK_SET(H_QPCB0_ALL, 0xFFFF), cb0);
+	if (hret != H_SUCCESS) {
+		ehea_error("query_ehea_qp failed (1)");
+		goto out;
+	}
+
+	cb0->qp_ctl_reg = H_QP_CR_STATE_INITIALIZED;
+	hret = ehea_h_modify_ehea_qp(adapter->handle, 0, qp->fw_handle,
+				     EHEA_BMASK_SET(H_QPCB0_QP_CTL_REG, 1), cb0,
+				     &dummy64, &dummy64, &dummy16, &dummy16);
+	if (hret != H_SUCCESS) {
+		ehea_error("modify_ehea_qp failed (1)");
+		goto out;
+	}
+
+	hret = ehea_h_query_ehea_qp(adapter->handle, 0, qp->fw_handle,
+				    EHEA_BMASK_SET(H_QPCB0_ALL, 0xFFFF), cb0);
+	if (hret != H_SUCCESS) {
+		ehea_error("query_ehea_qp failed (2)");
+		goto out;
+	}
+
+	cb0->qp_ctl_reg = H_QP_CR_ENABLED | H_QP_CR_STATE_INITIALIZED;
+	hret = ehea_h_modify_ehea_qp(adapter->handle, 0, qp->fw_handle,
+				     EHEA_BMASK_SET(H_QPCB0_QP_CTL_REG, 1), cb0,
+				     &dummy64, &dummy64, &dummy16, &dummy16);
+	if (hret != H_SUCCESS) {
+		ehea_error("modify_ehea_qp failed (2)");
+		goto out;
+	}
+
+	hret = ehea_h_query_ehea_qp(adapter->handle, 0, qp->fw_handle,
+				    EHEA_BMASK_SET(H_QPCB0_ALL, 0xFFFF), cb0);
+	if (hret != H_SUCCESS) {
+		ehea_error("query_ehea_qp failed (3)");
+		goto out;
+	}
+
+	cb0->qp_ctl_reg = H_QP_CR_ENABLED | H_QP_CR_STATE_RDY2SND;
+	hret = ehea_h_modify_ehea_qp(adapter->handle, 0, qp->fw_handle,
+				     EHEA_BMASK_SET(H_QPCB0_QP_CTL_REG, 1), cb0,
+				     &dummy64, &dummy64, &dummy16, &dummy16);
+	if (hret != H_SUCCESS) {
+		ehea_error("modify_ehea_qp failed (3)");
+		goto out;
+	}
+
+	hret = ehea_h_query_ehea_qp(adapter->handle, 0, qp->fw_handle,
+				    EHEA_BMASK_SET(H_QPCB0_ALL, 0xFFFF), cb0);
+	if (hret != H_SUCCESS) {
+		ehea_error("query_ehea_qp failed (4)");
+		goto out;
+	}
+
+	ret = 0;
+out:
+	kfree(cb0);
+	return ret;
+}
+
+static int ehea_port_res_setup(struct ehea_port *port, int def_qps,
+			       int add_tx_qps)
+{
+	int ret, i;
+	struct port_res_cfg pr_cfg, pr_cfg_small_rx;
+	enum ehea_eq_type eq_type = EHEA_EQ;
+
+	port->qp_eq = ehea_create_eq(port->adapter, eq_type,
+				   EHEA_MAX_ENTRIES_EQ, 1);
+	if (!port->qp_eq) {
+		ret = -EINVAL;
+		ehea_error("ehea_create_eq failed (qp_eq)");
+		goto out_kill_eq;
+	}
+
+	pr_cfg.max_entries_rcq = rq1_entries + rq2_entries + rq3_entries;
+	pr_cfg.max_entries_scq = sq_entries;
+	pr_cfg.max_entries_sq = sq_entries;
+	pr_cfg.max_entries_rq1 = rq1_entries;
+	pr_cfg.max_entries_rq2 = rq2_entries;
+	pr_cfg.max_entries_rq3 = rq3_entries;
+
+	pr_cfg_small_rx.max_entries_rcq = 1;
+	pr_cfg_small_rx.max_entries_scq = sq_entries;
+	pr_cfg_small_rx.max_entries_sq = sq_entries;
+	pr_cfg_small_rx.max_entries_rq1 = 1;
+	pr_cfg_small_rx.max_entries_rq2 = 1;
+	pr_cfg_small_rx.max_entries_rq3 = 1;
+
+	for (i = 0; i < def_qps; i++) {
+		ret = ehea_init_port_res(port, &port->port_res[i], &pr_cfg, i);
+		if (ret)
+			goto out_clean_pr;
+	}
+	for (i = def_qps; i < def_qps + add_tx_qps; i++) {
+		ret = ehea_init_port_res(port, &port->port_res[i],
+					 &pr_cfg_small_rx, i);
+		if (ret)
+			goto out_clean_pr;
+	}
+
+	return 0;
+
+out_clean_pr:
+	while (--i >= 0)
+		ehea_clean_portres(port, &port->port_res[i]);
+
+out_kill_eq:
+	ehea_destroy_eq(port->qp_eq);
+	return ret;
+}
+
+static int ehea_clean_all_portres(struct ehea_port *port)
+{
+	int ret = 0;
+	int i;
+
+	for(i = 0; i < port->num_def_qps + port->num_add_tx_qps; i++)
+		ret |= ehea_clean_portres(port, &port->port_res[i]);
+
+	ret |= ehea_destroy_eq(port->qp_eq);
+
+	return ret;
+}
+
+static int ehea_up(struct net_device *dev)
+{
+	int ret, i;
+	struct ehea_port *port = netdev_priv(dev);
+	u64 mac_addr = 0;
+
+	if (port->state == EHEA_PORT_UP)
+		return 0;
+
+	ret = ehea_port_res_setup(port, port->num_def_qps,
+				  port->num_add_tx_qps);
+	if (ret) {
+		ehea_error("port_res_failed");
+		goto out;
+	}
+
+	/* Set default QP for this port */
+	ret = ehea_configure_port(port);
+	if (ret) {
+		ehea_error("ehea_configure_port failed. ret:%d", ret);
+		goto out_clean_pr;
+	}
+
+	ret = ehea_broadcast_reg_helper(port, H_REG_BCMC);
+	if (ret) {
+		ret = -EIO;
+		ehea_error("out_clean_pr");
+		goto out_clean_pr;
+	}
+	mac_addr = (*(u64*)dev->dev_addr) >> 16;
+
+	ret = ehea_reg_interrupts(dev);
+	if (ret) {
+		ehea_error("out_dereg_bc");
+		goto out_dereg_bc;
+	}
+
+	for(i = 0; i < port->num_def_qps + port->num_add_tx_qps; i++) {
+		ret = ehea_activate_qp(port->adapter, port->port_res[i].qp);
+		if (ret) {
+			ehea_error("activate_qp failed");
+			goto out_free_irqs;
+		}
+	}
+
+	for(i = 0; i < port->num_def_qps; i++) {
+		ret = ehea_fill_port_res(&port->port_res[i]);
+		if (ret) {
+			ehea_error("out_free_irqs");
+			goto out_free_irqs;
+		}
+	}
+
+	ret = 0;
+	port->state = EHEA_PORT_UP;
+	goto out;
+
+out_free_irqs:
+	ehea_free_interrupts(dev);
+
+out_dereg_bc:
+	ehea_broadcast_reg_helper(port, H_DEREG_BCMC);
+
+out_clean_pr:
+	ehea_clean_all_portres(port);
+out:
+	return ret;
+}
+
+static int ehea_open(struct net_device *dev)
+{
+	int ret;
+	struct ehea_port *port = netdev_priv(dev);
+
+	down(&port->port_lock);
+
+	if (netif_msg_ifup(port))
+		ehea_info("enabling port %s", dev->name);
+
+	ret = ehea_up(dev);
+	if (!ret)
+		netif_start_queue(dev);
+
+	up(&port->port_lock);
+
+	return ret;
+}
+
+static int ehea_down(struct net_device *dev)
+{
+	int ret, i;
+	struct ehea_port *port = netdev_priv(dev);
+
+	if (port->state == EHEA_PORT_DOWN)
+		return 0;
+
+	ehea_drop_multicast_list(dev);
+	ehea_free_interrupts(dev);
+
+	for (i = 0; i < port->num_def_qps + port->num_add_tx_qps; i++)
+		tasklet_kill(&port->port_res[i].send_comp_task);
+
+	ehea_broadcast_reg_helper(port, H_DEREG_BCMC);
+	ret = ehea_clean_all_portres(port);
+	port->state = EHEA_PORT_DOWN;
+	return ret;
+}
+
+static int ehea_stop(struct net_device *dev)
+{
+	int ret;
+	struct ehea_port *port = netdev_priv(dev);
+
+	if (netif_msg_ifdown(port))
+		ehea_info("disabling port %s", dev->name);
+
+	flush_workqueue(port->adapter->ehea_wq);
+	down(&port->port_lock);
+	netif_stop_queue(dev);
+	ret = ehea_down(dev);
+	up(&port->port_lock);
+	return ret;
+}
+
+static void ehea_reset_port(void *data)
+{
+	int ret;
+	struct net_device *dev = data;
+	struct ehea_port *port = netdev_priv(dev);
+
+	port->resets++;
+	down(&port->port_lock);
+	netif_stop_queue(dev);
+	netif_poll_disable(dev);
+
+	ret = ehea_down(dev);
+	if (ret)
+		ehea_error("ehea_down failed. not all resources are freed");
+
+	ret = ehea_up(dev);
+	if (ret) {
+		ehea_error("Reset device %s failed: ret=%d", dev->name, ret);
+		goto out;
+	}
+
+	if (netif_msg_timer(port))
+		ehea_info("Device %s resetted successfully", dev->name);
+
+	netif_poll_enable(dev);
+	netif_wake_queue(dev);
+out:
+	up(&port->port_lock);
+	return;
+}
+
+static void ehea_tx_watchdog(struct net_device *dev)
+{
+	struct ehea_port *port = netdev_priv(dev);
+
+	if (netif_carrier_ok(dev))
+		queue_work(port->adapter->ehea_wq, &port->reset_task);
+}
+
+int ehea_sense_adapter_attr(struct ehea_adapter *adapter)
+{
+	struct hcp_query_ehea *cb;
+	u64 hret;
+	int ret;
+
+	cb = kzalloc(H_CB_ALIGNMENT, GFP_KERNEL);
+	if (!cb) {
+		ret = -ENOMEM;
+		goto out;
+	}
+
+	hret = ehea_h_query_ehea(adapter->handle, cb);
+
+	if (hret != H_SUCCESS) {
+		ret = -EIO;
+		goto out_herr;
+	}
+
+	adapter->num_ports = cb->num_ports;
+	adapter->max_mc_mac = cb->max_mc_mac - 1;
+	ret = 0;
+
+out_herr:
+	kfree(cb);
+out:
+	return ret;
+}
+
+static int ehea_setup_single_port(struct ehea_port *port,
+				  struct device_node *dn)
+{
+	int ret;
+	u64 hret;
+	struct net_device *dev = port->netdev;
+	struct ehea_adapter *adapter = port->adapter;
+	struct hcp_ehea_port_cb4 *cb4;
+	u32 *dn_log_port_id;
+
+	sema_init(&port->port_lock, 1);
+	port->state = EHEA_PORT_DOWN;
+	port->sig_comp_iv = sq_entries / 10;
+
+	if (!dn) {
+		ehea_error("bad device node: dn=%p", dn);
+		ret = -EINVAL;
+		goto out;
+	}
+
+	port->of_dev_node = dn;
+
+	/* Determine logical port id */
+	dn_log_port_id = (u32*)get_property(dn, "ibm,hea-port-no", NULL);
+
+	if (!dn_log_port_id) {
+		ehea_error("bad device node: dn_log_port_id=%p",
+			   dn_log_port_id);
+		ret = -EINVAL;
+		goto out;
+	}
+	port->logical_port_id = *dn_log_port_id;
+
+	port->mc_list = kzalloc(sizeof(struct ehea_mc_list), GFP_KERNEL);
+	if (!port->mc_list) {
+		ret = -ENOMEM;
+		goto out;
+	}
+
+	INIT_LIST_HEAD(&port->mc_list->list);
+
+	ehea_set_portspeed(port, EHEA_SPEED_AUTONEG);
+
+	ret = ehea_sense_port_attr(port);
+	if (ret)
+		goto out;
+
+	/* Enable Jumbo frames */
+	cb4 = kzalloc(H_CB_ALIGNMENT, GFP_KERNEL);
+	if (!cb4) {
+		ehea_error("no mem for cb4");
+	} else {
+		cb4->jumbo_frame = 1;
+		hret = ehea_h_modify_ehea_port(adapter->handle,
+					       port->logical_port_id,
+					       H_PORT_CB4, H_PORT_CB4_JUMBO,
+					       cb4);
+		if (hret != H_SUCCESS) {
+			ehea_info("Jumbo frames not activated");
+		}
+		kfree(cb4);
+	}
+
+	/* initialize net_device structure */
+	SET_MODULE_OWNER(dev);
+
+	memcpy(dev->dev_addr, &port->mac_addr, ETH_ALEN);
+
+	dev->open = ehea_open;
+	dev->poll = ehea_poll;
+	dev->weight = 64;
+	dev->stop = ehea_stop;
+	dev->hard_start_xmit = ehea_start_xmit;
+	dev->get_stats = ehea_get_stats;
+	dev->set_multicast_list = ehea_set_multicast_list;
+	dev->set_mac_address = ehea_set_mac_addr;
+	dev->change_mtu = ehea_change_mtu;
+	dev->vlan_rx_register = ehea_vlan_rx_register;
+	dev->vlan_rx_add_vid = ehea_vlan_rx_add_vid;
+	dev->vlan_rx_kill_vid = ehea_vlan_rx_kill_vid;
+	dev->features = NETIF_F_SG | NETIF_F_FRAGLIST | NETIF_F_TSO
+		      | NETIF_F_HIGHDMA | NETIF_F_HW_CSUM | NETIF_F_HW_VLAN_TX
+		      | NETIF_F_HW_VLAN_RX | NETIF_F_HW_VLAN_FILTER
+		      | NETIF_F_LLTX;
+	dev->tx_timeout = &ehea_tx_watchdog;
+	dev->watchdog_timeo = EHEA_WATCH_DOG_TIMEOUT;
+
+	INIT_WORK(&port->reset_task, ehea_reset_port, dev);
+
+	ehea_set_ethtool_ops(dev);
+
+	ret = register_netdev(dev);
+	if (ret) {
+		ehea_error("register_netdev failed. ret=%d", ret);
+		goto out_free;
+	}
+
+	port->netdev = dev;
+	ret = 0;
+	goto out;
+
+out_free:
+	kfree(port->mc_list);
+out:
+	return ret;
+}
+
+static int ehea_setup_ports(struct ehea_adapter *adapter)
+{
+	int ret;
+	int port_setup_ok = 0;
+	struct ehea_port *port;
+	struct device_node *dn = NULL;
+	struct net_device *dev;
+	int i;
+
+	/* get port properties for all ports */
+	for (i = 0; i < adapter->num_ports; i++) {
+
+		if (adapter->port[i])
+			continue;	/* port already up and running */
+
+		/* allocate memory for the port structures */
+		dev = alloc_etherdev(sizeof(struct ehea_port));
+
+		if (!dev) {
+			ehea_error("no mem for net_device");
+			break;
+		}
+
+		port = netdev_priv(dev);
+		port->adapter = adapter;
+		port->netdev = dev;
+		adapter->port[i] = port;
+		port->msg_enable = netif_msg_init(msg_level, EHEA_MSG_DEFAULT);
+
+		dn = of_find_node_by_name(dn, "ethernet");
+		ret = ehea_setup_single_port(port, dn);
+		if (ret) {
+			/* Free mem for this port struct. The others will be
+			   processed on rollback */
+			free_netdev(dev);
+			adapter->port[i] = NULL;
+			ehea_error("eHEA port %d setup failed, ret=%d", i, ret);
+		}
+	}
+
+	of_node_put(dn);
+
+	/* Check for succesfully set up ports */
+	for (i = 0; i < adapter->num_ports; i++)
+		if (adapter->port[i])
+			port_setup_ok++;
+
+	if (port_setup_ok)
+		ret = 0;	/* At least some ports are setup correctly */
+	else
+		ret = -EINVAL;
+
+	return ret;
+}
+
+static int __devinit ehea_probe(struct ibmebus_dev *dev,
+				const struct of_device_id *id)
+{
+	struct ehea_adapter *adapter;
+	u64 *adapter_handle;
+	int ret;
+
+	adapter = kzalloc(sizeof(*adapter), GFP_KERNEL);
+	if (!adapter) {
+		ret = -ENOMEM;
+		dev_err(&dev->ofdev.dev, "no mem for ehea_adapter\n");
+		goto out;
+	}
+
+	adapter_handle = (u64*)get_property(dev->ofdev.node, "ibm,hea-handle",
+					    NULL);
+	if (!adapter_handle) {
+		dev_err(&dev->ofdev.dev, "failed getting handle for adapter"
+			" '%s'\n", dev->ofdev.node->full_name);
+		ret = -ENODEV;
+		goto out_free_ad;
+	}
+
+	adapter->handle = *adapter_handle;
+	adapter->pd = EHEA_PD_ID;
+
+	dev->ofdev.dev.driver_data = adapter;
+
+	ret = ehea_reg_mr_adapter(adapter);
+	if (ret) {
+		dev_err(&dev->ofdev.dev, "reg_mr_adapter failed\n");
+		goto out_free_ad;
+	}
+
+	/* initialize adapter and ports */
+	/* get adapter properties */
+	ret = ehea_sense_adapter_attr(adapter);
+	if (ret) {
+		dev_err(&dev->ofdev.dev, "sense_adapter_attr failed: %d", ret);
+		goto out_free_res;
+	}
+	dev_info(&dev->ofdev.dev, "%d eHEA ports found\n", adapter->num_ports);
+
+	adapter->neq = ehea_create_eq(adapter,
+				      EHEA_NEQ, EHEA_MAX_ENTRIES_EQ, 1);
+	if (!adapter->neq) {
+		dev_err(&dev->ofdev.dev, "NEQ creation failed");
+		goto out_free_res;
+	}
+
+	tasklet_init(&adapter->neq_tasklet, ehea_neq_tasklet,
+		     (unsigned long)adapter);
+
+	ret = ibmebus_request_irq(NULL, adapter->neq->attr.ist1,
+				  ehea_interrupt_neq, SA_INTERRUPT,
+				  "ehea_neq", adapter);
+	if (ret) {
+		dev_err(&dev->ofdev.dev, "requesting NEQ IRQ failed");
+		goto out_kill_eq;
+	}
+
+	adapter->ehea_wq = create_workqueue("ehea_wq");
+	if (!adapter->ehea_wq)
+		goto out_free_irq;
+
+	ret = ehea_setup_ports(adapter);
+	if (ret) {
+		dev_err(&dev->ofdev.dev, "setup_ports failed");
+		goto out_kill_wq;
+	}
+
+	ret = 0;
+	goto out;
+
+out_kill_wq:
+	destroy_workqueue(adapter->ehea_wq);
+
+out_free_irq:
+	ibmebus_free_irq(NULL, adapter->neq->attr.ist1, adapter);
+
+out_kill_eq:
+	ehea_destroy_eq(adapter->neq);
+
+out_free_res:
+	ehea_h_free_resource(adapter->handle, adapter->mr.handle);
+
+out_free_ad:
+	kfree(adapter);
+out:
+	return ret;
+}
+
+static void ehea_shutdown_single_port(struct ehea_port *port)
+{
+	unregister_netdev(port->netdev);
+	kfree(port->mc_list);
+	free_netdev(port->netdev);
+}
+
+static int __devexit ehea_remove(struct ibmebus_dev *dev)
+{
+	struct ehea_adapter *adapter = dev->ofdev.dev.driver_data;
+	u64 hret;
+	int i;
+
+	for (i = 0; i < adapter->num_ports; i++)
+		if (adapter->port[i]) {
+			ehea_shutdown_single_port(adapter->port[i]);
+			adapter->port[i] = NULL;
+		}
+
+	ibmebus_free_irq(NULL, adapter->neq->attr.ist1, adapter);
+
+	ehea_destroy_eq(adapter->neq);
+
+	hret = ehea_h_free_resource(adapter->handle, adapter->mr.handle);
+	if (hret) {
+		dev_err(&dev->ofdev.dev, "free_resource_mr failed");
+		return -EIO;
+	}
+	kfree(adapter);
+	return 0;
+}
+
+static int check_module_parm(void)
+{
+	int ret = 0;
+
+	if ((rq1_entries < EHEA_MIN_ENTRIES_QP) ||
+	    (rq1_entries > EHEA_MAX_ENTRIES_RQ1)) {
+		ehea_info("Bad parameter: rq1_entries");
+		ret = -EINVAL;
+	}
+	if ((rq2_entries < EHEA_MIN_ENTRIES_QP) ||
+	    (rq2_entries > EHEA_MAX_ENTRIES_RQ2)) {
+		ehea_info("Bad parameter: rq2_entries");
+		ret = -EINVAL;
+	}
+	if ((rq3_entries < EHEA_MIN_ENTRIES_QP) ||
+	    (rq3_entries > EHEA_MAX_ENTRIES_RQ3)) {
+		ehea_info("Bad parameter: rq3_entries");
+		ret = -EINVAL;
+	}
+	if ((sq_entries < EHEA_MIN_ENTRIES_QP) ||
+	    (sq_entries > EHEA_MAX_ENTRIES_SQ)) {
+		ehea_info("Bad parameter: sq_entries");
+		ret = -EINVAL;
+	}
+
+	return ret;
+}
+
+static struct of_device_id ehea_device_table[] = {
+	{
+		.name = "lhea",
+		.compatible = "IBM,lhea",
+	},
+	{},
+};
+
+static struct ibmebus_driver ehea_driver = {
+	.name = "ehea",
+	.id_table = ehea_device_table,
+	.probe = ehea_probe,
+	.remove = ehea_remove,
+};
+
+int __init ehea_module_init(void)
+{
+	int ret;
+
+	printk(KERN_INFO "IBM eHEA ethernet device driver (Release %s)\n",
+	       DRV_VERSION);
+
+	ret = check_module_parm();
+	if (ret)
+		goto out;
+	ret = ibmebus_register_driver(&ehea_driver);
+	if (ret)
+		ehea_error("failed registering eHEA device driver on ebus");
+
+out:
+	return ret;
+}
+
+static void __exit ehea_module_exit(void)
+{
+	ibmebus_unregister_driver(&ehea_driver);
+}
+
+module_init(ehea_module_init);
+module_exit(ehea_module_exit);

^ permalink raw reply

* [2.6.19 PATCH 2/7] ehea: pHYP interface
From: Jan-Bernd Themann @ 2006-09-06 13:31 UTC (permalink / raw)
  To: netdev
  Cc: Thomas Klein, Jan-Bernd Themann, linux-kernel, linux-ppc,
	Christoph Raisch, Marcus Eder

Signed-off-by: Jan-Bernd Themann <themann@de.ibm.com> 


 drivers/net/ehea/ehea_hcall.h |   51 +++
 drivers/net/ehea/ehea_phyp.c  |  705 ++++++++++++++++++++++++++++++++++++++++++
 drivers/net/ehea/ehea_phyp.h  |  454 +++++++++++++++++++++++++++
 3 files changed, 1210 insertions(+)



--- linux-2.6.18-rc6-orig/drivers/net/ehea/ehea_phyp.c	1970-01-01 01:00:00.000000000 +0100
+++ kernel/drivers/net/ehea/ehea_phyp.c	2006-09-06 15:53:42.000000000 +0200
@@ -0,0 +1,705 @@
+/*
+ *  linux/drivers/net/ehea/ehea_phyp.c
+ *
+ *  eHEA ethernet device driver for IBM eServer System p
+ *
+ *  (C) Copyright IBM Corp. 2006
+ *
+ *  Authors:
+ *       Christoph Raisch <raisch@de.ibm.com>
+ *       Jan-Bernd Themann <themann@de.ibm.com>
+ *       Thomas Klein <tklein@de.ibm.com>
+ *
+ *
+ * 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, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.	 See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include "ehea_phyp.h"
+
+
+static inline u16 get_order_of_qentries(u16 queue_entries)
+{
+	u8 ld = 1;		/*  logarithmus dualis */
+	while (((1U << ld) - 1) < queue_entries)
+		ld++;
+	return ld - 1;
+}
+
+/* Defines for H_CALL H_ALLOC_RESOURCE */
+#define H_ALL_RES_TYPE_QP        1
+#define H_ALL_RES_TYPE_CQ        2
+#define H_ALL_RES_TYPE_EQ        3
+#define H_ALL_RES_TYPE_MR        5
+#define H_ALL_RES_TYPE_MW        6
+
+static long ehea_hcall_9arg_9ret(unsigned long opcode,
+         			 unsigned long arg1, unsigned long arg2,
+         			 unsigned long arg3, unsigned long arg4,
+         			 unsigned long arg5, unsigned long arg6,
+         			 unsigned long arg7, unsigned long arg8,
+         			 unsigned long arg9, unsigned long *out1,
+         			 unsigned long *out2,unsigned long *out3,
+         			 unsigned long *out4,unsigned long *out5,
+         			 unsigned long *out6,unsigned long *out7,
+         			 unsigned long *out8,unsigned long *out9)
+{
+	long hret;
+	int i, sleep_msecs;
+
+	for (i = 0; i < 5; i++) {
+		hret = plpar_hcall_9arg_9ret(opcode,arg1, arg2, arg3, arg4,
+					     arg5, arg6, arg7, arg8, arg9, out1,
+					     out2, out3, out4, out5, out6, out7,
+					     out8, out9);
+		if (H_IS_LONG_BUSY(hret)) {
+			sleep_msecs = get_longbusy_msecs(hret);
+			msleep_interruptible(sleep_msecs);
+			continue;
+		}
+
+		if (hret < H_SUCCESS)
+			ehea_error("op=%lx hret=%lx "
+				   "i1=%lx i2=%lx i3=%lx i4=%lx i5=%lx i6=%lx "
+				   "i7=%lx i8=%lx i9=%lx "
+				   "o1=%lx o2=%lx o3=%lx o4=%lx o5=%lx o6=%lx "
+				   "o7=%lx o8=%lx o9=%lx",
+				   opcode, hret, arg1, arg2, arg3, arg4, arg5,
+				   arg6, arg7, arg8, arg9, *out1, *out2, *out3,
+				   *out4, *out5, *out6, *out7, *out8, *out9);
+		return hret;
+	}
+	return H_BUSY;
+}
+
+u64 ehea_h_query_ehea_qp(const u64 adapter_handle, const u8 qp_category,
+			 const u64 qp_handle, const u64 sel_mask, void *cb_addr)
+{
+	u64 dummy;
+
+	if ((((u64)cb_addr) & (PAGE_SIZE - 1)) != 0) {
+		ehea_error("not on pageboundary");
+		return H_PARAMETER;
+	}
+
+	return ehea_hcall_9arg_9ret(H_QUERY_HEA_QP,
+				    adapter_handle,	        /* R4 */
+				    qp_category,	        /* R5 */
+				    qp_handle,	                /* R6 */
+				    sel_mask,	                /* R7 */
+				    virt_to_abs(cb_addr),	/* R8 */
+				    0, 0, 0, 0,	                /* R9-R12 */
+				    &dummy,                     /* R4 */
+				    &dummy,                     /* R5 */
+				    &dummy,	                /* R6 */
+				    &dummy,	                /* R7 */
+				    &dummy,	                /* R8 */
+				    &dummy,	                /* R9 */
+				    &dummy,	                /* R10 */
+				    &dummy,	                /* R11 */
+				    &dummy);	                /* R12 */
+}
+
+/* input param R5 */
+#define H_ALL_RES_QP_EQPO         EHEA_BMASK_IBM(9, 11)
+#define H_ALL_RES_QP_QPP          EHEA_BMASK_IBM(12, 12)
+#define H_ALL_RES_QP_RQR          EHEA_BMASK_IBM(13, 15)
+#define H_ALL_RES_QP_EQEG         EHEA_BMASK_IBM(16, 16)
+#define H_ALL_RES_QP_LL_QP        EHEA_BMASK_IBM(17, 17)
+#define H_ALL_RES_QP_DMA128       EHEA_BMASK_IBM(19, 19)
+#define H_ALL_RES_QP_HSM          EHEA_BMASK_IBM(20, 21)
+#define H_ALL_RES_QP_SIGT         EHEA_BMASK_IBM(22, 23)
+#define H_ALL_RES_QP_TENURE       EHEA_BMASK_IBM(48, 55)
+#define H_ALL_RES_QP_RES_TYP      EHEA_BMASK_IBM(56, 63)
+
+/* input param R9  */
+#define H_ALL_RES_QP_TOKEN        EHEA_BMASK_IBM(0, 31)
+#define H_ALL_RES_QP_PD           EHEA_BMASK_IBM(32,63)
+
+/* input param R10 */
+#define H_ALL_RES_QP_MAX_SWQE     EHEA_BMASK_IBM(4, 7)
+#define H_ALL_RES_QP_MAX_R1WQE    EHEA_BMASK_IBM(12, 15)
+#define H_ALL_RES_QP_MAX_R2WQE    EHEA_BMASK_IBM(20, 23)
+#define H_ALL_RES_QP_MAX_R3WQE    EHEA_BMASK_IBM(28, 31)
+/* Max Send Scatter Gather Elements */
+#define H_ALL_RES_QP_MAX_SSGE     EHEA_BMASK_IBM(37, 39)
+#define H_ALL_RES_QP_MAX_R1SGE    EHEA_BMASK_IBM(45, 47)
+/* Max Receive SG Elements RQ1 */
+#define H_ALL_RES_QP_MAX_R2SGE    EHEA_BMASK_IBM(53, 55)
+#define H_ALL_RES_QP_MAX_R3SGE    EHEA_BMASK_IBM(61, 63)
+
+/* input param R11 */
+#define H_ALL_RES_QP_SWQE_IDL     EHEA_BMASK_IBM(0, 7)
+/* max swqe immediate data length */
+#define H_ALL_RES_QP_PORT_NUM     EHEA_BMASK_IBM(48, 63)
+
+/* input param R12 */
+#define H_ALL_RES_QP_TH_RQ2       EHEA_BMASK_IBM(0, 15)
+/* Threshold RQ2 */
+#define H_ALL_RES_QP_TH_RQ3       EHEA_BMASK_IBM(16, 31)
+/* Threshold RQ3 */
+
+/* output param R6 */
+#define H_ALL_RES_QP_ACT_SWQE     EHEA_BMASK_IBM(0, 15)
+#define H_ALL_RES_QP_ACT_R1WQE    EHEA_BMASK_IBM(16, 31)
+#define H_ALL_RES_QP_ACT_R2WQE    EHEA_BMASK_IBM(32, 47)
+#define H_ALL_RES_QP_ACT_R3WQE    EHEA_BMASK_IBM(48, 63)
+
+/* output param, R7 */
+#define H_ALL_RES_QP_ACT_SSGE     EHEA_BMASK_IBM(0, 7)
+#define H_ALL_RES_QP_ACT_R1SGE    EHEA_BMASK_IBM(8, 15)
+#define H_ALL_RES_QP_ACT_R2SGE    EHEA_BMASK_IBM(16, 23)
+#define H_ALL_RES_QP_ACT_R3SGE    EHEA_BMASK_IBM(24, 31)
+#define H_ALL_RES_QP_ACT_SWQE_IDL EHEA_BMASK_IBM(32, 39)
+
+/* output param R8,R9 */
+#define H_ALL_RES_QP_SIZE_SQ      EHEA_BMASK_IBM(0, 31)
+#define H_ALL_RES_QP_SIZE_RQ1     EHEA_BMASK_IBM(32, 63)
+#define H_ALL_RES_QP_SIZE_RQ2     EHEA_BMASK_IBM(0, 31)
+#define H_ALL_RES_QP_SIZE_RQ3     EHEA_BMASK_IBM(32, 63)
+
+/* output param R11,R12 */
+#define H_ALL_RES_QP_LIOBN_SQ     EHEA_BMASK_IBM(0, 31)
+#define H_ALL_RES_QP_LIOBN_RQ1    EHEA_BMASK_IBM(32, 63)
+#define H_ALL_RES_QP_LIOBN_RQ2    EHEA_BMASK_IBM(0, 31)
+#define H_ALL_RES_QP_LIOBN_RQ3    EHEA_BMASK_IBM(32, 63)
+
+u64 ehea_h_alloc_resource_qp(const u64 adapter_handle,
+			     struct ehea_qp_init_attr *init_attr, const u32 pd,
+			     u64 *qp_handle, struct h_epas *h_epas)
+{
+	u64 hret;
+
+	u64 allocate_controls =
+	    EHEA_BMASK_SET(H_ALL_RES_QP_EQPO, init_attr->low_lat_rq1 ? 1 : 0)
+	    | EHEA_BMASK_SET(H_ALL_RES_QP_QPP, 0)
+	    | EHEA_BMASK_SET(H_ALL_RES_QP_RQR, 6)	/* rq1 & rq2 & rq3 */
+	    | EHEA_BMASK_SET(H_ALL_RES_QP_EQEG, 0)	/* EQE gen. disabled */
+	    | EHEA_BMASK_SET(H_ALL_RES_QP_LL_QP, init_attr->low_lat_rq1)
+	    | EHEA_BMASK_SET(H_ALL_RES_QP_DMA128, 0)
+	    | EHEA_BMASK_SET(H_ALL_RES_QP_HSM, 0)
+	    | EHEA_BMASK_SET(H_ALL_RES_QP_SIGT, init_attr->signalingtype)
+	    | EHEA_BMASK_SET(H_ALL_RES_QP_RES_TYP, H_ALL_RES_TYPE_QP);
+
+	u64 r9_reg = EHEA_BMASK_SET(H_ALL_RES_QP_PD, pd)
+	    | EHEA_BMASK_SET(H_ALL_RES_QP_TOKEN, init_attr->qp_token);
+
+	u64 max_r10_reg =
+	    EHEA_BMASK_SET(H_ALL_RES_QP_MAX_SWQE,
+			   get_order_of_qentries(init_attr->max_nr_send_wqes))
+	    | EHEA_BMASK_SET(H_ALL_RES_QP_MAX_R1WQE,
+			     get_order_of_qentries(init_attr->max_nr_rwqes_rq1))
+	    | EHEA_BMASK_SET(H_ALL_RES_QP_MAX_R2WQE,
+			     get_order_of_qentries(init_attr->max_nr_rwqes_rq2))
+	    | EHEA_BMASK_SET(H_ALL_RES_QP_MAX_R3WQE,
+			     get_order_of_qentries(init_attr->max_nr_rwqes_rq3))
+	    | EHEA_BMASK_SET(H_ALL_RES_QP_MAX_SSGE, init_attr->wqe_size_enc_sq)
+	    | EHEA_BMASK_SET(H_ALL_RES_QP_MAX_R1SGE,
+			     init_attr->wqe_size_enc_rq1)
+	    | EHEA_BMASK_SET(H_ALL_RES_QP_MAX_R2SGE,
+			     init_attr->wqe_size_enc_rq2)
+	    | EHEA_BMASK_SET(H_ALL_RES_QP_MAX_R3SGE,
+			     init_attr->wqe_size_enc_rq3);
+
+	u64 r11_in =
+	    EHEA_BMASK_SET(H_ALL_RES_QP_SWQE_IDL, init_attr->swqe_imm_data_len)
+	    | EHEA_BMASK_SET(H_ALL_RES_QP_PORT_NUM, init_attr->port_nr);
+	u64 threshold =
+	    EHEA_BMASK_SET(H_ALL_RES_QP_TH_RQ2, init_attr->rq2_threshold)
+	    | EHEA_BMASK_SET(H_ALL_RES_QP_TH_RQ3, init_attr->rq3_threshold);
+
+	u64 r5_out = 0;
+	u64 r6_out = 0;
+	u64 r7_out = 0;
+	u64 r8_out = 0;
+	u64 r9_out = 0;
+	u64 g_la_user_out = 0;
+	u64 r11_out = 0;
+	u64 r12_out = 0;
+
+	hret = ehea_hcall_9arg_9ret(H_ALLOC_HEA_RESOURCE,
+				    adapter_handle,		/* R4 */
+				    allocate_controls,		/* R5 */
+				    init_attr->send_cq_handle,	/* R6 */
+				    init_attr->recv_cq_handle,	/* R7 */
+				    init_attr->aff_eq_handle,	/* R8 */
+				    r9_reg,			/* R9 */
+				    max_r10_reg,		/* R10 */
+				    r11_in,			/* R11 */
+				    threshold,			/* R12 */
+				    qp_handle,			/* R4 */
+				    &r5_out,			/* R5 */
+				    &r6_out,			/* R6 */
+				    &r7_out,			/* R7 */
+				    &r8_out,			/* R8 */
+				    &r9_out,			/* R9 */
+				    &g_la_user_out,		/* R10 */
+				    &r11_out,			/* R11 */
+				    &r12_out);			/* R12 */
+
+	init_attr->qp_nr = (u32)r5_out;
+
+	init_attr->act_nr_send_wqes =
+	    (u16)EHEA_BMASK_GET(H_ALL_RES_QP_ACT_SWQE, r6_out);
+	init_attr->act_nr_rwqes_rq1 =
+	    (u16)EHEA_BMASK_GET(H_ALL_RES_QP_ACT_R1WQE, r6_out);
+	init_attr->act_nr_rwqes_rq2 =
+	    (u16)EHEA_BMASK_GET(H_ALL_RES_QP_ACT_R2WQE, r6_out);
+	init_attr->act_nr_rwqes_rq3 =
+	    (u16)EHEA_BMASK_GET(H_ALL_RES_QP_ACT_R3WQE, r6_out);
+
+	init_attr->act_wqe_size_enc_sq = init_attr->wqe_size_enc_sq;
+	init_attr->act_wqe_size_enc_rq1 = init_attr->wqe_size_enc_rq1;
+	init_attr->act_wqe_size_enc_rq2 = init_attr->wqe_size_enc_rq2;
+	init_attr->act_wqe_size_enc_rq3 = init_attr->wqe_size_enc_rq3;
+
+	init_attr->nr_sq_pages =
+	    (u32)EHEA_BMASK_GET(H_ALL_RES_QP_SIZE_SQ, r8_out);
+	init_attr->nr_rq1_pages =
+	    (u32)EHEA_BMASK_GET(H_ALL_RES_QP_SIZE_RQ1, r8_out);
+	init_attr->nr_rq2_pages =
+	    (u32)EHEA_BMASK_GET(H_ALL_RES_QP_SIZE_RQ2, r9_out);
+	init_attr->nr_rq3_pages =
+	    (u32)EHEA_BMASK_GET(H_ALL_RES_QP_SIZE_RQ3, r9_out);
+
+	init_attr->liobn_sq =
+	    (u32)EHEA_BMASK_GET(H_ALL_RES_QP_LIOBN_SQ, r11_out);
+	init_attr->liobn_rq1 =
+	    (u32)EHEA_BMASK_GET(H_ALL_RES_QP_LIOBN_RQ1, r11_out);
+	init_attr->liobn_rq2 =
+	    (u32)EHEA_BMASK_GET(H_ALL_RES_QP_LIOBN_RQ2, r12_out);
+	init_attr->liobn_rq3 =
+	    (u32)EHEA_BMASK_GET(H_ALL_RES_QP_LIOBN_RQ3, r12_out);
+
+	if (!hret)
+		hcp_epas_ctor(h_epas, g_la_user_out, g_la_user_out);
+
+	return hret;
+}
+
+u64 ehea_h_alloc_resource_cq(const u64 adapter_handle,
+			     struct ehea_cq_attr *cq_attr,
+			     u64 *cq_handle, struct h_epas *epas)
+{
+	u64 hret, dummy, act_nr_of_cqes_out, act_pages_out;
+	u64 g_la_privileged_out, g_la_user_out;
+
+	hret = ehea_hcall_9arg_9ret(H_ALLOC_HEA_RESOURCE,
+				    adapter_handle,		/* R4 */
+				    H_ALL_RES_TYPE_CQ,		/* R5 */
+				    cq_attr->eq_handle,		/* R6 */
+				    cq_attr->cq_token,		/* R7 */
+				    cq_attr->max_nr_of_cqes,	/* R8 */
+				    0, 0, 0, 0,			/* R9-R12 */
+				    cq_handle,			/* R4 */
+				    &dummy,			/* R5 */
+				    &dummy,			/* R6 */
+				    &act_nr_of_cqes_out,	/* R7 */
+				    &act_pages_out,		/* R8 */
+				    &g_la_privileged_out,	/* R9 */
+				    &g_la_user_out,		/* R10 */
+				    &dummy,	                /* R11 */
+				    &dummy);	                /* R12 */
+
+	cq_attr->act_nr_of_cqes = act_nr_of_cqes_out;
+	cq_attr->nr_pages = act_pages_out;
+
+	if (!hret)
+		hcp_epas_ctor(epas, g_la_privileged_out, g_la_user_out);
+
+	return hret;
+}
+
+/* Defines for H_CALL H_ALLOC_RESOURCE */
+#define H_ALL_RES_TYPE_QP        1
+#define H_ALL_RES_TYPE_CQ        2
+#define H_ALL_RES_TYPE_EQ        3
+#define H_ALL_RES_TYPE_MR        5
+#define H_ALL_RES_TYPE_MW        6
+
+/*  input param R5 */
+#define H_ALL_RES_EQ_NEQ             EHEA_BMASK_IBM(0, 0)
+#define H_ALL_RES_EQ_NON_NEQ_ISN     EHEA_BMASK_IBM(6, 7)
+#define H_ALL_RES_EQ_INH_EQE_GEN     EHEA_BMASK_IBM(16, 16)
+#define H_ALL_RES_EQ_RES_TYPE        EHEA_BMASK_IBM(56, 63)
+/*  input param R6 */
+#define H_ALL_RES_EQ_MAX_EQE         EHEA_BMASK_IBM(32, 63)
+
+/*  output param R6 */
+#define H_ALL_RES_EQ_LIOBN           EHEA_BMASK_IBM(32, 63)
+
+/*  output param R7 */
+#define H_ALL_RES_EQ_ACT_EQE         EHEA_BMASK_IBM(32, 63)
+
+/*  output param R8 */
+#define H_ALL_RES_EQ_ACT_PS          EHEA_BMASK_IBM(32, 63)
+
+/*  output param R9 */
+#define H_ALL_RES_EQ_ACT_EQ_IST_C    EHEA_BMASK_IBM(30, 31)
+#define H_ALL_RES_EQ_ACT_EQ_IST_1    EHEA_BMASK_IBM(40, 63)
+
+/*  output param R10 */
+#define H_ALL_RES_EQ_ACT_EQ_IST_2    EHEA_BMASK_IBM(40, 63)
+
+/*  output param R11 */
+#define H_ALL_RES_EQ_ACT_EQ_IST_3    EHEA_BMASK_IBM(40, 63)
+
+/*  output param R12 */
+#define H_ALL_RES_EQ_ACT_EQ_IST_4    EHEA_BMASK_IBM(40, 63)
+
+u64 ehea_h_alloc_resource_eq(const u64 adapter_handle,
+			     struct ehea_eq_attr *eq_attr, u64 *eq_handle)
+{
+	u64 hret, dummy, eq_liobn, allocate_controls;
+	u64 ist1_out, ist2_out, ist3_out, ist4_out;
+	u64 act_nr_of_eqes_out, act_pages_out;
+
+	/* resource type */
+	allocate_controls =
+	    EHEA_BMASK_SET(H_ALL_RES_EQ_RES_TYPE, H_ALL_RES_TYPE_EQ)
+	    | EHEA_BMASK_SET(H_ALL_RES_EQ_NEQ, eq_attr->type ? 1 : 0)
+	    | EHEA_BMASK_SET(H_ALL_RES_EQ_INH_EQE_GEN, !eq_attr->eqe_gen)
+	    | EHEA_BMASK_SET(H_ALL_RES_EQ_NON_NEQ_ISN, 1);
+
+	hret = ehea_hcall_9arg_9ret(H_ALLOC_HEA_RESOURCE,
+				    adapter_handle,		/* R4 */
+				    allocate_controls,		/* R5 */
+				    eq_attr->max_nr_of_eqes,	/* R6 */
+				    0, 0, 0, 0, 0, 0,		/* R7-R10 */
+				    eq_handle,			/* R4 */
+				    &dummy,			/* R5 */
+				    &eq_liobn,			/* R6 */
+				    &act_nr_of_eqes_out,	/* R7 */
+				    &act_pages_out,		/* R8 */
+				    &ist1_out,			/* R9 */
+				    &ist2_out,			/* R10 */
+				    &ist3_out,			/* R11 */
+				    &ist4_out);			/* R12 */
+
+	eq_attr->act_nr_of_eqes = act_nr_of_eqes_out;
+	eq_attr->nr_pages = act_pages_out;
+	eq_attr->ist1 = ist1_out;
+	eq_attr->ist2 = ist2_out;
+	eq_attr->ist3 = ist3_out;
+	eq_attr->ist4 = ist4_out;
+
+	return hret;
+}
+
+u64 ehea_h_modify_ehea_qp(const u64 adapter_handle, const u8 cat,
+			  const u64 qp_handle, const u64 sel_mask,
+			  void *cb_addr, u64 *inv_attr_id, u64 *proc_mask,
+			  u16 *out_swr, u16 *out_rwr)
+{
+	u64 hret, dummy, act_out_swr, act_out_rwr;
+
+	if ((((u64)cb_addr) & (PAGE_SIZE - 1)) != 0) {
+		ehea_error("not on page boundary");
+		return H_PARAMETER;
+	}
+
+	hret = ehea_hcall_9arg_9ret(H_MODIFY_HEA_QP,
+				    adapter_handle,		/* R4 */
+				    (u64) cat,			/* R5 */
+				    qp_handle,			/* R6 */
+				    sel_mask,			/* R7 */
+				    virt_to_abs(cb_addr),	/* R8 */
+				    0, 0, 0, 0,			/* R9-R12 */
+				    inv_attr_id,		/* R4 */
+				    &dummy,			/* R5 */
+				    &dummy,			/* R6 */
+				    &act_out_swr,		/* R7 */
+				    &act_out_rwr,		/* R8 */
+				    proc_mask,			/* R9 */
+				    &dummy,			/* R10 */
+				    &dummy,			/* R11 */
+				    &dummy);			/* R12 */
+	*out_swr = act_out_swr;
+	*out_rwr = act_out_rwr;
+
+	return hret;
+}
+
+u64 ehea_h_register_rpage(const u64 adapter_handle, const u8 pagesize,
+			  const u8 queue_type, const u64 resource_handle,
+			  const u64 log_pageaddr, u64 count)
+{
+	u64 dummy, reg_control;
+
+	reg_control = EHEA_BMASK_SET(H_REG_RPAGE_PAGE_SIZE, pagesize)
+		    | EHEA_BMASK_SET(H_REG_RPAGE_QT, queue_type);
+
+	return ehea_hcall_9arg_9ret(H_REGISTER_HEA_RPAGES,
+				    adapter_handle,		/* R4 */
+				    reg_control,		/* R5 */
+				    resource_handle,		/* R6 */
+				    log_pageaddr,		/* R7 */
+				    count,			/* R8 */
+				    0, 0, 0, 0,			/* R9-R12 */
+				    &dummy,			/* R4 */
+				    &dummy,			/* R5 */
+				    &dummy,			/* R6 */
+				    &dummy,			/* R7 */
+				    &dummy,			/* R8 */
+				    &dummy,			/* R9 */
+				    &dummy,			/* R10 */
+				    &dummy,	                /* R11 */
+				    &dummy);	                /* R12 */
+}
+
+u64 ehea_h_register_smr(const u64 adapter_handle, const u64 orig_mr_handle,
+			const u64 vaddr_in, const u32 access_ctrl, const u32 pd,
+			struct ehea_mr *mr)
+{
+	u64 hret, dummy, lkey_out;
+
+	hret = ehea_hcall_9arg_9ret(H_REGISTER_SMR,
+				    adapter_handle       ,          /* R4 */
+				    orig_mr_handle,                 /* R5 */
+				    vaddr_in,                       /* R6 */
+				    (((u64)access_ctrl) << 32ULL),  /* R7 */
+				    pd,                             /* R8 */
+				    0, 0, 0, 0,			    /* R9-R12 */
+				    &mr->handle,                    /* R4 */
+				    &dummy,                         /* R5 */
+				    &lkey_out,                      /* R6 */
+				    &dummy,                         /* R7 */
+				    &dummy,                         /* R8 */
+				    &dummy,                         /* R9 */
+				    &dummy,                         /* R10 */
+				    &dummy,                         /* R11 */
+				    &dummy);                        /* R12 */
+	mr->lkey = (u32)lkey_out;
+
+	return hret;
+}
+
+u64 ehea_h_disable_and_get_hea(const u64 adapter_handle, const u64 qp_handle)
+{
+	u64 hret, dummy, ladr_next_sq_wqe_out;
+	u64 ladr_next_rq1_wqe_out, ladr_next_rq2_wqe_out, ladr_next_rq3_wqe_out;
+
+	hret = ehea_hcall_9arg_9ret(H_DISABLE_AND_GET_HEA,
+				    adapter_handle,		/* R4 */
+				    H_DISABLE_GET_EHEA_WQE_P,	/* R5 */
+				    qp_handle,			/* R6 */
+				    0, 0, 0, 0, 0, 0,		/* R7-R12 */
+				    &ladr_next_sq_wqe_out,	/* R4 */
+				    &ladr_next_rq1_wqe_out,	/* R5 */
+				    &ladr_next_rq2_wqe_out,	/* R6 */
+				    &ladr_next_rq3_wqe_out,	/* R7 */
+				    &dummy,			/* R8 */
+				    &dummy,			/* R9 */
+				    &dummy,			/* R10 */
+				    &dummy,                     /* R11 */
+				    &dummy);                    /* R12 */
+	return hret;
+}
+
+u64 ehea_h_free_resource(const u64 adapter_handle, const u64 res_handle)
+{
+	u64 dummy;
+
+	return ehea_hcall_9arg_9ret(H_FREE_RESOURCE,
+				    adapter_handle,	   /* R4 */
+				    res_handle,            /* R5 */
+				    0, 0, 0, 0, 0, 0, 0,   /* R6-R12 */
+				    &dummy,                /* R4 */
+				    &dummy,                /* R5 */
+				    &dummy,                /* R6 */
+				    &dummy,                /* R7 */
+				    &dummy,                /* R8 */
+				    &dummy,                /* R9 */
+				    &dummy,		   /* R10 */
+				    &dummy,                /* R11 */
+				    &dummy);               /* R12 */
+}
+
+u64 ehea_h_alloc_resource_mr(const u64 adapter_handle, const u64 vaddr,
+			     const u64 length, const u32 access_ctrl,
+			     const u32 pd, u64 *mr_handle, u32 *lkey)
+{
+	u64 hret, dummy, lkey_out;
+
+	hret = ehea_hcall_9arg_9ret(H_ALLOC_HEA_RESOURCE,
+				    adapter_handle,		   /* R4 */
+				    5,				   /* R5 */
+				    vaddr,			   /* R6 */
+				    length,			   /* R7 */
+				    (((u64) access_ctrl) << 32ULL),/* R8 */
+				    pd,				   /* R9 */
+				    0, 0, 0,			   /* R10-R12 */
+				    mr_handle,			   /* R4 */
+				    &dummy,			   /* R5 */
+				    &lkey_out,			   /* R6 */
+				    &dummy,			   /* R7 */
+				    &dummy,			   /* R8 */
+				    &dummy,			   /* R9 */
+				    &dummy,			   /* R10 */
+				    &dummy,                        /* R11 */
+				    &dummy);                       /* R12 */
+	*lkey = (u32) lkey_out;
+
+	return hret;
+}
+
+u64 ehea_h_register_rpage_mr(const u64 adapter_handle, const u64 mr_handle,
+			     const u8 pagesize, const u8 queue_type,
+			     const u64 log_pageaddr, const u64 count)
+{
+	if ((count > 1) && (log_pageaddr & 0xfff)) {
+		ehea_error("not on pageboundary");
+		return H_PARAMETER;
+	}
+
+	return ehea_h_register_rpage(adapter_handle, pagesize,
+				     queue_type, mr_handle,
+				     log_pageaddr, count);
+}
+
+u64 ehea_h_query_ehea(const u64 adapter_handle, void *cb_addr)
+{
+	u64 hret, dummy, cb_logaddr;
+
+	cb_logaddr = virt_to_abs(cb_addr);
+
+	hret = ehea_hcall_9arg_9ret(H_QUERY_HEA,
+				    adapter_handle,		/* R4 */
+				    cb_logaddr,			/* R5 */
+				    0, 0, 0, 0, 0, 0, 0,	/* R6-R12 */
+				    &dummy,			/* R4 */
+				    &dummy,			/* R5 */
+				    &dummy,			/* R6 */
+				    &dummy,			/* R7 */
+				    &dummy,			/* R8 */
+				    &dummy,			/* R9 */
+				    &dummy,			/* R10 */
+				    &dummy,             	/* R11 */
+				    &dummy);            	/* R12 */
+#ifdef DEBUG
+	ehea_dmp(cb_addr, sizeof(struct hcp_query_ehea), "hcp_query_ehea");
+#endif
+	return hret;
+}
+
+u64 ehea_h_query_ehea_port(const u64 adapter_handle, const u16 port_num,
+			   const u8 cb_cat, const u64 select_mask,
+			   void *cb_addr)
+{
+	u64 port_info, dummy;
+	u64 cb_logaddr = virt_to_abs(cb_addr);
+	u64 arr_index = 0;
+
+	port_info = EHEA_BMASK_SET(H_MEHEAPORT_CAT, cb_cat)
+		  | EHEA_BMASK_SET(H_MEHEAPORT_PN, port_num);
+
+	return ehea_hcall_9arg_9ret(H_QUERY_HEA_PORT,
+				    adapter_handle,		/* R4 */
+				    port_info,			/* R5 */
+				    select_mask,		/* R6 */
+				    arr_index,			/* R7 */
+				    cb_logaddr,			/* R8 */
+				    0, 0, 0, 0,			/* R9-R12 */
+				    &dummy,			/* R4 */
+				    &dummy,			/* R5 */
+				    &dummy,			/* R6 */
+				    &dummy,			/* R7 */
+				    &dummy,			/* R8 */
+				    &dummy,			/* R9 */
+				    &dummy,			/* R10 */
+				    &dummy,                     /* R11 */
+				    &dummy);                    /* R12 */
+}
+
+u64 ehea_h_modify_ehea_port(const u64 adapter_handle, const u16 port_num,
+			    const u8 cb_cat, const u64 select_mask,
+			    void *cb_addr)
+{
+	u64 port_info, dummy, inv_attr_ident, proc_mask;
+	u64 arr_index = 0;
+	u64 cb_logaddr = virt_to_abs(cb_addr);
+
+	port_info = EHEA_BMASK_SET(H_MEHEAPORT_CAT, cb_cat)
+		  | EHEA_BMASK_SET(H_MEHEAPORT_PN, port_num);
+#ifdef DEBUG
+	ehea_dump(cb_addr, sizeof(struct hcp_ehea_port_cb0), "Before HCALL");
+#endif
+	return ehea_hcall_9arg_9ret(H_MODIFY_HEA_PORT,
+				    adapter_handle,		/* R4 */
+				    port_info,			/* R5 */
+				    select_mask,		/* R6 */
+				    arr_index,			/* R7 */
+				    cb_logaddr,			/* R8 */
+				    0, 0, 0, 0,			/* R9-R12 */
+				    &inv_attr_ident,		/* R4 */
+				    &proc_mask,			/* R5 */
+				    &dummy,			/* R6 */
+				    &dummy,			/* R7 */
+				    &dummy,			/* R8 */
+				    &dummy,			/* R9 */
+				    &dummy,			/* R10 */
+				    &dummy,                     /* R11 */
+				    &dummy);                    /* R12 */
+}
+
+u64 ehea_h_reg_dereg_bcmc(const u64 adapter_handle, const u16 port_num,
+			  const u8 reg_type, const u64 mc_mac_addr,
+			  const u16 vlan_id, const u32 hcall_id)
+{
+	u64 r5_port_num, r6_reg_type, r7_mc_mac_addr, r8_vlan_id, dummy;
+	u64 mac_addr = mc_mac_addr >> 16;
+
+	r5_port_num = EHEA_BMASK_SET(H_REGBCMC_PN, port_num);
+	r6_reg_type = EHEA_BMASK_SET(H_REGBCMC_REGTYPE, reg_type);
+	r7_mc_mac_addr = EHEA_BMASK_SET(H_REGBCMC_MACADDR, mac_addr);
+	r8_vlan_id = EHEA_BMASK_SET(H_REGBCMC_VLANID, vlan_id);
+
+	return ehea_hcall_9arg_9ret(hcall_id,
+				    adapter_handle,		/* R4 */
+				    r5_port_num,		/* R5 */
+				    r6_reg_type,		/* R6 */
+				    r7_mc_mac_addr,		/* R7 */
+				    r8_vlan_id,			/* R8 */
+				    0, 0, 0, 0,			/* R9-R12 */
+				    &dummy,			/* R4 */
+				    &dummy,			/* R5 */
+				    &dummy,			/* R6 */
+				    &dummy,			/* R7 */
+				    &dummy,			/* R8 */
+				    &dummy,			/* R9 */
+				    &dummy,			/* R10 */
+				    &dummy,                     /* R11 */
+				    &dummy);                    /* R12 */
+}
+
+u64 ehea_h_reset_events(const u64 adapter_handle, const u64 neq_handle,
+			const u64 event_mask)
+{
+	u64 dummy;
+
+	return ehea_hcall_9arg_9ret(H_RESET_EVENTS,
+				    adapter_handle,		/* R4 */
+				    neq_handle,			/* R5 */
+				    event_mask,			/* R6 */
+				    0, 0, 0, 0, 0, 0,		/* R7-R12 */
+				    &dummy,			/* R4 */
+				    &dummy,			/* R5 */
+				    &dummy,			/* R6 */
+				    &dummy,			/* R7 */
+				    &dummy,			/* R8 */
+				    &dummy,			/* R9 */
+				    &dummy,			/* R10 */
+				    &dummy,                     /* R11 */
+				    &dummy);                    /* R12 */
+}
--- linux-2.6.18-rc6-orig/drivers/net/ehea/ehea_phyp.h	1970-01-01 01:00:00.000000000 +0100
+++ kernel/drivers/net/ehea/ehea_phyp.h	2006-09-06 15:53:42.000000000 +0200
@@ -0,0 +1,454 @@
+/*
+ *  linux/drivers/net/ehea/ehea_phyp.h
+ *
+ *  eHEA ethernet device driver for IBM eServer System p
+ *
+ *  (C) Copyright IBM Corp. 2006
+ *
+ *  Authors:
+ *       Christoph Raisch <raisch@de.ibm.com>
+ *       Jan-Bernd Themann <themann@de.ibm.com>
+ *       Thomas Klein <tklein@de.ibm.com>
+ *
+ *
+ * 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, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.	 See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#ifndef __EHEA_PHYP_H__
+#define __EHEA_PHYP_H__
+
+#include <linux/delay.h>
+#include <asm/hvcall.h>
+#include "ehea.h"
+#include "ehea_hw.h"
+#include "ehea_hcall.h"
+
+/* Some abbreviations used here:
+ *
+ * hcp_*  - structures, variables and functions releated to Hypervisor Calls
+ */
+
+static inline u32 get_longbusy_msecs(int long_busy_ret_code)
+{
+	switch (long_busy_ret_code) {
+	case H_LONG_BUSY_ORDER_1_MSEC:
+		return 1;
+	case H_LONG_BUSY_ORDER_10_MSEC:
+		return 10;
+	case H_LONG_BUSY_ORDER_100_MSEC:
+		return 100;
+	case H_LONG_BUSY_ORDER_1_SEC:
+		return 1000;
+	case H_LONG_BUSY_ORDER_10_SEC:
+		return 10000;
+	case H_LONG_BUSY_ORDER_100_SEC:
+		return 100000;
+	default:
+		return 1;
+	}
+}
+
+/* Notification Event Queue (NEQ) Entry bit masks */
+#define NEQE_EVENT_CODE		EHEA_BMASK_IBM(2, 7)
+#define NEQE_PORTNUM  		EHEA_BMASK_IBM(32, 47)
+#define NEQE_PORT_UP		EHEA_BMASK_IBM(16, 16)
+#define NEQE_EXTSWITCH_PORT_UP	EHEA_BMASK_IBM(17, 17)
+#define NEQE_EXTSWITCH_PRIMARY	EHEA_BMASK_IBM(18, 18)
+#define NEQE_PLID		EHEA_BMASK_IBM(16, 47)
+
+/* Notification Event Codes */
+#define EHEA_EC_PORTSTATE_CHG	0x30
+#define EHEA_EC_ADAPTER_MALFUNC	0x32
+#define EHEA_EC_PORT_MALFUNC	0x33
+
+/* Notification Event Log Register (NELR) bit masks */
+#define NELR_PORT_MALFUNC	EHEA_BMASK_IBM(61, 61)
+#define NELR_ADAPTER_MALFUNC	EHEA_BMASK_IBM(62, 62)
+#define NELR_PORTSTATE_CHG	EHEA_BMASK_IBM(63, 63)
+
+static inline void hcp_epas_ctor(struct h_epas *epas, u64 paddr_kernel,
+				 u64 paddr_user)
+{
+	epas->kernel.addr = (u64)ioremap(paddr_kernel, PAGE_SIZE);
+	epas->user.addr = paddr_user;
+}
+
+static inline void hcp_epas_dtor(struct h_epas *epas)
+{
+	if (epas->kernel.addr)
+		iounmap((void*)epas->kernel.addr);
+
+	epas->user.addr = epas->kernel.addr = 0;
+}
+
+struct hcp_modify_qp_cb0 {
+	u64 qp_ctl_reg;		/* 00 */
+	u32 max_swqe;		/* 02 */
+	u32 max_rwqe;		/* 03 */
+	u32 port_nb;		/* 04 */
+	u32 reserved0;		/* 05 */
+	u64 qp_aer;		/* 06 */
+	u64 qp_tenure;		/* 08 */
+};
+
+/* Hcall Query/Modify Queue Pair Control Block 0 Selection Mask Bits */
+#define H_QPCB0_ALL             EHEA_BMASK_IBM(0, 5)
+#define H_QPCB0_QP_CTL_REG      EHEA_BMASK_IBM(0, 0)
+#define H_QPCB0_MAX_SWQE        EHEA_BMASK_IBM(1, 1)
+#define H_QPCB0_MAX_RWQE        EHEA_BMASK_IBM(2, 2)
+#define H_QPCB0_PORT_NB         EHEA_BMASK_IBM(3, 3)
+#define H_QPCB0_QP_AER          EHEA_BMASK_IBM(4, 4)
+#define H_QPCB0_QP_TENURE       EHEA_BMASK_IBM(5, 5)
+
+/* Queue Pair Control Register Status Bits */
+#define H_QP_CR_ENABLED		    0x8000000000000000ULL /* QP enabled */
+							  /* QP States: */
+#define H_QP_CR_STATE_RESET	    0x0000010000000000ULL /*  Reset */
+#define H_QP_CR_STATE_INITIALIZED   0x0000020000000000ULL /*  Initialized */
+#define H_QP_CR_STATE_RDY2RCV	    0x0000030000000000ULL /*  Ready to recv */
+#define H_QP_CR_STATE_RDY2SND	    0x0000050000000000ULL /*  Ready to send */
+#define H_QP_CR_STATE_ERROR	    0x0000800000000000ULL /*  Error */
+
+struct hcp_modify_qp_cb1 {
+	u32 qpn;		/* 00 */
+	u32 qp_asyn_ev_eq_nb;	/* 01 */
+	u64 sq_cq_handle;	/* 02 */
+	u64 rq_cq_handle;	/* 04 */
+	/* sgel = scatter gather element */
+	u32 sgel_nb_sq;		/* 06 */
+	u32 sgel_nb_rq1;	/* 07 */
+	u32 sgel_nb_rq2;	/* 08 */
+	u32 sgel_nb_rq3;	/* 09 */
+};
+
+/* Hcall Query/Modify Queue Pair Control Block 1 Selection Mask Bits */
+#define H_QPCB1_ALL             EHEA_BMASK_IBM(0, 7)
+#define H_QPCB1_QPN             EHEA_BMASK_IBM(0, 0)
+#define H_QPCB1_ASYN_EV_EQ_NB   EHEA_BMASK_IBM(1, 1)
+#define H_QPCB1_SQ_CQ_HANDLE    EHEA_BMASK_IBM(2, 2)
+#define H_QPCB1_RQ_CQ_HANDLE    EHEA_BMASK_IBM(3, 3)
+#define H_QPCB1_SGEL_NB_SQ      EHEA_BMASK_IBM(4, 4)
+#define H_QPCB1_SGEL_NB_RQ1     EHEA_BMASK_IBM(5, 5)
+#define H_QPCB1_SGEL_NB_RQ2     EHEA_BMASK_IBM(6, 6)
+#define H_QPCB1_SGEL_NB_RQ3     EHEA_BMASK_IBM(7, 7)
+
+struct hcp_query_ehea {
+	u32 cur_num_qps;		/* 00 */
+	u32 cur_num_cqs;		/* 01 */
+	u32 cur_num_eqs;		/* 02 */
+	u32 cur_num_mrs;		/* 03 */
+	u32 auth_level;			/* 04 */
+	u32 max_num_qps;		/* 05 */
+	u32 max_num_cqs;		/* 06 */
+	u32 max_num_eqs;		/* 07 */
+	u32 max_num_mrs;		/* 08 */
+	u32 reserved0;			/* 09 */
+	u32 int_clock_freq;		/* 10 */
+	u32 max_num_pds;		/* 11 */
+	u32 max_num_addr_handles;	/* 12 */
+	u32 max_num_cqes;		/* 13 */
+	u32 max_num_wqes;		/* 14 */
+	u32 max_num_sgel_rq1wqe;	/* 15 */
+	u32 max_num_sgel_rq2wqe;	/* 16 */
+	u32 max_num_sgel_rq3wqe;	/* 17 */
+	u32 mr_page_size;		/* 18 */
+	u32 reserved1;			/* 19 */
+	u64 max_mr_size;		/* 20 */
+	u64 reserved2;			/* 22 */
+	u32 num_ports;			/* 24 */
+	u32 reserved3;			/* 25 */
+	u32 reserved4;			/* 26 */
+	u32 reserved5;			/* 27 */
+	u64 max_mc_mac;			/* 28 */
+	u64 ehea_cap;			/* 30 */
+	u32 max_isn_per_eq;		/* 32 */
+	u32 max_num_neq;		/* 33 */
+	u64 max_num_vlan_ids;		/* 34 */
+	u32 max_num_port_group;		/* 36 */
+	u32 max_num_phys_port;		/* 37 */
+
+};
+
+/* Hcall Query/Modify Port Control Block defines */
+#define H_PORT_CB0	 0
+#define H_PORT_CB1	 1
+#define H_PORT_CB2	 2
+#define H_PORT_CB3	 3
+#define H_PORT_CB4	 4
+#define H_PORT_CB5	 5
+#define H_PORT_CB6	 6
+#define H_PORT_CB7	 7
+
+struct hcp_ehea_port_cb0 {
+	u64 port_mac_addr;
+	u64 port_rc;
+	u64 reserved0;
+	u32 port_op_state;
+	u32 port_speed;
+	u32 ext_swport_op_state;
+	u32 neg_tpf_prpf;
+	u32 num_default_qps;
+	u32 reserved1;
+	u64 default_qpn_arr[16];
+};
+
+/* Hcall Query/Modify Port Control Block 0 Selection Mask Bits */
+#define H_PORT_CB0_ALL		EHEA_BMASK_IBM(0, 7)    /* Set all bits */
+#define H_PORT_CB0_MAC		EHEA_BMASK_IBM(0, 0)    /* MAC address */
+#define H_PORT_CB0_PRC		EHEA_BMASK_IBM(1, 1)    /* Port Recv Control */
+#define H_PORT_CB0_DEFQPNARRAY	EHEA_BMASK_IBM(7, 7)    /* Default QPN Array */
+
+/*  Hcall Query Port: Returned port speed values */
+#define H_SPEED_10M_H	1	/*  10 Mbps, Half Duplex */
+#define H_SPEED_10M_F	2	/*  10 Mbps, Full Duplex */
+#define H_SPEED_100M_H	3	/* 100 Mbps, Half Duplex */
+#define H_SPEED_100M_F	4	/* 100 Mbps, Full Duplex */
+#define H_SPEED_1G_F	6	/*   1 Gbps, Full Duplex */
+#define H_SPEED_10G_F	8	/*  10 Gbps, Full Duplex */
+
+/* Port Receive Control Status Bits */
+#define PXLY_RC_VALID           EHEA_BMASK_IBM(49, 49)
+#define PXLY_RC_VLAN_XTRACT     EHEA_BMASK_IBM(50, 50)
+#define PXLY_RC_TCP_6_TUPLE     EHEA_BMASK_IBM(51, 51)
+#define PXLY_RC_UDP_6_TUPLE     EHEA_BMASK_IBM(52, 52)
+#define PXLY_RC_TCP_3_TUPLE     EHEA_BMASK_IBM(53, 53)
+#define PXLY_RC_TCP_2_TUPLE     EHEA_BMASK_IBM(54, 54)
+#define PXLY_RC_LLC_SNAP        EHEA_BMASK_IBM(55, 55)
+#define PXLY_RC_JUMBO_FRAME     EHEA_BMASK_IBM(56, 56)
+#define PXLY_RC_FRAG_IP_PKT     EHEA_BMASK_IBM(57, 57)
+#define PXLY_RC_TCP_UDP_CHKSUM  EHEA_BMASK_IBM(58, 58)
+#define PXLY_RC_IP_CHKSUM       EHEA_BMASK_IBM(59, 59)
+#define PXLY_RC_MAC_FILTER      EHEA_BMASK_IBM(60, 60)
+#define PXLY_RC_UNTAG_FILTER    EHEA_BMASK_IBM(61, 61)
+#define PXLY_RC_VLAN_TAG_FILTER EHEA_BMASK_IBM(62, 63)
+
+#define PXLY_RC_VLAN_FILTER     2
+#define PXLY_RC_VLAN_PERM       0
+
+
+#define H_PORT_CB1_ALL          0x8000000000000000ULL
+
+struct hcp_ehea_port_cb1 {
+	u64 vlan_filter[64];
+};
+
+#define H_PORT_CB2_ALL          0xFFE0000000000000ULL
+
+struct hcp_ehea_port_cb2 {
+	u64 rxo;
+	u64 rxucp;
+	u64 rxufd;
+	u64 rxuerr;
+	u64 rxftl;
+	u64 rxmcp;
+	u64 rxbcp;
+	u64 txo;
+	u64 txucp;
+	u64 txmcp;
+	u64 txbcp;
+};
+
+struct hcp_ehea_port_cb3 {
+	u64 vlan_bc_filter[64];
+	u64 vlan_mc_filter[64];
+	u64 vlan_un_filter[64];
+	u64 port_mac_hash_array[64];
+};
+
+#define H_PORT_CB4_ALL          0xF000000000000000ULL
+#define H_PORT_CB4_JUMBO        0x1000000000000000ULL
+#define H_PORT_CB4_SPEED        0x8000000000000000ULL
+
+struct hcp_ehea_port_cb4 {
+	u32 port_speed;
+	u32 pause_frame;
+	u32 ens_port_op_state;
+	u32 jumbo_frame;
+	u32 ens_port_wrap;
+};
+
+/* Hcall Query/Modify Port Control Block 5 Selection Mask Bits */
+#define H_PORT_CB5_RCU		0x0001000000000000ULL
+#define PXS_RCU			EHEA_BMASK_IBM(61, 63)
+
+struct hcp_ehea_port_cb5 {
+	u64 prc;	        /* 00 */
+	u64 uaa;		/* 01 */
+	u64 macvc;		/* 02 */
+	u64 xpcsc;		/* 03 */
+	u64 xpcsp;		/* 04 */
+	u64 pcsid;		/* 05 */
+	u64 xpcsst;		/* 06 */
+	u64 pthlb;		/* 07 */
+	u64 pthrb;		/* 08 */
+	u64 pqu;		/* 09 */
+	u64 pqd;		/* 10 */
+	u64 prt;		/* 11 */
+	u64 wsth;		/* 12 */
+	u64 rcb;		/* 13 */
+	u64 rcm;		/* 14 */
+	u64 rcu;		/* 15 */
+	u64 macc;		/* 16 */
+	u64 pc;			/* 17 */
+	u64 pst;		/* 18 */
+	u64 ducqpn;		/* 19 */
+	u64 mcqpn;		/* 20 */
+	u64 mma;		/* 21 */
+	u64 pmc0h;		/* 22 */
+	u64 pmc0l;		/* 23 */
+	u64 lbc;		/* 24 */
+};
+
+#define H_PORT_CB6_ALL  0xFFFFFE7FFFFF8000ULL
+
+struct hcp_ehea_port_cb6 {
+	u64 rxo;		/* 00 */
+	u64 rx64;		/* 01 */
+	u64 rx65;		/* 02 */
+	u64 rx128;		/* 03 */
+	u64 rx256;		/* 04 */
+	u64 rx512;		/* 05 */
+	u64 rx1024;		/* 06 */
+	u64 rxbfcs;		/* 07 */
+	u64 rxime;		/* 08 */
+	u64 rxrle;		/* 09 */
+	u64 rxorle;		/* 10 */
+	u64 rxftl;		/* 11 */
+	u64 rxjab;		/* 12 */
+	u64 rxse;		/* 13 */
+	u64 rxce;		/* 14 */
+	u64 rxrf;		/* 15 */
+	u64 rxfrag;		/* 16 */
+	u64 rxuoc;		/* 17 */
+	u64 rxcpf;		/* 18 */
+	u64 rxsb;		/* 19 */
+	u64 rxfd;		/* 20 */
+	u64 rxoerr;		/* 21 */
+	u64 rxaln;		/* 22 */
+	u64 ducqpn;		/* 23 */
+	u64 reserved0;		/* 24 */
+	u64 rxmcp;		/* 25 */
+	u64 rxbcp;		/* 26 */
+	u64 txmcp;		/* 27 */
+	u64 txbcp;		/* 28 */
+	u64 txo;		/* 29 */
+	u64 tx64;		/* 30 */
+	u64 tx65;		/* 31 */
+	u64 tx128;		/* 32 */
+	u64 tx256;		/* 33 */
+	u64 tx512;		/* 34 */
+	u64 tx1024;		/* 35 */
+	u64 txbfcs;		/* 36 */
+	u64 txcpf;		/* 37 */
+	u64 txlf;		/* 38 */
+	u64 txrf;		/* 39 */
+	u64 txime;		/* 40 */
+	u64 txsc;		/* 41 */
+	u64 txmc;		/* 42 */
+	u64 txsqe;		/* 43 */
+	u64 txdef;		/* 44 */
+	u64 txlcol;		/* 45 */
+	u64 txexcol;		/* 46 */
+	u64 txcse;		/* 47 */
+	u64 txbor;		/* 48 */
+};
+
+#define H_PORT_CB7_DUCQPN 0x8000000000000000ULL
+
+struct hcp_ehea_port_cb7 {
+	u64 def_uc_qpn;
+};
+
+u64 ehea_h_query_ehea_qp(const u64 adapter_handle,
+			 const u8 qp_category,
+			 const u64 qp_handle, const u64 sel_mask,
+			 void *cb_addr);
+
+u64 ehea_h_modify_ehea_qp(const u64 adapter_handle,
+			  const u8 cat,
+			  const u64 qp_handle,
+			  const u64 sel_mask,
+			  void *cb_addr,
+			  u64 * inv_attr_id,
+			  u64 * proc_mask, u16 * out_swr, u16 * out_rwr);
+
+u64 ehea_h_alloc_resource_eq(const u64 adapter_handle,
+			     struct ehea_eq_attr *eq_attr, u64 * eq_handle);
+
+u64 ehea_h_alloc_resource_cq(const u64 adapter_handle,
+			     struct ehea_cq_attr *cq_attr,
+			     u64 * cq_handle, struct h_epas *epas);
+
+u64 ehea_h_alloc_resource_qp(const u64 adapter_handle,
+			     struct ehea_qp_init_attr *init_attr,
+			     const u32 pd,
+			     u64 * qp_handle, struct h_epas *h_epas);
+
+#define H_REG_RPAGE_PAGE_SIZE          EHEA_BMASK_IBM(48,55)
+#define H_REG_RPAGE_QT                 EHEA_BMASK_IBM(62,63)
+
+u64 ehea_h_register_rpage(const u64 adapter_handle,
+			  const u8 pagesize,
+			  const u8 queue_type,
+			  const u64 resource_handle,
+			  const u64 log_pageaddr, u64 count);
+
+#define H_DISABLE_GET_EHEA_WQE_P  1
+#define H_DISABLE_GET_SQ_WQE_P    2
+#define H_DISABLE_GET_RQC         3
+
+u64 ehea_h_disable_and_get_hea(const u64 adapter_handle, const u64 qp_handle);
+
+u64 ehea_h_free_resource(const u64 adapter_handle, const u64 res_handle);
+
+u64 ehea_h_alloc_resource_mr(const u64 adapter_handle, const u64 vaddr,
+			     const u64 length, const u32 access_ctrl,
+			     const u32 pd, u64 * mr_handle, u32 * lkey);
+
+u64 ehea_h_register_rpage_mr(const u64 adapter_handle, const u64 mr_handle,
+			     const u8 pagesize, const u8 queue_type,
+			     const u64 log_pageaddr, const u64 count);
+
+u64 ehea_h_register_smr(const u64 adapter_handle, const u64 orig_mr_handle,
+			const u64 vaddr_in, const u32 access_ctrl, const u32 pd,
+			struct ehea_mr *mr);
+
+u64 ehea_h_query_ehea(const u64 adapter_handle, void *cb_addr);
+
+/* output param R5 */
+#define H_MEHEAPORT_CAT		EHEA_BMASK_IBM(40,47)
+#define H_MEHEAPORT_PN		EHEA_BMASK_IBM(48,63)
+
+u64 ehea_h_query_ehea_port(const u64 adapter_handle, const u16 port_num,
+			   const u8 cb_cat, const u64 select_mask,
+			   void *cb_addr);
+
+u64 ehea_h_modify_ehea_port(const u64 adapter_handle, const u16 port_num,
+			    const u8 cb_cat, const u64 select_mask,
+			    void *cb_addr);
+
+#define H_REGBCMC_PN            EHEA_BMASK_IBM(48, 63)
+#define H_REGBCMC_REGTYPE       EHEA_BMASK_IBM(61, 63)
+#define H_REGBCMC_MACADDR       EHEA_BMASK_IBM(16, 63)
+#define H_REGBCMC_VLANID        EHEA_BMASK_IBM(52, 63)
+
+u64 ehea_h_reg_dereg_bcmc(const u64 adapter_handle, const u16 port_num,
+			  const u8 reg_type, const u64 mc_mac_addr,
+			  const u16 vlan_id, const u32 hcall_id);
+
+u64 ehea_h_reset_events(const u64 adapter_handle, const u64 neq_handle,
+			const u64 event_mask);
+
+#endif	/* __EHEA_PHYP_H__ */
--- linux-2.6.18-rc6-orig/drivers/net/ehea/ehea_hcall.h	1970-01-01 01:00:00.000000000 +0100
+++ kernel/drivers/net/ehea/ehea_hcall.h	2006-09-06 15:53:41.000000000 +0200
@@ -0,0 +1,51 @@
+/*
+ *  linux/drivers/net/ehea/ehea_hcall.h
+ *
+ *  eHEA ethernet device driver for IBM eServer System p
+ *
+ *  (C) Copyright IBM Corp. 2006
+ *
+ *  Authors:
+ *       Christoph Raisch <raisch@de.ibm.com>
+ *       Jan-Bernd Themann <themann@de.ibm.com>
+ *       Thomas Klein <tklein@de.ibm.com>
+ *
+ *
+ * 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, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.	 See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#ifndef __EHEA_HCALL_H__
+#define __EHEA_HCALL_H__
+
+/**
+ * This file contains HCALL defines that are to be included in the appropriate
+ * kernel files later
+ */
+
+#define H_ALLOC_HEA_RESOURCE   0x278
+#define H_MODIFY_HEA_QP        0x250
+#define H_QUERY_HEA_QP         0x254
+#define H_QUERY_HEA            0x258
+#define H_QUERY_HEA_PORT       0x25C
+#define H_MODIFY_HEA_PORT      0x260
+#define H_REG_BCMC             0x264
+#define H_DEREG_BCMC           0x268
+#define H_REGISTER_HEA_RPAGES  0x26C
+#define H_DISABLE_AND_GET_HEA  0x270
+#define H_GET_HEA_INFO         0x274
+#define H_ADD_CONN             0x284
+#define H_DEL_CONN             0x288
+
+#endif	/* __EHEA_HCALL_H__ */

^ permalink raw reply

* [2.6.19 PATCH 3/7] ehea: queue management
From: Jan-Bernd Themann @ 2006-09-06 13:32 UTC (permalink / raw)
  To: netdev
  Cc: Thomas Klein, Jan-Bernd Themann, linux-kernel, linux-ppc,
	Christoph Raisch, Marcus Eder

Signed-off-by: Jan-Bernd Themann <themann@de.ibm.com> 


 drivers/net/ehea/ehea_qmr.c |  604 ++++++++++++++++++++++++++++++++++++++++++++
 drivers/net/ehea/ehea_qmr.h |  362 ++++++++++++++++++++++++++
 2 files changed, 966 insertions(+)



--- linux-2.6.18-rc6-orig/drivers/net/ehea/ehea_qmr.c	1970-01-01 01:00:00.000000000 +0100
+++ kernel/drivers/net/ehea/ehea_qmr.c	2006-09-06 15:53:42.000000000 +0200
@@ -0,0 +1,604 @@
+/*
+ *  linux/drivers/net/ehea/ehea_qmr.c
+ *
+ *  eHEA ethernet device driver for IBM eServer System p
+ *
+ *  (C) Copyright IBM Corp. 2006
+ *
+ *  Authors:
+ *       Christoph Raisch <raisch@de.ibm.com>
+ *       Jan-Bernd Themann <themann@de.ibm.com>
+ *       Thomas Klein <tklein@de.ibm.com>
+ *
+ *
+ * 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, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.	 See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include "ehea.h"
+#include "ehea_phyp.h"
+#include "ehea_qmr.h"
+
+static void *hw_qpageit_get_inc(struct hw_queue *queue)
+{
+	void *retvalue = hw_qeit_get(queue);
+
+	queue->current_q_offset += queue->pagesize;
+	if (queue->current_q_offset > queue->queue_length) {
+		queue->current_q_offset -= queue->pagesize;
+		retvalue = NULL;
+	} else if (((u64) retvalue) & (EHEA_PAGESIZE-1)) {
+		ehea_error("not on pageboundary");
+		retvalue = NULL;
+	}
+	return retvalue;
+}
+
+static int hw_queue_ctor(struct hw_queue *queue, const u32 nr_of_pages,
+			  const u32 pagesize, const u32 qe_size)
+{
+	int pages_per_kpage = PAGE_SIZE / pagesize;
+	int i, k;
+
+	if ((pagesize > PAGE_SIZE) || (!pages_per_kpage)) {
+		ehea_error("pagesize conflict! kernel pagesize=%d, "
+			   "ehea pagesize=%d", (int)PAGE_SIZE, (int)pagesize);
+		return -EINVAL;
+	}
+
+	queue->queue_length = nr_of_pages * pagesize;
+	queue->queue_pages = kmalloc(nr_of_pages * sizeof(void*), GFP_KERNEL);
+	if (!queue->queue_pages) {
+		ehea_error("no mem for queue_pages");
+		return -ENOMEM;
+	}
+
+	/*
+	 * allocate pages for queue:
+	 * outer loop allocates whole kernel pages (page aligned) and
+	 * inner loop divides a kernel page into smaller hea queue pages
+	 */
+	i = 0;
+	while (i < nr_of_pages) {
+		u8 *kpage = (u8*)get_zeroed_page(GFP_KERNEL);
+		if (!kpage)
+			goto out_nomem;
+		for (k = 0; k < pages_per_kpage && i < nr_of_pages; k++) {
+			(queue->queue_pages)[i] = (struct ehea_page*)kpage;
+			kpage += pagesize;
+			i++;
+		}
+	}
+
+	queue->current_q_offset = 0;
+	queue->qe_size = qe_size;
+	queue->pagesize = pagesize;
+	queue->toggle_state = 1;
+
+	return 0;
+out_nomem:
+	for (i = 0; i < nr_of_pages; i += pages_per_kpage) {
+		if (!(queue->queue_pages)[i])
+			break;
+		free_page((unsigned long)(queue->queue_pages)[i]);
+	}
+	return -ENOMEM;
+}
+
+static void hw_queue_dtor(struct hw_queue *queue)
+{
+	int pages_per_kpage = PAGE_SIZE / queue->pagesize;
+	int i, nr_pages;
+
+	if (!queue || !queue->queue_pages)
+		return;
+
+	nr_pages = queue->queue_length / queue->pagesize;
+
+	for (i = 0; i < nr_pages; i += pages_per_kpage)
+		free_page((unsigned long)(queue->queue_pages)[i]);
+
+	kfree(queue->queue_pages);
+}
+
+struct ehea_cq *ehea_create_cq(struct ehea_adapter *adapter,
+			       int nr_of_cqe, u64 eq_handle, u32 cq_token)
+{
+	struct ehea_cq *cq;
+	struct h_epa epa;
+	u64 *cq_handle_ref, hret, rpage;
+	u32 act_nr_of_entries, act_pages, counter;
+	int ret;
+	void *vpage;
+
+	cq = kzalloc(sizeof(*cq), GFP_KERNEL);
+	if (!cq) {
+		ehea_error("no mem for cq");
+		goto out_nomem;
+	}
+
+	cq->attr.max_nr_of_cqes = nr_of_cqe;
+	cq->attr.cq_token = cq_token;
+	cq->attr.eq_handle = eq_handle;
+
+	cq->adapter = adapter;
+
+	cq_handle_ref = &cq->fw_handle;
+	act_nr_of_entries = 0;
+	act_pages = 0;
+
+	hret = ehea_h_alloc_resource_cq(adapter->handle, &cq->attr,
+					&cq->fw_handle, &cq->epas);
+	if (hret != H_SUCCESS) {
+		ehea_error("alloc_resource_cq failed");
+		goto out_freemem;
+	}
+
+	ret = hw_queue_ctor(&cq->hw_queue, cq->attr.nr_pages,
+			    EHEA_PAGESIZE, sizeof(struct ehea_cqe));
+	if (ret)
+		goto out_freeres;
+
+	for (counter = 0; counter < cq->attr.nr_pages; counter++) {
+		vpage = hw_qpageit_get_inc(&cq->hw_queue);
+		if (!vpage) {
+			ehea_error("hw_qpageit_get_inc failed");
+			goto out_kill_hwq;
+		}
+
+		rpage = virt_to_abs(vpage);
+		hret = ehea_h_register_rpage(adapter->handle,
+					     0, EHEA_CQ_REGISTER_ORIG,
+					     cq->fw_handle, rpage, 1);
+		if (hret < H_SUCCESS) {
+			ehea_error("register_rpage_cq failed ehea_cq=%p "
+				   "hret=%lx counter=%i act_pages=%i",
+				   cq, hret, counter, cq->attr.nr_pages);
+			goto out_kill_hwq;
+		}
+
+		if (counter == (cq->attr.nr_pages - 1)) {
+			vpage = hw_qpageit_get_inc(&cq->hw_queue);
+
+			if ((hret != H_SUCCESS) || (vpage)) {
+				ehea_error("registration of pages not "
+					   "complete hret=%lx\n", hret);
+				goto out_kill_hwq;
+			}
+		} else {
+			if ((hret != H_PAGE_REGISTERED) || (!vpage)) {
+				ehea_error("CQ: registration of page failed "
+					   "hret=%lx\n", hret);
+				goto out_kill_hwq;
+			}
+		}
+	}
+
+	hw_qeit_reset(&cq->hw_queue);
+	epa = cq->epas.kernel;
+	ehea_reset_cq_ep(cq);
+	ehea_reset_cq_n1(cq);
+
+	return cq;
+
+out_kill_hwq:
+	hw_queue_dtor(&cq->hw_queue);
+
+out_freeres:
+	ehea_h_free_resource(adapter->handle, cq->fw_handle);
+
+out_freemem:
+	kfree(cq);
+
+out_nomem:
+	return NULL;
+}
+
+int ehea_destroy_cq(struct ehea_cq *cq)
+{
+	u64 adapter_handle, hret;
+
+	adapter_handle = cq->adapter->handle;
+
+	if (!cq)
+		return 0;
+
+	/* deregister all previous registered pages */
+	hret = ehea_h_free_resource(adapter_handle, cq->fw_handle);
+	if (hret != H_SUCCESS) {
+		ehea_error("destroy CQ failed");
+		return -EIO;
+	}
+
+	hw_queue_dtor(&cq->hw_queue);
+	kfree(cq);
+
+	return 0;
+}
+
+struct ehea_eq *ehea_create_eq(struct ehea_adapter *adapter,
+			       const enum ehea_eq_type type,
+			       const u32 max_nr_of_eqes, const u8 eqe_gen)
+{
+	int ret, i;
+	u64 hret, rpage;
+	void *vpage;
+	struct ehea_eq *eq;
+
+	eq = kzalloc(sizeof(*eq), GFP_KERNEL);
+	if (!eq) {
+		ehea_error("no mem for eq");
+		return NULL;
+	}
+
+	eq->adapter = adapter;
+	eq->attr.type = type;
+	eq->attr.max_nr_of_eqes = max_nr_of_eqes;
+	eq->attr.eqe_gen = eqe_gen;
+	spin_lock_init(&eq->spinlock);
+
+	hret = ehea_h_alloc_resource_eq(adapter->handle,
+					&eq->attr, &eq->fw_handle);
+	if (hret != H_SUCCESS) {
+		ehea_error("alloc_resource_eq failed");
+		goto out_freemem;
+	}
+
+	ret = hw_queue_ctor(&eq->hw_queue, eq->attr.nr_pages,
+			    EHEA_PAGESIZE, sizeof(struct ehea_eqe));
+	if (ret) {
+		ehea_error("can't allocate eq pages");
+		goto out_freeres;
+	}
+
+	for (i = 0; i < eq->attr.nr_pages; i++) {
+		vpage = hw_qpageit_get_inc(&eq->hw_queue);
+		if (!vpage) {
+			ehea_error("hw_qpageit_get_inc failed");
+			hret = H_RESOURCE;
+			goto out_kill_hwq;
+		}
+
+		rpage = virt_to_abs(vpage);
+
+		hret = ehea_h_register_rpage(adapter->handle, 0,
+					     EHEA_EQ_REGISTER_ORIG,
+					     eq->fw_handle, rpage, 1);
+
+		if (i == (eq->attr.nr_pages - 1)) {
+			/* last page */
+			vpage = hw_qpageit_get_inc(&eq->hw_queue);
+			if ((hret != H_SUCCESS) || (vpage)) {
+				goto out_kill_hwq;
+			}
+		} else {
+			if ((hret != H_PAGE_REGISTERED) || (!vpage)) {
+				goto out_kill_hwq;
+			}
+		}
+	}
+
+	hw_qeit_reset(&eq->hw_queue);
+	return eq;
+
+out_kill_hwq:
+	hw_queue_dtor(&eq->hw_queue);
+
+out_freeres:
+	ehea_h_free_resource(adapter->handle, eq->fw_handle);
+
+out_freemem:
+	kfree(eq);
+	return NULL;
+}
+
+struct ehea_eqe *ehea_poll_eq(struct ehea_eq *eq)
+{
+	struct ehea_eqe *eqe;
+	unsigned long flags;
+
+	spin_lock_irqsave(&eq->spinlock, flags);
+	eqe = (struct ehea_eqe*)hw_eqit_eq_get_inc_valid(&eq->hw_queue);
+	spin_unlock_irqrestore(&eq->spinlock, flags);
+
+	return eqe;
+}
+
+int ehea_destroy_eq(struct ehea_eq *eq)
+{
+	u64 hret;
+	unsigned long flags;
+
+	if (!eq)
+		return 0;
+
+	spin_lock_irqsave(&eq->spinlock, flags);
+
+	hret = ehea_h_free_resource(eq->adapter->handle, eq->fw_handle);
+	spin_unlock_irqrestore(&eq->spinlock, flags);
+
+	if (hret != H_SUCCESS) {
+		ehea_error("destroy_eq failed");
+		return -EIO;
+	}
+
+	hw_queue_dtor(&eq->hw_queue);
+	kfree(eq);
+
+	return 0;
+}
+
+/**
+ * allocates memory for a queue and registers pages in phyp
+ */
+int ehea_qp_alloc_register(struct ehea_qp *qp, struct hw_queue *hw_queue,
+			   int nr_pages, int wqe_size, int act_nr_sges,
+			   struct ehea_adapter *adapter, int h_call_q_selector)
+{
+	u64 hret, rpage;
+	int ret, cnt;
+	void *vpage;
+
+	ret = hw_queue_ctor(hw_queue, nr_pages, EHEA_PAGESIZE, wqe_size);
+	if (ret)
+		return ret;
+
+	for (cnt = 0; cnt < nr_pages; cnt++) {
+		vpage = hw_qpageit_get_inc(hw_queue);
+		if (!vpage) {
+			ehea_error("hw_qpageit_get_inc failed");
+			goto out_kill_hwq;
+		}
+		rpage = virt_to_abs(vpage);
+		hret = ehea_h_register_rpage(adapter->handle,
+					     0, h_call_q_selector,
+					     qp->fw_handle, rpage, 1);
+		if (hret < H_SUCCESS) {
+			ehea_error("register_rpage_qp failed");
+			goto out_kill_hwq;
+		}
+	}
+	hw_qeit_reset(hw_queue);
+	return 0;
+
+out_kill_hwq:
+	hw_queue_dtor(hw_queue);
+	return -EIO;
+}
+
+static inline u32 map_wqe_size(u8 wqe_enc_size)
+{
+	return 128 << wqe_enc_size;
+}
+
+struct ehea_qp *ehea_create_qp(struct ehea_adapter *adapter,
+			       u32 pd, struct ehea_qp_init_attr *init_attr)
+{
+	int ret;
+	u64 hret;
+	struct ehea_qp *qp;
+	u32 wqe_size_in_bytes_sq, wqe_size_in_bytes_rq1;
+	u32 wqe_size_in_bytes_rq2, wqe_size_in_bytes_rq3;
+
+
+	qp = kzalloc(sizeof(*qp), GFP_KERNEL);
+	if (!qp) {
+		ehea_error("no mem for qp");
+		return NULL;
+	}
+
+	qp->adapter = adapter;
+
+	hret = ehea_h_alloc_resource_qp(adapter->handle, init_attr, pd,
+					&qp->fw_handle, &qp->epas);
+	if (hret != H_SUCCESS) {
+		ehea_error("ehea_h_alloc_resource_qp failed");
+		goto out_freemem;
+	}
+
+	wqe_size_in_bytes_sq = map_wqe_size(init_attr->act_wqe_size_enc_sq);
+	wqe_size_in_bytes_rq1 = map_wqe_size(init_attr->act_wqe_size_enc_rq1);
+	wqe_size_in_bytes_rq2 = map_wqe_size(init_attr->act_wqe_size_enc_rq2);
+	wqe_size_in_bytes_rq3 = map_wqe_size(init_attr->act_wqe_size_enc_rq3);
+
+	ret = ehea_qp_alloc_register(qp, &qp->hw_squeue, init_attr->nr_sq_pages,
+				     wqe_size_in_bytes_sq,
+				     init_attr->act_wqe_size_enc_sq, adapter,
+				     0);
+	if (ret) {
+		ehea_error("can't register for sq ret=%x", ret);
+		goto out_freeres;
+	}
+
+	ret = ehea_qp_alloc_register(qp, &qp->hw_rqueue1,
+				     init_attr->nr_rq1_pages,
+				     wqe_size_in_bytes_rq1,
+				     init_attr->act_wqe_size_enc_rq1,
+				     adapter, 1);
+	if (ret) {
+		ehea_error("can't register for rq1 ret=%x", ret);
+		goto out_kill_hwsq;
+	}
+
+	if (init_attr->rq_count > 1) {
+		ret = ehea_qp_alloc_register(qp, &qp->hw_rqueue2,
+					     init_attr->nr_rq2_pages,
+					     wqe_size_in_bytes_rq2,
+					     init_attr->act_wqe_size_enc_rq2,
+					     adapter, 2);
+		if (ret) {
+			ehea_error("can't register for rq2 ret=%x", ret);
+			goto out_kill_hwr1q;
+		}
+	}
+
+	if (init_attr->rq_count > 2) {
+		ret = ehea_qp_alloc_register(qp, &qp->hw_rqueue3,
+					     init_attr->nr_rq3_pages,
+					     wqe_size_in_bytes_rq3,
+					     init_attr->act_wqe_size_enc_rq3,
+					     adapter, 3);
+		if (ret) {
+			ehea_error("can't register for rq3 ret=%x", ret);
+			goto out_kill_hwr2q;
+		}
+	}
+
+	qp->init_attr = *init_attr;
+
+	return qp;
+
+out_kill_hwr2q:
+	hw_queue_dtor(&qp->hw_rqueue2);
+
+out_kill_hwr1q:
+	hw_queue_dtor(&qp->hw_rqueue1);
+
+out_kill_hwsq:
+	hw_queue_dtor(&qp->hw_squeue);
+
+out_freeres:
+	ehea_h_disable_and_get_hea(adapter->handle, qp->fw_handle);
+	ehea_h_free_resource(adapter->handle, qp->fw_handle);
+
+out_freemem:
+	kfree(qp);
+	return NULL;
+}
+
+int ehea_destroy_qp(struct ehea_qp *qp)
+{
+	u64 hret;
+	struct ehea_qp_init_attr *qp_attr = &qp->init_attr;
+
+	if (!qp)
+		return 0;
+
+	hret = ehea_h_free_resource(qp->adapter->handle, qp->fw_handle);
+	if (hret != H_SUCCESS) {
+		ehea_error("destroy_qp failed");
+		return -EIO;
+	}
+
+	hw_queue_dtor(&qp->hw_squeue);
+	hw_queue_dtor(&qp->hw_rqueue1);
+
+   	if (qp_attr->rq_count > 1)
+		hw_queue_dtor(&qp->hw_rqueue2);
+   	if (qp_attr->rq_count > 2)
+		hw_queue_dtor(&qp->hw_rqueue3);
+	kfree(qp);
+
+	return 0;
+}
+
+int ehea_reg_mr_adapter(struct ehea_adapter *adapter)
+{
+	int i, k;
+	u64 hret, pt_abs, start, end, nr_pages;
+	u32 acc_ctrl = EHEA_MR_ACC_CTRL;
+	u64 *pt;
+
+	start = KERNELBASE;
+	end = (u64)high_memory;
+	nr_pages = (end - start) / PAGE_SIZE;
+
+	pt =  kzalloc(PAGE_SIZE, GFP_KERNEL);
+	if (!pt) {
+		ehea_error("no mem");
+		return -ENOMEM;
+	}
+	pt_abs = virt_to_abs(pt);
+
+	hret = ehea_h_alloc_resource_mr(adapter->handle, start, end - start,
+					acc_ctrl, adapter->pd,
+					&adapter->mr.handle, &adapter->mr.lkey);
+	if (hret != H_SUCCESS) {
+		ehea_error("alloc_resource_mr failed");
+		return -EIO;
+	}
+
+	adapter->mr.vaddr = KERNELBASE;
+	k = 0;
+
+	while (nr_pages > 0) {
+		if (nr_pages > 1) {
+			u64 num_pages = min(nr_pages, (u64)512);
+			for (i = 0; i < num_pages; i++)
+				pt[i] = virt_to_abs((void*)(((u64)start)
+							     + ((k++) *
+								PAGE_SIZE)));
+
+			hret = ehea_h_register_rpage_mr(adapter->handle,
+							adapter->mr.handle, 0,
+							0, (u64)pt_abs,
+							num_pages);
+			nr_pages -= num_pages;
+		} else {
+			u64 abs_adr = virt_to_abs((void*)(((u64)start)
+							   + (k * PAGE_SIZE)));
+			hret = ehea_h_register_rpage_mr(adapter->handle,
+							adapter->mr.handle, 0,
+							0, abs_adr,1);
+			nr_pages--;
+		}
+
+		if ((hret != H_SUCCESS) && (hret != H_PAGE_REGISTERED)) {
+			ehea_h_free_resource(adapter->handle,
+						adapter->mr.handle);
+			ehea_error("free_resource_mr failed");
+			return -EIO;
+		}
+	}
+
+	if (hret != H_SUCCESS) {
+		ehea_h_free_resource(adapter->handle, adapter->mr.handle);
+		ehea_error("free_resource_mr failed for last page");
+		return -EIO;
+	}
+	return 0;
+}
+
+int ehea_reg_mr_pages(struct ehea_adapter *adapter, struct ehea_mr *mr,
+		      u64 start, u64 *pt, int nr_pages)
+{
+	u64 hret;
+	u32 acc_ctrl = EHEA_MR_ACC_CTRL;
+
+	u64 pt_abs = virt_to_abs(pt);
+	u64 first_page = pt[0];
+
+	hret = ehea_h_alloc_resource_mr(adapter->handle, start,
+					PAGE_SIZE * nr_pages, acc_ctrl,
+					adapter->pd, &mr->handle, &mr->lkey);
+	if (hret != H_SUCCESS) {
+		ehea_error("alloc_resource_mr failed");
+		return -EIO;
+	}
+
+	if (nr_pages > 1)
+		hret = ehea_h_register_rpage_mr(adapter->handle, mr->handle,
+						0, 0, (u64)pt_abs, nr_pages);
+	else
+		hret = ehea_h_register_rpage_mr(adapter->handle, mr->handle,
+						0, 0, first_page, 1);
+
+	if (hret != H_SUCCESS) {
+		ehea_h_free_resource(adapter->handle, mr->handle);
+		ehea_error("free_resource_mr failed for last page");
+		return -EIO;
+	}
+	mr->vaddr = start;
+	return 0;
+}
--- linux-2.6.18-rc6-orig/drivers/net/ehea/ehea_qmr.h	1970-01-01 01:00:00.000000000 +0100
+++ kernel/drivers/net/ehea/ehea_qmr.h	2006-09-06 15:53:42.000000000 +0200
@@ -0,0 +1,362 @@
+/*
+ *  linux/drivers/net/ehea/ehea_qmr.h
+ *
+ *  eHEA ethernet device driver for IBM eServer System p
+ *
+ *  (C) Copyright IBM Corp. 2006
+ *
+ *  Authors:
+ *       Christoph Raisch <raisch@de.ibm.com>
+ *       Jan-Bernd Themann <themann@de.ibm.com>
+ *       Thomas Klein <tklein@de.ibm.com>
+ *
+ *
+ * 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, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.	 See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#ifndef __EHEA_QMR_H__
+#define __EHEA_QMR_H__
+
+#include "ehea.h"
+#include "ehea_hw.h"
+
+/*
+ * page size of ehea hardware queues
+ */
+
+#define EHEA_PAGESHIFT  12
+#define EHEA_PAGESIZE   4096UL
+
+/* Some abbreviations used here:
+ *
+ * WQE  - Work Queue Entry
+ * SWQE - Send Work Queue Entry
+ * RWQE - Receive Work Queue Entry
+ * CQE  - Completion Queue Entry
+ * EQE  - Event Queue Entry
+ * MR   - Memory Region
+ */
+
+/* Use of WR_ID field for EHEA */
+#define EHEA_WR_ID_COUNT   EHEA_BMASK_IBM(0, 19)
+#define EHEA_WR_ID_TYPE    EHEA_BMASK_IBM(20, 23)
+#define EHEA_SWQE2_TYPE    0x1
+#define EHEA_SWQE3_TYPE    0x2
+#define EHEA_RWQE2_TYPE    0x3
+#define EHEA_RWQE3_TYPE    0x4
+#define EHEA_WR_ID_INDEX   EHEA_BMASK_IBM(24, 47)
+#define EHEA_WR_ID_REFILL  EHEA_BMASK_IBM(48, 63)
+
+struct ehea_vsgentry {
+	u64 vaddr;
+	u32 l_key;
+	u32 len;
+};
+
+/* maximum number of sg entries allowed in a WQE */
+#define EHEA_MAX_WQE_SG_ENTRIES  	252
+#define SWQE2_MAX_IMM            	(0xD0 - 0x30)
+#define SWQE3_MAX_IMM            	224
+
+/* tx control flags for swqe */
+#define EHEA_SWQE_CRC                   0x8000
+#define EHEA_SWQE_IP_CHECKSUM           0x4000
+#define EHEA_SWQE_TCP_CHECKSUM          0x2000
+#define EHEA_SWQE_TSO                   0x1000
+#define EHEA_SWQE_SIGNALLED_COMPLETION  0x0800
+#define EHEA_SWQE_VLAN_INSERT           0x0400
+#define EHEA_SWQE_IMM_DATA_PRESENT      0x0200
+#define EHEA_SWQE_DESCRIPTORS_PRESENT   0x0100
+#define EHEA_SWQE_WRAP_CTL_REC          0x0080
+#define EHEA_SWQE_WRAP_CTL_FORCE        0x0040
+#define EHEA_SWQE_BIND                  0x0020
+#define EHEA_SWQE_PURGE                 0x0010
+
+/* sizeof(struct ehea_swqe) less the union */
+#define SWQE_HEADER_SIZE		32
+
+struct ehea_swqe {
+	u64 wr_id;
+	u16 tx_control;
+	u16 vlan_tag;
+	u8 reserved1;
+	u8 ip_start;
+	u8 ip_end;
+	u8 immediate_data_length;
+	u8 tcp_offset;
+	u8 reserved2;
+	u16 tcp_end;
+	u8 wrap_tag;
+	u8 descriptors;		/* number of valid descriptors in WQE */
+	u16 reserved3;
+	u16 reserved4;
+	u16 mss;
+	u32 reserved5;
+	union {
+		/*  Send WQE Format 1 */
+		struct {
+			struct ehea_vsgentry sg_list[EHEA_MAX_WQE_SG_ENTRIES];
+		} no_immediate_data;
+
+		/*  Send WQE Format 2 */
+		struct {
+			struct ehea_vsgentry sg_entry;
+			/* 0x30 */
+			u8 immediate_data[SWQE2_MAX_IMM];
+			/* 0xd0 */
+			struct ehea_vsgentry sg_list[EHEA_MAX_WQE_SG_ENTRIES-1];
+		} immdata_desc __attribute__ ((packed));
+
+		/*  Send WQE Format 3 */
+		struct {
+			u8 immediate_data[SWQE3_MAX_IMM];
+		} immdata_nodesc;
+	} u;
+};
+
+struct ehea_rwqe {
+	u64 wr_id;		/* work request ID */
+	u8 reserved1[5];
+	u8 data_segments;
+	u16 reserved2;
+	u64 reserved3;
+	u64 reserved4;
+	struct ehea_vsgentry sg_list[EHEA_MAX_WQE_SG_ENTRIES];
+};
+
+#define EHEA_CQE_VLAN_TAG_XTRACT   0x0400
+
+#define EHEA_CQE_TYPE_RQ           0x60
+#define EHEA_CQE_STAT_ERR_MASK     0x721F
+#define EHEA_CQE_STAT_FAT_ERR_MASK 0x1F
+#define EHEA_CQE_STAT_ERR_TCP      0x4000
+
+struct ehea_cqe {
+	u64 wr_id;		/* work request ID from WQE */
+	u8 type;
+	u8 valid;
+	u16 status;
+	u16 reserved1;
+	u16 num_bytes_transfered;
+	u16 vlan_tag;
+	u16 inet_checksum_value;
+	u8 reserved2;
+	u8 header_length;
+	u16 reserved3;
+	u16 page_offset;
+	u16 wqe_count;
+	u32 qp_token;
+	u32 timestamp;
+	u32 reserved4;
+	u64 reserved5[3];
+};
+
+#define EHEA_EQE_VALID           EHEA_BMASK_IBM(0, 0)
+#define EHEA_EQE_IS_CQE          EHEA_BMASK_IBM(1, 1)
+#define EHEA_EQE_IDENTIFIER      EHEA_BMASK_IBM(2, 7)
+#define EHEA_EQE_QP_CQ_NUMBER    EHEA_BMASK_IBM(8, 31)
+#define EHEA_EQE_QP_TOKEN        EHEA_BMASK_IBM(32, 63)
+#define EHEA_EQE_CQ_TOKEN        EHEA_BMASK_IBM(32, 63)
+#define EHEA_EQE_KEY             EHEA_BMASK_IBM(32, 63)
+#define EHEA_EQE_PORT_NUMBER     EHEA_BMASK_IBM(56, 63)
+#define EHEA_EQE_EQ_NUMBER       EHEA_BMASK_IBM(48, 63)
+#define EHEA_EQE_SM_ID           EHEA_BMASK_IBM(48, 63)
+#define EHEA_EQE_SM_MECH_NUMBER  EHEA_BMASK_IBM(48, 55)
+#define EHEA_EQE_SM_PORT_NUMBER  EHEA_BMASK_IBM(56, 63)
+
+struct ehea_eqe {
+	u64 entry;
+};
+
+static inline void *hw_qeit_calc(struct hw_queue *queue, u64 q_offset)
+{
+	struct ehea_page *current_page;
+
+	if (q_offset >= queue->queue_length)
+		q_offset -= queue->queue_length;
+	current_page = (queue->queue_pages)[q_offset >> EHEA_PAGESHIFT];
+	return &current_page->entries[q_offset & (EHEA_PAGESIZE - 1)];
+}
+
+static inline void *hw_qeit_get(struct hw_queue *queue)
+{
+	return hw_qeit_calc(queue, queue->current_q_offset);
+}
+
+static inline void hw_qeit_inc(struct hw_queue *queue)
+{
+	queue->current_q_offset += queue->qe_size;
+	if (queue->current_q_offset >= queue->queue_length) {
+		queue->current_q_offset = 0;
+		/* toggle the valid flag */
+		queue->toggle_state = (~queue->toggle_state) & 1;
+	}
+}
+
+static inline void *hw_qeit_get_inc(struct hw_queue *queue)
+{
+	void *retvalue = hw_qeit_get(queue);
+	hw_qeit_inc(queue);
+	return retvalue;
+}
+
+static inline void *hw_qeit_get_inc_valid(struct hw_queue *queue)
+{
+	struct ehea_cqe *retvalue = hw_qeit_get(queue);
+	u8 valid = retvalue->valid;
+	void *pref;
+
+	if ((valid >> 7) == (queue->toggle_state & 1)) {
+		/* this is a good one */
+		hw_qeit_inc(queue);
+		pref = hw_qeit_calc(queue, queue->current_q_offset);
+		prefetch(pref);
+		prefetch(pref + 128);
+	} else
+		retvalue = NULL;
+	return retvalue;
+}
+
+static inline void *hw_qeit_get_valid(struct hw_queue *queue)
+{
+	struct ehea_cqe *retvalue = hw_qeit_get(queue);
+	void *pref;
+	u8 valid;
+
+	pref = hw_qeit_calc(queue, queue->current_q_offset);
+	prefetch(pref);
+	prefetch(pref + 128);
+	prefetch(pref + 256);
+	valid = retvalue->valid;
+	if (!((valid >> 7) == (queue->toggle_state & 1)))
+		retvalue = NULL;
+	return retvalue;
+}
+
+static inline void *hw_qeit_reset(struct hw_queue *queue)
+{
+	queue->current_q_offset = 0;
+	return hw_qeit_get(queue);
+}
+
+static inline void *hw_qeit_eq_get_inc(struct hw_queue *queue)
+{
+	u64 last_entry_in_q = queue->queue_length - queue->qe_size;
+	void *retvalue;
+
+	retvalue = hw_qeit_get(queue);
+	queue->current_q_offset += queue->qe_size;
+	if (queue->current_q_offset > last_entry_in_q) {
+		queue->current_q_offset = 0;
+		queue->toggle_state = (~queue->toggle_state) & 1;
+	}
+	return retvalue;
+}
+
+static inline void *hw_eqit_eq_get_inc_valid(struct hw_queue *queue)
+{
+	void *retvalue = hw_qeit_get(queue);
+	u32 qe = *(u8*)retvalue;
+	if ((qe >> 7) == (queue->toggle_state & 1))
+		hw_qeit_eq_get_inc(queue);
+	else
+		retvalue = NULL;
+	return retvalue;
+}
+
+static inline struct ehea_rwqe *ehea_get_next_rwqe(struct ehea_qp *qp,
+						   int rq_nr)
+{
+	struct hw_queue *queue;
+
+	if (rq_nr == 1)
+		queue = &qp->hw_rqueue1;
+	else if (rq_nr == 2)
+		queue = &qp->hw_rqueue2;
+	else
+		queue = &qp->hw_rqueue3;
+
+	return hw_qeit_get_inc(queue);
+}
+
+static inline struct ehea_swqe *ehea_get_swqe(struct ehea_qp *my_qp,
+					      int *wqe_index)
+{
+	struct hw_queue *queue = &my_qp->hw_squeue;
+	struct ehea_swqe *wqe_p;
+
+	*wqe_index = (queue->current_q_offset) >> (7 + EHEA_SG_SQ);
+	wqe_p = hw_qeit_get_inc(&my_qp->hw_squeue);
+
+	return wqe_p;
+}
+
+static inline void ehea_post_swqe(struct ehea_qp *my_qp, struct ehea_swqe *swqe)
+{
+	iosync();
+	ehea_update_sqa(my_qp, 1);
+}
+
+static inline struct ehea_cqe *ehea_poll_rq1(struct ehea_qp *qp, int *wqe_index)
+{
+	struct hw_queue *queue = &qp->hw_rqueue1;
+
+	*wqe_index = (queue->current_q_offset) >> (7 + EHEA_SG_RQ1);
+	return hw_qeit_get_valid(queue);
+}
+
+static inline void ehea_inc_rq1(struct ehea_qp *qp)
+{
+	hw_qeit_inc(&qp->hw_rqueue1);
+}
+
+static inline struct ehea_cqe *ehea_poll_cq(struct ehea_cq *my_cq)
+{
+	return hw_qeit_get_inc_valid(&my_cq->hw_queue);
+}
+
+#define EHEA_CQ_REGISTER_ORIG 0
+#define EHEA_EQ_REGISTER_ORIG 0
+
+enum ehea_eq_type {
+	EHEA_EQ = 0,		/* event queue              */
+	EHEA_NEQ		/* notification event queue */
+};
+
+struct ehea_eq *ehea_create_eq(struct ehea_adapter *adapter,
+			       enum ehea_eq_type type,
+			       const u32 length, const u8 eqe_gen);
+
+int ehea_destroy_eq(struct ehea_eq *eq);
+
+struct ehea_eqe *ehea_poll_eq(struct ehea_eq *eq);
+
+struct ehea_cq *ehea_create_cq(struct ehea_adapter *adapter, int cqe,
+			       u64 eq_handle, u32 cq_token);
+
+int ehea_destroy_cq(struct ehea_cq *cq);
+
+struct ehea_qp *ehea_create_qp(struct ehea_adapter * adapter, u32 pd,
+			       struct ehea_qp_init_attr *init_attr);
+
+int ehea_destroy_qp(struct ehea_qp *qp);
+
+int ehea_reg_mr_adapter(struct ehea_adapter *adapter);
+
+int ehea_reg_mr_pages(struct ehea_adapter *adapter,
+		      struct ehea_mr *mr,
+		      u64 start, u64 *pt, int nr_pages);
+
+#endif	/* __EHEA_QMR_H__ */

^ permalink raw reply

* [2.6.19 PATCH 4/7] ehea: ethtool interface
From: Jan-Bernd Themann @ 2006-09-06 13:33 UTC (permalink / raw)
  To: netdev
  Cc: Thomas Klein, Jan-Bernd Themann, linux-kernel, linux-ppc,
	Christoph Raisch, Marcus Eder

Signed-off-by: Jan-Bernd Themann <themann@de.ibm.com> 


 drivers/net/ehea/ehea_ethtool.c |  294 ++++++++++++++++++++++++++++++++++++++++
 1 file changed, 294 insertions(+)



--- linux-2.6.18-rc6-orig/drivers/net/ehea/ehea_ethtool.c	1970-01-01 01:00:00.000000000 +0100
+++ kernel/drivers/net/ehea/ehea_ethtool.c	2006-09-06 15:53:43.000000000 +0200
@@ -0,0 +1,294 @@
+/*
+ *  linux/drivers/net/ehea/ehea_ethtool.c
+ *
+ *  eHEA ethernet device driver for IBM eServer System p
+ *
+ *  (C) Copyright IBM Corp. 2006
+ *
+ *  Authors:
+ *       Christoph Raisch <raisch@de.ibm.com>
+ *       Jan-Bernd Themann <themann@de.ibm.com>
+ *       Thomas Klein <tklein@de.ibm.com>
+ *
+ *
+ * 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, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.	 See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include "ehea.h"
+#include "ehea_phyp.h"
+
+static int ehea_get_settings(struct net_device *dev, struct ethtool_cmd *cmd)
+{
+	struct ehea_port *port = netdev_priv(dev);
+	int ret;
+
+	ret = ehea_sense_port_attr(port);
+
+	if (ret)
+		return ret;
+
+	if (netif_carrier_ok(dev)) {
+		switch(port->port_speed) {
+		case EHEA_SPEED_10M: cmd->speed = SPEED_10; break;
+		case EHEA_SPEED_100M: cmd->speed = SPEED_100; break;
+		case EHEA_SPEED_1G: cmd->speed = SPEED_1000; break;
+		case EHEA_SPEED_10G: cmd->speed = SPEED_10000; break;
+		}
+		cmd->duplex = port->full_duplex == 1 ?
+						     DUPLEX_FULL : DUPLEX_HALF;
+	} else {
+		cmd->speed = -1;
+		cmd->duplex = -1;
+	}
+
+	cmd->supported = (SUPPORTED_10000baseT_Full | SUPPORTED_1000baseT_Full
+		       | SUPPORTED_100baseT_Full |  SUPPORTED_100baseT_Half
+		       | SUPPORTED_10baseT_Full | SUPPORTED_10baseT_Half
+		       | SUPPORTED_Autoneg | SUPPORTED_FIBRE);
+
+	cmd->advertising = (ADVERTISED_10000baseT_Full | ADVERTISED_Autoneg
+			 | ADVERTISED_FIBRE);
+
+	cmd->port = PORT_FIBRE;
+	cmd->autoneg = port->autoneg == 1 ? AUTONEG_ENABLE : AUTONEG_DISABLE;
+
+	return 0;
+}
+
+static int ehea_set_settings(struct net_device *dev, struct ethtool_cmd *cmd)
+{
+	struct ehea_port *port = netdev_priv(dev);
+	int ret = 0;
+	u32 sp;
+
+	if (cmd->autoneg == AUTONEG_ENABLE) {
+		sp = EHEA_SPEED_AUTONEG;
+		goto doit;
+	}
+
+	switch(cmd->speed) {
+	case SPEED_10:
+		if (cmd->duplex == DUPLEX_FULL)
+			sp = H_SPEED_10M_F;
+		else
+			sp = H_SPEED_10M_H;
+		break;
+
+	case SPEED_100:
+		if (cmd->duplex == DUPLEX_FULL)
+			sp = H_SPEED_100M_F;
+		else
+			sp = H_SPEED_100M_H;
+		break;
+
+	case SPEED_1000:
+		if (cmd->duplex == DUPLEX_FULL)
+			sp = H_SPEED_1G_F;
+		else
+			ret = -EINVAL;
+		break;
+
+	case SPEED_10000:
+		if (cmd->duplex == DUPLEX_FULL)
+			sp = H_SPEED_10G_F;
+		else
+			ret = -EINVAL;
+		break;
+
+	default:
+			ret = -EINVAL;
+		break;
+	}
+
+	if (ret)
+		goto out;
+doit:
+	ret = ehea_set_portspeed(port, sp);
+
+	if (!ret)
+		ehea_info("%s: Port speed succesfully set: %dMbps "
+			  "%s Duplex",
+			  port->netdev->name, port->port_speed,
+			  port->full_duplex == 1 ? "Full" : "Half");
+out:
+	return ret;
+}
+
+static int ehea_nway_reset(struct net_device *dev)
+{
+	struct ehea_port *port = netdev_priv(dev);
+	int ret;
+
+	ret = ehea_set_portspeed(port, EHEA_SPEED_AUTONEG);
+
+	if (!ret)
+		ehea_info("%s: Port speed succesfully set: %dMbps "
+			  "%s Duplex",
+			  port->netdev->name, port->port_speed,
+			  port->full_duplex == 1 ? "Full" : "Half");
+	return ret;
+}
+
+static void ehea_get_drvinfo(struct net_device *dev,
+			       struct ethtool_drvinfo *info)
+{
+	strlcpy(info->driver, DRV_NAME, sizeof(info->driver) - 1);
+	strlcpy(info->version, DRV_VERSION, sizeof(info->version) - 1);
+}
+
+static u32 ehea_get_msglevel(struct net_device *dev)
+{
+	struct ehea_port *port = netdev_priv(dev);
+	return port->msg_enable;
+}
+
+static void ehea_set_msglevel(struct net_device *dev, u32 value)
+{
+	struct ehea_port *port = netdev_priv(dev);
+	port->msg_enable = value;
+}
+
+static u32 ehea_get_rx_csum(struct net_device *dev)
+{
+	return 1;
+}
+
+static char ehea_ethtool_stats_keys[][ETH_GSTRING_LEN] = {
+	{"poll_max_processed"},
+	{"queue_stopped"},
+	{"min_swqe_avail"},
+	{"poll_receive_err"},
+	{"pkt_send"},
+	{"pkt_xmit"},
+	{"send_tasklet"},
+	{"ehea_poll"},
+	{"nwqe"},
+	{"swqe_available_0"},
+	{"sig_comp_iv"},
+	{"swqe_refill_th"},
+	{"port resets"},
+	{"rxo"},
+	{"rx64"},
+	{"rx65"},
+	{"rx128"},
+	{"rx256"},
+	{"rx512"},
+	{"rx1024"},
+	{"txo"},
+	{"tx64"},
+	{"tx65"},
+	{"tx128"},
+	{"tx256"},
+	{"tx512"},
+	{"tx1024"},
+};
+
+static void ehea_get_strings(struct net_device *dev, u32 stringset, u8 *data)
+{
+	if (stringset == ETH_SS_STATS) {
+		memcpy(data, &ehea_ethtool_stats_keys,
+		       sizeof(ehea_ethtool_stats_keys));
+	}
+}
+
+static int ehea_get_stats_count(struct net_device *dev)
+{
+	return ARRAY_SIZE(ehea_ethtool_stats_keys);
+}
+
+static void ehea_get_ethtool_stats(struct net_device *dev,
+				     struct ethtool_stats *stats, u64 *data)
+{
+	u64 hret;
+	int i;
+	struct ehea_port *port = netdev_priv(dev);
+	struct ehea_adapter *adapter = port->adapter;
+	struct ehea_port_res *pr = &port->port_res[0];
+	struct port_state *p_state = &pr->p_state;
+	struct hcp_ehea_port_cb6 *cb6;
+
+	for (i = 0; i < ehea_get_stats_count(dev); i++)
+		data[i] = 0;
+
+	i = 0;
+
+	data[i++] = p_state->poll_max_processed;
+	data[i++] = p_state->queue_stopped;
+	data[i++] = p_state->min_swqe_avail;
+	data[i++] = p_state->poll_receive_errors;
+	data[i++] = p_state->pkt_send;
+	data[i++] = p_state->pkt_xmit;
+	data[i++] = p_state->send_tasklet;
+	data[i++] = p_state->ehea_poll;
+	data[i++] = p_state->nwqe;
+	data[i++] = atomic_read(&port->port_res[0].swqe_avail);
+	data[i++] = port->sig_comp_iv;
+	data[i++] = port->port_res[0].swqe_refill_th;
+	data[i++] = port->resets;
+
+	cb6 = kzalloc(H_CB_ALIGNMENT, GFP_KERNEL);
+	if (!cb6) {
+		ehea_error("no mem for cb6");
+		return;
+	}
+
+	hret = ehea_h_query_ehea_port(adapter->handle, port->logical_port_id,
+				      H_PORT_CB6, H_PORT_CB6_ALL, cb6);
+	if (netif_msg_hw(port))
+		ehea_dump(cb6, sizeof(*cb6), "ehea_get_ethtool_stats");
+
+	if (hret == H_SUCCESS) {
+		data[i++] = cb6->rxo;
+		data[i++] = cb6->rx64;
+		data[i++] = cb6->rx65;
+		data[i++] = cb6->rx128;
+		data[i++] = cb6->rx256;
+		data[i++] = cb6->rx512;
+		data[i++] = cb6->rx1024;
+		data[i++] = cb6->txo;
+		data[i++] = cb6->tx64;
+		data[i++] = cb6->tx65;
+		data[i++] = cb6->tx128;
+		data[i++] = cb6->tx256;
+		data[i++] = cb6->tx512;
+		data[i++] = cb6->tx1024;
+	} else
+		ehea_error("query_ehea_port failed");
+
+	kfree(cb6);
+}
+
+struct ethtool_ops ehea_ethtool_ops = {
+	.get_settings = ehea_get_settings,
+	.get_drvinfo = ehea_get_drvinfo,
+	.get_msglevel = ehea_get_msglevel,
+	.set_msglevel = ehea_set_msglevel,
+	.get_link = ethtool_op_get_link,
+	.get_tx_csum = ethtool_op_get_tx_csum,
+	.get_sg = ethtool_op_get_sg,
+	.get_tso = ethtool_op_get_tso,
+	.set_tso = ethtool_op_set_tso,
+	.get_strings = ehea_get_strings,
+	.get_stats_count = ehea_get_stats_count,
+	.get_ethtool_stats = ehea_get_ethtool_stats,
+	.get_rx_csum = ehea_get_rx_csum,
+	.set_settings = ehea_set_settings,
+	.nway_reset = ehea_nway_reset,		/* Restart autonegotiation */
+};
+
+void ehea_set_ethtool_ops(struct net_device *netdev)
+{
+	SET_ETHTOOL_OPS(netdev, &ehea_ethtool_ops);
+}

^ 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