📄 umem.c
字号:
/* * mm.c - Micro Memory(tm) PCI memory board block device driver - v2.3 * * (C) 2001 San Mehat <nettwerk@valinux.com> * (C) 2001 Johannes Erdfelt <jerdfelt@valinux.com> * (C) 2001 NeilBrown <neilb@cse.unsw.edu.au> * * This driver for the Micro Memory PCI Memory Module with Battery Backup * is Copyright Micro Memory Inc 2001-2002. All rights reserved. * * This driver is released to the public under the terms of the * GNU GENERAL PUBLIC LICENSE version 2 * See the file COPYING for details. * * This driver provides a standard block device interface for Micro Memory(tm) * PCI based RAM boards. * 10/05/01: Phap Nguyen - Rebuilt the driver * 10/22/01: Phap Nguyen - v2.1 Added disk partitioning * 29oct2001:NeilBrown - Use make_request_fn instead of request_fn * - use stand disk partitioning (so fdisk works). * 08nov2001:NeilBrown - change driver name from "mm" to "umem" * - incorporate into main kernel * 08apr2002:NeilBrown - Move some of interrupt handle to tasklet * - use spin_lock_bh instead of _irq * - Never block on make_request. queue * bh's instead. * - unregister umem from devfs at mod unload * - Change version to 2.3 * 07Nov2001:Phap Nguyen - Select pci read command: 06, 12, 15 (Decimal) * 07Jan2002: P. Nguyen - Used PCI Memory Write & Invalidate for DMA * 15May2002:NeilBrown - convert to bio for 2.5 * 17May2002:NeilBrown - remove init_mem initialisation. Instead detect * - a sequence of writes that cover the card, and * - set initialised bit then. *///#define DEBUG /* uncomment if you want debugging info (pr_debug) */#include <linux/config.h>#include <linux/sched.h>#include <linux/fs.h>#include <linux/bio.h>#include <linux/kernel.h>#include <linux/mm.h>#include <linux/mman.h>#include <linux/ioctl.h>#include <linux/module.h>#include <linux/init.h>#include <linux/interrupt.h>#include <linux/smp_lock.h>#include <linux/timer.h>#include <linux/pci.h>#include <linux/slab.h>#include <linux/fcntl.h> /* O_ACCMODE */#include <linux/hdreg.h> /* HDIO_GETGEO */#include <linux/umem.h>#include <asm/uaccess.h>#include <asm/io.h>#define MM_MAXCARDS 4#define MM_RAHEAD 2 /* two sectors */#define MM_BLKSIZE 1024 /* 1k blocks */#define MM_HARDSECT 512 /* 512-byte hardware sectors */#define MM_SHIFT 6 /* max 64 partitions on 4 cards *//* * Version Information */#define DRIVER_VERSION "v2.3"#define DRIVER_AUTHOR "San Mehat, Johannes Erdfelt, NeilBrown"#define DRIVER_DESC "Micro Memory(tm) PCI memory board block driver"static int debug;/* #define HW_TRACE(x) writeb(x,cards[0].csr_remap + MEMCTRLSTATUS_MAGIC) */#define HW_TRACE(x)#define DEBUG_LED_ON_TRANSFER 0x01#define DEBUG_BATTERY_POLLING 0x02module_param(debug, int, 0644);MODULE_PARM_DESC(debug, "Debug bitmask");static int pci_read_cmd = 0x0C; /* Read Multiple */module_param(pci_read_cmd, int, 0);MODULE_PARM_DESC(pci_read_cmd, "PCI read command");static int pci_write_cmd = 0x0F; /* Write and Invalidate */module_param(pci_write_cmd, int, 0);MODULE_PARM_DESC(pci_write_cmd, "PCI write command");static int pci_cmds;static int major_nr;#include <linux/blkdev.h>#include <linux/blkpg.h>struct cardinfo { int card_number; struct pci_dev *dev; int irq; unsigned long csr_base; unsigned char __iomem *csr_remap; unsigned long csr_len;#ifdef CONFIG_MM_MAP_MEMORY unsigned long mem_base; unsigned char __iomem *mem_remap; unsigned long mem_len;#endif unsigned int win_size; /* PCI window size */ unsigned int mm_size; /* size in kbytes */ unsigned int init_size; /* initial segment, in sectors, * that we know to * have been written */ struct bio *bio, *currentbio, **biotail; request_queue_t *queue; struct mm_page { dma_addr_t page_dma; struct mm_dma_desc *desc; int cnt, headcnt; struct bio *bio, **biotail; } mm_pages[2];#define DESC_PER_PAGE ((PAGE_SIZE*2)/sizeof(struct mm_dma_desc)) int Active, Ready; struct tasklet_struct tasklet; unsigned int dma_status; struct { int good; int warned; unsigned long last_change; } battery[2]; spinlock_t lock; int check_batteries; int flags;};static struct cardinfo cards[MM_MAXCARDS];static struct block_device_operations mm_fops;static struct timer_list battery_timer;static int num_cards = 0;static struct gendisk *mm_gendisk[MM_MAXCARDS];static void check_batteries(struct cardinfo *card);/*------------------------------------------------------------------------------------- get_userbit-----------------------------------------------------------------------------------*/static int get_userbit(struct cardinfo *card, int bit){ unsigned char led; led = readb(card->csr_remap + MEMCTRLCMD_LEDCTRL); return led & bit;}/*------------------------------------------------------------------------------------- set_userbit-----------------------------------------------------------------------------------*/static int set_userbit(struct cardinfo *card, int bit, unsigned char state){ unsigned char led; led = readb(card->csr_remap + MEMCTRLCMD_LEDCTRL); if (state) led |= bit; else led &= ~bit; writeb(led, card->csr_remap + MEMCTRLCMD_LEDCTRL); return 0;}/*------------------------------------------------------------------------------------- set_led-----------------------------------------------------------------------------------*//* * NOTE: For the power LED, use the LED_POWER_* macros since they differ */static void set_led(struct cardinfo *card, int shift, unsigned char state){ unsigned char led; led = readb(card->csr_remap + MEMCTRLCMD_LEDCTRL); if (state == LED_FLIP) led ^= (1<<shift); else { led &= ~(0x03 << shift); led |= (state << shift); } writeb(led, card->csr_remap + MEMCTRLCMD_LEDCTRL);}#ifdef MM_DIAG/*------------------------------------------------------------------------------------- dump_regs-----------------------------------------------------------------------------------*/static void dump_regs(struct cardinfo *card){ unsigned char *p; int i, i1; p = card->csr_remap; for (i = 0; i < 8; i++) { printk(KERN_DEBUG "%p ", p); for (i1 = 0; i1 < 16; i1++) printk("%02x ", *p++); printk("\n"); }}#endif/*------------------------------------------------------------------------------------- dump_dmastat-----------------------------------------------------------------------------------*/static void dump_dmastat(struct cardinfo *card, unsigned int dmastat){ printk(KERN_DEBUG "MM%d*: DMAstat - ", card->card_number); if (dmastat & DMASCR_ANY_ERR) printk("ANY_ERR "); if (dmastat & DMASCR_MBE_ERR) printk("MBE_ERR "); if (dmastat & DMASCR_PARITY_ERR_REP) printk("PARITY_ERR_REP "); if (dmastat & DMASCR_PARITY_ERR_DET) printk("PARITY_ERR_DET "); if (dmastat & DMASCR_SYSTEM_ERR_SIG) printk("SYSTEM_ERR_SIG "); if (dmastat & DMASCR_TARGET_ABT) printk("TARGET_ABT "); if (dmastat & DMASCR_MASTER_ABT) printk("MASTER_ABT "); if (dmastat & DMASCR_CHAIN_COMPLETE) printk("CHAIN_COMPLETE "); if (dmastat & DMASCR_DMA_COMPLETE) printk("DMA_COMPLETE "); printk("\n");}/* * Theory of request handling * * Each bio is assigned to one mm_dma_desc - which may not be enough FIXME * We have two pages of mm_dma_desc, holding about 64 descriptors * each. These are allocated at init time. * One page is "Ready" and is either full, or can have request added. * The other page might be "Active", which DMA is happening on it. * * Whenever IO on the active page completes, the Ready page is activated * and the ex-Active page is clean out and made Ready. * Otherwise the Ready page is only activated when it becomes full, or * when mm_unplug_device is called via the unplug_io_fn. * * If a request arrives while both pages a full, it is queued, and b_rdev is * overloaded to record whether it was a read or a write. * * The interrupt handler only polls the device to clear the interrupt. * The processing of the result is done in a tasklet. */static void mm_start_io(struct cardinfo *card){ /* we have the lock, we know there is * no IO active, and we know that card->Active * is set */ struct mm_dma_desc *desc; struct mm_page *page; int offset; /* make the last descriptor end the chain */ page = &card->mm_pages[card->Active]; pr_debug("start_io: %d %d->%d\n", card->Active, page->headcnt, page->cnt-1); desc = &page->desc[page->cnt-1]; desc->control_bits |= cpu_to_le32(DMASCR_CHAIN_COMP_EN); desc->control_bits &= ~cpu_to_le32(DMASCR_CHAIN_EN); desc->sem_control_bits = desc->control_bits; if (debug & DEBUG_LED_ON_TRANSFER) set_led(card, LED_REMOVE, LED_ON); desc = &page->desc[page->headcnt]; writel(0, card->csr_remap + DMA_PCI_ADDR); writel(0, card->csr_remap + DMA_PCI_ADDR + 4); writel(0, card->csr_remap + DMA_LOCAL_ADDR); writel(0, card->csr_remap + DMA_LOCAL_ADDR + 4); writel(0, card->csr_remap + DMA_TRANSFER_SIZE); writel(0, card->csr_remap + DMA_TRANSFER_SIZE + 4); writel(0, card->csr_remap + DMA_SEMAPHORE_ADDR); writel(0, card->csr_remap + DMA_SEMAPHORE_ADDR + 4); offset = ((char*)desc) - ((char*)page->desc); writel(cpu_to_le32((page->page_dma+offset)&0xffffffff), card->csr_remap + DMA_DESCRIPTOR_ADDR); /* Force the value to u64 before shifting otherwise >> 32 is undefined C * and on some ports will do nothing ! */ writel(cpu_to_le32(((u64)page->page_dma)>>32), card->csr_remap + DMA_DESCRIPTOR_ADDR + 4); /* Go, go, go */ writel(cpu_to_le32(DMASCR_GO | DMASCR_CHAIN_EN | pci_cmds), card->csr_remap + DMA_STATUS_CTRL);}static int add_bio(struct cardinfo *card);static void activate(struct cardinfo *card){ /* if No page is Active, and Ready is * not empty, then switch Ready page * to active and start IO. * Then add any bh's that are available to Ready */ do { while (add_bio(card)) ; if (card->Active == -1 && card->mm_pages[card->Ready].cnt > 0) { card->Active = card->Ready; card->Ready = 1-card->Ready; mm_start_io(card); } } while (card->Active == -1 && add_bio(card));}static inline void reset_page(struct mm_page *page){ page->cnt = 0; page->headcnt = 0; page->bio = NULL; page->biotail = & page->bio;}static void mm_unplug_device(request_queue_t *q){ struct cardinfo *card = q->queuedata; unsigned long flags; spin_lock_irqsave(&card->lock, flags); if (blk_remove_plug(q)) activate(card); spin_unlock_irqrestore(&card->lock, flags);}/* * If there is room on Ready page, take * one bh off list and add it. * return 1 if there was room, else 0. */static int add_bio(struct cardinfo *card){ struct mm_page *p; struct mm_dma_desc *desc; dma_addr_t dma_handle; int offset; struct bio *bio; int rw; int len; bio = card->currentbio; if (!bio && card->bio) { card->currentbio = card->bio; card->bio = card->bio->bi_next; if (card->bio == NULL) card->biotail = &card->bio; card->currentbio->bi_next = NULL; return 1; } if (!bio) return 0; rw = bio_rw(bio); if (card->mm_pages[card->Ready].cnt >= DESC_PER_PAGE) return 0; len = bio_iovec(bio)->bv_len; dma_handle = pci_map_page(card->dev, bio_page(bio), bio_offset(bio), len, (rw==READ) ? PCI_DMA_FROMDEVICE : PCI_DMA_TODEVICE); p = &card->mm_pages[card->Ready];
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -