📄 via-pmu.c
字号:
/* * Device driver for the via-pmu on Apple Powermacs. * * The VIA (versatile interface adapter) interfaces to the PMU, * a 6805 microprocessor core whose primary function is to control * battery charging and system power on the PowerBook 3400 and 2400. * The PMU also controls the ADB (Apple Desktop Bus) which connects * to the keyboard and mouse, as well as the non-volatile RAM * and the RTC (real time clock) chip. * * Copyright (C) 1998 Paul Mackerras and Fabio Riccardi. * Copyright (C) 2001-2002 Benjamin Herrenschmidt * * THIS DRIVER IS BECOMING A TOTAL MESS ! * - Cleanup atomically disabling reply to PMU events after * a sleep or a freq. switch * - Move sleep code out of here to pmac_pm, merge into new * common PM infrastructure * - Save/Restore PCI space properly * */#include <stdarg.h>#include <linux/types.h>#include <linux/errno.h>#include <linux/kernel.h>#include <linux/delay.h>#include <linux/sched.h>#include <linux/miscdevice.h>#include <linux/blkdev.h>#include <linux/pci.h>#include <linux/slab.h>#include <linux/poll.h>#include <linux/adb.h>#include <linux/pmu.h>#include <linux/cuda.h>#include <linux/smp_lock.h>#include <linux/module.h>#include <linux/spinlock.h>#include <linux/pm.h>#include <linux/proc_fs.h>#include <linux/init.h>#include <linux/interrupt.h>#include <linux/device.h>#include <linux/sysdev.h>#include <linux/freezer.h>#include <linux/syscalls.h>#include <linux/suspend.h>#include <linux/cpu.h>#include <asm/prom.h>#include <asm/machdep.h>#include <asm/io.h>#include <asm/pgtable.h>#include <asm/system.h>#include <asm/sections.h>#include <asm/irq.h>#include <asm/pmac_feature.h>#include <asm/pmac_pfunc.h>#include <asm/pmac_low_i2c.h>#include <asm/uaccess.h>#include <asm/mmu_context.h>#include <asm/cputable.h>#include <asm/time.h>#include <asm/backlight.h>#include "via-pmu-event.h"/* Some compile options */#undef SUSPEND_USES_PMU#define DEBUG_SLEEP#undef HACKED_PCI_SAVE/* Misc minor number allocated for /dev/pmu */#define PMU_MINOR 154/* How many iterations between battery polls */#define BATTERY_POLLING_COUNT 2static volatile unsigned char __iomem *via;/* VIA registers - spaced 0x200 bytes apart */#define RS 0x200 /* skip between registers */#define B 0 /* B-side data */#define A RS /* A-side data */#define DIRB (2*RS) /* B-side direction (1=output) */#define DIRA (3*RS) /* A-side direction (1=output) */#define T1CL (4*RS) /* Timer 1 ctr/latch (low 8 bits) */#define T1CH (5*RS) /* Timer 1 counter (high 8 bits) */#define T1LL (6*RS) /* Timer 1 latch (low 8 bits) */#define T1LH (7*RS) /* Timer 1 latch (high 8 bits) */#define T2CL (8*RS) /* Timer 2 ctr/latch (low 8 bits) */#define T2CH (9*RS) /* Timer 2 counter (high 8 bits) */#define SR (10*RS) /* Shift register */#define ACR (11*RS) /* Auxiliary control register */#define PCR (12*RS) /* Peripheral control register */#define IFR (13*RS) /* Interrupt flag register */#define IER (14*RS) /* Interrupt enable register */#define ANH (15*RS) /* A-side data, no handshake *//* Bits in B data register: both active low */#define TACK 0x08 /* Transfer acknowledge (input) */#define TREQ 0x10 /* Transfer request (output) *//* Bits in ACR */#define SR_CTRL 0x1c /* Shift register control bits */#define SR_EXT 0x0c /* Shift on external clock */#define SR_OUT 0x10 /* Shift out if 1 *//* Bits in IFR and IER */#define IER_SET 0x80 /* set bits in IER */#define IER_CLR 0 /* clear bits in IER */#define SR_INT 0x04 /* Shift register full/empty */#define CB2_INT 0x08#define CB1_INT 0x10 /* transition on CB1 input */static volatile enum pmu_state { idle, sending, intack, reading, reading_intr, locked,} pmu_state;static volatile enum int_data_state { int_data_empty, int_data_fill, int_data_ready, int_data_flush} int_data_state[2] = { int_data_empty, int_data_empty };static struct adb_request *current_req;static struct adb_request *last_req;static struct adb_request *req_awaiting_reply;static unsigned char interrupt_data[2][32];static int interrupt_data_len[2];static int int_data_last;static unsigned char *reply_ptr;static int data_index;static int data_len;static volatile int adb_int_pending;static volatile int disable_poll;static struct device_node *vias;static int pmu_kind = PMU_UNKNOWN;static int pmu_fully_inited;static int pmu_has_adb;static struct device_node *gpio_node;static unsigned char __iomem *gpio_reg;static int gpio_irq = NO_IRQ;static int gpio_irq_enabled = -1;static volatile int pmu_suspended;static spinlock_t pmu_lock;static u8 pmu_intr_mask;static int pmu_version;static int drop_interrupts;#if defined(CONFIG_PM_SLEEP) && defined(CONFIG_PPC32)static int option_lid_wakeup = 1;#endif /* CONFIG_PM_SLEEP && CONFIG_PPC32 */#if (defined(CONFIG_PM_SLEEP)&&defined(CONFIG_PPC32))||defined(CONFIG_PMAC_BACKLIGHT_LEGACY)static int sleep_in_progress;#endifstatic unsigned long async_req_locks;static unsigned int pmu_irq_stats[11];static struct proc_dir_entry *proc_pmu_root;static struct proc_dir_entry *proc_pmu_info;static struct proc_dir_entry *proc_pmu_irqstats;static struct proc_dir_entry *proc_pmu_options;static int option_server_mode;int pmu_battery_count;int pmu_cur_battery;unsigned int pmu_power_flags = PMU_PWR_AC_PRESENT;struct pmu_battery_info pmu_batteries[PMU_MAX_BATTERIES];static int query_batt_timer = BATTERY_POLLING_COUNT;static struct adb_request batt_req;static struct proc_dir_entry *proc_pmu_batt[PMU_MAX_BATTERIES];int __fake_sleep;int asleep;BLOCKING_NOTIFIER_HEAD(sleep_notifier_list);#ifdef CONFIG_ADBstatic int adb_dev_map;static int pmu_adb_flags;static int pmu_probe(void);static int pmu_init(void);static int pmu_send_request(struct adb_request *req, int sync);static int pmu_adb_autopoll(int devs);static int pmu_adb_reset_bus(void);#endif /* CONFIG_ADB */static int init_pmu(void);static void pmu_start(void);static irqreturn_t via_pmu_interrupt(int irq, void *arg);static irqreturn_t gpio1_interrupt(int irq, void *arg);static int proc_get_info(char *page, char **start, off_t off, int count, int *eof, void *data);static int proc_get_irqstats(char *page, char **start, off_t off, int count, int *eof, void *data);static void pmu_pass_intr(unsigned char *data, int len);static int proc_get_batt(char *page, char **start, off_t off, int count, int *eof, void *data);static int proc_read_options(char *page, char **start, off_t off, int count, int *eof, void *data);static int proc_write_options(struct file *file, const char __user *buffer, unsigned long count, void *data);#ifdef CONFIG_ADBstruct adb_driver via_pmu_driver = { "PMU", pmu_probe, pmu_init, pmu_send_request, pmu_adb_autopoll, pmu_poll_adb, pmu_adb_reset_bus};#endif /* CONFIG_ADB */extern void low_sleep_handler(void);extern void enable_kernel_altivec(void);extern void enable_kernel_fp(void);#ifdef DEBUG_SLEEPint pmu_polled_request(struct adb_request *req);int pmu_wink(struct adb_request *req);#endif/* * This table indicates for each PMU opcode: * - the number of data bytes to be sent with the command, or -1 * if a length byte should be sent, * - the number of response bytes which the PMU will return, or * -1 if it will send a length byte. */static const s8 pmu_data_len[256][2] = {/* 0 1 2 3 4 5 6 7 *//*00*/ {-1, 0},{-1, 0},{-1, 0},{-1, 0},{-1, 0},{-1, 0},{-1, 0},{-1, 0},/*08*/ {-1,-1},{-1,-1},{-1,-1},{-1,-1},{-1,-1},{-1,-1},{-1,-1},{-1,-1},/*10*/ { 1, 0},{ 1, 0},{-1, 0},{-1, 0},{-1, 0},{-1, 0},{-1, 0},{-1, 0},/*18*/ { 0, 1},{ 0, 1},{-1,-1},{-1,-1},{-1,-1},{-1,-1},{-1,-1},{ 0, 0},/*20*/ {-1, 0},{ 0, 0},{ 2, 0},{ 1, 0},{ 1, 0},{-1, 0},{-1, 0},{-1, 0},/*28*/ { 0,-1},{ 0,-1},{-1,-1},{-1,-1},{-1,-1},{-1,-1},{-1,-1},{ 0,-1},/*30*/ { 4, 0},{20, 0},{-1, 0},{ 3, 0},{-1, 0},{-1, 0},{-1, 0},{-1, 0},/*38*/ { 0, 4},{ 0,20},{ 2,-1},{ 2, 1},{ 3,-1},{-1,-1},{-1,-1},{ 4, 0},/*40*/ { 1, 0},{ 1, 0},{-1, 0},{-1, 0},{-1, 0},{-1, 0},{-1, 0},{-1, 0},/*48*/ { 0, 1},{ 0, 1},{-1,-1},{ 1, 0},{ 1, 0},{-1,-1},{-1,-1},{-1,-1},/*50*/ { 1, 0},{ 0, 0},{ 2, 0},{ 2, 0},{-1, 0},{ 1, 0},{ 3, 0},{ 1, 0},/*58*/ { 0, 1},{ 1, 0},{ 0, 2},{ 0, 2},{ 0,-1},{-1,-1},{-1,-1},{-1,-1},/*60*/ { 2, 0},{-1, 0},{-1, 0},{-1, 0},{-1, 0},{-1, 0},{-1, 0},{-1, 0},/*68*/ { 0, 3},{ 0, 3},{ 0, 2},{ 0, 8},{ 0,-1},{ 0,-1},{-1,-1},{-1,-1},/*70*/ { 1, 0},{ 1, 0},{ 1, 0},{-1, 0},{-1, 0},{-1, 0},{-1, 0},{-1, 0},/*78*/ { 0,-1},{ 0,-1},{-1,-1},{-1,-1},{-1,-1},{ 5, 1},{ 4, 1},{ 4, 1},/*80*/ { 4, 0},{-1, 0},{ 0, 0},{-1, 0},{-1, 0},{-1, 0},{-1, 0},{-1, 0},/*88*/ { 0, 5},{-1,-1},{-1,-1},{-1,-1},{-1,-1},{-1,-1},{-1,-1},{-1,-1},/*90*/ { 1, 0},{ 2, 0},{-1, 0},{-1, 0},{-1, 0},{-1, 0},{-1, 0},{-1, 0},/*98*/ { 0, 1},{ 0, 1},{-1,-1},{-1,-1},{-1,-1},{-1,-1},{-1,-1},{-1,-1},/*a0*/ { 2, 0},{ 2, 0},{ 2, 0},{ 4, 0},{-1, 0},{ 0, 0},{-1, 0},{-1, 0},/*a8*/ { 1, 1},{ 1, 0},{ 3, 0},{ 2, 0},{-1,-1},{-1,-1},{-1,-1},{-1,-1},/*b0*/ {-1, 0},{-1, 0},{-1, 0},{-1, 0},{-1, 0},{-1, 0},{-1, 0},{-1, 0},/*b8*/ {-1,-1},{-1,-1},{-1,-1},{-1,-1},{-1,-1},{-1,-1},{-1,-1},{-1,-1},/*c0*/ {-1, 0},{-1, 0},{-1, 0},{-1, 0},{-1, 0},{-1, 0},{-1, 0},{-1, 0},/*c8*/ {-1,-1},{-1,-1},{-1,-1},{-1,-1},{-1,-1},{-1,-1},{-1,-1},{-1,-1},/*d0*/ { 0, 0},{-1, 0},{-1, 0},{-1, 0},{-1, 0},{-1, 0},{-1, 0},{-1, 0},/*d8*/ { 1, 1},{ 1, 1},{-1,-1},{-1,-1},{ 0, 1},{ 0,-1},{-1,-1},{-1,-1},/*e0*/ {-1, 0},{ 4, 0},{ 0, 1},{-1, 0},{-1, 0},{ 4, 0},{-1, 0},{-1, 0},/*e8*/ { 3,-1},{-1,-1},{ 0, 1},{-1,-1},{ 0,-1},{-1,-1},{-1,-1},{ 0, 0},/*f0*/ {-1, 0},{-1, 0},{-1, 0},{-1, 0},{-1, 0},{-1, 0},{-1, 0},{-1, 0},/*f8*/ {-1,-1},{-1,-1},{-1,-1},{-1,-1},{-1,-1},{-1,-1},{-1,-1},{-1,-1},};static char *pbook_type[] = { "Unknown PowerBook", "PowerBook 2400/3400/3500(G3)", "PowerBook G3 Series", "1999 PowerBook G3", "Core99"};int __init find_via_pmu(void){ u64 taddr; const u32 *reg; if (via != 0) return 1; vias = of_find_node_by_name(NULL, "via-pmu"); if (vias == NULL) return 0; reg = of_get_property(vias, "reg", NULL); if (reg == NULL) { printk(KERN_ERR "via-pmu: No \"reg\" property !\n"); goto fail; } taddr = of_translate_address(vias, reg); if (taddr == OF_BAD_ADDR) { printk(KERN_ERR "via-pmu: Can't translate address !\n"); goto fail; } spin_lock_init(&pmu_lock); pmu_has_adb = 1; pmu_intr_mask = PMU_INT_PCEJECT | PMU_INT_SNDBRT | PMU_INT_ADB | PMU_INT_TICK; if (vias->parent->name && ((strcmp(vias->parent->name, "ohare") == 0) || of_device_is_compatible(vias->parent, "ohare"))) pmu_kind = PMU_OHARE_BASED; else if (of_device_is_compatible(vias->parent, "paddington")) pmu_kind = PMU_PADDINGTON_BASED; else if (of_device_is_compatible(vias->parent, "heathrow")) pmu_kind = PMU_HEATHROW_BASED; else if (of_device_is_compatible(vias->parent, "Keylargo") || of_device_is_compatible(vias->parent, "K2-Keylargo")) { struct device_node *gpiop; struct device_node *adbp; u64 gaddr = OF_BAD_ADDR; pmu_kind = PMU_KEYLARGO_BASED; adbp = of_find_node_by_type(NULL, "adb"); pmu_has_adb = (adbp != NULL); of_node_put(adbp); pmu_intr_mask = PMU_INT_PCEJECT | PMU_INT_SNDBRT | PMU_INT_ADB | PMU_INT_TICK | PMU_INT_ENVIRONMENT; gpiop = of_find_node_by_name(NULL, "gpio"); if (gpiop) { reg = of_get_property(gpiop, "reg", NULL); if (reg) gaddr = of_translate_address(gpiop, reg); if (gaddr != OF_BAD_ADDR) gpio_reg = ioremap(gaddr, 0x10); } if (gpio_reg == NULL) { printk(KERN_ERR "via-pmu: Can't find GPIO reg !\n"); goto fail_gpio; } } else pmu_kind = PMU_UNKNOWN; via = ioremap(taddr, 0x2000); if (via == NULL) { printk(KERN_ERR "via-pmu: Can't map address !\n"); goto fail; } out_8(&via[IER], IER_CLR | 0x7f); /* disable all intrs */ out_8(&via[IFR], 0x7f); /* clear IFR */ pmu_state = idle; if (!init_pmu()) { via = NULL; return 0; } printk(KERN_INFO "PMU driver v%d initialized for %s, firmware: %02x\n", PMU_DRIVER_VERSION, pbook_type[pmu_kind], pmu_version); sys_ctrler = SYS_CTRLER_PMU; return 1; fail: of_node_put(vias); iounmap(gpio_reg); gpio_reg = NULL; fail_gpio: vias = NULL; return 0;}#ifdef CONFIG_ADBstatic int pmu_probe(void){ return vias == NULL? -ENODEV: 0;}static int __init pmu_init(void){ if (vias == NULL) return -ENODEV; return 0;}#endif /* CONFIG_ADB *//* * We can't wait until pmu_init gets called, that happens too late. * It happens after IDE and SCSI initialization, which can take a few * seconds, and by that time the PMU could have given up on us and * turned us off. * Thus this is called with arch_initcall rather than device_initcall. */static int __init via_pmu_start(void){ unsigned int irq; if (vias == NULL) return -ENODEV; batt_req.complete = 1; irq = irq_of_parse_and_map(vias, 0); if (irq == NO_IRQ) { printk(KERN_ERR "via-pmu: can't map interrupt\n"); return -ENODEV; } if (request_irq(irq, via_pmu_interrupt, 0, "VIA-PMU", (void *)0)) { printk(KERN_ERR "via-pmu: can't request irq %d\n", irq); return -ENODEV; } if (pmu_kind == PMU_KEYLARGO_BASED) { gpio_node = of_find_node_by_name(NULL, "extint-gpio1"); if (gpio_node == NULL) gpio_node = of_find_node_by_name(NULL, "pmu-interrupt"); if (gpio_node) gpio_irq = irq_of_parse_and_map(gpio_node, 0); if (gpio_irq != NO_IRQ) { if (request_irq(gpio_irq, gpio1_interrupt, 0, "GPIO1 ADB", (void *)0)) printk(KERN_ERR "pmu: can't get irq %d" " (GPIO1)\n", gpio_irq); else gpio_irq_enabled = 1; } } /* Enable interrupts */ out_8(&via[IER], IER_SET | SR_INT | CB1_INT); pmu_fully_inited = 1; /* Make sure PMU settle down before continuing. This is _very_ important * since the IDE probe may shut interrupts down for quite a bit of time. If * a PMU communication is pending while this happens, the PMU may timeout * Not that on Core99 machines, the PMU keeps sending us environement * messages, we should find a way to either fix IDE or make it call * pmu_suspend() before masking interrupts. This can also happens while * scolling with some fbdevs. */ do { pmu_poll(); } while (pmu_state != idle); return 0;}arch_initcall(via_pmu_start);/* * This has to be done after pci_init, which is a subsys_initcall. */static int __init via_pmu_dev_init(void){ if (vias == NULL) return -ENODEV;#ifdef CONFIG_PMAC_BACKLIGHT /* Initialize backlight */ pmu_backlight_init();#endif#ifdef CONFIG_PPC32 if (machine_is_compatible("AAPL,3400/2400") || machine_is_compatible("AAPL,3500")) { int mb = pmac_call_feature(PMAC_FTR_GET_MB_INFO, NULL, PMAC_MB_INFO_MODEL, 0); pmu_battery_count = 1; if (mb == PMAC_TYPE_COMET) pmu_batteries[0].flags |= PMU_BATT_TYPE_COMET; else pmu_batteries[0].flags |= PMU_BATT_TYPE_HOOPER; } else if (machine_is_compatible("AAPL,PowerBook1998") || machine_is_compatible("PowerBook1,1")) { pmu_battery_count = 2; pmu_batteries[0].flags |= PMU_BATT_TYPE_SMART; pmu_batteries[1].flags |= PMU_BATT_TYPE_SMART; } else { struct device_node* prim = of_find_node_by_name(NULL, "power-mgt"); const u32 *prim_info = NULL; if (prim) prim_info = of_get_property(prim, "prim-info", NULL); if (prim_info) { /* Other stuffs here yet unknown */ pmu_battery_count = (prim_info[6] >> 16) & 0xff; pmu_batteries[0].flags |= PMU_BATT_TYPE_SMART; if (pmu_battery_count > 1) pmu_batteries[1].flags |= PMU_BATT_TYPE_SMART; } of_node_put(prim); }#endif /* CONFIG_PPC32 */ /* Create /proc/pmu */ proc_pmu_root = proc_mkdir("pmu", NULL); if (proc_pmu_root) { long i; for (i=0; i<pmu_battery_count; i++) { char title[16]; sprintf(title, "battery_%ld", i); proc_pmu_batt[i] = create_proc_read_entry(title, 0, proc_pmu_root, proc_get_batt, (void *)i); } proc_pmu_info = create_proc_read_entry("info", 0, proc_pmu_root, proc_get_info, NULL); proc_pmu_irqstats = create_proc_read_entry("interrupts", 0, proc_pmu_root, proc_get_irqstats, NULL); proc_pmu_options = create_proc_entry("options", 0600, proc_pmu_root); if (proc_pmu_options) { proc_pmu_options->read_proc = proc_read_options; proc_pmu_options->write_proc = proc_write_options; } } return 0;}device_initcall(via_pmu_dev_init);static intinit_pmu(void){ int timeout;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -