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

📄 via-pmu.c

📁 linux 内核源代码
💻 C
📖 第 1 页 / 共 5 页
字号:
/* Construct and send a pmu request */intpmu_request(struct adb_request *req, void (*done)(struct adb_request *),	    int nbytes, ...){	va_list list;	int i;	if (vias == NULL)		return -ENXIO;	if (nbytes < 0 || nbytes > 32) {		printk(KERN_ERR "pmu_request: bad nbytes (%d)\n", nbytes);		req->complete = 1;		return -EINVAL;	}	req->nbytes = nbytes;	req->done = done;	va_start(list, nbytes);	for (i = 0; i < nbytes; ++i)		req->data[i] = va_arg(list, int);	va_end(list);	req->reply_len = 0;	req->reply_expected = 0;	return pmu_queue_request(req);}intpmu_queue_request(struct adb_request *req){	unsigned long flags;	int nsend;	if (via == NULL) {		req->complete = 1;		return -ENXIO;	}	if (req->nbytes <= 0) {		req->complete = 1;		return 0;	}	nsend = pmu_data_len[req->data[0]][0];	if (nsend >= 0 && req->nbytes != nsend + 1) {		req->complete = 1;		return -EINVAL;	}	req->next = NULL;	req->sent = 0;	req->complete = 0;	spin_lock_irqsave(&pmu_lock, flags);	if (current_req != 0) {		last_req->next = req;		last_req = req;	} else {		current_req = req;		last_req = req;		if (pmu_state == idle)			pmu_start();	}	spin_unlock_irqrestore(&pmu_lock, flags);	return 0;}static inline voidwait_for_ack(void){	/* Sightly increased the delay, I had one occurrence of the message	 * reported	 */	int timeout = 4000;	while ((in_8(&via[B]) & TACK) == 0) {		if (--timeout < 0) {			printk(KERN_ERR "PMU not responding (!ack)\n");			return;		}		udelay(10);	}}/* New PMU seems to be very sensitive to those timings, so we make sure * PCI is flushed immediately */static inline voidsend_byte(int x){	volatile unsigned char __iomem *v = via;	out_8(&v[ACR], in_8(&v[ACR]) | SR_OUT | SR_EXT);	out_8(&v[SR], x);	out_8(&v[B], in_8(&v[B]) & ~TREQ);		/* assert TREQ */	(void)in_8(&v[B]);}static inline voidrecv_byte(void){	volatile unsigned char __iomem *v = via;	out_8(&v[ACR], (in_8(&v[ACR]) & ~SR_OUT) | SR_EXT);	in_8(&v[SR]);		/* resets SR */	out_8(&v[B], in_8(&v[B]) & ~TREQ);	(void)in_8(&v[B]);}static inline voidpmu_done(struct adb_request *req){	void (*done)(struct adb_request *) = req->done;	mb();	req->complete = 1;    	/* Here, we assume that if the request has a done member, the    	 * struct request will survive to setting req->complete to 1    	 */	if (done)		(*done)(req);}static voidpmu_start(void){	struct adb_request *req;	/* assert pmu_state == idle */	/* get the packet to send */	req = current_req;	if (req == 0 || pmu_state != idle	    || (/*req->reply_expected && */req_awaiting_reply))		return;	pmu_state = sending;	data_index = 1;	data_len = pmu_data_len[req->data[0]][0];	/* Sounds safer to make sure ACK is high before writing. This helped	 * kill a problem with ADB and some iBooks	 */	wait_for_ack();	/* set the shift register to shift out and send a byte */	send_byte(req->data[0]);}voidpmu_poll(void){	if (!via)		return;	if (disable_poll)		return;	via_pmu_interrupt(0, NULL);}voidpmu_poll_adb(void){	if (!via)		return;	if (disable_poll)		return;	/* Kicks ADB read when PMU is suspended */	adb_int_pending = 1;	do {		via_pmu_interrupt(0, NULL);	} while (pmu_suspended && (adb_int_pending || pmu_state != idle		|| req_awaiting_reply));}voidpmu_wait_complete(struct adb_request *req){	if (!via)		return;	while((pmu_state != idle && pmu_state != locked) || !req->complete)		via_pmu_interrupt(0, NULL);}/* This function loops until the PMU is idle and prevents it from * anwsering to ADB interrupts. pmu_request can still be called. * This is done to avoid spurrious shutdowns when we know we'll have * interrupts switched off for a long time */voidpmu_suspend(void){	unsigned long flags;#ifdef SUSPEND_USES_PMU	struct adb_request *req;#endif	if (!via)		return;		spin_lock_irqsave(&pmu_lock, flags);	pmu_suspended++;	if (pmu_suspended > 1) {		spin_unlock_irqrestore(&pmu_lock, flags);		return;	}	do {		spin_unlock_irqrestore(&pmu_lock, flags);		if (req_awaiting_reply)			adb_int_pending = 1;		via_pmu_interrupt(0, NULL);		spin_lock_irqsave(&pmu_lock, flags);		if (!adb_int_pending && pmu_state == idle && !req_awaiting_reply) {#ifdef SUSPEND_USES_PMU			pmu_request(&req, NULL, 2, PMU_SET_INTR_MASK, 0);			spin_unlock_irqrestore(&pmu_lock, flags);			while(!req.complete)				pmu_poll();#else /* SUSPEND_USES_PMU */			if (gpio_irq >= 0)				disable_irq_nosync(gpio_irq);			out_8(&via[IER], CB1_INT | IER_CLR);			spin_unlock_irqrestore(&pmu_lock, flags);#endif /* SUSPEND_USES_PMU */			break;		}	} while (1);}voidpmu_resume(void){	unsigned long flags;	if (!via || (pmu_suspended < 1))		return;	spin_lock_irqsave(&pmu_lock, flags);	pmu_suspended--;	if (pmu_suspended > 0) {		spin_unlock_irqrestore(&pmu_lock, flags);		return;	}	adb_int_pending = 1;#ifdef SUSPEND_USES_PMU	pmu_request(&req, NULL, 2, PMU_SET_INTR_MASK, pmu_intr_mask);	spin_unlock_irqrestore(&pmu_lock, flags);	while(!req.complete)		pmu_poll();#else /* SUSPEND_USES_PMU */	if (gpio_irq >= 0)		enable_irq(gpio_irq);	out_8(&via[IER], CB1_INT | IER_SET);	spin_unlock_irqrestore(&pmu_lock, flags);	pmu_poll();#endif /* SUSPEND_USES_PMU */}/* Interrupt data could be the result data from an ADB cmd */static voidpmu_handle_data(unsigned char *data, int len){	unsigned char ints, pirq;	int i = 0;	asleep = 0;	if (drop_interrupts || len < 1) {		adb_int_pending = 0;		pmu_irq_stats[8]++;		return;	}	/* Get PMU interrupt mask */	ints = data[0];	/* Record zero interrupts for stats */	if (ints == 0)		pmu_irq_stats[9]++;	/* Hack to deal with ADB autopoll flag */	if (ints & PMU_INT_ADB)		ints &= ~(PMU_INT_ADB_AUTO | PMU_INT_AUTO_SRQ_POLL);next:	if (ints == 0) {		if (i > pmu_irq_stats[10])			pmu_irq_stats[10] = i;		return;	}	for (pirq = 0; pirq < 8; pirq++)		if (ints & (1 << pirq))			break;	pmu_irq_stats[pirq]++;	i++;	ints &= ~(1 << pirq);	/* Note: for some reason, we get an interrupt with len=1,	 * data[0]==0 after each normal ADB interrupt, at least	 * on the Pismo. Still investigating...  --BenH	 */	if ((1 << pirq) & PMU_INT_ADB) {		if ((data[0] & PMU_INT_ADB_AUTO) == 0) {			struct adb_request *req = req_awaiting_reply;			if (req == 0) {				printk(KERN_ERR "PMU: extra ADB reply\n");				return;			}			req_awaiting_reply = NULL;			if (len <= 2)				req->reply_len = 0;			else {				memcpy(req->reply, data + 1, len - 1);				req->reply_len = len - 1;			}			pmu_done(req);		} else {			if (len == 4 && data[1] == 0x2c) {				extern int xmon_wants_key, xmon_adb_keycode;				if (xmon_wants_key) {					xmon_adb_keycode = data[2];					return;				}			}#ifdef CONFIG_ADB			/*			 * XXX On the [23]400 the PMU gives us an up			 * event for keycodes 0x74 or 0x75 when the PC			 * card eject buttons are released, so we			 * ignore those events.			 */			if (!(pmu_kind == PMU_OHARE_BASED && len == 4			      && data[1] == 0x2c && data[3] == 0xff			      && (data[2] & ~1) == 0xf4))				adb_input(data+1, len-1, 1);#endif /* CONFIG_ADB */				}	}	/* Sound/brightness button pressed */	else if ((1 << pirq) & PMU_INT_SNDBRT) {#ifdef CONFIG_PMAC_BACKLIGHT		if (len == 3)			pmac_backlight_set_legacy_brightness_pmu(data[1] >> 4);#endif	}	/* Tick interrupt */	else if ((1 << pirq) & PMU_INT_TICK) {		/* Environement or tick interrupt, query batteries */		if (pmu_battery_count) {			if ((--query_batt_timer) == 0) {				query_battery_state();				query_batt_timer = BATTERY_POLLING_COUNT;			}		}        }	else if ((1 << pirq) & PMU_INT_ENVIRONMENT) {		if (pmu_battery_count)			query_battery_state();		pmu_pass_intr(data, len);		/* len == 6 is probably a bad check. But how do I		 * know what PMU versions send what events here? */		if (len == 6) {			via_pmu_event(PMU_EVT_POWER, !!(data[1]&8));			via_pmu_event(PMU_EVT_LID, data[1]&1);		}	} else {	       pmu_pass_intr(data, len);	}	goto next;}static struct adb_request*pmu_sr_intr(void){	struct adb_request *req;	int bite = 0;	if (via[B] & TREQ) {		printk(KERN_ERR "PMU: spurious SR intr (%x)\n", via[B]);		out_8(&via[IFR], SR_INT);		return NULL;	}	/* The ack may not yet be low when we get the interrupt */	while ((in_8(&via[B]) & TACK) != 0)			;	/* if reading grab the byte, and reset the interrupt */	if (pmu_state == reading || pmu_state == reading_intr)		bite = in_8(&via[SR]);	/* reset TREQ and wait for TACK to go high */	out_8(&via[B], in_8(&via[B]) | TREQ);	wait_for_ack();	switch (pmu_state) {	case sending:		req = current_req;		if (data_len < 0) {			data_len = req->nbytes - 1;			send_byte(data_len);			break;		}		if (data_index <= data_len) {			send_byte(req->data[data_index++]);			break;		}		req->sent = 1;		data_len = pmu_data_len[req->data[0]][1];		if (data_len == 0) {			pmu_state = idle;			current_req = req->next;			if (req->reply_expected)				req_awaiting_reply = req;			else				return req;		} else {			pmu_state = reading;			data_index = 0;			reply_ptr = req->reply + req->reply_len;			recv_byte();		}		break;	case intack:		data_index = 0;		data_len = -1;		pmu_state = reading_intr;		reply_ptr = interrupt_data[int_data_last];		recv_byte();		if (gpio_irq >= 0 && !gpio_irq_enabled) {			enable_irq(gpio_irq);			gpio_irq_enabled = 1;		}		break;	case reading:	case reading_intr:		if (data_len == -1) {			data_len = bite;			if (bite > 32)				printk(KERN_ERR "PMU: bad reply len %d\n", bite);		} else if (data_index < 32) {			reply_ptr[data_index++] = bite;		}		if (data_index < data_len) {			recv_byte();			break;		}		if (pmu_state == reading_intr) {			pmu_state = idle;			int_data_state[int_data_last] = int_data_ready;			interrupt_data_len[int_data_last] = data_len;		} else {			req = current_req;			/* 			 * For PMU sleep and freq change requests, we lock the			 * PMU until it's explicitly unlocked. This avoids any			 * spurrious event polling getting in			 */			current_req = req->next;			req->reply_len += data_index;			if (req->data[0] == PMU_SLEEP || req->data[0] == PMU_CPU_SPEED)				pmu_state = locked;			else				pmu_state = idle;			return req;		}		break;	default:		printk(KERN_ERR "via_pmu_interrupt: unknown state %d?\n",		       pmu_state);	}	return NULL;}static irqreturn_tvia_pmu_interrupt(int irq, void *arg){	unsigned long flags;	int intr;	int nloop = 0;	int int_data = -1;	struct adb_request *req = NULL;	int handled = 0;	/* This is a bit brutal, we can probably do better */	spin_lock_irqsave(&pmu_lock, flags);	++disable_poll;		for (;;) {		intr = in_8(&via[IFR]) & (SR_INT | CB1_INT);		if (intr == 0)			break;		handled = 1;		if (++nloop > 1000) {			printk(KERN_DEBUG "PMU: stuck in intr loop, "			       "intr=%x, ier=%x pmu_state=%d\n",			       intr, in_8(&via[IER]), pmu_state);			break;		}		out_8(&via[IFR], intr);		if (intr & CB1_INT) {			adb_int_pending = 1;			pmu_irq_stats[0]++;		}		if (intr & SR_INT) {			req = pmu_sr_intr();			if (req)				break;		}	}recheck:	if (pmu_state == idle) {		if (adb_int_pending) {			if (int_data_state[0] == int_data_empty)				int_data_last = 0;			else if (int_data_state[1] == int_data_empty)				int_data_last = 1;			else				goto no_free_slot;			pmu_state = intack;			int_data_state[int_data_last] = int_data_fill;			/* Sounds safer to make sure ACK is high before writing.			 * This helped kill a problem with ADB and some iBooks			 */			wait_for_ack();			send_byte(PMU_INT_ACK);			adb_int_pending = 0;		} else if (current_req)			pmu_start();	}no_free_slot:				/* Mark the oldest buffer for flushing */	if (int_data_state[!int_data_last] == int_data_ready) {		int_data_state[!int_data_last] = int_data_flush;		int_data = !int_data_last;	} else if (int_data_state[int_data_last] == int_data_ready) {		int_data_state[int_data_last] = int_data_flush;		int_data = int_data_last;

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -