/*
* 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 int 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;
}