⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 syncflash.c

📁 一个2.4.21版本的嵌入式linux内核
💻 C
字号:
/* * MTD driver for Micron SyncFlash flash memory. * * Author: Jon McClintock <jonm@bluemug.com> * * Based loosely upon the LART flash driver, authored by Abraham vd Merwe * <abraham@2d3d.co.za>. * * Copyright 2003, Blue Mug, Inc. for Motorola, Inc. * * This code is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as * published by the Free Software Foundation. * * References: * * 	[1] Micron SyncFlash homepage * 		- http://www.syncflash.com/ * * 	[2] MT28S4M16LC -- 4Mx16 SyncFlash memory datasheet * 		- http://syncflash.com/pdfs/datasheets/mt28s4m16lc_6.pdf * * 	[3] MTD internal API documentation * 		- http://www.linux-mtd.infradead.org/tech/ * * Limitations: * * 	Even though this driver is written for Micron SyncFlash, it is quite * 	specific to the Motorola MX1 ADS development board. */#include <linux/kernel.h>#include <linux/module.h>#include <linux/types.h>#include <linux/version.h>#include <linux/errno.h>#include <linux/mtd/mtd.h>#include <asm/io.h>/* partition support */#define HAVE_PARTITIONS#ifdef HAVE_PARTITIONS#include <linux/mtd/partitions.h>#endif#ifndef CONFIG_ARCH_MX1ADS#error The SyncFlash driver currently only supports the MX1 ADS platform.#endif/* * General flash configuration parameters. */#define BUSWIDTH		4#define FLASH_BLOCKSIZE		(256 * 1024 * BUSWIDTH)#define FLASH_NUMBLOCKS		16#define BUSWIDTH		4#define FLASH_ADDRESS		IO_ADDRESS(MX1ADS_FLASH_BASE)#define FLASH_MANUFACTURER	0x002C002C#define FLASH_DEVICE_ID		0x00D300D3/* * The size and extent of the bootloader in flash. */#define NUM_BOOTLOADER_BLOCKS	1#define BOOTLOADER_START	0x00000000#define BOOTLOADER_LEN		(NUM_BOOTLOADER_BLOCKS * FLASH_BLOCKSIZE)/* * The size and extent of the kernel in flash. */#define NUM_KERNEL_BLOCKS	1#define KERNEL_START		(BOOTLOADER_START + BOOTLOADER_LEN)#define KERNEL_LEN		(NUM_KERNEL_BLOCKS * FLASH_BLOCKSIZE)/* File system */#define NUM_FILESYSTEM_BLOCKS	14#define FILESYSTEM_START	(KERNEL_START + KERNEL_LEN)#define FILESYSTEM_LEN		(NUM_FILESYSTEM_BLOCKS * FLASH_BLOCKSIZE)/* * SDRAM controller register location and values. These are very specific * to the MX1. */#define SDRAMC_REGISTER		IO_ADDRESS(0x00221004)/* * This the mask we use to get the start of a block from a given address. */#define BLOCK_MASK	(0xFFF00000)/* * This is the A10 address line of the SyncFlash; it's used to initiate * a precharge command. */#define SYNCFLASH_A10	(0x00100000)/* * SDRAM controller MODE settings. */#define CMD_NORMAL	(0x81020300)			/* Normal Mode */#define CMD_PREC	(CMD_NORMAL + 0x10000000)	/* Precharge command */#define CMD_AUTO	(CMD_NORMAL + 0x20000000)	/* Auto refresh */#define CMD_LMR		(CMD_NORMAL + 0x30000000)	/* Load Mode Register */#define CMD_LCR		(CMD_NORMAL + 0x60000000)	/* LCR Command */#define CMD_PROGRAM	(CMD_NORMAL + 0x70000000)	/* SyncFlash Program *//* * SyncFlash LCR Commands adjusted for the DBMX1 AHB internal address bus . */#define LCR_READ_STATUS		(0x0001C000)	/* 0x70 */#define LCR_READ_CONFIG		(0x00024000)	/* 0x90 */#define LCR_ERASE_CONFIRM	(0x00008000)	/* 0x20 */#define LCR_ERASE_NVMODE	(0x0000C000)	/* 0x30 */#define LCR_PROG_NVMODE		(0x00028000)	/* 0xA0 */#define LCR_SR_CLEAR		(0x00014000)	/* 0x50 *//* * Status register bits */#define SR_VPS_ERROR		(1 << 8)	/* Power-Up status error */#define SR_ISM_READY		(1 << 7)	/* State machine isn't busy */#define SR_ERASE_ERROR		(1 << 5)	/* Erase/Unprotect error */#define SR_PROGRAM_ERROR	(1 << 4)	/* Program/Protect error */#define SR_DEVICE_PROTECTED	(1 << 3)	/* Device is protected */#define SR_ISM_STATUS_H		(1 << 2)	/* Bank ISM status, high bit */#define SR_ISM_STATUS_L		(1 << 1)	/* Bank ISM status, low bit */#define SR_DEVICE_ISM_STATUS	(1 << 0)	/* ISM is device-level */#define SR_ERROR		(SR_VPS_ERROR|SR_ERASE_ERROR|SR_PROGRAM_ERROR|SR_DEVICE_PROTECTED)#define STATUS_VALUE(a)		((a) | ((a) << 16))/* * Device configuration register offsets */#define DC_MANUFACTURER		(0 * BUSWIDTH)#define DC_DEVICE_ID		(1 * BUSWIDTH)#define DC_BLOCK_PROTECT	(2 * BUSWIDTH)#define DC_DEVICE_PROTECT	(3 * BUSWIDTH)#define FL_WORD(addr) (*(volatile unsigned long*)(addr))static char module_name[] = "syncflash";inline __u8 read8 (__u32 offset){	return *(volatile __u8 *) (FLASH_ADDRESS + offset);}inline __u32 read32 (__u32 offset){	return *(volatile __u32 *) (FLASH_ADDRESS + offset);}inline void write32 (__u32 x,__u32 offset){	*(volatile __u32 *) (FLASH_ADDRESS + offset) = x;}static __u32 read_device_configuration_register(__u32 reg_number){	__u32 tmp;	/* Setup the SDRAM controller to issue an LCR command. */	FL_WORD(SDRAMC_REGISTER) = CMD_LCR;	/* Perform a read to issue the Read Device Configuration Command. */	tmp = read32(LCR_READ_CONFIG);	/* Return the SDRAM controller to normal mode. */	FL_WORD(SDRAMC_REGISTER) = CMD_NORMAL;	/* Return the value of the specified register. */	tmp = read32(reg_number);	return tmp;}/* * Get the status of the flash devices. */static __u32 flash_read_status(){	__u32 status, tmp;	/* Enter the SyncFlash Program READ/WRITE mode. */	FL_WORD(SDRAMC_REGISTER) = CMD_PROGRAM;	/* Read the status register. */	status = read32(LCR_READ_STATUS);	/* Clear the status register. */	FL_WORD(SDRAMC_REGISTER) = CMD_LCR;	tmp = read32(LCR_SR_CLEAR);	/* Return to Normal mode. */	FL_WORD(SDRAMC_REGISTER) = CMD_NORMAL;	return status;}/* * Loop until both write state machines are ready. */static __u32 flash_status_wait(){	__u32 status;	do {		status = flash_read_status();	} while ((status & STATUS_VALUE(SR_ISM_READY)) !=			STATUS_VALUE(SR_ISM_READY));	return status;}/* * Loop until the Write State machine is ready, then do a full error * check.  Clear status and leave the flash in Read Array mode; return * 0 for no error, -1 for error. */static int flash_status_full_check(){	__u32 status;	status = flash_status_wait() & STATUS_VALUE(SR_ERROR);	return status ? -EIO : 0;}/* * Return the flash to the normal mode. */static void flash_normal_mode(){	__u32 tmp;	/* First issue a precharge all command. */	FL_WORD(SDRAMC_REGISTER) = CMD_PREC;	tmp = read32(SYNCFLASH_A10);	/* Now place the SDRAM controller in Normal mode. */	FL_WORD(SDRAMC_REGISTER) = CMD_NORMAL;}/* * Probe for SyncFlash memory on MX1ADS board. * * Returns 1 if we found SyncFlash memory, 0 otherwise. */static int flash_probe (void){	__u32 manufacturer, device_id;	/* For some reason, the first read doesn't work, so we do it	 * twice. */	manufacturer = read_device_configuration_register(DC_MANUFACTURER);	manufacturer = read_device_configuration_register(DC_MANUFACTURER);	device_id = read_device_configuration_register(DC_DEVICE_ID);	printk("SyncFlash probe: manufacturer 0x%08lx, device_id 0x%08lx\n",		manufacturer, device_id);	return (manufacturer == FLASH_MANUFACTURER &&		device_id == FLASH_DEVICE_ID);}/* * Erase one block of flash memory at offset ``offset'' which is any * address within the block which should be erased. * * Returns 0 if successful, -1 otherwise. */static inline int erase_block (__u32 offset){	__u32 tmp;	/* Mask off the lower bits of the address to get the first address	 * in the flash block. */	offset &= (__u32)BLOCK_MASK;	/* Perform a read and precharge of the bank before the LCR|ACT|WRIT	 * sequence to avoid the inadvertent precharge command occurring	 * during the LCR_ACT_WRIT sequence. */	FL_WORD(SDRAMC_REGISTER) = CMD_NORMAL;	tmp = read32(offset);	FL_WORD(SDRAMC_REGISTER) = CMD_PREC;	tmp = read32(offset);	/* Now start the actual erase. */	/* LCR|ACT|WRIT sequence */	FL_WORD(SDRAMC_REGISTER) = CMD_LCR;	write32(0, offset + LCR_ERASE_CONFIRM);	/* Return to normal mode to issue the erase confirm. */	FL_WORD(SDRAMC_REGISTER) = CMD_NORMAL;	write32(0xD0D0D0D0, offset);	if (flash_status_full_check()) {		printk (KERN_WARNING "%s: erase error at address 0x%.8x.\n",			module_name, offset);		return (-1);	}	flash_normal_mode();	return 0;}static int flash_erase (struct mtd_info *mtd,struct erase_info *instr){	__u32 addr,len;	int i,first;	/* sanity checks */	if (instr->addr + instr->len > mtd->size) return (-EINVAL);	/*	 * check that both start and end of the requested erase are	 * aligned with the erasesize at the appropriate addresses.	 *	 * skip all erase regions which are ended before the start of	 * the requested erase. Actually, to save on the calculations,	 * we skip to the first erase region which starts after the	 * start of the requested erase, and then go back one.	 */	for (i = 0; (i < mtd->numeraseregions) &&		    (instr->addr >= mtd->eraseregions[i].offset); i++) ;	i--;	/*	 * ok, now i is pointing at the erase region in which this	 * erase request starts. Check the start of the requested	 * erase range is aligned with the erase size which is in	 * effect here.	 */	if (instr->addr & (mtd->eraseregions[i].erasesize - 1))		return (-EINVAL);	/* Remember the erase region we start on */	first = i;	/*	 * next, check that the end of the requested erase is aligned	 * with the erase region at that address.	 *	 * as before, drop back one to point at the region in which	 * the address actually falls	 */	for (;	     (i < mtd->numeraseregions) &&	     ((instr->addr + instr->len) >= mtd->eraseregions[i].offset) ;	     i++) ;	i--;	/* is the end aligned on a block boundary? */	if ((instr->addr + instr->len) & (mtd->eraseregions[i].erasesize - 1))		return (-EINVAL);	addr = instr->addr;	len = instr->len;	i = first;	/* now erase those blocks */	while (len)	{		if (erase_block (addr))		{			instr->state = MTD_ERASE_FAILED;			return (-EIO);		}		addr += mtd->eraseregions[i].erasesize;		len -= mtd->eraseregions[i].erasesize;		if (addr == (mtd->eraseregions[i].offset +				(mtd->eraseregions[i].erasesize *				 mtd->eraseregions[i].numblocks)))			i++;	}	instr->state = MTD_ERASE_DONE;	if (instr->callback) instr->callback (instr);	return (0);}static int flash_read (struct mtd_info *mtd, loff_t from,		       size_t len, size_t *retlen, u_char *buf){	/* Sanity checks. */	if (!len) return (0);	if (from + len > mtd->size) return (-EINVAL);	/* Ensure that we are in normal mode. */	flash_normal_mode();	/* We always read len bytes. */	*retlen = len;	/* first, we read bytes until we reach a dword boundary */	if (from & (BUSWIDTH - 1))	{		int gap = BUSWIDTH - (from & (BUSWIDTH - 1));		while (len && gap--) *buf++ = read8(from++), len--;	}	/* now we read dwords until we reach a non-dword boundary */	while (len >= BUSWIDTH)	{		*((__u32 *) buf) = read32(from);		buf += BUSWIDTH;		from += BUSWIDTH;		len -= BUSWIDTH;	}	/* top up the last unaligned bytes */	if (len & (BUSWIDTH - 1))		while (len--) *buf++ = read8(from++);	return (0);}/* * Write one dword ``x'' to flash memory at offset ``offset''. ``offset'' * must be 32 bits, i.e. it must be on a dword boundary. * * Returns 0 if successful, -1 otherwise. */static int flash_write_dword(__u32 offset, __u32 x){	__u32 tmp;	/* First issue a precharge all command. */	FL_WORD(SDRAMC_REGISTER) = CMD_PREC;	tmp = read32(SYNCFLASH_A10);	/* Enter the SyncFlash programming mode. */	FL_WORD(SDRAMC_REGISTER) = CMD_PROGRAM;	write32(x, offset);	/* Wait for the write to complete. */	flash_status_wait();	/* Return to normal mode. */	flash_normal_mode();	return 0;}static int flash_write (struct mtd_info *mtd,loff_t to,size_t len,size_t *retlen,const u_char *buf){	__u8 tmp[4];	int i,n;	*retlen = 0;	/* Sanity checks */	if (!len) return (0);	if (to + len > mtd->size) return (-EINVAL);	/* First, we write a 0xFF.... padded byte until we reach a	 * dword boundary. */	if (to & (BUSWIDTH - 1))	{		__u32 aligned = to & ~(BUSWIDTH - 1);		int gap = to - aligned;		i = n = 0;		while (gap--) tmp[i++] = 0xFF;		while (len && i < BUSWIDTH) tmp[i++] = buf[n++], len--;		while (i < BUSWIDTH) tmp[i++] = 0xFF;		if (flash_write_dword(aligned, *((__u32 *) tmp)))			return (-EIO);		to += n;		buf += n;		*retlen += n;	}	/* Now we write dwords until we reach a non-dword boundary. */	while (len >= BUSWIDTH)	{		if (flash_write_dword (to,*((__u32 *) buf))) return (-EIO);		to += BUSWIDTH;		buf += BUSWIDTH;		*retlen += BUSWIDTH;		len -= BUSWIDTH;	}	/* Top up the last unaligned bytes, padded with 0xFF.... */	if (len & (BUSWIDTH - 1))	{		i = n = 0;		while (len--) tmp[i++] = buf[n++];		while (i < BUSWIDTH) tmp[i++] = 0xFF;		if (flash_write_dword (to,*((__u32 *) tmp))) return (-EIO);		*retlen += n;	}	return flash_status_full_check();}#define NB_OF(x) (sizeof (x) / sizeof (x[0]))static struct mtd_info mtd;static struct mtd_erase_region_info erase_regions[] ={	/* flash blocks */	{		offset: 	0x00000000,		erasesize: 	FLASH_BLOCKSIZE,		numblocks: 	FLASH_NUMBLOCKS	},};#ifdef HAVE_PARTITIONSstatic struct mtd_partition syncflash_partitions[] ={   /* bootloader */   {	       name: "bootloader",	     offset: BOOTLOADER_START,	       size: BOOTLOADER_LEN,	 mask_flags: 0   },   /* Kernel */   {	       name: "kernel",	     offset: KERNEL_START,			/* MTDPART_OFS_APPEND */	       size: KERNEL_LEN,	 mask_flags: 0   },   /* file system */   {	       name: "file system",	     offset: FILESYSTEM_START,			/* MTDPART_OFS_APPEND */	       size: FILESYSTEM_LEN,			/* MTDPART_SIZ_FULL */	 mask_flags: 0   }};#endifint __init syncflash_init (void){	int result;	memset (&mtd,0,sizeof (mtd));	printk ("MTD driver for Micron SyncFlash.\n");	printk ("%s: Probing for SyncFlash on MX1ADS...\n",module_name);	if (!flash_probe ())	{		printk (KERN_WARNING "%s: Found no SyncFlash devices\n",			module_name);		return (-ENXIO);	}	printk ("%s: Found a SyncFlash device.\n",module_name);	mtd.name = module_name;	mtd.type = MTD_NORFLASH;	mtd.flags = MTD_CAP_NORFLASH;	mtd.size = FLASH_BLOCKSIZE * FLASH_NUMBLOCKS;	mtd.erasesize = FLASH_BLOCKSIZE;	mtd.numeraseregions = NB_OF(erase_regions);	mtd.eraseregions = erase_regions;	mtd.module = THIS_MODULE;	mtd.erase = flash_erase;	mtd.read = flash_read;	mtd.write = flash_write;#ifndef HAVE_PARTITIONS	result = add_mtd_device(&mtd);#else	result = add_mtd_partitions(&mtd,				    syncflash_partitions,				    NB_OF(syncflash_partitions));#endif	return (result);}void __exit syncflash_exit (void){#ifndef HAVE_PARTITIONS	del_mtd_device (&mtd);#else	del_mtd_partitions (&mtd);#endif}module_init (syncflash_init);module_exit (syncflash_exit);MODULE_LICENSE("GPL");MODULE_AUTHOR("Jon McClintock <jonm@bluemug.com>");MODULE_DESCRIPTION("MTD driver for Micron MT28S4M16LC SyncFlash on MX1ADS board");

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -