diff options
author | Mingjun Zhang <troy.zhangmingjun@huawei.com> | 2013-01-29 15:52:30 +0800 |
---|---|---|
committer | Guodong Xu <guodong.xu@linaro.org> | 2013-02-21 16:12:29 +0800 |
commit | b6ce50d779dd46750c35a5c0d548ef212dec13dd (patch) | |
tree | d20c7ade008843988720f6ac03c193827aa47cab /drivers | |
parent | 2ec5131cc00286299f807c894cfda0cf0d87dc4d (diff) |
mtd: HS SFC: add SFC driver for serial NOR flash
Hisilicon Serial NOR Flash controller provide three ways to access the
serial NOR flash chip: by reg interface, mapping it to a range of bus
address and using dma.
This driver implement the dma read/write ops and reg erase ops.
Signed-off-by: Mingjun Zhang <troy.zhangmingjun@huawei.com>
Diffstat (limited to 'drivers')
-rw-r--r-- | drivers/mtd/devices/Kconfig | 11 | ||||
-rw-r--r-- | drivers/mtd/devices/Makefile | 3 | ||||
-rw-r--r-- | drivers/mtd/devices/hs_sfc/Makefile | 1 | ||||
-rw-r--r-- | drivers/mtd/devices/hs_sfc/flash_info.c | 41 | ||||
-rw-r--r-- | drivers/mtd/devices/hs_sfc/flash_info.h | 161 | ||||
-rw-r--r-- | drivers/mtd/devices/hs_sfc/hisfc350.c | 860 | ||||
-rw-r--r-- | drivers/mtd/devices/hs_sfc/hisfc350.h | 191 |
7 files changed, 1267 insertions, 1 deletions
diff --git a/drivers/mtd/devices/Kconfig b/drivers/mtd/devices/Kconfig index 46dcb54c32e..214e8481967 100644 --- a/drivers/mtd/devices/Kconfig +++ b/drivers/mtd/devices/Kconfig @@ -343,4 +343,15 @@ config MTD_DOCPROBE_55AA LinuxBIOS or if you need to recover a DiskOnChip Millennium on which you have managed to wipe the first block. +config MTD_HS_SFC + tristate "hisilicon MTD serial NOR Support through SFC controller" + depends on ARCH_HS + default y + help + This enable SNOR support on hisilicon platforms using SFC controller. + Hisilicon SFC have three ways to access the serial NOR flash: using + reg interface, mapping flash-chip to a range of bus address, using DMA + Using DMA is more faster and convenient. this driver uses dma to + read/write flash, and use reg interface to control erasing it. + endmenu diff --git a/drivers/mtd/devices/Makefile b/drivers/mtd/devices/Makefile index 395733a30ef..9de3c7ed6ae 100644 --- a/drivers/mtd/devices/Makefile +++ b/drivers/mtd/devices/Makefile @@ -20,5 +20,6 @@ obj-$(CONFIG_MTD_M25P80) += m25p80.o obj-$(CONFIG_MTD_SPEAR_SMI) += spear_smi.o obj-$(CONFIG_MTD_SST25L) += sst25l.o obj-$(CONFIG_MTD_BCM47XXSFLASH) += bcm47xxsflash.o +obj-$(CONFIG_MTD_HS_SFC) += hs_sfc/ -CFLAGS_docg3.o += -I$(src)
\ No newline at end of file +CFLAGS_docg3.o += -I$(src) diff --git a/drivers/mtd/devices/hs_sfc/Makefile b/drivers/mtd/devices/hs_sfc/Makefile new file mode 100644 index 00000000000..62ec536ce94 --- /dev/null +++ b/drivers/mtd/devices/hs_sfc/Makefile @@ -0,0 +1 @@ +obj-$(CONFIG_MTD_HS_SFC) += flash_info.o hisfc350.o diff --git a/drivers/mtd/devices/hs_sfc/flash_info.c b/drivers/mtd/devices/hs_sfc/flash_info.c new file mode 100644 index 00000000000..debbd160d4e --- /dev/null +++ b/drivers/mtd/devices/hs_sfc/flash_info.c @@ -0,0 +1,41 @@ +/****************************************************************************** +* Copyright (c) 2009-2010 by HiC. +* All rights reserved. +* *** +* Create by HiC 2010-12-18 +* +******************************************************************************/ +#include "flash_info.h" + +/*****************************************************************************/ +char *hisfc_get_iftype_str(int iftype) +{ + static char *iftype_str[HISFC_IFTYPE_MAX] = { + "single", + "single",/* fast */ + "dual", + "dual-addr", + "dual-cmd", + "quad", + "quad-addr", + "quad-cmd", + "invalid" + }; + + if (iftype >= HISFC_IFTYPE_MAX) + return iftype_str[HISFC_IFTYPE_MAX - 1]; + else + return iftype_str[iftype]; +} +/*****************************************************************************/ +const struct flash_info hisfc_ids_table[] = { + {"S25FL128P-0", 0x012018, 0x0300, 3, _256K, _256K * 64, + RD(FAST, 1, 104), WR(STD, 0, 104), ES(0, 104)}, + {"S25FL128P-1", 0x012018, 0x0301, 3, _64K, (_64K * 256), + RD(FAST, 1, 104), WR(STD, 0, 104), ES(0, 104)}, + + /* make sure the last entry of the table is 0 filled, so probe code will + * know it's the end of the table + */ + {0} +}; diff --git a/drivers/mtd/devices/hs_sfc/flash_info.h b/drivers/mtd/devices/hs_sfc/flash_info.h new file mode 100644 index 00000000000..19b71165a3d --- /dev/null +++ b/drivers/mtd/devices/hs_sfc/flash_info.h @@ -0,0 +1,161 @@ +/****************************************************************************** + * Copyright (c) 2009-2010 HiC. + * All rights reserved. + * *** + * HiC 2010-12-18 + * + ******************************************************************************/ + +#ifndef _FLASH_INFO_H +#define _FLASH_INFO_H + +#include <linux/types.h> + +/*****************************************************************************/ +#define _1K (0x400) +#define _2K (0x800) + +#define _4K (0x1000) +#define _8K (0x2000) +#define _16K (0x4000) +#define _32K (0x8000) + +#define _64K (0x10000) +#define _128K (0x20000) +#define _256K (0x40000) +#define _512K (0x80000) + +#define _1M (0x100000) +#define _2M (0x200000) +#define _4M (0x400000) +#define _8M (0x800000) + +#define _16M (0x1000000) +#define _32M (0x2000000) + +#define INFINITE (0xFFFFFFFF) +/*****************************************************************************/ +/* sector erase, 64K */ +#define SPI_IF_ERASE_SECTOR (0x01) +/* chip erase */ +#define SPI_IF_ERASE_CHIP (0x02) +/* 4K */ +#define SPI_IF_ERASE_4K (0x04) +/* 8K */ +#define SPI_IF_ERASE_8K (0x08) +/*****************************************************************************/ +/* Write Enable */ +#define SPI_CMD_WREN 0x06 +/*---------------------------------------------------------------------------*/ +/* 4KB sector Erase */ +#define SPI_CMD_SE_4K 0x20 +/* 32KB sector Erase */ +#define SPI_CMD_SE_32K 0x52 +/* 64KB Sector Erase */ +#define SPI_CMD_SE 0xD8 +/* chip erase */ +#define SPI_CMD_BE 0xC7 +/*---------------------------------------------------------------------------*/ +/* Read Status Register */ +#define SPI_CMD_RDSR 0x05 +/* Read Identification */ +#define SPI_CMD_RDID 0x9F +/*---------------------------------------------------------------------------*/ +/* Page Programming */ +#define SPI_CMD_WRITE_STD 0x02 +/* fast program dual input */ +#define SPI_CMD_WRITE_DUAL 0xA2 +/* fast program quad input */ +#define SPI_CMD_WRITE_QUAD 0x32 +/* Dual I/O High Performance Write */ +#define SPI_CMD_WRITE_DUAL_ADDR 0xD2 +/* Quad I/O High Performance Write */ +#define SPI_CMD_WRITE_QUAD_ADDR 0x12 +/*---------------------------------------------------------------------------*/ +/* Read Data bytes */ +#define SPI_CMD_READ_STD 0x03 +/* Read Data Bytes at Higher Speed */ +#define SPI_CMD_READ_FAST 0x0B +/* fast read dual output */ +#define SPI_CMD_READ_DUAL 0x3B +/* fast read quad output */ +#define SPI_CMD_READ_QUAD 0x6B +/* Dual I/O High Performance Read */ +#define SPI_CMD_READ_DUAL_ADDR 0xBB +/* Quad I/O High Performance Read */ +#define SPI_CMD_READ_QUAD_ADDR 0xEB +/*---------------------------------------------------------------------------*/ +/* Write in Progress */ +#define SPI_CMD_SR_WIP 1 +/* Write Enable Latch */ +#define SPI_CMD_SR_WEL 2 +/*---------------------------------------------------------------------------*/ +/* enter to 4 bytes mode and set 4 byte bit as '1' */ +#define SPI_CMD_EN4B 0xB7 +/* exit 4 bytes mode and clear 4 byte bit as '0' */ +#define SPI_CMD_EX4B 0xE9 +/*---------------------------------------------------------------------------*/ +enum { + HISFC_IFTYPE_STD, +#define HISFC_IFTYPE_STD HISFC_IFTYPE_STD + HISFC_IFTYPE_FAST, +#define HISFC_IFTYPE_FAST HISFC_IFTYPE_FAST + HISFC_IFTYPE_DUAL, +#define HISFC_IFTYPE_DUAL HISFC_IFTYPE_DUAL + HISFC_IFTYPE_DUAL_ADDR, +#define HISFC_IFTYPE_DUAL_ADDR HISFC_IFTYPE_DUAL_ADDR + HISFC_IFTYPE_DUAL_CMD, +#define HISFC_IFTYPE_DUAL_CMD HISFC_IFTYPE_DUAL_CMD + HISFC_IFTYPE_QUAD, +#define HISFC_IFTYPE_QUAD HISFC_IFTYPE_QUAD + HISFC_IFTYPE_QUAD_ADDR, +#define HISFC_IFTYPE_QUAD_ADDR HISFC_IFTYPE_QUAD_ADDR + HISFC_IFTYPE_QUAD_CMD, +#define HISFC_IFTYPE_QUAD_CMD HISFC_IFTYPE_QUAD_CMD + HISFC_IFTYPE_INVALID, +#define HISFC_IFTYPE_INVALID HISFC_IFTYPE_INVALID + HISFC_IFTYPE_MAX, +#define HISFC_IFTYPE_MAX HISFC_IFTYPE_MAX +}; + +#define RD(iftype, dummy, clk) \ +{ HISFC_IFTYPE_##iftype, SPI_CMD_READ_##iftype, dummy, clk } + +#define WR(iftype, dummy, clk) \ +{ HISFC_IFTYPE_##iftype, SPI_CMD_WRITE_##iftype, dummy, clk } + +/* use sector erase */ +#define ES(dummy, clk) \ +{ HISFC_IFTYPE_STD, SPI_CMD_SE, dummy, clk } + +/*****************************************************************************/ +struct sfc_operation { + unsigned char iftype; + unsigned char cmd; + unsigned char dummy; + unsigned int clock; +}; + +struct flash_info { + char *name; + /* JEDEC id zero means "no ID" (most older chips); otherwise it has + * a high byte of zero plus three data bytes: the manufacturer id, + * then a two byte device id. + */ + u32 jedec_id; + u16 ext_id; + + u16 addr_width; + unsigned long erase_size; + unsigned long chip_size; + + struct sfc_operation read; + struct sfc_operation write; + struct sfc_operation erase; +}; + +extern const struct flash_info hisfc_ids_table[]; + +char *hisfc_get_iftype_str(int iftype); +/******************************************************************************/ +#endif diff --git a/drivers/mtd/devices/hs_sfc/hisfc350.c b/drivers/mtd/devices/hs_sfc/hisfc350.c new file mode 100644 index 00000000000..97292e0546b --- /dev/null +++ b/drivers/mtd/devices/hs_sfc/hisfc350.c @@ -0,0 +1,860 @@ +/***************************************************************************** + * Copyright (c) 2009-2011 by Hisi + * All rights reserved. + * *** + * Create by CCC 2010-09-01 + * + *****************************************************************************/ + +/*****************************************************************************/ +#include <linux/init.h> +#include <linux/module.h> +#include <linux/device.h> +#include <linux/interrupt.h> +#include <linux/slab.h> +#include <linux/mtd/mtd.h> +#include <linux/mtd/partitions.h> +#include <linux/delay.h> +#include <linux/sched.h> +#include <asm/setup.h> +#include <linux/dma-mapping.h> +#include <linux/kernel.h> +#include <linux/sched.h> +#include <linux/delay.h> +#include <linux/semaphore.h> +#include <linux/platform_device.h> +#include <linux/of.h> + +#include "hisfc350.h" + +/*****************************************************************************/ +/* Don't change the follow config */ +#define HISFC350_SUPPORT_READ (SPI_IF_READ_STD \ + | SPI_IF_READ_FAST \ + | SPI_IF_READ_DUAL \ + | SPI_IF_READ_DUAL_ADDR \ + | SPI_IF_READ_QUAD \ + | SPI_IF_READ_QUAD_ADDR) + +#define HISFC350_SUPPORT_WRITE (SPI_IF_WRITE_STD \ + | SPI_IF_WRITE_DUAL \ + | SPI_IF_WRITE_DUAL_ADDR \ + | SPI_IF_WRITE_QUAD \ + | SPI_IF_WRITE_QUAD_ADDR) + +#define HISFC350_SUPPORT_MAX_DUMMY (7) + +/* this function only for debug, reg read is slower then dma read */ +#undef HISFCV350_SUPPORT_REG_READ + +#define DRIVER_NAME "hi_sfc" + +/*****************************************************************************/ +static char *ultohstr(unsigned long long size, char *buffer) +{ + char *fmt[] = {"%u", "%uK", "%uM", "%uG", "%uT", "%uT"}; + int ix; + + for (ix = 0; (ix < 5) && !(size & 0x3FF) && size; ix++) + size = (size >> 10); + + sprintf(buffer, fmt[ix], size); + return buffer; +} +static int hisfc350_wait_ready(struct hisfc_host *host, + struct hisfc_flash_device *flash) +{ + unsigned long val; + unsigned long deadline = jiffies + HISFC350_MAX_READY_WAIT_JIFFIES; + + do { + hisfc_write(HISFC350_CMD_INS, SPI_CMD_RDSR); + hisfc_write(HISFC350_CMD_CONFIG, + HISFC350_CMD_CONFIG_SEL_CS(flash->cs) + | HISFC350_CMD_CONFIG_DATA_CNT(1) + | HISFC350_CMD_CONFIG_DATA_EN + | HISFC350_CMD_CONFIG_RW_READ + | HISFC350_CMD_CONFIG_START); + + HISFC350_CMD_WAIT_CPU_FINISH(host); + + val = hisfc_read(HISFC350_CMD_DATABUF0); + if (!(val & SPI_CMD_SR_WIP)) + return 0; + + cond_resched(); + + } while (!time_after_eq(jiffies, deadline)); + + pr_info(KERN_ERR "Wait flash-dev ready timeout.\n"); + + return -EBUSY; +} +/*****************************************************************************/ +static void hisfc350_flash_set_4byte(struct hisfc_flash_device *flash, + int enable) +{ + struct hisfc_host *host = flash->priv; + + if (enable) { + hisfc_write(HISFC350_CMD_INS, SPI_CMD_EN4B); + flash->addr_mode = addr_mode_4byte; + } else { + hisfc_write(HISFC350_CMD_INS, SPI_CMD_EX4B); + flash->addr_mode = addr_mode_3byte; + } + + hisfc_write(HISFC350_CMD_CONFIG, + HISFC350_CMD_CONFIG_SEL_CS(flash->cs) + | HISFC350_CMD_CONFIG_START); + + HISFC350_CMD_WAIT_CPU_FINISH(host); +} + +/* config host to co-operate with the flash chip */ +static inline void hisfc350_host_select_flash(struct hisfc_host *host, + struct hisfc_flash_device *flash) +{ + unsigned int val; + + if (host->addr_mode != flash->addr_mode) { + val = hisfc_read(HISFC350_GLOBAL_CONFIG); + if (flash->addr_mode == addr_mode_4byte) + val |= HISFC350_GLOBAL_CONFIG_ADDR_MODE_4B; + else + val &= ~HISFC350_GLOBAL_CONFIG_ADDR_MODE_4B; + hisfc_write(HISFC350_GLOBAL_CONFIG, val); + host->addr_mode = flash->addr_mode; + } + + host->cs = flash->cs; +} + +/*****************************************************************************/ +static void hisfc350_dma_bus_config(struct hisfc_host *host, + struct hisfc_flash_device *flash) +{ + unsigned int val = 0; + + val |= HISFC350_BUS_CONFIG1_WRITE_INS(flash->info.write.cmd); + val |= HISFC350_BUS_CONFIG1_WRITE_DUMMY_CNT(flash->info.write.dummy); + val |= HISFC350_BUS_CONFIG1_WRITE_IF_TYPE(flash->info.write.iftype); + + val |= HISFC350_BUS_CONFIG1_READ_PREF_CNT(0); + val |= HISFC350_BUS_CONFIG1_READ_INS(flash->info.read.cmd); + val |= HISFC350_BUS_CONFIG1_READ_DUMMY_CNT(flash->info.read.dummy); + val |= HISFC350_BUS_CONFIG1_READ_IF_TYPE(flash->info.read.iftype); + + hisfc_write(HISFC350_BUS_CONFIG1, val); +} +/*****************************************************************************/ +static void hisfc350_map_iftype(struct hisfc_flash_device *flash) +{ + struct flash_info *info = &flash->info; + char iftype_hw_map[HISFC_IFTYPE_MAX] = { + [HISFC_IFTYPE_STD] = HISFC350_IF_HW_STD, + [HISFC_IFTYPE_FAST] = HISFC350_IF_HW_STD, + [HISFC_IFTYPE_DUAL] = HISFC350_IF_HW_DUAL, + [HISFC_IFTYPE_DUAL_ADDR] = HISFC350_IF_HW_DUAL_ADDR, + [HISFC_IFTYPE_DUAL_CMD] = HISFC350_IF_HW_DUAL_CMD, + [HISFC_IFTYPE_QUAD] = HISFC350_IF_HW_QUAD, + [HISFC_IFTYPE_QUAD_ADDR] = HISFC350_IF_HW_QUAD_ADDR, + [HISFC_IFTYPE_QUAD_CMD] = HISFC350_IF_HW_QUAD_CMD, + [HISFC_IFTYPE_INVALID] = HISFC_IFTYPE_INVALID, + }; + + if (info->read.iftype >= HISFC_IFTYPE_INVALID) + info->read.iftype = HISFC_IFTYPE_INVALID; + else + info->read.iftype = iftype_hw_map[info->read.iftype]; + + if (info->write.iftype >= HISFC_IFTYPE_INVALID) + info->write.iftype = HISFC_IFTYPE_INVALID; + else + info->write.iftype = iftype_hw_map[info->write.iftype]; +} +/*****************************************************************************/ +static void hisfc350_dma_transfer(struct hisfc_host *host, + unsigned int spi_start_addr, dma_addr_t dma_buffer, + unsigned char is_read, unsigned int size, unsigned char cs) +{ + hisfc_write(HISFC350_BUS_DMA_MEM_SADDR, dma_buffer); + + hisfc_write(HISFC350_BUS_DMA_FLASH_SADDR, + spi_start_addr); + + hisfc_write(HISFC350_BUS_DMA_LEN, + HISFC350_BUS_DMA_LEN_DATA_CNT(size)); + + hisfc_write(HISFC350_BUS_DMA_AHB_CTRL, + HISFC350_BUS_DMA_AHB_CTRL_INCR4_EN + | HISFC350_BUS_DMA_AHB_CTRL_INCR8_EN + | HISFC350_BUS_DMA_AHB_CTRL_INCR16_EN); + + hisfc_write(HISFC350_BUS_DMA_CTRL, + HISFC350_BUS_DMA_CTRL_RW(is_read) + | HISFC350_BUS_DMA_CTRL_CS(cs) + | HISFC350_BUS_DMA_CTRL_START); + + HISFC350_DMA_WAIT_CPU_FINISH(host); + +} +/*****************************************************************************/ +#ifdef HISFCV350_SUPPORT_REG_READ +static char *hisfc350_reg_read_buf(struct hisfc_host *host, + struct hisfc_flash_device *flash, unsigned int spi_start_addr, + unsigned int size, unsigned char *buffer) +{ + int numread; + int index = 0; + + hisfc_write(HISFC350_CMD_INS, flash->info.read.cmd); + hisfc_write(HISFC350_CMD_ADDR, + (spi_start_addr & HISFC350_CMD_ADDR_MASK)); + hisfc_write(HISFC350_CMD_CONFIG, + HISFC350_CMD_CONFIG_MEM_IF_TYPE(flash->info.read.iftype) + | HISFC350_CMD_CONFIG_DATA_CNT(size) + | HISFC350_CMD_CONFIG_RW_READ + | HISFC350_CMD_CONFIG_DATA_EN + | HISFC350_CMD_CONFIG_DUMMY_CNT(flash->info.read.dummy) + | HISFC350_CMD_CONFIG_ADDR_EN + | HISFC350_CMD_CONFIG_SEL_CS(flash->cs) + | HISFC350_CMD_CONFIG_START); + + HISFC350_CMD_WAIT_CPU_FINISH(host); + + memcpy(buffer, host->io_base + HISFC350_CMD_DATABUF0, size); + + return buffer; +} +/*****************************************************************************/ +static int hisfc350_reg_read(struct mtd_info *mtd, loff_t from, size_t len, + size_t *retlen, u_char *buf) +{ + int num; + int result = -EIO; + unsigned char *ptr = buf; + struct hisfc_host *host = MTD_TO_HOST(mtd); + struct hisfc_flash_device *flash = host->flash; + + mutex_lock(&host->lock); + + if (hisfc350_wait_ready(host, flash)) + goto fail; + /* clk_round_rate(host->clk, flash->info.read.clock); */ + + while (len > 0) { + num = len > HISFC350_REG_BUF_SIZE ? + HISFC350_REG_BUF_SIZE : len; + + hisfc350_reg_read_buf(host, flash, from, num, ptr); + from += num; + ptr += num; + len -= num; + } + result = 0; + *retlen = (size_t)(ptr - buf); +fail: + mutex_unlock(&host->lock); + return result; +} +#endif /* HISFCV350_SUPPORT_REG_READ */ +/*****************************************************************************/ + +static int hisfc350_dma_read(struct mtd_info *mtd, loff_t from, size_t len, + size_t *retlen, u_char *buf) +{ + int num; + int result = -EIO; + unsigned char *ptr = buf; + struct hisfc_flash_device *flash = mtd->priv; + struct hisfc_host *host = flash->priv; + + mutex_lock(&host->lock); + + hisfc350_host_select_flash(host, flash); + + if (hisfc350_wait_ready(host, flash)) + goto fail; + hisfc350_dma_bus_config(host, flash); + + /* clk_round_rate(host->clk, flash->info.read.clock); */ + + /* when start addr is not aligned, sfc-dma engine will misfunction + * at the first aligned boudary. so we break the dma read ops to + * two parts: 1) read the not aligned part; 2) read the reset + */ + if (from & HISFC350_DMA_ALIGN_MASK) {/* 1) not aligned part */ + num = HISFC350_DMA_ALIGN_SIZE - + (from & HISFC350_DMA_ALIGN_MASK); + if (num > len) + num = len; + + hisfc350_dma_transfer(host, from, host->dma_buffer, READ, + num, flash->cs); + memcpy(ptr, host->buffer, num); + from += num; + ptr += num; + len -= num; + } + + while (len) {/* 2) the reset */ + num = len > HISFC350_DMA_MAX_SIZE ? HISFC350_DMA_MAX_SIZE : len; + hisfc350_dma_transfer(host, from, host->dma_buffer, READ, + num, flash->cs); + memcpy(ptr, host->buffer, num); + ptr += num; + from += num; + len -= num; + } + + result = 0; + *retlen = (size_t)(ptr - buf); +fail: + mutex_unlock(&host->lock); + return result; +} +/*****************************************************************************/ +static int hisfc350_read_id(struct hisfc_host *host, int cs, u8 *id, int len) +{ + hisfc_write(HISFC350_CMD_INS, SPI_CMD_RDID); + hisfc_write(HISFC350_CMD_CONFIG, + HISFC350_CMD_CONFIG_SEL_CS(cs) + | HISFC350_CMD_CONFIG_RW_READ + | HISFC350_CMD_CONFIG_DATA_EN + | HISFC350_CMD_CONFIG_DATA_CNT(len) + | HISFC350_CMD_CONFIG_START); + + HISFC350_CMD_WAIT_CPU_FINISH(host); + + memcpy(id, host->io_base + HISFC350_CMD_DATABUF0, len); + + return 0; +} +/*****************************************************************************/ +static int hisfc350_write_enable(struct hisfc_host *host, + struct hisfc_flash_device *flash) +{ + unsigned int val = 0; + + hisfc_write(HISFC350_CMD_INS, SPI_CMD_WREN); + + val = HISFC350_CMD_CONFIG_SEL_CS(flash->cs) + | HISFC350_CMD_CONFIG_START; + hisfc_write(HISFC350_CMD_CONFIG, val); + + HISFC350_CMD_WAIT_CPU_FINISH(host); + + return 0; +} +/*****************************************************************************/ +static int hisfc350_sector_erase(struct hisfc_host *host, + struct hisfc_flash_device *flash, u32 offset) +{ + if (hisfc350_wait_ready(host, flash)) + return -EBUSY; + hisfc350_write_enable(host, flash); + + /* clk_round_rate(host->clk, flash->info.erase.clock); */ + + hisfc_write(HISFC350_CMD_INS, flash->info.erase.cmd); + + hisfc_write(HISFC350_CMD_ADDR, offset & HISFC350_CMD_ADDR_MASK); + + hisfc_write(HISFC350_CMD_CONFIG, + HISFC350_CMD_CONFIG_SEL_CS(flash->cs) + | HISFC350_CMD_CONFIG_MEM_IF_TYPE(flash->info.erase.iftype) + | HISFC350_CMD_CONFIG_DUMMY_CNT(flash->info.erase.dummy) + | HISFC350_CMD_CONFIG_ADDR_EN + | HISFC350_CMD_CONFIG_START); + + HISFC350_CMD_WAIT_CPU_FINISH(host); + + return 0; +} +/*****************************************************************************/ +static int hisfc350_dma_write(struct mtd_info *mtd, loff_t to, size_t len, + size_t *retlen, const u_char *buf) +{ + int num; + int result = -EIO; + + const u_char *ptr = buf; + struct hisfc_flash_device *flash = mtd->priv; + struct hisfc_host *host = flash->priv; + + mutex_lock(&host->lock); + hisfc350_host_select_flash(host, flash); + + if (hisfc350_wait_ready(host, flash)) + goto fail; + hisfc350_write_enable(host, flash); + hisfc350_dma_bus_config(host, flash); + + /* clk_round_rate(host->clk, flash->info.write.clock); */ + + /* see comment in dma_read */ + if (to & HISFC350_DMA_ALIGN_MASK) {/* 1) not aligned part */ + num = HISFC350_DMA_ALIGN_SIZE - (to & HISFC350_DMA_ALIGN_MASK); + if (num > len) + num = len; + + memcpy(host->buffer, ptr, num); + hisfc350_dma_transfer(host, to, host->dma_buffer, WRITE, + num, flash->cs); + to += num; + ptr += num; + len -= num; + } + + while (len > 0) {/* 2) the reset */ + num = len >= HISFC350_DMA_MAX_SIZE + ? HISFC350_DMA_MAX_SIZE : len; + + memcpy(host->buffer, ptr, num); + hisfc350_dma_transfer(host, to, host->dma_buffer, WRITE, + num, flash->cs); + + to += num; + ptr += num; + len -= num; + } + *retlen = (size_t)(ptr - buf); + result = 0; +fail: + mutex_unlock(&host->lock); + return result; +} +/*****************************************************************************/ +static int hisfc350_reg_erase(struct mtd_info *mtd, struct erase_info *instr) +{ + struct hisfc_flash_device *flash = mtd->priv; + struct hisfc_host *host = flash->priv; + uint64_t addr = instr->addr; + uint64_t len = instr->len; + int ret; + + mutex_lock(&host->lock); + hisfc350_host_select_flash(host, flash); + + while (len) { + ret = hisfc350_sector_erase(host, flash, (u32)addr); + if (ret) { + instr->state = MTD_ERASE_FAILED; + mutex_unlock(&host->lock); + return ret; + } + addr += mtd->erasesize; + len -= mtd->erasesize; + } + + mutex_unlock(&host->lock); + instr->state = MTD_ERASE_DONE; + mtd_erase_callback(instr); + + return 0; +} +/*****************************************************************************/ +static const struct flash_info *__devinit hisfc_flash_probe( + struct hisfc_host *host, int cs) +{ + int tmp; + u8 id[5]; + u32 jedec; + u16 ext_jedec; + const struct flash_info *info; + + /* JEDEC also defines an optional "extended device information" + * string for after vendor-specific data, after the three bytes + * we use here. Supporting some chips might require using it. + */ + tmp = hisfc350_read_id(host, cs, id, 5); + if (tmp < 0) { + dev_dbg(host->dev, "error %d reading JEDEC ID\n", tmp); + return ERR_PTR(tmp); + } + jedec = id[0]; + jedec = jedec << 8; + jedec |= id[1]; + jedec = jedec << 8; + jedec |= id[2]; + + ext_jedec = id[3] << 8 | id[4]; + + /* make sure the last entry of the table is 0 filled */ + for (tmp = 0; hisfc_ids_table[tmp].name; tmp++) { + info = &hisfc_ids_table[tmp]; + if (info->jedec_id == jedec && info->ext_id == ext_jedec) + return info; + } + + if (jedec == 0 || jedec == 0xffffff) + dev_info(host->dev, "no flash chip found on cs%d\n", cs); + else + dev_err(host->dev, "unrecognized JEDEC id %06x\n", jedec); + + return ERR_PTR(-ENODEV); +} +static inline void dump_flash_info(struct hisfc_flash_device *flash) +{ + struct flash_info *info = &flash->info; + char buf1[20]; + char buf2[20]; + + pr_info("SFC(cs%d): Name:\"%s\" Block:%sB Chip:%sB\n", + flash->cs, + info->name, + ultohstr(info->erase_size, buf1), + ultohstr(info->chip_size, buf2)); + + pr_info("addr_width:%sB read:%s,0x%02x,%dM\n", + info->addr_width == 3 ? "3" : "4", + hisfc_get_iftype_str(info->read.iftype), + info->read.cmd, + info->read.clock); + + pr_info("write:%s,0x%02x,%dM\n", + hisfc_get_iftype_str(info->write.iftype), + info->write.cmd, + info->write.clock); + + pr_info("erase:%s,0x%02x,%dM\n", + hisfc_get_iftype_str(info->erase.iftype), + info->erase.cmd, + info->erase.clock); +} +static int hisfc350_setup_flash_device(struct hisfc_host *host, int cs) +{ + struct hisfc_flash_device *flash; + const struct flash_info *info; + int ret; + + info = hisfc_flash_probe(host, cs); + if (IS_ERR(info)) + return PTR_ERR(info); + + flash = devm_kzalloc(host->dev, sizeof(*flash), GFP_ATOMIC); + if (!flash) + return -ENOMEM; + + host->flash[cs] = flash; + flash->cs = cs; + flash->priv = host; + flash->mtd.priv = flash; + memcpy(&flash->info, info, sizeof(*info)); + dump_flash_info(flash); + /* + * use a fixed name instead of flash node's to + * make it easier to config the cmdline partition, + * like mtdparts="hi_sfc:xxx" + */ + flash->mtd.name = DRIVER_NAME; + flash->mtd.type = MTD_NORFLASH; + flash->mtd.size = info->chip_size; + flash->mtd.writesize = 1; + flash->mtd.erasesize = info->erase_size; + flash->mtd.flags = MTD_CAP_NORFLASH; + flash->mtd.owner = THIS_MODULE; + flash->mtd._erase = hisfc350_reg_erase; + flash->mtd._write = hisfc350_dma_write; + flash->mtd._read = hisfc350_dma_read; + + hisfc350_map_iftype(flash); + + if (info->addr_width == 4) + hisfc350_flash_set_4byte(flash, 1); + + /* use the default cmdlinepart parser */ + ret = mtd_device_parse_register(&flash->mtd, NULL, NULL, NULL, 0); + if (ret) { + dev_err(host->dev, "Err MTD partition=%d\n", ret); + ret = -ENODEV; + } + + return ret; +} + +static void hisfc350_hw_init(struct hisfc_host *host) +{ + /* ret = clk_prepare_enable(host->clk); */ + + hisfc_write(HISFC350_TIMING, + HISFC350_TIMING_TCSS(0x6) + | HISFC350_TIMING_TCSH(0x6) + | HISFC350_TIMING_TSHSL(0xf)); +} + +/*****************************************************************************/ +#ifdef CONFIG_PM +/*****************************************************************************/ +static int hisfc350_driver_suspend(struct platform_device *pdev, + pm_message_t state) +{ + struct hisfc_host *host = platform_get_drvdata(pdev); + struct hisfc_flash_device *flash; + int cs; + + /* see comment in suspend() */ + for (cs = 0; cs < CONFIG_HISFC350_CHIP_NUM; cs++) { + flash = host->flash[cs]; + if (!flash) + continue; + if (flash->info.addr_width == 4) + hisfc350_flash_set_4byte(flash, 0); + } + + /* clk_disable_unprepare(host->clk); */ + + return 0; +} +/*****************************************************************************/ + +static int hisfc350_driver_resume(struct platform_device *pdev) +{ + struct hisfc_host *host = platform_get_drvdata(pdev); + struct hisfc_flash_device *flash; + int cs; + + hisfc350_hw_init(host); + + for (cs = 0; cs < CONFIG_HISFC350_CHIP_NUM; cs++) { + flash = host->flash[cs]; + if (!flash) + continue; + if (flash->info.addr_width == 4) + hisfc350_flash_set_4byte(flash, 1); + } + + host->addr_mode = -1; + hisfc350_host_select_flash(host, host->flash[host->cs]); + + return 0; +} +#endif /* CONFIG_PM */ +/*****************************************************************************/ +#ifdef CONFIG_OF +static int __devinit hisfc_probe_config_dt(struct platform_device *pdev, + struct device_node *np) +{ + struct hisfc_host *host = platform_get_drvdata(pdev); + unsigned int version; + + if (!np) + return -ENODEV; + + /* TODO: get clk here */ + + if (!of_property_read_u32(np, "version_reg_offset", &version)) + host->version = version; + + return 0; +} +#else +static int __devinit hisfc_probe_config_dt(struct platform_device *pdev, + struct device_node *np) +{ + return -ENOSYS; +} +#endif + +/*****************************************************************************/ +static inline void hisfc_check_version(struct hisfc_host *host) +{ + int version = hisfc_read(host->version); + + pr_info("hs-sfc controller version 0x%x\n", version); +} + +static int __devinit hisfc350_driver_probe(struct platform_device *pdev) +{ + struct device_node *np = pdev->dev.of_node; + struct device *dev = &pdev->dev; + struct hisfc_host *host; + struct hisfc_flash_device *flash; + struct resource *res; + int ret, tmp, cs; + + if (np) { + host = devm_kzalloc(&pdev->dev, sizeof(*host), GFP_KERNEL); + if (!host) { + pr_err("%s: ERROR: no memory", __func__); + ret = -ENOMEM; + goto err; + } + + platform_set_drvdata(pdev, host); + ret = hisfc_probe_config_dt(pdev, np); + if (ret) { + ret = -ENODEV; + dev_err(&pdev->dev, "no platform data\n"); + goto err_res; + } + } else { + ret = -ENODEV; + dev_err(&pdev->dev, "no device node found\n"); + goto err; + } + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!res) { + ret = -ENODEV; + goto err_res; + } + dev_dbg(dev, "res.start=0x%x, res.end=0x%x\n", res->start, res->end); + + host->io_base = devm_request_and_ioremap(&pdev->dev, res); + if (!host->io_base) { + ret = -EIO; + dev_err(&pdev->dev, "devm_request_and_ioremap fail\n"); + goto err_res; + } + + host->dev = dev; + host->buffer = dma_alloc_coherent(host->dev, HISFC350_DMA_MAX_SIZE, + &host->dma_buffer, GFP_KERNEL); + if (!host->buffer) { + pr_err("sfc alloc dma buffer failed.\n"); + goto err_res; + } + hisfc_check_version(host); +#if 0 + host->clk = devm_clk_get(&pdev->dev, NULL); + if (IS_ERR(dev->clk)) { + ret = PTR_ERR(dev->clk); + goto free; + } + + ret = clk_prepare_enable(host->clk); + if (ret) + goto err; +#endif + + mutex_init(&host->lock); + hisfc350_hw_init(host); + + /* loop for each serial nor-flash which is connected to sfc */ + for (cs = 0; cs < CONFIG_HISFC350_CHIP_NUM; cs++) { + ret = hisfc350_setup_flash_device(host, cs); + if (ret == -ENODEV) + continue; + else if (ret < 0) { + dev_err(host->dev, "cs%d setup failed!", cs); + goto err_setup; + } + } + + return 0; + +err_setup: + /* clean up for all nor flash */ + for (cs = 0; cs < CONFIG_HISFC350_CHIP_NUM; cs++) { + flash = host->flash[cs]; + if (!flash) + continue; + + /* clean up mtd stuff */ + tmp = mtd_device_unregister(&flash->mtd); + if (tmp) + dev_err(&pdev->dev, "error removing mtd\n"); + } + + if (host->buffer) + dma_free_coherent(host->dev, HISFC350_DMA_MAX_SIZE, + host->buffer, host->dma_buffer); + mutex_destroy(&host->lock); +err_res: + platform_set_drvdata(pdev, NULL); +err: + return ret; +} + +static int __devexit hisfc350_driver_remove(struct platform_device *pdev) +{ + struct hisfc_host *host = platform_get_drvdata(pdev); + struct hisfc_flash_device *flash; + int cs, ret; + + /* clean up for all nor flash */ + for (cs = 0; cs < CONFIG_HISFC350_CHIP_NUM; cs++) { + flash = host->flash[cs]; + if (!flash) + continue; + + /* clean up mtd stuff */ + ret = mtd_device_unregister(&flash->mtd); + if (ret) + dev_err(&pdev->dev, "error removing mtd\n"); + } + + if (host->buffer) + dma_free_coherent(host->dev, HISFC350_DMA_MAX_SIZE, + host->buffer, host->dma_buffer); + mutex_destroy(&host->lock); + + platform_set_drvdata(pdev, NULL); + + return 0; +} + +static void hisfc350_driver_shutdown(struct platform_device *pdev) +{ + /* after system reboot, bootmonitor will read the flash using + * 3-byte address mode. Usually flash-chip will not reset with + * the soc-chip, so we have to command the flash-chip into 3-byte + * mode if necessary + */ + struct hisfc_host *host = platform_get_drvdata(pdev); + struct hisfc_flash_device *flash; + int cs; + + /* clean up for all nor flash */ + for (cs = 0; cs < CONFIG_HISFC350_CHIP_NUM; cs++) { + flash = host->flash[cs]; + if (!flash) + continue; + if (flash->info.addr_width == 4) + hisfc350_flash_set_4byte(flash, 0); + } +} + +#ifdef CONFIG_OF +static const struct of_device_id sfc_id_table[] = { + { .compatible = "hs,sfc350" }, + {} +}; +MODULE_DEVICE_TABLE(of, sfc_id_table); +#endif + +static struct platform_driver hisfc350_driver_pltdrv = { + .probe = hisfc350_driver_probe, + .remove = hisfc350_driver_remove, + .shutdown = hisfc350_driver_shutdown, +#ifdef CONFIG_PM + .suspend = hisfc350_driver_suspend, + .resume = hisfc350_driver_resume, +#endif /* CONFIG_PM */ + + .driver = { + .name = DRIVER_NAME, + .owner = THIS_MODULE, +#ifdef CONFIG_OF + .of_match_table = of_match_ptr(sfc_id_table), +#endif + } +}; + +static int __init hisfc350_module_init(void) +{ + return platform_driver_register(&hisfc350_driver_pltdrv); +} +module_init(hisfc350_module_init); + +static void __exit hisfc350_module_exit(void) +{ + platform_driver_unregister(&hisfc350_driver_pltdrv); +} +module_exit(hisfc350_module_exit); + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("HiC"); +MODULE_DESCRIPTION("Hisilicon Spi Flash Controller V350 Device Driver, Version 1.00"); diff --git a/drivers/mtd/devices/hs_sfc/hisfc350.h b/drivers/mtd/devices/hs_sfc/hisfc350.h new file mode 100644 index 00000000000..bbfb692f90b --- /dev/null +++ b/drivers/mtd/devices/hs_sfc/hisfc350.h @@ -0,0 +1,191 @@ +/****************************************************************************** + * Copyright (c) 2009-2010 by Hisi. + * All rights reserved. + * *** + * Create by CCCC. 2010-12-16 + * + ******************************************************************************/ + +#ifndef HISFC350H +#define HISFC350H +/******************************************************************************/ +#include "flash_info.h" + +#define CONFIG_HISFC350_CHIP_NUM (2) +/*****************************************************************************/ +#define HISFC350_MAX_READY_WAIT_JIFFIES (40 * HZ) + +/*****************************************************************************/ +#define HISFC350_DMA_ALIGN_SIZE (256) +#define HISFC350_DMA_ALIGN_MASK (HISFC350_DMA_ALIGN_SIZE - 1) + +#define HISFC350_DMA_MAX_SIZE (4096) +#define HISFC350_DMA_MAX_MASK (HISFC350_DMA_MAX_SIZE - 1) + +/*****************************************************************************/ +#define HISFC350_GLOBAL_CONFIG 0x0100 +#define HISFC350_GLOBAL_CONFIG_READ_DELAY(n) (((n) & 0x03) << 3) +#define HISFC350_GLOBAL_CONFIG_ADDR_MODE_4B (1 << 2) +#define HISFC350_GLOBAL_CONFIG_WRITE_PROTECT (1 << 1) +#define HISFC350_GLOBAL_CONFIG_SPI_MODE3 (1 << 0) + +#define HISFC350_TIMING 0x0110 +#define HISFC350_TIMING_TSHSL(n) ((n) & 0xF) +#define HISFC350_TIMING_TCSS(n) (((n) & 0x7) << 8) +#define HISFC350_TIMING_TCSH(n) (((n) & 0x7) << 12) + +#define HISFC350_INT_RAW_STATUS 0x0120 +#define HISFC350_INT_RAW_STATUS_DMA_DONE (1 << 1) +#define HISFC350_INT_STATUS 0x0124 +#define HISFC350_INT_MASK 0x0128 +#define HISFC350_INT_CLEAR 0x012C +#define HISFC350_INT_CLEAR_DMA_DONE (1 << 1) + +#define HISFC350_BUS_CONFIG1 0x0200 +#define HISFC350_BUS_CONFIG1_READ_EN (1 << 31) +#define HISFC350_BUS_CONFIG1_WRITE_EN (1 << 30) +#define HISFC350_BUS_CONFIG1_WRITE_INS(n) ((n & 0xFF) << 22) +#define HISFC350_BUS_CONFIG1_WRITE_DUMMY_CNT(n) ((n & 0x7) << 19) +#define HISFC350_BUS_CONFIG1_WRITE_IF_TYPE(n) ((n & 0x7) << 16) +#define HISFC350_BUS_CONFIG1_READ_INS(n) ((n & 0xFF) << 8) +#define HISFC350_BUS_CONFIG1_READ_PREF_CNT(n) ((n & 0x3) << 6) +#define HISFC350_BUS_CONFIG1_READ_DUMMY_CNT(n) ((n & 0x7) << 3) +#define HISFC350_BUS_CONFIG1_READ_IF_TYPE(n) (n & 0x7) + +#define HISFC350_BUS_FLASH_SIZE 0x0210 +#define HISFC350_BUS_FLASH_SIZE_CS0_MASK 0x0F +#define HISFC350_BUS_FLASH_SIZE_CS1_MASK (0x0F << 8) +#define HISFC350_BUS_BASE_ADDR_CS0 0x0214 +#define HISFC350_BUS_BASE_ADDR_CS1 0x0218 +#define HISFC350_BUS_ALIAS_ADDR 0x021C +#define HISFC350_BUS_ALIAS_CS 0x0220 +#define HISFC350_BUS_DMA_CTRL 0x0240 +#define HISFC350_BUS_DMA_CTRL_START (1 << 0) +#define HISFC350_BUS_DMA_CTRL_RW(_rw) ((_rw) << 1) +#define HISFC350_BUS_DMA_CTRL_CS(_cs) (((_cs) & 0x01) << 4) + +#define HISFC350_BUS_DMA_MEM_SADDR 0x0244 +#define HISFC350_BUS_DMA_FLASH_SADDR 0x0248 +#define HISFC350_BUS_DMA_LEN 0x024C +#define HISFC350_BUS_DMA_LEN_DATA_CNT(n) ((n - 1) & 0x0FFFFFFF) +#define HISFC350_BUS_DMA_AHB_CTRL 0x0250 +#define HISFC350_BUS_DMA_AHB_CTRL_INCR4_EN (1 << 0) +#define HISFC350_BUS_DMA_AHB_CTRL_INCR8_EN (1 << 1) +#define HISFC350_BUS_DMA_AHB_CTRL_INCR16_EN (1 << 2) + +#define HISFC350_CMD_CONFIG 0x0300 +#define HISFC350_CMD_CONFIG_MEM_IF_TYPE(n) (((n) & 0x07) << 17) +#define HISFC350_CMD_CONFIG_DATA_CNT(n) (((n - 1) & 0x3F) << 9) +#define HISFC350_CMD_CONFIG_RW_READ (1 << 8) +#define HISFC350_CMD_CONFIG_DATA_EN (1 << 7) +#define HISFC350_CMD_CONFIG_DUMMY_CNT(n) (((n) & 0x07) << 4) +#define HISFC350_CMD_CONFIG_ADDR_EN (1 << 3) +#define HISFC350_CMD_CONFIG_SEL_CS(_cs) (((_cs) & 0x01) << 1) +#define HISFC350_CMD_CONFIG_START (1 << 0) + +#define HISFC350_CMD_INS 0x0308 +#define HISFC350_CMD_ADDR 0x030C +#define HISFC350_CMD_ADDR_MASK 0x3FFFFFFF +#define HISFC350_CMD_DATABUF0 0x0400 +#define HISFC350_CMD_DATABUF15 0x043C + +/* hw iftype definition */ +#define HISFC350_IF_HW_STD 0 +#define HISFC350_IF_HW_DUAL 1 +#define HISFC350_IF_HW_DUAL_ADDR 2 +#define HISFC350_IF_HW_DUAL_CMD 3 +#define HISFC350_IF_HW_QUAD 5 +#define HISFC350_IF_HW_QUAD_ADDR 6 +#define HISFC350_IF_HW_QUAD_CMD 7 + +#define HISFC350_REG_BUF_SIZE \ + (HISFC350_CMD_DATABUF15 - HISFC350_CMD_DATABUF0 + 0x04) + +#undef READ +#define READ 1 + +#undef WRITE +#define WRITE 0 + +#undef FALSE +#define FALSE 0 + +#undef TRUE +#define TRUE 1 + +/*****************************************************************************/ +#define HISFC350_VERSION (0x01F8) + +enum { + addr_mode_3byte = 0, + addr_mode_4byte = 1, +}; + +struct hisfc_flash_device { + struct mtd_info mtd; + int cs; + int addr_mode; + + void *priv; /* host */ + struct flash_info info; +}; + +struct hisfc_host { + void __iomem *io_base; + int version;/* version reg */ + + struct clk *clk; + struct device *dev; + + struct mutex lock; + int cs;/* current selected cs */ + int addr_mode;/* current host work_mode */ + + char *buffer; + dma_addr_t dma_buffer;/* dma addr of buffer */ + + struct hisfc_flash_device *flash[CONFIG_HISFC350_CHIP_NUM]; +}; + +/*****************************************************************************/ +#define hisfc_read(reg) readl(host->io_base + reg) + +#define hisfc_write(reg, val) writel(val, host->io_base + reg) + +#define HISFC350_CMD_WAIT_CPU_FINISH(_host) do {\ + unsigned int timeout = 0x10000000; \ + while (((hisfc_read(HISFC350_CMD_CONFIG) \ + & HISFC350_CMD_CONFIG_START)) && timeout)\ + --timeout; \ + if (!timeout) \ + DBG_BUG("cmd wait cpu finish timeout\n"); \ +} while (0) + +#define HISFC350_DMA_WAIT_CPU_FINISH(_host) do {\ + unsigned int timeout = 0x10000000; \ + while (((hisfc_read(HISFC350_BUS_DMA_CTRL) \ + & HISFC350_BUS_DMA_CTRL_START)) && timeout) {\ + --timeout; cond_resched(); } \ + if (!timeout) \ + DBG_BUG("dma wait cpu finish timeout\n");\ +} while (0) + +/*****************************************************************************/ +#if 0 +# define DBG_MSG(_fmt, arg...) +#else +# define DBG_MSG(_fmt, arg...) \ + pr_info("%s(%d): " _fmt, __FILE__, __LINE__, ##arg); +#endif + +#define DBG_WARN(_fmt, arg...) \ + pr_info("%s(%d): " _fmt, __FILE__, __LINE__, ##arg); + +#define DBG_BUG(fmt, args...) do {\ + pr_err("%s(%d): BUG: " fmt, __FILE__, __LINE__, ##args); \ + while (1)\ + ;\ +} while (0) + +/******************************************************************************/ +#endif /* HISFC350H */ |