Commit 94709014 authored by Tony Lindgren's avatar Tony Lindgren

Merge tag 'gpmc-czimage' of git://gitorious.org/x0148406-public/linux-kernel...

Merge tag 'gpmc-czimage' of git://gitorious.org/x0148406-public/linux-kernel into omap-for-v3.8/cleanup-headers-gpmc

gpmc cleanup for common ARM zImage
parents ddffeb8c 3ef5d007
......@@ -34,7 +34,7 @@
#include <asm/mach/map.h>
#include "common.h"
#include <plat/gpmc.h>
#include "gpmc.h"
#include <plat/usb.h>
#include "gpmc-smc91x.h"
......
......@@ -33,7 +33,7 @@
#include <plat/usb.h>
#include "common.h"
#include <plat/dma.h>
#include <plat/gpmc.h>
#include "gpmc.h"
#include <video/omapdss.h>
#include <video/omap-panel-tfp410.h>
......
......@@ -35,7 +35,7 @@
#include <plat/led.h>
#include "common.h"
#include <plat/gpmc.h>
#include "gpmc.h"
#include <video/omapdss.h>
#include <video/omap-panel-generic-dpi.h>
......
......@@ -40,7 +40,7 @@
#include "common.h"
#include <linux/platform_data/mtd-nand-omap2.h>
#include <plat/gpmc.h>
#include "gpmc.h"
#include <plat/usb.h>
#include <video/omapdss.h>
#include <video/omap-panel-generic-dpi.h>
......@@ -53,6 +53,7 @@
#include "sdram-micron-mt46h32m32lf-6.h"
#include "hsmmc.h"
#include "common-board-devices.h"
#include "gpmc-nand.h"
#define CM_T35_GPIO_PENDOWN 57
#define SB_T35_USB_HUB_RESET_GPIO 167
......@@ -181,7 +182,7 @@ static struct omap_nand_platform_data cm_t35_nand_data = {
static void __init cm_t35_init_nand(void)
{
if (gpmc_nand_init(&cm_t35_nand_data) < 0)
if (gpmc_nand_init(&cm_t35_nand_data, NULL) < 0)
pr_err("CM-T35: Unable to register NAND device\n");
}
#else
......
......@@ -41,7 +41,7 @@
#include "common.h"
#include <plat/usb.h>
#include <linux/platform_data/mtd-nand-omap2.h>
#include <plat/gpmc.h>
#include "gpmc.h"
#include "am35xx.h"
......@@ -49,6 +49,7 @@
#include "control.h"
#include "common-board-devices.h"
#include "am35xx-emac.h"
#include "gpmc-nand.h"
#if defined(CONFIG_LEDS_GPIO) || defined(CONFIG_LEDS_GPIO_MODULE)
static struct gpio_led cm_t3517_leds[] = {
......@@ -240,7 +241,7 @@ static struct omap_nand_platform_data cm_t3517_nand_data = {
static void __init cm_t3517_init_nand(void)
{
if (gpmc_nand_init(&cm_t3517_nand_data) < 0)
if (gpmc_nand_init(&cm_t3517_nand_data, NULL) < 0)
pr_err("CM-T3517: NAND initialization failed\n");
}
#else
......
......@@ -39,7 +39,7 @@
#include <asm/mach/flash.h>
#include "common.h"
#include <plat/gpmc.h>
#include "gpmc.h"
#include <linux/platform_data/mtd-nand-omap2.h>
#include <plat/usb.h>
#include <video/omapdss.h>
......@@ -55,8 +55,11 @@
#include "sdram-micron-mt46h32m32lf-6.h"
#include "mux.h"
#include "hsmmc.h"
#include "board-flash.h"
#include "common-board-devices.h"
#define NAND_CS 0
#define OMAP_DM9000_GPIO_IRQ 25
#define OMAP3_DEVKIT_TS_GPIO 27
......@@ -621,8 +624,9 @@ static void __init devkit8000_init(void)
usb_musb_init(NULL);
usbhs_init(&usbhs_bdata);
omap_nand_flash_init(NAND_BUSWIDTH_16, devkit8000_nand_partitions,
ARRAY_SIZE(devkit8000_nand_partitions));
board_nand_init(devkit8000_nand_partitions,
ARRAY_SIZE(devkit8000_nand_partitions), NAND_CS,
NAND_BUSWIDTH_16, NULL);
omap_twl4030_audio_init("omap3beagle");
/* Ensure SDRC pins are mux'd for self-refresh */
......
......@@ -18,13 +18,15 @@
#include <linux/io.h>
#include <plat/cpu.h>
#include <plat/gpmc.h>
#include "gpmc.h"
#include <linux/platform_data/mtd-nand-omap2.h>
#include <linux/platform_data/mtd-onenand-omap2.h>
#include <plat/tc.h>
#include "common.h"
#include "board-flash.h"
#include "gpmc-onenand.h"
#include "gpmc-nand.h"
#define REG_FPGA_REV 0x10
#define REG_FPGA_DIP_SWITCH_INPUT2 0x60
......@@ -104,36 +106,35 @@ __init board_onenand_init(struct mtd_partition *onenand_parts,
defined(CONFIG_MTD_NAND_OMAP2_MODULE)
/* Note that all values in this struct are in nanoseconds */
static struct gpmc_timings nand_timings = {
struct gpmc_timings nand_default_timings[1] = {
{
.sync_clk = 0,
.sync_clk = 0,
.cs_on = 0,
.cs_rd_off = 36,
.cs_wr_off = 36,
.cs_on = 0,
.cs_rd_off = 36,
.cs_wr_off = 36,
.adv_on = 6,
.adv_rd_off = 24,
.adv_wr_off = 36,
.adv_on = 6,
.adv_rd_off = 24,
.adv_wr_off = 36,
.we_off = 30,
.oe_off = 48,
.we_off = 30,
.oe_off = 48,
.access = 54,
.rd_cycle = 72,
.wr_cycle = 72,
.access = 54,
.rd_cycle = 72,
.wr_cycle = 72,
.wr_access = 30,
.wr_data_mux_bus = 0,
.wr_access = 30,
.wr_data_mux_bus = 0,
},
};
static struct omap_nand_platform_data board_nand_data = {
.gpmc_t = &nand_timings,
};
static struct omap_nand_platform_data board_nand_data;
void
__init board_nand_init(struct mtd_partition *nand_parts,
u8 nr_parts, u8 cs, int nand_type)
__init board_nand_init(struct mtd_partition *nand_parts, u8 nr_parts, u8 cs,
int nand_type, struct gpmc_timings *gpmc_t)
{
board_nand_data.cs = cs;
board_nand_data.parts = nand_parts;
......@@ -141,7 +142,7 @@ __init board_nand_init(struct mtd_partition *nand_parts,
board_nand_data.devsize = nand_type;
board_nand_data.ecc_opt = OMAP_ECC_HAMMING_CODE_DEFAULT;
gpmc_nand_init(&board_nand_data);
gpmc_nand_init(&board_nand_data, gpmc_t);
}
#endif /* CONFIG_MTD_NAND_OMAP2 || CONFIG_MTD_NAND_OMAP2_MODULE */
......@@ -238,5 +239,6 @@ void __init board_flash_init(struct flash_partitions partition_info[],
pr_err("NAND: Unable to find configuration in GPMC\n");
else
board_nand_init(partition_info[2].parts,
partition_info[2].nr_parts, nandcs, nand_type);
partition_info[2].nr_parts, nandcs,
nand_type, nand_default_timings);
}
......@@ -12,7 +12,7 @@
*/
#include <linux/mtd/mtd.h>
#include <linux/mtd/partitions.h>
#include <plat/gpmc.h>
#include "gpmc.h"
#define PDC_NOR 1
#define PDC_NAND 2
......@@ -40,12 +40,14 @@ static inline void board_flash_init(struct flash_partitions part[],
#if defined(CONFIG_MTD_NAND_OMAP2) || \
defined(CONFIG_MTD_NAND_OMAP2_MODULE)
extern void board_nand_init(struct mtd_partition *nand_parts,
u8 nr_parts, u8 cs, int nand_type);
u8 nr_parts, u8 cs, int nand_type, struct gpmc_timings *gpmc_t);
extern struct gpmc_timings nand_default_timings[];
#else
static inline void board_nand_init(struct mtd_partition *nand_parts,
u8 nr_parts, u8 cs, int nand_type)
u8 nr_parts, u8 cs, int nand_type, struct gpmc_timings *gpmc_t)
{
}
#define nand_default_timings NULL
#endif
#if defined(CONFIG_MTD_ONENAND_OMAP2) || \
......
......@@ -33,7 +33,6 @@
#include <plat/menelaus.h>
#include <plat/dma.h>
#include <plat/gpmc.h>
#include "debug-devices.h"
#include <video/omapdss.h>
......@@ -42,6 +41,7 @@
#include "common.h"
#include "mux.h"
#include "control.h"
#include "gpmc.h"
#define H4_FLASH_CS 0
#define H4_SMC91X_CS 1
......
......@@ -30,7 +30,7 @@
#include <asm/mach/arch.h>
#include "common.h"
#include <plat/gpmc.h>
#include "gpmc.h"
#include <plat/usb.h>
#include <video/omapdss.h>
......@@ -43,6 +43,7 @@
#include "common-board-devices.h"
#include "board-flash.h"
#include "control.h"
#include "gpmc-onenand.h"
#define IGEP2_SMSC911X_CS 5
#define IGEP2_SMSC911X_GPIO 176
......@@ -175,7 +176,7 @@ static void __init igep_flash_init(void)
pr_info("IGEP: initializing NAND memory device\n");
board_nand_init(igep_flash_partitions,
ARRAY_SIZE(igep_flash_partitions),
0, NAND_BUSWIDTH_16);
0, NAND_BUSWIDTH_16, nand_default_timings);
} else if (mux == IGEP_SYSBOOT_ONENAND) {
pr_info("IGEP: initializing OneNAND memory device\n");
board_onenand_init(igep_flash_partitions,
......
......@@ -35,7 +35,7 @@
#include <asm/mach/map.h>
#include "common.h"
#include <plat/gpmc.h>
#include "gpmc.h"
#include <mach/board-zoom.h>
#include <plat/usb.h>
#include "gpmc-smsc911x.h"
......@@ -420,8 +420,8 @@ static void __init omap_ldp_init(void)
omap_serial_init();
omap_sdrc_init(NULL, NULL);
usb_musb_init(NULL);
board_nand_init(ldp_nand_partitions,
ARRAY_SIZE(ldp_nand_partitions), ZOOM_NAND_CS, 0);
board_nand_init(ldp_nand_partitions, ARRAY_SIZE(ldp_nand_partitions),
ZOOM_NAND_CS, 0, nand_default_timings);
omap_hsmmc_init(mmc);
ldp_display_init();
......
......@@ -32,6 +32,7 @@
#include <plat/mmc.h>
#include "mux.h"
#include "gpmc-onenand.h"
#define TUSB6010_ASYNC_CS 1
#define TUSB6010_SYNC_CS 4
......
......@@ -41,7 +41,7 @@
#include "common.h"
#include <video/omapdss.h>
#include <video/omap-panel-tfp410.h>
#include <plat/gpmc.h>
#include "gpmc.h"
#include <linux/platform_data/mtd-nand-omap2.h>
#include <plat/usb.h>
#include <plat/omap_device.h>
......@@ -49,8 +49,11 @@
#include "mux.h"
#include "hsmmc.h"
#include "pm.h"
#include "board-flash.h"
#include "common-board-devices.h"
#define NAND_CS 0
/*
* OMAP3 Beagle revision
* Run time detection of Beagle revision is done by reading GPIO.
......@@ -512,8 +515,9 @@ static void __init omap3_beagle_init(void)
usb_musb_init(NULL);
usbhs_init(&usbhs_bdata);
omap_nand_flash_init(NAND_BUSWIDTH_16, omap3beagle_nand_partitions,
ARRAY_SIZE(omap3beagle_nand_partitions));
board_nand_init(omap3beagle_nand_partitions,
ARRAY_SIZE(omap3beagle_nand_partitions), NAND_CS,
NAND_BUSWIDTH_16, NULL);
omap_twl4030_audio_init("omap3beagle");
/* Ensure msecure is mux'd to be able to set the RTC. */
......
......@@ -56,6 +56,9 @@
#include "sdram-micron-mt46h32m32lf-6.h"
#include "hsmmc.h"
#include "common-board-devices.h"
#include "board-flash.h"
#define NAND_CS 0
#define OMAP3_EVM_TS_GPIO 175
#define OMAP3_EVM_EHCI_VBUS 22
......@@ -731,8 +734,9 @@ static void __init omap3_evm_init(void)
}
usb_musb_init(&musb_board_data);
usbhs_init(&usbhs_bdata);
omap_nand_flash_init(NAND_BUSWIDTH_16, omap3evm_nand_partitions,
ARRAY_SIZE(omap3evm_nand_partitions));
board_nand_init(omap3evm_nand_partitions,
ARRAY_SIZE(omap3evm_nand_partitions), NAND_CS,
NAND_BUSWIDTH_16, NULL);
omap_ads7846_init(1, OMAP3_EVM_TS_GPIO, 310, NULL);
omap3evm_init_smsc911x();
......
......@@ -35,7 +35,7 @@
#include <asm/mach/map.h>
#include "gpmc-smsc911x.h"
#include <plat/gpmc.h>
#include "gpmc.h"
#include <plat/sdrc.h>
#include <plat/usb.h>
......
......@@ -50,6 +50,7 @@
#include "sdram-micron-mt46h32m32lf-6.h"
#include "hsmmc.h"
#include "common-board-devices.h"
#include "gpmc-nand.h"
#define PANDORA_WIFI_IRQ_GPIO 21
#define PANDORA_WIFI_NRESET_GPIO 23
......@@ -602,7 +603,7 @@ static void __init omap3pandora_init(void)
omap_ads7846_init(1, OMAP3_PANDORA_TS_GPIO, 0, NULL);
usbhs_init(&usbhs_bdata);
usb_musb_init(NULL);
gpmc_nand_init(&pandora_nand_data);
gpmc_nand_init(&pandora_nand_data, NULL);
/* Ensure SDRC pins are mux'd for self-refresh */
omap_mux_init_signal("sdrc_cke0", OMAP_PIN_OUTPUT);
......
......@@ -40,7 +40,7 @@
#include <asm/mach/flash.h>
#include "common.h"
#include <plat/gpmc.h>
#include "gpmc.h"
#include <linux/platform_data/mtd-nand-omap2.h>
#include <plat/usb.h>
#include <video/omapdss.h>
......
......@@ -44,12 +44,13 @@
#include <asm/system_info.h>
#include "common.h"
#include <plat/gpmc.h>
#include "gpmc.h"
#include <linux/platform_data/mtd-nand-omap2.h>
#include <plat/usb.h>
#include "mux.h"
#include "hsmmc.h"
#include "board-flash.h"
#include "common-board-devices.h"
#include <asm/setup.h>
......@@ -59,6 +60,8 @@
#define TB_BL_PWM_TIMER 9
#define TB_KILL_POWER_GPIO 168
#define NAND_CS 0
static unsigned long touchbook_revision;
static struct mtd_partition omap3touchbook_nand_partitions[] = {
......@@ -365,8 +368,9 @@ static void __init omap3_touchbook_init(void)
omap_ads7846_init(4, OMAP3_TS_GPIO, 310, &ads7846_pdata);
usb_musb_init(NULL);
usbhs_init(&usbhs_bdata);
omap_nand_flash_init(NAND_BUSWIDTH_16, omap3touchbook_nand_partitions,
ARRAY_SIZE(omap3touchbook_nand_partitions));
board_nand_init(omap3touchbook_nand_partitions,
ARRAY_SIZE(omap3touchbook_nand_partitions), NAND_CS,
NAND_BUSWIDTH_16, NULL);
/* Ensure SDRC pins are mux'd for self-refresh */
omap_mux_init_signal("sdrc_cke0", OMAP_PIN_OUTPUT);
......
......@@ -49,14 +49,17 @@
#include <video/omapdss.h>
#include <video/omap-panel-generic-dpi.h>
#include <video/omap-panel-tfp410.h>
#include <plat/gpmc.h>
#include "gpmc.h"
#include <plat/usb.h>
#include "mux.h"
#include "sdram-micron-mt46h32m32lf-6.h"
#include "hsmmc.h"
#include "board-flash.h"
#include "common-board-devices.h"
#define NAND_CS 0
#define OVERO_GPIO_BT_XGATE 15
#define OVERO_GPIO_W2W_NRESET 16
#define OVERO_GPIO_PENDOWN 114
......@@ -495,8 +498,8 @@ static void __init overo_init(void)
omap_serial_init();
omap_sdrc_init(mt46h32m32lf6_sdrc_params,
mt46h32m32lf6_sdrc_params);
omap_nand_flash_init(0, overo_nand_partitions,
ARRAY_SIZE(overo_nand_partitions));
board_nand_init(overo_nand_partitions,
ARRAY_SIZE(overo_nand_partitions), NAND_CS, 0, NULL);
usb_musb_init(NULL);
usbhs_init(&usbhs_bdata);
overo_spi_init();
......
......@@ -25,7 +25,7 @@
#include <plat/i2c.h>
#include <plat/mmc.h>
#include <plat/usb.h>
#include <plat/gpmc.h>
#include "gpmc.h"
#include "common.h"
#include <plat/serial.h>
......@@ -33,6 +33,7 @@
#include "hsmmc.h"
#include "sdram-nokia.h"
#include "common-board-devices.h"
#include "gpmc-onenand.h"
static struct regulator_consumer_supply rm680_vemmc_consumers[] = {
REGULATOR_SUPPLY("vmmc", "omap_hsmmc.1"),
......
......@@ -32,7 +32,7 @@
#include "common.h"
#include <plat/dma.h>
#include <plat/gpmc.h>
#include "gpmc.h"
#include <plat/omap-pm.h>
#include "gpmc-smc91x.h"
......@@ -54,6 +54,7 @@
#include "mux.h"
#include "hsmmc.h"
#include "common-board-devices.h"
#include "gpmc-onenand.h"
#define SYSTEM_REV_B_USES_VAUX3 0x1699
#define SYSTEM_REV_S_USES_VAUX3 0x8
......
......@@ -25,7 +25,7 @@
#include "common.h"
#include <plat/dma.h>
#include <plat/gpmc.h>
#include "gpmc.h"
#include <plat/usb.h>
#include "mux.h"
......
......@@ -17,7 +17,7 @@
#include <linux/regulator/fixed.h>
#include <linux/regulator/machine.h>
#include <plat/gpmc.h>
#include "gpmc.h"
#include "gpmc-smsc911x.h"
#include <mach/board-zoom.h>
......
......@@ -113,8 +113,9 @@ static void __init omap_zoom_init(void)
usbhs_init(&usbhs_bdata);
}
board_nand_init(zoom_nand_partitions, ARRAY_SIZE(zoom_nand_partitions),
ZOOM_NAND_CS, NAND_BUSWIDTH_16);
board_nand_init(zoom_nand_partitions,
ARRAY_SIZE(zoom_nand_partitions), ZOOM_NAND_CS,
NAND_BUSWIDTH_16, nand_default_timings);
zoom_debugboard_init();
zoom_peripherals_init();
......
......@@ -25,7 +25,6 @@
#include <linux/spi/ads7846.h>
#include <linux/platform_data/spi-omap2-mcspi.h>
#include <linux/platform_data/mtd-nand-omap2.h>
#include "common.h"
#include "common-board-devices.h"
......@@ -96,48 +95,3 @@ void __init omap_ads7846_init(int bus_num, int gpio_pendown, int gpio_debounce,
{
}
#endif
#if defined(CONFIG_MTD_NAND_OMAP2) || defined(CONFIG_MTD_NAND_OMAP2_MODULE)
static struct omap_nand_platform_data nand_data;
void __init omap_nand_flash_init(int options, struct mtd_partition *parts,
int nr_parts)
{
u8 cs = 0;
u8 nandcs = GPMC_CS_NUM + 1;
/* find out the chip-select on which NAND exists */
while (cs < GPMC_CS_NUM) {
u32 ret = 0;
ret = gpmc_cs_read_reg(cs, GPMC_CS_CONFIG1);
if ((ret & 0xC00) == 0x800) {
printk(KERN_INFO "Found NAND on CS%d\n", cs);
if (nandcs > GPMC_CS_NUM)
nandcs = cs;
}
cs++;
}
if (nandcs > GPMC_CS_NUM) {
pr_info("NAND: Unable to find configuration in GPMC\n");
return;
}
if (nandcs < GPMC_CS_NUM) {
nand_data.cs = nandcs;
nand_data.parts = parts;
nand_data.nr_parts = nr_parts;
nand_data.devsize = options;
printk(KERN_INFO "Registering NAND on CS%d\n", nandcs);
if (gpmc_nand_init(&nand_data) < 0)
printk(KERN_ERR "Unable to register NAND device\n");
}
}
#else
void __init omap_nand_flash_init(int options, struct mtd_partition *parts,
int nr_parts)
{
}
#endif
......@@ -10,6 +10,5 @@ struct ads7846_platform_data;
void omap_ads7846_init(int bus_num, int gpio_pendown, int gpio_debounce,
struct ads7846_platform_data *board_pdata);
void omap_nand_flash_init(int opts, struct mtd_partition *parts, int n_parts);
#endif /* __OMAP_COMMON_BOARD_DEVICES__ */
......@@ -17,9 +17,12 @@
#include <asm/mach/flash.h>
#include <plat/gpmc.h>
#include "gpmc.h"
#include "soc.h"
#include "gpmc-nand.h"
/* minimum size for IO mapping */
#define NAND_IO_SIZE 4
static struct resource gpmc_nand_resource[] = {
{
......@@ -40,41 +43,36 @@ static struct platform_device gpmc_nand_device = {
.resource = gpmc_nand_resource,
};
static int omap2_nand_gpmc_retime(struct omap_nand_platform_data *gpmc_nand_data)
static int omap2_nand_gpmc_retime(
struct omap_nand_platform_data *gpmc_nand_data,
struct gpmc_timings *gpmc_t)
{
struct gpmc_timings t;
int err;
if (!gpmc_nand_data->gpmc_t)
return 0;
memset(&t, 0, sizeof(t));
t.sync_clk = gpmc_nand_data->gpmc_t->sync_clk;
t.cs_on = gpmc_round_ns_to_ticks(gpmc_nand_data->gpmc_t->cs_on);
t.adv_on = gpmc_round_ns_to_ticks(gpmc_nand_data->gpmc_t->adv_on);
t.sync_clk = gpmc_t->sync_clk;
t.cs_on = gpmc_round_ns_to_ticks(gpmc_t->cs_on);
t.adv_on = gpmc_round_ns_to_ticks(gpmc_t->adv_on);
/* Read */
t.adv_rd_off = gpmc_round_ns_to_ticks(
gpmc_nand_data->gpmc_t->adv_rd_off);
t.adv_rd_off = gpmc_round_ns_to_ticks(gpmc_t->adv_rd_off);
t.oe_on = t.adv_on;
t.access = gpmc_round_ns_to_ticks(gpmc_nand_data->gpmc_t->access);
t.oe_off = gpmc_round_ns_to_ticks(gpmc_nand_data->gpmc_t->oe_off);
t.cs_rd_off = gpmc_round_ns_to_ticks(gpmc_nand_data->gpmc_t->cs_rd_off);
t.rd_cycle = gpmc_round_ns_to_ticks(gpmc_nand_data->gpmc_t->rd_cycle);
t.access = gpmc_round_ns_to_ticks(gpmc_t->access);
t.oe_off = gpmc_round_ns_to_ticks(gpmc_t->oe_off);
t.cs_rd_off = gpmc_round_ns_to_ticks(gpmc_t->cs_rd_off);
t.rd_cycle = gpmc_round_ns_to_ticks(gpmc_t->rd_cycle);
/* Write */
t.adv_wr_off = gpmc_round_ns_to_ticks(
gpmc_nand_data->gpmc_t->adv_wr_off);
t.adv_wr_off = gpmc_round_ns_to_ticks(gpmc_t->adv_wr_off);
t.we_on = t.oe_on;
if (cpu_is_omap34xx()) {
t.wr_data_mux_bus = gpmc_round_ns_to_ticks(
gpmc_nand_data->gpmc_t->wr_data_mux_bus);
t.wr_access = gpmc_round_ns_to_ticks(
gpmc_nand_data->gpmc_t->wr_access);
t.wr_data_mux_bus = gpmc_round_ns_to_ticks(gpmc_t->wr_data_mux_bus);
t.wr_access = gpmc_round_ns_to_ticks(gpmc_t->wr_access);
}
t.we_off = gpmc_round_ns_to_ticks(gpmc_nand_data->gpmc_t->we_off);
t.cs_wr_off = gpmc_round_ns_to_ticks(gpmc_nand_data->gpmc_t->cs_wr_off);
t.wr_cycle = gpmc_round_ns_to_ticks(gpmc_nand_data->gpmc_t->wr_cycle);
t.we_off = gpmc_round_ns_to_ticks(gpmc_t->we_off);
t.cs_wr_off = gpmc_round_ns_to_ticks(gpmc_t->cs_wr_off);
t.wr_cycle = gpmc_round_ns_to_ticks(gpmc_t->wr_cycle);
/* Configure GPMC */
if (gpmc_nand_data->devsize == NAND_BUSWIDTH_16)
......@@ -91,7 +89,29 @@ static int omap2_nand_gpmc_retime(struct omap_nand_platform_data *gpmc_nand_data
return 0;
}
int __init gpmc_nand_init(struct omap_nand_platform_data *gpmc_nand_data)
static bool __init gpmc_hwecc_bch_capable(enum omap_ecc ecc_opt)
{
/* support only OMAP3 class */
if (!cpu_is_omap34xx()) {
pr_err("BCH ecc is not supported on this CPU\n");
return 0;
}
/*
* For now, assume 4-bit mode is only supported on OMAP3630 ES1.x, x>=1.
* Other chips may be added if confirmed to work.
*/
if ((ecc_opt == OMAP_ECC_BCH4_CODE_HW) &&
(!cpu_is_omap3630() || (GET_OMAP_REVISION() == 0))) {
pr_err("BCH 4-bit mode is not supported on this CPU\n");
return 0;
}
return 1;
}
int __init gpmc_nand_init(struct omap_nand_platform_data *gpmc_nand_data,
struct gpmc_timings *gpmc_t)
{
int err = 0;
struct device *dev = &gpmc_nand_device.dev;
......@@ -112,11 +132,13 @@ int __init gpmc_nand_init(struct omap_nand_platform_data *gpmc_nand_data)
gpmc_get_client_irq(GPMC_IRQ_FIFOEVENTENABLE);
gpmc_nand_resource[2].start =
gpmc_get_client_irq(GPMC_IRQ_COUNT_EVENT);
/* Set timings in GPMC */
err = omap2_nand_gpmc_retime(gpmc_nand_data);
if (err < 0) {
dev_err(dev, "Unable to set gpmc timings: %d\n", err);
return err;
if (gpmc_t) {
err = omap2_nand_gpmc_retime(gpmc_nand_data, gpmc_t);
if (err < 0) {
dev_err(dev, "Unable to set gpmc timings: %d\n", err);
return err;
}
}
/* Enable RD PIN Monitoring Reg */
......@@ -126,6 +148,9 @@ int __init gpmc_nand_init(struct omap_nand_platform_data *gpmc_nand_data)
gpmc_update_nand_reg(&gpmc_nand_data->reg, gpmc_nand_data->cs);
if (!gpmc_hwecc_bch_capable(gpmc_nand_data->ecc_opt))
return -EINVAL;
err = platform_device_register(&gpmc_nand_device);
if (err < 0) {
dev_err(dev, "Unable to register NAND device\n");
......
/*
* arch/arm/mach-omap2/gpmc-nand.h
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the
* Free Software Foundation; either version 2 of the License, or (at your
* option) any later version.
*/
#ifndef __OMAP2_GPMC_NAND_H
#define __OMAP2_GPMC_NAND_H
#include "gpmc.h"
#include <linux/platform_data/mtd-nand-omap2.h>
#if IS_ENABLED(CONFIG_MTD_NAND_OMAP2)
extern int gpmc_nand_init(struct omap_nand_platform_data *d,
struct gpmc_timings *gpmc_t);
#else
static inline int gpmc_nand_init(struct omap_nand_platform_data *d,
struct gpmc_timings *gpmc_t)
{
return 0;
}
#endif
#endif
......@@ -16,15 +16,25 @@
#include <linux/mtd/onenand_regs.h>
#include <linux/io.h>
#include <linux/platform_data/mtd-onenand-omap2.h>
#include <linux/err.h>
#include <asm/mach/flash.h>
#include <plat/gpmc.h>
#include "gpmc.h"
#include "soc.h"
#include "gpmc-onenand.h"
#define ONENAND_IO_SIZE SZ_128K
#define ONENAND_FLAG_SYNCREAD (1 << 0)
#define ONENAND_FLAG_SYNCWRITE (1 << 1)
#define ONENAND_FLAG_HF (1 << 2)
#define ONENAND_FLAG_VHF (1 << 3)
static unsigned onenand_flags;
static unsigned latency;
static int fclk_offset;
static struct omap_onenand_platform_data *gpmc_onenand_data;
static struct resource gpmc_onenand_resource = {
......@@ -38,11 +48,9 @@ static struct platform_device gpmc_onenand_device = {
.resource = &gpmc_onenand_resource,
};
static int omap2_onenand_set_async_mode(int cs, void __iomem *onenand_base)
static struct gpmc_timings omap2_onenand_calc_async_timings(void)
{
struct gpmc_timings t;
u32 reg;
int err;
const int t_cer = 15;
const int t_avdp = 12;
......@@ -55,11 +63,6 @@ static int omap2_onenand_set_async_mode(int cs, void __iomem *onenand_base)
const int t_wpl = 40;
const int t_wph = 30;
/* Ensure sync read and sync write are disabled */
reg = readw(onenand_base + ONENAND_REG_SYS_CFG1);
reg &= ~ONENAND_SYS_CFG1_SYNC_READ & ~ONENAND_SYS_CFG1_SYNC_WRITE;
writew(reg, onenand_base + ONENAND_REG_SYS_CFG1);
memset(&t, 0, sizeof(t));
t.sync_clk = 0;
t.cs_on = 0;
......@@ -86,25 +89,30 @@ static int omap2_onenand_set_async_mode(int cs, void __iomem *onenand_base)
t.cs_wr_off = t.we_off + gpmc_round_ns_to_ticks(t_wph);
t.wr_cycle = t.cs_wr_off + gpmc_round_ns_to_ticks(t_cez);
return t;
}
static int gpmc_set_async_mode(int cs, struct gpmc_timings *t)
{
/* Configure GPMC for asynchronous read */
gpmc_cs_write_reg(cs, GPMC_CS_CONFIG1,
GPMC_CONFIG1_DEVICESIZE_16 |
GPMC_CONFIG1_MUXADDDATA);
err = gpmc_cs_set_timings(cs, &t);
if (err)
return err;
return gpmc_cs_set_timings(cs, t);
}
static void omap2_onenand_set_async_mode(void __iomem *onenand_base)
{
u32 reg;
/* Ensure sync read and sync write are disabled */
reg = readw(onenand_base + ONENAND_REG_SYS_CFG1);
reg &= ~ONENAND_SYS_CFG1_SYNC_READ & ~ONENAND_SYS_CFG1_SYNC_WRITE;
writew(reg, onenand_base + ONENAND_REG_SYS_CFG1);
return 0;
}
static void set_onenand_cfg(void __iomem *onenand_base, int latency,
int sync_read, int sync_write, int hf, int vhf)
static void set_onenand_cfg(void __iomem *onenand_base)
{
u32 reg;
......@@ -112,19 +120,19 @@ static void set_onenand_cfg(void __iomem *onenand_base, int latency,
reg &= ~((0x7 << ONENAND_SYS_CFG1_BRL_SHIFT) | (0x7 << 9));
reg |= (latency << ONENAND_SYS_CFG1_BRL_SHIFT) |
ONENAND_SYS_CFG1_BL_16;
if (sync_read)
if (onenand_flags & ONENAND_FLAG_SYNCREAD)
reg |= ONENAND_SYS_CFG1_SYNC_READ;
else
reg &= ~ONENAND_SYS_CFG1_SYNC_READ;
if (sync_write)
if (onenand_flags & ONENAND_FLAG_SYNCWRITE)
reg |= ONENAND_SYS_CFG1_SYNC_WRITE;
else
reg &= ~ONENAND_SYS_CFG1_SYNC_WRITE;
if (hf)
if (onenand_flags & ONENAND_FLAG_HF)
reg |= ONENAND_SYS_CFG1_HF;
else
reg &= ~ONENAND_SYS_CFG1_HF;
if (vhf)
if (onenand_flags & ONENAND_FLAG_VHF)
reg |= ONENAND_SYS_CFG1_VHF;
else
reg &= ~ONENAND_SYS_CFG1_VHF;
......@@ -132,21 +140,10 @@ static void set_onenand_cfg(void __iomem *onenand_base, int latency,
}
static int omap2_onenand_get_freq(struct omap_onenand_platform_data *cfg,
void __iomem *onenand_base, bool *clk_dep)
void __iomem *onenand_base)
{
u16 ver = readw(onenand_base + ONENAND_REG_VERSION_ID);
int freq = 0;
if (cfg->get_freq) {
struct onenand_freq_info fi;
fi.maf_id = readw(onenand_base + ONENAND_REG_MANUFACTURER_ID);
fi.dev_id = readw(onenand_base + ONENAND_REG_DEVICE_ID);
fi.ver_id = ver;
freq = cfg->get_freq(&fi, clk_dep);
if (freq)
return freq;
}
int freq;
switch ((ver >> 4) & 0xf) {
case 0:
......@@ -172,9 +169,9 @@ static int omap2_onenand_get_freq(struct omap_onenand_platform_data *cfg,
return freq;
}
static int omap2_onenand_set_sync_mode(struct omap_onenand_platform_data *cfg,
void __iomem *onenand_base,
int *freq_ptr)
static struct gpmc_timings
omap2_onenand_calc_sync_timings(struct omap_onenand_platform_data *cfg,
int freq)
{
struct gpmc_timings t;
const int t_cer = 15;
......@@ -184,29 +181,15 @@ static int omap2_onenand_set_sync_mode(struct omap_onenand_platform_data *cfg,
const int t_wpl = 40;
const int t_wph = 30;
int min_gpmc_clk_period, t_ces, t_avds, t_avdh, t_ach, t_aavdh, t_rdyo;
int div, fclk_offset_ns, fclk_offset, gpmc_clk_ns, latency;
int first_time = 0, hf = 0, vhf = 0, sync_read = 0, sync_write = 0;
int err, ticks_cez;
int cs = cfg->cs, freq = *freq_ptr;
u32 reg;
bool clk_dep = false;
int div, fclk_offset_ns, gpmc_clk_ns;
int ticks_cez;
int cs = cfg->cs;
if (cfg->flags & ONENAND_SYNC_READ) {
sync_read = 1;
} else if (cfg->flags & ONENAND_SYNC_READWRITE) {
sync_read = 1;
sync_write = 1;
} else
return omap2_onenand_set_async_mode(cs, onenand_base);
if (!freq) {
/* Very first call freq is not known */
err = omap2_onenand_set_async_mode(cs, onenand_base);
if (err)
return err;
freq = omap2_onenand_get_freq(cfg, onenand_base, &clk_dep);
first_time = 1;
}
if (cfg->flags & ONENAND_SYNC_READ)
onenand_flags = ONENAND_FLAG_SYNCREAD;
else if (cfg->flags & ONENAND_SYNC_READWRITE)
onenand_flags = ONENAND_FLAG_SYNCREAD | ONENAND_FLAG_SYNCWRITE;
switch (freq) {
case 104:
......@@ -244,44 +227,31 @@ static int omap2_onenand_set_sync_mode(struct omap_onenand_platform_data *cfg,
t_ach = 9;
t_aavdh = 7;
t_rdyo = 15;
sync_write = 0;
onenand_flags &= ~ONENAND_FLAG_SYNCWRITE;
break;
}
div = gpmc_cs_calc_divider(cs, min_gpmc_clk_period);
div = gpmc_calc_divider(min_gpmc_clk_period);
gpmc_clk_ns = gpmc_ticks_to_ns(div);
if (gpmc_clk_ns < 15) /* >66Mhz */
hf = 1;
onenand_flags |= ONENAND_FLAG_HF;
else
onenand_flags &= ~ONENAND_FLAG_HF;
if (gpmc_clk_ns < 12) /* >83Mhz */
vhf = 1;
if (vhf)
onenand_flags |= ONENAND_FLAG_VHF;
else
onenand_flags &= ~ONENAND_FLAG_VHF;
if (onenand_flags & ONENAND_FLAG_VHF)
latency = 8;
else if (hf)
else if (onenand_flags & ONENAND_FLAG_HF)
latency = 6;
else if (gpmc_clk_ns >= 25) /* 40 MHz*/
latency = 3;
else
latency = 4;
if (clk_dep) {
if (gpmc_clk_ns < 12) { /* >83Mhz */
t_ces = 3;
t_avds = 4;
} else if (gpmc_clk_ns < 15) { /* >66Mhz */
t_ces = 5;
t_avds = 4;
} else if (gpmc_clk_ns < 25) { /* >40Mhz */
t_ces = 6;
t_avds = 5;
} else {
t_ces = 7;
t_avds = 7;
}
}
if (first_time)
set_onenand_cfg(onenand_base, latency,
sync_read, sync_write, hf, vhf);
/* Set synchronous read timings */
memset(&t, 0, sizeof(t));
if (div == 1) {
reg = gpmc_cs_read_reg(cs, GPMC_CS_CONFIG2);
......@@ -307,8 +277,6 @@ static int omap2_onenand_set_sync_mode(struct omap_onenand_platform_data *cfg,
gpmc_cs_write_reg(cs, GPMC_CS_CONFIG4, reg);
}
/* Set synchronous read timings */
memset(&t, 0, sizeof(t));
t.sync_clk = min_gpmc_clk_period;
t.cs_on = 0;
t.adv_on = 0;
......@@ -330,7 +298,7 @@ static int omap2_onenand_set_sync_mode(struct omap_onenand_platform_data *cfg,
ticks_cez);
/* Write */
if (sync_write) {
if (onenand_flags & ONENAND_FLAG_SYNCWRITE) {
t.adv_wr_off = t.adv_rd_off;
t.we_on = 0;
t.we_off = t.cs_rd_off;
......@@ -355,6 +323,14 @@ static int omap2_onenand_set_sync_mode(struct omap_onenand_platform_data *cfg,
}
}
return t;
}
static int gpmc_set_sync_mode(int cs, struct gpmc_timings *t)
{
unsigned sync_read = onenand_flags & ONENAND_FLAG_SYNCREAD;
unsigned sync_write = onenand_flags & ONENAND_FLAG_SYNCWRITE;
/* Configure GPMC for synchronous read */
gpmc_cs_write_reg(cs, GPMC_CS_CONFIG1,
GPMC_CONFIG1_WRAPBURST_SUPP |
......@@ -371,11 +347,45 @@ static int omap2_onenand_set_sync_mode(struct omap_onenand_platform_data *cfg,
GPMC_CONFIG1_DEVICETYPE_NOR |
GPMC_CONFIG1_MUXADDDATA);
err = gpmc_cs_set_timings(cs, &t);
if (err)
return err;
return gpmc_cs_set_timings(cs, t);
}
static int omap2_onenand_setup_async(void __iomem *onenand_base)
{
struct gpmc_timings t;
int ret;
omap2_onenand_set_async_mode(onenand_base);
set_onenand_cfg(onenand_base, latency, sync_read, sync_write, hf, vhf);
t = omap2_onenand_calc_async_timings();
ret = gpmc_set_async_mode(gpmc_onenand_data->cs, &t);
if (IS_ERR_VALUE(ret))
return ret;
omap2_onenand_set_async_mode(onenand_base);
return 0;
}
static int omap2_onenand_setup_sync(void __iomem *onenand_base, int *freq_ptr)
{
int ret, freq = *freq_ptr;
struct gpmc_timings t;
if (!freq) {
/* Very first call freq is not known */
freq = omap2_onenand_get_freq(gpmc_onenand_data, onenand_base);
set_onenand_cfg(onenand_base);
}
t = omap2_onenand_calc_sync_timings(gpmc_onenand_data, freq);
ret = gpmc_set_sync_mode(gpmc_onenand_data->cs, &t);
if (IS_ERR_VALUE(ret))
return ret;
set_onenand_cfg(onenand_base);
*freq_ptr = freq;
......@@ -385,15 +395,22 @@ static int omap2_onenand_set_sync_mode(struct omap_onenand_platform_data *cfg,
static int gpmc_onenand_setup(void __iomem *onenand_base, int *freq_ptr)
{
struct device *dev = &gpmc_onenand_device.dev;
unsigned l = ONENAND_SYNC_READ | ONENAND_SYNC_READWRITE;
int ret;
/* Set sync timings in GPMC */
if (omap2_onenand_set_sync_mode(gpmc_onenand_data, onenand_base,
freq_ptr) < 0) {
dev_err(dev, "Unable to set synchronous mode\n");
return -EINVAL;
ret = omap2_onenand_setup_async(onenand_base);
if (ret) {
dev_err(dev, "unable to set to async mode\n");
return ret;
}
return 0;
if (!(gpmc_onenand_data->flags & l))
return 0;
ret = omap2_onenand_setup_sync(onenand_base, freq_ptr);
if (ret)
dev_err(dev, "unable to set to sync mode\n");
return ret;
}
void __init gpmc_onenand_init(struct omap_onenand_platform_data *_onenand_data)
......@@ -411,6 +428,11 @@ void __init gpmc_onenand_init(struct omap_onenand_platform_data *_onenand_data)
gpmc_onenand_data->flags |= ONENAND_SYNC_READ;
}
if (cpu_is_omap34xx())
gpmc_onenand_data->flags |= ONENAND_IN_OMAP34XX;
else
gpmc_onenand_data->flags &= ~ONENAND_IN_OMAP34XX;
err = gpmc_cs_request(gpmc_onenand_data->cs, ONENAND_IO_SIZE,
(unsigned long *)&gpmc_onenand_resource.start);
if (err < 0) {
......
/*
* arch/arm/mach-omap2/gpmc-onenand.h
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the
* Free Software Foundation; either version 2 of the License, or (at your
* option) any later version.
*/
#ifndef __OMAP2_GPMC_ONENAND_H
#define __OMAP2_GPMC_ONENAND_H
#include <linux/platform_data/mtd-onenand-omap2.h>
#if IS_ENABLED(CONFIG_MTD_ONENAND_OMAP2)
extern void gpmc_onenand_init(struct omap_onenand_platform_data *d);
#else
#define board_onenand_data NULL
static inline void gpmc_onenand_init(struct omap_onenand_platform_data *d)
{
}
#endif
#endif
......@@ -17,7 +17,7 @@
#include <linux/io.h>
#include <linux/smc91x.h>
#include <plat/gpmc.h>
#include "gpmc.h"
#include "gpmc-smc91x.h"
#include "soc.h"
......
......@@ -20,7 +20,7 @@
#include <linux/io.h>
#include <linux/smsc911x.h>
#include <plat/gpmc.h>
#include "gpmc.h"
#include "gpmc-smsc911x.h"
static struct resource gpmc_smsc911x_resources[] = {
......
......@@ -26,16 +26,17 @@
#include <linux/interrupt.h>
#include <linux/platform_device.h>
#include <linux/platform_data/mtd-nand-omap2.h>
#include <asm/mach-types.h>
#include <plat/gpmc.h>
#include <plat/cpu.h>
#include <plat/gpmc.h>
#include <plat/sdrc.h>
#include <plat/omap_device.h>
#include "soc.h"
#include "common.h"
#include "gpmc.h"
#define DEVICE_NAME "omap-gpmc"
......@@ -59,6 +60,9 @@
#define GPMC_ECC_SIZE_CONFIG 0x1fc
#define GPMC_ECC1_RESULT 0x200
#define GPMC_ECC_BCH_RESULT_0 0x240 /* not available on OMAP2 */
#define GPMC_ECC_BCH_RESULT_1 0x244 /* not available on OMAP2 */
#define GPMC_ECC_BCH_RESULT_2 0x248 /* not available on OMAP2 */
#define GPMC_ECC_BCH_RESULT_3 0x24c /* not available on OMAP2 */
/* GPMC ECC control settings */
#define GPMC_ECC_CTRL_ECCCLEAR 0x100
......@@ -75,6 +79,7 @@
#define GPMC_CS0_OFFSET 0x60
#define GPMC_CS_SIZE 0x30
#define GPMC_BCH_SIZE 0x10
#define GPMC_MEM_START 0x00000000
#define GPMC_MEM_END 0x3FFFFFFF
......@@ -137,7 +142,6 @@ static struct resource gpmc_mem_root;
static struct resource gpmc_cs_mem[GPMC_CS_NUM];
static DEFINE_SPINLOCK(gpmc_mem_lock);
static unsigned int gpmc_cs_map; /* flag for cs which are initialized */
static int gpmc_ecc_used = -EINVAL; /* cs using ecc engine */
static struct device *gpmc_dev;
static int gpmc_irq;
static resource_size_t phys_base, mem_size;
......@@ -158,22 +162,6 @@ static u32 gpmc_read_reg(int idx)
return __raw_readl(gpmc_base + idx);
}
static void gpmc_cs_write_byte(int cs, int idx, u8 val)
{
void __iomem *reg_addr;
reg_addr = gpmc_base + GPMC_CS0_OFFSET + (cs * GPMC_CS_SIZE) + idx;
__raw_writeb(val, reg_addr);
}
static u8 gpmc_cs_read_byte(int cs, int idx)
{
void __iomem *reg_addr;
reg_addr = gpmc_base + GPMC_CS0_OFFSET + (cs * GPMC_CS_SIZE) + idx;
return __raw_readb(reg_addr);
}
void gpmc_cs_write_reg(int cs, int idx, u32 val)
{
void __iomem *reg_addr;
......@@ -288,7 +276,7 @@ static int set_gpmc_timing_reg(int cs, int reg, int st_bit, int end_bit,
return -1
#endif
int gpmc_cs_calc_divider(int cs, unsigned int sync_clk)
int gpmc_calc_divider(unsigned int sync_clk)
{
int div;
u32 l;
......@@ -308,7 +296,7 @@ int gpmc_cs_set_timings(int cs, const struct gpmc_timings *t)
int div;
u32 l;
div = gpmc_cs_calc_divider(cs, t->sync_clk);
div = gpmc_calc_divider(t->sync_clk);
if (div < 0)
return div;
......@@ -508,44 +496,6 @@ void gpmc_cs_free(int cs)
}
EXPORT_SYMBOL(gpmc_cs_free);
/**
* gpmc_read_status - read access request to get the different gpmc status
* @cmd: command type
* @return status
*/
int gpmc_read_status(int cmd)
{
int status = -EINVAL;
u32 regval = 0;
switch (cmd) {
case GPMC_GET_IRQ_STATUS:
status = gpmc_read_reg(GPMC_IRQSTATUS);
break;
case GPMC_PREFETCH_FIFO_CNT:
regval = gpmc_read_reg(GPMC_PREFETCH_STATUS);
status = GPMC_PREFETCH_STATUS_FIFO_CNT(regval);
break;
case GPMC_PREFETCH_COUNT:
regval = gpmc_read_reg(GPMC_PREFETCH_STATUS);
status = GPMC_PREFETCH_STATUS_COUNT(regval);
break;
case GPMC_STATUS_BUFFER:
regval = gpmc_read_reg(GPMC_STATUS);
/* 1 : buffer is available to write */
status = regval & GPMC_STATUS_BUFF_EMPTY;
break;
default:
printk(KERN_ERR "gpmc_read_status: Not supported\n");
}
return status;
}
EXPORT_SYMBOL(gpmc_read_status);
/**
* gpmc_cs_configure - write request to configure gpmc
* @cs: chip select number
......@@ -614,121 +564,10 @@ int gpmc_cs_configure(int cs, int cmd, int wval)
}
EXPORT_SYMBOL(gpmc_cs_configure);
/**
* gpmc_nand_read - nand specific read access request
* @cs: chip select number
* @cmd: command type
*/
int gpmc_nand_read(int cs, int cmd)
{
int rval = -EINVAL;
switch (cmd) {
case GPMC_NAND_DATA:
rval = gpmc_cs_read_byte(cs, GPMC_CS_NAND_DATA);
break;
default:
printk(KERN_ERR "gpmc_read_nand_ctrl: Not supported\n");
}
return rval;
}
EXPORT_SYMBOL(gpmc_nand_read);
/**
* gpmc_nand_write - nand specific write request
* @cs: chip select number
* @cmd: command type
* @wval: value to write
*/
int gpmc_nand_write(int cs, int cmd, int wval)
{
int err = 0;
switch (cmd) {
case GPMC_NAND_COMMAND:
gpmc_cs_write_byte(cs, GPMC_CS_NAND_COMMAND, wval);
break;
case GPMC_NAND_ADDRESS:
gpmc_cs_write_byte(cs, GPMC_CS_NAND_ADDRESS, wval);
break;
case GPMC_NAND_DATA:
gpmc_cs_write_byte(cs, GPMC_CS_NAND_DATA, wval);
default:
printk(KERN_ERR "gpmc_write_nand_ctrl: Not supported\n");
err = -EINVAL;
}
return err;
}
EXPORT_SYMBOL(gpmc_nand_write);
/**
* gpmc_prefetch_enable - configures and starts prefetch transfer
* @cs: cs (chip select) number
* @fifo_th: fifo threshold to be used for read/ write
* @dma_mode: dma mode enable (1) or disable (0)
* @u32_count: number of bytes to be transferred
* @is_write: prefetch read(0) or write post(1) mode
*/
int gpmc_prefetch_enable(int cs, int fifo_th, int dma_mode,
unsigned int u32_count, int is_write)
{
if (fifo_th > PREFETCH_FIFOTHRESHOLD_MAX) {
pr_err("gpmc: fifo threshold is not supported\n");
return -1;
} else if (!(gpmc_read_reg(GPMC_PREFETCH_CONTROL))) {
/* Set the amount of bytes to be prefetched */
gpmc_write_reg(GPMC_PREFETCH_CONFIG2, u32_count);
/* Set dma/mpu mode, the prefetch read / post write and
* enable the engine. Set which cs is has requested for.
*/
gpmc_write_reg(GPMC_PREFETCH_CONFIG1, ((cs << CS_NUM_SHIFT) |
PREFETCH_FIFOTHRESHOLD(fifo_th) |
ENABLE_PREFETCH |
(dma_mode << DMA_MPU_MODE) |
(0x1 & is_write)));
/* Start the prefetch engine */
gpmc_write_reg(GPMC_PREFETCH_CONTROL, 0x1);
} else {
return -EBUSY;
}
return 0;
}
EXPORT_SYMBOL(gpmc_prefetch_enable);
/**
* gpmc_prefetch_reset - disables and stops the prefetch engine
*/
int gpmc_prefetch_reset(int cs)
{
u32 config1;
/* check if the same module/cs is trying to reset */
config1 = gpmc_read_reg(GPMC_PREFETCH_CONFIG1);
if (((config1 >> CS_NUM_SHIFT) & 0x7) != cs)
return -EINVAL;
/* Stop the PFPW engine */
gpmc_write_reg(GPMC_PREFETCH_CONTROL, 0x0);
/* Reset/disable the PFPW engine */
gpmc_write_reg(GPMC_PREFETCH_CONFIG1, 0x0);
return 0;
}
EXPORT_SYMBOL(gpmc_prefetch_reset);
void gpmc_update_nand_reg(struct gpmc_nand_regs *reg, int cs)
{
int i;
reg->gpmc_status = gpmc_base + GPMC_STATUS;
reg->gpmc_nand_command = gpmc_base + GPMC_CS0_OFFSET +
GPMC_CS_NAND_COMMAND + GPMC_CS_SIZE * cs;
......@@ -744,7 +583,17 @@ void gpmc_update_nand_reg(struct gpmc_nand_regs *reg, int cs)
reg->gpmc_ecc_control = gpmc_base + GPMC_ECC_CONTROL;
reg->gpmc_ecc_size_config = gpmc_base + GPMC_ECC_SIZE_CONFIG;
reg->gpmc_ecc1_result = gpmc_base + GPMC_ECC1_RESULT;
reg->gpmc_bch_result0 = gpmc_base + GPMC_ECC_BCH_RESULT_0;
for (i = 0; i < GPMC_BCH_NUM_REMAINDER; i++) {
reg->gpmc_bch_result0[i] = gpmc_base + GPMC_ECC_BCH_RESULT_0 +
GPMC_BCH_SIZE * i;
reg->gpmc_bch_result1[i] = gpmc_base + GPMC_ECC_BCH_RESULT_1 +
GPMC_BCH_SIZE * i;
reg->gpmc_bch_result2[i] = gpmc_base + GPMC_ECC_BCH_RESULT_2 +
GPMC_BCH_SIZE * i;
reg->gpmc_bch_result3[i] = gpmc_base + GPMC_ECC_BCH_RESULT_3 +
GPMC_BCH_SIZE * i;
}
}
int gpmc_get_client_irq(unsigned irq_config)
......@@ -1079,267 +928,3 @@ void omap3_gpmc_restore_context(void)
}
}
#endif /* CONFIG_ARCH_OMAP3 */
/**
* gpmc_enable_hwecc - enable hardware ecc functionality
* @cs: chip select number
* @mode: read/write mode
* @dev_width: device bus width(1 for x16, 0 for x8)
* @ecc_size: bytes for which ECC will be generated
*/
int gpmc_enable_hwecc(int cs, int mode, int dev_width, int ecc_size)
{
unsigned int val;
/* check if ecc module is in used */
if (gpmc_ecc_used != -EINVAL)
return -EINVAL;
gpmc_ecc_used = cs;
/* clear ecc and enable bits */
gpmc_write_reg(GPMC_ECC_CONTROL,
GPMC_ECC_CTRL_ECCCLEAR |
GPMC_ECC_CTRL_ECCREG1);
/* program ecc and result sizes */
val = ((((ecc_size >> 1) - 1) << 22) | (0x0000000F));
gpmc_write_reg(GPMC_ECC_SIZE_CONFIG, val);
switch (mode) {
case GPMC_ECC_READ:
case GPMC_ECC_WRITE:
gpmc_write_reg(GPMC_ECC_CONTROL,
GPMC_ECC_CTRL_ECCCLEAR |
GPMC_ECC_CTRL_ECCREG1);
break;
case GPMC_ECC_READSYN:
gpmc_write_reg(GPMC_ECC_CONTROL,
GPMC_ECC_CTRL_ECCCLEAR |
GPMC_ECC_CTRL_ECCDISABLE);
break;
default:
printk(KERN_INFO "Error: Unrecognized Mode[%d]!\n", mode);
break;
}
/* (ECC 16 or 8 bit col) | ( CS ) | ECC Enable */
val = (dev_width << 7) | (cs << 1) | (0x1);
gpmc_write_reg(GPMC_ECC_CONFIG, val);
return 0;
}
EXPORT_SYMBOL_GPL(gpmc_enable_hwecc);
/**
* gpmc_calculate_ecc - generate non-inverted ecc bytes
* @cs: chip select number
* @dat: data pointer over which ecc is computed
* @ecc_code: ecc code buffer
*
* Using non-inverted ECC is considered ugly since writing a blank
* page (padding) will clear the ECC bytes. This is not a problem as long
* no one is trying to write data on the seemingly unused page. Reading
* an erased page will produce an ECC mismatch between generated and read
* ECC bytes that has to be dealt with separately.
*/
int gpmc_calculate_ecc(int cs, const u_char *dat, u_char *ecc_code)
{
unsigned int val = 0x0;
if (gpmc_ecc_used != cs)
return -EINVAL;
/* read ecc result */
val = gpmc_read_reg(GPMC_ECC1_RESULT);
*ecc_code++ = val; /* P128e, ..., P1e */
*ecc_code++ = val >> 16; /* P128o, ..., P1o */
/* P2048o, P1024o, P512o, P256o, P2048e, P1024e, P512e, P256e */
*ecc_code++ = ((val >> 8) & 0x0f) | ((val >> 20) & 0xf0);
gpmc_ecc_used = -EINVAL;
return 0;
}
EXPORT_SYMBOL_GPL(gpmc_calculate_ecc);
#ifdef CONFIG_ARCH_OMAP3
/**
* gpmc_init_hwecc_bch - initialize hardware BCH ecc functionality
* @cs: chip select number
* @nsectors: how many 512-byte sectors to process
* @nerrors: how many errors to correct per sector (4 or 8)
*
* This function must be executed before any call to gpmc_enable_hwecc_bch.
*/
int gpmc_init_hwecc_bch(int cs, int nsectors, int nerrors)
{
/* check if ecc module is in use */
if (gpmc_ecc_used != -EINVAL)
return -EINVAL;
/* support only OMAP3 class */
if (!cpu_is_omap34xx()) {
printk(KERN_ERR "BCH ecc is not supported on this CPU\n");
return -EINVAL;
}
/*
* For now, assume 4-bit mode is only supported on OMAP3630 ES1.x, x>=1.
* Other chips may be added if confirmed to work.
*/
if ((nerrors == 4) &&
(!cpu_is_omap3630() || (GET_OMAP_REVISION() == 0))) {
printk(KERN_ERR "BCH 4-bit mode is not supported on this CPU\n");
return -EINVAL;
}
/* sanity check */
if (nsectors > 8) {
printk(KERN_ERR "BCH cannot process %d sectors (max is 8)\n",
nsectors);
return -EINVAL;
}
return 0;
}
EXPORT_SYMBOL_GPL(gpmc_init_hwecc_bch);
/**
* gpmc_enable_hwecc_bch - enable hardware BCH ecc functionality
* @cs: chip select number
* @mode: read/write mode
* @dev_width: device bus width(1 for x16, 0 for x8)
* @nsectors: how many 512-byte sectors to process
* @nerrors: how many errors to correct per sector (4 or 8)
*/
int gpmc_enable_hwecc_bch(int cs, int mode, int dev_width, int nsectors,
int nerrors)
{
unsigned int val;
/* check if ecc module is in use */
if (gpmc_ecc_used != -EINVAL)
return -EINVAL;
gpmc_ecc_used = cs;
/* clear ecc and enable bits */
gpmc_write_reg(GPMC_ECC_CONTROL, 0x1);
/*
* When using BCH, sector size is hardcoded to 512 bytes.
* Here we are using wrapping mode 6 both for reading and writing, with:
* size0 = 0 (no additional protected byte in spare area)
* size1 = 32 (skip 32 nibbles = 16 bytes per sector in spare area)
*/
gpmc_write_reg(GPMC_ECC_SIZE_CONFIG, (32 << 22) | (0 << 12));
/* BCH configuration */
val = ((1 << 16) | /* enable BCH */
(((nerrors == 8) ? 1 : 0) << 12) | /* 8 or 4 bits */
(0x06 << 8) | /* wrap mode = 6 */
(dev_width << 7) | /* bus width */
(((nsectors-1) & 0x7) << 4) | /* number of sectors */
(cs << 1) | /* ECC CS */
(0x1)); /* enable ECC */
gpmc_write_reg(GPMC_ECC_CONFIG, val);
gpmc_write_reg(GPMC_ECC_CONTROL, 0x101);
return 0;
}
EXPORT_SYMBOL_GPL(gpmc_enable_hwecc_bch);
/**
* gpmc_calculate_ecc_bch4 - Generate 7 ecc bytes per sector of 512 data bytes
* @cs: chip select number
* @dat: The pointer to data on which ecc is computed
* @ecc: The ecc output buffer
*/
int gpmc_calculate_ecc_bch4(int cs, const u_char *dat, u_char *ecc)
{
int i;
unsigned long nsectors, reg, val1, val2;
if (gpmc_ecc_used != cs)
return -EINVAL;
nsectors = ((gpmc_read_reg(GPMC_ECC_CONFIG) >> 4) & 0x7) + 1;
for (i = 0; i < nsectors; i++) {
reg = GPMC_ECC_BCH_RESULT_0 + 16*i;
/* Read hw-computed remainder */
val1 = gpmc_read_reg(reg + 0);
val2 = gpmc_read_reg(reg + 4);
/*
* Add constant polynomial to remainder, in order to get an ecc
* sequence of 0xFFs for a buffer filled with 0xFFs; and
* left-justify the resulting polynomial.
*/
*ecc++ = 0x28 ^ ((val2 >> 12) & 0xFF);
*ecc++ = 0x13 ^ ((val2 >> 4) & 0xFF);
*ecc++ = 0xcc ^ (((val2 & 0xF) << 4)|((val1 >> 28) & 0xF));
*ecc++ = 0x39 ^ ((val1 >> 20) & 0xFF);
*ecc++ = 0x96 ^ ((val1 >> 12) & 0xFF);
*ecc++ = 0xac ^ ((val1 >> 4) & 0xFF);
*ecc++ = 0x7f ^ ((val1 & 0xF) << 4);
}
gpmc_ecc_used = -EINVAL;
return 0;
}
EXPORT_SYMBOL_GPL(gpmc_calculate_ecc_bch4);
/**
* gpmc_calculate_ecc_bch8 - Generate 13 ecc bytes per block of 512 data bytes
* @cs: chip select number
* @dat: The pointer to data on which ecc is computed
* @ecc: The ecc output buffer
*/
int gpmc_calculate_ecc_bch8(int cs, const u_char *dat, u_char *ecc)
{
int i;
unsigned long nsectors, reg, val1, val2, val3, val4;
if (gpmc_ecc_used != cs)
return -EINVAL;
nsectors = ((gpmc_read_reg(GPMC_ECC_CONFIG) >> 4) & 0x7) + 1;
for (i = 0; i < nsectors; i++) {
reg = GPMC_ECC_BCH_RESULT_0 + 16*i;
/* Read hw-computed remainder */
val1 = gpmc_read_reg(reg + 0);
val2 = gpmc_read_reg(reg + 4);
val3 = gpmc_read_reg(reg + 8);
val4 = gpmc_read_reg(reg + 12);
/*
* Add constant polynomial to remainder, in order to get an ecc
* sequence of 0xFFs for a buffer filled with 0xFFs.
*/
*ecc++ = 0xef ^ (val4 & 0xFF);
*ecc++ = 0x51 ^ ((val3 >> 24) & 0xFF);
*ecc++ = 0x2e ^ ((val3 >> 16) & 0xFF);
*ecc++ = 0x09 ^ ((val3 >> 8) & 0xFF);
*ecc++ = 0xed ^ (val3 & 0xFF);
*ecc++ = 0x93 ^ ((val2 >> 24) & 0xFF);
*ecc++ = 0x9a ^ ((val2 >> 16) & 0xFF);
*ecc++ = 0xc2 ^ ((val2 >> 8) & 0xFF);
*ecc++ = 0x97 ^ (val2 & 0xFF);
*ecc++ = 0x79 ^ ((val1 >> 24) & 0xFF);
*ecc++ = 0xe5 ^ ((val1 >> 16) & 0xFF);
*ecc++ = 0x24 ^ ((val1 >> 8) & 0xFF);
*ecc++ = 0xb5 ^ (val1 & 0xFF);
}
gpmc_ecc_used = -EINVAL;
return 0;
}
EXPORT_SYMBOL_GPL(gpmc_calculate_ecc_bch8);
#endif /* CONFIG_ARCH_OMAP3 */
......@@ -11,6 +11,8 @@
#ifndef __OMAP2_GPMC_H
#define __OMAP2_GPMC_H
#include <linux/platform_data/mtd-nand-omap2.h>
/* Maximum Number of Chip Selects */
#define GPMC_CS_NUM 8
......@@ -32,15 +34,6 @@
#define GPMC_SET_IRQ_STATUS 0x00000004
#define GPMC_CONFIG_WP 0x00000005
#define GPMC_GET_IRQ_STATUS 0x00000006
#define GPMC_PREFETCH_FIFO_CNT 0x00000007 /* bytes available in FIFO for r/w */
#define GPMC_PREFETCH_COUNT 0x00000008 /* remaining bytes to be read/write*/
#define GPMC_STATUS_BUFFER 0x00000009 /* 1: buffer is available to write */
#define GPMC_NAND_COMMAND 0x0000000a
#define GPMC_NAND_ADDRESS 0x0000000b
#define GPMC_NAND_DATA 0x0000000c
#define GPMC_ENABLE_IRQ 0x0000000d
/* ECC commands */
......@@ -76,25 +69,10 @@
#define GPMC_DEVICETYPE_NOR 0
#define GPMC_DEVICETYPE_NAND 2
#define GPMC_CONFIG_WRITEPROTECT 0x00000010
#define GPMC_STATUS_BUFF_EMPTY 0x00000001
#define WR_RD_PIN_MONITORING 0x00600000
#define GPMC_PREFETCH_STATUS_FIFO_CNT(val) ((val >> 24) & 0x7F)
#define GPMC_PREFETCH_STATUS_COUNT(val) (val & 0x00003fff)
#define GPMC_IRQ_FIFOEVENTENABLE 0x01
#define GPMC_IRQ_COUNT_EVENT 0x02
#define PREFETCH_FIFOTHRESHOLD_MAX 0x40
#define PREFETCH_FIFOTHRESHOLD(val) ((val) << 8)
enum omap_ecc {
/* 1-bit ecc: stored at end of spare area */
OMAP_ECC_HAMMING_CODE_DEFAULT = 0, /* Default, s/w method */
OMAP_ECC_HAMMING_CODE_HW, /* gpmc to detect the error */
/* 1-bit ecc: stored at beginning of spare area as romcode */
OMAP_ECC_HAMMING_CODE_HW_ROMCODE, /* gpmc method & romcode layout */
OMAP_ECC_BCH4_CODE_HW, /* 4-bit BCH ecc code */
OMAP_ECC_BCH8_CODE_HW, /* 8-bit BCH ecc code */
};
/*
* Note that all values in this struct are in nanoseconds except sync_clk
......@@ -133,22 +111,6 @@ struct gpmc_timings {
u16 wr_data_mux_bus; /* WRDATAONADMUXBUS */
};
struct gpmc_nand_regs {
void __iomem *gpmc_status;
void __iomem *gpmc_nand_command;
void __iomem *gpmc_nand_address;
void __iomem *gpmc_nand_data;
void __iomem *gpmc_prefetch_config1;
void __iomem *gpmc_prefetch_config2;
void __iomem *gpmc_prefetch_control;
void __iomem *gpmc_prefetch_status;
void __iomem *gpmc_ecc_config;
void __iomem *gpmc_ecc_control;
void __iomem *gpmc_ecc_size_config;
void __iomem *gpmc_ecc1_result;
void __iomem *gpmc_bch_result0;
};
extern void gpmc_update_nand_reg(struct gpmc_nand_regs *reg, int cs);
extern int gpmc_get_client_irq(unsigned irq_config);
......@@ -160,31 +122,14 @@ extern unsigned long gpmc_get_fclk_period(void);
extern void gpmc_cs_write_reg(int cs, int idx, u32 val);
extern u32 gpmc_cs_read_reg(int cs, int idx);
extern int gpmc_cs_calc_divider(int cs, unsigned int sync_clk);
extern int gpmc_calc_divider(unsigned int sync_clk);
extern int gpmc_cs_set_timings(int cs, const struct gpmc_timings *t);
extern int gpmc_cs_request(int cs, unsigned long size, unsigned long *base);
extern void gpmc_cs_free(int cs);
extern int gpmc_cs_set_reserved(int cs, int reserved);
extern int gpmc_cs_reserved(int cs);
extern int gpmc_prefetch_enable(int cs, int fifo_th, int dma_mode,
unsigned int u32_count, int is_write);
extern int gpmc_prefetch_reset(int cs);
extern void omap3_gpmc_save_context(void);
extern void omap3_gpmc_restore_context(void);
extern int gpmc_read_status(int cmd);
extern int gpmc_cs_configure(int cs, int cmd, int wval);
extern int gpmc_nand_read(int cs, int cmd);
extern int gpmc_nand_write(int cs, int cmd, int wval);
int gpmc_enable_hwecc(int cs, int mode, int dev_width, int ecc_size);
int gpmc_calculate_ecc(int cs, const u_char *dat, u_char *ecc_code);
#ifdef CONFIG_ARCH_OMAP3
int gpmc_init_hwecc_bch(int cs, int nsectors, int nerrors);
int gpmc_enable_hwecc_bch(int cs, int mode, int dev_width, int nsectors,
int nerrors);
int gpmc_calculate_ecc_bch4(int cs, const u_char *dat, u_char *ecc);
int gpmc_calculate_ecc_bch8(int cs, const u_char *dat, u_char *ecc);
#endif /* CONFIG_ARCH_OMAP3 */
#endif
......@@ -40,7 +40,7 @@
#include "powerdomain.h"
#include <plat/sdrc.h>
#include <plat/prcm.h>
#include <plat/gpmc.h>
#include "gpmc.h"
#include <plat/dma.h>
#include "common.h"
......
......@@ -18,7 +18,7 @@
#include <linux/usb/musb.h>
#include <plat/gpmc.h>
#include "gpmc.h"
#include "mux.h"
......
......@@ -28,7 +28,6 @@
#endif
#include <plat/dma.h>
#include <plat/gpmc.h>
#include <linux/platform_data/mtd-nand-omap2.h>
#define DRIVER_NAME "omap2-nand"
......@@ -106,10 +105,16 @@
#define CS_MASK 0x7
#define ENABLE_PREFETCH (0x1 << 7)
#define DMA_MPU_MODE_SHIFT 2
#define ECCSIZE0_SHIFT 12
#define ECCSIZE1_SHIFT 22
#define ECC1RESULTSIZE 0x1
#define ECCCLEAR 0x100
#define ECC1 0x1
#define PREFETCH_FIFOTHRESHOLD_MAX 0x40
#define PREFETCH_FIFOTHRESHOLD(val) ((val) << 8)
#define PREFETCH_STATUS_COUNT(val) (val & 0x00003fff)
#define PREFETCH_STATUS_FIFO_CNT(val) ((val >> 24) & 0x7F)
#define STATUS_BUFF_EMPTY 0x00000001
/* oob info generated runtime depending on ecc algorithm and layout selected */
static struct nand_ecclayout omap_oobinfo;
......@@ -269,7 +274,7 @@ static void omap_write_buf8(struct mtd_info *mtd, const u_char *buf, int len)
/* wait until buffer is available for write */
do {
status = readl(info->reg.gpmc_status) &
GPMC_STATUS_BUFF_EMPTY;
STATUS_BUFF_EMPTY;
} while (!status);
}
}
......@@ -307,7 +312,7 @@ static void omap_write_buf16(struct mtd_info *mtd, const u_char * buf, int len)
/* wait until buffer is available for write */
do {
status = readl(info->reg.gpmc_status) &
GPMC_STATUS_BUFF_EMPTY;
STATUS_BUFF_EMPTY;
} while (!status);
}
}
......@@ -348,7 +353,7 @@ static void omap_read_buf_pref(struct mtd_info *mtd, u_char *buf, int len)
} else {
do {
r_count = readl(info->reg.gpmc_prefetch_status);
r_count = GPMC_PREFETCH_STATUS_FIFO_CNT(r_count);
r_count = PREFETCH_STATUS_FIFO_CNT(r_count);
r_count = r_count >> 2;
ioread32_rep(info->nand.IO_ADDR_R, p, r_count);
p += r_count;
......@@ -395,7 +400,7 @@ static void omap_write_buf_pref(struct mtd_info *mtd,
} else {
while (len) {
w_count = readl(info->reg.gpmc_prefetch_status);
w_count = GPMC_PREFETCH_STATUS_FIFO_CNT(w_count);
w_count = PREFETCH_STATUS_FIFO_CNT(w_count);
w_count = w_count >> 1;
for (i = 0; (i < w_count) && len; i++, len -= 2)
iowrite16(*p++, info->nand.IO_ADDR_W);
......@@ -407,7 +412,7 @@ static void omap_write_buf_pref(struct mtd_info *mtd,
do {
cpu_relax();
val = readl(info->reg.gpmc_prefetch_status);
val = GPMC_PREFETCH_STATUS_COUNT(val);
val = PREFETCH_STATUS_COUNT(val);
} while (val && (tim++ < limit));
/* disable and stop the PFPW engine */
......@@ -493,7 +498,7 @@ static inline int omap_nand_dma_transfer(struct mtd_info *mtd, void *addr,
do {
cpu_relax();
val = readl(info->reg.gpmc_prefetch_status);
val = GPMC_PREFETCH_STATUS_COUNT(val);
val = PREFETCH_STATUS_COUNT(val);
} while (val && (tim++ < limit));
/* disable and stop the PFPW engine */
......@@ -556,7 +561,7 @@ static irqreturn_t omap_nand_irq(int this_irq, void *dev)
u32 bytes;
bytes = readl(info->reg.gpmc_prefetch_status);
bytes = GPMC_PREFETCH_STATUS_FIFO_CNT(bytes);
bytes = PREFETCH_STATUS_FIFO_CNT(bytes);
bytes = bytes & 0xFFFC; /* io in multiple of 4 bytes */
if (info->iomode == OMAP_NAND_IO_WRITE) { /* checks for write io */
if (this_irq == info->gpmc_irq_count)
......@@ -682,7 +687,7 @@ static void omap_write_buf_irq_pref(struct mtd_info *mtd,
limit = (loops_per_jiffy * msecs_to_jiffies(OMAP_NAND_TIMEOUT_MS));
do {
val = readl(info->reg.gpmc_prefetch_status);
val = GPMC_PREFETCH_STATUS_COUNT(val);
val = PREFETCH_STATUS_COUNT(val);
cpu_relax();
} while (val && (tim++ < limit));
......@@ -996,7 +1001,7 @@ static int omap_wait(struct mtd_info *mtd, struct nand_chip *chip)
cond_resched();
}
status = gpmc_nand_read(info->gpmc_cs, GPMC_NAND_DATA);
status = readb(info->reg.gpmc_nand_data);
return status;
}
......@@ -1029,19 +1034,45 @@ static int omap_dev_ready(struct mtd_info *mtd)
static void omap3_enable_hwecc_bch(struct mtd_info *mtd, int mode)
{
int nerrors;
unsigned int dev_width;
unsigned int dev_width, nsectors;
struct omap_nand_info *info = container_of(mtd, struct omap_nand_info,
mtd);
struct nand_chip *chip = mtd->priv;
u32 val;
nerrors = (info->nand.ecc.bytes == 13) ? 8 : 4;
dev_width = (chip->options & NAND_BUSWIDTH_16) ? 1 : 0;
nsectors = 1;
/*
* Program GPMC to perform correction on one 512-byte sector at a time.
* Using 4 sectors at a time (i.e. ecc.size = 2048) is also possible and
* gives a slight (5%) performance gain (but requires additional code).
*/
(void)gpmc_enable_hwecc_bch(info->gpmc_cs, mode, dev_width, 1, nerrors);
writel(ECC1, info->reg.gpmc_ecc_control);
/*
* When using BCH, sector size is hardcoded to 512 bytes.
* Here we are using wrapping mode 6 both for reading and writing, with:
* size0 = 0 (no additional protected byte in spare area)
* size1 = 32 (skip 32 nibbles = 16 bytes per sector in spare area)
*/
val = (32 << ECCSIZE1_SHIFT) | (0 << ECCSIZE0_SHIFT);
writel(val, info->reg.gpmc_ecc_size_config);
/* BCH configuration */
val = ((1 << 16) | /* enable BCH */
(((nerrors == 8) ? 1 : 0) << 12) | /* 8 or 4 bits */
(0x06 << 8) | /* wrap mode = 6 */
(dev_width << 7) | /* bus width */
(((nsectors-1) & 0x7) << 4) | /* number of sectors */
(info->gpmc_cs << 1) | /* ECC CS */
(0x1)); /* enable ECC */
writel(val, info->reg.gpmc_ecc_config);
/* clear ecc and enable bits */
writel(ECCCLEAR | ECC1, info->reg.gpmc_ecc_control);
}
/**
......@@ -1055,7 +1086,32 @@ static int omap3_calculate_ecc_bch4(struct mtd_info *mtd, const u_char *dat,
{
struct omap_nand_info *info = container_of(mtd, struct omap_nand_info,
mtd);
return gpmc_calculate_ecc_bch4(info->gpmc_cs, dat, ecc_code);
unsigned long nsectors, val1, val2;
int i;
nsectors = ((readl(info->reg.gpmc_ecc_config) >> 4) & 0x7) + 1;
for (i = 0; i < nsectors; i++) {
/* Read hw-computed remainder */
val1 = readl(info->reg.gpmc_bch_result0[i]);
val2 = readl(info->reg.gpmc_bch_result1[i]);
/*
* Add constant polynomial to remainder, in order to get an ecc
* sequence of 0xFFs for a buffer filled with 0xFFs; and
* left-justify the resulting polynomial.
*/
*ecc_code++ = 0x28 ^ ((val2 >> 12) & 0xFF);
*ecc_code++ = 0x13 ^ ((val2 >> 4) & 0xFF);
*ecc_code++ = 0xcc ^ (((val2 & 0xF) << 4)|((val1 >> 28) & 0xF));
*ecc_code++ = 0x39 ^ ((val1 >> 20) & 0xFF);
*ecc_code++ = 0x96 ^ ((val1 >> 12) & 0xFF);
*ecc_code++ = 0xac ^ ((val1 >> 4) & 0xFF);
*ecc_code++ = 0x7f ^ ((val1 & 0xF) << 4);
}
return 0;
}
/**
......@@ -1069,7 +1125,39 @@ static int omap3_calculate_ecc_bch8(struct mtd_info *mtd, const u_char *dat,
{
struct omap_nand_info *info = container_of(mtd, struct omap_nand_info,
mtd);
return gpmc_calculate_ecc_bch8(info->gpmc_cs, dat, ecc_code);
unsigned long nsectors, val1, val2, val3, val4;
int i;
nsectors = ((readl(info->reg.gpmc_ecc_config) >> 4) & 0x7) + 1;
for (i = 0; i < nsectors; i++) {
/* Read hw-computed remainder */
val1 = readl(info->reg.gpmc_bch_result0[i]);
val2 = readl(info->reg.gpmc_bch_result1[i]);
val3 = readl(info->reg.gpmc_bch_result2[i]);
val4 = readl(info->reg.gpmc_bch_result3[i]);
/*
* Add constant polynomial to remainder, in order to get an ecc
* sequence of 0xFFs for a buffer filled with 0xFFs.
*/
*ecc_code++ = 0xef ^ (val4 & 0xFF);
*ecc_code++ = 0x51 ^ ((val3 >> 24) & 0xFF);
*ecc_code++ = 0x2e ^ ((val3 >> 16) & 0xFF);
*ecc_code++ = 0x09 ^ ((val3 >> 8) & 0xFF);
*ecc_code++ = 0xed ^ (val3 & 0xFF);
*ecc_code++ = 0x93 ^ ((val2 >> 24) & 0xFF);
*ecc_code++ = 0x9a ^ ((val2 >> 16) & 0xFF);
*ecc_code++ = 0xc2 ^ ((val2 >> 8) & 0xFF);
*ecc_code++ = 0x97 ^ (val2 & 0xFF);
*ecc_code++ = 0x79 ^ ((val1 >> 24) & 0xFF);
*ecc_code++ = 0xe5 ^ ((val1 >> 16) & 0xFF);
*ecc_code++ = 0x24 ^ ((val1 >> 8) & 0xFF);
*ecc_code++ = 0xb5 ^ (val1 & 0xFF);
}
return 0;
}
/**
......@@ -1125,7 +1213,7 @@ static void omap3_free_bch(struct mtd_info *mtd)
*/
static int omap3_init_bch(struct mtd_info *mtd, int ecc_opt)
{
int ret, max_errors;
int max_errors;
struct omap_nand_info *info = container_of(mtd, struct omap_nand_info,
mtd);
#ifdef CONFIG_MTD_NAND_OMAP_BCH8
......@@ -1142,11 +1230,6 @@ static int omap3_init_bch(struct mtd_info *mtd, int ecc_opt)
goto fail;
}
/* initialize GPMC BCH engine */
ret = gpmc_init_hwecc_bch(info->gpmc_cs, 1, max_errors);
if (ret)
goto fail;
/* software bch library is only used to detect and locate errors */
info->bch = init_bch(13, max_errors, 0x201b /* hw polynomial */);
if (!info->bch)
......@@ -1513,7 +1596,7 @@ static int omap_nand_remove(struct platform_device *pdev)
/* Release NAND device, its internal structures and partitions */
nand_release(&info->mtd);
iounmap(info->nand.IO_ADDR_R);
release_mem_region(info->phys_base, NAND_IO_SIZE);
release_mem_region(info->phys_base, info->mem_size);
kfree(info);
return 0;
}
......
......@@ -38,12 +38,10 @@
#include <linux/regulator/consumer.h>
#include <asm/mach/flash.h>
#include <plat/gpmc.h>
#include <linux/platform_data/mtd-onenand-omap2.h>
#include <asm/gpio.h>
#include <plat/dma.h>
#include <plat/cpu.h>
#define DRIVER_NAME "omap2-onenand"
......@@ -63,6 +61,7 @@ struct omap2_onenand {
int freq;
int (*setup)(void __iomem *base, int *freq_ptr);
struct regulator *regulator;
u8 flags;
};
static void omap2_onenand_dma_cb(int lch, u16 ch_status, void *data)
......@@ -155,7 +154,7 @@ static int omap2_onenand_wait(struct mtd_info *mtd, int state)
if (!(syscfg & ONENAND_SYS_CFG1_IOBE)) {
syscfg |= ONENAND_SYS_CFG1_IOBE;
write_reg(c, syscfg, ONENAND_REG_SYS_CFG1);
if (cpu_is_omap34xx())
if (c->flags & ONENAND_IN_OMAP34XX)
/* Add a delay to let GPIO settle */
syscfg = read_reg(c, ONENAND_REG_SYS_CFG1);
}
......@@ -639,6 +638,7 @@ static int __devinit omap2_onenand_probe(struct platform_device *pdev)
init_completion(&c->irq_done);
init_completion(&c->dma_done);
c->flags = pdata->flags;
c->gpmc_cs = pdata->cs;
c->gpio_irq = pdata->gpio_irq;
c->dma_channel = pdata->dma_channel;
......@@ -729,7 +729,7 @@ static int __devinit omap2_onenand_probe(struct platform_device *pdev)
this = &c->onenand;
if (c->dma_channel >= 0) {
this->wait = omap2_onenand_wait;
if (cpu_is_omap34xx()) {
if (c->flags & ONENAND_IN_OMAP34XX) {
this->read_bufferram = omap3_onenand_read_bufferram;
this->write_bufferram = omap3_onenand_write_bufferram;
} else {
......@@ -803,7 +803,6 @@ static int __devexit omap2_onenand_remove(struct platform_device *pdev)
}
iounmap(c->onenand.base);
release_mem_region(c->phys_base, c->mem_size);
gpmc_cs_free(c->gpmc_cs);
kfree(c);
return 0;
......
......@@ -8,9 +8,13 @@
* published by the Free Software Foundation.
*/
#include <plat/gpmc.h>
#ifndef _MTD_NAND_OMAP2_H
#define _MTD_NAND_OMAP2_H
#include <linux/mtd/partitions.h>
#define GPMC_BCH_NUM_REMAINDER 8
enum nand_io {
NAND_OMAP_PREFETCH_POLLED = 0, /* prefetch polled mode, default */
NAND_OMAP_POLLED, /* polled mode, without prefetch */
......@@ -18,10 +22,38 @@ enum nand_io {
NAND_OMAP_PREFETCH_IRQ /* prefetch enabled irq mode */
};
enum omap_ecc {
/* 1-bit ecc: stored at end of spare area */
OMAP_ECC_HAMMING_CODE_DEFAULT = 0, /* Default, s/w method */
OMAP_ECC_HAMMING_CODE_HW, /* gpmc to detect the error */
/* 1-bit ecc: stored at beginning of spare area as romcode */
OMAP_ECC_HAMMING_CODE_HW_ROMCODE, /* gpmc method & romcode layout */
OMAP_ECC_BCH4_CODE_HW, /* 4-bit BCH ecc code */
OMAP_ECC_BCH8_CODE_HW, /* 8-bit BCH ecc code */
};
struct gpmc_nand_regs {
void __iomem *gpmc_status;
void __iomem *gpmc_nand_command;
void __iomem *gpmc_nand_address;
void __iomem *gpmc_nand_data;
void __iomem *gpmc_prefetch_config1;
void __iomem *gpmc_prefetch_config2;
void __iomem *gpmc_prefetch_control;
void __iomem *gpmc_prefetch_status;
void __iomem *gpmc_ecc_config;
void __iomem *gpmc_ecc_control;
void __iomem *gpmc_ecc_size_config;
void __iomem *gpmc_ecc1_result;
void __iomem *gpmc_bch_result0[GPMC_BCH_NUM_REMAINDER];
void __iomem *gpmc_bch_result1[GPMC_BCH_NUM_REMAINDER];
void __iomem *gpmc_bch_result2[GPMC_BCH_NUM_REMAINDER];
void __iomem *gpmc_bch_result3[GPMC_BCH_NUM_REMAINDER];
};
struct omap_nand_platform_data {
int cs;
struct mtd_partition *parts;
struct gpmc_timings *gpmc_t;
int nr_parts;
bool dev_ready;
enum nand_io xfer_type;
......@@ -30,14 +62,4 @@ struct omap_nand_platform_data {
struct gpmc_nand_regs reg;
};
/* minimum size for IO mapping */
#define NAND_IO_SIZE 4
#if defined(CONFIG_MTD_NAND_OMAP2) || defined(CONFIG_MTD_NAND_OMAP2_MODULE)
extern int gpmc_nand_init(struct omap_nand_platform_data *d);
#else
static inline int gpmc_nand_init(struct omap_nand_platform_data *d)
{
return 0;
}
#endif
......@@ -9,17 +9,15 @@
* published by the Free Software Foundation.
*/
#ifndef __MTD_ONENAND_OMAP2_H
#define __MTD_ONENAND_OMAP2_H
#include <linux/mtd/mtd.h>
#include <linux/mtd/partitions.h>
#define ONENAND_SYNC_READ (1 << 0)
#define ONENAND_SYNC_READWRITE (1 << 1)
struct onenand_freq_info {
u16 maf_id;
u16 dev_id;
u16 ver_id;
};
#define ONENAND_IN_OMAP34XX (1 << 2)
struct omap_onenand_platform_data {
int cs;
......@@ -27,27 +25,9 @@ struct omap_onenand_platform_data {
struct mtd_partition *parts;
int nr_parts;
int (*onenand_setup)(void __iomem *, int *freq_ptr);
int (*get_freq)(const struct onenand_freq_info *freq_info,
bool *clk_dep);
int dma_channel;
u8 flags;
u8 regulator_can_sleep;
u8 skip_initial_unlocking;
};
#define ONENAND_MAX_PARTITIONS 8
#if defined(CONFIG_MTD_ONENAND_OMAP2) || \
defined(CONFIG_MTD_ONENAND_OMAP2_MODULE)
extern void gpmc_onenand_init(struct omap_onenand_platform_data *d);
#else
#define board_onenand_data NULL
static inline void gpmc_onenand_init(struct omap_onenand_platform_data *d)
{
}
#endif
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment