162306a36Sopenharmony_ci/* 262306a36Sopenharmony_ci * SMI (Serial Memory Controller) device driver for Serial NOR Flash on 362306a36Sopenharmony_ci * SPEAr platform 462306a36Sopenharmony_ci * The serial nor interface is largely based on m25p80.c, however the SPI 562306a36Sopenharmony_ci * interface has been replaced by SMI. 662306a36Sopenharmony_ci * 762306a36Sopenharmony_ci * Copyright © 2010 STMicroelectronics. 862306a36Sopenharmony_ci * Ashish Priyadarshi 962306a36Sopenharmony_ci * Shiraz Hashim <shiraz.linux.kernel@gmail.com> 1062306a36Sopenharmony_ci * 1162306a36Sopenharmony_ci * This file is licensed under the terms of the GNU General Public 1262306a36Sopenharmony_ci * License version 2. This program is licensed "as is" without any 1362306a36Sopenharmony_ci * warranty of any kind, whether express or implied. 1462306a36Sopenharmony_ci */ 1562306a36Sopenharmony_ci 1662306a36Sopenharmony_ci#include <linux/clk.h> 1762306a36Sopenharmony_ci#include <linux/delay.h> 1862306a36Sopenharmony_ci#include <linux/device.h> 1962306a36Sopenharmony_ci#include <linux/err.h> 2062306a36Sopenharmony_ci#include <linux/errno.h> 2162306a36Sopenharmony_ci#include <linux/interrupt.h> 2262306a36Sopenharmony_ci#include <linux/io.h> 2362306a36Sopenharmony_ci#include <linux/ioport.h> 2462306a36Sopenharmony_ci#include <linux/jiffies.h> 2562306a36Sopenharmony_ci#include <linux/kernel.h> 2662306a36Sopenharmony_ci#include <linux/module.h> 2762306a36Sopenharmony_ci#include <linux/param.h> 2862306a36Sopenharmony_ci#include <linux/platform_device.h> 2962306a36Sopenharmony_ci#include <linux/pm.h> 3062306a36Sopenharmony_ci#include <linux/mtd/mtd.h> 3162306a36Sopenharmony_ci#include <linux/mtd/partitions.h> 3262306a36Sopenharmony_ci#include <linux/mtd/spear_smi.h> 3362306a36Sopenharmony_ci#include <linux/mutex.h> 3462306a36Sopenharmony_ci#include <linux/sched.h> 3562306a36Sopenharmony_ci#include <linux/slab.h> 3662306a36Sopenharmony_ci#include <linux/wait.h> 3762306a36Sopenharmony_ci#include <linux/of.h> 3862306a36Sopenharmony_ci#include <linux/of_address.h> 3962306a36Sopenharmony_ci 4062306a36Sopenharmony_ci/* SMI clock rate */ 4162306a36Sopenharmony_ci#define SMI_MAX_CLOCK_FREQ 50000000 /* 50 MHz */ 4262306a36Sopenharmony_ci 4362306a36Sopenharmony_ci/* MAX time out to safely come out of a erase or write busy conditions */ 4462306a36Sopenharmony_ci#define SMI_PROBE_TIMEOUT (HZ / 10) 4562306a36Sopenharmony_ci#define SMI_MAX_TIME_OUT (3 * HZ) 4662306a36Sopenharmony_ci 4762306a36Sopenharmony_ci/* timeout for command completion */ 4862306a36Sopenharmony_ci#define SMI_CMD_TIMEOUT (HZ / 10) 4962306a36Sopenharmony_ci 5062306a36Sopenharmony_ci/* registers of smi */ 5162306a36Sopenharmony_ci#define SMI_CR1 0x0 /* SMI control register 1 */ 5262306a36Sopenharmony_ci#define SMI_CR2 0x4 /* SMI control register 2 */ 5362306a36Sopenharmony_ci#define SMI_SR 0x8 /* SMI status register */ 5462306a36Sopenharmony_ci#define SMI_TR 0xC /* SMI transmit register */ 5562306a36Sopenharmony_ci#define SMI_RR 0x10 /* SMI receive register */ 5662306a36Sopenharmony_ci 5762306a36Sopenharmony_ci/* defines for control_reg 1 */ 5862306a36Sopenharmony_ci#define BANK_EN (0xF << 0) /* enables all banks */ 5962306a36Sopenharmony_ci#define DSEL_TIME (0x6 << 4) /* Deselect time 6 + 1 SMI_CK periods */ 6062306a36Sopenharmony_ci#define SW_MODE (0x1 << 28) /* enables SW Mode */ 6162306a36Sopenharmony_ci#define WB_MODE (0x1 << 29) /* Write Burst Mode */ 6262306a36Sopenharmony_ci#define FAST_MODE (0x1 << 15) /* Fast Mode */ 6362306a36Sopenharmony_ci#define HOLD1 (0x1 << 16) /* Clock Hold period selection */ 6462306a36Sopenharmony_ci 6562306a36Sopenharmony_ci/* defines for control_reg 2 */ 6662306a36Sopenharmony_ci#define SEND (0x1 << 7) /* Send data */ 6762306a36Sopenharmony_ci#define TFIE (0x1 << 8) /* Transmission Flag Interrupt Enable */ 6862306a36Sopenharmony_ci#define WCIE (0x1 << 9) /* Write Complete Interrupt Enable */ 6962306a36Sopenharmony_ci#define RD_STATUS_REG (0x1 << 10) /* reads status reg */ 7062306a36Sopenharmony_ci#define WE (0x1 << 11) /* Write Enable */ 7162306a36Sopenharmony_ci 7262306a36Sopenharmony_ci#define TX_LEN_SHIFT 0 7362306a36Sopenharmony_ci#define RX_LEN_SHIFT 4 7462306a36Sopenharmony_ci#define BANK_SHIFT 12 7562306a36Sopenharmony_ci 7662306a36Sopenharmony_ci/* defines for status register */ 7762306a36Sopenharmony_ci#define SR_WIP 0x1 /* Write in progress */ 7862306a36Sopenharmony_ci#define SR_WEL 0x2 /* Write enable latch */ 7962306a36Sopenharmony_ci#define SR_BP0 0x4 /* Block protect 0 */ 8062306a36Sopenharmony_ci#define SR_BP1 0x8 /* Block protect 1 */ 8162306a36Sopenharmony_ci#define SR_BP2 0x10 /* Block protect 2 */ 8262306a36Sopenharmony_ci#define SR_SRWD 0x80 /* SR write protect */ 8362306a36Sopenharmony_ci#define TFF 0x100 /* Transfer Finished Flag */ 8462306a36Sopenharmony_ci#define WCF 0x200 /* Transfer Finished Flag */ 8562306a36Sopenharmony_ci#define ERF1 0x400 /* Forbidden Write Request */ 8662306a36Sopenharmony_ci#define ERF2 0x800 /* Forbidden Access */ 8762306a36Sopenharmony_ci 8862306a36Sopenharmony_ci#define WM_SHIFT 12 8962306a36Sopenharmony_ci 9062306a36Sopenharmony_ci/* flash opcodes */ 9162306a36Sopenharmony_ci#define OPCODE_RDID 0x9f /* Read JEDEC ID */ 9262306a36Sopenharmony_ci 9362306a36Sopenharmony_ci/* Flash Device Ids maintenance section */ 9462306a36Sopenharmony_ci 9562306a36Sopenharmony_ci/* data structure to maintain flash ids from different vendors */ 9662306a36Sopenharmony_cistruct flash_device { 9762306a36Sopenharmony_ci char *name; 9862306a36Sopenharmony_ci u8 erase_cmd; 9962306a36Sopenharmony_ci u32 device_id; 10062306a36Sopenharmony_ci u32 pagesize; 10162306a36Sopenharmony_ci unsigned long sectorsize; 10262306a36Sopenharmony_ci unsigned long size_in_bytes; 10362306a36Sopenharmony_ci}; 10462306a36Sopenharmony_ci 10562306a36Sopenharmony_ci#define FLASH_ID(n, es, id, psize, ssize, size) \ 10662306a36Sopenharmony_ci{ \ 10762306a36Sopenharmony_ci .name = n, \ 10862306a36Sopenharmony_ci .erase_cmd = es, \ 10962306a36Sopenharmony_ci .device_id = id, \ 11062306a36Sopenharmony_ci .pagesize = psize, \ 11162306a36Sopenharmony_ci .sectorsize = ssize, \ 11262306a36Sopenharmony_ci .size_in_bytes = size \ 11362306a36Sopenharmony_ci} 11462306a36Sopenharmony_ci 11562306a36Sopenharmony_cistatic struct flash_device flash_devices[] = { 11662306a36Sopenharmony_ci FLASH_ID("st m25p16" , 0xd8, 0x00152020, 0x100, 0x10000, 0x200000), 11762306a36Sopenharmony_ci FLASH_ID("st m25p32" , 0xd8, 0x00162020, 0x100, 0x10000, 0x400000), 11862306a36Sopenharmony_ci FLASH_ID("st m25p64" , 0xd8, 0x00172020, 0x100, 0x10000, 0x800000), 11962306a36Sopenharmony_ci FLASH_ID("st m25p128" , 0xd8, 0x00182020, 0x100, 0x40000, 0x1000000), 12062306a36Sopenharmony_ci FLASH_ID("st m25p05" , 0xd8, 0x00102020, 0x80 , 0x8000 , 0x10000), 12162306a36Sopenharmony_ci FLASH_ID("st m25p10" , 0xd8, 0x00112020, 0x80 , 0x8000 , 0x20000), 12262306a36Sopenharmony_ci FLASH_ID("st m25p20" , 0xd8, 0x00122020, 0x100, 0x10000, 0x40000), 12362306a36Sopenharmony_ci FLASH_ID("st m25p40" , 0xd8, 0x00132020, 0x100, 0x10000, 0x80000), 12462306a36Sopenharmony_ci FLASH_ID("st m25p80" , 0xd8, 0x00142020, 0x100, 0x10000, 0x100000), 12562306a36Sopenharmony_ci FLASH_ID("st m45pe10" , 0xd8, 0x00114020, 0x100, 0x10000, 0x20000), 12662306a36Sopenharmony_ci FLASH_ID("st m45pe20" , 0xd8, 0x00124020, 0x100, 0x10000, 0x40000), 12762306a36Sopenharmony_ci FLASH_ID("st m45pe40" , 0xd8, 0x00134020, 0x100, 0x10000, 0x80000), 12862306a36Sopenharmony_ci FLASH_ID("st m45pe80" , 0xd8, 0x00144020, 0x100, 0x10000, 0x100000), 12962306a36Sopenharmony_ci FLASH_ID("sp s25fl004" , 0xd8, 0x00120201, 0x100, 0x10000, 0x80000), 13062306a36Sopenharmony_ci FLASH_ID("sp s25fl008" , 0xd8, 0x00130201, 0x100, 0x10000, 0x100000), 13162306a36Sopenharmony_ci FLASH_ID("sp s25fl016" , 0xd8, 0x00140201, 0x100, 0x10000, 0x200000), 13262306a36Sopenharmony_ci FLASH_ID("sp s25fl032" , 0xd8, 0x00150201, 0x100, 0x10000, 0x400000), 13362306a36Sopenharmony_ci FLASH_ID("sp s25fl064" , 0xd8, 0x00160201, 0x100, 0x10000, 0x800000), 13462306a36Sopenharmony_ci FLASH_ID("atmel 25f512" , 0x52, 0x0065001F, 0x80 , 0x8000 , 0x10000), 13562306a36Sopenharmony_ci FLASH_ID("atmel 25f1024" , 0x52, 0x0060001F, 0x100, 0x8000 , 0x20000), 13662306a36Sopenharmony_ci FLASH_ID("atmel 25f2048" , 0x52, 0x0063001F, 0x100, 0x10000, 0x40000), 13762306a36Sopenharmony_ci FLASH_ID("atmel 25f4096" , 0x52, 0x0064001F, 0x100, 0x10000, 0x80000), 13862306a36Sopenharmony_ci FLASH_ID("atmel 25fs040" , 0xd7, 0x0004661F, 0x100, 0x10000, 0x80000), 13962306a36Sopenharmony_ci FLASH_ID("mac 25l512" , 0xd8, 0x001020C2, 0x010, 0x10000, 0x10000), 14062306a36Sopenharmony_ci FLASH_ID("mac 25l1005" , 0xd8, 0x001120C2, 0x010, 0x10000, 0x20000), 14162306a36Sopenharmony_ci FLASH_ID("mac 25l2005" , 0xd8, 0x001220C2, 0x010, 0x10000, 0x40000), 14262306a36Sopenharmony_ci FLASH_ID("mac 25l4005" , 0xd8, 0x001320C2, 0x010, 0x10000, 0x80000), 14362306a36Sopenharmony_ci FLASH_ID("mac 25l4005a" , 0xd8, 0x001320C2, 0x010, 0x10000, 0x80000), 14462306a36Sopenharmony_ci FLASH_ID("mac 25l8005" , 0xd8, 0x001420C2, 0x010, 0x10000, 0x100000), 14562306a36Sopenharmony_ci FLASH_ID("mac 25l1605" , 0xd8, 0x001520C2, 0x100, 0x10000, 0x200000), 14662306a36Sopenharmony_ci FLASH_ID("mac 25l1605a" , 0xd8, 0x001520C2, 0x010, 0x10000, 0x200000), 14762306a36Sopenharmony_ci FLASH_ID("mac 25l3205" , 0xd8, 0x001620C2, 0x100, 0x10000, 0x400000), 14862306a36Sopenharmony_ci FLASH_ID("mac 25l3205a" , 0xd8, 0x001620C2, 0x100, 0x10000, 0x400000), 14962306a36Sopenharmony_ci FLASH_ID("mac 25l6405" , 0xd8, 0x001720C2, 0x100, 0x10000, 0x800000), 15062306a36Sopenharmony_ci}; 15162306a36Sopenharmony_ci 15262306a36Sopenharmony_ci/* Define spear specific structures */ 15362306a36Sopenharmony_ci 15462306a36Sopenharmony_cistruct spear_snor_flash; 15562306a36Sopenharmony_ci 15662306a36Sopenharmony_ci/** 15762306a36Sopenharmony_ci * struct spear_smi - Structure for SMI Device 15862306a36Sopenharmony_ci * 15962306a36Sopenharmony_ci * @clk: functional clock 16062306a36Sopenharmony_ci * @status: current status register of SMI. 16162306a36Sopenharmony_ci * @clk_rate: functional clock rate of SMI (default: SMI_MAX_CLOCK_FREQ) 16262306a36Sopenharmony_ci * @lock: lock to prevent parallel access of SMI. 16362306a36Sopenharmony_ci * @io_base: base address for registers of SMI. 16462306a36Sopenharmony_ci * @pdev: platform device 16562306a36Sopenharmony_ci * @cmd_complete: queue to wait for command completion of NOR-flash. 16662306a36Sopenharmony_ci * @num_flashes: number of flashes actually present on board. 16762306a36Sopenharmony_ci * @flash: separate structure for each Serial NOR-flash attached to SMI. 16862306a36Sopenharmony_ci */ 16962306a36Sopenharmony_cistruct spear_smi { 17062306a36Sopenharmony_ci struct clk *clk; 17162306a36Sopenharmony_ci u32 status; 17262306a36Sopenharmony_ci unsigned long clk_rate; 17362306a36Sopenharmony_ci struct mutex lock; 17462306a36Sopenharmony_ci void __iomem *io_base; 17562306a36Sopenharmony_ci struct platform_device *pdev; 17662306a36Sopenharmony_ci wait_queue_head_t cmd_complete; 17762306a36Sopenharmony_ci u32 num_flashes; 17862306a36Sopenharmony_ci struct spear_snor_flash *flash[MAX_NUM_FLASH_CHIP]; 17962306a36Sopenharmony_ci}; 18062306a36Sopenharmony_ci 18162306a36Sopenharmony_ci/** 18262306a36Sopenharmony_ci * struct spear_snor_flash - Structure for Serial NOR Flash 18362306a36Sopenharmony_ci * 18462306a36Sopenharmony_ci * @bank: Bank number(0, 1, 2, 3) for each NOR-flash. 18562306a36Sopenharmony_ci * @dev_id: Device ID of NOR-flash. 18662306a36Sopenharmony_ci * @lock: lock to manage flash read, write and erase operations 18762306a36Sopenharmony_ci * @mtd: MTD info for each NOR-flash. 18862306a36Sopenharmony_ci * @num_parts: Total number of partition in each bank of NOR-flash. 18962306a36Sopenharmony_ci * @parts: Partition info for each bank of NOR-flash. 19062306a36Sopenharmony_ci * @page_size: Page size of NOR-flash. 19162306a36Sopenharmony_ci * @base_addr: Base address of NOR-flash. 19262306a36Sopenharmony_ci * @erase_cmd: erase command may vary on different flash types 19362306a36Sopenharmony_ci * @fast_mode: flash supports read in fast mode 19462306a36Sopenharmony_ci */ 19562306a36Sopenharmony_cistruct spear_snor_flash { 19662306a36Sopenharmony_ci u32 bank; 19762306a36Sopenharmony_ci u32 dev_id; 19862306a36Sopenharmony_ci struct mutex lock; 19962306a36Sopenharmony_ci struct mtd_info mtd; 20062306a36Sopenharmony_ci u32 num_parts; 20162306a36Sopenharmony_ci struct mtd_partition *parts; 20262306a36Sopenharmony_ci u32 page_size; 20362306a36Sopenharmony_ci void __iomem *base_addr; 20462306a36Sopenharmony_ci u8 erase_cmd; 20562306a36Sopenharmony_ci u8 fast_mode; 20662306a36Sopenharmony_ci}; 20762306a36Sopenharmony_ci 20862306a36Sopenharmony_cistatic inline struct spear_snor_flash *get_flash_data(struct mtd_info *mtd) 20962306a36Sopenharmony_ci{ 21062306a36Sopenharmony_ci return container_of(mtd, struct spear_snor_flash, mtd); 21162306a36Sopenharmony_ci} 21262306a36Sopenharmony_ci 21362306a36Sopenharmony_ci/** 21462306a36Sopenharmony_ci * spear_smi_read_sr - Read status register of flash through SMI 21562306a36Sopenharmony_ci * @dev: structure of SMI information. 21662306a36Sopenharmony_ci * @bank: bank to which flash is connected 21762306a36Sopenharmony_ci * 21862306a36Sopenharmony_ci * This routine will return the status register of the flash chip present at the 21962306a36Sopenharmony_ci * given bank. 22062306a36Sopenharmony_ci */ 22162306a36Sopenharmony_cistatic int spear_smi_read_sr(struct spear_smi *dev, u32 bank) 22262306a36Sopenharmony_ci{ 22362306a36Sopenharmony_ci int ret; 22462306a36Sopenharmony_ci u32 ctrlreg1; 22562306a36Sopenharmony_ci 22662306a36Sopenharmony_ci mutex_lock(&dev->lock); 22762306a36Sopenharmony_ci dev->status = 0; /* Will be set in interrupt handler */ 22862306a36Sopenharmony_ci 22962306a36Sopenharmony_ci ctrlreg1 = readl(dev->io_base + SMI_CR1); 23062306a36Sopenharmony_ci /* program smi in hw mode */ 23162306a36Sopenharmony_ci writel(ctrlreg1 & ~(SW_MODE | WB_MODE), dev->io_base + SMI_CR1); 23262306a36Sopenharmony_ci 23362306a36Sopenharmony_ci /* performing a rsr instruction in hw mode */ 23462306a36Sopenharmony_ci writel((bank << BANK_SHIFT) | RD_STATUS_REG | TFIE, 23562306a36Sopenharmony_ci dev->io_base + SMI_CR2); 23662306a36Sopenharmony_ci 23762306a36Sopenharmony_ci /* wait for tff */ 23862306a36Sopenharmony_ci ret = wait_event_interruptible_timeout(dev->cmd_complete, 23962306a36Sopenharmony_ci dev->status & TFF, SMI_CMD_TIMEOUT); 24062306a36Sopenharmony_ci 24162306a36Sopenharmony_ci /* copy dev->status (lower 16 bits) in order to release lock */ 24262306a36Sopenharmony_ci if (ret > 0) 24362306a36Sopenharmony_ci ret = dev->status & 0xffff; 24462306a36Sopenharmony_ci else if (ret == 0) 24562306a36Sopenharmony_ci ret = -ETIMEDOUT; 24662306a36Sopenharmony_ci 24762306a36Sopenharmony_ci /* restore the ctrl regs state */ 24862306a36Sopenharmony_ci writel(ctrlreg1, dev->io_base + SMI_CR1); 24962306a36Sopenharmony_ci writel(0, dev->io_base + SMI_CR2); 25062306a36Sopenharmony_ci mutex_unlock(&dev->lock); 25162306a36Sopenharmony_ci 25262306a36Sopenharmony_ci return ret; 25362306a36Sopenharmony_ci} 25462306a36Sopenharmony_ci 25562306a36Sopenharmony_ci/** 25662306a36Sopenharmony_ci * spear_smi_wait_till_ready - wait till flash is ready 25762306a36Sopenharmony_ci * @dev: structure of SMI information. 25862306a36Sopenharmony_ci * @bank: flash corresponding to this bank 25962306a36Sopenharmony_ci * @timeout: timeout for busy wait condition 26062306a36Sopenharmony_ci * 26162306a36Sopenharmony_ci * This routine checks for WIP (write in progress) bit in Status register 26262306a36Sopenharmony_ci * If successful the routine returns 0 else -EBUSY 26362306a36Sopenharmony_ci */ 26462306a36Sopenharmony_cistatic int spear_smi_wait_till_ready(struct spear_smi *dev, u32 bank, 26562306a36Sopenharmony_ci unsigned long timeout) 26662306a36Sopenharmony_ci{ 26762306a36Sopenharmony_ci unsigned long finish; 26862306a36Sopenharmony_ci int status; 26962306a36Sopenharmony_ci 27062306a36Sopenharmony_ci finish = jiffies + timeout; 27162306a36Sopenharmony_ci do { 27262306a36Sopenharmony_ci status = spear_smi_read_sr(dev, bank); 27362306a36Sopenharmony_ci if (status < 0) { 27462306a36Sopenharmony_ci if (status == -ETIMEDOUT) 27562306a36Sopenharmony_ci continue; /* try till finish */ 27662306a36Sopenharmony_ci return status; 27762306a36Sopenharmony_ci } else if (!(status & SR_WIP)) { 27862306a36Sopenharmony_ci return 0; 27962306a36Sopenharmony_ci } 28062306a36Sopenharmony_ci 28162306a36Sopenharmony_ci cond_resched(); 28262306a36Sopenharmony_ci } while (!time_after_eq(jiffies, finish)); 28362306a36Sopenharmony_ci 28462306a36Sopenharmony_ci dev_err(&dev->pdev->dev, "smi controller is busy, timeout\n"); 28562306a36Sopenharmony_ci return -EBUSY; 28662306a36Sopenharmony_ci} 28762306a36Sopenharmony_ci 28862306a36Sopenharmony_ci/** 28962306a36Sopenharmony_ci * spear_smi_int_handler - SMI Interrupt Handler. 29062306a36Sopenharmony_ci * @irq: irq number 29162306a36Sopenharmony_ci * @dev_id: structure of SMI device, embedded in dev_id. 29262306a36Sopenharmony_ci * 29362306a36Sopenharmony_ci * The handler clears all interrupt conditions and records the status in 29462306a36Sopenharmony_ci * dev->status which is used by the driver later. 29562306a36Sopenharmony_ci */ 29662306a36Sopenharmony_cistatic irqreturn_t spear_smi_int_handler(int irq, void *dev_id) 29762306a36Sopenharmony_ci{ 29862306a36Sopenharmony_ci u32 status = 0; 29962306a36Sopenharmony_ci struct spear_smi *dev = dev_id; 30062306a36Sopenharmony_ci 30162306a36Sopenharmony_ci status = readl(dev->io_base + SMI_SR); 30262306a36Sopenharmony_ci 30362306a36Sopenharmony_ci if (unlikely(!status)) 30462306a36Sopenharmony_ci return IRQ_NONE; 30562306a36Sopenharmony_ci 30662306a36Sopenharmony_ci /* clear all interrupt conditions */ 30762306a36Sopenharmony_ci writel(0, dev->io_base + SMI_SR); 30862306a36Sopenharmony_ci 30962306a36Sopenharmony_ci /* copy the status register in dev->status */ 31062306a36Sopenharmony_ci dev->status |= status; 31162306a36Sopenharmony_ci 31262306a36Sopenharmony_ci /* send the completion */ 31362306a36Sopenharmony_ci wake_up_interruptible(&dev->cmd_complete); 31462306a36Sopenharmony_ci 31562306a36Sopenharmony_ci return IRQ_HANDLED; 31662306a36Sopenharmony_ci} 31762306a36Sopenharmony_ci 31862306a36Sopenharmony_ci/** 31962306a36Sopenharmony_ci * spear_smi_hw_init - initializes the smi controller. 32062306a36Sopenharmony_ci * @dev: structure of smi device 32162306a36Sopenharmony_ci * 32262306a36Sopenharmony_ci * this routine initializes the smi controller wit the default values 32362306a36Sopenharmony_ci */ 32462306a36Sopenharmony_cistatic void spear_smi_hw_init(struct spear_smi *dev) 32562306a36Sopenharmony_ci{ 32662306a36Sopenharmony_ci unsigned long rate = 0; 32762306a36Sopenharmony_ci u32 prescale = 0; 32862306a36Sopenharmony_ci u32 val; 32962306a36Sopenharmony_ci 33062306a36Sopenharmony_ci rate = clk_get_rate(dev->clk); 33162306a36Sopenharmony_ci 33262306a36Sopenharmony_ci /* functional clock of smi */ 33362306a36Sopenharmony_ci prescale = DIV_ROUND_UP(rate, dev->clk_rate); 33462306a36Sopenharmony_ci 33562306a36Sopenharmony_ci /* 33662306a36Sopenharmony_ci * setting the standard values, fast mode, prescaler for 33762306a36Sopenharmony_ci * SMI_MAX_CLOCK_FREQ (50MHz) operation and bank enable 33862306a36Sopenharmony_ci */ 33962306a36Sopenharmony_ci val = HOLD1 | BANK_EN | DSEL_TIME | (prescale << 8); 34062306a36Sopenharmony_ci 34162306a36Sopenharmony_ci mutex_lock(&dev->lock); 34262306a36Sopenharmony_ci /* clear all interrupt conditions */ 34362306a36Sopenharmony_ci writel(0, dev->io_base + SMI_SR); 34462306a36Sopenharmony_ci 34562306a36Sopenharmony_ci writel(val, dev->io_base + SMI_CR1); 34662306a36Sopenharmony_ci mutex_unlock(&dev->lock); 34762306a36Sopenharmony_ci} 34862306a36Sopenharmony_ci 34962306a36Sopenharmony_ci/** 35062306a36Sopenharmony_ci * get_flash_index - match chip id from a flash list. 35162306a36Sopenharmony_ci * @flash_id: a valid nor flash chip id obtained from board. 35262306a36Sopenharmony_ci * 35362306a36Sopenharmony_ci * try to validate the chip id by matching from a list, if not found then simply 35462306a36Sopenharmony_ci * returns negative. In case of success returns index in to the flash devices 35562306a36Sopenharmony_ci * array. 35662306a36Sopenharmony_ci */ 35762306a36Sopenharmony_cistatic int get_flash_index(u32 flash_id) 35862306a36Sopenharmony_ci{ 35962306a36Sopenharmony_ci int index; 36062306a36Sopenharmony_ci 36162306a36Sopenharmony_ci /* Matches chip-id to entire list of 'serial-nor flash' ids */ 36262306a36Sopenharmony_ci for (index = 0; index < ARRAY_SIZE(flash_devices); index++) { 36362306a36Sopenharmony_ci if (flash_devices[index].device_id == flash_id) 36462306a36Sopenharmony_ci return index; 36562306a36Sopenharmony_ci } 36662306a36Sopenharmony_ci 36762306a36Sopenharmony_ci /* Memory chip is not listed and not supported */ 36862306a36Sopenharmony_ci return -ENODEV; 36962306a36Sopenharmony_ci} 37062306a36Sopenharmony_ci 37162306a36Sopenharmony_ci/** 37262306a36Sopenharmony_ci * spear_smi_write_enable - Enable the flash to do write operation 37362306a36Sopenharmony_ci * @dev: structure of SMI device 37462306a36Sopenharmony_ci * @bank: enable write for flash connected to this bank 37562306a36Sopenharmony_ci * 37662306a36Sopenharmony_ci * Set write enable latch with Write Enable command. 37762306a36Sopenharmony_ci * Returns 0 on success. 37862306a36Sopenharmony_ci */ 37962306a36Sopenharmony_cistatic int spear_smi_write_enable(struct spear_smi *dev, u32 bank) 38062306a36Sopenharmony_ci{ 38162306a36Sopenharmony_ci int ret; 38262306a36Sopenharmony_ci u32 ctrlreg1; 38362306a36Sopenharmony_ci 38462306a36Sopenharmony_ci mutex_lock(&dev->lock); 38562306a36Sopenharmony_ci dev->status = 0; /* Will be set in interrupt handler */ 38662306a36Sopenharmony_ci 38762306a36Sopenharmony_ci ctrlreg1 = readl(dev->io_base + SMI_CR1); 38862306a36Sopenharmony_ci /* program smi in h/w mode */ 38962306a36Sopenharmony_ci writel(ctrlreg1 & ~SW_MODE, dev->io_base + SMI_CR1); 39062306a36Sopenharmony_ci 39162306a36Sopenharmony_ci /* give the flash, write enable command */ 39262306a36Sopenharmony_ci writel((bank << BANK_SHIFT) | WE | TFIE, dev->io_base + SMI_CR2); 39362306a36Sopenharmony_ci 39462306a36Sopenharmony_ci ret = wait_event_interruptible_timeout(dev->cmd_complete, 39562306a36Sopenharmony_ci dev->status & TFF, SMI_CMD_TIMEOUT); 39662306a36Sopenharmony_ci 39762306a36Sopenharmony_ci /* restore the ctrl regs state */ 39862306a36Sopenharmony_ci writel(ctrlreg1, dev->io_base + SMI_CR1); 39962306a36Sopenharmony_ci writel(0, dev->io_base + SMI_CR2); 40062306a36Sopenharmony_ci 40162306a36Sopenharmony_ci if (ret == 0) { 40262306a36Sopenharmony_ci ret = -EIO; 40362306a36Sopenharmony_ci dev_err(&dev->pdev->dev, 40462306a36Sopenharmony_ci "smi controller failed on write enable\n"); 40562306a36Sopenharmony_ci } else if (ret > 0) { 40662306a36Sopenharmony_ci /* check whether write mode status is set for required bank */ 40762306a36Sopenharmony_ci if (dev->status & (1 << (bank + WM_SHIFT))) 40862306a36Sopenharmony_ci ret = 0; 40962306a36Sopenharmony_ci else { 41062306a36Sopenharmony_ci dev_err(&dev->pdev->dev, "couldn't enable write\n"); 41162306a36Sopenharmony_ci ret = -EIO; 41262306a36Sopenharmony_ci } 41362306a36Sopenharmony_ci } 41462306a36Sopenharmony_ci 41562306a36Sopenharmony_ci mutex_unlock(&dev->lock); 41662306a36Sopenharmony_ci return ret; 41762306a36Sopenharmony_ci} 41862306a36Sopenharmony_ci 41962306a36Sopenharmony_cistatic inline u32 42062306a36Sopenharmony_ciget_sector_erase_cmd(struct spear_snor_flash *flash, u32 offset) 42162306a36Sopenharmony_ci{ 42262306a36Sopenharmony_ci u32 cmd; 42362306a36Sopenharmony_ci u8 *x = (u8 *)&cmd; 42462306a36Sopenharmony_ci 42562306a36Sopenharmony_ci x[0] = flash->erase_cmd; 42662306a36Sopenharmony_ci x[1] = offset >> 16; 42762306a36Sopenharmony_ci x[2] = offset >> 8; 42862306a36Sopenharmony_ci x[3] = offset; 42962306a36Sopenharmony_ci 43062306a36Sopenharmony_ci return cmd; 43162306a36Sopenharmony_ci} 43262306a36Sopenharmony_ci 43362306a36Sopenharmony_ci/** 43462306a36Sopenharmony_ci * spear_smi_erase_sector - erase one sector of flash 43562306a36Sopenharmony_ci * @dev: structure of SMI information 43662306a36Sopenharmony_ci * @command: erase command to be send 43762306a36Sopenharmony_ci * @bank: bank to which this command needs to be send 43862306a36Sopenharmony_ci * @bytes: size of command 43962306a36Sopenharmony_ci * 44062306a36Sopenharmony_ci * Erase one sector of flash memory at offset ``offset'' which is any 44162306a36Sopenharmony_ci * address within the sector which should be erased. 44262306a36Sopenharmony_ci * Returns 0 if successful, non-zero otherwise. 44362306a36Sopenharmony_ci */ 44462306a36Sopenharmony_cistatic int spear_smi_erase_sector(struct spear_smi *dev, 44562306a36Sopenharmony_ci u32 bank, u32 command, u32 bytes) 44662306a36Sopenharmony_ci{ 44762306a36Sopenharmony_ci u32 ctrlreg1 = 0; 44862306a36Sopenharmony_ci int ret; 44962306a36Sopenharmony_ci 45062306a36Sopenharmony_ci ret = spear_smi_wait_till_ready(dev, bank, SMI_MAX_TIME_OUT); 45162306a36Sopenharmony_ci if (ret) 45262306a36Sopenharmony_ci return ret; 45362306a36Sopenharmony_ci 45462306a36Sopenharmony_ci ret = spear_smi_write_enable(dev, bank); 45562306a36Sopenharmony_ci if (ret) 45662306a36Sopenharmony_ci return ret; 45762306a36Sopenharmony_ci 45862306a36Sopenharmony_ci mutex_lock(&dev->lock); 45962306a36Sopenharmony_ci 46062306a36Sopenharmony_ci ctrlreg1 = readl(dev->io_base + SMI_CR1); 46162306a36Sopenharmony_ci writel((ctrlreg1 | SW_MODE) & ~WB_MODE, dev->io_base + SMI_CR1); 46262306a36Sopenharmony_ci 46362306a36Sopenharmony_ci /* send command in sw mode */ 46462306a36Sopenharmony_ci writel(command, dev->io_base + SMI_TR); 46562306a36Sopenharmony_ci 46662306a36Sopenharmony_ci writel((bank << BANK_SHIFT) | SEND | TFIE | (bytes << TX_LEN_SHIFT), 46762306a36Sopenharmony_ci dev->io_base + SMI_CR2); 46862306a36Sopenharmony_ci 46962306a36Sopenharmony_ci ret = wait_event_interruptible_timeout(dev->cmd_complete, 47062306a36Sopenharmony_ci dev->status & TFF, SMI_CMD_TIMEOUT); 47162306a36Sopenharmony_ci 47262306a36Sopenharmony_ci if (ret == 0) { 47362306a36Sopenharmony_ci ret = -EIO; 47462306a36Sopenharmony_ci dev_err(&dev->pdev->dev, "sector erase failed\n"); 47562306a36Sopenharmony_ci } else if (ret > 0) 47662306a36Sopenharmony_ci ret = 0; /* success */ 47762306a36Sopenharmony_ci 47862306a36Sopenharmony_ci /* restore ctrl regs */ 47962306a36Sopenharmony_ci writel(ctrlreg1, dev->io_base + SMI_CR1); 48062306a36Sopenharmony_ci writel(0, dev->io_base + SMI_CR2); 48162306a36Sopenharmony_ci 48262306a36Sopenharmony_ci mutex_unlock(&dev->lock); 48362306a36Sopenharmony_ci return ret; 48462306a36Sopenharmony_ci} 48562306a36Sopenharmony_ci 48662306a36Sopenharmony_ci/** 48762306a36Sopenharmony_ci * spear_mtd_erase - perform flash erase operation as requested by user 48862306a36Sopenharmony_ci * @mtd: Provides the memory characteristics 48962306a36Sopenharmony_ci * @e_info: Provides the erase information 49062306a36Sopenharmony_ci * 49162306a36Sopenharmony_ci * Erase an address range on the flash chip. The address range may extend 49262306a36Sopenharmony_ci * one or more erase sectors. Return an error is there is a problem erasing. 49362306a36Sopenharmony_ci */ 49462306a36Sopenharmony_cistatic int spear_mtd_erase(struct mtd_info *mtd, struct erase_info *e_info) 49562306a36Sopenharmony_ci{ 49662306a36Sopenharmony_ci struct spear_snor_flash *flash = get_flash_data(mtd); 49762306a36Sopenharmony_ci struct spear_smi *dev = mtd->priv; 49862306a36Sopenharmony_ci u32 addr, command, bank; 49962306a36Sopenharmony_ci int len, ret; 50062306a36Sopenharmony_ci 50162306a36Sopenharmony_ci if (!flash || !dev) 50262306a36Sopenharmony_ci return -ENODEV; 50362306a36Sopenharmony_ci 50462306a36Sopenharmony_ci bank = flash->bank; 50562306a36Sopenharmony_ci if (bank > dev->num_flashes - 1) { 50662306a36Sopenharmony_ci dev_err(&dev->pdev->dev, "Invalid Bank Num"); 50762306a36Sopenharmony_ci return -EINVAL; 50862306a36Sopenharmony_ci } 50962306a36Sopenharmony_ci 51062306a36Sopenharmony_ci addr = e_info->addr; 51162306a36Sopenharmony_ci len = e_info->len; 51262306a36Sopenharmony_ci 51362306a36Sopenharmony_ci mutex_lock(&flash->lock); 51462306a36Sopenharmony_ci 51562306a36Sopenharmony_ci /* now erase sectors in loop */ 51662306a36Sopenharmony_ci while (len) { 51762306a36Sopenharmony_ci command = get_sector_erase_cmd(flash, addr); 51862306a36Sopenharmony_ci /* preparing the command for flash */ 51962306a36Sopenharmony_ci ret = spear_smi_erase_sector(dev, bank, command, 4); 52062306a36Sopenharmony_ci if (ret) { 52162306a36Sopenharmony_ci mutex_unlock(&flash->lock); 52262306a36Sopenharmony_ci return ret; 52362306a36Sopenharmony_ci } 52462306a36Sopenharmony_ci addr += mtd->erasesize; 52562306a36Sopenharmony_ci len -= mtd->erasesize; 52662306a36Sopenharmony_ci } 52762306a36Sopenharmony_ci 52862306a36Sopenharmony_ci mutex_unlock(&flash->lock); 52962306a36Sopenharmony_ci 53062306a36Sopenharmony_ci return 0; 53162306a36Sopenharmony_ci} 53262306a36Sopenharmony_ci 53362306a36Sopenharmony_ci/** 53462306a36Sopenharmony_ci * spear_mtd_read - performs flash read operation as requested by the user 53562306a36Sopenharmony_ci * @mtd: MTD information of the memory bank 53662306a36Sopenharmony_ci * @from: Address from which to start read 53762306a36Sopenharmony_ci * @len: Number of bytes to be read 53862306a36Sopenharmony_ci * @retlen: Fills the Number of bytes actually read 53962306a36Sopenharmony_ci * @buf: Fills this after reading 54062306a36Sopenharmony_ci * 54162306a36Sopenharmony_ci * Read an address range from the flash chip. The address range 54262306a36Sopenharmony_ci * may be any size provided it is within the physical boundaries. 54362306a36Sopenharmony_ci * Returns 0 on success, non zero otherwise 54462306a36Sopenharmony_ci */ 54562306a36Sopenharmony_cistatic int spear_mtd_read(struct mtd_info *mtd, loff_t from, size_t len, 54662306a36Sopenharmony_ci size_t *retlen, u8 *buf) 54762306a36Sopenharmony_ci{ 54862306a36Sopenharmony_ci struct spear_snor_flash *flash = get_flash_data(mtd); 54962306a36Sopenharmony_ci struct spear_smi *dev = mtd->priv; 55062306a36Sopenharmony_ci void __iomem *src; 55162306a36Sopenharmony_ci u32 ctrlreg1, val; 55262306a36Sopenharmony_ci int ret; 55362306a36Sopenharmony_ci 55462306a36Sopenharmony_ci if (!flash || !dev) 55562306a36Sopenharmony_ci return -ENODEV; 55662306a36Sopenharmony_ci 55762306a36Sopenharmony_ci if (flash->bank > dev->num_flashes - 1) { 55862306a36Sopenharmony_ci dev_err(&dev->pdev->dev, "Invalid Bank Num"); 55962306a36Sopenharmony_ci return -EINVAL; 56062306a36Sopenharmony_ci } 56162306a36Sopenharmony_ci 56262306a36Sopenharmony_ci /* select address as per bank number */ 56362306a36Sopenharmony_ci src = flash->base_addr + from; 56462306a36Sopenharmony_ci 56562306a36Sopenharmony_ci mutex_lock(&flash->lock); 56662306a36Sopenharmony_ci 56762306a36Sopenharmony_ci /* wait till previous write/erase is done. */ 56862306a36Sopenharmony_ci ret = spear_smi_wait_till_ready(dev, flash->bank, SMI_MAX_TIME_OUT); 56962306a36Sopenharmony_ci if (ret) { 57062306a36Sopenharmony_ci mutex_unlock(&flash->lock); 57162306a36Sopenharmony_ci return ret; 57262306a36Sopenharmony_ci } 57362306a36Sopenharmony_ci 57462306a36Sopenharmony_ci mutex_lock(&dev->lock); 57562306a36Sopenharmony_ci /* put smi in hw mode not wbt mode */ 57662306a36Sopenharmony_ci ctrlreg1 = val = readl(dev->io_base + SMI_CR1); 57762306a36Sopenharmony_ci val &= ~(SW_MODE | WB_MODE); 57862306a36Sopenharmony_ci if (flash->fast_mode) 57962306a36Sopenharmony_ci val |= FAST_MODE; 58062306a36Sopenharmony_ci 58162306a36Sopenharmony_ci writel(val, dev->io_base + SMI_CR1); 58262306a36Sopenharmony_ci 58362306a36Sopenharmony_ci memcpy_fromio(buf, src, len); 58462306a36Sopenharmony_ci 58562306a36Sopenharmony_ci /* restore ctrl reg1 */ 58662306a36Sopenharmony_ci writel(ctrlreg1, dev->io_base + SMI_CR1); 58762306a36Sopenharmony_ci mutex_unlock(&dev->lock); 58862306a36Sopenharmony_ci 58962306a36Sopenharmony_ci *retlen = len; 59062306a36Sopenharmony_ci mutex_unlock(&flash->lock); 59162306a36Sopenharmony_ci 59262306a36Sopenharmony_ci return 0; 59362306a36Sopenharmony_ci} 59462306a36Sopenharmony_ci 59562306a36Sopenharmony_ci/* 59662306a36Sopenharmony_ci * The purpose of this function is to ensure a memcpy_toio() with byte writes 59762306a36Sopenharmony_ci * only. Its structure is inspired from the ARM implementation of _memcpy_toio() 59862306a36Sopenharmony_ci * which also does single byte writes but cannot be used here as this is just an 59962306a36Sopenharmony_ci * implementation detail and not part of the API. Not mentioning the comment 60062306a36Sopenharmony_ci * stating that _memcpy_toio() should be optimized. 60162306a36Sopenharmony_ci */ 60262306a36Sopenharmony_cistatic void spear_smi_memcpy_toio_b(volatile void __iomem *dest, 60362306a36Sopenharmony_ci const void *src, size_t len) 60462306a36Sopenharmony_ci{ 60562306a36Sopenharmony_ci const unsigned char *from = src; 60662306a36Sopenharmony_ci 60762306a36Sopenharmony_ci while (len) { 60862306a36Sopenharmony_ci len--; 60962306a36Sopenharmony_ci writeb(*from, dest); 61062306a36Sopenharmony_ci from++; 61162306a36Sopenharmony_ci dest++; 61262306a36Sopenharmony_ci } 61362306a36Sopenharmony_ci} 61462306a36Sopenharmony_ci 61562306a36Sopenharmony_cistatic inline int spear_smi_cpy_toio(struct spear_smi *dev, u32 bank, 61662306a36Sopenharmony_ci void __iomem *dest, const void *src, size_t len) 61762306a36Sopenharmony_ci{ 61862306a36Sopenharmony_ci int ret; 61962306a36Sopenharmony_ci u32 ctrlreg1; 62062306a36Sopenharmony_ci 62162306a36Sopenharmony_ci /* wait until finished previous write command. */ 62262306a36Sopenharmony_ci ret = spear_smi_wait_till_ready(dev, bank, SMI_MAX_TIME_OUT); 62362306a36Sopenharmony_ci if (ret) 62462306a36Sopenharmony_ci return ret; 62562306a36Sopenharmony_ci 62662306a36Sopenharmony_ci /* put smi in write enable */ 62762306a36Sopenharmony_ci ret = spear_smi_write_enable(dev, bank); 62862306a36Sopenharmony_ci if (ret) 62962306a36Sopenharmony_ci return ret; 63062306a36Sopenharmony_ci 63162306a36Sopenharmony_ci /* put smi in hw, write burst mode */ 63262306a36Sopenharmony_ci mutex_lock(&dev->lock); 63362306a36Sopenharmony_ci 63462306a36Sopenharmony_ci ctrlreg1 = readl(dev->io_base + SMI_CR1); 63562306a36Sopenharmony_ci writel((ctrlreg1 | WB_MODE) & ~SW_MODE, dev->io_base + SMI_CR1); 63662306a36Sopenharmony_ci 63762306a36Sopenharmony_ci /* 63862306a36Sopenharmony_ci * In Write Burst mode (WB_MODE), the specs states that writes must be: 63962306a36Sopenharmony_ci * - incremental 64062306a36Sopenharmony_ci * - of the same size 64162306a36Sopenharmony_ci * The ARM implementation of memcpy_toio() will optimize the number of 64262306a36Sopenharmony_ci * I/O by using as much 4-byte writes as possible, surrounded by 64362306a36Sopenharmony_ci * 2-byte/1-byte access if: 64462306a36Sopenharmony_ci * - the destination is not 4-byte aligned 64562306a36Sopenharmony_ci * - the length is not a multiple of 4-byte. 64662306a36Sopenharmony_ci * Avoid this alternance of write access size by using our own 'byte 64762306a36Sopenharmony_ci * access' helper if at least one of the two conditions above is true. 64862306a36Sopenharmony_ci */ 64962306a36Sopenharmony_ci if (IS_ALIGNED(len, sizeof(u32)) && 65062306a36Sopenharmony_ci IS_ALIGNED((uintptr_t)dest, sizeof(u32))) 65162306a36Sopenharmony_ci memcpy_toio(dest, src, len); 65262306a36Sopenharmony_ci else 65362306a36Sopenharmony_ci spear_smi_memcpy_toio_b(dest, src, len); 65462306a36Sopenharmony_ci 65562306a36Sopenharmony_ci writel(ctrlreg1, dev->io_base + SMI_CR1); 65662306a36Sopenharmony_ci 65762306a36Sopenharmony_ci mutex_unlock(&dev->lock); 65862306a36Sopenharmony_ci return 0; 65962306a36Sopenharmony_ci} 66062306a36Sopenharmony_ci 66162306a36Sopenharmony_ci/** 66262306a36Sopenharmony_ci * spear_mtd_write - performs write operation as requested by the user. 66362306a36Sopenharmony_ci * @mtd: MTD information of the memory bank. 66462306a36Sopenharmony_ci * @to: Address to write. 66562306a36Sopenharmony_ci * @len: Number of bytes to be written. 66662306a36Sopenharmony_ci * @retlen: Number of bytes actually wrote. 66762306a36Sopenharmony_ci * @buf: Buffer from which the data to be taken. 66862306a36Sopenharmony_ci * 66962306a36Sopenharmony_ci * Write an address range to the flash chip. Data must be written in 67062306a36Sopenharmony_ci * flash_page_size chunks. The address range may be any size provided 67162306a36Sopenharmony_ci * it is within the physical boundaries. 67262306a36Sopenharmony_ci * Returns 0 on success, non zero otherwise 67362306a36Sopenharmony_ci */ 67462306a36Sopenharmony_cistatic int spear_mtd_write(struct mtd_info *mtd, loff_t to, size_t len, 67562306a36Sopenharmony_ci size_t *retlen, const u8 *buf) 67662306a36Sopenharmony_ci{ 67762306a36Sopenharmony_ci struct spear_snor_flash *flash = get_flash_data(mtd); 67862306a36Sopenharmony_ci struct spear_smi *dev = mtd->priv; 67962306a36Sopenharmony_ci void __iomem *dest; 68062306a36Sopenharmony_ci u32 page_offset, page_size; 68162306a36Sopenharmony_ci int ret; 68262306a36Sopenharmony_ci 68362306a36Sopenharmony_ci if (!flash || !dev) 68462306a36Sopenharmony_ci return -ENODEV; 68562306a36Sopenharmony_ci 68662306a36Sopenharmony_ci if (flash->bank > dev->num_flashes - 1) { 68762306a36Sopenharmony_ci dev_err(&dev->pdev->dev, "Invalid Bank Num"); 68862306a36Sopenharmony_ci return -EINVAL; 68962306a36Sopenharmony_ci } 69062306a36Sopenharmony_ci 69162306a36Sopenharmony_ci /* select address as per bank number */ 69262306a36Sopenharmony_ci dest = flash->base_addr + to; 69362306a36Sopenharmony_ci mutex_lock(&flash->lock); 69462306a36Sopenharmony_ci 69562306a36Sopenharmony_ci page_offset = (u32)to % flash->page_size; 69662306a36Sopenharmony_ci 69762306a36Sopenharmony_ci /* do if all the bytes fit onto one page */ 69862306a36Sopenharmony_ci if (page_offset + len <= flash->page_size) { 69962306a36Sopenharmony_ci ret = spear_smi_cpy_toio(dev, flash->bank, dest, buf, len); 70062306a36Sopenharmony_ci if (!ret) 70162306a36Sopenharmony_ci *retlen += len; 70262306a36Sopenharmony_ci } else { 70362306a36Sopenharmony_ci u32 i; 70462306a36Sopenharmony_ci 70562306a36Sopenharmony_ci /* the size of data remaining on the first page */ 70662306a36Sopenharmony_ci page_size = flash->page_size - page_offset; 70762306a36Sopenharmony_ci 70862306a36Sopenharmony_ci ret = spear_smi_cpy_toio(dev, flash->bank, dest, buf, 70962306a36Sopenharmony_ci page_size); 71062306a36Sopenharmony_ci if (ret) 71162306a36Sopenharmony_ci goto err_write; 71262306a36Sopenharmony_ci else 71362306a36Sopenharmony_ci *retlen += page_size; 71462306a36Sopenharmony_ci 71562306a36Sopenharmony_ci /* write everything in pagesize chunks */ 71662306a36Sopenharmony_ci for (i = page_size; i < len; i += page_size) { 71762306a36Sopenharmony_ci page_size = len - i; 71862306a36Sopenharmony_ci if (page_size > flash->page_size) 71962306a36Sopenharmony_ci page_size = flash->page_size; 72062306a36Sopenharmony_ci 72162306a36Sopenharmony_ci ret = spear_smi_cpy_toio(dev, flash->bank, dest + i, 72262306a36Sopenharmony_ci buf + i, page_size); 72362306a36Sopenharmony_ci if (ret) 72462306a36Sopenharmony_ci break; 72562306a36Sopenharmony_ci else 72662306a36Sopenharmony_ci *retlen += page_size; 72762306a36Sopenharmony_ci } 72862306a36Sopenharmony_ci } 72962306a36Sopenharmony_ci 73062306a36Sopenharmony_cierr_write: 73162306a36Sopenharmony_ci mutex_unlock(&flash->lock); 73262306a36Sopenharmony_ci 73362306a36Sopenharmony_ci return ret; 73462306a36Sopenharmony_ci} 73562306a36Sopenharmony_ci 73662306a36Sopenharmony_ci/** 73762306a36Sopenharmony_ci * spear_smi_probe_flash - Detects the NOR Flash chip. 73862306a36Sopenharmony_ci * @dev: structure of SMI information. 73962306a36Sopenharmony_ci * @bank: bank on which flash must be probed 74062306a36Sopenharmony_ci * 74162306a36Sopenharmony_ci * This routine will check whether there exists a flash chip on a given memory 74262306a36Sopenharmony_ci * bank ID. 74362306a36Sopenharmony_ci * Return index of the probed flash in flash devices structure 74462306a36Sopenharmony_ci */ 74562306a36Sopenharmony_cistatic int spear_smi_probe_flash(struct spear_smi *dev, u32 bank) 74662306a36Sopenharmony_ci{ 74762306a36Sopenharmony_ci int ret; 74862306a36Sopenharmony_ci u32 val = 0; 74962306a36Sopenharmony_ci 75062306a36Sopenharmony_ci ret = spear_smi_wait_till_ready(dev, bank, SMI_PROBE_TIMEOUT); 75162306a36Sopenharmony_ci if (ret) 75262306a36Sopenharmony_ci return ret; 75362306a36Sopenharmony_ci 75462306a36Sopenharmony_ci mutex_lock(&dev->lock); 75562306a36Sopenharmony_ci 75662306a36Sopenharmony_ci dev->status = 0; /* Will be set in interrupt handler */ 75762306a36Sopenharmony_ci /* put smi in sw mode */ 75862306a36Sopenharmony_ci val = readl(dev->io_base + SMI_CR1); 75962306a36Sopenharmony_ci writel(val | SW_MODE, dev->io_base + SMI_CR1); 76062306a36Sopenharmony_ci 76162306a36Sopenharmony_ci /* send readid command in sw mode */ 76262306a36Sopenharmony_ci writel(OPCODE_RDID, dev->io_base + SMI_TR); 76362306a36Sopenharmony_ci 76462306a36Sopenharmony_ci val = (bank << BANK_SHIFT) | SEND | (1 << TX_LEN_SHIFT) | 76562306a36Sopenharmony_ci (3 << RX_LEN_SHIFT) | TFIE; 76662306a36Sopenharmony_ci writel(val, dev->io_base + SMI_CR2); 76762306a36Sopenharmony_ci 76862306a36Sopenharmony_ci /* wait for TFF */ 76962306a36Sopenharmony_ci ret = wait_event_interruptible_timeout(dev->cmd_complete, 77062306a36Sopenharmony_ci dev->status & TFF, SMI_CMD_TIMEOUT); 77162306a36Sopenharmony_ci if (ret <= 0) { 77262306a36Sopenharmony_ci ret = -ENODEV; 77362306a36Sopenharmony_ci goto err_probe; 77462306a36Sopenharmony_ci } 77562306a36Sopenharmony_ci 77662306a36Sopenharmony_ci /* get memory chip id */ 77762306a36Sopenharmony_ci val = readl(dev->io_base + SMI_RR); 77862306a36Sopenharmony_ci val &= 0x00ffffff; 77962306a36Sopenharmony_ci ret = get_flash_index(val); 78062306a36Sopenharmony_ci 78162306a36Sopenharmony_cierr_probe: 78262306a36Sopenharmony_ci /* clear sw mode */ 78362306a36Sopenharmony_ci val = readl(dev->io_base + SMI_CR1); 78462306a36Sopenharmony_ci writel(val & ~SW_MODE, dev->io_base + SMI_CR1); 78562306a36Sopenharmony_ci 78662306a36Sopenharmony_ci mutex_unlock(&dev->lock); 78762306a36Sopenharmony_ci return ret; 78862306a36Sopenharmony_ci} 78962306a36Sopenharmony_ci 79062306a36Sopenharmony_ci 79162306a36Sopenharmony_ci#ifdef CONFIG_OF 79262306a36Sopenharmony_cistatic int spear_smi_probe_config_dt(struct platform_device *pdev, 79362306a36Sopenharmony_ci struct device_node *np) 79462306a36Sopenharmony_ci{ 79562306a36Sopenharmony_ci struct spear_smi_plat_data *pdata = dev_get_platdata(&pdev->dev); 79662306a36Sopenharmony_ci struct device_node *pp; 79762306a36Sopenharmony_ci const __be32 *addr; 79862306a36Sopenharmony_ci u32 val; 79962306a36Sopenharmony_ci int len; 80062306a36Sopenharmony_ci int i = 0; 80162306a36Sopenharmony_ci 80262306a36Sopenharmony_ci if (!np) 80362306a36Sopenharmony_ci return -ENODEV; 80462306a36Sopenharmony_ci 80562306a36Sopenharmony_ci of_property_read_u32(np, "clock-rate", &val); 80662306a36Sopenharmony_ci pdata->clk_rate = val; 80762306a36Sopenharmony_ci 80862306a36Sopenharmony_ci pdata->board_flash_info = devm_kzalloc(&pdev->dev, 80962306a36Sopenharmony_ci sizeof(*pdata->board_flash_info), 81062306a36Sopenharmony_ci GFP_KERNEL); 81162306a36Sopenharmony_ci if (!pdata->board_flash_info) 81262306a36Sopenharmony_ci return -ENOMEM; 81362306a36Sopenharmony_ci 81462306a36Sopenharmony_ci /* Fill structs for each subnode (flash device) */ 81562306a36Sopenharmony_ci for_each_child_of_node(np, pp) { 81662306a36Sopenharmony_ci pdata->np[i] = pp; 81762306a36Sopenharmony_ci 81862306a36Sopenharmony_ci /* Read base-addr and size from DT */ 81962306a36Sopenharmony_ci addr = of_get_property(pp, "reg", &len); 82062306a36Sopenharmony_ci pdata->board_flash_info->mem_base = be32_to_cpup(&addr[0]); 82162306a36Sopenharmony_ci pdata->board_flash_info->size = be32_to_cpup(&addr[1]); 82262306a36Sopenharmony_ci 82362306a36Sopenharmony_ci pdata->board_flash_info->fast_mode = 82462306a36Sopenharmony_ci of_property_read_bool(pp, "st,smi-fast-mode"); 82562306a36Sopenharmony_ci 82662306a36Sopenharmony_ci i++; 82762306a36Sopenharmony_ci } 82862306a36Sopenharmony_ci 82962306a36Sopenharmony_ci pdata->num_flashes = i; 83062306a36Sopenharmony_ci 83162306a36Sopenharmony_ci return 0; 83262306a36Sopenharmony_ci} 83362306a36Sopenharmony_ci#else 83462306a36Sopenharmony_cistatic int spear_smi_probe_config_dt(struct platform_device *pdev, 83562306a36Sopenharmony_ci struct device_node *np) 83662306a36Sopenharmony_ci{ 83762306a36Sopenharmony_ci return -ENOSYS; 83862306a36Sopenharmony_ci} 83962306a36Sopenharmony_ci#endif 84062306a36Sopenharmony_ci 84162306a36Sopenharmony_cistatic int spear_smi_setup_banks(struct platform_device *pdev, 84262306a36Sopenharmony_ci u32 bank, struct device_node *np) 84362306a36Sopenharmony_ci{ 84462306a36Sopenharmony_ci struct spear_smi *dev = platform_get_drvdata(pdev); 84562306a36Sopenharmony_ci struct spear_smi_flash_info *flash_info; 84662306a36Sopenharmony_ci struct spear_smi_plat_data *pdata; 84762306a36Sopenharmony_ci struct spear_snor_flash *flash; 84862306a36Sopenharmony_ci struct mtd_partition *parts = NULL; 84962306a36Sopenharmony_ci int count = 0; 85062306a36Sopenharmony_ci int flash_index; 85162306a36Sopenharmony_ci int ret = 0; 85262306a36Sopenharmony_ci 85362306a36Sopenharmony_ci pdata = dev_get_platdata(&pdev->dev); 85462306a36Sopenharmony_ci if (bank > pdata->num_flashes - 1) 85562306a36Sopenharmony_ci return -EINVAL; 85662306a36Sopenharmony_ci 85762306a36Sopenharmony_ci flash_info = &pdata->board_flash_info[bank]; 85862306a36Sopenharmony_ci if (!flash_info) 85962306a36Sopenharmony_ci return -ENODEV; 86062306a36Sopenharmony_ci 86162306a36Sopenharmony_ci flash = devm_kzalloc(&pdev->dev, sizeof(*flash), GFP_ATOMIC); 86262306a36Sopenharmony_ci if (!flash) 86362306a36Sopenharmony_ci return -ENOMEM; 86462306a36Sopenharmony_ci flash->bank = bank; 86562306a36Sopenharmony_ci flash->fast_mode = flash_info->fast_mode ? 1 : 0; 86662306a36Sopenharmony_ci mutex_init(&flash->lock); 86762306a36Sopenharmony_ci 86862306a36Sopenharmony_ci /* verify whether nor flash is really present on board */ 86962306a36Sopenharmony_ci flash_index = spear_smi_probe_flash(dev, bank); 87062306a36Sopenharmony_ci if (flash_index < 0) { 87162306a36Sopenharmony_ci dev_info(&dev->pdev->dev, "smi-nor%d not found\n", bank); 87262306a36Sopenharmony_ci return flash_index; 87362306a36Sopenharmony_ci } 87462306a36Sopenharmony_ci /* map the memory for nor flash chip */ 87562306a36Sopenharmony_ci flash->base_addr = devm_ioremap(&pdev->dev, flash_info->mem_base, 87662306a36Sopenharmony_ci flash_info->size); 87762306a36Sopenharmony_ci if (!flash->base_addr) 87862306a36Sopenharmony_ci return -EIO; 87962306a36Sopenharmony_ci 88062306a36Sopenharmony_ci dev->flash[bank] = flash; 88162306a36Sopenharmony_ci flash->mtd.priv = dev; 88262306a36Sopenharmony_ci 88362306a36Sopenharmony_ci if (flash_info->name) 88462306a36Sopenharmony_ci flash->mtd.name = flash_info->name; 88562306a36Sopenharmony_ci else 88662306a36Sopenharmony_ci flash->mtd.name = flash_devices[flash_index].name; 88762306a36Sopenharmony_ci 88862306a36Sopenharmony_ci flash->mtd.dev.parent = &pdev->dev; 88962306a36Sopenharmony_ci mtd_set_of_node(&flash->mtd, np); 89062306a36Sopenharmony_ci flash->mtd.type = MTD_NORFLASH; 89162306a36Sopenharmony_ci flash->mtd.writesize = 1; 89262306a36Sopenharmony_ci flash->mtd.flags = MTD_CAP_NORFLASH; 89362306a36Sopenharmony_ci flash->mtd.size = flash_info->size; 89462306a36Sopenharmony_ci flash->mtd.erasesize = flash_devices[flash_index].sectorsize; 89562306a36Sopenharmony_ci flash->page_size = flash_devices[flash_index].pagesize; 89662306a36Sopenharmony_ci flash->mtd.writebufsize = flash->page_size; 89762306a36Sopenharmony_ci flash->erase_cmd = flash_devices[flash_index].erase_cmd; 89862306a36Sopenharmony_ci flash->mtd._erase = spear_mtd_erase; 89962306a36Sopenharmony_ci flash->mtd._read = spear_mtd_read; 90062306a36Sopenharmony_ci flash->mtd._write = spear_mtd_write; 90162306a36Sopenharmony_ci flash->dev_id = flash_devices[flash_index].device_id; 90262306a36Sopenharmony_ci 90362306a36Sopenharmony_ci dev_info(&dev->pdev->dev, "mtd .name=%s .size=%llx(%lluM)\n", 90462306a36Sopenharmony_ci flash->mtd.name, flash->mtd.size, 90562306a36Sopenharmony_ci flash->mtd.size / (1024 * 1024)); 90662306a36Sopenharmony_ci 90762306a36Sopenharmony_ci dev_info(&dev->pdev->dev, ".erasesize = 0x%x(%uK)\n", 90862306a36Sopenharmony_ci flash->mtd.erasesize, flash->mtd.erasesize / 1024); 90962306a36Sopenharmony_ci 91062306a36Sopenharmony_ci#ifndef CONFIG_OF 91162306a36Sopenharmony_ci if (flash_info->partitions) { 91262306a36Sopenharmony_ci parts = flash_info->partitions; 91362306a36Sopenharmony_ci count = flash_info->nr_partitions; 91462306a36Sopenharmony_ci } 91562306a36Sopenharmony_ci#endif 91662306a36Sopenharmony_ci 91762306a36Sopenharmony_ci ret = mtd_device_register(&flash->mtd, parts, count); 91862306a36Sopenharmony_ci if (ret) { 91962306a36Sopenharmony_ci dev_err(&dev->pdev->dev, "Err MTD partition=%d\n", ret); 92062306a36Sopenharmony_ci return ret; 92162306a36Sopenharmony_ci } 92262306a36Sopenharmony_ci 92362306a36Sopenharmony_ci return 0; 92462306a36Sopenharmony_ci} 92562306a36Sopenharmony_ci 92662306a36Sopenharmony_ci/** 92762306a36Sopenharmony_ci * spear_smi_probe - Entry routine 92862306a36Sopenharmony_ci * @pdev: platform device structure 92962306a36Sopenharmony_ci * 93062306a36Sopenharmony_ci * This is the first routine which gets invoked during booting and does all 93162306a36Sopenharmony_ci * initialization/allocation work. The routine looks for available memory banks, 93262306a36Sopenharmony_ci * and do proper init for any found one. 93362306a36Sopenharmony_ci * Returns 0 on success, non zero otherwise 93462306a36Sopenharmony_ci */ 93562306a36Sopenharmony_cistatic int spear_smi_probe(struct platform_device *pdev) 93662306a36Sopenharmony_ci{ 93762306a36Sopenharmony_ci struct device_node *np = pdev->dev.of_node; 93862306a36Sopenharmony_ci struct spear_smi_plat_data *pdata = NULL; 93962306a36Sopenharmony_ci struct spear_smi *dev; 94062306a36Sopenharmony_ci int irq, ret = 0; 94162306a36Sopenharmony_ci int i; 94262306a36Sopenharmony_ci 94362306a36Sopenharmony_ci if (np) { 94462306a36Sopenharmony_ci pdata = devm_kzalloc(&pdev->dev, sizeof(*pdata), GFP_KERNEL); 94562306a36Sopenharmony_ci if (!pdata) { 94662306a36Sopenharmony_ci ret = -ENOMEM; 94762306a36Sopenharmony_ci goto err; 94862306a36Sopenharmony_ci } 94962306a36Sopenharmony_ci pdev->dev.platform_data = pdata; 95062306a36Sopenharmony_ci ret = spear_smi_probe_config_dt(pdev, np); 95162306a36Sopenharmony_ci if (ret) { 95262306a36Sopenharmony_ci ret = -ENODEV; 95362306a36Sopenharmony_ci dev_err(&pdev->dev, "no platform data\n"); 95462306a36Sopenharmony_ci goto err; 95562306a36Sopenharmony_ci } 95662306a36Sopenharmony_ci } else { 95762306a36Sopenharmony_ci pdata = dev_get_platdata(&pdev->dev); 95862306a36Sopenharmony_ci if (!pdata) { 95962306a36Sopenharmony_ci ret = -ENODEV; 96062306a36Sopenharmony_ci dev_err(&pdev->dev, "no platform data\n"); 96162306a36Sopenharmony_ci goto err; 96262306a36Sopenharmony_ci } 96362306a36Sopenharmony_ci } 96462306a36Sopenharmony_ci 96562306a36Sopenharmony_ci irq = platform_get_irq(pdev, 0); 96662306a36Sopenharmony_ci if (irq < 0) { 96762306a36Sopenharmony_ci ret = -ENODEV; 96862306a36Sopenharmony_ci goto err; 96962306a36Sopenharmony_ci } 97062306a36Sopenharmony_ci 97162306a36Sopenharmony_ci dev = devm_kzalloc(&pdev->dev, sizeof(*dev), GFP_KERNEL); 97262306a36Sopenharmony_ci if (!dev) { 97362306a36Sopenharmony_ci ret = -ENOMEM; 97462306a36Sopenharmony_ci goto err; 97562306a36Sopenharmony_ci } 97662306a36Sopenharmony_ci 97762306a36Sopenharmony_ci dev->io_base = devm_platform_ioremap_resource(pdev, 0); 97862306a36Sopenharmony_ci if (IS_ERR(dev->io_base)) { 97962306a36Sopenharmony_ci ret = PTR_ERR(dev->io_base); 98062306a36Sopenharmony_ci goto err; 98162306a36Sopenharmony_ci } 98262306a36Sopenharmony_ci 98362306a36Sopenharmony_ci dev->pdev = pdev; 98462306a36Sopenharmony_ci dev->clk_rate = pdata->clk_rate; 98562306a36Sopenharmony_ci 98662306a36Sopenharmony_ci if (dev->clk_rate > SMI_MAX_CLOCK_FREQ) 98762306a36Sopenharmony_ci dev->clk_rate = SMI_MAX_CLOCK_FREQ; 98862306a36Sopenharmony_ci 98962306a36Sopenharmony_ci dev->num_flashes = pdata->num_flashes; 99062306a36Sopenharmony_ci 99162306a36Sopenharmony_ci if (dev->num_flashes > MAX_NUM_FLASH_CHIP) { 99262306a36Sopenharmony_ci dev_err(&pdev->dev, "exceeding max number of flashes\n"); 99362306a36Sopenharmony_ci dev->num_flashes = MAX_NUM_FLASH_CHIP; 99462306a36Sopenharmony_ci } 99562306a36Sopenharmony_ci 99662306a36Sopenharmony_ci dev->clk = devm_clk_get_enabled(&pdev->dev, NULL); 99762306a36Sopenharmony_ci if (IS_ERR(dev->clk)) { 99862306a36Sopenharmony_ci ret = PTR_ERR(dev->clk); 99962306a36Sopenharmony_ci goto err; 100062306a36Sopenharmony_ci } 100162306a36Sopenharmony_ci 100262306a36Sopenharmony_ci ret = devm_request_irq(&pdev->dev, irq, spear_smi_int_handler, 0, 100362306a36Sopenharmony_ci pdev->name, dev); 100462306a36Sopenharmony_ci if (ret) { 100562306a36Sopenharmony_ci dev_err(&dev->pdev->dev, "SMI IRQ allocation failed\n"); 100662306a36Sopenharmony_ci goto err; 100762306a36Sopenharmony_ci } 100862306a36Sopenharmony_ci 100962306a36Sopenharmony_ci mutex_init(&dev->lock); 101062306a36Sopenharmony_ci init_waitqueue_head(&dev->cmd_complete); 101162306a36Sopenharmony_ci spear_smi_hw_init(dev); 101262306a36Sopenharmony_ci platform_set_drvdata(pdev, dev); 101362306a36Sopenharmony_ci 101462306a36Sopenharmony_ci /* loop for each serial nor-flash which is connected to smi */ 101562306a36Sopenharmony_ci for (i = 0; i < dev->num_flashes; i++) { 101662306a36Sopenharmony_ci ret = spear_smi_setup_banks(pdev, i, pdata->np[i]); 101762306a36Sopenharmony_ci if (ret) { 101862306a36Sopenharmony_ci dev_err(&dev->pdev->dev, "bank setup failed\n"); 101962306a36Sopenharmony_ci goto err; 102062306a36Sopenharmony_ci } 102162306a36Sopenharmony_ci } 102262306a36Sopenharmony_ci 102362306a36Sopenharmony_ci return 0; 102462306a36Sopenharmony_cierr: 102562306a36Sopenharmony_ci return ret; 102662306a36Sopenharmony_ci} 102762306a36Sopenharmony_ci 102862306a36Sopenharmony_ci/** 102962306a36Sopenharmony_ci * spear_smi_remove - Exit routine 103062306a36Sopenharmony_ci * @pdev: platform device structure 103162306a36Sopenharmony_ci * 103262306a36Sopenharmony_ci * free all allocations and delete the partitions. 103362306a36Sopenharmony_ci */ 103462306a36Sopenharmony_cistatic int spear_smi_remove(struct platform_device *pdev) 103562306a36Sopenharmony_ci{ 103662306a36Sopenharmony_ci struct spear_smi *dev; 103762306a36Sopenharmony_ci struct spear_snor_flash *flash; 103862306a36Sopenharmony_ci int i; 103962306a36Sopenharmony_ci 104062306a36Sopenharmony_ci dev = platform_get_drvdata(pdev); 104162306a36Sopenharmony_ci 104262306a36Sopenharmony_ci /* clean up for all nor flash */ 104362306a36Sopenharmony_ci for (i = 0; i < dev->num_flashes; i++) { 104462306a36Sopenharmony_ci flash = dev->flash[i]; 104562306a36Sopenharmony_ci if (!flash) 104662306a36Sopenharmony_ci continue; 104762306a36Sopenharmony_ci 104862306a36Sopenharmony_ci /* clean up mtd stuff */ 104962306a36Sopenharmony_ci WARN_ON(mtd_device_unregister(&flash->mtd)); 105062306a36Sopenharmony_ci } 105162306a36Sopenharmony_ci 105262306a36Sopenharmony_ci return 0; 105362306a36Sopenharmony_ci} 105462306a36Sopenharmony_ci 105562306a36Sopenharmony_ci#ifdef CONFIG_PM_SLEEP 105662306a36Sopenharmony_cistatic int spear_smi_suspend(struct device *dev) 105762306a36Sopenharmony_ci{ 105862306a36Sopenharmony_ci struct spear_smi *sdev = dev_get_drvdata(dev); 105962306a36Sopenharmony_ci 106062306a36Sopenharmony_ci if (sdev && sdev->clk) 106162306a36Sopenharmony_ci clk_disable_unprepare(sdev->clk); 106262306a36Sopenharmony_ci 106362306a36Sopenharmony_ci return 0; 106462306a36Sopenharmony_ci} 106562306a36Sopenharmony_ci 106662306a36Sopenharmony_cistatic int spear_smi_resume(struct device *dev) 106762306a36Sopenharmony_ci{ 106862306a36Sopenharmony_ci struct spear_smi *sdev = dev_get_drvdata(dev); 106962306a36Sopenharmony_ci int ret = -EPERM; 107062306a36Sopenharmony_ci 107162306a36Sopenharmony_ci if (sdev && sdev->clk) 107262306a36Sopenharmony_ci ret = clk_prepare_enable(sdev->clk); 107362306a36Sopenharmony_ci 107462306a36Sopenharmony_ci if (!ret) 107562306a36Sopenharmony_ci spear_smi_hw_init(sdev); 107662306a36Sopenharmony_ci return ret; 107762306a36Sopenharmony_ci} 107862306a36Sopenharmony_ci#endif 107962306a36Sopenharmony_ci 108062306a36Sopenharmony_cistatic SIMPLE_DEV_PM_OPS(spear_smi_pm_ops, spear_smi_suspend, spear_smi_resume); 108162306a36Sopenharmony_ci 108262306a36Sopenharmony_ci#ifdef CONFIG_OF 108362306a36Sopenharmony_cistatic const struct of_device_id spear_smi_id_table[] = { 108462306a36Sopenharmony_ci { .compatible = "st,spear600-smi" }, 108562306a36Sopenharmony_ci {} 108662306a36Sopenharmony_ci}; 108762306a36Sopenharmony_ciMODULE_DEVICE_TABLE(of, spear_smi_id_table); 108862306a36Sopenharmony_ci#endif 108962306a36Sopenharmony_ci 109062306a36Sopenharmony_cistatic struct platform_driver spear_smi_driver = { 109162306a36Sopenharmony_ci .driver = { 109262306a36Sopenharmony_ci .name = "smi", 109362306a36Sopenharmony_ci .bus = &platform_bus_type, 109462306a36Sopenharmony_ci .of_match_table = of_match_ptr(spear_smi_id_table), 109562306a36Sopenharmony_ci .pm = &spear_smi_pm_ops, 109662306a36Sopenharmony_ci }, 109762306a36Sopenharmony_ci .probe = spear_smi_probe, 109862306a36Sopenharmony_ci .remove = spear_smi_remove, 109962306a36Sopenharmony_ci}; 110062306a36Sopenharmony_cimodule_platform_driver(spear_smi_driver); 110162306a36Sopenharmony_ci 110262306a36Sopenharmony_ciMODULE_LICENSE("GPL"); 110362306a36Sopenharmony_ciMODULE_AUTHOR("Ashish Priyadarshi, Shiraz Hashim <shiraz.linux.kernel@gmail.com>"); 110462306a36Sopenharmony_ciMODULE_DESCRIPTION("MTD SMI driver for serial nor flash chips"); 1105