1/* 2 * gpmc-nand.c 3 * 4 * Copyright (C) 2009 Texas Instruments 5 * Vimal Singh <vimalsingh@ti.com> 6 * 7 * This program is free software; you can redistribute it and/or modify 8 * it under the terms of the GNU General Public License version 2 as 9 * published by the Free Software Foundation. 10 */ 11 12#include <linux/kernel.h> 13#include <linux/platform_device.h> 14#include <linux/io.h> 15 16#include <asm/mach/flash.h> 17 18#include <plat/nand.h> 19#include <plat/board.h> 20#include <plat/gpmc.h> 21 22static struct omap_nand_platform_data *gpmc_nand_data; 23 24static struct resource gpmc_nand_resource = { 25 .flags = IORESOURCE_MEM, 26}; 27 28static struct platform_device gpmc_nand_device = { 29 .name = "omap2-nand", 30 .id = 0, 31 .num_resources = 1, 32 .resource = &gpmc_nand_resource, 33}; 34 35static int omap2_nand_gpmc_retime(void) 36{ 37 struct gpmc_timings t; 38 int err; 39 40 if (!gpmc_nand_data->gpmc_t) 41 return 0; 42 43 memset(&t, 0, sizeof(t)); 44 t.sync_clk = gpmc_round_ns_to_ticks(gpmc_nand_data->gpmc_t->sync_clk); 45 t.cs_on = gpmc_round_ns_to_ticks(gpmc_nand_data->gpmc_t->cs_on); 46 t.adv_on = gpmc_round_ns_to_ticks(gpmc_nand_data->gpmc_t->adv_on); 47 48 /* Read */ 49 t.adv_rd_off = gpmc_round_ns_to_ticks( 50 gpmc_nand_data->gpmc_t->adv_rd_off); 51 t.oe_on = t.adv_on; 52 t.access = gpmc_round_ns_to_ticks(gpmc_nand_data->gpmc_t->access); 53 t.oe_off = gpmc_round_ns_to_ticks(gpmc_nand_data->gpmc_t->oe_off); 54 t.cs_rd_off = gpmc_round_ns_to_ticks(gpmc_nand_data->gpmc_t->cs_rd_off); 55 t.rd_cycle = gpmc_round_ns_to_ticks(gpmc_nand_data->gpmc_t->rd_cycle); 56 57 /* Write */ 58 t.adv_wr_off = gpmc_round_ns_to_ticks( 59 gpmc_nand_data->gpmc_t->adv_wr_off); 60 t.we_on = t.oe_on; 61 if (cpu_is_omap34xx()) { 62 t.wr_data_mux_bus = gpmc_round_ns_to_ticks( 63 gpmc_nand_data->gpmc_t->wr_data_mux_bus); 64 t.wr_access = gpmc_round_ns_to_ticks( 65 gpmc_nand_data->gpmc_t->wr_access); 66 } 67 t.we_off = gpmc_round_ns_to_ticks(gpmc_nand_data->gpmc_t->we_off); 68 t.cs_wr_off = gpmc_round_ns_to_ticks(gpmc_nand_data->gpmc_t->cs_wr_off); 69 t.wr_cycle = gpmc_round_ns_to_ticks(gpmc_nand_data->gpmc_t->wr_cycle); 70 71 /* Configure GPMC */ 72 gpmc_cs_configure(gpmc_nand_data->cs, 73 GPMC_CONFIG_DEV_SIZE, gpmc_nand_data->devsize); 74 gpmc_cs_configure(gpmc_nand_data->cs, 75 GPMC_CONFIG_DEV_TYPE, GPMC_DEVICETYPE_NAND); 76 err = gpmc_cs_set_timings(gpmc_nand_data->cs, &t); 77 if (err) 78 return err; 79 80 return 0; 81} 82 83int __init gpmc_nand_init(struct omap_nand_platform_data *_nand_data) 84{ 85 int err = 0; 86 struct device *dev = &gpmc_nand_device.dev; 87 88 gpmc_nand_data = _nand_data; 89 gpmc_nand_data->nand_setup = omap2_nand_gpmc_retime; 90 gpmc_nand_device.dev.platform_data = gpmc_nand_data; 91 92 err = gpmc_cs_request(gpmc_nand_data->cs, NAND_IO_SIZE, 93 &gpmc_nand_data->phys_base); 94 if (err < 0) { 95 dev_err(dev, "Cannot request GPMC CS\n"); 96 return err; 97 } 98 99 /* Set timings in GPMC */ 100 err = omap2_nand_gpmc_retime(); 101 if (err < 0) { 102 dev_err(dev, "Unable to set gpmc timings: %d\n", err); 103 return err; 104 } 105 106 /* Enable RD PIN Monitoring Reg */ 107 if (gpmc_nand_data->dev_ready) { 108 gpmc_cs_configure(gpmc_nand_data->cs, GPMC_CONFIG_RDY_BSY, 1); 109 } 110 111 err = platform_device_register(&gpmc_nand_device); 112 if (err < 0) { 113 dev_err(dev, "Unable to register NAND device\n"); 114 goto out_free_cs; 115 } 116 117 return 0; 118 119out_free_cs: 120 gpmc_cs_free(gpmc_nand_data->cs); 121 122 return err; 123} 124