* Re: MPC831x (and others?) NAND erase performance improvements
From: Joakim Tjernlund @ 2010-12-10 8:56 UTC (permalink / raw)
To: Andre Schwarz; +Cc: Mark Mason, Scott Wood, linuxppc-dev
In-Reply-To: <4D01E90E.8070404@matrix-vision.de>
Andre Schwarz <andre.schwarz@matrix-vision.de> wrote on 2010/12/10 09:4=
7:10:
>
> Scott,
>
> do you think this issue also applies to MPC8377 ?
Probably, I think this is so for all eLBC controllers.
>
> I'm in the middle of a small redesign for series production and would=
> like not to miss a thing.
> We have Nand, Nor and MRAM connected to LBC.
>
> Since RFS is running from NAND and we use the MRAM as a non-volatile
> SRAM I'd like to avoid being hit by this issue.
Please report back, I really want to know if this works and if there
are any drawbacks.
>
> Any comments from your side ?
>
> Regards,
> Andr=E9
>
> > On Wed, 8 Dec 2010 22:26:59 +0100
> > Joakim Tjernlund<joakim.tjernlund@transmode.se> wrote:
> >
> >> Scott Wood<scottwood@freescale.com> wrote on 2010/12/08 21:25:51:=
> >>> On Wed, 8 Dec 2010 21:11:08 +0100
> >>> Joakim Tjernlund<joakim.tjernlund@transmode.se> wrote:
> >>>
> >>>> Scott Wood<scottwood@freescale.com> wrote on 2010/12/08 20:59:2=
8:
> >>>>> On Wed, 8 Dec 2010 20:57:03 +0100
> >>>>> Joakim Tjernlund<joakim.tjernlund@transmode.se> wrote:
> >>>>>
> >>>>>> Can you think of any workaround such as not connecting the BUS=
Y pin at all?
> >>>>> Maybe connect the busy pin to a gpio?
> >>>> Is BUSY required for sane operation or it an optimization?
> >>> You could probably get away without it by inserting delays if you=
know
> >>> the chip specs well enough.
> >> Urgh, that does not feel like a good solution.
> > No, but you asked if it could be done, and if it was just a
> > performance issue. :-)
> >
> >>>> Is there any risk that the NAND device will drive the LB and cor=
rupt
> >>>> the bus for other devices?
> >>> I think the only thing the NAND chip should be driving is the bus=
y pin,
> >> OK, good. What function is actually lost if one uses an GPIO inste=
ad of
> >> BUSY?
> > Not much, if you enable interrupts on the GPIO pin. The driver wou=
ld
> > have to be reworked a bit, of course.
> >
> >> You think Freescale could test and validate a GPIO solution? I don=
't
> >> think we will be very happy to design our board around an unproven=
> >> workaround.
> > Ask your sales/support contacts.
> >
> >> An even better workaround would be if one could add logic between =
the
> >> NAND and the CPU which would compensate for this defect without ne=
eding
> >> special SW fixes.
> > The problem with that is when would you assert the chipselect again=
to
> > check if it's done? Current SW depends on being able to tell the L=
BC
> > to interrupt (or take other action) when busy goes away.
> >
> > I suppose you could poll with status reads, which could at least be=
> > preempted if you've got something higher priority to do with the LB=
C.
> >
> > -Scott
> >
> > _______________________________________________
> > Linuxppc-dev mailing list
> > Linuxppc-dev@lists.ozlabs.org
> > https://lists.ozlabs.org/listinfo/linuxppc-dev
>
>
> MATRIX VISION GmbH, Talstrasse 16, DE-71570 Oppenweiler
> Registergericht: Amtsgericht Stuttgart, HRB 271090
> Geschaeftsfuehrer: Gerhard Thullner, Werner Armingeon, Uwe Furtner=
^ permalink raw reply
* [PATCH 1/3 v2] edac: Use ccsr_pci structure instead of hardcoded define
From: Lan Chunhe @ 2010-12-10 10:51 UTC (permalink / raw)
To: linuxppc-dev; +Cc: Lan Chunhe, Kai.Jiang, dougthompson, akpm
There are some differences of register offset and definition between
pci and pcie error management registers. While, some other pci/pcie
error management registers are nearly the same.
To merge pci and pcie edac code into one, it is easier to use ccsr_pci
structure than the hardcoded define. So remove the hardcoded define and
add pci/pcie error management register in ccsr_pci structure.
Signed-off-by: Kai.Jiang <Kai.Jiang@freescale.com>
Signed-off-by: Kumar Gala <galak@kernel.crashing.org>
Signed-off-by: Lan Chunhe <b25806@freescale.com>
---
arch/powerpc/sysdev/fsl_pci.h | 54 +++++++++++++++++++++++++++++++---------
drivers/edac/mpc85xx_edac.c | 8 ------
drivers/edac/mpc85xx_edac.h | 21 +++++++--------
3 files changed, 52 insertions(+), 31 deletions(-)
diff --git a/arch/powerpc/sysdev/fsl_pci.h b/arch/powerpc/sysdev/fsl_pci.h
index 8ad72a1..796985b 100644
--- a/arch/powerpc/sysdev/fsl_pci.h
+++ b/arch/powerpc/sysdev/fsl_pci.h
@@ -1,7 +1,7 @@
/*
* MPC85xx/86xx PCI Express structure define
*
- * Copyright 2007 Freescale Semiconductor, Inc
+ * Copyright 2007, 2010 Freescale Semiconductor, Inc
*
* 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
@@ -14,6 +14,8 @@
#ifndef __POWERPC_FSL_PCI_H
#define __POWERPC_FSL_PCI_H
+#include <asm/pci-bridge.h>
+
#define PCIE_LTSSM 0x0404 /* PCIE Link Training and Status */
#define PCIE_LTSSM_L0 0x16 /* L0 state */
#define PIWAR_EN 0x80000000 /* Enable */
@@ -71,18 +73,46 @@ struct ccsr_pci {
*/
struct pci_inbound_window_regs piw[3];
- __be32 pex_err_dr; /* 0x.e00 - PCI/PCIE error detect register */
- u8 res21[4];
- __be32 pex_err_en; /* 0x.e08 - PCI/PCIE error interrupt enable register */
- u8 res22[4];
- __be32 pex_err_disr; /* 0x.e10 - PCI/PCIE error disable register */
- u8 res23[12];
- __be32 pex_err_cap_stat; /* 0x.e20 - PCI/PCIE error capture status register */
+/* Merge PCI/PCI Express error management registers */
+ __be32 pex_err_dr; /* 0x.e00
+ * - PCI/PCIE error detect register
+ */
+ __be32 pex_err_cap_dr; /* 0x.e04
+ * - PCI error capture disabled register
+ * - PCIE has no this register
+ */
+ __be32 pex_err_en; /* 0x.e08
+ * - PCI/PCIE error interrupt enable register
+ */
+ __be32 pex_err_attrib; /* 0x.e0c
+ * - PCI error attributes capture register
+ * - PCIE has no this register
+ */
+ __be32 pex_err_disr; /* 0x.e10
+ * - PCI error address capture register
+ * - PCIE error disable register
+ */
+ __be32 pex_err_ext_addr; /* 0x.e14
+ * - PCI error extended addr capture register
+ * - PCIE has no this register
+ */
+ __be32 pex_err_dl; /* 0x.e18
+ * - PCI error data low capture register
+ * - PCIE has no this register
+ */
+ __be32 pex_err_dh; /* 0x.e1c
+ * - PCI error data high capture register
+ * - PCIE has no this register
+ */
+ __be32 pex_err_cap_stat; /* 0x.e20
+ * - PCI gasket timer register
+ * - PCIE error capture status register
+ */
u8 res24[4];
- __be32 pex_err_cap_r0; /* 0x.e28 - PCIE error capture register 0 */
- __be32 pex_err_cap_r1; /* 0x.e2c - PCIE error capture register 0 */
- __be32 pex_err_cap_r2; /* 0x.e30 - PCIE error capture register 0 */
- __be32 pex_err_cap_r3; /* 0x.e34 - PCIE error capture register 0 */
+ __be32 pex_err_cap_r0; /* 0x.e28 - PCIE error capture register 0 */
+ __be32 pex_err_cap_r1; /* 0x.e2c - PCIE error capture register 1 */
+ __be32 pex_err_cap_r2; /* 0x.e30 - PCIE error capture register 2 */
+ __be32 pex_err_cap_r3; /* 0x.e34 - PCIE error capture register 3 */
};
extern int fsl_add_bridge(struct device_node *dev, int is_primary);
diff --git a/drivers/edac/mpc85xx_edac.c b/drivers/edac/mpc85xx_edac.c
index b123bb3..4bdf384 100644
--- a/drivers/edac/mpc85xx_edac.c
+++ b/drivers/edac/mpc85xx_edac.c
@@ -34,14 +34,6 @@ static int edac_mc_idx;
static u32 orig_ddr_err_disable;
static u32 orig_ddr_err_sbe;
-/*
- * PCI Err defines
- */
-#ifdef CONFIG_PCI
-static u32 orig_pci_err_cap_dr;
-static u32 orig_pci_err_en;
-#endif
-
static u32 orig_l2_err_disable;
#ifdef CONFIG_FSL_SOC_BOOKE
static u32 orig_hid1[2];
diff --git a/drivers/edac/mpc85xx_edac.h b/drivers/edac/mpc85xx_edac.h
index cb24df8..c258161 100644
--- a/drivers/edac/mpc85xx_edac.h
+++ b/drivers/edac/mpc85xx_edac.h
@@ -1,5 +1,8 @@
/*
* Freescale MPC85xx Memory Controller kenel module
+ *
+ * Copyright (c) 2010 Freescale Semiconductor, Inc.
+ *
* Author: Dave Jiang <djiang@mvista.com>
*
* 2006-2007 (c) MontaVista Software, Inc. This file is licensed under
@@ -131,16 +134,9 @@
#define PCI_EDE_PERR_MASK (PCI_EDE_TGT_PERR | PCI_EDE_MST_PERR | \
PCI_EDE_ADDR_PERR)
-#define MPC85XX_PCI_ERR_DR 0x0000
-#define MPC85XX_PCI_ERR_CAP_DR 0x0004
-#define MPC85XX_PCI_ERR_EN 0x0008
-#define MPC85XX_PCI_ERR_ATTRIB 0x000c
-#define MPC85XX_PCI_ERR_ADDR 0x0010
-#define MPC85XX_PCI_ERR_EXT_ADDR 0x0014
-#define MPC85XX_PCI_ERR_DL 0x0018
-#define MPC85XX_PCI_ERR_DH 0x001c
-#define MPC85XX_PCI_GAS_TIMR 0x0020
-#define MPC85XX_PCI_PCIX_TIMR 0x0024
+#define PCI_ERR_CAP_DR_DIS_MST 0x0040
+#define PCI_ERR_EN_DIS_MST (~PCI_ERR_CAP_DR_DIS_MST)
+#define MPC85XX_MPIC_EIMR0 0x3910
struct mpc85xx_mc_pdata {
char *name;
@@ -159,7 +155,10 @@ struct mpc85xx_l2_pdata {
struct mpc85xx_pci_pdata {
char *name;
int edac_idx;
- void __iomem *pci_vbase;
+ struct ccsr_pci *pci_reg;
+ u32 orig_pci_err_cap_dr;
+ u32 orig_pci_err_en;
+ u8 pcie_flag;
int irq;
};
--
1.5.4.5
^ permalink raw reply related
* [PATCH 2/3 v2] edac/85xx: PCI/PCIE error interrupt edac support
From: Lan Chunhe @ 2010-12-10 10:52 UTC (permalink / raw)
To: linuxppc-dev; +Cc: Lan Chunhe, Kai.Jiang, dougthompson, akpm
Add pcie error interrupt edac support for mpc85xx and p4080.
mpc85xx uses the legacy interrupt report mechanism - the error
interrupts are reported directly to mpic. While, p4080 attaches
most of error interrupts to interrupt 0. And report error interrupt
to mpic via interrupt 0. This patch can handle both of them.
Signed-off-by: Kai.Jiang <Kai.Jiang@freescale.com>
Signed-off-by: Kumar Gala <galak@kernel.crashing.org>
Signed-off-by: Lan Chunhe <b25806@freescale.com>
---
drivers/edac/mpc85xx_edac.c | 240 +++++++++++++++++++++++++++++++++---------
1 files changed, 188 insertions(+), 52 deletions(-)
diff --git a/drivers/edac/mpc85xx_edac.c b/drivers/edac/mpc85xx_edac.c
index 4bdf384..793d605 100644
--- a/drivers/edac/mpc85xx_edac.c
+++ b/drivers/edac/mpc85xx_edac.c
@@ -1,6 +1,8 @@
/*
* Freescale MPC85xx Memory Controller kenel module
*
+ * Copyright (c) 2010 Freescale Semiconductor, Inc.
+ *
* Author: Dave Jiang <djiang@mvista.com>
*
* 2006-2007 (c) MontaVista Software, Inc. This file is licensed under
@@ -21,6 +23,7 @@
#include <linux/of_platform.h>
#include <linux/of_device.h>
+#include <sysdev/fsl_pci.h>
#include "edac_module.h"
#include "edac_core.h"
#include "mpc85xx_edac.h"
@@ -35,7 +38,7 @@ static u32 orig_ddr_err_disable;
static u32 orig_ddr_err_sbe;
static u32 orig_l2_err_disable;
-#ifdef CONFIG_FSL_SOC_BOOKE
+#if defined(CONFIG_FSL_SOC_BOOKE) && !defined(CONFIG_PPC_E500MC)
static u32 orig_hid1[2];
#endif
@@ -143,37 +146,52 @@ static void mpc85xx_pci_check(struct edac_pci_ctl_info *pci)
{
struct mpc85xx_pci_pdata *pdata = pci->pvt_info;
u32 err_detect;
+ struct ccsr_pci *reg = pdata->pci_reg;
+
+ err_detect = in_be32(&pdata->pci_reg->pex_err_dr);
+
+ if (pdata->pcie_flag) {
+ printk(KERN_ERR "PCIE error(s) detected\n");
+ printk(KERN_ERR "PCIE ERR_DR register: 0x%08x\n", err_detect);
+ printk(KERN_ERR "PCIE ERR_CAP_STAT register: 0x%08x\n",
+ in_be32(®->pex_err_cap_stat));
+ printk(KERN_ERR "PCIE ERR_CAP_R0 register: 0x%08x\n",
+ in_be32(®->pex_err_cap_r0));
+ printk(KERN_ERR "PCIE ERR_CAP_R1 register: 0x%08x\n",
+ in_be32(®->pex_err_cap_r1));
+ printk(KERN_ERR "PCIE ERR_CAP_R2 register: 0x%08x\n",
+ in_be32(®->pex_err_cap_r2));
+ printk(KERN_ERR "PCIE ERR_CAP_R3 register: 0x%08x\n",
+ in_be32(®->pex_err_cap_r3));
+ } else {
+ /* master aborts can happen during PCI config cycles */
+ if (!(err_detect & ~(PCI_EDE_MULTI_ERR | PCI_EDE_MST_ABRT))) {
+ out_be32(®->pex_err_dr, err_detect);
+ return;
+ }
- err_detect = in_be32(pdata->pci_vbase + MPC85XX_PCI_ERR_DR);
-
- /* master aborts can happen during PCI config cycles */
- if (!(err_detect & ~(PCI_EDE_MULTI_ERR | PCI_EDE_MST_ABRT))) {
- out_be32(pdata->pci_vbase + MPC85XX_PCI_ERR_DR, err_detect);
- return;
+ printk(KERN_ERR "PCI error(s) detected\n");
+ printk(KERN_ERR "PCI/X ERR_DR register: 0x%08x\n", err_detect);
+ printk(KERN_ERR "PCI/X ERR_ATTRIB register: 0x%08x\n",
+ in_be32(®->pex_err_attrib));
+ printk(KERN_ERR "PCI/X ERR_ADDR register: 0x%08x\n",
+ in_be32(®->pex_err_disr));
+ printk(KERN_ERR "PCI/X ERR_EXT_ADDR register: 0x%08x\n",
+ in_be32(®->pex_err_ext_addr));
+ printk(KERN_ERR "PCI/X ERR_DL register: 0x%08x\n",
+ in_be32(®->pex_err_dl));
+ printk(KERN_ERR "PCI/X ERR_DH register: 0x%08x\n",
+ in_be32(®->pex_err_dh));
+
+ if (err_detect & PCI_EDE_PERR_MASK)
+ edac_pci_handle_pe(pci, pci->ctl_name);
+
+ if (err_detect & ~(PCI_EDE_MULTI_ERR | PCI_EDE_PERR_MASK))
+ edac_pci_handle_npe(pci, pci->ctl_name);
}
- printk(KERN_ERR "PCI error(s) detected\n");
- printk(KERN_ERR "PCI/X ERR_DR register: %#08x\n", err_detect);
-
- printk(KERN_ERR "PCI/X ERR_ATTRIB register: %#08x\n",
- in_be32(pdata->pci_vbase + MPC85XX_PCI_ERR_ATTRIB));
- printk(KERN_ERR "PCI/X ERR_ADDR register: %#08x\n",
- in_be32(pdata->pci_vbase + MPC85XX_PCI_ERR_ADDR));
- printk(KERN_ERR "PCI/X ERR_EXT_ADDR register: %#08x\n",
- in_be32(pdata->pci_vbase + MPC85XX_PCI_ERR_EXT_ADDR));
- printk(KERN_ERR "PCI/X ERR_DL register: %#08x\n",
- in_be32(pdata->pci_vbase + MPC85XX_PCI_ERR_DL));
- printk(KERN_ERR "PCI/X ERR_DH register: %#08x\n",
- in_be32(pdata->pci_vbase + MPC85XX_PCI_ERR_DH));
-
/* clear error bits */
- out_be32(pdata->pci_vbase + MPC85XX_PCI_ERR_DR, err_detect);
-
- if (err_detect & PCI_EDE_PERR_MASK)
- edac_pci_handle_pe(pci, pci->ctl_name);
-
- if ((err_detect & ~PCI_EDE_MULTI_ERR) & ~PCI_EDE_PERR_MASK)
- edac_pci_handle_npe(pci, pci->ctl_name);
+ out_be32(®->pex_err_dr, err_detect);
}
static irqreturn_t mpc85xx_pci_isr(int irq, void *dev_id)
@@ -182,7 +200,7 @@ static irqreturn_t mpc85xx_pci_isr(int irq, void *dev_id)
struct mpc85xx_pci_pdata *pdata = pci->pvt_info;
u32 err_detect;
- err_detect = in_be32(pdata->pci_vbase + MPC85XX_PCI_ERR_DR);
+ err_detect = in_be32(&pdata->pci_reg->pex_err_dr);
if (!err_detect)
return IRQ_NONE;
@@ -192,12 +210,103 @@ static irqreturn_t mpc85xx_pci_isr(int irq, void *dev_id)
return IRQ_HANDLED;
}
+/**
+ * This function is for error interrupt ORed mechanism.
+ * This mechanism attaches most functions' error interrupts to interrupt 0.
+ * And report error interrupt to mpic via interrupt 0.
+ * EIMR0 - Error Interrupt Mask Register 0.
+ *
+ * This function check whether the device support error interrupt ORed
+ * mechanism via device tree. If supported, umask pcie error interrupt
+ * bit in EIMR0.
+ */
+static int mpc85xx_err_int_en(struct platform_device *op)
+{
+ u32 *int_cell;
+ struct device_node *np;
+ void __iomem *mpic_base;
+ u32 reg_tmp;
+ u32 int_len;
+ struct resource r;
+ int res;
+
+ if (!op->dev.of_node)
+ return -EINVAL;
+
+ /*
+ * Unmask pcie error interrupt bit in EIMR0.
+ * Extend interrupt specifier has 4 cells.
+ * For the 3rd cell:
+ * 0 -- normal interrupt;
+ * 1 -- error interrupt.
+ */
+ int_cell = (u32 *)of_get_property(op->dev.of_node, "interrupts",
+ &int_len);
+ if ((int_len/sizeof(u32)) == 4) {
+ /* soc has error interrupt integration handling mechanism */
+ if (*(int_cell + 2) == 1) {
+ np = of_find_node_by_type(NULL, "open-pic");
+
+ if (of_address_to_resource(np, 0, &r)) {
+ printk(KERN_ERR "%s: Failed to map mpic regs\n",
+ __func__);
+ of_node_put(np);
+ res = -ENOMEM;
+ goto err;
+ }
+
+ if (!request_mem_region(r.start, r.end - r.start + 1,
+ "mpic")) {
+ printk(KERN_ERR "%s: Error while requesting "
+ "mem region\n", __func__);
+ res = -EBUSY;
+ goto err;
+ }
+
+ mpic_base = ioremap(r.start, r.end - r.start + 1);
+ if (!mpic_base) {
+ printk(KERN_ERR "%s: Unable to map mpic regs\n",
+ __func__);
+ res = -ENOMEM;
+ goto err_ioremap;
+ }
+
+ reg_tmp = in_be32(mpic_base + MPC85XX_MPIC_EIMR0);
+ out_be32(mpic_base + MPC85XX_MPIC_EIMR0,
+ reg_tmp & ~(1 << (31 - *(int_cell + 3))));
+ iounmap(mpic_base);
+ release_mem_region(r.start, r.end - r.start + 1);
+ of_node_put(np);
+ }
+ }
+
+ return 0;
+
+err_ioremap:
+ release_mem_region(r.start, r.end - r.start + 1);
+err:
+ return res;
+}
+
+static int mpc85xx_pcie_find_capability(struct device_node *np)
+{
+ struct pci_controller *hose;
+
+ if (!np)
+ return -EINVAL;
+
+ hose = pci_find_hose_for_OF_device(np);
+ return early_find_capability(hose, hose->bus->number, 0,
+ PCI_CAP_ID_EXP);
+}
+
static int __devinit mpc85xx_pci_err_probe(struct platform_device *op,
const struct of_device_id *match)
{
struct edac_pci_ctl_info *pci;
struct mpc85xx_pci_pdata *pdata;
struct resource r;
+ struct ccsr_pci *reg;
int res = 0;
if (!devres_open_group(&op->dev, mpc85xx_pci_err_probe, GFP_KERNEL))
@@ -210,6 +319,9 @@ static int __devinit mpc85xx_pci_err_probe(struct platform_device *op,
pdata = pci->pvt_info;
pdata->name = "mpc85xx_pci_err";
pdata->irq = NO_IRQ;
+ if (mpc85xx_pcie_find_capability(op->dev.of_node) > 0)
+ pdata->pcie_flag = 1;
+
dev_set_drvdata(&op->dev, pci);
pci->dev = &op->dev;
pci->mod_name = EDAC_MOD_STR;
@@ -228,9 +340,6 @@ static int __devinit mpc85xx_pci_err_probe(struct platform_device *op,
goto err;
}
- /* we only need the error registers */
- r.start += 0xe00;
-
if (!devm_request_mem_region(&op->dev, r.start, resource_size(&r),
pdata->name)) {
printk(KERN_ERR "%s: Error while requesting mem region\n",
@@ -239,26 +348,32 @@ static int __devinit mpc85xx_pci_err_probe(struct platform_device *op,
goto err;
}
- pdata->pci_vbase = devm_ioremap(&op->dev, r.start, resource_size(&r));
- if (!pdata->pci_vbase) {
+ pdata->pci_reg = devm_ioremap(&op->dev, r.start, resource_size(&r));
+ if (!pdata->pci_reg) {
printk(KERN_ERR "%s: Unable to setup PCI err regs\n", __func__);
res = -ENOMEM;
goto err;
}
- orig_pci_err_cap_dr =
- in_be32(pdata->pci_vbase + MPC85XX_PCI_ERR_CAP_DR);
-
- /* PCI master abort is expected during config cycles */
- out_be32(pdata->pci_vbase + MPC85XX_PCI_ERR_CAP_DR, 0x40);
+ if (mpc85xx_err_int_en(op) < 0)
+ goto err;
- orig_pci_err_en = in_be32(pdata->pci_vbase + MPC85XX_PCI_ERR_EN);
+ reg = pdata->pci_reg;
+ /* disable pci/pcie error detect */
+ if (pdata->pcie_flag) {
+ pdata->orig_pci_err_cap_dr = in_be32(®->pex_err_disr);
+ out_be32(®->pex_err_disr, ~0);
+ } else {
+ pdata->orig_pci_err_cap_dr = in_be32(®->pex_err_cap_dr);
+ out_be32(®->pex_err_cap_dr, ~0);
+ }
- /* disable master abort reporting */
- out_be32(pdata->pci_vbase + MPC85XX_PCI_ERR_EN, ~0x40);
+ /* disable all pcie error interrupt */
+ pdata->orig_pci_err_en = in_be32(®->pex_err_en);
+ out_be32(®->pex_err_en, 0);
- /* clear error bits */
- out_be32(pdata->pci_vbase + MPC85XX_PCI_ERR_DR, ~0);
+ /* clear all error bits */
+ out_be32(®->pex_err_dr, ~0);
if (edac_pci_add_device(pci, pdata->edac_idx) > 0) {
debugf3("%s(): failed edac_pci_add_device()\n", __func__);
@@ -268,7 +383,7 @@ static int __devinit mpc85xx_pci_err_probe(struct platform_device *op,
if (edac_op_state == EDAC_OPSTATE_INT) {
pdata->irq = irq_of_parse_and_map(op->dev.of_node, 0);
res = devm_request_irq(&op->dev, pdata->irq,
- mpc85xx_pci_isr, IRQF_DISABLED,
+ mpc85xx_pci_isr, IRQF_SHARED,
"[EDAC] PCI err", pci);
if (res < 0) {
printk(KERN_ERR
@@ -283,6 +398,17 @@ static int __devinit mpc85xx_pci_err_probe(struct platform_device *op,
pdata->irq);
}
+ if (pdata->pcie_flag) {
+ /* enable all pcie error interrupt & error detect */
+ out_be32(®->pex_err_en, ~0);
+ out_be32(®->pex_err_disr, 0);
+ } else {
+ /* PCI master abort is expected during config cycles */
+ out_be32(®->pex_err_cap_dr, PCI_ERR_CAP_DR_DIS_MST);
+ /* disable master abort reporting */
+ out_be32(®->pex_err_en, PCI_ERR_EN_DIS_MST);
+ }
+
devres_remove_group(&op->dev, mpc85xx_pci_err_probe);
debugf3("%s(): success\n", __func__);
printk(KERN_INFO EDAC_MOD_STR " PCI err registered\n");
@@ -304,10 +430,14 @@ static int mpc85xx_pci_err_remove(struct platform_device *op)
debugf0("%s()\n", __func__);
- out_be32(pdata->pci_vbase + MPC85XX_PCI_ERR_CAP_DR,
- orig_pci_err_cap_dr);
+ if (pdata->pcie_flag)
+ out_be32(&pdata->pci_reg->pex_err_disr,
+ pdata->orig_pci_err_cap_dr);
+ else
+ out_be32(&pdata->pci_reg->pex_err_cap_dr,
+ pdata->orig_pci_err_cap_dr);
- out_be32(pdata->pci_vbase + MPC85XX_PCI_ERR_EN, orig_pci_err_en);
+ out_be32(&pdata->pci_reg->pex_err_en, pdata->orig_pci_err_en);
edac_pci_del_device(pci->dev);
@@ -326,6 +456,12 @@ static struct of_device_id mpc85xx_pci_err_of_match[] = {
{
.compatible = "fsl,mpc8540-pci",
},
+ {
+ .compatible = "fsl,mpc8548-pcie",
+ },
+ {
+ .compatible = "fsl,p4080-pcie",
+ },
{},
};
MODULE_DEVICE_TABLE(of, mpc85xx_pci_err_of_match);
@@ -1138,7 +1274,7 @@ static struct of_platform_driver mpc85xx_mc_err_driver = {
},
};
-#ifdef CONFIG_FSL_SOC_BOOKE
+#if defined(CONFIG_FSL_SOC_BOOKE) && !defined(CONFIG_PPC_E500MC)
static void __init mpc85xx_mc_clear_rfxe(void *data)
{
orig_hid1[smp_processor_id()] = mfspr(SPRN_HID1);
@@ -1177,7 +1313,7 @@ static int __init mpc85xx_mc_init(void)
printk(KERN_WARNING EDAC_MOD_STR "PCI fails to register\n");
#endif
-#ifdef CONFIG_FSL_SOC_BOOKE
+#if defined(CONFIG_FSL_SOC_BOOKE) && !defined(CONFIG_PPC_E500MC)
/*
* need to clear HID1[RFXE] to disable machine check int
* so we can catch it
@@ -1191,7 +1327,7 @@ static int __init mpc85xx_mc_init(void)
module_init(mpc85xx_mc_init);
-#ifdef CONFIG_FSL_SOC_BOOKE
+#if defined(CONFIG_FSL_SOC_BOOKE) && !defined(CONFIG_PPC_E500MC)
static void __exit mpc85xx_mc_restore_hid1(void *data)
{
mtspr(SPRN_HID1, orig_hid1[smp_processor_id()]);
@@ -1200,7 +1336,7 @@ static void __exit mpc85xx_mc_restore_hid1(void *data)
static void __exit mpc85xx_mc_exit(void)
{
-#ifdef CONFIG_FSL_SOC_BOOKE
+#if defined(CONFIG_FSL_SOC_BOOKE) && !defined(CONFIG_PPC_E500MC)
on_each_cpu(mpc85xx_mc_restore_hid1, NULL, 0);
#endif
#ifdef CONFIG_PCI
--
1.5.4.5
^ permalink raw reply related
* [PATCH 3/3 v2] of/device: Register children with a compatible value in of_platform_bus_probe()
From: Lan Chunhe @ 2010-12-10 10:52 UTC (permalink / raw)
To: linuxppc-dev; +Cc: Lan Chunhe, akpm, dougthompson
Currently, of_platform_bus_probe() completely skips nodes which do not
explicitly match the 'matches' table passed in. Or, if the root node
matches, then it registers all the children unconditionally. However,
there are situations, such as registering devices from the root node,
when it is desirable to register child nodes, but only if they actually
represent devices. For example, the root node may contain both a local
bus and a PCI device, but it also contains the chosen, aliases and cpus
nodes which don't represent real devices.
This patch changes of_platform_bus_probe() to register all nodes at the
top level if they either match the matches table (the current behaviour),
or if they have a 'compatible' value (indicating it represents a device).
Signed-off-by: Lan Chunhe <b25806@freescale.com>
---
drivers/of/platform.c | 28 +++++++++++++++++++++++-----
1 files changed, 23 insertions(+), 5 deletions(-)
diff --git a/drivers/of/platform.c b/drivers/of/platform.c
index 5b4a07f..02755ab 100644
--- a/drivers/of/platform.c
+++ b/drivers/of/platform.c
@@ -714,6 +714,8 @@ int of_platform_bus_probe(struct device_node *root,
struct device_node *child;
struct platform_device *dev;
int rc = 0;
+ const void *compat;
+ const struct of_device_id *match;
if (WARN_ON(!matches || matches == OF_NO_DEEP_PROBE))
return -EINVAL;
@@ -741,16 +743,32 @@ int of_platform_bus_probe(struct device_node *root,
rc = of_platform_bus_create(root, matches, &dev->dev);
goto bail;
}
+
+ /*
+ * Register each child node if either:
+ * a) it has a 'compatible' value indicating they are a device, or
+ * b) it is specified by the 'matches' table (by name or device_type)
+ * If a node is specified in the matches table, then all its children
+ * also get registered.
+ */
for_each_child_of_node(root, child) {
- if (!of_match_node(matches, child))
+ compat = of_get_property(child, "compatible", NULL);
+ match = of_match_node(matches, child);
+ if (!compat && !match)
continue;
- pr_debug(" match: %s\n", child->full_name);
+ pr_debug(" register device: %s\n", child->full_name);
dev = of_platform_device_create(child, NULL, parent);
- if (dev == NULL)
+ if (!dev) {
rc = -ENOMEM;
- else
- rc = of_platform_bus_create(child, matches, &dev->dev);
+ of_node_put(child);
+ break;
+ }
+ if (!match)
+ continue;
+
+ pr_debug(" register children of: %s\n", child->full_name);
+ rc = of_platform_bus_create(child, matches, &dev->dev);
if (rc) {
of_node_put(child);
break;
--
1.5.4.5
^ permalink raw reply related
* Re: MPC831x (and others?) NAND erase performance improvements
From: Joakim Tjernlund @ 2010-12-10 12:39 UTC (permalink / raw)
To: Scott Wood; +Cc: Mark Mason, linuxppc-dev
In-Reply-To: <20101208162559.26e40cf2@udp111988uds.am.freescale.net>
Scott Wood <scottwood@freescale.com> wrote on 2010/12/08 23:25:59:
>
> On Wed, 8 Dec 2010 17:02:45 -0500
> Mark Mason <mason@postdiluvian.org> wrote:
>
> > I don't think that using a software NAND controller instead of the LBC
> > FCM mode is all that bad. Again, I haven't actually done it, so check
> > the MTD docs, but I'm pretty sure the software is meant to do that, so
> > it doesn't even really constitute a "fix". Assuming that it is
> > supported then I doubt that configuring the NAND layer to use your
> > setup would be any harder than configuring the FCM.
>
> The MTD layer supports some really simple NAND controllers, but what do
> you mean by not having a controller at all? Hooking everything up to
> GPIO? Using UPM?
>
> There is already a UPM NAND driver, BTW.
>
> You would lose hardware ECC and the ability to be interrupt-driven (the
> latter should be possible with SW changes, using GPIO interrupts).
hmm, you think it would be possible to use one of the IRQ pins instead?
^ permalink raw reply
* Re: [PATCH v5] ppc44x:PHY fixup for USB on canyonlands board
From: Josh Boyer @ 2010-12-10 13:39 UTC (permalink / raw)
To: Rupjyoti Sarmah; +Cc: linuxppc-dev, rsarmah, linux-kernel
In-Reply-To: <201012090424.oB94O1DV006463@amcc.com>
On Thu, Dec 09, 2010 at 09:54:01AM +0530, Rupjyoti Sarmah wrote:
A few error path issues, plus one leak in the good case. I'll wait to
see if there are any more comments for a couple of days.
>+static int __init ppc460ex_canyonlands_fixup(void)
>+{
>+ u8 __iomem *bcsr ;
>+ void __iomem *vaddr;
>+ struct device_node *np;
>+ int ret = 0;
>+
>+ np = of_find_compatible_node(NULL, NULL, "amcc,ppc460ex-bcsr");
>+ if (!np) {
>+ printk(KERN_ERR "failed did not find amcc, ppc460ex bcsr node\n");
>+ return -ENODEV;
>+ }
>+
>+ bcsr = of_iomap(np, 0);
>+ of_node_put(np);
>+
>+ if (!bcsr) {
>+ printk(KERN_CRIT "Could not remap bcsr\n");
>+ ret = -ENODEV;
>+ }
>+
>+ np = of_find_compatible_node(NULL, NULL, "ibm,ppc4xx-gpio");
This can fail if you boot a new kernel on a board with an old FDT that
lacks the gpio node. Calling of_iomap with a NULL np seems like it
would work without crashing anything, so maybe that's not a huge issue.
>+ vaddr = of_iomap(np, 0);
>+ if (!vaddr) {
>+ printk(KERN_CRIT "Could not get gpio node address\n");
>+ ret = -ENODEV;
>+ goto err_bcsr;
>+ }
A reference count is leaked for the node in this error case because
of_node_put isn't called for the gpio node. Also, iounmap is never
called on vaddr.
>+ /* Disable USB, through the BCSR7 bits */
>+ setbits8(&bcsr[7], BCSR_USB_EN);
>+
>+ /* Wait for a while after reset */
>+ msleep(100);
>+
>+ /* Enable USB here */
>+ clrbits8(&bcsr[7], BCSR_USB_EN);
>+
>+ /*
>+ * Configure multiplexed gpio16 and gpio19 as alternate1 output
>+ * source after USB reset. In this configuration gpio16 will be
>+ * USB2HStop and gpio19 will be USB2DStop. For more details refer to
>+ * table 34-7 of PPC460EX user manual.
>+ */
>+ setbits32((vaddr + GPIO0_OSRH), 0x42000000);
>+ setbits32((vaddr + GPIO0_TSRH), 0x42000000);
>+ of_node_put(np);
>+err_bcsr:
>+ iounmap(bcsr);
>+ return ret;
>+}
>+machine_device_initcall(canyonlands, ppc460ex_canyonlands_fixup);
josh
^ permalink raw reply
* ucc_geth: transmit queue timeout at half-duplex mode
From: Schmitz, Andreas @ 2010-12-10 13:52 UTC (permalink / raw)
To: leoli@freescale.com; +Cc: netdev@vger.kernel.org, linuxppc-dev@lists.ozlabs.org
[-- Attachment #1.1: Type: text/plain, Size: 1227 bytes --]
Hi all,
on my MPC8321E with linux-2.6.36 I get this netdev watchdog warning "NETDEV WATCHDOG: eth0 (of:ucc_geth): transmit queue 0 timed out" if the link mode is half-duplex.
The warning is caused, because all Tx BDs are full and packet transmission is stopped with netif_stop_queue() in ucc_geth_start_xmit().
You can reproduce the bug in the following way:
- Connect to a switch, that supports only 10baseT, or set the mode manually with "mii-diag -F 10baseT".
- Open a telnet session to the target. Generate higher traffic with executing maybe "cat /proc/interrupts" many times.
- After some tries the ethernet connection will be down, then again after approx. 30s seconds the netdev watchdog will dump the warning.
It is unclear to me why the TxBDs get full. Due to missing "Tx buffer sent" interrupts, it seems that the QE stops the transmission.
I found some issue in the errata: "QE_ENET20: UEC may stop transmitting after late collision". But UCCE[TXE] is never set in this case.
Thank you for your help in advance and best regards,
Andreas Schmitz
Software Engineer
_______________________________________
RIEDEL
Communications GmbH & Co. KG
Uellendahler Str. 353
42109 Wuppertal
Germany
[-- Attachment #1.2: Type: text/html, Size: 2213 bytes --]
[-- Attachment #2: ucc_geth_half_duplex_netdev_watchdog.txt --]
[-- Type: text/plain, Size: 13531 bytes --]
PHY: mdio@e0102320:01 - Link is Up - 10/Half
NETDEV WATCHDOG: eth0 (of:ucc_geth): transmit queue 0 timed out
------------[ cut here ]------------
WARNING: at net/sched/sch_generic.c:258
Modules linked in: gpio
NIP: c01887c0 LR: c01887c0 CTR: c012b8d4
REGS: c3ffbe30 TRAP: 0700 Not tainted (2.6.36)
MSR: 00029032 <EE,ME,CE,IR,DR> CR: 44022084 XER: 20000000
TASK = c07f6410[0] 'swapper' THREAD: c080e000
GPR00: c01887c0 c3ffbee0 c07f6410 00000046 00001848 ffffffff c012c2b0 2074696d
GPR08: 00001808 c3ffa000 c0817eb8 00000004 00000000 1007d144 03fff000 c07f94e4
GPR16: 00200200 c020dcd4 00000000 00000001 c0837fc0 c08381ec c083826c c08382ec
GPR24: c083836c 00000002 ffffffff 00000000 c3ffa03c c2607fa0 00000000 c2583000
NIP [c01887c0] dev_watchdog+0x198/0x28c
LR [c01887c0] dev_watchdog+0x198/0x28c
Call Trace:
[c3ffbee0] [c01887c0] dev_watchdog+0x198/0x28c (unreliable)
[c3ffbf50] [c003c9b0] run_timer_softirq+0x1b0/0x250
[c3ffbfb0] [c0036d18] __do_softirq+0xa8/0x124
[c3ffbff0] [c000e5dc] call_do_softirq+0x14/0x24
[c080fe70] [c0005e38] do_softirq+0x6c/0x8c
[c080fe90] [c00367b4] irq_exit+0x3c/0x54
[c080fea0] [c0005fec] do_IRQ+0x128/0x144
[c080fec0] [c000f2ac] ret_from_except+0x0/0x14
--- Exception: 501 at cpu_idle+0x94/0xe4
LR = cpu_idle+0x94/0xe4
[c080ff80] [c00090c4] cpu_idle+0xe0/0xe4 (unreliable)
[c080ffa0] [c0003e50] rest_init+0xa8/0xd8
[c080ffc0] [c023e80c] start_kernel+0x2b8/0x2cc
[c080fff0] [00003438] 0x3438
Instruction dump:
2f800000 40be003c 38810008 7fe3fb78 38a00040 4bfeb799 7fc6f378 7fe4fb78
7c651b78 3c60c022 386316b0 4804f631 <0fe00000> 38000001 3d20c085 9809aa6c
---[ end trace 0cbe1c7362251e03 ]---
UCC2 Fast registers:
Base address: 0xc91c6200
gumr : addr=0xc91c6200, val=0x0000003c
upsmr : addr=0xc91c6204, val=0x02002000
utodr : addr=0xc91c6208, val=0x0000
udsr : addr=0xc91c620c, val=0x7e7e
ucce : addr=0xc91c6210, val=0x00000000
uccm : addr=0xc91c6214, val=0x5fff00ff
uccs : addr=0xc91c6218, val=0x00
urfb : addr=0xc91c6220, val=0x00001780
urfs : addr=0xc91c6224, val=0x0200
urfet : addr=0xc91c6228, val=0x0100
urfset: addr=0xc91c622a, val=0x0180
utfb : addr=0xc91c622c, val=0x00001580
utfs : addr=0xc91c6230, val=0x0200
utfet : addr=0xc91c6234, val=0x0100
utftt : addr=0xc91c6238, val=0x0200
utpt : addr=0xc91c623c, val=0x0100
urtry : addr=0xc91c6240, val=0x00000000
guemr : addr=0xc91c6290, val=0x13
UCC3 Geth registers:
Base address: 0xc91ca200
maccfg1 : addr - 0xc91ca300, val - 0x0000003f
maccfg2 : addr - 0xc91ca304, val - 0x00007124
ipgifg : addr - 0xc91ca308, val - 0x40605060
hafdup : addr - 0xc91ca30c, val - 0x00a1f037
ifctl : addr - 0xc91ca338, val - 0x01000000
ifstat : addr - 0xc91ca33c, val - 0x00000008
macstnaddr1: addr - 0xc91ca340, val - 0x4708007c
macstnaddr2: addr - 0xc91ca344, val - 0x19000000
uempr : addr - 0xc91ca350, val - 0x00000000
utbipar : addr - 0xc91ca354, val - 0x00000020
uescr : addr - 0xc91ca358, val - 0x0804
tx64 : addr - 0xc91ca380, val - 0x00000005
tx127 : addr - 0xc91ca384, val - 0x00000046
tx255 : addr - 0xc91ca388, val - 0x00000121
rx64 : addr - 0xc91ca38c, val - 0x00000002
rx127 : addr - 0xc91ca390, val - 0x00000078
rx255 : addr - 0xc91ca394, val - 0x00000139
txok : addr - 0xc91ca398, val - 0x0001294f
txcf : addr - 0xc91ca39c, val - 0x0000
tmca : addr - 0xc91ca3a0, val - 0x00000000
tbca : addr - 0xc91ca3a4, val - 0x00000002
rxfok : addr - 0xc91ca3a8, val - 0x000001a8
rxbok : addr - 0xc91ca3ac, val - 0x0001563d
rbyt : addr - 0xc91ca3b0, val - 0x0001843b
rmca : addr - 0xc91ca3b4, val - 0x00000000
rbca : addr - 0xc91ca3b8, val - 0x00000004
scar : addr - 0xc91ca3bc, val - 0x00000000
scam : addr - 0xc91ca3c0, val - 0xfffe0000
Thread data TXs:
Base address: 0xfdefbb00
Thread data TX[0]:
Base address: 0xfdefbb00
0xfdefbb00: 18000000 188000ca 18000000 18800044
0xfdefbb10: 18000000 1880005e 0260e130 18800044
0xfdefbb20: 02000318 202490c5 00181204 0c00800e
0xfdefbb30: 69fdf7fe b6a7fa9f eddffeaf bb7fff77
0xfdefbb40: 800016e0 b2000080 0260e138 02179876
0xfdefbb50: 80001580 b0000080 0260e138 021798f6
0xfdefbb60: 80001608 b0000080
Thread data RX:
Base address: 0xfdefbd00
Thread data RX[0]:
Base address: 0xfdefbd00
0xfdefbd00: 00000000 08c01055 00000000 02003000
0xfdefbd10: 00000000 08c01055 00000000 02003000
0xfdefbd20: 00001f00 00000005
TX global param:
Base address: 0xfdefba00
temoder : addr - 0xfdefba00, val - 0x0110
sqptr : addr - 0xfdefba38, val - 0x000019a0
schedulerbasepointer: addr - 0xfdefba3c, val - 0x00000000
txrmonbaseptr: addr - 0xfdefba40, val - 0x00001a80
tstate : addr - 0xfdefba44, val - 0x30000000
iphoffset[0] : addr - 0xfdefba48, val - 0x00
iphoffset[1] : addr - 0xfdefba49, val - 0x00
iphoffset[2] : addr - 0xfdefba4a, val - 0x00
iphoffset[3] : addr - 0xfdefba4b, val - 0x00
iphoffset[4] : addr - 0xfdefba4c, val - 0x00
iphoffset[5] : addr - 0xfdefba4d, val - 0x00
iphoffset[6] : addr - 0xfdefba4e, val - 0x00
iphoffset[7] : addr - 0xfdefba4f, val - 0x00
vtagtable[0] : addr - 0xfdefba50, val - 0x00000000
vtagtable[1] : addr - 0xfdefba54, val - 0x00000000
vtagtable[2] : addr - 0xfdefba58, val - 0x00000000
vtagtable[3] : addr - 0xfdefba5c, val - 0x00000000
vtagtable[4] : addr - 0xfdefba60, val - 0x00000000
vtagtable[5] : addr - 0xfdefba64, val - 0x00000000
vtagtable[6] : addr - 0xfdefba68, val - 0x00000000
vtagtable[7] : addr - 0xfdefba6c, val - 0x00000000
tqptr : addr - 0xfdefba70, val - 0x00001b00
RX global param:
Base address: 0xfdefbc00
remoder : addr - 0xfdefbc00, val - 0x00001000
rqptr : addr - 0xfdefbc04, val - 0x00001d00
typeorlen : addr - 0xfdefbc20, val - 0x0c00
rxgstpack : addr - 0xfdefbc23, val - 0x00
rxrmonbaseptr : addr - 0xfdefbc24, val - 0x00001e00
intcoalescingptr: addr - 0xfdefbc30, val - 0x00001ac0
rstate : addr - 0xfdefbc36, val - 0x30
mrblr : addr - 0xfdefbc46, val - 0x0600
rbdqptr : addr - 0xfdefbc48, val - 0x00001e60
mflr : addr - 0xfdefbc4c, val - 0x05ee
minflr : addr - 0xfdefbc4e, val - 0x0040
maxd1 : addr - 0xfdefbc50, val - 0x05f0
maxd2 : addr - 0xfdefbc52, val - 0x05f0
ecamptr : addr - 0xfdefbc54, val - 0x00000000
l2qt : addr - 0xfdefbc58, val - 0x00000000
l3qt[0] : addr - 0xfdefbc5c, val - 0x00000000
l3qt[1] : addr - 0xfdefbc60, val - 0x00000000
l3qt[2] : addr - 0xfdefbc64, val - 0x00000000
l3qt[3] : addr - 0xfdefbc68, val - 0x00000000
l3qt[4] : addr - 0xfdefbc6c, val - 0x00000000
l3qt[5] : addr - 0xfdefbc70, val - 0x00000000
l3qt[6] : addr - 0xfdefbc74, val - 0x00000000
l3qt[7] : addr - 0xfdefbc78, val - 0x00000000
vlantype : addr - 0xfdefbc7c, val - 0x8100
vlantci : addr - 0xfdefbc7e, val - 0x0000
addressfiltering[0]: addr - 0xfdefbc80, val - 0x00
addressfiltering[1]: addr - 0xfdefbc81, val - 0x00
addressfiltering[2]: addr - 0xfdefbc82, val - 0x00
addressfiltering[3]: addr - 0xfdefbc83, val - 0x00
addressfiltering[4]: addr - 0xfdefbc84, val - 0x00
addressfiltering[5]: addr - 0xfdefbc85, val - 0x00
addressfiltering[6]: addr - 0xfdefbc86, val - 0x00
addressfiltering[7]: addr - 0xfdefbc87, val - 0x00
addressfiltering[8]: addr - 0xfdefbc88, val - 0x00
addressfiltering[9]: addr - 0xfdefbc89, val - 0x00
addressfiltering[10]: addr - 0xfdefbc8a, val - 0x00
addressfiltering[11]: addr - 0xfdefbc8b, val - 0x01
addressfiltering[12]: addr - 0xfdefbc8c, val - 0x00
addressfiltering[13]: addr - 0xfdefbc8d, val - 0x00
addressfiltering[14]: addr - 0xfdefbc8e, val - 0x00
addressfiltering[15]: addr - 0xfdefbc8f, val - 0x00
addressfiltering[16]: addr - 0xfdefbc90, val - 0x00
addressfiltering[17]: addr - 0xfdefbc91, val - 0x00
addressfiltering[18]: addr - 0xfdefbc92, val - 0x01
addressfiltering[19]: addr - 0xfdefbc93, val - 0x00
addressfiltering[20]: addr - 0xfdefbc94, val - 0x00
addressfiltering[21]: addr - 0xfdefbc95, val - 0x5e
addressfiltering[22]: addr - 0xfdefbc96, val - 0x00
addressfiltering[23]: addr - 0xfdefbc97, val - 0x01
addressfiltering[24]: addr - 0xfdefbc98, val - 0x00
addressfiltering[25]: addr - 0xfdefbc99, val - 0x00
addressfiltering[26]: addr - 0xfdefbc9a, val - 0xff
addressfiltering[27]: addr - 0xfdefbc9b, val - 0xff
addressfiltering[28]: addr - 0xfdefbc9c, val - 0xff
addressfiltering[29]: addr - 0xfdefbc9d, val - 0xff
addressfiltering[30]: addr - 0xfdefbc9e, val - 0xff
addressfiltering[31]: addr - 0xfdefbc9f, val - 0xff
addressfiltering[32]: addr - 0xfdefbca0, val - 0x00
addressfiltering[33]: addr - 0xfdefbca1, val - 0x00
addressfiltering[34]: addr - 0xfdefbca2, val - 0xff
addressfiltering[35]: addr - 0xfdefbca3, val - 0xff
addressfiltering[36]: addr - 0xfdefbca4, val - 0xff
addressfiltering[37]: addr - 0xfdefbca5, val - 0xff
addressfiltering[38]: addr - 0xfdefbca6, val - 0xff
addressfiltering[39]: addr - 0xfdefbca7, val - 0xff
addressfiltering[40]: addr - 0xfdefbca8, val - 0x00
addressfiltering[41]: addr - 0xfdefbca9, val - 0x00
addressfiltering[42]: addr - 0xfdefbcaa, val - 0xff
addressfiltering[43]: addr - 0xfdefbcab, val - 0xff
addressfiltering[44]: addr - 0xfdefbcac, val - 0xff
addressfiltering[45]: addr - 0xfdefbcad, val - 0xff
addressfiltering[46]: addr - 0xfdefbcae, val - 0xff
addressfiltering[47]: addr - 0xfdefbcaf, val - 0xff
addressfiltering[48]: addr - 0xfdefbcb0, val - 0x00
addressfiltering[49]: addr - 0xfdefbcb1, val - 0x00
addressfiltering[50]: addr - 0xfdefbcb2, val - 0xff
addressfiltering[51]: addr - 0xfdefbcb3, val - 0xff
addressfiltering[52]: addr - 0xfdefbcb4, val - 0xff
addressfiltering[53]: addr - 0xfdefbcb5, val - 0xff
addressfiltering[54]: addr - 0xfdefbcb6, val - 0xff
addressfiltering[55]: addr - 0xfdefbcb7, val - 0xff
addressfiltering[56]: addr - 0xfdefbcb8, val - 0x81
addressfiltering[57]: addr - 0xfdefbcb9, val - 0x00
addressfiltering[58]: addr - 0xfdefbcba, val - 0x00
addressfiltering[59]: addr - 0xfdefbcbb, val - 0x00
addressfiltering[60]: addr - 0xfdefbcbc, val - 0x00
addressfiltering[61]: addr - 0xfdefbcbd, val - 0x00
addressfiltering[62]: addr - 0xfdefbcbe, val - 0x00
addressfiltering[63]: addr - 0xfdefbcbf, val - 0x00
exfGlobalParam : addr - 0xfdefbcc0, val - 0x00000000
Send Q memory registers:
Base address: 0xfdefb9a0
SQQD[0]:
Base address: 0xfdefb9a0
0xfdefb9a0: 0260e0c0 000019c0 0260e0c0 0260e128
0xfdefb9b0: b73b3fff 0260e128 0260e0c0 0260e138
0xfdefb9c0: 180000cb 02158076 18000213 02175876
0xfdefb9d0: 18000044 02158076 1800005e 02203876
TX FW statistics pram:
Base address: 0xfdefba80
0xfdefba80: 00000000 00000000 00000000 00000000
0xfdefba90: 00000000 00000033 0000015b 00000000
0xfdefbaa0: 00000015 0000000c 00000002 00000000
RX FW statistics pram:
Base address: 0xfdefbe00
0xfdefbe00: 00000032 00000000 00000000 00000000
0xfdefbe10: 00000000 00000001 00000000 00000000
0xfdefbe20: 00000000 00000000 00000000 00000000
0xfdefbe30: 00000000 0000000c 00000013 00000007
0xfdefbe40: 00000000 00000000 00000000 00000000
0xfdefbe50: 00000000 00000000 00000000
RX IRQ coalescing tables:
Base address: 0xfdefbac0
RX IRQ coalescing table entry[0]:
Base address: 0xfdefbac0
interruptcoalescingmaxvalue: addr - 0xfdefbac0, val - 0x00000001
interruptcoalescingcounter : addr - 0xfdefbac4, val - 0x00000001
RX BD QS tables:
Base address: 0xfdefbe60
RX BD QS table[0]:
Base address: 0xfdefbe60
bdbaseptr : addr - 0xfdefbe60, val - 0x00001e70
bdptr : addr - 0xfdefbe64, val - 0x00001e70
externalbdbaseptr: addr - 0xfdefbe68, val - 0x0260e180
externalbdptr : addr - 0xfdefbe6c, val - 0x0260e1c0
ucode RX Prefetched BDs:
Base address: 0xfdefbe70
0xfdefbe70: 90000000 0215b040 90000000 02157040
0xfdefbe80: 90000000 0220d040 90000000 02179040
Init enet param shadow:
Base address: 0xc209ccc0
0xc209ccc0: 0630ff00 04000000 11001c03 04000003
0xc209ccd0: 05001f03 00000000 00000000 00000000
0xc209cce0: 00000000 00000000 00000000 00000000
0xc209ccf0: 00000000 00000000 00001a03 0c001ec3
0xc209cd00: 00000000 00000000 00000000 00000000
0xc209cd10: 00000000 00000000 00000000 00
Init enet entry 0:
Base address: 0xfdefbec0
0xfdefbec0: 40001a00 86001b70 80000000 0000000c
0xfdefbed0: a4e3ff7f e27eff3f df196bd9 33fffc2f
0xfdefbee0: 10904802 00000020 06001b60 06001b60
0xfdefbef0: 67bdf7ed 1aebfcba 35fbeddb ff799df6
Init enet entry 1:
Base address: 0xfdefbf00
0xfdefbf00: 00001c00 00000000 80201000 30000001
0xfdefbf10: 8ebffdbf df001ec0 020031fe 08c01055
0xfdefbf20: 1cc00056 0215b840 05f00000 00001e60
0xfdefbf30: 021588c0 00bf2d7e 00000000 ebc77fc7
0xfdefbf40: 00060402 00016482 ca1c80c6 9810e8c3
0xfdefbf50: a7e195b7 0260e1b8 00000005 00000005
0xfdefbf60: 00001000 81000000 8f040182 30069982
0xfdefbf70: affc77bf f1e3eeff 00000580 00000056
TX BDs[0]
0xc260e0c0: 980001b4 02083876 980001b4 0215a076
0xc260e0d0: 980001b4 02159076 98000042 0216a25e
0xc260e0e0: 9800004e 02079452 9800004e 0216aa52
0xc260e0f0: 980001b4 02158076 9800004e 0216ac52
0xc260e100: 9800004e 0216a052 980001b4 0260c076
0xc260e110: 9800004e 02625e52 980001b4 02161876
0xc260e120: 9800004e 02625052 980001b4 02161076
0xc260e130: 98000044 02175876 b80001b4 02179876
RX BDs[0]
0xc260e180: 90000000 02154840 90000000 0220d840
0xc260e190: 90000000 02158840 90000000 02154040
0xc260e1a0: 90000000 02159840 90000000 02175040
0xc260e1b0: 90000000 02203840 90000000 0215b840
0xc260e1c0: 90000000 0215b040 90000000 02157040
0xc260e1d0: 90000000 0220d040 90000000 02179040
0xc260e1e0: 90000000 02083040 90000000 0215a840
0xc260e1f0: 90000000 02157840 b0000000 0260c840
PHY: mdio@e0102320:01 - Link is Up - 10/Half
^ permalink raw reply
* Re: Getting the IRQ number (Was: Basic driver devel questions ?)
From: Guillaume Dargaud @ 2010-12-10 16:21 UTC (permalink / raw)
To: linuxppc-dev
In-Reply-To: <1291854156.14490.32.camel@concordia>
Hello all,
> Are you sure that you want to have the chrdev registration here (the
> following code)?
It was commented out in my lastest attempts after reading Michael's
explainations.
> Such stuff typically goes into the probe() function. The modules's
> init() just registers the driver. Furthermore your global variables
> prohibit having more than one device instance using the driver.
Only one such device will ever be build, so don't expect a mainline kernel
patch from me anytime soon ! C;-)
> Also turn on CONFIG_DEBUG_DRIVER and see if that gives you anything
> interesting.
I see an unset CONFIG_DEBUG_KERNEL but no CONFIG_DEBUG_DRIVER in the .config of
my current kernel.
Does it need to be changed in the .config with a full kernel recompilation, or
can I still benefit from it in my module only by doing a #define at the begining
of my code ? Some #defines like DEBUG seem to work locally on things like
dev_dbg but I doubt the CONFIG_* work the same...
> Er. Not sure sorry. I can't see anything obviously wrong. Maybe post
> your driver code again.
Err... I ran it again this morning and it worked farther. I now get into the
probe function and can now register my interrupt, yeah!, but I don't see the
ISR being called. I'm currently checking if it can be a hardware problem
before coming back here for more questions !
BTW, is errno/strerror used within the kernel ?
Thanks all.
--
Guillaume Dargaud
http://www.gdargaud.net/
^ permalink raw reply
* Re: MPC831x (and others?) NAND erase performance improvements
From: Scott Wood @ 2010-12-10 17:56 UTC (permalink / raw)
To: Joakim Tjernlund; +Cc: Mark Mason, linuxppc-dev
In-Reply-To: <OF260A956E.E4DC084C-ONC12577F5.00456569-C12577F5.00457DC0@transmode.se>
On Fri, 10 Dec 2010 13:39:01 +0100
Joakim Tjernlund <joakim.tjernlund@transmode.se> wrote:
> Scott Wood <scottwood@freescale.com> wrote on 2010/12/08 23:25:59:
> >
> > On Wed, 8 Dec 2010 17:02:45 -0500
> > Mark Mason <mason@postdiluvian.org> wrote:
> >
> > > I don't think that using a software NAND controller instead of the LBC
> > > FCM mode is all that bad. Again, I haven't actually done it, so check
> > > the MTD docs, but I'm pretty sure the software is meant to do that, so
> > > it doesn't even really constitute a "fix". Assuming that it is
> > > supported then I doubt that configuring the NAND layer to use your
> > > setup would be any harder than configuring the FCM.
> >
> > The MTD layer supports some really simple NAND controllers, but what do
> > you mean by not having a controller at all? Hooking everything up to
> > GPIO? Using UPM?
> >
> > There is already a UPM NAND driver, BTW.
> >
> > You would lose hardware ECC and the ability to be interrupt-driven (the
> > latter should be possible with SW changes, using GPIO interrupts).
>
> hmm, you think it would be possible to use one of the IRQ pins instead?
GPIO should be fine, software just needs to be changed to use the
interrupt functionality.
An external IRQ line would let you limit interrupts to rising edges
rather than all edges, though you'd lose the ability to directly read
the line status.
-Scott
^ permalink raw reply
* [PATCH -mm 2/4] RapidIO: Add device object linking into discovery
From: Alexandre Bounine @ 2010-12-10 20:38 UTC (permalink / raw)
To: akpm, linux-kernel, linuxppc-dev; +Cc: Alexandre Bounine, Thomas Moll
In-Reply-To: <1292013498-31406-1-git-send-email-alexandre.bounine@idt.com>
Add setting links between rio_dev objects into the discovery process.
This needed to report device connections on agent (non-host) processors
that perform RIO discovery. Originally, these links have been introduced
for enumerating host only to support error management.
Signed-off-by: Alexandre Bounine <alexandre.bounine@idt.com>
Cc: Kumar Gala <galak@kernel.crashing.org>
Cc: Matt Porter <mporter@kernel.crashing.org>
Cc: Li Yang <leoli@freescale.com>
Cc: Thomas Moll <thomas.moll@sysgo.com>
Cc: Micha Nelissen <micha@neli.hopto.org>
---
drivers/rapidio/rio-scan.c | 11 +++++++----
1 files changed, 7 insertions(+), 4 deletions(-)
diff --git a/drivers/rapidio/rio-scan.c b/drivers/rapidio/rio-scan.c
index 45d14cd..e655756 100644
--- a/drivers/rapidio/rio-scan.c
+++ b/drivers/rapidio/rio-scan.c
@@ -947,7 +947,7 @@ static int rio_enum_complete(struct rio_mport *port)
*/
static int __devinit
rio_disc_peer(struct rio_net *net, struct rio_mport *port, u16 destid,
- u8 hopcount)
+ u8 hopcount, struct rio_dev *prev, int prev_port)
{
u8 port_num, route_port;
struct rio_dev *rdev;
@@ -957,6 +957,9 @@ rio_disc_peer(struct rio_net *net, struct rio_mport *port, u16 destid,
if ((rdev = rio_setup_device(net, port, destid, hopcount, 0))) {
/* Add device to the global and bus/net specific list. */
list_add_tail(&rdev->net_list, &net->devices);
+ rdev->prev = prev;
+ if (prev && rio_is_switch(prev))
+ prev->rswitch->nextdev[prev_port] = rdev;
} else
return -1;
@@ -998,8 +1001,8 @@ rio_disc_peer(struct rio_net *net, struct rio_mport *port, u16 destid,
if (ndestid == RIO_ANY_DESTID(port->sys_size))
continue;
rio_unlock_device(port, destid, hopcount);
- if (rio_disc_peer
- (net, port, ndestid, hopcount + 1) < 0)
+ if (rio_disc_peer(net, port, ndestid,
+ hopcount + 1, rdev, port_num) < 0)
return -1;
}
}
@@ -1291,7 +1294,7 @@ int __devinit rio_disc_mport(struct rio_mport *mport)
mport->host_deviceid);
if (rio_disc_peer(net, mport, RIO_ANY_DESTID(mport->sys_size),
- 0) < 0) {
+ 0, NULL, 0) < 0) {
printk(KERN_INFO
"RIO: master port %d device has failed discovery\n",
mport->id);
--
1.7.3.1
^ permalink raw reply related
* [PATCH -mm 4/4] RapidIO: Add new sysfs attributes
From: Alexandre Bounine @ 2010-12-10 20:38 UTC (permalink / raw)
To: akpm, linux-kernel, linuxppc-dev; +Cc: Alexandre Bounine, Thomas Moll
In-Reply-To: <1292013498-31406-1-git-send-email-alexandre.bounine@idt.com>
Add new sysfs attributes.
1. Routing information required to to reach the RIO device:
destid - device destination ID (real for for endpoint, route for switch)
hopcount - hopcount for maintenance requests (switches only)
2. device linking information:
lprev - name of device that precedes the given device in the enumeration
or discovery order (displayed along with of the port to which it
is attached).
lnext - names of devices (with corresponding port numbers) that are
attached to the given device as next in the enumeration or
discovery order (switches only)
Signed-off-by: Alexandre Bounine <alexandre.bounine@idt.com>
Cc: Kumar Gala <galak@kernel.crashing.org>
Cc: Matt Porter <mporter@kernel.crashing.org>
Cc: Li Yang <leoli@freescale.com>
Cc: Thomas Moll <thomas.moll@sysgo.com>
Cc: Micha Nelissen <micha@neli.hopto.org>
---
drivers/rapidio/rio-sysfs.c | 39 ++++++++++++++++++++++++++++++++++++++-
1 files changed, 38 insertions(+), 1 deletions(-)
diff --git a/drivers/rapidio/rio-sysfs.c b/drivers/rapidio/rio-sysfs.c
index 76b4185..b8d9eaf 100644
--- a/drivers/rapidio/rio-sysfs.c
+++ b/drivers/rapidio/rio-sysfs.c
@@ -33,6 +33,8 @@ rio_config_attr(device_rev, "0x%08x\n");
rio_config_attr(asm_did, "0x%04x\n");
rio_config_attr(asm_vid, "0x%04x\n");
rio_config_attr(asm_rev, "0x%04x\n");
+rio_config_attr(destid, "0x%04x\n");
+rio_config_attr(hopcount, "0x%02x\n");
static ssize_t routes_show(struct device *dev, struct device_attribute *attr, char *buf)
{
@@ -52,6 +54,33 @@ static ssize_t routes_show(struct device *dev, struct device_attribute *attr, ch
return (str - buf);
}
+static ssize_t lprev_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct rio_dev *rdev = to_rio_dev(dev);
+
+ return sprintf(buf, "%02d %s\n", RIO_GET_PORT_NUM(rdev->swpinfo),
+ (rdev->prev) ? rio_name(rdev->prev) : "host");
+}
+
+static ssize_t lnext_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct rio_dev *rdev = to_rio_dev(dev);
+ char *str = buf;
+ int i;
+
+ if (rdev->pef & RIO_PEF_SWITCH) {
+ for (i = 0; i < RIO_GET_TOTAL_PORTS(rdev->swpinfo); i++) {
+ if (rdev->rswitch->nextdev[i])
+ str += sprintf(str, "%02d %s\n", i,
+ rio_name(rdev->rswitch->nextdev[i]));
+ }
+ }
+
+ return str - buf;
+}
+
struct device_attribute rio_dev_attrs[] = {
__ATTR_RO(did),
__ATTR_RO(vid),
@@ -59,10 +88,14 @@ struct device_attribute rio_dev_attrs[] = {
__ATTR_RO(asm_did),
__ATTR_RO(asm_vid),
__ATTR_RO(asm_rev),
+ __ATTR_RO(lprev),
+ __ATTR_RO(destid),
__ATTR_NULL,
};
static DEVICE_ATTR(routes, S_IRUGO, routes_show, NULL);
+static DEVICE_ATTR(lnext, S_IRUGO, lnext_show, NULL);
+static DEVICE_ATTR(hopcount, S_IRUGO, hopcount_show, NULL);
static ssize_t
rio_read_config(struct file *filp, struct kobject *kobj,
@@ -218,7 +251,9 @@ int rio_create_sysfs_dev_files(struct rio_dev *rdev)
err = device_create_bin_file(&rdev->dev, &rio_config_attr);
if (!err && (rdev->pef & RIO_PEF_SWITCH)) {
- err = device_create_file(&rdev->dev, &dev_attr_routes);
+ err |= device_create_file(&rdev->dev, &dev_attr_routes);
+ err |= device_create_file(&rdev->dev, &dev_attr_lnext);
+ err |= device_create_file(&rdev->dev, &dev_attr_hopcount);
if (!err && rdev->rswitch->sw_sysfs)
err = rdev->rswitch->sw_sysfs(rdev, RIO_SW_SYSFS_CREATE);
}
@@ -241,6 +276,8 @@ void rio_remove_sysfs_dev_files(struct rio_dev *rdev)
device_remove_bin_file(&rdev->dev, &rio_config_attr);
if (rdev->pef & RIO_PEF_SWITCH) {
device_remove_file(&rdev->dev, &dev_attr_routes);
+ device_remove_file(&rdev->dev, &dev_attr_lnext);
+ device_remove_file(&rdev->dev, &dev_attr_hopcount);
if (rdev->rswitch->sw_sysfs)
rdev->rswitch->sw_sysfs(rdev, RIO_SW_SYSFS_REMOVE);
}
--
1.7.3.1
^ permalink raw reply related
* [PATCH -mm 0/4] RapidIO: Misc updates
From: Alexandre Bounine @ 2010-12-10 20:38 UTC (permalink / raw)
To: akpm, linux-kernel, linuxppc-dev; +Cc: Alexandre Bounine, Thomas Moll
The following four patches are follow-up to two RapidIO patches
that are in the -mm tree now.
Alexandre Bounine (4):
RapidIO: Add definitions of Component Tag fields
RapidIO: Add device object linking into discovery
RapidIO: Use Component Tag for unified switch identification
RapidIO: Add new sysfs attributes
drivers/rapidio/rio-scan.c | 21 ++++++++++++---------
drivers/rapidio/rio-sysfs.c | 39 ++++++++++++++++++++++++++++++++++++++-
drivers/rapidio/rio.c | 2 +-
include/linux/rio.h | 10 ++++++++++
4 files changed, 61 insertions(+), 11 deletions(-)
--
1.7.3.1
^ permalink raw reply
* [PATCH -mm 1/4] RapidIO: Add definitions of Component Tag fields
From: Alexandre Bounine @ 2010-12-10 20:38 UTC (permalink / raw)
To: akpm, linux-kernel, linuxppc-dev; +Cc: Alexandre Bounine, Thomas Moll
In-Reply-To: <1292013498-31406-1-git-send-email-alexandre.bounine@idt.com>
Add definition of the unique device identifier field in the component tag.
RIO_CTAG_UDEVID does not take all 32 bits of the component tag value to
allow future extensions to the component tag use.
Selected size of the RIO_CTAG_UDEVID field (17 bits) is sufficient to accommodate
maximum number of endpoints in large RIO network (16-bit id) plus switches.
Signed-off-by: Alexandre Bounine <alexandre.bounine@idt.com>
Cc: Kumar Gala <galak@kernel.crashing.org>
Cc: Matt Porter <mporter@kernel.crashing.org>
Cc: Li Yang <leoli@freescale.com>
Cc: Thomas Moll <thomas.moll@sysgo.com>
Cc: Micha Nelissen <micha@neli.hopto.org>
---
drivers/rapidio/rio.c | 2 +-
include/linux/rio.h | 10 ++++++++++
2 files changed, 11 insertions(+), 1 deletions(-)
diff --git a/drivers/rapidio/rio.c b/drivers/rapidio/rio.c
index c13289e..cc2a3b7 100644
--- a/drivers/rapidio/rio.c
+++ b/drivers/rapidio/rio.c
@@ -710,7 +710,7 @@ int rio_inb_pwrite_handler(union rio_pw_msg *pw_msg)
u32 err_status, em_perrdet, em_ltlerrdet;
int rc, portnum;
- rdev = rio_get_comptag(pw_msg->em.comptag, NULL);
+ rdev = rio_get_comptag((pw_msg->em.comptag & RIO_CTAG_UDEVID), NULL);
if (rdev == NULL) {
/* Device removed or enumeration error */
pr_debug("RIO: %s No matching device for CTag 0x%08x\n",
diff --git a/include/linux/rio.h b/include/linux/rio.h
index 9b55885..ff681eb 100644
--- a/include/linux/rio.h
+++ b/include/linux/rio.h
@@ -66,6 +66,16 @@
#define RIO_PW_MSG_SIZE 64
+/*
+ * A component tag value (stored in the component tag CSR) is used as device's
+ * unique identifier assigned during enumeration. Besides being used for
+ * identifying switches (which do not have device ID register), it also is used
+ * by error management notification and therefore has to be assigned
+ * to endpoints as well.
+ */
+#define RIO_CTAG_RESRVD 0xfffe0000 /* Reserved */
+#define RIO_CTAG_UDEVID 0x0001ffff /* Unique device identifier */
+
extern struct bus_type rio_bus_type;
extern struct device rio_bus;
extern struct list_head rio_devices; /* list of all devices */
--
1.7.3.1
^ permalink raw reply related
* [PATCH -mm 3/4] RapidIO: Use Component Tag for unified switch identification
From: Alexandre Bounine @ 2010-12-10 20:38 UTC (permalink / raw)
To: akpm, linux-kernel, linuxppc-dev; +Cc: Alexandre Bounine, Thomas Moll
In-Reply-To: <1292013498-31406-1-git-send-email-alexandre.bounine@idt.com>
Change the way how switchid value is set. Local counter variable does not
provide unified way to identify switch devices in a system with multiple
processors. Using local counter leads to the situation when the same RIO
switch has different switch ID for each processor. Replacing local counter
with unique portion of the Component Tag provides unified reference to the
switch by every processor in the system.
Signed-off-by: Alexandre Bounine <alexandre.bounine@idt.com>
Cc: Kumar Gala <galak@kernel.crashing.org>
Cc: Matt Porter <mporter@kernel.crashing.org>
Cc: Li Yang <leoli@freescale.com>
Cc: Thomas Moll <thomas.moll@sysgo.com>
Cc: Micha Nelissen <micha@neli.hopto.org>
---
drivers/rapidio/rio-scan.c | 10 +++++-----
1 files changed, 5 insertions(+), 5 deletions(-)
diff --git a/drivers/rapidio/rio-scan.c b/drivers/rapidio/rio-scan.c
index e655756..467e82b 100644
--- a/drivers/rapidio/rio-scan.c
+++ b/drivers/rapidio/rio-scan.c
@@ -46,7 +46,6 @@ static void rio_init_em(struct rio_dev *rdev);
DEFINE_SPINLOCK(rio_global_list_lock);
static int next_destid = 0;
-static int next_switchid = 0;
static int next_net = 0;
static int next_comptag = 1;
@@ -438,6 +437,10 @@ static struct rio_dev __devinit *rio_setup_device(struct rio_net *net,
rio_mport_write_config_32(port, destid, hopcount,
RIO_COMPONENT_TAG_CSR, next_comptag);
rdev->comp_tag = next_comptag++;
+ } else {
+ rio_mport_read_config_32(port, destid, hopcount,
+ RIO_COMPONENT_TAG_CSR,
+ &rdev->comp_tag);
}
if (rio_device_has_destid(port, rdev->src_ops, rdev->dst_ops)) {
@@ -461,7 +464,7 @@ static struct rio_dev __devinit *rio_setup_device(struct rio_net *net,
/* If a PE has both switch and other functions, show it as a switch */
if (rio_is_switch(rdev)) {
rswitch = rdev->rswitch;
- rswitch->switchid = next_switchid;
+ rswitch->switchid = rdev->comp_tag & RIO_CTAG_UDEVID;
rswitch->port_ok = 0;
rswitch->route_table = kzalloc(sizeof(u8)*
RIO_MAX_ROUTE_ENTRIES(port->sys_size),
@@ -816,7 +819,6 @@ static int __devinit rio_enum_peer(struct rio_net *net, struct rio_mport *port,
return -1;
if (rio_is_switch(rdev)) {
- next_switchid++;
sw_inport = RIO_GET_PORT_NUM(rdev->swpinfo);
rio_route_add_entry(rdev, RIO_GLOBAL_TABLE,
port->host_deviceid, sw_inport, 0);
@@ -964,8 +966,6 @@ rio_disc_peer(struct rio_net *net, struct rio_mport *port, u16 destid,
return -1;
if (rio_is_switch(rdev)) {
- next_switchid++;
-
/* Associated destid is how we accessed this switch */
rdev->destid = destid;
--
1.7.3.1
^ permalink raw reply related
* [PATCH] RapidIO: Add new IDT sRIO switches
From: Alexandre Bounine @ 2010-12-10 21:52 UTC (permalink / raw)
To: akpm, linux-kernel, linuxppc-dev; +Cc: Alexandre Bounine, Thomas Moll
Add new sRIO switch device IDs and enable a basic support for them.
Signed-off-by: Alexandre Bounine <alexandre.bounine@idt.com>
Cc: Kumar Gala <galak@kernel.crashing.org>
Cc: Matt Porter <mporter@kernel.crashing.org>
Cc: Li Yang <leoli@freescale.com>
Cc: Thomas Moll <thomas.moll@sysgo.com>
Cc: Micha Nelissen <micha@neli.hopto.org>
---
drivers/rapidio/switches/idt_gen2.c | 2 ++
include/linux/rio_ids.h | 2 ++
2 files changed, 4 insertions(+), 0 deletions(-)
diff --git a/drivers/rapidio/switches/idt_gen2.c b/drivers/rapidio/switches/idt_gen2.c
index dd4b2b7..095016a 100644
--- a/drivers/rapidio/switches/idt_gen2.c
+++ b/drivers/rapidio/switches/idt_gen2.c
@@ -416,3 +416,5 @@ static int idtg2_switch_init(struct rio_dev *rdev, int do_enum)
DECLARE_RIO_SWITCH_INIT(RIO_VID_IDT, RIO_DID_IDTCPS1848, idtg2_switch_init);
DECLARE_RIO_SWITCH_INIT(RIO_VID_IDT, RIO_DID_IDTCPS1616, idtg2_switch_init);
+DECLARE_RIO_SWITCH_INIT(RIO_VID_IDT, RIO_DID_IDTVPS1616, idtg2_switch_init);
+DECLARE_RIO_SWITCH_INIT(RIO_VID_IDT, RIO_DID_IDTSPS1616, idtg2_switch_init);
diff --git a/include/linux/rio_ids.h b/include/linux/rio_ids.h
index ee7b6ad..7410d33 100644
--- a/include/linux/rio_ids.h
+++ b/include/linux/rio_ids.h
@@ -36,5 +36,7 @@
#define RIO_DID_IDTCPS10Q 0x035e
#define RIO_DID_IDTCPS1848 0x0374
#define RIO_DID_IDTCPS1616 0x0379
+#define RIO_DID_IDTVPS1616 0x0377
+#define RIO_DID_IDTSPS1616 0x0378
#endif /* LINUX_RIO_IDS_H */
--
1.7.3.1
^ permalink raw reply related
* Re: [PATCH V6 09/10] USB/ppc4xx:Synopsys DWC OTG driver enable gadget support
From: Greg KH @ 2010-12-10 22:13 UTC (permalink / raw)
To: Tirumala Marri
Cc: Mark Miesfeld, linux-usb, linuxppc-dev, Fushen Chen,
Sergei Shtylyov
In-Reply-To: <AANLkTimp8iF2PxApA2kXF64TM47_EeGM9BZrfi1uEVJj@mail.gmail.com>
On Thu, Dec 09, 2010 at 08:52:45AM -0800, Tirumala Marri wrote:
> On Thu, Dec 9, 2010 at 5:24 AM, Sergei Shtylyov <sshtylyov@mvista.com> wrote:
> > Hello.
> >
> > On 09-12-2010 3:32, tmarri@apm.com wrote:
> >
> >> From: Tirumala Marri<tmarri@apm.com>
> >
> >> Enable gadget support
> >
> >> Signed-off-by: Tirumala R Marri<tmarri@apm.com>
> >> Signed-off-by: Fushen Chen<fchen@apm.com>
> >> Signed-off-by: Mark Miesfeld<mmiesfeld@apm.com>
> >
> > [...]
> >
> >> diff --git a/drivers/usb/gadget/Kconfig b/drivers/usb/gadget/Kconfig
> >> index 747b0d3..b2bcc4e 100644
> >> --- a/drivers/usb/gadget/Kconfig
> >> +++ b/drivers/usb/gadget/Kconfig
> >> @@ -365,6 +365,28 @@ config USB_GADGET_MUSB_HDRC
> >
> > [...]
> >>
> >> +config USB_OTG
> >> + boolean "OTG Support"
> >
> > This symbol is already defined in drivers/usb/core/Kconfig.
> >
> > WBR, Sergei
> >
> Sure I will fix that.
Other than this minor thing, the patches look good. I'll go apply
patches 1-8 for now, care to resend 9 and 10 with this change made?
thanks,
greg k-h
^ permalink raw reply
* Re: [PATCH V6 01/10] USB/ppc4xx: Add Synopsys DWC OTG Register definitions
From: Greg KH @ 2010-12-10 22:33 UTC (permalink / raw)
To: tmarri; +Cc: Mark Miesfeld, linux-usb, linuxppc-dev, Fushen Chen
In-Reply-To: <1291854722-24744-1-git-send-email-tmarri@apm.com>
On Wed, Dec 08, 2010 at 04:32:02PM -0800, tmarri@apm.com wrote:
> From: Tirumala Marri <tmarri@apm.com>
>
> Add Synopsys Design Ware core register definitions.
>
> Signed-off-by: Tirumala R Marri<tmarri@apm.com>
Minor nit for future patches, you need a space after your name and
before the '<' character.
thanks,
greg k-h
^ permalink raw reply
* Re: [PATCH V6 00/10] Add-Synopsys-DesignWare-HS-USB-OTG-driver
From: Greg KH @ 2010-12-10 22:44 UTC (permalink / raw)
To: tmarri; +Cc: linux-usb, linuxppc-dev
In-Reply-To: <1291854539-23879-1-git-send-email-tmarri@apm.com>
On Wed, Dec 08, 2010 at 04:28:59PM -0800, tmarri@apm.com wrote:
> From: Tirumala Marri <tmarri@apm.com>
>
> v6:
> 1. Replaced register definitions and bit fields with macros.
> 2. Replace printks with dev_dbg or dev_err functions.
> 3. Cleanup some assignments.
> 4. Remove chip specific selections in Kconfig file.
There are still a lot of checkpatch errors and warnings in these
patches. Patch 1-8 has:
total: 64 errors, 126 warnings, 13905 lines checked
Care to fix all of these up in your next submission? I can't take these
as-is, sorry.
And, if you haven't run checkpatch.pl before, odds are you haven't run
'sparse'. Please run that as well and resolve the issues it finds.
thanks,
greg k-h
^ permalink raw reply
* Re: [PATCH -mm 0/4] RapidIO: Misc updates
From: Andrew Morton @ 2010-12-10 23:19 UTC (permalink / raw)
To: Alexandre Bounine; +Cc: linux-kernel, Thomas Moll, linuxppc-dev
In-Reply-To: <1292013498-31406-1-git-send-email-alexandre.bounine@idt.com>
On Fri, 10 Dec 2010 15:38:14 -0500
Alexandre Bounine <alexandre.bounine@idt.com> wrote:
> The following four patches are follow-up to two RapidIO patches
> that are in the -mm tree now.
>
> Alexandre Bounine (4):
> RapidIO: Add definitions of Component Tag fields
> RapidIO: Add device object linking into discovery
> RapidIO: Use Component Tag for unified switch identification
> RapidIO: Add new sysfs attributes
>
I held
rapidio-use-common-destid-storage-for-endpoints-and-switches.patch and
rapidio-integrate-rio_switch-into-rio_dev.patch back from 2.6.37 due to
ongoing discussion. What is the situation now?
^ permalink raw reply
* Re: [PATCH -mm 4/4] RapidIO: Add new sysfs attributes
From: Andrew Morton @ 2010-12-10 23:22 UTC (permalink / raw)
To: Alexandre Bounine; +Cc: linux-kernel, Thomas Moll, linuxppc-dev
In-Reply-To: <1292013498-31406-5-git-send-email-alexandre.bounine@idt.com>
On Fri, 10 Dec 2010 15:38:18 -0500
Alexandre Bounine <alexandre.bounine@idt.com> wrote:
> Add new sysfs attributes.
One would like to see documentation updates along with sysfs API
updates. But one fears that this entire interface wasn't documented
anyway :(
Please at least fully describe the proposed interface in the changelog
so others can review the proposal.
The proposed new sysfs files appear to break the
one-value-per-sysfs-file rule.
^ permalink raw reply
* [PATCH 7/7 v3] ppc: add dynamic dma window support
From: Nishanth Aravamudan @ 2010-12-11 0:07 UTC (permalink / raw)
To: sonnyrao, miltonm, Benjamin Herrenschmidt, Paul Mackerras,
Grant Likely, Anton Blanchard, linuxppc-dev
In-Reply-To: <20101209190920.GB25003@us.ibm.com>
On 09.12.2010 [11:09:20 -0800], Nishanth Aravamudan wrote:
> On 26.10.2010 [20:35:17 -0700], Nishanth Aravamudan wrote:
> > If firmware allows us to map all of a partition's memory for DMA on a
> > particular bridge, create a 1:1 mapping of that memory. Add hooks for
> > dealing with hotplug events. Dyanmic DMA windows can use larger than the
> > default page size, and we use the largest one possible.
> >
> > Not-yet-signed-off-by: Nishanth Aravamudan <nacc@us.ibm.com>
> >
> > ---
> >
> > I've tested this briefly on a machine with suitable firmware/hardware.
> > Things seem to work well, but I want to do more exhaustive I/O testing
> > before asking for upstream merging. I would really appreciate any
> > feedback on the updated approach.
> >
> > Specific questions:
> >
> > Ben, did I hook into the dma_set_mask() platform callback as you
> > expected? Anything I can do better or which perhaps might lead to
> > gotchas later?
> >
> > I've added a disable_ddw option, but perhaps it would be better to
> > just disable the feature if iommu=force?
>
> So for the final version, I probably should document this option in
> kernel-parameters.txt w/ the patch, right?
Here's an updated version. Ben, think you can pick this up to your tree?
Thanks,
Nish
ppc: add dynamic dma window support
If firmware allows us to map all of a partition's memory for DMA on a
particular bridge, create a 1:1 mapping of that memory. Add hooks for
dealing with hotplug events. Dyanmic DMA windows can use larger than the
default page size, and we use the largest one possible.
Signed-off-by: Nishanth Aravamudan <nacc@us.ibm.com>
---
I've tested this fairly heavily on a machine with suitable
firmware/hardware, including dlpar operations.
diff --git a/Documentation/kernel-parameters.txt b/Documentation/kernel-parameters.txt
index cdd2a6e..e9ac890 100644
--- a/Documentation/kernel-parameters.txt
+++ b/Documentation/kernel-parameters.txt
@@ -618,6 +618,10 @@ and is between 256 and 4096 characters. It is defined in the file
disable= [IPV6]
See Documentation/networking/ipv6.txt.
+ disable_ddw [PPC]
+ Disable Dynamic DMA Window support. Use this if
+ to workaround buggy firmware.
+
disable_ipv6= [IPV6]
See Documentation/networking/ipv6.txt.
diff --git a/arch/powerpc/platforms/pseries/iommu.c b/arch/powerpc/platforms/pseries/iommu.c
index 45c6865..4ba2338 100644
--- a/arch/powerpc/platforms/pseries/iommu.c
+++ b/arch/powerpc/platforms/pseries/iommu.c
@@ -33,6 +33,7 @@
#include <linux/pci.h>
#include <linux/dma-mapping.h>
#include <linux/crash_dump.h>
+#include <linux/memory.h>
#include <asm/io.h>
#include <asm/prom.h>
#include <asm/rtas.h>
@@ -45,6 +46,7 @@
#include <asm/tce.h>
#include <asm/ppc-pci.h>
#include <asm/udbg.h>
+#include <asm/mmzone.h>
#include "plpar_wrappers.h"
@@ -270,6 +272,137 @@ static unsigned long tce_get_pSeriesLP(struct iommu_table *tbl, long tcenum)
return tce_ret;
}
+/* this is compatable with cells for the device tree property */
+struct dynamic_dma_window_prop {
+ __be32 liobn; /* tce table number */
+ __be64 dma_base; /* address hi,lo */
+ __be32 tce_shift; /* ilog2(tce_page_size) */
+ __be32 window_shift; /* ilog2(tce_window_size) */
+};
+
+struct direct_window {
+ struct device_node *device;
+ const struct dynamic_dma_window_prop *prop;
+ struct list_head list;
+};
+static LIST_HEAD(direct_window_list);
+/* prevents races between memory on/offline and window creation */
+static DEFINE_SPINLOCK(direct_window_list_lock);
+/* protects initializing window twice for same device */
+static DEFINE_MUTEX(direct_window_init_mutex);
+#define DIRECT64_PROPNAME "linux,direct64-ddr-window-info"
+
+static int tce_clearrange_multi_pSeriesLP(unsigned long start_pfn,
+ unsigned long num_pfn, const void *arg)
+{
+ const struct dynamic_dma_window_prop *maprange = arg;
+ int rc;
+ u64 tce_size, num_tce, dma_offset, next;
+ u32 tce_shift;
+ long limit;
+
+ tce_shift = be32_to_cpu(maprange->tce_shift);
+ tce_size = 1ULL << tce_shift;
+ next = start_pfn << PAGE_SHIFT;
+ num_tce = num_pfn << PAGE_SHIFT;
+
+ /* round back to the beginning of the tce page size */
+ num_tce += next & (tce_size - 1);
+ next &= ~(tce_size - 1);
+
+ /* covert to number of tces */
+ num_tce |= tce_size - 1;
+ num_tce >>= tce_shift;
+
+ do {
+ /*
+ * Set up the page with TCE data, looping through and setting
+ * the values.
+ */
+ limit = min_t(long, num_tce, 512);
+ dma_offset = next + be64_to_cpu(maprange->dma_base);
+
+ rc = plpar_tce_stuff((u64)be32_to_cpu(maprange->liobn),
+ (u64)dma_offset,
+ 0, limit);
+ num_tce -= limit;
+ } while (num_tce > 0 && !rc);
+
+ return rc;
+}
+
+static int tce_setrange_multi_pSeriesLP(unsigned long start_pfn,
+ unsigned long num_pfn, const void *arg)
+{
+ const struct dynamic_dma_window_prop *maprange = arg;
+ u64 *tcep, tce_size, num_tce, dma_offset, next, proto_tce, liobn;
+ u32 tce_shift;
+ u64 rc = 0;
+ long l, limit;
+
+ local_irq_disable(); /* to protect tcep and the page behind it */
+ tcep = __get_cpu_var(tce_page);
+
+ if (!tcep) {
+ tcep = (u64 *)__get_free_page(GFP_ATOMIC);
+ if (!tcep) {
+ local_irq_enable();
+ return -ENOMEM;
+ }
+ __get_cpu_var(tce_page) = tcep;
+ }
+
+ proto_tce = TCE_PCI_READ | TCE_PCI_WRITE;
+
+ liobn = (u64)be32_to_cpu(maprange->liobn);
+ tce_shift = be32_to_cpu(maprange->tce_shift);
+ tce_size = 1ULL << tce_shift;
+ next = start_pfn << PAGE_SHIFT;
+ num_tce = num_pfn << PAGE_SHIFT;
+
+ /* round back to the beginning of the tce page size */
+ num_tce += next & (tce_size - 1);
+ next &= ~(tce_size - 1);
+
+ /* covert to number of tces */
+ num_tce |= tce_size - 1;
+ num_tce >>= tce_shift;
+
+ /* We can map max one pageful of TCEs at a time */
+ do {
+ /*
+ * Set up the page with TCE data, looping through and setting
+ * the values.
+ */
+ limit = min_t(long, num_tce, 4096/TCE_ENTRY_SIZE);
+ dma_offset = next + be64_to_cpu(maprange->dma_base);
+
+ for (l = 0; l < limit; l++) {
+ tcep[l] = proto_tce | next;
+ next += tce_size;
+ }
+
+ rc = plpar_tce_put_indirect(liobn,
+ (u64)dma_offset,
+ (u64)virt_to_abs(tcep),
+ limit);
+
+ num_tce -= limit;
+ } while (num_tce > 0 && !rc);
+
+ /* error cleanup: caller will clear whole range */
+
+ local_irq_enable();
+ return rc;
+}
+
+static int tce_setrange_multi_pSeriesLP_walk(unsigned long start_pfn,
+ unsigned long num_pfn, void *arg)
+{
+ return tce_setrange_multi_pSeriesLP(start_pfn, num_pfn, arg);
+}
+
+
#ifdef CONFIG_PCI
static void iommu_table_setparms(struct pci_controller *phb,
struct device_node *dn,
@@ -449,8 +582,7 @@ static void pci_dma_bus_setup_pSeriesLP(struct pci_bus *bus)
if (!ppci->iommu_table) {
tbl = kzalloc_node(sizeof(struct iommu_table), GFP_KERNEL,
ppci->phb->node);
- iommu_table_setparms_lpar(ppci->phb, pdn, tbl, dma_window,
- bus->number);
+ iommu_table_setparms_lpar(ppci->phb, pdn, tbl, dma_window);
ppci->iommu_table = iommu_init_table(tbl, ppci->phb->node);
pr_debug(" created table: %p\n", ppci->iommu_table);
}
@@ -496,6 +628,328 @@ static void pci_dma_dev_setup_pSeries(struct pci_dev *dev)
pci_name(dev));
}
+static int __read_mostly disable_ddw;
+
+static int __init disable_ddw_setup(char *str)
+{
+ disable_ddw = 1;
+ printk(KERN_INFO "ppc iommu: disabling ddw.\n");
+
+ return 0;
+}
+
+early_param("disable_ddw", disable_ddw_setup);
+
+static void remove_ddw(struct device_node *np)
+{
+ struct dynamic_dma_window_prop *dwp;
+ struct property *win64;
+ const u32 *ddr_avail;
+ u64 liobn;
+ int len, ret;
+
+ ddr_avail = of_get_property(np, "ibm,ddw-applicable", &len);
+ win64 = of_find_property(np, DIRECT64_PROPNAME, NULL);
+ if (!win64 || !ddr_avail || len < 3 * sizeof(u32))
+ return;
+
+ dwp = win64->value;
+ liobn = (u64)be32_to_cpu(dwp->liobn);
+
+ /* clear the whole window, note the arg is in kernel pages */
+ ret = tce_clearrange_multi_pSeriesLP(0,
+ 1ULL << (be32_to_cpu(dwp->window_shift) - PAGE_SHIFT), dwp);
+ if (ret)
+ pr_warning("%s failed to clear tces in window.\n",
+ np->full_name);
+ else
+ pr_warning("%s successfully cleared tces in window.\n",
+ np->full_name);
+
+ ret = rtas_call(ddr_avail[2], 1, 1, NULL, liobn);
+ if (ret)
+ pr_warning("%s: failed to remove direct window: rtas returned "
+ "%d to ibm,remove-pe-dma-window(%x) %llx\n",
+ np->full_name, ret, ddr_avail[2], liobn);
+ else
+ pr_warning("%s: successfully removed direct window: rtas returned "
+ "%d to ibm,remove-pe-dma-window(%x) %llx\n",
+ np->full_name, ret, ddr_avail[2], liobn);
+}
+
+
+static int dupe_ddw_if_already_created(struct pci_dev *dev, struct device_node *pdn)
+{
+ struct device_node *dn;
+ struct pci_dn *pcidn;
+ struct direct_window *window;
+ const struct dynamic_dma_window_prop *direct64;
+ u64 dma_addr;
+
+ dn = pci_device_to_OF_node(dev);
+ pcidn = PCI_DN(dn);
+ spin_lock(&direct_window_list_lock);
+ /* check if we already created a window and dupe that config if so */
+ list_for_each_entry(window, &direct_window_list, list) {
+ if (window->device == pdn) {
+ direct64 = window->prop;
+ dma_addr = direct64->dma_base;
+ break;
+ }
+ }
+ spin_unlock(&direct_window_list_lock);
+
+ return dma_addr;
+}
+
+static u64 dupe_ddw_if_kexec(struct pci_dev *dev, struct device_node *pdn)
+{
+ struct device_node *dn;
+ struct pci_dn *pcidn;
+ int len;
+ struct direct_window *window;
+ const struct dynamic_dma_window_prop *direct64;
+ u64 dma_addr;
+
+ dn = pci_device_to_OF_node(dev);
+ pcidn = PCI_DN(dn);
+ direct64 = of_get_property(pdn, DIRECT64_PROPNAME, &len);
+ if (direct64) {
+ window = kzalloc(sizeof(*window), GFP_KERNEL);
+ if (!window) {
+ remove_ddw(pdn);
+ } else {
+ window->device = pdn;
+ window->prop = direct64;
+ spin_lock(&direct_window_list_lock);
+ list_add(&window->list, &direct_window_list);
+ spin_unlock(&direct_window_list_lock);
+ dma_addr = direct64->dma_base;
+ }
+ }
+
+ return dma_addr;
+}
+
+static int query_ddw(struct pci_dev *dev, const u32 *ddr_avail, u32 *query)
+{
+ struct device_node *dn;
+ struct pci_dn *pcidn;
+ u32 cfg_addr;
+ u64 buid;
+ int ret;
+
+ /*
+ * Get the config address and phb build of the PE window.
+ * Rely on eeh to retrieve this for us.
+ * Retrieve them from the pci device, not the node with the
+ * dma-window property
+ */
+ dn = pci_device_to_OF_node(dev);
+ pcidn = PCI_DN(dn);
+ cfg_addr = pcidn->eeh_config_addr;
+ if (pcidn->eeh_pe_config_addr)
+ cfg_addr = pcidn->eeh_pe_config_addr;
+ buid = pcidn->phb->buid;
+ ret = rtas_call(ddr_avail[0], 3, 5, query,
+ cfg_addr, BUID_HI(buid), BUID_LO(buid));
+ dev_info(&dev->dev, "ibm,query-pe-dma-windows(%x) %x %x %x"
+ " returned %d\n", ddr_avail[0], cfg_addr, BUID_HI(buid),
+ BUID_LO(buid), ret);
+ return ret;
+}
+
+static int create_ddw(struct pci_dev *dev, const u32 *ddr_avail, u32 *create, int page_shift, int window_shift)
+{
+ struct device_node *dn;
+ struct pci_dn *pcidn;
+ u32 cfg_addr;
+ u64 buid;
+ int ret;
+
+ /*
+ * Get the config address and phb build of the PE window.
+ * Rely on eeh to retrieve this for us.
+ * Retrieve them from the pci device, not the node with the
+ * dma-window property
+ */
+ dn = pci_device_to_OF_node(dev);
+ pcidn = PCI_DN(dn);
+ cfg_addr = pcidn->eeh_config_addr;
+ if (pcidn->eeh_pe_config_addr)
+ cfg_addr = pcidn->eeh_pe_config_addr;
+ buid = pcidn->phb->buid;
+
+ do {
+ /* extra outputs are LIOBN and dma-addr (hi, lo) */
+ ret = rtas_call(ddr_avail[1], 5, 4, &create[0], cfg_addr,
+ BUID_HI(buid), BUID_LO(buid), page_shift, window_shift);
+ } while(rtas_busy_delay(ret));
+ dev_info(&dev->dev,
+ "ibm,create-pe-dma-window(%x) %x %x %x %x %x returned %d "
+ "(liobn = 0x%x starting addr = %x %x)\n", ddr_avail[1],
+ cfg_addr, BUID_HI(buid), BUID_LO(buid), page_shift,
+ window_shift, ret, create[0], create[1], create[2]);
+
+ return ret;
+}
+
+/*
+ * If the PE supports dynamic dma windows, and there is space for a table
+ * that can map all pages in a linear offset, then setup such a table,
+ * and record the dma-offset in the struct device.
+ *
+ * dev: the pci device we are checking
+ * pdn: the parent pe node with the ibm,dma_window property
+ * Future: also check if we can remap the base window for our base page size
+ *
+ * returns the dma offset for use by dma_set_mask
+ */
+static u64 enable_ddw(struct pci_dev *dev, struct device_node *pdn)
+{
+ int len, ret;
+ u32 query[4], create[3];
+ int page_shift;
+ u64 dma_addr, max_addr;
+ struct device_node *dn;
+ const u32 *uninitialized_var(ddr_avail);
+ struct direct_window *window;
+ struct property *uninitialized_var(win64);
+ struct dynamic_dma_window_prop *ddwprop;
+
+ mutex_lock(&direct_window_init_mutex);
+
+ dma_addr = dupe_ddw_if_already_created(dev, pdn);
+ if (dma_addr != 0)
+ goto out_unlock;
+
+ dma_addr = dupe_ddw_if_kexec(dev, pdn);
+ if (dma_addr != 0)
+ goto out_unlock;
+
+ /*
+ * the ibm,ddw-applicable property holds the tokens for:
+ * ibm,query-pe-dma-window
+ * ibm,create-pe-dma-window
+ * ibm,remove-pe-dma-window
+ * for the given node in that order.
+ * the property is actually in the parent, not the PE
+ */
+ ddr_avail = of_get_property(pdn, "ibm,ddw-applicable", &len);
+ if (!ddr_avail || len < 3 * sizeof(u32))
+ goto out_unlock;
+
+ /*
+ * Query if there is a second window of size to map the
+ * whole partition. Query returns number of windows, largest
+ * block assigned to PE (partition endpoint), and two bitmasks
+ * of page sizes: supported and supported for migrate-dma.
+ */
+ dn = pci_device_to_OF_node(dev);
+ ret = query_ddw(dev, ddr_avail, &query[0]);
+ if (ret != 0)
+ goto out_unlock;
+
+ if (!query[0]) {
+ /*
+ * no additional windows are available for this device.
+ * We might be able to reallocate the existing window,
+ * trading in for a larger page size.
+ */
+ dev_dbg(&dev->dev, "no free dynamic windows");
+ goto out_unlock;
+ }
+ if (query[2] & 4) {
+ page_shift = 24; /* 16MB */
+ } else if (query[2] & 2) {
+ page_shift = 16; /* 64kB */
+ } else if (query[2] & 1) {
+ page_shift = 12; /* 4kB */
+ } else {
+ dev_dbg(&dev->dev, "no supported direct page size in mask %x",
+ query[2]);
+ goto out_unlock;
+ }
+ /* verify the window * number of ptes will map the partition */
+ /* check largest block * page size > max memory hotplug addr */
+ max_addr = memory_hotplug_max();
+ if (query[1] < (max_addr >> page_shift)) {
+ dev_dbg(&dev->dev, "can't map partiton max 0x%llx with %u "
+ "%llu-sized pages\n", max_addr, query[1],
+ 1ULL << page_shift);
+ goto out_unlock;
+ }
+ len = order_base_2(max_addr);
+ win64 = kzalloc(sizeof(struct property), GFP_KERNEL);
+ if (!win64) {
+ dev_info(&dev->dev,
+ "couldn't allocate property for 64bit dma window\n");
+ goto out_unlock;
+ }
+ win64->name = kstrdup(DIRECT64_PROPNAME, GFP_KERNEL);
+ win64->value = ddwprop = kmalloc(sizeof(*ddwprop), GFP_KERNEL);
+ if (!win64->name || !win64->value) {
+ dev_info(&dev->dev,
+ "couldn't allocate property name and value\n");
+ goto out_free_prop;
+ }
+
+ ret = create_ddw(dev, ddr_avail, &create[0], page_shift, len);
+ if (ret != 0)
+ goto out_free_prop;
+
+ *ddwprop = (struct dynamic_dma_window_prop) {
+ .liobn = cpu_to_be32(create[0]),
+ .dma_base = cpu_to_be64(((u64)create[1] << 32) + (u64)create[2]),
+ .tce_shift = cpu_to_be32(page_shift),
+ .window_shift = cpu_to_be32(len)
+ };
+
+ dev_dbg(&dev->dev, "created tce table LIOBN 0x%x for %s\n",
+ create[0], dn->full_name);
+
+ window = kzalloc(sizeof(*window), GFP_KERNEL);
+ if (!window)
+ goto out_clear_window;
+
+ ret = walk_system_ram_range(0, memblock_end_of_DRAM() >> PAGE_SHIFT,
+ win64->value, tce_setrange_multi_pSeriesLP_walk);
+ if (ret) {
+ dev_info(&dev->dev, "failed to map direct window for %s: %d\n",
+ dn->full_name, ret);
+ goto out_clear_window;
+ }
+
+ ret = prom_add_property(pdn, win64);
+ if (ret) {
+ dev_err(&dev->dev, "unable to add dma window property for %s: %d",
+ pdn->full_name, ret);
+ goto out_clear_window;
+ }
+
+ window->device = pdn;
+ window->prop = ddwprop;
+ spin_lock(&direct_window_list_lock);
+ list_add(&window->list, &direct_window_list);
+ spin_unlock(&direct_window_list_lock);
+
+ dma_addr = of_read_number(&create[1], 2);
+ set_dma_offset(&dev->dev, dma_addr);
+ goto out_unlock;
+
+out_clear_window:
+ remove_ddw(pdn);
+
+out_free_prop:
+ kfree(win64->name);
+ kfree(win64->value);
+ kfree(win64);
+
+out_unlock:
+ mutex_unlock(&direct_window_init_mutex);
+ return dma_addr;
+}
+
static void pci_dma_dev_setup_pSeriesLP(struct pci_dev *dev)
{
struct device_node *pdn, *dn;
@@ -542,23 +996,129 @@ static void pci_dma_dev_setup_pSeriesLP(struct pci_dev *dev)
set_iommu_table_base(&dev->dev, pci->iommu_table);
}
+
+static int dma_set_mask_pSeriesLP(struct device *dev, u64 dma_mask)
+{
+ bool ddw_enabled = false;
+ struct device_node *pdn, *dn;
+ struct pci_dev *pdev;
+ const void *dma_window = NULL;
+ u64 dma_offset;
+
+ if (!dev->dma_mask || !dma_supported(dev, dma_mask))
+ return -EIO;
+
+ /* only attempt to use a new window if 64-bit DMA is requested */
+ if (!disable_ddw && dma_mask == DMA_BIT_MASK(64)) {
+ pdev = to_pci_dev(dev);
+
+ dn = pci_device_to_OF_node(pdev);
+ dev_dbg(dev, "node is %s\n", dn->full_name);
+
+ /*
+ * the device tree might contain the dma-window properties
+ * per-device and not neccesarily for the bus. So we need to
+ * search upwards in the tree until we either hit a dma-window
+ * property, OR find a parent with a table already allocated.
+ */
+ for (pdn = dn; pdn && PCI_DN(pdn) && !PCI_DN(pdn)->iommu_table;
+ pdn = pdn->parent) {
+ dma_window = of_get_property(pdn, "ibm,dma-window", NULL);
+ if (dma_window)
+ break;
+ }
+ if (pdn && PCI_DN(pdn)) {
+ dma_offset = enable_ddw(pdev, pdn);
+ if (dma_offset != 0) {
+ dev_info(dev, "Using 64-bit direct DMA at offset %llx\n", dma_offset);
+ set_dma_offset(dev, dma_offset);
+ set_dma_ops(dev, &dma_direct_ops);
+ ddw_enabled = true;
+ }
+ }
+ }
+
+ /* fall-through to iommu ops */
+ if (!ddw_enabled) {
+ dev_info(dev, "Using 32-bit DMA via iommu\n");
+ set_dma_ops(dev, &dma_iommu_ops);
+ }
+
+ *dev->dma_mask = dma_mask;
+ return 0;
+}
+
#else /* CONFIG_PCI */
#define pci_dma_bus_setup_pSeries NULL
#define pci_dma_dev_setup_pSeries NULL
#define pci_dma_bus_setup_pSeriesLP NULL
#define pci_dma_dev_setup_pSeriesLP NULL
+#define dma_set_mask_pSeriesLP NULL
#endif /* !CONFIG_PCI */
+static int iommu_mem_notifier(struct notifier_block *nb, unsigned long action,
+ void *data)
+{
+ struct direct_window *window;
+ struct memory_notify *arg = data;
+ int ret = 0;
+
+ switch (action) {
+ case MEM_GOING_ONLINE:
+ spin_lock(&direct_window_list_lock);
+ list_for_each_entry(window, &direct_window_list, list) {
+ ret |= tce_setrange_multi_pSeriesLP(arg->start_pfn,
+ arg->nr_pages, window->prop);
+ /* XXX log error */
+ }
+ spin_unlock(&direct_window_list_lock);
+ break;
+ case MEM_CANCEL_ONLINE:
+ case MEM_OFFLINE:
+ spin_lock(&direct_window_list_lock);
+ list_for_each_entry(window, &direct_window_list, list) {
+ ret |= tce_clearrange_multi_pSeriesLP(arg->start_pfn,
+ arg->nr_pages, window->prop);
+ /* XXX log error */
+ }
+ spin_unlock(&direct_window_list_lock);
+ break;
+ default:
+ break;
+ }
+ if (ret && action != MEM_CANCEL_ONLINE)
+ return NOTIFY_BAD;
+
+ return NOTIFY_OK;
+}
+
+static struct notifier_block iommu_mem_nb = {
+ .notifier_call = iommu_mem_notifier,
+};
+
static int iommu_reconfig_notifier(struct notifier_block *nb, unsigned long action, void *node)
{
int err = NOTIFY_OK;
struct device_node *np = node;
struct pci_dn *pci = PCI_DN(np);
+ struct direct_window *window;
switch (action) {
case PSERIES_RECONFIG_REMOVE:
if (pci && pci->iommu_table)
iommu_free_table(pci->iommu_table, np->full_name);
+
+ spin_lock(&direct_window_list_lock);
+ list_for_each_entry(window, &direct_window_list, list) {
+ if (window->device == np) {
+ list_del(&window->list);
+ kfree(window);
+ break;
+ }
+ }
+ spin_unlock(&direct_window_list_lock);
+
+ remove_ddw(np);
break;
default:
err = NOTIFY_DONE;
@@ -588,6 +1148,7 @@ void iommu_init_early_pSeries(void)
ppc_md.tce_get = tce_get_pSeriesLP;
ppc_md.pci_dma_bus_setup = pci_dma_bus_setup_pSeriesLP;
ppc_md.pci_dma_dev_setup = pci_dma_dev_setup_pSeriesLP;
+ ppc_md.dma_set_mask = dma_set_mask_pSeriesLP;
} else {
ppc_md.tce_build = tce_build_pSeries;
ppc_md.tce_free = tce_free_pSeries;
@@ -598,6 +1159,7 @@ void iommu_init_early_pSeries(void)
pSeries_reconfig_notifier_register(&iommu_reconfig_nb);
+ register_memory_notifier(&iommu_mem_nb);
set_pci_dma_ops(&dma_iommu_ops);
}
--
Nishanth Aravamudan <nacc@us.ibm.com>
IBM Linux Technology Center
^ permalink raw reply related
* [PATCH 1/2] powerpc, mpc5200: update defconfig to fit with 2.6.37-rc4
From: Heiko Schocher @ 2010-12-11 6:13 UTC (permalink / raw)
To: linuxppc-dev; +Cc: Heiko Schocher
Signed-off-by: Heiko Schocher <hs@denx.de>
cc: Wolfram Sang <w.sang@pengutronix.de>
cc: linuxppc-dev@lists.ozlabs.org
---
- based against 2.6.37-rc4
- I did the following steps to generate this patch:
make mpc5200_defconfig
make savedefconfig
cp ./defconfig arch/powerpc/configs/mpc5200_defconfig
arch/powerpc/configs/mpc5200_defconfig | 5 +----
1 files changed, 1 insertions(+), 4 deletions(-)
diff --git a/arch/powerpc/configs/mpc5200_defconfig b/arch/powerpc/configs/mpc5200_defconfig
index 7376e27..6f75623 100644
--- a/arch/powerpc/configs/mpc5200_defconfig
+++ b/arch/powerpc/configs/mpc5200_defconfig
@@ -1,5 +1,6 @@
CONFIG_EXPERIMENTAL=y
CONFIG_SYSVIPC=y
+CONFIG_SPARSE_IRQ=y
CONFIG_LOG_BUF_SHIFT=14
CONFIG_BLK_DEV_INITRD=y
# CONFIG_CC_OPTIMIZE_FOR_SIZE is not set
@@ -20,7 +21,6 @@ CONFIG_PPC_BESTCOMM=y
CONFIG_SIMPLE_GPIO=y
CONFIG_NO_HZ=y
CONFIG_HIGH_RES_TIMERS=y
-CONFIG_SPARSE_IRQ=y
CONFIG_PM=y
CONFIG_NET=y
CONFIG_PACKET=y
@@ -52,7 +52,6 @@ CONFIG_PROC_DEVICETREE=y
CONFIG_BLK_DEV_LOOP=y
CONFIG_BLK_DEV_RAM=y
CONFIG_BLK_DEV_RAM_SIZE=32768
-CONFIG_EEPROM_AT24=y
CONFIG_SCSI_TGT=y
CONFIG_BLK_DEV_SD=y
CONFIG_CHR_DEV_SG=y
@@ -114,7 +113,6 @@ CONFIG_RTC_DRV_DS1307=y
CONFIG_EXT2_FS=y
CONFIG_EXT3_FS=y
# CONFIG_EXT3_DEFAULTS_TO_ORDERED is not set
-CONFIG_INOTIFY=y
CONFIG_MSDOS_FS=y
CONFIG_VFAT_FS=y
CONFIG_PROC_KCORE=y
@@ -132,5 +130,4 @@ CONFIG_PRINTK_TIME=y
CONFIG_DEBUG_KERNEL=y
CONFIG_DETECT_HUNG_TASK=y
CONFIG_DEBUG_INFO=y
-# CONFIG_RCU_CPU_STALL_DETECTOR is not set
# CONFIG_CRYPTO_ANSI_CPRNG is not set
--
1.7.2.3
^ permalink raw reply related
* [PATCH 2/2] powerpc, mpc5200: update mpc5200_defconfig to fit for charon board.
From: Heiko Schocher @ 2010-12-11 6:13 UTC (permalink / raw)
To: linuxppc-dev; +Cc: Heiko Schocher
In-Reply-To: <1292047981-1435-1-git-send-email-hs@denx.de>
add to mpc5200_defconfig:
CONFIG_SENSORS_LM80=m
CONFIG_RTC_DRV_DS1374=m
as this is used by the charon board.
Signed-off-by: Heiko Schocher <hs@denx.de>
cc: Wolfram Sang <w.sang@pengutronix.de>
cc: linuxppc-dev@lists.ozlabs.org
---
- based against 2.6.37-rc4
arch/powerpc/configs/mpc5200_defconfig | 2 ++
1 files changed, 2 insertions(+), 0 deletions(-)
diff --git a/arch/powerpc/configs/mpc5200_defconfig b/arch/powerpc/configs/mpc5200_defconfig
index 6f75623..70e5d8e 100644
--- a/arch/powerpc/configs/mpc5200_defconfig
+++ b/arch/powerpc/configs/mpc5200_defconfig
@@ -78,6 +78,7 @@ CONFIG_SPI_GPIO=m
CONFIG_SPI_MPC52xx=m
CONFIG_SPI_MPC52xx_PSC=m
CONFIG_SPI_SPIDEV=m
+CONFIG_SENSORS_LM80=m
CONFIG_WATCHDOG=y
CONFIG_DRM=y
CONFIG_VIDEO_OUTPUT_CONTROL=y
@@ -110,6 +111,7 @@ CONFIG_USB_STORAGE=y
CONFIG_NEW_LEDS=y
CONFIG_RTC_CLASS=y
CONFIG_RTC_DRV_DS1307=y
+CONFIG_RTC_DRV_DS1374=m
CONFIG_EXT2_FS=y
CONFIG_EXT3_FS=y
# CONFIG_EXT3_DEFAULTS_TO_ORDERED is not set
--
1.7.2.3
^ permalink raw reply related
* [PATCH v2 1/2] video, sm501: add OF binding to support SM501
From: Heiko Schocher @ 2010-12-11 6:31 UTC (permalink / raw)
To: linuxppc-dev
Cc: linux-fbdev, devicetree-discuss, Samuel Ortiz, Vincent Sanders,
linux-kernel, Ben Dooks, Heiko Schocher
In-Reply-To: <1291451028-22532-1-git-send-email-hs@denx.de>
- add binding to OF, compatible name "smi,sm501"
- add read/write functions for using this driver
also on powerpc plattforms
- add commandline options:
sm501.fb_mode:
Specify resolution as "<xres>x<yres>[-<bpp>][@<refresh>]"
sm501.bpp:
Specify bit-per-pixel if not specified mode
- Add support for encoding display mode information
in the device tree using verbatim EDID block.
If the "edid" entry in the "smi,sm501" node is present,
the driver will build mode database using EDID data
and allow setting the display modes from this database.
Signed-off-by: Heiko Schocher <hs@denx.de>
cc: linux-fbdev@vger.kernel.org
cc: devicetree-discuss@ozlabs.org
cc: Ben Dooks <ben@simtec.co.uk>
cc: Vincent Sanders <vince@simtec.co.uk>
cc: Samuel Ortiz <sameo@linux.intel.com>
cc: linux-kernel@vger.kernel.org
---
- changes since v1:
add Ben Dooks, Vincent Sanders and Samuel Ortiz to cc, as suggested from
Paul Mundt.
Documentation/kernel-parameters.txt | 7 +
Documentation/powerpc/dts-bindings/sm501.txt | 30 +++
drivers/mfd/sm501.c | 141 ++++++++------
drivers/video/sm501fb.c | 264 +++++++++++++++++---------
include/linux/sm501.h | 8 +
5 files changed, 299 insertions(+), 151 deletions(-)
create mode 100644 Documentation/powerpc/dts-bindings/sm501.txt
diff --git a/Documentation/kernel-parameters.txt b/Documentation/kernel-parameters.txt
index cdd2a6e..6341541 100644
--- a/Documentation/kernel-parameters.txt
+++ b/Documentation/kernel-parameters.txt
@@ -2301,6 +2301,13 @@ and is between 256 and 4096 characters. It is defined in the file
merging on their own.
For more information see Documentation/vm/slub.txt.
+ sm501.bpp= SM501 Display driver:
+ Specify bit-per-pixel if not specified mode
+
+ sm501fb.mode= SM501 Display driver:
+ Specify resolution as
+ "<xres>x<yres>[-<bpp>][@<refresh>]"
+
smart2= [HW]
Format: <io1>[,<io2>[,...,<io8>]]
diff --git a/Documentation/powerpc/dts-bindings/sm501.txt b/Documentation/powerpc/dts-bindings/sm501.txt
new file mode 100644
index 0000000..9905dd9
--- /dev/null
+++ b/Documentation/powerpc/dts-bindings/sm501.txt
@@ -0,0 +1,30 @@
+* SM SM501
+
+The SM SM501 is a LCD controller, with proper hardware, it can also
+drive DVI monitors.
+
+Required properties:
+- compatible : should be "smi,sm501".
+- reg : contain two entries:
+ - First entry: System Configuration register
+ - Second entry: IO space (Display Controller register)
+- interrupts : SMI interrupt to the cpu should be described here.
+- interrupt-parent : the phandle for the interrupt controller that
+ services interrupts for this device.
+
+Optional properties:
+- mode : select a video mode:
+ <xres>x<yres>[-<bpp>][@<refresh>]
+- edid : verbatim EDID data block describing attached display.
+ Data from the detailed timing descriptor will be used to
+ program the display controller.
+
+Example for MPC5200:
+ display@1,0 {
+ compatible = "smi,sm501";
+ reg = <1 0x00000000 0x00800000
+ 1 0x03e00000 0x00200000>;
+ interrupts = <1 1 3>;
+ mode = "640x480-32@60";
+ edid = [edid-data];
+ };
diff --git a/drivers/mfd/sm501.c b/drivers/mfd/sm501.c
index bc9275c..d1f952c 100644
--- a/drivers/mfd/sm501.c
+++ b/drivers/mfd/sm501.c
@@ -133,10 +133,10 @@ static unsigned long decode_div(unsigned long pll2, unsigned long val,
static void sm501_dump_clk(struct sm501_devdata *sm)
{
- unsigned long misct = readl(sm->regs + SM501_MISC_TIMING);
- unsigned long pm0 = readl(sm->regs + SM501_POWER_MODE_0_CLOCK);
- unsigned long pm1 = readl(sm->regs + SM501_POWER_MODE_1_CLOCK);
- unsigned long pmc = readl(sm->regs + SM501_POWER_MODE_CONTROL);
+ unsigned long misct = smc501_readl(sm->regs + SM501_MISC_TIMING);
+ unsigned long pm0 = smc501_readl(sm->regs + SM501_POWER_MODE_0_CLOCK);
+ unsigned long pm1 = smc501_readl(sm->regs + SM501_POWER_MODE_1_CLOCK);
+ unsigned long pmc = smc501_readl(sm->regs + SM501_POWER_MODE_CONTROL);
unsigned long sdclk0, sdclk1;
unsigned long pll2 = 0;
@@ -193,29 +193,29 @@ static void sm501_dump_regs(struct sm501_devdata *sm)
void __iomem *regs = sm->regs;
dev_info(sm->dev, "System Control %08x\n",
- readl(regs + SM501_SYSTEM_CONTROL));
+ smc501_readl(regs + SM501_SYSTEM_CONTROL));
dev_info(sm->dev, "Misc Control %08x\n",
- readl(regs + SM501_MISC_CONTROL));
+ smc501_readl(regs + SM501_MISC_CONTROL));
dev_info(sm->dev, "GPIO Control Low %08x\n",
- readl(regs + SM501_GPIO31_0_CONTROL));
+ smc501_readl(regs + SM501_GPIO31_0_CONTROL));
dev_info(sm->dev, "GPIO Control Hi %08x\n",
- readl(regs + SM501_GPIO63_32_CONTROL));
+ smc501_readl(regs + SM501_GPIO63_32_CONTROL));
dev_info(sm->dev, "DRAM Control %08x\n",
- readl(regs + SM501_DRAM_CONTROL));
+ smc501_readl(regs + SM501_DRAM_CONTROL));
dev_info(sm->dev, "Arbitration Ctrl %08x\n",
- readl(regs + SM501_ARBTRTN_CONTROL));
+ smc501_readl(regs + SM501_ARBTRTN_CONTROL));
dev_info(sm->dev, "Misc Timing %08x\n",
- readl(regs + SM501_MISC_TIMING));
+ smc501_readl(regs + SM501_MISC_TIMING));
}
static void sm501_dump_gate(struct sm501_devdata *sm)
{
dev_info(sm->dev, "CurrentGate %08x\n",
- readl(sm->regs + SM501_CURRENT_GATE));
+ smc501_readl(sm->regs + SM501_CURRENT_GATE));
dev_info(sm->dev, "CurrentClock %08x\n",
- readl(sm->regs + SM501_CURRENT_CLOCK));
+ smc501_readl(sm->regs + SM501_CURRENT_CLOCK));
dev_info(sm->dev, "PowerModeControl %08x\n",
- readl(sm->regs + SM501_POWER_MODE_CONTROL));
+ smc501_readl(sm->regs + SM501_POWER_MODE_CONTROL));
}
#else
@@ -231,7 +231,7 @@ static inline void sm501_dump_clk(struct sm501_devdata *sm) { }
static void sm501_sync_regs(struct sm501_devdata *sm)
{
- readl(sm->regs);
+ smc501_readl(sm->regs);
}
static inline void sm501_mdelay(struct sm501_devdata *sm, unsigned int delay)
@@ -261,11 +261,11 @@ int sm501_misc_control(struct device *dev,
spin_lock_irqsave(&sm->reg_lock, save);
- misc = readl(sm->regs + SM501_MISC_CONTROL);
+ misc = smc501_readl(sm->regs + SM501_MISC_CONTROL);
to = (misc & ~clear) | set;
if (to != misc) {
- writel(to, sm->regs + SM501_MISC_CONTROL);
+ smc501_writel(to, sm->regs + SM501_MISC_CONTROL);
sm501_sync_regs(sm);
dev_dbg(sm->dev, "MISC_CONTROL %08lx\n", misc);
@@ -294,11 +294,11 @@ unsigned long sm501_modify_reg(struct device *dev,
spin_lock_irqsave(&sm->reg_lock, save);
- data = readl(sm->regs + reg);
+ data = smc501_readl(sm->regs + reg);
data |= set;
data &= ~clear;
- writel(data, sm->regs + reg);
+ smc501_writel(data, sm->regs + reg);
sm501_sync_regs(sm);
spin_unlock_irqrestore(&sm->reg_lock, save);
@@ -322,9 +322,9 @@ int sm501_unit_power(struct device *dev, unsigned int unit, unsigned int to)
mutex_lock(&sm->clock_lock);
- mode = readl(sm->regs + SM501_POWER_MODE_CONTROL);
- gate = readl(sm->regs + SM501_CURRENT_GATE);
- clock = readl(sm->regs + SM501_CURRENT_CLOCK);
+ mode = smc501_readl(sm->regs + SM501_POWER_MODE_CONTROL);
+ gate = smc501_readl(sm->regs + SM501_CURRENT_GATE);
+ clock = smc501_readl(sm->regs + SM501_CURRENT_CLOCK);
mode &= 3; /* get current power mode */
@@ -356,14 +356,14 @@ int sm501_unit_power(struct device *dev, unsigned int unit, unsigned int to)
switch (mode) {
case 1:
- writel(gate, sm->regs + SM501_POWER_MODE_0_GATE);
- writel(clock, sm->regs + SM501_POWER_MODE_0_CLOCK);
+ smc501_writel(gate, sm->regs + SM501_POWER_MODE_0_GATE);
+ smc501_writel(clock, sm->regs + SM501_POWER_MODE_0_CLOCK);
mode = 0;
break;
case 2:
case 0:
- writel(gate, sm->regs + SM501_POWER_MODE_1_GATE);
- writel(clock, sm->regs + SM501_POWER_MODE_1_CLOCK);
+ smc501_writel(gate, sm->regs + SM501_POWER_MODE_1_GATE);
+ smc501_writel(clock, sm->regs + SM501_POWER_MODE_1_CLOCK);
mode = 1;
break;
@@ -372,7 +372,7 @@ int sm501_unit_power(struct device *dev, unsigned int unit, unsigned int to)
goto already;
}
- writel(mode, sm->regs + SM501_POWER_MODE_CONTROL);
+ smc501_writel(mode, sm->regs + SM501_POWER_MODE_CONTROL);
sm501_sync_regs(sm);
dev_dbg(sm->dev, "gate %08lx, clock %08lx, mode %08lx\n",
@@ -519,9 +519,9 @@ unsigned long sm501_set_clock(struct device *dev,
unsigned long req_freq)
{
struct sm501_devdata *sm = dev_get_drvdata(dev);
- unsigned long mode = readl(sm->regs + SM501_POWER_MODE_CONTROL);
- unsigned long gate = readl(sm->regs + SM501_CURRENT_GATE);
- unsigned long clock = readl(sm->regs + SM501_CURRENT_CLOCK);
+ unsigned long mode = smc501_readl(sm->regs + SM501_POWER_MODE_CONTROL);
+ unsigned long gate = smc501_readl(sm->regs + SM501_CURRENT_GATE);
+ unsigned long clock = smc501_readl(sm->regs + SM501_CURRENT_CLOCK);
unsigned char reg;
unsigned int pll_reg = 0;
unsigned long sm501_freq; /* the actual frequency achieved */
@@ -592,9 +592,9 @@ unsigned long sm501_set_clock(struct device *dev,
mutex_lock(&sm->clock_lock);
- mode = readl(sm->regs + SM501_POWER_MODE_CONTROL);
- gate = readl(sm->regs + SM501_CURRENT_GATE);
- clock = readl(sm->regs + SM501_CURRENT_CLOCK);
+ mode = smc501_readl(sm->regs + SM501_POWER_MODE_CONTROL);
+ gate = smc501_readl(sm->regs + SM501_CURRENT_GATE);
+ clock = smc501_readl(sm->regs + SM501_CURRENT_CLOCK);
clock = clock & ~(0xFF << clksrc);
clock |= reg<<clksrc;
@@ -603,14 +603,14 @@ unsigned long sm501_set_clock(struct device *dev,
switch (mode) {
case 1:
- writel(gate, sm->regs + SM501_POWER_MODE_0_GATE);
- writel(clock, sm->regs + SM501_POWER_MODE_0_CLOCK);
+ smc501_writel(gate, sm->regs + SM501_POWER_MODE_0_GATE);
+ smc501_writel(clock, sm->regs + SM501_POWER_MODE_0_CLOCK);
mode = 0;
break;
case 2:
case 0:
- writel(gate, sm->regs + SM501_POWER_MODE_1_GATE);
- writel(clock, sm->regs + SM501_POWER_MODE_1_CLOCK);
+ smc501_writel(gate, sm->regs + SM501_POWER_MODE_1_GATE);
+ smc501_writel(clock, sm->regs + SM501_POWER_MODE_1_CLOCK);
mode = 1;
break;
@@ -619,10 +619,11 @@ unsigned long sm501_set_clock(struct device *dev,
return -1;
}
- writel(mode, sm->regs + SM501_POWER_MODE_CONTROL);
+ smc501_writel(mode, sm->regs + SM501_POWER_MODE_CONTROL);
if (pll_reg)
- writel(pll_reg, sm->regs + SM501_PROGRAMMABLE_PLL_CONTROL);
+ smc501_writel(pll_reg,
+ sm->regs + SM501_PROGRAMMABLE_PLL_CONTROL);
sm501_sync_regs(sm);
@@ -905,7 +906,7 @@ static int sm501_gpio_get(struct gpio_chip *chip, unsigned offset)
struct sm501_gpio_chip *smgpio = to_sm501_gpio(chip);
unsigned long result;
- result = readl(smgpio->regbase + SM501_GPIO_DATA_LOW);
+ result = smc501_readl(smgpio->regbase + SM501_GPIO_DATA_LOW);
result >>= offset;
return result & 1UL;
@@ -918,13 +919,13 @@ static void sm501_gpio_ensure_gpio(struct sm501_gpio_chip *smchip,
/* check and modify if this pin is not set as gpio. */
- if (readl(smchip->control) & bit) {
+ if (smc501_readl(smchip->control) & bit) {
dev_info(sm501_gpio_to_dev(smchip->ourgpio)->dev,
"changing mode of gpio, bit %08lx\n", bit);
- ctrl = readl(smchip->control);
+ ctrl = smc501_readl(smchip->control);
ctrl &= ~bit;
- writel(ctrl, smchip->control);
+ smc501_writel(ctrl, smchip->control);
sm501_sync_regs(sm501_gpio_to_dev(smchip->ourgpio));
}
@@ -945,10 +946,10 @@ static void sm501_gpio_set(struct gpio_chip *chip, unsigned offset, int value)
spin_lock_irqsave(&smgpio->lock, save);
- val = readl(regs + SM501_GPIO_DATA_LOW) & ~bit;
+ val = smc501_readl(regs + SM501_GPIO_DATA_LOW) & ~bit;
if (value)
val |= bit;
- writel(val, regs);
+ smc501_writel(val, regs);
sm501_sync_regs(sm501_gpio_to_dev(smgpio));
sm501_gpio_ensure_gpio(smchip, bit);
@@ -970,8 +971,8 @@ static int sm501_gpio_input(struct gpio_chip *chip, unsigned offset)
spin_lock_irqsave(&smgpio->lock, save);
- ddr = readl(regs + SM501_GPIO_DDR_LOW);
- writel(ddr & ~bit, regs + SM501_GPIO_DDR_LOW);
+ ddr = smc501_readl(regs + SM501_GPIO_DDR_LOW);
+ smc501_writel(ddr & ~bit, regs + SM501_GPIO_DDR_LOW);
sm501_sync_regs(sm501_gpio_to_dev(smgpio));
sm501_gpio_ensure_gpio(smchip, bit);
@@ -997,18 +998,18 @@ static int sm501_gpio_output(struct gpio_chip *chip,
spin_lock_irqsave(&smgpio->lock, save);
- val = readl(regs + SM501_GPIO_DATA_LOW);
+ val = smc501_readl(regs + SM501_GPIO_DATA_LOW);
if (value)
val |= bit;
else
val &= ~bit;
- writel(val, regs);
+ smc501_writel(val, regs);
- ddr = readl(regs + SM501_GPIO_DDR_LOW);
- writel(ddr | bit, regs + SM501_GPIO_DDR_LOW);
+ ddr = smc501_readl(regs + SM501_GPIO_DDR_LOW);
+ smc501_writel(ddr | bit, regs + SM501_GPIO_DDR_LOW);
sm501_sync_regs(sm501_gpio_to_dev(smgpio));
- writel(val, regs + SM501_GPIO_DATA_LOW);
+ smc501_writel(val, regs + SM501_GPIO_DATA_LOW);
sm501_sync_regs(sm501_gpio_to_dev(smgpio));
spin_unlock_irqrestore(&smgpio->lock, save);
@@ -1234,7 +1235,7 @@ static ssize_t sm501_dbg_regs(struct device *dev,
for (reg = 0x00; reg < 0x70; reg += 4) {
ret = sprintf(ptr, "%08x = %08x\n",
- reg, readl(sm->regs + reg));
+ reg, smc501_readl(sm->regs + reg));
ptr += ret;
}
@@ -1258,10 +1259,10 @@ static inline void sm501_init_reg(struct sm501_devdata *sm,
{
unsigned long tmp;
- tmp = readl(sm->regs + reg);
+ tmp = smc501_readl(sm->regs + reg);
tmp &= ~r->mask;
tmp |= r->set;
- writel(tmp, sm->regs + reg);
+ smc501_writel(tmp, sm->regs + reg);
}
/* sm501_init_regs
@@ -1302,7 +1303,7 @@ static void sm501_init_regs(struct sm501_devdata *sm,
static int sm501_check_clocks(struct sm501_devdata *sm)
{
- unsigned long pwrmode = readl(sm->regs + SM501_CURRENT_CLOCK);
+ unsigned long pwrmode = smc501_readl(sm->regs + SM501_CURRENT_CLOCK);
unsigned long msrc = (pwrmode & SM501_POWERMODE_M_SRC);
unsigned long m1src = (pwrmode & SM501_POWERMODE_M1_SRC);
@@ -1337,7 +1338,7 @@ static int __devinit sm501_init_dev(struct sm501_devdata *sm)
INIT_LIST_HEAD(&sm->devices);
- devid = readl(sm->regs + SM501_DEVICEID);
+ devid = smc501_readl(sm->regs + SM501_DEVICEID);
if ((devid & SM501_DEVICEID_IDMASK) != SM501_DEVICEID_SM501) {
dev_err(sm->dev, "incorrect device id %08lx\n", devid);
@@ -1345,9 +1346,9 @@ static int __devinit sm501_init_dev(struct sm501_devdata *sm)
}
/* disable irqs */
- writel(0, sm->regs + SM501_IRQ_MASK);
+ smc501_writel(0, sm->regs + SM501_IRQ_MASK);
- dramctrl = readl(sm->regs + SM501_DRAM_CONTROL);
+ dramctrl = smc501_readl(sm->regs + SM501_DRAM_CONTROL);
mem_avail = sm501_mem_local[(dramctrl >> 13) & 0x7];
dev_info(sm->dev, "SM501 At %p: Version %08lx, %ld Mb, IRQ %d\n",
@@ -1379,7 +1380,7 @@ static int __devinit sm501_init_dev(struct sm501_devdata *sm)
sm501_register_gpio(sm);
}
- if (pdata->gpio_i2c != NULL && pdata->gpio_i2c_nr > 0) {
+ if (pdata && pdata->gpio_i2c != NULL && pdata->gpio_i2c_nr > 0) {
if (!sm501_gpio_isregistered(sm))
dev_err(sm->dev, "no gpio available for i2c gpio.\n");
else
@@ -1424,6 +1425,14 @@ static int __devinit sm501_plat_probe(struct platform_device *dev)
sm->io_res = platform_get_resource(dev, IORESOURCE_MEM, 1);
sm->mem_res = platform_get_resource(dev, IORESOURCE_MEM, 0);
+
+ if (sm->mem_res)
+ pr_debug("sm501 mem 0x%lx, 0x%lx\n",
+ sm->mem_res->start, sm->mem_res->end);
+ if (sm->io_res)
+ pr_debug("sm501 io 0x%lx, 0x%lx\n",
+ sm->io_res->start, sm->io_res->end);
+
if (sm->io_res == NULL || sm->mem_res == NULL) {
dev_err(&dev->dev, "failed to get IO resource\n");
ret = -ENOENT;
@@ -1492,7 +1501,7 @@ static int sm501_plat_suspend(struct platform_device *pdev, pm_message_t state)
struct sm501_devdata *sm = platform_get_drvdata(pdev);
sm->in_suspend = 1;
- sm->pm_misc = readl(sm->regs + SM501_MISC_CONTROL);
+ sm->pm_misc = smc501_readl(sm->regs + SM501_MISC_CONTROL);
sm501_dump_regs(sm);
@@ -1516,9 +1525,9 @@ static int sm501_plat_resume(struct platform_device *pdev)
/* check to see if we are in the same state as when suspended */
- if (readl(sm->regs + SM501_MISC_CONTROL) != sm->pm_misc) {
+ if (smc501_readl(sm->regs + SM501_MISC_CONTROL) != sm->pm_misc) {
dev_info(sm->dev, "SM501_MISC_CONTROL changed over sleep\n");
- writel(sm->pm_misc, sm->regs + SM501_MISC_CONTROL);
+ smc501_writel(sm->pm_misc, sm->regs + SM501_MISC_CONTROL);
/* our suspend causes the controller state to change,
* either by something attempting setup, power loss,
@@ -1737,10 +1746,16 @@ static struct pci_driver sm501_pci_driver = {
MODULE_ALIAS("platform:sm501");
+static struct of_device_id __devinitdata of_sm501_match_tbl[] = {
+ { .compatible = "smi,sm501", },
+ { /* end */ }
+};
+
static struct platform_driver sm501_plat_driver = {
.driver = {
.name = "sm501",
.owner = THIS_MODULE,
+ .of_match_table = of_sm501_match_tbl,
},
.probe = sm501_plat_probe,
.remove = sm501_plat_remove,
diff --git a/drivers/video/sm501fb.c b/drivers/video/sm501fb.c
index b7dc180..fec8461 100644
--- a/drivers/video/sm501fb.c
+++ b/drivers/video/sm501fb.c
@@ -41,6 +41,26 @@
#include <linux/sm501.h>
#include <linux/sm501-regs.h>
+#include "edid.h"
+
+static char *fb_mode = "640x480-16@60";
+static unsigned long default_bpp = 16;
+
+static struct fb_videomode __devinitdata sm501_default_mode = {
+ .refresh = 60,
+ .xres = 640,
+ .yres = 480,
+ .pixclock = 20833,
+ .left_margin = 142,
+ .right_margin = 13,
+ .upper_margin = 21,
+ .lower_margin = 1,
+ .hsync_len = 69,
+ .vsync_len = 3,
+ .sync = FB_SYNC_HOR_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT,
+ .vmode = FB_VMODE_NONINTERLACED
+};
+
#define NR_PALETTE 256
enum sm501_controller {
@@ -77,6 +97,7 @@ struct sm501fb_info {
void __iomem *regs2d; /* 2d remapped registers */
void __iomem *fbmem; /* remapped framebuffer */
size_t fbmem_len; /* length of remapped region */
+ u8 *edid_data;
};
/* per-framebuffer private data */
@@ -117,7 +138,7 @@ static inline int v_total(struct fb_var_screeninfo *var)
static inline void sm501fb_sync_regs(struct sm501fb_info *info)
{
- readl(info->regs);
+ smc501_readl(info->regs);
}
/* sm501_alloc_mem
@@ -262,7 +283,7 @@ static void sm501fb_setup_gamma(struct sm501fb_info *fbi,
/* set gamma values */
for (offset = 0; offset < 256 * 4; offset += 4) {
- writel(value, fbi->regs + palette + offset);
+ smc501_writel(value, fbi->regs + palette + offset);
value += 0x010101; /* Advance RGB by 1,1,1.*/
}
}
@@ -476,7 +497,8 @@ static int sm501fb_set_par_common(struct fb_info *info,
/* set start of framebuffer to the screen */
- writel(par->screen.sm_addr | SM501_ADDR_FLIP, fbi->regs + head_addr);
+ smc501_writel(par->screen.sm_addr | SM501_ADDR_FLIP,
+ fbi->regs + head_addr);
/* program CRT clock */
@@ -519,7 +541,7 @@ static void sm501fb_set_par_geometry(struct fb_info *info,
reg = info->fix.line_length;
reg |= ((var->xres * var->bits_per_pixel)/8) << 16;
- writel(reg, fbi->regs + (par->head == HEAD_CRT ?
+ smc501_writel(reg, fbi->regs + (par->head == HEAD_CRT ?
SM501_DC_CRT_FB_OFFSET : SM501_DC_PANEL_FB_OFFSET));
/* program horizontal total */
@@ -527,27 +549,27 @@ static void sm501fb_set_par_geometry(struct fb_info *info,
reg = (h_total(var) - 1) << 16;
reg |= (var->xres - 1);
- writel(reg, base + SM501_OFF_DC_H_TOT);
+ smc501_writel(reg, base + SM501_OFF_DC_H_TOT);
/* program horizontal sync */
reg = var->hsync_len << 16;
reg |= var->xres + var->right_margin - 1;
- writel(reg, base + SM501_OFF_DC_H_SYNC);
+ smc501_writel(reg, base + SM501_OFF_DC_H_SYNC);
/* program vertical total */
reg = (v_total(var) - 1) << 16;
reg |= (var->yres - 1);
- writel(reg, base + SM501_OFF_DC_V_TOT);
+ smc501_writel(reg, base + SM501_OFF_DC_V_TOT);
/* program vertical sync */
reg = var->vsync_len << 16;
reg |= var->yres + var->lower_margin - 1;
- writel(reg, base + SM501_OFF_DC_V_SYNC);
+ smc501_writel(reg, base + SM501_OFF_DC_V_SYNC);
}
/* sm501fb_pan_crt
@@ -566,15 +588,15 @@ static int sm501fb_pan_crt(struct fb_var_screeninfo *var,
xoffs = var->xoffset * bytes_pixel;
- reg = readl(fbi->regs + SM501_DC_CRT_CONTROL);
+ reg = smc501_readl(fbi->regs + SM501_DC_CRT_CONTROL);
reg &= ~SM501_DC_CRT_CONTROL_PIXEL_MASK;
reg |= ((xoffs & 15) / bytes_pixel) << 4;
- writel(reg, fbi->regs + SM501_DC_CRT_CONTROL);
+ smc501_writel(reg, fbi->regs + SM501_DC_CRT_CONTROL);
reg = (par->screen.sm_addr + xoffs +
var->yoffset * info->fix.line_length);
- writel(reg | SM501_ADDR_FLIP, fbi->regs + SM501_DC_CRT_FB_ADDR);
+ smc501_writel(reg | SM501_ADDR_FLIP, fbi->regs + SM501_DC_CRT_FB_ADDR);
sm501fb_sync_regs(fbi);
return 0;
@@ -593,10 +615,10 @@ static int sm501fb_pan_pnl(struct fb_var_screeninfo *var,
unsigned long reg;
reg = var->xoffset | (var->xres_virtual << 16);
- writel(reg, fbi->regs + SM501_DC_PANEL_FB_WIDTH);
+ smc501_writel(reg, fbi->regs + SM501_DC_PANEL_FB_WIDTH);
reg = var->yoffset | (var->yres_virtual << 16);
- writel(reg, fbi->regs + SM501_DC_PANEL_FB_HEIGHT);
+ smc501_writel(reg, fbi->regs + SM501_DC_PANEL_FB_HEIGHT);
sm501fb_sync_regs(fbi);
return 0;
@@ -622,7 +644,7 @@ static int sm501fb_set_par_crt(struct fb_info *info)
/* enable CRT DAC - note 0 is on!*/
sm501_misc_control(fbi->dev->parent, 0, SM501_MISC_DAC_POWER);
- control = readl(fbi->regs + SM501_DC_CRT_CONTROL);
+ control = smc501_readl(fbi->regs + SM501_DC_CRT_CONTROL);
control &= (SM501_DC_CRT_CONTROL_PIXEL_MASK |
SM501_DC_CRT_CONTROL_GAMMA |
@@ -684,7 +706,7 @@ static int sm501fb_set_par_crt(struct fb_info *info)
out_update:
dev_dbg(fbi->dev, "new control is %08lx\n", control);
- writel(control, fbi->regs + SM501_DC_CRT_CONTROL);
+ smc501_writel(control, fbi->regs + SM501_DC_CRT_CONTROL);
sm501fb_sync_regs(fbi);
return 0;
@@ -696,18 +718,18 @@ static void sm501fb_panel_power(struct sm501fb_info *fbi, int to)
void __iomem *ctrl_reg = fbi->regs + SM501_DC_PANEL_CONTROL;
struct sm501_platdata_fbsub *pd = fbi->pdata->fb_pnl;
- control = readl(ctrl_reg);
+ control = smc501_readl(ctrl_reg);
if (to && (control & SM501_DC_PANEL_CONTROL_VDD) == 0) {
/* enable panel power */
control |= SM501_DC_PANEL_CONTROL_VDD; /* FPVDDEN */
- writel(control, ctrl_reg);
+ smc501_writel(control, ctrl_reg);
sm501fb_sync_regs(fbi);
mdelay(10);
control |= SM501_DC_PANEL_CONTROL_DATA; /* DATA */
- writel(control, ctrl_reg);
+ smc501_writel(control, ctrl_reg);
sm501fb_sync_regs(fbi);
mdelay(10);
@@ -719,7 +741,7 @@ static void sm501fb_panel_power(struct sm501fb_info *fbi, int to)
else
control |= SM501_DC_PANEL_CONTROL_BIAS;
- writel(control, ctrl_reg);
+ smc501_writel(control, ctrl_reg);
sm501fb_sync_regs(fbi);
mdelay(10);
}
@@ -730,7 +752,7 @@ static void sm501fb_panel_power(struct sm501fb_info *fbi, int to)
else
control |= SM501_DC_PANEL_CONTROL_FPEN;
- writel(control, ctrl_reg);
+ smc501_writel(control, ctrl_reg);
sm501fb_sync_regs(fbi);
mdelay(10);
}
@@ -742,7 +764,7 @@ static void sm501fb_panel_power(struct sm501fb_info *fbi, int to)
else
control &= ~SM501_DC_PANEL_CONTROL_FPEN;
- writel(control, ctrl_reg);
+ smc501_writel(control, ctrl_reg);
sm501fb_sync_regs(fbi);
mdelay(10);
}
@@ -753,18 +775,18 @@ static void sm501fb_panel_power(struct sm501fb_info *fbi, int to)
else
control &= ~SM501_DC_PANEL_CONTROL_BIAS;
- writel(control, ctrl_reg);
+ smc501_writel(control, ctrl_reg);
sm501fb_sync_regs(fbi);
mdelay(10);
}
control &= ~SM501_DC_PANEL_CONTROL_DATA;
- writel(control, ctrl_reg);
+ smc501_writel(control, ctrl_reg);
sm501fb_sync_regs(fbi);
mdelay(10);
control &= ~SM501_DC_PANEL_CONTROL_VDD;
- writel(control, ctrl_reg);
+ smc501_writel(control, ctrl_reg);
sm501fb_sync_regs(fbi);
mdelay(10);
}
@@ -799,7 +821,7 @@ static int sm501fb_set_par_pnl(struct fb_info *info)
/* update control register */
- control = readl(fbi->regs + SM501_DC_PANEL_CONTROL);
+ control = smc501_readl(fbi->regs + SM501_DC_PANEL_CONTROL);
control &= (SM501_DC_PANEL_CONTROL_GAMMA |
SM501_DC_PANEL_CONTROL_VDD |
SM501_DC_PANEL_CONTROL_DATA |
@@ -833,16 +855,16 @@ static int sm501fb_set_par_pnl(struct fb_info *info)
BUG();
}
- writel(0x0, fbi->regs + SM501_DC_PANEL_PANNING_CONTROL);
+ smc501_writel(0x0, fbi->regs + SM501_DC_PANEL_PANNING_CONTROL);
/* panel plane top left and bottom right location */
- writel(0x00, fbi->regs + SM501_DC_PANEL_TL_LOC);
+ smc501_writel(0x00, fbi->regs + SM501_DC_PANEL_TL_LOC);
reg = var->xres - 1;
reg |= (var->yres - 1) << 16;
- writel(reg, fbi->regs + SM501_DC_PANEL_BR_LOC);
+ smc501_writel(reg, fbi->regs + SM501_DC_PANEL_BR_LOC);
/* program panel control register */
@@ -855,7 +877,7 @@ static int sm501fb_set_par_pnl(struct fb_info *info)
if ((var->sync & FB_SYNC_VERT_HIGH_ACT) == 0)
control |= SM501_DC_PANEL_CONTROL_VSP;
- writel(control, fbi->regs + SM501_DC_PANEL_CONTROL);
+ smc501_writel(control, fbi->regs + SM501_DC_PANEL_CONTROL);
sm501fb_sync_regs(fbi);
/* ensure the panel interface is not tristated at this point */
@@ -924,7 +946,7 @@ static int sm501fb_setcolreg(unsigned regno,
val |= (green >> 8) << 8;
val |= blue >> 8;
- writel(val, base + (regno * 4));
+ smc501_writel(val, base + (regno * 4));
}
break;
@@ -980,7 +1002,7 @@ static int sm501fb_blank_crt(int blank_mode, struct fb_info *info)
dev_dbg(fbi->dev, "%s(mode=%d, %p)\n", __func__, blank_mode, info);
- ctrl = readl(fbi->regs + SM501_DC_CRT_CONTROL);
+ ctrl = smc501_readl(fbi->regs + SM501_DC_CRT_CONTROL);
switch (blank_mode) {
case FB_BLANK_POWERDOWN:
@@ -1004,7 +1026,7 @@ static int sm501fb_blank_crt(int blank_mode, struct fb_info *info)
}
- writel(ctrl, fbi->regs + SM501_DC_CRT_CONTROL);
+ smc501_writel(ctrl, fbi->regs + SM501_DC_CRT_CONTROL);
sm501fb_sync_regs(fbi);
return 0;
@@ -1041,12 +1063,14 @@ static int sm501fb_cursor(struct fb_info *info, struct fb_cursor *cursor)
if (cursor->image.depth > 1)
return -EINVAL;
- hwc_addr = readl(base + SM501_OFF_HWC_ADDR);
+ hwc_addr = smc501_readl(base + SM501_OFF_HWC_ADDR);
if (cursor->enable)
- writel(hwc_addr | SM501_HWC_EN, base + SM501_OFF_HWC_ADDR);
+ smc501_writel(hwc_addr | SM501_HWC_EN,
+ base + SM501_OFF_HWC_ADDR);
else
- writel(hwc_addr & ~SM501_HWC_EN, base + SM501_OFF_HWC_ADDR);
+ smc501_writel(hwc_addr & ~SM501_HWC_EN,
+ base + SM501_OFF_HWC_ADDR);
/* set data */
if (cursor->set & FB_CUR_SETPOS) {
@@ -1060,7 +1084,7 @@ static int sm501fb_cursor(struct fb_info *info, struct fb_cursor *cursor)
//y += cursor->image.height;
- writel(x | (y << 16), base + SM501_OFF_HWC_LOC);
+ smc501_writel(x | (y << 16), base + SM501_OFF_HWC_LOC);
}
if (cursor->set & FB_CUR_SETCMAP) {
@@ -1080,8 +1104,8 @@ static int sm501fb_cursor(struct fb_info *info, struct fb_cursor *cursor)
dev_dbg(fbi->dev, "fgcol %08lx, bgcol %08lx\n", fg, bg);
- writel(bg, base + SM501_OFF_HWC_COLOR_1_2);
- writel(fg, base + SM501_OFF_HWC_COLOR_3);
+ smc501_writel(bg, base + SM501_OFF_HWC_COLOR_1_2);
+ smc501_writel(fg, base + SM501_OFF_HWC_COLOR_3);
}
if (cursor->set & FB_CUR_SETSIZE ||
@@ -1102,7 +1126,7 @@ static int sm501fb_cursor(struct fb_info *info, struct fb_cursor *cursor)
__func__, cursor->image.width, cursor->image.height);
for (op = 0; op < (64*64*2)/8; op+=4)
- writel(0x0, dst + op);
+ smc501_writel(0x0, dst + op);
for (y = 0; y < cursor->image.height; y++) {
for (x = 0; x < cursor->image.width; x++) {
@@ -1141,7 +1165,7 @@ static ssize_t sm501fb_crtsrc_show(struct device *dev,
struct sm501fb_info *info = dev_get_drvdata(dev);
unsigned long ctrl;
- ctrl = readl(info->regs + SM501_DC_CRT_CONTROL);
+ ctrl = smc501_readl(info->regs + SM501_DC_CRT_CONTROL);
ctrl &= SM501_DC_CRT_CONTROL_SEL;
return snprintf(buf, PAGE_SIZE, "%s\n", ctrl ? "crt" : "panel");
@@ -1172,7 +1196,7 @@ static ssize_t sm501fb_crtsrc_store(struct device *dev,
dev_info(dev, "setting crt source to head %d\n", head);
- ctrl = readl(info->regs + SM501_DC_CRT_CONTROL);
+ ctrl = smc501_readl(info->regs + SM501_DC_CRT_CONTROL);
if (head == HEAD_CRT) {
ctrl |= SM501_DC_CRT_CONTROL_SEL;
@@ -1184,7 +1208,7 @@ static ssize_t sm501fb_crtsrc_store(struct device *dev,
ctrl &= ~SM501_DC_CRT_CONTROL_TE;
}
- writel(ctrl, info->regs + SM501_DC_CRT_CONTROL);
+ smc501_writel(ctrl, info->regs + SM501_DC_CRT_CONTROL);
sm501fb_sync_regs(info);
return len;
@@ -1205,7 +1229,8 @@ static int sm501fb_show_regs(struct sm501fb_info *info, char *ptr,
unsigned int reg;
for (reg = start; reg < (len + start); reg += 4)
- ptr += sprintf(ptr, "%08x = %08x\n", reg, readl(mem + reg));
+ ptr += sprintf(ptr, "%08x = %08x\n", reg,
+ smc501_readl(mem + reg));
return ptr - buf;
}
@@ -1257,7 +1282,7 @@ static int sm501fb_sync(struct fb_info *info)
/* wait for the 2d engine to be ready */
while ((count > 0) &&
- (readl(fbi->regs + SM501_SYSTEM_CONTROL) &
+ (smc501_readl(fbi->regs + SM501_SYSTEM_CONTROL) &
SM501_SYSCTRL_2D_ENGINE_STATUS) != 0)
count--;
@@ -1312,45 +1337,46 @@ static void sm501fb_copyarea(struct fb_info *info, const struct fb_copyarea *are
return;
/* set the base addresses */
- writel(par->screen.sm_addr, fbi->regs2d + SM501_2D_SOURCE_BASE);
- writel(par->screen.sm_addr, fbi->regs2d + SM501_2D_DESTINATION_BASE);
+ smc501_writel(par->screen.sm_addr, fbi->regs2d + SM501_2D_SOURCE_BASE);
+ smc501_writel(par->screen.sm_addr,
+ fbi->regs2d + SM501_2D_DESTINATION_BASE);
/* set the window width */
- writel((info->var.xres << 16) | info->var.xres,
+ smc501_writel((info->var.xres << 16) | info->var.xres,
fbi->regs2d + SM501_2D_WINDOW_WIDTH);
/* set window stride */
- writel((info->var.xres_virtual << 16) | info->var.xres_virtual,
+ smc501_writel((info->var.xres_virtual << 16) | info->var.xres_virtual,
fbi->regs2d + SM501_2D_PITCH);
/* set data format */
switch (info->var.bits_per_pixel) {
case 8:
- writel(0, fbi->regs2d + SM501_2D_STRETCH);
+ smc501_writel(0, fbi->regs2d + SM501_2D_STRETCH);
break;
case 16:
- writel(0x00100000, fbi->regs2d + SM501_2D_STRETCH);
+ smc501_writel(0x00100000, fbi->regs2d + SM501_2D_STRETCH);
break;
case 32:
- writel(0x00200000, fbi->regs2d + SM501_2D_STRETCH);
+ smc501_writel(0x00200000, fbi->regs2d + SM501_2D_STRETCH);
break;
}
/* 2d compare mask */
- writel(0xffffffff, fbi->regs2d + SM501_2D_COLOR_COMPARE_MASK);
+ smc501_writel(0xffffffff, fbi->regs2d + SM501_2D_COLOR_COMPARE_MASK);
/* 2d mask */
- writel(0xffffffff, fbi->regs2d + SM501_2D_MASK);
+ smc501_writel(0xffffffff, fbi->regs2d + SM501_2D_MASK);
/* source and destination x y */
- writel((sx << 16) | sy, fbi->regs2d + SM501_2D_SOURCE);
- writel((dx << 16) | dy, fbi->regs2d + SM501_2D_DESTINATION);
+ smc501_writel((sx << 16) | sy, fbi->regs2d + SM501_2D_SOURCE);
+ smc501_writel((dx << 16) | dy, fbi->regs2d + SM501_2D_DESTINATION);
/* w/h */
- writel((width << 16) | height, fbi->regs2d + SM501_2D_DIMENSION);
+ smc501_writel((width << 16) | height, fbi->regs2d + SM501_2D_DIMENSION);
/* do area move */
- writel(0x800000cc | rtl, fbi->regs2d + SM501_2D_CONTROL);
+ smc501_writel(0x800000cc | rtl, fbi->regs2d + SM501_2D_CONTROL);
}
static void sm501fb_fillrect(struct fb_info *info, const struct fb_fillrect *rect)
@@ -1372,47 +1398,49 @@ static void sm501fb_fillrect(struct fb_info *info, const struct fb_fillrect *rec
return;
/* set the base addresses */
- writel(par->screen.sm_addr, fbi->regs2d + SM501_2D_SOURCE_BASE);
- writel(par->screen.sm_addr, fbi->regs2d + SM501_2D_DESTINATION_BASE);
+ smc501_writel(par->screen.sm_addr, fbi->regs2d + SM501_2D_SOURCE_BASE);
+ smc501_writel(par->screen.sm_addr,
+ fbi->regs2d + SM501_2D_DESTINATION_BASE);
/* set the window width */
- writel((info->var.xres << 16) | info->var.xres,
+ smc501_writel((info->var.xres << 16) | info->var.xres,
fbi->regs2d + SM501_2D_WINDOW_WIDTH);
/* set window stride */
- writel((info->var.xres_virtual << 16) | info->var.xres_virtual,
+ smc501_writel((info->var.xres_virtual << 16) | info->var.xres_virtual,
fbi->regs2d + SM501_2D_PITCH);
/* set data format */
switch (info->var.bits_per_pixel) {
case 8:
- writel(0, fbi->regs2d + SM501_2D_STRETCH);
+ smc501_writel(0, fbi->regs2d + SM501_2D_STRETCH);
break;
case 16:
- writel(0x00100000, fbi->regs2d + SM501_2D_STRETCH);
+ smc501_writel(0x00100000, fbi->regs2d + SM501_2D_STRETCH);
break;
case 32:
- writel(0x00200000, fbi->regs2d + SM501_2D_STRETCH);
+ smc501_writel(0x00200000, fbi->regs2d + SM501_2D_STRETCH);
break;
}
/* 2d compare mask */
- writel(0xffffffff, fbi->regs2d + SM501_2D_COLOR_COMPARE_MASK);
+ smc501_writel(0xffffffff, fbi->regs2d + SM501_2D_COLOR_COMPARE_MASK);
/* 2d mask */
- writel(0xffffffff, fbi->regs2d + SM501_2D_MASK);
+ smc501_writel(0xffffffff, fbi->regs2d + SM501_2D_MASK);
/* colour */
- writel(rect->color, fbi->regs2d + SM501_2D_FOREGROUND);
+ smc501_writel(rect->color, fbi->regs2d + SM501_2D_FOREGROUND);
/* x y */
- writel((rect->dx << 16) | rect->dy, fbi->regs2d + SM501_2D_DESTINATION);
+ smc501_writel((rect->dx << 16) | rect->dy,
+ fbi->regs2d + SM501_2D_DESTINATION);
/* w/h */
- writel((width << 16) | height, fbi->regs2d + SM501_2D_DIMENSION);
+ smc501_writel((width << 16) | height, fbi->regs2d + SM501_2D_DIMENSION);
/* do rectangle fill */
- writel(0x800100cc, fbi->regs2d + SM501_2D_CONTROL);
+ smc501_writel(0x800100cc, fbi->regs2d + SM501_2D_CONTROL);
}
@@ -1470,11 +1498,12 @@ static int sm501_init_cursor(struct fb_info *fbi, unsigned int reg_base)
/* initialise the colour registers */
- writel(par->cursor.sm_addr, par->cursor_regs + SM501_OFF_HWC_ADDR);
+ smc501_writel(par->cursor.sm_addr,
+ par->cursor_regs + SM501_OFF_HWC_ADDR);
- writel(0x00, par->cursor_regs + SM501_OFF_HWC_LOC);
- writel(0x00, par->cursor_regs + SM501_OFF_HWC_COLOR_1_2);
- writel(0x00, par->cursor_regs + SM501_OFF_HWC_COLOR_3);
+ smc501_writel(0x00, par->cursor_regs + SM501_OFF_HWC_LOC);
+ smc501_writel(0x00, par->cursor_regs + SM501_OFF_HWC_COLOR_1_2);
+ smc501_writel(0x00, par->cursor_regs + SM501_OFF_HWC_COLOR_3);
sm501fb_sync_regs(info);
return 0;
@@ -1581,7 +1610,7 @@ static int sm501fb_start(struct sm501fb_info *info,
/* clear palette ram - undefined at power on */
for (k = 0; k < (256 * 3); k++)
- writel(0, info->regs + SM501_DC_PANEL_PALETTE + (k * 4));
+ smc501_writel(0, info->regs + SM501_DC_PANEL_PALETTE + (k * 4));
/* enable display controller */
sm501_unit_power(dev->parent, SM501_GATE_DISPLAY, 1);
@@ -1649,20 +1678,20 @@ static int sm501fb_init_fb(struct fb_info *fb,
switch (head) {
case HEAD_CRT:
pd = info->pdata->fb_crt;
- ctrl = readl(info->regs + SM501_DC_CRT_CONTROL);
+ ctrl = smc501_readl(info->regs + SM501_DC_CRT_CONTROL);
enable = (ctrl & SM501_DC_CRT_CONTROL_ENABLE) ? 1 : 0;
/* ensure we set the correct source register */
if (info->pdata->fb_route != SM501_FB_CRT_PANEL) {
ctrl |= SM501_DC_CRT_CONTROL_SEL;
- writel(ctrl, info->regs + SM501_DC_CRT_CONTROL);
+ smc501_writel(ctrl, info->regs + SM501_DC_CRT_CONTROL);
}
break;
case HEAD_PANEL:
pd = info->pdata->fb_pnl;
- ctrl = readl(info->regs + SM501_DC_PANEL_CONTROL);
+ ctrl = smc501_readl(info->regs + SM501_DC_PANEL_CONTROL);
enable = (ctrl & SM501_DC_PANEL_CONTROL_EN) ? 1 : 0;
break;
@@ -1680,7 +1709,7 @@ static int sm501fb_init_fb(struct fb_info *fb,
if (head == HEAD_CRT && info->pdata->fb_route == SM501_FB_CRT_PANEL) {
ctrl &= ~SM501_DC_CRT_CONTROL_SEL;
- writel(ctrl, info->regs + SM501_DC_CRT_CONTROL);
+ smc501_writel(ctrl, info->regs + SM501_DC_CRT_CONTROL);
enable = 0;
}
@@ -1698,6 +1727,9 @@ static int sm501fb_init_fb(struct fb_info *fb,
fb->fbops = &par->ops;
fb->flags = FBINFO_FLAG_DEFAULT | FBINFO_READS_FAST |
FBINFO_HWACCEL_COPYAREA | FBINFO_HWACCEL_FILLRECT |
+#if defined(CONFIG_PPC_MPC52xx)
+ FBINFO_FOREIGN_ENDIAN |
+#endif
FBINFO_HWACCEL_XPAN | FBINFO_HWACCEL_YPAN;
/* fixed data */
@@ -1717,9 +1749,16 @@ static int sm501fb_init_fb(struct fb_info *fb,
fb->var.vmode = FB_VMODE_NONINTERLACED;
fb->var.bits_per_pixel = 16;
+ if (info->edid_data) {
+ /* Now build modedb from EDID */
+ fb_edid_to_monspecs(info->edid_data, &fb->monspecs);
+ fb_videomode_to_modelist(fb->monspecs.modedb,
+ fb->monspecs.modedb_len,
+ &fb->modelist);
+ }
+
if (enable && (pd->flags & SM501FB_FLAG_USE_INIT_MODE) && 0) {
/* TODO read the mode from the current display */
-
} else {
if (pd->def_mode) {
dev_info(info->dev, "using supplied mode\n");
@@ -1729,12 +1768,34 @@ static int sm501fb_init_fb(struct fb_info *fb,
fb->var.xres_virtual = fb->var.xres;
fb->var.yres_virtual = fb->var.yres;
} else {
- ret = fb_find_mode(&fb->var, fb,
+ if (info->edid_data)
+ ret = fb_find_mode(&fb->var, fb, fb_mode,
+ fb->monspecs.modedb,
+ fb->monspecs.modedb_len,
+ &sm501_default_mode, default_bpp);
+ else
+ ret = fb_find_mode(&fb->var, fb,
NULL, NULL, 0, NULL, 8);
- if (ret == 0 || ret == 4) {
- dev_err(info->dev,
- "failed to get initial mode\n");
+ switch (ret) {
+ case 1:
+ dev_info(info->dev, "using mode specified in "
+ "@mode\n");
+ break;
+ case 2:
+ dev_info(info->dev, "using mode specified in "
+ "@mode with ignored refresh rate\n");
+ break;
+ case 3:
+ dev_info(info->dev, "using mode default "
+ "mode\n");
+ break;
+ case 4:
+ dev_info(info->dev, "using mode from list\n");
+ break;
+ default:
+ dev_info(info->dev, "ret = %d\n", ret);
+ dev_info(info->dev, "failed to find mode\n");
return -EINVAL;
}
}
@@ -1819,6 +1880,7 @@ static void sm501_free_init_fb(struct sm501fb_info *info,
{
struct fb_info *fbi = info->fb[head];
+ kfree(info->edid_data);
fb_dealloc_cmap(&fbi->cmap);
}
@@ -1875,8 +1937,29 @@ static int __devinit sm501fb_probe(struct platform_device *pdev)
}
if (info->pdata == NULL) {
- dev_info(dev, "using default configuration data\n");
+ int found = 0;
+#if defined(CONFIG_OF)
+ struct device_node *np = pdev->dev.parent->of_node;
+ const u8 *prop;
+ const char *cp;
+ int len;
+
info->pdata = &sm501fb_def_pdata;
+ if (np) {
+ /* Get EDID */
+ cp = of_get_property(np, "mode", &len);
+ if (cp)
+ strcpy(fb_mode, cp);
+ prop = of_get_property(np, "edid", &len);
+ if (prop && len == EDID_LENGTH) {
+ info->edid_data = kmemdup(prop, EDID_LENGTH,
+ GFP_KERNEL);
+ found = 1;
+ }
+ }
+#endif
+ if (!found)
+ dev_info(dev, "using default configuration data\n");
}
/* probe for the presence of each panel */
@@ -2085,7 +2168,7 @@ static int sm501fb_suspend(struct platform_device *pdev, pm_message_t state)
struct sm501fb_info *info = platform_get_drvdata(pdev);
/* store crt control to resume with */
- info->pm_crt_ctrl = readl(info->regs + SM501_DC_CRT_CONTROL);
+ info->pm_crt_ctrl = smc501_readl(info->regs + SM501_DC_CRT_CONTROL);
sm501fb_suspend_fb(info, HEAD_CRT);
sm501fb_suspend_fb(info, HEAD_PANEL);
@@ -2109,10 +2192,10 @@ static int sm501fb_resume(struct platform_device *pdev)
/* restore the items we want to be saved for crt control */
- crt_ctrl = readl(info->regs + SM501_DC_CRT_CONTROL);
+ crt_ctrl = smc501_readl(info->regs + SM501_DC_CRT_CONTROL);
crt_ctrl &= ~SM501_CRT_CTRL_SAVE;
crt_ctrl |= info->pm_crt_ctrl & SM501_CRT_CTRL_SAVE;
- writel(crt_ctrl, info->regs + SM501_DC_CRT_CONTROL);
+ smc501_writel(crt_ctrl, info->regs + SM501_DC_CRT_CONTROL);
sm501fb_resume_fb(info, HEAD_CRT);
sm501fb_resume_fb(info, HEAD_PANEL);
@@ -2149,6 +2232,11 @@ static void __exit sm501fb_cleanup(void)
module_init(sm501fb_init);
module_exit(sm501fb_cleanup);
+module_param_named(mode, fb_mode, charp, 0);
+MODULE_PARM_DESC(mode,
+ "Specify resolution as \"<xres>x<yres>[-<bpp>][@<refresh>]\" ");
+module_param_named(bpp, default_bpp, ulong, 0);
+MODULE_PARM_DESC(bpp, "Specify bit-per-pixel if not specified mode");
MODULE_AUTHOR("Ben Dooks, Vincent Sanders");
MODULE_DESCRIPTION("SM501 Framebuffer driver");
MODULE_LICENSE("GPL v2");
diff --git a/include/linux/sm501.h b/include/linux/sm501.h
index 214f932..090a07b 100644
--- a/include/linux/sm501.h
+++ b/include/linux/sm501.h
@@ -172,3 +172,11 @@ struct sm501_platdata {
struct sm501_platdata_gpio_i2c *gpio_i2c;
unsigned int gpio_i2c_nr;
};
+
+#if defined(CONFIG_PPC_MPC52xx)
+#define smc501_readl(addr) __do_readl_be((addr))
+#define smc501_writel(val, addr) __do_writel_be((val), (addr))
+#else
+#define smc501_readl(addr) readl(addr)
+#define smc501_writel(val, addr) writel(val, addr)
+#endif
--
1.7.2.3
^ permalink raw reply related
* [PATCH v3 2/2] powerpc, video: add SM501 support for charon board.
From: Heiko Schocher @ 2010-12-11 6:31 UTC (permalink / raw)
To: linuxppc-dev
Cc: linux-fbdev, devicetree-discuss, Samuel Ortiz, Vincent Sanders,
linux-kernel, Ben Dooks, Heiko Schocher
In-Reply-To: <1291451028-22532-2-git-send-email-hs@denx.de>
Signed-off-by: Heiko Schocher <hs@denx.de>
cc: linux-fbdev@vger.kernel.org
cc: devicetree-discuss@ozlabs.org
cc: Ben Dooks <ben@simtec.co.uk>
cc: Vincent Sanders <vince@simtec.co.uk>
cc: Samuel Ortiz <sameo@linux.intel.com>
cc: linux-kernel@vger.kernel.org
---
- based against 2.6.37-rc4
./scripts/checkpatch.pl 0003-powerpc-video-add-SM501-support-for-charon-board.patch
total: 0 errors, 0 warnings, 21 lines checked
0003-powerpc-video-add-SM501-support-for-charon-board.patch has no obvious style problems and is ready for submission.
- changes since v1:
- no board specific defconfig file for mpc52xx based boards as suggested
from Wolfram Sang
- changes since v2:
add Ben Dooks, Vincent Sanders and Samuel Ortiz and lkml to cc, as
suggested from Paul Mundt.
arch/powerpc/boot/dts/charon.dts | 9 +++++++++
1 files changed, 9 insertions(+), 0 deletions(-)
diff --git a/arch/powerpc/boot/dts/charon.dts b/arch/powerpc/boot/dts/charon.dts
index 9776889..d9af022 100644
--- a/arch/powerpc/boot/dts/charon.dts
+++ b/arch/powerpc/boot/dts/charon.dts
@@ -186,6 +186,7 @@
#address-cells = <2>;
#size-cells = <1>;
ranges = < 0 0 0xfc000000 0x02000000
+ 1 0 0xe0000000 0x04000000 // CS1 range, SM501
3 0 0xe8000000 0x00080000>;
flash@0,0 {
@@ -197,6 +198,14 @@
#address-cells = <1>;
};
+ display@1,0 {
+ compatible = "smi,sm501";
+ reg = <1 0x00000000 0x00800000
+ 1 0x03e00000 0x00200000>;
+ mode = "640x480-32@60";
+ interrupts = <1 1 3>;
+ };
+
mram0@3,0 {
compatible = "mtd-ram";
reg = <3 0x00000 0x80000>;
--
1.7.2.3
^ permalink raw reply related
page: next (older) | prev (newer) | latest
- recent:[subjects (threaded)|topics (new)|topics (active)]
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox