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