Skip to content

Commit

Permalink
EDAC, fsl_ddr: Add support for little endian
Browse files Browse the repository at this point in the history
Get endianness from device tree. Both big endian and little endian are
supported. Default to big endian for backwards compatibility to MPC85xx.

Signed-off-by: York Sun <[email protected]>
Acked-by: Rob Herring <[email protected]>
Cc: [email protected]
Cc: linux-edac <[email protected]>
Cc: [email protected]
Cc: [email protected]
Cc: [email protected]
Link: http://lkml.kernel.org/r/[email protected]
Signed-off-by: Borislav Petkov <[email protected]>
  • Loading branch information
York Sun authored and suryasaimadhu committed Sep 1, 2016
1 parent 4e2c325 commit 339fdff
Show file tree
Hide file tree
Showing 2 changed files with 58 additions and 40 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@ Properties:
"fsl,qoriq-memory-controller".
- reg : Address and size of DDR controller registers
- interrupts : Error interrupt of DDR controller
- little-endian : Specifies little-endian access to registers
If omitted, big-endian will be used.

Example 1:

Expand Down
96 changes: 56 additions & 40 deletions drivers/edac/fsl_ddr_edac.c
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,6 @@
* the terms of the GNU General Public License version 2. This program
* is licensed "as is" without any warranty of any kind, whether express
* or implied.
*
*/
#include <linux/module.h>
#include <linux/init.h>
Expand All @@ -37,6 +36,20 @@ static int edac_mc_idx;

static u32 orig_ddr_err_disable;
static u32 orig_ddr_err_sbe;
static bool little_endian;

static inline u32 ddr_in32(void __iomem *addr)
{
return little_endian ? ioread32(addr) : ioread32be(addr);
}

static inline void ddr_out32(void __iomem *addr, u32 value)
{
if (little_endian)
iowrite32(value, addr);
else
iowrite32be(value, addr);
}

/************************ MC SYSFS parts ***********************************/

Expand All @@ -49,8 +62,7 @@ static ssize_t fsl_mc_inject_data_hi_show(struct device *dev,
struct mem_ctl_info *mci = to_mci(dev);
struct fsl_mc_pdata *pdata = mci->pvt_info;
return sprintf(data, "0x%08x",
in_be32(pdata->mc_vbase +
FSL_MC_DATA_ERR_INJECT_HI));
ddr_in32(pdata->mc_vbase + FSL_MC_DATA_ERR_INJECT_HI));
}

static ssize_t fsl_mc_inject_data_lo_show(struct device *dev,
Expand All @@ -60,8 +72,7 @@ static ssize_t fsl_mc_inject_data_lo_show(struct device *dev,
struct mem_ctl_info *mci = to_mci(dev);
struct fsl_mc_pdata *pdata = mci->pvt_info;
return sprintf(data, "0x%08x",
in_be32(pdata->mc_vbase +
FSL_MC_DATA_ERR_INJECT_LO));
ddr_in32(pdata->mc_vbase + FSL_MC_DATA_ERR_INJECT_LO));
}

static ssize_t fsl_mc_inject_ctrl_show(struct device *dev,
Expand All @@ -71,7 +82,7 @@ static ssize_t fsl_mc_inject_ctrl_show(struct device *dev,
struct mem_ctl_info *mci = to_mci(dev);
struct fsl_mc_pdata *pdata = mci->pvt_info;
return sprintf(data, "0x%08x",
in_be32(pdata->mc_vbase + FSL_MC_ECC_ERR_INJECT));
ddr_in32(pdata->mc_vbase + FSL_MC_ECC_ERR_INJECT));
}

