 16735d022f
			
		
	
	
	16735d022f
	
	
	
		
			
			Use this new function to make code more comprehensible, since we are reinitialzing the completion, not initializing. [akpm@linux-foundation.org: linux-next resyncs] Signed-off-by: Wolfram Sang <wsa@the-dreams.de> Acked-by: Linus Walleij <linus.walleij@linaro.org> (personally at LCE13) Cc: Ingo Molnar <mingo@kernel.org> Signed-off-by: Andrew Morton <akpm@linux-foundation.org> Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
		
			
				
	
	
		
			896 lines
		
	
	
	
		
			21 KiB
			
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			896 lines
		
	
	
	
		
			21 KiB
			
		
	
	
	
		
			C
		
	
	
	
	
	
| /*
 | |
|  * Copyright (C) 2010 - Maxim Levitsky
 | |
|  * driver for Ricoh memstick readers
 | |
|  *
 | |
|  * This program 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.
 | |
|  */
 | |
| 
 | |
| #include <linux/kernel.h>
 | |
| #include <linux/module.h>
 | |
| #include <linux/freezer.h>
 | |
| #include <linux/jiffies.h>
 | |
| #include <linux/interrupt.h>
 | |
| #include <linux/pci.h>
 | |
| #include <linux/pci_ids.h>
 | |
| #include <linux/delay.h>
 | |
| #include <linux/slab.h>
 | |
| #include <linux/kthread.h>
 | |
| #include <linux/sched.h>
 | |
| #include <linux/highmem.h>
 | |
| #include <asm/byteorder.h>
 | |
| #include <linux/swab.h>
 | |
| #include "r592.h"
 | |
| 
 | |
| static bool r592_enable_dma = 1;
 | |
| static int debug;
 | |
| 
 | |
| static const char *tpc_names[] = {
 | |
| 	"MS_TPC_READ_MG_STATUS",
 | |
| 	"MS_TPC_READ_LONG_DATA",
 | |
| 	"MS_TPC_READ_SHORT_DATA",
 | |
| 	"MS_TPC_READ_REG",
 | |
| 	"MS_TPC_READ_QUAD_DATA",
 | |
| 	"INVALID",
 | |
| 	"MS_TPC_GET_INT",
 | |
| 	"MS_TPC_SET_RW_REG_ADRS",
 | |
| 	"MS_TPC_EX_SET_CMD",
 | |
| 	"MS_TPC_WRITE_QUAD_DATA",
 | |
| 	"MS_TPC_WRITE_REG",
 | |
| 	"MS_TPC_WRITE_SHORT_DATA",
 | |
| 	"MS_TPC_WRITE_LONG_DATA",
 | |
| 	"MS_TPC_SET_CMD",
 | |
| };
 | |
| 
 | |
| /**
 | |
|  * memstick_debug_get_tpc_name - debug helper that returns string for
 | |
|  * a TPC number
 | |
|  */
 | |
| const char *memstick_debug_get_tpc_name(int tpc)
 | |
| {
 | |
| 	return tpc_names[tpc-1];
 | |
| }
 | |
| EXPORT_SYMBOL(memstick_debug_get_tpc_name);
 | |
| 
 | |
| 
 | |
| /* Read a register*/
 | |
| static inline u32 r592_read_reg(struct r592_device *dev, int address)
 | |
| {
 | |
| 	u32 value = readl(dev->mmio + address);
 | |
| 	dbg_reg("reg #%02d == 0x%08x", address, value);
 | |
| 	return value;
 | |
| }
 | |
| 
 | |
| /* Write a register */
 | |
| static inline void r592_write_reg(struct r592_device *dev,
 | |
| 							int address, u32 value)
 | |
| {
 | |
| 	dbg_reg("reg #%02d <- 0x%08x", address, value);
 | |
| 	writel(value, dev->mmio + address);
 | |
| }
 | |
| 
 | |
| /* Reads a big endian DWORD register */
 | |
| static inline u32 r592_read_reg_raw_be(struct r592_device *dev, int address)
 | |
| {
 | |
| 	u32 value = __raw_readl(dev->mmio + address);
 | |
| 	dbg_reg("reg #%02d == 0x%08x", address, value);
 | |
| 	return be32_to_cpu(value);
 | |
| }
 | |
| 
 | |
| /* Writes a big endian DWORD register */
 | |
| static inline void r592_write_reg_raw_be(struct r592_device *dev,
 | |
| 							int address, u32 value)
 | |
| {
 | |
| 	dbg_reg("reg #%02d <- 0x%08x", address, value);
 | |
| 	__raw_writel(cpu_to_be32(value), dev->mmio + address);
 | |
| }
 | |
| 
 | |
| /* Set specific bits in a register (little endian) */
 | |
| static inline void r592_set_reg_mask(struct r592_device *dev,
 | |
| 							int address, u32 mask)
 | |
| {
 | |
| 	u32 reg = readl(dev->mmio + address);
 | |
| 	dbg_reg("reg #%02d |= 0x%08x (old =0x%08x)", address, mask, reg);
 | |
| 	writel(reg | mask , dev->mmio + address);
 | |
| }
 | |
| 
 | |
| /* Clear specific bits in a register (little endian) */
 | |
| static inline void r592_clear_reg_mask(struct r592_device *dev,
 | |
| 						int address, u32 mask)
 | |
| {
 | |
| 	u32 reg = readl(dev->mmio + address);
 | |
| 	dbg_reg("reg #%02d &= 0x%08x (old = 0x%08x, mask = 0x%08x)",
 | |
| 						address, ~mask, reg, mask);
 | |
| 	writel(reg & ~mask, dev->mmio + address);
 | |
| }
 | |
| 
 | |
| 
 | |
| /* Wait for status bits while checking for errors */
 | |
| static int r592_wait_status(struct r592_device *dev, u32 mask, u32 wanted_mask)
 | |
| {
 | |
| 	unsigned long timeout = jiffies + msecs_to_jiffies(1000);
 | |
| 	u32 reg = r592_read_reg(dev, R592_STATUS);
 | |
| 
 | |
| 	if ((reg & mask) == wanted_mask)
 | |
| 		return 0;
 | |
| 
 | |
| 	while (time_before(jiffies, timeout)) {
 | |
| 
 | |
| 		reg = r592_read_reg(dev, R592_STATUS);
 | |
| 
 | |
| 		if ((reg & mask) == wanted_mask)
 | |
| 			return 0;
 | |
| 
 | |
| 		if (reg & (R592_STATUS_SEND_ERR | R592_STATUS_RECV_ERR))
 | |
| 			return -EIO;
 | |
| 
 | |
| 		cpu_relax();
 | |
| 	}
 | |
| 	return -ETIME;
 | |
| }
 | |
| 
 | |
| 
 | |
| /* Enable/disable device */
 | |
| static int r592_enable_device(struct r592_device *dev, bool enable)
 | |
| {
 | |
| 	dbg("%sabling the device", enable ? "en" : "dis");
 | |
| 
 | |
| 	if (enable) {
 | |
| 
 | |
| 		/* Power up the card */
 | |
| 		r592_write_reg(dev, R592_POWER, R592_POWER_0 | R592_POWER_1);
 | |
| 
 | |
| 		/* Perform a reset */
 | |
| 		r592_set_reg_mask(dev, R592_IO, R592_IO_RESET);
 | |
| 
 | |
| 		msleep(100);
 | |
| 	} else
 | |
| 		/* Power down the card */
 | |
| 		r592_write_reg(dev, R592_POWER, 0);
 | |
| 
 | |
| 	return 0;
 | |
| }
 | |
| 
 | |
| /* Set serial/parallel mode */
 | |
| static int r592_set_mode(struct r592_device *dev, bool parallel_mode)
 | |
| {
 | |
| 	if (!parallel_mode) {
 | |
| 		dbg("switching to serial mode");
 | |
| 
 | |
| 		/* Set serial mode */
 | |
| 		r592_write_reg(dev, R592_IO_MODE, R592_IO_MODE_SERIAL);
 | |
| 
 | |
| 		r592_clear_reg_mask(dev, R592_POWER, R592_POWER_20);
 | |
| 
 | |
| 	} else {
 | |
| 		dbg("switching to parallel mode");
 | |
| 
 | |
| 		/* This setting should be set _before_ switch TPC */
 | |
| 		r592_set_reg_mask(dev, R592_POWER, R592_POWER_20);
 | |
| 
 | |
| 		r592_clear_reg_mask(dev, R592_IO,
 | |
| 			R592_IO_SERIAL1 | R592_IO_SERIAL2);
 | |
| 
 | |
| 		/* Set the parallel mode now */
 | |
| 		r592_write_reg(dev, R592_IO_MODE, R592_IO_MODE_PARALLEL);
 | |
| 	}
 | |
| 
 | |
| 	dev->parallel_mode = parallel_mode;
 | |
| 	return 0;
 | |
| }
 | |
| 
 | |
| /* Perform a controller reset without powering down the card */
 | |
| static void r592_host_reset(struct r592_device *dev)
 | |
| {
 | |
| 	r592_set_reg_mask(dev, R592_IO, R592_IO_RESET);
 | |
| 	msleep(100);
 | |
| 	r592_set_mode(dev, dev->parallel_mode);
 | |
| }
 | |
| 
 | |
| /* Disable all hardware interrupts */
 | |
| static void r592_clear_interrupts(struct r592_device *dev)
 | |
| {
 | |
| 	/* Disable & ACK all interrupts */
 | |
| 	r592_clear_reg_mask(dev, R592_REG_MSC, IRQ_ALL_ACK_MASK);
 | |
| 	r592_clear_reg_mask(dev, R592_REG_MSC, IRQ_ALL_EN_MASK);
 | |
| }
 | |
| 
 | |
| /* Tests if there is an CRC error */
 | |
| static int r592_test_io_error(struct r592_device *dev)
 | |
| {
 | |
| 	if (!(r592_read_reg(dev, R592_STATUS) &
 | |
| 		(R592_STATUS_SEND_ERR | R592_STATUS_RECV_ERR)))
 | |
| 		return 0;
 | |
| 
 | |
| 	return -EIO;
 | |
| }
 | |
| 
 | |
| /* Ensure that FIFO is ready for use */
 | |
| static int r592_test_fifo_empty(struct r592_device *dev)
 | |
| {
 | |
| 	if (r592_read_reg(dev, R592_REG_MSC) & R592_REG_MSC_FIFO_EMPTY)
 | |
| 		return 0;
 | |
| 
 | |
| 	dbg("FIFO not ready, trying to reset the device");
 | |
| 	r592_host_reset(dev);
 | |
| 
 | |
| 	if (r592_read_reg(dev, R592_REG_MSC) & R592_REG_MSC_FIFO_EMPTY)
 | |
| 		return 0;
 | |
| 
 | |
| 	message("FIFO still not ready, giving up");
 | |
| 	return -EIO;
 | |
| }
 | |
