18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci * GPMC support functions
48c2ecf20Sopenharmony_ci *
58c2ecf20Sopenharmony_ci * Copyright (C) 2005-2006 Nokia Corporation
68c2ecf20Sopenharmony_ci *
78c2ecf20Sopenharmony_ci * Author: Juha Yrjola
88c2ecf20Sopenharmony_ci *
98c2ecf20Sopenharmony_ci * Copyright (C) 2009 Texas Instruments
108c2ecf20Sopenharmony_ci * Added OMAP4 support - Santosh Shilimkar <santosh.shilimkar@ti.com>
118c2ecf20Sopenharmony_ci */
128c2ecf20Sopenharmony_ci#include <linux/irq.h>
138c2ecf20Sopenharmony_ci#include <linux/kernel.h>
148c2ecf20Sopenharmony_ci#include <linux/init.h>
158c2ecf20Sopenharmony_ci#include <linux/err.h>
168c2ecf20Sopenharmony_ci#include <linux/clk.h>
178c2ecf20Sopenharmony_ci#include <linux/ioport.h>
188c2ecf20Sopenharmony_ci#include <linux/spinlock.h>
198c2ecf20Sopenharmony_ci#include <linux/io.h>
208c2ecf20Sopenharmony_ci#include <linux/gpio/driver.h>
218c2ecf20Sopenharmony_ci#include <linux/gpio/consumer.h> /* GPIO descriptor enum */
228c2ecf20Sopenharmony_ci#include <linux/gpio/machine.h>
238c2ecf20Sopenharmony_ci#include <linux/interrupt.h>
248c2ecf20Sopenharmony_ci#include <linux/irqdomain.h>
258c2ecf20Sopenharmony_ci#include <linux/platform_device.h>
268c2ecf20Sopenharmony_ci#include <linux/of.h>
278c2ecf20Sopenharmony_ci#include <linux/of_address.h>
288c2ecf20Sopenharmony_ci#include <linux/of_device.h>
298c2ecf20Sopenharmony_ci#include <linux/of_platform.h>
308c2ecf20Sopenharmony_ci#include <linux/omap-gpmc.h>
318c2ecf20Sopenharmony_ci#include <linux/pm_runtime.h>
328c2ecf20Sopenharmony_ci#include <linux/sizes.h>
338c2ecf20Sopenharmony_ci
348c2ecf20Sopenharmony_ci#include <linux/platform_data/mtd-nand-omap2.h>
358c2ecf20Sopenharmony_ci
368c2ecf20Sopenharmony_ci#define	DEVICE_NAME		"omap-gpmc"
378c2ecf20Sopenharmony_ci
388c2ecf20Sopenharmony_ci/* GPMC register offsets */
398c2ecf20Sopenharmony_ci#define GPMC_REVISION		0x00
408c2ecf20Sopenharmony_ci#define GPMC_SYSCONFIG		0x10
418c2ecf20Sopenharmony_ci#define GPMC_SYSSTATUS		0x14
428c2ecf20Sopenharmony_ci#define GPMC_IRQSTATUS		0x18
438c2ecf20Sopenharmony_ci#define GPMC_IRQENABLE		0x1c
448c2ecf20Sopenharmony_ci#define GPMC_TIMEOUT_CONTROL	0x40
458c2ecf20Sopenharmony_ci#define GPMC_ERR_ADDRESS	0x44
468c2ecf20Sopenharmony_ci#define GPMC_ERR_TYPE		0x48
478c2ecf20Sopenharmony_ci#define GPMC_CONFIG		0x50
488c2ecf20Sopenharmony_ci#define GPMC_STATUS		0x54
498c2ecf20Sopenharmony_ci#define GPMC_PREFETCH_CONFIG1	0x1e0
508c2ecf20Sopenharmony_ci#define GPMC_PREFETCH_CONFIG2	0x1e4
518c2ecf20Sopenharmony_ci#define GPMC_PREFETCH_CONTROL	0x1ec
528c2ecf20Sopenharmony_ci#define GPMC_PREFETCH_STATUS	0x1f0
538c2ecf20Sopenharmony_ci#define GPMC_ECC_CONFIG		0x1f4
548c2ecf20Sopenharmony_ci#define GPMC_ECC_CONTROL	0x1f8
558c2ecf20Sopenharmony_ci#define GPMC_ECC_SIZE_CONFIG	0x1fc
568c2ecf20Sopenharmony_ci#define GPMC_ECC1_RESULT        0x200
578c2ecf20Sopenharmony_ci#define GPMC_ECC_BCH_RESULT_0   0x240   /* not available on OMAP2 */
588c2ecf20Sopenharmony_ci#define	GPMC_ECC_BCH_RESULT_1	0x244	/* not available on OMAP2 */
598c2ecf20Sopenharmony_ci#define	GPMC_ECC_BCH_RESULT_2	0x248	/* not available on OMAP2 */
608c2ecf20Sopenharmony_ci#define	GPMC_ECC_BCH_RESULT_3	0x24c	/* not available on OMAP2 */
618c2ecf20Sopenharmony_ci#define	GPMC_ECC_BCH_RESULT_4	0x300	/* not available on OMAP2 */
628c2ecf20Sopenharmony_ci#define	GPMC_ECC_BCH_RESULT_5	0x304	/* not available on OMAP2 */
638c2ecf20Sopenharmony_ci#define	GPMC_ECC_BCH_RESULT_6	0x308	/* not available on OMAP2 */
648c2ecf20Sopenharmony_ci
658c2ecf20Sopenharmony_ci/* GPMC ECC control settings */
668c2ecf20Sopenharmony_ci#define GPMC_ECC_CTRL_ECCCLEAR		0x100
678c2ecf20Sopenharmony_ci#define GPMC_ECC_CTRL_ECCDISABLE	0x000
688c2ecf20Sopenharmony_ci#define GPMC_ECC_CTRL_ECCREG1		0x001
698c2ecf20Sopenharmony_ci#define GPMC_ECC_CTRL_ECCREG2		0x002
708c2ecf20Sopenharmony_ci#define GPMC_ECC_CTRL_ECCREG3		0x003
718c2ecf20Sopenharmony_ci#define GPMC_ECC_CTRL_ECCREG4		0x004
728c2ecf20Sopenharmony_ci#define GPMC_ECC_CTRL_ECCREG5		0x005
738c2ecf20Sopenharmony_ci#define GPMC_ECC_CTRL_ECCREG6		0x006
748c2ecf20Sopenharmony_ci#define GPMC_ECC_CTRL_ECCREG7		0x007
758c2ecf20Sopenharmony_ci#define GPMC_ECC_CTRL_ECCREG8		0x008
768c2ecf20Sopenharmony_ci#define GPMC_ECC_CTRL_ECCREG9		0x009
778c2ecf20Sopenharmony_ci
788c2ecf20Sopenharmony_ci#define GPMC_CONFIG_LIMITEDADDRESS		BIT(1)
798c2ecf20Sopenharmony_ci
808c2ecf20Sopenharmony_ci#define GPMC_STATUS_EMPTYWRITEBUFFERSTATUS	BIT(0)
818c2ecf20Sopenharmony_ci
828c2ecf20Sopenharmony_ci#define	GPMC_CONFIG2_CSEXTRADELAY		BIT(7)
838c2ecf20Sopenharmony_ci#define	GPMC_CONFIG3_ADVEXTRADELAY		BIT(7)
848c2ecf20Sopenharmony_ci#define	GPMC_CONFIG4_OEEXTRADELAY		BIT(7)
858c2ecf20Sopenharmony_ci#define	GPMC_CONFIG4_WEEXTRADELAY		BIT(23)
868c2ecf20Sopenharmony_ci#define	GPMC_CONFIG6_CYCLE2CYCLEDIFFCSEN	BIT(6)
878c2ecf20Sopenharmony_ci#define	GPMC_CONFIG6_CYCLE2CYCLESAMECSEN	BIT(7)
888c2ecf20Sopenharmony_ci
898c2ecf20Sopenharmony_ci#define GPMC_CS0_OFFSET		0x60
908c2ecf20Sopenharmony_ci#define GPMC_CS_SIZE		0x30
918c2ecf20Sopenharmony_ci#define	GPMC_BCH_SIZE		0x10
928c2ecf20Sopenharmony_ci
938c2ecf20Sopenharmony_ci/*
948c2ecf20Sopenharmony_ci * The first 1MB of GPMC address space is typically mapped to
958c2ecf20Sopenharmony_ci * the internal ROM. Never allocate the first page, to
968c2ecf20Sopenharmony_ci * facilitate bug detection; even if we didn't boot from ROM.
978c2ecf20Sopenharmony_ci * As GPMC minimum partition size is 16MB we can only start from
988c2ecf20Sopenharmony_ci * there.
998c2ecf20Sopenharmony_ci */
1008c2ecf20Sopenharmony_ci#define GPMC_MEM_START		0x1000000
1018c2ecf20Sopenharmony_ci#define GPMC_MEM_END		0x3FFFFFFF
1028c2ecf20Sopenharmony_ci
1038c2ecf20Sopenharmony_ci#define GPMC_CHUNK_SHIFT	24		/* 16 MB */
1048c2ecf20Sopenharmony_ci#define GPMC_SECTION_SHIFT	28		/* 128 MB */
1058c2ecf20Sopenharmony_ci
1068c2ecf20Sopenharmony_ci#define CS_NUM_SHIFT		24
1078c2ecf20Sopenharmony_ci#define ENABLE_PREFETCH		(0x1 << 7)
1088c2ecf20Sopenharmony_ci#define DMA_MPU_MODE		2
1098c2ecf20Sopenharmony_ci
1108c2ecf20Sopenharmony_ci#define	GPMC_REVISION_MAJOR(l)		(((l) >> 4) & 0xf)
1118c2ecf20Sopenharmony_ci#define	GPMC_REVISION_MINOR(l)		((l) & 0xf)
1128c2ecf20Sopenharmony_ci
1138c2ecf20Sopenharmony_ci#define	GPMC_HAS_WR_ACCESS		0x1
1148c2ecf20Sopenharmony_ci#define	GPMC_HAS_WR_DATA_MUX_BUS	0x2
1158c2ecf20Sopenharmony_ci#define	GPMC_HAS_MUX_AAD		0x4
1168c2ecf20Sopenharmony_ci
1178c2ecf20Sopenharmony_ci#define GPMC_NR_WAITPINS		4
1188c2ecf20Sopenharmony_ci
1198c2ecf20Sopenharmony_ci#define GPMC_CS_CONFIG1		0x00
1208c2ecf20Sopenharmony_ci#define GPMC_CS_CONFIG2		0x04
1218c2ecf20Sopenharmony_ci#define GPMC_CS_CONFIG3		0x08
1228c2ecf20Sopenharmony_ci#define GPMC_CS_CONFIG4		0x0c
1238c2ecf20Sopenharmony_ci#define GPMC_CS_CONFIG5		0x10
1248c2ecf20Sopenharmony_ci#define GPMC_CS_CONFIG6		0x14
1258c2ecf20Sopenharmony_ci#define GPMC_CS_CONFIG7		0x18
1268c2ecf20Sopenharmony_ci#define GPMC_CS_NAND_COMMAND	0x1c
1278c2ecf20Sopenharmony_ci#define GPMC_CS_NAND_ADDRESS	0x20
1288c2ecf20Sopenharmony_ci#define GPMC_CS_NAND_DATA	0x24
1298c2ecf20Sopenharmony_ci
1308c2ecf20Sopenharmony_ci/* Control Commands */
1318c2ecf20Sopenharmony_ci#define GPMC_CONFIG_RDY_BSY	0x00000001
1328c2ecf20Sopenharmony_ci#define GPMC_CONFIG_DEV_SIZE	0x00000002
1338c2ecf20Sopenharmony_ci#define GPMC_CONFIG_DEV_TYPE	0x00000003
1348c2ecf20Sopenharmony_ci
1358c2ecf20Sopenharmony_ci#define GPMC_CONFIG1_WRAPBURST_SUPP     (1 << 31)
1368c2ecf20Sopenharmony_ci#define GPMC_CONFIG1_READMULTIPLE_SUPP  (1 << 30)
1378c2ecf20Sopenharmony_ci#define GPMC_CONFIG1_READTYPE_ASYNC     (0 << 29)
1388c2ecf20Sopenharmony_ci#define GPMC_CONFIG1_READTYPE_SYNC      (1 << 29)
1398c2ecf20Sopenharmony_ci#define GPMC_CONFIG1_WRITEMULTIPLE_SUPP (1 << 28)
1408c2ecf20Sopenharmony_ci#define GPMC_CONFIG1_WRITETYPE_ASYNC    (0 << 27)
1418c2ecf20Sopenharmony_ci#define GPMC_CONFIG1_WRITETYPE_SYNC     (1 << 27)
1428c2ecf20Sopenharmony_ci#define GPMC_CONFIG1_CLKACTIVATIONTIME(val) (((val) & 3) << 25)
1438c2ecf20Sopenharmony_ci/** CLKACTIVATIONTIME Max Ticks */
1448c2ecf20Sopenharmony_ci#define GPMC_CONFIG1_CLKACTIVATIONTIME_MAX 2
1458c2ecf20Sopenharmony_ci#define GPMC_CONFIG1_PAGE_LEN(val)      (((val) & 3) << 23)
1468c2ecf20Sopenharmony_ci/** ATTACHEDDEVICEPAGELENGTH Max Value */
1478c2ecf20Sopenharmony_ci#define GPMC_CONFIG1_ATTACHEDDEVICEPAGELENGTH_MAX 2
1488c2ecf20Sopenharmony_ci#define GPMC_CONFIG1_WAIT_READ_MON      (1 << 22)
1498c2ecf20Sopenharmony_ci#define GPMC_CONFIG1_WAIT_WRITE_MON     (1 << 21)
1508c2ecf20Sopenharmony_ci#define GPMC_CONFIG1_WAIT_MON_TIME(val) (((val) & 3) << 18)
1518c2ecf20Sopenharmony_ci/** WAITMONITORINGTIME Max Ticks */
1528c2ecf20Sopenharmony_ci#define GPMC_CONFIG1_WAITMONITORINGTIME_MAX  2
1538c2ecf20Sopenharmony_ci#define GPMC_CONFIG1_WAIT_PIN_SEL(val)  (((val) & 3) << 16)
1548c2ecf20Sopenharmony_ci#define GPMC_CONFIG1_DEVICESIZE(val)    (((val) & 3) << 12)
1558c2ecf20Sopenharmony_ci#define GPMC_CONFIG1_DEVICESIZE_16      GPMC_CONFIG1_DEVICESIZE(1)
1568c2ecf20Sopenharmony_ci/** DEVICESIZE Max Value */
1578c2ecf20Sopenharmony_ci#define GPMC_CONFIG1_DEVICESIZE_MAX     1
1588c2ecf20Sopenharmony_ci#define GPMC_CONFIG1_DEVICETYPE(val)    (((val) & 3) << 10)
1598c2ecf20Sopenharmony_ci#define GPMC_CONFIG1_DEVICETYPE_NOR     GPMC_CONFIG1_DEVICETYPE(0)
1608c2ecf20Sopenharmony_ci#define GPMC_CONFIG1_MUXTYPE(val)       (((val) & 3) << 8)
1618c2ecf20Sopenharmony_ci#define GPMC_CONFIG1_TIME_PARA_GRAN     (1 << 4)
1628c2ecf20Sopenharmony_ci#define GPMC_CONFIG1_FCLK_DIV(val)      ((val) & 3)
1638c2ecf20Sopenharmony_ci#define GPMC_CONFIG1_FCLK_DIV2          (GPMC_CONFIG1_FCLK_DIV(1))
1648c2ecf20Sopenharmony_ci#define GPMC_CONFIG1_FCLK_DIV3          (GPMC_CONFIG1_FCLK_DIV(2))
1658c2ecf20Sopenharmony_ci#define GPMC_CONFIG1_FCLK_DIV4          (GPMC_CONFIG1_FCLK_DIV(3))
1668c2ecf20Sopenharmony_ci#define GPMC_CONFIG7_CSVALID		(1 << 6)
1678c2ecf20Sopenharmony_ci
1688c2ecf20Sopenharmony_ci#define GPMC_CONFIG7_BASEADDRESS_MASK	0x3f
1698c2ecf20Sopenharmony_ci#define GPMC_CONFIG7_CSVALID_MASK	BIT(6)
1708c2ecf20Sopenharmony_ci#define GPMC_CONFIG7_MASKADDRESS_OFFSET	8
1718c2ecf20Sopenharmony_ci#define GPMC_CONFIG7_MASKADDRESS_MASK	(0xf << GPMC_CONFIG7_MASKADDRESS_OFFSET)
1728c2ecf20Sopenharmony_ci/* All CONFIG7 bits except reserved bits */
1738c2ecf20Sopenharmony_ci#define GPMC_CONFIG7_MASK		(GPMC_CONFIG7_BASEADDRESS_MASK | \
1748c2ecf20Sopenharmony_ci					 GPMC_CONFIG7_CSVALID_MASK |     \
1758c2ecf20Sopenharmony_ci					 GPMC_CONFIG7_MASKADDRESS_MASK)
1768c2ecf20Sopenharmony_ci
1778c2ecf20Sopenharmony_ci#define GPMC_DEVICETYPE_NOR		0
1788c2ecf20Sopenharmony_ci#define GPMC_DEVICETYPE_NAND		2
1798c2ecf20Sopenharmony_ci#define GPMC_CONFIG_WRITEPROTECT	0x00000010
1808c2ecf20Sopenharmony_ci#define WR_RD_PIN_MONITORING		0x00600000
1818c2ecf20Sopenharmony_ci
1828c2ecf20Sopenharmony_ci/* ECC commands */
1838c2ecf20Sopenharmony_ci#define GPMC_ECC_READ		0 /* Reset Hardware ECC for read */
1848c2ecf20Sopenharmony_ci#define GPMC_ECC_WRITE		1 /* Reset Hardware ECC for write */
1858c2ecf20Sopenharmony_ci#define GPMC_ECC_READSYN	2 /* Reset before syndrom is read back */
1868c2ecf20Sopenharmony_ci
1878c2ecf20Sopenharmony_ci#define	GPMC_NR_NAND_IRQS	2 /* number of NAND specific IRQs */
1888c2ecf20Sopenharmony_ci
1898c2ecf20Sopenharmony_cienum gpmc_clk_domain {
1908c2ecf20Sopenharmony_ci	GPMC_CD_FCLK,
1918c2ecf20Sopenharmony_ci	GPMC_CD_CLK
1928c2ecf20Sopenharmony_ci};
1938c2ecf20Sopenharmony_ci
1948c2ecf20Sopenharmony_cistruct gpmc_cs_data {
1958c2ecf20Sopenharmony_ci	const char *name;
1968c2ecf20Sopenharmony_ci
1978c2ecf20Sopenharmony_ci#define GPMC_CS_RESERVED	(1 << 0)
1988c2ecf20Sopenharmony_ci	u32 flags;
1998c2ecf20Sopenharmony_ci
2008c2ecf20Sopenharmony_ci	struct resource mem;
2018c2ecf20Sopenharmony_ci};
2028c2ecf20Sopenharmony_ci
2038c2ecf20Sopenharmony_ci/* Structure to save gpmc cs context */
2048c2ecf20Sopenharmony_cistruct gpmc_cs_config {
2058c2ecf20Sopenharmony_ci	u32 config1;
2068c2ecf20Sopenharmony_ci	u32 config2;
2078c2ecf20Sopenharmony_ci	u32 config3;
2088c2ecf20Sopenharmony_ci	u32 config4;
2098c2ecf20Sopenharmony_ci	u32 config5;
2108c2ecf20Sopenharmony_ci	u32 config6;
2118c2ecf20Sopenharmony_ci	u32 config7;
2128c2ecf20Sopenharmony_ci	int is_valid;
2138c2ecf20Sopenharmony_ci};
2148c2ecf20Sopenharmony_ci
2158c2ecf20Sopenharmony_ci/*
2168c2ecf20Sopenharmony_ci * Structure to save/restore gpmc context
2178c2ecf20Sopenharmony_ci * to support core off on OMAP3
2188c2ecf20Sopenharmony_ci */
2198c2ecf20Sopenharmony_cistruct omap3_gpmc_regs {
2208c2ecf20Sopenharmony_ci	u32 sysconfig;
2218c2ecf20Sopenharmony_ci	u32 irqenable;
2228c2ecf20Sopenharmony_ci	u32 timeout_ctrl;
2238c2ecf20Sopenharmony_ci	u32 config;
2248c2ecf20Sopenharmony_ci	u32 prefetch_config1;
2258c2ecf20Sopenharmony_ci	u32 prefetch_config2;
2268c2ecf20Sopenharmony_ci	u32 prefetch_control;
2278c2ecf20Sopenharmony_ci	struct gpmc_cs_config cs_context[GPMC_CS_NUM];
2288c2ecf20Sopenharmony_ci};
2298c2ecf20Sopenharmony_ci
2308c2ecf20Sopenharmony_cistruct gpmc_device {
2318c2ecf20Sopenharmony_ci	struct device *dev;
2328c2ecf20Sopenharmony_ci	int irq;
2338c2ecf20Sopenharmony_ci	struct irq_chip irq_chip;
2348c2ecf20Sopenharmony_ci	struct gpio_chip gpio_chip;
2358c2ecf20Sopenharmony_ci	int nirqs;
2368c2ecf20Sopenharmony_ci};
2378c2ecf20Sopenharmony_ci
2388c2ecf20Sopenharmony_cistatic struct irq_domain *gpmc_irq_domain;
2398c2ecf20Sopenharmony_ci
2408c2ecf20Sopenharmony_cistatic struct resource	gpmc_mem_root;
2418c2ecf20Sopenharmony_cistatic struct gpmc_cs_data gpmc_cs[GPMC_CS_NUM];
2428c2ecf20Sopenharmony_cistatic DEFINE_SPINLOCK(gpmc_mem_lock);
2438c2ecf20Sopenharmony_ci/* Define chip-selects as reserved by default until probe completes */
2448c2ecf20Sopenharmony_cistatic unsigned int gpmc_cs_num = GPMC_CS_NUM;
2458c2ecf20Sopenharmony_cistatic unsigned int gpmc_nr_waitpins;
2468c2ecf20Sopenharmony_cistatic unsigned int gpmc_capability;
2478c2ecf20Sopenharmony_cistatic void __iomem *gpmc_base;
2488c2ecf20Sopenharmony_ci
2498c2ecf20Sopenharmony_cistatic struct clk *gpmc_l3_clk;
2508c2ecf20Sopenharmony_ci
2518c2ecf20Sopenharmony_cistatic irqreturn_t gpmc_handle_irq(int irq, void *dev);
2528c2ecf20Sopenharmony_ci
2538c2ecf20Sopenharmony_cistatic void gpmc_write_reg(int idx, u32 val)
2548c2ecf20Sopenharmony_ci{
2558c2ecf20Sopenharmony_ci	writel_relaxed(val, gpmc_base + idx);
2568c2ecf20Sopenharmony_ci}
2578c2ecf20Sopenharmony_ci
2588c2ecf20Sopenharmony_cistatic u32 gpmc_read_reg(int idx)
2598c2ecf20Sopenharmony_ci{
2608c2ecf20Sopenharmony_ci	return readl_relaxed(gpmc_base + idx);
2618c2ecf20Sopenharmony_ci}
2628c2ecf20Sopenharmony_ci
2638c2ecf20Sopenharmony_civoid gpmc_cs_write_reg(int cs, int idx, u32 val)
2648c2ecf20Sopenharmony_ci{
2658c2ecf20Sopenharmony_ci	void __iomem *reg_addr;
2668c2ecf20Sopenharmony_ci
2678c2ecf20Sopenharmony_ci	reg_addr = gpmc_base + GPMC_CS0_OFFSET + (cs * GPMC_CS_SIZE) + idx;
2688c2ecf20Sopenharmony_ci	writel_relaxed(val, reg_addr);
2698c2ecf20Sopenharmony_ci}
2708c2ecf20Sopenharmony_ci
2718c2ecf20Sopenharmony_cistatic u32 gpmc_cs_read_reg(int cs, int idx)
2728c2ecf20Sopenharmony_ci{
2738c2ecf20Sopenharmony_ci	void __iomem *reg_addr;
2748c2ecf20Sopenharmony_ci
2758c2ecf20Sopenharmony_ci	reg_addr = gpmc_base + GPMC_CS0_OFFSET + (cs * GPMC_CS_SIZE) + idx;
2768c2ecf20Sopenharmony_ci	return readl_relaxed(reg_addr);
2778c2ecf20Sopenharmony_ci}
2788c2ecf20Sopenharmony_ci
2798c2ecf20Sopenharmony_ci/* TODO: Add support for gpmc_fck to clock framework and use it */
2808c2ecf20Sopenharmony_cistatic unsigned long gpmc_get_fclk_period(void)
2818c2ecf20Sopenharmony_ci{
2828c2ecf20Sopenharmony_ci	unsigned long rate = clk_get_rate(gpmc_l3_clk);
2838c2ecf20Sopenharmony_ci
2848c2ecf20Sopenharmony_ci	rate /= 1000;
2858c2ecf20Sopenharmony_ci	rate = 1000000000 / rate;	/* In picoseconds */
2868c2ecf20Sopenharmony_ci
2878c2ecf20Sopenharmony_ci	return rate;
2888c2ecf20Sopenharmony_ci}
2898c2ecf20Sopenharmony_ci
2908c2ecf20Sopenharmony_ci/**
2918c2ecf20Sopenharmony_ci * gpmc_get_clk_period - get period of selected clock domain in ps
2928c2ecf20Sopenharmony_ci * @cs: Chip Select Region.
2938c2ecf20Sopenharmony_ci * @cd: Clock Domain.
2948c2ecf20Sopenharmony_ci *
2958c2ecf20Sopenharmony_ci * GPMC_CS_CONFIG1 GPMCFCLKDIVIDER for cs has to be setup
2968c2ecf20Sopenharmony_ci * prior to calling this function with GPMC_CD_CLK.
2978c2ecf20Sopenharmony_ci */
2988c2ecf20Sopenharmony_cistatic unsigned long gpmc_get_clk_period(int cs, enum gpmc_clk_domain cd)
2998c2ecf20Sopenharmony_ci{
3008c2ecf20Sopenharmony_ci	unsigned long tick_ps = gpmc_get_fclk_period();
3018c2ecf20Sopenharmony_ci	u32 l;
3028c2ecf20Sopenharmony_ci	int div;
3038c2ecf20Sopenharmony_ci
3048c2ecf20Sopenharmony_ci	switch (cd) {
3058c2ecf20Sopenharmony_ci	case GPMC_CD_CLK:
3068c2ecf20Sopenharmony_ci		/* get current clk divider */
3078c2ecf20Sopenharmony_ci		l = gpmc_cs_read_reg(cs, GPMC_CS_CONFIG1);
3088c2ecf20Sopenharmony_ci		div = (l & 0x03) + 1;
3098c2ecf20Sopenharmony_ci		/* get GPMC_CLK period */
3108c2ecf20Sopenharmony_ci		tick_ps *= div;
3118c2ecf20Sopenharmony_ci		break;
3128c2ecf20Sopenharmony_ci	case GPMC_CD_FCLK:
3138c2ecf20Sopenharmony_ci	default:
3148c2ecf20Sopenharmony_ci		break;
3158c2ecf20Sopenharmony_ci	}
3168c2ecf20Sopenharmony_ci
3178c2ecf20Sopenharmony_ci	return tick_ps;
3188c2ecf20Sopenharmony_ci}
3198c2ecf20Sopenharmony_ci
3208c2ecf20Sopenharmony_cistatic unsigned int gpmc_ns_to_clk_ticks(unsigned int time_ns, int cs,
3218c2ecf20Sopenharmony_ci					 enum gpmc_clk_domain cd)
3228c2ecf20Sopenharmony_ci{
3238c2ecf20Sopenharmony_ci	unsigned long tick_ps;
3248c2ecf20Sopenharmony_ci
3258c2ecf20Sopenharmony_ci	/* Calculate in picosecs to yield more exact results */
3268c2ecf20Sopenharmony_ci	tick_ps = gpmc_get_clk_period(cs, cd);
3278c2ecf20Sopenharmony_ci
3288c2ecf20Sopenharmony_ci	return (time_ns * 1000 + tick_ps - 1) / tick_ps;
3298c2ecf20Sopenharmony_ci}
3308c2ecf20Sopenharmony_ci
3318c2ecf20Sopenharmony_cistatic unsigned int gpmc_ns_to_ticks(unsigned int time_ns)
3328c2ecf20Sopenharmony_ci{
3338c2ecf20Sopenharmony_ci	return gpmc_ns_to_clk_ticks(time_ns, /* any CS */ 0, GPMC_CD_FCLK);
3348c2ecf20Sopenharmony_ci}
3358c2ecf20Sopenharmony_ci
3368c2ecf20Sopenharmony_cistatic unsigned int gpmc_ps_to_ticks(unsigned int time_ps)
3378c2ecf20Sopenharmony_ci{
3388c2ecf20Sopenharmony_ci	unsigned long tick_ps;
3398c2ecf20Sopenharmony_ci
3408c2ecf20Sopenharmony_ci	/* Calculate in picosecs to yield more exact results */
3418c2ecf20Sopenharmony_ci	tick_ps = gpmc_get_fclk_period();
3428c2ecf20Sopenharmony_ci
3438c2ecf20Sopenharmony_ci	return (time_ps + tick_ps - 1) / tick_ps;
3448c2ecf20Sopenharmony_ci}
3458c2ecf20Sopenharmony_ci
3468c2ecf20Sopenharmony_cistatic unsigned int gpmc_clk_ticks_to_ns(unsigned int ticks, int cs,
3478c2ecf20Sopenharmony_ci					 enum gpmc_clk_domain cd)
3488c2ecf20Sopenharmony_ci{
3498c2ecf20Sopenharmony_ci	return ticks * gpmc_get_clk_period(cs, cd) / 1000;
3508c2ecf20Sopenharmony_ci}
3518c2ecf20Sopenharmony_ci
3528c2ecf20Sopenharmony_ciunsigned int gpmc_ticks_to_ns(unsigned int ticks)
3538c2ecf20Sopenharmony_ci{
3548c2ecf20Sopenharmony_ci	return gpmc_clk_ticks_to_ns(ticks, /* any CS */ 0, GPMC_CD_FCLK);
3558c2ecf20Sopenharmony_ci}
3568c2ecf20Sopenharmony_ci
3578c2ecf20Sopenharmony_cistatic unsigned int gpmc_ticks_to_ps(unsigned int ticks)
3588c2ecf20Sopenharmony_ci{
3598c2ecf20Sopenharmony_ci	return ticks * gpmc_get_fclk_period();
3608c2ecf20Sopenharmony_ci}
3618c2ecf20Sopenharmony_ci
3628c2ecf20Sopenharmony_cistatic unsigned int gpmc_round_ps_to_ticks(unsigned int time_ps)
3638c2ecf20Sopenharmony_ci{
3648c2ecf20Sopenharmony_ci	unsigned long ticks = gpmc_ps_to_ticks(time_ps);
3658c2ecf20Sopenharmony_ci
3668c2ecf20Sopenharmony_ci	return ticks * gpmc_get_fclk_period();
3678c2ecf20Sopenharmony_ci}
3688c2ecf20Sopenharmony_ci
3698c2ecf20Sopenharmony_cistatic inline void gpmc_cs_modify_reg(int cs, int reg, u32 mask, bool value)
3708c2ecf20Sopenharmony_ci{
3718c2ecf20Sopenharmony_ci	u32 l;
3728c2ecf20Sopenharmony_ci
3738c2ecf20Sopenharmony_ci	l = gpmc_cs_read_reg(cs, reg);
3748c2ecf20Sopenharmony_ci	if (value)
3758c2ecf20Sopenharmony_ci		l |= mask;
3768c2ecf20Sopenharmony_ci	else
3778c2ecf20Sopenharmony_ci		l &= ~mask;
3788c2ecf20Sopenharmony_ci	gpmc_cs_write_reg(cs, reg, l);
3798c2ecf20Sopenharmony_ci}
3808c2ecf20Sopenharmony_ci
3818c2ecf20Sopenharmony_cistatic void gpmc_cs_bool_timings(int cs, const struct gpmc_bool_timings *p)
3828c2ecf20Sopenharmony_ci{
3838c2ecf20Sopenharmony_ci	gpmc_cs_modify_reg(cs, GPMC_CS_CONFIG1,
3848c2ecf20Sopenharmony_ci			   GPMC_CONFIG1_TIME_PARA_GRAN,
3858c2ecf20Sopenharmony_ci			   p->time_para_granularity);
3868c2ecf20Sopenharmony_ci	gpmc_cs_modify_reg(cs, GPMC_CS_CONFIG2,
3878c2ecf20Sopenharmony_ci			   GPMC_CONFIG2_CSEXTRADELAY, p->cs_extra_delay);
3888c2ecf20Sopenharmony_ci	gpmc_cs_modify_reg(cs, GPMC_CS_CONFIG3,
3898c2ecf20Sopenharmony_ci			   GPMC_CONFIG3_ADVEXTRADELAY, p->adv_extra_delay);
3908c2ecf20Sopenharmony_ci	gpmc_cs_modify_reg(cs, GPMC_CS_CONFIG4,
3918c2ecf20Sopenharmony_ci			   GPMC_CONFIG4_OEEXTRADELAY, p->oe_extra_delay);
3928c2ecf20Sopenharmony_ci	gpmc_cs_modify_reg(cs, GPMC_CS_CONFIG4,
3938c2ecf20Sopenharmony_ci			   GPMC_CONFIG4_WEEXTRADELAY, p->we_extra_delay);
3948c2ecf20Sopenharmony_ci	gpmc_cs_modify_reg(cs, GPMC_CS_CONFIG6,
3958c2ecf20Sopenharmony_ci			   GPMC_CONFIG6_CYCLE2CYCLESAMECSEN,
3968c2ecf20Sopenharmony_ci			   p->cycle2cyclesamecsen);
3978c2ecf20Sopenharmony_ci	gpmc_cs_modify_reg(cs, GPMC_CS_CONFIG6,
3988c2ecf20Sopenharmony_ci			   GPMC_CONFIG6_CYCLE2CYCLEDIFFCSEN,
3998c2ecf20Sopenharmony_ci			   p->cycle2cyclediffcsen);
4008c2ecf20Sopenharmony_ci}
4018c2ecf20Sopenharmony_ci
4028c2ecf20Sopenharmony_ci#ifdef CONFIG_OMAP_GPMC_DEBUG
4038c2ecf20Sopenharmony_ci/**
4048c2ecf20Sopenharmony_ci * get_gpmc_timing_reg - read a timing parameter and print DTS settings for it.
4058c2ecf20Sopenharmony_ci * @cs:      Chip Select Region
4068c2ecf20Sopenharmony_ci * @reg:     GPMC_CS_CONFIGn register offset.
4078c2ecf20Sopenharmony_ci * @st_bit:  Start Bit
4088c2ecf20Sopenharmony_ci * @end_bit: End Bit. Must be >= @st_bit.
4098c2ecf20Sopenharmony_ci * @max:     Maximum parameter value (before optional @shift).
4108c2ecf20Sopenharmony_ci *           If 0, maximum is as high as @st_bit and @end_bit allow.
4118c2ecf20Sopenharmony_ci * @name:    DTS node name, w/o "gpmc,"
4128c2ecf20Sopenharmony_ci * @cd:      Clock Domain of timing parameter.
4138c2ecf20Sopenharmony_ci * @shift:   Parameter value left shifts @shift, which is then printed instead of value.
4148c2ecf20Sopenharmony_ci * @raw:     Raw Format Option.
4158c2ecf20Sopenharmony_ci *           raw format:  gpmc,name = <value>
4168c2ecf20Sopenharmony_ci *           tick format: gpmc,name = <value> /&zwj;* x ns -- y ns; x ticks *&zwj;/
4178c2ecf20Sopenharmony_ci *           Where x ns -- y ns result in the same tick value.
4188c2ecf20Sopenharmony_ci *           When @max is exceeded, "invalid" is printed inside comment.
4198c2ecf20Sopenharmony_ci * @noval:   Parameter values equal to 0 are not printed.
4208c2ecf20Sopenharmony_ci * @return:  Specified timing parameter (after optional @shift).
4218c2ecf20Sopenharmony_ci *
4228c2ecf20Sopenharmony_ci */
4238c2ecf20Sopenharmony_cistatic int get_gpmc_timing_reg(
4248c2ecf20Sopenharmony_ci	/* timing specifiers */
4258c2ecf20Sopenharmony_ci	int cs, int reg, int st_bit, int end_bit, int max,
4268c2ecf20Sopenharmony_ci	const char *name, const enum gpmc_clk_domain cd,
4278c2ecf20Sopenharmony_ci	/* value transform */
4288c2ecf20Sopenharmony_ci	int shift,
4298c2ecf20Sopenharmony_ci	/* format specifiers */
4308c2ecf20Sopenharmony_ci	bool raw, bool noval)
4318c2ecf20Sopenharmony_ci{
4328c2ecf20Sopenharmony_ci	u32 l;
4338c2ecf20Sopenharmony_ci	int nr_bits;
4348c2ecf20Sopenharmony_ci	int mask;
4358c2ecf20Sopenharmony_ci	bool invalid;
4368c2ecf20Sopenharmony_ci
4378c2ecf20Sopenharmony_ci	l = gpmc_cs_read_reg(cs, reg);
4388c2ecf20Sopenharmony_ci	nr_bits = end_bit - st_bit + 1;
4398c2ecf20Sopenharmony_ci	mask = (1 << nr_bits) - 1;
4408c2ecf20Sopenharmony_ci	l = (l >> st_bit) & mask;
4418c2ecf20Sopenharmony_ci	if (!max)
4428c2ecf20Sopenharmony_ci		max = mask;
4438c2ecf20Sopenharmony_ci	invalid = l > max;
4448c2ecf20Sopenharmony_ci	if (shift)
4458c2ecf20Sopenharmony_ci		l = (shift << l);
4468c2ecf20Sopenharmony_ci	if (noval && (l == 0))
4478c2ecf20Sopenharmony_ci		return 0;
4488c2ecf20Sopenharmony_ci	if (!raw) {
4498c2ecf20Sopenharmony_ci		/* DTS tick format for timings in ns */
4508c2ecf20Sopenharmony_ci		unsigned int time_ns;
4518c2ecf20Sopenharmony_ci		unsigned int time_ns_min = 0;
4528c2ecf20Sopenharmony_ci
4538c2ecf20Sopenharmony_ci		if (l)
4548c2ecf20Sopenharmony_ci			time_ns_min = gpmc_clk_ticks_to_ns(l - 1, cs, cd) + 1;
4558c2ecf20Sopenharmony_ci		time_ns = gpmc_clk_ticks_to_ns(l, cs, cd);
4568c2ecf20Sopenharmony_ci		pr_info("gpmc,%s = <%u>; /* %u ns - %u ns; %i ticks%s*/\n",
4578c2ecf20Sopenharmony_ci			name, time_ns, time_ns_min, time_ns, l,
4588c2ecf20Sopenharmony_ci			invalid ? "; invalid " : " ");
4598c2ecf20Sopenharmony_ci	} else {
4608c2ecf20Sopenharmony_ci		/* raw format */
4618c2ecf20Sopenharmony_ci		pr_info("gpmc,%s = <%u>;%s\n", name, l,
4628c2ecf20Sopenharmony_ci			invalid ? " /* invalid */" : "");
4638c2ecf20Sopenharmony_ci	}
4648c2ecf20Sopenharmony_ci
4658c2ecf20Sopenharmony_ci	return l;
4668c2ecf20Sopenharmony_ci}
4678c2ecf20Sopenharmony_ci
4688c2ecf20Sopenharmony_ci#define GPMC_PRINT_CONFIG(cs, config) \
4698c2ecf20Sopenharmony_ci	pr_info("cs%i %s: 0x%08x\n", cs, #config, \
4708c2ecf20Sopenharmony_ci		gpmc_cs_read_reg(cs, config))
4718c2ecf20Sopenharmony_ci#define GPMC_GET_RAW(reg, st, end, field) \
4728c2ecf20Sopenharmony_ci	get_gpmc_timing_reg(cs, (reg), (st), (end), 0, field, GPMC_CD_FCLK, 0, 1, 0)
4738c2ecf20Sopenharmony_ci#define GPMC_GET_RAW_MAX(reg, st, end, max, field) \
4748c2ecf20Sopenharmony_ci	get_gpmc_timing_reg(cs, (reg), (st), (end), (max), field, GPMC_CD_FCLK, 0, 1, 0)
4758c2ecf20Sopenharmony_ci#define GPMC_GET_RAW_BOOL(reg, st, end, field) \
4768c2ecf20Sopenharmony_ci	get_gpmc_timing_reg(cs, (reg), (st), (end), 0, field, GPMC_CD_FCLK, 0, 1, 1)
4778c2ecf20Sopenharmony_ci#define GPMC_GET_RAW_SHIFT_MAX(reg, st, end, shift, max, field) \
4788c2ecf20Sopenharmony_ci	get_gpmc_timing_reg(cs, (reg), (st), (end), (max), field, GPMC_CD_FCLK, (shift), 1, 1)
4798c2ecf20Sopenharmony_ci#define GPMC_GET_TICKS(reg, st, end, field) \
4808c2ecf20Sopenharmony_ci	get_gpmc_timing_reg(cs, (reg), (st), (end), 0, field, GPMC_CD_FCLK, 0, 0, 0)
4818c2ecf20Sopenharmony_ci#define GPMC_GET_TICKS_CD(reg, st, end, field, cd) \
4828c2ecf20Sopenharmony_ci	get_gpmc_timing_reg(cs, (reg), (st), (end), 0, field, (cd), 0, 0, 0)
4838c2ecf20Sopenharmony_ci#define GPMC_GET_TICKS_CD_MAX(reg, st, end, max, field, cd) \
4848c2ecf20Sopenharmony_ci	get_gpmc_timing_reg(cs, (reg), (st), (end), (max), field, (cd), 0, 0, 0)
4858c2ecf20Sopenharmony_ci
4868c2ecf20Sopenharmony_cistatic void gpmc_show_regs(int cs, const char *desc)
4878c2ecf20Sopenharmony_ci{
4888c2ecf20Sopenharmony_ci	pr_info("gpmc cs%i %s:\n", cs, desc);
4898c2ecf20Sopenharmony_ci	GPMC_PRINT_CONFIG(cs, GPMC_CS_CONFIG1);
4908c2ecf20Sopenharmony_ci	GPMC_PRINT_CONFIG(cs, GPMC_CS_CONFIG2);
4918c2ecf20Sopenharmony_ci	GPMC_PRINT_CONFIG(cs, GPMC_CS_CONFIG3);
4928c2ecf20Sopenharmony_ci	GPMC_PRINT_CONFIG(cs, GPMC_CS_CONFIG4);
4938c2ecf20Sopenharmony_ci	GPMC_PRINT_CONFIG(cs, GPMC_CS_CONFIG5);
4948c2ecf20Sopenharmony_ci	GPMC_PRINT_CONFIG(cs, GPMC_CS_CONFIG6);
4958c2ecf20Sopenharmony_ci}
4968c2ecf20Sopenharmony_ci
4978c2ecf20Sopenharmony_ci/*
4988c2ecf20Sopenharmony_ci * Note that gpmc,wait-pin handing wrongly assumes bit 8 is available,
4998c2ecf20Sopenharmony_ci * see commit c9fb809.
5008c2ecf20Sopenharmony_ci */
5018c2ecf20Sopenharmony_cistatic void gpmc_cs_show_timings(int cs, const char *desc)
5028c2ecf20Sopenharmony_ci{
5038c2ecf20Sopenharmony_ci	gpmc_show_regs(cs, desc);
5048c2ecf20Sopenharmony_ci
5058c2ecf20Sopenharmony_ci	pr_info("gpmc cs%i access configuration:\n", cs);
5068c2ecf20Sopenharmony_ci	GPMC_GET_RAW_BOOL(GPMC_CS_CONFIG1,  4,  4, "time-para-granularity");
5078c2ecf20Sopenharmony_ci	GPMC_GET_RAW(GPMC_CS_CONFIG1,  8,  9, "mux-add-data");
5088c2ecf20Sopenharmony_ci	GPMC_GET_RAW_SHIFT_MAX(GPMC_CS_CONFIG1, 12, 13, 1,
5098c2ecf20Sopenharmony_ci			       GPMC_CONFIG1_DEVICESIZE_MAX, "device-width");
5108c2ecf20Sopenharmony_ci	GPMC_GET_RAW(GPMC_CS_CONFIG1, 16, 17, "wait-pin");
5118c2ecf20Sopenharmony_ci	GPMC_GET_RAW_BOOL(GPMC_CS_CONFIG1, 21, 21, "wait-on-write");
5128c2ecf20Sopenharmony_ci	GPMC_GET_RAW_BOOL(GPMC_CS_CONFIG1, 22, 22, "wait-on-read");
5138c2ecf20Sopenharmony_ci	GPMC_GET_RAW_SHIFT_MAX(GPMC_CS_CONFIG1, 23, 24, 4,
5148c2ecf20Sopenharmony_ci			       GPMC_CONFIG1_ATTACHEDDEVICEPAGELENGTH_MAX,
5158c2ecf20Sopenharmony_ci			       "burst-length");
5168c2ecf20Sopenharmony_ci	GPMC_GET_RAW_BOOL(GPMC_CS_CONFIG1, 27, 27, "sync-write");
5178c2ecf20Sopenharmony_ci	GPMC_GET_RAW_BOOL(GPMC_CS_CONFIG1, 28, 28, "burst-write");
5188c2ecf20Sopenharmony_ci	GPMC_GET_RAW_BOOL(GPMC_CS_CONFIG1, 29, 29, "gpmc,sync-read");
5198c2ecf20Sopenharmony_ci	GPMC_GET_RAW_BOOL(GPMC_CS_CONFIG1, 30, 30, "burst-read");
5208c2ecf20Sopenharmony_ci	GPMC_GET_RAW_BOOL(GPMC_CS_CONFIG1, 31, 31, "burst-wrap");
5218c2ecf20Sopenharmony_ci
5228c2ecf20Sopenharmony_ci	GPMC_GET_RAW_BOOL(GPMC_CS_CONFIG2,  7,  7, "cs-extra-delay");
5238c2ecf20Sopenharmony_ci
5248c2ecf20Sopenharmony_ci	GPMC_GET_RAW_BOOL(GPMC_CS_CONFIG3,  7,  7, "adv-extra-delay");
5258c2ecf20Sopenharmony_ci
5268c2ecf20Sopenharmony_ci	GPMC_GET_RAW_BOOL(GPMC_CS_CONFIG4, 23, 23, "we-extra-delay");
5278c2ecf20Sopenharmony_ci	GPMC_GET_RAW_BOOL(GPMC_CS_CONFIG4,  7,  7, "oe-extra-delay");
5288c2ecf20Sopenharmony_ci
5298c2ecf20Sopenharmony_ci	GPMC_GET_RAW_BOOL(GPMC_CS_CONFIG6,  7,  7, "cycle2cycle-samecsen");
5308c2ecf20Sopenharmony_ci	GPMC_GET_RAW_BOOL(GPMC_CS_CONFIG6,  6,  6, "cycle2cycle-diffcsen");
5318c2ecf20Sopenharmony_ci
5328c2ecf20Sopenharmony_ci	pr_info("gpmc cs%i timings configuration:\n", cs);
5338c2ecf20Sopenharmony_ci	GPMC_GET_TICKS(GPMC_CS_CONFIG2,  0,  3, "cs-on-ns");
5348c2ecf20Sopenharmony_ci	GPMC_GET_TICKS(GPMC_CS_CONFIG2,  8, 12, "cs-rd-off-ns");
5358c2ecf20Sopenharmony_ci	GPMC_GET_TICKS(GPMC_CS_CONFIG2, 16, 20, "cs-wr-off-ns");
5368c2ecf20Sopenharmony_ci
5378c2ecf20Sopenharmony_ci	GPMC_GET_TICKS(GPMC_CS_CONFIG3,  0,  3, "adv-on-ns");
5388c2ecf20Sopenharmony_ci	GPMC_GET_TICKS(GPMC_CS_CONFIG3,  8, 12, "adv-rd-off-ns");
5398c2ecf20Sopenharmony_ci	GPMC_GET_TICKS(GPMC_CS_CONFIG3, 16, 20, "adv-wr-off-ns");
5408c2ecf20Sopenharmony_ci	if (gpmc_capability & GPMC_HAS_MUX_AAD) {
5418c2ecf20Sopenharmony_ci		GPMC_GET_TICKS(GPMC_CS_CONFIG3, 4, 6, "adv-aad-mux-on-ns");
5428c2ecf20Sopenharmony_ci		GPMC_GET_TICKS(GPMC_CS_CONFIG3, 24, 26,
5438c2ecf20Sopenharmony_ci				"adv-aad-mux-rd-off-ns");
5448c2ecf20Sopenharmony_ci		GPMC_GET_TICKS(GPMC_CS_CONFIG3, 28, 30,
5458c2ecf20Sopenharmony_ci				"adv-aad-mux-wr-off-ns");
5468c2ecf20Sopenharmony_ci	}
5478c2ecf20Sopenharmony_ci
5488c2ecf20Sopenharmony_ci	GPMC_GET_TICKS(GPMC_CS_CONFIG4,  0,  3, "oe-on-ns");
5498c2ecf20Sopenharmony_ci	GPMC_GET_TICKS(GPMC_CS_CONFIG4,  8, 12, "oe-off-ns");
5508c2ecf20Sopenharmony_ci	if (gpmc_capability & GPMC_HAS_MUX_AAD) {
5518c2ecf20Sopenharmony_ci		GPMC_GET_TICKS(GPMC_CS_CONFIG4,  4,  6, "oe-aad-mux-on-ns");
5528c2ecf20Sopenharmony_ci		GPMC_GET_TICKS(GPMC_CS_CONFIG4, 13, 15, "oe-aad-mux-off-ns");
5538c2ecf20Sopenharmony_ci	}
5548c2ecf20Sopenharmony_ci	GPMC_GET_TICKS(GPMC_CS_CONFIG4, 16, 19, "we-on-ns");
5558c2ecf20Sopenharmony_ci	GPMC_GET_TICKS(GPMC_CS_CONFIG4, 24, 28, "we-off-ns");
5568c2ecf20Sopenharmony_ci
5578c2ecf20Sopenharmony_ci	GPMC_GET_TICKS(GPMC_CS_CONFIG5,  0,  4, "rd-cycle-ns");
5588c2ecf20Sopenharmony_ci	GPMC_GET_TICKS(GPMC_CS_CONFIG5,  8, 12, "wr-cycle-ns");
5598c2ecf20Sopenharmony_ci	GPMC_GET_TICKS(GPMC_CS_CONFIG5, 16, 20, "access-ns");
5608c2ecf20Sopenharmony_ci
5618c2ecf20Sopenharmony_ci	GPMC_GET_TICKS(GPMC_CS_CONFIG5, 24, 27, "page-burst-access-ns");
5628c2ecf20Sopenharmony_ci
5638c2ecf20Sopenharmony_ci	GPMC_GET_TICKS(GPMC_CS_CONFIG6, 0, 3, "bus-turnaround-ns");
5648c2ecf20Sopenharmony_ci	GPMC_GET_TICKS(GPMC_CS_CONFIG6, 8, 11, "cycle2cycle-delay-ns");
5658c2ecf20Sopenharmony_ci
5668c2ecf20Sopenharmony_ci	GPMC_GET_TICKS_CD_MAX(GPMC_CS_CONFIG1, 18, 19,
5678c2ecf20Sopenharmony_ci			      GPMC_CONFIG1_WAITMONITORINGTIME_MAX,
5688c2ecf20Sopenharmony_ci			      "wait-monitoring-ns", GPMC_CD_CLK);
5698c2ecf20Sopenharmony_ci	GPMC_GET_TICKS_CD_MAX(GPMC_CS_CONFIG1, 25, 26,
5708c2ecf20Sopenharmony_ci			      GPMC_CONFIG1_CLKACTIVATIONTIME_MAX,
5718c2ecf20Sopenharmony_ci			      "clk-activation-ns", GPMC_CD_FCLK);
5728c2ecf20Sopenharmony_ci
5738c2ecf20Sopenharmony_ci	GPMC_GET_TICKS(GPMC_CS_CONFIG6, 16, 19, "wr-data-mux-bus-ns");
5748c2ecf20Sopenharmony_ci	GPMC_GET_TICKS(GPMC_CS_CONFIG6, 24, 28, "wr-access-ns");
5758c2ecf20Sopenharmony_ci}
5768c2ecf20Sopenharmony_ci#else
5778c2ecf20Sopenharmony_cistatic inline void gpmc_cs_show_timings(int cs, const char *desc)
5788c2ecf20Sopenharmony_ci{
5798c2ecf20Sopenharmony_ci}
5808c2ecf20Sopenharmony_ci#endif
5818c2ecf20Sopenharmony_ci
5828c2ecf20Sopenharmony_ci/**
5838c2ecf20Sopenharmony_ci * set_gpmc_timing_reg - set a single timing parameter for Chip Select Region.
5848c2ecf20Sopenharmony_ci * Caller is expected to have initialized CONFIG1 GPMCFCLKDIVIDER
5858c2ecf20Sopenharmony_ci * prior to calling this function with @cd equal to GPMC_CD_CLK.
5868c2ecf20Sopenharmony_ci *
5878c2ecf20Sopenharmony_ci * @cs:      Chip Select Region.
5888c2ecf20Sopenharmony_ci * @reg:     GPMC_CS_CONFIGn register offset.
5898c2ecf20Sopenharmony_ci * @st_bit:  Start Bit
5908c2ecf20Sopenharmony_ci * @end_bit: End Bit. Must be >= @st_bit.
5918c2ecf20Sopenharmony_ci * @max:     Maximum parameter value.
5928c2ecf20Sopenharmony_ci *           If 0, maximum is as high as @st_bit and @end_bit allow.
5938c2ecf20Sopenharmony_ci * @time:    Timing parameter in ns.
5948c2ecf20Sopenharmony_ci * @cd:      Timing parameter clock domain.
5958c2ecf20Sopenharmony_ci * @name:    Timing parameter name.
5968c2ecf20Sopenharmony_ci * @return:  0 on success, -1 on error.
5978c2ecf20Sopenharmony_ci */
5988c2ecf20Sopenharmony_cistatic int set_gpmc_timing_reg(int cs, int reg, int st_bit, int end_bit, int max,
5998c2ecf20Sopenharmony_ci			       int time, enum gpmc_clk_domain cd, const char *name)
6008c2ecf20Sopenharmony_ci{
6018c2ecf20Sopenharmony_ci	u32 l;
6028c2ecf20Sopenharmony_ci	int ticks, mask, nr_bits;
6038c2ecf20Sopenharmony_ci
6048c2ecf20Sopenharmony_ci	if (time == 0)
6058c2ecf20Sopenharmony_ci		ticks = 0;
6068c2ecf20Sopenharmony_ci	else
6078c2ecf20Sopenharmony_ci		ticks = gpmc_ns_to_clk_ticks(time, cs, cd);
6088c2ecf20Sopenharmony_ci	nr_bits = end_bit - st_bit + 1;
6098c2ecf20Sopenharmony_ci	mask = (1 << nr_bits) - 1;
6108c2ecf20Sopenharmony_ci
6118c2ecf20Sopenharmony_ci	if (!max)
6128c2ecf20Sopenharmony_ci		max = mask;
6138c2ecf20Sopenharmony_ci
6148c2ecf20Sopenharmony_ci	if (ticks > max) {
6158c2ecf20Sopenharmony_ci		pr_err("%s: GPMC CS%d: %s %d ns, %d ticks > %d ticks\n",
6168c2ecf20Sopenharmony_ci		       __func__, cs, name, time, ticks, max);
6178c2ecf20Sopenharmony_ci
6188c2ecf20Sopenharmony_ci		return -1;
6198c2ecf20Sopenharmony_ci	}
6208c2ecf20Sopenharmony_ci
6218c2ecf20Sopenharmony_ci	l = gpmc_cs_read_reg(cs, reg);
6228c2ecf20Sopenharmony_ci#ifdef CONFIG_OMAP_GPMC_DEBUG
6238c2ecf20Sopenharmony_ci	pr_info("GPMC CS%d: %-17s: %3d ticks, %3lu ns (was %3i ticks) %3d ns\n",
6248c2ecf20Sopenharmony_ci		cs, name, ticks, gpmc_get_clk_period(cs, cd) * ticks / 1000,
6258c2ecf20Sopenharmony_ci			(l >> st_bit) & mask, time);
6268c2ecf20Sopenharmony_ci#endif
6278c2ecf20Sopenharmony_ci	l &= ~(mask << st_bit);
6288c2ecf20Sopenharmony_ci	l |= ticks << st_bit;
6298c2ecf20Sopenharmony_ci	gpmc_cs_write_reg(cs, reg, l);
6308c2ecf20Sopenharmony_ci
6318c2ecf20Sopenharmony_ci	return 0;
6328c2ecf20Sopenharmony_ci}
6338c2ecf20Sopenharmony_ci
6348c2ecf20Sopenharmony_ci/**
6358c2ecf20Sopenharmony_ci * gpmc_calc_waitmonitoring_divider - calculate proper GPMCFCLKDIVIDER based on WAITMONITORINGTIME
6368c2ecf20Sopenharmony_ci * WAITMONITORINGTIME will be _at least_ as long as desired, i.e.
6378c2ecf20Sopenharmony_ci * read  --> don't sample bus too early
6388c2ecf20Sopenharmony_ci * write --> data is longer on bus
6398c2ecf20Sopenharmony_ci *
6408c2ecf20Sopenharmony_ci * Formula:
6418c2ecf20Sopenharmony_ci * gpmc_clk_div + 1 = ceil(ceil(waitmonitoringtime_ns / gpmc_fclk_ns)
6428c2ecf20Sopenharmony_ci *                    / waitmonitoring_ticks)
6438c2ecf20Sopenharmony_ci * WAITMONITORINGTIME resulting in 0 or 1 tick with div = 1 are caught by
6448c2ecf20Sopenharmony_ci * div <= 0 check.
6458c2ecf20Sopenharmony_ci *
6468c2ecf20Sopenharmony_ci * @wait_monitoring: WAITMONITORINGTIME in ns.
6478c2ecf20Sopenharmony_ci * @return:          -1 on failure to scale, else proper divider > 0.
6488c2ecf20Sopenharmony_ci */
6498c2ecf20Sopenharmony_cistatic int gpmc_calc_waitmonitoring_divider(unsigned int wait_monitoring)
6508c2ecf20Sopenharmony_ci{
6518c2ecf20Sopenharmony_ci	int div = gpmc_ns_to_ticks(wait_monitoring);
6528c2ecf20Sopenharmony_ci
6538c2ecf20Sopenharmony_ci	div += GPMC_CONFIG1_WAITMONITORINGTIME_MAX - 1;
6548c2ecf20Sopenharmony_ci	div /= GPMC_CONFIG1_WAITMONITORINGTIME_MAX;
6558c2ecf20Sopenharmony_ci
6568c2ecf20Sopenharmony_ci	if (div > 4)
6578c2ecf20Sopenharmony_ci		return -1;
6588c2ecf20Sopenharmony_ci	if (div <= 0)
6598c2ecf20Sopenharmony_ci		div = 1;
6608c2ecf20Sopenharmony_ci
6618c2ecf20Sopenharmony_ci	return div;
6628c2ecf20Sopenharmony_ci}
6638c2ecf20Sopenharmony_ci
6648c2ecf20Sopenharmony_ci/**
6658c2ecf20Sopenharmony_ci * gpmc_calc_divider - calculate GPMC_FCLK divider for sync_clk GPMC_CLK period.
6668c2ecf20Sopenharmony_ci * @sync_clk: GPMC_CLK period in ps.
6678c2ecf20Sopenharmony_ci * @return:   Returns at least 1 if GPMC_FCLK can be divided to GPMC_CLK.
6688c2ecf20Sopenharmony_ci *            Else, returns -1.
6698c2ecf20Sopenharmony_ci */
6708c2ecf20Sopenharmony_ciint gpmc_calc_divider(unsigned int sync_clk)
6718c2ecf20Sopenharmony_ci{
6728c2ecf20Sopenharmony_ci	int div = gpmc_ps_to_ticks(sync_clk);
6738c2ecf20Sopenharmony_ci
6748c2ecf20Sopenharmony_ci	if (div > 4)
6758c2ecf20Sopenharmony_ci		return -1;
6768c2ecf20Sopenharmony_ci	if (div <= 0)
6778c2ecf20Sopenharmony_ci		div = 1;
6788c2ecf20Sopenharmony_ci
6798c2ecf20Sopenharmony_ci	return div;
6808c2ecf20Sopenharmony_ci}
6818c2ecf20Sopenharmony_ci
6828c2ecf20Sopenharmony_ci/**
6838c2ecf20Sopenharmony_ci * gpmc_cs_set_timings - program timing parameters for Chip Select Region.
6848c2ecf20Sopenharmony_ci * @cs:     Chip Select Region.
6858c2ecf20Sopenharmony_ci * @t:      GPMC timing parameters.
6868c2ecf20Sopenharmony_ci * @s:      GPMC timing settings.
6878c2ecf20Sopenharmony_ci * @return: 0 on success, -1 on error.
6888c2ecf20Sopenharmony_ci */
6898c2ecf20Sopenharmony_ciint gpmc_cs_set_timings(int cs, const struct gpmc_timings *t,
6908c2ecf20Sopenharmony_ci			const struct gpmc_settings *s)
6918c2ecf20Sopenharmony_ci{
6928c2ecf20Sopenharmony_ci	int div, ret;
6938c2ecf20Sopenharmony_ci	u32 l;
6948c2ecf20Sopenharmony_ci
6958c2ecf20Sopenharmony_ci	div = gpmc_calc_divider(t->sync_clk);
6968c2ecf20Sopenharmony_ci	if (div < 0)
6978c2ecf20Sopenharmony_ci		return -EINVAL;
6988c2ecf20Sopenharmony_ci
6998c2ecf20Sopenharmony_ci	/*
7008c2ecf20Sopenharmony_ci	 * See if we need to change the divider for waitmonitoringtime.
7018c2ecf20Sopenharmony_ci	 *
7028c2ecf20Sopenharmony_ci	 * Calculate GPMCFCLKDIVIDER independent of gpmc,sync-clk-ps in DT for
7038c2ecf20Sopenharmony_ci	 * pure asynchronous accesses, i.e. both read and write asynchronous.
7048c2ecf20Sopenharmony_ci	 * However, only do so if WAITMONITORINGTIME is actually used, i.e.
7058c2ecf20Sopenharmony_ci	 * either WAITREADMONITORING or WAITWRITEMONITORING is set.
7068c2ecf20Sopenharmony_ci	 *
7078c2ecf20Sopenharmony_ci	 * This statement must not change div to scale async WAITMONITORINGTIME
7088c2ecf20Sopenharmony_ci	 * to protect mixed synchronous and asynchronous accesses.
7098c2ecf20Sopenharmony_ci	 *
7108c2ecf20Sopenharmony_ci	 * We raise an error later if WAITMONITORINGTIME does not fit.
7118c2ecf20Sopenharmony_ci	 */
7128c2ecf20Sopenharmony_ci	if (!s->sync_read && !s->sync_write &&
7138c2ecf20Sopenharmony_ci	    (s->wait_on_read || s->wait_on_write)
7148c2ecf20Sopenharmony_ci	   ) {
7158c2ecf20Sopenharmony_ci		div = gpmc_calc_waitmonitoring_divider(t->wait_monitoring);
7168c2ecf20Sopenharmony_ci		if (div < 0) {
7178c2ecf20Sopenharmony_ci			pr_err("%s: waitmonitoringtime %3d ns too large for greatest gpmcfclkdivider.\n",
7188c2ecf20Sopenharmony_ci			       __func__,
7198c2ecf20Sopenharmony_ci			       t->wait_monitoring
7208c2ecf20Sopenharmony_ci			       );
7218c2ecf20Sopenharmony_ci			return -ENXIO;
7228c2ecf20Sopenharmony_ci		}
7238c2ecf20Sopenharmony_ci	}
7248c2ecf20Sopenharmony_ci
7258c2ecf20Sopenharmony_ci	ret = 0;
7268c2ecf20Sopenharmony_ci	ret |= set_gpmc_timing_reg(cs, GPMC_CS_CONFIG2, 0, 3, 0, t->cs_on,
7278c2ecf20Sopenharmony_ci				   GPMC_CD_FCLK, "cs_on");
7288c2ecf20Sopenharmony_ci	ret |= set_gpmc_timing_reg(cs, GPMC_CS_CONFIG2, 8, 12, 0, t->cs_rd_off,
7298c2ecf20Sopenharmony_ci				   GPMC_CD_FCLK, "cs_rd_off");
7308c2ecf20Sopenharmony_ci	ret |= set_gpmc_timing_reg(cs, GPMC_CS_CONFIG2, 16, 20, 0, t->cs_wr_off,
7318c2ecf20Sopenharmony_ci				   GPMC_CD_FCLK, "cs_wr_off");
7328c2ecf20Sopenharmony_ci	if (ret)
7338c2ecf20Sopenharmony_ci		return -ENXIO;
7348c2ecf20Sopenharmony_ci
7358c2ecf20Sopenharmony_ci	ret |= set_gpmc_timing_reg(cs, GPMC_CS_CONFIG3, 0, 3, 0, t->adv_on,
7368c2ecf20Sopenharmony_ci				   GPMC_CD_FCLK, "adv_on");
7378c2ecf20Sopenharmony_ci	ret |= set_gpmc_timing_reg(cs, GPMC_CS_CONFIG3, 8, 12, 0, t->adv_rd_off,
7388c2ecf20Sopenharmony_ci				   GPMC_CD_FCLK, "adv_rd_off");
7398c2ecf20Sopenharmony_ci	ret |= set_gpmc_timing_reg(cs, GPMC_CS_CONFIG3, 16, 20, 0, t->adv_wr_off,
7408c2ecf20Sopenharmony_ci				   GPMC_CD_FCLK, "adv_wr_off");
7418c2ecf20Sopenharmony_ci	if (ret)
7428c2ecf20Sopenharmony_ci		return -ENXIO;
7438c2ecf20Sopenharmony_ci
7448c2ecf20Sopenharmony_ci	if (gpmc_capability & GPMC_HAS_MUX_AAD) {
7458c2ecf20Sopenharmony_ci		ret |= set_gpmc_timing_reg(cs, GPMC_CS_CONFIG3, 4, 6, 0,
7468c2ecf20Sopenharmony_ci					   t->adv_aad_mux_on, GPMC_CD_FCLK,
7478c2ecf20Sopenharmony_ci					   "adv_aad_mux_on");
7488c2ecf20Sopenharmony_ci		ret |= set_gpmc_timing_reg(cs, GPMC_CS_CONFIG3, 24, 26, 0,
7498c2ecf20Sopenharmony_ci					   t->adv_aad_mux_rd_off, GPMC_CD_FCLK,
7508c2ecf20Sopenharmony_ci					   "adv_aad_mux_rd_off");
7518c2ecf20Sopenharmony_ci		ret |= set_gpmc_timing_reg(cs, GPMC_CS_CONFIG3, 28, 30, 0,
7528c2ecf20Sopenharmony_ci					   t->adv_aad_mux_wr_off, GPMC_CD_FCLK,
7538c2ecf20Sopenharmony_ci					   "adv_aad_mux_wr_off");
7548c2ecf20Sopenharmony_ci		if (ret)
7558c2ecf20Sopenharmony_ci			return -ENXIO;
7568c2ecf20Sopenharmony_ci	}
7578c2ecf20Sopenharmony_ci
7588c2ecf20Sopenharmony_ci	ret |= set_gpmc_timing_reg(cs, GPMC_CS_CONFIG4, 0, 3, 0, t->oe_on,
7598c2ecf20Sopenharmony_ci				   GPMC_CD_FCLK, "oe_on");
7608c2ecf20Sopenharmony_ci	ret |= set_gpmc_timing_reg(cs, GPMC_CS_CONFIG4, 8, 12, 0, t->oe_off,
7618c2ecf20Sopenharmony_ci				   GPMC_CD_FCLK, "oe_off");
7628c2ecf20Sopenharmony_ci	if (gpmc_capability & GPMC_HAS_MUX_AAD) {
7638c2ecf20Sopenharmony_ci		ret |= set_gpmc_timing_reg(cs, GPMC_CS_CONFIG4, 4, 6, 0,
7648c2ecf20Sopenharmony_ci					   t->oe_aad_mux_on, GPMC_CD_FCLK,
7658c2ecf20Sopenharmony_ci					   "oe_aad_mux_on");
7668c2ecf20Sopenharmony_ci		ret |= set_gpmc_timing_reg(cs, GPMC_CS_CONFIG4, 13, 15, 0,
7678c2ecf20Sopenharmony_ci					   t->oe_aad_mux_off, GPMC_CD_FCLK,
7688c2ecf20Sopenharmony_ci					   "oe_aad_mux_off");
7698c2ecf20Sopenharmony_ci	}
7708c2ecf20Sopenharmony_ci	ret |= set_gpmc_timing_reg(cs, GPMC_CS_CONFIG4, 16, 19, 0, t->we_on,
7718c2ecf20Sopenharmony_ci				   GPMC_CD_FCLK, "we_on");
7728c2ecf20Sopenharmony_ci	ret |= set_gpmc_timing_reg(cs, GPMC_CS_CONFIG4, 24, 28, 0, t->we_off,
7738c2ecf20Sopenharmony_ci				   GPMC_CD_FCLK, "we_off");
7748c2ecf20Sopenharmony_ci	if (ret)
7758c2ecf20Sopenharmony_ci		return -ENXIO;
7768c2ecf20Sopenharmony_ci
7778c2ecf20Sopenharmony_ci	ret |= set_gpmc_timing_reg(cs, GPMC_CS_CONFIG5, 0, 4, 0, t->rd_cycle,
7788c2ecf20Sopenharmony_ci				   GPMC_CD_FCLK, "rd_cycle");
7798c2ecf20Sopenharmony_ci	ret |= set_gpmc_timing_reg(cs, GPMC_CS_CONFIG5, 8, 12, 0, t->wr_cycle,
7808c2ecf20Sopenharmony_ci				   GPMC_CD_FCLK, "wr_cycle");
7818c2ecf20Sopenharmony_ci	ret |= set_gpmc_timing_reg(cs, GPMC_CS_CONFIG5, 16, 20, 0, t->access,
7828c2ecf20Sopenharmony_ci				   GPMC_CD_FCLK, "access");
7838c2ecf20Sopenharmony_ci	ret |= set_gpmc_timing_reg(cs, GPMC_CS_CONFIG5, 24, 27, 0,
7848c2ecf20Sopenharmony_ci				   t->page_burst_access, GPMC_CD_FCLK,
7858c2ecf20Sopenharmony_ci				   "page_burst_access");
7868c2ecf20Sopenharmony_ci	if (ret)
7878c2ecf20Sopenharmony_ci		return -ENXIO;
7888c2ecf20Sopenharmony_ci
7898c2ecf20Sopenharmony_ci	ret |= set_gpmc_timing_reg(cs, GPMC_CS_CONFIG6, 0, 3, 0,
7908c2ecf20Sopenharmony_ci				   t->bus_turnaround, GPMC_CD_FCLK,
7918c2ecf20Sopenharmony_ci				   "bus_turnaround");
7928c2ecf20Sopenharmony_ci	ret |= set_gpmc_timing_reg(cs, GPMC_CS_CONFIG6, 8, 11, 0,
7938c2ecf20Sopenharmony_ci				   t->cycle2cycle_delay, GPMC_CD_FCLK,
7948c2ecf20Sopenharmony_ci				   "cycle2cycle_delay");
7958c2ecf20Sopenharmony_ci	if (ret)
7968c2ecf20Sopenharmony_ci		return -ENXIO;
7978c2ecf20Sopenharmony_ci
7988c2ecf20Sopenharmony_ci	if (gpmc_capability & GPMC_HAS_WR_DATA_MUX_BUS) {
7998c2ecf20Sopenharmony_ci		ret |= set_gpmc_timing_reg(cs, GPMC_CS_CONFIG6, 16, 19, 0,
8008c2ecf20Sopenharmony_ci					   t->wr_data_mux_bus, GPMC_CD_FCLK,
8018c2ecf20Sopenharmony_ci					   "wr_data_mux_bus");
8028c2ecf20Sopenharmony_ci		if (ret)
8038c2ecf20Sopenharmony_ci			return -ENXIO;
8048c2ecf20Sopenharmony_ci	}
8058c2ecf20Sopenharmony_ci	if (gpmc_capability & GPMC_HAS_WR_ACCESS) {
8068c2ecf20Sopenharmony_ci		ret |= set_gpmc_timing_reg(cs, GPMC_CS_CONFIG6, 24, 28, 0,
8078c2ecf20Sopenharmony_ci					   t->wr_access, GPMC_CD_FCLK,
8088c2ecf20Sopenharmony_ci					   "wr_access");
8098c2ecf20Sopenharmony_ci		if (ret)
8108c2ecf20Sopenharmony_ci			return -ENXIO;
8118c2ecf20Sopenharmony_ci	}
8128c2ecf20Sopenharmony_ci
8138c2ecf20Sopenharmony_ci	l = gpmc_cs_read_reg(cs, GPMC_CS_CONFIG1);
8148c2ecf20Sopenharmony_ci	l &= ~0x03;
8158c2ecf20Sopenharmony_ci	l |= (div - 1);
8168c2ecf20Sopenharmony_ci	gpmc_cs_write_reg(cs, GPMC_CS_CONFIG1, l);
8178c2ecf20Sopenharmony_ci
8188c2ecf20Sopenharmony_ci	ret = 0;
8198c2ecf20Sopenharmony_ci	ret |= set_gpmc_timing_reg(cs, GPMC_CS_CONFIG1, 18, 19,
8208c2ecf20Sopenharmony_ci				   GPMC_CONFIG1_WAITMONITORINGTIME_MAX,
8218c2ecf20Sopenharmony_ci				   t->wait_monitoring, GPMC_CD_CLK,
8228c2ecf20Sopenharmony_ci				   "wait_monitoring");
8238c2ecf20Sopenharmony_ci	ret |= set_gpmc_timing_reg(cs, GPMC_CS_CONFIG1, 25, 26,
8248c2ecf20Sopenharmony_ci				   GPMC_CONFIG1_CLKACTIVATIONTIME_MAX,
8258c2ecf20Sopenharmony_ci				   t->clk_activation, GPMC_CD_FCLK,
8268c2ecf20Sopenharmony_ci				   "clk_activation");
8278c2ecf20Sopenharmony_ci	if (ret)
8288c2ecf20Sopenharmony_ci		return -ENXIO;
8298c2ecf20Sopenharmony_ci
8308c2ecf20Sopenharmony_ci#ifdef CONFIG_OMAP_GPMC_DEBUG
8318c2ecf20Sopenharmony_ci	pr_info("GPMC CS%d CLK period is %lu ns (div %d)\n",
8328c2ecf20Sopenharmony_ci			cs, (div * gpmc_get_fclk_period()) / 1000, div);
8338c2ecf20Sopenharmony_ci#endif
8348c2ecf20Sopenharmony_ci
8358c2ecf20Sopenharmony_ci	gpmc_cs_bool_timings(cs, &t->bool_timings);
8368c2ecf20Sopenharmony_ci	gpmc_cs_show_timings(cs, "after gpmc_cs_set_timings");
8378c2ecf20Sopenharmony_ci
8388c2ecf20Sopenharmony_ci	return 0;
8398c2ecf20Sopenharmony_ci}
8408c2ecf20Sopenharmony_ci
8418c2ecf20Sopenharmony_cistatic int gpmc_cs_set_memconf(int cs, u32 base, u32 size)
8428c2ecf20Sopenharmony_ci{
8438c2ecf20Sopenharmony_ci	u32 l;
8448c2ecf20Sopenharmony_ci	u32 mask;
8458c2ecf20Sopenharmony_ci
8468c2ecf20Sopenharmony_ci	/*
8478c2ecf20Sopenharmony_ci	 * Ensure that base address is aligned on a
8488c2ecf20Sopenharmony_ci	 * boundary equal to or greater than size.
8498c2ecf20Sopenharmony_ci	 */
8508c2ecf20Sopenharmony_ci	if (base & (size - 1))
8518c2ecf20Sopenharmony_ci		return -EINVAL;
8528c2ecf20Sopenharmony_ci
8538c2ecf20Sopenharmony_ci	base >>= GPMC_CHUNK_SHIFT;
8548c2ecf20Sopenharmony_ci	mask = (1 << GPMC_SECTION_SHIFT) - size;
8558c2ecf20Sopenharmony_ci	mask >>= GPMC_CHUNK_SHIFT;
8568c2ecf20Sopenharmony_ci	mask <<= GPMC_CONFIG7_MASKADDRESS_OFFSET;
8578c2ecf20Sopenharmony_ci
8588c2ecf20Sopenharmony_ci	l = gpmc_cs_read_reg(cs, GPMC_CS_CONFIG7);
8598c2ecf20Sopenharmony_ci	l &= ~GPMC_CONFIG7_MASK;
8608c2ecf20Sopenharmony_ci	l |= base & GPMC_CONFIG7_BASEADDRESS_MASK;
8618c2ecf20Sopenharmony_ci	l |= mask & GPMC_CONFIG7_MASKADDRESS_MASK;
8628c2ecf20Sopenharmony_ci	l |= GPMC_CONFIG7_CSVALID;
8638c2ecf20Sopenharmony_ci	gpmc_cs_write_reg(cs, GPMC_CS_CONFIG7, l);
8648c2ecf20Sopenharmony_ci
8658c2ecf20Sopenharmony_ci	return 0;
8668c2ecf20Sopenharmony_ci}
8678c2ecf20Sopenharmony_ci
8688c2ecf20Sopenharmony_cistatic void gpmc_cs_enable_mem(int cs)
8698c2ecf20Sopenharmony_ci{
8708c2ecf20Sopenharmony_ci	u32 l;
8718c2ecf20Sopenharmony_ci
8728c2ecf20Sopenharmony_ci	l = gpmc_cs_read_reg(cs, GPMC_CS_CONFIG7);
8738c2ecf20Sopenharmony_ci	l |= GPMC_CONFIG7_CSVALID;
8748c2ecf20Sopenharmony_ci	gpmc_cs_write_reg(cs, GPMC_CS_CONFIG7, l);
8758c2ecf20Sopenharmony_ci}
8768c2ecf20Sopenharmony_ci
8778c2ecf20Sopenharmony_cistatic void gpmc_cs_disable_mem(int cs)
8788c2ecf20Sopenharmony_ci{
8798c2ecf20Sopenharmony_ci	u32 l;
8808c2ecf20Sopenharmony_ci
8818c2ecf20Sopenharmony_ci	l = gpmc_cs_read_reg(cs, GPMC_CS_CONFIG7);
8828c2ecf20Sopenharmony_ci	l &= ~GPMC_CONFIG7_CSVALID;
8838c2ecf20Sopenharmony_ci	gpmc_cs_write_reg(cs, GPMC_CS_CONFIG7, l);
8848c2ecf20Sopenharmony_ci}
8858c2ecf20Sopenharmony_ci
8868c2ecf20Sopenharmony_cistatic void gpmc_cs_get_memconf(int cs, u32 *base, u32 *size)
8878c2ecf20Sopenharmony_ci{
8888c2ecf20Sopenharmony_ci	u32 l;
8898c2ecf20Sopenharmony_ci	u32 mask;
8908c2ecf20Sopenharmony_ci
8918c2ecf20Sopenharmony_ci	l = gpmc_cs_read_reg(cs, GPMC_CS_CONFIG7);
8928c2ecf20Sopenharmony_ci	*base = (l & 0x3f) << GPMC_CHUNK_SHIFT;
8938c2ecf20Sopenharmony_ci	mask = (l >> 8) & 0x0f;
8948c2ecf20Sopenharmony_ci	*size = (1 << GPMC_SECTION_SHIFT) - (mask << GPMC_CHUNK_SHIFT);
8958c2ecf20Sopenharmony_ci}
8968c2ecf20Sopenharmony_ci
8978c2ecf20Sopenharmony_cistatic int gpmc_cs_mem_enabled(int cs)
8988c2ecf20Sopenharmony_ci{
8998c2ecf20Sopenharmony_ci	u32 l;
9008c2ecf20Sopenharmony_ci
9018c2ecf20Sopenharmony_ci	l = gpmc_cs_read_reg(cs, GPMC_CS_CONFIG7);
9028c2ecf20Sopenharmony_ci	return l & GPMC_CONFIG7_CSVALID;
9038c2ecf20Sopenharmony_ci}
9048c2ecf20Sopenharmony_ci
9058c2ecf20Sopenharmony_cistatic void gpmc_cs_set_reserved(int cs, int reserved)
9068c2ecf20Sopenharmony_ci{
9078c2ecf20Sopenharmony_ci	struct gpmc_cs_data *gpmc = &gpmc_cs[cs];
9088c2ecf20Sopenharmony_ci
9098c2ecf20Sopenharmony_ci	gpmc->flags |= GPMC_CS_RESERVED;
9108c2ecf20Sopenharmony_ci}
9118c2ecf20Sopenharmony_ci
9128c2ecf20Sopenharmony_cistatic bool gpmc_cs_reserved(int cs)
9138c2ecf20Sopenharmony_ci{
9148c2ecf20Sopenharmony_ci	struct gpmc_cs_data *gpmc = &gpmc_cs[cs];
9158c2ecf20Sopenharmony_ci
9168c2ecf20Sopenharmony_ci	return gpmc->flags & GPMC_CS_RESERVED;
9178c2ecf20Sopenharmony_ci}
9188c2ecf20Sopenharmony_ci
9198c2ecf20Sopenharmony_cistatic unsigned long gpmc_mem_align(unsigned long size)
9208c2ecf20Sopenharmony_ci{
9218c2ecf20Sopenharmony_ci	int order;
9228c2ecf20Sopenharmony_ci
9238c2ecf20Sopenharmony_ci	size = (size - 1) >> (GPMC_CHUNK_SHIFT - 1);
9248c2ecf20Sopenharmony_ci	order = GPMC_CHUNK_SHIFT - 1;
9258c2ecf20Sopenharmony_ci	do {
9268c2ecf20Sopenharmony_ci		size >>= 1;
9278c2ecf20Sopenharmony_ci		order++;
9288c2ecf20Sopenharmony_ci	} while (size);
9298c2ecf20Sopenharmony_ci	size = 1 << order;
9308c2ecf20Sopenharmony_ci	return size;
9318c2ecf20Sopenharmony_ci}
9328c2ecf20Sopenharmony_ci
9338c2ecf20Sopenharmony_cistatic int gpmc_cs_insert_mem(int cs, unsigned long base, unsigned long size)
9348c2ecf20Sopenharmony_ci{
9358c2ecf20Sopenharmony_ci	struct gpmc_cs_data *gpmc = &gpmc_cs[cs];
9368c2ecf20Sopenharmony_ci	struct resource *res = &gpmc->mem;
9378c2ecf20Sopenharmony_ci	int r;
9388c2ecf20Sopenharmony_ci
9398c2ecf20Sopenharmony_ci	size = gpmc_mem_align(size);
9408c2ecf20Sopenharmony_ci	spin_lock(&gpmc_mem_lock);
9418c2ecf20Sopenharmony_ci	res->start = base;
9428c2ecf20Sopenharmony_ci	res->end = base + size - 1;
9438c2ecf20Sopenharmony_ci	r = request_resource(&gpmc_mem_root, res);
9448c2ecf20Sopenharmony_ci	spin_unlock(&gpmc_mem_lock);
9458c2ecf20Sopenharmony_ci
9468c2ecf20Sopenharmony_ci	return r;
9478c2ecf20Sopenharmony_ci}
9488c2ecf20Sopenharmony_ci
9498c2ecf20Sopenharmony_cistatic int gpmc_cs_delete_mem(int cs)
9508c2ecf20Sopenharmony_ci{
9518c2ecf20Sopenharmony_ci	struct gpmc_cs_data *gpmc = &gpmc_cs[cs];
9528c2ecf20Sopenharmony_ci	struct resource *res = &gpmc->mem;
9538c2ecf20Sopenharmony_ci	int r;
9548c2ecf20Sopenharmony_ci
9558c2ecf20Sopenharmony_ci	spin_lock(&gpmc_mem_lock);
9568c2ecf20Sopenharmony_ci	r = release_resource(res);
9578c2ecf20Sopenharmony_ci	res->start = 0;
9588c2ecf20Sopenharmony_ci	res->end = 0;
9598c2ecf20Sopenharmony_ci	spin_unlock(&gpmc_mem_lock);
9608c2ecf20Sopenharmony_ci
9618c2ecf20Sopenharmony_ci	return r;
9628c2ecf20Sopenharmony_ci}
9638c2ecf20Sopenharmony_ci
9648c2ecf20Sopenharmony_ciint gpmc_cs_request(int cs, unsigned long size, unsigned long *base)
9658c2ecf20Sopenharmony_ci{
9668c2ecf20Sopenharmony_ci	struct gpmc_cs_data *gpmc = &gpmc_cs[cs];
9678c2ecf20Sopenharmony_ci	struct resource *res = &gpmc->mem;
9688c2ecf20Sopenharmony_ci	int r = -1;
9698c2ecf20Sopenharmony_ci
9708c2ecf20Sopenharmony_ci	if (cs >= gpmc_cs_num) {
9718c2ecf20Sopenharmony_ci		pr_err("%s: requested chip-select is disabled\n", __func__);
9728c2ecf20Sopenharmony_ci		return -ENODEV;
9738c2ecf20Sopenharmony_ci	}
9748c2ecf20Sopenharmony_ci	size = gpmc_mem_align(size);
9758c2ecf20Sopenharmony_ci	if (size > (1 << GPMC_SECTION_SHIFT))
9768c2ecf20Sopenharmony_ci		return -ENOMEM;
9778c2ecf20Sopenharmony_ci
9788c2ecf20Sopenharmony_ci	spin_lock(&gpmc_mem_lock);
9798c2ecf20Sopenharmony_ci	if (gpmc_cs_reserved(cs)) {
9808c2ecf20Sopenharmony_ci		r = -EBUSY;
9818c2ecf20Sopenharmony_ci		goto out;
9828c2ecf20Sopenharmony_ci	}
9838c2ecf20Sopenharmony_ci	if (gpmc_cs_mem_enabled(cs))
9848c2ecf20Sopenharmony_ci		r = adjust_resource(res, res->start & ~(size - 1), size);
9858c2ecf20Sopenharmony_ci	if (r < 0)
9868c2ecf20Sopenharmony_ci		r = allocate_resource(&gpmc_mem_root, res, size, 0, ~0,
9878c2ecf20Sopenharmony_ci				      size, NULL, NULL);
9888c2ecf20Sopenharmony_ci	if (r < 0)
9898c2ecf20Sopenharmony_ci		goto out;
9908c2ecf20Sopenharmony_ci
9918c2ecf20Sopenharmony_ci	/* Disable CS while changing base address and size mask */
9928c2ecf20Sopenharmony_ci	gpmc_cs_disable_mem(cs);
9938c2ecf20Sopenharmony_ci
9948c2ecf20Sopenharmony_ci	r = gpmc_cs_set_memconf(cs, res->start, resource_size(res));
9958c2ecf20Sopenharmony_ci	if (r < 0) {
9968c2ecf20Sopenharmony_ci		release_resource(res);
9978c2ecf20Sopenharmony_ci		goto out;
9988c2ecf20Sopenharmony_ci	}
9998c2ecf20Sopenharmony_ci
10008c2ecf20Sopenharmony_ci	/* Enable CS */
10018c2ecf20Sopenharmony_ci	gpmc_cs_enable_mem(cs);
10028c2ecf20Sopenharmony_ci	*base = res->start;
10038c2ecf20Sopenharmony_ci	gpmc_cs_set_reserved(cs, 1);
10048c2ecf20Sopenharmony_ciout:
10058c2ecf20Sopenharmony_ci	spin_unlock(&gpmc_mem_lock);
10068c2ecf20Sopenharmony_ci	return r;
10078c2ecf20Sopenharmony_ci}
10088c2ecf20Sopenharmony_ciEXPORT_SYMBOL(gpmc_cs_request);
10098c2ecf20Sopenharmony_ci
10108c2ecf20Sopenharmony_civoid gpmc_cs_free(int cs)
10118c2ecf20Sopenharmony_ci{
10128c2ecf20Sopenharmony_ci	struct gpmc_cs_data *gpmc;
10138c2ecf20Sopenharmony_ci	struct resource *res;
10148c2ecf20Sopenharmony_ci
10158c2ecf20Sopenharmony_ci	spin_lock(&gpmc_mem_lock);
10168c2ecf20Sopenharmony_ci	if (cs >= gpmc_cs_num || cs < 0 || !gpmc_cs_reserved(cs)) {
10178c2ecf20Sopenharmony_ci		WARN(1, "Trying to free non-reserved GPMC CS%d\n", cs);
10188c2ecf20Sopenharmony_ci		spin_unlock(&gpmc_mem_lock);
10198c2ecf20Sopenharmony_ci		return;
10208c2ecf20Sopenharmony_ci	}
10218c2ecf20Sopenharmony_ci	gpmc = &gpmc_cs[cs];
10228c2ecf20Sopenharmony_ci	res = &gpmc->mem;
10238c2ecf20Sopenharmony_ci
10248c2ecf20Sopenharmony_ci	gpmc_cs_disable_mem(cs);
10258c2ecf20Sopenharmony_ci	if (res->flags)
10268c2ecf20Sopenharmony_ci		release_resource(res);
10278c2ecf20Sopenharmony_ci	gpmc_cs_set_reserved(cs, 0);
10288c2ecf20Sopenharmony_ci	spin_unlock(&gpmc_mem_lock);
10298c2ecf20Sopenharmony_ci}
10308c2ecf20Sopenharmony_ciEXPORT_SYMBOL(gpmc_cs_free);
10318c2ecf20Sopenharmony_ci
10328c2ecf20Sopenharmony_ci/**
10338c2ecf20Sopenharmony_ci * gpmc_configure - write request to configure gpmc
10348c2ecf20Sopenharmony_ci * @cmd: command type
10358c2ecf20Sopenharmony_ci * @wval: value to write
10368c2ecf20Sopenharmony_ci * @return status of the operation
10378c2ecf20Sopenharmony_ci */
10388c2ecf20Sopenharmony_ciint gpmc_configure(int cmd, int wval)
10398c2ecf20Sopenharmony_ci{
10408c2ecf20Sopenharmony_ci	u32 regval;
10418c2ecf20Sopenharmony_ci
10428c2ecf20Sopenharmony_ci	switch (cmd) {
10438c2ecf20Sopenharmony_ci	case GPMC_CONFIG_WP:
10448c2ecf20Sopenharmony_ci		regval = gpmc_read_reg(GPMC_CONFIG);
10458c2ecf20Sopenharmony_ci		if (wval)
10468c2ecf20Sopenharmony_ci			regval &= ~GPMC_CONFIG_WRITEPROTECT; /* WP is ON */
10478c2ecf20Sopenharmony_ci		else
10488c2ecf20Sopenharmony_ci			regval |= GPMC_CONFIG_WRITEPROTECT;  /* WP is OFF */
10498c2ecf20Sopenharmony_ci		gpmc_write_reg(GPMC_CONFIG, regval);
10508c2ecf20Sopenharmony_ci		break;
10518c2ecf20Sopenharmony_ci
10528c2ecf20Sopenharmony_ci	default:
10538c2ecf20Sopenharmony_ci		pr_err("%s: command not supported\n", __func__);
10548c2ecf20Sopenharmony_ci		return -EINVAL;
10558c2ecf20Sopenharmony_ci	}
10568c2ecf20Sopenharmony_ci
10578c2ecf20Sopenharmony_ci	return 0;
10588c2ecf20Sopenharmony_ci}
10598c2ecf20Sopenharmony_ciEXPORT_SYMBOL(gpmc_configure);
10608c2ecf20Sopenharmony_ci
10618c2ecf20Sopenharmony_cistatic bool gpmc_nand_writebuffer_empty(void)
10628c2ecf20Sopenharmony_ci{
10638c2ecf20Sopenharmony_ci	if (gpmc_read_reg(GPMC_STATUS) & GPMC_STATUS_EMPTYWRITEBUFFERSTATUS)
10648c2ecf20Sopenharmony_ci		return true;
10658c2ecf20Sopenharmony_ci
10668c2ecf20Sopenharmony_ci	return false;
10678c2ecf20Sopenharmony_ci}
10688c2ecf20Sopenharmony_ci
10698c2ecf20Sopenharmony_cistatic struct gpmc_nand_ops nand_ops = {
10708c2ecf20Sopenharmony_ci	.nand_writebuffer_empty = gpmc_nand_writebuffer_empty,
10718c2ecf20Sopenharmony_ci};
10728c2ecf20Sopenharmony_ci
10738c2ecf20Sopenharmony_ci/**
10748c2ecf20Sopenharmony_ci * gpmc_omap_get_nand_ops - Get the GPMC NAND interface
10758c2ecf20Sopenharmony_ci * @reg: the GPMC NAND register map exclusive for NAND use.
10768c2ecf20Sopenharmony_ci * @cs: GPMC chip select number on which the NAND sits. The
10778c2ecf20Sopenharmony_ci *      register map returned will be specific to this chip select.
10788c2ecf20Sopenharmony_ci *
10798c2ecf20Sopenharmony_ci * Returns NULL on error e.g. invalid cs.
10808c2ecf20Sopenharmony_ci */
10818c2ecf20Sopenharmony_cistruct gpmc_nand_ops *gpmc_omap_get_nand_ops(struct gpmc_nand_regs *reg, int cs)
10828c2ecf20Sopenharmony_ci{
10838c2ecf20Sopenharmony_ci	int i;
10848c2ecf20Sopenharmony_ci
10858c2ecf20Sopenharmony_ci	if (cs >= gpmc_cs_num)
10868c2ecf20Sopenharmony_ci		return NULL;
10878c2ecf20Sopenharmony_ci
10888c2ecf20Sopenharmony_ci	reg->gpmc_nand_command = gpmc_base + GPMC_CS0_OFFSET +
10898c2ecf20Sopenharmony_ci				GPMC_CS_NAND_COMMAND + GPMC_CS_SIZE * cs;
10908c2ecf20Sopenharmony_ci	reg->gpmc_nand_address = gpmc_base + GPMC_CS0_OFFSET +
10918c2ecf20Sopenharmony_ci				GPMC_CS_NAND_ADDRESS + GPMC_CS_SIZE * cs;
10928c2ecf20Sopenharmony_ci	reg->gpmc_nand_data = gpmc_base + GPMC_CS0_OFFSET +
10938c2ecf20Sopenharmony_ci				GPMC_CS_NAND_DATA + GPMC_CS_SIZE * cs;
10948c2ecf20Sopenharmony_ci	reg->gpmc_prefetch_config1 = gpmc_base + GPMC_PREFETCH_CONFIG1;
10958c2ecf20Sopenharmony_ci	reg->gpmc_prefetch_config2 = gpmc_base + GPMC_PREFETCH_CONFIG2;
10968c2ecf20Sopenharmony_ci	reg->gpmc_prefetch_control = gpmc_base + GPMC_PREFETCH_CONTROL;
10978c2ecf20Sopenharmony_ci	reg->gpmc_prefetch_status = gpmc_base + GPMC_PREFETCH_STATUS;
10988c2ecf20Sopenharmony_ci	reg->gpmc_ecc_config = gpmc_base + GPMC_ECC_CONFIG;
10998c2ecf20Sopenharmony_ci	reg->gpmc_ecc_control = gpmc_base + GPMC_ECC_CONTROL;
11008c2ecf20Sopenharmony_ci	reg->gpmc_ecc_size_config = gpmc_base + GPMC_ECC_SIZE_CONFIG;
11018c2ecf20Sopenharmony_ci	reg->gpmc_ecc1_result = gpmc_base + GPMC_ECC1_RESULT;
11028c2ecf20Sopenharmony_ci
11038c2ecf20Sopenharmony_ci	for (i = 0; i < GPMC_BCH_NUM_REMAINDER; i++) {
11048c2ecf20Sopenharmony_ci		reg->gpmc_bch_result0[i] = gpmc_base + GPMC_ECC_BCH_RESULT_0 +
11058c2ecf20Sopenharmony_ci					   GPMC_BCH_SIZE * i;
11068c2ecf20Sopenharmony_ci		reg->gpmc_bch_result1[i] = gpmc_base + GPMC_ECC_BCH_RESULT_1 +
11078c2ecf20Sopenharmony_ci					   GPMC_BCH_SIZE * i;
11088c2ecf20Sopenharmony_ci		reg->gpmc_bch_result2[i] = gpmc_base + GPMC_ECC_BCH_RESULT_2 +
11098c2ecf20Sopenharmony_ci					   GPMC_BCH_SIZE * i;
11108c2ecf20Sopenharmony_ci		reg->gpmc_bch_result3[i] = gpmc_base + GPMC_ECC_BCH_RESULT_3 +
11118c2ecf20Sopenharmony_ci					   GPMC_BCH_SIZE * i;
11128c2ecf20Sopenharmony_ci		reg->gpmc_bch_result4[i] = gpmc_base + GPMC_ECC_BCH_RESULT_4 +
11138c2ecf20Sopenharmony_ci					   i * GPMC_BCH_SIZE;
11148c2ecf20Sopenharmony_ci		reg->gpmc_bch_result5[i] = gpmc_base + GPMC_ECC_BCH_RESULT_5 +
11158c2ecf20Sopenharmony_ci					   i * GPMC_BCH_SIZE;
11168c2ecf20Sopenharmony_ci		reg->gpmc_bch_result6[i] = gpmc_base + GPMC_ECC_BCH_RESULT_6 +
11178c2ecf20Sopenharmony_ci					   i * GPMC_BCH_SIZE;
11188c2ecf20Sopenharmony_ci	}
11198c2ecf20Sopenharmony_ci
11208c2ecf20Sopenharmony_ci	return &nand_ops;
11218c2ecf20Sopenharmony_ci}
11228c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(gpmc_omap_get_nand_ops);
11238c2ecf20Sopenharmony_ci
11248c2ecf20Sopenharmony_cistatic void gpmc_omap_onenand_calc_sync_timings(struct gpmc_timings *t,
11258c2ecf20Sopenharmony_ci						struct gpmc_settings *s,
11268c2ecf20Sopenharmony_ci						int freq, int latency)
11278c2ecf20Sopenharmony_ci{
11288c2ecf20Sopenharmony_ci	struct gpmc_device_timings dev_t;
11298c2ecf20Sopenharmony_ci	const int t_cer  = 15;
11308c2ecf20Sopenharmony_ci	const int t_avdp = 12;
11318c2ecf20Sopenharmony_ci	const int t_cez  = 20; /* max of t_cez, t_oez */
11328c2ecf20Sopenharmony_ci	const int t_wpl  = 40;
11338c2ecf20Sopenharmony_ci	const int t_wph  = 30;
11348c2ecf20Sopenharmony_ci	int min_gpmc_clk_period, t_ces, t_avds, t_avdh, t_ach, t_aavdh, t_rdyo;
11358c2ecf20Sopenharmony_ci
11368c2ecf20Sopenharmony_ci	switch (freq) {
11378c2ecf20Sopenharmony_ci	case 104:
11388c2ecf20Sopenharmony_ci		min_gpmc_clk_period = 9600; /* 104 MHz */
11398c2ecf20Sopenharmony_ci		t_ces   = 3;
11408c2ecf20Sopenharmony_ci		t_avds  = 4;
11418c2ecf20Sopenharmony_ci		t_avdh  = 2;
11428c2ecf20Sopenharmony_ci		t_ach   = 3;
11438c2ecf20Sopenharmony_ci		t_aavdh = 6;
11448c2ecf20Sopenharmony_ci		t_rdyo  = 6;
11458c2ecf20Sopenharmony_ci		break;
11468c2ecf20Sopenharmony_ci	case 83:
11478c2ecf20Sopenharmony_ci		min_gpmc_clk_period = 12000; /* 83 MHz */
11488c2ecf20Sopenharmony_ci		t_ces   = 5;
11498c2ecf20Sopenharmony_ci		t_avds  = 4;
11508c2ecf20Sopenharmony_ci		t_avdh  = 2;
11518c2ecf20Sopenharmony_ci		t_ach   = 6;
11528c2ecf20Sopenharmony_ci		t_aavdh = 6;
11538c2ecf20Sopenharmony_ci		t_rdyo  = 9;
11548c2ecf20Sopenharmony_ci		break;
11558c2ecf20Sopenharmony_ci	case 66:
11568c2ecf20Sopenharmony_ci		min_gpmc_clk_period = 15000; /* 66 MHz */
11578c2ecf20Sopenharmony_ci		t_ces   = 6;
11588c2ecf20Sopenharmony_ci		t_avds  = 5;
11598c2ecf20Sopenharmony_ci		t_avdh  = 2;
11608c2ecf20Sopenharmony_ci		t_ach   = 6;
11618c2ecf20Sopenharmony_ci		t_aavdh = 6;
11628c2ecf20Sopenharmony_ci		t_rdyo  = 11;
11638c2ecf20Sopenharmony_ci		break;
11648c2ecf20Sopenharmony_ci	default:
11658c2ecf20Sopenharmony_ci		min_gpmc_clk_period = 18500; /* 54 MHz */
11668c2ecf20Sopenharmony_ci		t_ces   = 7;
11678c2ecf20Sopenharmony_ci		t_avds  = 7;
11688c2ecf20Sopenharmony_ci		t_avdh  = 7;
11698c2ecf20Sopenharmony_ci		t_ach   = 9;
11708c2ecf20Sopenharmony_ci		t_aavdh = 7;
11718c2ecf20Sopenharmony_ci		t_rdyo  = 15;
11728c2ecf20Sopenharmony_ci		break;
11738c2ecf20Sopenharmony_ci	}
11748c2ecf20Sopenharmony_ci
11758c2ecf20Sopenharmony_ci	/* Set synchronous read timings */
11768c2ecf20Sopenharmony_ci	memset(&dev_t, 0, sizeof(dev_t));
11778c2ecf20Sopenharmony_ci
11788c2ecf20Sopenharmony_ci	if (!s->sync_write) {
11798c2ecf20Sopenharmony_ci		dev_t.t_avdp_w = max(t_avdp, t_cer) * 1000;
11808c2ecf20Sopenharmony_ci		dev_t.t_wpl = t_wpl * 1000;
11818c2ecf20Sopenharmony_ci		dev_t.t_wph = t_wph * 1000;
11828c2ecf20Sopenharmony_ci		dev_t.t_aavdh = t_aavdh * 1000;
11838c2ecf20Sopenharmony_ci	}
11848c2ecf20Sopenharmony_ci	dev_t.ce_xdelay = true;
11858c2ecf20Sopenharmony_ci	dev_t.avd_xdelay = true;
11868c2ecf20Sopenharmony_ci	dev_t.oe_xdelay = true;
11878c2ecf20Sopenharmony_ci	dev_t.we_xdelay = true;
11888c2ecf20Sopenharmony_ci	dev_t.clk = min_gpmc_clk_period;
11898c2ecf20Sopenharmony_ci	dev_t.t_bacc = dev_t.clk;
11908c2ecf20Sopenharmony_ci	dev_t.t_ces = t_ces * 1000;
11918c2ecf20Sopenharmony_ci	dev_t.t_avds = t_avds * 1000;
11928c2ecf20Sopenharmony_ci	dev_t.t_avdh = t_avdh * 1000;
11938c2ecf20Sopenharmony_ci	dev_t.t_ach = t_ach * 1000;
11948c2ecf20Sopenharmony_ci	dev_t.cyc_iaa = (latency + 1);
11958c2ecf20Sopenharmony_ci	dev_t.t_cez_r = t_cez * 1000;
11968c2ecf20Sopenharmony_ci	dev_t.t_cez_w = dev_t.t_cez_r;
11978c2ecf20Sopenharmony_ci	dev_t.cyc_aavdh_oe = 1;
11988c2ecf20Sopenharmony_ci	dev_t.t_rdyo = t_rdyo * 1000 + min_gpmc_clk_period;
11998c2ecf20Sopenharmony_ci
12008c2ecf20Sopenharmony_ci	gpmc_calc_timings(t, s, &dev_t);
12018c2ecf20Sopenharmony_ci}
12028c2ecf20Sopenharmony_ci
12038c2ecf20Sopenharmony_ciint gpmc_omap_onenand_set_timings(struct device *dev, int cs, int freq,
12048c2ecf20Sopenharmony_ci				  int latency,
12058c2ecf20Sopenharmony_ci				  struct gpmc_onenand_info *info)
12068c2ecf20Sopenharmony_ci{
12078c2ecf20Sopenharmony_ci	int ret;
12088c2ecf20Sopenharmony_ci	struct gpmc_timings gpmc_t;
12098c2ecf20Sopenharmony_ci	struct gpmc_settings gpmc_s;
12108c2ecf20Sopenharmony_ci
12118c2ecf20Sopenharmony_ci	gpmc_read_settings_dt(dev->of_node, &gpmc_s);
12128c2ecf20Sopenharmony_ci
12138c2ecf20Sopenharmony_ci	info->sync_read = gpmc_s.sync_read;
12148c2ecf20Sopenharmony_ci	info->sync_write = gpmc_s.sync_write;
12158c2ecf20Sopenharmony_ci	info->burst_len = gpmc_s.burst_len;
12168c2ecf20Sopenharmony_ci
12178c2ecf20Sopenharmony_ci	if (!gpmc_s.sync_read && !gpmc_s.sync_write)
12188c2ecf20Sopenharmony_ci		return 0;
12198c2ecf20Sopenharmony_ci
12208c2ecf20Sopenharmony_ci	gpmc_omap_onenand_calc_sync_timings(&gpmc_t, &gpmc_s, freq, latency);
12218c2ecf20Sopenharmony_ci
12228c2ecf20Sopenharmony_ci	ret = gpmc_cs_program_settings(cs, &gpmc_s);
12238c2ecf20Sopenharmony_ci	if (ret < 0)
12248c2ecf20Sopenharmony_ci		return ret;
12258c2ecf20Sopenharmony_ci
12268c2ecf20Sopenharmony_ci	return gpmc_cs_set_timings(cs, &gpmc_t, &gpmc_s);
12278c2ecf20Sopenharmony_ci}
12288c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(gpmc_omap_onenand_set_timings);
12298c2ecf20Sopenharmony_ci
12308c2ecf20Sopenharmony_ciint gpmc_get_client_irq(unsigned int irq_config)
12318c2ecf20Sopenharmony_ci{
12328c2ecf20Sopenharmony_ci	if (!gpmc_irq_domain) {
12338c2ecf20Sopenharmony_ci		pr_warn("%s called before GPMC IRQ domain available\n",
12348c2ecf20Sopenharmony_ci			__func__);
12358c2ecf20Sopenharmony_ci		return 0;
12368c2ecf20Sopenharmony_ci	}
12378c2ecf20Sopenharmony_ci
12388c2ecf20Sopenharmony_ci	/* we restrict this to NAND IRQs only */
12398c2ecf20Sopenharmony_ci	if (irq_config >= GPMC_NR_NAND_IRQS)
12408c2ecf20Sopenharmony_ci		return 0;
12418c2ecf20Sopenharmony_ci
12428c2ecf20Sopenharmony_ci	return irq_create_mapping(gpmc_irq_domain, irq_config);
12438c2ecf20Sopenharmony_ci}
12448c2ecf20Sopenharmony_ci
12458c2ecf20Sopenharmony_cistatic int gpmc_irq_endis(unsigned long hwirq, bool endis)
12468c2ecf20Sopenharmony_ci{
12478c2ecf20Sopenharmony_ci	u32 regval;
12488c2ecf20Sopenharmony_ci
12498c2ecf20Sopenharmony_ci	/* bits GPMC_NR_NAND_IRQS to 8 are reserved */
12508c2ecf20Sopenharmony_ci	if (hwirq >= GPMC_NR_NAND_IRQS)
12518c2ecf20Sopenharmony_ci		hwirq += 8 - GPMC_NR_NAND_IRQS;
12528c2ecf20Sopenharmony_ci
12538c2ecf20Sopenharmony_ci	regval = gpmc_read_reg(GPMC_IRQENABLE);
12548c2ecf20Sopenharmony_ci	if (endis)
12558c2ecf20Sopenharmony_ci		regval |= BIT(hwirq);
12568c2ecf20Sopenharmony_ci	else
12578c2ecf20Sopenharmony_ci		regval &= ~BIT(hwirq);
12588c2ecf20Sopenharmony_ci	gpmc_write_reg(GPMC_IRQENABLE, regval);
12598c2ecf20Sopenharmony_ci
12608c2ecf20Sopenharmony_ci	return 0;
12618c2ecf20Sopenharmony_ci}
12628c2ecf20Sopenharmony_ci
12638c2ecf20Sopenharmony_cistatic void gpmc_irq_disable(struct irq_data *p)
12648c2ecf20Sopenharmony_ci{
12658c2ecf20Sopenharmony_ci	gpmc_irq_endis(p->hwirq, false);
12668c2ecf20Sopenharmony_ci}
12678c2ecf20Sopenharmony_ci
12688c2ecf20Sopenharmony_cistatic void gpmc_irq_enable(struct irq_data *p)
12698c2ecf20Sopenharmony_ci{
12708c2ecf20Sopenharmony_ci	gpmc_irq_endis(p->hwirq, true);
12718c2ecf20Sopenharmony_ci}
12728c2ecf20Sopenharmony_ci
12738c2ecf20Sopenharmony_cistatic void gpmc_irq_mask(struct irq_data *d)
12748c2ecf20Sopenharmony_ci{
12758c2ecf20Sopenharmony_ci	gpmc_irq_endis(d->hwirq, false);
12768c2ecf20Sopenharmony_ci}
12778c2ecf20Sopenharmony_ci
12788c2ecf20Sopenharmony_cistatic void gpmc_irq_unmask(struct irq_data *d)
12798c2ecf20Sopenharmony_ci{
12808c2ecf20Sopenharmony_ci	gpmc_irq_endis(d->hwirq, true);
12818c2ecf20Sopenharmony_ci}
12828c2ecf20Sopenharmony_ci
12838c2ecf20Sopenharmony_cistatic void gpmc_irq_edge_config(unsigned long hwirq, bool rising_edge)
12848c2ecf20Sopenharmony_ci{
12858c2ecf20Sopenharmony_ci	u32 regval;
12868c2ecf20Sopenharmony_ci
12878c2ecf20Sopenharmony_ci	/* NAND IRQs polarity is not configurable */
12888c2ecf20Sopenharmony_ci	if (hwirq < GPMC_NR_NAND_IRQS)
12898c2ecf20Sopenharmony_ci		return;
12908c2ecf20Sopenharmony_ci
12918c2ecf20Sopenharmony_ci	/* WAITPIN starts at BIT 8 */
12928c2ecf20Sopenharmony_ci	hwirq += 8 - GPMC_NR_NAND_IRQS;
12938c2ecf20Sopenharmony_ci
12948c2ecf20Sopenharmony_ci	regval = gpmc_read_reg(GPMC_CONFIG);
12958c2ecf20Sopenharmony_ci	if (rising_edge)
12968c2ecf20Sopenharmony_ci		regval &= ~BIT(hwirq);
12978c2ecf20Sopenharmony_ci	else
12988c2ecf20Sopenharmony_ci		regval |= BIT(hwirq);
12998c2ecf20Sopenharmony_ci
13008c2ecf20Sopenharmony_ci	gpmc_write_reg(GPMC_CONFIG, regval);
13018c2ecf20Sopenharmony_ci}
13028c2ecf20Sopenharmony_ci
13038c2ecf20Sopenharmony_cistatic void gpmc_irq_ack(struct irq_data *d)
13048c2ecf20Sopenharmony_ci{
13058c2ecf20Sopenharmony_ci	unsigned int hwirq = d->hwirq;
13068c2ecf20Sopenharmony_ci
13078c2ecf20Sopenharmony_ci	/* skip reserved bits */
13088c2ecf20Sopenharmony_ci	if (hwirq >= GPMC_NR_NAND_IRQS)
13098c2ecf20Sopenharmony_ci		hwirq += 8 - GPMC_NR_NAND_IRQS;
13108c2ecf20Sopenharmony_ci
13118c2ecf20Sopenharmony_ci	/* Setting bit to 1 clears (or Acks) the interrupt */
13128c2ecf20Sopenharmony_ci	gpmc_write_reg(GPMC_IRQSTATUS, BIT(hwirq));
13138c2ecf20Sopenharmony_ci}
13148c2ecf20Sopenharmony_ci
13158c2ecf20Sopenharmony_cistatic int gpmc_irq_set_type(struct irq_data *d, unsigned int trigger)
13168c2ecf20Sopenharmony_ci{
13178c2ecf20Sopenharmony_ci	/* can't set type for NAND IRQs */
13188c2ecf20Sopenharmony_ci	if (d->hwirq < GPMC_NR_NAND_IRQS)
13198c2ecf20Sopenharmony_ci		return -EINVAL;
13208c2ecf20Sopenharmony_ci
13218c2ecf20Sopenharmony_ci	/* We can support either rising or falling edge at a time */
13228c2ecf20Sopenharmony_ci	if (trigger == IRQ_TYPE_EDGE_FALLING)
13238c2ecf20Sopenharmony_ci		gpmc_irq_edge_config(d->hwirq, false);
13248c2ecf20Sopenharmony_ci	else if (trigger == IRQ_TYPE_EDGE_RISING)
13258c2ecf20Sopenharmony_ci		gpmc_irq_edge_config(d->hwirq, true);
13268c2ecf20Sopenharmony_ci	else
13278c2ecf20Sopenharmony_ci		return -EINVAL;
13288c2ecf20Sopenharmony_ci
13298c2ecf20Sopenharmony_ci	return 0;
13308c2ecf20Sopenharmony_ci}
13318c2ecf20Sopenharmony_ci
13328c2ecf20Sopenharmony_cistatic int gpmc_irq_map(struct irq_domain *d, unsigned int virq,
13338c2ecf20Sopenharmony_ci			irq_hw_number_t hw)
13348c2ecf20Sopenharmony_ci{
13358c2ecf20Sopenharmony_ci	struct gpmc_device *gpmc = d->host_data;
13368c2ecf20Sopenharmony_ci
13378c2ecf20Sopenharmony_ci	irq_set_chip_data(virq, gpmc);
13388c2ecf20Sopenharmony_ci	if (hw < GPMC_NR_NAND_IRQS) {
13398c2ecf20Sopenharmony_ci		irq_modify_status(virq, IRQ_NOREQUEST, IRQ_NOAUTOEN);
13408c2ecf20Sopenharmony_ci		irq_set_chip_and_handler(virq, &gpmc->irq_chip,
13418c2ecf20Sopenharmony_ci					 handle_simple_irq);
13428c2ecf20Sopenharmony_ci	} else {
13438c2ecf20Sopenharmony_ci		irq_set_chip_and_handler(virq, &gpmc->irq_chip,
13448c2ecf20Sopenharmony_ci					 handle_edge_irq);
13458c2ecf20Sopenharmony_ci	}
13468c2ecf20Sopenharmony_ci
13478c2ecf20Sopenharmony_ci	return 0;
13488c2ecf20Sopenharmony_ci}
13498c2ecf20Sopenharmony_ci
13508c2ecf20Sopenharmony_cistatic const struct irq_domain_ops gpmc_irq_domain_ops = {
13518c2ecf20Sopenharmony_ci	.map    = gpmc_irq_map,
13528c2ecf20Sopenharmony_ci	.xlate  = irq_domain_xlate_twocell,
13538c2ecf20Sopenharmony_ci};
13548c2ecf20Sopenharmony_ci
13558c2ecf20Sopenharmony_cistatic irqreturn_t gpmc_handle_irq(int irq, void *data)
13568c2ecf20Sopenharmony_ci{
13578c2ecf20Sopenharmony_ci	int hwirq, virq;
13588c2ecf20Sopenharmony_ci	u32 regval, regvalx;
13598c2ecf20Sopenharmony_ci	struct gpmc_device *gpmc = data;
13608c2ecf20Sopenharmony_ci
13618c2ecf20Sopenharmony_ci	regval = gpmc_read_reg(GPMC_IRQSTATUS);
13628c2ecf20Sopenharmony_ci	regvalx = regval;
13638c2ecf20Sopenharmony_ci
13648c2ecf20Sopenharmony_ci	if (!regval)
13658c2ecf20Sopenharmony_ci		return IRQ_NONE;
13668c2ecf20Sopenharmony_ci
13678c2ecf20Sopenharmony_ci	for (hwirq = 0; hwirq < gpmc->nirqs; hwirq++) {
13688c2ecf20Sopenharmony_ci		/* skip reserved status bits */
13698c2ecf20Sopenharmony_ci		if (hwirq == GPMC_NR_NAND_IRQS)
13708c2ecf20Sopenharmony_ci			regvalx >>= 8 - GPMC_NR_NAND_IRQS;
13718c2ecf20Sopenharmony_ci
13728c2ecf20Sopenharmony_ci		if (regvalx & BIT(hwirq)) {
13738c2ecf20Sopenharmony_ci			virq = irq_find_mapping(gpmc_irq_domain, hwirq);
13748c2ecf20Sopenharmony_ci			if (!virq) {
13758c2ecf20Sopenharmony_ci				dev_warn(gpmc->dev,
13768c2ecf20Sopenharmony_ci					 "spurious irq detected hwirq %d, virq %d\n",
13778c2ecf20Sopenharmony_ci					 hwirq, virq);
13788c2ecf20Sopenharmony_ci			}
13798c2ecf20Sopenharmony_ci
13808c2ecf20Sopenharmony_ci			generic_handle_irq(virq);
13818c2ecf20Sopenharmony_ci		}
13828c2ecf20Sopenharmony_ci	}
13838c2ecf20Sopenharmony_ci
13848c2ecf20Sopenharmony_ci	gpmc_write_reg(GPMC_IRQSTATUS, regval);
13858c2ecf20Sopenharmony_ci
13868c2ecf20Sopenharmony_ci	return IRQ_HANDLED;
13878c2ecf20Sopenharmony_ci}
13888c2ecf20Sopenharmony_ci
13898c2ecf20Sopenharmony_cistatic int gpmc_setup_irq(struct gpmc_device *gpmc)
13908c2ecf20Sopenharmony_ci{
13918c2ecf20Sopenharmony_ci	u32 regval;
13928c2ecf20Sopenharmony_ci	int rc;
13938c2ecf20Sopenharmony_ci
13948c2ecf20Sopenharmony_ci	/* Disable interrupts */
13958c2ecf20Sopenharmony_ci	gpmc_write_reg(GPMC_IRQENABLE, 0);
13968c2ecf20Sopenharmony_ci
13978c2ecf20Sopenharmony_ci	/* clear interrupts */
13988c2ecf20Sopenharmony_ci	regval = gpmc_read_reg(GPMC_IRQSTATUS);
13998c2ecf20Sopenharmony_ci	gpmc_write_reg(GPMC_IRQSTATUS, regval);
14008c2ecf20Sopenharmony_ci
14018c2ecf20Sopenharmony_ci	gpmc->irq_chip.name = "gpmc";
14028c2ecf20Sopenharmony_ci	gpmc->irq_chip.irq_enable = gpmc_irq_enable;
14038c2ecf20Sopenharmony_ci	gpmc->irq_chip.irq_disable = gpmc_irq_disable;
14048c2ecf20Sopenharmony_ci	gpmc->irq_chip.irq_ack = gpmc_irq_ack;
14058c2ecf20Sopenharmony_ci	gpmc->irq_chip.irq_mask = gpmc_irq_mask;
14068c2ecf20Sopenharmony_ci	gpmc->irq_chip.irq_unmask = gpmc_irq_unmask;
14078c2ecf20Sopenharmony_ci	gpmc->irq_chip.irq_set_type = gpmc_irq_set_type;
14088c2ecf20Sopenharmony_ci
14098c2ecf20Sopenharmony_ci	gpmc_irq_domain = irq_domain_add_linear(gpmc->dev->of_node,
14108c2ecf20Sopenharmony_ci						gpmc->nirqs,
14118c2ecf20Sopenharmony_ci						&gpmc_irq_domain_ops,
14128c2ecf20Sopenharmony_ci						gpmc);
14138c2ecf20Sopenharmony_ci	if (!gpmc_irq_domain) {
14148c2ecf20Sopenharmony_ci		dev_err(gpmc->dev, "IRQ domain add failed\n");
14158c2ecf20Sopenharmony_ci		return -ENODEV;
14168c2ecf20Sopenharmony_ci	}
14178c2ecf20Sopenharmony_ci
14188c2ecf20Sopenharmony_ci	rc = request_irq(gpmc->irq, gpmc_handle_irq, 0, "gpmc", gpmc);
14198c2ecf20Sopenharmony_ci	if (rc) {
14208c2ecf20Sopenharmony_ci		dev_err(gpmc->dev, "failed to request irq %d: %d\n",
14218c2ecf20Sopenharmony_ci			gpmc->irq, rc);
14228c2ecf20Sopenharmony_ci		irq_domain_remove(gpmc_irq_domain);
14238c2ecf20Sopenharmony_ci		gpmc_irq_domain = NULL;
14248c2ecf20Sopenharmony_ci	}
14258c2ecf20Sopenharmony_ci
14268c2ecf20Sopenharmony_ci	return rc;
14278c2ecf20Sopenharmony_ci}
14288c2ecf20Sopenharmony_ci
14298c2ecf20Sopenharmony_cistatic int gpmc_free_irq(struct gpmc_device *gpmc)
14308c2ecf20Sopenharmony_ci{
14318c2ecf20Sopenharmony_ci	int hwirq;
14328c2ecf20Sopenharmony_ci
14338c2ecf20Sopenharmony_ci	free_irq(gpmc->irq, gpmc);
14348c2ecf20Sopenharmony_ci
14358c2ecf20Sopenharmony_ci	for (hwirq = 0; hwirq < gpmc->nirqs; hwirq++)
14368c2ecf20Sopenharmony_ci		irq_dispose_mapping(irq_find_mapping(gpmc_irq_domain, hwirq));
14378c2ecf20Sopenharmony_ci
14388c2ecf20Sopenharmony_ci	irq_domain_remove(gpmc_irq_domain);
14398c2ecf20Sopenharmony_ci	gpmc_irq_domain = NULL;
14408c2ecf20Sopenharmony_ci
14418c2ecf20Sopenharmony_ci	return 0;
14428c2ecf20Sopenharmony_ci}
14438c2ecf20Sopenharmony_ci
14448c2ecf20Sopenharmony_cistatic void gpmc_mem_exit(void)
14458c2ecf20Sopenharmony_ci{
14468c2ecf20Sopenharmony_ci	int cs;
14478c2ecf20Sopenharmony_ci
14488c2ecf20Sopenharmony_ci	for (cs = 0; cs < gpmc_cs_num; cs++) {
14498c2ecf20Sopenharmony_ci		if (!gpmc_cs_mem_enabled(cs))
14508c2ecf20Sopenharmony_ci			continue;
14518c2ecf20Sopenharmony_ci		gpmc_cs_delete_mem(cs);
14528c2ecf20Sopenharmony_ci	}
14538c2ecf20Sopenharmony_ci}
14548c2ecf20Sopenharmony_ci
14558c2ecf20Sopenharmony_cistatic void gpmc_mem_init(void)
14568c2ecf20Sopenharmony_ci{
14578c2ecf20Sopenharmony_ci	int cs;
14588c2ecf20Sopenharmony_ci
14598c2ecf20Sopenharmony_ci	gpmc_mem_root.start = GPMC_MEM_START;
14608c2ecf20Sopenharmony_ci	gpmc_mem_root.end = GPMC_MEM_END;
14618c2ecf20Sopenharmony_ci
14628c2ecf20Sopenharmony_ci	/* Reserve all regions that has been set up by bootloader */
14638c2ecf20Sopenharmony_ci	for (cs = 0; cs < gpmc_cs_num; cs++) {
14648c2ecf20Sopenharmony_ci		u32 base, size;
14658c2ecf20Sopenharmony_ci
14668c2ecf20Sopenharmony_ci		if (!gpmc_cs_mem_enabled(cs))
14678c2ecf20Sopenharmony_ci			continue;
14688c2ecf20Sopenharmony_ci		gpmc_cs_get_memconf(cs, &base, &size);
14698c2ecf20Sopenharmony_ci		if (gpmc_cs_insert_mem(cs, base, size)) {
14708c2ecf20Sopenharmony_ci			pr_warn("%s: disabling cs %d mapped at 0x%x-0x%x\n",
14718c2ecf20Sopenharmony_ci				__func__, cs, base, base + size);
14728c2ecf20Sopenharmony_ci			gpmc_cs_disable_mem(cs);
14738c2ecf20Sopenharmony_ci		}
14748c2ecf20Sopenharmony_ci	}
14758c2ecf20Sopenharmony_ci}
14768c2ecf20Sopenharmony_ci
14778c2ecf20Sopenharmony_cistatic u32 gpmc_round_ps_to_sync_clk(u32 time_ps, u32 sync_clk)
14788c2ecf20Sopenharmony_ci{
14798c2ecf20Sopenharmony_ci	u32 temp;
14808c2ecf20Sopenharmony_ci	int div;
14818c2ecf20Sopenharmony_ci
14828c2ecf20Sopenharmony_ci	div = gpmc_calc_divider(sync_clk);
14838c2ecf20Sopenharmony_ci	temp = gpmc_ps_to_ticks(time_ps);
14848c2ecf20Sopenharmony_ci	temp = (temp + div - 1) / div;
14858c2ecf20Sopenharmony_ci	return gpmc_ticks_to_ps(temp * div);
14868c2ecf20Sopenharmony_ci}
14878c2ecf20Sopenharmony_ci
14888c2ecf20Sopenharmony_ci/* XXX: can the cycles be avoided ? */
14898c2ecf20Sopenharmony_cistatic int gpmc_calc_sync_read_timings(struct gpmc_timings *gpmc_t,
14908c2ecf20Sopenharmony_ci				       struct gpmc_device_timings *dev_t,
14918c2ecf20Sopenharmony_ci				       bool mux)
14928c2ecf20Sopenharmony_ci{
14938c2ecf20Sopenharmony_ci	u32 temp;
14948c2ecf20Sopenharmony_ci
14958c2ecf20Sopenharmony_ci	/* adv_rd_off */
14968c2ecf20Sopenharmony_ci	temp = dev_t->t_avdp_r;
14978c2ecf20Sopenharmony_ci	/* XXX: mux check required ? */
14988c2ecf20Sopenharmony_ci	if (mux) {
14998c2ecf20Sopenharmony_ci		/* XXX: t_avdp not to be required for sync, only added for tusb
15008c2ecf20Sopenharmony_ci		 * this indirectly necessitates requirement of t_avdp_r and
15018c2ecf20Sopenharmony_ci		 * t_avdp_w instead of having a single t_avdp
15028c2ecf20Sopenharmony_ci		 */
15038c2ecf20Sopenharmony_ci		temp = max_t(u32, temp,	gpmc_t->clk_activation + dev_t->t_avdh);
15048c2ecf20Sopenharmony_ci		temp = max_t(u32, gpmc_t->adv_on + gpmc_ticks_to_ps(1), temp);
15058c2ecf20Sopenharmony_ci	}
15068c2ecf20Sopenharmony_ci	gpmc_t->adv_rd_off = gpmc_round_ps_to_ticks(temp);
15078c2ecf20Sopenharmony_ci
15088c2ecf20Sopenharmony_ci	/* oe_on */
15098c2ecf20Sopenharmony_ci	temp = dev_t->t_oeasu; /* XXX: remove this ? */
15108c2ecf20Sopenharmony_ci	if (mux) {
15118c2ecf20Sopenharmony_ci		temp = max_t(u32, temp,	gpmc_t->clk_activation + dev_t->t_ach);
15128c2ecf20Sopenharmony_ci		temp = max_t(u32, temp, gpmc_t->adv_rd_off +
15138c2ecf20Sopenharmony_ci				gpmc_ticks_to_ps(dev_t->cyc_aavdh_oe));
15148c2ecf20Sopenharmony_ci	}
15158c2ecf20Sopenharmony_ci	gpmc_t->oe_on = gpmc_round_ps_to_ticks(temp);
15168c2ecf20Sopenharmony_ci
15178c2ecf20Sopenharmony_ci	/* access */
15188c2ecf20Sopenharmony_ci	/* XXX: any scope for improvement ?, by combining oe_on
15198c2ecf20Sopenharmony_ci	 * and clk_activation, need to check whether
15208c2ecf20Sopenharmony_ci	 * access = clk_activation + round to sync clk ?
15218c2ecf20Sopenharmony_ci	 */
15228c2ecf20Sopenharmony_ci	temp = max_t(u32, dev_t->t_iaa,	dev_t->cyc_iaa * gpmc_t->sync_clk);
15238c2ecf20Sopenharmony_ci	temp += gpmc_t->clk_activation;
15248c2ecf20Sopenharmony_ci	if (dev_t->cyc_oe)
15258c2ecf20Sopenharmony_ci		temp = max_t(u32, temp, gpmc_t->oe_on +
15268c2ecf20Sopenharmony_ci				gpmc_ticks_to_ps(dev_t->cyc_oe));
15278c2ecf20Sopenharmony_ci	gpmc_t->access = gpmc_round_ps_to_ticks(temp);
15288c2ecf20Sopenharmony_ci
15298c2ecf20Sopenharmony_ci	gpmc_t->oe_off = gpmc_t->access + gpmc_ticks_to_ps(1);
15308c2ecf20Sopenharmony_ci	gpmc_t->cs_rd_off = gpmc_t->oe_off;
15318c2ecf20Sopenharmony_ci
15328c2ecf20Sopenharmony_ci	/* rd_cycle */
15338c2ecf20Sopenharmony_ci	temp = max_t(u32, dev_t->t_cez_r, dev_t->t_oez);
15348c2ecf20Sopenharmony_ci	temp = gpmc_round_ps_to_sync_clk(temp, gpmc_t->sync_clk) +
15358c2ecf20Sopenharmony_ci							gpmc_t->access;
15368c2ecf20Sopenharmony_ci	/* XXX: barter t_ce_rdyz with t_cez_r ? */
15378c2ecf20Sopenharmony_ci	if (dev_t->t_ce_rdyz)
15388c2ecf20Sopenharmony_ci		temp = max_t(u32, temp,	gpmc_t->cs_rd_off + dev_t->t_ce_rdyz);
15398c2ecf20Sopenharmony_ci	gpmc_t->rd_cycle = gpmc_round_ps_to_ticks(temp);
15408c2ecf20Sopenharmony_ci
15418c2ecf20Sopenharmony_ci	return 0;
15428c2ecf20Sopenharmony_ci}
15438c2ecf20Sopenharmony_ci
15448c2ecf20Sopenharmony_cistatic int gpmc_calc_sync_write_timings(struct gpmc_timings *gpmc_t,
15458c2ecf20Sopenharmony_ci					struct gpmc_device_timings *dev_t,
15468c2ecf20Sopenharmony_ci					bool mux)
15478c2ecf20Sopenharmony_ci{
15488c2ecf20Sopenharmony_ci	u32 temp;
15498c2ecf20Sopenharmony_ci
15508c2ecf20Sopenharmony_ci	/* adv_wr_off */
15518c2ecf20Sopenharmony_ci	temp = dev_t->t_avdp_w;
15528c2ecf20Sopenharmony_ci	if (mux) {
15538c2ecf20Sopenharmony_ci		temp = max_t(u32, temp,
15548c2ecf20Sopenharmony_ci			gpmc_t->clk_activation + dev_t->t_avdh);
15558c2ecf20Sopenharmony_ci		temp = max_t(u32, gpmc_t->adv_on + gpmc_ticks_to_ps(1), temp);
15568c2ecf20Sopenharmony_ci	}
15578c2ecf20Sopenharmony_ci	gpmc_t->adv_wr_off = gpmc_round_ps_to_ticks(temp);
15588c2ecf20Sopenharmony_ci
15598c2ecf20Sopenharmony_ci	/* wr_data_mux_bus */
15608c2ecf20Sopenharmony_ci	temp = max_t(u32, dev_t->t_weasu,
15618c2ecf20Sopenharmony_ci			gpmc_t->clk_activation + dev_t->t_rdyo);
15628c2ecf20Sopenharmony_ci	/* XXX: shouldn't mux be kept as a whole for wr_data_mux_bus ?,
15638c2ecf20Sopenharmony_ci	 * and in that case remember to handle we_on properly
15648c2ecf20Sopenharmony_ci	 */
15658c2ecf20Sopenharmony_ci	if (mux) {
15668c2ecf20Sopenharmony_ci		temp = max_t(u32, temp,
15678c2ecf20Sopenharmony_ci			gpmc_t->adv_wr_off + dev_t->t_aavdh);
15688c2ecf20Sopenharmony_ci		temp = max_t(u32, temp, gpmc_t->adv_wr_off +
15698c2ecf20Sopenharmony_ci				gpmc_ticks_to_ps(dev_t->cyc_aavdh_we));
15708c2ecf20Sopenharmony_ci	}
15718c2ecf20Sopenharmony_ci	gpmc_t->wr_data_mux_bus = gpmc_round_ps_to_ticks(temp);
15728c2ecf20Sopenharmony_ci
15738c2ecf20Sopenharmony_ci	/* we_on */
15748c2ecf20Sopenharmony_ci	if (gpmc_capability & GPMC_HAS_WR_DATA_MUX_BUS)
15758c2ecf20Sopenharmony_ci		gpmc_t->we_on = gpmc_round_ps_to_ticks(dev_t->t_weasu);
15768c2ecf20Sopenharmony_ci	else
15778c2ecf20Sopenharmony_ci		gpmc_t->we_on = gpmc_t->wr_data_mux_bus;
15788c2ecf20Sopenharmony_ci
15798c2ecf20Sopenharmony_ci	/* wr_access */
15808c2ecf20Sopenharmony_ci	/* XXX: gpmc_capability check reqd ? , even if not, will not harm */
15818c2ecf20Sopenharmony_ci	gpmc_t->wr_access = gpmc_t->access;
15828c2ecf20Sopenharmony_ci
15838c2ecf20Sopenharmony_ci	/* we_off */
15848c2ecf20Sopenharmony_ci	temp = gpmc_t->we_on + dev_t->t_wpl;
15858c2ecf20Sopenharmony_ci	temp = max_t(u32, temp,
15868c2ecf20Sopenharmony_ci			gpmc_t->wr_access + gpmc_ticks_to_ps(1));
15878c2ecf20Sopenharmony_ci	temp = max_t(u32, temp,
15888c2ecf20Sopenharmony_ci		gpmc_t->we_on + gpmc_ticks_to_ps(dev_t->cyc_wpl));
15898c2ecf20Sopenharmony_ci	gpmc_t->we_off = gpmc_round_ps_to_ticks(temp);
15908c2ecf20Sopenharmony_ci
15918c2ecf20Sopenharmony_ci	gpmc_t->cs_wr_off = gpmc_round_ps_to_ticks(gpmc_t->we_off +
15928c2ecf20Sopenharmony_ci							dev_t->t_wph);
15938c2ecf20Sopenharmony_ci
15948c2ecf20Sopenharmony_ci	/* wr_cycle */
15958c2ecf20Sopenharmony_ci	temp = gpmc_round_ps_to_sync_clk(dev_t->t_cez_w, gpmc_t->sync_clk);
15968c2ecf20Sopenharmony_ci	temp += gpmc_t->wr_access;
15978c2ecf20Sopenharmony_ci	/* XXX: barter t_ce_rdyz with t_cez_w ? */
15988c2ecf20Sopenharmony_ci	if (dev_t->t_ce_rdyz)
15998c2ecf20Sopenharmony_ci		temp = max_t(u32, temp,
16008c2ecf20Sopenharmony_ci				 gpmc_t->cs_wr_off + dev_t->t_ce_rdyz);
16018c2ecf20Sopenharmony_ci	gpmc_t->wr_cycle = gpmc_round_ps_to_ticks(temp);
16028c2ecf20Sopenharmony_ci
16038c2ecf20Sopenharmony_ci	return 0;
16048c2ecf20Sopenharmony_ci}
16058c2ecf20Sopenharmony_ci
16068c2ecf20Sopenharmony_cistatic int gpmc_calc_async_read_timings(struct gpmc_timings *gpmc_t,
16078c2ecf20Sopenharmony_ci					struct gpmc_device_timings *dev_t,
16088c2ecf20Sopenharmony_ci					bool mux)
16098c2ecf20Sopenharmony_ci{
16108c2ecf20Sopenharmony_ci	u32 temp;
16118c2ecf20Sopenharmony_ci
16128c2ecf20Sopenharmony_ci	/* adv_rd_off */
16138c2ecf20Sopenharmony_ci	temp = dev_t->t_avdp_r;
16148c2ecf20Sopenharmony_ci	if (mux)
16158c2ecf20Sopenharmony_ci		temp = max_t(u32, gpmc_t->adv_on + gpmc_ticks_to_ps(1), temp);
16168c2ecf20Sopenharmony_ci	gpmc_t->adv_rd_off = gpmc_round_ps_to_ticks(temp);
16178c2ecf20Sopenharmony_ci
16188c2ecf20Sopenharmony_ci	/* oe_on */
16198c2ecf20Sopenharmony_ci	temp = dev_t->t_oeasu;
16208c2ecf20Sopenharmony_ci	if (mux)
16218c2ecf20Sopenharmony_ci		temp = max_t(u32, temp, gpmc_t->adv_rd_off + dev_t->t_aavdh);
16228c2ecf20Sopenharmony_ci	gpmc_t->oe_on = gpmc_round_ps_to_ticks(temp);
16238c2ecf20Sopenharmony_ci
16248c2ecf20Sopenharmony_ci	/* access */
16258c2ecf20Sopenharmony_ci	temp = max_t(u32, dev_t->t_iaa, /* XXX: remove t_iaa in async ? */
16268c2ecf20Sopenharmony_ci		     gpmc_t->oe_on + dev_t->t_oe);
16278c2ecf20Sopenharmony_ci	temp = max_t(u32, temp, gpmc_t->cs_on + dev_t->t_ce);
16288c2ecf20Sopenharmony_ci	temp = max_t(u32, temp, gpmc_t->adv_on + dev_t->t_aa);
16298c2ecf20Sopenharmony_ci	gpmc_t->access = gpmc_round_ps_to_ticks(temp);
16308c2ecf20Sopenharmony_ci
16318c2ecf20Sopenharmony_ci	gpmc_t->oe_off = gpmc_t->access + gpmc_ticks_to_ps(1);
16328c2ecf20Sopenharmony_ci	gpmc_t->cs_rd_off = gpmc_t->oe_off;
16338c2ecf20Sopenharmony_ci
16348c2ecf20Sopenharmony_ci	/* rd_cycle */
16358c2ecf20Sopenharmony_ci	temp = max_t(u32, dev_t->t_rd_cycle,
16368c2ecf20Sopenharmony_ci			gpmc_t->cs_rd_off + dev_t->t_cez_r);
16378c2ecf20Sopenharmony_ci	temp = max_t(u32, temp, gpmc_t->oe_off + dev_t->t_oez);
16388c2ecf20Sopenharmony_ci	gpmc_t->rd_cycle = gpmc_round_ps_to_ticks(temp);
16398c2ecf20Sopenharmony_ci
16408c2ecf20Sopenharmony_ci	return 0;
16418c2ecf20Sopenharmony_ci}
16428c2ecf20Sopenharmony_ci
16438c2ecf20Sopenharmony_cistatic int gpmc_calc_async_write_timings(struct gpmc_timings *gpmc_t,
16448c2ecf20Sopenharmony_ci					 struct gpmc_device_timings *dev_t,
16458c2ecf20Sopenharmony_ci					 bool mux)
16468c2ecf20Sopenharmony_ci{
16478c2ecf20Sopenharmony_ci	u32 temp;
16488c2ecf20Sopenharmony_ci
16498c2ecf20Sopenharmony_ci	/* adv_wr_off */
16508c2ecf20Sopenharmony_ci	temp = dev_t->t_avdp_w;
16518c2ecf20Sopenharmony_ci	if (mux)
16528c2ecf20Sopenharmony_ci		temp = max_t(u32, gpmc_t->adv_on + gpmc_ticks_to_ps(1), temp);
16538c2ecf20Sopenharmony_ci	gpmc_t->adv_wr_off = gpmc_round_ps_to_ticks(temp);
16548c2ecf20Sopenharmony_ci
16558c2ecf20Sopenharmony_ci	/* wr_data_mux_bus */
16568c2ecf20Sopenharmony_ci	temp = dev_t->t_weasu;
16578c2ecf20Sopenharmony_ci	if (mux) {
16588c2ecf20Sopenharmony_ci		temp = max_t(u32, temp,	gpmc_t->adv_wr_off + dev_t->t_aavdh);
16598c2ecf20Sopenharmony_ci		temp = max_t(u32, temp, gpmc_t->adv_wr_off +
16608c2ecf20Sopenharmony_ci				gpmc_ticks_to_ps(dev_t->cyc_aavdh_we));
16618c2ecf20Sopenharmony_ci	}
16628c2ecf20Sopenharmony_ci	gpmc_t->wr_data_mux_bus = gpmc_round_ps_to_ticks(temp);
16638c2ecf20Sopenharmony_ci
16648c2ecf20Sopenharmony_ci	/* we_on */
16658c2ecf20Sopenharmony_ci	if (gpmc_capability & GPMC_HAS_WR_DATA_MUX_BUS)
16668c2ecf20Sopenharmony_ci		gpmc_t->we_on = gpmc_round_ps_to_ticks(dev_t->t_weasu);
16678c2ecf20Sopenharmony_ci	else
16688c2ecf20Sopenharmony_ci		gpmc_t->we_on = gpmc_t->wr_data_mux_bus;
16698c2ecf20Sopenharmony_ci
16708c2ecf20Sopenharmony_ci	/* we_off */
16718c2ecf20Sopenharmony_ci	temp = gpmc_t->we_on + dev_t->t_wpl;
16728c2ecf20Sopenharmony_ci	gpmc_t->we_off = gpmc_round_ps_to_ticks(temp);
16738c2ecf20Sopenharmony_ci
16748c2ecf20Sopenharmony_ci	gpmc_t->cs_wr_off = gpmc_round_ps_to_ticks(gpmc_t->we_off +
16758c2ecf20Sopenharmony_ci							dev_t->t_wph);
16768c2ecf20Sopenharmony_ci
16778c2ecf20Sopenharmony_ci	/* wr_cycle */
16788c2ecf20Sopenharmony_ci	temp = max_t(u32, dev_t->t_wr_cycle,
16798c2ecf20Sopenharmony_ci				gpmc_t->cs_wr_off + dev_t->t_cez_w);
16808c2ecf20Sopenharmony_ci	gpmc_t->wr_cycle = gpmc_round_ps_to_ticks(temp);
16818c2ecf20Sopenharmony_ci
16828c2ecf20Sopenharmony_ci	return 0;
16838c2ecf20Sopenharmony_ci}
16848c2ecf20Sopenharmony_ci
16858c2ecf20Sopenharmony_cistatic int gpmc_calc_sync_common_timings(struct gpmc_timings *gpmc_t,
16868c2ecf20Sopenharmony_ci			struct gpmc_device_timings *dev_t)
16878c2ecf20Sopenharmony_ci{
16888c2ecf20Sopenharmony_ci	u32 temp;
16898c2ecf20Sopenharmony_ci
16908c2ecf20Sopenharmony_ci	gpmc_t->sync_clk = gpmc_calc_divider(dev_t->clk) *
16918c2ecf20Sopenharmony_ci						gpmc_get_fclk_period();
16928c2ecf20Sopenharmony_ci
16938c2ecf20Sopenharmony_ci	gpmc_t->page_burst_access = gpmc_round_ps_to_sync_clk(
16948c2ecf20Sopenharmony_ci					dev_t->t_bacc,
16958c2ecf20Sopenharmony_ci					gpmc_t->sync_clk);
16968c2ecf20Sopenharmony_ci
16978c2ecf20Sopenharmony_ci	temp = max_t(u32, dev_t->t_ces, dev_t->t_avds);
16988c2ecf20Sopenharmony_ci	gpmc_t->clk_activation = gpmc_round_ps_to_ticks(temp);
16998c2ecf20Sopenharmony_ci
17008c2ecf20Sopenharmony_ci	if (gpmc_calc_divider(gpmc_t->sync_clk) != 1)
17018c2ecf20Sopenharmony_ci		return 0;
17028c2ecf20Sopenharmony_ci
17038c2ecf20Sopenharmony_ci	if (dev_t->ce_xdelay)
17048c2ecf20Sopenharmony_ci		gpmc_t->bool_timings.cs_extra_delay = true;
17058c2ecf20Sopenharmony_ci	if (dev_t->avd_xdelay)
17068c2ecf20Sopenharmony_ci		gpmc_t->bool_timings.adv_extra_delay = true;
17078c2ecf20Sopenharmony_ci	if (dev_t->oe_xdelay)
17088c2ecf20Sopenharmony_ci		gpmc_t->bool_timings.oe_extra_delay = true;
17098c2ecf20Sopenharmony_ci	if (dev_t->we_xdelay)
17108c2ecf20Sopenharmony_ci		gpmc_t->bool_timings.we_extra_delay = true;
17118c2ecf20Sopenharmony_ci
17128c2ecf20Sopenharmony_ci	return 0;
17138c2ecf20Sopenharmony_ci}
17148c2ecf20Sopenharmony_ci
17158c2ecf20Sopenharmony_cistatic int gpmc_calc_common_timings(struct gpmc_timings *gpmc_t,
17168c2ecf20Sopenharmony_ci				    struct gpmc_device_timings *dev_t,
17178c2ecf20Sopenharmony_ci				    bool sync)
17188c2ecf20Sopenharmony_ci{
17198c2ecf20Sopenharmony_ci	u32 temp;
17208c2ecf20Sopenharmony_ci
17218c2ecf20Sopenharmony_ci	/* cs_on */
17228c2ecf20Sopenharmony_ci	gpmc_t->cs_on = gpmc_round_ps_to_ticks(dev_t->t_ceasu);
17238c2ecf20Sopenharmony_ci
17248c2ecf20Sopenharmony_ci	/* adv_on */
17258c2ecf20Sopenharmony_ci	temp = dev_t->t_avdasu;
17268c2ecf20Sopenharmony_ci	if (dev_t->t_ce_avd)
17278c2ecf20Sopenharmony_ci		temp = max_t(u32, temp,
17288c2ecf20Sopenharmony_ci				gpmc_t->cs_on + dev_t->t_ce_avd);
17298c2ecf20Sopenharmony_ci	gpmc_t->adv_on = gpmc_round_ps_to_ticks(temp);
17308c2ecf20Sopenharmony_ci
17318c2ecf20Sopenharmony_ci	if (sync)
17328c2ecf20Sopenharmony_ci		gpmc_calc_sync_common_timings(gpmc_t, dev_t);
17338c2ecf20Sopenharmony_ci
17348c2ecf20Sopenharmony_ci	return 0;
17358c2ecf20Sopenharmony_ci}
17368c2ecf20Sopenharmony_ci
17378c2ecf20Sopenharmony_ci/*
17388c2ecf20Sopenharmony_ci * TODO: remove this function once all peripherals are confirmed to
17398c2ecf20Sopenharmony_ci * work with generic timing. Simultaneously gpmc_cs_set_timings()
17408c2ecf20Sopenharmony_ci * has to be modified to handle timings in ps instead of ns
17418c2ecf20Sopenharmony_ci */
17428c2ecf20Sopenharmony_cistatic void gpmc_convert_ps_to_ns(struct gpmc_timings *t)
17438c2ecf20Sopenharmony_ci{
17448c2ecf20Sopenharmony_ci	t->cs_on /= 1000;
17458c2ecf20Sopenharmony_ci	t->cs_rd_off /= 1000;
17468c2ecf20Sopenharmony_ci	t->cs_wr_off /= 1000;
17478c2ecf20Sopenharmony_ci	t->adv_on /= 1000;
17488c2ecf20Sopenharmony_ci	t->adv_rd_off /= 1000;
17498c2ecf20Sopenharmony_ci	t->adv_wr_off /= 1000;
17508c2ecf20Sopenharmony_ci	t->we_on /= 1000;
17518c2ecf20Sopenharmony_ci	t->we_off /= 1000;
17528c2ecf20Sopenharmony_ci	t->oe_on /= 1000;
17538c2ecf20Sopenharmony_ci	t->oe_off /= 1000;
17548c2ecf20Sopenharmony_ci	t->page_burst_access /= 1000;
17558c2ecf20Sopenharmony_ci	t->access /= 1000;
17568c2ecf20Sopenharmony_ci	t->rd_cycle /= 1000;
17578c2ecf20Sopenharmony_ci	t->wr_cycle /= 1000;
17588c2ecf20Sopenharmony_ci	t->bus_turnaround /= 1000;
17598c2ecf20Sopenharmony_ci	t->cycle2cycle_delay /= 1000;
17608c2ecf20Sopenharmony_ci	t->wait_monitoring /= 1000;
17618c2ecf20Sopenharmony_ci	t->clk_activation /= 1000;
17628c2ecf20Sopenharmony_ci	t->wr_access /= 1000;
17638c2ecf20Sopenharmony_ci	t->wr_data_mux_bus /= 1000;
17648c2ecf20Sopenharmony_ci}
17658c2ecf20Sopenharmony_ci
17668c2ecf20Sopenharmony_ciint gpmc_calc_timings(struct gpmc_timings *gpmc_t,
17678c2ecf20Sopenharmony_ci		      struct gpmc_settings *gpmc_s,
17688c2ecf20Sopenharmony_ci		      struct gpmc_device_timings *dev_t)
17698c2ecf20Sopenharmony_ci{
17708c2ecf20Sopenharmony_ci	bool mux = false, sync = false;
17718c2ecf20Sopenharmony_ci
17728c2ecf20Sopenharmony_ci	if (gpmc_s) {
17738c2ecf20Sopenharmony_ci		mux = gpmc_s->mux_add_data ? true : false;
17748c2ecf20Sopenharmony_ci		sync = (gpmc_s->sync_read || gpmc_s->sync_write);
17758c2ecf20Sopenharmony_ci	}
17768c2ecf20Sopenharmony_ci
17778c2ecf20Sopenharmony_ci	memset(gpmc_t, 0, sizeof(*gpmc_t));
17788c2ecf20Sopenharmony_ci
17798c2ecf20Sopenharmony_ci	gpmc_calc_common_timings(gpmc_t, dev_t, sync);
17808c2ecf20Sopenharmony_ci
17818c2ecf20Sopenharmony_ci	if (gpmc_s && gpmc_s->sync_read)
17828c2ecf20Sopenharmony_ci		gpmc_calc_sync_read_timings(gpmc_t, dev_t, mux);
17838c2ecf20Sopenharmony_ci	else
17848c2ecf20Sopenharmony_ci		gpmc_calc_async_read_timings(gpmc_t, dev_t, mux);
17858c2ecf20Sopenharmony_ci
17868c2ecf20Sopenharmony_ci	if (gpmc_s && gpmc_s->sync_write)
17878c2ecf20Sopenharmony_ci		gpmc_calc_sync_write_timings(gpmc_t, dev_t, mux);
17888c2ecf20Sopenharmony_ci	else
17898c2ecf20Sopenharmony_ci		gpmc_calc_async_write_timings(gpmc_t, dev_t, mux);
17908c2ecf20Sopenharmony_ci
17918c2ecf20Sopenharmony_ci	/* TODO: remove, see function definition */
17928c2ecf20Sopenharmony_ci	gpmc_convert_ps_to_ns(gpmc_t);
17938c2ecf20Sopenharmony_ci
17948c2ecf20Sopenharmony_ci	return 0;
17958c2ecf20Sopenharmony_ci}
17968c2ecf20Sopenharmony_ci
17978c2ecf20Sopenharmony_ci/**
17988c2ecf20Sopenharmony_ci * gpmc_cs_program_settings - programs non-timing related settings
17998c2ecf20Sopenharmony_ci * @cs:		GPMC chip-select to program
18008c2ecf20Sopenharmony_ci * @p:		pointer to GPMC settings structure
18018c2ecf20Sopenharmony_ci *
18028c2ecf20Sopenharmony_ci * Programs non-timing related settings for a GPMC chip-select, such as
18038c2ecf20Sopenharmony_ci * bus-width, burst configuration, etc. Function should be called once
18048c2ecf20Sopenharmony_ci * for each chip-select that is being used and must be called before
18058c2ecf20Sopenharmony_ci * calling gpmc_cs_set_timings() as timing parameters in the CONFIG1
18068c2ecf20Sopenharmony_ci * register will be initialised to zero by this function. Returns 0 on
18078c2ecf20Sopenharmony_ci * success and appropriate negative error code on failure.
18088c2ecf20Sopenharmony_ci */
18098c2ecf20Sopenharmony_ciint gpmc_cs_program_settings(int cs, struct gpmc_settings *p)
18108c2ecf20Sopenharmony_ci{
18118c2ecf20Sopenharmony_ci	u32 config1;
18128c2ecf20Sopenharmony_ci
18138c2ecf20Sopenharmony_ci	if ((!p->device_width) || (p->device_width > GPMC_DEVWIDTH_16BIT)) {
18148c2ecf20Sopenharmony_ci		pr_err("%s: invalid width %d!", __func__, p->device_width);
18158c2ecf20Sopenharmony_ci		return -EINVAL;
18168c2ecf20Sopenharmony_ci	}
18178c2ecf20Sopenharmony_ci
18188c2ecf20Sopenharmony_ci	/* Address-data multiplexing not supported for NAND devices */
18198c2ecf20Sopenharmony_ci	if (p->device_nand && p->mux_add_data) {
18208c2ecf20Sopenharmony_ci		pr_err("%s: invalid configuration!\n", __func__);
18218c2ecf20Sopenharmony_ci		return -EINVAL;
18228c2ecf20Sopenharmony_ci	}
18238c2ecf20Sopenharmony_ci
18248c2ecf20Sopenharmony_ci	if ((p->mux_add_data > GPMC_MUX_AD) ||
18258c2ecf20Sopenharmony_ci	    ((p->mux_add_data == GPMC_MUX_AAD) &&
18268c2ecf20Sopenharmony_ci	     !(gpmc_capability & GPMC_HAS_MUX_AAD))) {
18278c2ecf20Sopenharmony_ci		pr_err("%s: invalid multiplex configuration!\n", __func__);
18288c2ecf20Sopenharmony_ci		return -EINVAL;
18298c2ecf20Sopenharmony_ci	}
18308c2ecf20Sopenharmony_ci
18318c2ecf20Sopenharmony_ci	/* Page/burst mode supports lengths of 4, 8 and 16 bytes */
18328c2ecf20Sopenharmony_ci	if (p->burst_read || p->burst_write) {
18338c2ecf20Sopenharmony_ci		switch (p->burst_len) {
18348c2ecf20Sopenharmony_ci		case GPMC_BURST_4:
18358c2ecf20Sopenharmony_ci		case GPMC_BURST_8:
18368c2ecf20Sopenharmony_ci		case GPMC_BURST_16:
18378c2ecf20Sopenharmony_ci			break;
18388c2ecf20Sopenharmony_ci		default:
18398c2ecf20Sopenharmony_ci			pr_err("%s: invalid page/burst-length (%d)\n",
18408c2ecf20Sopenharmony_ci			       __func__, p->burst_len);
18418c2ecf20Sopenharmony_ci			return -EINVAL;
18428c2ecf20Sopenharmony_ci		}
18438c2ecf20Sopenharmony_ci	}
18448c2ecf20Sopenharmony_ci
18458c2ecf20Sopenharmony_ci	if (p->wait_pin > gpmc_nr_waitpins) {
18468c2ecf20Sopenharmony_ci		pr_err("%s: invalid wait-pin (%d)\n", __func__, p->wait_pin);
18478c2ecf20Sopenharmony_ci		return -EINVAL;
18488c2ecf20Sopenharmony_ci	}
18498c2ecf20Sopenharmony_ci
18508c2ecf20Sopenharmony_ci	config1 = GPMC_CONFIG1_DEVICESIZE((p->device_width - 1));
18518c2ecf20Sopenharmony_ci
18528c2ecf20Sopenharmony_ci	if (p->sync_read)
18538c2ecf20Sopenharmony_ci		config1 |= GPMC_CONFIG1_READTYPE_SYNC;
18548c2ecf20Sopenharmony_ci	if (p->sync_write)
18558c2ecf20Sopenharmony_ci		config1 |= GPMC_CONFIG1_WRITETYPE_SYNC;
18568c2ecf20Sopenharmony_ci	if (p->wait_on_read)
18578c2ecf20Sopenharmony_ci		config1 |= GPMC_CONFIG1_WAIT_READ_MON;
18588c2ecf20Sopenharmony_ci	if (p->wait_on_write)
18598c2ecf20Sopenharmony_ci		config1 |= GPMC_CONFIG1_WAIT_WRITE_MON;
18608c2ecf20Sopenharmony_ci	if (p->wait_on_read || p->wait_on_write)
18618c2ecf20Sopenharmony_ci		config1 |= GPMC_CONFIG1_WAIT_PIN_SEL(p->wait_pin);
18628c2ecf20Sopenharmony_ci	if (p->device_nand)
18638c2ecf20Sopenharmony_ci		config1	|= GPMC_CONFIG1_DEVICETYPE(GPMC_DEVICETYPE_NAND);
18648c2ecf20Sopenharmony_ci	if (p->mux_add_data)
18658c2ecf20Sopenharmony_ci		config1	|= GPMC_CONFIG1_MUXTYPE(p->mux_add_data);
18668c2ecf20Sopenharmony_ci	if (p->burst_read)
18678c2ecf20Sopenharmony_ci		config1 |= GPMC_CONFIG1_READMULTIPLE_SUPP;
18688c2ecf20Sopenharmony_ci	if (p->burst_write)
18698c2ecf20Sopenharmony_ci		config1 |= GPMC_CONFIG1_WRITEMULTIPLE_SUPP;
18708c2ecf20Sopenharmony_ci	if (p->burst_read || p->burst_write) {
18718c2ecf20Sopenharmony_ci		config1 |= GPMC_CONFIG1_PAGE_LEN(p->burst_len >> 3);
18728c2ecf20Sopenharmony_ci		config1 |= p->burst_wrap ? GPMC_CONFIG1_WRAPBURST_SUPP : 0;
18738c2ecf20Sopenharmony_ci	}
18748c2ecf20Sopenharmony_ci
18758c2ecf20Sopenharmony_ci	gpmc_cs_write_reg(cs, GPMC_CS_CONFIG1, config1);
18768c2ecf20Sopenharmony_ci
18778c2ecf20Sopenharmony_ci	return 0;
18788c2ecf20Sopenharmony_ci}
18798c2ecf20Sopenharmony_ci
18808c2ecf20Sopenharmony_ci#ifdef CONFIG_OF
18818c2ecf20Sopenharmony_cistatic const struct of_device_id gpmc_dt_ids[] = {
18828c2ecf20Sopenharmony_ci	{ .compatible = "ti,omap2420-gpmc" },
18838c2ecf20Sopenharmony_ci	{ .compatible = "ti,omap2430-gpmc" },
18848c2ecf20Sopenharmony_ci	{ .compatible = "ti,omap3430-gpmc" },	/* omap3430 & omap3630 */
18858c2ecf20Sopenharmony_ci	{ .compatible = "ti,omap4430-gpmc" },	/* omap4430 & omap4460 & omap543x */
18868c2ecf20Sopenharmony_ci	{ .compatible = "ti,am3352-gpmc" },	/* am335x devices */
18878c2ecf20Sopenharmony_ci	{ }
18888c2ecf20Sopenharmony_ci};
18898c2ecf20Sopenharmony_ci
18908c2ecf20Sopenharmony_cistatic void gpmc_cs_set_name(int cs, const char *name)
18918c2ecf20Sopenharmony_ci{
18928c2ecf20Sopenharmony_ci	struct gpmc_cs_data *gpmc = &gpmc_cs[cs];
18938c2ecf20Sopenharmony_ci
18948c2ecf20Sopenharmony_ci	gpmc->name = name;
18958c2ecf20Sopenharmony_ci}
18968c2ecf20Sopenharmony_ci
18978c2ecf20Sopenharmony_cistatic const char *gpmc_cs_get_name(int cs)
18988c2ecf20Sopenharmony_ci{
18998c2ecf20Sopenharmony_ci	struct gpmc_cs_data *gpmc = &gpmc_cs[cs];
19008c2ecf20Sopenharmony_ci
19018c2ecf20Sopenharmony_ci	return gpmc->name;
19028c2ecf20Sopenharmony_ci}
19038c2ecf20Sopenharmony_ci
19048c2ecf20Sopenharmony_ci/**
19058c2ecf20Sopenharmony_ci * gpmc_cs_remap - remaps a chip-select physical base address
19068c2ecf20Sopenharmony_ci * @cs:		chip-select to remap
19078c2ecf20Sopenharmony_ci * @base:	physical base address to re-map chip-select to
19088c2ecf20Sopenharmony_ci *
19098c2ecf20Sopenharmony_ci * Re-maps a chip-select to a new physical base address specified by
19108c2ecf20Sopenharmony_ci * "base". Returns 0 on success and appropriate negative error code
19118c2ecf20Sopenharmony_ci * on failure.
19128c2ecf20Sopenharmony_ci */
19138c2ecf20Sopenharmony_cistatic int gpmc_cs_remap(int cs, u32 base)
19148c2ecf20Sopenharmony_ci{
19158c2ecf20Sopenharmony_ci	int ret;
19168c2ecf20Sopenharmony_ci	u32 old_base, size;
19178c2ecf20Sopenharmony_ci
19188c2ecf20Sopenharmony_ci	if (cs >= gpmc_cs_num) {
19198c2ecf20Sopenharmony_ci		pr_err("%s: requested chip-select is disabled\n", __func__);
19208c2ecf20Sopenharmony_ci		return -ENODEV;
19218c2ecf20Sopenharmony_ci	}
19228c2ecf20Sopenharmony_ci
19238c2ecf20Sopenharmony_ci	/*
19248c2ecf20Sopenharmony_ci	 * Make sure we ignore any device offsets from the GPMC partition
19258c2ecf20Sopenharmony_ci	 * allocated for the chip select and that the new base confirms
19268c2ecf20Sopenharmony_ci	 * to the GPMC 16MB minimum granularity.
19278c2ecf20Sopenharmony_ci	 */
19288c2ecf20Sopenharmony_ci	base &= ~(SZ_16M - 1);
19298c2ecf20Sopenharmony_ci
19308c2ecf20Sopenharmony_ci	gpmc_cs_get_memconf(cs, &old_base, &size);
19318c2ecf20Sopenharmony_ci	if (base == old_base)
19328c2ecf20Sopenharmony_ci		return 0;
19338c2ecf20Sopenharmony_ci
19348c2ecf20Sopenharmony_ci	ret = gpmc_cs_delete_mem(cs);
19358c2ecf20Sopenharmony_ci	if (ret < 0)
19368c2ecf20Sopenharmony_ci		return ret;
19378c2ecf20Sopenharmony_ci
19388c2ecf20Sopenharmony_ci	ret = gpmc_cs_insert_mem(cs, base, size);
19398c2ecf20Sopenharmony_ci	if (ret < 0)
19408c2ecf20Sopenharmony_ci		return ret;
19418c2ecf20Sopenharmony_ci
19428c2ecf20Sopenharmony_ci	ret = gpmc_cs_set_memconf(cs, base, size);
19438c2ecf20Sopenharmony_ci
19448c2ecf20Sopenharmony_ci	return ret;
19458c2ecf20Sopenharmony_ci}
19468c2ecf20Sopenharmony_ci
19478c2ecf20Sopenharmony_ci/**
19488c2ecf20Sopenharmony_ci * gpmc_read_settings_dt - read gpmc settings from device-tree
19498c2ecf20Sopenharmony_ci * @np:		pointer to device-tree node for a gpmc child device
19508c2ecf20Sopenharmony_ci * @p:		pointer to gpmc settings structure
19518c2ecf20Sopenharmony_ci *
19528c2ecf20Sopenharmony_ci * Reads the GPMC settings for a GPMC child device from device-tree and
19538c2ecf20Sopenharmony_ci * stores them in the GPMC settings structure passed. The GPMC settings
19548c2ecf20Sopenharmony_ci * structure is initialised to zero by this function and so any
19558c2ecf20Sopenharmony_ci * previously stored settings will be cleared.
19568c2ecf20Sopenharmony_ci */
19578c2ecf20Sopenharmony_civoid gpmc_read_settings_dt(struct device_node *np, struct gpmc_settings *p)
19588c2ecf20Sopenharmony_ci{
19598c2ecf20Sopenharmony_ci	memset(p, 0, sizeof(struct gpmc_settings));
19608c2ecf20Sopenharmony_ci
19618c2ecf20Sopenharmony_ci	p->sync_read = of_property_read_bool(np, "gpmc,sync-read");
19628c2ecf20Sopenharmony_ci	p->sync_write = of_property_read_bool(np, "gpmc,sync-write");
19638c2ecf20Sopenharmony_ci	of_property_read_u32(np, "gpmc,device-width", &p->device_width);
19648c2ecf20Sopenharmony_ci	of_property_read_u32(np, "gpmc,mux-add-data", &p->mux_add_data);
19658c2ecf20Sopenharmony_ci
19668c2ecf20Sopenharmony_ci	if (!of_property_read_u32(np, "gpmc,burst-length", &p->burst_len)) {
19678c2ecf20Sopenharmony_ci		p->burst_wrap = of_property_read_bool(np, "gpmc,burst-wrap");
19688c2ecf20Sopenharmony_ci		p->burst_read = of_property_read_bool(np, "gpmc,burst-read");
19698c2ecf20Sopenharmony_ci		p->burst_write = of_property_read_bool(np, "gpmc,burst-write");
19708c2ecf20Sopenharmony_ci		if (!p->burst_read && !p->burst_write)
19718c2ecf20Sopenharmony_ci			pr_warn("%s: page/burst-length set but not used!\n",
19728c2ecf20Sopenharmony_ci				__func__);
19738c2ecf20Sopenharmony_ci	}
19748c2ecf20Sopenharmony_ci
19758c2ecf20Sopenharmony_ci	if (!of_property_read_u32(np, "gpmc,wait-pin", &p->wait_pin)) {
19768c2ecf20Sopenharmony_ci		p->wait_on_read = of_property_read_bool(np,
19778c2ecf20Sopenharmony_ci							"gpmc,wait-on-read");
19788c2ecf20Sopenharmony_ci		p->wait_on_write = of_property_read_bool(np,
19798c2ecf20Sopenharmony_ci							 "gpmc,wait-on-write");
19808c2ecf20Sopenharmony_ci		if (!p->wait_on_read && !p->wait_on_write)
19818c2ecf20Sopenharmony_ci			pr_debug("%s: rd/wr wait monitoring not enabled!\n",
19828c2ecf20Sopenharmony_ci				 __func__);
19838c2ecf20Sopenharmony_ci	}
19848c2ecf20Sopenharmony_ci}
19858c2ecf20Sopenharmony_ci
19868c2ecf20Sopenharmony_cistatic void __maybe_unused gpmc_read_timings_dt(struct device_node *np,
19878c2ecf20Sopenharmony_ci						struct gpmc_timings *gpmc_t)
19888c2ecf20Sopenharmony_ci{
19898c2ecf20Sopenharmony_ci	struct gpmc_bool_timings *p;
19908c2ecf20Sopenharmony_ci
19918c2ecf20Sopenharmony_ci	if (!np || !gpmc_t)
19928c2ecf20Sopenharmony_ci		return;
19938c2ecf20Sopenharmony_ci
19948c2ecf20Sopenharmony_ci	memset(gpmc_t, 0, sizeof(*gpmc_t));
19958c2ecf20Sopenharmony_ci
19968c2ecf20Sopenharmony_ci	/* minimum clock period for syncronous mode */
19978c2ecf20Sopenharmony_ci	of_property_read_u32(np, "gpmc,sync-clk-ps", &gpmc_t->sync_clk);
19988c2ecf20Sopenharmony_ci
19998c2ecf20Sopenharmony_ci	/* chip select timtings */
20008c2ecf20Sopenharmony_ci	of_property_read_u32(np, "gpmc,cs-on-ns", &gpmc_t->cs_on);
20018c2ecf20Sopenharmony_ci	of_property_read_u32(np, "gpmc,cs-rd-off-ns", &gpmc_t->cs_rd_off);
20028c2ecf20Sopenharmony_ci	of_property_read_u32(np, "gpmc,cs-wr-off-ns", &gpmc_t->cs_wr_off);
20038c2ecf20Sopenharmony_ci
20048c2ecf20Sopenharmony_ci	/* ADV signal timings */
20058c2ecf20Sopenharmony_ci	of_property_read_u32(np, "gpmc,adv-on-ns", &gpmc_t->adv_on);
20068c2ecf20Sopenharmony_ci	of_property_read_u32(np, "gpmc,adv-rd-off-ns", &gpmc_t->adv_rd_off);
20078c2ecf20Sopenharmony_ci	of_property_read_u32(np, "gpmc,adv-wr-off-ns", &gpmc_t->adv_wr_off);
20088c2ecf20Sopenharmony_ci	of_property_read_u32(np, "gpmc,adv-aad-mux-on-ns",
20098c2ecf20Sopenharmony_ci			     &gpmc_t->adv_aad_mux_on);
20108c2ecf20Sopenharmony_ci	of_property_read_u32(np, "gpmc,adv-aad-mux-rd-off-ns",
20118c2ecf20Sopenharmony_ci			     &gpmc_t->adv_aad_mux_rd_off);
20128c2ecf20Sopenharmony_ci	of_property_read_u32(np, "gpmc,adv-aad-mux-wr-off-ns",
20138c2ecf20Sopenharmony_ci			     &gpmc_t->adv_aad_mux_wr_off);
20148c2ecf20Sopenharmony_ci
20158c2ecf20Sopenharmony_ci	/* WE signal timings */
20168c2ecf20Sopenharmony_ci	of_property_read_u32(np, "gpmc,we-on-ns", &gpmc_t->we_on);
20178c2ecf20Sopenharmony_ci	of_property_read_u32(np, "gpmc,we-off-ns", &gpmc_t->we_off);
20188c2ecf20Sopenharmony_ci
20198c2ecf20Sopenharmony_ci	/* OE signal timings */
20208c2ecf20Sopenharmony_ci	of_property_read_u32(np, "gpmc,oe-on-ns", &gpmc_t->oe_on);
20218c2ecf20Sopenharmony_ci	of_property_read_u32(np, "gpmc,oe-off-ns", &gpmc_t->oe_off);
20228c2ecf20Sopenharmony_ci	of_property_read_u32(np, "gpmc,oe-aad-mux-on-ns",
20238c2ecf20Sopenharmony_ci			     &gpmc_t->oe_aad_mux_on);
20248c2ecf20Sopenharmony_ci	of_property_read_u32(np, "gpmc,oe-aad-mux-off-ns",
20258c2ecf20Sopenharmony_ci			     &gpmc_t->oe_aad_mux_off);
20268c2ecf20Sopenharmony_ci
20278c2ecf20Sopenharmony_ci	/* access and cycle timings */
20288c2ecf20Sopenharmony_ci	of_property_read_u32(np, "gpmc,page-burst-access-ns",
20298c2ecf20Sopenharmony_ci			     &gpmc_t->page_burst_access);
20308c2ecf20Sopenharmony_ci	of_property_read_u32(np, "gpmc,access-ns", &gpmc_t->access);
20318c2ecf20Sopenharmony_ci	of_property_read_u32(np, "gpmc,rd-cycle-ns", &gpmc_t->rd_cycle);
20328c2ecf20Sopenharmony_ci	of_property_read_u32(np, "gpmc,wr-cycle-ns", &gpmc_t->wr_cycle);
20338c2ecf20Sopenharmony_ci	of_property_read_u32(np, "gpmc,bus-turnaround-ns",
20348c2ecf20Sopenharmony_ci			     &gpmc_t->bus_turnaround);
20358c2ecf20Sopenharmony_ci	of_property_read_u32(np, "gpmc,cycle2cycle-delay-ns",
20368c2ecf20Sopenharmony_ci			     &gpmc_t->cycle2cycle_delay);
20378c2ecf20Sopenharmony_ci	of_property_read_u32(np, "gpmc,wait-monitoring-ns",
20388c2ecf20Sopenharmony_ci			     &gpmc_t->wait_monitoring);
20398c2ecf20Sopenharmony_ci	of_property_read_u32(np, "gpmc,clk-activation-ns",
20408c2ecf20Sopenharmony_ci			     &gpmc_t->clk_activation);
20418c2ecf20Sopenharmony_ci
20428c2ecf20Sopenharmony_ci	/* only applicable to OMAP3+ */
20438c2ecf20Sopenharmony_ci	of_property_read_u32(np, "gpmc,wr-access-ns", &gpmc_t->wr_access);
20448c2ecf20Sopenharmony_ci	of_property_read_u32(np, "gpmc,wr-data-mux-bus-ns",
20458c2ecf20Sopenharmony_ci			     &gpmc_t->wr_data_mux_bus);
20468c2ecf20Sopenharmony_ci
20478c2ecf20Sopenharmony_ci	/* bool timing parameters */
20488c2ecf20Sopenharmony_ci	p = &gpmc_t->bool_timings;
20498c2ecf20Sopenharmony_ci
20508c2ecf20Sopenharmony_ci	p->cycle2cyclediffcsen =
20518c2ecf20Sopenharmony_ci		of_property_read_bool(np, "gpmc,cycle2cycle-diffcsen");
20528c2ecf20Sopenharmony_ci	p->cycle2cyclesamecsen =
20538c2ecf20Sopenharmony_ci		of_property_read_bool(np, "gpmc,cycle2cycle-samecsen");
20548c2ecf20Sopenharmony_ci	p->we_extra_delay = of_property_read_bool(np, "gpmc,we-extra-delay");
20558c2ecf20Sopenharmony_ci	p->oe_extra_delay = of_property_read_bool(np, "gpmc,oe-extra-delay");
20568c2ecf20Sopenharmony_ci	p->adv_extra_delay = of_property_read_bool(np, "gpmc,adv-extra-delay");
20578c2ecf20Sopenharmony_ci	p->cs_extra_delay = of_property_read_bool(np, "gpmc,cs-extra-delay");
20588c2ecf20Sopenharmony_ci	p->time_para_granularity =
20598c2ecf20Sopenharmony_ci		of_property_read_bool(np, "gpmc,time-para-granularity");
20608c2ecf20Sopenharmony_ci}
20618c2ecf20Sopenharmony_ci
20628c2ecf20Sopenharmony_ci/**
20638c2ecf20Sopenharmony_ci * gpmc_probe_generic_child - configures the gpmc for a child device
20648c2ecf20Sopenharmony_ci * @pdev:	pointer to gpmc platform device
20658c2ecf20Sopenharmony_ci * @child:	pointer to device-tree node for child device
20668c2ecf20Sopenharmony_ci *
20678c2ecf20Sopenharmony_ci * Allocates and configures a GPMC chip-select for a child device.
20688c2ecf20Sopenharmony_ci * Returns 0 on success and appropriate negative error code on failure.
20698c2ecf20Sopenharmony_ci */
20708c2ecf20Sopenharmony_cistatic int gpmc_probe_generic_child(struct platform_device *pdev,
20718c2ecf20Sopenharmony_ci				struct device_node *child)
20728c2ecf20Sopenharmony_ci{
20738c2ecf20Sopenharmony_ci	struct gpmc_settings gpmc_s;
20748c2ecf20Sopenharmony_ci	struct gpmc_timings gpmc_t;
20758c2ecf20Sopenharmony_ci	struct resource res;
20768c2ecf20Sopenharmony_ci	unsigned long base;
20778c2ecf20Sopenharmony_ci	const char *name;
20788c2ecf20Sopenharmony_ci	int ret, cs;
20798c2ecf20Sopenharmony_ci	u32 val;
20808c2ecf20Sopenharmony_ci	struct gpio_desc *waitpin_desc = NULL;
20818c2ecf20Sopenharmony_ci	struct gpmc_device *gpmc = platform_get_drvdata(pdev);
20828c2ecf20Sopenharmony_ci
20838c2ecf20Sopenharmony_ci	if (of_property_read_u32(child, "reg", &cs) < 0) {
20848c2ecf20Sopenharmony_ci		dev_err(&pdev->dev, "%pOF has no 'reg' property\n",
20858c2ecf20Sopenharmony_ci			child);
20868c2ecf20Sopenharmony_ci		return -ENODEV;
20878c2ecf20Sopenharmony_ci	}
20888c2ecf20Sopenharmony_ci
20898c2ecf20Sopenharmony_ci	if (of_address_to_resource(child, 0, &res) < 0) {
20908c2ecf20Sopenharmony_ci		dev_err(&pdev->dev, "%pOF has malformed 'reg' property\n",
20918c2ecf20Sopenharmony_ci			child);
20928c2ecf20Sopenharmony_ci		return -ENODEV;
20938c2ecf20Sopenharmony_ci	}
20948c2ecf20Sopenharmony_ci
20958c2ecf20Sopenharmony_ci	/*
20968c2ecf20Sopenharmony_ci	 * Check if we have multiple instances of the same device
20978c2ecf20Sopenharmony_ci	 * on a single chip select. If so, use the already initialized
20988c2ecf20Sopenharmony_ci	 * timings.
20998c2ecf20Sopenharmony_ci	 */
21008c2ecf20Sopenharmony_ci	name = gpmc_cs_get_name(cs);
21018c2ecf20Sopenharmony_ci	if (name && of_node_name_eq(child, name))
21028c2ecf20Sopenharmony_ci		goto no_timings;
21038c2ecf20Sopenharmony_ci
21048c2ecf20Sopenharmony_ci	ret = gpmc_cs_request(cs, resource_size(&res), &base);
21058c2ecf20Sopenharmony_ci	if (ret < 0) {
21068c2ecf20Sopenharmony_ci		dev_err(&pdev->dev, "cannot request GPMC CS %d\n", cs);
21078c2ecf20Sopenharmony_ci		return ret;
21088c2ecf20Sopenharmony_ci	}
21098c2ecf20Sopenharmony_ci	gpmc_cs_set_name(cs, child->full_name);
21108c2ecf20Sopenharmony_ci
21118c2ecf20Sopenharmony_ci	gpmc_read_settings_dt(child, &gpmc_s);
21128c2ecf20Sopenharmony_ci	gpmc_read_timings_dt(child, &gpmc_t);
21138c2ecf20Sopenharmony_ci
21148c2ecf20Sopenharmony_ci	/*
21158c2ecf20Sopenharmony_ci	 * For some GPMC devices we still need to rely on the bootloader
21168c2ecf20Sopenharmony_ci	 * timings because the devices can be connected via FPGA.
21178c2ecf20Sopenharmony_ci	 * REVISIT: Add timing support from slls644g.pdf.
21188c2ecf20Sopenharmony_ci	 */
21198c2ecf20Sopenharmony_ci	if (!gpmc_t.cs_rd_off) {
21208c2ecf20Sopenharmony_ci		WARN(1, "enable GPMC debug to configure .dts timings for CS%i\n",
21218c2ecf20Sopenharmony_ci			cs);
21228c2ecf20Sopenharmony_ci		gpmc_cs_show_timings(cs,
21238c2ecf20Sopenharmony_ci				     "please add GPMC bootloader timings to .dts");
21248c2ecf20Sopenharmony_ci		goto no_timings;
21258c2ecf20Sopenharmony_ci	}
21268c2ecf20Sopenharmony_ci
21278c2ecf20Sopenharmony_ci	/* CS must be disabled while making changes to gpmc configuration */
21288c2ecf20Sopenharmony_ci	gpmc_cs_disable_mem(cs);
21298c2ecf20Sopenharmony_ci
21308c2ecf20Sopenharmony_ci	/*
21318c2ecf20Sopenharmony_ci	 * FIXME: gpmc_cs_request() will map the CS to an arbitrary
21328c2ecf20Sopenharmony_ci	 * location in the gpmc address space. When booting with
21338c2ecf20Sopenharmony_ci	 * device-tree we want the NOR flash to be mapped to the
21348c2ecf20Sopenharmony_ci	 * location specified in the device-tree blob. So remap the
21358c2ecf20Sopenharmony_ci	 * CS to this location. Once DT migration is complete should
21368c2ecf20Sopenharmony_ci	 * just make gpmc_cs_request() map a specific address.
21378c2ecf20Sopenharmony_ci	 */
21388c2ecf20Sopenharmony_ci	ret = gpmc_cs_remap(cs, res.start);
21398c2ecf20Sopenharmony_ci	if (ret < 0) {
21408c2ecf20Sopenharmony_ci		dev_err(&pdev->dev, "cannot remap GPMC CS %d to %pa\n",
21418c2ecf20Sopenharmony_ci			cs, &res.start);
21428c2ecf20Sopenharmony_ci		if (res.start < GPMC_MEM_START) {
21438c2ecf20Sopenharmony_ci			dev_info(&pdev->dev,
21448c2ecf20Sopenharmony_ci				 "GPMC CS %d start cannot be lesser than 0x%x\n",
21458c2ecf20Sopenharmony_ci				 cs, GPMC_MEM_START);
21468c2ecf20Sopenharmony_ci		} else if (res.end > GPMC_MEM_END) {
21478c2ecf20Sopenharmony_ci			dev_info(&pdev->dev,
21488c2ecf20Sopenharmony_ci				 "GPMC CS %d end cannot be greater than 0x%x\n",
21498c2ecf20Sopenharmony_ci				 cs, GPMC_MEM_END);
21508c2ecf20Sopenharmony_ci		}
21518c2ecf20Sopenharmony_ci		goto err;
21528c2ecf20Sopenharmony_ci	}
21538c2ecf20Sopenharmony_ci
21548c2ecf20Sopenharmony_ci	if (of_node_name_eq(child, "nand")) {
21558c2ecf20Sopenharmony_ci		/* Warn about older DT blobs with no compatible property */
21568c2ecf20Sopenharmony_ci		if (!of_property_read_bool(child, "compatible")) {
21578c2ecf20Sopenharmony_ci			dev_warn(&pdev->dev,
21588c2ecf20Sopenharmony_ci				 "Incompatible NAND node: missing compatible");
21598c2ecf20Sopenharmony_ci			ret = -EINVAL;
21608c2ecf20Sopenharmony_ci			goto err;
21618c2ecf20Sopenharmony_ci		}
21628c2ecf20Sopenharmony_ci	}
21638c2ecf20Sopenharmony_ci
21648c2ecf20Sopenharmony_ci	if (of_node_name_eq(child, "onenand")) {
21658c2ecf20Sopenharmony_ci		/* Warn about older DT blobs with no compatible property */
21668c2ecf20Sopenharmony_ci		if (!of_property_read_bool(child, "compatible")) {
21678c2ecf20Sopenharmony_ci			dev_warn(&pdev->dev,
21688c2ecf20Sopenharmony_ci				 "Incompatible OneNAND node: missing compatible");
21698c2ecf20Sopenharmony_ci			ret = -EINVAL;
21708c2ecf20Sopenharmony_ci			goto err;
21718c2ecf20Sopenharmony_ci		}
21728c2ecf20Sopenharmony_ci	}
21738c2ecf20Sopenharmony_ci
21748c2ecf20Sopenharmony_ci	if (of_device_is_compatible(child, "ti,omap2-nand")) {
21758c2ecf20Sopenharmony_ci		/* NAND specific setup */
21768c2ecf20Sopenharmony_ci		val = 8;
21778c2ecf20Sopenharmony_ci		of_property_read_u32(child, "nand-bus-width", &val);
21788c2ecf20Sopenharmony_ci		switch (val) {
21798c2ecf20Sopenharmony_ci		case 8:
21808c2ecf20Sopenharmony_ci			gpmc_s.device_width = GPMC_DEVWIDTH_8BIT;
21818c2ecf20Sopenharmony_ci			break;
21828c2ecf20Sopenharmony_ci		case 16:
21838c2ecf20Sopenharmony_ci			gpmc_s.device_width = GPMC_DEVWIDTH_16BIT;
21848c2ecf20Sopenharmony_ci			break;
21858c2ecf20Sopenharmony_ci		default:
21868c2ecf20Sopenharmony_ci			dev_err(&pdev->dev, "%pOFn: invalid 'nand-bus-width'\n",
21878c2ecf20Sopenharmony_ci				child);
21888c2ecf20Sopenharmony_ci			ret = -EINVAL;
21898c2ecf20Sopenharmony_ci			goto err;
21908c2ecf20Sopenharmony_ci		}
21918c2ecf20Sopenharmony_ci
21928c2ecf20Sopenharmony_ci		/* disable write protect */
21938c2ecf20Sopenharmony_ci		gpmc_configure(GPMC_CONFIG_WP, 0);
21948c2ecf20Sopenharmony_ci		gpmc_s.device_nand = true;
21958c2ecf20Sopenharmony_ci	} else {
21968c2ecf20Sopenharmony_ci		ret = of_property_read_u32(child, "bank-width",
21978c2ecf20Sopenharmony_ci					   &gpmc_s.device_width);
21988c2ecf20Sopenharmony_ci		if (ret < 0 && !gpmc_s.device_width) {
21998c2ecf20Sopenharmony_ci			dev_err(&pdev->dev,
22008c2ecf20Sopenharmony_ci				"%pOF has no 'gpmc,device-width' property\n",
22018c2ecf20Sopenharmony_ci				child);
22028c2ecf20Sopenharmony_ci			goto err;
22038c2ecf20Sopenharmony_ci		}
22048c2ecf20Sopenharmony_ci	}
22058c2ecf20Sopenharmony_ci
22068c2ecf20Sopenharmony_ci	/* Reserve wait pin if it is required and valid */
22078c2ecf20Sopenharmony_ci	if (gpmc_s.wait_on_read || gpmc_s.wait_on_write) {
22088c2ecf20Sopenharmony_ci		unsigned int wait_pin = gpmc_s.wait_pin;
22098c2ecf20Sopenharmony_ci
22108c2ecf20Sopenharmony_ci		waitpin_desc = gpiochip_request_own_desc(&gpmc->gpio_chip,
22118c2ecf20Sopenharmony_ci							 wait_pin, "WAITPIN",
22128c2ecf20Sopenharmony_ci							 GPIO_ACTIVE_HIGH,
22138c2ecf20Sopenharmony_ci							 GPIOD_IN);
22148c2ecf20Sopenharmony_ci		if (IS_ERR(waitpin_desc)) {
22158c2ecf20Sopenharmony_ci			dev_err(&pdev->dev, "invalid wait-pin: %d\n", wait_pin);
22168c2ecf20Sopenharmony_ci			ret = PTR_ERR(waitpin_desc);
22178c2ecf20Sopenharmony_ci			goto err;
22188c2ecf20Sopenharmony_ci		}
22198c2ecf20Sopenharmony_ci	}
22208c2ecf20Sopenharmony_ci
22218c2ecf20Sopenharmony_ci	gpmc_cs_show_timings(cs, "before gpmc_cs_program_settings");
22228c2ecf20Sopenharmony_ci
22238c2ecf20Sopenharmony_ci	ret = gpmc_cs_program_settings(cs, &gpmc_s);
22248c2ecf20Sopenharmony_ci	if (ret < 0)
22258c2ecf20Sopenharmony_ci		goto err_cs;
22268c2ecf20Sopenharmony_ci
22278c2ecf20Sopenharmony_ci	ret = gpmc_cs_set_timings(cs, &gpmc_t, &gpmc_s);
22288c2ecf20Sopenharmony_ci	if (ret) {
22298c2ecf20Sopenharmony_ci		dev_err(&pdev->dev, "failed to set gpmc timings for: %pOFn\n",
22308c2ecf20Sopenharmony_ci			child);
22318c2ecf20Sopenharmony_ci		goto err_cs;
22328c2ecf20Sopenharmony_ci	}
22338c2ecf20Sopenharmony_ci
22348c2ecf20Sopenharmony_ci	/* Clear limited address i.e. enable A26-A11 */
22358c2ecf20Sopenharmony_ci	val = gpmc_read_reg(GPMC_CONFIG);
22368c2ecf20Sopenharmony_ci	val &= ~GPMC_CONFIG_LIMITEDADDRESS;
22378c2ecf20Sopenharmony_ci	gpmc_write_reg(GPMC_CONFIG, val);
22388c2ecf20Sopenharmony_ci
22398c2ecf20Sopenharmony_ci	/* Enable CS region */
22408c2ecf20Sopenharmony_ci	gpmc_cs_enable_mem(cs);
22418c2ecf20Sopenharmony_ci
22428c2ecf20Sopenharmony_cino_timings:
22438c2ecf20Sopenharmony_ci
22448c2ecf20Sopenharmony_ci	/* create platform device, NULL on error or when disabled */
22458c2ecf20Sopenharmony_ci	if (!of_platform_device_create(child, NULL, &pdev->dev))
22468c2ecf20Sopenharmony_ci		goto err_child_fail;
22478c2ecf20Sopenharmony_ci
22488c2ecf20Sopenharmony_ci	/* is child a common bus? */
22498c2ecf20Sopenharmony_ci	if (of_match_node(of_default_bus_match_table, child))
22508c2ecf20Sopenharmony_ci		/* create children and other common bus children */
22518c2ecf20Sopenharmony_ci		if (of_platform_default_populate(child, NULL, &pdev->dev))
22528c2ecf20Sopenharmony_ci			goto err_child_fail;
22538c2ecf20Sopenharmony_ci
22548c2ecf20Sopenharmony_ci	return 0;
22558c2ecf20Sopenharmony_ci
22568c2ecf20Sopenharmony_cierr_child_fail:
22578c2ecf20Sopenharmony_ci
22588c2ecf20Sopenharmony_ci	dev_err(&pdev->dev, "failed to create gpmc child %pOFn\n", child);
22598c2ecf20Sopenharmony_ci	ret = -ENODEV;
22608c2ecf20Sopenharmony_ci
22618c2ecf20Sopenharmony_cierr_cs:
22628c2ecf20Sopenharmony_ci	gpiochip_free_own_desc(waitpin_desc);
22638c2ecf20Sopenharmony_cierr:
22648c2ecf20Sopenharmony_ci	gpmc_cs_free(cs);
22658c2ecf20Sopenharmony_ci
22668c2ecf20Sopenharmony_ci	return ret;
22678c2ecf20Sopenharmony_ci}
22688c2ecf20Sopenharmony_ci
22698c2ecf20Sopenharmony_cistatic int gpmc_probe_dt(struct platform_device *pdev)
22708c2ecf20Sopenharmony_ci{
22718c2ecf20Sopenharmony_ci	int ret;
22728c2ecf20Sopenharmony_ci	const struct of_device_id *of_id =
22738c2ecf20Sopenharmony_ci		of_match_device(gpmc_dt_ids, &pdev->dev);
22748c2ecf20Sopenharmony_ci
22758c2ecf20Sopenharmony_ci	if (!of_id)
22768c2ecf20Sopenharmony_ci		return 0;
22778c2ecf20Sopenharmony_ci
22788c2ecf20Sopenharmony_ci	ret = of_property_read_u32(pdev->dev.of_node, "gpmc,num-cs",
22798c2ecf20Sopenharmony_ci				   &gpmc_cs_num);
22808c2ecf20Sopenharmony_ci	if (ret < 0) {
22818c2ecf20Sopenharmony_ci		pr_err("%s: number of chip-selects not defined\n", __func__);
22828c2ecf20Sopenharmony_ci		return ret;
22838c2ecf20Sopenharmony_ci	} else if (gpmc_cs_num < 1) {
22848c2ecf20Sopenharmony_ci		pr_err("%s: all chip-selects are disabled\n", __func__);
22858c2ecf20Sopenharmony_ci		return -EINVAL;
22868c2ecf20Sopenharmony_ci	} else if (gpmc_cs_num > GPMC_CS_NUM) {
22878c2ecf20Sopenharmony_ci		pr_err("%s: number of supported chip-selects cannot be > %d\n",
22888c2ecf20Sopenharmony_ci					 __func__, GPMC_CS_NUM);
22898c2ecf20Sopenharmony_ci		return -EINVAL;
22908c2ecf20Sopenharmony_ci	}
22918c2ecf20Sopenharmony_ci
22928c2ecf20Sopenharmony_ci	ret = of_property_read_u32(pdev->dev.of_node, "gpmc,num-waitpins",
22938c2ecf20Sopenharmony_ci				   &gpmc_nr_waitpins);
22948c2ecf20Sopenharmony_ci	if (ret < 0) {
22958c2ecf20Sopenharmony_ci		pr_err("%s: number of wait pins not found!\n", __func__);
22968c2ecf20Sopenharmony_ci		return ret;
22978c2ecf20Sopenharmony_ci	}
22988c2ecf20Sopenharmony_ci
22998c2ecf20Sopenharmony_ci	return 0;
23008c2ecf20Sopenharmony_ci}
23018c2ecf20Sopenharmony_ci
23028c2ecf20Sopenharmony_cistatic void gpmc_probe_dt_children(struct platform_device *pdev)
23038c2ecf20Sopenharmony_ci{
23048c2ecf20Sopenharmony_ci	int ret;
23058c2ecf20Sopenharmony_ci	struct device_node *child;
23068c2ecf20Sopenharmony_ci
23078c2ecf20Sopenharmony_ci	for_each_available_child_of_node(pdev->dev.of_node, child) {
23088c2ecf20Sopenharmony_ci		ret = gpmc_probe_generic_child(pdev, child);
23098c2ecf20Sopenharmony_ci		if (ret) {
23108c2ecf20Sopenharmony_ci			dev_err(&pdev->dev, "failed to probe DT child '%pOFn': %d\n",
23118c2ecf20Sopenharmony_ci				child, ret);
23128c2ecf20Sopenharmony_ci		}
23138c2ecf20Sopenharmony_ci	}
23148c2ecf20Sopenharmony_ci}
23158c2ecf20Sopenharmony_ci#else
23168c2ecf20Sopenharmony_civoid gpmc_read_settings_dt(struct device_node *np, struct gpmc_settings *p)
23178c2ecf20Sopenharmony_ci{
23188c2ecf20Sopenharmony_ci	memset(p, 0, sizeof(*p));
23198c2ecf20Sopenharmony_ci}
23208c2ecf20Sopenharmony_cistatic int gpmc_probe_dt(struct platform_device *pdev)
23218c2ecf20Sopenharmony_ci{
23228c2ecf20Sopenharmony_ci	return 0;
23238c2ecf20Sopenharmony_ci}
23248c2ecf20Sopenharmony_ci
23258c2ecf20Sopenharmony_cistatic void gpmc_probe_dt_children(struct platform_device *pdev)
23268c2ecf20Sopenharmony_ci{
23278c2ecf20Sopenharmony_ci}
23288c2ecf20Sopenharmony_ci#endif /* CONFIG_OF */
23298c2ecf20Sopenharmony_ci
23308c2ecf20Sopenharmony_cistatic int gpmc_gpio_get_direction(struct gpio_chip *chip, unsigned int offset)
23318c2ecf20Sopenharmony_ci{
23328c2ecf20Sopenharmony_ci	return 1;	/* we're input only */
23338c2ecf20Sopenharmony_ci}
23348c2ecf20Sopenharmony_ci
23358c2ecf20Sopenharmony_cistatic int gpmc_gpio_direction_input(struct gpio_chip *chip,
23368c2ecf20Sopenharmony_ci				     unsigned int offset)
23378c2ecf20Sopenharmony_ci{
23388c2ecf20Sopenharmony_ci	return 0;	/* we're input only */
23398c2ecf20Sopenharmony_ci}
23408c2ecf20Sopenharmony_ci
23418c2ecf20Sopenharmony_cistatic int gpmc_gpio_direction_output(struct gpio_chip *chip,
23428c2ecf20Sopenharmony_ci				      unsigned int offset, int value)
23438c2ecf20Sopenharmony_ci{
23448c2ecf20Sopenharmony_ci	return -EINVAL;	/* we're input only */
23458c2ecf20Sopenharmony_ci}
23468c2ecf20Sopenharmony_ci
23478c2ecf20Sopenharmony_cistatic void gpmc_gpio_set(struct gpio_chip *chip, unsigned int offset,
23488c2ecf20Sopenharmony_ci			  int value)
23498c2ecf20Sopenharmony_ci{
23508c2ecf20Sopenharmony_ci}
23518c2ecf20Sopenharmony_ci
23528c2ecf20Sopenharmony_cistatic int gpmc_gpio_get(struct gpio_chip *chip, unsigned int offset)
23538c2ecf20Sopenharmony_ci{
23548c2ecf20Sopenharmony_ci	u32 reg;
23558c2ecf20Sopenharmony_ci
23568c2ecf20Sopenharmony_ci	offset += 8;
23578c2ecf20Sopenharmony_ci
23588c2ecf20Sopenharmony_ci	reg = gpmc_read_reg(GPMC_STATUS) & BIT(offset);
23598c2ecf20Sopenharmony_ci
23608c2ecf20Sopenharmony_ci	return !!reg;
23618c2ecf20Sopenharmony_ci}
23628c2ecf20Sopenharmony_ci
23638c2ecf20Sopenharmony_cistatic int gpmc_gpio_init(struct gpmc_device *gpmc)
23648c2ecf20Sopenharmony_ci{
23658c2ecf20Sopenharmony_ci	int ret;
23668c2ecf20Sopenharmony_ci
23678c2ecf20Sopenharmony_ci	gpmc->gpio_chip.parent = gpmc->dev;
23688c2ecf20Sopenharmony_ci	gpmc->gpio_chip.owner = THIS_MODULE;
23698c2ecf20Sopenharmony_ci	gpmc->gpio_chip.label = DEVICE_NAME;
23708c2ecf20Sopenharmony_ci	gpmc->gpio_chip.ngpio = gpmc_nr_waitpins;
23718c2ecf20Sopenharmony_ci	gpmc->gpio_chip.get_direction = gpmc_gpio_get_direction;
23728c2ecf20Sopenharmony_ci	gpmc->gpio_chip.direction_input = gpmc_gpio_direction_input;
23738c2ecf20Sopenharmony_ci	gpmc->gpio_chip.direction_output = gpmc_gpio_direction_output;
23748c2ecf20Sopenharmony_ci	gpmc->gpio_chip.set = gpmc_gpio_set;
23758c2ecf20Sopenharmony_ci	gpmc->gpio_chip.get = gpmc_gpio_get;
23768c2ecf20Sopenharmony_ci	gpmc->gpio_chip.base = -1;
23778c2ecf20Sopenharmony_ci
23788c2ecf20Sopenharmony_ci	ret = devm_gpiochip_add_data(gpmc->dev, &gpmc->gpio_chip, NULL);
23798c2ecf20Sopenharmony_ci	if (ret < 0) {
23808c2ecf20Sopenharmony_ci		dev_err(gpmc->dev, "could not register gpio chip: %d\n", ret);
23818c2ecf20Sopenharmony_ci		return ret;
23828c2ecf20Sopenharmony_ci	}
23838c2ecf20Sopenharmony_ci
23848c2ecf20Sopenharmony_ci	return 0;
23858c2ecf20Sopenharmony_ci}
23868c2ecf20Sopenharmony_ci
23878c2ecf20Sopenharmony_cistatic int gpmc_probe(struct platform_device *pdev)
23888c2ecf20Sopenharmony_ci{
23898c2ecf20Sopenharmony_ci	int rc;
23908c2ecf20Sopenharmony_ci	u32 l;
23918c2ecf20Sopenharmony_ci	struct resource *res;
23928c2ecf20Sopenharmony_ci	struct gpmc_device *gpmc;
23938c2ecf20Sopenharmony_ci
23948c2ecf20Sopenharmony_ci	gpmc = devm_kzalloc(&pdev->dev, sizeof(*gpmc), GFP_KERNEL);
23958c2ecf20Sopenharmony_ci	if (!gpmc)
23968c2ecf20Sopenharmony_ci		return -ENOMEM;
23978c2ecf20Sopenharmony_ci
23988c2ecf20Sopenharmony_ci	gpmc->dev = &pdev->dev;
23998c2ecf20Sopenharmony_ci	platform_set_drvdata(pdev, gpmc);
24008c2ecf20Sopenharmony_ci
24018c2ecf20Sopenharmony_ci	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
24028c2ecf20Sopenharmony_ci	if (!res)
24038c2ecf20Sopenharmony_ci		return -ENOENT;
24048c2ecf20Sopenharmony_ci
24058c2ecf20Sopenharmony_ci	gpmc_base = devm_ioremap_resource(&pdev->dev, res);
24068c2ecf20Sopenharmony_ci	if (IS_ERR(gpmc_base))
24078c2ecf20Sopenharmony_ci		return PTR_ERR(gpmc_base);
24088c2ecf20Sopenharmony_ci
24098c2ecf20Sopenharmony_ci	res = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
24108c2ecf20Sopenharmony_ci	if (!res) {
24118c2ecf20Sopenharmony_ci		dev_err(&pdev->dev, "Failed to get resource: irq\n");
24128c2ecf20Sopenharmony_ci		return -ENOENT;
24138c2ecf20Sopenharmony_ci	}
24148c2ecf20Sopenharmony_ci
24158c2ecf20Sopenharmony_ci	gpmc->irq = res->start;
24168c2ecf20Sopenharmony_ci
24178c2ecf20Sopenharmony_ci	gpmc_l3_clk = devm_clk_get(&pdev->dev, "fck");
24188c2ecf20Sopenharmony_ci	if (IS_ERR(gpmc_l3_clk)) {
24198c2ecf20Sopenharmony_ci		dev_err(&pdev->dev, "Failed to get GPMC fck\n");
24208c2ecf20Sopenharmony_ci		return PTR_ERR(gpmc_l3_clk);
24218c2ecf20Sopenharmony_ci	}
24228c2ecf20Sopenharmony_ci
24238c2ecf20Sopenharmony_ci	if (!clk_get_rate(gpmc_l3_clk)) {
24248c2ecf20Sopenharmony_ci		dev_err(&pdev->dev, "Invalid GPMC fck clock rate\n");
24258c2ecf20Sopenharmony_ci		return -EINVAL;
24268c2ecf20Sopenharmony_ci	}
24278c2ecf20Sopenharmony_ci
24288c2ecf20Sopenharmony_ci	if (pdev->dev.of_node) {
24298c2ecf20Sopenharmony_ci		rc = gpmc_probe_dt(pdev);
24308c2ecf20Sopenharmony_ci		if (rc)
24318c2ecf20Sopenharmony_ci			return rc;
24328c2ecf20Sopenharmony_ci	} else {
24338c2ecf20Sopenharmony_ci		gpmc_cs_num = GPMC_CS_NUM;
24348c2ecf20Sopenharmony_ci		gpmc_nr_waitpins = GPMC_NR_WAITPINS;
24358c2ecf20Sopenharmony_ci	}
24368c2ecf20Sopenharmony_ci
24378c2ecf20Sopenharmony_ci	pm_runtime_enable(&pdev->dev);
24388c2ecf20Sopenharmony_ci	pm_runtime_get_sync(&pdev->dev);
24398c2ecf20Sopenharmony_ci
24408c2ecf20Sopenharmony_ci	l = gpmc_read_reg(GPMC_REVISION);
24418c2ecf20Sopenharmony_ci
24428c2ecf20Sopenharmony_ci	/*
24438c2ecf20Sopenharmony_ci	 * FIXME: Once device-tree migration is complete the below flags
24448c2ecf20Sopenharmony_ci	 * should be populated based upon the device-tree compatible
24458c2ecf20Sopenharmony_ci	 * string. For now just use the IP revision. OMAP3+ devices have
24468c2ecf20Sopenharmony_ci	 * the wr_access and wr_data_mux_bus register fields. OMAP4+
24478c2ecf20Sopenharmony_ci	 * devices support the addr-addr-data multiplex protocol.
24488c2ecf20Sopenharmony_ci	 *
24498c2ecf20Sopenharmony_ci	 * GPMC IP revisions:
24508c2ecf20Sopenharmony_ci	 * - OMAP24xx			= 2.0
24518c2ecf20Sopenharmony_ci	 * - OMAP3xxx			= 5.0
24528c2ecf20Sopenharmony_ci	 * - OMAP44xx/54xx/AM335x	= 6.0
24538c2ecf20Sopenharmony_ci	 */
24548c2ecf20Sopenharmony_ci	if (GPMC_REVISION_MAJOR(l) > 0x4)
24558c2ecf20Sopenharmony_ci		gpmc_capability = GPMC_HAS_WR_ACCESS | GPMC_HAS_WR_DATA_MUX_BUS;
24568c2ecf20Sopenharmony_ci	if (GPMC_REVISION_MAJOR(l) > 0x5)
24578c2ecf20Sopenharmony_ci		gpmc_capability |= GPMC_HAS_MUX_AAD;
24588c2ecf20Sopenharmony_ci	dev_info(gpmc->dev, "GPMC revision %d.%d\n", GPMC_REVISION_MAJOR(l),
24598c2ecf20Sopenharmony_ci		 GPMC_REVISION_MINOR(l));
24608c2ecf20Sopenharmony_ci
24618c2ecf20Sopenharmony_ci	gpmc_mem_init();
24628c2ecf20Sopenharmony_ci	rc = gpmc_gpio_init(gpmc);
24638c2ecf20Sopenharmony_ci	if (rc)
24648c2ecf20Sopenharmony_ci		goto gpio_init_failed;
24658c2ecf20Sopenharmony_ci
24668c2ecf20Sopenharmony_ci	gpmc->nirqs = GPMC_NR_NAND_IRQS + gpmc_nr_waitpins;
24678c2ecf20Sopenharmony_ci	rc = gpmc_setup_irq(gpmc);
24688c2ecf20Sopenharmony_ci	if (rc) {
24698c2ecf20Sopenharmony_ci		dev_err(gpmc->dev, "gpmc_setup_irq failed\n");
24708c2ecf20Sopenharmony_ci		goto gpio_init_failed;
24718c2ecf20Sopenharmony_ci	}
24728c2ecf20Sopenharmony_ci
24738c2ecf20Sopenharmony_ci	gpmc_probe_dt_children(pdev);
24748c2ecf20Sopenharmony_ci
24758c2ecf20Sopenharmony_ci	return 0;
24768c2ecf20Sopenharmony_ci
24778c2ecf20Sopenharmony_cigpio_init_failed:
24788c2ecf20Sopenharmony_ci	gpmc_mem_exit();
24798c2ecf20Sopenharmony_ci	pm_runtime_put_sync(&pdev->dev);
24808c2ecf20Sopenharmony_ci	pm_runtime_disable(&pdev->dev);
24818c2ecf20Sopenharmony_ci
24828c2ecf20Sopenharmony_ci	return rc;
24838c2ecf20Sopenharmony_ci}
24848c2ecf20Sopenharmony_ci
24858c2ecf20Sopenharmony_cistatic int gpmc_remove(struct platform_device *pdev)
24868c2ecf20Sopenharmony_ci{
24878c2ecf20Sopenharmony_ci	struct gpmc_device *gpmc = platform_get_drvdata(pdev);
24888c2ecf20Sopenharmony_ci
24898c2ecf20Sopenharmony_ci	gpmc_free_irq(gpmc);
24908c2ecf20Sopenharmony_ci	gpmc_mem_exit();
24918c2ecf20Sopenharmony_ci	pm_runtime_put_sync(&pdev->dev);
24928c2ecf20Sopenharmony_ci	pm_runtime_disable(&pdev->dev);
24938c2ecf20Sopenharmony_ci
24948c2ecf20Sopenharmony_ci	return 0;
24958c2ecf20Sopenharmony_ci}
24968c2ecf20Sopenharmony_ci
24978c2ecf20Sopenharmony_ci#ifdef CONFIG_PM_SLEEP
24988c2ecf20Sopenharmony_cistatic int gpmc_suspend(struct device *dev)
24998c2ecf20Sopenharmony_ci{
25008c2ecf20Sopenharmony_ci	omap3_gpmc_save_context();
25018c2ecf20Sopenharmony_ci	pm_runtime_put_sync(dev);
25028c2ecf20Sopenharmony_ci	return 0;
25038c2ecf20Sopenharmony_ci}
25048c2ecf20Sopenharmony_ci
25058c2ecf20Sopenharmony_cistatic int gpmc_resume(struct device *dev)
25068c2ecf20Sopenharmony_ci{
25078c2ecf20Sopenharmony_ci	pm_runtime_get_sync(dev);
25088c2ecf20Sopenharmony_ci	omap3_gpmc_restore_context();
25098c2ecf20Sopenharmony_ci	return 0;
25108c2ecf20Sopenharmony_ci}
25118c2ecf20Sopenharmony_ci#endif
25128c2ecf20Sopenharmony_ci
25138c2ecf20Sopenharmony_cistatic SIMPLE_DEV_PM_OPS(gpmc_pm_ops, gpmc_suspend, gpmc_resume);
25148c2ecf20Sopenharmony_ci
25158c2ecf20Sopenharmony_cistatic struct platform_driver gpmc_driver = {
25168c2ecf20Sopenharmony_ci	.probe		= gpmc_probe,
25178c2ecf20Sopenharmony_ci	.remove		= gpmc_remove,
25188c2ecf20Sopenharmony_ci	.driver		= {
25198c2ecf20Sopenharmony_ci		.name	= DEVICE_NAME,
25208c2ecf20Sopenharmony_ci		.of_match_table = of_match_ptr(gpmc_dt_ids),
25218c2ecf20Sopenharmony_ci		.pm	= &gpmc_pm_ops,
25228c2ecf20Sopenharmony_ci	},
25238c2ecf20Sopenharmony_ci};
25248c2ecf20Sopenharmony_ci
25258c2ecf20Sopenharmony_cistatic __init int gpmc_init(void)
25268c2ecf20Sopenharmony_ci{
25278c2ecf20Sopenharmony_ci	return platform_driver_register(&gpmc_driver);
25288c2ecf20Sopenharmony_ci}
25298c2ecf20Sopenharmony_cipostcore_initcall(gpmc_init);
25308c2ecf20Sopenharmony_ci
25318c2ecf20Sopenharmony_cistatic struct omap3_gpmc_regs gpmc_context;
25328c2ecf20Sopenharmony_ci
25338c2ecf20Sopenharmony_civoid omap3_gpmc_save_context(void)
25348c2ecf20Sopenharmony_ci{
25358c2ecf20Sopenharmony_ci	int i;
25368c2ecf20Sopenharmony_ci
25378c2ecf20Sopenharmony_ci	if (!gpmc_base)
25388c2ecf20Sopenharmony_ci		return;
25398c2ecf20Sopenharmony_ci
25408c2ecf20Sopenharmony_ci	gpmc_context.sysconfig = gpmc_read_reg(GPMC_SYSCONFIG);
25418c2ecf20Sopenharmony_ci	gpmc_context.irqenable = gpmc_read_reg(GPMC_IRQENABLE);
25428c2ecf20Sopenharmony_ci	gpmc_context.timeout_ctrl = gpmc_read_reg(GPMC_TIMEOUT_CONTROL);
25438c2ecf20Sopenharmony_ci	gpmc_context.config = gpmc_read_reg(GPMC_CONFIG);
25448c2ecf20Sopenharmony_ci	gpmc_context.prefetch_config1 = gpmc_read_reg(GPMC_PREFETCH_CONFIG1);
25458c2ecf20Sopenharmony_ci	gpmc_context.prefetch_config2 = gpmc_read_reg(GPMC_PREFETCH_CONFIG2);
25468c2ecf20Sopenharmony_ci	gpmc_context.prefetch_control = gpmc_read_reg(GPMC_PREFETCH_CONTROL);
25478c2ecf20Sopenharmony_ci	for (i = 0; i < gpmc_cs_num; i++) {
25488c2ecf20Sopenharmony_ci		gpmc_context.cs_context[i].is_valid = gpmc_cs_mem_enabled(i);
25498c2ecf20Sopenharmony_ci		if (gpmc_context.cs_context[i].is_valid) {
25508c2ecf20Sopenharmony_ci			gpmc_context.cs_context[i].config1 =
25518c2ecf20Sopenharmony_ci				gpmc_cs_read_reg(i, GPMC_CS_CONFIG1);
25528c2ecf20Sopenharmony_ci			gpmc_context.cs_context[i].config2 =
25538c2ecf20Sopenharmony_ci				gpmc_cs_read_reg(i, GPMC_CS_CONFIG2);
25548c2ecf20Sopenharmony_ci			gpmc_context.cs_context[i].config3 =
25558c2ecf20Sopenharmony_ci				gpmc_cs_read_reg(i, GPMC_CS_CONFIG3);
25568c2ecf20Sopenharmony_ci			gpmc_context.cs_context[i].config4 =
25578c2ecf20Sopenharmony_ci				gpmc_cs_read_reg(i, GPMC_CS_CONFIG4);
25588c2ecf20Sopenharmony_ci			gpmc_context.cs_context[i].config5 =
25598c2ecf20Sopenharmony_ci				gpmc_cs_read_reg(i, GPMC_CS_CONFIG5);
25608c2ecf20Sopenharmony_ci			gpmc_context.cs_context[i].config6 =
25618c2ecf20Sopenharmony_ci				gpmc_cs_read_reg(i, GPMC_CS_CONFIG6);
25628c2ecf20Sopenharmony_ci			gpmc_context.cs_context[i].config7 =
25638c2ecf20Sopenharmony_ci				gpmc_cs_read_reg(i, GPMC_CS_CONFIG7);
25648c2ecf20Sopenharmony_ci		}
25658c2ecf20Sopenharmony_ci	}
25668c2ecf20Sopenharmony_ci}
25678c2ecf20Sopenharmony_ci
25688c2ecf20Sopenharmony_civoid omap3_gpmc_restore_context(void)
25698c2ecf20Sopenharmony_ci{
25708c2ecf20Sopenharmony_ci	int i;
25718c2ecf20Sopenharmony_ci
25728c2ecf20Sopenharmony_ci	if (!gpmc_base)
25738c2ecf20Sopenharmony_ci		return;
25748c2ecf20Sopenharmony_ci
25758c2ecf20Sopenharmony_ci	gpmc_write_reg(GPMC_SYSCONFIG, gpmc_context.sysconfig);
25768c2ecf20Sopenharmony_ci	gpmc_write_reg(GPMC_IRQENABLE, gpmc_context.irqenable);
25778c2ecf20Sopenharmony_ci	gpmc_write_reg(GPMC_TIMEOUT_CONTROL, gpmc_context.timeout_ctrl);
25788c2ecf20Sopenharmony_ci	gpmc_write_reg(GPMC_CONFIG, gpmc_context.config);
25798c2ecf20Sopenharmony_ci	gpmc_write_reg(GPMC_PREFETCH_CONFIG1, gpmc_context.prefetch_config1);
25808c2ecf20Sopenharmony_ci	gpmc_write_reg(GPMC_PREFETCH_CONFIG2, gpmc_context.prefetch_config2);
25818c2ecf20Sopenharmony_ci	gpmc_write_reg(GPMC_PREFETCH_CONTROL, gpmc_context.prefetch_control);
25828c2ecf20Sopenharmony_ci	for (i = 0; i < gpmc_cs_num; i++) {
25838c2ecf20Sopenharmony_ci		if (gpmc_context.cs_context[i].is_valid) {
25848c2ecf20Sopenharmony_ci			gpmc_cs_write_reg(i, GPMC_CS_CONFIG1,
25858c2ecf20Sopenharmony_ci				gpmc_context.cs_context[i].config1);
25868c2ecf20Sopenharmony_ci			gpmc_cs_write_reg(i, GPMC_CS_CONFIG2,
25878c2ecf20Sopenharmony_ci				gpmc_context.cs_context[i].config2);
25888c2ecf20Sopenharmony_ci			gpmc_cs_write_reg(i, GPMC_CS_CONFIG3,
25898c2ecf20Sopenharmony_ci				gpmc_context.cs_context[i].config3);
25908c2ecf20Sopenharmony_ci			gpmc_cs_write_reg(i, GPMC_CS_CONFIG4,
25918c2ecf20Sopenharmony_ci				gpmc_context.cs_context[i].config4);
25928c2ecf20Sopenharmony_ci			gpmc_cs_write_reg(i, GPMC_CS_CONFIG5,
25938c2ecf20Sopenharmony_ci				gpmc_context.cs_context[i].config5);
25948c2ecf20Sopenharmony_ci			gpmc_cs_write_reg(i, GPMC_CS_CONFIG6,
25958c2ecf20Sopenharmony_ci				gpmc_context.cs_context[i].config6);
25968c2ecf20Sopenharmony_ci			gpmc_cs_write_reg(i, GPMC_CS_CONFIG7,
25978c2ecf20Sopenharmony_ci				gpmc_context.cs_context[i].config7);
25988c2ecf20Sopenharmony_ci		}
25998c2ecf20Sopenharmony_ci	}
26008c2ecf20Sopenharmony_ci}
2601