Skip to contentSkip to conversation

GPMC driver conversion

PATCH Superseded Afzal Mohammed 4 participants 116 messages Jun 11, 2012 – Jun 22, 2012

Subsystems (5)

v5 of 9 Superseded Latest is v9 →

lore
Share & tools
lore
#0 lore raw

v5 cover GPMC driver conversion

Hi,

This series is based on 3.5-rc1, and is dependent on [1,2,3]

This series has been tested on omap3evm (smsc911x) rev G & C and beagle board(nand) using patch series which is going to be posted shortly (this series only creates a driver out of GPMC code)

Also using private patches, nand & onenand was tested on omap3evm, rev G & C respectively (as support for these were not in mainline)

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 regarding the same.

[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/msg69501.html
[2] http://www.mail-archive.com/linux-omap at vger.kernel.org/msg69881.html
[3] http://www.mail-archive.com/linux-omap at vger.kernel.org/msg69891.html

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 (14):
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: time setting (register#) helper
ARM: OMAP2+: gpmc: bool type timing 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: writeprotect helper

 arch/arm/mach-omap2/gpmc.c             |  817 ++++++++++++++++++++++++++++++--
 arch/arm/plat-omap/include/plat/gpmc.h |   68 +++
 2 files changed, 842 insertions(+), 43 deletions(-)
Signature · 1 line
1.7.10.2
#1 lore raw

PATCH v5 1/14 ARM: OMAP2+: gpmc: platform definitions

gpmc driver platform definitions

---
 arch/arm/plat-omap/include/plat/gpmc.h |   49 ++++++++++++++++++++++++++++++++
 1 file changed, 49 insertions(+)
arch/arm/plat-omap/include/plat/gpmc.h +49 -0
index 802fb22..21a8cce 100644
@@ -139,6 +139,55 @@ struct gpmc_timings {
 	u16 wr_data_mux_bus;	/* WRDATAONADMUXBUS */
 };
 
+/* bool type time settings */
+struct gpmc_misc_timings {
+	bool cycle2cyclediffcsen;
+	bool cycle2cyclesamecsen;
+	bool we_extra_delay;
+	bool oe_extra_delay;
+	bool adv_extra_delay;
+	bool cs_extra_delay;
+	bool time_para_granularity;
+};
+
+enum {
+	has_none,
+	has_period,
+	has_clock
+};
+
+struct gpmc_time_ctrl {
+	int type;
+	struct gpmc_timings timings;
+	struct gpmc_misc_timings bool_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			num_device;
+	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
#2 lore raw

PATCH v5 1/14 ARM: OMAP2+: gpmc: platform definitions

Quoted 27 lines · Afzal Mohammed

gpmc driver platform definitions

---
 arch/arm/plat-omap/include/plat/gpmc.h |   49 ++++++++++++++++++++++++++++++++
 1 file changed, 49 insertions(+)
arch/arm/plat-omap/include/plat/gpmc.h +12 -0
index 802fb22..21a8cce 100644
@@ -139,6 +139,55 @@ struct gpmc_timings {
 	u16 wr_data_mux_bus;	/* WRDATAONADMUXBUS */
 };
 
+/* bool type time settings */
+struct gpmc_misc_timings {
+	bool cycle2cyclediffcsen;
+	bool cycle2cyclesamecsen;
+	bool we_extra_delay;
+	bool oe_extra_delay;
+	bool adv_extra_delay;
+	bool cs_extra_delay;
+	bool time_para_granularity;
+};
+
+enum {

arch/arm/plat-omap/include/plat/gpmc.h struct gpmc_timings { · line 163

Quoted 10 lines
arch/arm/plat-omap/include/plat/gpmc.h +10 -0
+	has_none,
+	has_period,
+	has_clock
+};
+
+struct gpmc_time_ctrl {
+	int type;
+	struct gpmc_timings timings;
+	struct gpmc_misc_timings bool_timings;
+};

Why not combine misc_timings and the original timings? I don't see why these need to be kept separate. Even if combined it can still be backward compatible for legacy boards as they will not specify the misc timing fields. I am also not convinced we need this type member.

Jon

Quoted 29 lines
arch/arm/plat-omap/include/plat/gpmc.h +26 -0
+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			num_device;
+	struct gpmc_device_pdata	**device_pdata;
+};
+
 struct gpmc_nand_regs {
 	void __iomem	*gpmc_status;
 	void __iomem	*gpmc_nand_command;
#3 lore raw

PATCH v5 1/14 ARM: OMAP2+: gpmc: platform definitions

Hi Jon,

Quoted 16 lines · Jon

arch/arm/plat-omap/include/plat/gpmc.h struct gpmc_timings { · line 163

Quoted 11 lines · Afzal Mohammed
arch/arm/plat-omap/include/plat/gpmc.h +11 -0
+enum {
+	has_none,
+	has_period,
+	has_clock
+};
+
+struct gpmc_time_ctrl {
+	int type;
+	struct gpmc_timings timings;
+	struct gpmc_misc_timings bool_timings;
+};

Why not combine misc_timings and the original timings? I don't see why these need to be kept separate. Even if combined it can still be

To keep similar things together, as misc_timings are all bool type, while existing are not so. This could have been put inside gpmc_timings, but wanted to reduce chance of issues for users of old interface.

Quoted 2 lines

backward compatible for legacy boards as they will not specify the misc timing fields. I am also not convinced we need this type member.

Reason: http://www.mail-archive.com/linux-omap at vger.kernel.org/msg69041.html

Regards
Afzal

#4 lore raw

PATCH v5 2/14 ARM: OMAP2+: gpmc: Adapt to HWMOD

In reply to Afzal Mohammed

Create API for platforms to adapt gpmc to HWMOD

---
 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
index 517953f..b471c2f 100644
@@ -27,6 +27,7 @@
 
 #include <asm/mach-types.h>
 #include <plat/gpmc.h>
+#include <plat/omap_device.h>
 
 #include <plat/sdrc.h>
 
@@ -898,6 +899,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;
+	}
+
+	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);
+	}
+
+	gpmc_l3_clk = clk_get(NULL, oh->main_clk);
+	if (IS_ERR(gpmc_l3_clk)) {
+		pr_err("Could not get GPMC clock\n");
+		return PTR_ERR(gpmc_l3_clk);
+	}
+
+	return 0;
+}
+
 static irqreturn_t gpmc_handle_irq(int irq, void *dev)
 {
 	int i;
arch/arm/plat-omap/include/plat/gpmc.h +2 -0
index 21a8cce..698fa33 100644
@@ -207,6 +207,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
#5 lore raw

PATCH v5 2/14 ARM: OMAP2+: gpmc: Adapt to HWMOD

Hi Afzal,

Quoted 40 lines · Afzal Mohammed

Create API for platforms to adapt gpmc to HWMOD

---
 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 +17 -0
index 517953f..b471c2f 100644
@@ -27,6 +27,7 @@
 
 #include <asm/mach-types.h>
 #include <plat/gpmc.h>
+#include <plat/omap_device.h>
 
 #include <plat/sdrc.h>
 
@@ -898,6 +899,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;
+	}
+
+	pdev = omap_device_build(name, -1, oh, pdata,
+					sizeof(*pdata), NULL, 0, 0);
+	if (IS_ERR(pdev)) {

arch/arm/mach-omap2/gpmc.c static int __init gpmc_init(void) · line 927

Quoted 10 lines
arch/arm/mach-omap2/gpmc.c +10 -0
+		WARN(1, "Can't build omap_device for %s:%s.\n",
+						name, oh->name);
+		return PTR_ERR(pdev);
+	}
+
+	gpmc_l3_clk = clk_get(NULL, oh->main_clk);
+	if (IS_ERR(gpmc_l3_clk)) {
+		pr_err("Could not get GPMC clock\n");
+		return PTR_ERR(gpmc_l3_clk);
+	}

My preference would be to store gpmc_l3_clk in the pdata and pass to probe via the pdata. The aim would be to remove the global gpmc_l3_clk altogether.

Also we should attempt to get the clk before calling omap_device_build which is registering the driver.

Cheers
Jon

#6 lore raw

PATCH v5 2/14 ARM: OMAP2+: gpmc: Adapt to HWMOD

Hi Jon,

Quoted 9 lines · Jon
Quoted 9 lines
arch/arm/mach-omap2/gpmc.c +9 -0
+	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);
+	}
+
+	gpmc_l3_clk = clk_get(NULL, oh->main_clk);
Quoted 8 lines

arch/arm/mach-omap2/gpmc.c static int __init gpmc_init(void) · line 927

Quoted 4 lines
arch/arm/mach-omap2/gpmc.c +4 -0
+	if (IS_ERR(gpmc_l3_clk)) {
+		pr_err("Could not get GPMC clock\n");
+		return PTR_ERR(gpmc_l3_clk);
+	}

My preference would be to store gpmc_l3_clk in the pdata and pass to probe via the pdata. The aim would be to remove the global gpmc_l3_clk altogether.

For timing calculation by platform outside of driver, we need clk rate

Quoted 2 lines

Also we should attempt to get the clk before calling omap_device_build which is registering the driver.

omap_device_build registers only device, but I agree that clk should be obtained before it as driver can get probed at that point

Regards,
Afzal

#7 lore raw

PATCH v5 2/14 ARM: OMAP2+: gpmc: Adapt to HWMOD

Quoted 22 lines · Afzal

Hi Jon,

Quoted 9 lines · Jon
Quoted 9 lines
arch/arm/mach-omap2/gpmc.c +9 -0
+	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);
+	}
+
+	gpmc_l3_clk = clk_get(NULL, oh->main_clk);
Quoted 8 lines

arch/arm/mach-omap2/gpmc.c static int __init gpmc_init(void) · line 927

Quoted 4 lines
arch/arm/mach-omap2/gpmc.c +4 -0
+	if (IS_ERR(gpmc_l3_clk)) {
+		pr_err("Could not get GPMC clock\n");
+		return PTR_ERR(gpmc_l3_clk);
+	}

My preference would be to store gpmc_l3_clk in the pdata and pass to probe via the pdata. The aim would be to remove the global gpmc_l3_clk altogether.

Quoted 1 line

For timing calculation by platform outside of driver, we need clk rate

Right but potentially, this could be done by the driver.

Quoted 5 lines
Quoted 2 lines

Also we should attempt to get the clk before calling omap_device_build which is registering the driver.

omap_device_build registers only device, but I agree that clk should be obtained before it as driver can get probed at that point

Exactly.

Jon

#8 lore raw

PATCH v5 2/14 ARM: OMAP2+: gpmc: Adapt to HWMOD

Hi Jon,

Quoted 10 lines · Jon
Quoted 7 lines · Afzal
Quoted 3 lines · Jon

My preference would be to store gpmc_l3_clk in the pdata and pass to probe via the pdata. The aim would be to remove the global gpmc_l3_clk altogether.

For timing calculation by platform outside of driver, we need clk rate

Right but potentially, this could be done by the driver.

I do not think it is practically possible. Please see timing calculations in arch/arm/mach-omap2/gpmc-*, the way it is done for different peripherals are different, and we cannot expect gpmc driver to do those as that would require gpmc driver being aware of type of peripheral connected.

And all those gpmc-* timing calculation needs to be done before driver is ready, they rely on functions like gpmc_get_fclk_rate(), which in turn requires the clk rate to be available before driver is probed.

Regards
Afzal

maintainer
#9 lore raw

PATCH v5 2/14 ARM: OMAP2+: gpmc: Adapt to HWMOD

Quoted 14 lines · Mohammed, Afzal

Hi Jon,

Quoted 10 lines · Jon
Quoted 7 lines · Afzal
Quoted 3 lines · Jon

My preference would be to store gpmc_l3_clk in the pdata and pass to probe via the pdata. The aim would be to remove the global gpmc_l3_clk altogether.

For timing calculation by platform outside of driver, we need clk rate

Right but potentially, this could be done by the driver.

Quoted 8 lines

I do not think it is practically possible. Please see timing calculations in arch/arm/mach-omap2/gpmc-*, the way it is done for different peripherals are different, and we cannot expect gpmc driver to do those as that would require gpmc driver being aware of type of peripheral connected.

And all those gpmc-* timing calculation needs to be done before driver is ready, they rely on functions like gpmc_get_fclk_rate(), which in turn requires the clk rate to be available before driver is probed.

Yeah I also think the GPMC code should handle the L3 timings, and dynamically calculate them for DVFS when L3 frequency changes. Does the GPMC have enough information now to do that?

Additionally GPMC should add constraints to DVFS if not enough data is know to scale L3.

Tony

#10 lore raw

PATCH v5 2/14 ARM: OMAP2+: gpmc: Adapt to HWMOD

Hi Tony,

Quoted 12 lines · Tony Lindgren
Quoted 11 lines · Mohammed, Afzal

Hi Jon,

Quoted 1 line · Jon

Right but potentially, this could be done by the driver.

I do not think it is practically possible. Please see timing calculations in arch/arm/mach-omap2/gpmc-*, the way it is done for different peripherals are different, and we cannot expect gpmc driver to do those as that would require gpmc driver being aware of type of peripheral connected.

Quoted 7 lines
Quoted 3 lines

And all those gpmc-* timing calculation needs to be done before driver is ready, they rely on functions like gpmc_get_fclk_rate(), which in turn requires the clk rate to be available before driver is probed.

Yeah I also think the GPMC code should handle the L3 timings, and dynamically calculate them for DVFS when L3 frequency changes. Does the GPMC have enough information now to do that?

Do you mean that gpmc driver should have the capability to calculate peripheral timings at runtime based on frequency ?, I am not sure how this can be handled by gpmc driver as calculation for different peripherals are done in different way, requiring gpmc driver to know about connected peripheral, that would imply that gpmc driver would not be peripheral agnostic.

Or else some sort of a callback to be used ?

Out of the 20,14 are depending on bootloader, both omap3evm & beagle has been converted to utilize runtime calculation, but for other 12 boards, first we need to get values used by bootloader (those include peripherals that doesn't have gpmc-* helpers), then derive it based on expression, after that only, we will have information to achieve it and those are the ones that I do not have access to.

Regards
Afzal

Quoted 6 lines

Additionally GPMC should add constraints to DVFS if not enough data is know to scale L3.

Tony

maintainer
#11 lore raw

PATCH v5 2/14 ARM: OMAP2+: gpmc: Adapt to HWMOD

Quoted 23 lines · Mohammed, Afzal

Hi Tony,

Quoted 12 lines · Tony Lindgren
Quoted 11 lines · Mohammed, Afzal

Hi Jon,

Quoted 1 line · Jon

Right but potentially, this could be done by the driver.

I do not think it is practically possible. Please see timing calculations in arch/arm/mach-omap2/gpmc-*, the way it is done for different peripherals are different, and we cannot expect gpmc driver to do those as that would require gpmc driver being aware of type of peripheral connected.

Quoted 7 lines
Quoted 3 lines

And all those gpmc-* timing calculation needs to be done before driver is ready, they rely on functions like gpmc_get_fclk_rate(), which in turn requires the clk rate to be available before driver is probed.

Yeah I also think the GPMC code should handle the L3 timings, and dynamically calculate them for DVFS when L3 frequency changes. Does the GPMC have enough information now to do that?

Quoted 7 lines

Do you mean that gpmc driver should have the capability to calculate peripheral timings at runtime based on frequency ?, I am not sure how this can be handled by gpmc driver as calculation for different peripherals are done in different way, requiring gpmc driver to know about connected peripheral, that would imply that gpmc driver would not be peripheral agnostic.

Or else some sort of a callback to be used ?

Oops yeah right, we have some platform_retime functions that are in the driver platform init code. It would be best that the drivers can do the recalculation with no platform init code callbacks needed.

Quoted 6 lines

Out of the 20,14 are depending on bootloader, both omap3evm & beagle has been converted to utilize runtime calculation, but for other 12 boards, first we need to get values used by bootloader (those include peripherals that doesn't have gpmc-* helpers), then derive it based on expression, after that only, we will have information to achieve it and those are the ones that I do not have access to.

It's OK to use fixed timings as long as we disable L3 scaling. Some of these values we'll probably never be able to calculate dynamically.

Regards,

Tony

#12 lore raw

PATCH v5 2/14 ARM: OMAP2+: gpmc: Adapt to HWMOD

Hi Tony,

Quoted 9 lines · Tony Lindgren
Quoted 8 lines · Mohammed, Afzal

On Wed, Jun 13, 2012 at 17:32:09, Tony Lindgren wrote:

Do you mean that gpmc driver should have the capability to calculate peripheral timings at runtime based on frequency ?, I am not sure how this can be handled by gpmc driver as calculation for different peripherals are done in different way, requiring gpmc driver to know about connected peripheral, that would imply that gpmc driver would not be peripheral agnostic.

Quoted 5 lines
Quoted 1 line

Or else some sort of a callback to be used ?

Oops yeah right, we have some platform_retime functions that are in the driver platform init code. It would be best that the drivers can do the recalculation with no platform init code callbacks needed.

Other than using a platform callback, I am not getting any idea as of now, how this can be achieved, let me think over it, if you have any suggestions on ways to deal it, please let me know.

Quoted 10 lines
Quoted 7 lines

Out of the 20,14 are depending on bootloader, both omap3evm & beagle has been converted to utilize runtime calculation, but for other 12 boards, first we need to get values used by bootloader (those include peripherals that doesn't have gpmc-* helpers), then derive it based on expression, after that only, we will have information to achieve it and those are the ones that I do not have access to.

It's OK to use fixed timings as long as we disable L3 scaling. Some of these values we'll probably never be able to calculate dynamically.

Ok

Regards
Afzal

#13 lore raw

PATCH v5 2/14 ARM: OMAP2+: gpmc: Adapt to HWMOD

In reply to Mohammed, Afzal

Hi Afzal,

Quoted 23 lines · Afzal

Hi Tony,

Quoted 12 lines · Tony Lindgren
Quoted 11 lines · Mohammed, Afzal

Hi Jon,

Quoted 1 line · Jon

Right but potentially, this could be done by the driver.

I do not think it is practically possible. Please see timing calculations in arch/arm/mach-omap2/gpmc-*, the way it is done for different peripherals are different, and we cannot expect gpmc driver to do those as that would require gpmc driver being aware of type of peripheral connected.

Quoted 7 lines
Quoted 3 lines

And all those gpmc-* timing calculation needs to be done before driver is ready, they rely on functions like gpmc_get_fclk_rate(), which in turn requires the clk rate to be available before driver is probed.

Yeah I also think the GPMC code should handle the L3 timings, and dynamically calculate them for DVFS when L3 frequency changes. Does the GPMC have enough information now to do that?

Quoted 5 lines

Do you mean that gpmc driver should have the capability to calculate peripheral timings at runtime based on frequency ?, I am not sure how this can be handled by gpmc driver as calculation for different peripherals are done in different way, requiring gpmc driver to know about connected peripheral, that would imply that gpmc driver would not be peripheral agnostic.

I guess I still don't agree with this. From the gpmc timing point of view it should not care what device is connected, it just needs to know the associated timings. Therefore, the gpmc driver just needs the time associated with the different timings fields in the various GPMC_CONFIGx registers and then convert to clocks based upon the gpmc fclk. The only item that is device specific here is the actual timing values and these can be passed to the driver.

Furthermore, gpmc_cs_set_timings function is completely device agnostic. Why can we not call this from within the driver? Why does it need to be called outside the driver?

I am looking for you to prove me wrong about this, but if the only reason is because we need to fclk rate and timings, then as I have mentioned before we can pass these to the driver. So I am looking for a stronger reason than this of why this would not work.

Cheers
Jon

#14 lore raw

PATCH v5 2/14 ARM: OMAP2+: gpmc: Adapt to HWMOD

Hi Jon,

Quoted 16 lines · Jon
Quoted 5 lines · Afzal

Do you mean that gpmc driver should have the capability to calculate peripheral timings at runtime based on frequency ?, I am not sure how this can be handled by gpmc driver as calculation for different peripherals are done in different way, requiring gpmc driver to know about connected peripheral, that would imply that gpmc driver would not be peripheral agnostic.

I guess I still don't agree with this. From the gpmc timing point of view it should not care what device is connected, it just needs to know the associated timings. Therefore, the gpmc driver just needs the time associated with the different timings fields in the various GPMC_CONFIGx registers and then convert to clocks based upon the gpmc fclk. The only item that is device specific here is the actual timing values and these can be passed to the driver.

Quoted 3 lines

Furthermore, gpmc_cs_set_timings function is completely device agnostic. Why can we not call this from within the driver? Why does it need to be called outside the driver?

As mentioned in one of my previous mails, input to gpmc_cs_set_timings sometimes are a function of gpmc clock, and that function can vary with the type of peripheral connected.

Regards
Afzal

#15 lore raw

PATCH v5 2/14 ARM: OMAP2+: gpmc: Adapt to HWMOD

In reply to Mohammed, Afzal

Hi Afzal,

Quoted 14 lines · Afzal

Hi Jon,

Quoted 10 lines · Jon
Quoted 7 lines · Afzal
Quoted 3 lines · Jon

My preference would be to store gpmc_l3_clk in the pdata and pass to probe via the pdata. The aim would be to remove the global gpmc_l3_clk altogether.

For timing calculation by platform outside of driver, we need clk rate

Right but potentially, this could be done by the driver.

Quoted 8 lines

I do not think it is practically possible. Please see timing calculations in arch/arm/mach-omap2/gpmc-*, the way it is done for different peripherals are different, and we cannot expect gpmc driver to do those as that would require gpmc driver being aware of type of peripheral connected.

And all those gpmc-* timing calculation needs to be done before driver is ready, they rely on functions like gpmc_get_fclk_rate(), which in turn requires the clk rate to be available before driver is probed.

So I see that the various gpmc-*.c files have some form of _retime() function. However, at the end of the day they all call gpmc_cs_set_timings() to convert time into gpmc clocks. Converting time to gpmc clocks is completely independent of the actual device and so this can be performed by the driver. We just need to populate the gpmc_timings struct and pass to the driver to convert to clocks and program into the registers.

If the clk handle for the gpmc is passed to the gpmc driver, then there is no reason why the driver cannot do this.

Obviously, I could be missing something fundamental here, but my assumption is that if the driver has the handle the fclk (and hence can query the clock rate) and has then gpmc_timings struct (with timings in time), then that is all it needs.

Cheers
Jon

#16 lore raw

PATCH v5 2/14 ARM: OMAP2+: gpmc: Adapt to HWMOD

Hi Jon,

Quoted 9 lines · Jon
Quoted 8 lines

I do not think it is practically possible. Please see timing calculations in arch/arm/mach-omap2/gpmc-*, the way it is done for different peripherals are different, and we cannot expect gpmc driver to do those as that would require gpmc driver being aware of type of peripheral connected.

And all those gpmc-* timing calculation needs to be done before driver is ready, they rely on functions like gpmc_get_fclk_rate(), which in turn requires the clk rate to be available before driver is probed.

Quoted 7 lines

So I see that the various gpmc-*.c files have some form of _retime() function. However, at the end of the day they all call gpmc_cs_set_timings() to convert time into gpmc clocks. Converting time to gpmc clocks is completely independent of the actual device and so this can be performed by the driver. We just need to populate the gpmc_timings struct and pass to the driver to convert to clocks and program into the registers.

gpmc_cs_set_timings() does currently convert time to clock cycles required, and this gpmc driver have the capability to do it.

What I was saying is a different issue, input to gpmc_cs_set_timings, which is time sometimes in turn is a function of time or to be exact depends on gpmc clock period also. So timings provided to gpmc_cs_set_timings for a particular frequency may not hold good for another frequency, unless we change the input time to gpmc_cs_set_timings based on gpmc clock.

If you see gpmc-* files, many a times, they need to know value of gpmc fclk, to calculate the input time to be fed for gpmc_cs_set_timings

Regards
Afzal

#17 lore raw

PATCH v5 2/14 ARM: OMAP2+: gpmc: Adapt to HWMOD

Hi Jon,

Quoted 11 lines · Afzal

gpmc_cs_set_timings() does currently convert time to clock cycles required, and this gpmc driver have the capability to do it.

What I was saying is a different issue, input to gpmc_cs_set_timings, which is time sometimes in turn is a function of time or to be exact depends on gpmc clock period also. So timings provided to gpmc_cs_set_timings for a particular frequency may not hold good for another frequency, unless we change the input time to gpmc_cs_set_timings based on gpmc clock.

If you see gpmc-* files, many a times, they need to know value of gpmc fclk, to calculate the input time to be fed for gpmc_cs_set_timings

And the way value of gpmc period has to be handled differs depending on the peripheral.

Regards
Afzal

#18 lore raw

PATCH v5 2/14 ARM: OMAP2+: gpmc: Adapt to HWMOD

In reply to Mohammed, Afzal

Hi Afzal,

Quoted 21 lines · Afzal

Hi Jon,

Quoted 9 lines · Jon
Quoted 8 lines

I do not think it is practically possible. Please see timing calculations in arch/arm/mach-omap2/gpmc-*, the way it is done for different peripherals are different, and we cannot expect gpmc driver to do those as that would require gpmc driver being aware of type of peripheral connected.

And all those gpmc-* timing calculation needs to be done before driver is ready, they rely on functions like gpmc_get_fclk_rate(), which in turn requires the clk rate to be available before driver is probed.

Quoted 7 lines

So I see that the various gpmc-*.c files have some form of _retime() function. However, at the end of the day they all call gpmc_cs_set_timings() to convert time into gpmc clocks. Converting time to gpmc clocks is completely independent of the actual device and so this can be performed by the driver. We just need to populate the gpmc_timings struct and pass to the driver to convert to clocks and program into the registers.

Quoted 8 lines

gpmc_cs_set_timings() does currently convert time to clock cycles required, and this gpmc driver have the capability to do it.

What I was saying is a different issue, input to gpmc_cs_set_timings, which is time sometimes in turn is a function of time or to be exact depends on gpmc clock period also. So timings provided to gpmc_cs_set_timings for a particular frequency may not hold good for another frequency, unless we change the input time to gpmc_cs_set_timings based on gpmc clock.

Absolutely! No argument there.

Quoted 2 lines

If you see gpmc-* files, many a times, they need to know value of gpmc fclk, to calculate the input time to be fed for gpmc_cs_set_timings

I understand, but that is not my point. My point is that timings should be provided in nanoseconds for all devices. Then the gpmc driver can query the gpmc fclk and convert to gpmc cycles. This way the gpmc driver will not care what device is connected in terms of the timings and can convert them.

What does make this a bit more difficult is the function gpmc_round_ns_to_ticks(). It appears to convert nanoseconds to ticks and back to nanoseconds. I am guessing to account for some rounding error. I am curious what impact this function is having on the timing. Ideally this should be handle in gpmc_cs_set_timings().

Cheers
Jon

#19 lore raw

PATCH v5 2/14 ARM: OMAP2+: gpmc: Adapt to HWMOD

Quoted 5 lines · Jon Hunter

What does make this a bit more difficult is the function gpmc_round_ns_to_ticks(). It appears to convert nanoseconds to ticks and back to nanoseconds. I am guessing to account for some rounding error. I am curious what impact this function is having on the timing. Ideally this should be handle in gpmc_cs_set_timings().

I don't have the code in front of me at the moment, nor my old GPMC timing spreadsheet, so this comment may be superfluous. But since kernel arithmetic needs to be in integers, it might be necessary to do some of the timing calculations in picosecond units to avoid roundoff issues.

- Paul

#20 lore raw

PATCH v5 2/14 ARM: OMAP2+: gpmc: Adapt to HWMOD

Hi Paul,

Quoted 8 lines · Paul Walmsley
Quoted 5 lines · Jon Hunter

What does make this a bit more difficult is the function gpmc_round_ns_to_ticks(). It appears to convert nanoseconds to ticks and back to nanoseconds. I am guessing to account for some rounding error. I am curious what impact this function is having on the timing. Ideally this should be handle in gpmc_cs_set_timings().

Quoted 4 lines

I don't have the code in front of me at the moment, nor my old GPMC timing spreadsheet, so this comment may be superfluous. But since kernel arithmetic needs to be in integers, it might be necessary to do some of the timing calculations in picosecond units to avoid roundoff issues.

A lot of the GPMC timing functions are converting to pico-seconds and then rounding up to nano-seconds. So I think that the calculations are ok.

The real problem is, that as Afzal has highlighted, is that there is no common method between GPMC peripherals (onenand, smsc, etc) for populating the gpmc timings structure. This is going to cause a bit of a headache for migrating them over to DT. So what we need to do and maybe this is another series, separate from Afzal's series, is really clean-up the populating of the timings.

Cheers
Jon

#21 lore raw

PATCH v5 2/14 ARM: OMAP2+: gpmc: Adapt to HWMOD

In reply to Jon Hunter

Hi Jon,

Quoted 13 lines · Jon
Quoted 8 lines · Afzal

gpmc_cs_set_timings() does currently convert time to clock cycles required, and this gpmc driver have the capability to do it.

What I was saying is a different issue, input to gpmc_cs_set_timings, which is time sometimes in turn is a function of time or to be exact depends on gpmc clock period also. So timings provided to gpmc_cs_set_timings for a particular frequency may not hold good for another frequency, unless we change the input time to gpmc_cs_set_timings based on gpmc clock.

Absolutely! No argument there.

Quoted 8 lines
Quoted 2 lines

If you see gpmc-* files, many a times, they need to know value of gpmc fclk, to calculate the input time to be fed for gpmc_cs_set_timings

I understand, but that is not my point. My point is that timings should be provided in nanoseconds for all devices. Then the gpmc driver can query the gpmc fclk and convert to gpmc cycles. This way the gpmc driver will not care what device is connected in terms of the timings and can convert them.

But the way gpmc clock period has its effect on timings required for different peripherals is different, i.e. input timing to be provided for gpmc_cs_set_timings depends on gpmc clk & it's effect varies with the peripheral

Even for the same peripheral, directly scaling timings for a different frequency may not work

Regards
Afzal

#22 lore raw

PATCH v5 2/14 ARM: OMAP2+: gpmc: Adapt to HWMOD

In reply to Jon Hunter

Hi Jon,

Quoted 2 lines · Jon

If the clk handle for the gpmc is passed to the gpmc driver, then there is no reason why the driver cannot do this.

I believe passing clk details through platform data is an abuse of clock framework.

Regards
Afzal

#23 lore raw

PATCH v5 2/14 ARM: OMAP2+: gpmc: Adapt to HWMOD

Quoted 9 lines · Afzal

Hi Jon,

Quoted 2 lines · Jon

If the clk handle for the gpmc is passed to the gpmc driver, then there is no reason why the driver cannot do this.

I believe passing clk details through platform data is an abuse of clock framework.

Why? You currently have a global variable storing the clock handle. It can be quite common for drivers to know the clock frequencies of their functional clocks. How else can drivers calculate timings?

Jon

#24 lore raw

PATCH v5 2/14 ARM: OMAP2+: gpmc: Adapt to HWMOD

Hi Jon,

Quoted 6 lines · Jon
Quoted 5 lines · Afzal
Quoted 2 lines · Jon

If the clk handle for the gpmc is passed to the gpmc driver, then there is no reason why the driver cannot do this.

Quoted 6 lines
Quoted 2 lines

I believe passing clk details through platform data is an abuse of clock framework.

Why? You currently have a global variable storing the clock handle. It can be quite common for drivers to know the clock frequencies of their functional clocks. How else can drivers calculate timings?

Please see Russell King's comments,

[1] http://permalink.gmane.org/gmane.linux.ports.ppc.embedded/27634
[2] http://www.mail-archive.com/linux-samsung-soc at vger.kernel.org/msg05365.html

Regards
Afzal

#25 lore raw

PATCH v5 2/14 ARM: OMAP2+: gpmc: Adapt to HWMOD

Quoted 17 lines · Afzal

Hi Jon,

Quoted 6 lines · Jon
Quoted 5 lines · Afzal
Quoted 2 lines · Jon

If the clk handle for the gpmc is passed to the gpmc driver, then there is no reason why the driver cannot do this.

Quoted 6 lines
Quoted 2 lines

I believe passing clk details through platform data is an abuse of clock framework.

Why? You currently have a global variable storing the clock handle. It can be quite common for drivers to know the clock frequencies of their functional clocks. How else can drivers calculate timings?

Quoted 4 lines

Please see Russell King's comments,

[1] http://permalink.gmane.org/gmane.linux.ports.ppc.embedded/27634
[2] http://www.mail-archive.com/[email protected]/msg05365.html

Thanks. So I still think you need to get rid of the global variable for storing the gpmc fclk, that is really my point.

So if you look at commit [1] mentioned by Russell in the above thread, the appropriate thing to do would be to create a gpmc clock alias for all OMAP2+ devices and then you could simply call the following from the gpmc probe ...

	gpmc_fck = clk_get(&pdev->dev, "fck");

You could then store somewhere in one of the gpmc structures.

Cheers
Jon

[1] 39a80c7f379e1c1d3e63b204b8353b7381d0a3d5

#26 lore raw

PATCH v5 2/14 ARM: OMAP2+: gpmc: Adapt to HWMOD

Hi Jon,

Quoted 16 lines · Jon
Quoted 11 lines · Afzal
Quoted 3 lines · Jon

Why? You currently have a global variable storing the clock handle. It can be quite common for drivers to know the clock frequencies of their functional clocks. How else can drivers calculate timings?

Please see Russell King's comments,

[1] http://permalink.gmane.org/gmane.linux.ports.ppc.embedded/27634
[2] http://www.mail-archive.com/linux-samsung-soc at vger.kernel.org/msg05365.html

Thanks. So I still think you need to get rid of the global variable for storing the gpmc fclk, that is really my point.

Quoted 8 lines

So if you look at commit [1] mentioned by Russell in the above thread, the appropriate thing to do would be to create a gpmc clock alias for all OMAP2+ devices and then you could simply call the following from the gpmc probe ...

	gpmc_fck = clk_get(&pdev->dev, "fck");

You could then store somewhere in one of the gpmc structures.

Here clock is required even before driver is probed, i.e for platform to calculate timings, that has to be passed through platform data.

I understand the necessity for clk rate information in driver, but seems unless we have a generic way to scale timings for all the kinds of peripheral, having it may not be of much help.

Regards
Afzal

maintainer
#27 lore raw

PATCH v5 2/14 ARM: OMAP2+: gpmc: Adapt to HWMOD

Quoted 24 lines · Mohammed, Afzal

Hi Jon,

Quoted 21 lines · Jon
Quoted 11 lines · Afzal
Quoted 3 lines · Jon

Why? You currently have a global variable storing the clock handle. It can be quite common for drivers to know the clock frequencies of their functional clocks. How else can drivers calculate timings?

Please see Russell King's comments,

[1] http://permalink.gmane.org/gmane.linux.ports.ppc.embedded/27634
[2] http://www.mail-archive.com/linux-samsung-soc at vger.kernel.org/msg05365.html

Thanks. So I still think you need to get rid of the global variable for storing the gpmc fclk, that is really my point.

So if you look at commit [1] mentioned by Russell in the above thread, the appropriate thing to do would be to create a gpmc clock alias for all OMAP2+ devices and then you could simply call the following from the gpmc probe ...

Quoted 6 lines
Quoted 3 lines
	gpmc_fck = clk_get(&pdev->dev, "fck");

You could then store somewhere in one of the gpmc structures.

Here clock is required even before driver is probed, i.e for platform to calculate timings, that has to be passed through platform data.

Eventually we should be able to move the gpmc registration to the driver probe, especially with device tree. There's no need to set up gpmc before the driver probe runs for the device using gpmc. Just how the gpmc init gets called from the driver probe is still a bit open though.. It may require some bus level hooks, or wrapper drivers for the generic device drivers like smsc911x.

Quoted 3 lines

I understand the necessity for clk rate information in driver, but seems unless we have a generic way to scale timings for all the kinds of peripheral, having it may not be of much help.

We should not need to pass clock handles around. It's better to export some helper functions in the gpmc code for the calculation.

Regards,

Tony

#28 lore raw

PATCH v5 2/14 ARM: OMAP2+: gpmc: Adapt to HWMOD

Hi Tony,

Quoted 9 lines · Tony Lindgren
Quoted 2 lines · Mohammed, Afzal

Here clock is required even before driver is probed, i.e for platform to calculate timings, that has to be passed through platform data.

Eventually we should be able to move the gpmc registration to the driver probe, especially with device tree. There's no need to set up gpmc before the driver probe runs for the device using gpmc. Just how the gpmc init gets called from the driver probe is still a bit open though..

Sorry, I did not get you, can you please clarify

By gpmc registration, if you meant registering platform device for gpmc peripherals, for a board that uses the new gpmc driver interface*, it will be done in probe only.

All the responsibilities of old gpmc init is now taken care by probe; probe in addition does setting up gpmc for each peripheral, registering platform device (if the board makes use of new driver interface). If a board uses new gpmc driver interface, gpmc for that device is not setup before gpmc probe.

Quoted 2 lines

It may require some bus level hooks, or wrapper drivers for the generic device drivers like smsc911x.

This too, not sure whether I follow you

Quoted 7 lines
Quoted 4 lines

I understand the necessity for clk rate information in driver, but seems unless we have a generic way to scale timings for all the kinds of peripheral, having it may not be of much help.

We should not need to pass clock handles around. It's better to export some helper functions in the gpmc code for the calculation.

Currently we have helper function in gpmc.c for the same, were you referring those ?

Regards
Afzal

*: [1] converts omap3evm & beagle board to use new interface, in addition
to [1], similar to it, one more series would be posted to convert remaining boards also to use the new gpmc driver interface. As these cannot be tested by me and as it depends on final shape of this basic driver conversion series (or the present series), they have not yet been converted

[1] http://www.mail-archive.com/linux-omap at vger.kernel.org/msg69917.html

maintainer
#29 lore raw

PATCH v5 2/14 ARM: OMAP2+: gpmc: Adapt to HWMOD

Quoted 8 lines · Mohammed, Afzal

Hi Tony,

Quoted 5 lines · Tony Lindgren
Quoted 2 lines · Mohammed, Afzal

Here clock is required even before driver is probed, i.e for platform to calculate timings, that has to be passed through platform data.

Quoted 6 lines
Quoted 4 lines

Eventually we should be able to move the gpmc registration to the driver probe, especially with device tree. There's no need to set up gpmc before the driver probe runs for the device using gpmc. Just how the gpmc init gets called from the driver probe is still a bit open though..

Sorry, I did not get you, can you please clarify

OK, I'll try :)

Quoted 3 lines

By gpmc registration, if you meant registering platform device for gpmc peripherals, for a board that uses the new gpmc driver interface*, it will be done in probe only.

I was thinking when the gpmc needs to be initialized, and there should not be any need to do it earlier than at the gpmc using driver probe time. With device tree that is, as there's no need to stuff the gpmc timings into a board-*.c file.

Quoted 5 lines

All the responsibilities of old gpmc init is now taken care by probe; probe in addition does setting up gpmc for each peripheral, registering platform device (if the board makes use of new driver interface). If a board uses new gpmc driver interface, gpmc for that device is not setup before gpmc probe.

OK

Quoted 4 lines
Quoted 2 lines

It may require some bus level hooks, or wrapper drivers for the generic device drivers like smsc911x.

This too, not sure whether I follow you

Well smsc911x has device tree binding, and is a generic driver. How do we trigger the gpmc initialization from a generic driver probe?

Quoted 9 lines
Quoted 6 lines
Quoted 3 lines

I understand the necessity for clk rate information in driver, but seems unless we have a generic way to scale timings for all the kinds of peripheral, having it may not be of much help.

We should not need to pass clock handles around. It's better to export some helper functions in the gpmc code for the calculation.

Currently we have helper function in gpmc.c for the same, were you referring those ?

Yes something that let's the driver call gpmc code to do the calculation. The other option would be to just add gpmc clock as a clock fwk node, and then the driver could clk_get() it as ick.

Regards,

Tony

Quoted 8 lines

*: [1] converts omap3evm & beagle board to use new interface, in addition
to [1], similar to it, one more series would be posted to convert remaining boards also to use the new gpmc driver interface. As these cannot be tested by me and as it depends on final shape of this basic driver conversion series (or the present series), they have not yet been converted

[1] http://www.mail-archive.com/linux-omap at vger.kernel.org/msg69917.html

#30 lore raw

PATCH v5 2/14 ARM: OMAP2+: gpmc: Adapt to HWMOD

Hi Tony,

Quoted 11 lines · Tony Lindgren
Quoted 5 lines · Mohammed, Afzal

On Fri, Jun 15, 2012 at 18:15:20, Tony Lindgren wrote:

By gpmc registration, if you meant registering platform device for gpmc peripherals, for a board that uses the new gpmc driver interface*, it will be done in probe only.

I was thinking when the gpmc needs to be initialized, and there should not be any need to do it earlier than at the gpmc using driver probe time. With device tree that is, as there's no need to stuff the gpmc timings into a board-*.c file.

I believe by "gpmc needs to be initialized", you meant calculating gpmc timings, determining configuration, the things that are done in functions like gpmc_smsc911x_update etc. as in [1] and not initializing gpmc at hardware level. With the above assumption, I feel we need to have a way first to generalize gpmc timing calculation for different peripherals as suggested by Jon as well as have logic to handle timings that depends on cycles too.

Quoted 7 lines
Quoted 4 lines
Quoted 2 lines

It may require some bus level hooks, or wrapper drivers for the generic device drivers like smsc911x.

This too, not sure whether I follow you

Well smsc911x has device tree binding, and is a generic driver. How do we trigger the gpmc initialization from a generic driver probe?

Not sure whether device tree have capability to represent something like child devices, if non bus devices can have child devices, then we can have peripherals connected to gpmc as childs, but may be this will remain only as a dream; I need to get into DT to find things out

Quoted 9 lines
Quoted 5 lines
Quoted 2 lines

We should not need to pass clock handles around. It's better to export some helper functions in the gpmc code for the calculation.

Currently we have helper function in gpmc.c for the same, were you referring those ?

Yes something that let's the driver call gpmc code to do the calculation. The other option would be to just add gpmc clock as a clock fwk node, and then the driver could clk_get() it as ick.

For gpmc driver to calculate timings rather than platform code doing it, we first need to have a generalized way to calculate gpmc timings for all peripherals as well as have a logic to calculate timings based on time & cycles, correct ? (to make sure we are talking the same thing)

Regards
Afzal

[1] http://www.mail-archive.com/linux-omap at vger.kernel.org/msg69926.html

maintainer
#31 lore raw

PATCH v5 2/14 ARM: OMAP2+: gpmc: Adapt to HWMOD

Quoted 15 lines · Mohammed, Afzal

Hi Tony,

Quoted 11 lines · Tony Lindgren
Quoted 5 lines · Mohammed, Afzal

On Fri, Jun 15, 2012 at 18:15:20, Tony Lindgren wrote:

By gpmc registration, if you meant registering platform device for gpmc peripherals, for a board that uses the new gpmc driver interface*, it will be done in probe only.

I was thinking when the gpmc needs to be initialized, and there should not be any need to do it earlier than at the gpmc using driver probe time. With device tree that is, as there's no need to stuff the gpmc timings into a board-*.c file.

Quoted 7 lines

I believe by "gpmc needs to be initialized", you meant calculating gpmc timings, determining configuration, the things that are done in functions like gpmc_smsc911x_update etc. as in [1] and not initializing gpmc at hardware level. With the above assumption, I feel we need to have a way first to generalize gpmc timing calculation for different peripherals as suggested by Jon as well as have logic to handle timings that depends on cycles too.

Yup. We'll be only getting the timings from device tree. Ideally the gpmc code would just do all the calculations, but it sounds like device timings may require some calculation on the gpmc consumer driver side too.

Quoted 5 lines
Quoted 5 lines
Quoted 4 lines
Quoted 2 lines

It may require some bus level hooks, or wrapper drivers for the generic device drivers like smsc911x.

This too, not sure whether I follow you

Quoted 7 lines
Quoted 2 lines

Well smsc911x has device tree binding, and is a generic driver. How do we trigger the gpmc initialization from a generic driver probe?

Not sure whether device tree have capability to represent something like child devices, if non bus devices can have child devices, then we can have peripherals connected to gpmc as childs, but may be this will remain only as a dream; I need to get into DT to find things out

Yes the that's there set up automatically. But for the timings we could have something like this to associate the following invented gpmc timings with smsc911x:

/* in omap[234].dtsi */

gpmc: gpmc at 480121234 {
	compatible = "ti,gpmc";
	reg = <0x480121234 0x1234>;
	#address-cells = <0>;
	#size-cells = <0>;
};

...

/* in some board *.dts file */

&gpmc {
	gpmc_cs1: gpmc_cs1_timings {
		...
	};
};
net at 48001234 {
	compatible = "smsc,lan91c111";
	reg = <0x48001234 0x10000>;
	interrupts = <12>;
	gpmc_timings = <&gpmc_cs1>;
};
Quoted 6 lines
Quoted 6 lines
Quoted 5 lines
Quoted 2 lines

We should not need to pass clock handles around. It's better to export some helper functions in the gpmc code for the calculation.

Currently we have helper function in gpmc.c for the same, were you referring those ?

Quoted 8 lines
Quoted 3 lines

Yes something that let's the driver call gpmc code to do the calculation. The other option would be to just add gpmc clock as a clock fwk node, and then the driver could clk_get() it as ick.

For gpmc driver to calculate timings rather than platform code doing it, we first need to have a generalized way to calculate gpmc timings for all peripherals as well as have a logic to calculate timings based on time & cycles, correct ? (to make sure we are talking the same thing)

Yes that might be tricky..

Tony

Quoted 2 lines

[1] http://www.mail-archive.com/linux-omap at vger.kernel.org/msg69926.html

#32 lore raw

PATCH v5 2/14 ARM: OMAP2+: gpmc: Adapt to HWMOD

Quoted 61 lines · Tony Lindgren
Quoted 15 lines · Mohammed, Afzal

Hi Tony,

Quoted 11 lines · Tony Lindgren
Quoted 5 lines · Mohammed, Afzal

On Fri, Jun 15, 2012 at 18:15:20, Tony Lindgren wrote:

By gpmc registration, if you meant registering platform device for gpmc peripherals, for a board that uses the new gpmc driver interface*, it will be done in probe only.

I was thinking when the gpmc needs to be initialized, and there should not be any need to do it earlier than at the gpmc using driver probe time. With device tree that is, as there's no need to stuff the gpmc timings into a board-*.c file.

Quoted 7 lines

I believe by "gpmc needs to be initialized", you meant calculating gpmc timings, determining configuration, the things that are done in functions like gpmc_smsc911x_update etc. as in [1] and not initializing gpmc at hardware level. With the above assumption, I feel we need to have a way first to generalize gpmc timing calculation for different peripherals as suggested by Jon as well as have logic to handle timings that depends on cycles too.

Yup. We'll be only getting the timings from device tree. Ideally the gpmc code would just do all the calculations, but it sounds like device timings may require some calculation on the gpmc consumer driver side too.

Quoted 5 lines
Quoted 5 lines
Quoted 4 lines
Quoted 2 lines

It may require some bus level hooks, or wrapper drivers for the generic device drivers like smsc911x.

This too, not sure whether I follow you

Quoted 7 lines
Quoted 2 lines

Well smsc911x has device tree binding, and is a generic driver. How do we trigger the gpmc initialization from a generic driver probe?

Not sure whether device tree have capability to represent something like child devices, if non bus devices can have child devices, then we can have peripherals connected to gpmc as childs, but may be this will remain only as a dream; I need to get into DT to find things out

Yes the that's there set up automatically. But for the timings we could have something like this to associate the following invented gpmc timings with smsc911x:

/* in omap[234].dtsi */

gpmc: gpmc@480121234 {
	compatible = "ti,gpmc";
	reg = <0x480121234 0x1234>;
	#address-cells = <0>;
	#size-cells = <0>;
};

...

/* in some board *.dts file */

&gpmc {
	gpmc_cs1: gpmc_cs1_timings {
		...
	};
};
Quoted 6 lines
net@48001234 {
	compatible = "smsc,lan91c111";
	reg = <0x48001234 0x10000>;
	interrupts = <12>;
	gpmc_timings = <&gpmc_cs1>;
};

The above make sense to me, but I am struggling to see how we can make the above work with the proposed driver Afzal has created. The problem is how devices are getting registered with Afzal's driver in comparison to how device tree will register them.

In Afzal's driver the gpmc_probe() is going to request and configure the chip-selects and if successful, then call platform_device_register() to register the child devices. So all child devices get registered during the gpmc_probe() itself.

With device tree, my understanding is that, it will first register the gpmc device and then the children afterwards. In other words, the gpmc_probe will be called first and the children registered/probed after.

Therefore, I am wondering if Afzal's driver needs to register the gpmc devices outside of the gpmc_probe() and add the devices as children. Or maybe we tackle that when we migrate it to DT.

Quoted 10 lines
Quoted 10 lines
Quoted 9 lines
Quoted 5 lines
Quoted 2 lines

We should not need to pass clock handles around. It's better to export some helper functions in the gpmc code for the calculation.

Currently we have helper function in gpmc.c for the same, were you referring those ?

Yes something that let's the driver call gpmc code to do the calculation. The other option would be to just add gpmc clock as a clock fwk node, and then the driver could clk_get() it as ick.

Quoted 6 lines
Quoted 4 lines

For gpmc driver to calculate timings rather than platform code doing it, we first need to have a generalized way to calculate gpmc timings for all peripherals as well as have a logic to calculate timings based on time & cycles, correct ? (to make sure we are talking the same thing)

Yes that might be tricky..

I am wondering more and more if we need to do a clean-up of the timing calculations prior to driver migration ...

Cheers
Jon

#33 lore raw

PATCH v5 2/14 ARM: OMAP2+: gpmc: Adapt to HWMOD

Hi Tony, Jon,

Quoted 6 lines · Jon
Quoted 1 line · Tony Lindgren

* Mohammed, Afzal <[email protected]> [120620 07:57]:

Therefore, I am wondering if Afzal's driver needs to register the gpmc devices outside of the gpmc_probe() and add the devices as children. Or maybe we tackle that when we migrate it to DT.

V6 of this series has been posted assuming that we tackle above issue during DT migration

Quoted 9 lines
Quoted 6 lines
Quoted 4 lines

For gpmc driver to calculate timings rather than platform code doing it, we first need to have a generalized way to calculate gpmc timings for all peripherals as well as have a logic to calculate timings based on time & cycles, correct ? (to make sure we are talking the same thing)

Yes that might be tricky..

I am wondering more and more if we need to do a clean-up of the timing calculations prior to driver migration ...

I propose to have clean-up of timing calculations after driver migration, v6 of this series has been posted, except the controversial issue of clock, I believe other issues has been addressed.

Please let me know your comments.

Regards
Afzal

#34 lore raw

PATCH v5 3/14 ARM: OMAP2+: gpmc: driver migration helper

In reply to Afzal Mohammed

A 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 as well as without the help of driver. 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

---
 arch/arm/mach-omap2/gpmc.c |   19 ++++++++++++++++++-
 1 file changed, 18 insertions(+), 1 deletion(-)
arch/arm/mach-omap2/gpmc.c +18 -1
index b471c2f..6dbddb9 100644
@@ -902,7 +902,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";
 
@@ -912,6 +912,12 @@ __init int omap_gpmc_init(struct gpmc_pdata *pdata)
 		return -ENODEV;
 	}
 
+	if (pdev != NULL) {
+		clk_put(gpmc_l3_clk);
+		omap_device_delete(pdev->archdata.od);
+		platform_device_unregister(pdev);
+	}
+
 	pdev = omap_device_build(name, -1, oh, pdata,
 					sizeof(*pdata), NULL, 0, 0);
 	if (IS_ERR(pdev)) {
@@ -929,6 +935,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
#35 lore raw

PATCH v5 3/14 ARM: OMAP2+: gpmc: driver migration helper

Hi Afzal,

Quoted 27 lines · Afzal Mohammed

A 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 as well as without the help of driver. 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

---
 arch/arm/mach-omap2/gpmc.c |   19 ++++++++++++++++++-
 1 file changed, 18 insertions(+), 1 deletion(-)
arch/arm/mach-omap2/gpmc.c +1 -1
index b471c2f..6dbddb9 100644
@@ -902,7 +902,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";
 

arch/arm/mach-omap2/gpmc.c __init int omap_gpmc_init(struct gpmc_pdata *pdata) · line 920

Quoted 9 lines
arch/arm/mach-omap2/gpmc.c +6 -0
@@ -912,6 +912,12 @@ __init int omap_gpmc_init(struct gpmc_pdata *pdata)
 		return -ENODEV;
 	}
 
+	if (pdev != NULL) {
+		clk_put(gpmc_l3_clk);
+		omap_device_delete(pdev->archdata.od);
+		platform_device_unregister(pdev);
+	}
+

I am not sure if I am missing something, but it appears that pdev will always be NULL here as it is a local uninitialised variable.

Quoted 8 lines
arch/arm/mach-omap2/gpmc.c +1 -0
 	pdev = omap_device_build(name, -1, oh, pdata,
 					sizeof(*pdata), NULL, 0, 0);
 	if (IS_ERR(pdev)) {
@@ -929,6 +935,17 @@ __init int omap_gpmc_init(struct gpmc_pdata *pdata)
 	return 0;
 }
 
+static int __init gpmc_pre_init(void)

arch/arm/mach-omap2/gpmc.c __init int omap_gpmc_init(struct gpmc_pdata *pdata) · line 948

Quoted 10 lines
arch/arm/mach-omap2/gpmc.c +10 -0
+{
+	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);
+

Not sure I see the point in the above function. Why not declare the gpmc_device_data struct as static in the file and access it directly instead of passing it in omap_gpmc_init(). The postcore_init can then call omap_gpmc_init() directly.

Shouldn't the post_initcall be added in patch #4, when the driver is created?

Cheers
Jon

#36 lore raw

PATCH v5 3/14 ARM: OMAP2+: gpmc: driver migration helper

Hi Jon,

This change is required only till driver migration of all platforms are done, after it, this hackish patch has to be reverted. This has been done so that existing interface will work for each patch of this series as well as till all boards are migrated.

Quoted 12 lines · Jon
Quoted 12 lines

__init int omap_gpmc_init(struct gpmc_pdata *pdata) {

arch/arm/mach-omap2/gpmc.c +1 -1
 	struct omap_hwmod *oh;
-	struct platform_device *pdev;
+	static struct platform_device *pdev;
 	char *name = "omap-gpmc";
 	char *oh_name = "gpmc";
@@ -912,6 +912,12 @@ __init int omap_gpmc_init(struct gpmc_pdata *pdata)
 		return -ENODEV;
 	}
 
Quoted 9 lines

arch/arm/mach-omap2/gpmc.c __init int omap_gpmc_init(struct gpmc_pdata *pdata) · line 920

Quoted 6 lines
arch/arm/mach-omap2/gpmc.c +6 -0
+	if (pdev != NULL) {
+		clk_put(gpmc_l3_clk);
+		omap_device_delete(pdev->archdata.od);
+		platform_device_unregister(pdev);
+	}
+

I am not sure if I am missing something, but it appears that pdev will always be NULL here as it is a local uninitialised variable.

omap_gpmc_init will be called by board files once again, at that time, existing omap device will be destroyed (resulting in driver remove being executed, and inverse of omap_device_build is not available, hence doing circus as above). Again omap device will be created, this time with details about gpmc peripherals (first time, there were no peripheral details provided, and this was done for old interface to work with same driver), once this happens new interface will starting it's job.

This kind of change was required as old interface works at arch_init

Quoted 16 lines

+static int __init gpmc_pre_init(void)

arch/arm/mach-omap2/gpmc.c __init int omap_gpmc_init(struct gpmc_pdata *pdata) · line 948

Quoted 10 lines
arch/arm/mach-omap2/gpmc.c +10 -0
+{
+	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);
+

Not sure I see the point in the above function. Why not declare the gpmc_device_data struct as static in the file and access it directly instead of passing it in omap_gpmc_init(). The postcore_init can then call omap_gpmc_init() directly.

Can be done, but it is not necessary to make it available outside the function

Quoted 3 lines

Shouldn't the post_initcall be added in patch #4, when the driver is created?

As this has to be reverted once all boards has been migrated, this was made separate.

Regards
Afzal

#37 lore raw

PATCH v5 3/14 ARM: OMAP2+: gpmc: driver migration helper

Quoted 39 lines · Afzal

Hi Jon,

This change is required only till driver migration of all platforms are done, after it, this hackish patch has to be reverted. This has been done so that existing interface will work for each patch of this series as well as till all boards are migrated.

Quoted 12 lines · Jon
Quoted 12 lines

__init int omap_gpmc_init(struct gpmc_pdata *pdata) {

arch/arm/mach-omap2/gpmc.c +1 -1
 	struct omap_hwmod *oh;
-	struct platform_device *pdev;
+	static struct platform_device *pdev;
 	char *name = "omap-gpmc";
 	char *oh_name = "gpmc";
@@ -912,6 +912,12 @@ __init int omap_gpmc_init(struct gpmc_pdata *pdata)
 		return -ENODEV;
 	}
 
Quoted 9 lines

arch/arm/mach-omap2/gpmc.c __init int omap_gpmc_init(struct gpmc_pdata *pdata) · line 920

Quoted 6 lines
arch/arm/mach-omap2/gpmc.c +6 -0
+	if (pdev != NULL) {
+		clk_put(gpmc_l3_clk);
+		omap_device_delete(pdev->archdata.od);
+		platform_device_unregister(pdev);
+	}
+

I am not sure if I am missing something, but it appears that pdev will always be NULL here as it is a local uninitialised variable.

omap_gpmc_init will be called by board files once again, at that time, existing omap device will be destroyed (resulting in driver remove being executed, and inverse of omap_device_build is not available, hence doing circus as above). Again omap device will be created, this time with details about gpmc peripherals (first time, there were no peripheral details provided, and this was done for old interface to work with same driver), once this happens new interface will starting it's job.

Quoted 1 line

This kind of change was required as old interface works at arch_init

Ok, make sense. Then please label with a FIXME.

Quoted 17 lines
Quoted 16 lines

+static int __init gpmc_pre_init(void)

arch/arm/mach-omap2/gpmc.c __init int omap_gpmc_init(struct gpmc_pdata *pdata) · line 948

Quoted 10 lines
arch/arm/mach-omap2/gpmc.c +10 -0
+{
+	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);
+

Not sure I see the point in the above function. Why not declare the gpmc_device_data struct as static in the file and access it directly instead of passing it in omap_gpmc_init(). The postcore_init can then call omap_gpmc_init() directly.

Quoted 2 lines

Can be done, but it is not necessary to make it available outside the function

Ok, I see that this is necessary until all boards are migrated. However, please label with a FIXME to make it clear that this is to be removed.

Jon

#38 lore raw

PATCH v5 3/14 ARM: OMAP2+: gpmc: driver migration helper

Hi Jon,

Quoted 2 lines · Jon

Ok, I see that this is necessary until all boards are migrated. However, please label with a FIXME to make it clear that this is to be removed.

Ok, I will update this.

Regards
Afzal

maintainer
#39 lore raw

PATCH v5 3/14 ARM: OMAP2+: gpmc: driver migration helper

In reply to Jon Hunter
Quoted 33 lines · Jon Hunter

Hi Afzal,

Quoted 30 lines · Afzal Mohammed

A 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 as well as without the help of driver. 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

---
 arch/arm/mach-omap2/gpmc.c |   19 ++++++++++++++++++-
 1 file changed, 18 insertions(+), 1 deletion(-)
arch/arm/mach-omap2/gpmc.c +1 -1
index b471c2f..6dbddb9 100644
@@ -902,7 +902,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";
 
@@ -912,6 +912,12 @@ __init int omap_gpmc_init(struct gpmc_pdata *pdata)
 		return -ENODEV;
 	}
 
Quoted 9 lines

arch/arm/mach-omap2/gpmc.c __init int omap_gpmc_init(struct gpmc_pdata *pdata) · line 920

Quoted 6 lines
arch/arm/mach-omap2/gpmc.c +6 -0
+	if (pdev != NULL) {
+		clk_put(gpmc_l3_clk);
+		omap_device_delete(pdev->archdata.od);
+		platform_device_unregister(pdev);
+	}
+

I am not sure if I am missing something, but it appears that pdev will always be NULL here as it is a local uninitialised variable.

This also creates a new warning:

arch/arm/mach-omap2/gpmc.c: In function ?gpmc_create_device?:
arch/arm/mach-omap2/gpmc.c:1434: warning: ?pdev? may be used uninitialized in this function

Quoted 7 lines

Not sure I see the point in the above function. Why not declare the gpmc_device_data struct as static in the file and access it directly instead of passing it in omap_gpmc_init(). The postcore_init can then call omap_gpmc_init() directly.

Shouldn't the post_initcall be added in patch #4, when the driver is created?

There should no longer be any need to initialize GPMC early. It should behave like any other device driver, and also work as a loadable module.

Regards,

Tony

#40 lore raw

PATCH v5 3/14 ARM: OMAP2+: gpmc: driver migration helper

Hi Tony,

Quoted 7 lines · Tony Lindgren
Quoted 2 lines

I am not sure if I am missing something, but it appears that pdev will always be NULL here as it is a local uninitialised variable.

This also creates a new warning:

arch/arm/mach-omap2/gpmc.c: In function ?gpmc_create_device?:
arch/arm/mach-omap2/gpmc.c:1434: warning: ?pdev? may be used uninitialized in this function

I use an internal toolchain, that does not give warning, seems I need to check with codesourcery.

Quoted 11 lines
Quoted 8 lines

Not sure I see the point in the above function. Why not declare the gpmc_device_data struct as static in the file and access it directly instead of passing it in omap_gpmc_init(). The postcore_init can then call omap_gpmc_init() directly.

Shouldn't the post_initcall be added in patch #4, when the driver is created?

There should no longer be any need to initialize GPMC early. It should behave like any other device driver, and also work as a loadable module.

Once driver migration is complete for all the boards, we can remove the initcall and make it loadable.

But till that time as we need to support both old & new interface, I feel this is necessary as old interface would happen in arch initcall, and old gpmc_init is now gpmc_probe, and hence it needs to get executed before arch initcall, necessitating this.

Regards
Afzal

#41 lore raw

PATCH v5 3/14 ARM: OMAP2+: gpmc: driver migration helper

Hi Tony,

Quoted 5 lines · Afzal
Quoted 2 lines · Tony Lindgren

There should no longer be any need to initialize GPMC early. It should behave like any other device driver, and also work as a loadable module.

Quoted 7 lines

Once driver migration is complete for all the boards, we can remove the initcall and make it loadable.

But till that time as we need to support both old & new interface, I feel this is necessary as old interface would happen in arch initcall, and old gpmc_init is now gpmc_probe, and hence it needs to get executed before arch initcall, necessitating this.

Seems you missed this in the flood of mails, please let me know your comments on this. This is a commit that has to be reverted once driver migration is complete for all boards.

Regards
Afzal

maintainer
#42 lore raw

PATCH v5 3/14 ARM: OMAP2+: gpmc: driver migration helper

Quoted 11 lines · Mohammed, Afzal

Hi Tony,

Quoted 8 lines · Afzal
Quoted 2 lines · Tony Lindgren

There should no longer be any need to initialize GPMC early. It should behave like any other device driver, and also work as a loadable module.

Once driver migration is complete for all the boards, we can remove the initcall and make it loadable.

Quoted 8 lines
Quoted 4 lines

But till that time as we need to support both old & new interface, I feel this is necessary as old interface would happen in arch initcall, and old gpmc_init is now gpmc_probe, and hence it needs to get executed before arch initcall, necessitating this.

Seems you missed this in the flood of mails, please let me know your comments on this. This is a commit that has to be reverted once driver migration is complete for all boards.

Yes sounds good to me.

Tony

#43 lore raw

PATCH v5 4/14 ARM: OMAP2+: gpmc: minimal driver support

In reply to Afzal Mohammed

Create a minimal driver out of gpmc code.
Responsibilities handled by earlier gpmc
initialization is now achieved in probe.

---
 arch/arm/mach-omap2/gpmc.c |  170 ++++++++++++++++++++++++++++++++------------
 1 file changed, 123 insertions(+), 47 deletions(-)
arch/arm/mach-omap2/gpmc.c +123 -47
index 6dbddb9..a91f40f 100644
@@ -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
@@ -115,6 +118,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_polarity;
+	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;
@@ -124,6 +142,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_revision;
+static int gpmc_irq;
+static resource_size_t phys_base, mem_size;
 
 static void __iomem *gpmc_base;
 
@@ -433,6 +455,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];
@@ -769,7 +804,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;
@@ -813,7 +848,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;
@@ -840,64 +905,75 @@ 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;
 
