Re: about sc16is7x2 drivier

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



Hello Kathy Chang,

I will have a look at your changes. Please send your mails also to
spi-devel-general@xxxxxxxxxxxxxxxxxxxxx and
spi-devel-general@xxxxxxxxxxxxxxxxxxxxx (simply press reply-to-all).

Also remember that you should not simply remove existing copyright
notices when you modify driver!

Regards,
Manuel Stahl

hello ,Manuel Stahl:

I deleted the part of the IO driver.And I compile the driver from the kernel independent.

Please refer to the attached source code.

kathy chang

2010.12.20
	

======= 2010-12-20 10:09:00 æåæääåéï=======

>Hello Kathy Chang,
>
>thank you for your effort. Can you provide a patch for the sc16is7x2.c file?
>
>Regards,
>Manuel Stahl
>
>On 20.12.2010 10:01, changgx wrote:
>> hello,Manuel Stahl:
>> >> Sc16is7x2 driver has been successfully debugged. >> >> In sc16is7x2.cïthe control to the var .cs_change does not match with at91sam9260 driver. >> >> 1ã
>> In function sc16is7x2_startup(),the spi chip select maintains low level after the setence cmds = sc16is7x2_alloc_spi_cmds(8).
>> It caused a error about next SPI operation.
>> >> Improved method is to use 8 sc16is7x2_write_async() function to set sc16is7x2 reg. >> >> 2ã >> >> In sc16is7x2_msg_add_fifo_tx() functionï you need to take anti-cs_change variables. >> >> >> good luck!!! >> >> >> ======= 2010-12-14 11:52:00 æåæääåéï======= >>
>>> Hello again,
>>>
>>> The commands in sc16is7x2_spi_async are sent async (as the name says),
>>> so the first time you execute sc16is7x2_read(ts->spi, UART_IER,ch) the
>>> async part was not yet completed, the second time it was completed and
>>> has set UART_IER to 0.
>>>
>>> Regards,
>>> Manuel
>>>
>>> On 13.12.2010 06:43, changgx wrote:
>>>>      helloïmanuel.stahlï
>>>>
>>>>      excuse me!!
>>>>
>>>>      I found the sc16is7x2.c driver provided by you on the internet the
>>>>      other day. And I explante it to my board.
>>>>
>>>>      My develop environment is about ïCPU for AT91SAM9260 and kennel for
>>>>      linux-2.6.27.
>>>>
>>>>      But I found some questions in debugging.
>>>>
>>>>      In the function of sc16is7x2_startup( ), i add the sentence to read
>>>>      register value after sc16is7x2_spi_async(ts->spi, cmds, 8) sentence
>>>>      and i find that the return value is always 0xff.
>>>>
>>>>      for example:
>>>>
>>>>      int uier1,uier2;
>>>>
>>>>      uier1 = sc16is7x2_read(ts->spi, UART_IER,ch);
>>>>      SC16_PRINTK("+++++++++++++++++++++>  s ïd UART_IER = x
>>>>      \n",__func__,__LINE__,uier1);
>>>>      uier2 = sc16is7x2_read(ts->spi, UART_IER,ch);
>>>>      SC16_PRINTK("+++++++++++++++++++++>  s ïd UART_IER = x
>>>>      \n",__func__,__LINE__,uier2);
>>>>
>>>>      the print result:
>>>>
>>>>      +++++++++++++++++++++>  sc16is7x2_startup : 512 UART_IER = ff
>>>>      +++++++++++++++++++++>  sc16is7x2_startup : 515 UART_IER = 0
>>>>
>>>>      The results is that the first time is 0xff, the secend is 0.
>>>>
>>>>      And it is same to read other register.
>>>>
>>>>      please give me some advice for this question.
>>>>
>>>>      ps: thank you very much for your open souce and look forward to your
>>>>      email.
>>>>
>>>>      thanksï
>>>>
>>>>      kathy chang
>>>>
>>>>      2010-12-13
>>>>
>>>>      ------------------------------------------------
>>>>
>>>>      Emailïchanggx@xxxxxxx<mailto:changgx@xxxxxxx>
>>>>
>>>>      ------------------------------------------------
>>>>
>>>>
>>>>
>>>> = = = = = = = = = = = = = = = = = = = =
>>>> ããããããããè
>>>> çï
>>>> changgx
>>>> changgx@xxxxxxx<mailto:changgx@xxxxxxx>
>>>> 2010-12-13
>>>> ãããããããããããããã
>>>
>>>
>>> -- >>> Manuel Stahl
>>> Fraunhofer-Institut IIS
>>> Leistungsoptimierte Systeme
>>>
>>> Nordostpark 93
>>> D90411 NÃrnberg
>>> Telefon  +49 (0)911/58061-6419
>>> Fax      +49 (0)911/58061-6398
>>> E-Mail   manuel.stahl@xxxxxxxxxxxxxxxxx
>>>
>>> http://www.iis.fraunhofer.de
>>> http://www.smart-power.fraunhofer.de
>>>
>>>
>>>
>>> __________ Information from ESET Smart Security, version of virus signature database 5563 (20101026) __________
>>>
>>> The message was checked by ESET Smart Security.
>>>
>>> http://www.eset.com
>> >> = = = = = = = = = = = = = = = = = = = =
>> 			
>> >> ããããããããè
>> çï
>> >>
>> ããããããããchanggx
>> ããããããããchanggx@xxxxxxx
>> ãããããããããã2010-12-20
>> >> >> >>
>
>
>-- >Manuel Stahl
>Fraunhofer-Institut IIS
>Leistungsoptimierte Systeme
>
>Nordostpark 93
>D90411 NÃrnberg
>Telefon  +49 (0)911/58061-6419
>Fax      +49 (0)911/58061-6398
>E-Mail   manuel.stahl@xxxxxxxxxxxxxxxxx
>
>http://www.iis.fraunhofer.de
>http://www.smart-power.fraunhofer.de
>
>
>
>__________ Information from ESET Smart Security, version of virus signature database 5563 (20101026) __________
>
>The message was checked by ESET Smart Security.
>
>http://www.eset.com
= = = = = = = = = = = = = = = = = = = =
			

ããããããããè
çï
ããããããããchanggx
ããããããããchanggx@xxxxxxx
ãããããããããã2010-12-20


sa16is7x2.h

NÂnârÂÂÃÃ)emÃhÃyhiÃÂÂw^âÂÃ


sc16is7x2.c

NÂnârÂÂÃÃ)emÃhÃyhiÃÂÂw^âÂÃ


Attachment: Makefile
Description: Binary data

#ifndef LINUX_SPI_SC16IS752_H
#define LINUX_SPI_SC16IS752_H

struct sc16is7x2_platform_data {
	unsigned int	uartclk;
	/* uart line number of the first channel */
	unsigned	uart_base;
};

