162a8a094STuomas Tynkkynen /* 262a8a094STuomas Tynkkynen * Tegra124 DFLL FCPU clock source driver 362a8a094STuomas Tynkkynen * 462a8a094STuomas Tynkkynen * Copyright (C) 2012-2014 NVIDIA Corporation. All rights reserved. 562a8a094STuomas Tynkkynen * 662a8a094STuomas Tynkkynen * Aleksandr Frid <afrid@nvidia.com> 762a8a094STuomas Tynkkynen * Paul Walmsley <pwalmsley@nvidia.com> 862a8a094STuomas Tynkkynen * 962a8a094STuomas Tynkkynen * This program is free software; you can redistribute it and/or modify 1062a8a094STuomas Tynkkynen * it under the terms of the GNU General Public License version 2 as 1162a8a094STuomas Tynkkynen * published by the Free Software Foundation. 1262a8a094STuomas Tynkkynen * 1362a8a094STuomas Tynkkynen * This program is distributed in the hope that it will be useful, but WITHOUT 1462a8a094STuomas Tynkkynen * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 1562a8a094STuomas Tynkkynen * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for 1662a8a094STuomas Tynkkynen * more details. 1762a8a094STuomas Tynkkynen * 1862a8a094STuomas Tynkkynen */ 1962a8a094STuomas Tynkkynen 2062a8a094STuomas Tynkkynen #include <linux/cpu.h> 2162a8a094STuomas Tynkkynen #include <linux/err.h> 2262a8a094STuomas Tynkkynen #include <linux/kernel.h> 2362a8a094STuomas Tynkkynen #include <linux/module.h> 2462a8a094STuomas Tynkkynen #include <linux/platform_device.h> 2562a8a094STuomas Tynkkynen #include <soc/tegra/fuse.h> 2662a8a094STuomas Tynkkynen 2762a8a094STuomas Tynkkynen #include "clk.h" 2862a8a094STuomas Tynkkynen #include "clk-dfll.h" 2962a8a094STuomas Tynkkynen #include "cvb.h" 3062a8a094STuomas Tynkkynen 3162a8a094STuomas Tynkkynen /* Maximum CPU frequency, indexed by CPU speedo id */ 3262a8a094STuomas Tynkkynen static const unsigned long cpu_max_freq_table[] = { 3362a8a094STuomas Tynkkynen [0] = 2014500000UL, 3462a8a094STuomas Tynkkynen [1] = 2320500000UL, 3562a8a094STuomas Tynkkynen [2] = 2116500000UL, 3662a8a094STuomas Tynkkynen [3] = 2524500000UL, 3762a8a094STuomas Tynkkynen }; 3862a8a094STuomas Tynkkynen 3962a8a094STuomas Tynkkynen static const struct cvb_table tegra124_cpu_cvb_tables[] = { 4062a8a094STuomas Tynkkynen { 4162a8a094STuomas Tynkkynen .speedo_id = -1, 4262a8a094STuomas Tynkkynen .process_id = -1, 4362a8a094STuomas Tynkkynen .min_millivolts = 900, 4462a8a094STuomas Tynkkynen .max_millivolts = 1260, 4562a8a094STuomas Tynkkynen .alignment = { 4662a8a094STuomas Tynkkynen .step_uv = 10000, /* 10mV */ 4762a8a094STuomas Tynkkynen }, 4862a8a094STuomas Tynkkynen .speedo_scale = 100, 4962a8a094STuomas Tynkkynen .voltage_scale = 1000, 5062a8a094STuomas Tynkkynen .cvb_table = { 5162a8a094STuomas Tynkkynen {204000000UL, {1112619, -29295, 402} }, 5262a8a094STuomas Tynkkynen {306000000UL, {1150460, -30585, 402} }, 5362a8a094STuomas Tynkkynen {408000000UL, {1190122, -31865, 402} }, 5462a8a094STuomas Tynkkynen {510000000UL, {1231606, -33155, 402} }, 5562a8a094STuomas Tynkkynen {612000000UL, {1274912, -34435, 402} }, 5662a8a094STuomas Tynkkynen {714000000UL, {1320040, -35725, 402} }, 5762a8a094STuomas Tynkkynen {816000000UL, {1366990, -37005, 402} }, 5862a8a094STuomas Tynkkynen {918000000UL, {1415762, -38295, 402} }, 5962a8a094STuomas Tynkkynen {1020000000UL, {1466355, -39575, 402} }, 6062a8a094STuomas Tynkkynen {1122000000UL, {1518771, -40865, 402} }, 6162a8a094STuomas Tynkkynen {1224000000UL, {1573009, -42145, 402} }, 6262a8a094STuomas Tynkkynen {1326000000UL, {1629068, -43435, 402} }, 6362a8a094STuomas Tynkkynen {1428000000UL, {1686950, -44715, 402} }, 6462a8a094STuomas Tynkkynen {1530000000UL, {1746653, -46005, 402} }, 6562a8a094STuomas Tynkkynen {1632000000UL, {1808179, -47285, 402} }, 6662a8a094STuomas Tynkkynen {1734000000UL, {1871526, -48575, 402} }, 6762a8a094STuomas Tynkkynen {1836000000UL, {1936696, -49855, 402} }, 6862a8a094STuomas Tynkkynen {1938000000UL, {2003687, -51145, 402} }, 6962a8a094STuomas Tynkkynen {2014500000UL, {2054787, -52095, 402} }, 7062a8a094STuomas Tynkkynen {2116500000UL, {2124957, -53385, 402} }, 7162a8a094STuomas Tynkkynen {2218500000UL, {2196950, -54665, 402} }, 7262a8a094STuomas Tynkkynen {2320500000UL, {2270765, -55955, 402} }, 7362a8a094STuomas Tynkkynen {2422500000UL, {2346401, -57235, 402} }, 7462a8a094STuomas Tynkkynen {2524500000UL, {2437299, -58535, 402} }, 7562a8a094STuomas Tynkkynen {0, { 0, 0, 0} }, 7662a8a094STuomas Tynkkynen }, 7762a8a094STuomas Tynkkynen .cpu_dfll_data = { 7862a8a094STuomas Tynkkynen .tune0_low = 0x005020ff, 7962a8a094STuomas Tynkkynen .tune0_high = 0x005040ff, 8062a8a094STuomas Tynkkynen .tune1 = 0x00000060, 8162a8a094STuomas Tynkkynen } 8262a8a094STuomas Tynkkynen }, 8362a8a094STuomas Tynkkynen }; 8462a8a094STuomas Tynkkynen 8562a8a094STuomas Tynkkynen static int tegra124_dfll_fcpu_probe(struct platform_device *pdev) 8662a8a094STuomas Tynkkynen { 8762a8a094STuomas Tynkkynen int process_id, speedo_id, speedo_value; 8862a8a094STuomas Tynkkynen struct tegra_dfll_soc_data *soc; 8962a8a094STuomas Tynkkynen 9062a8a094STuomas Tynkkynen process_id = tegra_sku_info.cpu_process_id; 9162a8a094STuomas Tynkkynen speedo_id = tegra_sku_info.cpu_speedo_id; 9262a8a094STuomas Tynkkynen speedo_value = tegra_sku_info.cpu_speedo_value; 9362a8a094STuomas Tynkkynen 9462a8a094STuomas Tynkkynen if (speedo_id >= ARRAY_SIZE(cpu_max_freq_table)) { 9562a8a094STuomas Tynkkynen dev_err(&pdev->dev, "unknown max CPU freq for speedo_id=%d\n", 9662a8a094STuomas Tynkkynen speedo_id); 9762a8a094STuomas Tynkkynen return -ENODEV; 9862a8a094STuomas Tynkkynen } 9962a8a094STuomas Tynkkynen 10062a8a094STuomas Tynkkynen soc = devm_kzalloc(&pdev->dev, sizeof(*soc), GFP_KERNEL); 10162a8a094STuomas Tynkkynen if (!soc) 10262a8a094STuomas Tynkkynen return -ENOMEM; 10362a8a094STuomas Tynkkynen 10462a8a094STuomas Tynkkynen soc->dev = get_cpu_device(0); 10562a8a094STuomas Tynkkynen if (!soc->dev) { 10662a8a094STuomas Tynkkynen dev_err(&pdev->dev, "no CPU0 device\n"); 10762a8a094STuomas Tynkkynen return -ENODEV; 10862a8a094STuomas Tynkkynen } 10962a8a094STuomas Tynkkynen 110*27ed2f7eSThierry Reding soc->cvb = tegra_cvb_build_opp_table(tegra124_cpu_cvb_tables, 11162a8a094STuomas Tynkkynen ARRAY_SIZE(tegra124_cpu_cvb_tables), 11262a8a094STuomas Tynkkynen process_id, speedo_id, speedo_value, 11362a8a094STuomas Tynkkynen cpu_max_freq_table[speedo_id], 11462a8a094STuomas Tynkkynen soc->dev); 115*27ed2f7eSThierry Reding if (IS_ERR(soc->cvb)) { 116*27ed2f7eSThierry Reding dev_err(&pdev->dev, "couldn't add OPP table: %ld\n", 117*27ed2f7eSThierry Reding PTR_ERR(soc->cvb)); 118*27ed2f7eSThierry Reding return PTR_ERR(soc->cvb); 11962a8a094STuomas Tynkkynen } 12062a8a094STuomas Tynkkynen 12162a8a094STuomas Tynkkynen 12262a8a094STuomas Tynkkynen return tegra_dfll_register(pdev, soc); 12362a8a094STuomas Tynkkynen } 12462a8a094STuomas Tynkkynen 12562a8a094STuomas Tynkkynen static const struct of_device_id tegra124_dfll_fcpu_of_match[] = { 12662a8a094STuomas Tynkkynen { .compatible = "nvidia,tegra124-dfll", }, 12762a8a094STuomas Tynkkynen { }, 12862a8a094STuomas Tynkkynen }; 12962a8a094STuomas Tynkkynen MODULE_DEVICE_TABLE(of, tegra124_dfll_fcpu_of_match); 13062a8a094STuomas Tynkkynen 13162a8a094STuomas Tynkkynen static const struct dev_pm_ops tegra124_dfll_pm_ops = { 13262a8a094STuomas Tynkkynen SET_RUNTIME_PM_OPS(tegra_dfll_runtime_suspend, 13362a8a094STuomas Tynkkynen tegra_dfll_runtime_resume, NULL) 13462a8a094STuomas Tynkkynen }; 13562a8a094STuomas Tynkkynen 13662a8a094STuomas Tynkkynen static struct platform_driver tegra124_dfll_fcpu_driver = { 13762a8a094STuomas Tynkkynen .probe = tegra124_dfll_fcpu_probe, 13862a8a094STuomas Tynkkynen .remove = tegra_dfll_unregister, 13962a8a094STuomas Tynkkynen .driver = { 14062a8a094STuomas Tynkkynen .name = "tegra124-dfll", 14162a8a094STuomas Tynkkynen .of_match_table = tegra124_dfll_fcpu_of_match, 14262a8a094STuomas Tynkkynen .pm = &tegra124_dfll_pm_ops, 14362a8a094STuomas Tynkkynen }, 14462a8a094STuomas Tynkkynen }; 14562a8a094STuomas Tynkkynen 14662a8a094STuomas Tynkkynen static int __init tegra124_dfll_fcpu_init(void) 14762a8a094STuomas Tynkkynen { 14862a8a094STuomas Tynkkynen return platform_driver_register(&tegra124_dfll_fcpu_driver); 14962a8a094STuomas Tynkkynen } 15062a8a094STuomas Tynkkynen module_init(tegra124_dfll_fcpu_init); 15162a8a094STuomas Tynkkynen 15262a8a094STuomas Tynkkynen static void __exit tegra124_dfll_fcpu_exit(void) 15362a8a094STuomas Tynkkynen { 15462a8a094STuomas Tynkkynen platform_driver_unregister(&tegra124_dfll_fcpu_driver); 15562a8a094STuomas Tynkkynen } 15662a8a094STuomas Tynkkynen module_exit(tegra124_dfll_fcpu_exit); 15762a8a094STuomas Tynkkynen 15862a8a094STuomas Tynkkynen MODULE_DESCRIPTION("Tegra124 DFLL clock source driver"); 15962a8a094STuomas Tynkkynen MODULE_LICENSE("GPL v2"); 16062a8a094STuomas Tynkkynen MODULE_AUTHOR("Aleksandr Frid <afrid@nvidia.com>"); 16162a8a094STuomas Tynkkynen MODULE_AUTHOR("Paul Walmsley <pwalmsley@nvidia.com>"); 162