-	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);
+	gpmc_revision = (l >> 4) & 0xf;
+	dev_info(gpmc_dev, "GPMC revision %d.%d\n", gpmc_revision, l & 0x0f);
+
 	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
#44 lore raw

PATCH v5 4/14 ARM: OMAP2+: gpmc: minimal driver support

Quoted 193 lines · Afzal Mohammed

Create a minimal driver out of gpmc code.
Responsibilities handled by earlier gpmc
initialization is now achieved in probe.

---
 arch/arm/mach-omap2/gpmc.c |  170 ++++++++++++++++++++++++++++++++------------
 1 file changed, 123 insertions(+), 47 deletions(-)
arch/arm/mach-omap2/gpmc.c +85 -36
index 6dbddb9..a91f40f 100644
@@ -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
@@ -115,6 +118,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_polarity;
+	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;
@@ -124,6 +142,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_revision;
+static int gpmc_irq;
+static resource_size_t phys_base, mem_size;
 
 static void __iomem *gpmc_base;
 
@@ -433,6 +455,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];
@@ -769,7 +804,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;
@@ -813,7 +848,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;
@@ -840,64 +905,75 @@ 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;
 
-	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;
 

arch/arm/mach-omap2/gpmc.c static void __init gpmc_mem_init(void) · line 935

Quoted 10 lines
arch/arm/mach-omap2/gpmc.c +1 -6
 	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);
+	gpmc_revision = (l >> 4) & 0xf;

Why are you only storing the major part of the rev? Why not keep both parts?

arch/arm/mach-omap2/gpmc.c static void __init gpmc_mem_init(void) · line 936

Quoted 1 line
arch/arm/mach-omap2/gpmc.c +1 -0
+	dev_info(gpmc_dev, "GPMC revision %d.%d\n", gpmc_revision, l & 0x0f);

Nit-pick, please add a mask and shift definition for the revision register major and minor fields and use these above.

Thanks
Jon

#45 lore raw

PATCH v5 4/14 ARM: OMAP2+: gpmc: minimal driver support

Hi Jon,

Quoted 3 lines · Jon

arch/arm/mach-omap2/gpmc.c static void __init gpmc_mem_init(void) · line 935

Quoted 1 line
arch/arm/mach-omap2/gpmc.c +1 -0
+	gpmc_revision = (l >> 4) & 0xf;

Why are you only storing the major part of the rev? Why not keep both parts?

Does having minor revision add any value ?, at least as of now, I do not see any.

Quoted 5 lines

arch/arm/mach-omap2/gpmc.c static void __init gpmc_mem_init(void) · line 936

Quoted 2 lines
arch/arm/mach-omap2/gpmc.c +1 -0
+	dev_info(gpmc_dev, "GPMC revision %d.%d\n", gpmc_revision, l & 0x0f);

Nit-pick, please add a mask and shift definition for the revision register major and minor fields and use these above.

Yes, right

Regards
Afzal

#46 lore raw

PATCH v5 4/14 ARM: OMAP2+: gpmc: minimal driver support

Quoted 10 lines · Afzal

Hi Jon,

Quoted 3 lines · Jon

arch/arm/mach-omap2/gpmc.c static void __init gpmc_mem_init(void) · line 935

Quoted 1 line
arch/arm/mach-omap2/gpmc.c +1 -0
+	gpmc_revision = (l >> 4) & 0xf;

Why are you only storing the major part of the rev? Why not keep both parts?

Does having minor revision add any value ?, at least as of now, I do not see any.

May be not but it does not hurt either.

Quoted 6 lines
Quoted 4 lines

arch/arm/mach-omap2/gpmc.c static void __init gpmc_mem_init(void) · line 936

Quoted 1 line
arch/arm/mach-omap2/gpmc.c +1 -0
+	dev_info(gpmc_dev, "GPMC revision %d.%d\n", gpmc_revision, l & 0x0f);

Nit-pick, please add a mask and shift definition for the revision register major and minor fields and use these above.

Yes, right

Thanks
Jon

maintainer
#47 lore raw

PATCH v5 4/14 ARM: OMAP2+: gpmc: minimal driver support

Quoted 6 lines · Jon Hunter
Quoted 4 lines · Afzal

Hi Jon,

On Tue, Jun 12, 2012 at 02:13:22, Hunter, Jon wrote:

Quoted 8 lines
Quoted 6 lines
Quoted 3 lines

arch/arm/mach-omap2/gpmc.c static void __init gpmc_mem_init(void) · line 935

Quoted 1 line
arch/arm/mach-omap2/gpmc.c +1 -0
+	gpmc_revision = (l >> 4) & 0xf;

Why are you only storing the major part of the rev? Why not keep both parts?

Does having minor revision add any value ?, at least as of now, I do not see any.

May be not but it does not hurt either.

Yeah might as well show the full revision.

Tony

#48 lore raw

PATCH v5 4/14 ARM: OMAP2+: gpmc: minimal driver support

Hi Tony,

Quoted 10 lines · Tony Lindgren
Quoted 7 lines · Jon Hunter
Quoted 2 lines · Afzal

Does having minor revision add any value ?, at least as of now, I do not see any.

May be not but it does not hurt either.

Yeah might as well show the full revision.

We are already showing full version, but only storing major, it seems you want to store minor version along with major version in the same variable, right ?

Regards
Afzal

maintainer
#49 lore raw

PATCH v5 4/14 ARM: OMAP2+: gpmc: minimal driver support

Quoted 10 lines · Mohammed, Afzal

Hi Tony,

Quoted 7 lines · Tony Lindgren
Quoted 6 lines · Jon Hunter
Quoted 2 lines · Afzal

Does having minor revision add any value ?, at least as of now, I do not see any.

Quoted 7 lines
Quoted 3 lines
Quoted 1 line

May be not but it does not hurt either.

Yeah might as well show the full revision.

We are already showing full version, but only storing major, it seems you want to store minor version along with major version in the same variable, right ?

Ah OK, yeah might as well just store the minor version.

Tony

maintainer
#50 lore raw

PATCH v5 4/14 ARM: OMAP2+: gpmc: minimal driver support

Quoted 13 lines · Tony Lindgren
Quoted 12 lines · Mohammed, Afzal

Hi Tony,

Quoted 9 lines · Tony Lindgren
Quoted 7 lines · Jon Hunter
Quoted 2 lines · Afzal

Does having minor revision add any value ?, at least as of now, I do not see any.

May be not but it does not hurt either.

Quoted 7 lines
Quoted 5 lines
Quoted 1 line

Yeah might as well show the full revision.

We are already showing full version, but only storing major, it seems you want to store minor version along with major version in the same variable, right ?

Ah OK, yeah might as well just store the minor version.

Actually why do you even need to store the revision? You can just read it from the hardware as needed.

Tony

#51 lore raw

PATCH v5 4/14 ARM: OMAP2+: gpmc: minimal driver support

Hi Tony,

Quoted 2 lines · Tony Lindgren

Actually why do you even need to store the revision? You can just read it from the hardware as needed.

Earlier for wr_access & wr_data_mux_bus, we were checking, cpu_is_34xx, but I feel it should instead be based on IP revision as it is the IP revision that decides the availability of those register bits.

Regards
Afzal

#52 lore raw

PATCH v5 4/14 ARM: OMAP2+: gpmc: minimal driver support

In reply to Tony Lindgren

Hi Tony,

Quoted 11 lines · Afzal

Hi Tony,

Quoted 2 lines · Tony Lindgren

Actually why do you even need to store the revision? You can just read it from the hardware as needed.

Earlier for wr_access & wr_data_mux_bus, we were checking, cpu_is_34xx, but I feel it should instead be based on IP revision as it is the IP revision that decides the availability of those register bits.

Or you meant, even there read revision register ?

Regards
Afzal

maintainer
#53 lore raw

PATCH v5 4/14 ARM: OMAP2+: gpmc: minimal driver support

Quoted 10 lines · Mohammed, Afzal

Hi Tony,

Quoted 7 lines · Afzal

Hi Tony,

Quoted 2 lines · Tony Lindgren

Actually why do you even need to store the revision? You can just read it from the hardware as needed.

Quoted 6 lines
Quoted 4 lines

Earlier for wr_access & wr_data_mux_bus, we were checking, cpu_is_34xx, but I feel it should instead be based on IP revision as it is the IP revision that decides the availability of those register bits.

Or you meant, even there read revision register ?

Yeah that should be fine as this really should only happen during init and whatever revision specific features can be initialized for GPMC.

Tony

#54 lore raw

PATCH v5 4/14 ARM: OMAP2+: gpmc: minimal driver support

Hi Tony,

Quoted 8 lines · Tony Lindgren
Quoted 1 line · Mohammed, Afzal

Or you meant, even there read revision register ?

Yeah that should be fine as this really should only happen during init and whatever revision specific features can be initialized for GPMC.

Sorry, I got confused, we need revision to be available while setting time also, so you meant to store it as a variable or read revision at probe as well as during setting time ?

Regards
Afzal

maintainer
#55 lore raw

PATCH v5 4/14 ARM: OMAP2+: gpmc: minimal driver support

Quoted 8 lines · Mohammed, Afzal

Hi Tony,

Quoted 5 lines · Tony Lindgren
Quoted 1 line · Mohammed, Afzal

Or you meant, even there read revision register ?

Quoted 7 lines
Quoted 3 lines

Yeah that should be fine as this really should only happen during init and whatever revision specific features can be initialized for GPMC.

Sorry, I got confused, we need revision to be available while setting time also, so you meant to store it as a variable or read revision at probe as well as during setting time ?

You can set up GPMC specific flags during init, something like OMAP_GPMC_HAS_XYZ, or you can set up function pointers if only some features are available on some revisions.

Regards,

Tony

#56 lore raw

PATCH v5 4/14 ARM: OMAP2+: gpmc: minimal driver support

Hi Tony,

Quoted 9 lines · Tony Lindgren
Quoted 3 lines · Mohammed, Afzal

Sorry, I got confused, we need revision to be available while setting time also, so you meant to store it as a variable or read revision at probe as well as during setting time ?

You can set up GPMC specific flags during init, something like OMAP_GPMC_HAS_XYZ, or you can set up function pointers if only some features are available on some revisions.

Ok, got it

Regards
Afzal

#57 lore raw

PATCH v5 4/14 ARM: OMAP2+: gpmc: minimal driver support

In reply to Tony Lindgren
Quoted 13 lines · Tony Lindgren
Quoted 12 lines · Mohammed, Afzal

Hi Tony,

Quoted 9 lines · Tony Lindgren
Quoted 7 lines · Jon Hunter
Quoted 2 lines · Afzal

Does having minor revision add any value ?, at least as of now, I do not see any.

May be not but it does not hurt either.

Quoted 7 lines
Quoted 5 lines
Quoted 1 line

Yeah might as well show the full revision.

We are already showing full version, but only storing major, it seems you want to store minor version along with major version in the same variable, right ?

Ah OK, yeah might as well just store the minor version.

Actually, you really need the major version. However, IMO if you are going to store it, then store the whole thing, you have allocated 32-bits and the major and minor combined are 8-bits.

Jon

#58 lore raw

PATCH v5 4/14 ARM: OMAP2+: gpmc: minimal driver support

In reply to Afzal Mohammed
Quoted 184 lines · Afzal Mohammed

Create a minimal driver out of gpmc code.
Responsibilities handled by earlier gpmc
initialization is now achieved in probe.

---
 arch/arm/mach-omap2/gpmc.c |  170 ++++++++++++++++++++++++++++++++------------
 1 file changed, 123 insertions(+), 47 deletions(-)
arch/arm/mach-omap2/gpmc.c +77 -36
index 6dbddb9..a91f40f 100644
@@ -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
@@ -115,6 +118,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_polarity;
+	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;
@@ -124,6 +142,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_revision;
+static int gpmc_irq;
+static resource_size_t phys_base, mem_size;
 
 static void __iomem *gpmc_base;
 
@@ -433,6 +455,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];
@@ -769,7 +804,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;
@@ -813,7 +848,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;
@@ -840,64 +905,75 @@ 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;
 
