162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * BCM47XX NAND flash driver 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * Copyright (C) 2012 Rafał Miłecki <zajec5@gmail.com> 662306a36Sopenharmony_ci */ 762306a36Sopenharmony_ci 862306a36Sopenharmony_ci#include "bcm47xxnflash.h" 962306a36Sopenharmony_ci 1062306a36Sopenharmony_ci#include <linux/module.h> 1162306a36Sopenharmony_ci#include <linux/kernel.h> 1262306a36Sopenharmony_ci#include <linux/slab.h> 1362306a36Sopenharmony_ci#include <linux/delay.h> 1462306a36Sopenharmony_ci#include <linux/bcma/bcma.h> 1562306a36Sopenharmony_ci 1662306a36Sopenharmony_ci/* Broadcom uses 1'000'000 but it seems to be too many. Tests on WNDR4500 has 1762306a36Sopenharmony_ci * shown ~1000 retries as maximum. */ 1862306a36Sopenharmony_ci#define NFLASH_READY_RETRIES 10000 1962306a36Sopenharmony_ci 2062306a36Sopenharmony_ci#define NFLASH_SECTOR_SIZE 512 2162306a36Sopenharmony_ci 2262306a36Sopenharmony_ci#define NCTL_CMD0 0x00010000 2362306a36Sopenharmony_ci#define NCTL_COL 0x00020000 /* Update column with value from BCMA_CC_NFLASH_COL_ADDR */ 2462306a36Sopenharmony_ci#define NCTL_ROW 0x00040000 /* Update row (page) with value from BCMA_CC_NFLASH_ROW_ADDR */ 2562306a36Sopenharmony_ci#define NCTL_CMD1W 0x00080000 2662306a36Sopenharmony_ci#define NCTL_READ 0x00100000 2762306a36Sopenharmony_ci#define NCTL_WRITE 0x00200000 2862306a36Sopenharmony_ci#define NCTL_SPECADDR 0x01000000 2962306a36Sopenharmony_ci#define NCTL_READY 0x04000000 3062306a36Sopenharmony_ci#define NCTL_ERR 0x08000000 3162306a36Sopenharmony_ci#define NCTL_CSA 0x40000000 3262306a36Sopenharmony_ci#define NCTL_START 0x80000000 3362306a36Sopenharmony_ci 3462306a36Sopenharmony_ci/************************************************** 3562306a36Sopenharmony_ci * Various helpers 3662306a36Sopenharmony_ci **************************************************/ 3762306a36Sopenharmony_ci 3862306a36Sopenharmony_cistatic inline u8 bcm47xxnflash_ops_bcm4706_ns_to_cycle(u16 ns, u16 clock) 3962306a36Sopenharmony_ci{ 4062306a36Sopenharmony_ci return ((ns * 1000 * clock) / 1000000) + 1; 4162306a36Sopenharmony_ci} 4262306a36Sopenharmony_ci 4362306a36Sopenharmony_cistatic int bcm47xxnflash_ops_bcm4706_ctl_cmd(struct bcma_drv_cc *cc, u32 code) 4462306a36Sopenharmony_ci{ 4562306a36Sopenharmony_ci int i = 0; 4662306a36Sopenharmony_ci 4762306a36Sopenharmony_ci bcma_cc_write32(cc, BCMA_CC_NFLASH_CTL, NCTL_START | code); 4862306a36Sopenharmony_ci for (i = 0; i < NFLASH_READY_RETRIES; i++) { 4962306a36Sopenharmony_ci if (!(bcma_cc_read32(cc, BCMA_CC_NFLASH_CTL) & NCTL_START)) { 5062306a36Sopenharmony_ci i = 0; 5162306a36Sopenharmony_ci break; 5262306a36Sopenharmony_ci } 5362306a36Sopenharmony_ci } 5462306a36Sopenharmony_ci if (i) { 5562306a36Sopenharmony_ci pr_err("NFLASH control command not ready!\n"); 5662306a36Sopenharmony_ci return -EBUSY; 5762306a36Sopenharmony_ci } 5862306a36Sopenharmony_ci return 0; 5962306a36Sopenharmony_ci} 6062306a36Sopenharmony_ci 6162306a36Sopenharmony_cistatic int bcm47xxnflash_ops_bcm4706_poll(struct bcma_drv_cc *cc) 6262306a36Sopenharmony_ci{ 6362306a36Sopenharmony_ci int i; 6462306a36Sopenharmony_ci 6562306a36Sopenharmony_ci for (i = 0; i < NFLASH_READY_RETRIES; i++) { 6662306a36Sopenharmony_ci if (bcma_cc_read32(cc, BCMA_CC_NFLASH_CTL) & NCTL_READY) { 6762306a36Sopenharmony_ci if (bcma_cc_read32(cc, BCMA_CC_NFLASH_CTL) & 6862306a36Sopenharmony_ci BCMA_CC_NFLASH_CTL_ERR) { 6962306a36Sopenharmony_ci pr_err("Error on polling\n"); 7062306a36Sopenharmony_ci return -EBUSY; 7162306a36Sopenharmony_ci } else { 7262306a36Sopenharmony_ci return 0; 7362306a36Sopenharmony_ci } 7462306a36Sopenharmony_ci } 7562306a36Sopenharmony_ci } 7662306a36Sopenharmony_ci 7762306a36Sopenharmony_ci pr_err("Polling timeout!\n"); 7862306a36Sopenharmony_ci return -EBUSY; 7962306a36Sopenharmony_ci} 8062306a36Sopenharmony_ci 8162306a36Sopenharmony_ci/************************************************** 8262306a36Sopenharmony_ci * R/W 8362306a36Sopenharmony_ci **************************************************/ 8462306a36Sopenharmony_ci 8562306a36Sopenharmony_cistatic void bcm47xxnflash_ops_bcm4706_read(struct mtd_info *mtd, uint8_t *buf, 8662306a36Sopenharmony_ci int len) 8762306a36Sopenharmony_ci{ 8862306a36Sopenharmony_ci struct nand_chip *nand_chip = mtd_to_nand(mtd); 8962306a36Sopenharmony_ci struct bcm47xxnflash *b47n = nand_get_controller_data(nand_chip); 9062306a36Sopenharmony_ci 9162306a36Sopenharmony_ci u32 ctlcode; 9262306a36Sopenharmony_ci u32 *dest = (u32 *)buf; 9362306a36Sopenharmony_ci int i; 9462306a36Sopenharmony_ci int toread; 9562306a36Sopenharmony_ci 9662306a36Sopenharmony_ci BUG_ON(b47n->curr_page_addr & ~nand_chip->pagemask); 9762306a36Sopenharmony_ci /* Don't validate column using nand_chip->page_shift, it may be bigger 9862306a36Sopenharmony_ci * when accessing OOB */ 9962306a36Sopenharmony_ci 10062306a36Sopenharmony_ci while (len) { 10162306a36Sopenharmony_ci /* We can read maximum of 0x200 bytes at once */ 10262306a36Sopenharmony_ci toread = min(len, 0x200); 10362306a36Sopenharmony_ci 10462306a36Sopenharmony_ci /* Set page and column */ 10562306a36Sopenharmony_ci bcma_cc_write32(b47n->cc, BCMA_CC_NFLASH_COL_ADDR, 10662306a36Sopenharmony_ci b47n->curr_column); 10762306a36Sopenharmony_ci bcma_cc_write32(b47n->cc, BCMA_CC_NFLASH_ROW_ADDR, 10862306a36Sopenharmony_ci b47n->curr_page_addr); 10962306a36Sopenharmony_ci 11062306a36Sopenharmony_ci /* Prepare to read */ 11162306a36Sopenharmony_ci ctlcode = NCTL_CSA | NCTL_CMD1W | NCTL_ROW | NCTL_COL | 11262306a36Sopenharmony_ci NCTL_CMD0; 11362306a36Sopenharmony_ci ctlcode |= NAND_CMD_READSTART << 8; 11462306a36Sopenharmony_ci if (bcm47xxnflash_ops_bcm4706_ctl_cmd(b47n->cc, ctlcode)) 11562306a36Sopenharmony_ci return; 11662306a36Sopenharmony_ci if (bcm47xxnflash_ops_bcm4706_poll(b47n->cc)) 11762306a36Sopenharmony_ci return; 11862306a36Sopenharmony_ci 11962306a36Sopenharmony_ci /* Eventually read some data :) */ 12062306a36Sopenharmony_ci for (i = 0; i < toread; i += 4, dest++) { 12162306a36Sopenharmony_ci ctlcode = NCTL_CSA | 0x30000000 | NCTL_READ; 12262306a36Sopenharmony_ci if (i == toread - 4) /* Last read goes without that */ 12362306a36Sopenharmony_ci ctlcode &= ~NCTL_CSA; 12462306a36Sopenharmony_ci if (bcm47xxnflash_ops_bcm4706_ctl_cmd(b47n->cc, 12562306a36Sopenharmony_ci ctlcode)) 12662306a36Sopenharmony_ci return; 12762306a36Sopenharmony_ci *dest = bcma_cc_read32(b47n->cc, BCMA_CC_NFLASH_DATA); 12862306a36Sopenharmony_ci } 12962306a36Sopenharmony_ci 13062306a36Sopenharmony_ci b47n->curr_column += toread; 13162306a36Sopenharmony_ci len -= toread; 13262306a36Sopenharmony_ci } 13362306a36Sopenharmony_ci} 13462306a36Sopenharmony_ci 13562306a36Sopenharmony_cistatic void bcm47xxnflash_ops_bcm4706_write(struct mtd_info *mtd, 13662306a36Sopenharmony_ci const uint8_t *buf, int len) 13762306a36Sopenharmony_ci{ 13862306a36Sopenharmony_ci struct nand_chip *nand_chip = mtd_to_nand(mtd); 13962306a36Sopenharmony_ci struct bcm47xxnflash *b47n = nand_get_controller_data(nand_chip); 14062306a36Sopenharmony_ci struct bcma_drv_cc *cc = b47n->cc; 14162306a36Sopenharmony_ci 14262306a36Sopenharmony_ci u32 ctlcode; 14362306a36Sopenharmony_ci const u32 *data = (u32 *)buf; 14462306a36Sopenharmony_ci int i; 14562306a36Sopenharmony_ci 14662306a36Sopenharmony_ci BUG_ON(b47n->curr_page_addr & ~nand_chip->pagemask); 14762306a36Sopenharmony_ci /* Don't validate column using nand_chip->page_shift, it may be bigger 14862306a36Sopenharmony_ci * when accessing OOB */ 14962306a36Sopenharmony_ci 15062306a36Sopenharmony_ci for (i = 0; i < len; i += 4, data++) { 15162306a36Sopenharmony_ci bcma_cc_write32(cc, BCMA_CC_NFLASH_DATA, *data); 15262306a36Sopenharmony_ci 15362306a36Sopenharmony_ci ctlcode = NCTL_CSA | 0x30000000 | NCTL_WRITE; 15462306a36Sopenharmony_ci if (i == len - 4) /* Last read goes without that */ 15562306a36Sopenharmony_ci ctlcode &= ~NCTL_CSA; 15662306a36Sopenharmony_ci if (bcm47xxnflash_ops_bcm4706_ctl_cmd(cc, ctlcode)) { 15762306a36Sopenharmony_ci pr_err("%s ctl_cmd didn't work!\n", __func__); 15862306a36Sopenharmony_ci return; 15962306a36Sopenharmony_ci } 16062306a36Sopenharmony_ci } 16162306a36Sopenharmony_ci 16262306a36Sopenharmony_ci b47n->curr_column += len; 16362306a36Sopenharmony_ci} 16462306a36Sopenharmony_ci 16562306a36Sopenharmony_ci/************************************************** 16662306a36Sopenharmony_ci * NAND chip ops 16762306a36Sopenharmony_ci **************************************************/ 16862306a36Sopenharmony_ci 16962306a36Sopenharmony_cistatic void bcm47xxnflash_ops_bcm4706_cmd_ctrl(struct nand_chip *nand_chip, 17062306a36Sopenharmony_ci int cmd, unsigned int ctrl) 17162306a36Sopenharmony_ci{ 17262306a36Sopenharmony_ci struct bcm47xxnflash *b47n = nand_get_controller_data(nand_chip); 17362306a36Sopenharmony_ci u32 code = 0; 17462306a36Sopenharmony_ci 17562306a36Sopenharmony_ci if (cmd == NAND_CMD_NONE) 17662306a36Sopenharmony_ci return; 17762306a36Sopenharmony_ci 17862306a36Sopenharmony_ci if (cmd & NAND_CTRL_CLE) 17962306a36Sopenharmony_ci code = cmd | NCTL_CMD0; 18062306a36Sopenharmony_ci 18162306a36Sopenharmony_ci /* nCS is not needed for reset command */ 18262306a36Sopenharmony_ci if (cmd != NAND_CMD_RESET) 18362306a36Sopenharmony_ci code |= NCTL_CSA; 18462306a36Sopenharmony_ci 18562306a36Sopenharmony_ci bcm47xxnflash_ops_bcm4706_ctl_cmd(b47n->cc, code); 18662306a36Sopenharmony_ci} 18762306a36Sopenharmony_ci 18862306a36Sopenharmony_ci/* Default nand_select_chip calls cmd_ctrl, which is not used in BCM4706 */ 18962306a36Sopenharmony_cistatic void bcm47xxnflash_ops_bcm4706_select_chip(struct nand_chip *chip, 19062306a36Sopenharmony_ci int cs) 19162306a36Sopenharmony_ci{ 19262306a36Sopenharmony_ci return; 19362306a36Sopenharmony_ci} 19462306a36Sopenharmony_ci 19562306a36Sopenharmony_cistatic int bcm47xxnflash_ops_bcm4706_dev_ready(struct nand_chip *nand_chip) 19662306a36Sopenharmony_ci{ 19762306a36Sopenharmony_ci struct bcm47xxnflash *b47n = nand_get_controller_data(nand_chip); 19862306a36Sopenharmony_ci 19962306a36Sopenharmony_ci return !!(bcma_cc_read32(b47n->cc, BCMA_CC_NFLASH_CTL) & NCTL_READY); 20062306a36Sopenharmony_ci} 20162306a36Sopenharmony_ci 20262306a36Sopenharmony_ci/* 20362306a36Sopenharmony_ci * Default nand_command and nand_command_lp don't match BCM4706 hardware layout. 20462306a36Sopenharmony_ci * For example, reading chip id is performed in a non-standard way. 20562306a36Sopenharmony_ci * Setting column and page is also handled differently, we use a special 20662306a36Sopenharmony_ci * registers of ChipCommon core. Hacking cmd_ctrl to understand and convert 20762306a36Sopenharmony_ci * standard commands would be much more complicated. 20862306a36Sopenharmony_ci */ 20962306a36Sopenharmony_cistatic void bcm47xxnflash_ops_bcm4706_cmdfunc(struct nand_chip *nand_chip, 21062306a36Sopenharmony_ci unsigned command, int column, 21162306a36Sopenharmony_ci int page_addr) 21262306a36Sopenharmony_ci{ 21362306a36Sopenharmony_ci struct mtd_info *mtd = nand_to_mtd(nand_chip); 21462306a36Sopenharmony_ci struct bcm47xxnflash *b47n = nand_get_controller_data(nand_chip); 21562306a36Sopenharmony_ci struct bcma_drv_cc *cc = b47n->cc; 21662306a36Sopenharmony_ci u32 ctlcode; 21762306a36Sopenharmony_ci int i; 21862306a36Sopenharmony_ci 21962306a36Sopenharmony_ci if (column != -1) 22062306a36Sopenharmony_ci b47n->curr_column = column; 22162306a36Sopenharmony_ci if (page_addr != -1) 22262306a36Sopenharmony_ci b47n->curr_page_addr = page_addr; 22362306a36Sopenharmony_ci 22462306a36Sopenharmony_ci switch (command) { 22562306a36Sopenharmony_ci case NAND_CMD_RESET: 22662306a36Sopenharmony_ci nand_chip->legacy.cmd_ctrl(nand_chip, command, NAND_CTRL_CLE); 22762306a36Sopenharmony_ci 22862306a36Sopenharmony_ci ndelay(100); 22962306a36Sopenharmony_ci nand_wait_ready(nand_chip); 23062306a36Sopenharmony_ci break; 23162306a36Sopenharmony_ci case NAND_CMD_READID: 23262306a36Sopenharmony_ci ctlcode = NCTL_CSA | 0x01000000 | NCTL_CMD1W | NCTL_CMD0; 23362306a36Sopenharmony_ci ctlcode |= NAND_CMD_READID; 23462306a36Sopenharmony_ci if (bcm47xxnflash_ops_bcm4706_ctl_cmd(b47n->cc, ctlcode)) { 23562306a36Sopenharmony_ci pr_err("READID error\n"); 23662306a36Sopenharmony_ci break; 23762306a36Sopenharmony_ci } 23862306a36Sopenharmony_ci 23962306a36Sopenharmony_ci /* 24062306a36Sopenharmony_ci * Reading is specific, last one has to go without NCTL_CSA 24162306a36Sopenharmony_ci * bit. We don't know how many reads NAND subsystem is going 24262306a36Sopenharmony_ci * to perform, so cache everything. 24362306a36Sopenharmony_ci */ 24462306a36Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(b47n->id_data); i++) { 24562306a36Sopenharmony_ci ctlcode = NCTL_CSA | NCTL_READ; 24662306a36Sopenharmony_ci if (i == ARRAY_SIZE(b47n->id_data) - 1) 24762306a36Sopenharmony_ci ctlcode &= ~NCTL_CSA; 24862306a36Sopenharmony_ci if (bcm47xxnflash_ops_bcm4706_ctl_cmd(b47n->cc, 24962306a36Sopenharmony_ci ctlcode)) { 25062306a36Sopenharmony_ci pr_err("READID error\n"); 25162306a36Sopenharmony_ci break; 25262306a36Sopenharmony_ci } 25362306a36Sopenharmony_ci b47n->id_data[i] = 25462306a36Sopenharmony_ci bcma_cc_read32(b47n->cc, BCMA_CC_NFLASH_DATA) 25562306a36Sopenharmony_ci & 0xFF; 25662306a36Sopenharmony_ci } 25762306a36Sopenharmony_ci 25862306a36Sopenharmony_ci break; 25962306a36Sopenharmony_ci case NAND_CMD_STATUS: 26062306a36Sopenharmony_ci ctlcode = NCTL_CSA | NCTL_CMD0 | NAND_CMD_STATUS; 26162306a36Sopenharmony_ci if (bcm47xxnflash_ops_bcm4706_ctl_cmd(cc, ctlcode)) 26262306a36Sopenharmony_ci pr_err("STATUS command error\n"); 26362306a36Sopenharmony_ci break; 26462306a36Sopenharmony_ci case NAND_CMD_READ0: 26562306a36Sopenharmony_ci break; 26662306a36Sopenharmony_ci case NAND_CMD_READOOB: 26762306a36Sopenharmony_ci if (page_addr != -1) 26862306a36Sopenharmony_ci b47n->curr_column += mtd->writesize; 26962306a36Sopenharmony_ci break; 27062306a36Sopenharmony_ci case NAND_CMD_ERASE1: 27162306a36Sopenharmony_ci bcma_cc_write32(cc, BCMA_CC_NFLASH_ROW_ADDR, 27262306a36Sopenharmony_ci b47n->curr_page_addr); 27362306a36Sopenharmony_ci ctlcode = NCTL_ROW | NCTL_CMD1W | NCTL_CMD0 | 27462306a36Sopenharmony_ci NAND_CMD_ERASE1 | (NAND_CMD_ERASE2 << 8); 27562306a36Sopenharmony_ci if (bcm47xxnflash_ops_bcm4706_ctl_cmd(cc, ctlcode)) 27662306a36Sopenharmony_ci pr_err("ERASE1 failed\n"); 27762306a36Sopenharmony_ci break; 27862306a36Sopenharmony_ci case NAND_CMD_ERASE2: 27962306a36Sopenharmony_ci break; 28062306a36Sopenharmony_ci case NAND_CMD_SEQIN: 28162306a36Sopenharmony_ci /* Set page and column */ 28262306a36Sopenharmony_ci bcma_cc_write32(cc, BCMA_CC_NFLASH_COL_ADDR, 28362306a36Sopenharmony_ci b47n->curr_column); 28462306a36Sopenharmony_ci bcma_cc_write32(cc, BCMA_CC_NFLASH_ROW_ADDR, 28562306a36Sopenharmony_ci b47n->curr_page_addr); 28662306a36Sopenharmony_ci 28762306a36Sopenharmony_ci /* Prepare to write */ 28862306a36Sopenharmony_ci ctlcode = 0x40000000 | NCTL_ROW | NCTL_COL | NCTL_CMD0; 28962306a36Sopenharmony_ci ctlcode |= NAND_CMD_SEQIN; 29062306a36Sopenharmony_ci if (bcm47xxnflash_ops_bcm4706_ctl_cmd(cc, ctlcode)) 29162306a36Sopenharmony_ci pr_err("SEQIN failed\n"); 29262306a36Sopenharmony_ci break; 29362306a36Sopenharmony_ci case NAND_CMD_PAGEPROG: 29462306a36Sopenharmony_ci if (bcm47xxnflash_ops_bcm4706_ctl_cmd(cc, NCTL_CMD0 | 29562306a36Sopenharmony_ci NAND_CMD_PAGEPROG)) 29662306a36Sopenharmony_ci pr_err("PAGEPROG failed\n"); 29762306a36Sopenharmony_ci if (bcm47xxnflash_ops_bcm4706_poll(cc)) 29862306a36Sopenharmony_ci pr_err("PAGEPROG not ready\n"); 29962306a36Sopenharmony_ci break; 30062306a36Sopenharmony_ci default: 30162306a36Sopenharmony_ci pr_err("Command 0x%X unsupported\n", command); 30262306a36Sopenharmony_ci break; 30362306a36Sopenharmony_ci } 30462306a36Sopenharmony_ci b47n->curr_command = command; 30562306a36Sopenharmony_ci} 30662306a36Sopenharmony_ci 30762306a36Sopenharmony_cistatic u8 bcm47xxnflash_ops_bcm4706_read_byte(struct nand_chip *nand_chip) 30862306a36Sopenharmony_ci{ 30962306a36Sopenharmony_ci struct mtd_info *mtd = nand_to_mtd(nand_chip); 31062306a36Sopenharmony_ci struct bcm47xxnflash *b47n = nand_get_controller_data(nand_chip); 31162306a36Sopenharmony_ci struct bcma_drv_cc *cc = b47n->cc; 31262306a36Sopenharmony_ci u32 tmp = 0; 31362306a36Sopenharmony_ci 31462306a36Sopenharmony_ci switch (b47n->curr_command) { 31562306a36Sopenharmony_ci case NAND_CMD_READID: 31662306a36Sopenharmony_ci if (b47n->curr_column >= ARRAY_SIZE(b47n->id_data)) { 31762306a36Sopenharmony_ci pr_err("Requested invalid id_data: %d\n", 31862306a36Sopenharmony_ci b47n->curr_column); 31962306a36Sopenharmony_ci return 0; 32062306a36Sopenharmony_ci } 32162306a36Sopenharmony_ci return b47n->id_data[b47n->curr_column++]; 32262306a36Sopenharmony_ci case NAND_CMD_STATUS: 32362306a36Sopenharmony_ci if (bcm47xxnflash_ops_bcm4706_ctl_cmd(cc, NCTL_READ)) 32462306a36Sopenharmony_ci return 0; 32562306a36Sopenharmony_ci return bcma_cc_read32(cc, BCMA_CC_NFLASH_DATA) & 0xff; 32662306a36Sopenharmony_ci case NAND_CMD_READOOB: 32762306a36Sopenharmony_ci bcm47xxnflash_ops_bcm4706_read(mtd, (u8 *)&tmp, 4); 32862306a36Sopenharmony_ci return tmp & 0xFF; 32962306a36Sopenharmony_ci } 33062306a36Sopenharmony_ci 33162306a36Sopenharmony_ci pr_err("Invalid command for byte read: 0x%X\n", b47n->curr_command); 33262306a36Sopenharmony_ci return 0; 33362306a36Sopenharmony_ci} 33462306a36Sopenharmony_ci 33562306a36Sopenharmony_cistatic void bcm47xxnflash_ops_bcm4706_read_buf(struct nand_chip *nand_chip, 33662306a36Sopenharmony_ci uint8_t *buf, int len) 33762306a36Sopenharmony_ci{ 33862306a36Sopenharmony_ci struct bcm47xxnflash *b47n = nand_get_controller_data(nand_chip); 33962306a36Sopenharmony_ci 34062306a36Sopenharmony_ci switch (b47n->curr_command) { 34162306a36Sopenharmony_ci case NAND_CMD_READ0: 34262306a36Sopenharmony_ci case NAND_CMD_READOOB: 34362306a36Sopenharmony_ci bcm47xxnflash_ops_bcm4706_read(nand_to_mtd(nand_chip), buf, 34462306a36Sopenharmony_ci len); 34562306a36Sopenharmony_ci return; 34662306a36Sopenharmony_ci } 34762306a36Sopenharmony_ci 34862306a36Sopenharmony_ci pr_err("Invalid command for buf read: 0x%X\n", b47n->curr_command); 34962306a36Sopenharmony_ci} 35062306a36Sopenharmony_ci 35162306a36Sopenharmony_cistatic void bcm47xxnflash_ops_bcm4706_write_buf(struct nand_chip *nand_chip, 35262306a36Sopenharmony_ci const uint8_t *buf, int len) 35362306a36Sopenharmony_ci{ 35462306a36Sopenharmony_ci struct bcm47xxnflash *b47n = nand_get_controller_data(nand_chip); 35562306a36Sopenharmony_ci 35662306a36Sopenharmony_ci switch (b47n->curr_command) { 35762306a36Sopenharmony_ci case NAND_CMD_SEQIN: 35862306a36Sopenharmony_ci bcm47xxnflash_ops_bcm4706_write(nand_to_mtd(nand_chip), buf, 35962306a36Sopenharmony_ci len); 36062306a36Sopenharmony_ci return; 36162306a36Sopenharmony_ci } 36262306a36Sopenharmony_ci 36362306a36Sopenharmony_ci pr_err("Invalid command for buf write: 0x%X\n", b47n->curr_command); 36462306a36Sopenharmony_ci} 36562306a36Sopenharmony_ci 36662306a36Sopenharmony_ci/************************************************** 36762306a36Sopenharmony_ci * Init 36862306a36Sopenharmony_ci **************************************************/ 36962306a36Sopenharmony_ci 37062306a36Sopenharmony_ciint bcm47xxnflash_ops_bcm4706_init(struct bcm47xxnflash *b47n) 37162306a36Sopenharmony_ci{ 37262306a36Sopenharmony_ci struct nand_chip *nand_chip = (struct nand_chip *)&b47n->nand_chip; 37362306a36Sopenharmony_ci int err; 37462306a36Sopenharmony_ci u32 freq; 37562306a36Sopenharmony_ci u16 clock; 37662306a36Sopenharmony_ci u8 w0, w1, w2, w3, w4; 37762306a36Sopenharmony_ci 37862306a36Sopenharmony_ci unsigned long chipsize; /* MiB */ 37962306a36Sopenharmony_ci u8 tbits, col_bits, col_size, row_bits, row_bsize; 38062306a36Sopenharmony_ci u32 val; 38162306a36Sopenharmony_ci 38262306a36Sopenharmony_ci nand_chip->legacy.select_chip = bcm47xxnflash_ops_bcm4706_select_chip; 38362306a36Sopenharmony_ci nand_chip->legacy.cmd_ctrl = bcm47xxnflash_ops_bcm4706_cmd_ctrl; 38462306a36Sopenharmony_ci nand_chip->legacy.dev_ready = bcm47xxnflash_ops_bcm4706_dev_ready; 38562306a36Sopenharmony_ci b47n->nand_chip.legacy.cmdfunc = bcm47xxnflash_ops_bcm4706_cmdfunc; 38662306a36Sopenharmony_ci b47n->nand_chip.legacy.read_byte = bcm47xxnflash_ops_bcm4706_read_byte; 38762306a36Sopenharmony_ci b47n->nand_chip.legacy.read_buf = bcm47xxnflash_ops_bcm4706_read_buf; 38862306a36Sopenharmony_ci b47n->nand_chip.legacy.write_buf = bcm47xxnflash_ops_bcm4706_write_buf; 38962306a36Sopenharmony_ci b47n->nand_chip.legacy.set_features = nand_get_set_features_notsupp; 39062306a36Sopenharmony_ci b47n->nand_chip.legacy.get_features = nand_get_set_features_notsupp; 39162306a36Sopenharmony_ci 39262306a36Sopenharmony_ci nand_chip->legacy.chip_delay = 50; 39362306a36Sopenharmony_ci b47n->nand_chip.bbt_options = NAND_BBT_USE_FLASH; 39462306a36Sopenharmony_ci /* TODO: implement ECC */ 39562306a36Sopenharmony_ci b47n->nand_chip.ecc.engine_type = NAND_ECC_ENGINE_TYPE_NONE; 39662306a36Sopenharmony_ci 39762306a36Sopenharmony_ci /* Enable NAND flash access */ 39862306a36Sopenharmony_ci bcma_cc_set32(b47n->cc, BCMA_CC_4706_FLASHSCFG, 39962306a36Sopenharmony_ci BCMA_CC_4706_FLASHSCFG_NF1); 40062306a36Sopenharmony_ci 40162306a36Sopenharmony_ci /* Configure wait counters */ 40262306a36Sopenharmony_ci if (b47n->cc->status & BCMA_CC_CHIPST_4706_PKG_OPTION) { 40362306a36Sopenharmony_ci /* 400 MHz */ 40462306a36Sopenharmony_ci freq = 400000000 / 4; 40562306a36Sopenharmony_ci } else { 40662306a36Sopenharmony_ci freq = bcma_chipco_pll_read(b47n->cc, 4); 40762306a36Sopenharmony_ci freq = (freq & 0xFFF) >> 3; 40862306a36Sopenharmony_ci /* Fixed reference clock 25 MHz and m = 2 */ 40962306a36Sopenharmony_ci freq = (freq * 25000000 / 2) / 4; 41062306a36Sopenharmony_ci } 41162306a36Sopenharmony_ci clock = freq / 1000000; 41262306a36Sopenharmony_ci w0 = bcm47xxnflash_ops_bcm4706_ns_to_cycle(15, clock); 41362306a36Sopenharmony_ci w1 = bcm47xxnflash_ops_bcm4706_ns_to_cycle(20, clock); 41462306a36Sopenharmony_ci w2 = bcm47xxnflash_ops_bcm4706_ns_to_cycle(10, clock); 41562306a36Sopenharmony_ci w3 = bcm47xxnflash_ops_bcm4706_ns_to_cycle(10, clock); 41662306a36Sopenharmony_ci w4 = bcm47xxnflash_ops_bcm4706_ns_to_cycle(100, clock); 41762306a36Sopenharmony_ci bcma_cc_write32(b47n->cc, BCMA_CC_NFLASH_WAITCNT0, 41862306a36Sopenharmony_ci (w4 << 24 | w3 << 18 | w2 << 12 | w1 << 6 | w0)); 41962306a36Sopenharmony_ci 42062306a36Sopenharmony_ci /* Scan NAND */ 42162306a36Sopenharmony_ci err = nand_scan(&b47n->nand_chip, 1); 42262306a36Sopenharmony_ci if (err) { 42362306a36Sopenharmony_ci pr_err("Could not scan NAND flash: %d\n", err); 42462306a36Sopenharmony_ci goto exit; 42562306a36Sopenharmony_ci } 42662306a36Sopenharmony_ci 42762306a36Sopenharmony_ci /* Configure FLASH */ 42862306a36Sopenharmony_ci chipsize = nanddev_target_size(&b47n->nand_chip.base) >> 20; 42962306a36Sopenharmony_ci tbits = ffs(chipsize); /* find first bit set */ 43062306a36Sopenharmony_ci if (!tbits || tbits != fls(chipsize)) { 43162306a36Sopenharmony_ci pr_err("Invalid flash size: 0x%lX\n", chipsize); 43262306a36Sopenharmony_ci err = -ENOTSUPP; 43362306a36Sopenharmony_ci goto exit; 43462306a36Sopenharmony_ci } 43562306a36Sopenharmony_ci tbits += 19; /* Broadcom increases *index* by 20, we increase *pos* */ 43662306a36Sopenharmony_ci 43762306a36Sopenharmony_ci col_bits = b47n->nand_chip.page_shift + 1; 43862306a36Sopenharmony_ci col_size = (col_bits + 7) / 8; 43962306a36Sopenharmony_ci 44062306a36Sopenharmony_ci row_bits = tbits - col_bits + 1; 44162306a36Sopenharmony_ci row_bsize = (row_bits + 7) / 8; 44262306a36Sopenharmony_ci 44362306a36Sopenharmony_ci val = ((row_bsize - 1) << 6) | ((col_size - 1) << 4) | 2; 44462306a36Sopenharmony_ci bcma_cc_write32(b47n->cc, BCMA_CC_NFLASH_CONF, val); 44562306a36Sopenharmony_ci 44662306a36Sopenharmony_ciexit: 44762306a36Sopenharmony_ci if (err) 44862306a36Sopenharmony_ci bcma_cc_mask32(b47n->cc, BCMA_CC_4706_FLASHSCFG, 44962306a36Sopenharmony_ci ~BCMA_CC_4706_FLASHSCFG_NF1); 45062306a36Sopenharmony_ci return err; 45162306a36Sopenharmony_ci} 452