static int __init g5_neo2_cpufreq_init()

in pmac64-cpufreq.c [341:474]


static int __init g5_neo2_cpufreq_init(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 (of_machine_is_compatible("PowerMac8,1") ||
	    of_machine_is_compatible("PowerMac8,2") ||
	    of_machine_is_compatible("PowerMac9,1") ||
	    of_machine_is_compatible("PowerMac12,1"))
		use_volts_smu = 1;
	else if (of_machine_is_compatible("PowerMac11,2"))
		use_volts_vdnap = 1;
	else
		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) {
		pr_err("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(*shdr);
		g5_fvt_count = ssize / sizeof(*g5_fvt_table);
		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) {
			pr_err("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");
		of_node_put(root);
		if (pfunc_set_vdnap0 == NULL ||
		    pfunc_vdnap0_complete == NULL) {
			pr_err("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 */
	transition_latency = 12000;
	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());

	pr_info("Registering G5 CPU frequency driver\n");
	pr_info("Frequency method: %s, Voltage method: %s\n",
		freq_method, volt_method);
	pr_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;
}