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 + -
显示快捷键?