⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 via-pmu.c

📁 linux 内核源代码
💻 C
📖 第 1 页 / 共 5 页
字号:
/* * 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 + -