Re: [RFC PATCH 1/4] EDAC: mvebu: Add driver for Marvell Armada SoCs

2017-06-22 Thread Chris Packham
On 23/06/17 02:37, Jan Lübbe wrote:
> Hi Chris,
> 
> On Fr, 2017-06-09 at 15:14 +0200, Jan Lübbe wrote:
>>> +static void mvebu_init_csrows(struct mem_ctl_info *mci,
>>> + struct mvebu_mc_pdata *pdata)
>> [...]
>>> + devtype = (ctl >> 20) & 0x3;
>>> + switch (devtype) {
>>> + case 0x0:
>>> + dimm->dtype = DEV_X32;
>>> + break;
>>> + case 0x2:   /* could be X8 too, but no way to tell
>> */
>>> + dimm->dtype = DEV_X16;
>>> + break;
>>> + case 0x3:
>>> + dimm->dtype = DEV_X4;
>>> + break;
>>> + default:
>>> + dimm->dtype = DEV_UNKNOWN;
>>> + break;
>>> + }
>> This register is documented as reserved? I pulled the X8/X16
>> information from the Address Control Register (CSnStruct bits).
> 
> Do you have more information on how to decode the Bus width?
> 
> It's not clear from the MV78230/78x60 docs:
> - The SDRAM Configuration Register, offset 15 bit is:
>0 = Half (32 bit data bus)
>1 = Full (64 bit data bus)

For the integrated version it still is just half and full but half == 16 
and full == 32.

> - The SDRAM Address Control Register, offsets 0-1, 4-5, 8-9 and 12-13
> (for CS 0, 1, 3 and 4):
>0 = X8
>1 = X16
>2 and 3 are not documented

This definition is the same for the integrated version.

> Is this clearer in your documentation, so that we can have the same code
> handle both variants? Otherwise, we'd probably need separate DT
> compatibles.

I think we do need to use the compatible string to decide how to 
interpret full/half.




Re: [RFC PATCH 1/4] EDAC: mvebu: Add driver for Marvell Armada SoCs

2017-06-22 Thread Chris Packham
On 23/06/17 02:37, Jan Lübbe wrote:
> Hi Chris,
> 
> On Fr, 2017-06-09 at 15:14 +0200, Jan Lübbe wrote:
>>> +static void mvebu_init_csrows(struct mem_ctl_info *mci,
>>> + struct mvebu_mc_pdata *pdata)
>> [...]
>>> + devtype = (ctl >> 20) & 0x3;
>>> + switch (devtype) {
>>> + case 0x0:
>>> + dimm->dtype = DEV_X32;
>>> + break;
>>> + case 0x2:   /* could be X8 too, but no way to tell
>> */
>>> + dimm->dtype = DEV_X16;
>>> + break;
>>> + case 0x3:
>>> + dimm->dtype = DEV_X4;
>>> + break;
>>> + default:
>>> + dimm->dtype = DEV_UNKNOWN;
>>> + break;
>>> + }
>> This register is documented as reserved? I pulled the X8/X16
>> information from the Address Control Register (CSnStruct bits).
> 
> Do you have more information on how to decode the Bus width?
> 
> It's not clear from the MV78230/78x60 docs:
> - The SDRAM Configuration Register, offset 15 bit is:
>0 = Half (32 bit data bus)
>1 = Full (64 bit data bus)

For the integrated version it still is just half and full but half == 16 
and full == 32.

> - The SDRAM Address Control Register, offsets 0-1, 4-5, 8-9 and 12-13
> (for CS 0, 1, 3 and 4):
>0 = X8
>1 = X16
>2 and 3 are not documented

This definition is the same for the integrated version.

> Is this clearer in your documentation, so that we can have the same code
> handle both variants? Otherwise, we'd probably need separate DT
> compatibles.

I think we do need to use the compatible string to decide how to 
interpret full/half.




Re: [RFC PATCH 1/4] EDAC: mvebu: Add driver for Marvell Armada SoCs

2017-06-22 Thread Jan Lübbe
Hi Chris,

On Fr, 2017-06-09 at 15:14 +0200, Jan Lübbe wrote:
> > +static void mvebu_init_csrows(struct mem_ctl_info *mci,
> > + struct mvebu_mc_pdata *pdata)
> [...]
> > + devtype = (ctl >> 20) & 0x3;
> > + switch (devtype) {
> > + case 0x0:
> > + dimm->dtype = DEV_X32;
> > + break;
> > + case 0x2:   /* could be X8 too, but no way to tell
> */
> > + dimm->dtype = DEV_X16;
> > + break;
> > + case 0x3:
> > + dimm->dtype = DEV_X4;
> > + break;
> > + default:
> > + dimm->dtype = DEV_UNKNOWN;
> > + break;
> > + }
> This register is documented as reserved? I pulled the X8/X16
> information from the Address Control Register (CSnStruct bits).

Do you have more information on how to decode the Bus width?

It's not clear from the MV78230/78x60 docs:
- The SDRAM Configuration Register, offset 15 bit is:
  0 = Half (32 bit data bus)
  1 = Full (64 bit data bus)
- The SDRAM Address Control Register, offsets 0-1, 4-5, 8-9 and 12-13
(for CS 0, 1, 3 and 4):
  0 = X8
  1 = X16
  2 and 3 are not documented

Is this clearer in your documentation, so that we can have the same code
handle both variants? Otherwise, we'd probably need separate DT
compatibles.

Regards,
Jan
-- 
Pengutronix e.K.   | |
Industrial Linux Solutions | http://www.pengutronix.de/  |
Peiner Str. 6-8, 31137 Hildesheim, Germany | Phone: +49-5121-206917-0|
Amtsgericht Hildesheim, HRA 2686   | Fax:   +49-5121-206917- |



Re: [RFC PATCH 1/4] EDAC: mvebu: Add driver for Marvell Armada SoCs

2017-06-22 Thread Jan Lübbe
Hi Chris,

On Fr, 2017-06-09 at 15:14 +0200, Jan Lübbe wrote:
> > +static void mvebu_init_csrows(struct mem_ctl_info *mci,
> > + struct mvebu_mc_pdata *pdata)
> [...]
> > + devtype = (ctl >> 20) & 0x3;
> > + switch (devtype) {
> > + case 0x0:
> > + dimm->dtype = DEV_X32;
> > + break;
> > + case 0x2:   /* could be X8 too, but no way to tell
> */
> > + dimm->dtype = DEV_X16;
> > + break;
> > + case 0x3:
> > + dimm->dtype = DEV_X4;
> > + break;
> > + default:
> > + dimm->dtype = DEV_UNKNOWN;
> > + break;
> > + }
> This register is documented as reserved? I pulled the X8/X16
> information from the Address Control Register (CSnStruct bits).

Do you have more information on how to decode the Bus width?

It's not clear from the MV78230/78x60 docs:
- The SDRAM Configuration Register, offset 15 bit is:
  0 = Half (32 bit data bus)
  1 = Full (64 bit data bus)
- The SDRAM Address Control Register, offsets 0-1, 4-5, 8-9 and 12-13
(for CS 0, 1, 3 and 4):
  0 = X8
  1 = X16
  2 and 3 are not documented

Is this clearer in your documentation, so that we can have the same code
handle both variants? Otherwise, we'd probably need separate DT
compatibles.

Regards,
Jan
-- 
Pengutronix e.K.   | |
Industrial Linux Solutions | http://www.pengutronix.de/  |
Peiner Str. 6-8, 31137 Hildesheim, Germany | Phone: +49-5121-206917-0|
Amtsgericht Hildesheim, HRA 2686   | Fax:   +49-5121-206917- |



Re: [RFC PATCH 1/4] EDAC: mvebu: Add driver for Marvell Armada SoCs

