12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127 |
- /*
- * SMI (Serial Memory Controller) device driver for Serial NOR Flash on
- * SPEAr platform
- * The serial nor interface is largely based on m25p80.c, however the SPI
- * interface has been replaced by SMI.
- *
- * Copyright © 2010 STMicroelectronics.
- * Ashish Priyadarshi
- * Shiraz Hashim <shiraz.linux.kernel@gmail.com>
- *
- * This file is licensed under the terms of the GNU General Public
- * License version 2. This program is licensed "as is" without any
- * warranty of any kind, whether express or implied.
- */
- #include <linux/clk.h>
- #include <linux/delay.h>
- #include <linux/device.h>
- #include <linux/err.h>
- #include <linux/errno.h>
- #include <linux/interrupt.h>
- #include <linux/io.h>
- #include <linux/ioport.h>
- #include <linux/jiffies.h>
- #include <linux/kernel.h>
- #include <linux/module.h>
- #include <linux/param.h>
- #include <linux/platform_device.h>
- #include <linux/pm.h>
- #include <linux/mtd/mtd.h>
- #include <linux/mtd/partitions.h>
- #include <linux/mtd/spear_smi.h>
- #include <linux/mutex.h>
- #include <linux/sched.h>
- #include <linux/slab.h>
- #include <linux/wait.h>
- #include <linux/of.h>
- #include <linux/of_address.h>
- /* SMI clock rate */
- #define SMI_MAX_CLOCK_FREQ 50000000 /* 50 MHz */
- /* MAX time out to safely come out of a erase or write busy conditions */
- #define SMI_PROBE_TIMEOUT (HZ / 10)
- #define SMI_MAX_TIME_OUT (3 * HZ)
- /* timeout for command completion */
- #define SMI_CMD_TIMEOUT (HZ / 10)
- /* registers of smi */
- #define SMI_CR1 0x0 /* SMI control register 1 */
- #define SMI_CR2 0x4 /* SMI control register 2 */
- #define SMI_SR 0x8 /* SMI status register */
- #define SMI_TR 0xC /* SMI transmit register */
- #define SMI_RR 0x10 /* SMI receive register */
- /* defines for control_reg 1 */
- #define BANK_EN (0xF << 0) /* enables all banks */
- #define DSEL_TIME (0x6 << 4) /* Deselect time 6 + 1 SMI_CK periods */
- #define SW_MODE (0x1 << 28) /* enables SW Mode */
- #define WB_MODE (0x1 << 29) /* Write Burst Mode */
- #define FAST_MODE (0x1 << 15) /* Fast Mode */
- #define HOLD1 (0x1 << 16) /* Clock Hold period selection */
- /* defines for control_reg 2 */
- #define SEND (0x1 << 7) /* Send data */
- #define TFIE (0x1 << 8) /* Transmission Flag Interrupt Enable */
- #define WCIE (0x1 << 9) /* Write Complete Interrupt Enable */
- #define RD_STATUS_REG (0x1 << 10) /* reads status reg */
- #define WE (0x1 << 11) /* Write Enable */
- #define TX_LEN_SHIFT 0
- #define RX_LEN_SHIFT 4
- #define BANK_SHIFT 12
- /* defines for status register */
- #define SR_WIP 0x1 /* Write in progress */
- #define SR_WEL 0x2 /* Write enable latch */
- #define SR_BP0 0x4 /* Block protect 0 */
- #define SR_BP1 0x8 /* Block protect 1 */
- #define SR_BP2 0x10 /* Block protect 2 */
- #define SR_SRWD 0x80 /* SR write protect */
- #define TFF 0x100 /* Transfer Finished Flag */
- #define WCF 0x200 /* Transfer Finished Flag */
- #define ERF1 0x400 /* Forbidden Write Request */
- #define ERF2 0x800 /* Forbidden Access */
- #define WM_SHIFT 12
- /* flash opcodes */
- #define OPCODE_RDID 0x9f /* Read JEDEC ID */
- /* Flash Device Ids maintenance section */
- /* data structure to maintain flash ids from different vendors */
- struct flash_device {
- char *name;
- u8 erase_cmd;
- u32 device_id;
- u32 pagesize;
- unsigned long sectorsize;
- unsigned long size_in_bytes;
- };
- #define FLASH_ID(n, es, id, psize, ssize, size) \
- { \
- .name = n, \
- .erase_cmd = es, \
- .device_id = id, \
- .pagesize = psize, \
- .sectorsize = ssize, \
- .size_in_bytes = size \
- }
- static struct flash_device flash_devices[] = {
- FLASH_ID("st m25p16" , 0xd8, 0x00152020, 0x100, 0x10000, 0x200000),
- FLASH_ID("st m25p32" , 0xd8, 0x00162020, 0x100, 0x10000, 0x400000),
- FLASH_ID("st m25p64" , 0xd8, 0x00172020, 0x100, 0x10000, 0x800000),
- FLASH_ID("st m25p128" , 0xd8, 0x00182020, 0x100, 0x40000, 0x1000000),
- FLASH_ID("st m25p05" , 0xd8, 0x00102020, 0x80 , 0x8000 , 0x10000),
- FLASH_ID("st m25p10" , 0xd8, 0x00112020, 0x80 , 0x8000 , 0x20000),
- FLASH_ID("st m25p20" , 0xd8, 0x00122020, 0x100, 0x10000, 0x40000),
- FLASH_ID("st m25p40" , 0xd8, 0x00132020, 0x100, 0x10000, 0x80000),
- FLASH_ID("st m25p80" , 0xd8, 0x00142020, 0x100, 0x10000, 0x100000),
- FLASH_ID("st m45pe10" , 0xd8, 0x00114020, 0x100, 0x10000, 0x20000),
- FLASH_ID("st m45pe20" , 0xd8, 0x00124020, 0x100, 0x10000, 0x40000),
- FLASH_ID("st m45pe40" , 0xd8, 0x00134020, 0x100, 0x10000, 0x80000),
- FLASH_ID("st m45pe80" , 0xd8, 0x00144020, 0x100, 0x10000, 0x100000),
- FLASH_ID("sp s25fl004" , 0xd8, 0x00120201, 0x100, 0x10000, 0x80000),
- FLASH_ID("sp s25fl008" , 0xd8, 0x00130201, 0x100, 0x10000, 0x100000),
- FLASH_ID("sp s25fl016" , 0xd8, 0x00140201, 0x100, 0x10000, 0x200000),
- FLASH_ID("sp s25fl032" , 0xd8, 0x00150201, 0x100, 0x10000, 0x400000),
- FLASH_ID("sp s25fl064" , 0xd8, 0x00160201, 0x100, 0x10000, 0x800000),
- FLASH_ID("atmel 25f512" , 0x52, 0x0065001F, 0x80 , 0x8000 , 0x10000),
- FLASH_ID("atmel 25f1024" , 0x52, 0x0060001F, 0x100, 0x8000 , 0x20000),
- FLASH_ID("atmel 25f2048" , 0x52, 0x0063001F, 0x100, 0x10000, 0x40000),
- FLASH_ID("atmel 25f4096" , 0x52, 0x0064001F, 0x100, 0x10000, 0x80000),
- FLASH_ID("atmel 25fs040" , 0xd7, 0x0004661F, 0x100, 0x10000, 0x80000),
- FLASH_ID("mac 25l512" , 0xd8, 0x001020C2, 0x010, 0x10000, 0x10000),
- FLASH_ID("mac 25l1005" , 0xd8, 0x001120C2, 0x010, 0x10000, 0x20000),
- FLASH_ID("mac 25l2005" , 0xd8, 0x001220C2, 0x010, 0x10000, 0x40000),
- FLASH_ID("mac 25l4005" , 0xd8, 0x001320C2, 0x010, 0x10000, 0x80000),
- FLASH_ID("mac 25l4005a" , 0xd8, 0x001320C2, 0x010, 0x10000, 0x80000),
- FLASH_ID("mac 25l8005" , 0xd8, 0x001420C2, 0x010, 0x10000, 0x100000),
- FLASH_ID("mac 25l1605" , 0xd8, 0x001520C2, 0x100, 0x10000, 0x200000),
- FLASH_ID("mac 25l1605a" , 0xd8, 0x001520C2, 0x010, 0x10000, 0x200000),
- FLASH_ID("mac 25l3205" , 0xd8, 0x001620C2, 0x100, 0x10000, 0x400000),
- FLASH_ID("mac 25l3205a" , 0xd8, 0x001620C2, 0x100, 0x10000, 0x400000),
- FLASH_ID("mac 25l6405" , 0xd8, 0x001720C2, 0x100, 0x10000, 0x800000),
- };
- /* Define spear specific structures */
- struct spear_snor_flash;
- /**
- * struct spear_smi - Structure for SMI Device
- *
- * @clk: functional clock
- * @status: current status register of SMI.
- * @clk_rate: functional clock rate of SMI (default: SMI_MAX_CLOCK_FREQ)
- * @lock: lock to prevent parallel access of SMI.
- * @io_base: base address for registers of SMI.
- * @pdev: platform device
- * @cmd_complete: queue to wait for command completion of NOR-flash.
- * @num_flashes: number of flashes actually present on board.
- * @flash: separate structure for each Serial NOR-flash attached to SMI.
- */
- struct spear_smi {
- struct clk *clk;
- u32 status;
- unsigned long clk_rate;
- struct mutex lock;
- void __iomem *io_base;
- struct platform_device *pdev;
- wait_queue_head_t cmd_complete;
- u32 num_flashes;
- struct spear_snor_flash *flash[MAX_NUM_FLASH_CHIP];
- };
- /**
- * struct spear_snor_flash - Structure for Serial NOR Flash
- *
- * @bank: Bank number(0, 1, 2, 3) for each NOR-flash.
- * @dev_id: Device ID of NOR-flash.
- * @lock: lock to manage flash read, write and erase operations
- * @mtd: MTD info for each NOR-flash.
- * @num_parts: Total number of partition in each bank of NOR-flash.
- * @parts: Partition info for each bank of NOR-flash.
- * @page_size: Page size of NOR-flash.
- * @base_addr: Base address of NOR-flash.
- * @erase_cmd: erase command may vary on different flash types
- * @fast_mode: flash supports read in fast mode
- */
- struct spear_snor_flash {
- u32 bank;
- u32 dev_id;
- struct mutex lock;
- struct mtd_info mtd;
- u32 num_parts;
- struct mtd_partition *parts;
- u32 page_size;
- void __iomem *base_addr;
- u8 erase_cmd;
- u8 fast_mode;
- };
- static inline struct spear_snor_flash *get_flash_data(struct mtd_info *mtd)
- {
- return container_of(mtd, struct spear_snor_flash, mtd);
- }
- /**
- * spear_smi_read_sr - Read status register of flash through SMI
- * @dev: structure of SMI information.
- * @bank: bank to which flash is connected
- *
- * This routine will return the status register of the flash chip present at the
- * given bank.
- */
- static int spear_smi_read_sr(struct spear_smi *dev, u32 bank)
- {
- int ret;
- u32 ctrlreg1;
- mutex_lock(&dev->lock);
- dev->status = 0; /* Will be set in interrupt handler */
- ctrlreg1 = readl(dev->io_base + SMI_CR1);
- /* program smi in hw mode */
- writel(ctrlreg1 & ~(SW_MODE | WB_MODE), dev->io_base + SMI_CR1);
- /* performing a rsr instruction in hw mode */
- writel((bank << BANK_SHIFT) | RD_STATUS_REG | TFIE,
- dev->io_base + SMI_CR2);
- /* wait for tff */
- ret = wait_event_interruptible_timeout(dev->cmd_complete,
- dev->status & TFF, SMI_CMD_TIMEOUT);
- /* copy dev->status (lower 16 bits) in order to release lock */
- if (ret > 0)
- ret = dev->status & 0xffff;
- else if (ret == 0)
- ret = -ETIMEDOUT;
- /* restore the ctrl regs state */
- writel(ctrlreg1, dev->io_base + SMI_CR1);
- writel(0, dev->io_base + SMI_CR2);
- mutex_unlock(&dev->lock);
- return ret;
- }
- /**
- * spear_smi_wait_till_ready - wait till flash is ready
- * @dev: structure of SMI information.
- * @bank: flash corresponding to this bank
- * @timeout: timeout for busy wait condition
- *
- * This routine checks for WIP (write in progress) bit in Status register
- * If successful the routine returns 0 else -EBUSY
- */
- static int spear_smi_wait_till_ready(struct spear_smi *dev, u32 bank,
- unsigned long timeout)
- {
- unsigned long finish;
- int status;
- finish = jiffies + timeout;
- do {
- status = spear_smi_read_sr(dev, bank);
- if (status < 0) {
- if (status == -ETIMEDOUT)
- continue; /* try till finish */
- return status;
- } else if (!(status & SR_WIP)) {
- return 0;
- }
- cond_resched();
- } while (!time_after_eq(jiffies, finish));
- dev_err(&dev->pdev->dev, "smi controller is busy, timeout\n");
- return -EBUSY;
- }
- /**
- * spear_smi_int_handler - SMI Interrupt Handler.
- * @irq: irq number
- * @dev_id: structure of SMI device, embedded in dev_id.
- *
- * The handler clears all interrupt conditions and records the status in
- * dev->status which is used by the driver later.
- */
- static irqreturn_t spear_smi_int_handler(int irq, void *dev_id)
- {
- u32 status = 0;
- struct spear_smi *dev = dev_id;
- status = readl(dev->io_base + SMI_SR);
- if (unlikely(!status))
- return IRQ_NONE;
- /* clear all interrupt conditions */
- writel(0, dev->io_base + SMI_SR);
- /* copy the status register in dev->status */
- dev->status |= status;
- /* send the completion */
- wake_up_interruptible(&dev->cmd_complete);
- return IRQ_HANDLED;
- }
- /**
- * spear_smi_hw_init - initializes the smi controller.
- * @dev: structure of smi device
- *
- * this routine initializes the smi controller wit the default values
- */
- static void spear_smi_hw_init(struct spear_smi *dev)
- {
- unsigned long rate = 0;
- u32 prescale = 0;
- u32 val;
- rate = clk_get_rate(dev->clk);
- /* functional clock of smi */
- prescale = DIV_ROUND_UP(rate, dev->clk_rate);
- /*
- * setting the standard values, fast mode, prescaler for
- * SMI_MAX_CLOCK_FREQ (50MHz) operation and bank enable
- */
- val = HOLD1 | BANK_EN | DSEL_TIME | (prescale << 8);
- mutex_lock(&dev->lock);
- /* clear all interrupt conditions */
- writel(0, dev->io_base + SMI_SR);
- writel(val, dev->io_base + SMI_CR1);
- mutex_unlock(&dev->lock);
- }
- /**
- * get_flash_index - match chip id from a flash list.
- * @flash_id: a valid nor flash chip id obtained from board.
- *
- * try to validate the chip id by matching from a list, if not found then simply
- * returns negative. In case of success returns index in to the flash devices
- * array.
- */
- static int get_flash_index(u32 flash_id)
- {
- int index;
- /* Matches chip-id to entire list of 'serial-nor flash' ids */
- for (index = 0; index < ARRAY_SIZE(flash_devices); index++) {
- if (flash_devices[index].device_id == flash_id)
- return index;
- }
- /* Memory chip is not listed and not supported */
- return -ENODEV;
- }
- /**
- * spear_smi_write_enable - Enable the flash to do write operation
- * @dev: structure of SMI device
- * @bank: enable write for flash connected to this bank
- *
- * Set write enable latch with Write Enable command.
- * Returns 0 on success.
- */
- static int spear_smi_write_enable(struct spear_smi *dev, u32 bank)
- {
- int ret;
- u32 ctrlreg1;
- mutex_lock(&dev->lock);
- dev->status = 0; /* Will be set in interrupt handler */
- ctrlreg1 = readl(dev->io_base + SMI_CR1);
- /* program smi in h/w mode */
- writel(ctrlreg1 & ~SW_MODE, dev->io_base + SMI_CR1);
- /* give the flash, write enable command */
- writel((bank << BANK_SHIFT) | WE | TFIE, dev->io_base + SMI_CR2);
- ret = wait_event_interruptible_timeout(dev->cmd_complete,
- dev->status & TFF, SMI_CMD_TIMEOUT);
- /* restore the ctrl regs state */
- writel(ctrlreg1, dev->io_base + SMI_CR1);
- writel(0, dev->io_base + SMI_CR2);
- if (ret == 0) {
- ret = -EIO;
- dev_err(&dev->pdev->dev,
- "smi controller failed on write enable\n");
- } else if (ret > 0) {
- /* check whether write mode status is set for required bank */
- if (dev->status & (1 << (bank + WM_SHIFT)))
- ret = 0;
- else {
- dev_err(&dev->pdev->dev, "couldn't enable write\n");
- ret = -EIO;
- }
- }
- mutex_unlock(&dev->lock);
- return ret;
- }
- static inline u32
- get_sector_erase_cmd(struct spear_snor_flash *flash, u32 offset)
- {
- u32 cmd;
- u8 *x = (u8 *)&cmd;
- x[0] = flash->erase_cmd;
- x[1] = offset >> 16;
- x[2] = offset >> 8;
- x[3] = offset;
- return cmd;
- }
- /**
- * spear_smi_erase_sector - erase one sector of flash
- * @dev: structure of SMI information
- * @command: erase command to be send
- * @bank: bank to which this command needs to be send
- * @bytes: size of command
- *
- * Erase one sector of flash memory at offset ``offset'' which is any
- * address within the sector which should be erased.
- * Returns 0 if successful, non-zero otherwise.
- */
- static int spear_smi_erase_sector(struct spear_smi *dev,
- u32 bank, u32 command, u32 bytes)
- {
- u32 ctrlreg1 = 0;
- int ret;
- ret = spear_smi_wait_till_ready(dev, bank, SMI_MAX_TIME_OUT);
- if (ret)
- return ret;
- ret = spear_smi_write_enable(dev, bank);
- if (ret)
- return ret;
- mutex_lock(&dev->lock);
- ctrlreg1 = readl(dev->io_base + SMI_CR1);
- writel((ctrlreg1 | SW_MODE) & ~WB_MODE, dev->io_base + SMI_CR1);
- /* send command in sw mode */
- writel(command, dev->io_base + SMI_TR);
- writel((bank << BANK_SHIFT) | SEND | TFIE | (bytes << TX_LEN_SHIFT),
- dev->io_base + SMI_CR2);
- ret = wait_event_interruptible_timeout(dev->cmd_complete,
- dev->status & TFF, SMI_CMD_TIMEOUT);
- if (ret == 0) {
- ret = -EIO;
- dev_err(&dev->pdev->dev, "sector erase failed\n");
- } else if (ret > 0)
- ret = 0; /* success */
- /* restore ctrl regs */
- writel(ctrlreg1, dev->io_base + SMI_CR1);
- writel(0, dev->io_base + SMI_CR2);
- mutex_unlock(&dev->lock);
- return ret;
- }
- /**
- * spear_mtd_erase - perform flash erase operation as requested by user
- * @mtd: Provides the memory characteristics
- * @e_info: Provides the erase information
- *
- * Erase an address range on the flash chip. The address range may extend
- * one or more erase sectors. Return an error is there is a problem erasing.
- */
- static int spear_mtd_erase(struct mtd_info *mtd, struct erase_info *e_info)
- {
- struct spear_snor_flash *flash = get_flash_data(mtd);
- struct spear_smi *dev = mtd->priv;
- u32 addr, command, bank;
- int len, ret;
- if (!flash || !dev)
- return -ENODEV;
- bank = flash->bank;
- if (bank > dev->num_flashes - 1) {
- dev_err(&dev->pdev->dev, "Invalid Bank Num");
- return -EINVAL;
- }
- addr = e_info->addr;
- len = e_info->len;
- mutex_lock(&flash->lock);
- /* now erase sectors in loop */
- while (len) {
- command = get_sector_erase_cmd(flash, addr);
- /* preparing the command for flash */
- ret = spear_smi_erase_sector(dev, bank, command, 4);
- if (ret) {
- mutex_unlock(&flash->lock);
- return ret;
- }
- addr += mtd->erasesize;
- len -= mtd->erasesize;
- }
- mutex_unlock(&flash->lock);
- return 0;
- }
- /**
- * spear_mtd_read - performs flash read operation as requested by the user
- * @mtd: MTD information of the memory bank
- * @from: Address from which to start read
- * @len: Number of bytes to be read
- * @retlen: Fills the Number of bytes actually read
- * @buf: Fills this after reading
- *
- * Read an address range from the flash chip. The address range
- * may be any size provided it is within the physical boundaries.
- * Returns 0 on success, non zero otherwise
- */
- static int spear_mtd_read(struct mtd_info *mtd, loff_t from, size_t len,
- size_t *retlen, u8 *buf)
- {
- struct spear_snor_flash *flash = get_flash_data(mtd);
- struct spear_smi *dev = mtd->priv;
- void __iomem *src;
- u32 ctrlreg1, val;
- int ret;
- if (!flash || !dev)
- return -ENODEV;
- if (flash->bank > dev->num_flashes - 1) {
- dev_err(&dev->pdev->dev, "Invalid Bank Num");
- return -EINVAL;
- }
- /* select address as per bank number */
- src = flash->base_addr + from;
- mutex_lock(&flash->lock);
- /* wait till previous write/erase is done. */
- ret = spear_smi_wait_till_ready(dev, flash->bank, SMI_MAX_TIME_OUT);
- if (ret) {
- mutex_unlock(&flash->lock);
- return ret;
- }
- mutex_lock(&dev->lock);
- /* put smi in hw mode not wbt mode */
- ctrlreg1 = val = readl(dev->io_base + SMI_CR1);
- val &= ~(SW_MODE | WB_MODE);
- if (flash->fast_mode)
- val |= FAST_MODE;
- writel(val, dev->io_base + SMI_CR1);
- memcpy_fromio(buf, src, len);
- /* restore ctrl reg1 */
- writel(ctrlreg1, dev->io_base + SMI_CR1);
- mutex_unlock(&dev->lock);
- *retlen = len;
- mutex_unlock(&flash->lock);
- return 0;
- }
- /*
- * The purpose of this function is to ensure a memcpy_toio() with byte writes
- * only. Its structure is inspired from the ARM implementation of _memcpy_toio()
- * which also does single byte writes but cannot be used here as this is just an
- * implementation detail and not part of the API. Not mentioning the comment
- * stating that _memcpy_toio() should be optimized.
- */
- static void spear_smi_memcpy_toio_b(volatile void __iomem *dest,
- const void *src, size_t len)
- {
- const unsigned char *from = src;
- while (len) {
- len--;
- writeb(*from, dest);
- from++;
- dest++;
- }
- }
- static inline int spear_smi_cpy_toio(struct spear_smi *dev, u32 bank,
- void __iomem *dest, const void *src, size_t len)
- {
- int ret;
- u32 ctrlreg1;
- /* wait until finished previous write command. */
- ret = spear_smi_wait_till_ready(dev, bank, SMI_MAX_TIME_OUT);
- if (ret)
- return ret;
- /* put smi in write enable */
- ret = spear_smi_write_enable(dev, bank);
- if (ret)
- return ret;
- /* put smi in hw, write burst mode */
- mutex_lock(&dev->lock);
- ctrlreg1 = readl(dev->io_base + SMI_CR1);
- writel((ctrlreg1 | WB_MODE) & ~SW_MODE, dev->io_base + SMI_CR1);
- /*
- * In Write Burst mode (WB_MODE), the specs states that writes must be:
- * - incremental
- * - of the same size
- * The ARM implementation of memcpy_toio() will optimize the number of
- * I/O by using as much 4-byte writes as possible, surrounded by
- * 2-byte/1-byte access if:
- * - the destination is not 4-byte aligned
- * - the length is not a multiple of 4-byte.
- * Avoid this alternance of write access size by using our own 'byte
- * access' helper if at least one of the two conditions above is true.
- */
- if (IS_ALIGNED(len, sizeof(u32)) &&
- IS_ALIGNED((uintptr_t)dest, sizeof(u32)))
- memcpy_toio(dest, src, len);
- else
- spear_smi_memcpy_toio_b(dest, src, len);
- writel(ctrlreg1, dev->io_base + SMI_CR1);
- mutex_unlock(&dev->lock);
- return 0;
- }
- /**
- * spear_mtd_write - performs write operation as requested by the user.
- * @mtd: MTD information of the memory bank.
- * @to: Address to write.
- * @len: Number of bytes to be written.
- * @retlen: Number of bytes actually wrote.
- * @buf: Buffer from which the data to be taken.
- *
- * Write an address range to the flash chip. Data must be written in
- * flash_page_size chunks. The address range may be any size provided
- * it is within the physical boundaries.
- * Returns 0 on success, non zero otherwise
- */
- static int spear_mtd_write(struct mtd_info *mtd, loff_t to, size_t len,
- size_t *retlen, const u8 *buf)
- {
- struct spear_snor_flash *flash = get_flash_data(mtd);
- struct spear_smi *dev = mtd->priv;
- void __iomem *dest;
- u32 page_offset, page_size;
- int ret;
- if (!flash || !dev)
- return -ENODEV;
- if (flash->bank > dev->num_flashes - 1) {
- dev_err(&dev->pdev->dev, "Invalid Bank Num");
- return -EINVAL;
- }
- /* select address as per bank number */
- dest = flash->base_addr + to;
- mutex_lock(&flash->lock);
- page_offset = (u32)to % flash->page_size;
- /* do if all the bytes fit onto one page */
- if (page_offset + len <= flash->page_size) {
- ret = spear_smi_cpy_toio(dev, flash->bank, dest, buf, len);
- if (!ret)
- *retlen += len;
- } else {
- u32 i;
- /* the size of data remaining on the first page */
- page_size = flash->page_size - page_offset;
- ret = spear_smi_cpy_toio(dev, flash->bank, dest, buf,
- page_size);
- if (ret)
- goto err_write;
- else
- *retlen += page_size;
- /* write everything in pagesize chunks */
- for (i = page_size; i < len; i += page_size) {
- page_size = len - i;
- if (page_size > flash->page_size)
- page_size = flash->page_size;
- ret = spear_smi_cpy_toio(dev, flash->bank, dest + i,
- buf + i, page_size);
- if (ret)
- break;
- else
- *retlen += page_size;
- }
- }
- err_write:
- mutex_unlock(&flash->lock);
- return ret;
- }
- /**
- * spear_smi_probe_flash - Detects the NOR Flash chip.
- * @dev: structure of SMI information.
- * @bank: bank on which flash must be probed
- *
- * This routine will check whether there exists a flash chip on a given memory
- * bank ID.
- * Return index of the probed flash in flash devices structure
- */
- static int spear_smi_probe_flash(struct spear_smi *dev, u32 bank)
- {
- int ret;
- u32 val = 0;
- ret = spear_smi_wait_till_ready(dev, bank, SMI_PROBE_TIMEOUT);
- if (ret)
- return ret;
- mutex_lock(&dev->lock);
- dev->status = 0; /* Will be set in interrupt handler */
- /* put smi in sw mode */
- val = readl(dev->io_base + SMI_CR1);
- writel(val | SW_MODE, dev->io_base + SMI_CR1);
- /* send readid command in sw mode */
- writel(OPCODE_RDID, dev->io_base + SMI_TR);
- val = (bank << BANK_SHIFT) | SEND | (1 << TX_LEN_SHIFT) |
- (3 << RX_LEN_SHIFT) | TFIE;
- writel(val, dev->io_base + SMI_CR2);
- /* wait for TFF */
- ret = wait_event_interruptible_timeout(dev->cmd_complete,
- dev->status & TFF, SMI_CMD_TIMEOUT);
- if (ret <= 0) {
- ret = -ENODEV;
- goto err_probe;
- }
- /* get memory chip id */
- val = readl(dev->io_base + SMI_RR);
- val &= 0x00ffffff;
- ret = get_flash_index(val);
- err_probe:
- /* clear sw mode */
- val = readl(dev->io_base + SMI_CR1);
- writel(val & ~SW_MODE, dev->io_base + SMI_CR1);
- mutex_unlock(&dev->lock);
- return ret;
- }
- #ifdef CONFIG_OF
- static int spear_smi_probe_config_dt(struct platform_device *pdev,
- struct device_node *np)
- {
- struct spear_smi_plat_data *pdata = dev_get_platdata(&pdev->dev);
- struct device_node *pp = NULL;
- const __be32 *addr;
- u32 val;
- int len;
- int i = 0;
- if (!np)
- return -ENODEV;
- of_property_read_u32(np, "clock-rate", &val);
- pdata->clk_rate = val;
- pdata->board_flash_info = devm_kzalloc(&pdev->dev,
- sizeof(*pdata->board_flash_info),
- GFP_KERNEL);
- if (!pdata->board_flash_info)
- return -ENOMEM;
- /* Fill structs for each subnode (flash device) */
- while ((pp = of_get_next_child(np, pp))) {
- struct spear_smi_flash_info *flash_info;
- flash_info = &pdata->board_flash_info[i];
- pdata->np[i] = pp;
- /* Read base-addr and size from DT */
- addr = of_get_property(pp, "reg", &len);
- pdata->board_flash_info->mem_base = be32_to_cpup(&addr[0]);
- pdata->board_flash_info->size = be32_to_cpup(&addr[1]);
- if (of_get_property(pp, "st,smi-fast-mode", NULL))
- pdata->board_flash_info->fast_mode = 1;
- i++;
- }
- pdata->num_flashes = i;
- return 0;
- }
- #else
- static int spear_smi_probe_config_dt(struct platform_device *pdev,
- struct device_node *np)
- {
- return -ENOSYS;
- }
- #endif
- static int spear_smi_setup_banks(struct platform_device *pdev,
- u32 bank, struct device_node *np)
- {
- struct spear_smi *dev = platform_get_drvdata(pdev);
- struct spear_smi_flash_info *flash_info;
- struct spear_smi_plat_data *pdata;
- struct spear_snor_flash *flash;
- struct mtd_partition *parts = NULL;
- int count = 0;
- int flash_index;
- int ret = 0;
- pdata = dev_get_platdata(&pdev->dev);
- if (bank > pdata->num_flashes - 1)
- return -EINVAL;
- flash_info = &pdata->board_flash_info[bank];
- if (!flash_info)
- return -ENODEV;
- flash = devm_kzalloc(&pdev->dev, sizeof(*flash), GFP_ATOMIC);
- if (!flash)
- return -ENOMEM;
- flash->bank = bank;
- flash->fast_mode = flash_info->fast_mode ? 1 : 0;
- mutex_init(&flash->lock);
- /* verify whether nor flash is really present on board */
- flash_index = spear_smi_probe_flash(dev, bank);
- if (flash_index < 0) {
- dev_info(&dev->pdev->dev, "smi-nor%d not found\n", bank);
- return flash_index;
- }
- /* map the memory for nor flash chip */
- flash->base_addr = devm_ioremap(&pdev->dev, flash_info->mem_base,
- flash_info->size);
- if (!flash->base_addr)
- return -EIO;
- dev->flash[bank] = flash;
- flash->mtd.priv = dev;
- if (flash_info->name)
- flash->mtd.name = flash_info->name;
- else
- flash->mtd.name = flash_devices[flash_index].name;
- flash->mtd.dev.parent = &pdev->dev;
- mtd_set_of_node(&flash->mtd, np);
- flash->mtd.type = MTD_NORFLASH;
- flash->mtd.writesize = 1;
- flash->mtd.flags = MTD_CAP_NORFLASH;
- flash->mtd.size = flash_info->size;
- flash->mtd.erasesize = flash_devices[flash_index].sectorsize;
- flash->page_size = flash_devices[flash_index].pagesize;
- flash->mtd.writebufsize = flash->page_size;
- flash->erase_cmd = flash_devices[flash_index].erase_cmd;
- flash->mtd._erase = spear_mtd_erase;
- flash->mtd._read = spear_mtd_read;
- flash->mtd._write = spear_mtd_write;
- flash->dev_id = flash_devices[flash_index].device_id;
- dev_info(&dev->pdev->dev, "mtd .name=%s .size=%llx(%lluM)\n",
- flash->mtd.name, flash->mtd.size,
- flash->mtd.size / (1024 * 1024));
- dev_info(&dev->pdev->dev, ".erasesize = 0x%x(%uK)\n",
- flash->mtd.erasesize, flash->mtd.erasesize / 1024);
- #ifndef CONFIG_OF
- if (flash_info->partitions) {
- parts = flash_info->partitions;
- count = flash_info->nr_partitions;
- }
- #endif
- ret = mtd_device_register(&flash->mtd, parts, count);
- if (ret) {
- dev_err(&dev->pdev->dev, "Err MTD partition=%d\n", ret);
- return ret;
- }
- return 0;
- }
- /**
- * spear_smi_probe - Entry routine
- * @pdev: platform device structure
- *
- * This is the first routine which gets invoked during booting and does all
- * initialization/allocation work. The routine looks for available memory banks,
- * and do proper init for any found one.
- * Returns 0 on success, non zero otherwise
- */
- static int spear_smi_probe(struct platform_device *pdev)
- {
- struct device_node *np = pdev->dev.of_node;
- struct spear_smi_plat_data *pdata = NULL;
- struct spear_smi *dev;
- struct resource *smi_base;
- int irq, ret = 0;
- int i;
- if (np) {
- pdata = devm_kzalloc(&pdev->dev, sizeof(*pdata), GFP_KERNEL);
- if (!pdata) {
- ret = -ENOMEM;
- goto err;
- }
- pdev->dev.platform_data = pdata;
- ret = spear_smi_probe_config_dt(pdev, np);
- if (ret) {
- ret = -ENODEV;
- dev_err(&pdev->dev, "no platform data\n");
- goto err;
- }
- } else {
- pdata = dev_get_platdata(&pdev->dev);
- if (!pdata) {
- ret = -ENODEV;
- dev_err(&pdev->dev, "no platform data\n");
- goto err;
- }
- }
- irq = platform_get_irq(pdev, 0);
- if (irq < 0) {
- ret = -ENODEV;
- dev_err(&pdev->dev, "invalid smi irq\n");
- goto err;
- }
- dev = devm_kzalloc(&pdev->dev, sizeof(*dev), GFP_ATOMIC);
- if (!dev) {
- ret = -ENOMEM;
- goto err;
- }
- smi_base = platform_get_resource(pdev, IORESOURCE_MEM, 0);
- dev->io_base = devm_ioremap_resource(&pdev->dev, smi_base);
- if (IS_ERR(dev->io_base)) {
- ret = PTR_ERR(dev->io_base);
- goto err;
- }
- dev->pdev = pdev;
- dev->clk_rate = pdata->clk_rate;
- if (dev->clk_rate > SMI_MAX_CLOCK_FREQ)
- dev->clk_rate = SMI_MAX_CLOCK_FREQ;
- dev->num_flashes = pdata->num_flashes;
- if (dev->num_flashes > MAX_NUM_FLASH_CHIP) {
- dev_err(&pdev->dev, "exceeding max number of flashes\n");
- dev->num_flashes = MAX_NUM_FLASH_CHIP;
- }
- dev->clk = devm_clk_get(&pdev->dev, NULL);
- if (IS_ERR(dev->clk)) {
- ret = PTR_ERR(dev->clk);
- goto err;
- }
- ret = clk_prepare_enable(dev->clk);
- if (ret)
- goto err;
- ret = devm_request_irq(&pdev->dev, irq, spear_smi_int_handler, 0,
- pdev->name, dev);
- if (ret) {
- dev_err(&dev->pdev->dev, "SMI IRQ allocation failed\n");
- goto err_irq;
- }
- mutex_init(&dev->lock);
- init_waitqueue_head(&dev->cmd_complete);
- spear_smi_hw_init(dev);
- platform_set_drvdata(pdev, dev);
- /* loop for each serial nor-flash which is connected to smi */
- for (i = 0; i < dev->num_flashes; i++) {
- ret = spear_smi_setup_banks(pdev, i, pdata->np[i]);
- if (ret) {
- dev_err(&dev->pdev->dev, "bank setup failed\n");
- goto err_irq;
- }
- }
- return 0;
- err_irq:
- clk_disable_unprepare(dev->clk);
- err:
- return ret;
- }
- /**
- * spear_smi_remove - Exit routine
- * @pdev: platform device structure
- *
- * free all allocations and delete the partitions.
- */
- static int spear_smi_remove(struct platform_device *pdev)
- {
- struct spear_smi *dev;
- struct spear_snor_flash *flash;
- int ret, i;
- dev = platform_get_drvdata(pdev);
- if (!dev) {
- dev_err(&pdev->dev, "dev is null\n");
- return -ENODEV;
- }
- /* clean up for all nor flash */
- for (i = 0; i < dev->num_flashes; i++) {
- flash = dev->flash[i];
- if (!flash)
- continue;
- /* clean up mtd stuff */
- ret = mtd_device_unregister(&flash->mtd);
- if (ret)
- dev_err(&pdev->dev, "error removing mtd\n");
- }
- clk_disable_unprepare(dev->clk);
- return 0;
- }
- #ifdef CONFIG_PM_SLEEP
- static int spear_smi_suspend(struct device *dev)
- {
- struct spear_smi *sdev = dev_get_drvdata(dev);
- if (sdev && sdev->clk)
- clk_disable_unprepare(sdev->clk);
- return 0;
- }
- static int spear_smi_resume(struct device *dev)
- {
- struct spear_smi *sdev = dev_get_drvdata(dev);
- int ret = -EPERM;
- if (sdev && sdev->clk)
- ret = clk_prepare_enable(sdev->clk);
- if (!ret)
- spear_smi_hw_init(sdev);
- return ret;
- }
- #endif
- static SIMPLE_DEV_PM_OPS(spear_smi_pm_ops, spear_smi_suspend, spear_smi_resume);
- #ifdef CONFIG_OF
- static const struct of_device_id spear_smi_id_table[] = {
- { .compatible = "st,spear600-smi" },
- {}
- };
- MODULE_DEVICE_TABLE(of, spear_smi_id_table);
- #endif
- static struct platform_driver spear_smi_driver = {
- .driver = {
- .name = "smi",
- .bus = &platform_bus_type,
- .of_match_table = of_match_ptr(spear_smi_id_table),
- .pm = &spear_smi_pm_ops,
- },
- .probe = spear_smi_probe,
- .remove = spear_smi_remove,
- };
- module_platform_driver(spear_smi_driver);
- MODULE_LICENSE("GPL");
- MODULE_AUTHOR("Ashish Priyadarshi, Shiraz Hashim <shiraz.linux.kernel@gmail.com>");
- MODULE_DESCRIPTION("MTD SMI driver for serial nor flash chips");
|