diff --git a/drivers/mtd/nand/gpmi-nand/gpmi-lib.c b/drivers/mtd/nand/gpmi-nand/gpmi-lib.c index a1f43329ad43..6bb0998dcb40 100644 --- a/drivers/mtd/nand/gpmi-nand/gpmi-lib.c +++ b/drivers/mtd/nand/gpmi-nand/gpmi-lib.c @@ -124,12 +124,42 @@ error: return -ETIMEDOUT; } +static int __gpmi_enable_clk(struct gpmi_nand_data *this, bool v) +{ + struct clk *clk; + int ret; + int i; + + for (i = 0; i < GPMI_CLK_MAX; i++) { + clk = this->resources.clock[i]; + if (!clk) + break; + + if (v) { + ret = clk_prepare_enable(clk); + if (ret) + goto err_clk; + } else { + clk_disable_unprepare(clk); + } + } + return 0; + +err_clk: + for (; i > 0; i--) + clk_disable_unprepare(this->resources.clock[i - 1]); + return ret; +} + +#define gpmi_enable_clk(x) __gpmi_enable_clk(x, true) +#define gpmi_disable_clk(x) __gpmi_enable_clk(x, false) + int gpmi_init(struct gpmi_nand_data *this) { struct resources *r = &this->resources; int ret; - ret = clk_prepare_enable(r->clock); + ret = gpmi_enable_clk(this); if (ret) goto err_out; ret = gpmi_reset_block(r->gpmi_regs, false); @@ -149,7 +179,7 @@ int gpmi_init(struct gpmi_nand_data *this) /* Select BCH ECC. */ writel(BM_GPMI_CTRL1_BCH_MODE, r->gpmi_regs + HW_GPMI_CTRL1_SET); - clk_disable_unprepare(r->clock); + gpmi_disable_clk(this); return 0; err_out: return ret; @@ -205,7 +235,7 @@ int bch_set_geometry(struct gpmi_nand_data *this) ecc_strength = bch_geo->ecc_strength >> 1; page_size = bch_geo->page_size; - ret = clk_prepare_enable(r->clock); + ret = gpmi_enable_clk(this); if (ret) goto err_out; @@ -240,7 +270,7 @@ int bch_set_geometry(struct gpmi_nand_data *this) writel(BM_BCH_CTRL_COMPLETE_IRQ_EN, r->bch_regs + HW_BCH_CTRL_SET); - clk_disable_unprepare(r->clock); + gpmi_disable_clk(this); return 0; err_out: return ret; @@ -716,7 +746,7 @@ void gpmi_begin(struct gpmi_nand_data *this) int ret; /* Enable the clock. */ - ret = clk_prepare_enable(r->clock); + ret = gpmi_enable_clk(this); if (ret) { pr_err("We failed in enable the clk\n"); goto err_out; @@ -727,7 +757,7 @@ void gpmi_begin(struct gpmi_nand_data *this) gpmi_regs + HW_GPMI_TIMING1); /* Get the timing information we need. */ - nfc->clock_frequency_in_hz = clk_get_rate(r->clock); + nfc->clock_frequency_in_hz = clk_get_rate(r->clock[0]); clock_period_in_ns = 1000000000 / nfc->clock_frequency_in_hz; gpmi_nfc_compute_hardware_timing(this, &hw); @@ -784,8 +814,7 @@ err_out: void gpmi_end(struct gpmi_nand_data *this) { - struct resources *r = &this->resources; - clk_disable_unprepare(r->clock); + gpmi_disable_clk(this); } /* Clears a BCH interrupt. */ diff --git a/drivers/mtd/nand/gpmi-nand/gpmi-nand.c b/drivers/mtd/nand/gpmi-nand/gpmi-nand.c index 9da9ee88a824..8c0d2f0a526f 100644 --- a/drivers/mtd/nand/gpmi-nand/gpmi-nand.c +++ b/drivers/mtd/nand/gpmi-nand/gpmi-nand.c @@ -465,9 +465,78 @@ acquire_err: return -EINVAL; } +static void gpmi_put_clks(struct gpmi_nand_data *this) +{ + struct resources *r = &this->resources; + struct clk *clk; + int i; + + for (i = 0; i < GPMI_CLK_MAX; i++) { + clk = r->clock[i]; + if (clk) { + clk_put(clk); + r->clock[i] = NULL; + } + } +} + +static char *extra_clks_for_mx6q[GPMI_CLK_MAX] = { + "gpmi_apb", "gpmi_bch", "gpmi_bch_apb", "per1_bch", +}; + +static int __devinit gpmi_get_clks(struct gpmi_nand_data *this) +{ + struct resources *r = &this->resources; + char **extra_clks = NULL; + struct clk *clk; + int i; + + /* The main clock is stored in the first. */ + r->clock[0] = clk_get(this->dev, "gpmi_io"); + if (IS_ERR(r->clock[0])) + goto err_clock; + + /* Get extra clocks */ + if (GPMI_IS_MX6Q(this)) + extra_clks = extra_clks_for_mx6q; + if (!extra_clks) + return 0; + + for (i = 1; i < GPMI_CLK_MAX; i++) { + if (extra_clks[i - 1] == NULL) + break; + + clk = clk_get(this->dev, extra_clks[i - 1]); + if (IS_ERR(clk)) + goto err_clock; + + r->clock[i] = clk; + } + + if (GPMI_IS_MX6Q(this)) { + /* + * Set the default values for the clocks in mx6q: + * The main clock(enfc) : 22MHz + * The others : 44.5MHz + * + * These are just the default values. If you want to use + * the ONFI nand which is in the Synchronous Mode, you should + * change the clocks's frequencies as you need. + */ + clk_set_rate(r->clock[0], 22000000); + for (i = 1; i < GPMI_CLK_MAX && r->clock[i]; i++) + clk_set_rate(r->clock[i], 44500000); + } + return 0; + +err_clock: + dev_dbg(this->dev, "failed in finding the clocks.\n"); + gpmi_put_clks(this); + return -ENOMEM; +} + static int __devinit acquire_resources(struct gpmi_nand_data *this) { - struct resources *res = &this->resources; struct pinctrl *pinctrl; int ret; @@ -493,12 +562,9 @@ static int __devinit acquire_resources(struct gpmi_nand_data *this) goto exit_pin; } - res->clock = clk_get(&this->pdev->dev, NULL); - if (IS_ERR(res->clock)) { - pr_err("can not get the clock\n"); - ret = -ENOENT; + ret = gpmi_get_clks(this); + if (ret) goto exit_clock; - } return 0; exit_clock: @@ -513,9 +579,7 @@ exit_regs: static void release_resources(struct gpmi_nand_data *this) { - struct resources *r = &this->resources; - - clk_put(r->clock); + gpmi_put_clks(this); release_register_block(this); release_bch_irq(this); release_dma_channels(this); diff --git a/drivers/mtd/nand/gpmi-nand/gpmi-nand.h b/drivers/mtd/nand/gpmi-nand/gpmi-nand.h index ce5daa160920..1547a60c1c6f 100644 --- a/drivers/mtd/nand/gpmi-nand/gpmi-nand.h +++ b/drivers/mtd/nand/gpmi-nand/gpmi-nand.h @@ -22,6 +22,7 @@ #include #include +#define GPMI_CLK_MAX 5 /* MX6Q needs five clocks */ struct resources { void *gpmi_regs; void *bch_regs; @@ -29,7 +30,7 @@ struct resources { unsigned int bch_high_interrupt; unsigned int dma_low_channel; unsigned int dma_high_channel; - struct clk *clock; + struct clk *clock[GPMI_CLK_MAX]; }; /**