Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

goldfish: NAND flash driver

Fold together the NAND driver for Goldfish from Arve with cleanups by
Jun Nakajima and a tidy up to 3.7 and checkpatch.

This provides a virtual flash driver for the Goldfish Android Virtual Platform,
and which is normally used as the root file system when testing emulated
devices.

Signed-off-by: Mike A. Chan <mikechan@google.com>
Signed-off-by: Arve Hjønnevåg <arve@android.com>
[Ported to handle x86]
Signed-off-by: Sheng Yang <sheng@linux.intel.com>
Signed-off-by: Yunhong Jiang <yunhong.jiang@intel.com>
Signed-off-by: Xiaohui Xin <xiaohui.xin@intel.com>
Signed-off-by: Jun Nakajima <jun.nakajima@intel.com>
Signed-off-by: Bruce Beare <bruce.j.beare@intel.com>
[Ported to 3.4]
Signed-off-by: Tom Keel <thomas.keel@intel.com>
[Ported to 3.7 and tided for checkpatch etc]
Signed-off-by: Alan Cox <alan@linux.intel.com>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>

authored by

Arve Hjønnevåg and committed by
Greg Kroah-Hartman
8e404fff 2e82b83d

+533
+6
drivers/staging/goldfish/Kconfig
··· 4 4 ---help--- 5 5 Emulated audio channel for the Goldfish Android Virtual Device 6 6 7 + config MTD_GOLDFISH_NAND 8 + tristate "Goldfish NAND device" 9 + depends on GOLDFISH 10 + help 11 + Drives the emulated NAND flash device on the Google Goldfish 12 + Android virtual device.
+1
drivers/staging/goldfish/Makefile
··· 3 3 # 4 4 5 5 obj-$(CONFIG_GOLDFISH_AUDIO) += goldfish_audio.o 6 + obj-$(CONFIG_MTD_GOLDFISH_NAND) += goldfish_nand.o
+10
drivers/staging/goldfish/README
··· 1 + Audio 2 + ----- 1 3 - Move to using the ALSA framework not faking it 2 4 - Fix the wrong user page DMA (moving to ALSA may fix that too) 5 + 6 + NAND 7 + ---- 8 + - Switch from spinlock to mutex 9 + - Remove excess checking of parameters in calls 10 + - Use dma coherent memory not kmalloc/__pa for the memory (this is just 11 + a cleanliness issue not a correctness one) 12 +
+444
drivers/staging/goldfish/goldfish_nand.c
··· 1 + /* 2 + * drivers/mtd/devices/goldfish_nand.c 3 + * 4 + * Copyright (C) 2007 Google, Inc. 5 + * Copyright (C) 2012 Intel, Inc. 6 + * Copyright (C) 2013 Intel, Inc. 7 + * 8 + * This software is licensed under the terms of the GNU General Public 9 + * License version 2, as published by the Free Software Foundation, and 10 + * may be copied, distributed, and modified under those terms. 11 + * 12 + * This program is distributed in the hope that it will be useful, 13 + * but WITHOUT ANY WARRANTY; without even the implied warranty of 14 + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 + * GNU General Public License for more details. 16 + * 17 + */ 18 + 19 + #include <linux/io.h> 20 + #include <linux/device.h> 21 + #include <linux/module.h> 22 + #include <linux/slab.h> 23 + #include <linux/ioport.h> 24 + #include <linux/vmalloc.h> 25 + #include <linux/init.h> 26 + #include <linux/mtd/mtd.h> 27 + #include <linux/platform_device.h> 28 + 29 + #include <asm/div64.h> 30 + 31 + #include "goldfish_nand_reg.h" 32 + 33 + struct goldfish_nand { 34 + spinlock_t lock; 35 + unsigned char __iomem *base; 36 + struct cmd_params *cmd_params; 37 + size_t mtd_count; 38 + struct mtd_info mtd[0]; 39 + }; 40 + 41 + static u32 goldfish_nand_cmd_with_params(struct mtd_info *mtd, 42 + enum nand_cmd cmd, u64 addr, u32 len, 43 + void *ptr, u32 *rv) 44 + { 45 + u32 cmdp; 46 + struct goldfish_nand *nand = mtd->priv; 47 + struct cmd_params *cps = nand->cmd_params; 48 + unsigned char __iomem *base = nand->base; 49 + 50 + if (cps == NULL) 51 + return -1; 52 + 53 + switch (cmd) { 54 + case NAND_CMD_ERASE: 55 + cmdp = NAND_CMD_ERASE_WITH_PARAMS; 56 + break; 57 + case NAND_CMD_READ: 58 + cmdp = NAND_CMD_READ_WITH_PARAMS; 59 + break; 60 + case NAND_CMD_WRITE: 61 + cmdp = NAND_CMD_WRITE_WITH_PARAMS; 62 + break; 63 + default: 64 + return -1; 65 + } 66 + cps->dev = mtd - nand->mtd; 67 + cps->addr_high = (u32)(addr >> 32); 68 + cps->addr_low = (u32)addr; 69 + cps->transfer_size = len; 70 + cps->data = (u32)ptr; 71 + writel(cmdp, base + NAND_COMMAND); 72 + *rv = cps->result; 73 + return 0; 74 + } 75 + 76 + static u32 goldfish_nand_cmd(struct mtd_info *mtd, enum nand_cmd cmd, 77 + u64 addr, u32 len, void *ptr) 78 + { 79 + struct goldfish_nand *nand = mtd->priv; 80 + u32 rv; 81 + unsigned long irq_flags; 82 + unsigned char __iomem *base = nand->base; 83 + 84 + spin_lock_irqsave(&nand->lock, irq_flags); 85 + if (goldfish_nand_cmd_with_params(mtd, cmd, addr, len, ptr, &rv)) { 86 + writel(mtd - nand->mtd, base + NAND_DEV); 87 + writel((u32)(addr >> 32), base + NAND_ADDR_HIGH); 88 + writel((u32)addr, base + NAND_ADDR_LOW); 89 + writel(len, base + NAND_TRANSFER_SIZE); 90 + writel((u32)ptr, base + NAND_DATA); 91 + writel(cmd, base + NAND_COMMAND); 92 + rv = readl(base + NAND_RESULT); 93 + } 94 + spin_unlock_irqrestore(&nand->lock, irq_flags); 95 + return rv; 96 + } 97 + 98 + static int goldfish_nand_erase(struct mtd_info *mtd, struct erase_info *instr) 99 + { 100 + loff_t ofs = instr->addr; 101 + u32 len = instr->len; 102 + u32 rem; 103 + 104 + if (ofs + len > mtd->size) 105 + goto invalid_arg; 106 + rem = do_div(ofs, mtd->writesize); 107 + if (rem) 108 + goto invalid_arg; 109 + ofs *= (mtd->writesize + mtd->oobsize); 110 + 111 + if (len % mtd->writesize) 112 + goto invalid_arg; 113 + len = len / mtd->writesize * (mtd->writesize + mtd->oobsize); 114 + 115 + if (goldfish_nand_cmd(mtd, NAND_CMD_ERASE, ofs, len, NULL) != len) { 116 + pr_err("goldfish_nand_erase: erase failed, start %llx, len %x, dev_size %llx, erase_size %x\n", 117 + ofs, len, mtd->size, mtd->erasesize); 118 + return -EIO; 119 + } 120 + 121 + instr->state = MTD_ERASE_DONE; 122 + mtd_erase_callback(instr); 123 + 124 + return 0; 125 + 126 + invalid_arg: 127 + pr_err("goldfish_nand_erase: invalid erase, start %llx, len %x, dev_size %llx, erase_size %x\n", 128 + ofs, len, mtd->size, mtd->erasesize); 129 + return -EINVAL; 130 + } 131 + 132 + static int goldfish_nand_read_oob(struct mtd_info *mtd, loff_t ofs, 133 + struct mtd_oob_ops *ops) 134 + { 135 + u32 rem; 136 + 137 + if (ofs + ops->len > mtd->size) 138 + goto invalid_arg; 139 + if (ops->datbuf && ops->len && ops->len != mtd->writesize) 140 + goto invalid_arg; 141 + if (ops->ooblen + ops->ooboffs > mtd->oobsize) 142 + goto invalid_arg; 143 + 144 + rem = do_div(ofs, mtd->writesize); 145 + if (rem) 146 + goto invalid_arg; 147 + ofs *= (mtd->writesize + mtd->oobsize); 148 + 149 + if (ops->datbuf) 150 + ops->retlen = goldfish_nand_cmd(mtd, NAND_CMD_READ, ofs, 151 + ops->len, ops->datbuf); 152 + ofs += mtd->writesize + ops->ooboffs; 153 + if (ops->oobbuf) 154 + ops->oobretlen = goldfish_nand_cmd(mtd, NAND_CMD_READ, ofs, 155 + ops->ooblen, ops->oobbuf); 156 + return 0; 157 + 158 + invalid_arg: 159 + pr_err("goldfish_nand_read_oob: invalid read, start %llx, len %x, ooblen %x, dev_size %llx, write_size %x\n", 160 + ofs, ops->len, ops->ooblen, mtd->size, mtd->writesize); 161 + return -EINVAL; 162 + } 163 + 164 + static int goldfish_nand_write_oob(struct mtd_info *mtd, loff_t ofs, 165 + struct mtd_oob_ops *ops) 166 + { 167 + u32 rem; 168 + 169 + if (ofs + ops->len > mtd->size) 170 + goto invalid_arg; 171 + if (ops->len && ops->len != mtd->writesize) 172 + goto invalid_arg; 173 + if (ops->ooblen + ops->ooboffs > mtd->oobsize) 174 + goto invalid_arg; 175 + 176 + rem = do_div(ofs, mtd->writesize); 177 + if (rem) 178 + goto invalid_arg; 179 + ofs *= (mtd->writesize + mtd->oobsize); 180 + 181 + if (ops->datbuf) 182 + ops->retlen = goldfish_nand_cmd(mtd, NAND_CMD_WRITE, ofs, 183 + ops->len, ops->datbuf); 184 + ofs += mtd->writesize + ops->ooboffs; 185 + if (ops->oobbuf) 186 + ops->oobretlen = goldfish_nand_cmd(mtd, NAND_CMD_WRITE, ofs, 187 + ops->ooblen, ops->oobbuf); 188 + return 0; 189 + 190 + invalid_arg: 191 + pr_err("goldfish_nand_write_oob: invalid write, start %llx, len %x, ooblen %x, dev_size %llx, write_size %x\n", 192 + ofs, ops->len, ops->ooblen, mtd->size, mtd->writesize); 193 + return -EINVAL; 194 + } 195 + 196 + static int goldfish_nand_read(struct mtd_info *mtd, loff_t from, size_t len, 197 + size_t *retlen, u_char *buf) 198 + { 199 + u32 rem; 200 + 201 + if (from + len > mtd->size) 202 + goto invalid_arg; 203 + if (len != mtd->writesize) 204 + goto invalid_arg; 205 + 206 + rem = do_div(from, mtd->writesize); 207 + if (rem) 208 + goto invalid_arg; 209 + from *= (mtd->writesize + mtd->oobsize); 210 + 211 + *retlen = goldfish_nand_cmd(mtd, NAND_CMD_READ, from, len, buf); 212 + return 0; 213 + 214 + invalid_arg: 215 + pr_err("goldfish_nand_read: invalid read, start %llx, len %x, dev_size %llx, write_size %x\n", 216 + from, len, mtd->size, mtd->writesize); 217 + return -EINVAL; 218 + } 219 + 220 + static int goldfish_nand_write(struct mtd_info *mtd, loff_t to, size_t len, 221 + size_t *retlen, const u_char *buf) 222 + { 223 + u32 rem; 224 + 225 + if (to + len > mtd->size) 226 + goto invalid_arg; 227 + if (len != mtd->writesize) 228 + goto invalid_arg; 229 + 230 + rem = do_div(to, mtd->writesize); 231 + if (rem) 232 + goto invalid_arg; 233 + to *= (mtd->writesize + mtd->oobsize); 234 + 235 + *retlen = goldfish_nand_cmd(mtd, NAND_CMD_WRITE, to, len, (void *)buf); 236 + return 0; 237 + 238 + invalid_arg: 239 + pr_err("goldfish_nand_write: invalid write, start %llx, len %x, dev_size %llx, write_size %x\n", 240 + to, len, mtd->size, mtd->writesize); 241 + return -EINVAL; 242 + } 243 + 244 + static int goldfish_nand_block_isbad(struct mtd_info *mtd, loff_t ofs) 245 + { 246 + u32 rem; 247 + 248 + if (ofs >= mtd->size) 249 + goto invalid_arg; 250 + 251 + rem = do_div(ofs, mtd->erasesize); 252 + if (rem) 253 + goto invalid_arg; 254 + ofs *= mtd->erasesize / mtd->writesize; 255 + ofs *= (mtd->writesize + mtd->oobsize); 256 + 257 + return goldfish_nand_cmd(mtd, NAND_CMD_BLOCK_BAD_GET, ofs, 0, NULL); 258 + 259 + invalid_arg: 260 + pr_err("goldfish_nand_block_isbad: invalid arg, ofs %llx, dev_size %llx, write_size %x\n", 261 + ofs, mtd->size, mtd->writesize); 262 + return -EINVAL; 263 + } 264 + 265 + static int goldfish_nand_block_markbad(struct mtd_info *mtd, loff_t ofs) 266 + { 267 + u32 rem; 268 + 269 + if (ofs >= mtd->size) 270 + goto invalid_arg; 271 + 272 + rem = do_div(ofs, mtd->erasesize); 273 + if (rem) 274 + goto invalid_arg; 275 + ofs *= mtd->erasesize / mtd->writesize; 276 + ofs *= (mtd->writesize + mtd->oobsize); 277 + 278 + if (goldfish_nand_cmd(mtd, NAND_CMD_BLOCK_BAD_SET, ofs, 0, NULL) != 1) 279 + return -EIO; 280 + return 0; 281 + 282 + invalid_arg: 283 + pr_err("goldfish_nand_block_markbad: invalid arg, ofs %llx, dev_size %llx, write_size %x\n", 284 + ofs, mtd->size, mtd->writesize); 285 + return -EINVAL; 286 + } 287 + 288 + static int nand_setup_cmd_params(struct platform_device *pdev, 289 + struct goldfish_nand *nand) 290 + { 291 + u64 paddr; 292 + unsigned char __iomem *base = nand->base; 293 + 294 + nand->cmd_params = devm_kzalloc(&pdev->dev, 295 + sizeof(struct cmd_params), GFP_KERNEL); 296 + if (!nand->cmd_params) 297 + return -1; 298 + 299 + paddr = __pa(nand->cmd_params); 300 + writel((u32)(paddr >> 32), base + NAND_CMD_PARAMS_ADDR_HIGH); 301 + writel((u32)paddr, base + NAND_CMD_PARAMS_ADDR_LOW); 302 + return 0; 303 + } 304 + 305 + static int goldfish_nand_init_device(struct platform_device *pdev, 306 + struct goldfish_nand *nand, int id) 307 + { 308 + u32 name_len; 309 + u32 result; 310 + u32 flags; 311 + unsigned long irq_flags; 312 + unsigned char __iomem *base = nand->base; 313 + struct mtd_info *mtd = &nand->mtd[id]; 314 + char *name; 315 + 316 + spin_lock_irqsave(&nand->lock, irq_flags); 317 + writel(id, base + NAND_DEV); 318 + flags = readl(base + NAND_DEV_FLAGS); 319 + name_len = readl(base + NAND_DEV_NAME_LEN); 320 + mtd->writesize = readl(base + NAND_DEV_PAGE_SIZE); 321 + mtd->size = readl(base + NAND_DEV_SIZE_LOW); 322 + mtd->size |= (u64)readl(base + NAND_DEV_SIZE_HIGH) << 32; 323 + mtd->oobsize = readl(base + NAND_DEV_EXTRA_SIZE); 324 + mtd->oobavail = mtd->oobsize; 325 + mtd->erasesize = readl(base + NAND_DEV_ERASE_SIZE) / 326 + (mtd->writesize + mtd->oobsize) * mtd->writesize; 327 + do_div(mtd->size, mtd->writesize + mtd->oobsize); 328 + mtd->size *= mtd->writesize; 329 + dev_dbg(&pdev->dev, 330 + "goldfish nand dev%d: size %llx, page %d, extra %d, erase %d\n", 331 + id, mtd->size, mtd->writesize, mtd->oobsize, mtd->erasesize); 332 + spin_unlock_irqrestore(&nand->lock, irq_flags); 333 + 334 + mtd->priv = nand; 335 + 336 + mtd->name = name = devm_kzalloc(&pdev->dev, name_len + 1, GFP_KERNEL); 337 + if (name == NULL) 338 + return -ENOMEM; 339 + 340 + result = goldfish_nand_cmd(mtd, NAND_CMD_GET_DEV_NAME, 0, name_len, 341 + name); 342 + if (result != name_len) { 343 + dev_err(&pdev->dev, 344 + "goldfish_nand_init_device failed to get dev name %d != %d\n", 345 + result, name_len); 346 + return -ENODEV; 347 + } 348 + ((char *) mtd->name)[name_len] = '\0'; 349 + 350 + /* Setup the MTD structure */ 351 + mtd->type = MTD_NANDFLASH; 352 + mtd->flags = MTD_CAP_NANDFLASH; 353 + if (flags & NAND_DEV_FLAG_READ_ONLY) 354 + mtd->flags &= ~MTD_WRITEABLE; 355 + if (flags & NAND_DEV_FLAG_CMD_PARAMS_CAP) 356 + nand_setup_cmd_params(pdev, nand); 357 + 358 + mtd->owner = THIS_MODULE; 359 + mtd->_erase = goldfish_nand_erase; 360 + mtd->_read = goldfish_nand_read; 361 + mtd->_write = goldfish_nand_write; 362 + mtd->_read_oob = goldfish_nand_read_oob; 363 + mtd->_write_oob = goldfish_nand_write_oob; 364 + mtd->_block_isbad = goldfish_nand_block_isbad; 365 + mtd->_block_markbad = goldfish_nand_block_markbad; 366 + 367 + if (mtd_device_register(mtd, NULL, 0)) 368 + return -EIO; 369 + 370 + return 0; 371 + } 372 + 373 + static int goldfish_nand_probe(struct platform_device *pdev) 374 + { 375 + u32 num_dev; 376 + int i; 377 + int err; 378 + u32 num_dev_working; 379 + u32 version; 380 + struct resource *r; 381 + struct goldfish_nand *nand; 382 + unsigned char __iomem *base; 383 + 384 + r = platform_get_resource(pdev, IORESOURCE_MEM, 0); 385 + if (r == NULL) 386 + return -ENODEV; 387 + 388 + base = devm_ioremap(&pdev->dev, r->start, PAGE_SIZE); 389 + if (base == NULL) 390 + return -ENOMEM; 391 + 392 + version = readl(base + NAND_VERSION); 393 + if (version != NAND_VERSION_CURRENT) { 394 + dev_err(&pdev->dev, 395 + "goldfish_nand_init: version mismatch, got %d, expected %d\n", 396 + version, NAND_VERSION_CURRENT); 397 + return -ENODEV; 398 + } 399 + num_dev = readl(base + NAND_NUM_DEV); 400 + if (num_dev == 0) 401 + return -ENODEV; 402 + 403 + nand = devm_kzalloc(&pdev->dev, sizeof(*nand) + 404 + sizeof(struct mtd_info) * num_dev, GFP_KERNEL); 405 + if (nand == NULL) 406 + return -ENOMEM; 407 + 408 + spin_lock_init(&nand->lock); 409 + nand->base = base; 410 + nand->mtd_count = num_dev; 411 + platform_set_drvdata(pdev, nand); 412 + 413 + num_dev_working = 0; 414 + for (i = 0; i < num_dev; i++) { 415 + err = goldfish_nand_init_device(pdev, nand, i); 416 + if (err == 0) 417 + num_dev_working++; 418 + } 419 + if (num_dev_working == 0) 420 + return -ENODEV; 421 + return 0; 422 + } 423 + 424 + static int goldfish_nand_remove(struct platform_device *pdev) 425 + { 426 + struct goldfish_nand *nand = platform_get_drvdata(pdev); 427 + int i; 428 + for (i = 0; i < nand->mtd_count; i++) { 429 + if (nand->mtd[i].name) 430 + mtd_device_unregister(&nand->mtd[i]); 431 + } 432 + return 0; 433 + } 434 + 435 + static struct platform_driver goldfish_nand_driver = { 436 + .probe = goldfish_nand_probe, 437 + .remove = goldfish_nand_remove, 438 + .driver = { 439 + .name = "goldfish_nand" 440 + } 441 + }; 442 + 443 + module_platform_driver(goldfish_nand_driver); 444 + MODULE_LICENSE("GPL");
+72
drivers/staging/goldfish/goldfish_nand_reg.h
··· 1 + /* drivers/mtd/devices/goldfish_nand_reg.h 2 + ** 3 + ** Copyright (C) 2007 Google, Inc. 4 + ** 5 + ** This software is licensed under the terms of the GNU General Public 6 + ** License version 2, as published by the Free Software Foundation, and 7 + ** may be copied, distributed, and modified under those terms. 8 + ** 9 + ** This program is distributed in the hope that it will be useful, 10 + ** but WITHOUT ANY WARRANTY; without even the implied warranty of 11 + ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 + ** GNU General Public License for more details. 13 + ** 14 + */ 15 + 16 + #ifndef GOLDFISH_NAND_REG_H 17 + #define GOLDFISH_NAND_REG_H 18 + 19 + enum nand_cmd { 20 + NAND_CMD_GET_DEV_NAME, /* Write device name for NAND_DEV to NAND_DATA (vaddr) */ 21 + NAND_CMD_READ, 22 + NAND_CMD_WRITE, 23 + NAND_CMD_ERASE, 24 + NAND_CMD_BLOCK_BAD_GET, /* NAND_RESULT is 1 if block is bad, 0 if it is not */ 25 + NAND_CMD_BLOCK_BAD_SET, 26 + NAND_CMD_READ_WITH_PARAMS, 27 + NAND_CMD_WRITE_WITH_PARAMS, 28 + NAND_CMD_ERASE_WITH_PARAMS 29 + }; 30 + 31 + enum nand_dev_flags { 32 + NAND_DEV_FLAG_READ_ONLY = 0x00000001, 33 + NAND_DEV_FLAG_CMD_PARAMS_CAP = 0x00000002, 34 + }; 35 + 36 + #define NAND_VERSION_CURRENT (1) 37 + 38 + enum nand_reg { 39 + /* Global */ 40 + NAND_VERSION = 0x000, 41 + NAND_NUM_DEV = 0x004, 42 + NAND_DEV = 0x008, 43 + 44 + /* Dev info */ 45 + NAND_DEV_FLAGS = 0x010, 46 + NAND_DEV_NAME_LEN = 0x014, 47 + NAND_DEV_PAGE_SIZE = 0x018, 48 + NAND_DEV_EXTRA_SIZE = 0x01c, 49 + NAND_DEV_ERASE_SIZE = 0x020, 50 + NAND_DEV_SIZE_LOW = 0x028, 51 + NAND_DEV_SIZE_HIGH = 0x02c, 52 + 53 + /* Command */ 54 + NAND_RESULT = 0x040, 55 + NAND_COMMAND = 0x044, 56 + NAND_DATA = 0x048, 57 + NAND_TRANSFER_SIZE = 0x04c, 58 + NAND_ADDR_LOW = 0x050, 59 + NAND_ADDR_HIGH = 0x054, 60 + NAND_CMD_PARAMS_ADDR_LOW = 0x058, 61 + NAND_CMD_PARAMS_ADDR_HIGH = 0x05c, 62 + }; 63 + 64 + struct cmd_params { 65 + uint32_t dev; 66 + uint32_t addr_low; 67 + uint32_t addr_high; 68 + uint32_t transfer_size; 69 + uint32_t data; 70 + uint32_t result; 71 + }; 72 + #endif