[PATCH v3 1/4] NAND: FSL-UPM: add multi chip support

Grant Likely grant.likely at secretlab.ca
Wed Mar 25 09:31:06 EDT 2009


On Wed, Mar 25, 2009 at 4:08 AM, Wolfgang Grandegger <wg at grandegger.com> wrote:
> This patch adds support for multi-chip NAND devices to the FSL-UPM
> driver. This requires support for multiple GPIOs for the RNB pins.
> The NAND chips are selected through address lines defined by the
> FDT property "chip-offset".
>
> Signed-off-by: Wolfgang Grandegger <wg at grandegger.com>

Hi Wolfgang,

Can you please send a sample device tree snippit for this and add
documentation updates to your patch for the extended binding?

Thanks,
g.

> ---
>  arch/powerpc/sysdev/fsl_lbc.c |    2 +-
>  drivers/mtd/nand/fsl_upm.c    |   95 +++++++++++++++++++++++++++++++---------
>  2 files changed, 74 insertions(+), 23 deletions(-)
>
> diff --git a/arch/powerpc/sysdev/fsl_lbc.c b/arch/powerpc/sysdev/fsl_lbc.c
> index 0494ee5..dceb8d1 100644
> --- a/arch/powerpc/sysdev/fsl_lbc.c
> +++ b/arch/powerpc/sysdev/fsl_lbc.c
> @@ -150,7 +150,7 @@ int fsl_upm_run_pattern(struct fsl_upm *upm, void __iomem *io_base, u32 mar)
>
>        spin_lock_irqsave(&fsl_lbc_lock, flags);
>
> -       out_be32(&fsl_lbc_regs->mar, mar << (32 - upm->width));
> +       out_be32(&fsl_lbc_regs->mar, mar);
>
>        switch (upm->width) {
>        case 8:
> diff --git a/drivers/mtd/nand/fsl_upm.c b/drivers/mtd/nand/fsl_upm.c
> index 7815a40..9b314ce 100644
> --- a/drivers/mtd/nand/fsl_upm.c
> +++ b/drivers/mtd/nand/fsl_upm.c
> @@ -36,8 +36,11 @@ struct fsl_upm_nand {
>        uint8_t upm_addr_offset;
>        uint8_t upm_cmd_offset;
>        void __iomem *io_base;
> -       int rnb_gpio;
> +       int rnb_gpio[NAND_MAX_CHIPS];
>        int chip_delay;
> +       uint32_t num_chips;
> +       uint32_t chip_number;
> +       uint32_t chip_offset;
>  };
>
>  #define to_fsl_upm_nand(mtd) container_of(mtd, struct fsl_upm_nand, mtd)
> @@ -46,7 +49,7 @@ static int fun_chip_ready(struct mtd_info *mtd)
>  {
>        struct fsl_upm_nand *fun = to_fsl_upm_nand(mtd);
>
> -       if (gpio_get_value(fun->rnb_gpio))
> +       if (gpio_get_value(fun->rnb_gpio[fun->chip_number]))
>                return 1;
>
>        dev_vdbg(fun->dev, "busy\n");
> @@ -55,9 +58,9 @@ static int fun_chip_ready(struct mtd_info *mtd)
>
>  static void fun_wait_rnb(struct fsl_upm_nand *fun)
>  {
> -       int cnt = 1000000;
>
> -       if (fun->rnb_gpio >= 0) {
> +       if (fun->rnb_gpio[fun->chip_number] >= 0) {
> +               int cnt = 1000000;
>                while (--cnt && !fun_chip_ready(&fun->mtd))
>                        cpu_relax();
>                if (!cnt)
> @@ -69,7 +72,9 @@ static void fun_wait_rnb(struct fsl_upm_nand *fun)
>
>  static void fun_cmd_ctrl(struct mtd_info *mtd, int cmd, unsigned int ctrl)
>  {
> +       struct nand_chip *chip = mtd->priv;
>        struct fsl_upm_nand *fun = to_fsl_upm_nand(mtd);
> +       u32 mar;
>
>        if (!(ctrl & fun->last_ctrl)) {
>                fsl_upm_end_pattern(&fun->upm);
> @@ -87,11 +92,30 @@ static void fun_cmd_ctrl(struct mtd_info *mtd, int cmd, unsigned int ctrl)
>                        fsl_upm_start_pattern(&fun->upm, fun->upm_cmd_offset);
>        }
>
> -       fsl_upm_run_pattern(&fun->upm, fun->io_base, cmd);
> +       mar = cmd << (32 - fun->upm.width);
> +       if (fun->chip_offset && fun->chip_number > 0)
> +               mar |= fun->chip_number * fun->chip_offset;
> +       fsl_upm_run_pattern(&fun->upm, chip->IO_ADDR_R, mar);
>
>        fun_wait_rnb(fun);
>  }
>
> +static void fun_select_chip(struct mtd_info *mtd, int chip_nr)
> +{
> +       struct nand_chip *chip = mtd->priv;
> +       struct fsl_upm_nand *fun = to_fsl_upm_nand(mtd);
> +
> +       if (chip_nr == -1) {
> +               chip->cmd_ctrl(mtd, NAND_CMD_NONE, 0 | NAND_CTRL_CHANGE);
> +       } else if (chip_nr >= 0) {
> +               fun->chip_number = chip_nr;
> +               chip->IO_ADDR_R = chip->IO_ADDR_W =
> +                       fun->io_base + chip_nr * fun->chip_offset;
> +       } else {
> +               BUG();
> +       }
> +}
> +
>  static uint8_t fun_read_byte(struct mtd_info *mtd)
>  {
>        struct fsl_upm_nand *fun = to_fsl_upm_nand(mtd);
> @@ -137,8 +161,10 @@ static int __devinit fun_chip_init(struct fsl_upm_nand *fun,
>        fun->chip.read_buf = fun_read_buf;
>        fun->chip.write_buf = fun_write_buf;
>        fun->chip.ecc.mode = NAND_ECC_SOFT;
> +       if (fun->num_chips > 1)
> +               fun->chip.select_chip = fun_select_chip;
>
> -       if (fun->rnb_gpio >= 0)
> +       if (fun->rnb_gpio[0] >= 0)
>                fun->chip.dev_ready = fun_chip_ready;
>
>        fun->mtd.priv = &fun->chip;
> @@ -155,7 +181,7 @@ static int __devinit fun_chip_init(struct fsl_upm_nand *fun,
>                goto err;
>        }
>
> -       ret = nand_scan(&fun->mtd, 1);
> +       ret = nand_scan(&fun->mtd, fun->num_chips);
>        if (ret)
>                goto err;
>
> @@ -187,6 +213,7 @@ static int __devinit fun_probe(struct of_device *ofdev,
>        const uint32_t *prop;
>        int ret;
>        int size;
> +       int i;
>
>        fun = kzalloc(sizeof(*fun), GFP_KERNEL);
>        if (!fun)
> @@ -208,7 +235,7 @@ static int __devinit fun_probe(struct of_device *ofdev,
>        if (!prop || size != sizeof(uint32_t)) {
>                dev_err(&ofdev->dev, "can't get UPM address offset\n");
>                ret = -EINVAL;
> -               goto err2;
> +               goto err1;
>        }
>        fun->upm_addr_offset = *prop;
>
> @@ -216,21 +243,36 @@ static int __devinit fun_probe(struct of_device *ofdev,
>        if (!prop || size != sizeof(uint32_t)) {
>                dev_err(&ofdev->dev, "can't get UPM command offset\n");
>                ret = -EINVAL;
> -               goto err2;
> +               goto err1;
>        }
>        fun->upm_cmd_offset = *prop;
>
> -       fun->rnb_gpio = of_get_gpio(ofdev->node, 0);
> -       if (fun->rnb_gpio >= 0) {
> -               ret = gpio_request(fun->rnb_gpio, dev_name(&ofdev->dev));
> -               if (ret) {
> -                       dev_err(&ofdev->dev, "can't request RNB gpio\n");
> +       prop = of_get_property(ofdev->node, "num-chips", &size);
> +       if (prop && size == sizeof(uint32_t)) {
> +               fun->num_chips = *prop;
> +               if (fun->num_chips >= NAND_MAX_CHIPS) {
> +                       dev_err(&ofdev->dev, "too much chips");
> +                       ret = -EINVAL;
> +                       goto err1;
> +               }
> +       } else {
> +               fun->num_chips = 1;
> +       }
> +
> +       for (i = 0; i < fun->num_chips; i++) {
> +               fun->rnb_gpio[i] = of_get_gpio(ofdev->node, i);
> +               if (fun->rnb_gpio[i] >= 0) {
> +                       ret = gpio_request(fun->rnb_gpio[i],
> +                                          dev_name(&ofdev->dev));
> +                       if (ret) {
> +                               dev_err(&ofdev->dev, "can't request RNB gpio\n");
> +                               goto err2;
> +                       }
> +                       gpio_direction_input(fun->rnb_gpio[i]);
> +               } else if (fun->rnb_gpio[i]  == -EINVAL) {
> +                       dev_err(&ofdev->dev, "specified RNB gpio is invalid\n");
>                        goto err2;
>                }
> -               gpio_direction_input(fun->rnb_gpio);
> -       } else if (fun->rnb_gpio == -EINVAL) {
> -               dev_err(&ofdev->dev, "specified RNB gpio is invalid\n");
> -               goto err2;
>        }
>
>        prop = of_get_property(ofdev->node, "chip-delay", NULL);
> @@ -239,6 +281,10 @@ static int __devinit fun_probe(struct of_device *ofdev,
>        else
>                fun->chip_delay = 50;
>
> +       prop = of_get_property(ofdev->node, "chip-offset", &size);
> +       if (prop && size == sizeof(uint32_t))
> +               fun->chip_offset = *prop;
> +
>        fun->io_base = devm_ioremap_nocache(&ofdev->dev, io_res.start,
>                                          io_res.end - io_res.start + 1);
>        if (!fun->io_base) {
> @@ -257,8 +303,10 @@ static int __devinit fun_probe(struct of_device *ofdev,
>
>        return 0;
>  err2:
> -       if (fun->rnb_gpio >= 0)
> -               gpio_free(fun->rnb_gpio);
> +       for (i = 0; i < fun->num_chips; i++) {
> +               if (fun->rnb_gpio[i] >= 0)
> +                       gpio_free(fun->rnb_gpio[i]);
> +       }
>  err1:
>        kfree(fun);
>
> @@ -268,12 +316,15 @@ err1:
>  static int __devexit fun_remove(struct of_device *ofdev)
>  {
>        struct fsl_upm_nand *fun = dev_get_drvdata(&ofdev->dev);
> +       int i;
>
>        nand_release(&fun->mtd);
>        kfree(fun->mtd.name);
>
> -       if (fun->rnb_gpio >= 0)
> -               gpio_free(fun->rnb_gpio);
> +        for (i = 0; i < fun->num_chips; i++) {
> +                if (fun->rnb_gpio[i] >= 0)
> +                        gpio_free(fun->rnb_gpio[i]);
> +        }
>
>        kfree(fun);
>
> --
> 1.6.0.6
>
> _______________________________________________
> Linuxppc-dev mailing list
> Linuxppc-dev at ozlabs.org
> https://ozlabs.org/mailman/listinfo/linuxppc-dev
>



-- 
Grant Likely, B.Sc., P.Eng.
Secret Lab Technologies Ltd.



More information about the linux-mtd mailing list