* Re: [Cbe-oss-dev] [PATCH] spufs: Fix test in spufs_switch_log_read()
From: Arnd Bergmann @ 2009-10-13 13:31 UTC (permalink / raw)
To: linuxppc-dev
Cc: cbe-oss-dev, Roel Kluin, linuxppc-dev, Jeremy Kerr, Andrew Morton,
cbe-oss-dev
In-Reply-To: <200910130849.56671.jk@ozlabs.org>
On Tuesday 13 October 2009, Jeremy Kerr wrote:
> > Or can this test be removed?
>
> I'd prefer just to remove the test.
Yes, sounds good.
Arnd <><
^ permalink raw reply
* Re: [Patch] powerpc: Fix memory leak in axon_msi.c
From: Arnd Bergmann @ 2009-10-13 13:30 UTC (permalink / raw)
To: linuxppc-dev, michael; +Cc: linuxppc-dev list, eric.sesterhenn
In-Reply-To: <1255393780.9570.3.camel@concordia>
On Tuesday 13 October 2009, Michael Ellerman wrote:
> cppcheck found a memory leak in axon_msi, if dcr_base or dcr_len are zero,
> we have already allocated msic, so we should free it in the error path.
>
> Signed-off-by: Eric Sesterhenn <eric.sesterhenn@lsexperts.de>
> Acked-by: Michael Ellerman <michael@ellerman.id.au>
Acked-by: Arnd Bergmann <arnd@arndb.de>
^ permalink raw reply
* Re: [U-Boot] Linux seamless booting
From: Kenneth Johansson @ 2009-10-13 12:22 UTC (permalink / raw)
To: Fortini Matteo
Cc: u-boot@lists.denx.de, Roberto Guerra,
linuxppc-dev@lists.ozlabs.org
In-Reply-To: <4AD33533.5030900@mta.it>
On Mon, 2009-10-12 at 15:54 +0200, Fortini Matteo wrote:
> Yes, that's what we're currently using, but the problem is a little
> broader: I should answer to CAN messages in at most 100-200ms from
> powerup, and that can be done in u-boot.
if you are in that interval you definitely need to go to a more exotic
start sequence than usual.
one solution would be to do as you suggest and do a special driver that
is living outside of the kernel during startup. you still need to hack
into the interrupt code to let your "external" driver handle the CAN.
then you need to hack up the ordinary driver to take over from yours.
I have not seen this solution on any project I worked on but should be
doable.
optimizing the boot time of linux so it starts up in 200ms is probably
going to be quite hard. I did 2 seconds to /sbin/init started from ide
driver without to much trouble. removing the IDE and going to a root on
NOR would probably get closer to 1.5 but to get down to 200ms would
probably mean removing most of u-boot and only keep the dram setup then
you probably need to remove most of the drivers from the kernel and load
them later as modules. I have never really tried to do a insane fast
boot like this so I'm not sure what problems you will run up against.
but maybe it's possible. but 200ms feels a bit to optimistic.
> However, handing CAN transmission control over to Linux is quite
> complicated nowadays, since it would involve passing structures in
> memory and hacking through device init.
> It'd be nice to have a framework with which u-boot could hand-over
> devices to Linux in a clean and defined way.
not likely to happen as a generic solution. Much better to just remove
the boat loader then and work on optimizing the linux startup code.
^ permalink raw reply
* Re: i2c-powermac fails
From: Jean Delvare @ 2009-10-13 9:49 UTC (permalink / raw)
To: Benjamin Herrenschmidt; +Cc: linuxppc-dev, Paul Mackerras, Tim Shepard
In-Reply-To: <1255426348.2192.187.camel@pasglop>
On Tue, 13 Oct 2009 20:32:28 +1100, Benjamin Herrenschmidt wrote:
> On Tue, 2009-10-13 at 11:23 +0200, Jean Delvare wrote:
> > Hi Ben, Paul,
> >
> > I had a report by Tim Shepard (Cc'd) that the therm_adt746x driver
> > sometimes fails to initialize on his PowerBook G4 running kernel
> > 2.6.31. The following error message can be seen in the logs when the
> > failure happens:
> >
> > therm_adt746x 7-002e: Thermostat failed to read config!
> >
> > After enabling low-level i2c debugging, it turns out that the problem
> > is caused by low-level errors at the I2C bus level:
>
> Nothing comes to mind immediately, but I'll have another look tomorrow.
>
> Maybe we are configuring the i2c bus too fast ? Another possibility
> would be that the device needs some retries ...
I guess that retrying would work around the problem, yes. But I do not
think this is the proper solution. If retries were needed, they would
be needed all the time, not just at initialization time. And as I said,
the SMBus specification says that devices have to always ack their
slave address (they can always delay the transaction later if they need
more time) so I am reasonably certain that the ADT7467 does ack his
address always. If it seems otherwise, this suggests that either the
message was not properly sent on the bus (so the ADT7467 did not have
anything to ack), or the ADT7467's ack went on the bus but the I2C
master didn't see it.
I2C bus being setup too fast sounds more likely. It might be worth
adding an arbitrary delay after initialization, just to see if it
helps. Not sure where though, as I'm not familiar with the Powermac
initialization steps. Maybe right before i2c_add_adapter() in
i2c_powermac_probe?
--
Jean Delvare
^ permalink raw reply
* Re: i2c-powermac fails
From: Benjamin Herrenschmidt @ 2009-10-13 9:32 UTC (permalink / raw)
To: Jean Delvare; +Cc: linuxppc-dev, Paul Mackerras, Tim Shepard
In-Reply-To: <20091013112304.74083fd1@hyperion.delvare>
On Tue, 2009-10-13 at 11:23 +0200, Jean Delvare wrote:
> Hi Ben, Paul,
>
> I had a report by Tim Shepard (Cc'd) that the therm_adt746x driver
> sometimes fails to initialize on his PowerBook G4 running kernel
> 2.6.31. The following error message can be seen in the logs when the
> failure happens:
>
> therm_adt746x 7-002e: Thermostat failed to read config!
>
> After enabling low-level i2c debugging, it turns out that the problem
> is caused by low-level errors at the I2C bus level:
Nothing comes to mind immediately, but I'll have another look tomorrow.
Maybe we are configuring the i2c bus too fast ? Another possibility
would be that the device needs some retries ...
Ben.
^ permalink raw reply
* i2c-powermac fails
From: Jean Delvare @ 2009-10-13 9:23 UTC (permalink / raw)
To: Benjamin Herrenschmidt, Paul Mackerras; +Cc: linuxppc-dev, Tim Shepard
Hi Ben, Paul,
I had a report by Tim Shepard (Cc'd) that the therm_adt746x driver
sometimes fails to initialize on his PowerBook G4 running kernel
2.6.31. The following error message can be seen in the logs when the
failure happens:
therm_adt746x 7-002e: Thermostat failed to read config!
After enabling low-level i2c debugging, it turns out that the problem
is caused by low-level errors at the I2C bus level:
PowerMac i2c bus pmu 2 registered
PowerMac i2c bus pmu 1 registered
PowerMac i2c bus mac-io 0 registered
low_i2c:xfer() chan=0, addrdir=0x5d, mode=4, subsize=1, subaddr=0x0, 1 bytes, bus /uni-n@f8000000/i2c@f8001000/i2c-bus@0
low_i2c:kw_handle_interrupt(state_addr, isr: 6)
low_i2c:KW: NAK on address
low_i2c:xfer error -6
i2c-adapter i2c-7: I2C transfer at 0x2e failed, size 2, err -6
therm_adt746x 7-002e: Thermostat failed to read config!
PowerMac i2c bus uni-n 0 registered
So apparently the I2C controller doesn't see the ack from the ADT7467.
However the ADT7467 is a SMBus-compliant device, so it must always ack
his address.
It is worth noting that many other I2C errors happen and go unnoticed.
Below is the log of a "successful" therm_adt746x registration:
PowerMac i2c bus pmu 2 registered
PowerMac i2c bus pmu 1 registered
PowerMac i2c bus mac-io 0 registered
low_i2c:xfer() chan=0, addrdir=0x5d, mode=4, subsize=1, subaddr=0x0, 1 bytes, bus /uni-n@f8000000/i2c@f8001000/i2c-bus@0
low_i2c:kw_handle_interrupt(state_addr, isr: 2)
low_i2c:kw_handle_interrupt(state_read, isr: 5)
adt746x: ADT7467 initializing
low_i2c:xfer() chan=0, addrdir=0x5d, mode=4, subsize=1, subaddr=0x6b, 1 bytes, bus /uni-n@f8000000/i2c@f8001000/i2c-bus@0
low_i2c:kw_handle_interrupt(state_addr, isr: 2)
low_i2c:kw_handle_interrupt(state_read, isr: 5)
low_i2c:xfer() chan=0, addrdir=0x5c, mode=3, subsize=1, subaddr=0x6b, 1 bytes, bus /uni-n@f8000000/i2c@f8001000/i2c-bus@0
low_i2c:kw_handle_interrupt(state_addr, isr: 6)
low_i2c:KW: NAK on address
low_i2c:xfer error -6
i2c-adapter i2c-7: I2C transfer at 0x2e failed, size 2, err -6
low_i2c:xfer() chan=0, addrdir=0x5d, mode=4, subsize=1, subaddr=0x6a, 1 bytes, bus /uni-n@f8000000/i2c@f8001000/i2c-bus@0
low_i2c:kw_handle_interrupt(state_addr, isr: 2)
low_i2c:kw_handle_interrupt(state_read, isr: 1)
low_i2c:kw_handle_interrupt(state_stop, isr: 4)
low_i2c:xfer() chan=0, addrdir=0x5c, mode=3, subsize=1, subaddr=0x6a, 1 bytes, bus /uni-n@f8000000/i2c@f8001000/i2c-bus@0
ieee1394: Host added: ID:BUS[0-00:1023] GUID[001124fffed61a88]
low_i2c:kw_handle_interrupt(state_addr, isr: 6)
low_i2c:KW: NAK on address
low_i2c:xfer error -6
i2c-adapter i2c-7: I2C transfer at 0x2e failed, size 2, err -6
low_i2c:xfer() chan=0, addrdir=0x5d, mode=4, subsize=1, subaddr=0x6c, 1 bytes, bus /uni-n@f8000000/i2c@f8001000/i2c-bus@0
low_i2c:kw_handle_interrupt(state_addr, isr: 2)
low_i2c:kw_handle_interrupt(state_read, isr: 5)
low_i2c:xfer() chan=0, addrdir=0x5c, mode=3, subsize=1, subaddr=0x6c, 1 bytes, bus /uni-n@f8000000/i2c@f8001000/i2c-bus@0
low_i2c:kw_handle_interrupt(state_addr, isr: 2)
low_i2c:kw_handle_interrupt(state_write, isr: 1)
low_i2c:kw_handle_interrupt(state_stop, isr: 4)
adt746x: Lowering max temperatures from 81, 80, 87 to 70, 50, 70
low_i2c:xfer() chan=0, addrdir=0x5d, mode=4, subsize=1, subaddr=0x5c, 1 bytes, bus /uni-n@f8000000/i2c@f8001000/i2c-bus@0
eth0: Link is up at 1000 Mbps, full-duplex.
low_i2c:kw_handle_interrupt(state_addr, isr: 6)
low_i2c:KW: NAK on address
low_i2c:xfer error -6
i2c-adapter i2c-7: I2C transfer at 0x2e failed, size 2, err -6
low_i2c:xfer() chan=0, addrdir=0x5c, mode=3, subsize=1, subaddr=0x5c, 1 bytes, bus /uni-n@f8000000/i2c@f8001000/i2c-bus@0
low_i2c:kw_handle_interrupt(state_addr, isr: 6)
low_i2c:KW: NAK on address
low_i2c:xfer error -6
i2c-adapter i2c-7: I2C transfer at 0x2e failed, size 2, err -6
low_i2c:xfer() chan=0, addrdir=0x5c, mode=3, subsize=1, subaddr=0x30, 1 bytes, bus /uni-n@f8000000/i2c@f8001000/i2c-bus@0
low_i2c:kw_handle_interrupt(state_addr, isr: 2)
low_i2c:kw_handle_interrupt(state_write, isr: 1)
low_i2c:kw_handle_interrupt(state_stop, isr: 4)
PowerMac i2c bus uni-n 0 registered
As you can see there are 4 errors, but the config register read doesn't
fail so this is considered a success.
Ever heard of this problem?
One very interesting thing I've noticed is that therm_adt746x register
access _after_ the initialization works reliably. Errors only happen in
probe_thermostat(). This makes me suspect that the problem is either a
low level initialization happening too late, or another initialization
step happening in parallel and interfering with probe_thermostat().
Tim found evidences in older boot logs that the problem isn't new and
was already present back in kernel 2.6.24 at least.
Any idea what the problem can be and/or how to debug it further?
--
Jean Delvare
^ permalink raw reply
* Re: [PATCH 2/8] bitmap: Introduce bitmap_set, bitmap_clear, bitmap_find_next_zero_area
From: Akinobu Mita @ 2009-10-13 9:10 UTC (permalink / raw)
To: Andrew Morton
Cc: Fenghua Yu, Greg Kroah-Hartman, linux-ia64, Tony Luck, x86,
netdev, linux-kernel, linux-altix, Yevgeny Petrilin,
FUJITA Tomonori, linuxppc-dev, Ingo Molnar, Paul Mackerras,
H. Peter Anvin, sparclinux, Thomas Gleixner, linux-usb,
David S. Miller, Lothar Wassmann
In-Reply-To: <20091013021818.GA3898@localhost.localdomain>
My user space testing exposed off-by-one error find_next_zero_area
in iommu-helper. Some zero area cannot be found by this bug.
Subject: [PATCH] Fix off-by-one error in find_next_zero_area
Signed-off-by: Akinobu Mita <akinobu.mita@gmail.com>
---
lib/iommu-helper.c | 2 +-
1 files changed, 1 insertions(+), 1 deletions(-)
diff --git a/lib/iommu-helper.c b/lib/iommu-helper.c
index 75dbda0..afc58bc 100644
--- a/lib/iommu-helper.c
+++ b/lib/iommu-helper.c
@@ -19,7 +19,7 @@ again:
index = (index + align_mask) & ~align_mask;
end = index + nr;
- if (end >= size)
+ if (end > size)
return -1;
for (i = index; i < end; i++) {
if (test_bit(i, map)) {
--
1.5.4.3
^ permalink raw reply related
* Re: [PATCH] powerpc/mm: Fix hang accessing top of vmalloc space
From: Sachin Sant @ 2009-10-13 8:23 UTC (permalink / raw)
To: Benjamin Herrenschmidt; +Cc: Tejun Heo, paulus, linuxppc-dev
In-Reply-To: <1255416227.2192.184.camel@pasglop>
Benjamin Herrenschmidt wrote:
> On pSeries, we always force the IO space to be mapped using 4K
> pages even with a 64K base page size to cope with some limitations
> in the HV interface to some devices.
>
> However, the SLB miss handler code to discriminate between vmalloc
> and ioremap space uses a CPU feature section such that the code
> is nop'ed out when the processor support large pages non-cachable
> mappings.
>
> Thus, we end up always using the ioremap page size for vmalloc
> segments on such processors, causing a discrepency between the
> segment and the hash table, and thus a hang continously hashing
> the page.
>
> It works for the first segment of the vmalloc space since that
> segment is "bolted" in by C code correctly, and thankfully we
> almost never use the vmalloc space beyond the first segment,
> but the new percpu code made the bug happen.
>
> This fixes it by removing the feature section from the assembly,
> we now always do the comparison between vmalloc and ioremap.
>
> Signed-off-by; Benjamin Herrenschmidt <benh@kernel.crashing.org>
> ---
>
> Sachin, can you verify that works for you ?
Works great. Thanks Ben.
Tested by: Sachin Sant <sachinp@in.ibm.com>
Regards
-Sachin
--
---------------------------------
Sachin Sant
IBM Linux Technology Center
India Systems and Technology Labs
Bangalore, India
---------------------------------
^ permalink raw reply
* Re: [PATCH 0/8] gianfar: Add support for hibernation
From: David Miller @ 2009-10-13 6:57 UTC (permalink / raw)
To: avorontsov; +Cc: scottwood, linuxppc-dev, netdev, afleming
In-Reply-To: <20091012160000.GA32406@oksana.dev.rtsoft.ru>
From: Anton Vorontsov <avorontsov@ru.mvista.com>
Date: Mon, 12 Oct 2009 20:00:00 +0400
> Here are few patches that add support for hibernation for gianfar
> driver.
>
> Technically, we could just do gfar_close() and then gfar_enet_open()
> sequence to restore gianfar functionality after hibernation, but
> close/open does so many unneeded things (e.g. BDs buffers freeing and
> allocation, IRQ freeing and requesting), that I felt it would be much
> better to cleanup and refactor some code to make the hibernation [and
> not only hibernation] code a little bit prettier.
I applied all of this, it's a really nice patch set. If there are any
problems we can deal with it using follow-on fixups.
I noticed something, in patch #3 where you remove the spurious wrap
bit setting in startup_gfar(). It looks like that was not only
spurious but it was doing it wrong too.
It's writing garbage into the status word, because it's not using the
BD_LFLAG() macro to shift the value up 16 bits.
^ permalink raw reply
* [PATCH] powerpc/mm: Fix hang accessing top of vmalloc space
From: Benjamin Herrenschmidt @ 2009-10-13 6:43 UTC (permalink / raw)
To: linuxppc-dev; +Cc: Tejun Heo, paulus
On pSeries, we always force the IO space to be mapped using 4K
pages even with a 64K base page size to cope with some limitations
in the HV interface to some devices.
However, the SLB miss handler code to discriminate between vmalloc
and ioremap space uses a CPU feature section such that the code
is nop'ed out when the processor support large pages non-cachable
mappings.
Thus, we end up always using the ioremap page size for vmalloc
segments on such processors, causing a discrepency between the
segment and the hash table, and thus a hang continously hashing
the page.
It works for the first segment of the vmalloc space since that
segment is "bolted" in by C code correctly, and thankfully we
almost never use the vmalloc space beyond the first segment,
but the new percpu code made the bug happen.
This fixes it by removing the feature section from the assembly,
we now always do the comparison between vmalloc and ioremap.
Signed-off-by; Benjamin Herrenschmidt <benh@kernel.crashing.org>
---
Sachin, can you verify that works for you ?
diff --git a/arch/powerpc/mm/slb_low.S b/arch/powerpc/mm/slb_low.S
index bc44dc4..95ce355 100644
--- a/arch/powerpc/mm/slb_low.S
+++ b/arch/powerpc/mm/slb_low.S
@@ -72,19 +72,17 @@ _GLOBAL(slb_miss_kernel_load_vmemmap)
1:
#endif /* CONFIG_SPARSEMEM_VMEMMAP */
- /* vmalloc/ioremap mapping encoding bits, the "li" instructions below
- * will be patched by the kernel at boot
+ /* vmalloc mapping gets the encoding from the PACA as the mapping
+ * can be demoted from 64K -> 4K dynamically on some machines
*/
-BEGIN_FTR_SECTION
- /* check whether this is in vmalloc or ioremap space */
clrldi r11,r10,48
cmpldi r11,(VMALLOC_SIZE >> 28) - 1
bgt 5f
lhz r11,PACAVMALLOCSLLP(r13)
b 6f
5:
-END_FTR_SECTION_IFCLR(CPU_FTR_CI_LARGE_PAGE)
-_GLOBAL(slb_miss_kernel_load_io)
+ /* IO mapping */
+ _GLOBAL(slb_miss_kernel_load_io)
li r11,0
6:
BEGIN_FTR_SECTION
^ permalink raw reply related
* Serial console under current qemu?
From: Rob Landley @ 2009-10-13 3:32 UTC (permalink / raw)
To: linuxppc-dev
Has anybody gotten a serial console to work under current qemu (ala the 0.11.0
release)?
I've tried the 2.6.30 and 2.6.31.4 kernels, and in both cases both the
bootloader and the kernel's boot messages write to the serial console just
fine, but as soon as userspace tries to write to /dev/console the kernel panics
with:
ieee1394: raw1394: /dev/raw1394 device initialized
mice: PS/2 mouse device common for all mice
TCP cubic registered
NET: Registered protocol family 17
VFS: Mounted root (squashfs filesystem) readonly on device 3:0.
Freeing unused kernel memory: 168k init
Type exit when done.Unable to handle kernel paging request for data at address
0x00000084
Faulting instruction address: 0xc012dc9c
Oops: Kernel access of bad area, sig: 11 [#1]
PowerMac
NIP: c012dc9c LR: c01467c0 CTR: c01467ac
REGS: cf831be0 TRAP: 0300 Not tainted (2.6.31.4)
MSR: 00009032 <EE,ME,IR,DR> CR: 42224022 XER: 00000000
DAR: 00000084, DSISR: 40000000
TASK = cf82f8f0[1] 'init.sh' THREAD: cf830000
GPR00: c01467c0 cf831c90 cf82f8f0 00000000 cf82f920 cf824e40 91a8bb6b 00000000
GPR08: 00000001 00000001 c01467ac 00000000 90778e6b 100834dc 0127e698 1005b940
GPR16: 100859a0 00000000 1007d074 100429a4 00000000 c02dc614 c0281678 c02dc498
GPR24: 0000000a cf830000 c0321e00 00000005 00000014 c0321e00 c02dc638 00000000
NIP [c012dc9c] tty_wakeup+0x14/0xa0
LR [c01467c0] uart_tasklet_action+0x14/0x24
Call Trace:
[cf831c90] [c012dcbc] tty_wakeup+0x34/0xa0 (unreliable)
[cf831ca0] [c01467c0] uart_tasklet_action+0x14/0x24
[cf831cb0] [c003123c] tasklet_action+0x80/0x104
[cf831cd0] [c0031368] __do_softirq+0xa8/0x120
[cf831d10] [c0006ea4] do_softirq+0x58/0x5c
[cf831d20] [c00311b8] irq_exit+0x98/0x9c
[cf831d30] [c0006f44] do_IRQ+0x9c/0xb4
[cf831d50] [c0012b60] ret_from_except+0x0/0x1c
--- Exception: 501 at uart_start+0x24/0x38
LR = uart_start+0x20/0x38
[cf831e20] [c0148130] uart_write+0xc0/0xe4
[cf831e50] [c0130bc0] n_tty_write+0x1d4/0x430
[cf831eb0] [c012db30] tty_write+0x188/0x268
[cf831ef0] [c0082314] vfs_write+0xb4/0x188
[cf831f10] [c008287c] sys_write+0x4c/0x90
[cf831f40] [c0012494] ret_from_syscall+0x0/0x40
--- Exception: c01 at 0x48039f8c
LR = 0x4804c4bc
Instruction dump:
7c0803a6 4e800020 80010024 bba10014 38210020 7c0803a6 4bfffd24 9421fff0
7c0802a6 bfc10008 7c7f1b78 90010014 <80030084> 70090020 4082002c 387f00e0
Kernel panic - not syncing: Fatal exception in interrupt
Call Trace:
[cf831b10] [c0008d74] show_stack+0x4c/0x16c (unreliable)
[cf831b50] [c002b600] panic+0x90/0x160
[cf831ba0] [c0010064] die+0x148/0x154
[cf831bc0] [c0015b34] bad_page_fault+0x90/0xd8
[cf831bd0] [c001294c] handle_page_fault+0x7c/0x80
--- Exception: 300 at tty_wakeup+0x14/0xa0
LR = uart_tasklet_action+0x14/0x24
[cf831c90] [c012dcbc] tty_wakeup+0x34/0xa0 (unreliable)
[cf831ca0] [c01467c0] uart_tasklet_action+0x14/0x24
[cf831cb0] [c003123c] tasklet_action+0x80/0x104
[cf831cd0] [c0031368] __do_softirq+0xa8/0x120
[cf831d10] [c0006ea4] do_softirq+0x58/0x5c
[cf831d20] [c00311b8] irq_exit+0x98/0x9c
[cf831d30] [c0006f44] do_IRQ+0x9c/0xb4
[cf831d50] [c0012b60] ret_from_except+0x0/0x1c
--- Exception: 501 at uart_start+0x24/0x38
LR = uart_start+0x20/0x38
[cf831e20] [c0148130] uart_write+0xc0/0xe4
[cf831e50] [c0130bc0] n_tty_write+0x1d4/0x430
[cf831eb0] [c012db30] tty_write+0x188/0x268
[cf831ef0] [c0082314] vfs_write+0xb4/0x188
[cf831f10] [c008287c] sys_write+0x4c/0x90
[cf831f40] [c0012494] ret_from_syscall+0x0/0x40
--- Exception: c01 at 0x48039f8c
LR = 0x4804c4bc
Rebooting in 1 seconds..
I've reproduced this with my Firmware Linux project (download
http://impactlinux.com/fwl/downloads/binaries/system-image-powerpc.tar.bz2 ,
extract it, run "sed -i 's@-hda @-hdc @' run-emulator.sh" because qemu's
device tree layout changed betwee 0.10.0 and 0.11.0, and then ./run-
emulator.sh).
I've also reproduced it with debian's kernel .config and root filesystem image,
details on that posted here:
http://lists.gnu.org/archive/html/qemu-devel/2009-10/msg01056.html
(Oddly, with debian's kernel .config -hda sets /dev/hda instead of /dev/hdc, I
need to track down why so I can fix it in my project's .config.)
The debian image boots fine with a graphics console, it's just trying to use
the serial console that panics it. I don't know if this is a qemu device
emulation issue, a kernel issue, or maybe something to do with the device tree
qemu's openbios is feeding in at boot time? I'm stumped.
Er... Help? Pretty please?
Rob
--
Latency is more important than throughput. It's that simple. - Linus Torvalds
^ permalink raw reply
* Re: [PATCH 2/8] bitmap: Introduce bitmap_set, bitmap_clear, bitmap_find_next_zero_area
From: Akinobu Mita @ 2009-10-13 2:18 UTC (permalink / raw)
To: Andrew Morton
Cc: Fenghua Yu, Greg Kroah-Hartman, linux-ia64, Tony Luck, x86,
netdev, linux-kernel, linux-altix, Yevgeny Petrilin,
FUJITA Tomonori, linuxppc-dev, Ingo Molnar, Paul Mackerras,
H. Peter Anvin, sparclinux, Thomas Gleixner, linux-usb,
David S. Miller, Lothar Wassmann
In-Reply-To: <20091009164100.85a36188.akpm@linux-foundation.org>
On Fri, Oct 09, 2009 at 04:41:00PM -0700, Andrew Morton wrote:
> On Fri, 9 Oct 2009 17:29:15 +0900
> Akinobu Mita <akinobu.mita@gmail.com> wrote:
>
> > This introduces new bitmap functions:
> >
> > bitmap_set: Set specified bit area
> > bitmap_clear: Clear specified bit area
> > bitmap_find_next_zero_area: Find free bit area
> >
> > These are stolen from iommu helper.
> >
> > I changed the return value of bitmap_find_next_zero_area if there is
> > no zero area.
> >
> > find_next_zero_area in iommu helper: returns -1
> > bitmap_find_next_zero_area: return >= bitmap size
>
> I'll plan to merge this patch into 2.6.32 so we can trickle all the
> other patches into subsystems in an orderly fashion.
Sounds good.
> > +void bitmap_set(unsigned long *map, int i, int len)
> > +{
> > + int end = i + len;
> > +
> > + while (i < end) {
> > + __set_bit(i, map);
> > + i++;
> > + }
> > +}
>
> This is really inefficient, isn't it? It's a pretty trivial matter to
> romp through memory 32 or 64 bits at a time.
OK. I'll do
> > +unsigned long bitmap_find_next_zero_area(unsigned long *map,
> > + unsigned long size,
> > + unsigned long start,
> > + unsigned int nr,
> > + unsigned long align_mask)
> > +{
> > + unsigned long index, end, i;
> > +again:
> > + index = find_next_zero_bit(map, size, start);
> > +
> > + /* Align allocation */
> > + index = (index + align_mask) & ~align_mask;
> > +
> > + end = index + nr;
> > + if (end >= size)
> > + return end;
> > + i = find_next_bit(map, end, index);
> > + if (i < end) {
> > + start = i + 1;
> > + goto again;
> > + }
> > + return index;
> > +}
> > +EXPORT_SYMBOL(bitmap_find_next_zero_area);
>
> This needs documentation, please. It appears that `size' is the size
> of the bitmap and `nr' is the number of zeroed bits we're looking for,
> but an inattentive programmer could get those reversed.
>
> Also the semantics of `align_mask' could benefit from spelling out. Is
> the alignment with respect to memory boundaries or with respect to
> `map' or with respect to map+start or what?
OK. I will document it.
And I plan to change bitmap_find_next_zero_area() to take the alignment
instead of an align_mask as Roland said.
> And why does align_mask exist at all? I was a bit surprised to see it
> there. In which scenarios will it be non-zero?
Because the users of iommu-helper and mlx4 need the alignment requirement
for the zero area.
arch/powerpc/kernel/iommu.c
arch/x86/kernel/amd_iommu.c
arch/x86/kernel/pci-gart_64.c
drivers/net/mlx4/alloc.c
^ permalink raw reply
* Re: [Cbe-oss-dev] [PATCH] spufs: Fix test in spufs_switch_log_read()
From: Jeremy Kerr @ 2009-10-13 0:49 UTC (permalink / raw)
To: cbe-oss-dev; +Cc: linuxppc-dev, Andrew Morton, Roel Kluin, cbe-oss-dev
In-Reply-To: <4AD32B3F.9060804@gmail.com>
Roel,
> Or can this test be removed?
I'd prefer just to remove the test.
Cheers,
Jeremy
^ permalink raw reply
* [Patch] powerpc: Fix memory leak in axon_msi.c
From: Michael Ellerman @ 2009-10-13 0:29 UTC (permalink / raw)
To: linuxppc-dev list; +Cc: eric.sesterhenn
cppcheck found a memory leak in axon_msi, if dcr_base or dcr_len are zero,
we have already allocated msic, so we should free it in the error path.
Signed-off-by: Eric Sesterhenn <eric.sesterhenn@lsexperts.de>
Acked-by: Michael Ellerman <michael@ellerman.id.au>
--- linux/arch/powerpc/platforms/cell/axon_msi.c.orig 2009-10-12 14:48:26.000000000 +0200
+++ linux/arch/powerpc/platforms/cell/axon_msi.c 2009-10-12 14:48:52.000000000 +0200
@@ -365,7 +365,7 @@ static int axon_msi_probe(struct of_devi
printk(KERN_ERR
"axon_msi: couldn't parse dcr properties on %s\n",
dn->full_name);
- goto out;
+ goto out_free_msic;
}
msic->dcr_host = dcr_map(dn, dcr_base, dcr_len);
^ permalink raw reply
* Re: [PATCH] of/platform: Implement support for dev_pm_ops
From: Anton Vorontsov @ 2009-10-12 22:44 UTC (permalink / raw)
To: Grant Likely; +Cc: linux-pm, David Miller, linuxppc-dev
In-Reply-To: <fa686aa40910121509n1b7dba87nbe9ddf34f0b4d7c@mail.gmail.com>
On Mon, Oct 12, 2009 at 03:09:53PM -0700, Grant Likely wrote:
> On Mon, Oct 12, 2009 at 8:50 AM, Anton Vorontsov
> <avorontsov@ru.mvista.com> wrote:
> > Linux power management subsystem supports vast amount of new PM
> > callbacks that are crucial for proper suspend and hibernation support
> > in drivers.
> >
> > This patch implements support for dev_pm_ops, preserving support
> > for legacy callbacks.
> >
> > Signed-off-by: Anton Vorontsov <avorontsov@ru.mvista.com>
>
> Hmmm... I'm not very familiar with the PM callbacks, but change
> doesn't look right to me. In particular, a lot of these new hooks
> don't do anything remotely of_platform bus specific. For example,
> of_platform_pm_prepare() checks if there is drv, drv->pm, and
> drv->pm->prepare. If all are true, then it calls drv->pm->prepare().
> I see that the platform bus platform_pm_prepare() function is
> absolutely identical. I haven't looked, but I wouldn't be surprised
> if other busses do the same.
>
> I think these simple pm ops should be made library functions that
> platform, of_platform and other simple busses can just populate their
> pm ops structure with.
Some hooks can be made as library functions, but some can't (for
example that do of_plaform_driver->suspend(), as opposite to
of_platform_driver->driver.suspend(), i.e. the legacy hooks).
Also, if you look into PCI bus hooks, you'll see that these hooks
aren't pure proxies for drivers, they do real work, so they won't
like to reuse or share anything.
For OF platfrom bus, we can share these functions with platform:
of_platform_pm_suspend_noirq
of_platform_pm_resume_noirq
of_platform_pm_freeze_noirq
of_platform_pm_thaw_noirq
of_platform_pm_poweroff_noirq
of_platform_pm_restore_noirq
These we cannot share:
of_platform_pm_suspend
of_platform_pm_resume
of_platform_pm_freeze
of_platform_pm_thaw
of_platform_pm_poweroff
of_platform_pm_restore
I agree that there is some room for improvements in general (e.g.
merging platform and of_platform devices/drivers), but it's not as
easy as you would like to think. Let's make it in a separate step
that don't stop real features from being implemented (e.g.
hibernate).
For the six functions that we can reuse I can prepare a cleanup
patch that we can merge via -mm, or it can just sit and collect
needed acks and can be merged via any tree. But please, no
cross-tree dependencies for the cruicial features.
Thanks,
--
Anton Vorontsov
email: cbouatmailru@gmail.com
irc://irc.freenode.net/bd2
^ permalink raw reply
* Re: [PATCH] of/platform: Implement support for dev_pm_ops
From: Grant Likely @ 2009-10-12 22:09 UTC (permalink / raw)
To: Anton Vorontsov; +Cc: linux-pm, David Miller, linuxppc-dev
In-Reply-To: <20091012155041.GA1071@oksana.dev.rtsoft.ru>
On Mon, Oct 12, 2009 at 8:50 AM, Anton Vorontsov
<avorontsov@ru.mvista.com> wrote:
> Linux power management subsystem supports vast amount of new PM
> callbacks that are crucial for proper suspend and hibernation support
> in drivers.
>
> This patch implements support for dev_pm_ops, preserving support
> for legacy callbacks.
>
> Signed-off-by: Anton Vorontsov <avorontsov@ru.mvista.com>
Hmmm... I'm not very familiar with the PM callbacks, but change
doesn't look right to me. In particular, a lot of these new hooks
don't do anything remotely of_platform bus specific. For example,
of_platform_pm_prepare() checks if there is drv, drv->pm, and
drv->pm->prepare. If all are true, then it calls drv->pm->prepare().
I see that the platform bus platform_pm_prepare() function is
absolutely identical. I haven't looked, but I wouldn't be surprised
if other busses do the same.
I think these simple pm ops should be made library functions that
platform, of_platform and other simple busses can just populate their
pm ops structure with.
g.
> ---
> =A0drivers/of/platform.c | =A0305 +++++++++++++++++++++++++++++++++++++++=
+++++++---
> =A01 files changed, 290 insertions(+), 15 deletions(-)
>
> diff --git a/drivers/of/platform.c b/drivers/of/platform.c
> index 298de0f..d58ade1 100644
> --- a/drivers/of/platform.c
> +++ b/drivers/of/platform.c
> @@ -65,47 +65,322 @@ static int of_platform_device_remove(struct device *=
dev)
> =A0 =A0 =A0 =A0return 0;
> =A0}
>
> -static int of_platform_device_suspend(struct device *dev, pm_message_t s=
tate)
> +static void of_platform_device_shutdown(struct device *dev)
> =A0{
> =A0 =A0 =A0 =A0struct of_device *of_dev =3D to_of_device(dev);
> =A0 =A0 =A0 =A0struct of_platform_driver *drv =3D to_of_platform_driver(d=
ev->driver);
> - =A0 =A0 =A0 int error =3D 0;
>
> - =A0 =A0 =A0 if (dev->driver && drv->suspend)
> - =A0 =A0 =A0 =A0 =A0 =A0 =A0 error =3D drv->suspend(of_dev, state);
> - =A0 =A0 =A0 return error;
> + =A0 =A0 =A0 if (dev->driver && drv->shutdown)
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 drv->shutdown(of_dev);
> =A0}
>
> -static int of_platform_device_resume(struct device * dev)
> +#ifdef CONFIG_PM_SLEEP
> +
> +static int of_platform_legacy_suspend(struct device *dev, pm_message_t m=
esg)
> =A0{
> =A0 =A0 =A0 =A0struct of_device *of_dev =3D to_of_device(dev);
> =A0 =A0 =A0 =A0struct of_platform_driver *drv =3D to_of_platform_driver(d=
ev->driver);
> - =A0 =A0 =A0 int error =3D 0;
> + =A0 =A0 =A0 int ret =3D 0;
>
> - =A0 =A0 =A0 if (dev->driver && drv->resume)
> - =A0 =A0 =A0 =A0 =A0 =A0 =A0 error =3D drv->resume(of_dev);
> - =A0 =A0 =A0 return error;
> + =A0 =A0 =A0 if (dev->driver && drv->suspend)
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 ret =3D drv->suspend(of_dev, mesg);
> + =A0 =A0 =A0 return ret;
> =A0}
>
> -static void of_platform_device_shutdown(struct device *dev)
> +static int of_platform_legacy_resume(struct device *dev)
> =A0{
> =A0 =A0 =A0 =A0struct of_device *of_dev =3D to_of_device(dev);
> =A0 =A0 =A0 =A0struct of_platform_driver *drv =3D to_of_platform_driver(d=
ev->driver);
> + =A0 =A0 =A0 int ret =3D 0;
>
> - =A0 =A0 =A0 if (dev->driver && drv->shutdown)
> - =A0 =A0 =A0 =A0 =A0 =A0 =A0 drv->shutdown(of_dev);
> + =A0 =A0 =A0 if (dev->driver && drv->resume)
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 ret =3D drv->resume(of_dev);
> + =A0 =A0 =A0 return ret;
> +}
> +
> +static int of_platform_pm_prepare(struct device *dev)
> +{
> + =A0 =A0 =A0 struct device_driver *drv =3D dev->driver;
> + =A0 =A0 =A0 int ret =3D 0;
> +
> + =A0 =A0 =A0 if (drv && drv->pm && drv->pm->prepare)
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 ret =3D drv->pm->prepare(dev);
> +
> + =A0 =A0 =A0 return ret;
> +}
> +
> +static void of_platform_pm_complete(struct device *dev)
> +{
> + =A0 =A0 =A0 struct device_driver *drv =3D dev->driver;
> +
> + =A0 =A0 =A0 if (drv && drv->pm && drv->pm->complete)
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 drv->pm->complete(dev);
> +}
> +
> +#ifdef CONFIG_SUSPEND
> +
> +static int of_platform_pm_suspend(struct device *dev)
> +{
> + =A0 =A0 =A0 struct device_driver *drv =3D dev->driver;
> + =A0 =A0 =A0 int ret =3D 0;
> +
> + =A0 =A0 =A0 if (!drv)
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 return 0;
> +
> + =A0 =A0 =A0 if (drv->pm) {
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 if (drv->pm->suspend)
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 ret =3D drv->pm->suspend(de=
v);
> + =A0 =A0 =A0 } else {
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 ret =3D of_platform_legacy_suspend(dev, PMS=
G_SUSPEND);
> + =A0 =A0 =A0 }
> +
> + =A0 =A0 =A0 return ret;
> =A0}
>
> +static int of_platform_pm_suspend_noirq(struct device *dev)
> +{
> + =A0 =A0 =A0 struct device_driver *drv =3D dev->driver;
> + =A0 =A0 =A0 int ret =3D 0;
> +
> + =A0 =A0 =A0 if (!drv)
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 return 0;
> +
> + =A0 =A0 =A0 if (drv->pm) {
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 if (drv->pm->suspend_noirq)
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 ret =3D drv->pm->suspend_no=
irq(dev);
> + =A0 =A0 =A0 }
> +
> + =A0 =A0 =A0 return ret;
> +}
> +
> +static int of_platform_pm_resume(struct device *dev)
> +{
> + =A0 =A0 =A0 struct device_driver *drv =3D dev->driver;
> + =A0 =A0 =A0 int ret =3D 0;
> +
> + =A0 =A0 =A0 if (!drv)
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 return 0;
> +
> + =A0 =A0 =A0 if (drv->pm) {
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 if (drv->pm->resume)
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 ret =3D drv->pm->resume(dev=
);
> + =A0 =A0 =A0 } else {
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 ret =3D of_platform_legacy_resume(dev);
> + =A0 =A0 =A0 }
> +
> + =A0 =A0 =A0 return ret;
> +}
> +
> +static int of_platform_pm_resume_noirq(struct device *dev)
> +{
> + =A0 =A0 =A0 struct device_driver *drv =3D dev->driver;
> + =A0 =A0 =A0 int ret =3D 0;
> +
> + =A0 =A0 =A0 if (!drv)
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 return 0;
> +
> + =A0 =A0 =A0 if (drv->pm) {
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 if (drv->pm->resume_noirq)
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 ret =3D drv->pm->resume_noi=
rq(dev);
> + =A0 =A0 =A0 }
> +
> + =A0 =A0 =A0 return ret;
> +}
> +
> +#else /* !CONFIG_SUSPEND */
> +
> +#define of_platform_pm_suspend =A0 =A0 =A0 =A0 NULL
> +#define of_platform_pm_resume =A0 =A0 =A0 =A0 =A0NULL
> +#define of_platform_pm_suspend_noirq =A0 NULL
> +#define of_platform_pm_resume_noirq =A0 =A0NULL
> +
> +#endif /* !CONFIG_SUSPEND */
> +
> +#ifdef CONFIG_HIBERNATION
> +
> +static int of_platform_pm_freeze(struct device *dev)
> +{
> + =A0 =A0 =A0 struct device_driver *drv =3D dev->driver;
> + =A0 =A0 =A0 int ret =3D 0;
> +
> + =A0 =A0 =A0 if (!drv)
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 return 0;
> +
> + =A0 =A0 =A0 if (drv->pm) {
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 if (drv->pm->freeze)
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 ret =3D drv->pm->freeze(dev=
);
> + =A0 =A0 =A0 } else {
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 ret =3D of_platform_legacy_suspend(dev, PMS=
G_FREEZE);
> + =A0 =A0 =A0 }
> +
> + =A0 =A0 =A0 return ret;
> +}
> +
> +static int of_platform_pm_freeze_noirq(struct device *dev)
> +{
> + =A0 =A0 =A0 struct device_driver *drv =3D dev->driver;
> + =A0 =A0 =A0 int ret =3D 0;
> +
> + =A0 =A0 =A0 if (!drv)
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 return 0;
> +
> + =A0 =A0 =A0 if (drv->pm) {
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 if (drv->pm->freeze_noirq)
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 ret =3D drv->pm->freeze_noi=
rq(dev);
> + =A0 =A0 =A0 }
> +
> + =A0 =A0 =A0 return ret;
> +}
> +
> +static int of_platform_pm_thaw(struct device *dev)
> +{
> + =A0 =A0 =A0 struct device_driver *drv =3D dev->driver;
> + =A0 =A0 =A0 int ret =3D 0;
> +
> + =A0 =A0 =A0 if (!drv)
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 return 0;
> +
> + =A0 =A0 =A0 if (drv->pm) {
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 if (drv->pm->thaw)
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 ret =3D drv->pm->thaw(dev);
> + =A0 =A0 =A0 } else {
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 ret =3D of_platform_legacy_resume(dev);
> + =A0 =A0 =A0 }
> +
> + =A0 =A0 =A0 return ret;
> +}
> +
> +static int of_platform_pm_thaw_noirq(struct device *dev)
> +{
> + =A0 =A0 =A0 struct device_driver *drv =3D dev->driver;
> + =A0 =A0 =A0 int ret =3D 0;
> +
> + =A0 =A0 =A0 if (!drv)
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 return 0;
> +
> + =A0 =A0 =A0 if (drv->pm) {
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 if (drv->pm->thaw_noirq)
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 ret =3D drv->pm->thaw_noirq=
(dev);
> + =A0 =A0 =A0 }
> +
> + =A0 =A0 =A0 return ret;
> +}
> +
> +static int of_platform_pm_poweroff(struct device *dev)
> +{
> + =A0 =A0 =A0 struct device_driver *drv =3D dev->driver;
> + =A0 =A0 =A0 int ret =3D 0;
> +
> + =A0 =A0 =A0 if (!drv)
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 return 0;
> +
> + =A0 =A0 =A0 if (drv->pm) {
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 if (drv->pm->poweroff)
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 ret =3D drv->pm->poweroff(d=
ev);
> + =A0 =A0 =A0 } else {
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 ret =3D of_platform_legacy_suspend(dev, PMS=
G_HIBERNATE);
> + =A0 =A0 =A0 }
> +
> + =A0 =A0 =A0 return ret;
> +}
> +
> +static int of_platform_pm_poweroff_noirq(struct device *dev)
> +{
> + =A0 =A0 =A0 struct device_driver *drv =3D dev->driver;
> + =A0 =A0 =A0 int ret =3D 0;
> +
> + =A0 =A0 =A0 if (!drv)
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 return 0;
> +
> + =A0 =A0 =A0 if (drv->pm) {
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 if (drv->pm->poweroff_noirq)
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 ret =3D drv->pm->poweroff_n=
oirq(dev);
> + =A0 =A0 =A0 }
> +
> + =A0 =A0 =A0 return ret;
> +}
> +
> +static int of_platform_pm_restore(struct device *dev)
> +{
> + =A0 =A0 =A0 struct device_driver *drv =3D dev->driver;
> + =A0 =A0 =A0 int ret =3D 0;
> +
> + =A0 =A0 =A0 if (!drv)
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 return 0;
> +
> + =A0 =A0 =A0 if (drv->pm) {
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 if (drv->pm->restore)
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 ret =3D drv->pm->restore(de=
v);
> + =A0 =A0 =A0 } else {
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 ret =3D of_platform_legacy_resume(dev);
> + =A0 =A0 =A0 }
> +
> + =A0 =A0 =A0 return ret;
> +}
> +
> +static int of_platform_pm_restore_noirq(struct device *dev)
> +{
> + =A0 =A0 =A0 struct device_driver *drv =3D dev->driver;
> + =A0 =A0 =A0 int ret =3D 0;
> +
> + =A0 =A0 =A0 if (!drv)
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 return 0;
> +
> + =A0 =A0 =A0 if (drv->pm) {
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 if (drv->pm->restore_noirq)
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 ret =3D drv->pm->restore_no=
irq(dev);
> + =A0 =A0 =A0 }
> +
> + =A0 =A0 =A0 return ret;
> +}
> +
> +#else /* !CONFIG_HIBERNATION */
> +
> +#define of_platform_pm_freeze =A0 =A0 =A0 =A0 =A0NULL
> +#define of_platform_pm_thaw =A0 =A0 =A0 =A0 =A0 =A0NULL
> +#define of_platform_pm_poweroff =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0NULL
> +#define of_platform_pm_restore =A0 =A0 =A0 =A0 NULL
> +#define of_platform_pm_freeze_noirq =A0 =A0NULL
> +#define of_platform_pm_thaw_noirq =A0 =A0 =A0 =A0 =A0 =A0 =A0NULL
> +#define of_platform_pm_poweroff_noirq =A0NULL
> +#define of_platform_pm_restore_noirq =A0 NULL
> +
> +#endif /* !CONFIG_HIBERNATION */
> +
> +static struct dev_pm_ops of_platform_dev_pm_ops =3D {
> + =A0 =A0 =A0 .prepare =3D of_platform_pm_prepare,
> + =A0 =A0 =A0 .complete =3D of_platform_pm_complete,
> + =A0 =A0 =A0 .suspend =3D of_platform_pm_suspend,
> + =A0 =A0 =A0 .resume =3D of_platform_pm_resume,
> + =A0 =A0 =A0 .freeze =3D of_platform_pm_freeze,
> + =A0 =A0 =A0 .thaw =3D of_platform_pm_thaw,
> + =A0 =A0 =A0 .poweroff =3D of_platform_pm_poweroff,
> + =A0 =A0 =A0 .restore =3D of_platform_pm_restore,
> + =A0 =A0 =A0 .suspend_noirq =3D of_platform_pm_suspend_noirq,
> + =A0 =A0 =A0 .resume_noirq =3D of_platform_pm_resume_noirq,
> + =A0 =A0 =A0 .freeze_noirq =3D of_platform_pm_freeze_noirq,
> + =A0 =A0 =A0 .thaw_noirq =3D of_platform_pm_thaw_noirq,
> + =A0 =A0 =A0 .poweroff_noirq =3D of_platform_pm_poweroff_noirq,
> + =A0 =A0 =A0 .restore_noirq =3D of_platform_pm_restore_noirq,
> +};
> +
> +#define OF_PLATFORM_PM_OPS_PTR (&of_platform_dev_pm_ops)
> +
> +#else /* !CONFIG_PM_SLEEP */
> +
> +#define OF_PLATFORM_PM_OPS_PTR NULL
> +
> +#endif /* !CONFIG_PM_SLEEP */
> +
> =A0int of_bus_type_init(struct bus_type *bus, const char *name)
> =A0{
> =A0 =A0 =A0 =A0bus->name =3D name;
> =A0 =A0 =A0 =A0bus->match =3D of_platform_bus_match;
> =A0 =A0 =A0 =A0bus->probe =3D of_platform_device_probe;
> =A0 =A0 =A0 =A0bus->remove =3D of_platform_device_remove;
> - =A0 =A0 =A0 bus->suspend =3D of_platform_device_suspend;
> - =A0 =A0 =A0 bus->resume =3D of_platform_device_resume;
> =A0 =A0 =A0 =A0bus->shutdown =3D of_platform_device_shutdown;
> =A0 =A0 =A0 =A0bus->dev_attrs =3D of_platform_device_attrs;
> + =A0 =A0 =A0 bus->pm =3D OF_PLATFORM_PM_OPS_PTR;
> =A0 =A0 =A0 =A0return bus_register(bus);
> =A0}
>
> --
> 1.6.3.3
>
--=20
Grant Likely, B.Sc., P.Eng.
Secret Lab Technologies Ltd.
^ permalink raw reply
* [PATCH 2/2][v4] powerpc: Make the CMM memory hotplug aware
From: Robert Jennings @ 2009-10-12 19:49 UTC (permalink / raw)
To: Benjamin Herrenschmidt
Cc: linux-mm, Mel Gorman, Gerald Schaefer, linux-kernel, linuxppc-dev,
Martin Schwidefsky, Badari Pulavarty, Brian King, Paul Mackerras,
Andrew Morton, Ingo Molnar, KAMEZAWA Hiroyuki
In-Reply-To: <1255324007.2192.106.camel@pasglop>
The Collaborative Memory Manager (CMM) module allocates individual pages
over time that are not migratable. On a long running system this can
severely impact the ability to find enough pages to support a hotplug
memory remove operation.
This patch adds a memory isolation notifier and a memory hotplug notifier.
The memory isolation notifier will return the number of pages found
in the range specified. This is used to determine if all of the used
pages in a pageblock are owned by the balloon (or other entities in
the notifier chain). The hotplug notifier will free pages in the range
which is to be removed. The priority of this hotplug notifier is low
so that it will be called near last, this helps avoids removing loaned
pages in operations that fail due to other handlers.
CMM activity will be halted when hotplug remove operations are active
and resume activity after a delay period to allow the hypervisor time
to adjust.
Signed-off-by: Robert Jennings <rcj@linux.vnet.ibm.com>
---
Changes since v3:
* Changed from atomic to mutex for hotplug state tracking.
* Clarified documentation for the new module parameter description.
Changes since v2:
* None, resent with parent patch to keep them together.
arch/powerpc/platforms/pseries/cmm.c | 221 ++++++++++++++++++++++++++++++++++-
1 file changed, 215 insertions(+), 6 deletions(-)
Index: b/arch/powerpc/platforms/pseries/cmm.c
===================================================================
--- a/arch/powerpc/platforms/pseries/cmm.c
+++ b/arch/powerpc/platforms/pseries/cmm.c
@@ -38,19 +38,28 @@
#include <asm/mmu.h>
#include <asm/pgalloc.h>
#include <asm/uaccess.h>
+#include <linux/memory.h>
#include "plpar_wrappers.h"
#define CMM_DRIVER_VERSION "1.0.0"
#define CMM_DEFAULT_DELAY 1
+#define CMM_HOTPLUG_DELAY 5
#define CMM_DEBUG 0
#define CMM_DISABLE 0
#define CMM_OOM_KB 1024
#define CMM_MIN_MEM_MB 256
#define KB2PAGES(_p) ((_p)>>(PAGE_SHIFT-10))
#define PAGES2KB(_p) ((_p)<<(PAGE_SHIFT-10))
+/*
+ * The priority level tries to ensure that this notifier is called as
+ * late as possible to reduce thrashing in the shared memory pool.
+ */
+#define CMM_MEM_HOTPLUG_PRI 1
+#define CMM_MEM_ISOLATE_PRI 15
static unsigned int delay = CMM_DEFAULT_DELAY;
+static unsigned int hotplug_delay = CMM_HOTPLUG_DELAY;
static unsigned int oom_kb = CMM_OOM_KB;
static unsigned int cmm_debug = CMM_DEBUG;
static unsigned int cmm_disabled = CMM_DISABLE;
@@ -65,6 +74,10 @@ MODULE_VERSION(CMM_DRIVER_VERSION);
module_param_named(delay, delay, uint, S_IRUGO | S_IWUSR);
MODULE_PARM_DESC(delay, "Delay (in seconds) between polls to query hypervisor paging requests. "
"[Default=" __stringify(CMM_DEFAULT_DELAY) "]");
+module_param_named(hotplug_delay, hotplug_delay, uint, S_IRUGO | S_IWUSR);
+MODULE_PARM_DESC(delay, "Delay (in seconds) after memory hotplug remove "
+ "before loaning resumes. "
+ "[Default=" __stringify(CMM_HOTPLUG_DELAY) "]");
module_param_named(oom_kb, oom_kb, uint, S_IRUGO | S_IWUSR);
MODULE_PARM_DESC(oom_kb, "Amount of memory in kb to free on OOM. "
"[Default=" __stringify(CMM_OOM_KB) "]");
@@ -92,6 +105,9 @@ static unsigned long oom_freed_pages;
static struct cmm_page_array *cmm_page_list;
static DEFINE_SPINLOCK(cmm_lock);
+static DEFINE_MUTEX(hotplug_mutex);
+static int hotplug_occurred; /* protected by the hotplug mutex */
+
static struct task_struct *cmm_thread_ptr;
/**
@@ -110,6 +126,17 @@ static long cmm_alloc_pages(long nr)
cmm_dbg("Begin request for %ld pages\n", nr);
while (nr) {
+ /* Exit if a hotplug operation is in progress or occurred */
+ if (mutex_trylock(&hotplug_mutex)) {
+ if (hotplug_occurred) {
+ mutex_unlock(&hotplug_mutex);
+ break;
+ }
+ mutex_unlock(&hotplug_mutex);
+ } else {
+ break;
+ }
+
addr = __get_free_page(GFP_NOIO | __GFP_NOWARN |
__GFP_NORETRY | __GFP_NOMEMALLOC);
if (!addr)
@@ -119,8 +146,10 @@ static long cmm_alloc_pages(long nr)
if (!pa || pa->index >= CMM_NR_PAGES) {
/* Need a new page for the page list. */
spin_unlock(&cmm_lock);
- npa = (struct cmm_page_array *)__get_free_page(GFP_NOIO | __GFP_NOWARN |
- __GFP_NORETRY | __GFP_NOMEMALLOC);
+ npa = (struct cmm_page_array *)__get_free_page(
+ GFP_NOIO | __GFP_NOWARN |
+ __GFP_NORETRY | __GFP_NOMEMALLOC |
+ __GFP_MOVABLE);
if (!npa) {
pr_info("%s: Can not allocate new page list\n", __func__);
free_page(addr);
@@ -273,9 +302,28 @@ static int cmm_thread(void *dummy)
while (1) {
timeleft = msleep_interruptible(delay * 1000);
- if (kthread_should_stop() || timeleft) {
- loaned_pages_target = loaned_pages;
+ if (kthread_should_stop() || timeleft)
break;
+
+ if (mutex_trylock(&hotplug_mutex)) {
+ if (hotplug_occurred) {
+ hotplug_occurred = 0;
+ mutex_unlock(&hotplug_mutex);
+ cmm_dbg("Hotplug operation has occurred, "
+ "loaning activity suspended "
+ "for %d seconds.\n",
+ hotplug_delay);
+ timeleft = msleep_interruptible(hotplug_delay *
+ 1000);
+ if (kthread_should_stop() || timeleft)
+ break;
+ continue;
+ }
+ mutex_unlock(&hotplug_mutex);
+ } else {
+ cmm_dbg("Hotplug operation in progress, activity "
+ "suspended\n");
+ continue;
}
cmm_get_mpp();
@@ -405,6 +453,159 @@ static struct notifier_block cmm_reboot_
};
/**
+ * cmm_count_pages - Count the number of pages loaned in a particular range.
+ *
+ * @arg: memory_isolate_notify structure with address range and count
+ *
+ * Return value:
+ * 0 on success
+ **/
+static unsigned long cmm_count_pages(void *arg)
+{
+ struct memory_isolate_notify *marg = arg;
+ struct cmm_page_array *pa;
+ unsigned long start = (unsigned long)pfn_to_kaddr(marg->start_pfn);
+ unsigned long end = start + (marg->nr_pages << PAGE_SHIFT);
+ unsigned long idx;
+
+ spin_lock(&cmm_lock);
+ pa = cmm_page_list;
+ while (pa) {
+ for (idx = 0; idx < pa->index; idx++)
+ if (pa->page[idx] >= start && pa->page[idx] < end)
+ marg->pages_found++;
+ pa = pa->next;
+ }
+ spin_unlock(&cmm_lock);
+ return 0;
+}
+
+/**
+ * cmm_memory_isolate_cb - Handle memory isolation notifier calls
+ * @self: notifier block struct
+ * @action: action to take
+ * @arg: struct memory_isolate_notify data for handler
+ *
+ * Return value:
+ * NOTIFY_OK or notifier error based on subfunction return value
+ **/
+static int cmm_memory_isolate_cb(struct notifier_block *self,
+ unsigned long action, void *arg)
+{
+ int ret = 0;
+
+ if (action == MEM_ISOLATE_COUNT)
+ ret = cmm_count_pages(arg);
+
+ if (ret)
+ ret = notifier_from_errno(ret);
+ else
+ ret = NOTIFY_OK;
+
+ return ret;
+}
+
+static struct notifier_block cmm_mem_isolate_nb = {
+ .notifier_call = cmm_memory_isolate_cb,
+ .priority = CMM_MEM_ISOLATE_PRI
+};
+
+/**
+ * cmm_mem_going_offline - Unloan pages where memory is to be removed
+ * @arg: memory_notify structure with page range to be offlined
+ *
+ * Return value:
+ * 0 on success
+ **/
+static int cmm_mem_going_offline(void *arg)
+{
+ struct memory_notify *marg = arg;
+ unsigned long start_page = (unsigned long)pfn_to_kaddr(marg->start_pfn);
+ unsigned long end_page = start_page + (marg->nr_pages << PAGE_SHIFT);
+ struct cmm_page_array *pa_curr, *pa_last;
+ unsigned long idx;
+ unsigned long freed = 0;
+
+ cmm_dbg("Memory going offline, searching 0x%lx (%ld pages).\n",
+ start_page, marg->nr_pages);
+ spin_lock(&cmm_lock);
+
+ pa_last = pa_curr = cmm_page_list;
+ while (pa_curr) {
+ for (idx = (pa_curr->index - 1); (idx + 1) > 0; idx--) {
+ if ((pa_curr->page[idx] < start_page) ||
+ (pa_curr->page[idx] >= end_page))
+ continue;
+
+ plpar_page_set_active(__pa(pa_curr->page[idx]));
+ free_page(pa_curr->page[idx]);
+ freed++;
+ loaned_pages--;
+ totalram_pages++;
+ pa_curr->page[idx] = pa_last->page[--pa_last->index];
+ if (pa_last->index == 0) {
+ if (pa_curr == pa_last)
+ pa_curr = pa_last->next;
+ pa_last = pa_last->next;
+ free_page((unsigned long)cmm_page_list);
+ cmm_page_list = pa_last;
+ continue;
+ }
+ }
+ pa_curr = pa_curr->next;
+ }
+ spin_unlock(&cmm_lock);
+ cmm_dbg("Released %ld pages in the search range.\n", freed);
+
+ return 0;
+}
+
+/**
+ * cmm_memory_cb - Handle memory hotplug notifier calls
+ * @self: notifier block struct
+ * @action: action to take
+ * @arg: struct memory_notify data for handler
+ *
+ * Return value:
+ * NOTIFY_OK or notifier error based on subfunction return value
+ *
+ **/
+static int cmm_memory_cb(struct notifier_block *self,
+ unsigned long action, void *arg)
+{
+ int ret = 0;
+
+ switch (action) {
+ case MEM_GOING_OFFLINE:
+ mutex_lock(&hotplug_mutex);
+ hotplug_occurred = 1;
+ ret = cmm_mem_going_offline(arg);
+ break;
+ case MEM_OFFLINE:
+ case MEM_CANCEL_OFFLINE:
+ mutex_unlock(&hotplug_mutex);
+ cmm_dbg("Memory offline operation complete.\n");
+ break;
+ case MEM_GOING_ONLINE:
+ case MEM_ONLINE:
+ case MEM_CANCEL_ONLINE:
+ break;
+ }
+
+ if (ret)
+ ret = notifier_from_errno(ret);
+ else
+ ret = NOTIFY_OK;
+
+ return ret;
+}
+
+static struct notifier_block cmm_mem_nb = {
+ .notifier_call = cmm_memory_cb,
+ .priority = CMM_MEM_HOTPLUG_PRI
+};
+
+/**
* cmm_init - Module initialization
*
* Return value:
@@ -426,18 +627,24 @@ static int cmm_init(void)
if ((rc = cmm_sysfs_register(&cmm_sysdev)))
goto out_reboot_notifier;
+ if (register_memory_notifier(&cmm_mem_nb) ||
+ register_memory_isolate_notifier(&cmm_mem_isolate_nb))
+ goto out_unregister_notifier;
+
if (cmm_disabled)
return rc;
cmm_thread_ptr = kthread_run(cmm_thread, NULL, "cmmthread");
if (IS_ERR(cmm_thread_ptr)) {
rc = PTR_ERR(cmm_thread_ptr);
- goto out_unregister_sysfs;
+ goto out_unregister_notifier;
}
return rc;
-out_unregister_sysfs:
+out_unregister_notifier:
+ unregister_memory_notifier(&cmm_mem_nb);
+ unregister_memory_isolate_notifier(&cmm_mem_isolate_nb);
cmm_unregister_sysfs(&cmm_sysdev);
out_reboot_notifier:
unregister_reboot_notifier(&cmm_reboot_nb);
@@ -458,6 +665,8 @@ static void cmm_exit(void)
kthread_stop(cmm_thread_ptr);
unregister_oom_notifier(&cmm_oom_nb);
unregister_reboot_notifier(&cmm_reboot_nb);
+ unregister_memory_notifier(&cmm_mem_nb);
+ unregister_memory_isolate_notifier(&cmm_mem_isolate_nb);
cmm_free_pages(loaned_pages);
cmm_unregister_sysfs(&cmm_sysdev);
}
^ permalink raw reply
* Re: [PATCH 2/2][v3] powerpc: Make the CMM memory hotplug aware
From: Robert Jennings @ 2009-10-12 19:23 UTC (permalink / raw)
To: Benjamin Herrenschmidt
Cc: linux-mm, Mel Gorman, Gerald Schaefer, linux-kernel, linuxppc-dev,
Martin Schwidefsky, Badari Pulavarty, Brian King, Paul Mackerras,
Andrew Morton, Ingo Molnar, KAMEZAWA Hiroyuki
In-Reply-To: <1255324007.2192.106.camel@pasglop>
* Benjamin Herrenschmidt (benh@kernel.crashing.org) wrote:
> On Fri, 2009-10-09 at 15:41 -0500, Robert Jennings wrote:
> > The Collaborative Memory Manager (CMM) module allocates individual pages
> > over time that are not migratable. On a long running system this can
> > severely impact the ability to find enough pages to support a hotplug
> > memory remove operation.
> >
> > This patch adds a memory isolation notifier and a memory hotplug notifier.
> > The memory isolation notifier will return the number of pages found
> > in the range specified. This is used to determine if all of the used
> > pages in a pageblock are owned by the balloon (or other entities in
> > the notifier chain). The hotplug notifier will free pages in the range
> > which is to be removed. The priority of this hotplug notifier is low
> > so that it will be called near last, this helps avoids removing loaned
> > pages in operations that fail due to other handlers.
> >
> > CMM activity will be halted when hotplug remove operations are active
> > and resume activity after a delay period to allow the hypervisor time
> > to adjust.
> >
> > Signed-off-by: Robert Jennings <rcj@linux.vnet.ibm.com>
>
> Do you need me to merge that via the powerpc tree after the relevant
> generic parts go in ? This is 2.6.33 material ?
I didn't know how this part works honestly, this is the first time I've
pushed patches with dependencies like this. Andrew Morton had pulled
an earlier version of this and the mm hotplug related changes for 2.6.32.
> > +module_param_named(hotplug_delay, hotplug_delay, uint, S_IRUGO | S_IWUSR);
> > +MODULE_PARM_DESC(delay, "Delay (in seconds) after memory hotplug remove "
> > + "before activity resumes. "
> > + "[Default=" __stringify(CMM_HOTPLUG_DELAY) "]");
>
> What is the above ? That sounds scary :-)
I'm changing this to read "Delay (in seconds) after memory hotplug
remove before loaning resumes." in order to clear this us. This is a
period where loaning from the balloon is paused after the hotplug
completes. This gives the userspace tools time to mark the sections
as isolated and unusable with the hypervisor and the hypervisor to take
this into account regarding the loaning levels it requests of the OS.
> > module_param_named(oom_kb, oom_kb, uint, S_IRUGO | S_IWUSR);
> > MODULE_PARM_DESC(oom_kb, "Amount of memory in kb to free on OOM. "
> > "[Default=" __stringify(CMM_OOM_KB) "]");
> > @@ -88,6 +101,8 @@ struct cmm_page_array {
> > static unsigned long loaned_pages;
> > static unsigned long loaned_pages_target;
> > static unsigned long oom_freed_pages;
> > +static atomic_t hotplug_active = ATOMIC_INIT(0);
> > +static atomic_t hotplug_occurred = ATOMIC_INIT(0);
>
> That sounds like a hand made lock with atomics... rarely a good idea,
> tends to miss appropriate barriers etc...
>
I have changes this so that we have a mutex held during the memory
hotplug remove. The hotplug_occurred flag is now and integer protected
by the mutex; it is used to provide the delay after the hotplug remove
completes.
> > static struct cmm_page_array *cmm_page_list;
> > static DEFINE_SPINLOCK(cmm_lock);
> > @@ -110,6 +125,9 @@ static long cmm_alloc_pages(long nr)
> > cmm_dbg("Begin request for %ld pages\n", nr);
> >
> > while (nr) {
> > + if (atomic_read(&hotplug_active))
> > + break;
> > +
>
> Ok so I'm not familiar with that whole memory hotplug stuff, so the code
> might be right, but wouldn't the above be racy anyways in case hotplug
> just becomes active after this statement ?
>
> Shouldn't you use a mutex_trylock instead ? That has clearer semantics
> and will provide the appropriate memory barriers.
I have changed this to use a mutex in the same location. This allows
the allocation of pages to terminate early during a hotplug remove
operation.
If hotplug becomes active after this check in cmm_alloc_pages() one page
will be allocated to the balloon, but this page will not belong to the
memory range going offline because the pageblock will have already been
isolated. After allocating the page we might need to wait on the lock to
add the page to the list if hotplug is removing pages from the balloon.
After one page is added, cmm_alloc_pages() will exit early when it checks
to see if hotplug is active or if it has occurred.
I wanted to keep the section locked by cmm_lock small, so that we can
safely respond to the hotplug notifier as quickly as possible while
minimizing memory pressure.
There are no checks in cmm_free_pages() to have it abort early during
hotplug memory remove with the rationale that it's good for hotplug
to allow the balloon to shrink even if it means holding the cmm_lock a
bit longer.
> > addr = __get_free_page(GFP_NOIO | __GFP_NOWARN |
> > __GFP_NORETRY | __GFP_NOMEMALLOC);
> > if (!addr)
> > @@ -119,8 +137,10 @@ static long cmm_alloc_pages(long nr)
> > if (!pa || pa->index >= CMM_NR_PAGES) {
> > /* Need a new page for the page list. */
> > spin_unlock(&cmm_lock);
> > - npa = (struct cmm_page_array *)__get_free_page(GFP_NOIO | __GFP_NOWARN |
> > - __GFP_NORETRY | __GFP_NOMEMALLOC);
> > + npa = (struct cmm_page_array *)__get_free_page(
> > + GFP_NOIO | __GFP_NOWARN |
> > + __GFP_NORETRY | __GFP_NOMEMALLOC |
> > + __GFP_MOVABLE);
> > if (!npa) {
> > pr_info("%s: Can not allocate new page list\n", __func__);
> > free_page(addr);
> > @@ -273,9 +293,23 @@ static int cmm_thread(void *dummy)
> > while (1) {
> > timeleft = msleep_interruptible(delay * 1000);
> >
> > - if (kthread_should_stop() || timeleft) {
> > - loaned_pages_target = loaned_pages;
> > + if (kthread_should_stop() || timeleft)
> > break;
> > +
> > + if (atomic_read(&hotplug_active)) {
> > + cmm_dbg("Hotplug operation in progress, activity "
> > + "suspended\n");
> > + continue;
> > + }
> > +
> > + if (atomic_dec_if_positive(&hotplug_occurred) >= 0) {
> > + cmm_dbg("Hotplug operation has occurred, loaning "
> > + "activity suspended for %d seconds.\n",
> > + hotplug_delay);
> > + timeleft = msleep_interruptible(hotplug_delay * 1000);
> > + if (kthread_should_stop() || timeleft)
> > + break;
> > + continue;
> > }
>
> I have less problems with hotplug_occured but if you use a
> mutex_trylock, overall, you can turn the above into a normal int instead
> of an atomic.
Changed to a mutex to indicate that a hotplug operation is active and an
int protected by the mutex to indicate that a hotplug operation has
occurred.
> ../..
>
> > +static int cmm_memory_cb(struct notifier_block *self,
> > + unsigned long action, void *arg)
> > +{
> > + int ret = 0;
> > +
> > + switch (action) {
> > + case MEM_GOING_OFFLINE:
> > + atomic_set(&hotplug_active, 1);
>
> So that would become a mutex_lock(). Added advantage is that
> it would wait for a current CMM operation to complete.
I've added the mutex but the scope will not prevent hotplug from
starting before the current CMM operation has completed. This allows us
to abort the allocation. The important globals for managing the list of
pages are still covered by the cmm_lock spinlock.
I've tested the patch and I'll send it out shortly.
^ permalink raw reply
* Re: [v8 PATCH 2/8]: cpuidle: implement a list based approach to register a set of idle routines.
From: Andi Kleen @ 2009-10-12 18:00 UTC (permalink / raw)
To: Peter Zijlstra
Cc: linux-arch, linux-kernel, linux-acpi, arun, Ingo Molnar,
linuxppc-dev, Arjan van de Ven
In-Reply-To: <1254999033.26976.272.camel@twins>
Peter Zijlstra <a.p.zijlstra@chello.nl> writes:
>
> So does it make sense to have a set of sets?
>
> Why not integrate them all into one set to be ruled by this governor
> thing?
cpuidle is currently optional, that is why the two level hierarchy
is there so that you can still have simple idle selection without it.
% size drivers/cpuidle/*.o
text data bss dec hex filename
5514 1416 44 6974 1b3e drivers/cpuidle/built-in.o
Adding it unconditionally would add ~7k to everyone who wants idle functions.
I think making it unconditional would require putting it on a serious
diet first.
-Andi
--
ak@linux.intel.com -- Speaking for myself only.
^ permalink raw reply
* [PATCH 8/8] spi_mpc8xxx: Add support for QE DMA mode and CPM1/CPM2 chips
From: Anton Vorontsov @ 2009-10-12 16:49 UTC (permalink / raw)
To: Kumar Gala
Cc: David Brownell, Greg Kroah-Hartman, linux-kernel, linuxppc-dev,
spi-devel-general, Andrew Morton
In-Reply-To: <20091012164841.GA32214@oksana.dev.rtsoft.ru>
This patch adds QE buffer descriptors mode support for the
spi_mpc8xxx driver, and as a side effect we now support CPM1
and CPM2 SPI controllers.
That means that today we support almost all MPC SPI controllers:
- MPC834x-style controllers (support PIO mode only);
- CPM1 and CPM2 controllers (support DMA mode only);
- QE SPI controllers in CPU mode (PIO mode with shift quirks);
- QE SPI controllers in buffer descriptors (DMA) mode;
The only controller we don't currently support is a newer eSPI
(with a dedicated chip selects and a bit different registers map).
Signed-off-by: Anton Vorontsov <avorontsov@ru.mvista.com>
Acked-by: David Brownell <dbrownell@users.sourceforge.net>
---
drivers/spi/Kconfig | 3 -
drivers/spi/spi_mpc8xxx.c | 540 +++++++++++++++++++++++++++++++++++++++++----
2 files changed, 500 insertions(+), 43 deletions(-)
diff --git a/drivers/spi/Kconfig b/drivers/spi/Kconfig
index 4b6f7cb..94058c6 100644
--- a/drivers/spi/Kconfig
+++ b/drivers/spi/Kconfig
@@ -147,9 +147,6 @@ config SPI_MPC8xxx
This enables using the Freescale MPC8xxx SPI controllers in master
mode.
- This driver uses a simple set of shift registers for data (opposed
- to the CPM based descriptor model).
-
config SPI_OMAP_UWIRE
tristate "OMAP1 MicroWire"
depends on ARCH_OMAP1
diff --git a/drivers/spi/spi_mpc8xxx.c b/drivers/spi/spi_mpc8xxx.c
index 80374df..394b658 100644
--- a/drivers/spi/spi_mpc8xxx.c
+++ b/drivers/spi/spi_mpc8xxx.c
@@ -5,6 +5,10 @@
*
* Copyright (C) 2006 Polycom, Inc.
*
+ * CPM SPI and QE buffer descriptors mode support:
+ * Copyright (c) 2009 MontaVista Software, Inc.
+ * Author: Anton Vorontsov <avorontsov@ru.mvista.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 of the License, or (at your
@@ -27,6 +31,9 @@
#include <linux/spi/spi_bitbang.h>
#include <linux/platform_device.h>
#include <linux/fsl_devices.h>
+#include <linux/dma-mapping.h>
+#include <linux/mm.h>
+#include <linux/mutex.h>
#include <linux/of.h>
#include <linux/of_platform.h>
#include <linux/gpio.h>
@@ -34,8 +41,19 @@
#include <linux/of_spi.h>
#include <sysdev/fsl_soc.h>
+#include <asm/cpm.h>
+#include <asm/qe.h>
#include <asm/irq.h>
+/* CPM1 and CPM2 are mutually exclusive. */
+#ifdef CONFIG_CPM1
+#include <asm/cpm1.h>
+#define CPM_SPI_CMD mk_cr_cmd(CPM_CR_CH_SPI, 0)
+#else
+#include <asm/cpm2.h>
+#define CPM_SPI_CMD mk_cr_cmd(CPM_CR_SPI_PAGE, CPM_CR_SPI_SBLOCK, 0, 0)
+#endif
+
/* SPI Controller registers */
struct mpc8xxx_spi_reg {
u8 res1[0x20];
@@ -47,6 +65,28 @@ struct mpc8xxx_spi_reg {
__be32 receive;
};
+/* SPI Parameter RAM */
+struct spi_pram {
+ __be16 rbase; /* Rx Buffer descriptor base address */
+ __be16 tbase; /* Tx Buffer descriptor base address */
+ u8 rfcr; /* Rx function code */
+ u8 tfcr; /* Tx function code */
+ __be16 mrblr; /* Max receive buffer length */
+ __be32 rstate; /* Internal */
+ __be32 rdp; /* Internal */
+ __be16 rbptr; /* Internal */
+ __be16 rbc; /* Internal */
+ __be32 rxtmp; /* Internal */
+ __be32 tstate; /* Internal */
+ __be32 tdp; /* Internal */
+ __be16 tbptr; /* Internal */
+ __be16 tbc; /* Internal */
+ __be32 txtmp; /* Internal */
+ __be32 res; /* Tx temp. */
+ __be16 rpbase; /* Relocation pointer (CPM1 only) */
+ __be16 res1; /* Reserved */
+};
+
/* SPI Controller mode register definitions */
#define SPMODE_LOOP (1 << 30)
#define SPMODE_CI_INACTIVEHIGH (1 << 29)
@@ -75,14 +115,40 @@ struct mpc8xxx_spi_reg {
#define SPIM_NE 0x00000200 /* Not empty */
#define SPIM_NF 0x00000100 /* Not full */
+#define SPIE_TXB 0x00000200 /* Last char is written to tx fifo */
+#define SPIE_RXB 0x00000100 /* Last char is written to rx buf */
+
+/* SPCOM register values */
+#define SPCOM_STR (1 << 23) /* Start transmit */
+
+#define SPI_PRAM_SIZE 0x100
+#define SPI_MRBLR ((unsigned int)PAGE_SIZE)
+
/* SPI Controller driver's private data. */
struct mpc8xxx_spi {
+ struct device *dev;
struct mpc8xxx_spi_reg __iomem *base;
/* rx & tx bufs from the spi_transfer */
const void *tx;
void *rx;
+ int subblock;
+ struct spi_pram __iomem *pram;
+ struct cpm_buf_desc __iomem *tx_bd;
+ struct cpm_buf_desc __iomem *rx_bd;
+
+ struct spi_transfer *xfer_in_progress;
+
+ /* dma addresses for CPM transfers */
+ dma_addr_t tx_dma;
+ dma_addr_t rx_dma;
+ bool map_tx_dma;
+ bool map_rx_dma;
+
+ dma_addr_t dma_dummy_tx;
+ dma_addr_t dma_dummy_rx;
+
/* functions to deal with different sized buffers */
void (*get_rx) (u32 rx_data, struct mpc8xxx_spi *);
u32(*get_tx) (struct mpc8xxx_spi *);
@@ -98,6 +164,10 @@ struct mpc8xxx_spi {
unsigned int flags;
#define SPI_QE_CPU_MODE (1 << 0) /* QE CPU ("PIO") mode */
+#define SPI_CPM_MODE (1 << 1) /* CPM/QE ("DMA") mode */
+#define SPI_CPM1 (1 << 2) /* SPI unit is in CPM1 block */
+#define SPI_CPM2 (1 << 3) /* SPI unit is in CPM2 block */
+#define SPI_QE (1 << 4) /* SPI unit is in QE block */
struct workqueue_struct *workqueue;
struct work_struct work;
@@ -108,6 +178,10 @@ struct mpc8xxx_spi {
struct completion done;
};
+static void *mpc8xxx_dummy_rx;
+static DEFINE_MUTEX(mpc8xxx_dummy_rx_lock);
+static int mpc8xxx_dummy_rx_refcnt;
+
struct spi_mpc8xxx_cs {
/* functions to deal with different sized buffers */
void (*get_rx) (u32 rx_data, struct mpc8xxx_spi *);
@@ -173,6 +247,22 @@ static void mpc8xxx_spi_change_mode(struct spi_device *spi)
mpc8xxx_spi_write_reg(mode, cs->hw_mode & ~SPMODE_ENABLE);
mpc8xxx_spi_write_reg(mode, cs->hw_mode);
+ /* When in CPM mode, we need to reinit tx and rx. */
+ if (mspi->flags & SPI_CPM_MODE) {
+ if (mspi->flags & SPI_QE) {
+ qe_issue_cmd(QE_INIT_TX_RX, mspi->subblock,
+ QE_CR_PROTOCOL_UNSPECIFIED, 0);
+ } else {
+ cpm_command(CPM_SPI_CMD, CPM_CR_INIT_TRX);
+ if (mspi->flags & SPI_CPM1) {
+ out_be16(&mspi->pram->rbptr,
+ in_be16(&mspi->pram->rbase));
+ out_be16(&mspi->pram->tbptr,
+ in_be16(&mspi->pram->tbase));
+ }
+ }
+ }
+
local_irq_restore(flags);
}
@@ -298,19 +388,133 @@ int mpc8xxx_spi_setup_transfer(struct spi_device *spi, struct spi_transfer *t)
return 0;
}
-static int mpc8xxx_spi_bufs(struct spi_device *spi, struct spi_transfer *t)
+static void mpc8xxx_spi_cpm_bufs_start(struct mpc8xxx_spi *mspi)
{
- struct mpc8xxx_spi *mpc8xxx_spi;
- u32 word, len, bits_per_word;
+ struct cpm_buf_desc __iomem *tx_bd = mspi->tx_bd;
+ struct cpm_buf_desc __iomem *rx_bd = mspi->rx_bd;
+ unsigned int xfer_len = min(mspi->count, SPI_MRBLR);
+ unsigned int xfer_ofs;
- mpc8xxx_spi = spi_master_get_devdata(spi->master);
+ xfer_ofs = mspi->xfer_in_progress->len - mspi->count;
+
+ out_be32(&rx_bd->cbd_bufaddr, mspi->rx_dma + xfer_ofs);
+ out_be16(&rx_bd->cbd_datlen, 0);
+ out_be16(&rx_bd->cbd_sc, BD_SC_EMPTY | BD_SC_INTRPT | BD_SC_WRAP);
+
+ out_be32(&tx_bd->cbd_bufaddr, mspi->tx_dma + xfer_ofs);
+ out_be16(&tx_bd->cbd_datlen, xfer_len);
+ out_be16(&tx_bd->cbd_sc, BD_SC_READY | BD_SC_INTRPT | BD_SC_WRAP |
+ BD_SC_LAST);
+
+ /* start transfer */
+ mpc8xxx_spi_write_reg(&mspi->base->command, SPCOM_STR);
+}
+
+static int mpc8xxx_spi_cpm_bufs(struct mpc8xxx_spi *mspi,
+ struct spi_transfer *t, bool is_dma_mapped)
+{
+ struct device *dev = mspi->dev;
+
+ if (is_dma_mapped) {
+ mspi->map_tx_dma = 0;
+ mspi->map_rx_dma = 0;
+ } else {
+ mspi->map_tx_dma = 1;
+ mspi->map_rx_dma = 1;
+ }
+
+ if (!t->tx_buf) {
+ mspi->tx_dma = mspi->dma_dummy_tx;
+ mspi->map_tx_dma = 0;
+ }
+
+ if (!t->rx_buf) {
+ mspi->rx_dma = mspi->dma_dummy_rx;
+ mspi->map_rx_dma = 0;
+ }
+
+ if (mspi->map_tx_dma) {
+ void *nonconst_tx = (void *)mspi->tx; /* shut up gcc */
+
+ mspi->tx_dma = dma_map_single(dev, nonconst_tx, t->len,
+ DMA_TO_DEVICE);
+ if (dma_mapping_error(dev, mspi->tx_dma)) {
+ dev_err(dev, "unable to map tx dma\n");
+ return -ENOMEM;
+ }
+ } else {
+ mspi->tx_dma = t->tx_dma;
+ }
+
+ if (mspi->map_rx_dma) {
+ mspi->rx_dma = dma_map_single(dev, mspi->rx, t->len,
+ DMA_FROM_DEVICE);
+ if (dma_mapping_error(dev, mspi->rx_dma)) {
+ dev_err(dev, "unable to map rx dma\n");
+ goto err_rx_dma;
+ }
+ } else {
+ mspi->rx_dma = t->rx_dma;
+ }
+
+ /* enable rx ints */
+ mpc8xxx_spi_write_reg(&mspi->base->mask, SPIE_RXB);
+
+ mspi->xfer_in_progress = t;
+ mspi->count = t->len;
+
+ /* start CPM transfers */
+ mpc8xxx_spi_cpm_bufs_start(mspi);
+
+ return 0;
+
+err_rx_dma:
+ if (mspi->map_tx_dma)
+ dma_unmap_single(dev, mspi->tx_dma, t->len, DMA_TO_DEVICE);
+ return -ENOMEM;
+}
+
+static void mpc8xxx_spi_cpm_bufs_complete(struct mpc8xxx_spi *mspi)
+{
+ struct device *dev = mspi->dev;
+ struct spi_transfer *t = mspi->xfer_in_progress;
+
+ if (mspi->map_tx_dma)
+ dma_unmap_single(dev, mspi->tx_dma, t->len, DMA_TO_DEVICE);
+ if (mspi->map_tx_dma)
+ dma_unmap_single(dev, mspi->rx_dma, t->len, DMA_FROM_DEVICE);
+ mspi->xfer_in_progress = NULL;
+}
+
+static int mpc8xxx_spi_cpu_bufs(struct mpc8xxx_spi *mspi,
+ struct spi_transfer *t, unsigned int len)
+{
+ u32 word;
+
+ mspi->count = len;
+
+ /* enable rx ints */
+ mpc8xxx_spi_write_reg(&mspi->base->mask, SPIM_NE);
+
+ /* transmit word */
+ word = mspi->get_tx(mspi);
+ mpc8xxx_spi_write_reg(&mspi->base->transmit, word);
+
+ return 0;
+}
+
+static int mpc8xxx_spi_bufs(struct spi_device *spi, struct spi_transfer *t,
+ bool is_dma_mapped)
+{
+ struct mpc8xxx_spi *mpc8xxx_spi = spi_master_get_devdata(spi->master);
+ unsigned int len = t->len;
+ u8 bits_per_word;
+ int ret;
- mpc8xxx_spi->tx = t->tx_buf;
- mpc8xxx_spi->rx = t->rx_buf;
bits_per_word = spi->bits_per_word;
if (t->bits_per_word)
bits_per_word = t->bits_per_word;
- len = t->len;
+
if (bits_per_word > 8) {
/* invalid length? */
if (len & 1)
@@ -323,22 +527,27 @@ static int mpc8xxx_spi_bufs(struct spi_device *spi, struct spi_transfer *t)
return -EINVAL;
len /= 2;
}
- mpc8xxx_spi->count = len;
- INIT_COMPLETION(mpc8xxx_spi->done);
+ mpc8xxx_spi->tx = t->tx_buf;
+ mpc8xxx_spi->rx = t->rx_buf;
- /* enable rx ints */
- mpc8xxx_spi_write_reg(&mpc8xxx_spi->base->mask, SPIM_NE);
+ INIT_COMPLETION(mpc8xxx_spi->done);
- /* transmit word */
- word = mpc8xxx_spi->get_tx(mpc8xxx_spi);
- mpc8xxx_spi_write_reg(&mpc8xxx_spi->base->transmit, word);
+ if (mpc8xxx_spi->flags & SPI_CPM_MODE)
+ ret = mpc8xxx_spi_cpm_bufs(mpc8xxx_spi, t, is_dma_mapped);
+ else
+ ret = mpc8xxx_spi_cpu_bufs(mpc8xxx_spi, t, len);
+ if (ret)
+ return ret;
wait_for_completion(&mpc8xxx_spi->done);
/* disable rx ints */
mpc8xxx_spi_write_reg(&mpc8xxx_spi->base->mask, 0);
+ if (mpc8xxx_spi->flags & SPI_CPM_MODE)
+ mpc8xxx_spi_cpm_bufs_complete(mpc8xxx_spi);
+
return mpc8xxx_spi->count;
}
@@ -369,7 +578,7 @@ static void mpc8xxx_spi_do_one_msg(struct spi_message *m)
}
cs_change = t->cs_change;
if (t->len)
- status = mpc8xxx_spi_bufs(spi, t);
+ status = mpc8xxx_spi_bufs(spi, t, m->is_dma_mapped);
if (status) {
status = -EMSGSIZE;
break;
@@ -458,45 +667,80 @@ static int mpc8xxx_spi_setup(struct spi_device *spi)
return 0;
}
-static irqreturn_t mpc8xxx_spi_irq(s32 irq, void *context_data)
+static void mpc8xxx_spi_cpm_irq(struct mpc8xxx_spi *mspi, u32 events)
{
- struct mpc8xxx_spi *mpc8xxx_spi = context_data;
- u32 event;
- irqreturn_t ret = IRQ_NONE;
+ u16 len;
- /* Get interrupt events(tx/rx) */
- event = mpc8xxx_spi_read_reg(&mpc8xxx_spi->base->event);
+ dev_dbg(mspi->dev, "%s: bd datlen %d, count %d\n", __func__,
+ in_be16(&mspi->rx_bd->cbd_datlen), mspi->count);
- /* We need handle RX first */
- if (event & SPIE_NE) {
- u32 rx_data = mpc8xxx_spi_read_reg(&mpc8xxx_spi->base->receive);
+ len = in_be16(&mspi->rx_bd->cbd_datlen);
+ if (len > mspi->count) {
+ WARN_ON(1);
+ len = mspi->count;
+ }
- if (mpc8xxx_spi->rx)
- mpc8xxx_spi->get_rx(rx_data, mpc8xxx_spi);
+ /* Clear the events */
+ mpc8xxx_spi_write_reg(&mspi->base->event, events);
- ret = IRQ_HANDLED;
+ mspi->count -= len;
+ if (mspi->count)
+ mpc8xxx_spi_cpm_bufs_start(mspi);
+ else
+ complete(&mspi->done);
+}
+
+static void mpc8xxx_spi_cpu_irq(struct mpc8xxx_spi *mspi, u32 events)
+{
+ /* We need handle RX first */
+ if (events & SPIE_NE) {
+ u32 rx_data = mpc8xxx_spi_read_reg(&mspi->base->receive);
+
+ if (mspi->rx)
+ mspi->get_rx(rx_data, mspi);
}
- if ((event & SPIE_NF) == 0)
+ if ((events & SPIE_NF) == 0)
/* spin until TX is done */
- while (((event =
- mpc8xxx_spi_read_reg(&mpc8xxx_spi->base->event)) &
+ while (((events =
+ mpc8xxx_spi_read_reg(&mspi->base->event)) &
SPIE_NF) == 0)
cpu_relax();
- mpc8xxx_spi->count -= 1;
- if (mpc8xxx_spi->count) {
- u32 word = mpc8xxx_spi->get_tx(mpc8xxx_spi);
- mpc8xxx_spi_write_reg(&mpc8xxx_spi->base->transmit, word);
+ /* Clear the events */
+ mpc8xxx_spi_write_reg(&mspi->base->event, events);
+
+ mspi->count -= 1;
+ if (mspi->count) {
+ u32 word = mspi->get_tx(mspi);
+
+ mpc8xxx_spi_write_reg(&mspi->base->transmit, word);
} else {
- complete(&mpc8xxx_spi->done);
+ complete(&mspi->done);
}
+}
- /* Clear the events */
- mpc8xxx_spi_write_reg(&mpc8xxx_spi->base->event, event);
+static irqreturn_t mpc8xxx_spi_irq(s32 irq, void *context_data)
+{
+ struct mpc8xxx_spi *mspi = context_data;
+ irqreturn_t ret = IRQ_NONE;
+ u32 events;
+
+ /* Get interrupt events(tx/rx) */
+ events = mpc8xxx_spi_read_reg(&mspi->base->event);
+ if (events)
+ ret = IRQ_HANDLED;
+
+ dev_dbg(mspi->dev, "%s: events %x\n", __func__, events);
+
+ if (mspi->flags & SPI_CPM_MODE)
+ mpc8xxx_spi_cpm_irq(mspi, events);
+ else
+ mpc8xxx_spi_cpu_irq(mspi, events);
return ret;
}
+
static int mpc8xxx_spi_transfer(struct spi_device *spi,
struct spi_message *m)
{
@@ -520,10 +764,212 @@ static void mpc8xxx_spi_cleanup(struct spi_device *spi)
kfree(spi->controller_state);
}
+static void *mpc8xxx_spi_alloc_dummy_rx(void)
+{
+ mutex_lock(&mpc8xxx_dummy_rx_lock);
+
+ if (!mpc8xxx_dummy_rx)
+ mpc8xxx_dummy_rx = kmalloc(SPI_MRBLR, GFP_KERNEL);
+ if (mpc8xxx_dummy_rx)
+ mpc8xxx_dummy_rx_refcnt++;
+
+ mutex_unlock(&mpc8xxx_dummy_rx_lock);
+
+ return mpc8xxx_dummy_rx;
+}
+
+static void mpc8xxx_spi_free_dummy_rx(void)
+{
+ mutex_lock(&mpc8xxx_dummy_rx_lock);
+
+ switch (mpc8xxx_dummy_rx_refcnt) {
+ case 0:
+ WARN_ON(1);
+ break;
+ case 1:
+ kfree(mpc8xxx_dummy_rx);
+ mpc8xxx_dummy_rx = NULL;
+ /* fall through */
+ default:
+ mpc8xxx_dummy_rx_refcnt--;
+ break;
+ }
+
+ mutex_unlock(&mpc8xxx_dummy_rx_lock);
+}
+
+static unsigned long mpc8xxx_spi_cpm_get_pram(struct mpc8xxx_spi *mspi)
+{
+ struct device *dev = mspi->dev;
+ struct device_node *np = dev_archdata_get_node(&dev->archdata);
+ const u32 *iprop;
+ int size;
+ unsigned long spi_base_ofs;
+ unsigned long pram_ofs = -ENOMEM;
+
+ /* Can't use of_address_to_resource(), QE muram isn't at 0. */
+ iprop = of_get_property(np, "reg", &size);
+
+ /* QE with a fixed pram location? */
+ if (mspi->flags & SPI_QE && iprop && size == sizeof(*iprop) * 4)
+ return cpm_muram_alloc_fixed(iprop[2], SPI_PRAM_SIZE);
+
+ /* QE but with a dynamic pram location? */
+ if (mspi->flags & SPI_QE) {
+ pram_ofs = cpm_muram_alloc(SPI_PRAM_SIZE, 64);
+ qe_issue_cmd(QE_ASSIGN_PAGE_TO_DEVICE, mspi->subblock,
+ QE_CR_PROTOCOL_UNSPECIFIED, pram_ofs);
+ return pram_ofs;
+ }
+
+ /* CPM1 and CPM2 pram must be at a fixed addr. */
+ if (!iprop || size != sizeof(*iprop) * 4)
+ return -ENOMEM;
+
+ spi_base_ofs = cpm_muram_alloc_fixed(iprop[2], 2);
+ if (IS_ERR_VALUE(spi_base_ofs))
+ return -ENOMEM;
+
+ if (mspi->flags & SPI_CPM2) {
+ pram_ofs = cpm_muram_alloc(SPI_PRAM_SIZE, 64);
+ if (!IS_ERR_VALUE(pram_ofs)) {
+ u16 __iomem *spi_base = cpm_muram_addr(spi_base_ofs);
+
+ out_be16(spi_base, pram_ofs);
+ }
+ } else {
+ struct spi_pram __iomem *pram = cpm_muram_addr(spi_base_ofs);
+ u16 rpbase = in_be16(&pram->rpbase);
+
+ /* Microcode relocation patch applied? */
+ if (rpbase)
+ pram_ofs = rpbase;
+ else
+ return spi_base_ofs;
+ }
+
+ cpm_muram_free(spi_base_ofs);
+ return pram_ofs;
+}
+
+static int mpc8xxx_spi_cpm_init(struct mpc8xxx_spi *mspi)
+{
+ struct device *dev = mspi->dev;
+ struct device_node *np = dev_archdata_get_node(&dev->archdata);
+ const u32 *iprop;
+ int size;
+ unsigned long pram_ofs;
+ unsigned long bds_ofs;
+
+ if (!(mspi->flags & SPI_CPM_MODE))
+ return 0;
+
+ if (!mpc8xxx_spi_alloc_dummy_rx())
+ return -ENOMEM;
+
+ if (mspi->flags & SPI_QE) {
+ iprop = of_get_property(np, "cell-index", &size);
+ if (iprop && size == sizeof(*iprop))
+ mspi->subblock = *iprop;
+
+ switch (mspi->subblock) {
+ default:
+ dev_warn(dev, "cell-index unspecified, assuming SPI1");
+ /* fall through */
+ case 0:
+ mspi->subblock = QE_CR_SUBBLOCK_SPI1;
+ break;
+ case 1:
+ mspi->subblock = QE_CR_SUBBLOCK_SPI2;
+ break;
+ }
+ }
+
+ pram_ofs = mpc8xxx_spi_cpm_get_pram(mspi);
+ if (IS_ERR_VALUE(pram_ofs)) {
+ dev_err(dev, "can't allocate spi parameter ram\n");
+ goto err_pram;
+ }
+
+ bds_ofs = cpm_muram_alloc(sizeof(*mspi->tx_bd) +
+ sizeof(*mspi->rx_bd), 8);
+ if (IS_ERR_VALUE(bds_ofs)) {
+ dev_err(dev, "can't allocate bds\n");
+ goto err_bds;
+ }
+
+ mspi->dma_dummy_tx = dma_map_single(dev, empty_zero_page, PAGE_SIZE,
+ DMA_TO_DEVICE);
+ if (dma_mapping_error(dev, mspi->dma_dummy_tx)) {
+ dev_err(dev, "unable to map dummy tx buffer\n");
+ goto err_dummy_tx;
+ }
+
+ mspi->dma_dummy_rx = dma_map_single(dev, mpc8xxx_dummy_rx, SPI_MRBLR,
+ DMA_FROM_DEVICE);
+ if (dma_mapping_error(dev, mspi->dma_dummy_rx)) {
+ dev_err(dev, "unable to map dummy rx buffer\n");
+ goto err_dummy_rx;
+ }
+
+ mspi->pram = cpm_muram_addr(pram_ofs);
+
+ mspi->tx_bd = cpm_muram_addr(bds_ofs);
+ mspi->rx_bd = cpm_muram_addr(bds_ofs + sizeof(*mspi->tx_bd));
+
+ /* Initialize parameter ram. */
+ out_be16(&mspi->pram->tbase, cpm_muram_offset(mspi->tx_bd));
+ out_be16(&mspi->pram->rbase, cpm_muram_offset(mspi->rx_bd));
+ out_8(&mspi->pram->tfcr, CPMFCR_EB | CPMFCR_GBL);
+ out_8(&mspi->pram->rfcr, CPMFCR_EB | CPMFCR_GBL);
+ out_be16(&mspi->pram->mrblr, SPI_MRBLR);
+ out_be32(&mspi->pram->rstate, 0);
+ out_be32(&mspi->pram->rdp, 0);
+ out_be16(&mspi->pram->rbptr, 0);
+ out_be16(&mspi->pram->rbc, 0);
+ out_be32(&mspi->pram->rxtmp, 0);
+ out_be32(&mspi->pram->tstate, 0);
+ out_be32(&mspi->pram->tdp, 0);
+ out_be16(&mspi->pram->tbptr, 0);
+ out_be16(&mspi->pram->tbc, 0);
+ out_be32(&mspi->pram->txtmp, 0);
+
+ return 0;
+
+err_dummy_rx:
+ dma_unmap_single(dev, mspi->dma_dummy_tx, PAGE_SIZE, DMA_TO_DEVICE);
+err_dummy_tx:
+ cpm_muram_free(bds_ofs);
+err_bds:
+ cpm_muram_free(pram_ofs);
+err_pram:
+ mpc8xxx_spi_free_dummy_rx();
+ return -ENOMEM;
+}
+
+static void mpc8xxx_spi_cpm_free(struct mpc8xxx_spi *mspi)
+{
+ struct device *dev = mspi->dev;
+
+ dma_unmap_single(dev, mspi->dma_dummy_rx, SPI_MRBLR, DMA_FROM_DEVICE);
+ dma_unmap_single(dev, mspi->dma_dummy_tx, PAGE_SIZE, DMA_TO_DEVICE);
+ cpm_muram_free(cpm_muram_offset(mspi->tx_bd));
+ cpm_muram_free(cpm_muram_offset(mspi->pram));
+ mpc8xxx_spi_free_dummy_rx();
+}
+
static const char *mpc8xxx_spi_strmode(unsigned int flags)
{
- if (flags & SPI_QE_CPU_MODE)
+ if (flags & SPI_QE_CPU_MODE) {
return "QE CPU";
+ } else if (flags & SPI_CPM_MODE) {
+ if (flags & SPI_QE)
+ return "QE";
+ else if (flags & SPI_CPM2)
+ return "CPM2";
+ else
+ return "CPM1";
+ }
return "CPU";
}
@@ -553,11 +999,16 @@ mpc8xxx_spi_probe(struct device *dev, struct resource *mem, unsigned int irq)
master->cleanup = mpc8xxx_spi_cleanup;
mpc8xxx_spi = spi_master_get_devdata(master);
+ mpc8xxx_spi->dev = dev;
mpc8xxx_spi->get_rx = mpc8xxx_spi_rx_buf_u8;
mpc8xxx_spi->get_tx = mpc8xxx_spi_tx_buf_u8;
mpc8xxx_spi->flags = pdata->flags;
mpc8xxx_spi->spibrg = pdata->sysclk;
+ ret = mpc8xxx_spi_cpm_init(mpc8xxx_spi);
+ if (ret)
+ goto err_cpm_init;
+
mpc8xxx_spi->rx_shift = 0;
mpc8xxx_spi->tx_shift = 0;
if (mpc8xxx_spi->flags & SPI_QE_CPU_MODE) {
@@ -570,7 +1021,7 @@ mpc8xxx_spi_probe(struct device *dev, struct resource *mem, unsigned int irq)
mpc8xxx_spi->base = ioremap(mem->start, mem->end - mem->start + 1);
if (mpc8xxx_spi->base == NULL) {
ret = -ENOMEM;
- goto put_master;
+ goto err_ioremap;
}
mpc8xxx_spi->irq = irq;
@@ -624,7 +1075,9 @@ free_irq:
free_irq(mpc8xxx_spi->irq, mpc8xxx_spi);
unmap_io:
iounmap(mpc8xxx_spi->base);
-put_master:
+err_ioremap:
+ mpc8xxx_spi_cpm_free(mpc8xxx_spi);
+err_cpm_init:
spi_master_put(master);
err:
return ERR_PTR(ret);
@@ -644,6 +1097,7 @@ static int __devexit mpc8xxx_spi_remove(struct device *dev)
free_irq(mpc8xxx_spi->irq, mpc8xxx_spi);
iounmap(mpc8xxx_spi->base);
+ mpc8xxx_spi_cpm_free(mpc8xxx_spi);
return 0;
}
@@ -806,6 +1260,12 @@ static int __devinit of_mpc8xxx_spi_probe(struct of_device *ofdev,
prop = of_get_property(np, "mode", NULL);
if (prop && !strcmp(prop, "cpu-qe"))
pdata->flags = SPI_QE_CPU_MODE;
+ else if (prop && !strcmp(prop, "qe"))
+ pdata->flags = SPI_CPM_MODE | SPI_QE;
+ else if (of_device_is_compatible(np, "fsl,cpm2-spi"))
+ pdata->flags = SPI_CPM_MODE | SPI_CPM2;
+ else if (of_device_is_compatible(np, "fsl,cpm1-spi"))
+ pdata->flags = SPI_CPM_MODE | SPI_CPM1;
ret = of_mpc8xxx_spi_get_chipselects(dev);
if (ret)
--
1.6.3.3
^ permalink raw reply related
* [PATCH 7/8] spi_mpc8xxx: Turn qe_mode into flags
From: Anton Vorontsov @ 2009-10-12 16:49 UTC (permalink / raw)
To: Kumar Gala
Cc: David Brownell, Greg Kroah-Hartman, linux-kernel, linuxppc-dev,
spi-devel-general, Andrew Morton
In-Reply-To: <20091012164841.GA32214@oksana.dev.rtsoft.ru>
Soon there will be more flags introduced in subsequent patches, so
let's turn qe_mode into flags.
Also introduce mpc8xxx_spi_strmode() and print current SPI mode.
Signed-off-by: Anton Vorontsov <avorontsov@ru.mvista.com>
Acked-by: David Brownell <dbrownell@users.sourceforge.net>
---
drivers/spi/spi_mpc8xxx.c | 30 +++++++++++++++++++-----------
include/linux/fsl_devices.h | 2 +-
2 files changed, 20 insertions(+), 12 deletions(-)
diff --git a/drivers/spi/spi_mpc8xxx.c b/drivers/spi/spi_mpc8xxx.c
index 4b119ea..80374df 100644
--- a/drivers/spi/spi_mpc8xxx.c
+++ b/drivers/spi/spi_mpc8xxx.c
@@ -96,7 +96,8 @@ struct mpc8xxx_spi {
u32 rx_shift; /* RX data reg shift when in qe mode */
u32 tx_shift; /* TX data reg shift when in qe mode */
- bool qe_mode;
+ unsigned int flags;
+#define SPI_QE_CPU_MODE (1 << 0) /* QE CPU ("PIO") mode */
struct workqueue_struct *workqueue;
struct work_struct work;
@@ -235,14 +236,14 @@ int mpc8xxx_spi_setup_transfer(struct spi_device *spi, struct spi_transfer *t)
if (bits_per_word <= 8) {
cs->get_rx = mpc8xxx_spi_rx_buf_u8;
cs->get_tx = mpc8xxx_spi_tx_buf_u8;
- if (mpc8xxx_spi->qe_mode) {
+ if (mpc8xxx_spi->flags & SPI_QE_CPU_MODE) {
cs->rx_shift = 16;
cs->tx_shift = 24;
}
} else if (bits_per_word <= 16) {
cs->get_rx = mpc8xxx_spi_rx_buf_u16;
cs->get_tx = mpc8xxx_spi_tx_buf_u16;
- if (mpc8xxx_spi->qe_mode) {
+ if (mpc8xxx_spi->flags & SPI_QE_CPU_MODE) {
cs->rx_shift = 16;
cs->tx_shift = 16;
}
@@ -252,7 +253,8 @@ int mpc8xxx_spi_setup_transfer(struct spi_device *spi, struct spi_transfer *t)
} else
return -EINVAL;
- if (mpc8xxx_spi->qe_mode && spi->mode & SPI_LSB_FIRST) {
+ if (mpc8xxx_spi->flags & SPI_QE_CPU_MODE &&
+ spi->mode & SPI_LSB_FIRST) {
cs->tx_shift = 0;
if (bits_per_word <= 8)
cs->rx_shift = 8;
@@ -518,6 +520,13 @@ static void mpc8xxx_spi_cleanup(struct spi_device *spi)
kfree(spi->controller_state);
}
+static const char *mpc8xxx_spi_strmode(unsigned int flags)
+{
+ if (flags & SPI_QE_CPU_MODE)
+ return "QE CPU";
+ return "CPU";
+}
+
static struct spi_master * __devinit
mpc8xxx_spi_probe(struct device *dev, struct resource *mem, unsigned int irq)
{
@@ -544,14 +553,14 @@ mpc8xxx_spi_probe(struct device *dev, struct resource *mem, unsigned int irq)
master->cleanup = mpc8xxx_spi_cleanup;
mpc8xxx_spi = spi_master_get_devdata(master);
- mpc8xxx_spi->qe_mode = pdata->qe_mode;
mpc8xxx_spi->get_rx = mpc8xxx_spi_rx_buf_u8;
mpc8xxx_spi->get_tx = mpc8xxx_spi_tx_buf_u8;
+ mpc8xxx_spi->flags = pdata->flags;
mpc8xxx_spi->spibrg = pdata->sysclk;
mpc8xxx_spi->rx_shift = 0;
mpc8xxx_spi->tx_shift = 0;
- if (mpc8xxx_spi->qe_mode) {
+ if (mpc8xxx_spi->flags & SPI_QE_CPU_MODE) {
mpc8xxx_spi->rx_shift = 16;
mpc8xxx_spi->tx_shift = 24;
}
@@ -584,7 +593,7 @@ mpc8xxx_spi_probe(struct device *dev, struct resource *mem, unsigned int irq)
/* Enable SPI interface */
regval = pdata->initial_spmode | SPMODE_INIT_VAL | SPMODE_ENABLE;
- if (pdata->qe_mode)
+ if (mpc8xxx_spi->flags & SPI_QE_CPU_MODE)
regval |= SPMODE_OP;
mpc8xxx_spi_write_reg(&mpc8xxx_spi->base->mode, regval);
@@ -604,9 +613,8 @@ mpc8xxx_spi_probe(struct device *dev, struct resource *mem, unsigned int irq)
if (ret < 0)
goto unreg_master;
- printk(KERN_INFO
- "%s: MPC8xxx SPI Controller driver at 0x%p (irq = %d)\n",
- dev_name(dev), mpc8xxx_spi->base, mpc8xxx_spi->irq);
+ dev_info(dev, "at 0x%p (irq = %d), %s mode\n", mpc8xxx_spi->base,
+ mpc8xxx_spi->irq, mpc8xxx_spi_strmode(mpc8xxx_spi->flags));
return master;
@@ -797,7 +805,7 @@ static int __devinit of_mpc8xxx_spi_probe(struct of_device *ofdev,
prop = of_get_property(np, "mode", NULL);
if (prop && !strcmp(prop, "cpu-qe"))
- pdata->qe_mode = 1;
+ pdata->flags = SPI_QE_CPU_MODE;
ret = of_mpc8xxx_spi_get_chipselects(dev);
if (ret)
diff --git a/include/linux/fsl_devices.h b/include/linux/fsl_devices.h
index 43fc95d..39fd946 100644
--- a/include/linux/fsl_devices.h
+++ b/include/linux/fsl_devices.h
@@ -74,7 +74,7 @@ struct spi_device;
struct fsl_spi_platform_data {
u32 initial_spmode; /* initial SPMODE value */
s16 bus_num;
- bool qe_mode;
+ unsigned int flags;
/* board specific information */
u16 max_chipselect;
void (*cs_control)(struct spi_device *spi, bool on);
--
1.6.3.3
^ permalink raw reply related
* [PATCH 6/8] spi_mpc8xxx: Factor out SPI mode change steps into a call
From: Anton Vorontsov @ 2009-10-12 16:49 UTC (permalink / raw)
To: Kumar Gala
Cc: David Brownell, Greg Kroah-Hartman, linux-kernel, linuxppc-dev,
spi-devel-general, Andrew Morton
In-Reply-To: <20091012164841.GA32214@oksana.dev.rtsoft.ru>
We'll add more steps soon, so get rid of the duplication.
Signed-off-by: Anton Vorontsov <avorontsov@ru.mvista.com>
Acked-by: David Brownell <dbrownell@users.sourceforge.net>
---
drivers/spi/spi_mpc8xxx.c | 56 +++++++++++++++++++-------------------------
1 files changed, 24 insertions(+), 32 deletions(-)
diff --git a/drivers/spi/spi_mpc8xxx.c b/drivers/spi/spi_mpc8xxx.c
index 518671b..4b119ea 100644
--- a/drivers/spi/spi_mpc8xxx.c
+++ b/drivers/spi/spi_mpc8xxx.c
@@ -155,6 +155,26 @@ MPC83XX_SPI_TX_BUF(u8)
MPC83XX_SPI_TX_BUF(u16)
MPC83XX_SPI_TX_BUF(u32)
+static void mpc8xxx_spi_change_mode(struct spi_device *spi)
+{
+ struct mpc8xxx_spi *mspi = spi_master_get_devdata(spi->master);
+ struct spi_mpc8xxx_cs *cs = spi->controller_state;
+ __be32 __iomem *mode = &mspi->base->mode;
+ unsigned long flags;
+
+ if (cs->hw_mode == mpc8xxx_spi_read_reg(mode))
+ return;
+
+ /* Turn off IRQs locally to minimize time that SPI is disabled. */
+ local_irq_save(flags);
+
+ /* Turn off SPI unit prior changing mode */
+ mpc8xxx_spi_write_reg(mode, cs->hw_mode & ~SPMODE_ENABLE);
+ mpc8xxx_spi_write_reg(mode, cs->hw_mode);
+
+ local_irq_restore(flags);
+}
+
static void mpc8xxx_spi_chipselect(struct spi_device *spi, int value)
{
struct mpc8xxx_spi *mpc8xxx_spi = spi_master_get_devdata(spi->master);
@@ -168,27 +188,13 @@ static void mpc8xxx_spi_chipselect(struct spi_device *spi, int value)
}
if (value == BITBANG_CS_ACTIVE) {
- u32 regval = mpc8xxx_spi_read_reg(&mpc8xxx_spi->base->mode);
-
mpc8xxx_spi->rx_shift = cs->rx_shift;
mpc8xxx_spi->tx_shift = cs->tx_shift;
mpc8xxx_spi->get_rx = cs->get_rx;
mpc8xxx_spi->get_tx = cs->get_tx;
- if (cs->hw_mode != regval) {
- unsigned long flags;
- __be32 __iomem *mode = &mpc8xxx_spi->base->mode;
-
- regval = cs->hw_mode;
- /* Turn off IRQs locally to minimize time that
- * SPI is disabled
- */
- local_irq_save(flags);
- /* Turn off SPI unit prior changing mode */
- mpc8xxx_spi_write_reg(mode, regval & ~SPMODE_ENABLE);
- mpc8xxx_spi_write_reg(mode, regval);
- local_irq_restore(flags);
- }
+ mpc8xxx_spi_change_mode(spi);
+
if (pdata->cs_control)
pdata->cs_control(spi, pol);
}
@@ -198,7 +204,6 @@ static
int mpc8xxx_spi_setup_transfer(struct spi_device *spi, struct spi_transfer *t)
{
struct mpc8xxx_spi *mpc8xxx_spi;
- u32 regval;
u8 bits_per_word, pm;
u32 hz;
struct spi_mpc8xxx_cs *cs = spi->controller_state;
@@ -286,21 +291,8 @@ int mpc8xxx_spi_setup_transfer(struct spi_device *spi, struct spi_transfer *t)
pm--;
cs->hw_mode |= SPMODE_PM(pm);
- regval = mpc8xxx_spi_read_reg(&mpc8xxx_spi->base->mode);
- if (cs->hw_mode != regval) {
- unsigned long flags;
- __be32 __iomem *mode = &mpc8xxx_spi->base->mode;
-
- regval = cs->hw_mode;
- /* Turn off IRQs locally to minimize time
- * that SPI is disabled
- */
- local_irq_save(flags);
- /* Turn off SPI unit prior changing mode */
- mpc8xxx_spi_write_reg(mode, regval & ~SPMODE_ENABLE);
- mpc8xxx_spi_write_reg(mode, regval);
- local_irq_restore(flags);
- }
+
+ mpc8xxx_spi_change_mode(spi);
return 0;
}
--
1.6.3.3
^ permalink raw reply related
* [PATCH 5/8] spi_mpc8xxx: Fix uninitialized variable
From: Anton Vorontsov @ 2009-10-12 16:49 UTC (permalink / raw)
To: Kumar Gala
Cc: David Brownell, Greg Kroah-Hartman, linux-kernel, linuxppc-dev,
spi-devel-general, Andrew Morton
In-Reply-To: <20091012164841.GA32214@oksana.dev.rtsoft.ru>
This patch fixes the following warning:
CC drivers/spi/spi_mpc8xxx.o
spi_mpc8xxx.c: In function 'of_mpc8xxx_spi_probe':
spi_mpc8xxx.c:681: warning: 'ret' may be used uninitialized in this function
Signed-off-by: Anton Vorontsov <avorontsov@ru.mvista.com>
Acked-by: David Brownell <dbrownell@users.sourceforge.net>
---
drivers/spi/spi_mpc8xxx.c | 1 +
1 files changed, 1 insertions(+), 0 deletions(-)
diff --git a/drivers/spi/spi_mpc8xxx.c b/drivers/spi/spi_mpc8xxx.c
index 0fd0ec4..518671b 100644
--- a/drivers/spi/spi_mpc8xxx.c
+++ b/drivers/spi/spi_mpc8xxx.c
@@ -709,6 +709,7 @@ static int of_mpc8xxx_spi_get_chipselects(struct device *dev)
gpio = of_get_gpio_flags(np, i, &flags);
if (!gpio_is_valid(gpio)) {
dev_err(dev, "invalid gpio #%d: %d\n", i, gpio);
+ ret = gpio;
goto err_loop;
}
--
1.6.3.3
^ permalink raw reply related
* [PATCH 4/8] powerpc/qe&cpm: Implement static inline stubs for non-QE/CPM builds
From: Anton Vorontsov @ 2009-10-12 16:49 UTC (permalink / raw)
To: Kumar Gala
Cc: David Brownell, Greg Kroah-Hartman, linux-kernel, linuxppc-dev,
spi-devel-general, Andrew Morton
In-Reply-To: <20091012164841.GA32214@oksana.dev.rtsoft.ru>
This is needed to avoid ugly #ifdefs in drivers. Also update fsl_qe_udc
driver so that now it doesn't define its own versions that cause build
breakage when the generic stubs are used.
Signed-off-by: Anton Vorontsov <avorontsov@ru.mvista.com>
Acked-by: Greg Kroah-Hartman <gregkh@suse.de>
---
arch/powerpc/include/asm/cpm.h | 44 +++++++++++++++++++++++++++++++++++++++
arch/powerpc/include/asm/qe.h | 11 ++++++++-
drivers/usb/gadget/fsl_qe_udc.h | 15 -------------
3 files changed, 54 insertions(+), 16 deletions(-)
diff --git a/arch/powerpc/include/asm/cpm.h b/arch/powerpc/include/asm/cpm.h
index ea3fdb9..0835eb9 100644
--- a/arch/powerpc/include/asm/cpm.h
+++ b/arch/powerpc/include/asm/cpm.h
@@ -3,6 +3,7 @@
#include <linux/compiler.h>
#include <linux/types.h>
+#include <linux/errno.h>
#include <linux/of.h>
/*
@@ -131,13 +132,56 @@ typedef struct cpm_buf_desc {
#define BD_I2C_START (0x0400)
int cpm_muram_init(void);
+
+#if defined(CONFIG_CPM) || defined(CONFIG_QUICC_ENGINE)
unsigned long cpm_muram_alloc(unsigned long size, unsigned long align);
int cpm_muram_free(unsigned long offset);
unsigned long cpm_muram_alloc_fixed(unsigned long offset, unsigned long size);
void __iomem *cpm_muram_addr(unsigned long offset);
unsigned long cpm_muram_offset(void __iomem *addr);
dma_addr_t cpm_muram_dma(void __iomem *addr);
+#else
+static inline unsigned long cpm_muram_alloc(unsigned long size,
+ unsigned long align)
+{
+ return -ENOSYS;
+}
+
+static inline int cpm_muram_free(unsigned long offset)
+{
+ return -ENOSYS;
+}
+
+static inline unsigned long cpm_muram_alloc_fixed(unsigned long offset,
+ unsigned long size)
+{
+ return -ENOSYS;
+}
+
+static inline void __iomem *cpm_muram_addr(unsigned long offset)
+{
+ return NULL;
+}
+
+static inline unsigned long cpm_muram_offset(void __iomem *addr)
+{
+ return -ENOSYS;
+}
+
+static inline dma_addr_t cpm_muram_dma(void __iomem *addr)
+{
+ return 0;
+}
+#endif /* defined(CONFIG_CPM) || defined(CONFIG_QUICC_ENGINE) */
+
+#ifdef CONFIG_CPM
int cpm_command(u32 command, u8 opcode);
+#else
+static inline int cpm_command(u32 command, u8 opcode)
+{
+ return -ENOSYS;
+}
+#endif /* CONFIG_CPM */
int cpm2_gpiochip_add32(struct device_node *np);
diff --git a/arch/powerpc/include/asm/qe.h b/arch/powerpc/include/asm/qe.h
index f388f0a..d013d7e 100644
--- a/arch/powerpc/include/asm/qe.h
+++ b/arch/powerpc/include/asm/qe.h
@@ -145,8 +145,17 @@ static inline void qe_pin_set_gpio(struct qe_pin *qe_pin) {}
static inline void qe_pin_set_dedicated(struct qe_pin *pin) {}
#endif /* CONFIG_QE_GPIO */
-/* QE internal API */
+#ifdef CONFIG_QUICC_ENGINE
int qe_issue_cmd(u32 cmd, u32 device, u8 mcn_protocol, u32 cmd_input);
+#else
+static inline int qe_issue_cmd(u32 cmd, u32 device, u8 mcn_protocol,
+ u32 cmd_input)
+{
+ return -ENOSYS;
+}
+#endif /* CONFIG_QUICC_ENGINE */
+
+/* QE internal API */
enum qe_clock qe_clock_source(const char *source);
unsigned int qe_get_brg_clk(void);
int qe_setbrg(enum qe_clock brg, unsigned int rate, unsigned int multiplier);
diff --git a/drivers/usb/gadget/fsl_qe_udc.h b/drivers/usb/gadget/fsl_qe_udc.h
index 31b2710..bea5b82 100644
--- a/drivers/usb/gadget/fsl_qe_udc.h
+++ b/drivers/usb/gadget/fsl_qe_udc.h
@@ -419,19 +419,4 @@ struct qe_udc {
#define CPM_USB_RESTART_TX_OPCODE 0x0b
#define CPM_USB_EP_SHIFT 5
-#ifndef CONFIG_CPM
-inline int cpm_command(u32 command, u8 opcode)
-{
- return -EOPNOTSUPP;
-}
-#endif
-
-#ifndef CONFIG_QUICC_ENGINE
-inline int qe_issue_cmd(u32 cmd, u32 device, u8 mcn_protocol,
- u32 cmd_input)
-{
- return -EOPNOTSUPP;
-}
-#endif
-
#endif /* __FSL_QE_UDC_H */
--
1.6.3.3
^ permalink raw reply related
* [PATCH 3/8] powerpc/cpm: Move CPMFCR_* defines into cpm.h
From: Anton Vorontsov @ 2009-10-12 16:49 UTC (permalink / raw)
To: Kumar Gala
Cc: David Brownell, Greg Kroah-Hartman, linux-kernel, linuxppc-dev,
spi-devel-general, Andrew Morton
In-Reply-To: <20091012164841.GA32214@oksana.dev.rtsoft.ru>
The bits are generic to CPM devices, so let's move them to the
common header file, so drivers won't need to privately reintroduce
another bunch of the same bits (as we can't include cpm2.h header
together with cpm1.h).
Signed-off-by: Anton Vorontsov <avorontsov@ru.mvista.com>
---
arch/powerpc/include/asm/cpm.h | 16 ++++++++++++++++
arch/powerpc/include/asm/cpm2.h | 8 --------
2 files changed, 16 insertions(+), 8 deletions(-)
diff --git a/arch/powerpc/include/asm/cpm.h b/arch/powerpc/include/asm/cpm.h
index b5f1534..ea3fdb9 100644
--- a/arch/powerpc/include/asm/cpm.h
+++ b/arch/powerpc/include/asm/cpm.h
@@ -27,6 +27,22 @@ struct usb_ctlr {
u8 res6[0x22];
} __attribute__ ((packed));
+/*
+ * Function code bits, usually generic to devices.
+ */
+#ifdef CONFIG_CPM1
+#define CPMFCR_GBL ((u_char)0x00) /* Flag doesn't exist in CPM1 */
+#define CPMFCR_TC2 ((u_char)0x00) /* Flag doesn't exist in CPM1 */
+#define CPMFCR_DTB ((u_char)0x00) /* Flag doesn't exist in CPM1 */
+#define CPMFCR_BDB ((u_char)0x00) /* Flag doesn't exist in CPM1 */
+#else
+#define CPMFCR_GBL ((u_char)0x20) /* Set memory snooping */
+#define CPMFCR_TC2 ((u_char)0x04) /* Transfer code 2 value */
+#define CPMFCR_DTB ((u_char)0x02) /* Use local bus for data when set */
+#define CPMFCR_BDB ((u_char)0x01) /* Use local bus for BD when set */
+#endif
+#define CPMFCR_EB ((u_char)0x10) /* Set big endian byte order */
+
/* Opcodes common to CPM1 and CPM2
*/
#define CPM_CR_INIT_TRX ((ushort)0x0000)
diff --git a/arch/powerpc/include/asm/cpm2.h b/arch/powerpc/include/asm/cpm2.h
index 236cfa3..f42e9ba 100644
--- a/arch/powerpc/include/asm/cpm2.h
+++ b/arch/powerpc/include/asm/cpm2.h
@@ -124,14 +124,6 @@ static inline void cpm2_fastbrg(uint brg, uint rate, int div16)
__cpm2_setbrg(brg, rate, CPM2_BRG_INT_CLK, div16, CPM_BRG_EXTC_INT);
}
-/* Function code bits, usually generic to devices.
-*/
-#define CPMFCR_GBL ((u_char)0x20) /* Set memory snooping */
-#define CPMFCR_EB ((u_char)0x10) /* Set big endian byte order */
-#define CPMFCR_TC2 ((u_char)0x04) /* Transfer code 2 value */
-#define CPMFCR_DTB ((u_char)0x02) /* Use local bus for data when set */
-#define CPMFCR_BDB ((u_char)0x01) /* Use local bus for BD when set */
-
/* Parameter RAM offsets from the base.
*/
#define PROFF_SCC1 ((uint)0x8000)
--
1.6.3.3
^ permalink raw reply related
page: next (older) | prev (newer) | latest
- recent:[subjects (threaded)|topics (new)|topics (active)]
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox