xref: /linux/drivers/mtd/devices/spear_smi.c (revision 4f6b838c378a52ea3ae0b15f12ca8a20849072fa)
1f18dbbb1SShiraz Hashim /*
2f18dbbb1SShiraz Hashim  * SMI (Serial Memory Controller) device driver for Serial NOR Flash on
3f18dbbb1SShiraz Hashim  * SPEAr platform
4b74bdbe5SBrian Norris  * The serial nor interface is largely based on m25p80.c, however the SPI
5b74bdbe5SBrian Norris  * interface has been replaced by SMI.
6f18dbbb1SShiraz Hashim  *
7f18dbbb1SShiraz Hashim  * Copyright © 2010 STMicroelectronics.
8f18dbbb1SShiraz Hashim  * Ashish Priyadarshi
99cc23682SViresh Kumar  * Shiraz Hashim <shiraz.linux.kernel@gmail.com>
10f18dbbb1SShiraz Hashim  *
11f18dbbb1SShiraz Hashim  * This file is licensed under the terms of the GNU General Public
12f18dbbb1SShiraz Hashim  * License version 2. This program is licensed "as is" without any
13f18dbbb1SShiraz Hashim  * warranty of any kind, whether express or implied.
14f18dbbb1SShiraz Hashim  */
15f18dbbb1SShiraz Hashim 
16f18dbbb1SShiraz Hashim #include <linux/clk.h>
17f18dbbb1SShiraz Hashim #include <linux/delay.h>
18f18dbbb1SShiraz Hashim #include <linux/device.h>
19f18dbbb1SShiraz Hashim #include <linux/err.h>
20f18dbbb1SShiraz Hashim #include <linux/errno.h>
21f18dbbb1SShiraz Hashim #include <linux/interrupt.h>
22f18dbbb1SShiraz Hashim #include <linux/io.h>
23f18dbbb1SShiraz Hashim #include <linux/ioport.h>
24f18dbbb1SShiraz Hashim #include <linux/jiffies.h>
25f18dbbb1SShiraz Hashim #include <linux/kernel.h>
26f18dbbb1SShiraz Hashim #include <linux/module.h>
27f18dbbb1SShiraz Hashim #include <linux/param.h>
28f18dbbb1SShiraz Hashim #include <linux/platform_device.h>
29770daa43SViresh Kumar #include <linux/pm.h>
30f18dbbb1SShiraz Hashim #include <linux/mtd/mtd.h>
31f18dbbb1SShiraz Hashim #include <linux/mtd/partitions.h>
32f18dbbb1SShiraz Hashim #include <linux/mtd/spear_smi.h>
33f18dbbb1SShiraz Hashim #include <linux/mutex.h>
34f18dbbb1SShiraz Hashim #include <linux/sched.h>
35f18dbbb1SShiraz Hashim #include <linux/slab.h>
36f18dbbb1SShiraz Hashim #include <linux/wait.h>
376551ab5dSStefan Roese #include <linux/of.h>
386551ab5dSStefan Roese #include <linux/of_address.h>
39f18dbbb1SShiraz Hashim 
40f18dbbb1SShiraz Hashim /* SMI clock rate */
41f18dbbb1SShiraz Hashim #define SMI_MAX_CLOCK_FREQ	50000000 /* 50 MHz */
42f18dbbb1SShiraz Hashim 
43f18dbbb1SShiraz Hashim /* MAX time out to safely come out of a erase or write busy conditions */
44f18dbbb1SShiraz Hashim #define SMI_PROBE_TIMEOUT	(HZ / 10)
45f18dbbb1SShiraz Hashim #define SMI_MAX_TIME_OUT	(3 * HZ)
46f18dbbb1SShiraz Hashim 
47f18dbbb1SShiraz Hashim /* timeout for command completion */
48f18dbbb1SShiraz Hashim #define SMI_CMD_TIMEOUT		(HZ / 10)
49f18dbbb1SShiraz Hashim 
50f18dbbb1SShiraz Hashim /* registers of smi */
51f18dbbb1SShiraz Hashim #define SMI_CR1		0x0	/* SMI control register 1 */
52f18dbbb1SShiraz Hashim #define SMI_CR2		0x4	/* SMI control register 2 */
53f18dbbb1SShiraz Hashim #define SMI_SR		0x8	/* SMI status register */
54f18dbbb1SShiraz Hashim #define SMI_TR		0xC	/* SMI transmit register */
55f18dbbb1SShiraz Hashim #define SMI_RR		0x10	/* SMI receive register */
56f18dbbb1SShiraz Hashim 
57f18dbbb1SShiraz Hashim /* defines for control_reg 1 */
58f18dbbb1SShiraz Hashim #define BANK_EN		(0xF << 0)	/* enables all banks */
59f18dbbb1SShiraz Hashim #define DSEL_TIME	(0x6 << 4)	/* Deselect time 6 + 1 SMI_CK periods */
60f18dbbb1SShiraz Hashim #define SW_MODE		(0x1 << 28)	/* enables SW Mode */
61f18dbbb1SShiraz Hashim #define WB_MODE		(0x1 << 29)	/* Write Burst Mode */
62f18dbbb1SShiraz Hashim #define FAST_MODE	(0x1 << 15)	/* Fast Mode */
63f18dbbb1SShiraz Hashim #define HOLD1		(0x1 << 16)	/* Clock Hold period selection */
64f18dbbb1SShiraz Hashim 
65f18dbbb1SShiraz Hashim /* defines for control_reg 2 */
66f18dbbb1SShiraz Hashim #define SEND		(0x1 << 7)	/* Send data */
67f18dbbb1SShiraz Hashim #define TFIE		(0x1 << 8)	/* Transmission Flag Interrupt Enable */
68f18dbbb1SShiraz Hashim #define WCIE		(0x1 << 9)	/* Write Complete Interrupt Enable */
69f18dbbb1SShiraz Hashim #define RD_STATUS_REG	(0x1 << 10)	/* reads status reg */
70f18dbbb1SShiraz Hashim #define WE		(0x1 << 11)	/* Write Enable */
71f18dbbb1SShiraz Hashim 
72f18dbbb1SShiraz Hashim #define TX_LEN_SHIFT	0
73f18dbbb1SShiraz Hashim #define RX_LEN_SHIFT	4
74f18dbbb1SShiraz Hashim #define BANK_SHIFT	12
75f18dbbb1SShiraz Hashim 
76f18dbbb1SShiraz Hashim /* defines for status register */
77f18dbbb1SShiraz Hashim #define SR_WIP		0x1	/* Write in progress */
78f18dbbb1SShiraz Hashim #define SR_WEL		0x2	/* Write enable latch */
79f18dbbb1SShiraz Hashim #define SR_BP0		0x4	/* Block protect 0 */
80f18dbbb1SShiraz Hashim #define SR_BP1		0x8	/* Block protect 1 */
81f18dbbb1SShiraz Hashim #define SR_BP2		0x10	/* Block protect 2 */
82f18dbbb1SShiraz Hashim #define SR_SRWD		0x80	/* SR write protect */
83f18dbbb1SShiraz Hashim #define TFF		0x100	/* Transfer Finished Flag */
84f18dbbb1SShiraz Hashim #define WCF		0x200	/* Transfer Finished Flag */
85f18dbbb1SShiraz Hashim #define ERF1		0x400	/* Forbidden Write Request */
86f18dbbb1SShiraz Hashim #define ERF2		0x800	/* Forbidden Access */
87f18dbbb1SShiraz Hashim 
88f18dbbb1SShiraz Hashim #define WM_SHIFT	12
89f18dbbb1SShiraz Hashim 
90f18dbbb1SShiraz Hashim /* flash opcodes */
91f18dbbb1SShiraz Hashim #define OPCODE_RDID	0x9f	/* Read JEDEC ID */
92f18dbbb1SShiraz Hashim 
93f18dbbb1SShiraz Hashim /* Flash Device Ids maintenance section */
94f18dbbb1SShiraz Hashim 
95f18dbbb1SShiraz Hashim /* data structure to maintain flash ids from different vendors */
96f18dbbb1SShiraz Hashim struct flash_device {
97f18dbbb1SShiraz Hashim 	char *name;
98f18dbbb1SShiraz Hashim 	u8 erase_cmd;
99f18dbbb1SShiraz Hashim 	u32 device_id;
100f18dbbb1SShiraz Hashim 	u32 pagesize;
101f18dbbb1SShiraz Hashim 	unsigned long sectorsize;
102f18dbbb1SShiraz Hashim 	unsigned long size_in_bytes;
103f18dbbb1SShiraz Hashim };
104f18dbbb1SShiraz Hashim 
105f18dbbb1SShiraz Hashim #define FLASH_ID(n, es, id, psize, ssize, size)	\
106f18dbbb1SShiraz Hashim {				\
107f18dbbb1SShiraz Hashim 	.name = n,		\
108f18dbbb1SShiraz Hashim 	.erase_cmd = es,	\
109f18dbbb1SShiraz Hashim 	.device_id = id,	\
110f18dbbb1SShiraz Hashim 	.pagesize = psize,	\
111f18dbbb1SShiraz Hashim 	.sectorsize = ssize,	\
112f18dbbb1SShiraz Hashim 	.size_in_bytes = size	\
113f18dbbb1SShiraz Hashim }
114f18dbbb1SShiraz Hashim 
115f18dbbb1SShiraz Hashim static struct flash_device flash_devices[] = {
116f18dbbb1SShiraz Hashim 	FLASH_ID("st m25p16"     , 0xd8, 0x00152020, 0x100, 0x10000, 0x200000),
117f18dbbb1SShiraz Hashim 	FLASH_ID("st m25p32"     , 0xd8, 0x00162020, 0x100, 0x10000, 0x400000),
118f18dbbb1SShiraz Hashim 	FLASH_ID("st m25p64"     , 0xd8, 0x00172020, 0x100, 0x10000, 0x800000),
119f18dbbb1SShiraz Hashim 	FLASH_ID("st m25p128"    , 0xd8, 0x00182020, 0x100, 0x40000, 0x1000000),
120f18dbbb1SShiraz Hashim 	FLASH_ID("st m25p05"     , 0xd8, 0x00102020, 0x80 , 0x8000 , 0x10000),
121f18dbbb1SShiraz Hashim 	FLASH_ID("st m25p10"     , 0xd8, 0x00112020, 0x80 , 0x8000 , 0x20000),
122f18dbbb1SShiraz Hashim 	FLASH_ID("st m25p20"     , 0xd8, 0x00122020, 0x100, 0x10000, 0x40000),
123f18dbbb1SShiraz Hashim 	FLASH_ID("st m25p40"     , 0xd8, 0x00132020, 0x100, 0x10000, 0x80000),
124f18dbbb1SShiraz Hashim 	FLASH_ID("st m25p80"     , 0xd8, 0x00142020, 0x100, 0x10000, 0x100000),
125f18dbbb1SShiraz Hashim 	FLASH_ID("st m45pe10"    , 0xd8, 0x00114020, 0x100, 0x10000, 0x20000),
126f18dbbb1SShiraz Hashim 	FLASH_ID("st m45pe20"    , 0xd8, 0x00124020, 0x100, 0x10000, 0x40000),
127f18dbbb1SShiraz Hashim 	FLASH_ID("st m45pe40"    , 0xd8, 0x00134020, 0x100, 0x10000, 0x80000),
128f18dbbb1SShiraz Hashim 	FLASH_ID("st m45pe80"    , 0xd8, 0x00144020, 0x100, 0x10000, 0x100000),
129f18dbbb1SShiraz Hashim 	FLASH_ID("sp s25fl004"   , 0xd8, 0x00120201, 0x100, 0x10000, 0x80000),
130f18dbbb1SShiraz Hashim 	FLASH_ID("sp s25fl008"   , 0xd8, 0x00130201, 0x100, 0x10000, 0x100000),
131f18dbbb1SShiraz Hashim 	FLASH_ID("sp s25fl016"   , 0xd8, 0x00140201, 0x100, 0x10000, 0x200000),
132f18dbbb1SShiraz Hashim 	FLASH_ID("sp s25fl032"   , 0xd8, 0x00150201, 0x100, 0x10000, 0x400000),
133f18dbbb1SShiraz Hashim 	FLASH_ID("sp s25fl064"   , 0xd8, 0x00160201, 0x100, 0x10000, 0x800000),
134f18dbbb1SShiraz Hashim 	FLASH_ID("atmel 25f512"  , 0x52, 0x0065001F, 0x80 , 0x8000 , 0x10000),
135f18dbbb1SShiraz Hashim 	FLASH_ID("atmel 25f1024" , 0x52, 0x0060001F, 0x100, 0x8000 , 0x20000),
136f18dbbb1SShiraz Hashim 	FLASH_ID("atmel 25f2048" , 0x52, 0x0063001F, 0x100, 0x10000, 0x40000),
137f18dbbb1SShiraz Hashim 	FLASH_ID("atmel 25f4096" , 0x52, 0x0064001F, 0x100, 0x10000, 0x80000),
138f18dbbb1SShiraz Hashim 	FLASH_ID("atmel 25fs040" , 0xd7, 0x0004661F, 0x100, 0x10000, 0x80000),
139f18dbbb1SShiraz Hashim 	FLASH_ID("mac 25l512"    , 0xd8, 0x001020C2, 0x010, 0x10000, 0x10000),
140f18dbbb1SShiraz Hashim 	FLASH_ID("mac 25l1005"   , 0xd8, 0x001120C2, 0x010, 0x10000, 0x20000),
141f18dbbb1SShiraz Hashim 	FLASH_ID("mac 25l2005"   , 0xd8, 0x001220C2, 0x010, 0x10000, 0x40000),
142f18dbbb1SShiraz Hashim 	FLASH_ID("mac 25l4005"   , 0xd8, 0x001320C2, 0x010, 0x10000, 0x80000),
143f18dbbb1SShiraz Hashim 	FLASH_ID("mac 25l4005a"  , 0xd8, 0x001320C2, 0x010, 0x10000, 0x80000),
144f18dbbb1SShiraz Hashim 	FLASH_ID("mac 25l8005"   , 0xd8, 0x001420C2, 0x010, 0x10000, 0x100000),
145f18dbbb1SShiraz Hashim 	FLASH_ID("mac 25l1605"   , 0xd8, 0x001520C2, 0x100, 0x10000, 0x200000),
146f18dbbb1SShiraz Hashim 	FLASH_ID("mac 25l1605a"  , 0xd8, 0x001520C2, 0x010, 0x10000, 0x200000),
147f18dbbb1SShiraz Hashim 	FLASH_ID("mac 25l3205"   , 0xd8, 0x001620C2, 0x100, 0x10000, 0x400000),
148f18dbbb1SShiraz Hashim 	FLASH_ID("mac 25l3205a"  , 0xd8, 0x001620C2, 0x100, 0x10000, 0x400000),
149f18dbbb1SShiraz Hashim 	FLASH_ID("mac 25l6405"   , 0xd8, 0x001720C2, 0x100, 0x10000, 0x800000),
150f18dbbb1SShiraz Hashim };
151f18dbbb1SShiraz Hashim 
152f18dbbb1SShiraz Hashim /* Define spear specific structures */
153f18dbbb1SShiraz Hashim 
154f18dbbb1SShiraz Hashim struct spear_snor_flash;
155f18dbbb1SShiraz Hashim 
156f18dbbb1SShiraz Hashim /**
157f18dbbb1SShiraz Hashim  * struct spear_smi - Structure for SMI Device
158f18dbbb1SShiraz Hashim  *
159f18dbbb1SShiraz Hashim  * @clk: functional clock
160f18dbbb1SShiraz Hashim  * @status: current status register of SMI.
161f18dbbb1SShiraz Hashim  * @clk_rate: functional clock rate of SMI (default: SMI_MAX_CLOCK_FREQ)
162f18dbbb1SShiraz Hashim  * @lock: lock to prevent parallel access of SMI.
163f18dbbb1SShiraz Hashim  * @io_base: base address for registers of SMI.
164f18dbbb1SShiraz Hashim  * @pdev: platform device
165f18dbbb1SShiraz Hashim  * @cmd_complete: queue to wait for command completion of NOR-flash.
166f18dbbb1SShiraz Hashim  * @num_flashes: number of flashes actually present on board.
167f18dbbb1SShiraz Hashim  * @flash: separate structure for each Serial NOR-flash attached to SMI.
168f18dbbb1SShiraz Hashim  */
169f18dbbb1SShiraz Hashim struct spear_smi {
170f18dbbb1SShiraz Hashim 	struct clk *clk;
171f18dbbb1SShiraz Hashim 	u32 status;
172f18dbbb1SShiraz Hashim 	unsigned long clk_rate;
173f18dbbb1SShiraz Hashim 	struct mutex lock;
174f18dbbb1SShiraz Hashim 	void __iomem *io_base;
175f18dbbb1SShiraz Hashim 	struct platform_device *pdev;
176f18dbbb1SShiraz Hashim 	wait_queue_head_t cmd_complete;
177f18dbbb1SShiraz Hashim 	u32 num_flashes;
178f18dbbb1SShiraz Hashim 	struct spear_snor_flash *flash[MAX_NUM_FLASH_CHIP];
179f18dbbb1SShiraz Hashim };
180f18dbbb1SShiraz Hashim 
181f18dbbb1SShiraz Hashim /**
182f18dbbb1SShiraz Hashim  * struct spear_snor_flash - Structure for Serial NOR Flash
183f18dbbb1SShiraz Hashim  *
184f18dbbb1SShiraz Hashim  * @bank: Bank number(0, 1, 2, 3) for each NOR-flash.
185f18dbbb1SShiraz Hashim  * @dev_id: Device ID of NOR-flash.
186f18dbbb1SShiraz Hashim  * @lock: lock to manage flash read, write and erase operations
187f18dbbb1SShiraz Hashim  * @mtd: MTD info for each NOR-flash.
188f18dbbb1SShiraz Hashim  * @num_parts: Total number of partition in each bank of NOR-flash.
189f18dbbb1SShiraz Hashim  * @parts: Partition info for each bank of NOR-flash.
190f18dbbb1SShiraz Hashim  * @page_size: Page size of NOR-flash.
191f18dbbb1SShiraz Hashim  * @base_addr: Base address of NOR-flash.
192f18dbbb1SShiraz Hashim  * @erase_cmd: erase command may vary on different flash types
193f18dbbb1SShiraz Hashim  * @fast_mode: flash supports read in fast mode
194f18dbbb1SShiraz Hashim  */
195f18dbbb1SShiraz Hashim struct spear_snor_flash {
196f18dbbb1SShiraz Hashim 	u32 bank;
197f18dbbb1SShiraz Hashim 	u32 dev_id;
198f18dbbb1SShiraz Hashim 	struct mutex lock;
199f18dbbb1SShiraz Hashim 	struct mtd_info mtd;
200f18dbbb1SShiraz Hashim 	u32 num_parts;
201f18dbbb1SShiraz Hashim 	struct mtd_partition *parts;
202f18dbbb1SShiraz Hashim 	u32 page_size;
203f18dbbb1SShiraz Hashim 	void __iomem *base_addr;
204f18dbbb1SShiraz Hashim 	u8 erase_cmd;
205f18dbbb1SShiraz Hashim 	u8 fast_mode;
206f18dbbb1SShiraz Hashim };
207f18dbbb1SShiraz Hashim 
208f18dbbb1SShiraz Hashim static inline struct spear_snor_flash *get_flash_data(struct mtd_info *mtd)
209f18dbbb1SShiraz Hashim {
210f18dbbb1SShiraz Hashim 	return container_of(mtd, struct spear_snor_flash, mtd);
211f18dbbb1SShiraz Hashim }
212f18dbbb1SShiraz Hashim 
213f18dbbb1SShiraz Hashim /**
214f18dbbb1SShiraz Hashim  * spear_smi_read_sr - Read status register of flash through SMI
215f18dbbb1SShiraz Hashim  * @dev: structure of SMI information.
216f18dbbb1SShiraz Hashim  * @bank: bank to which flash is connected
217f18dbbb1SShiraz Hashim  *
218f18dbbb1SShiraz Hashim  * This routine will return the status register of the flash chip present at the
219f18dbbb1SShiraz Hashim  * given bank.
220f18dbbb1SShiraz Hashim  */
221f18dbbb1SShiraz Hashim static int spear_smi_read_sr(struct spear_smi *dev, u32 bank)
222f18dbbb1SShiraz Hashim {
223f18dbbb1SShiraz Hashim 	int ret;
224f18dbbb1SShiraz Hashim 	u32 ctrlreg1;
225f18dbbb1SShiraz Hashim 
226f18dbbb1SShiraz Hashim 	mutex_lock(&dev->lock);
227f18dbbb1SShiraz Hashim 	dev->status = 0; /* Will be set in interrupt handler */
228f18dbbb1SShiraz Hashim 
229f18dbbb1SShiraz Hashim 	ctrlreg1 = readl(dev->io_base + SMI_CR1);
230f18dbbb1SShiraz Hashim 	/* program smi in hw mode */
231f18dbbb1SShiraz Hashim 	writel(ctrlreg1 & ~(SW_MODE | WB_MODE), dev->io_base + SMI_CR1);
232f18dbbb1SShiraz Hashim 
233f18dbbb1SShiraz Hashim 	/* performing a rsr instruction in hw mode */
234f18dbbb1SShiraz Hashim 	writel((bank << BANK_SHIFT) | RD_STATUS_REG | TFIE,
235f18dbbb1SShiraz Hashim 			dev->io_base + SMI_CR2);
236f18dbbb1SShiraz Hashim 
237f18dbbb1SShiraz Hashim 	/* wait for tff */
238f18dbbb1SShiraz Hashim 	ret = wait_event_interruptible_timeout(dev->cmd_complete,
239f18dbbb1SShiraz Hashim 			dev->status & TFF, SMI_CMD_TIMEOUT);
240f18dbbb1SShiraz Hashim 
241f18dbbb1SShiraz Hashim 	/* copy dev->status (lower 16 bits) in order to release lock */
242f18dbbb1SShiraz Hashim 	if (ret > 0)
243f18dbbb1SShiraz Hashim 		ret = dev->status & 0xffff;
2442c99b8bfSVipin Kumar 	else if (ret == 0)
2452c99b8bfSVipin Kumar 		ret = -ETIMEDOUT;
246f18dbbb1SShiraz Hashim 
247f18dbbb1SShiraz Hashim 	/* restore the ctrl regs state */
248f18dbbb1SShiraz Hashim 	writel(ctrlreg1, dev->io_base + SMI_CR1);
249f18dbbb1SShiraz Hashim 	writel(0, dev->io_base + SMI_CR2);
250f18dbbb1SShiraz Hashim 	mutex_unlock(&dev->lock);
251f18dbbb1SShiraz Hashim 
252f18dbbb1SShiraz Hashim 	return ret;
253f18dbbb1SShiraz Hashim }
254f18dbbb1SShiraz Hashim 
255f18dbbb1SShiraz Hashim /**
256f18dbbb1SShiraz Hashim  * spear_smi_wait_till_ready - wait till flash is ready
257f18dbbb1SShiraz Hashim  * @dev: structure of SMI information.
258f18dbbb1SShiraz Hashim  * @bank: flash corresponding to this bank
259f18dbbb1SShiraz Hashim  * @timeout: timeout for busy wait condition
260f18dbbb1SShiraz Hashim  *
261f18dbbb1SShiraz Hashim  * This routine checks for WIP (write in progress) bit in Status register
262f18dbbb1SShiraz Hashim  * If successful the routine returns 0 else -EBUSY
263f18dbbb1SShiraz Hashim  */
264f18dbbb1SShiraz Hashim static int spear_smi_wait_till_ready(struct spear_smi *dev, u32 bank,
265f18dbbb1SShiraz Hashim 		unsigned long timeout)
266f18dbbb1SShiraz Hashim {
267f18dbbb1SShiraz Hashim 	unsigned long finish;
268f18dbbb1SShiraz Hashim 	int status;
269f18dbbb1SShiraz Hashim 
270f18dbbb1SShiraz Hashim 	finish = jiffies + timeout;
271f18dbbb1SShiraz Hashim 	do {
272f18dbbb1SShiraz Hashim 		status = spear_smi_read_sr(dev, bank);
2732c99b8bfSVipin Kumar 		if (status < 0) {
2742c99b8bfSVipin Kumar 			if (status == -ETIMEDOUT)
2752c99b8bfSVipin Kumar 				continue; /* try till finish */
2762c99b8bfSVipin Kumar 			return status;
2772c99b8bfSVipin Kumar 		} else if (!(status & SR_WIP)) {
278f18dbbb1SShiraz Hashim 			return 0;
2792c99b8bfSVipin Kumar 		}
280f18dbbb1SShiraz Hashim 
281f18dbbb1SShiraz Hashim 		cond_resched();
282f18dbbb1SShiraz Hashim 	} while (!time_after_eq(jiffies, finish));
283f18dbbb1SShiraz Hashim 
284f18dbbb1SShiraz Hashim 	dev_err(&dev->pdev->dev, "smi controller is busy, timeout\n");
2852c99b8bfSVipin Kumar 	return -EBUSY;
286f18dbbb1SShiraz Hashim }
287f18dbbb1SShiraz Hashim 
288f18dbbb1SShiraz Hashim /**
289f18dbbb1SShiraz Hashim  * spear_smi_int_handler - SMI Interrupt Handler.
290f18dbbb1SShiraz Hashim  * @irq: irq number
291f18dbbb1SShiraz Hashim  * @dev_id: structure of SMI device, embedded in dev_id.
292f18dbbb1SShiraz Hashim  *
293f18dbbb1SShiraz Hashim  * The handler clears all interrupt conditions and records the status in
294f18dbbb1SShiraz Hashim  * dev->status which is used by the driver later.
295f18dbbb1SShiraz Hashim  */
296f18dbbb1SShiraz Hashim static irqreturn_t spear_smi_int_handler(int irq, void *dev_id)
297f18dbbb1SShiraz Hashim {
298f18dbbb1SShiraz Hashim 	u32 status = 0;
299f18dbbb1SShiraz Hashim 	struct spear_smi *dev = dev_id;
300f18dbbb1SShiraz Hashim 
301f18dbbb1SShiraz Hashim 	status = readl(dev->io_base + SMI_SR);
302f18dbbb1SShiraz Hashim 
303f18dbbb1SShiraz Hashim 	if (unlikely(!status))
304f18dbbb1SShiraz Hashim 		return IRQ_NONE;
305f18dbbb1SShiraz Hashim 
306f18dbbb1SShiraz Hashim 	/* clear all interrupt conditions */
307f18dbbb1SShiraz Hashim 	writel(0, dev->io_base + SMI_SR);
308f18dbbb1SShiraz Hashim 
309f18dbbb1SShiraz Hashim 	/* copy the status register in dev->status */
310f18dbbb1SShiraz Hashim 	dev->status |= status;
311f18dbbb1SShiraz Hashim 
312f18dbbb1SShiraz Hashim 	/* send the completion */
313f18dbbb1SShiraz Hashim 	wake_up_interruptible(&dev->cmd_complete);
314f18dbbb1SShiraz Hashim 
315f18dbbb1SShiraz Hashim 	return IRQ_HANDLED;
316f18dbbb1SShiraz Hashim }
317f18dbbb1SShiraz Hashim 
318f18dbbb1SShiraz Hashim /**
319f18dbbb1SShiraz Hashim  * spear_smi_hw_init - initializes the smi controller.
320f18dbbb1SShiraz Hashim  * @dev: structure of smi device
321f18dbbb1SShiraz Hashim  *
322f18dbbb1SShiraz Hashim  * this routine initializes the smi controller wit the default values
323f18dbbb1SShiraz Hashim  */
324f18dbbb1SShiraz Hashim static void spear_smi_hw_init(struct spear_smi *dev)
325f18dbbb1SShiraz Hashim {
326f18dbbb1SShiraz Hashim 	unsigned long rate = 0;
327f18dbbb1SShiraz Hashim 	u32 prescale = 0;
328f18dbbb1SShiraz Hashim 	u32 val;
329f18dbbb1SShiraz Hashim 
330f18dbbb1SShiraz Hashim 	rate = clk_get_rate(dev->clk);
331f18dbbb1SShiraz Hashim 
332f18dbbb1SShiraz Hashim 	/* functional clock of smi */
333f18dbbb1SShiraz Hashim 	prescale = DIV_ROUND_UP(rate, dev->clk_rate);
334f18dbbb1SShiraz Hashim 
335f18dbbb1SShiraz Hashim 	/*
336f18dbbb1SShiraz Hashim 	 * setting the standard values, fast mode, prescaler for
337f18dbbb1SShiraz Hashim 	 * SMI_MAX_CLOCK_FREQ (50MHz) operation and bank enable
338f18dbbb1SShiraz Hashim 	 */
339f18dbbb1SShiraz Hashim 	val = HOLD1 | BANK_EN | DSEL_TIME | (prescale << 8);
340f18dbbb1SShiraz Hashim 
341f18dbbb1SShiraz Hashim 	mutex_lock(&dev->lock);
3424dc48c37SShiraz Hashim 	/* clear all interrupt conditions */
3434dc48c37SShiraz Hashim 	writel(0, dev->io_base + SMI_SR);
3444dc48c37SShiraz Hashim 
345f18dbbb1SShiraz Hashim 	writel(val, dev->io_base + SMI_CR1);
346f18dbbb1SShiraz Hashim 	mutex_unlock(&dev->lock);
347f18dbbb1SShiraz Hashim }
348f18dbbb1SShiraz Hashim 
349f18dbbb1SShiraz Hashim /**
350f18dbbb1SShiraz Hashim  * get_flash_index - match chip id from a flash list.
351f18dbbb1SShiraz Hashim  * @flash_id: a valid nor flash chip id obtained from board.
352f18dbbb1SShiraz Hashim  *
353f18dbbb1SShiraz Hashim  * try to validate the chip id by matching from a list, if not found then simply
354f18dbbb1SShiraz Hashim  * returns negative. In case of success returns index in to the flash devices
355f18dbbb1SShiraz Hashim  * array.
356f18dbbb1SShiraz Hashim  */
357f18dbbb1SShiraz Hashim static int get_flash_index(u32 flash_id)
358f18dbbb1SShiraz Hashim {
359f18dbbb1SShiraz Hashim 	int index;
360f18dbbb1SShiraz Hashim 
361f18dbbb1SShiraz Hashim 	/* Matches chip-id to entire list of 'serial-nor flash' ids */
362f18dbbb1SShiraz Hashim 	for (index = 0; index < ARRAY_SIZE(flash_devices); index++) {
363f18dbbb1SShiraz Hashim 		if (flash_devices[index].device_id == flash_id)
364f18dbbb1SShiraz Hashim 			return index;
365f18dbbb1SShiraz Hashim 	}
366f18dbbb1SShiraz Hashim 
367f18dbbb1SShiraz Hashim 	/* Memory chip is not listed and not supported */
368f18dbbb1SShiraz Hashim 	return -ENODEV;
369f18dbbb1SShiraz Hashim }
370f18dbbb1SShiraz Hashim 
371f18dbbb1SShiraz Hashim /**
372f18dbbb1SShiraz Hashim  * spear_smi_write_enable - Enable the flash to do write operation
373f18dbbb1SShiraz Hashim  * @dev: structure of SMI device
374f18dbbb1SShiraz Hashim  * @bank: enable write for flash connected to this bank
375f18dbbb1SShiraz Hashim  *
376f18dbbb1SShiraz Hashim  * Set write enable latch with Write Enable command.
377f18dbbb1SShiraz Hashim  * Returns 0 on success.
378f18dbbb1SShiraz Hashim  */
379f18dbbb1SShiraz Hashim static int spear_smi_write_enable(struct spear_smi *dev, u32 bank)
380f18dbbb1SShiraz Hashim {
381f18dbbb1SShiraz Hashim 	int ret;
382f18dbbb1SShiraz Hashim 	u32 ctrlreg1;
383f18dbbb1SShiraz Hashim 
384f18dbbb1SShiraz Hashim 	mutex_lock(&dev->lock);
385f18dbbb1SShiraz Hashim 	dev->status = 0; /* Will be set in interrupt handler */
386f18dbbb1SShiraz Hashim 
387f18dbbb1SShiraz Hashim 	ctrlreg1 = readl(dev->io_base + SMI_CR1);
388f18dbbb1SShiraz Hashim 	/* program smi in h/w mode */
389f18dbbb1SShiraz Hashim 	writel(ctrlreg1 & ~SW_MODE, dev->io_base + SMI_CR1);
390f18dbbb1SShiraz Hashim 
391f18dbbb1SShiraz Hashim 	/* give the flash, write enable command */
392f18dbbb1SShiraz Hashim 	writel((bank << BANK_SHIFT) | WE | TFIE, dev->io_base + SMI_CR2);
393f18dbbb1SShiraz Hashim 
394f18dbbb1SShiraz Hashim 	ret = wait_event_interruptible_timeout(dev->cmd_complete,
395f18dbbb1SShiraz Hashim 			dev->status & TFF, SMI_CMD_TIMEOUT);
396f18dbbb1SShiraz Hashim 
397f18dbbb1SShiraz Hashim 	/* restore the ctrl regs state */
398f18dbbb1SShiraz Hashim 	writel(ctrlreg1, dev->io_base + SMI_CR1);
399f18dbbb1SShiraz Hashim 	writel(0, dev->io_base + SMI_CR2);
400f18dbbb1SShiraz Hashim 
4012c99b8bfSVipin Kumar 	if (ret == 0) {
402f18dbbb1SShiraz Hashim 		ret = -EIO;
403f18dbbb1SShiraz Hashim 		dev_err(&dev->pdev->dev,
404f18dbbb1SShiraz Hashim 			"smi controller failed on write enable\n");
4052c99b8bfSVipin Kumar 	} else if (ret > 0) {
406f18dbbb1SShiraz Hashim 		/* check whether write mode status is set for required bank */
407f18dbbb1SShiraz Hashim 		if (dev->status & (1 << (bank + WM_SHIFT)))
408f18dbbb1SShiraz Hashim 			ret = 0;
409f18dbbb1SShiraz Hashim 		else {
410f18dbbb1SShiraz Hashim 			dev_err(&dev->pdev->dev, "couldn't enable write\n");
411f18dbbb1SShiraz Hashim 			ret = -EIO;
412f18dbbb1SShiraz Hashim 		}
413f18dbbb1SShiraz Hashim 	}
414f18dbbb1SShiraz Hashim 
415f18dbbb1SShiraz Hashim 	mutex_unlock(&dev->lock);
416f18dbbb1SShiraz Hashim 	return ret;
417f18dbbb1SShiraz Hashim }
418f18dbbb1SShiraz Hashim 
419f18dbbb1SShiraz Hashim static inline u32
420f18dbbb1SShiraz Hashim get_sector_erase_cmd(struct spear_snor_flash *flash, u32 offset)
421f18dbbb1SShiraz Hashim {
422f18dbbb1SShiraz Hashim 	u32 cmd;
423f18dbbb1SShiraz Hashim 	u8 *x = (u8 *)&cmd;
424f18dbbb1SShiraz Hashim 
425f18dbbb1SShiraz Hashim 	x[0] = flash->erase_cmd;
426f18dbbb1SShiraz Hashim 	x[1] = offset >> 16;
427f18dbbb1SShiraz Hashim 	x[2] = offset >> 8;
428f18dbbb1SShiraz Hashim 	x[3] = offset;
429f18dbbb1SShiraz Hashim 
430f18dbbb1SShiraz Hashim 	return cmd;
431f18dbbb1SShiraz Hashim }
432f18dbbb1SShiraz Hashim 
433f18dbbb1SShiraz Hashim /**
434f18dbbb1SShiraz Hashim  * spear_smi_erase_sector - erase one sector of flash
435f18dbbb1SShiraz Hashim  * @dev: structure of SMI information
436f18dbbb1SShiraz Hashim  * @command: erase command to be send
437f18dbbb1SShiraz Hashim  * @bank: bank to which this command needs to be send
438f18dbbb1SShiraz Hashim  * @bytes: size of command
439f18dbbb1SShiraz Hashim  *
440f18dbbb1SShiraz Hashim  * Erase one sector of flash memory at offset ``offset'' which is any
441f18dbbb1SShiraz Hashim  * address within the sector which should be erased.
442f18dbbb1SShiraz Hashim  * Returns 0 if successful, non-zero otherwise.
443f18dbbb1SShiraz Hashim  */
444f18dbbb1SShiraz Hashim static int spear_smi_erase_sector(struct spear_smi *dev,
445f18dbbb1SShiraz Hashim 		u32 bank, u32 command, u32 bytes)
446f18dbbb1SShiraz Hashim {
447f18dbbb1SShiraz Hashim 	u32 ctrlreg1 = 0;
448f18dbbb1SShiraz Hashim 	int ret;
449f18dbbb1SShiraz Hashim 
450f18dbbb1SShiraz Hashim 	ret = spear_smi_wait_till_ready(dev, bank, SMI_MAX_TIME_OUT);
451f18dbbb1SShiraz Hashim 	if (ret)
452f18dbbb1SShiraz Hashim 		return ret;
453f18dbbb1SShiraz Hashim 
454f18dbbb1SShiraz Hashim 	ret = spear_smi_write_enable(dev, bank);
455f18dbbb1SShiraz Hashim 	if (ret)
456f18dbbb1SShiraz Hashim 		return ret;
457f18dbbb1SShiraz Hashim 
458f18dbbb1SShiraz Hashim 	mutex_lock(&dev->lock);
459f18dbbb1SShiraz Hashim 
460f18dbbb1SShiraz Hashim 	ctrlreg1 = readl(dev->io_base + SMI_CR1);
461f18dbbb1SShiraz Hashim 	writel((ctrlreg1 | SW_MODE) & ~WB_MODE, dev->io_base + SMI_CR1);
462f18dbbb1SShiraz Hashim 
463f18dbbb1SShiraz Hashim 	/* send command in sw mode */
464f18dbbb1SShiraz Hashim 	writel(command, dev->io_base + SMI_TR);
465f18dbbb1SShiraz Hashim 
466f18dbbb1SShiraz Hashim 	writel((bank << BANK_SHIFT) | SEND | TFIE | (bytes << TX_LEN_SHIFT),
467f18dbbb1SShiraz Hashim 			dev->io_base + SMI_CR2);
468f18dbbb1SShiraz Hashim 
469f18dbbb1SShiraz Hashim 	ret = wait_event_interruptible_timeout(dev->cmd_complete,
470f18dbbb1SShiraz Hashim 			dev->status & TFF, SMI_CMD_TIMEOUT);
471f18dbbb1SShiraz Hashim 
4722c99b8bfSVipin Kumar 	if (ret == 0) {
473f18dbbb1SShiraz Hashim 		ret = -EIO;
474f18dbbb1SShiraz Hashim 		dev_err(&dev->pdev->dev, "sector erase failed\n");
4752c99b8bfSVipin Kumar 	} else if (ret > 0)
476f18dbbb1SShiraz Hashim 		ret = 0; /* success */
477f18dbbb1SShiraz Hashim 
478f18dbbb1SShiraz Hashim 	/* restore ctrl regs */
479f18dbbb1SShiraz Hashim 	writel(ctrlreg1, dev->io_base + SMI_CR1);
480f18dbbb1SShiraz Hashim 	writel(0, dev->io_base + SMI_CR2);
481f18dbbb1SShiraz Hashim 
482f18dbbb1SShiraz Hashim 	mutex_unlock(&dev->lock);
483f18dbbb1SShiraz Hashim 	return ret;
484f18dbbb1SShiraz Hashim }
485f18dbbb1SShiraz Hashim 
486f18dbbb1SShiraz Hashim /**
487f18dbbb1SShiraz Hashim  * spear_mtd_erase - perform flash erase operation as requested by user
488f18dbbb1SShiraz Hashim  * @mtd: Provides the memory characteristics
489f18dbbb1SShiraz Hashim  * @e_info: Provides the erase information
490f18dbbb1SShiraz Hashim  *
491f18dbbb1SShiraz Hashim  * Erase an address range on the flash chip. The address range may extend
492f18dbbb1SShiraz Hashim  * one or more erase sectors. Return an error is there is a problem erasing.
493f18dbbb1SShiraz Hashim  */
494f18dbbb1SShiraz Hashim static int spear_mtd_erase(struct mtd_info *mtd, struct erase_info *e_info)
495f18dbbb1SShiraz Hashim {
496f18dbbb1SShiraz Hashim 	struct spear_snor_flash *flash = get_flash_data(mtd);
497f18dbbb1SShiraz Hashim 	struct spear_smi *dev = mtd->priv;
498f18dbbb1SShiraz Hashim 	u32 addr, command, bank;
499f18dbbb1SShiraz Hashim 	int len, ret;
500f18dbbb1SShiraz Hashim 
501f18dbbb1SShiraz Hashim 	if (!flash || !dev)
502f18dbbb1SShiraz Hashim 		return -ENODEV;
503f18dbbb1SShiraz Hashim 
504f18dbbb1SShiraz Hashim 	bank = flash->bank;
505f18dbbb1SShiraz Hashim 	if (bank > dev->num_flashes - 1) {
506f18dbbb1SShiraz Hashim 		dev_err(&dev->pdev->dev, "Invalid Bank Num");
507f18dbbb1SShiraz Hashim 		return -EINVAL;
508f18dbbb1SShiraz Hashim 	}
509f18dbbb1SShiraz Hashim 
510f18dbbb1SShiraz Hashim 	addr = e_info->addr;
511f18dbbb1SShiraz Hashim 	len = e_info->len;
512f18dbbb1SShiraz Hashim 
513f18dbbb1SShiraz Hashim 	mutex_lock(&flash->lock);
514f18dbbb1SShiraz Hashim 
515f18dbbb1SShiraz Hashim 	/* now erase sectors in loop */
516f18dbbb1SShiraz Hashim 	while (len) {
517f18dbbb1SShiraz Hashim 		command = get_sector_erase_cmd(flash, addr);
518f18dbbb1SShiraz Hashim 		/* preparing the command for flash */
519f18dbbb1SShiraz Hashim 		ret = spear_smi_erase_sector(dev, bank, command, 4);
520f18dbbb1SShiraz Hashim 		if (ret) {
521f18dbbb1SShiraz Hashim 			mutex_unlock(&flash->lock);
522f18dbbb1SShiraz Hashim 			return ret;
523f18dbbb1SShiraz Hashim 		}
524f18dbbb1SShiraz Hashim 		addr += mtd->erasesize;
525f18dbbb1SShiraz Hashim 		len -= mtd->erasesize;
526f18dbbb1SShiraz Hashim 	}
527f18dbbb1SShiraz Hashim 
528f18dbbb1SShiraz Hashim 	mutex_unlock(&flash->lock);
529f18dbbb1SShiraz Hashim 
530f18dbbb1SShiraz Hashim 	return 0;
531f18dbbb1SShiraz Hashim }
532f18dbbb1SShiraz Hashim 
533f18dbbb1SShiraz Hashim /**
534f18dbbb1SShiraz Hashim  * spear_mtd_read - performs flash read operation as requested by the user
535f18dbbb1SShiraz Hashim  * @mtd: MTD information of the memory bank
536f18dbbb1SShiraz Hashim  * @from: Address from which to start read
537f18dbbb1SShiraz Hashim  * @len: Number of bytes to be read
538f18dbbb1SShiraz Hashim  * @retlen: Fills the Number of bytes actually read
539f18dbbb1SShiraz Hashim  * @buf: Fills this after reading
540f18dbbb1SShiraz Hashim  *
541f18dbbb1SShiraz Hashim  * Read an address range from the flash chip. The address range
542f18dbbb1SShiraz Hashim  * may be any size provided it is within the physical boundaries.
543f18dbbb1SShiraz Hashim  * Returns 0 on success, non zero otherwise
544f18dbbb1SShiraz Hashim  */
545f18dbbb1SShiraz Hashim static int spear_mtd_read(struct mtd_info *mtd, loff_t from, size_t len,
546f18dbbb1SShiraz Hashim 		size_t *retlen, u8 *buf)
547f18dbbb1SShiraz Hashim {
548f18dbbb1SShiraz Hashim 	struct spear_snor_flash *flash = get_flash_data(mtd);
549f18dbbb1SShiraz Hashim 	struct spear_smi *dev = mtd->priv;
550e51fb2cbSJingoo Han 	void __iomem *src;
551f18dbbb1SShiraz Hashim 	u32 ctrlreg1, val;
552f18dbbb1SShiraz Hashim 	int ret;
553f18dbbb1SShiraz Hashim 
554f18dbbb1SShiraz Hashim 	if (!flash || !dev)
555f18dbbb1SShiraz Hashim 		return -ENODEV;
556f18dbbb1SShiraz Hashim 
557f18dbbb1SShiraz Hashim 	if (flash->bank > dev->num_flashes - 1) {
558f18dbbb1SShiraz Hashim 		dev_err(&dev->pdev->dev, "Invalid Bank Num");
559f18dbbb1SShiraz Hashim 		return -EINVAL;
560f18dbbb1SShiraz Hashim 	}
561f18dbbb1SShiraz Hashim 
562f18dbbb1SShiraz Hashim 	/* select address as per bank number */
563f18dbbb1SShiraz Hashim 	src = flash->base_addr + from;
564f18dbbb1SShiraz Hashim 
565f18dbbb1SShiraz Hashim 	mutex_lock(&flash->lock);
566f18dbbb1SShiraz Hashim 
567f18dbbb1SShiraz Hashim 	/* wait till previous write/erase is done. */
568f18dbbb1SShiraz Hashim 	ret = spear_smi_wait_till_ready(dev, flash->bank, SMI_MAX_TIME_OUT);
569f18dbbb1SShiraz Hashim 	if (ret) {
570f18dbbb1SShiraz Hashim 		mutex_unlock(&flash->lock);
571f18dbbb1SShiraz Hashim 		return ret;
572f18dbbb1SShiraz Hashim 	}
573f18dbbb1SShiraz Hashim 
574f18dbbb1SShiraz Hashim 	mutex_lock(&dev->lock);
575f18dbbb1SShiraz Hashim 	/* put smi in hw mode not wbt mode */
576f18dbbb1SShiraz Hashim 	ctrlreg1 = val = readl(dev->io_base + SMI_CR1);
577f18dbbb1SShiraz Hashim 	val &= ~(SW_MODE | WB_MODE);
578f18dbbb1SShiraz Hashim 	if (flash->fast_mode)
579f18dbbb1SShiraz Hashim 		val |= FAST_MODE;
580f18dbbb1SShiraz Hashim 
581f18dbbb1SShiraz Hashim 	writel(val, dev->io_base + SMI_CR1);
582f18dbbb1SShiraz Hashim 
583e51fb2cbSJingoo Han 	memcpy_fromio(buf, src, len);
584f18dbbb1SShiraz Hashim 
585f18dbbb1SShiraz Hashim 	/* restore ctrl reg1 */
586f18dbbb1SShiraz Hashim 	writel(ctrlreg1, dev->io_base + SMI_CR1);
587f18dbbb1SShiraz Hashim 	mutex_unlock(&dev->lock);
588f18dbbb1SShiraz Hashim 
589f18dbbb1SShiraz Hashim 	*retlen = len;
590f18dbbb1SShiraz Hashim 	mutex_unlock(&flash->lock);
591f18dbbb1SShiraz Hashim 
592f18dbbb1SShiraz Hashim 	return 0;
593f18dbbb1SShiraz Hashim }
594f18dbbb1SShiraz Hashim 
59569c7f461SMiquel Raynal /*
59669c7f461SMiquel Raynal  * The purpose of this function is to ensure a memcpy_toio() with byte writes
59769c7f461SMiquel Raynal  * only. Its structure is inspired from the ARM implementation of _memcpy_toio()
59869c7f461SMiquel Raynal  * which also does single byte writes but cannot be used here as this is just an
59969c7f461SMiquel Raynal  * implementation detail and not part of the API. Not mentioning the comment
60069c7f461SMiquel Raynal  * stating that _memcpy_toio() should be optimized.
60169c7f461SMiquel Raynal  */
60269c7f461SMiquel Raynal static void spear_smi_memcpy_toio_b(volatile void __iomem *dest,
60369c7f461SMiquel Raynal 				    const void *src, size_t len)
60469c7f461SMiquel Raynal {
60569c7f461SMiquel Raynal 	const unsigned char *from = src;
60669c7f461SMiquel Raynal 
60769c7f461SMiquel Raynal 	while (len) {
60869c7f461SMiquel Raynal 		len--;
60969c7f461SMiquel Raynal 		writeb(*from, dest);
61069c7f461SMiquel Raynal 		from++;
61169c7f461SMiquel Raynal 		dest++;
61269c7f461SMiquel Raynal 	}
61369c7f461SMiquel Raynal }
61469c7f461SMiquel Raynal 
615f18dbbb1SShiraz Hashim static inline int spear_smi_cpy_toio(struct spear_smi *dev, u32 bank,
616e51fb2cbSJingoo Han 		void __iomem *dest, const void *src, size_t len)
617f18dbbb1SShiraz Hashim {
618f18dbbb1SShiraz Hashim 	int ret;
619f18dbbb1SShiraz Hashim 	u32 ctrlreg1;
620f18dbbb1SShiraz Hashim 
621f18dbbb1SShiraz Hashim 	/* wait until finished previous write command. */
622f18dbbb1SShiraz Hashim 	ret = spear_smi_wait_till_ready(dev, bank, SMI_MAX_TIME_OUT);
623f18dbbb1SShiraz Hashim 	if (ret)
624f18dbbb1SShiraz Hashim 		return ret;
625f18dbbb1SShiraz Hashim 
626f18dbbb1SShiraz Hashim 	/* put smi in write enable */
627f18dbbb1SShiraz Hashim 	ret = spear_smi_write_enable(dev, bank);
628f18dbbb1SShiraz Hashim 	if (ret)
629f18dbbb1SShiraz Hashim 		return ret;
630f18dbbb1SShiraz Hashim 
631f18dbbb1SShiraz Hashim 	/* put smi in hw, write burst mode */
632f18dbbb1SShiraz Hashim 	mutex_lock(&dev->lock);
633f18dbbb1SShiraz Hashim 
634f18dbbb1SShiraz Hashim 	ctrlreg1 = readl(dev->io_base + SMI_CR1);
635f18dbbb1SShiraz Hashim 	writel((ctrlreg1 | WB_MODE) & ~SW_MODE, dev->io_base + SMI_CR1);
636f18dbbb1SShiraz Hashim 
63769c7f461SMiquel Raynal 	/*
63869c7f461SMiquel Raynal 	 * In Write Burst mode (WB_MODE), the specs states that writes must be:
63969c7f461SMiquel Raynal 	 * - incremental
64069c7f461SMiquel Raynal 	 * - of the same size
64169c7f461SMiquel Raynal 	 * The ARM implementation of memcpy_toio() will optimize the number of
64269c7f461SMiquel Raynal 	 * I/O by using as much 4-byte writes as possible, surrounded by
64369c7f461SMiquel Raynal 	 * 2-byte/1-byte access if:
64469c7f461SMiquel Raynal 	 * - the destination is not 4-byte aligned
64569c7f461SMiquel Raynal 	 * - the length is not a multiple of 4-byte.
64669c7f461SMiquel Raynal 	 * Avoid this alternance of write access size by using our own 'byte
64769c7f461SMiquel Raynal 	 * access' helper if at least one of the two conditions above is true.
64869c7f461SMiquel Raynal 	 */
64969c7f461SMiquel Raynal 	if (IS_ALIGNED(len, sizeof(u32)) &&
65069c7f461SMiquel Raynal 	    IS_ALIGNED((uintptr_t)dest, sizeof(u32)))
651f18dbbb1SShiraz Hashim 		memcpy_toio(dest, src, len);
65269c7f461SMiquel Raynal 	else
65369c7f461SMiquel Raynal 		spear_smi_memcpy_toio_b(dest, src, len);
654f18dbbb1SShiraz Hashim 
655f18dbbb1SShiraz Hashim 	writel(ctrlreg1, dev->io_base + SMI_CR1);
656f18dbbb1SShiraz Hashim 
657f18dbbb1SShiraz Hashim 	mutex_unlock(&dev->lock);
658f18dbbb1SShiraz Hashim 	return 0;
659f18dbbb1SShiraz Hashim }
660f18dbbb1SShiraz Hashim 
661f18dbbb1SShiraz Hashim /**
662f18dbbb1SShiraz Hashim  * spear_mtd_write - performs write operation as requested by the user.
663f18dbbb1SShiraz Hashim  * @mtd: MTD information of the memory bank.
664f18dbbb1SShiraz Hashim  * @to:	Address to write.
665f18dbbb1SShiraz Hashim  * @len: Number of bytes to be written.
666f18dbbb1SShiraz Hashim  * @retlen: Number of bytes actually wrote.
667f18dbbb1SShiraz Hashim  * @buf: Buffer from which the data to be taken.
668f18dbbb1SShiraz Hashim  *
669f18dbbb1SShiraz Hashim  * Write an address range to the flash chip. Data must be written in
670f18dbbb1SShiraz Hashim  * flash_page_size chunks. The address range may be any size provided
671f18dbbb1SShiraz Hashim  * it is within the physical boundaries.
672f18dbbb1SShiraz Hashim  * Returns 0 on success, non zero otherwise
673f18dbbb1SShiraz Hashim  */
674f18dbbb1SShiraz Hashim static int spear_mtd_write(struct mtd_info *mtd, loff_t to, size_t len,
675f18dbbb1SShiraz Hashim 		size_t *retlen, const u8 *buf)
676f18dbbb1SShiraz Hashim {
677f18dbbb1SShiraz Hashim 	struct spear_snor_flash *flash = get_flash_data(mtd);
678f18dbbb1SShiraz Hashim 	struct spear_smi *dev = mtd->priv;
679e51fb2cbSJingoo Han 	void __iomem *dest;
680f18dbbb1SShiraz Hashim 	u32 page_offset, page_size;
681f18dbbb1SShiraz Hashim 	int ret;
682f18dbbb1SShiraz Hashim 
683f18dbbb1SShiraz Hashim 	if (!flash || !dev)
684f18dbbb1SShiraz Hashim 		return -ENODEV;
685f18dbbb1SShiraz Hashim 
686f18dbbb1SShiraz Hashim 	if (flash->bank > dev->num_flashes - 1) {
687f18dbbb1SShiraz Hashim 		dev_err(&dev->pdev->dev, "Invalid Bank Num");
688f18dbbb1SShiraz Hashim 		return -EINVAL;
689f18dbbb1SShiraz Hashim 	}
690f18dbbb1SShiraz Hashim 
691f18dbbb1SShiraz Hashim 	/* select address as per bank number */
692f18dbbb1SShiraz Hashim 	dest = flash->base_addr + to;
693f18dbbb1SShiraz Hashim 	mutex_lock(&flash->lock);
694f18dbbb1SShiraz Hashim 
695f18dbbb1SShiraz Hashim 	page_offset = (u32)to % flash->page_size;
696f18dbbb1SShiraz Hashim 
697f18dbbb1SShiraz Hashim 	/* do if all the bytes fit onto one page */
698f18dbbb1SShiraz Hashim 	if (page_offset + len <= flash->page_size) {
699f18dbbb1SShiraz Hashim 		ret = spear_smi_cpy_toio(dev, flash->bank, dest, buf, len);
700f18dbbb1SShiraz Hashim 		if (!ret)
701f18dbbb1SShiraz Hashim 			*retlen += len;
702f18dbbb1SShiraz Hashim 	} else {
703f18dbbb1SShiraz Hashim 		u32 i;
704f18dbbb1SShiraz Hashim 
705f18dbbb1SShiraz Hashim 		/* the size of data remaining on the first page */
706f18dbbb1SShiraz Hashim 		page_size = flash->page_size - page_offset;
707f18dbbb1SShiraz Hashim 
708f18dbbb1SShiraz Hashim 		ret = spear_smi_cpy_toio(dev, flash->bank, dest, buf,
709f18dbbb1SShiraz Hashim 				page_size);
710f18dbbb1SShiraz Hashim 		if (ret)
711f18dbbb1SShiraz Hashim 			goto err_write;
712f18dbbb1SShiraz Hashim 		else
713f18dbbb1SShiraz Hashim 			*retlen += page_size;
714f18dbbb1SShiraz Hashim 
715f18dbbb1SShiraz Hashim 		/* write everything in pagesize chunks */
716f18dbbb1SShiraz Hashim 		for (i = page_size; i < len; i += page_size) {
717f18dbbb1SShiraz Hashim 			page_size = len - i;
718f18dbbb1SShiraz Hashim 			if (page_size > flash->page_size)
719f18dbbb1SShiraz Hashim 				page_size = flash->page_size;
720f18dbbb1SShiraz Hashim 
721f18dbbb1SShiraz Hashim 			ret = spear_smi_cpy_toio(dev, flash->bank, dest + i,
722f18dbbb1SShiraz Hashim 					buf + i, page_size);
723f18dbbb1SShiraz Hashim 			if (ret)
724f18dbbb1SShiraz Hashim 				break;
725f18dbbb1SShiraz Hashim 			else
726f18dbbb1SShiraz Hashim 				*retlen += page_size;
727f18dbbb1SShiraz Hashim 		}
728f18dbbb1SShiraz Hashim 	}
729f18dbbb1SShiraz Hashim 
730f18dbbb1SShiraz Hashim err_write:
731f18dbbb1SShiraz Hashim 	mutex_unlock(&flash->lock);
732f18dbbb1SShiraz Hashim 
733f18dbbb1SShiraz Hashim 	return ret;
734f18dbbb1SShiraz Hashim }
735f18dbbb1SShiraz Hashim 
736f18dbbb1SShiraz Hashim /**
737f18dbbb1SShiraz Hashim  * spear_smi_probe_flash - Detects the NOR Flash chip.
738f18dbbb1SShiraz Hashim  * @dev: structure of SMI information.
739f18dbbb1SShiraz Hashim  * @bank: bank on which flash must be probed
740f18dbbb1SShiraz Hashim  *
741f18dbbb1SShiraz Hashim  * This routine will check whether there exists a flash chip on a given memory
742f18dbbb1SShiraz Hashim  * bank ID.
743f18dbbb1SShiraz Hashim  * Return index of the probed flash in flash devices structure
744f18dbbb1SShiraz Hashim  */
745f18dbbb1SShiraz Hashim static int spear_smi_probe_flash(struct spear_smi *dev, u32 bank)
746f18dbbb1SShiraz Hashim {
747f18dbbb1SShiraz Hashim 	int ret;
748f18dbbb1SShiraz Hashim 	u32 val = 0;
749f18dbbb1SShiraz Hashim 
750f18dbbb1SShiraz Hashim 	ret = spear_smi_wait_till_ready(dev, bank, SMI_PROBE_TIMEOUT);
751f18dbbb1SShiraz Hashim 	if (ret)
752f18dbbb1SShiraz Hashim 		return ret;
753f18dbbb1SShiraz Hashim 
754f18dbbb1SShiraz Hashim 	mutex_lock(&dev->lock);
755f18dbbb1SShiraz Hashim 
756f18dbbb1SShiraz Hashim 	dev->status = 0; /* Will be set in interrupt handler */
757f18dbbb1SShiraz Hashim 	/* put smi in sw mode */
758f18dbbb1SShiraz Hashim 	val = readl(dev->io_base + SMI_CR1);
759f18dbbb1SShiraz Hashim 	writel(val | SW_MODE, dev->io_base + SMI_CR1);
760f18dbbb1SShiraz Hashim 
761f18dbbb1SShiraz Hashim 	/* send readid command in sw mode */
762f18dbbb1SShiraz Hashim 	writel(OPCODE_RDID, dev->io_base + SMI_TR);
763f18dbbb1SShiraz Hashim 
764f18dbbb1SShiraz Hashim 	val = (bank << BANK_SHIFT) | SEND | (1 << TX_LEN_SHIFT) |
765f18dbbb1SShiraz Hashim 		(3 << RX_LEN_SHIFT) | TFIE;
766f18dbbb1SShiraz Hashim 	writel(val, dev->io_base + SMI_CR2);
767f18dbbb1SShiraz Hashim 
768f18dbbb1SShiraz Hashim 	/* wait for TFF */
769f18dbbb1SShiraz Hashim 	ret = wait_event_interruptible_timeout(dev->cmd_complete,
770f18dbbb1SShiraz Hashim 			dev->status & TFF, SMI_CMD_TIMEOUT);
771f18dbbb1SShiraz Hashim 	if (ret <= 0) {
772f18dbbb1SShiraz Hashim 		ret = -ENODEV;
773f18dbbb1SShiraz Hashim 		goto err_probe;
774f18dbbb1SShiraz Hashim 	}
775f18dbbb1SShiraz Hashim 
776f18dbbb1SShiraz Hashim 	/* get memory chip id */
777f18dbbb1SShiraz Hashim 	val = readl(dev->io_base + SMI_RR);
778f18dbbb1SShiraz Hashim 	val &= 0x00ffffff;
779f18dbbb1SShiraz Hashim 	ret = get_flash_index(val);
780f18dbbb1SShiraz Hashim 
781f18dbbb1SShiraz Hashim err_probe:
782f18dbbb1SShiraz Hashim 	/* clear sw mode */
783f18dbbb1SShiraz Hashim 	val = readl(dev->io_base + SMI_CR1);
784f18dbbb1SShiraz Hashim 	writel(val & ~SW_MODE, dev->io_base + SMI_CR1);
785f18dbbb1SShiraz Hashim 
786f18dbbb1SShiraz Hashim 	mutex_unlock(&dev->lock);
787f18dbbb1SShiraz Hashim 	return ret;
788f18dbbb1SShiraz Hashim }
789f18dbbb1SShiraz Hashim 
7906551ab5dSStefan Roese 
7916551ab5dSStefan Roese #ifdef CONFIG_OF
79206f25510SBill Pemberton static int spear_smi_probe_config_dt(struct platform_device *pdev,
7936551ab5dSStefan Roese 				     struct device_node *np)
7946551ab5dSStefan Roese {
7956551ab5dSStefan Roese 	struct spear_smi_plat_data *pdata = dev_get_platdata(&pdev->dev);
796*670c898cSQinglang Miao 	struct device_node *pp;
7976551ab5dSStefan Roese 	const __be32 *addr;
7986551ab5dSStefan Roese 	u32 val;
7996551ab5dSStefan Roese 	int len;
8006551ab5dSStefan Roese 	int i = 0;
8016551ab5dSStefan Roese 
8026551ab5dSStefan Roese 	if (!np)
8036551ab5dSStefan Roese 		return -ENODEV;
8046551ab5dSStefan Roese 
8056551ab5dSStefan Roese 	of_property_read_u32(np, "clock-rate", &val);
8066551ab5dSStefan Roese 	pdata->clk_rate = val;
8076551ab5dSStefan Roese 
8086551ab5dSStefan Roese 	pdata->board_flash_info = devm_kzalloc(&pdev->dev,
8096551ab5dSStefan Roese 					       sizeof(*pdata->board_flash_info),
8106551ab5dSStefan Roese 					       GFP_KERNEL);
81190cc62f3SGustavo A. R. Silva 	if (!pdata->board_flash_info)
81290cc62f3SGustavo A. R. Silva 		return -ENOMEM;
8136551ab5dSStefan Roese 
8146551ab5dSStefan Roese 	/* Fill structs for each subnode (flash device) */
815*670c898cSQinglang Miao 	for_each_child_of_node(np, pp) {
8166551ab5dSStefan Roese 		pdata->np[i] = pp;
8176551ab5dSStefan Roese 
8186551ab5dSStefan Roese 		/* Read base-addr and size from DT */
8196551ab5dSStefan Roese 		addr = of_get_property(pp, "reg", &len);
8206551ab5dSStefan Roese 		pdata->board_flash_info->mem_base = be32_to_cpup(&addr[0]);
8216551ab5dSStefan Roese 		pdata->board_flash_info->size = be32_to_cpup(&addr[1]);
8226551ab5dSStefan Roese 
8236551ab5dSStefan Roese 		if (of_get_property(pp, "st,smi-fast-mode", NULL))
8246551ab5dSStefan Roese 			pdata->board_flash_info->fast_mode = 1;
8256551ab5dSStefan Roese 
8266551ab5dSStefan Roese 		i++;
8276551ab5dSStefan Roese 	}
8286551ab5dSStefan Roese 
8296551ab5dSStefan Roese 	pdata->num_flashes = i;
8306551ab5dSStefan Roese 
8316551ab5dSStefan Roese 	return 0;
8326551ab5dSStefan Roese }
8336551ab5dSStefan Roese #else
83406f25510SBill Pemberton static int spear_smi_probe_config_dt(struct platform_device *pdev,
8356551ab5dSStefan Roese 				     struct device_node *np)
8366551ab5dSStefan Roese {
8376551ab5dSStefan Roese 	return -ENOSYS;
8386551ab5dSStefan Roese }
8396551ab5dSStefan Roese #endif
8406551ab5dSStefan Roese 
8416551ab5dSStefan Roese static int spear_smi_setup_banks(struct platform_device *pdev,
8426551ab5dSStefan Roese 				 u32 bank, struct device_node *np)
843f18dbbb1SShiraz Hashim {
844f18dbbb1SShiraz Hashim 	struct spear_smi *dev = platform_get_drvdata(pdev);
845f18dbbb1SShiraz Hashim 	struct spear_smi_flash_info *flash_info;
846f18dbbb1SShiraz Hashim 	struct spear_smi_plat_data *pdata;
847f18dbbb1SShiraz Hashim 	struct spear_snor_flash *flash;
848f7e3dd8fSStefan Roese 	struct mtd_partition *parts = NULL;
849f7e3dd8fSStefan Roese 	int count = 0;
850f18dbbb1SShiraz Hashim 	int flash_index;
851f18dbbb1SShiraz Hashim 	int ret = 0;
852f18dbbb1SShiraz Hashim 
853f18dbbb1SShiraz Hashim 	pdata = dev_get_platdata(&pdev->dev);
854f18dbbb1SShiraz Hashim 	if (bank > pdata->num_flashes - 1)
855f18dbbb1SShiraz Hashim 		return -EINVAL;
856f18dbbb1SShiraz Hashim 
857f18dbbb1SShiraz Hashim 	flash_info = &pdata->board_flash_info[bank];
858f18dbbb1SShiraz Hashim 	if (!flash_info)
859f18dbbb1SShiraz Hashim 		return -ENODEV;
860f18dbbb1SShiraz Hashim 
861e1ed147fSJulia Lawall 	flash = devm_kzalloc(&pdev->dev, sizeof(*flash), GFP_ATOMIC);
862f18dbbb1SShiraz Hashim 	if (!flash)
863f18dbbb1SShiraz Hashim 		return -ENOMEM;
864f18dbbb1SShiraz Hashim 	flash->bank = bank;
865f18dbbb1SShiraz Hashim 	flash->fast_mode = flash_info->fast_mode ? 1 : 0;
866f18dbbb1SShiraz Hashim 	mutex_init(&flash->lock);
867f18dbbb1SShiraz Hashim 
868f18dbbb1SShiraz Hashim 	/* verify whether nor flash is really present on board */
869f18dbbb1SShiraz Hashim 	flash_index = spear_smi_probe_flash(dev, bank);
870f18dbbb1SShiraz Hashim 	if (flash_index < 0) {
871f18dbbb1SShiraz Hashim 		dev_info(&dev->pdev->dev, "smi-nor%d not found\n", bank);
872e1ed147fSJulia Lawall 		return flash_index;
873f18dbbb1SShiraz Hashim 	}
874f18dbbb1SShiraz Hashim 	/* map the memory for nor flash chip */
875e1ed147fSJulia Lawall 	flash->base_addr = devm_ioremap(&pdev->dev, flash_info->mem_base,
876e1ed147fSJulia Lawall 					flash_info->size);
877e1ed147fSJulia Lawall 	if (!flash->base_addr)
878e1ed147fSJulia Lawall 		return -EIO;
879f18dbbb1SShiraz Hashim 
880f18dbbb1SShiraz Hashim 	dev->flash[bank] = flash;
881f18dbbb1SShiraz Hashim 	flash->mtd.priv = dev;
882f18dbbb1SShiraz Hashim 
883f18dbbb1SShiraz Hashim 	if (flash_info->name)
884f18dbbb1SShiraz Hashim 		flash->mtd.name = flash_info->name;
885f18dbbb1SShiraz Hashim 	else
886f18dbbb1SShiraz Hashim 		flash->mtd.name = flash_devices[flash_index].name;
887f18dbbb1SShiraz Hashim 
8887d242722SFrans Klaver 	flash->mtd.dev.parent = &pdev->dev;
889004b5e60SBrian Norris 	mtd_set_of_node(&flash->mtd, np);
890f18dbbb1SShiraz Hashim 	flash->mtd.type = MTD_NORFLASH;
891f18dbbb1SShiraz Hashim 	flash->mtd.writesize = 1;
892f18dbbb1SShiraz Hashim 	flash->mtd.flags = MTD_CAP_NORFLASH;
893f18dbbb1SShiraz Hashim 	flash->mtd.size = flash_info->size;
894f18dbbb1SShiraz Hashim 	flash->mtd.erasesize = flash_devices[flash_index].sectorsize;
895f18dbbb1SShiraz Hashim 	flash->page_size = flash_devices[flash_index].pagesize;
89681fefdf2SArtem Bityutskiy 	flash->mtd.writebufsize = flash->page_size;
897f18dbbb1SShiraz Hashim 	flash->erase_cmd = flash_devices[flash_index].erase_cmd;
8983c3c10bbSArtem Bityutskiy 	flash->mtd._erase = spear_mtd_erase;
8993c3c10bbSArtem Bityutskiy 	flash->mtd._read = spear_mtd_read;
9003c3c10bbSArtem Bityutskiy 	flash->mtd._write = spear_mtd_write;
901f18dbbb1SShiraz Hashim 	flash->dev_id = flash_devices[flash_index].device_id;
902f18dbbb1SShiraz Hashim 
903f18dbbb1SShiraz Hashim 	dev_info(&dev->pdev->dev, "mtd .name=%s .size=%llx(%lluM)\n",
904f18dbbb1SShiraz Hashim 			flash->mtd.name, flash->mtd.size,
905f18dbbb1SShiraz Hashim 			flash->mtd.size / (1024 * 1024));
906f18dbbb1SShiraz Hashim 
907f18dbbb1SShiraz Hashim 	dev_info(&dev->pdev->dev, ".erasesize = 0x%x(%uK)\n",
908f18dbbb1SShiraz Hashim 			flash->mtd.erasesize, flash->mtd.erasesize / 1024);
909f18dbbb1SShiraz Hashim 
9106551ab5dSStefan Roese #ifndef CONFIG_OF
911f18dbbb1SShiraz Hashim 	if (flash_info->partitions) {
912f18dbbb1SShiraz Hashim 		parts = flash_info->partitions;
913f18dbbb1SShiraz Hashim 		count = flash_info->nr_partitions;
914f18dbbb1SShiraz Hashim 	}
9156551ab5dSStefan Roese #endif
9166551ab5dSStefan Roese 
917004b5e60SBrian Norris 	ret = mtd_device_register(&flash->mtd, parts, count);
918f7e3dd8fSStefan Roese 	if (ret) {
919f18dbbb1SShiraz Hashim 		dev_err(&dev->pdev->dev, "Err MTD partition=%d\n", ret);
920e1ed147fSJulia Lawall 		return ret;
921f7e3dd8fSStefan Roese 	}
922f18dbbb1SShiraz Hashim 
923f7e3dd8fSStefan Roese 	return 0;
924f18dbbb1SShiraz Hashim }
925f18dbbb1SShiraz Hashim 
926f18dbbb1SShiraz Hashim /**
927f18dbbb1SShiraz Hashim  * spear_smi_probe - Entry routine
928f18dbbb1SShiraz Hashim  * @pdev: platform device structure
929f18dbbb1SShiraz Hashim  *
930f18dbbb1SShiraz Hashim  * This is the first routine which gets invoked during booting and does all
931f18dbbb1SShiraz Hashim  * initialization/allocation work. The routine looks for available memory banks,
932f18dbbb1SShiraz Hashim  * and do proper init for any found one.
933f18dbbb1SShiraz Hashim  * Returns 0 on success, non zero otherwise
934f18dbbb1SShiraz Hashim  */
93506f25510SBill Pemberton static int spear_smi_probe(struct platform_device *pdev)
936f18dbbb1SShiraz Hashim {
9376551ab5dSStefan Roese 	struct device_node *np = pdev->dev.of_node;
9386551ab5dSStefan Roese 	struct spear_smi_plat_data *pdata = NULL;
939f18dbbb1SShiraz Hashim 	struct spear_smi *dev;
940f18dbbb1SShiraz Hashim 	struct resource *smi_base;
941f18dbbb1SShiraz Hashim 	int irq, ret = 0;
942f18dbbb1SShiraz Hashim 	int i;
943f18dbbb1SShiraz Hashim 
9446551ab5dSStefan Roese 	if (np) {
9456551ab5dSStefan Roese 		pdata = devm_kzalloc(&pdev->dev, sizeof(*pdata), GFP_KERNEL);
9466551ab5dSStefan Roese 		if (!pdata) {
9476551ab5dSStefan Roese 			ret = -ENOMEM;
9486551ab5dSStefan Roese 			goto err;
9496551ab5dSStefan Roese 		}
9506551ab5dSStefan Roese 		pdev->dev.platform_data = pdata;
9516551ab5dSStefan Roese 		ret = spear_smi_probe_config_dt(pdev, np);
9526551ab5dSStefan Roese 		if (ret) {
9536551ab5dSStefan Roese 			ret = -ENODEV;
9546551ab5dSStefan Roese 			dev_err(&pdev->dev, "no platform data\n");
9556551ab5dSStefan Roese 			goto err;
9566551ab5dSStefan Roese 		}
9576551ab5dSStefan Roese 	} else {
958f18dbbb1SShiraz Hashim 		pdata = dev_get_platdata(&pdev->dev);
959b5170978SJulia Lawall 		if (!pdata) {
960f18dbbb1SShiraz Hashim 			ret = -ENODEV;
961f18dbbb1SShiraz Hashim 			dev_err(&pdev->dev, "no platform data\n");
962f18dbbb1SShiraz Hashim 			goto err;
963f18dbbb1SShiraz Hashim 		}
9646551ab5dSStefan Roese 	}
965f18dbbb1SShiraz Hashim 
966f18dbbb1SShiraz Hashim 	irq = platform_get_irq(pdev, 0);
967f18dbbb1SShiraz Hashim 	if (irq < 0) {
968f18dbbb1SShiraz Hashim 		ret = -ENODEV;
969f18dbbb1SShiraz Hashim 		goto err;
970f18dbbb1SShiraz Hashim 	}
971f18dbbb1SShiraz Hashim 
972e1ed147fSJulia Lawall 	dev = devm_kzalloc(&pdev->dev, sizeof(*dev), GFP_ATOMIC);
973f18dbbb1SShiraz Hashim 	if (!dev) {
974f18dbbb1SShiraz Hashim 		ret = -ENOMEM;
975f18dbbb1SShiraz Hashim 		goto err;
976f18dbbb1SShiraz Hashim 	}
977f18dbbb1SShiraz Hashim 
978e1ed147fSJulia Lawall 	smi_base = platform_get_resource(pdev, IORESOURCE_MEM, 0);
979f18dbbb1SShiraz Hashim 
980b0de774cSThierry Reding 	dev->io_base = devm_ioremap_resource(&pdev->dev, smi_base);
981b0de774cSThierry Reding 	if (IS_ERR(dev->io_base)) {
982b0de774cSThierry Reding 		ret = PTR_ERR(dev->io_base);
983e1ed147fSJulia Lawall 		goto err;
984f18dbbb1SShiraz Hashim 	}
985f18dbbb1SShiraz Hashim 
986f18dbbb1SShiraz Hashim 	dev->pdev = pdev;
987f18dbbb1SShiraz Hashim 	dev->clk_rate = pdata->clk_rate;
988f18dbbb1SShiraz Hashim 
989036a1ac1SArtem Bityutskiy 	if (dev->clk_rate > SMI_MAX_CLOCK_FREQ)
990f18dbbb1SShiraz Hashim 		dev->clk_rate = SMI_MAX_CLOCK_FREQ;
991f18dbbb1SShiraz Hashim 
992f18dbbb1SShiraz Hashim 	dev->num_flashes = pdata->num_flashes;
993f18dbbb1SShiraz Hashim 
994f18dbbb1SShiraz Hashim 	if (dev->num_flashes > MAX_NUM_FLASH_CHIP) {
995f18dbbb1SShiraz Hashim 		dev_err(&pdev->dev, "exceeding max number of flashes\n");
996f18dbbb1SShiraz Hashim 		dev->num_flashes = MAX_NUM_FLASH_CHIP;
997f18dbbb1SShiraz Hashim 	}
998f18dbbb1SShiraz Hashim 
999e1ed147fSJulia Lawall 	dev->clk = devm_clk_get(&pdev->dev, NULL);
1000f18dbbb1SShiraz Hashim 	if (IS_ERR(dev->clk)) {
1001f18dbbb1SShiraz Hashim 		ret = PTR_ERR(dev->clk);
1002e1ed147fSJulia Lawall 		goto err;
1003f18dbbb1SShiraz Hashim 	}
1004f18dbbb1SShiraz Hashim 
1005c0010eb5SViresh Kumar 	ret = clk_prepare_enable(dev->clk);
1006f18dbbb1SShiraz Hashim 	if (ret)
1007e1ed147fSJulia Lawall 		goto err;
1008f18dbbb1SShiraz Hashim 
1009e1ed147fSJulia Lawall 	ret = devm_request_irq(&pdev->dev, irq, spear_smi_int_handler, 0,
1010e1ed147fSJulia Lawall 			       pdev->name, dev);
1011f18dbbb1SShiraz Hashim 	if (ret) {
1012f18dbbb1SShiraz Hashim 		dev_err(&dev->pdev->dev, "SMI IRQ allocation failed\n");
1013f18dbbb1SShiraz Hashim 		goto err_irq;
1014f18dbbb1SShiraz Hashim 	}
1015f18dbbb1SShiraz Hashim 
1016f18dbbb1SShiraz Hashim 	mutex_init(&dev->lock);
1017f18dbbb1SShiraz Hashim 	init_waitqueue_head(&dev->cmd_complete);
1018f18dbbb1SShiraz Hashim 	spear_smi_hw_init(dev);
1019f18dbbb1SShiraz Hashim 	platform_set_drvdata(pdev, dev);
1020f18dbbb1SShiraz Hashim 
1021f18dbbb1SShiraz Hashim 	/* loop for each serial nor-flash which is connected to smi */
1022f18dbbb1SShiraz Hashim 	for (i = 0; i < dev->num_flashes; i++) {
10236551ab5dSStefan Roese 		ret = spear_smi_setup_banks(pdev, i, pdata->np[i]);
1024f18dbbb1SShiraz Hashim 		if (ret) {
1025f18dbbb1SShiraz Hashim 			dev_err(&dev->pdev->dev, "bank setup failed\n");
10269fa34a1eSJingoo Han 			goto err_irq;
1027f18dbbb1SShiraz Hashim 		}
1028f18dbbb1SShiraz Hashim 	}
1029f18dbbb1SShiraz Hashim 
1030f18dbbb1SShiraz Hashim 	return 0;
1031f18dbbb1SShiraz Hashim 
1032f18dbbb1SShiraz Hashim err_irq:
1033c0010eb5SViresh Kumar 	clk_disable_unprepare(dev->clk);
1034f18dbbb1SShiraz Hashim err:
1035f18dbbb1SShiraz Hashim 	return ret;
1036f18dbbb1SShiraz Hashim }
1037f18dbbb1SShiraz Hashim 
1038f18dbbb1SShiraz Hashim /**
1039f18dbbb1SShiraz Hashim  * spear_smi_remove - Exit routine
1040f18dbbb1SShiraz Hashim  * @pdev: platform device structure
1041f18dbbb1SShiraz Hashim  *
1042f18dbbb1SShiraz Hashim  * free all allocations and delete the partitions.
1043f18dbbb1SShiraz Hashim  */
1044810b7e06SBill Pemberton static int spear_smi_remove(struct platform_device *pdev)
1045f18dbbb1SShiraz Hashim {
1046f18dbbb1SShiraz Hashim 	struct spear_smi *dev;
1047f18dbbb1SShiraz Hashim 	struct spear_snor_flash *flash;
1048e1ed147fSJulia Lawall 	int ret, i;
1049f18dbbb1SShiraz Hashim 
1050f18dbbb1SShiraz Hashim 	dev = platform_get_drvdata(pdev);
1051f18dbbb1SShiraz Hashim 	if (!dev) {
1052f18dbbb1SShiraz Hashim 		dev_err(&pdev->dev, "dev is null\n");
1053f18dbbb1SShiraz Hashim 		return -ENODEV;
1054f18dbbb1SShiraz Hashim 	}
1055f18dbbb1SShiraz Hashim 
1056f18dbbb1SShiraz Hashim 	/* clean up for all nor flash */
1057f18dbbb1SShiraz Hashim 	for (i = 0; i < dev->num_flashes; i++) {
1058f18dbbb1SShiraz Hashim 		flash = dev->flash[i];
1059f18dbbb1SShiraz Hashim 		if (!flash)
1060f18dbbb1SShiraz Hashim 			continue;
1061f18dbbb1SShiraz Hashim 
1062f18dbbb1SShiraz Hashim 		/* clean up mtd stuff */
1063f18dbbb1SShiraz Hashim 		ret = mtd_device_unregister(&flash->mtd);
1064f18dbbb1SShiraz Hashim 		if (ret)
1065f18dbbb1SShiraz Hashim 			dev_err(&pdev->dev, "error removing mtd\n");
1066f18dbbb1SShiraz Hashim 	}
1067f18dbbb1SShiraz Hashim 
1068c0010eb5SViresh Kumar 	clk_disable_unprepare(dev->clk);
1069f18dbbb1SShiraz Hashim 
1070f18dbbb1SShiraz Hashim 	return 0;
1071f18dbbb1SShiraz Hashim }
1072f18dbbb1SShiraz Hashim 
1073aba98cfdSJingoo Han #ifdef CONFIG_PM_SLEEP
1074770daa43SViresh Kumar static int spear_smi_suspend(struct device *dev)
1075f18dbbb1SShiraz Hashim {
1076770daa43SViresh Kumar 	struct spear_smi *sdev = dev_get_drvdata(dev);
1077f18dbbb1SShiraz Hashim 
1078770daa43SViresh Kumar 	if (sdev && sdev->clk)
1079770daa43SViresh Kumar 		clk_disable_unprepare(sdev->clk);
1080f18dbbb1SShiraz Hashim 
1081f18dbbb1SShiraz Hashim 	return 0;
1082f18dbbb1SShiraz Hashim }
1083f18dbbb1SShiraz Hashim 
1084770daa43SViresh Kumar static int spear_smi_resume(struct device *dev)
1085f18dbbb1SShiraz Hashim {
1086770daa43SViresh Kumar 	struct spear_smi *sdev = dev_get_drvdata(dev);
1087f18dbbb1SShiraz Hashim 	int ret = -EPERM;
1088f18dbbb1SShiraz Hashim 
1089770daa43SViresh Kumar 	if (sdev && sdev->clk)
1090770daa43SViresh Kumar 		ret = clk_prepare_enable(sdev->clk);
1091f18dbbb1SShiraz Hashim 
1092f18dbbb1SShiraz Hashim 	if (!ret)
1093770daa43SViresh Kumar 		spear_smi_hw_init(sdev);
1094f18dbbb1SShiraz Hashim 	return ret;
1095f18dbbb1SShiraz Hashim }
1096aba98cfdSJingoo Han #endif
1097f18dbbb1SShiraz Hashim 
1098770daa43SViresh Kumar static SIMPLE_DEV_PM_OPS(spear_smi_pm_ops, spear_smi_suspend, spear_smi_resume);
1099770daa43SViresh Kumar 
11006551ab5dSStefan Roese #ifdef CONFIG_OF
11016551ab5dSStefan Roese static const struct of_device_id spear_smi_id_table[] = {
11026551ab5dSStefan Roese 	{ .compatible = "st,spear600-smi" },
11036551ab5dSStefan Roese 	{}
11046551ab5dSStefan Roese };
11056551ab5dSStefan Roese MODULE_DEVICE_TABLE(of, spear_smi_id_table);
11066551ab5dSStefan Roese #endif
11076551ab5dSStefan Roese 
1108f18dbbb1SShiraz Hashim static struct platform_driver spear_smi_driver = {
1109f18dbbb1SShiraz Hashim 	.driver = {
1110f18dbbb1SShiraz Hashim 		.name = "smi",
1111f18dbbb1SShiraz Hashim 		.bus = &platform_bus_type,
11126551ab5dSStefan Roese 		.of_match_table = of_match_ptr(spear_smi_id_table),
1113770daa43SViresh Kumar 		.pm = &spear_smi_pm_ops,
1114f18dbbb1SShiraz Hashim 	},
1115f18dbbb1SShiraz Hashim 	.probe = spear_smi_probe,
11165153b88cSBill Pemberton 	.remove = spear_smi_remove,
1117f18dbbb1SShiraz Hashim };
1118a90019e2SSrinivas Kandagatla module_platform_driver(spear_smi_driver);
1119f18dbbb1SShiraz Hashim 
1120f18dbbb1SShiraz Hashim MODULE_LICENSE("GPL");
11219cc23682SViresh Kumar MODULE_AUTHOR("Ashish Priyadarshi, Shiraz Hashim <shiraz.linux.kernel@gmail.com>");
1122f18dbbb1SShiraz Hashim MODULE_DESCRIPTION("MTD SMI driver for serial nor flash chips");
1123