/* * * Based on arch/arm/mach-pxa/htcblueangel/blueangel_dpram.c * Autor: Angell Fear * MSM6280 to PXA270 communication on Dual Port RAM for Toshiba G900 * */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #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 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; } /*************************/ /* хед до куда надо читать таил до куда прочитали прочитали хед таил начали писать с тайл { проверяем не сдвинулся ли указатель на макс. и если так то скидываем в нуль проверяем чтобы не перезаписать то что msm еще не прочитал. } записали в хед 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; // выход, DPram похоже полон //} 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)) // выход если в uarte пусто 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 "); MODULE_DESCRIPTION("Dual Port RAM communication for Toshiba G900, PXA270<->MSM6280"); MODULE_LICENSE("GPL");