#endif
/**
 * drivers/serial/sc16is7x2.c
 *
 * Copyright (C) 2010 kathy chang changgx@xxxxxxx
 *
 * 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.
 *
 * The SC16IS7x2 device is a SPI driven dual UART with GPIOs.
 *
 * The driver exports two uarts .
 */

#define DEBUG 

#include <linux/irq.h>
#include <linux/init.h>
#include <linux/platform_device.h>
#include <linux/mutex.h>
#include <linux/spi/spi.h>
#include <linux/freezer.h>
#include <linux/serial_core.h>
#include <linux/serial_reg.h>
#include <linux/gpio.h>
#include <linux/serial.h>

#include "sc16is7x2.h"

#define H_SPI_SPEED       (2*1000*1000)

#define SC16IS7X2_MAJOR		204
#define SC16IS7X2_MINOR		209
#define MAX_SC16IS7X2			2
#define FIFO_SIZE					64

#define PMC_PCER   0x10

#define DRIVER_NAME				"sc16is7x2"
#define TYPE_NAME					"SC16IS7x2"

#define REG_READ	0x80
#define REG_WRITE	0x00

/* Special registers */
#define REG_TXLVL	0x08	/* Transmitter FIFO Level register */
#define REG_RXLVL	0x09	/* Receiver FIFO Level register */
#define REG_IOD		0x0A	/* IO Direction register */
#define REG_IOS		0x0B	/* IO State register */
#define REG_IOI		0x0C	/* IO Interrupt Enable register */
#define REG_IOC		0x0E	/* IO Control register */

#define IOC_SRESET	0x08    /* Software reset */
#define IOC_GPIO30	0x04    /* GPIO 3:0 unset: as IO, set: as modem pins */
#define IOC_GPIO74	0x02    /* GPIO 7:4 unset: as IO, set: as modem pins */

#define IOC_IOLATCH 0x01 /* Unset: input unlatched, set: input latched */ 

/* Redefine some MCR bits */
#ifdef UART_MCR_TCRTLR
#undef UART_MCR_TCRTLR
#endif
#define UART_MCR_TCRTLR		0x04
#define UART_MCR_IRDA		0x40


#define WRITE_CMD(reg, ch) (REG_WRITE | (reg & 0xf) << 3 | (ch & 0x1) << 1)
#define READ_CMD(reg, ch)  (REG_READ  | (reg & 0xf) << 3 | (ch & 0x1) << 1)

static struct sc16is7x2_platform_data sc16is7x2_spi_data = {
	  .uartclk = 18432000,
	  .uart_base = 0,
};

/* 16bit SPI command to read or write a register */
struct sc16is7x2_spi_reg {
	u8 cmd;
	u8 value;
} __attribute__ ((packed));

struct sc16is7x2_chip;

/*
 * Some registers must be read back to modify.
 * To save time we cache them here in memory
 */
struct sc16is7x2_channel {
	struct sc16is7x2_chip	*chip;	/* back link */
	struct mutex		lock;
	struct uart_port 	uart;
	struct spi_transfer fifo_rx;
	struct spi_transfer fifo_tx[3];
	u8		iir;
	u8		lsr;
	u8		msr;
	u8		ier;		/* cache for IER register */
	u8		fcr;		/* cache for FCR register */
	u8		lcr;		/* cache for LCR register */
	u8		mcr;		/* cache for MCR register */
	u8		rxbuf[FIFO_SIZE+1];
	u8		write_fifo_cmd;
	u8		read_fifo_cmd;
	bool	active;
};

struct sc16is7x2_chip {
	struct spi_device *spi;

	struct mutex	 lock;
	struct sc16is7x2_channel channel[2];

	/* for handling irqs: need workqueue since we do spi_sync */
	struct workqueue_struct *workqueue;
	struct work_struct work;
	/* set to 1 to make the workhandler exit as soon as possible */
	int force_end_work;
	/* need to know we are suspending to avoid deadlock on workqueue */
	int suspending;

	struct spi_message fifo_message;

#define UART_BUG_TXEN	BIT(1)	/* UART has buggy TX IIR status */
#define UART_BUG_NOMSR	BIT(2)	/* UART has buggy MSR status bits (Au1x00) */
#define UART_BUG_THRE	BIT(3)	/* UART has buggy THRE reassertion */
	u16		bugs;		/* port bugs */

#define LSR_SAVE_FLAGS UART_LSR_BRK_ERROR_BITS
	u8		lsr_saved_flags;
#define MSR_SAVE_FLAGS UART_MSR_ANY_DELTA
	u8		msr_saved_flags;	
};


/* ******************************** SPI ********************************* */ 


/*
 * Reserve memory for command sequence
 * @param cnt number of commands
 */
static inline struct sc16is7x2_spi_reg *
sc16is7x2_alloc_spi_cmds(unsigned cnt)
{
	return kzalloc(sizeof(struct sc16is7x2_spi_reg)*cnt, GFP_KERNEL);
}

/*
 * sc16is7x2_add_write_cmd - Add write command to sequence
 */
static inline void sc16is7x2_add_write_cmd(struct sc16is7x2_spi_reg *cmd,
		u8 reg, u8 ch, u8 value)
{
	cmd->cmd = WRITE_CMD(reg, ch);
	cmd->value = value;
}

/*
 * sc16is7x2_add_read_cmd - Add read command to sequence
 */
static inline void sc16is7x2_add_read_cmd(struct sc16is7x2_spi_reg *cmd,
		u8 reg, u8 ch)
{
	cmd->cmd = READ_CMD(reg, ch);
	cmd->value = 0;
}

/*
 * sc16is7x2_complete - Completion handler for async SPI transfers
 */
static void sc16is7x2_complete(void *context)
{
	struct spi_message *m = context;
	u8 *tx_chain = m->state;
   
	kfree(tx_chain);
	kfree(m);
}

/*
 * sc16is7x2_spi_async - Send command sequence
 */
static int sc16is7x2_spi_async(struct spi_device *spi,
		struct sc16is7x2_spi_reg *cmds, unsigned len)
{
	
	struct spi_transfer *t;
	struct spi_message *m;

	m = spi_message_alloc(len, GFP_KERNEL);
	if (!m)
		return -ENOMEM;

	m->complete = sc16is7x2_complete;
	m->context = m;
	m->state = cmds;
	list_for_each_entry(t, &m->transfers, transfer_list) {
		t->tx_buf = (u8 *)cmds;
		t->len = 2;
		t->cs_change = false;
		//t->cs_change = true;
		cmds++;
	}
	
	return spi_async(spi, m);
}

/*
 * sc16is7x2_write_async - Write a new register content (async)
 */

static inline int sc16is7x2_write_async(struct spi_device *spi, u8 reg, u8 ch, 
		u8 value)
{
	struct sc16is7x2_spi_reg *cmd = sc16is7x2_alloc_spi_cmds(1);
	if (!cmd)
		return -ENOMEM;
	sc16is7x2_add_write_cmd(cmd, reg, ch, value);
	return sc16is7x2_spi_async(spi, cmd, 1);
}

/*
 * sc16is7x2_write - Write a new register content (sync)
 */
static int sc16is7x2_write(struct spi_device *spi, u8 reg, u8 ch, u8 val)
{
	u16 word = REG_WRITE | (reg & 0xf) << 3 | (ch & 0x3) << 1 | val << 8;
	return spi_write(spi, (const u8 *)&word, sizeof(word));
}

/**
 * sc16is7x2_read - Read back register content
 * @spi: The SPI device
 * @reg: Register offset
 *
 * Returns positive 8 bit value from device if successful or a
 * negative value on error
 */

static int sc16is7x2_read(struct spi_device *spi, unsigned reg, unsigned ch) 
{
	u8 cmd = REG_READ | (reg & 0xf) << 3 | (ch & 0x3) << 1;
	return spi_w8r8(spi, cmd);
}


/* ******************************** UART ********************************* */ 

/* Uart divisor latch write */

static inline void sc16is7x2_add_dl_write_cmd(struct sc16is7x2_spi_reg *cmd, 
		u8 ch, int value)
{
	sc16is7x2_add_write_cmd(&cmd[0], UART_DLL, ch, value & 0xff);
	sc16is7x2_add_write_cmd(&cmd[1], UART_DLM, ch, (value >> 8) & 0xff);	
}

static unsigned int sc16is7x2_tx_empty(struct uart_port *port)
{
	struct sc16is7x2_channel *chan =
			container_of(port, struct sc16is7x2_channel, uart);
	struct sc16is7x2_chip *ts = chan->chip;
	unsigned lsr;
  printk(KERN_DEBUG "---------> function call %s\n", __func__);
	dev_dbg(&ts->spi->dev, "%s\n", __func__);

	mutex_lock(&chan->lock);
	lsr = chan->lsr;
	mutex_unlock(&chan->lock);

	return lsr & UART_LSR_TEMT ? TIOCSER_TEMT : 0;
}

static unsigned int sc16is7x2_get_mctrl(struct uart_port *port)
{
	struct sc16is7x2_channel *chan =
			container_of(port, struct sc16is7x2_channel, uart);
	struct sc16is7x2_chip *ts = chan->chip;
	unsigned int status;
	unsigned int ret;

	dev_dbg(&ts->spi->dev, "%s\n", __func__);
  printk(KERN_DEBUG "---------> function call %s\n", __func__);
	status = chan->msr;

	ret = 0;
	if (status & UART_MSR_DCD)
		ret |= TIOCM_CAR;
	if (status & UART_MSR_RI)
		ret |= TIOCM_RNG;
	if (status & UART_MSR_DSR)
		ret |= TIOCM_DSR;
	if (status & UART_MSR_CTS)
		ret |= TIOCM_CTS;
	return ret;
}

static unsigned int __set_mctrl(unsigned int mctrl)
{
	unsigned char mcr = 0;

	if (mctrl & TIOCM_RTS)
		mcr |= UART_MCR_RTS;
	if (mctrl & TIOCM_DTR)
		mcr |= UART_MCR_DTR;
	if (mctrl & TIOCM_LOOP)
		mcr |= UART_MCR_LOOP;

	return mcr;
}

static void sc16is7x2_set_mctrl(struct uart_port *port, unsigned int mctrl)
{
	struct sc16is7x2_channel *chan =
			container_of(port, struct sc16is7x2_channel, uart);
	struct sc16is7x2_chip *ts = chan->chip;
	unsigned ch = port->line & 0x01;
  
  printk(KERN_DEBUG "---------> function call %s\n", __func__);
   
	dev_dbg(&ts->spi->dev, "%s\n", __func__);

  sc16is7x2_write_async(ts->spi, UART_MCR, ch, __set_mctrl(mctrl));
 
}

static inline void __stop_tx(struct sc16is7x2_channel *chan)
{
	struct sc16is7x2_chip *ts = chan->chip;
	unsigned ch = chan->uart.line & 0x01;
	if (chan->ier & UART_IER_THRI) {
		chan->ier &= ~UART_IER_THRI;
		sc16is7x2_write_async(ts->spi, UART_IER, ch, chan->ier);  
	}
}

static void sc16is7x2_stop_tx(struct uart_port *port)
{
	struct sc16is7x2_channel *chan =
			container_of(port, struct sc16is7x2_channel, uart);
	struct sc16is7x2_chip *ts = chan->chip;
  
  printk(KERN_DEBUG "---------> function call %s\n", __func__);
	dev_dbg(&ts->spi->dev, "%s\n", __func__);
	__stop_tx(chan);
}

/*
open
*/

static void sc16is7x2_start_tx(struct uart_port *port)
{
	struct sc16is7x2_channel *chan =
			container_of(port, struct sc16is7x2_channel, uart);
	struct sc16is7x2_chip *ts = chan->chip;
	unsigned ch = port->line & 0x01;
	
  printk(KERN_DEBUG "---------> function call %s\n", __func__);
	dev_dbg(&ts->spi->dev, "%s\n", __func__);

	if (!(chan->ier & UART_IER_THRI)) {
		chan->ier |= UART_IER_THRI;
   sc16is7x2_write_async(ts->spi, UART_IER, ch, chan->ier); 
	}
}

static void sc16is7x2_stop_rx(struct uart_port *port)
{
	struct sc16is7x2_channel *chan =
			container_of(port, struct sc16is7x2_channel, uart);
	struct sc16is7x2_chip *ts = chan->chip;
	unsigned ch = port->line & 0x01;
  
  printk(KERN_DEBUG "---------> function call %s\n", __func__);
	dev_dbg(&ts->spi->dev, "%s\n", __func__);

	chan->ier &= ~UART_IER_RLSI;
	chan->uart.read_status_mask &= ~UART_LSR_DR;
	sc16is7x2_write_async(ts->spi, UART_IER, ch, chan->ier);
}

static void sc16is7x2_enable_ms(struct uart_port *port)
{
	struct sc16is7x2_channel *chan =
			container_of(port, struct sc16is7x2_channel, uart);
	struct sc16is7x2_chip *ts = chan->chip;
	unsigned ch = port->line & 0x01;

	dev_dbg(&ts->spi->dev, "%s\n", __func__);
  printk(KERN_DEBUG "---------> function call %s\n", __func__);
	chan->ier |= UART_IER_MSI;
  sc16is7x2_write_async(ts->spi, UART_IER, ch, chan->ier);
}

static void sc16is7x2_break_ctl(struct uart_port *port, int break_state)
{
	struct sc16is7x2_channel *chan =
			container_of(port, struct sc16is7x2_channel, uart);
	struct sc16is7x2_chip *ts = chan->chip;
	unsigned ch = port->line & 0x01;
	unsigned long flags;
 
	dev_dbg(&ts->spi->dev, "%s\n", __func__);
  printk(KERN_DEBUG "---------> function call %s\n", __func__);
	spin_lock_irqsave(&chan->uart.lock, flags);
	if (break_state == -1)
		chan->lcr |= UART_LCR_SBC;
	else
		chan->lcr &= ~UART_LCR_SBC;
	spin_unlock_irqrestore(&chan->uart.lock, flags);

	sc16is7x2_write_async(ts->spi, UART_LCR, ch, chan->lcr);
}

static int sc16is7x2_startup(struct uart_port *port)
{
	struct sc16is7x2_channel *chan =
			container_of(port, struct sc16is7x2_channel, uart);
	struct sc16is7x2_chip *ts = chan->chip;
	unsigned ch = port->line & 0x01;
	unsigned long flags;
	
	dev_dbg(&ts->spi->dev, "%s (line %d)\n", __func__, port->line);	
	spin_lock_irqsave(&chan->uart.lock, flags);
	chan->lcr = UART_LCR_WLEN8;
	chan->mcr = __set_mctrl(chan->uart.mctrl);
	chan->fcr = 0;
	chan->ier = UART_IER_RLSI | UART_IER_RDI;
	spin_unlock_irqrestore(&chan->uart.lock, flags);

	{
	  u8 tmp;
	  sc16is7x2_write_async(ts->spi, UART_IER, ch,0);
	  tmp = sc16is7x2_read(ts->spi, UART_IIR, ch);
    tmp = sc16is7x2_read(ts->spi, UART_LSR, ch);
    tmp = sc16is7x2_read(ts->spi, UART_MSR, ch);
	  sc16is7x2_write_async(ts->spi, UART_FCR, ch,UART_FCR_ENABLE_FIFO | UART_FCR_CLEAR_RCVR | UART_FCR_CLEAR_XMIT);
	  sc16is7x2_write_async(ts->spi, UART_FCR, ch,chan->fcr);                                                       
	  sc16is7x2_write_async(ts->spi, UART_LCR, ch,chan->lcr);                                                       
	  sc16is7x2_write_async(ts->spi, UART_MCR, ch,chan->mcr);                                                      
	  
	}
	chan->active = true;
	return 0;
}

static void sc16is7x2_shutdown(struct uart_port *port)
{
	struct sc16is7x2_channel *chan =
			container_of(port, struct sc16is7x2_channel, uart);
	struct sc16is7x2_chip *ts = chan->chip;
	unsigned long flags;
	unsigned ch = port->line & 0x01;

	dev_dbg(&ts->spi->dev, "%s\n", __func__);

	BUG_ON(!chan);
	BUG_ON(!ts);

	if (ts->suspending)
		return;

	/* Disable interrupts from this port */
	chan->ier = 0;
	chan->active = false;
	sc16is7x2_write(ts->spi, UART_IER, ch, chan->ier);

	/* Wait for worker of this channel to finish */
	mutex_lock(&chan->lock);

	spin_lock_irqsave(&chan->uart.lock, flags);
	chan->mcr = __set_mctrl(chan->uart.mctrl);
	spin_unlock_irqrestore(&chan->uart.lock, flags);

	/* Disable break condition and FIFOs */
	chan->lcr &= ~UART_LCR_SBC;

	sc16is7x2_write(ts->spi, UART_MCR, ch, chan->mcr);
	sc16is7x2_write(ts->spi, UART_LCR, ch, chan->lcr);

	mutex_unlock(&chan->lock);
}

static void
sc16is7x2_set_termios(struct uart_port *port, struct ktermios *termios,
		       struct ktermios *old)
{
	struct sc16is7x2_channel *chan =
			container_of(port, struct sc16is7x2_channel, uart);
	struct sc16is7x2_chip *ts = chan->chip;
	unsigned ch = port->line & 0x01;
	unsigned long flags;
	unsigned int baud, quot;
	u8 ier, mcr, lcr, fcr = 0;
	u8 efr = UART_EFR_ECB;
	
  printk(KERN_DEBUG "---------> function call %s\n", __func__);
  
	/* set word length */
	switch (termios->c_cflag & CSIZE) {
	case CS5:
		lcr = UART_LCR_WLEN5;
		break;
	case CS6:
		lcr = UART_LCR_WLEN6;
		break;
	case CS7:
		lcr = UART_LCR_WLEN7;
		break;
	default:
	case CS8:
		lcr = UART_LCR_WLEN8;
		break;
	}

	if (termios->c_cflag & CSTOPB)
		lcr |= UART_LCR_STOP;
	if (termios->c_cflag & PARENB)
		lcr |= UART_LCR_PARITY;
	if (!(termios->c_cflag & PARODD))
		lcr |= UART_LCR_EPAR;
#ifdef CMSPAR
	if (termios->c_cflag & CMSPAR)
		lcr |= UART_LCR_SPAR;
#endif
   
	/* Ask the core to calculate the divisor for us. */
	baud = uart_get_baud_rate(port, termios, old,
				  port->uartclk / 16 / 0xffff,
				  port->uartclk / 16);  
	quot = uart_get_divisor(port, baud);

	dev_dbg(&ts->spi->dev, "%s (baud %u)\n", __func__, baud);


	/* configure the fifo */
	if (baud < 2400)
		fcr = UART_FCR_ENABLE_FIFO | UART_FCR_R_TRIG_00;
	else
		fcr = UART_FCR_ENABLE_FIFO | UART_FCR_R_TRIG_01;
		
	/*
	 * MCR-based auto flow control.  When AFE is enabled, RTS will be
	 * deasserted when the receive FIFO contains more characters than
	 * the trigger, or the MCR RTS bit is cleared.  In the case where
	 * the remote UART is not using CTS auto flow control, we must
	 * have sufficient FIFO entries for the latency of the remote
	 * UART to respond.  IOW, at least 32 bytes of FIFO.
	 */
	chan->mcr &= ~UART_MCR_AFE;
	if (termios->c_cflag & CRTSCTS)
		chan->mcr |= UART_MCR_AFE;

	/*
	 * Ok, we're now changing the port state.  Do it with
	 * interrupts disabled.
	 */
	spin_lock_irqsave(&chan->uart.lock, flags);

	/* we are sending char from a workqueue so enable */
//	chan->uart.state->port.tty->low_latency = 1;
   chan->uart.info->port.tty->low_latency = 1;
	/* Update the per-port timeout. */
	uart_update_timeout(port, termios->c_cflag, baud);

	chan->uart.read_status_mask = UART_LSR_OE | UART_LSR_THRE | UART_LSR_DR;
	if (termios->c_iflag & INPCK)
		chan->uart.read_status_mask |= UART_LSR_FE | UART_LSR_PE;
	if (termios->c_iflag & (BRKINT | PARMRK))
		chan->uart.read_status_mask |= UART_LSR_BI;

	/* Characters to ignore */
	chan->uart.ignore_status_mask = 0;
	if (termios->c_iflag & IGNPAR)
		chan->uart.ignore_status_mask |= UART_LSR_PE | UART_LSR_FE;
	if (termios->c_iflag & IGNBRK) {
		chan->uart.ignore_status_mask |= UART_LSR_BI;
		/*
		 * If we're ignoring parity and break indicators,
		 * ignore overruns too (for real raw support).
		 */
		if (termios->c_iflag & IGNPAR)
			chan->uart.ignore_status_mask |= UART_LSR_OE;
	}

	/* ignore all characters if CREAD is not set */
	if ((termios->c_cflag & CREAD) == 0)
		chan->uart.ignore_status_mask |= UART_LSR_DR;

	/* CTS flow control flag and modem status interrupts */
	chan->ier &= ~UART_IER_MSI;
	if (UART_ENABLE_MS(&chan->uart, termios->c_cflag))
		chan->ier |= UART_IER_MSI;

	if (termios->c_cflag & CRTSCTS)
		efr |= UART_EFR_CTS | UART_EFR_RTS;

	mcr = __set_mctrl(chan->uart.mctrl);
	ier = chan->ier;
	chan->lcr = lcr;				/* Save LCR */
	chan->fcr = fcr;				/* Save FCR */
	chan->mcr = mcr;				/* Save MCR */

	fcr |= UART_FCR_CLEAR_RCVR | UART_FCR_CLEAR_XMIT;

	spin_unlock_irqrestore(&chan->uart.lock, flags);
  
  sc16is7x2_write_async(ts->spi, UART_LCR, ch,UART_LCR_DLAB);
  printk(KERN_DEBUG "+++++++++++++++++++> %s £º%d after write to read UART_LCR = %x \n",__func__,__LINE__,sc16is7x2_read(ts->spi, UART_LCR, ch));
  sc16is7x2_write_async(ts->spi, UART_DLL, ch,quot & 0xff);       
  sc16is7x2_write_async(ts->spi, UART_DLM, ch,(quot >> 8) & 0xff);
  sc16is7x2_write_async(ts->spi, UART_LCR, ch,0xBF);              
  sc16is7x2_write_async(ts->spi, UART_EFR, ch, efr);              
  sc16is7x2_write_async(ts->spi, UART_LCR, ch, lcr);              
  sc16is7x2_write_async(ts->spi, UART_FCR, ch, fcr);              
  sc16is7x2_write_async(ts->spi, UART_MCR, ch, mcr);              
  sc16is7x2_write_async(ts->spi, UART_IER, ch, ier); 
  
  #if 0 //for test
  {
    printk(KERN_DEBUG "+++++++++++++++++++> %s £º%d termios UART_LCR = %x lcr = %x\n",__func__,__LINE__,sc16is7x2_read(ts->spi, UART_LCR, ch),lcr);
    printk(KERN_DEBUG "+++++++++++++++++++> %s £º%d termios UART_LCR = %x lcr = %x\n",__func__,__LINE__,sc16is7x2_read(ts->spi, UART_LCR, ch),lcr);
    sc16is7x2_write(ts->spi, UART_LCR, ch,UART_LCR_DLAB);	  
	  printk(KERN_DEBUG "+++++++++++++++++++++> %s £º%d UART_LCR = %x  \n",__func__,__LINE__,sc16is7x2_read(ts->spi, UART_LCR,ch));
    printk(KERN_DEBUG "+++++++++++++++++++++> %s £º%d UART_DLL = %x  \n",__func__,__LINE__,sc16is7x2_read(ts->spi, UART_DLL,ch));
    printk(KERN_DEBUG "+++++++++++++++++++++> %s £º%d UART_DLM = %x  \n",__func__,__LINE__,sc16is7x2_read(ts->spi, UART_DLM,ch));
    
    printk(KERN_DEBUG "+++++++++++++++++++++> %s £º%d UART_MCR = %x  \n",__func__,__LINE__,sc16is7x2_read(ts->spi, UART_MCR,ch));
	  printk(KERN_DEBUG "+++++++++++++++++++++> %s £º%d UART_IER = %x  \n",__func__,__LINE__,sc16is7x2_read(ts->spi, UART_IER,ch));
    printk(KERN_DEBUG "+++++++++++++++++++++> %s £º%d UART_IIR = %x  \n",__func__,__LINE__,sc16is7x2_read(ts->spi, UART_IIR,ch));
    printk(KERN_DEBUG "+++++++++++++++++++++> %s £º%d UART_LSR = %x  \n",__func__,__LINE__,sc16is7x2_read(ts->spi, UART_LSR,ch));
    
    sc16is7x2_write(ts->spi, UART_LCR, ch,lcr);
    printk(KERN_DEBUG "+++++++++++++++++++++> %s £º%d after restore UART_LCR = %x  \n",__func__,__LINE__,sc16is7x2_read(ts->spi, UART_LCR,ch));
    
  }
  #endif

	/* Don't rewrite B0 */
	if (tty_termios_baud_rate(termios))
		tty_termios_encode_baud_rate(termios, baud, baud);

}

static const char *
sc16is7x2_type(struct uart_port *port)
{
	struct sc16is7x2_channel *chan =
			container_of(port, struct sc16is7x2_channel, uart);
	struct sc16is7x2_chip *ts = chan->chip;
	dev_dbg(&ts->spi->dev, "%s\n", __func__);
	return TYPE_NAME;
}

static void sc16is7x2_release_port(struct uart_port *port)
{
	struct sc16is7x2_channel *chan =
			container_of(port, struct sc16is7x2_channel, uart);
	struct sc16is7x2_chip *ts = chan->chip;
	dev_dbg(&ts->spi->dev, "%s\n", __func__);
	ts->force_end_work = 1;
}

static int sc16is7x2_request_port(struct uart_port *port)
{
	struct sc16is7x2_channel *chan =
			container_of(port, struct sc16is7x2_channel, uart);
	struct sc16is7x2_chip *ts = chan->chip;
	dev_dbg(&ts->spi->dev, "%s\n", __func__);
	return 0;
}

static void sc16is7x2_config_port(struct uart_port *port, int flags)
{
	struct sc16is7x2_channel *chan =
			container_of(port, struct sc16is7x2_channel, uart);
	struct sc16is7x2_chip *ts = chan->chip;
	dev_dbg(&ts->spi->dev, "%s\n", __func__);
	if (flags & UART_CONFIG_TYPE)
		chan->uart.type = PORT_SC16IS7X2;
}

static int
sc16is7x2_verify_port(struct uart_port *port, struct serial_struct *ser)
{
	struct sc16is7x2_channel *chan =
			container_of(port, struct sc16is7x2_channel, uart);
	struct sc16is7x2_chip *ts = chan->chip;
	dev_dbg(&ts->spi->dev, "%s\n", __func__);
	if (ser->irq < 0 || ser->baud_base < 9600 ||
			ser->type != PORT_SC16IS7X2)
		return -EINVAL;
	return 0;
}

static struct uart_ops sc16is7x2_uart_ops = {
	.tx_empty	= sc16is7x2_tx_empty,
	.set_mctrl	= sc16is7x2_set_mctrl, //open»áµ÷ÓÃ?????
	.get_mctrl	= sc16is7x2_get_mctrl,
	.stop_tx        = sc16is7x2_stop_tx,
	.start_tx	= sc16is7x2_start_tx,
	.stop_rx	= sc16is7x2_stop_rx,
	.enable_ms      = sc16is7x2_enable_ms,
	.break_ctl      = sc16is7x2_break_ctl,
	.startup	= sc16is7x2_startup,   //open µÄÈë¿Ú
	.shutdown	= sc16is7x2_shutdown,
	.set_termios	= sc16is7x2_set_termios, //open»áµ÷Óà tcgetattr 
	.type		= sc16is7x2_type,
	.release_port   = sc16is7x2_release_port,
	.request_port   = sc16is7x2_request_port,
	.config_port	= sc16is7x2_config_port,
	.verify_port	= sc16is7x2_verify_port,
};


#define MIN(a, b) ((a < b) ? (a) : (b))


/* ******************************** IRQ ********************************* */ 

static void sc16is7x2_handle_fifo_rx(struct sc16is7x2_channel *chan)
{
	struct uart_port *uart = &chan->uart;
	//struct tty_struct *tty = uart->state->port.tty;
	struct tty_struct *tty = uart->info->port.tty;
	u8 *rxbuf = chan->rxbuf;
	u8 lsr = chan->lsr;
	unsigned i, count = chan->fifo_rx.len;
	unsigned long flags;
	char flag = TTY_NORMAL;
  
  printk(KERN_DEBUG "---------> function call %s\n", __func__);
	spin_lock_irqsave(&uart->lock, flags);

	if (unlikely(lsr & UART_LSR_BRK_ERROR_BITS)) {
		/*
		 * For statistics only
		 */
		if (lsr & UART_LSR_BI) {
			lsr &= ~(UART_LSR_FE | UART_LSR_PE);
			chan->uart.icount.brk++;
			/*
			 * We do the SysRQ and SAK checking
			 * here because otherwise the break
			 * may get masked by ignore_status_mask
			 * or read_status_mask.
			 */
			if (uart_handle_break(&chan->uart))
				goto ignore_char;
		} else if (lsr & UART_LSR_PE)
			chan->uart.icount.parity++;
		else if (lsr & UART_LSR_FE)
			chan->uart.icount.frame++;
		if (lsr & UART_LSR_OE)
			chan->uart.icount.overrun++;

		/*
		 * Mask off conditions which should be ignored.
		 */
		lsr &= chan->uart.read_status_mask;

		if (lsr & UART_LSR_BI)
			flag = TTY_BREAK;
		else if (lsr & UART_LSR_PE)
			flag = TTY_PARITY;
		else if (lsr & UART_LSR_FE)
			flag = TTY_FRAME;
	}

	for (i = 1; i < count; i++) {
		uart->icount.rx++;

		if (!uart_handle_sysrq_char(uart, rxbuf[i]))
			uart_insert_char(uart, lsr, UART_LSR_OE,
					rxbuf[i], flag);
	}

ignore_char:
	spin_unlock_irqrestore(&uart->lock, flags);

	if (count > 1)
		tty_flip_buffer_push(tty);
}

static void sc16is7x2_handle_fifo_tx(struct sc16is7x2_channel *chan)
{
	struct uart_port *uart = &chan->uart;
	//struct circ_buf *xmit = &uart->state->xmit;
	struct circ_buf *xmit = &uart->info->xmit;
	unsigned count = chan->fifo_tx[1].len + chan->fifo_tx[2].len;
	unsigned long flags;
 
	BUG_ON(!uart);
	BUG_ON(!xmit);  
  
	spin_lock_irqsave(&uart->lock, flags);

	uart->icount.tx += count;
	
	
	if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS)
		uart_write_wakeup(uart);

	if (uart_circ_empty(xmit))
	{	
		__stop_tx(chan);
  }

	spin_unlock_irqrestore(&uart->lock, flags);
}


static bool sc16is7x2_msg_add_fifo_rx(struct sc16is7x2_chip *ts, unsigned ch) 
{
	struct spi_message *m = &(ts->fifo_message);
	struct spi_transfer *t = &(ts->channel[ch].fifo_rx);
	int rxlvl = sc16is7x2_read(ts->spi, REG_RXLVL, ch);
	if (rxlvl > 0) {
		t->len = rxlvl + 1;
		spi_message_add_tail(t, m);
		return true;
	}
	return false;
}


static bool sc16is7x2_msg_add_fifo_tx(struct sc16is7x2_chip *ts, unsigned ch) 
{
	struct sc16is7x2_channel * const chan = &(ts->channel[ch]);
	struct uart_port *uart = &chan->uart;
	//struct circ_buf *xmit = &uart->state->xmit;
	struct circ_buf *xmit = &uart->info->xmit;
	unsigned count;
	bool split_transfer;
	u8 txlvl;
  printk(KERN_DEBUG "---------> function call %s\n", __func__);

	if (chan->uart.x_char && chan->lsr & UART_LSR_THRE) {
		dev_dbg(&ts->spi->dev, "tx: x-char\n");
		sc16is7x2_write(ts->spi, UART_TX, ch, uart->x_char);
		uart->icount.tx++;
		uart->x_char = 0;
		return false;
	}
	if (uart_tx_stopped(&chan->uart)) {
		dev_dbg(&ts->spi->dev, "tx: stopped!\n");
		sc16is7x2_stop_tx(uart);
		return false;
	}
	if (uart_circ_empty(xmit)) {
		__stop_tx(chan);
		return false;
	}

	txlvl = sc16is7x2_read(ts->spi, REG_TXLVL, ch);
	if (txlvl <= 0) {
		dev_dbg(&ts->spi->dev, " fifo full\n");
		return false;
	}

	/* number of bytes to transfer to the fifo */
	count = MIN(txlvl, uart_circ_chars_pending(xmit));  //uart_circ_chars_pending = ((head) - (tail)) & ((UART_XMIT_SIZE)-1)
  printk(KERN_DEBUG "+++++++++>kathy test%s:%d channel = %d bytes to transfer to the fifo = %d \n",__func__,__LINE__,ch,count);
	split_transfer = (UART_XMIT_SIZE - xmit->tail) <= count;
	/* add command transfer */
	spi_message_add_tail(&(chan->fifo_tx[0]), &(ts->fifo_message));
	/* add first fifo transfer */
	spi_message_add_tail(&(chan->fifo_tx[1]), &(ts->fifo_message));
  
	chan->fifo_tx[1].tx_buf = xmit->buf + xmit->tail;

	if (!split_transfer) {
		chan->fifo_tx[1].len = count;
		//chan->fifo_tx[1].cs_change = true;
		chan->fifo_tx[1].cs_change = false;

		chan->fifo_tx[2].len = 0;
	} else {
		chan->fifo_tx[1].len = (UART_XMIT_SIZE - 1) - xmit->tail;
//		chan->fifo_tx[1].cs_change = false;
    chan->fifo_tx[1].cs_change = true;

		chan->fifo_tx[2].tx_buf = xmit->buf;
	  chan->fifo_tx[2].cs_change = true;
//		chan->fifo_tx[2].cs_change = false;
		chan->fifo_tx[2].len = count - chan->fifo_tx[1].len;
		/* add second fifo transfer */
		spi_message_add_tail(&(chan->fifo_tx[2]), &(ts->fifo_message));
	}

	xmit->tail = (xmit->tail + count) & (UART_XMIT_SIZE - 1);
	return true;
}
#if 0 //kathy chang delet 2010-11-22 10:14
static void sc16is7x2_handle_modem(struct sc16is7x2_chip *ts, unsigned ch)
{
	struct sc16is7x2_channel *chan = &(ts->channel[ch]);
	struct uart_port *uart = &chan->uart;

	if (chan->msr & UART_MSR_ANY_DELTA
			&& chan->ier & UART_IER_MSI
			&& uart->state != NULL) {
		if (chan->msr & UART_MSR_TERI)
			uart->icount.rng++;
		if (chan->msr & UART_MSR_DDSR)
			uart->icount.dsr++;
		if (chan->msr & UART_MSR_DDCD)
			uart_handle_dcd_change(uart, chan->msr & UART_MSR_DCD);
		if (chan->msr & UART_MSR_DCTS)
			uart_handle_cts_change(uart, chan->msr & UART_MSR_CTS);

		wake_up_interruptible(&uart->state->port.delta_msr_wait);
	}
}
#endif