| 
 | |
| /* Activates the DMA transfer from to FIFO */
 | |
| static void r592_start_dma(struct r592_device *dev, bool is_write)
 | |
| {
 | |
| 	unsigned long flags;
 | |
| 	u32 reg;
 | |
| 	spin_lock_irqsave(&dev->irq_lock, flags);
 | |
| 
 | |
| 	/* Ack interrupts (just in case) + enable them */
 | |
| 	r592_clear_reg_mask(dev, R592_REG_MSC, DMA_IRQ_ACK_MASK);
 | |
| 	r592_set_reg_mask(dev, R592_REG_MSC, DMA_IRQ_EN_MASK);
 | |
| 
 | |
| 	/* Set DMA address */
 | |
| 	r592_write_reg(dev, R592_FIFO_DMA, sg_dma_address(&dev->req->sg));
 | |
| 
 | |
| 	/* Enable the DMA */
 | |
| 	reg = r592_read_reg(dev, R592_FIFO_DMA_SETTINGS);
 | |
| 	reg |= R592_FIFO_DMA_SETTINGS_EN;
 | |
| 
 | |
| 	if (!is_write)
 | |
| 		reg |= R592_FIFO_DMA_SETTINGS_DIR;
 | |
| 	else
 | |
| 		reg &= ~R592_FIFO_DMA_SETTINGS_DIR;
 | |
| 	r592_write_reg(dev, R592_FIFO_DMA_SETTINGS, reg);
 | |
| 
 | |
| 	spin_unlock_irqrestore(&dev->irq_lock, flags);
 | |
| }
 | |
| 
 | |
| /* Cleanups DMA related settings */
 | |
| static void r592_stop_dma(struct r592_device *dev, int error)
 | |
| {
 | |
| 	r592_clear_reg_mask(dev, R592_FIFO_DMA_SETTINGS,
 | |
| 		R592_FIFO_DMA_SETTINGS_EN);
 | |
| 
 | |
| 	/* This is only a precation */
 | |
| 	r592_write_reg(dev, R592_FIFO_DMA,
 | |
| 			dev->dummy_dma_page_physical_address);
 | |
| 
 | |
| 	r592_clear_reg_mask(dev, R592_REG_MSC, DMA_IRQ_EN_MASK);
 | |
| 	r592_clear_reg_mask(dev, R592_REG_MSC, DMA_IRQ_ACK_MASK);
 | |
| 	dev->dma_error = error;
 | |
| }
 | |
| 
 | |
| /* Test if hardware supports DMA */
 | |
| static void r592_check_dma(struct r592_device *dev)
 | |
| {
 | |
| 	dev->dma_capable = r592_enable_dma &&
 | |
| 		(r592_read_reg(dev, R592_FIFO_DMA_SETTINGS) &
 | |
| 			R592_FIFO_DMA_SETTINGS_CAP);
 | |
| }
 | |
| 
 | |
| /* Transfers fifo contents in/out using DMA */
 | |
| static int r592_transfer_fifo_dma(struct r592_device *dev)
 | |
| {
 | |
| 	int len, sg_count;
 | |
| 	bool is_write;
 | |
| 
 | |
| 	if (!dev->dma_capable || !dev->req->long_data)
 | |
| 		return -EINVAL;
 | |
| 
 | |
| 	len = dev->req->sg.length;
 | |
| 	is_write = dev->req->data_dir == WRITE;
 | |
| 
 | |
| 	if (len != R592_LFIFO_SIZE)
 | |
| 		return -EINVAL;
 | |
| 
 | |
| 	dbg_verbose("doing dma transfer");
 | |
| 
 | |
| 	dev->dma_error = 0;
 | |
| 	reinit_completion(&dev->dma_done);
 | |
| 
 | |
| 	/* TODO: hidden assumption about nenth beeing always 1 */
 | |
| 	sg_count = dma_map_sg(&dev->pci_dev->dev, &dev->req->sg, 1, is_write ?
 | |
| 		PCI_DMA_TODEVICE : PCI_DMA_FROMDEVICE);
 | |
| 
 | |
| 	if (sg_count != 1 ||
 | |
| 			(sg_dma_len(&dev->req->sg) < dev->req->sg.length)) {
 | |
| 		message("problem in dma_map_sg");
 | |
| 		return -EIO;
 | |
| 	}
 | |
| 
 | |
| 	r592_start_dma(dev, is_write);
 | |
| 
 | |
| 	/* Wait for DMA completion */
 | |
| 	if (!wait_for_completion_timeout(
 | |
| 			&dev->dma_done, msecs_to_jiffies(1000))) {
 | |
| 		message("DMA timeout");
 | |
| 		r592_stop_dma(dev, -ETIMEDOUT);
 | |
| 	}
 | |
| 
 | |
| 	dma_unmap_sg(&dev->pci_dev->dev, &dev->req->sg, 1, is_write ?
 | |
| 		PCI_DMA_TODEVICE : PCI_DMA_FROMDEVICE);
 | |
| 
 | |
| 
 | |
| 	return dev->dma_error;
 | |
| }
 | |
| 
 | |
