| Boris BREZILLON | 1fef62c | 2014-10-21 15:08:41 +0200 | [diff] [blame] | 1 | /* | 
|  | 2 | * Copyright (C) 2013 Boris BREZILLON <b.brezillon.dev@gmail.com> | 
|  | 3 | * | 
|  | 4 | * Derived from: | 
|  | 5 | *	https://github.com/yuq/sunxi-nfc-mtd | 
|  | 6 | *	Copyright (C) 2013 Qiang Yu <yuq825@gmail.com> | 
|  | 7 | * | 
|  | 8 | *	https://github.com/hno/Allwinner-Info | 
|  | 9 | *	Copyright (C) 2013 Henrik Nordström <Henrik Nordström> | 
|  | 10 | * | 
|  | 11 | *	Copyright (C) 2013 Dmitriy B. <rzk333@gmail.com> | 
|  | 12 | *	Copyright (C) 2013 Sergey Lapin <slapin@ossfans.org> | 
|  | 13 | * | 
|  | 14 | * This program is free software; you can redistribute it and/or modify | 
|  | 15 | * it under the terms of the GNU General Public License as published by | 
|  | 16 | * the Free Software Foundation; either version 2 of the License, or | 
|  | 17 | * (at your option) any later version. | 
|  | 18 | * | 
|  | 19 | * This program is distributed in the hope that it will be useful, | 
|  | 20 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | 
|  | 21 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the | 
|  | 22 | * GNU General Public License for more details. | 
|  | 23 | */ | 
|  | 24 |  | 
|  | 25 | #include <linux/dma-mapping.h> | 
|  | 26 | #include <linux/slab.h> | 
|  | 27 | #include <linux/module.h> | 
|  | 28 | #include <linux/moduleparam.h> | 
|  | 29 | #include <linux/platform_device.h> | 
|  | 30 | #include <linux/of.h> | 
|  | 31 | #include <linux/of_device.h> | 
|  | 32 | #include <linux/of_gpio.h> | 
|  | 33 | #include <linux/of_mtd.h> | 
|  | 34 | #include <linux/mtd/mtd.h> | 
|  | 35 | #include <linux/mtd/nand.h> | 
|  | 36 | #include <linux/mtd/partitions.h> | 
|  | 37 | #include <linux/clk.h> | 
|  | 38 | #include <linux/delay.h> | 
|  | 39 | #include <linux/dmaengine.h> | 
|  | 40 | #include <linux/gpio.h> | 
|  | 41 | #include <linux/interrupt.h> | 
|  | 42 | #include <linux/io.h> | 
|  | 43 |  | 
|  | 44 | #define NFC_REG_CTL		0x0000 | 
|  | 45 | #define NFC_REG_ST		0x0004 | 
|  | 46 | #define NFC_REG_INT		0x0008 | 
|  | 47 | #define NFC_REG_TIMING_CTL	0x000C | 
|  | 48 | #define NFC_REG_TIMING_CFG	0x0010 | 
|  | 49 | #define NFC_REG_ADDR_LOW	0x0014 | 
|  | 50 | #define NFC_REG_ADDR_HIGH	0x0018 | 
|  | 51 | #define NFC_REG_SECTOR_NUM	0x001C | 
|  | 52 | #define NFC_REG_CNT		0x0020 | 
|  | 53 | #define NFC_REG_CMD		0x0024 | 
|  | 54 | #define NFC_REG_RCMD_SET	0x0028 | 
|  | 55 | #define NFC_REG_WCMD_SET	0x002C | 
|  | 56 | #define NFC_REG_IO_DATA		0x0030 | 
|  | 57 | #define NFC_REG_ECC_CTL		0x0034 | 
|  | 58 | #define NFC_REG_ECC_ST		0x0038 | 
|  | 59 | #define NFC_REG_DEBUG		0x003C | 
| Boris BREZILLON | b6a02c0 | 2015-09-16 09:46:36 +0200 | [diff] [blame] | 60 | #define NFC_REG_ECC_ERR_CNT(x)	((0x0040 + (x)) & ~0x3) | 
|  | 61 | #define NFC_REG_USER_DATA(x)	(0x0050 + ((x) * 4)) | 
| Boris BREZILLON | 1fef62c | 2014-10-21 15:08:41 +0200 | [diff] [blame] | 62 | #define NFC_REG_SPARE_AREA	0x00A0 | 
|  | 63 | #define NFC_RAM0_BASE		0x0400 | 
|  | 64 | #define NFC_RAM1_BASE		0x0800 | 
|  | 65 |  | 
|  | 66 | /* define bit use in NFC_CTL */ | 
|  | 67 | #define NFC_EN			BIT(0) | 
|  | 68 | #define NFC_RESET		BIT(1) | 
| Boris BREZILLON | b6a02c0 | 2015-09-16 09:46:36 +0200 | [diff] [blame] | 69 | #define NFC_BUS_WIDTH_MSK	BIT(2) | 
|  | 70 | #define NFC_BUS_WIDTH_8		(0 << 2) | 
|  | 71 | #define NFC_BUS_WIDTH_16	(1 << 2) | 
|  | 72 | #define NFC_RB_SEL_MSK		BIT(3) | 
|  | 73 | #define NFC_RB_SEL(x)		((x) << 3) | 
|  | 74 | #define NFC_CE_SEL_MSK		GENMASK(26, 24) | 
|  | 75 | #define NFC_CE_SEL(x)		((x) << 24) | 
| Boris BREZILLON | 1fef62c | 2014-10-21 15:08:41 +0200 | [diff] [blame] | 76 | #define NFC_CE_CTL		BIT(6) | 
| Boris BREZILLON | b6a02c0 | 2015-09-16 09:46:36 +0200 | [diff] [blame] | 77 | #define NFC_PAGE_SHIFT_MSK	GENMASK(11, 8) | 
|  | 78 | #define NFC_PAGE_SHIFT(x)	(((x) < 10 ? 0 : (x) - 10) << 8) | 
| Boris BREZILLON | 1fef62c | 2014-10-21 15:08:41 +0200 | [diff] [blame] | 79 | #define NFC_SAM			BIT(12) | 
|  | 80 | #define NFC_RAM_METHOD		BIT(14) | 
|  | 81 | #define NFC_DEBUG_CTL		BIT(31) | 
|  | 82 |  | 
|  | 83 | /* define bit use in NFC_ST */ | 
|  | 84 | #define NFC_RB_B2R		BIT(0) | 
|  | 85 | #define NFC_CMD_INT_FLAG	BIT(1) | 
|  | 86 | #define NFC_DMA_INT_FLAG	BIT(2) | 
|  | 87 | #define NFC_CMD_FIFO_STATUS	BIT(3) | 
|  | 88 | #define NFC_STA			BIT(4) | 
|  | 89 | #define NFC_NATCH_INT_FLAG	BIT(5) | 
| Boris BREZILLON | b6a02c0 | 2015-09-16 09:46:36 +0200 | [diff] [blame] | 90 | #define NFC_RB_STATE(x)		BIT(x + 8) | 
| Boris BREZILLON | 1fef62c | 2014-10-21 15:08:41 +0200 | [diff] [blame] | 91 |  | 
|  | 92 | /* define bit use in NFC_INT */ | 
|  | 93 | #define NFC_B2R_INT_ENABLE	BIT(0) | 
|  | 94 | #define NFC_CMD_INT_ENABLE	BIT(1) | 
|  | 95 | #define NFC_DMA_INT_ENABLE	BIT(2) | 
|  | 96 | #define NFC_INT_MASK		(NFC_B2R_INT_ENABLE | \ | 
|  | 97 | NFC_CMD_INT_ENABLE | \ | 
|  | 98 | NFC_DMA_INT_ENABLE) | 
|  | 99 |  | 
| Roy Spliet | d052e50 | 2015-06-26 11:00:11 +0200 | [diff] [blame] | 100 | /* define bit use in NFC_TIMING_CTL */ | 
|  | 101 | #define NFC_TIMING_CTL_EDO	BIT(8) | 
|  | 102 |  | 
| Roy Spliet | 9c61829 | 2015-06-26 11:00:10 +0200 | [diff] [blame] | 103 | /* define NFC_TIMING_CFG register layout */ | 
|  | 104 | #define NFC_TIMING_CFG(tWB, tADL, tWHR, tRHW, tCAD)		\ | 
|  | 105 | (((tWB) & 0x3) | (((tADL) & 0x3) << 2) |		\ | 
|  | 106 | (((tWHR) & 0x3) << 4) | (((tRHW) & 0x3) << 6) |		\ | 
|  | 107 | (((tCAD) & 0x7) << 8)) | 
|  | 108 |  | 
| Boris BREZILLON | 1fef62c | 2014-10-21 15:08:41 +0200 | [diff] [blame] | 109 | /* define bit use in NFC_CMD */ | 
| Boris BREZILLON | b6a02c0 | 2015-09-16 09:46:36 +0200 | [diff] [blame] | 110 | #define NFC_CMD_LOW_BYTE_MSK	GENMASK(7, 0) | 
|  | 111 | #define NFC_CMD_HIGH_BYTE_MSK	GENMASK(15, 8) | 
|  | 112 | #define NFC_CMD(x)		(x) | 
|  | 113 | #define NFC_ADR_NUM_MSK		GENMASK(18, 16) | 
|  | 114 | #define NFC_ADR_NUM(x)		(((x) - 1) << 16) | 
| Boris BREZILLON | 1fef62c | 2014-10-21 15:08:41 +0200 | [diff] [blame] | 115 | #define NFC_SEND_ADR		BIT(19) | 
|  | 116 | #define NFC_ACCESS_DIR		BIT(20) | 
|  | 117 | #define NFC_DATA_TRANS		BIT(21) | 
|  | 118 | #define NFC_SEND_CMD1		BIT(22) | 
|  | 119 | #define NFC_WAIT_FLAG		BIT(23) | 
|  | 120 | #define NFC_SEND_CMD2		BIT(24) | 
|  | 121 | #define NFC_SEQ			BIT(25) | 
|  | 122 | #define NFC_DATA_SWAP_METHOD	BIT(26) | 
|  | 123 | #define NFC_ROW_AUTO_INC	BIT(27) | 
|  | 124 | #define NFC_SEND_CMD3		BIT(28) | 
|  | 125 | #define NFC_SEND_CMD4		BIT(29) | 
| Boris BREZILLON | b6a02c0 | 2015-09-16 09:46:36 +0200 | [diff] [blame] | 126 | #define NFC_CMD_TYPE_MSK	GENMASK(31, 30) | 
|  | 127 | #define NFC_NORMAL_OP		(0 << 30) | 
|  | 128 | #define NFC_ECC_OP		(1 << 30) | 
|  | 129 | #define NFC_PAGE_OP		(2 << 30) | 
| Boris BREZILLON | 1fef62c | 2014-10-21 15:08:41 +0200 | [diff] [blame] | 130 |  | 
|  | 131 | /* define bit use in NFC_RCMD_SET */ | 
| Boris BREZILLON | b6a02c0 | 2015-09-16 09:46:36 +0200 | [diff] [blame] | 132 | #define NFC_READ_CMD_MSK	GENMASK(7, 0) | 
|  | 133 | #define NFC_RND_READ_CMD0_MSK	GENMASK(15, 8) | 
|  | 134 | #define NFC_RND_READ_CMD1_MSK	GENMASK(23, 16) | 
| Boris BREZILLON | 1fef62c | 2014-10-21 15:08:41 +0200 | [diff] [blame] | 135 |  | 
|  | 136 | /* define bit use in NFC_WCMD_SET */ | 
| Boris BREZILLON | b6a02c0 | 2015-09-16 09:46:36 +0200 | [diff] [blame] | 137 | #define NFC_PROGRAM_CMD_MSK	GENMASK(7, 0) | 
|  | 138 | #define NFC_RND_WRITE_CMD_MSK	GENMASK(15, 8) | 
|  | 139 | #define NFC_READ_CMD0_MSK	GENMASK(23, 16) | 
|  | 140 | #define NFC_READ_CMD1_MSK	GENMASK(31, 24) | 
| Boris BREZILLON | 1fef62c | 2014-10-21 15:08:41 +0200 | [diff] [blame] | 141 |  | 
|  | 142 | /* define bit use in NFC_ECC_CTL */ | 
|  | 143 | #define NFC_ECC_EN		BIT(0) | 
|  | 144 | #define NFC_ECC_PIPELINE	BIT(3) | 
|  | 145 | #define NFC_ECC_EXCEPTION	BIT(4) | 
| Boris BREZILLON | b6a02c0 | 2015-09-16 09:46:36 +0200 | [diff] [blame] | 146 | #define NFC_ECC_BLOCK_SIZE_MSK	BIT(5) | 
| Boris BREZILLON | 1fef62c | 2014-10-21 15:08:41 +0200 | [diff] [blame] | 147 | #define NFC_RANDOM_EN		BIT(9) | 
|  | 148 | #define NFC_RANDOM_DIRECTION	BIT(10) | 
| Boris BREZILLON | b6a02c0 | 2015-09-16 09:46:36 +0200 | [diff] [blame] | 149 | #define NFC_ECC_MODE_MSK	GENMASK(15, 12) | 
|  | 150 | #define NFC_ECC_MODE(x)		((x) << 12) | 
|  | 151 | #define NFC_RANDOM_SEED_MSK	GENMASK(30, 16) | 
|  | 152 | #define NFC_RANDOM_SEED(x)	((x) << 16) | 
|  | 153 |  | 
|  | 154 | /* define bit use in NFC_ECC_ST */ | 
|  | 155 | #define NFC_ECC_ERR(x)		BIT(x) | 
|  | 156 | #define NFC_ECC_PAT_FOUND(x)	BIT(x + 16) | 
|  | 157 | #define NFC_ECC_ERR_CNT(b, x)	(((x) >> ((b) * 8)) & 0xff) | 
| Boris BREZILLON | 1fef62c | 2014-10-21 15:08:41 +0200 | [diff] [blame] | 158 |  | 
| Boris BREZILLON | 03a0e8a | 2015-09-14 10:41:03 +0200 | [diff] [blame] | 159 | /* NFC_USER_DATA helper macros */ | 
|  | 160 | #define NFC_BUF_TO_USER_DATA(buf)	((buf)[0] | ((buf)[1] << 8) | \ | 
|  | 161 | ((buf)[2] << 16) | ((buf)[3] << 24)) | 
|  | 162 |  | 
| Boris BREZILLON | 1fef62c | 2014-10-21 15:08:41 +0200 | [diff] [blame] | 163 | #define NFC_DEFAULT_TIMEOUT_MS	1000 | 
|  | 164 |  | 
|  | 165 | #define NFC_SRAM_SIZE		1024 | 
|  | 166 |  | 
|  | 167 | #define NFC_MAX_CS		7 | 
|  | 168 |  | 
|  | 169 | /* | 
|  | 170 | * Ready/Busy detection type: describes the Ready/Busy detection modes | 
|  | 171 | * | 
|  | 172 | * @RB_NONE:	no external detection available, rely on STATUS command | 
|  | 173 | *		and software timeouts | 
|  | 174 | * @RB_NATIVE:	use sunxi NAND controller Ready/Busy support. The Ready/Busy | 
|  | 175 | *		pin of the NAND flash chip must be connected to one of the | 
|  | 176 | *		native NAND R/B pins (those which can be muxed to the NAND | 
|  | 177 | *		Controller) | 
|  | 178 | * @RB_GPIO:	use a simple GPIO to handle Ready/Busy status. The Ready/Busy | 
|  | 179 | *		pin of the NAND flash chip must be connected to a GPIO capable | 
|  | 180 | *		pin. | 
|  | 181 | */ | 
|  | 182 | enum sunxi_nand_rb_type { | 
|  | 183 | RB_NONE, | 
|  | 184 | RB_NATIVE, | 
|  | 185 | RB_GPIO, | 
|  | 186 | }; | 
|  | 187 |  | 
|  | 188 | /* | 
|  | 189 | * Ready/Busy structure: stores information related to Ready/Busy detection | 
|  | 190 | * | 
|  | 191 | * @type:	the Ready/Busy detection mode | 
|  | 192 | * @info:	information related to the R/B detection mode. Either a gpio | 
|  | 193 | *		id or a native R/B id (those supported by the NAND controller). | 
|  | 194 | */ | 
|  | 195 | struct sunxi_nand_rb { | 
|  | 196 | enum sunxi_nand_rb_type type; | 
|  | 197 | union { | 
|  | 198 | int gpio; | 
|  | 199 | int nativeid; | 
|  | 200 | } info; | 
|  | 201 | }; | 
|  | 202 |  | 
|  | 203 | /* | 
|  | 204 | * Chip Select structure: stores information related to NAND Chip Select | 
|  | 205 | * | 
|  | 206 | * @cs:		the NAND CS id used to communicate with a NAND Chip | 
|  | 207 | * @rb:		the Ready/Busy description | 
|  | 208 | */ | 
|  | 209 | struct sunxi_nand_chip_sel { | 
|  | 210 | u8 cs; | 
|  | 211 | struct sunxi_nand_rb rb; | 
|  | 212 | }; | 
|  | 213 |  | 
|  | 214 | /* | 
|  | 215 | * sunxi HW ECC infos: stores information related to HW ECC support | 
|  | 216 | * | 
|  | 217 | * @mode:	the sunxi ECC mode field deduced from ECC requirements | 
|  | 218 | * @layout:	the OOB layout depending on the ECC requirements and the | 
|  | 219 | *		selected ECC mode | 
|  | 220 | */ | 
|  | 221 | struct sunxi_nand_hw_ecc { | 
|  | 222 | int mode; | 
|  | 223 | struct nand_ecclayout layout; | 
|  | 224 | }; | 
|  | 225 |  | 
|  | 226 | /* | 
|  | 227 | * NAND chip structure: stores NAND chip device related information | 
|  | 228 | * | 
|  | 229 | * @node:		used to store NAND chips into a list | 
|  | 230 | * @nand:		base NAND chip structure | 
|  | 231 | * @mtd:		base MTD structure | 
|  | 232 | * @clk_rate:		clk_rate required for this NAND chip | 
| Roy Spliet | 9c61829 | 2015-06-26 11:00:10 +0200 | [diff] [blame] | 233 | * @timing_cfg		TIMING_CFG register value for this NAND chip | 
| Boris BREZILLON | 1fef62c | 2014-10-21 15:08:41 +0200 | [diff] [blame] | 234 | * @selected:		current active CS | 
|  | 235 | * @nsels:		number of CS lines required by the NAND chip | 
|  | 236 | * @sels:		array of CS lines descriptions | 
|  | 237 | */ | 
|  | 238 | struct sunxi_nand_chip { | 
|  | 239 | struct list_head node; | 
|  | 240 | struct nand_chip nand; | 
|  | 241 | struct mtd_info mtd; | 
|  | 242 | unsigned long clk_rate; | 
| Roy Spliet | 9c61829 | 2015-06-26 11:00:10 +0200 | [diff] [blame] | 243 | u32 timing_cfg; | 
| Roy Spliet | d052e50 | 2015-06-26 11:00:11 +0200 | [diff] [blame] | 244 | u32 timing_ctl; | 
| Boris BREZILLON | 1fef62c | 2014-10-21 15:08:41 +0200 | [diff] [blame] | 245 | int selected; | 
|  | 246 | int nsels; | 
|  | 247 | struct sunxi_nand_chip_sel sels[0]; | 
|  | 248 | }; | 
|  | 249 |  | 
|  | 250 | static inline struct sunxi_nand_chip *to_sunxi_nand(struct nand_chip *nand) | 
|  | 251 | { | 
|  | 252 | return container_of(nand, struct sunxi_nand_chip, nand); | 
|  | 253 | } | 
|  | 254 |  | 
|  | 255 | /* | 
|  | 256 | * NAND Controller structure: stores sunxi NAND controller information | 
|  | 257 | * | 
|  | 258 | * @controller:		base controller structure | 
|  | 259 | * @dev:		parent device (used to print error messages) | 
|  | 260 | * @regs:		NAND controller registers | 
|  | 261 | * @ahb_clk:		NAND Controller AHB clock | 
|  | 262 | * @mod_clk:		NAND Controller mod clock | 
|  | 263 | * @assigned_cs:	bitmask describing already assigned CS lines | 
|  | 264 | * @clk_rate:		NAND controller current clock rate | 
|  | 265 | * @chips:		a list containing all the NAND chips attached to | 
|  | 266 | *			this NAND controller | 
|  | 267 | * @complete:		a completion object used to wait for NAND | 
|  | 268 | *			controller events | 
|  | 269 | */ | 
|  | 270 | struct sunxi_nfc { | 
|  | 271 | struct nand_hw_control controller; | 
|  | 272 | struct device *dev; | 
|  | 273 | void __iomem *regs; | 
|  | 274 | struct clk *ahb_clk; | 
|  | 275 | struct clk *mod_clk; | 
|  | 276 | unsigned long assigned_cs; | 
|  | 277 | unsigned long clk_rate; | 
|  | 278 | struct list_head chips; | 
|  | 279 | struct completion complete; | 
|  | 280 | }; | 
|  | 281 |  | 
|  | 282 | static inline struct sunxi_nfc *to_sunxi_nfc(struct nand_hw_control *ctrl) | 
|  | 283 | { | 
|  | 284 | return container_of(ctrl, struct sunxi_nfc, controller); | 
|  | 285 | } | 
|  | 286 |  | 
|  | 287 | static irqreturn_t sunxi_nfc_interrupt(int irq, void *dev_id) | 
|  | 288 | { | 
|  | 289 | struct sunxi_nfc *nfc = dev_id; | 
|  | 290 | u32 st = readl(nfc->regs + NFC_REG_ST); | 
|  | 291 | u32 ien = readl(nfc->regs + NFC_REG_INT); | 
|  | 292 |  | 
|  | 293 | if (!(ien & st)) | 
|  | 294 | return IRQ_NONE; | 
|  | 295 |  | 
|  | 296 | if ((ien & st) == ien) | 
|  | 297 | complete(&nfc->complete); | 
|  | 298 |  | 
|  | 299 | writel(st & NFC_INT_MASK, nfc->regs + NFC_REG_ST); | 
|  | 300 | writel(~st & ien & NFC_INT_MASK, nfc->regs + NFC_REG_INT); | 
|  | 301 |  | 
|  | 302 | return IRQ_HANDLED; | 
|  | 303 | } | 
|  | 304 |  | 
|  | 305 | static int sunxi_nfc_wait_int(struct sunxi_nfc *nfc, u32 flags, | 
|  | 306 | unsigned int timeout_ms) | 
|  | 307 | { | 
|  | 308 | init_completion(&nfc->complete); | 
|  | 309 |  | 
|  | 310 | writel(flags, nfc->regs + NFC_REG_INT); | 
|  | 311 |  | 
|  | 312 | if (!timeout_ms) | 
|  | 313 | timeout_ms = NFC_DEFAULT_TIMEOUT_MS; | 
|  | 314 |  | 
|  | 315 | if (!wait_for_completion_timeout(&nfc->complete, | 
|  | 316 | msecs_to_jiffies(timeout_ms))) { | 
|  | 317 | dev_err(nfc->dev, "wait interrupt timedout\n"); | 
|  | 318 | return -ETIMEDOUT; | 
|  | 319 | } | 
|  | 320 |  | 
|  | 321 | return 0; | 
|  | 322 | } | 
|  | 323 |  | 
|  | 324 | static int sunxi_nfc_wait_cmd_fifo_empty(struct sunxi_nfc *nfc) | 
|  | 325 | { | 
|  | 326 | unsigned long timeout = jiffies + | 
|  | 327 | msecs_to_jiffies(NFC_DEFAULT_TIMEOUT_MS); | 
|  | 328 |  | 
|  | 329 | do { | 
|  | 330 | if (!(readl(nfc->regs + NFC_REG_ST) & NFC_CMD_FIFO_STATUS)) | 
|  | 331 | return 0; | 
|  | 332 | } while (time_before(jiffies, timeout)); | 
|  | 333 |  | 
|  | 334 | dev_err(nfc->dev, "wait for empty cmd FIFO timedout\n"); | 
|  | 335 | return -ETIMEDOUT; | 
|  | 336 | } | 
|  | 337 |  | 
|  | 338 | static int sunxi_nfc_rst(struct sunxi_nfc *nfc) | 
|  | 339 | { | 
|  | 340 | unsigned long timeout = jiffies + | 
|  | 341 | msecs_to_jiffies(NFC_DEFAULT_TIMEOUT_MS); | 
|  | 342 |  | 
|  | 343 | writel(0, nfc->regs + NFC_REG_ECC_CTL); | 
|  | 344 | writel(NFC_RESET, nfc->regs + NFC_REG_CTL); | 
|  | 345 |  | 
|  | 346 | do { | 
|  | 347 | if (!(readl(nfc->regs + NFC_REG_CTL) & NFC_RESET)) | 
|  | 348 | return 0; | 
|  | 349 | } while (time_before(jiffies, timeout)); | 
|  | 350 |  | 
|  | 351 | dev_err(nfc->dev, "wait for NAND controller reset timedout\n"); | 
|  | 352 | return -ETIMEDOUT; | 
|  | 353 | } | 
|  | 354 |  | 
|  | 355 | static int sunxi_nfc_dev_ready(struct mtd_info *mtd) | 
|  | 356 | { | 
|  | 357 | struct nand_chip *nand = mtd->priv; | 
|  | 358 | struct sunxi_nand_chip *sunxi_nand = to_sunxi_nand(nand); | 
|  | 359 | struct sunxi_nfc *nfc = to_sunxi_nfc(sunxi_nand->nand.controller); | 
|  | 360 | struct sunxi_nand_rb *rb; | 
|  | 361 | unsigned long timeo = (sunxi_nand->nand.state == FL_ERASING ? 400 : 20); | 
|  | 362 | int ret; | 
|  | 363 |  | 
|  | 364 | if (sunxi_nand->selected < 0) | 
|  | 365 | return 0; | 
|  | 366 |  | 
|  | 367 | rb = &sunxi_nand->sels[sunxi_nand->selected].rb; | 
|  | 368 |  | 
|  | 369 | switch (rb->type) { | 
|  | 370 | case RB_NATIVE: | 
|  | 371 | ret = !!(readl(nfc->regs + NFC_REG_ST) & | 
| Boris BREZILLON | b6a02c0 | 2015-09-16 09:46:36 +0200 | [diff] [blame] | 372 | NFC_RB_STATE(rb->info.nativeid)); | 
| Boris BREZILLON | 1fef62c | 2014-10-21 15:08:41 +0200 | [diff] [blame] | 373 | if (ret) | 
|  | 374 | break; | 
|  | 375 |  | 
|  | 376 | sunxi_nfc_wait_int(nfc, NFC_RB_B2R, timeo); | 
|  | 377 | ret = !!(readl(nfc->regs + NFC_REG_ST) & | 
| Boris BREZILLON | b6a02c0 | 2015-09-16 09:46:36 +0200 | [diff] [blame] | 378 | NFC_RB_STATE(rb->info.nativeid)); | 
| Boris BREZILLON | 1fef62c | 2014-10-21 15:08:41 +0200 | [diff] [blame] | 379 | break; | 
|  | 380 | case RB_GPIO: | 
|  | 381 | ret = gpio_get_value(rb->info.gpio); | 
|  | 382 | break; | 
|  | 383 | case RB_NONE: | 
|  | 384 | default: | 
|  | 385 | ret = 0; | 
|  | 386 | dev_err(nfc->dev, "cannot check R/B NAND status!\n"); | 
|  | 387 | break; | 
|  | 388 | } | 
|  | 389 |  | 
|  | 390 | return ret; | 
|  | 391 | } | 
|  | 392 |  | 
|  | 393 | static void sunxi_nfc_select_chip(struct mtd_info *mtd, int chip) | 
|  | 394 | { | 
|  | 395 | struct nand_chip *nand = mtd->priv; | 
|  | 396 | struct sunxi_nand_chip *sunxi_nand = to_sunxi_nand(nand); | 
|  | 397 | struct sunxi_nfc *nfc = to_sunxi_nfc(sunxi_nand->nand.controller); | 
|  | 398 | struct sunxi_nand_chip_sel *sel; | 
|  | 399 | u32 ctl; | 
|  | 400 |  | 
|  | 401 | if (chip > 0 && chip >= sunxi_nand->nsels) | 
|  | 402 | return; | 
|  | 403 |  | 
|  | 404 | if (chip == sunxi_nand->selected) | 
|  | 405 | return; | 
|  | 406 |  | 
|  | 407 | ctl = readl(nfc->regs + NFC_REG_CTL) & | 
| Boris BREZILLON | b6a02c0 | 2015-09-16 09:46:36 +0200 | [diff] [blame] | 408 | ~(NFC_PAGE_SHIFT_MSK | NFC_CE_SEL_MSK | NFC_RB_SEL_MSK | NFC_EN); | 
| Boris BREZILLON | 1fef62c | 2014-10-21 15:08:41 +0200 | [diff] [blame] | 409 |  | 
|  | 410 | if (chip >= 0) { | 
|  | 411 | sel = &sunxi_nand->sels[chip]; | 
|  | 412 |  | 
| Boris BREZILLON | b6a02c0 | 2015-09-16 09:46:36 +0200 | [diff] [blame] | 413 | ctl |= NFC_CE_SEL(sel->cs) | NFC_EN | | 
|  | 414 | NFC_PAGE_SHIFT(nand->page_shift - 10); | 
| Boris BREZILLON | 1fef62c | 2014-10-21 15:08:41 +0200 | [diff] [blame] | 415 | if (sel->rb.type == RB_NONE) { | 
|  | 416 | nand->dev_ready = NULL; | 
|  | 417 | } else { | 
|  | 418 | nand->dev_ready = sunxi_nfc_dev_ready; | 
|  | 419 | if (sel->rb.type == RB_NATIVE) | 
| Boris BREZILLON | b6a02c0 | 2015-09-16 09:46:36 +0200 | [diff] [blame] | 420 | ctl |= NFC_RB_SEL(sel->rb.info.nativeid); | 
| Boris BREZILLON | 1fef62c | 2014-10-21 15:08:41 +0200 | [diff] [blame] | 421 | } | 
|  | 422 |  | 
|  | 423 | writel(mtd->writesize, nfc->regs + NFC_REG_SPARE_AREA); | 
|  | 424 |  | 
|  | 425 | if (nfc->clk_rate != sunxi_nand->clk_rate) { | 
|  | 426 | clk_set_rate(nfc->mod_clk, sunxi_nand->clk_rate); | 
|  | 427 | nfc->clk_rate = sunxi_nand->clk_rate; | 
|  | 428 | } | 
|  | 429 | } | 
|  | 430 |  | 
| Roy Spliet | d052e50 | 2015-06-26 11:00:11 +0200 | [diff] [blame] | 431 | writel(sunxi_nand->timing_ctl, nfc->regs + NFC_REG_TIMING_CTL); | 
| Roy Spliet | 9c61829 | 2015-06-26 11:00:10 +0200 | [diff] [blame] | 432 | writel(sunxi_nand->timing_cfg, nfc->regs + NFC_REG_TIMING_CFG); | 
| Boris BREZILLON | 1fef62c | 2014-10-21 15:08:41 +0200 | [diff] [blame] | 433 | writel(ctl, nfc->regs + NFC_REG_CTL); | 
|  | 434 |  | 
|  | 435 | sunxi_nand->selected = chip; | 
|  | 436 | } | 
|  | 437 |  | 
|  | 438 | static void sunxi_nfc_read_buf(struct mtd_info *mtd, uint8_t *buf, int len) | 
|  | 439 | { | 
|  | 440 | struct nand_chip *nand = mtd->priv; | 
|  | 441 | struct sunxi_nand_chip *sunxi_nand = to_sunxi_nand(nand); | 
|  | 442 | struct sunxi_nfc *nfc = to_sunxi_nfc(sunxi_nand->nand.controller); | 
|  | 443 | int ret; | 
|  | 444 | int cnt; | 
|  | 445 | int offs = 0; | 
|  | 446 | u32 tmp; | 
|  | 447 |  | 
|  | 448 | while (len > offs) { | 
|  | 449 | cnt = min(len - offs, NFC_SRAM_SIZE); | 
|  | 450 |  | 
|  | 451 | ret = sunxi_nfc_wait_cmd_fifo_empty(nfc); | 
|  | 452 | if (ret) | 
|  | 453 | break; | 
|  | 454 |  | 
|  | 455 | writel(cnt, nfc->regs + NFC_REG_CNT); | 
|  | 456 | tmp = NFC_DATA_TRANS | NFC_DATA_SWAP_METHOD; | 
|  | 457 | writel(tmp, nfc->regs + NFC_REG_CMD); | 
|  | 458 |  | 
|  | 459 | ret = sunxi_nfc_wait_int(nfc, NFC_CMD_INT_FLAG, 0); | 
|  | 460 | if (ret) | 
|  | 461 | break; | 
|  | 462 |  | 
|  | 463 | if (buf) | 
|  | 464 | memcpy_fromio(buf + offs, nfc->regs + NFC_RAM0_BASE, | 
|  | 465 | cnt); | 
|  | 466 | offs += cnt; | 
|  | 467 | } | 
|  | 468 | } | 
|  | 469 |  | 
|  | 470 | static void sunxi_nfc_write_buf(struct mtd_info *mtd, const uint8_t *buf, | 
|  | 471 | int len) | 
|  | 472 | { | 
|  | 473 | struct nand_chip *nand = mtd->priv; | 
|  | 474 | struct sunxi_nand_chip *sunxi_nand = to_sunxi_nand(nand); | 
|  | 475 | struct sunxi_nfc *nfc = to_sunxi_nfc(sunxi_nand->nand.controller); | 
|  | 476 | int ret; | 
|  | 477 | int cnt; | 
|  | 478 | int offs = 0; | 
|  | 479 | u32 tmp; | 
|  | 480 |  | 
|  | 481 | while (len > offs) { | 
|  | 482 | cnt = min(len - offs, NFC_SRAM_SIZE); | 
|  | 483 |  | 
|  | 484 | ret = sunxi_nfc_wait_cmd_fifo_empty(nfc); | 
|  | 485 | if (ret) | 
|  | 486 | break; | 
|  | 487 |  | 
|  | 488 | writel(cnt, nfc->regs + NFC_REG_CNT); | 
|  | 489 | memcpy_toio(nfc->regs + NFC_RAM0_BASE, buf + offs, cnt); | 
|  | 490 | tmp = NFC_DATA_TRANS | NFC_DATA_SWAP_METHOD | | 
|  | 491 | NFC_ACCESS_DIR; | 
|  | 492 | writel(tmp, nfc->regs + NFC_REG_CMD); | 
|  | 493 |  | 
|  | 494 | ret = sunxi_nfc_wait_int(nfc, NFC_CMD_INT_FLAG, 0); | 
|  | 495 | if (ret) | 
|  | 496 | break; | 
|  | 497 |  | 
|  | 498 | offs += cnt; | 
|  | 499 | } | 
|  | 500 | } | 
|  | 501 |  | 
|  | 502 | static uint8_t sunxi_nfc_read_byte(struct mtd_info *mtd) | 
|  | 503 | { | 
|  | 504 | uint8_t ret; | 
|  | 505 |  | 
|  | 506 | sunxi_nfc_read_buf(mtd, &ret, 1); | 
|  | 507 |  | 
|  | 508 | return ret; | 
|  | 509 | } | 
|  | 510 |  | 
|  | 511 | static void sunxi_nfc_cmd_ctrl(struct mtd_info *mtd, int dat, | 
|  | 512 | unsigned int ctrl) | 
|  | 513 | { | 
|  | 514 | struct nand_chip *nand = mtd->priv; | 
|  | 515 | struct sunxi_nand_chip *sunxi_nand = to_sunxi_nand(nand); | 
|  | 516 | struct sunxi_nfc *nfc = to_sunxi_nfc(sunxi_nand->nand.controller); | 
|  | 517 | int ret; | 
|  | 518 | u32 tmp; | 
|  | 519 |  | 
|  | 520 | ret = sunxi_nfc_wait_cmd_fifo_empty(nfc); | 
|  | 521 | if (ret) | 
|  | 522 | return; | 
|  | 523 |  | 
|  | 524 | if (ctrl & NAND_CTRL_CHANGE) { | 
|  | 525 | tmp = readl(nfc->regs + NFC_REG_CTL); | 
|  | 526 | if (ctrl & NAND_NCE) | 
|  | 527 | tmp |= NFC_CE_CTL; | 
|  | 528 | else | 
|  | 529 | tmp &= ~NFC_CE_CTL; | 
|  | 530 | writel(tmp, nfc->regs + NFC_REG_CTL); | 
|  | 531 | } | 
|  | 532 |  | 
|  | 533 | if (dat == NAND_CMD_NONE) | 
|  | 534 | return; | 
|  | 535 |  | 
|  | 536 | if (ctrl & NAND_CLE) { | 
|  | 537 | writel(NFC_SEND_CMD1 | dat, nfc->regs + NFC_REG_CMD); | 
|  | 538 | } else { | 
|  | 539 | writel(dat, nfc->regs + NFC_REG_ADDR_LOW); | 
|  | 540 | writel(NFC_SEND_ADR, nfc->regs + NFC_REG_CMD); | 
|  | 541 | } | 
|  | 542 |  | 
|  | 543 | sunxi_nfc_wait_int(nfc, NFC_CMD_INT_FLAG, 0); | 
|  | 544 | } | 
|  | 545 |  | 
| Boris BREZILLON | c9118ec | 2015-09-30 23:45:23 +0200 | [diff] [blame^] | 546 | static void sunxi_nfc_hw_ecc_enable(struct mtd_info *mtd) | 
|  | 547 | { | 
|  | 548 | struct nand_chip *nand = mtd->priv; | 
|  | 549 | struct sunxi_nfc *nfc = to_sunxi_nfc(nand->controller); | 
|  | 550 | struct sunxi_nand_hw_ecc *data = nand->ecc.priv; | 
|  | 551 | u32 ecc_ctl; | 
|  | 552 |  | 
|  | 553 | ecc_ctl = readl(nfc->regs + NFC_REG_ECC_CTL); | 
|  | 554 | ecc_ctl &= ~(NFC_ECC_MODE_MSK | NFC_ECC_PIPELINE | | 
|  | 555 | NFC_ECC_BLOCK_SIZE_MSK); | 
|  | 556 | ecc_ctl |= NFC_ECC_EN | NFC_ECC_MODE(data->mode) | NFC_ECC_EXCEPTION; | 
|  | 557 |  | 
|  | 558 | writel(ecc_ctl, nfc->regs + NFC_REG_ECC_CTL); | 
|  | 559 | } | 
|  | 560 |  | 
|  | 561 | static void sunxi_nfc_hw_ecc_disable(struct mtd_info *mtd) | 
|  | 562 | { | 
|  | 563 | struct nand_chip *nand = mtd->priv; | 
|  | 564 | struct sunxi_nfc *nfc = to_sunxi_nfc(nand->controller); | 
|  | 565 |  | 
|  | 566 | writel(readl(nfc->regs + NFC_REG_ECC_CTL) & ~NFC_ECC_EN, | 
|  | 567 | nfc->regs + NFC_REG_ECC_CTL); | 
|  | 568 | } | 
|  | 569 |  | 
| Boris BREZILLON | 1fef62c | 2014-10-21 15:08:41 +0200 | [diff] [blame] | 570 | static int sunxi_nfc_hw_ecc_read_page(struct mtd_info *mtd, | 
|  | 571 | struct nand_chip *chip, uint8_t *buf, | 
|  | 572 | int oob_required, int page) | 
|  | 573 | { | 
|  | 574 | struct sunxi_nfc *nfc = to_sunxi_nfc(chip->controller); | 
|  | 575 | struct nand_ecc_ctrl *ecc = &chip->ecc; | 
|  | 576 | struct nand_ecclayout *layout = ecc->layout; | 
| Boris BREZILLON | 1fef62c | 2014-10-21 15:08:41 +0200 | [diff] [blame] | 577 | unsigned int max_bitflips = 0; | 
|  | 578 | int offset; | 
|  | 579 | int ret; | 
|  | 580 | u32 tmp; | 
|  | 581 | int i; | 
|  | 582 | int cnt; | 
|  | 583 |  | 
| Boris BREZILLON | c9118ec | 2015-09-30 23:45:23 +0200 | [diff] [blame^] | 584 | sunxi_nfc_hw_ecc_enable(mtd); | 
| Boris BREZILLON | 1fef62c | 2014-10-21 15:08:41 +0200 | [diff] [blame] | 585 |  | 
|  | 586 | for (i = 0; i < ecc->steps; i++) { | 
|  | 587 | if (i) | 
|  | 588 | chip->cmdfunc(mtd, NAND_CMD_RNDOUT, i * ecc->size, -1); | 
|  | 589 |  | 
|  | 590 | offset = mtd->writesize + layout->eccpos[i * ecc->bytes] - 4; | 
|  | 591 |  | 
|  | 592 | chip->read_buf(mtd, NULL, ecc->size); | 
|  | 593 |  | 
|  | 594 | chip->cmdfunc(mtd, NAND_CMD_RNDOUT, offset, -1); | 
|  | 595 |  | 
|  | 596 | ret = sunxi_nfc_wait_cmd_fifo_empty(nfc); | 
|  | 597 | if (ret) | 
|  | 598 | return ret; | 
|  | 599 |  | 
| Boris BREZILLON | b6a02c0 | 2015-09-16 09:46:36 +0200 | [diff] [blame] | 600 | tmp = NFC_DATA_TRANS | NFC_DATA_SWAP_METHOD | NFC_ECC_OP; | 
| Boris BREZILLON | 1fef62c | 2014-10-21 15:08:41 +0200 | [diff] [blame] | 601 | writel(tmp, nfc->regs + NFC_REG_CMD); | 
|  | 602 |  | 
|  | 603 | ret = sunxi_nfc_wait_int(nfc, NFC_CMD_INT_FLAG, 0); | 
|  | 604 | if (ret) | 
|  | 605 | return ret; | 
|  | 606 |  | 
|  | 607 | memcpy_fromio(buf + (i * ecc->size), | 
|  | 608 | nfc->regs + NFC_RAM0_BASE, ecc->size); | 
|  | 609 |  | 
| Boris BREZILLON | b6a02c0 | 2015-09-16 09:46:36 +0200 | [diff] [blame] | 610 | if (readl(nfc->regs + NFC_REG_ECC_ST) & NFC_ECC_ERR(0)) { | 
| Boris BREZILLON | 1fef62c | 2014-10-21 15:08:41 +0200 | [diff] [blame] | 611 | mtd->ecc_stats.failed++; | 
|  | 612 | } else { | 
| Boris BREZILLON | b6a02c0 | 2015-09-16 09:46:36 +0200 | [diff] [blame] | 613 | tmp = readl(nfc->regs + NFC_REG_ECC_ERR_CNT(0)); | 
|  | 614 | mtd->ecc_stats.corrected += NFC_ECC_ERR_CNT(0, tmp); | 
| Boris BREZILLON | 1fef62c | 2014-10-21 15:08:41 +0200 | [diff] [blame] | 615 | max_bitflips = max_t(unsigned int, max_bitflips, tmp); | 
|  | 616 | } | 
|  | 617 |  | 
|  | 618 | if (oob_required) { | 
|  | 619 | chip->cmdfunc(mtd, NAND_CMD_RNDOUT, offset, -1); | 
|  | 620 |  | 
|  | 621 | ret = sunxi_nfc_wait_cmd_fifo_empty(nfc); | 
|  | 622 | if (ret) | 
|  | 623 | return ret; | 
|  | 624 |  | 
|  | 625 | offset -= mtd->writesize; | 
|  | 626 | chip->read_buf(mtd, chip->oob_poi + offset, | 
|  | 627 | ecc->bytes + 4); | 
|  | 628 | } | 
|  | 629 | } | 
|  | 630 |  | 
|  | 631 | if (oob_required) { | 
|  | 632 | cnt = ecc->layout->oobfree[ecc->steps].length; | 
|  | 633 | if (cnt > 0) { | 
|  | 634 | offset = mtd->writesize + | 
|  | 635 | ecc->layout->oobfree[ecc->steps].offset; | 
|  | 636 | chip->cmdfunc(mtd, NAND_CMD_RNDOUT, offset, -1); | 
|  | 637 | offset -= mtd->writesize; | 
|  | 638 | chip->read_buf(mtd, chip->oob_poi + offset, cnt); | 
|  | 639 | } | 
|  | 640 | } | 
|  | 641 |  | 
| Boris BREZILLON | c9118ec | 2015-09-30 23:45:23 +0200 | [diff] [blame^] | 642 | sunxi_nfc_hw_ecc_disable(mtd); | 
| Boris BREZILLON | 1fef62c | 2014-10-21 15:08:41 +0200 | [diff] [blame] | 643 |  | 
|  | 644 | return max_bitflips; | 
|  | 645 | } | 
|  | 646 |  | 
|  | 647 | static int sunxi_nfc_hw_ecc_write_page(struct mtd_info *mtd, | 
|  | 648 | struct nand_chip *chip, | 
|  | 649 | const uint8_t *buf, int oob_required) | 
|  | 650 | { | 
|  | 651 | struct sunxi_nfc *nfc = to_sunxi_nfc(chip->controller); | 
|  | 652 | struct nand_ecc_ctrl *ecc = &chip->ecc; | 
|  | 653 | struct nand_ecclayout *layout = ecc->layout; | 
| Boris BREZILLON | 1fef62c | 2014-10-21 15:08:41 +0200 | [diff] [blame] | 654 | int offset; | 
|  | 655 | int ret; | 
|  | 656 | u32 tmp; | 
|  | 657 | int i; | 
|  | 658 | int cnt; | 
|  | 659 |  | 
| Boris BREZILLON | c9118ec | 2015-09-30 23:45:23 +0200 | [diff] [blame^] | 660 | sunxi_nfc_hw_ecc_enable(mtd); | 
| Boris BREZILLON | 1fef62c | 2014-10-21 15:08:41 +0200 | [diff] [blame] | 661 |  | 
|  | 662 | for (i = 0; i < ecc->steps; i++) { | 
|  | 663 | if (i) | 
|  | 664 | chip->cmdfunc(mtd, NAND_CMD_RNDIN, i * ecc->size, -1); | 
|  | 665 |  | 
|  | 666 | chip->write_buf(mtd, buf + (i * ecc->size), ecc->size); | 
|  | 667 |  | 
|  | 668 | offset = layout->eccpos[i * ecc->bytes] - 4 + mtd->writesize; | 
|  | 669 |  | 
|  | 670 | /* Fill OOB data in */ | 
| Boris BREZILLON | 03a0e8a | 2015-09-14 10:41:03 +0200 | [diff] [blame] | 671 | writel(NFC_BUF_TO_USER_DATA(chip->oob_poi + | 
|  | 672 | layout->oobfree[i].offset), | 
| Boris BREZILLON | b6a02c0 | 2015-09-16 09:46:36 +0200 | [diff] [blame] | 673 | nfc->regs + NFC_REG_USER_DATA(0)); | 
| Boris BREZILLON | 1fef62c | 2014-10-21 15:08:41 +0200 | [diff] [blame] | 674 |  | 
|  | 675 | chip->cmdfunc(mtd, NAND_CMD_RNDIN, offset, -1); | 
|  | 676 |  | 
|  | 677 | ret = sunxi_nfc_wait_cmd_fifo_empty(nfc); | 
|  | 678 | if (ret) | 
|  | 679 | return ret; | 
|  | 680 |  | 
|  | 681 | tmp = NFC_DATA_TRANS | NFC_DATA_SWAP_METHOD | NFC_ACCESS_DIR | | 
| Boris BREZILLON | b6a02c0 | 2015-09-16 09:46:36 +0200 | [diff] [blame] | 682 | NFC_ECC_OP; | 
| Boris BREZILLON | 1fef62c | 2014-10-21 15:08:41 +0200 | [diff] [blame] | 683 | writel(tmp, nfc->regs + NFC_REG_CMD); | 
|  | 684 | ret = sunxi_nfc_wait_int(nfc, NFC_CMD_INT_FLAG, 0); | 
|  | 685 | if (ret) | 
|  | 686 | return ret; | 
|  | 687 | } | 
|  | 688 |  | 
|  | 689 | if (oob_required) { | 
|  | 690 | cnt = ecc->layout->oobfree[i].length; | 
|  | 691 | if (cnt > 0) { | 
|  | 692 | offset = mtd->writesize + | 
|  | 693 | ecc->layout->oobfree[i].offset; | 
|  | 694 | chip->cmdfunc(mtd, NAND_CMD_RNDIN, offset, -1); | 
|  | 695 | offset -= mtd->writesize; | 
|  | 696 | chip->write_buf(mtd, chip->oob_poi + offset, cnt); | 
|  | 697 | } | 
|  | 698 | } | 
|  | 699 |  | 
| Boris BREZILLON | c9118ec | 2015-09-30 23:45:23 +0200 | [diff] [blame^] | 700 | sunxi_nfc_hw_ecc_disable(mtd); | 
| Boris BREZILLON | 1fef62c | 2014-10-21 15:08:41 +0200 | [diff] [blame] | 701 |  | 
|  | 702 | return 0; | 
|  | 703 | } | 
|  | 704 |  | 
|  | 705 | static int sunxi_nfc_hw_syndrome_ecc_read_page(struct mtd_info *mtd, | 
|  | 706 | struct nand_chip *chip, | 
|  | 707 | uint8_t *buf, int oob_required, | 
|  | 708 | int page) | 
|  | 709 | { | 
|  | 710 | struct sunxi_nfc *nfc = to_sunxi_nfc(chip->controller); | 
|  | 711 | struct nand_ecc_ctrl *ecc = &chip->ecc; | 
| Boris BREZILLON | 1fef62c | 2014-10-21 15:08:41 +0200 | [diff] [blame] | 712 | unsigned int max_bitflips = 0; | 
|  | 713 | uint8_t *oob = chip->oob_poi; | 
|  | 714 | int offset = 0; | 
|  | 715 | int ret; | 
|  | 716 | int cnt; | 
|  | 717 | u32 tmp; | 
|  | 718 | int i; | 
|  | 719 |  | 
| Boris BREZILLON | c9118ec | 2015-09-30 23:45:23 +0200 | [diff] [blame^] | 720 | sunxi_nfc_hw_ecc_enable(mtd); | 
| Boris BREZILLON | 1fef62c | 2014-10-21 15:08:41 +0200 | [diff] [blame] | 721 |  | 
|  | 722 | for (i = 0; i < ecc->steps; i++) { | 
|  | 723 | chip->read_buf(mtd, NULL, ecc->size); | 
|  | 724 |  | 
| Boris BREZILLON | b6a02c0 | 2015-09-16 09:46:36 +0200 | [diff] [blame] | 725 | tmp = NFC_DATA_TRANS | NFC_DATA_SWAP_METHOD | NFC_ECC_OP; | 
| Boris BREZILLON | 1fef62c | 2014-10-21 15:08:41 +0200 | [diff] [blame] | 726 | writel(tmp, nfc->regs + NFC_REG_CMD); | 
|  | 727 |  | 
|  | 728 | ret = sunxi_nfc_wait_int(nfc, NFC_CMD_INT_FLAG, 0); | 
|  | 729 | if (ret) | 
|  | 730 | return ret; | 
|  | 731 |  | 
|  | 732 | memcpy_fromio(buf, nfc->regs + NFC_RAM0_BASE, ecc->size); | 
|  | 733 | buf += ecc->size; | 
|  | 734 | offset += ecc->size; | 
|  | 735 |  | 
| Boris BREZILLON | b6a02c0 | 2015-09-16 09:46:36 +0200 | [diff] [blame] | 736 | if (readl(nfc->regs + NFC_REG_ECC_ST) & NFC_ECC_ERR(0)) { | 
| Boris BREZILLON | 1fef62c | 2014-10-21 15:08:41 +0200 | [diff] [blame] | 737 | mtd->ecc_stats.failed++; | 
|  | 738 | } else { | 
| Boris BREZILLON | b6a02c0 | 2015-09-16 09:46:36 +0200 | [diff] [blame] | 739 | tmp = readl(nfc->regs + NFC_REG_ECC_ERR_CNT(0)); | 
|  | 740 | mtd->ecc_stats.corrected += NFC_ECC_ERR_CNT(0, tmp); | 
| Boris BREZILLON | 1fef62c | 2014-10-21 15:08:41 +0200 | [diff] [blame] | 741 | max_bitflips = max_t(unsigned int, max_bitflips, tmp); | 
|  | 742 | } | 
|  | 743 |  | 
|  | 744 | if (oob_required) { | 
|  | 745 | chip->cmdfunc(mtd, NAND_CMD_RNDOUT, offset, -1); | 
|  | 746 | chip->read_buf(mtd, oob, ecc->bytes + ecc->prepad); | 
|  | 747 | oob += ecc->bytes + ecc->prepad; | 
|  | 748 | } | 
|  | 749 |  | 
|  | 750 | offset += ecc->bytes + ecc->prepad; | 
|  | 751 | } | 
|  | 752 |  | 
|  | 753 | if (oob_required) { | 
|  | 754 | cnt = mtd->oobsize - (oob - chip->oob_poi); | 
|  | 755 | if (cnt > 0) { | 
|  | 756 | chip->cmdfunc(mtd, NAND_CMD_RNDOUT, offset, -1); | 
|  | 757 | chip->read_buf(mtd, oob, cnt); | 
|  | 758 | } | 
|  | 759 | } | 
|  | 760 |  | 
| Boris BREZILLON | c9118ec | 2015-09-30 23:45:23 +0200 | [diff] [blame^] | 761 | sunxi_nfc_hw_ecc_disable(mtd); | 
| Boris BREZILLON | 1fef62c | 2014-10-21 15:08:41 +0200 | [diff] [blame] | 762 |  | 
|  | 763 | return max_bitflips; | 
|  | 764 | } | 
|  | 765 |  | 
|  | 766 | static int sunxi_nfc_hw_syndrome_ecc_write_page(struct mtd_info *mtd, | 
|  | 767 | struct nand_chip *chip, | 
|  | 768 | const uint8_t *buf, | 
|  | 769 | int oob_required) | 
|  | 770 | { | 
|  | 771 | struct sunxi_nfc *nfc = to_sunxi_nfc(chip->controller); | 
|  | 772 | struct nand_ecc_ctrl *ecc = &chip->ecc; | 
| Boris BREZILLON | 1fef62c | 2014-10-21 15:08:41 +0200 | [diff] [blame] | 773 | uint8_t *oob = chip->oob_poi; | 
|  | 774 | int offset = 0; | 
|  | 775 | int ret; | 
|  | 776 | int cnt; | 
|  | 777 | u32 tmp; | 
|  | 778 | int i; | 
|  | 779 |  | 
| Boris BREZILLON | c9118ec | 2015-09-30 23:45:23 +0200 | [diff] [blame^] | 780 | sunxi_nfc_hw_ecc_enable(mtd); | 
| Boris BREZILLON | 1fef62c | 2014-10-21 15:08:41 +0200 | [diff] [blame] | 781 |  | 
|  | 782 | for (i = 0; i < ecc->steps; i++) { | 
|  | 783 | chip->write_buf(mtd, buf + (i * ecc->size), ecc->size); | 
|  | 784 | offset += ecc->size; | 
|  | 785 |  | 
|  | 786 | /* Fill OOB data in */ | 
| Boris BREZILLON | 03a0e8a | 2015-09-14 10:41:03 +0200 | [diff] [blame] | 787 | writel(NFC_BUF_TO_USER_DATA(oob), | 
| Boris BREZILLON | b6a02c0 | 2015-09-16 09:46:36 +0200 | [diff] [blame] | 788 | nfc->regs + NFC_REG_USER_DATA(0)); | 
| Boris BREZILLON | 1fef62c | 2014-10-21 15:08:41 +0200 | [diff] [blame] | 789 |  | 
|  | 790 | tmp = NFC_DATA_TRANS | NFC_DATA_SWAP_METHOD | NFC_ACCESS_DIR | | 
| Boris BREZILLON | b6a02c0 | 2015-09-16 09:46:36 +0200 | [diff] [blame] | 791 | NFC_ECC_OP; | 
| Boris BREZILLON | 1fef62c | 2014-10-21 15:08:41 +0200 | [diff] [blame] | 792 | writel(tmp, nfc->regs + NFC_REG_CMD); | 
|  | 793 |  | 
|  | 794 | ret = sunxi_nfc_wait_int(nfc, NFC_CMD_INT_FLAG, 0); | 
|  | 795 | if (ret) | 
|  | 796 | return ret; | 
|  | 797 |  | 
|  | 798 | offset += ecc->bytes + ecc->prepad; | 
|  | 799 | oob += ecc->bytes + ecc->prepad; | 
|  | 800 | } | 
|  | 801 |  | 
|  | 802 | if (oob_required) { | 
|  | 803 | cnt = mtd->oobsize - (oob - chip->oob_poi); | 
|  | 804 | if (cnt > 0) { | 
|  | 805 | chip->cmdfunc(mtd, NAND_CMD_RNDIN, offset, -1); | 
|  | 806 | chip->write_buf(mtd, oob, cnt); | 
|  | 807 | } | 
|  | 808 | } | 
|  | 809 |  | 
| Boris BREZILLON | c9118ec | 2015-09-30 23:45:23 +0200 | [diff] [blame^] | 810 | sunxi_nfc_hw_ecc_disable(mtd); | 
| Boris BREZILLON | 1fef62c | 2014-10-21 15:08:41 +0200 | [diff] [blame] | 811 |  | 
|  | 812 | return 0; | 
|  | 813 | } | 
|  | 814 |  | 
| Roy Spliet | 9c61829 | 2015-06-26 11:00:10 +0200 | [diff] [blame] | 815 | static const s32 tWB_lut[] = {6, 12, 16, 20}; | 
|  | 816 | static const s32 tRHW_lut[] = {4, 8, 12, 20}; | 
|  | 817 |  | 
|  | 818 | static int _sunxi_nand_lookup_timing(const s32 *lut, int lut_size, u32 duration, | 
|  | 819 | u32 clk_period) | 
|  | 820 | { | 
|  | 821 | u32 clk_cycles = DIV_ROUND_UP(duration, clk_period); | 
|  | 822 | int i; | 
|  | 823 |  | 
|  | 824 | for (i = 0; i < lut_size; i++) { | 
|  | 825 | if (clk_cycles <= lut[i]) | 
|  | 826 | return i; | 
|  | 827 | } | 
|  | 828 |  | 
|  | 829 | /* Doesn't fit */ | 
|  | 830 | return -EINVAL; | 
|  | 831 | } | 
|  | 832 |  | 
|  | 833 | #define sunxi_nand_lookup_timing(l, p, c) \ | 
|  | 834 | _sunxi_nand_lookup_timing(l, ARRAY_SIZE(l), p, c) | 
|  | 835 |  | 
| Boris BREZILLON | 1fef62c | 2014-10-21 15:08:41 +0200 | [diff] [blame] | 836 | static int sunxi_nand_chip_set_timings(struct sunxi_nand_chip *chip, | 
|  | 837 | const struct nand_sdr_timings *timings) | 
|  | 838 | { | 
| Roy Spliet | 9c61829 | 2015-06-26 11:00:10 +0200 | [diff] [blame] | 839 | struct sunxi_nfc *nfc = to_sunxi_nfc(chip->nand.controller); | 
| Boris BREZILLON | 1fef62c | 2014-10-21 15:08:41 +0200 | [diff] [blame] | 840 | u32 min_clk_period = 0; | 
| Roy Spliet | 9c61829 | 2015-06-26 11:00:10 +0200 | [diff] [blame] | 841 | s32 tWB, tADL, tWHR, tRHW, tCAD; | 
| Boris BREZILLON | 1fef62c | 2014-10-21 15:08:41 +0200 | [diff] [blame] | 842 |  | 
|  | 843 | /* T1 <=> tCLS */ | 
|  | 844 | if (timings->tCLS_min > min_clk_period) | 
|  | 845 | min_clk_period = timings->tCLS_min; | 
|  | 846 |  | 
|  | 847 | /* T2 <=> tCLH */ | 
|  | 848 | if (timings->tCLH_min > min_clk_period) | 
|  | 849 | min_clk_period = timings->tCLH_min; | 
|  | 850 |  | 
|  | 851 | /* T3 <=> tCS */ | 
|  | 852 | if (timings->tCS_min > min_clk_period) | 
|  | 853 | min_clk_period = timings->tCS_min; | 
|  | 854 |  | 
|  | 855 | /* T4 <=> tCH */ | 
|  | 856 | if (timings->tCH_min > min_clk_period) | 
|  | 857 | min_clk_period = timings->tCH_min; | 
|  | 858 |  | 
|  | 859 | /* T5 <=> tWP */ | 
|  | 860 | if (timings->tWP_min > min_clk_period) | 
|  | 861 | min_clk_period = timings->tWP_min; | 
|  | 862 |  | 
|  | 863 | /* T6 <=> tWH */ | 
|  | 864 | if (timings->tWH_min > min_clk_period) | 
|  | 865 | min_clk_period = timings->tWH_min; | 
|  | 866 |  | 
|  | 867 | /* T7 <=> tALS */ | 
|  | 868 | if (timings->tALS_min > min_clk_period) | 
|  | 869 | min_clk_period = timings->tALS_min; | 
|  | 870 |  | 
|  | 871 | /* T8 <=> tDS */ | 
|  | 872 | if (timings->tDS_min > min_clk_period) | 
|  | 873 | min_clk_period = timings->tDS_min; | 
|  | 874 |  | 
|  | 875 | /* T9 <=> tDH */ | 
|  | 876 | if (timings->tDH_min > min_clk_period) | 
|  | 877 | min_clk_period = timings->tDH_min; | 
|  | 878 |  | 
|  | 879 | /* T10 <=> tRR */ | 
|  | 880 | if (timings->tRR_min > (min_clk_period * 3)) | 
|  | 881 | min_clk_period = DIV_ROUND_UP(timings->tRR_min, 3); | 
|  | 882 |  | 
|  | 883 | /* T11 <=> tALH */ | 
|  | 884 | if (timings->tALH_min > min_clk_period) | 
|  | 885 | min_clk_period = timings->tALH_min; | 
|  | 886 |  | 
|  | 887 | /* T12 <=> tRP */ | 
|  | 888 | if (timings->tRP_min > min_clk_period) | 
|  | 889 | min_clk_period = timings->tRP_min; | 
|  | 890 |  | 
|  | 891 | /* T13 <=> tREH */ | 
|  | 892 | if (timings->tREH_min > min_clk_period) | 
|  | 893 | min_clk_period = timings->tREH_min; | 
|  | 894 |  | 
|  | 895 | /* T14 <=> tRC */ | 
|  | 896 | if (timings->tRC_min > (min_clk_period * 2)) | 
|  | 897 | min_clk_period = DIV_ROUND_UP(timings->tRC_min, 2); | 
|  | 898 |  | 
|  | 899 | /* T15 <=> tWC */ | 
|  | 900 | if (timings->tWC_min > (min_clk_period * 2)) | 
|  | 901 | min_clk_period = DIV_ROUND_UP(timings->tWC_min, 2); | 
|  | 902 |  | 
| Roy Spliet | 9c61829 | 2015-06-26 11:00:10 +0200 | [diff] [blame] | 903 | /* T16 - T19 + tCAD */ | 
|  | 904 | tWB  = sunxi_nand_lookup_timing(tWB_lut, timings->tWB_max, | 
|  | 905 | min_clk_period); | 
|  | 906 | if (tWB < 0) { | 
|  | 907 | dev_err(nfc->dev, "unsupported tWB\n"); | 
|  | 908 | return tWB; | 
|  | 909 | } | 
|  | 910 |  | 
|  | 911 | tADL = DIV_ROUND_UP(timings->tADL_min, min_clk_period) >> 3; | 
|  | 912 | if (tADL > 3) { | 
|  | 913 | dev_err(nfc->dev, "unsupported tADL\n"); | 
|  | 914 | return -EINVAL; | 
|  | 915 | } | 
|  | 916 |  | 
|  | 917 | tWHR = DIV_ROUND_UP(timings->tWHR_min, min_clk_period) >> 3; | 
|  | 918 | if (tWHR > 3) { | 
|  | 919 | dev_err(nfc->dev, "unsupported tWHR\n"); | 
|  | 920 | return -EINVAL; | 
|  | 921 | } | 
|  | 922 |  | 
|  | 923 | tRHW = sunxi_nand_lookup_timing(tRHW_lut, timings->tRHW_min, | 
|  | 924 | min_clk_period); | 
|  | 925 | if (tRHW < 0) { | 
|  | 926 | dev_err(nfc->dev, "unsupported tRHW\n"); | 
|  | 927 | return tRHW; | 
|  | 928 | } | 
|  | 929 |  | 
|  | 930 | /* | 
|  | 931 | * TODO: according to ONFI specs this value only applies for DDR NAND, | 
|  | 932 | * but Allwinner seems to set this to 0x7. Mimic them for now. | 
|  | 933 | */ | 
|  | 934 | tCAD = 0x7; | 
|  | 935 |  | 
|  | 936 | /* TODO: A83 has some more bits for CDQSS, CS, CLHZ, CCS, WC */ | 
|  | 937 | chip->timing_cfg = NFC_TIMING_CFG(tWB, tADL, tWHR, tRHW, tCAD); | 
| Boris BREZILLON | 1fef62c | 2014-10-21 15:08:41 +0200 | [diff] [blame] | 938 |  | 
| Roy Spliet | d052e50 | 2015-06-26 11:00:11 +0200 | [diff] [blame] | 939 | /* | 
|  | 940 | * ONFI specification 3.1, paragraph 4.15.2 dictates that EDO data | 
|  | 941 | * output cycle timings shall be used if the host drives tRC less than | 
|  | 942 | * 30 ns. | 
|  | 943 | */ | 
|  | 944 | chip->timing_ctl = (timings->tRC_min < 30000) ? NFC_TIMING_CTL_EDO : 0; | 
|  | 945 |  | 
| Boris BREZILLON | 1fef62c | 2014-10-21 15:08:41 +0200 | [diff] [blame] | 946 | /* Convert min_clk_period from picoseconds to nanoseconds */ | 
|  | 947 | min_clk_period = DIV_ROUND_UP(min_clk_period, 1000); | 
|  | 948 |  | 
|  | 949 | /* | 
|  | 950 | * Convert min_clk_period into a clk frequency, then get the | 
|  | 951 | * appropriate rate for the NAND controller IP given this formula | 
|  | 952 | * (specified in the datasheet): | 
|  | 953 | * nand clk_rate = 2 * min_clk_rate | 
|  | 954 | */ | 
|  | 955 | chip->clk_rate = (2 * NSEC_PER_SEC) / min_clk_period; | 
|  | 956 |  | 
| Boris BREZILLON | 1fef62c | 2014-10-21 15:08:41 +0200 | [diff] [blame] | 957 | return 0; | 
|  | 958 | } | 
|  | 959 |  | 
|  | 960 | static int sunxi_nand_chip_init_timings(struct sunxi_nand_chip *chip, | 
|  | 961 | struct device_node *np) | 
|  | 962 | { | 
|  | 963 | const struct nand_sdr_timings *timings; | 
|  | 964 | int ret; | 
|  | 965 | int mode; | 
|  | 966 |  | 
|  | 967 | mode = onfi_get_async_timing_mode(&chip->nand); | 
|  | 968 | if (mode == ONFI_TIMING_MODE_UNKNOWN) { | 
|  | 969 | mode = chip->nand.onfi_timing_mode_default; | 
|  | 970 | } else { | 
|  | 971 | uint8_t feature[ONFI_SUBFEATURE_PARAM_LEN] = {}; | 
| Stefan Roese | 7eadd47 | 2015-08-28 14:45:21 +0200 | [diff] [blame] | 972 | int i; | 
| Boris BREZILLON | 1fef62c | 2014-10-21 15:08:41 +0200 | [diff] [blame] | 973 |  | 
|  | 974 | mode = fls(mode) - 1; | 
|  | 975 | if (mode < 0) | 
|  | 976 | mode = 0; | 
|  | 977 |  | 
|  | 978 | feature[0] = mode; | 
| Stefan Roese | 7eadd47 | 2015-08-28 14:45:21 +0200 | [diff] [blame] | 979 | for (i = 0; i < chip->nsels; i++) { | 
|  | 980 | chip->nand.select_chip(&chip->mtd, i); | 
|  | 981 | ret = chip->nand.onfi_set_features(&chip->mtd, | 
|  | 982 | &chip->nand, | 
| Boris BREZILLON | 1fef62c | 2014-10-21 15:08:41 +0200 | [diff] [blame] | 983 | ONFI_FEATURE_ADDR_TIMING_MODE, | 
|  | 984 | feature); | 
| Stefan Roese | 7eadd47 | 2015-08-28 14:45:21 +0200 | [diff] [blame] | 985 | chip->nand.select_chip(&chip->mtd, -1); | 
|  | 986 | if (ret) | 
|  | 987 | return ret; | 
|  | 988 | } | 
| Boris BREZILLON | 1fef62c | 2014-10-21 15:08:41 +0200 | [diff] [blame] | 989 | } | 
|  | 990 |  | 
|  | 991 | timings = onfi_async_timing_mode_to_sdr_timings(mode); | 
|  | 992 | if (IS_ERR(timings)) | 
|  | 993 | return PTR_ERR(timings); | 
|  | 994 |  | 
|  | 995 | return sunxi_nand_chip_set_timings(chip, timings); | 
|  | 996 | } | 
|  | 997 |  | 
|  | 998 | static int sunxi_nand_hw_common_ecc_ctrl_init(struct mtd_info *mtd, | 
|  | 999 | struct nand_ecc_ctrl *ecc, | 
|  | 1000 | struct device_node *np) | 
|  | 1001 | { | 
|  | 1002 | static const u8 strengths[] = { 16, 24, 28, 32, 40, 48, 56, 60, 64 }; | 
|  | 1003 | struct nand_chip *nand = mtd->priv; | 
|  | 1004 | struct sunxi_nand_chip *sunxi_nand = to_sunxi_nand(nand); | 
|  | 1005 | struct sunxi_nfc *nfc = to_sunxi_nfc(sunxi_nand->nand.controller); | 
|  | 1006 | struct sunxi_nand_hw_ecc *data; | 
|  | 1007 | struct nand_ecclayout *layout; | 
|  | 1008 | int nsectors; | 
|  | 1009 | int ret; | 
|  | 1010 | int i; | 
|  | 1011 |  | 
|  | 1012 | data = kzalloc(sizeof(*data), GFP_KERNEL); | 
|  | 1013 | if (!data) | 
|  | 1014 | return -ENOMEM; | 
|  | 1015 |  | 
|  | 1016 | /* Add ECC info retrieval from DT */ | 
|  | 1017 | for (i = 0; i < ARRAY_SIZE(strengths); i++) { | 
|  | 1018 | if (ecc->strength <= strengths[i]) | 
|  | 1019 | break; | 
|  | 1020 | } | 
|  | 1021 |  | 
|  | 1022 | if (i >= ARRAY_SIZE(strengths)) { | 
|  | 1023 | dev_err(nfc->dev, "unsupported strength\n"); | 
|  | 1024 | ret = -ENOTSUPP; | 
|  | 1025 | goto err; | 
|  | 1026 | } | 
|  | 1027 |  | 
|  | 1028 | data->mode = i; | 
|  | 1029 |  | 
|  | 1030 | /* HW ECC always request ECC bytes for 1024 bytes blocks */ | 
|  | 1031 | ecc->bytes = DIV_ROUND_UP(ecc->strength * fls(8 * 1024), 8); | 
|  | 1032 |  | 
|  | 1033 | /* HW ECC always work with even numbers of ECC bytes */ | 
|  | 1034 | ecc->bytes = ALIGN(ecc->bytes, 2); | 
|  | 1035 |  | 
|  | 1036 | layout = &data->layout; | 
|  | 1037 | nsectors = mtd->writesize / ecc->size; | 
|  | 1038 |  | 
|  | 1039 | if (mtd->oobsize < ((ecc->bytes + 4) * nsectors)) { | 
|  | 1040 | ret = -EINVAL; | 
|  | 1041 | goto err; | 
|  | 1042 | } | 
|  | 1043 |  | 
|  | 1044 | layout->eccbytes = (ecc->bytes * nsectors); | 
|  | 1045 |  | 
|  | 1046 | ecc->layout = layout; | 
|  | 1047 | ecc->priv = data; | 
|  | 1048 |  | 
|  | 1049 | return 0; | 
|  | 1050 |  | 
|  | 1051 | err: | 
|  | 1052 | kfree(data); | 
|  | 1053 |  | 
|  | 1054 | return ret; | 
|  | 1055 | } | 
|  | 1056 |  | 
|  | 1057 | static void sunxi_nand_hw_common_ecc_ctrl_cleanup(struct nand_ecc_ctrl *ecc) | 
|  | 1058 | { | 
|  | 1059 | kfree(ecc->priv); | 
|  | 1060 | } | 
|  | 1061 |  | 
|  | 1062 | static int sunxi_nand_hw_ecc_ctrl_init(struct mtd_info *mtd, | 
|  | 1063 | struct nand_ecc_ctrl *ecc, | 
|  | 1064 | struct device_node *np) | 
|  | 1065 | { | 
|  | 1066 | struct nand_ecclayout *layout; | 
|  | 1067 | int nsectors; | 
|  | 1068 | int i, j; | 
|  | 1069 | int ret; | 
|  | 1070 |  | 
|  | 1071 | ret = sunxi_nand_hw_common_ecc_ctrl_init(mtd, ecc, np); | 
|  | 1072 | if (ret) | 
|  | 1073 | return ret; | 
|  | 1074 |  | 
|  | 1075 | ecc->read_page = sunxi_nfc_hw_ecc_read_page; | 
|  | 1076 | ecc->write_page = sunxi_nfc_hw_ecc_write_page; | 
|  | 1077 | layout = ecc->layout; | 
|  | 1078 | nsectors = mtd->writesize / ecc->size; | 
|  | 1079 |  | 
|  | 1080 | for (i = 0; i < nsectors; i++) { | 
|  | 1081 | if (i) { | 
|  | 1082 | layout->oobfree[i].offset = | 
|  | 1083 | layout->oobfree[i - 1].offset + | 
|  | 1084 | layout->oobfree[i - 1].length + | 
|  | 1085 | ecc->bytes; | 
|  | 1086 | layout->oobfree[i].length = 4; | 
|  | 1087 | } else { | 
|  | 1088 | /* | 
|  | 1089 | * The first 2 bytes are used for BB markers, hence we | 
|  | 1090 | * only have 2 bytes available in the first user data | 
|  | 1091 | * section. | 
|  | 1092 | */ | 
|  | 1093 | layout->oobfree[i].length = 2; | 
|  | 1094 | layout->oobfree[i].offset = 2; | 
|  | 1095 | } | 
|  | 1096 |  | 
|  | 1097 | for (j = 0; j < ecc->bytes; j++) | 
|  | 1098 | layout->eccpos[(ecc->bytes * i) + j] = | 
|  | 1099 | layout->oobfree[i].offset + | 
|  | 1100 | layout->oobfree[i].length + j; | 
|  | 1101 | } | 
|  | 1102 |  | 
|  | 1103 | if (mtd->oobsize > (ecc->bytes + 4) * nsectors) { | 
|  | 1104 | layout->oobfree[nsectors].offset = | 
|  | 1105 | layout->oobfree[nsectors - 1].offset + | 
|  | 1106 | layout->oobfree[nsectors - 1].length + | 
|  | 1107 | ecc->bytes; | 
|  | 1108 | layout->oobfree[nsectors].length = mtd->oobsize - | 
|  | 1109 | ((ecc->bytes + 4) * nsectors); | 
|  | 1110 | } | 
|  | 1111 |  | 
|  | 1112 | return 0; | 
|  | 1113 | } | 
|  | 1114 |  | 
|  | 1115 | static int sunxi_nand_hw_syndrome_ecc_ctrl_init(struct mtd_info *mtd, | 
|  | 1116 | struct nand_ecc_ctrl *ecc, | 
|  | 1117 | struct device_node *np) | 
|  | 1118 | { | 
|  | 1119 | struct nand_ecclayout *layout; | 
|  | 1120 | int nsectors; | 
|  | 1121 | int i; | 
|  | 1122 | int ret; | 
|  | 1123 |  | 
|  | 1124 | ret = sunxi_nand_hw_common_ecc_ctrl_init(mtd, ecc, np); | 
|  | 1125 | if (ret) | 
|  | 1126 | return ret; | 
|  | 1127 |  | 
|  | 1128 | ecc->prepad = 4; | 
|  | 1129 | ecc->read_page = sunxi_nfc_hw_syndrome_ecc_read_page; | 
|  | 1130 | ecc->write_page = sunxi_nfc_hw_syndrome_ecc_write_page; | 
|  | 1131 |  | 
|  | 1132 | layout = ecc->layout; | 
|  | 1133 | nsectors = mtd->writesize / ecc->size; | 
|  | 1134 |  | 
|  | 1135 | for (i = 0; i < (ecc->bytes * nsectors); i++) | 
|  | 1136 | layout->eccpos[i] = i; | 
|  | 1137 |  | 
|  | 1138 | layout->oobfree[0].length = mtd->oobsize - i; | 
|  | 1139 | layout->oobfree[0].offset = i; | 
|  | 1140 |  | 
|  | 1141 | return 0; | 
|  | 1142 | } | 
|  | 1143 |  | 
|  | 1144 | static void sunxi_nand_ecc_cleanup(struct nand_ecc_ctrl *ecc) | 
|  | 1145 | { | 
|  | 1146 | switch (ecc->mode) { | 
|  | 1147 | case NAND_ECC_HW: | 
|  | 1148 | case NAND_ECC_HW_SYNDROME: | 
|  | 1149 | sunxi_nand_hw_common_ecc_ctrl_cleanup(ecc); | 
|  | 1150 | break; | 
|  | 1151 | case NAND_ECC_NONE: | 
|  | 1152 | kfree(ecc->layout); | 
|  | 1153 | default: | 
|  | 1154 | break; | 
|  | 1155 | } | 
|  | 1156 | } | 
|  | 1157 |  | 
|  | 1158 | static int sunxi_nand_ecc_init(struct mtd_info *mtd, struct nand_ecc_ctrl *ecc, | 
|  | 1159 | struct device_node *np) | 
|  | 1160 | { | 
|  | 1161 | struct nand_chip *nand = mtd->priv; | 
| Boris BREZILLON | 1fef62c | 2014-10-21 15:08:41 +0200 | [diff] [blame] | 1162 | int ret; | 
|  | 1163 |  | 
| Boris BREZILLON | a3d22a5 | 2015-09-02 10:30:25 +0200 | [diff] [blame] | 1164 | if (!ecc->size) { | 
| Boris BREZILLON | 1fef62c | 2014-10-21 15:08:41 +0200 | [diff] [blame] | 1165 | ecc->size = nand->ecc_step_ds; | 
|  | 1166 | ecc->strength = nand->ecc_strength_ds; | 
|  | 1167 | } | 
|  | 1168 |  | 
|  | 1169 | if (!ecc->size || !ecc->strength) | 
|  | 1170 | return -EINVAL; | 
|  | 1171 |  | 
| Boris BREZILLON | 1fef62c | 2014-10-21 15:08:41 +0200 | [diff] [blame] | 1172 | switch (ecc->mode) { | 
|  | 1173 | case NAND_ECC_SOFT_BCH: | 
| Boris BREZILLON | 1fef62c | 2014-10-21 15:08:41 +0200 | [diff] [blame] | 1174 | break; | 
|  | 1175 | case NAND_ECC_HW: | 
|  | 1176 | ret = sunxi_nand_hw_ecc_ctrl_init(mtd, ecc, np); | 
|  | 1177 | if (ret) | 
|  | 1178 | return ret; | 
|  | 1179 | break; | 
|  | 1180 | case NAND_ECC_HW_SYNDROME: | 
|  | 1181 | ret = sunxi_nand_hw_syndrome_ecc_ctrl_init(mtd, ecc, np); | 
|  | 1182 | if (ret) | 
|  | 1183 | return ret; | 
|  | 1184 | break; | 
|  | 1185 | case NAND_ECC_NONE: | 
|  | 1186 | ecc->layout = kzalloc(sizeof(*ecc->layout), GFP_KERNEL); | 
|  | 1187 | if (!ecc->layout) | 
|  | 1188 | return -ENOMEM; | 
|  | 1189 | ecc->layout->oobfree[0].length = mtd->oobsize; | 
|  | 1190 | case NAND_ECC_SOFT: | 
|  | 1191 | break; | 
|  | 1192 | default: | 
|  | 1193 | return -EINVAL; | 
|  | 1194 | } | 
|  | 1195 |  | 
|  | 1196 | return 0; | 
|  | 1197 | } | 
|  | 1198 |  | 
|  | 1199 | static int sunxi_nand_chip_init(struct device *dev, struct sunxi_nfc *nfc, | 
|  | 1200 | struct device_node *np) | 
|  | 1201 | { | 
|  | 1202 | const struct nand_sdr_timings *timings; | 
|  | 1203 | struct sunxi_nand_chip *chip; | 
|  | 1204 | struct mtd_part_parser_data ppdata; | 
|  | 1205 | struct mtd_info *mtd; | 
|  | 1206 | struct nand_chip *nand; | 
|  | 1207 | int nsels; | 
|  | 1208 | int ret; | 
|  | 1209 | int i; | 
|  | 1210 | u32 tmp; | 
|  | 1211 |  | 
|  | 1212 | if (!of_get_property(np, "reg", &nsels)) | 
|  | 1213 | return -EINVAL; | 
|  | 1214 |  | 
|  | 1215 | nsels /= sizeof(u32); | 
|  | 1216 | if (!nsels) { | 
|  | 1217 | dev_err(dev, "invalid reg property size\n"); | 
|  | 1218 | return -EINVAL; | 
|  | 1219 | } | 
|  | 1220 |  | 
|  | 1221 | chip = devm_kzalloc(dev, | 
|  | 1222 | sizeof(*chip) + | 
|  | 1223 | (nsels * sizeof(struct sunxi_nand_chip_sel)), | 
|  | 1224 | GFP_KERNEL); | 
|  | 1225 | if (!chip) { | 
|  | 1226 | dev_err(dev, "could not allocate chip\n"); | 
|  | 1227 | return -ENOMEM; | 
|  | 1228 | } | 
|  | 1229 |  | 
|  | 1230 | chip->nsels = nsels; | 
|  | 1231 | chip->selected = -1; | 
|  | 1232 |  | 
|  | 1233 | for (i = 0; i < nsels; i++) { | 
|  | 1234 | ret = of_property_read_u32_index(np, "reg", i, &tmp); | 
|  | 1235 | if (ret) { | 
|  | 1236 | dev_err(dev, "could not retrieve reg property: %d\n", | 
|  | 1237 | ret); | 
|  | 1238 | return ret; | 
|  | 1239 | } | 
|  | 1240 |  | 
|  | 1241 | if (tmp > NFC_MAX_CS) { | 
|  | 1242 | dev_err(dev, | 
|  | 1243 | "invalid reg value: %u (max CS = 7)\n", | 
|  | 1244 | tmp); | 
|  | 1245 | return -EINVAL; | 
|  | 1246 | } | 
|  | 1247 |  | 
|  | 1248 | if (test_and_set_bit(tmp, &nfc->assigned_cs)) { | 
|  | 1249 | dev_err(dev, "CS %d already assigned\n", tmp); | 
|  | 1250 | return -EINVAL; | 
|  | 1251 | } | 
|  | 1252 |  | 
|  | 1253 | chip->sels[i].cs = tmp; | 
|  | 1254 |  | 
|  | 1255 | if (!of_property_read_u32_index(np, "allwinner,rb", i, &tmp) && | 
|  | 1256 | tmp < 2) { | 
|  | 1257 | chip->sels[i].rb.type = RB_NATIVE; | 
|  | 1258 | chip->sels[i].rb.info.nativeid = tmp; | 
|  | 1259 | } else { | 
|  | 1260 | ret = of_get_named_gpio(np, "rb-gpios", i); | 
|  | 1261 | if (ret >= 0) { | 
|  | 1262 | tmp = ret; | 
|  | 1263 | chip->sels[i].rb.type = RB_GPIO; | 
|  | 1264 | chip->sels[i].rb.info.gpio = tmp; | 
|  | 1265 | ret = devm_gpio_request(dev, tmp, "nand-rb"); | 
|  | 1266 | if (ret) | 
|  | 1267 | return ret; | 
|  | 1268 |  | 
|  | 1269 | ret = gpio_direction_input(tmp); | 
|  | 1270 | if (ret) | 
|  | 1271 | return ret; | 
|  | 1272 | } else { | 
|  | 1273 | chip->sels[i].rb.type = RB_NONE; | 
|  | 1274 | } | 
|  | 1275 | } | 
|  | 1276 | } | 
|  | 1277 |  | 
|  | 1278 | timings = onfi_async_timing_mode_to_sdr_timings(0); | 
|  | 1279 | if (IS_ERR(timings)) { | 
|  | 1280 | ret = PTR_ERR(timings); | 
|  | 1281 | dev_err(dev, | 
|  | 1282 | "could not retrieve timings for ONFI mode 0: %d\n", | 
|  | 1283 | ret); | 
|  | 1284 | return ret; | 
|  | 1285 | } | 
|  | 1286 |  | 
|  | 1287 | ret = sunxi_nand_chip_set_timings(chip, timings); | 
|  | 1288 | if (ret) { | 
|  | 1289 | dev_err(dev, "could not configure chip timings: %d\n", ret); | 
|  | 1290 | return ret; | 
|  | 1291 | } | 
|  | 1292 |  | 
|  | 1293 | nand = &chip->nand; | 
|  | 1294 | /* Default tR value specified in the ONFI spec (chapter 4.15.1) */ | 
|  | 1295 | nand->chip_delay = 200; | 
|  | 1296 | nand->controller = &nfc->controller; | 
| Boris BREZILLON | a3d22a5 | 2015-09-02 10:30:25 +0200 | [diff] [blame] | 1297 | /* | 
|  | 1298 | * Set the ECC mode to the default value in case nothing is specified | 
|  | 1299 | * in the DT. | 
|  | 1300 | */ | 
|  | 1301 | nand->ecc.mode = NAND_ECC_HW; | 
|  | 1302 | nand->flash_node = np; | 
| Boris BREZILLON | 1fef62c | 2014-10-21 15:08:41 +0200 | [diff] [blame] | 1303 | nand->select_chip = sunxi_nfc_select_chip; | 
|  | 1304 | nand->cmd_ctrl = sunxi_nfc_cmd_ctrl; | 
|  | 1305 | nand->read_buf = sunxi_nfc_read_buf; | 
|  | 1306 | nand->write_buf = sunxi_nfc_write_buf; | 
|  | 1307 | nand->read_byte = sunxi_nfc_read_byte; | 
|  | 1308 |  | 
| Boris BREZILLON | 1fef62c | 2014-10-21 15:08:41 +0200 | [diff] [blame] | 1309 | mtd = &chip->mtd; | 
|  | 1310 | mtd->dev.parent = dev; | 
|  | 1311 | mtd->priv = nand; | 
|  | 1312 | mtd->owner = THIS_MODULE; | 
|  | 1313 |  | 
|  | 1314 | ret = nand_scan_ident(mtd, nsels, NULL); | 
|  | 1315 | if (ret) | 
|  | 1316 | return ret; | 
|  | 1317 |  | 
| Boris BREZILLON | a3d22a5 | 2015-09-02 10:30:25 +0200 | [diff] [blame] | 1318 | if (nand->bbt_options & NAND_BBT_USE_FLASH) | 
|  | 1319 | nand->bbt_options |= NAND_BBT_NO_OOB; | 
|  | 1320 |  | 
| Boris BREZILLON | 1fef62c | 2014-10-21 15:08:41 +0200 | [diff] [blame] | 1321 | ret = sunxi_nand_chip_init_timings(chip, np); | 
|  | 1322 | if (ret) { | 
|  | 1323 | dev_err(dev, "could not configure chip timings: %d\n", ret); | 
|  | 1324 | return ret; | 
|  | 1325 | } | 
|  | 1326 |  | 
|  | 1327 | ret = sunxi_nand_ecc_init(mtd, &nand->ecc, np); | 
|  | 1328 | if (ret) { | 
|  | 1329 | dev_err(dev, "ECC init failed: %d\n", ret); | 
|  | 1330 | return ret; | 
|  | 1331 | } | 
|  | 1332 |  | 
|  | 1333 | ret = nand_scan_tail(mtd); | 
|  | 1334 | if (ret) { | 
|  | 1335 | dev_err(dev, "nand_scan_tail failed: %d\n", ret); | 
|  | 1336 | return ret; | 
|  | 1337 | } | 
|  | 1338 |  | 
|  | 1339 | ppdata.of_node = np; | 
|  | 1340 | ret = mtd_device_parse_register(mtd, NULL, &ppdata, NULL, 0); | 
|  | 1341 | if (ret) { | 
|  | 1342 | dev_err(dev, "failed to register mtd device: %d\n", ret); | 
|  | 1343 | nand_release(mtd); | 
|  | 1344 | return ret; | 
|  | 1345 | } | 
|  | 1346 |  | 
|  | 1347 | list_add_tail(&chip->node, &nfc->chips); | 
|  | 1348 |  | 
|  | 1349 | return 0; | 
|  | 1350 | } | 
|  | 1351 |  | 
|  | 1352 | static int sunxi_nand_chips_init(struct device *dev, struct sunxi_nfc *nfc) | 
|  | 1353 | { | 
|  | 1354 | struct device_node *np = dev->of_node; | 
|  | 1355 | struct device_node *nand_np; | 
|  | 1356 | int nchips = of_get_child_count(np); | 
|  | 1357 | int ret; | 
|  | 1358 |  | 
|  | 1359 | if (nchips > 8) { | 
|  | 1360 | dev_err(dev, "too many NAND chips: %d (max = 8)\n", nchips); | 
|  | 1361 | return -EINVAL; | 
|  | 1362 | } | 
|  | 1363 |  | 
|  | 1364 | for_each_child_of_node(np, nand_np) { | 
|  | 1365 | ret = sunxi_nand_chip_init(dev, nfc, nand_np); | 
|  | 1366 | if (ret) | 
|  | 1367 | return ret; | 
|  | 1368 | } | 
|  | 1369 |  | 
|  | 1370 | return 0; | 
|  | 1371 | } | 
|  | 1372 |  | 
|  | 1373 | static void sunxi_nand_chips_cleanup(struct sunxi_nfc *nfc) | 
|  | 1374 | { | 
|  | 1375 | struct sunxi_nand_chip *chip; | 
|  | 1376 |  | 
|  | 1377 | while (!list_empty(&nfc->chips)) { | 
|  | 1378 | chip = list_first_entry(&nfc->chips, struct sunxi_nand_chip, | 
|  | 1379 | node); | 
|  | 1380 | nand_release(&chip->mtd); | 
|  | 1381 | sunxi_nand_ecc_cleanup(&chip->nand.ecc); | 
| Boris BREZILLON | 8e375cc | 2015-09-13 18:14:43 +0200 | [diff] [blame] | 1382 | list_del(&chip->node); | 
| Boris BREZILLON | 1fef62c | 2014-10-21 15:08:41 +0200 | [diff] [blame] | 1383 | } | 
|  | 1384 | } | 
|  | 1385 |  | 
|  | 1386 | static int sunxi_nfc_probe(struct platform_device *pdev) | 
|  | 1387 | { | 
|  | 1388 | struct device *dev = &pdev->dev; | 
|  | 1389 | struct resource *r; | 
|  | 1390 | struct sunxi_nfc *nfc; | 
|  | 1391 | int irq; | 
|  | 1392 | int ret; | 
|  | 1393 |  | 
|  | 1394 | nfc = devm_kzalloc(dev, sizeof(*nfc), GFP_KERNEL); | 
|  | 1395 | if (!nfc) | 
|  | 1396 | return -ENOMEM; | 
|  | 1397 |  | 
|  | 1398 | nfc->dev = dev; | 
|  | 1399 | spin_lock_init(&nfc->controller.lock); | 
|  | 1400 | init_waitqueue_head(&nfc->controller.wq); | 
|  | 1401 | INIT_LIST_HEAD(&nfc->chips); | 
|  | 1402 |  | 
|  | 1403 | r = platform_get_resource(pdev, IORESOURCE_MEM, 0); | 
|  | 1404 | nfc->regs = devm_ioremap_resource(dev, r); | 
|  | 1405 | if (IS_ERR(nfc->regs)) | 
|  | 1406 | return PTR_ERR(nfc->regs); | 
|  | 1407 |  | 
|  | 1408 | irq = platform_get_irq(pdev, 0); | 
|  | 1409 | if (irq < 0) { | 
|  | 1410 | dev_err(dev, "failed to retrieve irq\n"); | 
|  | 1411 | return irq; | 
|  | 1412 | } | 
|  | 1413 |  | 
|  | 1414 | nfc->ahb_clk = devm_clk_get(dev, "ahb"); | 
|  | 1415 | if (IS_ERR(nfc->ahb_clk)) { | 
|  | 1416 | dev_err(dev, "failed to retrieve ahb clk\n"); | 
|  | 1417 | return PTR_ERR(nfc->ahb_clk); | 
|  | 1418 | } | 
|  | 1419 |  | 
|  | 1420 | ret = clk_prepare_enable(nfc->ahb_clk); | 
|  | 1421 | if (ret) | 
|  | 1422 | return ret; | 
|  | 1423 |  | 
|  | 1424 | nfc->mod_clk = devm_clk_get(dev, "mod"); | 
|  | 1425 | if (IS_ERR(nfc->mod_clk)) { | 
|  | 1426 | dev_err(dev, "failed to retrieve mod clk\n"); | 
|  | 1427 | ret = PTR_ERR(nfc->mod_clk); | 
|  | 1428 | goto out_ahb_clk_unprepare; | 
|  | 1429 | } | 
|  | 1430 |  | 
|  | 1431 | ret = clk_prepare_enable(nfc->mod_clk); | 
|  | 1432 | if (ret) | 
|  | 1433 | goto out_ahb_clk_unprepare; | 
|  | 1434 |  | 
|  | 1435 | ret = sunxi_nfc_rst(nfc); | 
|  | 1436 | if (ret) | 
|  | 1437 | goto out_mod_clk_unprepare; | 
|  | 1438 |  | 
|  | 1439 | writel(0, nfc->regs + NFC_REG_INT); | 
|  | 1440 | ret = devm_request_irq(dev, irq, sunxi_nfc_interrupt, | 
|  | 1441 | 0, "sunxi-nand", nfc); | 
|  | 1442 | if (ret) | 
|  | 1443 | goto out_mod_clk_unprepare; | 
|  | 1444 |  | 
|  | 1445 | platform_set_drvdata(pdev, nfc); | 
|  | 1446 |  | 
| Boris BREZILLON | 1fef62c | 2014-10-21 15:08:41 +0200 | [diff] [blame] | 1447 | ret = sunxi_nand_chips_init(dev, nfc); | 
|  | 1448 | if (ret) { | 
|  | 1449 | dev_err(dev, "failed to init nand chips\n"); | 
|  | 1450 | goto out_mod_clk_unprepare; | 
|  | 1451 | } | 
|  | 1452 |  | 
|  | 1453 | return 0; | 
|  | 1454 |  | 
|  | 1455 | out_mod_clk_unprepare: | 
|  | 1456 | clk_disable_unprepare(nfc->mod_clk); | 
|  | 1457 | out_ahb_clk_unprepare: | 
|  | 1458 | clk_disable_unprepare(nfc->ahb_clk); | 
|  | 1459 |  | 
|  | 1460 | return ret; | 
|  | 1461 | } | 
|  | 1462 |  | 
|  | 1463 | static int sunxi_nfc_remove(struct platform_device *pdev) | 
|  | 1464 | { | 
|  | 1465 | struct sunxi_nfc *nfc = platform_get_drvdata(pdev); | 
|  | 1466 |  | 
|  | 1467 | sunxi_nand_chips_cleanup(nfc); | 
|  | 1468 |  | 
|  | 1469 | return 0; | 
|  | 1470 | } | 
|  | 1471 |  | 
|  | 1472 | static const struct of_device_id sunxi_nfc_ids[] = { | 
|  | 1473 | { .compatible = "allwinner,sun4i-a10-nand" }, | 
|  | 1474 | { /* sentinel */ } | 
|  | 1475 | }; | 
|  | 1476 | MODULE_DEVICE_TABLE(of, sunxi_nfc_ids); | 
|  | 1477 |  | 
|  | 1478 | static struct platform_driver sunxi_nfc_driver = { | 
|  | 1479 | .driver = { | 
|  | 1480 | .name = "sunxi_nand", | 
|  | 1481 | .of_match_table = sunxi_nfc_ids, | 
|  | 1482 | }, | 
|  | 1483 | .probe = sunxi_nfc_probe, | 
|  | 1484 | .remove = sunxi_nfc_remove, | 
|  | 1485 | }; | 
|  | 1486 | module_platform_driver(sunxi_nfc_driver); | 
|  | 1487 |  | 
|  | 1488 | MODULE_LICENSE("GPL v2"); | 
|  | 1489 | MODULE_AUTHOR("Boris BREZILLON"); | 
|  | 1490 | MODULE_DESCRIPTION("Allwinner NAND Flash Controller driver"); | 
|  | 1491 | MODULE_ALIAS("platform:sunxi_nand"); |