📄 hx4700_power.c
字号:
/* * Copyright 2005, SDG Systems, LLC * * This file is subject to the terms and conditions of the GNU General Public * License. See the file COPYING in the main directory of this archive for * more details. * * Sat 11 Jun 2005 Aric D. Blumer */#include <linux/module.h>#include <linux/platform_device.h>#include <linux/interrupt.h>#include <linux/soc/asic3_base.h>#include <linux/delay.h>#include <linux/battery.h>#include <asm/io.h>#include <asm/apm.h>#include <asm/hardware/ipaq-asic3.h>#include <asm/arch/hx4700-gpio.h>#include <asm/arch/hx4700-asic.h>#include <asm/arch/hx4700-core.h>#define DRIVER_NAME "hx4700_power"static char driver_name[] = DRIVER_NAME;static void w1_init(void);static void w1_deinit(void);static enum { POWER_NONE, POWER_AC, POWER_USB,} power_status;static int cdiv = 0x2;static int net = 0x0;module_param(cdiv, int, 0444);MODULE_PARM_DESC(cdiv, "Clock Divisor");module_param(net, int, 0444);MODULE_PARM_DESC(net, "Use net address");struct w1_data { volatile unsigned short __iomem *base; int w1_irq; wait_queue_head_t irqwait; unsigned short data_register; unsigned short irqstatus; int battery_class; struct timer_list irqtimer; unsigned int temp; unsigned int voltage; int Current; int current_accum; int acr_reset; int minutes; int battery_life; int ac_irq; int usb_irq; int initialized; char net_address[8]; u64 jiffies_64;} module_data;#define DS1WM_COMMAND 0#define DS1WM_TXRX 1#define DS1WM_IRQ 2#define DS1WM_IRQ_EN 3#define DS1WM_CLOCK_DIV 4#define WRITE_REGISTER(r, value) do { \ /* printk("Write %p = 0x%04x\n", (module_data.base + (r)), value); */ \ ((*(module_data.base + (r))) = (value)); \ } while(0)#define READ_REGISTER(R, r) do { \ (R = *(module_data.base + (r))); \ /* printk("Read %p = 0x%04x\n", (module_data.base + (r)), R); */ \ } while(0)static int w1_detect(void);static int w1_write_byte(unsigned short data);static int w1_read_byte(unsigned int *data);static void w1_send_net_address(void);DECLARE_MUTEX(update_mutex);static voidupdate_data(int force){ int flag = 0; int retries = 30; /* Only allow an update every second. */ if(!force && (module_data.jiffies_64 + 5 * HZ > jiffies_64)) { return; } if(down_trylock(&update_mutex)) { return; }try_again: if(w1_detect()) { unsigned int readval; w1_send_net_address(); w1_write_byte(0x69); /* Read */ w1_write_byte(0x00); /* Starting at offset 0x0 */ w1_read_byte(&readval); /* 0x0 */ if(readval == 0xff) { /* * We have an unknown read problem. Try again. */ msleep(1 * retries); if(0 == retries--) { // printk("hx4700_power: Whoops\n"); goto exit_please; } goto try_again; } /* Get rid of stuff we don't want */ w1_read_byte(&readval); /* 0x1 */ w1_read_byte(&readval); /* 0x2 */ w1_read_byte(&readval); /* 0x3 */ w1_read_byte(&readval); /* 0x4 */ w1_read_byte(&readval); /* 0x5 */ w1_read_byte(&readval); /* 0x6 */ w1_read_byte(&readval); /* 0x7 */ w1_read_byte(&readval); /* 0x8 */ w1_read_byte(&readval); /* 0x9 */ w1_read_byte(&readval); /* 0xa */ w1_read_byte(&readval); /* 0xb */ /* VOLTAGE */ w1_read_byte(&readval); /* 0xc */ if(readval == 0xff) { /* * We have an unknown read problem. Try again. */ msleep(1 * retries); if(0 == retries--) { // printk("hx4700_power: Whoops2\n"); goto exit_please; } goto try_again; } module_data.voltage = readval << 8; w1_read_byte(&readval); /* 0xd */ if(readval == 0xff) { /* * We have an unknown read problem. Try again. */ msleep(1 * retries); if(0 == retries--) { // printk("hx4700_power: Whoops2\n"); goto exit_please; } goto try_again; } module_data.voltage |= readval; module_data.voltage >>= 5; /* 4997 / 1024 is almost equal to 4880 / 1000 */ module_data.voltage *= 4997; module_data.voltage /= 1024; /****************/ /* Current */ w1_read_byte(&readval); /* 0xe */ if(readval == 0xff) { /* * We have an unknown read problem. Try again. */ msleep(1 * retries); if(0 == retries--) { // printk("hx4700_power: Whoops2\n"); goto exit_please; } goto try_again; } if(readval & (1 << 7)) { /* The sign bit is set */ module_data.Current = -1 ^ 0x7fff; } else { module_data.Current = 0; } module_data.Current |= readval << 8; w1_read_byte(&readval); /* 0xf */ if(readval == 0xff) { /* * We have an unknown read problem. Try again. */ msleep(1 * retries); if(0 == retries--) { // printk("hx4700_power: Whoops2\n"); goto exit_please; } goto try_again; } module_data.Current |= readval; module_data.Current >>= 3; /* 655360 / 1024*1024 is equal to 6250000 / 1000000 */ module_data.Current *= 655360; module_data.Current /= 1024 * 1024; /****************/ /* Current Accumulator */ w1_read_byte(&readval); /* 0x10 */ if(readval == 0xff) { /* * We have an unknown read problem. Try again. */ msleep(1 * retries); if(0 == retries--) { // printk("hx4700_power: Whoops2\n"); goto exit_please; } goto try_again; } if(readval & (1 << 7)) { /* The sign bit is set */ module_data.current_accum = -1 ^ 0xffff; } else { module_data.current_accum = 0; } module_data.current_accum |= readval << 8; w1_read_byte(&readval); /* 0x11 */ if(readval == 0xff) { /* * We have an unknown read problem. Try again. */ msleep(1 * retries); if(0 == retries--) { // printk("hx4700_power: Whoops2\n"); goto exit_please; } goto try_again; } module_data.current_accum |= readval; /* Units are 250 uAh. We wan't mAh. So each unit is 1/4 mAh. But * we want fractions of hours, so let's keep the 1/4 mAh units. */ /* Now convert to quarter hours by dividing by mA. */ if(module_data.Current == 0) { module_data.minutes = module_data.current_accum * 15; } else { module_data.minutes = -((module_data.current_accum * 15) / module_data.Current); } /****************/ /* Get rid of stuff we don't want */ w1_read_byte(&readval); /* 0x12 */ w1_read_byte(&readval); /* 0x13 */ w1_read_byte(&readval); /* 0x14 */ w1_read_byte(&readval); /* 0x15 */ w1_read_byte(&readval); /* 0x16 */ w1_read_byte(&readval); /* 0x17 */ /* Temperature */ w1_read_byte(&readval); /* 0x18 */ if(readval == 0xff) { /* * We have an unknown read problem. Try again. */ msleep(1 * retries); if(0 == retries--) { // printk("hx4700_power: Whoops2\n"); goto exit_please; } goto try_again; } module_data.temp = readval << 8; w1_read_byte(&readval); /* 0x19 */ if(readval == 0xff) { /* * We have an unknown read problem. Try again. */ msleep(1 * retries); if(0 == retries--) { // printk("hx4700_power: Whoops2\n"); goto exit_please; } goto try_again; } module_data.temp |= readval; module_data.temp >>= 5; module_data.temp += module_data.temp / 4; /****************/ if(module_data.voltage > 8000) { /* There is an unknown problem where things get into a strange * state. We know it because the voltage is reported as too * high. This is an attempt at robustness to correct the problem. * I'd like to fix it permanently, though. */ if(!flag) { printk("%s: Bad state detected. Retrying.\n", driver_name); flag = 1; mdelay(1); goto try_again; } else { printk("w1 fix didn't work. We give up.\n"); } } /* Reset ACR when battery gets full. */ if (module_data.Current >= 0 && module_data.Current < 32 && !module_data.acr_reset) { printk(KERN_INFO "ACR reset\n"); module_data.current_accum = 1800 * 4; if (w1_detect()) { w1_send_net_address(); w1_write_byte(0x6c); /* write */ w1_write_byte(0x10); /* current accum msb */ w1_write_byte((module_data.current_accum >> 8) & 0xff); w1_write_byte(module_data.current_accum & 0xff); module_data.acr_reset = 1; } } module_data.battery_life = (module_data.current_accum * 25) / 1800; if(module_data.battery_life > 100) module_data.battery_life = 100; if(module_data.battery_life < 0) module_data.battery_life = 0; } else { printk("%s: Device not detected\n", driver_name); module_data.temp = 0; module_data.voltage = 0; module_data.current_accum = 0; module_data.Current = 0; }exit_please: module_data.jiffies_64 = jiffies_64; up(&update_mutex);}static irqreturn_tw1_isr(int irq, void *opaque, struct pt_regs *regs){ /* * Note that the w1 control logic is quite slow, running at 1 MHz. It is * possible to read the IRQ status, and return and the interrupt still be * active. In this case, we're OK because we only update the irqstatus * when an actual interrupt causing bit is set. */ unsigned short tmp; /* Just reading the register clears the source */ READ_REGISTER(tmp, DS1WM_IRQ); if( tmp & (1 << 0) /* Presence Detect */ || tmp & (1 << 4) /* Receive Buffer Full */ ) { int junk, count; module_data.irqstatus = tmp; for(count = 0; count < 10; count++) { READ_REGISTER(module_data.data_register, DS1WM_TXRX); READ_REGISTER(junk, DS1WM_CLOCK_DIV); /* Delay */ READ_REGISTER(tmp, DS1WM_IRQ); if(!(tmp & (1 << 4))) break; } wake_up_interruptible(&module_data.irqwait); } return IRQ_HANDLED;}static voidw1_init(void){ /* Turn on external clocks and the OWM clock */ asic3_set_clock_cdex(&hx4700_asic3.dev, CLOCK_CDEX_EX0 | CLOCK_CDEX_EX1 | CLOCK_CDEX_OWM, CLOCK_CDEX_EX0 | CLOCK_CDEX_EX1 | CLOCK_CDEX_OWM); mdelay(1); asic3_set_extcf_reset(&hx4700_asic3.dev, (1 << 6), (1 << 6)); mdelay(1); asic3_set_extcf_reset(&hx4700_asic3.dev, (1 << 6), 0); mdelay(1); /* Clear OWM_SMB, set OWM_EN */ asic3_set_extcf_select(&hx4700_asic3.dev, ASIC3_EXTCF_OWM_SMB | ASIC3_EXTCF_OWM_EN, 0 | ASIC3_EXTCF_OWM_EN); mdelay(1); WRITE_REGISTER(DS1WM_CLOCK_DIV, cdiv); /* Enable interrupts: RBF, PD */ /* Set Interrupt Active High (IAS = 1) */ WRITE_REGISTER(DS1WM_IRQ_EN, (0 << 6) /* ENBSY */ | (0 << 5) /* ESINT */ | (1 << 4) /* ERBF */ | (0 << 3) /* ETMT */ | (0 << 2) /* ETBE */ | (1 << 1) /* IAS 0 = active low interrupt, 1 = active high */ | (1 << 0) /* EPD */ ); mdelay(1); /* Set asic3 interrupt status to zero. Hmmmmm. not accesible in * asic3_base.c */}static intw1_detect(void){ int retry_count = 5;try_again: module_data.irqstatus = 0; /* Set 1WR (one wire reset) bit */ WRITE_REGISTER(DS1WM_COMMAND, 1); if(wait_event_interruptible_timeout(module_data.irqwait, module_data.irqstatus & (1 << 0) /* PD set? */, HZ)) { if(0 == (module_data.irqstatus & (1 << 1)) /* PDR set? */) { mdelay(1); return 1; } } else { printk("%s: detect timeout\n", driver_name); } if(retry_count--) { mdelay(1); goto try_again; } return 0;}static intw1_read_byte(unsigned int *data){ module_data.irqstatus = 0; WRITE_REGISTER(DS1WM_TXRX, 0xff); if(wait_event_interruptible_timeout(module_data.irqwait, module_data.irqstatus & (1 << 4) /* RBF set? */, HZ)) { *data = module_data.data_register; return 1; } else { printk("%s: RBF not set\n", driver_name); } return 0;}static intw1_write_byte(unsigned short data){ module_data.irqstatus = 0; WRITE_REGISTER(DS1WM_TXRX, data); if(wait_event_interruptible_timeout(module_data.irqwait, module_data.irqstatus & (1 << 2) /* TBE set? */, HZ)) { return 1; } else { printk("%s: TBE not set\n", driver_name); } return 0;}int get_min_voltage(struct battery *b)
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -