diff options
Diffstat (limited to 'drivers/mtd/onenand/samsung.c')
| -rw-r--r-- | drivers/mtd/onenand/samsung.c | 189 |
1 files changed, 131 insertions, 58 deletions
diff --git a/drivers/mtd/onenand/samsung.c b/drivers/mtd/onenand/samsung.c index a460f1b748c..efb819c3df2 100644 --- a/drivers/mtd/onenand/samsung.c +++ b/drivers/mtd/onenand/samsung.c @@ -22,11 +22,12 @@ #include <linux/mtd/onenand.h> #include <linux/mtd/partitions.h> #include <linux/dma-mapping.h> +#include <linux/interrupt.h> +#include <linux/io.h> #include <asm/mach/flash.h> -#include <plat/regs-onenand.h> -#include <linux/io.h> +#include "samsung.h" enum soc_type { TYPE_S3C6400, @@ -58,7 +59,7 @@ enum soc_type { #define MAP_11 (0x3) #define S3C64XX_CMD_MAP_SHIFT 24 -#define S5PC1XX_CMD_MAP_SHIFT 26 +#define S5PC100_CMD_MAP_SHIFT 26 #define S3C6400_FBA_SHIFT 10 #define S3C6400_FPA_SHIFT 4 @@ -81,6 +82,17 @@ enum soc_type { #define S5PC110_DMA_TRANS_CMD 0x418 #define S5PC110_DMA_TRANS_STATUS 0x41C #define S5PC110_DMA_TRANS_DIR 0x420 +#define S5PC110_INTC_DMA_CLR 0x1004 +#define S5PC110_INTC_ONENAND_CLR 0x1008 +#define S5PC110_INTC_DMA_MASK 0x1024 +#define S5PC110_INTC_ONENAND_MASK 0x1028 +#define S5PC110_INTC_DMA_PEND 0x1044 +#define S5PC110_INTC_ONENAND_PEND 0x1048 +#define S5PC110_INTC_DMA_STATUS 0x1064 +#define S5PC110_INTC_ONENAND_STATUS 0x1068 + +#define S5PC110_INTC_DMA_TD (1 << 24) +#define S5PC110_INTC_DMA_TE (1 << 16) #define S5PC110_DMA_CFG_SINGLE (0x0 << 16) #define S5PC110_DMA_CFG_4BURST (0x2 << 16) @@ -134,9 +146,7 @@ struct s3c_onenand { void __iomem *dma_addr; struct resource *dma_res; unsigned long phys_base; -#ifdef CONFIG_MTD_PARTITIONS - struct mtd_partition *parts; -#endif + struct completion complete; }; #define CMD_MAP_00(dev, addr) (dev->cmd_map(MAP_00, ((addr) << 1))) @@ -146,10 +156,6 @@ struct s3c_onenand { static struct s3c_onenand *onenand; -#ifdef CONFIG_MTD_PARTITIONS -static const char *part_probes[] = { "cmdlinepart", NULL, }; -#endif - static inline int s3c_read_reg(int offset) { return readl(onenand->base + offset); @@ -191,7 +197,7 @@ static unsigned int s3c64xx_cmd_map(unsigned type, unsigned val) static unsigned int s5pc1xx_cmd_map(unsigned type, unsigned val) { - return (type << S5PC1XX_CMD_MAP_SHIFT) | val; + return (type << S5PC100_CMD_MAP_SHIFT) | val; } static unsigned int s3c6400_mem_addr(int fba, int fpa, int fsa) @@ -531,10 +537,13 @@ static int onenand_write_bufferram(struct mtd_info *mtd, int area, return 0; } -static int s5pc110_dma_ops(void *dst, void *src, size_t count, int direction) +static int (*s5pc110_dma_ops)(dma_addr_t dst, dma_addr_t src, size_t count, int direction); + +static int s5pc110_dma_poll(dma_addr_t dst, dma_addr_t src, size_t count, int direction) { void __iomem *base = onenand->dma_addr; int status; + unsigned long timeout; writel(src, base + S5PC110_DMA_SRC_ADDR); writel(dst, base + S5PC110_DMA_DST_ADDR); @@ -552,6 +561,13 @@ static int s5pc110_dma_ops(void *dst, void *src, size_t count, int direction) writel(S5PC110_DMA_TRANS_CMD_TR, base + S5PC110_DMA_TRANS_CMD); + /* + * There's no exact timeout values at Spec. + * In real case it takes under 1 msec. + * So 20 msecs are enough. + */ + timeout = jiffies + msecs_to_jiffies(20); + do { status = readl(base + S5PC110_DMA_TRANS_STATUS); if (status & S5PC110_DMA_TRANS_STATUS_TE) { @@ -559,13 +575,68 @@ static int s5pc110_dma_ops(void *dst, void *src, size_t count, int direction) base + S5PC110_DMA_TRANS_CMD); return -EIO; } - } while (!(status & S5PC110_DMA_TRANS_STATUS_TD)); + } while (!(status & S5PC110_DMA_TRANS_STATUS_TD) && + time_before(jiffies, timeout)); writel(S5PC110_DMA_TRANS_CMD_TDC, base + S5PC110_DMA_TRANS_CMD); return 0; } +static irqreturn_t s5pc110_onenand_irq(int irq, void *data) +{ + void __iomem *base = onenand->dma_addr; + int status, cmd = 0; + + status = readl(base + S5PC110_INTC_DMA_STATUS); + + if (likely(status & S5PC110_INTC_DMA_TD)) + cmd = S5PC110_DMA_TRANS_CMD_TDC; + + if (unlikely(status & S5PC110_INTC_DMA_TE)) + cmd = S5PC110_DMA_TRANS_CMD_TEC; + + writel(cmd, base + S5PC110_DMA_TRANS_CMD); + writel(status, base + S5PC110_INTC_DMA_CLR); + + if (!onenand->complete.done) + complete(&onenand->complete); + + return IRQ_HANDLED; +} + +static int s5pc110_dma_irq(dma_addr_t dst, dma_addr_t src, size_t count, int direction) +{ + void __iomem *base = onenand->dma_addr; + int status; + + status = readl(base + S5PC110_INTC_DMA_MASK); + if (status) { + status &= ~(S5PC110_INTC_DMA_TD | S5PC110_INTC_DMA_TE); + writel(status, base + S5PC110_INTC_DMA_MASK); + } + + writel(src, base + S5PC110_DMA_SRC_ADDR); + writel(dst, base + S5PC110_DMA_DST_ADDR); + + if (direction == S5PC110_DMA_DIR_READ) { + writel(S5PC110_DMA_SRC_CFG_READ, base + S5PC110_DMA_SRC_CFG); + writel(S5PC110_DMA_DST_CFG_READ, base + S5PC110_DMA_DST_CFG); + } else { + writel(S5PC110_DMA_SRC_CFG_WRITE, base + S5PC110_DMA_SRC_CFG); + writel(S5PC110_DMA_DST_CFG_WRITE, base + S5PC110_DMA_DST_CFG); + } + + writel(count, base + S5PC110_DMA_TRANS_SIZE); + writel(direction, base + S5PC110_DMA_TRANS_DIR); + + writel(S5PC110_DMA_TRANS_CMD_TR, base + S5PC110_DMA_TRANS_CMD); + + wait_for_completion_timeout(&onenand->complete, msecs_to_jiffies(20)); + + return 0; +} + static int s5pc110_read_bufferram(struct mtd_info *mtd, int area, unsigned char *buffer, int offset, size_t count) { @@ -573,7 +644,8 @@ static int s5pc110_read_bufferram(struct mtd_info *mtd, int area, void __iomem *p; void *buf = (void *) buffer; dma_addr_t dma_src, dma_dst; - int err; + int err, ofs, page_dma = 0; + struct device *dev = &onenand->pdev->dev; p = this->base + area; if (ONENAND_CURRENT_BUFFERRAM(this)) { @@ -597,21 +669,30 @@ static int s5pc110_read_bufferram(struct mtd_info *mtd, int area, page = vmalloc_to_page(buf); if (!page) goto normal; - buf = page_address(page) + ((size_t) buf & ~PAGE_MASK); - } - /* DMA routine */ - dma_src = onenand->phys_base + (p - this->base); - dma_dst = dma_map_single(&onenand->pdev->dev, - buf, count, DMA_FROM_DEVICE); - if (dma_mapping_error(&onenand->pdev->dev, dma_dst)) { - dev_err(&onenand->pdev->dev, - "Couldn't map a %d byte buffer for DMA\n", count); + /* Page offset */ + ofs = ((size_t) buf & ~PAGE_MASK); + page_dma = 1; + + /* DMA routine */ + dma_src = onenand->phys_base + (p - this->base); + dma_dst = dma_map_page(dev, page, ofs, count, DMA_FROM_DEVICE); + } else { + /* DMA routine */ + dma_src = onenand->phys_base + (p - this->base); + dma_dst = dma_map_single(dev, buf, count, DMA_FROM_DEVICE); + } + if (dma_mapping_error(dev, dma_dst)) { + dev_err(dev, "Couldn't map a %d byte buffer for DMA\n", count); goto normal; } - err = s5pc110_dma_ops((void *) dma_dst, (void *) dma_src, + err = s5pc110_dma_ops(dma_dst, dma_src, count, S5PC110_DMA_DIR_READ); - dma_unmap_single(&onenand->pdev->dev, dma_dst, count, DMA_FROM_DEVICE); + + if (page_dma) + dma_unmap_page(dev, dma_dst, count, DMA_FROM_DEVICE); + else + dma_unmap_single(dev, dma_dst, count, DMA_FROM_DEVICE); if (!err) return 0; @@ -759,7 +840,6 @@ static void s3c_onenand_setup(struct mtd_info *mtd) onenand->cmd_map = s5pc1xx_cmd_map; } else if (onenand->type == TYPE_S5PC110) { /* Use generic onenand functions */ - onenand->cmd_map = s5pc1xx_cmd_map; this->read_bufferram = s5pc110_read_bufferram; this->chip_probe = s5pc110_chip_probe; return; @@ -787,15 +867,13 @@ static int s3c_onenand_probe(struct platform_device *pdev) struct resource *r; int size, err; - pdata = pdev->dev.platform_data; + pdata = dev_get_platdata(&pdev->dev); /* No need to check pdata. the platform data is optional */ size = sizeof(struct mtd_info) + sizeof(struct onenand_chip); mtd = kzalloc(size, GFP_KERNEL); - if (!mtd) { - dev_err(&pdev->dev, "failed to allocate memory\n"); + if (!mtd) return -ENOMEM; - } onenand = kzalloc(sizeof(struct s3c_onenand), GFP_KERNEL); if (!onenand) { @@ -843,7 +921,7 @@ static int s3c_onenand_probe(struct platform_device *pdev) r = platform_get_resource(pdev, IORESOURCE_MEM, 1); if (!r) { dev_err(&pdev->dev, "no buffer memory resource defined\n"); - return -ENOENT; + err = -ENOENT; goto ahb_resource_failed; } @@ -884,7 +962,7 @@ static int s3c_onenand_probe(struct platform_device *pdev) r = platform_get_resource(pdev, IORESOURCE_MEM, 1); if (!r) { dev_err(&pdev->dev, "no dma memory resource defined\n"); - return -ENOENT; + err = -ENOENT; goto dma_resource_failed; } @@ -904,6 +982,20 @@ static int s3c_onenand_probe(struct platform_device *pdev) } onenand->phys_base = onenand->base_res->start; + + s5pc110_dma_ops = s5pc110_dma_poll; + /* Interrupt support */ + r = platform_get_resource(pdev, IORESOURCE_IRQ, 0); + if (r) { + init_completion(&onenand->complete); + s5pc110_dma_ops = s5pc110_dma_irq; + err = request_irq(r->start, s5pc110_onenand_irq, + IRQF_SHARED, "onenand", &onenand); + if (err) { + dev_err(&pdev->dev, "failed to get irq\n"); + goto scan_failed; + } + } } if (onenand_scan(mtd, 1)) { @@ -920,15 +1012,9 @@ static int s3c_onenand_probe(struct platform_device *pdev) if (s3c_read_reg(MEM_CFG_OFFSET) & ONENAND_SYS_CFG1_SYNC_READ) dev_info(&onenand->pdev->dev, "OneNAND Sync. Burst Read enabled\n"); -#ifdef CONFIG_MTD_PARTITIONS - err = parse_mtd_partitions(mtd, part_probes, &onenand->parts, 0); - if (err > 0) - add_mtd_partitions(mtd, onenand->parts, err); - else if (err <= 0 && pdata && pdata->parts) - add_mtd_partitions(mtd, pdata->parts, pdata->nr_parts); - else -#endif - err = add_mtd_device(mtd); + err = mtd_device_parse_register(mtd, NULL, NULL, + pdata ? pdata->parts : NULL, + pdata ? pdata->nr_parts : 0); platform_set_drvdata(pdev, mtd); @@ -965,7 +1051,7 @@ onenand_fail: return err; } -static int __devexit s3c_onenand_remove(struct platform_device *pdev) +static int s3c_onenand_remove(struct platform_device *pdev) { struct mtd_info *mtd = platform_get_drvdata(pdev); @@ -985,7 +1071,6 @@ static int __devexit s3c_onenand_remove(struct platform_device *pdev) release_mem_region(onenand->base_res->start, resource_size(onenand->base_res)); - platform_set_drvdata(pdev, NULL); kfree(onenand->oob_buf); kfree(onenand->page_buf); kfree(onenand); @@ -1000,7 +1085,7 @@ static int s3c_pm_ops_suspend(struct device *dev) struct onenand_chip *this = mtd->priv; this->wait(mtd, FL_PM_SUSPENDED); - return mtd->suspend(mtd); + return 0; } static int s3c_pm_ops_resume(struct device *dev) @@ -1009,7 +1094,6 @@ static int s3c_pm_ops_resume(struct device *dev) struct mtd_info *mtd = platform_get_drvdata(pdev); struct onenand_chip *this = mtd->priv; - mtd->resume(mtd); this->unlock_all(mtd); return 0; } @@ -1043,21 +1127,10 @@ static struct platform_driver s3c_onenand_driver = { }, .id_table = s3c_onenand_driver_ids, .probe = s3c_onenand_probe, - .remove = __devexit_p(s3c_onenand_remove), + .remove = s3c_onenand_remove, }; -static int __init s3c_onenand_init(void) -{ - return platform_driver_register(&s3c_onenand_driver); -} - -static void __exit s3c_onenand_exit(void) -{ - platform_driver_unregister(&s3c_onenand_driver); -} - -module_init(s3c_onenand_init); -module_exit(s3c_onenand_exit); +module_platform_driver(s3c_onenand_driver); MODULE_LICENSE("GPL"); MODULE_AUTHOR("Kyungmin Park <kyungmin.park@samsung.com>"); |
