📄 hx4700_power.c
字号:
{ return 0;}int get_min_current(struct battery *b){ return -1900; /* negative 1900 mA */}int get_min_charge(struct battery *b){ return 0;}int get_max_voltage(struct battery *b){ return 4750; /* mV */}int get_max_current(struct battery *b){ return 1900; /* positive 1900 mA */}int get_max_charge(struct battery *b){ return 1;}int get_temp(struct battery *b){ update_data(0); return module_data.temp;}int get_voltage(struct battery *b){ update_data(0); return module_data.voltage;}int get_Current(struct battery *b){ update_data(0); return module_data.Current;}int get_charge(struct battery *b){ return module_data.current_accum / 4;}int get_status(struct battery *b){ return power_status == POWER_NONE? 0: 1;}static struct battery hx4700_power = { .name = "hx4700_primary", .id = "main", .get_min_voltage = get_min_voltage, .get_min_current = get_min_current, .get_min_charge = get_min_charge, .get_max_voltage = get_max_voltage, .get_max_current = get_max_current, .get_max_charge = get_max_charge, .get_temp = get_temp, .get_voltage = get_voltage, .get_current = get_Current, .get_charge = get_charge, .get_status = get_status,};static voidset_leds(int status, int Current, int battery_life){ if (status == APM_AC_ONLINE) { /* check life to update LEDs. LEDs are off when on battery */ /* * It has been observed that the Current is greater when the device is * suspended compared to when it is awake. So we have to use * different parameters here compared to bootldr */ if((Current < 32) && (battery_life == 100)) { /* Green LED on solid, amber off */ hx4700_set_led(0, 0, 16); hx4700_set_led(1, 16, 16); } else { /* Amber LED blinking, green off */ hx4700_set_led(0, 128, 256); hx4700_set_led(1, 0, 16); } } else { /* No charging power is applied; both LEDs off */ hx4700_set_led(0, 0, 16); hx4700_set_led(1, 0, 16); }}static voidhx4700_apm_get_power_status( struct apm_power_info *info ){ update_data(0); info->ac_line_status = (power_status == POWER_NONE) ? APM_AC_OFFLINE : APM_AC_ONLINE; /* It is possible to be hooked up to USB and getting some power from * it, but still having a current drain on the battery with a busy CPU */ info->battery_life = module_data.battery_life; if (info->ac_line_status == APM_AC_ONLINE) { info->battery_status = APM_BATTERY_STATUS_CHARGING; } else { info->time = module_data.minutes; info->units = APM_UNITS_MINS; module_data.acr_reset = 0; info->battery_status = APM_BATTERY_STATUS_CRITICAL; if(info->battery_life > 5) { info->battery_status = APM_BATTERY_STATUS_LOW; } if(info->battery_life > 20) { info->battery_status = APM_BATTERY_STATUS_HIGH; } } set_leds(info->ac_line_status, module_data.Current, module_data.battery_life);}static voidpower_change_task_handler(unsigned long enableirq){ unsigned int retval; int ac_in, usb_in; if(!module_data.initialized) return; retval = ipaq_asic3_read_gpio_status_d( &hx4700_asic3.dev ); ac_in = (retval & (1<<GPIOD_AC_IN_N)) == 0; usb_in = (retval & (1<<GPIOD_USBC_DETECT_N)) == 0; printk( KERN_INFO "hx4700 power_change: ac_in=%d\n", ac_in ); printk( KERN_INFO "hx4700 power_change: usb_in=%d\n", usb_in ); if (usb_in) { set_irq_type( module_data.usb_irq, IRQT_RISING ); } else { set_irq_type( module_data.usb_irq, IRQT_FALLING ); } if (ac_in) { set_irq_type( module_data.ac_irq, IRQT_RISING ); } else { set_irq_type( module_data.ac_irq, IRQT_FALLING ); } if (ac_in) { /* If we're on AC, it doesn't matter if we're on USB or not, use AC * only */ SET_HX4700_GPIO_N( CHARGE_EN, 1 ); SET_HX4700_GPIO( USB_CHARGE_RATE, 0 ); power_status = POWER_AC; set_leds(APM_AC_ONLINE, module_data.Current, module_data.battery_life); } else if (usb_in) { /* We're not on AC, but we are on USB, so charge with that */ SET_HX4700_GPIO( USB_CHARGE_RATE, 1 ); SET_HX4700_GPIO_N( CHARGE_EN, 1 ); power_status = POWER_USB; set_leds(APM_AC_ONLINE, module_data.Current, module_data.battery_life); } else { /* We're not on AC or USB, don't charge */ SET_HX4700_GPIO_N( CHARGE_EN, 0 ); SET_HX4700_GPIO( USB_CHARGE_RATE, 0 ); power_status = POWER_NONE; hx4700_clear_led(0); hx4700_clear_led(1); set_leds(APM_AC_OFFLINE, module_data.Current, module_data.battery_life); module_data.acr_reset = 0; } /* update_data(1); */ module_data.jiffies_64 = 0; /* Force a re-read on next try */ if(enableirq) { enable_irq(module_data.usb_irq); enable_irq(module_data.ac_irq); }}static intattach_isr(int irq, void *dev_id, struct pt_regs *regs){if(irq != module_data.usb_irq && irq != module_data.ac_irq) { printk("Bad irq: %d, not %d or %d\n", irq, module_data.usb_irq, module_data.ac_irq);} if(module_data.initialized) { SET_HX4700_GPIO_N( CHARGE_EN, 0 ); mod_timer(&module_data.irqtimer, jiffies + HZ/10); disable_irq(module_data.usb_irq); disable_irq(module_data.ac_irq); } return IRQ_HANDLED;}static intbattery_class_uevent(struct class_device *dev, char **envp, int num_envp, char *buffer, int buffer_size){ return 0;}static voidbattery_class_release(struct class_device *dev){}static voidbattery_class_class_release(struct class *class){}static voidw1_send_net_address(void){ if(net) { int i; w1_write_byte(0x55); /* Match Net Address */ for(i = 0; i < sizeof(module_data.net_address)/sizeof(module_data.net_address[0]); i++) { w1_write_byte(module_data.net_address[i]); } } else { w1_write_byte(0xcc); /* Skip Net Address */ }}static intw1_probe(struct device *dev){ int retval; module_data.initialized = 0; module_data.w1_irq = -1; module_data.usb_irq = -1; module_data.ac_irq = -1; module_data.base = 0; module_data.jiffies_64 = 0; module_data.acr_reset = 0; init_timer(&module_data.irqtimer); module_data.irqtimer.function = power_change_task_handler; module_data.irqtimer.data = 1; init_waitqueue_head(&module_data.irqwait); dev_set_drvdata(dev, &module_data); module_data.base = ioremap_nocache(0x0c000600, 64); module_data.battery_class = 0; if(!module_data.base) { printk(KERN_NOTICE "%s: Unable to map device\n", driver_name); return -ENODEV; } module_data.w1_irq = asic3_irq_base(&hx4700_asic3.dev) + ASIC3_OWM_IRQ; retval = request_irq(module_data.w1_irq, w1_isr, SA_INTERRUPT, driver_name, &module_data); if(retval) { printk(KERN_NOTICE "%s: Unable to get interrupt %d: %d\n", driver_name, module_data.w1_irq, retval); iounmap((void __iomem *)module_data.base); module_data.base = 0; module_data.w1_irq = -1; return -ENODEV; } w1_init(); if(w1_detect()) { unsigned int readval; int i; w1_write_byte(0x33); for(i = 0; i < sizeof(module_data.net_address)/sizeof(module_data.net_address[0]); i++) { w1_read_byte(&readval); module_data.net_address[i] = readval & 0xff; } if(module_data.net_address[0] != 0x30) { printk("Looks like wrong net address is " "%02x %02x %02x %02x %02x %02x %02x %02x\n", module_data.net_address[0], module_data.net_address[1], module_data.net_address[2], module_data.net_address[3], module_data.net_address[4], module_data.net_address[5], module_data.net_address[6], module_data.net_address[7] ); } } else { printk("Could not detect device for net address\n"); iounmap((void __iomem *)module_data.base); free_irq(module_data.w1_irq, &module_data); module_data.base = 0; module_data.w1_irq = -1; return -ENODEV; } if(w1_detect()) { unsigned int readval; if(battery_class_register(&hx4700_power)) { printk(KERN_ERR "%s: Could not register battery class\n", driver_name); } else { module_data.battery_class = 1; hx4700_power.class_dev.class->uevent = battery_class_uevent; hx4700_power.class_dev.class->release = battery_class_release; hx4700_power.class_dev.class->class_release = battery_class_class_release; } w1_send_net_address(); w1_write_byte(0x69); /* Read */ w1_write_byte(0x08); /* Special Feature Address */ w1_read_byte(&readval); if(!(readval & (1 << 7))) { printk("PS is low. Writing 1.\n"); /* The PS signal is low. The docs say to write a 1 to it to ensure * proper operation. */ if(w1_detect()) { w1_send_net_address(); w1_write_byte(0x6c); /* Write */ w1_write_byte(0x08); /* Special Feature Address */ w1_write_byte(readval | (1 << 7)); /* Data */ } else { printk("No detect when writing PS\n"); } } } else { printk("%s: Device not detected on init\n", driver_name); return -ENODEV; } module_data.usb_irq = asic3_irq_base( &hx4700_asic3.dev ) + ASIC3_GPIOD_IRQ_BASE + GPIOD_USBC_DETECT_N; module_data.ac_irq = asic3_irq_base( &hx4700_asic3.dev ) + ASIC3_GPIOD_IRQ_BASE + GPIOD_AC_IN_N; module_data.initialized = 1; /* Get first power state */ power_change_task_handler(0); /* USB IRQ */ if (request_irq( module_data.usb_irq, attach_isr, SA_INTERRUPT, "Hx4700 USB Detect", NULL ) != 0) { printk( KERN_ERR "Unable to configure USB detect interrupt.\n" ); module_data.usb_irq = -1; } /* AC IRQ */ if (request_irq( module_data.ac_irq, attach_isr, SA_INTERRUPT, "Hx4700 AC Detect", NULL ) != 0) { printk( KERN_ERR "Unable to configure AC detect interrupt.\n" ); module_data.ac_irq = -1; }#ifdef CONFIG_PM apm_get_power_status = hx4700_apm_get_power_status;#endif return 0;}static voidw1_deinit(void){ /* Turn off OWM clock. I hate to touch the others because they might be * used by other devices like MMC. */ asic3_set_clock_cdex(&hx4700_asic3.dev, /* CLOCK_CDEX_EX0 | CLOCK_CDEX_EX1 | */ CLOCK_CDEX_OWM, /* CLOCK_CDEX_EX0 | CLOCK_CDEX_EX1 | */ 0); /* Clear OWM_EN */ asic3_set_extcf_select(&hx4700_asic3.dev, ASIC3_EXTCF_OWM_EN, 0); mdelay(1);}static intw1_remove(struct device *dev){ printk("w1_remove\n");#ifdef CONFIG_PM apm_get_power_status = NULL;#endif if(module_data.base) { w1_deinit(); } if(module_data.battery_class) { battery_class_unregister(&hx4700_power); printk("battery class unregistered\n"); } if(module_data.base) { iounmap((void __iomem *)module_data.base); printk("Base unmapped\n"); } if(module_data.w1_irq != -1) { disable_irq(module_data.w1_irq); free_irq(module_data.w1_irq, &module_data); printk("w1 irq freed\n"); } if (module_data.ac_irq != -1) { disable_irq(module_data.ac_irq); free_irq( module_data.ac_irq, NULL ); printk("ac irq freed\n"); } if (module_data.usb_irq != -1) { disable_irq(module_data.usb_irq); free_irq( module_data.usb_irq, NULL ); printk("usb irq freed\n"); } while(timer_pending(&module_data.irqtimer)) { msleep(100); } del_timer(&module_data.irqtimer); return 0;}static intw1_suspend(struct device *dev, pm_message_t state){ // printk("w1_suspend\n"); w1_deinit(); return 0;}static intw1_resume(struct device *dev){ // printk("w1_resume\n"); w1_init(); /* check for changes to power that may have occurred */ power_change_task_handler(0); /* I think this will work to ensure the interrupt is unmasked. */ disable_irq(module_data.w1_irq); enable_irq(module_data.w1_irq); /* Clear OWM_SMB, set OWM_EN */ asic3_set_extcf_select(&hx4700_asic3.dev, ASIC3_EXTCF_OWM_EN, ASIC3_EXTCF_OWM_EN); return 0;}static struct device_driver w1_driver = { .name = "hx4700-power", .bus = &platform_bus_type, .probe = w1_probe, .remove = w1_remove, .suspend = w1_suspend, .resume = w1_resume};static int __initw1init(void){ printk(KERN_NOTICE "hx4700 Power Management Driver\n"); return driver_register(&w1_driver);}static void __exitw1exit(void){ driver_unregister(&w1_driver);}module_init(w1init);module_exit(w1exit);MODULE_AUTHOR("Aric D. Blumer, SDG Systems, LLC");MODULE_DESCRIPTION("hx4700 Power Management Driver");MODULE_LICENSE("GPL");/* vim600: set expandtab: */
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -