18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * Handles the M-Systems DiskOnChip G3 chip 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * Copyright (C) 2011 Robert Jarzmik 68c2ecf20Sopenharmony_ci */ 78c2ecf20Sopenharmony_ci 88c2ecf20Sopenharmony_ci#include <linux/kernel.h> 98c2ecf20Sopenharmony_ci#include <linux/module.h> 108c2ecf20Sopenharmony_ci#include <linux/errno.h> 118c2ecf20Sopenharmony_ci#include <linux/of.h> 128c2ecf20Sopenharmony_ci#include <linux/platform_device.h> 138c2ecf20Sopenharmony_ci#include <linux/string.h> 148c2ecf20Sopenharmony_ci#include <linux/slab.h> 158c2ecf20Sopenharmony_ci#include <linux/io.h> 168c2ecf20Sopenharmony_ci#include <linux/delay.h> 178c2ecf20Sopenharmony_ci#include <linux/mtd/mtd.h> 188c2ecf20Sopenharmony_ci#include <linux/mtd/partitions.h> 198c2ecf20Sopenharmony_ci#include <linux/bitmap.h> 208c2ecf20Sopenharmony_ci#include <linux/bitrev.h> 218c2ecf20Sopenharmony_ci#include <linux/bch.h> 228c2ecf20Sopenharmony_ci 238c2ecf20Sopenharmony_ci#include <linux/debugfs.h> 248c2ecf20Sopenharmony_ci#include <linux/seq_file.h> 258c2ecf20Sopenharmony_ci 268c2ecf20Sopenharmony_ci#define CREATE_TRACE_POINTS 278c2ecf20Sopenharmony_ci#include "docg3.h" 288c2ecf20Sopenharmony_ci 298c2ecf20Sopenharmony_ci/* 308c2ecf20Sopenharmony_ci * This driver handles the DiskOnChip G3 flash memory. 318c2ecf20Sopenharmony_ci * 328c2ecf20Sopenharmony_ci * As no specification is available from M-Systems/Sandisk, this drivers lacks 338c2ecf20Sopenharmony_ci * several functions available on the chip, as : 348c2ecf20Sopenharmony_ci * - IPL write 358c2ecf20Sopenharmony_ci * 368c2ecf20Sopenharmony_ci * The bus data width (8bits versus 16bits) is not handled (if_cfg flag), and 378c2ecf20Sopenharmony_ci * the driver assumes a 16bits data bus. 388c2ecf20Sopenharmony_ci * 398c2ecf20Sopenharmony_ci * DocG3 relies on 2 ECC algorithms, which are handled in hardware : 408c2ecf20Sopenharmony_ci * - a 1 byte Hamming code stored in the OOB for each page 418c2ecf20Sopenharmony_ci * - a 7 bytes BCH code stored in the OOB for each page 428c2ecf20Sopenharmony_ci * The BCH ECC is : 438c2ecf20Sopenharmony_ci * - BCH is in GF(2^14) 448c2ecf20Sopenharmony_ci * - BCH is over data of 520 bytes (512 page + 7 page_info bytes 458c2ecf20Sopenharmony_ci * + 1 hamming byte) 468c2ecf20Sopenharmony_ci * - BCH can correct up to 4 bits (t = 4) 478c2ecf20Sopenharmony_ci * - BCH syndroms are calculated in hardware, and checked in hardware as well 488c2ecf20Sopenharmony_ci * 498c2ecf20Sopenharmony_ci */ 508c2ecf20Sopenharmony_ci 518c2ecf20Sopenharmony_cistatic unsigned int reliable_mode; 528c2ecf20Sopenharmony_cimodule_param(reliable_mode, uint, 0); 538c2ecf20Sopenharmony_ciMODULE_PARM_DESC(reliable_mode, "Set the docg3 mode (0=normal MLC, 1=fast, " 548c2ecf20Sopenharmony_ci "2=reliable) : MLC normal operations are in normal mode"); 558c2ecf20Sopenharmony_ci 568c2ecf20Sopenharmony_cistatic int docg3_ooblayout_ecc(struct mtd_info *mtd, int section, 578c2ecf20Sopenharmony_ci struct mtd_oob_region *oobregion) 588c2ecf20Sopenharmony_ci{ 598c2ecf20Sopenharmony_ci if (section) 608c2ecf20Sopenharmony_ci return -ERANGE; 618c2ecf20Sopenharmony_ci 628c2ecf20Sopenharmony_ci /* byte 7 is Hamming ECC, byte 8-14 are BCH ECC */ 638c2ecf20Sopenharmony_ci oobregion->offset = 7; 648c2ecf20Sopenharmony_ci oobregion->length = 8; 658c2ecf20Sopenharmony_ci 668c2ecf20Sopenharmony_ci return 0; 678c2ecf20Sopenharmony_ci} 688c2ecf20Sopenharmony_ci 698c2ecf20Sopenharmony_cistatic int docg3_ooblayout_free(struct mtd_info *mtd, int section, 708c2ecf20Sopenharmony_ci struct mtd_oob_region *oobregion) 718c2ecf20Sopenharmony_ci{ 728c2ecf20Sopenharmony_ci if (section > 1) 738c2ecf20Sopenharmony_ci return -ERANGE; 748c2ecf20Sopenharmony_ci 758c2ecf20Sopenharmony_ci /* free bytes: byte 0 until byte 6, byte 15 */ 768c2ecf20Sopenharmony_ci if (!section) { 778c2ecf20Sopenharmony_ci oobregion->offset = 0; 788c2ecf20Sopenharmony_ci oobregion->length = 7; 798c2ecf20Sopenharmony_ci } else { 808c2ecf20Sopenharmony_ci oobregion->offset = 15; 818c2ecf20Sopenharmony_ci oobregion->length = 1; 828c2ecf20Sopenharmony_ci } 838c2ecf20Sopenharmony_ci 848c2ecf20Sopenharmony_ci return 0; 858c2ecf20Sopenharmony_ci} 868c2ecf20Sopenharmony_ci 878c2ecf20Sopenharmony_cistatic const struct mtd_ooblayout_ops nand_ooblayout_docg3_ops = { 888c2ecf20Sopenharmony_ci .ecc = docg3_ooblayout_ecc, 898c2ecf20Sopenharmony_ci .free = docg3_ooblayout_free, 908c2ecf20Sopenharmony_ci}; 918c2ecf20Sopenharmony_ci 928c2ecf20Sopenharmony_cistatic inline u8 doc_readb(struct docg3 *docg3, u16 reg) 938c2ecf20Sopenharmony_ci{ 948c2ecf20Sopenharmony_ci u8 val = readb(docg3->cascade->base + reg); 958c2ecf20Sopenharmony_ci 968c2ecf20Sopenharmony_ci trace_docg3_io(0, 8, reg, (int)val); 978c2ecf20Sopenharmony_ci return val; 988c2ecf20Sopenharmony_ci} 998c2ecf20Sopenharmony_ci 1008c2ecf20Sopenharmony_cistatic inline u16 doc_readw(struct docg3 *docg3, u16 reg) 1018c2ecf20Sopenharmony_ci{ 1028c2ecf20Sopenharmony_ci u16 val = readw(docg3->cascade->base + reg); 1038c2ecf20Sopenharmony_ci 1048c2ecf20Sopenharmony_ci trace_docg3_io(0, 16, reg, (int)val); 1058c2ecf20Sopenharmony_ci return val; 1068c2ecf20Sopenharmony_ci} 1078c2ecf20Sopenharmony_ci 1088c2ecf20Sopenharmony_cistatic inline void doc_writeb(struct docg3 *docg3, u8 val, u16 reg) 1098c2ecf20Sopenharmony_ci{ 1108c2ecf20Sopenharmony_ci writeb(val, docg3->cascade->base + reg); 1118c2ecf20Sopenharmony_ci trace_docg3_io(1, 8, reg, val); 1128c2ecf20Sopenharmony_ci} 1138c2ecf20Sopenharmony_ci 1148c2ecf20Sopenharmony_cistatic inline void doc_writew(struct docg3 *docg3, u16 val, u16 reg) 1158c2ecf20Sopenharmony_ci{ 1168c2ecf20Sopenharmony_ci writew(val, docg3->cascade->base + reg); 1178c2ecf20Sopenharmony_ci trace_docg3_io(1, 16, reg, val); 1188c2ecf20Sopenharmony_ci} 1198c2ecf20Sopenharmony_ci 1208c2ecf20Sopenharmony_cistatic inline void doc_flash_command(struct docg3 *docg3, u8 cmd) 1218c2ecf20Sopenharmony_ci{ 1228c2ecf20Sopenharmony_ci doc_writeb(docg3, cmd, DOC_FLASHCOMMAND); 1238c2ecf20Sopenharmony_ci} 1248c2ecf20Sopenharmony_ci 1258c2ecf20Sopenharmony_cistatic inline void doc_flash_sequence(struct docg3 *docg3, u8 seq) 1268c2ecf20Sopenharmony_ci{ 1278c2ecf20Sopenharmony_ci doc_writeb(docg3, seq, DOC_FLASHSEQUENCE); 1288c2ecf20Sopenharmony_ci} 1298c2ecf20Sopenharmony_ci 1308c2ecf20Sopenharmony_cistatic inline void doc_flash_address(struct docg3 *docg3, u8 addr) 1318c2ecf20Sopenharmony_ci{ 1328c2ecf20Sopenharmony_ci doc_writeb(docg3, addr, DOC_FLASHADDRESS); 1338c2ecf20Sopenharmony_ci} 1348c2ecf20Sopenharmony_ci 1358c2ecf20Sopenharmony_cistatic char const * const part_probes[] = { "cmdlinepart", "saftlpart", NULL }; 1368c2ecf20Sopenharmony_ci 1378c2ecf20Sopenharmony_cistatic int doc_register_readb(struct docg3 *docg3, int reg) 1388c2ecf20Sopenharmony_ci{ 1398c2ecf20Sopenharmony_ci u8 val; 1408c2ecf20Sopenharmony_ci 1418c2ecf20Sopenharmony_ci doc_writew(docg3, reg, DOC_READADDRESS); 1428c2ecf20Sopenharmony_ci val = doc_readb(docg3, reg); 1438c2ecf20Sopenharmony_ci doc_vdbg("Read register %04x : %02x\n", reg, val); 1448c2ecf20Sopenharmony_ci return val; 1458c2ecf20Sopenharmony_ci} 1468c2ecf20Sopenharmony_ci 1478c2ecf20Sopenharmony_cistatic int doc_register_readw(struct docg3 *docg3, int reg) 1488c2ecf20Sopenharmony_ci{ 1498c2ecf20Sopenharmony_ci u16 val; 1508c2ecf20Sopenharmony_ci 1518c2ecf20Sopenharmony_ci doc_writew(docg3, reg, DOC_READADDRESS); 1528c2ecf20Sopenharmony_ci val = doc_readw(docg3, reg); 1538c2ecf20Sopenharmony_ci doc_vdbg("Read register %04x : %04x\n", reg, val); 1548c2ecf20Sopenharmony_ci return val; 1558c2ecf20Sopenharmony_ci} 1568c2ecf20Sopenharmony_ci 1578c2ecf20Sopenharmony_ci/** 1588c2ecf20Sopenharmony_ci * doc_delay - delay docg3 operations 1598c2ecf20Sopenharmony_ci * @docg3: the device 1608c2ecf20Sopenharmony_ci * @nbNOPs: the number of NOPs to issue 1618c2ecf20Sopenharmony_ci * 1628c2ecf20Sopenharmony_ci * As no specification is available, the right timings between chip commands are 1638c2ecf20Sopenharmony_ci * unknown. The only available piece of information are the observed nops on a 1648c2ecf20Sopenharmony_ci * working docg3 chip. 1658c2ecf20Sopenharmony_ci * Therefore, doc_delay relies on a busy loop of NOPs, instead of scheduler 1668c2ecf20Sopenharmony_ci * friendlier msleep() functions or blocking mdelay(). 1678c2ecf20Sopenharmony_ci */ 1688c2ecf20Sopenharmony_cistatic void doc_delay(struct docg3 *docg3, int nbNOPs) 1698c2ecf20Sopenharmony_ci{ 1708c2ecf20Sopenharmony_ci int i; 1718c2ecf20Sopenharmony_ci 1728c2ecf20Sopenharmony_ci doc_vdbg("NOP x %d\n", nbNOPs); 1738c2ecf20Sopenharmony_ci for (i = 0; i < nbNOPs; i++) 1748c2ecf20Sopenharmony_ci doc_writeb(docg3, 0, DOC_NOP); 1758c2ecf20Sopenharmony_ci} 1768c2ecf20Sopenharmony_ci 1778c2ecf20Sopenharmony_cistatic int is_prot_seq_error(struct docg3 *docg3) 1788c2ecf20Sopenharmony_ci{ 1798c2ecf20Sopenharmony_ci int ctrl; 1808c2ecf20Sopenharmony_ci 1818c2ecf20Sopenharmony_ci ctrl = doc_register_readb(docg3, DOC_FLASHCONTROL); 1828c2ecf20Sopenharmony_ci return ctrl & (DOC_CTRL_PROTECTION_ERROR | DOC_CTRL_SEQUENCE_ERROR); 1838c2ecf20Sopenharmony_ci} 1848c2ecf20Sopenharmony_ci 1858c2ecf20Sopenharmony_cistatic int doc_is_ready(struct docg3 *docg3) 1868c2ecf20Sopenharmony_ci{ 1878c2ecf20Sopenharmony_ci int ctrl; 1888c2ecf20Sopenharmony_ci 1898c2ecf20Sopenharmony_ci ctrl = doc_register_readb(docg3, DOC_FLASHCONTROL); 1908c2ecf20Sopenharmony_ci return ctrl & DOC_CTRL_FLASHREADY; 1918c2ecf20Sopenharmony_ci} 1928c2ecf20Sopenharmony_ci 1938c2ecf20Sopenharmony_cistatic int doc_wait_ready(struct docg3 *docg3) 1948c2ecf20Sopenharmony_ci{ 1958c2ecf20Sopenharmony_ci int maxWaitCycles = 100; 1968c2ecf20Sopenharmony_ci 1978c2ecf20Sopenharmony_ci do { 1988c2ecf20Sopenharmony_ci doc_delay(docg3, 4); 1998c2ecf20Sopenharmony_ci cpu_relax(); 2008c2ecf20Sopenharmony_ci } while (!doc_is_ready(docg3) && maxWaitCycles--); 2018c2ecf20Sopenharmony_ci doc_delay(docg3, 2); 2028c2ecf20Sopenharmony_ci if (maxWaitCycles > 0) 2038c2ecf20Sopenharmony_ci return 0; 2048c2ecf20Sopenharmony_ci else 2058c2ecf20Sopenharmony_ci return -EIO; 2068c2ecf20Sopenharmony_ci} 2078c2ecf20Sopenharmony_ci 2088c2ecf20Sopenharmony_cistatic int doc_reset_seq(struct docg3 *docg3) 2098c2ecf20Sopenharmony_ci{ 2108c2ecf20Sopenharmony_ci int ret; 2118c2ecf20Sopenharmony_ci 2128c2ecf20Sopenharmony_ci doc_writeb(docg3, 0x10, DOC_FLASHCONTROL); 2138c2ecf20Sopenharmony_ci doc_flash_sequence(docg3, DOC_SEQ_RESET); 2148c2ecf20Sopenharmony_ci doc_flash_command(docg3, DOC_CMD_RESET); 2158c2ecf20Sopenharmony_ci doc_delay(docg3, 2); 2168c2ecf20Sopenharmony_ci ret = doc_wait_ready(docg3); 2178c2ecf20Sopenharmony_ci 2188c2ecf20Sopenharmony_ci doc_dbg("doc_reset_seq() -> isReady=%s\n", ret ? "false" : "true"); 2198c2ecf20Sopenharmony_ci return ret; 2208c2ecf20Sopenharmony_ci} 2218c2ecf20Sopenharmony_ci 2228c2ecf20Sopenharmony_ci/** 2238c2ecf20Sopenharmony_ci * doc_read_data_area - Read data from data area 2248c2ecf20Sopenharmony_ci * @docg3: the device 2258c2ecf20Sopenharmony_ci * @buf: the buffer to fill in (might be NULL is dummy reads) 2268c2ecf20Sopenharmony_ci * @len: the length to read 2278c2ecf20Sopenharmony_ci * @first: first time read, DOC_READADDRESS should be set 2288c2ecf20Sopenharmony_ci * 2298c2ecf20Sopenharmony_ci * Reads bytes from flash data. Handles the single byte / even bytes reads. 2308c2ecf20Sopenharmony_ci */ 2318c2ecf20Sopenharmony_cistatic void doc_read_data_area(struct docg3 *docg3, void *buf, int len, 2328c2ecf20Sopenharmony_ci int first) 2338c2ecf20Sopenharmony_ci{ 2348c2ecf20Sopenharmony_ci int i, cdr, len4; 2358c2ecf20Sopenharmony_ci u16 data16, *dst16; 2368c2ecf20Sopenharmony_ci u8 data8, *dst8; 2378c2ecf20Sopenharmony_ci 2388c2ecf20Sopenharmony_ci doc_dbg("doc_read_data_area(buf=%p, len=%d)\n", buf, len); 2398c2ecf20Sopenharmony_ci cdr = len & 0x1; 2408c2ecf20Sopenharmony_ci len4 = len - cdr; 2418c2ecf20Sopenharmony_ci 2428c2ecf20Sopenharmony_ci if (first) 2438c2ecf20Sopenharmony_ci doc_writew(docg3, DOC_IOSPACE_DATA, DOC_READADDRESS); 2448c2ecf20Sopenharmony_ci dst16 = buf; 2458c2ecf20Sopenharmony_ci for (i = 0; i < len4; i += 2) { 2468c2ecf20Sopenharmony_ci data16 = doc_readw(docg3, DOC_IOSPACE_DATA); 2478c2ecf20Sopenharmony_ci if (dst16) { 2488c2ecf20Sopenharmony_ci *dst16 = data16; 2498c2ecf20Sopenharmony_ci dst16++; 2508c2ecf20Sopenharmony_ci } 2518c2ecf20Sopenharmony_ci } 2528c2ecf20Sopenharmony_ci 2538c2ecf20Sopenharmony_ci if (cdr) { 2548c2ecf20Sopenharmony_ci doc_writew(docg3, DOC_IOSPACE_DATA | DOC_READADDR_ONE_BYTE, 2558c2ecf20Sopenharmony_ci DOC_READADDRESS); 2568c2ecf20Sopenharmony_ci doc_delay(docg3, 1); 2578c2ecf20Sopenharmony_ci dst8 = (u8 *)dst16; 2588c2ecf20Sopenharmony_ci for (i = 0; i < cdr; i++) { 2598c2ecf20Sopenharmony_ci data8 = doc_readb(docg3, DOC_IOSPACE_DATA); 2608c2ecf20Sopenharmony_ci if (dst8) { 2618c2ecf20Sopenharmony_ci *dst8 = data8; 2628c2ecf20Sopenharmony_ci dst8++; 2638c2ecf20Sopenharmony_ci } 2648c2ecf20Sopenharmony_ci } 2658c2ecf20Sopenharmony_ci } 2668c2ecf20Sopenharmony_ci} 2678c2ecf20Sopenharmony_ci 2688c2ecf20Sopenharmony_ci/** 2698c2ecf20Sopenharmony_ci * doc_write_data_area - Write data into data area 2708c2ecf20Sopenharmony_ci * @docg3: the device 2718c2ecf20Sopenharmony_ci * @buf: the buffer to get input bytes from 2728c2ecf20Sopenharmony_ci * @len: the length to write 2738c2ecf20Sopenharmony_ci * 2748c2ecf20Sopenharmony_ci * Writes bytes into flash data. Handles the single byte / even bytes writes. 2758c2ecf20Sopenharmony_ci */ 2768c2ecf20Sopenharmony_cistatic void doc_write_data_area(struct docg3 *docg3, const void *buf, int len) 2778c2ecf20Sopenharmony_ci{ 2788c2ecf20Sopenharmony_ci int i, cdr, len4; 2798c2ecf20Sopenharmony_ci u16 *src16; 2808c2ecf20Sopenharmony_ci u8 *src8; 2818c2ecf20Sopenharmony_ci 2828c2ecf20Sopenharmony_ci doc_dbg("doc_write_data_area(buf=%p, len=%d)\n", buf, len); 2838c2ecf20Sopenharmony_ci cdr = len & 0x3; 2848c2ecf20Sopenharmony_ci len4 = len - cdr; 2858c2ecf20Sopenharmony_ci 2868c2ecf20Sopenharmony_ci doc_writew(docg3, DOC_IOSPACE_DATA, DOC_READADDRESS); 2878c2ecf20Sopenharmony_ci src16 = (u16 *)buf; 2888c2ecf20Sopenharmony_ci for (i = 0; i < len4; i += 2) { 2898c2ecf20Sopenharmony_ci doc_writew(docg3, *src16, DOC_IOSPACE_DATA); 2908c2ecf20Sopenharmony_ci src16++; 2918c2ecf20Sopenharmony_ci } 2928c2ecf20Sopenharmony_ci 2938c2ecf20Sopenharmony_ci src8 = (u8 *)src16; 2948c2ecf20Sopenharmony_ci for (i = 0; i < cdr; i++) { 2958c2ecf20Sopenharmony_ci doc_writew(docg3, DOC_IOSPACE_DATA | DOC_READADDR_ONE_BYTE, 2968c2ecf20Sopenharmony_ci DOC_READADDRESS); 2978c2ecf20Sopenharmony_ci doc_writeb(docg3, *src8, DOC_IOSPACE_DATA); 2988c2ecf20Sopenharmony_ci src8++; 2998c2ecf20Sopenharmony_ci } 3008c2ecf20Sopenharmony_ci} 3018c2ecf20Sopenharmony_ci 3028c2ecf20Sopenharmony_ci/** 3038c2ecf20Sopenharmony_ci * doc_set_data_mode - Sets the flash to normal or reliable data mode 3048c2ecf20Sopenharmony_ci * @docg3: the device 3058c2ecf20Sopenharmony_ci * 3068c2ecf20Sopenharmony_ci * The reliable data mode is a bit slower than the fast mode, but less errors 3078c2ecf20Sopenharmony_ci * occur. Entering the reliable mode cannot be done without entering the fast 3088c2ecf20Sopenharmony_ci * mode first. 3098c2ecf20Sopenharmony_ci * 3108c2ecf20Sopenharmony_ci * In reliable mode, pages 2*n and 2*n+1 are clones. Writing to page 0 of blocks 3118c2ecf20Sopenharmony_ci * (4,5) make the hardware write also to page 1 of blocks blocks(4,5). Reading 3128c2ecf20Sopenharmony_ci * from page 0 of blocks (4,5) or from page 1 of blocks (4,5) gives the same 3138c2ecf20Sopenharmony_ci * result, which is a logical and between bytes from page 0 and page 1 (which is 3148c2ecf20Sopenharmony_ci * consistent with the fact that writing to a page is _clearing_ bits of that 3158c2ecf20Sopenharmony_ci * page). 3168c2ecf20Sopenharmony_ci */ 3178c2ecf20Sopenharmony_cistatic void doc_set_reliable_mode(struct docg3 *docg3) 3188c2ecf20Sopenharmony_ci{ 3198c2ecf20Sopenharmony_ci static char *strmode[] = { "normal", "fast", "reliable", "invalid" }; 3208c2ecf20Sopenharmony_ci 3218c2ecf20Sopenharmony_ci doc_dbg("doc_set_reliable_mode(%s)\n", strmode[docg3->reliable]); 3228c2ecf20Sopenharmony_ci switch (docg3->reliable) { 3238c2ecf20Sopenharmony_ci case 0: 3248c2ecf20Sopenharmony_ci break; 3258c2ecf20Sopenharmony_ci case 1: 3268c2ecf20Sopenharmony_ci doc_flash_sequence(docg3, DOC_SEQ_SET_FASTMODE); 3278c2ecf20Sopenharmony_ci doc_flash_command(docg3, DOC_CMD_FAST_MODE); 3288c2ecf20Sopenharmony_ci break; 3298c2ecf20Sopenharmony_ci case 2: 3308c2ecf20Sopenharmony_ci doc_flash_sequence(docg3, DOC_SEQ_SET_RELIABLEMODE); 3318c2ecf20Sopenharmony_ci doc_flash_command(docg3, DOC_CMD_FAST_MODE); 3328c2ecf20Sopenharmony_ci doc_flash_command(docg3, DOC_CMD_RELIABLE_MODE); 3338c2ecf20Sopenharmony_ci break; 3348c2ecf20Sopenharmony_ci default: 3358c2ecf20Sopenharmony_ci doc_err("doc_set_reliable_mode(): invalid mode\n"); 3368c2ecf20Sopenharmony_ci break; 3378c2ecf20Sopenharmony_ci } 3388c2ecf20Sopenharmony_ci doc_delay(docg3, 2); 3398c2ecf20Sopenharmony_ci} 3408c2ecf20Sopenharmony_ci 3418c2ecf20Sopenharmony_ci/** 3428c2ecf20Sopenharmony_ci * doc_set_asic_mode - Set the ASIC mode 3438c2ecf20Sopenharmony_ci * @docg3: the device 3448c2ecf20Sopenharmony_ci * @mode: the mode 3458c2ecf20Sopenharmony_ci * 3468c2ecf20Sopenharmony_ci * The ASIC can work in 3 modes : 3478c2ecf20Sopenharmony_ci * - RESET: all registers are zeroed 3488c2ecf20Sopenharmony_ci * - NORMAL: receives and handles commands 3498c2ecf20Sopenharmony_ci * - POWERDOWN: minimal poweruse, flash parts shut off 3508c2ecf20Sopenharmony_ci */ 3518c2ecf20Sopenharmony_cistatic void doc_set_asic_mode(struct docg3 *docg3, u8 mode) 3528c2ecf20Sopenharmony_ci{ 3538c2ecf20Sopenharmony_ci int i; 3548c2ecf20Sopenharmony_ci 3558c2ecf20Sopenharmony_ci for (i = 0; i < 12; i++) 3568c2ecf20Sopenharmony_ci doc_readb(docg3, DOC_IOSPACE_IPL); 3578c2ecf20Sopenharmony_ci 3588c2ecf20Sopenharmony_ci mode |= DOC_ASICMODE_MDWREN; 3598c2ecf20Sopenharmony_ci doc_dbg("doc_set_asic_mode(%02x)\n", mode); 3608c2ecf20Sopenharmony_ci doc_writeb(docg3, mode, DOC_ASICMODE); 3618c2ecf20Sopenharmony_ci doc_writeb(docg3, ~mode, DOC_ASICMODECONFIRM); 3628c2ecf20Sopenharmony_ci doc_delay(docg3, 1); 3638c2ecf20Sopenharmony_ci} 3648c2ecf20Sopenharmony_ci 3658c2ecf20Sopenharmony_ci/** 3668c2ecf20Sopenharmony_ci * doc_set_device_id - Sets the devices id for cascaded G3 chips 3678c2ecf20Sopenharmony_ci * @docg3: the device 3688c2ecf20Sopenharmony_ci * @id: the chip to select (amongst 0, 1, 2, 3) 3698c2ecf20Sopenharmony_ci * 3708c2ecf20Sopenharmony_ci * There can be 4 cascaded G3 chips. This function selects the one which will 3718c2ecf20Sopenharmony_ci * should be the active one. 3728c2ecf20Sopenharmony_ci */ 3738c2ecf20Sopenharmony_cistatic void doc_set_device_id(struct docg3 *docg3, int id) 3748c2ecf20Sopenharmony_ci{ 3758c2ecf20Sopenharmony_ci u8 ctrl; 3768c2ecf20Sopenharmony_ci 3778c2ecf20Sopenharmony_ci doc_dbg("doc_set_device_id(%d)\n", id); 3788c2ecf20Sopenharmony_ci doc_writeb(docg3, id, DOC_DEVICESELECT); 3798c2ecf20Sopenharmony_ci ctrl = doc_register_readb(docg3, DOC_FLASHCONTROL); 3808c2ecf20Sopenharmony_ci 3818c2ecf20Sopenharmony_ci ctrl &= ~DOC_CTRL_VIOLATION; 3828c2ecf20Sopenharmony_ci ctrl |= DOC_CTRL_CE; 3838c2ecf20Sopenharmony_ci doc_writeb(docg3, ctrl, DOC_FLASHCONTROL); 3848c2ecf20Sopenharmony_ci} 3858c2ecf20Sopenharmony_ci 3868c2ecf20Sopenharmony_ci/** 3878c2ecf20Sopenharmony_ci * doc_set_extra_page_mode - Change flash page layout 3888c2ecf20Sopenharmony_ci * @docg3: the device 3898c2ecf20Sopenharmony_ci * 3908c2ecf20Sopenharmony_ci * Normally, the flash page is split into the data (512 bytes) and the out of 3918c2ecf20Sopenharmony_ci * band data (16 bytes). For each, 4 more bytes can be accessed, where the wear 3928c2ecf20Sopenharmony_ci * leveling counters are stored. To access this last area of 4 bytes, a special 3938c2ecf20Sopenharmony_ci * mode must be input to the flash ASIC. 3948c2ecf20Sopenharmony_ci * 3958c2ecf20Sopenharmony_ci * Returns 0 if no error occurred, -EIO else. 3968c2ecf20Sopenharmony_ci */ 3978c2ecf20Sopenharmony_cistatic int doc_set_extra_page_mode(struct docg3 *docg3) 3988c2ecf20Sopenharmony_ci{ 3998c2ecf20Sopenharmony_ci int fctrl; 4008c2ecf20Sopenharmony_ci 4018c2ecf20Sopenharmony_ci doc_dbg("doc_set_extra_page_mode()\n"); 4028c2ecf20Sopenharmony_ci doc_flash_sequence(docg3, DOC_SEQ_PAGE_SIZE_532); 4038c2ecf20Sopenharmony_ci doc_flash_command(docg3, DOC_CMD_PAGE_SIZE_532); 4048c2ecf20Sopenharmony_ci doc_delay(docg3, 2); 4058c2ecf20Sopenharmony_ci 4068c2ecf20Sopenharmony_ci fctrl = doc_register_readb(docg3, DOC_FLASHCONTROL); 4078c2ecf20Sopenharmony_ci if (fctrl & (DOC_CTRL_PROTECTION_ERROR | DOC_CTRL_SEQUENCE_ERROR)) 4088c2ecf20Sopenharmony_ci return -EIO; 4098c2ecf20Sopenharmony_ci else 4108c2ecf20Sopenharmony_ci return 0; 4118c2ecf20Sopenharmony_ci} 4128c2ecf20Sopenharmony_ci 4138c2ecf20Sopenharmony_ci/** 4148c2ecf20Sopenharmony_ci * doc_setup_addr_sector - Setup blocks/page/ofs address for one plane 4158c2ecf20Sopenharmony_ci * @docg3: the device 4168c2ecf20Sopenharmony_ci * @sector: the sector 4178c2ecf20Sopenharmony_ci */ 4188c2ecf20Sopenharmony_cistatic void doc_setup_addr_sector(struct docg3 *docg3, int sector) 4198c2ecf20Sopenharmony_ci{ 4208c2ecf20Sopenharmony_ci doc_delay(docg3, 1); 4218c2ecf20Sopenharmony_ci doc_flash_address(docg3, sector & 0xff); 4228c2ecf20Sopenharmony_ci doc_flash_address(docg3, (sector >> 8) & 0xff); 4238c2ecf20Sopenharmony_ci doc_flash_address(docg3, (sector >> 16) & 0xff); 4248c2ecf20Sopenharmony_ci doc_delay(docg3, 1); 4258c2ecf20Sopenharmony_ci} 4268c2ecf20Sopenharmony_ci 4278c2ecf20Sopenharmony_ci/** 4288c2ecf20Sopenharmony_ci * doc_setup_writeaddr_sector - Setup blocks/page/ofs address for one plane 4298c2ecf20Sopenharmony_ci * @docg3: the device 4308c2ecf20Sopenharmony_ci * @sector: the sector 4318c2ecf20Sopenharmony_ci * @ofs: the offset in the page, between 0 and (512 + 16 + 512) 4328c2ecf20Sopenharmony_ci */ 4338c2ecf20Sopenharmony_cistatic void doc_setup_writeaddr_sector(struct docg3 *docg3, int sector, int ofs) 4348c2ecf20Sopenharmony_ci{ 4358c2ecf20Sopenharmony_ci ofs = ofs >> 2; 4368c2ecf20Sopenharmony_ci doc_delay(docg3, 1); 4378c2ecf20Sopenharmony_ci doc_flash_address(docg3, ofs & 0xff); 4388c2ecf20Sopenharmony_ci doc_flash_address(docg3, sector & 0xff); 4398c2ecf20Sopenharmony_ci doc_flash_address(docg3, (sector >> 8) & 0xff); 4408c2ecf20Sopenharmony_ci doc_flash_address(docg3, (sector >> 16) & 0xff); 4418c2ecf20Sopenharmony_ci doc_delay(docg3, 1); 4428c2ecf20Sopenharmony_ci} 4438c2ecf20Sopenharmony_ci 4448c2ecf20Sopenharmony_ci/** 4458c2ecf20Sopenharmony_ci * doc_seek - Set both flash planes to the specified block, page for reading 4468c2ecf20Sopenharmony_ci * @docg3: the device 4478c2ecf20Sopenharmony_ci * @block0: the first plane block index 4488c2ecf20Sopenharmony_ci * @block1: the second plane block index 4498c2ecf20Sopenharmony_ci * @page: the page index within the block 4508c2ecf20Sopenharmony_ci * @wear: if true, read will occur on the 4 extra bytes of the wear area 4518c2ecf20Sopenharmony_ci * @ofs: offset in page to read 4528c2ecf20Sopenharmony_ci * 4538c2ecf20Sopenharmony_ci * Programs the flash even and odd planes to the specific block and page. 4548c2ecf20Sopenharmony_ci * Alternatively, programs the flash to the wear area of the specified page. 4558c2ecf20Sopenharmony_ci */ 4568c2ecf20Sopenharmony_cistatic int doc_read_seek(struct docg3 *docg3, int block0, int block1, int page, 4578c2ecf20Sopenharmony_ci int wear, int ofs) 4588c2ecf20Sopenharmony_ci{ 4598c2ecf20Sopenharmony_ci int sector, ret = 0; 4608c2ecf20Sopenharmony_ci 4618c2ecf20Sopenharmony_ci doc_dbg("doc_seek(blocks=(%d,%d), page=%d, ofs=%d, wear=%d)\n", 4628c2ecf20Sopenharmony_ci block0, block1, page, ofs, wear); 4638c2ecf20Sopenharmony_ci 4648c2ecf20Sopenharmony_ci if (!wear && (ofs < 2 * DOC_LAYOUT_PAGE_SIZE)) { 4658c2ecf20Sopenharmony_ci doc_flash_sequence(docg3, DOC_SEQ_SET_PLANE1); 4668c2ecf20Sopenharmony_ci doc_flash_command(docg3, DOC_CMD_READ_PLANE1); 4678c2ecf20Sopenharmony_ci doc_delay(docg3, 2); 4688c2ecf20Sopenharmony_ci } else { 4698c2ecf20Sopenharmony_ci doc_flash_sequence(docg3, DOC_SEQ_SET_PLANE2); 4708c2ecf20Sopenharmony_ci doc_flash_command(docg3, DOC_CMD_READ_PLANE2); 4718c2ecf20Sopenharmony_ci doc_delay(docg3, 2); 4728c2ecf20Sopenharmony_ci } 4738c2ecf20Sopenharmony_ci 4748c2ecf20Sopenharmony_ci doc_set_reliable_mode(docg3); 4758c2ecf20Sopenharmony_ci if (wear) 4768c2ecf20Sopenharmony_ci ret = doc_set_extra_page_mode(docg3); 4778c2ecf20Sopenharmony_ci if (ret) 4788c2ecf20Sopenharmony_ci goto out; 4798c2ecf20Sopenharmony_ci 4808c2ecf20Sopenharmony_ci doc_flash_sequence(docg3, DOC_SEQ_READ); 4818c2ecf20Sopenharmony_ci sector = (block0 << DOC_ADDR_BLOCK_SHIFT) + (page & DOC_ADDR_PAGE_MASK); 4828c2ecf20Sopenharmony_ci doc_flash_command(docg3, DOC_CMD_PROG_BLOCK_ADDR); 4838c2ecf20Sopenharmony_ci doc_setup_addr_sector(docg3, sector); 4848c2ecf20Sopenharmony_ci 4858c2ecf20Sopenharmony_ci sector = (block1 << DOC_ADDR_BLOCK_SHIFT) + (page & DOC_ADDR_PAGE_MASK); 4868c2ecf20Sopenharmony_ci doc_flash_command(docg3, DOC_CMD_PROG_BLOCK_ADDR); 4878c2ecf20Sopenharmony_ci doc_setup_addr_sector(docg3, sector); 4888c2ecf20Sopenharmony_ci doc_delay(docg3, 1); 4898c2ecf20Sopenharmony_ci 4908c2ecf20Sopenharmony_ciout: 4918c2ecf20Sopenharmony_ci return ret; 4928c2ecf20Sopenharmony_ci} 4938c2ecf20Sopenharmony_ci 4948c2ecf20Sopenharmony_ci/** 4958c2ecf20Sopenharmony_ci * doc_write_seek - Set both flash planes to the specified block, page for writing 4968c2ecf20Sopenharmony_ci * @docg3: the device 4978c2ecf20Sopenharmony_ci * @block0: the first plane block index 4988c2ecf20Sopenharmony_ci * @block1: the second plane block index 4998c2ecf20Sopenharmony_ci * @page: the page index within the block 5008c2ecf20Sopenharmony_ci * @ofs: offset in page to write 5018c2ecf20Sopenharmony_ci * 5028c2ecf20Sopenharmony_ci * Programs the flash even and odd planes to the specific block and page. 5038c2ecf20Sopenharmony_ci * Alternatively, programs the flash to the wear area of the specified page. 5048c2ecf20Sopenharmony_ci */ 5058c2ecf20Sopenharmony_cistatic int doc_write_seek(struct docg3 *docg3, int block0, int block1, int page, 5068c2ecf20Sopenharmony_ci int ofs) 5078c2ecf20Sopenharmony_ci{ 5088c2ecf20Sopenharmony_ci int ret = 0, sector; 5098c2ecf20Sopenharmony_ci 5108c2ecf20Sopenharmony_ci doc_dbg("doc_write_seek(blocks=(%d,%d), page=%d, ofs=%d)\n", 5118c2ecf20Sopenharmony_ci block0, block1, page, ofs); 5128c2ecf20Sopenharmony_ci 5138c2ecf20Sopenharmony_ci doc_set_reliable_mode(docg3); 5148c2ecf20Sopenharmony_ci 5158c2ecf20Sopenharmony_ci if (ofs < 2 * DOC_LAYOUT_PAGE_SIZE) { 5168c2ecf20Sopenharmony_ci doc_flash_sequence(docg3, DOC_SEQ_SET_PLANE1); 5178c2ecf20Sopenharmony_ci doc_flash_command(docg3, DOC_CMD_READ_PLANE1); 5188c2ecf20Sopenharmony_ci doc_delay(docg3, 2); 5198c2ecf20Sopenharmony_ci } else { 5208c2ecf20Sopenharmony_ci doc_flash_sequence(docg3, DOC_SEQ_SET_PLANE2); 5218c2ecf20Sopenharmony_ci doc_flash_command(docg3, DOC_CMD_READ_PLANE2); 5228c2ecf20Sopenharmony_ci doc_delay(docg3, 2); 5238c2ecf20Sopenharmony_ci } 5248c2ecf20Sopenharmony_ci 5258c2ecf20Sopenharmony_ci doc_flash_sequence(docg3, DOC_SEQ_PAGE_SETUP); 5268c2ecf20Sopenharmony_ci doc_flash_command(docg3, DOC_CMD_PROG_CYCLE1); 5278c2ecf20Sopenharmony_ci 5288c2ecf20Sopenharmony_ci sector = (block0 << DOC_ADDR_BLOCK_SHIFT) + (page & DOC_ADDR_PAGE_MASK); 5298c2ecf20Sopenharmony_ci doc_setup_writeaddr_sector(docg3, sector, ofs); 5308c2ecf20Sopenharmony_ci 5318c2ecf20Sopenharmony_ci doc_flash_command(docg3, DOC_CMD_PROG_CYCLE3); 5328c2ecf20Sopenharmony_ci doc_delay(docg3, 2); 5338c2ecf20Sopenharmony_ci ret = doc_wait_ready(docg3); 5348c2ecf20Sopenharmony_ci if (ret) 5358c2ecf20Sopenharmony_ci goto out; 5368c2ecf20Sopenharmony_ci 5378c2ecf20Sopenharmony_ci doc_flash_command(docg3, DOC_CMD_PROG_CYCLE1); 5388c2ecf20Sopenharmony_ci sector = (block1 << DOC_ADDR_BLOCK_SHIFT) + (page & DOC_ADDR_PAGE_MASK); 5398c2ecf20Sopenharmony_ci doc_setup_writeaddr_sector(docg3, sector, ofs); 5408c2ecf20Sopenharmony_ci doc_delay(docg3, 1); 5418c2ecf20Sopenharmony_ci 5428c2ecf20Sopenharmony_ciout: 5438c2ecf20Sopenharmony_ci return ret; 5448c2ecf20Sopenharmony_ci} 5458c2ecf20Sopenharmony_ci 5468c2ecf20Sopenharmony_ci 5478c2ecf20Sopenharmony_ci/** 5488c2ecf20Sopenharmony_ci * doc_read_page_ecc_init - Initialize hardware ECC engine 5498c2ecf20Sopenharmony_ci * @docg3: the device 5508c2ecf20Sopenharmony_ci * @len: the number of bytes covered by the ECC (BCH covered) 5518c2ecf20Sopenharmony_ci * 5528c2ecf20Sopenharmony_ci * The function does initialize the hardware ECC engine to compute the Hamming 5538c2ecf20Sopenharmony_ci * ECC (on 1 byte) and the BCH hardware ECC (on 7 bytes). 5548c2ecf20Sopenharmony_ci * 5558c2ecf20Sopenharmony_ci * Return 0 if succeeded, -EIO on error 5568c2ecf20Sopenharmony_ci */ 5578c2ecf20Sopenharmony_cistatic int doc_read_page_ecc_init(struct docg3 *docg3, int len) 5588c2ecf20Sopenharmony_ci{ 5598c2ecf20Sopenharmony_ci doc_writew(docg3, DOC_ECCCONF0_READ_MODE 5608c2ecf20Sopenharmony_ci | DOC_ECCCONF0_BCH_ENABLE | DOC_ECCCONF0_HAMMING_ENABLE 5618c2ecf20Sopenharmony_ci | (len & DOC_ECCCONF0_DATA_BYTES_MASK), 5628c2ecf20Sopenharmony_ci DOC_ECCCONF0); 5638c2ecf20Sopenharmony_ci doc_delay(docg3, 4); 5648c2ecf20Sopenharmony_ci doc_register_readb(docg3, DOC_FLASHCONTROL); 5658c2ecf20Sopenharmony_ci return doc_wait_ready(docg3); 5668c2ecf20Sopenharmony_ci} 5678c2ecf20Sopenharmony_ci 5688c2ecf20Sopenharmony_ci/** 5698c2ecf20Sopenharmony_ci * doc_write_page_ecc_init - Initialize hardware BCH ECC engine 5708c2ecf20Sopenharmony_ci * @docg3: the device 5718c2ecf20Sopenharmony_ci * @len: the number of bytes covered by the ECC (BCH covered) 5728c2ecf20Sopenharmony_ci * 5738c2ecf20Sopenharmony_ci * The function does initialize the hardware ECC engine to compute the Hamming 5748c2ecf20Sopenharmony_ci * ECC (on 1 byte) and the BCH hardware ECC (on 7 bytes). 5758c2ecf20Sopenharmony_ci * 5768c2ecf20Sopenharmony_ci * Return 0 if succeeded, -EIO on error 5778c2ecf20Sopenharmony_ci */ 5788c2ecf20Sopenharmony_cistatic int doc_write_page_ecc_init(struct docg3 *docg3, int len) 5798c2ecf20Sopenharmony_ci{ 5808c2ecf20Sopenharmony_ci doc_writew(docg3, DOC_ECCCONF0_WRITE_MODE 5818c2ecf20Sopenharmony_ci | DOC_ECCCONF0_BCH_ENABLE | DOC_ECCCONF0_HAMMING_ENABLE 5828c2ecf20Sopenharmony_ci | (len & DOC_ECCCONF0_DATA_BYTES_MASK), 5838c2ecf20Sopenharmony_ci DOC_ECCCONF0); 5848c2ecf20Sopenharmony_ci doc_delay(docg3, 4); 5858c2ecf20Sopenharmony_ci doc_register_readb(docg3, DOC_FLASHCONTROL); 5868c2ecf20Sopenharmony_ci return doc_wait_ready(docg3); 5878c2ecf20Sopenharmony_ci} 5888c2ecf20Sopenharmony_ci 5898c2ecf20Sopenharmony_ci/** 5908c2ecf20Sopenharmony_ci * doc_ecc_disable - Disable Hamming and BCH ECC hardware calculator 5918c2ecf20Sopenharmony_ci * @docg3: the device 5928c2ecf20Sopenharmony_ci * 5938c2ecf20Sopenharmony_ci * Disables the hardware ECC generator and checker, for unchecked reads (as when 5948c2ecf20Sopenharmony_ci * reading OOB only or write status byte). 5958c2ecf20Sopenharmony_ci */ 5968c2ecf20Sopenharmony_cistatic void doc_ecc_disable(struct docg3 *docg3) 5978c2ecf20Sopenharmony_ci{ 5988c2ecf20Sopenharmony_ci doc_writew(docg3, DOC_ECCCONF0_READ_MODE, DOC_ECCCONF0); 5998c2ecf20Sopenharmony_ci doc_delay(docg3, 4); 6008c2ecf20Sopenharmony_ci} 6018c2ecf20Sopenharmony_ci 6028c2ecf20Sopenharmony_ci/** 6038c2ecf20Sopenharmony_ci * doc_hamming_ecc_init - Initialize hardware Hamming ECC engine 6048c2ecf20Sopenharmony_ci * @docg3: the device 6058c2ecf20Sopenharmony_ci * @nb_bytes: the number of bytes covered by the ECC (Hamming covered) 6068c2ecf20Sopenharmony_ci * 6078c2ecf20Sopenharmony_ci * This function programs the ECC hardware to compute the hamming code on the 6088c2ecf20Sopenharmony_ci * last provided N bytes to the hardware generator. 6098c2ecf20Sopenharmony_ci */ 6108c2ecf20Sopenharmony_cistatic void doc_hamming_ecc_init(struct docg3 *docg3, int nb_bytes) 6118c2ecf20Sopenharmony_ci{ 6128c2ecf20Sopenharmony_ci u8 ecc_conf1; 6138c2ecf20Sopenharmony_ci 6148c2ecf20Sopenharmony_ci ecc_conf1 = doc_register_readb(docg3, DOC_ECCCONF1); 6158c2ecf20Sopenharmony_ci ecc_conf1 &= ~DOC_ECCCONF1_HAMMING_BITS_MASK; 6168c2ecf20Sopenharmony_ci ecc_conf1 |= (nb_bytes & DOC_ECCCONF1_HAMMING_BITS_MASK); 6178c2ecf20Sopenharmony_ci doc_writeb(docg3, ecc_conf1, DOC_ECCCONF1); 6188c2ecf20Sopenharmony_ci} 6198c2ecf20Sopenharmony_ci 6208c2ecf20Sopenharmony_ci/** 6218c2ecf20Sopenharmony_ci * doc_ecc_bch_fix_data - Fix if need be read data from flash 6228c2ecf20Sopenharmony_ci * @docg3: the device 6238c2ecf20Sopenharmony_ci * @buf: the buffer of read data (512 + 7 + 1 bytes) 6248c2ecf20Sopenharmony_ci * @hwecc: the hardware calculated ECC. 6258c2ecf20Sopenharmony_ci * It's in fact recv_ecc ^ calc_ecc, where recv_ecc was read from OOB 6268c2ecf20Sopenharmony_ci * area data, and calc_ecc the ECC calculated by the hardware generator. 6278c2ecf20Sopenharmony_ci * 6288c2ecf20Sopenharmony_ci * Checks if the received data matches the ECC, and if an error is detected, 6298c2ecf20Sopenharmony_ci * tries to fix the bit flips (at most 4) in the buffer buf. As the docg3 6308c2ecf20Sopenharmony_ci * understands the (data, ecc, syndroms) in an inverted order in comparison to 6318c2ecf20Sopenharmony_ci * the BCH library, the function reverses the order of bits (ie. bit7 and bit0, 6328c2ecf20Sopenharmony_ci * bit6 and bit 1, ...) for all ECC data. 6338c2ecf20Sopenharmony_ci * 6348c2ecf20Sopenharmony_ci * The hardware ecc unit produces oob_ecc ^ calc_ecc. The kernel's bch 6358c2ecf20Sopenharmony_ci * algorithm is used to decode this. However the hw operates on page 6368c2ecf20Sopenharmony_ci * data in a bit order that is the reverse of that of the bch alg, 6378c2ecf20Sopenharmony_ci * requiring that the bits be reversed on the result. Thanks to Ivan 6388c2ecf20Sopenharmony_ci * Djelic for his analysis. 6398c2ecf20Sopenharmony_ci * 6408c2ecf20Sopenharmony_ci * Returns number of fixed bits (0, 1, 2, 3, 4) or -EBADMSG if too many bit 6418c2ecf20Sopenharmony_ci * errors were detected and cannot be fixed. 6428c2ecf20Sopenharmony_ci */ 6438c2ecf20Sopenharmony_cistatic int doc_ecc_bch_fix_data(struct docg3 *docg3, void *buf, u8 *hwecc) 6448c2ecf20Sopenharmony_ci{ 6458c2ecf20Sopenharmony_ci u8 ecc[DOC_ECC_BCH_SIZE]; 6468c2ecf20Sopenharmony_ci int errorpos[DOC_ECC_BCH_T], i, numerrs; 6478c2ecf20Sopenharmony_ci 6488c2ecf20Sopenharmony_ci for (i = 0; i < DOC_ECC_BCH_SIZE; i++) 6498c2ecf20Sopenharmony_ci ecc[i] = bitrev8(hwecc[i]); 6508c2ecf20Sopenharmony_ci numerrs = bch_decode(docg3->cascade->bch, NULL, 6518c2ecf20Sopenharmony_ci DOC_ECC_BCH_COVERED_BYTES, 6528c2ecf20Sopenharmony_ci NULL, ecc, NULL, errorpos); 6538c2ecf20Sopenharmony_ci BUG_ON(numerrs == -EINVAL); 6548c2ecf20Sopenharmony_ci if (numerrs < 0) 6558c2ecf20Sopenharmony_ci goto out; 6568c2ecf20Sopenharmony_ci 6578c2ecf20Sopenharmony_ci for (i = 0; i < numerrs; i++) 6588c2ecf20Sopenharmony_ci errorpos[i] = (errorpos[i] & ~7) | (7 - (errorpos[i] & 7)); 6598c2ecf20Sopenharmony_ci for (i = 0; i < numerrs; i++) 6608c2ecf20Sopenharmony_ci if (errorpos[i] < DOC_ECC_BCH_COVERED_BYTES*8) 6618c2ecf20Sopenharmony_ci /* error is located in data, correct it */ 6628c2ecf20Sopenharmony_ci change_bit(errorpos[i], buf); 6638c2ecf20Sopenharmony_ciout: 6648c2ecf20Sopenharmony_ci doc_dbg("doc_ecc_bch_fix_data: flipped %d bits\n", numerrs); 6658c2ecf20Sopenharmony_ci return numerrs; 6668c2ecf20Sopenharmony_ci} 6678c2ecf20Sopenharmony_ci 6688c2ecf20Sopenharmony_ci 6698c2ecf20Sopenharmony_ci/** 6708c2ecf20Sopenharmony_ci * doc_read_page_prepare - Prepares reading data from a flash page 6718c2ecf20Sopenharmony_ci * @docg3: the device 6728c2ecf20Sopenharmony_ci * @block0: the first plane block index on flash memory 6738c2ecf20Sopenharmony_ci * @block1: the second plane block index on flash memory 6748c2ecf20Sopenharmony_ci * @page: the page index in the block 6758c2ecf20Sopenharmony_ci * @offset: the offset in the page (must be a multiple of 4) 6768c2ecf20Sopenharmony_ci * 6778c2ecf20Sopenharmony_ci * Prepares the page to be read in the flash memory : 6788c2ecf20Sopenharmony_ci * - tell ASIC to map the flash pages 6798c2ecf20Sopenharmony_ci * - tell ASIC to be in read mode 6808c2ecf20Sopenharmony_ci * 6818c2ecf20Sopenharmony_ci * After a call to this method, a call to doc_read_page_finish is mandatory, 6828c2ecf20Sopenharmony_ci * to end the read cycle of the flash. 6838c2ecf20Sopenharmony_ci * 6848c2ecf20Sopenharmony_ci * Read data from a flash page. The length to be read must be between 0 and 6858c2ecf20Sopenharmony_ci * (page_size + oob_size + wear_size), ie. 532, and a multiple of 4 (because 6868c2ecf20Sopenharmony_ci * the extra bytes reading is not implemented). 6878c2ecf20Sopenharmony_ci * 6888c2ecf20Sopenharmony_ci * As pages are grouped by 2 (in 2 planes), reading from a page must be done 6898c2ecf20Sopenharmony_ci * in two steps: 6908c2ecf20Sopenharmony_ci * - one read of 512 bytes at offset 0 6918c2ecf20Sopenharmony_ci * - one read of 512 bytes at offset 512 + 16 6928c2ecf20Sopenharmony_ci * 6938c2ecf20Sopenharmony_ci * Returns 0 if successful, -EIO if a read error occurred. 6948c2ecf20Sopenharmony_ci */ 6958c2ecf20Sopenharmony_cistatic int doc_read_page_prepare(struct docg3 *docg3, int block0, int block1, 6968c2ecf20Sopenharmony_ci int page, int offset) 6978c2ecf20Sopenharmony_ci{ 6988c2ecf20Sopenharmony_ci int wear_area = 0, ret = 0; 6998c2ecf20Sopenharmony_ci 7008c2ecf20Sopenharmony_ci doc_dbg("doc_read_page_prepare(blocks=(%d,%d), page=%d, ofsInPage=%d)\n", 7018c2ecf20Sopenharmony_ci block0, block1, page, offset); 7028c2ecf20Sopenharmony_ci if (offset >= DOC_LAYOUT_WEAR_OFFSET) 7038c2ecf20Sopenharmony_ci wear_area = 1; 7048c2ecf20Sopenharmony_ci if (!wear_area && offset > (DOC_LAYOUT_PAGE_OOB_SIZE * 2)) 7058c2ecf20Sopenharmony_ci return -EINVAL; 7068c2ecf20Sopenharmony_ci 7078c2ecf20Sopenharmony_ci doc_set_device_id(docg3, docg3->device_id); 7088c2ecf20Sopenharmony_ci ret = doc_reset_seq(docg3); 7098c2ecf20Sopenharmony_ci if (ret) 7108c2ecf20Sopenharmony_ci goto err; 7118c2ecf20Sopenharmony_ci 7128c2ecf20Sopenharmony_ci /* Program the flash address block and page */ 7138c2ecf20Sopenharmony_ci ret = doc_read_seek(docg3, block0, block1, page, wear_area, offset); 7148c2ecf20Sopenharmony_ci if (ret) 7158c2ecf20Sopenharmony_ci goto err; 7168c2ecf20Sopenharmony_ci 7178c2ecf20Sopenharmony_ci doc_flash_command(docg3, DOC_CMD_READ_ALL_PLANES); 7188c2ecf20Sopenharmony_ci doc_delay(docg3, 2); 7198c2ecf20Sopenharmony_ci doc_wait_ready(docg3); 7208c2ecf20Sopenharmony_ci 7218c2ecf20Sopenharmony_ci doc_flash_command(docg3, DOC_CMD_SET_ADDR_READ); 7228c2ecf20Sopenharmony_ci doc_delay(docg3, 1); 7238c2ecf20Sopenharmony_ci if (offset >= DOC_LAYOUT_PAGE_SIZE * 2) 7248c2ecf20Sopenharmony_ci offset -= 2 * DOC_LAYOUT_PAGE_SIZE; 7258c2ecf20Sopenharmony_ci doc_flash_address(docg3, offset >> 2); 7268c2ecf20Sopenharmony_ci doc_delay(docg3, 1); 7278c2ecf20Sopenharmony_ci doc_wait_ready(docg3); 7288c2ecf20Sopenharmony_ci 7298c2ecf20Sopenharmony_ci doc_flash_command(docg3, DOC_CMD_READ_FLASH); 7308c2ecf20Sopenharmony_ci 7318c2ecf20Sopenharmony_ci return 0; 7328c2ecf20Sopenharmony_cierr: 7338c2ecf20Sopenharmony_ci doc_writeb(docg3, 0, DOC_DATAEND); 7348c2ecf20Sopenharmony_ci doc_delay(docg3, 2); 7358c2ecf20Sopenharmony_ci return -EIO; 7368c2ecf20Sopenharmony_ci} 7378c2ecf20Sopenharmony_ci 7388c2ecf20Sopenharmony_ci/** 7398c2ecf20Sopenharmony_ci * doc_read_page_getbytes - Reads bytes from a prepared page 7408c2ecf20Sopenharmony_ci * @docg3: the device 7418c2ecf20Sopenharmony_ci * @len: the number of bytes to be read (must be a multiple of 4) 7428c2ecf20Sopenharmony_ci * @buf: the buffer to be filled in (or NULL is forget bytes) 7438c2ecf20Sopenharmony_ci * @first: 1 if first time read, DOC_READADDRESS should be set 7448c2ecf20Sopenharmony_ci * @last_odd: 1 if last read ended up on an odd byte 7458c2ecf20Sopenharmony_ci * 7468c2ecf20Sopenharmony_ci * Reads bytes from a prepared page. There is a trickery here : if the last read 7478c2ecf20Sopenharmony_ci * ended up on an odd offset in the 1024 bytes double page, ie. between the 2 7488c2ecf20Sopenharmony_ci * planes, the first byte must be read apart. If a word (16bit) read was used, 7498c2ecf20Sopenharmony_ci * the read would return the byte of plane 2 as low *and* high endian, which 7508c2ecf20Sopenharmony_ci * will mess the read. 7518c2ecf20Sopenharmony_ci * 7528c2ecf20Sopenharmony_ci */ 7538c2ecf20Sopenharmony_cistatic int doc_read_page_getbytes(struct docg3 *docg3, int len, u_char *buf, 7548c2ecf20Sopenharmony_ci int first, int last_odd) 7558c2ecf20Sopenharmony_ci{ 7568c2ecf20Sopenharmony_ci if (last_odd && len > 0) { 7578c2ecf20Sopenharmony_ci doc_read_data_area(docg3, buf, 1, first); 7588c2ecf20Sopenharmony_ci doc_read_data_area(docg3, buf ? buf + 1 : buf, len - 1, 0); 7598c2ecf20Sopenharmony_ci } else { 7608c2ecf20Sopenharmony_ci doc_read_data_area(docg3, buf, len, first); 7618c2ecf20Sopenharmony_ci } 7628c2ecf20Sopenharmony_ci doc_delay(docg3, 2); 7638c2ecf20Sopenharmony_ci return len; 7648c2ecf20Sopenharmony_ci} 7658c2ecf20Sopenharmony_ci 7668c2ecf20Sopenharmony_ci/** 7678c2ecf20Sopenharmony_ci * doc_write_page_putbytes - Writes bytes into a prepared page 7688c2ecf20Sopenharmony_ci * @docg3: the device 7698c2ecf20Sopenharmony_ci * @len: the number of bytes to be written 7708c2ecf20Sopenharmony_ci * @buf: the buffer of input bytes 7718c2ecf20Sopenharmony_ci * 7728c2ecf20Sopenharmony_ci */ 7738c2ecf20Sopenharmony_cistatic void doc_write_page_putbytes(struct docg3 *docg3, int len, 7748c2ecf20Sopenharmony_ci const u_char *buf) 7758c2ecf20Sopenharmony_ci{ 7768c2ecf20Sopenharmony_ci doc_write_data_area(docg3, buf, len); 7778c2ecf20Sopenharmony_ci doc_delay(docg3, 2); 7788c2ecf20Sopenharmony_ci} 7798c2ecf20Sopenharmony_ci 7808c2ecf20Sopenharmony_ci/** 7818c2ecf20Sopenharmony_ci * doc_get_bch_hw_ecc - Get hardware calculated BCH ECC 7828c2ecf20Sopenharmony_ci * @docg3: the device 7838c2ecf20Sopenharmony_ci * @hwecc: the array of 7 integers where the hardware ecc will be stored 7848c2ecf20Sopenharmony_ci */ 7858c2ecf20Sopenharmony_cistatic void doc_get_bch_hw_ecc(struct docg3 *docg3, u8 *hwecc) 7868c2ecf20Sopenharmony_ci{ 7878c2ecf20Sopenharmony_ci int i; 7888c2ecf20Sopenharmony_ci 7898c2ecf20Sopenharmony_ci for (i = 0; i < DOC_ECC_BCH_SIZE; i++) 7908c2ecf20Sopenharmony_ci hwecc[i] = doc_register_readb(docg3, DOC_BCH_HW_ECC(i)); 7918c2ecf20Sopenharmony_ci} 7928c2ecf20Sopenharmony_ci 7938c2ecf20Sopenharmony_ci/** 7948c2ecf20Sopenharmony_ci * doc_page_finish - Ends reading/writing of a flash page 7958c2ecf20Sopenharmony_ci * @docg3: the device 7968c2ecf20Sopenharmony_ci */ 7978c2ecf20Sopenharmony_cistatic void doc_page_finish(struct docg3 *docg3) 7988c2ecf20Sopenharmony_ci{ 7998c2ecf20Sopenharmony_ci doc_writeb(docg3, 0, DOC_DATAEND); 8008c2ecf20Sopenharmony_ci doc_delay(docg3, 2); 8018c2ecf20Sopenharmony_ci} 8028c2ecf20Sopenharmony_ci 8038c2ecf20Sopenharmony_ci/** 8048c2ecf20Sopenharmony_ci * doc_read_page_finish - Ends reading of a flash page 8058c2ecf20Sopenharmony_ci * @docg3: the device 8068c2ecf20Sopenharmony_ci * 8078c2ecf20Sopenharmony_ci * As a side effect, resets the chip selector to 0. This ensures that after each 8088c2ecf20Sopenharmony_ci * read operation, the floor 0 is selected. Therefore, if the systems halts, the 8098c2ecf20Sopenharmony_ci * reboot will boot on floor 0, where the IPL is. 8108c2ecf20Sopenharmony_ci */ 8118c2ecf20Sopenharmony_cistatic void doc_read_page_finish(struct docg3 *docg3) 8128c2ecf20Sopenharmony_ci{ 8138c2ecf20Sopenharmony_ci doc_page_finish(docg3); 8148c2ecf20Sopenharmony_ci doc_set_device_id(docg3, 0); 8158c2ecf20Sopenharmony_ci} 8168c2ecf20Sopenharmony_ci 8178c2ecf20Sopenharmony_ci/** 8188c2ecf20Sopenharmony_ci * calc_block_sector - Calculate blocks, pages and ofs. 8198c2ecf20Sopenharmony_ci 8208c2ecf20Sopenharmony_ci * @from: offset in flash 8218c2ecf20Sopenharmony_ci * @block0: first plane block index calculated 8228c2ecf20Sopenharmony_ci * @block1: second plane block index calculated 8238c2ecf20Sopenharmony_ci * @page: page calculated 8248c2ecf20Sopenharmony_ci * @ofs: offset in page 8258c2ecf20Sopenharmony_ci * @reliable: 0 if docg3 in normal mode, 1 if docg3 in fast mode, 2 if docg3 in 8268c2ecf20Sopenharmony_ci * reliable mode. 8278c2ecf20Sopenharmony_ci * 8288c2ecf20Sopenharmony_ci * The calculation is based on the reliable/normal mode. In normal mode, the 64 8298c2ecf20Sopenharmony_ci * pages of a block are available. In reliable mode, as pages 2*n and 2*n+1 are 8308c2ecf20Sopenharmony_ci * clones, only 32 pages per block are available. 8318c2ecf20Sopenharmony_ci */ 8328c2ecf20Sopenharmony_cistatic void calc_block_sector(loff_t from, int *block0, int *block1, int *page, 8338c2ecf20Sopenharmony_ci int *ofs, int reliable) 8348c2ecf20Sopenharmony_ci{ 8358c2ecf20Sopenharmony_ci uint sector, pages_biblock; 8368c2ecf20Sopenharmony_ci 8378c2ecf20Sopenharmony_ci pages_biblock = DOC_LAYOUT_PAGES_PER_BLOCK * DOC_LAYOUT_NBPLANES; 8388c2ecf20Sopenharmony_ci if (reliable == 1 || reliable == 2) 8398c2ecf20Sopenharmony_ci pages_biblock /= 2; 8408c2ecf20Sopenharmony_ci 8418c2ecf20Sopenharmony_ci sector = from / DOC_LAYOUT_PAGE_SIZE; 8428c2ecf20Sopenharmony_ci *block0 = sector / pages_biblock * DOC_LAYOUT_NBPLANES; 8438c2ecf20Sopenharmony_ci *block1 = *block0 + 1; 8448c2ecf20Sopenharmony_ci *page = sector % pages_biblock; 8458c2ecf20Sopenharmony_ci *page /= DOC_LAYOUT_NBPLANES; 8468c2ecf20Sopenharmony_ci if (reliable == 1 || reliable == 2) 8478c2ecf20Sopenharmony_ci *page *= 2; 8488c2ecf20Sopenharmony_ci if (sector % 2) 8498c2ecf20Sopenharmony_ci *ofs = DOC_LAYOUT_PAGE_OOB_SIZE; 8508c2ecf20Sopenharmony_ci else 8518c2ecf20Sopenharmony_ci *ofs = 0; 8528c2ecf20Sopenharmony_ci} 8538c2ecf20Sopenharmony_ci 8548c2ecf20Sopenharmony_ci/** 8558c2ecf20Sopenharmony_ci * doc_read_oob - Read out of band bytes from flash 8568c2ecf20Sopenharmony_ci * @mtd: the device 8578c2ecf20Sopenharmony_ci * @from: the offset from first block and first page, in bytes, aligned on page 8588c2ecf20Sopenharmony_ci * size 8598c2ecf20Sopenharmony_ci * @ops: the mtd oob structure 8608c2ecf20Sopenharmony_ci * 8618c2ecf20Sopenharmony_ci * Reads flash memory OOB area of pages. 8628c2ecf20Sopenharmony_ci * 8638c2ecf20Sopenharmony_ci * Returns 0 if read successful, of -EIO, -EINVAL if an error occurred 8648c2ecf20Sopenharmony_ci */ 8658c2ecf20Sopenharmony_cistatic int doc_read_oob(struct mtd_info *mtd, loff_t from, 8668c2ecf20Sopenharmony_ci struct mtd_oob_ops *ops) 8678c2ecf20Sopenharmony_ci{ 8688c2ecf20Sopenharmony_ci struct docg3 *docg3 = mtd->priv; 8698c2ecf20Sopenharmony_ci int block0, block1, page, ret, skip, ofs = 0; 8708c2ecf20Sopenharmony_ci u8 *oobbuf = ops->oobbuf; 8718c2ecf20Sopenharmony_ci u8 *buf = ops->datbuf; 8728c2ecf20Sopenharmony_ci size_t len, ooblen, nbdata, nboob; 8738c2ecf20Sopenharmony_ci u8 hwecc[DOC_ECC_BCH_SIZE], eccconf1; 8748c2ecf20Sopenharmony_ci int max_bitflips = 0; 8758c2ecf20Sopenharmony_ci 8768c2ecf20Sopenharmony_ci if (buf) 8778c2ecf20Sopenharmony_ci len = ops->len; 8788c2ecf20Sopenharmony_ci else 8798c2ecf20Sopenharmony_ci len = 0; 8808c2ecf20Sopenharmony_ci if (oobbuf) 8818c2ecf20Sopenharmony_ci ooblen = ops->ooblen; 8828c2ecf20Sopenharmony_ci else 8838c2ecf20Sopenharmony_ci ooblen = 0; 8848c2ecf20Sopenharmony_ci 8858c2ecf20Sopenharmony_ci if (oobbuf && ops->mode == MTD_OPS_PLACE_OOB) 8868c2ecf20Sopenharmony_ci oobbuf += ops->ooboffs; 8878c2ecf20Sopenharmony_ci 8888c2ecf20Sopenharmony_ci doc_dbg("doc_read_oob(from=%lld, mode=%d, data=(%p:%zu), oob=(%p:%zu))\n", 8898c2ecf20Sopenharmony_ci from, ops->mode, buf, len, oobbuf, ooblen); 8908c2ecf20Sopenharmony_ci if (ooblen % DOC_LAYOUT_OOB_SIZE) 8918c2ecf20Sopenharmony_ci return -EINVAL; 8928c2ecf20Sopenharmony_ci 8938c2ecf20Sopenharmony_ci ops->oobretlen = 0; 8948c2ecf20Sopenharmony_ci ops->retlen = 0; 8958c2ecf20Sopenharmony_ci ret = 0; 8968c2ecf20Sopenharmony_ci skip = from % DOC_LAYOUT_PAGE_SIZE; 8978c2ecf20Sopenharmony_ci mutex_lock(&docg3->cascade->lock); 8988c2ecf20Sopenharmony_ci while (ret >= 0 && (len > 0 || ooblen > 0)) { 8998c2ecf20Sopenharmony_ci calc_block_sector(from - skip, &block0, &block1, &page, &ofs, 9008c2ecf20Sopenharmony_ci docg3->reliable); 9018c2ecf20Sopenharmony_ci nbdata = min_t(size_t, len, DOC_LAYOUT_PAGE_SIZE - skip); 9028c2ecf20Sopenharmony_ci nboob = min_t(size_t, ooblen, (size_t)DOC_LAYOUT_OOB_SIZE); 9038c2ecf20Sopenharmony_ci ret = doc_read_page_prepare(docg3, block0, block1, page, ofs); 9048c2ecf20Sopenharmony_ci if (ret < 0) 9058c2ecf20Sopenharmony_ci goto out; 9068c2ecf20Sopenharmony_ci ret = doc_read_page_ecc_init(docg3, DOC_ECC_BCH_TOTAL_BYTES); 9078c2ecf20Sopenharmony_ci if (ret < 0) 9088c2ecf20Sopenharmony_ci goto err_in_read; 9098c2ecf20Sopenharmony_ci ret = doc_read_page_getbytes(docg3, skip, NULL, 1, 0); 9108c2ecf20Sopenharmony_ci if (ret < skip) 9118c2ecf20Sopenharmony_ci goto err_in_read; 9128c2ecf20Sopenharmony_ci ret = doc_read_page_getbytes(docg3, nbdata, buf, 0, skip % 2); 9138c2ecf20Sopenharmony_ci if (ret < nbdata) 9148c2ecf20Sopenharmony_ci goto err_in_read; 9158c2ecf20Sopenharmony_ci doc_read_page_getbytes(docg3, 9168c2ecf20Sopenharmony_ci DOC_LAYOUT_PAGE_SIZE - nbdata - skip, 9178c2ecf20Sopenharmony_ci NULL, 0, (skip + nbdata) % 2); 9188c2ecf20Sopenharmony_ci ret = doc_read_page_getbytes(docg3, nboob, oobbuf, 0, 0); 9198c2ecf20Sopenharmony_ci if (ret < nboob) 9208c2ecf20Sopenharmony_ci goto err_in_read; 9218c2ecf20Sopenharmony_ci doc_read_page_getbytes(docg3, DOC_LAYOUT_OOB_SIZE - nboob, 9228c2ecf20Sopenharmony_ci NULL, 0, nboob % 2); 9238c2ecf20Sopenharmony_ci 9248c2ecf20Sopenharmony_ci doc_get_bch_hw_ecc(docg3, hwecc); 9258c2ecf20Sopenharmony_ci eccconf1 = doc_register_readb(docg3, DOC_ECCCONF1); 9268c2ecf20Sopenharmony_ci 9278c2ecf20Sopenharmony_ci if (nboob >= DOC_LAYOUT_OOB_SIZE) { 9288c2ecf20Sopenharmony_ci doc_dbg("OOB - INFO: %*phC\n", 7, oobbuf); 9298c2ecf20Sopenharmony_ci doc_dbg("OOB - HAMMING: %02x\n", oobbuf[7]); 9308c2ecf20Sopenharmony_ci doc_dbg("OOB - BCH_ECC: %*phC\n", 7, oobbuf + 8); 9318c2ecf20Sopenharmony_ci doc_dbg("OOB - UNUSED: %02x\n", oobbuf[15]); 9328c2ecf20Sopenharmony_ci } 9338c2ecf20Sopenharmony_ci doc_dbg("ECC checks: ECCConf1=%x\n", eccconf1); 9348c2ecf20Sopenharmony_ci doc_dbg("ECC HW_ECC: %*phC\n", 7, hwecc); 9358c2ecf20Sopenharmony_ci 9368c2ecf20Sopenharmony_ci ret = -EIO; 9378c2ecf20Sopenharmony_ci if (is_prot_seq_error(docg3)) 9388c2ecf20Sopenharmony_ci goto err_in_read; 9398c2ecf20Sopenharmony_ci ret = 0; 9408c2ecf20Sopenharmony_ci if ((block0 >= DOC_LAYOUT_BLOCK_FIRST_DATA) && 9418c2ecf20Sopenharmony_ci (eccconf1 & DOC_ECCCONF1_BCH_SYNDROM_ERR) && 9428c2ecf20Sopenharmony_ci (eccconf1 & DOC_ECCCONF1_PAGE_IS_WRITTEN) && 9438c2ecf20Sopenharmony_ci (ops->mode != MTD_OPS_RAW) && 9448c2ecf20Sopenharmony_ci (nbdata == DOC_LAYOUT_PAGE_SIZE)) { 9458c2ecf20Sopenharmony_ci ret = doc_ecc_bch_fix_data(docg3, buf, hwecc); 9468c2ecf20Sopenharmony_ci if (ret < 0) { 9478c2ecf20Sopenharmony_ci mtd->ecc_stats.failed++; 9488c2ecf20Sopenharmony_ci ret = -EBADMSG; 9498c2ecf20Sopenharmony_ci } 9508c2ecf20Sopenharmony_ci if (ret > 0) { 9518c2ecf20Sopenharmony_ci mtd->ecc_stats.corrected += ret; 9528c2ecf20Sopenharmony_ci max_bitflips = max(max_bitflips, ret); 9538c2ecf20Sopenharmony_ci ret = max_bitflips; 9548c2ecf20Sopenharmony_ci } 9558c2ecf20Sopenharmony_ci } 9568c2ecf20Sopenharmony_ci 9578c2ecf20Sopenharmony_ci doc_read_page_finish(docg3); 9588c2ecf20Sopenharmony_ci ops->retlen += nbdata; 9598c2ecf20Sopenharmony_ci ops->oobretlen += nboob; 9608c2ecf20Sopenharmony_ci buf += nbdata; 9618c2ecf20Sopenharmony_ci oobbuf += nboob; 9628c2ecf20Sopenharmony_ci len -= nbdata; 9638c2ecf20Sopenharmony_ci ooblen -= nboob; 9648c2ecf20Sopenharmony_ci from += DOC_LAYOUT_PAGE_SIZE; 9658c2ecf20Sopenharmony_ci skip = 0; 9668c2ecf20Sopenharmony_ci } 9678c2ecf20Sopenharmony_ci 9688c2ecf20Sopenharmony_ciout: 9698c2ecf20Sopenharmony_ci mutex_unlock(&docg3->cascade->lock); 9708c2ecf20Sopenharmony_ci return ret; 9718c2ecf20Sopenharmony_cierr_in_read: 9728c2ecf20Sopenharmony_ci doc_read_page_finish(docg3); 9738c2ecf20Sopenharmony_ci goto out; 9748c2ecf20Sopenharmony_ci} 9758c2ecf20Sopenharmony_ci 9768c2ecf20Sopenharmony_cistatic int doc_reload_bbt(struct docg3 *docg3) 9778c2ecf20Sopenharmony_ci{ 9788c2ecf20Sopenharmony_ci int block = DOC_LAYOUT_BLOCK_BBT; 9798c2ecf20Sopenharmony_ci int ret = 0, nbpages, page; 9808c2ecf20Sopenharmony_ci u_char *buf = docg3->bbt; 9818c2ecf20Sopenharmony_ci 9828c2ecf20Sopenharmony_ci nbpages = DIV_ROUND_UP(docg3->max_block + 1, 8 * DOC_LAYOUT_PAGE_SIZE); 9838c2ecf20Sopenharmony_ci for (page = 0; !ret && (page < nbpages); page++) { 9848c2ecf20Sopenharmony_ci ret = doc_read_page_prepare(docg3, block, block + 1, 9858c2ecf20Sopenharmony_ci page + DOC_LAYOUT_PAGE_BBT, 0); 9868c2ecf20Sopenharmony_ci if (!ret) 9878c2ecf20Sopenharmony_ci ret = doc_read_page_ecc_init(docg3, 9888c2ecf20Sopenharmony_ci DOC_LAYOUT_PAGE_SIZE); 9898c2ecf20Sopenharmony_ci if (!ret) 9908c2ecf20Sopenharmony_ci doc_read_page_getbytes(docg3, DOC_LAYOUT_PAGE_SIZE, 9918c2ecf20Sopenharmony_ci buf, 1, 0); 9928c2ecf20Sopenharmony_ci buf += DOC_LAYOUT_PAGE_SIZE; 9938c2ecf20Sopenharmony_ci } 9948c2ecf20Sopenharmony_ci doc_read_page_finish(docg3); 9958c2ecf20Sopenharmony_ci return ret; 9968c2ecf20Sopenharmony_ci} 9978c2ecf20Sopenharmony_ci 9988c2ecf20Sopenharmony_ci/** 9998c2ecf20Sopenharmony_ci * doc_block_isbad - Checks whether a block is good or not 10008c2ecf20Sopenharmony_ci * @mtd: the device 10018c2ecf20Sopenharmony_ci * @from: the offset to find the correct block 10028c2ecf20Sopenharmony_ci * 10038c2ecf20Sopenharmony_ci * Returns 1 if block is bad, 0 if block is good 10048c2ecf20Sopenharmony_ci */ 10058c2ecf20Sopenharmony_cistatic int doc_block_isbad(struct mtd_info *mtd, loff_t from) 10068c2ecf20Sopenharmony_ci{ 10078c2ecf20Sopenharmony_ci struct docg3 *docg3 = mtd->priv; 10088c2ecf20Sopenharmony_ci int block0, block1, page, ofs, is_good; 10098c2ecf20Sopenharmony_ci 10108c2ecf20Sopenharmony_ci calc_block_sector(from, &block0, &block1, &page, &ofs, 10118c2ecf20Sopenharmony_ci docg3->reliable); 10128c2ecf20Sopenharmony_ci doc_dbg("doc_block_isbad(from=%lld) => block=(%d,%d), page=%d, ofs=%d\n", 10138c2ecf20Sopenharmony_ci from, block0, block1, page, ofs); 10148c2ecf20Sopenharmony_ci 10158c2ecf20Sopenharmony_ci if (block0 < DOC_LAYOUT_BLOCK_FIRST_DATA) 10168c2ecf20Sopenharmony_ci return 0; 10178c2ecf20Sopenharmony_ci if (block1 > docg3->max_block) 10188c2ecf20Sopenharmony_ci return -EINVAL; 10198c2ecf20Sopenharmony_ci 10208c2ecf20Sopenharmony_ci is_good = docg3->bbt[block0 >> 3] & (1 << (block0 & 0x7)); 10218c2ecf20Sopenharmony_ci return !is_good; 10228c2ecf20Sopenharmony_ci} 10238c2ecf20Sopenharmony_ci 10248c2ecf20Sopenharmony_ci#if 0 10258c2ecf20Sopenharmony_ci/** 10268c2ecf20Sopenharmony_ci * doc_get_erase_count - Get block erase count 10278c2ecf20Sopenharmony_ci * @docg3: the device 10288c2ecf20Sopenharmony_ci * @from: the offset in which the block is. 10298c2ecf20Sopenharmony_ci * 10308c2ecf20Sopenharmony_ci * Get the number of times a block was erased. The number is the maximum of 10318c2ecf20Sopenharmony_ci * erase times between first and second plane (which should be equal normally). 10328c2ecf20Sopenharmony_ci * 10338c2ecf20Sopenharmony_ci * Returns The number of erases, or -EINVAL or -EIO on error. 10348c2ecf20Sopenharmony_ci */ 10358c2ecf20Sopenharmony_cistatic int doc_get_erase_count(struct docg3 *docg3, loff_t from) 10368c2ecf20Sopenharmony_ci{ 10378c2ecf20Sopenharmony_ci u8 buf[DOC_LAYOUT_WEAR_SIZE]; 10388c2ecf20Sopenharmony_ci int ret, plane1_erase_count, plane2_erase_count; 10398c2ecf20Sopenharmony_ci int block0, block1, page, ofs; 10408c2ecf20Sopenharmony_ci 10418c2ecf20Sopenharmony_ci doc_dbg("doc_get_erase_count(from=%lld, buf=%p)\n", from, buf); 10428c2ecf20Sopenharmony_ci if (from % DOC_LAYOUT_PAGE_SIZE) 10438c2ecf20Sopenharmony_ci return -EINVAL; 10448c2ecf20Sopenharmony_ci calc_block_sector(from, &block0, &block1, &page, &ofs, docg3->reliable); 10458c2ecf20Sopenharmony_ci if (block1 > docg3->max_block) 10468c2ecf20Sopenharmony_ci return -EINVAL; 10478c2ecf20Sopenharmony_ci 10488c2ecf20Sopenharmony_ci ret = doc_reset_seq(docg3); 10498c2ecf20Sopenharmony_ci if (!ret) 10508c2ecf20Sopenharmony_ci ret = doc_read_page_prepare(docg3, block0, block1, page, 10518c2ecf20Sopenharmony_ci ofs + DOC_LAYOUT_WEAR_OFFSET, 0); 10528c2ecf20Sopenharmony_ci if (!ret) 10538c2ecf20Sopenharmony_ci ret = doc_read_page_getbytes(docg3, DOC_LAYOUT_WEAR_SIZE, 10548c2ecf20Sopenharmony_ci buf, 1, 0); 10558c2ecf20Sopenharmony_ci doc_read_page_finish(docg3); 10568c2ecf20Sopenharmony_ci 10578c2ecf20Sopenharmony_ci if (ret || (buf[0] != DOC_ERASE_MARK) || (buf[2] != DOC_ERASE_MARK)) 10588c2ecf20Sopenharmony_ci return -EIO; 10598c2ecf20Sopenharmony_ci plane1_erase_count = (u8)(~buf[1]) | ((u8)(~buf[4]) << 8) 10608c2ecf20Sopenharmony_ci | ((u8)(~buf[5]) << 16); 10618c2ecf20Sopenharmony_ci plane2_erase_count = (u8)(~buf[3]) | ((u8)(~buf[6]) << 8) 10628c2ecf20Sopenharmony_ci | ((u8)(~buf[7]) << 16); 10638c2ecf20Sopenharmony_ci 10648c2ecf20Sopenharmony_ci return max(plane1_erase_count, plane2_erase_count); 10658c2ecf20Sopenharmony_ci} 10668c2ecf20Sopenharmony_ci#endif 10678c2ecf20Sopenharmony_ci 10688c2ecf20Sopenharmony_ci/** 10698c2ecf20Sopenharmony_ci * doc_get_op_status - get erase/write operation status 10708c2ecf20Sopenharmony_ci * @docg3: the device 10718c2ecf20Sopenharmony_ci * 10728c2ecf20Sopenharmony_ci * Queries the status from the chip, and returns it 10738c2ecf20Sopenharmony_ci * 10748c2ecf20Sopenharmony_ci * Returns the status (bits DOC_PLANES_STATUS_*) 10758c2ecf20Sopenharmony_ci */ 10768c2ecf20Sopenharmony_cistatic int doc_get_op_status(struct docg3 *docg3) 10778c2ecf20Sopenharmony_ci{ 10788c2ecf20Sopenharmony_ci u8 status; 10798c2ecf20Sopenharmony_ci 10808c2ecf20Sopenharmony_ci doc_flash_sequence(docg3, DOC_SEQ_PLANES_STATUS); 10818c2ecf20Sopenharmony_ci doc_flash_command(docg3, DOC_CMD_PLANES_STATUS); 10828c2ecf20Sopenharmony_ci doc_delay(docg3, 5); 10838c2ecf20Sopenharmony_ci 10848c2ecf20Sopenharmony_ci doc_ecc_disable(docg3); 10858c2ecf20Sopenharmony_ci doc_read_data_area(docg3, &status, 1, 1); 10868c2ecf20Sopenharmony_ci return status; 10878c2ecf20Sopenharmony_ci} 10888c2ecf20Sopenharmony_ci 10898c2ecf20Sopenharmony_ci/** 10908c2ecf20Sopenharmony_ci * doc_write_erase_wait_status - wait for write or erase completion 10918c2ecf20Sopenharmony_ci * @docg3: the device 10928c2ecf20Sopenharmony_ci * 10938c2ecf20Sopenharmony_ci * Wait for the chip to be ready again after erase or write operation, and check 10948c2ecf20Sopenharmony_ci * erase/write status. 10958c2ecf20Sopenharmony_ci * 10968c2ecf20Sopenharmony_ci * Returns 0 if erase successful, -EIO if erase/write issue, -ETIMEOUT if 10978c2ecf20Sopenharmony_ci * timeout 10988c2ecf20Sopenharmony_ci */ 10998c2ecf20Sopenharmony_cistatic int doc_write_erase_wait_status(struct docg3 *docg3) 11008c2ecf20Sopenharmony_ci{ 11018c2ecf20Sopenharmony_ci int i, status, ret = 0; 11028c2ecf20Sopenharmony_ci 11038c2ecf20Sopenharmony_ci for (i = 0; !doc_is_ready(docg3) && i < 5; i++) 11048c2ecf20Sopenharmony_ci msleep(20); 11058c2ecf20Sopenharmony_ci if (!doc_is_ready(docg3)) { 11068c2ecf20Sopenharmony_ci doc_dbg("Timeout reached and the chip is still not ready\n"); 11078c2ecf20Sopenharmony_ci ret = -EAGAIN; 11088c2ecf20Sopenharmony_ci goto out; 11098c2ecf20Sopenharmony_ci } 11108c2ecf20Sopenharmony_ci 11118c2ecf20Sopenharmony_ci status = doc_get_op_status(docg3); 11128c2ecf20Sopenharmony_ci if (status & DOC_PLANES_STATUS_FAIL) { 11138c2ecf20Sopenharmony_ci doc_dbg("Erase/Write failed on (a) plane(s), status = %x\n", 11148c2ecf20Sopenharmony_ci status); 11158c2ecf20Sopenharmony_ci ret = -EIO; 11168c2ecf20Sopenharmony_ci } 11178c2ecf20Sopenharmony_ci 11188c2ecf20Sopenharmony_ciout: 11198c2ecf20Sopenharmony_ci doc_page_finish(docg3); 11208c2ecf20Sopenharmony_ci return ret; 11218c2ecf20Sopenharmony_ci} 11228c2ecf20Sopenharmony_ci 11238c2ecf20Sopenharmony_ci/** 11248c2ecf20Sopenharmony_ci * doc_erase_block - Erase a couple of blocks 11258c2ecf20Sopenharmony_ci * @docg3: the device 11268c2ecf20Sopenharmony_ci * @block0: the first block to erase (leftmost plane) 11278c2ecf20Sopenharmony_ci * @block1: the second block to erase (rightmost plane) 11288c2ecf20Sopenharmony_ci * 11298c2ecf20Sopenharmony_ci * Erase both blocks, and return operation status 11308c2ecf20Sopenharmony_ci * 11318c2ecf20Sopenharmony_ci * Returns 0 if erase successful, -EIO if erase issue, -ETIMEOUT if chip not 11328c2ecf20Sopenharmony_ci * ready for too long 11338c2ecf20Sopenharmony_ci */ 11348c2ecf20Sopenharmony_cistatic int doc_erase_block(struct docg3 *docg3, int block0, int block1) 11358c2ecf20Sopenharmony_ci{ 11368c2ecf20Sopenharmony_ci int ret, sector; 11378c2ecf20Sopenharmony_ci 11388c2ecf20Sopenharmony_ci doc_dbg("doc_erase_block(blocks=(%d,%d))\n", block0, block1); 11398c2ecf20Sopenharmony_ci ret = doc_reset_seq(docg3); 11408c2ecf20Sopenharmony_ci if (ret) 11418c2ecf20Sopenharmony_ci return -EIO; 11428c2ecf20Sopenharmony_ci 11438c2ecf20Sopenharmony_ci doc_set_reliable_mode(docg3); 11448c2ecf20Sopenharmony_ci doc_flash_sequence(docg3, DOC_SEQ_ERASE); 11458c2ecf20Sopenharmony_ci 11468c2ecf20Sopenharmony_ci sector = block0 << DOC_ADDR_BLOCK_SHIFT; 11478c2ecf20Sopenharmony_ci doc_flash_command(docg3, DOC_CMD_PROG_BLOCK_ADDR); 11488c2ecf20Sopenharmony_ci doc_setup_addr_sector(docg3, sector); 11498c2ecf20Sopenharmony_ci sector = block1 << DOC_ADDR_BLOCK_SHIFT; 11508c2ecf20Sopenharmony_ci doc_flash_command(docg3, DOC_CMD_PROG_BLOCK_ADDR); 11518c2ecf20Sopenharmony_ci doc_setup_addr_sector(docg3, sector); 11528c2ecf20Sopenharmony_ci doc_delay(docg3, 1); 11538c2ecf20Sopenharmony_ci 11548c2ecf20Sopenharmony_ci doc_flash_command(docg3, DOC_CMD_ERASECYCLE2); 11558c2ecf20Sopenharmony_ci doc_delay(docg3, 2); 11568c2ecf20Sopenharmony_ci 11578c2ecf20Sopenharmony_ci if (is_prot_seq_error(docg3)) { 11588c2ecf20Sopenharmony_ci doc_err("Erase blocks %d,%d error\n", block0, block1); 11598c2ecf20Sopenharmony_ci return -EIO; 11608c2ecf20Sopenharmony_ci } 11618c2ecf20Sopenharmony_ci 11628c2ecf20Sopenharmony_ci return doc_write_erase_wait_status(docg3); 11638c2ecf20Sopenharmony_ci} 11648c2ecf20Sopenharmony_ci 11658c2ecf20Sopenharmony_ci/** 11668c2ecf20Sopenharmony_ci * doc_erase - Erase a portion of the chip 11678c2ecf20Sopenharmony_ci * @mtd: the device 11688c2ecf20Sopenharmony_ci * @info: the erase info 11698c2ecf20Sopenharmony_ci * 11708c2ecf20Sopenharmony_ci * Erase a bunch of contiguous blocks, by pairs, as a "mtd" page of 1024 is 11718c2ecf20Sopenharmony_ci * split into 2 pages of 512 bytes on 2 contiguous blocks. 11728c2ecf20Sopenharmony_ci * 11738c2ecf20Sopenharmony_ci * Returns 0 if erase successful, -EINVAL if addressing error, -EIO if erase 11748c2ecf20Sopenharmony_ci * issue 11758c2ecf20Sopenharmony_ci */ 11768c2ecf20Sopenharmony_cistatic int doc_erase(struct mtd_info *mtd, struct erase_info *info) 11778c2ecf20Sopenharmony_ci{ 11788c2ecf20Sopenharmony_ci struct docg3 *docg3 = mtd->priv; 11798c2ecf20Sopenharmony_ci uint64_t len; 11808c2ecf20Sopenharmony_ci int block0, block1, page, ret = 0, ofs = 0; 11818c2ecf20Sopenharmony_ci 11828c2ecf20Sopenharmony_ci doc_dbg("doc_erase(from=%lld, len=%lld\n", info->addr, info->len); 11838c2ecf20Sopenharmony_ci 11848c2ecf20Sopenharmony_ci calc_block_sector(info->addr + info->len, &block0, &block1, &page, 11858c2ecf20Sopenharmony_ci &ofs, docg3->reliable); 11868c2ecf20Sopenharmony_ci if (info->addr + info->len > mtd->size || page || ofs) 11878c2ecf20Sopenharmony_ci return -EINVAL; 11888c2ecf20Sopenharmony_ci 11898c2ecf20Sopenharmony_ci calc_block_sector(info->addr, &block0, &block1, &page, &ofs, 11908c2ecf20Sopenharmony_ci docg3->reliable); 11918c2ecf20Sopenharmony_ci mutex_lock(&docg3->cascade->lock); 11928c2ecf20Sopenharmony_ci doc_set_device_id(docg3, docg3->device_id); 11938c2ecf20Sopenharmony_ci doc_set_reliable_mode(docg3); 11948c2ecf20Sopenharmony_ci for (len = info->len; !ret && len > 0; len -= mtd->erasesize) { 11958c2ecf20Sopenharmony_ci ret = doc_erase_block(docg3, block0, block1); 11968c2ecf20Sopenharmony_ci block0 += 2; 11978c2ecf20Sopenharmony_ci block1 += 2; 11988c2ecf20Sopenharmony_ci } 11998c2ecf20Sopenharmony_ci mutex_unlock(&docg3->cascade->lock); 12008c2ecf20Sopenharmony_ci 12018c2ecf20Sopenharmony_ci return ret; 12028c2ecf20Sopenharmony_ci} 12038c2ecf20Sopenharmony_ci 12048c2ecf20Sopenharmony_ci/** 12058c2ecf20Sopenharmony_ci * doc_write_page - Write a single page to the chip 12068c2ecf20Sopenharmony_ci * @docg3: the device 12078c2ecf20Sopenharmony_ci * @to: the offset from first block and first page, in bytes, aligned on page 12088c2ecf20Sopenharmony_ci * size 12098c2ecf20Sopenharmony_ci * @buf: buffer to get bytes from 12108c2ecf20Sopenharmony_ci * @oob: buffer to get out of band bytes from (can be NULL if no OOB should be 12118c2ecf20Sopenharmony_ci * written) 12128c2ecf20Sopenharmony_ci * @autoecc: if 0, all 16 bytes from OOB are taken, regardless of HW Hamming or 12138c2ecf20Sopenharmony_ci * BCH computations. If 1, only bytes 0-7 and byte 15 are taken, 12148c2ecf20Sopenharmony_ci * remaining ones are filled with hardware Hamming and BCH 12158c2ecf20Sopenharmony_ci * computations. Its value is not meaningfull is oob == NULL. 12168c2ecf20Sopenharmony_ci * 12178c2ecf20Sopenharmony_ci * Write one full page (ie. 1 page split on two planes), of 512 bytes, with the 12188c2ecf20Sopenharmony_ci * OOB data. The OOB ECC is automatically computed by the hardware Hamming and 12198c2ecf20Sopenharmony_ci * BCH generator if autoecc is not null. 12208c2ecf20Sopenharmony_ci * 12218c2ecf20Sopenharmony_ci * Returns 0 if write successful, -EIO if write error, -EAGAIN if timeout 12228c2ecf20Sopenharmony_ci */ 12238c2ecf20Sopenharmony_cistatic int doc_write_page(struct docg3 *docg3, loff_t to, const u_char *buf, 12248c2ecf20Sopenharmony_ci const u_char *oob, int autoecc) 12258c2ecf20Sopenharmony_ci{ 12268c2ecf20Sopenharmony_ci int block0, block1, page, ret, ofs = 0; 12278c2ecf20Sopenharmony_ci u8 hwecc[DOC_ECC_BCH_SIZE], hamming; 12288c2ecf20Sopenharmony_ci 12298c2ecf20Sopenharmony_ci doc_dbg("doc_write_page(to=%lld)\n", to); 12308c2ecf20Sopenharmony_ci calc_block_sector(to, &block0, &block1, &page, &ofs, docg3->reliable); 12318c2ecf20Sopenharmony_ci 12328c2ecf20Sopenharmony_ci doc_set_device_id(docg3, docg3->device_id); 12338c2ecf20Sopenharmony_ci ret = doc_reset_seq(docg3); 12348c2ecf20Sopenharmony_ci if (ret) 12358c2ecf20Sopenharmony_ci goto err; 12368c2ecf20Sopenharmony_ci 12378c2ecf20Sopenharmony_ci /* Program the flash address block and page */ 12388c2ecf20Sopenharmony_ci ret = doc_write_seek(docg3, block0, block1, page, ofs); 12398c2ecf20Sopenharmony_ci if (ret) 12408c2ecf20Sopenharmony_ci goto err; 12418c2ecf20Sopenharmony_ci 12428c2ecf20Sopenharmony_ci doc_write_page_ecc_init(docg3, DOC_ECC_BCH_TOTAL_BYTES); 12438c2ecf20Sopenharmony_ci doc_delay(docg3, 2); 12448c2ecf20Sopenharmony_ci doc_write_page_putbytes(docg3, DOC_LAYOUT_PAGE_SIZE, buf); 12458c2ecf20Sopenharmony_ci 12468c2ecf20Sopenharmony_ci if (oob && autoecc) { 12478c2ecf20Sopenharmony_ci doc_write_page_putbytes(docg3, DOC_LAYOUT_OOB_PAGEINFO_SZ, oob); 12488c2ecf20Sopenharmony_ci doc_delay(docg3, 2); 12498c2ecf20Sopenharmony_ci oob += DOC_LAYOUT_OOB_UNUSED_OFS; 12508c2ecf20Sopenharmony_ci 12518c2ecf20Sopenharmony_ci hamming = doc_register_readb(docg3, DOC_HAMMINGPARITY); 12528c2ecf20Sopenharmony_ci doc_delay(docg3, 2); 12538c2ecf20Sopenharmony_ci doc_write_page_putbytes(docg3, DOC_LAYOUT_OOB_HAMMING_SZ, 12548c2ecf20Sopenharmony_ci &hamming); 12558c2ecf20Sopenharmony_ci doc_delay(docg3, 2); 12568c2ecf20Sopenharmony_ci 12578c2ecf20Sopenharmony_ci doc_get_bch_hw_ecc(docg3, hwecc); 12588c2ecf20Sopenharmony_ci doc_write_page_putbytes(docg3, DOC_LAYOUT_OOB_BCH_SZ, hwecc); 12598c2ecf20Sopenharmony_ci doc_delay(docg3, 2); 12608c2ecf20Sopenharmony_ci 12618c2ecf20Sopenharmony_ci doc_write_page_putbytes(docg3, DOC_LAYOUT_OOB_UNUSED_SZ, oob); 12628c2ecf20Sopenharmony_ci } 12638c2ecf20Sopenharmony_ci if (oob && !autoecc) 12648c2ecf20Sopenharmony_ci doc_write_page_putbytes(docg3, DOC_LAYOUT_OOB_SIZE, oob); 12658c2ecf20Sopenharmony_ci 12668c2ecf20Sopenharmony_ci doc_delay(docg3, 2); 12678c2ecf20Sopenharmony_ci doc_page_finish(docg3); 12688c2ecf20Sopenharmony_ci doc_delay(docg3, 2); 12698c2ecf20Sopenharmony_ci doc_flash_command(docg3, DOC_CMD_PROG_CYCLE2); 12708c2ecf20Sopenharmony_ci doc_delay(docg3, 2); 12718c2ecf20Sopenharmony_ci 12728c2ecf20Sopenharmony_ci /* 12738c2ecf20Sopenharmony_ci * The wait status will perform another doc_page_finish() call, but that 12748c2ecf20Sopenharmony_ci * seems to please the docg3, so leave it. 12758c2ecf20Sopenharmony_ci */ 12768c2ecf20Sopenharmony_ci ret = doc_write_erase_wait_status(docg3); 12778c2ecf20Sopenharmony_ci return ret; 12788c2ecf20Sopenharmony_cierr: 12798c2ecf20Sopenharmony_ci doc_read_page_finish(docg3); 12808c2ecf20Sopenharmony_ci return ret; 12818c2ecf20Sopenharmony_ci} 12828c2ecf20Sopenharmony_ci 12838c2ecf20Sopenharmony_ci/** 12848c2ecf20Sopenharmony_ci * doc_guess_autoecc - Guess autoecc mode from mbd_oob_ops 12858c2ecf20Sopenharmony_ci * @ops: the oob operations 12868c2ecf20Sopenharmony_ci * 12878c2ecf20Sopenharmony_ci * Returns 0 or 1 if success, -EINVAL if invalid oob mode 12888c2ecf20Sopenharmony_ci */ 12898c2ecf20Sopenharmony_cistatic int doc_guess_autoecc(struct mtd_oob_ops *ops) 12908c2ecf20Sopenharmony_ci{ 12918c2ecf20Sopenharmony_ci int autoecc; 12928c2ecf20Sopenharmony_ci 12938c2ecf20Sopenharmony_ci switch (ops->mode) { 12948c2ecf20Sopenharmony_ci case MTD_OPS_PLACE_OOB: 12958c2ecf20Sopenharmony_ci case MTD_OPS_AUTO_OOB: 12968c2ecf20Sopenharmony_ci autoecc = 1; 12978c2ecf20Sopenharmony_ci break; 12988c2ecf20Sopenharmony_ci case MTD_OPS_RAW: 12998c2ecf20Sopenharmony_ci autoecc = 0; 13008c2ecf20Sopenharmony_ci break; 13018c2ecf20Sopenharmony_ci default: 13028c2ecf20Sopenharmony_ci autoecc = -EINVAL; 13038c2ecf20Sopenharmony_ci } 13048c2ecf20Sopenharmony_ci return autoecc; 13058c2ecf20Sopenharmony_ci} 13068c2ecf20Sopenharmony_ci 13078c2ecf20Sopenharmony_ci/** 13088c2ecf20Sopenharmony_ci * doc_fill_autooob - Fill a 16 bytes OOB from 8 non-ECC bytes 13098c2ecf20Sopenharmony_ci * @dst: the target 16 bytes OOB buffer 13108c2ecf20Sopenharmony_ci * @oobsrc: the source 8 bytes non-ECC OOB buffer 13118c2ecf20Sopenharmony_ci * 13128c2ecf20Sopenharmony_ci */ 13138c2ecf20Sopenharmony_cistatic void doc_fill_autooob(u8 *dst, u8 *oobsrc) 13148c2ecf20Sopenharmony_ci{ 13158c2ecf20Sopenharmony_ci memcpy(dst, oobsrc, DOC_LAYOUT_OOB_PAGEINFO_SZ); 13168c2ecf20Sopenharmony_ci dst[DOC_LAYOUT_OOB_UNUSED_OFS] = oobsrc[DOC_LAYOUT_OOB_PAGEINFO_SZ]; 13178c2ecf20Sopenharmony_ci} 13188c2ecf20Sopenharmony_ci 13198c2ecf20Sopenharmony_ci/** 13208c2ecf20Sopenharmony_ci * doc_backup_oob - Backup OOB into docg3 structure 13218c2ecf20Sopenharmony_ci * @docg3: the device 13228c2ecf20Sopenharmony_ci * @to: the page offset in the chip 13238c2ecf20Sopenharmony_ci * @ops: the OOB size and buffer 13248c2ecf20Sopenharmony_ci * 13258c2ecf20Sopenharmony_ci * As the docg3 should write a page with its OOB in one pass, and some userland 13268c2ecf20Sopenharmony_ci * applications do write_oob() to setup the OOB and then write(), store the OOB 13278c2ecf20Sopenharmony_ci * into a temporary storage. This is very dangerous, as 2 concurrent 13288c2ecf20Sopenharmony_ci * applications could store an OOB, and then write their pages (which will 13298c2ecf20Sopenharmony_ci * result into one having its OOB corrupted). 13308c2ecf20Sopenharmony_ci * 13318c2ecf20Sopenharmony_ci * The only reliable way would be for userland to call doc_write_oob() with both 13328c2ecf20Sopenharmony_ci * the page data _and_ the OOB area. 13338c2ecf20Sopenharmony_ci * 13348c2ecf20Sopenharmony_ci * Returns 0 if success, -EINVAL if ops content invalid 13358c2ecf20Sopenharmony_ci */ 13368c2ecf20Sopenharmony_cistatic int doc_backup_oob(struct docg3 *docg3, loff_t to, 13378c2ecf20Sopenharmony_ci struct mtd_oob_ops *ops) 13388c2ecf20Sopenharmony_ci{ 13398c2ecf20Sopenharmony_ci int ooblen = ops->ooblen, autoecc; 13408c2ecf20Sopenharmony_ci 13418c2ecf20Sopenharmony_ci if (ooblen != DOC_LAYOUT_OOB_SIZE) 13428c2ecf20Sopenharmony_ci return -EINVAL; 13438c2ecf20Sopenharmony_ci autoecc = doc_guess_autoecc(ops); 13448c2ecf20Sopenharmony_ci if (autoecc < 0) 13458c2ecf20Sopenharmony_ci return autoecc; 13468c2ecf20Sopenharmony_ci 13478c2ecf20Sopenharmony_ci docg3->oob_write_ofs = to; 13488c2ecf20Sopenharmony_ci docg3->oob_autoecc = autoecc; 13498c2ecf20Sopenharmony_ci if (ops->mode == MTD_OPS_AUTO_OOB) { 13508c2ecf20Sopenharmony_ci doc_fill_autooob(docg3->oob_write_buf, ops->oobbuf); 13518c2ecf20Sopenharmony_ci ops->oobretlen = 8; 13528c2ecf20Sopenharmony_ci } else { 13538c2ecf20Sopenharmony_ci memcpy(docg3->oob_write_buf, ops->oobbuf, DOC_LAYOUT_OOB_SIZE); 13548c2ecf20Sopenharmony_ci ops->oobretlen = DOC_LAYOUT_OOB_SIZE; 13558c2ecf20Sopenharmony_ci } 13568c2ecf20Sopenharmony_ci return 0; 13578c2ecf20Sopenharmony_ci} 13588c2ecf20Sopenharmony_ci 13598c2ecf20Sopenharmony_ci/** 13608c2ecf20Sopenharmony_ci * doc_write_oob - Write out of band bytes to flash 13618c2ecf20Sopenharmony_ci * @mtd: the device 13628c2ecf20Sopenharmony_ci * @ofs: the offset from first block and first page, in bytes, aligned on page 13638c2ecf20Sopenharmony_ci * size 13648c2ecf20Sopenharmony_ci * @ops: the mtd oob structure 13658c2ecf20Sopenharmony_ci * 13668c2ecf20Sopenharmony_ci * Either write OOB data into a temporary buffer, for the subsequent write 13678c2ecf20Sopenharmony_ci * page. The provided OOB should be 16 bytes long. If a data buffer is provided 13688c2ecf20Sopenharmony_ci * as well, issue the page write. 13698c2ecf20Sopenharmony_ci * Or provide data without OOB, and then a all zeroed OOB will be used (ECC will 13708c2ecf20Sopenharmony_ci * still be filled in if asked for). 13718c2ecf20Sopenharmony_ci * 13728c2ecf20Sopenharmony_ci * Returns 0 is successful, EINVAL if length is not 14 bytes 13738c2ecf20Sopenharmony_ci */ 13748c2ecf20Sopenharmony_cistatic int doc_write_oob(struct mtd_info *mtd, loff_t ofs, 13758c2ecf20Sopenharmony_ci struct mtd_oob_ops *ops) 13768c2ecf20Sopenharmony_ci{ 13778c2ecf20Sopenharmony_ci struct docg3 *docg3 = mtd->priv; 13788c2ecf20Sopenharmony_ci int ret, autoecc, oobdelta; 13798c2ecf20Sopenharmony_ci u8 *oobbuf = ops->oobbuf; 13808c2ecf20Sopenharmony_ci u8 *buf = ops->datbuf; 13818c2ecf20Sopenharmony_ci size_t len, ooblen; 13828c2ecf20Sopenharmony_ci u8 oob[DOC_LAYOUT_OOB_SIZE]; 13838c2ecf20Sopenharmony_ci 13848c2ecf20Sopenharmony_ci if (buf) 13858c2ecf20Sopenharmony_ci len = ops->len; 13868c2ecf20Sopenharmony_ci else 13878c2ecf20Sopenharmony_ci len = 0; 13888c2ecf20Sopenharmony_ci if (oobbuf) 13898c2ecf20Sopenharmony_ci ooblen = ops->ooblen; 13908c2ecf20Sopenharmony_ci else 13918c2ecf20Sopenharmony_ci ooblen = 0; 13928c2ecf20Sopenharmony_ci 13938c2ecf20Sopenharmony_ci if (oobbuf && ops->mode == MTD_OPS_PLACE_OOB) 13948c2ecf20Sopenharmony_ci oobbuf += ops->ooboffs; 13958c2ecf20Sopenharmony_ci 13968c2ecf20Sopenharmony_ci doc_dbg("doc_write_oob(from=%lld, mode=%d, data=(%p:%zu), oob=(%p:%zu))\n", 13978c2ecf20Sopenharmony_ci ofs, ops->mode, buf, len, oobbuf, ooblen); 13988c2ecf20Sopenharmony_ci switch (ops->mode) { 13998c2ecf20Sopenharmony_ci case MTD_OPS_PLACE_OOB: 14008c2ecf20Sopenharmony_ci case MTD_OPS_RAW: 14018c2ecf20Sopenharmony_ci oobdelta = mtd->oobsize; 14028c2ecf20Sopenharmony_ci break; 14038c2ecf20Sopenharmony_ci case MTD_OPS_AUTO_OOB: 14048c2ecf20Sopenharmony_ci oobdelta = mtd->oobavail; 14058c2ecf20Sopenharmony_ci break; 14068c2ecf20Sopenharmony_ci default: 14078c2ecf20Sopenharmony_ci return -EINVAL; 14088c2ecf20Sopenharmony_ci } 14098c2ecf20Sopenharmony_ci if ((len % DOC_LAYOUT_PAGE_SIZE) || (ooblen % oobdelta) || 14108c2ecf20Sopenharmony_ci (ofs % DOC_LAYOUT_PAGE_SIZE)) 14118c2ecf20Sopenharmony_ci return -EINVAL; 14128c2ecf20Sopenharmony_ci if (len && ooblen && 14138c2ecf20Sopenharmony_ci (len / DOC_LAYOUT_PAGE_SIZE) != (ooblen / oobdelta)) 14148c2ecf20Sopenharmony_ci return -EINVAL; 14158c2ecf20Sopenharmony_ci 14168c2ecf20Sopenharmony_ci ops->oobretlen = 0; 14178c2ecf20Sopenharmony_ci ops->retlen = 0; 14188c2ecf20Sopenharmony_ci ret = 0; 14198c2ecf20Sopenharmony_ci if (len == 0 && ooblen == 0) 14208c2ecf20Sopenharmony_ci return -EINVAL; 14218c2ecf20Sopenharmony_ci if (len == 0 && ooblen > 0) 14228c2ecf20Sopenharmony_ci return doc_backup_oob(docg3, ofs, ops); 14238c2ecf20Sopenharmony_ci 14248c2ecf20Sopenharmony_ci autoecc = doc_guess_autoecc(ops); 14258c2ecf20Sopenharmony_ci if (autoecc < 0) 14268c2ecf20Sopenharmony_ci return autoecc; 14278c2ecf20Sopenharmony_ci 14288c2ecf20Sopenharmony_ci mutex_lock(&docg3->cascade->lock); 14298c2ecf20Sopenharmony_ci while (!ret && len > 0) { 14308c2ecf20Sopenharmony_ci memset(oob, 0, sizeof(oob)); 14318c2ecf20Sopenharmony_ci if (ofs == docg3->oob_write_ofs) 14328c2ecf20Sopenharmony_ci memcpy(oob, docg3->oob_write_buf, DOC_LAYOUT_OOB_SIZE); 14338c2ecf20Sopenharmony_ci else if (ooblen > 0 && ops->mode == MTD_OPS_AUTO_OOB) 14348c2ecf20Sopenharmony_ci doc_fill_autooob(oob, oobbuf); 14358c2ecf20Sopenharmony_ci else if (ooblen > 0) 14368c2ecf20Sopenharmony_ci memcpy(oob, oobbuf, DOC_LAYOUT_OOB_SIZE); 14378c2ecf20Sopenharmony_ci ret = doc_write_page(docg3, ofs, buf, oob, autoecc); 14388c2ecf20Sopenharmony_ci 14398c2ecf20Sopenharmony_ci ofs += DOC_LAYOUT_PAGE_SIZE; 14408c2ecf20Sopenharmony_ci len -= DOC_LAYOUT_PAGE_SIZE; 14418c2ecf20Sopenharmony_ci buf += DOC_LAYOUT_PAGE_SIZE; 14428c2ecf20Sopenharmony_ci if (ooblen) { 14438c2ecf20Sopenharmony_ci oobbuf += oobdelta; 14448c2ecf20Sopenharmony_ci ooblen -= oobdelta; 14458c2ecf20Sopenharmony_ci ops->oobretlen += oobdelta; 14468c2ecf20Sopenharmony_ci } 14478c2ecf20Sopenharmony_ci ops->retlen += DOC_LAYOUT_PAGE_SIZE; 14488c2ecf20Sopenharmony_ci } 14498c2ecf20Sopenharmony_ci 14508c2ecf20Sopenharmony_ci doc_set_device_id(docg3, 0); 14518c2ecf20Sopenharmony_ci mutex_unlock(&docg3->cascade->lock); 14528c2ecf20Sopenharmony_ci return ret; 14538c2ecf20Sopenharmony_ci} 14548c2ecf20Sopenharmony_ci 14558c2ecf20Sopenharmony_cistatic struct docg3 *sysfs_dev2docg3(struct device *dev, 14568c2ecf20Sopenharmony_ci struct device_attribute *attr) 14578c2ecf20Sopenharmony_ci{ 14588c2ecf20Sopenharmony_ci int floor; 14598c2ecf20Sopenharmony_ci struct mtd_info **docg3_floors = dev_get_drvdata(dev); 14608c2ecf20Sopenharmony_ci 14618c2ecf20Sopenharmony_ci floor = attr->attr.name[1] - '0'; 14628c2ecf20Sopenharmony_ci if (floor < 0 || floor >= DOC_MAX_NBFLOORS) 14638c2ecf20Sopenharmony_ci return NULL; 14648c2ecf20Sopenharmony_ci else 14658c2ecf20Sopenharmony_ci return docg3_floors[floor]->priv; 14668c2ecf20Sopenharmony_ci} 14678c2ecf20Sopenharmony_ci 14688c2ecf20Sopenharmony_cistatic ssize_t dps0_is_key_locked(struct device *dev, 14698c2ecf20Sopenharmony_ci struct device_attribute *attr, char *buf) 14708c2ecf20Sopenharmony_ci{ 14718c2ecf20Sopenharmony_ci struct docg3 *docg3 = sysfs_dev2docg3(dev, attr); 14728c2ecf20Sopenharmony_ci int dps0; 14738c2ecf20Sopenharmony_ci 14748c2ecf20Sopenharmony_ci mutex_lock(&docg3->cascade->lock); 14758c2ecf20Sopenharmony_ci doc_set_device_id(docg3, docg3->device_id); 14768c2ecf20Sopenharmony_ci dps0 = doc_register_readb(docg3, DOC_DPS0_STATUS); 14778c2ecf20Sopenharmony_ci doc_set_device_id(docg3, 0); 14788c2ecf20Sopenharmony_ci mutex_unlock(&docg3->cascade->lock); 14798c2ecf20Sopenharmony_ci 14808c2ecf20Sopenharmony_ci return sprintf(buf, "%d\n", !(dps0 & DOC_DPS_KEY_OK)); 14818c2ecf20Sopenharmony_ci} 14828c2ecf20Sopenharmony_ci 14838c2ecf20Sopenharmony_cistatic ssize_t dps1_is_key_locked(struct device *dev, 14848c2ecf20Sopenharmony_ci struct device_attribute *attr, char *buf) 14858c2ecf20Sopenharmony_ci{ 14868c2ecf20Sopenharmony_ci struct docg3 *docg3 = sysfs_dev2docg3(dev, attr); 14878c2ecf20Sopenharmony_ci int dps1; 14888c2ecf20Sopenharmony_ci 14898c2ecf20Sopenharmony_ci mutex_lock(&docg3->cascade->lock); 14908c2ecf20Sopenharmony_ci doc_set_device_id(docg3, docg3->device_id); 14918c2ecf20Sopenharmony_ci dps1 = doc_register_readb(docg3, DOC_DPS1_STATUS); 14928c2ecf20Sopenharmony_ci doc_set_device_id(docg3, 0); 14938c2ecf20Sopenharmony_ci mutex_unlock(&docg3->cascade->lock); 14948c2ecf20Sopenharmony_ci 14958c2ecf20Sopenharmony_ci return sprintf(buf, "%d\n", !(dps1 & DOC_DPS_KEY_OK)); 14968c2ecf20Sopenharmony_ci} 14978c2ecf20Sopenharmony_ci 14988c2ecf20Sopenharmony_cistatic ssize_t dps0_insert_key(struct device *dev, 14998c2ecf20Sopenharmony_ci struct device_attribute *attr, 15008c2ecf20Sopenharmony_ci const char *buf, size_t count) 15018c2ecf20Sopenharmony_ci{ 15028c2ecf20Sopenharmony_ci struct docg3 *docg3 = sysfs_dev2docg3(dev, attr); 15038c2ecf20Sopenharmony_ci int i; 15048c2ecf20Sopenharmony_ci 15058c2ecf20Sopenharmony_ci if (count != DOC_LAYOUT_DPS_KEY_LENGTH) 15068c2ecf20Sopenharmony_ci return -EINVAL; 15078c2ecf20Sopenharmony_ci 15088c2ecf20Sopenharmony_ci mutex_lock(&docg3->cascade->lock); 15098c2ecf20Sopenharmony_ci doc_set_device_id(docg3, docg3->device_id); 15108c2ecf20Sopenharmony_ci for (i = 0; i < DOC_LAYOUT_DPS_KEY_LENGTH; i++) 15118c2ecf20Sopenharmony_ci doc_writeb(docg3, buf[i], DOC_DPS0_KEY); 15128c2ecf20Sopenharmony_ci doc_set_device_id(docg3, 0); 15138c2ecf20Sopenharmony_ci mutex_unlock(&docg3->cascade->lock); 15148c2ecf20Sopenharmony_ci return count; 15158c2ecf20Sopenharmony_ci} 15168c2ecf20Sopenharmony_ci 15178c2ecf20Sopenharmony_cistatic ssize_t dps1_insert_key(struct device *dev, 15188c2ecf20Sopenharmony_ci struct device_attribute *attr, 15198c2ecf20Sopenharmony_ci const char *buf, size_t count) 15208c2ecf20Sopenharmony_ci{ 15218c2ecf20Sopenharmony_ci struct docg3 *docg3 = sysfs_dev2docg3(dev, attr); 15228c2ecf20Sopenharmony_ci int i; 15238c2ecf20Sopenharmony_ci 15248c2ecf20Sopenharmony_ci if (count != DOC_LAYOUT_DPS_KEY_LENGTH) 15258c2ecf20Sopenharmony_ci return -EINVAL; 15268c2ecf20Sopenharmony_ci 15278c2ecf20Sopenharmony_ci mutex_lock(&docg3->cascade->lock); 15288c2ecf20Sopenharmony_ci doc_set_device_id(docg3, docg3->device_id); 15298c2ecf20Sopenharmony_ci for (i = 0; i < DOC_LAYOUT_DPS_KEY_LENGTH; i++) 15308c2ecf20Sopenharmony_ci doc_writeb(docg3, buf[i], DOC_DPS1_KEY); 15318c2ecf20Sopenharmony_ci doc_set_device_id(docg3, 0); 15328c2ecf20Sopenharmony_ci mutex_unlock(&docg3->cascade->lock); 15338c2ecf20Sopenharmony_ci return count; 15348c2ecf20Sopenharmony_ci} 15358c2ecf20Sopenharmony_ci 15368c2ecf20Sopenharmony_ci#define FLOOR_SYSFS(id) { \ 15378c2ecf20Sopenharmony_ci __ATTR(f##id##_dps0_is_keylocked, S_IRUGO, dps0_is_key_locked, NULL), \ 15388c2ecf20Sopenharmony_ci __ATTR(f##id##_dps1_is_keylocked, S_IRUGO, dps1_is_key_locked, NULL), \ 15398c2ecf20Sopenharmony_ci __ATTR(f##id##_dps0_protection_key, S_IWUSR|S_IWGRP, NULL, dps0_insert_key), \ 15408c2ecf20Sopenharmony_ci __ATTR(f##id##_dps1_protection_key, S_IWUSR|S_IWGRP, NULL, dps1_insert_key), \ 15418c2ecf20Sopenharmony_ci} 15428c2ecf20Sopenharmony_ci 15438c2ecf20Sopenharmony_cistatic struct device_attribute doc_sys_attrs[DOC_MAX_NBFLOORS][4] = { 15448c2ecf20Sopenharmony_ci FLOOR_SYSFS(0), FLOOR_SYSFS(1), FLOOR_SYSFS(2), FLOOR_SYSFS(3) 15458c2ecf20Sopenharmony_ci}; 15468c2ecf20Sopenharmony_ci 15478c2ecf20Sopenharmony_cistatic int doc_register_sysfs(struct platform_device *pdev, 15488c2ecf20Sopenharmony_ci struct docg3_cascade *cascade) 15498c2ecf20Sopenharmony_ci{ 15508c2ecf20Sopenharmony_ci struct device *dev = &pdev->dev; 15518c2ecf20Sopenharmony_ci int floor; 15528c2ecf20Sopenharmony_ci int ret; 15538c2ecf20Sopenharmony_ci int i; 15548c2ecf20Sopenharmony_ci 15558c2ecf20Sopenharmony_ci for (floor = 0; 15568c2ecf20Sopenharmony_ci floor < DOC_MAX_NBFLOORS && cascade->floors[floor]; 15578c2ecf20Sopenharmony_ci floor++) { 15588c2ecf20Sopenharmony_ci for (i = 0; i < 4; i++) { 15598c2ecf20Sopenharmony_ci ret = device_create_file(dev, &doc_sys_attrs[floor][i]); 15608c2ecf20Sopenharmony_ci if (ret) 15618c2ecf20Sopenharmony_ci goto remove_files; 15628c2ecf20Sopenharmony_ci } 15638c2ecf20Sopenharmony_ci } 15648c2ecf20Sopenharmony_ci 15658c2ecf20Sopenharmony_ci return 0; 15668c2ecf20Sopenharmony_ci 15678c2ecf20Sopenharmony_ciremove_files: 15688c2ecf20Sopenharmony_ci do { 15698c2ecf20Sopenharmony_ci while (--i >= 0) 15708c2ecf20Sopenharmony_ci device_remove_file(dev, &doc_sys_attrs[floor][i]); 15718c2ecf20Sopenharmony_ci i = 4; 15728c2ecf20Sopenharmony_ci } while (--floor >= 0); 15738c2ecf20Sopenharmony_ci 15748c2ecf20Sopenharmony_ci return ret; 15758c2ecf20Sopenharmony_ci} 15768c2ecf20Sopenharmony_ci 15778c2ecf20Sopenharmony_cistatic void doc_unregister_sysfs(struct platform_device *pdev, 15788c2ecf20Sopenharmony_ci struct docg3_cascade *cascade) 15798c2ecf20Sopenharmony_ci{ 15808c2ecf20Sopenharmony_ci struct device *dev = &pdev->dev; 15818c2ecf20Sopenharmony_ci int floor, i; 15828c2ecf20Sopenharmony_ci 15838c2ecf20Sopenharmony_ci for (floor = 0; floor < DOC_MAX_NBFLOORS && cascade->floors[floor]; 15848c2ecf20Sopenharmony_ci floor++) 15858c2ecf20Sopenharmony_ci for (i = 0; i < 4; i++) 15868c2ecf20Sopenharmony_ci device_remove_file(dev, &doc_sys_attrs[floor][i]); 15878c2ecf20Sopenharmony_ci} 15888c2ecf20Sopenharmony_ci 15898c2ecf20Sopenharmony_ci/* 15908c2ecf20Sopenharmony_ci * Debug sysfs entries 15918c2ecf20Sopenharmony_ci */ 15928c2ecf20Sopenharmony_cistatic int flashcontrol_show(struct seq_file *s, void *p) 15938c2ecf20Sopenharmony_ci{ 15948c2ecf20Sopenharmony_ci struct docg3 *docg3 = (struct docg3 *)s->private; 15958c2ecf20Sopenharmony_ci 15968c2ecf20Sopenharmony_ci u8 fctrl; 15978c2ecf20Sopenharmony_ci 15988c2ecf20Sopenharmony_ci mutex_lock(&docg3->cascade->lock); 15998c2ecf20Sopenharmony_ci fctrl = doc_register_readb(docg3, DOC_FLASHCONTROL); 16008c2ecf20Sopenharmony_ci mutex_unlock(&docg3->cascade->lock); 16018c2ecf20Sopenharmony_ci 16028c2ecf20Sopenharmony_ci seq_printf(s, "FlashControl : 0x%02x (%s,CE# %s,%s,%s,flash %s)\n", 16038c2ecf20Sopenharmony_ci fctrl, 16048c2ecf20Sopenharmony_ci fctrl & DOC_CTRL_VIOLATION ? "protocol violation" : "-", 16058c2ecf20Sopenharmony_ci fctrl & DOC_CTRL_CE ? "active" : "inactive", 16068c2ecf20Sopenharmony_ci fctrl & DOC_CTRL_PROTECTION_ERROR ? "protection error" : "-", 16078c2ecf20Sopenharmony_ci fctrl & DOC_CTRL_SEQUENCE_ERROR ? "sequence error" : "-", 16088c2ecf20Sopenharmony_ci fctrl & DOC_CTRL_FLASHREADY ? "ready" : "not ready"); 16098c2ecf20Sopenharmony_ci 16108c2ecf20Sopenharmony_ci return 0; 16118c2ecf20Sopenharmony_ci} 16128c2ecf20Sopenharmony_ciDEFINE_SHOW_ATTRIBUTE(flashcontrol); 16138c2ecf20Sopenharmony_ci 16148c2ecf20Sopenharmony_cistatic int asic_mode_show(struct seq_file *s, void *p) 16158c2ecf20Sopenharmony_ci{ 16168c2ecf20Sopenharmony_ci struct docg3 *docg3 = (struct docg3 *)s->private; 16178c2ecf20Sopenharmony_ci 16188c2ecf20Sopenharmony_ci int pctrl, mode; 16198c2ecf20Sopenharmony_ci 16208c2ecf20Sopenharmony_ci mutex_lock(&docg3->cascade->lock); 16218c2ecf20Sopenharmony_ci pctrl = doc_register_readb(docg3, DOC_ASICMODE); 16228c2ecf20Sopenharmony_ci mode = pctrl & 0x03; 16238c2ecf20Sopenharmony_ci mutex_unlock(&docg3->cascade->lock); 16248c2ecf20Sopenharmony_ci 16258c2ecf20Sopenharmony_ci seq_printf(s, 16268c2ecf20Sopenharmony_ci "%04x : RAM_WE=%d,RSTIN_RESET=%d,BDETCT_RESET=%d,WRITE_ENABLE=%d,POWERDOWN=%d,MODE=%d%d (", 16278c2ecf20Sopenharmony_ci pctrl, 16288c2ecf20Sopenharmony_ci pctrl & DOC_ASICMODE_RAM_WE ? 1 : 0, 16298c2ecf20Sopenharmony_ci pctrl & DOC_ASICMODE_RSTIN_RESET ? 1 : 0, 16308c2ecf20Sopenharmony_ci pctrl & DOC_ASICMODE_BDETCT_RESET ? 1 : 0, 16318c2ecf20Sopenharmony_ci pctrl & DOC_ASICMODE_MDWREN ? 1 : 0, 16328c2ecf20Sopenharmony_ci pctrl & DOC_ASICMODE_POWERDOWN ? 1 : 0, 16338c2ecf20Sopenharmony_ci mode >> 1, mode & 0x1); 16348c2ecf20Sopenharmony_ci 16358c2ecf20Sopenharmony_ci switch (mode) { 16368c2ecf20Sopenharmony_ci case DOC_ASICMODE_RESET: 16378c2ecf20Sopenharmony_ci seq_puts(s, "reset"); 16388c2ecf20Sopenharmony_ci break; 16398c2ecf20Sopenharmony_ci case DOC_ASICMODE_NORMAL: 16408c2ecf20Sopenharmony_ci seq_puts(s, "normal"); 16418c2ecf20Sopenharmony_ci break; 16428c2ecf20Sopenharmony_ci case DOC_ASICMODE_POWERDOWN: 16438c2ecf20Sopenharmony_ci seq_puts(s, "powerdown"); 16448c2ecf20Sopenharmony_ci break; 16458c2ecf20Sopenharmony_ci } 16468c2ecf20Sopenharmony_ci seq_puts(s, ")\n"); 16478c2ecf20Sopenharmony_ci return 0; 16488c2ecf20Sopenharmony_ci} 16498c2ecf20Sopenharmony_ciDEFINE_SHOW_ATTRIBUTE(asic_mode); 16508c2ecf20Sopenharmony_ci 16518c2ecf20Sopenharmony_cistatic int device_id_show(struct seq_file *s, void *p) 16528c2ecf20Sopenharmony_ci{ 16538c2ecf20Sopenharmony_ci struct docg3 *docg3 = (struct docg3 *)s->private; 16548c2ecf20Sopenharmony_ci int id; 16558c2ecf20Sopenharmony_ci 16568c2ecf20Sopenharmony_ci mutex_lock(&docg3->cascade->lock); 16578c2ecf20Sopenharmony_ci id = doc_register_readb(docg3, DOC_DEVICESELECT); 16588c2ecf20Sopenharmony_ci mutex_unlock(&docg3->cascade->lock); 16598c2ecf20Sopenharmony_ci 16608c2ecf20Sopenharmony_ci seq_printf(s, "DeviceId = %d\n", id); 16618c2ecf20Sopenharmony_ci return 0; 16628c2ecf20Sopenharmony_ci} 16638c2ecf20Sopenharmony_ciDEFINE_SHOW_ATTRIBUTE(device_id); 16648c2ecf20Sopenharmony_ci 16658c2ecf20Sopenharmony_cistatic int protection_show(struct seq_file *s, void *p) 16668c2ecf20Sopenharmony_ci{ 16678c2ecf20Sopenharmony_ci struct docg3 *docg3 = (struct docg3 *)s->private; 16688c2ecf20Sopenharmony_ci int protect, dps0, dps0_low, dps0_high, dps1, dps1_low, dps1_high; 16698c2ecf20Sopenharmony_ci 16708c2ecf20Sopenharmony_ci mutex_lock(&docg3->cascade->lock); 16718c2ecf20Sopenharmony_ci protect = doc_register_readb(docg3, DOC_PROTECTION); 16728c2ecf20Sopenharmony_ci dps0 = doc_register_readb(docg3, DOC_DPS0_STATUS); 16738c2ecf20Sopenharmony_ci dps0_low = doc_register_readw(docg3, DOC_DPS0_ADDRLOW); 16748c2ecf20Sopenharmony_ci dps0_high = doc_register_readw(docg3, DOC_DPS0_ADDRHIGH); 16758c2ecf20Sopenharmony_ci dps1 = doc_register_readb(docg3, DOC_DPS1_STATUS); 16768c2ecf20Sopenharmony_ci dps1_low = doc_register_readw(docg3, DOC_DPS1_ADDRLOW); 16778c2ecf20Sopenharmony_ci dps1_high = doc_register_readw(docg3, DOC_DPS1_ADDRHIGH); 16788c2ecf20Sopenharmony_ci mutex_unlock(&docg3->cascade->lock); 16798c2ecf20Sopenharmony_ci 16808c2ecf20Sopenharmony_ci seq_printf(s, "Protection = 0x%02x (", protect); 16818c2ecf20Sopenharmony_ci if (protect & DOC_PROTECT_FOUNDRY_OTP_LOCK) 16828c2ecf20Sopenharmony_ci seq_puts(s, "FOUNDRY_OTP_LOCK,"); 16838c2ecf20Sopenharmony_ci if (protect & DOC_PROTECT_CUSTOMER_OTP_LOCK) 16848c2ecf20Sopenharmony_ci seq_puts(s, "CUSTOMER_OTP_LOCK,"); 16858c2ecf20Sopenharmony_ci if (protect & DOC_PROTECT_LOCK_INPUT) 16868c2ecf20Sopenharmony_ci seq_puts(s, "LOCK_INPUT,"); 16878c2ecf20Sopenharmony_ci if (protect & DOC_PROTECT_STICKY_LOCK) 16888c2ecf20Sopenharmony_ci seq_puts(s, "STICKY_LOCK,"); 16898c2ecf20Sopenharmony_ci if (protect & DOC_PROTECT_PROTECTION_ENABLED) 16908c2ecf20Sopenharmony_ci seq_puts(s, "PROTECTION ON,"); 16918c2ecf20Sopenharmony_ci if (protect & DOC_PROTECT_IPL_DOWNLOAD_LOCK) 16928c2ecf20Sopenharmony_ci seq_puts(s, "IPL_DOWNLOAD_LOCK,"); 16938c2ecf20Sopenharmony_ci if (protect & DOC_PROTECT_PROTECTION_ERROR) 16948c2ecf20Sopenharmony_ci seq_puts(s, "PROTECT_ERR,"); 16958c2ecf20Sopenharmony_ci else 16968c2ecf20Sopenharmony_ci seq_puts(s, "NO_PROTECT_ERR"); 16978c2ecf20Sopenharmony_ci seq_puts(s, ")\n"); 16988c2ecf20Sopenharmony_ci 16998c2ecf20Sopenharmony_ci seq_printf(s, "DPS0 = 0x%02x : Protected area [0x%x - 0x%x] : OTP=%d, READ=%d, WRITE=%d, HW_LOCK=%d, KEY_OK=%d\n", 17008c2ecf20Sopenharmony_ci dps0, dps0_low, dps0_high, 17018c2ecf20Sopenharmony_ci !!(dps0 & DOC_DPS_OTP_PROTECTED), 17028c2ecf20Sopenharmony_ci !!(dps0 & DOC_DPS_READ_PROTECTED), 17038c2ecf20Sopenharmony_ci !!(dps0 & DOC_DPS_WRITE_PROTECTED), 17048c2ecf20Sopenharmony_ci !!(dps0 & DOC_DPS_HW_LOCK_ENABLED), 17058c2ecf20Sopenharmony_ci !!(dps0 & DOC_DPS_KEY_OK)); 17068c2ecf20Sopenharmony_ci seq_printf(s, "DPS1 = 0x%02x : Protected area [0x%x - 0x%x] : OTP=%d, READ=%d, WRITE=%d, HW_LOCK=%d, KEY_OK=%d\n", 17078c2ecf20Sopenharmony_ci dps1, dps1_low, dps1_high, 17088c2ecf20Sopenharmony_ci !!(dps1 & DOC_DPS_OTP_PROTECTED), 17098c2ecf20Sopenharmony_ci !!(dps1 & DOC_DPS_READ_PROTECTED), 17108c2ecf20Sopenharmony_ci !!(dps1 & DOC_DPS_WRITE_PROTECTED), 17118c2ecf20Sopenharmony_ci !!(dps1 & DOC_DPS_HW_LOCK_ENABLED), 17128c2ecf20Sopenharmony_ci !!(dps1 & DOC_DPS_KEY_OK)); 17138c2ecf20Sopenharmony_ci return 0; 17148c2ecf20Sopenharmony_ci} 17158c2ecf20Sopenharmony_ciDEFINE_SHOW_ATTRIBUTE(protection); 17168c2ecf20Sopenharmony_ci 17178c2ecf20Sopenharmony_cistatic void __init doc_dbg_register(struct mtd_info *floor) 17188c2ecf20Sopenharmony_ci{ 17198c2ecf20Sopenharmony_ci struct dentry *root = floor->dbg.dfs_dir; 17208c2ecf20Sopenharmony_ci struct docg3 *docg3 = floor->priv; 17218c2ecf20Sopenharmony_ci 17228c2ecf20Sopenharmony_ci if (IS_ERR_OR_NULL(root)) { 17238c2ecf20Sopenharmony_ci if (IS_ENABLED(CONFIG_DEBUG_FS) && 17248c2ecf20Sopenharmony_ci !IS_ENABLED(CONFIG_MTD_PARTITIONED_MASTER)) 17258c2ecf20Sopenharmony_ci dev_warn(floor->dev.parent, 17268c2ecf20Sopenharmony_ci "CONFIG_MTD_PARTITIONED_MASTER must be enabled to expose debugfs stuff\n"); 17278c2ecf20Sopenharmony_ci return; 17288c2ecf20Sopenharmony_ci } 17298c2ecf20Sopenharmony_ci 17308c2ecf20Sopenharmony_ci debugfs_create_file("docg3_flashcontrol", S_IRUSR, root, docg3, 17318c2ecf20Sopenharmony_ci &flashcontrol_fops); 17328c2ecf20Sopenharmony_ci debugfs_create_file("docg3_asic_mode", S_IRUSR, root, docg3, 17338c2ecf20Sopenharmony_ci &asic_mode_fops); 17348c2ecf20Sopenharmony_ci debugfs_create_file("docg3_device_id", S_IRUSR, root, docg3, 17358c2ecf20Sopenharmony_ci &device_id_fops); 17368c2ecf20Sopenharmony_ci debugfs_create_file("docg3_protection", S_IRUSR, root, docg3, 17378c2ecf20Sopenharmony_ci &protection_fops); 17388c2ecf20Sopenharmony_ci} 17398c2ecf20Sopenharmony_ci 17408c2ecf20Sopenharmony_ci/** 17418c2ecf20Sopenharmony_ci * doc_set_driver_info - Fill the mtd_info structure and docg3 structure 17428c2ecf20Sopenharmony_ci * @chip_id: The chip ID of the supported chip 17438c2ecf20Sopenharmony_ci * @mtd: The structure to fill 17448c2ecf20Sopenharmony_ci */ 17458c2ecf20Sopenharmony_cistatic int __init doc_set_driver_info(int chip_id, struct mtd_info *mtd) 17468c2ecf20Sopenharmony_ci{ 17478c2ecf20Sopenharmony_ci struct docg3 *docg3 = mtd->priv; 17488c2ecf20Sopenharmony_ci int cfg; 17498c2ecf20Sopenharmony_ci 17508c2ecf20Sopenharmony_ci cfg = doc_register_readb(docg3, DOC_CONFIGURATION); 17518c2ecf20Sopenharmony_ci docg3->if_cfg = (cfg & DOC_CONF_IF_CFG ? 1 : 0); 17528c2ecf20Sopenharmony_ci docg3->reliable = reliable_mode; 17538c2ecf20Sopenharmony_ci 17548c2ecf20Sopenharmony_ci switch (chip_id) { 17558c2ecf20Sopenharmony_ci case DOC_CHIPID_G3: 17568c2ecf20Sopenharmony_ci mtd->name = devm_kasprintf(docg3->dev, GFP_KERNEL, "docg3.%d", 17578c2ecf20Sopenharmony_ci docg3->device_id); 17588c2ecf20Sopenharmony_ci if (!mtd->name) 17598c2ecf20Sopenharmony_ci return -ENOMEM; 17608c2ecf20Sopenharmony_ci docg3->max_block = 2047; 17618c2ecf20Sopenharmony_ci break; 17628c2ecf20Sopenharmony_ci } 17638c2ecf20Sopenharmony_ci mtd->type = MTD_NANDFLASH; 17648c2ecf20Sopenharmony_ci mtd->flags = MTD_CAP_NANDFLASH; 17658c2ecf20Sopenharmony_ci mtd->size = (docg3->max_block + 1) * DOC_LAYOUT_BLOCK_SIZE; 17668c2ecf20Sopenharmony_ci if (docg3->reliable == 2) 17678c2ecf20Sopenharmony_ci mtd->size /= 2; 17688c2ecf20Sopenharmony_ci mtd->erasesize = DOC_LAYOUT_BLOCK_SIZE * DOC_LAYOUT_NBPLANES; 17698c2ecf20Sopenharmony_ci if (docg3->reliable == 2) 17708c2ecf20Sopenharmony_ci mtd->erasesize /= 2; 17718c2ecf20Sopenharmony_ci mtd->writebufsize = mtd->writesize = DOC_LAYOUT_PAGE_SIZE; 17728c2ecf20Sopenharmony_ci mtd->oobsize = DOC_LAYOUT_OOB_SIZE; 17738c2ecf20Sopenharmony_ci mtd->_erase = doc_erase; 17748c2ecf20Sopenharmony_ci mtd->_read_oob = doc_read_oob; 17758c2ecf20Sopenharmony_ci mtd->_write_oob = doc_write_oob; 17768c2ecf20Sopenharmony_ci mtd->_block_isbad = doc_block_isbad; 17778c2ecf20Sopenharmony_ci mtd_set_ooblayout(mtd, &nand_ooblayout_docg3_ops); 17788c2ecf20Sopenharmony_ci mtd->oobavail = 8; 17798c2ecf20Sopenharmony_ci mtd->ecc_strength = DOC_ECC_BCH_T; 17808c2ecf20Sopenharmony_ci 17818c2ecf20Sopenharmony_ci return 0; 17828c2ecf20Sopenharmony_ci} 17838c2ecf20Sopenharmony_ci 17848c2ecf20Sopenharmony_ci/** 17858c2ecf20Sopenharmony_ci * doc_probe_device - Check if a device is available 17868c2ecf20Sopenharmony_ci * @base: the io space where the device is probed 17878c2ecf20Sopenharmony_ci * @floor: the floor of the probed device 17888c2ecf20Sopenharmony_ci * @dev: the device 17898c2ecf20Sopenharmony_ci * @cascade: the cascade of chips this devices will belong to 17908c2ecf20Sopenharmony_ci * 17918c2ecf20Sopenharmony_ci * Checks whether a device at the specified IO range, and floor is available. 17928c2ecf20Sopenharmony_ci * 17938c2ecf20Sopenharmony_ci * Returns a mtd_info struct if there is a device, ENODEV if none found, ENOMEM 17948c2ecf20Sopenharmony_ci * if a memory allocation failed. If floor 0 is checked, a reset of the ASIC is 17958c2ecf20Sopenharmony_ci * launched. 17968c2ecf20Sopenharmony_ci */ 17978c2ecf20Sopenharmony_cistatic struct mtd_info * __init 17988c2ecf20Sopenharmony_cidoc_probe_device(struct docg3_cascade *cascade, int floor, struct device *dev) 17998c2ecf20Sopenharmony_ci{ 18008c2ecf20Sopenharmony_ci int ret, bbt_nbpages; 18018c2ecf20Sopenharmony_ci u16 chip_id, chip_id_inv; 18028c2ecf20Sopenharmony_ci struct docg3 *docg3; 18038c2ecf20Sopenharmony_ci struct mtd_info *mtd; 18048c2ecf20Sopenharmony_ci 18058c2ecf20Sopenharmony_ci ret = -ENOMEM; 18068c2ecf20Sopenharmony_ci docg3 = kzalloc(sizeof(struct docg3), GFP_KERNEL); 18078c2ecf20Sopenharmony_ci if (!docg3) 18088c2ecf20Sopenharmony_ci goto nomem1; 18098c2ecf20Sopenharmony_ci mtd = kzalloc(sizeof(struct mtd_info), GFP_KERNEL); 18108c2ecf20Sopenharmony_ci if (!mtd) 18118c2ecf20Sopenharmony_ci goto nomem2; 18128c2ecf20Sopenharmony_ci mtd->priv = docg3; 18138c2ecf20Sopenharmony_ci mtd->dev.parent = dev; 18148c2ecf20Sopenharmony_ci bbt_nbpages = DIV_ROUND_UP(docg3->max_block + 1, 18158c2ecf20Sopenharmony_ci 8 * DOC_LAYOUT_PAGE_SIZE); 18168c2ecf20Sopenharmony_ci docg3->bbt = kcalloc(DOC_LAYOUT_PAGE_SIZE, bbt_nbpages, GFP_KERNEL); 18178c2ecf20Sopenharmony_ci if (!docg3->bbt) 18188c2ecf20Sopenharmony_ci goto nomem3; 18198c2ecf20Sopenharmony_ci 18208c2ecf20Sopenharmony_ci docg3->dev = dev; 18218c2ecf20Sopenharmony_ci docg3->device_id = floor; 18228c2ecf20Sopenharmony_ci docg3->cascade = cascade; 18238c2ecf20Sopenharmony_ci doc_set_device_id(docg3, docg3->device_id); 18248c2ecf20Sopenharmony_ci if (!floor) 18258c2ecf20Sopenharmony_ci doc_set_asic_mode(docg3, DOC_ASICMODE_RESET); 18268c2ecf20Sopenharmony_ci doc_set_asic_mode(docg3, DOC_ASICMODE_NORMAL); 18278c2ecf20Sopenharmony_ci 18288c2ecf20Sopenharmony_ci chip_id = doc_register_readw(docg3, DOC_CHIPID); 18298c2ecf20Sopenharmony_ci chip_id_inv = doc_register_readw(docg3, DOC_CHIPID_INV); 18308c2ecf20Sopenharmony_ci 18318c2ecf20Sopenharmony_ci ret = 0; 18328c2ecf20Sopenharmony_ci if (chip_id != (u16)(~chip_id_inv)) { 18338c2ecf20Sopenharmony_ci goto nomem4; 18348c2ecf20Sopenharmony_ci } 18358c2ecf20Sopenharmony_ci 18368c2ecf20Sopenharmony_ci switch (chip_id) { 18378c2ecf20Sopenharmony_ci case DOC_CHIPID_G3: 18388c2ecf20Sopenharmony_ci doc_info("Found a G3 DiskOnChip at addr %p, floor %d\n", 18398c2ecf20Sopenharmony_ci docg3->cascade->base, floor); 18408c2ecf20Sopenharmony_ci break; 18418c2ecf20Sopenharmony_ci default: 18428c2ecf20Sopenharmony_ci doc_err("Chip id %04x is not a DiskOnChip G3 chip\n", chip_id); 18438c2ecf20Sopenharmony_ci goto nomem4; 18448c2ecf20Sopenharmony_ci } 18458c2ecf20Sopenharmony_ci 18468c2ecf20Sopenharmony_ci ret = doc_set_driver_info(chip_id, mtd); 18478c2ecf20Sopenharmony_ci if (ret) 18488c2ecf20Sopenharmony_ci goto nomem4; 18498c2ecf20Sopenharmony_ci 18508c2ecf20Sopenharmony_ci doc_hamming_ecc_init(docg3, DOC_LAYOUT_OOB_PAGEINFO_SZ); 18518c2ecf20Sopenharmony_ci doc_reload_bbt(docg3); 18528c2ecf20Sopenharmony_ci return mtd; 18538c2ecf20Sopenharmony_ci 18548c2ecf20Sopenharmony_cinomem4: 18558c2ecf20Sopenharmony_ci kfree(docg3->bbt); 18568c2ecf20Sopenharmony_cinomem3: 18578c2ecf20Sopenharmony_ci kfree(mtd); 18588c2ecf20Sopenharmony_cinomem2: 18598c2ecf20Sopenharmony_ci kfree(docg3); 18608c2ecf20Sopenharmony_cinomem1: 18618c2ecf20Sopenharmony_ci return ret ? ERR_PTR(ret) : NULL; 18628c2ecf20Sopenharmony_ci} 18638c2ecf20Sopenharmony_ci 18648c2ecf20Sopenharmony_ci/** 18658c2ecf20Sopenharmony_ci * doc_release_device - Release a docg3 floor 18668c2ecf20Sopenharmony_ci * @mtd: the device 18678c2ecf20Sopenharmony_ci */ 18688c2ecf20Sopenharmony_cistatic void doc_release_device(struct mtd_info *mtd) 18698c2ecf20Sopenharmony_ci{ 18708c2ecf20Sopenharmony_ci struct docg3 *docg3 = mtd->priv; 18718c2ecf20Sopenharmony_ci 18728c2ecf20Sopenharmony_ci mtd_device_unregister(mtd); 18738c2ecf20Sopenharmony_ci kfree(docg3->bbt); 18748c2ecf20Sopenharmony_ci kfree(docg3); 18758c2ecf20Sopenharmony_ci kfree(mtd); 18768c2ecf20Sopenharmony_ci} 18778c2ecf20Sopenharmony_ci 18788c2ecf20Sopenharmony_ci/** 18798c2ecf20Sopenharmony_ci * docg3_resume - Awakens docg3 floor 18808c2ecf20Sopenharmony_ci * @pdev: platfrom device 18818c2ecf20Sopenharmony_ci * 18828c2ecf20Sopenharmony_ci * Returns 0 (always successful) 18838c2ecf20Sopenharmony_ci */ 18848c2ecf20Sopenharmony_cistatic int docg3_resume(struct platform_device *pdev) 18858c2ecf20Sopenharmony_ci{ 18868c2ecf20Sopenharmony_ci int i; 18878c2ecf20Sopenharmony_ci struct docg3_cascade *cascade; 18888c2ecf20Sopenharmony_ci struct mtd_info **docg3_floors, *mtd; 18898c2ecf20Sopenharmony_ci struct docg3 *docg3; 18908c2ecf20Sopenharmony_ci 18918c2ecf20Sopenharmony_ci cascade = platform_get_drvdata(pdev); 18928c2ecf20Sopenharmony_ci docg3_floors = cascade->floors; 18938c2ecf20Sopenharmony_ci mtd = docg3_floors[0]; 18948c2ecf20Sopenharmony_ci docg3 = mtd->priv; 18958c2ecf20Sopenharmony_ci 18968c2ecf20Sopenharmony_ci doc_dbg("docg3_resume()\n"); 18978c2ecf20Sopenharmony_ci for (i = 0; i < 12; i++) 18988c2ecf20Sopenharmony_ci doc_readb(docg3, DOC_IOSPACE_IPL); 18998c2ecf20Sopenharmony_ci return 0; 19008c2ecf20Sopenharmony_ci} 19018c2ecf20Sopenharmony_ci 19028c2ecf20Sopenharmony_ci/** 19038c2ecf20Sopenharmony_ci * docg3_suspend - Put in low power mode the docg3 floor 19048c2ecf20Sopenharmony_ci * @pdev: platform device 19058c2ecf20Sopenharmony_ci * @state: power state 19068c2ecf20Sopenharmony_ci * 19078c2ecf20Sopenharmony_ci * Shuts off most of docg3 circuitery to lower power consumption. 19088c2ecf20Sopenharmony_ci * 19098c2ecf20Sopenharmony_ci * Returns 0 if suspend succeeded, -EIO if chip refused suspend 19108c2ecf20Sopenharmony_ci */ 19118c2ecf20Sopenharmony_cistatic int docg3_suspend(struct platform_device *pdev, pm_message_t state) 19128c2ecf20Sopenharmony_ci{ 19138c2ecf20Sopenharmony_ci int floor, i; 19148c2ecf20Sopenharmony_ci struct docg3_cascade *cascade; 19158c2ecf20Sopenharmony_ci struct mtd_info **docg3_floors, *mtd; 19168c2ecf20Sopenharmony_ci struct docg3 *docg3; 19178c2ecf20Sopenharmony_ci u8 ctrl, pwr_down; 19188c2ecf20Sopenharmony_ci 19198c2ecf20Sopenharmony_ci cascade = platform_get_drvdata(pdev); 19208c2ecf20Sopenharmony_ci docg3_floors = cascade->floors; 19218c2ecf20Sopenharmony_ci for (floor = 0; floor < DOC_MAX_NBFLOORS; floor++) { 19228c2ecf20Sopenharmony_ci mtd = docg3_floors[floor]; 19238c2ecf20Sopenharmony_ci if (!mtd) 19248c2ecf20Sopenharmony_ci continue; 19258c2ecf20Sopenharmony_ci docg3 = mtd->priv; 19268c2ecf20Sopenharmony_ci 19278c2ecf20Sopenharmony_ci doc_writeb(docg3, floor, DOC_DEVICESELECT); 19288c2ecf20Sopenharmony_ci ctrl = doc_register_readb(docg3, DOC_FLASHCONTROL); 19298c2ecf20Sopenharmony_ci ctrl &= ~DOC_CTRL_VIOLATION & ~DOC_CTRL_CE; 19308c2ecf20Sopenharmony_ci doc_writeb(docg3, ctrl, DOC_FLASHCONTROL); 19318c2ecf20Sopenharmony_ci 19328c2ecf20Sopenharmony_ci for (i = 0; i < 10; i++) { 19338c2ecf20Sopenharmony_ci usleep_range(3000, 4000); 19348c2ecf20Sopenharmony_ci pwr_down = doc_register_readb(docg3, DOC_POWERMODE); 19358c2ecf20Sopenharmony_ci if (pwr_down & DOC_POWERDOWN_READY) 19368c2ecf20Sopenharmony_ci break; 19378c2ecf20Sopenharmony_ci } 19388c2ecf20Sopenharmony_ci if (pwr_down & DOC_POWERDOWN_READY) { 19398c2ecf20Sopenharmony_ci doc_dbg("docg3_suspend(): floor %d powerdown ok\n", 19408c2ecf20Sopenharmony_ci floor); 19418c2ecf20Sopenharmony_ci } else { 19428c2ecf20Sopenharmony_ci doc_err("docg3_suspend(): floor %d powerdown failed\n", 19438c2ecf20Sopenharmony_ci floor); 19448c2ecf20Sopenharmony_ci return -EIO; 19458c2ecf20Sopenharmony_ci } 19468c2ecf20Sopenharmony_ci } 19478c2ecf20Sopenharmony_ci 19488c2ecf20Sopenharmony_ci mtd = docg3_floors[0]; 19498c2ecf20Sopenharmony_ci docg3 = mtd->priv; 19508c2ecf20Sopenharmony_ci doc_set_asic_mode(docg3, DOC_ASICMODE_POWERDOWN); 19518c2ecf20Sopenharmony_ci return 0; 19528c2ecf20Sopenharmony_ci} 19538c2ecf20Sopenharmony_ci 19548c2ecf20Sopenharmony_ci/** 19558c2ecf20Sopenharmony_ci * doc_probe - Probe the IO space for a DiskOnChip G3 chip 19568c2ecf20Sopenharmony_ci * @pdev: platform device 19578c2ecf20Sopenharmony_ci * 19588c2ecf20Sopenharmony_ci * Probes for a G3 chip at the specified IO space in the platform data 19598c2ecf20Sopenharmony_ci * ressources. The floor 0 must be available. 19608c2ecf20Sopenharmony_ci * 19618c2ecf20Sopenharmony_ci * Returns 0 on success, -ENOMEM, -ENXIO on error 19628c2ecf20Sopenharmony_ci */ 19638c2ecf20Sopenharmony_cistatic int __init docg3_probe(struct platform_device *pdev) 19648c2ecf20Sopenharmony_ci{ 19658c2ecf20Sopenharmony_ci struct device *dev = &pdev->dev; 19668c2ecf20Sopenharmony_ci struct mtd_info *mtd; 19678c2ecf20Sopenharmony_ci struct resource *ress; 19688c2ecf20Sopenharmony_ci void __iomem *base; 19698c2ecf20Sopenharmony_ci int ret, floor; 19708c2ecf20Sopenharmony_ci struct docg3_cascade *cascade; 19718c2ecf20Sopenharmony_ci 19728c2ecf20Sopenharmony_ci ret = -ENXIO; 19738c2ecf20Sopenharmony_ci ress = platform_get_resource(pdev, IORESOURCE_MEM, 0); 19748c2ecf20Sopenharmony_ci if (!ress) { 19758c2ecf20Sopenharmony_ci dev_err(dev, "No I/O memory resource defined\n"); 19768c2ecf20Sopenharmony_ci return ret; 19778c2ecf20Sopenharmony_ci } 19788c2ecf20Sopenharmony_ci 19798c2ecf20Sopenharmony_ci ret = -ENOMEM; 19808c2ecf20Sopenharmony_ci base = devm_ioremap(dev, ress->start, DOC_IOSPACE_SIZE); 19818c2ecf20Sopenharmony_ci if (!base) { 19828c2ecf20Sopenharmony_ci dev_err(dev, "devm_ioremap dev failed\n"); 19838c2ecf20Sopenharmony_ci return ret; 19848c2ecf20Sopenharmony_ci } 19858c2ecf20Sopenharmony_ci 19868c2ecf20Sopenharmony_ci cascade = devm_kcalloc(dev, DOC_MAX_NBFLOORS, sizeof(*cascade), 19878c2ecf20Sopenharmony_ci GFP_KERNEL); 19888c2ecf20Sopenharmony_ci if (!cascade) 19898c2ecf20Sopenharmony_ci return ret; 19908c2ecf20Sopenharmony_ci cascade->base = base; 19918c2ecf20Sopenharmony_ci mutex_init(&cascade->lock); 19928c2ecf20Sopenharmony_ci cascade->bch = bch_init(DOC_ECC_BCH_M, DOC_ECC_BCH_T, 19938c2ecf20Sopenharmony_ci DOC_ECC_BCH_PRIMPOLY, false); 19948c2ecf20Sopenharmony_ci if (!cascade->bch) 19958c2ecf20Sopenharmony_ci return ret; 19968c2ecf20Sopenharmony_ci 19978c2ecf20Sopenharmony_ci for (floor = 0; floor < DOC_MAX_NBFLOORS; floor++) { 19988c2ecf20Sopenharmony_ci mtd = doc_probe_device(cascade, floor, dev); 19998c2ecf20Sopenharmony_ci if (IS_ERR(mtd)) { 20008c2ecf20Sopenharmony_ci ret = PTR_ERR(mtd); 20018c2ecf20Sopenharmony_ci goto err_probe; 20028c2ecf20Sopenharmony_ci } 20038c2ecf20Sopenharmony_ci if (!mtd) { 20048c2ecf20Sopenharmony_ci if (floor == 0) 20058c2ecf20Sopenharmony_ci goto notfound; 20068c2ecf20Sopenharmony_ci else 20078c2ecf20Sopenharmony_ci continue; 20088c2ecf20Sopenharmony_ci } 20098c2ecf20Sopenharmony_ci cascade->floors[floor] = mtd; 20108c2ecf20Sopenharmony_ci ret = mtd_device_parse_register(mtd, part_probes, NULL, NULL, 20118c2ecf20Sopenharmony_ci 0); 20128c2ecf20Sopenharmony_ci if (ret) 20138c2ecf20Sopenharmony_ci goto err_probe; 20148c2ecf20Sopenharmony_ci 20158c2ecf20Sopenharmony_ci doc_dbg_register(cascade->floors[floor]); 20168c2ecf20Sopenharmony_ci } 20178c2ecf20Sopenharmony_ci 20188c2ecf20Sopenharmony_ci ret = doc_register_sysfs(pdev, cascade); 20198c2ecf20Sopenharmony_ci if (ret) 20208c2ecf20Sopenharmony_ci goto err_probe; 20218c2ecf20Sopenharmony_ci 20228c2ecf20Sopenharmony_ci platform_set_drvdata(pdev, cascade); 20238c2ecf20Sopenharmony_ci return 0; 20248c2ecf20Sopenharmony_ci 20258c2ecf20Sopenharmony_cinotfound: 20268c2ecf20Sopenharmony_ci ret = -ENODEV; 20278c2ecf20Sopenharmony_ci dev_info(dev, "No supported DiskOnChip found\n"); 20288c2ecf20Sopenharmony_cierr_probe: 20298c2ecf20Sopenharmony_ci bch_free(cascade->bch); 20308c2ecf20Sopenharmony_ci for (floor = 0; floor < DOC_MAX_NBFLOORS; floor++) 20318c2ecf20Sopenharmony_ci if (cascade->floors[floor]) 20328c2ecf20Sopenharmony_ci doc_release_device(cascade->floors[floor]); 20338c2ecf20Sopenharmony_ci return ret; 20348c2ecf20Sopenharmony_ci} 20358c2ecf20Sopenharmony_ci 20368c2ecf20Sopenharmony_ci/** 20378c2ecf20Sopenharmony_ci * docg3_release - Release the driver 20388c2ecf20Sopenharmony_ci * @pdev: the platform device 20398c2ecf20Sopenharmony_ci * 20408c2ecf20Sopenharmony_ci * Returns 0 20418c2ecf20Sopenharmony_ci */ 20428c2ecf20Sopenharmony_cistatic int docg3_release(struct platform_device *pdev) 20438c2ecf20Sopenharmony_ci{ 20448c2ecf20Sopenharmony_ci struct docg3_cascade *cascade = platform_get_drvdata(pdev); 20458c2ecf20Sopenharmony_ci struct docg3 *docg3 = cascade->floors[0]->priv; 20468c2ecf20Sopenharmony_ci int floor; 20478c2ecf20Sopenharmony_ci 20488c2ecf20Sopenharmony_ci doc_unregister_sysfs(pdev, cascade); 20498c2ecf20Sopenharmony_ci for (floor = 0; floor < DOC_MAX_NBFLOORS; floor++) 20508c2ecf20Sopenharmony_ci if (cascade->floors[floor]) 20518c2ecf20Sopenharmony_ci doc_release_device(cascade->floors[floor]); 20528c2ecf20Sopenharmony_ci 20538c2ecf20Sopenharmony_ci bch_free(docg3->cascade->bch); 20548c2ecf20Sopenharmony_ci return 0; 20558c2ecf20Sopenharmony_ci} 20568c2ecf20Sopenharmony_ci 20578c2ecf20Sopenharmony_ci#ifdef CONFIG_OF 20588c2ecf20Sopenharmony_cistatic const struct of_device_id docg3_dt_ids[] = { 20598c2ecf20Sopenharmony_ci { .compatible = "m-systems,diskonchip-g3" }, 20608c2ecf20Sopenharmony_ci {} 20618c2ecf20Sopenharmony_ci}; 20628c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(of, docg3_dt_ids); 20638c2ecf20Sopenharmony_ci#endif 20648c2ecf20Sopenharmony_ci 20658c2ecf20Sopenharmony_cistatic struct platform_driver g3_driver = { 20668c2ecf20Sopenharmony_ci .driver = { 20678c2ecf20Sopenharmony_ci .name = "docg3", 20688c2ecf20Sopenharmony_ci .of_match_table = of_match_ptr(docg3_dt_ids), 20698c2ecf20Sopenharmony_ci }, 20708c2ecf20Sopenharmony_ci .suspend = docg3_suspend, 20718c2ecf20Sopenharmony_ci .resume = docg3_resume, 20728c2ecf20Sopenharmony_ci .remove = docg3_release, 20738c2ecf20Sopenharmony_ci}; 20748c2ecf20Sopenharmony_ci 20758c2ecf20Sopenharmony_cimodule_platform_driver_probe(g3_driver, docg3_probe); 20768c2ecf20Sopenharmony_ci 20778c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL"); 20788c2ecf20Sopenharmony_ciMODULE_AUTHOR("Robert Jarzmik <robert.jarzmik@free.fr>"); 20798c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("MTD driver for DiskOnChip G3"); 2080