-	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;

arch/arm/mach-omap2/gpmc.c static void __init gpmc_mem_init(void) · line 932

Quoted 10 lines
arch/arm/mach-omap2/gpmc.c +8 -0
+
+	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);

We should be able to get rid of the clk_enable() here and use ...

	pm_runtime_enable(&pdev->dev);
	pm_runtime_get(&pdev->dev);

The clk_disable should also be replaced with a pm_runtime_put(). You will also need to include pm_runtime.h.

Jon

#59 lore raw

PATCH v5 4/14 ARM: OMAP2+: gpmc: minimal driver support

Hi Jon,

Quoted 11 lines · Jon
Quoted 1 line · Afzal Mohammed

clk_enable(gpmc_l3_clk);

We should be able to get rid of the clk_enable() here and use ...

	pm_runtime_enable(&pdev->dev);
	pm_runtime_get(&pdev->dev);

The clk_disable should also be replaced with a pm_runtime_put(). You will also need to include pm_runtime.h.

Once driver conversion is in, it would be made rpm capable.

Regards
Afzal

#60 lore raw

PATCH v5 5/14 ARM: OMAP2+: gpmc: resource creation helpers

In reply to Afzal Mohammed

Helpers for propulating 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
index a91f40f..652b245 100644
@@ -905,6 +905,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 1;
+}
+
+static inline unsigned gpmc_bit_to_irq(unsigned bitmask)
+{
+	return bitmask;
+}
+
+static __devinit int gpmc_setup_cs_irq(struct gpmc_cs_data *cs,
+						struct resource *res)
+{
+	int i, n;
+
+	for (i = 0, n = 0; i < GPMC_NR_IRQ; i++)
+		if (gpmc_bit_to_irq(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
#61 lore raw

PATCH v5 5/14 ARM: OMAP2+: gpmc: resource creation helpers

Quoted 27 lines · Afzal Mohammed

Helpers for propulating 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 +11 -0
index a91f40f..652b245 100644
@@ -905,6 +905,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;
+	}

arch/arm/mach-omap2/gpmc.c static void __devinit gpmc_mem_init(void) · line 928

Quoted 10 lines
arch/arm/mach-omap2/gpmc.c +10 -0
+
+	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 1;
+}

Nit-pick, CodingStyle chapter 16 states that 0 should be used for success when returning from a function.

arch/arm/mach-omap2/gpmc.c static void __devinit gpmc_mem_init(void) · line 933

Quoted 5 lines
arch/arm/mach-omap2/gpmc.c +5 -0
+
+static inline unsigned gpmc_bit_to_irq(unsigned bitmask)
+{
+	return bitmask;
+}

I have to ask what is the value in this helper function? ;-)

Jon

#62 lore raw

PATCH v5 5/14 ARM: OMAP2+: gpmc: resource creation helpers

Hi Jon,

Quoted 8 lines · Jon

arch/arm/mach-omap2/gpmc.c static void __devinit gpmc_mem_init(void) · line 928

Quoted 5 lines
arch/arm/mach-omap2/gpmc.c +4 -0
+static __devinit int gpmc_setup_cs_mem(struct gpmc_cs_data *cs,
+						struct resource *res)
 
+	return 1;
+}

Nit-pick, CodingStyle chapter 16 states that 0 should be used for success when returning from a function.

I doubt whether this falls to the category you are referring. This function returns number of resources setup, please see similar for irq too, there it can be 2 also, success or failure is not sufficient for the other, and with this we have both functions returning values similar way.

Quoted 6 lines

arch/arm/mach-omap2/gpmc.c static void __devinit gpmc_mem_init(void) · line 933

Quoted 4 lines
arch/arm/mach-omap2/gpmc.c +4 -0
+static inline unsigned gpmc_bit_to_irq(unsigned bitmask)
+{
+	return bitmask;
+}

I have to ask what is the value in this helper function? ;-)

It is to stress the fact that flag user is passing need not be same as irq bit (here both have same value), this kind of an arrangement would be required later if waitpin interrupt also has to be handled, do you still prefer removing it now ?

Regards
Afzal

#63 lore raw

PATCH v5 5/14 ARM: OMAP2+: gpmc: resource creation helpers

Quoted 13 lines · Afzal

Hi Jon,

Quoted 8 lines · Jon

arch/arm/mach-omap2/gpmc.c static void __devinit gpmc_mem_init(void) · line 928

Quoted 5 lines
arch/arm/mach-omap2/gpmc.c +4 -0
+static __devinit int gpmc_setup_cs_mem(struct gpmc_cs_data *cs,
+						struct resource *res)
 
+	return 1;
+}

Nit-pick, CodingStyle chapter 16 states that 0 should be used for success when returning from a function.

Quoted 5 lines

I doubt whether this falls to the category you are referring. This function returns number of resources setup, please see similar for irq too, there it can be 2 also, success or failure is not sufficient for the other, and with this we have both functions returning values similar way.

Well looking at the function it seems that you either return an error code or 1. So if you are never going to return anything other than 1 on success it may as well be 0.

Quoted 11 lines
Quoted 6 lines

arch/arm/mach-omap2/gpmc.c static void __devinit gpmc_mem_init(void) · line 933

Quoted 4 lines
arch/arm/mach-omap2/gpmc.c +4 -0
+static inline unsigned gpmc_bit_to_irq(unsigned bitmask)
+{
+	return bitmask;
+}

I have to ask what is the value in this helper function? ;-)

It is to stress the fact that flag user is passing need not be same as irq bit (here both have same value), this kind of an arrangement would be required later if waitpin interrupt also has to be handled, do you still prefer removing it now ?

I don't have strong feelings, but if you are not planning to add support for the wait pin irq anytime soon I would leave it out.

Jon

#64 lore raw

PATCH v5 5/14 ARM: OMAP2+: gpmc: resource creation helpers

Hi Jon,

Quoted 3 lines · Jon

Well looking at the function it seems that you either return an error code or 1. So if you are never going to return anything other than 1 on success it may as well be 0.

Irq & memory resource creation functions returns number of resources, if memory function only is modified, that will cause loss of uniformity w.r.t irq function, even though both does similar things.

Regards
Afzal

#65 lore raw

PATCH v5 5/14 ARM: OMAP2+: gpmc: resource creation helpers

Hi Afzal,

Quoted 11 lines · Afzal

Hi Jon,

Quoted 3 lines · Jon

Well looking at the function it seems that you either return an error code or 1. So if you are never going to return anything other than 1 on success it may as well be 0.

Irq & memory resource creation functions returns number of resources, if memory function only is modified, that will cause loss of uniformity w.r.t irq function, even though both does similar things.

Ok, I see what you mean but from a readability standpoint it looked odd. The other function is returning "n" where as this is just returning 1 on success but it is not clear that this actually means 1 resource.

Jon

#66 lore raw

PATCH v5 5/14 ARM: OMAP2+: gpmc: resource creation helpers

Hi Jon,

Quoted 9 lines · Jon
Quoted 3 lines · Afzal

Irq & memory resource creation functions returns number of resources, if memory function only is modified, that will cause loss of uniformity w.r.t irq function, even though both does similar things.

Ok, I see what you mean but from a readability standpoint it looked odd. The other function is returning "n" where as this is just returning 1 on success but it is not clear that this actually means 1 resource.

Ok, I will add comments to make it clear

Regards
Afzal

#67 lore raw

PATCH v5 6/14 ARM: OMAP2+: gpmc: CS configuration helper

In reply to Afzal Mohammed

Helper for configuring given CS based on flags.

---
 arch/arm/mach-omap2/gpmc.c             |   33 ++++++++++++++++++++++++++++++++
 arch/arm/plat-omap/include/plat/gpmc.h |    5 +++++
 2 files changed, 38 insertions(+)
arch/arm/mach-omap2/gpmc.c +33 -0
index 652b245..4e19010 100644
@@ -927,6 +927,39 @@ static __devinit int gpmc_setup_cs_mem(struct gpmc_cs_data *cs,
 	return 1;
 }
 
+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));
+
+	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 inline unsigned gpmc_bit_to_irq(unsigned bitmask)
 {
 	return bitmask;
arch/arm/plat-omap/include/plat/gpmc.h +5 -0
index 698fa33..ff3f32c 100644
@@ -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
#68 lore raw

PATCH v5 6/14 ARM: OMAP2+: gpmc: CS configuration helper

Quoted 13 lines · Afzal Mohammed

Helper for configuring given CS based on flags.

---
 arch/arm/mach-omap2/gpmc.c             |   33 ++++++++++++++++++++++++++++++++
 arch/arm/plat-omap/include/plat/gpmc.h |    5 +++++
 2 files changed, 38 insertions(+)
arch/arm/mach-omap2/gpmc.c +0 -0
index 652b245..4e19010 100644

arch/arm/mach-omap2/gpmc.c static __devinit int gpmc_setup_cs_mem(struct gpmc_cs_data *cs, · line 932

Quoted 6 lines
arch/arm/mach-omap2/gpmc.c +3 -0
@@ -927,6 +927,39 @@ static __devinit int gpmc_setup_cs_mem(struct gpmc_cs_data *cs,
 	return 1;
 }
 
+static void gpmc_setup_cs_config(unsigned cs, unsigned conf)
+{
+	u32 l = gpmc_cs_read_reg(cs, GPMC_CS_CONFIG1);

Why is it necessary to read the register first? I thought you wanted to get away from relying on bootloader settings?

arch/arm/mach-omap2/gpmc.c static __devinit int gpmc_setup_cs_mem(struct gpmc_cs_data *cs, · line 945

Quoted 12 lines
arch/arm/mach-omap2/gpmc.c +12 -0
+	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));
+
+	l |= conf & GPMC_CONFIG1_DEVICETYPE_NAND;
+	l |= conf & GPMC_CONFIG1_DEVICESIZE_16;

I can see that it works to use the above definitions as masks because of the possible values that can be programmed into these fields. However, from a read-ability standpoint this is unclear and requires people to review the documentation to understand what you are doing here. Furthermore, if any new device-types or sizes were added in the future this could lead to bugs. Hence, it would be better to define a mask for these fields.

Now you may say what happens if someone pass a bad device-type or device-size that would equate to a reserved value being programmed. Well if someone passes a bad value we don't know what they intended to program and that raises the question should we be checking the conf value of bad device types, size, and page lengths here?

Jon

#69 lore raw

PATCH v5 6/14 ARM: OMAP2+: gpmc: CS configuration helper

Hi Jon,

Quoted 6 lines · Jon

arch/arm/mach-omap2/gpmc.c static __devinit int gpmc_setup_cs_mem(struct gpmc_cs_data *cs, · line 932

Quoted 3 lines
arch/arm/mach-omap2/gpmc.c +3 -0
+static void gpmc_setup_cs_config(unsigned cs, unsigned conf)
+{
+	u32 l = gpmc_cs_read_reg(cs, GPMC_CS_CONFIG1);

Why is it necessary to read the register first? I thought you wanted to get away from relying on bootloader settings?

This is not trying to depend on bootloader, it is to alter bits that are only meant for configuration. There are other bits in the same register configured as part of time setting.

Quoted 14 lines

arch/arm/mach-omap2/gpmc.c static __devinit int gpmc_setup_cs_mem(struct gpmc_cs_data *cs, · line 945

Quoted 13 lines
arch/arm/mach-omap2/gpmc.c +12 -0
+	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));
+
+	l |= conf & GPMC_CONFIG1_DEVICETYPE_NAND;
+	l |= conf & GPMC_CONFIG1_DEVICESIZE_16;
Quoted 7 lines

I can see that it works to use the above definitions as masks because of the possible values that can be programmed into these fields. However, from a read-ability standpoint this is unclear and requires people to review the documentation to understand what you are doing here. Furthermore, if any new device-types or sizes were added in the future this could lead to bugs. Hence, it would be better to define a mask for these fields.

I had thought about it initially, but then it was felt it will lead to a less simple code, that path was not taken, let me revisit this.

Regards
Afzal

#70 lore raw

PATCH v5 6/14 ARM: OMAP2+: gpmc: CS configuration helper

Hi Jon,

Quoted 11 lines · Afzal
Quoted 10 lines

arch/arm/mach-omap2/gpmc.c static __devinit int gpmc_setup_cs_mem(struct gpmc_cs_data *cs, · line 945

Quoted 2 lines
arch/arm/mach-omap2/gpmc.c +2 -0
+	l |= conf & GPMC_CONFIG1_DEVICETYPE_NAND;
+	l |= conf & GPMC_CONFIG1_DEVICESIZE_16;

I can see that it works to use the above definitions as masks because of the possible values that can be programmed into these fields. However, from a read-ability standpoint this is unclear and requires people to review the documentation to understand what you are doing here. Furthermore, if any new device-types or sizes were added in the future this could lead to bugs. Hence, it would be better to define a mask for these fields.

Quoted 3 lines

I had thought about it initially, but then it was felt it will lead to a less simple code, that path was not taken, let me revisit this.

Thinking again over it, I am feeling above is sufficient, reason same as said earlier, to keep code simple & currently this is sufficient to handle GPMC bit patterns for IPs presently available. What you are suggesting is to take care of the imaginary case when new GPMC IP happens with new device type or size, I think that should be handled when such a scenario happens. Probably, it is better here to add a comment to make intention clear.

Regards
Afzal

#71 lore raw

PATCH v5 6/14 ARM: OMAP2+: gpmc: CS configuration helper

Quoted 19 lines · Afzal

Hi Jon,

Quoted 11 lines · Afzal
Quoted 10 lines

arch/arm/mach-omap2/gpmc.c static __devinit int gpmc_setup_cs_mem(struct gpmc_cs_data *cs, · line 945

Quoted 2 lines
arch/arm/mach-omap2/gpmc.c +2 -0
+	l |= conf & GPMC_CONFIG1_DEVICETYPE_NAND;
+	l |= conf & GPMC_CONFIG1_DEVICESIZE_16;

I can see that it works to use the above definitions as masks because of the possible values that can be programmed into these fields. However, from a read-ability standpoint this is unclear and requires people to review the documentation to understand what you are doing here. Furthermore, if any new device-types or sizes were added in the future this could lead to bugs. Hence, it would be better to define a mask for these fields.

Quoted 3 lines

I had thought about it initially, but then it was felt it will lead to a less simple code, that path was not taken, let me revisit this.

Quoted 7 lines

Thinking again over it, I am feeling above is sufficient, reason same as said earlier, to keep code simple & currently this is sufficient to handle GPMC bit patterns for IPs presently available. What you are suggesting is to take care of the imaginary case when new GPMC IP happens with new device type or size, I think that should be handled when such a scenario happens. Probably, it is better here to add a comment to make intention clear.

That is one possibility but I think that more important reasons are ...

1. Readability, at first it appears that we are always configuring the CS for NAND. However, this is not the case. Maybe a comment here can help as you mentioned.

2. A bad setting in the configuration passed. Hopefully, people will stick to the flags but we know that we expect the device type to be a 0 or 2 and so should we check?

Cheers
Jon

#72 lore raw

PATCH v5 6/14 ARM: OMAP2+: gpmc: CS configuration helper

Hi Jon,

Quoted 10 lines · Jon
Quoted 7 lines · Afzal

Thinking again over it, I am feeling above is sufficient, reason same as said earlier, to keep code simple & currently this is sufficient to handle GPMC bit patterns for IPs presently available. What you are suggesting is to take care of the imaginary case when new GPMC IP happens with new device type or size, I think that should be handled when such a scenario happens. Probably, it is better here to add a comment to make intention clear.

Quoted 5 lines

That is one possibility but I think that more important reasons are ...

1. Readability, at first it appears that we are always configuring the CS for NAND. However, this is not the case. Maybe a comment here can help as you mentioned.

As far as the users of GPMC is considered there is no confusion. Eg. For device type, they have been provided with two macros,

GPMC_CONFIG1_DEVICETYPE_NOR, GPMC_CONFIG1_DEVICETYPE_NAND

So for NOR, user can feel satisfied by giving NOR flag, little does he know that driver doesn't do anything with the flag, but he still gets what he want NOR flag is defined as zero. Yes even if he doesn't specify any type, device type will be set as NOR, but then that is the default - the only other option

Quoted 4 lines

2. A bad setting in the configuration passed. Hopefully, people will stick to the flags but we know that we expect the device type to be a 0 or 2 and so should we check?

Value of device type is something driver has to worry about, not its users, they have been provided with two flags, one for NAND & other for NOR.

Regards
Afzal

#73 lore raw

PATCH v5 6/14 ARM: OMAP2+: gpmc: CS configuration helper

Hi Afzal,

Quoted 22 lines · Afzal

Hi Jon,

Quoted 10 lines · Jon
Quoted 7 lines · Afzal

Thinking again over it, I am feeling above is sufficient, reason same as said earlier, to keep code simple & currently this is sufficient to handle GPMC bit patterns for IPs presently available. What you are suggesting is to take care of the imaginary case when new GPMC IP happens with new device type or size, I think that should be handled when such a scenario happens. Probably, it is better here to add a comment to make intention clear.

Quoted 5 lines

That is one possibility but I think that more important reasons are ...

1. Readability, at first it appears that we are always configuring the CS for NAND. However, this is not the case. Maybe a comment here can help as you mentioned.

As far as the users of GPMC is considered there is no confusion. Eg. For device type, they have been provided with two macros,

Quoted 6 lines

GPMC_CONFIG1_DEVICETYPE_NOR, GPMC_CONFIG1_DEVICETYPE_NAND

So for NOR, user can feel satisfied by giving NOR flag, little does he know that driver doesn't do anything with the flag, but he still gets what he want NOR flag is defined as zero. Yes even if he doesn't specify any type, device type will be set as NOR, but then that is the default - the only other option

Sure, but reviewing the function it still looks odd from a readability standpoint. At least it made me think "what is going on here ...". So a comment is definitely needed.

Quoted 7 lines
Quoted 4 lines

2. A bad setting in the configuration passed. Hopefully, people will stick to the flags but we know that we expect the device type to be a 0 or 2 and so should we check?

Value of device type is something driver has to worry about, not its users, they have been provided with two flags, one for NAND & other for NOR.

Yes, but the driver does not seem to worry about it. In other words, there is no error checking. Ok so not a big deal a comment should suffice.

Jon

#74 lore raw

PATCH v5 6/14 ARM: OMAP2+: gpmc: CS configuration helper

Hi Jon,

Quoted 9 lines · Jon

Sure, but reviewing the function it still looks odd from a readability standpoint. At least it made me think "what is going on here ...". So a comment is definitely needed.

Quoted 5 lines
Quoted 4 lines

2. A bad setting in the configuration passed. Hopefully, people will stick to the flags but we know that we expect the device type to be a 0 or 2 and so should we check?

Quoted 5 lines
Quoted 2 lines

Value of device type is something driver has to worry about, not its users, they have been provided with two flags, one for NAND & other for NOR.

Yes, but the driver does not seem to worry about it. In other words, there is no error checking. Ok so not a big deal a comment should suffice.

Ok, comments will be added to make intentions clear

Regards
Afzal

#75 lore raw

PATCH v5 6/14 ARM: OMAP2+: gpmc: CS configuration helper

In reply to Mohammed, Afzal
Quoted 11 lines · Afzal

Hi Jon,

Quoted 6 lines · Jon

arch/arm/mach-omap2/gpmc.c static __devinit int gpmc_setup_cs_mem(struct gpmc_cs_data *cs, · line 932

Quoted 3 lines
arch/arm/mach-omap2/gpmc.c +3 -0
+static void gpmc_setup_cs_config(unsigned cs, unsigned conf)
+{
+	u32 l = gpmc_cs_read_reg(cs, GPMC_CS_CONFIG1);

Why is it necessary to read the register first? I thought you wanted to get away from relying on bootloader settings?

Quoted 3 lines

This is not trying to depend on bootloader, it is to alter bits that are only meant for configuration. There are other bits in the same register configured as part of time setting.

Well it is unclear what the code flow is for using this helper as this change simply adds the helper. If someone other function is writing to the CONFIG1 register before this function, then you may wish to highlight the programming sequence in the changelog or at least describe why this function performs a read-modify-write and not just a write.

Jon

#76 lore raw

PATCH v5 6/14 ARM: OMAP2+: gpmc: CS configuration helper

Hi Jon,

Quoted 5 lines · Jon

Well it is unclear what the code flow is for using this helper as this change simply adds the helper. If someone other function is writing to the CONFIG1 register before this function, then you may wish to highlight the programming sequence in the changelog or at least describe why this function performs a read-modify-write and not just a write.

Logic followed here: CS configuration helper needs to worry only about bits that are relevant for CS configuration and don't alter any other bits/registers.

Same is applicable for time setting helper.

Regards
Afzal

#77 lore raw

PATCH v5 7/14 ARM: OMAP2+: gpmc: time setting (register#) helper

In reply to Afzal Mohammed

Helper for setting GPMC timing by taking input as register values.

---
 arch/arm/mach-omap2/gpmc.c |   43 +++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 43 insertions(+)
arch/arm/mach-omap2/gpmc.c +43 -0
index 4e19010..e60076e3 100644
@@ -960,6 +960,49 @@ 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_revision >= 4) {
+		gpmc_set_one_timing(cs, GPMC_CS_CONFIG6, 16, 19,
+						t->wr_data_mux_bus);
+		gpmc_set_one_timing(cs, GPMC_CS_CONFIG6, 24, 28, t->wr_access);
+	}
+}
+
 static inline unsigned gpmc_bit_to_irq(unsigned bitmask)
 {
 	return bitmask;
Signature · 1 line
1.7.10.2
#78 lore raw

PATCH v5 7/14 ARM: OMAP2+: gpmc: time setting (register#) helper

Quoted 1 line · Afzal Mohammed

Helper for setting GPMC timing by taking input as register values.

When are the timings calculated? Why not just use the existing gpmc_cs_set_timings()?

I guess I am not convinced that we need to have multiple formats to pass timings such as clock, period, etc.

I think that it is sufficient to pass the timings as time (period) as is currently done and have the driver convert to clocks based upon the gpmc fclk rate.

Jon

Quoted 59 lines
---
 arch/arm/mach-omap2/gpmc.c |   43 +++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 43 insertions(+)
arch/arm/mach-omap2/gpmc.c +43 -0
index 4e19010..e60076e3 100644
@@ -960,6 +960,49 @@ 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_revision >= 4) {
+		gpmc_set_one_timing(cs, GPMC_CS_CONFIG6, 16, 19,
+						t->wr_data_mux_bus);
+		gpmc_set_one_timing(cs, GPMC_CS_CONFIG6, 24, 28, t->wr_access);
+	}
+}
+
 static inline unsigned gpmc_bit_to_irq(unsigned bitmask)
 {
 	return bitmask;
#79 lore raw

PATCH v5 7/14 ARM: OMAP2+: gpmc: time setting (register#) helper

Hi Jon,

Quoted 9 lines · Jon

When are the timings calculated? Why not just use the existing gpmc_cs_set_timings()?

I guess I am not convinced that we need to have multiple formats to pass timings such as clock, period, etc.

I think that it is sufficient to pass the timings as time (period) as is currently done and have the driver convert to clocks based upon the gpmc fclk rate.

Please see http://www.mail-archive.com/linux-omap at vger.kernel.org/msg69041.html

Regards
Afzal

#80 lore raw

PATCH v5 8/14 ARM: OMAP2+: gpmc: bool type timing helper

In reply to Afzal Mohammed

Some of the timing configuration like extra delay
has bool type configurations. Provide a helper so
that these too can be configured in Kernel.

---
 arch/arm/mach-omap2/gpmc.c |   55 ++++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 55 insertions(+)
arch/arm/mach-omap2/gpmc.c +55 -0
index e60076e3..65052f8 100644
@@ -68,6 +68,13 @@
 #define GPMC_ECC_CTRL_ECCREG8		0x008
 #define GPMC_ECC_CTRL_ECCREG9		0x009
 
+#define	GPMC_CONFIG2_CSEXTRADELAY		BIT(7)
+#define	GPMC_CONFIG3_ADVEXTRADELAY		BIT(7)
+#define	GPMC_CONFIG4_OEEXTRADELAY		BIT(7)
+#define	GPMC_CONFIG4_WEEXTRADELAY		BIT(23)
+#define	GPMC_CONFIG6_CYCLE2CYCLEDIFFCSEN	BIT(6)
+#define	GPMC_CONFIG6_CYCLE2CYCLESAMECSEN	BIT(7)
+
 #define GPMC_CS0_OFFSET		0x60
 #define GPMC_CS_SIZE		0x30
 
@@ -960,6 +967,54 @@ static void gpmc_setup_cs_config(unsigned cs, unsigned conf)
 	gpmc_cs_write_reg(cs, GPMC_CS_CONFIG1, l);
 }
 
+static void gpmc_cs_misc_timings(int cs, const struct gpmc_misc_timings *p)
+{
+	u32 l;
+
+	l = gpmc_cs_read_reg(cs, GPMC_CS_CONFIG1);
+	if (p->time_para_granularity)
+		l |= GPMC_CONFIG1_TIME_PARA_GRAN;
+	else
+		l &= ~GPMC_CONFIG1_TIME_PARA_GRAN;
+	gpmc_cs_write_reg(cs, GPMC_CS_CONFIG1, l);
+
+	l = gpmc_cs_read_reg(cs, GPMC_CS_CONFIG2);
+	if (p->cs_extra_delay)
+		l |= GPMC_CONFIG2_CSEXTRADELAY;
+	else
+		l &= ~GPMC_CONFIG2_CSEXTRADELAY;
+	gpmc_cs_write_reg(cs, GPMC_CS_CONFIG2, l);
+
+	l = gpmc_cs_read_reg(cs, GPMC_CS_CONFIG3);
+	if (p->adv_extra_delay)
+		l |= GPMC_CONFIG3_ADVEXTRADELAY;
+	else
+		l &= ~GPMC_CONFIG3_ADVEXTRADELAY;
+	gpmc_cs_write_reg(cs, GPMC_CS_CONFIG3, l);
+
+	l = gpmc_cs_read_reg(cs, GPMC_CS_CONFIG4);
+	if (p->oe_extra_delay)
+		l |= GPMC_CONFIG4_OEEXTRADELAY;
+	else
+		l &= ~GPMC_CONFIG4_OEEXTRADELAY;
+	if (p->we_extra_delay)
+		l |= GPMC_CONFIG4_WEEXTRADELAY;
+	else
+		l &= ~GPMC_CONFIG4_WEEXTRADELAY;
+	gpmc_cs_write_reg(cs, GPMC_CS_CONFIG4, l);
+
+	l = gpmc_cs_read_reg(cs, GPMC_CS_CONFIG6);
+	if (p->cycle2cyclesamecsen)
+		l |= GPMC_CONFIG6_CYCLE2CYCLESAMECSEN;
+	else
+		l &= ~GPMC_CONFIG6_CYCLE2CYCLESAMECSEN;
+	if (p->cycle2cyclediffcsen)
+		l |= GPMC_CONFIG6_CYCLE2CYCLEDIFFCSEN;
+	else
+		l &= ~GPMC_CONFIG6_CYCLE2CYCLEDIFFCSEN;
+	gpmc_cs_write_reg(cs, GPMC_CS_CONFIG6, l);
+}
+
 static inline void gpmc_set_one_timing(int cs, int reg, int start,
 							int end, u32 val)
 {
Signature · 1 line
1.7.10.2
#81 lore raw

PATCH v5 8/14 ARM: OMAP2+: gpmc: bool type timing helper

Quoted 68 lines · Afzal Mohammed

Some of the timing configuration like extra delay
has bool type configurations. Provide a helper so
that these too can be configured in Kernel.

---
 arch/arm/mach-omap2/gpmc.c |   55 ++++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 55 insertions(+)
arch/arm/mach-omap2/gpmc.c +44 -0
index e60076e3..65052f8 100644
@@ -68,6 +68,13 @@
 #define GPMC_ECC_CTRL_ECCREG8		0x008
 #define GPMC_ECC_CTRL_ECCREG9		0x009
 
+#define	GPMC_CONFIG2_CSEXTRADELAY		BIT(7)
+#define	GPMC_CONFIG3_ADVEXTRADELAY		BIT(7)
+#define	GPMC_CONFIG4_OEEXTRADELAY		BIT(7)
+#define	GPMC_CONFIG4_WEEXTRADELAY		BIT(23)
+#define	GPMC_CONFIG6_CYCLE2CYCLEDIFFCSEN	BIT(6)
+#define	GPMC_CONFIG6_CYCLE2CYCLESAMECSEN	BIT(7)
+
 #define GPMC_CS0_OFFSET		0x60
 #define GPMC_CS_SIZE		0x30
 
@@ -960,6 +967,54 @@ static void gpmc_setup_cs_config(unsigned cs, unsigned conf)
 	gpmc_cs_write_reg(cs, GPMC_CS_CONFIG1, l);
 }
 
+static void gpmc_cs_misc_timings(int cs, const struct gpmc_misc_timings *p)
+{
+	u32 l;
+
+	l = gpmc_cs_read_reg(cs, GPMC_CS_CONFIG1);
+	if (p->time_para_granularity)
+		l |= GPMC_CONFIG1_TIME_PARA_GRAN;
+	else
+		l &= ~GPMC_CONFIG1_TIME_PARA_GRAN;
+	gpmc_cs_write_reg(cs, GPMC_CS_CONFIG1, l);
+
+	l = gpmc_cs_read_reg(cs, GPMC_CS_CONFIG2);
+	if (p->cs_extra_delay)
+		l |= GPMC_CONFIG2_CSEXTRADELAY;
+	else
+		l &= ~GPMC_CONFIG2_CSEXTRADELAY;
+	gpmc_cs_write_reg(cs, GPMC_CS_CONFIG2, l);
+
+	l = gpmc_cs_read_reg(cs, GPMC_CS_CONFIG3);
+	if (p->adv_extra_delay)
+		l |= GPMC_CONFIG3_ADVEXTRADELAY;
+	else
+		l &= ~GPMC_CONFIG3_ADVEXTRADELAY;
+	gpmc_cs_write_reg(cs, GPMC_CS_CONFIG3, l);
+
+	l = gpmc_cs_read_reg(cs, GPMC_CS_CONFIG4);
+	if (p->oe_extra_delay)
+		l |= GPMC_CONFIG4_OEEXTRADELAY;gpmc_cs_misc_timings
+	else
+		l &= ~GPMC_CONFIG4_OEEXTRADELAY;
+	if (p->we_extra_delay)
+		l |= GPMC_CONFIG4_WEEXTRADELAY;
+	else
+		l &= ~GPMC_CONFIG4_WEEXTRADELAY;
+	gpmc_cs_write_reg(cs, GPMC_CS_CONFIG4, l);
+
+	l = gpmc_cs_read_reg(cs, GPMC_CS_CONFIG6);

arch/arm/mach-omap2/gpmc.c static void gpmc_setup_cs_config(unsigned cs, unsigned conf) · line 1016

Quoted 10 lines
arch/arm/mach-omap2/gpmc.c +10 -0
+	if (p->cycle2cyclesamecsen)
+		l |= GPMC_CONFIG6_CYCLE2CYCLESAMECSEN;
+	else
+		l &= ~GPMC_CONFIG6_CYCLE2CYCLESAMECSEN;
+	if (p->cycle2cyclediffcsen)
+		l |= GPMC_CONFIG6_CYCLE2CYCLEDIFFCSEN;
+	else
+		l &= ~GPMC_CONFIG6_CYCLE2CYCLEDIFFCSEN;
+	gpmc_cs_write_reg(cs, GPMC_CS_CONFIG6, l);
+}

How about adding a sub-function like ...

static inline
void _gpmc_cs_misc_timings(int cs, int reg, int flag, int bit)

{
	if (flag)
		gpmc_set_one_timing(cs, reg, bit, bit, 1);
	else
		gpmc_set_one_timing(cs, reg, bit, bit, 0);
}

Or maybe make it into a generic set/clear bit function. Should reduce overall lines of code.

Cheers
Jon

#82 lore raw

PATCH v5 8/14 ARM: OMAP2+: gpmc: bool type timing helper

Hi Jon,

Quoted 4 lines · Jon
Quoted 4 lines
arch/arm/mach-omap2/gpmc.c +4 -0
+	if (p->cycle2cyclesamecsen)
+		l |= GPMC_CONFIG6_CYCLE2CYCLESAMECSEN;
+	else
+		l &= ~GPMC_CONFIG6_CYCLE2CYCLESAMECSEN;
Quoted 8 lines

arch/arm/mach-omap2/gpmc.c static void gpmc_setup_cs_config(unsigned cs, unsigned conf) · line 1016

Quoted 6 lines
arch/arm/mach-omap2/gpmc.c +6 -0
+	if (p->cycle2cyclediffcsen)
+		l |= GPMC_CONFIG6_CYCLE2CYCLEDIFFCSEN;
+	else
+		l &= ~GPMC_CONFIG6_CYCLE2CYCLEDIFFCSEN;
+	gpmc_cs_write_reg(cs, GPMC_CS_CONFIG6, l);
+}

How about adding a sub-function like ...

Agree, that is better.

Regards
Afzal

#83 lore raw

PATCH v5 9/14 ARM: OMAP2+: gpmc: holler if no configuration

In reply to Afzal Mohammed

Some 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.

---
 arch/arm/mach-omap2/gpmc.c |  104 ++++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 104 insertions(+)
arch/arm/mach-omap2/gpmc.c +104 -0
index 65052f8..5a6f708 100644
@@ -1058,6 +1058,110 @@ static void gpmc_cs_set_register_timings(int cs, const struct gpmc_timings *t)
 	}
 }
 
+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 inline unsigned gpmc_bit_to_irq(unsigned bitmask)
 {
 	return bitmask;
Signature · 1 line
1.7.10.2
#84 lore raw

PATCH v5 9/14 ARM: OMAP2+: gpmc: holler if no configuration

Quoted 4 lines · Afzal Mohammed

Some 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.

Nit, "holler" is slang. Just say WARN.

Jon

Quoted 120 lines
---
 arch/arm/mach-omap2/gpmc.c |  104 ++++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 104 insertions(+)
arch/arm/mach-omap2/gpmc.c +104 -0
index 65052f8..5a6f708 100644
@@ -1058,6 +1058,110 @@ static void gpmc_cs_set_register_timings(int cs, const struct gpmc_timings *t)
 	}
 }
 
+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 inline unsigned gpmc_bit_to_irq(unsigned bitmask)
 {
 	return bitmask;
#85 lore raw

PATCH v5 9/14 ARM: OMAP2+: gpmc: holler if no configuration

Hi Jon,

Quoted 1 line · Jon

Nit, "holler" is slang. Just say WARN.

It was a deliberate attempt to add human (or read humorous) touch

Regards
Afzal

#86 lore raw

PATCH v5 9/14 ARM: OMAP2+: gpmc: holler if no configuration

Quoted 7 lines · Afzal

Hi Jon,

Quoted 1 line · Jon

Nit, "holler" is slang. Just say WARN.

It was a deliberate attempt to add human (or read humorous) touch

I like that, but I wonder if it could confuse some :-)

Jon

#87 lore raw

PATCH v5 10/14 ARM: OMAP2+: gpmc: waitpin helper

In reply to Afzal Mohammed

Helper 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             |  122 ++++++++++++++++++++++++++++++++
 arch/arm/plat-omap/include/plat/gpmc.h |    9 +++
 2 files changed, 131 insertions(+)
arch/arm/mach-omap2/gpmc.c +122 -0
index 5a6f708..9073a8a 100644
@@ -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
 
@@ -93,6 +95,19 @@
  */
 #define	GPMC_NR_IRQ		2
 
+enum {
+	GPMC_WAITPIN_IDX0,
+	GPMC_WAITPIN_IDX1,
+	GPMC_WAITPIN_IDX2,
+	GPMC_WAITPIN_IDX3,
+	GPMC_NR_WAITPIN
+};
+
+enum {
+	LOW,
+	HIGH
+};
+
 struct gpmc_client_irq	{
 	unsigned		irq;
 	u32			bitmask;
@@ -140,6 +155,8 @@ struct gpmc_peripheral {
 	struct platform_device	*pdev;
 };
 
+static unsigned gpmc_waitpin_map;
+
 static struct gpmc_client_irq gpmc_client_irq[GPMC_NR_IRQ];
 static struct irq_chip gpmc_irq_chip;
 static unsigned gpmc_irq_start;
@@ -1162,6 +1179,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_polarity != polarity) {
+			dev_err(gpmc_dev, "error: conflict: waitpin %u with polarity %d on device %s.%d\n",
+				g_per->waitpin, g_per->waitpin_polarity,
+				g_per->name, g_per->id);
+			return -EBUSY;
+		}
+	} else {
+		g_per->have_waitpin = true;
+		g_per->waitpin = idx;
+		g_per->waitpin_polarity = 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 inline unsigned gpmc_bit_to_irq(unsigned bitmask)
 {
 	return bitmask;
@@ -1185,6 +1258,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_NR_WAITPIN))
+		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_polarity == 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;
arch/arm/plat-omap/include/plat/gpmc.h +9 -0
index ff3f32c..e1b130c 100644
@@ -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
#88 lore raw

PATCH v5 10/14 ARM: OMAP2+: gpmc: waitpin helper

Quoted 27 lines · Afzal Mohammed

Helper 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             |  122 ++++++++++++++++++++++++++++++++
 arch/arm/plat-omap/include/plat/gpmc.h |    9 +++
 2 files changed, 131 insertions(+)
arch/arm/mach-omap2/gpmc.c +2 -0
index 5a6f708..9073a8a 100644
@@ -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
 

arch/arm/mach-omap2/gpmc.c line 104

Quoted 10 lines
arch/arm/mach-omap2/gpmc.c +7 -0
@@ -93,6 +95,19 @@
  */
 #define	GPMC_NR_IRQ		2
 
+enum {
+	GPMC_WAITPIN_IDX0,
+	GPMC_WAITPIN_IDX1,
+	GPMC_WAITPIN_IDX2,
+	GPMC_WAITPIN_IDX3,
+	GPMC_NR_WAITPIN
+};

Max number of wait pins is 3 for omap4/5. I know that we discussed this in the past, but are you not supporting these devices are the moment? I know that you have not done the hwmod for these, but still I was hoping that you would take these into account.

arch/arm/mach-omap2/gpmc.c line 109

Quoted 5 lines
arch/arm/mach-omap2/gpmc.c +5 -0
+
+enum {
+	LOW,
+	HIGH
+};

To be honest, I don't see the point in either of the above enums when you have the definitions at the bottom. Seems that one set of definitions should be enough.

Quoted 16 lines
arch/arm/mach-omap2/gpmc.c +2 -0
 struct gpmc_client_irq	{
 	unsigned		irq;
 	u32			bitmask;
@@ -140,6 +155,8 @@ struct gpmc_peripheral {
 	struct platform_device	*pdev;
 };
 
+static unsigned gpmc_waitpin_map;
+
 static struct gpmc_client_irq gpmc_client_irq[GPMC_NR_IRQ];
 static struct irq_chip gpmc_irq_chip;
 static unsigned gpmc_irq_start;
@@ -1162,6 +1179,62 @@ static void gpmc_print_cs_timings(int cs)
 			gpmc_get_one_timing(cs, GPMC_CS_CONFIG6, 7, 7));
 }
 

arch/arm/mach-omap2/gpmc.c static void gpmc_print_cs_timings(int cs) · line 1191

Quoted 10 lines
arch/arm/mach-omap2/gpmc.c +10 -0
+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;

For example, here you could have ...

		idx = GPMC_WAITPIN_0 - 1;
Quoted 88 lines
arch/arm/mach-omap2/gpmc.c +81 -0
+		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_polarity != polarity) {
+			dev_err(gpmc_dev, "error: conflict: waitpin %u with polarity %d on device %s.%d\n",
+				g_per->waitpin, g_per->waitpin_polarity,
+				g_per->name, g_per->id);
+			return -EBUSY;
+		}
+	} else {
+		g_per->have_waitpin = true;
+		g_per->waitpin = idx;
+		g_per->waitpin_polarity = 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 inline unsigned gpmc_bit_to_irq(unsigned bitmask)
 {
 	return bitmask;
@@ -1185,6 +1258,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_NR_WAITPIN))
+		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);

