M7350v1_en_gpl

This commit is contained in:
T
2024-09-09 08:52:07 +00:00
commit f9cc65cfda
65988 changed files with 26357421 additions and 0 deletions
+44
View File
@@ -0,0 +1,44 @@
#
# MemoryStick host controller drivers
#
comment "MemoryStick Host Controller Drivers"
config MEMSTICK_TIFM_MS
tristate "TI Flash Media MemoryStick Interface support (EXPERIMENTAL)"
depends on EXPERIMENTAL && PCI
select TIFM_CORE
help
Say Y here if you want to be able to access MemoryStick cards with
the Texas Instruments(R) Flash Media card reader, found in many
laptops.
This option 'selects' (turns on, enables) 'TIFM_CORE', but you
probably also need appropriate card reader host adapter, such as
'Misc devices: TI Flash Media PCI74xx/PCI76xx host adapter support
(TIFM_7XX1)'.
To compile this driver as a module, choose M here: the
module will be called tifm_ms.
config MEMSTICK_JMICRON_38X
tristate "JMicron JMB38X MemoryStick interface support (EXPERIMENTAL)"
depends on EXPERIMENTAL && PCI
help
Say Y here if you want to be able to access MemoryStick cards with
the JMicron(R) JMB38X MemoryStick card reader.
To compile this driver as a module, choose M here: the
module will be called jmb38x_ms.
config MEMSTICK_R592
tristate "Ricoh R5C592 MemoryStick interface support (EXPERIMENTAL)"
depends on EXPERIMENTAL && PCI
help
Say Y here if you want to be able to access MemoryStick cards with
the Ricoh R5C592 MemoryStick card reader (which is part of 5 in one
multifunction reader)
To compile this driver as a module, choose M here: the module will
be called r592.
+7
View File
@@ -0,0 +1,7 @@
#
# Makefile for MemoryStick host controller drivers
#
obj-$(CONFIG_MEMSTICK_TIFM_MS) += tifm_ms.o
obj-$(CONFIG_MEMSTICK_JMICRON_38X) += jmb38x_ms.o
obj-$(CONFIG_MEMSTICK_R592) += r592.o
File diff suppressed because it is too large Load Diff
+908
View File
@@ -0,0 +1,908 @@
/*
* 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;
INIT_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 = dev->req->tpc >= MS_TPC_SET_RW_REG_ADRS;
int len, error;
u32 status, reg;
if (!dev->req) {
message("BUG: tpc execution without request!");
return;
}
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
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;
}
SIMPLE_DEV_PM_OPS(r592_pm_ops, r592_suspend, r592_resume);
#endif
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,
#ifdef CONFIG_PM
.driver.pm = &r592_pm_ops,
#endif
};
static __init int r592_module_init(void)
{
return pci_register_driver(&r852_pci_driver);
}
static void __exit r592_module_exit(void)
{
pci_unregister_driver(&r852_pci_driver);
}
module_init(r592_module_init);
module_exit(r592_module_exit);
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");
+175
View File
@@ -0,0 +1,175 @@
/*
* 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.
*/
#ifndef R592_H
#include <linux/memstick.h>
#include <linux/spinlock.h>
#include <linux/interrupt.h>
#include <linux/workqueue.h>
#include <linux/kfifo.h>
#include <linux/ctype.h>
/* write to this reg (number,len) triggers TPC execution */
#define R592_TPC_EXEC 0x00
#define R592_TPC_EXEC_LEN_SHIFT 16 /* Bits 16..25 are TPC len */
#define R592_TPC_EXEC_BIG_FIFO (1 << 26) /* If bit 26 is set, large fifo is used (reg 48) */
#define R592_TPC_EXEC_TPC_SHIFT 28 /* Bits 28..31 are the TPC number */
/* Window for small TPC fifo (big endian)*/
/* reads and writes always are done in 8 byte chunks */
/* Not used in driver, because large fifo does better job */
#define R592_SFIFO 0x08
/* Status register (ms int, small fifo, IO)*/
#define R592_STATUS 0x10
/* Parallel INT bits */
#define R592_STATUS_P_CMDNACK (1 << 16) /* INT reg: NACK (parallel mode) */
#define R592_STATUS_P_BREQ (1 << 17) /* INT reg: card ready (parallel mode)*/
#define R592_STATUS_P_INTERR (1 << 18) /* INT reg: int error (parallel mode)*/
#define R592_STATUS_P_CED (1 << 19) /* INT reg: command done (parallel mode) */
/* Fifo status */
#define R592_STATUS_SFIFO_FULL (1 << 20) /* Small Fifo almost full (last chunk is written) */
#define R592_STATUS_SFIFO_EMPTY (1 << 21) /* Small Fifo empty */
/* Error detection via CRC */
#define R592_STATUS_SEND_ERR (1 << 24) /* Send failed */
#define R592_STATUS_RECV_ERR (1 << 25) /* Receive failed */
/* Card state */
#define R592_STATUS_RDY (1 << 28) /* RDY signal received */
#define R592_STATUS_CED (1 << 29) /* INT: Command done (serial mode)*/
#define R592_STATUS_SFIFO_INPUT (1 << 30) /* Small fifo received data*/
#define R592_SFIFO_SIZE 32 /* total size of small fifo is 32 bytes */
#define R592_SFIFO_PACKET 8 /* packet size of small fifo */
/* IO control */
#define R592_IO 0x18
#define R592_IO_16 (1 << 16) /* Set by default, can be cleared */
#define R592_IO_18 (1 << 18) /* Set by default, can be cleared */
#define R592_IO_SERIAL1 (1 << 20) /* Set by default, can be cleared, (cleared on parallel) */
#define R592_IO_22 (1 << 22) /* Set by default, can be cleared */
#define R592_IO_DIRECTION (1 << 24) /* TPC direction (1 write 0 read) */
#define R592_IO_26 (1 << 26) /* Set by default, can be cleared */
#define R592_IO_SERIAL2 (1 << 30) /* Set by default, can be cleared (cleared on parallel), serial doesn't work if unset */
#define R592_IO_RESET (1 << 31) /* Reset, sets defaults*/
/* Turns hardware on/off */
#define R592_POWER 0x20 /* bits 0-7 writeable */
#define R592_POWER_0 (1 << 0) /* set on start, cleared on stop - must be set*/
#define R592_POWER_1 (1 << 1) /* set on start, cleared on stop - must be set*/
#define R592_POWER_3 (1 << 3) /* must be clear */
#define R592_POWER_20 (1 << 5) /* set before switch to parallel */
/* IO mode*/
#define R592_IO_MODE 0x24
#define R592_IO_MODE_SERIAL 1
#define R592_IO_MODE_PARALLEL 3
/* IRQ,card detection,large fifo (first word irq status, second enable) */
/* IRQs are ACKed by clearing the bits */
#define R592_REG_MSC 0x28
#define R592_REG_MSC_PRSNT (1 << 1) /* card present (only status)*/
#define R592_REG_MSC_IRQ_INSERT (1 << 8) /* detect insert / card insered */
#define R592_REG_MSC_IRQ_REMOVE (1 << 9) /* detect removal / card removed */
#define R592_REG_MSC_FIFO_EMPTY (1 << 10) /* fifo is empty */
#define R592_REG_MSC_FIFO_DMA_DONE (1 << 11) /* dma enable / dma done */
#define R592_REG_MSC_FIFO_USER_ORN (1 << 12) /* set if software reads empty fifo (if R592_REG_MSC_FIFO_EMPTY is set) */
#define R592_REG_MSC_FIFO_MISMATH (1 << 13) /* set if amount of data in fifo doesn't match amount in TPC */
#define R592_REG_MSC_FIFO_DMA_ERR (1 << 14) /* IO failure */
#define R592_REG_MSC_LED (1 << 15) /* clear to turn led off (only status)*/
#define DMA_IRQ_ACK_MASK \
(R592_REG_MSC_FIFO_DMA_DONE | R592_REG_MSC_FIFO_DMA_ERR)
#define DMA_IRQ_EN_MASK (DMA_IRQ_ACK_MASK << 16)
#define IRQ_ALL_ACK_MASK 0x00007F00
#define IRQ_ALL_EN_MASK (IRQ_ALL_ACK_MASK << 16)
/* DMA address for large FIFO read/writes*/
#define R592_FIFO_DMA 0x2C
/* PIO access to large FIFO (512 bytes) (big endian)*/
#define R592_FIFO_PIO 0x30
#define R592_LFIFO_SIZE 512 /* large fifo size */
/* large FIFO DMA settings */
#define R592_FIFO_DMA_SETTINGS 0x34
#define R592_FIFO_DMA_SETTINGS_EN (1 << 0) /* DMA enabled */
#define R592_FIFO_DMA_SETTINGS_DIR (1 << 1) /* Dma direction (1 read, 0 write) */
#define R592_FIFO_DMA_SETTINGS_CAP (1 << 24) /* Dma is aviable */
/* Maybe just an delay */
/* Bits 17..19 are just number */
/* bit 16 is set, then bit 20 is waited */
/* time to wait is about 50 spins * 2 ^ (bits 17..19) */
/* seems to be possible just to ignore */
/* Probably debug register */
#define R592_REG38 0x38
#define R592_REG38_CHANGE (1 << 16) /* Start bit */
#define R592_REG38_DONE (1 << 20) /* HW set this after the delay */
#define R592_REG38_SHIFT 17
/* Debug register, written (0xABCDEF00) when error happens - not used*/
#define R592_REG_3C 0x3C
struct r592_device {
struct pci_dev *pci_dev;
struct memstick_host *host; /* host backpointer */
struct memstick_request *req; /* current request */
/* Registers, IRQ */
void __iomem *mmio;
int irq;
spinlock_t irq_lock;
spinlock_t io_thread_lock;
struct timer_list detect_timer;
struct task_struct *io_thread;
bool parallel_mode;
DECLARE_KFIFO(pio_fifo, u8, sizeof(u32));
/* DMA area */
int dma_capable;
int dma_error;
struct completion dma_done;
void *dummy_dma_page;
dma_addr_t dummy_dma_page_physical_address;
};
#define DRV_NAME "r592"
#define message(format, ...) \
printk(KERN_INFO DRV_NAME ": " format "\n", ## __VA_ARGS__)
#define __dbg(level, format, ...) \
do { \
if (debug >= level) \
printk(KERN_DEBUG DRV_NAME \
": " format "\n", ## __VA_ARGS__); \
} while (0)
#define dbg(format, ...) __dbg(1, format, ## __VA_ARGS__)
#define dbg_verbose(format, ...) __dbg(2, format, ## __VA_ARGS__)
#define dbg_reg(format, ...) __dbg(3, format, ## __VA_ARGS__)
#endif
+688
View File
@@ -0,0 +1,688 @@
/*
* TI FlashMedia driver
*
* Copyright (C) 2007 Alex Dubov <oakad@yahoo.com>
*
* 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.
*
* Special thanks to Carlos Corbacho for providing various MemoryStick cards
* that made this driver possible.
*
*/
#include <linux/tifm.h>
#include <linux/memstick.h>
#include <linux/highmem.h>
#include <linux/scatterlist.h>
#include <linux/log2.h>
#include <linux/module.h>
#include <asm/io.h>
#define DRIVER_NAME "tifm_ms"
static bool no_dma;
module_param(no_dma, bool, 0644);
/*
* Some control bits of TIFM appear to conform to Sony's reference design,
* so I'm just assuming they all are.
*/
#define TIFM_MS_STAT_DRQ 0x04000
#define TIFM_MS_STAT_MSINT 0x02000
#define TIFM_MS_STAT_RDY 0x01000
#define TIFM_MS_STAT_CRC 0x00200
#define TIFM_MS_STAT_TOE 0x00100
#define TIFM_MS_STAT_EMP 0x00020
#define TIFM_MS_STAT_FUL 0x00010
#define TIFM_MS_STAT_CED 0x00008
#define TIFM_MS_STAT_ERR 0x00004
#define TIFM_MS_STAT_BRQ 0x00002
#define TIFM_MS_STAT_CNK 0x00001
#define TIFM_MS_SYS_DMA 0x10000
#define TIFM_MS_SYS_RESET 0x08000
#define TIFM_MS_SYS_SRAC 0x04000
#define TIFM_MS_SYS_INTEN 0x02000
#define TIFM_MS_SYS_NOCRC 0x01000
#define TIFM_MS_SYS_INTCLR 0x00800
#define TIFM_MS_SYS_MSIEN 0x00400
#define TIFM_MS_SYS_FCLR 0x00200
#define TIFM_MS_SYS_FDIR 0x00100
#define TIFM_MS_SYS_DAM 0x00080
#define TIFM_MS_SYS_DRM 0x00040
#define TIFM_MS_SYS_DRQSL 0x00020
#define TIFM_MS_SYS_REI 0x00010
#define TIFM_MS_SYS_REO 0x00008
#define TIFM_MS_SYS_BSY_MASK 0x00007
#define TIFM_MS_SYS_FIFO (TIFM_MS_SYS_INTEN | TIFM_MS_SYS_MSIEN \
| TIFM_MS_SYS_FCLR | TIFM_MS_SYS_BSY_MASK)
/* Hardware flags */
enum {
CMD_READY = 0x01,
FIFO_READY = 0x02,
CARD_INT = 0x04
};
struct tifm_ms {
struct tifm_dev *dev;
struct timer_list timer;
struct memstick_request *req;
struct tasklet_struct notify;
unsigned int mode_mask;
unsigned int block_pos;
unsigned long timeout_jiffies;
unsigned char eject:1,
use_dma:1;
unsigned char cmd_flags;
unsigned char io_pos;
unsigned int io_word;
};
static unsigned int tifm_ms_read_data(struct tifm_ms *host,
unsigned char *buf, unsigned int length)
{
struct tifm_dev *sock = host->dev;
unsigned int off = 0;
while (host->io_pos && length) {
buf[off++] = host->io_word & 0xff;
host->io_word >>= 8;
length--;
host->io_pos--;
}
if (!length)
return off;
while (!(TIFM_MS_STAT_EMP & readl(sock->addr + SOCK_MS_STATUS))) {
if (length < 4)
break;
*(unsigned int *)(buf + off) = __raw_readl(sock->addr
+ SOCK_MS_DATA);
length -= 4;
off += 4;
}
if (length
&& !(TIFM_MS_STAT_EMP & readl(sock->addr + SOCK_MS_STATUS))) {
host->io_word = readl(sock->addr + SOCK_MS_DATA);
for (host->io_pos = 4; host->io_pos; --host->io_pos) {
buf[off++] = host->io_word & 0xff;
host->io_word >>= 8;
length--;
if (!length)
break;
}
}
return off;
}
static unsigned int tifm_ms_write_data(struct tifm_ms *host,
unsigned char *buf, unsigned int length)
{
struct tifm_dev *sock = host->dev;
unsigned int off = 0;
if (host->io_pos) {
while (host->io_pos < 4 && length) {
host->io_word |= buf[off++] << (host->io_pos * 8);
host->io_pos++;
length--;
}
}
if (host->io_pos == 4
&& !(TIFM_MS_STAT_FUL & readl(sock->addr + SOCK_MS_STATUS))) {
writel(TIFM_MS_SYS_FDIR | readl(sock->addr + SOCK_MS_SYSTEM),
sock->addr + SOCK_MS_SYSTEM);
writel(host->io_word, sock->addr + SOCK_MS_DATA);
host->io_pos = 0;
host->io_word = 0;
} else if (host->io_pos) {
return off;
}
if (!length)
return off;
while (!(TIFM_MS_STAT_FUL & readl(sock->addr + SOCK_MS_STATUS))) {
if (length < 4)
break;
writel(TIFM_MS_SYS_FDIR | readl(sock->addr + SOCK_MS_SYSTEM),
sock->addr + SOCK_MS_SYSTEM);
__raw_writel(*(unsigned int *)(buf + off),
sock->addr + SOCK_MS_DATA);
length -= 4;
off += 4;
}
switch (length) {
case 3:
host->io_word |= buf[off + 2] << 16;
host->io_pos++;
case 2:
host->io_word |= buf[off + 1] << 8;
host->io_pos++;
case 1:
host->io_word |= buf[off];
host->io_pos++;
}
off += host->io_pos;
return off;
}
static unsigned int tifm_ms_transfer_data(struct tifm_ms *host)
{
struct tifm_dev *sock = host->dev;
unsigned int length;
unsigned int off;
unsigned int t_size, p_cnt;
unsigned char *buf;
struct page *pg;
unsigned long flags = 0;
if (host->req->long_data) {
length = host->req->sg.length - host->block_pos;
off = host->req->sg.offset + host->block_pos;
} else {
length = host->req->data_len - host->block_pos;
off = 0;
}
dev_dbg(&sock->dev, "fifo data transfer, %d, %d\n", length,
host->block_pos);
while (length) {
unsigned int uninitialized_var(p_off);
if (host->req->long_data) {
pg = nth_page(sg_page(&host->req->sg),
off >> PAGE_SHIFT);
p_off = offset_in_page(off);
p_cnt = PAGE_SIZE - p_off;
p_cnt = min(p_cnt, length);
local_irq_save(flags);
buf = kmap_atomic(pg) + p_off;
} else {
buf = host->req->data + host->block_pos;
p_cnt = host->req->data_len - host->block_pos;
}
t_size = host->req->data_dir == WRITE
? tifm_ms_write_data(host, buf, p_cnt)
: tifm_ms_read_data(host, buf, p_cnt);
if (host->req->long_data) {
kunmap_atomic(buf - p_off);
local_irq_restore(flags);
}
if (!t_size)
break;
host->block_pos += t_size;
length -= t_size;
off += t_size;
}
dev_dbg(&sock->dev, "fifo data transfer, %d remaining\n", length);
if (!length && (host->req->data_dir == WRITE)) {
if (host->io_pos) {
writel(TIFM_MS_SYS_FDIR
| readl(sock->addr + SOCK_MS_SYSTEM),
sock->addr + SOCK_MS_SYSTEM);
writel(host->io_word, sock->addr + SOCK_MS_DATA);
}
writel(TIFM_MS_SYS_FDIR
| readl(sock->addr + SOCK_MS_SYSTEM),
sock->addr + SOCK_MS_SYSTEM);
writel(0, sock->addr + SOCK_MS_DATA);
} else {
readl(sock->addr + SOCK_MS_DATA);
}
return length;
}
static int tifm_ms_issue_cmd(struct tifm_ms *host)
{
struct tifm_dev *sock = host->dev;
unsigned char *data;
unsigned int data_len, cmd, sys_param;
host->cmd_flags = 0;
host->block_pos = 0;
host->io_pos = 0;
host->io_word = 0;
host->cmd_flags = 0;
data = host->req->data;
host->use_dma = !no_dma;
if (host->req->long_data) {
data_len = host->req->sg.length;
if (!is_power_of_2(data_len))
host->use_dma = 0;
} else {
data_len = host->req->data_len;
host->use_dma = 0;
}
writel(TIFM_FIFO_INT_SETALL,
sock->addr + SOCK_DMA_FIFO_INT_ENABLE_CLEAR);
writel(TIFM_FIFO_ENABLE,
sock->addr + SOCK_FIFO_CONTROL);
if (host->use_dma) {
if (1 != tifm_map_sg(sock, &host->req->sg, 1,
host->req->data_dir == READ
? PCI_DMA_FROMDEVICE
: PCI_DMA_TODEVICE)) {
host->req->error = -ENOMEM;
return host->req->error;
}
data_len = sg_dma_len(&host->req->sg);
writel(ilog2(data_len) - 2,
sock->addr + SOCK_FIFO_PAGE_SIZE);
writel(TIFM_FIFO_INTMASK,
sock->addr + SOCK_DMA_FIFO_INT_ENABLE_SET);
sys_param = TIFM_DMA_EN | (1 << 8);
if (host->req->data_dir == WRITE)
sys_param |= TIFM_DMA_TX;
writel(TIFM_FIFO_INTMASK,
sock->addr + SOCK_DMA_FIFO_INT_ENABLE_SET);
writel(sg_dma_address(&host->req->sg),
sock->addr + SOCK_DMA_ADDRESS);
writel(sys_param, sock->addr + SOCK_DMA_CONTROL);
} else {
writel(host->mode_mask | TIFM_MS_SYS_FIFO,
sock->addr + SOCK_MS_SYSTEM);
writel(TIFM_FIFO_MORE,
sock->addr + SOCK_DMA_FIFO_INT_ENABLE_SET);
}
mod_timer(&host->timer, jiffies + host->timeout_jiffies);
writel(TIFM_CTRL_LED | readl(sock->addr + SOCK_CONTROL),
sock->addr + SOCK_CONTROL);
host->req->error = 0;
sys_param = readl(sock->addr + SOCK_MS_SYSTEM);
sys_param |= TIFM_MS_SYS_INTCLR;
if (host->use_dma)
sys_param |= TIFM_MS_SYS_DMA;
else
sys_param &= ~TIFM_MS_SYS_DMA;
writel(sys_param, sock->addr + SOCK_MS_SYSTEM);
cmd = (host->req->tpc & 0xf) << 12;
cmd |= data_len;
writel(cmd, sock->addr + SOCK_MS_COMMAND);
dev_dbg(&sock->dev, "executing TPC %x, %x\n", cmd, sys_param);
return 0;
}
static void tifm_ms_complete_cmd(struct tifm_ms *host)
{
struct tifm_dev *sock = host->dev;
struct memstick_host *msh = tifm_get_drvdata(sock);
int rc;
del_timer(&host->timer);
host->req->int_reg = readl(sock->addr + SOCK_MS_STATUS) & 0xff;
host->req->int_reg = (host->req->int_reg & 1)
| ((host->req->int_reg << 4) & 0xe0);
writel(TIFM_FIFO_INT_SETALL,
sock->addr + SOCK_DMA_FIFO_INT_ENABLE_CLEAR);
writel(TIFM_DMA_RESET, sock->addr + SOCK_DMA_CONTROL);
if (host->use_dma) {
tifm_unmap_sg(sock, &host->req->sg, 1,
host->req->data_dir == READ
? PCI_DMA_FROMDEVICE
: PCI_DMA_TODEVICE);
}
writel((~TIFM_CTRL_LED) & readl(sock->addr + SOCK_CONTROL),
sock->addr + SOCK_CONTROL);
dev_dbg(&sock->dev, "TPC complete\n");
do {
rc = memstick_next_req(msh, &host->req);
} while (!rc && tifm_ms_issue_cmd(host));
}
static int tifm_ms_check_status(struct tifm_ms *host)
{
if (!host->req->error) {
if (!(host->cmd_flags & CMD_READY))
return 1;
if (!(host->cmd_flags & FIFO_READY))
return 1;
if (host->req->need_card_int
&& !(host->cmd_flags & CARD_INT))
return 1;
}
return 0;
}
/* Called from interrupt handler */
static void tifm_ms_data_event(struct tifm_dev *sock)
{
struct tifm_ms *host;
unsigned int fifo_status = 0, host_status = 0;
int rc = 1;
spin_lock(&sock->lock);
host = memstick_priv((struct memstick_host *)tifm_get_drvdata(sock));
fifo_status = readl(sock->addr + SOCK_DMA_FIFO_STATUS);
host_status = readl(sock->addr + SOCK_MS_STATUS);
dev_dbg(&sock->dev,
"data event: fifo_status %x, host_status %x, flags %x\n",
fifo_status, host_status, host->cmd_flags);
if (host->req) {
if (host->use_dma && (fifo_status & 1)) {
host->cmd_flags |= FIFO_READY;
rc = tifm_ms_check_status(host);
}
if (!host->use_dma && (fifo_status & TIFM_FIFO_MORE)) {
if (!tifm_ms_transfer_data(host)) {
host->cmd_flags |= FIFO_READY;
rc = tifm_ms_check_status(host);
}
}
}
writel(fifo_status, sock->addr + SOCK_DMA_FIFO_STATUS);
if (!rc)
tifm_ms_complete_cmd(host);
spin_unlock(&sock->lock);
}
/* Called from interrupt handler */
static void tifm_ms_card_event(struct tifm_dev *sock)
{
struct tifm_ms *host;
unsigned int host_status = 0;
int rc = 1;
spin_lock(&sock->lock);
host = memstick_priv((struct memstick_host *)tifm_get_drvdata(sock));
host_status = readl(sock->addr + SOCK_MS_STATUS);
dev_dbg(&sock->dev, "host event: host_status %x, flags %x\n",
host_status, host->cmd_flags);
if (host->req) {
if (host_status & TIFM_MS_STAT_TOE)
host->req->error = -ETIME;
else if (host_status & TIFM_MS_STAT_CRC)
host->req->error = -EILSEQ;
if (host_status & TIFM_MS_STAT_RDY)
host->cmd_flags |= CMD_READY;
if (host_status & TIFM_MS_STAT_MSINT)
host->cmd_flags |= CARD_INT;
rc = tifm_ms_check_status(host);
}
writel(TIFM_MS_SYS_INTCLR | readl(sock->addr + SOCK_MS_SYSTEM),
sock->addr + SOCK_MS_SYSTEM);
if (!rc)
tifm_ms_complete_cmd(host);
spin_unlock(&sock->lock);
return;
}
static void tifm_ms_req_tasklet(unsigned long data)
{
struct memstick_host *msh = (struct memstick_host *)data;
struct tifm_ms *host = memstick_priv(msh);
struct tifm_dev *sock = host->dev;
unsigned long flags;
int rc;
spin_lock_irqsave(&sock->lock, flags);
if (!host->req) {
if (host->eject) {
do {
rc = memstick_next_req(msh, &host->req);
if (!rc)
host->req->error = -ETIME;
} while (!rc);
spin_unlock_irqrestore(&sock->lock, flags);
return;
}
do {
rc = memstick_next_req(msh, &host->req);
} while (!rc && tifm_ms_issue_cmd(host));
}
spin_unlock_irqrestore(&sock->lock, flags);
}
static void tifm_ms_dummy_submit(struct memstick_host *msh)
{
return;
}
static void tifm_ms_submit_req(struct memstick_host *msh)
{
struct tifm_ms *host = memstick_priv(msh);
tasklet_schedule(&host->notify);
}
static int tifm_ms_set_param(struct memstick_host *msh,
enum memstick_param param,
int value)
{
struct tifm_ms *host = memstick_priv(msh);
struct tifm_dev *sock = host->dev;
switch (param) {
case MEMSTICK_POWER:
/* also affected by media detection mechanism */
if (value == MEMSTICK_POWER_ON) {
host->mode_mask = TIFM_MS_SYS_SRAC | TIFM_MS_SYS_REI;
writel(TIFM_MS_SYS_RESET, sock->addr + SOCK_MS_SYSTEM);
writel(TIFM_MS_SYS_FCLR | TIFM_MS_SYS_INTCLR,
sock->addr + SOCK_MS_SYSTEM);
writel(0xffffffff, sock->addr + SOCK_MS_STATUS);
} else if (value == MEMSTICK_POWER_OFF) {
writel(TIFM_MS_SYS_FCLR | TIFM_MS_SYS_INTCLR,
sock->addr + SOCK_MS_SYSTEM);
writel(0xffffffff, sock->addr + SOCK_MS_STATUS);
} else
return -EINVAL;
break;
case MEMSTICK_INTERFACE:
if (value == MEMSTICK_SERIAL) {
host->mode_mask = TIFM_MS_SYS_SRAC | TIFM_MS_SYS_REI;
writel((~TIFM_CTRL_FAST_CLK)
& readl(sock->addr + SOCK_CONTROL),
sock->addr + SOCK_CONTROL);
} else if (value == MEMSTICK_PAR4) {
host->mode_mask = 0;
writel(TIFM_CTRL_FAST_CLK
| readl(sock->addr + SOCK_CONTROL),
sock->addr + SOCK_CONTROL);
} else
return -EINVAL;
break;
};
return 0;
}
static void tifm_ms_abort(unsigned long data)
{
struct tifm_ms *host = (struct tifm_ms *)data;
dev_dbg(&host->dev->dev, "status %x\n",
readl(host->dev->addr + SOCK_MS_STATUS));
printk(KERN_ERR
"%s : card failed to respond for a long period of time "
"(%x, %x)\n",
dev_name(&host->dev->dev), host->req ? host->req->tpc : 0,
host->cmd_flags);
tifm_eject(host->dev);
}
static int tifm_ms_probe(struct tifm_dev *sock)
{
struct memstick_host *msh;
struct tifm_ms *host;
int rc = -EIO;
if (!(TIFM_SOCK_STATE_OCCUPIED
& readl(sock->addr + SOCK_PRESENT_STATE))) {
printk(KERN_WARNING "%s : card gone, unexpectedly\n",
dev_name(&sock->dev));
return rc;
}
msh = memstick_alloc_host(sizeof(struct tifm_ms), &sock->dev);
if (!msh)
return -ENOMEM;
host = memstick_priv(msh);
tifm_set_drvdata(sock, msh);
host->dev = sock;
host->timeout_jiffies = msecs_to_jiffies(1000);
setup_timer(&host->timer, tifm_ms_abort, (unsigned long)host);
tasklet_init(&host->notify, tifm_ms_req_tasklet, (unsigned long)msh);
msh->request = tifm_ms_submit_req;
msh->set_param = tifm_ms_set_param;
sock->card_event = tifm_ms_card_event;
sock->data_event = tifm_ms_data_event;
if (tifm_has_ms_pif(sock))
msh->caps |= MEMSTICK_CAP_PAR4;
rc = memstick_add_host(msh);
if (!rc)
return 0;
memstick_free_host(msh);
return rc;
}
static void tifm_ms_remove(struct tifm_dev *sock)
{
struct memstick_host *msh = tifm_get_drvdata(sock);
struct tifm_ms *host = memstick_priv(msh);
int rc = 0;
unsigned long flags;
msh->request = tifm_ms_dummy_submit;
tasklet_kill(&host->notify);
spin_lock_irqsave(&sock->lock, flags);
host->eject = 1;
if (host->req) {
del_timer(&host->timer);
writel(TIFM_FIFO_INT_SETALL,
sock->addr + SOCK_DMA_FIFO_INT_ENABLE_CLEAR);
writel(TIFM_DMA_RESET, sock->addr + SOCK_DMA_CONTROL);
if (host->use_dma)
tifm_unmap_sg(sock, &host->req->sg, 1,
host->req->data_dir == READ
? PCI_DMA_TODEVICE
: PCI_DMA_FROMDEVICE);
host->req->error = -ETIME;
do {
rc = memstick_next_req(msh, &host->req);
if (!rc)
host->req->error = -ETIME;
} while (!rc);
}
spin_unlock_irqrestore(&sock->lock, flags);
memstick_remove_host(msh);
memstick_free_host(msh);
}
#ifdef CONFIG_PM
static int tifm_ms_suspend(struct tifm_dev *sock, pm_message_t state)
{
struct memstick_host *msh = tifm_get_drvdata(sock);
memstick_suspend_host(msh);
return 0;
}
static int tifm_ms_resume(struct tifm_dev *sock)
{
struct memstick_host *msh = tifm_get_drvdata(sock);
memstick_resume_host(msh);
return 0;
}
#else
#define tifm_ms_suspend NULL
#define tifm_ms_resume NULL
#endif /* CONFIG_PM */
static struct tifm_device_id tifm_ms_id_tbl[] = {
{ TIFM_TYPE_MS }, { 0 }
};
static struct tifm_driver tifm_ms_driver = {
.driver = {
.name = DRIVER_NAME,
.owner = THIS_MODULE
},
.id_table = tifm_ms_id_tbl,
.probe = tifm_ms_probe,
.remove = tifm_ms_remove,
.suspend = tifm_ms_suspend,
.resume = tifm_ms_resume
};
static int __init tifm_ms_init(void)
{
return tifm_register_driver(&tifm_ms_driver);
}
static void __exit tifm_ms_exit(void)
{
tifm_unregister_driver(&tifm_ms_driver);
}
MODULE_AUTHOR("Alex Dubov");
MODULE_DESCRIPTION("TI FlashMedia MemoryStick driver");
MODULE_LICENSE("GPL");
MODULE_DEVICE_TABLE(tifm, tifm_ms_id_tbl);
module_init(tifm_ms_init);
module_exit(tifm_ms_exit);