[PATCH v3 1/4] NAND: FSL-UPM: add multi chip support
Grant Likely
grant.likely at secretlab.ca
Wed Mar 25 09:32:04 EDT 2009
On Wed, Mar 25, 2009 at 7:31 AM, Grant Likely <grant.likely at secretlab.ca> wrote:
> 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?
Oh, and cc: devicetree-discuss at 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 at ozlabs.org
>> https://ozlabs.org/mailman/listinfo/linuxppc-dev
>>
>
>
>
> --
> Grant Likely, B.Sc., P.Eng.
> Secret Lab Technologies Ltd.
>
--
Grant Likely, B.Sc., P.Eng.
Secret Lab Technologies Ltd.
More information about the linux-mtd
mailing list