static bool sc16is7x2_handle_channel(struct sc16is7x2_chip *ts, unsigned ch) 
{
	struct sc16is7x2_channel *chan = &(ts->channel[ch]);
	struct spi_message *m = &(ts->fifo_message);
	bool rx, tx;
  
  printk(KERN_DEBUG "---------> function call %s\n", __func__);
	dev_dbg(&ts->spi->dev, "%s (%i)\n", __func__, ch);

	chan->iir = sc16is7x2_read(ts->spi, UART_IIR, ch);
	chan->msr = sc16is7x2_read(ts->spi, UART_MSR, ch);
	chan->lsr = sc16is7x2_read(ts->spi, UART_LSR, ch);	

#if 0 //kathy chang delete 2010-11-22 10:13
	sc16is7x2_handle_modem(ts, ch);
#endif
	spi_message_init(m);
	rx = sc16is7x2_msg_add_fifo_rx(ts, ch);
	tx = sc16is7x2_msg_add_fifo_tx(ts, ch);
  printk(KERN_DEBUG "+++++++++>kathy test%s:%d rx %d tx =%d\n",__func__,__LINE__,rx,tx);
	if (rx || tx)
		spi_sync(ts->spi, m);
  
	if (rx)
		sc16is7x2_handle_fifo_rx(chan);
	if (tx)
		sc16is7x2_handle_fifo_tx(chan);

	dev_dbg(&ts->spi->dev, "%s finished (iir = 0x%02x)\n",
			__func__, chan->iir);

	return (chan->iir & UART_IIR_NO_INT) == 0x00; //
}

static void sc16is7x2_work(struct work_struct *w)
{
	struct sc16is7x2_chip *ts =
			container_of(w, struct sc16is7x2_chip, work);
	unsigned pending = 0;
	unsigned ch = 0;
  
  printk(KERN_DEBUG "---------> function call %s\n", __func__);
	dev_dbg(&ts->spi->dev, "%s\n", __func__);
	BUG_ON(!w);
	BUG_ON(!ts);


	if (ts->force_end_work) {
		dev_dbg(&ts->spi->dev, "%s: force end!\n", __func__);
		return;
	}

	if (ts->channel[0].active)
		pending |= BIT(0);
	if (ts->channel[1].active)
		pending |= BIT(1);

	do {
		mutex_lock(&(ts->channel[ch].lock));
		if (pending & BIT(ch) && ts->channel[ch].active) {
			if (!sc16is7x2_handle_channel(ts, ch))
				pending &= ~BIT(ch);
		}
		mutex_unlock(&(ts->channel[ch].lock));
		ch ^= 1;	/* switch channel */
	} while (!ts->force_end_work && !freezing(current) && pending);

	dev_dbg(&ts->spi->dev, "%s finished\n", __func__);
}

static irqreturn_t sc16is7x2_interrupt(int irq, void *dev_id)
{
	struct sc16is7x2_chip *ts = dev_id;
 
  printk(KERN_DEBUG "---------> sc16is7x2_interrupt function call %s\n", __func__);
  
	dev_dbg(&ts->spi->dev, "%s\n", __func__); 
	
	
	if (!ts->force_end_work && !work_pending(&ts->work) &&
	    !freezing(current) && !ts->suspending)
	{		
		queue_work(ts->workqueue, &ts->work);
	}
	
	return IRQ_HANDLED;
}


/* ******************************** INIT ********************************* */ 

static struct uart_driver sc16is7x2_uart_driver;


static int sc16is7x2_register_uart_port(struct sc16is7x2_chip *ts,
		struct sc16is7x2_platform_data *pdata, unsigned ch)
{
	struct sc16is7x2_channel *chan = &(ts->channel[ch]);
	struct uart_port *uart = &chan->uart;

	mutex_init(&chan->lock);
	chan->active = false;	/* will be set in startup */
	chan->chip = ts;

	chan->read_fifo_cmd = READ_CMD(UART_RX, ch);
	chan->fifo_rx.tx_buf = &(chan->read_fifo_cmd);
	chan->fifo_rx.rx_buf = chan->rxbuf;
//	chan->fifo_rx.cs_change = true;
  chan->fifo_rx.cs_change = false;

	chan->write_fifo_cmd = WRITE_CMD(UART_TX, ch);
	chan->fifo_tx[0].tx_buf = &(chan->write_fifo_cmd);
	chan->fifo_tx[0].rx_buf = NULL;
	chan->fifo_tx[0].len = 1;
	chan->fifo_tx[0].cs_change = false;
	chan->fifo_tx[1].rx_buf = NULL;
	chan->fifo_tx[2].rx_buf = NULL;

	uart->irq = ts->spi->irq;
	uart->uartclk = pdata->uartclk;
	uart->fifosize = FIFO_SIZE;
	uart->ops = &sc16is7x2_uart_ops;
	uart->flags = UPF_SKIP_TEST | UPF_BOOT_AUTOCONF;
	uart->line = pdata->uart_base + ch;
	uart->type = PORT_SC16IS7X2;
	uart->dev = &ts->spi->dev;
 
	return uart_add_one_port(&sc16is7x2_uart_driver, uart);
}

static int __devinit sc16is7x2_probe(struct spi_device *spi)
{
	struct sc16is7x2_chip *ts;
	struct sc16is7x2_platform_data *pdata;
	int ret;
  printk(KERN_DEBUG "---------> function call %s\n", __func__);


  spi->max_speed_hz = H_SPI_SPEED; //can modify
	pdata = &sc16is7x2_spi_data;

	ret = spi_setup(spi);
	if (ret < 0)
		return ret;

	ts = kzalloc(sizeof(struct sc16is7x2_chip), GFP_KERNEL);
	if (!ts)
		return -ENOMEM;

	mutex_init(&ts->lock);
	dev_set_drvdata(&spi->dev, ts);
	ts->spi = spi;
	ts->force_end_work = 1;

	/* Reset the chip TODO: and disable IRQ output */
	sc16is7x2_write(spi, REG_IOC, 0, IOC_SRESET);
  
  at91_sys_write(AT91_PMC+PMC_PCER,1<<spi->irq);
	at91_set_gpio_input(AT91_PIN_PC12, 1);
  at91_set_A_periph(AT91_PIN_PC12, 1);

	at91_sys_write(AT91_AIC_SMR(spi->irq),(1<<5)|7);
	at91_sys_write(AT91_AIC_ICCR,1<<spi->irq);

  printk(KERN_DEBUG "+++++++++++++++++++++> %s £º%d  AT91_AIC_SMR(29) %x  \n",__func__,__LINE__,at91_sys_read(AT91_AIC_SMR(spi->irq)));
  
	ret = request_irq(spi->irq, sc16is7x2_interrupt,0, "sc16is7x2", ts);
	if (ret)
  {
    printk(KERN_INFO "ERROR: can't get assigned irq %d \n\r", spi->irq);         
  }  


	if (ret) {
		dev_warn(&ts->spi->dev, "cannot register interrupt\n");
		goto exit_destroy;
	}
  
	ret = sc16is7x2_register_uart_port(ts, pdata, 0);
	if (ret)
		goto exit_irq;
	 
	ret = sc16is7x2_register_uart_port(ts, pdata, 1);
	if (ret)
		goto exit_uart0;

	ts->workqueue = create_freezeable_workqueue(DRIVER_NAME);
	if (!ts->workqueue) {
		dev_warn(&ts->spi->dev, "cannot create workqueue\n");
		ret = -EBUSY;
    goto exit_uart1;
	}
	INIT_WORK(&ts->work, sc16is7x2_work);
	ts->force_end_work = 0;
  printk(KERN_INFO DRIVER_NAME " at CS%d (irq %d), 2 UARTs ttySC%d, ttySC%d is successful!!!!\n",
			spi->chip_select, spi->irq,pdata->uart_base, pdata->uart_base + 1);  
	return ret;

exit_uart1:
	uart_remove_one_port(&sc16is7x2_uart_driver, &ts->channel[1].uart);

exit_uart0:
	uart_remove_one_port(&sc16is7x2_uart_driver, &ts->channel[0].uart);

exit_irq:
	free_irq(spi->irq, ts);

exit_destroy:
	dev_set_drvdata(&spi->dev, NULL);
	mutex_destroy(&ts->lock);
	kfree(ts);
	return ret;
}

static int __devexit sc16is7x2_remove(struct spi_device *spi)
{
	struct sc16is7x2_chip *ts;
	int ret;

	ts = dev_get_drvdata(&spi->dev);
	
	if (ts == NULL)
		return -ENODEV;
	free_irq(spi->irq, ts);
	ts->force_end_work = 1;
	if (ts->workqueue) {
		flush_workqueue(ts->workqueue);
		destroy_workqueue(ts->workqueue);
		ts->workqueue = NULL;
	}
	dev_set_drvdata(&spi->dev, NULL);
	ret = uart_remove_one_port(&sc16is7x2_uart_driver,
			&ts->channel[0].uart);
	if (ret) {
		dev_err(&spi->dev, "Failed to remove the UART port 0: %d\n",
			ret);
		goto exit_error;
	}
	ret = uart_remove_one_port(&sc16is7x2_uart_driver,
			&ts->channel[1].uart);
	if (ret) {
		dev_err(&spi->dev, "Failed to remove the UART port 1: %d\n",
			ret);
		goto exit_error;
	}

	mutex_destroy(&ts->lock);
	kfree(ts);

exit_error:
	return ret;
}

static struct uart_driver sc16is7x2_uart_driver = {
	.owner          = THIS_MODULE,
	.driver_name    = DRIVER_NAME,
	.dev_name       = "ttySC",
	.major          = SC16IS7X2_MAJOR,
	.minor          = SC16IS7X2_MINOR,
	.nr             = MAX_SC16IS7X2,
};

static struct spi_driver sc16is7x2_spi_driver = {
	.driver = {
		.name		= DRIVER_NAME,
		.bus    = &spi_bus_type,
		.owner  = THIS_MODULE,
	},
	.probe		= sc16is7x2_probe,
	.remove		= __devexit_p(sc16is7x2_remove),
};

static int __init sc16is7x2_init(void)
{
	int ret;	
	
	ret = uart_register_driver(&sc16is7x2_uart_driver);
	if (ret) {
		printk(KERN_ERR "Couldn't register sc16is7x2 uart driver\n");
		return ret;
	}	
	return spi_register_driver(&sc16is7x2_spi_driver);
}

module_init(sc16is7x2_init);

static void __exit sc16is7x2_exit(void)
{	
	spi_unregister_driver(&sc16is7x2_spi_driver);	
	uart_unregister_driver(&sc16is7x2_uart_driver);
}
module_exit(sc16is7x2_exit);

MODULE_AUTHOR("kathy chang");
MODULE_LICENSE("GPL v2");
MODULE_DESCRIPTION("SC16IS7x2 SPI based UART chip");
MODULE_ALIAS("spi:" DRIVER_NAME);
begin:vcard
fn:Manuel Stahl
n:Stahl;Manuel
email;internet:manuel.stahl@xxxxxxxxxxxxxxxxx
tel;work:+49 911 58061-6419
x-mozilla-html:FALSE
version:2.1
end:vcard


[Index of Archives]     [Kernel Newbies]     [Security]     [Netfilter]     [Bugtraq]     [Linux PPP]     [Linux FS]     [Yosemite News]     [MIPS Linux]     [ARM Linux]     [Linux Security]     [Linux RAID]     [Samba]     [Video 4 Linux]     [Linmodem]     [Device Mapper]     [Linux Kernel for ARM]

  Powered by Linux