18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * BCM47XX NAND flash driver 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * Copyright (C) 2012 Rafał Miłecki <zajec5@gmail.com> 68c2ecf20Sopenharmony_ci */ 78c2ecf20Sopenharmony_ci 88c2ecf20Sopenharmony_ci#include "bcm47xxnflash.h" 98c2ecf20Sopenharmony_ci 108c2ecf20Sopenharmony_ci#include <linux/module.h> 118c2ecf20Sopenharmony_ci#include <linux/kernel.h> 128c2ecf20Sopenharmony_ci#include <linux/slab.h> 138c2ecf20Sopenharmony_ci#include <linux/delay.h> 148c2ecf20Sopenharmony_ci#include <linux/bcma/bcma.h> 158c2ecf20Sopenharmony_ci 168c2ecf20Sopenharmony_ci/* Broadcom uses 1'000'000 but it seems to be too many. Tests on WNDR4500 has 178c2ecf20Sopenharmony_ci * shown ~1000 retries as maxiumum. */ 188c2ecf20Sopenharmony_ci#define NFLASH_READY_RETRIES 10000 198c2ecf20Sopenharmony_ci 208c2ecf20Sopenharmony_ci#define NFLASH_SECTOR_SIZE 512 218c2ecf20Sopenharmony_ci 228c2ecf20Sopenharmony_ci#define NCTL_CMD0 0x00010000 238c2ecf20Sopenharmony_ci#define NCTL_COL 0x00020000 /* Update column with value from BCMA_CC_NFLASH_COL_ADDR */ 248c2ecf20Sopenharmony_ci#define NCTL_ROW 0x00040000 /* Update row (page) with value from BCMA_CC_NFLASH_ROW_ADDR */ 258c2ecf20Sopenharmony_ci#define NCTL_CMD1W 0x00080000 268c2ecf20Sopenharmony_ci#define NCTL_READ 0x00100000 278c2ecf20Sopenharmony_ci#define NCTL_WRITE 0x00200000 288c2ecf20Sopenharmony_ci#define NCTL_SPECADDR 0x01000000 298c2ecf20Sopenharmony_ci#define NCTL_READY 0x04000000 308c2ecf20Sopenharmony_ci#define NCTL_ERR 0x08000000 318c2ecf20Sopenharmony_ci#define NCTL_CSA 0x40000000 328c2ecf20Sopenharmony_ci#define NCTL_START 0x80000000 338c2ecf20Sopenharmony_ci 348c2ecf20Sopenharmony_ci/************************************************** 358c2ecf20Sopenharmony_ci * Various helpers 368c2ecf20Sopenharmony_ci **************************************************/ 378c2ecf20Sopenharmony_ci 388c2ecf20Sopenharmony_cistatic inline u8 bcm47xxnflash_ops_bcm4706_ns_to_cycle(u16 ns, u16 clock) 398c2ecf20Sopenharmony_ci{ 408c2ecf20Sopenharmony_ci return ((ns * 1000 * clock) / 1000000) + 1; 418c2ecf20Sopenharmony_ci} 428c2ecf20Sopenharmony_ci 438c2ecf20Sopenharmony_cistatic int bcm47xxnflash_ops_bcm4706_ctl_cmd(struct bcma_drv_cc *cc, u32 code) 448c2ecf20Sopenharmony_ci{ 458c2ecf20Sopenharmony_ci int i = 0; 468c2ecf20Sopenharmony_ci 478c2ecf20Sopenharmony_ci bcma_cc_write32(cc, BCMA_CC_NFLASH_CTL, NCTL_START | code); 488c2ecf20Sopenharmony_ci for (i = 0; i < NFLASH_READY_RETRIES; i++) { 498c2ecf20Sopenharmony_ci if (!(bcma_cc_read32(cc, BCMA_CC_NFLASH_CTL) & NCTL_START)) { 508c2ecf20Sopenharmony_ci i = 0; 518c2ecf20Sopenharmony_ci break; 528c2ecf20Sopenharmony_ci } 538c2ecf20Sopenharmony_ci } 548c2ecf20Sopenharmony_ci if (i) { 558c2ecf20Sopenharmony_ci pr_err("NFLASH control command not ready!\n"); 568c2ecf20Sopenharmony_ci return -EBUSY; 578c2ecf20Sopenharmony_ci } 588c2ecf20Sopenharmony_ci return 0; 598c2ecf20Sopenharmony_ci} 608c2ecf20Sopenharmony_ci 618c2ecf20Sopenharmony_cistatic int bcm47xxnflash_ops_bcm4706_poll(struct bcma_drv_cc *cc) 628c2ecf20Sopenharmony_ci{ 638c2ecf20Sopenharmony_ci int i; 648c2ecf20Sopenharmony_ci 658c2ecf20Sopenharmony_ci for (i = 0; i < NFLASH_READY_RETRIES; i++) { 668c2ecf20Sopenharmony_ci if (bcma_cc_read32(cc, BCMA_CC_NFLASH_CTL) & NCTL_READY) { 678c2ecf20Sopenharmony_ci if (bcma_cc_read32(cc, BCMA_CC_NFLASH_CTL) & 688c2ecf20Sopenharmony_ci BCMA_CC_NFLASH_CTL_ERR) { 698c2ecf20Sopenharmony_ci pr_err("Error on polling\n"); 708c2ecf20Sopenharmony_ci return -EBUSY; 718c2ecf20Sopenharmony_ci } else { 728c2ecf20Sopenharmony_ci return 0; 738c2ecf20Sopenharmony_ci } 748c2ecf20Sopenharmony_ci } 758c2ecf20Sopenharmony_ci } 768c2ecf20Sopenharmony_ci 778c2ecf20Sopenharmony_ci pr_err("Polling timeout!\n"); 788c2ecf20Sopenharmony_ci return -EBUSY; 798c2ecf20Sopenharmony_ci} 808c2ecf20Sopenharmony_ci 818c2ecf20Sopenharmony_ci/************************************************** 828c2ecf20Sopenharmony_ci * R/W 838c2ecf20Sopenharmony_ci **************************************************/ 848c2ecf20Sopenharmony_ci 858c2ecf20Sopenharmony_cistatic void bcm47xxnflash_ops_bcm4706_read(struct mtd_info *mtd, uint8_t *buf, 868c2ecf20Sopenharmony_ci int len) 878c2ecf20Sopenharmony_ci{ 888c2ecf20Sopenharmony_ci struct nand_chip *nand_chip = mtd_to_nand(mtd); 898c2ecf20Sopenharmony_ci struct bcm47xxnflash *b47n = nand_get_controller_data(nand_chip); 908c2ecf20Sopenharmony_ci 918c2ecf20Sopenharmony_ci u32 ctlcode; 928c2ecf20Sopenharmony_ci u32 *dest = (u32 *)buf; 938c2ecf20Sopenharmony_ci int i; 948c2ecf20Sopenharmony_ci int toread; 958c2ecf20Sopenharmony_ci 968c2ecf20Sopenharmony_ci BUG_ON(b47n->curr_page_addr & ~nand_chip->pagemask); 978c2ecf20Sopenharmony_ci /* Don't validate column using nand_chip->page_shift, it may be bigger 988c2ecf20Sopenharmony_ci * when accessing OOB */ 998c2ecf20Sopenharmony_ci 1008c2ecf20Sopenharmony_ci while (len) { 1018c2ecf20Sopenharmony_ci /* We can read maximum of 0x200 bytes at once */ 1028c2ecf20Sopenharmony_ci toread = min(len, 0x200); 1038c2ecf20Sopenharmony_ci 1048c2ecf20Sopenharmony_ci /* Set page and column */ 1058c2ecf20Sopenharmony_ci bcma_cc_write32(b47n->cc, BCMA_CC_NFLASH_COL_ADDR, 1068c2ecf20Sopenharmony_ci b47n->curr_column); 1078c2ecf20Sopenharmony_ci bcma_cc_write32(b47n->cc, BCMA_CC_NFLASH_ROW_ADDR, 1088c2ecf20Sopenharmony_ci b47n->curr_page_addr); 1098c2ecf20Sopenharmony_ci 1108c2ecf20Sopenharmony_ci /* Prepare to read */ 1118c2ecf20Sopenharmony_ci ctlcode = NCTL_CSA | NCTL_CMD1W | NCTL_ROW | NCTL_COL | 1128c2ecf20Sopenharmony_ci NCTL_CMD0; 1138c2ecf20Sopenharmony_ci ctlcode |= NAND_CMD_READSTART << 8; 1148c2ecf20Sopenharmony_ci if (bcm47xxnflash_ops_bcm4706_ctl_cmd(b47n->cc, ctlcode)) 1158c2ecf20Sopenharmony_ci return; 1168c2ecf20Sopenharmony_ci if (bcm47xxnflash_ops_bcm4706_poll(b47n->cc)) 1178c2ecf20Sopenharmony_ci return; 1188c2ecf20Sopenharmony_ci 1198c2ecf20Sopenharmony_ci /* Eventually read some data :) */ 1208c2ecf20Sopenharmony_ci for (i = 0; i < toread; i += 4, dest++) { 1218c2ecf20Sopenharmony_ci ctlcode = NCTL_CSA | 0x30000000 | NCTL_READ; 1228c2ecf20Sopenharmony_ci if (i == toread - 4) /* Last read goes without that */ 1238c2ecf20Sopenharmony_ci ctlcode &= ~NCTL_CSA; 1248c2ecf20Sopenharmony_ci if (bcm47xxnflash_ops_bcm4706_ctl_cmd(b47n->cc, 1258c2ecf20Sopenharmony_ci ctlcode)) 1268c2ecf20Sopenharmony_ci return; 1278c2ecf20Sopenharmony_ci *dest = bcma_cc_read32(b47n->cc, BCMA_CC_NFLASH_DATA); 1288c2ecf20Sopenharmony_ci } 1298c2ecf20Sopenharmony_ci 1308c2ecf20Sopenharmony_ci b47n->curr_column += toread; 1318c2ecf20Sopenharmony_ci len -= toread; 1328c2ecf20Sopenharmony_ci } 1338c2ecf20Sopenharmony_ci} 1348c2ecf20Sopenharmony_ci 1358c2ecf20Sopenharmony_cistatic void bcm47xxnflash_ops_bcm4706_write(struct mtd_info *mtd, 1368c2ecf20Sopenharmony_ci const uint8_t *buf, int len) 1378c2ecf20Sopenharmony_ci{ 1388c2ecf20Sopenharmony_ci struct nand_chip *nand_chip = mtd_to_nand(mtd); 1398c2ecf20Sopenharmony_ci struct bcm47xxnflash *b47n = nand_get_controller_data(nand_chip); 1408c2ecf20Sopenharmony_ci struct bcma_drv_cc *cc = b47n->cc; 1418c2ecf20Sopenharmony_ci 1428c2ecf20Sopenharmony_ci u32 ctlcode; 1438c2ecf20Sopenharmony_ci const u32 *data = (u32 *)buf; 1448c2ecf20Sopenharmony_ci int i; 1458c2ecf20Sopenharmony_ci 1468c2ecf20Sopenharmony_ci BUG_ON(b47n->curr_page_addr & ~nand_chip->pagemask); 1478c2ecf20Sopenharmony_ci /* Don't validate column using nand_chip->page_shift, it may be bigger 1488c2ecf20Sopenharmony_ci * when accessing OOB */ 1498c2ecf20Sopenharmony_ci 1508c2ecf20Sopenharmony_ci for (i = 0; i < len; i += 4, data++) { 1518c2ecf20Sopenharmony_ci bcma_cc_write32(cc, BCMA_CC_NFLASH_DATA, *data); 1528c2ecf20Sopenharmony_ci 1538c2ecf20Sopenharmony_ci ctlcode = NCTL_CSA | 0x30000000 | NCTL_WRITE; 1548c2ecf20Sopenharmony_ci if (i == len - 4) /* Last read goes without that */ 1558c2ecf20Sopenharmony_ci ctlcode &= ~NCTL_CSA; 1568c2ecf20Sopenharmony_ci if (bcm47xxnflash_ops_bcm4706_ctl_cmd(cc, ctlcode)) { 1578c2ecf20Sopenharmony_ci pr_err("%s ctl_cmd didn't work!\n", __func__); 1588c2ecf20Sopenharmony_ci return; 1598c2ecf20Sopenharmony_ci } 1608c2ecf20Sopenharmony_ci } 1618c2ecf20Sopenharmony_ci 1628c2ecf20Sopenharmony_ci b47n->curr_column += len; 1638c2ecf20Sopenharmony_ci} 1648c2ecf20Sopenharmony_ci 1658c2ecf20Sopenharmony_ci/************************************************** 1668c2ecf20Sopenharmony_ci * NAND chip ops 1678c2ecf20Sopenharmony_ci **************************************************/ 1688c2ecf20Sopenharmony_ci 1698c2ecf20Sopenharmony_cistatic void bcm47xxnflash_ops_bcm4706_cmd_ctrl(struct nand_chip *nand_chip, 1708c2ecf20Sopenharmony_ci int cmd, unsigned int ctrl) 1718c2ecf20Sopenharmony_ci{ 1728c2ecf20Sopenharmony_ci struct bcm47xxnflash *b47n = nand_get_controller_data(nand_chip); 1738c2ecf20Sopenharmony_ci u32 code = 0; 1748c2ecf20Sopenharmony_ci 1758c2ecf20Sopenharmony_ci if (cmd == NAND_CMD_NONE) 1768c2ecf20Sopenharmony_ci return; 1778c2ecf20Sopenharmony_ci 1788c2ecf20Sopenharmony_ci if (cmd & NAND_CTRL_CLE) 1798c2ecf20Sopenharmony_ci code = cmd | NCTL_CMD0; 1808c2ecf20Sopenharmony_ci 1818c2ecf20Sopenharmony_ci /* nCS is not needed for reset command */ 1828c2ecf20Sopenharmony_ci if (cmd != NAND_CMD_RESET) 1838c2ecf20Sopenharmony_ci code |= NCTL_CSA; 1848c2ecf20Sopenharmony_ci 1858c2ecf20Sopenharmony_ci bcm47xxnflash_ops_bcm4706_ctl_cmd(b47n->cc, code); 1868c2ecf20Sopenharmony_ci} 1878c2ecf20Sopenharmony_ci 1888c2ecf20Sopenharmony_ci/* Default nand_select_chip calls cmd_ctrl, which is not used in BCM4706 */ 1898c2ecf20Sopenharmony_cistatic void bcm47xxnflash_ops_bcm4706_select_chip(struct nand_chip *chip, 1908c2ecf20Sopenharmony_ci int cs) 1918c2ecf20Sopenharmony_ci{ 1928c2ecf20Sopenharmony_ci return; 1938c2ecf20Sopenharmony_ci} 1948c2ecf20Sopenharmony_ci 1958c2ecf20Sopenharmony_cistatic int bcm47xxnflash_ops_bcm4706_dev_ready(struct nand_chip *nand_chip) 1968c2ecf20Sopenharmony_ci{ 1978c2ecf20Sopenharmony_ci struct bcm47xxnflash *b47n = nand_get_controller_data(nand_chip); 1988c2ecf20Sopenharmony_ci 1998c2ecf20Sopenharmony_ci return !!(bcma_cc_read32(b47n->cc, BCMA_CC_NFLASH_CTL) & NCTL_READY); 2008c2ecf20Sopenharmony_ci} 2018c2ecf20Sopenharmony_ci 2028c2ecf20Sopenharmony_ci/* 2038c2ecf20Sopenharmony_ci * Default nand_command and nand_command_lp don't match BCM4706 hardware layout. 2048c2ecf20Sopenharmony_ci * For example, reading chip id is performed in a non-standard way. 2058c2ecf20Sopenharmony_ci * Setting column and page is also handled differently, we use a special 2068c2ecf20Sopenharmony_ci * registers of ChipCommon core. Hacking cmd_ctrl to understand and convert 2078c2ecf20Sopenharmony_ci * standard commands would be much more complicated. 2088c2ecf20Sopenharmony_ci */ 2098c2ecf20Sopenharmony_cistatic void bcm47xxnflash_ops_bcm4706_cmdfunc(struct nand_chip *nand_chip, 2108c2ecf20Sopenharmony_ci unsigned command, int column, 2118c2ecf20Sopenharmony_ci int page_addr) 2128c2ecf20Sopenharmony_ci{ 2138c2ecf20Sopenharmony_ci struct mtd_info *mtd = nand_to_mtd(nand_chip); 2148c2ecf20Sopenharmony_ci struct bcm47xxnflash *b47n = nand_get_controller_data(nand_chip); 2158c2ecf20Sopenharmony_ci struct bcma_drv_cc *cc = b47n->cc; 2168c2ecf20Sopenharmony_ci u32 ctlcode; 2178c2ecf20Sopenharmony_ci int i; 2188c2ecf20Sopenharmony_ci 2198c2ecf20Sopenharmony_ci if (column != -1) 2208c2ecf20Sopenharmony_ci b47n->curr_column = column; 2218c2ecf20Sopenharmony_ci if (page_addr != -1) 2228c2ecf20Sopenharmony_ci b47n->curr_page_addr = page_addr; 2238c2ecf20Sopenharmony_ci 2248c2ecf20Sopenharmony_ci switch (command) { 2258c2ecf20Sopenharmony_ci case NAND_CMD_RESET: 2268c2ecf20Sopenharmony_ci nand_chip->legacy.cmd_ctrl(nand_chip, command, NAND_CTRL_CLE); 2278c2ecf20Sopenharmony_ci 2288c2ecf20Sopenharmony_ci ndelay(100); 2298c2ecf20Sopenharmony_ci nand_wait_ready(nand_chip); 2308c2ecf20Sopenharmony_ci break; 2318c2ecf20Sopenharmony_ci case NAND_CMD_READID: 2328c2ecf20Sopenharmony_ci ctlcode = NCTL_CSA | 0x01000000 | NCTL_CMD1W | NCTL_CMD0; 2338c2ecf20Sopenharmony_ci ctlcode |= NAND_CMD_READID; 2348c2ecf20Sopenharmony_ci if (bcm47xxnflash_ops_bcm4706_ctl_cmd(b47n->cc, ctlcode)) { 2358c2ecf20Sopenharmony_ci pr_err("READID error\n"); 2368c2ecf20Sopenharmony_ci break; 2378c2ecf20Sopenharmony_ci } 2388c2ecf20Sopenharmony_ci 2398c2ecf20Sopenharmony_ci /* 2408c2ecf20Sopenharmony_ci * Reading is specific, last one has to go without NCTL_CSA 2418c2ecf20Sopenharmony_ci * bit. We don't know how many reads NAND subsystem is going 2428c2ecf20Sopenharmony_ci * to perform, so cache everything. 2438c2ecf20Sopenharmony_ci */ 2448c2ecf20Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(b47n->id_data); i++) { 2458c2ecf20Sopenharmony_ci ctlcode = NCTL_CSA | NCTL_READ; 2468c2ecf20Sopenharmony_ci if (i == ARRAY_SIZE(b47n->id_data) - 1) 2478c2ecf20Sopenharmony_ci ctlcode &= ~NCTL_CSA; 2488c2ecf20Sopenharmony_ci if (bcm47xxnflash_ops_bcm4706_ctl_cmd(b47n->cc, 2498c2ecf20Sopenharmony_ci ctlcode)) { 2508c2ecf20Sopenharmony_ci pr_err("READID error\n"); 2518c2ecf20Sopenharmony_ci break; 2528c2ecf20Sopenharmony_ci } 2538c2ecf20Sopenharmony_ci b47n->id_data[i] = 2548c2ecf20Sopenharmony_ci bcma_cc_read32(b47n->cc, BCMA_CC_NFLASH_DATA) 2558c2ecf20Sopenharmony_ci & 0xFF; 2568c2ecf20Sopenharmony_ci } 2578c2ecf20Sopenharmony_ci 2588c2ecf20Sopenharmony_ci break; 2598c2ecf20Sopenharmony_ci case NAND_CMD_STATUS: 2608c2ecf20Sopenharmony_ci ctlcode = NCTL_CSA | NCTL_CMD0 | NAND_CMD_STATUS; 2618c2ecf20Sopenharmony_ci if (bcm47xxnflash_ops_bcm4706_ctl_cmd(cc, ctlcode)) 2628c2ecf20Sopenharmony_ci pr_err("STATUS command error\n"); 2638c2ecf20Sopenharmony_ci break; 2648c2ecf20Sopenharmony_ci case NAND_CMD_READ0: 2658c2ecf20Sopenharmony_ci break; 2668c2ecf20Sopenharmony_ci case NAND_CMD_READOOB: 2678c2ecf20Sopenharmony_ci if (page_addr != -1) 2688c2ecf20Sopenharmony_ci b47n->curr_column += mtd->writesize; 2698c2ecf20Sopenharmony_ci break; 2708c2ecf20Sopenharmony_ci case NAND_CMD_ERASE1: 2718c2ecf20Sopenharmony_ci bcma_cc_write32(cc, BCMA_CC_NFLASH_ROW_ADDR, 2728c2ecf20Sopenharmony_ci b47n->curr_page_addr); 2738c2ecf20Sopenharmony_ci ctlcode = NCTL_ROW | NCTL_CMD1W | NCTL_CMD0 | 2748c2ecf20Sopenharmony_ci NAND_CMD_ERASE1 | (NAND_CMD_ERASE2 << 8); 2758c2ecf20Sopenharmony_ci if (bcm47xxnflash_ops_bcm4706_ctl_cmd(cc, ctlcode)) 2768c2ecf20Sopenharmony_ci pr_err("ERASE1 failed\n"); 2778c2ecf20Sopenharmony_ci break; 2788c2ecf20Sopenharmony_ci case NAND_CMD_ERASE2: 2798c2ecf20Sopenharmony_ci break; 2808c2ecf20Sopenharmony_ci case NAND_CMD_SEQIN: 2818c2ecf20Sopenharmony_ci /* Set page and column */ 2828c2ecf20Sopenharmony_ci bcma_cc_write32(cc, BCMA_CC_NFLASH_COL_ADDR, 2838c2ecf20Sopenharmony_ci b47n->curr_column); 2848c2ecf20Sopenharmony_ci bcma_cc_write32(cc, BCMA_CC_NFLASH_ROW_ADDR, 2858c2ecf20Sopenharmony_ci b47n->curr_page_addr); 2868c2ecf20Sopenharmony_ci 2878c2ecf20Sopenharmony_ci /* Prepare to write */ 2888c2ecf20Sopenharmony_ci ctlcode = 0x40000000 | NCTL_ROW | NCTL_COL | NCTL_CMD0; 2898c2ecf20Sopenharmony_ci ctlcode |= NAND_CMD_SEQIN; 2908c2ecf20Sopenharmony_ci if (bcm47xxnflash_ops_bcm4706_ctl_cmd(cc, ctlcode)) 2918c2ecf20Sopenharmony_ci pr_err("SEQIN failed\n"); 2928c2ecf20Sopenharmony_ci break; 2938c2ecf20Sopenharmony_ci case NAND_CMD_PAGEPROG: 2948c2ecf20Sopenharmony_ci if (bcm47xxnflash_ops_bcm4706_ctl_cmd(cc, NCTL_CMD0 | 2958c2ecf20Sopenharmony_ci NAND_CMD_PAGEPROG)) 2968c2ecf20Sopenharmony_ci pr_err("PAGEPROG failed\n"); 2978c2ecf20Sopenharmony_ci if (bcm47xxnflash_ops_bcm4706_poll(cc)) 2988c2ecf20Sopenharmony_ci pr_err("PAGEPROG not ready\n"); 2998c2ecf20Sopenharmony_ci break; 3008c2ecf20Sopenharmony_ci default: 3018c2ecf20Sopenharmony_ci pr_err("Command 0x%X unsupported\n", command); 3028c2ecf20Sopenharmony_ci break; 3038c2ecf20Sopenharmony_ci } 3048c2ecf20Sopenharmony_ci b47n->curr_command = command; 3058c2ecf20Sopenharmony_ci} 3068c2ecf20Sopenharmony_ci 3078c2ecf20Sopenharmony_cistatic u8 bcm47xxnflash_ops_bcm4706_read_byte(struct nand_chip *nand_chip) 3088c2ecf20Sopenharmony_ci{ 3098c2ecf20Sopenharmony_ci struct mtd_info *mtd = nand_to_mtd(nand_chip); 3108c2ecf20Sopenharmony_ci struct bcm47xxnflash *b47n = nand_get_controller_data(nand_chip); 3118c2ecf20Sopenharmony_ci struct bcma_drv_cc *cc = b47n->cc; 3128c2ecf20Sopenharmony_ci u32 tmp = 0; 3138c2ecf20Sopenharmony_ci 3148c2ecf20Sopenharmony_ci switch (b47n->curr_command) { 3158c2ecf20Sopenharmony_ci case NAND_CMD_READID: 3168c2ecf20Sopenharmony_ci if (b47n->curr_column >= ARRAY_SIZE(b47n->id_data)) { 3178c2ecf20Sopenharmony_ci pr_err("Requested invalid id_data: %d\n", 3188c2ecf20Sopenharmony_ci b47n->curr_column); 3198c2ecf20Sopenharmony_ci return 0; 3208c2ecf20Sopenharmony_ci } 3218c2ecf20Sopenharmony_ci return b47n->id_data[b47n->curr_column++]; 3228c2ecf20Sopenharmony_ci case NAND_CMD_STATUS: 3238c2ecf20Sopenharmony_ci if (bcm47xxnflash_ops_bcm4706_ctl_cmd(cc, NCTL_READ)) 3248c2ecf20Sopenharmony_ci return 0; 3258c2ecf20Sopenharmony_ci return bcma_cc_read32(cc, BCMA_CC_NFLASH_DATA) & 0xff; 3268c2ecf20Sopenharmony_ci case NAND_CMD_READOOB: 3278c2ecf20Sopenharmony_ci bcm47xxnflash_ops_bcm4706_read(mtd, (u8 *)&tmp, 4); 3288c2ecf20Sopenharmony_ci return tmp & 0xFF; 3298c2ecf20Sopenharmony_ci } 3308c2ecf20Sopenharmony_ci 3318c2ecf20Sopenharmony_ci pr_err("Invalid command for byte read: 0x%X\n", b47n->curr_command); 3328c2ecf20Sopenharmony_ci return 0; 3338c2ecf20Sopenharmony_ci} 3348c2ecf20Sopenharmony_ci 3358c2ecf20Sopenharmony_cistatic void bcm47xxnflash_ops_bcm4706_read_buf(struct nand_chip *nand_chip, 3368c2ecf20Sopenharmony_ci uint8_t *buf, int len) 3378c2ecf20Sopenharmony_ci{ 3388c2ecf20Sopenharmony_ci struct bcm47xxnflash *b47n = nand_get_controller_data(nand_chip); 3398c2ecf20Sopenharmony_ci 3408c2ecf20Sopenharmony_ci switch (b47n->curr_command) { 3418c2ecf20Sopenharmony_ci case NAND_CMD_READ0: 3428c2ecf20Sopenharmony_ci case NAND_CMD_READOOB: 3438c2ecf20Sopenharmony_ci bcm47xxnflash_ops_bcm4706_read(nand_to_mtd(nand_chip), buf, 3448c2ecf20Sopenharmony_ci len); 3458c2ecf20Sopenharmony_ci return; 3468c2ecf20Sopenharmony_ci } 3478c2ecf20Sopenharmony_ci 3488c2ecf20Sopenharmony_ci pr_err("Invalid command for buf read: 0x%X\n", b47n->curr_command); 3498c2ecf20Sopenharmony_ci} 3508c2ecf20Sopenharmony_ci 3518c2ecf20Sopenharmony_cistatic void bcm47xxnflash_ops_bcm4706_write_buf(struct nand_chip *nand_chip, 3528c2ecf20Sopenharmony_ci const uint8_t *buf, int len) 3538c2ecf20Sopenharmony_ci{ 3548c2ecf20Sopenharmony_ci struct bcm47xxnflash *b47n = nand_get_controller_data(nand_chip); 3558c2ecf20Sopenharmony_ci 3568c2ecf20Sopenharmony_ci switch (b47n->curr_command) { 3578c2ecf20Sopenharmony_ci case NAND_CMD_SEQIN: 3588c2ecf20Sopenharmony_ci bcm47xxnflash_ops_bcm4706_write(nand_to_mtd(nand_chip), buf, 3598c2ecf20Sopenharmony_ci len); 3608c2ecf20Sopenharmony_ci return; 3618c2ecf20Sopenharmony_ci } 3628c2ecf20Sopenharmony_ci 3638c2ecf20Sopenharmony_ci pr_err("Invalid command for buf write: 0x%X\n", b47n->curr_command); 3648c2ecf20Sopenharmony_ci} 3658c2ecf20Sopenharmony_ci 3668c2ecf20Sopenharmony_ci/************************************************** 3678c2ecf20Sopenharmony_ci * Init 3688c2ecf20Sopenharmony_ci **************************************************/ 3698c2ecf20Sopenharmony_ci 3708c2ecf20Sopenharmony_ciint bcm47xxnflash_ops_bcm4706_init(struct bcm47xxnflash *b47n) 3718c2ecf20Sopenharmony_ci{ 3728c2ecf20Sopenharmony_ci struct nand_chip *nand_chip = (struct nand_chip *)&b47n->nand_chip; 3738c2ecf20Sopenharmony_ci int err; 3748c2ecf20Sopenharmony_ci u32 freq; 3758c2ecf20Sopenharmony_ci u16 clock; 3768c2ecf20Sopenharmony_ci u8 w0, w1, w2, w3, w4; 3778c2ecf20Sopenharmony_ci 3788c2ecf20Sopenharmony_ci unsigned long chipsize; /* MiB */ 3798c2ecf20Sopenharmony_ci u8 tbits, col_bits, col_size, row_bits, row_bsize; 3808c2ecf20Sopenharmony_ci u32 val; 3818c2ecf20Sopenharmony_ci 3828c2ecf20Sopenharmony_ci nand_chip->legacy.select_chip = bcm47xxnflash_ops_bcm4706_select_chip; 3838c2ecf20Sopenharmony_ci nand_chip->legacy.cmd_ctrl = bcm47xxnflash_ops_bcm4706_cmd_ctrl; 3848c2ecf20Sopenharmony_ci nand_chip->legacy.dev_ready = bcm47xxnflash_ops_bcm4706_dev_ready; 3858c2ecf20Sopenharmony_ci b47n->nand_chip.legacy.cmdfunc = bcm47xxnflash_ops_bcm4706_cmdfunc; 3868c2ecf20Sopenharmony_ci b47n->nand_chip.legacy.read_byte = bcm47xxnflash_ops_bcm4706_read_byte; 3878c2ecf20Sopenharmony_ci b47n->nand_chip.legacy.read_buf = bcm47xxnflash_ops_bcm4706_read_buf; 3888c2ecf20Sopenharmony_ci b47n->nand_chip.legacy.write_buf = bcm47xxnflash_ops_bcm4706_write_buf; 3898c2ecf20Sopenharmony_ci b47n->nand_chip.legacy.set_features = nand_get_set_features_notsupp; 3908c2ecf20Sopenharmony_ci b47n->nand_chip.legacy.get_features = nand_get_set_features_notsupp; 3918c2ecf20Sopenharmony_ci 3928c2ecf20Sopenharmony_ci nand_chip->legacy.chip_delay = 50; 3938c2ecf20Sopenharmony_ci b47n->nand_chip.bbt_options = NAND_BBT_USE_FLASH; 3948c2ecf20Sopenharmony_ci /* TODO: implement ECC */ 3958c2ecf20Sopenharmony_ci b47n->nand_chip.ecc.engine_type = NAND_ECC_ENGINE_TYPE_NONE; 3968c2ecf20Sopenharmony_ci 3978c2ecf20Sopenharmony_ci /* Enable NAND flash access */ 3988c2ecf20Sopenharmony_ci bcma_cc_set32(b47n->cc, BCMA_CC_4706_FLASHSCFG, 3998c2ecf20Sopenharmony_ci BCMA_CC_4706_FLASHSCFG_NF1); 4008c2ecf20Sopenharmony_ci 4018c2ecf20Sopenharmony_ci /* Configure wait counters */ 4028c2ecf20Sopenharmony_ci if (b47n->cc->status & BCMA_CC_CHIPST_4706_PKG_OPTION) { 4038c2ecf20Sopenharmony_ci /* 400 MHz */ 4048c2ecf20Sopenharmony_ci freq = 400000000 / 4; 4058c2ecf20Sopenharmony_ci } else { 4068c2ecf20Sopenharmony_ci freq = bcma_chipco_pll_read(b47n->cc, 4); 4078c2ecf20Sopenharmony_ci freq = (freq & 0xFFF) >> 3; 4088c2ecf20Sopenharmony_ci /* Fixed reference clock 25 MHz and m = 2 */ 4098c2ecf20Sopenharmony_ci freq = (freq * 25000000 / 2) / 4; 4108c2ecf20Sopenharmony_ci } 4118c2ecf20Sopenharmony_ci clock = freq / 1000000; 4128c2ecf20Sopenharmony_ci w0 = bcm47xxnflash_ops_bcm4706_ns_to_cycle(15, clock); 4138c2ecf20Sopenharmony_ci w1 = bcm47xxnflash_ops_bcm4706_ns_to_cycle(20, clock); 4148c2ecf20Sopenharmony_ci w2 = bcm47xxnflash_ops_bcm4706_ns_to_cycle(10, clock); 4158c2ecf20Sopenharmony_ci w3 = bcm47xxnflash_ops_bcm4706_ns_to_cycle(10, clock); 4168c2ecf20Sopenharmony_ci w4 = bcm47xxnflash_ops_bcm4706_ns_to_cycle(100, clock); 4178c2ecf20Sopenharmony_ci bcma_cc_write32(b47n->cc, BCMA_CC_NFLASH_WAITCNT0, 4188c2ecf20Sopenharmony_ci (w4 << 24 | w3 << 18 | w2 << 12 | w1 << 6 | w0)); 4198c2ecf20Sopenharmony_ci 4208c2ecf20Sopenharmony_ci /* Scan NAND */ 4218c2ecf20Sopenharmony_ci err = nand_scan(&b47n->nand_chip, 1); 4228c2ecf20Sopenharmony_ci if (err) { 4238c2ecf20Sopenharmony_ci pr_err("Could not scan NAND flash: %d\n", err); 4248c2ecf20Sopenharmony_ci goto exit; 4258c2ecf20Sopenharmony_ci } 4268c2ecf20Sopenharmony_ci 4278c2ecf20Sopenharmony_ci /* Configure FLASH */ 4288c2ecf20Sopenharmony_ci chipsize = nanddev_target_size(&b47n->nand_chip.base) >> 20; 4298c2ecf20Sopenharmony_ci tbits = ffs(chipsize); /* find first bit set */ 4308c2ecf20Sopenharmony_ci if (!tbits || tbits != fls(chipsize)) { 4318c2ecf20Sopenharmony_ci pr_err("Invalid flash size: 0x%lX\n", chipsize); 4328c2ecf20Sopenharmony_ci err = -ENOTSUPP; 4338c2ecf20Sopenharmony_ci goto exit; 4348c2ecf20Sopenharmony_ci } 4358c2ecf20Sopenharmony_ci tbits += 19; /* Broadcom increases *index* by 20, we increase *pos* */ 4368c2ecf20Sopenharmony_ci 4378c2ecf20Sopenharmony_ci col_bits = b47n->nand_chip.page_shift + 1; 4388c2ecf20Sopenharmony_ci col_size = (col_bits + 7) / 8; 4398c2ecf20Sopenharmony_ci 4408c2ecf20Sopenharmony_ci row_bits = tbits - col_bits + 1; 4418c2ecf20Sopenharmony_ci row_bsize = (row_bits + 7) / 8; 4428c2ecf20Sopenharmony_ci 4438c2ecf20Sopenharmony_ci val = ((row_bsize - 1) << 6) | ((col_size - 1) << 4) | 2; 4448c2ecf20Sopenharmony_ci bcma_cc_write32(b47n->cc, BCMA_CC_NFLASH_CONF, val); 4458c2ecf20Sopenharmony_ci 4468c2ecf20Sopenharmony_ciexit: 4478c2ecf20Sopenharmony_ci if (err) 4488c2ecf20Sopenharmony_ci bcma_cc_mask32(b47n->cc, BCMA_CC_4706_FLASHSCFG, 4498c2ecf20Sopenharmony_ci ~BCMA_CC_4706_FLASHSCFG_NF1); 4508c2ecf20Sopenharmony_ci return err; 4518c2ecf20Sopenharmony_ci} 452