/*
* linux/arch/arm/mach-sa1100/simpad.c
*
*/
#define CONFIG_SA1100_SIMPAD_128M
#include <linux/config.h>
#include <linux/module.h>
#include <linux/init.h>
#include <linux/kernel.h>
#include <linux/tty.h>
#include <linux/proc_fs.h>
#include <linux/string.h>
#include <linux/pm.h>
#include <asm/hardware.h>
#include <asm/setup.h>
#include <asm/mach/arch.h>
#include <asm/mach/map.h>
#include <asm/mach/serial_sa1100.h>
#include <linux/serial_core.h>
#include "generic.h"
#undef SIMPAD_UART_USE_IRQ // irq handling on CTS/DCD doesn't work yet
long cs3_shadow;
long get_cs3_shadow(void)
{
return cs3_shadow;
}
void set_cs3(long value)
{
*(CS3BUSTYPE *)(CS3_BASE) = cs3_shadow = value;
}
void set_cs3_bit(int value)
{
cs3_shadow |= value;
*(CS3BUSTYPE *)(CS3_BASE) = cs3_shadow;
}
void clear_cs3_bit(int value)
{
cs3_shadow &= ~value;
*(CS3BUSTYPE *)(CS3_BASE) = cs3_shadow;
}
EXPORT_SYMBOL(set_cs3_bit);
EXPORT_SYMBOL(clear_cs3_bit);
static void simpad_power_off(void)
{
cli();
set_cs3(0x800); /* only SD_MEDIAQ */
/* disable internal oscillator, float CS lines */
PCFR = (PCFR_OPDE | PCFR_FP | PCFR_FS);
/* enable wake-up on GPIO0 (Assabet...) */
PWER = GFER = GRER = 1;
/*
* set scratchpad to zero, just in case it is used as a
* restart address by the bootloader.
*/
PSPR = 0;
PGSR = 0;
/* enter sleep mode */
PMCR = PMCR_SF;
while(1);
}
static int __init simpad_init(void)
{
pm_power_off = simpad_power_off;
return 0;
}
__initcall(simpad_init);
static void __init
fixup_simpad(struct machine_desc *desc, struct param_struct *params,
char **cmdline, struct meminfo *mi)
{
int numbanks=1;
#ifdef CONFIG_SA1100_SIMPAD_SINUSPAD
SET_BANK( 0, 0xc0000000, 32*1024*1024 );
#else
SET_BANK( 0, 0xc0000000, 64*1024*1024 );
#endif
#ifdef CONFIG_SA1100_SIMPAD_128M
SET_BANK( 1, 0xc8000000, 64*1024*1024 );
numbanks=2;
#endif
mi->nr_banks = numbanks;
setup_ramdisk( 1, 0, 0, 8192 );
setup_initrd( __phys_to_virt(0xc0800000), 4*1024*1024 );
}
static struct map_desc simpad_io_desc[] __initdata = {
/* virtual physical length domain r w c b */
{ 0xe8000000, 0x00000000, 0x01000000, DOMAIN_IO, 0, 1, 0, 0 },
{ 0xe9000000, 0x08000000, 0x01000000, DOMAIN_IO, 0, 1, 0, 0 },
{ 0xf1000000, 0x18000000, 0x00100000, DOMAIN_IO, 0, 1, 0, 0 }, /* CS3, write only */
{ 0xf2000000, 0x40000000, 0x00100000, DOMAIN_IO, 0, 1, 0, 0 }, /* CS4, tda8007 */
{ 0xf2800000, 0x4b800000, 0x00800000, DOMAIN_IO, 0, 1, 0, 0 }, /* MQ200 */
LAST_DESC
};
#define SER_INFO(fmt, arg...) printk(KERN_INFO fmt "\n" , ## arg)
static void simpad_uart_set_mctrl(struct uart_port *port, u_int mctrl)
{
if (port->mapbase == _Ser1UTCR0) {
/* internal serial port (ttySA1, DECT/Bluetooth) */
if (mctrl & TIOCM_RTS) GPCR = GPIO_UART1_RTS;
else GPSR = GPIO_UART1_RTS;
if (mctrl & TIOCM_DTR) GPCR = GPIO_UART1_DTR;
else GPSR = GPIO_UART1_DTR;
}
else if (port->mapbase == _Ser3UTCR0) {
/* external serial port (ttySA0, RS232) */
if (mctrl & TIOCM_RTS) GPCR = GPIO_UART3_RTS;
else GPSR = GPIO_UART3_RTS;
if (mctrl & TIOCM_DTR) GPCR = GPIO_UART3_DTR;
else GPSR = GPIO_UART3_DTR;
}
}
static u_int simpad_uart_get_mctrl(struct uart_port *port)
{
u_int ret = TIOCM_CD | TIOCM_CTS | TIOCM_DSR;
if (port->mapbase == _Ser1UTCR0) {
/* internal serial port (ttySA1, DECT/Bluetooth) */
int gplr = GPLR;
if (gplr & GPIO_UART1_DCD) ret &= ~TIOCM_CD;
if (gplr & GPIO_UART1_CTS) ret &= ~TIOCM_CTS;
if (gplr & GPIO_UART1_DSR) ret &= ~TIOCM_DSR;
}
else if (port->mapbase == _Ser3UTCR0) {
/* external serial port (ttySA0, RS232) */
int gplr = GPLR;
if (gplr & GPIO_UART3_DCD) ret &= ~TIOCM_CD;
if (gplr & GPIO_UART3_CTS) ret &= ~TIOCM_CTS;
if (gplr & GPIO_UART3_DSR) ret &= ~TIOCM_DSR;
}
return ret;
}
static void simpad_uart_pm(struct uart_port *port, u_int state, u_int oldstate)
{
if (port->mapbase == (u_int)&Ser3UTCR0) {
if (state)
clear_cs3_bit(RS232_ON);
else
set_cs3_bit(RS232_ON);
}
}
/*
* Enable/Disable wake up events for this serial port.
* Obviously, we only support this on the normal COM port.
*/
static int simpad_uart_set_wake(struct uart_port *port, u_int enable)
{
int err = -EINVAL;
#if 0 // TODO: port management
if (port->mapbase == _Ser3UTCR0) {
if (enable)
PWER |= PWER_GPIO23 | PWER_GPIO25 ; /* DCD and CTS */
else
PWER &= ~(PWER_GPIO23 | PWER_GPIO25); /* DCD and CTS */
err = 0;
}
#endif
return err;
}
#ifdef SIMPAD_UART_USE_IRQ
static void simpad_uart1_dcd_intr(int irq, void *dev_id, struct pt_regs *regs)
{
struct uart_port *port = dev_id;
/* Note: should only call this if something has changed */
uart_handle_dcd_change(port, !(GPLR & GPIO_UART1_DCD));
}
static void simpad_uart1_cts_intr(int irq, void *dev_id, struct pt_regs *regs)
{
struct uart_port *port = dev_id;
/* Note: should only call this if something has changed */
uart_handle_cts_change(port, !(GPLR & GPIO_UART1_CTS));
}
static void simpad_uart3_dcd_intr(int irq, void *dev_id, struct pt_regs *regs)
{
struct uart_port *port = dev_id;
/* Note: should only call this if something has changed */
uart_handle_dcd_change(port, !(GPLR & GPIO_UART3_DCD));
}
static void simpad_uart3_cts_intr(int irq, void *dev_id, struct pt_regs *regs)
{
struct uart_port *port = dev_id;
/* Note: should only call this if something has changed */
uart_handle_cts_change(port, !(GPLR & GPIO_UART3_CTS));
}
#endif
static int simpad_uart_open(struct uart_port *port)
{
int ret = 0;
#ifdef SIMPAD_UART_USE_IRQ
if (port->mapbase == _Ser1UTCR0) {
set_GPIO_IRQ_edge(GPIO_UART1_DCD|GPIO_UART1_CTS,
GPIO_BOTH_EDGES);
ret = request_irq(IRQ_GPIO_UART1_DCD, simpad_uart1_dcd_intr,
0, "UART1 DCD", port);
if (ret)
return ret;
ret = request_irq(IRQ_GPIO_UART1_CTS, simpad_uart1_cts_intr,
0, "UART1 CTS", port);
if (ret)
free_irq(IRQ_GPIO_UART1_DCD, port);
}
else if (port->mapbase == _Ser3UTCR0) {
set_GPIO_IRQ_edge(GPIO_UART3_DCD|GPIO_UART3_CTS,
GPIO_BOTH_EDGES);
ret = request_irq(IRQ_GPIO_UART3_DCD, simpad_uart3_dcd_intr,
0, "UART3 DCD", port);
if (ret)
return ret;
ret = request_irq(IRQ_GPIO_UART3_CTS, simpad_uart3_cts_intr,
0, "UART3 CTS", port);
if (ret)
free_irq(IRQ_GPIO_UART3_DCD, port);
}
#endif
return ret;
}
static void simpad_uart_close(struct uart_port *port)
{
#ifdef SIMPAD_UART_USE_IRQ
if (port->mapbase == _Ser1UTCR0) {
free_irq(IRQ_GPIO_UART1_DCD, port);
free_irq(IRQ_GPIO_UART1_CTS, port);
}
else if (port->mapbase == _Ser3UTCR0) {
free_irq(IRQ_GPIO_UART3_DCD, port);
free_irq(IRQ_GPIO_UART3_CTS, port);
}
#endif
}
static struct sa1100_port_fns simpad_port_fns __initdata = {
.set_mctrl = simpad_uart_set_mctrl,
.get_mctrl = simpad_uart_get_mctrl,
.pm = simpad_uart_pm,
.set_wake = simpad_uart_set_wake,
.open = simpad_uart_open,
.close = simpad_uart_close,
};
static void __init simpad_map_io(void)
{
sa1100_map_io();
iotable_init(simpad_io_desc);
set_cs3_bit (EN1 | EN0 | LED2_ON | DISPLAY_ON | RS232_ON |
ENABLE_5V | nRESET_SIMCARD);
sa1100_register_uart_fns(&simpad_port_fns);
//It is only possible to register 3 UART in serial_sa1100.c
sa1100_register_uart(0, 3);
sa1100_register_uart(1, 1);
// txd and rxd use their alternate function
GAFR |= (GPIO_UART_TXD | GPIO_UART_RXD);
// the control lines are gpio
GAFR &= ~(GPIO_UART1_RTS | GPIO_UART1_CTS | GPIO_UART1_DCD);
GAFR &= ~(GPIO_UART1_DSR | GPIO_UART1_DTR);
GAFR &= ~(GPIO_UART3_RTS | GPIO_UART3_CTS | GPIO_UART3_DCD);
GAFR &= ~(GPIO_UART3_DSR | GPIO_UART3_DTR);
// txd, rts and dtr are outputs
GPDR |= GPIO_UART_TXD;
GPDR |= GPIO_UART1_RTS | GPIO_UART3_RTS;
GPDR |= GPIO_UART1_DTR | GPIO_UART3_DTR;
// cts, dcd, dsr and rxd are inputs
GPDR &= ~(GPIO_UART1_CTS | GPIO_UART3_CTS);
GPDR &= ~(GPIO_UART1_DCD | GPIO_UART3_DCD);
GPDR &= ~(GPIO_UART1_DSR | GPIO_UART3_DSR);
GPDR &= ~GPIO_UART_RXD;
PPAR |= PPAR_UPR;
set_GPIO_IRQ_edge(GPIO_UCB1300_IRQ, GPIO_RISING_EDGE);
set_GPIO_IRQ_edge(GPIO_POWER_BUTTON, GPIO_FALLING_EDGE);
/*
* Set up registers for sleep mode.
*/
PWER = PWER_GPIO0;
PGSR = 0x818;
PCFR = 0;
PSDR = 0;
}
#ifdef CONFIG_PROC_FS
static char* name[]={
"VCC_5V_EN",
"VCC_3V_EN",
"EN1",
"EN0",
"DISPLAY_ON",
"PCMCIA_BUFF_DIS",
"MQ_RESET",
"PCMCIA_RESET",
"DECT_POWER_ON",
"IRDA_SD",
"RS232_ON",
"SD_MEDIAQ",
"LED2_ON",
"IRDA_MODE",
"ENABLE_5V",
"RESET_SIMCARD"
};
static int proc_cs3_read(char *page, char **start, off_t off,
int count, int *eof, void *data)
{
char *p = page;
int len, i;
p += sprintf(p, "Chipselect3 : %x\n", cs3_shadow);
for (i = 0; i <= 15; i++) {
if(cs3_shadow & (1<<i)) {
p += sprintf(p, "%s\t: TRUE \n",name[i]);
} else
p += sprintf(p, "%s\t: FALSE \n",name[i]);
}
len = (p - page) - off;
if (len < 0)
len = 0;
*eof = (len <= count) ? 1 : 0;
*start = page + off;
return len;
}
static int proc_cs3_write(struct file * file, const char * buffer,
size_t count, loff_t *ppos)
{
unsigned long newRegValue;
char *endp;
newRegValue = simple_strtoul(buffer,&endp,0);
set_cs3( newRegValue );
return (count+endp-buffer);
}
static struct proc_dir_entry *proc_cs3;
static int __init cs3_init(void)
{
proc_cs3 = create_proc_entry("cs3", 0, 0);
if (proc_cs3)
{
proc_cs3->read_proc = proc_cs3_read;
proc_cs3->write_proc = (void*)proc_cs3_write;
}
return 0;
}
static int __exit cs3_exit(void)
{
if (proc_cs3)
remove_proc_entry( "cs3", 0);
return 0;
}
__initcall(cs3_init);
#endif // CONFIG_PROC_FS
MACHINE_START(SIMPAD, "Simpad")
MAINTAINER("Juergen Messerer")
BOOT_MEM(0xc0000000, 0x80000000, 0xf8000000)
BOOT_PARAMS(0xc0000100)
FIXUP(fixup_simpad)
MAPIO(simpad_map_io)
INITIRQ(sa1100_init_irq)
MACHINE_END