18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci * mm.c - Micro Memory(tm) PCI memory board block device driver - v2.3
48c2ecf20Sopenharmony_ci *
58c2ecf20Sopenharmony_ci * (C) 2001 San Mehat <nettwerk@valinux.com>
68c2ecf20Sopenharmony_ci * (C) 2001 Johannes Erdfelt <jerdfelt@valinux.com>
78c2ecf20Sopenharmony_ci * (C) 2001 NeilBrown <neilb@cse.unsw.edu.au>
88c2ecf20Sopenharmony_ci *
98c2ecf20Sopenharmony_ci * This driver for the Micro Memory PCI Memory Module with Battery Backup
108c2ecf20Sopenharmony_ci * is Copyright Micro Memory Inc 2001-2002.  All rights reserved.
118c2ecf20Sopenharmony_ci *
128c2ecf20Sopenharmony_ci * This driver provides a standard block device interface for Micro Memory(tm)
138c2ecf20Sopenharmony_ci * PCI based RAM boards.
148c2ecf20Sopenharmony_ci * 10/05/01: Phap Nguyen - Rebuilt the driver
158c2ecf20Sopenharmony_ci * 10/22/01: Phap Nguyen - v2.1 Added disk partitioning
168c2ecf20Sopenharmony_ci * 29oct2001:NeilBrown   - Use make_request_fn instead of request_fn
178c2ecf20Sopenharmony_ci *                       - use stand disk partitioning (so fdisk works).
188c2ecf20Sopenharmony_ci * 08nov2001:NeilBrown	 - change driver name from "mm" to "umem"
198c2ecf20Sopenharmony_ci *			 - incorporate into main kernel
208c2ecf20Sopenharmony_ci * 08apr2002:NeilBrown   - Move some of interrupt handle to tasklet
218c2ecf20Sopenharmony_ci *			 - use spin_lock_bh instead of _irq
228c2ecf20Sopenharmony_ci *			 - Never block on make_request.  queue
238c2ecf20Sopenharmony_ci *			   bh's instead.
248c2ecf20Sopenharmony_ci *			 - unregister umem from devfs at mod unload
258c2ecf20Sopenharmony_ci *			 - Change version to 2.3
268c2ecf20Sopenharmony_ci * 07Nov2001:Phap Nguyen - Select pci read command: 06, 12, 15 (Decimal)
278c2ecf20Sopenharmony_ci * 07Jan2002: P. Nguyen  - Used PCI Memory Write & Invalidate for DMA
288c2ecf20Sopenharmony_ci * 15May2002:NeilBrown   - convert to bio for 2.5
298c2ecf20Sopenharmony_ci * 17May2002:NeilBrown   - remove init_mem initialisation.  Instead detect
308c2ecf20Sopenharmony_ci *			 - a sequence of writes that cover the card, and
318c2ecf20Sopenharmony_ci *			 - set initialised bit then.
328c2ecf20Sopenharmony_ci */
338c2ecf20Sopenharmony_ci
348c2ecf20Sopenharmony_ci#undef DEBUG	/* #define DEBUG if you want debugging info (pr_debug) */
358c2ecf20Sopenharmony_ci#include <linux/fs.h>
368c2ecf20Sopenharmony_ci#include <linux/bio.h>
378c2ecf20Sopenharmony_ci#include <linux/kernel.h>
388c2ecf20Sopenharmony_ci#include <linux/mm.h>
398c2ecf20Sopenharmony_ci#include <linux/mman.h>
408c2ecf20Sopenharmony_ci#include <linux/gfp.h>
418c2ecf20Sopenharmony_ci#include <linux/ioctl.h>
428c2ecf20Sopenharmony_ci#include <linux/module.h>
438c2ecf20Sopenharmony_ci#include <linux/init.h>
448c2ecf20Sopenharmony_ci#include <linux/interrupt.h>
458c2ecf20Sopenharmony_ci#include <linux/timer.h>
468c2ecf20Sopenharmony_ci#include <linux/pci.h>
478c2ecf20Sopenharmony_ci#include <linux/dma-mapping.h>
488c2ecf20Sopenharmony_ci
498c2ecf20Sopenharmony_ci#include <linux/fcntl.h>        /* O_ACCMODE */
508c2ecf20Sopenharmony_ci#include <linux/hdreg.h>  /* HDIO_GETGEO */
518c2ecf20Sopenharmony_ci
528c2ecf20Sopenharmony_ci#include "umem.h"
538c2ecf20Sopenharmony_ci
548c2ecf20Sopenharmony_ci#include <linux/uaccess.h>
558c2ecf20Sopenharmony_ci#include <asm/io.h>
568c2ecf20Sopenharmony_ci
578c2ecf20Sopenharmony_ci#define MM_MAXCARDS 4
588c2ecf20Sopenharmony_ci#define MM_RAHEAD 2      /* two sectors */
598c2ecf20Sopenharmony_ci#define MM_BLKSIZE 1024  /* 1k blocks */
608c2ecf20Sopenharmony_ci#define MM_HARDSECT 512  /* 512-byte hardware sectors */
618c2ecf20Sopenharmony_ci#define MM_SHIFT 6       /* max 64 partitions on 4 cards  */
628c2ecf20Sopenharmony_ci
638c2ecf20Sopenharmony_ci/*
648c2ecf20Sopenharmony_ci * Version Information
658c2ecf20Sopenharmony_ci */
668c2ecf20Sopenharmony_ci
678c2ecf20Sopenharmony_ci#define DRIVER_NAME	"umem"
688c2ecf20Sopenharmony_ci#define DRIVER_VERSION	"v2.3"
698c2ecf20Sopenharmony_ci#define DRIVER_AUTHOR	"San Mehat, Johannes Erdfelt, NeilBrown"
708c2ecf20Sopenharmony_ci#define DRIVER_DESC	"Micro Memory(tm) PCI memory board block driver"
718c2ecf20Sopenharmony_ci
728c2ecf20Sopenharmony_cistatic int debug;
738c2ecf20Sopenharmony_ci/* #define HW_TRACE(x)     writeb(x,cards[0].csr_remap + MEMCTRLSTATUS_MAGIC) */
748c2ecf20Sopenharmony_ci#define HW_TRACE(x)
758c2ecf20Sopenharmony_ci
768c2ecf20Sopenharmony_ci#define DEBUG_LED_ON_TRANSFER	0x01
778c2ecf20Sopenharmony_ci#define DEBUG_BATTERY_POLLING	0x02
788c2ecf20Sopenharmony_ci
798c2ecf20Sopenharmony_cimodule_param(debug, int, 0644);
808c2ecf20Sopenharmony_ciMODULE_PARM_DESC(debug, "Debug bitmask");
818c2ecf20Sopenharmony_ci
828c2ecf20Sopenharmony_cistatic int pci_read_cmd = 0x0C;		/* Read Multiple */
838c2ecf20Sopenharmony_cimodule_param(pci_read_cmd, int, 0);
848c2ecf20Sopenharmony_ciMODULE_PARM_DESC(pci_read_cmd, "PCI read command");
858c2ecf20Sopenharmony_ci
868c2ecf20Sopenharmony_cistatic int pci_write_cmd = 0x0F;	/* Write and Invalidate */
878c2ecf20Sopenharmony_cimodule_param(pci_write_cmd, int, 0);
888c2ecf20Sopenharmony_ciMODULE_PARM_DESC(pci_write_cmd, "PCI write command");
898c2ecf20Sopenharmony_ci
908c2ecf20Sopenharmony_cistatic int pci_cmds;
918c2ecf20Sopenharmony_ci
928c2ecf20Sopenharmony_cistatic int major_nr;
938c2ecf20Sopenharmony_ci
948c2ecf20Sopenharmony_ci#include <linux/blkdev.h>
958c2ecf20Sopenharmony_ci#include <linux/blkpg.h>
968c2ecf20Sopenharmony_ci
978c2ecf20Sopenharmony_cistruct cardinfo {
988c2ecf20Sopenharmony_ci	struct pci_dev	*dev;
998c2ecf20Sopenharmony_ci
1008c2ecf20Sopenharmony_ci	unsigned char	__iomem *csr_remap;
1018c2ecf20Sopenharmony_ci	unsigned int	mm_size;  /* size in kbytes */
1028c2ecf20Sopenharmony_ci
1038c2ecf20Sopenharmony_ci	unsigned int	init_size; /* initial segment, in sectors,
1048c2ecf20Sopenharmony_ci				    * that we know to
1058c2ecf20Sopenharmony_ci				    * have been written
1068c2ecf20Sopenharmony_ci				    */
1078c2ecf20Sopenharmony_ci	struct bio	*bio, *currentbio, **biotail;
1088c2ecf20Sopenharmony_ci	struct bvec_iter current_iter;
1098c2ecf20Sopenharmony_ci
1108c2ecf20Sopenharmony_ci	struct request_queue *queue;
1118c2ecf20Sopenharmony_ci
1128c2ecf20Sopenharmony_ci	struct mm_page {
1138c2ecf20Sopenharmony_ci		dma_addr_t		page_dma;
1148c2ecf20Sopenharmony_ci		struct mm_dma_desc	*desc;
1158c2ecf20Sopenharmony_ci		int	 		cnt, headcnt;
1168c2ecf20Sopenharmony_ci		struct bio		*bio, **biotail;
1178c2ecf20Sopenharmony_ci		struct bvec_iter	iter;
1188c2ecf20Sopenharmony_ci	} mm_pages[2];
1198c2ecf20Sopenharmony_ci#define DESC_PER_PAGE ((PAGE_SIZE*2)/sizeof(struct mm_dma_desc))
1208c2ecf20Sopenharmony_ci
1218c2ecf20Sopenharmony_ci	int  Active, Ready;
1228c2ecf20Sopenharmony_ci
1238c2ecf20Sopenharmony_ci	struct tasklet_struct	tasklet;
1248c2ecf20Sopenharmony_ci	unsigned int dma_status;
1258c2ecf20Sopenharmony_ci
1268c2ecf20Sopenharmony_ci	struct {
1278c2ecf20Sopenharmony_ci		int		good;
1288c2ecf20Sopenharmony_ci		int		warned;
1298c2ecf20Sopenharmony_ci		unsigned long	last_change;
1308c2ecf20Sopenharmony_ci	} battery[2];
1318c2ecf20Sopenharmony_ci
1328c2ecf20Sopenharmony_ci	spinlock_t 	lock;
1338c2ecf20Sopenharmony_ci	int		check_batteries;
1348c2ecf20Sopenharmony_ci
1358c2ecf20Sopenharmony_ci	int		flags;
1368c2ecf20Sopenharmony_ci};
1378c2ecf20Sopenharmony_ci
1388c2ecf20Sopenharmony_cistatic struct cardinfo cards[MM_MAXCARDS];
1398c2ecf20Sopenharmony_cistatic struct timer_list battery_timer;
1408c2ecf20Sopenharmony_ci
1418c2ecf20Sopenharmony_cistatic int num_cards;
1428c2ecf20Sopenharmony_ci
1438c2ecf20Sopenharmony_cistatic struct gendisk *mm_gendisk[MM_MAXCARDS];
1448c2ecf20Sopenharmony_ci
1458c2ecf20Sopenharmony_cistatic void check_batteries(struct cardinfo *card);
1468c2ecf20Sopenharmony_ci
1478c2ecf20Sopenharmony_cistatic int get_userbit(struct cardinfo *card, int bit)
1488c2ecf20Sopenharmony_ci{
1498c2ecf20Sopenharmony_ci	unsigned char led;
1508c2ecf20Sopenharmony_ci
1518c2ecf20Sopenharmony_ci	led = readb(card->csr_remap + MEMCTRLCMD_LEDCTRL);
1528c2ecf20Sopenharmony_ci	return led & bit;
1538c2ecf20Sopenharmony_ci}
1548c2ecf20Sopenharmony_ci
1558c2ecf20Sopenharmony_cistatic int set_userbit(struct cardinfo *card, int bit, unsigned char state)
1568c2ecf20Sopenharmony_ci{
1578c2ecf20Sopenharmony_ci	unsigned char led;
1588c2ecf20Sopenharmony_ci
1598c2ecf20Sopenharmony_ci	led = readb(card->csr_remap + MEMCTRLCMD_LEDCTRL);
1608c2ecf20Sopenharmony_ci	if (state)
1618c2ecf20Sopenharmony_ci		led |= bit;
1628c2ecf20Sopenharmony_ci	else
1638c2ecf20Sopenharmony_ci		led &= ~bit;
1648c2ecf20Sopenharmony_ci	writeb(led, card->csr_remap + MEMCTRLCMD_LEDCTRL);
1658c2ecf20Sopenharmony_ci
1668c2ecf20Sopenharmony_ci	return 0;
1678c2ecf20Sopenharmony_ci}
1688c2ecf20Sopenharmony_ci
1698c2ecf20Sopenharmony_ci/*
1708c2ecf20Sopenharmony_ci * NOTE: For the power LED, use the LED_POWER_* macros since they differ
1718c2ecf20Sopenharmony_ci */
1728c2ecf20Sopenharmony_cistatic void set_led(struct cardinfo *card, int shift, unsigned char state)
1738c2ecf20Sopenharmony_ci{
1748c2ecf20Sopenharmony_ci	unsigned char led;
1758c2ecf20Sopenharmony_ci
1768c2ecf20Sopenharmony_ci	led = readb(card->csr_remap + MEMCTRLCMD_LEDCTRL);
1778c2ecf20Sopenharmony_ci	if (state == LED_FLIP)
1788c2ecf20Sopenharmony_ci		led ^= (1<<shift);
1798c2ecf20Sopenharmony_ci	else {
1808c2ecf20Sopenharmony_ci		led &= ~(0x03 << shift);
1818c2ecf20Sopenharmony_ci		led |= (state << shift);
1828c2ecf20Sopenharmony_ci	}
1838c2ecf20Sopenharmony_ci	writeb(led, card->csr_remap + MEMCTRLCMD_LEDCTRL);
1848c2ecf20Sopenharmony_ci
1858c2ecf20Sopenharmony_ci}
1868c2ecf20Sopenharmony_ci
1878c2ecf20Sopenharmony_ci#ifdef MM_DIAG
1888c2ecf20Sopenharmony_cistatic void dump_regs(struct cardinfo *card)
1898c2ecf20Sopenharmony_ci{
1908c2ecf20Sopenharmony_ci	unsigned char *p;
1918c2ecf20Sopenharmony_ci	int i, i1;
1928c2ecf20Sopenharmony_ci
1938c2ecf20Sopenharmony_ci	p = card->csr_remap;
1948c2ecf20Sopenharmony_ci	for (i = 0; i < 8; i++) {
1958c2ecf20Sopenharmony_ci		printk(KERN_DEBUG "%p   ", p);
1968c2ecf20Sopenharmony_ci
1978c2ecf20Sopenharmony_ci		for (i1 = 0; i1 < 16; i1++)
1988c2ecf20Sopenharmony_ci			printk("%02x ", *p++);
1998c2ecf20Sopenharmony_ci
2008c2ecf20Sopenharmony_ci		printk("\n");
2018c2ecf20Sopenharmony_ci	}
2028c2ecf20Sopenharmony_ci}
2038c2ecf20Sopenharmony_ci#endif
2048c2ecf20Sopenharmony_ci
2058c2ecf20Sopenharmony_cistatic void dump_dmastat(struct cardinfo *card, unsigned int dmastat)
2068c2ecf20Sopenharmony_ci{
2078c2ecf20Sopenharmony_ci	dev_printk(KERN_DEBUG, &card->dev->dev, "DMAstat - ");
2088c2ecf20Sopenharmony_ci	if (dmastat & DMASCR_ANY_ERR)
2098c2ecf20Sopenharmony_ci		printk(KERN_CONT "ANY_ERR ");
2108c2ecf20Sopenharmony_ci	if (dmastat & DMASCR_MBE_ERR)
2118c2ecf20Sopenharmony_ci		printk(KERN_CONT "MBE_ERR ");
2128c2ecf20Sopenharmony_ci	if (dmastat & DMASCR_PARITY_ERR_REP)
2138c2ecf20Sopenharmony_ci		printk(KERN_CONT "PARITY_ERR_REP ");
2148c2ecf20Sopenharmony_ci	if (dmastat & DMASCR_PARITY_ERR_DET)
2158c2ecf20Sopenharmony_ci		printk(KERN_CONT "PARITY_ERR_DET ");
2168c2ecf20Sopenharmony_ci	if (dmastat & DMASCR_SYSTEM_ERR_SIG)
2178c2ecf20Sopenharmony_ci		printk(KERN_CONT "SYSTEM_ERR_SIG ");
2188c2ecf20Sopenharmony_ci	if (dmastat & DMASCR_TARGET_ABT)
2198c2ecf20Sopenharmony_ci		printk(KERN_CONT "TARGET_ABT ");
2208c2ecf20Sopenharmony_ci	if (dmastat & DMASCR_MASTER_ABT)
2218c2ecf20Sopenharmony_ci		printk(KERN_CONT "MASTER_ABT ");
2228c2ecf20Sopenharmony_ci	if (dmastat & DMASCR_CHAIN_COMPLETE)
2238c2ecf20Sopenharmony_ci		printk(KERN_CONT "CHAIN_COMPLETE ");
2248c2ecf20Sopenharmony_ci	if (dmastat & DMASCR_DMA_COMPLETE)
2258c2ecf20Sopenharmony_ci		printk(KERN_CONT "DMA_COMPLETE ");
2268c2ecf20Sopenharmony_ci	printk("\n");
2278c2ecf20Sopenharmony_ci}
2288c2ecf20Sopenharmony_ci
2298c2ecf20Sopenharmony_ci/*
2308c2ecf20Sopenharmony_ci * Theory of request handling
2318c2ecf20Sopenharmony_ci *
2328c2ecf20Sopenharmony_ci * Each bio is assigned to one mm_dma_desc - which may not be enough FIXME
2338c2ecf20Sopenharmony_ci * We have two pages of mm_dma_desc, holding about 64 descriptors
2348c2ecf20Sopenharmony_ci * each.  These are allocated at init time.
2358c2ecf20Sopenharmony_ci * One page is "Ready" and is either full, or can have request added.
2368c2ecf20Sopenharmony_ci * The other page might be "Active", which DMA is happening on it.
2378c2ecf20Sopenharmony_ci *
2388c2ecf20Sopenharmony_ci * Whenever IO on the active page completes, the Ready page is activated
2398c2ecf20Sopenharmony_ci * and the ex-Active page is clean out and made Ready.
2408c2ecf20Sopenharmony_ci * Otherwise the Ready page is only activated when it becomes full.
2418c2ecf20Sopenharmony_ci *
2428c2ecf20Sopenharmony_ci * If a request arrives while both pages a full, it is queued, and b_rdev is
2438c2ecf20Sopenharmony_ci * overloaded to record whether it was a read or a write.
2448c2ecf20Sopenharmony_ci *
2458c2ecf20Sopenharmony_ci * The interrupt handler only polls the device to clear the interrupt.
2468c2ecf20Sopenharmony_ci * The processing of the result is done in a tasklet.
2478c2ecf20Sopenharmony_ci */
2488c2ecf20Sopenharmony_ci
2498c2ecf20Sopenharmony_cistatic void mm_start_io(struct cardinfo *card)
2508c2ecf20Sopenharmony_ci{
2518c2ecf20Sopenharmony_ci	/* we have the lock, we know there is
2528c2ecf20Sopenharmony_ci	 * no IO active, and we know that card->Active
2538c2ecf20Sopenharmony_ci	 * is set
2548c2ecf20Sopenharmony_ci	 */
2558c2ecf20Sopenharmony_ci	struct mm_dma_desc *desc;
2568c2ecf20Sopenharmony_ci	struct mm_page *page;
2578c2ecf20Sopenharmony_ci	int offset;
2588c2ecf20Sopenharmony_ci
2598c2ecf20Sopenharmony_ci	/* make the last descriptor end the chain */
2608c2ecf20Sopenharmony_ci	page = &card->mm_pages[card->Active];
2618c2ecf20Sopenharmony_ci	pr_debug("start_io: %d %d->%d\n",
2628c2ecf20Sopenharmony_ci		card->Active, page->headcnt, page->cnt - 1);
2638c2ecf20Sopenharmony_ci	desc = &page->desc[page->cnt-1];
2648c2ecf20Sopenharmony_ci
2658c2ecf20Sopenharmony_ci	desc->control_bits |= cpu_to_le32(DMASCR_CHAIN_COMP_EN);
2668c2ecf20Sopenharmony_ci	desc->control_bits &= ~cpu_to_le32(DMASCR_CHAIN_EN);
2678c2ecf20Sopenharmony_ci	desc->sem_control_bits = desc->control_bits;
2688c2ecf20Sopenharmony_ci
2698c2ecf20Sopenharmony_ci
2708c2ecf20Sopenharmony_ci	if (debug & DEBUG_LED_ON_TRANSFER)
2718c2ecf20Sopenharmony_ci		set_led(card, LED_REMOVE, LED_ON);
2728c2ecf20Sopenharmony_ci
2738c2ecf20Sopenharmony_ci	desc = &page->desc[page->headcnt];
2748c2ecf20Sopenharmony_ci	writel(0, card->csr_remap + DMA_PCI_ADDR);
2758c2ecf20Sopenharmony_ci	writel(0, card->csr_remap + DMA_PCI_ADDR + 4);
2768c2ecf20Sopenharmony_ci
2778c2ecf20Sopenharmony_ci	writel(0, card->csr_remap + DMA_LOCAL_ADDR);
2788c2ecf20Sopenharmony_ci	writel(0, card->csr_remap + DMA_LOCAL_ADDR + 4);
2798c2ecf20Sopenharmony_ci
2808c2ecf20Sopenharmony_ci	writel(0, card->csr_remap + DMA_TRANSFER_SIZE);
2818c2ecf20Sopenharmony_ci	writel(0, card->csr_remap + DMA_TRANSFER_SIZE + 4);
2828c2ecf20Sopenharmony_ci
2838c2ecf20Sopenharmony_ci	writel(0, card->csr_remap + DMA_SEMAPHORE_ADDR);
2848c2ecf20Sopenharmony_ci	writel(0, card->csr_remap + DMA_SEMAPHORE_ADDR + 4);
2858c2ecf20Sopenharmony_ci
2868c2ecf20Sopenharmony_ci	offset = ((char *)desc) - ((char *)page->desc);
2878c2ecf20Sopenharmony_ci	writel(cpu_to_le32((page->page_dma+offset) & 0xffffffff),
2888c2ecf20Sopenharmony_ci	       card->csr_remap + DMA_DESCRIPTOR_ADDR);
2898c2ecf20Sopenharmony_ci	/* Force the value to u64 before shifting otherwise >> 32 is undefined C
2908c2ecf20Sopenharmony_ci	 * and on some ports will do nothing ! */
2918c2ecf20Sopenharmony_ci	writel(cpu_to_le32(((u64)page->page_dma)>>32),
2928c2ecf20Sopenharmony_ci	       card->csr_remap + DMA_DESCRIPTOR_ADDR + 4);
2938c2ecf20Sopenharmony_ci
2948c2ecf20Sopenharmony_ci	/* Go, go, go */
2958c2ecf20Sopenharmony_ci	writel(cpu_to_le32(DMASCR_GO | DMASCR_CHAIN_EN | pci_cmds),
2968c2ecf20Sopenharmony_ci	       card->csr_remap + DMA_STATUS_CTRL);
2978c2ecf20Sopenharmony_ci}
2988c2ecf20Sopenharmony_ci
2998c2ecf20Sopenharmony_cistatic int add_bio(struct cardinfo *card);
3008c2ecf20Sopenharmony_ci
3018c2ecf20Sopenharmony_cistatic void activate(struct cardinfo *card)
3028c2ecf20Sopenharmony_ci{
3038c2ecf20Sopenharmony_ci	/* if No page is Active, and Ready is
3048c2ecf20Sopenharmony_ci	 * not empty, then switch Ready page
3058c2ecf20Sopenharmony_ci	 * to active and start IO.
3068c2ecf20Sopenharmony_ci	 * Then add any bh's that are available to Ready
3078c2ecf20Sopenharmony_ci	 */
3088c2ecf20Sopenharmony_ci
3098c2ecf20Sopenharmony_ci	do {
3108c2ecf20Sopenharmony_ci		while (add_bio(card))
3118c2ecf20Sopenharmony_ci			;
3128c2ecf20Sopenharmony_ci
3138c2ecf20Sopenharmony_ci		if (card->Active == -1 &&
3148c2ecf20Sopenharmony_ci		    card->mm_pages[card->Ready].cnt > 0) {
3158c2ecf20Sopenharmony_ci			card->Active = card->Ready;
3168c2ecf20Sopenharmony_ci			card->Ready = 1-card->Ready;
3178c2ecf20Sopenharmony_ci			mm_start_io(card);
3188c2ecf20Sopenharmony_ci		}
3198c2ecf20Sopenharmony_ci
3208c2ecf20Sopenharmony_ci	} while (card->Active == -1 && add_bio(card));
3218c2ecf20Sopenharmony_ci}
3228c2ecf20Sopenharmony_ci
3238c2ecf20Sopenharmony_cistatic inline void reset_page(struct mm_page *page)
3248c2ecf20Sopenharmony_ci{
3258c2ecf20Sopenharmony_ci	page->cnt = 0;
3268c2ecf20Sopenharmony_ci	page->headcnt = 0;
3278c2ecf20Sopenharmony_ci	page->bio = NULL;
3288c2ecf20Sopenharmony_ci	page->biotail = &page->bio;
3298c2ecf20Sopenharmony_ci}
3308c2ecf20Sopenharmony_ci
3318c2ecf20Sopenharmony_ci/*
3328c2ecf20Sopenharmony_ci * If there is room on Ready page, take
3338c2ecf20Sopenharmony_ci * one bh off list and add it.
3348c2ecf20Sopenharmony_ci * return 1 if there was room, else 0.
3358c2ecf20Sopenharmony_ci */
3368c2ecf20Sopenharmony_cistatic int add_bio(struct cardinfo *card)
3378c2ecf20Sopenharmony_ci{
3388c2ecf20Sopenharmony_ci	struct mm_page *p;
3398c2ecf20Sopenharmony_ci	struct mm_dma_desc *desc;
3408c2ecf20Sopenharmony_ci	dma_addr_t dma_handle;
3418c2ecf20Sopenharmony_ci	int offset;
3428c2ecf20Sopenharmony_ci	struct bio *bio;
3438c2ecf20Sopenharmony_ci	struct bio_vec vec;
3448c2ecf20Sopenharmony_ci
3458c2ecf20Sopenharmony_ci	bio = card->currentbio;
3468c2ecf20Sopenharmony_ci	if (!bio && card->bio) {
3478c2ecf20Sopenharmony_ci		card->currentbio = card->bio;
3488c2ecf20Sopenharmony_ci		card->current_iter = card->bio->bi_iter;
3498c2ecf20Sopenharmony_ci		card->bio = card->bio->bi_next;
3508c2ecf20Sopenharmony_ci		if (card->bio == NULL)
3518c2ecf20Sopenharmony_ci			card->biotail = &card->bio;
3528c2ecf20Sopenharmony_ci		card->currentbio->bi_next = NULL;
3538c2ecf20Sopenharmony_ci		return 1;
3548c2ecf20Sopenharmony_ci	}
3558c2ecf20Sopenharmony_ci	if (!bio)
3568c2ecf20Sopenharmony_ci		return 0;
3578c2ecf20Sopenharmony_ci
3588c2ecf20Sopenharmony_ci	if (card->mm_pages[card->Ready].cnt >= DESC_PER_PAGE)
3598c2ecf20Sopenharmony_ci		return 0;
3608c2ecf20Sopenharmony_ci
3618c2ecf20Sopenharmony_ci	vec = bio_iter_iovec(bio, card->current_iter);
3628c2ecf20Sopenharmony_ci
3638c2ecf20Sopenharmony_ci	dma_handle = dma_map_page(&card->dev->dev,
3648c2ecf20Sopenharmony_ci				  vec.bv_page,
3658c2ecf20Sopenharmony_ci				  vec.bv_offset,
3668c2ecf20Sopenharmony_ci				  vec.bv_len,
3678c2ecf20Sopenharmony_ci				  bio_op(bio) == REQ_OP_READ ?
3688c2ecf20Sopenharmony_ci				  DMA_FROM_DEVICE : DMA_TO_DEVICE);
3698c2ecf20Sopenharmony_ci
3708c2ecf20Sopenharmony_ci	p = &card->mm_pages[card->Ready];
3718c2ecf20Sopenharmony_ci	desc = &p->desc[p->cnt];
3728c2ecf20Sopenharmony_ci	p->cnt++;
3738c2ecf20Sopenharmony_ci	if (p->bio == NULL)
3748c2ecf20Sopenharmony_ci		p->iter = card->current_iter;
3758c2ecf20Sopenharmony_ci	if ((p->biotail) != &bio->bi_next) {
3768c2ecf20Sopenharmony_ci		*(p->biotail) = bio;
3778c2ecf20Sopenharmony_ci		p->biotail = &(bio->bi_next);
3788c2ecf20Sopenharmony_ci		bio->bi_next = NULL;
3798c2ecf20Sopenharmony_ci	}
3808c2ecf20Sopenharmony_ci
3818c2ecf20Sopenharmony_ci	desc->data_dma_handle = dma_handle;
3828c2ecf20Sopenharmony_ci
3838c2ecf20Sopenharmony_ci	desc->pci_addr = cpu_to_le64((u64)desc->data_dma_handle);
3848c2ecf20Sopenharmony_ci	desc->local_addr = cpu_to_le64(card->current_iter.bi_sector << 9);
3858c2ecf20Sopenharmony_ci	desc->transfer_size = cpu_to_le32(vec.bv_len);
3868c2ecf20Sopenharmony_ci	offset = (((char *)&desc->sem_control_bits) - ((char *)p->desc));
3878c2ecf20Sopenharmony_ci	desc->sem_addr = cpu_to_le64((u64)(p->page_dma+offset));
3888c2ecf20Sopenharmony_ci	desc->zero1 = desc->zero2 = 0;
3898c2ecf20Sopenharmony_ci	offset = (((char *)(desc+1)) - ((char *)p->desc));
3908c2ecf20Sopenharmony_ci	desc->next_desc_addr = cpu_to_le64(p->page_dma+offset);
3918c2ecf20Sopenharmony_ci	desc->control_bits = cpu_to_le32(DMASCR_GO|DMASCR_ERR_INT_EN|
3928c2ecf20Sopenharmony_ci					 DMASCR_PARITY_INT_EN|
3938c2ecf20Sopenharmony_ci					 DMASCR_CHAIN_EN |
3948c2ecf20Sopenharmony_ci					 DMASCR_SEM_EN |
3958c2ecf20Sopenharmony_ci					 pci_cmds);
3968c2ecf20Sopenharmony_ci	if (bio_op(bio) == REQ_OP_WRITE)
3978c2ecf20Sopenharmony_ci		desc->control_bits |= cpu_to_le32(DMASCR_TRANSFER_READ);
3988c2ecf20Sopenharmony_ci	desc->sem_control_bits = desc->control_bits;
3998c2ecf20Sopenharmony_ci
4008c2ecf20Sopenharmony_ci
4018c2ecf20Sopenharmony_ci	bio_advance_iter(bio, &card->current_iter, vec.bv_len);
4028c2ecf20Sopenharmony_ci	if (!card->current_iter.bi_size)
4038c2ecf20Sopenharmony_ci		card->currentbio = NULL;
4048c2ecf20Sopenharmony_ci
4058c2ecf20Sopenharmony_ci	return 1;
4068c2ecf20Sopenharmony_ci}
4078c2ecf20Sopenharmony_ci
4088c2ecf20Sopenharmony_cistatic void process_page(unsigned long data)
4098c2ecf20Sopenharmony_ci{
4108c2ecf20Sopenharmony_ci	/* check if any of the requests in the page are DMA_COMPLETE,
4118c2ecf20Sopenharmony_ci	 * and deal with them appropriately.
4128c2ecf20Sopenharmony_ci	 * If we find a descriptor without DMA_COMPLETE in the semaphore, then
4138c2ecf20Sopenharmony_ci	 * dma must have hit an error on that descriptor, so use dma_status
4148c2ecf20Sopenharmony_ci	 * instead and assume that all following descriptors must be re-tried.
4158c2ecf20Sopenharmony_ci	 */
4168c2ecf20Sopenharmony_ci	struct mm_page *page;
4178c2ecf20Sopenharmony_ci	struct bio *return_bio = NULL;
4188c2ecf20Sopenharmony_ci	struct cardinfo *card = (struct cardinfo *)data;
4198c2ecf20Sopenharmony_ci	unsigned int dma_status = card->dma_status;
4208c2ecf20Sopenharmony_ci
4218c2ecf20Sopenharmony_ci	spin_lock(&card->lock);
4228c2ecf20Sopenharmony_ci	if (card->Active < 0)
4238c2ecf20Sopenharmony_ci		goto out_unlock;
4248c2ecf20Sopenharmony_ci	page = &card->mm_pages[card->Active];
4258c2ecf20Sopenharmony_ci
4268c2ecf20Sopenharmony_ci	while (page->headcnt < page->cnt) {
4278c2ecf20Sopenharmony_ci		struct bio *bio = page->bio;
4288c2ecf20Sopenharmony_ci		struct mm_dma_desc *desc = &page->desc[page->headcnt];
4298c2ecf20Sopenharmony_ci		int control = le32_to_cpu(desc->sem_control_bits);
4308c2ecf20Sopenharmony_ci		int last = 0;
4318c2ecf20Sopenharmony_ci		struct bio_vec vec;
4328c2ecf20Sopenharmony_ci
4338c2ecf20Sopenharmony_ci		if (!(control & DMASCR_DMA_COMPLETE)) {
4348c2ecf20Sopenharmony_ci			control = dma_status;
4358c2ecf20Sopenharmony_ci			last = 1;
4368c2ecf20Sopenharmony_ci		}
4378c2ecf20Sopenharmony_ci
4388c2ecf20Sopenharmony_ci		page->headcnt++;
4398c2ecf20Sopenharmony_ci		vec = bio_iter_iovec(bio, page->iter);
4408c2ecf20Sopenharmony_ci		bio_advance_iter(bio, &page->iter, vec.bv_len);
4418c2ecf20Sopenharmony_ci
4428c2ecf20Sopenharmony_ci		if (!page->iter.bi_size) {
4438c2ecf20Sopenharmony_ci			page->bio = bio->bi_next;
4448c2ecf20Sopenharmony_ci			if (page->bio)
4458c2ecf20Sopenharmony_ci				page->iter = page->bio->bi_iter;
4468c2ecf20Sopenharmony_ci		}
4478c2ecf20Sopenharmony_ci
4488c2ecf20Sopenharmony_ci		dma_unmap_page(&card->dev->dev, desc->data_dma_handle,
4498c2ecf20Sopenharmony_ci			       vec.bv_len,
4508c2ecf20Sopenharmony_ci				 (control & DMASCR_TRANSFER_READ) ?
4518c2ecf20Sopenharmony_ci				DMA_TO_DEVICE : DMA_FROM_DEVICE);
4528c2ecf20Sopenharmony_ci		if (control & DMASCR_HARD_ERROR) {
4538c2ecf20Sopenharmony_ci			/* error */
4548c2ecf20Sopenharmony_ci			bio->bi_status = BLK_STS_IOERR;
4558c2ecf20Sopenharmony_ci			dev_printk(KERN_WARNING, &card->dev->dev,
4568c2ecf20Sopenharmony_ci				"I/O error on sector %d/%d\n",
4578c2ecf20Sopenharmony_ci				le32_to_cpu(desc->local_addr)>>9,
4588c2ecf20Sopenharmony_ci				le32_to_cpu(desc->transfer_size));
4598c2ecf20Sopenharmony_ci			dump_dmastat(card, control);
4608c2ecf20Sopenharmony_ci		} else if (op_is_write(bio_op(bio)) &&
4618c2ecf20Sopenharmony_ci			   le32_to_cpu(desc->local_addr) >> 9 ==
4628c2ecf20Sopenharmony_ci				card->init_size) {
4638c2ecf20Sopenharmony_ci			card->init_size += le32_to_cpu(desc->transfer_size) >> 9;
4648c2ecf20Sopenharmony_ci			if (card->init_size >> 1 >= card->mm_size) {
4658c2ecf20Sopenharmony_ci				dev_printk(KERN_INFO, &card->dev->dev,
4668c2ecf20Sopenharmony_ci					"memory now initialised\n");
4678c2ecf20Sopenharmony_ci				set_userbit(card, MEMORY_INITIALIZED, 1);
4688c2ecf20Sopenharmony_ci			}
4698c2ecf20Sopenharmony_ci		}
4708c2ecf20Sopenharmony_ci		if (bio != page->bio) {
4718c2ecf20Sopenharmony_ci			bio->bi_next = return_bio;
4728c2ecf20Sopenharmony_ci			return_bio = bio;
4738c2ecf20Sopenharmony_ci		}
4748c2ecf20Sopenharmony_ci
4758c2ecf20Sopenharmony_ci		if (last)
4768c2ecf20Sopenharmony_ci			break;
4778c2ecf20Sopenharmony_ci	}
4788c2ecf20Sopenharmony_ci
4798c2ecf20Sopenharmony_ci	if (debug & DEBUG_LED_ON_TRANSFER)
4808c2ecf20Sopenharmony_ci		set_led(card, LED_REMOVE, LED_OFF);
4818c2ecf20Sopenharmony_ci
4828c2ecf20Sopenharmony_ci	if (card->check_batteries) {
4838c2ecf20Sopenharmony_ci		card->check_batteries = 0;
4848c2ecf20Sopenharmony_ci		check_batteries(card);
4858c2ecf20Sopenharmony_ci	}
4868c2ecf20Sopenharmony_ci	if (page->headcnt >= page->cnt) {
4878c2ecf20Sopenharmony_ci		reset_page(page);
4888c2ecf20Sopenharmony_ci		card->Active = -1;
4898c2ecf20Sopenharmony_ci		activate(card);
4908c2ecf20Sopenharmony_ci	} else {
4918c2ecf20Sopenharmony_ci		/* haven't finished with this one yet */
4928c2ecf20Sopenharmony_ci		pr_debug("do some more\n");
4938c2ecf20Sopenharmony_ci		mm_start_io(card);
4948c2ecf20Sopenharmony_ci	}
4958c2ecf20Sopenharmony_ci out_unlock:
4968c2ecf20Sopenharmony_ci	spin_unlock(&card->lock);
4978c2ecf20Sopenharmony_ci
4988c2ecf20Sopenharmony_ci	while (return_bio) {
4998c2ecf20Sopenharmony_ci		struct bio *bio = return_bio;
5008c2ecf20Sopenharmony_ci
5018c2ecf20Sopenharmony_ci		return_bio = bio->bi_next;
5028c2ecf20Sopenharmony_ci		bio->bi_next = NULL;
5038c2ecf20Sopenharmony_ci		bio_endio(bio);
5048c2ecf20Sopenharmony_ci	}
5058c2ecf20Sopenharmony_ci}
5068c2ecf20Sopenharmony_ci
5078c2ecf20Sopenharmony_cistatic void mm_unplug(struct blk_plug_cb *cb, bool from_schedule)
5088c2ecf20Sopenharmony_ci{
5098c2ecf20Sopenharmony_ci	struct cardinfo *card = cb->data;
5108c2ecf20Sopenharmony_ci
5118c2ecf20Sopenharmony_ci	spin_lock_irq(&card->lock);
5128c2ecf20Sopenharmony_ci	activate(card);
5138c2ecf20Sopenharmony_ci	spin_unlock_irq(&card->lock);
5148c2ecf20Sopenharmony_ci	kfree(cb);
5158c2ecf20Sopenharmony_ci}
5168c2ecf20Sopenharmony_ci
5178c2ecf20Sopenharmony_cistatic int mm_check_plugged(struct cardinfo *card)
5188c2ecf20Sopenharmony_ci{
5198c2ecf20Sopenharmony_ci	return !!blk_check_plugged(mm_unplug, card, sizeof(struct blk_plug_cb));
5208c2ecf20Sopenharmony_ci}
5218c2ecf20Sopenharmony_ci
5228c2ecf20Sopenharmony_cistatic blk_qc_t mm_submit_bio(struct bio *bio)
5238c2ecf20Sopenharmony_ci{
5248c2ecf20Sopenharmony_ci	struct cardinfo *card = bio->bi_disk->private_data;
5258c2ecf20Sopenharmony_ci
5268c2ecf20Sopenharmony_ci	pr_debug("mm_make_request %llu %u\n",
5278c2ecf20Sopenharmony_ci		 (unsigned long long)bio->bi_iter.bi_sector,
5288c2ecf20Sopenharmony_ci		 bio->bi_iter.bi_size);
5298c2ecf20Sopenharmony_ci
5308c2ecf20Sopenharmony_ci	blk_queue_split(&bio);
5318c2ecf20Sopenharmony_ci
5328c2ecf20Sopenharmony_ci	spin_lock_irq(&card->lock);
5338c2ecf20Sopenharmony_ci	*card->biotail = bio;
5348c2ecf20Sopenharmony_ci	bio->bi_next = NULL;
5358c2ecf20Sopenharmony_ci	card->biotail = &bio->bi_next;
5368c2ecf20Sopenharmony_ci	if (op_is_sync(bio->bi_opf) || !mm_check_plugged(card))
5378c2ecf20Sopenharmony_ci		activate(card);
5388c2ecf20Sopenharmony_ci	spin_unlock_irq(&card->lock);
5398c2ecf20Sopenharmony_ci
5408c2ecf20Sopenharmony_ci	return BLK_QC_T_NONE;
5418c2ecf20Sopenharmony_ci}
5428c2ecf20Sopenharmony_ci
5438c2ecf20Sopenharmony_cistatic irqreturn_t mm_interrupt(int irq, void *__card)
5448c2ecf20Sopenharmony_ci{
5458c2ecf20Sopenharmony_ci	struct cardinfo *card = (struct cardinfo *) __card;
5468c2ecf20Sopenharmony_ci	unsigned int dma_status;
5478c2ecf20Sopenharmony_ci	unsigned short cfg_status;
5488c2ecf20Sopenharmony_ci
5498c2ecf20Sopenharmony_ciHW_TRACE(0x30);
5508c2ecf20Sopenharmony_ci
5518c2ecf20Sopenharmony_ci	dma_status = le32_to_cpu(readl(card->csr_remap + DMA_STATUS_CTRL));
5528c2ecf20Sopenharmony_ci
5538c2ecf20Sopenharmony_ci	if (!(dma_status & (DMASCR_ERROR_MASK | DMASCR_CHAIN_COMPLETE))) {
5548c2ecf20Sopenharmony_ci		/* interrupt wasn't for me ... */
5558c2ecf20Sopenharmony_ci		return IRQ_NONE;
5568c2ecf20Sopenharmony_ci	}
5578c2ecf20Sopenharmony_ci
5588c2ecf20Sopenharmony_ci	/* clear COMPLETION interrupts */
5598c2ecf20Sopenharmony_ci	if (card->flags & UM_FLAG_NO_BYTE_STATUS)
5608c2ecf20Sopenharmony_ci		writel(cpu_to_le32(DMASCR_DMA_COMPLETE|DMASCR_CHAIN_COMPLETE),
5618c2ecf20Sopenharmony_ci		       card->csr_remap + DMA_STATUS_CTRL);
5628c2ecf20Sopenharmony_ci	else
5638c2ecf20Sopenharmony_ci		writeb((DMASCR_DMA_COMPLETE|DMASCR_CHAIN_COMPLETE) >> 16,
5648c2ecf20Sopenharmony_ci		       card->csr_remap + DMA_STATUS_CTRL + 2);
5658c2ecf20Sopenharmony_ci
5668c2ecf20Sopenharmony_ci	/* log errors and clear interrupt status */
5678c2ecf20Sopenharmony_ci	if (dma_status & DMASCR_ANY_ERR) {
5688c2ecf20Sopenharmony_ci		unsigned int	data_log1, data_log2;
5698c2ecf20Sopenharmony_ci		unsigned int	addr_log1, addr_log2;
5708c2ecf20Sopenharmony_ci		unsigned char	stat, count, syndrome, check;
5718c2ecf20Sopenharmony_ci
5728c2ecf20Sopenharmony_ci		stat = readb(card->csr_remap + MEMCTRLCMD_ERRSTATUS);
5738c2ecf20Sopenharmony_ci
5748c2ecf20Sopenharmony_ci		data_log1 = le32_to_cpu(readl(card->csr_remap +
5758c2ecf20Sopenharmony_ci						ERROR_DATA_LOG));
5768c2ecf20Sopenharmony_ci		data_log2 = le32_to_cpu(readl(card->csr_remap +
5778c2ecf20Sopenharmony_ci						ERROR_DATA_LOG + 4));
5788c2ecf20Sopenharmony_ci		addr_log1 = le32_to_cpu(readl(card->csr_remap +
5798c2ecf20Sopenharmony_ci						ERROR_ADDR_LOG));
5808c2ecf20Sopenharmony_ci		addr_log2 = readb(card->csr_remap + ERROR_ADDR_LOG + 4);
5818c2ecf20Sopenharmony_ci
5828c2ecf20Sopenharmony_ci		count = readb(card->csr_remap + ERROR_COUNT);
5838c2ecf20Sopenharmony_ci		syndrome = readb(card->csr_remap + ERROR_SYNDROME);
5848c2ecf20Sopenharmony_ci		check = readb(card->csr_remap + ERROR_CHECK);
5858c2ecf20Sopenharmony_ci
5868c2ecf20Sopenharmony_ci		dump_dmastat(card, dma_status);
5878c2ecf20Sopenharmony_ci
5888c2ecf20Sopenharmony_ci		if (stat & 0x01)
5898c2ecf20Sopenharmony_ci			dev_printk(KERN_ERR, &card->dev->dev,
5908c2ecf20Sopenharmony_ci				"Memory access error detected (err count %d)\n",
5918c2ecf20Sopenharmony_ci				count);
5928c2ecf20Sopenharmony_ci		if (stat & 0x02)
5938c2ecf20Sopenharmony_ci			dev_printk(KERN_ERR, &card->dev->dev,
5948c2ecf20Sopenharmony_ci				"Multi-bit EDC error\n");
5958c2ecf20Sopenharmony_ci
5968c2ecf20Sopenharmony_ci		dev_printk(KERN_ERR, &card->dev->dev,
5978c2ecf20Sopenharmony_ci			"Fault Address 0x%02x%08x, Fault Data 0x%08x%08x\n",
5988c2ecf20Sopenharmony_ci			addr_log2, addr_log1, data_log2, data_log1);
5998c2ecf20Sopenharmony_ci		dev_printk(KERN_ERR, &card->dev->dev,
6008c2ecf20Sopenharmony_ci			"Fault Check 0x%02x, Fault Syndrome 0x%02x\n",
6018c2ecf20Sopenharmony_ci			check, syndrome);
6028c2ecf20Sopenharmony_ci
6038c2ecf20Sopenharmony_ci		writeb(0, card->csr_remap + ERROR_COUNT);
6048c2ecf20Sopenharmony_ci	}
6058c2ecf20Sopenharmony_ci
6068c2ecf20Sopenharmony_ci	if (dma_status & DMASCR_PARITY_ERR_REP) {
6078c2ecf20Sopenharmony_ci		dev_printk(KERN_ERR, &card->dev->dev,
6088c2ecf20Sopenharmony_ci			"PARITY ERROR REPORTED\n");
6098c2ecf20Sopenharmony_ci		pci_read_config_word(card->dev, PCI_STATUS, &cfg_status);
6108c2ecf20Sopenharmony_ci		pci_write_config_word(card->dev, PCI_STATUS, cfg_status);
6118c2ecf20Sopenharmony_ci	}
6128c2ecf20Sopenharmony_ci
6138c2ecf20Sopenharmony_ci	if (dma_status & DMASCR_PARITY_ERR_DET) {
6148c2ecf20Sopenharmony_ci		dev_printk(KERN_ERR, &card->dev->dev,
6158c2ecf20Sopenharmony_ci			"PARITY ERROR DETECTED\n");
6168c2ecf20Sopenharmony_ci		pci_read_config_word(card->dev, PCI_STATUS, &cfg_status);
6178c2ecf20Sopenharmony_ci		pci_write_config_word(card->dev, PCI_STATUS, cfg_status);
6188c2ecf20Sopenharmony_ci	}
6198c2ecf20Sopenharmony_ci
6208c2ecf20Sopenharmony_ci	if (dma_status & DMASCR_SYSTEM_ERR_SIG) {
6218c2ecf20Sopenharmony_ci		dev_printk(KERN_ERR, &card->dev->dev, "SYSTEM ERROR\n");
6228c2ecf20Sopenharmony_ci		pci_read_config_word(card->dev, PCI_STATUS, &cfg_status);
6238c2ecf20Sopenharmony_ci		pci_write_config_word(card->dev, PCI_STATUS, cfg_status);
6248c2ecf20Sopenharmony_ci	}
6258c2ecf20Sopenharmony_ci
6268c2ecf20Sopenharmony_ci	if (dma_status & DMASCR_TARGET_ABT) {
6278c2ecf20Sopenharmony_ci		dev_printk(KERN_ERR, &card->dev->dev, "TARGET ABORT\n");
6288c2ecf20Sopenharmony_ci		pci_read_config_word(card->dev, PCI_STATUS, &cfg_status);
6298c2ecf20Sopenharmony_ci		pci_write_config_word(card->dev, PCI_STATUS, cfg_status);
6308c2ecf20Sopenharmony_ci	}
6318c2ecf20Sopenharmony_ci
6328c2ecf20Sopenharmony_ci	if (dma_status & DMASCR_MASTER_ABT) {
6338c2ecf20Sopenharmony_ci		dev_printk(KERN_ERR, &card->dev->dev, "MASTER ABORT\n");
6348c2ecf20Sopenharmony_ci		pci_read_config_word(card->dev, PCI_STATUS, &cfg_status);
6358c2ecf20Sopenharmony_ci		pci_write_config_word(card->dev, PCI_STATUS, cfg_status);
6368c2ecf20Sopenharmony_ci	}
6378c2ecf20Sopenharmony_ci
6388c2ecf20Sopenharmony_ci	/* and process the DMA descriptors */
6398c2ecf20Sopenharmony_ci	card->dma_status = dma_status;
6408c2ecf20Sopenharmony_ci	tasklet_schedule(&card->tasklet);
6418c2ecf20Sopenharmony_ci
6428c2ecf20Sopenharmony_ciHW_TRACE(0x36);
6438c2ecf20Sopenharmony_ci
6448c2ecf20Sopenharmony_ci	return IRQ_HANDLED;
6458c2ecf20Sopenharmony_ci}
6468c2ecf20Sopenharmony_ci
6478c2ecf20Sopenharmony_ci/*
6488c2ecf20Sopenharmony_ci * If both batteries are good, no LED
6498c2ecf20Sopenharmony_ci * If either battery has been warned, solid LED
6508c2ecf20Sopenharmony_ci * If both batteries are bad, flash the LED quickly
6518c2ecf20Sopenharmony_ci * If either battery is bad, flash the LED semi quickly
6528c2ecf20Sopenharmony_ci */
6538c2ecf20Sopenharmony_cistatic void set_fault_to_battery_status(struct cardinfo *card)
6548c2ecf20Sopenharmony_ci{
6558c2ecf20Sopenharmony_ci	if (card->battery[0].good && card->battery[1].good)
6568c2ecf20Sopenharmony_ci		set_led(card, LED_FAULT, LED_OFF);
6578c2ecf20Sopenharmony_ci	else if (card->battery[0].warned || card->battery[1].warned)
6588c2ecf20Sopenharmony_ci		set_led(card, LED_FAULT, LED_ON);
6598c2ecf20Sopenharmony_ci	else if (!card->battery[0].good && !card->battery[1].good)
6608c2ecf20Sopenharmony_ci		set_led(card, LED_FAULT, LED_FLASH_7_0);
6618c2ecf20Sopenharmony_ci	else
6628c2ecf20Sopenharmony_ci		set_led(card, LED_FAULT, LED_FLASH_3_5);
6638c2ecf20Sopenharmony_ci}
6648c2ecf20Sopenharmony_ci
6658c2ecf20Sopenharmony_cistatic void init_battery_timer(void);
6668c2ecf20Sopenharmony_ci
6678c2ecf20Sopenharmony_cistatic int check_battery(struct cardinfo *card, int battery, int status)
6688c2ecf20Sopenharmony_ci{
6698c2ecf20Sopenharmony_ci	if (status != card->battery[battery].good) {
6708c2ecf20Sopenharmony_ci		card->battery[battery].good = !card->battery[battery].good;
6718c2ecf20Sopenharmony_ci		card->battery[battery].last_change = jiffies;
6728c2ecf20Sopenharmony_ci
6738c2ecf20Sopenharmony_ci		if (card->battery[battery].good) {
6748c2ecf20Sopenharmony_ci			dev_printk(KERN_ERR, &card->dev->dev,
6758c2ecf20Sopenharmony_ci				"Battery %d now good\n", battery + 1);
6768c2ecf20Sopenharmony_ci			card->battery[battery].warned = 0;
6778c2ecf20Sopenharmony_ci		} else
6788c2ecf20Sopenharmony_ci			dev_printk(KERN_ERR, &card->dev->dev,
6798c2ecf20Sopenharmony_ci				"Battery %d now FAILED\n", battery + 1);
6808c2ecf20Sopenharmony_ci
6818c2ecf20Sopenharmony_ci		return 1;
6828c2ecf20Sopenharmony_ci	} else if (!card->battery[battery].good &&
6838c2ecf20Sopenharmony_ci		   !card->battery[battery].warned &&
6848c2ecf20Sopenharmony_ci		   time_after_eq(jiffies, card->battery[battery].last_change +
6858c2ecf20Sopenharmony_ci				 (HZ * 60 * 60 * 5))) {
6868c2ecf20Sopenharmony_ci		dev_printk(KERN_ERR, &card->dev->dev,
6878c2ecf20Sopenharmony_ci			"Battery %d still FAILED after 5 hours\n", battery + 1);
6888c2ecf20Sopenharmony_ci		card->battery[battery].warned = 1;
6898c2ecf20Sopenharmony_ci
6908c2ecf20Sopenharmony_ci		return 1;
6918c2ecf20Sopenharmony_ci	}
6928c2ecf20Sopenharmony_ci
6938c2ecf20Sopenharmony_ci	return 0;
6948c2ecf20Sopenharmony_ci}
6958c2ecf20Sopenharmony_ci
6968c2ecf20Sopenharmony_cistatic void check_batteries(struct cardinfo *card)
6978c2ecf20Sopenharmony_ci{
6988c2ecf20Sopenharmony_ci	/* NOTE: this must *never* be called while the card
6998c2ecf20Sopenharmony_ci	 * is doing (bus-to-card) DMA, or you will need the
7008c2ecf20Sopenharmony_ci	 * reset switch
7018c2ecf20Sopenharmony_ci	 */
7028c2ecf20Sopenharmony_ci	unsigned char status;
7038c2ecf20Sopenharmony_ci	int ret1, ret2;
7048c2ecf20Sopenharmony_ci
7058c2ecf20Sopenharmony_ci	status = readb(card->csr_remap + MEMCTRLSTATUS_BATTERY);
7068c2ecf20Sopenharmony_ci	if (debug & DEBUG_BATTERY_POLLING)
7078c2ecf20Sopenharmony_ci		dev_printk(KERN_DEBUG, &card->dev->dev,
7088c2ecf20Sopenharmony_ci			"checking battery status, 1 = %s, 2 = %s\n",
7098c2ecf20Sopenharmony_ci		       (status & BATTERY_1_FAILURE) ? "FAILURE" : "OK",
7108c2ecf20Sopenharmony_ci		       (status & BATTERY_2_FAILURE) ? "FAILURE" : "OK");
7118c2ecf20Sopenharmony_ci
7128c2ecf20Sopenharmony_ci	ret1 = check_battery(card, 0, !(status & BATTERY_1_FAILURE));
7138c2ecf20Sopenharmony_ci	ret2 = check_battery(card, 1, !(status & BATTERY_2_FAILURE));
7148c2ecf20Sopenharmony_ci
7158c2ecf20Sopenharmony_ci	if (ret1 || ret2)
7168c2ecf20Sopenharmony_ci		set_fault_to_battery_status(card);
7178c2ecf20Sopenharmony_ci}
7188c2ecf20Sopenharmony_ci
7198c2ecf20Sopenharmony_cistatic void check_all_batteries(struct timer_list *unused)
7208c2ecf20Sopenharmony_ci{
7218c2ecf20Sopenharmony_ci	int i;
7228c2ecf20Sopenharmony_ci
7238c2ecf20Sopenharmony_ci	for (i = 0; i < num_cards; i++)
7248c2ecf20Sopenharmony_ci		if (!(cards[i].flags & UM_FLAG_NO_BATT)) {
7258c2ecf20Sopenharmony_ci			struct cardinfo *card = &cards[i];
7268c2ecf20Sopenharmony_ci			spin_lock_bh(&card->lock);
7278c2ecf20Sopenharmony_ci			if (card->Active >= 0)
7288c2ecf20Sopenharmony_ci				card->check_batteries = 1;
7298c2ecf20Sopenharmony_ci			else
7308c2ecf20Sopenharmony_ci				check_batteries(card);
7318c2ecf20Sopenharmony_ci			spin_unlock_bh(&card->lock);
7328c2ecf20Sopenharmony_ci		}
7338c2ecf20Sopenharmony_ci
7348c2ecf20Sopenharmony_ci	init_battery_timer();
7358c2ecf20Sopenharmony_ci}
7368c2ecf20Sopenharmony_ci
7378c2ecf20Sopenharmony_cistatic void init_battery_timer(void)
7388c2ecf20Sopenharmony_ci{
7398c2ecf20Sopenharmony_ci	timer_setup(&battery_timer, check_all_batteries, 0);
7408c2ecf20Sopenharmony_ci	battery_timer.expires = jiffies + (HZ * 60);
7418c2ecf20Sopenharmony_ci	add_timer(&battery_timer);
7428c2ecf20Sopenharmony_ci}
7438c2ecf20Sopenharmony_ci
7448c2ecf20Sopenharmony_cistatic void del_battery_timer(void)
7458c2ecf20Sopenharmony_ci{
7468c2ecf20Sopenharmony_ci	del_timer(&battery_timer);
7478c2ecf20Sopenharmony_ci}
7488c2ecf20Sopenharmony_ci
7498c2ecf20Sopenharmony_ci/*
7508c2ecf20Sopenharmony_ci * Note no locks taken out here.  In a worst case scenario, we could drop
7518c2ecf20Sopenharmony_ci * a chunk of system memory.  But that should never happen, since validation
7528c2ecf20Sopenharmony_ci * happens at open or mount time, when locks are held.
7538c2ecf20Sopenharmony_ci *
7548c2ecf20Sopenharmony_ci *	That's crap, since doing that while some partitions are opened
7558c2ecf20Sopenharmony_ci * or mounted will give you really nasty results.
7568c2ecf20Sopenharmony_ci */
7578c2ecf20Sopenharmony_cistatic int mm_revalidate(struct gendisk *disk)
7588c2ecf20Sopenharmony_ci{
7598c2ecf20Sopenharmony_ci	struct cardinfo *card = disk->private_data;
7608c2ecf20Sopenharmony_ci	set_capacity(disk, card->mm_size << 1);
7618c2ecf20Sopenharmony_ci	return 0;
7628c2ecf20Sopenharmony_ci}
7638c2ecf20Sopenharmony_ci
7648c2ecf20Sopenharmony_cistatic int mm_getgeo(struct block_device *bdev, struct hd_geometry *geo)
7658c2ecf20Sopenharmony_ci{
7668c2ecf20Sopenharmony_ci	struct cardinfo *card = bdev->bd_disk->private_data;
7678c2ecf20Sopenharmony_ci	int size = card->mm_size * (1024 / MM_HARDSECT);
7688c2ecf20Sopenharmony_ci
7698c2ecf20Sopenharmony_ci	/*
7708c2ecf20Sopenharmony_ci	 * get geometry: we have to fake one...  trim the size to a
7718c2ecf20Sopenharmony_ci	 * multiple of 2048 (1M): tell we have 32 sectors, 64 heads,
7728c2ecf20Sopenharmony_ci	 * whatever cylinders.
7738c2ecf20Sopenharmony_ci	 */
7748c2ecf20Sopenharmony_ci	geo->heads     = 64;
7758c2ecf20Sopenharmony_ci	geo->sectors   = 32;
7768c2ecf20Sopenharmony_ci	geo->cylinders = size / (geo->heads * geo->sectors);
7778c2ecf20Sopenharmony_ci	return 0;
7788c2ecf20Sopenharmony_ci}
7798c2ecf20Sopenharmony_ci
7808c2ecf20Sopenharmony_cistatic const struct block_device_operations mm_fops = {
7818c2ecf20Sopenharmony_ci	.owner		= THIS_MODULE,
7828c2ecf20Sopenharmony_ci	.submit_bio	= mm_submit_bio,
7838c2ecf20Sopenharmony_ci	.getgeo		= mm_getgeo,
7848c2ecf20Sopenharmony_ci	.revalidate_disk = mm_revalidate,
7858c2ecf20Sopenharmony_ci};
7868c2ecf20Sopenharmony_ci
7878c2ecf20Sopenharmony_cistatic int mm_pci_probe(struct pci_dev *dev, const struct pci_device_id *id)
7888c2ecf20Sopenharmony_ci{
7898c2ecf20Sopenharmony_ci	int ret;
7908c2ecf20Sopenharmony_ci	struct cardinfo *card = &cards[num_cards];
7918c2ecf20Sopenharmony_ci	unsigned char	mem_present;
7928c2ecf20Sopenharmony_ci	unsigned char	batt_status;
7938c2ecf20Sopenharmony_ci	unsigned int	saved_bar, data;
7948c2ecf20Sopenharmony_ci	unsigned long	csr_base;
7958c2ecf20Sopenharmony_ci	unsigned long	csr_len;
7968c2ecf20Sopenharmony_ci	int		magic_number;
7978c2ecf20Sopenharmony_ci	static int	printed_version;
7988c2ecf20Sopenharmony_ci
7998c2ecf20Sopenharmony_ci	if (!printed_version++)
8008c2ecf20Sopenharmony_ci		printk(KERN_INFO DRIVER_VERSION " : " DRIVER_DESC "\n");
8018c2ecf20Sopenharmony_ci
8028c2ecf20Sopenharmony_ci	ret = pci_enable_device(dev);
8038c2ecf20Sopenharmony_ci	if (ret)
8048c2ecf20Sopenharmony_ci		return ret;
8058c2ecf20Sopenharmony_ci
8068c2ecf20Sopenharmony_ci	pci_write_config_byte(dev, PCI_LATENCY_TIMER, 0xF8);
8078c2ecf20Sopenharmony_ci	pci_set_master(dev);
8088c2ecf20Sopenharmony_ci
8098c2ecf20Sopenharmony_ci	card->dev         = dev;
8108c2ecf20Sopenharmony_ci
8118c2ecf20Sopenharmony_ci	csr_base = pci_resource_start(dev, 0);
8128c2ecf20Sopenharmony_ci	csr_len  = pci_resource_len(dev, 0);
8138c2ecf20Sopenharmony_ci	if (!csr_base || !csr_len)
8148c2ecf20Sopenharmony_ci		return -ENODEV;
8158c2ecf20Sopenharmony_ci
8168c2ecf20Sopenharmony_ci	dev_printk(KERN_INFO, &dev->dev,
8178c2ecf20Sopenharmony_ci	  "Micro Memory(tm) controller found (PCI Mem Module (Battery Backup))\n");
8188c2ecf20Sopenharmony_ci
8198c2ecf20Sopenharmony_ci	if (dma_set_mask(&dev->dev, DMA_BIT_MASK(64)) &&
8208c2ecf20Sopenharmony_ci	    dma_set_mask(&dev->dev, DMA_BIT_MASK(32))) {
8218c2ecf20Sopenharmony_ci		dev_printk(KERN_WARNING, &dev->dev, "NO suitable DMA found\n");
8228c2ecf20Sopenharmony_ci		return  -ENOMEM;
8238c2ecf20Sopenharmony_ci	}
8248c2ecf20Sopenharmony_ci
8258c2ecf20Sopenharmony_ci	ret = pci_request_regions(dev, DRIVER_NAME);
8268c2ecf20Sopenharmony_ci	if (ret) {
8278c2ecf20Sopenharmony_ci		dev_printk(KERN_ERR, &card->dev->dev,
8288c2ecf20Sopenharmony_ci			"Unable to request memory region\n");
8298c2ecf20Sopenharmony_ci		goto failed_req_csr;
8308c2ecf20Sopenharmony_ci	}
8318c2ecf20Sopenharmony_ci
8328c2ecf20Sopenharmony_ci	card->csr_remap = ioremap(csr_base, csr_len);
8338c2ecf20Sopenharmony_ci	if (!card->csr_remap) {
8348c2ecf20Sopenharmony_ci		dev_printk(KERN_ERR, &card->dev->dev,
8358c2ecf20Sopenharmony_ci			"Unable to remap memory region\n");
8368c2ecf20Sopenharmony_ci		ret = -ENOMEM;
8378c2ecf20Sopenharmony_ci
8388c2ecf20Sopenharmony_ci		goto failed_remap_csr;
8398c2ecf20Sopenharmony_ci	}
8408c2ecf20Sopenharmony_ci
8418c2ecf20Sopenharmony_ci	dev_printk(KERN_INFO, &card->dev->dev,
8428c2ecf20Sopenharmony_ci		"CSR 0x%08lx -> 0x%p (0x%lx)\n",
8438c2ecf20Sopenharmony_ci	       csr_base, card->csr_remap, csr_len);
8448c2ecf20Sopenharmony_ci
8458c2ecf20Sopenharmony_ci	switch (card->dev->device) {
8468c2ecf20Sopenharmony_ci	case 0x5415:
8478c2ecf20Sopenharmony_ci		card->flags |= UM_FLAG_NO_BYTE_STATUS | UM_FLAG_NO_BATTREG;
8488c2ecf20Sopenharmony_ci		magic_number = 0x59;
8498c2ecf20Sopenharmony_ci		break;
8508c2ecf20Sopenharmony_ci
8518c2ecf20Sopenharmony_ci	case 0x5425:
8528c2ecf20Sopenharmony_ci		card->flags |= UM_FLAG_NO_BYTE_STATUS;
8538c2ecf20Sopenharmony_ci		magic_number = 0x5C;
8548c2ecf20Sopenharmony_ci		break;
8558c2ecf20Sopenharmony_ci
8568c2ecf20Sopenharmony_ci	case 0x6155:
8578c2ecf20Sopenharmony_ci		card->flags |= UM_FLAG_NO_BYTE_STATUS |
8588c2ecf20Sopenharmony_ci				UM_FLAG_NO_BATTREG | UM_FLAG_NO_BATT;
8598c2ecf20Sopenharmony_ci		magic_number = 0x99;
8608c2ecf20Sopenharmony_ci		break;
8618c2ecf20Sopenharmony_ci
8628c2ecf20Sopenharmony_ci	default:
8638c2ecf20Sopenharmony_ci		magic_number = 0x100;
8648c2ecf20Sopenharmony_ci		break;
8658c2ecf20Sopenharmony_ci	}
8668c2ecf20Sopenharmony_ci
8678c2ecf20Sopenharmony_ci	if (readb(card->csr_remap + MEMCTRLSTATUS_MAGIC) != magic_number) {
8688c2ecf20Sopenharmony_ci		dev_printk(KERN_ERR, &card->dev->dev, "Magic number invalid\n");
8698c2ecf20Sopenharmony_ci		ret = -ENOMEM;
8708c2ecf20Sopenharmony_ci		goto failed_magic;
8718c2ecf20Sopenharmony_ci	}
8728c2ecf20Sopenharmony_ci
8738c2ecf20Sopenharmony_ci	card->mm_pages[0].desc = dma_alloc_coherent(&card->dev->dev,
8748c2ecf20Sopenharmony_ci			PAGE_SIZE * 2, &card->mm_pages[0].page_dma, GFP_KERNEL);
8758c2ecf20Sopenharmony_ci	card->mm_pages[1].desc = dma_alloc_coherent(&card->dev->dev,
8768c2ecf20Sopenharmony_ci			PAGE_SIZE * 2, &card->mm_pages[1].page_dma, GFP_KERNEL);
8778c2ecf20Sopenharmony_ci	if (card->mm_pages[0].desc == NULL ||
8788c2ecf20Sopenharmony_ci	    card->mm_pages[1].desc == NULL) {
8798c2ecf20Sopenharmony_ci		dev_printk(KERN_ERR, &card->dev->dev, "alloc failed\n");
8808c2ecf20Sopenharmony_ci		ret = -ENOMEM;
8818c2ecf20Sopenharmony_ci		goto failed_alloc;
8828c2ecf20Sopenharmony_ci	}
8838c2ecf20Sopenharmony_ci	reset_page(&card->mm_pages[0]);
8848c2ecf20Sopenharmony_ci	reset_page(&card->mm_pages[1]);
8858c2ecf20Sopenharmony_ci	card->Ready = 0;	/* page 0 is ready */
8868c2ecf20Sopenharmony_ci	card->Active = -1;	/* no page is active */
8878c2ecf20Sopenharmony_ci	card->bio = NULL;
8888c2ecf20Sopenharmony_ci	card->biotail = &card->bio;
8898c2ecf20Sopenharmony_ci	spin_lock_init(&card->lock);
8908c2ecf20Sopenharmony_ci
8918c2ecf20Sopenharmony_ci	card->queue = blk_alloc_queue(NUMA_NO_NODE);
8928c2ecf20Sopenharmony_ci	if (!card->queue) {
8938c2ecf20Sopenharmony_ci		ret = -ENOMEM;
8948c2ecf20Sopenharmony_ci		goto failed_alloc;
8958c2ecf20Sopenharmony_ci	}
8968c2ecf20Sopenharmony_ci
8978c2ecf20Sopenharmony_ci	tasklet_init(&card->tasklet, process_page, (unsigned long)card);
8988c2ecf20Sopenharmony_ci
8998c2ecf20Sopenharmony_ci	card->check_batteries = 0;
9008c2ecf20Sopenharmony_ci
9018c2ecf20Sopenharmony_ci	mem_present = readb(card->csr_remap + MEMCTRLSTATUS_MEMORY);
9028c2ecf20Sopenharmony_ci	switch (mem_present) {
9038c2ecf20Sopenharmony_ci	case MEM_128_MB:
9048c2ecf20Sopenharmony_ci		card->mm_size = 1024 * 128;
9058c2ecf20Sopenharmony_ci		break;
9068c2ecf20Sopenharmony_ci	case MEM_256_MB:
9078c2ecf20Sopenharmony_ci		card->mm_size = 1024 * 256;
9088c2ecf20Sopenharmony_ci		break;
9098c2ecf20Sopenharmony_ci	case MEM_512_MB:
9108c2ecf20Sopenharmony_ci		card->mm_size = 1024 * 512;
9118c2ecf20Sopenharmony_ci		break;
9128c2ecf20Sopenharmony_ci	case MEM_1_GB:
9138c2ecf20Sopenharmony_ci		card->mm_size = 1024 * 1024;
9148c2ecf20Sopenharmony_ci		break;
9158c2ecf20Sopenharmony_ci	case MEM_2_GB:
9168c2ecf20Sopenharmony_ci		card->mm_size = 1024 * 2048;
9178c2ecf20Sopenharmony_ci		break;
9188c2ecf20Sopenharmony_ci	default:
9198c2ecf20Sopenharmony_ci		card->mm_size = 0;
9208c2ecf20Sopenharmony_ci		break;
9218c2ecf20Sopenharmony_ci	}
9228c2ecf20Sopenharmony_ci
9238c2ecf20Sopenharmony_ci	/* Clear the LED's we control */
9248c2ecf20Sopenharmony_ci	set_led(card, LED_REMOVE, LED_OFF);
9258c2ecf20Sopenharmony_ci	set_led(card, LED_FAULT, LED_OFF);
9268c2ecf20Sopenharmony_ci
9278c2ecf20Sopenharmony_ci	batt_status = readb(card->csr_remap + MEMCTRLSTATUS_BATTERY);
9288c2ecf20Sopenharmony_ci
9298c2ecf20Sopenharmony_ci	card->battery[0].good = !(batt_status & BATTERY_1_FAILURE);
9308c2ecf20Sopenharmony_ci	card->battery[1].good = !(batt_status & BATTERY_2_FAILURE);
9318c2ecf20Sopenharmony_ci	card->battery[0].last_change = card->battery[1].last_change = jiffies;
9328c2ecf20Sopenharmony_ci
9338c2ecf20Sopenharmony_ci	if (card->flags & UM_FLAG_NO_BATT)
9348c2ecf20Sopenharmony_ci		dev_printk(KERN_INFO, &card->dev->dev,
9358c2ecf20Sopenharmony_ci			"Size %d KB\n", card->mm_size);
9368c2ecf20Sopenharmony_ci	else {
9378c2ecf20Sopenharmony_ci		dev_printk(KERN_INFO, &card->dev->dev,
9388c2ecf20Sopenharmony_ci			"Size %d KB, Battery 1 %s (%s), Battery 2 %s (%s)\n",
9398c2ecf20Sopenharmony_ci		       card->mm_size,
9408c2ecf20Sopenharmony_ci		       batt_status & BATTERY_1_DISABLED ? "Disabled" : "Enabled",
9418c2ecf20Sopenharmony_ci		       card->battery[0].good ? "OK" : "FAILURE",
9428c2ecf20Sopenharmony_ci		       batt_status & BATTERY_2_DISABLED ? "Disabled" : "Enabled",
9438c2ecf20Sopenharmony_ci		       card->battery[1].good ? "OK" : "FAILURE");
9448c2ecf20Sopenharmony_ci
9458c2ecf20Sopenharmony_ci		set_fault_to_battery_status(card);
9468c2ecf20Sopenharmony_ci	}
9478c2ecf20Sopenharmony_ci
9488c2ecf20Sopenharmony_ci	pci_read_config_dword(dev, PCI_BASE_ADDRESS_1, &saved_bar);
9498c2ecf20Sopenharmony_ci	data = 0xffffffff;
9508c2ecf20Sopenharmony_ci	pci_write_config_dword(dev, PCI_BASE_ADDRESS_1, data);
9518c2ecf20Sopenharmony_ci	pci_read_config_dword(dev, PCI_BASE_ADDRESS_1, &data);
9528c2ecf20Sopenharmony_ci	pci_write_config_dword(dev, PCI_BASE_ADDRESS_1, saved_bar);
9538c2ecf20Sopenharmony_ci	data &= 0xfffffff0;
9548c2ecf20Sopenharmony_ci	data = ~data;
9558c2ecf20Sopenharmony_ci	data += 1;
9568c2ecf20Sopenharmony_ci
9578c2ecf20Sopenharmony_ci	if (request_irq(dev->irq, mm_interrupt, IRQF_SHARED, DRIVER_NAME,
9588c2ecf20Sopenharmony_ci			card)) {
9598c2ecf20Sopenharmony_ci		dev_printk(KERN_ERR, &card->dev->dev,
9608c2ecf20Sopenharmony_ci			"Unable to allocate IRQ\n");
9618c2ecf20Sopenharmony_ci		ret = -ENODEV;
9628c2ecf20Sopenharmony_ci		goto failed_req_irq;
9638c2ecf20Sopenharmony_ci	}
9648c2ecf20Sopenharmony_ci
9658c2ecf20Sopenharmony_ci	dev_printk(KERN_INFO, &card->dev->dev,
9668c2ecf20Sopenharmony_ci		"Window size %d bytes, IRQ %d\n", data, dev->irq);
9678c2ecf20Sopenharmony_ci
9688c2ecf20Sopenharmony_ci	pci_set_drvdata(dev, card);
9698c2ecf20Sopenharmony_ci
9708c2ecf20Sopenharmony_ci	if (pci_write_cmd != 0x0F) 	/* If not Memory Write & Invalidate */
9718c2ecf20Sopenharmony_ci		pci_write_cmd = 0x07;	/* then Memory Write command */
9728c2ecf20Sopenharmony_ci
9738c2ecf20Sopenharmony_ci	if (pci_write_cmd & 0x08) { /* use Memory Write and Invalidate */
9748c2ecf20Sopenharmony_ci		unsigned short cfg_command;
9758c2ecf20Sopenharmony_ci		pci_read_config_word(dev, PCI_COMMAND, &cfg_command);
9768c2ecf20Sopenharmony_ci		cfg_command |= 0x10; /* Memory Write & Invalidate Enable */
9778c2ecf20Sopenharmony_ci		pci_write_config_word(dev, PCI_COMMAND, cfg_command);
9788c2ecf20Sopenharmony_ci	}
9798c2ecf20Sopenharmony_ci	pci_cmds = (pci_read_cmd << 28) | (pci_write_cmd << 24);
9808c2ecf20Sopenharmony_ci
9818c2ecf20Sopenharmony_ci	num_cards++;
9828c2ecf20Sopenharmony_ci
9838c2ecf20Sopenharmony_ci	if (!get_userbit(card, MEMORY_INITIALIZED)) {
9848c2ecf20Sopenharmony_ci		dev_printk(KERN_INFO, &card->dev->dev,
9858c2ecf20Sopenharmony_ci		  "memory NOT initialized. Consider over-writing whole device.\n");
9868c2ecf20Sopenharmony_ci		card->init_size = 0;
9878c2ecf20Sopenharmony_ci	} else {
9888c2ecf20Sopenharmony_ci		dev_printk(KERN_INFO, &card->dev->dev,
9898c2ecf20Sopenharmony_ci			"memory already initialized\n");
9908c2ecf20Sopenharmony_ci		card->init_size = card->mm_size;
9918c2ecf20Sopenharmony_ci	}
9928c2ecf20Sopenharmony_ci
9938c2ecf20Sopenharmony_ci	/* Enable ECC */
9948c2ecf20Sopenharmony_ci	writeb(EDC_STORE_CORRECT, card->csr_remap + MEMCTRLCMD_ERRCTRL);
9958c2ecf20Sopenharmony_ci
9968c2ecf20Sopenharmony_ci	return 0;
9978c2ecf20Sopenharmony_ci
9988c2ecf20Sopenharmony_ci failed_req_irq:
9998c2ecf20Sopenharmony_ci failed_alloc:
10008c2ecf20Sopenharmony_ci	if (card->mm_pages[0].desc)
10018c2ecf20Sopenharmony_ci		dma_free_coherent(&card->dev->dev, PAGE_SIZE * 2,
10028c2ecf20Sopenharmony_ci				  card->mm_pages[0].desc,
10038c2ecf20Sopenharmony_ci				  card->mm_pages[0].page_dma);
10048c2ecf20Sopenharmony_ci	if (card->mm_pages[1].desc)
10058c2ecf20Sopenharmony_ci		dma_free_coherent(&card->dev->dev, PAGE_SIZE * 2,
10068c2ecf20Sopenharmony_ci				  card->mm_pages[1].desc,
10078c2ecf20Sopenharmony_ci				  card->mm_pages[1].page_dma);
10088c2ecf20Sopenharmony_ci failed_magic:
10098c2ecf20Sopenharmony_ci	iounmap(card->csr_remap);
10108c2ecf20Sopenharmony_ci failed_remap_csr:
10118c2ecf20Sopenharmony_ci	pci_release_regions(dev);
10128c2ecf20Sopenharmony_ci failed_req_csr:
10138c2ecf20Sopenharmony_ci
10148c2ecf20Sopenharmony_ci	return ret;
10158c2ecf20Sopenharmony_ci}
10168c2ecf20Sopenharmony_ci
10178c2ecf20Sopenharmony_cistatic void mm_pci_remove(struct pci_dev *dev)
10188c2ecf20Sopenharmony_ci{
10198c2ecf20Sopenharmony_ci	struct cardinfo *card = pci_get_drvdata(dev);
10208c2ecf20Sopenharmony_ci
10218c2ecf20Sopenharmony_ci	tasklet_kill(&card->tasklet);
10228c2ecf20Sopenharmony_ci	free_irq(dev->irq, card);
10238c2ecf20Sopenharmony_ci	iounmap(card->csr_remap);
10248c2ecf20Sopenharmony_ci
10258c2ecf20Sopenharmony_ci	if (card->mm_pages[0].desc)
10268c2ecf20Sopenharmony_ci		dma_free_coherent(&card->dev->dev, PAGE_SIZE * 2,
10278c2ecf20Sopenharmony_ci				    card->mm_pages[0].desc,
10288c2ecf20Sopenharmony_ci				    card->mm_pages[0].page_dma);
10298c2ecf20Sopenharmony_ci	if (card->mm_pages[1].desc)
10308c2ecf20Sopenharmony_ci		dma_free_coherent(&card->dev->dev, PAGE_SIZE * 2,
10318c2ecf20Sopenharmony_ci				    card->mm_pages[1].desc,
10328c2ecf20Sopenharmony_ci				    card->mm_pages[1].page_dma);
10338c2ecf20Sopenharmony_ci	blk_cleanup_queue(card->queue);
10348c2ecf20Sopenharmony_ci
10358c2ecf20Sopenharmony_ci	pci_release_regions(dev);
10368c2ecf20Sopenharmony_ci	pci_disable_device(dev);
10378c2ecf20Sopenharmony_ci}
10388c2ecf20Sopenharmony_ci
10398c2ecf20Sopenharmony_cistatic const struct pci_device_id mm_pci_ids[] = {
10408c2ecf20Sopenharmony_ci    {PCI_DEVICE(PCI_VENDOR_ID_MICRO_MEMORY, PCI_DEVICE_ID_MICRO_MEMORY_5415CN)},
10418c2ecf20Sopenharmony_ci    {PCI_DEVICE(PCI_VENDOR_ID_MICRO_MEMORY, PCI_DEVICE_ID_MICRO_MEMORY_5425CN)},
10428c2ecf20Sopenharmony_ci    {PCI_DEVICE(PCI_VENDOR_ID_MICRO_MEMORY, PCI_DEVICE_ID_MICRO_MEMORY_6155)},
10438c2ecf20Sopenharmony_ci    {
10448c2ecf20Sopenharmony_ci	.vendor	=	0x8086,
10458c2ecf20Sopenharmony_ci	.device	=	0xB555,
10468c2ecf20Sopenharmony_ci	.subvendor =	0x1332,
10478c2ecf20Sopenharmony_ci	.subdevice =	0x5460,
10488c2ecf20Sopenharmony_ci	.class =	0x050000,
10498c2ecf20Sopenharmony_ci	.class_mask =	0,
10508c2ecf20Sopenharmony_ci    }, { /* end: all zeroes */ }
10518c2ecf20Sopenharmony_ci};
10528c2ecf20Sopenharmony_ci
10538c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(pci, mm_pci_ids);
10548c2ecf20Sopenharmony_ci
10558c2ecf20Sopenharmony_cistatic struct pci_driver mm_pci_driver = {
10568c2ecf20Sopenharmony_ci	.name		= DRIVER_NAME,
10578c2ecf20Sopenharmony_ci	.id_table	= mm_pci_ids,
10588c2ecf20Sopenharmony_ci	.probe		= mm_pci_probe,
10598c2ecf20Sopenharmony_ci	.remove		= mm_pci_remove,
10608c2ecf20Sopenharmony_ci};
10618c2ecf20Sopenharmony_ci
10628c2ecf20Sopenharmony_cistatic int __init mm_init(void)
10638c2ecf20Sopenharmony_ci{
10648c2ecf20Sopenharmony_ci	int retval, i;
10658c2ecf20Sopenharmony_ci	int err;
10668c2ecf20Sopenharmony_ci
10678c2ecf20Sopenharmony_ci	retval = pci_register_driver(&mm_pci_driver);
10688c2ecf20Sopenharmony_ci	if (retval)
10698c2ecf20Sopenharmony_ci		return -ENOMEM;
10708c2ecf20Sopenharmony_ci
10718c2ecf20Sopenharmony_ci	err = major_nr = register_blkdev(0, DRIVER_NAME);
10728c2ecf20Sopenharmony_ci	if (err < 0) {
10738c2ecf20Sopenharmony_ci		pci_unregister_driver(&mm_pci_driver);
10748c2ecf20Sopenharmony_ci		return -EIO;
10758c2ecf20Sopenharmony_ci	}
10768c2ecf20Sopenharmony_ci
10778c2ecf20Sopenharmony_ci	for (i = 0; i < num_cards; i++) {
10788c2ecf20Sopenharmony_ci		mm_gendisk[i] = alloc_disk(1 << MM_SHIFT);
10798c2ecf20Sopenharmony_ci		if (!mm_gendisk[i])
10808c2ecf20Sopenharmony_ci			goto out;
10818c2ecf20Sopenharmony_ci	}
10828c2ecf20Sopenharmony_ci
10838c2ecf20Sopenharmony_ci	for (i = 0; i < num_cards; i++) {
10848c2ecf20Sopenharmony_ci		struct gendisk *disk = mm_gendisk[i];
10858c2ecf20Sopenharmony_ci		sprintf(disk->disk_name, "umem%c", 'a'+i);
10868c2ecf20Sopenharmony_ci		spin_lock_init(&cards[i].lock);
10878c2ecf20Sopenharmony_ci		disk->major = major_nr;
10888c2ecf20Sopenharmony_ci		disk->first_minor  = i << MM_SHIFT;
10898c2ecf20Sopenharmony_ci		disk->fops = &mm_fops;
10908c2ecf20Sopenharmony_ci		disk->private_data = &cards[i];
10918c2ecf20Sopenharmony_ci		disk->queue = cards[i].queue;
10928c2ecf20Sopenharmony_ci		set_capacity(disk, cards[i].mm_size << 1);
10938c2ecf20Sopenharmony_ci		add_disk(disk);
10948c2ecf20Sopenharmony_ci	}
10958c2ecf20Sopenharmony_ci
10968c2ecf20Sopenharmony_ci	init_battery_timer();
10978c2ecf20Sopenharmony_ci	printk(KERN_INFO "MM: desc_per_page = %ld\n", DESC_PER_PAGE);
10988c2ecf20Sopenharmony_ci/* printk("mm_init: Done. 10-19-01 9:00\n"); */
10998c2ecf20Sopenharmony_ci	return 0;
11008c2ecf20Sopenharmony_ci
11018c2ecf20Sopenharmony_ciout:
11028c2ecf20Sopenharmony_ci	pci_unregister_driver(&mm_pci_driver);
11038c2ecf20Sopenharmony_ci	unregister_blkdev(major_nr, DRIVER_NAME);
11048c2ecf20Sopenharmony_ci	while (i--)
11058c2ecf20Sopenharmony_ci		put_disk(mm_gendisk[i]);
11068c2ecf20Sopenharmony_ci	return -ENOMEM;
11078c2ecf20Sopenharmony_ci}
11088c2ecf20Sopenharmony_ci
11098c2ecf20Sopenharmony_cistatic void __exit mm_cleanup(void)
11108c2ecf20Sopenharmony_ci{
11118c2ecf20Sopenharmony_ci	int i;
11128c2ecf20Sopenharmony_ci
11138c2ecf20Sopenharmony_ci	del_battery_timer();
11148c2ecf20Sopenharmony_ci
11158c2ecf20Sopenharmony_ci	for (i = 0; i < num_cards ; i++) {
11168c2ecf20Sopenharmony_ci		del_gendisk(mm_gendisk[i]);
11178c2ecf20Sopenharmony_ci		put_disk(mm_gendisk[i]);
11188c2ecf20Sopenharmony_ci	}
11198c2ecf20Sopenharmony_ci
11208c2ecf20Sopenharmony_ci	pci_unregister_driver(&mm_pci_driver);
11218c2ecf20Sopenharmony_ci
11228c2ecf20Sopenharmony_ci	unregister_blkdev(major_nr, DRIVER_NAME);
11238c2ecf20Sopenharmony_ci}
11248c2ecf20Sopenharmony_ci
11258c2ecf20Sopenharmony_cimodule_init(mm_init);
11268c2ecf20Sopenharmony_cimodule_exit(mm_cleanup);
11278c2ecf20Sopenharmony_ci
11288c2ecf20Sopenharmony_ciMODULE_AUTHOR(DRIVER_AUTHOR);
11298c2ecf20Sopenharmony_ciMODULE_DESCRIPTION(DRIVER_DESC);
11308c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL");
1131