/*
* Copyright © 2009 - Maxim Levitsky
* driver for Ricoh xD 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/jiffies.h>
#include <linux/workqueue.h>
#include <linux/interrupt.h>
#include <linux/pci.h>
#include <linux/pci_ids.h>
#include <linux/delay.h>
#include <linux/slab.h>
#include <asm/byteorder.h>
#include <linux/sched.h>
#include "sm_common.h"
#include "r852.h"
static bool r852_enable_dma = 1;
module_param(r852_enable_dma, bool, S_IRUGO);
MODULE_PARM_DESC(r852_enable_dma, "Enable usage of the DMA (default)");
static int debug;
module_param(debug, int, S_IRUGO | S_IWUSR);
MODULE_PARM_DESC(debug, "Debug level (0-2)");
/* read register */
static inline uint8_t r852_read_reg(struct r852_device *dev, int address)
{
uint8_t reg = readb(dev->mmio + address);
return reg;
}
/* write register */
static inline void r852_write_reg(struct r852_device *dev,
int address, uint8_t value)
{
writeb(value, dev->mmio + address);
mmiowb();
}
/* read dword sized register */
static inline uint32_t r852_read_reg_dword(struct r852_device *dev, int address)
{
uint32_t reg = le32_to_cpu(readl(dev->mmio + address));
return reg;
}
/* write dword sized register */
static inline void r852_write_reg_dword(struct r852_device *dev,
int address, uint32_t value)
{
writel(cpu_to_le32(value), dev->mmio + address);
mmiowb();
}
/* returns pointer to our private structure */
static inline struct r852_device *r852_get_dev(struct mtd_info *mtd)
{
struct nand_chip *chip = mtd_to_nand(mtd);
return chip->priv;
}
/* check if controller supports dma */
static void r852_dma_test(struct r852_device *dev)
{
dev->dma_usable = (r852_read_reg(dev, R852_DMA_CAP) &
(R852_DMA1 | R852_DMA2)) == (R852_DMA1 | R852_DMA2);
if (!dev->dma_usable)
message("Non dma capable device detected, dma disabled");
if (!r852_enable_dma) {
message("disabling dma on user request");
dev->dma_usable = 0;
}
}
/*
* Enable dma. Enables ether first or second stage of the DMA,
* Expects dev->dma_dir and dev->dma_state be set
*/
static void r852_dma_enable(struct r852_device *dev)
{
uint8_t dma_reg, dma_irq_reg;
/* Set up dma settings */
dma_reg = r852_read_reg_dword(dev, R852_DMA_SETTINGS);
dma_reg &= ~(R852_DMA_READ | R852_DMA_INTERNAL | R852_DMA_MEMORY);
if (dev->dma_dir)
dma_reg |= R852_DMA_READ;
if (dev->dma_state == DMA_INTERNAL) {
dma_reg |= R852_DMA_INTERNAL;
/* Precaution to make sure HW doesn't write */
/* to random kernel memory */
r852_write_reg_dword(dev, R852_DMA_ADDR,
cpu_to_le32(dev->phys_bounce_buffer));
} else {
dma_reg |= R852_DMA_MEMORY;
r852_write_reg_dword(dev, R852_DMA_ADDR,
cpu_to_le32(dev->phys_dma_addr));
}
/* Precaution: make sure write reached the device */
r852_read_reg_dword(dev, R852_DMA_ADDR);
r852_write_reg_dword(dev, R852_DMA_SETTINGS, dma_reg);
/* Set dma irq */
dma_irq_reg = r852_read_reg_dword(dev, R852_DMA_IRQ_ENABLE);
r852_write_reg_dword(dev, R852_DMA_IRQ_ENABLE,
dma_irq_reg |
R852_DMA_IRQ_INTERNAL |
R852_DMA_IRQ_ERROR |
R852_DMA_IRQ_MEMORY);
}
/*
* Disable dma, called from the interrupt handler, which specifies
* success of the operation via 'error' argument
*/
static void r852_dma_done(struct r852_device *dev, int error)
{
WARN_ON(dev->dma_stage == 0);
r852_write_reg_dword(dev, R852_DMA_IRQ_STA,
r852_read_reg_dword(dev, R852_DMA_IRQ_STA));
r852_write_reg_dword(dev, R852_DMA_SETTINGS, 0);
r852_write_reg_dword(dev, R852_DMA_IRQ_E