* Re: about sc16is7x2 drivier
2010-12-20 9:09 ` Fw: about sc16is7x2 drivier Manuel Stahl
@ 2010-12-20 10:03 ` Manuel Stahl
0 siblings, 0 replies; 3+ messages in thread
From: Manuel Stahl @ 2010-12-20 10:03 UTC (permalink / raw)
To: changgx; +Cc: hunterzhu@baite-group.com, spi-devel-general, linux-serial
[-- Attachment #1: Type: text/plain, Size: 6118 bytes --]
Hello Kathy Chang,
I will have a look at your changes. Please send your mails also to
spi-devel-general@lists.sourceforge.net and
spi-devel-general@lists.sourceforge.net (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@163.com<mailto:changgx@163.com>
>>>>> >>>>
>>>>> >>>> ------------------------------------------------
>>>>> >>>>
>>>>> >>>>
>>>>> >>>>
>>>>> >>>> = = = = = = = = = = = = = = = = = = = =
>>>>> >>>> 致
>>>>> >>>> 礼!
>>>>> >>>> changgx
>>>>> >>>> changgx@163.com<mailto:changgx@163.com>
>>>>> >>>> 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@iis.fraunhofer.de
>>>> >>>
>>>> >>> 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@163.com
>>> >> 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@iis.fraunhofer.de
>> >
>> >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@163.com
> 2010-12-20
>
>
> sa16is7x2.h
>
> N\x18¬n‡r¥ªíÂ)emçhÂyhi×¢w^™©Ý
>
>
> sc16is7x2.c
>
> N\x18¬n‡r¥ªíÂ)emçhÂyhi×¢w^™©Ý
[-- Attachment #2: Makefile --]
[-- Type: application/octet-stream, Size: 364 bytes --]
EXTRA_CFLAGS = -Wall -O2 -gstabs+
CROSS-COMPILE = arm-none-linux-gnueabi-
CC=$(CROSS-COMPILE)gcc
obj-m := sc16is7x2.o
KDIR := /home/at91sam9260ek/linux-2.6.27
PWD := $(shell pwd)
EXTRA_CFLAGS += -I$(obj)/../include
default:
$(MAKE) -C $(KDIR) M=$(PWD) modules
clean:
rm -rf *.o *~ core .depend .*.cmd *.ko *.mod.c .tmp_versions *.symvers test *.order
[-- Warning: decoded text below may be mangled, UTF-8 assumed --]
[-- Attachment #3: sa16is7x2.h --]
[-- Type: text/x-chdr; name="sa16is7x2.h", Size: 202 bytes --]
#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
[-- Attachment #4: sc16is7x2.c --]
[-- Type: text/plain, Size: 35119 bytes --]
/**
* drivers/serial/sc16is7x2.c
*
* Copyright (C) 2010 kathy chang changgx@163.com
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*
* 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);
[-- Attachment #5: manuel_stahl.vcf --]
[-- Type: text/x-vcard, Size: 170 bytes --]
begin:vcard
fn:Manuel Stahl
n:Stahl;Manuel
email;internet:manuel.stahl@iis.fraunhofer.de
tel;work:+49 911 58061-6419
x-mozilla-html:FALSE
version:2.1
end:vcard
^ permalink raw reply [flat|nested] 3+ messages in thread