* How to access DCR registers in powerpc440gx? Got err when use macro def in Linux kernel
From: g r1x @ 2009-09-11 4:14 UTC (permalink / raw)
To: linuxppc-dev
In-Reply-To: <8abf57fa0909101024n668ad353kf8be038898305c28@mail.gmail.com>
[-- Attachment #1: Type: text/plain, Size: 2870 bytes --]
Now, I'm writing a DMA driver on powerpc
440gx platform(2.6.26.5), as the only way to set up DMA Controller is
to access it's dcr registers with 'mfdcr' and 'mtdcr'.
I've found some dma code in Linux kernel 2.6.26.5, so I copy the code
u wrote to my driver module directory, and include them, but when I
compile my driver, gcc complains following err messages:
--------------------------------------------------------
{standard input}: Assembler messages:
{standard input}:83: Error: unsupported relocation against dmanr
---------------------------------------------------------
code I copy from kernel 2.6.26.5 (arch/ppc/syslib/ppc4xx_dma.c)
--------------------------------------------
#include "dcr.h"
/* #inlcude <asm/dcr-native.h> */
ppc_dma_ch_t dma_channels[MAX_PPC4xx_DMA_CHANNELS];
int ppc4xx_get_dma_status(void)
{
return (mfdcr(DCRN_DMASR));
}
void ppc4xx_enable_dma(unsigned int dmanr)
{
unsigned int control;
ppc_dma_ch_t *p_dma_ch = &dma_channels[dmanr];
.........
/ * for other xfer modes, the addresses are already set */
control = mfdcr(DCRN_DMACR0 + (dmanr * 0x8)); <----------------err
}
void ppc4xx_set_src_addr(int dmanr, phys_addr_t src_addr)
{
if (dmanr >= MAX_PPC4xx_DMA_CHANNELS) {
printk("set_src_addr: bad channel: %d\n", dmanr);
return;
}
#ifdef PPC4xx_DMA_64BIT
mtdcr(DCRN_DMASAH0 + dmanr*2, (u32)(src_addr >> 32));
#else
mtdcr(DCRN_DMASA0 + dmanr*2, (u32)src_addr);
#endif
}
--------------------------------------------------
DCR access micro I copied:
----------------------------------
#define mfdcr(rn) \
({ \
unsigned long rval; \
asm volatile("mfdcr %0,%1" : "=r"(rval) : "i"(rn)); \
rval; \
})
#define mtdcr(rn, val) \
asm volatile("mtdcr %0,%1" : : "i"(rn), "r"(val))
-----------------------------------------------------
Makefile I worte
-------------------------
obj-m := dmatest.o
dmatest-objs += dma.o core.o
KBUILD_CFLAGS += -m440 -mregnames -Wa, -booke
KBUILD_ASFAGS += -m440 -mregnames -Wa, -booke
---------
KBUILD_CFLAGS += -m440 -mregnames -Wa, -booke
KBUILD_ASFAGS += -m440 -mregnames -Wa, -booke
I add these two sentences when I read these
http://sourceware.org/ml/binutils/2004-09/msg00161.html.
When I change it to mfdcr(DCRN_DMACR0); then everything is fine. but
from the result I got when I insmod my module driver, dcr reg is not
changed.
It seems that it's the problem with gnu assembler, my GNU assembler ver is 2.17
The attachments are the c source file I copy from linux 2.6.26.5, and
I use these dma function to directly manipulate dcr registers, but
when I make my driver module outside kernel source tree, it complains
the error I mentioned above.
Thanks!
[-- Attachment #2: ppc4xx_dma.c --]
[-- Type: application/octet-stream, Size: 18648 bytes --]
/*
* IBM PPC4xx DMA engine core library
*
* Copyright 2000-2004 MontaVista Software Inc.
*
* Cleaned up and converted to new DCR access
* Matt Porter <mporter@kernel.crashing.org>
*
* Original code by Armin Kuster <akuster@mvista.com>
* and Pete Popov <ppopov@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
* option) any later version.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 675 Mass Ave, Cambridge, MA 02139, USA.
*/
#include <linux/kernel.h>
#include <linux/mm.h>
#include <linux/miscdevice.h>
#include <linux/init.h>
#include <linux/module.h>
#include <asm/system.h>
#include <asm/io.h>
#include <asm/dma.h>
#include <asm/ppc4xx_dma.h>
ppc_dma_ch_t dma_channels[MAX_PPC4xx_DMA_CHANNELS];
int
ppc4xx_get_dma_status(void)
{
return (mfdcr(DCRN_DMASR));
}
void
ppc4xx_set_src_addr(int dmanr, phys_addr_t src_addr)
{
if (dmanr >= MAX_PPC4xx_DMA_CHANNELS) {
printk("set_src_addr: bad channel: %d\n", dmanr);
return;
}
#ifdef PPC4xx_DMA_64BIT
mtdcr(DCRN_DMASAH0 + dmanr*2, (u32)(src_addr >> 32));
#else
mtdcr(DCRN_DMASA0 + dmanr*2, (u32)src_addr);
#endif
}
void
ppc4xx_set_dst_addr(int dmanr, phys_addr_t dst_addr)
{
if (dmanr >= MAX_PPC4xx_DMA_CHANNELS) {
printk("set_dst_addr: bad channel: %d\n", dmanr);
return;
}
#ifdef PPC4xx_DMA_64BIT
mtdcr(DCRN_DMADAH0 + dmanr*2, (u32)(dst_addr >> 32));
#else
mtdcr(DCRN_DMADA0 + dmanr*2, (u32)dst_addr);
#endif
}
void
ppc4xx_enable_dma(unsigned int dmanr)
{
unsigned int control;
ppc_dma_ch_t *p_dma_ch = &dma_channels[dmanr];
unsigned int status_bits[] = { DMA_CS0 | DMA_TS0 | DMA_CH0_ERR,
DMA_CS1 | DMA_TS1 | DMA_CH1_ERR,
DMA_CS2 | DMA_TS2 | DMA_CH2_ERR,
DMA_CS3 | DMA_TS3 | DMA_CH3_ERR};
if (p_dma_ch->in_use) {
printk("enable_dma: channel %d in use\n", dmanr);
return;
}
if (dmanr >= MAX_PPC4xx_DMA_CHANNELS) {
printk("enable_dma: bad channel: %d\n", dmanr);
return;
}
if (p_dma_ch->mode == DMA_MODE_READ) {
/* peripheral to memory */
ppc4xx_set_src_addr(dmanr, 0);
ppc4xx_set_dst_addr(dmanr, p_dma_ch->addr);
} else if (p_dma_ch->mode == DMA_MODE_WRITE) {
/* memory to peripheral */
ppc4xx_set_src_addr(dmanr, p_dma_ch->addr);
ppc4xx_set_dst_addr(dmanr, 0);
}
/* for other xfer modes, the addresses are already set */
control = mfdcr(DCRN_DMACR0 + (dmanr * 0x8));
control &= ~(DMA_TM_MASK | DMA_TD); /* clear all mode bits */
if (p_dma_ch->mode == DMA_MODE_MM) {
/* software initiated memory to memory */
control |= DMA_ETD_OUTPUT | DMA_TCE_ENABLE;
}
mtdcr(DCRN_DMACR0 + (dmanr * 0x8), control);
/*
* Clear the CS, TS, RI bits for the channel from DMASR. This
* has been observed to happen correctly only after the mode and
* ETD/DCE bits in DMACRx are set above. Must do this before
* enabling the channel.
*/
mtdcr(DCRN_DMASR, status_bits[dmanr]);
/*
* For device-paced transfers, Terminal Count Enable apparently
* must be on, and this must be turned on after the mode, etc.
* bits are cleared above (at least on Redwood-6).
*/
if ((p_dma_ch->mode == DMA_MODE_MM_DEVATDST) ||
(p_dma_ch->mode == DMA_MODE_MM_DEVATSRC))
control |= DMA_TCE_ENABLE;
/*
* Now enable the channel.
*/
control |= (p_dma_ch->mode | DMA_CE_ENABLE);
mtdcr(DCRN_DMACR0 + (dmanr * 0x8), control);
p_dma_ch->in_use = 1;
}
void
ppc4xx_disable_dma(unsigned int dmanr)
{
unsigned int control;
ppc_dma_ch_t *p_dma_ch = &dma_channels[dmanr];
if (!p_dma_ch->in_use) {
printk("disable_dma: channel %d not in use\n", dmanr);
return;
}
if (dmanr >= MAX_PPC4xx_DMA_CHANNELS) {
printk("disable_dma: bad channel: %d\n", dmanr);
return;
}
control = mfdcr(DCRN_DMACR0 + (dmanr * 0x8));
control &= ~DMA_CE_ENABLE;
mtdcr(DCRN_DMACR0 + (dmanr * 0x8), control);
p_dma_ch->in_use = 0;
}
/*
* Sets the dma mode for single DMA transfers only.
* For scatter/gather transfers, the mode is passed to the
* alloc_dma_handle() function as one of the parameters.
*
* The mode is simply saved and used later. This allows
* the driver to call set_dma_mode() and set_dma_addr() in
* any order.
*
* Valid mode values are:
*
* DMA_MODE_READ peripheral to memory
* DMA_MODE_WRITE memory to peripheral
* DMA_MODE_MM memory to memory
* DMA_MODE_MM_DEVATSRC device-paced memory to memory, device at src
* DMA_MODE_MM_DEVATDST device-paced memory to memory, device at dst
*/
int
ppc4xx_set_dma_mode(unsigned int dmanr, unsigned int mode)
{
ppc_dma_ch_t *p_dma_ch = &dma_channels[dmanr];
if (dmanr >= MAX_PPC4xx_DMA_CHANNELS) {
printk("set_dma_mode: bad channel 0x%x\n", dmanr);
return DMA_STATUS_BAD_CHANNEL;
}
p_dma_ch->mode = mode;
return DMA_STATUS_GOOD;
}
/*
* Sets the DMA Count register. Note that 'count' is in bytes.
* However, the DMA Count register counts the number of "transfers",
* where each transfer is equal to the bus width. Thus, count
* MUST be a multiple of the bus width.
*/
void
ppc4xx_set_dma_count(unsigned int dmanr, unsigned int count)
{
ppc_dma_ch_t *p_dma_ch = &dma_channels[dmanr];
#ifdef DEBUG_4xxDMA
{
int error = 0;
switch (p_dma_ch->pwidth) {
case PW_8:
break;
case PW_16:
if (count & 0x1)
error = 1;
break;
case PW_32:
if (count & 0x3)
error = 1;
break;
case PW_64:
if (count & 0x7)
error = 1;
break;
default:
printk("set_dma_count: invalid bus width: 0x%x\n",
p_dma_ch->pwidth);
return;
}
if (error)
printk
("Warning: set_dma_count count 0x%x bus width %d\n",
count, p_dma_ch->pwidth);
}
#endif
count = count >> p_dma_ch->shift;
mtdcr(DCRN_DMACT0 + (dmanr * 0x8), count);
}
/*
* Returns the number of bytes left to be transferred.
* After a DMA transfer, this should return zero.
* Reading this while a DMA transfer is still in progress will return
* unpredictable results.
*/
int
ppc4xx_get_dma_residue(unsigned int dmanr)
{
unsigned int count;
ppc_dma_ch_t *p_dma_ch = &dma_channels[dmanr];
if (dmanr >= MAX_PPC4xx_DMA_CHANNELS) {
printk("ppc4xx_get_dma_residue: bad channel 0x%x\n", dmanr);
return DMA_STATUS_BAD_CHANNEL;
}
count = mfdcr(DCRN_DMACT0 + (dmanr * 0x8));
return (count << p_dma_ch->shift);
}
/*
* Sets the DMA address for a memory to peripheral or peripheral
* to memory transfer. The address is just saved in the channel
* structure for now and used later in enable_dma().
*/
void
ppc4xx_set_dma_addr(unsigned int dmanr, phys_addr_t addr)
{
ppc_dma_ch_t *p_dma_ch = &dma_channels[dmanr];
if (dmanr >= MAX_PPC4xx_DMA_CHANNELS) {
printk("ppc4xx_set_dma_addr: bad channel: %d\n", dmanr);
return;
}
#ifdef DEBUG_4xxDMA
{
int error = 0;
switch (p_dma_ch->pwidth) {
case PW_8:
break;
case PW_16:
if ((unsigned) addr & 0x1)
error = 1;
break;
case PW_32:
if ((unsigned) addr & 0x3)
error = 1;
break;
case PW_64:
if ((unsigned) addr & 0x7)
error = 1;
break;
default:
printk("ppc4xx_set_dma_addr: invalid bus width: 0x%x\n",
p_dma_ch->pwidth);
return;
}
if (error)
printk("Warning: ppc4xx_set_dma_addr addr 0x%x bus width %d\n",
addr, p_dma_ch->pwidth);
}
#endif
/* save dma address and program it later after we know the xfer mode */
p_dma_ch->addr = addr;
}
/*
* Sets both DMA addresses for a memory to memory transfer.
* For memory to peripheral or peripheral to memory transfers
* the function set_dma_addr() should be used instead.
*/
void
ppc4xx_set_dma_addr2(unsigned int dmanr, phys_addr_t src_dma_addr,
phys_addr_t dst_dma_addr)
{
if (dmanr >= MAX_PPC4xx_DMA_CHANNELS) {
printk("ppc4xx_set_dma_addr2: bad channel: %d\n", dmanr);
return;
}
#ifdef DEBUG_4xxDMA
{
ppc_dma_ch_t *p_dma_ch = &dma_channels[dmanr];
int error = 0;
switch (p_dma_ch->pwidth) {
case PW_8:
break;
case PW_16:
if (((unsigned) src_dma_addr & 0x1) ||
((unsigned) dst_dma_addr & 0x1)
)
error = 1;
break;
case PW_32:
if (((unsigned) src_dma_addr & 0x3) ||
((unsigned) dst_dma_addr & 0x3)
)
error = 1;
break;
case PW_64:
if (((unsigned) src_dma_addr & 0x7) ||
((unsigned) dst_dma_addr & 0x7)
)
error = 1;
break;
default:
printk("ppc4xx_set_dma_addr2: invalid bus width: 0x%x\n",
p_dma_ch->pwidth);
return;
}
if (error)
printk
("Warning: ppc4xx_set_dma_addr2 src 0x%x dst 0x%x bus width %d\n",
src_dma_addr, dst_dma_addr, p_dma_ch->pwidth);
}
#endif
ppc4xx_set_src_addr(dmanr, src_dma_addr);
ppc4xx_set_dst_addr(dmanr, dst_dma_addr);
}
/*
* Enables the channel interrupt.
*
* If performing a scatter/gatter transfer, this function
* MUST be called before calling alloc_dma_handle() and building
* the sgl list. Otherwise, interrupts will not be enabled, if
* they were previously disabled.
*/
int
ppc4xx_enable_dma_interrupt(unsigned int dmanr)
{
unsigned int control;
ppc_dma_ch_t *p_dma_ch = &dma_channels[dmanr];
if (dmanr >= MAX_PPC4xx_DMA_CHANNELS) {
printk("ppc4xx_enable_dma_interrupt: bad channel: %d\n", dmanr);
return DMA_STATUS_BAD_CHANNEL;
}
p_dma_ch->int_enable = 1;
control = mfdcr(DCRN_DMACR0 + (dmanr * 0x8));
control |= DMA_CIE_ENABLE; /* Channel Interrupt Enable */
mtdcr(DCRN_DMACR0 + (dmanr * 0x8), control);
return DMA_STATUS_GOOD;
}
/*
* Disables the channel interrupt.
*
* If performing a scatter/gatter transfer, this function
* MUST be called before calling alloc_dma_handle() and building
* the sgl list. Otherwise, interrupts will not be disabled, if
* they were previously enabled.
*/
int
ppc4xx_disable_dma_interrupt(unsigned int dmanr)
{
unsigned int control;
ppc_dma_ch_t *p_dma_ch = &dma_channels[dmanr];
if (dmanr >= MAX_PPC4xx_DMA_CHANNELS) {
printk("ppc4xx_disable_dma_interrupt: bad channel: %d\n", dmanr);
return DMA_STATUS_BAD_CHANNEL;
}
p_dma_ch->int_enable = 0;
control = mfdcr(DCRN_DMACR0 + (dmanr * 0x8));
control &= ~DMA_CIE_ENABLE; /* Channel Interrupt Enable */
mtdcr(DCRN_DMACR0 + (dmanr * 0x8), control);
return DMA_STATUS_GOOD;
}
/*
* Configures a DMA channel, including the peripheral bus width, if a
* peripheral is attached to the channel, the polarity of the DMAReq and
* DMAAck signals, etc. This information should really be setup by the boot
* code, since most likely the configuration won't change dynamically.
* If the kernel has to call this function, it's recommended that it's
* called from platform specific init code. The driver should not need to
* call this function.
*/
int
ppc4xx_init_dma_channel(unsigned int dmanr, ppc_dma_ch_t * p_init)
{
unsigned int polarity;
uint32_t control = 0;
ppc_dma_ch_t *p_dma_ch = &dma_channels[dmanr];
DMA_MODE_READ = (unsigned long) DMA_TD; /* Peripheral to Memory */
DMA_MODE_WRITE = 0; /* Memory to Peripheral */
if (!p_init) {
printk("ppc4xx_init_dma_channel: NULL p_init\n");
return DMA_STATUS_NULL_POINTER;
}
if (dmanr >= MAX_PPC4xx_DMA_CHANNELS) {
printk("ppc4xx_init_dma_channel: bad channel %d\n", dmanr);
return DMA_STATUS_BAD_CHANNEL;
}
#if DCRN_POL > 0
polarity = mfdcr(DCRN_POL);
#else
polarity = 0;
#endif
/* Setup the control register based on the values passed to
* us in p_init. Then, over-write the control register with this
* new value.
*/
control |= SET_DMA_CONTROL;
/* clear all polarity signals and then "or" in new signal levels */
polarity &= ~GET_DMA_POLARITY(dmanr);
polarity |= p_init->polarity;
#if DCRN_POL > 0
mtdcr(DCRN_POL, polarity);
#endif
mtdcr(DCRN_DMACR0 + (dmanr * 0x8), control);
/* save these values in our dma channel structure */
memcpy(p_dma_ch, p_init, sizeof (ppc_dma_ch_t));
/*
* The peripheral width values written in the control register are:
* PW_8 0
* PW_16 1
* PW_32 2
* PW_64 3
*
* Since the DMA count register takes the number of "transfers",
* we need to divide the count sent to us in certain
* functions by the appropriate number. It so happens that our
* right shift value is equal to the peripheral width value.
*/
p_dma_ch->shift = p_init->pwidth;
/*
* Save the control word for easy access.
*/
p_dma_ch->control = control;
mtdcr(DCRN_DMASR, 0xffffffff); /* clear status register */
return DMA_STATUS_GOOD;
}
/*
* This function returns the channel configuration.
*/
int
ppc4xx_get_channel_config(unsigned int dmanr, ppc_dma_ch_t * p_dma_ch)
{
unsigned int polarity;
unsigned int control;
if (dmanr >= MAX_PPC4xx_DMA_CHANNELS) {
printk("ppc4xx_get_channel_config: bad channel %d\n", dmanr);
return DMA_STATUS_BAD_CHANNEL;
}
memcpy(p_dma_ch, &dma_channels[dmanr], sizeof (ppc_dma_ch_t));
#if DCRN_POL > 0
polarity = mfdcr(DCRN_POL);
#else
polarity = 0;
#endif
p_dma_ch->polarity = polarity & GET_DMA_POLARITY(dmanr);
control = mfdcr(DCRN_DMACR0 + (dmanr * 0x8));
p_dma_ch->cp = GET_DMA_PRIORITY(control);
p_dma_ch->pwidth = GET_DMA_PW(control);
p_dma_ch->psc = GET_DMA_PSC(control);
p_dma_ch->pwc = GET_DMA_PWC(control);
p_dma_ch->phc = GET_DMA_PHC(control);
p_dma_ch->ce = GET_DMA_CE_ENABLE(control);
p_dma_ch->int_enable = GET_DMA_CIE_ENABLE(control);
p_dma_ch->shift = GET_DMA_PW(control);
#ifdef CONFIG_PPC4xx_EDMA
p_dma_ch->pf = GET_DMA_PREFETCH(control);
#else
p_dma_ch->ch_enable = GET_DMA_CH(control);
p_dma_ch->ece_enable = GET_DMA_ECE(control);
p_dma_ch->tcd_disable = GET_DMA_TCD(control);
#endif
return DMA_STATUS_GOOD;
}
/*
* Sets the priority for the DMA channel dmanr.
* Since this is setup by the hardware init function, this function
* can be used to dynamically change the priority of a channel.
*
* Acceptable priorities:
*
* PRIORITY_LOW
* PRIORITY_MID_LOW
* PRIORITY_MID_HIGH
* PRIORITY_HIGH
*
*/
int
ppc4xx_set_channel_priority(unsigned int dmanr, unsigned int priority)
{
unsigned int control;
if (dmanr >= MAX_PPC4xx_DMA_CHANNELS) {
printk("ppc4xx_set_channel_priority: bad channel %d\n", dmanr);
return DMA_STATUS_BAD_CHANNEL;
}
if ((priority != PRIORITY_LOW) &&
(priority != PRIORITY_MID_LOW) &&
(priority != PRIORITY_MID_HIGH) && (priority != PRIORITY_HIGH)) {
printk("ppc4xx_set_channel_priority: bad priority: 0x%x\n", priority);
}
control = mfdcr(DCRN_DMACR0 + (dmanr * 0x8));
control |= SET_DMA_PRIORITY(priority);
mtdcr(DCRN_DMACR0 + (dmanr * 0x8), control);
return DMA_STATUS_GOOD;
}
/*
* Returns the width of the peripheral attached to this channel. This assumes
* that someone who knows the hardware configuration, boot code or some other
* init code, already set the width.
*
* The return value is one of:
* PW_8
* PW_16
* PW_32
* PW_64
*
* The function returns 0 on error.
*/
unsigned int
ppc4xx_get_peripheral_width(unsigned int dmanr)
{
unsigned int control;
if (dmanr >= MAX_PPC4xx_DMA_CHANNELS) {
printk("ppc4xx_get_peripheral_width: bad channel %d\n", dmanr);
return DMA_STATUS_BAD_CHANNEL;
}
control = mfdcr(DCRN_DMACR0 + (dmanr * 0x8));
return (GET_DMA_PW(control));
}
/*
* Clears the channel status bits
*/
int
ppc4xx_clr_dma_status(unsigned int dmanr)
{
if (dmanr >= MAX_PPC4xx_DMA_CHANNELS) {
printk(KERN_ERR "ppc4xx_clr_dma_status: bad channel: %d\n", dmanr);
return DMA_STATUS_BAD_CHANNEL;
}
mtdcr(DCRN_DMASR, ((u32)DMA_CH0_ERR | (u32)DMA_CS0 | (u32)DMA_TS0) >> dmanr);
return DMA_STATUS_GOOD;
}
#ifdef CONFIG_PPC4xx_EDMA
/*
* Enables the burst on the channel (BTEN bit in the control/count register)
* Note:
* For scatter/gather dma, this function MUST be called before the
* ppc4xx_alloc_dma_handle() func as the chan count register is copied into the
* sgl list and used as each sgl element is added.
*/
int
ppc4xx_enable_burst(unsigned int dmanr)
{
unsigned int ctc;
if (dmanr >= MAX_PPC4xx_DMA_CHANNELS) {
printk(KERN_ERR "ppc4xx_enable_burst: bad channel: %d\n", dmanr);
return DMA_STATUS_BAD_CHANNEL;
}
ctc = mfdcr(DCRN_DMACT0 + (dmanr * 0x8)) | DMA_CTC_BTEN;
mtdcr(DCRN_DMACT0 + (dmanr * 0x8), ctc);
return DMA_STATUS_GOOD;
}
/*
* Disables the burst on the channel (BTEN bit in the control/count register)
* Note:
* For scatter/gather dma, this function MUST be called before the
* ppc4xx_alloc_dma_handle() func as the chan count register is copied into the
* sgl list and used as each sgl element is added.
*/
int
ppc4xx_disable_burst(unsigned int dmanr)
{
unsigned int ctc;
if (dmanr >= MAX_PPC4xx_DMA_CHANNELS) {
printk(KERN_ERR "ppc4xx_disable_burst: bad channel: %d\n", dmanr);
return DMA_STATUS_BAD_CHANNEL;
}
ctc = mfdcr(DCRN_DMACT0 + (dmanr * 0x8)) &~ DMA_CTC_BTEN;
mtdcr(DCRN_DMACT0 + (dmanr * 0x8), ctc);
return DMA_STATUS_GOOD;
}
/*
* Sets the burst size (number of peripheral widths) for the channel
* (BSIZ bits in the control/count register))
* must be one of:
* DMA_CTC_BSIZ_2
* DMA_CTC_BSIZ_4
* DMA_CTC_BSIZ_8
* DMA_CTC_BSIZ_16
* Note:
* For scatter/gather dma, this function MUST be called before the
* ppc4xx_alloc_dma_handle() func as the chan count register is copied into the
* sgl list and used as each sgl element is added.
*/
int
ppc4xx_set_burst_size(unsigned int dmanr, unsigned int bsize)
{
unsigned int ctc;
if (dmanr >= MAX_PPC4xx_DMA_CHANNELS) {
printk(KERN_ERR "ppc4xx_set_burst_size: bad channel: %d\n", dmanr);
return DMA_STATUS_BAD_CHANNEL;
}
ctc = mfdcr(DCRN_DMACT0 + (dmanr * 0x8)) &~ DMA_CTC_BSIZ_MSK;
ctc |= (bsize & DMA_CTC_BSIZ_MSK);
mtdcr(DCRN_DMACT0 + (dmanr * 0x8), ctc);
return DMA_STATUS_GOOD;
}
EXPORT_SYMBOL(ppc4xx_enable_burst);
EXPORT_SYMBOL(ppc4xx_disable_burst);
EXPORT_SYMBOL(ppc4xx_set_burst_size);
#endif /* CONFIG_PPC4xx_EDMA */
EXPORT_SYMBOL(ppc4xx_init_dma_channel);
EXPORT_SYMBOL(ppc4xx_get_channel_config);
EXPORT_SYMBOL(ppc4xx_set_channel_priority);
EXPORT_SYMBOL(ppc4xx_get_peripheral_width);
EXPORT_SYMBOL(dma_channels);
EXPORT_SYMBOL(ppc4xx_set_src_addr);
EXPORT_SYMBOL(ppc4xx_set_dst_addr);
EXPORT_SYMBOL(ppc4xx_set_dma_addr);
EXPORT_SYMBOL(ppc4xx_set_dma_addr2);
EXPORT_SYMBOL(ppc4xx_enable_dma);
EXPORT_SYMBOL(ppc4xx_disable_dma);
EXPORT_SYMBOL(ppc4xx_set_dma_mode);
EXPORT_SYMBOL(ppc4xx_set_dma_count);
EXPORT_SYMBOL(ppc4xx_get_dma_residue);
EXPORT_SYMBOL(ppc4xx_enable_dma_interrupt);
EXPORT_SYMBOL(ppc4xx_disable_dma_interrupt);
EXPORT_SYMBOL(ppc4xx_get_dma_status);
EXPORT_SYMBOL(ppc4xx_clr_dma_status);
[-- Attachment #3: dcr-native.h --]
[-- Type: application/octet-stream, Size: 3320 bytes --]
/*
* (c) Copyright 2006 Benjamin Herrenschmidt, IBM Corp.
* <benh@kernel.crashing.org>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See
* the GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#ifndef _ASM_POWERPC_DCR_NATIVE_H
#define _ASM_POWERPC_DCR_NATIVE_H
#ifdef __KERNEL__
#ifndef __ASSEMBLY__
#include <linux/spinlock.h>
typedef struct {
unsigned int base;
} dcr_host_t;
#define DCR_MAP_OK(host) (1)
#define dcr_map(dev, dcr_n, dcr_c) ((dcr_host_t){ .base = (dcr_n) })
#define dcr_unmap(host, dcr_c) do {} while (0)
#define dcr_read(host, dcr_n) mfdcr(dcr_n + host.base)
#define dcr_write(host, dcr_n, value) mtdcr(dcr_n + host.base, value)
/* Device Control Registers */
void __mtdcr(int reg, unsigned int val);
unsigned int __mfdcr(int reg);
#define mfdcr(rn) \
({unsigned int rval; \
if (__builtin_constant_p(rn)) \
asm volatile("mfdcr %0," __stringify(rn) \
: "=r" (rval)); \
else \
rval = __mfdcr(rn); \
rval;})
#define mtdcr(rn, v) \
do { \
if (__builtin_constant_p(rn)) \
asm volatile("mtdcr " __stringify(rn) ",%0" \
: : "r" (v)); \
else \
__mtdcr(rn, v); \
} while (0)
/* R/W of indirect DCRs make use of standard naming conventions for DCRs */
extern spinlock_t dcr_ind_lock;
static inline unsigned __mfdcri(int base_addr, int base_data, int reg)
{
unsigned long flags;
unsigned int val;
spin_lock_irqsave(&dcr_ind_lock, flags);
__mtdcr(base_addr, reg);
val = __mfdcr(base_data);
spin_unlock_irqrestore(&dcr_ind_lock, flags);
return val;
}
static inline void __mtdcri(int base_addr, int base_data, int reg,
unsigned val)
{
unsigned long flags;
spin_lock_irqsave(&dcr_ind_lock, flags);
__mtdcr(base_addr, reg);
__mtdcr(base_data, val);
spin_unlock_irqrestore(&dcr_ind_lock, flags);
}
static inline void __dcri_clrset(int base_addr, int base_data, int reg,
unsigned clr, unsigned set)
{
unsigned long flags;
unsigned int val;
spin_lock_irqsave(&dcr_ind_lock, flags);
__mtdcr(base_addr, reg);
val = (__mfdcr(base_data) & ~clr) | set;
__mtdcr(base_data, val);
spin_unlock_irqrestore(&dcr_ind_lock, flags);
}
#define mfdcri(base, reg) __mfdcri(DCRN_ ## base ## _CONFIG_ADDR, \
DCRN_ ## base ## _CONFIG_DATA, \
reg)
#define mtdcri(base, reg, data) __mtdcri(DCRN_ ## base ## _CONFIG_ADDR, \
DCRN_ ## base ## _CONFIG_DATA, \
reg, data)
#define dcri_clrset(base, reg, clr, set) __dcri_clrset(DCRN_ ## base ## _CONFIG_ADDR, \
DCRN_ ## base ## _CONFIG_DATA, \
reg, clr, set)
#endif /* __ASSEMBLY__ */
#endif /* __KERNEL__ */
#endif /* _ASM_POWERPC_DCR_NATIVE_H */
[-- Attachment #4: dcr.h --]
[-- Type: application/octet-stream, Size: 6308 bytes --]
#ifndef _PPC_BOOT_DCR_H_
#define _PPC_BOOT_DCR_H_
#define mfdcr(rn) \
({ \
unsigned long rval; \
asm volatile("mfdcr %0,%1" : "=r"(rval) : "i"(rn)); \
rval; \
})
#define mtdcr(rn, val) \
asm volatile("mtdcr %0,%1" : : "i"(rn), "r"(val))
/* 440GP/440GX SDRAM controller DCRs */
#define DCRN_SDRAM0_CFGADDR 0x010
#define DCRN_SDRAM0_CFGDATA 0x011
#define SDRAM0_READ(offset) ({\
mtdcr(DCRN_SDRAM0_CFGADDR, offset); \
mfdcr(DCRN_SDRAM0_CFGDATA); })
#define SDRAM0_WRITE(offset, data) ({\
mtdcr(DCRN_SDRAM0_CFGADDR, offset); \
mtdcr(DCRN_SDRAM0_CFGDATA, data); })
#define SDRAM0_B0CR 0x40
#define SDRAM0_B1CR 0x44
#define SDRAM0_B2CR 0x48
#define SDRAM0_B3CR 0x4c
static const unsigned long sdram_bxcr[] = { SDRAM0_B0CR, SDRAM0_B1CR,
SDRAM0_B2CR, SDRAM0_B3CR };
#define SDRAM_CONFIG_BANK_ENABLE 0x00000001
#define SDRAM_CONFIG_SIZE_MASK 0x000e0000
#define SDRAM_CONFIG_BANK_SIZE(reg) \
(0x00400000 << ((reg & SDRAM_CONFIG_SIZE_MASK) >> 17))
/* 440GP External Bus Controller (EBC) */
#define DCRN_EBC0_CFGADDR 0x012
#define DCRN_EBC0_CFGDATA 0x013
#define EBC_NUM_BANKS 8
#define EBC_B0CR 0x00
#define EBC_B1CR 0x01
#define EBC_B2CR 0x02
#define EBC_B3CR 0x03
#define EBC_B4CR 0x04
#define EBC_B5CR 0x05
#define EBC_B6CR 0x06
#define EBC_B7CR 0x07
#define EBC_BXCR(n) (n)
#define EBC_BXCR_BAS 0xfff00000
#define EBC_BXCR_BS 0x000e0000
#define EBC_BXCR_BANK_SIZE(reg) \
(0x100000 << (((reg) & EBC_BXCR_BS) >> 17))
#define EBC_BXCR_BU 0x00018000
#define EBC_BXCR_BU_OFF 0x00000000
#define EBC_BXCR_BU_RO 0x00008000
#define EBC_BXCR_BU_WO 0x00010000
#define EBC_BXCR_BU_RW 0x00018000
#define EBC_BXCR_BW 0x00006000
#define EBC_B0AP 0x10
#define EBC_B1AP 0x11
#define EBC_B2AP 0x12
#define EBC_B3AP 0x13
#define EBC_B4AP 0x14
#define EBC_B5AP 0x15
#define EBC_B6AP 0x16
#define EBC_B7AP 0x17
#define EBC_BXAP(n) (0x10+(n))
#define EBC_BEAR 0x20
#define EBC_BESR 0x21
#define EBC_CFG 0x23
#define EBC_CID 0x24
/* 440GP Clock, PM, chip control */
#define DCRN_CPC0_SR 0x0b0
#define DCRN_CPC0_ER 0x0b1
#define DCRN_CPC0_FR 0x0b2
#define DCRN_CPC0_SYS0 0x0e0
#define CPC0_SYS0_TUNE 0xffc00000
#define CPC0_SYS0_FBDV_MASK 0x003c0000
#define CPC0_SYS0_FWDVA_MASK 0x00038000
#define CPC0_SYS0_FWDVB_MASK 0x00007000
#define CPC0_SYS0_OPDV_MASK 0x00000c00
#define CPC0_SYS0_EPDV_MASK 0x00000300
/* Helper macros to compute the actual clock divider values from the
* encodings in the CPC0 register */
#define CPC0_SYS0_FBDV(reg) \
((((((reg) & CPC0_SYS0_FBDV_MASK) >> 18) - 1) & 0xf) + 1)
#define CPC0_SYS0_FWDVA(reg) \
(8 - (((reg) & CPC0_SYS0_FWDVA_MASK) >> 15))
#define CPC0_SYS0_FWDVB(reg) \
(8 - (((reg) & CPC0_SYS0_FWDVB_MASK) >> 12))
#define CPC0_SYS0_OPDV(reg) \
((((reg) & CPC0_SYS0_OPDV_MASK) >> 10) + 1)
#define CPC0_SYS0_EPDV(reg) \
((((reg) & CPC0_SYS0_EPDV_MASK) >> 8) + 1)
#define CPC0_SYS0_EXTSL 0x00000080
#define CPC0_SYS0_RW_MASK 0x00000060
#define CPC0_SYS0_RL 0x00000010
#define CPC0_SYS0_ZMIISL_MASK 0x0000000c
#define CPC0_SYS0_BYPASS 0x00000002
#define CPC0_SYS0_NTO1 0x00000001
#define DCRN_CPC0_SYS1 0x0e1
#define DCRN_CPC0_CUST0 0x0e2
#define DCRN_CPC0_CUST1 0x0e3
#define DCRN_CPC0_STRP0 0x0e4
#define DCRN_CPC0_STRP1 0x0e5
#define DCRN_CPC0_STRP2 0x0e6
#define DCRN_CPC0_STRP3 0x0e7
#define DCRN_CPC0_GPIO 0x0e8
#define DCRN_CPC0_PLB 0x0e9
#define DCRN_CPC0_CR1 0x0ea
#define DCRN_CPC0_CR0 0x0eb
#define CPC0_CR0_SWE 0x80000000
#define CPC0_CR0_CETE 0x40000000
#define CPC0_CR0_U1FCS 0x20000000
#define CPC0_CR0_U0DTE 0x10000000
#define CPC0_CR0_U0DRE 0x08000000
#define CPC0_CR0_U0DC 0x04000000
#define CPC0_CR0_U1DTE 0x02000000
#define CPC0_CR0_U1DRE 0x01000000
#define CPC0_CR0_U1DC 0x00800000
#define CPC0_CR0_U0EC 0x00400000
#define CPC0_CR0_U1EC 0x00200000
#define CPC0_CR0_UDIV_MASK 0x001f0000
#define CPC0_CR0_UDIV(reg) \
((((reg) & CPC0_CR0_UDIV_MASK) >> 16) + 1)
#define DCRN_CPC0_MIRQ0 0x0ec
#define DCRN_CPC0_MIRQ1 0x0ed
#define DCRN_CPC0_JTAGID 0x0ef
#define DCRN_MAL0_CFG 0x180
#define MAL_RESET 0x80000000
/* 440EP Clock/Power-on Reset regs */
#define DCRN_CPR0_ADDR 0xc
#define DCRN_CPR0_DATA 0xd
#define CPR0_PLLD0 0x60
#define CPR0_OPBD0 0xc0
#define CPR0_PERD0 0xe0
#define CPR0_PRIMBD0 0xa0
#define CPR0_SCPID 0x120
#define CPR0_PLLC0 0x40
/* 405GP Clocking/Power Management/Chip Control regs */
#define DCRN_CPC0_PLLMR 0xb0
#define DCRN_405_CPC0_CR0 0xb1
#define DCRN_405_CPC0_CR1 0xb2
#define DCRN_405_CPC0_PSR 0xb4
/* 405EP Clocking/Power Management/Chip Control regs */
#define DCRN_CPC0_PLLMR0 0xf0
#define DCRN_CPC0_PLLMR1 0xf4
#define DCRN_CPC0_UCR 0xf5
/* 440GX Clock control etc */
#define DCRN_CPR0_CLKUPD 0x020
#define DCRN_CPR0_PLLC 0x040
#define DCRN_CPR0_PLLD 0x060
#define DCRN_CPR0_PRIMAD 0x080
#define DCRN_CPR0_PRIMBD 0x0a0
#define DCRN_CPR0_OPBD 0x0c0
#define DCRN_CPR0_PERD 0x0e0
#define DCRN_CPR0_MALD 0x100
#define DCRN_SDR0_CONFIG_ADDR 0xe
#define DCRN_SDR0_CONFIG_DATA 0xf
/* SDR read/write helper macros */
#define SDR0_READ(offset) ({\
mtdcr(DCRN_SDR0_CONFIG_ADDR, offset); \
mfdcr(DCRN_SDR0_CONFIG_DATA); })
#define SDR0_WRITE(offset, data) ({\
mtdcr(DCRN_SDR0_CONFIG_ADDR, offset); \
mtdcr(DCRN_SDR0_CONFIG_DATA, data); })
#define DCRN_SDR0_UART0 0x0120
#define DCRN_SDR0_UART1 0x0121
#define DCRN_SDR0_UART2 0x0122
#define DCRN_SDR0_UART3 0x0123
/* CPRs read/write helper macros - based off include/asm-ppc/ibm44x.h */
#define DCRN_CPR0_CFGADDR 0xc
#define DCRN_CPR0_CFGDATA 0xd
#define CPR0_READ(offset) ({\
mtdcr(DCRN_CPR0_CFGADDR, offset); \
mfdcr(DCRN_CPR0_CFGDATA); })
#define CPR0_WRITE(offset, data) ({\
mtdcr(DCRN_CPR0_CFGADDR, offset); \
mtdcr(DCRN_CPR0_CFGDATA, data); })
#endif /* _PPC_BOOT_DCR_H_ */
^ permalink raw reply
* Re: [FTRACE] Enabling function_graph causes OOPS
From: Steven Rostedt @ 2009-09-11 3:05 UTC (permalink / raw)
To: Sachin Sant; +Cc: linuxppc-dev
In-Reply-To: <4AA88F7D.5030302@in.ibm.com>
On Thu, 2009-09-10 at 11:02 +0530, Sachin Sant wrote:
> Steven Rostedt wrote:
> > Ah, seems the bug happens to be in the module handling. Does the call
> > back always have .mod_return_to_handler?
> >
> Yes. Every time it ends up in .mod_return_to_handler
BTW, do you have CONFIG_IRQSTACK set?
-- Steve
^ permalink raw reply
* RE: AW: PowerPC PCI DMA issues (prefetch/coherency?)
From: Benjamin Herrenschmidt @ 2009-09-11 2:44 UTC (permalink / raw)
To: Pravin Bathija
Cc: Andrea Zypchen, lebon, Prodyut Hazarika, tburns, linuxppc-dev,
azilkie
In-Reply-To: <9D1E2BDCB5C57B46B56E6D80843439EB090397EC@SDCEXCHANGE01.ad.amcc.com>
On Thu, 2009-09-10 at 13:30 -0700, Pravin Bathija wrote:
> There is also a patch that was submitted for 440EPX a couple of years
> back. The 440EPX SOC causes hangs with Memory Read Multiple (MRM)
> commands. Whether MRM is used or not depends on the value of
> PCI_CACHE_LINE_SIZE register. I see that the changes are no longer
> present in linux 2.6.30+ kernels. Although the patch certainly resolved
> the hang issue with Silicon Image 680 PATA card as the 680 driver
> attempts to use MRM commands - I don't know if it would resolve the data
> corruption issue. It is certainly worth trying in my opinion. Below is a
> link to the patch submission:
>
> http://git.denx.de/?p=linux-2.6-denx.git;a=commit;h=cffefde924123e685327
> 48dd58fcb780eab5e219
The changes in the above repository is a quick hack that can't be merged
as-is (and afaik hasn't been submitted).
If indeed we need to clamp the PCI cache line size on those critters,
then we need something in ppc4xx_pci.c to detect the need, set
pci_cache_line_size to 0 and eventually fixup the existing values in
devices in case u-boot don't have them right.
Care to send a patch ? :-)
Cheers,
Ben
^ permalink raw reply
* Re: AW: PowerPC PCI DMA issues (prefetch/coherency?)
From: Benjamin Herrenschmidt @ 2009-09-11 1:59 UTC (permalink / raw)
To: tburns; +Cc: Prodyut Hazarika, Andrea Zypchen, linuxppc-dev, azilkie, lebon
In-Reply-To: <4AA95941.20506@datacast.com>
On Thu, 2009-09-10 at 15:53 -0400, Tom Burns wrote:
> Hi,
>
> Thank you everyone for your help.
>
> I've been looking into the other dma/pci API calls (dma_alloc_coherent,
> pci_alloc_consistent). I don't see how either of these return memory
> mapped to a TLB with the I bit set to 1 in kernel 2.6.24. In our kernel
> code, the only use of the PPC44x_TLB_I define is in head_44x.S in
> _start. We have CONFIG_NON_COHERENT_CACHE enabled.
It uses _PAGE_NO_CACHE which translates into the I bit in the TLB from
the TLB miss handler.
> We changed our code to use dma_alloc_coherent, removed our manual
> cacheline flushing, and saw the corrupted data return. To me this means
> dma_alloc_coherent cannot be setting the I=1 bit in the TLB entry.
It must and it is. However, what are you allocating with
dma_alloc_coherent ? The actual data buffers must -also- be properly
cache managed, probably via dma_sync().
It would help tremendously if you sent us the source of the driver :-)
> I tried, using our JTAG debugger (BDI3000), to pause operation after
> calling dma_alloc_coherent to examine the TLB entry for the memory
> returned by the call (which was just past
> CONFIG_CONSISTENT_START=0xff100000). The TLB list loaded at the time
> that I paused operation did not show a mapping for this area. I guess
> the kernel swaps TLB entries on the fly so it isn't limited to only 64
> entries? I will try to sleep in the same context as the
> dma_alloc_coherent call to try to catch the TLB entry while loaded to
> see if it has the I bit set.
>
Ben.
> If that fails, any ideas?
>
> Thanks,
> Tom Burns
> International Datacasting Corporation
>
> Mikhail Zolotaryov wrote:
> > Hi Tom,
> >
> > possible solution could be to use tasklet to perform DMA-related job
> > (as in most cases DMA transfer is interrupt driven - makes sense).
> >
> >
> > Tom Burns wrote:
> >> Hi,
> >>
> >> With the default config for the Sequoia board on 2.6.24, calling
> >> pci_dma_sync_sg_for_cpu() results in executing
> >> invalidate_dcache_range() in arch/ppc/kernel/misc.S from
> >> __dma_sync(). This OOPses on PPC440 since it tries to call directly
> >> the assembly instruction dcbi, which can only be executed in
> >> supervisor mode. We tried that before resorting to manual cache line
> >> management with usermode-safe assembly calls.
> >>
> >> Regards,
> >> Tom Burns
> >> International Datacasting Corporation
> >>
> >> Mikhail Zolotaryov wrote:
> >>> Hi,
> >>>
> >>> Why manage cache lines manually, if appropriate code is a part of
> >>> __dma_sync / dma_sync_single_for_device of DMA API ? (implies
> >>> CONFIG_NOT_COHERENT_CACHE enabled, as default for Sequoia Board)
> >>>
> >>> Prodyut Hazarika wrote:
> >>>> Hi Adam,
> >>>>
> >>>>
> >>>>> Yes, I am using the 440EPx (same as the sequoia board). Our
> >>>>> ideDriver is DMA'ing blocks of 192-byte data over the PCI bus
> >>>>>
> >>>> (using
> >>>>
> >>>>> the Sil0680A PCI-IDE bridge). Most of the DMA's (depending on timing)
> >>>>> end up being partially corrupted when we try to parse the data in the
> >>>>> virtual page. We have confirmed the data is good before the PCI-IDE
> >>>>> bridge. We are creating two 8K pages and map them to physical DMA
> >>>>>
> >>>> memory
> >>>>
> >>>>> using single-entry scatter/gather structs. When a DMA block is
> >>>>> corrupted, we see a random portion of it (always a multiple of 16byte
> >>>>> cache lines) is overwritten with old data from the last time the
> >>>>>
> >>>> buffer
> >>>>
> >>>>> was used.
> >>>>
> >>>> This looks like a cache coherency problem.
> >>>> Can you ensure that the TLB entries corresponding to the DMA region
> >>>> has
> >>>> the CacheInhibit bit set.
> >>>> You will need a BDI connected to your system.
> >>>>
> >>>> Also, you will need to invalidate and flush the lines appropriately,
> >>>> since in 440 cores,
> >>>> L1Cache coherency is managed entirely by software.
> >>>> Please look at drivers/net/ibm_newemac/mal.c and core.c for example on
> >>>> how to do it.
> >>>>
> >>>> Thanks
> >>>> Prodyut
> >>>>
> >>>> On Thu, 2009-09-03 at 13:27 -0700, Prodyut Hazarika wrote:
> >>>>
> >>>>> Hi Adam,
> >>>>>
> >>>>>
> >>>>>> Are you sure there is L2 cache on the 440?
> >>>>>>
> >>>>> It depends on the SoC you are using. SoC like 460EX (Canyonlands
> >>>>>
> >>>> board)
> >>>>
> >>>>> have L2Cache.
> >>>>> It seems you are using a Sequoia board, which has a 440EPx SoC.
> >>>>> 440EPx
> >>>>> has a 440 cpu core, but no L2Cache.
> >>>>> Could you please tell me which SoC you are using?
> >>>>> You can also refer to the appropriate dts file to see if there is
> >>>>> L2C.
> >>>>> For example, in canyonlands.dts (460EX based board), we have the L2C
> >>>>> entry.
> >>>>> L2C0: l2c {
> >>>>> ...
> >>>>> }
> >>>>>
> >>>>>
> >>>>>> I am seeing this problem with our custom IDE driver which is
> >>>>>> based on
> >>>>>>
> >>>>
> >>>>
> >>>>>> pretty old code. Our driver uses pci_alloc_consistent() to allocate
> >>>>>>
> >>>> the
> >>>>
> >>>>>> physical DMA memory and alloc_pages() to allocate a virtual page.
> >>>>>> It then uses pci_map_sg() to map to a scatter/gather buffer.
> >>>>>> Perhaps I should convert these to the DMA API calls as you suggest.
> >>>>>>
> >>>>> Could you give more details on the consistency problem? It is a good
> >>>>> idea to change to the new DMA APIs, but pci_alloc_consistent() should
> >>>>> work too
> >>>>>
> >>>>> Thanks
> >>>>> Prodyut On Thu, 2009-09-03 at 19:57 +1000, Benjamin Herrenschmidt
> >>>>> wrote:
> >>>>>
> >>>>>> On Thu, 2009-09-03 at 09:05 +0100, Chris Pringle wrote:
> >>>>>>
> >>>>>>> Hi Adam,
> >>>>>>>
> >>>>>>> If you have a look in include/asm-ppc/pgtable.h for the following
> >>>>>>>
> >>>>> section:
> >>>>>
> >>>>>>> #ifdef CONFIG_44x
> >>>>>>> #define _PAGE_BASE (_PAGE_PRESENT | _PAGE_ACCESSED |
> >>>>>>>
> >>>>> _PAGE_GUARDED)
> >>>>>
> >>>>>>> #else
> >>>>>>> #define _PAGE_BASE (_PAGE_PRESENT | _PAGE_ACCESSED)
> >>>>>>> #endif
> >>>>>>>
> >>>>>>> Try adding _PAGE_COHERENT to the appropriate line above and see if
> >>>>>>>
> >>>>> that
> >>>>>>> fixes your issue - this causes the 'M' bit to be set on the page
> >>>>>>>
> >>>>> which
> >>>>>>> sure enforce cache coherency. If it doesn't, you'll need to check
> >>>>>>>
> >>>>> the
> >>>>>>> 'M' bit isn't being masked out in head_44x.S (it was originally
> >>>>>>>
> >>>>> masked
> >>>>>>> out on arch/powerpc, but was fixed in later kernels when the cache
> >>>>>>>
> >>>>
> >>>>
> >>>>>>> coherency issues with non-SMP systems were resolved).
> >>>>>>>
> >>>>>> I have some doubts about the usefulness of doing that for 4xx.
> >>>>>>
> >>>> AFAIK,
> >>>>
> >>>>>> the 440 core just ignores M.
> >>>>>>
> >>>>>> The problem lies probably elsewhere. Maybe the L2 cache coherency
> >>>>>>
> >>>>> isn't
> >>>>>
> >>>>>> enabled or not working ?
> >>>>>>
> >>>>>> The L1 cache on 440 is simply not coherent, so drivers have to make
> >>>>>>
> >>>>> sure
> >>>>>
> >>>>>> they use the appropriate DMA APIs which will do cache flushing when
> >>>>>> needed.
> >>>>>>
> >>>>>> Adam, what driver is causing you that sort of problems ?
> >>>>>>
> >>>>>> Cheers,
> >>>>>> Ben.
> >>>>>>
> >>>>>>
> >>>>>>
> >>>
> >>>
> >>
> >>
> >
> >
>
>
> _______________________________________________
> Linuxppc-dev mailing list
> Linuxppc-dev@lists.ozlabs.org
> https://lists.ozlabs.org/listinfo/linuxppc-dev
^ permalink raw reply
* Re: AW: PowerPC PCI DMA issues (prefetch/coherency?)
From: Benjamin Herrenschmidt @ 2009-09-11 1:57 UTC (permalink / raw)
To: Mikhail Zolotaryov
Cc: Prodyut Hazarika, tburns, Andrea Zypchen, linuxppc-dev, azilkie
In-Reply-To: <4AA7BE5A.2070507@lebon.org.ua>
On Wed, 2009-09-09 at 17:40 +0300, Mikhail Zolotaryov wrote:
> Hi Tom,
>
> In my case __dma_sync() calls flush_dcache_range() (it's due to
> alignment) from a tasklet - no OOPS. It uses dcbf instruction instead of
> dcbi - that's the difference as dcbf is not privileged.
What it calls depends on the direction of the transfer. The tasklet runs
in priviledged mode, dcbi should work just fine... if passed a correct
address :-)
Cheers,
Ben.
> Tom Burns wrote:
> > Hi Mikhail,
> >
> > Sorry, this DMA code is in a tasklet. Are you suggesting the
> > processor is in supervisor mode at that time? Calling
> > pci_dma_sync_sg_for_cpu() from the tasklet context is what generates
> > the OOPS. The entire oops is as follows, if it's relevant:
> >
> > Oops: kernel access of bad area, sig: 11 [#1]
> > NIP: c0003ab0 LR: c0010c30 CTR: 02400001
> > REGS: df117bd0 TRAP: 0300 Tainted: P (2.6.24.2)
> > MSR: 00029000 <EE,ME> CR: 44224042 XER: 20000000
> > DEAR: 3fd39000, ESR: 00800000
> > TASK = de5db7d0[157] 'cat' THREAD: df116000
> > GPR00: e11e5854 df117c80 de5db7d0 3fd39000 02400001 0000001f 00000002
> > 0079a169
> > GPR08: 00000001 c0310000 00000000 c0010c84 24224042 101c0dac c0310000
> > 10177000
> > GPR16: deb14200 df116000 e12062d0 e11f6104 de0f16c0 e11f0000 c0310000
> > e11f59cc
> > GPR24: e11f62d0 e11f0000 e11f0000 00000000 00000002 defee014 3fd39008
> > 87d39009
> > NIP [c0003ab0] invalidate_dcache_range+0x1c/0x30
> > LR [c0010c30] __dma_sync+0x58/0xac
> > Call Trace:
> > [df117c80] [0000000a] 0xa (unreliable)
> > [df117c90] [e11e5854] DoTasklet+0x67c/0xc90 [ideDriverDuo_cyph]
> > [df117ce0] [c001ee24] tasklet_action+0x60/0xcc
> > [df117cf0] [c001ef04] __do_softirq+0x74/0xe0
> > [df117d10] [c00067a8] do_softirq+0x54/0x58
> > [df117d20] [c001edb4] irq_exit+0x48/0x58
> > [df117d30] [c00069d0] do_IRQ+0x6c/0xc0
> > [df117d40] [c00020e0] ret_from_except+0x0/0x18
> > [df117e00] [c00501e0] unmap_vmas+0x2c4/0x560
> > [df117e90] [c0053ebc] exit_mmap+0x64/0xec
> > [df117ec0] [c00171ac] mmput+0x50/0xd4
> > [df117ed0] [c001aef8] exit_mm+0x80/0xe0
> > [df117ef0] [c001c818] do_exit+0x134/0x6f8
> > [df117f30] [c001ce14] do_group_exit+0x38/0x74
> > [df117f40] [c0001a80] ret_from_syscall+0x0/0x3c
> > Instruction dump:
> > 7c0018ac 38630020 4200fff8 7c0004ac 4e800020 38a0001f 7c632878 7c832050
> > 7c842a14 5484d97f 4d820020 7c8903a6 <7c001bac> 38630020 4200fff8
> > 7c0004ac
> > Kernel panic - not syncing: Aiee, killing interrupt handler!
> > Rebooting in 180 seconds..
> >
> >
> > Cheers,
> > Tom
> >
> > Mikhail Zolotaryov wrote:
> >> Hi Tom,
> >>
> >> possible solution could be to use tasklet to perform DMA-related job
> >> (as in most cases DMA transfer is interrupt driven - makes sense).
> >>
> >>
> >> Tom Burns wrote:
> >>> Hi,
> >>>
> >>> With the default config for the Sequoia board on 2.6.24, calling
> >>> pci_dma_sync_sg_for_cpu() results in executing
> >>> invalidate_dcache_range() in arch/ppc/kernel/misc.S from
> >>> __dma_sync(). This OOPses on PPC440 since it tries to call directly
> >>> the assembly instruction dcbi, which can only be executed in
> >>> supervisor mode. We tried that before resorting to manual cache
> >>> line management with usermode-safe assembly calls.
> >>>
> >>> Regards,
> >>> Tom Burns
> >>> International Datacasting Corporation
> >>>
> >>> Mikhail Zolotaryov wrote:
> >>>> Hi,
> >>>>
> >>>> Why manage cache lines manually, if appropriate code is a part of
> >>>> __dma_sync / dma_sync_single_for_device of DMA API ? (implies
> >>>> CONFIG_NOT_COHERENT_CACHE enabled, as default for Sequoia Board)
> >>>>
> >>>> Prodyut Hazarika wrote:
> >>>>> Hi Adam,
> >>>>>
> >>>>>
> >>>>>> Yes, I am using the 440EPx (same as the sequoia board). Our
> >>>>>> ideDriver is DMA'ing blocks of 192-byte data over the PCI bus
> >>>>>>
> >>>>> (using
> >>>>>
> >>>>>> the Sil0680A PCI-IDE bridge). Most of the DMA's (depending on
> >>>>>> timing)
> >>>>>> end up being partially corrupted when we try to parse the data in
> >>>>>> the
> >>>>>> virtual page. We have confirmed the data is good before the PCI-IDE
> >>>>>> bridge. We are creating two 8K pages and map them to physical DMA
> >>>>>>
> >>>>> memory
> >>>>>
> >>>>>> using single-entry scatter/gather structs. When a DMA block is
> >>>>>> corrupted, we see a random portion of it (always a multiple of
> >>>>>> 16byte
> >>>>>> cache lines) is overwritten with old data from the last time the
> >>>>>>
> >>>>> buffer
> >>>>>
> >>>>>> was used.
> >>>>>
> >>>>> This looks like a cache coherency problem.
> >>>>> Can you ensure that the TLB entries corresponding to the DMA
> >>>>> region has
> >>>>> the CacheInhibit bit set.
> >>>>> You will need a BDI connected to your system.
> >>>>>
> >>>>> Also, you will need to invalidate and flush the lines appropriately,
> >>>>> since in 440 cores,
> >>>>> L1Cache coherency is managed entirely by software.
> >>>>> Please look at drivers/net/ibm_newemac/mal.c and core.c for
> >>>>> example on
> >>>>> how to do it.
> >>>>>
> >>>>> Thanks
> >>>>> Prodyut
> >>>>>
> >>>>> On Thu, 2009-09-03 at 13:27 -0700, Prodyut Hazarika wrote:
> >>>>>
> >>>>>> Hi Adam,
> >>>>>>
> >>>>>>
> >>>>>>> Are you sure there is L2 cache on the 440?
> >>>>>>>
> >>>>>> It depends on the SoC you are using. SoC like 460EX (Canyonlands
> >>>>>>
> >>>>> board)
> >>>>>
> >>>>>> have L2Cache.
> >>>>>> It seems you are using a Sequoia board, which has a 440EPx SoC.
> >>>>>> 440EPx
> >>>>>> has a 440 cpu core, but no L2Cache.
> >>>>>> Could you please tell me which SoC you are using?
> >>>>>> You can also refer to the appropriate dts file to see if there is
> >>>>>> L2C.
> >>>>>> For example, in canyonlands.dts (460EX based board), we have the L2C
> >>>>>> entry.
> >>>>>> L2C0: l2c {
> >>>>>> ...
> >>>>>> }
> >>>>>>
> >>>>>>
> >>>>>>> I am seeing this problem with our custom IDE driver which is
> >>>>>>> based on
> >>>>>>>
> >>>>>
> >>>>>
> >>>>>>> pretty old code. Our driver uses pci_alloc_consistent() to allocate
> >>>>>>>
> >>>>> the
> >>>>>
> >>>>>>> physical DMA memory and alloc_pages() to allocate a virtual
> >>>>>>> page. It then uses pci_map_sg() to map to a scatter/gather
> >>>>>>> buffer. Perhaps I should convert these to the DMA API calls as
> >>>>>>> you suggest.
> >>>>>>>
> >>>>>> Could you give more details on the consistency problem? It is a good
> >>>>>> idea to change to the new DMA APIs, but pci_alloc_consistent()
> >>>>>> should
> >>>>>> work too
> >>>>>>
> >>>>>> Thanks
> >>>>>> Prodyut On Thu, 2009-09-03 at 19:57 +1000, Benjamin
> >>>>>> Herrenschmidt wrote:
> >>>>>>
> >>>>>>> On Thu, 2009-09-03 at 09:05 +0100, Chris Pringle wrote:
> >>>>>>>
> >>>>>>>> Hi Adam,
> >>>>>>>>
> >>>>>>>> If you have a look in include/asm-ppc/pgtable.h for the following
> >>>>>>>>
> >>>>>> section:
> >>>>>>
> >>>>>>>> #ifdef CONFIG_44x
> >>>>>>>> #define _PAGE_BASE (_PAGE_PRESENT | _PAGE_ACCESSED |
> >>>>>>>>
> >>>>>> _PAGE_GUARDED)
> >>>>>>
> >>>>>>>> #else
> >>>>>>>> #define _PAGE_BASE (_PAGE_PRESENT | _PAGE_ACCESSED)
> >>>>>>>> #endif
> >>>>>>>>
> >>>>>>>> Try adding _PAGE_COHERENT to the appropriate line above and see if
> >>>>>>>>
> >>>>>> that
> >>>>>>>> fixes your issue - this causes the 'M' bit to be set on the page
> >>>>>>>>
> >>>>>> which
> >>>>>>>> sure enforce cache coherency. If it doesn't, you'll need to check
> >>>>>>>>
> >>>>>> the
> >>>>>>>> 'M' bit isn't being masked out in head_44x.S (it was originally
> >>>>>>>>
> >>>>>> masked
> >>>>>>>> out on arch/powerpc, but was fixed in later kernels when the cache
> >>>>>>>>
> >>>>>
> >>>>>
> >>>>>>>> coherency issues with non-SMP systems were resolved).
> >>>>>>>>
> >>>>>>> I have some doubts about the usefulness of doing that for 4xx.
> >>>>>>>
> >>>>> AFAIK,
> >>>>>
> >>>>>>> the 440 core just ignores M.
> >>>>>>>
> >>>>>>> The problem lies probably elsewhere. Maybe the L2 cache coherency
> >>>>>>>
> >>>>>> isn't
> >>>>>>
> >>>>>>> enabled or not working ?
> >>>>>>>
> >>>>>>> The L1 cache on 440 is simply not coherent, so drivers have to make
> >>>>>>>
> >>>>>> sure
> >>>>>>
> >>>>>>> they use the appropriate DMA APIs which will do cache flushing when
> >>>>>>> needed.
> >>>>>>>
> >>>>>>> Adam, what driver is causing you that sort of problems ?
> >>>>>>>
> >>>>>>> Cheers,
> >>>>>>> Ben.
> >>>>>>>
> >>>>>>>
> >>>>>>>
> >>>>
> >>>>
> >>>
> >>>
> >>
> >>
> >
> >
> _______________________________________________
> Linuxppc-dev mailing list
> Linuxppc-dev@lists.ozlabs.org
> https://lists.ozlabs.org/listinfo/linuxppc-dev
^ permalink raw reply
* Re: AW: PowerPC PCI DMA issues (prefetch/coherency?)
From: Benjamin Herrenschmidt @ 2009-09-11 1:57 UTC (permalink / raw)
To: tburns; +Cc: Prodyut Hazarika, Andrea Zypchen, linuxppc-dev, azilkie, lebon
In-Reply-To: <4AA7B766.2040501@datacast.com>
On Wed, 2009-09-09 at 10:10 -0400, Tom Burns wrote:
> Hi Mikhail,
>
> Sorry, this DMA code is in a tasklet. Are you suggesting the processor
> is in supervisor mode at that time? Calling pci_dma_sync_sg_for_cpu()
> from the tasklet context is what generates the OOPS. The entire oops is
> as follows, if it's relevant:
Yes. A tasklet runs in supervisor mode.
> Oops: kernel access of bad area, sig: 11 [#1]
That seems to indicate that your passed an incorrect address to dcbi
(ie, an address that isn't currently mapped, 0x3fd39000 according to
your log), so that looks like a driver bug to me (unless something is
busted in 2.6.24 version of __dma_sync on 44x...)
Cheers,
Ben.
> NIP: c0003ab0 LR: c0010c30 CTR: 02400001
> REGS: df117bd0 TRAP: 0300 Tainted: P (2.6.24.2)
> MSR: 00029000 <EE,ME> CR: 44224042 XER: 20000000
> DEAR: 3fd39000, ESR: 00800000
> TASK = de5db7d0[157] 'cat' THREAD: df116000
> GPR00: e11e5854 df117c80 de5db7d0 3fd39000 02400001 0000001f 00000002
> 0079a169
> GPR08: 00000001 c0310000 00000000 c0010c84 24224042 101c0dac c0310000
> 10177000
> GPR16: deb14200 df116000 e12062d0 e11f6104 de0f16c0 e11f0000 c0310000
> e11f59cc
> GPR24: e11f62d0 e11f0000 e11f0000 00000000 00000002 defee014 3fd39008
> 87d39009
> NIP [c0003ab0] invalidate_dcache_range+0x1c/0x30
> LR [c0010c30] __dma_sync+0x58/0xac
> Call Trace:
> [df117c80] [0000000a] 0xa (unreliable)
> [df117c90] [e11e5854] DoTasklet+0x67c/0xc90 [ideDriverDuo_cyph]
> [df117ce0] [c001ee24] tasklet_action+0x60/0xcc
> [df117cf0] [c001ef04] __do_softirq+0x74/0xe0
> [df117d10] [c00067a8] do_softirq+0x54/0x58
> [df117d20] [c001edb4] irq_exit+0x48/0x58
> [df117d30] [c00069d0] do_IRQ+0x6c/0xc0
> [df117d40] [c00020e0] ret_from_except+0x0/0x18
> [df117e00] [c00501e0] unmap_vmas+0x2c4/0x560
> [df117e90] [c0053ebc] exit_mmap+0x64/0xec
> [df117ec0] [c00171ac] mmput+0x50/0xd4
> [df117ed0] [c001aef8] exit_mm+0x80/0xe0
> [df117ef0] [c001c818] do_exit+0x134/0x6f8
> [df117f30] [c001ce14] do_group_exit+0x38/0x74
> [df117f40] [c0001a80] ret_from_syscall+0x0/0x3c
> Instruction dump:
> 7c0018ac 38630020 4200fff8 7c0004ac 4e800020 38a0001f 7c632878 7c832050
> 7c842a14 5484d97f 4d820020 7c8903a6 <7c001bac> 38630020 4200fff8
> 7c0004ac
> Kernel panic - not syncing: Aiee, killing interrupt handler!
> Rebooting in 180 seconds..
>
>
> Cheers,
> Tom
>
> Mikhail Zolotaryov wrote:
> > Hi Tom,
> >
> > possible solution could be to use tasklet to perform DMA-related job
> > (as in most cases DMA transfer is interrupt driven - makes sense).
> >
> >
> > Tom Burns wrote:
> >> Hi,
> >>
> >> With the default config for the Sequoia board on 2.6.24, calling
> >> pci_dma_sync_sg_for_cpu() results in executing
> >> invalidate_dcache_range() in arch/ppc/kernel/misc.S from
> >> __dma_sync(). This OOPses on PPC440 since it tries to call directly
> >> the assembly instruction dcbi, which can only be executed in
> >> supervisor mode. We tried that before resorting to manual cache line
> >> management with usermode-safe assembly calls.
> >>
> >> Regards,
> >> Tom Burns
> >> International Datacasting Corporation
> >>
> >> Mikhail Zolotaryov wrote:
> >>> Hi,
> >>>
> >>> Why manage cache lines manually, if appropriate code is a part of
> >>> __dma_sync / dma_sync_single_for_device of DMA API ? (implies
> >>> CONFIG_NOT_COHERENT_CACHE enabled, as default for Sequoia Board)
> >>>
> >>> Prodyut Hazarika wrote:
> >>>> Hi Adam,
> >>>>
> >>>>
> >>>>> Yes, I am using the 440EPx (same as the sequoia board). Our
> >>>>> ideDriver is DMA'ing blocks of 192-byte data over the PCI bus
> >>>>>
> >>>> (using
> >>>>
> >>>>> the Sil0680A PCI-IDE bridge). Most of the DMA's (depending on timing)
> >>>>> end up being partially corrupted when we try to parse the data in the
> >>>>> virtual page. We have confirmed the data is good before the PCI-IDE
> >>>>> bridge. We are creating two 8K pages and map them to physical DMA
> >>>>>
> >>>> memory
> >>>>
> >>>>> using single-entry scatter/gather structs. When a DMA block is
> >>>>> corrupted, we see a random portion of it (always a multiple of 16byte
> >>>>> cache lines) is overwritten with old data from the last time the
> >>>>>
> >>>> buffer
> >>>>
> >>>>> was used.
> >>>>
> >>>> This looks like a cache coherency problem.
> >>>> Can you ensure that the TLB entries corresponding to the DMA region
> >>>> has
> >>>> the CacheInhibit bit set.
> >>>> You will need a BDI connected to your system.
> >>>>
> >>>> Also, you will need to invalidate and flush the lines appropriately,
> >>>> since in 440 cores,
> >>>> L1Cache coherency is managed entirely by software.
> >>>> Please look at drivers/net/ibm_newemac/mal.c and core.c for example on
> >>>> how to do it.
> >>>>
> >>>> Thanks
> >>>> Prodyut
> >>>>
> >>>> On Thu, 2009-09-03 at 13:27 -0700, Prodyut Hazarika wrote:
> >>>>
> >>>>> Hi Adam,
> >>>>>
> >>>>>
> >>>>>> Are you sure there is L2 cache on the 440?
> >>>>>>
> >>>>> It depends on the SoC you are using. SoC like 460EX (Canyonlands
> >>>>>
> >>>> board)
> >>>>
> >>>>> have L2Cache.
> >>>>> It seems you are using a Sequoia board, which has a 440EPx SoC.
> >>>>> 440EPx
> >>>>> has a 440 cpu core, but no L2Cache.
> >>>>> Could you please tell me which SoC you are using?
> >>>>> You can also refer to the appropriate dts file to see if there is
> >>>>> L2C.
> >>>>> For example, in canyonlands.dts (460EX based board), we have the L2C
> >>>>> entry.
> >>>>> L2C0: l2c {
> >>>>> ...
> >>>>> }
> >>>>>
> >>>>>
> >>>>>> I am seeing this problem with our custom IDE driver which is
> >>>>>> based on
> >>>>>>
> >>>>
> >>>>
> >>>>>> pretty old code. Our driver uses pci_alloc_consistent() to allocate
> >>>>>>
> >>>> the
> >>>>
> >>>>>> physical DMA memory and alloc_pages() to allocate a virtual page.
> >>>>>> It then uses pci_map_sg() to map to a scatter/gather buffer.
> >>>>>> Perhaps I should convert these to the DMA API calls as you suggest.
> >>>>>>
> >>>>> Could you give more details on the consistency problem? It is a good
> >>>>> idea to change to the new DMA APIs, but pci_alloc_consistent() should
> >>>>> work too
> >>>>>
> >>>>> Thanks
> >>>>> Prodyut On Thu, 2009-09-03 at 19:57 +1000, Benjamin Herrenschmidt
> >>>>> wrote:
> >>>>>
> >>>>>> On Thu, 2009-09-03 at 09:05 +0100, Chris Pringle wrote:
> >>>>>>
> >>>>>>> Hi Adam,
> >>>>>>>
> >>>>>>> If you have a look in include/asm-ppc/pgtable.h for the following
> >>>>>>>
> >>>>> section:
> >>>>>
> >>>>>>> #ifdef CONFIG_44x
> >>>>>>> #define _PAGE_BASE (_PAGE_PRESENT | _PAGE_ACCESSED |
> >>>>>>>
> >>>>> _PAGE_GUARDED)
> >>>>>
> >>>>>>> #else
> >>>>>>> #define _PAGE_BASE (_PAGE_PRESENT | _PAGE_ACCESSED)
> >>>>>>> #endif
> >>>>>>>
> >>>>>>> Try adding _PAGE_COHERENT to the appropriate line above and see if
> >>>>>>>
> >>>>> that
> >>>>>>> fixes your issue - this causes the 'M' bit to be set on the page
> >>>>>>>
> >>>>> which
> >>>>>>> sure enforce cache coherency. If it doesn't, you'll need to check
> >>>>>>>
> >>>>> the
> >>>>>>> 'M' bit isn't being masked out in head_44x.S (it was originally
> >>>>>>>
> >>>>> masked
> >>>>>>> out on arch/powerpc, but was fixed in later kernels when the cache
> >>>>>>>
> >>>>
> >>>>
> >>>>>>> coherency issues with non-SMP systems were resolved).
> >>>>>>>
> >>>>>> I have some doubts about the usefulness of doing that for 4xx.
> >>>>>>
> >>>> AFAIK,
> >>>>
> >>>>>> the 440 core just ignores M.
> >>>>>>
> >>>>>> The problem lies probably elsewhere. Maybe the L2 cache coherency
> >>>>>>
> >>>>> isn't
> >>>>>
> >>>>>> enabled or not working ?
> >>>>>>
> >>>>>> The L1 cache on 440 is simply not coherent, so drivers have to make
> >>>>>>
> >>>>> sure
> >>>>>
> >>>>>> they use the appropriate DMA APIs which will do cache flushing when
> >>>>>> needed.
> >>>>>>
> >>>>>> Adam, what driver is causing you that sort of problems ?
> >>>>>>
> >>>>>> Cheers,
> >>>>>> Ben.
> >>>>>>
> >>>>>>
> >>>>>>
> >>>
> >>>
> >>
> >>
> >
> >
>
>
> _______________________________________________
> Linuxppc-dev mailing list
> Linuxppc-dev@lists.ozlabs.org
> https://lists.ozlabs.org/listinfo/linuxppc-dev
^ permalink raw reply
* Re: AW: PowerPC PCI DMA issues (prefetch/coherency?)
From: Benjamin Herrenschmidt @ 2009-09-11 1:55 UTC (permalink / raw)
To: tburns; +Cc: Prodyut Hazarika, Andrea Zypchen, linuxppc-dev, azilkie, lebon
In-Reply-To: <4AA7B0EC.4000106@datacast.com>
On Wed, 2009-09-09 at 09:43 -0400, Tom Burns wrote:
> Hi,
>
> With the default config for the Sequoia board on 2.6.24, calling
> pci_dma_sync_sg_for_cpu() results in executing
> invalidate_dcache_range() in arch/ppc/kernel/misc.S from __dma_sync().
> This OOPses on PPC440 since it tries to call directly the assembly
> instruction dcbi, which can only be executed in supervisor mode. We
> tried that before resorting to manual cache line management with
> usermode-safe assembly calls.
Wait a minute.... usermode ? You are doing all of that from userspace ?
I don't understand the story here. You can't call all those kernel APIs
form userspace in the first place, indeed... But then an IDE driver has
nothing to do in userspace neither.
Cheers,
Ben.
> Regards,
> Tom Burns
> International Datacasting Corporation
>
> Mikhail Zolotaryov wrote:
> > Hi,
> >
> > Why manage cache lines manually, if appropriate code is a part of
> > __dma_sync / dma_sync_single_for_device of DMA API ? (implies
> > CONFIG_NOT_COHERENT_CACHE enabled, as default for Sequoia Board)
> >
> > Prodyut Hazarika wrote:
> >> Hi Adam,
> >>
> >>
> >>> Yes, I am using the 440EPx (same as the sequoia board). Our
> >>> ideDriver is DMA'ing blocks of 192-byte data over the PCI bus
> >>>
> >> (using
> >>
> >>> the Sil0680A PCI-IDE bridge). Most of the DMA's (depending on timing)
> >>> end up being partially corrupted when we try to parse the data in the
> >>> virtual page. We have confirmed the data is good before the PCI-IDE
> >>> bridge. We are creating two 8K pages and map them to physical DMA
> >>>
> >> memory
> >>
> >>> using single-entry scatter/gather structs. When a DMA block is
> >>> corrupted, we see a random portion of it (always a multiple of 16byte
> >>> cache lines) is overwritten with old data from the last time the
> >>>
> >> buffer
> >>
> >>> was used.
> >>
> >> This looks like a cache coherency problem.
> >> Can you ensure that the TLB entries corresponding to the DMA region has
> >> the CacheInhibit bit set.
> >> You will need a BDI connected to your system.
> >>
> >> Also, you will need to invalidate and flush the lines appropriately,
> >> since in 440 cores,
> >> L1Cache coherency is managed entirely by software.
> >> Please look at drivers/net/ibm_newemac/mal.c and core.c for example on
> >> how to do it.
> >>
> >> Thanks
> >> Prodyut
> >>
> >> On Thu, 2009-09-03 at 13:27 -0700, Prodyut Hazarika wrote:
> >>
> >>> Hi Adam,
> >>>
> >>>
> >>>> Are you sure there is L2 cache on the 440?
> >>>>
> >>> It depends on the SoC you are using. SoC like 460EX (Canyonlands
> >>>
> >> board)
> >>
> >>> have L2Cache.
> >>> It seems you are using a Sequoia board, which has a 440EPx SoC. 440EPx
> >>> has a 440 cpu core, but no L2Cache.
> >>> Could you please tell me which SoC you are using?
> >>> You can also refer to the appropriate dts file to see if there is L2C.
> >>> For example, in canyonlands.dts (460EX based board), we have the L2C
> >>> entry.
> >>> L2C0: l2c {
> >>> ...
> >>> }
> >>>
> >>>
> >>>> I am seeing this problem with our custom IDE driver which is based on
> >>>>
> >>
> >>
> >>>> pretty old code. Our driver uses pci_alloc_consistent() to allocate
> >>>>
> >> the
> >>
> >>>> physical DMA memory and alloc_pages() to allocate a virtual page.
> >>>> It then uses pci_map_sg() to map to a scatter/gather buffer.
> >>>> Perhaps I should convert these to the DMA API calls as you suggest.
> >>>>
> >>> Could you give more details on the consistency problem? It is a good
> >>> idea to change to the new DMA APIs, but pci_alloc_consistent() should
> >>> work too
> >>>
> >>> Thanks
> >>> Prodyut
> >>>
> >>> On Thu, 2009-09-03 at 19:57 +1000, Benjamin Herrenschmidt wrote:
> >>>
> >>>> On Thu, 2009-09-03 at 09:05 +0100, Chris Pringle wrote:
> >>>>
> >>>>> Hi Adam,
> >>>>>
> >>>>> If you have a look in include/asm-ppc/pgtable.h for the following
> >>>>>
> >>> section:
> >>>
> >>>>> #ifdef CONFIG_44x
> >>>>> #define _PAGE_BASE (_PAGE_PRESENT | _PAGE_ACCESSED |
> >>>>>
> >>> _PAGE_GUARDED)
> >>>
> >>>>> #else
> >>>>> #define _PAGE_BASE (_PAGE_PRESENT | _PAGE_ACCESSED)
> >>>>> #endif
> >>>>>
> >>>>> Try adding _PAGE_COHERENT to the appropriate line above and see if
> >>>>>
> >>> that
> >>>>> fixes your issue - this causes the 'M' bit to be set on the page
> >>>>>
> >>> which
> >>>>> sure enforce cache coherency. If it doesn't, you'll need to check
> >>>>>
> >>> the
> >>>>> 'M' bit isn't being masked out in head_44x.S (it was originally
> >>>>>
> >>> masked
> >>>>> out on arch/powerpc, but was fixed in later kernels when the cache
> >>>>>
> >>
> >>
> >>>>> coherency issues with non-SMP systems were resolved).
> >>>>>
> >>>> I have some doubts about the usefulness of doing that for 4xx.
> >>>>
> >> AFAIK,
> >>
> >>>> the 440 core just ignores M.
> >>>>
> >>>> The problem lies probably elsewhere. Maybe the L2 cache coherency
> >>>>
> >>> isn't
> >>>
> >>>> enabled or not working ?
> >>>>
> >>>> The L1 cache on 440 is simply not coherent, so drivers have to make
> >>>>
> >>> sure
> >>>
> >>>> they use the appropriate DMA APIs which will do cache flushing when
> >>>> needed.
> >>>>
> >>>> Adam, what driver is causing you that sort of problems ?
> >>>>
> >>>> Cheers,
> >>>> Ben.
> >>>>
> >>>>
> >>>>
> >
> >
>
>
> _______________________________________________
> Linuxppc-dev mailing list
> Linuxppc-dev@lists.ozlabs.org
> https://lists.ozlabs.org/listinfo/linuxppc-dev
^ permalink raw reply
* Re: [PATCH 01/10] Add support for GCC-4.5's __builtin_unreachable() to compiler.h
From: Richard Henderson @ 2009-09-11 0:14 UTC (permalink / raw)
To: David Daney
Cc: linux-mips, Heiko Carstens, linuxppc-dev, Paul Mackerras,
H. Peter Anvin, linux-s390, linux-am33-list, Helge Deller, x86,
Ingo Molnar, Mike Frysinger, Ivan Kokshaysky, uclinux-dist-devel,
Thomas Gleixner, Haavard Skinnemoen, linux-parisc, linux-kernel,
ralf, Kyle McMartin, linux-alpha, Martin Schwidefsky, linux390,
akpm, Koichi Yasutake, torvalds
In-Reply-To: <1252627011-2933-1-git-send-email-ddaney@caviumnetworks.com>
On 09/10/2009 04:56 PM, David Daney wrote:
> +#ifndef unreachable
> +# define unreachable() do { for (;;) ; } while (0)
> +#endif
#define unreachable() do { } while (1)
r~
^ permalink raw reply
* [PATCH 07/10] powerpc: Convert BUG() to use unreachable()
From: David Daney @ 2009-09-10 23:56 UTC (permalink / raw)
To: torvalds, akpm; +Cc: linuxppc-dev, Paul Mackerras, linux-kernel, David Daney
In-Reply-To: <4AA991C1.1050800@caviumnetworks.com>
Use the new unreachable() macro instead of for(;;);
Signed-off-by: David Daney <ddaney@caviumnetworks.com>
CC: Benjamin Herrenschmidt <benh@kernel.crashing.org>
CC: Paul Mackerras <paulus@samba.org>
CC: linuxppc-dev@ozlabs.org
---
arch/powerpc/include/asm/bug.h | 2 +-
1 files changed, 1 insertions(+), 1 deletions(-)
diff --git a/arch/powerpc/include/asm/bug.h b/arch/powerpc/include/asm/bug.h
index 64e1fdc..2c15212 100644
--- a/arch/powerpc/include/asm/bug.h
+++ b/arch/powerpc/include/asm/bug.h
@@ -68,7 +68,7 @@
_EMIT_BUG_ENTRY \
: : "i" (__FILE__), "i" (__LINE__), \
"i" (0), "i" (sizeof(struct bug_entry))); \
- for(;;) ; \
+ unreachable(); \
} while (0)
#define BUG_ON(x) do { \
--
1.6.2.5
^ permalink raw reply related
* [PATCH 01/10] Add support for GCC-4.5's __builtin_unreachable() to compiler.h
From: David Daney @ 2009-09-10 23:56 UTC (permalink / raw)
To: torvalds, akpm
Cc: linux-mips, Heiko Carstens, linuxppc-dev, Paul Mackerras,
H. Peter Anvin, linux-s390, linux-am33-list, Helge Deller, x86,
David Daney, Ingo Molnar, Mike Frysinger, Ivan Kokshaysky,
uclinux-dist-devel, Thomas Gleixner, Richard Henderson,
Haavard Skinnemoen, linux-parisc, linux-kernel, ralf,
Kyle McMartin, linux-alpha, Martin Schwidefsky, linux390,
Koichi Yasutake
In-Reply-To: <4AA991C1.1050800@caviumnetworks.com>
Starting with version 4.5, GCC has a new built-in function
__builtin_unreachable() that can be used in places like the kernel's
BUG() where inline assembly is used to transfer control flow. This
eliminated the need for an endless loop in these places.
The patch adds a new macro 'unreachable()' that will expand to either
__builtin_unreachable() or an endless loop depending on the compiler
version.
Signed-off-by: David Daney <ddaney@caviumnetworks.com>
CC: Thomas Gleixner <tglx@linutronix.de>
CC: Ingo Molnar <mingo@redhat.com>
CC: "H. Peter Anvin" <hpa@zytor.com>
CC: x86@kernel.org
CC: ralf@linux-mips.org
CC: linux-mips@linux-mips.org
CC: Martin Schwidefsky <schwidefsky@de.ibm.com>
CC: Heiko Carstens <heiko.carstens@de.ibm.com>
CC: linux390@de.ibm.com
CC: linux-s390@vger.kernel.org
CC: David Howells <dhowells@redhat.com>
CC: Koichi Yasutake <yasutake.koichi@jp.panasonic.com>
CC: linux-am33-list@redhat.com
CC: Kyle McMartin <kyle@mcmartin.ca>
CC: Helge Deller <deller@gmx.de>
CC: linux-parisc@vger.kernel.org
CC: Benjamin Herrenschmidt <benh@kernel.crashing.org>
CC: Paul Mackerras <paulus@samba.org>
CC: linuxppc-dev@ozlabs.org
CC: Richard Henderson <rth@twiddle.net>
CC: Ivan Kokshaysky <ink@jurassic.park.msu.ru>
CC: linux-alpha@vger.kernel.org
CC: Haavard Skinnemoen <hskinnemoen@atmel.com>
CC: Mike Frysinger <vapier@gentoo.org>
CC: uclinux-dist-devel@blackfin.uclinux.org
---
include/linux/compiler-gcc4.h | 14 ++++++++++++++
include/linux/compiler.h | 5 +++++
2 files changed, 19 insertions(+), 0 deletions(-)
diff --git a/include/linux/compiler-gcc4.h b/include/linux/compiler-gcc4.h
index 450fa59..ab3af40 100644
--- a/include/linux/compiler-gcc4.h
+++ b/include/linux/compiler-gcc4.h
@@ -36,4 +36,18 @@
the kernel context */
#define __cold __attribute__((__cold__))
+
+#if __GNUC_MINOR__ >= 5
+/*
+ * Mark a position in code as unreachable. This can be used to
+ * suppress control flow warnings after asm blocks that transfer
+ * control elsewhere.
+ *
+ * Early snapshots of gcc 4.5 don't support this and we can't detect
+ * this in the preprocessor, but we can live with this because they're
+ * unreleased. Really, we need to have autoconf for the kernel.
+ */
+#define unreachable() __builtin_unreachable()
+#endif
+
#endif
diff --git a/include/linux/compiler.h b/include/linux/compiler.h
index 04fb513..7efd73f 100644
--- a/include/linux/compiler.h
+++ b/include/linux/compiler.h
@@ -144,6 +144,11 @@ void ftrace_likely_update(struct ftrace_branch_data *f, int val, int expect);
# define barrier() __memory_barrier()
#endif
+/* Unreachable code */
+#ifndef unreachable
+# define unreachable() do { for (;;) ; } while (0)
+#endif
+
#ifndef RELOC_HIDE
# define RELOC_HIDE(ptr, off) \
({ unsigned long __ptr; \
--
1.6.2.5
^ permalink raw reply related
* [PATCH 00/10] Add support for GCC's __builtin_unreachable() and use it in BUG.
From: David Daney @ 2009-09-10 23:54 UTC (permalink / raw)
To: Linus Torvalds, Andrew Morton
Cc: linux-mips, Heiko Carstens, linuxppc-dev, Paul Mackerras,
H. Peter Anvin, linux-s390, linux-am33-list, Helge Deller, x86,
Ingo Molnar, Mike Frysinger, Ivan Kokshaysky, uclinux-dist-devel,
Thomas Gleixner, Richard Henderson, Haavard Skinnemoen,
linux-parisc, ralf, Kyle McMartin, linux-alpha,
Martin Schwidefsky, linux390, Koichi Yasutake
Starting with version 4.5, GCC has a new built-in function called
__builtin_unreachable(). The function tells the compiler that control
flow will never reach that point. Currently we trick the compiler by
putting in for(;;); but this has the disadvantage that extra code is
emitted for an endless loop. For an i386 kernel using
__builtin_unreachable() results in an allyesconfig that is nearly 4000
bytes smaller.
This patch set adds support to compiler.h creating a
new macro usable in the kernel called unreachable(). If the compiler
lacks __builtin_unreachable(), it just expands to for(;;).
The x86 and MIPS patches I actually tested with a GCC-4.5 snapshot.
Lacking the ability to test the rest of the architectures, I just did
what seemed right without even trying to compile the kernel.
01/10 adds the compiler.h support, the rest of the patches retrofit
the various architecture BUG macros to use it instead of for(;;) or
while(1) loops.
I will reply with the 10 patches.
The architecture specific patches I will send to a smaller set of
people.
David Daney (10):
Add support for GCC-4.5's __builtin_unreachable() to compiler.h
x86: Convert BUG() to use unreachable()
MIPS: Convert BUG() to use unreachable()
s390: Convert BUG() to use unreachable()
mn10300: Convert BUG() to use unreachable()
parisc: Convert BUG() to use unreachable()
powerpc: Convert BUG() to use unreachable()
alpha: Convert BUG() to use unreachable()
avr32: Convert BUG() to use unreachable()
blackfin: Convert BUG() to use unreachable()
arch/alpha/include/asm/bug.h | 2 +-
arch/avr32/include/asm/bug.h | 2 +-
arch/blackfin/include/asm/bug.h | 2 +-
arch/mips/include/asm/bug.h | 4 +---
arch/mn10300/include/asm/bug.h | 3 ++-
arch/parisc/include/asm/bug.h | 4 ++--
arch/powerpc/include/asm/bug.h | 2 +-
arch/s390/include/asm/bug.h | 2 +-
arch/x86/include/asm/bug.h | 4 ++--
include/linux/compiler-gcc4.h | 14 ++++++++++++++
include/linux/compiler.h | 5 +++++
11 files changed, 31 insertions(+), 13 deletions(-)
^ permalink raw reply
* [PATCH] powerpc/include/asm/irq.h: improve nanodoc
From: Wolfram Sang @ 2009-09-10 22:47 UTC (permalink / raw)
To: linuxppc-dev
The OF helpers look like nanodoc but are missing the header. Fix this and a
typo (s/nad/and/) while we are here.
Signed-off-by: Wolfram Sang <w.sang@pengutronix.de>
Cc: Benjamin Herrenschmidt <benh@kernel.crashing.org>
---
arch/powerpc/include/asm/irq.h | 7 ++++---
1 files changed, 4 insertions(+), 3 deletions(-)
diff --git a/arch/powerpc/include/asm/irq.h b/arch/powerpc/include/asm/irq.h
index 0a51376..bbcd1aa 100644
--- a/arch/powerpc/include/asm/irq.h
+++ b/arch/powerpc/include/asm/irq.h
@@ -302,7 +302,8 @@ extern void irq_free_virt(unsigned int virq, unsigned int count);
/* -- OF helpers -- */
-/* irq_create_of_mapping - Map a hardware interrupt into linux virq space
+/**
+ * irq_create_of_mapping - Map a hardware interrupt into linux virq space
* @controller: Device node of the interrupt controller
* @inspec: Interrupt specifier from the device-tree
* @intsize: Size of the interrupt specifier from the device-tree
@@ -314,8 +315,8 @@ extern void irq_free_virt(unsigned int virq, unsigned int count);
extern unsigned int irq_create_of_mapping(struct device_node *controller,
u32 *intspec, unsigned int intsize);
-
-/* irq_of_parse_and_map - Parse nad Map an interrupt into linux virq space
+/**
+ * irq_of_parse_and_map - Parse and Map an interrupt into linux virq space
* @device: Device node of the device whose interrupt is to be mapped
* @index: Index of the interrupt to map
*
--
1.6.3.3
^ permalink raw reply related
* Re: MPC85xx External/Internal Interrupts
From: Scott Wood @ 2009-09-10 22:27 UTC (permalink / raw)
To: Sebastian Andrzej Siewior; +Cc: linuxppc-dev, Alemao, linux-kernel
In-Reply-To: <20090910222400.GB26587@Chamillionaire.breakpoint.cc>
Sebastian Andrzej Siewior wrote:
> So the split is a FSL thing.
> What do you thing about making this clear? Adding into every .dts a
> comment right on top or maybe in
> Documentation/powerpc/dts-bindings/fsl/?
Add an fsl/mpic.txt binding that describes this and any other
pecularities above and beyond the base openpic binding.
-Scott
^ permalink raw reply
* Re: MPC85xx External/Internal Interrupts
From: Sebastian Andrzej Siewior @ 2009-09-10 22:24 UTC (permalink / raw)
To: Scott Wood; +Cc: linuxppc-dev, Alemao, linux-kernel
In-Reply-To: <20090910131544.GB23433@Chamillionaire.breakpoint.cc>
* Sebastian Andrzej Siewior | 2009-09-10 15:15:44 [+0200]:
>* Scott Wood | 2009-09-09 13:28:57 [-0500]:
>
>>> That's why you always have an offset of 16 between every internal
>>> interupt source number in the MPC855ERM document and those weired
>>> numbers in the device tree :)
>>
>>something in the dts bindings that explains it. Am I correct in assuming
>>that this particular internal/external split is Freescale-specific and
>>not a general OpenPIC thing?
>Yes it looks like this.
>manual for both of them to check. I also don't have an OpenPIC spec to
>check there.
I had to say this. Now I got one. According to the OpenPIC there is no
such thing as an internal and external interrupt sources. Base + 0x10000
is the first interrupt source register and the number of interrupt
sources is specified in the feature register.
So the split is a FSL thing.
What do you thing about making this clear? Adding into every .dts a
comment right on top or maybe in
Documentation/powerpc/dts-bindings/fsl/?
>>
>>-Scott
Sebastian
^ permalink raw reply
* [PATCH v3 3/3] ucc_geth: Fix hangs after switching from full to half duplex
From: Anton Vorontsov @ 2009-09-10 21:48 UTC (permalink / raw)
To: Scott Wood; +Cc: linuxppc-dev, netdev, Andy Fleming, David Miller, Timur Tabi
In-Reply-To: <20090910210935.GA26037@oksana.dev.rtsoft.ru>
MPC8360 QE UCC ethernet controllers hang when changing link duplex
under a load (a bit of NFS activity is enough).
PHY: mdio@e0102120:00 - Link is Up - 1000/Full
sh-3.00# ethtool -s eth0 speed 100 duplex half autoneg off
PHY: mdio@e0102120:00 - Link is Down
PHY: mdio@e0102120:00 - Link is Up - 100/Half
NETDEV WATCHDOG: eth0 (ucc_geth): transmit queue 0 timed out
------------[ cut here ]------------
Badness at c01fcbd0 [verbose debug info unavailable]
NIP: c01fcbd0 LR: c01fcbd0 CTR: c0194e44
...
The cure is to disable the controller before changing speed/duplex
and enable it afterwards.
Though, disabling the controller might take quite a while, so we
better not grab any spinlocks in adjust_link(). Instead, we quiesce
the driver's activity, and only then disable the controller.
Signed-off-by: Anton Vorontsov <avorontsov@ru.mvista.com>
---
On Fri, Sep 11, 2009 at 01:09:36AM +0400, Anton Vorontsov wrote:
> On Thu, Sep 10, 2009 at 11:40:53PM +0400, Anton Vorontsov wrote:
> > On Thu, Sep 10, 2009 at 01:04:32PM -0500, Scott Wood wrote:
> > > Anton Vorontsov wrote:
> > > >MPC8360 QE UCC ethernet controllers hang when changing link duplex
> > > >under a load (a bit of NFS activity is enough).
> > > >
> > > > PHY: mdio@e0102120:00 - Link is Up - 1000/Full
> > > > sh-3.00# ethtool -s eth0 speed 100 duplex half autoneg off
> > > > PHY: mdio@e0102120:00 - Link is Down
> > > > PHY: mdio@e0102120:00 - Link is Up - 100/Half
> > > > NETDEV WATCHDOG: eth0 (ucc_geth): transmit queue 0 timed out
> > > > ------------[ cut here ]------------
> > > > Badness at c01fcbd0 [verbose debug info unavailable]
> > > > NIP: c01fcbd0 LR: c01fcbd0 CTR: c0194e44
> > > > ...
> > > >
> > > >The cure is to disable the controller before changing speed/duplex
> > > >and enable it afterwards.
> > > >
> > > >Since ugeth_graceful_stop_{tx,rx} now may be called from an atomic
> > > >context, switch the two functions from msleep() to mdelay().
> > >
> > > Ouch.
> >
> > Yeah, right... delaying for 10ms with irqs off isn't good.
> >
> > > Can we put this in a workqueue or something?
> >
> > adjust_link() itself isn't called from an atomic context.
>
> Oops. I though that phylib calls us from a workqueue, not a timer. Hm...
>
> Will be a little bit more work..
Ignore me. I'm working on two kernel versions in parallel (2.6.21 and
mainline), and it's 2.6.21 where phylib uses a timer. Mainline is OK.
How about this patch?
drivers/net/ucc_geth.c | 36 +++++++++++++++++++++++++++++++-----
1 files changed, 31 insertions(+), 5 deletions(-)
diff --git a/drivers/net/ucc_geth.c b/drivers/net/ucc_geth.c
index 2a2c973..232fef9 100644
--- a/drivers/net/ucc_geth.c
+++ b/drivers/net/ucc_geth.c
@@ -1560,6 +1560,25 @@ static int ugeth_disable(struct ucc_geth_private *ugeth, enum comm_dir mode)
return 0;
}
+static void ugeth_quiesce(struct ucc_geth_private *ugeth)
+{
+ /* Wait for and prevent any further xmits. */
+ netif_tx_disable(ugeth->ndev);
+
+ /* Disable the interrupt to avoid NAPI rescheduling. */
+ disable_irq(ugeth->ug_info->uf_info.irq);
+
+ /* Stop NAPI, and possibly wait for its completion. */
+ napi_disable(&ugeth->napi);
+}
+
+static void ugeth_activate(struct ucc_geth_private *ugeth)
+{
+ napi_enable(&ugeth->napi);
+ enable_irq(ugeth->ug_info->uf_info.irq);
+ netif_tx_wake_all_queues(ugeth->ndev);
+}
+
/* Called every time the controller might need to be made
* aware of new link state. The PHY code conveys this
* information through variables in the ugeth structure, and this
@@ -1573,14 +1592,11 @@ static void adjust_link(struct net_device *dev)
struct ucc_geth __iomem *ug_regs;
struct ucc_fast __iomem *uf_regs;
struct phy_device *phydev = ugeth->phydev;
- unsigned long flags;
int new_state = 0;
ug_regs = ugeth->ug_regs;
uf_regs = ugeth->uccf->uf_regs;
- spin_lock_irqsave(&ugeth->lock, flags);
-
if (phydev->link) {
u32 tempval = in_be32(&ug_regs->maccfg2);
u32 upsmr = in_be32(&uf_regs->upsmr);
@@ -1631,9 +1647,21 @@ static void adjust_link(struct net_device *dev)
ugeth->oldspeed = phydev->speed;
}
+ /*
+ * To change the MAC configuration we need to disable the
+ * controller. To do so, we have to either grab ugeth->lock,
+ * which is a bad idea since 'graceful stop' commands might
+ * take quite a while, or we can quiesce driver's activity.
+ */
+ ugeth_quiesce(ugeth);
+ ugeth_disable(ugeth, COMM_DIR_RX_AND_TX);
+
out_be32(&ug_regs->maccfg2, tempval);
out_be32(&uf_regs->upsmr, upsmr);
+ ugeth_enable(ugeth, COMM_DIR_RX_AND_TX);
+ ugeth_activate(ugeth);
+
if (!ugeth->oldlink) {
new_state = 1;
ugeth->oldlink = 1;
@@ -1647,8 +1675,6 @@ static void adjust_link(struct net_device *dev)
if (new_state && netif_msg_link(ugeth))
phy_print_status(phydev);
-
- spin_unlock_irqrestore(&ugeth->lock, flags);
}
/* Initialize TBI PHY interface for communicating with the
--
1.6.3.3
^ permalink raw reply related
* Re: [PATCH v2 3/3] ucc_geth: Fix hangs after switching from full to half duplex
From: Anton Vorontsov @ 2009-09-10 21:09 UTC (permalink / raw)
To: Scott Wood; +Cc: linuxppc-dev, netdev, Andy Fleming, David Miller, Timur Tabi
In-Reply-To: <20090910194053.GA24363@oksana.dev.rtsoft.ru>
On Thu, Sep 10, 2009 at 11:40:53PM +0400, Anton Vorontsov wrote:
> On Thu, Sep 10, 2009 at 01:04:32PM -0500, Scott Wood wrote:
> > Anton Vorontsov wrote:
> > >MPC8360 QE UCC ethernet controllers hang when changing link duplex
> > >under a load (a bit of NFS activity is enough).
> > >
> > > PHY: mdio@e0102120:00 - Link is Up - 1000/Full
> > > sh-3.00# ethtool -s eth0 speed 100 duplex half autoneg off
> > > PHY: mdio@e0102120:00 - Link is Down
> > > PHY: mdio@e0102120:00 - Link is Up - 100/Half
> > > NETDEV WATCHDOG: eth0 (ucc_geth): transmit queue 0 timed out
> > > ------------[ cut here ]------------
> > > Badness at c01fcbd0 [verbose debug info unavailable]
> > > NIP: c01fcbd0 LR: c01fcbd0 CTR: c0194e44
> > > ...
> > >
> > >The cure is to disable the controller before changing speed/duplex
> > >and enable it afterwards.
> > >
> > >Since ugeth_graceful_stop_{tx,rx} now may be called from an atomic
> > >context, switch the two functions from msleep() to mdelay().
> >
> > Ouch.
>
> Yeah, right... delaying for 10ms with irqs off isn't good.
>
> > Can we put this in a workqueue or something?
>
> adjust_link() itself isn't called from an atomic context.
Oops. I though that phylib calls us from a workqueue, not a timer. Hm...
Will be a little bit more work..
--
Anton Vorontsov
email: cbouatmailru@gmail.com
irc://irc.freenode.net/bd2
^ permalink raw reply
* RE: AW: PowerPC PCI DMA issues (prefetch/coherency?)
From: Pravin Bathija @ 2009-09-10 20:30 UTC (permalink / raw)
To: tburns, lebon; +Cc: Prodyut Hazarika, Andrea Zypchen, linuxppc-dev, azilkie
In-Reply-To: <4AA95941.20506@datacast.com>
> Tom Burns wrote=20
> Hi,
>=20
> Thank you everyone for your help.
>=20
> I've been looking into the other dma/pci API calls
(dma_alloc_coherent,
> pci_alloc_consistent). I don't see how either of these return memory
> mapped to a TLB with the I bit set to 1 in kernel 2.6.24. In our
> kernel
> code, the only use of the PPC44x_TLB_I define is in head_44x.S in
> _start. We have CONFIG_NON_COHERENT_CACHE enabled.
>=20
> We changed our code to use dma_alloc_coherent, removed our manual
> cacheline flushing, and saw the corrupted data return. To me this
> means
> dma_alloc_coherent cannot be setting the I=3D1 bit in the TLB entry.
>=20
> I tried, using our JTAG debugger (BDI3000), to pause operation after
> calling dma_alloc_coherent to examine the TLB entry for the memory
> returned by the call (which was just past
> CONFIG_CONSISTENT_START=3D0xff100000). The TLB list loaded at the =
time
> that I paused operation did not show a mapping for this area. I guess
> the kernel swaps TLB entries on the fly so it isn't limited to only 64
> entries? I will try to sleep in the same context as the
> dma_alloc_coherent call to try to catch the TLB entry while loaded to
> see if it has the I bit set.
>=20
> If that fails, any ideas?
>=20
> Thanks,
> Tom Burns
> International Datacasting Corporation
>=20
There is also a patch that was submitted for 440EPX a couple of years
back. The 440EPX SOC causes hangs with Memory Read Multiple (MRM)
commands. Whether MRM is used or not depends on the value of
PCI_CACHE_LINE_SIZE register. I see that the changes are no longer
present in linux 2.6.30+ kernels. Although the patch certainly resolved
the hang issue with Silicon Image 680 PATA card as the 680 driver
attempts to use MRM commands - I don't know if it would resolve the data
corruption issue. It is certainly worth trying in my opinion. Below is a
link to the patch submission:
http://git.denx.de/?p=3Dlinux-2.6-denx.git;a=3Dcommit;h=3Dcffefde924123e6=
85327
48dd58fcb780eab5e219
> Mikhail Zolotaryov wrote:
> > Hi Tom,
> >
> > possible solution could be to use tasklet to perform DMA-related job
> > (as in most cases DMA transfer is interrupt driven - makes sense).
> >
> >
> > Tom Burns wrote:
> >> Hi,
> >>
> >> With the default config for the Sequoia board on 2.6.24, calling
> >> pci_dma_sync_sg_for_cpu() results in executing
> >> invalidate_dcache_range() in arch/ppc/kernel/misc.S from
> >> __dma_sync(). This OOPses on PPC440 since it tries to call
directly
> >> the assembly instruction dcbi, which can only be executed in
> >> supervisor mode. We tried that before resorting to manual cache
> line
> >> management with usermode-safe assembly calls.
> >>
> >> Regards,
> >> Tom Burns
> >> International Datacasting Corporation
> >>
> >> Mikhail Zolotaryov wrote:
> >>> Hi,
> >>>
> >>> Why manage cache lines manually, if appropriate code is a part of
> >>> __dma_sync / dma_sync_single_for_device of DMA API ? (implies
> >>> CONFIG_NOT_COHERENT_CACHE enabled, as default for Sequoia Board)
> >>>
> >>> Prodyut Hazarika wrote:
> >>>> Hi Adam,
> >>>>
> >>>>
> >>>>> Yes, I am using the 440EPx (same as the sequoia board). Our
> >>>>> ideDriver is DMA'ing blocks of 192-byte data over the PCI bus
> >>>>>
> >>>> (using
> >>>>
> >>>>> the Sil0680A PCI-IDE bridge). Most of the DMA's (depending on
> timing)
> >>>>> end up being partially corrupted when we try to parse the data
in
> the
> >>>>> virtual page. We have confirmed the data is good before the PCI-
> IDE
> >>>>> bridge. We are creating two 8K pages and map them to physical
DMA
> >>>>>
> >>>> memory
> >>>>
> >>>>> using single-entry scatter/gather structs. When a DMA block is
> >>>>> corrupted, we see a random portion of it (always a multiple of
> 16byte
> >>>>> cache lines) is overwritten with old data from the last time the
> >>>>>
> >>>> buffer
> >>>>
> >>>>> was used.
> >>>>
> >>>> This looks like a cache coherency problem.
> >>>> Can you ensure that the TLB entries corresponding to the DMA
> region
> >>>> has
> >>>> the CacheInhibit bit set.
> >>>> You will need a BDI connected to your system.
> >>>>
> >>>> Also, you will need to invalidate and flush the lines
> appropriately,
> >>>> since in 440 cores,
> >>>> L1Cache coherency is managed entirely by software.
> >>>> Please look at drivers/net/ibm_newemac/mal.c and core.c for
> example on
> >>>> how to do it.
> >>>>
> >>>> Thanks
> >>>> Prodyut
> >>>>
> >>>> On Thu, 2009-09-03 at 13:27 -0700, Prodyut Hazarika wrote:
> >>>>
> >>>>> Hi Adam,
> >>>>>
> >>>>>
> >>>>>> Are you sure there is L2 cache on the 440?
> >>>>>>
> >>>>> It depends on the SoC you are using. SoC like 460EX (Canyonlands
> >>>>>
> >>>> board)
> >>>>
> >>>>> have L2Cache.
> >>>>> It seems you are using a Sequoia board, which has a 440EPx SoC.
> >>>>> 440EPx
> >>>>> has a 440 cpu core, but no L2Cache.
> >>>>> Could you please tell me which SoC you are using?
> >>>>> You can also refer to the appropriate dts file to see if there
is
> >>>>> L2C.
> >>>>> For example, in canyonlands.dts (460EX based board), we have the
> L2C
> >>>>> entry.
> >>>>> L2C0: l2c {
> >>>>> ...
> >>>>> }
> >>>>>
> >>>>>
> >>>>>> I am seeing this problem with our custom IDE driver which is
> >>>>>> based on
> >>>>>>
> >>>>
> >>>>
> >>>>>> pretty old code. Our driver uses pci_alloc_consistent() to
> allocate
> >>>>>>
> >>>> the
> >>>>
> >>>>>> physical DMA memory and alloc_pages() to allocate a virtual
> page.
> >>>>>> It then uses pci_map_sg() to map to a scatter/gather buffer.
> >>>>>> Perhaps I should convert these to the DMA API calls as you
> suggest.
> >>>>>>
> >>>>> Could you give more details on the consistency problem? It is a
> good
> >>>>> idea to change to the new DMA APIs, but pci_alloc_consistent()
> should
> >>>>> work too
> >>>>>
> >>>>> Thanks
> >>>>> Prodyut On Thu, 2009-09-03 at 19:57 +1000, Benjamin
> Herrenschmidt
> >>>>> wrote:
> >>>>>
> >>>>>> On Thu, 2009-09-03 at 09:05 +0100, Chris Pringle wrote:
> >>>>>>
> >>>>>>> Hi Adam,
> >>>>>>>
> >>>>>>> If you have a look in include/asm-ppc/pgtable.h for the
> following
> >>>>>>>
> >>>>> section:
> >>>>>
> >>>>>>> #ifdef CONFIG_44x
> >>>>>>> #define _PAGE_BASE (_PAGE_PRESENT | _PAGE_ACCESSED |
> >>>>>>>
> >>>>> _PAGE_GUARDED)
> >>>>>
> >>>>>>> #else
> >>>>>>> #define _PAGE_BASE (_PAGE_PRESENT | _PAGE_ACCESSED)
> >>>>>>> #endif
> >>>>>>>
> >>>>>>> Try adding _PAGE_COHERENT to the appropriate line above and
see
> if
> >>>>>>>
> >>>>> that
> >>>>>>> fixes your issue - this causes the 'M' bit to be set on the
> page
> >>>>>>>
> >>>>> which
> >>>>>>> sure enforce cache coherency. If it doesn't, you'll need to
> check
> >>>>>>>
> >>>>> the
> >>>>>>> 'M' bit isn't being masked out in head_44x.S (it was
originally
> >>>>>>>
> >>>>> masked
> >>>>>>> out on arch/powerpc, but was fixed in later kernels when the
> cache
> >>>>>>>
> >>>>
> >>>>
> >>>>>>> coherency issues with non-SMP systems were resolved).
> >>>>>>>
> >>>>>> I have some doubts about the usefulness of doing that for 4xx.
> >>>>>>
> >>>> AFAIK,
> >>>>
> >>>>>> the 440 core just ignores M.
> >>>>>>
> >>>>>> The problem lies probably elsewhere. Maybe the L2 cache
> coherency
> >>>>>>
> >>>>> isn't
> >>>>>
> >>>>>> enabled or not working ?
> >>>>>>
> >>>>>> The L1 cache on 440 is simply not coherent, so drivers have to
> make
> >>>>>>
> >>>>> sure
> >>>>>
> >>>>>> they use the appropriate DMA APIs which will do cache flushing
> when
> >>>>>> needed.
> >>>>>>
> >>>>>> Adam, what driver is causing you that sort of problems ?
> >>>>>>
> >>>>>> Cheers,
> >>>>>> Ben.
> >>>>>>
> >>>>>>
> >>>>>>
> >>>
> >>>
> >>
> >>
> >
> >
>=20
>=20
> _______________________________________________
> Linuxppc-dev mailing list
> Linuxppc-dev@lists.ozlabs.org
> https://lists.ozlabs.org/listinfo/linuxppc-dev
^ permalink raw reply
* Re: AW: PowerPC PCI DMA issues (prefetch/coherency?)
From: Tom Burns @ 2009-09-10 19:53 UTC (permalink / raw)
To: lebon; +Cc: Prodyut Hazarika, Andrea Zypchen, linuxppc-dev, azilkie
In-Reply-To: <4AA7B7EA.2090500@lebon.org.ua>
Hi,
Thank you everyone for your help.
I've been looking into the other dma/pci API calls (dma_alloc_coherent,
pci_alloc_consistent). I don't see how either of these return memory
mapped to a TLB with the I bit set to 1 in kernel 2.6.24. In our kernel
code, the only use of the PPC44x_TLB_I define is in head_44x.S in
_start. We have CONFIG_NON_COHERENT_CACHE enabled.
We changed our code to use dma_alloc_coherent, removed our manual
cacheline flushing, and saw the corrupted data return. To me this means
dma_alloc_coherent cannot be setting the I=1 bit in the TLB entry.
I tried, using our JTAG debugger (BDI3000), to pause operation after
calling dma_alloc_coherent to examine the TLB entry for the memory
returned by the call (which was just past
CONFIG_CONSISTENT_START=0xff100000). The TLB list loaded at the time
that I paused operation did not show a mapping for this area. I guess
the kernel swaps TLB entries on the fly so it isn't limited to only 64
entries? I will try to sleep in the same context as the
dma_alloc_coherent call to try to catch the TLB entry while loaded to
see if it has the I bit set.
If that fails, any ideas?
Thanks,
Tom Burns
International Datacasting Corporation
Mikhail Zolotaryov wrote:
> Hi Tom,
>
> possible solution could be to use tasklet to perform DMA-related job
> (as in most cases DMA transfer is interrupt driven - makes sense).
>
>
> Tom Burns wrote:
>> Hi,
>>
>> With the default config for the Sequoia board on 2.6.24, calling
>> pci_dma_sync_sg_for_cpu() results in executing
>> invalidate_dcache_range() in arch/ppc/kernel/misc.S from
>> __dma_sync(). This OOPses on PPC440 since it tries to call directly
>> the assembly instruction dcbi, which can only be executed in
>> supervisor mode. We tried that before resorting to manual cache line
>> management with usermode-safe assembly calls.
>>
>> Regards,
>> Tom Burns
>> International Datacasting Corporation
>>
>> Mikhail Zolotaryov wrote:
>>> Hi,
>>>
>>> Why manage cache lines manually, if appropriate code is a part of
>>> __dma_sync / dma_sync_single_for_device of DMA API ? (implies
>>> CONFIG_NOT_COHERENT_CACHE enabled, as default for Sequoia Board)
>>>
>>> Prodyut Hazarika wrote:
>>>> Hi Adam,
>>>>
>>>>
>>>>> Yes, I am using the 440EPx (same as the sequoia board). Our
>>>>> ideDriver is DMA'ing blocks of 192-byte data over the PCI bus
>>>>>
>>>> (using
>>>>
>>>>> the Sil0680A PCI-IDE bridge). Most of the DMA's (depending on timing)
>>>>> end up being partially corrupted when we try to parse the data in the
>>>>> virtual page. We have confirmed the data is good before the PCI-IDE
>>>>> bridge. We are creating two 8K pages and map them to physical DMA
>>>>>
>>>> memory
>>>>
>>>>> using single-entry scatter/gather structs. When a DMA block is
>>>>> corrupted, we see a random portion of it (always a multiple of 16byte
>>>>> cache lines) is overwritten with old data from the last time the
>>>>>
>>>> buffer
>>>>
>>>>> was used.
>>>>
>>>> This looks like a cache coherency problem.
>>>> Can you ensure that the TLB entries corresponding to the DMA region
>>>> has
>>>> the CacheInhibit bit set.
>>>> You will need a BDI connected to your system.
>>>>
>>>> Also, you will need to invalidate and flush the lines appropriately,
>>>> since in 440 cores,
>>>> L1Cache coherency is managed entirely by software.
>>>> Please look at drivers/net/ibm_newemac/mal.c and core.c for example on
>>>> how to do it.
>>>>
>>>> Thanks
>>>> Prodyut
>>>>
>>>> On Thu, 2009-09-03 at 13:27 -0700, Prodyut Hazarika wrote:
>>>>
>>>>> Hi Adam,
>>>>>
>>>>>
>>>>>> Are you sure there is L2 cache on the 440?
>>>>>>
>>>>> It depends on the SoC you are using. SoC like 460EX (Canyonlands
>>>>>
>>>> board)
>>>>
>>>>> have L2Cache.
>>>>> It seems you are using a Sequoia board, which has a 440EPx SoC.
>>>>> 440EPx
>>>>> has a 440 cpu core, but no L2Cache.
>>>>> Could you please tell me which SoC you are using?
>>>>> You can also refer to the appropriate dts file to see if there is
>>>>> L2C.
>>>>> For example, in canyonlands.dts (460EX based board), we have the L2C
>>>>> entry.
>>>>> L2C0: l2c {
>>>>> ...
>>>>> }
>>>>>
>>>>>
>>>>>> I am seeing this problem with our custom IDE driver which is
>>>>>> based on
>>>>>>
>>>>
>>>>
>>>>>> pretty old code. Our driver uses pci_alloc_consistent() to allocate
>>>>>>
>>>> the
>>>>
>>>>>> physical DMA memory and alloc_pages() to allocate a virtual page.
>>>>>> It then uses pci_map_sg() to map to a scatter/gather buffer.
>>>>>> Perhaps I should convert these to the DMA API calls as you suggest.
>>>>>>
>>>>> Could you give more details on the consistency problem? It is a good
>>>>> idea to change to the new DMA APIs, but pci_alloc_consistent() should
>>>>> work too
>>>>>
>>>>> Thanks
>>>>> Prodyut On Thu, 2009-09-03 at 19:57 +1000, Benjamin Herrenschmidt
>>>>> wrote:
>>>>>
>>>>>> On Thu, 2009-09-03 at 09:05 +0100, Chris Pringle wrote:
>>>>>>
>>>>>>> Hi Adam,
>>>>>>>
>>>>>>> If you have a look in include/asm-ppc/pgtable.h for the following
>>>>>>>
>>>>> section:
>>>>>
>>>>>>> #ifdef CONFIG_44x
>>>>>>> #define _PAGE_BASE (_PAGE_PRESENT | _PAGE_ACCESSED |
>>>>>>>
>>>>> _PAGE_GUARDED)
>>>>>
>>>>>>> #else
>>>>>>> #define _PAGE_BASE (_PAGE_PRESENT | _PAGE_ACCESSED)
>>>>>>> #endif
>>>>>>>
>>>>>>> Try adding _PAGE_COHERENT to the appropriate line above and see if
>>>>>>>
>>>>> that
>>>>>>> fixes your issue - this causes the 'M' bit to be set on the page
>>>>>>>
>>>>> which
>>>>>>> sure enforce cache coherency. If it doesn't, you'll need to check
>>>>>>>
>>>>> the
>>>>>>> 'M' bit isn't being masked out in head_44x.S (it was originally
>>>>>>>
>>>>> masked
>>>>>>> out on arch/powerpc, but was fixed in later kernels when the cache
>>>>>>>
>>>>
>>>>
>>>>>>> coherency issues with non-SMP systems were resolved).
>>>>>>>
>>>>>> I have some doubts about the usefulness of doing that for 4xx.
>>>>>>
>>>> AFAIK,
>>>>
>>>>>> the 440 core just ignores M.
>>>>>>
>>>>>> The problem lies probably elsewhere. Maybe the L2 cache coherency
>>>>>>
>>>>> isn't
>>>>>
>>>>>> enabled or not working ?
>>>>>>
>>>>>> The L1 cache on 440 is simply not coherent, so drivers have to make
>>>>>>
>>>>> sure
>>>>>
>>>>>> they use the appropriate DMA APIs which will do cache flushing when
>>>>>> needed.
>>>>>>
>>>>>> Adam, what driver is causing you that sort of problems ?
>>>>>>
>>>>>> Cheers,
>>>>>> Ben.
>>>>>>
>>>>>>
>>>>>>
>>>
>>>
>>
>>
>
>
^ permalink raw reply
* Re: [PATCH v2 3/3] ucc_geth: Fix hangs after switching from full to half duplex
From: Anton Vorontsov @ 2009-09-10 19:40 UTC (permalink / raw)
To: Scott Wood; +Cc: linuxppc-dev, netdev, Andy Fleming, David Miller, Timur Tabi
In-Reply-To: <4AA93FB0.5060802@freescale.com>
On Thu, Sep 10, 2009 at 01:04:32PM -0500, Scott Wood wrote:
> Anton Vorontsov wrote:
> >MPC8360 QE UCC ethernet controllers hang when changing link duplex
> >under a load (a bit of NFS activity is enough).
> >
> > PHY: mdio@e0102120:00 - Link is Up - 1000/Full
> > sh-3.00# ethtool -s eth0 speed 100 duplex half autoneg off
> > PHY: mdio@e0102120:00 - Link is Down
> > PHY: mdio@e0102120:00 - Link is Up - 100/Half
> > NETDEV WATCHDOG: eth0 (ucc_geth): transmit queue 0 timed out
> > ------------[ cut here ]------------
> > Badness at c01fcbd0 [verbose debug info unavailable]
> > NIP: c01fcbd0 LR: c01fcbd0 CTR: c0194e44
> > ...
> >
> >The cure is to disable the controller before changing speed/duplex
> >and enable it afterwards.
> >
> >Since ugeth_graceful_stop_{tx,rx} now may be called from an atomic
> >context, switch the two functions from msleep() to mdelay().
>
> Ouch.
Yeah, right... delaying for 10ms with irqs off isn't good.
> Can we put this in a workqueue or something?
adjust_link() itself isn't called from an atomic context.
It's we are grabbing ugeth->lock, i.e. a spinlock. I don't see
why the lock is needed in adjust_link() in its current form,
but if we're going to disable the controller for some time,
we'll have to make sure that no start_xmit() or NAPI is running,
scheduled or will be scheduled until we say so.
I think that lock-less, and thus completely sleep-able variant
of adjust_link is doable.
Thanks,
--
Anton Vorontsov
email: cbouatmailru@gmail.com
irc://irc.freenode.net/bd2
^ permalink raw reply
* Re: [PATCH v2 3/3] ucc_geth: Fix hangs after switching from full to half duplex
From: Scott Wood @ 2009-09-10 18:04 UTC (permalink / raw)
To: avorontsov; +Cc: linuxppc-dev, netdev, Andy Fleming, David Miller, Timur Tabi
In-Reply-To: <20090910175852.GA18948@oksana.dev.rtsoft.ru>
Anton Vorontsov wrote:
> MPC8360 QE UCC ethernet controllers hang when changing link duplex
> under a load (a bit of NFS activity is enough).
>
> PHY: mdio@e0102120:00 - Link is Up - 1000/Full
> sh-3.00# ethtool -s eth0 speed 100 duplex half autoneg off
> PHY: mdio@e0102120:00 - Link is Down
> PHY: mdio@e0102120:00 - Link is Up - 100/Half
> NETDEV WATCHDOG: eth0 (ucc_geth): transmit queue 0 timed out
> ------------[ cut here ]------------
> Badness at c01fcbd0 [verbose debug info unavailable]
> NIP: c01fcbd0 LR: c01fcbd0 CTR: c0194e44
> ...
>
> The cure is to disable the controller before changing speed/duplex
> and enable it afterwards.
>
> Since ugeth_graceful_stop_{tx,rx} now may be called from an atomic
> context, switch the two functions from msleep() to mdelay().
Ouch. Can we put this in a workqueue or something?
-Scott
^ permalink raw reply
* [PATCH v2 3/3] ucc_geth: Fix hangs after switching from full to half duplex
From: Anton Vorontsov @ 2009-09-10 17:58 UTC (permalink / raw)
To: David Miller; +Cc: netdev, Andy Fleming, Timur Tabi, linuxppc-dev
In-Reply-To: <20090910020145.GC31083@oksana.dev.rtsoft.ru>
MPC8360 QE UCC ethernet controllers hang when changing link duplex
under a load (a bit of NFS activity is enough).
PHY: mdio@e0102120:00 - Link is Up - 1000/Full
sh-3.00# ethtool -s eth0 speed 100 duplex half autoneg off
PHY: mdio@e0102120:00 - Link is Down
PHY: mdio@e0102120:00 - Link is Up - 100/Half
NETDEV WATCHDOG: eth0 (ucc_geth): transmit queue 0 timed out
------------[ cut here ]------------
Badness at c01fcbd0 [verbose debug info unavailable]
NIP: c01fcbd0 LR: c01fcbd0 CTR: c0194e44
...
The cure is to disable the controller before changing speed/duplex
and enable it afterwards.
Since ugeth_graceful_stop_{tx,rx} now may be called from an atomic
context, switch the two functions from msleep() to mdelay().
Signed-off-by: Anton Vorontsov <avorontsov@ru.mvista.com>
---
v2: Switch ugeth_graceful_stop_{tx,rx} to mdelay. Noticed with
CONFIG_DEBUG_SPINLOCK_SLEEP.
drivers/net/ucc_geth.c | 8 ++++++--
1 files changed, 6 insertions(+), 2 deletions(-)
diff --git a/drivers/net/ucc_geth.c b/drivers/net/ucc_geth.c
index 2a2c973..5a0803f 100644
--- a/drivers/net/ucc_geth.c
+++ b/drivers/net/ucc_geth.c
@@ -1432,7 +1432,7 @@ static int ugeth_graceful_stop_tx(struct ucc_geth_private *ugeth)
/* Wait for command to complete */
do {
- msleep(10);
+ mdelay(10);
temp = in_be32(uccf->p_ucce);
} while (!(temp & UCC_GETH_UCCE_GRA) && --i);
@@ -1464,7 +1464,7 @@ static int ugeth_graceful_stop_rx(struct ucc_geth_private *ugeth)
ucc_num);
qe_issue_cmd(QE_GRACEFUL_STOP_RX, cecr_subblock,
QE_CR_PROTOCOL_ETHERNET, 0);
- msleep(10);
+ mdelay(10);
temp = in_8(&ugeth->p_rx_glbl_pram->rxgstpack);
} while (!(temp & GRACEFUL_STOP_ACKNOWLEDGE_RX) && --i);
@@ -1631,9 +1631,13 @@ static void adjust_link(struct net_device *dev)
ugeth->oldspeed = phydev->speed;
}
+ ugeth_disable(ugeth, COMM_DIR_RX_AND_TX);
+
out_be32(&ug_regs->maccfg2, tempval);
out_be32(&uf_regs->upsmr, upsmr);
+ ugeth_enable(ugeth, COMM_DIR_RX_AND_TX);
+
if (!ugeth->oldlink) {
new_state = 1;
ugeth->oldlink = 1;
--
1.6.3.3
^ permalink raw reply related
* Re: Debian on MPC8572DS
From: Kumar Gala @ 2009-09-10 17:40 UTC (permalink / raw)
To: Isaac Gomez Morales; +Cc: linuxppc-dev
In-Reply-To: <38a5a2d10909100801x11049cddi1799c0ed6ee44410@mail.gmail.com>
Do you have floating point emulation turned on in the kernel?
- k
On Sep 10, 2009, at 10:01 AM, Isaac Gomez Morales wrote:
> Hello,
>
> I'm trying to get a Linux distro such as Debian in the following
> system
>
> System:
> mpc8572ds HW
> u-boot programmed on flash memory
> vanillia linux installed on sata disk
>
> Restrictions:
> i have not apt-get resident on system
>
>
> I have tried the following ways with no succeful results:
>
> I tried to run deboostrap with chroot on mpc8572ds
>
> I tried to run deboostrap on another machine, downloading the
> correct packages for ppc enviroment and copiny those packages in a
> new ext2 partition of my 8572 hw. After that i executed "chroot /mnt/
> newpartition /bin/bash" and it returned "Illegal instruction". At
> this point i can't follow the installation and if i force to startup
> the machine with that rf system, it returns a "kernel panic" with
> error to handling the signal 4 of init process.
>
> I tried to get start the machine with a debain livecd installation.
> I created an uImage and initrd from cdrom's vmlinux and initrd.gz
> with the uboot-mkimage tool, then i booted it from u-boot but the
> kernel didn't start.
>
> I tried to start a preinstalled Debian for a simillar architecture,
> in cocnret from a mpc8561 ppc's Debian. I put its hhdd with the
> Debian rfs on my machine and start it with my uImage and dtb files.
> The results was the same "kernel panic" and signal 4 error.
>
>
>
> I accept all ideas.
>
> Thanks you all and sorry for my english
>
>
> _______________________________________________
> Linuxppc-dev mailing list
> Linuxppc-dev@lists.ozlabs.org
> https://lists.ozlabs.org/listinfo/linuxppc-dev
^ permalink raw reply
* How to access DCR registers in powerpc440gx? Got err when use macro def in Linux kernel
From: g r1x @ 2009-09-10 17:24 UTC (permalink / raw)
To: Linuxppc-dev
[-- Attachment #1: Type: text/plain, Size: 2797 bytes --]
Now, I'm writing a DMA driver on powerpc
440gx platform(2.6.26.5), as the only way to set up DMA Controller is
to access it's dcr registers with 'mfdcr' and 'mtdcr'.
I've found some dma code in Linux kernel 2.6.26.5, so I copy the code
u wrote to my driver module directory, and include them, but when I
compile my driver, gcc complains following err messages:
--------------------------------------------------------
{standard input}: Assembler messages:
{standard input}:83: Error: unsupported relocation against dmanr
---------------------------------------------------------
code I copy from kernel 2.6.26.5 (arch/ppc/syslib/ppc4xx_dma.c)
--------------------------------------------
#include "dcr.h"
/* #inlcude <asm/dcr-native.h> */
ppc_dma_ch_t dma_channels[MAX_PPC4xx_DMA_CHANNELS];
int ppc4xx_get_dma_status(void)
{
return (mfdcr(DCRN_DMASR));
}
void ppc4xx_enable_dma(unsigned int dmanr)
{
unsigned int control;
ppc_dma_ch_t *p_dma_ch = &dma_channels[dmanr];
.........
/ * for other xfer modes, the addresses are already set */
control = mfdcr(DCRN_DMACR0 + (dmanr * 0x8)); <----------------err
}
void ppc4xx_set_src_addr(int dmanr, phys_addr_t src_addr)
{
if (dmanr >= MAX_PPC4xx_DMA_CHANNELS) {
printk("set_src_addr: bad channel: %d\n", dmanr);
return;
}
#ifdef PPC4xx_DMA_64BIT
mtdcr(DCRN_DMASAH0 + dmanr*2, (u32)(src_addr >> 32));
#else
mtdcr(DCRN_DMASA0 + dmanr*2, (u32)src_addr);
#endif
}
--------------------------------------------------
DCR access micro I copied:
----------------------------------
#define mfdcr(rn) \
({ \
unsigned long rval; \
asm volatile("mfdcr %0,%1" : "=r"(rval) : "i"(rn)); \
rval; \
})
#define mtdcr(rn, val) \
asm volatile("mtdcr %0,%1" : : "i"(rn), "r"(val))
-----------------------------------------------------
Makefile I worte
-------------------------
obj-m := dmatest.o
dmatest-objs += dma.o core.o
KBUILD_CFLAGS += -m440 -mregnames -Wa, -booke
KBUILD_ASFAGS += -m440 -mregnames -Wa, -booke
---------
KBUILD_CFLAGS += -m440 -mregnames -Wa, -booke
KBUILD_ASFAGS += -m440 -mregnames -Wa, -booke
I add these two sentences when I read these
http://sourceware.org/ml/binutils/2004-09/msg00161.html.
When I change it to mfdcr(DCRN_DMACR0); then everything is fine. but
from the result I got when I insmod my module driver, dcr reg is not
changed.
It seems that it's the problem with gnu assembler, my GNU assembler ver is 2.17
The attachments are the c source file I copy from linux 2.6.26.5, and
I use these dma function to directly manipulate dcr registers, but
when I make my driver module outside kernel source tree, it complains
the error I mentioned above.
Thanks!
[-- Attachment #2: ppc4xx_dma.c --]
[-- Type: application/octet-stream, Size: 18648 bytes --]
/*
* IBM PPC4xx DMA engine core library
*
* Copyright 2000-2004 MontaVista Software Inc.
*
* Cleaned up and converted to new DCR access
* Matt Porter <mporter@kernel.crashing.org>
*
* Original code by Armin Kuster <akuster@mvista.com>
* and Pete Popov <ppopov@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
* option) any later version.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 675 Mass Ave, Cambridge, MA 02139, USA.
*/
#include <linux/kernel.h>
#include <linux/mm.h>
#include <linux/miscdevice.h>
#include <linux/init.h>
#include <linux/module.h>
#include <asm/system.h>
#include <asm/io.h>
#include <asm/dma.h>
#include <asm/ppc4xx_dma.h>
ppc_dma_ch_t dma_channels[MAX_PPC4xx_DMA_CHANNELS];
int
ppc4xx_get_dma_status(void)
{
return (mfdcr(DCRN_DMASR));
}
void
ppc4xx_set_src_addr(int dmanr, phys_addr_t src_addr)
{
if (dmanr >= MAX_PPC4xx_DMA_CHANNELS) {
printk("set_src_addr: bad channel: %d\n", dmanr);
return;
}
#ifdef PPC4xx_DMA_64BIT
mtdcr(DCRN_DMASAH0 + dmanr*2, (u32)(src_addr >> 32));
#else
mtdcr(DCRN_DMASA0 + dmanr*2, (u32)src_addr);
#endif
}
void
ppc4xx_set_dst_addr(int dmanr, phys_addr_t dst_addr)
{
if (dmanr >= MAX_PPC4xx_DMA_CHANNELS) {
printk("set_dst_addr: bad channel: %d\n", dmanr);
return;
}
#ifdef PPC4xx_DMA_64BIT
mtdcr(DCRN_DMADAH0 + dmanr*2, (u32)(dst_addr >> 32));
#else
mtdcr(DCRN_DMADA0 + dmanr*2, (u32)dst_addr);
#endif
}
void
ppc4xx_enable_dma(unsigned int dmanr)
{
unsigned int control;
ppc_dma_ch_t *p_dma_ch = &dma_channels[dmanr];
unsigned int status_bits[] = { DMA_CS0 | DMA_TS0 | DMA_CH0_ERR,
DMA_CS1 | DMA_TS1 | DMA_CH1_ERR,
DMA_CS2 | DMA_TS2 | DMA_CH2_ERR,
DMA_CS3 | DMA_TS3 | DMA_CH3_ERR};
if (p_dma_ch->in_use) {
printk("enable_dma: channel %d in use\n", dmanr);
return;
}
if (dmanr >= MAX_PPC4xx_DMA_CHANNELS) {
printk("enable_dma: bad channel: %d\n", dmanr);
return;
}
if (p_dma_ch->mode == DMA_MODE_READ) {
/* peripheral to memory */
ppc4xx_set_src_addr(dmanr, 0);
ppc4xx_set_dst_addr(dmanr, p_dma_ch->addr);
} else if (p_dma_ch->mode == DMA_MODE_WRITE) {
/* memory to peripheral */
ppc4xx_set_src_addr(dmanr, p_dma_ch->addr);
ppc4xx_set_dst_addr(dmanr, 0);
}
/* for other xfer modes, the addresses are already set */
control = mfdcr(DCRN_DMACR0 + (dmanr * 0x8));
control &= ~(DMA_TM_MASK | DMA_TD); /* clear all mode bits */
if (p_dma_ch->mode == DMA_MODE_MM) {
/* software initiated memory to memory */
control |= DMA_ETD_OUTPUT | DMA_TCE_ENABLE;
}
mtdcr(DCRN_DMACR0 + (dmanr * 0x8), control);
/*
* Clear the CS, TS, RI bits for the channel from DMASR. This
* has been observed to happen correctly only after the mode and
* ETD/DCE bits in DMACRx are set above. Must do this before
* enabling the channel.
*/
mtdcr(DCRN_DMASR, status_bits[dmanr]);
/*
* For device-paced transfers, Terminal Count Enable apparently
* must be on, and this must be turned on after the mode, etc.
* bits are cleared above (at least on Redwood-6).
*/
if ((p_dma_ch->mode == DMA_MODE_MM_DEVATDST) ||
(p_dma_ch->mode == DMA_MODE_MM_DEVATSRC))
control |= DMA_TCE_ENABLE;
/*
* Now enable the channel.
*/
control |= (p_dma_ch->mode | DMA_CE_ENABLE);
mtdcr(DCRN_DMACR0 + (dmanr * 0x8), control);
p_dma_ch->in_use = 1;
}
void
ppc4xx_disable_dma(unsigned int dmanr)
{
unsigned int control;
ppc_dma_ch_t *p_dma_ch = &dma_channels[dmanr];
if (!p_dma_ch->in_use) {
printk("disable_dma: channel %d not in use\n", dmanr);
return;
}
if (dmanr >= MAX_PPC4xx_DMA_CHANNELS) {
printk("disable_dma: bad channel: %d\n", dmanr);
return;
}
control = mfdcr(DCRN_DMACR0 + (dmanr * 0x8));
control &= ~DMA_CE_ENABLE;
mtdcr(DCRN_DMACR0 + (dmanr * 0x8), control);
p_dma_ch->in_use = 0;
}
/*
* Sets the dma mode for single DMA transfers only.
* For scatter/gather transfers, the mode is passed to the
* alloc_dma_handle() function as one of the parameters.
*
* The mode is simply saved and used later. This allows
* the driver to call set_dma_mode() and set_dma_addr() in
* any order.
*
* Valid mode values are:
*
* DMA_MODE_READ peripheral to memory
* DMA_MODE_WRITE memory to peripheral
* DMA_MODE_MM memory to memory
* DMA_MODE_MM_DEVATSRC device-paced memory to memory, device at src
* DMA_MODE_MM_DEVATDST device-paced memory to memory, device at dst
*/
int
ppc4xx_set_dma_mode(unsigned int dmanr, unsigned int mode)
{
ppc_dma_ch_t *p_dma_ch = &dma_channels[dmanr];
if (dmanr >= MAX_PPC4xx_DMA_CHANNELS) {
printk("set_dma_mode: bad channel 0x%x\n", dmanr);
return DMA_STATUS_BAD_CHANNEL;
}
p_dma_ch->mode = mode;
return DMA_STATUS_GOOD;
}
/*
* Sets the DMA Count register. Note that 'count' is in bytes.
* However, the DMA Count register counts the number of "transfers",
* where each transfer is equal to the bus width. Thus, count
* MUST be a multiple of the bus width.
*/
void
ppc4xx_set_dma_count(unsigned int dmanr, unsigned int count)
{
ppc_dma_ch_t *p_dma_ch = &dma_channels[dmanr];
#ifdef DEBUG_4xxDMA
{
int error = 0;
switch (p_dma_ch->pwidth) {
case PW_8:
break;
case PW_16:
if (count & 0x1)
error = 1;
break;
case PW_32:
if (count & 0x3)
error = 1;
break;
case PW_64:
if (count & 0x7)
error = 1;
break;
default:
printk("set_dma_count: invalid bus width: 0x%x\n",
p_dma_ch->pwidth);
return;
}
if (error)
printk
("Warning: set_dma_count count 0x%x bus width %d\n",
count, p_dma_ch->pwidth);
}
#endif
count = count >> p_dma_ch->shift;
mtdcr(DCRN_DMACT0 + (dmanr * 0x8), count);
}
/*
* Returns the number of bytes left to be transferred.
* After a DMA transfer, this should return zero.
* Reading this while a DMA transfer is still in progress will return
* unpredictable results.
*/
int
ppc4xx_get_dma_residue(unsigned int dmanr)
{
unsigned int count;
ppc_dma_ch_t *p_dma_ch = &dma_channels[dmanr];
if (dmanr >= MAX_PPC4xx_DMA_CHANNELS) {
printk("ppc4xx_get_dma_residue: bad channel 0x%x\n", dmanr);
return DMA_STATUS_BAD_CHANNEL;
}
count = mfdcr(DCRN_DMACT0 + (dmanr * 0x8));
return (count << p_dma_ch->shift);
}
/*
* Sets the DMA address for a memory to peripheral or peripheral
* to memory transfer. The address is just saved in the channel
* structure for now and used later in enable_dma().
*/
void
ppc4xx_set_dma_addr(unsigned int dmanr, phys_addr_t addr)
{
ppc_dma_ch_t *p_dma_ch = &dma_channels[dmanr];
if (dmanr >= MAX_PPC4xx_DMA_CHANNELS) {
printk("ppc4xx_set_dma_addr: bad channel: %d\n", dmanr);
return;
}
#ifdef DEBUG_4xxDMA
{
int error = 0;
switch (p_dma_ch->pwidth) {
case PW_8:
break;
case PW_16:
if ((unsigned) addr & 0x1)
error = 1;
break;
case PW_32:
if ((unsigned) addr & 0x3)
error = 1;
break;
case PW_64:
if ((unsigned) addr & 0x7)
error = 1;
break;
default:
printk("ppc4xx_set_dma_addr: invalid bus width: 0x%x\n",
p_dma_ch->pwidth);
return;
}
if (error)
printk("Warning: ppc4xx_set_dma_addr addr 0x%x bus width %d\n",
addr, p_dma_ch->pwidth);
}
#endif
/* save dma address and program it later after we know the xfer mode */
p_dma_ch->addr = addr;
}
/*
* Sets both DMA addresses for a memory to memory transfer.
* For memory to peripheral or peripheral to memory transfers
* the function set_dma_addr() should be used instead.
*/
void
ppc4xx_set_dma_addr2(unsigned int dmanr, phys_addr_t src_dma_addr,
phys_addr_t dst_dma_addr)
{
if (dmanr >= MAX_PPC4xx_DMA_CHANNELS) {
printk("ppc4xx_set_dma_addr2: bad channel: %d\n", dmanr);
return;
}
#ifdef DEBUG_4xxDMA
{
ppc_dma_ch_t *p_dma_ch = &dma_channels[dmanr];
int error = 0;
switch (p_dma_ch->pwidth) {
case PW_8:
break;
case PW_16:
if (((unsigned) src_dma_addr & 0x1) ||
((unsigned) dst_dma_addr & 0x1)
)
error = 1;
break;
case PW_32:
if (((unsigned) src_dma_addr & 0x3) ||
((unsigned) dst_dma_addr & 0x3)
)
error = 1;
break;
case PW_64:
if (((unsigned) src_dma_addr & 0x7) ||
((unsigned) dst_dma_addr & 0x7)
)
error = 1;
break;
default:
printk("ppc4xx_set_dma_addr2: invalid bus width: 0x%x\n",
p_dma_ch->pwidth);
return;
}
if (error)
printk
("Warning: ppc4xx_set_dma_addr2 src 0x%x dst 0x%x bus width %d\n",
src_dma_addr, dst_dma_addr, p_dma_ch->pwidth);
}
#endif
ppc4xx_set_src_addr(dmanr, src_dma_addr);
ppc4xx_set_dst_addr(dmanr, dst_dma_addr);
}
/*
* Enables the channel interrupt.
*
* If performing a scatter/gatter transfer, this function
* MUST be called before calling alloc_dma_handle() and building
* the sgl list. Otherwise, interrupts will not be enabled, if
* they were previously disabled.
*/
int
ppc4xx_enable_dma_interrupt(unsigned int dmanr)
{
unsigned int control;
ppc_dma_ch_t *p_dma_ch = &dma_channels[dmanr];
if (dmanr >= MAX_PPC4xx_DMA_CHANNELS) {
printk("ppc4xx_enable_dma_interrupt: bad channel: %d\n", dmanr);
return DMA_STATUS_BAD_CHANNEL;
}
p_dma_ch->int_enable = 1;
control = mfdcr(DCRN_DMACR0 + (dmanr * 0x8));
control |= DMA_CIE_ENABLE; /* Channel Interrupt Enable */
mtdcr(DCRN_DMACR0 + (dmanr * 0x8), control);
return DMA_STATUS_GOOD;
}
/*
* Disables the channel interrupt.
*
* If performing a scatter/gatter transfer, this function
* MUST be called before calling alloc_dma_handle() and building
* the sgl list. Otherwise, interrupts will not be disabled, if
* they were previously enabled.
*/
int
ppc4xx_disable_dma_interrupt(unsigned int dmanr)
{
unsigned int control;
ppc_dma_ch_t *p_dma_ch = &dma_channels[dmanr];
if (dmanr >= MAX_PPC4xx_DMA_CHANNELS) {
printk("ppc4xx_disable_dma_interrupt: bad channel: %d\n", dmanr);
return DMA_STATUS_BAD_CHANNEL;
}
p_dma_ch->int_enable = 0;
control = mfdcr(DCRN_DMACR0 + (dmanr * 0x8));
control &= ~DMA_CIE_ENABLE; /* Channel Interrupt Enable */
mtdcr(DCRN_DMACR0 + (dmanr * 0x8), control);
return DMA_STATUS_GOOD;
}
/*
* Configures a DMA channel, including the peripheral bus width, if a
* peripheral is attached to the channel, the polarity of the DMAReq and
* DMAAck signals, etc. This information should really be setup by the boot
* code, since most likely the configuration won't change dynamically.
* If the kernel has to call this function, it's recommended that it's
* called from platform specific init code. The driver should not need to
* call this function.
*/
int
ppc4xx_init_dma_channel(unsigned int dmanr, ppc_dma_ch_t * p_init)
{
unsigned int polarity;
uint32_t control = 0;
ppc_dma_ch_t *p_dma_ch = &dma_channels[dmanr];
DMA_MODE_READ = (unsigned long) DMA_TD; /* Peripheral to Memory */
DMA_MODE_WRITE = 0; /* Memory to Peripheral */
if (!p_init) {
printk("ppc4xx_init_dma_channel: NULL p_init\n");
return DMA_STATUS_NULL_POINTER;
}
if (dmanr >= MAX_PPC4xx_DMA_CHANNELS) {
printk("ppc4xx_init_dma_channel: bad channel %d\n", dmanr);
return DMA_STATUS_BAD_CHANNEL;
}
#if DCRN_POL > 0
polarity = mfdcr(DCRN_POL);
#else
polarity = 0;
#endif
/* Setup the control register based on the values passed to
* us in p_init. Then, over-write the control register with this
* new value.
*/
control |= SET_DMA_CONTROL;
/* clear all polarity signals and then "or" in new signal levels */
polarity &= ~GET_DMA_POLARITY(dmanr);
polarity |= p_init->polarity;
#if DCRN_POL > 0
mtdcr(DCRN_POL, polarity);
#endif
mtdcr(DCRN_DMACR0 + (dmanr * 0x8), control);
/* save these values in our dma channel structure */
memcpy(p_dma_ch, p_init, sizeof (ppc_dma_ch_t));
/*
* The peripheral width values written in the control register are:
* PW_8 0
* PW_16 1
* PW_32 2
* PW_64 3
*
* Since the DMA count register takes the number of "transfers",
* we need to divide the count sent to us in certain
* functions by the appropriate number. It so happens that our
* right shift value is equal to the peripheral width value.
*/
p_dma_ch->shift = p_init->pwidth;
/*
* Save the control word for easy access.
*/
p_dma_ch->control = control;
mtdcr(DCRN_DMASR, 0xffffffff); /* clear status register */
return DMA_STATUS_GOOD;
}
/*
* This function returns the channel configuration.
*/
int
ppc4xx_get_channel_config(unsigned int dmanr, ppc_dma_ch_t * p_dma_ch)
{
unsigned int polarity;
unsigned int control;
if (dmanr >= MAX_PPC4xx_DMA_CHANNELS) {
printk("ppc4xx_get_channel_config: bad channel %d\n", dmanr);
return DMA_STATUS_BAD_CHANNEL;
}
memcpy(p_dma_ch, &dma_channels[dmanr], sizeof (ppc_dma_ch_t));
#if DCRN_POL > 0
polarity = mfdcr(DCRN_POL);
#else
polarity = 0;
#endif
p_dma_ch->polarity = polarity & GET_DMA_POLARITY(dmanr);
control = mfdcr(DCRN_DMACR0 + (dmanr * 0x8));
p_dma_ch->cp = GET_DMA_PRIORITY(control);
p_dma_ch->pwidth = GET_DMA_PW(control);
p_dma_ch->psc = GET_DMA_PSC(control);
p_dma_ch->pwc = GET_DMA_PWC(control);
p_dma_ch->phc = GET_DMA_PHC(control);
p_dma_ch->ce = GET_DMA_CE_ENABLE(control);
p_dma_ch->int_enable = GET_DMA_CIE_ENABLE(control);
p_dma_ch->shift = GET_DMA_PW(control);
#ifdef CONFIG_PPC4xx_EDMA
p_dma_ch->pf = GET_DMA_PREFETCH(control);
#else
p_dma_ch->ch_enable = GET_DMA_CH(control);
p_dma_ch->ece_enable = GET_DMA_ECE(control);
p_dma_ch->tcd_disable = GET_DMA_TCD(control);
#endif
return DMA_STATUS_GOOD;
}
/*
* Sets the priority for the DMA channel dmanr.
* Since this is setup by the hardware init function, this function
* can be used to dynamically change the priority of a channel.
*
* Acceptable priorities:
*
* PRIORITY_LOW
* PRIORITY_MID_LOW
* PRIORITY_MID_HIGH
* PRIORITY_HIGH
*
*/
int
ppc4xx_set_channel_priority(unsigned int dmanr, unsigned int priority)
{
unsigned int control;
if (dmanr >= MAX_PPC4xx_DMA_CHANNELS) {
printk("ppc4xx_set_channel_priority: bad channel %d\n", dmanr);
return DMA_STATUS_BAD_CHANNEL;
}
if ((priority != PRIORITY_LOW) &&
(priority != PRIORITY_MID_LOW) &&
(priority != PRIORITY_MID_HIGH) && (priority != PRIORITY_HIGH)) {
printk("ppc4xx_set_channel_priority: bad priority: 0x%x\n", priority);
}
control = mfdcr(DCRN_DMACR0 + (dmanr * 0x8));
control |= SET_DMA_PRIORITY(priority);
mtdcr(DCRN_DMACR0 + (dmanr * 0x8), control);
return DMA_STATUS_GOOD;
}
/*
* Returns the width of the peripheral attached to this channel. This assumes
* that someone who knows the hardware configuration, boot code or some other
* init code, already set the width.
*
* The return value is one of:
* PW_8
* PW_16
* PW_32
* PW_64
*
* The function returns 0 on error.
*/
unsigned int
ppc4xx_get_peripheral_width(unsigned int dmanr)
{
unsigned int control;
if (dmanr >= MAX_PPC4xx_DMA_CHANNELS) {
printk("ppc4xx_get_peripheral_width: bad channel %d\n", dmanr);
return DMA_STATUS_BAD_CHANNEL;
}
control = mfdcr(DCRN_DMACR0 + (dmanr * 0x8));
return (GET_DMA_PW(control));
}
/*
* Clears the channel status bits
*/
int
ppc4xx_clr_dma_status(unsigned int dmanr)
{
if (dmanr >= MAX_PPC4xx_DMA_CHANNELS) {
printk(KERN_ERR "ppc4xx_clr_dma_status: bad channel: %d\n", dmanr);
return DMA_STATUS_BAD_CHANNEL;
}
mtdcr(DCRN_DMASR, ((u32)DMA_CH0_ERR | (u32)DMA_CS0 | (u32)DMA_TS0) >> dmanr);
return DMA_STATUS_GOOD;
}
#ifdef CONFIG_PPC4xx_EDMA
/*
* Enables the burst on the channel (BTEN bit in the control/count register)
* Note:
* For scatter/gather dma, this function MUST be called before the
* ppc4xx_alloc_dma_handle() func as the chan count register is copied into the
* sgl list and used as each sgl element is added.
*/
int
ppc4xx_enable_burst(unsigned int dmanr)
{
unsigned int ctc;
if (dmanr >= MAX_PPC4xx_DMA_CHANNELS) {
printk(KERN_ERR "ppc4xx_enable_burst: bad channel: %d\n", dmanr);
return DMA_STATUS_BAD_CHANNEL;
}
ctc = mfdcr(DCRN_DMACT0 + (dmanr * 0x8)) | DMA_CTC_BTEN;
mtdcr(DCRN_DMACT0 + (dmanr * 0x8), ctc);
return DMA_STATUS_GOOD;
}
/*
* Disables the burst on the channel (BTEN bit in the control/count register)
* Note:
* For scatter/gather dma, this function MUST be called before the
* ppc4xx_alloc_dma_handle() func as the chan count register is copied into the
* sgl list and used as each sgl element is added.
*/
int
ppc4xx_disable_burst(unsigned int dmanr)
{
unsigned int ctc;
if (dmanr >= MAX_PPC4xx_DMA_CHANNELS) {
printk(KERN_ERR "ppc4xx_disable_burst: bad channel: %d\n", dmanr);
return DMA_STATUS_BAD_CHANNEL;
}
ctc = mfdcr(DCRN_DMACT0 + (dmanr * 0x8)) &~ DMA_CTC_BTEN;
mtdcr(DCRN_DMACT0 + (dmanr * 0x8), ctc);
return DMA_STATUS_GOOD;
}
/*
* Sets the burst size (number of peripheral widths) for the channel
* (BSIZ bits in the control/count register))
* must be one of:
* DMA_CTC_BSIZ_2
* DMA_CTC_BSIZ_4
* DMA_CTC_BSIZ_8
* DMA_CTC_BSIZ_16
* Note:
* For scatter/gather dma, this function MUST be called before the
* ppc4xx_alloc_dma_handle() func as the chan count register is copied into the
* sgl list and used as each sgl element is added.
*/
int
ppc4xx_set_burst_size(unsigned int dmanr, unsigned int bsize)
{
unsigned int ctc;
if (dmanr >= MAX_PPC4xx_DMA_CHANNELS) {
printk(KERN_ERR "ppc4xx_set_burst_size: bad channel: %d\n", dmanr);
return DMA_STATUS_BAD_CHANNEL;
}
ctc = mfdcr(DCRN_DMACT0 + (dmanr * 0x8)) &~ DMA_CTC_BSIZ_MSK;
ctc |= (bsize & DMA_CTC_BSIZ_MSK);
mtdcr(DCRN_DMACT0 + (dmanr * 0x8), ctc);
return DMA_STATUS_GOOD;
}
EXPORT_SYMBOL(ppc4xx_enable_burst);
EXPORT_SYMBOL(ppc4xx_disable_burst);
EXPORT_SYMBOL(ppc4xx_set_burst_size);
#endif /* CONFIG_PPC4xx_EDMA */
EXPORT_SYMBOL(ppc4xx_init_dma_channel);
EXPORT_SYMBOL(ppc4xx_get_channel_config);
EXPORT_SYMBOL(ppc4xx_set_channel_priority);
EXPORT_SYMBOL(ppc4xx_get_peripheral_width);
EXPORT_SYMBOL(dma_channels);
EXPORT_SYMBOL(ppc4xx_set_src_addr);
EXPORT_SYMBOL(ppc4xx_set_dst_addr);
EXPORT_SYMBOL(ppc4xx_set_dma_addr);
EXPORT_SYMBOL(ppc4xx_set_dma_addr2);
EXPORT_SYMBOL(ppc4xx_enable_dma);
EXPORT_SYMBOL(ppc4xx_disable_dma);
EXPORT_SYMBOL(ppc4xx_set_dma_mode);
EXPORT_SYMBOL(ppc4xx_set_dma_count);
EXPORT_SYMBOL(ppc4xx_get_dma_residue);
EXPORT_SYMBOL(ppc4xx_enable_dma_interrupt);
EXPORT_SYMBOL(ppc4xx_disable_dma_interrupt);
EXPORT_SYMBOL(ppc4xx_get_dma_status);
EXPORT_SYMBOL(ppc4xx_clr_dma_status);
[-- Attachment #3: dcr-native.h --]
[-- Type: application/octet-stream, Size: 3320 bytes --]
/*
* (c) Copyright 2006 Benjamin Herrenschmidt, IBM Corp.
* <benh@kernel.crashing.org>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See
* the GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#ifndef _ASM_POWERPC_DCR_NATIVE_H
#define _ASM_POWERPC_DCR_NATIVE_H
#ifdef __KERNEL__
#ifndef __ASSEMBLY__
#include <linux/spinlock.h>
typedef struct {
unsigned int base;
} dcr_host_t;
#define DCR_MAP_OK(host) (1)
#define dcr_map(dev, dcr_n, dcr_c) ((dcr_host_t){ .base = (dcr_n) })
#define dcr_unmap(host, dcr_c) do {} while (0)
#define dcr_read(host, dcr_n) mfdcr(dcr_n + host.base)
#define dcr_write(host, dcr_n, value) mtdcr(dcr_n + host.base, value)
/* Device Control Registers */
void __mtdcr(int reg, unsigned int val);
unsigned int __mfdcr(int reg);
#define mfdcr(rn) \
({unsigned int rval; \
if (__builtin_constant_p(rn)) \
asm volatile("mfdcr %0," __stringify(rn) \
: "=r" (rval)); \
else \
rval = __mfdcr(rn); \
rval;})
#define mtdcr(rn, v) \
do { \
if (__builtin_constant_p(rn)) \
asm volatile("mtdcr " __stringify(rn) ",%0" \
: : "r" (v)); \
else \
__mtdcr(rn, v); \
} while (0)
/* R/W of indirect DCRs make use of standard naming conventions for DCRs */
extern spinlock_t dcr_ind_lock;
static inline unsigned __mfdcri(int base_addr, int base_data, int reg)
{
unsigned long flags;
unsigned int val;
spin_lock_irqsave(&dcr_ind_lock, flags);
__mtdcr(base_addr, reg);
val = __mfdcr(base_data);
spin_unlock_irqrestore(&dcr_ind_lock, flags);
return val;
}
static inline void __mtdcri(int base_addr, int base_data, int reg,
unsigned val)
{
unsigned long flags;
spin_lock_irqsave(&dcr_ind_lock, flags);
__mtdcr(base_addr, reg);
__mtdcr(base_data, val);
spin_unlock_irqrestore(&dcr_ind_lock, flags);
}
static inline void __dcri_clrset(int base_addr, int base_data, int reg,
unsigned clr, unsigned set)
{
unsigned long flags;
unsigned int val;
spin_lock_irqsave(&dcr_ind_lock, flags);
__mtdcr(base_addr, reg);
val = (__mfdcr(base_data) & ~clr) | set;
__mtdcr(base_data, val);
spin_unlock_irqrestore(&dcr_ind_lock, flags);
}
#define mfdcri(base, reg) __mfdcri(DCRN_ ## base ## _CONFIG_ADDR, \
DCRN_ ## base ## _CONFIG_DATA, \
reg)
#define mtdcri(base, reg, data) __mtdcri(DCRN_ ## base ## _CONFIG_ADDR, \
DCRN_ ## base ## _CONFIG_DATA, \
reg, data)
#define dcri_clrset(base, reg, clr, set) __dcri_clrset(DCRN_ ## base ## _CONFIG_ADDR, \
DCRN_ ## base ## _CONFIG_DATA, \
reg, clr, set)
#endif /* __ASSEMBLY__ */
#endif /* __KERNEL__ */
#endif /* _ASM_POWERPC_DCR_NATIVE_H */
[-- Attachment #4: dcr.h --]
[-- Type: application/octet-stream, Size: 6308 bytes --]
#ifndef _PPC_BOOT_DCR_H_
#define _PPC_BOOT_DCR_H_
#define mfdcr(rn) \
({ \
unsigned long rval; \
asm volatile("mfdcr %0,%1" : "=r"(rval) : "i"(rn)); \
rval; \
})
#define mtdcr(rn, val) \
asm volatile("mtdcr %0,%1" : : "i"(rn), "r"(val))
/* 440GP/440GX SDRAM controller DCRs */
#define DCRN_SDRAM0_CFGADDR 0x010
#define DCRN_SDRAM0_CFGDATA 0x011
#define SDRAM0_READ(offset) ({\
mtdcr(DCRN_SDRAM0_CFGADDR, offset); \
mfdcr(DCRN_SDRAM0_CFGDATA); })
#define SDRAM0_WRITE(offset, data) ({\
mtdcr(DCRN_SDRAM0_CFGADDR, offset); \
mtdcr(DCRN_SDRAM0_CFGDATA, data); })
#define SDRAM0_B0CR 0x40
#define SDRAM0_B1CR 0x44
#define SDRAM0_B2CR 0x48
#define SDRAM0_B3CR 0x4c
static const unsigned long sdram_bxcr[] = { SDRAM0_B0CR, SDRAM0_B1CR,
SDRAM0_B2CR, SDRAM0_B3CR };
#define SDRAM_CONFIG_BANK_ENABLE 0x00000001
#define SDRAM_CONFIG_SIZE_MASK 0x000e0000
#define SDRAM_CONFIG_BANK_SIZE(reg) \
(0x00400000 << ((reg & SDRAM_CONFIG_SIZE_MASK) >> 17))
/* 440GP External Bus Controller (EBC) */
#define DCRN_EBC0_CFGADDR 0x012
#define DCRN_EBC0_CFGDATA 0x013
#define EBC_NUM_BANKS 8
#define EBC_B0CR 0x00
#define EBC_B1CR 0x01
#define EBC_B2CR 0x02
#define EBC_B3CR 0x03
#define EBC_B4CR 0x04
#define EBC_B5CR 0x05
#define EBC_B6CR 0x06
#define EBC_B7CR 0x07
#define EBC_BXCR(n) (n)
#define EBC_BXCR_BAS 0xfff00000
#define EBC_BXCR_BS 0x000e0000
#define EBC_BXCR_BANK_SIZE(reg) \
(0x100000 << (((reg) & EBC_BXCR_BS) >> 17))
#define EBC_BXCR_BU 0x00018000
#define EBC_BXCR_BU_OFF 0x00000000
#define EBC_BXCR_BU_RO 0x00008000
#define EBC_BXCR_BU_WO 0x00010000
#define EBC_BXCR_BU_RW 0x00018000
#define EBC_BXCR_BW 0x00006000
#define EBC_B0AP 0x10
#define EBC_B1AP 0x11
#define EBC_B2AP 0x12
#define EBC_B3AP 0x13
#define EBC_B4AP 0x14
#define EBC_B5AP 0x15
#define EBC_B6AP 0x16
#define EBC_B7AP 0x17
#define EBC_BXAP(n) (0x10+(n))
#define EBC_BEAR 0x20
#define EBC_BESR 0x21
#define EBC_CFG 0x23
#define EBC_CID 0x24
/* 440GP Clock, PM, chip control */
#define DCRN_CPC0_SR 0x0b0
#define DCRN_CPC0_ER 0x0b1
#define DCRN_CPC0_FR 0x0b2
#define DCRN_CPC0_SYS0 0x0e0
#define CPC0_SYS0_TUNE 0xffc00000
#define CPC0_SYS0_FBDV_MASK 0x003c0000
#define CPC0_SYS0_FWDVA_MASK 0x00038000
#define CPC0_SYS0_FWDVB_MASK 0x00007000
#define CPC0_SYS0_OPDV_MASK 0x00000c00
#define CPC0_SYS0_EPDV_MASK 0x00000300
/* Helper macros to compute the actual clock divider values from the
* encodings in the CPC0 register */
#define CPC0_SYS0_FBDV(reg) \
((((((reg) & CPC0_SYS0_FBDV_MASK) >> 18) - 1) & 0xf) + 1)
#define CPC0_SYS0_FWDVA(reg) \
(8 - (((reg) & CPC0_SYS0_FWDVA_MASK) >> 15))
#define CPC0_SYS0_FWDVB(reg) \
(8 - (((reg) & CPC0_SYS0_FWDVB_MASK) >> 12))
#define CPC0_SYS0_OPDV(reg) \
((((reg) & CPC0_SYS0_OPDV_MASK) >> 10) + 1)
#define CPC0_SYS0_EPDV(reg) \
((((reg) & CPC0_SYS0_EPDV_MASK) >> 8) + 1)
#define CPC0_SYS0_EXTSL 0x00000080
#define CPC0_SYS0_RW_MASK 0x00000060
#define CPC0_SYS0_RL 0x00000010
#define CPC0_SYS0_ZMIISL_MASK 0x0000000c
#define CPC0_SYS0_BYPASS 0x00000002
#define CPC0_SYS0_NTO1 0x00000001
#define DCRN_CPC0_SYS1 0x0e1
#define DCRN_CPC0_CUST0 0x0e2
#define DCRN_CPC0_CUST1 0x0e3
#define DCRN_CPC0_STRP0 0x0e4
#define DCRN_CPC0_STRP1 0x0e5
#define DCRN_CPC0_STRP2 0x0e6
#define DCRN_CPC0_STRP3 0x0e7
#define DCRN_CPC0_GPIO 0x0e8
#define DCRN_CPC0_PLB 0x0e9
#define DCRN_CPC0_CR1 0x0ea
#define DCRN_CPC0_CR0 0x0eb
#define CPC0_CR0_SWE 0x80000000
#define CPC0_CR0_CETE 0x40000000
#define CPC0_CR0_U1FCS 0x20000000
#define CPC0_CR0_U0DTE 0x10000000
#define CPC0_CR0_U0DRE 0x08000000
#define CPC0_CR0_U0DC 0x04000000
#define CPC0_CR0_U1DTE 0x02000000
#define CPC0_CR0_U1DRE 0x01000000
#define CPC0_CR0_U1DC 0x00800000
#define CPC0_CR0_U0EC 0x00400000
#define CPC0_CR0_U1EC 0x00200000
#define CPC0_CR0_UDIV_MASK 0x001f0000
#define CPC0_CR0_UDIV(reg) \
((((reg) & CPC0_CR0_UDIV_MASK) >> 16) + 1)
#define DCRN_CPC0_MIRQ0 0x0ec
#define DCRN_CPC0_MIRQ1 0x0ed
#define DCRN_CPC0_JTAGID 0x0ef
#define DCRN_MAL0_CFG 0x180
#define MAL_RESET 0x80000000
/* 440EP Clock/Power-on Reset regs */
#define DCRN_CPR0_ADDR 0xc
#define DCRN_CPR0_DATA 0xd
#define CPR0_PLLD0 0x60
#define CPR0_OPBD0 0xc0
#define CPR0_PERD0 0xe0
#define CPR0_PRIMBD0 0xa0
#define CPR0_SCPID 0x120
#define CPR0_PLLC0 0x40
/* 405GP Clocking/Power Management/Chip Control regs */
#define DCRN_CPC0_PLLMR 0xb0
#define DCRN_405_CPC0_CR0 0xb1
#define DCRN_405_CPC0_CR1 0xb2
#define DCRN_405_CPC0_PSR 0xb4
/* 405EP Clocking/Power Management/Chip Control regs */
#define DCRN_CPC0_PLLMR0 0xf0
#define DCRN_CPC0_PLLMR1 0xf4
#define DCRN_CPC0_UCR 0xf5
/* 440GX Clock control etc */
#define DCRN_CPR0_CLKUPD 0x020
#define DCRN_CPR0_PLLC 0x040
#define DCRN_CPR0_PLLD 0x060
#define DCRN_CPR0_PRIMAD 0x080
#define DCRN_CPR0_PRIMBD 0x0a0
#define DCRN_CPR0_OPBD 0x0c0
#define DCRN_CPR0_PERD 0x0e0
#define DCRN_CPR0_MALD 0x100
#define DCRN_SDR0_CONFIG_ADDR 0xe
#define DCRN_SDR0_CONFIG_DATA 0xf
/* SDR read/write helper macros */
#define SDR0_READ(offset) ({\
mtdcr(DCRN_SDR0_CONFIG_ADDR, offset); \
mfdcr(DCRN_SDR0_CONFIG_DATA); })
#define SDR0_WRITE(offset, data) ({\
mtdcr(DCRN_SDR0_CONFIG_ADDR, offset); \
mtdcr(DCRN_SDR0_CONFIG_DATA, data); })
#define DCRN_SDR0_UART0 0x0120
#define DCRN_SDR0_UART1 0x0121
#define DCRN_SDR0_UART2 0x0122
#define DCRN_SDR0_UART3 0x0123
/* CPRs read/write helper macros - based off include/asm-ppc/ibm44x.h */
#define DCRN_CPR0_CFGADDR 0xc
#define DCRN_CPR0_CFGDATA 0xd
#define CPR0_READ(offset) ({\
mtdcr(DCRN_CPR0_CFGADDR, offset); \
mfdcr(DCRN_CPR0_CFGDATA); })
#define CPR0_WRITE(offset, data) ({\
mtdcr(DCRN_CPR0_CFGADDR, offset); \
mtdcr(DCRN_CPR0_CFGDATA, data); })
#endif /* _PPC_BOOT_DCR_H_ */
^ permalink raw reply
* Re: Debian on MPC8572DS
From: Scott Wood @ 2009-09-10 16:32 UTC (permalink / raw)
To: Isaac Gomez Morales; +Cc: linuxppc-dev
In-Reply-To: <38a5a2d10909100801x11049cddi1799c0ed6ee44410@mail.gmail.com>
On Thu, Sep 10, 2009 at 05:01:53PM +0200, Isaac Gomez Morales wrote:
> Hello,
>
> I'm trying to get a Linux distro such as Debian in the following system
>
> System:
> mpc8572ds HW
The 8572 does not have classic PowerPC floating point. The Debian
binaries use this, so they cannot run without emulation.
Emulation is slow, so you probably want something like this instead:
http://groups.google.com/group/linux.debian.ports.powerpc/browse_thread/thread/235607517fd7ae1e/f4638a2cdef09bf7?lnk=raot
http://download.breakpoint.cc/debian/linutronix-lenny-gnuspe/
-Scott
^ permalink raw reply
* Re: [PATCH][v1] powerpc/85xx: Create dts for each core in CAMPmode for P2020RDB
From: Gabriel Paubert @ 2009-09-10 16:02 UTC (permalink / raw)
To: Aggrwal Poonam-B10812; +Cc: linuxppc-dev
In-Reply-To: <1BD5CFC378ED0946B688E0C9BA2EF0951FD268@zin33exm24.fsl.freescale.net>
On Thu, Sep 10, 2009 at 08:48:38PM +0530, Aggrwal Poonam-B10812 wrote:
>
>
> > -----Original Message-----
> > From: Gabriel Paubert [mailto:paubert@iram.es]
> > Sent: Thursday, September 10, 2009 3:03 PM
> > To: Aggrwal Poonam-B10812
> > Cc: linuxppc-dev@ozlabs.org
> > Subject: Re: [PATCH][v1] powerpc/85xx: Create dts for each
> > core in CAMPmode for P2020RDB
> >
> > On Thu, Sep 10, 2009 at 02:27:11PM +0530, Poonam Aggrwal wrote:
> > > This patch creates the dts files for each core and splits
> > the devices
> > > between the two cores for P2020RDB.
> > >
> > > core0 has memory, L2, i2c, spi, dma1, usb, eth0, eth1, crypto,
> > > global-util, pci0
> > > core1 has L2, dma2, eth0, pci1, msi.
> >
> > Surely you mean eth1 and eth2 for core0, no?
> Yes you are right , I'll fix this.
> >
> > At least it's what I gather from the code.
> >
> > Also both cores have a node called serial0, at different
> > addresses but with the same interrupt!
> Yes both the UARTS use the same int line in shared mode.
> >
> > But in the mpic comment line there is "serial1", and
> > interrupt 42 is the only number which appears in both lists
> > of mpic protected interrupts.
> I am not sure how to handle the shared interrupts in AMP scenario,
> although this has been tested and both serials are working one on each
> core.
Ok, I wrongly understood protected interrupts as reserved
for one core. However, I still dislike two devices having the same
name.
Otherwise it may work if every interrupt is delivered to both
cores although statistically only one core will actually have
some work to do. Doesn't the kernel complain about unhandled irqs
however?
Regards,
Gabriel
> > > +
> > > + serial0: serial@4500 {
> > > + cell-index = <0>;
> > > + device_type = "serial";
> > > + compatible = "ns16550";
> > > + reg = <0x4500 0x100>;
> > > + clock-frequency = <0>;
> > > + interrupts = <42 2>;
> > > + interrupt-parent = <&mpic>;
> > > + };
> > > +
> > > + mpic: pic@40000 {
> > > + interrupt-controller;
> > > + #address-cells = <0>;
> > > + #interrupt-cells = <2>;
> > > + reg = <0x40000 0x40000>;
> > > + compatible = "chrp,open-pic";
> > > + device_type = "open-pic";
> > > + protected-sources = <
> > > + 42 76 77 78 79 /* serial1 , dma2 */
> > > + 29 30 34 26 /* enet0, pci1 */
> > > + 0xe0 0xe1 0xe2 0xe3 /* msi */
> > > + 0xe4 0xe5 0xe6 0xe7
> > > + >;
> > > + };
> > > +
> > > +
> > > + serial0: serial@4600 {
> > > + cell-index = <1>;
> > > + device_type = "serial";
> > > + compatible = "ns16550";
> > > + reg = <0x4600 0x100>;
> > > + clock-frequency = <0>;
> > > + interrupts = <42 2>;
> > > + interrupt-parent = <&mpic>;
> > > + };
> > > +
> > > +
> > > + mpic: pic@40000 {
> > > + interrupt-controller;
> > > + #address-cells = <0>;
> > > + #interrupt-cells = <2>;
> > > + reg = <0x40000 0x40000>;
> > > + compatible = "chrp,open-pic";
> > > + device_type = "open-pic";
> > > + protected-sources = <
> > > + 17 18 43 42 59 47 /*ecm, mem, i2c, serial0, spi,gpio */
> > > + 16 20 21 22 23 28 /* L2, dma1, USB */
> > > + 03 35 36 40 31 32 33 /* mdio, enet1, enet2 */
> > > + 72 45 58 25 /* sdhci, crypto , pci */
> > > + >;
> > > + };
> > > +
^ permalink raw reply
page: next (older) | prev (newer) | latest
- recent:[subjects (threaded)|topics (new)|topics (active)]
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox