This repository has been archived on 2025-06-06. You can view files and clone it, but cannot push or open issues or pull requests.
Files
android-g900/kernel-2.6.33/arch/arm/mach-pxa/g900/g900_msm6280.c
2010-09-05 20:34:22 +06:00

841 lines
16 KiB
C
Executable File
Raw Blame History

/*
*
* Based on arch/arm/mach-pxa/htcblueangel/blueangel_dpram.c
* Autor: Angell Fear <angell@angellfear.ru>
* MSM6280 to PXA270 communication on Dual Port RAM for Toshiba G900
*
*/
#include <linux/module.h>
#include <linux/ioport.h>
#include <linux/init.h>
#include <linux/serial_reg.h>
#include <linux/circ_buf.h>
#include <linux/delay.h>
#include <linux/interrupt.h>
#include <linux/platform_device.h>
#include <linux/tty.h>
#include <linux/tty_flip.h>
#include <linux/serial_core.h>
#include <linux/irq.h>
#include <asm/io.h>
#include <asm/mach-types.h>
#include <mach/hardware.h>
#include <asm/irq.h>
#include <mach/pxa2xx-regs.h>
#include <asm/gpio.h>
#include <linux/types.h>
#include <linux/ctype.h>
#include <mach/g900-gpio.h>
#include <linux/pda_power.h>
#define RX_AT_BUF (0x400-4)
#define TX_AT_BUF (0x400-4)
#define IRQ_RX gpio_to_irq(0)
#define MBOX1 *((u8*)dpram_data.data_virt + 0x7FFC)
#define MBOX2 *((u8*)dpram_data.data_virt + 0x7FFD)
#define MBOX3 *((u8*)dpram_data.data_virt + 0x7FFE)
#define MBOX4 *((u8*)dpram_data.data_virt + 0x7FFF)
/* DPRAM */
#define PORT_DPRAM 69
struct dpram_data {
unsigned long data_phys;
void *data_virt;
} dpram_data;
int p_BATT_CAPACITY=50;
int p_AC_POWER=0;
#if 0
#define dbprintk(x...) printk(x)
#define dprintk(x...) printk(x)
#define dvprintk(x...) printk(x)
#define dmprintk(x...) printk(x)
#define dcharprint(s) \
if( isascii(s) && isprint(s) ){ \
dprintk("%c",s); \
}else{ \
dprintk("<0x%02x>",s); \
}
#else
#define dbprintk(x...)
#define dprintk(x...)
#define dvprintk(x...)
#define dmprintk(s,b1,b2,b3,b4) \
mbtmp = b1; mbtmp = b2; mbtmp = b3; mbtmp = b4;
#define dcharprint(s)
#endif
u8 mbtmp;
/** BATTERY **/
/** BATTERY **/
/** BATTERY **/
/** BATTERY **/
/** BATTERY **/
/** BATTERY **/
/** BATTERY **/
/** BATTERY **/
#ifdef CONFIG_POWER_SUPPLY
#include <linux/power_supply.h>
static int g900_power_get_property(struct power_supply *psy,
enum power_supply_property prop,
union power_supply_propval *val)
{
int ret = 0;
switch (prop) {
case POWER_SUPPLY_PROP_CAPACITY:
val->intval = p_BATT_CAPACITY;
break;
case POWER_SUPPLY_PROP_TECHNOLOGY: /* All our batteries are Li-ions (right?)*/
val->intval = POWER_SUPPLY_TECHNOLOGY_LION;
break;
case POWER_SUPPLY_PROP_STATUS:
if (p_AC_POWER > 0 && p_BATT_CAPACITY <= 99)
val->intval = POWER_SUPPLY_STATUS_CHARGING;
else if (p_AC_POWER == 1 && p_BATT_CAPACITY > 99)
val->intval = POWER_SUPPLY_STATUS_FULL;
else
val->intval = POWER_SUPPLY_STATUS_DISCHARGING;
break;
case POWER_SUPPLY_PROP_ONLINE: /* either USB or AC */
if (psy->type == POWER_SUPPLY_TYPE_MAINS){ /* AC on/offline*/
if(p_AC_POWER==1){
val->intval = 1;
}else{
val->intval = 0;
}
}else if(psy->type == POWER_SUPPLY_TYPE_USB) { /* USB on/offline */
/*
40 USB detect IRQ
41 USB_P2_7(Client = 1, host =0(up 75,93) )
*/
val->intval =0 ;// (GPIO41_USB_MODE ? 1 : 0); /* TODO!!! */
}else
dbprintk("MSM6280:POWER_SUPPLY_PROP_ONLINE type = %d = %d \n",psy->type,val->intval);
break;
#define VOLTAGE_MIN 3635000 /* todo */
#define VOLTAGE_MAX 4158000 /* todo */
#define VOLTAGE_STEP ((VOLTAGE_MAX - VOLTAGE_MIN)/100)
case POWER_SUPPLY_PROP_BATT_VOL:
val->intval = (p_BATT_CAPACITY * VOLTAGE_STEP ) + VOLTAGE_MIN; //fake !!! percent math
break;
case POWER_SUPPLY_PROP_BATT_TEMP:
val->intval = 27; /// fake !!!
break;
case POWER_SUPPLY_PROP_PRESENT:
val->intval = 0;
break;
case POWER_SUPPLY_PROP_HEALTH:
val->intval = 1;
break;
#if 0
case POWER_SUPPLY_PROP_VOLTAGE_MIN: /* lowest measured value in uV */
val->intval = VOLTAGE_MIN;
break;
case POWER_SUPPLY_PROP_VOLTAGE_MAX: /* highest measured value in uV */
val->intval = VOLTAGE_MAX;
break;
case POWER_SUPPLY_PROP_VOLTAGE_NOW:
val->intval = (p_BATT_CAPACITY * VOLTAGE_STEP ) + VOLTAGE_MIN; /*mV*/ //fake !!! percent math
break;
#endif
default:
ret = -EINVAL;
}
return ret;
}
/* property for AC and USB */
static enum power_supply_property g900_power_line_props[] = {
POWER_SUPPLY_PROP_ONLINE,
};
/* main battery properties */
static enum power_supply_property g900_power_battery_props[] = {
POWER_SUPPLY_PROP_CAPACITY,
POWER_SUPPLY_PROP_TECHNOLOGY,
POWER_SUPPLY_PROP_STATUS,
POWER_SUPPLY_PROP_PRESENT,
POWER_SUPPLY_PROP_HEALTH,
POWER_SUPPLY_PROP_BATT_VOL,
POWER_SUPPLY_PROP_BATT_TEMP,
#if 0
POWER_SUPPLY_PROP_VOLTAGE_MIN,
POWER_SUPPLY_PROP_VOLTAGE_MAX,
POWER_SUPPLY_PROP_VOLTAGE_NOW,
#endif
};
static char *g900_power_supplied_to[] = {
"main-battery",
/* backup-battery could be added if we knew how to access info about it */
};
static struct power_supply g900_psy_ac = {
.name = "ac",
.type = POWER_SUPPLY_TYPE_MAINS,
.supplied_to = g900_power_supplied_to,
.num_supplicants = ARRAY_SIZE(g900_power_supplied_to),
.properties = g900_power_line_props,
.num_properties = ARRAY_SIZE(g900_power_line_props),
.get_property = g900_power_get_property,
};
static struct power_supply g900_psy_usb = {
.name = "usb",
.type = POWER_SUPPLY_TYPE_USB,
.supplied_to = g900_power_supplied_to,
.num_supplicants = ARRAY_SIZE(g900_power_supplied_to),
.properties = g900_power_line_props,
.num_properties = ARRAY_SIZE(g900_power_line_props),
.get_property = g900_power_get_property,
};
static struct power_supply g900_psy_battery = {
.name = "battery",
.type = POWER_SUPPLY_TYPE_BATTERY,
.properties = g900_power_battery_props,
.num_properties = ARRAY_SIZE(g900_power_battery_props),
.get_property = g900_power_get_property,
.use_for_apm = 1,
};
#endif
static struct platform_device *pdev;
void batt_probe(void){
#ifdef CONFIG_POWER_SUPPLY
int ret;
dbprintk("MSM6280:batt_probe \n");
pdev = platform_device_register_simple("MSM6280_batt",
0, NULL, 0);
if (IS_ERR(pdev)) {
ret = PTR_ERR(pdev);
dbprintk("MSM6280: failed to register pdev ret=%d\n",ret);
return;
}
if (power_supply_register(&pdev->dev, &g900_psy_ac) != 0)
dev_err(&pdev->dev, "failed to register %s power supply\n",g900_psy_ac.name);
if (power_supply_register(&pdev->dev, &g900_psy_usb) != 0)
dev_err(&pdev->dev, "failed to register %s power supply\n",g900_psy_usb.name);
if (power_supply_register(&pdev->dev, &g900_psy_battery) != 0)
dev_err(&pdev->dev, "failed to register %s power supply\n",g900_psy_battery.name);
#endif
}
void batt_remove(void){
#ifdef CONFIG_POWER_SUPPLY
power_supply_unregister(&g900_psy_ac);
power_supply_unregister(&g900_psy_usb);
power_supply_unregister(&g900_psy_battery);
#endif
}
#ifdef CONFIG_POWER_SUPPLY
void battery(char *percent,int acpower)
{
int capacity = 0;
dbprintk("MSM6280:BATERY %s%% - POWER %d\n",percent,acpower);
capacity = percent[0]-'0';
if((percent[1]-'0') >=0 && (percent[1]-'0') <=9)
{
capacity=(capacity*10)+(percent[1]-'0');
if((percent[2]-'0') >=0 && (percent[2]-'0') <=9)
{
capacity=(capacity*10)+(percent[2]-'0');
}
}
if(p_BATT_CAPACITY != capacity)
{
p_BATT_CAPACITY = capacity;
}
if((acpower >= 0) && (p_AC_POWER != acpower))
{
p_AC_POWER = acpower;
power_supply_changed(&g900_psy_ac);
}
power_supply_changed(&g900_psy_battery);
dbprintk("MSM6280:BATERY %d%% - POWER %d\n",p_BATT_CAPACITY,p_AC_POWER);
}
#endif
/** BATTERY **/
/** BATTERY **/
/** BATTERY **/
/** BATTERY **/
/** BATTERY **/
/** BATTERY **/
/** BATTERY **/
/** BATTERY **/
/** SPLITER **/
int atresp(char *atbuf)
{
char *p;
int end=0,j=0,i=0,len,x,y,z;
char buf[256],values[256];
char tmp[5][16];
//printk("MSM6280: atresp = %s\n",atbuf);
for (p = atbuf,j=0; *p != '\0' ;p++,j++) {
if (*p == ':'){
while(*(atbuf + end) == 0x0d || *(atbuf + end) == 0x0a) end++;
len=j-end;
strncpy(buf, atbuf+end, len);
buf[len] = 0;
p++;
for(i=0;((*p != 0) && (*p != 0x0d)) ;p++,i++)
{
values[i]=*p;
}
end = j;
values[i]=0;
//printk("MSM6280: AT ANSWER end=%d,len=%d, %s - %s\n",end,len,buf,values);
#ifdef CONFIG_POWER_SUPPLY
/**
BATTERY
plug charger
$CBN: 1,49,1
unplug charger
$CBN: 1,49,4
$CBN - 1,61,14 ??
$CBN: 1,49,8
$CBN: 1,77,0
call AT+CBN
+CBN: 1,49
call AT+CBC
+CBC: 0,49 ?? power capacity??
*/
if(
(buf[0] == '$' || buf[0] == '+')&&
buf[1] == 'C' &&
buf[2] == 'B' &&
(buf[3] == 'N' /*|| buf[3] == 'C'*/)
)
{
dbprintk("MSM6280: AT BATERY ANSWER %s - %s\n",buf,values);
for (x=0,y=0,z=0; values[z] != '\0' ;z++,y++) {
if(values[z] == ','){
tmp[x][y] = 0;
x++;
z++;
y=0;
}
tmp[x][y] = values[z];
}
tmp[x][y] = 0;
for (x=0; x<4 ;x++) {
dbprintk("tmp[%i] = \"%s\"\n",x,tmp[x]);
}
z = -1;
if(tmp[2][0] == '1' && (tmp[2][1] == 0 || (tmp[2][1] == '4' && tmp[2][2] == 0) )){
z = 1;
}
if(tmp[2][0] == '4' && tmp[2][1] == 0){
z = 0;
}
dbprintk("MSM6280: AT !! call battery( %s , %d)\n",tmp[1],z);
battery(tmp[1],z);
}
#endif
}
}
/**
======================
LED
AT+LED=3,10,0
======================
*/
return 0;
}
/**/
static void dpram_enable_ms(struct uart_port *port)
{
dprintk("dpram_enable_ms\n");
}
static void dpram_stop_tx(struct uart_port *port)
{
//dprintk("dpram_stop_tx\n");
}
static void dpram_stop_rx(struct uart_port *port)
{
dprintk("dpram_stop_rx\n");
}
static void
receive_chars(struct uart_port *up)
{
struct tty_struct *tty = up->state->port.tty;
unsigned int ch,atcnt,i=0;
u16 head,tail;
char atbuf[256];
// unsigned long flags;
//spin_lock_irqsave(&up->port.lock, flags);
dmprintk("receive_chars MBOX=%02x,%02x,%02x,%02x\n",MBOX1,MBOX2,MBOX3,MBOX4);
//MBOX2 = 0x1;
//MBOX3 = 0x1;
//MBOX4 = 0x1;
head = *((u16*)dpram_data.data_virt + 0x00);
tail = *((u16*)dpram_data.data_virt + 0x01);
if(tail == head ) return ; // mb fast IRQ
//dvprintk("receive_chars head = 0x%04x, tail = 0x%04x: \n",head,tail);
dprintk("R<-: ");
atcnt=0;
atbuf[0] = 0;
while((head != tail))
{
if(tail >= RX_AT_BUF){
tail = 0x00;
}
if(i >= RX_AT_BUF) break;
ch =*((u8*)dpram_data.data_virt+tail+0x04);
tail++;
i++;
up->icount.rx++;
if (!uart_handle_sysrq_char(up, c))
{
tty_insert_flip_char(tty, ch, TTY_NORMAL);
/*
at resporens other devices
*/
atbuf[atcnt] = ch;
atcnt++;
dcharprint(ch);
}
}
atbuf[atcnt] = 0;
atresp(atbuf);
tty_flip_buffer_push(tty);
*((u16*)dpram_data.data_virt+0x01)=tail;
MBOX1 = 0x0;
MBOX2 = 0x0;
MBOX3 = 0x0; /*MAILBOX clean ch1 INTERPUT*/
MBOX4 = 0x0;
dprintk("\n");
return;
}
static unsigned char
transmit_char(struct uart_port *up, struct circ_buf *xmit)
{
unsigned char ret;
ret=xmit->buf[xmit->tail];
xmit->tail = (xmit->tail + 1) & (UART_XMIT_SIZE - 1);
up->icount.tx++;
return ret;
}
static void transmit_chars(struct uart_port *up)
{
struct circ_buf *xmit = &up->state->xmit;
u16 count;
unsigned short *txptr;
u16 head,tail;
char s;
txptr=dpram_data.data_virt;
if (up->x_char) {
dprintk("send x_char 0x%02x(%c)\n",up->x_char,up->x_char);
up->icount.tx++;
up->x_char = 0;
return;
}
if (uart_circ_empty(xmit) || uart_tx_stopped(up)) {
dpram_stop_tx(up);
return;
}
/*************************/
/*
<EFBFBD><EFBFBD><EFBFBD> <20><> <20><><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD>
<EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20><> <20><><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>
<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20><><EFBFBD> <20><><EFBFBD><EFBFBD>
<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20> <20><><EFBFBD><EFBFBD>
{
<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20><> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20><> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20><> <20><><EFBFBD><EFBFBD>. <20> <20><><EFBFBD><EFBFBD> <20><><EFBFBD> <20><> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20> <20><><EFBFBD><EFBFBD>
<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD> <20><> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20><> <20><><EFBFBD> msm <20><><EFBFBD> <20><> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>.
}
<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20> <20><><EFBFBD> len+tail
*/
head = *((u16*)dpram_data.data_virt + 0x200);
tail = *((u16*)dpram_data.data_virt + 0x201);
count=TX_AT_BUF-1;
//dvprintk("transmit_chars head = 0x%04x, tail = 0x%04x: \n",head,tail);
dmprintk("transmit_chars MBOX=%02x,%02x,%02x,%02x\n",MBOX1,MBOX2,MBOX3,MBOX4);
dprintk("\nS-> :");
while(count--)
{
//if(tail==(head+1)) {
// printk("FULL BUF0x%04x, tail = 0x%04x: \n",head,tail);
// break; // <20><><EFBFBD><EFBFBD><EFBFBD>, DPram <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD>
//}
if(head >= TX_AT_BUF){
head = 0x00;
}
s=transmit_char(up, xmit);
*((u8*)dpram_data.data_virt+head+0x404)=s;
dcharprint(s);
head++;
if (uart_circ_empty(xmit)) // <20><><EFBFBD><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD> <20> uarte <20><><EFBFBD><EFBFBD><EFBFBD>
break;
}
*((u16*)dpram_data.data_virt + 0x200) = head;
//dprintk("dpram_end_Tx head = 0x%04x, tail = 0x%04x: \n",head,tail);
MBOX1 = 0x1; /*MAILBOX set ch1 INTERPUT*/
MBOX2 = 0x0;
MBOX3 = 0x0;
MBOX4 = 0x0;
dprintk("\n");
/*************************/
if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS)
uart_write_wakeup(up);
if (uart_circ_empty(xmit))
dpram_stop_tx(up);
}
static void dpram_start_tx(struct uart_port *port)
{
//dprintk("dpram_start_tx\n");
transmit_chars(port);
}
static unsigned int dpram_tx_empty(struct uart_port *port)
{
dprintk("dpram_tx_empty\n");
return TIOCSER_TEMT;
}
static unsigned int dpram_get_mctrl(struct uart_port *port)
{
dprintk("dpram_get_mctrl\n");
return TIOCM_CTS | TIOCM_DSR | TIOCM_CAR;
}
static void dpram_set_mctrl(struct uart_port *port, unsigned int mctrl)
{
dprintk("dpram_set_mctrl\n");
}
static void dpram_break_ctl(struct uart_port *port, int break_state)
{
dprintk("dpram_break_ctl\n");
}
/*
* This handles the interrupt from one port.
*/
static irqreturn_t
dpram_irq_rx(int irq, void *dev_id)
{
//dprintk("\ndpram_irq_rx!!!!!!!!!!!\n");
receive_chars(dev_id);
return IRQ_HANDLED;
}
static int dpram_startup(struct uart_port *port)
{
int retval;
dprintk("\n!!!dpram_startup!!!!\n");
retval = request_irq(IRQ_RX, dpram_irq_rx, 0, "DPRAM_RX", port);
if (retval)
{
printk(KERN_ERR "%s: Cannot assign GPIO_0 IRQ\n", __FUNCTION__);
return retval;
}
set_irq_type(IRQ_RX, IRQ_TYPE_EDGE_FALLING);
receive_chars(port);
return retval;
}
static void dpram_shutdown(struct uart_port *port)
{
dprintk("dpram_shutdown\n");
free_irq(IRQ_RX, port);
}
static void
dpram_set_termios(struct uart_port *port, struct ktermios *termios,
struct ktermios *old)
{
dprintk("dpram_set_termios\n");
}
static void
dpram_pm(struct uart_port *port, unsigned int state,
unsigned int oldstate)
{
dprintk("dpram_pm %d %d\n", state, oldstate);
}
static void dpram_release_port(struct uart_port *port)
{
dprintk("dpram_release_port\n");
}
static int dpram_request_port(struct uart_port *port)
{
dprintk("dpram_request_port\n");
return 0;
}
static void dpram_config_port(struct uart_port *port, int flags)
{
dprintk("dpram_config_port\n");
port->type = PORT_DPRAM;
}
static int
dpram_verify_port(struct uart_port *port, struct serial_struct *ser)
{
/* we don't want the core code to modify any port params */
return -EINVAL;
}
static const char *
dpram_type(struct uart_port *port)
{
return "DPRAM";
}
struct uart_ops dpram_pops = {
.tx_empty = dpram_tx_empty,
.set_mctrl = dpram_set_mctrl,
.get_mctrl = dpram_get_mctrl,
.stop_tx = dpram_stop_tx,
.start_tx = dpram_start_tx,
.stop_rx = dpram_stop_rx,
.enable_ms = dpram_enable_ms,
.break_ctl = dpram_break_ctl,
.startup = dpram_startup,
.shutdown = dpram_shutdown,
.set_termios = dpram_set_termios,
.pm = dpram_pm,
.type = dpram_type,
.release_port = dpram_release_port,
.request_port = dpram_request_port,
.config_port = dpram_config_port,
.verify_port = dpram_verify_port,
};
static struct uart_port dpram_port = {
.type = PORT_DPRAM,
.iotype = UPIO_MEM,
.irq = IRQ_RX,
.uartclk = 115000 * 16,
.fifosize = 64,
.ops = &dpram_pops,
.line = 0,
};
static struct uart_driver dpram_reg = {
.owner = THIS_MODULE,
.driver_name = "DPRAM serial",
.dev_name = "ttyS",
.major = TTY_MAJOR,
.minor = 72,
.nr = 1,
};
/*END AT UART*/
int __init dpram_init(void)
{
int ret;
if (!machine_is_g900())
return -ENODEV;
/** maping **/
dpram_data.data_phys=PXA_CS5_PHYS;
dpram_data.data_virt=ioremap(dpram_data.data_phys, 0x8000);
if (! dpram_data.data_virt) {
ret=-ENOMEM;
goto err1;
}
dpram_port.mapbase=dpram_data.data_phys;
/** at uart **/
ret = uart_register_driver(&dpram_reg);
if (ret != 0)
goto err2;
ret = uart_add_one_port(&dpram_reg, &dpram_port);
if (ret != 0)
goto err3;
batt_probe();
return 0;
err3:
uart_unregister_driver(&dpram_reg);
err2:
iounmap(dpram_data.data_virt);
err1:
return ret;
}
void __exit dpram_exit(void)
{
batt_remove();
uart_remove_one_port(&dpram_reg, &dpram_port);
uart_unregister_driver(&dpram_reg);
iounmap(dpram_data.data_virt);
}
module_init(dpram_init);
module_exit(dpram_exit);
MODULE_AUTHOR("Angell Fear <angell@angellfear.ru>");
MODULE_DESCRIPTION("Dual Port RAM communication for Toshiba G900, PXA270<->MSM6280");
MODULE_LICENSE("GPL");