214 lines
		
	
	
		
			5.5 KiB
		
	
	
	
		
			C
		
	
	
		
			Executable File
		
	
	
	
	
			
		
		
	
	
			214 lines
		
	
	
		
			5.5 KiB
		
	
	
	
		
			C
		
	
	
		
			Executable File
		
	
	
	
	
| #include <linux/of.h>
 | |
| #include <linux/cpufreq.h>
 | |
| #include <linux/cpu.h>
 | |
| #include <linux/clk.h>
 | |
| #include <linux/clk-provider.h>
 | |
| #include <linux/platform_device.h>
 | |
| #include <plat/top.h>
 | |
| #define DRV_VERSION "1.0.1"
 | |
| 
 | |
| static const struct of_device_id machines[] __initconst = {
 | |
| 	{ .compatible = "nvt,ca9" },
 | |
| 	{ /* sentinel */ }
 | |
| };
 | |
| 
 | |
| static struct cpufreq_frequency_table freq_table[] = {
 | |
| 	{ .frequency = 240000 },
 | |
| 	{ .frequency = 480000 },
 | |
| 	{ .frequency = 800000 },
 | |
| 	{ .frequency = 960000 },
 | |
| 	{ .frequency = CPUFREQ_TABLE_END },
 | |
| };
 | |
| 
 | |
| static struct cpufreq_frequency_table freq_table_528[] = {
 | |
| 	{ .frequency = 480000 },
 | |
| 	{ .frequency = 800000 },
 | |
| 	{ .frequency = 1000000 },
 | |
| 	{ .frequency = CPUFREQ_TABLE_END },
 | |
| };
 | |
| 
 | |
| struct nvt_cpufreq {
 | |
| 	struct device *dev;
 | |
| 	struct cpufreq_driver driver;
 | |
| 	struct clk *cpu_clk;        //PLL8
 | |
| 	struct clk *cpu_clk_parent; //PLL8
 | |
| };
 | |
| 
 | |
| static int nvt_target(struct cpufreq_policy *policy, unsigned int index)
 | |
| {
 | |
| 	struct nvt_cpufreq *cpufreq = cpufreq_get_driver_data();
 | |
| 	unsigned long rate;
 | |
| 	if (nvt_get_chip_id() == CHIP_NA51084) {
 | |
| 		rate = freq_table_528[index].frequency;
 | |
| 	} else {
 | |
| 		rate = freq_table[index].frequency;
 | |
| 	}
 | |
| 	if (nvt_get_chip_id() == CHIP_NA51084) {
 | |
| 		if (rate == 480000) {
 | |
| 			if (clk_set_parent(cpufreq->cpu_clk_parent, clk_get(NULL, "fix480m"))) {
 | |
| 				printk("set fixed 480MHz error\r\n");
 | |
| 			} else {
 | |
| 				if (__clk_is_enabled(cpufreq->cpu_clk)) {
 | |
| 					clk_disable(cpufreq->cpu_clk);
 | |
| 				}
 | |
| 			}
 | |
| 			return 0;
 | |
| 		} else {
 | |
| 			if (!__clk_is_enabled(cpufreq->cpu_clk)) {
 | |
| 				clk_enable(cpufreq->cpu_clk);
 | |
| 			}
 | |
| 			if (clk_set_parent(cpufreq->cpu_clk_parent, clk_get(NULL, "pll8"))) {
 | |
| 				printk("set pll8 error\r\n");
 | |
| 			} else {
 | |
| 				printk("set pll8 success\r\n");
 | |
| 			}
 | |
| 			return clk_set_rate(cpufreq->cpu_clk, rate * 1000 / 8);
 | |
| 		}
 | |
| 	} else {
 | |
| 		if (rate == 480000) {
 | |
| 			if (clk_set_parent(cpufreq->cpu_clk_parent, clk_get(NULL, "fix480m"))) {
 | |
| 				printk("set fixed 480MHz error\r\n");
 | |
| 			} else {
 | |
| 				if (__clk_is_enabled(cpufreq->cpu_clk)) {
 | |
| 					clk_disable(cpufreq->cpu_clk);
 | |
| 				}
 | |
| 			}
 | |
| 			return 0;
 | |
| 		} else {
 | |
| 			if (!__clk_is_enabled(cpufreq->cpu_clk)) {
 | |
| 				clk_enable(cpufreq->cpu_clk);
 | |
| 			}
 | |
| 			if (clk_set_parent(cpufreq->cpu_clk_parent, clk_get(NULL, "pll8"))) {
 | |
| 				printk("set pll8 error\r\n");
 | |
| 			} //else {
 | |
| 			//  printk("set pll8 success\r\n");
 | |
| 			//}
 | |
| 			return clk_set_rate(cpufreq->cpu_clk, rate * 1000);
 | |
| 		}
 | |
| 	}
 | |
| }
 | |
| 
 | |
| static unsigned int nvt_cpufreq_get(unsigned int cpu)
 | |
| {
 | |
| 	struct cpufreq_policy *policy = cpufreq_cpu_get_raw(cpu);
 | |
| 	struct nvt_cpufreq *cpufreq = cpufreq_get_driver_data();
 | |
| 	if (!policy || IS_ERR(policy->clk)) {
 | |
| 		pr_err("%s: No %s associated to cpu: %d\n",
 | |
| 			   __func__, policy ? "clk" : "policy", cpu);
 | |
| 		return 0;
 | |
| 	}
 | |
| 
 | |
| 	if (nvt_get_chip_id() == CHIP_NA51084) {
 | |
| 		return (clk_get_rate(policy->clk) * 8) / 1000;
 | |
| 	} else {
 | |
| 		return clk_get_rate(clk_get_parent(cpufreq->cpu_clk_parent)) / 1000;
 | |
| 	}
 | |
| }
 | |
| 
 | |
| static int nvt_cpu_init(struct cpufreq_policy *policy)
 | |
| {
 | |
| 	struct nvt_cpufreq *cpufreq = cpufreq_get_driver_data();
 | |
| 	int ret;
 | |
| 	if (!__clk_is_enabled(cpufreq->cpu_clk)) {
 | |
| 		clk_prepare_enable(cpufreq->cpu_clk);
 | |
| 		clk_disable(cpufreq->cpu_clk);
 | |
| 	}
 | |
| 	if (nvt_get_chip_id() == CHIP_NA51084) {
 | |
| 		ret = cpufreq_generic_init(policy, freq_table_528, 300 * 1000);
 | |
| 	} else {
 | |
| 		ret = cpufreq_generic_init(policy, freq_table, 300 * 1000);
 | |
| 	}
 | |
| 	if (ret) {
 | |
| 		clk_disable_unprepare(cpufreq->cpu_clk);
 | |
| 		return ret;
 | |
| 	}
 | |
| 
 | |
| 	policy->clk = cpufreq->cpu_clk;
 | |
| 	if (nvt_get_chip_id() == CHIP_NA51084) {
 | |
| 		policy->suspend_freq = freq_table_528[0].frequency;
 | |
| 	} else {
 | |
| 		policy->suspend_freq = freq_table[0].frequency;
 | |
| 	}
 | |
| 	return 0;
 | |
| }
 | |
| 
 | |
| static int nvt_cpu_exit(struct cpufreq_policy *policy)
 | |
| {
 | |
| 	struct nvt_cpufreq *cpufreq = cpufreq_get_driver_data();
 | |
| 
 | |
| 	clk_disable_unprepare(cpufreq->cpu_clk);
 | |
| 	return 0;
 | |
| }
 | |
| 
 | |
| static int __init nvt_cpufreq_init(void)
 | |
| {
 | |
| 	struct nvt_cpufreq *cpufreq;
 | |
| 	struct device *cpu_dev = get_cpu_device(0);
 | |
| 	unsigned long max_freq;
 | |
| 	struct clk *cpu_clk;
 | |
| 	u32 cpu_freq_table[4] = {0};
 | |
| 	int err, i;
 | |
| 
 | |
| 	if (!of_match_node(machines, of_root)) {
 | |
| 		return -ENODEV;
 | |
| 	}
 | |
| 
 | |
| 	of_property_read_u32_array(cpu_dev->of_node, "clock-frequency", cpu_freq_table, 4);
 | |
| 
 | |
| 	if (cpu_freq_table[0]) {
 | |
| 		for (i = 0; i < 4; i++) {
 | |
| 			if (nvt_get_chip_id() == CHIP_NA51084) {
 | |
| 				freq_table_528[i].frequency = cpu_freq_table[i] / 1000;
 | |
| 			} else {
 | |
| 				freq_table[i].frequency = cpu_freq_table[i] / 1000;
 | |
| 			}
 | |
| 			printk("[%d] freq[%d]\r\n", i, freq_table[i].frequency);
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	cpufreq = devm_kzalloc(cpu_dev, sizeof(*cpufreq), GFP_KERNEL);
 | |
| 	if (!cpufreq) {
 | |
| 		return -ENOMEM;
 | |
| 	}
 | |
| 
 | |
| 	cpufreq->cpu_clk = clk_get(NULL, "pll8");
 | |
| 	if (IS_ERR(cpufreq->cpu_clk)) {
 | |
| 		return -ENODEV;
 | |
| 	}
 | |
| 
 | |
| 	if (nvt_get_chip_id() == CHIP_NA51089 || nvt_get_chip_id() == CHIP_NA51055 || nvt_get_chip_id() == CHIP_NA51084) {
 | |
| 		cpufreq->cpu_clk_parent = clk_get(NULL, "cpu_clk");
 | |
| 		if (IS_ERR(cpufreq->cpu_clk_parent)) {
 | |
| 			printk("cpu clk parent error\r\n");
 | |
| 			return -ENODEV;
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	max_freq = clk_get_rate(cpu_clk);
 | |
| 
 | |
| 	cpufreq->dev = cpu_dev;
 | |
| 	cpufreq->driver.get = nvt_cpufreq_get;
 | |
| 	cpufreq->driver.attr = cpufreq_generic_attr;
 | |
| 	cpufreq->driver.init = nvt_cpu_init;
 | |
| 	cpufreq->driver.exit = nvt_cpu_exit;
 | |
| 	cpufreq->driver.flags = CPUFREQ_NEED_INITIAL_FREQ_CHECK;
 | |
| 	cpufreq->driver.verify = cpufreq_generic_frequency_table_verify;
 | |
| 	cpufreq->driver.suspend = cpufreq_generic_suspend;
 | |
| 	cpufreq->driver.driver_data = cpufreq;
 | |
| 	cpufreq->driver.target_index = nvt_target;
 | |
| 	snprintf(cpufreq->driver.name, CPUFREQ_NAME_LEN, "novatek");
 | |
| 
 | |
| 	err = cpufreq_register_driver(&cpufreq->driver);
 | |
| 	if (err) {
 | |
| 		goto put_pll;
 | |
| 	}
 | |
| 
 | |
| 	return 0;
 | |
| 
 | |
| put_pll:
 | |
| 	clk_put(cpufreq->cpu_clk);
 | |
| 
 | |
| 	return err;
 | |
| }
 | |
| device_initcall(nvt_cpufreq_init);
 | 
