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 + -
显示快捷键?