arch/arm/mach-omap2/gpmc.c static __devinit int gpmc_setup_cs_irq(struct gpmc_cs_data *cs, · line 1305

Quoted 10 lines
arch/arm/mach-omap2/gpmc.c +10 -0
+		return ret;
+	}
+
+	l = gpmc_read_reg(GPMC_CONFIG);
+	shift = g_per->waitpin + GPMC_CONFIG_WAITPIN_POLARITY_SHIFT;
+	if (g_per->waitpin_polarity == HIGH)
+		l |= 1 << shift;
+	else
+		l &= ~(1 << shift);
+	gpmc_write_reg(GPMC_CONFIG, l);

Looks like another good place for a _gpmc_cs_set_bit() function.

Jon

Quoted 34 lines
arch/arm/mach-omap2/gpmc.c +4 -0
+
+	return 0;
+}
+
 static __devinit int gpmc_probe(struct platform_device *pdev)
 {
 	u32 l;
arch/arm/plat-omap/include/plat/gpmc.h +9 -0
index ff3f32c..e1b130c 100644
@@ -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
#89 lore raw

PATCH v5 10/14 ARM: OMAP2+: gpmc: waitpin helper

Hi Jon,

Quoted 4 lines · Jon
Quoted 4 lines
arch/arm/mach-omap2/gpmc.c +4 -0
+enum {
+	GPMC_WAITPIN_IDX0,
+	GPMC_WAITPIN_IDX1,
+	GPMC_WAITPIN_IDX2,
Quoted 8 lines

arch/arm/mach-omap2/gpmc.c line 104

Quoted 3 lines
arch/arm/mach-omap2/gpmc.c +3 -0
+	GPMC_WAITPIN_IDX3,
+	GPMC_NR_WAITPIN
+};

Max number of wait pins is 3 for omap4/5. I know that we discussed this in the past, but are you not supporting these devices are the moment? I know that you have not done the hwmod for these, but still I was hoping that you would take these into account.

I don't see reason for omap4/5 not to work with this

Quoted 10 lines

arch/arm/mach-omap2/gpmc.c line 109

Quoted 6 lines
arch/arm/mach-omap2/gpmc.c +5 -0
+
+enum {
+	LOW,
+	HIGH
+};

To be honest, I don't see the point in either of the above enums when you have the definitions at the bottom. Seems that one set of definitions should be enough.

Thanks for catching that, it will be removed

Quoted 6 lines

arch/arm/mach-omap2/gpmc.c static void gpmc_print_cs_timings(int cs) · line 1191

Quoted 2 lines
arch/arm/mach-omap2/gpmc.c +2 -0
+	case GPMC_WAITPIN_0:
+		idx =  GPMC_WAITPIN_IDX0;

For example, here you could have ...

		idx = GPMC_WAITPIN_0 - 1;

Sorry, not sure whether I follow you, can you please clarify

Regards
Afzal

#90 lore raw

PATCH v5 10/14 ARM: OMAP2+: gpmc: waitpin helper

Quoted 17 lines · Afzal

Hi Jon,

Quoted 4 lines · Jon
Quoted 4 lines
arch/arm/mach-omap2/gpmc.c +4 -0
+enum {
+	GPMC_WAITPIN_IDX0,
+	GPMC_WAITPIN_IDX1,
+	GPMC_WAITPIN_IDX2,
Quoted 8 lines

arch/arm/mach-omap2/gpmc.c line 104

Quoted 3 lines
arch/arm/mach-omap2/gpmc.c +3 -0
+	GPMC_WAITPIN_IDX3,
+	GPMC_NR_WAITPIN
+};

Max number of wait pins is 3 for omap4/5. I know that we discussed this in the past, but are you not supporting these devices are the moment? I know that you have not done the hwmod for these, but still I was hoping that you would take these into account.

Quoted 1 line

I don't see reason for omap4/5 not to work with this

Well, GPMC_NR_WAITPIN will be 4 and this is not accurate for OMAP4/5.

Quoted 13 lines
Quoted 10 lines

arch/arm/mach-omap2/gpmc.c line 109

Quoted 6 lines
arch/arm/mach-omap2/gpmc.c +5 -0
+
+enum {
+	LOW,
+	HIGH
+};

To be honest, I don't see the point in either of the above enums when you have the definitions at the bottom. Seems that one set of definitions should be enough.

Thanks for catching that, it will be removed

Quoted 8 lines
Quoted 6 lines

arch/arm/mach-omap2/gpmc.c static void gpmc_print_cs_timings(int cs) · line 1191

Quoted 2 lines
arch/arm/mach-omap2/gpmc.c +2 -0
+	case GPMC_WAITPIN_0:
+		idx =  GPMC_WAITPIN_IDX0;

For example, here you could have ...

		idx = GPMC_WAITPIN_0 - 1;

Sorry, not sure whether I follow you, can you please clarify

GPMC_WAITPIN_IDX0 = 0
GPMC_WAITPIN_0 = 1

So, GPMC_WAITPIN_IDX0 = GPMC_WAITPIN_0 - 1, assuming that you want idx = 0 and not 1. Or you could change you shift value too. I was just highlighting that they are not equal but one set of definitions could be used.

Jon

#91 lore raw

PATCH v5 10/14 ARM: OMAP2+: gpmc: waitpin helper

Hi Jon,

Quoted 7 lines · Jon

GPMC_WAITPIN_IDX0 = 0
GPMC_WAITPIN_0 = 1

So, GPMC_WAITPIN_IDX0 = GPMC_WAITPIN_0 - 1, assuming that you want idx = 0 and not 1. Or you could change you shift value too. I was just highlighting that they are not equal but one set of definitions could be used.

In that case we would be directly depending on user flag whose value may or may not change and I don't think it is good to directly depend on it for waitpin # calculation.

Regards
Afzal

#92 lore raw

PATCH v5 10/14 ARM: OMAP2+: gpmc: waitpin helper

Hi Afzal,

Quoted 7 lines · Afzal

Hi Jon,

Quoted 3 lines · Jon

GPMC_WAITPIN_IDX0 = 0
GPMC_WAITPIN_0 = 1

Quoted 8 lines
Quoted 4 lines

So, GPMC_WAITPIN_IDX0 = GPMC_WAITPIN_0 - 1, assuming that you want idx = 0 and not 1. Or you could change you shift value too. I was just highlighting that they are not equal but one set of definitions could be used.

In that case we would be directly depending on user flag whose value may or may not change and I don't think it is good to directly depend on it for waitpin # calculation.

You are already dependent on it. In other words, you are going to set the waitpin # according to these flags in the first place. I don't see the difference.

Bottom line is that we don't need to have multiple definitions for the same thing.

Jon

#93 lore raw

PATCH v5 10/14 ARM: OMAP2+: gpmc: waitpin helper

Hi Jon,

Quoted 7 lines · Jon
Quoted 3 lines · Afzal

In that case we would be directly depending on user flag whose value may or may not change and I don't think it is good to directly depend on it for waitpin # calculation.

You are already dependent on it. In other words, you are going to set

What I meant is we are not dependent on absolute value of flag to find waitpin, and I disagree in depending on its absolute value, which can change, while flag would be the same.

Regards
Afzal

#94 lore raw

PATCH v5 10/14 ARM: OMAP2+: gpmc: waitpin helper

Hi Afzal,

Quoted 9 lines · Afzal

Hi Jon,

Quoted 6 lines · Jon
Quoted 3 lines · Afzal

In that case we would be directly depending on user flag whose value may or may not change and I don't think it is good to directly depend on it for waitpin # calculation.

Quoted 5 lines
Quoted 1 line

You are already dependent on it. In other words, you are going to set

What I meant is we are not dependent on absolute value of flag to find waitpin, and I disagree in depending on its absolute value, which can change, while flag would be the same.

As long as we can avoid having multiple #defines for the same thing, I don't mind how it is done.

Jon

#95 lore raw

PATCH v5 10/14 ARM: OMAP2+: gpmc: waitpin helper

Hi Jon,

Quoted 8 lines · Jon
Quoted 3 lines · Afzal

What I meant is we are not dependent on absolute value of flag to find waitpin, and I disagree in depending on its absolute value, which can change, while flag would be the same.

As long as we can avoid having multiple #defines for the same thing, I don't mind how it is done.

I feel such a dependency is fragile

Regards
Afzal

#96 lore raw

PATCH v5 10/14 ARM: OMAP2+: gpmc: waitpin helper

In reply to Afzal Mohammed
Quoted 73 lines · Afzal Mohammed

Helper 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             |  122 ++++++++++++++++++++++++++++++++
 arch/arm/plat-omap/include/plat/gpmc.h |    9 +++
 2 files changed, 131 insertions(+)
arch/arm/mach-omap2/gpmc.c +31 -0
index 5a6f708..9073a8a 100644
@@ -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
 
@@ -93,6 +95,19 @@
  */
 #define	GPMC_NR_IRQ		2
 
+enum {
+	GPMC_WAITPIN_IDX0,
+	GPMC_WAITPIN_IDX1,
+	GPMC_WAITPIN_IDX2,
+	GPMC_WAITPIN_IDX3,
+	GPMC_NR_WAITPIN
+};
+
+enum {
+	LOW,
+	HIGH
+};
+
 struct gpmc_client_irq	{
 	unsigned		irq;
 	u32			bitmask;
@@ -140,6 +155,8 @@ struct gpmc_peripheral {
 	struct platform_device	*pdev;
 };
 
+static unsigned gpmc_waitpin_map;
+
 static struct gpmc_client_irq gpmc_client_irq[GPMC_NR_IRQ];
 static struct irq_chip gpmc_irq_chip;
 static unsigned gpmc_irq_start;
@@ -1162,6 +1179,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;

arch/arm/mach-omap2/gpmc.c static void gpmc_print_cs_timings(int cs) · line 1205

Quoted 10 lines
arch/arm/mach-omap2/gpmc.c +10 -0
+	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;

Do you need the break and return?

arch/arm/mach-omap2/gpmc.c static void gpmc_print_cs_timings(int cs) · line 1209

Quoted 4 lines
arch/arm/mach-omap2/gpmc.c +4 -0
+	default:
+		dev_err(gpmc_dev, "multiple waitpins selected on CS:%u\n", cs);
+		return -EINVAL;
+		break;

Same as above.

Quoted 48 lines
arch/arm/mach-omap2/gpmc.c +41 -0
+	}
+
+	polarity = !!(conf & GPMC_WAITPIN_ACTIVE_HIGH);
+
+	if (g_per->have_waitpin) {
+		if (g_per->waitpin != idx ||
+				g_per->waitpin_polarity != polarity) {
+			dev_err(gpmc_dev, "error: conflict: waitpin %u with polarity %d on device %s.%d\n",
+				g_per->waitpin, g_per->waitpin_polarity,
+				g_per->name, g_per->id);
+			return -EBUSY;
+		}
+	} else {
+		g_per->have_waitpin = true;
+		g_per->waitpin = idx;
+		g_per->waitpin_polarity = 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 inline unsigned gpmc_bit_to_irq(unsigned bitmask)
 {
 	return bitmask;
@@ -1185,6 +1258,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)
+{

arch/arm/mach-omap2/gpmc.c static __devinit int gpmc_setup_cs_irq(struct gpmc_cs_data *cs, · line 1283

Quoted 10 lines
arch/arm/mach-omap2/gpmc.c +10 -0
+	if (!(waitpin < GPMC_NR_WAITPIN))
+		return -ENODEV;
+
+	if (gpmc_waitpin_is_reserved(waitpin))
+		return -EBUSY;
+	else
+		gpmc_reserve_waitpin(waitpin);
+
+	return 0;
+}

I am wondering if we should combine all the gpmc_xxx_request pin functions into one. For example ...

static int gpmc_pin_request(int type, int pin)

{
	int pin_num, pin_mask;

	switch(type) {
	case GPMC_PIN_TYPE_CS:
		pin_num = GPMC_CS_NUM;
		pin_mask = gpmc_cs_map;
		break;
	case GPMC_PIN_TYPE_WAIT:
		pin_num = GPMC_NR_WAITPN;
		pin_mask = gpmc_waitpin_map;
		break;
	case GPMC_PIN_TYPE_WRITEPROTECT:
		pin_num = GPMC_NR_WP;
		pin_mask = gpmc_wp_map;
		break;
	default:
		return -ENODEV;
	}

	if (pin >= pin_num)
		return -ENODEV;

	if (gpmc_pin_is_reserved(pin_mask, pin))
		return -EBUSY;

	gpmc_reserve_pin(pin_mask, pin);

	return 0;
}

Jon

#97 lore raw

PATCH v5 10/14 ARM: OMAP2+: gpmc: waitpin helper

Hi Jon,

Quoted 9 lines · Jon

arch/arm/mach-omap2/gpmc.c static void gpmc_print_cs_timings(int cs) · line 1205

Quoted 7 lines
arch/arm/mach-omap2/gpmc.c +7 -0
+	case GPMC_WAITPIN_3:
+		idx =  GPMC_WAITPIN_IDX3;
+		break;
+	/* no waitpin */
+	case 0:
+		return 0;
+		break;

Do you need the break and return?

Not necessary, but to keep uniformity with the normal the way of case usage, can remove it.

Quoted 27 lines

I am wondering if we should combine all the gpmc_xxx_request pin functions into one. For example ...

static int gpmc_pin_request(int type, int pin)

{
	int pin_num, pin_mask;

	switch(type) {
	case GPMC_PIN_TYPE_CS:
		pin_num = GPMC_CS_NUM;
		pin_mask = gpmc_cs_map;
		break;
	case GPMC_PIN_TYPE_WAIT:
		pin_num = GPMC_NR_WAITPN;
		pin_mask = gpmc_waitpin_map;
		break;
	case GPMC_PIN_TYPE_WRITEPROTECT:
		pin_num = GPMC_NR_WP;
		pin_mask = gpmc_wp_map;
		break;
	default:
		return -ENODEV;
	}

	if (pin >= pin_num)
		return -ENODEV;
Quoted 7 lines
	if (gpmc_pin_is_reserved(pin_mask, pin))
		return -EBUSY;

	gpmc_reserve_pin(pin_mask, pin);

	return 0;
}

I don't prefer it as part of this series, due to,
1. not sure whether this is right approach for writeprotect, and no current users for it
2. CS will affect existing interface, hence more chances of issues for the existing interface due to driver conversion

I would prefer optimization/cleanup to done separately, not part of driver conversion series

Regards
Afzal

#98 lore raw

PATCH v5 11/14 ARM: OMAP2+: gpmc: handle connected peripherals

In reply to Afzal Mohammed

Platform 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 |  146 ++++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 146 insertions(+)
arch/arm/mach-omap2/gpmc.c +146 -0
index 9073a8a..281bd23 100644
@@ -155,6 +155,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 struct gpmc_client_irq gpmc_client_irq[GPMC_NR_IRQ];
@@ -1235,6 +1237,39 @@ 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;
+		}
+		gpmc_cs_misc_timings(cs->cs, &cs->time_ctrl.bool_timings);
+	} else if (cs->time_ctrl.type == has_clock) {
+		gpmc_cs_set_register_timings(cs->cs, &cs->time_ctrl.timings);
+		gpmc_cs_misc_timings(cs->cs, &cs->time_ctrl.bool_timings);
+	} else
+		gpmc_print_cs_timings(cs->cs);
+
+	return 0;
+}
+
 static inline unsigned gpmc_bit_to_irq(unsigned bitmask)
 {
 	return bitmask;
@@ -1307,10 +1342,100 @@ 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;
+
+	num = gpmc_setup_cs_mem(cs, res);
+	if (IS_ERR_VALUE(num))
+		return num;
+
+	ret = gpmc_setup_cs_config_timing(g_per, cs);
+	if (IS_ERR_VALUE(ret))
+		return ret;
+
+	num += gpmc_setup_cs_irq(cs, res + num);
+
+	return num;
+}
+
+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;
+	struct platform_device *pdev;
+
+	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 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)
@@ -1342,11 +1467,32 @@ 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");
 
