GPMC driver conversion
Subsystems (5)
v6 cover GPMC driver conversion
Hi,
This series is based on 3.5-rc1, and is dependent on [1,2,3], and has been tested on omap3evm (smsc911x) rev G & C and beagle board(nand). Also using private patches, nand & onenand was tested on omap3evm, rev G & C respectively (as support for these were not in mainline)
All boards will work using the old existing interface. Further patch series would convert all boards to use new interface.
Many of GPMC peripherals depend on bootloader for configuration. This is going to be deprecated. feature-removal-schedule.txt will be updated in one of the upcoming patch series.
Thanks to Tony & Jon for their various suggestions.
[PATCH 03/13] ARM: OMAP2+: gpmc: driver migration helper, is to be reverted once all GPMC peripherals are migrated to use driver interface.
GPMC (General Purpose Memory Controller) in brief:
GPMC is an unified memory controller dedicated to interfacing external memory devices like
Asynchronous SRAM like memories and application specific integrated circuit devices. Asynchronous, synchronous, and page mode burst NOR flash devices NAND flash Pseudo-SRAM devices
GPMC details can be referred in AM335X Technical Reference Manual @ http://www.ti.com/lit/pdf/spruh73
Regards
Afzal
[1] http://www.mail-archive.com/linux-omap at vger.kernel.org/msg70096.html
[2] http://www.mail-archive.com/linux-omap at vger.kernel.org/msg70730.html
[3] http://www.mail-archive.com/linux-omap at vger.kernel.org/msg69881.html
v6: Capability flag added that stores features based on revision
Macros used for finding revision
Return value from memory setup function corrected Comments added to clarify handling of device type, size Bool type time setting patch removed as has been taken care in [2] Handle variable number of waitpin
Warn if driver is unable to configure interrupt
Enhance some of commit messages
Handle shared writeprotect case
Fix a bug in gpmc_create_device
Get clk from hwmod
Remove unwanted code
v5: Make this a purely driver conversion series, i.e. gpmc-mtd
interactions has been made as a separate series, so is adding hwmod entry for OMAP2/3.
And modifying gpmc peripheral platform initialization has been separated out of this series, so is migrating boards to use new driver interface. GPMC driver conversion which was done in a few patches in v4 has been tranformed to series of small patches. Also care has been taken care that old interface will not break with any of these patches, so both interfaces can coexist. This helps in converting boards one-by-one gradually. Acquiring CS has been thrown out. And conclusive comments on v4 has been addressed.
v4: Handle wait pin (except for interrupts), enhance configuration
& timing interface of GPMC to take care of all boards. Dynamic allocation of interrupt instead of static. Convert remaining peripherals to work with GPMC driver. Handle acquiring NAND CS#, adapt to HWMOD, update HWMOD OMAP2/3 entries, other minor commenst on v3.
v3: Single device structure passed from platform for peripherals using
multiple CS instead of using multiple device structure having a few redundant data, handle interrupts, GPMC NAND handling by GPMC NAND driver instead of GPMC driver
v2: Avoid code movement that kept similar code together (for easy review)
Afzal Mohammed (13):
ARM: OMAP2+: gpmc: platform definitions
ARM: OMAP2+: gpmc: Adapt to HWMOD
ARM: OMAP2+: gpmc: driver migration helper
ARM: OMAP2+: gpmc: minimal driver support
ARM: OMAP2+: gpmc: resource creation helpers
ARM: OMAP2+: gpmc: CS configuration helper
ARM: OMAP2+: gpmc: register time setting helper
ARM: OMAP2+: gpmc: holler if no configuration
ARM: OMAP2+: gpmc: waitpin helper
ARM: OMAP2+: gpmc: handle connected peripherals
ARM: OMAP2+: gpmc: cs reconfigure helper
ARM: OMAP2+: gpmc: update nand register info
ARM: OMAP2+: gpmc: configure writeprotect
arch/arm/mach-omap2/gpmc.c | 797 ++++++++++++++++++++++++++++++-- arch/arm/plat-omap/include/plat/gpmc.h | 58 +++ 2 files changed, 812 insertions(+), 43 deletions(-)
Signature · 1 line
1.7.10.2
PATCH v6 1/13 ARM: OMAP2+: gpmc: platform definitions
gpmc driver platform definitions
--- arch/arm/plat-omap/include/plat/gpmc.h | 37 ++++++++++++++++++++++++++++++++ 1 file changed, 37 insertions(+)
arch/arm/plat-omap/include/plat/gpmc.h +37 -0
@@ -152,6 +152,43 @@ struct gpmc_timings {struct gpmc_bool_timings bool_timings; }; +enum { + has_none, + has_period, + has_clock +}; + +struct gpmc_time_ctrl { +int type; +struct gpmc_timings timings; +}; + +struct gpmc_cs_data { +unsigned cs; +unsigned long mem_size; +unsigned long mem_offset; +bool have_config; +unsigned config; +struct gpmc_time_ctrl time_ctrl; +unsigned irq_config; +}; + +struct gpmc_device_pdata { +char *name; +int id; +void *pdata; +unsigned pdata_size; +struct resource *per_res; +unsigned per_res_cnt; +struct gpmc_cs_data *cs_data; +unsigned num_cs; +}; + +struct gpmc_pdata { +unsigned waitpin_nr; +struct gpmc_device_pdata **device_pdata; +}; +struct gpmc_nand_regs {void __iomem *gpmc_status;void __iomem *gpmc_nand_command;
Signature · 1 line
1.7.10.2
PATCH v6 2/13 ARM: OMAP2+: gpmc: Adapt to HWMOD
In reply to Afzal MohammedCreate API for platforms to adapt gpmc to HWMOD
gpmc clk is stored as timing calculation for
platform code require its rate so that GPMC
timing values (platform data for gpmc driver)
can be calculated.
--- arch/arm/mach-omap2/gpmc.c | 31 +++++++++++++++++++++++++++++++ arch/arm/plat-omap/include/plat/gpmc.h | 2 ++ 2 files changed, 33 insertions(+)
arch/arm/mach-omap2/gpmc.c +31 -0
@@ -27,6 +27,7 @@#include <asm/mach-types.h> #include <plat/gpmc.h> +#include <plat/omap_device.h> #include <plat/sdrc.h>
@@ -937,6 +938,36 @@ static int __init gpmc_init(void) }postcore_initcall (gpmc_init); +__init int omap_gpmc_init (struct gpmc_pdata *pdata) +{ +struct omap_hwmod *oh; +struct platform_device *pdev; +char *name ="omap-gpmc" ; +char *oh_name ="gpmc" ; + + oh =omap_hwmod_lookup (oh_name); +if (!oh) { +pr_err ("Could not look up %s\n" , oh_name); +return -ENODEV ; + } + + gpmc_l3_clk = oh->_clk; +if (!gpmc_l3_clk) { +pr_err ("Could not get GPMC clock\n" ); +return -EINVAL ; + } + + pdev =omap_device_build (name,-1 , oh, pdata, +sizeof (*pdata),NULL ,0 ,0 ); +if (IS_ERR (pdev)) { +WARN (1 ,"Can't build omap_device for %s:%s.\n" , + name, oh->name); +return PTR_ERR (pdev); + } + +return 0 ; +} +static irqreturn_t gpmc_handle_irq (int irq,void *dev) {int i;
arch/arm/plat-omap/include/plat/gpmc.h +2 -0
@@ -208,6 +208,8 @@ struct gpmc_nand_regs {extern void gpmc_update_nand_reg (struct gpmc_nand_regs *reg,int cs);extern int gpmc_get_client_irq (unsigned irq_config); +extern int omap_gpmc_init (struct gpmc_pdata *pdata); +extern unsigned int gpmc_ns_to_ticks (unsigned int time_ns);extern unsigned int gpmc_ps_to_ticks (unsigned int time_ps);extern unsigned int gpmc_ticks_to_ns (unsigned int ticks);
Signature · 1 line
1.7.10.2
PATCH v6 3/13 ARM: OMAP2+: gpmc: driver migration helper
In reply to Afzal MohammedA driver is being created out of GPMC code. This is being attempted to acheive by not breaking existing interface, necessitating requirement of GPMC peripherals being able to work with using new interface as well as with the old existing interface. To not break existing, initcall is required as in old interface GPMC is configured at arch initcall and GPMC should be ready to handle old interface by that time
Note: This commit has to be reverted once all boards have been converted to work with gpmc driver interface
--- arch/arm/mach-omap2/gpmc.c | 18 +++++++++++++++++- 1 file changed, 17 insertions(+), 1 deletion(-)
arch/arm/mach-omap2/gpmc.c +17 -1
@@ -941,7 +941,7 @@ postcore_initcall(gpmc_init);__init int omap_gpmc_init (struct gpmc_pdata *pdata) {struct omap_hwmod *oh; -struct platform_device *pdev; +static struct platform_device *pdev;char *name ="omap-gpmc" ;char *oh_name ="gpmc" ;
@@ -951,6 +951,11 @@ __init int omap_gpmc_init(struct gpmc_pdata *pdata)return -ENODEV ; } +if (pdev !=NULL ) { +omap_device_delete (pdev->archdata.od); +platform_device_unregister (pdev); + } + gpmc_l3_clk = oh->_clk;if (!gpmc_l3_clk) {pr_err ("Could not get GPMC clock\n" );
@@ -968,6 +973,17 @@ __init int omap_gpmc_init(struct gpmc_pdata *pdata)return 0 ; } +static int __init gpmc_pre_init (void ) +{ +static struct gpmc_device_pdata *gpmc_device_data[1 ]; +struct gpmc_pdata gpmc_data = { + .device_pdata = gpmc_device_data, + }; + +return omap_gpmc_init (&gpmc_data); +} +postcore_initcall (gpmc_pre_init); +static irqreturn_t gpmc_handle_irq (int irq,void *dev) {int i;
Signature · 1 line
1.7.10.2
PATCH v6 4/13 ARM: OMAP2+: gpmc: minimal driver support
In reply to Afzal MohammedCreate a minimal driver out of gpmc code.
Responsibilities handled by earlier gpmc
initialization is now achieved in probe.
--- arch/arm/mach-omap2/gpmc.c | 179 ++++++++++++++++++++++++++++++++------------ 1 file changed, 132 insertions(+), 47 deletions(-)
arch/arm/mach-omap2/gpmc.c +132 -47
@@ -24,6 +24,7 @@#include <linux/io.h> #include <linux/module.h> #include <linux/interrupt.h> +#include <linux/platform_device.h> #include <asm/mach-types.h> #include <plat/gpmc.h>
@@ -31,6 +32,8 @@#include <plat/sdrc.h> +#define DRIVER_NAME "omap-gpmc" +/* GPMC register offsets */ #define GPMC_REVISION 0x00#define GPMC_SYSCONFIG 0x10
@@ -86,6 +89,12 @@#define ENABLE_PREFETCH (0x1 << 7)#define DMA_MPU_MODE 2 +#define GPMC_REVISION_MAJOR (l) ((l >> 4) & 0xf) +#define GPMC_REVISION_MINOR (l) (l & 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
@@ -122,6 +131,21 @@ struct omap3_gpmc_regs {struct gpmc_cs_config cs_context[GPMC_CS_NUM ]; }; +struct gpmc_peripheral { +char *name; +int id; +void *pdata; +unsigned pdata_size; +struct resource *per_res; +unsigned per_res_cnt; +struct resource *gpmc_res; +unsigned gpmc_res_cnt; +bool have_waitpin; +bool waitpin_high; +unsigned waitpin; +struct platform_device *pdev; +}; +static struct gpmc_client_irq gpmc_client_irq[GPMC_NR_IRQ ];static struct irq_chip gpmc_irq_chip;static unsigned gpmc_irq_start;
@@ -131,6 +155,10 @@ static struct resource gpmc_cs_mem[GPMC_CS_NUM];static DEFINE_SPINLOCK (gpmc_mem_lock);static unsigned int gpmc_cs_map;/* flag for cs which are initialized */ static int gpmc_ecc_used = -EINVAL ;/* cs using ecc engine */ +static struct device *gpmc_dev; +static u32 gpmc_capability; +static int gpmc_irq; +static resource_size_t phys_base, mem_size;static void __iomem *gpmc_base;
@@ -472,6 +500,19 @@ static int gpmc_cs_insert_mem(int cs, unsigned long base, unsigned long size)return r; } +static int gpmc_cs_delete_mem (int cs) +{ +struct resource *res = &gpmc_cs_mem[cs]; +int r; + +spin_lock (&gpmc_mem_lock); + r =release_resource (&gpmc_cs_mem[cs]); + res->start = res->end =0 ; +spin_unlock (&gpmc_mem_lock); + +return r; +} +int gpmc_cs_request (int cs,unsigned long size,unsigned long *base) {struct resource *res = &gpmc_cs_mem[cs];
@@ -808,7 +849,7 @@ static void gpmc_irq_noop(struct irq_data *data) { }static unsigned int gpmc_irq_noop_ret (struct irq_data *data) {return 0 ; } -static int gpmc_setup_irq (int gpmc_irq) +static int gpmc_setup_irq (void ) {int i;u32 regval;
@@ -852,7 +893,37 @@ static int gpmc_setup_irq(int gpmc_irq)return request_irq (gpmc_irq, gpmc_handle_irq,0 ,"gpmc" ,NULL ); } -static void __init gpmc_mem_init (void ) +static __exit int gpmc_free_irq(void) +{ +int i; + +if (gpmc_irq) +free_irq (gpmc_irq,NULL ); + +for (i =0 ; i <GPMC_NR_IRQ ; i++) { +irq_set_handler (gpmc_client_irq[i].irq,NULL ); +irq_set_chip (gpmc_client_irq[i].irq, &no_irq_chip); +irq_modify_status (gpmc_client_irq[i].irq,0 ,0 ); + } + +irq_free_descs (gpmc_irq_start,GPMC_NR_IRQ ); + +return 0 ; +} + +static void __devexit gpmc_mem_exit(void) +{ +int cs; + +for (cs =0 ; cs <GPMC_CS_NUM ; cs++) { +if (!gpmc_cs_mem_enabled (cs)) +continue ; +gpmc_cs_delete_mem (cs); + } + +} + +static void __devinit gpmc_mem_init (void ) {int cs;unsigned long boot_rom_space =0 ;
@@ -879,64 +950,78 @@ static void __init gpmc_mem_init(void) } } -static int __init gpmc_init (void ) +static __devinit int gpmc_probe (struct platform_device *pdev) {u32 l; -int ret = -EINVAL ; -int gpmc_irq; -char *ck =NULL ; - -if (cpu_is_omap24xx ()) { - ck ="core_l3_ck" ; -if (cpu_is_omap2420 ()) - l =OMAP2420_GPMC_BASE ; -else - l =OMAP34XX_GPMC_BASE ; - gpmc_irq =INT_34XX_GPMC_IRQ ; - }else if (cpu_is_omap34xx ()) { - ck ="gpmc_fck" ; - l =OMAP34XX_GPMC_BASE ; - gpmc_irq =INT_34XX_GPMC_IRQ ; - }else if (cpu_is_omap44xx ()) { - ck ="gpmc_ck" ; - l =OMAP44XX_GPMC_BASE ; - gpmc_irq =OMAP44XX_IRQ_GPMC ; - } +struct resource *res; +struct gpmc_pdata *gp =dev_get_platdata (&pdev->dev); -if (WARN_ON (!ck)) -return ret; + res =platform_get_resource (pdev,IORESOURCE_MEM ,0 ); +if (res ==NULL ) +return -ENOENT ; - gpmc_l3_clk =clk_get (NULL , ck); -if (IS_ERR (gpmc_l3_clk)) { -printk (KERN_ERR "Could not get GPMC clock %s\n" , ck); -BUG (); - } + phys_base = res->start; + mem_size =resource_size (res); - gpmc_base =ioremap (l,SZ_4K ); -if (!gpmc_base) { -clk_put (gpmc_l3_clk); -printk (KERN_ERR "Could not get GPMC register memory\n" ); -BUG (); - } + gpmc_base =devm_request_and_ioremap (&pdev->dev, res); +if (!gpmc_base) +return -EADDRNOTAVAIL ; + + gpmc_dev = &pdev->dev; + + res =platform_get_resource (pdev,IORESOURCE_IRQ ,0 ); +if (res ==NULL ) +dev_warn (gpmc_dev,"Failed to get resource: irq\n" ); +else + gpmc_irq = res->start;clk_enable (gpmc_l3_clk); l =gpmc_read_reg (GPMC_REVISION ); -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 ); - l &=0x03 <<3 ; - l |= (0x02 <<3 ) | (1 <<0 ); -gpmc_write_reg (GPMC_SYSCONFIG , l); +if (GPMC_REVISION_MAJOR (l) >0x4 ) + gpmc_capability =GPMC_HAS_WR_ACCESS |GPMC_HAS_WR_DATA_MUX_BUS ; +dev_info (gpmc_dev,"GPMC revision %d.%d\n" , +GPMC_REVISION_MAJOR (l),GPMC_REVISION_MINOR (l)); +gpmc_mem_init (); - ret =gpmc_setup_irq (gpmc_irq); -if (ret) -pr_err ("gpmc: irq-%d could not claim: err %d\n" , - gpmc_irq, ret); -return ret; +if (IS_ERR_VALUE (gpmc_setup_irq ())) +dev_warn (gpmc_dev,"gpmc_setup_irq failed\n" ); + +return 0 ; } + +static __exit int gpmc_remove(struct platform_device *pdev) +{ +gpmc_free_irq (); +gpmc_mem_exit (); + gpmc_dev =NULL ; +return 0 ; +} + +static struct platform_driver gpmc_driver = { + .probe = gpmc_probe, + .remove =__devexit_p (gpmc_remove), + .driver = { + .name =DRIVER_NAME , + .owner =THIS_MODULE , + }, +}; + +static __init int gpmc_init (void ) +{ +return platform_driver_register (&gpmc_driver); + +} + +static __exit void gpmc_exit(void) +{ +platform_driver_unregister (&gpmc_driver); + +} +postcore_initcall (gpmc_init); +module_exit (gpmc_exit);__init int omap_gpmc_init (struct gpmc_pdata *pdata) {
Signature · 1 line
1.7.10.2
PATCH v6 5/13 ARM: OMAP2+: gpmc: resource creation helpers
In reply to Afzal MohammedHelpers for populating given resource structure
with memory & interrupt information.
--- arch/arm/mach-omap2/gpmc.c | 45 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 45 insertions(+)
arch/arm/mach-omap2/gpmc.c +45 -0
@@ -950,6 +950,51 @@ static void __devinit gpmc_mem_init(void) } } +static __devinit int gpmc_setup_cs_mem (struct gpmc_cs_data *cs, +struct resource *res) +{ +unsigned long start; +int ret; + + ret =gpmc_cs_request (cs->cs, cs->mem_size, &start); +if (IS_ERR_VALUE (ret)) { +dev_err (gpmc_dev,"error: gpmc request on CS: %d\n" , cs->cs); +return ret; + } + + res->start = start + cs->mem_offset; + res->end = res->start + cs->mem_size -1 ; + res->flags =IORESOURCE_MEM ; + +dev_info (gpmc_dev,"memory 0x%x-0x%x on CS %d\n" , res->start, + res->end, cs->cs); + +return 0 ; +} + +static __devinit int gpmc_setup_cs_irq (struct gpmc_cs_data *cs, +struct resource *res) +{ +int i, n; + +if (!gpmc_irq || !gpmc_irq_start) { +if (cs->irq_config) +dev_err (gpmc_dev,"gpmc not able to configure irq\n" ); +return 0 ; + } + +for (i =0 , n =0 ; i <GPMC_NR_IRQ ; i++) +if (gpmc_client_irq[i].bitmask & cs->irq_config) { + res[n].start = res[n].end = gpmc_client_irq[i].irq; + res[n].flags =IORESOURCE_IRQ ; +dev_info (gpmc_dev,"irq %u on CS %d\n" , + res[n].start, cs->cs); + n++; + } + +return n; +} +static __devinit int gpmc_probe (struct platform_device *pdev) {u32 l;
Signature · 1 line
1.7.10.2
PATCH v6 6/13 ARM: OMAP2+: gpmc: CS configuration helper
In reply to Afzal MohammedHelper for configuring given CS based on flags.
--- arch/arm/mach-omap2/gpmc.c | 39 ++++++++++++++++++++++++++++++++ arch/arm/plat-omap/include/plat/gpmc.h | 5 ++++ 2 files changed, 44 insertions(+)
arch/arm/mach-omap2/gpmc.c +39 -0
@@ -972,6 +972,45 @@ static __devinit int gpmc_setup_cs_mem(struct gpmc_cs_data *cs,return 0 ; } +static void gpmc_setup_cs_config (unsigned cs,unsigned conf) +{ +u32 l =gpmc_cs_read_reg (cs,GPMC_CS_CONFIG1 ); + + l &= ~(GPMC_CONFIG1_MUXADDDATA | +GPMC_CONFIG1_WRITETYPE_SYNC | +GPMC_CONFIG1_WRITEMULTIPLE_SUPP | +GPMC_CONFIG1_READTYPE_SYNC | +GPMC_CONFIG1_READMULTIPLE_SUPP | +GPMC_CONFIG1_WRAPBURST_SUPP | +GPMC_CONFIG1_DEVICETYPE (~0 ) | +GPMC_CONFIG1_DEVICESIZE (~0 ) | +GPMC_CONFIG1_PAGE_LEN (~0 )); + +/* device type & size bits are cleared above, implying NOR & 8 bit, +* only other options possible are NAND & 16 bit respectively, +* configure if user asked for it, else default is already setup +* users has been provided with NOR & 8 bit macros, nothing to be +* done here as already it has been setup above +*/ + l |= conf &GPMC_CONFIG1_DEVICETYPE_NAND ; + l |= conf &GPMC_CONFIG1_DEVICESIZE_16 ; + +if (conf &GPMC_CONFIG1_PAGE_LEN_8 ) + l |=GPMC_CONFIG1_PAGE_LEN_8 ; +else if (conf &GPMC_CONFIG1_PAGE_LEN_16 ) + l |=GPMC_CONFIG1_PAGE_LEN_16 ; + + conf &= (GPMC_CONFIG1_MUXADDDATA | +GPMC_CONFIG1_WRITETYPE_SYNC | +GPMC_CONFIG1_WRITEMULTIPLE_SUPP | +GPMC_CONFIG1_READTYPE_SYNC | +GPMC_CONFIG1_READMULTIPLE_SUPP | +GPMC_CONFIG1_WRAPBURST_SUPP ); + l |= conf; + +gpmc_cs_write_reg (cs,GPMC_CS_CONFIG1 , l); +} +static __devinit int gpmc_setup_cs_irq (struct gpmc_cs_data *cs,struct resource *res) {
arch/arm/plat-omap/include/plat/gpmc.h +5 -0
@@ -57,14 +57,19 @@#define GPMC_CONFIG1_WRITETYPE_SYNC (1 << 27)#define GPMC_CONFIG1_CLKACTIVATIONTIME (val) ((val & 3) << 25)#define GPMC_CONFIG1_PAGE_LEN (val) ((val & 3) << 23) +#define GPMC_CONFIG1_PAGE_LEN_4 GPMC_CONFIG1_PAGE_LEN(0) +#define GPMC_CONFIG1_PAGE_LEN_8 GPMC_CONFIG1_PAGE_LEN(1) +#define GPMC_CONFIG1_PAGE_LEN_16 GPMC_CONFIG1_PAGE_LEN(2)#define GPMC_CONFIG1_WAIT_READ_MON (1 << 22)#define GPMC_CONFIG1_WAIT_WRITE_MON (1 << 21)#define GPMC_CONFIG1_WAIT_MON_IIME (val) ((val & 3) << 18)#define GPMC_CONFIG1_WAIT_PIN_SEL (val) ((val & 3) << 16)#define GPMC_CONFIG1_DEVICESIZE (val) ((val & 3) << 12) +#define GPMC_CONFIG1_DEVICESIZE_8 GPMC_CONFIG1_DEVICESIZE(0)#define GPMC_CONFIG1_DEVICESIZE_16 GPMC_CONFIG1_DEVICESIZE(1)#define GPMC_CONFIG1_DEVICETYPE (val) ((val & 3) << 10)#define GPMC_CONFIG1_DEVICETYPE_NOR GPMC_CONFIG1_DEVICETYPE(0) +#define GPMC_CONFIG1_DEVICETYPE_NAND GPMC_CONFIG1_DEVICETYPE(2)#define GPMC_CONFIG1_MUXADDDATA (1 << 9)#define GPMC_CONFIG1_TIME_PARA_GRAN (1 << 4)#define GPMC_CONFIG1_FCLK_DIV (val) (val & 3)
Signature · 1 line
1.7.10.2
PATCH v6 7/13 ARM: OMAP2+: gpmc: register time setting helper
In reply to Afzal MohammedHelper for setting GPMC timing by taking input as register values.
--- arch/arm/mach-omap2/gpmc.c | 45 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 45 insertions(+)
arch/arm/mach-omap2/gpmc.c +45 -0
@@ -1011,6 +1011,51 @@ static void gpmc_setup_cs_config(unsigned cs, unsigned conf)gpmc_cs_write_reg (cs,GPMC_CS_CONFIG1 , l); } +static inline void gpmc_set_one_timing (int cs,int reg,int start, +int end,u32 val) +{ +u32 l; +unsigned mask; + + mask = (1 << (end - start +1 )) -1 ; + l =gpmc_cs_read_reg (cs, reg); + l &= ~(mask << start); + l |= val << start; +gpmc_cs_write_reg (cs, reg, l); +} + +static void gpmc_cs_set_register_timings (int cs,const struct gpmc_timings *t) +{ +gpmc_set_one_timing (cs,GPMC_CS_CONFIG1 ,0 ,1 , t->sync_clk); +gpmc_set_one_timing (cs,GPMC_CS_CONFIG1 ,18 ,19 , t->wait_monitoring); +gpmc_set_one_timing (cs,GPMC_CS_CONFIG1 ,25 ,26 , t->clk_activation); + +gpmc_set_one_timing (cs,GPMC_CS_CONFIG2 ,0 ,3 , t->cs_on); +gpmc_set_one_timing (cs,GPMC_CS_CONFIG2 ,8 ,12 , t->cs_rd_off); +gpmc_set_one_timing (cs,GPMC_CS_CONFIG2 ,16 ,20 , t->cs_wr_off); + +gpmc_set_one_timing (cs,GPMC_CS_CONFIG3 ,0 ,3 , t->adv_on); +gpmc_set_one_timing (cs,GPMC_CS_CONFIG3 ,8 ,12 , t->adv_rd_off); +gpmc_set_one_timing (cs,GPMC_CS_CONFIG3 ,16 ,20 , t->adv_wr_off); + +gpmc_set_one_timing (cs,GPMC_CS_CONFIG4 ,0 ,3 , t->oe_on); +gpmc_set_one_timing (cs,GPMC_CS_CONFIG4 ,8 ,12 , t->oe_off); +gpmc_set_one_timing (cs,GPMC_CS_CONFIG4 ,16 ,19 , t->we_on); +gpmc_set_one_timing (cs,GPMC_CS_CONFIG4 ,24 ,28 , t->we_off); + +gpmc_set_one_timing (cs,GPMC_CS_CONFIG5 ,24 ,27 , t->page_burst_access); +gpmc_set_one_timing (cs,GPMC_CS_CONFIG6 ,0 ,3 , t->bus_turnaround); +gpmc_set_one_timing (cs,GPMC_CS_CONFIG6 ,8 ,11 , t->cycle2cycle_delay); + +if (gpmc_capability &GPMC_HAS_WR_ACCESS ) +gpmc_set_one_timing (cs,GPMC_CS_CONFIG6 ,24 ,28 , t->wr_access); +if (gpmc_capability &GPMC_HAS_WR_DATA_MUX_BUS ) +gpmc_set_one_timing (cs,GPMC_CS_CONFIG6 ,16 ,19 , + t->wr_data_mux_bus); + +gpmc_cs_bool_timings (cs, &t->bool_timings); +} +static __devinit int gpmc_setup_cs_irq (struct gpmc_cs_data *cs,struct resource *res) {
Signature · 1 line
1.7.10.2
PATCH v6 8/13 ARM: OMAP2+: gpmc: holler if no configuration
In reply to Afzal MohammedSome of the GPMC peripherals depend on bootloader to do the configuration. This facility is deprecated, notify user about the present GPMC settings & inform that that relying on bootloader for GPMC setting is deprecated.
Note: This has to be reverted once Kernel is updated with sufficient details so that all the gpmc periherals can be configured in Kernel for all boards instead of relying on bootloader.
--- arch/arm/mach-omap2/gpmc.c | 104 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 104 insertions(+)
arch/arm/mach-omap2/gpmc.c +104 -0
@@ -1056,6 +1056,110 @@ static void gpmc_cs_set_register_timings(int cs, const struct gpmc_timings *t)gpmc_cs_bool_timings (cs, &t->bool_timings); } +static void gpmc_print_cs_config (int cs) +{ +u32 l =gpmc_cs_read_reg (cs,GPMC_CS_CONFIG1 ); + +dev_warn (gpmc_dev,"GPMC CS%d depends on bootloader for config\n" , cs); +dev_warn (gpmc_dev,"Please update it in Kernel ASAP to prevent losing support for this peripheral\n" ); +dev_warn (gpmc_dev,"Bootloader dependency for GPMC configuration is deprecated\n" ); + +dev_warn (gpmc_dev,"muxadddata: %s\n" , + l &GPMC_CONFIG1_MUXADDDATA ?"yes" :"no" ); +dev_warn (gpmc_dev,"writetypesync: %s\n" , + l &GPMC_CONFIG1_WRITETYPE_SYNC ?"yes" :"no" ); +dev_warn (gpmc_dev,"writemultiple: %s\n" , + l &GPMC_CONFIG1_WRITEMULTIPLE_SUPP ?"yes" :"no" ); +dev_warn (gpmc_dev,"readtypesync: %s\n" , + l &GPMC_CONFIG1_READTYPE_SYNC ?"yes" :"no" ); +dev_warn (gpmc_dev,"readmultiple: %s\n" , + l &GPMC_CONFIG1_READMULTIPLE_SUPP ?"yes" :"no" ); +dev_warn (gpmc_dev,"wrapburst: %s\n" , + l &GPMC_CONFIG1_WRAPBURST_SUPP ?"yes" :"no" ); +dev_warn (gpmc_dev,"devicetype: %s\n" , + l &GPMC_CONFIG1_DEVICETYPE_NAND ?"nand" :"nor" ); +dev_warn (gpmc_dev,"devicesize: %d\n" , + l &GPMC_CONFIG1_DEVICESIZE_16 ?16 :8 ); +dev_warn (gpmc_dev,"pagelength: %d\n" , + l &GPMC_CONFIG1_PAGE_LEN (~0 ) ? + (l &GPMC_CONFIG1_PAGE_LEN_16 ?16 :8 ) :4 ); +} +static inline unsigned gpmc_get_one_timing (int cs,int reg,int start,int end) +{ +u32 l =gpmc_cs_read_reg (cs, reg); +unsigned mask; + + mask = (1 << (end - start +1 )) -1 ; + l &= (mask << start); +return l >>= start; +} + +static void gpmc_print_cs_timings (int cs) +{ +dev_warn (gpmc_dev,"GPMC CS%d depends on bootloader for timing\n" , cs); +dev_warn (gpmc_dev,"Please update it in Kernel ASAP to prevent losing support for this peripheral\n" ); +dev_warn (gpmc_dev,"Bootloader dependency for GPMC configuration is deprecated\n" ); + +dev_warn (gpmc_dev,"fclk period: %lups\n" ,gpmc_get_fclk_period ()); +dev_warn (gpmc_dev,"sync_clk: %u\n" , +gpmc_get_one_timing (cs,GPMC_CS_CONFIG1 ,0 ,1 )); +dev_warn (gpmc_dev,"wait_monitoring: %u\n" , +gpmc_get_one_timing (cs,GPMC_CS_CONFIG1 ,18 ,19 )); +dev_warn (gpmc_dev,"clk_activation: %u\n" , +gpmc_get_one_timing (cs,GPMC_CS_CONFIG1 ,25 ,26 )); +dev_warn (gpmc_dev,"cs_on: %u\n" , +gpmc_get_one_timing (cs,GPMC_CS_CONFIG2 ,0 ,3 )); +dev_warn (gpmc_dev,"cs_rd_off: %u\n" , +gpmc_get_one_timing (cs,GPMC_CS_CONFIG2 ,8 ,12 )); +dev_warn (gpmc_dev,"cs_wr_off: %u\n" , +gpmc_get_one_timing (cs,GPMC_CS_CONFIG2 ,16 ,20 )); +dev_warn (gpmc_dev,"adv_on: %u\n" , +gpmc_get_one_timing (cs,GPMC_CS_CONFIG3 ,0 ,3 )); +dev_warn (gpmc_dev,"adv_rd_off: %u\n" , +gpmc_get_one_timing (cs,GPMC_CS_CONFIG3 ,8 ,12 )); +dev_warn (gpmc_dev,"adv_wr_off: %u\n" , +gpmc_get_one_timing (cs,GPMC_CS_CONFIG3 ,16 ,20 )); +dev_warn (gpmc_dev,"oe_on: %u\n" , +gpmc_get_one_timing (cs,GPMC_CS_CONFIG4 ,0 ,3 )); +dev_warn (gpmc_dev,"oe_off: %u\n" , +gpmc_get_one_timing (cs,GPMC_CS_CONFIG4 ,8 ,12 )); +dev_warn (gpmc_dev,"we_on: %u\n" , +gpmc_get_one_timing (cs,GPMC_CS_CONFIG4 ,16 ,19 )); +dev_warn (gpmc_dev,"we_off: %u\n" , +gpmc_get_one_timing (cs,GPMC_CS_CONFIG4 ,24 ,28 )); +dev_warn (gpmc_dev,"rd_cycle: %u\n" , +gpmc_get_one_timing (cs,GPMC_CS_CONFIG5 ,0 ,4 )); +dev_warn (gpmc_dev,"wr_cycle: %u\n" , +gpmc_get_one_timing (cs,GPMC_CS_CONFIG5 ,8 ,12 )); +dev_warn (gpmc_dev,"acess: %u\n" , +gpmc_get_one_timing (cs,GPMC_CS_CONFIG5 ,16 ,20 )); +dev_warn (gpmc_dev,"page_burst_access: %u\n" , +gpmc_get_one_timing (cs,GPMC_CS_CONFIG5 ,24 ,27 )); +dev_warn (gpmc_dev,"bus_turnaround: %u\n" , +gpmc_get_one_timing (cs,GPMC_CS_CONFIG6 ,0 ,3 )); +dev_warn (gpmc_dev,"cycle2cycle_delay: %u\n" , +gpmc_get_one_timing (cs,GPMC_CS_CONFIG6 ,8 ,11 )); +dev_warn (gpmc_dev,"wr_data_mux_bus: %u\n" , +gpmc_get_one_timing (cs,GPMC_CS_CONFIG6 ,16 ,19 )); +dev_warn (gpmc_dev,"wr_access: %u\n" , +gpmc_get_one_timing (cs,GPMC_CS_CONFIG6 ,24 ,28 )); + +dev_warn (gpmc_dev,"time_para_granularity: %u\n" , +gpmc_get_one_timing (cs,GPMC_CS_CONFIG1 ,4 ,4 )); +dev_warn (gpmc_dev,"cs_extra_delay: %u\n" , +gpmc_get_one_timing (cs,GPMC_CS_CONFIG2 ,7 ,7 )); +dev_warn (gpmc_dev,"adv_extra_delay: %u\n" , +gpmc_get_one_timing (cs,GPMC_CS_CONFIG3 ,7 ,7 )); +dev_warn (gpmc_dev,"oe_extra_delay: %u\n" , +gpmc_get_one_timing (cs,GPMC_CS_CONFIG4 ,7 ,7 )); +dev_warn (gpmc_dev,"we_extra_delay: %u\n" , +gpmc_get_one_timing (cs,GPMC_CS_CONFIG4 ,23 ,23 )); +dev_warn (gpmc_dev,"cycle2cyclediffcsen: %u\n" , +gpmc_get_one_timing (cs,GPMC_CS_CONFIG6 ,6 ,6 )); +dev_warn (gpmc_dev,"cycle2cyclesamecsen: %u\n" , +gpmc_get_one_timing (cs,GPMC_CS_CONFIG6 ,7 ,7 )); +} +static __devinit int gpmc_setup_cs_irq (struct gpmc_cs_data *cs,struct resource *res) {
Signature · 1 line
1.7.10.2
PATCH v6 9/13 ARM: OMAP2+: gpmc: waitpin helper
In reply to Afzal MohammedHelper for configuring waitpin. There are two parts to it; configuring at CS level and the other at device level. A device embedding multiple CS has been provided the capability to use same waitpin (different waitpins has not been supported as presently there are no GPMC peripherals doing so)
--- arch/arm/mach-omap2/gpmc.c | 123 ++++++++++++++++++++++++++++++++ arch/arm/plat-omap/include/plat/gpmc.h | 9 +++ 2 files changed, 132 insertions(+)
arch/arm/mach-omap2/gpmc.c +123 -0
@@ -75,6 +75,8 @@#define GPMC_CONFIG6_CYCLE2CYCLEDIFFCSEN BIT(6)#define GPMC_CONFIG6_CYCLE2CYCLESAMECSEN BIT(7) +#define GPMC_CONFIG_WAITPIN_POLARITY_SHIFT 0x8 +#define GPMC_CS0_OFFSET 0x60#define GPMC_CS_SIZE 0x30
@@ -99,6 +101,14 @@*/ #define GPMC_NR_IRQ 2 +enum { +GPMC_WAITPIN_IDX0 , +GPMC_WAITPIN_IDX1 , +GPMC_WAITPIN_IDX2 , +GPMC_WAITPIN_IDX3 , +GPMC_MAX_NR_WAITPIN +}; +struct gpmc_client_irq {unsigned irq;u32 bitmask;
@@ -146,6 +156,9 @@ struct gpmc_peripheral {struct platform_device *pdev; }; +static unsigned gpmc_waitpin_map; +static unsigned gpmc_waitpin_nr =GPMC_MAX_NR_WAITPIN ; +static struct gpmc_client_irq gpmc_client_irq[GPMC_NR_IRQ ];static struct irq_chip gpmc_irq_chip;static unsigned gpmc_irq_start;
@@ -1160,6 +1173,62 @@ static void gpmc_print_cs_timings(int cs) gpmc_get_one_timing(cs, GPMC_CS_CONFIG6, 7, 7)); } +static int gpmc_setup_cs_waitpin (struct gpmc_peripheral *g_per,unsigned cs, +unsigned conf) +{ +unsigned idx; +bool polarity =0 ; +u32 l =gpmc_cs_read_reg (cs,GPMC_CS_CONFIG1 ); + +switch (conf &GPMC_WAITPIN_MASK ) { +case GPMC_WAITPIN_0 : + idx =GPMC_WAITPIN_IDX0 ; +break ; +case GPMC_WAITPIN_1 : + idx =GPMC_WAITPIN_IDX1 ; +break ; +case GPMC_WAITPIN_2 : + idx =GPMC_WAITPIN_IDX2 ; +break ; +case GPMC_WAITPIN_3 : + idx =GPMC_WAITPIN_IDX3 ; +break ; +/* no waitpin */ +case 0 : +return 0 ; +break ; +default : +dev_err (gpmc_dev,"multiple waitpins selected on CS:%u\n" , cs); +return -EINVAL ; +break ; + } + + polarity = !!(conf &GPMC_WAITPIN_ACTIVE_HIGH ); + +if (g_per->have_waitpin) { +if (g_per->waitpin != idx || + g_per->waitpin_high != polarity) { +dev_err (gpmc_dev,"error: conflict: waitpin %u with polarity %d on device %s.%d\n" , + g_per->waitpin, g_per->waitpin_high, + g_per->name, g_per->id); +return -EBUSY ; + } + }else { + g_per->have_waitpin = true; + g_per->waitpin = idx; + g_per->waitpin_high = polarity; + } + + l |= conf &GPMC_CONFIG1_WAIT_WRITE_MON ; + l |= conf &GPMC_CONFIG1_WAIT_READ_MON ; + l &= ~GPMC_CONFIG1_WAIT_PIN_SEL_MASK ; + l |=GPMC_CONFIG1_WAIT_PIN_SEL (idx); + +gpmc_cs_write_reg (cs,GPMC_CS_CONFIG1 , l); + +return 0 ; +} +static __devinit int gpmc_setup_cs_irq (struct gpmc_cs_data *cs,struct resource *res) {
@@ -1183,6 +1252,55 @@ static __devinit int gpmc_setup_cs_irq(struct gpmc_cs_data *cs,return n; } +static inline int gpmc_waitpin_is_reserved (unsigned waitpin) +{ +return gpmc_waitpin_map & (0x1 << waitpin); +} + +static inline void gpmc_reserve_waitpin (unsigned waitpin) +{ + gpmc_waitpin_map &= ~(0x1 << waitpin); + gpmc_waitpin_map |= (0x1 << waitpin); +} + +static int gpmc_waitpin_request (unsigned waitpin) +{ +if (!(waitpin < gpmc_waitpin_nr)) +return -ENODEV ; + +if (gpmc_waitpin_is_reserved (waitpin)) +return -EBUSY ; +else +gpmc_reserve_waitpin (waitpin); + +return 0 ; +} + +static int gpmc_setup_waitpin (struct gpmc_peripheral *g_per) +{ +int ret; +u32 l, shift; + +if (!g_per->have_waitpin) +return 0 ; + + ret =gpmc_waitpin_request (g_per->waitpin); +if (IS_ERR_VALUE (ret)) { +dev_err (gpmc_dev,"waitpin %u reserved\n" , g_per->waitpin); +return ret; + } + + l =gpmc_read_reg (GPMC_CONFIG ); + shift = g_per->waitpin +GPMC_CONFIG_WAITPIN_POLARITY_SHIFT ; +if (g_per->waitpin_high) + l |=1 << shift; +else + l &= ~(1 << shift); +gpmc_write_reg (GPMC_CONFIG , l); + +return 0 ; +} +static __devinit int gpmc_probe (struct platform_device *pdev) {u32 l;
@@ -1221,11 +1339,16 @@ static __devinit int gpmc_probe(struct platform_device *pdev)if (IS_ERR_VALUE (gpmc_setup_irq ()))dev_warn (gpmc_dev,"gpmc_setup_irq failed\n" ); +/* waitpin default 4, update if platform passed available waitpin no. */ +if (gp->waitpin_nr) + gpmc_waitpin_nr = gp->waitpin_nr; +return 0 ; } static __exit int gpmc_remove(struct platform_device *pdev) { + gpmc_waitpin_nr =GPMC_MAX_NR_WAITPIN ;gpmc_free_irq ();gpmc_mem_exit (); gpmc_dev =NULL ;
arch/arm/plat-omap/include/plat/gpmc.h +9 -0
@@ -64,6 +64,7 @@#define GPMC_CONFIG1_WAIT_WRITE_MON (1 << 21)#define GPMC_CONFIG1_WAIT_MON_IIME (val) ((val & 3) << 18)#define GPMC_CONFIG1_WAIT_PIN_SEL (val) ((val & 3) << 16) +#define GPMC_CONFIG1_WAIT_PIN_SEL_MASK GPMC_CONFIG1_WAIT_PIN_SEL(3)#define GPMC_CONFIG1_DEVICESIZE (val) ((val & 3) << 12)#define GPMC_CONFIG1_DEVICESIZE_8 GPMC_CONFIG1_DEVICESIZE(0)#define GPMC_CONFIG1_DEVICESIZE_16 GPMC_CONFIG1_DEVICESIZE(1)
@@ -78,6 +79,14 @@#define GPMC_CONFIG1_FCLK_DIV4 (GPMC_CONFIG1_FCLK_DIV(3))#define GPMC_CONFIG7_CSVALID (1 << 6) +#define GPMC_WAITPIN_ACTIVE_HIGH (1 << 4) +#define GPMC_WAITPIN_ACTIVE_LOW (0 << 4) +#define GPMC_WAITPIN_0 (1 << 0) +#define GPMC_WAITPIN_1 (1 << 1) +#define GPMC_WAITPIN_2 (1 << 2) +#define GPMC_WAITPIN_3 (1 << 3) +#define GPMC_WAITPIN_MASK (GPMC_WAITPIN_0 | GPMC_WAITPIN_1 | \ + GPMC_WAITPIN_2 | GPMC_WAITPIN_3)#define GPMC_DEVICETYPE_NOR 0#define GPMC_DEVICETYPE_NAND 2#define GPMC_CONFIG_WRITEPROTECT 0x00000010
Signature · 1 line
1.7.10.2
PATCH v6 10/13 ARM: OMAP2+: gpmc: handle connected peripherals
In reply to Afzal MohammedPlatform will provide driver with configuration details for each CS like configuration, timing, interrupts. Setup GPMC based on it. Platform data also provides platform data & resources used for connected peripheral (eg. gpio irq). GPMC driver tunnels those information to respective driver.
--- arch/arm/mach-omap2/gpmc.c | 142 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 142 insertions(+)
arch/arm/mach-omap2/gpmc.c +142 -0
@@ -156,6 +156,8 @@ struct gpmc_peripheral {struct platform_device *pdev; }; +static struct gpmc_peripheral gpmc_peripheral[GPMC_CS_NUM ]; +static unsigned gpmc_num_peripheral;static unsigned gpmc_waitpin_map;static unsigned gpmc_waitpin_nr =GPMC_MAX_NR_WAITPIN ;
@@ -1229,6 +1231,37 @@ static int gpmc_setup_cs_waitpin(struct gpmc_peripheral *g_per, unsigned cs,return 0 ; } +static int gpmc_setup_cs_config_timing (struct gpmc_peripheral *g_per, +struct gpmc_cs_data *cs) +{ +int ret; + +/* some boards rely on bootloader for configuration */ +if (cs->have_config) { +gpmc_setup_cs_config (cs->cs, cs->config); + ret =gpmc_setup_cs_waitpin (g_per, cs->cs, cs->config); +if (IS_ERR_VALUE (ret)) { +dev_err (gpmc_dev,"error: waitpin on CS %d\n" , cs->cs); +return ret; + } + }else +gpmc_print_cs_config (cs->cs); + +/* some boards rely on bootloader for timing */ +if (cs->time_ctrl.type == has_period) { + ret =gpmc_cs_set_timings (cs->cs, &cs->time_ctrl.timings); +if (IS_ERR_VALUE (ret)) { +dev_err (gpmc_dev,"error: timing on CS: %d\n" , cs->cs); +return ret; + } + }else if (cs->time_ctrl.type == has_clock) +gpmc_cs_set_register_timings (cs->cs, &cs->time_ctrl.timings); +else +gpmc_print_cs_timings (cs->cs); + +return 0 ; +} +static __devinit int gpmc_setup_cs_irq (struct gpmc_cs_data *cs,struct resource *res) {
@@ -1301,11 +1334,99 @@ static int gpmc_setup_waitpin(struct gpmc_peripheral *g_per)return 0 ; } +static __devinit int gpmc_setup_cs (struct gpmc_peripheral *g_per, +struct gpmc_cs_data *cs,struct resource *res) +{ +int num, ret; + + ret =gpmc_setup_cs_mem (cs, res); +if (IS_ERR_VALUE (ret)) +return ret; + + ret =gpmc_setup_cs_config_timing (g_per, cs); +if (IS_ERR_VALUE (ret)) +return ret; + + num =gpmc_setup_cs_irq (cs, res +1 );/* +1 to account for memory */ + +return num +1 ;/* +1 to account for memory*/ +} + +static __devinit int gpmc_setup_device (struct gpmc_peripheral *g_per, +struct gpmc_device_pdata *gdp) +{ +int i, n, ret; +struct gpmc_cs_data *cs; + +for (i =0 , n = gdp->num_cs, cs = gdp->cs_data; + i < gdp->num_cs; i++, cs++) + n +=hweight32 (cs->irq_config); + + g_per->gpmc_res =devm_kzalloc (gpmc_dev,sizeof (*g_per->gpmc_res) * n, +GFP_KERNEL ); +if (g_per->gpmc_res ==NULL ) { +dev_err (gpmc_dev,"error: memory allocation\n" ); +return -ENOMEM ; + } + +for (i =0 , cs = gdp->cs_data, g_per->gpmc_res_cnt =0 ; + i < gdp->num_cs; cs++, i++) { + ret =gpmc_setup_cs (g_per, cs, + g_per->gpmc_res + g_per->gpmc_res_cnt); +if (IS_ERR_VALUE (ret) || +IS_ERR_VALUE (gpmc_setup_waitpin (g_per))) { +dev_err (gpmc_dev,"error: setup for %s\n" , gdp->name); +devm_kfree (gpmc_dev, g_per->gpmc_res); + g_per->gpmc_res =NULL ; + g_per->gpmc_res_cnt =0 ; +return -EINVAL ; + }else + g_per->gpmc_res_cnt += ret; + } + + g_per->name = gdp->name; + g_per->id = gdp->id; + g_per->pdata = gdp->pdata; + g_per->pdata_size = gdp->pdata_size; + g_per->per_res = gdp->per_res; + g_per->per_res_cnt = gdp->per_res_cnt; + +return 0 ; +} + +static __devinit +struct platform_device *gpmc_create_device (struct gpmc_peripheral *p) +{ +int num = p->per_res_cnt + p->gpmc_res_cnt; +struct resource *res; + + res =devm_kzalloc (gpmc_dev,sizeof (struct resource ) * num, +GFP_KERNEL ); +if (!res) { +dev_err (gpmc_dev,"error: allocating memory\n" ); +return NULL ; + } + +memcpy ((char *)res, (const char *) p->gpmc_res, +sizeof (struct resource ) * p->gpmc_res_cnt); +memcpy ((char *)(res + p->gpmc_res_cnt), (const char *)p->per_res, +sizeof (struct resource ) * p->per_res_cnt); + + p->pdev =platform_device_register_resndata (gpmc_dev, p->name, p->id, + res, num, p->pdata, p->pdata_size); + +devm_kfree (gpmc_dev, res); + +return p->pdev; +} +static __devinit int gpmc_probe (struct platform_device *pdev) {u32 l;struct resource *res;struct gpmc_pdata *gp =dev_get_platdata (&pdev->dev); +struct gpmc_device_pdata **gdq =NULL ; +struct gpmc_peripheral *g_per =NULL ; res =platform_get_resource (pdev,IORESOURCE_MEM ,0 );if (res ==NULL )
@@ -1343,11 +1464,32 @@ static __devinit int gpmc_probe(struct platform_device *pdev)if (gp->waitpin_nr) gpmc_waitpin_nr = gp->waitpin_nr; +/* Traverse NULL terminated array of peripheral pointers and setup */ +for (gdq = gp->device_pdata, g_per = gpmc_peripheral; *gdq; gdq++) +if (IS_ERR_VALUE (gpmc_setup_device (g_per, *gdq))) +dev_err (gpmc_dev,"gpmc setup on %s failed\n" , + (*gdq)->name); +else + g_per++; + gpmc_num_peripheral = g_per - gpmc_peripheral; + +for (l =0 , g_per = gpmc_peripheral; + l < gpmc_num_peripheral; l++, g_per++) +if (IS_ERR (gpmc_create_device (g_per))) +dev_err (gpmc_dev,"device creation on %s failed\n" , + g_per->name); +return 0 ; } static __exit int gpmc_remove(struct platform_device *pdev) { +struct gpmc_peripheral *g_per = gpmc_peripheral; + +for (; gpmc_num_peripheral; g_per++, gpmc_num_peripheral--) +platform_device_unregister (g_per->pdev); + + gpmc_waitpin_map =0 ; gpmc_waitpin_nr =GPMC_MAX_NR_WAITPIN ;gpmc_free_irq ();gpmc_mem_exit ();
Signature · 1 line
1.7.10.2
PATCH v6 11/13 ARM: OMAP2+: gpmc: cs reconfigure helper
In reply to Afzal MohammedHelper for reconfiguring CS. This helps if a
peripheral needs to reconfigure GPMC settings
different from the one which was configured
during probe.
--- arch/arm/mach-omap2/gpmc.c | 32 ++++++++++++++++++++++++++++++++ arch/arm/plat-omap/include/plat/gpmc.h | 2 ++ 2 files changed, 34 insertions(+)
arch/arm/mach-omap2/gpmc.c +32 -0
@@ -1420,6 +1420,38 @@ struct platform_device *gpmc_create_device(struct gpmc_peripheral *p)return p->pdev; } +static int gpmc_match_device (char *name,int id) +{ +int i; +struct gpmc_peripheral *g_per = gpmc_peripheral; + +for (i =0 ; i < gpmc_num_peripheral; i++, g_per++) +if (!strcmp (g_per->name, name) && g_per->id == id) +return i; + +return -ENOENT ; +} + +int gpmc_cs_reconfigure (char *name,int id,struct gpmc_cs_data *c) +{ +int i; + + i =gpmc_match_device (name, id); +if (IS_ERR_VALUE (i)) { +dev_err (gpmc_dev,"no device %s.%d to configure\n" , name, id); +return i; + } + +if (IS_ERR_VALUE (gpmc_setup_cs_config_timing (gpmc_peripheral + i, c))) { +dev_err (gpmc_dev,"error: configure device %s.%d\n" , name, id); +return i; + } + +return gpmc_setup_waitpin (gpmc_peripheral + i); + +} +EXPORT_SYMBOL_GPL (gpmc_cs_reconfigure); +static __devinit int gpmc_probe (struct platform_device *pdev) {u32 l;
arch/arm/plat-omap/include/plat/gpmc.h +2 -0
@@ -248,6 +248,8 @@ extern int gpmc_cs_configure(int cs, int cmd, int wval);extern int gpmc_nand_read (int cs,int cmd);extern int gpmc_nand_write (int cs,int cmd,int wval); +extern int gpmc_cs_reconfigure (char *name,int id,struct gpmc_cs_data *c); +int gpmc_enable_hwecc (int cs,int mode,int dev_width,int ecc_size);int gpmc_calculate_ecc (int cs,const u_char *dat,u_char *ecc_code);
Signature · 1 line
1.7.10.2
PATCH v6 12/13 ARM: OMAP2+: gpmc: update nand register info
In reply to Afzal MohammedGPMC has dedicated NAND handling blocks and have a few registers exclusively meant for NAND operations. These registers can be handled by OMAP NAND driver as it is meant for handling NAND on GPMC. Update OMAP NAND platform data with GPMC-NAND register details so that OMAP NAND driver can handle by itself instead of relying on GPMC exported symbols.
--- arch/arm/mach-omap2/gpmc.c | 9 ++++++++- arch/arm/plat-omap/include/plat/gpmc.h | 1 + 2 files changed, 9 insertions(+), 1 deletion(-)
arch/arm/mach-omap2/gpmc.c +8 -1
@@ -28,6 +28,7 @@#include <asm/mach-types.h> #include <plat/gpmc.h> +#include <plat/nand.h> #include <plat/omap_device.h> #include <plat/sdrc.h>
@@ -1497,12 +1498,18 @@ static __devinit int gpmc_probe(struct platform_device *pdev) gpmc_waitpin_nr = gp->waitpin_nr;/* Traverse NULL terminated array of peripheral pointers and setup */ -for (gdq = gp->device_pdata, g_per = gpmc_peripheral; *gdq; gdq++) +for (gdq = gp->device_pdata, g_per = gpmc_peripheral; *gdq; gdq++) { +if ((*gdq)->is_nand) { +struct omap_nand_platform_data *p = (*gdq)->pdata; + +gpmc_update_nand_reg (&p->reg, p->cs); + }if (IS_ERR_VALUE (gpmc_setup_device (g_per, *gdq)))dev_err (gpmc_dev,"gpmc setup on %s failed\n" , (*gdq)->name);else g_per++; + } gpmc_num_peripheral = g_per - gpmc_peripheral; for (l = 0, g_per = gpmc_peripheral;
arch/arm/plat-omap/include/plat/gpmc.h +1 -0
@@ -196,6 +196,7 @@ struct gpmc_device_pdata {unsigned per_res_cnt;struct gpmc_cs_data *cs_data;unsigned num_cs; +bool is_nand; };struct gpmc_pdata {
Signature · 1 line
1.7.10.2
PATCH v6 13/13 ARM: OMAP2+: gpmc: configure writeprotect
In reply to Afzal MohammedGPMC has a writeprotect pin that can be connected to peripherals. Default is to disable writeprotect. In case of conflicting requirement of writeprotect, it will be left in enabled state to be on safer side, along with a warning to attract user attention.
--- arch/arm/mach-omap2/gpmc.c | 42 ++++++++++++++++++++++++++++++++ arch/arm/plat-omap/include/plat/gpmc.h | 2 ++ 2 files changed, 44 insertions(+)
arch/arm/mach-omap2/gpmc.c +42 -0
@@ -110,6 +110,12 @@ enum {GPMC_MAX_NR_WAITPIN }; +enum { +NONE , +OFF , +ON +}; +struct gpmc_client_irq {unsigned irq;u32 bitmask;
@@ -161,6 +167,7 @@ static struct gpmc_peripheral gpmc_peripheral[GPMC_CS_NUM];static unsigned gpmc_num_peripheral;static unsigned gpmc_waitpin_map;static unsigned gpmc_waitpin_nr =GPMC_MAX_NR_WAITPIN ; +static unsigned gpmc_writeprotect;static struct gpmc_client_irq gpmc_client_irq[GPMC_NR_IRQ ];static struct irq_chip gpmc_irq_chip;
@@ -192,6 +199,18 @@ static u32 gpmc_read_reg(int idx)return __raw_readl (gpmc_base + idx); } +static inline void gpmc_modify_reg (int idx,u32 mask,bool value) +{ +u32 l; + + l =gpmc_read_reg (idx); +if (value) + l |= mask; +else + l &= ~mask; +gpmc_write_reg (idx, l); +} +static void gpmc_cs_write_byte (int cs,int idx,u8 val) {void __iomem *reg_addr;
@@ -991,6 +1010,7 @@ static __devinit int gpmc_setup_cs_mem(struct gpmc_cs_data *cs,static void gpmc_setup_cs_config (unsigned cs,unsigned conf) {u32 l =gpmc_cs_read_reg (cs,GPMC_CS_CONFIG1 ); +unsigned wp; l &= ~(GPMC_CONFIG1_MUXADDDATA | GPMC_CONFIG1_WRITETYPE_SYNC |
@@ -1025,6 +1045,19 @@ static void gpmc_setup_cs_config(unsigned cs, unsigned conf) l |= conf;gpmc_cs_write_reg (cs,GPMC_CS_CONFIG1 , l); + +if (conf &GPMC_CONFIG_ENABLE_WRITEPROTECT ) + wp =ON ; +else + wp =OFF ; + +if (gpmc_writeprotect) { +if (gpmc_writeprotect != wp) { +dev_warn (gpmc_dev,"conflicting writeprotect requests, writeprotect is left enabled\n" ); + gpmc_writeprotect =ON ; + } + }else + gpmc_writeprotect = wp; } static inline void gpmc_set_one_timing(int cs, int reg, int start,
@@ -1453,6 +1486,12 @@ int gpmc_cs_reconfigure(char *name, int id, struct gpmc_cs_data *c) }EXPORT_SYMBOL_GPL (gpmc_cs_reconfigure); +static inline void gpmc_setup_writeprotect (void ) +{ +gpmc_modify_reg (GPMC_CONFIG ,GPMC_CONFIG_WRITEPROTECT , + gpmc_writeprotect ==ON ? false : true); +} +static __devinit int gpmc_probe (struct platform_device *pdev) {u32 l;
@@ -1512,6 +1551,8 @@ static __devinit int gpmc_probe(struct platform_device *pdev) } gpmc_num_peripheral = g_per - gpmc_peripheral; +gpmc_setup_writeprotect (); +for (l =0 , g_per = gpmc_peripheral; l < gpmc_num_peripheral; l++, g_per++)if (IS_ERR (gpmc_create_device (g_per)))
@@ -1528,6 +1569,7 @@ static __exit int gpmc_remove(struct platform_device *pdev)for (; gpmc_num_peripheral; g_per++, gpmc_num_peripheral--)platform_device_unregister (g_per->pdev); + gpmc_writeprotect =NONE ; gpmc_waitpin_map =0 ; gpmc_waitpin_nr =GPMC_MAX_NR_WAITPIN ;gpmc_free_irq ();
arch/arm/plat-omap/include/plat/gpmc.h +2 -0
@@ -79,6 +79,8 @@#define GPMC_CONFIG1_FCLK_DIV4 (GPMC_CONFIG1_FCLK_DIV(3))#define GPMC_CONFIG7_CSVALID (1 << 6) +#define GPMC_CONFIG_ENABLE_WRITEPROTECT (1 << 5) +#define GPMC_WAITPIN_ACTIVE_HIGH (1 << 4)#define GPMC_WAITPIN_ACTIVE_LOW (0 << 4)#define GPMC_WAITPIN_0 (1 << 0)
Signature · 1 line
1.7.10.2
v6 cover GPMC driver conversion
In reply to Afzal MohammedHi Tony,
Quoted 12 lines · Afzal
This series is based on 3.5-rc1, and is dependent on [1,2,3], and has been tested on omap3evm (smsc911x) rev G & C and beagle board(nand). Also using private patches, nand & onenand was tested on omap3evm, rev G & C respectively (as support for these were not in mainline)
All boards will work using the old existing interface. Further patch series would convert all boards to use new interface.
Many of GPMC peripherals depend on bootloader for configuration. This is going to be deprecated. feature-removal-schedule.txt will be updated in one of the upcoming patch series.
Quoted 5 lines
Thanks to Tony & Jon for their various suggestions.
[PATCH 03/13] ARM: OMAP2+: gpmc: driver migration helper, is to be reverted once all GPMC peripherals are migrated to use driver interface.
Please let me know your comments on this series.
Regards
Afzal