diff options
Diffstat (limited to 'drivers/mtd/devices/spear_smi.c')
-rw-r--r-- | drivers/mtd/devices/spear_smi.c | 1147 |
1 files changed, 1147 insertions, 0 deletions
diff --git a/drivers/mtd/devices/spear_smi.c b/drivers/mtd/devices/spear_smi.c new file mode 100644 index 000000000000..797d43cd3550 --- /dev/null +++ b/drivers/mtd/devices/spear_smi.c | |||
@@ -0,0 +1,1147 @@ | |||
1 | /* | ||
2 | * SMI (Serial Memory Controller) device driver for Serial NOR Flash on | ||
3 | * SPEAr platform | ||
4 | * The serial nor interface is largely based on drivers/mtd/m25p80.c, | ||
5 | * however the SPI interface has been replaced by SMI. | ||
6 | * | ||
7 | * Copyright © 2010 STMicroelectronics. | ||
8 | * Ashish Priyadarshi | ||
9 | * Shiraz Hashim <shiraz.hashim@st.com> | ||
10 | * | ||
11 | * This file is licensed under the terms of the GNU General Public | ||
12 | * License version 2. This program is licensed "as is" without any | ||
13 | * warranty of any kind, whether express or implied. | ||
14 | */ | ||
15 | |||
16 | #include <linux/clk.h> | ||
17 | #include <linux/delay.h> | ||
18 | #include <linux/device.h> | ||
19 | #include <linux/err.h> | ||
20 | #include <linux/errno.h> | ||
21 | #include <linux/interrupt.h> | ||
22 | #include <linux/io.h> | ||
23 | #include <linux/ioport.h> | ||
24 | #include <linux/jiffies.h> | ||
25 | #include <linux/kernel.h> | ||
26 | #include <linux/module.h> | ||
27 | #include <linux/param.h> | ||
28 | #include <linux/platform_device.h> | ||
29 | #include <linux/mtd/mtd.h> | ||
30 | #include <linux/mtd/partitions.h> | ||
31 | #include <linux/mtd/spear_smi.h> | ||
32 | #include <linux/mutex.h> | ||
33 | #include <linux/sched.h> | ||
34 | #include <linux/slab.h> | ||
35 | #include <linux/wait.h> | ||
36 | #include <linux/of.h> | ||
37 | #include <linux/of_address.h> | ||
38 | |||
39 | /* SMI clock rate */ | ||
40 | #define SMI_MAX_CLOCK_FREQ 50000000 /* 50 MHz */ | ||
41 | |||
42 | /* MAX time out to safely come out of a erase or write busy conditions */ | ||
43 | #define SMI_PROBE_TIMEOUT (HZ / 10) | ||
44 | #define SMI_MAX_TIME_OUT (3 * HZ) | ||
45 | |||
46 | /* timeout for command completion */ | ||
47 | #define SMI_CMD_TIMEOUT (HZ / 10) | ||
48 | |||
49 | /* registers of smi */ | ||
50 | #define SMI_CR1 0x0 /* SMI control register 1 */ | ||
51 | #define SMI_CR2 0x4 /* SMI control register 2 */ | ||
52 | #define SMI_SR 0x8 /* SMI status register */ | ||
53 | #define SMI_TR 0xC /* SMI transmit register */ | ||
54 | #define SMI_RR 0x10 /* SMI receive register */ | ||
55 | |||
56 | /* defines for control_reg 1 */ | ||
57 | #define BANK_EN (0xF << 0) /* enables all banks */ | ||
58 | #define DSEL_TIME (0x6 << 4) /* Deselect time 6 + 1 SMI_CK periods */ | ||
59 | #define SW_MODE (0x1 << 28) /* enables SW Mode */ | ||
60 | #define WB_MODE (0x1 << 29) /* Write Burst Mode */ | ||
61 | #define FAST_MODE (0x1 << 15) /* Fast Mode */ | ||
62 | #define HOLD1 (0x1 << 16) /* Clock Hold period selection */ | ||
63 | |||
64 | /* defines for control_reg 2 */ | ||
65 | #define SEND (0x1 << 7) /* Send data */ | ||
66 | #define TFIE (0x1 << 8) /* Transmission Flag Interrupt Enable */ | ||
67 | #define WCIE (0x1 << 9) /* Write Complete Interrupt Enable */ | ||
68 | #define RD_STATUS_REG (0x1 << 10) /* reads status reg */ | ||
69 | #define WE (0x1 << 11) /* Write Enable */ | ||
70 | |||
71 | #define TX_LEN_SHIFT 0 | ||
72 | #define RX_LEN_SHIFT 4 | ||
73 | #define BANK_SHIFT 12 | ||
74 | |||
75 | /* defines for status register */ | ||
76 | #define SR_WIP 0x1 /* Write in progress */ | ||
77 | #define SR_WEL 0x2 /* Write enable latch */ | ||
78 | #define SR_BP0 0x4 /* Block protect 0 */ | ||
79 | #define SR_BP1 0x8 /* Block protect 1 */ | ||
80 | #define SR_BP2 0x10 /* Block protect 2 */ | ||
81 | #define SR_SRWD 0x80 /* SR write protect */ | ||
82 | #define TFF 0x100 /* Transfer Finished Flag */ | ||
83 | #define WCF 0x200 /* Transfer Finished Flag */ | ||
84 | #define ERF1 0x400 /* Forbidden Write Request */ | ||
85 | #define ERF2 0x800 /* Forbidden Access */ | ||
86 | |||
87 | #define WM_SHIFT 12 | ||
88 | |||
89 | /* flash opcodes */ | ||
90 | #define OPCODE_RDID 0x9f /* Read JEDEC ID */ | ||
91 | |||
92 | /* Flash Device Ids maintenance section */ | ||
93 | |||
94 | /* data structure to maintain flash ids from different vendors */ | ||
95 | struct flash_device { | ||
96 | char *name; | ||
97 | u8 erase_cmd; | ||
98 | u32 device_id; | ||
99 | u32 pagesize; | ||
100 | unsigned long sectorsize; | ||
101 | unsigned long size_in_bytes; | ||
102 | }; | ||
103 | |||
104 | #define FLASH_ID(n, es, id, psize, ssize, size) \ | ||
105 | { \ | ||
106 | .name = n, \ | ||
107 | .erase_cmd = es, \ | ||
108 | .device_id = id, \ | ||
109 | .pagesize = psize, \ | ||
110 | .sectorsize = ssize, \ | ||
111 | .size_in_bytes = size \ | ||
112 | } | ||
113 | |||
114 | static struct flash_device flash_devices[] = { | ||
115 | FLASH_ID("st m25p16" , 0xd8, 0x00152020, 0x100, 0x10000, 0x200000), | ||
116 | FLASH_ID("st m25p32" , 0xd8, 0x00162020, 0x100, 0x10000, 0x400000), | ||
117 | FLASH_ID("st m25p64" , 0xd8, 0x00172020, 0x100, 0x10000, 0x800000), | ||
118 | FLASH_ID("st m25p128" , 0xd8, 0x00182020, 0x100, 0x40000, 0x1000000), | ||
119 | FLASH_ID("st m25p05" , 0xd8, 0x00102020, 0x80 , 0x8000 , 0x10000), | ||
120 | FLASH_ID("st m25p10" , 0xd8, 0x00112020, 0x80 , 0x8000 , 0x20000), | ||
121 | FLASH_ID("st m25p20" , 0xd8, 0x00122020, 0x100, 0x10000, 0x40000), | ||
122 | FLASH_ID("st m25p40" , 0xd8, 0x00132020, 0x100, 0x10000, 0x80000), | ||
123 | FLASH_ID("st m25p80" , 0xd8, 0x00142020, 0x100, 0x10000, 0x100000), | ||
124 | FLASH_ID("st m45pe10" , 0xd8, 0x00114020, 0x100, 0x10000, 0x20000), | ||
125 | FLASH_ID("st m45pe20" , 0xd8, 0x00124020, 0x100, 0x10000, 0x40000), | ||
126 | FLASH_ID("st m45pe40" , 0xd8, 0x00134020, 0x100, 0x10000, 0x80000), | ||
127 | FLASH_ID("st m45pe80" , 0xd8, 0x00144020, 0x100, 0x10000, 0x100000), | ||
128 | FLASH_ID("sp s25fl004" , 0xd8, 0x00120201, 0x100, 0x10000, 0x80000), | ||
129 | FLASH_ID("sp s25fl008" , 0xd8, 0x00130201, 0x100, 0x10000, 0x100000), | ||
130 | FLASH_ID("sp s25fl016" , 0xd8, 0x00140201, 0x100, 0x10000, 0x200000), | ||
131 | FLASH_ID("sp s25fl032" , 0xd8, 0x00150201, 0x100, 0x10000, 0x400000), | ||
132 | FLASH_ID("sp s25fl064" , 0xd8, 0x00160201, 0x100, 0x10000, 0x800000), | ||
133 | FLASH_ID("atmel 25f512" , 0x52, 0x0065001F, 0x80 , 0x8000 , 0x10000), | ||
134 | FLASH_ID("atmel 25f1024" , 0x52, 0x0060001F, 0x100, 0x8000 , 0x20000), | ||
135 | FLASH_ID("atmel 25f2048" , 0x52, 0x0063001F, 0x100, 0x10000, 0x40000), | ||
136 | FLASH_ID("atmel 25f4096" , 0x52, 0x0064001F, 0x100, 0x10000, 0x80000), | ||
137 | FLASH_ID("atmel 25fs040" , 0xd7, 0x0004661F, 0x100, 0x10000, 0x80000), | ||
138 | FLASH_ID("mac 25l512" , 0xd8, 0x001020C2, 0x010, 0x10000, 0x10000), | ||
139 | FLASH_ID("mac 25l1005" , 0xd8, 0x001120C2, 0x010, 0x10000, 0x20000), | ||
140 | FLASH_ID("mac 25l2005" , 0xd8, 0x001220C2, 0x010, 0x10000, 0x40000), | ||
141 | FLASH_ID("mac 25l4005" , 0xd8, 0x001320C2, 0x010, 0x10000, 0x80000), | ||
142 | FLASH_ID("mac 25l4005a" , 0xd8, 0x001320C2, 0x010, 0x10000, 0x80000), | ||
143 | FLASH_ID("mac 25l8005" , 0xd8, 0x001420C2, 0x010, 0x10000, 0x100000), | ||
144 | FLASH_ID("mac 25l1605" , 0xd8, 0x001520C2, 0x100, 0x10000, 0x200000), | ||
145 | FLASH_ID("mac 25l1605a" , 0xd8, 0x001520C2, 0x010, 0x10000, 0x200000), | ||
146 | FLASH_ID("mac 25l3205" , 0xd8, 0x001620C2, 0x100, 0x10000, 0x400000), | ||
147 | FLASH_ID("mac 25l3205a" , 0xd8, 0x001620C2, 0x100, 0x10000, 0x400000), | ||
148 | FLASH_ID("mac 25l6405" , 0xd8, 0x001720C2, 0x100, 0x10000, 0x800000), | ||
149 | }; | ||
150 | |||
151 | /* Define spear specific structures */ | ||
152 | |||
153 | struct spear_snor_flash; | ||
154 | |||
155 | /** | ||
156 | * struct spear_smi - Structure for SMI Device | ||
157 | * | ||
158 | * @clk: functional clock | ||
159 | * @status: current status register of SMI. | ||
160 | * @clk_rate: functional clock rate of SMI (default: SMI_MAX_CLOCK_FREQ) | ||
161 | * @lock: lock to prevent parallel access of SMI. | ||
162 | * @io_base: base address for registers of SMI. | ||
163 | * @pdev: platform device | ||
164 | * @cmd_complete: queue to wait for command completion of NOR-flash. | ||
165 | * @num_flashes: number of flashes actually present on board. | ||
166 | * @flash: separate structure for each Serial NOR-flash attached to SMI. | ||
167 | */ | ||
168 | struct spear_smi { | ||
169 | struct clk *clk; | ||
170 | u32 status; | ||
171 | unsigned long clk_rate; | ||
172 | struct mutex lock; | ||
173 | void __iomem *io_base; | ||
174 | struct platform_device *pdev; | ||
175 | wait_queue_head_t cmd_complete; | ||
176 | u32 num_flashes; | ||
177 | struct spear_snor_flash *flash[MAX_NUM_FLASH_CHIP]; | ||
178 | }; | ||
179 | |||
180 | /** | ||
181 | * struct spear_snor_flash - Structure for Serial NOR Flash | ||
182 | * | ||
183 | * @bank: Bank number(0, 1, 2, 3) for each NOR-flash. | ||
184 | * @dev_id: Device ID of NOR-flash. | ||
185 | * @lock: lock to manage flash read, write and erase operations | ||
186 | * @mtd: MTD info for each NOR-flash. | ||
187 | * @num_parts: Total number of partition in each bank of NOR-flash. | ||
188 | * @parts: Partition info for each bank of NOR-flash. | ||
189 | * @page_size: Page size of NOR-flash. | ||
190 | * @base_addr: Base address of NOR-flash. | ||
191 | * @erase_cmd: erase command may vary on different flash types | ||
192 | * @fast_mode: flash supports read in fast mode | ||
193 | */ | ||
194 | struct spear_snor_flash { | ||
195 | u32 bank; | ||
196 | u32 dev_id; | ||
197 | struct mutex lock; | ||
198 | struct mtd_info mtd; | ||
199 | u32 num_parts; | ||
200 | struct mtd_partition *parts; | ||
201 | u32 page_size; | ||
202 | void __iomem *base_addr; | ||
203 | u8 erase_cmd; | ||
204 | u8 fast_mode; | ||
205 | }; | ||
206 | |||
207 | static inline struct spear_snor_flash *get_flash_data(struct mtd_info *mtd) | ||
208 | { | ||
209 | return container_of(mtd, struct spear_snor_flash, mtd); | ||
210 | } | ||
211 | |||
212 | /** | ||
213 | * spear_smi_read_sr - Read status register of flash through SMI | ||
214 | * @dev: structure of SMI information. | ||
215 | * @bank: bank to which flash is connected | ||
216 | * | ||
217 | * This routine will return the status register of the flash chip present at the | ||
218 | * given bank. | ||
219 | */ | ||
220 | static int spear_smi_read_sr(struct spear_smi *dev, u32 bank) | ||
221 | { | ||
222 | int ret; | ||
223 | u32 ctrlreg1; | ||
224 | |||
225 | mutex_lock(&dev->lock); | ||
226 | dev->status = 0; /* Will be set in interrupt handler */ | ||
227 | |||
228 | ctrlreg1 = readl(dev->io_base + SMI_CR1); | ||
229 | /* program smi in hw mode */ | ||
230 | writel(ctrlreg1 & ~(SW_MODE | WB_MODE), dev->io_base + SMI_CR1); | ||
231 | |||
232 | /* performing a rsr instruction in hw mode */ | ||
233 | writel((bank << BANK_SHIFT) | RD_STATUS_REG | TFIE, | ||
234 | dev->io_base + SMI_CR2); | ||
235 | |||
236 | /* wait for tff */ | ||
237 | ret = wait_event_interruptible_timeout(dev->cmd_complete, | ||
238 | dev->status & TFF, SMI_CMD_TIMEOUT); | ||
239 | |||
240 | /* copy dev->status (lower 16 bits) in order to release lock */ | ||
241 | if (ret > 0) | ||
242 | ret = dev->status & 0xffff; | ||
243 | else | ||
244 | ret = -EIO; | ||
245 | |||
246 | /* restore the ctrl regs state */ | ||
247 | writel(ctrlreg1, dev->io_base + SMI_CR1); | ||
248 | writel(0, dev->io_base + SMI_CR2); | ||
249 | mutex_unlock(&dev->lock); | ||
250 | |||
251 | return ret; | ||
252 | } | ||
253 | |||
254 | /** | ||
255 | * spear_smi_wait_till_ready - wait till flash is ready | ||
256 | * @dev: structure of SMI information. | ||
257 | * @bank: flash corresponding to this bank | ||
258 | * @timeout: timeout for busy wait condition | ||
259 | * | ||
260 | * This routine checks for WIP (write in progress) bit in Status register | ||
261 | * If successful the routine returns 0 else -EBUSY | ||
262 | */ | ||
263 | static int spear_smi_wait_till_ready(struct spear_smi *dev, u32 bank, | ||
264 | unsigned long timeout) | ||
265 | { | ||
266 | unsigned long finish; | ||
267 | int status; | ||
268 | |||
269 | finish = jiffies + timeout; | ||
270 | do { | ||
271 | status = spear_smi_read_sr(dev, bank); | ||
272 | if (status < 0) | ||
273 | continue; /* try till timeout */ | ||
274 | else if (!(status & SR_WIP)) | ||
275 | return 0; | ||
276 | |||
277 | cond_resched(); | ||
278 | } while (!time_after_eq(jiffies, finish)); | ||
279 | |||
280 | dev_err(&dev->pdev->dev, "smi controller is busy, timeout\n"); | ||
281 | return status; | ||
282 | } | ||
283 | |||
284 | /** | ||
285 | * spear_smi_int_handler - SMI Interrupt Handler. | ||
286 | * @irq: irq number | ||
287 | * @dev_id: structure of SMI device, embedded in dev_id. | ||
288 | * | ||
289 | * The handler clears all interrupt conditions and records the status in | ||
290 | * dev->status which is used by the driver later. | ||
291 | */ | ||
292 | static irqreturn_t spear_smi_int_handler(int irq, void *dev_id) | ||
293 | { | ||
294 | u32 status = 0; | ||
295 | struct spear_smi *dev = dev_id; | ||
296 | |||
297 | status = readl(dev->io_base + SMI_SR); | ||
298 | |||
299 | if (unlikely(!status)) | ||
300 | return IRQ_NONE; | ||
301 | |||
302 | /* clear all interrupt conditions */ | ||
303 | writel(0, dev->io_base + SMI_SR); | ||
304 | |||
305 | /* copy the status register in dev->status */ | ||
306 | dev->status |= status; | ||
307 | |||
308 | /* send the completion */ | ||
309 | wake_up_interruptible(&dev->cmd_complete); | ||
310 | |||
311 | return IRQ_HANDLED; | ||
312 | } | ||
313 | |||
314 | /** | ||
315 | * spear_smi_hw_init - initializes the smi controller. | ||
316 | * @dev: structure of smi device | ||
317 | * | ||
318 | * this routine initializes the smi controller wit the default values | ||
319 | */ | ||
320 | static void spear_smi_hw_init(struct spear_smi *dev) | ||
321 | { | ||
322 | unsigned long rate = 0; | ||
323 | u32 prescale = 0; | ||
324 | u32 val; | ||
325 | |||
326 | rate = clk_get_rate(dev->clk); | ||
327 | |||
328 | /* functional clock of smi */ | ||
329 | prescale = DIV_ROUND_UP(rate, dev->clk_rate); | ||
330 | |||
331 | /* | ||
332 | * setting the standard values, fast mode, prescaler for | ||
333 | * SMI_MAX_CLOCK_FREQ (50MHz) operation and bank enable | ||
334 | */ | ||
335 | val = HOLD1 | BANK_EN | DSEL_TIME | (prescale << 8); | ||
336 | |||
337 | mutex_lock(&dev->lock); | ||
338 | writel(val, dev->io_base + SMI_CR1); | ||
339 | mutex_unlock(&dev->lock); | ||
340 | } | ||
341 | |||
342 | /** | ||
343 | * get_flash_index - match chip id from a flash list. | ||
344 | * @flash_id: a valid nor flash chip id obtained from board. | ||
345 | * | ||
346 | * try to validate the chip id by matching from a list, if not found then simply | ||
347 | * returns negative. In case of success returns index in to the flash devices | ||
348 | * array. | ||
349 | */ | ||
350 | static int get_flash_index(u32 flash_id) | ||
351 | { | ||
352 | int index; | ||
353 | |||
354 | /* Matches chip-id to entire list of 'serial-nor flash' ids */ | ||
355 | for (index = 0; index < ARRAY_SIZE(flash_devices); index++) { | ||
356 | if (flash_devices[index].device_id == flash_id) | ||
357 | return index; | ||
358 | } | ||
359 | |||
360 | /* Memory chip is not listed and not supported */ | ||
361 | return -ENODEV; | ||
362 | } | ||
363 | |||
364 | /** | ||
365 | * spear_smi_write_enable - Enable the flash to do write operation | ||
366 | * @dev: structure of SMI device | ||
367 | * @bank: enable write for flash connected to this bank | ||
368 | * | ||
369 | * Set write enable latch with Write Enable command. | ||
370 | * Returns 0 on success. | ||
371 | */ | ||
372 | static int spear_smi_write_enable(struct spear_smi *dev, u32 bank) | ||
373 | { | ||
374 | int ret; | ||
375 | u32 ctrlreg1; | ||
376 | |||
377 | mutex_lock(&dev->lock); | ||
378 | dev->status = 0; /* Will be set in interrupt handler */ | ||
379 | |||
380 | ctrlreg1 = readl(dev->io_base + SMI_CR1); | ||
381 | /* program smi in h/w mode */ | ||
382 | writel(ctrlreg1 & ~SW_MODE, dev->io_base + SMI_CR1); | ||
383 | |||
384 | /* give the flash, write enable command */ | ||
385 | writel((bank << BANK_SHIFT) | WE | TFIE, dev->io_base + SMI_CR2); | ||
386 | |||
387 | ret = wait_event_interruptible_timeout(dev->cmd_complete, | ||
388 | dev->status & TFF, SMI_CMD_TIMEOUT); | ||
389 | |||
390 | /* restore the ctrl regs state */ | ||
391 | writel(ctrlreg1, dev->io_base + SMI_CR1); | ||
392 | writel(0, dev->io_base + SMI_CR2); | ||
393 | |||
394 | if (ret <= 0) { | ||
395 | ret = -EIO; | ||
396 | dev_err(&dev->pdev->dev, | ||
397 | "smi controller failed on write enable\n"); | ||
398 | } else { | ||
399 | /* check whether write mode status is set for required bank */ | ||
400 | if (dev->status & (1 << (bank + WM_SHIFT))) | ||
401 | ret = 0; | ||
402 | else { | ||
403 | dev_err(&dev->pdev->dev, "couldn't enable write\n"); | ||
404 | ret = -EIO; | ||
405 | } | ||
406 | } | ||
407 | |||
408 | mutex_unlock(&dev->lock); | ||
409 | return ret; | ||
410 | } | ||
411 | |||
412 | static inline u32 | ||
413 | get_sector_erase_cmd(struct spear_snor_flash *flash, u32 offset) | ||
414 | { | ||
415 | u32 cmd; | ||
416 | u8 *x = (u8 *)&cmd; | ||
417 | |||
418 | x[0] = flash->erase_cmd; | ||
419 | x[1] = offset >> 16; | ||
420 | x[2] = offset >> 8; | ||
421 | x[3] = offset; | ||
422 | |||
423 | return cmd; | ||
424 | } | ||
425 | |||
426 | /** | ||
427 | * spear_smi_erase_sector - erase one sector of flash | ||
428 | * @dev: structure of SMI information | ||
429 | * @command: erase command to be send | ||
430 | * @bank: bank to which this command needs to be send | ||
431 | * @bytes: size of command | ||
432 | * | ||
433 | * Erase one sector of flash memory at offset ``offset'' which is any | ||
434 | * address within the sector which should be erased. | ||
435 | * Returns 0 if successful, non-zero otherwise. | ||
436 | */ | ||
437 | static int spear_smi_erase_sector(struct spear_smi *dev, | ||
438 | u32 bank, u32 command, u32 bytes) | ||
439 | { | ||
440 | u32 ctrlreg1 = 0; | ||
441 | int ret; | ||
442 | |||
443 | ret = spear_smi_wait_till_ready(dev, bank, SMI_MAX_TIME_OUT); | ||
444 | if (ret) | ||
445 | return ret; | ||
446 | |||
447 | ret = spear_smi_write_enable(dev, bank); | ||
448 | if (ret) | ||
449 | return ret; | ||
450 | |||
451 | mutex_lock(&dev->lock); | ||
452 | |||
453 | ctrlreg1 = readl(dev->io_base + SMI_CR1); | ||
454 | writel((ctrlreg1 | SW_MODE) & ~WB_MODE, dev->io_base + SMI_CR1); | ||
455 | |||
456 | /* send command in sw mode */ | ||
457 | writel(command, dev->io_base + SMI_TR); | ||
458 | |||
459 | writel((bank << BANK_SHIFT) | SEND | TFIE | (bytes << TX_LEN_SHIFT), | ||
460 | dev->io_base + SMI_CR2); | ||
461 | |||
462 | ret = wait_event_interruptible_timeout(dev->cmd_complete, | ||
463 | dev->status & TFF, SMI_CMD_TIMEOUT); | ||
464 | |||
465 | if (ret <= 0) { | ||
466 | ret = -EIO; | ||
467 | dev_err(&dev->pdev->dev, "sector erase failed\n"); | ||
468 | } else | ||
469 | ret = 0; /* success */ | ||
470 | |||
471 | /* restore ctrl regs */ | ||
472 | writel(ctrlreg1, dev->io_base + SMI_CR1); | ||
473 | writel(0, dev->io_base + SMI_CR2); | ||
474 | |||
475 | mutex_unlock(&dev->lock); | ||
476 | return ret; | ||
477 | } | ||
478 | |||
479 | /** | ||
480 | * spear_mtd_erase - perform flash erase operation as requested by user | ||
481 | * @mtd: Provides the memory characteristics | ||
482 | * @e_info: Provides the erase information | ||
483 | * | ||
484 | * Erase an address range on the flash chip. The address range may extend | ||
485 | * one or more erase sectors. Return an error is there is a problem erasing. | ||
486 | */ | ||
487 | static int spear_mtd_erase(struct mtd_info *mtd, struct erase_info *e_info) | ||
488 | { | ||
489 | struct spear_snor_flash *flash = get_flash_data(mtd); | ||
490 | struct spear_smi *dev = mtd->priv; | ||
491 | u32 addr, command, bank; | ||
492 | int len, ret; | ||
493 | |||
494 | if (!flash || !dev) | ||
495 | return -ENODEV; | ||
496 | |||
497 | bank = flash->bank; | ||
498 | if (bank > dev->num_flashes - 1) { | ||
499 | dev_err(&dev->pdev->dev, "Invalid Bank Num"); | ||
500 | return -EINVAL; | ||
501 | } | ||
502 | |||
503 | addr = e_info->addr; | ||
504 | len = e_info->len; | ||
505 | |||
506 | mutex_lock(&flash->lock); | ||
507 | |||
508 | /* now erase sectors in loop */ | ||
509 | while (len) { | ||
510 | command = get_sector_erase_cmd(flash, addr); | ||
511 | /* preparing the command for flash */ | ||
512 | ret = spear_smi_erase_sector(dev, bank, command, 4); | ||
513 | if (ret) { | ||
514 | e_info->state = MTD_ERASE_FAILED; | ||
515 | mutex_unlock(&flash->lock); | ||
516 | return ret; | ||
517 | } | ||
518 | addr += mtd->erasesize; | ||
519 | len -= mtd->erasesize; | ||
520 | } | ||
521 | |||
522 | mutex_unlock(&flash->lock); | ||
523 | e_info->state = MTD_ERASE_DONE; | ||
524 | mtd_erase_callback(e_info); | ||
525 | |||
526 | return 0; | ||
527 | } | ||
528 | |||
529 | /** | ||
530 | * spear_mtd_read - performs flash read operation as requested by the user | ||
531 | * @mtd: MTD information of the memory bank | ||
532 | * @from: Address from which to start read | ||
533 | * @len: Number of bytes to be read | ||
534 | * @retlen: Fills the Number of bytes actually read | ||
535 | * @buf: Fills this after reading | ||
536 | * | ||
537 | * Read an address range from the flash chip. The address range | ||
538 | * may be any size provided it is within the physical boundaries. | ||
539 | * Returns 0 on success, non zero otherwise | ||
540 | */ | ||
541 | static int spear_mtd_read(struct mtd_info *mtd, loff_t from, size_t len, | ||
542 | size_t *retlen, u8 *buf) | ||
543 | { | ||
544 | struct spear_snor_flash *flash = get_flash_data(mtd); | ||
545 | struct spear_smi *dev = mtd->priv; | ||
546 | void *src; | ||
547 | u32 ctrlreg1, val; | ||
548 | int ret; | ||
549 | |||
550 | if (!flash || !dev) | ||
551 | return -ENODEV; | ||
552 | |||
553 | if (flash->bank > dev->num_flashes - 1) { | ||
554 | dev_err(&dev->pdev->dev, "Invalid Bank Num"); | ||
555 | return -EINVAL; | ||
556 | } | ||
557 | |||
558 | /* select address as per bank number */ | ||
559 | src = flash->base_addr + from; | ||
560 | |||
561 | mutex_lock(&flash->lock); | ||
562 | |||
563 | /* wait till previous write/erase is done. */ | ||
564 | ret = spear_smi_wait_till_ready(dev, flash->bank, SMI_MAX_TIME_OUT); | ||
565 | if (ret) { | ||
566 | mutex_unlock(&flash->lock); | ||
567 | return ret; | ||
568 | } | ||
569 | |||
570 | mutex_lock(&dev->lock); | ||
571 | /* put smi in hw mode not wbt mode */ | ||
572 | ctrlreg1 = val = readl(dev->io_base + SMI_CR1); | ||
573 | val &= ~(SW_MODE | WB_MODE); | ||
574 | if (flash->fast_mode) | ||
575 | val |= FAST_MODE; | ||
576 | |||
577 | writel(val, dev->io_base + SMI_CR1); | ||
578 | |||
579 | memcpy_fromio(buf, (u8 *)src, len); | ||
580 | |||
581 | /* restore ctrl reg1 */ | ||
582 | writel(ctrlreg1, dev->io_base + SMI_CR1); | ||
583 | mutex_unlock(&dev->lock); | ||
584 | |||
585 | *retlen = len; | ||
586 | mutex_unlock(&flash->lock); | ||
587 | |||
588 | return 0; | ||
589 | } | ||
590 | |||
591 | static inline int spear_smi_cpy_toio(struct spear_smi *dev, u32 bank, | ||
592 | void *dest, const void *src, size_t len) | ||
593 | { | ||
594 | int ret; | ||
595 | u32 ctrlreg1; | ||
596 | |||
597 | /* wait until finished previous write command. */ | ||
598 | ret = spear_smi_wait_till_ready(dev, bank, SMI_MAX_TIME_OUT); | ||
599 | if (ret) | ||
600 | return ret; | ||
601 | |||
602 | /* put smi in write enable */ | ||
603 | ret = spear_smi_write_enable(dev, bank); | ||
604 | if (ret) | ||
605 | return ret; | ||
606 | |||
607 | /* put smi in hw, write burst mode */ | ||
608 | mutex_lock(&dev->lock); | ||
609 | |||
610 | ctrlreg1 = readl(dev->io_base + SMI_CR1); | ||
611 | writel((ctrlreg1 | WB_MODE) & ~SW_MODE, dev->io_base + SMI_CR1); | ||
612 | |||
613 | memcpy_toio(dest, src, len); | ||
614 | |||
615 | writel(ctrlreg1, dev->io_base + SMI_CR1); | ||
616 | |||
617 | mutex_unlock(&dev->lock); | ||
618 | return 0; | ||
619 | } | ||
620 | |||
621 | /** | ||
622 | * spear_mtd_write - performs write operation as requested by the user. | ||
623 | * @mtd: MTD information of the memory bank. | ||
624 | * @to: Address to write. | ||
625 | * @len: Number of bytes to be written. | ||
626 | * @retlen: Number of bytes actually wrote. | ||
627 | * @buf: Buffer from which the data to be taken. | ||
628 | * | ||
629 | * Write an address range to the flash chip. Data must be written in | ||
630 | * flash_page_size chunks. The address range may be any size provided | ||
631 | * it is within the physical boundaries. | ||
632 | * Returns 0 on success, non zero otherwise | ||
633 | */ | ||
634 | static int spear_mtd_write(struct mtd_info *mtd, loff_t to, size_t len, | ||
635 | size_t *retlen, const u8 *buf) | ||
636 | { | ||
637 | struct spear_snor_flash *flash = get_flash_data(mtd); | ||
638 | struct spear_smi *dev = mtd->priv; | ||
639 | void *dest; | ||
640 | u32 page_offset, page_size; | ||
641 | int ret; | ||
642 | |||
643 | if (!flash || !dev) | ||
644 | return -ENODEV; | ||
645 | |||
646 | if (flash->bank > dev->num_flashes - 1) { | ||
647 | dev_err(&dev->pdev->dev, "Invalid Bank Num"); | ||
648 | return -EINVAL; | ||
649 | } | ||
650 | |||
651 | /* select address as per bank number */ | ||
652 | dest = flash->base_addr + to; | ||
653 | mutex_lock(&flash->lock); | ||
654 | |||
655 | page_offset = (u32)to % flash->page_size; | ||
656 | |||
657 | /* do if all the bytes fit onto one page */ | ||
658 | if (page_offset + len <= flash->page_size) { | ||
659 | ret = spear_smi_cpy_toio(dev, flash->bank, dest, buf, len); | ||
660 | if (!ret) | ||
661 | *retlen += len; | ||
662 | } else { | ||
663 | u32 i; | ||
664 | |||
665 | /* the size of data remaining on the first page */ | ||
666 | page_size = flash->page_size - page_offset; | ||
667 | |||
668 | ret = spear_smi_cpy_toio(dev, flash->bank, dest, buf, | ||
669 | page_size); | ||
670 | if (ret) | ||
671 | goto err_write; | ||
672 | else | ||
673 | *retlen += page_size; | ||
674 | |||
675 | /* write everything in pagesize chunks */ | ||
676 | for (i = page_size; i < len; i += page_size) { | ||
677 | page_size = len - i; | ||
678 | if (page_size > flash->page_size) | ||
679 | page_size = flash->page_size; | ||
680 | |||
681 | ret = spear_smi_cpy_toio(dev, flash->bank, dest + i, | ||
682 | buf + i, page_size); | ||
683 | if (ret) | ||
684 | break; | ||
685 | else | ||
686 | *retlen += page_size; | ||
687 | } | ||
688 | } | ||
689 | |||
690 | err_write: | ||
691 | mutex_unlock(&flash->lock); | ||
692 | |||
693 | return ret; | ||
694 | } | ||
695 | |||
696 | /** | ||
697 | * spear_smi_probe_flash - Detects the NOR Flash chip. | ||
698 | * @dev: structure of SMI information. | ||
699 | * @bank: bank on which flash must be probed | ||
700 | * | ||
701 | * This routine will check whether there exists a flash chip on a given memory | ||
702 | * bank ID. | ||
703 | * Return index of the probed flash in flash devices structure | ||
704 | */ | ||
705 | static int spear_smi_probe_flash(struct spear_smi *dev, u32 bank) | ||
706 | { | ||
707 | int ret; | ||
708 | u32 val = 0; | ||
709 | |||
710 | ret = spear_smi_wait_till_ready(dev, bank, SMI_PROBE_TIMEOUT); | ||
711 | if (ret) | ||
712 | return ret; | ||
713 | |||
714 | mutex_lock(&dev->lock); | ||
715 | |||
716 | dev->status = 0; /* Will be set in interrupt handler */ | ||
717 | /* put smi in sw mode */ | ||
718 | val = readl(dev->io_base + SMI_CR1); | ||
719 | writel(val | SW_MODE, dev->io_base + SMI_CR1); | ||
720 | |||
721 | /* send readid command in sw mode */ | ||
722 | writel(OPCODE_RDID, dev->io_base + SMI_TR); | ||
723 | |||
724 | val = (bank << BANK_SHIFT) | SEND | (1 << TX_LEN_SHIFT) | | ||
725 | (3 << RX_LEN_SHIFT) | TFIE; | ||
726 | writel(val, dev->io_base + SMI_CR2); | ||
727 | |||
728 | /* wait for TFF */ | ||
729 | ret = wait_event_interruptible_timeout(dev->cmd_complete, | ||
730 | dev->status & TFF, SMI_CMD_TIMEOUT); | ||
731 | if (ret <= 0) { | ||
732 | ret = -ENODEV; | ||
733 | goto err_probe; | ||
734 | } | ||
735 | |||
736 | /* get memory chip id */ | ||
737 | val = readl(dev->io_base + SMI_RR); | ||
738 | val &= 0x00ffffff; | ||
739 | ret = get_flash_index(val); | ||
740 | |||
741 | err_probe: | ||
742 | /* clear sw mode */ | ||
743 | val = readl(dev->io_base + SMI_CR1); | ||
744 | writel(val & ~SW_MODE, dev->io_base + SMI_CR1); | ||
745 | |||
746 | mutex_unlock(&dev->lock); | ||
747 | return ret; | ||
748 | } | ||
749 | |||
750 | |||
751 | #ifdef CONFIG_OF | ||
752 | static int __devinit spear_smi_probe_config_dt(struct platform_device *pdev, | ||
753 | struct device_node *np) | ||
754 | { | ||
755 | struct spear_smi_plat_data *pdata = dev_get_platdata(&pdev->dev); | ||
756 | struct device_node *pp = NULL; | ||
757 | const __be32 *addr; | ||
758 | u32 val; | ||
759 | int len; | ||
760 | int i = 0; | ||
761 | |||
762 | if (!np) | ||
763 | return -ENODEV; | ||
764 | |||
765 | of_property_read_u32(np, "clock-rate", &val); | ||
766 | pdata->clk_rate = val; | ||
767 | |||
768 | pdata->board_flash_info = devm_kzalloc(&pdev->dev, | ||
769 | sizeof(*pdata->board_flash_info), | ||
770 | GFP_KERNEL); | ||
771 | |||
772 | /* Fill structs for each subnode (flash device) */ | ||
773 | while ((pp = of_get_next_child(np, pp))) { | ||
774 | struct spear_smi_flash_info *flash_info; | ||
775 | |||
776 | flash_info = &pdata->board_flash_info[i]; | ||
777 | pdata->np[i] = pp; | ||
778 | |||
779 | /* Read base-addr and size from DT */ | ||
780 | addr = of_get_property(pp, "reg", &len); | ||
781 | pdata->board_flash_info->mem_base = be32_to_cpup(&addr[0]); | ||
782 | pdata->board_flash_info->size = be32_to_cpup(&addr[1]); | ||
783 | |||
784 | if (of_get_property(pp, "st,smi-fast-mode", NULL)) | ||
785 | pdata->board_flash_info->fast_mode = 1; | ||
786 | |||
787 | i++; | ||
788 | } | ||
789 | |||
790 | pdata->num_flashes = i; | ||
791 | |||
792 | return 0; | ||
793 | } | ||
794 | #else | ||
795 | static int __devinit spear_smi_probe_config_dt(struct platform_device *pdev, | ||
796 | struct device_node *np) | ||
797 | { | ||
798 | return -ENOSYS; | ||
799 | } | ||
800 | #endif | ||
801 | |||
802 | static int spear_smi_setup_banks(struct platform_device *pdev, | ||
803 | u32 bank, struct device_node *np) | ||
804 | { | ||
805 | struct spear_smi *dev = platform_get_drvdata(pdev); | ||
806 | struct mtd_part_parser_data ppdata = {}; | ||
807 | struct spear_smi_flash_info *flash_info; | ||
808 | struct spear_smi_plat_data *pdata; | ||
809 | struct spear_snor_flash *flash; | ||
810 | struct mtd_partition *parts = NULL; | ||
811 | int count = 0; | ||
812 | int flash_index; | ||
813 | int ret = 0; | ||
814 | |||
815 | pdata = dev_get_platdata(&pdev->dev); | ||
816 | if (bank > pdata->num_flashes - 1) | ||
817 | return -EINVAL; | ||
818 | |||
819 | flash_info = &pdata->board_flash_info[bank]; | ||
820 | if (!flash_info) | ||
821 | return -ENODEV; | ||
822 | |||
823 | flash = kzalloc(sizeof(*flash), GFP_ATOMIC); | ||
824 | if (!flash) | ||
825 | return -ENOMEM; | ||
826 | flash->bank = bank; | ||
827 | flash->fast_mode = flash_info->fast_mode ? 1 : 0; | ||
828 | mutex_init(&flash->lock); | ||
829 | |||
830 | /* verify whether nor flash is really present on board */ | ||
831 | flash_index = spear_smi_probe_flash(dev, bank); | ||
832 | if (flash_index < 0) { | ||
833 | dev_info(&dev->pdev->dev, "smi-nor%d not found\n", bank); | ||
834 | ret = flash_index; | ||
835 | goto err_probe; | ||
836 | } | ||
837 | /* map the memory for nor flash chip */ | ||
838 | flash->base_addr = ioremap(flash_info->mem_base, flash_info->size); | ||
839 | if (!flash->base_addr) { | ||
840 | ret = -EIO; | ||
841 | goto err_probe; | ||
842 | } | ||
843 | |||
844 | dev->flash[bank] = flash; | ||
845 | flash->mtd.priv = dev; | ||
846 | |||
847 | if (flash_info->name) | ||
848 | flash->mtd.name = flash_info->name; | ||
849 | else | ||
850 | flash->mtd.name = flash_devices[flash_index].name; | ||
851 | |||
852 | flash->mtd.type = MTD_NORFLASH; | ||
853 | flash->mtd.writesize = 1; | ||
854 | flash->mtd.flags = MTD_CAP_NORFLASH; | ||
855 | flash->mtd.size = flash_info->size; | ||
856 | flash->mtd.erasesize = flash_devices[flash_index].sectorsize; | ||
857 | flash->page_size = flash_devices[flash_index].pagesize; | ||
858 | flash->mtd.writebufsize = flash->page_size; | ||
859 | flash->erase_cmd = flash_devices[flash_index].erase_cmd; | ||
860 | flash->mtd._erase = spear_mtd_erase; | ||
861 | flash->mtd._read = spear_mtd_read; | ||
862 | flash->mtd._write = spear_mtd_write; | ||
863 | flash->dev_id = flash_devices[flash_index].device_id; | ||
864 | |||
865 | dev_info(&dev->pdev->dev, "mtd .name=%s .size=%llx(%lluM)\n", | ||
866 | flash->mtd.name, flash->mtd.size, | ||
867 | flash->mtd.size / (1024 * 1024)); | ||
868 | |||
869 | dev_info(&dev->pdev->dev, ".erasesize = 0x%x(%uK)\n", | ||
870 | flash->mtd.erasesize, flash->mtd.erasesize / 1024); | ||
871 | |||
872 | #ifndef CONFIG_OF | ||
873 | if (flash_info->partitions) { | ||
874 | parts = flash_info->partitions; | ||
875 | count = flash_info->nr_partitions; | ||
876 | } | ||
877 | #endif | ||
878 | ppdata.of_node = np; | ||
879 | |||
880 | ret = mtd_device_parse_register(&flash->mtd, NULL, &ppdata, parts, | ||
881 | count); | ||
882 | if (ret) { | ||
883 | dev_err(&dev->pdev->dev, "Err MTD partition=%d\n", ret); | ||
884 | goto err_map; | ||
885 | } | ||
886 | |||
887 | return 0; | ||
888 | |||
889 | err_map: | ||
890 | iounmap(flash->base_addr); | ||
891 | |||
892 | err_probe: | ||
893 | kfree(flash); | ||
894 | return ret; | ||
895 | } | ||
896 | |||
897 | /** | ||
898 | * spear_smi_probe - Entry routine | ||
899 | * @pdev: platform device structure | ||
900 | * | ||
901 | * This is the first routine which gets invoked during booting and does all | ||
902 | * initialization/allocation work. The routine looks for available memory banks, | ||
903 | * and do proper init for any found one. | ||
904 | * Returns 0 on success, non zero otherwise | ||
905 | */ | ||
906 | static int __devinit spear_smi_probe(struct platform_device *pdev) | ||
907 | { | ||
908 | struct device_node *np = pdev->dev.of_node; | ||
909 | struct spear_smi_plat_data *pdata = NULL; | ||
910 | struct spear_smi *dev; | ||
911 | struct resource *smi_base; | ||
912 | int irq, ret = 0; | ||
913 | int i; | ||
914 | |||
915 | if (np) { | ||
916 | pdata = devm_kzalloc(&pdev->dev, sizeof(*pdata), GFP_KERNEL); | ||
917 | if (!pdata) { | ||
918 | pr_err("%s: ERROR: no memory", __func__); | ||
919 | ret = -ENOMEM; | ||
920 | goto err; | ||
921 | } | ||
922 | pdev->dev.platform_data = pdata; | ||
923 | ret = spear_smi_probe_config_dt(pdev, np); | ||
924 | if (ret) { | ||
925 | ret = -ENODEV; | ||
926 | dev_err(&pdev->dev, "no platform data\n"); | ||
927 | goto err; | ||
928 | } | ||
929 | } else { | ||
930 | pdata = dev_get_platdata(&pdev->dev); | ||
931 | if (pdata < 0) { | ||
932 | ret = -ENODEV; | ||
933 | dev_err(&pdev->dev, "no platform data\n"); | ||
934 | goto err; | ||
935 | } | ||
936 | } | ||
937 | |||
938 | smi_base = platform_get_resource(pdev, IORESOURCE_MEM, 0); | ||
939 | if (!smi_base) { | ||
940 | ret = -ENODEV; | ||
941 | dev_err(&pdev->dev, "invalid smi base address\n"); | ||
942 | goto err; | ||
943 | } | ||
944 | |||
945 | irq = platform_get_irq(pdev, 0); | ||
946 | if (irq < 0) { | ||
947 | ret = -ENODEV; | ||
948 | dev_err(&pdev->dev, "invalid smi irq\n"); | ||
949 | goto err; | ||
950 | } | ||
951 | |||
952 | dev = kzalloc(sizeof(*dev), GFP_ATOMIC); | ||
953 | if (!dev) { | ||
954 | ret = -ENOMEM; | ||
955 | dev_err(&pdev->dev, "mem alloc fail\n"); | ||
956 | goto err; | ||
957 | } | ||
958 | |||
959 | smi_base = request_mem_region(smi_base->start, resource_size(smi_base), | ||
960 | pdev->name); | ||
961 | if (!smi_base) { | ||
962 | ret = -EBUSY; | ||
963 | dev_err(&pdev->dev, "request mem region fail\n"); | ||
964 | goto err_mem; | ||
965 | } | ||
966 | |||
967 | dev->io_base = ioremap(smi_base->start, resource_size(smi_base)); | ||
968 | if (!dev->io_base) { | ||
969 | ret = -EIO; | ||
970 | dev_err(&pdev->dev, "ioremap fail\n"); | ||
971 | goto err_ioremap; | ||
972 | } | ||
973 | |||
974 | dev->pdev = pdev; | ||
975 | dev->clk_rate = pdata->clk_rate; | ||
976 | |||
977 | if (dev->clk_rate < 0 || dev->clk_rate > SMI_MAX_CLOCK_FREQ) | ||
978 | dev->clk_rate = SMI_MAX_CLOCK_FREQ; | ||
979 | |||
980 | dev->num_flashes = pdata->num_flashes; | ||
981 | |||
982 | if (dev->num_flashes > MAX_NUM_FLASH_CHIP) { | ||
983 | dev_err(&pdev->dev, "exceeding max number of flashes\n"); | ||
984 | dev->num_flashes = MAX_NUM_FLASH_CHIP; | ||
985 | } | ||
986 | |||
987 | dev->clk = clk_get(&pdev->dev, NULL); | ||
988 | if (IS_ERR(dev->clk)) { | ||
989 | ret = PTR_ERR(dev->clk); | ||
990 | goto err_clk; | ||
991 | } | ||
992 | |||
993 | ret = clk_enable(dev->clk); | ||
994 | if (ret) | ||
995 | goto err_clk_enable; | ||
996 | |||
997 | ret = request_irq(irq, spear_smi_int_handler, 0, pdev->name, dev); | ||
998 | if (ret) { | ||
999 | dev_err(&dev->pdev->dev, "SMI IRQ allocation failed\n"); | ||
1000 | goto err_irq; | ||
1001 | } | ||
1002 | |||
1003 | mutex_init(&dev->lock); | ||
1004 | init_waitqueue_head(&dev->cmd_complete); | ||
1005 | spear_smi_hw_init(dev); | ||
1006 | platform_set_drvdata(pdev, dev); | ||
1007 | |||
1008 | /* loop for each serial nor-flash which is connected to smi */ | ||
1009 | for (i = 0; i < dev->num_flashes; i++) { | ||
1010 | ret = spear_smi_setup_banks(pdev, i, pdata->np[i]); | ||
1011 | if (ret) { | ||
1012 | dev_err(&dev->pdev->dev, "bank setup failed\n"); | ||
1013 | goto err_bank_setup; | ||
1014 | } | ||
1015 | } | ||
1016 | |||
1017 | return 0; | ||
1018 | |||
1019 | err_bank_setup: | ||
1020 | free_irq(irq, dev); | ||
1021 | platform_set_drvdata(pdev, NULL); | ||
1022 | err_irq: | ||
1023 | clk_disable(dev->clk); | ||
1024 | err_clk_enable: | ||
1025 | clk_put(dev->clk); | ||
1026 | err_clk: | ||
1027 | iounmap(dev->io_base); | ||
1028 | err_ioremap: | ||
1029 | release_mem_region(smi_base->start, resource_size(smi_base)); | ||
1030 | err_mem: | ||
1031 | kfree(dev); | ||
1032 | err: | ||
1033 | return ret; | ||
1034 | } | ||
1035 | |||
1036 | /** | ||
1037 | * spear_smi_remove - Exit routine | ||
1038 | * @pdev: platform device structure | ||
1039 | * | ||
1040 | * free all allocations and delete the partitions. | ||
1041 | */ | ||
1042 | static int __devexit spear_smi_remove(struct platform_device *pdev) | ||
1043 | { | ||
1044 | struct spear_smi *dev; | ||
1045 | struct spear_smi_plat_data *pdata; | ||
1046 | struct spear_snor_flash *flash; | ||
1047 | struct resource *smi_base; | ||
1048 | int ret; | ||
1049 | int i, irq; | ||
1050 | |||
1051 | dev = platform_get_drvdata(pdev); | ||
1052 | if (!dev) { | ||
1053 | dev_err(&pdev->dev, "dev is null\n"); | ||
1054 | return -ENODEV; | ||
1055 | } | ||
1056 | |||
1057 | pdata = dev_get_platdata(&pdev->dev); | ||
1058 | |||
1059 | /* clean up for all nor flash */ | ||
1060 | for (i = 0; i < dev->num_flashes; i++) { | ||
1061 | flash = dev->flash[i]; | ||
1062 | if (!flash) | ||
1063 | continue; | ||
1064 | |||
1065 | /* clean up mtd stuff */ | ||
1066 | ret = mtd_device_unregister(&flash->mtd); | ||
1067 | if (ret) | ||
1068 | dev_err(&pdev->dev, "error removing mtd\n"); | ||
1069 | |||
1070 | iounmap(flash->base_addr); | ||
1071 | kfree(flash); | ||
1072 | } | ||
1073 | |||
1074 | irq = platform_get_irq(pdev, 0); | ||
1075 | free_irq(irq, dev); | ||
1076 | |||
1077 | clk_disable(dev->clk); | ||
1078 | clk_put(dev->clk); | ||
1079 | iounmap(dev->io_base); | ||
1080 | kfree(dev); | ||
1081 | |||
1082 | smi_base = platform_get_resource(pdev, IORESOURCE_MEM, 0); | ||
1083 | release_mem_region(smi_base->start, resource_size(smi_base)); | ||
1084 | platform_set_drvdata(pdev, NULL); | ||
1085 | |||
1086 | return 0; | ||
1087 | } | ||
1088 | |||
1089 | int spear_smi_suspend(struct platform_device *pdev, pm_message_t state) | ||
1090 | { | ||
1091 | struct spear_smi *dev = platform_get_drvdata(pdev); | ||
1092 | |||
1093 | if (dev && dev->clk) | ||
1094 | clk_disable(dev->clk); | ||
1095 | |||
1096 | return 0; | ||
1097 | } | ||
1098 | |||
1099 | int spear_smi_resume(struct platform_device *pdev) | ||
1100 | { | ||
1101 | struct spear_smi *dev = platform_get_drvdata(pdev); | ||
1102 | int ret = -EPERM; | ||
1103 | |||
1104 | if (dev && dev->clk) | ||
1105 | ret = clk_enable(dev->clk); | ||
1106 | |||
1107 | if (!ret) | ||
1108 | spear_smi_hw_init(dev); | ||
1109 | return ret; | ||
1110 | } | ||
1111 | |||
1112 | #ifdef CONFIG_OF | ||
1113 | static const struct of_device_id spear_smi_id_table[] = { | ||
1114 | { .compatible = "st,spear600-smi" }, | ||
1115 | {} | ||
1116 | }; | ||
1117 | MODULE_DEVICE_TABLE(of, spear_smi_id_table); | ||
1118 | #endif | ||
1119 | |||
1120 | static struct platform_driver spear_smi_driver = { | ||
1121 | .driver = { | ||
1122 | .name = "smi", | ||
1123 | .bus = &platform_bus_type, | ||
1124 | .owner = THIS_MODULE, | ||
1125 | .of_match_table = of_match_ptr(spear_smi_id_table), | ||
1126 | }, | ||
1127 | .probe = spear_smi_probe, | ||
1128 | .remove = __devexit_p(spear_smi_remove), | ||
1129 | .suspend = spear_smi_suspend, | ||
1130 | .resume = spear_smi_resume, | ||
1131 | }; | ||
1132 | |||
1133 | static int spear_smi_init(void) | ||
1134 | { | ||
1135 | return platform_driver_register(&spear_smi_driver); | ||
1136 | } | ||
1137 | module_init(spear_smi_init); | ||
1138 | |||
1139 | static void spear_smi_exit(void) | ||
1140 | { | ||
1141 | platform_driver_unregister(&spear_smi_driver); | ||
1142 | } | ||
1143 | module_exit(spear_smi_exit); | ||
1144 | |||
1145 | MODULE_LICENSE("GPL"); | ||
1146 | MODULE_AUTHOR("Ashish Priyadarshi, Shiraz Hashim <shiraz.hashim@st.com>"); | ||
1147 | MODULE_DESCRIPTION("MTD SMI driver for serial nor flash chips"); | ||