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

📄 cpufreq_64.c

📁 linux 内核源代码
💻 C
📖 第 1 页 / 共 2 页
字号:
	.name		= "powermac",	.owner		= THIS_MODULE,	.flags		= CPUFREQ_CONST_LOOPS,	.init		= g5_cpufreq_cpu_init,	.verify		= g5_cpufreq_verify,	.target		= g5_cpufreq_target,	.get		= g5_cpufreq_get_speed,	.attr 		= g5_cpu_freqs_attr,};#ifdef CONFIG_PMAC_SMUstatic int __init g5_neo2_cpufreq_init(struct device_node *cpus){	struct device_node *cpunode;	unsigned int psize, ssize;	unsigned long max_freq;	char *freq_method, *volt_method;	const u32 *valp;	u32 pvr_hi;	int use_volts_vdnap = 0;	int use_volts_smu = 0;	int rc = -ENODEV;	/* Check supported platforms */	if (machine_is_compatible("PowerMac8,1") ||	    machine_is_compatible("PowerMac8,2") ||	    machine_is_compatible("PowerMac9,1"))		use_volts_smu = 1;	else if (machine_is_compatible("PowerMac11,2"))		use_volts_vdnap = 1;	else		return -ENODEV;	/* Get first CPU node */	for (cpunode = NULL;	     (cpunode = of_get_next_child(cpus, cpunode)) != NULL;) {		const u32 *reg = of_get_property(cpunode, "reg", NULL);		if (reg == NULL || (*reg) != 0)			continue;		if (!strcmp(cpunode->type, "cpu"))			break;	}	if (cpunode == NULL) {		printk(KERN_ERR "cpufreq: Can't find any CPU 0 node\n");		return -ENODEV;	}	/* Check 970FX for now */	valp = of_get_property(cpunode, "cpu-version", NULL);	if (!valp) {		DBG("No cpu-version property !\n");		goto bail_noprops;	}	pvr_hi = (*valp) >> 16;	if (pvr_hi != 0x3c && pvr_hi != 0x44) {		printk(KERN_ERR "cpufreq: Unsupported CPU version\n");		goto bail_noprops;	}	/* Look for the powertune data in the device-tree */	g5_pmode_data = of_get_property(cpunode, "power-mode-data",&psize);	if (!g5_pmode_data) {		DBG("No power-mode-data !\n");		goto bail_noprops;	}	g5_pmode_max = psize / sizeof(u32) - 1;	if (use_volts_smu) {		const struct smu_sdbp_header *shdr;		/* Look for the FVT table */		shdr = smu_get_sdb_partition(SMU_SDB_FVT_ID, NULL);		if (!shdr)			goto bail_noprops;		g5_fvt_table = (struct smu_sdbp_fvt *)&shdr[1];		ssize = (shdr->len * sizeof(u32)) -			sizeof(struct smu_sdbp_header);		g5_fvt_count = ssize / sizeof(struct smu_sdbp_fvt);		g5_fvt_cur = 0;		/* Sanity checking */		if (g5_fvt_count < 1 || g5_pmode_max < 1)			goto bail_noprops;		g5_switch_volt = g5_smu_switch_volt;		volt_method = "SMU";	} else if (use_volts_vdnap) {		struct device_node *root;		root = of_find_node_by_path("/");		if (root == NULL) {			printk(KERN_ERR "cpufreq: Can't find root of "			       "device tree\n");			goto bail_noprops;		}		pfunc_set_vdnap0 = pmf_find_function(root, "set-vdnap0");		pfunc_vdnap0_complete =			pmf_find_function(root, "slewing-done");		if (pfunc_set_vdnap0 == NULL ||		    pfunc_vdnap0_complete == NULL) {			printk(KERN_ERR "cpufreq: Can't find required "			       "platform function\n");			goto bail_noprops;		}		g5_switch_volt = g5_vdnap_switch_volt;		volt_method = "GPIO";	} else {		g5_switch_volt = g5_dummy_switch_volt;		volt_method = "none";	}	/*	 * From what I see, clock-frequency is always the maximal frequency.	 * The current driver can not slew sysclk yet, so we really only deal	 * with powertune steps for now. We also only implement full freq and	 * half freq in this version. So far, I haven't yet seen a machine	 * supporting anything else.	 */	valp = of_get_property(cpunode, "clock-frequency", NULL);	if (!valp)		return -ENODEV;	max_freq = (*valp)/1000;	g5_cpu_freqs[0].frequency = max_freq;	g5_cpu_freqs[1].frequency = max_freq/2;	/* Set callbacks */	g5_switch_freq = g5_scom_switch_freq;	g5_query_freq = g5_scom_query_freq;	freq_method = "SCOM";	/* Force apply current frequency to make sure everything is in	 * sync (voltage is right for example). Firmware may leave us with	 * a strange setting ...	 */	g5_switch_volt(CPUFREQ_HIGH);	msleep(10);	g5_pmode_cur = -1;	g5_switch_freq(g5_query_freq());	printk(KERN_INFO "Registering G5 CPU frequency driver\n");	printk(KERN_INFO "Frequency method: %s, Voltage method: %s\n",	       freq_method, volt_method);	printk(KERN_INFO "Low: %d Mhz, High: %d Mhz, Cur: %d MHz\n",		g5_cpu_freqs[1].frequency/1000,		g5_cpu_freqs[0].frequency/1000,		g5_cpu_freqs[g5_pmode_cur].frequency/1000);	rc = cpufreq_register_driver(&g5_cpufreq_driver);	/* We keep the CPU node on hold... hopefully, Apple G5 don't have	 * hotplug CPU with a dynamic device-tree ...	 */	return rc; bail_noprops:	of_node_put(cpunode);	return rc;}#endif /* CONFIG_PMAC_SMU */static int __init g5_pm72_cpufreq_init(struct device_node *cpus){	struct device_node *cpuid = NULL, *hwclock = NULL, *cpunode = NULL;	const u8 *eeprom = NULL;	const u32 *valp;	u64 max_freq, min_freq, ih, il;	int has_volt = 1, rc = 0;	DBG("cpufreq: Initializing for PowerMac7,2, PowerMac7,3 and"	    " RackMac3,1...\n");	/* Get first CPU node */	for (cpunode = NULL;	     (cpunode = of_get_next_child(cpus, cpunode)) != NULL;) {		if (!strcmp(cpunode->type, "cpu"))			break;	}	if (cpunode == NULL) {		printk(KERN_ERR "cpufreq: Can't find any CPU node\n");		return -ENODEV;	}	/* Lookup the cpuid eeprom node */        cpuid = of_find_node_by_path("/u3@0,f8000000/i2c@f8001000/cpuid@a0");	if (cpuid != NULL)		eeprom = of_get_property(cpuid, "cpuid", NULL);	if (eeprom == NULL) {		printk(KERN_ERR "cpufreq: Can't find cpuid EEPROM !\n");		rc = -ENODEV;		goto bail;	}	/* Lookup the i2c hwclock */	for (hwclock = NULL;	     (hwclock = of_find_node_by_name(hwclock, "i2c-hwclock")) != NULL;){		const char *loc = of_get_property(hwclock,				"hwctrl-location", NULL);		if (loc == NULL)			continue;		if (strcmp(loc, "CPU CLOCK"))			continue;		if (!of_get_property(hwclock, "platform-get-frequency", NULL))			continue;		break;	}	if (hwclock == NULL) {		printk(KERN_ERR "cpufreq: Can't find i2c clock chip !\n");		rc = -ENODEV;		goto bail;	}	DBG("cpufreq: i2c clock chip found: %s\n", hwclock->full_name);	/* Now get all the platform functions */	pfunc_cpu_getfreq =		pmf_find_function(hwclock, "get-frequency");	pfunc_cpu_setfreq_high =		pmf_find_function(hwclock, "set-frequency-high");	pfunc_cpu_setfreq_low =		pmf_find_function(hwclock, "set-frequency-low");	pfunc_slewing_done =		pmf_find_function(hwclock, "slewing-done");	pfunc_cpu0_volt_high =		pmf_find_function(hwclock, "set-voltage-high-0");	pfunc_cpu0_volt_low =		pmf_find_function(hwclock, "set-voltage-low-0");	pfunc_cpu1_volt_high =		pmf_find_function(hwclock, "set-voltage-high-1");	pfunc_cpu1_volt_low =		pmf_find_function(hwclock, "set-voltage-low-1");	/* Check we have minimum requirements */	if (pfunc_cpu_getfreq == NULL || pfunc_cpu_setfreq_high == NULL ||	    pfunc_cpu_setfreq_low == NULL || pfunc_slewing_done == NULL) {		printk(KERN_ERR "cpufreq: Can't find platform functions !\n");		rc = -ENODEV;		goto bail;	}	/* Check that we have complete sets */	if (pfunc_cpu0_volt_high == NULL || pfunc_cpu0_volt_low == NULL) {		pmf_put_function(pfunc_cpu0_volt_high);		pmf_put_function(pfunc_cpu0_volt_low);		pfunc_cpu0_volt_high = pfunc_cpu0_volt_low = NULL;		has_volt = 0;	}	if (!has_volt ||	    pfunc_cpu1_volt_high == NULL || pfunc_cpu1_volt_low == NULL) {		pmf_put_function(pfunc_cpu1_volt_high);		pmf_put_function(pfunc_cpu1_volt_low);		pfunc_cpu1_volt_high = pfunc_cpu1_volt_low = NULL;	}	/* Note: The device tree also contains a "platform-set-values"	 * function for which I haven't quite figured out the usage. It	 * might have to be called on init and/or wakeup, I'm not too sure	 * but things seem to work fine without it so far ...	 */	/* Get max frequency from device-tree */	valp = of_get_property(cpunode, "clock-frequency", NULL);	if (!valp) {		printk(KERN_ERR "cpufreq: Can't find CPU frequency !\n");		rc = -ENODEV;		goto bail;	}	max_freq = (*valp)/1000;	/* Now calculate reduced frequency by using the cpuid input freq	 * ratio. This requires 64 bits math unless we are willing to lose	 * some precision	 */	ih = *((u32 *)(eeprom + 0x10));	il = *((u32 *)(eeprom + 0x20));	/* Check for machines with no useful settings */	if (il == ih) {		printk(KERN_WARNING "cpufreq: No low frequency mode available"		       " on this model !\n");		rc = -ENODEV;		goto bail;	}	min_freq = 0;	if (ih != 0 && il != 0)		min_freq = (max_freq * il) / ih;	/* Sanity check */	if (min_freq >= max_freq || min_freq < 1000) {		printk(KERN_ERR "cpufreq: Can't calculate low frequency !\n");		rc = -ENXIO;		goto bail;	}	g5_cpu_freqs[0].frequency = max_freq;	g5_cpu_freqs[1].frequency = min_freq;	/* Set callbacks */	g5_switch_volt = g5_pfunc_switch_volt;	g5_switch_freq = g5_pfunc_switch_freq;	g5_query_freq = g5_pfunc_query_freq;	/* Force apply current frequency to make sure everything is in	 * sync (voltage is right for example). Firmware may leave us with	 * a strange setting ...	 */	g5_switch_volt(CPUFREQ_HIGH);	msleep(10);	g5_pmode_cur = -1;	g5_switch_freq(g5_query_freq());	printk(KERN_INFO "Registering G5 CPU frequency driver\n");	printk(KERN_INFO "Frequency method: i2c/pfunc, "	       "Voltage method: %s\n", has_volt ? "i2c/pfunc" : "none");	printk(KERN_INFO "Low: %d Mhz, High: %d Mhz, Cur: %d MHz\n",		g5_cpu_freqs[1].frequency/1000,		g5_cpu_freqs[0].frequency/1000,		g5_cpu_freqs[g5_pmode_cur].frequency/1000);	rc = cpufreq_register_driver(&g5_cpufreq_driver); bail:	if (rc != 0) {		pmf_put_function(pfunc_cpu_getfreq);		pmf_put_function(pfunc_cpu_setfreq_high);		pmf_put_function(pfunc_cpu_setfreq_low);		pmf_put_function(pfunc_slewing_done);		pmf_put_function(pfunc_cpu0_volt_high);		pmf_put_function(pfunc_cpu0_volt_low);		pmf_put_function(pfunc_cpu1_volt_high);		pmf_put_function(pfunc_cpu1_volt_low);	}	of_node_put(hwclock);	of_node_put(cpuid);	of_node_put(cpunode);	return rc;}static int __init g5_cpufreq_init(void){	struct device_node *cpus;	int rc = 0;	cpus = of_find_node_by_path("/cpus");	if (cpus == NULL) {		DBG("No /cpus node !\n");		return -ENODEV;	}	if (machine_is_compatible("PowerMac7,2") ||	    machine_is_compatible("PowerMac7,3") ||	    machine_is_compatible("RackMac3,1"))		rc = g5_pm72_cpufreq_init(cpus);#ifdef CONFIG_PMAC_SMU	else		rc = g5_neo2_cpufreq_init(cpus);#endif /* CONFIG_PMAC_SMU */	of_node_put(cpus);	return rc;}module_init(g5_cpufreq_init);MODULE_LICENSE("GPL");

⌨️ 快捷键说明

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