+	/* 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_free_irq();
 	gpmc_mem_exit();
 	gpmc_dev = NULL;
Signature · 1 line
1.7.10.2
#99 lore raw

PATCH v5 11/14 ARM: OMAP2+: gpmc: handle connected peripherals

Hi Afzal,

Quoted 72 lines · Afzal Mohammed

Platform 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 |  146 ++++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 146 insertions(+)
arch/arm/mach-omap2/gpmc.c +39 -0
index 9073a8a..281bd23 100644
@@ -155,6 +155,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 struct gpmc_client_irq gpmc_client_irq[GPMC_NR_IRQ];
@@ -1235,6 +1237,39 @@ 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;
+		}
+		gpmc_cs_misc_timings(cs->cs, &cs->time_ctrl.bool_timings);
+	} else if (cs->time_ctrl.type == has_clock) {
+		gpmc_cs_set_register_timings(cs->cs, &cs->time_ctrl.timings);
+		gpmc_cs_misc_timings(cs->cs, &cs->time_ctrl.bool_timings);
+	} else
+		gpmc_print_cs_timings(cs->cs);
+
+	return 0;
+}
+
 static inline unsigned gpmc_bit_to_irq(unsigned bitmask)
 {
 	return bitmask;
@@ -1307,10 +1342,100 @@ 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;

arch/arm/mach-omap2/gpmc.c static int gpmc_setup_waitpin(struct gpmc_peripheral *g_per) · line 1358

Quoted 10 lines
arch/arm/mach-omap2/gpmc.c +10 -0
+
+	num = gpmc_setup_cs_mem(cs, res);
+	if (IS_ERR_VALUE(num))
+		return num;
+
+	ret = gpmc_setup_cs_config_timing(g_per, cs);
+	if (IS_ERR_VALUE(ret))
+		return ret;
+
+	num += gpmc_setup_cs_irq(cs, res + num);

What happens if the above function returns an error?

Jon

#100 lore raw

PATCH v5 11/14 ARM: OMAP2+: gpmc: handle connected peripherals

Hi Jon,

Quoted 18 lines · Jon

arch/arm/mach-omap2/gpmc.c static int gpmc_setup_waitpin(struct gpmc_peripheral *g_per) · line 1358

Quoted 13 lines · Afzal Mohammed
arch/arm/mach-omap2/gpmc.c +13 -0
+static __devinit int gpmc_setup_cs(struct gpmc_peripheral *g_per,
+				struct gpmc_cs_data *cs, struct resource *res) {
+	int num, ret;
+
+	num = gpmc_setup_cs_mem(cs, res);
+	if (IS_ERR_VALUE(num))
+		return num;
+
+	ret = gpmc_setup_cs_config_timing(g_per, cs);
+	if (IS_ERR_VALUE(ret))
+		return ret;
+
+	num += gpmc_setup_cs_irq(cs, res + num);

What happens if the above function returns an error?

That has been deliberately done as that implies user has not specified irq flag & hence is not an error.

But it seems, I should print error information if user tries to use an interrupt and if driver does not want/unable to use gpmc interrupt.

Regards
Afzal

#101 lore raw

PATCH v5 12/14 ARM: OMAP2+: gpmc: cs reconfigure helper

In reply to Afzal Mohammed

Helper for reconfiguring CS, peripheral that necessitated it was OneNAND.

---
 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
index 281bd23..d493a4c 100644
@@ -1429,6 +1429,38 @@ struct platform_device *gpmc_create_device(struct gpmc_peripheral *p)
 	return 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
index e1b130c..32017a1 100644
@@ -247,6 +247,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
#102 lore raw

PATCH v5 12/14 ARM: OMAP2+: gpmc: cs reconfigure helper

Quoted 2 lines · Afzal Mohammed

Helper for reconfiguring CS, peripheral that necessitated it was OneNAND.

Why? I think you need to add more about why this was needed.

Jon

Quoted 62 lines
---
 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
index 281bd23..d493a4c 100644
@@ -1429,6 +1429,38 @@ struct platform_device *gpmc_create_device(struct gpmc_peripheral *p)
 	return 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
index e1b130c..32017a1 100644
@@ -247,6 +247,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);
 
#103 lore raw

PATCH v5 12/14 ARM: OMAP2+: gpmc: cs reconfigure helper

Hi Jon,

Quoted 6 lines · Jon
Quoted 2 lines · Afzal Mohammed

Helper for reconfiguring CS, peripheral that necessitated it was OneNAND.

Why? I think you need to add more about why this was needed.

Ok, I will describe more

Regards
Afzal

#104 lore raw

PATCH v5 13/14 ARM: OMAP2+: gpmc: update nand register info

In reply to Afzal Mohammed

GPMC 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
index d493a4c..98b52c3 100644
@@ -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>
@@ -1500,12 +1501,18 @@ static __devinit int gpmc_probe(struct platform_device *pdev)
 		dev_warn(gpmc_dev, "gpmc_setup_irq failed\n");
 
 	/* 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
index 32017a1..32d7f3d 100644
@@ -195,6 +195,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
#105 lore raw

PATCH v5 14/14 ARM: OMAP2+: gpmc: writeprotect helper

In reply to Afzal Mohammed

GPMC has a writeprotect pin that can be connected to peripherals. If any CS wants to enable writeprotect, writeprotect will be enabled, once CS configurations are finished.

---
 arch/arm/mach-omap2/gpmc.c |   20 ++++++++++++++++++++
 1 file changed, 20 insertions(+)
arch/arm/mach-omap2/gpmc.c +20 -0
index 98b52c3..eec011a 100644
@@ -159,6 +159,7 @@ struct gpmc_peripheral {
 static struct gpmc_peripheral gpmc_peripheral[GPMC_CS_NUM];
 static unsigned gpmc_num_peripheral;
 static unsigned gpmc_waitpin_map;
+static bool gpmc_writeprotect;
 
 static struct gpmc_client_irq gpmc_client_irq[GPMC_NR_IRQ];
 static struct irq_chip gpmc_irq_chip;
@@ -976,6 +977,9 @@ static void gpmc_setup_cs_config(unsigned cs, unsigned conf)
 	else if (conf & GPMC_CONFIG1_PAGE_LEN_16)
 		l |= GPMC_CONFIG1_PAGE_LEN_16;
 
+	if (conf & GPMC_CONFIG_WRITEPROTECT)
+		gpmc_writeprotect = true;
+
 	conf &= (GPMC_CONFIG1_MUXADDDATA |
 			GPMC_CONFIG1_WRITETYPE_SYNC |
 			GPMC_CONFIG1_WRITEMULTIPLE_SUPP |
@@ -1462,6 +1466,19 @@ 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)
+{
+	u32 l;
+
+	l = gpmc_read_reg(GPMC_CONFIG);
+	if (gpmc_writeprotect == true) {
+		l &= ~GPMC_CONFIG_WRITEPROTECT;
+		dev_info(gpmc_dev, "write protect enabled\n");
+	} else
+		l |= GPMC_CONFIG_WRITEPROTECT;
+	gpmc_write_reg(GPMC_CONFIG, l);
+}
+
 static __devinit int gpmc_probe(struct platform_device *pdev)
 {
 	u32 l;
@@ -1521,6 +1538,8 @@ static __devinit int gpmc_probe(struct platform_device *pdev)
 			dev_err(gpmc_dev, "device creation on %s failed\n",
 								g_per->name);
 
+	gpmc_setup_writeprotect();
+
 	return 0;
 }
 
@@ -1531,6 +1550,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 = false;
 	gpmc_waitpin_map = 0;
 	gpmc_free_irq();
 	gpmc_mem_exit();
Signature · 1 line
1.7.10.2
#106 lore raw

PATCH v5 14/14 ARM: OMAP2+: gpmc: writeprotect helper

Quoted 4 lines · Afzal Mohammed

GPMC has a writeprotect pin that can be connected to peripherals. If any CS wants to enable writeprotect, writeprotect will be enabled, once CS configurations are finished.

I am still wondering if we should warn against multiple devices using the wait pin. I see that if could be valid to have multiple memory devices of the same type using the WP pin spanning multiple CS. However, in that case would/should the memory devices be registered as a single child? If so then we could still impose a policy of only one child device using the WP.

Jon

Quoted 64 lines
---
 arch/arm/mach-omap2/gpmc.c |   20 ++++++++++++++++++++
 1 file changed, 20 insertions(+)
arch/arm/mach-omap2/gpmc.c +20 -0
index 98b52c3..eec011a 100644
@@ -159,6 +159,7 @@ struct gpmc_peripheral {
 static struct gpmc_peripheral gpmc_peripheral[GPMC_CS_NUM];
 static unsigned gpmc_num_peripheral;
 static unsigned gpmc_waitpin_map;
+static bool gpmc_writeprotect;
 
 static struct gpmc_client_irq gpmc_client_irq[GPMC_NR_IRQ];
 static struct irq_chip gpmc_irq_chip;
@@ -976,6 +977,9 @@ static void gpmc_setup_cs_config(unsigned cs, unsigned conf)
 	else if (conf & GPMC_CONFIG1_PAGE_LEN_16)
 		l |= GPMC_CONFIG1_PAGE_LEN_16;
 
+	if (conf & GPMC_CONFIG_WRITEPROTECT)
+		gpmc_writeprotect = true;
+
 	conf &= (GPMC_CONFIG1_MUXADDDATA |
 			GPMC_CONFIG1_WRITETYPE_SYNC |
 			GPMC_CONFIG1_WRITEMULTIPLE_SUPP |
@@ -1462,6 +1466,19 @@ 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)
+{
+	u32 l;
+
+	l = gpmc_read_reg(GPMC_CONFIG);
+	if (gpmc_writeprotect == true) {
+		l &= ~GPMC_CONFIG_WRITEPROTECT;
+		dev_info(gpmc_dev, "write protect enabled\n");
+	} else
+		l |= GPMC_CONFIG_WRITEPROTECT;
+	gpmc_write_reg(GPMC_CONFIG, l);
+}
+
 static __devinit int gpmc_probe(struct platform_device *pdev)
 {
 	u32 l;
@@ -1521,6 +1538,8 @@ static __devinit int gpmc_probe(struct platform_device *pdev)
 			dev_err(gpmc_dev, "device creation on %s failed\n",
 								g_per->name);
 
+	gpmc_setup_writeprotect();
+
 	return 0;
 }
 
@@ -1531,6 +1550,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 = false;
 	gpmc_waitpin_map = 0;
 	gpmc_free_irq();
 	gpmc_mem_exit();
#107 lore raw

PATCH v5 14/14 ARM: OMAP2+: gpmc: writeprotect helper

Hi Jon,

Quoted 6 lines · Jon

I am still wondering if we should warn against multiple devices using the wait pin. I see that if could be valid to have multiple memory devices of the same type using the WP pin spanning multiple CS. However, in that case would/should the memory devices be registered as a single child? If so then we could still impose a policy of only one child device using the WP.

Presently there are no peripherals in mainline turning on writeprotect, initially intention was to just disable writeprotect at the end of probe and not provide platforms opportunity to enable writeprotect as none need it, still a provision has been provided for platform to enable it.

Probably these to be taken care when there is a requirement.

Regards
Afzal

#108 lore raw

PATCH v5 14/14 ARM: OMAP2+: gpmc: writeprotect helper

Hi Afzal,

Quoted 11 lines · Afzal

Hi Jon,

Quoted 6 lines · Jon

I am still wondering if we should warn against multiple devices using the wait pin. I see that if could be valid to have multiple memory devices of the same type using the WP pin spanning multiple CS. However, in that case would/should the memory devices be registered as a single child? If so then we could still impose a policy of only one child device using the WP.

Quoted 6 lines

Presently there are no peripherals in mainline turning on writeprotect, initially intention was to just disable writeprotect at the end of probe and not provide platforms opportunity to enable writeprotect as none need it, still a provision has been provided for platform to enable it.

Probably these to be taken care when there is a requirement.

Ok, but I am still not happy about this. So I did find that our omap2420h4 board does route the write protect to both NOR and NAND. Therefore, it does appear to be a valid use-case that multiple child devices can share the write-protect. So maybe we do not need to reserve the write-protect like we are doing for chip-selects, but I think that devices should indicate if they use the write-protect pin and we should either have this "enable on boot" as a global setting not specific to a child device or ensure that multiple devices using the wp have the same configuration of the wp on boot. In other words, if one device says enable on boot and the other does not, then warn.

Cheers
Jon

#109 lore raw

PATCH v5 14/14 ARM: OMAP2+: gpmc: writeprotect helper

Hi Tony,

Quoted 5 lines · Jon
Quoted 5 lines

Presently there are no peripherals in mainline turning on writeprotect, initially intention was to just disable writeprotect at the end of probe and not provide platforms opportunity to enable writeprotect as none need it, still a provision has been provided for platform to enable it.

Quoted 7 lines
Quoted 1 line

Probably these to be taken care when there is a requirement.

Ok, but I am still not happy about this. So I did find that our omap2420h4 board does route the write protect to both NOR and NAND. Therefore, it does appear to be a valid use-case that multiple child devices can share the write-protect. So maybe we do not need to reserve the write-protect like we are doing for chip-selects, but I think that

:

Quoted 5 lines

devices should indicate if they use the write-protect pin and we should either have this "enable on boot" as a global setting not specific to a child device or ensure that multiple devices using the wp have the same configuration of the wp on boot. In other words, if one device says enable on boot and the other does not, then warn.

Which one of Jon's above suggestions would you prefer,

i.e.
writeprotect as a global setting for all devices
Or
Ensure that multiple devices using wp have same configuration

Or anything else you have in mind ?

Regards
Afzal

maintainer
#110 lore raw

PATCH v5 14/14 ARM: OMAP2+: gpmc: writeprotect helper

Quoted 25 lines · Mohammed, Afzal

Hi Tony,

Quoted 5 lines · Jon
Quoted 5 lines

Presently there are no peripherals in mainline turning on writeprotect, initially intention was to just disable writeprotect at the end of probe and not provide platforms opportunity to enable writeprotect as none need it, still a provision has been provided for platform to enable it.

Quoted 7 lines
Quoted 1 line

Probably these to be taken care when there is a requirement.

Ok, but I am still not happy about this. So I did find that our omap2420h4 board does route the write protect to both NOR and NAND. Therefore, it does appear to be a valid use-case that multiple child devices can share the write-protect. So maybe we do not need to reserve the write-protect like we are doing for chip-selects, but I think that

:

Quoted 5 lines

devices should indicate if they use the write-protect pin and we should either have this "enable on boot" as a global setting not specific to a child device or ensure that multiple devices using the wp have the same configuration of the wp on boot. In other words, if one device says enable on boot and the other does not, then warn.

Quoted 8 lines

Which one of Jon's above suggestions would you prefer,

i.e.
writeprotect as a global setting for all devices
Or
Ensure that multiple devices using wp have same configuration

Or anything else you have in mind ?

If it's shared it can cause undesired effects on the mounted rootfs if one device changes it. So some kind of checking for a shared case should be available before a device is allowed to change it.

Tony

#111 lore raw

PATCH v5 14/14 ARM: OMAP2+: gpmc: writeprotect helper

Hi,

Quoted 16 lines · Tony Lindgren
Quoted 15 lines · Mohammed, Afzal
Quoted 5 lines · Jon

devices should indicate if they use the write-protect pin and we should either have this "enable on boot" as a global setting not specific to a child device or ensure that multiple devices using the wp have the same configuration of the wp on boot. In other words, if one device says enable on boot and the other does not, then warn.

Which one of Jon's above suggestions would you prefer,

i.e.
writeprotect as a global setting for all devices
Or
Ensure that multiple devices using wp have same configuration

Quoted 5 lines
Quoted 1 line

Or anything else you have in mind ?

If it's shared it can cause undesired effects on the mounted rootfs if one device changes it. So some kind of checking for a shared case should be available before a device is allowed to change it.

Ok, I will add a check for shared case.

Regards
Afzal

#112 lore raw

v5 cover GPMC driver conversion

In reply to Afzal Mohammed

Hi Tony,

Quoted 11 lines · Afzal

Hi,

This series is based on 3.5-rc1, and is dependent on [1,2,3]

This series has been tested on omap3evm (smsc911x) rev G & C and beagle board(nand) using patch series which is going to be posted shortly (this series only creates a driver out of GPMC code)

Also using private patches, nand & onenand was tested on omap3evm, rev G & C respectively (as support for these were not in mainline)

Quoted 8 lines

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 regarding the same.

[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. I had tried to go as per your requirements.

This series keeps old interface while providing a new driver interface. Old interface will work (to the best of my knowledge; omap3evm & beagle are the ones available here to test) at each of the commits.

Regards
Afzal

maintainer
#113 lore raw

v5 cover GPMC driver conversion

Quoted 23 lines · Mohammed, Afzal

Hi Tony,

Quoted 11 lines · Afzal

Hi,

This series is based on 3.5-rc1, and is dependent on [1,2,3]

This series has been tested on omap3evm (smsc911x) rev G & C and beagle board(nand) using patch series which is going to be posted shortly (this series only creates a driver out of GPMC code)

Also using private patches, nand & onenand was tested on omap3evm, rev G & C respectively (as support for these were not in mainline)

Quoted 8 lines

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 regarding the same.

[PATCH 03/13] ARM: OMAP2+: gpmc: driver migration helper, is to be reverted once all GPMC peripherals are migrated to use driver interface.

Quoted 6 lines

Please let me know your comments on this series. I had tried to go as per your requirements.

This series keeps old interface while providing a new driver interface. Old interface will work (to the best of my knowledge; omap3evm & beagle are the ones available here to test) at each of the commits.

Cool, yeah looks like the old interface almost works. I had to undo the new additions for tusb6010 DMA to work as below. Then Jon has some good comments. I also made few comments to the GPMC using driver changes.

In general, please keep in mind that eventually we want this to be a regular device driver that also works as a loadable module.

And we need the device tree bindings for GPMC so we can start dropping board-*.c files as the GPMC seems to be last remaining blocker for making rarely used legacy boards DT only. So it might be worth playing with that so we don't again have to redo some parts.

Regards,

Tony

arch/arm/mach-omap2/gpmc.c +2 -2
@@ -363,13 +363,13 @@ int gpmc_cs_set_timings(int cs, const struct gpmc_timings *t)
 	GPMC_SET_ONE(GPMC_CS_CONFIG5, 16, 20, access);
 
 	GPMC_SET_ONE(GPMC_CS_CONFIG5, 24, 27, page_burst_access);
-
+#if 0
 	GPMC_SET_ONE(GPMC_CS_CONFIG6, 0, 3, bus_turnaround);
 	GPMC_SET_ONE(GPMC_CS_CONFIG6, 8, 11, cycle2cycle_delay);
 
 	GPMC_SET_ONE(GPMC_CS_CONFIG1, 18, 19, wait_monitoring);
 	GPMC_SET_ONE(GPMC_CS_CONFIG1, 25, 26, clk_activation);
-
+#endif
 	if (cpu_is_omap34xx()) {
 		GPMC_SET_ONE(GPMC_CS_CONFIG6, 16, 19, wr_data_mux_bus);
 		GPMC_SET_ONE(GPMC_CS_CONFIG6, 24, 28, wr_access);
#114 lore raw

v5 cover GPMC driver conversion

Hi Tony,

Quoted 3 lines · Tony Lindgren

Cool, yeah looks like the old interface almost works. I had to undo the new additions for tusb6010 DMA to work as below. Then Jon has some good comments. I also made few comments to the GPMC using driver changes.

Thanks and sorry for the trouble it caused you

I will post next version based on the comments

Quoted 2 lines

In general, please keep in mind that eventually we want this to be a regular device driver that also works as a loadable module.

Yes, that is my intention, at the end of driver conversion, it should be able to work as a module.

Quoted 5 lines

And we need the device tree bindings for GPMC so we can start dropping board-*.c files as the GPMC seems to be last remaining blocker for making rarely used legacy boards DT only. So it might be worth playing with that so we don't again have to redo some parts.

We would prefer to do it one step at a time, have driver conversion changes first in, and then work on DT migration. Once these changes are in we have a base to work on for further DT migration.

Please let me know your opinion

Regards
Afzal

maintainer
#115 lore raw

v5 cover GPMC driver conversion

Quoted 8 lines · Mohammed, Afzal
Quoted 5 lines · Tony Lindgren

And we need the device tree bindings for GPMC so we can start dropping board-*.c files as the GPMC seems to be last remaining blocker for making rarely used legacy boards DT only. So it might be worth playing with that so we don't again have to redo some parts.

Quoted 5 lines

We would prefer to do it one step at a time, have driver conversion changes first in, and then work on DT migration. Once these changes are in we have a base to work on for further DT migration.

Please let me know your opinion

Sounds good to me.

Tony