/*
 *  File Name       : linux/drivers/char/mp200_spi.c
 *  Function        : MP200 SPI interface
 *  Release Version : Ver 1.02
 *  Release Date    : 2006/03/08
 *
 *  Copyright (C) NEC Electronics Corporation 2005-2006
 *
 *
 *  This program is free software;you can redistribute it and/or modify it under the terms of
 *  the GNU General Public License as published by Free Softwere Foundation; either version 2
 *  of License, or (at your option) any later version.
 *
 *  This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
 *  without even the implied warrnty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 *  See the GNU General Public License for more details.
 *
 *  You should have received a copy of the GNU General Public License along with this program;
 *  If not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston,
 *  MA 02111-1307, USA.
 *
 */

#include <linux/module.h>
#include <linux/init.h>
#include <linux/poll.h>
#include <linux/delay.h>
#include <linux/dma-mapping.h>
#include <linux/interrupt.h>

#include <asm/io.h>
#include <asm/irq.h>
#include <asm/dma.h>
#include <asm/arch/smu.h>
#include <asm/arch/pmu.h>

#include "mp200_spi.h"

static void spi_interrupt_dma_rx(void *, int, int);
static void spi_interrupt_dma_tx(void *, int, int);

#ifdef CONFIG_MP201 /* 0602 */
int spi_write_without_intr(const SPI_CONFIG *, const char *, int) 
	__attribute__ ((weak));
#endif

static int spi_major = 0;
static int spi_sleep_flag = 0;
static int spi_clock_source = 0;

static int spi_devcnt = 0;

static struct class_simple *spi_class;

#ifdef CONFIG_MP201 /* fixed length */
/* comment: 固定長DMA転送を使用するため、端数CPU転送用の処理不要 */
/* no code */
#else
static unsigned int spi_dma_size[] = {
	256,			/* 1500KHz */
	256,			/* 3MHz */
	256,			/* 6MHz */
	512,			/* 12MHz */
	1024,			/* 24MHz */
	2048,			/* 48MHz */
};

static unsigned int spi_rsv_size[] = {
	4,			/* 1500KHz */
	4,			/* 3MHz */
	4,			/* 6MHz */
	160,			/* 12MHz */
	736,			/* 24MHz */
	1536,			/* 48MHz */
};
#endif

static unsigned int spi_clock_table[] = {
	1500,			/* 1500KHz */
	3000,			/* 3MHz */
	6000,			/* 6MHz */
	12000,			/* 12MHz */
	24000,			/* 24MHz */
	48000,			/* 48MHz */
};

#ifdef CONFIG_MP201
/* comment: SPI0 を追加 */
/* spi register info (SPI0) */
static spi_regs_t regs_spi0 = {
	.mode = &SPx_MODE(MP200_SP0_BASE),
	.pol = &SPx_POL(MP200_SP0_BASE),
	.control = &SPx_CONTROL(MP200_SP0_BASE),
	.tx_tim = &SPx_TX_TIM(MP200_SP0_BASE),
	.tx_data = &SPx_TX_DATA(MP200_SP0_BASE),
	.rx_data = &SPx_RX_DATA(MP200_SP0_BASE),
	.status = &SPx_STATUS(MP200_SP0_BASE),
	.raw_status = &SPx_RAW_STATUS(MP200_SP0_BASE),
	.enset = &SPx_ENSET(MP200_SP0_BASE),
	.enclr = &SPx_ENCLR(MP200_SP0_BASE),
	.ffclr = &SPx_FFCLR(MP200_SP0_BASE),
	.tx_fifo_p = &SPx_TX_FIFO_P(MP200_SP0_BASE),
	.rx_fifo_p = &SPx_RX_FIFO_P(MP200_SP0_BASE),
	.control2 = &SPx_CONTROL2(MP200_SP0_BASE),
};
#endif

/* spi register info (SPI1) */
static spi_regs_t regs_spi1 = {
	.mode = &SPx_MODE(MP200_SP1_BASE),
	.pol = &SPx_POL(MP200_SP1_BASE),
	.control = &SPx_CONTROL(MP200_SP1_BASE),
	.tx_tim = &SPx_TX_TIM(MP200_SP1_BASE),
	.tx_data = &SPx_TX_DATA(MP200_SP1_BASE),
	.rx_data = &SPx_RX_DATA(MP200_SP1_BASE),
	.status = &SPx_STATUS(MP200_SP1_BASE),
	.raw_status = &SPx_RAW_STATUS(MP200_SP1_BASE),
	.enset = &SPx_ENSET(MP200_SP1_BASE),
	.enclr = &SPx_ENCLR(MP200_SP1_BASE),
	.ffclr = &SPx_FFCLR(MP200_SP1_BASE),
	.tx_fifo_p = &SPx_TX_FIFO_P(MP200_SP1_BASE),
	.rx_fifo_p = &SPx_RX_FIFO_P(MP200_SP1_BASE),
#ifdef CONFIG_MP201 /* fixed length */
/* comment: MP201 にてCONTROL2レジスタ追加 */
	.control2 = &SPx_CONTROL2(MP200_SP1_BASE),
#endif
};

#ifdef CONFIG_MP201
/* comment: SPI2 を削除 */
/* no code */
#else
/* spi register info (SPI2) */
static spi_regs_t regs_spi2 = {
	.mode = &SPx_MODE(MP200_SP2_BASE),
	.pol = &SPx_POL(MP200_SP2_BASE),
	.control = &SPx_CONTROL(MP200_SP2_BASE),
	.tx_tim = &SPx_TX_TIM(MP200_SP2_BASE),
	.tx_data = &SPx_TX_DATA(MP200_SP2_BASE),
	.rx_data = &SPx_RX_DATA(MP200_SP2_BASE),
	.status = &SPx_STATUS(MP200_SP2_BASE),
	.raw_status = &SPx_RAW_STATUS(MP200_SP2_BASE),
	.enset = &SPx_ENSET(MP200_SP2_BASE),
	.enclr = &SPx_ENCLR(MP200_SP2_BASE),
	.ffclr = &SPx_FFCLR(MP200_SP2_BASE),
	.tx_fifo_p = &SPx_TX_FIFO_P(MP200_SP2_BASE),
	.rx_fifo_p = &SPx_RX_FIFO_P(MP200_SP2_BASE),
};
#endif

#ifdef CONFIG_MP201
/* comment: SPI0 を追加 */
/* spi mode info (SPI0) */
static spi_mode_t mode_spi0 = {
	.nb = SPI_NB_32BIT,
	.cs_sel = SPI_CS_SEL_CS0,
	.m_s = SPI_M_S_MASTER,
	.dma = SPI_DMA_ON,
	.tx_tim = SPI_TX_TIM_1WORD,
	.sclk = SPI_SCLK_3MHZ,
	.blksize = SPI_BLKSIZE,
};
#endif

/* spi mode info (SPI1) */
static spi_mode_t mode_spi1 = {
	.nb = SPI_NB_32BIT,
	.cs_sel = SPI_CS_SEL_CS0,
#ifdef CONFIG_MP201
/* comment: デフォルトをスレーブに変更 */
	.m_s = SPI_M_S_SLAVE,
#else
	.m_s = SPI_M_S_MASTER,
#endif
	.dma = SPI_DMA_ON,
	.tx_tim = SPI_TX_TIM_1WORD,
	.sclk = SPI_SCLK_3MHZ,
	.blksize = SPI_BLKSIZE,
};

#ifdef CONFIG_MP201
/* comment: SPI2 を削除 */
/* no code */
#else
/* spi mode info (SPI2) */
static spi_mode_t mode_spi2 = {
	.nb = SPI_NB_32BIT,
	.cs_sel = SPI_CS_SEL_CS0,
	.m_s = SPI_M_S_SLAVE,
	.dma = SPI_DMA_ON,
	.tx_tim = SPI_TX_TIM_1WORD,
	.sclk = SPI_SCLK_3MHZ,
	.blksize = SPI_BLKSIZE,
};
#endif

#ifdef CONFIG_MP201
/* comment: SPI0 を追加 */
/* transfer info (SPI0) */
static spi_trans_t trans_spi0 = {
	.dma_regs = NULL,
	.rx_lch = MP200_DMAC_P2M_LCH1,
	.tx_lch = MP200_DMAC_M2P_LCH1,
	.rx_data = SPx_RX_DATA_PHYS(MP200_SP0_BASE),
	.tx_data = SPx_TX_DATA_PHYS(MP200_SP0_BASE),
	.int_spi = INT_SP0,
	.dma_err = 0,
	.spi_err = 0,
	.state = SPI_STOP,
	.spinlock = SPIN_LOCK_UNLOCKED,
};
#endif

/* transfer info (SPI1) */
static spi_trans_t trans_spi1 = {
	.dma_regs = NULL,
#ifdef CONFIG_MP201
/* comment: DMAチャンネル変更 */
	.rx_lch = MP200_DMAC_P2M_LCH2,
	.tx_lch = MP200_DMAC_M2P_LCH2,
#else
	.rx_lch = MP200_DMAC_P2M_LCH13,
	.tx_lch = MP200_DMAC_M2P_LCH13,
#endif
	.rx_data = SPx_RX_DATA_PHYS(MP200_SP1_BASE),
	.tx_data = SPx_TX_DATA_PHYS(MP200_SP1_BASE),
	.int_spi = INT_SP1,
	.dma_err = 0,
	.spi_err = 0,
#ifdef CONFIG_MP201 /* fixed length */
/* comment: DMAモード保持が不要になったため削除 */
/* no code */
#else
	.dma = SPI_DMA_OFF,
#endif
	.state = SPI_STOP,
	.spinlock = SPIN_LOCK_UNLOCKED,
};

#ifdef CONFIG_MP201
/* comment: SPI2 を削除 */
/* no code */
#else
/* transfer info (SPI2) */
static spi_trans_t trans_spi2 = {
	.dma_regs = NULL,
	.rx_lch = MP200_DMAC_P2M_LCH14,
	.tx_lch = MP200_DMAC_M2P_LCH14,
	.rx_data = SPx_RX_DATA_PHYS(MP200_SP2_BASE),
	.tx_data = SPx_TX_DATA_PHYS(MP200_SP2_BASE),
	.int_spi = INT_SP2,
	.dma_err = 0,
	.spi_err = 0,
	.dma = SPI_DMA_OFF,
	.state = SPI_STOP,
	.spinlock = SPIN_LOCK_UNLOCKED,
};
#endif

#ifdef CONFIG_MP201
/* comment: SPI0 を追加 */
/* smu info (SPI0) */
static spi_smu_t smu_spi0 = {
	.pclk = MP200_CLOCKGATE_SPI0_PCLK,
	.sclk = MP200_CLOCKGATE_SPI0_SCLK,
	.pclk_ctrl = MP200_CLKCTRL_SPI0PCLK,
	.sclk_ctrl = MP200_CLKCTRL_SPI0SCLKLP,
	.reset = MP200_RESETDEVICE_SPI0,
	.div_sclk = SMU_SPI0_SCLK_DIV,
};
#endif

/* smu info (SPI1) */
static spi_smu_t smu_spi1 = {
#ifdef CONFIG_MP201
/* comment: SMU 制御インターフェース用定義名の変更 */
	.pclk = MP200_CLOCKGATE_SPI1_PCLK,
	.sclk = MP200_CLOCKGATE_SPI1_SCLK,
	.pclk_ctrl = MP200_CLKCTRL_SPI1PCLK,
	.sclk_ctrl = MP200_CLKCTRL_SPI1SCLKLP,
	.reset = MP200_RESETDEVICE_SPI1,
	.div_sclk = SMU_SPI1_SCLK_DIV,
#else
	.pclk = MP200_CLOCKGATE_SP1_PCLK,
	.sclk = MP200_CLOCKGATE_SP1_SCLK,
	.pclk_ctrl = MP200_CLKCTRL_SP1PCLK,
	.sclk_ctrl = MP200_CLKCTRL_SP1SCLK,
	.reset = MP200_RESETDEVICE_SP1,
	.div_sclk = SMU_DIVSP1SCLK,
#endif
};

#ifdef CONFIG_MP201
/* comment: SPI2 を削除 */
/* no code */
#else
/* smu info (SPI2) */
static spi_smu_t smu_spi2 = {
	.pclk = MP200_CLOCKGATE_SP2_PCLK,
	.sclk = MP200_CLOCKGATE_SP2_SCLK,
	.pclk_ctrl = MP200_CLKCTRL_SP2PCLK,
	.sclk_ctrl = MP200_CLKCTRL_SP2SCLK,
	.reset = MP200_RESETDEVICE_SP2,
	.div_sclk = SMU_DIVSP2SCLK,
};
#endif

/* spi data info (MP211:SPI1/SPI2 / MP201:SPI0/SPI1) */
static spi_data_t spi_private[] = {
#ifdef CONFIG_MP201
/* comment: SPI0 を追加 */
	{
	 .regs = &regs_spi0,
	 .mode = &mode_spi0,
	 .pol = SPx_POL_VAL_SPI0,
	 .smu = &smu_spi0,
	 .trans = &trans_spi0,
	 .opened = 0,
	 .pctl_spi = SPI_SMU_PCTL_SPI0_SK,
	 .k_flag = 0,
	 .sem_rw = __MUTEX_INITIALIZER(spi_private[0].sem_rw),
	 .sem_open = __MUTEX_INITIALIZER(spi_private[0].sem_open),
	 },
#endif
	{
	 .regs = &regs_spi1,
	 .mode = &mode_spi1,
	 .pol = SPx_POL_VAL_SPI1,
	 .smu = &smu_spi1,
	 .trans = &trans_spi1,
	 .opened = 0,
#ifdef CONFIG_MP201
/* comment: 兼用端子操作用変数追加 
            配列位置変更           */
	 .pctl_spi = SPI_SMU_PCTL_SPI1_SK,
	 .k_flag = 0,
	 .sem_rw = __MUTEX_INITIALIZER(spi_private[1].sem_rw),
	 .sem_open = __MUTEX_INITIALIZER(spi_private[1].sem_open),
#else
	 .sem_open = __MUTEX_INITIALIZER(spi_private[0].sem_open),
#endif
	 },
#ifdef CONFIG_MP201
/* comment: SPI2 を削除 */
/* no code */
#else
	{
	 .regs = &regs_spi2,
	 .mode = &mode_spi2,
	 .pol = SPx_POL_VAL_SPI2,
	 .smu = &smu_spi2,
	 .trans = &trans_spi2,
	 .opened = 0,
	 .sem_open = __MUTEX_INITIALIZER(spi_private[1].sem_open),
	 },
#endif
};

static int spi_get_clock_source(void)
{
#ifdef CONFIG_MP201
/* comment: SMU仕様変更に伴う変更 */

	unsigned long plln, pllm, pllp;
	unsigned long pllclk, nrm_div;
	static const unsigned long smu_table_lplln[] = {
	  122880, 126976, 131072, 135168, 139264, 143360,
	  147456, 151552, 155648, 159744, 163840
	};
	int clock;

	switch (inl(SMU_PLLSEL) & MASK_SMU_PLLSEL) {
	case SMU_PLLSEL_OSC:
		/* 12MHz */
		clock = 12000;
		break;

	case SMU_PLLSEL_RTC:
		/* 32KHz */
		clock = 32;
		break;

	case SMU_PLLSEL_PLL:
		/* PLL */
		if(inl(SMU_PLLSEL2) & SMU_NRM_CK_SEL){ /* LPLL */
		  plln = inl(SMU_LPLL_CTRL1);
		  pllclk = smu_table_lplln[plln];
		}
		else /* HPLL */
		  {
		    plln = inl(SMU_HPLL_CTRL1) + 1;
		    pllm = inl(SMU_HPLL_CTRL2) + 1;
		    pllp = inl(SMU_HPLL_CTRL3);

		    pllclk = (12000 * plln) / pllm;
		    if(pllp != 0){
		      pllclk >>= 1;
		    }
		  }
		nrm_div = (inl(SMU_AUTO_FRQ_CHANGE) & 0xF000000) >> 24;
		clock = pllclk / (nrm_div + 1);

		break;

	default:
		/* 12MHz */
		clock = 12000;
		break;
	}
#else
	unsigned long pll1_p_val;
	unsigned long pll1_m_val;
	unsigned long pll1_n_val;
	unsigned long pll1ctrl0;
	unsigned long pll1ctrl1;
	unsigned long pll1;
	int clock;

	switch (inl(SMU_PLLSEL) & MASK_SMU_PLLSEL) {
	case SMU_PLLSEL_12M:
		/* 12MHz */
		clock = 12000;
		break;

	case SMU_PLLSEL_32K:
		/* 32KHz */
		clock = 32;
		break;

	case SMU_PLLSEL_PLL:
		/* PLL */
		pll1ctrl0 = inl(SMU_PLL1CTRL0);
		pll1ctrl1 = inl(SMU_PLL1CTRL1);
		pll1_n_val = (pll1ctrl0 & MASK_SMU_PLL1_N_VAL) + 1;

		if ((pll1ctrl0 & MASK_SMU_PLL1_MPFIX) != 0) {
			pll1_m_val = 2;
			pll1_p_val = 0;
		} else {
			pll1_m_val = (pll1ctrl1 & MASK_SMU_PLL1_M_VAL) + 1;
			pll1_p_val = (pll1ctrl1 & MASK_SMU_PLL1_P_VAL);
		}

		pll1 = (12000 * pll1_n_val) / pll1_m_val;
		if (pll1_p_val != 0) {
			pll1 >>= 1;
		}
		clock = pll1;
		break;

	default:
		/* 12MHz */
		clock = 12000;
		break;
	}
#endif

#ifdef SPI_DEBUG
	printk(KERN_INFO "%s(): clock = %d[KHz]\n", __FUNCTION__, clock);
#endif

	return clock;
}

#ifdef CONFIG_MP201 /* software reset */
/* comment: software reset関連Erattaが解決され、 software reset処理追加 */
/*
 * software reset
 */
static void spi_sft_reset(spi_data_t * spi)
{
	if (spi == NULL) {
		return;
	}

	*spi->regs->control |= SPx_CONTROL_RST;
	udelay((4000/spi_clock_table[spi->mode->sclk])+1);
	*spi->regs->control &= ~SPx_CONTROL_RST;
}
#endif

/*
 * reset on/off
 */
static void spi_reset_ctrl(spi_data_t * spi, unsigned char onoff)
{
#ifdef CONFIG_MP201 /* 0602 */
	if (spi_write_without_intr != 0) {
		return;
	}
#endif

	if (spi == NULL) {
		return;
	}

	if ((onoff != SPI_OFF) && (onoff != SPI_ON) && (onoff != SPI_RESET)) {
		return;
	}

	/* auto clock off */
	mp200_pmu_clkctrl_off(spi->smu->sclk_ctrl);
	mp200_pmu_clkctrl_off(spi->smu->pclk_ctrl);

	/* clock on */
	mp200_pmu_open_clockgate(spi->smu->pclk);
	mp200_pmu_open_clockgate(spi->smu->sclk);

	switch (onoff) {
	case SPI_OFF:
		/* reset off */
		mp200_pmu_unreset_device(spi->smu->reset);

#ifndef CONFIG_MP201
		/* clock off */
		mp200_pmu_close_clockgate(spi->smu->sclk);
#endif
		break;

	case SPI_ON:
		/* reset on */
#ifndef CONFIG_MP201
		mp200_pmu_reset_device(spi->smu->reset);
#endif
		/* clock off */
		mp200_pmu_close_clockgate(spi->smu->sclk);
		mp200_pmu_close_clockgate(spi->smu->pclk);
		break;

	case SPI_RESET:
		/* reset on -> off */
#ifndef CONFIG_MP201
		mp200_pmu_reset_device(spi->smu->reset);
#endif
		mp200_pmu_unreset_device(spi->smu->reset);

#ifndef CONFIG_MP201
		/* clock off */
		mp200_pmu_close_clockgate(spi->smu->sclk);
#endif
		break;

	default:
		break;
	}

	/* auto clock on */
	mp200_pmu_clkctrl_on(spi->smu->pclk_ctrl);
#ifdef CONFIG_MP201 /* clock control */
/* comment: 自動クロック制御関連のErattaが解決され、自動クロック制御追加 */
	mp200_pmu_clkctrl_on(spi->smu->sclk_ctrl);
#endif
}

/*
 * set sclk
 */
static unsigned int spi_set_sclk(spi_data_t * spi, unsigned int offset)
{
	unsigned int sclk;
	int divsclk;
	int i;

	if (spi == NULL) {
		return -EINVAL;
	}

	if (offset > SPI_SCLK_48MHZ) {
		offset = SPI_SCLK_48MHZ;
	}
	sclk = spi_clock_table[offset];

	divsclk = (spi_clock_source / sclk) - 1;
	if (divsclk < 1) {
		divsclk = 1;
		sclk = spi_clock_source / (divsclk + 1);
	} else if (divsclk > 255) {
		divsclk = 255;
		sclk = spi_clock_source / (divsclk + 1);
	}

	outl(divsclk, spi->smu->div_sclk);

#ifdef SPI_DEBUG
	printk(KERN_INFO "%s(): sclk(1/%d) = %d[KHz]\n", __FUNCTION__,
	       (divsclk + 1), sclk);
#endif

	for (i = 0; i <= SPI_SCLK_48MHZ; i++) {
		if (sclk == spi_clock_table[i]) {
			break;
		}
	}
	return i;
}

/*
 * initialize data
 */
static void spi_init_data(spi_data_t * spi)
{
	spi->trans->buf.dma_ptr = 0;
	spi->trans->buf.usr_ptr = 0;
	spi->trans->buf.state = SPI_BUFF_EMPTY;
	spi->trans->spi_err = 0;
	spi->trans->dma_err = 0;
}

/*
 * allocate DMA buffer
 */
static int spi_allocate_buffer(spi_data_t * spi, unsigned int blksize)
{
	dma_addr_t phys;
	unsigned int blknum;
	void *virt;

	if (spi == NULL) {
		return -EINVAL;
	}

	if (spi->trans->buf.dma_addr == 0) {
		blknum = SPI_BUFSIZE_MAX / blksize;
		if (blknum > SPI_BLKNUM_MAX) {
			blknum = SPI_BLKNUM_MAX;
		}

		virt = dma_alloc_coherent(NULL, (blksize * blknum), &phys, 0);
		if (virt == NULL) {
			printk(KERN_INFO "%s: memory allocation error\n",
			       SPI_NAME);
			return -ENOMEM;
		}
		spi->trans->buf.blknum = blknum;
		spi->trans->buf.dma_addr = phys;
		spi->trans->buf.top = (unsigned long)virt;
		spi->trans->buf.end = (unsigned long)virt + (blksize * blknum);
		spi_init_data(spi);
	}

	return 0;
}

/*
 * free DMA buffer
 */
static void spi_free_buffer(spi_data_t * spi)
{
	if (spi == NULL) {
		return;
	}

	if (spi->trans->buf.dma_addr != 0) {
		dma_free_coherent(NULL,
				  (spi->mode->blksize * spi->trans->buf.blknum),
				  (void *)(spi->trans->buf.top),
				  spi->trans->buf.dma_addr);
		spi->trans->buf.blknum = 0;
		spi->trans->buf.dma_addr = 0;
		spi->trans->buf.top = 0;
		spi->trans->buf.end = 0;
		spi_init_data(spi);
	}
}

/*
 * request DMA interrupt
 */
static int spi_request_dma(spi_data_t * spi)
{
	int ret = 0;

	if (spi == NULL) {
		return -EINVAL;
	}

	if (spi->trans->dma_regs == NULL) {
		if (spi->opened & FMODE_READ) {
			ret =
			    mp200_request_dma(spi->trans->rx_lch, SPI_NAME,
					      spi_interrupt_dma_rx,
					      (void *)spi,
					      &spi->trans->dma_regs);
		} else {
			ret =
			    mp200_request_dma(spi->trans->tx_lch, SPI_NAME,
					      spi_interrupt_dma_tx,
					      (void *)spi,
					      &spi->trans->dma_regs);
		}
	}
#ifdef SPI_DEBUG
	if (ret < 0) {
		printk(KERN_INFO "%s(): error\n", __FUNCTION__);
	}
#endif

	return ret;
}

/*
 * free DMA interrupt
 */
static void spi_free_dma(spi_data_t * spi)
{
	if (spi == NULL) {
		return;
	}

	if (spi->trans->dma_regs != NULL) {
		if (spi->opened & FMODE_READ) {
			mp200_free_dma(spi->trans->rx_lch);
		} else {
			mp200_free_dma(spi->trans->tx_lch);
		}
		spi->trans->dma_regs = NULL;
	}
}

/*
 * copy from user
 */
static int spi_copy_from_user(char *dst, char *src, unsigned int size)
{
	int ret = 0;

	ret = copy_from_user(dst, src, size);
	if (ret < 0) {
#ifdef SPI_DEBUG
		printk(KERN_INFO "%s(): copy error\n", __FUNCTION__);
#endif
		return -EFAULT;
	}

	return 0;
}

/*
 * copy to user
 */
static int spi_copy_to_user(char *dst, char *src, unsigned int size)
{
	int ret = 0;

	ret = copy_to_user(dst, src, size);
	if (ret < 0) {
#ifdef SPI_DEBUG
		printk(KERN_INFO "%s(): copy error\n", __FUNCTION__);
#endif
		return -EFAULT;
	}

	return 0;
}

/*
 * set mode
 */
static int spi_set_mode(spi_data_t * spi, spi_mode_t * mode)
{
	unsigned int mode_val = 0;
	unsigned int tx_tim_val = 0;
#ifdef CONFIG_MP201 /* fixed length */
/* comment: CONTROL2レジスタ設定用変数追加 */
	unsigned int control2 = 0;
#endif
	int ret = 0;

	if (spi == NULL) {
		return -EINVAL;
	}

	/* bit length */
	if ((SPI_NB_8BIT <= mode->nb) && (mode->nb <= SPI_NB_32BIT)) {
		mode_val |= ((mode->nb - 1) << 8);
	} else {
		return -EINVAL;
	}
	
#ifdef CONFIG_MP201 /* fixed length */
/* comment: DMA転送時のbit幅判定処理追加 */
	if (mode->dma == SPI_DMA_ON) {
		switch (mode->nb) {
		case SPI_NB_8BIT: 
		case SPI_NB_16BIT: 
		case SPI_NB_32BIT: 
			break;
		default:
			return -EINVAL;
		}
	}
#endif

	/* chip select */
	switch (mode->cs_sel) {
	case SPI_CS_SEL_CS0:
	case SPI_CS_SEL_CS1:
	case SPI_CS_SEL_CS2:
	case SPI_CS_SEL_CS3:
		mode_val |= (mode->cs_sel << 4);
		break;
	default:
		return -EINVAL;
	}

	/* master/slave */
	switch (mode->m_s) {
	case SPI_M_S_MASTER:
#ifdef CONFIG_MP201
/* comment: 兼用端子設定処理追加 */
		outl(inl(SMU_PCTL_SPI) & ~(spi->pctl_spi), SMU_PCTL_SPI);
		break;
#endif
	case SPI_M_S_SLAVE:
#ifdef CONFIG_MP201
/* comment: 兼用端子設定処理追加 */
		outl(inl(SMU_PCTL_SPI) | spi->pctl_spi, SMU_PCTL_SPI);
#else
		mode_val |= (mode->m_s << 1);
#endif
		break;
	default:
		return -EINVAL;
	}
#ifdef CONFIG_MP201
/* comment: 共通処理のため移動 */
	mode_val |= (mode->m_s << 1);
#endif

	/* dma on/off */
	switch (mode->dma) {
	case SPI_DMA_OFF:
		if ((spi->opened & FMODE_READ)
		    || ((spi->opened & FMODE_WRITE)
			&& (mode->m_s == SPI_M_S_SLAVE))) {
			mode->dma = SPI_DMA_ON;
		}
		/* through */
	case SPI_DMA_ON:
		mode_val |= (mode->dma << 0);
		break;
	default:
		return -EINVAL;
	}

#ifdef CONFIG_MP201 /* fixed length */
/* comment: CONTROL2設定処理追加 */
	/* fixed length mode */
	if ((mode->dma == SPI_DMA_ON) && (mode->m_s == SPI_M_S_MASTER)) {
		control2 = SPx_CONTROL2_TX_STOP_MODE;
	}
#endif

	/* transfer beginning cycle */
	switch (mode->tx_tim) {
	case SPI_TX_TIM_30WORD:
	case SPI_TX_TIM_16WORD:
	case SPI_TX_TIM_8WORD:
	case SPI_TX_TIM_1WORD:
		tx_tim_val |= (mode->tx_tim << 0);
		break;
	default:
		return -EINVAL;
	}

	/* block size */
	if ((mode->blksize == 0) || (mode->blksize > SPI_BLKSIZE_MAX) ||
	    ((mode->blksize % 4) != 0)) {
		return -EINVAL;
	}

	/* sclk */
	switch (mode->sclk) {
	case SPI_SCLK_1500KHZ:
	case SPI_SCLK_3MHZ:
	case SPI_SCLK_6MHZ:
	case SPI_SCLK_12MHZ:
	case SPI_SCLK_24MHZ:
	case SPI_SCLK_48MHZ:
		break;
	default:
		return -EINVAL;
	}

#ifdef CONFIG_MP201 /* software reset */
/* comment: software reset を使用するため不要 */
#else
	/* reset */
	spi_reset_ctrl(spi, SPI_RESET);
#endif
	/* set SPx_MODE */
	*spi->regs->mode = mode_val;

	/* set SPx_TX_TIM */
	*spi->regs->tx_tim = tx_tim_val;

	/* set SPx_POL */
	*spi->regs->pol = spi->pol;

#ifdef CONFIG_MP201 /* fixed length */
/* comment: CONTROL2設定処理追加 */
	*spi->regs->control2 = control2;
#endif

#ifdef CONFIG_MP201 /* software reset */
/* comment: software reset 処理追加 */
	/* reset */
	spi_sft_reset(spi);
#endif
	/* set sclk */
	ret = spi_set_sclk(spi, mode->sclk);
	if (ret < 0) {
		return ret;
	}
	mode->sclk = ret;

	if (mode->blksize != spi->mode->blksize) {
		spi_free_buffer(spi);
	}
	ret = spi_allocate_buffer(spi, mode->blksize);
	if (ret < 0) {
		return ret;
	}

	if (mode->dma == SPI_DMA_ON) {
		ret = spi_request_dma(spi);
	} else {
		spi_free_dma(spi);
	}
#ifdef CONFIG_MP201 /* fixed length */
/* comment: DMAモードは切り替わらないので保持不要 */
#else
	spi->trans->dma = mode->dma;
#endif

	return ret;
}

