18c2ecf20Sopenharmony_ci/* 28c2ecf20Sopenharmony_ci * Toshiba TMIO NAND flash controller driver 38c2ecf20Sopenharmony_ci * 48c2ecf20Sopenharmony_ci * Slightly murky pre-git history of the driver: 58c2ecf20Sopenharmony_ci * 68c2ecf20Sopenharmony_ci * Copyright (c) Ian Molton 2004, 2005, 2008 78c2ecf20Sopenharmony_ci * Original work, independent of sharps code. Included hardware ECC support. 88c2ecf20Sopenharmony_ci * Hard ECC did not work for writes in the early revisions. 98c2ecf20Sopenharmony_ci * Copyright (c) Dirk Opfer 2005. 108c2ecf20Sopenharmony_ci * Modifications developed from sharps code but 118c2ecf20Sopenharmony_ci * NOT containing any, ported onto Ians base. 128c2ecf20Sopenharmony_ci * Copyright (c) Chris Humbert 2005 138c2ecf20Sopenharmony_ci * Copyright (c) Dmitry Baryshkov 2008 148c2ecf20Sopenharmony_ci * Minor fixes 158c2ecf20Sopenharmony_ci * 168c2ecf20Sopenharmony_ci * Parts copyright Sebastian Carlier 178c2ecf20Sopenharmony_ci * 188c2ecf20Sopenharmony_ci * This file is licensed under 198c2ecf20Sopenharmony_ci * the terms of the GNU General Public License version 2. This program 208c2ecf20Sopenharmony_ci * is licensed "as is" without any warranty of any kind, whether express 218c2ecf20Sopenharmony_ci * or implied. 228c2ecf20Sopenharmony_ci * 238c2ecf20Sopenharmony_ci */ 248c2ecf20Sopenharmony_ci 258c2ecf20Sopenharmony_ci 268c2ecf20Sopenharmony_ci#include <linux/kernel.h> 278c2ecf20Sopenharmony_ci#include <linux/module.h> 288c2ecf20Sopenharmony_ci#include <linux/platform_device.h> 298c2ecf20Sopenharmony_ci#include <linux/mfd/core.h> 308c2ecf20Sopenharmony_ci#include <linux/mfd/tmio.h> 318c2ecf20Sopenharmony_ci#include <linux/delay.h> 328c2ecf20Sopenharmony_ci#include <linux/io.h> 338c2ecf20Sopenharmony_ci#include <linux/irq.h> 348c2ecf20Sopenharmony_ci#include <linux/interrupt.h> 358c2ecf20Sopenharmony_ci#include <linux/ioport.h> 368c2ecf20Sopenharmony_ci#include <linux/mtd/mtd.h> 378c2ecf20Sopenharmony_ci#include <linux/mtd/rawnand.h> 388c2ecf20Sopenharmony_ci#include <linux/mtd/nand_ecc.h> 398c2ecf20Sopenharmony_ci#include <linux/mtd/partitions.h> 408c2ecf20Sopenharmony_ci#include <linux/slab.h> 418c2ecf20Sopenharmony_ci 428c2ecf20Sopenharmony_ci/*--------------------------------------------------------------------------*/ 438c2ecf20Sopenharmony_ci 448c2ecf20Sopenharmony_ci/* 458c2ecf20Sopenharmony_ci * NAND Flash Host Controller Configuration Register 468c2ecf20Sopenharmony_ci */ 478c2ecf20Sopenharmony_ci#define CCR_COMMAND 0x04 /* w Command */ 488c2ecf20Sopenharmony_ci#define CCR_BASE 0x10 /* l NAND Flash Control Reg Base Addr */ 498c2ecf20Sopenharmony_ci#define CCR_INTP 0x3d /* b Interrupt Pin */ 508c2ecf20Sopenharmony_ci#define CCR_INTE 0x48 /* b Interrupt Enable */ 518c2ecf20Sopenharmony_ci#define CCR_EC 0x4a /* b Event Control */ 528c2ecf20Sopenharmony_ci#define CCR_ICC 0x4c /* b Internal Clock Control */ 538c2ecf20Sopenharmony_ci#define CCR_ECCC 0x5b /* b ECC Control */ 548c2ecf20Sopenharmony_ci#define CCR_NFTC 0x60 /* b NAND Flash Transaction Control */ 558c2ecf20Sopenharmony_ci#define CCR_NFM 0x61 /* b NAND Flash Monitor */ 568c2ecf20Sopenharmony_ci#define CCR_NFPSC 0x62 /* b NAND Flash Power Supply Control */ 578c2ecf20Sopenharmony_ci#define CCR_NFDC 0x63 /* b NAND Flash Detect Control */ 588c2ecf20Sopenharmony_ci 598c2ecf20Sopenharmony_ci/* 608c2ecf20Sopenharmony_ci * NAND Flash Control Register 618c2ecf20Sopenharmony_ci */ 628c2ecf20Sopenharmony_ci#define FCR_DATA 0x00 /* bwl Data Register */ 638c2ecf20Sopenharmony_ci#define FCR_MODE 0x04 /* b Mode Register */ 648c2ecf20Sopenharmony_ci#define FCR_STATUS 0x05 /* b Status Register */ 658c2ecf20Sopenharmony_ci#define FCR_ISR 0x06 /* b Interrupt Status Register */ 668c2ecf20Sopenharmony_ci#define FCR_IMR 0x07 /* b Interrupt Mask Register */ 678c2ecf20Sopenharmony_ci 688c2ecf20Sopenharmony_ci/* FCR_MODE Register Command List */ 698c2ecf20Sopenharmony_ci#define FCR_MODE_DATA 0x94 /* Data Data_Mode */ 708c2ecf20Sopenharmony_ci#define FCR_MODE_COMMAND 0x95 /* Data Command_Mode */ 718c2ecf20Sopenharmony_ci#define FCR_MODE_ADDRESS 0x96 /* Data Address_Mode */ 728c2ecf20Sopenharmony_ci 738c2ecf20Sopenharmony_ci#define FCR_MODE_HWECC_CALC 0xB4 /* HW-ECC Data */ 748c2ecf20Sopenharmony_ci#define FCR_MODE_HWECC_RESULT 0xD4 /* HW-ECC Calc result Read_Mode */ 758c2ecf20Sopenharmony_ci#define FCR_MODE_HWECC_RESET 0xF4 /* HW-ECC Reset */ 768c2ecf20Sopenharmony_ci 778c2ecf20Sopenharmony_ci#define FCR_MODE_POWER_ON 0x0C /* Power Supply ON to SSFDC card */ 788c2ecf20Sopenharmony_ci#define FCR_MODE_POWER_OFF 0x08 /* Power Supply OFF to SSFDC card */ 798c2ecf20Sopenharmony_ci 808c2ecf20Sopenharmony_ci#define FCR_MODE_LED_OFF 0x00 /* LED OFF */ 818c2ecf20Sopenharmony_ci#define FCR_MODE_LED_ON 0x04 /* LED ON */ 828c2ecf20Sopenharmony_ci 838c2ecf20Sopenharmony_ci#define FCR_MODE_EJECT_ON 0x68 /* Ejection events active */ 848c2ecf20Sopenharmony_ci#define FCR_MODE_EJECT_OFF 0x08 /* Ejection events ignored */ 858c2ecf20Sopenharmony_ci 868c2ecf20Sopenharmony_ci#define FCR_MODE_LOCK 0x6C /* Lock_Mode. Eject Switch Invalid */ 878c2ecf20Sopenharmony_ci#define FCR_MODE_UNLOCK 0x0C /* UnLock_Mode. Eject Switch is valid */ 888c2ecf20Sopenharmony_ci 898c2ecf20Sopenharmony_ci#define FCR_MODE_CONTROLLER_ID 0x40 /* Controller ID Read */ 908c2ecf20Sopenharmony_ci#define FCR_MODE_STANDBY 0x00 /* SSFDC card Changes Standby State */ 918c2ecf20Sopenharmony_ci 928c2ecf20Sopenharmony_ci#define FCR_MODE_WE 0x80 938c2ecf20Sopenharmony_ci#define FCR_MODE_ECC1 0x40 948c2ecf20Sopenharmony_ci#define FCR_MODE_ECC0 0x20 958c2ecf20Sopenharmony_ci#define FCR_MODE_CE 0x10 968c2ecf20Sopenharmony_ci#define FCR_MODE_PCNT1 0x08 978c2ecf20Sopenharmony_ci#define FCR_MODE_PCNT0 0x04 988c2ecf20Sopenharmony_ci#define FCR_MODE_ALE 0x02 998c2ecf20Sopenharmony_ci#define FCR_MODE_CLE 0x01 1008c2ecf20Sopenharmony_ci 1018c2ecf20Sopenharmony_ci#define FCR_STATUS_BUSY 0x80 1028c2ecf20Sopenharmony_ci 1038c2ecf20Sopenharmony_ci/*--------------------------------------------------------------------------*/ 1048c2ecf20Sopenharmony_ci 1058c2ecf20Sopenharmony_cistruct tmio_nand { 1068c2ecf20Sopenharmony_ci struct nand_controller controller; 1078c2ecf20Sopenharmony_ci struct nand_chip chip; 1088c2ecf20Sopenharmony_ci struct completion comp; 1098c2ecf20Sopenharmony_ci 1108c2ecf20Sopenharmony_ci struct platform_device *dev; 1118c2ecf20Sopenharmony_ci 1128c2ecf20Sopenharmony_ci void __iomem *ccr; 1138c2ecf20Sopenharmony_ci void __iomem *fcr; 1148c2ecf20Sopenharmony_ci unsigned long fcr_base; 1158c2ecf20Sopenharmony_ci 1168c2ecf20Sopenharmony_ci unsigned int irq; 1178c2ecf20Sopenharmony_ci 1188c2ecf20Sopenharmony_ci /* for tmio_nand_read_byte */ 1198c2ecf20Sopenharmony_ci u8 read; 1208c2ecf20Sopenharmony_ci unsigned read_good:1; 1218c2ecf20Sopenharmony_ci}; 1228c2ecf20Sopenharmony_ci 1238c2ecf20Sopenharmony_cistatic inline struct tmio_nand *mtd_to_tmio(struct mtd_info *mtd) 1248c2ecf20Sopenharmony_ci{ 1258c2ecf20Sopenharmony_ci return container_of(mtd_to_nand(mtd), struct tmio_nand, chip); 1268c2ecf20Sopenharmony_ci} 1278c2ecf20Sopenharmony_ci 1288c2ecf20Sopenharmony_ci 1298c2ecf20Sopenharmony_ci/*--------------------------------------------------------------------------*/ 1308c2ecf20Sopenharmony_ci 1318c2ecf20Sopenharmony_cistatic void tmio_nand_hwcontrol(struct nand_chip *chip, int cmd, 1328c2ecf20Sopenharmony_ci unsigned int ctrl) 1338c2ecf20Sopenharmony_ci{ 1348c2ecf20Sopenharmony_ci struct tmio_nand *tmio = mtd_to_tmio(nand_to_mtd(chip)); 1358c2ecf20Sopenharmony_ci 1368c2ecf20Sopenharmony_ci if (ctrl & NAND_CTRL_CHANGE) { 1378c2ecf20Sopenharmony_ci u8 mode; 1388c2ecf20Sopenharmony_ci 1398c2ecf20Sopenharmony_ci if (ctrl & NAND_NCE) { 1408c2ecf20Sopenharmony_ci mode = FCR_MODE_DATA; 1418c2ecf20Sopenharmony_ci 1428c2ecf20Sopenharmony_ci if (ctrl & NAND_CLE) 1438c2ecf20Sopenharmony_ci mode |= FCR_MODE_CLE; 1448c2ecf20Sopenharmony_ci else 1458c2ecf20Sopenharmony_ci mode &= ~FCR_MODE_CLE; 1468c2ecf20Sopenharmony_ci 1478c2ecf20Sopenharmony_ci if (ctrl & NAND_ALE) 1488c2ecf20Sopenharmony_ci mode |= FCR_MODE_ALE; 1498c2ecf20Sopenharmony_ci else 1508c2ecf20Sopenharmony_ci mode &= ~FCR_MODE_ALE; 1518c2ecf20Sopenharmony_ci } else { 1528c2ecf20Sopenharmony_ci mode = FCR_MODE_STANDBY; 1538c2ecf20Sopenharmony_ci } 1548c2ecf20Sopenharmony_ci 1558c2ecf20Sopenharmony_ci tmio_iowrite8(mode, tmio->fcr + FCR_MODE); 1568c2ecf20Sopenharmony_ci tmio->read_good = 0; 1578c2ecf20Sopenharmony_ci } 1588c2ecf20Sopenharmony_ci 1598c2ecf20Sopenharmony_ci if (cmd != NAND_CMD_NONE) 1608c2ecf20Sopenharmony_ci tmio_iowrite8(cmd, chip->legacy.IO_ADDR_W); 1618c2ecf20Sopenharmony_ci} 1628c2ecf20Sopenharmony_ci 1638c2ecf20Sopenharmony_cistatic int tmio_nand_dev_ready(struct nand_chip *chip) 1648c2ecf20Sopenharmony_ci{ 1658c2ecf20Sopenharmony_ci struct tmio_nand *tmio = mtd_to_tmio(nand_to_mtd(chip)); 1668c2ecf20Sopenharmony_ci 1678c2ecf20Sopenharmony_ci return !(tmio_ioread8(tmio->fcr + FCR_STATUS) & FCR_STATUS_BUSY); 1688c2ecf20Sopenharmony_ci} 1698c2ecf20Sopenharmony_ci 1708c2ecf20Sopenharmony_cistatic irqreturn_t tmio_irq(int irq, void *__tmio) 1718c2ecf20Sopenharmony_ci{ 1728c2ecf20Sopenharmony_ci struct tmio_nand *tmio = __tmio; 1738c2ecf20Sopenharmony_ci 1748c2ecf20Sopenharmony_ci /* disable RDYREQ interrupt */ 1758c2ecf20Sopenharmony_ci tmio_iowrite8(0x00, tmio->fcr + FCR_IMR); 1768c2ecf20Sopenharmony_ci complete(&tmio->comp); 1778c2ecf20Sopenharmony_ci 1788c2ecf20Sopenharmony_ci return IRQ_HANDLED; 1798c2ecf20Sopenharmony_ci} 1808c2ecf20Sopenharmony_ci 1818c2ecf20Sopenharmony_ci/* 1828c2ecf20Sopenharmony_ci *The TMIO core has a RDYREQ interrupt on the posedge of #SMRB. 1838c2ecf20Sopenharmony_ci *This interrupt is normally disabled, but for long operations like 1848c2ecf20Sopenharmony_ci *erase and write, we enable it to wake us up. The irq handler 1858c2ecf20Sopenharmony_ci *disables the interrupt. 1868c2ecf20Sopenharmony_ci */ 1878c2ecf20Sopenharmony_cistatic int tmio_nand_wait(struct nand_chip *nand_chip) 1888c2ecf20Sopenharmony_ci{ 1898c2ecf20Sopenharmony_ci struct tmio_nand *tmio = mtd_to_tmio(nand_to_mtd(nand_chip)); 1908c2ecf20Sopenharmony_ci long timeout; 1918c2ecf20Sopenharmony_ci u8 status; 1928c2ecf20Sopenharmony_ci 1938c2ecf20Sopenharmony_ci /* enable RDYREQ interrupt */ 1948c2ecf20Sopenharmony_ci 1958c2ecf20Sopenharmony_ci tmio_iowrite8(0x0f, tmio->fcr + FCR_ISR); 1968c2ecf20Sopenharmony_ci reinit_completion(&tmio->comp); 1978c2ecf20Sopenharmony_ci tmio_iowrite8(0x81, tmio->fcr + FCR_IMR); 1988c2ecf20Sopenharmony_ci 1998c2ecf20Sopenharmony_ci timeout = 400; 2008c2ecf20Sopenharmony_ci timeout = wait_for_completion_timeout(&tmio->comp, 2018c2ecf20Sopenharmony_ci msecs_to_jiffies(timeout)); 2028c2ecf20Sopenharmony_ci 2038c2ecf20Sopenharmony_ci if (unlikely(!tmio_nand_dev_ready(nand_chip))) { 2048c2ecf20Sopenharmony_ci tmio_iowrite8(0x00, tmio->fcr + FCR_IMR); 2058c2ecf20Sopenharmony_ci dev_warn(&tmio->dev->dev, "still busy after 400 ms\n"); 2068c2ecf20Sopenharmony_ci 2078c2ecf20Sopenharmony_ci } else if (unlikely(!timeout)) { 2088c2ecf20Sopenharmony_ci tmio_iowrite8(0x00, tmio->fcr + FCR_IMR); 2098c2ecf20Sopenharmony_ci dev_warn(&tmio->dev->dev, "timeout waiting for interrupt\n"); 2108c2ecf20Sopenharmony_ci } 2118c2ecf20Sopenharmony_ci 2128c2ecf20Sopenharmony_ci nand_status_op(nand_chip, &status); 2138c2ecf20Sopenharmony_ci return status; 2148c2ecf20Sopenharmony_ci} 2158c2ecf20Sopenharmony_ci 2168c2ecf20Sopenharmony_ci/* 2178c2ecf20Sopenharmony_ci *The TMIO controller combines two 8-bit data bytes into one 16-bit 2188c2ecf20Sopenharmony_ci *word. This function separates them so nand_base.c works as expected, 2198c2ecf20Sopenharmony_ci *especially its NAND_CMD_READID routines. 2208c2ecf20Sopenharmony_ci * 2218c2ecf20Sopenharmony_ci *To prevent stale data from being read, tmio_nand_hwcontrol() clears 2228c2ecf20Sopenharmony_ci *tmio->read_good. 2238c2ecf20Sopenharmony_ci */ 2248c2ecf20Sopenharmony_cistatic u_char tmio_nand_read_byte(struct nand_chip *chip) 2258c2ecf20Sopenharmony_ci{ 2268c2ecf20Sopenharmony_ci struct tmio_nand *tmio = mtd_to_tmio(nand_to_mtd(chip)); 2278c2ecf20Sopenharmony_ci unsigned int data; 2288c2ecf20Sopenharmony_ci 2298c2ecf20Sopenharmony_ci if (tmio->read_good--) 2308c2ecf20Sopenharmony_ci return tmio->read; 2318c2ecf20Sopenharmony_ci 2328c2ecf20Sopenharmony_ci data = tmio_ioread16(tmio->fcr + FCR_DATA); 2338c2ecf20Sopenharmony_ci tmio->read = data >> 8; 2348c2ecf20Sopenharmony_ci return data; 2358c2ecf20Sopenharmony_ci} 2368c2ecf20Sopenharmony_ci 2378c2ecf20Sopenharmony_ci/* 2388c2ecf20Sopenharmony_ci *The TMIO controller converts an 8-bit NAND interface to a 16-bit 2398c2ecf20Sopenharmony_ci *bus interface, so all data reads and writes must be 16-bit wide. 2408c2ecf20Sopenharmony_ci *Thus, we implement 16-bit versions of the read, write, and verify 2418c2ecf20Sopenharmony_ci *buffer functions. 2428c2ecf20Sopenharmony_ci */ 2438c2ecf20Sopenharmony_cistatic void 2448c2ecf20Sopenharmony_citmio_nand_write_buf(struct nand_chip *chip, const u_char *buf, int len) 2458c2ecf20Sopenharmony_ci{ 2468c2ecf20Sopenharmony_ci struct tmio_nand *tmio = mtd_to_tmio(nand_to_mtd(chip)); 2478c2ecf20Sopenharmony_ci 2488c2ecf20Sopenharmony_ci tmio_iowrite16_rep(tmio->fcr + FCR_DATA, buf, len >> 1); 2498c2ecf20Sopenharmony_ci} 2508c2ecf20Sopenharmony_ci 2518c2ecf20Sopenharmony_cistatic void tmio_nand_read_buf(struct nand_chip *chip, u_char *buf, int len) 2528c2ecf20Sopenharmony_ci{ 2538c2ecf20Sopenharmony_ci struct tmio_nand *tmio = mtd_to_tmio(nand_to_mtd(chip)); 2548c2ecf20Sopenharmony_ci 2558c2ecf20Sopenharmony_ci tmio_ioread16_rep(tmio->fcr + FCR_DATA, buf, len >> 1); 2568c2ecf20Sopenharmony_ci} 2578c2ecf20Sopenharmony_ci 2588c2ecf20Sopenharmony_cistatic void tmio_nand_enable_hwecc(struct nand_chip *chip, int mode) 2598c2ecf20Sopenharmony_ci{ 2608c2ecf20Sopenharmony_ci struct tmio_nand *tmio = mtd_to_tmio(nand_to_mtd(chip)); 2618c2ecf20Sopenharmony_ci 2628c2ecf20Sopenharmony_ci tmio_iowrite8(FCR_MODE_HWECC_RESET, tmio->fcr + FCR_MODE); 2638c2ecf20Sopenharmony_ci tmio_ioread8(tmio->fcr + FCR_DATA); /* dummy read */ 2648c2ecf20Sopenharmony_ci tmio_iowrite8(FCR_MODE_HWECC_CALC, tmio->fcr + FCR_MODE); 2658c2ecf20Sopenharmony_ci} 2668c2ecf20Sopenharmony_ci 2678c2ecf20Sopenharmony_cistatic int tmio_nand_calculate_ecc(struct nand_chip *chip, const u_char *dat, 2688c2ecf20Sopenharmony_ci u_char *ecc_code) 2698c2ecf20Sopenharmony_ci{ 2708c2ecf20Sopenharmony_ci struct tmio_nand *tmio = mtd_to_tmio(nand_to_mtd(chip)); 2718c2ecf20Sopenharmony_ci unsigned int ecc; 2728c2ecf20Sopenharmony_ci 2738c2ecf20Sopenharmony_ci tmio_iowrite8(FCR_MODE_HWECC_RESULT, tmio->fcr + FCR_MODE); 2748c2ecf20Sopenharmony_ci 2758c2ecf20Sopenharmony_ci ecc = tmio_ioread16(tmio->fcr + FCR_DATA); 2768c2ecf20Sopenharmony_ci ecc_code[1] = ecc; /* 000-255 LP7-0 */ 2778c2ecf20Sopenharmony_ci ecc_code[0] = ecc >> 8; /* 000-255 LP15-8 */ 2788c2ecf20Sopenharmony_ci ecc = tmio_ioread16(tmio->fcr + FCR_DATA); 2798c2ecf20Sopenharmony_ci ecc_code[2] = ecc; /* 000-255 CP5-0,11b */ 2808c2ecf20Sopenharmony_ci ecc_code[4] = ecc >> 8; /* 256-511 LP7-0 */ 2818c2ecf20Sopenharmony_ci ecc = tmio_ioread16(tmio->fcr + FCR_DATA); 2828c2ecf20Sopenharmony_ci ecc_code[3] = ecc; /* 256-511 LP15-8 */ 2838c2ecf20Sopenharmony_ci ecc_code[5] = ecc >> 8; /* 256-511 CP5-0,11b */ 2848c2ecf20Sopenharmony_ci 2858c2ecf20Sopenharmony_ci tmio_iowrite8(FCR_MODE_DATA, tmio->fcr + FCR_MODE); 2868c2ecf20Sopenharmony_ci return 0; 2878c2ecf20Sopenharmony_ci} 2888c2ecf20Sopenharmony_ci 2898c2ecf20Sopenharmony_cistatic int tmio_nand_correct_data(struct nand_chip *chip, unsigned char *buf, 2908c2ecf20Sopenharmony_ci unsigned char *read_ecc, 2918c2ecf20Sopenharmony_ci unsigned char *calc_ecc) 2928c2ecf20Sopenharmony_ci{ 2938c2ecf20Sopenharmony_ci int r0, r1; 2948c2ecf20Sopenharmony_ci 2958c2ecf20Sopenharmony_ci /* assume ecc.size = 512 and ecc.bytes = 6 */ 2968c2ecf20Sopenharmony_ci r0 = __nand_correct_data(buf, read_ecc, calc_ecc, 256, false); 2978c2ecf20Sopenharmony_ci if (r0 < 0) 2988c2ecf20Sopenharmony_ci return r0; 2998c2ecf20Sopenharmony_ci r1 = __nand_correct_data(buf + 256, read_ecc + 3, calc_ecc + 3, 256, 3008c2ecf20Sopenharmony_ci false); 3018c2ecf20Sopenharmony_ci if (r1 < 0) 3028c2ecf20Sopenharmony_ci return r1; 3038c2ecf20Sopenharmony_ci return r0 + r1; 3048c2ecf20Sopenharmony_ci} 3058c2ecf20Sopenharmony_ci 3068c2ecf20Sopenharmony_cistatic int tmio_hw_init(struct platform_device *dev, struct tmio_nand *tmio) 3078c2ecf20Sopenharmony_ci{ 3088c2ecf20Sopenharmony_ci const struct mfd_cell *cell = mfd_get_cell(dev); 3098c2ecf20Sopenharmony_ci int ret; 3108c2ecf20Sopenharmony_ci 3118c2ecf20Sopenharmony_ci if (cell->enable) { 3128c2ecf20Sopenharmony_ci ret = cell->enable(dev); 3138c2ecf20Sopenharmony_ci if (ret) 3148c2ecf20Sopenharmony_ci return ret; 3158c2ecf20Sopenharmony_ci } 3168c2ecf20Sopenharmony_ci 3178c2ecf20Sopenharmony_ci /* (4Ch) CLKRUN Enable 1st spcrunc */ 3188c2ecf20Sopenharmony_ci tmio_iowrite8(0x81, tmio->ccr + CCR_ICC); 3198c2ecf20Sopenharmony_ci 3208c2ecf20Sopenharmony_ci /* (10h)BaseAddress 0x1000 spba.spba2 */ 3218c2ecf20Sopenharmony_ci tmio_iowrite16(tmio->fcr_base, tmio->ccr + CCR_BASE); 3228c2ecf20Sopenharmony_ci tmio_iowrite16(tmio->fcr_base >> 16, tmio->ccr + CCR_BASE + 2); 3238c2ecf20Sopenharmony_ci 3248c2ecf20Sopenharmony_ci /* (04h)Command Register I/O spcmd */ 3258c2ecf20Sopenharmony_ci tmio_iowrite8(0x02, tmio->ccr + CCR_COMMAND); 3268c2ecf20Sopenharmony_ci 3278c2ecf20Sopenharmony_ci /* (62h) Power Supply Control ssmpwc */ 3288c2ecf20Sopenharmony_ci /* HardPowerOFF - SuspendOFF - PowerSupplyWait_4MS */ 3298c2ecf20Sopenharmony_ci tmio_iowrite8(0x02, tmio->ccr + CCR_NFPSC); 3308c2ecf20Sopenharmony_ci 3318c2ecf20Sopenharmony_ci /* (63h) Detect Control ssmdtc */ 3328c2ecf20Sopenharmony_ci tmio_iowrite8(0x02, tmio->ccr + CCR_NFDC); 3338c2ecf20Sopenharmony_ci 3348c2ecf20Sopenharmony_ci /* Interrupt status register clear sintst */ 3358c2ecf20Sopenharmony_ci tmio_iowrite8(0x0f, tmio->fcr + FCR_ISR); 3368c2ecf20Sopenharmony_ci 3378c2ecf20Sopenharmony_ci /* After power supply, Media are reset smode */ 3388c2ecf20Sopenharmony_ci tmio_iowrite8(FCR_MODE_POWER_ON, tmio->fcr + FCR_MODE); 3398c2ecf20Sopenharmony_ci tmio_iowrite8(FCR_MODE_COMMAND, tmio->fcr + FCR_MODE); 3408c2ecf20Sopenharmony_ci tmio_iowrite8(NAND_CMD_RESET, tmio->fcr + FCR_DATA); 3418c2ecf20Sopenharmony_ci 3428c2ecf20Sopenharmony_ci /* Standby Mode smode */ 3438c2ecf20Sopenharmony_ci tmio_iowrite8(FCR_MODE_STANDBY, tmio->fcr + FCR_MODE); 3448c2ecf20Sopenharmony_ci 3458c2ecf20Sopenharmony_ci mdelay(5); 3468c2ecf20Sopenharmony_ci 3478c2ecf20Sopenharmony_ci return 0; 3488c2ecf20Sopenharmony_ci} 3498c2ecf20Sopenharmony_ci 3508c2ecf20Sopenharmony_cistatic void tmio_hw_stop(struct platform_device *dev, struct tmio_nand *tmio) 3518c2ecf20Sopenharmony_ci{ 3528c2ecf20Sopenharmony_ci const struct mfd_cell *cell = mfd_get_cell(dev); 3538c2ecf20Sopenharmony_ci 3548c2ecf20Sopenharmony_ci tmio_iowrite8(FCR_MODE_POWER_OFF, tmio->fcr + FCR_MODE); 3558c2ecf20Sopenharmony_ci if (cell->disable) 3568c2ecf20Sopenharmony_ci cell->disable(dev); 3578c2ecf20Sopenharmony_ci} 3588c2ecf20Sopenharmony_ci 3598c2ecf20Sopenharmony_cistatic int tmio_attach_chip(struct nand_chip *chip) 3608c2ecf20Sopenharmony_ci{ 3618c2ecf20Sopenharmony_ci if (chip->ecc.engine_type != NAND_ECC_ENGINE_TYPE_ON_HOST) 3628c2ecf20Sopenharmony_ci return 0; 3638c2ecf20Sopenharmony_ci 3648c2ecf20Sopenharmony_ci chip->ecc.size = 512; 3658c2ecf20Sopenharmony_ci chip->ecc.bytes = 6; 3668c2ecf20Sopenharmony_ci chip->ecc.strength = 2; 3678c2ecf20Sopenharmony_ci chip->ecc.hwctl = tmio_nand_enable_hwecc; 3688c2ecf20Sopenharmony_ci chip->ecc.calculate = tmio_nand_calculate_ecc; 3698c2ecf20Sopenharmony_ci chip->ecc.correct = tmio_nand_correct_data; 3708c2ecf20Sopenharmony_ci 3718c2ecf20Sopenharmony_ci return 0; 3728c2ecf20Sopenharmony_ci} 3738c2ecf20Sopenharmony_ci 3748c2ecf20Sopenharmony_cistatic const struct nand_controller_ops tmio_ops = { 3758c2ecf20Sopenharmony_ci .attach_chip = tmio_attach_chip, 3768c2ecf20Sopenharmony_ci}; 3778c2ecf20Sopenharmony_ci 3788c2ecf20Sopenharmony_cistatic int tmio_probe(struct platform_device *dev) 3798c2ecf20Sopenharmony_ci{ 3808c2ecf20Sopenharmony_ci struct tmio_nand_data *data = dev_get_platdata(&dev->dev); 3818c2ecf20Sopenharmony_ci struct resource *fcr = platform_get_resource(dev, 3828c2ecf20Sopenharmony_ci IORESOURCE_MEM, 0); 3838c2ecf20Sopenharmony_ci struct resource *ccr = platform_get_resource(dev, 3848c2ecf20Sopenharmony_ci IORESOURCE_MEM, 1); 3858c2ecf20Sopenharmony_ci int irq = platform_get_irq(dev, 0); 3868c2ecf20Sopenharmony_ci struct tmio_nand *tmio; 3878c2ecf20Sopenharmony_ci struct mtd_info *mtd; 3888c2ecf20Sopenharmony_ci struct nand_chip *nand_chip; 3898c2ecf20Sopenharmony_ci int retval; 3908c2ecf20Sopenharmony_ci 3918c2ecf20Sopenharmony_ci if (data == NULL) 3928c2ecf20Sopenharmony_ci dev_warn(&dev->dev, "NULL platform data!\n"); 3938c2ecf20Sopenharmony_ci 3948c2ecf20Sopenharmony_ci tmio = devm_kzalloc(&dev->dev, sizeof(*tmio), GFP_KERNEL); 3958c2ecf20Sopenharmony_ci if (!tmio) 3968c2ecf20Sopenharmony_ci return -ENOMEM; 3978c2ecf20Sopenharmony_ci 3988c2ecf20Sopenharmony_ci init_completion(&tmio->comp); 3998c2ecf20Sopenharmony_ci 4008c2ecf20Sopenharmony_ci tmio->dev = dev; 4018c2ecf20Sopenharmony_ci 4028c2ecf20Sopenharmony_ci platform_set_drvdata(dev, tmio); 4038c2ecf20Sopenharmony_ci nand_chip = &tmio->chip; 4048c2ecf20Sopenharmony_ci mtd = nand_to_mtd(nand_chip); 4058c2ecf20Sopenharmony_ci mtd->name = "tmio-nand"; 4068c2ecf20Sopenharmony_ci mtd->dev.parent = &dev->dev; 4078c2ecf20Sopenharmony_ci 4088c2ecf20Sopenharmony_ci nand_controller_init(&tmio->controller); 4098c2ecf20Sopenharmony_ci tmio->controller.ops = &tmio_ops; 4108c2ecf20Sopenharmony_ci nand_chip->controller = &tmio->controller; 4118c2ecf20Sopenharmony_ci 4128c2ecf20Sopenharmony_ci tmio->ccr = devm_ioremap(&dev->dev, ccr->start, resource_size(ccr)); 4138c2ecf20Sopenharmony_ci if (!tmio->ccr) 4148c2ecf20Sopenharmony_ci return -EIO; 4158c2ecf20Sopenharmony_ci 4168c2ecf20Sopenharmony_ci tmio->fcr_base = fcr->start & 0xfffff; 4178c2ecf20Sopenharmony_ci tmio->fcr = devm_ioremap(&dev->dev, fcr->start, resource_size(fcr)); 4188c2ecf20Sopenharmony_ci if (!tmio->fcr) 4198c2ecf20Sopenharmony_ci return -EIO; 4208c2ecf20Sopenharmony_ci 4218c2ecf20Sopenharmony_ci retval = tmio_hw_init(dev, tmio); 4228c2ecf20Sopenharmony_ci if (retval) 4238c2ecf20Sopenharmony_ci return retval; 4248c2ecf20Sopenharmony_ci 4258c2ecf20Sopenharmony_ci /* Set address of NAND IO lines */ 4268c2ecf20Sopenharmony_ci nand_chip->legacy.IO_ADDR_R = tmio->fcr; 4278c2ecf20Sopenharmony_ci nand_chip->legacy.IO_ADDR_W = tmio->fcr; 4288c2ecf20Sopenharmony_ci 4298c2ecf20Sopenharmony_ci /* Set address of hardware control function */ 4308c2ecf20Sopenharmony_ci nand_chip->legacy.cmd_ctrl = tmio_nand_hwcontrol; 4318c2ecf20Sopenharmony_ci nand_chip->legacy.dev_ready = tmio_nand_dev_ready; 4328c2ecf20Sopenharmony_ci nand_chip->legacy.read_byte = tmio_nand_read_byte; 4338c2ecf20Sopenharmony_ci nand_chip->legacy.write_buf = tmio_nand_write_buf; 4348c2ecf20Sopenharmony_ci nand_chip->legacy.read_buf = tmio_nand_read_buf; 4358c2ecf20Sopenharmony_ci 4368c2ecf20Sopenharmony_ci if (data) 4378c2ecf20Sopenharmony_ci nand_chip->badblock_pattern = data->badblock_pattern; 4388c2ecf20Sopenharmony_ci 4398c2ecf20Sopenharmony_ci /* 15 us command delay time */ 4408c2ecf20Sopenharmony_ci nand_chip->legacy.chip_delay = 15; 4418c2ecf20Sopenharmony_ci 4428c2ecf20Sopenharmony_ci retval = devm_request_irq(&dev->dev, irq, &tmio_irq, 0, 4438c2ecf20Sopenharmony_ci dev_name(&dev->dev), tmio); 4448c2ecf20Sopenharmony_ci if (retval) { 4458c2ecf20Sopenharmony_ci dev_err(&dev->dev, "request_irq error %d\n", retval); 4468c2ecf20Sopenharmony_ci goto err_irq; 4478c2ecf20Sopenharmony_ci } 4488c2ecf20Sopenharmony_ci 4498c2ecf20Sopenharmony_ci tmio->irq = irq; 4508c2ecf20Sopenharmony_ci nand_chip->legacy.waitfunc = tmio_nand_wait; 4518c2ecf20Sopenharmony_ci 4528c2ecf20Sopenharmony_ci /* Scan to find existence of the device */ 4538c2ecf20Sopenharmony_ci retval = nand_scan(nand_chip, 1); 4548c2ecf20Sopenharmony_ci if (retval) 4558c2ecf20Sopenharmony_ci goto err_irq; 4568c2ecf20Sopenharmony_ci 4578c2ecf20Sopenharmony_ci /* Register the partitions */ 4588c2ecf20Sopenharmony_ci retval = mtd_device_parse_register(mtd, 4598c2ecf20Sopenharmony_ci data ? data->part_parsers : NULL, 4608c2ecf20Sopenharmony_ci NULL, 4618c2ecf20Sopenharmony_ci data ? data->partition : NULL, 4628c2ecf20Sopenharmony_ci data ? data->num_partitions : 0); 4638c2ecf20Sopenharmony_ci if (!retval) 4648c2ecf20Sopenharmony_ci return retval; 4658c2ecf20Sopenharmony_ci 4668c2ecf20Sopenharmony_ci nand_cleanup(nand_chip); 4678c2ecf20Sopenharmony_ci 4688c2ecf20Sopenharmony_cierr_irq: 4698c2ecf20Sopenharmony_ci tmio_hw_stop(dev, tmio); 4708c2ecf20Sopenharmony_ci return retval; 4718c2ecf20Sopenharmony_ci} 4728c2ecf20Sopenharmony_ci 4738c2ecf20Sopenharmony_cistatic int tmio_remove(struct platform_device *dev) 4748c2ecf20Sopenharmony_ci{ 4758c2ecf20Sopenharmony_ci struct tmio_nand *tmio = platform_get_drvdata(dev); 4768c2ecf20Sopenharmony_ci struct nand_chip *chip = &tmio->chip; 4778c2ecf20Sopenharmony_ci int ret; 4788c2ecf20Sopenharmony_ci 4798c2ecf20Sopenharmony_ci ret = mtd_device_unregister(nand_to_mtd(chip)); 4808c2ecf20Sopenharmony_ci WARN_ON(ret); 4818c2ecf20Sopenharmony_ci nand_cleanup(chip); 4828c2ecf20Sopenharmony_ci tmio_hw_stop(dev, tmio); 4838c2ecf20Sopenharmony_ci return 0; 4848c2ecf20Sopenharmony_ci} 4858c2ecf20Sopenharmony_ci 4868c2ecf20Sopenharmony_ci#ifdef CONFIG_PM 4878c2ecf20Sopenharmony_cistatic int tmio_suspend(struct platform_device *dev, pm_message_t state) 4888c2ecf20Sopenharmony_ci{ 4898c2ecf20Sopenharmony_ci const struct mfd_cell *cell = mfd_get_cell(dev); 4908c2ecf20Sopenharmony_ci 4918c2ecf20Sopenharmony_ci if (cell->suspend) 4928c2ecf20Sopenharmony_ci cell->suspend(dev); 4938c2ecf20Sopenharmony_ci 4948c2ecf20Sopenharmony_ci tmio_hw_stop(dev, platform_get_drvdata(dev)); 4958c2ecf20Sopenharmony_ci return 0; 4968c2ecf20Sopenharmony_ci} 4978c2ecf20Sopenharmony_ci 4988c2ecf20Sopenharmony_cistatic int tmio_resume(struct platform_device *dev) 4998c2ecf20Sopenharmony_ci{ 5008c2ecf20Sopenharmony_ci const struct mfd_cell *cell = mfd_get_cell(dev); 5018c2ecf20Sopenharmony_ci 5028c2ecf20Sopenharmony_ci /* FIXME - is this required or merely another attack of the broken 5038c2ecf20Sopenharmony_ci * SHARP platform? Looks suspicious. 5048c2ecf20Sopenharmony_ci */ 5058c2ecf20Sopenharmony_ci tmio_hw_init(dev, platform_get_drvdata(dev)); 5068c2ecf20Sopenharmony_ci 5078c2ecf20Sopenharmony_ci if (cell->resume) 5088c2ecf20Sopenharmony_ci cell->resume(dev); 5098c2ecf20Sopenharmony_ci 5108c2ecf20Sopenharmony_ci return 0; 5118c2ecf20Sopenharmony_ci} 5128c2ecf20Sopenharmony_ci#else 5138c2ecf20Sopenharmony_ci#define tmio_suspend NULL 5148c2ecf20Sopenharmony_ci#define tmio_resume NULL 5158c2ecf20Sopenharmony_ci#endif 5168c2ecf20Sopenharmony_ci 5178c2ecf20Sopenharmony_cistatic struct platform_driver tmio_driver = { 5188c2ecf20Sopenharmony_ci .driver.name = "tmio-nand", 5198c2ecf20Sopenharmony_ci .driver.owner = THIS_MODULE, 5208c2ecf20Sopenharmony_ci .probe = tmio_probe, 5218c2ecf20Sopenharmony_ci .remove = tmio_remove, 5228c2ecf20Sopenharmony_ci .suspend = tmio_suspend, 5238c2ecf20Sopenharmony_ci .resume = tmio_resume, 5248c2ecf20Sopenharmony_ci}; 5258c2ecf20Sopenharmony_ci 5268c2ecf20Sopenharmony_cimodule_platform_driver(tmio_driver); 5278c2ecf20Sopenharmony_ci 5288c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL v2"); 5298c2ecf20Sopenharmony_ciMODULE_AUTHOR("Ian Molton, Dirk Opfer, Chris Humbert, Dmitry Baryshkov"); 5308c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("NAND flash driver on Toshiba Mobile IO controller"); 5318c2ecf20Sopenharmony_ciMODULE_ALIAS("platform:tmio-nand"); 532