static ssize_t fsl_mc_inject_data_hi_store(struct device *dev,
Expand All @@ -81,8 +92,8 @@ static ssize_t fsl_mc_inject_data_hi_store(struct device *dev,
struct mem_ctl_info *mci = to_mci(dev);
struct fsl_mc_pdata *pdata = mci->pvt_info;
if (isdigit(*data)) {
out_be32(pdata->mc_vbase + FSL_MC_DATA_ERR_INJECT_HI,
simple_strtoul(data, NULL, 0));
ddr_out32(pdata->mc_vbase + FSL_MC_DATA_ERR_INJECT_HI,
simple_strtoul(data, NULL, 0));
return count;
}
return 0;
Expand All @@ -95,8 +106,8 @@ static ssize_t fsl_mc_inject_data_lo_store(struct device *dev,
struct mem_ctl_info *mci = to_mci(dev);
struct fsl_mc_pdata *pdata = mci->pvt_info;
if (isdigit(*data)) {
out_be32(pdata->mc_vbase + FSL_MC_DATA_ERR_INJECT_LO,
simple_strtoul(data, NULL, 0));
ddr_out32(pdata->mc_vbase + FSL_MC_DATA_ERR_INJECT_LO,
simple_strtoul(data, NULL, 0));
return count;
}
return 0;
Expand All @@ -109,8 +120,8 @@ static ssize_t fsl_mc_inject_ctrl_store(struct device *dev,
struct mem_ctl_info *mci = to_mci(dev);
struct fsl_mc_pdata *pdata = mci->pvt_info;
if (isdigit(*data)) {
out_be32(pdata->mc_vbase + FSL_MC_ECC_ERR_INJECT,
simple_strtoul(data, NULL, 0));
ddr_out32(pdata->mc_vbase + FSL_MC_ECC_ERR_INJECT,
simple_strtoul(data, NULL, 0));
return count;
}
return 0;
Expand Down Expand Up @@ -256,7 +267,7 @@ static void fsl_mc_check(struct mem_ctl_info *mci)
int bad_data_bit;
int bad_ecc_bit;

err_detect = in_be32(pdata->mc_vbase + FSL_MC_ERR_DETECT);
err_detect = ddr_in32(pdata->mc_vbase + FSL_MC_ERR_DETECT);
if (!err_detect)
return;

Expand All @@ -265,23 +276,23 @@ static void fsl_mc_check(struct mem_ctl_info *mci)

/* no more processing if not ECC bit errors */
if (!(err_detect & (DDR_EDE_SBE | DDR_EDE_MBE))) {
out_be32(pdata->mc_vbase + FSL_MC_ERR_DETECT, err_detect);
ddr_out32(pdata->mc_vbase + FSL_MC_ERR_DETECT, err_detect);
return;
}

syndrome = in_be32(pdata->mc_vbase + FSL_MC_CAPTURE_ECC);
syndrome = ddr_in32(pdata->mc_vbase + FSL_MC_CAPTURE_ECC);

/* Mask off appropriate bits of syndrome based on bus width */
bus_width = (in_be32(pdata->mc_vbase + FSL_MC_DDR_SDRAM_CFG) &
DSC_DBW_MASK) ? 32 : 64;
bus_width = (ddr_in32(pdata->mc_vbase + FSL_MC_DDR_SDRAM_CFG) &
DSC_DBW_MASK) ? 32 : 64;
if (bus_width == 64)
syndrome &= 0xff;
else
syndrome &= 0xffff;

err_addr = make64(
in_be32(pdata->mc_vbase + FSL_MC_CAPTURE_EXT_ADDRESS),
in_be32(pdata->mc_vbase + FSL_MC_CAPTURE_ADDRESS));
ddr_in32(pdata->mc_vbase + FSL_MC_CAPTURE_EXT_ADDRESS),
ddr_in32(pdata->mc_vbase + FSL_MC_CAPTURE_ADDRESS));
pfn = err_addr >> PAGE_SHIFT;

for (row_index = 0; row_index < mci->nr_csrows; row_index++) {
Expand All @@ -290,8 +301,8 @@ static void fsl_mc_check(struct mem_ctl_info *mci)
break;
}

cap_high = in_be32(pdata->mc_vbase + FSL_MC_CAPTURE_DATA_HI);
cap_low = in_be32(pdata->mc_vbase + FSL_MC_CAPTURE_DATA_LO);
cap_high = ddr_in32(pdata->mc_vbase + FSL_MC_CAPTURE_DATA_HI);
cap_low = ddr_in32(pdata->mc_vbase + FSL_MC_CAPTURE_DATA_LO);

/*
* Analyze single-bit errors on 64-bit wide buses
Expand Down Expand Up @@ -337,7 +348,7 @@ static void fsl_mc_check(struct mem_ctl_info *mci)
row_index, 0, -1,
mci->ctl_name, "");

out_be32(pdata->mc_vbase + FSL_MC_ERR_DETECT, err_detect);
ddr_out32(pdata->mc_vbase + FSL_MC_ERR_DETECT, err_detect);
}

static irqreturn_t fsl_mc_isr(int irq, void *dev_id)
Expand All @@ -346,7 +357,7 @@ static irqreturn_t fsl_mc_isr(int irq, void *dev_id)
struct fsl_mc_pdata *pdata = mci->pvt_info;
u32 err_detect;

err_detect = in_be32(pdata->mc_vbase + FSL_MC_ERR_DETECT);
err_detect = ddr_in32(pdata->mc_vbase + FSL_MC_ERR_DETECT);
if (!err_detect)
return IRQ_NONE;

Expand All @@ -366,7 +377,7 @@ static void fsl_ddr_init_csrows(struct mem_ctl_info *mci)
u32 cs_bnds;
int index;

sdram_ctl = in_be32(pdata->mc_vbase + FSL_MC_DDR_SDRAM_CFG);
sdram_ctl = ddr_in32(pdata->mc_vbase + FSL_MC_DDR_SDRAM_CFG);

sdtype = sdram_ctl & DSC_SDTYPE_MASK;
if (sdram_ctl & DSC_RD_EN) {
Expand Down Expand Up @@ -414,8 +425,8 @@ static void fsl_ddr_init_csrows(struct mem_ctl_info *mci)
csrow = mci->csrows[index];
dimm = csrow->channels[0]->dimm;

cs_bnds = in_be32(pdata->mc_vbase + FSL_MC_CS_BNDS_0 +
(index * FSL_MC_CS_BNDS_OFS));
cs_bnds = ddr_in32(pdata->mc_vbase + FSL_MC_CS_BNDS_0 +
(index * FSL_MC_CS_BNDS_OFS));

start = (cs_bnds & 0xffff0000) >> 16;
end = (cs_bnds & 0x0000ffff);
Expand Down Expand Up @@ -474,6 +485,12 @@ int fsl_mc_err_probe(struct platform_device *op)
mci->ctl_name = pdata->name;
mci->dev_name = pdata->name;

/*
* Get the endianness of DDR controller registers.
* Default is big endian.
*/
little_endian = of_property_read_bool(op->dev.of_node, "little-endian");

res = of_address_to_resource(op->dev.of_node, 0, &r);
if (res) {
pr_err("%s: Unable to get resource for MC err regs\n",
Expand All @@ -496,7 +513,7 @@ int fsl_mc_err_probe(struct platform_device *op)
goto err;
}

sdram_ctl = in_be32(pdata->mc_vbase + FSL_MC_DDR_SDRAM_CFG);
sdram_ctl = ddr_in32(pdata->mc_vbase + FSL_MC_DDR_SDRAM_CFG);
if (!(sdram_ctl & DSC_ECC_EN)) {
/* no ECC */
pr_warn("%s: No ECC DIMMs discovered\n", __func__);
Expand All @@ -523,28 +540,27 @@ int fsl_mc_err_probe(struct platform_device *op)
fsl_ddr_init_csrows(mci);

/* store the original error disable bits */
orig_ddr_err_disable =
in_be32(pdata->mc_vbase + FSL_MC_ERR_DISABLE);
out_be32(pdata->mc_vbase + FSL_MC_ERR_DISABLE, 0);
orig_ddr_err_disable = ddr_in32(pdata->mc_vbase + FSL_MC_ERR_DISABLE);
ddr_out32(pdata->mc_vbase + FSL_MC_ERR_DISABLE, 0);

/* clear all error bits */
out_be32(pdata->mc_vbase + FSL_MC_ERR_DETECT, ~0);
ddr_out32(pdata->mc_vbase + FSL_MC_ERR_DETECT, ~0);

if (edac_mc_add_mc_with_groups(mci, fsl_ddr_dev_groups)) {
edac_dbg(3, "failed edac_mc_add_mc()\n");
goto err;
}

if (edac_op_state == EDAC_OPSTATE_INT) {
out_be32(pdata->mc_vbase + FSL_MC_ERR_INT_EN,
DDR_EIE_MBEE | DDR_EIE_SBEE);
ddr_out32(pdata->mc_vbase + FSL_MC_ERR_INT_EN,
DDR_EIE_MBEE | DDR_EIE_SBEE);

/* store the original error management threshold */
orig_ddr_err_sbe = in_be32(pdata->mc_vbase +
FSL_MC_ERR_SBE) & 0xff0000;
orig_ddr_err_sbe = ddr_in32(pdata->mc_vbase +
FSL_MC_ERR_SBE) & 0xff0000;

/* set threshold to 1 error per interrupt */
out_be32(pdata->mc_vbase + FSL_MC_ERR_SBE, 0x10000);
ddr_out32(pdata->mc_vbase + FSL_MC_ERR_SBE, 0x10000);

/* register interrupts */
pdata->irq = irq_of_parse_and_map(op->dev.of_node, 0);
Expand Down Expand Up @@ -586,13 +602,13 @@ int fsl_mc_err_remove(struct platform_device *op)
edac_dbg(0, "\n");

if (edac_op_state == EDAC_OPSTATE_INT) {
out_be32(pdata->mc_vbase + FSL_MC_ERR_INT_EN, 0);
irq_dispose_mapping(pdata->irq);
ddr_out32(pdata->mc_vbase + FSL_MC_ERR_INT_EN, 0);
}

out_be32(pdata->mc_vbase + FSL_MC_ERR_DISABLE,
orig_ddr_err_disable);
out_be32(pdata->mc_vbase + FSL_MC_ERR_SBE, orig_ddr_err_sbe);
ddr_out32(pdata->mc_vbase + FSL_MC_ERR_DISABLE,
orig_ddr_err_disable);
ddr_out32(pdata->mc_vbase + FSL_MC_ERR_SBE, orig_ddr_err_sbe);

edac_mc_del_mc(&op->dev);
edac_mc_free(mci);
Expand Down

0 comments on commit 339fdff

Please sign in to comment.