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> 23*33996b02SPaul Gortmaker #include <linux/init.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, 50e8f6a68cSThierry Reding .entries = { 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 } }, 752690e912SThierry Reding { 0UL, { 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 { 87f7c42d98SThierry Reding int process_id, speedo_id, speedo_value, err; 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 110f7c42d98SThierry Reding soc->max_freq = cpu_max_freq_table[speedo_id]; 111f7c42d98SThierry Reding 112e8f6a68cSThierry Reding soc->cvb = tegra_cvb_add_opp_table(soc->dev, tegra124_cpu_cvb_tables, 11362a8a094STuomas Tynkkynen ARRAY_SIZE(tegra124_cpu_cvb_tables), 11462a8a094STuomas Tynkkynen process_id, speedo_id, speedo_value, 115f7c42d98SThierry Reding soc->max_freq); 11627ed2f7eSThierry Reding if (IS_ERR(soc->cvb)) { 11727ed2f7eSThierry Reding dev_err(&pdev->dev, "couldn't add OPP table: %ld\n", 11827ed2f7eSThierry Reding PTR_ERR(soc->cvb)); 11927ed2f7eSThierry Reding return PTR_ERR(soc->cvb); 12062a8a094STuomas Tynkkynen } 12162a8a094STuomas Tynkkynen 122f7c42d98SThierry Reding err = tegra_dfll_register(pdev, soc); 123f7c42d98SThierry Reding if (err < 0) { 124f7c42d98SThierry Reding tegra_cvb_remove_opp_table(soc->dev, soc->cvb, soc->max_freq); 125f7c42d98SThierry Reding return err; 126f7c42d98SThierry Reding } 12762a8a094STuomas Tynkkynen 128f7c42d98SThierry Reding platform_set_drvdata(pdev, soc); 129f7c42d98SThierry Reding 130f7c42d98SThierry Reding return 0; 131f7c42d98SThierry Reding } 132f7c42d98SThierry Reding 133f7c42d98SThierry Reding static int tegra124_dfll_fcpu_remove(struct platform_device *pdev) 134f7c42d98SThierry Reding { 135f7c42d98SThierry Reding struct tegra_dfll_soc_data *soc = platform_get_drvdata(pdev); 136f7c42d98SThierry Reding int err; 137f7c42d98SThierry Reding 138f7c42d98SThierry Reding err = tegra_dfll_unregister(pdev); 139f7c42d98SThierry Reding if (err < 0) 140f7c42d98SThierry Reding dev_err(&pdev->dev, "failed to unregister DFLL: %d\n", err); 141f7c42d98SThierry Reding 142f7c42d98SThierry Reding tegra_cvb_remove_opp_table(soc->dev, soc->cvb, soc->max_freq); 143f7c42d98SThierry Reding 144f7c42d98SThierry Reding return 0; 14562a8a094STuomas Tynkkynen } 14662a8a094STuomas Tynkkynen 14762a8a094STuomas Tynkkynen static const struct of_device_id tegra124_dfll_fcpu_of_match[] = { 14862a8a094STuomas Tynkkynen { .compatible = "nvidia,tegra124-dfll", }, 14962a8a094STuomas Tynkkynen { }, 15062a8a094STuomas Tynkkynen }; 15162a8a094STuomas Tynkkynen 15262a8a094STuomas Tynkkynen static const struct dev_pm_ops tegra124_dfll_pm_ops = { 15362a8a094STuomas Tynkkynen SET_RUNTIME_PM_OPS(tegra_dfll_runtime_suspend, 15462a8a094STuomas Tynkkynen tegra_dfll_runtime_resume, NULL) 15562a8a094STuomas Tynkkynen }; 15662a8a094STuomas Tynkkynen 15762a8a094STuomas Tynkkynen static struct platform_driver tegra124_dfll_fcpu_driver = { 15862a8a094STuomas Tynkkynen .probe = tegra124_dfll_fcpu_probe, 159f7c42d98SThierry Reding .remove = tegra124_dfll_fcpu_remove, 16062a8a094STuomas Tynkkynen .driver = { 16162a8a094STuomas Tynkkynen .name = "tegra124-dfll", 16262a8a094STuomas Tynkkynen .of_match_table = tegra124_dfll_fcpu_of_match, 16362a8a094STuomas Tynkkynen .pm = &tegra124_dfll_pm_ops, 16462a8a094STuomas Tynkkynen }, 16562a8a094STuomas Tynkkynen }; 16662a8a094STuomas Tynkkynen 16762a8a094STuomas Tynkkynen static int __init tegra124_dfll_fcpu_init(void) 16862a8a094STuomas Tynkkynen { 16962a8a094STuomas Tynkkynen return platform_driver_register(&tegra124_dfll_fcpu_driver); 17062a8a094STuomas Tynkkynen } 171*33996b02SPaul Gortmaker device_initcall(tegra124_dfll_fcpu_init); 172