| /*
 | |
|  * Writes the FIFO in 4 byte chunks.
 | |
|  * If length isn't 4 byte aligned, rest of the data if put to a fifo
 | |
|  * to be written later
 | |
|  * Use r592_flush_fifo_write to flush that fifo when writing for the
 | |
|  * last time
 | |
|  */
 | |
| static void r592_write_fifo_pio(struct r592_device *dev,
 | |
| 					unsigned char *buffer, int len)
 | |
| {
 | |
| 	/* flush spill from former write */
 | |
| 	if (!kfifo_is_empty(&dev->pio_fifo)) {
 | |
| 
 | |
| 		u8 tmp[4] = {0};
 | |
| 		int copy_len = kfifo_in(&dev->pio_fifo, buffer, len);
 | |
| 
 | |
| 		if (!kfifo_is_full(&dev->pio_fifo))
 | |
| 			return;
 | |
| 		len -= copy_len;
 | |
| 		buffer += copy_len;
 | |
| 
 | |
| 		copy_len = kfifo_out(&dev->pio_fifo, tmp, 4);
 | |
| 		WARN_ON(copy_len != 4);
 | |
| 		r592_write_reg_raw_be(dev, R592_FIFO_PIO, *(u32 *)tmp);
 | |
| 	}
 | |
| 
 | |
| 	WARN_ON(!kfifo_is_empty(&dev->pio_fifo));
 | |
| 
 | |
| 	/* write full dwords */
 | |
| 	while (len >= 4) {
 | |
| 		r592_write_reg_raw_be(dev, R592_FIFO_PIO, *(u32 *)buffer);
 | |
| 		buffer += 4;
 | |
| 		len -= 4;
 | |
| 	}
 | |
| 
 | |
| 	/* put remaining bytes to the spill */
 | |
| 	if (len)
 | |
| 		kfifo_in(&dev->pio_fifo, buffer, len);
 | |
| }
 | |
| 
 | |
| /* Flushes the temporary FIFO used to make aligned DWORD writes */
 | |
| static void r592_flush_fifo_write(struct r592_device *dev)
 | |
| {
 | |
| 	u8 buffer[4] = { 0 };
 | |
| 	int len;
 | |
| 
 | |
| 	if (kfifo_is_empty(&dev->pio_fifo))
 | |
| 		return;
 | |
| 
 | |
| 	len = kfifo_out(&dev->pio_fifo, buffer, 4);
 | |
| 	r592_write_reg_raw_be(dev, R592_FIFO_PIO, *(u32 *)buffer);
 | |
| }
 | |
| 
 | |
| /*
 | |
|  * Read a fifo in 4 bytes chunks.
 | |
|  * If input doesn't fit the buffer, it places bytes of last dword in spill
 | |
|  * buffer, so that they don't get lost on last read, just throw these away.
 | |
|  */
 | |
| static void r592_read_fifo_pio(struct r592_device *dev,
 | |
| 						unsigned char *buffer, int len)
 | |
| {
 | |
| 	u8 tmp[4];
 | |
| 
 | |
| 	/* Read from last spill */
 | |
| 	if (!kfifo_is_empty(&dev->pio_fifo)) {
 | |
| 		int bytes_copied =
 | |
| 			kfifo_out(&dev->pio_fifo, buffer, min(4, len));
 | |
| 		buffer += bytes_copied;
 | |
| 		len -= bytes_copied;
 | |
| 
 | |
| 		if (!kfifo_is_empty(&dev->pio_fifo))
 | |
| 			return;
 | |
| 	}
 | |
| 
 | |
| 	/* Reads dwords from FIFO */
 | |
| 	while (len >= 4) {
 | |
| 		*(u32 *)buffer = r592_read_reg_raw_be(dev, R592_FIFO_PIO);
 | |
| 		buffer += 4;
 | |
| 		len -= 4;
 | |
| 	}
 | |
| 
 | |
| 	if (len) {
 | |
| 		*(u32 *)tmp = r592_read_reg_raw_be(dev, R592_FIFO_PIO);
 | |
| 		kfifo_in(&dev->pio_fifo, tmp, 4);
 | |
| 		len -= kfifo_out(&dev->pio_fifo, buffer, len);
 | |
| 	}
 | |
| 
 | |
| 	WARN_ON(len);
 | |
| 	return;
 | |
| }
 | |
| 
 | |
| /* Transfers actual data using PIO. */
 | |
| static int r592_transfer_fifo_pio(struct r592_device *dev)
 | |
| {
 | |
| 	unsigned long flags;
 | |
| 
 | |
| 	bool is_write = dev->req->tpc >= MS_TPC_SET_RW_REG_ADRS;
 | |
| 	struct sg_mapping_iter miter;
 | |
| 
 | |
| 	kfifo_reset(&dev->pio_fifo);
 | |
| 
 | |
| 	if (!dev->req->long_data) {
 | |
| 		if (is_write) {
 | |
| 			r592_write_fifo_pio(dev, dev->req->data,
 | |
| 							dev->req->data_len);
 | |
| 			r592_flush_fifo_write(dev);
 | |
| 		} else
 | |
| 			r592_read_fifo_pio(dev, dev->req->data,
 | |
| 							dev->req->data_len);
 | |
| 		return 0;
 | |
| 	}
 | |
| 
 | |
| 	local_irq_save(flags);
 | |
| 	sg_miter_start(&miter, &dev->req->sg, 1, SG_MITER_ATOMIC |
 | |
| 		(is_write ? SG_MITER_FROM_SG : SG_MITER_TO_SG));
 | |
| 
 | |
| 	/* Do the transfer fifo<->memory*/
 | |
| 	while (sg_miter_next(&miter))
 | |
| 		if (is_write)
 | |
| 			r592_write_fifo_pio(dev, miter.addr, miter.length);
 | |
| 		else
 | |
| 			r592_read_fifo_pio(dev, miter.addr, miter.length);
 | |
| 
 | |
| 
 | |
| 	/* Write last few non aligned bytes*/
 | |
| 	if (is_write)
 | |
| 		r592_flush_fifo_write(dev);
 | |
| 
 | |
| 	sg_miter_stop(&miter);
 | |
| 	local_irq_restore(flags);
 | |
| 	return 0;
 | |
| }
 | |