2017-06-11 Thread Chris Packham
On 10/06/17 01:19, Jan Lübbe wrote:
>> +
>> +if (edac_op_state == EDAC_OPSTATE_INT) {
>> +/* acquire interrupt that reports errors */
>> +pdata->irq = platform_get_irq(pdev, 0);
>> +res = devm_request_irq(>dev,
>> +   pdata->irq,
>> +   mvebu_mc_isr,
>> +   0,
>> +   "[EDAC] MC err",
>> +   mci);
> Which IRQ do you use? The current DT doesn't configure interrupts. Also
> it seems that the events are passed through additional layers of
> mask/status registers which are not yet represented in the Armada-XP IRQ
> hierarchy. So my driver currently uses polling.

Yes I'd been forcing polling too. To get this working properly I think 
we'd need to add another irqchip driver for the SoC Err interrupts. 
Which is kind of where I'd got stuck, the datasheet is a little 
confusing in that area. I think I'd figured out that the root interrupt 
comes through INT 4 which could be cascaded to the as yet unwritten SoC 
Err irqchip.



Re: [RFC PATCH 1/4] EDAC: mvebu: Add driver for Marvell Armada SoCs

2017-06-11 Thread Chris Packham
On 10/06/17 01:19, Jan Lübbe wrote:
>> +
>> +if (edac_op_state == EDAC_OPSTATE_INT) {
>> +/* acquire interrupt that reports errors */
>> +pdata->irq = platform_get_irq(pdev, 0);
>> +res = devm_request_irq(>dev,
>> +   pdata->irq,
>> +   mvebu_mc_isr,
>> +   0,
>> +   "[EDAC] MC err",
>> +   mci);
> Which IRQ do you use? The current DT doesn't configure interrupts. Also
> it seems that the events are passed through additional layers of
> mask/status registers which are not yet represented in the Armada-XP IRQ
> hierarchy. So my driver currently uses polling.

Yes I'd been forcing polling too. To get this working properly I think 
we'd need to add another irqchip driver for the SoC Err interrupts. 
Which is kind of where I'd got stuck, the datasheet is a little 
confusing in that area. I think I'd figured out that the root interrupt 
comes through INT 4 which could be cascaded to the as yet unwritten SoC 
Err irqchip.



Re: [RFC PATCH 1/4] EDAC: mvebu: Add driver for Marvell Armada SoCs

2017-06-09 Thread Jan Lübbe
Hi Chris!

On Do, 2017-06-08 at 16:11 +1200, Chris Packham wrote:
> This adds an EDAC driver for the memory controller and L2 cache used on
> a number of Marvell Armada SoCs.
Why have two separate drivers in the same file and enabled with the same
Kconfig option?

[...]
> +static void mvebu_mc_check(struct mem_ctl_info *mci)
> +{
> + struct mvebu_mc_pdata *pdata = mci->pvt_info;
> + u32 reg;
> + u32 err_addr;
> + u32 sdram_ecc;
> + u32 comp_ecc;
> + u32 syndrome;
> +
> + reg = readl(pdata->mc_vbase + MVEBU_SDRAM_ERR_ADDR);
> + if (!reg)
> + return;
> +
> + err_addr = reg & ~0x3;
The ERR_ADDR register is not a physical address but contains multiple
fields (bank, col, chip select and error type).

> + sdram_ecc = readl(pdata->mc_vbase + MVEBU_SDRAM_ERR_ECC_RCVD);
> + comp_ecc = readl(pdata->mc_vbase + MVEBU_SDRAM_ERR_ECC_CALC);
> + syndrome = sdram_ecc ^ comp_ecc;
> +
> + /* first bit clear in ECC Err Reg, 1 bit error, correctable by HW */
> + if (!(reg & 0x1))
> + edac_mc_handle_error(HW_EVENT_ERR_CORRECTED, mci, 1,
> +  err_addr >> PAGE_SHIFT,
> +  err_addr & PAGE_MASK, syndrome,
> +  0, 0, -1,
> +  mci->ctl_name, "");
> + else/* 2 bit error, UE */
> + edac_mc_handle_error(HW_EVENT_ERR_UNCORRECTED, mci, 1,
> +  err_addr >> PAGE_SHIFT,
> +  err_addr & PAGE_MASK, 0,
> +  0, 0, -1,
> +  mci->ctl_name, "");
> +
> + /* clear the error */
> + writel(0, pdata->mc_vbase + MVEBU_SDRAM_ERR_ADDR);
This field is documented to be read-only. I had to write the Error
Interrupt Cause Register (0x14d0) or Message Interrupt Cause Register
(0x14d8) to allow the capture of new error details.

[...]
> +static void get_total_mem(struct mvebu_mc_pdata *pdata)
> +{
> + struct device_node *np = NULL;
> + struct resource res;
> + int ret;
> + unsigned long total_mem = 0;
> +
> + for_each_node_by_type(np, "memory") {
> + ret = of_address_to_resource(np, 0, );
> + if (ret)
> + continue;
> +
> + total_mem += resource_size();
> + }
> +
> + pdata->total_mem = total_mem;
> +}
I think we can calculate the sizes by reading back the individual CS
configuration registers.

> +static void mvebu_init_csrows(struct mem_ctl_info *mci,
> + struct mvebu_mc_pdata *pdata)
[...]
> + devtype = (ctl >> 20) & 0x3;
> + switch (devtype) {
> + case 0x0:
> + dimm->dtype = DEV_X32;
> + break;
> + case 0x2:   /* could be X8 too, but no way to tell */
> + dimm->dtype = DEV_X16;
> + break;
> + case 0x3:
> + dimm->dtype = DEV_X4;
> + break;
> + default:
> + dimm->dtype = DEV_UNKNOWN;
> + break;
> + }
This register is documented as reserved? I pulled the X8/X16 information
from the Address Control Register (CSnStruct bits).

> + dimm->edac_mode = EDAC_SECDED;
> +}

[...]
> + if (!devres_open_group(>dev, mvebu_mc_err_probe, GFP_KERNEL))
> + return -ENOMEM;
devres automatically opens a group per device, so this shouln't be
needed (although other EADC drivers do the same).

> + layers[0].type = EDAC_MC_LAYER_CHIP_SELECT;
> + layers[0].size = 1;
> + layers[0].is_virt_csrow = true;
> + layers[1].type = EDAC_MC_LAYER_CHANNEL;
> + layers[1].size = 1;
> + layers[1].is_virt_csrow = false;
I'm not sure if this is needed. In my driver, only one CHIP_SELECT layer
with size 4 (max number of chip selects) is configured and seems to work
fine.

[...]
> + mci->scrub_mode = SCRUB_SW_SRC;
I'm not sure if this works as expected ARM as it is currently
implemented, but that's a topic for a different mail.

[...]
> + /* setup MC registers */
> + writel(0, pdata->mc_vbase + MVEBU_SDRAM_ERR_ADDR);
> + ctl = readl(pdata->mc_vbase + MVEBU_SDRAM_ERR_ECC_CNTL);
> + ctl = (ctl & 0xff00) | 0x1;
> + writel(ctl, pdata->mc_vbase + MVEBU_SDRAM_ERR_ECC_CNTL);
This configures the single bit error threshold to 1. My driver does the
same.

> +
> + if (edac_op_state == EDAC_OPSTATE_INT) {
> + /* acquire interrupt that reports errors */
> + pdata->irq = platform_get_irq(pdev, 0);
> + res = devm_request_irq(>dev,
> +pdata->irq,
> +mvebu_mc_isr,
> +0,
> +"[EDAC] MC err",
> +mci);
Which IRQ do you use? The current DT doesn't configure interrupts. Also
it seems that the events are passed through additional layers 

Re: [RFC PATCH 1/4] EDAC: mvebu: Add driver for Marvell Armada SoCs

2017-06-09 Thread Jan Lübbe
Hi Chris!

On Do, 2017-06-08 at 16:11 +1200, Chris Packham wrote:
> This adds an EDAC driver for the memory controller and L2 cache used on
> a number of Marvell Armada SoCs.
Why have two separate drivers in the same file and enabled with the same
Kconfig option?

[...]
> +static void mvebu_mc_check(struct mem_ctl_info *mci)
> +{
> + struct mvebu_mc_pdata *pdata = mci->pvt_info;
> + u32 reg;
> + u32 err_addr;
> + u32 sdram_ecc;
> + u32 comp_ecc;
> + u32 syndrome;
> +
> + reg = readl(pdata->mc_vbase + MVEBU_SDRAM_ERR_ADDR);
> + if (!reg)
> + return;
> +
> + err_addr = reg & ~0x3;
The ERR_ADDR register is not a physical address but contains multiple
fields (bank, col, chip select and error type).

> + sdram_ecc = readl(pdata->mc_vbase + MVEBU_SDRAM_ERR_ECC_RCVD);
> + comp_ecc = readl(pdata->mc_vbase + MVEBU_SDRAM_ERR_ECC_CALC);
> + syndrome = sdram_ecc ^ comp_ecc;
> +
> + /* first bit clear in ECC Err Reg, 1 bit error, correctable by HW */
> + if (!(reg & 0x1))
> + edac_mc_handle_error(HW_EVENT_ERR_CORRECTED, mci, 1,
> +  err_addr >> PAGE_SHIFT,
> +  err_addr & PAGE_MASK, syndrome,
> +  0, 0, -1,
> +  mci->ctl_name, "");
> + else/* 2 bit error, UE */
> + edac_mc_handle_error(HW_EVENT_ERR_UNCORRECTED, mci, 1,
> +  err_addr >> PAGE_SHIFT,
> +  err_addr & PAGE_MASK, 0,
> +  0, 0, -1,
> +  mci->ctl_name, "");
> +
> + /* clear the error */
> + writel(0, pdata->mc_vbase + MVEBU_SDRAM_ERR_ADDR);
This field is documented to be read-only. I had to write the Error
Interrupt Cause Register (0x14d0) or Message Interrupt Cause Register
(0x14d8) to allow the capture of new error details.

[...]
> +static void get_total_mem(struct mvebu_mc_pdata *pdata)
> +{
> + struct device_node *np = NULL;
> + struct resource res;
> + int ret;
> + unsigned long total_mem = 0;
> +
> + for_each_node_by_type(np, "memory") {
> + ret = of_address_to_resource(np, 0, );
> + if (ret)
> + continue;
> +
> + total_mem += resource_size();
> + }
> +
> + pdata->total_mem = total_mem;
> +}
I think we can calculate the sizes by reading back the individual CS
configuration registers.

> +static void mvebu_init_csrows(struct mem_ctl_info *mci,
> + struct mvebu_mc_pdata *pdata)
[...]
> + devtype = (ctl >> 20) & 0x3;
> + switch (devtype) {
> + case 0x0:
> + dimm->dtype = DEV_X32;
> + break;
> + case 0x2:   /* could be X8 too, but no way to tell */
> + dimm->dtype = DEV_X16;
> + break;
> + case 0x3:
> + dimm->dtype = DEV_X4;
> + break;
> + default:
> + dimm->dtype = DEV_UNKNOWN;
> + break;
> + }
This register is documented as reserved? I pulled the X8/X16 information
from the Address Control Register (CSnStruct bits).

> + dimm->edac_mode = EDAC_SECDED;
> +}

[...]
> + if (!devres_open_group(>dev, mvebu_mc_err_probe, GFP_KERNEL))
> + return -ENOMEM;
devres automatically opens a group per device, so this shouln't be
needed (although other EADC drivers do the same).

> + layers[0].type = EDAC_MC_LAYER_CHIP_SELECT;
> + layers[0].size = 1;
> + layers[0].is_virt_csrow = true;
> + layers[1].type = EDAC_MC_LAYER_CHANNEL;
> + layers[1].size = 1;
> + layers[1].is_virt_csrow = false;
I'm not sure if this is needed. In my driver, only one CHIP_SELECT layer
with size 4 (max number of chip selects) is configured and seems to work
fine.

[...]
> + mci->scrub_mode = SCRUB_SW_SRC;
I'm not sure if this works as expected ARM as it is currently
implemented, but that's a topic for a different mail.

[...]
> + /* setup MC registers */
> + writel(0, pdata->mc_vbase + MVEBU_SDRAM_ERR_ADDR);
> + ctl = readl(pdata->mc_vbase + MVEBU_SDRAM_ERR_ECC_CNTL);
> + ctl = (ctl & 0xff00) | 0x1;
> + writel(ctl, pdata->mc_vbase + MVEBU_SDRAM_ERR_ECC_CNTL);
This configures the single bit error threshold to 1. My driver does the
same.

> +
> + if (edac_op_state == EDAC_OPSTATE_INT) {
> + /* acquire interrupt that reports errors */
> + pdata->irq = platform_get_irq(pdev, 0);
> + res = devm_request_irq(>dev,
> +pdata->irq,
> +mvebu_mc_isr,
> +0,
> +"[EDAC] MC err",
> +mci);
Which IRQ do you use? The current DT doesn't configure interrupts. Also
it seems that the events are passed through additional layers 

Re: [RFC PATCH 1/4] EDAC: mvebu: Add driver for Marvell Armada SoCs

2017-06-09 Thread Borislav Petkov
On Thu, Jun 08, 2017 at 04:11:21PM +1200, Chris Packham wrote:
> This adds an EDAC driver for the memory controller and L2 cache used on
> a number of Marvell Armada SoCs.
> 
> Signed-off-by: Chris Packham 
> Cc: linux-arm-ker...@lists.infradead.org
> ---
>  drivers/edac/Kconfig  |   7 +
>  drivers/edac/Makefile |   1 +
>  drivers/edac/mvebu_edac.c | 506 
> ++
>  drivers/edac/mvebu_edac.h |  66 ++
>  4 files changed, 580 insertions(+)
>  create mode 100644 drivers/edac/mvebu_edac.c
>  create mode 100644 drivers/edac/mvebu_edac.h

...

> diff --git a/drivers/edac/mvebu_edac.c b/drivers/edac/mvebu_edac.c
> new file mode 100644
> index ..624cf10f821b
> --- /dev/null
> +++ b/drivers/edac/mvebu_edac.c
> @@ -0,0 +1,506 @@
> +/*
> + * EDAC driver for Marvell ARM SoCs
> + *
> + * Copyright (C) 2017 Allied Telesis Labs
> + *
> + * This program is free software; you can redistribute it and/or modify
> + * it under the terms of the GNU General Public License as published by
> + * the Free Software Foundation; version 2 of the License.
> + *
> + * This program is distributed in the hope that it will be useful,
> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
> + * GNU General Public License for more details
> + */
> +
> +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt

We have all those fancy edac_printk() macros, no need to use pr_* ones.

...

> +static int mvebu_mc_err_probe(struct platform_device *pdev)
> +{
> + struct mem_ctl_info *mci;
> + struct edac_mc_layer layers[2];
> + struct mvebu_mc_pdata *pdata;
> + struct resource *r;
> + u32 ctl;
> + int res = 0;
> +
> + if (!devres_open_group(>dev, mvebu_mc_err_probe, GFP_KERNEL))
> + return -ENOMEM;
> +
> + layers[0].type = EDAC_MC_LAYER_CHIP_SELECT;
> + layers[0].size = 1;
> + layers[0].is_virt_csrow = true;
> + layers[1].type = EDAC_MC_LAYER_CHANNEL;
> + layers[1].size = 1;
> + layers[1].is_virt_csrow = false;
> + mci = edac_mc_alloc(edac_mc_idx, ARRAY_SIZE(layers), layers,
> + sizeof(struct mvebu_mc_pdata));
> + if (!mci) {
> + pr_err("%s: No memory for CPU err\n", __func__);
> + devres_release_group(>dev, mvebu_mc_err_probe);
> + return -ENOMEM;
> + }

Make that call after all platform_get_resource(),
devm_ioremap_resource() so that you can save yourself the unwinding
"goto err" and return directly then.

> +
> + pdata = mci->pvt_info;
> + mci->pdev = >dev;
> + platform_set_drvdata(pdev, mci);
> + pdata->name = "mvebu_mc_err";
> + mci->dev_name = dev_name(>dev);
> + pdata->edac_idx = edac_mc_idx++;
> +
> + r = platform_get_resource(pdev, IORESOURCE_MEM, 0);
> + if (!r) {
> + pr_err("%s: Unable to get resource for MC err regs\n",
> +__func__);
> + res = -ENOENT;
> + goto err;
> + }
> +
> + pdata->mc_vbase = devm_ioremap_resource(>dev, r);
> + if (IS_ERR(pdata->mc_vbase)) {
> + pr_err("%s: Unable to setup MC err regs\n", __func__);
> + res = PTR_ERR(pdata->mc_vbase);
> + goto err;
> + }
> +
> + ctl = readl(pdata->mc_vbase + MVEBU_SDRAM_CONFIG);
> + if (!(ctl & MVEBU_SDRAM_ECC)) {
> + /* Non-ECC RAM? */
> + pr_warn("%s: No ECC DIMMs discovered\n", __func__);
> + res = -ENODEV;
> + goto err;
> + }
> +
> + edac_dbg(3, "init mci\n");
> + mci->mtype_cap = MEM_FLAG_RDDR | MEM_FLAG_DDR;
> + mci->edac_ctl_cap = EDAC_FLAG_NONE | EDAC_FLAG_SECDED;
> + mci->edac_cap = EDAC_FLAG_SECDED;
> + mci->mod_name = EDAC_MOD_STR;
> + mci->mod_ver = MVEBU_REVISION;
> + mci->ctl_name = mvebu_ctl_name;
> +
> + if (edac_op_state == EDAC_OPSTATE_POLL)
> + mci->edac_check = mvebu_mc_check;
> +
> + mci->ctl_page_to_phys = NULL;
> +
> + mci->scrub_mode = SCRUB_SW_SRC;
> +
> + mvebu_init_csrows(mci, pdata);
> +
> + /* setup MC registers */
> + writel(0, pdata->mc_vbase + MVEBU_SDRAM_ERR_ADDR);
> + ctl = readl(pdata->mc_vbase + MVEBU_SDRAM_ERR_ECC_CNTL);
> + ctl = (ctl & 0xff00) | 0x1;
> + writel(ctl, pdata->mc_vbase + MVEBU_SDRAM_ERR_ECC_CNTL);
> +
> + if (edac_op_state == EDAC_OPSTATE_INT) {
> + /* acquire interrupt that reports errors */
> + pdata->irq = platform_get_irq(pdev, 0);
> + res = devm_request_irq(>dev,
> +pdata->irq,
> +mvebu_mc_isr,
> +0,
> +"[EDAC] MC err",
> +mci);
> + if (res < 0) {
> + pr_err("%s: Unable to request irq %d\n", 

Re: [RFC PATCH 1/4] EDAC: mvebu: Add driver for Marvell Armada SoCs

2017-06-09 Thread Borislav Petkov
On Thu, Jun 08, 2017 at 04:11:21PM +1200, Chris Packham wrote:
> This adds an EDAC driver for the memory controller and L2 cache used on
> a number of Marvell Armada SoCs.
> 
> Signed-off-by: Chris Packham 
> Cc: linux-arm-ker...@lists.infradead.org
> ---
>  drivers/edac/Kconfig  |   7 +
>  drivers/edac/Makefile |   1 +
>  drivers/edac/mvebu_edac.c | 506 
> ++
>  drivers/edac/mvebu_edac.h |  66 ++
>  4 files changed, 580 insertions(+)
>  create mode 100644 drivers/edac/mvebu_edac.c
>  create mode 100644 drivers/edac/mvebu_edac.h

...

> diff --git a/drivers/edac/mvebu_edac.c b/drivers/edac/mvebu_edac.c
> new file mode 100644
> index ..624cf10f821b
> --- /dev/null
> +++ b/drivers/edac/mvebu_edac.c
> @@ -0,0 +1,506 @@
> +/*
> + * EDAC driver for Marvell ARM SoCs
> + *
> + * Copyright (C) 2017 Allied Telesis Labs
> + *
> + * This program is free software; you can redistribute it and/or modify
> + * it under the terms of the GNU General Public License as published by
> + * the Free Software Foundation; version 2 of the License.
> + *
> + * This program is distributed in the hope that it will be useful,
> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
> + * GNU General Public License for more details
> + */
> +
> +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt

We have all those fancy edac_printk() macros, no need to use pr_* ones.

...

> +static int mvebu_mc_err_probe(struct platform_device *pdev)
> +{
> + struct mem_ctl_info *mci;
> + struct edac_mc_layer layers[2];
> + struct mvebu_mc_pdata *pdata;
> + struct resource *r;
> + u32 ctl;
> + int res = 0;
> +
> + if (!devres_open_group(>dev, mvebu_mc_err_probe, GFP_KERNEL))
> + return -ENOMEM;
> +
> + layers[0].type = EDAC_MC_LAYER_CHIP_SELECT;
> + layers[0].size = 1;
> + layers[0].is_virt_csrow = true;
> + layers[1].type = EDAC_MC_LAYER_CHANNEL;
> + layers[1].size = 1;
> + layers[1].is_virt_csrow = false;
> + mci = edac_mc_alloc(edac_mc_idx, ARRAY_SIZE(layers), layers,
> + sizeof(struct mvebu_mc_pdata));
> + if (!mci) {
> + pr_err("%s: No memory for CPU err\n", __func__);
> + devres_release_group(>dev, mvebu_mc_err_probe);
> + return -ENOMEM;
> + }

Make that call after all platform_get_resource(),
devm_ioremap_resource() so that you can save yourself the unwinding
"goto err" and return directly then.

> +
> + pdata = mci->pvt_info;
> + mci->pdev = >dev;
> + platform_set_drvdata(pdev, mci);
> + pdata->name = "mvebu_mc_err";
> + mci->dev_name = dev_name(>dev);
> + pdata->edac_idx = edac_mc_idx++;
> +
> + r = platform_get_resource(pdev, IORESOURCE_MEM, 0);
> + if (!r) {
> + pr_err("%s: Unable to get resource for MC err regs\n",
> +__func__);
> + res = -ENOENT;
> + goto err;
> + }
> +
> + pdata->mc_vbase = devm_ioremap_resource(>dev, r);
> + if (IS_ERR(pdata->mc_vbase)) {
> + pr_err("%s: Unable to setup MC err regs\n", __func__);
> + res = PTR_ERR(pdata->mc_vbase);
> + goto err;
> + }
> +
> + ctl = readl(pdata->mc_vbase + MVEBU_SDRAM_CONFIG);
> + if (!(ctl & MVEBU_SDRAM_ECC)) {
> + /* Non-ECC RAM? */
> + pr_warn("%s: No ECC DIMMs discovered\n", __func__);
> + res = -ENODEV;
> + goto err;
> + }
> +
> + edac_dbg(3, "init mci\n");
> + mci->mtype_cap = MEM_FLAG_RDDR | MEM_FLAG_DDR;
> + mci->edac_ctl_cap = EDAC_FLAG_NONE | EDAC_FLAG_SECDED;
> + mci->edac_cap = EDAC_FLAG_SECDED;
> + mci->mod_name = EDAC_MOD_STR;
> + mci->mod_ver = MVEBU_REVISION;
> + mci->ctl_name = mvebu_ctl_name;
> +
> + if (edac_op_state == EDAC_OPSTATE_POLL)
> + mci->edac_check = mvebu_mc_check;
> +
> + mci->ctl_page_to_phys = NULL;
> +
> + mci->scrub_mode = SCRUB_SW_SRC;
> +
> + mvebu_init_csrows(mci, pdata);
> +
> + /* setup MC registers */
> + writel(0, pdata->mc_vbase + MVEBU_SDRAM_ERR_ADDR);
> + ctl = readl(pdata->mc_vbase + MVEBU_SDRAM_ERR_ECC_CNTL);
> + ctl = (ctl & 0xff00) | 0x1;
> + writel(ctl, pdata->mc_vbase + MVEBU_SDRAM_ERR_ECC_CNTL);
> +
> + if (edac_op_state == EDAC_OPSTATE_INT) {
> + /* acquire interrupt that reports errors */
> + pdata->irq = platform_get_irq(pdev, 0);
> + res = devm_request_irq(>dev,
> +pdata->irq,
> +mvebu_mc_isr,
> +0,
> +"[EDAC] MC err",
> +mci);
> + if (res < 0) {
> + pr_err("%s: Unable to request irq %d\n", __func__,
> +