* [PATCH 05/13] powerpc/5200: LocalPlus driver: fix DMA TX interrupt request
From: Roman Fietze @ 2009-12-22 7:02 UTC (permalink / raw)
To: linuxppc-dev
In-Reply-To: <200912220755.09756.roman.fietze@telemotive.de>
Signed-off-by: Roman Fietze <roman.fietze@telemotive.de>
=2D--
arch/powerpc/platforms/52xx/mpc52xx_lpbfifo.c | 4 ++--
1 files changed, 2 insertions(+), 2 deletions(-)
diff --git a/arch/powerpc/platforms/52xx/mpc52xx_lpbfifo.c b/arch/powerpc/p=
latforms/52xx/mpc52xx_lpbfifo.c
index 2fd1f3f..1e4f725 100644
=2D-- a/arch/powerpc/platforms/52xx/mpc52xx_lpbfifo.c
+++ b/arch/powerpc/platforms/52xx/mpc52xx_lpbfifo.c
@@ -460,9 +460,9 @@ mpc52xx_lpbfifo_probe(struct of_device *op, const struc=
t of_device_id *match)
if (!lpbfifo.bcom_tx_task)
goto err_bcom_tx;
=20
=2D rc =3D request_irq(bcom_get_task_irq(lpbfifo.bcom_rx_task),
+ rc =3D request_irq(bcom_get_task_irq(lpbfifo.bcom_tx_task),
mpc52xx_lpbfifo_bcom_irq, 0,
=2D "mpc52xx-lpbfifo-rx", &lpbfifo);
+ "mpc52xx-lpbfifo-tx", &lpbfifo);
if (rc)
goto err_bcom_tx_irq;
=20
=2D-=20
1.6.5.5
=2D-=20
Roman Fietze Telemotive AG B=FCro M=FChlhausen
Breitwiesen 73347 M=FChlhausen
Tel.: +49(0)7335/18493-45 http://www.telemotive.de
^ permalink raw reply
* [PATCH 06/13] powerpc/5200: LocalPlus driver: map and unmap DMA areas
From: Roman Fietze @ 2009-12-22 7:04 UTC (permalink / raw)
To: linuxppc-dev
In-Reply-To: <200912220755.09756.roman.fietze@telemotive.de>
Signed-off-by: Roman Fietze <roman.fietze@telemotive.de>
=2D--
arch/powerpc/include/asm/mpc52xx.h | 2 +-
arch/powerpc/platforms/52xx/mpc52xx_lpbfifo.c | 26 +++++++++++++++++++--=
=2D--
2 files changed, 21 insertions(+), 7 deletions(-)
diff --git a/arch/powerpc/include/asm/mpc52xx.h b/arch/powerpc/include/asm/=
mpc52xx.h
index c659d1d..043458e 100644
=2D-- a/arch/powerpc/include/asm/mpc52xx.h
+++ b/arch/powerpc/include/asm/mpc52xx.h
@@ -347,7 +347,7 @@ struct mpc52xx_lpbfifo_request {
=20
/* Memory address */
void *data;
=2D phys_addr_t data_phys;
+ dma_addr_t data_dma;
=20
/* Details of transfer */
size_t size;
diff --git a/arch/powerpc/platforms/52xx/mpc52xx_lpbfifo.c b/arch/powerpc/p=
latforms/52xx/mpc52xx_lpbfifo.c
index 1e4f725..8d8a63a 100644
=2D-- a/arch/powerpc/platforms/52xx/mpc52xx_lpbfifo.c
+++ b/arch/powerpc/platforms/52xx/mpc52xx_lpbfifo.c
@@ -14,6 +14,7 @@
#include <linux/of.h>
#include <linux/of_platform.h>
#include <linux/spinlock.h>
+#include <linux/dma-mapping.h>
#include <asm/io.h>
#include <asm/prom.h>
#include <asm/mpc52xx.h>
@@ -138,6 +139,7 @@ static void mpc52xx_lpbfifo_kick(struct mpc52xx_lpbfifo=
_request *req)
out_be32(&lpbfifo.regs->fifo_alarm, MPC52xx_SCLPC_FIFO_SIZE - 28);
out_be32(&lpbfifo.regs->fifo_control, MPC52xx_SLPC_FIFO_CONTROL_GR(7));
lpbfifo.bcom_cur_task =3D lpbfifo.bcom_tx_task;
+ req->data_dma =3D dma_map_single(lpbfifo.dev, req->data, req->size, DMA=
_TO_DEVICE);
} else {
out_be32(&lpbfifo.regs->fifo_alarm, MPC52xx_SCLPC_FIFO_SIZE - 1);
out_be32(&lpbfifo.regs->fifo_control, MPC52xx_SLPC_FIFO_CONTROL_GR(0));
@@ -154,6 +156,7 @@ static void mpc52xx_lpbfifo_kick(struct mpc52xx_lpbfifo=
_request *req)
lpbfifo.dma_irqs_enabled =3D 1;
}
}
+ req->data_dma =3D dma_map_single(lpbfifo.dev, req->data, req->size, DMA=
_FROM_DEVICE);
}
=20
/* error irq & master enabled bit */
@@ -161,7 +164,7 @@ static void mpc52xx_lpbfifo_kick(struct mpc52xx_lpbfifo=
_request *req)
=20
bd =3D bcom_prepare_next_buffer(lpbfifo.bcom_cur_task);
bd->status =3D transfer_size;
=2D bd->data[0] =3D req->data_phys + req->pos;
+ bd->data[0] =3D req->data_dma + req->pos;
bcom_submit_next_buffer(lpbfifo.bcom_cur_task, NULL);
}
=20
@@ -236,12 +239,13 @@ static irqreturn_t mpc52xx_lpbfifo_sclpc_irq(int irq,=
void *dev_id)
}
=20
rflags =3D req->flags;
+ status_count =3D in_be32(&lpbfifo.regs->bytes_done_status.bytes_done);
=20
=2D /* check normal termination bit */
+ /* Check normal termination bit */
if (!(status_count & MPC52xx_SCLPC_STATUS_NT))
goto out;
=20
=2D /* check abort bit */
+ /* Check abort bit */
if (status_count & MPC52xx_SCLPC_STATUS_AT) {
out_be32(&lpbfifo.regs->enable, MPC52xx_SCLPC_ENABLE_RC | MPC52xx_SCLPC_=
ENABLE_RF);
do_callback =3D 1;
@@ -250,7 +254,7 @@ static irqreturn_t mpc52xx_lpbfifo_sclpc_irq(int irq, v=
oid *dev_id)
=20
if (!mpc52xx_lpbfifo_is_dma(rflags)) {
=20
=2D /* bytes done */
+ /* Bytes done */
status_count &=3D MPC52xx_SCLPC_STATUS_BYTES_DONE_MASK;
=20
if (!mpc52xx_lpbfifo_is_write(rflags)) {
@@ -336,6 +340,16 @@ static irqreturn_t mpc52xx_lpbfifo_bcom_irq(int irq, v=
oid *dev_id)
bcom_retrieve_buffer(lpbfifo->bcom_cur_task, NULL, NULL);
// req->irq_ticks +=3D get_tbl() - ts;
=20
+ if (lpbfifo->req) {
+ if (mpc52xx_lpbfifo_is_write(lpbfifo->req->flags))
+ dma_unmap_single(lpbfifo->dev, lpbfifo->req->data_dma, lpbfifo->req->si=
ze, DMA_TO_DEVICE);
+ else
+ dma_unmap_single(lpbfifo->dev, lpbfifo->req->data_dma, lpbfifo->req->si=
ze, DMA_FROM_DEVICE);
+ } else
+ {
+ dev_err(lpbfifo->dev, "request is NULL\n");
+ }
+
spin_unlock_irqrestore(&lpbfifo->lock, flags);
=20
return IRQ_HANDLED;
@@ -439,7 +453,7 @@ mpc52xx_lpbfifo_probe(struct of_device *op, const struc=
t of_device_id *match)
goto err_irq;
=20
/* Request the Bestcomm receive (fifo --> memory) task and IRQ */
=2D lpbfifo.bcom_rx_task =3D bcom_gen_bd_rx_init(16,
+ lpbfifo.bcom_rx_task =3D bcom_gen_bd_rx_init(4,
res.start + offsetof(struct mpc52xx_sclpc, fifo_data),
BCOM_INITIATOR_SCLPC, BCOM_IPR_SCLPC,
16*1024*1024);
@@ -453,7 +467,7 @@ mpc52xx_lpbfifo_probe(struct of_device *op, const struc=
t of_device_id *match)
goto err_bcom_rx_irq;
=20
/* Request the Bestcomm transmit (memory --> fifo) task and IRQ */
=2D lpbfifo.bcom_tx_task =3D bcom_gen_bd_tx_init(16,
+ lpbfifo.bcom_tx_task =3D bcom_gen_bd_tx_init(4,
res.start + offsetof(struct mpc52xx_sclpc, fifo_data),
BCOM_INITIATOR_SCLPC,
BCOM_IPR_SCLPC);
=2D-=20
1.6.5.5
=2D-=20
Roman Fietze Telemotive AG B=FCro M=FChlhausen
Breitwiesen 73347 M=FChlhausen
Tel.: +49(0)7335/18493-45 http://www.telemotive.de
^ permalink raw reply
* [PATCH 07/13] powerpc/5200: LocalPlus driver: reset BestComm when committing new request
From: Roman Fietze @ 2009-12-22 7:05 UTC (permalink / raw)
To: linuxppc-dev
In-Reply-To: <200912220755.09756.roman.fietze@telemotive.de>
Signed-off-by: Roman Fietze <roman.fietze@telemotive.de>
=2D--
arch/powerpc/platforms/52xx/mpc52xx_lpbfifo.c | 4 +++-
1 files changed, 3 insertions(+), 1 deletions(-)
diff --git a/arch/powerpc/platforms/52xx/mpc52xx_lpbfifo.c b/arch/powerpc/p=
latforms/52xx/mpc52xx_lpbfifo.c
index 8d8a63a..a7cd585 100644
=2D-- a/arch/powerpc/platforms/52xx/mpc52xx_lpbfifo.c
+++ b/arch/powerpc/platforms/52xx/mpc52xx_lpbfifo.c
@@ -398,6 +398,8 @@ int mpc52xx_lpbfifo_submit(struct mpc52xx_lpbfifo_reque=
st *req)
req->buffer_not_done_cnt =3D 0;
req->pos =3D 0;
=20
+ bcom_gen_bd_rx_reset(lpbfifo.bcom_rx_task);
+ bcom_gen_bd_tx_reset(lpbfifo.bcom_tx_task);
mpc52xx_lpbfifo_kick(req);
spin_unlock_irqrestore(&lpbfifo.lock, flags);
=20
@@ -456,7 +458,7 @@ mpc52xx_lpbfifo_probe(struct of_device *op, const struc=
t of_device_id *match)
lpbfifo.bcom_rx_task =3D bcom_gen_bd_rx_init(4,
res.start + offsetof(struct mpc52xx_sclpc, fifo_data),
BCOM_INITIATOR_SCLPC, BCOM_IPR_SCLPC,
=2D 16*1024*1024);
+ 16 * 1024 * 1024);
if (!lpbfifo.bcom_rx_task)
goto err_bcom_rx;
=20
=2D-=20
1.6.5.5
=2D-=20
Roman Fietze Telemotive AG B=FCro M=FChlhausen
Breitwiesen 73347 M=FChlhausen
Tel.: +49(0)7335/18493-45 http://www.telemotive.de
^ permalink raw reply
* [PATCH 08/13] powerpc/5200: LocalPlus driver: smart flush of receive FIFO
From: Roman Fietze @ 2009-12-22 7:06 UTC (permalink / raw)
To: linuxppc-dev
In-Reply-To: <200912220755.09756.roman.fietze@telemotive.de>
Signed-off-by: Roman Fietze <roman.fietze@telemotive.de>
=2D--
arch/powerpc/platforms/52xx/mpc52xx_lpbfifo.c | 40 ++++++++++++++++-----=
=2D---
1 files changed, 26 insertions(+), 14 deletions(-)
diff --git a/arch/powerpc/platforms/52xx/mpc52xx_lpbfifo.c b/arch/powerpc/p=
latforms/52xx/mpc52xx_lpbfifo.c
index a7cd585..48f2b4f 100644
=2D-- a/arch/powerpc/platforms/52xx/mpc52xx_lpbfifo.c
+++ b/arch/powerpc/platforms/52xx/mpc52xx_lpbfifo.c
@@ -84,8 +84,7 @@ static void mpc52xx_lpbfifo_kick(struct mpc52xx_lpbfifo_r=
equest *req)
struct bcom_bd *bd;
void __iomem *reg;
u32 *data;
=2D int i;
=2D int bit_fields;
+ u32 bit_fields;
int rflags =3D req->flags;
=20
/* Set and clear the reset bits; is good practice in User Manual */
@@ -96,27 +95,32 @@ static void mpc52xx_lpbfifo_kick(struct mpc52xx_lpbfifo=
_request *req)
=20
/* Set CS and BPT */
bit_fields =3D MPC52xx_SCLPC_CONTROL_CS(req->cs) | 0x8;
=2D if (!(mpc52xx_lpbfifo_is_write(rflags))) {
+ if (!(mpc52xx_lpbfifo_is_write(rflags)))
bit_fields |=3D MPC52xx_SCLPC_CONTROL_RWB_RECEIVE; /* read mode */
=2D bit_fields |=3D MPC52xx_SCLPC_CONTROL_FLUSH;
=2D }
=2D out_be32(&lpbfifo.regs->control, bit_fields);
=20
if (!mpc52xx_lpbfifo_is_dma(rflags)) {
=2D /* While the FIFO can be setup for transfer sizes as large as
=2D * 16M-1, the FIFO itself is only 512 bytes deep and it does
=2D * not generate interrupts for FIFO full events (only transfer
=2D * complete will raise an IRQ). Therefore when not using
=2D * Bestcomm to drive the FIFO it needs to either be polled, or
=2D * transfers need to constrained to the size of the fifo.
+ /* While the FIFO can be setup for transfer sizes as
+ * large as 16M-1, the FIFO itself is only 512 bytes
+ * deep and it does not generate interrupts for FIFO
+ * full events (only transfer complete will raise an
+ * IRQ). Therefore when not using Bestcomm to drive the
+ * FIFO it needs to either be polled, or transfers need
+ * to constrained to the size of the fifo.
*
* This driver restricts the size of the transfer
+ *
+ * The last block of data will be received with the
+ * flush bit set. This avoids stale read data.
*/
if (transfer_size > 512)
transfer_size =3D 512;
+ else if (!(mpc52xx_lpbfifo_is_write(rflags)))
+ bit_fields |=3D MPC52xx_SCLPC_CONTROL_FLUSH;
=20
/* Load the FIFO with data */
if (mpc52xx_lpbfifo_is_write(rflags)) {
+ size_t i;
+
reg =3D &lpbfifo.regs->fifo_data;
data =3D req->data + req->pos;
for (i =3D 0; i < transfer_size; i +=3D 4)
@@ -128,6 +132,12 @@ static void mpc52xx_lpbfifo_kick(struct mpc52xx_lpbfif=
o_request *req)
MPC52xx_SCLPC_ENABLE_NIE |
MPC52xx_SCLPC_ENABLE_ME));
} else {
+
+ /* In DMA mode we can always set the flush bit to avoid
+ * stale read data. */
+ if (!(mpc52xx_lpbfifo_is_write(rflags)))
+ bit_fields |=3D MPC52xx_SCLPC_CONTROL_FLUSH;
+
/* Choose the correct direction
*
* Configure the watermarks so DMA will always complete correctly.
@@ -168,6 +178,8 @@ static void mpc52xx_lpbfifo_kick(struct mpc52xx_lpbfifo=
_request *req)
bcom_submit_next_buffer(lpbfifo.bcom_cur_task, NULL);
}
=20
+ out_be32(&lpbfifo.regs->control, bit_fields);
+
/* Set packet size and kick it off */
out_be32(&lpbfifo.regs->packet_size.packet_size, MPC52xx_SCLPC_PACKET_SIZ=
E_RESTART | transfer_size);
if (mpc52xx_lpbfifo_is_dma(rflags))
@@ -455,7 +467,7 @@ mpc52xx_lpbfifo_probe(struct of_device *op, const struc=
t of_device_id *match)
goto err_irq;
=20
/* Request the Bestcomm receive (fifo --> memory) task and IRQ */
=2D lpbfifo.bcom_rx_task =3D bcom_gen_bd_rx_init(4,
+ lpbfifo.bcom_rx_task =3D bcom_gen_bd_rx_init(2,
res.start + offsetof(struct mpc52xx_sclpc, fifo_data),
BCOM_INITIATOR_SCLPC, BCOM_IPR_SCLPC,
16 * 1024 * 1024);
@@ -469,7 +481,7 @@ mpc52xx_lpbfifo_probe(struct of_device *op, const struc=
t of_device_id *match)
goto err_bcom_rx_irq;
=20
/* Request the Bestcomm transmit (memory --> fifo) task and IRQ */
=2D lpbfifo.bcom_tx_task =3D bcom_gen_bd_tx_init(4,
+ lpbfifo.bcom_tx_task =3D bcom_gen_bd_tx_init(2,
res.start + offsetof(struct mpc52xx_sclpc, fifo_data),
BCOM_INITIATOR_SCLPC,
BCOM_IPR_SCLPC);
=2D-=20
1.6.5.5
=2D-=20
Roman Fietze Telemotive AG B=FCro M=FChlhausen
Breitwiesen 73347 M=FChlhausen
Tel.: +49(0)7335/18493-45 http://www.telemotive.de
^ permalink raw reply
* [PATCH 09/13] powerpc/5200: LocalPlus driver: smarter calculation of BPT, bytes per transfer
From: Roman Fietze @ 2009-12-22 7:08 UTC (permalink / raw)
To: linuxppc-dev
In-Reply-To: <200912220755.09756.roman.fietze@telemotive.de>
Signed-off-by: Roman Fietze <roman.fietze@telemotive.de>
=2D--
arch/powerpc/platforms/52xx/mpc52xx_lpbfifo.c | 33 +++++++++++++++------=
=2D---
1 files changed, 20 insertions(+), 13 deletions(-)
diff --git a/arch/powerpc/platforms/52xx/mpc52xx_lpbfifo.c b/arch/powerpc/p=
latforms/52xx/mpc52xx_lpbfifo.c
index 48f2b4f..21b2a40 100644
=2D-- a/arch/powerpc/platforms/52xx/mpc52xx_lpbfifo.c
+++ b/arch/powerpc/platforms/52xx/mpc52xx_lpbfifo.c
@@ -80,11 +80,11 @@ static inline int mpc52xx_lpbfifo_is_poll_dma(int flags)
*/
static void mpc52xx_lpbfifo_kick(struct mpc52xx_lpbfifo_request *req)
{
=2D size_t transfer_size =3D req->size - req->pos;
+ size_t tc =3D req->size - req->pos;
struct bcom_bd *bd;
void __iomem *reg;
u32 *data;
=2D u32 bit_fields;
+ u32 control;
int rflags =3D req->flags;
=20
/* Set and clear the reset bits; is good practice in User Manual */
@@ -93,10 +93,10 @@ static void mpc52xx_lpbfifo_kick(struct mpc52xx_lpbfifo=
_request *req)
/* Set width, chip select and READ mode */
out_be32(&lpbfifo.regs->start_address, req->offset + req->pos);
=20
=2D /* Set CS and BPT */
=2D bit_fields =3D MPC52xx_SCLPC_CONTROL_CS(req->cs) | 0x8;
+ /* Setup CS */
+ control =3D MPC52xx_SCLPC_CONTROL_CS(req->cs);
if (!(mpc52xx_lpbfifo_is_write(rflags)))
=2D bit_fields |=3D MPC52xx_SCLPC_CONTROL_RWB_RECEIVE; /* read mode */
+ control |=3D MPC52xx_SCLPC_CONTROL_RWB_RECEIVE; /* read mode */
=20
if (!mpc52xx_lpbfifo_is_dma(rflags)) {
/* While the FIFO can be setup for transfer sizes as
@@ -112,10 +112,10 @@ static void mpc52xx_lpbfifo_kick(struct mpc52xx_lpbfi=
fo_request *req)
* The last block of data will be received with the
* flush bit set. This avoids stale read data.
*/
=2D if (transfer_size > 512)
=2D transfer_size =3D 512;
+ if (tc > 512)
+ tc =3D 512;
else if (!(mpc52xx_lpbfifo_is_write(rflags)))
=2D bit_fields |=3D MPC52xx_SCLPC_CONTROL_FLUSH;
+ control |=3D MPC52xx_SCLPC_CONTROL_FLUSH;
=20
/* Load the FIFO with data */
if (mpc52xx_lpbfifo_is_write(rflags)) {
@@ -123,7 +123,7 @@ static void mpc52xx_lpbfifo_kick(struct mpc52xx_lpbfifo=
_request *req)
=20
reg =3D &lpbfifo.regs->fifo_data;
data =3D req->data + req->pos;
=2D for (i =3D 0; i < transfer_size; i +=3D 4)
+ for (i =3D 0; i < tc; i +=3D 4)
out_be32(reg, *data++);
}
=20
@@ -136,7 +136,7 @@ static void mpc52xx_lpbfifo_kick(struct mpc52xx_lpbfifo=
_request *req)
/* In DMA mode we can always set the flush bit to avoid
* stale read data. */
if (!(mpc52xx_lpbfifo_is_write(rflags)))
=2D bit_fields |=3D MPC52xx_SCLPC_CONTROL_FLUSH;
+ control |=3D MPC52xx_SCLPC_CONTROL_FLUSH;
=20
/* Choose the correct direction
*
@@ -173,15 +173,17 @@ static void mpc52xx_lpbfifo_kick(struct mpc52xx_lpbfi=
fo_request *req)
out_be32(&lpbfifo.regs->enable, MPC52xx_SCLPC_ENABLE_AIE | MPC52xx_SCLPC=
_ENABLE_NIE | MPC52xx_SCLPC_ENABLE_ME);
=20
bd =3D bcom_prepare_next_buffer(lpbfifo.bcom_cur_task);
=2D bd->status =3D transfer_size;
+ bd->status =3D tc;
bd->data[0] =3D req->data_dma + req->pos;
bcom_submit_next_buffer(lpbfifo.bcom_cur_task, NULL);
}
=20
=2D out_be32(&lpbfifo.regs->control, bit_fields);
+ /* Setup BPT. tc is already screened and a multiple of 4 */
+ control |=3D tc & 7 ? 4 : 8;
+ out_be32(&lpbfifo.regs->control, control);
=20
/* Set packet size and kick it off */
=2D out_be32(&lpbfifo.regs->packet_size.packet_size, MPC52xx_SCLPC_PACKET_S=
IZE_RESTART | transfer_size);
+ out_be32(&lpbfifo.regs->packet_size.packet_size, MPC52xx_SCLPC_PACKET_SIZ=
E_RESTART | tc);
if (mpc52xx_lpbfifo_is_dma(rflags))
bcom_enable(lpbfifo.bcom_cur_task);
}
@@ -395,6 +397,11 @@ int mpc52xx_lpbfifo_submit(struct mpc52xx_lpbfifo_requ=
est *req)
if (!lpbfifo.regs)
return -ENODEV;
=20
+ /* The gen bd BestComm task currently only allows an increment
+ * of 4 */
+ if (!req->size || req->size & 0x03)
+ return -EINVAL;
+
spin_lock_irqsave(&lpbfifo.lock, flags);
=20
/* If the req pointer is already set, then a transfer is in progress */
=2D-=20
1.6.5.5
=2D-=20
Roman Fietze Telemotive AG B=FCro M=FChlhausen
Breitwiesen 73347 M=FChlhausen
Tel.: +49(0)7335/18493-45 http://www.telemotive.de
^ permalink raw reply
* [PATCH 10/13] powerpc/5200: LocalPlus driver: fix problem caused by unpredictable IRQ order
From: Roman Fietze @ 2009-12-22 7:09 UTC (permalink / raw)
To: linuxppc-dev
In-Reply-To: <200912220755.09756.roman.fietze@telemotive.de>
The order of the raised interrupts of SCLPC and BCOM cannot be
predicted, because it depends on the individual BCOM and CPU loads. So
in DMA mode we just wait for both until we finish the transaction.
Signed-off-by: Roman Fietze <roman.fietze@telemotive.de>
=2D--
arch/powerpc/platforms/52xx/mpc52xx_lpbfifo.c | 94 +++++++++++++++++----=
=2D---
1 files changed, 64 insertions(+), 30 deletions(-)
diff --git a/arch/powerpc/platforms/52xx/mpc52xx_lpbfifo.c b/arch/powerpc/p=
latforms/52xx/mpc52xx_lpbfifo.c
index 21b2a40..cd8dc69 100644
=2D-- a/arch/powerpc/platforms/52xx/mpc52xx_lpbfifo.c
+++ b/arch/powerpc/platforms/52xx/mpc52xx_lpbfifo.c
@@ -32,7 +32,7 @@ struct mpc52xx_lpbfifo {
struct device *dev;
phys_addr_t regs_phys;
struct mpc52xx_sclpc __iomem *regs;
=2D int irq;
+ int sclpc_irq;
spinlock_t lock;
=20
struct bcom_task *bcom_tx_task;
@@ -41,6 +41,7 @@ struct mpc52xx_lpbfifo {
=20
/* Current state data */
struct mpc52xx_lpbfifo_request *req;
+ unsigned short irqs_pending;
int dma_irqs_enabled;
};
=20
@@ -48,6 +49,14 @@ struct mpc52xx_lpbfifo {
static struct mpc52xx_lpbfifo lpbfifo;
=20
=20
+/* The order of the raised interrupts of SCLPC and BCOM cann not be
+ * predicted, because it depends on the individual BCOM and CPU
+ * loads. So in DMA mode we just wait for both until we finish the
+ * transaction. */
+#define MPC52XX_LPBFIFO_PENDING_SCLPC BIT(0)
+#define MPC52XX_LPBFIFO_PENDING_BCOM BIT(1)
+
+
/**
* mpc52xx_lpbfifo_is_write - return true if it's a WRITE request
*/
@@ -127,6 +136,8 @@ static void mpc52xx_lpbfifo_kick(struct mpc52xx_lpbfifo=
_request *req)
out_be32(reg, *data++);
}
=20
+ lpbfifo.irqs_pending =3D MPC52XX_LPBFIFO_PENDING_SCLPC;
+
/* Unmask both error and completion irqs */
out_be32(&lpbfifo.regs->enable, (MPC52xx_SCLPC_ENABLE_AIE |
MPC52xx_SCLPC_ENABLE_NIE |
@@ -172,6 +183,8 @@ static void mpc52xx_lpbfifo_kick(struct mpc52xx_lpbfifo=
_request *req)
/* error irq & master enabled bit */
out_be32(&lpbfifo.regs->enable, MPC52xx_SCLPC_ENABLE_AIE | MPC52xx_SCLPC=
_ENABLE_NIE | MPC52xx_SCLPC_ENABLE_ME);
=20
+ lpbfifo.irqs_pending =3D MPC52XX_LPBFIFO_PENDING_BCOM | MPC52XX_LPBFIFO_=
PENDING_SCLPC;
+
bd =3D bcom_prepare_next_buffer(lpbfifo.bcom_cur_task);
bd->status =3D tc;
bd->data[0] =3D req->data_dma + req->pos;
@@ -188,6 +201,7 @@ static void mpc52xx_lpbfifo_kick(struct mpc52xx_lpbfifo=
_request *req)
bcom_enable(lpbfifo.bcom_cur_task);
}
=20
+
/**
* mpc52xx_lpbfifo_sclpc_irq - IRQ handler for LPB FIFO
*
@@ -232,8 +246,9 @@ static void mpc52xx_lpbfifo_kick(struct mpc52xx_lpbfifo=
_request *req)
*/
static irqreturn_t mpc52xx_lpbfifo_sclpc_irq(int irq, void *dev_id)
{
+ struct mpc52xx_lpbfifo *lpbfifo =3D dev_id;
struct mpc52xx_lpbfifo_request *req;
=2D u32 status_count =3D in_be32(&lpbfifo.regs->bytes_done_status.bytes_don=
e);
+ u32 status_count =3D in_be32(&lpbfifo->regs->bytes_done_status.bytes_done=
);
void __iomem *reg;
u32 *data;
size_t i;
@@ -242,18 +257,20 @@ static irqreturn_t mpc52xx_lpbfifo_sclpc_irq(int irq,=
void *dev_id)
unsigned long flags;
int rflags;
=20
=2D spin_lock_irqsave(&lpbfifo.lock, flags);
+ spin_lock_irqsave(&lpbfifo->lock, flags);
ts =3D get_tbl();
=20
=2D req =3D lpbfifo.req;
+ req =3D lpbfifo->req;
if (!req) {
=2D spin_unlock_irqrestore(&lpbfifo.lock, flags);
+ spin_unlock_irqrestore(&lpbfifo->lock, flags);
pr_err("bogus SCLPC IRQ\n");
return IRQ_HANDLED;
}
=20
+ lpbfifo->irqs_pending &=3D ~MPC52XX_LPBFIFO_PENDING_SCLPC;
+
rflags =3D req->flags;
=2D status_count =3D in_be32(&lpbfifo.regs->bytes_done_status.bytes_done);
+ status_count =3D in_be32(&lpbfifo->regs->bytes_done_status.bytes_done);
=20
/* Check normal termination bit */
if (!(status_count & MPC52xx_SCLPC_STATUS_NT))
@@ -261,19 +278,23 @@ static irqreturn_t mpc52xx_lpbfifo_sclpc_irq(int irq,=
void *dev_id)
=20
/* Check abort bit */
if (status_count & MPC52xx_SCLPC_STATUS_AT) {
=2D out_be32(&lpbfifo.regs->enable, MPC52xx_SCLPC_ENABLE_RC | MPC52xx_SCLP=
C_ENABLE_RF);
+ out_be32(&lpbfifo->regs->enable, MPC52xx_SCLPC_ENABLE_RC | MPC52xx_SCLPC=
_ENABLE_RF);
do_callback =3D 1;
goto out;
}
=20
=2D if (!mpc52xx_lpbfifo_is_dma(rflags)) {
+ if (mpc52xx_lpbfifo_is_dma(rflags)) {
+ if (!lpbfifo->irqs_pending)
+ do_callback =3D 1;
+ }
+ else {
=20
/* Bytes done */
status_count &=3D MPC52xx_SCLPC_STATUS_BYTES_DONE_MASK;
=20
if (!mpc52xx_lpbfifo_is_write(rflags)) {
/* copy the data out of the FIFO */
=2D reg =3D &lpbfifo.regs->fifo_data;
+ reg =3D &lpbfifo->regs->fifo_data;
data =3D req->data + req->pos;
for (i =3D 0; i < status_count; i +=3D sizeof(u32))
*data++ =3D in_be32(reg);
@@ -288,13 +309,10 @@ static irqreturn_t mpc52xx_lpbfifo_sclpc_irq(int irq,=
void *dev_id)
else
do_callback =3D 1;
}
=2D else {
=2D do_callback =3D 1;
=2D }
=20
out:
/* Clear the IRQ */
=2D out_8(&lpbfifo.regs->bytes_done_status.status, BIT(0));
+ out_8(&lpbfifo->regs->bytes_done_status.status, BIT(0));
=20
req->last_byte =3D ((u8 *)req->data)[req->size - 1];
=20
@@ -304,11 +322,11 @@ out:
/* When the do_callback flag is set; it means the transfer is finished
* so set the FIFO as idle */
if (do_callback) {
=2D lpbfifo.req =3D NULL;
=2D out_be32(&lpbfifo.regs->enable, MPC52xx_SCLPC_ENABLE_RC | MPC52xx_SCLP=
C_ENABLE_RF);
+ lpbfifo->req =3D NULL;
+ out_be32(&lpbfifo->regs->enable, MPC52xx_SCLPC_ENABLE_RC | MPC52xx_SCLPC=
_ENABLE_RF);
=20
req->irq_ticks +=3D get_tbl() - ts;
=2D spin_unlock_irqrestore(&lpbfifo.lock, flags);
+ spin_unlock_irqrestore(&lpbfifo->lock, flags);
=20
/* Spinlock is released; it is now safe to call the callback */
if (req->callback)
@@ -318,7 +336,7 @@ out:
}
else {
req->irq_ticks +=3D get_tbl() - ts;
=2D spin_unlock_irqrestore(&lpbfifo.lock, flags);
+ spin_unlock_irqrestore(&lpbfifo->lock, flags);
=20
return IRQ_HANDLED;
}
@@ -354,14 +372,30 @@ static irqreturn_t mpc52xx_lpbfifo_bcom_irq(int irq, =
void *dev_id)
bcom_retrieve_buffer(lpbfifo->bcom_cur_task, NULL, NULL);
// req->irq_ticks +=3D get_tbl() - ts;
=20
=2D if (lpbfifo->req) {
=2D if (mpc52xx_lpbfifo_is_write(lpbfifo->req->flags))
=2D dma_unmap_single(lpbfifo->dev, lpbfifo->req->data_dma, lpbfifo->req->=
size, DMA_TO_DEVICE);
=2D else
=2D dma_unmap_single(lpbfifo->dev, lpbfifo->req->data_dma, lpbfifo->req->=
size, DMA_FROM_DEVICE);
=2D } else
=2D {
=2D dev_err(lpbfifo->dev, "request is NULL\n");
+ lpbfifo->irqs_pending &=3D ~MPC52XX_LPBFIFO_PENDING_BCOM;
+ if (!lpbfifo->irqs_pending) {
+ struct mpc52xx_lpbfifo_request *req =3D lpbfifo->req;
+
+ if (req) {
+ if (mpc52xx_lpbfifo_is_write(lpbfifo->req->flags))
+ dma_unmap_single(lpbfifo->dev, lpbfifo->req->data_dma, lpbfifo->req->s=
ize, DMA_TO_DEVICE);
+ else
+ dma_unmap_single(lpbfifo->dev, lpbfifo->req->data_dma, lpbfifo->req->s=
ize, DMA_FROM_DEVICE);
+
+ lpbfifo->req =3D NULL;
+ out_be32(&lpbfifo->regs->enable, MPC52xx_SCLPC_ENABLE_RC | MPC52xx_SCLP=
C_ENABLE_RF);
+
+ spin_unlock_irqrestore(&lpbfifo->lock, flags);
+
+ /* Spinlock is released; it is now safe to call the callback */
+ if (req->callback)
+ req->callback(req);
+
+ return IRQ_HANDLED;
+ }
+ else {
+ dev_err(lpbfifo->dev, "bogus BCOM IRQ\n");
+ }
}
=20
spin_unlock_irqrestore(&lpbfifo->lock, flags);
@@ -451,8 +485,8 @@ mpc52xx_lpbfifo_probe(struct of_device *op, const struc=
t of_device_id *match)
if (lpbfifo.dev !=3D NULL)
return -ENOSPC;
=20
=2D lpbfifo.irq =3D irq_of_parse_and_map(op->node, 0);
=2D if (!lpbfifo.irq)
+ lpbfifo.sclpc_irq =3D irq_of_parse_and_map(op->node, 0);
+ if (!lpbfifo.sclpc_irq)
return -ENODEV;
=20
if (of_address_to_resource(op->node, 0, &res))
@@ -468,7 +502,7 @@ mpc52xx_lpbfifo_probe(struct of_device *op, const struc=
t of_device_id *match)
out_be32(&lpbfifo.regs->enable, MPC52xx_SCLPC_ENABLE_RC | MPC52xx_SCLPC_E=
NABLE_RF);
=20
/* register the interrupt handler */
=2D rc =3D request_irq(lpbfifo.irq, mpc52xx_lpbfifo_sclpc_irq, 0,
+ rc =3D request_irq(lpbfifo.sclpc_irq, mpc52xx_lpbfifo_sclpc_irq, 0,
"mpc52xx-lpbfifo", &lpbfifo);
if (rc)
goto err_irq;
@@ -539,7 +573,7 @@ static int __devexit mpc52xx_lpbfifo_remove(struct of_d=
evice *op)
free_irq(bcom_get_task_irq(lpbfifo.bcom_rx_task), &lpbfifo);
bcom_gen_bd_rx_release(lpbfifo.bcom_rx_task);
=20
=2D free_irq(lpbfifo.irq, &lpbfifo);
+ free_irq(lpbfifo.sclpc_irq, &lpbfifo);
iounmap(lpbfifo.regs);
lpbfifo.regs =3D NULL;
lpbfifo.dev =3D NULL;
@@ -547,7 +581,7 @@ static int __devexit mpc52xx_lpbfifo_remove(struct of_d=
evice *op)
return 0;
}
=20
=2Dstatic struct of_device_id mpc52xx_lpbfifo_match[] __devinitconst =3D {
+static const struct of_device_id mpc52xx_lpbfifo_match[] __devinitconst =
=3D {
{ .compatible =3D "fsl,mpc5200-lpbfifo", },
{},
};
=2D-=20
1.6.5.5
=2D-=20
Roman Fietze Telemotive AG B=FCro M=FChlhausen
Breitwiesen 73347 M=FChlhausen
Tel.: +49(0)7335/18493-45 http://www.telemotive.de
^ permalink raw reply
* [PATCH 11/13] powerpc/5200: LocalPlus driver: move RAM DMA address from request to driver
From: Roman Fietze @ 2009-12-22 7:10 UTC (permalink / raw)
To: linuxppc-dev
In-Reply-To: <200912220755.09756.roman.fietze@telemotive.de>
Signed-off-by: Roman Fietze <roman.fietze@telemotive.de>
=2D--
arch/powerpc/include/asm/mpc52xx.h | 1 -
arch/powerpc/platforms/52xx/mpc52xx_lpbfifo.c | 13 +++++++------
2 files changed, 7 insertions(+), 7 deletions(-)
diff --git a/arch/powerpc/include/asm/mpc52xx.h b/arch/powerpc/include/asm/=
mpc52xx.h
index 043458e..91c65d0 100644
=2D-- a/arch/powerpc/include/asm/mpc52xx.h
+++ b/arch/powerpc/include/asm/mpc52xx.h
@@ -347,7 +347,6 @@ struct mpc52xx_lpbfifo_request {
=20
/* Memory address */
void *data;
=2D dma_addr_t data_dma;
=20
/* Details of transfer */
size_t size;
diff --git a/arch/powerpc/platforms/52xx/mpc52xx_lpbfifo.c b/arch/powerpc/p=
latforms/52xx/mpc52xx_lpbfifo.c
index cd8dc69..b2c92f5 100644
=2D-- a/arch/powerpc/platforms/52xx/mpc52xx_lpbfifo.c
+++ b/arch/powerpc/platforms/52xx/mpc52xx_lpbfifo.c
@@ -41,6 +41,7 @@ struct mpc52xx_lpbfifo {
=20
/* Current state data */
struct mpc52xx_lpbfifo_request *req;
+ dma_addr_t data_dma;
unsigned short irqs_pending;
int dma_irqs_enabled;
};
@@ -49,7 +50,7 @@ struct mpc52xx_lpbfifo {
static struct mpc52xx_lpbfifo lpbfifo;
=20
=20
=2D/* The order of the raised interrupts of SCLPC and BCOM cann not be
+/* The order of the raised interrupts of SCLPC and BCOM cannot be
* predicted, because it depends on the individual BCOM and CPU
* loads. So in DMA mode we just wait for both until we finish the
* transaction. */
@@ -160,7 +161,7 @@ static void mpc52xx_lpbfifo_kick(struct mpc52xx_lpbfifo=
_request *req)
out_be32(&lpbfifo.regs->fifo_alarm, MPC52xx_SCLPC_FIFO_SIZE - 28);
out_be32(&lpbfifo.regs->fifo_control, MPC52xx_SLPC_FIFO_CONTROL_GR(7));
lpbfifo.bcom_cur_task =3D lpbfifo.bcom_tx_task;
=2D req->data_dma =3D dma_map_single(lpbfifo.dev, req->data, req->size, D=
MA_TO_DEVICE);
+ lpbfifo.data_dma =3D dma_map_single(lpbfifo.dev, req->data, req->size, =
DMA_TO_DEVICE);
} else {
out_be32(&lpbfifo.regs->fifo_alarm, MPC52xx_SCLPC_FIFO_SIZE - 1);
out_be32(&lpbfifo.regs->fifo_control, MPC52xx_SLPC_FIFO_CONTROL_GR(0));
@@ -177,7 +178,7 @@ static void mpc52xx_lpbfifo_kick(struct mpc52xx_lpbfifo=
_request *req)
lpbfifo.dma_irqs_enabled =3D 1;
}
}
=2D req->data_dma =3D dma_map_single(lpbfifo.dev, req->data, req->size, D=
MA_FROM_DEVICE);
+ lpbfifo.data_dma =3D dma_map_single(lpbfifo.dev, req->data, req->size, =
DMA_FROM_DEVICE);
}
=20
/* error irq & master enabled bit */
@@ -187,7 +188,7 @@ static void mpc52xx_lpbfifo_kick(struct mpc52xx_lpbfifo=
_request *req)
=20
bd =3D bcom_prepare_next_buffer(lpbfifo.bcom_cur_task);
bd->status =3D tc;
=2D bd->data[0] =3D req->data_dma + req->pos;
+ bd->data[0] =3D lpbfifo.data_dma + req->pos;
bcom_submit_next_buffer(lpbfifo.bcom_cur_task, NULL);
}
=20
@@ -378,9 +379,9 @@ static irqreturn_t mpc52xx_lpbfifo_bcom_irq(int irq, vo=
id *dev_id)
=20
if (req) {
if (mpc52xx_lpbfifo_is_write(lpbfifo->req->flags))
=2D dma_unmap_single(lpbfifo->dev, lpbfifo->req->data_dma, lpbfifo->req-=
>size, DMA_TO_DEVICE);
+ dma_unmap_single(lpbfifo->dev, lpbfifo->data_dma, lpbfifo->req->size, =
DMA_TO_DEVICE);
else
=2D dma_unmap_single(lpbfifo->dev, lpbfifo->req->data_dma, lpbfifo->req-=
>size, DMA_FROM_DEVICE);
+ dma_unmap_single(lpbfifo->dev, lpbfifo->data_dma, lpbfifo->req->size, =
DMA_FROM_DEVICE);
=20
lpbfifo->req =3D NULL;
out_be32(&lpbfifo->regs->enable, MPC52xx_SCLPC_ENABLE_RC | MPC52xx_SCLP=
C_ENABLE_RF);
=2D-=20
1.6.5.5
=2D-=20
Roman Fietze Telemotive AG B=FCro M=FChlhausen
Breitwiesen 73347 M=FChlhausen
Tel.: +49(0)7335/18493-45 http://www.telemotive.de
^ permalink raw reply
* [PATCH 12/13] mpc52xx: add mpc5200-localplus-test LocalPlus test driver
From: Roman Fietze @ 2009-12-22 7:12 UTC (permalink / raw)
To: linuxppc-dev
In-Reply-To: <200912220755.09756.roman.fietze@telemotive.de>
The original version was done by Grant Likely. This is a modified
version using the platform LocalPlus driver.
Signed-off-by: Roman Fietze <roman.fietze@telemotive.de>
=2D--
drivers/misc/mpc5200-localplus-test.c | 879 +++++++++++++++++++++++++++++=
++++
1 files changed, 879 insertions(+), 0 deletions(-)
create mode 100644 drivers/misc/mpc5200-localplus-test.c
diff --git a/drivers/misc/mpc5200-localplus-test.c b/drivers/misc/mpc5200-l=
ocalplus-test.c
new file mode 100644
index 0000000..dcfb282
=2D-- /dev/null
+++ b/drivers/misc/mpc5200-localplus-test.c
@@ -0,0 +1,879 @@
+/*
+ * LocalPlusBus performance tests.
+ *
+ * Copyright (C) Secret Lab Technologies Ltd. 2008-2009
+ *
+ * This file is licensed under the terms of the GNU General Public License
+ * version 2. This program is licensed "as is" without any warranty of any
+ * kind, whether express or implied.
+ *
+ * This file implements a set of LocalPlus bus performance tests when using
+ * direct Programmed IO (PIO), the LocalPlus FIFO, and when using the
+ * Bestcomm DMA engine to transfer data. It can be compiled into the
+ * kernel or loaded as a module.
+ *
+ * The test module is controlled via files in the sysfs filesystem.
+ * Special control files are created in
+ * /sys/devices/platform/lpbtest.0 which control the tests and report
+ * the results. Test parameters are set by writing values into the
+ * parameter files (blocksize, blockcount, bpt, period, and type).
+ * The test is started and stopped with the 'action' file. Results
+ * are retrieved by reading the contents of the 'results' file.
+ *
+ * The following parameters can be modified:
+ * blocksize: number of bytes to transfer in each block.
+ * blockcount: number of blocks to transfer per timer tick.
+ * period: period of timer or distance (leading '+') in jiffies
+ * ("[1-9]{1,}j") or microseconds. Every timer tick will
+ * start a new transfer of data blocks. If this value is 0,
+ * new transfers will be started as quickly as possible using
+ * a tasklet.
+ * type: type of test; may be 'ram', 'fifo' or 'bcom'.
+ * chipselect: chipselect to use for transfer
+ *
+ * The first test copies contents of an LPB address range using a memcpy.
+ * Usage:
+ * $ echo ram > /sys/devices/platform/lpbtest.0/type
+ * $ echo start > /sys/devices/platform/lpbtest.0/action
+ * $ sleep 5s
+ * $ echo stop > /sys/devices/platform/lpbtest.0/action
+ *
+ * The second test copies contents of an LPB range to RAM using the
+ * LocalPlus FIFO. The FIFO ISR copies each packet from the FIFO to RAM.
+ * Usage:
+ * $ echo fifo > /sys/devices/platform/lpbtest.0/type
+ * $ echo start > /sys/devices/platform/lpbtest.0/action
+ * $ sleep 5s
+ * $ echo stop > /sys/devices/platform/lpbtest.0/action
+ *
+ * The third test copies contents of an LPB range to RAM using both the FI=
=46O
+ * and the Bestcomm DMA engine.
+ *
+ * Usage:
+ * $ echo bcom > /sys/devices/platform/lpbtest.0/type
+ * $ echo start > /sys/devices/platform/lpbtest.0/action
+ * $ sleep 5s
+ * $ echo stop > /sys/devices/platform/lpbtest.0/action
+ *
+ * All sysfs entries can be read by using cat <parameter>
+ * e.g. cat /sys/devices/platform/lpbtest.0/type will show the test type
+ *
+ * The following is a useful command to dump out all the state of the modu=
le:
+ * $ grep '' *
+ *
+ */
+
+// #define DEBUG
+
+#include <linux/ctype.h>
+#include <linux/device.h>
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/string.h>
+#include <linux/mempool.h>
+#include <linux/timer.h>
+#include <linux/jiffies.h>
+#include <linux/io.h>
+#include <linux/interrupt.h>
+#include <linux/of_device.h>
+#include <linux/of_platform.h>
+#include <sysdev/bestcomm/bestcomm.h>
+#include <sysdev/bestcomm/gen_bd.h>
+#include <sysdev/bestcomm/bestcomm_priv.h>
+#include <asm/page.h>
+#include <asm/time.h>
+#include <asm/cacheflush.h>
+#include <asm/div64.h>
+
+MODULE_AUTHOR("Steven Cavanagh <scavan...@secretlab.ca>");
+MODULE_LICENSE("GPL");
+
+
+#define DRVNAME "lpbtest"
+
+#define LPBTEST_FLASH_BASE_ADDR (0xff000000)
+#define LPBTEST_FLASH_SIZE (0x01000000) /* 16 MiB */
+
+#define LPBTEST_BLOCK_SIZE_MIN 4
+#define LPBTEST_BLOCK_SIZE_MAX (0x20000) /* 128 KiB */
+
+#define LPBTEST_JIFFIES_MIN 0
+
+/* Address of SCLPC relative to MBAR
+ *
+ * TODO: move to OF tree?
+ */
+#define MPC52xx_SCLPC_OFFSET 0x3C00
+
+
+/**
+ * lpbtest - Private driver data
+ * @lpb_regs_base: pointer to the LPB's registers
+ * @irq: IRQ of this LPB FIFO
+ * @dev: struct device pointer
+ */
+struct lpbtest {
+ struct device *dev;
+
+ /* sysfs attributes */
+ int action;
+ size_t type;
+ int write; /* true for write test */
+ size_t blockcount;
+ size_t blocksize;
+ unsigned long period; /* in jiffies */
+ int period_us; /* true: period is in us, false: in jiffies */
+ int period_delta; /* true: period is time between the
+ * transactions, false: tme between
+ * start of two transactions */
+ unsigned int chipselect;
+ int verify;
+
+ spinlock_t lock;
+
+ void *ram_virt;
+
+ void *dev_virt;
+ phys_addr_t dev_phys;
+
+ struct mpc52xx_lpbfifo_request req;
+
+ /* Timeslice timer */
+ struct timer_list timer;
+ unsigned long tnext; /* next deadline; in jiffies */
+
+ /* Statistics */
+ unsigned long irq_time;
+ unsigned long timer_time;
+ unsigned long bcom_time;
+ unsigned long start_time;
+ unsigned long stop_time;
+ unsigned long long data_read;
+ int overrun_count;
+
+ /* state variables */
+ size_t next_block; /* Number of next block to send. If
+ * this is >=3D blockcount, then all the
+ * transfers are finished */
+
+ size_t rcvd_blocks; /* Number of blocks received. */
+};
+
+/* Helper functions to test selected behaviour */
+static inline int lpbtest_isfifo(struct lpbtest *priv)
+{
+ return priv->type =3D=3D 1;
+}
+
+
+static void lpbtest_stop(struct lpbtest *priv)
+{
+ priv->stop_time =3D get_tbl();
+ dev_dbg(priv->dev, "%s() stop_time=3D%lu\n", __FUNCTION__, priv->stop_tim=
e);
+}
+
+
+static inline unsigned long lpbtest_period_in_jiffies(struct lpbtest *priv)
+{
+ return priv->period_us ? usecs_to_jiffies(priv->period) : priv->period;
+}
+
+
+/* Start timer. In case finished is false this routine is called at
+ * the start of a transfer, if finished is true at the end of a
+ * transfer.
+ *
+ * In the first case we only start the timer if a period >
+ * LPBTEST_PERIOD_ISO_LIMIT is set, in the second case we only start
+ * the timer if a period <=3D LPBTEST_PERIOD_ISO_LIMIT is set.
+ */
+static void lpbtest_start_timer(struct lpbtest *priv, int finished)
+{
+ int ret;
+ if (finished) {
+ if (priv->period_delta) {
+ priv->tnext =3D lpbtest_period_in_jiffies(priv);
+#if LPBTEST_JIFFIES_MIN
+ if (priv->tnext < LPBTEST_JIFFIES_MIN)
+ priv->tnext =3D LPBTEST_JIFFIES_MIN;
+#endif
+ priv->tnext +=3D jiffies;
+ ret =3D mod_timer(&priv->timer, priv->tnext);
+ dev_dbg(priv->dev, "%s(%d): j=3D%lu tnext=3D%lu r=3D%d\n", __FUNCTION__=
, finished, jiffies, priv->tnext, ret);
+ return;
+ }
+ }
+ else {
+ if (!priv->period_delta) {
+ priv->tnext +=3D lpbtest_period_in_jiffies(priv);
+ /* dev_dbg(dev, "%s: jiffies=3D%lu time=3D%lu\n", __FUNCTION__, jiffies=
, priv->tnext); */
+ if (time_is_before_jiffies(priv->tnext)) {
+ dev_err(priv->dev, "Timeslice overrun by %i us; aborting\n",
+ jiffies_to_usecs(jiffies - priv->tnext));
+ lpbtest_stop(priv);
+ return;
+ }
+
+ ret =3D mod_timer(&priv->timer, priv->tnext);
+ dev_dbg(priv->dev, "%s(%d): j=3D%lu tnext=3D%lu r=3D%d\n", __FUNCTION__=
, finished, jiffies, priv->tnext, ret);
+ return;
+ }
+ }
+
+ dev_dbg(priv->dev, "%s(%d): j=3D%lu no action\n", __FUNCTION__, finished,=
jiffies);
+}
+
+static inline int lpbtest_check_stop(struct device *dev)
+{
+ struct lpbtest *priv =3D dev_get_drvdata(dev);
+
+ dev_dbg(dev, "%s()\n", __FUNCTION__);
+
+ if (!priv->action) {
+ lpbtest_stop(priv);
+ return 1;
+ }
+
+ return 0;
+}
+
+
+static void lpbtest_read_channels_to_ram(unsigned long _dev)
+{
+ struct device *dev =3D (struct device *)_dev;
+ struct lpbtest *priv =3D dev_get_drvdata(dev);
+ int i;
+
+ dev_dbg(dev, "%lu: %s()\n", jiffies, __FUNCTION__);
+
+ if (!lpbtest_check_stop(dev)) {
+ const unsigned long tstart =3D get_tbl();
+
+ lpbtest_start_timer(priv, 0);
+
+ /* Assume, that all channels have data available */
+ for (i =3D 0; i < priv->blockcount; i++) {
+ memcpy(priv->ram_virt, priv->dev_virt, priv->blocksize);
+ priv->data_read +=3D priv->blocksize;
+ }
+
+ priv->timer_time +=3D get_tbl() - tstart;
+
+ lpbtest_start_timer(priv, 1);
+ }
+}
+
+
+static void lpbtest_fifo_done(struct mpc52xx_lpbfifo_request *req)
+{
+ struct lpbtest *priv =3D req->priv;
+
+ // dev_info(priv->dev, "%s()\n", __FUNCTION__);
+
+ if (priv->verify) {
+ if (memcmp(priv->ram_virt, priv->dev_virt, priv->blocksize)) {
+ print_hex_dump(KERN_INFO, "RAM: ", DUMP_PREFIX_OFFSET,
+ 16, sizeof(uint32_t),
+ priv->ram_virt, priv->blocksize, 0);
+ print_hex_dump(KERN_INFO, "DEV: ", DUMP_PREFIX_OFFSET,
+ 16, sizeof(uint32_t),
+ priv->dev_virt, priv->blocksize, 0);
+ }
+ else {
+ /* dev_info(priv->dev, "memcmp OK\n"); */
+ }
+ }
+
+ lpbtest_start_timer(priv, 1);
+}
+
+
+static void lpbtest_do_next_transfer(struct lpbtest *priv)
+{
+ dev_dbg(priv->dev, "%s()\n", __FUNCTION__);
+
+ if (priv->next_block < priv->blockcount) {
+
+ int err;
+ struct mpc52xx_lpbfifo_request *req =3D &priv->req;
+
+ memset(req, 0, sizeof(*req));
+ req->cs =3D priv->chipselect;
+ req->offset =3D priv->dev_phys - LPBTEST_FLASH_BASE_ADDR;
+ req->data =3D priv->ram_virt;
+ req->size =3D priv->blocksize;
+ req->pos =3D 0;
+ req->flags =3D MPC52XX_LPBFIFO_FLAG_NO_INCREMENT;
+ req->flags |=3D priv->write ? MPC52XX_LPBFIFO_FLAG_WRITE : MPC52XX_LPBFI=
=46O_FLAG_READ;
+ if (lpbtest_isfifo(priv))
+ req->flags |=3D MPC52XX_LPBFIFO_FLAG_NO_DMA;
+ req->callback =3D lpbtest_fifo_done;
+ req->priv =3D priv;
+
+ priv->next_block++;
+ dev_dbg(priv->dev, "%s: next_block=3D%zu\n", __FUNCTION__, priv->next_bl=
ock);
+
+ memset(priv->ram_virt, 0x55, priv->blocksize);
+ err =3D mpc52xx_lpbfifo_submit(req);
+ if (err) {
+ dev_err(priv->dev, "cannot submit FIFO request: %d\n", err);
+ lpbtest_stop(priv);
+ }
+ }
+}
+
+static void lpbtest_read_channels(unsigned long _dev)
+{
+ struct device *dev =3D (struct device *)_dev;
+
+ dev_dbg(dev, "%lu: %s()\n", jiffies, __FUNCTION__);
+
+ if (!lpbtest_check_stop(dev)) {
+ struct lpbtest *priv =3D dev_get_drvdata(dev);
+ const unsigned long tstart =3D get_tbl();
+
+ lpbtest_start_timer(priv, 0);
+
+ /* This line is the FIFO throttle, the faster the next packet
+ * is cleared, the faster the FIFO can be read and filled by
+ * the IRQ. The ISR will stop handling the FIFO, when all the
+ * channels have been read.
+ */
+ priv->next_block =3D priv->rcvd_blocks =3D 0;
+ // dev_dbg(dev, "%s: next_block=3D%zu\n", __FUNCTION__, priv->next_block=
);
+ lpbtest_do_next_transfer(priv);
+
+ priv->timer_time +=3D get_tbl() - tstart;
+ }
+}
+
+
+static const struct lpbtest_type {
+ char *name;
+ void (*test_timer)(unsigned long);
+} lpbtest_type[] =3D {
+ {
+ .name =3D "ram",
+ .test_timer =3D lpbtest_read_channels_to_ram,
+ },
+ {
+ .name =3D "fifo",
+ .test_timer =3D lpbtest_read_channels,
+ },
+ {
+ .name =3D "bcom",
+ .test_timer =3D lpbtest_read_channels,
+ }
+};
+
+/* ---------------------------------------------------------------------
+ * sysfs interfaces
+ * --------------------------------------------------------------------- */
+static ssize_t lpbtest_set_type(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct lpbtest *priv =3D dev_get_drvdata(dev);
+ const char *name;
+ size_t i;
+
+ for (i =3D 0; i < ARRAY_SIZE(lpbtest_type); i++) {
+ name =3D lpbtest_type[i].name;
+
+ if (count < strlen(name))
+ continue;
+
+ if (strncmp(buf, name, strlen(name)) =3D=3D 0) {
+ priv->type =3D i;
+ return count;
+ }
+ }
+
+ return -EINVAL;
+}
+
+static ssize_t lpbtest_show_type(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct lpbtest *priv =3D dev_get_drvdata(dev);
+ return sprintf(buf, "%s\n", lpbtest_type[priv->type].name);
+}
+
+static ssize_t lpbtest_set_dir(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct lpbtest *priv =3D dev_get_drvdata(dev);
+
+ if (strncmp(buf, "read", strlen("read")) =3D=3D 0) {
+ priv->write =3D 0;
+ }
+ else if (strncmp(buf, "write", strlen("write")) =3D=3D 0) {
+ priv->write =3D 1;
+ }
+ else {
+ dev_err(dev, "Usage: echo [read,write] > dir\n");
+ return -EINVAL;
+ }
+
+ return count;
+}
+
+static ssize_t lpbtest_show_dir(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct lpbtest *priv =3D dev_get_drvdata(dev);
+ return sprintf(buf, "%s\n", priv->write ? "write" : "read");
+}
+
+static ssize_t lpbtest_set_action(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct lpbtest *priv =3D dev_get_drvdata(dev);
+ const struct lpbtest_type *type =3D &lpbtest_type[priv->type];
+ int oldaction =3D priv->action;
+
+ if (strncmp(buf, "start", strlen("start")) =3D=3D 0) {
+ priv->action =3D 1;
+ }
+ else if (strncmp(buf, "stop", strlen("stop")) =3D=3D 0) {
+ priv->action =3D 0;
+ }
+ else {
+ dev_err(dev, "Usage: echo [start,stop] > action\n");
+ return -EINVAL;
+ }
+
+ dev_dbg(dev, "%s: action=3D%d->%d\n", __FUNCTION__, oldaction, priv->acti=
on);
+
+ if (oldaction !=3D priv->action) {
+ oldaction =3D priv->action;
+
+ if (priv->action) {
+ init_timer(&priv->timer);
+ priv->timer.function =3D type->test_timer;
+ priv->timer.data =3D (unsigned long)dev;
+
+ priv->next_block =3D priv->rcvd_blocks =3D 0;
+ priv->irq_time =3D 0;
+ priv->timer_time =3D 0;
+ priv->bcom_time =3D 0;
+ priv->stop_time =3D priv->start_time =3D get_tbl();
+ priv->data_read =3D 0;
+ priv->overrun_count =3D 0;
+
+ /* Map the device */
+ priv->dev_virt =3D ioremap(priv->dev_phys, LPBTEST_BLOCK_SIZE_MAX);
+ if (!priv->dev_virt) {
+ dev_err(dev, "Error mapping device\n");
+ return -ENOMEM;
+ }
+
+ printk(KERN_INFO "Started %s test, blocksize=3D0x%zx\n", type->name, pr=
iv->blocksize);
+
+ /* Set the expiration time for the timer. */
+ priv->tnext =3D jiffies + 1;
+ mod_timer(&priv->timer, priv->tnext);
+
+ }
+ else {
+ del_timer_sync(&priv->timer);
+ lpbtest_stop(priv);
+
+ printk(KERN_INFO "Stopped %s test, blocksize=3D0x%zx\n", type->name, pr=
iv->blocksize);
+ }
+ }
+
+ return count;
+}
+
+static ssize_t lpbtest_show_action(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ char *action;
+ struct lpbtest *priv =3D dev_get_drvdata(dev);
+
+ action =3D (priv->action =3D=3D 1) ? "start" : "stop";
+ return sprintf(buf, "%s\n", action);
+}
+
+/*
+ * Export a blockcount attr
+ */
+static ssize_t lpbtest_set_blockcount(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct lpbtest *priv =3D dev_get_drvdata(dev);
+ unsigned long temp;
+
+ if (strict_strtoul(buf, 10, &temp))
+ return -EINVAL;
+ priv->blockcount =3D temp;
+
+ return count;
+}
+
+static ssize_t lpbtest_show_blockcount(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ struct lpbtest *priv =3D dev_get_drvdata(dev);
+ return sprintf(buf, "%zu\n", priv->blockcount);
+}
+
+/*
+ * Export a blocksize attr
+ */
+static ssize_t lpbtest_set_blocksize(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct lpbtest *priv =3D dev_get_drvdata(dev);
+ unsigned long temp;
+
+ if (strict_strtoul(buf, 0, &temp))
+ return -EINVAL;
+
+ if (temp < LPBTEST_BLOCK_SIZE_MIN || temp > LPBTEST_BLOCK_SIZE_MAX)
+ return -EINVAL;
+
+ priv->blocksize =3D temp;
+ return count;
+}
+
+static ssize_t lpbtest_show_blocksize(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ struct lpbtest *priv =3D dev_get_drvdata(dev);
+ return sprintf(buf, "%zu\n", priv->blocksize);
+}
+
+static ssize_t lpbtest_set_period(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ unsigned long temp;
+ struct lpbtest *priv =3D dev_get_drvdata(dev);
+
+ const char *pb =3D buf;
+ char *pe;
+
+ priv->period_delta =3D 0;
+ if (*pb =3D=3D '+') {
+ priv->period_delta =3D 1;
+ pb++;
+ }
+ temp =3D simple_strtoul(pb, &pe, 10);
+ if (buf =3D=3D pe) {
+ dev_err(dev, "period \"%s\" empty?\n", buf);
+ return -EINVAL;
+ }
+
+ if (*pe =3D=3D 'j') {
+ priv->period_us =3D 0;
+ }
+ else if (isspace(*pe) || *pe =3D=3D '\0') {
+ priv->period_us =3D 1;
+ }
+ else {
+ dev_err(dev, "period \"%s\" invalid\n", buf);
+ return -EINVAL;
+ }
+
+ priv->period =3D temp;
+ dev_dbg(dev, "period \"%s\" =3D %s%lu %s\n",
+ buf,
+ priv->period_delta ? "+" : "", temp,
+ priv->period_us ? "us" : "j");
+
+ return count;
+}
+
+static ssize_t lpbtest_show_period(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct lpbtest *priv =3D dev_get_drvdata(dev);
+ return sprintf(buf, "%s%lu%s\n",
+ priv->period_delta ? "+" : "",
+ priv->period,
+ priv->period_us ? "" : "j");
+}
+
+static ssize_t lpbtest_set_cs(struct device *dev, struct device_attribute =
*attr,
+ const char *buf, size_t count)
+{
+ unsigned long temp;
+ struct lpbtest *priv =3D dev_get_drvdata(dev);
+
+ if (strict_strtoul(buf, 0, &temp))
+ return -EINVAL;
+
+ if (temp > 7)
+ return -EINVAL;
+
+ priv->chipselect =3D temp;
+ return count;
+}
+
+static ssize_t lpbtest_show_cs(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct lpbtest *priv =3D dev_get_drvdata(dev);
+ return sprintf(buf, "%u\n", priv->chipselect);
+}
+
+static ssize_t lpbtest_set_baseaddr(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ unsigned long temp;
+ struct lpbtest *priv =3D dev_get_drvdata(dev);
+
+ if (strict_strtoul(buf, 0, &temp))
+ return -EINVAL;
+
+ priv->dev_phys =3D temp;
+ return count;
+}
+
+static ssize_t lpbtest_show_baseaddr(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct lpbtest *priv =3D dev_get_drvdata(dev);
+ return sprintf(buf, "%llx\n", (unsigned long long) priv->dev_phys);
+}
+
+static ssize_t lpbtest_set_verify(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct lpbtest *priv =3D dev_get_drvdata(dev);
+ unsigned long temp;
+
+ if (strict_strtoul(buf, 10, &temp))
+ return -EINVAL;
+
+ priv->verify =3D temp;
+ return count;
+}
+
+static ssize_t lpbtest_show_verify(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct lpbtest *priv =3D dev_get_drvdata(dev);
+ return sprintf(buf, "%d\n", priv->verify);
+}
+
+static ssize_t lpbtest_show_results(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct lpbtest *priv =3D dev_get_drvdata(dev);
+ int c;
+ unsigned long systime;
+ unsigned long realtime;
+ unsigned int utilization;
+ unsigned long rate;
+ unsigned long netrate;
+ unsigned long long tmp;
+
+ realtime =3D priv->stop_time - priv->start_time;
+ systime =3D priv->timer_time + priv->irq_time + priv->bcom_time;
+
+ if (!realtime) {
+ realtime =3D get_tbl() - priv->start_time;
+ }
+
+ if (realtime) {
+ tmp =3D systime * 10000ULL;
+ do_div(tmp, realtime);
+ utilization =3D tmp;
+
+ tmp =3D priv->data_read;
+ tmp *=3D tb_ticks_per_usec;
+ tmp *=3D 100;
+ do_div(tmp, realtime);
+ rate =3D tmp;
+ }
+ else {
+ utilization =3D 0;
+ rate =3D 0;
+ }
+
+ if (systime) {
+ tmp =3D priv->data_read;
+ tmp *=3D tb_ticks_per_usec;
+ tmp *=3D 100;
+ do_div(tmp, systime);
+ netrate =3D tmp;
+ }
+ else {
+ netrate =3D 0;
+ }
+
+ c =3D sprintf(buf, "real\t\t:\t%10lu ticks\t%9lu us\n",
+ realtime, realtime / tb_ticks_per_usec);
+ c +=3D sprintf(buf + c, "sys\t\t:\t%10lu ticks\t%9lu us\n",
+ systime, systime / tb_ticks_per_usec);
+ c +=3D sprintf(buf + c, "timer\t\t:\t%10lu ticks\t%9lu us\n",
+ priv->timer_time, priv->timer_time / tb_ticks_per_usec);
+ c +=3D sprintf(buf + c, "fifo irq\t:\t%10lu ticks\t%9lu us\n",
+ priv->irq_time, priv->irq_time / tb_ticks_per_usec);
+ c +=3D sprintf(buf + c, "bcom irq\t:\t%10lu ticks\t%9lu us\n",
+ priv->bcom_time, priv->bcom_time / tb_ticks_per_usec);
+ c +=3D sprintf(buf + c, "overruns\t:\t%10u\n", priv->overrun_count);
+ c +=3D sprintf(buf + c, "%%CPU\t\t:\t%10u.%.2u %%\n",
+ utilization / 100, utilization % 100);
+ c +=3D sprintf(buf + c, "byte count\t:\t%10llu\n", priv->data_read);
+ c +=3D sprintf(buf + c, "net data rate\t:\t%10lu.%.2lu MB/s\n",
+ rate / 100, rate % 100);
+ c +=3D sprintf(buf + c, "max data rate\t:\t%10lu.%.2lu MB/s\n",
+ netrate / 100, netrate % 100);
+
+ return c;
+}
+
+static struct device_attribute lpbtest_attrib[] =3D {
+ __ATTR(action, S_IWUSR | S_IRUGO,
+ lpbtest_show_action, lpbtest_set_action),
+ __ATTR(blockcount, S_IWUSR | S_IRUGO,
+ lpbtest_show_blockcount, lpbtest_set_blockcount),
+ __ATTR(blocksize, S_IWUSR | S_IRUGO,
+ lpbtest_show_blocksize, lpbtest_set_blocksize),
+ __ATTR(period, S_IWUSR | S_IRUGO,
+ lpbtest_show_period, lpbtest_set_period),
+ __ATTR(chipselect, S_IWUSR | S_IRUGO,
+ lpbtest_show_cs, lpbtest_set_cs),
+ __ATTR(baseaddr, S_IWUSR | S_IRUGO,
+ lpbtest_show_baseaddr, lpbtest_set_baseaddr),
+ __ATTR(verify, S_IWUSR | S_IRUGO,
+ lpbtest_show_verify, lpbtest_set_verify),
+ __ATTR(type, S_IWUSR | S_IRUGO,
+ lpbtest_show_type, lpbtest_set_type),
+ __ATTR(dir, S_IWUSR | S_IRUGO,
+ lpbtest_show_dir, lpbtest_set_dir),
+ __ATTR(results, S_IWUSR | S_IRUGO,
+ lpbtest_show_results, NULL),
+};
+
+static void lpbtest_cleanup_sysfs(struct platform_device *pdev)
+{
+ struct device *dev =3D &pdev->dev;
+ int i;
+
+ for (i =3D 0; i < ARRAY_SIZE(lpbtest_attrib); i++)
+ device_remove_file(dev, &lpbtest_attrib[i]);
+}
+
+static int lpbtest_setup(struct device *dev)
+{
+ struct lpbtest *priv =3D dev_get_drvdata(dev);
+
+ /* Allocate a destination buffer */
+ priv->ram_virt =3D kzalloc(LPBTEST_BLOCK_SIZE_MAX, GFP_KERNEL);
+ if (!priv->ram_virt) {
+ dev_err(dev, "Error allocating test buffer\n");
+ return -ENOMEM;
+ }
+
+ return 0;
+}
+
+static int __devinit lpbtest_probe(struct platform_device *pdev)
+{
+ struct device *dev =3D &pdev->dev;
+ struct lpbtest *priv;
+
+ int ret =3D 0, i;
+
+ /* Allocate and initialize the driver private data */
+ priv =3D kzalloc(sizeof *priv, GFP_KERNEL);
+ if (!priv)
+ return -ENOMEM;
+
+ priv->dev =3D dev;
+ priv->blockcount =3D 16;
+ priv->blocksize =3D MPC52xx_SCLPC_FIFO_SIZE;
+ priv->period =3D 20000;
+ priv->period_us =3D 1;
+ priv->period_delta =3D 0;
+ priv->dev_phys =3D LPBTEST_FLASH_BASE_ADDR;
+
+ spin_lock_init(&priv->lock);
+ platform_set_drvdata(pdev, priv);
+
+ ret =3D lpbtest_setup(dev);
+ if (ret) {
+ dev_err(dev, "lpbtest_setup() error\n");
+ return ret;
+ }
+
+ /* Register the SYSFS files */
+ for (i =3D 0; i < ARRAY_SIZE(lpbtest_attrib); i++) {
+ ret =3D device_create_file(dev, &lpbtest_attrib[i]);
+ if (ret) {
+ dev_err(dev, "error creating sysfs files (%d)\n", ret);
+ lpbtest_cleanup_sysfs(pdev);
+ return ret;
+ }
+ }
+
+ return ret;
+}
+
+static int __devexit lpbtest_remove(struct platform_device *pdev)
+{
+ struct lpbtest *priv =3D platform_get_drvdata(pdev);
+
+ lpbtest_cleanup_sysfs(pdev);
+
+ del_timer(&priv->timer);
+
+ kfree(priv->ram_virt);
+ kfree(priv);
+
+ return 0;
+}
+
+static struct platform_driver lpbtest_driver =3D {
+ .driver =3D {
+ .owner =3D THIS_MODULE,
+ .name =3D DRVNAME,
+ },
+ .probe =3D lpbtest_probe,
+ .remove =3D __devexit_p(lpbtest_remove),
+};
+
+static struct platform_device *lpbtest_pdev;
+
+
+static int __init lpbtest_init(void)
+{
+ int rc;
+
+ printk(KERN_INFO "%s init\n", DRVNAME);
+
+ lpbtest_pdev =3D platform_device_register_simple(DRVNAME, 0, NULL, 0);
+ if (!lpbtest_pdev) {
+ pr_err("%s: error registering test device\n", DRVNAME);
+ return -ENOMEM;
+ }
+
+ rc =3D platform_driver_register(&lpbtest_driver);
+ if (rc)
+ platform_device_unregister(lpbtest_pdev);
+ return rc;
+}
+
+static void lpbtest_exit(void)
+{
+ platform_device_unregister(lpbtest_pdev);
+ platform_driver_unregister(&lpbtest_driver);
+}
+
+module_init(lpbtest_init);
+module_exit(lpbtest_exit);
=2D-=20
1.6.5.5
=2D-=20
Roman Fietze Telemotive AG B=FCro M=FChlhausen
Breitwiesen 73347 M=FChlhausen
Tel.: +49(0)7335/18493-45 http://www.telemotive.de
^ permalink raw reply
* [PATCH 13/13] powerpc/5200: LocalPlus driver: clean up comments
From: Roman Fietze @ 2009-12-22 7:13 UTC (permalink / raw)
To: linuxppc-dev
In-Reply-To: <200912220755.09756.roman.fietze@telemotive.de>
Signed-off-by: Roman Fietze <roman.fietze@telemotive.de>
=2D--
arch/powerpc/platforms/52xx/mpc52xx_lpbfifo.c | 14 --------------
1 files changed, 0 insertions(+), 14 deletions(-)
diff --git a/arch/powerpc/platforms/52xx/mpc52xx_lpbfifo.c b/arch/powerpc/p=
latforms/52xx/mpc52xx_lpbfifo.c
index b2c92f5..a89072a 100644
=2D-- a/arch/powerpc/platforms/52xx/mpc52xx_lpbfifo.c
+++ b/arch/powerpc/platforms/52xx/mpc52xx_lpbfifo.c
@@ -206,17 +206,6 @@ static void mpc52xx_lpbfifo_kick(struct mpc52xx_lpbfif=
o_request *req)
/**
* mpc52xx_lpbfifo_sclpc_irq - IRQ handler for LPB FIFO
*
=2D * On transmit, the dma completion irq triggers before the fifo
=2D * completion triggers. Handle the dma completion here instead of the
=2D * LPB FIFO Bestcomm task completion irq because everything is not
=2D * really done until the LPB FIFO completion irq triggers.
=2D *
=2D * In other words:
=2D * For DMA, on receive, the "Fat Lady" is the bestcom completion irq. on
=2D * transmit, the fifo completion irq is the "Fat Lady". The opera (or in
=2D * this case the DMA/FIFO operation) is not finished until the "Fat
=2D * Lady" sings.
=2D *
* Reasons for entering this routine:
* 1) PIO mode rx and tx completion irq
* 2) DMA interrupt mode tx completion irq
@@ -411,9 +400,6 @@ void mpc52xx_lpbfifo_poll(void)
{
struct mpc52xx_lpbfifo_request *req =3D lpbfifo.req;
=20
=2D /*
=2D * For more information, see comments on the "Fat Lady"=20
=2D */
if (mpc52xx_lpbfifo_is_dma(req->flags) && (req->flags & MPC52XX_LPBFIFO_F=
LAG_WRITE))
mpc52xx_lpbfifo_sclpc_irq(0, NULL);
else=20
=2D-=20
1.6.5.5
=2D-=20
Roman Fietze Telemotive AG B=FCro M=FChlhausen
Breitwiesen 73347 M=FChlhausen
Tel.: +49(0)7335/18493-45 http://www.telemotive.de
^ permalink raw reply
* Re: [PATCH 05/13] powerpc/5200: LocalPlus driver: fix DMA TX interrupt request
From: Grant Likely @ 2009-12-22 7:20 UTC (permalink / raw)
To: Roman Fietze; +Cc: linuxppc-dev
In-Reply-To: <200912220802.50090.roman.fietze@telemotive.de>
On Tue, Dec 22, 2009 at 12:02 AM, Roman Fietze
<roman.fietze@telemotive.de> wrote:
>
> Signed-off-by: Roman Fietze <roman.fietze@telemotive.de>
> ---
> =A0arch/powerpc/platforms/52xx/mpc52xx_lpbfifo.c | =A0 =A04 ++--
> =A01 files changed, 2 insertions(+), 2 deletions(-)
>
> diff --git a/arch/powerpc/platforms/52xx/mpc52xx_lpbfifo.c b/arch/powerpc=
/platforms/52xx/mpc52xx_lpbfifo.c
> index 2fd1f3f..1e4f725 100644
> --- a/arch/powerpc/platforms/52xx/mpc52xx_lpbfifo.c
> +++ b/arch/powerpc/platforms/52xx/mpc52xx_lpbfifo.c
> @@ -460,9 +460,9 @@ mpc52xx_lpbfifo_probe(struct of_device *op, const str=
uct of_device_id *match)
> =A0 =A0 =A0 =A0if (!lpbfifo.bcom_tx_task)
> =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0goto err_bcom_tx;
>
> - =A0 =A0 =A0 rc =3D request_irq(bcom_get_task_irq(lpbfifo.bcom_rx_task),
> + =A0 =A0 =A0 rc =3D request_irq(bcom_get_task_irq(lpbfifo.bcom_tx_task),
> =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 mpc52xx_lpbfifo_bcom_irq,=
0,
> - =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0"mpc52xx-lpbfifo-rx", &l=
pbfifo);
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0"mpc52xx-lpbfifo-tx", &l=
pbfifo);
Is this really what you want? In the TX path, it is the FIFO irq that
indicates the transfer is complete (ie, the DMA fills the FIFO and
finishes before the FIFO has drained). If both irqs were enabled,
then the DMA irq would fire first, followed by the FIFO irq. For the
use cases I was dealing with, the next transfer cannot be kicked off
until the FIFO has drained.
In the RX path, the DMA irq indicates that the transfer is complete
(ie, the localbus fills the FIFO and finishes before the DMA drains
the FIFO.
For my use cases, the RX DMA irq was always required, but the TX DMA
irq was never needed.
Does your use case differ?
g.
--=20
Grant Likely, B.Sc., P.Eng.
Secret Lab Technologies Ltd.
^ permalink raw reply
* Re: [PATCH 05/13] powerpc/5200: LocalPlus driver: fix DMA TX interrupt request
From: Roman Fietze @ 2009-12-22 7:42 UTC (permalink / raw)
To: Grant Likely; +Cc: linuxppc-dev
In-Reply-To: <fa686aa40912212320v27c22752wbf63f1d2d4b61129@mail.gmail.com>
Hello Grant,
On Tuesday 22 December 2009 08:20:16 Grant Likely wrote:
> Is this really what you want? In the TX path, it is the FIFO irq
> that indicates the transfer is complete (ie, the DMA fills the FIFO
> and finishes before the FIFO has drained). If both irqs were
> enabled, then the DMA irq would fire first, followed by the FIFO
> irq. For the use cases I was dealing with, the next transfer cannot
> be kicked off until the FIFO has drained.
I added another patch (PATCH 10/13) that takes care of this problem.
Debugging output (some tiny printk's when I detected the "abnormal"
case) showed, that when receiving data one cannot predict the order of
the the SDMA and SCLPC interrupt, esp. when e.g. the BestComm has
other tasks with higher prio running, in my case ATA+FEC. I hope I
didn't kill the TX side with this change, because, as I said, could
not yet test it.
Two possible solutions came into my mind:
1) let the SDMA interrupts run freely in parallel and just use the
SCLPC interrupt to detect a finished transaction.
2) Always wait for both, the second one defined the end of the
transaction. IMHO some waste of time.
> In the RX path, the DMA irq indicates that the transfer is complete
> (ie, the localbus fills the FIFO and finishes before the DMA drains
> the FIFO.
See above. That's not always true as stress tests showed. I had cases
where the BestComm was second. The SDMA beeing first is almost always
true for non BD tasks (there is one available in the 2.4.25, but not
in the 2.6 yet), and is mostly true even with BD tasks, but not 100%.
> For my use cases, the RX DMA irq was always required, but the TX DMA
> irq was never needed.
Unloading the module caused an oops when I did not request the TX SDMA
IRQ. Have to check that again.
> Does your use case differ?
I'm currently using your modified lpc test driver and I am currently
in the process of porting a 2.4.25 MOST150 FPGA driver to 2.6 using
the platform LPC driver. As soon as I have results using the FPGA
driver (very high load) I will come back with the results or probably
further patches.
One of them might be increasing the number of BDs again, because the
BestComm seems to take some time until it gives them back to the
world, and then sometimes in batches. I had a similar problem on a
high speed SPI link using the BestComm.
Roman
=2D-=20
Roman Fietze Telemotive AG B=FCro M=FChlhausen
Breitwiesen 73347 M=FChlhausen
Tel.: +49(0)7335/18493-45 http://www.telemotive.de
^ permalink raw reply
* [PATCH 1/2] Adding PCI-E support for PowerPC 460SX based SOC.
From: tmarri @ 2009-12-22 8:49 UTC (permalink / raw)
To: jwboyer, linuxppc-dev; +Cc: linuxppc-dev, writetomarri, tmarri
From: Tirumala Marri <tmarri@amcc.com>
Signed-off-by: Tirumala Marri <tmarri@amcc.com>
---
Kerenl:2.6.33-rc1
Testing: This patch has been tested on 460SX based redwood board . One board configured as
root complex and other as Endpoint. Checked for link up . From root complex lspci command
shows the end point. Also programmed IO tested using loop back as well as board to board.
---
arch/powerpc/boot/dts/redwood.dts | 122 +++++++++++++++++++++++++++++++++++++
arch/powerpc/sysdev/ppc4xx_pci.c | 119 ++++++++++++++++++++++++++++++++++++
arch/powerpc/sysdev/ppc4xx_pci.h | 58 +++++++++++++++++
3 files changed, 299 insertions(+), 0 deletions(-)
diff --git a/arch/powerpc/boot/dts/redwood.dts b/arch/powerpc/boot/dts/redwood.dts
index d2af32e..81636c0 100644
--- a/arch/powerpc/boot/dts/redwood.dts
+++ b/arch/powerpc/boot/dts/redwood.dts
@@ -234,10 +234,132 @@
has-inverted-stacr-oc;
has-new-stacr-staopc;
};
+ };
+ PCIE0: pciex@d00000000 {
+ device_type = "pci";
+ #interrupt-cells = <1>;
+ #size-cells = <2>;
+ #address-cells = <3>;
+ compatible = "ibm,plb-pciex-460sx", "ibm,plb-pciex";
+ primary;
+ port = <0x0>; /* port number */
+ reg = <0x0000000d 0x00000000 0x20000000 /* Config space access */
+ 0x0000000c 0x10000000 0x00001000>; /* Registers */
+ dcr-reg = <0x100 0x020>;
+ sdr-base = <0x300>;
+
+ /* Outbound ranges, one memory and one IO,
+ * later cannot be changed
+ */
+ ranges = <0x02000000 0x00000000 0x80000000 0x0000000e 0x00000000 0x00000000 0x80000000
+ 0x01000000 0x00000000 0x00000000 0x0000000f 0x80000000 0x00000000 0x00010000>;
+
+ /* Inbound 2GB range starting at 0 */
+ dma-ranges = <0x42000000 0x0 0x0 0x0 0x0 0x0 0x80000000>;
+ /* This drives busses 10 to 0x1f */
+ bus-range = <0x10 0x1f>;
+
+ /* Legacy interrupts (note the weird polarity, the bridge seems
+ * to invert PCIe legacy interrupts).
+ * We are de-swizzling here because the numbers are actually for
+ * port of the root complex virtual P2P bridge. But I want
+ * to avoid putting a node for it in the tree, so the numbers
+ * below are basically de-swizzled numbers.
+ * The real slot is on idsel 0, so the swizzling is 1:1
+ */
+ interrupt-map-mask = <0x0 0x0 0x0 0x7>;
+ interrupt-map = <
+ 0x0 0x0 0x0 0x1 &UIC3 0x0 0x4 /* swizzled int A */
+ 0x0 0x0 0x0 0x2 &UIC3 0x1 0x4 /* swizzled int B */
+ 0x0 0x0 0x0 0x3 &UIC3 0x2 0x4 /* swizzled int C */
+ 0x0 0x0 0x0 0x4 &UIC3 0x3 0x4 /* swizzled int D */>;
+ };
+
+ PCIE1: pciex@d20000000 {
+ device_type = "pci";
+ #interrupt-cells = <1>;
+ #size-cells = <2>;
+ #address-cells = <3>;
+ compatible = "ibm,plb-pciex-460sx", "ibm,plb-pciex";
+ primary;
+ port = <0x1>; /* port number */
+ reg = <0x0000000d 0x20000000 0x20000000 /* Config space access */
+ 0x0000000c 0x10001000 0x00001000>; /* Registers */
+ dcr-reg = <0x120 0x020>;
+ sdr-base = <0x340>;
+
+ /* Outbound ranges, one memory and one IO,
+ * later cannot be changed
+ */
+ ranges = <0x02000000 0x00000000 0x80000000 0x0000000e 0x80000000 0x00000000 0x80000000
+ 0x01000000 0x00000000 0x00000000 0x0000000f 0x80010000 0x00000000 0x00010000>;
+
+ /* Inbound 2GB range starting at 0 */
+ dma-ranges = <0x42000000 0x0 0x0 0x0 0x0 0x0 0x80000000>;
+
+ /* This drives busses 10 to 0x1f */
+ bus-range = <0x20 0x2f>;
+
+ /* Legacy interrupts (note the weird polarity, the bridge seems
+ * to invert PCIe legacy interrupts).
+ * We are de-swizzling here because the numbers are actually for
+ * port of the root complex virtual P2P bridge. But I want
+ * to avoid putting a node for it in the tree, so the numbers
+ * below are basically de-swizzled numbers.
+ * The real slot is on idsel 0, so the swizzling is 1:1
+ */
+ interrupt-map-mask = <0x0 0x0 0x0 0x7>;
+ interrupt-map = <
+ 0x0 0x0 0x0 0x1 &UIC3 0x4 0x4 /* swizzled int A */
+ 0x0 0x0 0x0 0x2 &UIC3 0x5 0x4 /* swizzled int B */
+ 0x0 0x0 0x0 0x3 &UIC3 0x6 0x4 /* swizzled int C */
+ 0x0 0x0 0x0 0x4 &UIC3 0x7 0x4 /* swizzled int D */>;
+ };
+
+ PCIE2: pciex@d40000000 {
+ device_type = "pci";
+ #interrupt-cells = <1>;
+ #size-cells = <2>;
+ #address-cells = <3>;
+ compatible = "ibm,plb-pciex-460sx", "ibm,plb-pciex";
+ primary;
+ port = <0x2>; /* port number */
+ reg = <0x0000000d 0x40000000 0x20000000 /* Config space access */
+ 0x0000000c 0x10002000 0x00001000>; /* Registers */
+ dcr-reg = <0x140 0x020>;
+ sdr-base = <0x370>;
+
+ /* Outbound ranges, one memory and one IO,
+ * later cannot be changed
+ */
+ ranges = <0x02000000 0x00000000 0x80000000 0x0000000f 0x00000000 0x00000000 0x80000000
+ 0x01000000 0x00000000 0x00000000 0x0000000f 0x80020000 0x00000000 0x00010000>;
+
+ /* Inbound 2GB range starting at 0 */
+ dma-ranges = <0x42000000 0x0 0x0 0x0 0x0 0x0 0x80000000>;
+
+ /* This drives busses 10 to 0x1f */
+ bus-range = <0x30 0x3f>;
+
+ /* Legacy interrupts (note the weird polarity, the bridge seems
+ * to invert PCIe legacy interrupts).
+ * We are de-swizzling here because the numbers are actually for
+ * port of the root complex virtual P2P bridge. But I want
+ * to avoid putting a node for it in the tree, so the numbers
+ * below are basically de-swizzled numbers.
+ * The real slot is on idsel 0, so the swizzling is 1:1
+ */
+ interrupt-map-mask = <0x0 0x0 0x0 0x7>;
+ interrupt-map = <
+ 0x0 0x0 0x0 0x1 &UIC3 0x8 0x4 /* swizzled int A */
+ 0x0 0x0 0x0 0x2 &UIC3 0x9 0x4 /* swizzled int B */
+ 0x0 0x0 0x0 0x3 &UIC3 0xa 0x4 /* swizzled int C */
+ 0x0 0x0 0x0 0x4 &UIC3 0xb 0x4 /* swizzled int D */>;
};
};
+
chosen {
linux,stdout-path = "/plb/opb/serial@ef600200";
};
diff --git a/arch/powerpc/sysdev/ppc4xx_pci.c b/arch/powerpc/sysdev/ppc4xx_pci.c
index 6ff9d71..64cd020 100644
--- a/arch/powerpc/sysdev/ppc4xx_pci.c
+++ b/arch/powerpc/sysdev/ppc4xx_pci.c
@@ -972,6 +972,123 @@ static struct ppc4xx_pciex_hwops ppc460ex_pcie_hwops __initdata =
.setup_utl = ppc460ex_pciex_init_utl,
};
+static int __init ppc460sx_pciex_core_init(struct device_node *np)
+{
+ /* HSS drive amplitude */
+ mtdcri(SDR0, PESDR0_460SX_HSSL0DAMP, 0xB9843211);
+ mtdcri(SDR0, PESDR0_460SX_HSSL1DAMP, 0xB9843211);
+ mtdcri(SDR0, PESDR0_460SX_HSSL2DAMP, 0xB9843211);
+ mtdcri(SDR0, PESDR0_460SX_HSSL3DAMP, 0xB9843211);
+ mtdcri(SDR0, PESDR0_460SX_HSSL4DAMP, 0xB9843211);
+ mtdcri(SDR0, PESDR0_460SX_HSSL5DAMP, 0xB9843211);
+ mtdcri(SDR0, PESDR0_460SX_HSSL6DAMP, 0xB9843211);
+ mtdcri(SDR0, PESDR0_460SX_HSSL7DAMP, 0xB9843211);
+
+ mtdcri(SDR0, PESDR1_460SX_HSSL0DAMP, 0xB9843211);
+ mtdcri(SDR0, PESDR1_460SX_HSSL1DAMP, 0xB9843211);
+ mtdcri(SDR0, PESDR1_460SX_HSSL2DAMP, 0xB9843211);
+ mtdcri(SDR0, PESDR1_460SX_HSSL3DAMP, 0xB9843211);
+
+ mtdcri(SDR0, PESDR2_460SX_HSSL0DAMP, 0xB9843211);
+ mtdcri(SDR0, PESDR2_460SX_HSSL1DAMP, 0xB9843211);
+ mtdcri(SDR0, PESDR2_460SX_HSSL2DAMP, 0xB9843211);
+ mtdcri(SDR0, PESDR2_460SX_HSSL3DAMP, 0xB9843211);
+
+ /* HSS TX pre-emphasis */
+ mtdcri(SDR0, PESDR0_460SX_HSSL0COEFA, 0xDCB98987);
+ mtdcri(SDR0, PESDR0_460SX_HSSL1COEFA, 0xDCB98987);
+ mtdcri(SDR0, PESDR0_460SX_HSSL2COEFA, 0xDCB98987);
+ mtdcri(SDR0, PESDR0_460SX_HSSL3COEFA, 0xDCB98987);
+ mtdcri(SDR0, PESDR0_460SX_HSSL4COEFA, 0xDCB98987);
+ mtdcri(SDR0, PESDR0_460SX_HSSL5COEFA, 0xDCB98987);
+ mtdcri(SDR0, PESDR0_460SX_HSSL6COEFA, 0xDCB98987);
+ mtdcri(SDR0, PESDR0_460SX_HSSL7COEFA, 0xDCB98987);
+
+ mtdcri(SDR0, PESDR1_460SX_HSSL0COEFA, 0xDCB98987);
+ mtdcri(SDR0, PESDR1_460SX_HSSL1COEFA, 0xDCB98987);
+ mtdcri(SDR0, PESDR1_460SX_HSSL2COEFA, 0xDCB98987);
+ mtdcri(SDR0, PESDR1_460SX_HSSL3COEFA, 0xDCB98987);
+
+ mtdcri(SDR0, PESDR2_460SX_HSSL0COEFA, 0xDCB98987);
+ mtdcri(SDR0, PESDR2_460SX_HSSL1COEFA, 0xDCB98987);
+ mtdcri(SDR0, PESDR2_460SX_HSSL2COEFA, 0xDCB98987);
+ mtdcri(SDR0, PESDR2_460SX_HSSL3COEFA, 0xDCB98987);
+
+ /* HSS TX calibration control */
+ mtdcri(SDR0, PESDR0_460SX_HSSL1CALDRV, 0x22222222);
+ mtdcri(SDR0, PESDR1_460SX_HSSL1CALDRV, 0x22220000);
+ mtdcri(SDR0, PESDR2_460SX_HSSL1CALDRV, 0x22220000);
+
+ /* HSS TX slew control */
+ mtdcri(SDR0, PESDR0_460SX_HSSSLEW, 0xFFFFFFFF);
+ mtdcri(SDR0, PESDR1_460SX_HSSSLEW, 0xFFFF0000);
+ mtdcri(SDR0, PESDR2_460SX_HSSSLEW, 0xFFFF0000);
+
+ udelay(100);
+
+ /* De-assert PLLRESET */
+ dcri_clrset(SDR0, PESDR0_PLLLCT2, 0x00000100, 0);
+
+ /* Reset DL, UTL, GPL before configuration */
+ mtdcri(SDR0, PESDR0_460SX_RCSSET,
+ PESDRx_RCSSET_RSTDL | PESDRx_RCSSET_RSTGU);
+ mtdcri(SDR0, PESDR1_460SX_RCSSET,
+ PESDRx_RCSSET_RSTDL | PESDRx_RCSSET_RSTGU);
+ mtdcri(SDR0, PESDR2_460SX_RCSSET,
+ PESDRx_RCSSET_RSTDL | PESDRx_RCSSET_RSTGU);
+
+ udelay(100);
+
+ /*
+ * If bifurcation is not enabled, u-boot would have disabled the
+ * third PCIe port
+ */
+ if (((mfdcri(SDR0, PESDR1_460SX_HSSCTLSET) & 0x00000001) ==
+ 0x00000001)) {
+ printk(KERN_INFO "PCI: PCIE bifurcation setup successfully.\n");
+ printk(KERN_INFO "PCI: Total 3 PCIE ports are present\n");
+ return 3;
+ }
+
+ printk(KERN_INFO "PCI: Total 2 PCIE ports are present\n");
+ return 2;
+}
+
+static int ppc460sx_pciex_init_port_hw(struct ppc4xx_pciex_port *port)
+{
+
+ if (port->endpoint)
+ dcri_clrset(SDR0, port->sdr_base + PESDRn_UTLSET2,
+ 0x01000000, 0);
+ else
+ dcri_clrset(SDR0, port->sdr_base + PESDRn_UTLSET2,
+ 0, 0x01000000);
+
+ /*Gen-1*/
+ mtdcri(SDR0, port->sdr_base + PESDRn_460SX_RCEI, 0x08000000);
+
+ dcri_clrset(SDR0, port->sdr_base + PESDRn_RCSSET,
+ (PESDRx_RCSSET_RSTGU | PESDRx_RCSSET_RSTDL),
+ PESDRx_RCSSET_RSTPYN);
+
+ port->has_ibpre = 1;
+
+ return 0;
+}
+
+static int ppc460sx_pciex_init_utl(struct ppc4xx_pciex_port *port)
+{
+ /* Max 128 Bytes */
+ out_be32 (port->utl_base + PEUTL_PBBSZ, 0x00000000);
+ return 0;
+}
+
+static struct ppc4xx_pciex_hwops ppc460sx_pcie_hwops __initdata = {
+ .core_init = ppc460sx_pciex_core_init,
+ .port_init_hw = ppc460sx_pciex_init_port_hw,
+ .setup_utl = ppc460sx_pciex_init_utl,
+};
+
#endif /* CONFIG_44x */
#ifdef CONFIG_40x
@@ -1087,6 +1204,8 @@ static int __init ppc4xx_pciex_check_core_init(struct device_node *np)
}
if (of_device_is_compatible(np, "ibm,plb-pciex-460ex"))
ppc4xx_pciex_hwops = &ppc460ex_pcie_hwops;
+ if (of_device_is_compatible(np, "ibm,plb-pciex-460sx"))
+ ppc4xx_pciex_hwops = &ppc460sx_pcie_hwops;
#endif /* CONFIG_44x */
#ifdef CONFIG_40x
if (of_device_is_compatible(np, "ibm,plb-pciex-405ex"))
diff --git a/arch/powerpc/sysdev/ppc4xx_pci.h b/arch/powerpc/sysdev/ppc4xx_pci.h
index d04e40b..56d9e5d 100644
--- a/arch/powerpc/sysdev/ppc4xx_pci.h
+++ b/arch/powerpc/sysdev/ppc4xx_pci.h
@@ -324,6 +324,64 @@
#define PESDR0_460EX_IHS2 0x036D
/*
+ * 460SX addtional DCRs
+ */
+#define PESDRn_460SX_RCEI 0x02
+
+#define PESDR0_460SX_HSSL0DAMP 0x320
+#define PESDR0_460SX_HSSL1DAMP 0x321
+#define PESDR0_460SX_HSSL2DAMP 0x322
+#define PESDR0_460SX_HSSL3DAMP 0x323
+#define PESDR0_460SX_HSSL4DAMP 0x324
+#define PESDR0_460SX_HSSL5DAMP 0x325
+#define PESDR0_460SX_HSSL6DAMP 0x326
+#define PESDR0_460SX_HSSL7DAMP 0x327
+
+#define PESDR1_460SX_HSSL0DAMP 0x354
+#define PESDR1_460SX_HSSL1DAMP 0x355
+#define PESDR1_460SX_HSSL2DAMP 0x356
+#define PESDR1_460SX_HSSL3DAMP 0x357
+
+#define PESDR2_460SX_HSSL0DAMP 0x384
+#define PESDR2_460SX_HSSL1DAMP 0x385
+#define PESDR2_460SX_HSSL2DAMP 0x386
+#define PESDR2_460SX_HSSL3DAMP 0x387
+
+#define PESDR0_460SX_HSSL0COEFA 0x328
+#define PESDR0_460SX_HSSL1COEFA 0x329
+#define PESDR0_460SX_HSSL2COEFA 0x32A
+#define PESDR0_460SX_HSSL3COEFA 0x32B
+#define PESDR0_460SX_HSSL4COEFA 0x32C
+#define PESDR0_460SX_HSSL5COEFA 0x32D
+#define PESDR0_460SX_HSSL6COEFA 0x32E
+#define PESDR0_460SX_HSSL7COEFA 0x32F
+
+#define PESDR1_460SX_HSSL0COEFA 0x358
+#define PESDR1_460SX_HSSL1COEFA 0x359
+#define PESDR1_460SX_HSSL2COEFA 0x35A
+#define PESDR1_460SX_HSSL3COEFA 0x35B
+
+#define PESDR2_460SX_HSSL0COEFA 0x388
+#define PESDR2_460SX_HSSL1COEFA 0x389
+#define PESDR2_460SX_HSSL2COEFA 0x38A
+#define PESDR2_460SX_HSSL3COEFA 0x38B
+
+#define PESDR0_460SX_HSSL1CALDRV 0x339
+#define PESDR1_460SX_HSSL1CALDRV 0x361
+#define PESDR2_460SX_HSSL1CALDRV 0x391
+
+#define PESDR0_460SX_HSSSLEW 0x338
+#define PESDR1_460SX_HSSSLEW 0x360
+#define PESDR2_460SX_HSSSLEW 0x390
+
+#define PESDR0_460SX_HSSCTLSET 0x31E
+#define PESDR1_460SX_HSSCTLSET 0x352
+#define PESDR2_460SX_HSSCTLSET 0x382
+
+#define PESDR0_460SX_RCSSET 0x304
+#define PESDR1_460SX_RCSSET 0x344
+#define PESDR2_460SX_RCSSET 0x374
+/*
* Of the above, some are common offsets from the base
*/
#define PESDRn_UTLSET1 0x00
--
1.6.1.rc3
^ permalink raw reply related
* [PATCH 2/2] Adding PCI-E MSI support for PowerPC 460SX SOC.
From: tmarri @ 2009-12-22 8:49 UTC (permalink / raw)
To: jwboyer, linuxppc-dev; +Cc: linuxppc-dev, writetomarri, tmarri
From: Tirumala Marri <tmarri@amcc.com>
Signed-off-by: Tirumala Marri <tmarri@amcc.com>
---
Kernel version: 2.6.33-rc1
Testing:
When 460SX configured as root as a end point E1000(Intell Ethernet card)
was plugged into the one of the PCI-E ports. I was able to run the traffic
with MSI interrupts.
---
arch/powerpc/boot/dts/redwood.dts | 15 ++
arch/powerpc/configs/44x/redwood_defconfig | 5 +-
arch/powerpc/platforms/44x/Kconfig | 1 +
arch/powerpc/sysdev/Kconfig | 7 +
arch/powerpc/sysdev/Makefile | 1 +
arch/powerpc/sysdev/ppc4xx_msi.c | 335 ++++++++++++++++++++++++++++
arch/powerpc/sysdev/ppc4xx_msi.h | 49 ++++
7 files changed, 411 insertions(+), 2 deletions(-)
create mode 100644 arch/powerpc/sysdev/ppc4xx_msi.c
create mode 100644 arch/powerpc/sysdev/ppc4xx_msi.h
diff --git a/arch/powerpc/boot/dts/redwood.dts b/arch/powerpc/boot/dts/redwood.dts
index 81636c0..412d5f9 100644
--- a/arch/powerpc/boot/dts/redwood.dts
+++ b/arch/powerpc/boot/dts/redwood.dts
@@ -357,6 +357,21 @@
0x0 0x0 0x0 0x3 &UIC3 0xa 0x4 /* swizzled int C */
0x0 0x0 0x0 0x4 &UIC3 0xb 0x4 /* swizzled int D */>;
};
+ MSI: ppc4xx-msi@400300000 {
+ compatible = "amcc,ppc4xx-msi", "ppc4xx-msi";
+ reg = < 0x4 0x00300000 0x100
+ 0x4 0x00300000 0x100>;
+ sdr-base = <0x3B0>;
+ interrupts =<0 1 2 3>;
+ interrupt-parent = <&MSI>;
+ #interrupt-cells = <1>;
+ #address-cells = <0>;
+ #size-cells = <0>;
+ interrupt-map = <0 &UIC0 0xC 1
+ 1 &UIC0 0x0D 1
+ 2 &UIC0 0x0E 1
+ 3 &UIC0 0x0F 1>;
+ };
};
diff --git a/arch/powerpc/configs/44x/redwood_defconfig b/arch/powerpc/configs/44x/redwood_defconfig
index ed31d4f..5d16c88 100644
--- a/arch/powerpc/configs/44x/redwood_defconfig
+++ b/arch/powerpc/configs/44x/redwood_defconfig
@@ -158,6 +158,7 @@ CONFIG_DEFAULT_AS=y
CONFIG_DEFAULT_IOSCHED="anticipatory"
# CONFIG_FREEZER is not set
CONFIG_PPC4xx_PCI_EXPRESS=y
+CONFIG_PPC_MSI_BITMAP=y
#
# Platform support
@@ -264,7 +265,7 @@ CONFIG_PCIEPORTBUS=y
CONFIG_PCIEAER=y
# CONFIG_PCIEASPM is not set
CONFIG_ARCH_SUPPORTS_MSI=y
-# CONFIG_PCI_MSI is not set
+CONFIG_PCI_MSI=y
# CONFIG_PCI_LEGACY is not set
# CONFIG_PCI_DEBUG is not set
# CONFIG_PCI_STUB is not set
@@ -1062,7 +1063,7 @@ CONFIG_PRINT_STACK_DEPTH=64
# CONFIG_DEBUG_PAGEALLOC is not set
# CONFIG_CODE_PATCHING_SELFTEST is not set
# CONFIG_FTR_FIXUP_SELFTEST is not set
-# CONFIG_MSI_BITMAP_SELFTEST is not set
+CONFIG_MSI_BITMAP_SELFTEST=y
# CONFIG_XMON is not set
# CONFIG_IRQSTACKS is not set
# CONFIG_VIRQ_DEBUG is not set
diff --git a/arch/powerpc/platforms/44x/Kconfig b/arch/powerpc/platforms/44x/Kconfig
index 7486bff..85b9c33 100644
--- a/arch/powerpc/platforms/44x/Kconfig
+++ b/arch/powerpc/platforms/44x/Kconfig
@@ -126,6 +126,7 @@ config REDWOOD
select 460SX
select PCI
select PPC4xx_PCI_EXPRESS
+ select 4xx_MSI
help
This option enables support for the AMCC PPC460SX Redwood board.
diff --git a/arch/powerpc/sysdev/Kconfig b/arch/powerpc/sysdev/Kconfig
index 3965828..32f5a40 100644
--- a/arch/powerpc/sysdev/Kconfig
+++ b/arch/powerpc/sysdev/Kconfig
@@ -7,8 +7,15 @@ config PPC4xx_PCI_EXPRESS
depends on PCI && 4xx
default n
+config 4xx_MSI
+ bool
+ depends on PCI_MSI
+ depends on PCI && 4xx
+ default n
+
config PPC_MSI_BITMAP
bool
depends on PCI_MSI
default y if MPIC
default y if FSL_PCI
+ default y if 4xx_MSI
diff --git a/arch/powerpc/sysdev/Makefile b/arch/powerpc/sysdev/Makefile
index 5642924..d60c33b 100644
--- a/arch/powerpc/sysdev/Makefile
+++ b/arch/powerpc/sysdev/Makefile
@@ -36,6 +36,7 @@ obj-$(CONFIG_PPC_I8259) += i8259.o
obj-$(CONFIG_IPIC) += ipic.o
obj-$(CONFIG_4xx) += uic.o
obj-$(CONFIG_4xx_SOC) += ppc4xx_soc.o
+obj-$(CONFIG_4xx_MSI) += ppc4xx_msi.o
obj-$(CONFIG_XILINX_VIRTEX) += xilinx_intc.o
obj-$(CONFIG_XILINX_PCI) += xilinx_pci.o
obj-$(CONFIG_OF_RTC) += of_rtc.o
diff --git a/arch/powerpc/sysdev/ppc4xx_msi.c b/arch/powerpc/sysdev/ppc4xx_msi.c
new file mode 100644
index 0000000..752da4b
--- /dev/null
+++ b/arch/powerpc/sysdev/ppc4xx_msi.c
@@ -0,0 +1,335 @@
+/*
+ * Copyright (C) 2009 Applied Micro Circuits corporation,
+ * All rights reserved.
+ *
+ * Author: Feng Kan <fkan@amcc.com>
+ * Tirumala Marri <tmarri@amcc.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; version 2 of the
+ * License.
+ */
+#include <linux/irq.h>
+#include <linux/bootmem.h>
+#include <linux/pci.h>
+#include <linux/msi.h>
+#include <linux/of_platform.h>
+#include <linux/interrupt.h>
+#include <linux/device.h>
+#include <asm/prom.h>
+#include <asm/hw_irq.h>
+#include <asm/ppc-pci.h>
+#include <asm/dcr.h>
+#include <asm/dcr-regs.h>
+#include "ppc4xx_msi.h"
+
+
+static struct ppc4xx_msi *ppc4xx_msi;
+
+struct ppc4xx_msi_feature {
+ u32 ppc4xx_pic_ip;
+ u32 msiir_offset;
+};
+
+static int ppc4xx_msi_init_allocator(struct ppc4xx_msi *msi_data)
+{
+ int rc;
+
+ rc = msi_bitmap_alloc(&msi_data->bitmap, NR_MSI_IRQS,
+ msi_data->irqhost->of_node);
+ if (rc)
+ return rc;
+ rc = msi_bitmap_reserve_dt_hwirqs(&msi_data->bitmap);
+ if (rc < 0) {
+ msi_bitmap_free(&msi_data->bitmap);
+ return rc;
+ }
+ return 0;
+}
+
+
+static void ppc4xx_msi_cascade(unsigned int irq, struct irq_desc *desc)
+{
+ unsigned int cascade_irq;
+ struct ppc4xx_msi *msi_data = ppc4xx_msi;
+ int msir_index = -1;
+
+ raw_spin_lock(&desc->lock);
+ if (desc->chip->mask_ack) {
+ desc->chip->mask_ack(irq);
+ } else {
+ desc->chip->mask(irq);
+ desc->chip->ack(irq);
+ }
+
+ if (unlikely(desc->status & IRQ_INPROGRESS))
+ goto unlock;
+
+ msir_index = (int)desc->handler_data;
+
+ if (msir_index >= NR_MSI_IRQS)
+ cascade_irq = NO_IRQ;
+
+ desc->status |= IRQ_INPROGRESS;
+
+ cascade_irq = irq_linear_revmap(msi_data->irqhost, msir_index);
+ if (cascade_irq != NO_IRQ)
+ generic_handle_irq(cascade_irq);
+ desc->status &= ~IRQ_INPROGRESS;
+
+ if (!(desc->status & IRQ_DISABLED) && desc->chip->unmask)
+ desc->chip->unmask(irq);
+unlock:
+ raw_spin_unlock(&desc->lock);
+}
+static void ppc4xx_compose_msi_msg(struct pci_dev *pdev, int hwirq,
+ struct msi_msg *msg)
+{
+ struct ppc4xx_msi *msi_data = ppc4xx_msi;
+
+ msg->address_lo = msi_data->msi_addr_lo;
+ msg->address_hi = msi_data->msi_addr_hi;
+ msg->data = hwirq;
+}
+
+
+int ppc4xx_setup_msi_irqs(struct pci_dev *dev, int nvec, int type)
+{
+ struct msi_desc *entry;
+ int rc, hwirq;
+ unsigned int virq;
+ struct msi_msg msg;
+ struct ppc4xx_msi *msi_data = ppc4xx_msi;
+
+
+ list_for_each_entry(entry, &dev->msi_list, list) {
+ hwirq = msi_bitmap_alloc_hwirqs(&msi_data->bitmap, 1);
+ if (hwirq < 0) {
+ rc = hwirq;
+ dev_err(&dev->dev, "%s: fail allocating msi\
+ interrupt\n", __func__);
+ goto out_free;
+ }
+
+ pr_debug(KERN_INFO"mis is %p\n", msi_data->irqhost);
+ virq = irq_create_mapping(msi_data->irqhost, hwirq);
+ if (virq == NO_IRQ) {
+ dev_err(&dev->dev, "%s: fail mapping irq\n", __func__);
+ rc = -ENOSPC;
+ goto out_free;
+ }
+
+ set_irq_msi(virq, entry);
+ ppc4xx_compose_msi_msg(dev, hwirq, &msg);
+ write_msi_msg(virq, &msg);
+ }
+
+ return 0;
+out_free:
+ return rc;
+}
+
+void ppc4xx_teardown_msi_irqs(struct pci_dev *dev)
+{
+ struct msi_desc *entry;
+ struct ppc4xx_msi *msi_data = ppc4xx_msi;
+ dev_dbg(&dev->dev, "PCIE-MSI: tearing down msi irqs\n");
+
+ list_for_each_entry(entry, &dev->msi_list, list) {
+ if (entry->irq == NO_IRQ)
+ continue;
+ set_irq_msi(entry->irq, NULL);
+ msi_bitmap_free_hwirqs(&msi_data->bitmap,
+ virq_to_hw(entry->irq), 1);
+ irq_dispose_mapping(entry->irq);
+
+ }
+
+ return;
+}
+
+static int ppc4xx_msi_check_device(struct pci_dev *pdev, int nvec, int type)
+{
+ pr_debug(KERN_INFO"PCIE-MSI:%s called. vec %x type %d\n",
+ __func__, nvec, type);
+ return 0;
+}
+
+/*
+ * We do not need this actually. The MSIR register has been read once
+ * in the cascade interrupt. So, this MSI interrupt has been acked
+*/
+static void ppc4xx_msi_end_irq(unsigned int virq)
+{
+}
+
+
+static struct irq_chip ppc4xx_msi_chip = {
+ .mask = mask_msi_irq,
+ .unmask = unmask_msi_irq,
+ .ack = ppc4xx_msi_end_irq,
+ .name = " UIC",
+};
+
+static int ppc4xx_msi_host_map(struct irq_host *h, unsigned int virq,
+ irq_hw_number_t hw)
+{
+ struct irq_chip *chip = &ppc4xx_msi_chip;
+
+ irq_to_desc(virq)->status |= IRQ_TYPE_EDGE_RISING;
+
+ set_irq_chip_and_handler(virq, chip, handle_edge_irq);
+
+ return 0;
+}
+
+static struct irq_host_ops ppc4xx_msi_host_ops = {
+ .map = ppc4xx_msi_host_map,
+};
+
+
+static int __devinit ppc4xx_msi_probe(struct of_device *dev,
+ const struct of_device_id *match)
+{
+ struct ppc4xx_msi *msi;
+ struct resource res, rmsi;
+ int i, count;
+ int rc;
+ int virt_msir;
+ const u32 *p;
+ u32 *msi_virt = NULL;
+ dma_addr_t msi_phys;
+
+
+ msi = kzalloc(sizeof(struct ppc4xx_msi), GFP_KERNEL);
+ if (!msi) {
+ dev_err(&dev->dev, "No memory for MSI structure\n");
+ rc = -ENOMEM;
+ goto error_out;
+ }
+
+ msi->irqhost = irq_alloc_host(dev->node, IRQ_HOST_MAP_LINEAR,
+ NR_MSI_IRQS, &ppc4xx_msi_host_ops, 0);
+ if (msi->irqhost == NULL) {
+ dev_err(&dev->dev, "No memory for MSI irqhost\n");
+ rc = -ENOMEM;
+ goto error_out;
+ }
+
+
+ /* Get MSI ranges */
+ rc = of_address_to_resource(dev->node, 0, &rmsi);
+ if (rc) {
+ dev_err(&dev->dev, "%s resource error!\n",
+ dev->node->full_name);
+ goto error_out;
+ }
+
+
+ /* Get the MSI reg base */
+ rc = of_address_to_resource(dev->node, 1, &res);
+ if (rc) {
+ dev_err(&dev->dev, "%s resource error!\n",
+ dev->node->full_name);
+ goto error_out;
+ }
+#if defined(CONFIG_460SX)
+ mtdcri(SDR0, SDR0_PCIEH_H, PCIE_MSI_REG_BASE_H);
+ mtdcri(SDR0, SDR0_PCIEH_L, PCIE_MSI_REG_BASE_L);
+ msi->msi_regs = ioremap(((u64)PCIE_MSI_REG_BASE_H << 32) | res.start,
+ res.end - res.start + 1);
+#else
+ dev_err(&dev->dev, " Invalid Device \n");
+ goto error_out;
+#endif
+ if (!msi->msi_regs) {
+ dev_err(&dev->dev, "ioremap problem failed\n");
+ goto error_out;
+ }
+ /* MSI region always mapped in 4GB region*/
+ msi->msi_addr_hi = 0x0;
+ msi_virt = dma_alloc_coherent(&dev->dev, 64, &msi_phys,
+ GFP_KERNEL);
+ if (msi_virt == NULL) {
+ dev_err(&dev->dev, "No memory for MSI mem space\n");
+ rc = -ENOMEM;
+ goto error_out;
+ }
+ msi->msi_addr_lo = (u32)msi_phys;
+
+ /* Progam the Interrupt handler Termination addr registers */
+ out_be32(msi->msi_regs + PEIH_TERMADH, msi->msi_addr_hi);
+ out_be32(msi->msi_regs + PEIH_TERMADL, msi->msi_addr_lo);
+
+ /* Program MSI Expected data and Mask bits */
+ out_be32(msi->msi_regs + PEIH_MSIED, MSI_DATA_PATTERN);
+ out_be32(msi->msi_regs + PEIH_MSIMK, MSI_DATA_PATTERN);
+
+ msi->irqhost->host_data = msi;
+
+ if (ppc4xx_msi_init_allocator(msi)) {
+ dev_err(&dev->dev, "Error allocating MSI bitmap\n");
+ goto error_out;
+ }
+
+ p = of_get_property(dev->node, "interrupts", &count);
+ if (!p) {
+ dev_err(&dev->dev, "no interrupts property found on %s\n",
+ dev->node->full_name);
+ rc = -ENODEV;
+ goto error_out;
+ }
+ if (count == 0) {
+ dev_err(&dev->dev, "Malformed interrupts property on %s\n",
+ dev->node->full_name);
+ rc = -EINVAL;
+ goto error_out;
+ }
+
+ for (i = 0; i < NR_MSI_IRQS; i++) {
+ virt_msir = irq_of_parse_and_map(dev->node, i);
+ if (virt_msir != NO_IRQ) {
+ set_irq_data(virt_msir, (void *)i);
+ set_irq_chained_handler(virt_msir, ppc4xx_msi_cascade);
+ }
+ }
+
+ ppc4xx_msi = msi;
+
+ WARN_ON(ppc_md.setup_msi_irqs);
+ ppc_md.setup_msi_irqs = ppc4xx_setup_msi_irqs;
+ ppc_md.teardown_msi_irqs = ppc4xx_teardown_msi_irqs;
+ ppc_md.msi_check_device = ppc4xx_msi_check_device;
+ return 0;
+error_out:
+ if (msi_virt)
+ dma_free_coherent(&dev->dev, 64, msi_virt, msi_phys);
+ kfree(msi);
+ return rc;
+}
+
+static const struct ppc4xx_msi_feature ppc4xx_msi_feature = {
+ .ppc4xx_pic_ip = 0,
+ .msiir_offset = 0x140,
+};
+
+static const struct of_device_id ppc4xx_msi_ids[] = {
+ {
+ .compatible = "amcc,ppc4xx-msi",
+ .data = (void *)&ppc4xx_msi_feature,
+ },
+ {}
+};
+
+static struct of_platform_driver ppc4xx_msi_driver = {
+ .name = "ppc4xx-msi",
+ .match_table = ppc4xx_msi_ids,
+ .probe = ppc4xx_msi_probe,
+};
+
+static __init int ppc4xx_msi_init(void)
+{
+ return of_register_platform_driver(&ppc4xx_msi_driver);
+}
+
+subsys_initcall(ppc4xx_msi_init);
diff --git a/arch/powerpc/sysdev/ppc4xx_msi.h b/arch/powerpc/sysdev/ppc4xx_msi.h
new file mode 100644
index 0000000..7b8ac5c
--- /dev/null
+++ b/arch/powerpc/sysdev/ppc4xx_msi.h
@@ -0,0 +1,49 @@
+/*
+ * Copyright (C) 2009 Applied Micro Circuits Corporation,
+ * All rights reserved.
+ *
+ * Author: T irumala Marri <tmarri@amcc.com>
+ * Feng Kan <fkan@amcc.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; version 2 of the
+ * License.
+ */
+#ifndef __PPC4XX_MSI_H__
+#define __PPC4XX_MSI_H__
+
+#include <asm/msi_bitmap.h>
+
+#define PEIH_TERMADH 0x00
+#define PEIH_TERMADL 0x08
+#define PEIH_MSIED 0x10
+#define PEIH_MSIMK 0x18
+#define PEIH_MSIASS 0x20
+#define PEIH_FLUSH0 0x30
+#define PEIH_FLUSH1 0x38
+#define PEIH_CNTRST 0x48
+
+#define MSI_DATA_PATTERN 0x44440000
+
+#if defined(CONFIG_405Ex)
+#define SDR0_PCIEH 0x4B1
+#define PCIE_MSI_REG_BASE 0xef620000
+#elif defined(CONFIG_440SPe) || defined(CONFIG_460SX)
+#define SDR0_PCIEH_H 0x3B0
+#define SDR0_PCIEH_L 0x3B1
+#define PCIE_MSI_REG_BASE_L 0x00300000
+#define PCIE_MSI_REG_BASE_H 0x00000004
+#endif
+
+struct ppc4xx_msi {
+ struct irq_host *irqhost;
+ unsigned long cascade_irq;
+ u32 msi_addr_lo;
+ u32 msi_addr_hi;
+ void __iomem *msi_regs;
+ u32 feature;
+ struct msi_bitmap bitmap;
+};
+
+#define NR_MSI_IRQS 4
+#endif /* __PPC4XX_MSI_H__ */
--
1.6.1.rc3
^ permalink raw reply related
* Re: MPC8313e RDB rev A4 and rev C network throughput
From: RONETIX - Asen Dimov @ 2009-12-22 9:19 UTC (permalink / raw)
To: linuxppc-dev; +Cc: Liu Dave-R63238
In-Reply-To: <D7CCA83BB0796C49BC0BB53B6AB1208985F8CF@zch01exm21.fsl.freescale.net>
Hi,
I checked the RCW(RCWLR and RCWHR) and they are the same(taken from
NOR), S3 and S4 switches are the same too.
I will take a look at the priority levels on CSB.
Regards,
Asen
Liu Dave-R63238 wrote:
> One possible cause is the two board has different RCW.
> So that the freq of core/csb/.... Is different.
>
>
>> -----Original Message-----
>> From:
>> linuxppc-dev-bounces+daveliu=freescale.com@lists.ozlabs.org
>> [mailto:linuxppc-dev-bounces+daveliu=freescale.com@lists.ozlab
>> s.org] On Behalf Of RONETIX - Asen Dimov
>> Sent: Tuesday, December 22, 2009 6:28 AM
>> To: linuxppc-dev@lists.ozlabs.org
>> Subject: MPC8313e RDB rev A4 and rev C network throughput
>>
>> Hello all,
>> I have made some test on network throughput with MPC8313e RDB
>> revA4 and revC.
>> Some have mentioned that CSB(Coherent System Bus) frequency
>> or untuned TCP/IP stack, could cause decrease of network throughput.
>>
>> **Test results
>>
>> -on MPC8313e RDB revA4 with kernel 2.6.20 and u-boot 1.1.6
>> created with
>> ltib-mpc8313erdb-20070824
>> iperf -c 172.16.0.1 -l 2m -w 256k -throughput is 510Mbps
>>
>>
>> -on MPC8313e RDB revA4 with kernel 2.6.23 and u-boot 1.3.0 generated
>> with ltib-mpc8313erdb-20081222
>> iperf -c 172.16.0.1 -l 2m -w 256k
>> -throughput is 510Mbps
>>
>>
>> -on MPC8313e RDB revC with kernel 2.6.23 (the same u-boot, kernel and
>> rootfs as in rev A4, only dtb file differs)
>> iperf -c 172.16.0.1 -l 2m -w 256k
>> -throughput is 360Mbps.
>>
>>
>> Have someone made such measurements? Any ideas why MPC8313e RDB revC
>> gives worser throughput than revA4?
>>
>> ** Notes
>> *The PC (CPU:Intel(R) Core(TM)2 Duo CPU, E8400 @ 3.00GHz;
>> RAM: 2x2G DDR2 @ 800Mhz ;
>> NIC: R8168B PCI Express Gigabit Ethernet controller, driver
>> 8.014.00-NAPI;
>> OS: Fedora release 10 (Cambridge) with kernel:
>> 2.6.27.38-170.2.113.fc10.i686.PAE #1 SMP )
>> *Commnads to set PC
>>
>> ethtool -s eth0 autoneg off speed 1000 duplex full
>> ifconfig eth1 172.16.0.1/12 mtu 6000 txqueuelen 10000
>> echo 131071 > /proc/sys/net/core/rmem_max
>> echo 131071 > /proc/sys/net/core/wmem_max
>> echo "4096 1048576 8388608" > /proc/sys/net/ipv4/tcp_rmem
>> echo "4096 1048576 8388608" > /proc/sys/net/ipv4/tcp_wmem
>> iperf -s -l 2m -w 70k
>>
>>
>> *The MPC8313e RDB(CPU: 333Mhz; CSB: 166Mhz) revA4 and
>> revC(using PHY not
>> switch)
>> *Commnads to set a board before using iperf
>>
>> ifconfig eth1 172.16.0.1/12 mtu 6000 txqueuelen 10000
>> #The PC lan card is set to advertise 1000Mbps only, so the
>> board switches to 1000Mbps too.
>> echo 131071 > /proc/sys/net/core/rmem_max
>> echo 131071 > /proc/sys/net/core/wmem_max
>> echo "4096 1048576 8388608" > /proc/sys/net/ipv4/tcp_rmem
>> echo "4096 1048576 8388608" > /proc/sys/net/ipv4/tcp_wmem
>>
>> Regards,
>> Asen
>>
>> _______________________________________________
>> Linuxppc-dev mailing list
>> Linuxppc-dev@lists.ozlabs.org
>> https://lists.ozlabs.org/listinfo/linuxppc-dev
>>
>>
>>
^ permalink raw reply
* [PATCH] of/flattree: use callback to setup initrd from /chosen
From: Jeremy Kerr @ 2009-12-22 9:39 UTC (permalink / raw)
To: Grant Likely, devicetree-discuss; +Cc: microblaze-uclinux, linuxppc-dev
At present, the fdt code sets the kernel-wide initrd_start and
initrd_end variables when parsing /chosen. On ARM, we only set these
once the bootmem has been reserved.
This change adds an arch callback to setup the initrd from the device
tree:
void early_init_dt_setup_initrd_arch(unsigned long start,
unsigned long end);
The arch-specific code can then setup the initrd however it likes.
Compiled on powerpc, with CONFIG_BLK_DEV_INITRD=y and =n.
Signed-off-by: Jeremy Kerr <jeremy.kerr@canonical.com>
---
arch/microblaze/kernel/prom.c | 10 ++++++++++
arch/powerpc/kernel/prom.c | 10 ++++++++++
drivers/of/fdt.c | 15 +++++----------
include/linux/of_fdt.h | 10 ++++++++++
4 files changed, 35 insertions(+), 10 deletions(-)
diff --git a/arch/microblaze/kernel/prom.c b/arch/microblaze/kernel/prom.c
index 15853de..4fb5d87 100644
--- a/arch/microblaze/kernel/prom.c
+++ b/arch/microblaze/kernel/prom.c
@@ -101,6 +101,16 @@ void __init early_init_devtree_arch(void)
/* No Microblaze specific code here */
}
+#ifdef CONFIG_BLK_DEV_INITRD
+void __init early_init_dt_setup_initrd_arch(unsigned long start,
+ unsigned long end)
+{
+ initrd_start = (unsigned long)__va(start);
+ initrd_end = (unsigned long)__va(end);
+ initrd_below_start_ok = 1;
+}
+#endif
+
/*******
*
* New implementation of the OF "find" APIs, return a refcounted
diff --git a/arch/powerpc/kernel/prom.c b/arch/powerpc/kernel/prom.c
index 6fea025..236b02c 100644
--- a/arch/powerpc/kernel/prom.c
+++ b/arch/powerpc/kernel/prom.c
@@ -467,6 +467,16 @@ int __init early_init_dt_scan_memory_arch(unsigned long node, const char *uname,
}
#endif /* CONFIG_PPC_PSERIES */
+#ifdef CONFIG_BLK_DEV_INITRD
+void __init early_init_dt_setup_initrd_arch(unsigned long start,
+ unsigned long end)
+{
+ initrd_start = (unsigned long)__va(start);
+ initrd_end = (unsigned long)__va(end);
+ initrd_below_start_ok = 1;
+}
+#endif
+
void __init early_reserve_mem(void)
{
u64 base, size;
diff --git a/drivers/of/fdt.c b/drivers/of/fdt.c
index 7cb386c..ad0b09a 100644
--- a/drivers/of/fdt.c
+++ b/drivers/of/fdt.c
@@ -398,28 +398,23 @@ unsigned long __init unflatten_dt_node(unsigned long mem,
*/
void __init early_init_dt_check_for_initrd(unsigned long node)
{
- unsigned long len;
+ unsigned long start, end, len;
__be32 *prop;
pr_debug("Looking for initrd properties... ");
prop = of_get_flat_dt_prop(node, "linux,initrd-start", &len);
if (prop) {
- initrd_start = (unsigned long)
- __va(of_read_ulong(prop, len/4));
+ start = of_read_ulong(prop, len/4);
prop = of_get_flat_dt_prop(node, "linux,initrd-end", &len);
if (prop) {
- initrd_end = (unsigned long)
- __va(of_read_ulong(prop, len/4));
- initrd_below_start_ok = 1;
- } else {
- initrd_start = 0;
+ end = of_read_ulong(prop, len/4);
+ early_init_dt_setup_initrd_arch(start, end);
}
}
- pr_debug("initrd_start=0x%lx initrd_end=0x%lx\n",
- initrd_start, initrd_end);
+ pr_debug("initrd_start=0x%lx initrd_end=0x%lx\n", start, end);
}
#else
inline void early_init_dt_check_for_initrd(unsigned long node)
diff --git a/include/linux/of_fdt.h b/include/linux/of_fdt.h
index 77ae0a4..4aba9a6 100644
--- a/include/linux/of_fdt.h
+++ b/include/linux/of_fdt.h
@@ -81,6 +81,16 @@ extern void early_reserve_mem(void);
extern void early_init_devtree_arch(void);
extern u64 dt_mem_next_cell(int s, __be32 **cellp);
+/*
+ * If BLK_DEV_INITRD, the fdt early init code will call this function,
+ * to be provided by the arch code. start and end are specified as
+ * physical addresses.
+ */
+#ifdef CONFIG_BLK_DEV_INITRD
+extern void early_init_dt_setup_initrd_arch(unsigned long start,
+ unsigned long end);
+#endif
+
/* With CONFIG_HAVE_LMB, we can just use the lmb_ functions to add & allocate
* memory; otherwise, the arch has to provide its own functions to do this.
*/
^ permalink raw reply related
* Re: [PATCH] of/flattree: use callback to setup initrd from /chosen
From: Michael Ellerman @ 2009-12-22 10:47 UTC (permalink / raw)
To: Jeremy Kerr; +Cc: microblaze-uclinux, devicetree-discuss, linuxppc-dev
In-Reply-To: <1261474791.289871.854376051633.1.gpush@pororo>
[-- Attachment #1: Type: text/plain, Size: 897 bytes --]
On Tue, 2009-12-22 at 17:39 +0800, Jeremy Kerr wrote:
> At present, the fdt code sets the kernel-wide initrd_start and
> initrd_end variables when parsing /chosen. On ARM, we only set these
> once the bootmem has been reserved.
>
> This change adds an arch callback to setup the initrd from the device
> tree:
>
> void early_init_dt_setup_initrd_arch(unsigned long start,
> unsigned long end);
arch_early_init_dt_setup_initrd() makes more sense to me, but ..
> +#ifdef CONFIG_BLK_DEV_INITRD
> +void __init early_init_dt_setup_initrd_arch(unsigned long start,
> + unsigned long end)
> +{
> + initrd_start = (unsigned long)__va(start);
> + initrd_end = (unsigned long)__va(end);
> + initrd_below_start_ok = 1;
> +}
> +#endif
Given you have two identical implementations why not make that the
default and make it weak, and let ARM override it.
cheers
[-- Attachment #2: This is a digitally signed message part --]
[-- Type: application/pgp-signature, Size: 197 bytes --]
^ permalink raw reply
* Re: [PATCH] of/flattree: use callback to setup initrd from /chosen
From: Jeremy Kerr @ 2009-12-22 10:54 UTC (permalink / raw)
To: michael; +Cc: microblaze-uclinux, devicetree-discuss, linuxppc-dev
In-Reply-To: <1261478868.10767.2.camel@concordia>
Hi Michael,
> > void early_init_dt_setup_initrd_arch(unsigned long start,
> > unsigned long end);
>
> arch_early_init_dt_setup_initrd() makes more sense to me, but ..
<foo>_arch has been the general convention for arch-specific hooks in
drivers/of/.
> > +#ifdef CONFIG_BLK_DEV_INITRD
> > +void __init early_init_dt_setup_initrd_arch(unsigned long start,
> > + unsigned long end)
> > +{
> > + initrd_start = (unsigned long)__va(start);
> > + initrd_end = (unsigned long)__va(end);
> > + initrd_below_start_ok = 1;
> > +}
> > +#endif
>
> Given you have two identical implementations why not make that the
> default and make it weak, and let ARM override it.
Yeah, that would be good too; just been avoiding weak as a potential source of
magic voodoo complexity. Grant - up to you on this one.
Cheers,
Jeremy
^ permalink raw reply
* Re: [PATCH 1/2] Adding PCI-E support for PowerPC 460SX based SOC.
From: Josh Boyer @ 2009-12-22 11:49 UTC (permalink / raw)
To: tmarri; +Cc: linuxppc-dev, writetomarri
In-Reply-To: <1261471781-330-1-git-send-email-tmarri@amcc.com>
On Tue, Dec 22, 2009 at 12:49:41AM -0800, tmarri@amcc.com wrote:
>From: Tirumala Marri <tmarri@amcc.com>
>
>
>Signed-off-by: Tirumala Marri <tmarri@amcc.com>
Acked-by: Josh Boyer <jwboyer@linux.vnet.ibm.com
Ben, do you want to take this through your tree or mine?
josh
>---
>Kerenl:2.6.33-rc1
>Testing: This patch has been tested on 460SX based redwood board . One board configured as
>root complex and other as Endpoint. Checked for link up . From root complex lspci command
>shows the end point. Also programmed IO tested using loop back as well as board to board.
>---
> arch/powerpc/boot/dts/redwood.dts | 122 +++++++++++++++++++++++++++++++++++++
> arch/powerpc/sysdev/ppc4xx_pci.c | 119 ++++++++++++++++++++++++++++++++++++
> arch/powerpc/sysdev/ppc4xx_pci.h | 58 +++++++++++++++++
> 3 files changed, 299 insertions(+), 0 deletions(-)
>
>diff --git a/arch/powerpc/boot/dts/redwood.dts b/arch/powerpc/boot/dts/redwood.dts
>index d2af32e..81636c0 100644
>--- a/arch/powerpc/boot/dts/redwood.dts
>+++ b/arch/powerpc/boot/dts/redwood.dts
>@@ -234,10 +234,132 @@
> has-inverted-stacr-oc;
> has-new-stacr-staopc;
> };
>+ };
>+ PCIE0: pciex@d00000000 {
>+ device_type = "pci";
>+ #interrupt-cells = <1>;
>+ #size-cells = <2>;
>+ #address-cells = <3>;
>+ compatible = "ibm,plb-pciex-460sx", "ibm,plb-pciex";
>+ primary;
>+ port = <0x0>; /* port number */
>+ reg = <0x0000000d 0x00000000 0x20000000 /* Config space access */
>+ 0x0000000c 0x10000000 0x00001000>; /* Registers */
>+ dcr-reg = <0x100 0x020>;
>+ sdr-base = <0x300>;
>+
>+ /* Outbound ranges, one memory and one IO,
>+ * later cannot be changed
>+ */
>+ ranges = <0x02000000 0x00000000 0x80000000 0x0000000e 0x00000000 0x00000000 0x80000000
>+ 0x01000000 0x00000000 0x00000000 0x0000000f 0x80000000 0x00000000 0x00010000>;
>+
>+ /* Inbound 2GB range starting at 0 */
>+ dma-ranges = <0x42000000 0x0 0x0 0x0 0x0 0x0 0x80000000>;
>
>+ /* This drives busses 10 to 0x1f */
>+ bus-range = <0x10 0x1f>;
>+
>+ /* Legacy interrupts (note the weird polarity, the bridge seems
>+ * to invert PCIe legacy interrupts).
>+ * We are de-swizzling here because the numbers are actually for
>+ * port of the root complex virtual P2P bridge. But I want
>+ * to avoid putting a node for it in the tree, so the numbers
>+ * below are basically de-swizzled numbers.
>+ * The real slot is on idsel 0, so the swizzling is 1:1
>+ */
>+ interrupt-map-mask = <0x0 0x0 0x0 0x7>;
>+ interrupt-map = <
>+ 0x0 0x0 0x0 0x1 &UIC3 0x0 0x4 /* swizzled int A */
>+ 0x0 0x0 0x0 0x2 &UIC3 0x1 0x4 /* swizzled int B */
>+ 0x0 0x0 0x0 0x3 &UIC3 0x2 0x4 /* swizzled int C */
>+ 0x0 0x0 0x0 0x4 &UIC3 0x3 0x4 /* swizzled int D */>;
>+ };
>+
>+ PCIE1: pciex@d20000000 {
>+ device_type = "pci";
>+ #interrupt-cells = <1>;
>+ #size-cells = <2>;
>+ #address-cells = <3>;
>+ compatible = "ibm,plb-pciex-460sx", "ibm,plb-pciex";
>+ primary;
>+ port = <0x1>; /* port number */
>+ reg = <0x0000000d 0x20000000 0x20000000 /* Config space access */
>+ 0x0000000c 0x10001000 0x00001000>; /* Registers */
>+ dcr-reg = <0x120 0x020>;
>+ sdr-base = <0x340>;
>+
>+ /* Outbound ranges, one memory and one IO,
>+ * later cannot be changed
>+ */
>+ ranges = <0x02000000 0x00000000 0x80000000 0x0000000e 0x80000000 0x00000000 0x80000000
>+ 0x01000000 0x00000000 0x00000000 0x0000000f 0x80010000 0x00000000 0x00010000>;
>+
>+ /* Inbound 2GB range starting at 0 */
>+ dma-ranges = <0x42000000 0x0 0x0 0x0 0x0 0x0 0x80000000>;
>+
>+ /* This drives busses 10 to 0x1f */
>+ bus-range = <0x20 0x2f>;
>+
>+ /* Legacy interrupts (note the weird polarity, the bridge seems
>+ * to invert PCIe legacy interrupts).
>+ * We are de-swizzling here because the numbers are actually for
>+ * port of the root complex virtual P2P bridge. But I want
>+ * to avoid putting a node for it in the tree, so the numbers
>+ * below are basically de-swizzled numbers.
>+ * The real slot is on idsel 0, so the swizzling is 1:1
>+ */
>+ interrupt-map-mask = <0x0 0x0 0x0 0x7>;
>+ interrupt-map = <
>+ 0x0 0x0 0x0 0x1 &UIC3 0x4 0x4 /* swizzled int A */
>+ 0x0 0x0 0x0 0x2 &UIC3 0x5 0x4 /* swizzled int B */
>+ 0x0 0x0 0x0 0x3 &UIC3 0x6 0x4 /* swizzled int C */
>+ 0x0 0x0 0x0 0x4 &UIC3 0x7 0x4 /* swizzled int D */>;
>+ };
>+
>+ PCIE2: pciex@d40000000 {
>+ device_type = "pci";
>+ #interrupt-cells = <1>;
>+ #size-cells = <2>;
>+ #address-cells = <3>;
>+ compatible = "ibm,plb-pciex-460sx", "ibm,plb-pciex";
>+ primary;
>+ port = <0x2>; /* port number */
>+ reg = <0x0000000d 0x40000000 0x20000000 /* Config space access */
>+ 0x0000000c 0x10002000 0x00001000>; /* Registers */
>+ dcr-reg = <0x140 0x020>;
>+ sdr-base = <0x370>;
>+
>+ /* Outbound ranges, one memory and one IO,
>+ * later cannot be changed
>+ */
>+ ranges = <0x02000000 0x00000000 0x80000000 0x0000000f 0x00000000 0x00000000 0x80000000
>+ 0x01000000 0x00000000 0x00000000 0x0000000f 0x80020000 0x00000000 0x00010000>;
>+
>+ /* Inbound 2GB range starting at 0 */
>+ dma-ranges = <0x42000000 0x0 0x0 0x0 0x0 0x0 0x80000000>;
>+
>+ /* This drives busses 10 to 0x1f */
>+ bus-range = <0x30 0x3f>;
>+
>+ /* Legacy interrupts (note the weird polarity, the bridge seems
>+ * to invert PCIe legacy interrupts).
>+ * We are de-swizzling here because the numbers are actually for
>+ * port of the root complex virtual P2P bridge. But I want
>+ * to avoid putting a node for it in the tree, so the numbers
>+ * below are basically de-swizzled numbers.
>+ * The real slot is on idsel 0, so the swizzling is 1:1
>+ */
>+ interrupt-map-mask = <0x0 0x0 0x0 0x7>;
>+ interrupt-map = <
>+ 0x0 0x0 0x0 0x1 &UIC3 0x8 0x4 /* swizzled int A */
>+ 0x0 0x0 0x0 0x2 &UIC3 0x9 0x4 /* swizzled int B */
>+ 0x0 0x0 0x0 0x3 &UIC3 0xa 0x4 /* swizzled int C */
>+ 0x0 0x0 0x0 0x4 &UIC3 0xb 0x4 /* swizzled int D */>;
> };
>
> };
>+
> chosen {
> linux,stdout-path = "/plb/opb/serial@ef600200";
> };
>diff --git a/arch/powerpc/sysdev/ppc4xx_pci.c b/arch/powerpc/sysdev/ppc4xx_pci.c
>index 6ff9d71..64cd020 100644
>--- a/arch/powerpc/sysdev/ppc4xx_pci.c
>+++ b/arch/powerpc/sysdev/ppc4xx_pci.c
>@@ -972,6 +972,123 @@ static struct ppc4xx_pciex_hwops ppc460ex_pcie_hwops __initdata =
> .setup_utl = ppc460ex_pciex_init_utl,
> };
>
>+static int __init ppc460sx_pciex_core_init(struct device_node *np)
>+{
>+ /* HSS drive amplitude */
>+ mtdcri(SDR0, PESDR0_460SX_HSSL0DAMP, 0xB9843211);
>+ mtdcri(SDR0, PESDR0_460SX_HSSL1DAMP, 0xB9843211);
>+ mtdcri(SDR0, PESDR0_460SX_HSSL2DAMP, 0xB9843211);
>+ mtdcri(SDR0, PESDR0_460SX_HSSL3DAMP, 0xB9843211);
>+ mtdcri(SDR0, PESDR0_460SX_HSSL4DAMP, 0xB9843211);
>+ mtdcri(SDR0, PESDR0_460SX_HSSL5DAMP, 0xB9843211);
>+ mtdcri(SDR0, PESDR0_460SX_HSSL6DAMP, 0xB9843211);
>+ mtdcri(SDR0, PESDR0_460SX_HSSL7DAMP, 0xB9843211);
>+
>+ mtdcri(SDR0, PESDR1_460SX_HSSL0DAMP, 0xB9843211);
>+ mtdcri(SDR0, PESDR1_460SX_HSSL1DAMP, 0xB9843211);
>+ mtdcri(SDR0, PESDR1_460SX_HSSL2DAMP, 0xB9843211);
>+ mtdcri(SDR0, PESDR1_460SX_HSSL3DAMP, 0xB9843211);
>+
>+ mtdcri(SDR0, PESDR2_460SX_HSSL0DAMP, 0xB9843211);
>+ mtdcri(SDR0, PESDR2_460SX_HSSL1DAMP, 0xB9843211);
>+ mtdcri(SDR0, PESDR2_460SX_HSSL2DAMP, 0xB9843211);
>+ mtdcri(SDR0, PESDR2_460SX_HSSL3DAMP, 0xB9843211);
>+
>+ /* HSS TX pre-emphasis */
>+ mtdcri(SDR0, PESDR0_460SX_HSSL0COEFA, 0xDCB98987);
>+ mtdcri(SDR0, PESDR0_460SX_HSSL1COEFA, 0xDCB98987);
>+ mtdcri(SDR0, PESDR0_460SX_HSSL2COEFA, 0xDCB98987);
>+ mtdcri(SDR0, PESDR0_460SX_HSSL3COEFA, 0xDCB98987);
>+ mtdcri(SDR0, PESDR0_460SX_HSSL4COEFA, 0xDCB98987);
>+ mtdcri(SDR0, PESDR0_460SX_HSSL5COEFA, 0xDCB98987);
>+ mtdcri(SDR0, PESDR0_460SX_HSSL6COEFA, 0xDCB98987);
>+ mtdcri(SDR0, PESDR0_460SX_HSSL7COEFA, 0xDCB98987);
>+
>+ mtdcri(SDR0, PESDR1_460SX_HSSL0COEFA, 0xDCB98987);
>+ mtdcri(SDR0, PESDR1_460SX_HSSL1COEFA, 0xDCB98987);
>+ mtdcri(SDR0, PESDR1_460SX_HSSL2COEFA, 0xDCB98987);
>+ mtdcri(SDR0, PESDR1_460SX_HSSL3COEFA, 0xDCB98987);
>+
>+ mtdcri(SDR0, PESDR2_460SX_HSSL0COEFA, 0xDCB98987);
>+ mtdcri(SDR0, PESDR2_460SX_HSSL1COEFA, 0xDCB98987);
>+ mtdcri(SDR0, PESDR2_460SX_HSSL2COEFA, 0xDCB98987);
>+ mtdcri(SDR0, PESDR2_460SX_HSSL3COEFA, 0xDCB98987);
>+
>+ /* HSS TX calibration control */
>+ mtdcri(SDR0, PESDR0_460SX_HSSL1CALDRV, 0x22222222);
>+ mtdcri(SDR0, PESDR1_460SX_HSSL1CALDRV, 0x22220000);
>+ mtdcri(SDR0, PESDR2_460SX_HSSL1CALDRV, 0x22220000);
>+
>+ /* HSS TX slew control */
>+ mtdcri(SDR0, PESDR0_460SX_HSSSLEW, 0xFFFFFFFF);
>+ mtdcri(SDR0, PESDR1_460SX_HSSSLEW, 0xFFFF0000);
>+ mtdcri(SDR0, PESDR2_460SX_HSSSLEW, 0xFFFF0000);
>+
>+ udelay(100);
>+
>+ /* De-assert PLLRESET */
>+ dcri_clrset(SDR0, PESDR0_PLLLCT2, 0x00000100, 0);
>+
>+ /* Reset DL, UTL, GPL before configuration */
>+ mtdcri(SDR0, PESDR0_460SX_RCSSET,
>+ PESDRx_RCSSET_RSTDL | PESDRx_RCSSET_RSTGU);
>+ mtdcri(SDR0, PESDR1_460SX_RCSSET,
>+ PESDRx_RCSSET_RSTDL | PESDRx_RCSSET_RSTGU);
>+ mtdcri(SDR0, PESDR2_460SX_RCSSET,
>+ PESDRx_RCSSET_RSTDL | PESDRx_RCSSET_RSTGU);
>+
>+ udelay(100);
>+
>+ /*
>+ * If bifurcation is not enabled, u-boot would have disabled the
>+ * third PCIe port
>+ */
>+ if (((mfdcri(SDR0, PESDR1_460SX_HSSCTLSET) & 0x00000001) ==
>+ 0x00000001)) {
>+ printk(KERN_INFO "PCI: PCIE bifurcation setup successfully.\n");
>+ printk(KERN_INFO "PCI: Total 3 PCIE ports are present\n");
>+ return 3;
>+ }
>+
>+ printk(KERN_INFO "PCI: Total 2 PCIE ports are present\n");
>+ return 2;
>+}
>+
>+static int ppc460sx_pciex_init_port_hw(struct ppc4xx_pciex_port *port)
>+{
>+
>+ if (port->endpoint)
>+ dcri_clrset(SDR0, port->sdr_base + PESDRn_UTLSET2,
>+ 0x01000000, 0);
>+ else
>+ dcri_clrset(SDR0, port->sdr_base + PESDRn_UTLSET2,
>+ 0, 0x01000000);
>+
>+ /*Gen-1*/
>+ mtdcri(SDR0, port->sdr_base + PESDRn_460SX_RCEI, 0x08000000);
>+
>+ dcri_clrset(SDR0, port->sdr_base + PESDRn_RCSSET,
>+ (PESDRx_RCSSET_RSTGU | PESDRx_RCSSET_RSTDL),
>+ PESDRx_RCSSET_RSTPYN);
>+
>+ port->has_ibpre = 1;
>+
>+ return 0;
>+}
>+
>+static int ppc460sx_pciex_init_utl(struct ppc4xx_pciex_port *port)
>+{
>+ /* Max 128 Bytes */
>+ out_be32 (port->utl_base + PEUTL_PBBSZ, 0x00000000);
>+ return 0;
>+}
>+
>+static struct ppc4xx_pciex_hwops ppc460sx_pcie_hwops __initdata = {
>+ .core_init = ppc460sx_pciex_core_init,
>+ .port_init_hw = ppc460sx_pciex_init_port_hw,
>+ .setup_utl = ppc460sx_pciex_init_utl,
>+};
>+
> #endif /* CONFIG_44x */
>
> #ifdef CONFIG_40x
>@@ -1087,6 +1204,8 @@ static int __init ppc4xx_pciex_check_core_init(struct device_node *np)
> }
> if (of_device_is_compatible(np, "ibm,plb-pciex-460ex"))
> ppc4xx_pciex_hwops = &ppc460ex_pcie_hwops;
>+ if (of_device_is_compatible(np, "ibm,plb-pciex-460sx"))
>+ ppc4xx_pciex_hwops = &ppc460sx_pcie_hwops;
> #endif /* CONFIG_44x */
> #ifdef CONFIG_40x
> if (of_device_is_compatible(np, "ibm,plb-pciex-405ex"))
>diff --git a/arch/powerpc/sysdev/ppc4xx_pci.h b/arch/powerpc/sysdev/ppc4xx_pci.h
>index d04e40b..56d9e5d 100644
>--- a/arch/powerpc/sysdev/ppc4xx_pci.h
>+++ b/arch/powerpc/sysdev/ppc4xx_pci.h
>@@ -324,6 +324,64 @@
> #define PESDR0_460EX_IHS2 0x036D
>
> /*
>+ * 460SX addtional DCRs
>+ */
>+#define PESDRn_460SX_RCEI 0x02
>+
>+#define PESDR0_460SX_HSSL0DAMP 0x320
>+#define PESDR0_460SX_HSSL1DAMP 0x321
>+#define PESDR0_460SX_HSSL2DAMP 0x322
>+#define PESDR0_460SX_HSSL3DAMP 0x323
>+#define PESDR0_460SX_HSSL4DAMP 0x324
>+#define PESDR0_460SX_HSSL5DAMP 0x325
>+#define PESDR0_460SX_HSSL6DAMP 0x326
>+#define PESDR0_460SX_HSSL7DAMP 0x327
>+
>+#define PESDR1_460SX_HSSL0DAMP 0x354
>+#define PESDR1_460SX_HSSL1DAMP 0x355
>+#define PESDR1_460SX_HSSL2DAMP 0x356
>+#define PESDR1_460SX_HSSL3DAMP 0x357
>+
>+#define PESDR2_460SX_HSSL0DAMP 0x384
>+#define PESDR2_460SX_HSSL1DAMP 0x385
>+#define PESDR2_460SX_HSSL2DAMP 0x386
>+#define PESDR2_460SX_HSSL3DAMP 0x387
>+
>+#define PESDR0_460SX_HSSL0COEFA 0x328
>+#define PESDR0_460SX_HSSL1COEFA 0x329
>+#define PESDR0_460SX_HSSL2COEFA 0x32A
>+#define PESDR0_460SX_HSSL3COEFA 0x32B
>+#define PESDR0_460SX_HSSL4COEFA 0x32C
>+#define PESDR0_460SX_HSSL5COEFA 0x32D
>+#define PESDR0_460SX_HSSL6COEFA 0x32E
>+#define PESDR0_460SX_HSSL7COEFA 0x32F
>+
>+#define PESDR1_460SX_HSSL0COEFA 0x358
>+#define PESDR1_460SX_HSSL1COEFA 0x359
>+#define PESDR1_460SX_HSSL2COEFA 0x35A
>+#define PESDR1_460SX_HSSL3COEFA 0x35B
>+
>+#define PESDR2_460SX_HSSL0COEFA 0x388
>+#define PESDR2_460SX_HSSL1COEFA 0x389
>+#define PESDR2_460SX_HSSL2COEFA 0x38A
>+#define PESDR2_460SX_HSSL3COEFA 0x38B
>+
>+#define PESDR0_460SX_HSSL1CALDRV 0x339
>+#define PESDR1_460SX_HSSL1CALDRV 0x361
>+#define PESDR2_460SX_HSSL1CALDRV 0x391
>+
>+#define PESDR0_460SX_HSSSLEW 0x338
>+#define PESDR1_460SX_HSSSLEW 0x360
>+#define PESDR2_460SX_HSSSLEW 0x390
>+
>+#define PESDR0_460SX_HSSCTLSET 0x31E
>+#define PESDR1_460SX_HSSCTLSET 0x352
>+#define PESDR2_460SX_HSSCTLSET 0x382
>+
>+#define PESDR0_460SX_RCSSET 0x304
>+#define PESDR1_460SX_RCSSET 0x344
>+#define PESDR2_460SX_RCSSET 0x374
>+/*
> * Of the above, some are common offsets from the base
> */
> #define PESDRn_UTLSET1 0x00
>--
>1.6.1.rc3
>
>_______________________________________________
>Linuxppc-dev mailing list
>Linuxppc-dev@lists.ozlabs.org
>https://lists.ozlabs.org/listinfo/linuxppc-dev
^ permalink raw reply
* Re: [PATCH 2/2] Adding PCI-E MSI support for PowerPC 460SX SOC.
From: Josh Boyer @ 2009-12-22 12:07 UTC (permalink / raw)
To: tmarri; +Cc: linuxppc-dev, writetomarri
In-Reply-To: <1261471791-476-1-git-send-email-tmarri@amcc.com>
On Tue, Dec 22, 2009 at 12:49:51AM -0800, tmarri@amcc.com wrote:
>From: Tirumala Marri <tmarri@amcc.com>
>
>
>Signed-off-by: Tirumala Marri <tmarri@amcc.com>
>---
>Kernel version: 2.6.33-rc1
>Testing:
> When 460SX configured as root as a end point E1000(Intell Ethernet card)
> was plugged into the one of the PCI-E ports. I was able to run the traffic
> with MSI interrupts.
>---
> arch/powerpc/boot/dts/redwood.dts | 15 ++
> arch/powerpc/configs/44x/redwood_defconfig | 5 +-
> arch/powerpc/platforms/44x/Kconfig | 1 +
> arch/powerpc/sysdev/Kconfig | 7 +
> arch/powerpc/sysdev/Makefile | 1 +
> arch/powerpc/sysdev/ppc4xx_msi.c | 335 ++++++++++++++++++++++++++++
> arch/powerpc/sysdev/ppc4xx_msi.h | 49 ++++
> 7 files changed, 411 insertions(+), 2 deletions(-)
> create mode 100644 arch/powerpc/sysdev/ppc4xx_msi.c
> create mode 100644 arch/powerpc/sysdev/ppc4xx_msi.h
>
>diff --git a/arch/powerpc/boot/dts/redwood.dts b/arch/powerpc/boot/dts/redwood.dts
>index 81636c0..412d5f9 100644
>--- a/arch/powerpc/boot/dts/redwood.dts
>+++ b/arch/powerpc/boot/dts/redwood.dts
>@@ -357,6 +357,21 @@
> 0x0 0x0 0x0 0x3 &UIC3 0xa 0x4 /* swizzled int C */
> 0x0 0x0 0x0 0x4 &UIC3 0xb 0x4 /* swizzled int D */>;
> };
>+ MSI: ppc4xx-msi@400300000 {
>+ compatible = "amcc,ppc4xx-msi", "ppc4xx-msi";
>+ reg = < 0x4 0x00300000 0x100
>+ 0x4 0x00300000 0x100>;
>+ sdr-base = <0x3B0>;
>+ interrupts =<0 1 2 3>;
>+ interrupt-parent = <&MSI>;
>+ #interrupt-cells = <1>;
>+ #address-cells = <0>;
>+ #size-cells = <0>;
>+ interrupt-map = <0 &UIC0 0xC 1
>+ 1 &UIC0 0x0D 1
>+ 2 &UIC0 0x0E 1
>+ 3 &UIC0 0x0F 1>;
>+ };
>
> };
>
>diff --git a/arch/powerpc/sysdev/Kconfig b/arch/powerpc/sysdev/Kconfig
>index 3965828..32f5a40 100644
>--- a/arch/powerpc/sysdev/Kconfig
>+++ b/arch/powerpc/sysdev/Kconfig
>@@ -7,8 +7,15 @@ config PPC4xx_PCI_EXPRESS
> depends on PCI && 4xx
> default n
>
>+config 4xx_MSI
This should probably be named PPC4xx_MSI, similar to how
PPC4xx_PCI_EXPRESS is named.
>+ bool
>+ depends on PCI_MSI
>+ depends on PCI && 4xx
>+ default n
>+
> config PPC_MSI_BITMAP
> bool
> depends on PCI_MSI
> default y if MPIC
> default y if FSL_PCI
>+ default y if 4xx_MSI
>diff --git a/arch/powerpc/sysdev/ppc4xx_msi.c b/arch/powerpc/sysdev/ppc4xx_msi.c
>new file mode 100644
>index 0000000..752da4b
>--- /dev/null
>+++ b/arch/powerpc/sysdev/ppc4xx_msi.c
>@@ -0,0 +1,335 @@
>+/*
>+ * Copyright (C) 2009 Applied Micro Circuits corporation,
>+ * All rights reserved.
Please don't add the 'All rights reserved.' to new files. It is
inaccurate and confusing given that it's a GPLv2 file.
>+ *
>+ * Author: Feng Kan <fkan@amcc.com>
>+ * Tirumala Marri <tmarri@amcc.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; version 2 of the
>+ * License.
>+ */
>+#include <linux/irq.h>
>+#include <linux/bootmem.h>
>+#include <linux/pci.h>
>+#include <linux/msi.h>
>+#include <linux/of_platform.h>
>+#include <linux/interrupt.h>
>+#include <linux/device.h>
>+#include <asm/prom.h>
>+#include <asm/hw_irq.h>
>+#include <asm/ppc-pci.h>
>+#include <asm/dcr.h>
>+#include <asm/dcr-regs.h>
>+#include "ppc4xx_msi.h"
>+
>+
>+static struct ppc4xx_msi *ppc4xx_msi;
>+
>+struct ppc4xx_msi_feature {
>+ u32 ppc4xx_pic_ip;
>+ u32 msiir_offset;
>+};
>+
>+static int ppc4xx_msi_init_allocator(struct ppc4xx_msi *msi_data)
>+{
>+ int rc;
>+
>+ rc = msi_bitmap_alloc(&msi_data->bitmap, NR_MSI_IRQS,
>+ msi_data->irqhost->of_node);
>+ if (rc)
>+ return rc;
>+ rc = msi_bitmap_reserve_dt_hwirqs(&msi_data->bitmap);
>+ if (rc < 0) {
>+ msi_bitmap_free(&msi_data->bitmap);
>+ return rc;
>+ }
>+ return 0;
>+}
>+
>+
>+static void ppc4xx_msi_cascade(unsigned int irq, struct irq_desc *desc)
>+{
>+ unsigned int cascade_irq;
>+ struct ppc4xx_msi *msi_data = ppc4xx_msi;
>+ int msir_index = -1;
>+
>+ raw_spin_lock(&desc->lock);
>+ if (desc->chip->mask_ack) {
>+ desc->chip->mask_ack(irq);
>+ } else {
>+ desc->chip->mask(irq);
>+ desc->chip->ack(irq);
>+ }
>+
>+ if (unlikely(desc->status & IRQ_INPROGRESS))
>+ goto unlock;
>+
>+ msir_index = (int)desc->handler_data;
>+
>+ if (msir_index >= NR_MSI_IRQS)
>+ cascade_irq = NO_IRQ;
>+
>+ desc->status |= IRQ_INPROGRESS;
>+
>+ cascade_irq = irq_linear_revmap(msi_data->irqhost, msir_index);
>+ if (cascade_irq != NO_IRQ)
>+ generic_handle_irq(cascade_irq);
>+ desc->status &= ~IRQ_INPROGRESS;
>+
>+ if (!(desc->status & IRQ_DISABLED) && desc->chip->unmask)
>+ desc->chip->unmask(irq);
>+unlock:
>+ raw_spin_unlock(&desc->lock);
>+}
>+static void ppc4xx_compose_msi_msg(struct pci_dev *pdev, int hwirq,
>+ struct msi_msg *msg)
>+{
>+ struct ppc4xx_msi *msi_data = ppc4xx_msi;
>+
>+ msg->address_lo = msi_data->msi_addr_lo;
>+ msg->address_hi = msi_data->msi_addr_hi;
>+ msg->data = hwirq;
>+}
>+
>+
>+int ppc4xx_setup_msi_irqs(struct pci_dev *dev, int nvec, int type)
>+{
>+ struct msi_desc *entry;
>+ int rc, hwirq;
>+ unsigned int virq;
>+ struct msi_msg msg;
>+ struct ppc4xx_msi *msi_data = ppc4xx_msi;
>+
>+
>+ list_for_each_entry(entry, &dev->msi_list, list) {
>+ hwirq = msi_bitmap_alloc_hwirqs(&msi_data->bitmap, 1);
>+ if (hwirq < 0) {
>+ rc = hwirq;
>+ dev_err(&dev->dev, "%s: fail allocating msi\
>+ interrupt\n", __func__);
>+ goto out_free;
>+ }
>+
>+ pr_debug(KERN_INFO"mis is %p\n", msi_data->irqhost);
>+ virq = irq_create_mapping(msi_data->irqhost, hwirq);
>+ if (virq == NO_IRQ) {
>+ dev_err(&dev->dev, "%s: fail mapping irq\n", __func__);
>+ rc = -ENOSPC;
>+ goto out_free;
>+ }
>+
>+ set_irq_msi(virq, entry);
>+ ppc4xx_compose_msi_msg(dev, hwirq, &msg);
>+ write_msi_msg(virq, &msg);
>+ }
>+
>+ return 0;
>+out_free:
>+ return rc;
>+}
>+
>+void ppc4xx_teardown_msi_irqs(struct pci_dev *dev)
>+{
>+ struct msi_desc *entry;
>+ struct ppc4xx_msi *msi_data = ppc4xx_msi;
>+ dev_dbg(&dev->dev, "PCIE-MSI: tearing down msi irqs\n");
>+
>+ list_for_each_entry(entry, &dev->msi_list, list) {
>+ if (entry->irq == NO_IRQ)
>+ continue;
>+ set_irq_msi(entry->irq, NULL);
>+ msi_bitmap_free_hwirqs(&msi_data->bitmap,
>+ virq_to_hw(entry->irq), 1);
>+ irq_dispose_mapping(entry->irq);
>+
>+ }
>+
>+ return;
>+}
>+
>+static int ppc4xx_msi_check_device(struct pci_dev *pdev, int nvec, int type)
>+{
>+ pr_debug(KERN_INFO"PCIE-MSI:%s called. vec %x type %d\n",
>+ __func__, nvec, type);
>+ return 0;
>+}
>+
>+/*
>+ * We do not need this actually. The MSIR register has been read once
>+ * in the cascade interrupt. So, this MSI interrupt has been acked
>+*/
>+static void ppc4xx_msi_end_irq(unsigned int virq)
>+{
>+}
>+
>+
>+static struct irq_chip ppc4xx_msi_chip = {
>+ .mask = mask_msi_irq,
>+ .unmask = unmask_msi_irq,
>+ .ack = ppc4xx_msi_end_irq,
>+ .name = " UIC",
>+};
>+
>+static int ppc4xx_msi_host_map(struct irq_host *h, unsigned int virq,
>+ irq_hw_number_t hw)
>+{
>+ struct irq_chip *chip = &ppc4xx_msi_chip;
>+
>+ irq_to_desc(virq)->status |= IRQ_TYPE_EDGE_RISING;
>+
>+ set_irq_chip_and_handler(virq, chip, handle_edge_irq);
>+
>+ return 0;
>+}
>+
>+static struct irq_host_ops ppc4xx_msi_host_ops = {
>+ .map = ppc4xx_msi_host_map,
>+};
>+
>+
>+static int __devinit ppc4xx_msi_probe(struct of_device *dev,
>+ const struct of_device_id *match)
>+{
>+ struct ppc4xx_msi *msi;
>+ struct resource res, rmsi;
>+ int i, count;
>+ int rc;
>+ int virt_msir;
>+ const u32 *p;
>+ u32 *msi_virt = NULL;
>+ dma_addr_t msi_phys;
>+
>+
>+ msi = kzalloc(sizeof(struct ppc4xx_msi), GFP_KERNEL);
>+ if (!msi) {
>+ dev_err(&dev->dev, "No memory for MSI structure\n");
>+ rc = -ENOMEM;
>+ goto error_out;
>+ }
>+
>+ msi->irqhost = irq_alloc_host(dev->node, IRQ_HOST_MAP_LINEAR,
>+ NR_MSI_IRQS, &ppc4xx_msi_host_ops, 0);
>+ if (msi->irqhost == NULL) {
>+ dev_err(&dev->dev, "No memory for MSI irqhost\n");
>+ rc = -ENOMEM;
>+ goto error_out;
>+ }
>+
>+
>+ /* Get MSI ranges */
>+ rc = of_address_to_resource(dev->node, 0, &rmsi);
>+ if (rc) {
>+ dev_err(&dev->dev, "%s resource error!\n",
>+ dev->node->full_name);
>+ goto error_out;
>+ }
>+
>+
>+ /* Get the MSI reg base */
>+ rc = of_address_to_resource(dev->node, 1, &res);
>+ if (rc) {
>+ dev_err(&dev->dev, "%s resource error!\n",
>+ dev->node->full_name);
>+ goto error_out;
>+ }
>+#if defined(CONFIG_460SX)
>+ mtdcri(SDR0, SDR0_PCIEH_H, PCIE_MSI_REG_BASE_H);
>+ mtdcri(SDR0, SDR0_PCIEH_L, PCIE_MSI_REG_BASE_L);
>+ msi->msi_regs = ioremap(((u64)PCIE_MSI_REG_BASE_H << 32) | res.start,
>+ res.end - res.start + 1);
You defined sdr-base in the device tree. Please use it instead of the hard
coding.
>+#else
>+ dev_err(&dev->dev, " Invalid Device \n");
>+ goto error_out;
>+#endif
>+ if (!msi->msi_regs) {
>+ dev_err(&dev->dev, "ioremap problem failed\n");
>+ goto error_out;
>+ }
>+ /* MSI region always mapped in 4GB region*/
>+ msi->msi_addr_hi = 0x0;
>+ msi_virt = dma_alloc_coherent(&dev->dev, 64, &msi_phys,
>+ GFP_KERNEL);
>+ if (msi_virt == NULL) {
>+ dev_err(&dev->dev, "No memory for MSI mem space\n");
>+ rc = -ENOMEM;
>+ goto error_out;
>+ }
>+ msi->msi_addr_lo = (u32)msi_phys;
>+
>+ /* Progam the Interrupt handler Termination addr registers */
>+ out_be32(msi->msi_regs + PEIH_TERMADH, msi->msi_addr_hi);
>+ out_be32(msi->msi_regs + PEIH_TERMADL, msi->msi_addr_lo);
>+
>+ /* Program MSI Expected data and Mask bits */
>+ out_be32(msi->msi_regs + PEIH_MSIED, MSI_DATA_PATTERN);
>+ out_be32(msi->msi_regs + PEIH_MSIMK, MSI_DATA_PATTERN);
>+
>+ msi->irqhost->host_data = msi;
>+
>+ if (ppc4xx_msi_init_allocator(msi)) {
>+ dev_err(&dev->dev, "Error allocating MSI bitmap\n");
>+ goto error_out;
>+ }
>+
>+ p = of_get_property(dev->node, "interrupts", &count);
>+ if (!p) {
>+ dev_err(&dev->dev, "no interrupts property found on %s\n",
>+ dev->node->full_name);
>+ rc = -ENODEV;
>+ goto error_out;
>+ }
>+ if (count == 0) {
>+ dev_err(&dev->dev, "Malformed interrupts property on %s\n",
>+ dev->node->full_name);
>+ rc = -EINVAL;
>+ goto error_out;
>+ }
>+
>+ for (i = 0; i < NR_MSI_IRQS; i++) {
>+ virt_msir = irq_of_parse_and_map(dev->node, i);
>+ if (virt_msir != NO_IRQ) {
>+ set_irq_data(virt_msir, (void *)i);
>+ set_irq_chained_handler(virt_msir, ppc4xx_msi_cascade);
>+ }
>+ }
>+
>+ ppc4xx_msi = msi;
>+
>+ WARN_ON(ppc_md.setup_msi_irqs);
>+ ppc_md.setup_msi_irqs = ppc4xx_setup_msi_irqs;
>+ ppc_md.teardown_msi_irqs = ppc4xx_teardown_msi_irqs;
>+ ppc_md.msi_check_device = ppc4xx_msi_check_device;
>+ return 0;
>+error_out:
>+ if (msi_virt)
>+ dma_free_coherent(&dev->dev, 64, msi_virt, msi_phys);
>+ kfree(msi);
>+ return rc;
>+}
>+
>+static const struct ppc4xx_msi_feature ppc4xx_msi_feature = {
>+ .ppc4xx_pic_ip = 0,
>+ .msiir_offset = 0x140,
>+};
>+
>+static const struct of_device_id ppc4xx_msi_ids[] = {
>+ {
>+ .compatible = "amcc,ppc4xx-msi",
>+ .data = (void *)&ppc4xx_msi_feature,
>+ },
>+ {}
>+};
>+
>+static struct of_platform_driver ppc4xx_msi_driver = {
>+ .name = "ppc4xx-msi",
>+ .match_table = ppc4xx_msi_ids,
>+ .probe = ppc4xx_msi_probe,
>+};
>+
>+static __init int ppc4xx_msi_init(void)
>+{
>+ return of_register_platform_driver(&ppc4xx_msi_driver);
>+}
>+
>+subsys_initcall(ppc4xx_msi_init);
>diff --git a/arch/powerpc/sysdev/ppc4xx_msi.h b/arch/powerpc/sysdev/ppc4xx_msi.h
>new file mode 100644
>index 0000000..7b8ac5c
>--- /dev/null
>+++ b/arch/powerpc/sysdev/ppc4xx_msi.h
>@@ -0,0 +1,49 @@
>+/*
>+ * Copyright (C) 2009 Applied Micro Circuits Corporation,
>+ * All rights reserved.
Same comment as above.
>+ *
>+ * Author: T irumala Marri <tmarri@amcc.com>
>+ * Feng Kan <fkan@amcc.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; version 2 of the
>+ * License.
>+ */
>+#ifndef __PPC4XX_MSI_H__
>+#define __PPC4XX_MSI_H__
>+
>+#include <asm/msi_bitmap.h>
>+
>+#define PEIH_TERMADH 0x00
>+#define PEIH_TERMADL 0x08
>+#define PEIH_MSIED 0x10
>+#define PEIH_MSIMK 0x18
>+#define PEIH_MSIASS 0x20
>+#define PEIH_FLUSH0 0x30
>+#define PEIH_FLUSH1 0x38
>+#define PEIH_CNTRST 0x48
>+
>+#define MSI_DATA_PATTERN 0x44440000
>+
>+#if defined(CONFIG_405Ex)
>+#define SDR0_PCIEH 0x4B1
>+#define PCIE_MSI_REG_BASE 0xef620000
>+#elif defined(CONFIG_440SPe) || defined(CONFIG_460SX)
>+#define SDR0_PCIEH_H 0x3B0
>+#define SDR0_PCIEH_L 0x3B1
>+#define PCIE_MSI_REG_BASE_L 0x00300000
>+#define PCIE_MSI_REG_BASE_H 0x00000004
>+#endif
sdr-base covers quite a bit of this.
>+
>+struct ppc4xx_msi {
>+ struct irq_host *irqhost;
>+ unsigned long cascade_irq;
>+ u32 msi_addr_lo;
>+ u32 msi_addr_hi;
>+ void __iomem *msi_regs;
>+ u32 feature;
>+ struct msi_bitmap bitmap;
>+};
Perhaps add an sdr_base member to this struct, similar to
how the pci port structure looks.
josh
^ permalink raw reply
* Re: [PATCH] of/flattree: use callback to setup initrd from /chosen
From: Michael Ellerman @ 2009-12-22 13:17 UTC (permalink / raw)
To: Jeremy Kerr; +Cc: microblaze-uclinux, devicetree-discuss, linuxppc-dev
In-Reply-To: <200912221854.37572.jeremy.kerr@canonical.com>
[-- Attachment #1: Type: text/plain, Size: 1137 bytes --]
On Tue, 2009-12-22 at 18:54 +0800, Jeremy Kerr wrote:
> Hi Michael,
>
> > > void early_init_dt_setup_initrd_arch(unsigned long start,
> > > unsigned long end);
> >
> > arch_early_init_dt_setup_initrd() makes more sense to me, but ..
>
> <foo>_arch has been the general convention for arch-specific hooks in
> drivers/of/.
Yuck, doh, guess I should have read those patches before they went in :)
> > > +#ifdef CONFIG_BLK_DEV_INITRD
> > > +void __init early_init_dt_setup_initrd_arch(unsigned long start,
> > > + unsigned long end)
> > > +{
> > > + initrd_start = (unsigned long)__va(start);
> > > + initrd_end = (unsigned long)__va(end);
> > > + initrd_below_start_ok = 1;
> > > +}
> > > +#endif
> >
> > Given you have two identical implementations why not make that the
> > default and make it weak, and let ARM override it.
>
> Yeah, that would be good too; just been avoiding weak as a potential source of
> magic voodoo complexity. Grant - up to you on this one.
Yeah, depends on what toolchains you're supporting, modern ones should
be OK but it can be troublesome.
cheers
[-- Attachment #2: This is a digitally signed message part --]
[-- Type: application/pgp-signature, Size: 197 bytes --]
^ permalink raw reply
* [PATCH 03/31] hvc_console: make the ops pointer const.
From: Amit Shah @ 2009-12-22 14:34 UTC (permalink / raw)
To: rusty; +Cc: Amit Shah, linuxppc-dev, virtualization
In-Reply-To: <1261492481-19817-3-git-send-email-amit.shah@redhat.com>
From: Rusty Russell <rusty@rustcorp.com.au>
This is nicer for modern R/O protection. And noone needs it non-const, so
constify the callers as well.
Signed-off-by: Rusty Russell <rusty@rustcorp.com.au>
Signed-off-by: Amit Shah <amit.shah@redhat.com>
To: Christian Borntraeger <borntraeger@de.ibm.com>
Cc: linuxppc-dev@ozlabs.org
---
drivers/char/hvc_beat.c | 2 +-
drivers/char/hvc_console.c | 7 ++++---
drivers/char/hvc_console.h | 7 ++++---
drivers/char/hvc_iseries.c | 2 +-
drivers/char/hvc_iucv.c | 2 +-
drivers/char/hvc_rtas.c | 2 +-
drivers/char/hvc_udbg.c | 2 +-
drivers/char/hvc_vio.c | 2 +-
drivers/char/hvc_xen.c | 2 +-
drivers/char/virtio_console.c | 2 +-
10 files changed, 16 insertions(+), 14 deletions(-)
diff --git a/drivers/char/hvc_beat.c b/drivers/char/hvc_beat.c
index 0afc8b8..6913fc3 100644
--- a/drivers/char/hvc_beat.c
+++ b/drivers/char/hvc_beat.c
@@ -84,7 +84,7 @@ static int hvc_beat_put_chars(uint32_t vtermno, const char *buf, int cnt)
return cnt;
}
-static struct hv_ops hvc_beat_get_put_ops = {
+static const struct hv_ops hvc_beat_get_put_ops = {
.get_chars = hvc_beat_get_chars,
.put_chars = hvc_beat_put_chars,
};
diff --git a/drivers/char/hvc_console.c b/drivers/char/hvc_console.c
index 416d342..d8dac58 100644
--- a/drivers/char/hvc_console.c
+++ b/drivers/char/hvc_console.c
@@ -125,7 +125,7 @@ static struct hvc_struct *hvc_get_by_index(int index)
* console interfaces but can still be used as a tty device. This has to be
* static because kmalloc will not work during early console init.
*/
-static struct hv_ops *cons_ops[MAX_NR_HVC_CONSOLES];
+static const struct hv_ops *cons_ops[MAX_NR_HVC_CONSOLES];
static uint32_t vtermnos[MAX_NR_HVC_CONSOLES] =
{[0 ... MAX_NR_HVC_CONSOLES - 1] = -1};
@@ -247,7 +247,7 @@ static void destroy_hvc_struct(struct kref *kref)
* vty adapters do NOT get an hvc_instantiate() callback since they
* appear after early console init.
*/
-int hvc_instantiate(uint32_t vtermno, int index, struct hv_ops *ops)
+int hvc_instantiate(uint32_t vtermno, int index, const struct hv_ops *ops)
{
struct hvc_struct *hp;
@@ -749,7 +749,8 @@ static const struct tty_operations hvc_ops = {
};
struct hvc_struct __devinit *hvc_alloc(uint32_t vtermno, int data,
- struct hv_ops *ops, int outbuf_size)
+ const struct hv_ops *ops,
+ int outbuf_size)
{
struct hvc_struct *hp;
int i;
diff --git a/drivers/char/hvc_console.h b/drivers/char/hvc_console.h
index 10950ca..52ddf4d 100644
--- a/drivers/char/hvc_console.h
+++ b/drivers/char/hvc_console.h
@@ -55,7 +55,7 @@ struct hvc_struct {
int outbuf_size;
int n_outbuf;
uint32_t vtermno;
- struct hv_ops *ops;
+ const struct hv_ops *ops;
int irq_requested;
int data;
struct winsize ws;
@@ -76,11 +76,12 @@ struct hv_ops {
};
/* Register a vterm and a slot index for use as a console (console_init) */
-extern int hvc_instantiate(uint32_t vtermno, int index, struct hv_ops *ops);
+extern int hvc_instantiate(uint32_t vtermno, int index,
+ const struct hv_ops *ops);
/* register a vterm for hvc tty operation (module_init or hotplug add) */
extern struct hvc_struct * __devinit hvc_alloc(uint32_t vtermno, int data,
- struct hv_ops *ops, int outbuf_size);
+ const struct hv_ops *ops, int outbuf_size);
/* remove a vterm from hvc tty operation (module_exit or hotplug remove) */
extern int hvc_remove(struct hvc_struct *hp);
diff --git a/drivers/char/hvc_iseries.c b/drivers/char/hvc_iseries.c
index 936d05b..fd02426 100644
--- a/drivers/char/hvc_iseries.c
+++ b/drivers/char/hvc_iseries.c
@@ -197,7 +197,7 @@ done:
return sent;
}
-static struct hv_ops hvc_get_put_ops = {
+static const struct hv_ops hvc_get_put_ops = {
.get_chars = get_chars,
.put_chars = put_chars,
.notifier_add = notifier_add_irq,
diff --git a/drivers/char/hvc_iucv.c b/drivers/char/hvc_iucv.c
index fe62bd0..21681a8 100644
--- a/drivers/char/hvc_iucv.c
+++ b/drivers/char/hvc_iucv.c
@@ -922,7 +922,7 @@ static int hvc_iucv_pm_restore_thaw(struct device *dev)
/* HVC operations */
-static struct hv_ops hvc_iucv_ops = {
+static const struct hv_ops hvc_iucv_ops = {
.get_chars = hvc_iucv_get_chars,
.put_chars = hvc_iucv_put_chars,
.notifier_add = hvc_iucv_notifier_add,
diff --git a/drivers/char/hvc_rtas.c b/drivers/char/hvc_rtas.c
index 88590d0..61c4a61 100644
--- a/drivers/char/hvc_rtas.c
+++ b/drivers/char/hvc_rtas.c
@@ -71,7 +71,7 @@ static int hvc_rtas_read_console(uint32_t vtermno, char *buf, int count)
return i;
}
-static struct hv_ops hvc_rtas_get_put_ops = {
+static const struct hv_ops hvc_rtas_get_put_ops = {
.get_chars = hvc_rtas_read_console,
.put_chars = hvc_rtas_write_console,
};
diff --git a/drivers/char/hvc_udbg.c b/drivers/char/hvc_udbg.c
index bd63ba8..b0957e6 100644
--- a/drivers/char/hvc_udbg.c
+++ b/drivers/char/hvc_udbg.c
@@ -58,7 +58,7 @@ static int hvc_udbg_get(uint32_t vtermno, char *buf, int count)
return i;
}
-static struct hv_ops hvc_udbg_ops = {
+static const struct hv_ops hvc_udbg_ops = {
.get_chars = hvc_udbg_get,
.put_chars = hvc_udbg_put,
};
diff --git a/drivers/char/hvc_vio.c b/drivers/char/hvc_vio.c
index 10be343..27370e9 100644
--- a/drivers/char/hvc_vio.c
+++ b/drivers/char/hvc_vio.c
@@ -77,7 +77,7 @@ static int filtered_get_chars(uint32_t vtermno, char *buf, int count)
return got;
}
-static struct hv_ops hvc_get_put_ops = {
+static const struct hv_ops hvc_get_put_ops = {
.get_chars = filtered_get_chars,
.put_chars = hvc_put_chars,
.notifier_add = notifier_add_irq,
diff --git a/drivers/char/hvc_xen.c b/drivers/char/hvc_xen.c
index b1a7163..60446f8 100644
--- a/drivers/char/hvc_xen.c
+++ b/drivers/char/hvc_xen.c
@@ -122,7 +122,7 @@ static int read_console(uint32_t vtermno, char *buf, int len)
return recv;
}
-static struct hv_ops hvc_ops = {
+static const struct hv_ops hvc_ops = {
.get_chars = read_console,
.put_chars = write_console,
.notifier_add = notifier_add_irq,
diff --git a/drivers/char/virtio_console.c b/drivers/char/virtio_console.c
index 1d844a4..791be4e 100644
--- a/drivers/char/virtio_console.c
+++ b/drivers/char/virtio_console.c
@@ -163,7 +163,7 @@ static void hvc_handle_input(struct virtqueue *vq)
}
/* The operations for the console. */
-static struct hv_ops hv_ops = {
+static const struct hv_ops hv_ops = {
.get_chars = get_chars,
.put_chars = put_chars,
.notifier_add = notifier_add_vio,
--
1.6.2.5
^ permalink raw reply related
* [PATCH 04/31] hvc_console: Remove __devinit annotation from hvc_alloc
From: Amit Shah @ 2009-12-22 14:34 UTC (permalink / raw)
To: rusty; +Cc: Amit Shah, linuxppc-dev, virtualization
In-Reply-To: <1261492481-19817-4-git-send-email-amit.shah@redhat.com>
Virtio consoles can be hotplugged, so hvc_alloc gets called from
multiple sites: from the initial probe() routine as well as later on
from workqueue handlers which aren't __devinit code.
So, drop the __devinit annotation for hvc_alloc.
Signed-off-by: Amit Shah <amit.shah@redhat.com>
Cc: linuxppc-dev@ozlabs.org
---
drivers/char/hvc_console.c | 6 +++---
drivers/char/hvc_console.h | 4 ++--
2 files changed, 5 insertions(+), 5 deletions(-)
diff --git a/drivers/char/hvc_console.c b/drivers/char/hvc_console.c
index d8dac58..4c3b59b 100644
--- a/drivers/char/hvc_console.c
+++ b/drivers/char/hvc_console.c
@@ -748,9 +748,9 @@ static const struct tty_operations hvc_ops = {
.chars_in_buffer = hvc_chars_in_buffer,
};
-struct hvc_struct __devinit *hvc_alloc(uint32_t vtermno, int data,
- const struct hv_ops *ops,
- int outbuf_size)
+struct hvc_struct *hvc_alloc(uint32_t vtermno, int data,
+ const struct hv_ops *ops,
+ int outbuf_size)
{
struct hvc_struct *hp;
int i;
diff --git a/drivers/char/hvc_console.h b/drivers/char/hvc_console.h
index 52ddf4d..54381eb 100644
--- a/drivers/char/hvc_console.h
+++ b/drivers/char/hvc_console.h
@@ -80,8 +80,8 @@ extern int hvc_instantiate(uint32_t vtermno, int index,
const struct hv_ops *ops);
/* register a vterm for hvc tty operation (module_init or hotplug add) */
-extern struct hvc_struct * __devinit hvc_alloc(uint32_t vtermno, int data,
- const struct hv_ops *ops, int outbuf_size);
+extern struct hvc_struct * hvc_alloc(uint32_t vtermno, int data,
+ const struct hv_ops *ops, int outbuf_size);
/* remove a vterm from hvc tty operation (module_exit or hotplug remove) */
extern int hvc_remove(struct hvc_struct *hp);
--
1.6.2.5
^ permalink raw reply related
* [PATCH] Make cpu hotplug driver lock part of ppc_md
From: Nathan Fontenot @ 2009-12-22 14:45 UTC (permalink / raw)
To: linuxppc-dev; +Cc: Andreas Schwab
The recently introduced cpu_hotplug_driver_lock used to serialize
cpu hotplug operations, namely for the pseries platform, causes a build
issue for other platforms. The base cpu hotplug code attempts
to take this lock, but it may not be needed for all platforms. This patch
moves the lock/unlock routines to be part of the ppc_md structure
so that platforms needing the lock can take it. This also makes the
previous cpu_hotplug_driver_lock, defined in pseries code, pseries specific.
The past failure without this patch was seen when building pmac and may
be present in other platform builds. The error is included below for reference.
drivers/built-in.o: In function `.store_online':
cpu.c:(.ref.text+0xf5c): undefined reference to `.cpu_hotplug_driver_lock'
cpu.c:(.ref.text+0xfc8): undefined reference to `.cpu_hotplug_driver_unlock'
make: *** [.tmp_vmlinux1] Error 1
Signed-of-by: Nathan Fontenot <nfont@austin.ibm.com>
---
arch/powerpc/include/asm/machdep.h | 2 ++
arch/powerpc/kernel/smp.c | 14 ++++++++++++++
arch/powerpc/platforms/pseries/dlpar.c | 6 ++++--
3 files changed, 20 insertions(+), 2 deletions(-)
Index: powerpc/arch/powerpc/include/asm/machdep.h
===================================================================
--- powerpc.orig/arch/powerpc/include/asm/machdep.h 2009-12-21 20:51:49.000000000 -0600
+++ powerpc/arch/powerpc/include/asm/machdep.h 2009-12-21 21:07:40.000000000 -0600
@@ -270,6 +270,8 @@
#ifdef CONFIG_ARCH_CPU_PROBE_RELEASE
ssize_t (*cpu_probe)(const char *, size_t);
ssize_t (*cpu_release)(const char *, size_t);
+ void (*cpu_hotplug_driver_lock)(void);
+ void (*cpu_hotplug_driver_unlock)(void);
#endif
};
Index: powerpc/arch/powerpc/platforms/pseries/dlpar.c
===================================================================
--- powerpc.orig/arch/powerpc/platforms/pseries/dlpar.c 2009-12-21 20:51:49.000000000 -0600
+++ powerpc/arch/powerpc/platforms/pseries/dlpar.c 2009-12-21 21:26:23.000000000 -0600
@@ -346,12 +346,12 @@
static DEFINE_MUTEX(pseries_cpu_hotplug_mutex);
-void cpu_hotplug_driver_lock()
+static void pseries_cpu_hotplug_driver_lock(void)
{
mutex_lock(&pseries_cpu_hotplug_mutex);
}
-void cpu_hotplug_driver_unlock()
+static void pseries_cpu_hotplug_driver_unlock(void)
{
mutex_unlock(&pseries_cpu_hotplug_mutex);
}
@@ -550,6 +550,8 @@
{
ppc_md.cpu_probe = dlpar_cpu_probe;
ppc_md.cpu_release = dlpar_cpu_release;
+ ppc_md.cpu_hotplug_driver_lock = pseries_cpu_hotplug_driver_lock;
+ ppc_md.cpu_hotplug_driver_unlock = pseries_cpu_hotplug_driver_unlock;
return 0;
}
Index: powerpc/arch/powerpc/kernel/smp.c
===================================================================
--- powerpc.orig/arch/powerpc/kernel/smp.c 2009-12-21 20:51:49.000000000 -0600
+++ powerpc/arch/powerpc/kernel/smp.c 2009-12-21 21:24:23.000000000 -0600
@@ -619,4 +619,18 @@
if (smp_ops->cpu_die)
smp_ops->cpu_die(cpu);
}
+
+#ifdef CONFIG_ARCH_CPU_PROBE_RELEASE
+void cpu_hotplug_driver_lock(void)
+{
+ if (ppc_md.cpu_hotplug_driver_lock)
+ ppc_md.cpu_hotplug_driver_lock();
+}
+
+void cpu_hotplug_driver_unlock(void)
+{
+ if (ppc_md.cpu_hotplug_driver_unlock)
+ ppc_md.cpu_hotplug_driver_unlock();
+}
#endif
+#endif /* CONFIG_HOTPLUG_CPU */
^ permalink raw reply
* I2C bus clock on MPC85XX systems
From: Felix Radensky @ 2009-12-22 15:38 UTC (permalink / raw)
To: linuxppc-dev
Hi,
Almost all MPC85XX based systems have the compatible=:"fsl-i2c" in
respective
i2c device tree nodes. This causes FSL i2c driver to use the following
"backward
compatible" values: FSR=0x31 DFSR=0x10. This is regardless of CCB clock
frequency and i2c clock prescaler.
On my custom MPC8536 based board with 432MHz CCB clock this results in
65KHz i2c clock frequency (checked with scope). U-Boot correctly configures
the clock to 400KHz.
I've fixed the problem by modifying device tree to use different
compatible value,
similar to what socrates board does. Is this the right approach ?
Thanks.
Felix.
^ permalink raw reply
* Re: I2C bus clock on MPC85XX systems
From: Wolfgang Grandegger @ 2009-12-22 16:18 UTC (permalink / raw)
To: Felix Radensky; +Cc: linuxppc-dev
In-Reply-To: <4B30E7F5.8080201@embedded-sol.com>
Felix Radensky wrote:
> Hi,
>
> Almost all MPC85XX based systems have the compatible=:"fsl-i2c" in
> respective
> i2c device tree nodes. This causes FSL i2c driver to use the following
> "backward
> compatible" values: FSR=0x31 DFSR=0x10. This is regardless of CCB clock
> frequency and i2c clock prescaler.
>
> On my custom MPC8536 based board with 432MHz CCB clock this results in
> 65KHz i2c clock frequency (checked with scope). U-Boot correctly configures
> the clock to 400KHz.
>
> I've fixed the problem by modifying device tree to use different
> compatible value,
> similar to what socrates board does. Is this the right approach ?
Are you aware of the properties described in
"Documentation/powerpc/dts-bindings/fsl/i2c.txt":
http://lxr.linux.no/#linux+v2.6.32/Documentation/powerpc/dts-bindings/fsl/i2c.txt
Wolfgang.
^ 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