📄 tffsarch.c
字号:
/****************************************************************************** * * * Project: DOC Driver for Linux 2.6 Block device driver for mDOC H3 family * * of devices under Linux kernel 2.6. * * * * Version: 1.0 * * Email questions to: oemsupport@sandisk.com * * Copyright (C) SanDisk IL Ltd. 1995 - 2007 * * SanDisk IL Ltd., 7 Atir Yeda Street, Kfar Saba 44425, Israel * * * ****************************************************************************** * * * 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 any later version.* * This program is distributed in the hope that it will be useful, but WITHOUT* * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for * * more details, which is set forth in the readme.txt file. * * You should have received a copy of the GNU General Public License along * * with this program; if not, write to the Free Software Foundation, Inc., 51 * * Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * * * * This License does not grant you any right to use the trademarks, service * * marks or logos of SanDisk IL Ltd. or SanDisk Corporation. * * Subject to the foregoing, SanDisk IL Ltd., for itself and on behalf of its * * licensors, hereby reserves all intellectual property rights in the program,* * except for the rights expressly granted in this License. * * * ******************************************************************************//* * $Log$ */#include "tffsdrv.h"#include "flbase.h"#include "doch_sys.h"/* * macro */#define Align(ptr,alignment) (((unsigned long)(ptr)) & (~((alignment) - 1)))#if (LINUX_VERSION_CODE < KERNEL_VERSION(2,6,0)) && !defined(virt_addr_valid)# define virt_addr_valid(vaddr) (((vaddr) >= PAGE_OFFSET) && ((vaddr) < (unsigned long)high_memory))#endif/* * static routines */static void __dmasw_bread (int off, void *vbuf, unsigned int bytes);static void __dmasw_bwrite (int off, void *vbuf, unsigned int bytes);/*-----------------------------------------------------------------------* * * * t f f s _ d m a s w * * * * Transfer data between DiskOnChip's and RAM buffer. * * This routines handles any kind of virtual buffer (linearly mapped or * * vmalloc() allocated, spanning multiple memory pages, containing * * multiple sectors). * * * * Parameters: * * win pointer to DiskOnChip's I/O window * * off offset of DiskOnChip's I/O register from the base * * of DiskOnChip window * * vbuf virtual address of RAM buffer * * bytes bytes to transfer * * read DMA direction: '1' for DiskOnChip->RAM, or zero * * for RAM->DiskOnChip * * * *-----------------------------------------------------------------------*/void tffs_dmasw ( char * win, int off, void * vbuf, unsigned int bytes, int read ){ /* We check if 'vbuf' is linearly mapped kernel virtual address (and * is therefore laid out in consecutive physical pages). If 'vbuf' * is not linearly mapped (i.e. came from user space or allocated * via vmalloc()), we check if it entirely fits into a single memory * page. */ if ((virt_addr_valid((unsigned long)vbuf)) /* linearly mapped kernel virtual address */ || (Align(vbuf,PAGE_SIZE) == Align((unsigned long)vbuf + bytes,PAGE_SIZE))) { if (read == 1) __dmasw_bread (off, vbuf, bytes); else __dmasw_bwrite (off, vbuf, bytes); } else /* 'vbuf' crosses memory page boundary */ { if (bytes >= (FL_SECTOR_SIZE * 2)) /* two or more sectors */ { register unsigned long bytes_on_this_page; /* Do multiple DMA transfers, each one fitting into a single * memory page. */ do { bytes_on_this_page = PAGE_SIZE - ((unsigned long)vbuf & (PAGE_SIZE - 1)); if (bytes_on_this_page > bytes) bytes_on_this_page = bytes; if (read == 1) __dmasw_bread (off, vbuf, bytes_on_this_page); else __dmasw_bwrite (off, vbuf, bytes); bytes -= bytes_on_this_page; vbuf = (char *)vbuf + bytes_on_this_page; } while (bytes > 0); } else /* single sector; don't bother with DMA ... */ { if (read == 1) tffs_readw ((unsigned long)(win + off), vbuf, (bytes / sizeof(short))); else tffs_writew ((unsigned long)(win + off), vbuf, (bytes / sizeof(short))); } }}#if defined(CONFIG_ARCH_MAINSTONE) || defined(CONFIG_MACH_MAINSTONE)/*_____________________________________________________________________________ | | | | | Intel PXA27x ("Mainstone") board | | | |____________________________________________________________________________| */# include <asm/dma.h># include <linux/pci.h># include <asm/arch/pxa-regs.h>/* Routine blk_dma_inv_range_harvard() (arch/arm/mm/blockops.c) appears * to be buggy: it seems to constantly shoot past the end of the address * range [vbuf..vbuf+bytes] by 32 bytes. It also appears to write some * bogus data into this RAM address range while invalidating it in D-cache. */# define PXA27X_DCACHE_BUG_WORKAROUND/* * static routines for PXA27x */static void tffs_dma_init (int resume);static void tffs_dma_release (void);static void tffs_dma_intr (int dma, void *dummy, struct pt_regs *regs);/*-----------------------------------------------------------------------* * * * t f f s a r c h _ i n i t * * * * This routine is called during module initialization time with 'resume'* * argument set to FALSE, and from Power Manager's 'resume' routine with * * 'resume' argument set to TRUE. If 'resume' is FALSE, this routine * * allocates and initializes all required board resources; if 'resume' * * is TRUE, this routine does minimal required re-initialization of * * these resources after power suspend. * * * * Parameters: * * suspend FALSE (module initialization) or TRUE (power resume) * * * * Returns: * * always '1' (success) * * * *-----------------------------------------------------------------------*/unsigned char tffsarch_init ( int resume ){ /* chip select that DiskOnChip is connected to */#define PXA27X_CS 5 /* Value to write to chip select's conf register. Possible values are: * * 0x099C VLIO DiskOnChip adapter and DiskOnChip G4/G3/H3 device * 0x0AAC VLIO DiskOnChip adapter and DiskOnChip H1 device * 0xA998 sync DiskOnChip adapter and DiskOnChip G4/G3/H3 device * 0xAAA8 sync DiskOnChip adapter and DiskOnChip H1 device * * Register description: * * 31 0 - slow device (VLIO); 1 - fast device * 30:28 The value of this bit is half the number of memory clock cycles * from the time that chip select is de-asserted after a read or * write until the next chip select (of a different static memory * bank) or nSDCS is asserted * 27:24 ROM Delay Next Access The RDN field is encoded as follows: * ENCODED (Programmed) Value -----> DECODED (Actual) Value * 0-11 -----> 0-11 * 12 -----> 15 * 13 -----> 20 * 14 -----> 26 * 15 -----> 30 * RDNx * 2 = amount of time nOE or nPWE is deasserted to address * hold and address setup to nOE or nPWE assertion time * 23:20 This determines the minimum number of memory clock cycles * (minus 1) of nOE (nPWE) assert time for each beat of read (write) * 19 ROM Bus Width (1 = 16 bits) * 16-18 0 - Synchronous flash; 2 - burst 4; 3 - burst 8; 4 - Variable-latency * I/O (VLIO) */#define PXA27X_CS_VAL 0x026C /* andrayk Nov 242 006: originally was 0x044C */ int cs = PXA27X_CS; unsigned long val; if (resume == FALSE) { PrintkInfo ("use Chip Select %d", cs); } /* Line below configures GPIO33 as output. * Without this line, DiskOnChip driver fails to detect DiskOnChip G4. */ GPDR1 |= 0x2; /* was: GPDR1 = 0xfc3fab83 */ /* read chip select config reg */ val = __REG(0x48000008 + ((cs / 2) * sizeof(long))); if (cs & 1) val = ((PXA27X_CS_VAL << 16) | (val & 0xffff)); else val = ((val & 0xffff0000) | PXA27X_CS_VAL); /* write chip select config reg */ __REG(0x48000008 + ((cs / 2) * sizeof(long))) = val; /* if DMA is requested, configure it */ if (tffs_dma_mode > 0) { tffs_dma_init (resume); } return 1;}void TffsHWRelease(void){ /* if DMA was actually used, release all DMA resources */ if (tffs_dma_mode > 0) { tffs_dma_release (); }}/*-----------------------------------------------------------------------* * * * t f f s _ d m a _ i n i t * * * * This routine is called during module initialization time with 'resume'* * argument set to FALSE, and from Power Manager's 'resume' routine with * * 'resume' argument set to TRUE. If 'resume' is FALSE, this routine * * allocates and initializes all required DMA resources; if 'resume' is * * TRUE, this routine does minimal required re-initialization of DMA * * channel after power suspend. * * * * Parameters: * * resume FALSE (module initialization) or TRUE (power resume) * * * *-----------------------------------------------------------------------*/static void tffs_dma_init (int resume){ if (resume == FALSE) { /* We use single DMA channel for both 'read' and * 'write' operations, so we allocate it here. */ if ((tffsInfo.channel = pxa_request_dma(TFFS_DEVICE_NAME, DMA_PRIO_LOW, tffs_dma_intr, NULL)) < 0) { /* failed to allocate DMA channel, won't use DMA */ tffs_dma_mode = 0; PrintkError ("can't get DMA chan"); return; } PrintkInfo ("use DMA channel %d", tffsInfo.channel); } DRCMR(tffsInfo.channel) = (DRCMR_MAPVLD | (tffsInfo.channel & 0x1f)); __REG(0x400000a0) |= (1 << tffsInfo.channel); /* clear any pending DMA errors/interrupts */ DCSR(tffsInfo.channel) = (DCSR_NODESC | DCSR_ENDINTR | DCSR_STARTINTR | DCSR_BUSERR); if (resume == FALSE) { tffsInfo.dma_buf_vptr = NULL;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -