From: Afzal Mohammed <hidden> Date: 2012-08-21 10:41:56
Hi,
This series provides a generic gpmc timing calculation routine. There
were three peripherals (OneNAND, tusb6010, smc91x) using custom timing
calculations, they are migrated to use the generic timing calculation.
Such a generic routine would help create a driver out of gpmc platform
code, which would be peripheral agnostic and thus lead to DT finally.
Input to generic timing calculation routine would be gpmc peripheral
timings, output - translated timings that gpmc can understand. Later,
to DT'ify, gpmc peripheral timings could be passed through DT. Input
timings that has been used here are selected such that it represents
those that are present in peripheral timing datasheets.
Proposed generic routine has been tested on OneNAND (async) on
OMAP3EVM rev C (as mainline does not have the OneNAND support for this,
local patch were used to test). For other cases of custom timing
calculation (tusb6010, smc91x non-muxed, OneNAND sync), generic timing
calculation routine was verified by simulating on OMAP3EVM. Codebase
for such a testing is available
@ git://gitorious.org/x0148406-public/linux-kernel.git gpmc-prep-v6-verify
Regarding "we_on" for onenand async, it was found that even for muxed
address/data, it need not be greater than "adv_wr_off", but rather
could be derived from write setup time for peripheral from start of
access, hence would more be in line with peripheral timings. With
"we_on" calculated this way, it was working fine. If it is required in
some cases to have "we_on" same as "wr_data_mux_bus" (i.e. greater
than "adv_wr_off") other than for IP's not having "wr_data_mux_bus",
another variable could be added to indicate it, but such a
requirement is not expected though.
It has been observed that "adv_rd_off" & "adv_wr_off" are currently
calculated by adding an offset over "oe_on" & "we_on" respectively
in the case of smc91x. But peripheral datasheet does not specify
so and so "adv_rd(wr)_off" has been derived (to be specific, made
ignorant of "oe_on" & "we_on") observing datasheet rather than adding
an offset. Hence this generic routine is expected to work for smc91x
(91C96 RX51 board). This was verified on smsc911x (9220 on OMAP3EVM)
- a similar ethernet controller.
Also this series contains, patches 1-3 of v5 in addition to
minor cleanups that were low hanging fruits came across upon
implementing generic timing routine. One such is credited to
Jon Hunter, relevant patch is 1/2 of his series
@ http://marc.info/?l=linux-omap&m=134090910321284&w=2
This series has been tested on OMAP3EVM. As mentioned above, generic
timing could be tested on OneNAND hardware working in async mode.
Also generic timing calculation for other cases was validated by
comparing output values of existing custom timing calculation routine
& the new generic one, these conditions were simulated on OMAP3EVM
This series is available
@ git://gitorious.org/x0148406-public/linux-kernel.git gpmc-prep-v6
and is made on top of
http://marc.info/?l=linux-omap&m=134554192102899&w=2
which is based over 3.6-rc1.
Regards
Afzal
v6:
1. Generic timing calculation, move existing users of custom
calculation to use the new generic one
2. Set OneNAND part to async mode before gpmc configuration
3. Move extra delay time user handling to proper patch
(3/10 -> 2/10)
4. Modify nand init for OMAP3EVM too as support got added
v5:
Use flags for sync_read/write, hv, vhf
v4:
Reorganize OneNAND set_sync/async functions in a better way
v3:
1. Refactor OneNAND set_sync/async functions to separate out
timing and configurations
2. Handle bool type timings too
3. Swap patches 2 & 3 due to dependency of OneNAND change on
newly added bool type timings
v2:
1. Make use of timing api for setting clock activation time,
and remove direct writing to register for clock activation.
2. Move ensuring that async mode in OneNAND has been setup from
set_sync to setup function, improve commit message
Afzal Mohammed (9):
ARM: OMAP2+: nand: unify init functions
ARM: OMAP2+: gpmc: handle additional timings
ARM: OMAP2+: onenand: refactor for clarity
ARM: OMAP2+: gpmc: find features by ip rev check
ARM: OMAP2+: gpmc: remove cs# in sync clk div calc
ARM: OMAP2+: gpmc: generic timing calculation
ARM: OMAP2+: onenand: generic timing calculation
ARM: OMAP2+: smc91x: generic timing calculation
ARM: OMAP2+: tusb6010: generic timing calculation
Jon Hunter (1):
ARM: OMAP2+: GPMC: Remove unused OneNAND get_freq() platform function
arch/arm/mach-omap2/board-devkit8000.c | 8 +-
arch/arm/mach-omap2/board-flash.c | 45 ++--
arch/arm/mach-omap2/board-flash.h | 6 +-
arch/arm/mach-omap2/board-igep0020.c | 2 +-
arch/arm/mach-omap2/board-ldp.c | 4 +-
arch/arm/mach-omap2/board-omap3beagle.c | 8 +-
arch/arm/mach-omap2/board-omap3evm.c | 8 +-
arch/arm/mach-omap2/board-omap3touchbook.c | 8 +-
arch/arm/mach-omap2/board-overo.c | 7 +-
arch/arm/mach-omap2/board-zoom.c | 5 +-
arch/arm/mach-omap2/common-board-devices.c | 46 ----
arch/arm/mach-omap2/common-board-devices.h | 1 -
arch/arm/mach-omap2/gpmc-onenand.c | 334 +++++++++++---------------
arch/arm/mach-omap2/gpmc-smc91x.c | 43 ++--
arch/arm/mach-omap2/gpmc.c | 363 +++++++++++++++++++++++++++-
arch/arm/mach-omap2/usb-tusb6010.c | 180 ++++-----------
arch/arm/plat-omap/include/plat/gpmc.h | 82 ++++++-
arch/arm/plat-omap/include/plat/onenand.h | 8 -
18 files changed, 707 insertions(+), 451 deletions(-)
From: Afzal Mohammed <hidden> Date: 2012-08-21 10:45:07
Helper function for updating nand platform data has been
added the capability to take timing structure arguement.
Usage of omap_nand_flash_init() has been replaced by modifed
one, omap_nand_flash_init was doing things similar to
board_nand_init except that NAND CS# were being acquired
based on bootloader setting. As CS# is hardwired for a given
board, acquiring gpmc CS# has been removed, and updated with
the value on board.
NAND CS# used in beagle board & omap3evm was found to be CS0.
Thomas Weber [off-list ref] reported
that value of devkit8000 to be CS0. Overo board was found
to be using CS0 based on u-boot, while google grep says
omap3touchbook too has CS0.
Signed-off-by: Afzal Mohammed <redacted>
Reviewed-by: Jon Hunter <redacted>
---
v6: Modify nand init for OMAP3EVM too as support got added
arch/arm/mach-omap2/board-devkit8000.c | 8 ++++-
arch/arm/mach-omap2/board-flash.c | 45 ++++++++++++++-------------
arch/arm/mach-omap2/board-flash.h | 6 ++-
arch/arm/mach-omap2/board-igep0020.c | 2 +-
arch/arm/mach-omap2/board-ldp.c | 4 +-
arch/arm/mach-omap2/board-omap3beagle.c | 8 ++++-
arch/arm/mach-omap2/board-omap3evm.c | 8 ++++-
arch/arm/mach-omap2/board-omap3touchbook.c | 8 ++++-
arch/arm/mach-omap2/board-overo.c | 7 +++-
arch/arm/mach-omap2/board-zoom.c | 5 ++-
arch/arm/mach-omap2/common-board-devices.c | 46 ----------------------------
arch/arm/mach-omap2/common-board-devices.h | 1 -
12 files changed, 62 insertions(+), 86 deletions(-)
@@ -103,41 +103,41 @@ __init board_onenand_init(struct mtd_partition *onenand_parts,defined(CONFIG_MTD_NAND_OMAP2_MODULE)/* Note that all values in this struct are in nanoseconds */-staticstructgpmc_timingsnand_timings={+structgpmc_timingsnand_default_timings[1]={+{+.sync_clk=0,-.sync_clk=0,+.cs_on=0,+.cs_rd_off=36,+.cs_wr_off=36,-.cs_on=0,-.cs_rd_off=36,-.cs_wr_off=36,+.adv_on=6,+.adv_rd_off=24,+.adv_wr_off=36,-.adv_on=6,-.adv_rd_off=24,-.adv_wr_off=36,+.we_off=30,+.oe_off=48,-.we_off=30,-.oe_off=48,+.access=54,+.rd_cycle=72,+.wr_cycle=72,-.access=54,-.rd_cycle=72,-.wr_cycle=72,--.wr_access=30,-.wr_data_mux_bus=0,+.wr_access=30,+.wr_data_mux_bus=0,+},};-staticstructomap_nand_platform_databoard_nand_data={-.gpmc_t=&nand_timings,-};+staticstructomap_nand_platform_databoard_nand_data;void-__initboard_nand_init(structmtd_partition*nand_parts,-u8nr_parts,u8cs,intnand_type)+__initboard_nand_init(structmtd_partition*nand_parts,u8nr_parts,u8cs,+intnand_type,structgpmc_timings*gpmc_t){board_nand_data.cs=cs;board_nand_data.parts=nand_parts;board_nand_data.nr_parts=nr_parts;board_nand_data.devsize=nand_type;+board_nand_data.gpmc_t=gpmc_t;board_nand_data.ecc_opt=OMAP_ECC_HAMMING_CODE_DEFAULT;board_nand_data.gpmc_irq=OMAP_GPMC_IRQ_BASE+cs;
@@ -238,5 +238,6 @@ void __init board_flash_init(struct flash_partitions partition_info[],pr_err("NAND: Unable to find configuration in GPMC\n");elseboard_nand_init(partition_info[2].parts,-partition_info[2].nr_parts,nandcs,nand_type);+partition_info[2].nr_parts,nandcs,+nand_type,nand_default_timings);}
@@ -517,8 +520,9 @@ static void __init omap3_beagle_init(void)usb_musb_init(NULL);usbhs_init(&usbhs_bdata);-omap_nand_flash_init(NAND_BUSWIDTH_16,omap3beagle_nand_partitions,-ARRAY_SIZE(omap3beagle_nand_partitions));+board_nand_init(omap3beagle_nand_partitions,+ARRAY_SIZE(omap3beagle_nand_partitions),NAND_CS,+NAND_BUSWIDTH_16,NULL);/* Ensure msecure is mux'd to be able to set the RTC. */omap_mux_init_signal("sys_drm_msecure",OMAP_PIN_OFF_OUTPUT_HIGH);
@@ -95,49 +95,3 @@ void __init omap_ads7846_init(int bus_num, int gpio_pendown, int gpio_debounce,{}#endif--#if defined(CONFIG_MTD_NAND_OMAP2) || defined(CONFIG_MTD_NAND_OMAP2_MODULE)-staticstructomap_nand_platform_datanand_data;--void__initomap_nand_flash_init(intoptions,structmtd_partition*parts,-intnr_parts)-{-u8cs=0;-u8nandcs=GPMC_CS_NUM+1;--/* find out the chip-select on which NAND exists */-while(cs<GPMC_CS_NUM){-u32ret=0;-ret=gpmc_cs_read_reg(cs,GPMC_CS_CONFIG1);--if((ret&0xC00)==0x800){-printk(KERN_INFO"Found NAND on CS%d\n",cs);-if(nandcs>GPMC_CS_NUM)-nandcs=cs;-}-cs++;-}--if(nandcs>GPMC_CS_NUM){-printk(KERN_INFO"NAND: Unable to find configuration "-"in GPMC\n ");-return;-}--if(nandcs<GPMC_CS_NUM){-nand_data.cs=nandcs;-nand_data.parts=parts;-nand_data.nr_parts=nr_parts;-nand_data.devsize=options;--printk(KERN_INFO"Registering NAND on CS%d\n",nandcs);-if(gpmc_nand_init(&nand_data)<0)-printk(KERN_ERR"Unable to register NAND device\n");-}-}-#else-void__initomap_nand_flash_init(intoptions,structmtd_partition*parts,-intnr_parts)-{-}-#endif
From: Afzal Mohammed <hidden> Date: 2012-08-21 10:45:13
Configure busturnaround, cycle2cycledelay, waitmonitoringtime,
clkactivationtime in gpmc_cs_set_timings(). This is done so
that boards can configure these parameters of gpmc in Kernel
instead of relying on bootloader. Also configure bool type
timings like extradelay.
This needed change to the existing users that were configuring
clk activation time and extra delay by directly writing to
registers. Thanks to Tony for making me aware of users of clk
activation and being kind enough to test the modified one.
Signed-off-by: Afzal Mohammed <redacted>
---
v6: Move extra delay time user handling to present one from 3/10
v3: Handle bool type timings too
v2: Make use of timing api for setting clock activation time,
and remove direct writing to register for clock activation.
arch/arm/mach-omap2/gpmc-onenand.c | 28 ++++---------------
arch/arm/mach-omap2/gpmc.c | 45 ++++++++++++++++++++++++++++++++
arch/arm/mach-omap2/usb-tusb6010.c | 3 +-
arch/arm/plat-omap/include/plat/gpmc.h | 19 +++++++++++++
4 files changed, 72 insertions(+), 23 deletions(-)
@@ -220,6 +227,36 @@ unsigned int gpmc_round_ns_to_ticks(unsigned int time_ns)returnticks*gpmc_get_fclk_period()/1000;}+staticinlinevoidgpmc_cs_modify_reg(intcs,intreg,u32mask,boolvalue)+{+u32l;++l=gpmc_cs_read_reg(cs,reg);+if(value)+l|=mask;+else+l&=~mask;+gpmc_cs_write_reg(cs,reg,l);+}++staticvoidgpmc_cs_bool_timings(intcs,conststructgpmc_bool_timings*p)+{+gpmc_cs_modify_reg(cs,GPMC_CS_CONFIG1,+GPMC_CONFIG1_TIME_PARA_GRAN,p->time_para_granularity);+gpmc_cs_modify_reg(cs,GPMC_CS_CONFIG2,+GPMC_CONFIG2_CSEXTRADELAY,p->cs_extra_delay);+gpmc_cs_modify_reg(cs,GPMC_CS_CONFIG3,+GPMC_CONFIG3_ADVEXTRADELAY,p->adv_extra_delay);+gpmc_cs_modify_reg(cs,GPMC_CS_CONFIG4,+GPMC_CONFIG4_OEEXTRADELAY,p->oe_extra_delay);+gpmc_cs_modify_reg(cs,GPMC_CS_CONFIG4,+GPMC_CONFIG4_OEEXTRADELAY,p->we_extra_delay);+gpmc_cs_modify_reg(cs,GPMC_CS_CONFIG6,+GPMC_CONFIG6_CYCLE2CYCLESAMECSEN,p->cycle2cyclesamecsen);+gpmc_cs_modify_reg(cs,GPMC_CS_CONFIG6,+GPMC_CONFIG6_CYCLE2CYCLEDIFFCSEN,p->cycle2cyclediffcsen);+}+#ifdef DEBUGstaticintset_gpmc_timing_reg(intcs,intreg,intst_bit,intend_bit,inttime,constchar*name)
@@ -96,6 +96,17 @@ enum omap_ecc {OMAP_ECC_BCH8_CODE_HW,/* 8-bit BCH ecc code */};+/* bool type time settings */+structgpmc_bool_timings{+boolcycle2cyclediffcsen;+boolcycle2cyclesamecsen;+boolwe_extra_delay;+booloe_extra_delay;+booladv_extra_delay;+boolcs_extra_delay;+booltime_para_granularity;+};+/**Notethatallvaluesinthisstructareinnanosecondsexceptsync_clk*(whichisinpicoseconds),whiletheregistervaluesareingpmc_fckcycles.
@@ -128,9 +139,17 @@ struct gpmc_timings {u16rd_cycle;/* Total read cycle time */u16wr_cycle;/* Total write cycle time */+u16bus_turnaround;+u16cycle2cycle_delay;++u16wait_monitoring;+u16clk_activation;+/* The following are only on OMAP3430 */u16wr_access;/* WRACCESSTIME */u16wr_data_mux_bus;/* WRDATAONADMUXBUS */++structgpmc_bool_timingsbool_timings;};structgpmc_nand_regs{
From: Afzal Mohammed <hidden> Date: 2012-08-21 10:45:19
Refactor set_async_mode & set_sync_mode functions to
separate out timing calculation & actual configuration
(GPMC & OneNAND side).
Thanks to Jon for his suggestions.
Signed-off-by: Afzal Mohammed <redacted>
Reviewed-by: Jon Hunter <redacted>
---
v6:
1. Set OneNAND part to async mode before gpmc configuration
2. Move extra delay time user handling away from this patch
(3/10 -> 2/10)
v5: Use flags for sync_read/write, hv, vhf
v4: Reorganize OneNAND set_sync/async functions in a better way
v3: Refactor OneNAND set_sync/async functions to separate out
timing and configurations
v2: Move ensuring that async mode in OneNAND has been setup from
set_sync to setup function, improve commit message
arch/arm/mach-omap2/gpmc-onenand.c | 174 ++++++++++++++++++++++-------------
1 files changed, 109 insertions(+), 65 deletions(-)
@@ -172,9 +179,9 @@ static int omap2_onenand_get_freq(struct omap_onenand_platform_data *cfg,returnfreq;}-staticintomap2_onenand_set_sync_mode(structomap_onenand_platform_data*cfg,-void__iomem*onenand_base,-int*freq_ptr)+staticstructgpmc_timings+omap2_onenand_calc_sync_timings(structomap_onenand_platform_data*cfg,+intfreq,boolclk_dep){structgpmc_timingst;constintt_cer=15;
@@ -184,29 +191,14 @@ static int omap2_onenand_set_sync_mode(struct omap_onenand_platform_data *cfg,constintt_wpl=40;constintt_wph=30;intmin_gpmc_clk_period,t_ces,t_avds,t_avdh,t_ach,t_aavdh,t_rdyo;-intdiv,fclk_offset_ns,fclk_offset,gpmc_clk_ns,latency;-intfirst_time=0,hf=0,vhf=0,sync_read=0,sync_write=0;-interr,ticks_cez;-intcs=cfg->cs,freq=*freq_ptr;-u32reg;-boolclk_dep=false;--if(cfg->flags&ONENAND_SYNC_READ){-sync_read=1;-}elseif(cfg->flags&ONENAND_SYNC_READWRITE){-sync_read=1;-sync_write=1;-}else-returnomap2_onenand_set_async_mode(cs,onenand_base);+intdiv,fclk_offset_ns,fclk_offset,gpmc_clk_ns;+intticks_cez;+intcs=cfg->cs;-if(!freq){-/* Very first call freq is not known */-err=omap2_onenand_set_async_mode(cs,onenand_base);-if(err)-returnerr;-freq=omap2_onenand_get_freq(cfg,onenand_base,&clk_dep);-first_time=1;-}+if(cfg->flags&ONENAND_SYNC_READ)+onenand_flags=ONENAND_FLAG_SYNCREAD;+elseif(cfg->flags&ONENAND_SYNC_READWRITE)+onenand_flags=ONENAND_FLAG_SYNCREAD|ONENAND_FLAG_SYNCWRITE;switch(freq){case104:
@@ -340,6 +333,14 @@ static int omap2_onenand_set_sync_mode(struct omap_onenand_platform_data *cfg,}}+returnt;+}++staticintgpmc_set_sync_mode(intcs,structgpmc_timings*t)+{+unsignedsync_read=onenand_flags&ONENAND_FLAG_SYNCREAD;+unsignedsync_write=onenand_flags&ONENAND_FLAG_SYNCWRITE;+/* Configure GPMC for synchronous read */gpmc_cs_write_reg(cs,GPMC_CS_CONFIG1,GPMC_CONFIG1_WRAPBURST_SUPP|
@@ -355,11 +356,47 @@ static int omap2_onenand_set_sync_mode(struct omap_onenand_platform_data *cfg,GPMC_CONFIG1_DEVICETYPE_NOR|GPMC_CONFIG1_MUXADDDATA);-err=gpmc_cs_set_timings(cs,&t);-if(err)-returnerr;+returngpmc_cs_set_timings(cs,t);+}++staticintomap2_onenand_setup_async(void__iomem*onenand_base)+{+structgpmc_timingst;+intret;++omap2_onenand_set_async_mode(onenand_base);++t=omap2_onenand_calc_async_timings();++ret=gpmc_set_async_mode(gpmc_onenand_data->cs,&t);+if(IS_ERR_VALUE(ret))+returnret;++omap2_onenand_set_async_mode(onenand_base);++return0;+}++staticintomap2_onenand_setup_sync(void__iomem*onenand_base,int*freq_ptr)+{+intret,freq=*freq_ptr;+structgpmc_timingst;+boolclk_dep=false;++if(!freq){+/* Very first call freq is not known */+freq=omap2_onenand_get_freq(gpmc_onenand_data,+onenand_base,&clk_dep);+set_onenand_cfg(onenand_base);+}++t=omap2_onenand_calc_sync_timings(gpmc_onenand_data,freq,clk_dep);-set_onenand_cfg(onenand_base,latency,sync_read,sync_write,hf,vhf);+ret=gpmc_set_sync_mode(gpmc_onenand_data->cs,&t);+if(IS_ERR_VALUE(ret))+returnret;++set_onenand_cfg(onenand_base);*freq_ptr=freq;
@@ -369,15 +406,22 @@ static int omap2_onenand_set_sync_mode(struct omap_onenand_platform_data *cfg,staticintgpmc_onenand_setup(void__iomem*onenand_base,int*freq_ptr){structdevice*dev=&gpmc_onenand_device.dev;+unsignedl=ONENAND_SYNC_READ|ONENAND_SYNC_READWRITE;+intret;-/* Set sync timings in GPMC */-if(omap2_onenand_set_sync_mode(gpmc_onenand_data,onenand_base,-freq_ptr)<0){-dev_err(dev,"Unable to set synchronous mode\n");-return-EINVAL;+ret=omap2_onenand_setup_async(onenand_base);+if(ret){+dev_err(dev,"unable to set to async mode\n");+returnret;}-return0;+if(!(gpmc_onenand_data->flags&l))+return0;++ret=omap2_onenand_setup_sync(onenand_base,freq_ptr);+if(ret)+dev_err(dev,"unable to set to sync mode\n");+returnret;}void__initgpmc_onenand_init(structomap_onenand_platform_data*_onenand_data)
From: Afzal Mohammed <hidden> Date: 2012-08-21 10:45:25
From: Jon Hunter <redacted>
A platform function pointer for getting the frequency of a OneNAND device
was added so that a platform could specify a custom function for returning
the frequency and not just rely on the OneNAND version to determine the
frequency. However, this platform function pointer is not currently being
used and I am not sure if it ever has.
OneNAND devices are not so common these days and as far as I know not being
used with new devices. Therefore, it is most likely that this get_freq()
function pointer will not be used and so remove it.
Given that the get_freq() function pointer is not used, neither is the
clk_dep variable and so all references to it can also be removed.
Signed-off-by: Jon Hunter <redacted>
Signed-off-by: Afzal Mohammed <redacted>
---
arch/arm/mach-omap2/gpmc-onenand.c | 39 +++-------------------------
arch/arm/plat-omap/include/plat/onenand.h | 8 ------
2 files changed, 5 insertions(+), 42 deletions(-)
@@ -381,16 +354,14 @@ static int omap2_onenand_setup_sync(void __iomem *onenand_base, int *freq_ptr){intret,freq=*freq_ptr;structgpmc_timingst;-boolclk_dep=false;if(!freq){/* Very first call freq is not known */-freq=omap2_onenand_get_freq(gpmc_onenand_data,-onenand_base,&clk_dep);+freq=omap2_onenand_get_freq(gpmc_onenand_data,onenand_base);set_onenand_cfg(onenand_base);}-t=omap2_onenand_calc_sync_timings(gpmc_onenand_data,freq,clk_dep);+t=omap2_onenand_calc_sync_timings(gpmc_onenand_data,freq);ret=gpmc_set_sync_mode(gpmc_onenand_data->cs,&t);if(IS_ERR_VALUE(ret))
From: Afzal Mohammed <hidden> Date: 2012-08-21 10:45:33
Newer IP's have wr_access and wr_data_mux_bus fields. Use
IP revision values to determine availability of these
fields and hence decide on whether to configure them.
Signed-off-by: Afzal Mohammed <redacted>
---
arch/arm/mach-omap2/gpmc.c | 12 ++++++++++--
1 files changed, 10 insertions(+), 2 deletions(-)
@@ -85,6 +85,11 @@#define ENABLE_PREFETCH (0x1 << 7)#define DMA_MPU_MODE 2+#define GPMC_REVISION_MAJOR(l) ((l >> 4) & 0xf)++#define GPMC_HAS_WR_ACCESS 0x1+#define GPMC_HAS_WR_DATA_MUX_BUS 0x2+/* XXX: Only NAND irq has been considered,currently these are the only ones used*/#define GPMC_NR_IRQ 2
@@ -131,6 +136,7 @@ static DEFINE_SPINLOCK(gpmc_mem_lock);staticunsignedintgpmc_cs_map;/* flag for cs which are initialized */staticintgpmc_ecc_used=-EINVAL;/* cs using ecc engine */+staticunsignedgpmc_capability;staticvoid__iomem*gpmc_base;staticstructclk*gpmc_l3_clk;
@@ -356,10 +362,10 @@ int gpmc_cs_set_timings(int cs, const struct gpmc_timings *t)GPMC_SET_ONE(GPMC_CS_CONFIG1,18,19,wait_monitoring);GPMC_SET_ONE(GPMC_CS_CONFIG1,25,26,clk_activation);-if(cpu_is_omap34xx()){+if(gpmc_capability&GPMC_HAS_WR_DATA_MUX_BUS)GPMC_SET_ONE(GPMC_CS_CONFIG6,16,19,wr_data_mux_bus);+if(gpmc_capability&GPMC_HAS_WR_ACCESS)GPMC_SET_ONE(GPMC_CS_CONFIG6,24,28,wr_access);-}/* caller is expected to have initialized CONFIG1 to cover*atleastsyncvsasync
@@ -922,6 +928,8 @@ static int __init gpmc_init(void)clk_enable(gpmc_l3_clk);l=gpmc_read_reg(GPMC_REVISION);+if(GPMC_REVISION_MAJOR(l)>0x4)+gpmc_capability=GPMC_HAS_WR_ACCESS|GPMC_HAS_WR_DATA_MUX_BUS;printk(KERN_INFO"GPMC revision %d.%d\n",(l>>4)&0x0f,l&0x0f);/* Set smart idle mode and automatic L3 clock gating */l=gpmc_read_reg(GPMC_SYSCONFIG);
From: Afzal Mohammed <hidden> Date: 2012-08-21 10:45:40
Divider value for a certain sync clk is determined solely
based on gpmc fclk. CS# does not have any role here, thus
remove presence of CS# in clock divider calculation API.
Signed-off-by: Afzal Mohammed <redacted>
---
arch/arm/mach-omap2/gpmc-onenand.c | 3 +--
arch/arm/mach-omap2/gpmc.c | 4 ++--
arch/arm/plat-omap/include/plat/gpmc.h | 2 +-
3 files changed, 4 insertions(+), 5 deletions(-)
@@ -313,7 +313,7 @@ static int set_gpmc_timing_reg(int cs, int reg, int st_bit, int end_bit,return-1#endif-intgpmc_cs_calc_divider(intcs,unsignedintsync_clk)+intgpmc_calc_divider(unsignedintsync_clk){intdiv;u32l;
From: Afzal Mohammed <hidden> Date: 2012-08-21 10:45:47
Presently there are three peripherals that gets it timing
by runtime calculation. Those peripherals can work with
frequency scaling that affects gpmc clock. But timing
calculation for them are in different ways.
Here a generic runtime calculation method is proposed. Input
to this function were selected so that they represent timing
variables that are present in peripheral datasheets. Motive
behind this was to achieve DT bindings for the inputs as is.
Even though a few of the tusb6010 timings could not be made
directly related to timings normally found on peripherals,
expressions used were translated to those that could be
justified.
There are possibilities of improving the calculations, like
calculating timing for read & write operations in a more
similar way. Expressions derived here were tested for async
onenand on omap3evm (as vanilla Kernel does not have omap3evm
onenand support, local patch was used). Other peripherals,
tusb6010, smc91x calculations were validated by simulating
on omap3evm.
Regarding "we_on" for onenand async, it was found that even
for muxed address/data, it need not be greater than
"adv_wr_off", but rather could be derived from write setup
time for peripheral from start of access time, hence would
more be in line with peripheral timings. With this method
it was working fine. If it is required in some cases to
have "we_on" same as "wr_data_mux_bus" (i.e. greater than
"adv_wr_off"), another variable could be added to indicate
it. But such a requirement is not expected though.
Whole of this exercise is being done to achieve driver and
DT conversion. If timings could not be calculated in a
peripheral agnostic way, either gpmc driver would have to
be peripheral gnostic or a wrapper arrangement over gpmc
driver would be required.
Signed-off-by: Afzal Mohammed <redacted>
---
arch/arm/mach-omap2/gpmc.c | 302 ++++++++++++++++++++++++++++++++
arch/arm/plat-omap/include/plat/gpmc.h | 61 +++++++
2 files changed, 363 insertions(+), 0 deletions(-)
@@ -233,6 +233,18 @@ unsigned int gpmc_round_ns_to_ticks(unsigned int time_ns)returnticks*gpmc_get_fclk_period()/1000;}+unsignedintgpmc_ticks_to_ps(unsignedintticks)+{+returnticks*gpmc_get_fclk_period();+}++unsignedintgpmc_round_ps_to_ticks(unsignedinttime_ps)+{+unsignedlongticks=gpmc_ps_to_ticks(time_ps);++returnticks*gpmc_get_fclk_period();+}+staticinlinevoidgpmc_cs_modify_reg(intcs,intreg,u32mask,boolvalue){u32l;
@@ -884,6 +896,296 @@ static void __init gpmc_mem_init(void)}}+staticu32gpmc_round_ps_to_sync_clk(u32time_ps,u32sync_clk)+{+u32temp;+intdiv;++div=gpmc_calc_divider(sync_clk);+temp=gpmc_ps_to_ticks(time_ps);+temp=(temp+div-1)/div;+returngpmc_ticks_to_ps(temp*div);+}++/* can the cycles be avoided ? */+staticintgpmc_calc_sync_read_timings(structgpmc_timings*gpmc_t,+structgpmc_device_timings*dev_t)+{+boolmux=dev_t->mux;+u32temp;++/* adv_rd_off */+temp=dev_t->t_avdp_r;+/* mux check required ? */+if(mux){+/* t_avdp not to be required for sync, only added for tusb this+*indirectlynecessitatesrequirementoft_avdp_r&t_avdp_w+*insteadofhavingasinglet_avdp+*/+temp=max_t(u32,temp,gpmc_t->clk_activation*1000++dev_t->t_avdh);+temp=max_t(u32,+(gpmc_t->adv_on+gpmc_ticks_to_ns(1))*1000,temp);+}+gpmc_t->adv_rd_off=gpmc_round_ps_to_ticks(temp)/1000;++/* oe_on */+temp=dev_t->t_oeasu;/* remove this ? */+if(mux){+temp=max_t(u32,temp,+gpmc_t->clk_activation*1000+dev_t->t_ach);+temp=max_t(u32,temp,(gpmc_t->adv_rd_off++gpmc_ticks_to_ns(dev_t->cyc_aavdh_oe))*1000);+}+gpmc_t->oe_on=gpmc_round_ps_to_ticks(temp)/1000;++/* access */+/* any scope for improvement ?, by combining oe_on & clk_activation,+*needtocheckwhetheraccess=clk_activation+roundtosyncclk?+*/+temp=max_t(u32,dev_t->t_iaa,dev_t->cyc_iaa*gpmc_t->sync_clk);+temp+=gpmc_t->clk_activation*1000;+if(dev_t->cyc_oe)+temp=max_t(u32,temp,(gpmc_t->oe_on++gpmc_ticks_to_ns(dev_t->cyc_oe))*1000);+gpmc_t->access=gpmc_round_ps_to_ticks(temp)/1000;++gpmc_t->oe_off=gpmc_t->access+gpmc_ticks_to_ns(1);+gpmc_t->cs_rd_off=gpmc_t->oe_off;++/* rd_cycle */+temp=max_t(u32,dev_t->t_cez_r,dev_t->t_oez);+temp=gpmc_round_ps_to_sync_clk(temp,gpmc_t->sync_clk)++gpmc_t->access*1000;+/* barter t_ce_rdyz with t_cez_r ? */+if(dev_t->t_ce_rdyz)+temp=max_t(u32,temp,+gpmc_t->cs_rd_off*1000+dev_t->t_ce_rdyz);+gpmc_t->rd_cycle=gpmc_round_ps_to_ticks(temp)/1000;++return0;+}++staticintgpmc_calc_sync_write_timings(structgpmc_timings*gpmc_t,+structgpmc_device_timings*dev_t)+{+boolmux=dev_t->mux;+u32temp;++/* adv_wr_off */+temp=dev_t->t_avdp_w;+if(mux){+temp=max_t(u32,temp,+gpmc_t->clk_activation*1000+dev_t->t_avdh);+temp=max_t(u32,+(gpmc_t->adv_on+gpmc_ticks_to_ns(1))*1000,temp);+}+gpmc_t->adv_wr_off=gpmc_round_ps_to_ticks(temp)/1000;++/* wr_data_mux_bus */+temp=max_t(u32,dev_t->t_weasu,+gpmc_t->clk_activation*1000+dev_t->t_rdyo);+/* shouldn't mux be kept as a whole for wr_data_mux_bus ?,+*andinthatcaseremembertohandlewe_onproperly+*/+if(mux){+temp=max_t(u32,temp,+gpmc_t->adv_wr_off*1000+dev_t->t_aavdh);+temp=max_t(u32,temp,(gpmc_t->adv_wr_off++gpmc_ticks_to_ns(dev_t->cyc_aavdh_we))*1000);+}+gpmc_t->wr_data_mux_bus=gpmc_round_ps_to_ticks(temp)/1000;++/* we_on */+if(gpmc_capability&GPMC_HAS_WR_DATA_MUX_BUS)+gpmc_t->we_on=gpmc_round_ps_to_ticks(dev_t->t_weasu)/1000;+else+gpmc_t->we_on=gpmc_t->wr_data_mux_bus;++/* wr_access */+/* gpmc_capability check reqd ? , even if not, will not harm */+gpmc_t->wr_access=gpmc_t->access;++/* we_off */+temp=gpmc_t->we_on*1000+dev_t->t_wpl;+temp=max_t(u32,temp,+(gpmc_t->wr_access+gpmc_ticks_to_ns(1))*1000);+temp=max_t(u32,temp,+(gpmc_t->we_on+gpmc_ticks_to_ns(dev_t->cyc_wpl))*1000);+gpmc_t->we_off=gpmc_round_ps_to_ticks(temp)/1000;++gpmc_t->cs_wr_off=gpmc_round_ps_to_ticks(gpmc_t->we_off*1000++dev_t->t_wph)/1000;++/* wr_cycle */+temp=gpmc_round_ps_to_sync_clk(dev_t->t_cez_w,gpmc_t->sync_clk);+temp+=gpmc_t->wr_access*1000;+/* barter t_ce_rdyz with t_cez_w ? */+if(dev_t->t_ce_rdyz)+temp=max_t(u32,temp,+gpmc_t->cs_wr_off*1000+dev_t->t_ce_rdyz);+gpmc_t->wr_cycle=gpmc_round_ps_to_ticks(temp)/1000;++return0;+}++staticintgpmc_calc_async_read_timings(structgpmc_timings*gpmc_t,+structgpmc_device_timings*dev_t)+{+boolmux=dev_t->mux;+u32temp;++/* adv_rd_off */+temp=dev_t->t_avdp_r;+if(mux)+temp=max_t(u32,+(gpmc_t->adv_on+gpmc_ticks_to_ns(1))*1000,temp);+gpmc_t->adv_rd_off=gpmc_round_ps_to_ticks(temp)/1000;++/* oe_on */+temp=dev_t->t_oeasu;+if(mux)+temp=max_t(u32,temp,+gpmc_t->adv_rd_off*1000+dev_t->t_aavdh);+gpmc_t->oe_on=gpmc_round_ps_to_ticks(temp)/1000;++/* access */+temp=max_t(u32,dev_t->t_iaa,/* remove t_iaa in async ? */+gpmc_t->oe_on*1000+dev_t->t_oe);+temp=max_t(u32,temp,+gpmc_t->cs_on*1000+dev_t->t_ce);+temp=max_t(u32,temp,+gpmc_t->adv_on*1000+dev_t->t_aa);+gpmc_t->access=gpmc_round_ps_to_ticks(temp)/1000;++gpmc_t->oe_off=gpmc_t->access+gpmc_ticks_to_ns(1);+gpmc_t->cs_rd_off=gpmc_t->oe_off;++/* rd_cycle */+temp=max_t(u32,dev_t->t_rd_cycle,+gpmc_t->cs_rd_off*1000+dev_t->t_cez_r);+temp=max_t(u32,temp,+gpmc_t->oe_off*1000+dev_t->t_oez);+gpmc_t->rd_cycle=gpmc_round_ps_to_ticks(temp)/1000;++return0;+}++staticintgpmc_calc_async_write_timings(structgpmc_timings*gpmc_t,+structgpmc_device_timings*dev_t)+{+boolmux=dev_t->mux;+u32temp;++/* adv_wr_off */+temp=dev_t->t_avdp_w;+if(mux)+temp=max_t(u32,+(gpmc_t->adv_on+gpmc_ticks_to_ns(1))*1000,temp);+gpmc_t->adv_wr_off=gpmc_round_ps_to_ticks(temp)/1000;++/* wr_data_mux_bus */+temp=dev_t->t_weasu;+if(mux){+temp=max_t(u32,temp,+gpmc_t->adv_wr_off*1000+dev_t->t_aavdh);+temp=max_t(u32,temp,(gpmc_t->adv_wr_off++gpmc_ticks_to_ns(dev_t->cyc_aavdh_we))*1000);+}+gpmc_t->wr_data_mux_bus=gpmc_round_ps_to_ticks(temp)/1000;++/* we_on */+if(gpmc_capability&GPMC_HAS_WR_DATA_MUX_BUS)+gpmc_t->we_on=gpmc_round_ps_to_ticks(dev_t->t_weasu)/1000;+else+gpmc_t->we_on=gpmc_t->wr_data_mux_bus;++/* we_off */+temp=gpmc_t->we_on*1000+dev_t->t_wpl;+gpmc_t->we_off=gpmc_round_ps_to_ticks(temp)/1000;++gpmc_t->cs_wr_off=gpmc_round_ps_to_ticks((gpmc_t->we_off*1000++dev_t->t_wph))/1000;++/* wr_cycle */+temp=max_t(u32,dev_t->t_wr_cycle,+gpmc_t->cs_wr_off*1000+dev_t->t_cez_w);+gpmc_t->wr_cycle=gpmc_round_ps_to_ticks(temp)/1000;++return0;+}++staticintgpmc_calc_sync_common_timings(structgpmc_timings*gpmc_t,+structgpmc_device_timings*dev_t)+{+u32temp;++gpmc_t->sync_clk=gpmc_calc_divider(dev_t->clk)*+gpmc_get_fclk_period();++gpmc_t->page_burst_access=gpmc_round_ps_to_sync_clk(+dev_t->t_bacc,+gpmc_t->sync_clk)/1000;++temp=max_t(u32,dev_t->t_ces,dev_t->t_avds);+gpmc_t->clk_activation=gpmc_round_ps_to_ticks(temp)/1000;++if(gpmc_calc_divider(gpmc_t->sync_clk)!=1)+return0;++if(dev_t->ce_xdelay)+gpmc_t->bool_timings.cs_extra_delay=true;+if(dev_t->avd_xdelay)+gpmc_t->bool_timings.adv_extra_delay=true;+if(dev_t->oe_xdelay)+gpmc_t->bool_timings.oe_extra_delay=true;+if(dev_t->we_xdelay)+gpmc_t->bool_timings.we_extra_delay=true;++return0;+}++staticintgpmc_calc_common_timings(structgpmc_timings*gpmc_t,+structgpmc_device_timings*dev_t)+{+u32temp;++/* cs_on */+gpmc_t->cs_on=gpmc_round_ns_to_ticks(dev_t->t_ceasu/1000);++/* adv_on */+temp=dev_t->t_avdasu;+if(dev_t->t_ce_avd)+temp=max_t(u32,temp,+gpmc_t->cs_on*1000+dev_t->t_ce_avd);+gpmc_t->adv_on=gpmc_round_ns_to_ticks(temp/1000);++if(dev_t->sync_write||dev_t->sync_read)+gpmc_calc_sync_common_timings(gpmc_t,dev_t);++return0;+}++intgpmc_calc_timings(structgpmc_timings*gpmc_t,+structgpmc_device_timings*dev_t)+{+memset(gpmc_t,0,sizeof(*gpmc_t));++gpmc_calc_common_timings(gpmc_t,dev_t);++if(dev_t->sync_read)+gpmc_calc_sync_read_timings(gpmc_t,dev_t);+else+gpmc_calc_async_read_timings(gpmc_t,dev_t);++if(dev_t->sync_write)+gpmc_calc_sync_write_timings(gpmc_t,dev_t);+else+gpmc_calc_async_write_timings(gpmc_t,dev_t);++return0;+}+staticint__initgpmc_init(void){u32l;
@@ -152,6 +152,67 @@ struct gpmc_timings {structgpmc_bool_timingsbool_timings;};+/* Device timings in picoseconds */+structgpmc_device_timings{+u32t_ceasu;/* address setup to CS valid */+u32t_avdasu;/* address setup to ADV valid */+/* XXX: try to combine t_avdp_r & t_avdp_w. Issue is+*oftusbusingthesetimingsevenforsyncwhilst+*ideallyforadv_rd/(wr)_offitshouldhaveconsidered+*t_avdhinstead.Thisindirectlynecessitatesr/w+*variationsoft_avdpasitispossibletohaveone+*sync&otherasync+*/+u32t_avdp_r;/* ADV low time (what about t_cer ?) */+u32t_avdp_w;+u32t_aavdh;/* address hold time */+u32t_oeasu;/* address setup to OE valid */+u32t_aa;/* access time from ADV assertion */+u32t_iaa;/* initial access time */+u32t_oe;/* access time from OE assertion */+u32t_ce;/* access time from CS asertion */+u32t_rd_cycle;/* read cycle time */+u32t_cez_r;/* read CS deassertion to high Z */+u32t_cez_w;/* write CS deassertion to high Z */+u32t_oez;/* OE deassertion to high Z */+u32t_weasu;/* address setup to WE valid */+u32t_wpl;/* write assertion time */+u32t_wph;/* write deassertion time */+u32t_wr_cycle;/* write cycle time */++u32clk;+u32t_bacc;/* burst access valid clock to output delay */+u32t_ces;/* CS setup time to clk */+u32t_avds;/* ADV setup time to clk */+u32t_avdh;/* ADV hold time from clk */+u32t_ach;/* address hold time from clk */+u32t_rdyo;/* clk to ready valid */++u32t_ce_rdyz;/* XXX: description ?, or use t_cez instead */+u32t_ce_avd;/* CS on to ADV on delay */++/* XXX: check the possibility of combining+*cyc_aavhd_oe&cyc_aavdh_we+*/+u8cyc_aavdh_oe;+u8cyc_aavdh_we;+u8cyc_oe;+u8cyc_wpl;+u32cyc_iaa;++boolmux;/* address & data muxed */+boolsync_write;/* synchronous write */+boolsync_read;/* synchronous read */++boolce_xdelay;+boolavd_xdelay;+booloe_xdelay;+boolwe_xdelay;+};++externintgpmc_calc_timings(structgpmc_timings*gpmc_t,+structgpmc_device_timings*dev_t);+structgpmc_nand_regs{void__iomem*gpmc_status;void__iomem*gpmc_nand_command;
@@ -58,35 +59,22 @@ static struct gpmc_timings omap2_onenand_calc_async_timings(void)constintt_aa=76;constintt_oe=20;constintt_cez=20;/* max of t_cez, t_oez */-constintt_ds=30;constintt_wpl=40;constintt_wph=30;-memset(&t,0,sizeof(t));-t.sync_clk=0;-t.cs_on=0;-t.adv_on=0;--/* Read */-t.adv_rd_off=gpmc_round_ns_to_ticks(max_t(int,t_avdp,t_cer));-t.oe_on=t.adv_rd_off+gpmc_round_ns_to_ticks(t_aavdh);-t.access=t.adv_on+gpmc_round_ns_to_ticks(t_aa);-t.access=max_t(int,t.access,t.cs_on+gpmc_round_ns_to_ticks(t_ce));-t.access=max_t(int,t.access,t.oe_on+gpmc_round_ns_to_ticks(t_oe));-t.oe_off=t.access+gpmc_round_ns_to_ticks(1);-t.cs_rd_off=t.oe_off;-t.rd_cycle=t.cs_rd_off+gpmc_round_ns_to_ticks(t_cez);--/* Write */-t.adv_wr_off=t.adv_rd_off;-t.we_on=t.oe_on;-if(cpu_is_omap34xx()){-t.wr_data_mux_bus=t.we_on;-t.wr_access=t.we_on+gpmc_round_ns_to_ticks(t_ds);-}-t.we_off=t.we_on+gpmc_round_ns_to_ticks(t_wpl);-t.cs_wr_off=t.we_off+gpmc_round_ns_to_ticks(t_wph);-t.wr_cycle=t.cs_wr_off+gpmc_round_ns_to_ticks(t_cez);+memset(&dev_t,0,sizeof(dev_t));++dev_t.mux=true;+dev_t.t_avdp_r=dev_t.t_avdp_w=max_t(int,t_avdp,t_cer)*1000;+dev_t.t_aavdh=t_aavdh*1000;+dev_t.t_aa=t_aa*1000;+dev_t.t_ce=t_ce*1000;+dev_t.t_oe=t_oe*1000;+dev_t.t_cez_r=dev_t.t_cez_w=t_cez*1000;+dev_t.t_wpl=t_wpl*1000;+dev_t.t_wph=t_wph*1000;++gpmc_calc_timings(&t,&dev_t);returnt;}
@@ -172,16 +160,15 @@ static struct gpmc_timingsomap2_onenand_calc_sync_timings(structomap_onenand_platform_data*cfg,intfreq){+structgpmc_device_timingsdev_t;structgpmc_timingst;constintt_cer=15;constintt_avdp=12;constintt_cez=20;/* max of t_cez, t_oez */-constintt_ds=30;constintt_wpl=40;constintt_wph=30;intmin_gpmc_clk_period,t_ces,t_avds,t_avdh,t_ach,t_aavdh,t_rdyo;-intdiv,fclk_offset_ns,fclk_offset,gpmc_clk_ns;-intticks_cez;+intdiv,gpmc_clk_ns;if(cfg->flags&ONENAND_SYNC_READ)onenand_flags=ONENAND_FLAG_SYNCREAD;
@@ -248,62 +235,33 @@ omap2_onenand_calc_sync_timings(struct omap_onenand_platform_data *cfg,latency=4;/* Set synchronous read timings */-memset(&t,0,sizeof(t));+memset(&dev_t,0,sizeof(dev_t));-if(div==1){-t.bool_timings.cs_extra_delay=true;-t.bool_timings.adv_extra_delay=true;-t.bool_timings.oe_extra_delay=true;-t.bool_timings.we_extra_delay=true;-}--t.sync_clk=min_gpmc_clk_period;-t.cs_on=0;-t.adv_on=0;-fclk_offset_ns=gpmc_round_ns_to_ticks(max_t(int,t_ces,t_avds));-fclk_offset=gpmc_ns_to_ticks(fclk_offset_ns);-t.page_burst_access=gpmc_clk_ns;--/* 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;-ticks_cez=((gpmc_ns_to_ticks(t_cez)+div-1)/div)*div;-t.rd_cycle=gpmc_ticks_to_ns(fclk_offset+(latency+1)*div+-ticks_cez);--t.clk_activation=fclk_offset_ns;--/* Write */-if(onenand_flags&ONENAND_FLAG_SYNCWRITE){-t.adv_wr_off=t.adv_rd_off;-t.we_on=0;-t.we_off=t.cs_rd_off;-t.cs_wr_off=t.cs_rd_off;-t.wr_cycle=t.rd_cycle;-if(cpu_is_omap34xx()){-t.wr_data_mux_bus=gpmc_ticks_to_ns(fclk_offset+-gpmc_ps_to_ticks(min_gpmc_clk_period+-t_rdyo*1000));-t.wr_access=t.access;-}-}else{-t.adv_wr_off=gpmc_round_ns_to_ticks(max_t(int,-t_avdp,t_cer));-t.we_on=t.adv_wr_off+gpmc_round_ns_to_ticks(t_aavdh);-t.we_off=t.we_on+gpmc_round_ns_to_ticks(t_wpl);-t.cs_wr_off=t.we_off+gpmc_round_ns_to_ticks(t_wph);-t.wr_cycle=t.cs_wr_off+gpmc_round_ns_to_ticks(t_cez);-if(cpu_is_omap34xx()){-t.wr_data_mux_bus=t.we_on;-t.wr_access=t.we_on+gpmc_round_ns_to_ticks(t_ds);-}+dev_t.mux=true;+dev_t.sync_read=true;+if(onenand_flags&ONENAND_FLAG_SYNCWRITE)+dev_t.sync_write=true;+else{+dev_t.t_avdp_w=max(t_avdp,t_cer)*1000;+dev_t.t_wpl=t_wpl*1000;+dev_t.t_wph=t_wph*1000;+dev_t.t_aavdh=t_aavdh*1000;}+dev_t.ce_xdelay=true;+dev_t.avd_xdelay=true;+dev_t.oe_xdelay=true;+dev_t.we_xdelay=true;+dev_t.clk=dev_t.t_bacc=min_gpmc_clk_period;+dev_t.t_ces=t_ces*1000;+dev_t.t_avds=t_avds*1000;+dev_t.t_avdh=t_avdh*1000;+dev_t.t_ach=t_ach*1000;+dev_t.cyc_iaa=(latency+1);+dev_t.t_cez_r=dev_t.t_cez_w=t_cez*1000;+dev_t.cyc_aavdh_oe=1;+dev_t.t_rdyo=t_rdyo*1000+min_gpmc_clk_period;++gpmc_calc_timings(&t,&dev_t);returnt;}
From: Igor Grinberg <hidden> Date: 2012-08-21 11:37:00
Hi Afzal,
Thanks for the patches!
On 08/21/12 13:45, Afzal Mohammed wrote:
Helper function for updating nand platform data has been
added the capability to take timing structure arguement.
Usage of omap_nand_flash_init() has been replaced by modifed
one, omap_nand_flash_init was doing things similar to
board_nand_init except that NAND CS# were being acquired
based on bootloader setting. As CS# is hardwired for a given
board, acquiring gpmc CS# has been removed, and updated with
the value on board.
NAND CS# used in beagle board & omap3evm was found to be CS0.
Thomas Weber [off-list ref] reported
that value of devkit8000 to be CS0. Overo board was found
to be using CS0 based on u-boot, while google grep says
omap3touchbook too has CS0.
Signed-off-by: Afzal Mohammed <redacted>
Reviewed-by: Jon Hunter <redacted>
Acked-by: Igor Grinberg <redacted>
--
Regards,
Igor.
From: Jon Hunter <hidden> Date: 2012-08-22 02:08:39
On 08/21/2012 05:45 AM, Afzal Mohammed wrote:
quoted hunk
Newer IP's have wr_access and wr_data_mux_bus fields. Use
IP revision values to determine availability of these
fields and hence decide on whether to configure them.
Signed-off-by: Afzal Mohammed <redacted>
---
arch/arm/mach-omap2/gpmc.c | 12 ++++++++++--
1 files changed, 10 insertions(+), 2 deletions(-)
@@ -85,6 +85,11 @@#define ENABLE_PREFETCH (0x1 << 7)#define DMA_MPU_MODE 2+#define GPMC_REVISION_MAJOR(l) ((l >> 4) & 0xf)++#define GPMC_HAS_WR_ACCESS 0x1+#define GPMC_HAS_WR_DATA_MUX_BUS 0x2+/* XXX: Only NAND irq has been considered,currently these are the only ones used*/#define GPMC_NR_IRQ 2
@@ -131,6 +136,7 @@ static DEFINE_SPINLOCK(gpmc_mem_lock);staticunsignedintgpmc_cs_map;/* flag for cs which are initialized */staticintgpmc_ecc_used=-EINVAL;/* cs using ecc engine */+staticunsignedgpmc_capability;staticvoid__iomem*gpmc_base;staticstructclk*gpmc_l3_clk;
@@ -356,10 +362,10 @@ int gpmc_cs_set_timings(int cs, const struct gpmc_timings *t)GPMC_SET_ONE(GPMC_CS_CONFIG1,18,19,wait_monitoring);GPMC_SET_ONE(GPMC_CS_CONFIG1,25,26,clk_activation);-if(cpu_is_omap34xx()){+if(gpmc_capability&GPMC_HAS_WR_DATA_MUX_BUS)GPMC_SET_ONE(GPMC_CS_CONFIG6,16,19,wr_data_mux_bus);+if(gpmc_capability&GPMC_HAS_WR_ACCESS)GPMC_SET_ONE(GPMC_CS_CONFIG6,24,28,wr_access);-}/* caller is expected to have initialized CONFIG1 to cover*atleastsyncvsasync
@@ -922,6 +928,8 @@ static int __init gpmc_init(void)clk_enable(gpmc_l3_clk);l=gpmc_read_reg(GPMC_REVISION);+if(GPMC_REVISION_MAJOR(l)>0x4)+gpmc_capability=GPMC_HAS_WR_ACCESS|GPMC_HAS_WR_DATA_MUX_BUS;printk(KERN_INFO"GPMC revision %d.%d\n",(l>>4)&0x0f,l&0x0f);/* Set smart idle mode and automatic L3 clock gating */l=gpmc_read_reg(GPMC_SYSCONFIG);
From: Jon Hunter <hidden> Date: 2012-08-22 02:11:51
On 08/21/2012 05:45 AM, Afzal Mohammed wrote:
quoted hunk
Divider value for a certain sync clk is determined solely
based on gpmc fclk. CS# does not have any role here, thus
remove presence of CS# in clock divider calculation API.
Signed-off-by: Afzal Mohammed <redacted>
---
arch/arm/mach-omap2/gpmc-onenand.c | 3 +--
arch/arm/mach-omap2/gpmc.c | 4 ++--
arch/arm/plat-omap/include/plat/gpmc.h | 2 +-
3 files changed, 4 insertions(+), 5 deletions(-)
@@ -313,7 +313,7 @@ static int set_gpmc_timing_reg(int cs, int reg, int st_bit, int end_bit,return-1#endif-intgpmc_cs_calc_divider(intcs,unsignedintsync_clk)+intgpmc_calc_divider(unsignedintsync_clk){intdiv;u32l;
From: Jon Hunter <hidden> Date: 2012-08-23 02:58:44
Hi Afzal,
On 08/21/2012 05:45 AM, Afzal Mohammed wrote:
quoted hunk
Presently there are three peripherals that gets it timing
by runtime calculation. Those peripherals can work with
frequency scaling that affects gpmc clock. But timing
calculation for them are in different ways.
Here a generic runtime calculation method is proposed. Input
to this function were selected so that they represent timing
variables that are present in peripheral datasheets. Motive
behind this was to achieve DT bindings for the inputs as is.
Even though a few of the tusb6010 timings could not be made
directly related to timings normally found on peripherals,
expressions used were translated to those that could be
justified.
There are possibilities of improving the calculations, like
calculating timing for read & write operations in a more
similar way. Expressions derived here were tested for async
onenand on omap3evm (as vanilla Kernel does not have omap3evm
onenand support, local patch was used). Other peripherals,
tusb6010, smc91x calculations were validated by simulating
on omap3evm.
Regarding "we_on" for onenand async, it was found that even
for muxed address/data, it need not be greater than
"adv_wr_off", but rather could be derived from write setup
time for peripheral from start of access time, hence would
more be in line with peripheral timings. With this method
it was working fine. If it is required in some cases to
have "we_on" same as "wr_data_mux_bus" (i.e. greater than
"adv_wr_off"), another variable could be added to indicate
it. But such a requirement is not expected though.
Whole of this exercise is being done to achieve driver and
DT conversion. If timings could not be calculated in a
peripheral agnostic way, either gpmc driver would have to
be peripheral gnostic or a wrapper arrangement over gpmc
driver would be required.
Signed-off-by: Afzal Mohammed <redacted>
---
arch/arm/mach-omap2/gpmc.c | 302 ++++++++++++++++++++++++++++++++
arch/arm/plat-omap/include/plat/gpmc.h | 61 +++++++
2 files changed, 363 insertions(+), 0 deletions(-)
@@ -233,6 +233,18 @@ unsigned int gpmc_round_ns_to_ticks(unsigned int time_ns)returnticks*gpmc_get_fclk_period()/1000;}+unsignedintgpmc_ticks_to_ps(unsignedintticks)+{+returnticks*gpmc_get_fclk_period();+}++unsignedintgpmc_round_ps_to_ticks(unsignedinttime_ps)+{+unsignedlongticks=gpmc_ps_to_ticks(time_ps);++returnticks*gpmc_get_fclk_period();+}+staticinlinevoidgpmc_cs_modify_reg(intcs,intreg,u32mask,boolvalue){u32l;
@@ -884,6 +896,296 @@ static void __init gpmc_mem_init(void)}}+staticu32gpmc_round_ps_to_sync_clk(u32time_ps,u32sync_clk)+{+u32temp;+intdiv;++div=gpmc_calc_divider(sync_clk);+temp=gpmc_ps_to_ticks(time_ps);+temp=(temp+div-1)/div;+returngpmc_ticks_to_ps(temp*div);+}++/* can the cycles be avoided ? */
Why pico seconds and not nanoseconds? I understand you may need to
temporarily convert to pico-secs for rounding, but when providing timing
it seems nano-secs is more suitable.
+struct gpmc_device_timings {
+ u32 t_ceasu; /* address setup to CS valid */
+ u32 t_avdasu; /* address setup to ADV valid */
+ /* XXX: try to combine t_avdp_r & t_avdp_w. Issue is
+ * of tusb using these timings even for sync whilst
+ * ideally for adv_rd/(wr)_off it should have considered
+ * t_avdh instead. This indirectly necessitates r/w
+ * variations of t_avdp as it is possible to have one
+ * sync & other async
+ */
+ u32 t_avdp_r; /* ADV low time (what about t_cer ?) */
+ u32 t_avdp_w;
+ u32 t_aavdh; /* address hold time */
+ u32 t_oeasu; /* address setup to OE valid */
+ u32 t_aa; /* access time from ADV assertion */
+ u32 t_iaa; /* initial access time */
+ u32 t_oe; /* access time from OE assertion */
+ u32 t_ce; /* access time from CS asertion */
+ u32 t_rd_cycle; /* read cycle time */
+ u32 t_cez_r; /* read CS deassertion to high Z */
+ u32 t_cez_w; /* write CS deassertion to high Z */
+ u32 t_oez; /* OE deassertion to high Z */
+ u32 t_weasu; /* address setup to WE valid */
+ u32 t_wpl; /* write assertion time */
+ u32 t_wph; /* write deassertion time */
+ u32 t_wr_cycle; /* write cycle time */
+
+ u32 clk;
+ u32 t_bacc; /* burst access valid clock to output delay */
+ u32 t_ces; /* CS setup time to clk */
+ u32 t_avds; /* ADV setup time to clk */
+ u32 t_avdh; /* ADV hold time from clk */
+ u32 t_ach; /* address hold time from clk */
+ u32 t_rdyo; /* clk to ready valid */
+
+ u32 t_ce_rdyz; /* XXX: description ?, or use t_cez instead */
+ u32 t_ce_avd; /* CS on to ADV on delay */
+
+ /* XXX: check the possibility of combining
+ * cyc_aavhd_oe & cyc_aavdh_we
+ */
+ u8 cyc_aavdh_oe;
+ u8 cyc_aavdh_we;
+ u8 cyc_oe;
+ u8 cyc_wpl;
+ u32 cyc_iaa;
+
+ bool mux; /* address & data muxed */
+ bool sync_write; /* synchronous write */
+ bool sync_read; /* synchronous read */
+
+ bool ce_xdelay;
+ bool avd_xdelay;
+ bool oe_xdelay;
+ bool we_xdelay;
+};
I am a little concerned about the above timings structure. For example,
if I am adding support for a new devices it is not clear ...
1. Which are required
2. Which are applicable for async, sync, address-data multiplexed etc.
3. Exactly how these relate to the fields in the gpmc registers.
I understand that this is based upon how timings for onenand and tusb
are being calculated today, but I am not sure that this is the way to go
for all devices. Personally, I would like to see us get away from how
those devices are calculating timings for any new device.
In general, I am concerned that we are abstracting the timings further
from the actual register fields. For example, the timings parameter
"t_ceasu" is described "address setup to CS valid" which is not
incorrect but this value is really just programming the CSONTIME field
and so why not call this cs_on?
So although this may consolidate how the timings are calculated today, I
am concerned it will be confusing to add timings for a new device. At
least if I am calculating timings, I am taking the timing information
for the device and translating that to the how I need to program the
gpmc register fields.
Cheers
Jon
So although this may consolidate how the timings are calculated today, I
am concerned it will be confusing to add timings for a new device. At
least if I am calculating timings, I am taking the timing information
for the device and translating that to the how I need to program the
gpmc register fields.
Yes agreed. Also as some values make sense only in cycles, converting them
back and forth to time is wrong. So at least some values should have an
option to specify them in cycles directly, and then ignore any time based
values.
Regards,
Tony
Hi Tony,
On Sat, Aug 25, 2012 at 01:16:30, Tony Lindgren wrote:
This hangs n800 during the boot.
Shall I read the above as n800 boot without patch 10/10,
but with the other patches in this series ?
As per the board file, n800 has tusb6010 as well as
OneNAND in sync read & async write mode, was OneNAND
working without 10/10.
Do you have any idea, which timing could have gone wrong,
can you please sent me DEBUG enabled gpmc log with and
without 10/10.
Regards
Afzal
Hi Jon,
On Thu, Aug 23, 2012 at 08:28:44, Hunter, Jon wrote:
On 08/21/2012 05:45 AM, Afzal Mohammed wrote:
quoted
+/* can the cycles be avoided ? */
What is the above comment referring too?
This was added in the initial stages and refers to the usage of
cycles in struct gpmc_device_timings. I wanted to avoid usage
of cycles, but it seems it is required logically as well. This
was mentioned as a note for future to find out whether at any
future point of time this can be removed.
quoted
+/* Device timings in picoseconds */
Why pico seconds and not nanoseconds? I understand you may need to
temporarily convert to pico-secs for rounding, but when providing timing
it seems nano-secs is more suitable.
For more accuracy, if you see some of the tusb6010 calculation
are in picoseconds, this can be true for any device although
only tusb does so. If we hold on to picoseconds till the last
moment, value would be more accurate. For eg. 300 ps has to be
used in the case of tusb, and with ns, it can't be accounted for
I am a little concerned about the above timings structure. For example,
if I am adding support for a new devices it is not clear ...
1. Which are required
2. Which are applicable for async, sync, address-data multiplexed etc.
3. Exactly how these relate to the fields in the gpmc registers.
Please see at the end
I understand that this is based upon how timings for onenand and tusb
are being calculated today, but I am not sure that this is the way to go
for all devices. Personally, I would like to see us get away from how
those devices are calculating timings for any new device.
You cannot do away with many of the those, as logically they
are right. Eg. read data should be available at access time,
assuming a zero data hold time, we can very well derive an
expression as,
read de-assertion time (oe_off) = access time plus 1 gpmc clock,
and this is what the existing calculations do, and also the
generic routine. There could be other constraints, but this
certainly should be one (I do realize that oe_off could be
optimized to be less than access time, by relying on read
hold time, then again effect of it would be in similar way
for different peripherals, but let's forget about
optimization in the beginning)
In general, I am concerned that we are abstracting the timings further
from the actual register fields. For example, the timings parameter
"t_ceasu" is described "address setup to CS valid" which is not
incorrect but this value is really just programming the CSONTIME field
and so why not call this cs_on?
Timing fields of struct gpmc_device_timings are selected such
that they should be bindable by DT. At least one of the peripheral
datasheet has these fields. If user knows timings in terms of
gpmc values, he can directly use struct gpmc_timings, but
then all the values should be updated in struct gpmc_timings.
User should not update some of the values in terms of
peripheral timings, others in terms of gpmc timings, that
would make things complex and does not seem the right way
to me.
cs_on and other gpmc aware timings could be binded by DT, not as
peripheral timing, but as gpmc timing.
Bindings for peripheral DT timings should be something that
can be obtained from peripheral datasheet, here accidentally
it is same as gpmc timings, but not most timings are this way.
Also there could be other constraints that can come for cs_on,
even though I have not come across any till now.
So although this may consolidate how the timings are calculated today, I
am concerned it will be confusing to add timings for a new device. At
least if I am calculating timings, I am taking the timing information
for the device and translating that to the how I need to program the
gpmc register fields.
If I am not wrong, GPMC IP has been present for around a
decade, and so far I have not come across any generic time
calculation method that can be applied to all peripherals.
Getting to work same peripheral for a different gpmc
frequency is another problem.
Here we are trying to generalize based on the understanding of
gpmc & peripheral timings, as well as by a kind of reverse
engineering without most of the hardware or datasheet. Think of
this as an attempt to create one, let it evolve and become a
robust one. If a new user want to add a peripheral, let him add
support, input timings are selected such that it is found in
peripheral datasheet, if that does not work, let him try
to add any new timing field or alter existing expression
as he deems to be proper and verify that it does not break
others. And I can help provided I am not heavily loaded
with other works.
And at least for initial users, they are expected to have
some grasp on how to calculate timings, such a user will
not be much worried about your 3 concerns above, anyway as
of now they need to have a good grasp on it.
Meanwhile I will try to document more.
Regards
Afzal
Hi Tony,
On Sat, Aug 25, 2012 at 01:24:47, Tony Lindgren wrote:
Yes agreed. Also as some values make sense only in cycles, converting them
back and forth to time is wrong. So at least some values should have an
option to specify them in cycles directly, and then ignore any time based
values.
Which values in struct gpmc_device_timings are you referring to ?
Values that need to have an option to specify in cycles has been provided
such an option, while not updating time based field value, you can
achieve what you mentioned above. But if you want that to be handled in
in generic routine, I will do so.
Regards
Afzal
Hi Afzal,
On 21.08.2012 12:41, Afzal Mohammed wrote:
This series provides a generic gpmc timing calculation routine. There
were three peripherals (OneNAND, tusb6010, smc91x) using custom timing
calculations, they are migrated to use the generic timing calculation.
Such a generic routine would help create a driver out of gpmc platform
code, which would be peripheral agnostic and thus lead to DT finally.
Input to generic timing calculation routine would be gpmc peripheral
timings, output - translated timings that gpmc can understand. Later,
to DT'ify, gpmc peripheral timings could be passed through DT. Input
timings that has been used here are selected such that it represents
those that are present in peripheral timing datasheets.
What I don't understand yet about this new approach is where the gpmc
client code should live in. In order to probe the drivers via DT, each
driver would need to call the gpmc support functions itself, right? Is
the plan to obsolete helper functions like gpmc_nand_init() and move
that functionality to the drivers?
I applied these patches locally and would like to help get the NAND
controller on my AX33xx DT-driven board going. Let me know if I can do
anything here.
Regards,
Daniel
Hi Daniel,
On Mon, Aug 27, 2012 at 17:46:17, Daniel Mack wrote:
quoted
Such a generic routine would help create a driver out of gpmc platform
code, which would be peripheral agnostic and thus lead to DT finally.
Input to generic timing calculation routine would be gpmc peripheral
timings, output - translated timings that gpmc can understand. Later,
to DT'ify, gpmc peripheral timings could be passed through DT. Input
timings that has been used here are selected such that it represents
those that are present in peripheral timing datasheets.
What I don't understand yet about this new approach is where the gpmc
client code should live in. In order to probe the drivers via DT, each
driver would need to call the gpmc support functions itself, right? Is
By gpmc client code, if you are referring to helper functions in
mach-omap2/gpmc-*, if things go as per plan, with DT, they will go away
and gpmc driver would handle those based on the information from DT.
No change would be needed for client drivers like smc91x ethernet drivers,
but NAND driver may need some change to live with DT as it is tightly
coupled with GPMC.
the plan to obsolete helper functions like gpmc_nand_init() and move
that functionality to the drivers?
Yes, plan is to obsolete the functions like gpmc_nand_init, those tasks
would be handled by gpmc driver
I applied these patches locally and would like to help get the NAND
controller on my AX33xx DT-driven board going. Let me know if I can do
anything here.
Thanks for offering the help, will take liberty to ask you for help
when required. BTW, do you have any OMAP family boards ?
Regards
Afzal
On Mon, Aug 27, 2012 at 17:46:17, Daniel Mack wrote:
quoted
quoted
Such a generic routine would help create a driver out of gpmc platform
code, which would be peripheral agnostic and thus lead to DT finally.
Input to generic timing calculation routine would be gpmc peripheral
timings, output - translated timings that gpmc can understand. Later,
to DT'ify, gpmc peripheral timings could be passed through DT. Input
timings that has been used here are selected such that it represents
those that are present in peripheral timing datasheets.
What I don't understand yet about this new approach is where the gpmc
client code should live in. In order to probe the drivers via DT, each
driver would need to call the gpmc support functions itself, right? Is
By gpmc client code, if you are referring to helper functions in
mach-omap2/gpmc-*, if things go as per plan, with DT, they will go away
and gpmc driver would handle those based on the information from DT.
So the GPMC driver is the one that is matched from DT, and the NAND
driver will the be instanciated from the (generic) GPMC driver?
No change would be needed for client drivers like smc91x ethernet drivers,
but NAND driver may need some change to live with DT as it is tightly
coupled with GPMC.
NAND drivers also typically parse partition entries that are children of
the DT node the are owning.
quoted
the plan to obsolete helper functions like gpmc_nand_init() and move
that functionality to the drivers?
Yes, plan is to obsolete the functions like gpmc_nand_init, those tasks
would be handled by gpmc driver
quoted
I applied these patches locally and would like to help get the NAND
controller on my AX33xx DT-driven board going. Let me know if I can do
anything here.
Thanks for offering the help, will take liberty to ask you for help
when required. BTW, do you have any OMAP family boards ?
I do have a beaglboard xM, but the board I'm currently working with is a
custom one based on an AM33xx.
Thanks,
Daniel
Hi Daniel,
On Mon, Aug 27, 2012 at 19:00:32, Daniel Mack wrote:
So the GPMC driver is the one that is matched from DT, and the NAND
driver will the be instanciated from the (generic) GPMC driver?
I think you were referring to nand device being instantiated from
gpmc driver?, hence resulting in nand driver getting probed. What I
have in mind is to represent peripherals connected to gpmc as childs
of gpmc node. For devices like smsc911x, as they already have DT
bindings, creating an of device for childs by parsing DT hopefully
should relieve task of handling their platform data.
Similarly for nand, creating an of device is to be done by gpmc
driver.
Once the bigger issue of timing is resolved, I am planning to
implement DT as said above
Also for the existing boards on non-DT boot, plan is to keep the
existing interface as such, with no change needed in board files.
quoted
No change would be needed for client drivers like smc91x ethernet drivers,
but NAND driver may need some change to live with DT as it is tightly
coupled with GPMC.
NAND drivers also typically parse partition entries that are children of
the DT node the are owning.
Yes, in addition to the above, omap nand driver would need to determine a
few gpmc specific like transfer mode.
I do have a beaglboard xM, but the board I'm currently working with is a
custom one based on an AM33xx.
I was in need of a board like rx51, n800 that currently calculate gpmc
timings at runtime to test the generic timing function, np, thanks again
Regards
Afzal
From: Jon Hunter <hidden> Date: 2012-08-27 20:30:13
Hi Afzal,
On 08/27/2012 05:37 AM, Mohammed, Afzal wrote:
On Thu, Aug 23, 2012 at 08:28:44, Hunter, Jon wrote:
[snip]
quoted
I understand that this is based upon how timings for onenand and tusb
are being calculated today, but I am not sure that this is the way to go
for all devices. Personally, I would like to see us get away from how
those devices are calculating timings for any new device.
You cannot do away with many of the those, as logically they
are right. Eg. read data should be available at access time,
assuming a zero data hold time, we can very well derive an
expression as,
I am not saying that we do away with them for current devices, just
maintain them as is.
read de-assertion time (oe_off) = access time plus 1 gpmc clock,
and this is what the existing calculations do, and also the
generic routine. There could be other constraints, but this
certainly should be one (I do realize that oe_off could be
optimized to be less than access time, by relying on read
hold time, then again effect of it would be in similar way
for different peripherals, but let's forget about
optimization in the beginning)
quoted
In general, I am concerned that we are abstracting the timings further
from the actual register fields. For example, the timings parameter
"t_ceasu" is described "address setup to CS valid" which is not
incorrect but this value is really just programming the CSONTIME field
and so why not call this cs_on?
Timing fields of struct gpmc_device_timings are selected such
that they should be bindable by DT. At least one of the peripheral
datasheet has these fields.
Right, but these are not applicable to every device and so I worry this
could be confusing. However, more documentation may help clear this up.
If user knows timings in terms of
gpmc values, he can directly use struct gpmc_timings, but
then all the values should be updated in struct gpmc_timings.
User should not update some of the values in terms of
peripheral timings, others in terms of gpmc timings, that
would make things complex and does not seem the right way
to me.
cs_on and other gpmc aware timings could be binded by DT, not as
peripheral timing, but as gpmc timing.
Bindings for peripheral DT timings should be something that
can be obtained from peripheral datasheet, here accidentally
it is same as gpmc timings, but not most timings are this way.
Also there could be other constraints that can come for cs_on,
even though I have not come across any till now.
quoted
So although this may consolidate how the timings are calculated today, I
am concerned it will be confusing to add timings for a new device. At
least if I am calculating timings, I am taking the timing information
for the device and translating that to the how I need to program the
gpmc register fields.
If I am not wrong, GPMC IP has been present for around a
decade, and so far I have not come across any generic time
calculation method that can be applied to all peripherals.
Yes not an easy problem to solve :-(
Getting to work same peripheral for a different gpmc
frequency is another problem.
Here we are trying to generalize based on the understanding of
gpmc & peripheral timings, as well as by a kind of reverse
engineering without most of the hardware or datasheet. Think of
this as an attempt to create one, let it evolve and become a
robust one. If a new user want to add a peripheral, let him add
support, input timings are selected such that it is found in
peripheral datasheet, if that does not work, let him try
to add any new timing field or alter existing expression
as he deems to be proper and verify that it does not break
others. And I can help provided I am not heavily loaded
with other works.
So long as it is maintainable ;-)
And at least for initial users, they are expected to have
some grasp on how to calculate timings, such a user will
not be much worried about your 3 concerns above, anyway as
of now they need to have a good grasp on it.
I would consider myself to be an initial user and I am concerned,
doesn't that count?
An example, would be the following where you have 4 timing parameters
for access time. You need to dig through the code to understand how
these are being used.
+ u32 t_aa; /* access time from ADV assertion */
+ u32 t_iaa; /* initial access time */
+ u32 t_oe; /* access time from OE assertion */
+ u32 t_ce; /* access time from CS asertion */
Meanwhile I will try to document more.
Yes more documentation is definitely needed.
Cheers
Jon
Hi Jon,
On Tue, Aug 28, 2012 at 02:00:13, Hunter, Jon wrote:
On 08/27/2012 05:37 AM, Mohammed, Afzal wrote:
quoted
And at least for initial users, they are expected to have
some grasp on how to calculate timings, such a user will
not be much worried about your 3 concerns above, anyway as
of now they need to have a good grasp on it.
I would consider myself to be an initial user and I am concerned,
doesn't that count?
Yes sir, what I meant was new users who want to have runtime
calculation using the generic timing routine. For the
peripherals already making use of custom timing routine,
I am into that role too, hence the patches 8-10.
And if you have any board that makes use of existing custom
timing calculation routines (OneNAND, tusb6010 or smc91x),
can you please give this series a try.
Regards
Afzal
Hi Tony,
On Mon, Aug 27, 2012 at 14:04:44, Mohammed, Afzal wrote:
On Sat, Aug 25, 2012 at 01:16:30, Tony Lindgren wrote:
quoted
This hangs n800 during the boot.
Shall I read the above as n800 boot without patch 10/10,
but with the other patches in this series ?
As per the board file, n800 has tusb6010 as well as
OneNAND in sync read & async write mode, was OneNAND
working without 10/10.
Do you have any idea, which timing could have gone wrong,
can you please sent me DEBUG enabled gpmc log with and
without 10/10.
Can you please sent me GPMC DEBUG enabled logs with and
without this series.
Regards
Afzal
Shall I read the above as n800 boot without patch 10/10,
but with the other patches in this series ?
As per the board file, n800 has tusb6010 as well as
OneNAND in sync read & async write mode, was OneNAND
working without 10/10.
Do you have any idea, which timing could have gone wrong,
can you please sent me DEBUG enabled gpmc log with and
without 10/10.
Can you please sent me GPMC DEBUG enabled logs with and
without this series.
Hi Tony,
On Mon, Sep 03, 2012 at 11:04:10, Mohammed, Afzal wrote:
quoted
On Mon, Aug 27, 2012 at 14:04:44, Mohammed, Afzal wrote:
quoted
On Sat, Aug 25, 2012 at 01:16:30, Tony Lindgren wrote:
quoted
quoted
quoted
This hangs n800 during the boot.
Paul reported that n800 stopped booting on OMAP baseline [1]
due to an mmc issue and has posted a solution [2].
Are you facing the same issue ?, if so, then it is not
due to this series.
No that's a separate issue. Your series works except for
this patch makes thing hang.
Regards,
Tony
Shall I read the above as n800 boot without patch 10/10,
but with the other patches in this series ?
As per the board file, n800 has tusb6010 as well as
OneNAND in sync read & async write mode, was OneNAND
working without 10/10.
Do you have any idea, which timing could have gone wrong,
can you please sent me DEBUG enabled gpmc log with and
without 10/10.
Can you please sent me GPMC DEBUG enabled logs with and
without this series.
Hi Tony,
On Mon, Sep 03, 2012 at 11:04:10, Mohammed, Afzal wrote:
quoted
On Mon, Aug 27, 2012 at 14:04:44, Mohammed, Afzal wrote:
quoted
On Sat, Aug 25, 2012 at 01:16:30, Tony Lindgren wrote:
quoted
quoted
quoted
This hangs n800 during the boot.
Paul reported that n800 stopped booting on OMAP baseline [1]
due to an mmc issue and has posted a solution [2].
Are you facing the same issue ?, if so, then it is not
due to this series.
No that's a separate issue. Your series works except for
this patch makes thing hang.
Hi Tony,
On Wed, Sep 12, 2012 at 00:16:06, Tony Lindgren wrote:
Here are the timing changes with and without this patch from
my n800. You can just diff the two files to see some differences.
Hmm.. that was pretty close, OneNAND async,sync as well as
tusb sync values were same.
But some of the tusb async values is less by one. I need
to get it right.
And thanks for sending me the easily diff-able logs.
Regards
Afzal
* Mohammed, Afzal: Wednesday, September 12, 2012 3:20 PM
But some of the tusb async values is less by one. I need
to get it right.
Reason has been identified. It was due to rounding error,
no changes are required in the expressions. Moving
completely to picoseconds resolves the issue.
Can you please try with the attached patch ?
Once it is confirmed that issue is resolved, I will cleanup
gpmc-nand.c too (which would also take care of picoseconds)
Note: As this mail is sent via exchange, I am attaching the
patch so that it reaches you in proper way.
Regards
Afzal
-------------- next part --------------
A non-text attachment was scrubbed...
Name: 0001-gpmc-rounding-error-fix.patch
Type: text/x-patch
Size: 14228 bytes
Desc: 0001-gpmc-rounding-error-fix.patch
URL: <http://lists.infradead.org/pipermail/linux-arm-kernel/attachments/20120914/5b0b4601/attachment.bin>
Hi Tony,
On Fri, Sep 14, 2012 at 15:50:02, Mohammed, Afzal wrote:
* Mohammed, Afzal: Wednesday, September 12, 2012 3:20 PM
quoted
But some of the tusb async values is less by one. I need
to get it right.
Reason has been identified. It was due to rounding error,
no changes are required in the expressions. Moving
completely to picoseconds resolves the issue.
Can you please try with the attached patch ?
Can you please try with patch attached in previous message
of this thread and check whether it makes n800 gpmc
peripherals work properly
Changes as in above mentioned patch has been pasted below again.
Regards
Afzal
---8<------------------------
@@ -289,11 +289,11 @@ static int set_gpmc_timing_reg(int cs, int reg, int st_bit, int end_bit,if(time==0)ticks=0;else-ticks=gpmc_ns_to_ticks(time);+ticks=gpmc_ps_to_ticks(time);nr_bits=end_bit-st_bit+1;if(ticks>=1<<nr_bits){#ifdef DEBUG-printk(KERN_INFO"GPMC CS%d: %-10s* %3d ns, %3d ticks >= %d\n",+pr_info("GPMC CS%d: %-10s* %3d ps, %3d ticks >= %d\n",cs,name,time,ticks,1<<nr_bits);#endifreturn-1;
@@ -302,10 +302,9 @@ static int set_gpmc_timing_reg(int cs, int reg, int st_bit, int end_bit,mask=(1<<nr_bits)-1;l=gpmc_cs_read_reg(cs,reg);#ifdef DEBUG-printk(KERN_INFO-"GPMC CS%d: %-10s: %3d ticks, %3lu ns (was %3i ticks) %3d ns\n",-cs,name,ticks,gpmc_get_fclk_period()*ticks/1000,-(l>>st_bit)&mask,time);+pr_info("GPMC CS%d: %-10s: %3d ticks, %3lu ps (was %3i ticks) %3d ps\n",+cs,name,ticks,gpmc_get_fclk_period()*ticks,+(l>>st_bit)&mask,time);#endifl&=~(mask<<st_bit);l|=ticks<<st_bit;
@@ -385,8 +384,8 @@ int gpmc_cs_set_timings(int cs, const struct gpmc_timings *t)l=gpmc_cs_read_reg(cs,GPMC_CS_CONFIG1);if(l&(GPMC_CONFIG1_READTYPE_SYNC|GPMC_CONFIG1_WRITETYPE_SYNC)){#ifdef DEBUG-printk(KERN_INFO"GPMC CS%d CLK period is %lu ns (div %d)\n",-cs,(div*gpmc_get_fclk_period())/1000,div);+pr_info("GPMC CS%d CLK period is %lu ps (div %d)\n",+cs,div*gpmc_get_fclk_period(),div);#endifl&=~0x03;l|=(div-1);
@@ -922,46 +921,42 @@ static int gpmc_calc_sync_read_timings(struct gpmc_timings *gpmc_t,*indirectlynecessitatesrequirementoft_avdp_r&t_avdp_w*insteadofhavingasinglet_avdp*/-temp=max_t(u32,temp,gpmc_t->clk_activation*1000+-dev_t->t_avdh);-temp=max_t(u32,-(gpmc_t->adv_on+gpmc_ticks_to_ns(1))*1000,temp);+temp=max_t(u32,temp,gpmc_t->clk_activation+dev_t->t_avdh);+temp=max_t(u32,gpmc_t->adv_on+gpmc_ticks_to_ps(1),temp);}-gpmc_t->adv_rd_off=gpmc_round_ps_to_ticks(temp)/1000;+gpmc_t->adv_rd_off=gpmc_round_ps_to_ticks(temp);/* oe_on */temp=dev_t->t_oeasu;/* remove this ? */if(mux){-temp=max_t(u32,temp,-gpmc_t->clk_activation*1000+dev_t->t_ach);-temp=max_t(u32,temp,(gpmc_t->adv_rd_off+-gpmc_ticks_to_ns(dev_t->cyc_aavdh_oe))*1000);+temp=max_t(u32,temp,gpmc_t->clk_activation+dev_t->t_ach);+temp=max_t(u32,temp,gpmc_t->adv_rd_off++gpmc_ticks_to_ps(dev_t->cyc_aavdh_oe));}-gpmc_t->oe_on=gpmc_round_ps_to_ticks(temp)/1000;+gpmc_t->oe_on=gpmc_round_ps_to_ticks(temp);/* access *//* any scope for improvement ?, by combining oe_on & clk_activation,*needtocheckwhetheraccess=clk_activation+roundtosyncclk?*/temp=max_t(u32,dev_t->t_iaa,dev_t->cyc_iaa*gpmc_t->sync_clk);-temp+=gpmc_t->clk_activation*1000;+temp+=gpmc_t->clk_activation;if(dev_t->cyc_oe)-temp=max_t(u32,temp,(gpmc_t->oe_on+-gpmc_ticks_to_ns(dev_t->cyc_oe))*1000);-gpmc_t->access=gpmc_round_ps_to_ticks(temp)/1000;+temp=max_t(u32,temp,gpmc_t->oe_on++gpmc_ticks_to_ps(dev_t->cyc_oe));+gpmc_t->access=gpmc_round_ps_to_ticks(temp);-gpmc_t->oe_off=gpmc_t->access+gpmc_ticks_to_ns(1);+gpmc_t->oe_off=gpmc_t->access+gpmc_ticks_to_ps(1);gpmc_t->cs_rd_off=gpmc_t->oe_off;/* rd_cycle */temp=max_t(u32,dev_t->t_cez_r,dev_t->t_oez);temp=gpmc_round_ps_to_sync_clk(temp,gpmc_t->sync_clk)+-gpmc_t->access*1000;+gpmc_t->access;/* barter t_ce_rdyz with t_cez_r ? */if(dev_t->t_ce_rdyz)-temp=max_t(u32,temp,-gpmc_t->cs_rd_off*1000+dev_t->t_ce_rdyz);-gpmc_t->rd_cycle=gpmc_round_ps_to_ticks(temp)/1000;+temp=max_t(u32,temp,gpmc_t->cs_rd_off+dev_t->t_ce_rdyz);+gpmc_t->rd_cycle=gpmc_round_ps_to_ticks(temp);return0;}
@@ -976,29 +971,28 @@ static int gpmc_calc_sync_write_timings(struct gpmc_timings *gpmc_t,temp=dev_t->t_avdp_w;if(mux){temp=max_t(u32,temp,-gpmc_t->clk_activation*1000+dev_t->t_avdh);-temp=max_t(u32,-(gpmc_t->adv_on+gpmc_ticks_to_ns(1))*1000,temp);+gpmc_t->clk_activation+dev_t->t_avdh);+temp=max_t(u32,gpmc_t->adv_on+gpmc_ticks_to_ps(1),temp);}-gpmc_t->adv_wr_off=gpmc_round_ps_to_ticks(temp)/1000;+gpmc_t->adv_wr_off=gpmc_round_ps_to_ticks(temp);/* wr_data_mux_bus */temp=max_t(u32,dev_t->t_weasu,-gpmc_t->clk_activation*1000+dev_t->t_rdyo);+gpmc_t->clk_activation+dev_t->t_rdyo);/* shouldn't mux be kept as a whole for wr_data_mux_bus ?,*andinthatcaseremembertohandlewe_onproperly*/if(mux){temp=max_t(u32,temp,-gpmc_t->adv_wr_off*1000+dev_t->t_aavdh);-temp=max_t(u32,temp,(gpmc_t->adv_wr_off+-gpmc_ticks_to_ns(dev_t->cyc_aavdh_we))*1000);+gpmc_t->adv_wr_off+dev_t->t_aavdh);+temp=max_t(u32,temp,gpmc_t->adv_wr_off++gpmc_ticks_to_ps(dev_t->cyc_aavdh_we));}-gpmc_t->wr_data_mux_bus=gpmc_round_ps_to_ticks(temp)/1000;+gpmc_t->wr_data_mux_bus=gpmc_round_ps_to_ticks(temp);/* we_on */if(gpmc_capability&GPMC_HAS_WR_DATA_MUX_BUS)-gpmc_t->we_on=gpmc_round_ps_to_ticks(dev_t->t_weasu)/1000;+gpmc_t->we_on=gpmc_round_ps_to_ticks(dev_t->t_weasu);elsegpmc_t->we_on=gpmc_t->wr_data_mux_bus;
@@ -1007,24 +1001,24 @@ static int gpmc_calc_sync_write_timings(struct gpmc_timings *gpmc_t,gpmc_t->wr_access=gpmc_t->access;/* we_off */-temp=gpmc_t->we_on*1000+dev_t->t_wpl;+temp=gpmc_t->we_on+dev_t->t_wpl;temp=max_t(u32,temp,-(gpmc_t->wr_access+gpmc_ticks_to_ns(1))*1000);+gpmc_t->wr_access+gpmc_ticks_to_ps(1));temp=max_t(u32,temp,-(gpmc_t->we_on+gpmc_ticks_to_ns(dev_t->cyc_wpl))*1000);-gpmc_t->we_off=gpmc_round_ps_to_ticks(temp)/1000;+gpmc_t->we_on+gpmc_ticks_to_ps(dev_t->cyc_wpl));+gpmc_t->we_off=gpmc_round_ps_to_ticks(temp);-gpmc_t->cs_wr_off=gpmc_round_ps_to_ticks(gpmc_t->we_off*1000+-dev_t->t_wph)/1000;+gpmc_t->cs_wr_off=gpmc_round_ps_to_ticks(gpmc_t->we_off++dev_t->t_wph);/* wr_cycle */temp=gpmc_round_ps_to_sync_clk(dev_t->t_cez_w,gpmc_t->sync_clk);-temp+=gpmc_t->wr_access*1000;+temp+=gpmc_t->wr_access;/* barter t_ce_rdyz with t_cez_w ? */if(dev_t->t_ce_rdyz)temp=max_t(u32,temp,-gpmc_t->cs_wr_off*1000+dev_t->t_ce_rdyz);-gpmc_t->wr_cycle=gpmc_round_ps_to_ticks(temp)/1000;+gpmc_t->cs_wr_off+dev_t->t_ce_rdyz);+gpmc_t->wr_cycle=gpmc_round_ps_to_ticks(temp);return0;}
@@ -116,38 +116,38 @@ struct gpmc_timings {u32sync_clk;/* Chip-select signal timings corresponding to GPMC_CS_CONFIG2 */-u16cs_on;/* Assertion time */-u16cs_rd_off;/* Read deassertion time */-u16cs_wr_off;/* Write deassertion time */+u32cs_on;/* Assertion time */+u32cs_rd_off;/* Read deassertion time */+u32cs_wr_off;/* Write deassertion time *//* ADV signal timings corresponding to GPMC_CONFIG3 */-u16adv_on;/* Assertion time */-u16adv_rd_off;/* Read deassertion time */-u16adv_wr_off;/* Write deassertion time */+u32adv_on;/* Assertion time */+u32adv_rd_off;/* Read deassertion time */+u32adv_wr_off;/* Write deassertion time *//* WE signals timings corresponding to GPMC_CONFIG4 */-u16we_on;/* WE assertion time */-u16we_off;/* WE deassertion time */+u32we_on;/* WE assertion time */+u32we_off;/* WE deassertion time *//* OE signals timings corresponding to GPMC_CONFIG4 */-u16oe_on;/* OE assertion time */-u16oe_off;/* OE deassertion time */+u32oe_on;/* OE assertion time */+u32oe_off;/* OE deassertion time *//* Access time and cycle time timings corresponding to GPMC_CONFIG5 */-u16page_burst_access;/* Multiple access word delay */-u16access;/* Start-cycle to first data valid delay */-u16rd_cycle;/* Total read cycle time */-u16wr_cycle;/* Total write cycle time */+u32page_burst_access;/* Multiple access word delay */+u32access;/* Start-cycle to first data valid delay */+u32rd_cycle;/* Total read cycle time */+u32wr_cycle;/* Total write cycle time */-u16bus_turnaround;-u16cycle2cycle_delay;+u32bus_turnaround;+u32cycle2cycle_delay;-u16wait_monitoring;-u16clk_activation;+u32wait_monitoring;+u32clk_activation;/* The following are only on OMAP3430 */-u16wr_access;/* WRACCESSTIME */-u16wr_data_mux_bus;/* WRDATAONADMUXBUS */+u32wr_access;/* WRACCESSTIME */+u32wr_data_mux_bus;/* WRDATAONADMUXBUS */structgpmc_bool_timingsbool_timings;};
Hi Tony,
On Fri, Sep 14, 2012 at 15:50:02, Mohammed, Afzal wrote:
quoted
* Mohammed, Afzal: Wednesday, September 12, 2012 3:20 PM
quoted
quoted
But some of the tusb async values is less by one. I need
to get it right.
Reason has been identified. It was due to rounding error,
no changes are required in the expressions. Moving
completely to picoseconds resolves the issue.
Can you please try with the attached patch ?
Gave it a quick try and it seemed to work.. But when I tried
rebasing my patches for the cbus to keep things working with
the watchdog, I ran into multiple merge conflicts with
current linux next and gave up.
Care to repost this series updated against current linux
next?
I'm afraid I've pretty much lost track of all the patches
and rather not start resolving the conflicts as I'm sure
I'll break something else :)
Regards,
Tony
Hi Tony,
On Fri, Sep 14, 2012 at 15:50:02, Mohammed, Afzal wrote:
quoted
* Mohammed, Afzal: Wednesday, September 12, 2012 3:20 PM
quoted
quoted
But some of the tusb async values is less by one. I need
to get it right.
Reason has been identified. It was due to rounding error,
no changes are required in the expressions. Moving
completely to picoseconds resolves the issue.
Can you please try with the attached patch ?
Gave it a quick try and it seemed to work.. But when I tried
rebasing my patches for the cbus to keep things working with
the watchdog, I ran into multiple merge conflicts with
current linux next and gave up.
OK went back to my original branch without current linux
next and with the new cbus + retu driver from Aaro applied.
Confirmed it's now working on n800 tusb6010.
Care to repost this series updated against current linux
next?
I'm afraid I've pretty much lost track of all the patches
and rather not start resolving the conflicts as I'm sure
I'll break something else :)
You should still repost the whole updated series against
linux next.
Regards,
Tony
Hi Tony,
On Tue, Sep 18, 2012 at 04:40:02, Tony Lindgren wrote:
* Tony Lindgren [off-list ref] [120917 15:54]:
quoted
quoted
quoted
Can you please try with the attached patch ?
Gave it a quick try and it seemed to work.. But when I tried
rebasing my patches for the cbus to keep things working with
the watchdog, I ran into multiple merge conflicts with
current linux next and gave up.
OK went back to my original branch without current linux
next and with the new cbus + retu driver from Aaro applied.
Confirmed it's now working on n800 tusb6010.
Thanks for checking and confirming.
quoted
Care to repost this series updated against current linux
next?
I'm afraid I've pretty much lost track of all the patches
and rather not start resolving the conflicts as I'm sure
I'll break something else :)
You should still repost the whole updated series against
linux next.
New updated version - v7, based over linux next has been posted.
Regards
Afzal