/*
 * SPI stop
 */
static void spi_stop(spi_data_t * spi)
{
	if (spi == NULL) {
		return;
	}

	if (spi->trans->state == SPI_STOP) {
		return;
	}

#ifdef CONFIG_MP201 /* fixed length */
/* comment: DMAモード保持変数削除に伴い参照変数を変更 */
	if (spi->mode->dma == SPI_DMA_ON) {
#else
	if (spi->trans->dma == SPI_DMA_ON) {
#endif
		if (spi->mode->m_s == SPI_M_S_MASTER) {
			if (*spi->regs->control & SPx_CONTROL_START) {
				*spi->regs->control |= SPx_CONTROL_STOP;
				udelay(((1000 * 32) /
					spi_clock_table[spi->mode->sclk]) + 1);
			}
		}
	}

	/* reset and set mode */
	spi_set_mode(spi, spi->mode);

	/* interrupt disable */
#if 0
	*spi->regs->enclr |=
	    (SPx_ENCLR_ALLERR_MASK | SPx_ENCLR_RDV_MASK | SPx_ENCLR_END_MASK);
#else
/* comment: write-onlyレジスタなので代入に変更 */
	*spi->regs->enclr = SPx_ENCLR_ALL_MASK;
#endif

#ifdef CONFIG_MP201 /* clock control */
/* comment: 自動クロック制御使用のため、SCLK制御不要 */
/* nouse */
#else
	/* clock off */
	if (spi->mode->m_s == SPI_M_S_MASTER) {
		mp200_pmu_close_clockgate(spi->smu->sclk);
	}
#endif

	/* status to stop */
	spi->trans->state = SPI_STOP;
}

/*
 * RX stop
 */
static void spi_rx_stop(spi_data_t * spi)
{
	if (spi == NULL) {
		return;
	}

#ifdef CONFIG_MP201 /* fixed length */
/* comment: DMAモード保持変数削除に伴い参照変数を変更 */
	if (spi->mode->dma == SPI_DMA_ON) {
#else
	if (spi->trans->dma == SPI_DMA_ON) {
#endif
		/* SPI stop */
		spi_stop(spi);

		/* DMA stop */
		mp200_stop_dma(spi->trans->rx_lch);
	} else {
		/* SPI stop */
		spi_stop(spi);
	}
}

/*
 * TX stop
 */
static void spi_tx_stop(spi_data_t * spi)
{
	if (spi == NULL) {
		return;
	}

#ifdef CONFIG_MP201 /* fixed length */
/* comment: DMAモード保持変数削除に伴い参照変数を変更
            CONTROL2設定クリアを追加                  */
	if (spi->mode->dma == SPI_DMA_ON) {
		if (spi->mode->m_s == SPI_M_S_MASTER) {
			*spi->regs->control2 &= ~SPx_CONTROL2_TX_STOP_MODE;
		}
#else
	if (spi->trans->dma == SPI_DMA_ON) {
#endif
		/* SPI stop */
		spi_stop(spi);

		/* DMA stop */
		mp200_stop_dma(spi->trans->tx_lch);
	} else {
		/* SPI stop */
		spi_stop(spi);
	}
}

/*
 * RX start
 */
static int spi_rx_start(spi_data_t * spi, int size)
{
	int intmask = 0;
	int ret = 0;

	if (spi == NULL) {
		return -EINVAL;
	}

	/* status to active */
	spi->trans->state = SPI_ACTIVE;

#ifdef CONFIG_MP201 /* clock control */
/* comment: 自動クロック制御使用のため、SCLK制御不要 */
/* nouse */
#else
	/* clock on */
	if (spi->mode->m_s == SPI_M_S_MASTER) {
		mp200_pmu_open_clockgate(spi->smu->sclk);
	}
#endif

	/* initialize error status */
#if 0
	*spi->regs->ffclr |= SPx_FFCLR_ALLERR_CLR;
#else
/* comment: write-onlyレジスタなので代入に変更
            Error要因だけでなく全要因指定に変更    */
	*spi->regs->ffclr = SPx_FFCLR_ALL_CLR;
#endif
	/* interrupt enable (TERR/TX_UDR/RX_OVR) */
	*spi->regs->enset |= SPx_ENSET_ALLERR_EN;

#ifdef CONFIG_MP201 /* fixed length */
/* comment: DMAモード保持変数削除に伴い参照変数を変更
            DMA bit8/16/32幅転送の対応
            DMAモードエンディアン設定追加              */
	if (spi->mode->dma == SPI_DMA_ON) {
		unsigned int mode = 0;
		switch (spi->mode->nb) {
		case SPI_NB_8BIT: 
			mode = MP200_DMAC_AMODE_BIT8;
			break;
		case SPI_NB_16BIT: 
			mode = MP200_DMAC_AMODE_BIT16 | MP200_DMAC_ENDI_W10;
			break;
		case SPI_NB_32BIT: 
			mode = MP200_DMAC_AMODE_BIT32 | MP200_DMAC_ENDI_W3210;
			break;
		default:
			return -EINVAL;
		}
#else
	if (spi->trans->dma == SPI_DMA_ON) {
#endif
		if (spi->mode->m_s == SPI_M_S_MASTER) {
			/* DMA start (length) */
#ifdef CONFIG_MP201
/* comment: DMAモードエンディアン設定追加
            DMA bit8/16/32幅転送の対応    */
			spi->trans->dma_regs->mode = mode;
#else
			spi->trans->dma_regs->mode = MP200_DMAC_AMODE_BIT32;
#endif
			spi->trans->dma_regs->boff = 0;
			spi->trans->dma_regs->leng = size;
			spi->trans->dma_regs->bsize =
			    (size >
			     SPI_DMA_BLOCK_SIZE_MAX) ? SPI_DMA_BLOCK_SIZE_MAX :
			    size;
			spi->trans->dma_regs->bsize_count = 0;
			intmask =
			    MP200_DMAC_INT_ERROR_EN | MP200_DMAC_INT_LENG_EN;
		} else {
			/* DMA start (infinity length) */
#ifdef CONFIG_MP201
/* comment: DMAモードエンディアン設定追加
            DMA bit8/16/32幅転送の対応    */
			spi->trans->dma_regs->mode = 
			    MP200_DMAC_BMODE_REPEAT | mode;
#else
			spi->trans->dma_regs->mode =
			  MP200_DMAC_BMODE_REPEAT | MP200_DMAC_AMODE_BIT32;
#endif
			spi->trans->dma_regs->boff = 0;
			spi->trans->dma_regs->leng = 0;
			spi->trans->dma_regs->bsize = spi->mode->blksize;
			spi->trans->dma_regs->bsize_count =
			    spi->trans->buf.blknum - 1;
			intmask =
			    MP200_DMAC_INT_ERROR_EN | MP200_DMAC_INT_BLOCK_EN;
		}
		ret =
		    mp200_start_dma(spi->trans->rx_lch, spi->trans->rx_data, 0,
				    spi->trans->buf.dma_addr, intmask);
		if (ret < 0) {
			spi_rx_stop(spi);
#ifdef SPI_DEBUG
			printk(KERN_INFO "%s(): dma error\n", __FUNCTION__);
#endif
			return ret;
		}
	}

	/* RX start */
	*spi->regs->control |= (SPx_CONTROL_RD | SPx_CONTROL_START);

	return 0;
}

/*
 * TX start
 */
static int spi_tx_start(spi_data_t * spi)
{
#ifdef CONFIG_MP201 /* fixed length */
/* comment: 不要変数削除 */
#else
	spi_mode_t spi_mode;
#endif
	unsigned int addr = 0;
	unsigned int bufsize = 0;
	int intmask = 0;
	int size = 0;
	int ret = 0;

	if (spi == NULL) {
		return -EINVAL;
	}

	/* buffer is empty */
	if (spi->trans->buf.state & SPI_BUFF_EMPTY) {
		return 0;
	}

	/* status to active */
	spi->trans->state = SPI_ACTIVE;

	bufsize = spi->mode->blksize * spi->trans->buf.blknum;
	size = spi->trans->buf.usr_ptr - spi->trans->buf.dma_ptr;
	if (size <= 0) {
		size = bufsize - spi->trans->buf.dma_ptr;
	}

#ifdef CONFIG_MP201 /* fixed length, clock control */
/* comment: 固定長転送のためサイズチェック不要
            自動クロック制御使用のため、SCLK制御不要 */
#else
	/* dma mode change */
	if (spi->mode->m_s == SPI_M_S_MASTER) {
		if (spi->trans->dma == SPI_DMA_ON) {
			if (size < spi_dma_size[spi->mode->sclk]) {
				memcpy((char *)&spi_mode, (char *)spi->mode,
				       sizeof(spi_mode_t));
				spi_mode.dma = SPI_DMA_OFF;
				spi_set_mode(spi, &spi_mode);
			}
		} else if ((spi->trans->dma == SPI_DMA_OFF)
			   && (spi->mode->dma == SPI_DMA_ON)) {
			if (size >= spi_dma_size[spi->mode->sclk]) {
				spi_set_mode(spi, spi->mode);
			}
		}
		/* clock on */
		mp200_pmu_open_clockgate(spi->smu->sclk);
	}
#endif

	/* initialize error status */
#if 0
	*spi->regs->ffclr |= SPx_FFCLR_ALLERR_CLR;
#else
/* comment: write-onlyレジスタなので代入に変更
            Error要因だけでなく全要因指定に変更    */
	*spi->regs->ffclr = SPx_FFCLR_ALL_CLR;
#endif

	/* interrupt enable (TERR/TX_UDR/RX_OVR) */
	*spi->regs->enset |= SPx_ENSET_ALLERR_EN;

#ifdef CONFIG_MP201 /* fixed length */
/* comment: DMAモード保持変数削除に伴い参照変数を変更
 *          DMA bit8/16/32幅転送の対応
 *          DMAモードエンディアン設定追加              */
	if (spi->mode->dma == SPI_DMA_ON) {
		unsigned int mode = 0;
		switch (spi->mode->nb) {
		case SPI_NB_8BIT: 
			mode = MP200_DMAC_AMODE_BIT8;
			break;
		case SPI_NB_16BIT: 
			mode = MP200_DMAC_AMODE_BIT16 | MP200_DMAC_ENDI_W10;
			break;
		case SPI_NB_32BIT: 
			mode = MP200_DMAC_AMODE_BIT32 | MP200_DMAC_ENDI_W3210;
			break;
		default:
			return -EINVAL;
		}
		spi->trans->dma_regs->mode = mode;
		spi->trans->dma_regs->aoff = 0;
		spi->trans->dma_regs->leng = size;
		spi->trans->dma_regs->asize = 
		    (size > SPI_DMA_BLOCK_SIZE_MAX) ?
		     SPI_DMA_BLOCK_SIZE_MAX : size; /* asize: '0'設定禁止 */
		spi->trans->dma_regs->asize_count = 0;
		intmask = MP200_DMAC_INT_ERROR_EN | MP200_DMAC_INT_LENG_EN;

		if (spi->mode->m_s == SPI_M_S_MASTER) {
			/* interrupt enable (TX_STOP) */
			*spi->regs->enset |= SPx_ENSET_TX_STOP_EN;
		}
#else
	if (spi->trans->dma == SPI_DMA_ON) {
		if (spi->mode->m_s == SPI_M_S_MASTER) {
			/* DMA start (block) */
			spi->trans->dma_regs->mode = MP200_DMAC_AMODE_BIT32;
			spi->trans->dma_regs->aoff = 0;
			spi->trans->dma_regs->leng = size;
			spi->trans->dma_regs->asize =
			    size - spi_rsv_size[spi->mode->sclk];
			spi->trans->dma_regs->asize_count = 0;
			intmask =
			    MP200_DMAC_INT_ERROR_EN | MP200_DMAC_INT_BLOCK_EN;
		} else {
			/* DMA start (length) */
			spi->trans->dma_regs->mode = MP200_DMAC_AMODE_BIT32;
			spi->trans->dma_regs->aoff = 0;
			spi->trans->dma_regs->leng = size;
			spi->trans->dma_regs->asize =
			    (size >
			     SPI_DMA_BLOCK_SIZE_MAX) ? SPI_DMA_BLOCK_SIZE_MAX :
			    size;
			spi->trans->dma_regs->asize_count = 0;
			intmask =
			    MP200_DMAC_INT_ERROR_EN | MP200_DMAC_INT_LENG_EN;
		}
#endif
		ret =
		    mp200_start_dma(spi->trans->tx_lch,
				    spi->trans->buf.dma_addr +
				    spi->trans->buf.dma_ptr, 0,
				    spi->trans->tx_data, intmask);
		if (ret < 0) {
			spi_tx_stop(spi);
#ifdef SPI_DEBUG
			printk(KERN_INFO "%s(): dma error\n", __FUNCTION__);
#endif
			return ret;
		}
	} else {
		/* interrupt enable (END) */
		*spi->regs->enset |= SPx_ENSET_END_EN;

		/* set data */
		addr = spi->trans->buf.top + spi->trans->buf.dma_ptr;
		*spi->regs->tx_data = *(unsigned int *)addr;
	}

	/* TX start */
	*spi->regs->control |= (SPx_CONTROL_WRT | SPx_CONTROL_START);

	return 0;
}

#ifdef CONFIG_MP201 /* fixed length */
/* comment: 固定長DMA転送を使用するため、端数CPU転送用の処理不要 */
/* no code */
#else
/*
 * TX restart
 */
static int spi_tx_restart(spi_data_t * spi)
{
	unsigned int rp = 0;
	unsigned int wp = 0;
	unsigned int dma_pos = 0;
	unsigned int bufsize = 0;
	int dma_fifo_data = 0;
	int spi_fifo_data = 0;
	int ret = 0;

	if (spi == NULL) {
		return -EINVAL;
	}

	if (spi->trans->dma == SPI_DMA_ON) {
		/* calculate transfer completion data size */
		dma_pos = mp200_get_dma_pos(spi->trans->tx_lch);
		dma_pos = dma_pos - spi->trans->buf.dma_addr;
		if (*spi->regs->control & SPx_CONTROL_TX_FULL) {
			spi_fifo_data = SPI_FIFO_MAX * 4;
		} else {
			wp = (*spi->regs->
			      tx_fifo_p & SPx_TX_FIFO_P_TX_FIFO_WP) >> 8;
			rp = (*spi->regs->tx_fifo_p & SPx_TX_FIFO_P_TX_FIFO_RP);
			spi_fifo_data = wp - rp;
			if (spi_fifo_data < 0) {
				spi_fifo_data = spi_fifo_data + SPI_FIFO_MAX;
			}
			spi_fifo_data = spi_fifo_data * 4;
		}
		dma_fifo_data =
		    (spi->trans->dma_regs->wcount -
		     spi->trans->dma_regs->rcount);

		/* update dma pointer */
		bufsize = spi->mode->blksize * spi->trans->buf.blknum;
		spi->trans->buf.dma_ptr =
		    dma_pos - dma_fifo_data - spi_fifo_data;
		if (spi->trans->buf.dma_ptr == bufsize) {
			spi->trans->buf.dma_ptr = 0;
		}
#ifdef SPI_DEBUG
		printk(KERN_INFO "%s(): dma pos = %d\n", __FUNCTION__, dma_pos);
		printk(KERN_INFO "%s(): rcount  = %d\n", __FUNCTION__,
		       spi->trans->dma_regs->rcount);
		printk(KERN_INFO "%s(): wcount  = %d\n", __FUNCTION__,
		       spi->trans->dma_regs->wcount);
		printk(KERN_INFO "%s(): tx_full = %d\n", __FUNCTION__,
		       (*spi->regs->control & SPx_CONTROL_TX_FULL));
		printk(KERN_INFO "%s(): rp      = %d\n", __FUNCTION__, rp);
		printk(KERN_INFO "%s(): wp      = %d\n", __FUNCTION__, wp);
		printk(KERN_INFO "%s(): dma ptr = %d\n", __FUNCTION__,
		       (spi->trans->buf.dma_ptr));
#endif

		/* update buffer status */
		if (spi->trans->buf.dma_ptr == spi->trans->buf.usr_ptr) {
			spi->trans->buf.state |= SPI_BUFF_EMPTY;
		}
		if (spi->trans->buf.state & SPI_BUFF_FULL) {
			spi->trans->buf.state &= ~(SPI_BUFF_FULL);
		}

		/* reset and set mode */
		spi_set_mode(spi, spi->mode);
	}

	if (spi->trans->buf.state & SPI_BUFF_EMPTY) {
		spi_tx_stop(spi);
	} else {
		ret = spi_tx_start(spi);
		if (ret < 0) {
			return ret;
		}
	}

	return 0;
}
#endif

/*
 * SPI interrupt callback
 */
static irqreturn_t spi_interrupt(int irq, void *dev_id, struct pt_regs *regs)
{
	spi_data_t *spi;
	unsigned int status = 0;
	unsigned int bufsize = 0;
	int ret = 0;

	if (dev_id == NULL) {
		return IRQ_NONE;
	}
	spi = (spi_data_t *) dev_id;

	/* interrupt status */
	status = *spi->regs->status;

	/* initialize interrupt status */
	*spi->regs->ffclr |= status;

	/* error check (TERR/TX_UDR/RX_OVR) */
	if (status & SPx_STATUS_ALLERR) {
		spi->trans->spi_err = (status & SPx_STATUS_ALLERR);
		if (status & SPx_STATUS_TX_UDR) {
			if (spi->mode->m_s == SPI_M_S_MASTER) {
				spi_tx_stop(spi);
			} else {
				if (spi->trans->dma_regs->wcount == 0) {
					spi->trans->spi_err &=
					    ~(SPx_STATUS_TX_UDR);
#ifdef CONFIG_MP201 /* fixed length */
/* comment: スレーブの場合、固定長DMA転送ができないため要求サイズ分転送後
 *          UDRエラーが発生する。この場合、通常の転送完了扱いとする。
 *          DMAレングス割り込みでバッファ情報は更新されており、ここでは不要。
 *          未送信データが存在する場合は、送信処理を行う。未送信データが無い
 *          場合は終了処理をおこなう。                                       */
					if (spi->trans->buf.state 
					    & SPI_BUFF_EMPTY) {
						spi_tx_stop(spi);
					} else {
						ret = spi_tx_start(spi);
						if (ret < 0) {
							spi_tx_stop(spi);
						}
					}
#else
					ret = spi_tx_restart(spi);
					if (ret < 0) {
						spi_tx_stop(spi);
					}
#endif
				} else {
					spi_tx_stop(spi);
				}
			}
		}
		if (status & SPx_STATUS_RX_OVR) {
			if (spi->mode->m_s == SPI_M_S_MASTER) {
				if (spi->trans->dma_regs->rcount == 0) {
					spi->trans->spi_err &=
					    ~(SPx_STATUS_RX_OVR);
					spi_stop(spi);
					return IRQ_HANDLED;
				} else {
					spi_rx_stop(spi);
				}
			} else {
				spi_rx_stop(spi);
			}
		}
		if (status & SPx_STATUS_TERR) {
			spi_rx_stop(spi);
		}
		if (waitqueue_active(&spi->trans->wait)) {
			wake_up_interruptible(&spi->trans->wait);
		}
#ifdef SPI_DEBUG
		printk(KERN_INFO "%s(): status = %08x\n", __FUNCTION__, status);
#endif
		return IRQ_HANDLED;
	}
#ifdef SPI_DEBUG
	if (status & SPx_STATUS_RDV) {
		printk(KERN_INFO "%s(): RDV is not supported\n", __FUNCTION__);
		return IRQ_NONE;
	}
#endif

#ifdef CONFIG_MP201 /* fixed length */
/* comment: TX_STOP割り込み処理追加
 *          FIFOがEMPでない場合は、指定サイズ分転送できなかったためエラー
 *          転送完了時は、未送信データが存在する場合は送信処理を行う。
 *          未送信データが無い場合は終了処理をおこなう。                 */
	if (status & SPx_STATUS_TX_STOP) { /* master only */
		if(*spi->regs->control & SPx_CONTROL_TX_EMP) {
			if (spi->trans->buf.state & SPI_BUFF_EMPTY) {
				spi_tx_stop(spi);
			} else {
				ret = spi_tx_start(spi);
				if (ret < 0) {
					spi_tx_stop(spi);
				}
			}
		}
		else { /* error */
			spi->trans->spi_err = SPx_STATUS_TX_STOP;
			spi_tx_stop(spi);
		}
		if (waitqueue_active(&spi->trans->wait)) {
			wake_up_interruptible(&spi->trans->wait);
		}
#ifdef SPI_DEBUG
		printk(KERN_INFO "%s(): status = %08x\n", __FUNCTION__, status);
#endif
		return IRQ_HANDLED;
	}
#endif

	/* CPU write */
	if (status & SPx_STATUS_END) {
		/* update dma pointer */
		bufsize = spi->mode->blksize * spi->trans->buf.blknum;
		spi->trans->buf.dma_ptr = spi->trans->buf.dma_ptr + 4;
		if (spi->trans->buf.dma_ptr == bufsize) {
			spi->trans->buf.dma_ptr = 0;
		}

		/* update buffer status */
		if (spi->trans->buf.dma_ptr == spi->trans->buf.usr_ptr) {
			spi->trans->buf.state |= SPI_BUFF_EMPTY;
		}
		if (spi->trans->buf.state & SPI_BUFF_FULL) {
			spi->trans->buf.state &= ~(SPI_BUFF_FULL);
		}

		if (spi->trans->buf.state & SPI_BUFF_EMPTY) {
			spi_tx_stop(spi);
			if (waitqueue_active(&spi->trans->wait)) {
				wake_up_interruptible(&spi->trans->wait);
			}
		} else {
			ret = spi_tx_start(spi);
			if (ret < 0) {
				spi_tx_stop(spi);
				if (waitqueue_active(&spi->trans->wait)) {
					wake_up_interruptible(&spi->trans->
							      wait);
				}
				return IRQ_HANDLED;
			}
		}
	}

	return IRQ_HANDLED;
}

/*
 * DMA interrupt callback (RX)
 */
static void spi_interrupt_dma_rx(void *data, int intsts, int intrawsts)
{
	spi_data_t *spi;
	unsigned int dma_pos = 0;
	unsigned int bufsize = 0;
	int size = 0;
	int blanksize = 0;

	if (data == NULL) {
		return;
	}
	spi = (spi_data_t *) data;

	/* error check */
	if (intsts & (MP200_DMAC_INT_ERROR_WR | MP200_DMAC_INT_ERROR_RD)) {
		spi->trans->dma_err =
		    (intsts &
		     (MP200_DMAC_INT_ERROR_WR | MP200_DMAC_INT_ERROR_RD));
		spi_rx_stop(spi);
		if (waitqueue_active(&spi->trans->wait)) {
			wake_up_interruptible(&spi->trans->wait);
		}
#ifdef SPI_DEBUG
		printk(KERN_INFO "%s(): intsts = %08x\n", __FUNCTION__, intsts);
#endif
		return;
	}

	if (((spi->mode->m_s == SPI_M_S_MASTER)
	     && (intsts & MP200_DMAC_INT_LENG_WR))
	    || ((spi->mode->m_s == SPI_M_S_SLAVE)
		&& (intsts & MP200_DMAC_INT_BLOCK_WR))) {
		bufsize = spi->mode->blksize * spi->trans->buf.blknum;
		dma_pos = mp200_get_dma_pos(spi->trans->rx_lch);
		dma_pos = dma_pos - spi->trans->buf.dma_addr;
		size = dma_pos - spi->trans->buf.dma_ptr;
		if (size <= 0) {
			size = size + bufsize;
		}

		/* calculate blank size */
		if (spi->trans->buf.state & SPI_BUFF_FULL) {
			blanksize = 0;
		} else {
			blanksize =
			    spi->trans->buf.usr_ptr - spi->trans->buf.dma_ptr;
			if (blanksize <= 0) {
				blanksize = blanksize + bufsize;
			}
		}

		/* update dma counter */
		spi->trans->buf.dma_ptr = spi->trans->buf.dma_ptr + size;
		if (spi->trans->buf.dma_ptr >= bufsize) {
			spi->trans->buf.dma_ptr =
			    spi->trans->buf.dma_ptr - bufsize;
		}

		/* update buffer status */
		if (blanksize < size) {
			spi->trans->buf.state |=
			    (SPI_BUFF_FULL | SPI_BUFF_OVER_ERR);
			spi_rx_stop(spi);
#ifdef SPI_DEBUG
			printk(KERN_INFO "%s(): buffer overwrited\n",
			       __FUNCTION__);
#endif
		} else {
			if (spi->trans->buf.dma_ptr == spi->trans->buf.usr_ptr) {
				spi->trans->buf.state |= SPI_BUFF_FULL;
			}
			if (spi->trans->buf.state & SPI_BUFF_EMPTY) {
				spi->trans->buf.state &= ~(SPI_BUFF_EMPTY);
			}
		}

		if (spi->mode->m_s == SPI_M_S_MASTER) {
			spi_rx_stop(spi);
		}

		if (waitqueue_active(&spi->trans->wait)) {
			wake_up_interruptible(&spi->trans->wait);
		}
	}
}

/*
 * DMA interrupt callback (TX)
 */
static void spi_interrupt_dma_tx(void *data, int intsts, int intrawsts)
{
	spi_data_t *spi;
#ifndef CONFIG_MP201 /* fixed length */
/* comment: 使用しないので削除 */
	int ret = 0;
#endif
	if (data == NULL) {
		return;
	}
	spi = (spi_data_t *) data;

	/* error check */
	if (intsts & (MP200_DMAC_INT_ERROR_WR | MP200_DMAC_INT_ERROR_RD)) {
		spi->trans->dma_err =
		    (intsts &
		     (MP200_DMAC_INT_ERROR_WR | MP200_DMAC_INT_ERROR_RD));
		spi_tx_stop(spi);
		if (waitqueue_active(&spi->trans->wait)) {
			wake_up_interruptible(&spi->trans->wait);
		}
#ifdef SPI_DEBUG
		printk(KERN_INFO "%s(): intsts = %08x\n", __FUNCTION__, intsts);
#endif
		return;
	}

#ifdef CONFIG_MP201 /* fixed length */
/* comment: ブロック割り込み削除し、マスター/スレーブにかかわらずレングス
 *          割り込み時にバッファ情報の更新を行う。                       */
	if(intsts & MP200_DMAC_INT_LENG_WR) {
		spi->trans->buf.dma_ptr = 
		    mp200_get_dma_pos(spi->trans->tx_lch)
		     - spi->trans->buf.dma_addr;
		if (spi->trans->buf.dma_ptr == 
		    (spi->mode->blksize * spi->trans->buf.blknum)) {
			spi->trans->buf.dma_ptr = 0;
		}
#ifdef SPI_DEBUG
		printk(KERN_INFO "%s(): dma ptr = %d\n", __FUNCTION__,
		       (spi->trans->buf.dma_ptr));
#endif
		/* update buffer status */
		if (spi->trans->buf.dma_ptr == spi->trans->buf.usr_ptr) {
			spi->trans->buf.state |= SPI_BUFF_EMPTY;
		}
		if (spi->trans->buf.state & SPI_BUFF_FULL) {
			spi->trans->buf.state &= ~(SPI_BUFF_FULL);
		}

		if (waitqueue_active(&spi->trans->wait)) {
			wake_up_interruptible(&spi->trans->wait);
		}
	}
#else
	if ((spi->mode->m_s == SPI_M_S_MASTER)
	    && (intsts & MP200_DMAC_INT_BLOCK_RD)) {
		if (*spi->regs->control & SPx_CONTROL_START) {
			*spi->regs->control |= SPx_CONTROL_STOP;
			udelay(((1000 * 32) /
				spi_clock_table[spi->mode->sclk]) + 1);
		}
		mp200_stop_dma(spi->trans->tx_lch);

		/* TX restart */
		ret = spi_tx_restart(spi);
		if (ret < 0) {
			spi_tx_stop(spi);
		}

		if (waitqueue_active(&spi->trans->wait)) {
			wake_up_interruptible(&spi->trans->wait);
		}
	}
#endif
}

/*
 * read
 */
static ssize_t spi_read(unsigned char dev, struct file *file, char __user * buf,
			size_t count, loff_t * ppos)
{
	DECLARE_WAITQUEUE(wait, current);
	spi_data_t *spi;
#if 0 /* comment: 割り込み状態保持不要なため削除 */
	unsigned long flags;
#endif
	unsigned int errsts;
	unsigned int dma_ptr;
	unsigned int usr_ptr;
	unsigned int end;
	unsigned int cpsize;
	unsigned int bufsize;
	unsigned int len = 0;
	int size = 0;
	int ret = 0;

#ifdef SPI_DEBUG
	printk(KERN_INFO "%s(): count = %d\n", __FUNCTION__, count);
#endif

	/* device is not found */
	if (dev > SPI_DEV_MAX) {
		return -ENODEV;
	}

	spi = &spi_private[dev];

	/* seek is not supported */
	if (*ppos != file->f_pos) {
		return -ESPIPE;
	}

	/* size check */
	if (count == 0) {
		return 0;
	}
	if (count < 4) {
		return -EINVAL;
	}
	if ((count % 4) != 0) {
		count = (count / 4) * 4;
	}

	/* RX start */
	bufsize = spi->mode->blksize * spi->trans->buf.blknum;
#if 0 
/* comment: エラーを検知できなくなるので処理移動(削除) */

	if (!(spi->trans->state & SPI_ACTIVE)) {
		ret = spi_allocate_buffer(spi, spi->mode->blksize);
		if (ret < 0) {
			return ret;
		}
#ifdef CONFIG_MP201 /* fixed length */
/* comment: DMAモード保持変数削除に伴い参照変数を変更 */
		if (spi->mode->dma == SPI_DMA_ON) {
#else
		if (spi->trans->dma == SPI_DMA_ON) {
#endif
			ret = spi_request_dma(spi);
			if (ret < 0) {
				return ret;
			}
		}
		spi_init_data(spi);

		if (spi->mode->m_s == SPI_M_S_MASTER) {
			size =
			    ((count - len) > bufsize) ? bufsize : (count - len);
		} else {
			size = 0;
		}
		ret = spi_rx_start(spi, size);
		if (ret < 0) {
			return ret;
		}
	}
#endif
	add_wait_queue(&spi->trans->wait, &wait);
	while ((count - len) > 0) {
		set_current_state(TASK_INTERRUPTIBLE);

#if 0
		/* SPI error check */
		errsts = spi->trans->spi_err;
		if (errsts != 0) {
			ret = -EIO;
			break;
		}
		/* DMA error check */
		errsts = spi->trans->dma_err;
		if (errsts != 0) {
			ret = -EIO;
			break;
		}
		/* buffer error check */
		errsts = spi->trans->buf.state & SPI_BUFF_OVER_ERR;
		if (errsts != 0) {
			ret = -EIO;
			break;
		}
#else
/* comment: エラーを検知できなくなるので処理移動(追加)
 *          条件"バッファempty"を追加                 
 *          変数クリアおよび排他制御追加              */

		spin_lock_irq(&spi->trans->spinlock);
		errsts = spi->trans->spi_err;
		spi->trans->spi_err = 0;
		spin_unlock_irq(&spi->trans->spinlock);
		if (errsts != 0) {
			ret = -EIO;
			break;
		}
		/* DMA error check */
		spin_lock_irq(&spi->trans->spinlock);
		errsts = spi->trans->dma_err;
		spi->trans->dma_err = 0;
		spin_unlock_irq(&spi->trans->spinlock);
		if (errsts != 0) {
			ret = -EIO;
			break;
		}
		/* buffer error check */
		spin_lock_irq(&spi->trans->spinlock);
		errsts = spi->trans->buf.state & SPI_BUFF_OVER_ERR;
		spi->trans->buf.state &= ~SPI_BUFF_OVER_ERR;
		spin_unlock_irq(&spi->trans->spinlock);
		if (errsts != 0) {
			ret = -EIO;
			break;
		}

		if (!(spi->trans->state == SPI_ACTIVE)
		    && (spi->trans->buf.state & SPI_BUFF_EMPTY)) {
			ret = spi_allocate_buffer(spi, spi->mode->blksize);
			if (ret < 0) {
				return ret;
			}
#ifdef CONFIG_MP201 /* fixed length */
/* comment: DMAモード保持変数削除に伴い参照変数を変更 */
			if (spi->mode->dma == SPI_DMA_ON) {
#else
			if (spi->trans->dma == SPI_DMA_ON) {
#endif
				ret = spi_request_dma(spi);
				if (ret < 0) {
					return ret;
				}
			}
			spi_init_data(spi);

			if (spi->mode->m_s == SPI_M_S_MASTER) {
				size =
			 	   ((count - len) > bufsize) ?
			 	    bufsize : (count - len);
			} else {
				size = 0;
			}
			ret = spi_rx_start(spi, size);
			if (ret < 0) {
				return ret;
			}
		}

#endif

		/* readable */
		if (!(spi->trans->buf.state & SPI_BUFF_EMPTY)) {
			dma_ptr = spi->trans->buf.top + spi->trans->buf.dma_ptr;
			usr_ptr = spi->trans->buf.top + spi->trans->buf.usr_ptr;
			end = spi->trans->buf.end;
			cpsize =
			    (dma_ptr >
			     usr_ptr) ? (dma_ptr - usr_ptr) : (end - usr_ptr);
			if (cpsize > (count - len)) {
				cpsize = count - len;
			}
			ret =
			    spi_copy_to_user((char *)(buf + len),
					     (char *)usr_ptr, cpsize);
			if (ret < 0) {
				break;
			}
			len = len + cpsize;

			/* update user pointer */
#if 0
			spin_lock_irqsave(&spi->trans->spinlock, flags);
#else
/* comment: 割り込み状態保持不要のため変更 */
			spin_lock_irq(&spi->trans->spinlock);
#endif
			spi->trans->buf.usr_ptr =
			    spi->trans->buf.usr_ptr + cpsize;
			if (spi->trans->buf.usr_ptr == bufsize) {
				spi->trans->buf.usr_ptr = 0;
			}

			/* update buffer status */
			if (spi->trans->buf.usr_ptr == spi->trans->buf.dma_ptr) {
				spi->trans->buf.state |= SPI_BUFF_EMPTY;
			}
			if (spi->trans->buf.state & SPI_BUFF_FULL) {
				spi->trans->buf.state &= ~(SPI_BUFF_FULL);
			}
#if 0
			spin_unlock_irqrestore(&spi->trans->spinlock, flags);
#else
/* comment: 割り込み状態保持不要のため変更 */
			spin_unlock_irq(&spi->trans->spinlock);
#endif
			if (spi->mode->m_s == SPI_M_S_MASTER) {
				size =
				    ((count - len) >
				     bufsize) ? bufsize : (count - len);
				if (size > 0) {
					spi_init_data(spi);
					ret = spi_rx_start(spi, size);
					if (ret < 0) {
						break;
					}
				}
			}
		} else {
			if (file->f_flags & O_NONBLOCK) {
				if (len == 0) {
					ret = -EAGAIN;
				}
				break;
			}
			if (signal_pending(current)) {
				ret = -ERESTARTSYS;
				break;
			}
			schedule();
		}
	}
	remove_wait_queue(&spi->trans->wait, &wait);
	set_current_state(TASK_RUNNING);

	if (ret < 0) {
		if (ret != -EAGAIN) {
#if 0
			if (spi->trans->state & SPI_ACTIVE) {
#else
/* comment: 代入のみのため、変数そのままの判定に変更 */
			if (spi->trans->state == SPI_ACTIVE) {
#endif
				spi_rx_stop(spi);
			}
		}
		return ret;
	}

	/* return copy size */
	return len;
}

#ifdef CONFIG_MP201
int
mp201_spi_read(unsigned char dev, char *buf, unsigned int count, unsigned char block_mode)
{
	DECLARE_WAITQUEUE(wait, current);
	spi_data_t *spi;
#if 0 /* comment: 割り込み状態保持不要なため削除 */
	unsigned long flags;
#endif
	unsigned int errsts;
	unsigned int dma_ptr;
	unsigned int usr_ptr;
	unsigned int end;
	unsigned int cpsize;
	unsigned int bufsize;
	unsigned int len = 0;
	int size = 0;
	int ret = 0;

#ifdef SPI_DEBUG
	printk(KERN_INFO "%s(): count = %d\n", __FUNCTION__, count);
#endif

	/* device is not found */
	if(dev != SPI_DEV_SPI0 && dev != SPI_DEV_SPI1){
	  return -EACCES;
	}

	spi = &spi_private[dev];

	if(spi->k_flag != SPI_READ_MODE){
	  return -EACCES;
	}

	/* size check */
	if (count == 0) {
		return 0;
	}
	if (count < 4) {
		return -EINVAL;
	}
	if ((count % 4) != 0) {
		count = (count / 4) * 4;
	}

	if(down_interruptible(&spi->sem_rw)){
	  return -ERESTARTSYS;
	}

	/* RX start */
	bufsize = spi->mode->blksize * spi->trans->buf.blknum;
#if 0 
/* comment: エラーを検知できなくなるので処理移動(削除) */

	if (!(spi->trans->state & SPI_ACTIVE)) {
		ret = spi_allocate_buffer(spi, spi->mode->blksize);
		if (ret < 0) {
			return ret;
		}
#ifdef CONFIG_MP201 /* fixed length */
/* comment: DMAモード保持変数削除に伴い参照変数を変更 */
		if (spi->mode->dma == SPI_DMA_ON) {
#else
		if (spi->trans->dma == SPI_DMA_ON) {
#endif
			ret = spi_request_dma(spi);
			if (ret < 0) {
			  return ret;
			}
		}
		spi_init_data(spi);

		if (spi->mode->m_s == SPI_M_S_MASTER) {
			size =
			    ((count - len) > bufsize) ? bufsize : (count - len);
		} else {
			size = 0;
		}
		ret = spi_rx_start(spi, size);
		if (ret < 0) {
		  return ret;
		}
	}
#endif
	add_wait_queue(&spi->trans->wait, &wait);
	while ((count - len) > 0) {
		set_current_state(TASK_INTERRUPTIBLE);

#if 0
		/* SPI error check */
		errsts = spi->trans->spi_err;
		if (errsts != 0) {
			ret = -EIO;
			break;
		}
		/* DMA error check */
		errsts = spi->trans->dma_err;
		if (errsts != 0) {
			ret = -EIO;
			break;
		}
		/* buffer error check */
		errsts = spi->trans->buf.state & SPI_BUFF_OVER_ERR;
		if (errsts != 0) {
			ret = -EIO;
			break;
		}
#else
/* comment: エラーを検知できなくなるので処理移動(追加)
 *          条件"バッファempty"を追加                 
 *          変数クリアおよび排他制御追加              */

		spin_lock_irq(&spi->trans->spinlock);
		errsts = spi->trans->spi_err;
		spi->trans->spi_err = 0;
		spin_unlock_irq(&spi->trans->spinlock);
		if (errsts != 0) {
			ret = -EIO;
			break;
		}
		/* DMA error check */
		spin_lock_irq(&spi->trans->spinlock);
		errsts = spi->trans->dma_err;
		spi->trans->dma_err = 0;
		spin_unlock_irq(&spi->trans->spinlock);
		if (errsts != 0) {
			ret = -EIO;
			break;
		}
		/* buffer error check */
		spin_lock_irq(&spi->trans->spinlock);
		errsts = spi->trans->buf.state & SPI_BUFF_OVER_ERR;
		spi->trans->buf.state &= ~SPI_BUFF_OVER_ERR;
		spin_unlock_irq(&spi->trans->spinlock);
		if (errsts != 0) {
			ret = -EIO;
			break;
		}

		if (!(spi->trans->state == SPI_ACTIVE)
		    && (spi->trans->buf.state & SPI_BUFF_EMPTY)) {
			ret = spi_allocate_buffer(spi, spi->mode->blksize);
			if (ret < 0) {
			  up(&spi->sem_rw);
			  return ret;
			}
#ifdef CONFIG_MP201 /* fixed length */
/* comment: DMAモード保持変数削除に伴い参照変数を変更 */
			if (spi->mode->dma == SPI_DMA_ON) {
#else
			if (spi->trans->dma == SPI_DMA_ON) {
#endif
				ret = spi_request_dma(spi);
				if (ret < 0) {
				  up(&spi->sem_rw);
				  return ret;
				}
			}
			spi_init_data(spi);

			if (spi->mode->m_s == SPI_M_S_MASTER) {
				size =
			 	   ((count - len) > bufsize) ?
			 	    bufsize : (count - len);
			} else {
				size = 0;
			}
			ret = spi_rx_start(spi, size);
			if (ret < 0) {
				return ret;
			}
		}

#endif

		/* readable */
		if (!(spi->trans->buf.state & SPI_BUFF_EMPTY)) {
			dma_ptr = spi->trans->buf.top + spi->trans->buf.dma_ptr;
			usr_ptr = spi->trans->buf.top + spi->trans->buf.usr_ptr;
			end = spi->trans->buf.end;
			cpsize =
			    (dma_ptr >
			     usr_ptr) ? (dma_ptr - usr_ptr) : (end - usr_ptr);
			if (cpsize > (count - len)) {
				cpsize = count - len;
			}
			memcpy((char *)(buf + len),
			       (char *)usr_ptr, cpsize);

			len = len + cpsize;

			/* update user pointer */
#if 0
			spin_lock_irqsave(&spi->trans->spinlock, flags);
#else
/* comment: 割り込み状態保持不要のため変更 */
			spin_lock_irq(&spi->trans->spinlock);
#endif
			spi->trans->buf.usr_ptr =
			    spi->trans->buf.usr_ptr + cpsize;
			if (spi->trans->buf.usr_ptr == bufsize) {
				spi->trans->buf.usr_ptr = 0;
			}

			/* update buffer status */
			if (spi->trans->buf.usr_ptr == spi->trans->buf.dma_ptr) {
				spi->trans->buf.state |= SPI_BUFF_EMPTY;
			}
			if (spi->trans->buf.state & SPI_BUFF_FULL) {
				spi->trans->buf.state &= ~(SPI_BUFF_FULL);
			}
#if 0
			spin_unlock_irqrestore(&spi->trans->spinlock, flags);
#else
/* comment: 割り込み状態保持不要のため変更 */
			spin_unlock_irq(&spi->trans->spinlock);
#endif
			if (spi->mode->m_s == SPI_M_S_MASTER) {
				size =
				    ((count - len) >
				     bufsize) ? bufsize : (count - len);
				if (size > 0) {
					spi_init_data(spi);
					ret = spi_rx_start(spi, size);
					if (ret < 0) {
						break;
					}
				}
			}
		} else {
			if (block_mode & SPI_NONBLOCK) {
			  if (len == 0) {
					ret = -EAGAIN;
				}
				break;
			}
			if (signal_pending(current)) {
				ret = -ERESTARTSYS;
				break;
			}
			schedule();
		}
	}
	remove_wait_queue(&spi->trans->wait, &wait);
	set_current_state(TASK_RUNNING);

	if (ret < 0) {
		if (ret != -EAGAIN) {
#if 0
			if (spi->trans->state & SPI_ACTIVE) {
#else
/* comment: 代入のみのため、変数そのままの判定に変更 */
			if (spi->trans->state == SPI_ACTIVE) {
#endif
				spi_rx_stop(spi);
			}
		 }
		up(&spi->sem_rw);	
		return ret;
	}

	/* return copy size */
	up(&spi->sem_rw);
	return len;
}

#endif

/*
 * write
 */
static ssize_t spi_write(unsigned char dev, struct file *file,
			 const char __user * buf, size_t count, loff_t * ppos)
{
	DECLARE_WAITQUEUE(wait, current);
	spi_data_t *spi;
#if 0 /* comment: 割り込み状態保持不要なため削除 */
	unsigned long flags;
#endif
	unsigned int errsts;
	unsigned int dma_ptr;
	unsigned int usr_ptr;
	unsigned int end;
	unsigned int cpsize;
	unsigned int bufsize;
	unsigned int len = 0;
	int ret = 0;

#ifdef SPI_DEBUG
	printk(KERN_INFO "%s(): count = %d\n", __FUNCTION__, count);
#endif

	/* device is not found */
	if (dev > SPI_DEV_MAX) {
		return -ENODEV;
	}

	spi = &spi_private[dev];

	/* seek is not supported */
	if (*ppos != file->f_pos) {
		return -ESPIPE;
	}

	/* size check */
	if (count == 0) {
		return 0;
	}
	if (count < 4) {
		return -EINVAL;
	}
	if ((count % 4) != 0) {
		count = (count / 4) * 4;
	}

#if 0
/* comment: エラーを検知できなくなるので処理移動(削除) */

	if (!(spi->trans->state & SPI_ACTIVE)) {
		ret = spi_allocate_buffer(spi, spi->mode->blksize);
		if (ret < 0) {
			return ret;
		}
#ifdef CONFIG_MP201 /* fixed length */
/* comment: DMAモード保持変数削除に伴い参照変数を変更 */
		if (spi->mode->dma == SPI_DMA_ON) {
#else
		if (spi->trans->dma == SPI_DMA_ON) {
#endif
			ret = spi_request_dma(spi);
			if (ret < 0) {
				return ret;
			}
		}
		spi_init_data(spi);
	}
#endif 
	add_wait_queue(&spi->trans->wait, &wait);
	while ((count - len) > 0) {
		set_current_state(TASK_INTERRUPTIBLE);

#if 0
		/* SPI error check */
		errsts = spi->trans->spi_err;
		if (errsts != 0) {
			ret = -EIO;
			break;
		}
		/* DMA error check */
		errsts = spi->trans->dma_err;
		if (errsts != 0) {
			ret = -EIO;
			break;
		}
#else
/* comment: エラーを検知できなくなるので処理移動(追加)
 *          条件"バッファempty"を追加                 
 *          変数クリアのため排他制御追加              */
		spin_lock_irq(&spi->trans->spinlock);
		errsts = spi->trans->spi_err;
		spi->trans->spi_err = 0;
		spin_unlock_irq(&spi->trans->spinlock);
		if (errsts != 0) {
			ret = -EIO;
			break;
		}
		/* DMA error check */
		spin_lock_irq(&spi->trans->spinlock);
		errsts = spi->trans->dma_err;
		spi->trans->dma_err = 0;
		spin_unlock_irq(&spi->trans->spinlock);
		if (errsts != 0) {
			ret = -EIO;
			break;
		}

		if (!(spi->trans->state == SPI_ACTIVE)
		    && (spi->trans->buf.state & SPI_BUFF_EMPTY)) {
			ret = spi_allocate_buffer(spi, spi->mode->blksize);
			if (ret < 0) {
				return ret;
			}
#ifdef CONFIG_MP201 /* fixed length */
/* comment: DMAモード保持変数削除に伴い参照変数を変更 */
			if (spi->mode->dma == SPI_DMA_ON) {
#else
			if (spi->trans->dma == SPI_DMA_ON) {
#endif
				ret = spi_request_dma(spi);
				if (ret < 0) {
					return ret;
				}
			}
			spi_init_data(spi);
		}
#endif
		/* writable */
		if (!(spi->trans->buf.state & SPI_BUFF_FULL)) {
			dma_ptr = spi->trans->buf.top + spi->trans->buf.dma_ptr;
			usr_ptr = spi->trans->buf.top + spi->trans->buf.usr_ptr;
			end = spi->trans->buf.end;
			cpsize =
			    (dma_ptr >
			     usr_ptr) ? (dma_ptr - usr_ptr) : (end - usr_ptr);
			if (cpsize > (count - len)) {
				cpsize = count - len;
			}
			ret =
			    spi_copy_from_user((char *)usr_ptr,
					       (char *)(buf + len), cpsize);
			if (ret < 0) {
				break;
			}
			len = len + cpsize;

			/* update user pointer */
#if 0
			spin_lock_irqsave(&spi->trans->spinlock, flags);
#else
/* comment: 割り込み状態保持不要のため変更 */
			spin_lock_irq(&spi->trans->spinlock);
#endif
			spi->trans->buf.usr_ptr =
			    spi->trans->buf.usr_ptr + cpsize;
			bufsize = spi->mode->blksize * spi->trans->buf.blknum;
			if (spi->trans->buf.usr_ptr == bufsize) {
				spi->trans->buf.usr_ptr = 0;
			}

			/* update buffer status */
			if (spi->trans->buf.usr_ptr == spi->trans->buf.dma_ptr) {
				spi->trans->buf.state |= SPI_BUFF_FULL;
			}
			if (spi->trans->buf.state & SPI_BUFF_EMPTY) {
				spi->trans->buf.state &= ~(SPI_BUFF_EMPTY);
			}
#if 0
			spin_unlock_irqrestore(&spi->trans->spinlock, flags);
#else
/* comment: 割り込み状態保持不要のため変更 */
			spin_unlock_irq(&spi->trans->spinlock);
#endif

			/* TX start */
#if 0
			if (!(spi->trans->state & SPI_ACTIVE)) {
#else
/* comment: 代入のみのため、変数そのままの判定に変更 */
			if (!(spi->trans->state == SPI_ACTIVE)) {
#endif
				ret = spi_tx_start(spi);
				if (ret < 0) {
					break;
				}
			}
		} else {
			if (file->f_flags & O_NONBLOCK) {
				if (len == 0) {
					ret = -EAGAIN;
				}
				break;
			}
			if (signal_pending(current)) {
				ret = -ERESTARTSYS;
				break;
			}
			schedule();
		}
	}
	remove_wait_queue(&spi->trans->wait, &wait);
	set_current_state(TASK_RUNNING);

	if (ret < 0) {
		if (ret != -EAGAIN) {
#if 0
			if (spi->trans->state & SPI_ACTIVE) {
#else
/* comment: 代入のみのため、変数そのままの判定に変更 */
			if (spi->trans->state == SPI_ACTIVE) {
#endif
				spi_tx_stop(spi);
			}
		}
		return ret;
	}

	/* return copy size */
	return len;
}

#ifdef CONFIG_MP201
int
mp201_spi_write(unsigned char dev, const char *buf,  unsigned int count, unsigned char block_mode)
{
	DECLARE_WAITQUEUE(wait, current);
	spi_data_t *spi;
#if 0 /* comment: 割り込み状態保持不要なため削除 */
	unsigned long flags;
#endif
	unsigned int errsts;
	unsigned int dma_ptr;
	unsigned int usr_ptr;
	unsigned int end;
	unsigned int cpsize;
	unsigned int bufsize;
	unsigned int len = 0;
	int ret = 0;

#ifdef SPI_DEBUG
	printk(KERN_INFO "%s(): count = %d\n", __FUNCTION__, count);
#endif

	/* device is not found */
	if(dev != SPI_DEV_SPI0 && dev != SPI_DEV_SPI1){
	  return -EACCES;
	}

	spi = &spi_private[dev];

	if(spi->k_flag != SPI_WRITE_MODE){
	  return -EACCES;
	}

	/* size check */
	if (count == 0) {
		return 0;
	}
	if (count < 4) {
		return -EINVAL;
	}
	if ((count % 4) != 0) {
		count = (count / 4) * 4;
	}

	if(down_interruptible(&spi->sem_rw)){
	  return -ERESTARTSYS;
	}
#if 0
/* comment: エラーを検知できなくなるので処理移動(削除) */

	if (!(spi->trans->state & SPI_ACTIVE)) {
		ret = spi_allocate_buffer(spi, spi->mode->blksize);
		if (ret < 0) {
			return ret;
		}
#ifdef CONFIG_MP201 /* fixed length */
/* comment: DMAモード保持変数削除に伴い参照変数を変更 */
		if (spi->mode->dma == SPI_DMA_ON) {
#else
		if (spi->trans->dma == SPI_DMA_ON) {
#endif
			ret = spi_request_dma(spi);
			if (ret < 0) {
				return ret;
			}
		}
		spi_init_data(spi);
	}
#endif 
	add_wait_queue(&spi->trans->wait, &wait);
	while ((count - len) > 0) {
		set_current_state(TASK_INTERRUPTIBLE);

#if 0
		/* SPI error check */
		errsts = spi->trans->spi_err;
		if (errsts != 0) {
			ret = -EIO;
			break;
		}
		/* DMA error check */
		errsts = spi->trans->dma_err;
		if (errsts != 0) {
			ret = -EIO;
			break;
		}
#else
/* comment: エラーを検知できなくなるので処理移動(追加)
 *          条件"バッファempty"を追加                 
 *          変数クリアのため排他制御追加              */
		spin_lock_irq(&spi->trans->spinlock);
		errsts = spi->trans->spi_err;
		spi->trans->spi_err = 0;
		spin_unlock_irq(&spi->trans->spinlock);
		if (errsts != 0) {
			ret = -EIO;
			break;
		}
		/* DMA error check */
		spin_lock_irq(&spi->trans->spinlock);
		errsts = spi->trans->dma_err;
		spi->trans->dma_err = 0;
		spin_unlock_irq(&spi->trans->spinlock);
		if (errsts != 0) {
			ret = -EIO;
			break;
		}

		if (!(spi->trans->state == SPI_ACTIVE)
		    && (spi->trans->buf.state & SPI_BUFF_EMPTY)) {
			ret = spi_allocate_buffer(spi, spi->mode->blksize);
			if (ret < 0) {
			  up(&spi->sem_rw);
			  return ret;
			}
#ifdef CONFIG_MP201 /* fixed length */
/* comment: DMAモード保持変数削除に伴い参照変数を変更 */
			if (spi->mode->dma == SPI_DMA_ON) {
#else
			if (spi->trans->dma == SPI_DMA_ON) {
#endif
				ret = spi_request_dma(spi);
				if (ret < 0) {
				  up(&spi->sem_rw);
				  return ret;
				}
			}
			spi_init_data(spi);
		}
#endif
		/* writable */
		if (!(spi->trans->buf.state & SPI_BUFF_FULL)) {
			dma_ptr = spi->trans->buf.top + spi->trans->buf.dma_ptr;
			usr_ptr = spi->trans->buf.top + spi->trans->buf.usr_ptr;
			end = spi->trans->buf.end;
			cpsize =
			    (dma_ptr >
			     usr_ptr) ? (dma_ptr - usr_ptr) : (end - usr_ptr);
			if (cpsize > (count - len)) {
				cpsize = count - len;
			}
			memcpy((char *)usr_ptr,
			       (char *)(buf + len), cpsize);

			len = len + cpsize;

			/* update user pointer */
#if 0
			spin_lock_irqsave(&spi->trans->spinlock, flags);
#else
/* comment: 割り込み状態保持不要のため変更 */
			spin_lock_irq(&spi->trans->spinlock);
#endif
			spi->trans->buf.usr_ptr =
			    spi->trans->buf.usr_ptr + cpsize;
			bufsize = spi->mode->blksize * spi->trans->buf.blknum;
			if (spi->trans->buf.usr_ptr == bufsize) {
				spi->trans->buf.usr_ptr = 0;
			}

			/* update buffer status */
			if (spi->trans->buf.usr_ptr == spi->trans->buf.dma_ptr) {
				spi->trans->buf.state |= SPI_BUFF_FULL;
			}
			if (spi->trans->buf.state & SPI_BUFF_EMPTY) {
				spi->trans->buf.state &= ~(SPI_BUFF_EMPTY);
			}
#if 0
			spin_unlock_irqrestore(&spi->trans->spinlock, flags);
#else
/* comment: 割り込み状態保持不要のため変更 */
			spin_unlock_irq(&spi->trans->spinlock);
#endif

			/* TX start */
#if 0
			if (!(spi->trans->state & SPI_ACTIVE)) {
#else
/* comment: 代入のみのため、変数そのままの判定に変更 */
			if (!(spi->trans->state == SPI_ACTIVE)) {
#endif
				ret = spi_tx_start(spi);
				if (ret < 0) {
					break;
				}
			}
		} else {
			if (block_mode & SPI_NONBLOCK) {
				if (len == 0) {
					ret = -EAGAIN;
				}
				break;
			}
			if (signal_pending(current)) {
				ret = -ERESTARTSYS;
				break;
			}
			schedule();
		}
	}
	remove_wait_queue(&spi->trans->wait, &wait);
	set_current_state(TASK_RUNNING);

	if (ret < 0) {
		if (ret != -EAGAIN) {
#if 0
			if (spi->trans->state & SPI_ACTIVE) {
#else
/* comment: 代入のみのため、変数そのままの判定に変更 */
			if (spi->trans->state == SPI_ACTIVE) {
#endif
				spi_tx_stop(spi);
			}
		}
		up(&spi->sem_rw);
		return ret;
	}
	up(&spi->sem_rw);
	/* return copy size */
	return len;
}
#endif
/*
 * poll
 */
static unsigned int spi_poll(unsigned char dev, struct file *file,
			     poll_table * wait)
{
	spi_data_t *spi;
	unsigned int mask = 0;

	/* device is not found */
	if (dev > SPI_DEV_MAX) {
		/* error */
		return POLLERR;
	}

	spi = &spi_private[dev];

	poll_wait(file, &spi->trans->wait, wait);

	/* readable */
	if (spi->opened & FMODE_READ) {
		if (!(spi->trans->buf.state & SPI_BUFF_EMPTY)) {
			mask |= (POLLIN | POLLRDNORM);
		}
	}

	/* writable */
	if (spi->opened & FMODE_WRITE) {
		if (!(spi->trans->buf.state & SPI_BUFF_FULL)) {
			mask |= (POLLOUT | POLLWRNORM);
		}
	}

	/* error check */
	if ((spi->trans->spi_err != 0) || (spi->trans->dma_err != 0) ||
	    (spi->trans->buf.state & SPI_BUFF_OVER_ERR)) {
		mask |= POLLERR;
	}
#ifdef SPI_DEBUG
	printk(KERN_INFO "%s(): mask = %s%s\n", __FUNCTION__,
	       (mask & POLLIN) ? "r" : "", (mask & POLLOUT) ? "w" : "");
#endif

	return mask;
}

/*
 * ioctl
 */
static int spi_ioctl(unsigned char dev, struct inode *inode, struct file *file,
		     unsigned int cmd, unsigned long arg)
{
	spi_data_t *spi;
	spi_mode_t mode;
	int ret = 0;

	/* device is not found */
	if (dev > SPI_DEV_MAX) {
		return -ENODEV;
	}

	spi = &spi_private[dev];

	if (_IOC_DIR(cmd) == _IOC_WRITE) {
#if 0
		if (spi->trans->state & SPI_ACTIVE) {
#else
/* comment: 代入のみのため、変数そのままの判定に変更 */
		if (spi->trans->state == SPI_ACTIVE) {
#endif
			return -EBUSY;
		}
	}

	switch (cmd) {
	case SPI_CMD_RX_START:
		if ((file->f_mode & FMODE_READ)
		    && (spi->mode->m_s == SPI_M_S_SLAVE)) {
#if 0
			if (!(spi->trans->state & SPI_ACTIVE)) {
#else
/* comment: 代入のみのため、変数そのままの判定に変更 */
			if (!(spi->trans->state == SPI_ACTIVE)) {
#endif
				ret =
				    spi_allocate_buffer(spi,
							spi->mode->blksize);
				if (ret < 0) {
					break;
				}
#ifdef CONFIG_MP201 /* fixed length */
/* comment: DMAモード保持変数削除に伴い参照変数を変更 */
				if (spi->mode->dma == SPI_DMA_ON) {
#else
				if (spi->trans->dma == SPI_DMA_ON) {
#endif
					ret = spi_request_dma(spi);
					if (ret < 0) {
						return ret;
					}
				}
				spi_init_data(spi);
				ret = spi_rx_start(spi, 0);
			}
		}
		break;

	case SPI_CMD_RX_STOP:
		if ((file->f_mode & FMODE_READ)
		    && (spi->mode->m_s == SPI_M_S_SLAVE)) {
#if 0
			if (spi->trans->state & SPI_ACTIVE) {
#else
/* comment: 代入のみのため、変数そのままの判定に変更 */
			if (spi->trans->state == SPI_ACTIVE) {
#endif
				spi_rx_stop(spi);
			}
		}
		break;

	case SPI_CMD_GET_MODE:
		if (copy_to_user
		    ((char *)arg, (char *)spi->mode, sizeof(spi_mode_t))) {
#ifdef SPI_DEBUG
			printk(KERN_INFO "%s(): copy error\n", __FUNCTION__);
#endif
			ret = -EFAULT;
		}
		break;

	case SPI_CMD_SET_MODE:
		if (copy_from_user
		    ((char *)&mode, (char *)arg, sizeof(spi_mode_t))) {
#ifdef SPI_DEBUG
			printk(KERN_INFO "%s(): copy error\n", __FUNCTION__);
#endif
			ret = -EFAULT;
			break;
		}

		ret = spi_set_mode(spi, &mode);
		if (ret < 0) {
			break;
		}
		memcpy((char *)spi->mode, (char *)&mode, sizeof(spi_mode_t));
		break;

	default:
		/* error */
		ret = -EINVAL;
		break;
	}

	return ret;
}

/*
 * open
 */
static int spi_open(unsigned char dev, struct inode *inode, struct file *file)
{
	spi_data_t *spi;
	int ret;

	/* device is not found */
	if (dev > SPI_DEV_MAX) {
		return -ENODEV;
	}

	spi = &spi_private[dev];

	if(down_interruptible(&spi->sem_open)){
	  return -ERESTARTSYS;
	}

	/* device is busy */
	if (spi->opened != 0) {
#ifdef SPI_DEBUG
		printk(KERN_INFO "%s(): device is busy\n", __FUNCTION__);
#endif
		ret = -EBUSY;
		goto err;
	}

	/* R/W mode is not supported */
	if ((file->f_mode & FMODE_READ) && (file->f_mode & FMODE_WRITE)) {
		ret = -EACCES;
		goto err;
	}

	spi->opened = file->f_mode;

	memset(&spi->trans->buf, 0x00, sizeof(spi_buf_t));
	spi->trans->dma_regs = NULL;

	/* initialize data */
	spi_init_data(spi);

	/* sleep disable */
	spi_sleep_flag++;

	/* reset off */
	spi_reset_ctrl(spi, SPI_OFF);

	/* request SPI interrupt */
	ret =
	    request_irq(spi->trans->int_spi, spi_interrupt, SA_INTERRUPT,
			SPI_NAME, (void *)spi);
	if (ret < 0) {
#ifdef SPI_DEBUG
		printk(KERN_INFO "%s(): request irq error\n", __FUNCTION__);
#endif
		goto err1;
	}

	/* set mode */
	spi_clock_source = spi_get_clock_source();
#ifdef CONFIG_MP201
	if(dev == SPI_DEV_SPI1){
		outl(inl(SMU_PCTL_SPI) & ~SPI_SMU_PCTL_SPI1, SMU_PCTL_SPI);
	}
#endif
	ret = spi_set_mode(spi, spi->mode);
	if (ret < 0) {
		goto err2;
	}

	init_waitqueue_head(&spi->trans->wait);

	up(&spi->sem_open);

	return 0;

      err2:
	spi_free_buffer(spi);
	free_irq(spi->trans->int_spi, (void *)spi);
      err1:
	spi_reset_ctrl(spi, SPI_ON);
	spi_sleep_flag--;
	spi->opened = 0;
      err:
	up(&spi->sem_open);

	return ret;
}
#ifdef CONFIG_MP201
/*
 * same open
 */
int 
mp201_spi_init(unsigned char dev, unsigned char rw_mode)
{
	spi_data_t *spi;
	int ret;

	/* device is not found */
	if(dev != SPI_DEV_SPI0 && dev != SPI_DEV_SPI1){
	  return -EACCES;
	}
	/* R/W mode is not supported */
	if(rw_mode != SPI_READ_MODE && rw_mode != SPI_WRITE_MODE){
	  return -EACCES;
	}

	spi = &spi_private[dev];

	if(down_interruptible(&spi->sem_open)){
	  return -ERESTARTSYS;
	}

	/* device is busy */
	if (spi->opened != 0) {
#ifdef SPI_DEBUG
		printk(KERN_INFO "%s(): device is busy\n", __FUNCTION__);
#endif
		ret = -EBUSY;
		goto err;
	}

	if(rw_mode == SPI_READ_MODE){
	  spi->opened = FMODE_READ;
	  spi->k_flag = SPI_READ_MODE;
	}
	else{
	  spi->opened = FMODE_WRITE;
	  spi->k_flag = SPI_WRITE_MODE;
	}

	memset(&spi->trans->buf, 0x00, sizeof(spi_buf_t));
	spi->trans->dma_regs = NULL;

	/* initialize data */
	spi_init_data(spi);

	/* sleep disable */
	spi_sleep_flag++;

	/* reset off */
	spi_reset_ctrl(spi, SPI_OFF);

	/* request SPI interrupt */
	ret =
	    request_irq(spi->trans->int_spi, spi_interrupt, SA_INTERRUPT,
			SPI_NAME, (void *)spi);
	if (ret < 0) {
#ifdef SPI_DEBUG
		printk(KERN_INFO "%s(): request irq error\n", __FUNCTION__);
#endif
		goto err1;
	}

	/* set mode */
	spi_clock_source = spi_get_clock_source();
#ifdef CONFIG_MP201
	if(dev == SPI_DEV_SPI1){
		outl(inl(SMU_PCTL_SPI) & ~SPI_SMU_PCTL_SPI1, SMU_PCTL_SPI);
	}
#endif
	ret = spi_set_mode(spi, spi->mode);
	if (ret < 0) {
		goto err2;
	}

	init_waitqueue_head(&spi->trans->wait);

	up(&spi->sem_open);

	return 0;

      err2:
	spi_free_buffer(spi);
	free_irq(spi->trans->int_spi, (void *)spi);
      err1:
	spi_reset_ctrl(spi, SPI_ON);
	spi_sleep_flag--;
	spi->opened = 0;
	spi->k_flag = 0;
      err:
	up(&spi->sem_open);

	return ret;
}
#endif
/*
 * release
 */
static int spi_release(unsigned char dev, struct inode *inode,
		       struct file *file)
{
	spi_data_t *spi;

	/* device is not found */
	if (dev > SPI_DEV_MAX) {
		return -ENODEV;
	}

	spi = &spi_private[dev];

	if(down_interruptible(&spi->sem_open)){
	  return -ERESTARTSYS;
	}

	if (file->f_mode & FMODE_READ) {
#if 0
		if (spi->trans->state & SPI_ACTIVE) {
#else
/* comment: 代入のみのため、変数そのままの判定に変更 */
		if (spi->trans->state == SPI_ACTIVE) {
#endif
			/* RX stop */
			spi_rx_stop(spi);
		}
	}
	if (file->f_mode & FMODE_WRITE) {
#if 0
		if (spi->trans->state & SPI_ACTIVE) {
#else
/* comment: 代入のみのため、変数そのままの判定に変更 */
		if (spi->trans->state == SPI_ACTIVE) {
#endif
			/* sync of TX buffer */
			DECLARE_WAITQUEUE(wait, current);
			add_wait_queue(&spi->trans->wait, &wait);
#if 0
			while (spi->trans->state & SPI_ACTIVE) {
#else
/* comment: 代入のみのため、変数そのままの判定に変更 */
			while (spi->trans->state == SPI_ACTIVE) {
#endif
				set_current_state(TASK_INTERRUPTIBLE);
				if (signal_pending(current) != 0) {
					spi_tx_stop(spi);
					break;
				}
				schedule();
			}
			remove_wait_queue(&spi->trans->wait, &wait);
			set_current_state(TASK_RUNNING);
		}
	}

	spi_free_dma(spi);

	spi_free_buffer(spi);

	free_irq(spi->trans->int_spi, (void *)spi);

	/* reset on */
	spi_reset_ctrl(spi, SPI_ON);

	/* sleep enable */
	spi_sleep_flag--;

	/* initialize data */
	spi_init_data(spi);

	spi->opened = 0;

	up(&spi->sem_open);

	return 0;
}
#ifdef CONFIG_MP201
/*
 * same release
 */
int 
mp201_spi_end(unsigned char dev)
{
	spi_data_t *spi;

	/* device is not found */
	if(dev != SPI_DEV_SPI0 && dev != SPI_DEV_SPI1){
	  return -EACCES;
	}
	spi = &spi_private[dev];

#if 0 /* 0521 */
	if(spi->k_flag == 0){
#else
 	if(spi->k_flag != SPI_READ_MODE && spi->k_flag != SPI_WRITE_MODE) {
#endif
	  return -EACCES;
	}

	if(down_interruptible(&spi->sem_open)){
	  return -ERESTARTSYS;
	}

	if (spi->opened & FMODE_READ) {
#if 0
		if (spi->trans->state & SPI_ACTIVE) {
#else
/* comment: 代入のみのため、変数そのままの判定に変更 */
		if (spi->trans->state == SPI_ACTIVE) {
#endif
			/* RX stop */
			spi_rx_stop(spi);
		}
	}
	if (spi->opened & FMODE_WRITE) {
#if 0
		if (spi->trans->state & SPI_ACTIVE) {
#else
/* comment: 代入のみのため、変数そのままの判定に変更 */
		if (spi->trans->state == SPI_ACTIVE) {
#endif
			/* sync of TX buffer */
			DECLARE_WAITQUEUE(wait, current);
			add_wait_queue(&spi->trans->wait, &wait);
#if 0
			while (spi->trans->state & SPI_ACTIVE) {
#else
/* comment: 代入のみのため、変数そのままの判定に変更 */
			while (spi->trans->state == SPI_ACTIVE) {
#endif
				set_current_state(TASK_INTERRUPTIBLE);
				if (signal_pending(current) != 0) {
					spi_tx_stop(spi);
					break;
				}
				schedule();
			}
			remove_wait_queue(&spi->trans->wait, &wait);
			set_current_state(TASK_RUNNING);
		}
	}

	spi_free_dma(spi);

	spi_free_buffer(spi);

	free_irq(spi->trans->int_spi, (void *)spi);

	/* reset on */
	spi_reset_ctrl(spi, SPI_ON);

	/* sleep enable */
	spi_sleep_flag--;

	/* initialize data */
	spi_init_data(spi);

	spi->opened = 0;
	spi->k_flag = 0;

	up(&spi->sem_open);

	return 0;
}
#endif

#ifdef CONFIG_MP201
int
mp201_spi_setmode(unsigned char dev, spi_mode_t *mode)
{
    spi_data_t *spi;
    int ret;

    /* device is not found */
    if(dev != SPI_DEV_SPI0 && dev != SPI_DEV_SPI1){
      return -EACCES;
    }

    spi = &spi_private[dev];

#if 0 /* 0521 */
    if(spi->k_flag == 0){
#else
    if(spi->k_flag != SPI_READ_MODE && spi->k_flag != SPI_WRITE_MODE) {
#endif
      return -EACCES;
    }
    ret = spi_set_mode(spi, mode);
    if(ret < 0){
      return -EFAULT;
    }

    memcpy((char *) spi->mode, (char *) mode, sizeof (spi_mode_t));

    return 0;
}
#endif
#ifdef CONFIG_MP201
/* comment: SPI0 を追加 */
/*
 * read (SPI0)
 */
static ssize_t spi_read_spi0(struct file *file, char *buf, size_t count,
			     loff_t * ppos)
{
	return spi_read(SPI_DEV_SPI0, file, buf, count, ppos);
}
#endif

/*
 * read (SPI1)
 */
static ssize_t spi_read_spi1(struct file *file, char *buf, size_t count,
			     loff_t * ppos)
{
	return spi_read(SPI_DEV_SPI1, file, buf, count, ppos);
}

#ifdef CONFIG_MP201
/* comment: SPI2 を削除 */
/* no code */
#else
/*
 * read (SPI2)
 */
static ssize_t spi_read_spi2(struct file *file, char *buf, size_t count,
			     loff_t * ppos)
{
	return spi_read(SPI_DEV_SPI2, file, buf, count, ppos);
}
#endif

#ifdef CONFIG_MP201
/* comment: SPI0 を追加 */
/*
 * write (SPI0)
 */
static ssize_t spi_write_spi0(struct file *file, const char *buf, size_t count,
			      loff_t * ppos)
{
	return spi_write(SPI_DEV_SPI0, file, buf, count, ppos);
}
#endif

/*
 * write (SPI1)
 */
static ssize_t spi_write_spi1(struct file *file, const char *buf, size_t count,
			      loff_t * ppos)
{
	return spi_write(SPI_DEV_SPI1, file, buf, count, ppos);
}

#ifdef CONFIG_MP201
/* comment: SPI2 を削除 */
/* no code */
#else
/*
 * write (SPI2)
 */
static ssize_t spi_write_spi2(struct file *file, const char *buf, size_t count,
			      loff_t * ppos)
{
	return spi_write(SPI_DEV_SPI2, file, buf, count, ppos);
}
#endif 

#ifdef CONFIG_MP201
/* comment: SPI0 を追加 */
/*
 * poll (SPI0)
 */
static unsigned int spi_poll_spi0(struct file *file, poll_table * wait)
{
	return spi_poll(SPI_DEV_SPI0, file, wait);
}
#endif

/*
 * poll (SPI1)
 */
static unsigned int spi_poll_spi1(struct file *file, poll_table * wait)
{
	return spi_poll(SPI_DEV_SPI1, file, wait);
}

#ifdef CONFIG_MP201
/* comment: SPI2 を削除 */
/* no code */
#else
/*
 * poll (SPI2)
 */
static unsigned int spi_poll_spi2(struct file *file, poll_table * wait)
{
	return spi_poll(SPI_DEV_SPI2, file, wait);
}
#endif

#ifdef CONFIG_MP201
/* comment: SPI0 を追加 */
/*
 * ioctl (SPI0)
 */
static int spi_ioctl_spi0(struct inode *inode, struct file *file,
			  unsigned int cmd, unsigned long arg)
{
	return spi_ioctl(SPI_DEV_SPI0, inode, file, cmd, arg);
}
#endif

/*
 * ioctl (SPI1)
 */
static int spi_ioctl_spi1(struct inode *inode, struct file *file,
			  unsigned int cmd, unsigned long arg)
{
	return spi_ioctl(SPI_DEV_SPI1, inode, file, cmd, arg);
}

#ifdef CONFIG_MP201
/* comment: SPI2 を削除 */
/* no code */
#else
/*
 * ioctl (SPI2)
 */
static int spi_ioctl_spi2(struct inode *inode, struct file *file,
			  unsigned int cmd, unsigned long arg)
{
	return spi_ioctl(SPI_DEV_SPI2, inode, file, cmd, arg);
}
#endif

#ifdef CONFIG_MP201
/* comment: SPI0 を追加 */
/*
 * open (SPI0)
 */
static int spi_open_spi0(struct inode *inode, struct file *file)
{
	return spi_open(SPI_DEV_SPI0, inode, file);
}
#endif

/*
 * open (SPI1)
 */
static int spi_open_spi1(struct inode *inode, struct file *file)
{
	return spi_open(SPI_DEV_SPI1, inode, file);
}

#ifdef CONFIG_MP201
/* comment: SPI2 を削除 */
/* no code */
#else
/*
 * open (SPI2)
 */
static int spi_open_spi2(struct inode *inode, struct file *file)
{
	return spi_open(SPI_DEV_SPI2, inode, file);
}
#endif

#ifdef CONFIG_MP201
/* comment: SPI0 を追加 */
/*
 * release (SPI0)
 */
static int spi_release_spi0(struct inode *inode, struct file *file)
{
	return spi_release(SPI_DEV_SPI0, inode, file);
}
#endif

/*
 * release (SPI1)
 */
static int spi_release_spi1(struct inode *inode, struct file *file)
{
	return spi_release(SPI_DEV_SPI1, inode, file);
}

#ifdef CONFIG_MP201
/* comment: SPI2 を削除 */
/* no code */
#else
/*
 * release (SPI2)
 */
static int spi_release_spi2(struct inode *inode, struct file *file)
{
	return spi_release(SPI_DEV_SPI2, inode, file);
}
#endif

#ifdef CONFIG_MP201
/* comment: SPI0 を追加 */
/* file operations info (SPI0) */
static struct file_operations spi_fops_spi0 = {
	.read = spi_read_spi0,
	.write = spi_write_spi0,
	.poll = spi_poll_spi0,
	.ioctl = spi_ioctl_spi0,
	.open = spi_open_spi0,
	.release = spi_release_spi0,
	.owner = THIS_MODULE,
};
#endif

/* file operations info (SPI1) */
static struct file_operations spi_fops_spi1 = {
	.read = spi_read_spi1,
	.write = spi_write_spi1,
	.poll = spi_poll_spi1,
	.ioctl = spi_ioctl_spi1,
	.open = spi_open_spi1,
	.release = spi_release_spi1,
	.owner = THIS_MODULE,
};

#ifdef CONFIG_MP201
/* comment: SPI2 を削除 */
/* no code */
#else
/* file operations info (SPI2) */
static struct file_operations spi_fops_spi2 = {
	.read = spi_read_spi2,
	.write = spi_write_spi2,
	.poll = spi_poll_spi2,
	.ioctl = spi_ioctl_spi2,
	.open = spi_open_spi2,
	.release = spi_release_spi2,
	.owner = THIS_MODULE,
};
#endif

/*
 * open (MP211:SPI1/SPI2 / MP201:SPI0/SPI1)
 */
static int spi_open_chrdev(struct inode *inode, struct file *file)
{
	int minor = MINOR(inode->i_rdev);

	switch (minor) {
#ifdef CONFIG_MP201
/* comment: SPI0 を追加 */
		/* SPI0 */
	case SPI_MINOR_SPI0:
		file->f_op = &spi_fops_spi0;
		break;
#endif
		/* SPI1 */
	case SPI_MINOR_SPI1:
		file->f_op = &spi_fops_spi1;
		break;

#ifdef CONFIG_MP201
/* comment: SPI2 を削除 */
/* no code */
#else
		/* SPI2 */
	case SPI_MINOR_SPI2:
		file->f_op = &spi_fops_spi2;
		break;
#endif


	default:
		/* error */
		return -ENODEV;
	}

	if (file->f_op->open != 0) {
		return (*file->f_op->open) (inode, file);
	}

	return 0;
}

static struct file_operations spi_fops_chrdev = {
	.open = spi_open_chrdev,
	.owner = THIS_MODULE,
};

/*
 * unregister of device
 */
static int spi_remove(struct device *dev)
{
	struct platform_device *pdev = to_platform_device(dev);

	class_simple_device_remove(MKDEV(spi_major, pdev->id));

	spi_devcnt--;

	if (spi_devcnt == 0) {
		class_simple_destroy(spi_class);
		unregister_chrdev(spi_major, SPI_NAME);
	}

	return 0;
}

/*
 * register of device
 */
static int spi_probe(struct device *dev)
{
	static int probe_flag = 0;
	struct platform_device *pdev = to_platform_device(dev);
	int ret = 0;

	if (probe_flag == 0) {
		probe_flag = 1;

		ret = register_chrdev(spi_major, SPI_NAME, &spi_fops_chrdev);
		if (ret < 0) {
			return -ENOENT;
		}
		if (spi_major == 0) {
			spi_major = ret;
		}
#ifdef SPI_DEBUG
		printk(KERN_INFO "%s(): spi_major = %d\n", __FUNCTION__,
		       spi_major);
#endif
		spi_class = class_simple_create(THIS_MODULE, "spi");
		if (IS_ERR(spi_class)) {
			unregister_chrdev(spi_major, SPI_NAME);
			return PTR_ERR(spi_class);
		}
	} else if (spi_devcnt == 0) {
		return -1;
	}

	class_simple_device_add(spi_class, MKDEV(spi_major, pdev->id), NULL,
				"spi%d", pdev->id);
	spi_devcnt++;

	return 0;
}

static int spi_suspend(struct device *dev, u32 state, u32 level)
{
#ifdef SPI_DEBUG
	printk(KERN_INFO "%s(): bus_id=%s state=%d level=%d\n",
	       __FUNCTION__, dev->bus_id, state, level);
#endif

	switch (level) {
	case SUSPEND_DISABLE:
		if (spi_sleep_flag != 0) {
			return -EBUSY;
		}
		break;
	default:
		break;
	}

	return 0;
}

static int spi_resume(struct device *dev, u32 level)
{
#ifdef SPI_DEBUG
	printk(KERN_INFO "%s(): bus_id=%s level=%d\n",
	       __FUNCTION__, dev->bus_id, level);
#endif

	return 0;
}

static struct device_driver spi_drv = {
	.name = SPI_NAME,
	.bus = &platform_bus_type,
	.probe = spi_probe,
	.remove = spi_remove,
	.suspend = spi_suspend,
	.resume = spi_resume,
};

/*
 * initialize
 */
static int __init spi_init(void)
{
	printk(KERN_INFO "Starting %s.\n", SPI_NAME);

	return driver_register(&spi_drv);
}

/*
 * exit
 */
static void __exit spi_exit(void)
{
	driver_unregister(&spi_drv);
}

module_init(spi_init);
module_exit(spi_exit);
MODULE_LICENSE("GPL");
#ifdef CONFIG_MP201
EXPORT_SYMBOL(mp201_spi_read);
EXPORT_SYMBOL(mp201_spi_write);
EXPORT_SYMBOL(mp201_spi_init);
EXPORT_SYMBOL(mp201_spi_end);
EXPORT_SYMBOL(mp201_spi_setmode);
#endif
