onenand_base.c

来自「linux 内核源代码」· C语言 代码 · 共 2,493 行 · 第 1/5 页

C
2,493
字号
		return 0;	}	onenand_do_lock_cmd(mtd, 0x0, this->chipsize, ONENAND_CMD_UNLOCK);	return 0;}#ifdef CONFIG_MTD_ONENAND_OTP/* Interal OTP operation */typedef int (*otp_op_t)(struct mtd_info *mtd, loff_t form, size_t len,		size_t *retlen, u_char *buf);/** * do_otp_read - [DEFAULT] Read OTP block area * @param mtd		MTD device structure * @param from		The offset to read * @param len		number of bytes to read * @param retlen	pointer to variable to store the number of readbytes * @param buf		the databuffer to put/get data * * Read OTP block area. */static int do_otp_read(struct mtd_info *mtd, loff_t from, size_t len,		size_t *retlen, u_char *buf){	struct onenand_chip *this = mtd->priv;	struct mtd_oob_ops ops = {		.len	= len,		.ooblen	= 0,		.datbuf	= buf,		.oobbuf	= NULL,	};	int ret;	/* Enter OTP access mode */	this->command(mtd, ONENAND_CMD_OTP_ACCESS, 0, 0);	this->wait(mtd, FL_OTPING);	ret = onenand_read_ops_nolock(mtd, from, &ops);	/* Exit OTP access mode */	this->command(mtd, ONENAND_CMD_RESET, 0, 0);	this->wait(mtd, FL_RESETING);	return ret;}/** * do_otp_write - [DEFAULT] Write OTP block area * @param mtd		MTD device structure * @param to		The offset to write * @param len		number of bytes to write * @param retlen	pointer to variable to store the number of write bytes * @param buf		the databuffer to put/get data * * Write OTP block area. */static int do_otp_write(struct mtd_info *mtd, loff_t to, size_t len,		size_t *retlen, u_char *buf){	struct onenand_chip *this = mtd->priv;	unsigned char *pbuf = buf;	int ret;	struct mtd_oob_ops ops;	/* Force buffer page aligned */	if (len < mtd->writesize) {		memcpy(this->page_buf, buf, len);		memset(this->page_buf + len, 0xff, mtd->writesize - len);		pbuf = this->page_buf;		len = mtd->writesize;	}	/* Enter OTP access mode */	this->command(mtd, ONENAND_CMD_OTP_ACCESS, 0, 0);	this->wait(mtd, FL_OTPING);	ops.len = len;	ops.ooblen = 0;	ops.datbuf = pbuf;	ops.oobbuf = NULL;	ret = onenand_write_ops_nolock(mtd, to, &ops);	*retlen = ops.retlen;	/* Exit OTP access mode */	this->command(mtd, ONENAND_CMD_RESET, 0, 0);	this->wait(mtd, FL_RESETING);	return ret;}/** * do_otp_lock - [DEFAULT] Lock OTP block area * @param mtd		MTD device structure * @param from		The offset to lock * @param len		number of bytes to lock * @param retlen	pointer to variable to store the number of lock bytes * @param buf		the databuffer to put/get data * * Lock OTP block area. */static int do_otp_lock(struct mtd_info *mtd, loff_t from, size_t len,		size_t *retlen, u_char *buf){	struct onenand_chip *this = mtd->priv;	struct mtd_oob_ops ops = {		.mode = MTD_OOB_PLACE,		.ooblen = len,		.oobbuf = buf,		.ooboffs = 0,	};	int ret;	/* Enter OTP access mode */	this->command(mtd, ONENAND_CMD_OTP_ACCESS, 0, 0);	this->wait(mtd, FL_OTPING);	ret = onenand_write_oob_nolock(mtd, from, &ops);	*retlen = ops.oobretlen;	/* Exit OTP access mode */	this->command(mtd, ONENAND_CMD_RESET, 0, 0);	this->wait(mtd, FL_RESETING);	return ret;}/** * onenand_otp_walk - [DEFAULT] Handle OTP operation * @param mtd		MTD device structure * @param from		The offset to read/write * @param len		number of bytes to read/write * @param retlen	pointer to variable to store the number of read bytes * @param buf		the databuffer to put/get data * @param action	do given action * @param mode		specify user and factory * * Handle OTP operation. */static int onenand_otp_walk(struct mtd_info *mtd, loff_t from, size_t len,			size_t *retlen, u_char *buf,			otp_op_t action, int mode){	struct onenand_chip *this = mtd->priv;	int otp_pages;	int density;	int ret = 0;	*retlen = 0;	density = this->device_id >> ONENAND_DEVICE_DENSITY_SHIFT;	if (density < ONENAND_DEVICE_DENSITY_512Mb)		otp_pages = 20;	else		otp_pages = 10;	if (mode == MTD_OTP_FACTORY) {		from += mtd->writesize * otp_pages;		otp_pages = 64 - otp_pages;	}	/* Check User/Factory boundary */	if (((mtd->writesize * otp_pages) - (from + len)) < 0)		return 0;	onenand_get_device(mtd, FL_OTPING);	while (len > 0 && otp_pages > 0) {		if (!action) {	/* OTP Info functions */			struct otp_info *otpinfo;			len -= sizeof(struct otp_info);			if (len <= 0) {				ret = -ENOSPC;				break;			}			otpinfo = (struct otp_info *) buf;			otpinfo->start = from;			otpinfo->length = mtd->writesize;			otpinfo->locked = 0;			from += mtd->writesize;			buf += sizeof(struct otp_info);			*retlen += sizeof(struct otp_info);		} else {			size_t tmp_retlen;			int size = len;			ret = action(mtd, from, len, &tmp_retlen, buf);			buf += size;			len -= size;			*retlen += size;			if (ret)				break;		}		otp_pages--;	}	onenand_release_device(mtd);	return ret;}/** * onenand_get_fact_prot_info - [MTD Interface] Read factory OTP info * @param mtd		MTD device structure * @param buf		the databuffer to put/get data * @param len		number of bytes to read * * Read factory OTP info. */static int onenand_get_fact_prot_info(struct mtd_info *mtd,			struct otp_info *buf, size_t len){	size_t retlen;	int ret;	ret = onenand_otp_walk(mtd, 0, len, &retlen, (u_char *) buf, NULL, MTD_OTP_FACTORY);	return ret ? : retlen;}/** * onenand_read_fact_prot_reg - [MTD Interface] Read factory OTP area * @param mtd		MTD device structure * @param from		The offset to read * @param len		number of bytes to read * @param retlen	pointer to variable to store the number of read bytes * @param buf		the databuffer to put/get data * * Read factory OTP area. */static int onenand_read_fact_prot_reg(struct mtd_info *mtd, loff_t from,			size_t len, size_t *retlen, u_char *buf){	return onenand_otp_walk(mtd, from, len, retlen, buf, do_otp_read, MTD_OTP_FACTORY);}/** * onenand_get_user_prot_info - [MTD Interface] Read user OTP info * @param mtd		MTD device structure * @param buf		the databuffer to put/get data * @param len		number of bytes to read * * Read user OTP info. */static int onenand_get_user_prot_info(struct mtd_info *mtd,			struct otp_info *buf, size_t len){	size_t retlen;	int ret;	ret = onenand_otp_walk(mtd, 0, len, &retlen, (u_char *) buf, NULL, MTD_OTP_USER);	return ret ? : retlen;}/** * onenand_read_user_prot_reg - [MTD Interface] Read user OTP area * @param mtd		MTD device structure * @param from		The offset to read * @param len		number of bytes to read * @param retlen	pointer to variable to store the number of read bytes * @param buf		the databuffer to put/get data * * Read user OTP area. */static int onenand_read_user_prot_reg(struct mtd_info *mtd, loff_t from,			size_t len, size_t *retlen, u_char *buf){	return onenand_otp_walk(mtd, from, len, retlen, buf, do_otp_read, MTD_OTP_USER);}/** * onenand_write_user_prot_reg - [MTD Interface] Write user OTP area * @param mtd		MTD device structure * @param from		The offset to write * @param len		number of bytes to write * @param retlen	pointer to variable to store the number of write bytes * @param buf		the databuffer to put/get data * * Write user OTP area. */static int onenand_write_user_prot_reg(struct mtd_info *mtd, loff_t from,			size_t len, size_t *retlen, u_char *buf){	return onenand_otp_walk(mtd, from, len, retlen, buf, do_otp_write, MTD_OTP_USER);}/** * onenand_lock_user_prot_reg - [MTD Interface] Lock user OTP area * @param mtd		MTD device structure * @param from		The offset to lock * @param len		number of bytes to unlock * * Write lock mark on spare area in page 0 in OTP block */static int onenand_lock_user_prot_reg(struct mtd_info *mtd, loff_t from,			size_t len){	unsigned char oob_buf[64];	size_t retlen;	int ret;	memset(oob_buf, 0xff, mtd->oobsize);	/*	 * Note: OTP lock operation	 *       OTP block : 0xXXFC	 *       1st block : 0xXXF3 (If chip support)	 *       Both      : 0xXXF0 (If chip support)	 */	oob_buf[ONENAND_OTP_LOCK_OFFSET] = 0xFC;	/*	 * Write lock mark to 8th word of sector0 of page0 of the spare0.	 * We write 16 bytes spare area instead of 2 bytes.	 */	from = 0;	len = 16;	ret = onenand_otp_walk(mtd, from, len, &retlen, oob_buf, do_otp_lock, MTD_OTP_USER);	return ret ? : retlen;}#endif	/* CONFIG_MTD_ONENAND_OTP *//** * onenand_check_features - Check and set OneNAND features * @param mtd		MTD data structure * * Check and set OneNAND features * - lock scheme * - two plane */static void onenand_check_features(struct mtd_info *mtd){	struct onenand_chip *this = mtd->priv;	unsigned int density, process;	/* Lock scheme depends on density and process */	density = this->device_id >> ONENAND_DEVICE_DENSITY_SHIFT;	process = this->version_id >> ONENAND_VERSION_PROCESS_SHIFT;	/* Lock scheme */	switch (density) {	case ONENAND_DEVICE_DENSITY_4Gb:		this->options |= ONENAND_HAS_2PLANE;	case ONENAND_DEVICE_DENSITY_2Gb:		/* 2Gb DDP don't have 2 plane */		if (!ONENAND_IS_DDP(this))			this->options |= ONENAND_HAS_2PLANE;		this->options |= ONENAND_HAS_UNLOCK_ALL;	case ONENAND_DEVICE_DENSITY_1Gb:		/* A-Die has all block unlock */		if (process)			this->options |= ONENAND_HAS_UNLOCK_ALL;		break;	default:		/* Some OneNAND has continuous lock scheme */		if (!process)			this->options |= ONENAND_HAS_CONT_LOCK;		break;	}	if (this->options & ONENAND_HAS_CONT_LOCK)		printk(KERN_DEBUG "Lock scheme is Continuous Lock\n");	if (this->options & ONENAND_HAS_UNLOCK_ALL)		printk(KERN_DEBUG "Chip support all block unlock\n");	if (this->options & ONENAND_HAS_2PLANE)		printk(KERN_DEBUG "Chip has 2 plane\n");}/** * onenand_print_device_info - Print device & version ID * @param device        device ID * @param version	version ID * * Print device & version ID */static void onenand_print_device_info(int device, int version){        int vcc, demuxed, ddp, density;        vcc = device & ONENAND_DEVICE_VCC_MASK;        demuxed = device & ONENAND_DEVICE_IS_DEMUX;        ddp = device & ONENAND_DEVICE_IS_DDP;        density = device >> ONENAND_DEVICE_DENSITY_SHIFT;        printk(KERN_INFO "%sOneNAND%s %dMB %sV 16-bit (0x%02x)\n",                demuxed ? "" : "Muxed ",                ddp ? "(DDP)" : "",                (16 << density),                vcc ? "2.65/3.3" : "1.8",                device);	printk(KERN_INFO "OneNAND version = 0x%04x\n", version);}static const struct onenand_manufacturers onenand_manuf_ids[] = {        {ONENAND_MFR_SAMSUNG, "Samsung"},};/** * onenand_check_maf - Check manufacturer ID * @param manuf         manufacturer ID * * Check manufacturer ID */static int onenand_check_maf(int manuf){	int size = ARRAY_SIZE(onenand_manuf_ids);	char *name;        int i;	for (i = 0; i < size; i++)                if (manuf == onenand_manuf_ids[i].id)                        break;	if (i < size)		name = onenand_manuf_ids[i].name;	else		name = "Unknown";	printk(KERN_DEBUG "OneNAND Manufacturer: %s (0x%0x)\n", name, manuf);	return (i == size);}/** * onenand_probe - [OneNAND Interface] Probe the OneNAND device * @param mtd		MTD device structure * * OneNAND detection method: *   Compare the values from command with ones from register */static int onenand_probe(struct mtd_info *mtd){	struct onenand_chip *this = mtd->priv;	int bram_maf_id, bram_dev_id, maf_id, dev_id, ver_id;	int density;	int syscfg;	/* Save system configuration 1 */	syscfg = this->read_word(this->base + ONENAND_REG_SYS_CFG1);	/* Clear Sync. Burst Read mode to read BootRAM */	this->write_word((syscfg & ~ONENAND_SYS_CFG1_SYNC_READ), this->base + ONENAND_REG_SYS_CFG1);	/* Send the command for reading device ID from BootRAM */	this->write_word(ONENAND_CMD_READID, this->base + ONENAND_BOOTRAM);	/* Read manufacturer and device IDs from BootRAM */	bram_maf_id = this->read_word(this->base + ONENAND_BOOTRAM + 0x0);	bram_dev_id = this->read_word(this->base + ONENAND_BOOTRAM + 0x2);	/* Reset OneNAND to read default register values */	this->write_word(ONENAND_CMD_RESET, this->base + ONENAND_BOOTRAM);	/* Wait reset */	this->wait(mtd, FL_RESETING);	/* Restore system configuration 1 */	this->write_word(syscfg, this->base + ONENAND_REG_SYS_CFG1);	/* Check manufacturer ID */	if (onenand_check_maf(bram_maf_id))		return -ENXIO;	/* Read manufacturer and device IDs from Register */	maf_id = this->read_word(this->base + ONENAND_REG_MANUFACTURER_ID);	dev_id = this->read_word(this->base + ONENAND_REG_DEVICE_ID);	ver_id = this->read_word(this->base + ONENAND_REG_VERSION_ID);	/* Check OneNAND device */	if (maf_id != bram_maf_id || dev_id != bram_dev_id)		return -ENXIO;	/* Flash device information */	onenand_print_device_info(dev_id, ver_id);	this->device_id = dev_id;	this->version_id = ver_id;	density = dev_id >> ONENAND_DEVICE_DENSITY_SHIFT;	this->chipsize = (16 << density) << 20;	/* Set density mask. it is used for DDP */	if (ONENAND_IS_DDP(this))		this->density_mask = (1 << (density + 6));	else		this->density_mask = 0;	/* OneNAND page size & block size */	/* The data buffer size is equal to page size */	mtd->writesize = this->read_word(this->base + ONENAND_REG

⌨️ 快捷键说明

复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?