rack-meter.c

来自「linux 内核源代码」· C语言 代码 · 共 616 行 · 第 1/2 页

C
616
字号
/* * RackMac vu-meter driver * * (c) Copyright 2006 Benjamin Herrenschmidt, IBM Corp. *                    <benh@kernel.crashing.org> * * Released under the term of the GNU GPL v2. * * Support the CPU-meter LEDs of the Xserve G5 * * TODO: Implement PWM to do variable intensity and provide userland * interface for fun. Also, the CPU-meter could be made nicer by being * a bit less "immediate" but giving instead a more average load over * time. Patches welcome :-) * */#undef DEBUG#include <linux/types.h>#include <linux/kernel.h>#include <linux/device.h>#include <linux/interrupt.h>#include <linux/module.h>#include <linux/pci.h>#include <linux/dma-mapping.h>#include <linux/kernel_stat.h>#include <asm/io.h>#include <asm/prom.h>#include <asm/machdep.h>#include <asm/pmac_feature.h>#include <asm/dbdma.h>#include <asm/macio.h>#include <asm/keylargo.h>/* Number of samples in a sample buffer */#define SAMPLE_COUNT		256/* CPU meter sampling rate in ms */#define CPU_SAMPLING_RATE	250struct rackmeter_dma {	struct dbdma_cmd	cmd[4]			____cacheline_aligned;	u32			mark			____cacheline_aligned;	u32			buf1[SAMPLE_COUNT]	____cacheline_aligned;	u32			buf2[SAMPLE_COUNT]	____cacheline_aligned;} ____cacheline_aligned;struct rackmeter_cpu {	struct delayed_work	sniffer;	struct rackmeter	*rm;	cputime64_t		prev_wall;	cputime64_t		prev_idle;	int			zero;} ____cacheline_aligned;struct rackmeter {	struct macio_dev		*mdev;	unsigned int			irq;	struct device_node		*i2s;	u8				*ubuf;	struct dbdma_regs __iomem	*dma_regs;	void __iomem			*i2s_regs;	dma_addr_t			dma_buf_p;	struct rackmeter_dma		*dma_buf_v;	int				stale_irq;	struct rackmeter_cpu		cpu[2];	int				paused;	struct mutex			sem;};/* To be set as a tunable */static int rackmeter_ignore_nice;/* This GPIO is whacked by the OS X driver when initializing */#define RACKMETER_MAGIC_GPIO	0x78/* This is copied from cpufreq_ondemand, maybe we should put it in * a common header somewhere */static inline cputime64_t get_cpu_idle_time(unsigned int cpu){	cputime64_t retval;	retval = cputime64_add(kstat_cpu(cpu).cpustat.idle,			kstat_cpu(cpu).cpustat.iowait);	if (rackmeter_ignore_nice)		retval = cputime64_add(retval, kstat_cpu(cpu).cpustat.nice);	return retval;}static void rackmeter_setup_i2s(struct rackmeter *rm){	struct macio_chip *macio = rm->mdev->bus->chip;	/* First whack magic GPIO */	pmac_call_feature(PMAC_FTR_WRITE_GPIO, NULL, RACKMETER_MAGIC_GPIO, 5);	/* Call feature code to enable the sound channel and the proper	 * clock sources	 */	pmac_call_feature(PMAC_FTR_SOUND_CHIP_ENABLE, rm->i2s, 0, 1);	/* Power i2s and stop i2s clock. We whack MacIO FCRs directly for now.	 * This is a bit racy, thus we should add new platform functions to	 * handle that. snd-aoa needs that too	 */	MACIO_BIS(KEYLARGO_FCR1, KL1_I2S0_ENABLE);	MACIO_BIC(KEYLARGO_FCR1, KL1_I2S0_CLK_ENABLE_BIT);	(void)MACIO_IN32(KEYLARGO_FCR1);	udelay(10);	/* Then setup i2s. For now, we use the same magic value that	 * the OS X driver seems to use. We might want to play around	 * with the clock divisors later	 */	out_le32(rm->i2s_regs + 0x10, 0x01fa0000);	(void)in_le32(rm->i2s_regs + 0x10);	udelay(10);	/* Fully restart i2s*/	MACIO_BIS(KEYLARGO_FCR1, KL1_I2S0_CELL_ENABLE |		  KL1_I2S0_CLK_ENABLE_BIT);	(void)MACIO_IN32(KEYLARGO_FCR1);	udelay(10);}static void rackmeter_set_default_pattern(struct rackmeter *rm){	int i;	for (i = 0; i < 16; i++) {		if (i < 8)			rm->ubuf[i] = (i & 1) * 255;		else			rm->ubuf[i] = ((~i) & 1) * 255;	}}static void rackmeter_do_pause(struct rackmeter *rm, int pause){	struct rackmeter_dma *rdma = rm->dma_buf_v;	pr_debug("rackmeter: %s\n", pause ? "paused" : "started");	rm->paused = pause;	if (pause) {		DBDMA_DO_STOP(rm->dma_regs);		return;	}	memset(rdma->buf1, 0, SAMPLE_COUNT & sizeof(u32));	memset(rdma->buf2, 0, SAMPLE_COUNT & sizeof(u32));	rm->dma_buf_v->mark = 0;	mb();	out_le32(&rm->dma_regs->cmdptr_hi, 0);	out_le32(&rm->dma_regs->cmdptr, rm->dma_buf_p);	out_le32(&rm->dma_regs->control, (RUN << 16) | RUN);}static void rackmeter_setup_dbdma(struct rackmeter *rm){	struct rackmeter_dma *db = rm->dma_buf_v;	struct dbdma_cmd *cmd = db->cmd;	/* Make sure dbdma is reset */	DBDMA_DO_RESET(rm->dma_regs);	pr_debug("rackmeter: mark offset=0x%zx\n",		 offsetof(struct rackmeter_dma, mark));	pr_debug("rackmeter: buf1 offset=0x%zx\n",		 offsetof(struct rackmeter_dma, buf1));	pr_debug("rackmeter: buf2 offset=0x%zx\n",		 offsetof(struct rackmeter_dma, buf2));	/* Prepare 4 dbdma commands for the 2 buffers */	memset(cmd, 0, 4 * sizeof(struct dbdma_cmd));	st_le16(&cmd->req_count, 4);	st_le16(&cmd->command, STORE_WORD | INTR_ALWAYS | KEY_SYSTEM);	st_le32(&cmd->phy_addr, rm->dma_buf_p +		offsetof(struct rackmeter_dma, mark));	st_le32(&cmd->cmd_dep, 0x02000000);	cmd++;	st_le16(&cmd->req_count, SAMPLE_COUNT * 4);	st_le16(&cmd->command, OUTPUT_MORE);	st_le32(&cmd->phy_addr, rm->dma_buf_p +		offsetof(struct rackmeter_dma, buf1));	cmd++;	st_le16(&cmd->req_count, 4);	st_le16(&cmd->command, STORE_WORD | INTR_ALWAYS | KEY_SYSTEM);	st_le32(&cmd->phy_addr, rm->dma_buf_p +		offsetof(struct rackmeter_dma, mark));	st_le32(&cmd->cmd_dep, 0x01000000);	cmd++;	st_le16(&cmd->req_count, SAMPLE_COUNT * 4);	st_le16(&cmd->command, OUTPUT_MORE | BR_ALWAYS);	st_le32(&cmd->phy_addr, rm->dma_buf_p +		offsetof(struct rackmeter_dma, buf2));	st_le32(&cmd->cmd_dep, rm->dma_buf_p);	rackmeter_do_pause(rm, 0);}static void rackmeter_do_timer(struct work_struct *work){	struct rackmeter_cpu *rcpu =		container_of(work, struct rackmeter_cpu, sniffer.work);	struct rackmeter *rm = rcpu->rm;	unsigned int cpu = smp_processor_id();	cputime64_t cur_jiffies, total_idle_ticks;	unsigned int total_ticks, idle_ticks;	int i, offset, load, cumm, pause;	cur_jiffies = jiffies64_to_cputime64(get_jiffies_64());	total_ticks = (unsigned int)cputime64_sub(cur_jiffies,						  rcpu->prev_wall);	rcpu->prev_wall = cur_jiffies;	total_idle_ticks = get_cpu_idle_time(cpu);	idle_ticks = (unsigned int) cputime64_sub(total_idle_ticks,				rcpu->prev_idle);	rcpu->prev_idle = total_idle_ticks;	/* We do a very dumb calculation to update the LEDs for now,	 * we'll do better once we have actual PWM implemented	 */	load = (9 * (total_ticks - idle_ticks)) / total_ticks;	offset = cpu << 3;	cumm = 0;	for (i = 0; i < 8; i++) {		u8 ub = (load > i) ? 0xff : 0;		rm->ubuf[i + offset] = ub;		cumm |= ub;	}	rcpu->zero = (cumm == 0);	/* Now check if LEDs are all 0, we can stop DMA */	pause = (rm->cpu[0].zero && rm->cpu[1].zero);	if (pause != rm->paused) {		mutex_lock(&rm->sem);		pause = (rm->cpu[0].zero && rm->cpu[1].zero);		rackmeter_do_pause(rm, pause);		mutex_unlock(&rm->sem);	}	schedule_delayed_work_on(cpu, &rcpu->sniffer,				 msecs_to_jiffies(CPU_SAMPLING_RATE));}static void __devinit rackmeter_init_cpu_sniffer(struct rackmeter *rm){	unsigned int cpu;	/* This driver works only with 1 or 2 CPUs numbered 0 and 1,	 * but that's really all we have on Apple Xserve. It doesn't	 * play very nice with CPU hotplug neither but we don't do that	 * on those machines yet	 */	rm->cpu[0].rm = rm;	INIT_DELAYED_WORK(&rm->cpu[0].sniffer, rackmeter_do_timer);	rm->cpu[1].rm = rm;	INIT_DELAYED_WORK(&rm->cpu[1].sniffer, rackmeter_do_timer);	for_each_online_cpu(cpu) {		struct rackmeter_cpu *rcpu;		if (cpu > 1)			continue;		rcpu = &rm->cpu[cpu];;		rcpu->prev_idle = get_cpu_idle_time(cpu);		rcpu->prev_wall = jiffies64_to_cputime64(get_jiffies_64());		schedule_delayed_work_on(cpu, &rm->cpu[cpu].sniffer,					 msecs_to_jiffies(CPU_SAMPLING_RATE));	}}static void __devexit rackmeter_stop_cpu_sniffer(struct rackmeter *rm){	cancel_rearming_delayed_work(&rm->cpu[0].sniffer);	cancel_rearming_delayed_work(&rm->cpu[1].sniffer);}static int rackmeter_setup(struct rackmeter *rm){	pr_debug("rackmeter: setting up i2s..\n");	rackmeter_setup_i2s(rm);	pr_debug("rackmeter: setting up default pattern..\n");	rackmeter_set_default_pattern(rm);	pr_debug("rackmeter: setting up dbdma..\n");	rackmeter_setup_dbdma(rm);	pr_debug("rackmeter: start CPU measurements..\n");	rackmeter_init_cpu_sniffer(rm);	printk(KERN_INFO "RackMeter initialized\n");	return 0;}

⌨️ 快捷键说明

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