| 
 | |
| /* Executes one TPC (data is read/written from small or large fifo) */
 | |
| static void r592_execute_tpc(struct r592_device *dev)
 | |
| {
 | |
| 	bool is_write;
 | |
| 	int len, error;
 | |
| 	u32 status, reg;
 | |
| 
 | |
| 	if (!dev->req) {
 | |
| 		message("BUG: tpc execution without request!");
 | |
| 		return;
 | |
| 	}
 | |
| 
 | |
| 	is_write = dev->req->tpc >= MS_TPC_SET_RW_REG_ADRS;
 | |
| 	len = dev->req->long_data ?
 | |
| 		dev->req->sg.length : dev->req->data_len;
 | |
| 
 | |
| 	/* Ensure that FIFO can hold the input data */
 | |
| 	if (len > R592_LFIFO_SIZE) {
 | |
| 		message("IO: hardware doesn't support TPCs longer that 512");
 | |
| 		error = -ENOSYS;
 | |
| 		goto out;
 | |
| 	}
 | |
| 
 | |
| 	if (!(r592_read_reg(dev, R592_REG_MSC) & R592_REG_MSC_PRSNT)) {
 | |
| 		dbg("IO: refusing to send TPC because card is absent");
 | |
| 		error = -ENODEV;
 | |
| 		goto out;
 | |
| 	}
 | |
| 
 | |
| 	dbg("IO: executing %s LEN=%d",
 | |
| 			memstick_debug_get_tpc_name(dev->req->tpc), len);
 | |
| 
 | |
| 	/* Set IO direction */
 | |
| 	if (is_write)
 | |
| 		r592_set_reg_mask(dev, R592_IO, R592_IO_DIRECTION);
 | |
| 	else
 | |
| 		r592_clear_reg_mask(dev, R592_IO, R592_IO_DIRECTION);
 | |
| 
 | |
| 
 | |
| 	error = r592_test_fifo_empty(dev);
 | |
| 	if (error)
 | |
| 		goto out;
 | |
| 
 | |
| 	/* Transfer write data */
 | |
| 	if (is_write) {
 | |
| 		error = r592_transfer_fifo_dma(dev);
 | |
| 		if (error == -EINVAL)
 | |
| 			error = r592_transfer_fifo_pio(dev);
 | |
| 	}
 | |
| 
 | |
| 	if (error)
 | |
| 		goto out;
 | |
| 
 | |
| 	/* Trigger the TPC */
 | |
| 	reg = (len << R592_TPC_EXEC_LEN_SHIFT) |
 | |
| 		(dev->req->tpc << R592_TPC_EXEC_TPC_SHIFT) |
 | |
| 			R592_TPC_EXEC_BIG_FIFO;
 | |
| 
 | |
| 	r592_write_reg(dev, R592_TPC_EXEC, reg);
 | |
| 
 | |
| 	/* Wait for TPC completion */
 | |
| 	status = R592_STATUS_RDY;
 | |
| 	if (dev->req->need_card_int)
 | |
| 		status |= R592_STATUS_CED;
 | |
| 
 | |
| 	error = r592_wait_status(dev, status, status);
 | |
| 	if (error) {
 | |
| 		message("card didn't respond");
 | |
| 		goto out;
 | |
| 	}
 | |
| 
 | |
| 	/* Test IO errors */
 | |
| 	error = r592_test_io_error(dev);
 | |
| 	if (error) {
 | |
| 		dbg("IO error");
 | |
| 		goto out;
 | |
| 	}
 | |
| 
 | |
| 	/* Read data from FIFO */
 | |
| 	if (!is_write) {
 | |
| 		error = r592_transfer_fifo_dma(dev);
 | |
| 		if (error == -EINVAL)
 | |
| 			error = r592_transfer_fifo_pio(dev);
 | |
| 	}
 | |
| 
 | |
| 	/* read INT reg. This can be shortened with shifts, but that way
 | |
| 		its more readable */
 | |
| 	if (dev->parallel_mode && dev->req->need_card_int) {
 | |
| 
 | |
| 		dev->req->int_reg = 0;
 | |
| 		status = r592_read_reg(dev, R592_STATUS);
 | |
| 
 | |
| 		if (status & R592_STATUS_P_CMDNACK)
 | |
| 			dev->req->int_reg |= MEMSTICK_INT_CMDNAK;
 | |
| 		if (status & R592_STATUS_P_BREQ)
 | |
| 			dev->req->int_reg |= MEMSTICK_INT_BREQ;
 | |
| 		if (status & R592_STATUS_P_INTERR)
 | |
| 			dev->req->int_reg |= MEMSTICK_INT_ERR;
 | |
| 		if (status & R592_STATUS_P_CED)
 | |
| 			dev->req->int_reg |= MEMSTICK_INT_CED;
 | |
| 	}
 | |
| 
 | |
| 	if (error)
 | |
| 		dbg("FIFO read error");
 | |
| out:
 | |
| 	dev->req->error = error;
 | |
| 	r592_clear_reg_mask(dev, R592_REG_MSC, R592_REG_MSC_LED);
 | |
| 	return;
 | |
| }
 | |
| 
 | |
| /* Main request processing thread */
 | |
| static int r592_process_thread(void *data)
 | |
| {
 | |
| 	int error;
 | |
| 	struct r592_device *dev = (struct r592_device *)data;
 | |
| 	unsigned long flags;
 | |
| 
 | |
| 	while (!kthread_should_stop()) {
 | |
| 		spin_lock_irqsave(&dev->io_thread_lock, flags);
 | |
| 		set_current_state(TASK_INTERRUPTIBLE);
 | |
| 		error = memstick_next_req(dev->host, &dev->req);
 | |
| 		spin_unlock_irqrestore(&dev->io_thread_lock, flags);
 | |
| 
 | |
| 		if (error) {
 | |
| 			if (error == -ENXIO || error == -EAGAIN) {
 | |
| 				dbg_verbose("IO: done IO, sleeping");
 | |
| 			} else {
 | |
| 				dbg("IO: unknown error from "
 | |
| 					"memstick_next_req %d", error);
 | |
| 			}
 | |
| 
 | |
| 			if (kthread_should_stop())
 | |
| 				set_current_state(TASK_RUNNING);
 | |
| 
 | |
| 			schedule();
 | |
| 		} else {
 | |
| 			set_current_state(TASK_RUNNING);
 | |
| 			r592_execute_tpc(dev);
 | |
| 		}
 | |
| 	}
 | |
| 	return 0;
 | |
| }
 | |
| 
 | |
| /* Reprogram chip to detect change in card state */
 | |
| /* eg, if card is detected, arm it to detect removal, and vice versa */
 | |
| static void r592_update_card_detect(struct r592_device *dev)
 | |
| {
 | |
| 	u32 reg = r592_read_reg(dev, R592_REG_MSC);
 | |
| 	bool card_detected = reg & R592_REG_MSC_PRSNT;
 | |
| 
 | |
| 	dbg("update card detect. card state: %s", card_detected ?
 | |
| 		"present" : "absent");
 | |
| 
 | |
| 	reg &= ~((R592_REG_MSC_IRQ_REMOVE | R592_REG_MSC_IRQ_INSERT) << 16);
 | |
| 
 | |
| 	if (card_detected)
 | |
| 		reg |= (R592_REG_MSC_IRQ_REMOVE << 16);
 | |
| 	else
 | |
| 		reg |= (R592_REG_MSC_IRQ_INSERT << 16);
 | |
| 
 | |
| 	r592_write_reg(dev, R592_REG_MSC, reg);
 | |
| }
 | |
| 
 | |
| /* Timer routine that fires 1 second after last card detection event, */
 | |
| static void r592_detect_timer(long unsigned int data)
 | |
| {
 | |
| 	struct r592_device *dev = (struct r592_device *)data;
 | |
| 	r592_update_card_detect(dev);
 | |
| 	memstick_detect_change(dev->host);
 | |
| }
 | |
| 
 | |
| /* Interrupt handler */
 | |
| static irqreturn_t r592_irq(int irq, void *data)
 | |
| {
 | |
| 	struct r592_device *dev = (struct r592_device *)data;
 | |
| 	irqreturn_t ret = IRQ_NONE;
 | |
| 	u32 reg;
 | |
| 	u16 irq_enable, irq_status;
 | |
| 	unsigned long flags;
 | |
| 	int error;
 | |
| 
 | |
| 	spin_lock_irqsave(&dev->irq_lock, flags);
 | |
| 
 | |
| 	reg = r592_read_reg(dev, R592_REG_MSC);
 | |
| 	irq_enable = reg >> 16;
 | |
| 	irq_status = reg & 0xFFFF;
 | |
| 
 | |
| 	/* Ack the interrupts */
 | |
| 	reg &= ~irq_status;
 | |
| 	r592_write_reg(dev, R592_REG_MSC, reg);
 | |
| 
 | |
| 	/* Get the IRQ status minus bits that aren't enabled */
 | |
| 	irq_status &= (irq_enable);
 | |
| 
 | |
| 	/* Due to limitation of memstick core, we don't look at bits that
 | |
| 		indicate that card was removed/inserted and/or present */
 | |
| 	if (irq_status & (R592_REG_MSC_IRQ_INSERT | R592_REG_MSC_IRQ_REMOVE)) {
 | |
| 
 | |
| 		bool card_was_added = irq_status & R592_REG_MSC_IRQ_INSERT;
 | |
| 		ret = IRQ_HANDLED;
 | |
| 
 | |
| 		message("IRQ: card %s", card_was_added ? "added" : "removed");
 | |
| 
 | |
| 		mod_timer(&dev->detect_timer,
 | |
| 			jiffies + msecs_to_jiffies(card_was_added ? 500 : 50));
 | |
| 	}
 | |
| 
 | |
| 	if (irq_status &
 | |
| 		(R592_REG_MSC_FIFO_DMA_DONE | R592_REG_MSC_FIFO_DMA_ERR)) {
 | |
| 		ret = IRQ_HANDLED;
 | |
| 
 | |
| 		if (irq_status & R592_REG_MSC_FIFO_DMA_ERR) {
 | |
| 			message("IRQ: DMA error");
 | |
| 			error = -EIO;
 | |
| 		} else {
 | |
| 			dbg_verbose("IRQ: dma done");
 | |
| 			error = 0;
 | |
| 		}
 | |
| 
 | |
| 		r592_stop_dma(dev, error);
 | |
| 		complete(&dev->dma_done);
 | |
| 	}
 | |
| 
 | |
| 	spin_unlock_irqrestore(&dev->irq_lock, flags);
 | |
| 	return ret;
 | |
| }
 | |
| 
 | |
| /* External inteface: set settings */
 | |
| static int r592_set_param(struct memstick_host *host,
 | |
| 			enum memstick_param param, int value)
 | |
| {
 | |
| 	struct r592_device *dev = memstick_priv(host);
 | |
| 
 | |
| 	switch (param) {
 | |
| 	case MEMSTICK_POWER:
 | |
| 		switch (value) {
 | |
| 		case MEMSTICK_POWER_ON:
 | |
| 			return r592_enable_device(dev, true);
 | |
| 		case MEMSTICK_POWER_OFF:
 | |
| 			return r592_enable_device(dev, false);
 | |
| 		default:
 | |
| 			return -EINVAL;
 | |
| 		}
 | |
| 	case MEMSTICK_INTERFACE:
 | |
| 		switch (value) {
 | |
| 		case MEMSTICK_SERIAL:
 | |
| 			return r592_set_mode(dev, 0);
 | |
| 		case MEMSTICK_PAR4:
 | |
| 			return r592_set_mode(dev, 1);
 | |
| 		default:
 | |
| 			return -EINVAL;
 | |
| 		}
 | |
| 	default:
 | |
| 		return -EINVAL;
 | |
| 	}
 | |
| }
 | |
| 
 | |
| /* External interface: submit requests */
 | |
| static void r592_submit_req(struct memstick_host *host)
 | |
| {
 | |
| 	struct r592_device *dev = memstick_priv(host);
 | |
| 	unsigned long flags;
 | |
| 
 | |
| 	if (dev->req)
 | |
| 		return;
 | |
| 
 | |
| 	spin_lock_irqsave(&dev->io_thread_lock, flags);
 | |
| 	if (wake_up_process(dev->io_thread))
 | |
| 		dbg_verbose("IO thread woken to process requests");
 | |
| 	spin_unlock_irqrestore(&dev->io_thread_lock, flags);
 | |
| }
 | |
| 
 | |
| static const struct pci_device_id r592_pci_id_tbl[] = {
 | |
| 
 | |
| 	{ PCI_VDEVICE(RICOH, 0x0592), },
 | |
| 	{ },
 | |
| };
 | |
| 
 | |
| /* Main entry */
 | |
| static int r592_probe(struct pci_dev *pdev, const struct pci_device_id *id)
 | |
| {
 | |
| 	int error = -ENOMEM;
 | |
| 	struct memstick_host *host;
 | |
| 	struct r592_device *dev;
 | |
| 
 | |
| 	/* Allocate memory */
 | |
| 	host = memstick_alloc_host(sizeof(struct r592_device), &pdev->dev);
 | |
| 	if (!host)
 | |
| 		goto error1;
 | |
| 
 | |
| 	dev = memstick_priv(host);
 | |
| 	dev->host = host;
 | |
| 	dev->pci_dev = pdev;
 | |
| 	pci_set_drvdata(pdev, dev);
 | |
| 
 | |
| 	/* pci initialization */
 | |
| 	error = pci_enable_device(pdev);
 | |
| 	if (error)
 | |
| 		goto error2;
 | |
| 
 | |
| 	pci_set_master(pdev);
 | |
| 	error = pci_set_dma_mask(pdev, DMA_BIT_MASK(32));
 | |
| 	if (error)
 | |
| 		goto error3;
 | |
| 
 | |
| 	error = pci_request_regions(pdev, DRV_NAME);
 | |
| 	if (error)
 | |
| 		goto error3;
 | |
| 
 | |
| 	dev->mmio = pci_ioremap_bar(pdev, 0);
 | |
| 	if (!dev->mmio)
 | |
| 		goto error4;
 | |
| 
 | |
| 	dev->irq = pdev->irq;
 | |
| 	spin_lock_init(&dev->irq_lock);
 | |
| 	spin_lock_init(&dev->io_thread_lock);
 | |
| 	init_completion(&dev->dma_done);
 | |
| 	INIT_KFIFO(dev->pio_fifo);
 | |
| 	setup_timer(&dev->detect_timer,
 | |
| 		r592_detect_timer, (long unsigned int)dev);
 | |
| 
 | |
| 	/* Host initialization */
 | |
| 	host->caps = MEMSTICK_CAP_PAR4;
 | |
| 	host->request = r592_submit_req;
 | |
| 	host->set_param = r592_set_param;
 | |
| 	r592_check_dma(dev);
 | |
| 
 | |
| 	dev->io_thread = kthread_run(r592_process_thread, dev, "r592_io");
 | |
| 	if (IS_ERR(dev->io_thread)) {
 | |
| 		error = PTR_ERR(dev->io_thread);
 | |
| 		goto error5;
 | |
| 	}
 | |
| 
 | |
| 	/* This is just a precation, so don't fail */
 | |
| 	dev->dummy_dma_page = pci_alloc_consistent(pdev, PAGE_SIZE,
 | |
| 		&dev->dummy_dma_page_physical_address);
 | |
| 	r592_stop_dma(dev , 0);
 | |
| 
 | |
| 	if (request_irq(dev->irq, &r592_irq, IRQF_SHARED,
 | |
| 			  DRV_NAME, dev))
 | |
| 		goto error6;
 | |
| 
 | |
| 	r592_update_card_detect(dev);
 | |
| 	if (memstick_add_host(host))
 | |
| 		goto error7;
 | |
| 
 | |
| 	message("driver successfully loaded");
 | |
| 	return 0;
 | |
| error7:
 | |
| 	free_irq(dev->irq, dev);
 | |
| error6:
 | |
| 	if (dev->dummy_dma_page)
 | |
| 		pci_free_consistent(pdev, PAGE_SIZE, dev->dummy_dma_page,
 | |
| 			dev->dummy_dma_page_physical_address);
 | |
| 
 | |
| 	kthread_stop(dev->io_thread);
 | |
| error5:
 | |
| 	iounmap(dev->mmio);
 | |
| error4:
 | |
| 	pci_release_regions(pdev);
 | |
| error3:
 | |
| 	pci_disable_device(pdev);
 | |
| error2:
 | |
| 	memstick_free_host(host);
 | |
| error1:
 | |
| 	return error;
 | |
| }
 | |
| 
 | |
| static void r592_remove(struct pci_dev *pdev)
 | |
| {
 | |
| 	int error = 0;
 | |
| 	struct r592_device *dev = pci_get_drvdata(pdev);
 | |
| 
 | |
| 	/* Stop the processing thread.
 | |
| 	That ensures that we won't take any more requests */
 | |
| 	kthread_stop(dev->io_thread);
 | |
| 
 | |
| 	r592_enable_device(dev, false);
 | |
| 
 | |
| 	while (!error && dev->req) {
 | |
| 		dev->req->error = -ETIME;
 | |
| 		error = memstick_next_req(dev->host, &dev->req);
 | |
| 	}
 | |
| 	memstick_remove_host(dev->host);
 | |
| 
 | |
| 	free_irq(dev->irq, dev);
 | |
| 	iounmap(dev->mmio);
 | |
| 	pci_release_regions(pdev);
 | |
| 	pci_disable_device(pdev);
 | |
| 	memstick_free_host(dev->host);
 | |
| 
 | |
| 	if (dev->dummy_dma_page)
 | |
| 		pci_free_consistent(pdev, PAGE_SIZE, dev->dummy_dma_page,
 | |
| 			dev->dummy_dma_page_physical_address);
 | |
| }
 | |
| 
 | |
| #ifdef CONFIG_PM_SLEEP
 | |
| static int r592_suspend(struct device *core_dev)
 | |
| {
 | |
| 	struct pci_dev *pdev = to_pci_dev(core_dev);
 | |
| 	struct r592_device *dev = pci_get_drvdata(pdev);
 | |
| 
 | |
| 	r592_clear_interrupts(dev);
 | |
| 	memstick_suspend_host(dev->host);
 | |
| 	del_timer_sync(&dev->detect_timer);
 | |
| 	return 0;
 | |
| }
 | |
| 
 | |
| static int r592_resume(struct device *core_dev)
 | |
| {
 | |
| 	struct pci_dev *pdev = to_pci_dev(core_dev);
 | |
| 	struct r592_device *dev = pci_get_drvdata(pdev);
 | |
| 
 | |
| 	r592_clear_interrupts(dev);
 | |
| 	r592_enable_device(dev, false);
 | |
| 	memstick_resume_host(dev->host);
 | |
| 	r592_update_card_detect(dev);
 | |
| 	return 0;
 | |
| }
 | |
| #endif
 | |
| 
 | |
| static SIMPLE_DEV_PM_OPS(r592_pm_ops, r592_suspend, r592_resume);
 | |
| 
 | |
| MODULE_DEVICE_TABLE(pci, r592_pci_id_tbl);
 | |
| 
 | |
| static struct pci_driver r852_pci_driver = {
 | |
| 	.name		= DRV_NAME,
 | |
| 	.id_table	= r592_pci_id_tbl,
 | |
| 	.probe		= r592_probe,
 | |
| 	.remove		= r592_remove,
 | |
| 	.driver.pm	= &r592_pm_ops,
 | |
| };
 | |
| 
 | |
| module_pci_driver(r852_pci_driver);
 | |
| 
 | |
| module_param_named(enable_dma, r592_enable_dma, bool, S_IRUGO);
 | |
| MODULE_PARM_DESC(enable_dma, "Enable usage of the DMA (default)");
 | |
| module_param(debug, int, S_IRUGO | S_IWUSR);
 | |
| MODULE_PARM_DESC(debug, "Debug level (0-3)");
 | |
| 
 | |
| MODULE_LICENSE("GPL");
 | |
| MODULE_AUTHOR("Maxim Levitsky <maximlevitsky@gmail.com>");
 | |
| MODULE_DESCRIPTION("Ricoh R5C592 Memstick/Memstick PRO card reader driver");
 |