Fix 104MHz support for OMAP OneNAND
Subsystems (8)
cover Fix 104MHz support for OMAP OneNAND
Hi
Adrian Hunter (3):
OMAP: OneNAND: fix 104MHz support
OMAP: OneNAND: determine frequency in one place
OMAP: OneNAND: let boards determine OneNAND frequency
Roman Tereshonkov (1):
mtd: OneNAND: OMAP2: increase multiblock erase verify timeout
arch/arm/mach-omap2/gpmc-onenand.c | 113 +++++++++++++++++++++-------- arch/arm/plat-omap/include/plat/onenand.h | 10 +++- drivers/mtd/onenand/omap2.c | 36 ++------- include/linux/mtd/onenand_regs.h | 1 + 4 files changed, 100 insertions(+), 60 deletions(-)
Regards
Adrian
PATCH 1/4 OMAP: OneNAND: fix 104MHz support
104MHz needs a latency of 8 clock cycles and the VHF flag must be set. Also t_rdyo is specified as "not applicable" so pick a lower value, and force at least 1 clk between AVD High to OE Low.
--- arch/arm/mach-omap2/gpmc-onenand.c | 23 +++++++++++++++++------ include/linux/mtd/onenand_regs.h | 1 + 2 files changed, 18 insertions(+), 6 deletions(-)
arch/arm/mach-omap2/gpmc-onenand.c +17 -6
@@ -94,7 +94,7 @@ static int omap2_onenand_set_async_mode(int cs, void __iomem *onenand_base) }static void set_onenand_cfg (void __iomem *onenand_base,int latency, -int sync_read,int sync_write,int hf) +int sync_read,int sync_write,int hf,int vhf) {u32 reg;
@@ -114,6 +114,10 @@ static void set_onenand_cfg(void __iomem *onenand_base, int latency, reg |=ONENAND_SYS_CFG1_HF ; else reg &= ~ONENAND_SYS_CFG1_HF; +if (vhf) + reg |=ONENAND_SYS_CFG1_VHF ; +else + reg &= ~ONENAND_SYS_CFG1_VHF ;writew (reg, onenand_base +ONENAND_REG_SYS_CFG1 ); }
@@ -130,7 +134,7 @@ static int omap2_onenand_set_sync_mode(struct omap_onenand_platform_data *cfg,const int t_wph =30 ;int min_gpmc_clk_period, t_ces, t_avds, t_avdh, t_ach, t_aavdh, t_rdyo;int tick_ns, div, fclk_offset_ns, fclk_offset, gpmc_clk_ns, latency; -int first_time =0 , hf =0 , sync_read =0 , sync_write =0 ; +int first_time =0 , hf =0 , vhf =0 , sync_read =0 , sync_write =0 ;int err, ticks_cez;int cs = cfg->cs;u32 reg;
@@ -180,7 +184,7 @@ static int omap2_onenand_set_sync_mode(struct omap_onenand_platform_data *cfg, t_avdh =2 ; t_ach =3 ; t_aavdh =6 ; - t_rdyo =9 ; + t_rdyo =6 ;break ;case 83 : min_gpmc_clk_period =12000 ;/* 83 MHz */
@@ -217,7 +221,11 @@ static int omap2_onenand_set_sync_mode(struct omap_onenand_platform_data *cfg, gpmc_clk_ns =gpmc_ticks_to_ns (div);if (gpmc_clk_ns <15 )/* >66Mhz */ hf =1 ; -if (hf) +if (gpmc_clk_ns <12 )/* >83Mhz */ + vhf =1 ; +if (vhf) + latency =8 ; +else if (hf) latency =6 ;else if (gpmc_clk_ns >=25 )/* 40 MHz*/ latency =3 ;
@@ -226,7 +234,7 @@ static int omap2_onenand_set_sync_mode(struct omap_onenand_platform_data *cfg,if (first_time)set_onenand_cfg (onenand_base, latency, - sync_read, sync_write, hf); + sync_read, sync_write, hf, vhf);if (div ==1 ) { reg =gpmc_cs_read_reg (cs,GPMC_CS_CONFIG2 );
@@ -264,6 +272,9 @@ static int omap2_onenand_set_sync_mode(struct omap_onenand_platform_data *cfg,/* Read */ t.adv_rd_off =gpmc_ticks_to_ns (fclk_offset +gpmc_ns_to_ticks (t_avdh)); t.oe_on =gpmc_ticks_to_ns (fclk_offset +gpmc_ns_to_ticks (t_ach)); +/* Force at least 1 clk between AVD High to OE Low */ +if (t.oe_on <= t.adv_rd_off) + t.oe_on = t.adv_rd_off +gpmc_round_ns_to_ticks (1 ); t.access =gpmc_ticks_to_ns (fclk_offset + (latency +1 ) * div); t.oe_off = t.access +gpmc_round_ns_to_ticks (1 ); t.cs_rd_off = t.oe_off;
@@ -317,7 +328,7 @@ static int omap2_onenand_set_sync_mode(struct omap_onenand_platform_data *cfg,if (err)return err; -set_onenand_cfg (onenand_base, latency, sync_read, sync_write, hf); +set_onenand_cfg (onenand_base, latency, sync_read, sync_write, hf, vhf);return 0 ; }
include/linux/mtd/onenand_regs.h +1 -0
@@ -168,6 +168,7 @@#define ONENAND_SYS_CFG1_INT (1 << 6)#define ONENAND_SYS_CFG1_IOBE (1 << 5)#define ONENAND_SYS_CFG1_RDY_CONF (1 << 4) +#define ONENAND_SYS_CFG1_VHF (1 << 3)#define ONENAND_SYS_CFG1_HF (1 << 2)#define ONENAND_SYS_CFG1_SYNC_WRITE (1 << 1)
Signature · 1 line
1.7.0.4
PATCH 2/4 OMAP: OneNAND: determine frequency in one place
In reply to Adrian HunterOneNAND frequency is determined when calculating GPMC timings. Return that value instead of determining it again in the OMAP OneNAND driver.
--- arch/arm/mach-omap2/gpmc-onenand.c | 10 ++++++---- arch/arm/plat-omap/include/plat/onenand.h | 2 +- drivers/mtd/onenand/omap2.c | 28 +++++----------------------- 3 files changed, 12 insertions(+), 28 deletions(-)
arch/arm/mach-omap2/gpmc-onenand.c +6 -4
@@ -123,7 +123,7 @@ static void set_onenand_cfg(void __iomem *onenand_base, int latency,static int omap2_onenand_set_sync_mode (struct omap_onenand_platform_data *cfg,void __iomem *onenand_base, -int freq) +int *freq_ptr) {struct gpmc_timings t;const int t_cer =15 ;
@@ -136,7 +136,7 @@ static int omap2_onenand_set_sync_mode(struct omap_onenand_platform_data *cfg,int tick_ns, div, fclk_offset_ns, fclk_offset, gpmc_clk_ns, latency;int first_time =0 , hf =0 , vhf =0 , sync_read =0 , sync_write =0 ;int err, ticks_cez; -int cs = cfg->cs; +int cs = cfg->cs, freq = *freq_ptr;u32 reg;if (cfg->flags &ONENAND_SYNC_READ ) {
@@ -330,16 +330,18 @@ static int omap2_onenand_set_sync_mode(struct omap_onenand_platform_data *cfg,set_onenand_cfg (onenand_base, latency, sync_read, sync_write, hf, vhf); + *freq_ptr = freq; +return 0 ; } -static int gpmc_onenand_setup (void __iomem *onenand_base,int freq) +static int gpmc_onenand_setup (void __iomem *onenand_base,int *freq_ptr) {struct device *dev = &gpmc_onenand_device.dev;/* Set sync timings in GPMC */ if (omap2_onenand_set_sync_mode (gpmc_onenand_data, onenand_base, - freq) <0 ) { + freq_ptr) <0 ) {dev_err (dev,"Unable to set synchronous mode\n" );return -EINVAL ; }
arch/arm/plat-omap/include/plat/onenand.h +1 -1
@@ -20,7 +20,7 @@ struct omap_onenand_platform_data {int gpio_irq;struct mtd_partition *parts;int nr_parts; -int (*onenand_setup)(void __iomem *,int freq); +int (*onenand_setup)(void __iomem *,int *freq_ptr);int dma_channel;u8 flags;u8 regulator_can_sleep;
drivers/mtd/onenand/omap2.c +5 -23
@@ -63,7 +63,7 @@ struct omap2_onenand {struct completion dma_done;int dma_channel;int freq; -int (*setup)(void __iomem *base,int freq); +int (*setup)(void __iomem *base,int *freq_ptr);struct regulator *regulator; };
@@ -581,7 +581,7 @@ static int __adjust_timing(struct device *dev, void *data)/* DMA is not in use so this is all that is needed */ /* Revisit for OMAP3! */ - ret = c->setup (c->onenand.base, c->freq); + ret = c->setup (c->onenand.base, &c->freq);return ret; }
@@ -673,7 +673,7 @@ static int __devinit omap2_onenand_probe(struct platform_device *pdev) }if (pdata->onenand_setup !=NULL ) { - r = pdata->onenand_setup (c->onenand.base, c->freq); + r = pdata->onenand_setup (c->onenand.base, &c->freq);if (r <0 ) {dev_err (&pdev->dev,"Onenand platform setup failed: " "%d\n" , r);
@@ -718,8 +718,8 @@ static int __devinit omap2_onenand_probe(struct platform_device *pdev) }dev_info (&pdev->dev,"initializing on CS%d, phys base 0x%08lx, virtual " -"base %p\n" , c->gpmc_cs, c->phys_base, - c->onenand.base); +"base %p, freq %d MHz\n" , c->gpmc_cs, c->phys_base, + c->onenand.base, c->freq); c->pdev = pdev; c->mtd.name =dev_name (&pdev->dev);
@@ -754,24 +754,6 @@ static int __devinit omap2_onenand_probe(struct platform_device *pdev)if ((r =onenand_scan (&c->mtd,1 )) <0 ) goto err_release_regulator; -switch ((c->onenand.version_id >>4 ) &0xf ) { -case 0 : - c->freq =40 ; -break ; -case 1 : - c->freq =54 ; -break ; -case 2 : - c->freq =66 ; -break ; -case 3 : - c->freq =83 ; -break ; -case 4 : - c->freq =104 ; -break ; - } -#ifdef CONFIG_MTD_PARTITIONS r =parse_mtd_partitions (&c->mtd, part_probes, &c->parts,0 );if (r >0 )
Signature · 1 line
1.7.0.4
PATCH 3/4 OMAP: OneNAND: let boards determine OneNAND frequency
In reply to Adrian HunterOneNAND version ID may not give the highest frequency supported and some OneNAND's have setup times that are clock dependent. Let the board provide that information.
--- arch/arm/mach-omap2/gpmc-onenand.c | 80 +++++++++++++++++++++-------- arch/arm/plat-omap/include/plat/onenand.h | 8 +++ 2 files changed, 67 insertions(+), 21 deletions(-)
arch/arm/mach-omap2/gpmc-onenand.c +59 -21
@@ -121,6 +121,47 @@ static void set_onenand_cfg(void __iomem *onenand_base, int latency,writew (reg, onenand_base +ONENAND_REG_SYS_CFG1 ); } +static int omap2_onenand_get_freq (struct omap_onenand_platform_data *cfg, +void __iomem *onenand_base,bool *clk_dep) +{ +u16 ver =readw (onenand_base +ONENAND_REG_VERSION_ID ); +int freq =0 ; + +if (cfg->get_freq) { +struct onenand_freq_info fi; + + fi.maf_id =readw (onenand_base +ONENAND_REG_MANUFACTURER_ID ); + fi.dev_id =readw (onenand_base +ONENAND_REG_DEVICE_ID ); + fi.ver_id = ver; + freq = cfg->get_freq (&fi, clk_dep); +if (freq) +return freq; + } + +switch ((ver >>4 ) &0xf ) { +case 0 : + freq =40 ; +break ; +case 1 : + freq =54 ; +break ; +case 2 : + freq =66 ; +break ; +case 3 : + freq =83 ; +break ; +case 4 : + freq =104 ; +break ; +default : + freq =54 ; +break ; + } + +return freq; +} +static int omap2_onenand_set_sync_mode (struct omap_onenand_platform_data *cfg,void __iomem *onenand_base,int *freq_ptr)
@@ -138,6 +179,7 @@ static int omap2_onenand_set_sync_mode(struct omap_onenand_platform_data *cfg,int err, ticks_cez;int cs = cfg->cs, freq = *freq_ptr;u32 reg; +bool clk_dep = false;if (cfg->flags &ONENAND_SYNC_READ ) { sync_read =1 ;
@@ -152,27 +194,7 @@ static int omap2_onenand_set_sync_mode(struct omap_onenand_platform_data *cfg, err =omap2_onenand_set_async_mode (cs, onenand_base);if (err)return err; - reg =readw (onenand_base +ONENAND_REG_VERSION_ID ); -switch ((reg >>4 ) &0xf ) { -case 0 : - freq =40 ; -break ; -case 1 : - freq =54 ; -break ; -case 2 : - freq =66 ; -break ; -case 3 : - freq =83 ; -break ; -case 4 : - freq =104 ; -break ; -default : - freq =54 ; -break ; - } + freq =omap2_onenand_get_freq (cfg, onenand_base, &clk_dep); first_time =1 ; }
@@ -232,6 +254,22 @@ static int omap2_onenand_set_sync_mode(struct omap_onenand_platform_data *cfg,else latency =4 ; +if (clk_dep) { +if (gpmc_clk_ns <12 ) {/* >83Mhz */ + t_ces =3 ; + t_avds =4 ; + }else if (gpmc_clk_ns <15 ) {/* >66Mhz */ + t_ces =5 ; + t_avds =4 ; + }else if (gpmc_clk_ns <25 ) {/* >40Mhz */ + t_ces =6 ; + t_avds =5 ; + }else { + t_ces =7 ; + t_avds =7 ; + } + } +if (first_time)set_onenand_cfg (onenand_base, latency, sync_read, sync_write, hf, vhf);
arch/arm/plat-omap/include/plat/onenand.h +8 -0
@@ -15,12 +15,20 @@#define ONENAND_SYNC_READ (1 << 0)#define ONENAND_SYNC_READWRITE (1 << 1) +struct onenand_freq_info { +u16 maf_id; +u16 dev_id; +u16 ver_id; +}; +struct omap_onenand_platform_data {int cs;int gpio_irq;struct mtd_partition *parts;int nr_parts;int (*onenand_setup)(void __iomem *,int *freq_ptr); +int (*get_freq)(const struct onenand_freq_info *freq_info, +bool *clk_dep);int dma_channel;u8 flags;u8 regulator_can_sleep;
Signature · 1 line
1.7.0.4
PATCH 4/4 mtd: OneNAND: OMAP2: increase multiblock erase verify timeout
In reply to Adrian HunterFrom: Roman Tereshonkov <[email protected]>
The current multiblock erase verify read timeout 100us is the maximum for none-error case. If errors happen during multibock erase then the specification recommends to run multiblock erase verify command with maximum timeout 10ms (see specs. for KFM4G16Q2A and KFN8G16Q2A).
For the most common non-error case we wait 100us in udelay polling loop. In case of timeout the interrupt mode is used to wait for the command end.
--- drivers/mtd/onenand/omap2.c | 8 +++----- 1 files changed, 3 insertions(+), 5 deletions(-)
drivers/mtd/onenand/omap2.c +3 -5
@@ -148,11 +148,9 @@ static int omap2_onenand_wait(struct mtd_info *mtd, int state)wait_err ("controller error" , state, ctrl, intr);return -EIO ; } -if ((intr & intr_flags) != intr_flags) { -wait_err ("timeout" , state, ctrl, intr); -return -EIO ; - } -return 0 ; +if ((intr & intr_flags) == intr_flags) +return 0 ; +/* Continue in wait for interrupt branch */ }if (state !=FL_READING ) {
Signature · 1 line
1.7.0.4
Tony Lindgren
maintainerPATCH 4/4 mtd: OneNAND: OMAP2: increase multiblock erase verify timeout
Quoted 7 lines · Adrian Hunter
From: Roman Tereshonkov <[email protected]>
The current multiblock erase verify read timeout 100us is the maximum for none-error case. If errors happen during multibock erase then the specification recommends to run multiblock erase verify command with maximum timeout 10ms (see specs. for KFM4G16Q2A and KFN8G16Q2A).
Quoted 5 lines
For the most common non-error case we wait 100us in udelay polling loop. In case of timeout the interrupt mode is used to wait for the command end.
Signed-off-by Roman Tereshonkov <[email protected]>
I'll queue this series. Anybody from MTD list care to ack this patch?
Regards,
Tony
Quoted 26 lines
--- drivers/mtd/onenand/omap2.c | 8 +++----- 1 files changed, 3 insertions(+), 5 deletions(-)drivers/mtd/onenand/omap2.c +3
-5@@ -148,11 +148,9 @@ static int omap2_onenand_wait(struct mtd_info *mtd, int state)wait_err ("controller error" , state, ctrl, intr);return -EIO ; } -if ((intr & intr_flags) != intr_flags) { -wait_err ("timeout" , state, ctrl, intr); -return -EIO ; - } -return 0 ; +if ((intr & intr_flags) == intr_flags) +return 0 ; +/* Continue in wait for interrupt branch */ }if (state !=FL_READING ) {Signature · 1 line
1.7.0.4
PATCH 4/4 mtd: OneNAND: OMAP2: increase multiblock erase verify timeout
Hi,
Quoted 8 lines · Tony Lindgren
Quoted 7 lines · Adrian Hunter
From: Roman Tereshonkov <[email protected]>
The current multiblock erase verify read timeout 100us is the maximum for none-error case. If errors happen during multibock erase then the specification recommends to run multiblock erase verify command with maximum timeout 10ms (see specs. for KFM4G16Q2A and KFN8G16Q2A).
Quoted 7 lines
Quoted 5 lines
For the most common non-error case we wait 100us in udelay polling loop. In case of timeout the interrupt mode is used to wait for the command end.
Signed-off-by Roman Tereshonkov <[email protected]>I'll queue this series. Anybody from MTD list care to ack this patch?
Signature · 2 lines
Best Regards, Artem Bityutskiy (????? ????????)
Tony Lindgren
maintainerPATCH 4/4 mtd: OneNAND: OMAP2: increase multiblock erase verify timeout
Quoted 15 lines · Artem Bityutskiy
Hi,
Quoted 12 lines · Tony Lindgren
Quoted 11 lines · Adrian Hunter
From: Roman Tereshonkov <[email protected]>
The current multiblock erase verify read timeout 100us is the maximum for none-error case. If errors happen during multibock erase then the specification recommends to run multiblock erase verify command with maximum timeout 10ms (see specs. for KFM4G16Q2A and KFN8G16Q2A).
For the most common non-error case we wait 100us in udelay polling loop. In case of timeout the interrupt mode is used to wait for the command end.
Quoted 5 lines
Quoted 3 lines
Quoted 1 line
Signed-off-by Roman Tereshonkov <[email protected]>I'll queue this series. Anybody from MTD list care to ack this patch?
Acked-by Artem Bityutskiy <[email protected]>
Thanks!
Tony