Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Support
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Sign in / Register
Toggle navigation
L
linux
Project overview
Project overview
Details
Activity
Releases
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Issues
0
Issues
0
List
Boards
Labels
Milestones
Merge Requests
0
Merge Requests
0
Analytics
Analytics
Repository
Value Stream
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Create a new issue
Commits
Issue Boards
Open sidebar
Kirill Smelkov
linux
Commits
b203e95a
Commit
b203e95a
authored
Oct 05, 2004
by
Linus Torvalds
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Remove rest of legacy arch/m32r/drivers directory
parent
ef8932e3
Changes
7
Show whitespace changes
Inline
Side-by-side
Showing
7 changed files
with
0 additions
and
6049 deletions
+0
-6049
arch/m32r/drivers/ds1302.c
arch/m32r/drivers/ds1302.c
+0
-432
arch/m32r/drivers/m32r-pldsio.c
arch/m32r/drivers/m32r-pldsio.c
+0
-3067
arch/m32r/drivers/m32r_cfc.c
arch/m32r/drivers/m32r_cfc.c
+0
-910
arch/m32r/drivers/m32r_cfc.h
arch/m32r/drivers/m32r_cfc.h
+0
-85
arch/m32r/drivers/m32r_pcc.c
arch/m32r/drivers/m32r_pcc.c
+0
-821
arch/m32r/drivers/m32r_pcc.h
arch/m32r/drivers/m32r_pcc.h
+0
-70
arch/m32r/drivers/m5drv.c
arch/m32r/drivers/m5drv.c
+0
-664
No files found.
arch/m32r/drivers/ds1302.c
deleted
100644 → 0
View file @
ef8932e3
/*!***************************************************************************
*!
*! FILE NAME : ds1302.c
*!
*! DESCRIPTION: Implements an interface for the DS1302 RTC through Etrax I/O
*!
*! Functions exported: ds1302_readreg, ds1302_writereg, ds1302_init, get_rtc_status
*!
*! $Log: ds1302.c,v $
*! Revision 1.2 2003/10/29 08:42:58 fujiwara
*! Set PLD_RTCBAUR from bus clock
*!
*! Revision 1.12 2002/04/10 15:35:25 johana
*! Moved probe function closer to init function and marked it __init.
*!
*! Revision 1.11 2001/06/14 12:35:52 jonashg
*! The ATA hack is back. It is unfortunately the only way to set g27 to output.
*!
*! Revision 1.9 2001/06/14 10:00:14 jonashg
*! No need for tempudelay to be inline anymore (had to adjust the usec to
*! loops conversion because of this to make it slow enough to be a udelay).
*!
*! Revision 1.8 2001/06/14 08:06:32 jonashg
*! Made tempudelay delay usecs (well, just a tad more).
*!
*! Revision 1.7 2001/06/13 14:18:11 jonashg
*! Only allow processes with SYS_TIME capability to set time and charge.
*!
*! Revision 1.6 2001/06/12 15:22:07 jonashg
*! * Made init function __init.
*! * Parameter to out_byte() is unsigned char.
*! * The magic number 42 has got a name.
*! * Removed comment about /proc (nothing is exported there).
*!
*! Revision 1.5 2001/06/12 14:35:13 jonashg
*! Gave the module a name and added it to printk's.
*!
*! Revision 1.4 2001/05/31 14:53:40 jonashg
*! Made tempudelay() inline so that the watchdog doesn't reset (see
*! function comment).
*!
*! Revision 1.3 2001/03/26 16:03:06 bjornw
*! Needs linux/config.h
*!
*! Revision 1.2 2001/03/20 19:42:00 bjornw
*! Use the ETRAX prefix on the DS1302 options
*!
*! Revision 1.1 2001/03/20 09:13:50 magnusmn
*! Linux 2.4 port
*!
*! Revision 1.10 2000/07/05 15:38:23 bjornw
*! Dont update kernel time when a RTC_SET_TIME is done
*!
*! Revision 1.9 2000/03/02 15:42:59 macce
*! * Hack to make RTC work on all 2100/2400
*!
*! Revision 1.8 2000/02/23 16:59:18 torbjore
*! added setup of R_GEN_CONFIG when RTC is connected to the generic port.
*!
*! Revision 1.7 2000/01/17 15:51:43 johana
*! Added RTC_SET_CHARGE ioctl to enable trickle charger.
*!
*! Revision 1.6 1999/10/27 13:19:47 bjornw
*! Added update_xtime_from_cmos which reads back the updated RTC into the kernel.
*! /dev/rtc calls it now.
*!
*! Revision 1.5 1999/10/27 12:39:37 bjornw
*! Disabled superuser check. Anyone can now set the time.
*!
*! Revision 1.4 1999/09/02 13:27:46 pkj
*! Added shadow for R_PORT_PB_CONFIG.
*! Renamed port_g_shadow to port_g_data_shadow.
*!
*! Revision 1.3 1999/09/02 08:28:06 pkj
*! Made it possible to select either port PB or the generic port for the RST
*! signal line to the DS1302 RTC.
*! Also make sure the RST bit is configured as output on Port PB (if used).
*!
*! Revision 1.2 1999/09/01 14:47:20 bjornw
*! Added support for /dev/rtc operations with ioctl RD_TIME and SET_TIME to read
*! and set the date. Register as major 121.
*!
*! Revision 1.1 1999/09/01 09:45:29 bjornw
*! Implemented a DS1302 RTC driver.
*!
*!
*! ---------------------------------------------------------------------------
*!
*! (C) Copyright 1999, 2000, 2001 Axis Communications AB, LUND, SWEDEN
*!
*! $Id: ds1302.c,v 1.2 2003/10/29 08:42:58 fujiwara Exp $
*!
*!***************************************************************************/
#include <linux/config.h>
#include <linux/fs.h>
#include <linux/init.h>
#include <linux/mm.h>
#include <linux/module.h>
#include <linux/miscdevice.h>
#include <linux/delay.h>
#include <linux/bcd.h>
#include <asm/uaccess.h>
#include <asm/system.h>
#include <asm/io.h>
#include <asm/rtc.h>
#include <asm/m32r.h>
#define RTC_MAJOR_NR 121
/* local major, change later */
static
const
char
ds1302_name
[]
=
"ds1302"
;
/* Send 8 bits. */
static
void
out_byte_rtc
(
unsigned
int
reg_addr
,
unsigned
char
x
)
{
//RST H
outw
(
0x0001
,(
unsigned
long
)
PLD_RTCRSTODT
);
//write data
outw
(((
x
<<
8
)
|
(
reg_addr
&
0xff
)),(
unsigned
long
)
PLD_RTCWRDATA
);
//WE
outw
(
0x0002
,(
unsigned
long
)
PLD_RTCCR
);
//wait
while
(
inw
((
unsigned
long
)
PLD_RTCCR
));
//RST L
outw
(
0x0000
,(
unsigned
long
)
PLD_RTCRSTODT
);
}
static
unsigned
char
in_byte_rtc
(
unsigned
int
reg_addr
)
{
unsigned
char
retval
;
//RST H
outw
(
0x0001
,(
unsigned
long
)
PLD_RTCRSTODT
);
//write data
outw
((
reg_addr
&
0xff
),(
unsigned
long
)
PLD_RTCRDDATA
);
//RE
outw
(
0x0001
,(
unsigned
long
)
PLD_RTCCR
);
//wait
while
(
inw
((
unsigned
long
)
PLD_RTCCR
));
//read data
retval
=
(
inw
((
unsigned
long
)
PLD_RTCRDDATA
)
&
0xff00
)
>>
8
;
//RST L
outw
(
0x0000
,(
unsigned
long
)
PLD_RTCRSTODT
);
return
retval
;
}
/* Enable writing. */
static
void
ds1302_wenable
(
void
)
{
out_byte_rtc
(
0x8e
,
0x00
);
}
/* Disable writing. */
static
void
ds1302_wdisable
(
void
)
{
out_byte_rtc
(
0x8e
,
0x80
);
}
/* Read a byte from the selected register in the DS1302. */
unsigned
char
ds1302_readreg
(
int
reg
)
{
unsigned
char
x
;
x
=
in_byte_rtc
((
0x81
|
(
reg
<<
1
)));
/* read register */
return
x
;
}
/* Write a byte to the selected register. */
void
ds1302_writereg
(
int
reg
,
unsigned
char
val
)
{
ds1302_wenable
();
out_byte_rtc
((
0x80
|
(
reg
<<
1
)),
val
);
ds1302_wdisable
();
}
void
get_rtc_time
(
struct
rtc_time
*
rtc_tm
)
{
unsigned
long
flags
;
local_irq_save
(
flags
);
local_irq_disable
();
rtc_tm
->
tm_sec
=
CMOS_READ
(
RTC_SECONDS
);
rtc_tm
->
tm_min
=
CMOS_READ
(
RTC_MINUTES
);
rtc_tm
->
tm_hour
=
CMOS_READ
(
RTC_HOURS
);
rtc_tm
->
tm_mday
=
CMOS_READ
(
RTC_DAY_OF_MONTH
);
rtc_tm
->
tm_mon
=
CMOS_READ
(
RTC_MONTH
);
rtc_tm
->
tm_year
=
CMOS_READ
(
RTC_YEAR
);
local_irq_restore
(
flags
);
BCD_TO_BIN
(
rtc_tm
->
tm_sec
);
BCD_TO_BIN
(
rtc_tm
->
tm_min
);
BCD_TO_BIN
(
rtc_tm
->
tm_hour
);
BCD_TO_BIN
(
rtc_tm
->
tm_mday
);
BCD_TO_BIN
(
rtc_tm
->
tm_mon
);
BCD_TO_BIN
(
rtc_tm
->
tm_year
);
/*
* Account for differences between how the RTC uses the values
* and how they are defined in a struct rtc_time;
*/
if
(
rtc_tm
->
tm_year
<=
69
)
rtc_tm
->
tm_year
+=
100
;
rtc_tm
->
tm_mon
--
;
}
static
unsigned
char
days_in_mo
[]
=
{
0
,
31
,
28
,
31
,
30
,
31
,
30
,
31
,
31
,
30
,
31
,
30
,
31
};
/* ioctl that supports RTC_RD_TIME and RTC_SET_TIME (read and set time/date). */
static
int
rtc_ioctl
(
struct
inode
*
inode
,
struct
file
*
file
,
unsigned
int
cmd
,
unsigned
long
arg
)
{
unsigned
long
flags
;
switch
(
cmd
)
{
case
RTC_RD_TIME
:
/* read the time/date from RTC */
{
struct
rtc_time
rtc_tm
;
memset
(
&
rtc_tm
,
0
,
sizeof
(
struct
rtc_time
));
get_rtc_time
(
&
rtc_tm
);
if
(
copy_to_user
((
struct
rtc_time
*
)
arg
,
&
rtc_tm
,
sizeof
(
struct
rtc_time
)))
return
-
EFAULT
;
return
0
;
}
case
RTC_SET_TIME
:
/* set the RTC */
{
struct
rtc_time
rtc_tm
;
unsigned
char
mon
,
day
,
hrs
,
min
,
sec
,
leap_yr
;
unsigned
int
yrs
;
if
(
!
capable
(
CAP_SYS_TIME
))
return
-
EPERM
;
if
(
copy_from_user
(
&
rtc_tm
,
(
struct
rtc_time
*
)
arg
,
sizeof
(
struct
rtc_time
)))
return
-
EFAULT
;
yrs
=
rtc_tm
.
tm_year
+
1900
;
mon
=
rtc_tm
.
tm_mon
+
1
;
/* tm_mon starts at zero */
day
=
rtc_tm
.
tm_mday
;
hrs
=
rtc_tm
.
tm_hour
;
min
=
rtc_tm
.
tm_min
;
sec
=
rtc_tm
.
tm_sec
;
if
((
yrs
<
1970
)
||
(
yrs
>
2069
))
return
-
EINVAL
;
leap_yr
=
((
!
(
yrs
%
4
)
&&
(
yrs
%
100
))
||
!
(
yrs
%
400
));
if
((
mon
>
12
)
||
(
day
==
0
))
return
-
EINVAL
;
if
(
day
>
(
days_in_mo
[
mon
]
+
((
mon
==
2
)
&&
leap_yr
)))
return
-
EINVAL
;
if
((
hrs
>=
24
)
||
(
min
>=
60
)
||
(
sec
>=
60
))
return
-
EINVAL
;
if
(
yrs
>=
2000
)
yrs
-=
2000
;
/* RTC (0, 1, ... 69) */
else
yrs
-=
1900
;
/* RTC (70, 71, ... 99) */
BIN_TO_BCD
(
sec
);
BIN_TO_BCD
(
min
);
BIN_TO_BCD
(
hrs
);
BIN_TO_BCD
(
day
);
BIN_TO_BCD
(
mon
);
BIN_TO_BCD
(
yrs
);
local_irq_save
(
flags
);
local_irq_disable
();
CMOS_WRITE
(
yrs
,
RTC_YEAR
);
CMOS_WRITE
(
mon
,
RTC_MONTH
);
CMOS_WRITE
(
day
,
RTC_DAY_OF_MONTH
);
CMOS_WRITE
(
hrs
,
RTC_HOURS
);
CMOS_WRITE
(
min
,
RTC_MINUTES
);
CMOS_WRITE
(
sec
,
RTC_SECONDS
);
local_irq_restore
(
flags
);
/* Notice that at this point, the RTC is updated but
* the kernel is still running with the old time.
* You need to set that separately with settimeofday
* or adjtimex.
*/
return
0
;
}
case
RTC_SET_CHARGE
:
/* set the RTC TRICKLE CHARGE register */
{
int
tcs_val
;
if
(
!
capable
(
CAP_SYS_TIME
))
return
-
EPERM
;
if
(
copy_from_user
(
&
tcs_val
,
(
int
*
)
arg
,
sizeof
(
int
)))
return
-
EFAULT
;
tcs_val
=
RTC_TCR_PATTERN
|
(
tcs_val
&
0x0F
);
ds1302_writereg
(
RTC_TRICKLECHARGER
,
tcs_val
);
return
0
;
}
default:
return
-
EINVAL
;
}
}
int
get_rtc_status
(
char
*
buf
)
{
char
*
p
;
struct
rtc_time
tm
;
p
=
buf
;
get_rtc_time
(
&
tm
);
/*
* There is no way to tell if the luser has the RTC set for local
* time or for Universal Standard Time (GMT). Probably local though.
*/
p
+=
sprintf
(
p
,
"rtc_time
\t
: %02d:%02d:%02d
\n
"
"rtc_date
\t
: %04d-%02d-%02d
\n
"
,
tm
.
tm_hour
,
tm
.
tm_min
,
tm
.
tm_sec
,
tm
.
tm_year
+
1900
,
tm
.
tm_mon
+
1
,
tm
.
tm_mday
);
return
p
-
buf
;
}
/* The various file operations we support. */
static
struct
file_operations
rtc_fops
=
{
.
owner
=
THIS_MODULE
,
.
ioctl
=
rtc_ioctl
,
};
/* Probe for the chip by writing something to its RAM and try reading it back. */
#define MAGIC_PATTERN 0x42
static
int
__init
ds1302_probe
(
void
)
{
int
retval
,
res
,
baur
;
baur
=
(
boot_cpu_data
.
bus_clock
/
(
2
*
1000
*
1000
));
printk
(
"%s: Set PLD_RTCBAUR = %d
\n
"
,
ds1302_name
,
baur
);
outw
(
0x0000
,(
unsigned
long
)
PLD_RTCCR
);
outw
(
0x0000
,(
unsigned
long
)
PLD_RTCRSTODT
);
outw
(
baur
,(
unsigned
long
)
PLD_RTCBAUR
);
/* Try to talk to timekeeper. */
ds1302_wenable
();
/* write RAM byte 0 */
/* write something magic */
out_byte_rtc
(
0xc0
,
MAGIC_PATTERN
);
/* read RAM byte 0 */
if
((
res
=
in_byte_rtc
(
0xc1
))
==
MAGIC_PATTERN
)
{
char
buf
[
100
];
ds1302_wdisable
();
printk
(
"%s: RTC found.
\n
"
,
ds1302_name
);
get_rtc_status
(
buf
);
printk
(
buf
);
retval
=
1
;
}
else
{
printk
(
"%s: RTC not found.
\n
"
,
ds1302_name
);
retval
=
0
;
}
return
retval
;
}
/* Just probe for the RTC and register the device to handle the ioctl needed. */
int
__init
ds1302_init
(
void
)
{
if
(
!
ds1302_probe
())
{
return
-
1
;
}
return
0
;
}
static
int
__init
ds1302_register
(
void
)
{
ds1302_init
();
if
(
register_chrdev
(
RTC_MAJOR_NR
,
ds1302_name
,
&
rtc_fops
))
{
printk
(
KERN_INFO
"%s: unable to get major %d for rtc
\n
"
,
ds1302_name
,
RTC_MAJOR_NR
);
return
-
1
;
}
return
0
;
}
module_init
(
ds1302_register
);
arch/m32r/drivers/m32r-pldsio.c
deleted
100644 → 0
View file @
ef8932e3
/* $Id$
*
* M32R onboard PLD serial module support.
*
* Much of the design and some of the code came from serial.c:
* Copyright (C) 1991, 1992 Linus Torvalds
* Copyright (C) 1992, 1993, 1994, 1995, 1996, 1997,
* 1998, 1999 Theodore Ts'o
*
* M32R work:
* Copyright 1996, 2001, Mitsubishi Electric Corporation
* Copyright (C) 2000,2001 by Hiro Kondo, Hiro Takata, and Hitoshi Yamamoto.
*
* 2002-12-25: Support M32700UT Platform by Takeo Takahashi
* Derived from dbg_console.c.
*/
static
char
*
serial_version
=
"kondo"
;
static
char
*
serial_revdate
=
"2002-09-11"
;
static
char
*
serial_name
=
"M32R Serial driver"
;
#define LOCAL_VERSTRING ""
#include <linux/config.h>
#include <linux/version.h>
#include <linux/module.h>
#include <linux/ioport.h>
#include <linux/mm.h>
#include <linux/tty_driver.h>
#include <linux/smp_lock.h>
#include <linux/init.h>
#include <linux/major.h>
#include <linux/console.h>
#include <linux/kdev_t.h>
#include <linux/kernel.h>
#include <linux/interrupt.h>
#include <linux/serial.h>
#include <linux/serialP.h>
/* serial_state */
#include <linux/slab.h>
/* kmalloc */
#include <asm/uaccess.h>
#include <asm/io.h>
#include <asm/irq.h>
#include <asm/serial.h>
#include <asm/m32r.h>
extern
struct
console
console_for_debug
;
static
int
psio_write
(
struct
tty_struct
*
tty
,
int
from_user
,
const
unsigned
char
*
buf
,
int
count
);
static
int
psio_write_room
(
struct
tty_struct
*
tty
);
static
int
psio_chars_in_buffer
(
struct
tty_struct
*
tty
);
static
void
dbg_console_write
(
struct
console
*
,
const
char
*
,
unsigned
);
static
kdev_t
dbg_console_device
(
struct
console
*
c
);
//static void psio_interrupt_single(int, void *, struct pt_regs *);
void
psio_interrupt_single
(
int
,
void
*
,
struct
pt_regs
*
);
static
void
psio_receive_chars
(
struct
async_struct
*
,
int
*
);
static
void
psio_wait_until_sent
(
struct
tty_struct
*
,
int
);
static
void
change_speed
(
struct
async_struct
*
,
struct
termios
*
);
static
void
autoconfig
(
struct
serial_state
*
);
static
unsigned
detect_uart_irq
(
struct
serial_state
*
);
static
struct
tty_driver
psio_driver
;
static
int
psio_refcount
;
#define RS_STROBE_TIME (10*HZ)
#define RS_ISR_PASS_LIMIT 256
/* number of characters left in xmit buffer before we ask for more */
#define WAKEUP_CHARS 256
static
struct
async_struct
*
IRQ_ports
[
NR_IRQS
];
static
int
IRQ_timeout
[
NR_IRQS
];
#ifdef CONFIG_SERIAL_CONSOLE
static
struct
console
cons
;
static
int
lsr_break_flag
;
#endif
#define BAUDRATE 115200
/* Set Baudrate */
/*
* Here we define the default xmit fifo size used for each type of
* UART
*/
static
struct
serial_uart_config
uart_config
[]
=
{
{
"unknown"
,
1
,
0
},
{
"8250"
,
1
,
0
},
{
"16450"
,
1
,
0
},
{
"16550"
,
1
,
0
},
{
"16550A"
,
16
,
UART_CLEAR_FIFO
|
UART_USE_FIFO
},
{
"cirrus"
,
1
,
0
},
/* usurped by cyclades.c */
{
"ST16650"
,
1
,
UART_CLEAR_FIFO
|
UART_STARTECH
},
{
"ST16650V2"
,
32
,
UART_CLEAR_FIFO
|
UART_USE_FIFO
|
UART_STARTECH
},
{
"TI16750"
,
64
,
UART_CLEAR_FIFO
|
UART_USE_FIFO
},
{
"Startech"
,
1
,
0
},
/* usurped by cyclades.c */
{
"16C950/954"
,
128
,
UART_CLEAR_FIFO
|
UART_USE_FIFO
},
{
"ST16654"
,
64
,
UART_CLEAR_FIFO
|
UART_USE_FIFO
|
UART_STARTECH
},
{
"XR16850"
,
128
,
UART_CLEAR_FIFO
|
UART_USE_FIFO
|
UART_STARTECH
},
{
"RSA"
,
2048
,
UART_CLEAR_FIFO
|
UART_USE_FIFO
},
{
"m32102"
,
1
,
0
},
{
0
,
0
}
};
static
struct
serial_state
rs_table
[
RS_TABLE_SIZE
]
=
{
/* UART CLK PORT IRQ FLAGS */
{
0
,
BAUDRATE
,
((
int
)
PLD_ESIO0CR
+
NONCACHE_OFFSET
),
PLD_IRQ_SIO0_RCV
,
STD_COM_FLAGS
},
};
#define NR_PORTS (sizeof(rs_table)/sizeof(struct serial_state))
#define HIGH_BITS_OFFSET ((sizeof(long)-sizeof(int))*8)
static
DECLARE_TASK_QUEUE
(
tq_psio_serial
);
static
struct
tty_struct
*
psio_table
[
NR_PORTS
];
static
struct
termios
*
psio_termios
[
NR_PORTS
];
static
struct
termios
*
psio_termios_locked
[
NR_PORTS
];
#if defined(MODULE) && defined(SERIAL_DEBUG_MCOUNT)
#define DBG_CNT(s) printk("(%s): [%x] refc=%d, serc=%d, ttyc=%d -> %s\n", \
kdevname(tty->device), (info->flags), serial_refcount,info->count,tty->count,s
)
#else
#define DBG_CNT(s)
#endif
static
struct
timer_list
serial_timer
;
static
unsigned
char
*
tmp_buf
;
#ifdef DECLARE_MUTEX
static
DECLARE_MUTEX
(
tmp_buf_sem
);
#else
static
struct
semaphore
tmp_buf_sem
=
MUTEX
;
#endif
static
int
dbg_console_setup
(
struct
console
*
console
,
char
*
options
);
#undef SIO0CR
#undef SIO0MOD0
#undef SIO0MOD1
#undef SIO0STS
#undef SIO0IMASK
#undef SIO0BAUR
#undef SIO0TXB
#undef SIO0RXB
#define SIO0CR PLD_ESIO0CR
#define SIO0MOD0 PLD_ESIO0MOD0
#define SIO0MOD1 PLD_ESIO0MOD1
#define SIO0STS PLD_ESIO0STS
#define SIO0IMASK PLD_ESIO0INTCR
#define SIO0BAUR PLD_ESIO0BAUR
#define SIO0TXB PLD_ESIO0TXB
#define SIO0RXB PLD_ESIO0RXB
#define SIO_IMASK_TEMPIE (1UL<<1)
/* b14: enable */
#define SIO_IMASK_RXCEN (1UL<<2)
/* b13: enable */
#define SIO_IMASK_REIE (0UL)
#define SIO_SIO0STS_TEMP (1UL<<0)
/* Transmitter Register Empty */
#define SIO_SIO0STS_TXCP (1UL<<1)
#define SIO_SIO0STS_RXCP (1UL<<2)
#define SIO_SIO0STS_OERR (0UL)
#define SIO_SIO0STS_PERR (0UL)
#define SIO_SIO0STS_FERR (0UL)
#define SIO_SIO0MOD0_CTSS (1UL<<6)
#define SIO_SIO0MOD0_RTSS (1UL<<7)
#define SIO_NONE (0UL)
#define UART_TX ((unsigned char *)SIO0TXB - (unsigned char *)SIO0CR)
#define UART_RX ((unsigned char *)SIO0RXB - (unsigned char *)SIO0CR)
#define UART_IER ((unsigned char *)SIO0IMASK - (unsigned char *)SIO0CR)
#define UART_IER_THRI SIO_IMASK_TEMPIE
#define UART_IER_MSI SIO_NONE
#define UART_IER_RLSI SIO_IMASK_RXCEN
#define UART_IER_RDI SIO_IMASK_REIE
#define UART_LSR ((unsigned char *)SIO0STS - (unsigned char *)SIO0CR)
#define UART_LSR_DR SIO_SIO0STS_RXCP
#define UART_LSR_THRE SIO_SIO0STS_TEMP
#define UART_LSR_TEMT SIO_SIO0STS_TXCP
#define UART_EMPTY (UART_LSR_TEMT | UART_LSR_THRE)
#define UART_LSR_BI SIO_NONE
#define UART_LSR_PE SIO_SIO0STS_PERR
#define UART_LSR_FE SIO_SIO0STS_FERR
#define UART_LSR_OE SIO_SIO0STS_OERR
#define UART_IIR ((unsigned char *)SIO0STS - (unsigned char *)SIO0CR)
#define UART_LCR ((unsigned char *)SIO0CR - (unsigned char *)SIO0CR)
#define UART_LCR_SBC SIO_NONE
#define UART_LCR_PARITY SIO_NONE
#define UART_LCR_EPAR SIO_NONE
#define UART_LCR_SPAR SIO_NONE
#define UART_MCR ((unsigned char *)SIO0MOD0 - (unsigned char *)SIO0CR)
#define UART_MCR_RTS SIO_SIO0MOD0_RTSS
#define UART_MCR_DTR SIO_NONE
/* SIO_SIO0MOD0_CTSS */
#define UART_MCR_LOOP SIO_NONE
#define UART_MCR_OUT1 SIO_NONE
#define UART_MCR_OUT2 SIO_NONE
#define UART_MSR ((unsigned char *)SIO0MOD0 - (unsigned char *)SIO0CR)
#define UART_MSR_DCD SIO_NONE
#define UART_MSR_RI SIO_NONE
#define UART_MSR_DSR SIO_NONE
#define UART_MSR_CTS SIO_NONE
#define UART_BAUR ((unsigned char *)SIO0BAUR - (unsigned char *)SIO0CR)
#define UART_MOD0 ((unsigned char *)SIO0MOD0 - (unsigned char *)SIO0CR)
#define UART_MOD1 ((unsigned char *)SIO0MOD1 - (unsigned char *)SIO0CR)
static
inline
unsigned
int
psio_in
(
struct
async_struct
*
info
,
int
offset
)
{
return
*
(
volatile
unsigned
short
*
)(
info
->
port
+
offset
);
}
static
inline
void
psio_out
(
struct
async_struct
*
info
,
int
offset
,
int
value
)
{
*
(
volatile
unsigned
short
*
)(
info
->
port
+
offset
)
=
value
;
}
#define serial_in(info, offset) psio_in(info,(int)offset)
#define serial_out(info, offset, value) psio_out(info,(int)offset, value)
#define serial_inp(info, offset) psio_in(info,(int)offset)
#define serial_outp(info, offset, value) psio_out(info,(int)offset, value)
static
inline
int
serial_paranoia_check
(
struct
async_struct
*
info
,
kdev_t
device
,
const
char
*
routine
)
{
#ifdef SERIAL_PARANOIA_CHECK
static
const
char
*
badmagic
=
"Warning: bad magic number for serial struct (%s) in %s
\n
"
;
static
const
char
*
badinfo
=
"Warning: null async_struct for (%s) in %s
\n
"
;
if
(
!
info
)
{
printk
(
badinfo
,
kdevname
(
device
),
routine
);
return
1
;
}
if
(
info
->
magic
!=
SERIAL_MAGIC
)
{
printk
(
badmagic
,
kdevname
(
device
),
routine
);
return
1
;
}
#endif
return
0
;
}
/*
* ------------------------------------------------------------
* psio_stop() and psio_start()
*
* This routines are called before setting or resetting tty->stopped.
* They enable or disable transmitter interrupts, as necessary.
* ------------------------------------------------------------
*/
static
void
psio_stop
(
struct
tty_struct
*
tty
)
{
struct
async_struct
*
info
=
(
struct
async_struct
*
)
tty
->
driver_data
;
unsigned
long
flags
;
if
(
serial_paranoia_check
(
info
,
tty
->
device
,
"psio_stop"
))
return
;
save_flags
(
flags
);
cli
();
if
(
info
->
IER
&
UART_IER_THRI
)
{
info
->
IER
&=
~
UART_IER_THRI
;
serial_out
(
info
,
UART_IER
,
info
->
IER
);
}
restore_flags
(
flags
);
}
static
void
psio_start
(
struct
tty_struct
*
tty
)
{
struct
async_struct
*
info
=
(
struct
async_struct
*
)
tty
->
driver_data
;
unsigned
long
flags
;
if
(
serial_paranoia_check
(
info
,
tty
->
device
,
"psio_start"
))
return
;
save_flags
(
flags
);
cli
();
if
(
info
->
xmit
.
head
!=
info
->
xmit
.
tail
&&
info
->
xmit
.
buf
&&
!
(
info
->
IER
&
UART_IER_THRI
))
{
info
->
IER
|=
UART_IER_THRI
;
serial_out
(
info
,
UART_IER
,
info
->
IER
);
serial_out
(
info
,
UART_TX
,
info
->
xmit
.
buf
[
info
->
xmit
.
tail
]);
info
->
xmit
.
tail
=
(
info
->
xmit
.
tail
+
1
)
&
(
SERIAL_XMIT_SIZE
-
1
);
info
->
state
->
icount
.
tx
++
;
}
restore_flags
(
flags
);
}
/*
* This routine is used by the interrupt handler to schedule
* processing in the software interrupt portion of the driver.
*/
static
inline
void
psio_sched_event
(
struct
async_struct
*
info
,
int
event
)
{
info
->
event
|=
1
<<
event
;
queue_task
(
&
info
->
tqueue
,
&
tq_psio_serial
);
mark_bh
(
SERIAL_BH
);
}
static
inline
void
psio_receive_chars
(
struct
async_struct
*
info
,
int
*
status
)
{
struct
tty_struct
*
tty
=
info
->
tty
;
unsigned
char
ch
;
struct
async_icount
*
icount
;
int
max_count
=
256
;
icount
=
&
info
->
state
->
icount
;
do
{
if
(
tty
->
flip
.
count
>=
TTY_FLIPBUF_SIZE
)
{
tty
->
flip
.
tqueue
.
routine
((
void
*
)
tty
);
if
(
tty
->
flip
.
count
>=
TTY_FLIPBUF_SIZE
)
return
;
// if TTY_DONT_FLIP is set
}
ch
=
serial_inp
(
info
,
UART_RX
);
*
tty
->
flip
.
char_buf_ptr
=
ch
;
icount
->
rx
++
;
#ifdef SERIAL_DEBUG_INTR
printk
(
"DR%02x:%02x..."
,
ch
,
*
status
);
#endif
*
tty
->
flip
.
flag_buf_ptr
=
0
;
if
(
*
status
&
(
UART_LSR_BI
|
UART_LSR_PE
|
UART_LSR_FE
|
UART_LSR_OE
))
{
/*
* For statistics only
*/
if
(
*
status
&
UART_LSR_BI
)
{
*
status
&=
~
(
UART_LSR_FE
|
UART_LSR_PE
);
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 defined(CONFIG_SERIAL_CONSOLE) && defined(CONFIG_MAGIC_SYSRQ)
if
(
info
->
line
==
cons
.
index
)
{
if
(
!
break_pressed
)
{
break_pressed
=
jiffies
;
goto
ignore_char
;
}
break_pressed
=
0
;
}
#endif
if
(
info
->
flags
&
ASYNC_SAK
)
do_SAK
(
tty
);
}
else
if
(
*
status
&
UART_LSR_PE
)
icount
->
parity
++
;
else
if
(
*
status
&
UART_LSR_FE
)
icount
->
frame
++
;
if
(
*
status
&
UART_LSR_OE
)
icount
->
overrun
++
;
/*
* Mask off conditions which should be ignored.
*/
*
status
&=
info
->
read_status_mask
;
#ifdef CONFIG_SERIAL_CONSOLE
if
(
info
->
line
==
cons
.
index
)
{
/* Recover the break flag from console xmit */
*
status
|=
lsr_break_flag
;
lsr_break_flag
=
0
;
}
#endif
if
(
*
status
&
(
UART_LSR_BI
))
{
#ifdef SERIAL_DEBUG_INTR
printk
(
"handling break...."
);
#endif
*
tty
->
flip
.
flag_buf_ptr
=
TTY_BREAK
;
}
else
if
(
*
status
&
UART_LSR_PE
)
*
tty
->
flip
.
flag_buf_ptr
=
TTY_PARITY
;
else
if
(
*
status
&
UART_LSR_FE
)
*
tty
->
flip
.
flag_buf_ptr
=
TTY_FRAME
;
}
#if defined(CONFIG_SERIAL_CONSOLE) && defined(CONFIG_MAGIC_SYSRQ)
if
(
break_pressed
&&
info
->
line
==
cons
.
index
)
{
if
(
ch
!=
0
&&
time_before
(
jiffies
,
break_pressed
+
HZ
*
5
))
{
handle_sysrq
(
ch
,
regs
,
NULL
,
NULL
);
break_pressed
=
0
;
goto
ignore_char
;
}
break_pressed
=
0
;
}
#endif
if
((
*
status
&
info
->
ignore_status_mask
)
==
0
)
{
tty
->
flip
.
flag_buf_ptr
++
;
tty
->
flip
.
char_buf_ptr
++
;
tty
->
flip
.
count
++
;
}
#if defined(CONFIG_SERIAL_CONSOLE) && defined(CONFIG_MAGIC_SYSRQ)
ignore_char:
#endif
*
status
=
serial_inp
(
info
,
UART_LSR
);
}
while
((
*
status
&
UART_LSR_DR
)
&&
(
max_count
--
>
0
));
#if (LINUX_VERSION_CODE > 131394)
/* 2.1.66 */
tty_flip_buffer_push
(
tty
);
#else
queue_task_irq_off
(
&
tty
->
flip
.
tqueue
,
&
tq_timer
);
#endif
}
static
void
transmit_chars
(
struct
async_struct
*
info
,
int
*
intr_done
)
{
int
count
;
if
(
info
->
x_char
)
{
// for M32102 serial
// serial_outp(info, UART_TX, info->x_char);
info
->
state
->
icount
.
tx
++
;
info
->
x_char
=
0
;
if
(
intr_done
)
*
intr_done
=
0
;
while
((
serial_in
(
info
,
UART_LSR
)
&
UART_LSR_TEMT
)
==
0
);
return
;
}
if
(
info
->
xmit
.
head
==
info
->
xmit
.
tail
||
info
->
tty
->
stopped
||
info
->
tty
->
hw_stopped
)
{
info
->
IER
&=
~
UART_IER_THRI
;
serial_out
(
info
,
UART_IER
,
info
->
IER
);
return
;
}
count
=
info
->
xmit_fifo_size
;
count
=
SERIAL_XMIT_SIZE
-
1
;
do
{
serial_out
(
info
,
UART_TX
,
info
->
xmit
.
buf
[
info
->
xmit
.
tail
]);
info
->
xmit
.
tail
=
(
info
->
xmit
.
tail
+
1
)
&
(
SERIAL_XMIT_SIZE
-
1
);
info
->
state
->
icount
.
tx
++
;
if
(
info
->
xmit
.
head
==
info
->
xmit
.
tail
)
break
;
while
((
serial_in
(
info
,
UART_LSR
)
&
UART_LSR_THRE
)
==
0
);
}
while
(
--
count
>
0
);
while
((
serial_in
(
info
,
UART_LSR
)
&
UART_LSR_TEMT
)
==
0
);
if
(
CIRC_CNT
(
info
->
xmit
.
head
,
info
->
xmit
.
tail
,
SERIAL_XMIT_SIZE
)
<
WAKEUP_CHARS
)
psio_sched_event
(
info
,
RS_EVENT_WRITE_WAKEUP
);
#ifdef SERIAL_DEBUG_INTR
printk
(
"THRE..."
);
#endif
if
(
intr_done
)
*
intr_done
=
0
;
if
(
info
->
xmit
.
head
==
info
->
xmit
.
tail
)
{
info
->
IER
&=
~
UART_IER_THRI
;
serial_out
(
info
,
UART_IER
,
info
->
IER
);
}
}
/*
#ifdef CONFIG_SERIAL_SHARE_IRQ
static void rs_interrupt(int irq, void *dev_id, struct pt_regs * regs)
#endif
*/
static
void
sio_reset
(
struct
async_struct
*
info
)
{
unsigned
int
dummy
;
/* reset sio */
/* read receive buffer */
dummy
=
serial_inp
(
info
,
UART_RX
);
dummy
=
serial_inp
(
info
,
UART_RX
);
dummy
=
serial_inp
(
info
,
UART_LSR
);
serial_outp
(
info
,
UART_LCR
,
0x0300
);
/* RSCLR:1, TSCLR:1 */
serial_outp
(
info
,
UART_LCR
,
0x0003
);
/* RSEN:1, TXEN:1 */
}
static
void
sio_error
(
struct
async_struct
*
info
,
int
status
)
{
unsigned
int
dummy
;
/* reset sio */
printk
(
"sio[%d] error[%04x]
\n
"
,
info
->
line
,
status
);
/* read receive buffer */
dummy
=
serial_inp
(
info
,
UART_RX
);
dummy
=
serial_inp
(
info
,
UART_RX
);
dummy
=
serial_inp
(
info
,
UART_LSR
);
serial_outp
(
info
,
UART_LCR
,
0x0300
);
/* RSCLR:1, TSCLR:1 */
serial_outp
(
info
,
UART_LCR
,
0x0003
);
/* RSEN:1, TXEN:1 */
}
//static void psio_interrupt_single(int irq, void *dev_id, struct pt_regs * regs)
void
psio_interrupt_single
(
int
irq
,
void
*
dev_id
,
struct
pt_regs
*
regs
)
{
int
status
;
// int pass_counter = 0;
struct
async_struct
*
info
;
#ifdef CONFIG_SERIAL_MULTIPORT
int
first_multi
=
0
;
struct
rs_multiport_struct
*
multi
;
#endif
#ifdef SERIAL_DEBUG_INTR
printk
(
"psio_interrupt_single(%d)..."
,
irq
);
#endif
info
=
IRQ_ports
[
irq
&
(
~
1
)];
if
(
!
info
||
!
info
->
tty
)
return
;
#ifdef CONFIG_SERIAL_MULTIPORT
multi
=
&
rs_multiport
[
irq
];
if
(
multi
->
port_monitor
)
first_multi
=
inb
(
multi
->
port_monitor
);
#endif
{
status
=
serial_inp
(
info
,
UART_LSR
);
#ifdef SERIAL_DEBUG_INTR
printk
(
"status = %x..."
,
status
);
#endif
if
(
status
&
UART_LSR_DR
){
psio_receive_chars
(
info
,
&
status
);
}
if
((
serial_in
(
info
,
UART_LSR
)
&
UART_EMPTY
)
!=
UART_EMPTY
)
sio_error
(
info
,
status
);
if
(
status
&
UART_LSR_THRE
)
transmit_chars
(
info
,
0
);
#ifdef SERIAL_DEBUG_INTR
printk
(
"IIR = %x..."
,
serial_in
(
info
,
UART_IIR
));
#endif
}
info
->
last_active
=
jiffies
;
#ifdef CONFIG_SERIAL_MULTIPORT
if
(
multi
->
port_monitor
)
printk
(
"rs port monitor (single) irq %d: 0x%x, 0x%x
\n
"
,
info
->
state
->
irq
,
first_multi
,
inb
(
multi
->
port_monitor
));
#endif
#ifdef SERIAL_DEBUG_INTR
printk
(
"end.
\n
"
);
#endif
}
#ifdef CONFIG_SERIAL_MULTIPORT
static
void
rs_interrupt_multi
(
int
irq
,
void
*
dev_id
,
struct
pt_regs
*
regs
)
{}
#endif
static
void
do_psio_serial_bh
(
void
)
{
run_task_queue
(
&
tq_psio_serial
);
}
static
void
do_softint
(
void
*
private_
)
{
struct
async_struct
*
info
=
(
struct
async_struct
*
)
private_
;
struct
tty_struct
*
tty
;
tty
=
info
->
tty
;
if
(
!
tty
)
return
;
if
(
test_and_clear_bit
(
RS_EVENT_WRITE_WAKEUP
,
&
info
->
event
))
{
if
((
tty
->
flags
&
(
1
<<
TTY_DO_WRITE_WAKEUP
))
&&
tty
->
ldisc
.
write_wakeup
)
(
tty
->
ldisc
.
write_wakeup
)(
tty
);
wake_up_interruptible
(
&
tty
->
write_wait
);
#ifdef SERIAL_HAVE_POLL_WAIT
wake_up_interruptible
(
&
tty
->
poll_wait
);
#endif
}
}
static
void
psio_timer
(
void
)
{
static
unsigned
long
last_strobe
=
0
;
struct
async_struct
*
info
;
unsigned
int
i
;
unsigned
long
flags
;
if
((
jiffies
-
last_strobe
)
>=
RS_STROBE_TIME
)
{
for
(
i
=
0
;
i
<
NR_IRQS
;
i
++
)
{
info
=
IRQ_ports
[
i
];
if
(
!
info
)
continue
;
save_flags
(
flags
);
cli
();
psio_interrupt_single
(
i
,
NULL
,
NULL
);
restore_flags
(
flags
);
}
}
last_strobe
=
jiffies
;
#if 1
mod_timer
(
&
serial_timer
,
jiffies
+
10
);
#else
mod_timer
(
&
serial_timer
,
jiffies
+
RS_STROBE_TIME
);
#endif
if
(
IRQ_ports
[
0
])
{
save_flags
(
flags
);
cli
();
#ifdef CONFIG_SERIAL_SHARE_IRQ
psio_interrupt
(
0
,
NULL
,
NULL
);
#else
psio_interrupt_single
(
0
,
NULL
,
NULL
);
#endif
restore_flags
(
flags
);
mod_timer
(
&
serial_timer
,
jiffies
+
IRQ_timeout
[
0
]);
}
}
/*
* ---------------------------------------------------------------
* Low level utility subroutines for the serial driver: routines to
* figure out the appropriate timeout for an interrupt chain, routines
* to initialize and startup a serial port, and routines to shutdown a
* serial port. Useful stuff like that.
* ---------------------------------------------------------------
*/
/*
* This routine figures out the correct timeout for a particular IRQ.
* It uses the smallest timeout of all of the serial ports in a
* particular interrupt chain. Now only used for IRQ 0....
*/
static
void
figure_IRQ_timeout
(
int
irq
)
{
struct
async_struct
*
info
;
int
timeout
=
60
*
HZ
;
/* 60 seconds === a long time :-) */
info
=
IRQ_ports
[
irq
];
if
(
!
info
)
{
IRQ_timeout
[
irq
]
=
60
*
HZ
;
return
;
}
while
(
info
)
{
if
(
info
->
timeout
<
timeout
)
timeout
=
info
->
timeout
;
info
=
info
->
next_port
;
}
if
(
!
irq
)
timeout
=
timeout
/
2
;
IRQ_timeout
[
irq
]
=
timeout
?
timeout
:
1
;
}
#ifdef CONFIG_SERIAL_RSA
/* Attempts to turn on the RSA FIFO. Returns zero on failure */
static
int
enable_rsa
(
struct
async_struct
*
info
)
{}
/* Attempts to turn off the RSA FIFO. Returns zero on failure */
static
int
disable_rsa
(
struct
async_struct
*
info
)
{
}
#endif
/* CONFIG_SERIAL_RSA */
static
int
startup
(
struct
async_struct
*
info
)
{
unsigned
long
flags
;
int
retval
=
0
;
void
(
*
handler
)(
int
,
void
*
,
struct
pt_regs
*
);
struct
serial_state
*
state
=
info
->
state
;
unsigned
long
page
;
#ifdef CONFIG_SERIAL_MANY_PORTS
unsigned
short
ICP
;
#endif
page
=
get_zeroed_page
(
GFP_KERNEL
);
if
(
!
page
)
return
-
ENOMEM
;
save_flags
(
flags
);
cli
();
if
(
info
->
flags
&
ASYNC_INITIALIZED
)
{
free_page
(
page
);
goto
errout
;
}
if
(
info
->
xmit
.
buf
)
free_page
(
page
);
else
info
->
xmit
.
buf
=
(
unsigned
char
*
)
page
;
#ifdef SERIAL_DEBUG_OPEN
printk
(
"starting up ttyD%d (irq %d)..."
,
info
->
line
,
state
->
irq
);
#endif
/*
* Clear the FIFO buffers and disable them
* (they will be reenabled in change_speed())
*/
/*
* Clear the interrupt registers.
*/
sio_reset
(
info
);
/*
* Allocate the IRQ if necessary
*/
if
(
state
->
irq
&&
(
!
IRQ_ports
[
state
->
irq
]
||
!
IRQ_ports
[
state
->
irq
]
->
next_port
))
{
if
(
IRQ_ports
[
state
->
irq
])
{
#ifdef CONFIG_SERIAL_SHARE_IRQ
free_irq
(
state
->
irq
,
&
IRQ_ports
[
state
->
irq
]);
free_irq
(
state
->
irq
+
1
,
&
IRQ_ports
[
state
->
irq
]);
#ifdef CONFIG_SERIAL_MULTIPORT
if
(
rs_multiport
[
state
->
irq
].
port1
)
handler
=
rs_interrupt_multi
;
else
#endif
handler
=
psio_interrupt
;
#else
retval
=
-
EBUSY
;
goto
errout
;
#endif
/* CONFIG_SERIAL_SHARE_IRQ */
}
else
handler
=
psio_interrupt_single
;
/* 020116 */
retval
=
request_irq
(
state
->
irq
,
handler
,
SA_SHIRQ
,
"serial_rx"
,
&
IRQ_ports
[
state
->
irq
]);
retval
=
request_irq
(
state
->
irq
+
1
,
handler
,
SA_SHIRQ
,
"serial_tx"
,
&
IRQ_ports
[
state
->
irq
]);
if
(
retval
)
{
if
(
capable
(
CAP_SYS_ADMIN
))
{
if
(
info
->
tty
)
set_bit
(
TTY_IO_ERROR
,
&
info
->
tty
->
flags
);
retval
=
0
;
}
goto
errout
;
}
}
/*
* Insert serial port into IRQ chain.
*/
info
->
prev_port
=
0
;
info
->
next_port
=
IRQ_ports
[
state
->
irq
];
if
(
info
->
next_port
)
info
->
next_port
->
prev_port
=
info
;
IRQ_ports
[
state
->
irq
]
=
info
;
figure_IRQ_timeout
(
state
->
irq
);
/*
* Now, initialize the UART
*/
/* for m32r @020113 */
sio_reset
(
info
);
info
->
MCR
=
0
;
if
(
info
->
tty
->
termios
->
c_cflag
&
CBAUD
)
info
->
MCR
=
UART_MCR_DTR
|
UART_MCR_RTS
;
#ifdef CONFIG_SERIAL_MANY_PORTS
if
(
info
->
flags
&
ASYNC_FOURPORT
)
{
if
(
state
->
irq
==
0
)
info
->
MCR
|=
UART_MCR_OUT1
;
}
else
#endif
{
if
(
state
->
irq
!=
0
)
info
->
MCR
|=
UART_MCR_OUT2
;
}
info
->
MCR
|=
ALPHA_KLUDGE_MCR
;
/* Don't ask */
serial_outp
(
info
,
UART_MCR
,
info
->
MCR
);
/*
* Finally, enable interrupts
*/
info
->
IER
=
UART_IER_MSI
|
UART_IER_RLSI
|
UART_IER_RDI
;
serial_outp
(
info
,
UART_IER
,
info
->
IER
);
/* enable interrupts */
#ifdef CONFIG_SERIAL_MANY_PORTS
if
(
info
->
flags
&
ASYNC_FOURPORT
)
{
/* Enable interrupts on the AST Fourport board */
ICP
=
(
info
->
port
&
0xFE0
)
|
0x01F
;
outb_p
(
0x80
,
ICP
);
(
void
)
inb_p
(
ICP
);
}
#endif
/*
* And clear the interrupt registers again for luck.
*/
(
void
)
serial_inp
(
info
,
UART_LSR
);
(
void
)
serial_inp
(
info
,
UART_RX
);
(
void
)
serial_inp
(
info
,
UART_IIR
);
(
void
)
serial_inp
(
info
,
UART_MSR
);
if
(
info
->
tty
)
clear_bit
(
TTY_IO_ERROR
,
&
info
->
tty
->
flags
);
info
->
xmit
.
head
=
info
->
xmit
.
tail
=
0
;
/*
* Set up serial timers...
*/
mod_timer
(
&
serial_timer
,
jiffies
+
2
*
HZ
/
100
);
/*
* Set up the tty->alt_speed kludge
*/
#if (LINUX_VERSION_CODE >= 131394)
/* Linux 2.1.66 */
if
(
info
->
tty
)
{
if
((
info
->
flags
&
ASYNC_SPD_MASK
)
==
ASYNC_SPD_HI
)
info
->
tty
->
alt_speed
=
57600
;
if
((
info
->
flags
&
ASYNC_SPD_MASK
)
==
ASYNC_SPD_VHI
)
info
->
tty
->
alt_speed
=
115200
;
if
((
info
->
flags
&
ASYNC_SPD_MASK
)
==
ASYNC_SPD_SHI
)
info
->
tty
->
alt_speed
=
230400
;
if
((
info
->
flags
&
ASYNC_SPD_MASK
)
==
ASYNC_SPD_WARP
)
info
->
tty
->
alt_speed
=
460800
;
}
#endif
/*
* and set the speed of the serial port
*/
change_speed
(
info
,
0
);
info
->
flags
|=
ASYNC_INITIALIZED
;
restore_flags
(
flags
);
return
0
;
errout:
restore_flags
(
flags
);
return
retval
;
}
/*
* This routine will shutdown a serial port; interrupts are disabled, and
* DTR is dropped if the hangup on close termio flag is on.
*/
static
void
shutdown
(
struct
async_struct
*
info
)
{
unsigned
long
flags
;
struct
serial_state
*
state
;
int
retval
;
if
(
!
(
info
->
flags
&
ASYNC_INITIALIZED
))
return
;
state
=
info
->
state
;
#ifdef SERIAL_DEBUG_OPEN
printk
(
"Shutting down serial port %d (irq %d)...."
,
info
->
line
,
state
->
irq
);
#endif
save_flags
(
flags
);
cli
();
/* Disable interrupts */
/*
* clear delta_msr_wait queue to avoid mem leaks: we may free the irq
* here so the queue might never be waken up
*/
wake_up_interruptible
(
&
info
->
delta_msr_wait
);
/*
* First unlink the serial port from the IRQ chain...
*/
if
(
info
->
next_port
)
info
->
next_port
->
prev_port
=
info
->
prev_port
;
if
(
info
->
prev_port
)
info
->
prev_port
->
next_port
=
info
->
next_port
;
else
IRQ_ports
[
state
->
irq
]
=
info
->
next_port
;
figure_IRQ_timeout
(
state
->
irq
);
/*
* Free the IRQ, if necessary
*/
if
(
state
->
irq
&&
(
!
IRQ_ports
[
state
->
irq
]
||
!
IRQ_ports
[
state
->
irq
]
->
next_port
))
{
if
(
IRQ_ports
[
state
->
irq
])
{
/* 020116 */
free_irq
(
state
->
irq
+
1
,
&
IRQ_ports
[
state
->
irq
]);
retval
=
request_irq
(
state
->
irq
+
1
,
psio_interrupt_single
,
SA_SHIRQ
,
"serial_xx"
,
&
IRQ_ports
[
state
->
irq
]);
free_irq
(
state
->
irq
,
&
IRQ_ports
[
state
->
irq
]);
retval
=
request_irq
(
state
->
irq
,
psio_interrupt_single
,
SA_SHIRQ
,
"serial"
,
&
IRQ_ports
[
state
->
irq
]);
if
(
retval
)
printk
(
"serial shutdown: request_irq: error %d"
" Couldn't reacquire IRQ.
\n
"
,
retval
);
}
else
{
free_irq
(
state
->
irq
,
&
IRQ_ports
[
state
->
irq
]);
/* 020116 */
free_irq
(
state
->
irq
+
1
,
&
IRQ_ports
[
state
->
irq
]);
}
}
if
(
info
->
xmit
.
buf
)
{
unsigned
long
pg
=
(
unsigned
long
)
info
->
xmit
.
buf
;
info
->
xmit
.
buf
=
0
;
free_page
(
pg
);
}
info
->
IER
=
0
;
serial_outp
(
info
,
UART_IER
,
0x00
);
/* disable all intrs */
#ifdef CONFIG_SERIAL_MANY_PORTS
if
(
info
->
flags
&
ASYNC_FOURPORT
)
{
/* reset interrupts on the AST Fourport board */
(
void
)
inb
((
info
->
port
&
0xFE0
)
|
0x01F
);
info
->
MCR
|=
UART_MCR_OUT1
;
}
else
#endif
if
(
!
info
->
tty
||
(
info
->
tty
->
termios
->
c_cflag
&
HUPCL
))
info
->
MCR
&=
~
(
UART_MCR_DTR
|
UART_MCR_RTS
);
serial_outp
(
info
,
UART_MCR
,
info
->
MCR
);
#ifdef CONFIG_SERIAL_RSA
/*
* Reset the RSA board back to 115kbps compat mode.
*/
if
((
state
->
type
==
PORT_RSA
)
&&
(
state
->
baud_base
==
SERIAL_RSA_BAUD_BASE
&&
disable_rsa
(
info
)))
state
->
baud_base
=
SERIAL_RSA_BAUD_BASE_LO
;
#endif
(
void
)
serial_in
(
info
,
UART_RX
);
/* read data port to reset things */
if
(
info
->
tty
)
set_bit
(
TTY_IO_ERROR
,
&
info
->
tty
->
flags
);
sio_reset
(
info
);
info
->
flags
&=
~
ASYNC_INITIALIZED
;
restore_flags
(
flags
);
}
static
void
change_speed
(
struct
async_struct
*
info
,
struct
termios
*
old_termios
)
{
int
quot
=
0
,
baud_base
,
baud
;
unsigned
cflag
,
cval
=
0
;
int
bits
;
unsigned
long
flags
;
unsigned
mod0
,
mod1
;
if
(
!
info
->
tty
||
!
info
->
tty
->
termios
)
return
;
cflag
=
info
->
tty
->
termios
->
c_cflag
;
if
(
!
CONFIGURED_SERIAL_PORT
(
info
))
return
;
/* byte size and parity */
switch
(
cflag
&
CSIZE
)
{
case
CS5
:
mod1
=
0x05
;
bits
=
7
;
break
;
case
CS6
:
mod1
=
0x06
;
bits
=
8
;
break
;
case
CS7
:
mod1
=
0x07
;
bits
=
9
;
break
;
case
CS8
:
mod1
=
0x08
;
bits
=
10
;
break
;
/* Never happens, but GCC is too dumb to figure it out */
default:
mod1
=
0x05
;
bits
=
7
;
break
;
}
mod1
<<=
8
;
mod0
=
0
;
if
(
cflag
&
CSTOPB
)
{
mod0
|=
0x03
;
bits
++
;
}
if
(
cflag
&
PARENB
)
{
mod0
|=
0x10
;
bits
++
;
}
if
(
!
(
cflag
&
PARODD
))
{
mod0
|=
0x4
;
}
mod0
=
0x80
;
/* Use RTS# output only */
serial_outp
(
info
,
UART_MOD0
,
mod0
);
/* */
//serial_outp(info, UART_MOD1, mod1);
//mod1 = 0;
info
->
LCR
=
mod1
;
/* Save LCR */
/* Determine divisor based on baud rate */
baud
=
tty_get_baud_rate
(
info
->
tty
);
if
(
!
baud
)
baud
=
9600
;
/* B0 transition handled in rs_set_termios */
#ifdef CONFIG_SERIAL_RSA
if
((
info
->
state
->
type
==
PORT_RSA
)
&&
(
info
->
state
->
baud_base
!=
SERIAL_RSA_BAUD_BASE
)
&&
enable_rsa
(
info
))
info
->
state
->
baud_base
=
SERIAL_RSA_BAUD_BASE
;
#endif
baud_base
=
info
->
state
->
baud_base
;
if
(
baud
==
38400
&&
((
info
->
flags
&
ASYNC_SPD_MASK
)
==
ASYNC_SPD_CUST
))
quot
=
info
->
state
->
custom_divisor
;
else
{
if
(
baud
==
134
)
/* Special case since 134 is really 134.5 */
quot
=
(
2
*
baud_base
/
269
);
else
if
(
baud
)
quot
=
baud_base
/
baud
;
}
/* If the quotient is zero refuse the change */
if
(
!
quot
&&
old_termios
)
{
info
->
tty
->
termios
->
c_cflag
&=
~
CBAUD
;
info
->
tty
->
termios
->
c_cflag
|=
(
old_termios
->
c_cflag
&
CBAUD
);
baud
=
tty_get_baud_rate
(
info
->
tty
);
if
(
!
baud
)
baud
=
9600
;
if
(
baud
==
38400
&&
((
info
->
flags
&
ASYNC_SPD_MASK
)
==
ASYNC_SPD_CUST
))
quot
=
info
->
state
->
custom_divisor
;
else
{
if
(
baud
==
134
)
/* Special case since 134 is really 134.5 */
quot
=
(
2
*
baud_base
/
269
);
else
if
(
baud
)
quot
=
baud_base
/
baud
;
}
}
quot
=
baud_base
/
(
baud
*
4
)
;
/* As a last resort, if the quotient is zero, default to 9600 bps */
if
(
!
quot
)
quot
=
baud_base
/
9600
;
/*
* Work around a bug in the Oxford Semiconductor 952 rev B
* chip which causes it to seriously miscalculate baud rates
* when DLL is 0.
*/
if
(((
quot
&
0xFF
)
==
0
)
&&
(
info
->
state
->
type
==
PORT_16C950
)
&&
(
info
->
state
->
revision
==
0x5201
))
quot
++
;
info
->
quot
=
quot
;
info
->
timeout
=
((
info
->
xmit_fifo_size
*
HZ
*
bits
*
quot
)
/
baud_base
);
info
->
timeout
+=
HZ
/
50
;
/* Add .02 seconds of slop */
/* CTS flow control flag and modem status interrupts */
info
->
IER
&=
~
UART_IER_MSI
;
if
(
info
->
flags
&
ASYNC_HARDPPS_CD
)
info
->
IER
|=
UART_IER_MSI
;
if
(
cflag
&
CRTSCTS
)
{
info
->
flags
|=
ASYNC_CTS_FLOW
;
info
->
IER
|=
UART_IER_MSI
;
}
else
info
->
flags
&=
~
ASYNC_CTS_FLOW
;
if
(
cflag
&
CLOCAL
)
info
->
flags
&=
~
ASYNC_CHECK_CD
;
else
{
info
->
flags
|=
ASYNC_CHECK_CD
;
info
->
IER
|=
UART_IER_MSI
;
}
serial_out
(
info
,
UART_IER
,
info
->
IER
);
/*
* Set up parity check flag
*/
#define RELEVANT_IFLAG(iflag) (iflag & (IGNBRK|BRKINT|IGNPAR|PARMRK|INPCK))
info
->
read_status_mask
=
UART_LSR_OE
|
UART_LSR_THRE
|
UART_LSR_DR
;
if
(
I_INPCK
(
info
->
tty
))
info
->
read_status_mask
|=
UART_LSR_FE
|
UART_LSR_PE
;
if
(
I_BRKINT
(
info
->
tty
)
||
I_PARMRK
(
info
->
tty
))
info
->
read_status_mask
|=
UART_LSR_BI
;
/*
* Characters to ignore
*/
info
->
ignore_status_mask
=
0
;
if
(
I_IGNPAR
(
info
->
tty
))
info
->
ignore_status_mask
|=
UART_LSR_PE
|
UART_LSR_FE
;
if
(
I_IGNBRK
(
info
->
tty
))
{
info
->
ignore_status_mask
|=
UART_LSR_BI
;
/*
* If we're ignore parity and break indicators, ignore
* overruns too. (For real raw support).
*/
if
(
I_IGNPAR
(
info
->
tty
))
info
->
ignore_status_mask
|=
UART_LSR_OE
;
}
/*
* !!! ignore all characters if CREAD is not set
*/
if
((
cflag
&
CREAD
)
==
0
)
info
->
ignore_status_mask
|=
UART_LSR_DR
;
cval
=
(
baud_base
/
(
baud
*
4
))
-
1
;
save_flags
(
flags
);
cli
();
serial_outp
(
info
,
UART_BAUR
,
cval
);
/* set baurate reg */
serial_outp
(
info
,
UART_LCR
,
0x03
);
restore_flags
(
flags
);
}
static
void
psio_put_char
(
struct
tty_struct
*
tty
,
unsigned
char
ch
)
{
struct
async_struct
*
info
=
(
struct
async_struct
*
)
tty
->
driver_data
;
unsigned
long
flags
;
if
(
serial_paranoia_check
(
info
,
tty
->
device
,
"psio_put_char"
))
return
;
if
(
!
tty
||
!
info
->
xmit
.
buf
)
return
;
save_flags
(
flags
);
cli
();
if
(
CIRC_SPACE
(
info
->
xmit
.
head
,
info
->
xmit
.
tail
,
SERIAL_XMIT_SIZE
)
==
0
)
{
restore_flags
(
flags
);
return
;
}
info
->
xmit
.
buf
[
info
->
xmit
.
head
]
=
ch
;
info
->
xmit
.
head
=
(
info
->
xmit
.
head
+
1
)
&
(
SERIAL_XMIT_SIZE
-
1
);
restore_flags
(
flags
);
}
static
void
psio_flush_chars
(
struct
tty_struct
*
tty
)
{
struct
async_struct
*
info
=
(
struct
async_struct
*
)
tty
->
driver_data
;
unsigned
long
flags
;
if
(
serial_paranoia_check
(
info
,
tty
->
device
,
"psio_flush_chars"
))
return
;
if
(
info
->
xmit
.
head
==
info
->
xmit
.
tail
||
tty
->
stopped
||
tty
->
hw_stopped
||
!
info
->
xmit
.
buf
)
return
;
save_flags
(
flags
);
cli
();
if
(
!
(
info
->
IER
&
UART_IER_THRI
))
{
info
->
IER
|=
UART_IER_THRI
;
serial_out
(
info
,
UART_IER
,
info
->
IER
);
serial_out
(
info
,
UART_TX
,
info
->
xmit
.
buf
[
info
->
xmit
.
tail
]);
info
->
xmit
.
tail
=
(
info
->
xmit
.
tail
+
1
)
&
(
SERIAL_XMIT_SIZE
-
1
);
info
->
state
->
icount
.
tx
++
;
}
restore_flags
(
flags
);
while
((
serial_in
(
info
,
UART_LSR
)
&
UART_EMPTY
)
!=
UART_EMPTY
);
}
static
int
psio_write
(
struct
tty_struct
*
tty
,
int
from_user
,
const
unsigned
char
*
buf
,
int
count
)
{
int
c
,
ret
=
0
;
struct
async_struct
*
info
=
(
struct
async_struct
*
)
tty
->
driver_data
;
unsigned
long
flags
;
if
(
serial_paranoia_check
(
info
,
tty
->
device
,
"psio_write"
))
return
0
;
if
(
!
tty
||
!
info
->
xmit
.
buf
||
!
tmp_buf
)
return
0
;
save_flags
(
flags
);
if
(
from_user
)
{
down
(
&
tmp_buf_sem
);
while
(
1
)
{
int
c1
;
c
=
CIRC_SPACE_TO_END
(
info
->
xmit
.
head
,
info
->
xmit
.
tail
,
SERIAL_XMIT_SIZE
);
if
(
count
<
c
)
c
=
count
;
if
(
c
<=
0
)
break
;
c
-=
copy_from_user
(
tmp_buf
,
buf
,
c
);
if
(
!
c
)
{
if
(
!
ret
)
ret
=
-
EFAULT
;
break
;
}
cli
();
c1
=
CIRC_SPACE_TO_END
(
info
->
xmit
.
head
,
info
->
xmit
.
tail
,
SERIAL_XMIT_SIZE
);
if
(
c1
<
c
)
c
=
c1
;
memcpy
(
info
->
xmit
.
buf
+
info
->
xmit
.
head
,
tmp_buf
,
c
);
info
->
xmit
.
head
=
((
info
->
xmit
.
head
+
c
)
&
(
SERIAL_XMIT_SIZE
-
1
));
restore_flags
(
flags
);
buf
+=
c
;
count
-=
c
;
ret
+=
c
;
}
up
(
&
tmp_buf_sem
);
}
else
{
cli
();
while
(
1
)
{
c
=
CIRC_SPACE_TO_END
(
info
->
xmit
.
head
,
info
->
xmit
.
tail
,
SERIAL_XMIT_SIZE
);
if
(
count
<
c
)
c
=
count
;
if
(
c
<=
0
)
{
break
;
}
memcpy
(
info
->
xmit
.
buf
+
info
->
xmit
.
head
,
buf
,
c
);
info
->
xmit
.
head
=
((
info
->
xmit
.
head
+
c
)
&
(
SERIAL_XMIT_SIZE
-
1
));
buf
+=
c
;
count
-=
c
;
ret
+=
c
;
}
restore_flags
(
flags
);
}
save_flags
(
flags
);
cli
();
if
(
info
->
xmit
.
head
!=
info
->
xmit
.
tail
&&
!
tty
->
stopped
&&
!
tty
->
hw_stopped
&&
!
(
info
->
IER
&
UART_IER_THRI
))
{
info
->
IER
|=
UART_IER_THRI
;
serial_out
(
info
,
UART_IER
,
info
->
IER
);
serial_out
(
info
,
UART_TX
,
info
->
xmit
.
buf
[
info
->
xmit
.
tail
]);
info
->
xmit
.
tail
=
(
info
->
xmit
.
tail
+
1
)
&
(
SERIAL_XMIT_SIZE
-
1
);
info
->
state
->
icount
.
tx
++
;
}
restore_flags
(
flags
);
while
((
serial_in
(
info
,
UART_LSR
)
&
UART_EMPTY
)
!=
UART_EMPTY
);
return
ret
;
}
static
int
psio_write_room
(
struct
tty_struct
*
tty
)
{
struct
async_struct
*
info
=
(
struct
async_struct
*
)
tty
->
driver_data
;
if
(
serial_paranoia_check
(
info
,
tty
->
device
,
"psio_write_room"
))
return
0
;
return
CIRC_SPACE
(
info
->
xmit
.
head
,
info
->
xmit
.
tail
,
SERIAL_XMIT_SIZE
);
}
static
int
psio_chars_in_buffer
(
struct
tty_struct
*
tty
)
{
struct
async_struct
*
info
=
(
struct
async_struct
*
)
tty
->
driver_data
;
if
(
serial_paranoia_check
(
info
,
tty
->
device
,
"psio_chars_in_buffer"
))
return
0
;
return
CIRC_CNT
(
info
->
xmit
.
head
,
info
->
xmit
.
tail
,
SERIAL_XMIT_SIZE
);
}
static
void
psio_flush_buffer
(
struct
tty_struct
*
tty
)
{
struct
async_struct
*
info
=
(
struct
async_struct
*
)
tty
->
driver_data
;
unsigned
long
flags
;
if
(
serial_paranoia_check
(
info
,
tty
->
device
,
"psio_flush_buffer"
))
return
;
save_flags
(
flags
);
cli
();
info
->
xmit
.
head
=
info
->
xmit
.
tail
=
0
;
restore_flags
(
flags
);
wake_up_interruptible
(
&
tty
->
write_wait
);
#ifdef SERIAL_HAVE_POLL_WAIT
wake_up_interruptible
(
&
tty
->
poll_wait
);
#endif
if
((
tty
->
flags
&
(
1
<<
TTY_DO_WRITE_WAKEUP
))
&&
tty
->
ldisc
.
write_wakeup
)
(
tty
->
ldisc
.
write_wakeup
)(
tty
);
}
/*
* This function is used to send a high-priority XON/XOFF character to
* the device
*/
static
void
psio_send_xchar
(
struct
tty_struct
*
tty
,
char
ch
)
{
struct
async_struct
*
info
=
(
struct
async_struct
*
)
tty
->
driver_data
;
if
(
serial_paranoia_check
(
info
,
tty
->
device
,
"psio_send_char"
))
return
;
info
->
x_char
=
ch
;
if
(
ch
)
{
unsigned
long
flags
;
save_flags
(
flags
);
cli
();
if
(
!
(
info
->
IER
&
UART_IER_THRI
))
{
info
->
IER
|=
UART_IER_THRI
;
serial_out
(
info
,
UART_IER
,
info
->
IER
);
serial_out
(
info
,
UART_TX
,
info
->
x_char
);
}
restore_flags
(
flags
);
}
}
/*
* ------------------------------------------------------------
* rs_throttle()
*
* This routine is called by the upper-layer tty layer to signal that
* incoming characters should be throttled.
* ------------------------------------------------------------
*/
static
void
psio_throttle
(
struct
tty_struct
*
tty
)
{
struct
async_struct
*
info
=
(
struct
async_struct
*
)
tty
->
driver_data
;
unsigned
long
flags
;
#ifdef SERIAL_DEBUG_THROTTLE
char
buf
[
64
];
printk
(
"throttle %s: %d....
\n
"
,
tty_name
(
tty
,
buf
),
tty
->
ldisc
.
chars_in_buffer
(
tty
));
#endif
if
(
serial_paranoia_check
(
info
,
tty
->
device
,
"psio_throttle"
))
return
;
if
(
I_IXOFF
(
tty
))
psio_send_xchar
(
tty
,
STOP_CHAR
(
tty
));
if
(
tty
->
termios
->
c_cflag
&
CRTSCTS
)
info
->
MCR
&=
~
UART_MCR_RTS
;
save_flags
(
flags
);
cli
();
serial_out
(
info
,
UART_MCR
,
info
->
MCR
);
restore_flags
(
flags
);
}
static
void
psio_unthrottle
(
struct
tty_struct
*
tty
)
{
struct
async_struct
*
info
=
(
struct
async_struct
*
)
tty
->
driver_data
;
unsigned
long
flags
;
#ifdef SERIAL_DEBUG_THROTTLE
char
buf
[
64
];
printk
(
"unthrottle %s: %d....
\n
"
,
tty_name
(
tty
,
buf
),
tty
->
ldisc
.
chars_in_buffer
(
tty
));
#endif
if
(
serial_paranoia_check
(
info
,
tty
->
device
,
"psio_unthrottle"
))
return
;
if
(
I_IXOFF
(
tty
))
{
if
(
info
->
x_char
)
info
->
x_char
=
0
;
}
if
(
tty
->
termios
->
c_cflag
&
CRTSCTS
)
info
->
MCR
|=
UART_MCR_RTS
;
save_flags
(
flags
);
cli
();
serial_out
(
info
,
UART_MCR
,
info
->
MCR
);
restore_flags
(
flags
);
}
/*
* ------------------------------------------------------------
* rs_ioctl() and friends
* ------------------------------------------------------------
*/
static
int
get_serial_info
(
struct
async_struct
*
info
,
struct
serial_struct
*
retinfo
)
{
struct
serial_struct
tmp
;
struct
serial_state
*
state
=
info
->
state
;
if
(
!
retinfo
)
return
-
EFAULT
;
memset
(
&
tmp
,
0
,
sizeof
(
tmp
));
tmp
.
type
=
state
->
type
;
tmp
.
line
=
state
->
line
;
tmp
.
port
=
state
->
port
;
if
(
HIGH_BITS_OFFSET
)
tmp
.
port_high
=
state
->
port
>>
HIGH_BITS_OFFSET
;
else
tmp
.
port_high
=
0
;
tmp
.
irq
=
state
->
irq
;
tmp
.
flags
=
state
->
flags
;
tmp
.
xmit_fifo_size
=
state
->
xmit_fifo_size
;
tmp
.
baud_base
=
state
->
baud_base
;
tmp
.
close_delay
=
state
->
close_delay
;
tmp
.
closing_wait
=
state
->
closing_wait
;
tmp
.
custom_divisor
=
state
->
custom_divisor
;
tmp
.
hub6
=
state
->
hub6
;
tmp
.
io_type
=
state
->
io_type
;
if
(
copy_to_user
(
retinfo
,
&
tmp
,
sizeof
(
*
retinfo
)))
return
-
EFAULT
;
return
0
;
}
static
int
set_serial_info
(
struct
async_struct
*
info
,
struct
serial_struct
*
new_info
)
{
struct
serial_struct
new_serial
;
struct
serial_state
old_state
,
*
state
;
unsigned
int
i
,
change_irq
,
change_port
;
int
retval
=
0
;
unsigned
long
new_port
;
if
(
copy_from_user
(
&
new_serial
,
new_info
,
sizeof
(
new_serial
)))
return
-
EFAULT
;
state
=
info
->
state
;
old_state
=
*
state
;
new_port
=
new_serial
.
port
;
if
(
HIGH_BITS_OFFSET
)
new_port
+=
(
unsigned
long
)
new_serial
.
port_high
<<
HIGH_BITS_OFFSET
;
change_irq
=
new_serial
.
irq
!=
state
->
irq
;
change_port
=
(
new_port
!=
((
int
)
state
->
port
))
||
(
new_serial
.
hub6
!=
state
->
hub6
);
if
(
!
capable
(
CAP_SYS_ADMIN
))
{
if
(
change_irq
||
change_port
||
(
new_serial
.
baud_base
!=
state
->
baud_base
)
||
(
new_serial
.
type
!=
state
->
type
)
||
(
new_serial
.
close_delay
!=
state
->
close_delay
)
||
(
new_serial
.
xmit_fifo_size
!=
state
->
xmit_fifo_size
)
||
((
new_serial
.
flags
&
~
ASYNC_USR_MASK
)
!=
(
state
->
flags
&
~
ASYNC_USR_MASK
)))
return
-
EPERM
;
state
->
flags
=
((
state
->
flags
&
~
ASYNC_USR_MASK
)
|
(
new_serial
.
flags
&
ASYNC_USR_MASK
));
info
->
flags
=
((
info
->
flags
&
~
ASYNC_USR_MASK
)
|
(
new_serial
.
flags
&
ASYNC_USR_MASK
));
state
->
custom_divisor
=
new_serial
.
custom_divisor
;
goto
check_and_exit
;
}
new_serial
.
irq
=
irq_cannonicalize
(
new_serial
.
irq
);
if
((
new_serial
.
irq
>=
NR_IRQS
)
||
(
new_serial
.
irq
<
0
)
||
(
new_serial
.
baud_base
<
9600
)
||
(
new_serial
.
type
<
PORT_UNKNOWN
)
||
(
new_serial
.
type
>
PORT_MAX
)
||
(
new_serial
.
type
==
PORT_CIRRUS
)
||
(
new_serial
.
type
==
PORT_STARTECH
))
{
return
-
EINVAL
;
}
if
((
new_serial
.
type
!=
state
->
type
)
||
(
new_serial
.
xmit_fifo_size
<=
0
))
new_serial
.
xmit_fifo_size
=
uart_config
[
new_serial
.
type
].
dfl_xmit_fifo_size
;
/* Make sure address is not already in use */
if
(
new_serial
.
type
)
{
for
(
i
=
0
;
i
<
NR_PORTS
;
i
++
)
if
((
state
!=
&
rs_table
[
i
])
&&
(
rs_table
[
i
].
port
==
new_port
)
&&
rs_table
[
i
].
type
)
return
-
EADDRINUSE
;
}
if
((
change_port
||
change_irq
)
&&
(
state
->
count
>
1
))
return
-
EBUSY
;
/*
* OK, past this point, all the error checking has been done.
* At this point, we start making changes.....
*/
state
->
baud_base
=
new_serial
.
baud_base
;
state
->
flags
=
((
state
->
flags
&
~
ASYNC_FLAGS
)
|
(
new_serial
.
flags
&
ASYNC_FLAGS
));
info
->
flags
=
((
state
->
flags
&
~
ASYNC_INTERNAL_FLAGS
)
|
(
info
->
flags
&
ASYNC_INTERNAL_FLAGS
));
state
->
custom_divisor
=
new_serial
.
custom_divisor
;
state
->
close_delay
=
new_serial
.
close_delay
*
HZ
/
100
;
state
->
closing_wait
=
new_serial
.
closing_wait
*
HZ
/
100
;
#if (LINUX_VERSION_CODE > 0x20100)
info
->
tty
->
low_latency
=
(
info
->
flags
&
ASYNC_LOW_LATENCY
)
?
1
:
0
;
#endif
info
->
xmit_fifo_size
=
state
->
xmit_fifo_size
=
new_serial
.
xmit_fifo_size
;
if
((
state
->
type
!=
PORT_UNKNOWN
)
&&
state
->
port
)
{
#ifdef CONFIG_SERIAL_RSA
if
(
old_state
.
type
==
PORT_RSA
)
release_region
(
state
->
port
+
UART_RSA_BASE
,
16
);
else
#endif
release_region
(
state
->
port
,
8
);
}
state
->
type
=
new_serial
.
type
;
if
(
change_port
||
change_irq
)
{
/*
* We need to shutdown the serial port at the old
* port/irq combination.
*/
shutdown
(
info
);
state
->
irq
=
new_serial
.
irq
;
info
->
port
=
state
->
port
=
new_port
;
info
->
hub6
=
state
->
hub6
=
new_serial
.
hub6
;
if
(
info
->
hub6
)
info
->
io_type
=
state
->
io_type
=
SERIAL_IO_HUB6
;
else
if
(
info
->
io_type
==
SERIAL_IO_HUB6
)
info
->
io_type
=
state
->
io_type
=
SERIAL_IO_PORT
;
}
if
((
state
->
type
!=
PORT_UNKNOWN
)
&&
state
->
port
)
{
#ifdef CONFIG_SERIAL_RSA
if
(
state
->
type
==
PORT_RSA
)
request_region
(
state
->
port
+
UART_RSA_BASE
,
16
,
"serial_rsa(set)"
);
else
#endif
request_region
(
state
->
port
,
8
,
"serial(set)"
);
}
check_and_exit:
if
(
!
state
->
port
||
!
state
->
type
)
return
0
;
if
(
info
->
flags
&
ASYNC_INITIALIZED
)
{
if
(((
old_state
.
flags
&
ASYNC_SPD_MASK
)
!=
(
state
->
flags
&
ASYNC_SPD_MASK
))
||
(
old_state
.
custom_divisor
!=
state
->
custom_divisor
))
{
#if (LINUX_VERSION_CODE >= 131394)
/* Linux 2.1.66 */
if
((
state
->
flags
&
ASYNC_SPD_MASK
)
==
ASYNC_SPD_HI
)
info
->
tty
->
alt_speed
=
57600
;
if
((
state
->
flags
&
ASYNC_SPD_MASK
)
==
ASYNC_SPD_VHI
)
info
->
tty
->
alt_speed
=
115200
;
if
((
state
->
flags
&
ASYNC_SPD_MASK
)
==
ASYNC_SPD_SHI
)
info
->
tty
->
alt_speed
=
230400
;
if
((
state
->
flags
&
ASYNC_SPD_MASK
)
==
ASYNC_SPD_WARP
)
info
->
tty
->
alt_speed
=
460800
;
#endif
change_speed
(
info
,
0
);
}
}
else
retval
=
startup
(
info
);
return
retval
;
}
/*
* get_lsr_info - get line status register info
*
* Purpose: Let user call ioctl() to get info when the UART physically
* is emptied. On bus types like RS485, the transmitter must
* release the bus after transmitting. This must be done when
* the transmit shift register is empty, not be done when the
* transmit holding register is empty. This functionality
* allows an RS485 driver to be written in user space.
*/
static
int
get_lsr_info
(
struct
async_struct
*
info
,
unsigned
int
*
value
)
{
unsigned
char
status
;
unsigned
int
result
;
unsigned
long
flags
;
save_flags
(
flags
);
cli
();
status
=
serial_in
(
info
,
UART_LSR
);
restore_flags
(
flags
);
result
=
((
status
&
UART_LSR_TEMT
)
?
TIOCSER_TEMT
:
0
);
/*
* If we're about to load something into the transmit
* register, we'll pretend the transmitter isn't empty to
* avoid a race condition (depending on when the transmit
* interrupt happens).
*/
if
(
info
->
x_char
||
((
CIRC_CNT
(
info
->
xmit
.
head
,
info
->
xmit
.
tail
,
SERIAL_XMIT_SIZE
)
>
0
)
&&
!
info
->
tty
->
stopped
&&
!
info
->
tty
->
hw_stopped
))
result
&=
~
TIOCSER_TEMT
;
if
(
copy_to_user
(
value
,
&
result
,
sizeof
(
int
)))
return
-
EFAULT
;
return
0
;
}
static
int
get_modem_info
(
struct
async_struct
*
info
,
unsigned
int
*
value
)
{
unsigned
char
control
,
status
;
unsigned
int
result
;
unsigned
long
flags
;
control
=
info
->
MCR
;
save_flags
(
flags
);
cli
();
status
=
serial_in
(
info
,
UART_MSR
);
restore_flags
(
flags
);
result
=
((
control
&
UART_MCR_RTS
)
?
TIOCM_RTS
:
0
)
|
((
control
&
UART_MCR_DTR
)
?
TIOCM_DTR
:
0
)
#ifdef TIOCM_OUT1
|
((
control
&
UART_MCR_OUT1
)
?
TIOCM_OUT1
:
0
)
|
((
control
&
UART_MCR_OUT2
)
?
TIOCM_OUT2
:
0
)
#endif
|
((
status
&
UART_MSR_DCD
)
?
TIOCM_CAR
:
0
)
|
((
status
&
UART_MSR_RI
)
?
TIOCM_RNG
:
0
)
|
((
status
&
UART_MSR_DSR
)
?
TIOCM_DSR
:
0
)
|
((
status
&
UART_MSR_CTS
)
?
TIOCM_CTS
:
0
);
if
(
copy_to_user
(
value
,
&
result
,
sizeof
(
int
)))
return
-
EFAULT
;
return
0
;
}
static
int
set_modem_info
(
struct
async_struct
*
info
,
unsigned
int
cmd
,
unsigned
int
*
value
)
{
unsigned
int
arg
;
unsigned
long
flags
;
if
(
copy_from_user
(
&
arg
,
value
,
sizeof
(
int
)))
return
-
EFAULT
;
switch
(
cmd
)
{
case
TIOCMBIS
:
if
(
arg
&
TIOCM_RTS
)
info
->
MCR
|=
UART_MCR_RTS
;
if
(
arg
&
TIOCM_DTR
)
info
->
MCR
|=
UART_MCR_DTR
;
#ifdef TIOCM_OUT1
if
(
arg
&
TIOCM_OUT1
)
info
->
MCR
|=
UART_MCR_OUT1
;
if
(
arg
&
TIOCM_OUT2
)
info
->
MCR
|=
UART_MCR_OUT2
;
#endif
if
(
arg
&
TIOCM_LOOP
)
info
->
MCR
|=
UART_MCR_LOOP
;
break
;
case
TIOCMBIC
:
if
(
arg
&
TIOCM_RTS
)
info
->
MCR
&=
~
UART_MCR_RTS
;
if
(
arg
&
TIOCM_DTR
)
info
->
MCR
&=
~
UART_MCR_DTR
;
#ifdef TIOCM_OUT1
if
(
arg
&
TIOCM_OUT1
)
info
->
MCR
&=
~
UART_MCR_OUT1
;
if
(
arg
&
TIOCM_OUT2
)
info
->
MCR
&=
~
UART_MCR_OUT2
;
#endif
if
(
arg
&
TIOCM_LOOP
)
info
->
MCR
&=
~
UART_MCR_LOOP
;
break
;
case
TIOCMSET
:
info
->
MCR
=
((
info
->
MCR
&
~
(
UART_MCR_RTS
|
#ifdef TIOCM_OUT1
UART_MCR_OUT1
|
UART_MCR_OUT2
|
#endif
UART_MCR_LOOP
|
UART_MCR_DTR
))
|
((
arg
&
TIOCM_RTS
)
?
UART_MCR_RTS
:
0
)
#ifdef TIOCM_OUT1
|
((
arg
&
TIOCM_OUT1
)
?
UART_MCR_OUT1
:
0
)
|
((
arg
&
TIOCM_OUT2
)
?
UART_MCR_OUT2
:
0
)
#endif
|
((
arg
&
TIOCM_LOOP
)
?
UART_MCR_LOOP
:
0
)
|
((
arg
&
TIOCM_DTR
)
?
UART_MCR_DTR
:
0
));
break
;
default:
return
-
EINVAL
;
}
save_flags
(
flags
);
cli
();
info
->
MCR
|=
ALPHA_KLUDGE_MCR
;
/* Don't ask */
serial_out
(
info
,
UART_MCR
,
info
->
MCR
);
restore_flags
(
flags
);
return
0
;
}
static
int
do_autoconfig
(
struct
async_struct
*
info
)
{
int
irq
,
retval
;
if
(
!
capable
(
CAP_SYS_ADMIN
))
return
-
EPERM
;
if
(
info
->
state
->
count
>
1
)
return
-
EBUSY
;
shutdown
(
info
);
autoconfig
(
info
->
state
);
if
((
info
->
state
->
flags
&
ASYNC_AUTO_IRQ
)
&&
(
info
->
state
->
port
!=
0
||
info
->
state
->
iomem_base
!=
0
)
&&
(
info
->
state
->
type
!=
PORT_UNKNOWN
))
{
irq
=
detect_uart_irq
(
info
->
state
);
if
(
irq
>
0
)
info
->
state
->
irq
=
irq
;
}
retval
=
startup
(
info
);
if
(
retval
)
return
retval
;
return
0
;
}
/*
* rs_break() --- routine which turns the break handling on or off
*/
#if (LINUX_VERSION_CODE < 131394)
/* Linux 2.1.66 */
static
void
send_break
(
struct
async_struct
*
info
,
int
duration
)
{
if
(
!
CONFIGURED_SERIAL_PORT
(
info
))
return
;
current
->
state
=
TASK_INTERRUPTIBLE
;
current
->
timeout
=
jiffies
+
duration
;
cli
();
info
->
LCR
|=
UART_LCR_SBC
;
serial_out
(
info
,
UART_LCR
,
0x3
);
schedule
();
info
->
LCR
&=
~
UART_LCR_SBC
;
serial_out
(
info
,
UART_LCR
,
0x3
);
sti
();
}
#else
static
void
psio_break
(
struct
tty_struct
*
tty
,
int
break_state
)
{
struct
async_struct
*
info
=
(
struct
async_struct
*
)
tty
->
driver_data
;
unsigned
long
flags
;
if
(
serial_paranoia_check
(
info
,
tty
->
device
,
"rs_break"
))
return
;
if
(
!
CONFIGURED_SERIAL_PORT
(
info
))
return
;
save_flags
(
flags
);
cli
();
if
(
break_state
==
-
1
)
info
->
LCR
|=
UART_LCR_SBC
;
else
info
->
LCR
&=
~
UART_LCR_SBC
;
restore_flags
(
flags
);
}
#endif
#ifdef CONFIG_SERIAL_MULTIPORT
static
int
get_multiport_struct
(
struct
async_struct
*
info
,
struct
serial_multiport_struct
*
retinfo
)
{
struct
serial_multiport_struct
ret
;
struct
rs_multiport_struct
*
multi
;
multi
=
&
rs_multiport
[
info
->
state
->
irq
];
ret
.
port_monitor
=
multi
->
port_monitor
;
ret
.
port1
=
multi
->
port1
;
ret
.
mask1
=
multi
->
mask1
;
ret
.
match1
=
multi
->
match1
;
ret
.
port2
=
multi
->
port2
;
ret
.
mask2
=
multi
->
mask2
;
ret
.
match2
=
multi
->
match2
;
ret
.
port3
=
multi
->
port3
;
ret
.
mask3
=
multi
->
mask3
;
ret
.
match3
=
multi
->
match3
;
ret
.
port4
=
multi
->
port4
;
ret
.
mask4
=
multi
->
mask4
;
ret
.
match4
=
multi
->
match4
;
ret
.
irq
=
info
->
state
->
irq
;
if
(
copy_to_user
(
retinfo
,
&
ret
,
sizeof
(
*
retinfo
)))
return
-
EFAULT
;
return
0
;
}
static
int
set_multiport_struct
(
struct
async_struct
*
info
,
struct
serial_multiport_struct
*
in_multi
)
{
struct
serial_multiport_struct
new_multi
;
struct
rs_multiport_struct
*
multi
;
struct
serial_state
*
state
;
int
was_multi
,
now_multi
;
int
retval
;
void
(
*
handler
)(
int
,
void
*
,
struct
pt_regs
*
);
if
(
!
capable
(
CAP_SYS_ADMIN
))
return
-
EPERM
;
state
=
info
->
state
;
if
(
copy_from_user
(
&
new_multi
,
in_multi
,
sizeof
(
struct
serial_multiport_struct
)))
return
-
EFAULT
;
if
(
new_multi
.
irq
!=
state
->
irq
||
state
->
irq
==
0
||
!
IRQ_ports
[
state
->
irq
])
return
-
EINVAL
;
multi
=
&
rs_multiport
[
state
->
irq
];
was_multi
=
(
multi
->
port1
!=
0
);
multi
->
port_monitor
=
new_multi
.
port_monitor
;
if
(
multi
->
port1
)
release_region
(
multi
->
port1
,
1
);
multi
->
port1
=
new_multi
.
port1
;
multi
->
mask1
=
new_multi
.
mask1
;
multi
->
match1
=
new_multi
.
match1
;
if
(
multi
->
port1
)
request_region
(
multi
->
port1
,
1
,
"serial(multiport1)"
);
if
(
multi
->
port2
)
release_region
(
multi
->
port2
,
1
);
multi
->
port2
=
new_multi
.
port2
;
multi
->
mask2
=
new_multi
.
mask2
;
multi
->
match2
=
new_multi
.
match2
;
if
(
multi
->
port2
)
request_region
(
multi
->
port2
,
1
,
"serial(multiport2)"
);
if
(
multi
->
port3
)
release_region
(
multi
->
port3
,
1
);
multi
->
port3
=
new_multi
.
port3
;
multi
->
mask3
=
new_multi
.
mask3
;
multi
->
match3
=
new_multi
.
match3
;
if
(
multi
->
port3
)
request_region
(
multi
->
port3
,
1
,
"serial(multiport3)"
);
if
(
multi
->
port4
)
release_region
(
multi
->
port4
,
1
);
multi
->
port4
=
new_multi
.
port4
;
multi
->
mask4
=
new_multi
.
mask4
;
multi
->
match4
=
new_multi
.
match4
;
if
(
multi
->
port4
)
request_region
(
multi
->
port4
,
1
,
"serial(multiport4)"
);
now_multi
=
(
multi
->
port1
!=
0
);
if
(
IRQ_ports
[
state
->
irq
]
->
next_port
&&
(
was_multi
!=
now_multi
))
{
free_irq
(
state
->
irq
,
&
IRQ_ports
[
state
->
irq
]);
if
(
now_multi
)
handler
=
rs_interrupt_multi
;
else
handler
=
rs_interrupt
;
retval
=
request_irq
(
state
->
irq
,
handler
,
SA_SHIRQ
,
"serial"
,
&
IRQ_ports
[
state
->
irq
]);
if
(
retval
)
{
printk
(
"Couldn't reallocate serial interrupt "
"driver!!
\n
"
);
}
}
return
0
;
}
#endif
static
int
psio_ioctl
(
struct
tty_struct
*
tty
,
struct
file
*
file
,
unsigned
int
cmd
,
unsigned
long
arg
)
{
struct
async_struct
*
info
=
(
struct
async_struct
*
)
tty
->
driver_data
;
struct
async_icount
cprev
,
cnow
;
/* kernel counter temps */
struct
serial_icounter_struct
icount
;
unsigned
long
flags
;
#if (LINUX_VERSION_CODE < 131394)
/* Linux 2.1.66 */
int
retval
,
tmp
;
#endif
if
(
serial_paranoia_check
(
info
,
tty
->
device
,
"rs_ioctl"
))
return
-
ENODEV
;
if
((
cmd
!=
TIOCGSERIAL
)
&&
(
cmd
!=
TIOCSSERIAL
)
&&
(
cmd
!=
TIOCSERCONFIG
)
&&
(
cmd
!=
TIOCSERGSTRUCT
)
&&
(
cmd
!=
TIOCMIWAIT
)
&&
(
cmd
!=
TIOCGICOUNT
))
{
if
(
tty
->
flags
&
(
1
<<
TTY_IO_ERROR
))
return
-
EIO
;
}
switch
(
cmd
)
{
#if (LINUX_VERSION_CODE < 131394)
/* Linux 2.1.66 */
case
TCSBRK
:
/* SVID version: non-zero arg --> no break */
retval
=
tty_check_change
(
tty
);
if
(
retval
)
return
retval
;
tty_wait_until_sent
(
tty
,
0
);
if
(
signal_pending
(
current
))
return
-
EINTR
;
if
(
!
arg
)
{
send_break
(
info
,
HZ
/
4
);
/* 1/4 second */
if
(
signal_pending
(
current
))
return
-
EINTR
;
}
return
0
;
case
TCSBRKP
:
/* support for POSIX tcsendbreak() */
retval
=
tty_check_change
(
tty
);
if
(
retval
)
return
retval
;
tty_wait_until_sent
(
tty
,
0
);
if
(
signal_pending
(
current
))
return
-
EINTR
;
send_break
(
info
,
arg
?
arg
*
(
HZ
/
10
)
:
HZ
/
4
);
if
(
signal_pending
(
current
))
return
-
EINTR
;
return
0
;
case
TIOCGSOFTCAR
:
tmp
=
C_CLOCAL
(
tty
)
?
1
:
0
;
if
(
copy_to_user
((
void
*
)
arg
,
&
tmp
,
sizeof
(
int
)))
return
-
EFAULT
;
return
0
;
case
TIOCSSOFTCAR
:
if
(
copy_from_user
(
&
tmp
,
(
void
*
)
arg
,
sizeof
(
int
)))
return
-
EFAULT
;
tty
->
termios
->
c_cflag
=
((
tty
->
termios
->
c_cflag
&
~
CLOCAL
)
|
(
tmp
?
CLOCAL
:
0
));
return
0
;
#endif
case
TIOCMGET
:
return
get_modem_info
(
info
,
(
unsigned
int
*
)
arg
);
case
TIOCMBIS
:
case
TIOCMBIC
:
case
TIOCMSET
:
return
set_modem_info
(
info
,
cmd
,
(
unsigned
int
*
)
arg
);
case
TIOCGSERIAL
:
return
get_serial_info
(
info
,
(
struct
serial_struct
*
)
arg
);
case
TIOCSSERIAL
:
return
set_serial_info
(
info
,
(
struct
serial_struct
*
)
arg
);
case
TIOCSERCONFIG
:
return
do_autoconfig
(
info
);
case
TIOCSERGETLSR
:
/* Get line status register */
return
get_lsr_info
(
info
,
(
unsigned
int
*
)
arg
);
case
TIOCSERGSTRUCT
:
if
(
copy_to_user
((
struct
async_struct
*
)
arg
,
info
,
sizeof
(
struct
async_struct
)))
return
-
EFAULT
;
return
0
;
#ifdef CONFIG_SERIAL_MULTIPORT
case
TIOCSERGETMULTI
:
return
get_multiport_struct
(
info
,
(
struct
serial_multiport_struct
*
)
arg
);
case
TIOCSERSETMULTI
:
return
set_multiport_struct
(
info
,
(
struct
serial_multiport_struct
*
)
arg
);
#endif
/*
* Wait for any of the 4 modem inputs (DCD,RI,DSR,CTS) to change
* - mask passed in arg for lines of interest
* (use |'ed TIOCM_RNG/DSR/CD/CTS for masking)
* Caller should use TIOCGICOUNT to see which one it was
*/
case
TIOCMIWAIT
:
save_flags
(
flags
);
cli
();
/* note the counters on entry */
cprev
=
info
->
state
->
icount
;
restore_flags
(
flags
);
/* Force modem status interrupts on */
info
->
IER
|=
UART_IER_MSI
;
serial_out
(
info
,
UART_IER
,
info
->
IER
);
while
(
1
)
{
interruptible_sleep_on
(
&
info
->
delta_msr_wait
);
/* see if a signal did it */
if
(
signal_pending
(
current
))
return
-
ERESTARTSYS
;
save_flags
(
flags
);
cli
();
cnow
=
info
->
state
->
icount
;
/* atomic copy */
restore_flags
(
flags
);
if
(
cnow
.
rng
==
cprev
.
rng
&&
cnow
.
dsr
==
cprev
.
dsr
&&
cnow
.
dcd
==
cprev
.
dcd
&&
cnow
.
cts
==
cprev
.
cts
)
return
-
EIO
;
/* no change => error */
if
(
((
arg
&
TIOCM_RNG
)
&&
(
cnow
.
rng
!=
cprev
.
rng
))
||
((
arg
&
TIOCM_DSR
)
&&
(
cnow
.
dsr
!=
cprev
.
dsr
))
||
((
arg
&
TIOCM_CD
)
&&
(
cnow
.
dcd
!=
cprev
.
dcd
))
||
((
arg
&
TIOCM_CTS
)
&&
(
cnow
.
cts
!=
cprev
.
cts
))
)
{
return
0
;
}
cprev
=
cnow
;
}
/* NOTREACHED */
/*
* Get counter of input serial line interrupts (DCD,RI,DSR,CTS)
* Return: write counters to the user passed counter struct
* NB: both 1->0 and 0->1 transitions are counted except for
* RI where only 0->1 is counted.
*/
case
TIOCGICOUNT
:
save_flags
(
flags
);
cli
();
cnow
=
info
->
state
->
icount
;
restore_flags
(
flags
);
icount
.
cts
=
cnow
.
cts
;
icount
.
dsr
=
cnow
.
dsr
;
icount
.
rng
=
cnow
.
rng
;
icount
.
dcd
=
cnow
.
dcd
;
icount
.
rx
=
cnow
.
rx
;
icount
.
tx
=
cnow
.
tx
;
icount
.
frame
=
cnow
.
frame
;
icount
.
overrun
=
cnow
.
overrun
;
icount
.
parity
=
cnow
.
parity
;
icount
.
brk
=
cnow
.
brk
;
icount
.
buf_overrun
=
cnow
.
buf_overrun
;
if
(
copy_to_user
((
void
*
)
arg
,
&
icount
,
sizeof
(
icount
)))
return
-
EFAULT
;
return
0
;
case
TIOCSERGWILD
:
case
TIOCSERSWILD
:
/* "setserial -W" is called in Debian boot */
printk
(
"TIOCSER?WILD ioctl obsolete, ignored.
\n
"
);
return
0
;
default:
return
-
ENOIOCTLCMD
;
}
return
0
;
}
static
void
psio_set_termios
(
struct
tty_struct
*
tty
,
struct
termios
*
old_termios
)
{
struct
async_struct
*
info
=
(
struct
async_struct
*
)
tty
->
driver_data
;
unsigned
long
flags
;
unsigned
int
cflag
=
tty
->
termios
->
c_cflag
;
if
(
(
cflag
==
old_termios
->
c_cflag
)
&&
(
RELEVANT_IFLAG
(
tty
->
termios
->
c_iflag
)
==
RELEVANT_IFLAG
(
old_termios
->
c_iflag
)))
return
;
change_speed
(
info
,
old_termios
);
/* Handle transition to B0 status */
if
((
old_termios
->
c_cflag
&
CBAUD
)
&&
!
(
cflag
&
CBAUD
))
{
info
->
MCR
&=
~
(
UART_MCR_DTR
|
UART_MCR_RTS
);
save_flags
(
flags
);
cli
();
serial_out
(
info
,
UART_MCR
,
info
->
MCR
);
restore_flags
(
flags
);
}
/* Handle transition away from B0 status */
if
(
!
(
old_termios
->
c_cflag
&
CBAUD
)
&&
(
cflag
&
CBAUD
))
{
info
->
MCR
|=
UART_MCR_DTR
;
if
(
!
(
tty
->
termios
->
c_cflag
&
CRTSCTS
)
||
!
test_bit
(
TTY_THROTTLED
,
&
tty
->
flags
))
{
info
->
MCR
|=
UART_MCR_RTS
;
}
save_flags
(
flags
);
cli
();
serial_out
(
info
,
UART_MCR
,
info
->
MCR
);
restore_flags
(
flags
);
}
/* Handle turning off CRTSCTS */
if
((
old_termios
->
c_cflag
&
CRTSCTS
)
&&
!
(
tty
->
termios
->
c_cflag
&
CRTSCTS
))
{
tty
->
hw_stopped
=
0
;
psio_start
(
tty
);
}
}
/*
* -----------------------------------------------------------
* psio_close()
*
* This routine is called when the debug console port gets closed.
* First, we wait for the last remaining data to be sent. Then, we unlink
* its async structure from the interrupt chain if necessary, and we free
* that IRQ if nothing is left in the chain.
* -----------------------------------------------------------
*/
static
void
psio_close
(
struct
tty_struct
*
tty
,
struct
file
*
filp
)
{
struct
async_struct
*
info
=
(
struct
async_struct
*
)
tty
->
driver_data
;
struct
serial_state
*
state
;
unsigned
long
flags
;
if
(
!
info
||
serial_paranoia_check
(
info
,
tty
->
device
,
"rs_close"
))
return
;
state
=
info
->
state
;
save_flags
(
flags
);
cli
();
if
(
tty_hung_up_p
(
filp
))
{
DBG_CNT
(
"before DEC-hung"
);
MOD_DEC_USE_COUNT
;
restore_flags
(
flags
);
return
;
}
#ifdef SERIAL_DEBUG_OPEN
printk
(
"psio_close ttyD%d, count = %d
\n
"
,
info
->
line
,
state
->
count
);
#endif
if
((
tty
->
count
==
1
)
&&
(
state
->
count
!=
1
))
{
/*
* Uh, oh. tty->count is 1, which means that the tty
* structure will be freed. state->count should always
* be one in these conditions. If it's greater than
* one, we've got real problems, since it means the
* serial port won't be shutdown.
*/
printk
(
"rs_close: bad serial port count; tty->count is 1, "
"state->count is %d
\n
"
,
state
->
count
);
state
->
count
=
1
;
}
if
(
--
state
->
count
<
0
)
{
printk
(
"psio_close: bad serial port count for ttyD%d: %d
\n
"
,
info
->
line
,
state
->
count
);
state
->
count
=
0
;
}
if
(
state
->
count
)
{
DBG_CNT
(
"before DEC-2"
);
MOD_DEC_USE_COUNT
;
restore_flags
(
flags
);
return
;
}
info
->
flags
|=
ASYNC_CLOSING
;
restore_flags
(
flags
);
/*
* Save the termios structure, since this port may have
* separate termios for callout and dialin.
*/
if
(
info
->
flags
&
ASYNC_NORMAL_ACTIVE
)
info
->
state
->
normal_termios
=
*
tty
->
termios
;
if
(
info
->
flags
&
ASYNC_CALLOUT_ACTIVE
)
info
->
state
->
callout_termios
=
*
tty
->
termios
;
/*
* Now we wait for the transmit buffer to clear; and we notify
* the line discipline to only process XON/XOFF characters.
*/
tty
->
closing
=
1
;
if
(
info
->
closing_wait
!=
ASYNC_CLOSING_WAIT_NONE
)
tty_wait_until_sent
(
tty
,
info
->
closing_wait
);
/*
* At this point we stop accepting input. To do this, we
* disable the receive line status interrupts, and tell the
* interrupt driver to stop checking the data ready bit in the
* line status register.
*/
info
->
IER
&=
~
UART_IER_RLSI
;
info
->
read_status_mask
&=
~
UART_LSR_DR
;
if
(
info
->
flags
&
ASYNC_INITIALIZED
)
{
serial_out
(
info
,
UART_IER
,
info
->
IER
);
/*
* Before we drop DTR, make sure the UART transmitter
* has completely drained; this is especially
* important if there is a transmit FIFO!
*/
psio_wait_until_sent
(
tty
,
info
->
timeout
);
}
shutdown
(
info
);
if
(
tty
->
driver
.
flush_buffer
)
tty
->
driver
.
flush_buffer
(
tty
);
if
(
tty
->
ldisc
.
flush_buffer
)
tty
->
ldisc
.
flush_buffer
(
tty
);
tty
->
closing
=
0
;
info
->
event
=
0
;
info
->
tty
=
0
;
if
(
info
->
blocked_open
)
{
if
(
info
->
close_delay
)
{
set_current_state
(
TASK_INTERRUPTIBLE
);
schedule_timeout
(
info
->
close_delay
);
}
wake_up_interruptible
(
&
info
->
open_wait
);
}
info
->
flags
&=
~
(
ASYNC_NORMAL_ACTIVE
|
ASYNC_CALLOUT_ACTIVE
|
ASYNC_CLOSING
);
wake_up_interruptible
(
&
info
->
close_wait
);
MOD_DEC_USE_COUNT
;
}
/*
* rs_wait_until_sent() --- wait until the transmitter is empty
*/
static
void
psio_wait_until_sent
(
struct
tty_struct
*
tty
,
int
timeout
)
{
struct
async_struct
*
info
=
(
struct
async_struct
*
)
tty
->
driver_data
;
unsigned
long
orig_jiffies
,
char_time
;
int
lsr
;
if
(
serial_paranoia_check
(
info
,
tty
->
device
,
"psio_wait_until_sent"
))
return
;
if
(
info
->
state
->
type
==
PORT_UNKNOWN
)
return
;
if
(
info
->
xmit_fifo_size
==
0
)
return
;
/* Just in case.... */
orig_jiffies
=
jiffies
;
/*
* Set the check interval to be 1/5 of the estimated time to
* send a single character, and make it at least 1. The check
* interval should also be less than the timeout.
*
* Note: we have to use pretty tight timings here to satisfy
* the NIST-PCTS.
*/
char_time
=
(
info
->
timeout
-
HZ
/
50
)
/
info
->
xmit_fifo_size
;
char_time
=
char_time
/
5
;
if
(
char_time
==
0
)
char_time
=
1
;
if
(
timeout
&&
timeout
<
char_time
)
char_time
=
timeout
;
/*
* If the transmitter hasn't cleared in twice the approximate
* amount of time to send the entire FIFO, it probably won't
* ever clear. This assumes the UART isn't doing flow
* control, which is currently the case. Hence, if it ever
* takes longer than info->timeout, this is probably due to a
* UART bug of some kind. So, we clamp the timeout parameter at
* 2*info->timeout.
*/
if
(
!
timeout
||
timeout
>
2
*
info
->
timeout
)
timeout
=
2
*
info
->
timeout
;
#ifdef SERIAL_DEBUG_RS_WAIT_UNTIL_SENT
printk
(
"In rs_wait_until_sent(%d) check=%lu..."
,
timeout
,
char_time
);
printk
(
"jiff=%lu..."
,
jiffies
);
#endif
while
(
!
((
lsr
=
serial_inp
(
info
,
UART_LSR
))
&
UART_LSR_TEMT
))
{
#ifdef SERIAL_DEBUG_RS_WAIT_UNTIL_SENT
printk
(
"lsr = %d (jiff=%lu)..."
,
lsr
,
jiffies
);
#endif
set_current_state
(
TASK_INTERRUPTIBLE
);
schedule_timeout
(
char_time
);
if
(
signal_pending
(
current
))
break
;
if
(
timeout
&&
time_after
(
jiffies
,
orig_jiffies
+
timeout
))
break
;
}
#ifdef SERIAL_DEBUG_RS_WAIT_UNTIL_SENT
printk
(
"lsr = %d (jiff=%lu)...done
\n
"
,
lsr
,
jiffies
);
#endif
}
/*
* psio_hangup() --- called by tty_hangup() when a hangup is signaled.
*/
static
void
psio_hangup
(
struct
tty_struct
*
tty
)
{
struct
async_struct
*
info
=
(
struct
async_struct
*
)
tty
->
driver_data
;
struct
serial_state
*
state
=
info
->
state
;
if
(
serial_paranoia_check
(
info
,
tty
->
device
,
"psio_hangup"
))
return
;
state
=
info
->
state
;
psio_flush_buffer
(
tty
);
if
(
info
->
flags
&
ASYNC_CLOSING
)
return
;
shutdown
(
info
);
info
->
event
=
0
;
state
->
count
=
0
;
info
->
flags
&=
~
(
ASYNC_NORMAL_ACTIVE
|
ASYNC_CALLOUT_ACTIVE
);
info
->
tty
=
0
;
wake_up_interruptible
(
&
info
->
open_wait
);
}
/*
static void rs_wait_until_sent(struct tty_struct *tty, int timeout)
static void rs_hangup(struct tty_struct *tty)
*/
/*
* ------------------------------------------------------------
* psio_open() and friends
* ------------------------------------------------------------
*/
#define SERIAL_DEBUG_OPEN
static
int
block_til_ready
(
struct
tty_struct
*
tty
,
struct
file
*
filp
,
struct
async_struct
*
info
)
{
DECLARE_WAITQUEUE
(
wait
,
current
);
struct
serial_state
*
state
=
info
->
state
;
int
retval
;
int
do_clocal
=
0
,
extra_count
=
0
;
unsigned
long
flags
;
/*
* If the device is in the middle of being closed, then block
* until it's done, and then try again.
*/
if
(
tty_hung_up_p
(
filp
)
||
(
info
->
flags
&
ASYNC_CLOSING
))
{
if
(
info
->
flags
&
ASYNC_CLOSING
)
interruptible_sleep_on
(
&
info
->
close_wait
);
#ifdef SERIAL_DO_RESTART
return
((
info
->
flags
&
ASYNC_HUP_NOTIFY
)
?
-
EAGAIN
:
-
ERESTARTSYS
);
#else
return
-
EAGAIN
;
#endif
}
/*
* If this is a callout device, then just make sure the normal
* device isn't being used.
*/
if
(
tty
->
driver
.
subtype
==
SERIAL_TYPE_CALLOUT
)
{
if
(
info
->
flags
&
ASYNC_NORMAL_ACTIVE
)
return
-
EBUSY
;
if
((
info
->
flags
&
ASYNC_CALLOUT_ACTIVE
)
&&
(
info
->
flags
&
ASYNC_SESSION_LOCKOUT
)
&&
(
info
->
session
!=
current
->
session
))
return
-
EBUSY
;
if
((
info
->
flags
&
ASYNC_CALLOUT_ACTIVE
)
&&
(
info
->
flags
&
ASYNC_PGRP_LOCKOUT
)
&&
(
info
->
pgrp
!=
current
->
pgrp
))
return
-
EBUSY
;
info
->
flags
|=
ASYNC_CALLOUT_ACTIVE
;
return
0
;
}
#if 1
/* ? 020906 */
filp
->
f_flags
|=
O_NONBLOCK
;
#endif
/* ? 020906 */
/*
* If non-blocking mode is set, or the port is not enabled,
* then make the check up front and then exit.
*/
if
((
filp
->
f_flags
&
O_NONBLOCK
)
||
(
tty
->
flags
&
(
1
<<
TTY_IO_ERROR
)))
{
if
(
info
->
flags
&
ASYNC_CALLOUT_ACTIVE
)
return
-
EBUSY
;
info
->
flags
|=
ASYNC_NORMAL_ACTIVE
;
return
0
;
}
if
(
info
->
flags
&
ASYNC_CALLOUT_ACTIVE
)
{
if
(
state
->
normal_termios
.
c_cflag
&
CLOCAL
)
do_clocal
=
1
;
}
else
{
if
(
tty
->
termios
->
c_cflag
&
CLOCAL
)
do_clocal
=
1
;
}
/*
* Block waiting for the carrier detect and the line to become
* free (i.e., not in use by the callout). While we are in
* this loop, state->count is dropped by one, so that
* rs_close() knows when to free things. We restore it upon
* exit, either normal or abnormal.
*/
retval
=
0
;
add_wait_queue
(
&
info
->
open_wait
,
&
wait
);
#ifdef SERIAL_DEBUG_OPEN
printk
(
"block_til_ready before block: ttyD%d, count = %d
\n
"
,
state
->
line
,
state
->
count
);
#endif
save_flags
(
flags
);
cli
();
if
(
!
tty_hung_up_p
(
filp
))
{
extra_count
=
1
;
state
->
count
--
;
}
restore_flags
(
flags
);
info
->
blocked_open
++
;
while
(
1
)
{
save_flags
(
flags
);
cli
();
if
(
!
(
info
->
flags
&
ASYNC_CALLOUT_ACTIVE
)
&&
(
tty
->
termios
->
c_cflag
&
CBAUD
))
serial_out
(
info
,
UART_MCR
,
serial_inp
(
info
,
UART_MCR
)
|
(
UART_MCR_DTR
|
UART_MCR_RTS
));
restore_flags
(
flags
);
set_current_state
(
TASK_INTERRUPTIBLE
);
if
(
tty_hung_up_p
(
filp
)
||
!
(
info
->
flags
&
ASYNC_INITIALIZED
))
{
#ifdef SERIAL_DO_RESTART
if
(
info
->
flags
&
ASYNC_HUP_NOTIFY
)
retval
=
-
EAGAIN
;
else
retval
=
-
ERESTARTSYS
;
#else
retval
=
-
EAGAIN
;
#endif
break
;
}
if
(
!
(
info
->
flags
&
ASYNC_CALLOUT_ACTIVE
)
&&
!
(
info
->
flags
&
ASYNC_CLOSING
)
&&
(
do_clocal
||
(
serial_in
(
info
,
UART_MSR
)
&
UART_MSR_DCD
)))
break
;
if
(
signal_pending
(
current
))
{
retval
=
-
ERESTARTSYS
;
break
;
}
#ifdef SERIAL_DEBUG_OPEN
printk
(
"block_til_ready blocking: ttyD%d, count = %d
\n
"
,
info
->
line
,
state
->
count
);
#endif
schedule
();
}
set_current_state
(
TASK_RUNNING
);
remove_wait_queue
(
&
info
->
open_wait
,
&
wait
);
if
(
extra_count
)
state
->
count
++
;
info
->
blocked_open
--
;
#ifdef SERIAL_DEBUG_OPEN
printk
(
"block_til_ready after blocking: ttyD%d, count = %d
\n
"
,
info
->
line
,
state
->
count
);
#endif
if
(
retval
)
return
retval
;
info
->
flags
|=
ASYNC_NORMAL_ACTIVE
;
return
0
;
}
#undef SERIAL_DEBUG_OPEN
static
int
get_async_struct
(
int
line
,
struct
async_struct
**
ret_info
)
{
struct
async_struct
*
info
;
struct
serial_state
*
sstate
;
sstate
=
rs_table
+
line
;
sstate
->
count
++
;
if
(
sstate
->
info
)
{
*
ret_info
=
sstate
->
info
;
return
0
;
}
info
=
kmalloc
(
sizeof
(
struct
async_struct
),
GFP_KERNEL
);
if
(
!
info
)
{
sstate
->
count
--
;
return
-
ENOMEM
;
}
memset
(
info
,
0
,
sizeof
(
struct
async_struct
));
init_waitqueue_head
(
&
info
->
open_wait
);
init_waitqueue_head
(
&
info
->
close_wait
);
init_waitqueue_head
(
&
info
->
delta_msr_wait
);
info
->
magic
=
SERIAL_MAGIC
;
info
->
port
=
sstate
->
port
;
info
->
flags
=
sstate
->
flags
;
info
->
io_type
=
sstate
->
io_type
;
info
->
iomem_base
=
sstate
->
iomem_base
;
info
->
iomem_reg_shift
=
sstate
->
iomem_reg_shift
;
info
->
xmit_fifo_size
=
sstate
->
xmit_fifo_size
=
0
;
info
->
line
=
line
;
info
->
tqueue
.
routine
=
do_softint
;
info
->
tqueue
.
data
=
info
;
info
->
state
=
sstate
;
if
(
sstate
->
info
)
{
kfree
(
info
);
*
ret_info
=
sstate
->
info
;
return
0
;
}
*
ret_info
=
sstate
->
info
=
info
;
return
0
;
}
/*
* -----------------------------------------------------------
* psio_open()
*
* This routine is called whenever a debug console port is opened. It
* enables interrupts for a serial port, linking in its async structure into
* the IRQ chain. It also performs the serial-specific
* initialization for the tty structure.
* -----------------------------------------------------------
*/
static
int
psio_open
(
struct
tty_struct
*
tty
,
struct
file
*
filp
)
{
struct
async_struct
*
info
;
int
retval
,
line
=
0
;
unsigned
long
page
;
MOD_INC_USE_COUNT
;
line
=
MINOR
(
tty
->
device
)
-
tty
->
driver
.
minor_start
;
if
((
line
<
0
)
||
(
line
>=
NR_PORTS
))
{
MOD_DEC_USE_COUNT
;
return
-
ENODEV
;
}
retval
=
get_async_struct
(
line
,
&
info
);
if
(
retval
)
{
printk
(
"psio_open ttyD%d fail..."
,
line
);
MOD_DEC_USE_COUNT
;
return
retval
;
}
tty
->
driver_data
=
info
;
info
->
tty
=
tty
;
if
(
serial_paranoia_check
(
info
,
tty
->
device
,
"psio_open"
))
return
-
ENODEV
;
#ifdef SERIAL_DEBUG_OPEN
printk
(
"psio_open %s%d, count = %d
\n
"
,
tty
->
driver
.
name
,
info
->
line
,
info
->
state
->
count
);
#endif
info
->
tty
->
low_latency
=
(
info
->
flags
&
ASYNC_LOW_LATENCY
)
?
1
:
0
;
/*
* This relies on lock_kernel() stuff so wants tidying for 2.5
*/
if
(
!
tmp_buf
)
{
page
=
get_zeroed_page
(
GFP_KERNEL
);
if
(
!
page
)
return
-
ENOMEM
;
if
(
tmp_buf
)
free_page
(
page
);
else
tmp_buf
=
(
unsigned
char
*
)
page
;
}
/*
* If the port is the middle of closing, bail out now
*/
if
(
tty_hung_up_p
(
filp
)
||
(
info
->
flags
&
ASYNC_CLOSING
))
{
if
(
info
->
flags
&
ASYNC_CLOSING
)
interruptible_sleep_on
(
&
info
->
close_wait
);
#ifdef SERIAL_DO_RESTART
return
((
info
->
flags
&
ASYNC_HUP_NOTIFY
)
?
-
EAGAIN
:
-
ERESTARTSYS
);
#else
return
-
EAGAIN
;
#endif
}
/*
* Start up serial port
*/
retval
=
startup
(
info
);
if
(
retval
)
return
retval
;
retval
=
block_til_ready
(
tty
,
filp
,
info
);
if
(
retval
)
{
#ifdef SERIAL_DEBUG_OPEN
printk
(
"psio_open returning after block_til_ready with %d
\n
"
,
retval
);
#endif
return
retval
;
}
if
((
info
->
state
->
count
==
1
)
&&
(
info
->
flags
&
ASYNC_SPLIT_TERMIOS
))
{
if
(
tty
->
driver
.
subtype
==
SERIAL_TYPE_NORMAL
)
*
tty
->
termios
=
info
->
state
->
normal_termios
;
else
*
tty
->
termios
=
info
->
state
->
callout_termios
;
change_speed
(
info
,
0
);
}
#ifdef CONFIG_SERIAL_CONSOLE
if
(
cons
.
cflag
&&
cons
.
index
==
line
)
{
tty
->
termios
->
c_cflag
=
cons
.
cflag
;
cons
.
cflag
=
0
;
change_speed
(
info
,
0
);
}
#endif
info
->
session
=
current
->
session
;
info
->
pgrp
=
current
->
pgrp
;
return
0
;
}
/*
* /proc fs routines....
*/
static
inline
int
line_info
(
char
*
buf
,
struct
serial_state
*
state
)
{
struct
async_struct
*
info
=
state
->
info
,
scr_info
;
char
stat_buf
[
30
],
control
,
status
;
int
ret
;
unsigned
long
flags
;
ret
=
sprintf
(
buf
,
"%d: uart:%s port:%lX irq:%d"
,
state
->
line
,
uart_config
[
state
->
type
].
name
,
state
->
port
,
state
->
irq
);
if
(
!
state
->
port
||
(
state
->
type
==
PORT_UNKNOWN
))
{
ret
+=
sprintf
(
buf
+
ret
,
"
\n
"
);
return
ret
;
}
/*
* Figure out the current RS-232 lines
*/
if
(
!
info
)
{
info
=
&
scr_info
;
/* This is just for serial_{in,out} */
info
->
magic
=
SERIAL_MAGIC
;
info
->
port
=
state
->
port
;
info
->
flags
=
state
->
flags
;
info
->
hub6
=
state
->
hub6
;
info
->
io_type
=
state
->
io_type
;
info
->
iomem_base
=
state
->
iomem_base
;
info
->
iomem_reg_shift
=
state
->
iomem_reg_shift
;
info
->
quot
=
0
;
info
->
tty
=
0
;
}
save_flags
(
flags
);
cli
();
status
=
serial_in
(
info
,
UART_MSR
);
control
=
info
!=
&
scr_info
?
info
->
MCR
:
serial_in
(
info
,
UART_MCR
);
restore_flags
(
flags
);
stat_buf
[
0
]
=
0
;
stat_buf
[
1
]
=
0
;
if
(
control
&
UART_MCR_RTS
)
strcat
(
stat_buf
,
"|RTS"
);
if
(
status
&
UART_MSR_CTS
)
strcat
(
stat_buf
,
"|CTS"
);
if
(
control
&
UART_MCR_DTR
)
strcat
(
stat_buf
,
"|DTR"
);
if
(
status
&
UART_MSR_DSR
)
strcat
(
stat_buf
,
"|DSR"
);
if
(
status
&
UART_MSR_DCD
)
strcat
(
stat_buf
,
"|CD"
);
if
(
status
&
UART_MSR_RI
)
strcat
(
stat_buf
,
"|RI"
);
if
(
info
->
quot
)
{
ret
+=
sprintf
(
buf
+
ret
,
" baud:%d"
,
state
->
baud_base
/
(
16
*
info
->
quot
));
}
ret
+=
sprintf
(
buf
+
ret
,
" tx:%d rx:%d"
,
state
->
icount
.
tx
,
state
->
icount
.
rx
);
if
(
state
->
icount
.
frame
)
ret
+=
sprintf
(
buf
+
ret
,
" fe:%d"
,
state
->
icount
.
frame
);
if
(
state
->
icount
.
parity
)
ret
+=
sprintf
(
buf
+
ret
,
" pe:%d"
,
state
->
icount
.
parity
);
if
(
state
->
icount
.
brk
)
ret
+=
sprintf
(
buf
+
ret
,
" brk:%d"
,
state
->
icount
.
brk
);
if
(
state
->
icount
.
overrun
)
ret
+=
sprintf
(
buf
+
ret
,
" oe:%d"
,
state
->
icount
.
overrun
);
/*
* Last thing is the RS-232 status lines
*/
ret
+=
sprintf
(
buf
+
ret
,
" %s
\n
"
,
stat_buf
+
1
);
return
ret
;
}
int
psio_read_proc
(
char
*
page
,
char
**
start
,
off_t
off
,
int
count
,
int
*
eof
,
void
*
data
)
{
int
i
,
len
=
0
,
l
;
off_t
begin
=
0
;
len
+=
sprintf
(
page
,
"sioinfo:1.0 driver:%s%s revision:%s
\n
"
,
serial_version
,
LOCAL_VERSTRING
,
serial_revdate
);
for
(
i
=
0
;
i
<
NR_PORTS
&&
len
<
4000
;
i
++
)
{
l
=
line_info
(
page
+
len
,
&
rs_table
[
i
]);
len
+=
l
;
if
(
len
+
begin
>
off
+
count
)
goto
done
;
if
(
len
+
begin
<
off
)
{
begin
+=
len
;
len
=
0
;
}
}
*
eof
=
1
;
done:
if
(
off
>=
len
+
begin
)
return
0
;
*
start
=
page
+
(
off
-
begin
);
return
((
count
<
begin
+
len
-
off
)
?
count
:
begin
+
len
-
off
);
}
static
char
serial_options
[]
__initdata
=
" no serial options enabled
\n
"
;
static
inline
void
show_serial_version
(
void
)
{
printk
(
KERN_INFO
"%s version %s%s (%s) with%s"
,
serial_name
,
serial_version
,
LOCAL_VERSTRING
,
serial_revdate
,
serial_options
);
}
static
unsigned
detect_uart_irq
(
struct
serial_state
*
state
)
{
if
(
!
state
->
irq
)
printk
(
KERN_INFO
"detect_uart_irq: Ohh irq = 0
\n
"
);
return
state
->
irq
;
}
static
void
autoconfig
(
struct
serial_state
*
state
)
{
struct
async_struct
*
info
,
scr_info
;
//unsigned long flags;
state
->
type
=
PORT_UNKNOWN
;
#ifdef SERIAL_DEBUG_AUTOCONF
printk
(
"Testing ttyD%d (0x%04lx, 0x%04x)...
\n
"
,
state
->
line
,
state
->
port
,
(
unsigned
)
state
->
iomem_base
);
#endif
if
(
!
CONFIGURED_SERIAL_PORT
(
state
))
return
;
info
=
&
scr_info
;
/* This is just for serial_{in,out} */
info
->
magic
=
SERIAL_MAGIC
;
info
->
state
=
state
;
info
->
port
=
state
->
port
;
info
->
flags
=
state
->
flags
;
sio_reset
(
info
);
}
/*
* The debug console driver boot-time initialization code!
*/
/* 20020830 */
int
__init
psio_init
(
void
)
{
int
i
;
struct
serial_state
*
state
;
init_bh
(
SERIAL_BH
,
do_psio_serial_bh
);
init_timer
(
&
serial_timer
);
serial_timer
.
function
=
(
void
*
)
psio_timer
;
#if 1
mod_timer
(
&
serial_timer
,
jiffies
+
10
);
#else
/* 1 */
mod_timer
(
&
serial_timer
,
jiffies
+
RS_STROBE_TIME
);
#endif
/* 1 */
for
(
i
=
0
;
i
<
NR_IRQS
;
i
++
)
{
IRQ_ports
[
i
]
=
0
;
IRQ_timeout
[
i
]
=
0
;
}
/*
* Initialize the tty_driver structure
*/
memset
(
&
psio_driver
,
0
,
sizeof
(
struct
tty_driver
));
psio_driver
.
magic
=
TTY_DRIVER_MAGIC
;
#if (LINUX_VERSION_CODE > 0x20100)
psio_driver
.
driver_name
=
"serial_m32102"
;
#endif
#if 1
psio_driver
.
name
=
"ttyD"
;
#else
#if (LINUX_VERSION_CODE > 0x2032D && defined(CONFIG_DEVFS_FS))
psio_driver
.
name
=
"ttyd/%d"
;
#else
psio_driver
.
name
=
"ttyD"
;
#endif
#endif
/* 1 */
psio_driver
.
major
=
TTY_MAJOR
;
psio_driver
.
minor_start
=
80
;
psio_driver
.
name_base
=
0
;
psio_driver
.
num
=
NR_PORTS
;
psio_driver
.
type
=
TTY_DRIVER_TYPE_SERIAL
;
psio_driver
.
subtype
=
SERIAL_TYPE_NORMAL
;
psio_driver
.
init_termios
=
tty_std_termios
;
psio_driver
.
init_termios
.
c_cflag
=
B9600
|
CS8
|
CREAD
|
HUPCL
|
CLOCAL
;
psio_driver
.
flags
=
TTY_DRIVER_REAL_RAW
|
TTY_DRIVER_NO_DEVFS
;
psio_driver
.
refcount
=
&
psio_refcount
;
psio_driver
.
table
=
psio_table
;
psio_driver
.
termios
=
psio_termios
;
psio_driver
.
termios_locked
=
psio_termios_locked
;
psio_driver
.
open
=
psio_open
;
psio_driver
.
close
=
psio_close
;
psio_driver
.
write
=
psio_write
;
psio_driver
.
put_char
=
psio_put_char
;
psio_driver
.
flush_chars
=
psio_flush_chars
;
psio_driver
.
write_room
=
psio_write_room
;
psio_driver
.
chars_in_buffer
=
psio_chars_in_buffer
;
psio_driver
.
flush_buffer
=
psio_flush_buffer
;
psio_driver
.
ioctl
=
psio_ioctl
;
psio_driver
.
throttle
=
psio_throttle
;
psio_driver
.
unthrottle
=
psio_unthrottle
;
psio_driver
.
set_termios
=
psio_set_termios
;
psio_driver
.
stop
=
psio_stop
;
psio_driver
.
start
=
psio_start
;
psio_driver
.
hangup
=
psio_hangup
;
#if (LINUX_VERSION_CODE >= 131394)
/* Linux 2.1.66 */
psio_driver
.
break_ctl
=
psio_break
;
#endif
#if (LINUX_VERSION_CODE >= 131343)
psio_driver
.
send_xchar
=
psio_send_xchar
;
psio_driver
.
wait_until_sent
=
psio_wait_until_sent
;
psio_driver
.
read_proc
=
psio_read_proc
;
#endif
if
(
tty_register_driver
(
&
psio_driver
))
panic
(
"Couldn't register debug console driver
\n
"
);
for
(
i
=
0
,
state
=
rs_table
;
i
<
NR_PORTS
;
i
++
,
state
++
)
{
state
->
magic
=
SSTATE_MAGIC
;
state
->
line
=
i
;
state
->
type
=
14
;
state
->
custom_divisor
=
0
;
state
->
close_delay
=
5
*
HZ
/
10
;
state
->
closing_wait
=
30
*
HZ
;
state
->
callout_termios
=
psio_driver
.
init_termios
;
state
->
normal_termios
=
psio_driver
.
init_termios
;
state
->
icount
.
cts
=
state
->
icount
.
dsr
=
state
->
icount
.
rng
=
state
->
icount
.
dcd
=
0
;
state
->
icount
.
rx
=
state
->
icount
.
tx
=
0
;
state
->
icount
.
frame
=
state
->
icount
.
parity
=
0
;
state
->
icount
.
overrun
=
state
->
icount
.
brk
=
0
;
state
->
irq
=
irq_cannonicalize
(
state
->
irq
);
#if 0
if (check_region(state->port,8))
continue;
if (state->flags & ASYNC_BOOT_AUTOCONF)
autoconfig(psio_table);
#endif
state
->
baud_base
=
boot_cpu_data
.
bus_clock
;
printk
(
KERN_INFO
"ttyD%d initialized.
\n
"
,
i
);
tty_register_devfs
(
&
psio_driver
,
0
,
psio_driver
.
minor_start
+
state
->
line
);
}
return
0
;
}
/* 20020830 */
static
void
__exit
psio_fini
(
void
)
{
unsigned
long
flags
;
// int e1, e2;
int
e1
;
// int i;
// struct async_struct *info;
/* printk("Unloading %s: version %s\n", serial_name, serial_version); */
del_timer_sync
(
&
serial_timer
);
save_flags
(
flags
);
cli
();
remove_bh
(
SERIAL_BH
);
if
((
e1
=
tty_unregister_driver
(
&
psio_driver
)))
printk
(
"psio_serial: failed to unregister serial driver (%d)
\n
"
,
e1
);
restore_flags
(
flags
);
if
(
tmp_buf
)
{
unsigned
long
pg
=
(
unsigned
long
)
tmp_buf
;
tmp_buf
=
NULL
;
free_page
(
pg
);
}
}
module_init
(
psio_init
);
module_exit
(
psio_fini
);
MODULE_DESCRIPTION
(
"M32R/M32102 (dumb) serial driver"
);
MODULE_AUTHOR
(
"Hiroyuki Kondo <kondo.hiroyuki@renesas.com>, Takeo Takahashi <takahashi.takeo@renesas.com>"
);
MODULE_LICENSE
(
"GPL"
);
/*
* -----------------------------------------------------------
* Debug console driver
* -----------------------------------------------------------
*/
#define BOTH_EMPTY (UART_LSR_TEMT | UART_LSR_THRE)
static
struct
async_struct
async_dbgcons
;
/*
* Wait for transmitter & holding register to empty
*/
static
inline
void
wait_for_xmitr
(
struct
async_struct
*
info
)
{
unsigned
int
status
,
tmout
=
1000000
;
do
{
status
=
serial_in
(
info
,
UART_LSR
);
if
(
status
&
UART_LSR_BI
)
lsr_break_flag
=
UART_LSR_BI
;
if
(
--
tmout
==
0
)
break
;
}
while
((
status
&
BOTH_EMPTY
)
!=
BOTH_EMPTY
);
/* Wait for flow control if necessary */
if
(
info
->
flags
&
ASYNC_CONS_FLOW
)
{
tmout
=
1000000
;
while
(
--
tmout
&&
((
serial_in
(
info
,
UART_MSR
)
&
UART_MSR_CTS
)
==
0
));
}
}
static
void
dbg_console_write
(
struct
console
*
co
,
const
char
*
s
,
unsigned
count
)
{
static
struct
async_struct
*
info
=
&
async_dbgcons
;
int
ier
;
unsigned
i
;
/*
* First save the IER then disable the interrupts
*/
ier
=
serial_in
(
info
,
UART_IER
);
serial_out
(
info
,
UART_IER
,
0x00
);
/*
* Now, do each character
*/
for
(
i
=
0
;
i
<
count
;
i
++
,
s
++
)
{
wait_for_xmitr
(
info
);
/*
* Send the character out.
* If a LF, also do CR...
*/
serial_out
(
info
,
UART_TX
,
*
s
);
if
(
*
s
==
10
)
{
wait_for_xmitr
(
info
);
serial_out
(
info
,
UART_TX
,
13
);
}
}
/*
* Finally, Wait for transmitter & holding register to empty
* and restore the IER
*/
wait_for_xmitr
(
info
);
serial_out
(
info
,
UART_IER
,
ier
);
}
#if (LINUX_VERSION_CODE <= 132114)
/* Linux 2.4.18 */
/*
* Receive character from the serial port
*/
static
int
dbg_console_wait_key
(
struct
console
*
console
)
{
static
struct
async_struct
*
info
;
int
ier
,
c
;
info
=
&
async_dbgcons
;
/*
* First save the IER then disable the interrupts so
* that the real driver for the port does not get the
* character.
*/
ier
=
serial_in
(
info
,
UART_IER
);
serial_out
(
info
,
UART_IER
,
0x00
);
while
((
serial_in
(
info
,
UART_LSR
)
&
UART_LSR_DR
)
==
0
);
c
=
serial_in
(
info
,
UART_RX
);
/*
* Restore the interrupts
*/
serial_out
(
info
,
UART_IER
,
ier
);
return
c
;
}
#endif
static
kdev_t
dbg_console_device
(
struct
console
*
c
)
{
return
MKDEV
(
TTY_MAJOR
,
80
+
c
->
index
);
}
static
int
__init
dbg_console_setup
(
struct
console
*
co
,
char
*
options
)
{
static
struct
async_struct
*
info
;
struct
serial_state
*
state
;
int
baud
=
BAUDRATE
;
int
baud_base
=
boot_cpu_data
.
bus_clock
;
int
bits
=
8
;
int
parity
=
'n'
;
int
doflow
=
0
;
unsigned
int
cflag
=
CREAD
|
HUPCL
|
CLOCAL
|
CRTSCTS
;
int
cval
;
char
*
s
;
if
(
options
)
{
baud
=
simple_strtoul
(
options
,
NULL
,
10
);
s
=
options
;
while
(
*
s
>=
'0'
&&
*
s
<=
'9'
)
s
++
;
if
(
*
s
)
parity
=
*
s
++
;
if
(
*
s
)
bits
=
*
s
++
-
'0'
;
if
(
*
s
)
doflow
=
(
*
s
++
==
'r'
);
}
co
->
flags
|=
CON_ENABLED
;
/*
* Now construct a cflag setting.
*/
switch
(
baud
)
{
case
1200
:
cflag
|=
B1200
;
break
;
case
2400
:
cflag
|=
B2400
;
break
;
case
4800
:
cflag
|=
B4800
;
break
;
case
19200
:
cflag
|=
B19200
;
break
;
case
38400
:
cflag
|=
B38400
;
break
;
case
57600
:
cflag
|=
B57600
;
break
;
case
115200
:
cflag
|=
B115200
;
break
;
case
9600
:
default:
cflag
|=
B9600
;
baud
=
9600
;
break
;
}
switch
(
bits
)
{
case
7
:
cflag
|=
CS7
;
break
;
default:
case
8
:
cflag
|=
CS8
;
break
;
}
switch
(
parity
)
{
case
'o'
:
case
'O'
:
cflag
|=
PARODD
;
break
;
case
'e'
:
case
'E'
:
cflag
|=
PARENB
;
break
;
}
co
->
cflag
=
cflag
;
state
=
rs_table
+
co
->
index
;
if
(
doflow
)
state
->
flags
|=
ASYNC_CONS_FLOW
;
info
=
&
async_dbgcons
;
info
->
magic
=
SERIAL_MAGIC
;
info
->
state
=
state
;
info
->
port
=
state
->
port
;
info
->
flags
=
state
->
flags
;
info
->
io_type
=
state
->
io_type
;
info
->
iomem_base
=
state
->
iomem_base
;
info
->
iomem_reg_shift
=
state
->
iomem_reg_shift
;
cval
=
(
baud_base
/
(
baud
*
4
))
-
1
;
serial_outp
(
info
,
UART_LCR
,
0x0300
);
/* init status */
//serial_outp(info, UART_MOD1, 0x0800); /* 8bit */
serial_outp
(
info
,
UART_MOD0
,
0x80
);
/* cts/rts 1stop nonpari */
//serial_outp(info, UART_MOD0, 0x180); /* rts 1stop nonpari */
//serial_outp(info, UART_MOD0, 0xc0); /* cts/rts 1stop nonpari */
serial_outp
(
info
,
UART_BAUR
,
cval
);
/* set baurate reg */
//serial_outp(info, UART_RBAUR, adj); /* set adj baurate reg */
serial_outp
(
info
,
UART_IER
,
0x00
);
/* intr mask */
serial_outp
(
info
,
UART_LCR
,
0x03
);
return
0
;
}
static
struct
console
cons
=
{
name:
"ttyD"
,
write:
dbg_console_write
,
device:
dbg_console_device
,
#if (LINUX_VERSION_CODE <= 132114)
/* Linux 2.4.18 */
wait_key:
dbg_console_wait_key
,
#endif
setup:
dbg_console_setup
,
flags:
CON_PRINTBUFFER
,
index:
-
1
,
};
/*
* Register console.
*/
void
__init
psio_console_init
(
void
)
{
register_console
(
&
cons
);
}
arch/m32r/drivers/m32r_cfc.c
deleted
100644 → 0
View file @
ef8932e3
/*
* linux/arch/m32r/drivers/m32r_cfc.c
*
* Device driver for the CFC functionality of M32R.
*
* Copyright (c) 2001, 2002, 2003, 2004
* Hiroyuki Kondo, Naoto Sugai, Hayato Fujiwara
*/
#include <linux/module.h>
#include <linux/moduleparam.h>
#include <linux/init.h>
#include <linux/config.h>
#include <linux/types.h>
#include <linux/fcntl.h>
#include <linux/string.h>
#include <linux/kernel.h>
#include <linux/errno.h>
#include <linux/timer.h>
#include <linux/sched.h>
#include <linux/slab.h>
#include <linux/ioport.h>
#include <linux/delay.h>
#include <linux/workqueue.h>
#include <linux/interrupt.h>
#include <linux/device.h>
#include <asm/irq.h>
#include <asm/io.h>
#include <asm/bitops.h>
#include <asm/system.h>
#include <pcmcia/version.h>
#include <pcmcia/cs_types.h>
#include <pcmcia/ss.h>
#include <pcmcia/cs.h>
#undef MAX_IO_WIN
/* FIXME */
#define MAX_IO_WIN 1
#undef MAX_WIN
/* FIXME */
#define MAX_WIN 1
#include "m32r_cfc.h"
#if !defined(COFIG_PLAT_USRV)
#define PCMCIA_DEBUG 3
#endif
/* COFIG_PLAT_USRV */
#ifdef PCMCIA_DEBUG
int
m32r_cfc_debug
=
PCMCIA_DEBUG
;
module_param
(
m32r_cfc_debug
,
int
,
0444
);
#define DEBUG(n, args...) if (m32r_cfc_debug>(n)) printk(args)
#else
#define DEBUG(n, args...) do { } while (0)
#endif
/* Poll status interval -- 0 means default to interrupt */
static
int
poll_interval
=
0
;
typedef
enum
pcc_space
{
as_none
=
0
,
as_comm
,
as_attr
,
as_io
}
pcc_as_t
;
typedef
struct
pcc_socket
{
u_short
type
,
flags
;
struct
pcmcia_socket
socket
;
unsigned
int
number
;
ioaddr_t
ioaddr
;
u_long
mapaddr
;
u_long
base
;
/* PCC register base */
u_char
cs_irq1
,
cs_irq2
,
intr
;
pccard_io_map
io_map
[
MAX_IO_WIN
];
pccard_mem_map
mem_map
[
MAX_WIN
];
u_char
io_win
;
u_char
mem_win
;
pcc_as_t
current_space
;
u_char
last_iodbex
;
#ifdef CHAOS_PCC_DEBUG
u_char
last_iosize
;
#endif
#ifdef CONFIG_PROC_FS
struct
proc_dir_entry
*
proc
;
#endif
}
pcc_socket_t
;
static
int
pcc_sockets
=
0
;
static
pcc_socket_t
socket
[
M32R_MAX_PCC
]
=
{
{
0
,
},
/* ... */
};
#define ISA_LOCK(n, f) do { } while (0)
#define ISA_UNLOCK(n, f) do { } while (0)
/*====================================================================*/
static
unsigned
int
pcc_get
(
u_short
,
unsigned
int
);
static
void
pcc_set
(
u_short
,
unsigned
int
,
unsigned
int
);
static
spinlock_t
pcc_lock
=
SPIN_LOCK_UNLOCKED
;
#if !defined(CONFIG_PLAT_USRV)
static
__inline__
u_long
pcc_port2addr
(
unsigned
long
port
,
int
size
)
{
u_long
addr
=
0
;
u_long
odd
;
if
(
size
==
1
)
{
/* byte access */
odd
=
(
port
&
1
)
<<
11
;
port
-=
port
&
1
;
addr
=
CFC_IO_MAPBASE_BYTE
-
CFC_IOPORT_BASE
+
odd
+
port
;
}
else
if
(
size
==
2
)
addr
=
CFC_IO_MAPBASE_WORD
-
CFC_IOPORT_BASE
+
port
;
return
addr
;
}
#else
/* CONFIG_PLAT_USRV */
static
__inline__
u_long
pcc_port2addr
(
unsigned
long
port
,
int
size
)
{
u_long
odd
;
u_long
addr
=
((
port
-
CFC_IOPORT_BASE
)
&
0xf000
)
<<
8
;
if
(
size
==
1
)
{
/* byte access */
odd
=
port
&
1
;
port
-=
odd
;
odd
<<=
11
;
addr
=
(
addr
|
CFC_IO_MAPBASE_BYTE
)
+
odd
+
(
port
&
0xfff
);
}
else
if
(
size
==
2
)
/* word access */
addr
=
(
addr
|
CFC_IO_MAPBASE_WORD
)
+
(
port
&
0xfff
);
return
addr
;
}
#endif
/* CONFIG_PLAT_USRV */
void
pcc_ioread_byte
(
int
sock
,
unsigned
long
port
,
void
*
buf
,
size_t
size
,
size_t
nmemb
,
int
flag
)
{
u_long
addr
;
unsigned
char
*
bp
=
(
unsigned
char
*
)
buf
;
unsigned
long
flags
;
DEBUG
(
3
,
"m32r_cfc: pcc_ioread_byte: sock=%d, port=%#lx, buf=%p, "
"size=%u, nmemb=%d, flag=%d
\n
"
,
sock
,
port
,
buf
,
size
,
nmemb
,
flag
);
addr
=
pcc_port2addr
(
port
,
1
);
if
(
!
addr
)
{
printk
(
"m32r_cfc:ioread_byte null port :%#lx
\n
"
,
port
);
return
;
}
DEBUG
(
3
,
"m32r_cfc: pcc_ioread_byte: addr=%#lx
\n
"
,
addr
);
spin_lock_irqsave
(
&
pcc_lock
,
flags
);
/* read Byte */
while
(
nmemb
--
)
*
bp
++
=
readb
(
addr
);
spin_unlock_irqrestore
(
&
pcc_lock
,
flags
);
}
void
pcc_ioread_word
(
int
sock
,
unsigned
long
port
,
void
*
buf
,
size_t
size
,
size_t
nmemb
,
int
flag
)
{
u_long
addr
;
unsigned
short
*
bp
=
(
unsigned
short
*
)
buf
;
unsigned
long
flags
;
DEBUG
(
3
,
"m32r_cfc: pcc_ioread_word: sock=%d, port=%#lx, "
"buf=%p, size=%u, nmemb=%d, flag=%d
\n
"
,
sock
,
port
,
buf
,
size
,
nmemb
,
flag
);
if
(
size
!=
2
)
printk
(
"m32r_cfc: ioread_word :illigal size %u : %#lx
\n
"
,
size
,
port
);
if
(
size
==
9
)
printk
(
"m32r_cfc: ioread_word :insw
\n
"
);
addr
=
pcc_port2addr
(
port
,
2
);
if
(
!
addr
)
{
printk
(
"m32r_cfc:ioread_word null port :%#lx
\n
"
,
port
);
return
;
}
DEBUG
(
3
,
"m32r_cfc: pcc_ioread_word: addr=%#lx
\n
"
,
addr
);
spin_lock_irqsave
(
&
pcc_lock
,
flags
);
/* read Word */
while
(
nmemb
--
)
*
bp
++
=
readw
(
addr
);
spin_unlock_irqrestore
(
&
pcc_lock
,
flags
);
}
void
pcc_iowrite_byte
(
int
sock
,
unsigned
long
port
,
void
*
buf
,
size_t
size
,
size_t
nmemb
,
int
flag
)
{
u_long
addr
;
unsigned
char
*
bp
=
(
unsigned
char
*
)
buf
;
unsigned
long
flags
;
DEBUG
(
3
,
"m32r_cfc: pcc_iowrite_byte: sock=%d, port=%#lx, "
"buf=%p, size=%u, nmemb=%d, flag=%d
\n
"
,
sock
,
port
,
buf
,
size
,
nmemb
,
flag
);
/* write Byte */
addr
=
pcc_port2addr
(
port
,
1
);
if
(
!
addr
)
{
printk
(
"m32r_cfc:iowrite_byte null port:%#lx
\n
"
,
port
);
return
;
}
DEBUG
(
3
,
"m32r_cfc: pcc_iowrite_byte: addr=%#lx
\n
"
,
addr
);
spin_lock_irqsave
(
&
pcc_lock
,
flags
);
while
(
nmemb
--
)
writeb
(
*
bp
++
,
addr
);
spin_unlock_irqrestore
(
&
pcc_lock
,
flags
);
}
void
pcc_iowrite_word
(
int
sock
,
unsigned
long
port
,
void
*
buf
,
size_t
size
,
size_t
nmemb
,
int
flag
)
{
u_long
addr
;
unsigned
short
*
bp
=
(
unsigned
short
*
)
buf
;
unsigned
long
flags
;
DEBUG
(
3
,
"m32r_cfc: pcc_iowrite_word: sock=%d, port=%#lx, "
"buf=%p, size=%u, nmemb=%d, flag=%d
\n
"
,
sock
,
port
,
buf
,
size
,
nmemb
,
flag
);
if
(
size
!=
2
)
printk
(
"m32r_cfc: iowrite_word :illigal size %u : %#lx
\n
"
,
size
,
port
);
if
(
size
==
9
)
printk
(
"m32r_cfc: iowrite_word :outsw
\n
"
);
addr
=
pcc_port2addr
(
port
,
2
);
if
(
!
addr
)
{
printk
(
"m32r_cfc:iowrite_word null addr :%#lx
\n
"
,
port
);
return
;
}
#if 1
if
(
addr
&
1
)
{
printk
(
"m32r_cfc:iowrite_word port addr (%#lx):%#lx
\n
"
,
port
,
addr
);
return
;
}
#endif
DEBUG
(
3
,
"m32r_cfc: pcc_iowrite_word: addr=%#lx
\n
"
,
addr
);
spin_lock_irqsave
(
&
pcc_lock
,
flags
);
while
(
nmemb
--
)
writew
(
*
bp
++
,
addr
);
spin_unlock_irqrestore
(
&
pcc_lock
,
flags
);
}
/*====================================================================*/
#define IS_ALIVE 0x8000
typedef
struct
pcc_t
{
char
*
name
;
u_short
flags
;
}
pcc_t
;
static
pcc_t
pcc
[]
=
{
#if !defined(CONFIG_PLAT_USRV)
{
"m32r_cfc"
,
0
},
{
""
,
0
},
#else
/* CONFIG_PLAT_USRV */
{
"m32r_cfc"
,
0
},
{
"m32r_cfc"
,
0
},
{
"m32r_cfc"
,
0
},
{
"m32r_cfc"
,
0
},
{
"m32r_cfc"
,
0
},
{
""
,
0
},
#endif
/* CONFIG_PLAT_USRV */
};
static
irqreturn_t
pcc_interrupt
(
int
,
void
*
,
struct
pt_regs
*
regs
);
/*====================================================================*/
static
struct
timer_list
poll_timer
;
static
unsigned
int
pcc_get
(
u_short
sock
,
unsigned
int
reg
)
{
unsigned
int
val
=
inw
(
reg
);
DEBUG
(
3
,
"m32r_cfc: pcc_get: reg(0x%08x)=0x%04x
\n
"
,
reg
,
val
);
return
val
;
}
static
void
pcc_set
(
u_short
sock
,
unsigned
int
reg
,
unsigned
int
data
)
{
outw
(
data
,
reg
);
DEBUG
(
3
,
"m32r_cfc: pcc_set: reg(0x%08x)=0x%04x
\n
"
,
reg
,
data
);
}
/*======================================================================
See if a card is present, powered up, in IO mode, and already
bound to a (non PC Card) Linux driver. We leave these alone.
We make an exception for cards that seem to be serial devices.
======================================================================*/
static
int
__init
is_alive
(
u_short
sock
)
{
unsigned
int
stat
;
DEBUG
(
3
,
"m32r_cfc: is_alive:
\n
"
);
printk
(
"CF: "
);
stat
=
pcc_get
(
sock
,
(
unsigned
int
)
PLD_CFSTS
);
if
(
!
stat
)
printk
(
"No "
);
printk
(
"Card is detected at socket %d : stat = 0x%08x
\n
"
,
sock
,
stat
);
DEBUG
(
3
,
"m32r_cfc: is_alive: sock stat is 0x%04x
\n
"
,
stat
);
return
0
;
}
static
void
add_pcc_socket
(
ulong
base
,
int
irq
,
ulong
mapaddr
,
ioaddr_t
ioaddr
)
{
pcc_socket_t
*
t
=
&
socket
[
pcc_sockets
];
DEBUG
(
3
,
"m32r_cfc: add_pcc_socket: base=%#lx, irq=%d, "
"mapaddr=%#lx, ioaddr=%08x
\n
"
,
base
,
irq
,
mapaddr
,
ioaddr
);
/* add sockets */
t
->
ioaddr
=
ioaddr
;
t
->
mapaddr
=
mapaddr
;
#if !defined(CONFIG_PLAT_USRV)
t
->
base
=
0
;
t
->
flags
=
0
;
t
->
cs_irq1
=
irq
;
// insert irq
t
->
cs_irq2
=
irq
+
1
;
// eject irq
#else
/* CONFIG_PLAT_USRV */
t
->
base
=
base
;
t
->
flags
=
0
;
t
->
cs_irq1
=
0
;
// insert irq
t
->
cs_irq2
=
0
;
// eject irq
#endif
/* CONFIG_PLAT_USRV */
if
(
is_alive
(
pcc_sockets
))
t
->
flags
|=
IS_ALIVE
;
/* add pcc */
#if !defined(CONFIG_PLAT_USRV)
request_region
((
unsigned
int
)
PLD_CFRSTCR
,
0x20
,
"m32r_cfc"
);
#else
/* CONFIG_PLAT_USRV */
{
unsigned
int
reg_base
;
reg_base
=
(
unsigned
int
)
PLD_CFRSTCR
;
reg_base
|=
pcc_sockets
<<
8
;
request_region
(
reg_base
,
0x20
,
"m32r_cfc"
);
}
#endif
/* CONFIG_PLAT_USRV */
printk
(
KERN_INFO
" %s "
,
pcc
[
pcc_sockets
].
name
);
printk
(
"pcc at 0x%08lx
\n
"
,
t
->
base
);
/* Update socket interrupt information, capabilities */
t
->
socket
.
features
|=
(
SS_CAP_PCCARD
|
SS_CAP_STATIC_MAP
);
t
->
socket
.
map_size
=
M32R_PCC_MAPSIZE
;
t
->
socket
.
io_offset
=
ioaddr
;
/* use for io access offset */
t
->
socket
.
irq_mask
=
0
;
#if !defined(CONFIG_PLAT_USRV)
t
->
socket
.
pci_irq
=
PLD_IRQ_CFIREQ
;
/* card interrupt */
#else
/* CONFIG_PLAT_USRV */
t
->
socket
.
pci_irq
=
PLD_IRQ_CF0
+
pcc_sockets
;
#endif
/* CONFIG_PLAT_USRV */
#ifndef CONFIG_PLAT_USRV
/* insert interrupt */
request_irq
(
irq
,
pcc_interrupt
,
0
,
"m32r_cfc"
,
pcc_interrupt
);
/* eject interrupt */
request_irq
(
irq
+
1
,
pcc_interrupt
,
0
,
"m32r_cfc"
,
pcc_interrupt
);
DEBUG
(
3
,
"m32r_cfc: enable CFMSK, RDYSEL
\n
"
);
pcc_set
(
pcc_sockets
,
(
unsigned
int
)
PLD_CFIMASK
,
0x01
);
#endif
/* CONFIG_PLAT_USRV */
#if defined(CONFIG_PLAT_M32700UT) || defined(CONFIG_PLAT_USRV) || defined(CONFIG_PLAT_OPSPUT)
pcc_set
(
pcc_sockets
,
(
unsigned
int
)
PLD_CFCR1
,
0x0200
);
#endif
pcc_sockets
++
;
return
;
}
/*====================================================================*/
static
irqreturn_t
pcc_interrupt
(
int
irq
,
void
*
dev
,
struct
pt_regs
*
regs
)
{
int
i
;
u_int
events
=
0
;
int
handled
=
0
;
DEBUG
(
3
,
"m32r_cfc: pcc_interrupt: irq=%d, dev=%p, regs=%p
\n
"
,
irq
,
dev
,
regs
);
for
(
i
=
0
;
i
<
pcc_sockets
;
i
++
)
{
if
(
socket
[
i
].
cs_irq1
!=
irq
&&
socket
[
i
].
cs_irq2
!=
irq
)
continue
;
handled
=
1
;
DEBUG
(
3
,
"m32r_cfc: pcc_interrupt: socket %d irq 0x%02x "
,
i
,
irq
);
events
|=
SS_DETECT
;
/* insert or eject */
if
(
events
)
pcmcia_parse_events
(
&
socket
[
i
].
socket
,
events
);
}
DEBUG
(
3
,
"m32r_cfc: pcc_interrupt: done
\n
"
);
return
IRQ_RETVAL
(
handled
);
}
/* pcc_interrupt */
static
void
pcc_interrupt_wrapper
(
u_long
data
)
{
DEBUG
(
3
,
"m32r_cfc: pcc_interrupt_wrapper:
\n
"
);
pcc_interrupt
(
0
,
NULL
,
NULL
);
init_timer
(
&
poll_timer
);
poll_timer
.
expires
=
jiffies
+
poll_interval
;
add_timer
(
&
poll_timer
);
}
/*====================================================================*/
static
int
_pcc_get_status
(
u_short
sock
,
u_int
*
value
)
{
u_int
status
;
DEBUG
(
3
,
"m32r_cfc: _pcc_get_status:
\n
"
);
status
=
pcc_get
(
sock
,
(
unsigned
int
)
PLD_CFSTS
);
*
value
=
(
status
)
?
SS_DETECT
:
0
;
DEBUG
(
3
,
"m32r_cfc: _pcc_get_status: status=0x%08x
\n
"
,
status
);
#if defined(CONFIG_PLAT_M32700UT) || defined(CONFIG_PLAT_USRV) || defined(CONFIG_PLAT_OPSPUT)
if
(
status
)
{
/* enable CF power */
status
=
inw
((
unsigned
int
)
PLD_CPCR
);
if
(
!
(
status
&
PLD_CPCR_CF
))
{
DEBUG
(
3
,
"m32r_cfc: _pcc_get_status: "
"power on (CPCR=0x%08x)
\n
"
,
status
);
status
|=
PLD_CPCR_CF
;
outw
(
status
,
(
unsigned
int
)
PLD_CPCR
);
udelay
(
100
);
}
*
value
|=
SS_POWERON
;
pcc_set
(
sock
,
(
unsigned
int
)
PLD_CFBUFCR
,
0
);
/* enable buffer */
udelay
(
100
);
*
value
|=
SS_READY
;
/* always ready */
*
value
|=
SS_3VCARD
;
}
else
{
/* disable CF power */
status
=
inw
((
unsigned
int
)
PLD_CPCR
);
status
&=
~
PLD_CPCR_CF
;
outw
(
status
,
(
unsigned
int
)
PLD_CPCR
);
udelay
(
100
);
DEBUG
(
3
,
"m32r_cfc: _pcc_get_status: "
"power off (CPCR=0x%08x)
\n
"
,
status
);
}
#elif defined(CONFIG_PLAT_MAPPI2)
if
(
status
)
{
status
=
pcc_get
(
sock
,
(
unsigned
int
)
PLD_CPCR
);
if
(
status
==
0
)
{
/* power off */
pcc_set
(
sock
,
(
unsigned
int
)
PLD_CPCR
,
1
);
pcc_set
(
sock
,
(
unsigned
int
)
PLD_CFBUFCR
,
0
);
/* force buffer off for ZA-36 */
udelay
(
50
);
}
status
=
pcc_get
(
sock
,
(
unsigned
int
)
PLD_CFBUFCR
);
if
(
status
!=
0
)
{
/* buffer off */
pcc_set
(
sock
,
(
unsigned
int
)
PLD_CFBUFCR
,
0
);
udelay
(
50
);
pcc_set
(
sock
,
(
unsigned
int
)
PLD_CFRSTCR
,
0x0101
);
udelay
(
25
);
/* for IDE reset */
pcc_set
(
sock
,
(
unsigned
int
)
PLD_CFRSTCR
,
0x0100
);
mdelay
(
2
);
/* for IDE reset */
}
else
{
*
value
|=
SS_POWERON
;
*
value
|=
SS_READY
;
}
}
#else
#error no platform configuration
#endif
DEBUG
(
3
,
"m32r_cfc: _pcc_get_status: GetStatus(%d) = %#4.4x
\n
"
,
sock
,
*
value
);
return
0
;
}
/* _get_status */
/*====================================================================*/
static
int
_pcc_get_socket
(
u_short
sock
,
socket_state_t
*
state
)
{
// pcc_socket_t *t = &socket[sock];
#if defined(CONFIG_PLAT_M32700UT) || defined(CONFIG_PLAT_USRV) || defined(CONFIG_PLAT_OPSPUT)
state
->
flags
=
0
;
state
->
csc_mask
=
SS_DETECT
;
/* ??? */
state
->
csc_mask
|=
SS_READY
;
/* ??? */
state
->
io_irq
=
0
;
state
->
Vcc
=
33
;
/* 3.3V fixed */
state
->
Vpp
=
33
;
#endif
DEBUG
(
3
,
"m32r_cfc: GetSocket(%d) = flags %#3.3x, Vcc %d, Vpp %d, "
"io_irq %d, csc_mask %#2.2x
\n
"
,
sock
,
state
->
flags
,
state
->
Vcc
,
state
->
Vpp
,
state
->
io_irq
,
state
->
csc_mask
);
return
0
;
}
/* _get_socket */
/*====================================================================*/
static
int
_pcc_set_socket
(
u_short
sock
,
socket_state_t
*
state
)
{
#if defined(CONFIG_PLAT_MAPPI2)
u_long
reg
=
0
;
#endif
DEBUG
(
3
,
"m32r_cfc: SetSocket(%d, flags %#3.3x, Vcc %d, Vpp %d, "
"io_irq %d, csc_mask %#2.2x)
\n
"
,
sock
,
state
->
flags
,
state
->
Vcc
,
state
->
Vpp
,
state
->
io_irq
,
state
->
csc_mask
);
#if defined(CONFIG_PLAT_M32700UT) || defined(CONFIG_PLAT_USRV) || defined(CONFIG_PLAT_OPSPUT)
if
(
state
->
Vcc
)
{
if
((
state
->
Vcc
!=
50
)
&&
(
state
->
Vcc
!=
33
))
return
-
EINVAL
;
/* accept 5V and 3.3V */
}
#elif defined(CONFIG_PLAT_MAPPI2)
if
(
state
->
Vcc
)
{
/*
* 5V only
*/
if
(
state
->
Vcc
==
50
)
{
reg
|=
PCCSIGCR_VEN
;
}
else
{
return
-
EINVAL
;
}
}
#endif
if
(
state
->
flags
&
SS_RESET
)
{
DEBUG
(
3
,
":RESET
\n
"
);
pcc_set
(
sock
,(
unsigned
int
)
PLD_CFRSTCR
,
0x101
);
}
else
{
pcc_set
(
sock
,(
unsigned
int
)
PLD_CFRSTCR
,
0x100
);
}
if
(
state
->
flags
&
SS_OUTPUT_ENA
){
DEBUG
(
3
,
":OUTPUT_ENA
\n
"
);
/* bit clear */
pcc_set
(
sock
,(
unsigned
int
)
PLD_CFBUFCR
,
0
);
}
else
{
pcc_set
(
sock
,(
unsigned
int
)
PLD_CFBUFCR
,
1
);
}
#ifdef PCMCIA_DEBUG
if
(
state
->
flags
&
SS_IOCARD
){
DEBUG
(
3
,
":IOCARD"
);
}
if
(
state
->
flags
&
SS_PWR_AUTO
)
{
DEBUG
(
3
,
":PWR_AUTO"
);
}
if
(
state
->
csc_mask
&
SS_DETECT
)
DEBUG
(
3
,
":csc-SS_DETECT"
);
if
(
state
->
flags
&
SS_IOCARD
)
{
if
(
state
->
csc_mask
&
SS_STSCHG
)
DEBUG
(
3
,
":STSCHG"
);
}
else
{
if
(
state
->
csc_mask
&
SS_BATDEAD
)
DEBUG
(
3
,
":BATDEAD"
);
if
(
state
->
csc_mask
&
SS_BATWARN
)
DEBUG
(
3
,
":BATWARN"
);
if
(
state
->
csc_mask
&
SS_READY
)
DEBUG
(
3
,
":READY"
);
}
DEBUG
(
3
,
"
\n
"
);
#endif
return
0
;
}
/* _set_socket */
/*====================================================================*/
static
int
_pcc_set_io_map
(
u_short
sock
,
struct
pccard_io_map
*
io
)
{
u_char
map
;
DEBUG
(
3
,
"m32r_cfc: SetIOMap(%d, %d, %#2.2x, %d ns, "
"%#4.4x-%#4.4x)
\n
"
,
sock
,
io
->
map
,
io
->
flags
,
io
->
speed
,
io
->
start
,
io
->
stop
);
map
=
io
->
map
;
return
0
;
}
/* _set_io_map */
/*====================================================================*/
static
int
_pcc_set_mem_map
(
u_short
sock
,
struct
pccard_mem_map
*
mem
)
{
u_char
map
=
mem
->
map
;
u_long
mode
;
u_long
addr
;
pcc_socket_t
*
t
=
&
socket
[
sock
];
DEBUG
(
3
,
"m32r_cfc: SetMemMap(%d, %d, %#2.2x, %d ns, "
"%#5.5lx-%#5.5lx, %#5.5x)
\n
"
,
sock
,
map
,
mem
->
flags
,
mem
->
speed
,
mem
->
res
->
start
,
mem
->
res
->
end
,
mem
->
card_start
);
/*
* sanity check
*/
if
((
map
>
MAX_WIN
)
||
(
mem
->
card_start
>
0x3ffffff
)
||
(
mem
->
res
->
start
>
mem
->
res
->
end
))
{
return
-
EINVAL
;
}
/*
* de-activate
*/
if
((
mem
->
flags
&
MAP_ACTIVE
)
==
0
)
{
t
->
current_space
=
as_none
;
return
0
;
}
/*
* Disable first
*/
// pcc_set(sock, PCCR, 0);
/*
* Set mode
*/
if
(
mem
->
flags
&
MAP_ATTRIB
)
{
mode
=
PCMOD_AS_ATTRIB
|
PCMOD_CBSZ
;
t
->
current_space
=
as_attr
;
}
else
{
mode
=
0
;
/* common memory */
t
->
current_space
=
as_comm
;
}
// pcc_set(sock, PCMOD, mode);
/*
* Set address
*/
addr
=
t
->
mapaddr
+
(
mem
->
card_start
&
M32R_PCC_MAPMASK
);
// pcc_set(sock, PCADR, addr);
mem
->
res
->
start
=
addr
+
mem
->
card_start
;
mem
->
res
->
end
=
mem
->
res
->
start
+
(
M32R_PCC_MAPSIZE
-
1
);
/*
* Set timing
*/
// pcc_set(sock, PCATCR, pcc_access_timing);
/*
* Enable again
*/
// pcc_set(sock, PCCR, 1);
return
0
;
}
/* _set_mem_map */
#if 0 /* driver model ordering issue */
/*======================================================================
Routines for accessing socket information and register dumps via
/proc/bus/pccard/...
======================================================================*/
static ssize_t show_info(struct class_device *class_dev, char *buf)
{
pcc_socket_t *s = container_of(class_dev, struct pcc_socket,
socket.dev);
return sprintf(buf, "type: %s\nbase addr: 0x%08lx\n",
pcc[s->type].name, s->base);
}
static ssize_t show_exca(struct class_device *class_dev, char *buf)
{
/* FIXME */
return 0;
}
static CLASS_DEVICE_ATTR(info, S_IRUGO, show_info, NULL);
static CLASS_DEVICE_ATTR(exca, S_IRUGO, show_exca, NULL);
#endif
/*====================================================================*/
/* this is horribly ugly... proper locking needs to be done here at
* some time... */
#define LOCKED(x) do { \
int retval; \
unsigned long flags; \
spin_lock_irqsave(&pcc_lock, flags); \
retval = x; \
spin_unlock_irqrestore(&pcc_lock, flags); \
return retval; \
} while (0)
static
int
pcc_get_status
(
struct
pcmcia_socket
*
s
,
u_int
*
value
)
{
unsigned
int
sock
=
container_of
(
s
,
struct
pcc_socket
,
socket
)
->
number
;
if
(
socket
[
sock
].
flags
&
IS_ALIVE
)
{
DEBUG
(
3
,
"m32r_cfc: pcc_get_status: sock(%d) -EINVAL
\n
"
,
sock
);
*
value
=
0
;
return
-
EINVAL
;
}
DEBUG
(
3
,
"m32r_cfc: pcc_get_status: sock(%d)
\n
"
,
sock
);
LOCKED
(
_pcc_get_status
(
sock
,
value
));
}
static
int
pcc_get_socket
(
struct
pcmcia_socket
*
s
,
socket_state_t
*
state
)
{
unsigned
int
sock
=
container_of
(
s
,
struct
pcc_socket
,
socket
)
->
number
;
if
(
socket
[
sock
].
flags
&
IS_ALIVE
)
{
DEBUG
(
3
,
"m32r_cfc: pcc_get_socket: sock(%d) -EINVAL
\n
"
,
sock
);
return
-
EINVAL
;
}
DEBUG
(
3
,
"m32r_cfc: pcc_get_socket: sock(%d)
\n
"
,
sock
);
LOCKED
(
_pcc_get_socket
(
sock
,
state
));
}
static
int
pcc_set_socket
(
struct
pcmcia_socket
*
s
,
socket_state_t
*
state
)
{
unsigned
int
sock
=
container_of
(
s
,
struct
pcc_socket
,
socket
)
->
number
;
if
(
socket
[
sock
].
flags
&
IS_ALIVE
)
{
DEBUG
(
3
,
"m32r_cfc: pcc_set_socket: sock(%d) -EINVAL
\n
"
,
sock
);
return
-
EINVAL
;
}
DEBUG
(
3
,
"m32r_cfc: pcc_set_socket: sock(%d)
\n
"
,
sock
);
LOCKED
(
_pcc_set_socket
(
sock
,
state
));
}
static
int
pcc_set_io_map
(
struct
pcmcia_socket
*
s
,
struct
pccard_io_map
*
io
)
{
unsigned
int
sock
=
container_of
(
s
,
struct
pcc_socket
,
socket
)
->
number
;
if
(
socket
[
sock
].
flags
&
IS_ALIVE
)
{
DEBUG
(
3
,
"m32r_cfc: pcc_set_io_map: sock(%d) -EINVAL
\n
"
,
sock
);
return
-
EINVAL
;
}
DEBUG
(
3
,
"m32r_cfc: pcc_set_io_map: sock(%d)
\n
"
,
sock
);
LOCKED
(
_pcc_set_io_map
(
sock
,
io
));
}
static
int
pcc_set_mem_map
(
struct
pcmcia_socket
*
s
,
struct
pccard_mem_map
*
mem
)
{
unsigned
int
sock
=
container_of
(
s
,
struct
pcc_socket
,
socket
)
->
number
;
if
(
socket
[
sock
].
flags
&
IS_ALIVE
)
{
DEBUG
(
3
,
"m32r_cfc: pcc_set_mem_map: sock(%d) -EINVAL
\n
"
,
sock
);
return
-
EINVAL
;
}
DEBUG
(
3
,
"m32r_cfc: pcc_set_mem_map: sock(%d)
\n
"
,
sock
);
LOCKED
(
_pcc_set_mem_map
(
sock
,
mem
));
}
static
int
pcc_init
(
struct
pcmcia_socket
*
s
)
{
DEBUG
(
3
,
"m32r_cfc: pcc_init()
\n
"
);
return
0
;
}
static
int
pcc_suspend
(
struct
pcmcia_socket
*
sock
)
{
DEBUG
(
3
,
"m32r_cfc: pcc_suspend()
\n
"
);
return
pcc_set_socket
(
sock
,
&
dead_socket
);
}
static
struct
pccard_operations
pcc_operations
=
{
.
init
=
pcc_init
,
.
suspend
=
pcc_suspend
,
.
get_status
=
pcc_get_status
,
.
get_socket
=
pcc_get_socket
,
.
set_socket
=
pcc_set_socket
,
.
set_io_map
=
pcc_set_io_map
,
.
set_mem_map
=
pcc_set_mem_map
,
};
/*====================================================================*/
static
int
m32rpcc_suspend
(
struct
device
*
dev
,
u32
state
,
u32
level
)
{
int
ret
=
0
;
if
(
level
==
SUSPEND_SAVE_STATE
)
ret
=
pcmcia_socket_dev_suspend
(
dev
,
state
);
return
ret
;
}
static
int
m32rpcc_resume
(
struct
device
*
dev
,
u32
level
)
{
int
ret
=
0
;
if
(
level
==
RESUME_RESTORE_STATE
)
ret
=
pcmcia_socket_dev_resume
(
dev
);
return
ret
;
}
static
struct
device_driver
pcc_driver
=
{
.
name
=
"cfc"
,
.
bus
=
&
platform_bus_type
,
.
suspend
=
m32rpcc_suspend
,
.
resume
=
m32rpcc_resume
,
};
static
struct
platform_device
pcc_device
=
{
.
name
=
"cfc"
,
.
id
=
0
,
};
/*====================================================================*/
#define UT_CFC
static
int
__init
init_m32r_pcc
(
void
)
{
int
i
,
ret
;
ret
=
driver_register
(
&
pcc_driver
);
if
(
ret
)
return
ret
;
ret
=
platform_device_register
(
&
pcc_device
);
if
(
ret
){
driver_unregister
(
&
pcc_driver
);
return
ret
;
}
#if defined(CONFIG_PLAT_MAPPI2)
pcc_set
(
0
,
(
unsigned
int
)
PLD_CFCR0
,
0x0f0f
);
pcc_set
(
0
,
(
unsigned
int
)
PLD_CFCR1
,
0x0200
);
#endif
pcc_sockets
=
0
;
#if !defined(CONFIG_PLAT_USRV)
add_pcc_socket
(
M32R_PCC0_BASE
,
PLD_IRQ_CFC_INSERT
,
CFC_ATTR_MAPBASE
,
CFC_IOPORT_BASE
);
#else
/* CONFIG_PLAT_USRV */
{
ulong
base
,
mapaddr
;
ioaddr_t
ioaddr
;
for
(
i
=
0
;
i
<
M32R_MAX_PCC
;
i
++
)
{
base
=
(
ulong
)
PLD_CFRSTCR
;
base
=
base
|
(
i
<<
8
);
ioaddr
=
(
i
+
1
)
<<
12
;
mapaddr
=
CFC_ATTR_MAPBASE
|
(
i
<<
20
);
add_pcc_socket
(
base
,
0
,
mapaddr
,
ioaddr
);
}
}
#endif
/* CONFIG_PLAT_USRV */
if
(
pcc_sockets
==
0
)
{
printk
(
"socket is not found.
\n
"
);
platform_device_unregister
(
&
pcc_device
);
driver_unregister
(
&
pcc_driver
);
return
-
ENODEV
;
}
/* Set up interrupt handler(s) */
for
(
i
=
0
;
i
<
pcc_sockets
;
i
++
)
{
socket
[
i
].
socket
.
dev
.
dev
=
&
pcc_device
.
dev
;
socket
[
i
].
socket
.
ops
=
&
pcc_operations
;
socket
[
i
].
socket
.
owner
=
THIS_MODULE
;
socket
[
i
].
number
=
i
;
ret
=
pcmcia_register_socket
(
&
socket
[
i
].
socket
);
if
(
ret
&&
i
--
)
{
for
(;
i
>=
0
;
i
--
)
pcmcia_unregister_socket
(
&
socket
[
i
].
socket
);
break
;
}
#if 0 /* driver model ordering issue */
class_device_create_file(&socket[i].socket.dev,
&class_device_attr_info);
class_device_create_file(&socket[i].socket.dev,
&class_device_attr_exca);
#endif
}
/* Finally, schedule a polling interrupt */
if
(
poll_interval
!=
0
)
{
poll_timer
.
function
=
pcc_interrupt_wrapper
;
poll_timer
.
data
=
0
;
init_timer
(
&
poll_timer
);
poll_timer
.
expires
=
jiffies
+
poll_interval
;
add_timer
(
&
poll_timer
);
}
return
0
;
}
/* init_m32r_pcc */
static
void
__exit
exit_m32r_pcc
(
void
)
{
int
i
;
for
(
i
=
0
;
i
<
pcc_sockets
;
i
++
)
pcmcia_unregister_socket
(
&
socket
[
i
].
socket
);
platform_device_unregister
(
&
pcc_device
);
if
(
poll_interval
!=
0
)
del_timer_sync
(
&
poll_timer
);
driver_unregister
(
&
pcc_driver
);
}
/* exit_m32r_pcc */
module_init
(
init_m32r_pcc
);
module_exit
(
exit_m32r_pcc
);
MODULE_LICENSE
(
"Dual MPL/GPL"
);
/*====================================================================*/
arch/m32r/drivers/m32r_cfc.h
deleted
100644 → 0
View file @
ef8932e3
/*
* $Id$
*
* Copyright (C) 2001 by Hiroyuki Kondo
*/
#if !defined(CONFIG_PLAT_USRV)
#define M32R_MAX_PCC 2
#else
/* CONFIG_PLAT_USRV */
#define M32R_MAX_PCC CONFIG_CFC_NUM
#endif
/* CONFIG_PLAT_USRV */
/*
* M32R PC Card Controler
*/
#define M32R_PCC0_BASE 0x00ef7000
#define M32R_PCC1_BASE 0x00ef7020
/*
* Register offsets
*/
#define PCCR 0x00
#define PCADR 0x04
#define PCMOD 0x08
#define PCIRC 0x0c
#define PCCSIGCR 0x10
#define PCATCR 0x14
/*
* PCCR
*/
#define PCCR_PCEN (1UL<<(31-31))
/*
* PCIRC
*/
#define PCIRC_BWERR (1UL<<(31-7))
#define PCIRC_CDIN1 (1UL<<(31-14))
#define PCIRC_CDIN2 (1UL<<(31-15))
#define PCIRC_BEIEN (1UL<<(31-23))
#define PCIRC_CIIEN (1UL<<(31-30))
#define PCIRC_COIEN (1UL<<(31-31))
/*
* PCCSIGCR
*/
#define PCCSIGCR_SEN (1UL<<(31-3))
#define PCCSIGCR_VEN (1UL<<(31-7))
#define PCCSIGCR_CRST (1UL<<(31-15))
#define PCCSIGCR_COCR (1UL<<(31-31))
/*
*
*/
#define PCMOD_AS_ATTRIB (1UL<<(31-19))
#define PCMOD_AS_IO (1UL<<(31-18))
#define PCMOD_CBSZ (1UL<<(31-23))
/* set for 8bit */
#define PCMOD_DBEX (1UL<<(31-31))
/* set for excahnge */
/*
* M32R PCC Map addr
*/
#define M32R_PCC0_MAPBASE 0x14000000
#define M32R_PCC1_MAPBASE 0x16000000
#define M32R_PCC_MAPMAX 0x02000000
#define M32R_PCC_MAPSIZE 0x00001000
/* XXX */
#define M32R_PCC_MAPMASK (~(M32R_PCC_MAPMAX-1))
#define CFC_IOPORT_BASE 0x1000
#if !defined(CONFIG_PLAT_USRV)
#define CFC_ATTR_MAPBASE 0x0c014000
#define CFC_IO_MAPBASE_BYTE 0xac012000
#define CFC_IO_MAPBASE_WORD 0xac002000
#else
/* CONFIG_PLAT_USRV */
#define CFC_ATTR_MAPBASE 0x04014000
#define CFC_IO_MAPBASE_BYTE 0xa4012000
#define CFC_IO_MAPBASE_WORD 0xa4002000
#endif
/* CONFIG_PLAT_USRV */
arch/m32r/drivers/m32r_pcc.c
deleted
100644 → 0
View file @
ef8932e3
/*
* linux/arch/m32r/drivers/m32r_pcc.c
*
* Device driver for the PCMCIA functionality of M32R.
*
* Copyright (c) 2001, 2002, 2003, 2004
* Hiroyuki Kondo, Naoto Sugai, Hayato Fujiwara
*/
#include <linux/module.h>
#include <linux/moduleparam.h>
#include <linux/init.h>
#include <linux/config.h>
#include <linux/types.h>
#include <linux/fcntl.h>
#include <linux/string.h>
#include <linux/kernel.h>
#include <linux/errno.h>
#include <linux/timer.h>
#include <linux/sched.h>
#include <linux/slab.h>
#include <linux/ioport.h>
#include <linux/delay.h>
#include <linux/workqueue.h>
#include <linux/interrupt.h>
#include <linux/device.h>
#include <asm/irq.h>
#include <asm/io.h>
#include <asm/bitops.h>
#include <asm/system.h>
#include <asm/addrspace.h>
#include <pcmcia/version.h>
#include <pcmcia/cs_types.h>
#include <pcmcia/ss.h>
#include <pcmcia/cs.h>
/* XXX: should be moved into asm/irq.h */
#define PCC0_IRQ 24
#define PCC1_IRQ 25
#define CHAOS_PCC_DEBUG
#ifdef CHAOS_PCC_DEBUG
static
volatile
u_short
dummy_readbuf
;
#endif
#define PCC_DEBUG_DBEX
#define PCMCIA_DEBUG 0
#include "m32r_pcc.h"
#ifdef PCMCIA_DEBUG
int
pc_debug
=
PCMCIA_DEBUG
;
module_param
(
pc_debug
,
int
,
0444
);
#define DEBUG(n, args...) if (pc_debug>(n)) printk(args)
#else
#define DEBUG(n, args...) do { } while (0)
#endif
/* Poll status interval -- 0 means default to interrupt */
static
int
poll_interval
=
0
;
typedef
enum
pcc_space
{
as_none
=
0
,
as_comm
,
as_attr
,
as_io
}
pcc_as_t
;
typedef
struct
pcc_socket
{
u_short
type
,
flags
;
struct
pcmcia_socket
socket
;
unsigned
int
number
;
ioaddr_t
ioaddr
;
u_long
mapaddr
;
u_long
base
;
/* PCC register base */
u_char
cs_irq
,
intr
;
pccard_io_map
io_map
[
MAX_IO_WIN
];
pccard_mem_map
mem_map
[
MAX_WIN
];
u_char
io_win
;
u_char
mem_win
;
pcc_as_t
current_space
;
u_char
last_iodbex
;
#ifdef CHAOS_PCC_DEBUG
u_char
last_iosize
;
#endif
#ifdef CONFIG_PROC_FS
struct
proc_dir_entry
*
proc
;
#endif
}
pcc_socket_t
;
static
int
pcc_sockets
=
0
;
static
pcc_socket_t
socket
[
M32R_MAX_PCC
]
=
{
{
0
,
},
/* ... */
};
#define ISA_LOCK(n, f) do { } while (0)
#define ISA_UNLOCK(n, f) do { } while (0)
/*====================================================================*/
static
unsigned
int
pcc_get
(
u_short
,
unsigned
int
);
static
void
pcc_set
(
u_short
,
unsigned
int
,
unsigned
int
);
static
spinlock_t
pcc_lock
=
SPIN_LOCK_UNLOCKED
;
void
pcc_iorw
(
int
sock
,
unsigned
long
port
,
void
*
buf
,
size_t
size
,
size_t
nmemb
,
int
wr
,
int
flag
)
{
u_long
addr
;
u_long
flags
;
int
need_ex
;
#ifdef PCC_DEBUG_DBEX
int
_dbex
;
#endif
pcc_socket_t
*
t
=
&
socket
[
sock
];
#ifdef CHAOS_PCC_DEBUG
int
map_changed
=
0
;
#endif
/* Need lock ? */
spin_lock_irqsave
(
&
pcc_lock
,
flags
);
/*
* Check if need dbex
*/
need_ex
=
(
size
>
1
&&
flag
==
0
)
?
PCMOD_DBEX
:
0
;
#ifdef PCC_DEBUG_DBEX
_dbex
=
need_ex
;
need_ex
=
0
;
#endif
/*
* calculate access address
*/
addr
=
t
->
mapaddr
+
port
-
t
->
ioaddr
+
KSEG1
;
/* XXX */
/*
* Check current mapping
*/
if
(
t
->
current_space
!=
as_io
||
t
->
last_iodbex
!=
need_ex
)
{
u_long
cbsz
;
/*
* Disable first
*/
pcc_set
(
sock
,
PCCR
,
0
);
/*
* Set mode and io address
*/
cbsz
=
(
t
->
flags
&
MAP_16BIT
)
?
0
:
PCMOD_CBSZ
;
pcc_set
(
sock
,
PCMOD
,
PCMOD_AS_IO
|
cbsz
|
need_ex
);
pcc_set
(
sock
,
PCADR
,
addr
&
0x1ff00000
);
/*
* Enable and read it
*/
pcc_set
(
sock
,
PCCR
,
1
);
#ifdef CHAOS_PCC_DEBUG
#if 0
map_changed = (t->current_space == as_attr && size == 2); /* XXX */
#else
map_changed
=
1
;
#endif
#endif
t
->
current_space
=
as_io
;
}
/*
* access to IO space
*/
if
(
size
==
1
)
{
/* Byte */
unsigned
char
*
bp
=
(
unsigned
char
*
)
buf
;
#ifdef CHAOS_DEBUG
if
(
map_changed
)
{
dummy_readbuf
=
readb
(
addr
);
}
#endif
if
(
wr
)
{
/* write Byte */
while
(
nmemb
--
)
{
writeb
(
*
bp
++
,
addr
);
}
}
else
{
/* read Byte */
while
(
nmemb
--
)
{
*
bp
++
=
readb
(
addr
);
}
}
}
else
{
/* Word */
unsigned
short
*
bp
=
(
unsigned
short
*
)
buf
;
#ifdef CHAOS_PCC_DEBUG
if
(
map_changed
)
{
dummy_readbuf
=
readw
(
addr
);
}
#endif
if
(
wr
)
{
/* write Word */
while
(
nmemb
--
)
{
#ifdef PCC_DEBUG_DBEX
if
(
_dbex
)
{
unsigned
char
*
cp
=
(
unsigned
char
*
)
bp
;
unsigned
short
tmp
;
tmp
=
cp
[
1
]
<<
8
|
cp
[
0
];
writew
(
tmp
,
addr
);
bp
++
;
}
else
#endif
writew
(
*
bp
++
,
addr
);
}
}
else
{
/* read Word */
while
(
nmemb
--
)
{
#ifdef PCC_DEBUG_DBEX
if
(
_dbex
)
{
unsigned
char
*
cp
=
(
unsigned
char
*
)
bp
;
unsigned
short
tmp
;
tmp
=
readw
(
addr
);
cp
[
0
]
=
tmp
&
0xff
;
cp
[
1
]
=
(
tmp
>>
8
)
&
0xff
;
bp
++
;
}
else
#endif
*
bp
++
=
readw
(
addr
);
}
}
}
#if 1
/* addr is no longer used */
if
((
addr
=
pcc_get
(
sock
,
PCIRC
))
&
PCIRC_BWERR
)
{
printk
(
"m32r_pcc: BWERR detected : port 0x%04lx : iosize %dbit
\n
"
,
port
,
size
*
8
);
pcc_set
(
sock
,
PCIRC
,
addr
);
}
#endif
/*
* save state
*/
t
->
last_iosize
=
size
;
t
->
last_iodbex
=
need_ex
;
/* Need lock ? */
spin_unlock_irqrestore
(
&
pcc_lock
,
flags
);
return
;
}
void
pcc_ioread
(
int
sock
,
unsigned
long
port
,
void
*
buf
,
size_t
size
,
size_t
nmemb
,
int
flag
)
{
pcc_iorw
(
sock
,
port
,
buf
,
size
,
nmemb
,
0
,
flag
);
}
void
pcc_iowrite
(
int
sock
,
unsigned
long
port
,
void
*
buf
,
size_t
size
,
size_t
nmemb
,
int
flag
)
{
pcc_iorw
(
sock
,
port
,
buf
,
size
,
nmemb
,
1
,
flag
);
}
/*====================================================================*/
#define IS_ALIVE 0x8000
typedef
struct
pcc_t
{
char
*
name
;
u_short
flags
;
}
pcc_t
;
static
pcc_t
pcc
[]
=
{
{
"xnux2"
,
0
},
{
"xnux2"
,
0
},
};
static
irqreturn_t
pcc_interrupt
(
int
,
void
*
,
struct
pt_regs
*
);
/*====================================================================*/
static
struct
timer_list
poll_timer
;
static
unsigned
int
pcc_get
(
u_short
sock
,
unsigned
int
reg
)
{
return
inl
(
socket
[
sock
].
base
+
reg
);
}
static
void
pcc_set
(
u_short
sock
,
unsigned
int
reg
,
unsigned
int
data
)
{
outl
(
data
,
socket
[
sock
].
base
+
reg
);
}
/*======================================================================
See if a card is present, powered up, in IO mode, and already
bound to a (non PC Card) Linux driver. We leave these alone.
We make an exception for cards that seem to be serial devices.
======================================================================*/
static
int
__init
is_alive
(
u_short
sock
)
{
unsigned
int
stat
;
unsigned
int
f
;
stat
=
pcc_get
(
sock
,
PCIRC
);
f
=
(
stat
&
(
PCIRC_CDIN1
|
PCIRC_CDIN2
))
>>
16
;
if
(
!
f
){
printk
(
"m32r_pcc: No Card is detected at socket %d : stat = 0x%08x
\n
"
,
stat
,
sock
);
return
0
;
}
if
(
f
!=
3
)
printk
(
"m32r_pcc: Insertion fail (%.8x) at socket %d
\n
"
,
stat
,
sock
);
else
printk
(
"m32r_pcc: Card is Inserted at socket %d(%.8x)
\n
"
,
sock
,
stat
);
return
0
;
}
static
void
add_pcc_socket
(
ulong
base
,
int
irq
,
ulong
mapaddr
,
ioaddr_t
ioaddr
)
{
pcc_socket_t
*
t
=
&
socket
[
pcc_sockets
];
/* add sockets */
t
->
ioaddr
=
ioaddr
;
t
->
mapaddr
=
mapaddr
;
t
->
base
=
base
;
#ifdef CHAOS_PCC_DEBUG
t
->
flags
=
MAP_16BIT
;
#else
t
->
flags
=
0
;
#endif
if
(
is_alive
(
pcc_sockets
))
t
->
flags
|=
IS_ALIVE
;
/* add pcc */
if
(
t
->
base
>
0
)
{
request_region
(
t
->
base
,
0x20
,
"m32r-pcc"
);
}
printk
(
KERN_INFO
" %s "
,
pcc
[
pcc_sockets
].
name
);
printk
(
"pcc at 0x%08lx
\n
"
,
t
->
base
);
/* Update socket interrupt information, capabilities */
t
->
socket
.
features
|=
(
SS_CAP_PCCARD
|
SS_CAP_STATIC_MAP
);
t
->
socket
.
map_size
=
M32R_PCC_MAPSIZE
;
t
->
socket
.
io_offset
=
ioaddr
;
/* use for io access offset */
t
->
socket
.
irq_mask
=
0
;
t
->
socket
.
pci_irq
=
2
+
pcc_sockets
;
/* XXX */
request_irq
(
irq
,
pcc_interrupt
,
0
,
"m32r-pcc"
,
pcc_interrupt
);
pcc_sockets
++
;
return
;
}
/*====================================================================*/
static
irqreturn_t
pcc_interrupt
(
int
irq
,
void
*
dev
,
struct
pt_regs
*
regs
)
{
int
i
,
j
,
irc
;
u_int
events
,
active
;
int
handled
=
0
;
DEBUG
(
4
,
"m32r: pcc_interrupt(%d)
\n
"
,
irq
);
for
(
j
=
0
;
j
<
20
;
j
++
)
{
active
=
0
;
for
(
i
=
0
;
i
<
pcc_sockets
;
i
++
)
{
if
((
socket
[
i
].
cs_irq
!=
irq
)
&&
(
socket
[
i
].
socket
.
pci_irq
!=
irq
))
continue
;
handled
=
1
;
irc
=
pcc_get
(
i
,
PCIRC
);
irc
>>=
16
;
DEBUG
(
2
,
"m32r-pcc:interrput: socket %d pcirc 0x%02x "
,
i
,
irc
);
if
(
!
irc
)
continue
;
events
=
(
irc
)
?
SS_DETECT
:
0
;
events
|=
(
pcc_get
(
i
,
PCCR
)
&
PCCR_PCEN
)
?
SS_READY
:
0
;
DEBUG
(
2
,
" event 0x%02x
\n
"
,
events
);
if
(
events
)
pcmcia_parse_events
(
&
socket
[
i
].
socket
,
events
);
active
|=
events
;
active
=
0
;
}
if
(
!
active
)
break
;
}
if
(
j
==
20
)
printk
(
KERN_NOTICE
"m32r-pcc: infinite loop in interrupt handler
\n
"
);
DEBUG
(
4
,
"m32r-pcc: interrupt done
\n
"
);
return
IRQ_RETVAL
(
handled
);
}
/* pcc_interrupt */
static
void
pcc_interrupt_wrapper
(
u_long
data
)
{
pcc_interrupt
(
0
,
NULL
,
NULL
);
init_timer
(
&
poll_timer
);
poll_timer
.
expires
=
jiffies
+
poll_interval
;
add_timer
(
&
poll_timer
);
}
/*====================================================================*/
static
int
_pcc_get_status
(
u_short
sock
,
u_int
*
value
)
{
u_int
status
;
status
=
pcc_get
(
sock
,
PCIRC
);
*
value
=
((
status
&
PCIRC_CDIN1
)
&&
(
status
&
PCIRC_CDIN2
))
?
SS_DETECT
:
0
;
status
=
pcc_get
(
sock
,
PCCR
);
#if 0
*value |= (status & PCCR_PCEN) ? SS_READY : 0;
#else
*
value
|=
SS_READY
;
/* XXX: always */
#endif
status
=
pcc_get
(
sock
,
PCCSIGCR
);
*
value
|=
(
status
&
PCCSIGCR_VEN
)
?
SS_POWERON
:
0
;
DEBUG
(
3
,
"m32r-pcc: GetStatus(%d) = %#4.4x
\n
"
,
sock
,
*
value
);
return
0
;
}
/* _get_status */
/*====================================================================*/
static
int
_pcc_get_socket
(
u_short
sock
,
socket_state_t
*
state
)
{
DEBUG
(
3
,
"m32r-pcc: GetSocket(%d) = flags %#3.3x, Vcc %d, Vpp %d, "
"io_irq %d, csc_mask %#2.2x
\n
"
,
sock
,
state
->
flags
,
state
->
Vcc
,
state
->
Vpp
,
state
->
io_irq
,
state
->
csc_mask
);
return
0
;
}
/* _get_socket */
/*====================================================================*/
static
int
_pcc_set_socket
(
u_short
sock
,
socket_state_t
*
state
)
{
u_long
reg
=
0
;
DEBUG
(
3
,
"m32r-pcc: SetSocket(%d, flags %#3.3x, Vcc %d, Vpp %d, "
"io_irq %d, csc_mask %#2.2x)"
,
sock
,
state
->
flags
,
state
->
Vcc
,
state
->
Vpp
,
state
->
io_irq
,
state
->
csc_mask
);
if
(
state
->
Vcc
)
{
/*
* 5V only
*/
if
(
state
->
Vcc
==
50
)
{
reg
|=
PCCSIGCR_VEN
;
}
else
{
return
-
EINVAL
;
}
}
if
(
state
->
flags
&
SS_RESET
)
{
DEBUG
(
3
,
":RESET
\n
"
);
reg
|=
PCCSIGCR_CRST
;
}
if
(
state
->
flags
&
SS_OUTPUT_ENA
){
DEBUG
(
3
,
":OUTPUT_ENA
\n
"
);
/* bit clear */
}
else
{
reg
|=
PCCSIGCR_SEN
;
}
pcc_set
(
sock
,
PCCSIGCR
,
reg
);
#ifdef PCMCIA_DEBUG
if
(
state
->
flags
&
SS_IOCARD
){
DEBUG
(
3
,
":IOCARD"
);
}
if
(
state
->
flags
&
SS_PWR_AUTO
)
{
DEBUG
(
3
,
":PWR_AUTO"
);
}
if
(
state
->
csc_mask
&
SS_DETECT
)
DEBUG
(
3
,
":csc-SS_DETECT"
);
if
(
state
->
flags
&
SS_IOCARD
)
{
if
(
state
->
csc_mask
&
SS_STSCHG
)
DEBUG
(
3
,
":STSCHG"
);
}
else
{
if
(
state
->
csc_mask
&
SS_BATDEAD
)
DEBUG
(
3
,
":BATDEAD"
);
if
(
state
->
csc_mask
&
SS_BATWARN
)
DEBUG
(
3
,
":BATWARN"
);
if
(
state
->
csc_mask
&
SS_READY
)
DEBUG
(
3
,
":READY"
);
}
DEBUG
(
3
,
"
\n
"
);
#endif
return
0
;
}
/* _set_socket */
/*====================================================================*/
static
int
_pcc_set_io_map
(
u_short
sock
,
struct
pccard_io_map
*
io
)
{
u_char
map
;
DEBUG
(
3
,
"m32r-pcc: SetIOMap(%d, %d, %#2.2x, %d ns, "
"%#4.4x-%#4.4x)
\n
"
,
sock
,
io
->
map
,
io
->
flags
,
io
->
speed
,
io
->
start
,
io
->
stop
);
map
=
io
->
map
;
return
0
;
}
/* _set_io_map */
/*====================================================================*/
static
int
_pcc_set_mem_map
(
u_short
sock
,
struct
pccard_mem_map
*
mem
)
{
u_char
map
=
mem
->
map
;
u_long
mode
;
u_long
addr
;
pcc_socket_t
*
t
=
&
socket
[
sock
];
#ifdef CHAOS_PCC_DEBUG
#if 0
pcc_as_t last = t->current_space;
#endif
#endif
DEBUG
(
3
,
"m32r-pcc: SetMemMap(%d, %d, %#2.2x, %d ns, "
"%#5.5lx-%#5.5lx, %#5.5x)
\n
"
,
sock
,
map
,
mem
->
flags
,
mem
->
speed
,
mem
->
res
->
start
,
mem
->
res
->
end
,
mem
->
card_start
);
/*
* sanity check
*/
if
((
map
>
MAX_WIN
)
||
(
mem
->
card_start
>
0x3ffffff
)
||
(
mem
->
res
->
start
>
mem
->
res
->
end
))
{
return
-
EINVAL
;
}
/*
* de-activate
*/
if
((
mem
->
flags
&
MAP_ACTIVE
)
==
0
)
{
t
->
current_space
=
as_none
;
return
0
;
}
/*
* Disable first
*/
pcc_set
(
sock
,
PCCR
,
0
);
/*
* Set mode
*/
if
(
mem
->
flags
&
MAP_ATTRIB
)
{
mode
=
PCMOD_AS_ATTRIB
|
PCMOD_CBSZ
;
t
->
current_space
=
as_attr
;
}
else
{
mode
=
0
;
/* common memory */
t
->
current_space
=
as_comm
;
}
pcc_set
(
sock
,
PCMOD
,
mode
);
/*
* Set address
*/
addr
=
t
->
mapaddr
+
(
mem
->
card_start
&
M32R_PCC_MAPMASK
);
pcc_set
(
sock
,
PCADR
,
addr
);
mem
->
res
->
start
=
addr
+
mem
->
card_start
;
mem
->
res
->
end
=
mem
->
res
->
start
+
(
M32R_PCC_MAPSIZE
-
1
);
/*
* Enable again
*/
pcc_set
(
sock
,
PCCR
,
1
);
#ifdef CHAOS_PCC_DEBUG
#if 0
if (last != as_attr) {
#else
if
(
1
)
{
#endif
dummy_readbuf
=
*
(
u_char
*
)(
addr
+
KSEG1
);
}
#endif
return
0
;
}
/* _set_mem_map */
#if 0 /* driver model ordering issue */
/*======================================================================
Routines for accessing socket information and register dumps via
/proc/bus/pccard/...
======================================================================*/
static ssize_t show_info(struct class_device *class_dev, char *buf)
{
pcc_socket_t *s = container_of(class_dev, struct pcc_socket,
socket.dev);
return sprintf(buf, "type: %s\nbase addr: 0x%08lx\n",
pcc[s->type].name, s->base);
}
static ssize_t show_exca(struct class_device *class_dev, char *buf)
{
/* FIXME */
return 0;
}
static CLASS_DEVICE_ATTR(info, S_IRUGO, show_info, NULL);
static CLASS_DEVICE_ATTR(exca, S_IRUGO, show_exca, NULL);
#endif
/*====================================================================*/
/* this is horribly ugly... proper locking needs to be done here at
* some time... */
#define LOCKED(x) do { \
int retval; \
unsigned long flags; \
spin_lock_irqsave(&pcc_lock, flags); \
retval = x; \
spin_unlock_irqrestore(&pcc_lock, flags); \
return retval; \
} while (0)
static
int
pcc_get_status
(
struct
pcmcia_socket
*
s
,
u_int
*
value
)
{
unsigned
int
sock
=
container_of
(
s
,
struct
pcc_socket
,
socket
)
->
number
;
if
(
socket
[
sock
].
flags
&
IS_ALIVE
)
{
*
value
=
0
;
return
-
EINVAL
;
}
LOCKED
(
_pcc_get_status
(
sock
,
value
));
}
static
int
pcc_get_socket
(
struct
pcmcia_socket
*
s
,
socket_state_t
*
state
)
{
unsigned
int
sock
=
container_of
(
s
,
struct
pcc_socket
,
socket
)
->
number
;
if
(
socket
[
sock
].
flags
&
IS_ALIVE
)
return
-
EINVAL
;
LOCKED
(
_pcc_get_socket
(
sock
,
state
));
}
static
int
pcc_set_socket
(
struct
pcmcia_socket
*
s
,
socket_state_t
*
state
)
{
unsigned
int
sock
=
container_of
(
s
,
struct
pcc_socket
,
socket
)
->
number
;
if
(
socket
[
sock
].
flags
&
IS_ALIVE
)
return
-
EINVAL
;
LOCKED
(
_pcc_set_socket
(
sock
,
state
));
}
static
int
pcc_set_io_map
(
struct
pcmcia_socket
*
s
,
struct
pccard_io_map
*
io
)
{
unsigned
int
sock
=
container_of
(
s
,
struct
pcc_socket
,
socket
)
->
number
;
if
(
socket
[
sock
].
flags
&
IS_ALIVE
)
return
-
EINVAL
;
LOCKED
(
_pcc_set_io_map
(
sock
,
io
));
}
static
int
pcc_set_mem_map
(
struct
pcmcia_socket
*
s
,
struct
pccard_mem_map
*
mem
)
{
unsigned
int
sock
=
container_of
(
s
,
struct
pcc_socket
,
socket
)
->
number
;
if
(
socket
[
sock
].
flags
&
IS_ALIVE
)
return
-
EINVAL
;
LOCKED
(
_pcc_set_mem_map
(
sock
,
mem
));
}
static
int
pcc_init
(
struct
pcmcia_socket
*
s
)
{
DEBUG
(
4
,
"m32r-pcc: init call
\n
"
);
return
0
;
}
static
int
pcc_suspend
(
struct
pcmcia_socket
*
sock
)
{
return
pcc_set_socket
(
sock
,
&
dead_socket
);
}
static
struct
pccard_operations
pcc_operations
=
{
.
init
=
pcc_init
,
.
suspend
=
pcc_suspend
,
.
get_status
=
pcc_get_status
,
.
get_socket
=
pcc_get_socket
,
.
set_socket
=
pcc_set_socket
,
.
set_io_map
=
pcc_set_io_map
,
.
set_mem_map
=
pcc_set_mem_map
,
};
/*====================================================================*/
static
int
m32r_pcc_suspend
(
struct
device
*
dev
,
u32
state
,
u32
level
)
{
int
ret
=
0
;
if
(
level
==
SUSPEND_SAVE_STATE
)
ret
=
pcmcia_socket_dev_suspend
(
dev
,
state
);
return
ret
;
}
static
int
m32r_pcc_resume
(
struct
device
*
dev
,
u32
level
)
{
int
ret
=
0
;
if
(
level
==
RESUME_RESTORE_STATE
)
ret
=
pcmcia_socket_dev_resume
(
dev
);
return
ret
;
}
static
struct
device_driver
pcc_driver
=
{
.
name
=
"pcc"
,
.
bus
=
&
platform_bus_type
,
.
suspend
=
m32r_pcc_suspend
,
.
resume
=
m32r_pcc_resume
,
};
static
struct
platform_device
pcc_device
=
{
.
name
=
"pcc"
,
.
id
=
0
,
};
/*====================================================================*/
static
int
__init
init_m32r_pcc
(
void
)
{
int
i
,
ret
;
ret
=
driver_register
(
&
pcc_driver
);
if
(
ret
)
return
ret
;
ret
=
platform_device_register
(
&
pcc_device
);
if
(
ret
){
driver_unregister
(
&
pcc_driver
);
return
ret
;
}
printk
(
KERN_INFO
"m32r PCC probe:
\n
"
);
pcc_sockets
=
0
;
add_pcc_socket
(
M32R_PCC0_BASE
,
PCC0_IRQ
,
M32R_PCC0_MAPBASE
,
0x1000
);
#ifdef CONFIG_M32RPCC_SLOT2
add_pcc_socket
(
M32R_PCC1_BASE
,
PCC1_IRQ
,
M32R_PCC1_MAPBASE
,
0x2000
);
#endif
if
(
pcc_sockets
==
0
)
{
printk
(
"socket is not found.
\n
"
);
platform_device_unregister
(
&
pcc_device
);
driver_unregister
(
&
pcc_driver
);
return
-
ENODEV
;
}
/* Set up interrupt handler(s) */
for
(
i
=
0
;
i
<
pcc_sockets
;
i
++
)
{
socket
[
i
].
socket
.
dev
.
dev
=
&
pcc_device
.
dev
;
socket
[
i
].
socket
.
ops
=
&
pcc_operations
;
socket
[
i
].
socket
.
owner
=
THIS_MODULE
;
socket
[
i
].
number
=
i
;
ret
=
pcmcia_register_socket
(
&
socket
[
i
].
socket
);
if
(
ret
&&
i
--
)
{
for
(;
i
>=
0
;
i
--
)
pcmcia_unregister_socket
(
&
socket
[
i
].
socket
);
break
;
}
#if 0 /* driver model ordering issue */
class_device_create_file(&socket[i].socket.dev,
&class_device_attr_info);
class_device_create_file(&socket[i].socket.dev,
&class_device_attr_exca);
#endif
}
/* Finally, schedule a polling interrupt */
if
(
poll_interval
!=
0
)
{
poll_timer
.
function
=
pcc_interrupt_wrapper
;
poll_timer
.
data
=
0
;
init_timer
(
&
poll_timer
);
poll_timer
.
expires
=
jiffies
+
poll_interval
;
add_timer
(
&
poll_timer
);
}
return
0
;
}
/* init_m32r_pcc */
static
void
__exit
exit_m32r_pcc
(
void
)
{
int
i
;
for
(
i
=
0
;
i
<
pcc_sockets
;
i
++
)
pcmcia_unregister_socket
(
&
socket
[
i
].
socket
);
platform_device_unregister
(
&
pcc_device
);
if
(
poll_interval
!=
0
)
del_timer_sync
(
&
poll_timer
);
driver_unregister
(
&
pcc_driver
);
}
/* exit_m32r_pcc */
module_init
(
init_m32r_pcc
);
module_exit
(
exit_m32r_pcc
);
MODULE_LICENSE
(
"Dual MPL/GPL"
);
/*====================================================================*/
arch/m32r/drivers/m32r_pcc.h
deleted
100644 → 0
View file @
ef8932e3
/*
* $Id$
*
* Copyright (C) 2001 by Hiroyuki Kondo
*/
#define M32R_MAX_PCC 2
/*
* M32R PC Card Controler
*/
#define M32R_PCC0_BASE 0x00ef7000
#define M32R_PCC1_BASE 0x00ef7020
/*
* Register offsets
*/
#define PCCR 0x00
#define PCADR 0x04
#define PCMOD 0x08
#define PCIRC 0x0c
#define PCCSIGCR 0x10
#define PCATCR 0x14
/*
* PCCR
*/
#define PCCR_PCEN (1UL<<(31-31))
/*
* PCIRC
*/
#define PCIRC_BWERR (1UL<<(31-7))
#define PCIRC_CDIN1 (1UL<<(31-14))
#define PCIRC_CDIN2 (1UL<<(31-15))
#define PCIRC_BEIEN (1UL<<(31-23))
#define PCIRC_CIIEN (1UL<<(31-30))
#define PCIRC_COIEN (1UL<<(31-31))
/*
* PCCSIGCR
*/
#define PCCSIGCR_SEN (1UL<<(31-3))
#define PCCSIGCR_VEN (1UL<<(31-7))
#define PCCSIGCR_CRST (1UL<<(31-15))
#define PCCSIGCR_COCR (1UL<<(31-31))
/*
*
*/
#define PCMOD_AS_ATTRIB (1UL<<(31-19))
#define PCMOD_AS_IO (1UL<<(31-18))
#define PCMOD_CBSZ (1UL<<(31-23))
/* set for 8bit */
#define PCMOD_DBEX (1UL<<(31-31))
/* set for excahnge */
/*
* M32R PCC Map addr
*/
#define M32R_PCC0_MAPBASE 0x14000000
#define M32R_PCC1_MAPBASE 0x16000000
#define M32R_PCC_MAPMAX 0x02000000
#define M32R_PCC_MAPSIZE 0x00001000
/* XXX */
#define M32R_PCC_MAPMASK (~(M32R_PCC_MAPMAX-1))
arch/m32r/drivers/m5drv.c
deleted
100644 → 0
View file @
ef8932e3
/*
* MTD chip driver for M5M29GT320VP
*
* Copyright (C) 2003 Takeo Takahashi
*
* 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 the Free Software Foundation; either version
* 2 of the License, or (at your option) any later version.
*
* $Id$
*/
#ifndef __KERNEL__
# define __KERNEL__
#endif
#include <linux/config.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/version.h>
#include <linux/types.h>
#include <linux/sched.h>
#include <linux/errno.h>
#include <linux/interrupt.h>
#include <linux/mtd/map.h>
#include <linux/mtd/cfi.h>
#include <linux/delay.h>
#define M5DRV_DEBUG(n, args...) if ((n) & m5drv_debug) printk(KERN_DEBUG args)
#undef UNLOCK_BEFORE_ERASE
#define M5DRV_PAGE_SIZE (256)
/* page program size */
#define M5DRV_BLOCK_SIZE8 (8*1024)
/* 8K block size in byte */
#define M5DRV_BLOCK_SIZE64 (64*1024)
/* 64K block size in byte */
#define M5DRV_MAX_BLOCK_NUM 70
/* number of blocks */
#define M5DRV_ERASE_REGION 2
/* 64KB and 8KB */
/*
* Software commands
*/
#define CMD_READ_ARRAY 0xff
#define CMD_DEVICE_IDENT 0x90
#define CMD_READ_STATUS 0x70
#define CMD_CLEAR_STATUS 0x50
#define CMD_BLOCK_ERASE 0x20
#define CMD_CONFIRM 0xd0
#define CMD_PROGRAM_BYTE 0x40
#define CMD_PROGRAM_WORD CMD_PROGRAM_BYTE
#define CMD_PROGRAM_PAGE 0x41
#define CMD_SINGLE_LOAD_DATA 0x74
#define CMD_BUFF2FLASH 0x0e
#define CMD_FLASH2BUFF 0xf1
#define CMD_CLEAR_BUFF 0x55
#define CMD_SUSPEND 0xb0
#define CMD_RESUME 0xd0
#define IDENT_OFFSET 0
/* indent command offset */
/*
* Status
*/
#define STATUS_READY 0x80
/* 0:busy 1:ready */
#define STATUS_SUSPEND 0x40
/* 0:progress/complete 1:suspend */
#define STATUS_ERASE 0x20
/* 0:pass 1:error */
#define STATUS_PROGRAM 0x10
/* 0:pass 1:error */
#define STATUS_BLOCK 0x08
/* 0:pass 1:error */
/*
* Device Code
*/
#define MAKER (0x1c)
#define M5M29GT320VP (0x20)
#define M5M29GB320VP (0x21)
static
const
char
version
[]
=
"M5DRV Flash Driver"
;
static
const
char
date
[]
=
__DATE__
;
static
const
char
time
[]
=
__TIME__
;
static
int
m5drv_debug
=
0
;
MODULE_PARM
(
m5drv_debug
,
"i"
);
struct
m5drv_info
{
struct
flchip
*
chip
;
int
chipshift
;
int
numchips
;
struct
flchip
chips
[
1
];
unsigned
char
buf
[
M5DRV_BLOCK_SIZE64
];
#define M5BUF (m5drv->buf)
};
struct
mtd_info
*
m5drv_probe
(
struct
map_info
*
map
);
static
int
m5drv_probe_map
(
struct
map_info
*
map
,
struct
mtd_info
*
mtd
);
static
int
m5drv_wait
(
struct
map_info
*
map
,
struct
flchip
*
chip
,
loff_t
adr
);
static
void
m5drv_release
(
struct
flchip
*
chip
);
static
int
m5drv_query_blksize
(
loff_t
ofs
);
static
int
m5drv_read
(
struct
mtd_info
*
mtd
,
loff_t
from
,
size_t
len
,
size_t
*
retlen
,
u_char
*
buf
);
static
int
m5drv_read_oneblock
(
struct
map_info
*
map
,
loff_t
from
);
static
int
m5drv_write
(
struct
mtd_info
*
mtd
,
loff_t
adr
,
size_t
len
,
size_t
*
retlen
,
const
u_char
*
buf
);
static
int
m5drv_write_oneblock
(
struct
map_info
*
map
,
loff_t
adr
,
size_t
len
,
const
u_char
*
buf
);
static
int
m5drv_write_onepage
(
struct
map_info
*
map
,
struct
flchip
*
chip
,
unsigned
long
adr
,
const
u_char
*
buf
);
static
int
m5drv_erase
(
struct
mtd_info
*
mtd
,
struct
erase_info
*
instr
);
static
int
m5drv_do_wait_for_ready
(
struct
map_info
*
map
,
struct
flchip
*
chip
,
unsigned
long
adr
);
static
int
m5drv_erase_oneblock
(
struct
map_info
*
map
,
struct
flchip
*
chip
,
unsigned
long
adr
);
static
void
m5drv_sync
(
struct
mtd_info
*
mtd
);
static
int
m5drv_suspend
(
struct
mtd_info
*
mtd
);
static
void
m5drv_resume
(
struct
mtd_info
*
mtd
);
static
void
m5drv_destroy
(
struct
mtd_info
*
mtd
);
#ifdef UNLOCK_BEFORE_ERASE
static
void
m5drv_unlock_oneblock
(
struct
map_info
*
map
,
struct
flchip
*
chip
,
unsigned
long
adr
);
#endif
static
struct
mtd_chip_driver
m5drv_chipdrv
=
{
probe:
m5drv_probe
,
destroy:
m5drv_destroy
,
name:
"m5drv"
,
module:
THIS_MODULE
};
struct
mtd_info
*
m5drv_probe
(
struct
map_info
*
map
)
{
struct
mtd_info
*
mtd
=
NULL
;
struct
m5drv_info
*
m5drv
=
NULL
;
int
width
;
mtd
=
kmalloc
(
sizeof
(
*
mtd
),
GFP_KERNEL
);
if
(
!
mtd
)
{
printk
(
"m5drv: can not allocate memory for mtd_info
\n
"
);
return
NULL
;
}
m5drv
=
kmalloc
(
sizeof
(
*
m5drv
),
GFP_KERNEL
);
if
(
!
m5drv
)
{
printk
(
"m5drv: can not allocate memory for m5drv_info
\n
"
);
kfree
(
mtd
);
return
NULL
;
}
memset
(
mtd
,
0
,
sizeof
(
*
mtd
));
width
=
m5drv_probe_map
(
map
,
mtd
);
if
(
!
width
)
{
printk
(
"m5drv: m5drv_probe_map error (width=%d)
\n
"
,
width
);
kfree
(
mtd
);
kfree
(
m5drv
);
return
NULL
;
}
mtd
->
priv
=
map
;
mtd
->
type
=
MTD_OTHER
;
mtd
->
erase
=
m5drv_erase
;
mtd
->
read
=
m5drv_read
;
mtd
->
write
=
m5drv_write
;
mtd
->
sync
=
m5drv_sync
;
mtd
->
suspend
=
m5drv_suspend
;
mtd
->
resume
=
m5drv_resume
;
mtd
->
flags
=
MTD_CAP_NORFLASH
;
/* ??? */
mtd
->
name
=
map
->
name
;
memset
(
m5drv
,
0
,
sizeof
(
*
m5drv
));
m5drv
->
chipshift
=
23
;
m5drv
->
numchips
=
1
;
m5drv
->
chips
[
0
].
start
=
0
;
m5drv
->
chips
[
0
].
state
=
FL_READY
;
m5drv
->
chips
[
0
].
mutex
=
&
m5drv
->
chips
[
0
].
_spinlock
;
m5drv
->
chips
[
0
].
word_write_time
=
0
;
init_waitqueue_head
(
&
m5drv
->
chips
[
0
].
wq
);
spin_lock_init
(
&
m5drv
->
chips
[
0
].
_spinlock
);
map
->
fldrv
=
&
m5drv_chipdrv
;
map
->
fldrv_priv
=
m5drv
;
MOD_INC_USE_COUNT
;
return
mtd
;
}
static
int
m5drv_probe_map
(
struct
map_info
*
map
,
struct
mtd_info
*
mtd
)
{
u16
tmp
;
u16
maker
,
device
;
int
width
=
2
;
struct
mtd_erase_region_info
*
einfo
;
map
->
write16
(
map
,
CMD_READ_ARRAY
,
IDENT_OFFSET
);
tmp
=
map
->
read16
(
map
,
IDENT_OFFSET
);
map
->
write16
(
map
,
CMD_DEVICE_IDENT
,
IDENT_OFFSET
);
maker
=
map
->
read16
(
map
,
IDENT_OFFSET
);
maker
&=
0xff
;
if
(
maker
==
MAKER
)
{
/* FIXME: check device */
device
=
maker
>>
8
;
printk
(
"m5drv: detected M5M29GT320VP
\n
"
);
einfo
=
kmalloc
(
sizeof
(
*
einfo
)
*
M5DRV_ERASE_REGION
,
GFP_KERNEL
);
if
(
!
einfo
)
{
printk
(
"m5drv: cannot allocate memory for erase_region
\n
"
);
return
0
;
}
/* 64KB erase block (blk no# 0-62) */
einfo
[
0
].
offset
=
0
;
einfo
[
0
].
erasesize
=
0x8000
*
width
;
einfo
[
0
].
numblocks
=
(
7
+
8
+
24
+
24
);
/* 8KB erase block (blk no# 63-70) */
einfo
[
1
].
offset
=
0x3f0000
;
einfo
[
1
].
erasesize
=
0x1000
*
width
;
einfo
[
1
].
numblocks
=
(
2
+
8
);
mtd
->
numeraseregions
=
M5DRV_ERASE_REGION
;
mtd
->
eraseregions
=
einfo
;
mtd
->
size
=
0x200000
*
width
;
/* total 4MB */
/*
* mtd->erasesize is used in parse_xxx_partitions.
* last erase block has a partition table.
*/
mtd
->
erasesize
=
0x8000
*
width
;
return
width
;
}
else
if
(
map
->
read16
(
map
,
IDENT_OFFSET
)
==
CMD_DEVICE_IDENT
)
{
printk
(
"m5drv: looks like RAM
\n
"
);
map
->
write16
(
map
,
tmp
,
IDENT_OFFSET
);
}
else
{
printk
(
"m5drv: can not detect flash memory (0x%04x)
\n
"
,
maker
);
}
map
->
write16
(
map
,
CMD_READ_ARRAY
,
IDENT_OFFSET
);
return
0
;
}
static
int
m5drv_query_blksize
(
loff_t
ofs
)
{
int
blk
;
blk
=
ofs
>>
16
;
if
(
blk
>
0x3f
)
{
printk
(
"m5drv: out of block address (0x%08x)
\n
"
,
(
u32
)
ofs
);
return
M5DRV_BLOCK_SIZE64
;
}
if
(
blk
==
63
)
blk
+=
((
ofs
&
0x0000e000
)
>>
13
);
if
(
blk
>
M5DRV_MAX_BLOCK_NUM
)
{
printk
(
"m5drv: out of block address (0x%08x)
\n
"
,
(
u32
)
ofs
);
return
M5DRV_BLOCK_SIZE64
;
}
return
((
blk
>=
63
)
?
M5DRV_BLOCK_SIZE8
:
M5DRV_BLOCK_SIZE64
);
}
static
int
m5drv_wait
(
struct
map_info
*
map
,
struct
flchip
*
chip
,
loff_t
adr
)
{
__u16
status
;
unsigned
long
timeo
;
DECLARE_WAITQUEUE
(
wait
,
current
);
timeo
=
jiffies
+
HZ
;
adr
&=
~
1
;
/* align 2 */
retry:
spin_lock_bh
(
chip
->
mutex
);
switch
(
chip
->
state
)
{
case
FL_READY
:
map
->
write16
(
map
,
CMD_READ_STATUS
,
adr
);
chip
->
state
=
FL_STATUS
;
case
FL_STATUS
:
status
=
map
->
read16
(
map
,
adr
);
if
((
status
&
STATUS_READY
)
!=
STATUS_READY
)
{
udelay
(
100
);
}
break
;
default:
printk
(
"m5drv: waiting for chip
\n
"
);
if
(
time_after
(
jiffies
,
timeo
))
{
/* jiffies is after timeo */
set_current_state
(
TASK_INTERRUPTIBLE
);
add_wait_queue
(
&
chip
->
wq
,
&
wait
);
spin_unlock_bh
(
chip
->
mutex
);
schedule
();
remove_wait_queue
(
&
chip
->
wq
,
&
wait
);
spin_lock_bh
(
chip
->
mutex
);
// by takeo
if
(
signal_pending
(
current
))
{
printk
(
"m5drv: canceled
\n
"
);
map
->
write16
(
map
,
CMD_CLEAR_STATUS
,
adr
);
map
->
write16
(
map
,
CMD_READ_ARRAY
,
adr
);
chip
->
state
=
FL_READY
;
return
-
EINTR
;
}
}
timeo
=
jiffies
+
HZ
;
goto
retry
;
}
map
->
write16
(
map
,
CMD_READ_ARRAY
,
adr
);
chip
->
state
=
FL_READY
;
return
0
;
}
static
void
m5drv_release
(
struct
flchip
*
chip
)
{
M5DRV_DEBUG
(
1
,
"m5drv_release
\n
"
);
wake_up
(
&
chip
->
wq
);
spin_unlock_bh
(
chip
->
mutex
);
}
static
int
m5drv_read
(
struct
mtd_info
*
mtd
,
loff_t
from
,
size_t
len
,
size_t
*
retlen
,
u_char
*
buf
)
{
struct
map_info
*
map
=
mtd
->
priv
;
struct
m5drv_info
*
m5drv
=
map
->
fldrv_priv
;
int
chipnum
;
int
ret
;
*
retlen
=
0
;
chipnum
=
(
from
>>
m5drv
->
chipshift
);
if
(
chipnum
>=
m5drv
->
numchips
)
{
printk
(
"m5drv: out of chip number (%d)
\n
"
,
chipnum
);
return
-
EIO
;
}
/* We don't support erase suspend */
ret
=
m5drv_wait
(
map
,
&
m5drv
->
chips
[
chipnum
],
from
);
if
(
ret
<
0
)
return
ret
;
map
->
copy_from
(
map
,
buf
,
from
,
len
);
m5drv_release
(
&
m5drv
->
chips
[
chipnum
]);
*
retlen
=
len
;
return
0
;
}
static
int
m5drv_read_oneblock
(
struct
map_info
*
map
,
loff_t
from
)
{
struct
m5drv_info
*
m5drv
=
map
->
fldrv_priv
;
int
ofs
;
int
ret
;
int
blksize
;
int
chipnum
;
M5DRV_DEBUG
(
1
,
"m5drv_read_oneblock(0x%08x)
\n
"
,
(
u32
)
from
);
chipnum
=
(
from
>>
m5drv
->
chipshift
);
blksize
=
m5drv_query_blksize
(
from
);
ofs
=
(
from
&
~
(
blksize
-
1
));
ret
=
m5drv_wait
(
map
,
&
m5drv
->
chips
[
chipnum
],
from
);
if
(
ret
<
0
)
return
ret
;
map
->
copy_from
(
map
,
M5BUF
,
ofs
,
blksize
);
m5drv_release
(
&
m5drv
->
chips
[
chipnum
]);
return
0
;
}
static
int
m5drv_write
(
struct
mtd_info
*
mtd
,
loff_t
to
,
size_t
len
,
size_t
*
retlen
,
const
u_char
*
buf
)
{
struct
map_info
*
map
=
mtd
->
priv
;
struct
m5drv_info
*
m5drv
=
map
->
fldrv_priv
;
int
ret
=
0
;
int
blksize
;
int
chipnum
;
int
thislen
;
M5DRV_DEBUG
(
1
,
"m5drv_write(to=0x%08x, len=%d, buf=0x%08x
\n
"
,
(
u32
)
to
,
(
u32
)
len
,
(
u32
)
buf
);
*
retlen
=
0
;
blksize
=
m5drv_query_blksize
(
to
);
chipnum
=
(
to
>>
m5drv
->
chipshift
);
/*
* we does not support byte/word program yet.
*/
for
(
thislen
=
len
;
thislen
>
0
;
thislen
-=
blksize
)
{
thislen
=
((
thislen
>=
blksize
)
?
blksize
:
thislen
);
ret
=
m5drv_write_oneblock
(
map
,
to
,
thislen
,
buf
);
if
(
ret
<
0
)
return
ret
;
to
+=
blksize
;
buf
+=
blksize
;
*
retlen
+=
thislen
;
}
return
0
;
}
static
int
m5drv_write_oneblock
(
struct
map_info
*
map
,
loff_t
adr
,
size_t
len
,
const
u_char
*
buf
)
{
struct
m5drv_info
*
m5drv
=
map
->
fldrv_priv
;
int
ofs
;
int
blksize
;
int
ret
;
int
chipnum
;
int
i
;
M5DRV_DEBUG
(
1
,
"m5drv_write_oneblock(0x%08x, %d)
\n
"
,
(
u32
)
adr
,
(
u32
)
len
);
chipnum
=
(
adr
>>
m5drv
->
chipshift
);
ret
=
m5drv_read_oneblock
(
map
,
adr
);
if
(
ret
<
0
)
return
ret
;
blksize
=
m5drv_query_blksize
(
adr
);
ofs
=
(
adr
&
(
blksize
-
1
));
adr
=
adr
&
~
(
blksize
-
1
);
memcpy
(
M5BUF
+
ofs
,
buf
,
len
);
/* copy to block buffer */
#if 0 /*
* FIXME: erasing is unnecessary.
*/
ret = m5drv_erase_oneblock(map, &m5drv->chips[chipnum], adr);
if (ret < 0) return ret;
#endif
for
(
i
=
0
;
i
<
len
;
i
+=
M5DRV_PAGE_SIZE
)
{
ret
=
m5drv_write_onepage
(
map
,
&
m5drv
->
chips
[
chipnum
],
adr
,
M5BUF
+
i
);
if
(
ret
<
0
)
return
ret
;
adr
+=
M5DRV_PAGE_SIZE
;
}
return
0
;
}
static
int
m5drv_write_onepage
(
struct
map_info
*
map
,
struct
flchip
*
chip
,
unsigned
long
adr
,
const
u_char
*
buf
)
{
int
ret
;
int
i
;
u_short
data
;
long
padr
;
/* page address */
u_short
status
;
int
chipnum
;
struct
m5drv_info
*
m5drv
=
map
->
fldrv_priv
;
M5DRV_DEBUG
(
1
,
"m5drv_write_onepage(0x%08x, 0x%08x)
\n
"
,
(
u32
)
adr
,
(
u32
)
buf
);
padr
=
adr
;
padr
&=
~
1
;
/* align 2 */
chipnum
=
(
adr
>>
m5drv
->
chipshift
);
ret
=
m5drv_wait
(
map
,
chip
,
padr
);
if
(
ret
<
0
)
return
ret
;
map
->
write16
(
map
,
CMD_PROGRAM_PAGE
,
padr
);
chip
->
state
=
FL_WRITING
;
for
(
i
=
0
;
i
<
M5DRV_PAGE_SIZE
;
i
+=
map
->
buswidth
)
{
data
=
((
*
buf
<<
8
)
|
*
(
buf
+
1
));
/*
* FIXME: convert be->le ?
*/
map
->
write16
(
map
,
data
,
adr
);
adr
+=
map
->
buswidth
;
buf
+=
map
->
buswidth
;
}
ret
=
m5drv_do_wait_for_ready
(
map
,
chip
,
padr
);
if
(
ret
<
0
)
{
m5drv_release
(
&
m5drv
->
chips
[
chipnum
]);
return
ret
;
}
status
=
map
->
read16
(
map
,
padr
);
if
((
status
&
STATUS_READY
)
!=
STATUS_READY
)
{
printk
(
"m5drv: error page writing at addr=0x%08x status=0x%08x
\n
"
,
(
u32
)
padr
,
(
u32
)
status
);
map
->
write16
(
map
,
CMD_CLEAR_STATUS
,
padr
);
}
map
->
write16
(
map
,
CMD_READ_ARRAY
,
padr
);
chip
->
state
=
FL_READY
;
m5drv_release
(
&
m5drv
->
chips
[
chipnum
]);
return
0
;
}
static
int
m5drv_erase
(
struct
mtd_info
*
mtd
,
struct
erase_info
*
instr
)
{
struct
map_info
*
map
=
mtd
->
priv
;
struct
m5drv_info
*
m5drv
=
map
->
fldrv_priv
;
unsigned
long
adr
,
len
;
int
chipnum
,
ret
=
0
;
int
erasesize
=
0
;
int
i
;
M5DRV_DEBUG
(
2
,
"m5drv_erase(0x%08x)
\n
"
,
instr
->
addr
);
chipnum
=
instr
->
addr
>>
m5drv
->
chipshift
;
if
(
chipnum
>=
m5drv
->
numchips
)
{
printk
(
"m5drv: out of chip number (%d)
\n
"
,
chipnum
);
return
-
EIO
;
}
adr
=
instr
->
addr
&
((
1
<<
m5drv
->
chipshift
)
-
1
);
len
=
instr
->
len
;
if
(
mtd
->
numeraseregions
==
0
)
{
erasesize
=
mtd
->
erasesize
;
}
else
if
(
mtd
->
numeraseregions
==
1
)
{
erasesize
=
mtd
->
eraseregions
->
erasesize
;
}
else
{
for
(
i
=
0
;
i
<
(
mtd
->
numeraseregions
-
1
);
i
++
)
{
if
(
adr
<
mtd
->
eraseregions
[
i
+
1
].
offset
)
{
erasesize
=
mtd
->
eraseregions
[
i
].
erasesize
;
break
;
}
}
if
(
i
==
(
mtd
->
numeraseregions
-
1
))
{
/* last region */
erasesize
=
mtd
->
eraseregions
[
i
].
erasesize
;
}
}
M5DRV_DEBUG
(
2
,
"erasesize=%d, len=%ld
\n
"
,
erasesize
,
len
);
if
(
erasesize
==
0
)
return
-
EINVAL
;
if
(
instr
->
addr
&
(
erasesize
-
1
))
return
-
EINVAL
;
if
(
instr
->
len
&
(
erasesize
-
1
))
return
-
EINVAL
;
if
(
instr
->
len
+
instr
->
addr
>
mtd
->
size
)
return
-
EINVAL
;
while
(
len
)
{
ret
=
m5drv_erase_oneblock
(
map
,
&
m5drv
->
chips
[
chipnum
],
adr
);
if
(
ret
<
0
)
return
ret
;
adr
+=
erasesize
;
len
-=
erasesize
;
if
(
adr
>>
m5drv
->
chipshift
){
adr
=
0
;
chipnum
++
;
if
(
chipnum
>=
m5drv
->
numchips
)
break
;
}
}
instr
->
state
=
MTD_ERASE_DONE
;
if
(
instr
->
callback
)
{
M5DRV_DEBUG
(
1
,
"m5drv: call callback
\n
"
);
instr
->
callback
(
instr
);
}
return
0
;
}
static
int
m5drv_do_wait_for_ready
(
struct
map_info
*
map
,
struct
flchip
*
chip
,
unsigned
long
adr
)
{
int
ret
;
int
timeo
;
u_short
status
;
DECLARE_WAITQUEUE
(
wait
,
current
);
/* unnecessary CMD_READ_STATUS */
/*
map->write16(map, CMD_READ_STATUS, adr);
status = map->read16(map, adr);
*/
timeo
=
jiffies
+
HZ
;
while
(
time_before
(
jiffies
,
timeo
))
{
/*
map->write16(map, CMD_READ_STATUS, adr);
*/
status
=
map
->
read16
(
map
,
adr
);
if
((
status
&
STATUS_READY
)
==
STATUS_READY
)
{
M5DRV_DEBUG
(
1
,
"m5drv_wait_for_ready: ok, ready
\n
"
);
/*
* FIXME: do full status check
*/
ret
=
0
;
goto
out
;
}
set_current_state
(
TASK_INTERRUPTIBLE
);
add_wait_queue
(
&
chip
->
wq
,
&
wait
);
// enabled by takeo
spin_unlock_bh
(
chip
->
mutex
);
schedule_timeout
(
1
);
schedule
();
remove_wait_queue
(
&
chip
->
wq
,
&
wait
);
// enabled by takeo
spin_lock_bh
(
chip
->
mutex
);
if
(
signal_pending
(
current
))
{
ret
=
-
EINTR
;
goto
out
;
}
//timeo = jiffies + HZ;
}
ret
=
-
ETIME
;
out:
if
(
ret
<
0
)
{
map
->
write16
(
map
,
CMD_CLEAR_STATUS
,
adr
);
map
->
write16
(
map
,
CMD_READ_ARRAY
,
adr
);
chip
->
state
=
FL_READY
;
}
return
ret
;
}
static
int
m5drv_erase_oneblock
(
struct
map_info
*
map
,
struct
flchip
*
chip
,
unsigned
long
adr
)
{
int
ret
;
u_short
status
;
struct
m5drv_info
*
m5drv
=
map
->
fldrv_priv
;
int
chipnum
;
M5DRV_DEBUG
(
1
,
"m5drv_erase_oneblock()
\n
"
);
#ifdef UNLOCK_BEFORE_ERASE
m5drv_unlock_oneblock
(
map
,
chip
,
adr
);
#endif
chipnum
=
(
adr
>>
m5drv
->
chipshift
);
adr
&=
~
1
;
/* align 2 */
ret
=
m5drv_wait
(
map
,
chip
,
adr
);
if
(
ret
<
0
)
return
ret
;
map
->
write16
(
map
,
CMD_BLOCK_ERASE
,
adr
);
map
->
write16
(
map
,
CMD_CONFIRM
,
adr
);
chip
->
state
=
FL_ERASING
;
ret
=
m5drv_do_wait_for_ready
(
map
,
chip
,
adr
);
if
(
ret
<
0
)
{
m5drv_release
(
&
m5drv
->
chips
[
chipnum
]);
return
ret
;
}
status
=
map
->
read16
(
map
,
adr
);
if
((
status
&
STATUS_READY
)
==
STATUS_READY
)
{
M5DRV_DEBUG
(
1
,
"m5drv: erase completed status=%04x
\n
"
,
status
);
map
->
write16
(
map
,
CMD_READ_ARRAY
,
adr
);
chip
->
state
=
FL_READY
;
m5drv_release
(
&
m5drv
->
chips
[
chipnum
]);
return
0
;
/* ok, erasing completed */
}
printk
(
"m5drv: error erasing block at addr=%08lx status=%08x
\n
"
,
adr
,
status
);
map
->
write16
(
map
,
CMD_READ_ARRAY
,
adr
);
/* cancel erasing */
chip
->
state
=
FL_READY
;
m5drv_release
(
&
m5drv
->
chips
[
chipnum
]);
return
-
EIO
;
}
#ifdef UNLOCK_BEFORE_ERASE
/*
* we don't support unlock yet
*/
static
void
m5drv_unlock_oneblock
(
struct
map_info
*
map
,
struct
flchip
*
chip
,
unsigned
long
adr
)
{
M5DRV_DEBUG
(
1
,
"m5drv_unlock_oneblock
\n
"
);
}
#endif
static
void
m5drv_sync
(
struct
mtd_info
*
mtd
)
{
M5DRV_DEBUG
(
1
,
"m5drv_sync()
\n
"
);
}
static
int
m5drv_suspend
(
struct
mtd_info
*
mtd
)
{
M5DRV_DEBUG
(
1
,
"m5drv_suspend()
\n
"
);
return
-
EINVAL
;
}
static
void
m5drv_resume
(
struct
mtd_info
*
mtd
)
{
M5DRV_DEBUG
(
1
,
"m5drv_resume()
\n
"
);
}
static
void
m5drv_destroy
(
struct
mtd_info
*
mtd
)
{
M5DRV_DEBUG
(
1
,
"m5drv_destroy()
\n
"
);
}
int
__init
m5drv_probe_init
(
void
)
{
printk
(
"MTD chip driver
\n
"
);
register_mtd_chip_driver
(
&
m5drv_chipdrv
);
return
0
;
}
static
void
__exit
m5drv_probe_exit
(
void
)
{
M5DRV_DEBUG
(
1
,
"m5drv_probe_exit()
\n
"
);
unregister_mtd_chip_driver
(
&
m5drv_chipdrv
);
}
module_init
(
m5drv_probe_init
);
module_exit
(
m5drv_probe_exit
);
MODULE_AUTHOR
(
"Takeo Takahashi"
);
MODULE_DESCRIPTION
(
"MTD chip driver for M5M29GT320VP"
);
MODULE_LICENSE
(
"GPL"
);
EXPORT_NO_SYMBOLS
;
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment