Files

586 lines
18 KiB
C
Executable File

/*
* spinand.c- Sigmastar
*
* Copyright (C) 2018 Sigmastar Technology Corp.
*
* Author: edie.chen <edie.chen@sigmastar.com.tw>
*
* This software is licensed under the terms of the GNU General Public
* License version 2, as published by the Free Software Foundation, and
* may be copied, distributed, and modified under those terms.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
*/
#include <common.h>
#include <malloc.h>
#include <linux/err.h>
//#include <linux/compat.h>
#include <linux/mtd/mtd.h>
#include <linux/mtd/nand.h>
#include <linux/mtd/nand_ecc.h>
#include <asm/io.h>
#include <asm/errno.h>
#include "MsCommon.h"
#include "MsTypes.h"
#include "spinand.h"
#define NAND_USE_FLASH_BBT 0x00010000
#define NAND_BBT_BLOCK_NUM 4
/* SPI NAND messages */
// #define FPGA
static U32 u32_CurRow = 0;
static U32 u32_CurCol = 0;
/* These really don't belong here, as they are specific to the NAND Model */
static uint8_t scan_ff_pattern[] = {0xff};
/* struct nand_bbt_descr - bad block table descriptor */
static struct nand_bbt_descr spi_nand_bbt_descr = {
.options = NAND_BBT_2BIT | NAND_BBT_LASTBLOCK | NAND_BBT_VERSION | NAND_BBT_CREATE | NAND_BBT_WRITE,
.offs = 0,
.len = 1,
.pattern = scan_ff_pattern
};
static struct nand_ecclayout spi_nand_oobinfo = {
.eccbytes = 32,
.eccpos = {8, 9, 10, 11, 12, 13, 14, 15,
24, 25, 26, 27, 28, 29, 30, 31,
40, 41, 42, 43, 44, 45, 46, 47,
56, 57, 58, 59, 60, 61, 62, 63},
.oobavail = 30,
.oobfree = {
{2, 6},
{16, 8},
{32, 8},
{48, 8},
{0, 0}
},
};
static uint8_t bbt_pattern[] = {'B', 'b', 't', '0' };
static uint8_t mirror_pattern[] = {'1', 't', 'b', 'B' };
static struct nand_bbt_descr spi_nand_bbt_main_descr = {
.options = NAND_BBT_LASTBLOCK | NAND_BBT_CREATE | NAND_BBT_WRITE |
NAND_BBT_2BIT | NAND_BBT_VERSION | NAND_BBT_PERCHIP,
.offs = 1,
.len = 3,
.veroffs = 4,
.maxblocks = NAND_BBT_BLOCK_NUM,
.pattern = bbt_pattern
};
static struct nand_bbt_descr spi_nand_bbt_mirror_descr = {
.options = NAND_BBT_LASTBLOCK | NAND_BBT_CREATE | NAND_BBT_WRITE |
NAND_BBT_2BIT | NAND_BBT_VERSION | NAND_BBT_PERCHIP,
.offs = 1,
.len = 3,
.veroffs = 4,
.maxblocks = NAND_BBT_BLOCK_NUM,
.pattern = mirror_pattern
};
SPI_NAND_DRIVER_t gtSpiNandDrv;
void *drvSPINAND_get_DrvContext_address(void) // exposed API
{
return &gtSpiNandDrv;
}
U8 MDrv_SPINAND_CountBits(U32 u32_x)
{
U8 u8_i = 0;
while (u32_x) {
u8_i++;
u32_x >>= 1;
}
return u8_i-1;
}
uint8_t spi_nand_read_byte(struct mtd_info *mtd)
{
u8 u8_word;
SPI_NAND_DRIVER_t *pSpiNandDrv = (SPI_NAND_DRIVER_t*)drvSPINAND_get_DrvContext_address();
spi_nand_debug(" spi_nand_read_byte \n");
u8_word = pSpiNandDrv->pu8_sparebuf[pSpiNandDrv->u32_column];
if (pSpiNandDrv->u8_statusRequest)
{
/*If write protect, the status will be 0x80. Normal will return 0x0. It revert wiht P_NAND. */
/*See function nand_check_wp in nand_base.c */
u8_word = ~(*(pSpiNandDrv->pu8_statusbuf));
}
pSpiNandDrv->u32_column++;
return u8_word;
}
u16 spi_nand_read_word(struct mtd_info *mtd)
{
u16 u16_word;
SPI_NAND_DRIVER_t *pSpiNandDrv = (SPI_NAND_DRIVER_t*)drvSPINAND_get_DrvContext_address();
u16_word = ((u16)pSpiNandDrv->pu8_sparebuf[pSpiNandDrv->u32_column] | ((u16)pSpiNandDrv->pu8_sparebuf[pSpiNandDrv->u32_column+1]<<8));
pSpiNandDrv->u32_column += 2;
return u16_word;
}
void spi_nand_write_buf(struct mtd_info *mtd, const uint8_t *buf, int len)
{
spi_nand_debug("spi_nand_write_buf Not support");
}
void spi_nand_read_buf(struct mtd_info *mtd, uint8_t *buf, int len)
{
spi_nand_debug("spi_nand_read_buf Not support");
}
// int spi_nand_verify_buf(struct mtd_info *mtd, const uint8_t *buf, int len)
// {
// spi_nand_msg("not support");
// return 0;
// }
void spi_nand_select_chip(struct mtd_info *mtd, int chip)
{
spi_nand_debug("spi_nand_select_chip Not support");
}
void spi_nand_cmd_ctrl(struct mtd_info *mtd, int dat, unsigned int ctrl)
{
spi_nand_debug("spi_nand_cmd_ctrl Not support");
}
int spi_nand_dev_ready(struct mtd_info *mtd)
{
spi_nand_debug("spi_nand_dev_ready Not support");
return 1;
}
void spi_nand_cmdfunc(struct mtd_info *mtd, unsigned command, int column, int page_addr)
{
U32 ret;
int u32Ret;
SPI_NAND_DRIVER_t *pSpiNandDrv = (SPI_NAND_DRIVER_t*)drvSPINAND_get_DrvContext_address();
pSpiNandDrv->u8_statusRequest = FALSE;
// U8 *pu8_statusbuf = pSpiNandDrv->pu8_statusbuf;
switch (command) {
case NAND_CMD_STATUS:
spi_nand_debug("NAND_CMD_STATUS");
pSpiNandDrv->u8_statusRequest = TRUE;
u32Ret = MDrv_SPINAND_ReadStatusRegister(pSpiNandDrv->pu8_statusbuf, SPI_NAND_REG_PROT);
if (u32Ret != ERR_SPINAND_SUCCESS)
spi_nand_debug(" ReadStatusRegister != ERR_SPINAND_SUCCESS~! %d \n", u32Ret);
break;
case NAND_CMD_PAGEPROG:
spi_nand_debug("NAND_CMD_PAGEPROG");
break;
case NAND_CMD_READOOB:
spi_nand_debug("NAND_CMD_READOOB");
u32Ret=MDrv_SPINAND_Read(page_addr, (U8 *)pSpiNandDrv->pu8_pagebuf, (U8 *)pSpiNandDrv->pu8_sparebuf);
pSpiNandDrv->u32_column = column;
if (u32Ret != ERR_SPINAND_SUCCESS)
spi_nand_debug(" MDrv_SPINAND_Read != ERR_SPINAND_SUCCESS~! %d \n", u32Ret);
break;
case NAND_CMD_READID:
spi_nand_debug("NAND_CMD_READID");
if(MDrv_SPINAND_Init(&(pSpiNandDrv->tSpinandInfo)) != TRUE)
{
spi_nand_err("MDrv_SPINAND_Init fail");
}
printf("todo:%s\n", __func__);
memcpy((void *)pSpiNandDrv->pu8_sparebuf, (const void *)pSpiNandDrv->tSpinandInfo.au8_ID,SPINAND_ID_SIZE);
break;
case NAND_CMD_ERASE2:
spi_nand_debug("NAND_CMD_ERASE2");
break;
case NAND_CMD_ERASE1:
spi_nand_debug("NAND_CMD_ERASE1, page_addr: 0x%x\n", page_addr);
pSpiNandDrv->u8_status = NAND_STATUS_READY|NAND_STATUS_TRUE_READY;
ret = MDrv_SPINAND_BLOCK_ERASE(page_addr);
if(ret != ERR_SPINAND_SUCCESS)
{
spi_nand_err("MDrv_SPINAND_Erase=%x", ret);
pSpiNandDrv->u8_status |= NAND_STATUS_FAIL;
}
break;
case NAND_CMD_READ0:
spi_nand_debug("NAND_CMD_READ0");
u32_CurRow = page_addr;
u32_CurCol = column;
break;
case NAND_CMD_SEQIN:
spi_nand_debug("NAND_CMD_SEQIN");
/* send PAGE_PROG command(0x1080) */
u32_CurRow = page_addr;
u32_CurCol = column;
break;
case NAND_CMD_RESET:
spi_nand_debug("NAND_CMD_RESET");
break;
default:
spi_nand_err("unsupported command %02Xh\n", command);
break;
}
return;
}
int spi_nand_waitfunc(struct mtd_info *mtd, struct nand_chip *this)
{
SPI_NAND_DRIVER_t *pSpiNandDrv = (SPI_NAND_DRIVER_t*)drvSPINAND_get_DrvContext_address();
spi_nand_debug("spi_nand_waitfunc");
return (int)pSpiNandDrv->u8_status;
}
/**
* nand_write_page - write one page
* @mtd: MTD device structure
* @chip: NAND chip descriptor
* @buf: the data to write
* @oob_required: must write chip->oob_poi to OOB
* @page: page number to write
* @cached: cached programming
* @raw: use _raw version of write_page
*/
//int spi_nand_write_page(struct mtd_info *mtd,
// struct nand_chip *chip, const uint8_t *buf,
// int page, int cached, int raw)
int spi_nand_write_page(struct mtd_info *mtd, struct nand_chip *chip,
uint32_t offset, int data_len,
const uint8_t *buf, int oob_required,
int page, int cached, int raw)
{
U32 ret;
U8 *pSparebuf = NULL;
SPI_NAND_DRIVER_t *pSpiNandDrv = (SPI_NAND_DRIVER_t*)drvSPINAND_get_DrvContext_address();
// U8 *pu8_MainBuf = pSpiNandDrv->pu8_pagebuf;
// U8 *pu8_SpareBuf = pSpiNandDrv->pu8_sparebuf;
spi_nand_debug("spi_nand_write_page 0x%x", page);
if(oob_required)
{
pSparebuf = pSpiNandDrv->pu8_sparebuf;
}
ret = MDrv_SPINAND_Write(page,(U8*)buf, pSparebuf);
if(ret != ERR_SPINAND_SUCCESS)
{
spi_nand_err("MDrv_SPINAND_Write=%x\n", ret);
return -EIO;
}
return 0;
}
void spi_nand_ecc_hwctl(struct mtd_info *mtd, int mode)
{
spi_nand_debug(" spi_nand_ecc_hwctl Not support");
}
int spi_nand_ecc_calculate(struct mtd_info *mtd, const uint8_t *dat, uint8_t *ecc_code)
{
spi_nand_debug("spi_nand_ecc_calculate Not support");
return 0;
}
int spi_nand_ecc_correct(struct mtd_info *mtd, uint8_t *dat, uint8_t *read_ecc, uint8_t *calc_ecc)
{
spi_nand_debug(" spi_nand_ecc_correct Not support");
return 0;
}
int spi_nand_ecc_read_page_raw(struct mtd_info *mtd, struct nand_chip *chip,
uint8_t *buf, int oob_required, int page)
{
U32 ret;
ret = MDrv_SPINAND_Read(page, (U8 *)buf, (U8 *)chip->oob_poi);
if(ret == ECC_NOT_CORRECTED)
{
spi_nand_err("MDrv_SPINAND_Read=%x, P: 0x%x", ret, page);
}
if((ret != ERR_SPINAND_SUCCESS) && (ret != ECC_NOT_CORRECTED))
{
mtd->ecc_stats.corrected += 1;
}
return 0;
}
int spi_nand_ecc_write_page_raw(struct mtd_info *mtd, struct nand_chip *chip, const uint8_t *buf, int oob_required)
{
spi_nand_msg("spi_nand_ecc_write_page_raw NOT support");
return 0;
}
int spi_nand_ecc_read_page(struct mtd_info *mtd, struct nand_chip *chip,
uint8_t *buf, int oob_required, int page)
{
U32 ret;
ret = MDrv_SPINAND_Read(page, (U8 *)buf, (U8 *)chip->oob_poi);
if(ret == ECC_NOT_CORRECTED)
{
mtd->ecc_stats.failed++;
spi_nand_err("MDrv_SPINAND_Read=%x, P: 0x%x", ret, page);
}
if((ret != ERR_SPINAND_SUCCESS) && (ret != ECC_NOT_CORRECTED))
{
mtd->ecc_stats.corrected += 1;
}
return 0;
}
int spi_nand_ecc_read_subpage(struct mtd_info *mtd, struct nand_chip *chip,
uint32_t offs, uint32_t len, uint8_t *buf, int page)
{
U32 ret = 0;
SPI_NAND_DRIVER_t *pSpiNandDrv = (SPI_NAND_DRIVER_t*)drvSPINAND_get_DrvContext_address();
if(offs + len > pSpiNandDrv->tSpinandInfo.u16_PageByteCnt){
spi_nand_err("read len exceeds page len\n");
return -EINVAL;
}
buf += offs;
ret = MDrv_SPINAND_Read_RandomIn(u32_CurRow, offs, len, buf);
if(ret == ECC_NOT_CORRECTED)
{
mtd->ecc_stats.failed++;
spi_nand_err("Read_RandomIn=%x, P: 0x%x", ret, page);
}
if((ret != ERR_SPINAND_SUCCESS) && (ret != ECC_NOT_CORRECTED))
{
mtd->ecc_stats.corrected += 1;
}
else
return -EIO;
return 0;
}
int spi_nand_ecc_write_page(struct mtd_info *mtd, struct nand_chip *chip, const uint8_t *buf, int oob_required)
{
spi_nand_msg("spi_nand_ecc_write_page Not support");
return 0;
}
int spi_nand_ecc_read_oob(struct mtd_info *mtd, struct nand_chip *chip, int page)
{
U32 ret;
SPI_NAND_DRIVER_t *pSpiNandDrv = (SPI_NAND_DRIVER_t*)drvSPINAND_get_DrvContext_address();
ret = MDrv_SPINAND_Read(page, (U8 *)pSpiNandDrv->pu8_pagebuf, (U8 *)chip->oob_poi);
if((ret == ERR_SPINAND_ECC_ERROR))
{
spi_nand_err("MDrv_SPINAND_Read=%x", ret);
}
return 0;
}
int spi_nand_ecc_write_oob(struct mtd_info *mtd, struct nand_chip *chip, int page)
{
U32 ret;
spi_nand_msg("spi_nand_ecc_write_oob\n");
SPI_NAND_DRIVER_t *pSpiNandDrv = (SPI_NAND_DRIVER_t*)drvSPINAND_get_DrvContext_address();
memset((void *)pSpiNandDrv->pu8_pagebuf, 0xFF, mtd->writesize);
ret = MDrv_SPINAND_Write(page, (U8 *)pSpiNandDrv->pu8_pagebuf, (U8 *)chip->oob_poi);
if(ret != ERR_SPINAND_SUCCESS)
{
spi_nand_err("MDrv_SPINAND_Write=%x", ret);
return -EIO;
}
return 0;
}
static U32 CheckSum(U8 *pu8_Data, U16 u16_ByteCnt)
{
U32 u32_Sum = 0;
while (u16_ByteCnt--)
u32_Sum += *pu8_Data++;
return u32_Sum;
}
void setNandInformation(void)
{
SPI_NAND_DRIVER_t *pSpiNandDrv = (SPI_NAND_DRIVER_t*)drvSPINAND_get_DrvContext_address();
pSpiNandDrv->u8_SectorByteCntBits = MDrv_SPINAND_CountBits(pSpiNandDrv->tSpinandInfo.u16_SectorByteCnt);
pSpiNandDrv->u16_PageSectorCnt = pSpiNandDrv->tSpinandInfo.u16_PageByteCnt >> pSpiNandDrv->u8_SectorByteCntBits;
pSpiNandDrv->u8_PageSectorCntBits = MDrv_SPINAND_CountBits(pSpiNandDrv->u16_PageSectorCnt);
pSpiNandDrv->u8_PageByteCntBits = MDrv_SPINAND_CountBits(pSpiNandDrv->tSpinandInfo.u16_PageByteCnt);
pSpiNandDrv->u8_BlkPageCntBits = MDrv_SPINAND_CountBits(pSpiNandDrv->tSpinandInfo.u16_BlkPageCnt);
pSpiNandDrv->u8_status = NAND_STATUS_READY|NAND_STATUS_TRUE_READY;
pSpiNandDrv->u32_column = 0;
pSpiNandDrv->pu8_pagebuf = kmalloc(pSpiNandDrv->tSpinandInfo.u16_PageByteCnt, GFP_KERNEL);
pSpiNandDrv->pu8_sparebuf = kmalloc(pSpiNandDrv->tSpinandInfo.u16_SpareByteCnt, GFP_KERNEL);
pSpiNandDrv->pu8_statusbuf = kmalloc(16, GFP_KERNEL);
}
//void dumpNandInformation(void)
//{
// SPI_NAND_DRIVER_t *pSpiNandDrv = (SPI_NAND_DRIVER_t*)drvSPINAND_get_DrvContext_address();
// spi_nand_debug("Bytes / Page : %d\n", pSpiNandDrv->tSpinandInfo.u16_PageByteCnt);
// spi_nand_debug("Pages / Block: %d\n", pSpiNandDrv->tSpinandInfo.u16_BlkPageCnt);
// spi_nand_debug("Sector/ Page : %d\n", pSpiNandDrv->u16_PageSectorCnt);
// spi_nand_debug("Spare / Page : %d\n", pSpiNandDrv->tSpinandInfo.u16_SpareByteCnt);
//}
/*
* Board-specific NAND initialization.
* - hwcontrol: hardwarespecific function for accesing control-lines
* - dev_ready: hardwarespecific function for accesing device ready/busy line
* - eccmode: mode of ecc, see defines
*/
int board_nand_init(struct nand_chip *nand)
{
U8 u8_i;
const U8 NEXT_CIS = 2;//next cis interval
U32 u32_ret;
// struct mtd_info *mtd;
PARTITION_INFO_t *ptPartInfo;
int partinfoAddr = 1; //Partition Information is in the page 1
SPI_NAND_DRIVER_t *pSpiNandDrv = (SPI_NAND_DRIVER_t*)drvSPINAND_get_DrvContext_address();
if(MDrv_SPINAND_Init(&(pSpiNandDrv->tSpinandInfo)) != TRUE)
{
spi_nand_debug("MDrv_SPINAND_Init fail");
return -1;
}
setNandInformation();
// dumpNandInformation();
if(!pSpiNandDrv->pu8_pagebuf || !pSpiNandDrv->pu8_sparebuf)
{
spi_nand_err("Can not alloc memory for page/spare buffer");
return -1;
}
spi_nand_debug("%s %d todo: check get partition table\n",__func__, __LINE__);
ptPartInfo = (PARTITION_INFO_t *)pSpiNandDrv->pu8_pagebuf;
for(u8_i = 0 ; u8_i < CIS_MAX_BACKUP * NEXT_CIS ; u8_i += NEXT_CIS)
{
u32_ret = MDrv_SPINAND_Read(u8_i*pSpiNandDrv->tSpinandInfo.u16_BlkPageCnt, pSpiNandDrv->pu8_pagebuf, pSpiNandDrv->pu8_sparebuf);
if(u32_ret != ERR_SPINAND_ECC_ERROR)
{
if(memcmp((const void *) pSpiNandDrv->pu8_pagebuf, SPINAND_FLASH_INFO_TAG, 16) == 0)
{
//Partition Information is in the page 1
u32_ret = MDrv_SPINAND_Read((u8_i * pSpiNandDrv->tSpinandInfo.u16_BlkPageCnt) + partinfoAddr, pSpiNandDrv->pu8_pagebuf, pSpiNandDrv->pu8_sparebuf);
if(u32_ret != ERR_SPINAND_ECC_ERROR)
{
if(ptPartInfo->u32_ChkSum == CheckSum((u8*)&(ptPartInfo->u16_SpareByteCnt), 0x200 - 0x04))
break;
}
}
}
else
{
spi_nand_err("Can't search CIS in no_block: %d",u8_i);
}
}
if(u8_i == CIS_MAX_BACKUP * NEXT_CIS)
{
spi_nand_err("CIS doesn't contain part info");
pSpiNandDrv->u8_HasPNI = 0;
}
else
{
spi_nand_err("CIS contains part info");
pSpiNandDrv->u8_HasPNI = 1;
memcpy((void *)&pSpiNandDrv->tPartInfo, (const void *) ptPartInfo, 0x200);
}
/* please refer to include/linux/nand.h for more info. */
nand->read_byte = spi_nand_read_byte;
nand->read_word = spi_nand_read_word;
nand->write_buf = spi_nand_write_buf;
nand->read_buf = spi_nand_read_buf;
/* nand->verify_buf = spi_nand_verify_buf;*/
nand->select_chip = spi_nand_select_chip;
nand->cmd_ctrl = spi_nand_cmd_ctrl;
nand->dev_ready = spi_nand_dev_ready;
nand->cmdfunc = spi_nand_cmdfunc;
nand->waitfunc = spi_nand_waitfunc;
nand->write_page = spi_nand_write_page;
nand->scan_bbt = nand_default_bbt;
nand->options = NAND_USE_FLASH_BBT;
nand->bits_per_cell = 1;
nand->chip_delay = 0;
nand->badblock_pattern = &spi_nand_bbt_descr; //using default badblock pattern.
nand->bbt_td = &spi_nand_bbt_main_descr;
nand->bbt_md = &spi_nand_bbt_mirror_descr;
/*nand->require_randomizer = 0;
nand->ecc_corretable_bit = 8;
nand->ecc_bitflip_threshold = 2;
*/
nand->ecc.mode = NAND_ECC_HW;
nand->ecc.size = pSpiNandDrv->tSpinandInfo.u16_PageByteCnt;
nand->ecc.bytes = (pSpiNandDrv->tSpinandInfo.u16_SpareByteCnt>>1);
// nand->ecc.sectors = 4;
nand->ecc.layout = &spi_nand_oobinfo;
nand->ecc.hwctl = spi_nand_ecc_hwctl;
nand->ecc.calculate = spi_nand_ecc_calculate;
nand->ecc.correct = spi_nand_ecc_correct;
nand->ecc.read_page_raw = spi_nand_ecc_read_page_raw;
nand->ecc.write_page_raw = spi_nand_ecc_write_page_raw;
nand->ecc.read_page = spi_nand_ecc_read_page;
nand->ecc.read_subpage = spi_nand_ecc_read_subpage;
nand->ecc.write_page = spi_nand_ecc_write_page;
nand->ecc.read_oob = spi_nand_ecc_read_oob;
nand->ecc.write_oob = spi_nand_ecc_write_oob;
// mtd->_read_oob = spi_nand_ecc_read_oob;
spi_nand_debug("board_SPI_nand_init end");
return 0;
}
void dumpInformation(void)
{
SPI_NAND_DRIVER_t *pSpiNandDrv = (SPI_NAND_DRIVER_t*)drvSPINAND_get_DrvContext_address();
printk("u8_SectorByteCntBits %d\n", pSpiNandDrv->u8_SectorByteCntBits);
printk("u16_PageSectorCnt %d\n", pSpiNandDrv->u16_PageSectorCnt);
printk("u8_PageSectorCntBits %d\n", pSpiNandDrv->u8_PageSectorCntBits);
printk("u8_PageByteCntBits %d\n", pSpiNandDrv->u8_PageByteCntBits);
printk("u8_BlkPageCntBits %d\n", pSpiNandDrv->u8_BlkPageCntBits);
printk("pSpiNandDrv->tSpinandInfo.u16_PageByteCnt %d\n", pSpiNandDrv->tSpinandInfo.u16_PageByteCnt);
printk("pSpiNandDrv->tSpinandInfo.u16_BlkPageCnt %d\n", pSpiNandDrv->tSpinandInfo.u16_BlkPageCnt);
printk("pSpiNandDrv->tSpinandInfo.u16_SpareByteCnt %d\n", pSpiNandDrv->tSpinandInfo.u16_SpareByteCnt);
printk("au8_ID[0]=%x ", pSpiNandDrv->tSpinandInfo.au8_ID[0]);
printk("au8_ID[1]=%x ", pSpiNandDrv->tSpinandInfo.au8_ID[1]);
printk("\n");
}