Message ID | 1237975701-23201-2-git-send-email-wg@grandegger.com |
---|---|
State | New, archived |
Headers | show |
> +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(); > + } braces are not required here... > + 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; > + } ditto...
Singh, Vimal wrote: >> +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(); >> + } > braces are not required here... Really? In the coding style I read: http://lxr.linux.no/linux+v2.6.29/Documentation/CodingStyle#L171 > > >> + 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; >> + } > ditto... See above. Wolfgang.
On Wed, Mar 25, 2009 at 4:08 AM, Wolfgang Grandegger <wg@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@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@ozlabs.org > https://ozlabs.org/mailman/listinfo/linuxppc-dev >
On Wed, Mar 25, 2009 at 7:31 AM, Grant Likely <grant.likely@secretlab.ca> wrote: > On Wed, Mar 25, 2009 at 4:08 AM, Wolfgang Grandegger <wg@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@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? Oh, and cc: devicetree-discuss@ozlabs.org in your next posting. 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@ozlabs.org >> https://ozlabs.org/mailman/listinfo/linuxppc-dev >> > > > > -- > Grant Likely, B.Sc., P.Eng. > Secret Lab Technologies Ltd. >
Grant Likely wrote: > On Wed, Mar 25, 2009 at 7:31 AM, Grant Likely <grant.likely@secretlab.ca> wrote: >> On Wed, Mar 25, 2009 at 4:08 AM, Wolfgang Grandegger <wg@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@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? > > Oh, and cc: devicetree-discuss@ozlabs.org in your next posting. OK, does patch 3/4 not already contain what you are looking for? See: http://ozlabs.org/pipermail/linuxppc-dev/2009-March/069787.html I separated it from the NAND patches because they go through the MTD maintainer(s). BTW: did you have a chance to look into the following RFC on I2C bus speed setting? http://ozlabs.org/pipermail/linuxppc-dev/2009-March/069489.html Thanks, Wolfgang.
Hi Wolfgang, On Wed, Mar 25, 2009 at 11:08:18AM +0100, Wolfgang Grandegger 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@grandegger.com> > --- Some cosmetic issues are still there... > 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; > This empty line can be removed. > - if (fun->rnb_gpio >= 0) { > + if (fun->rnb_gpio[fun->chip_number] >= 0) { > + int cnt = 1000000; Add an empty line here. > 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; Please avoid = = constructions. chip->IO_ADDR_R = fun->io_base + chip_nr * fun->chip_offset; chip->IO_ADDR_W = chip->IO_ADDR_R; ^^ looks much better. > + > + } 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"); \n is missing in dev_err(). > + 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], trailing whitespace on that line. > + dev_name(&ofdev->dev)); > + if (ret) { > + dev_err(&ofdev->dev, "can't request RNB gpio\n"); line is over 80 chars. > + goto err2; > + } > + gpio_direction_input(fun->rnb_gpio[i]); > + } else if (fun->rnb_gpio[i] == -EINVAL) { stray whitespace in " ==". > + 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]); > + } code indent should use tabs where possible (not white spaces). When the cosmetic issues are fixed, I'll readily ack this patch. Thanks!
Hi Anton, Anton Vorontsov wrote: > Hi Wolfgang, > > On Wed, Mar 25, 2009 at 11:08:18AM +0100, Wolfgang Grandegger 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@grandegger.com> >> --- > > Some cosmetic issues are still there... Normally I use checkpatch.pl to validate my patches, but ... grrr, sorry. It finds all the obvious issues you reported. [snip] > When the cosmetic issues are fixed, I'll readily ack this patch. Thanks, will resend a.s.a.p. Wolfgang.
On Wed, Mar 25, 2009 at 7:43 AM, Wolfgang Grandegger <wg@grandegger.com> wrote: > Grant Likely wrote: >> On Wed, Mar 25, 2009 at 7:31 AM, Grant Likely <grant.likely@secretlab.ca> wrote: >>> On Wed, Mar 25, 2009 at 4:08 AM, Wolfgang Grandegger <wg@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@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? >> >> Oh, and cc: devicetree-discuss@ozlabs.org in your next posting. > > OK, does patch 3/4 not already contain what you are looking for? See: > > http://ozlabs.org/pipermail/linuxppc-dev/2009-March/069787.html Oops, sorry. Missed that. > I separated it from the NAND patches because they go through the MTD > maintainer(s). > > BTW: did you have a chance to look into the following RFC on I2C bus > speed setting? > > http://ozlabs.org/pipermail/linuxppc-dev/2009-March/069489.html No. Looking at it now. g.
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);
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@grandegger.com> --- arch/powerpc/sysdev/fsl_lbc.c | 2 +- drivers/mtd/nand/fsl_upm.c | 95 +++++++++++++++++++++++++++++++--------- 2 files changed, 74 insertions(+), 23 deletions(-)