From d1421147c328a7d06d9a6b8330c73e45139b1e48 Mon Sep 17 00:00:00 2001 From: John Crispin Date: Wed, 30 Mar 2016 23:48:53 +0200 Subject: [PATCH 77/78] cpufreq: mediatek: add driver Signed-off-by: John Crispin --- arch/arm/boot/dts/mt7623-evb.dts | 160 ++++++++++++---- arch/arm/boot/dts/mt7623.dtsi | 50 ++++- drivers/cpufreq/Kconfig.arm | 9 + drivers/cpufreq/Makefile | 1 + drivers/cpufreq/mt7623-cpufreq.c | 389 ++++++++++++++++++++++++++++++++++++++ 5 files changed, 570 insertions(+), 39 deletions(-) create mode 100644 drivers/cpufreq/mt7623-cpufreq.c diff --git a/arch/arm/boot/dts/mt7623-evb.dts b/arch/arm/boot/dts/mt7623-evb.dts index bc2b3f1..4a433f0 100644 --- a/arch/arm/boot/dts/mt7623-evb.dts +++ b/arch/arm/boot/dts/mt7623-evb.dts @@ -39,6 +39,22 @@ }; }; +&cpu0 { + proc-supply = <&mt6323_vproc_reg>; +}; + +&cpu1 { + proc-supply = <&mt6323_vproc_reg>; +}; + +&cpu2 { + proc-supply = <&mt6323_vproc_reg>; +}; + +&cpu3 { + proc-supply = <&mt6323_vproc_reg>; +}; + &pwrap { pmic: mt6323 { compatible = "mediatek,mt6323"; @@ -267,38 +283,36 @@ }; }; -&uart2 { - status = "okay"; -}; +&pio { + nand_pins_default: nanddefault { + pins_dat { + pinmux = , + , + , + , + , + , + , + , + ; + input-enable; + drive-strength = ; + bias-pull-up; + }; -&mmc0 { - status = "okay"; - pinctrl-names = "default", "state_uhs"; - pinctrl-0 = <&mmc0_pins_default>; - pinctrl-1 = <&mmc0_pins_uhs>; - bus-width = <8>; - max-frequency = <50000000>; - cap-mmc-highspeed; - vmmc-supply = <&mt6323_vemc3v3_reg>; - vqmmc-supply = <&mt6323_vio18_reg>; - non-removable; -}; + pins_we { + pinmux = ; + drive-strength = ; + bias-pull-up = ; + }; -&mmc1 { - status = "okay"; - pinctrl-names = "default", "state_uhs"; - pinctrl-0 = <&mmc1_pins_default>; - pinctrl-1 = <&mmc1_pins_uhs>; - bus-width = <4>; - max-frequency = <50000000>; - cap-sd-highspeed; - sd-uhs-sdr25; -// cd-gpios = <&pio 132 0>; - vmmc-supply = <&mt6323_vmch_reg>; - vqmmc-supply = <&mt6323_vmc_reg>; -}; + pins_ale { + pinmux = ; + drive-strength = ; + bias-pull-down = ; + }; + }; -&pio { mmc0_pins_default: mmc0default { pins_cmd_dat { pinmux = , @@ -370,11 +384,6 @@ bias-pull-down; drive-strength = ; }; - -// pins_insert { -// pinmux = ; -// bias-pull-up; -// }; }; mmc1_pins_uhs: mmc1 { @@ -422,6 +431,36 @@ }; }; +&uart2 { + status = "okay"; +}; + +&mmc0 { + status = "okay"; + pinctrl-names = "default", "state_uhs"; + pinctrl-0 = <&mmc0_pins_default>; + pinctrl-1 = <&mmc0_pins_uhs>; + bus-width = <8>; + max-frequency = <50000000>; + cap-mmc-highspeed; + vmmc-supply = <&mt6323_vemc3v3_reg>; + vqmmc-supply = <&mt6323_vio18_reg>; + non-removable; +}; + +&mmc1 { + status = "okay"; + pinctrl-names = "default", "state_uhs"; + pinctrl-0 = <&mmc1_pins_default>; + pinctrl-1 = <&mmc1_pins_uhs>; + bus-width = <4>; + max-frequency = <50000000>; + cap-sd-highspeed; + sd-uhs-sdr25; + vmmc-supply = <&mt6323_vmch_reg>; + vqmmc-supply = <&mt6323_vmc_reg>; +}; + &usb1 { vusb33-supply = <&mt6323_vusb_reg>; vbus-supply = <&usb_p1_vbus>; @@ -456,3 +495,56 @@ mediatek,reset-pin = <&pio 15 0>; status = "okay"; }; + +&nand { + status = "okay"; + #address-cells = <1>; + #size-cells = <1>; + + pinctrl-names = "default"; + pinctrl-0 = <&nand_pins_default>; + + partition@0 { + label = "preloader"; + reg = <0x0 0x40000>; + read-only; + }; + + partition@1 { + label = "u-boot"; + reg = <0x40000 0x80000>; + read-only; + }; + + partition@2 { + label = "u-boot-env"; + reg = <0xc0000 0x40000>; + read-only; + }; + + partition@3 { + label = "factory"; + reg = <0x100000 0x40000>; + read-only; + }; + + partition@4 { + label = "kernel"; + reg = <0x140000 0x2000000>; + }; + + partition@4 { + label = "kernel2"; + reg = <0x2140000 0x2000000>; + }; + + partition@5 { + label = "rootfs"; + reg = <0x4140000 0x1000000>; + }; + + partition@6 { + label = "usrdata"; + reg = <0x5140000 0x9f80000>; + }; +}; diff --git a/arch/arm/boot/dts/mt7623.dtsi b/arch/arm/boot/dts/mt7623.dtsi index f405ec7..76d603a 100644 --- a/arch/arm/boot/dts/mt7623.dtsi +++ b/arch/arm/boot/dts/mt7623.dtsi @@ -31,25 +31,65 @@ #size-cells = <0>; enable-method = "mediatek,mt6589-smp"; - cpu@0 { + cpu0: cpu@0 { device_type = "cpu"; compatible = "arm,cortex-a7"; reg = <0x0>; + clocks = <&infracfg CLK_INFRA_CPUSEL>, + <&apmixedsys CLK_APMIXED_MAINPLL>; + clock-names = "cpu", "intermediate"; + operating-points = < + 598000 1150000 + 747500 1150000 + 1040000 1150000 + 1196000 1200000 + 1300000 1300000 + >; }; - cpu@1 { + cpu1: cpu@1 { device_type = "cpu"; compatible = "arm,cortex-a7"; reg = <0x1>; + clocks = <&infracfg CLK_INFRA_CPUSEL>, + <&apmixedsys CLK_APMIXED_MAINPLL>; + clock-names = "cpu", "intermediate"; + operating-points = < + 598000 1150000 + 747500 1150000 + 1040000 1150000 + 1196000 1200000 + 1300000 1300000 + >; }; - cpu@2 { + cpu2: cpu@2 { device_type = "cpu"; compatible = "arm,cortex-a7"; reg = <0x2>; + clocks = <&infracfg CLK_INFRA_CPUSEL>, + <&apmixedsys CLK_APMIXED_MAINPLL>; + clock-names = "cpu", "intermediate"; + operating-points = < + 598000 1150000 + 747500 1150000 + 1040000 1150000 + 1196000 1200000 + 1300000 1300000 + >; }; - cpu@3 { + cpu3: cpu@3 { device_type = "cpu"; compatible = "arm,cortex-a7"; reg = <0x3>; + clocks = <&infracfg CLK_INFRA_CPUSEL>, + <&apmixedsys CLK_APMIXED_MAINPLL>; + clock-names = "cpu", "intermediate"; + operating-points = < + 598000 1150000 + 747500 1150000 + 1040000 1150000 + 1196000 1200000 + 1300000 1300000 + >; }; }; @@ -300,7 +340,7 @@ clocks = <&pericfg CLK_PERI_NFI>, <&pericfg CLK_PERI_NFI_ECC>, <&pericfg CLK_PERI_NFI_PAD>; clock-names = "nfi_clk", "nfiecc_clk", "pad_clk"; - nand-on-flash-bbt; + // nand-on-flash-bbt; status = "disabled"; }; diff --git a/drivers/cpufreq/Kconfig.arm b/drivers/cpufreq/Kconfig.arm index b1f8a73..baf945e 100644 --- a/drivers/cpufreq/Kconfig.arm +++ b/drivers/cpufreq/Kconfig.arm @@ -81,6 +81,15 @@ config ARM_KIRKWOOD_CPUFREQ This adds the CPUFreq driver for Marvell Kirkwood SoCs. +config ARM_MT7623_CPUFREQ + bool "Mediatek MT7623 CPUFreq support" + depends on ARCH_MEDIATEK && REGULATOR + depends on ARM || (ARM_CPU_TOPOLOGY && COMPILE_TEST) + depends on !CPU_THERMAL || THERMAL=y + select PM_OPP + help + This adds the CPUFreq driver support for Mediatek MT7623 SoC. + config ARM_MT8173_CPUFREQ bool "Mediatek MT8173 CPUFreq support" depends on ARCH_MEDIATEK && REGULATOR diff --git a/drivers/cpufreq/Makefile b/drivers/cpufreq/Makefile index c0af1a1..e198752 100644 --- a/drivers/cpufreq/Makefile +++ b/drivers/cpufreq/Makefile @@ -57,6 +57,7 @@ obj-$(CONFIG_ARM_HISI_ACPU_CPUFREQ) += hisi-acpu-cpufreq.o obj-$(CONFIG_ARM_IMX6Q_CPUFREQ) += imx6q-cpufreq.o obj-$(CONFIG_ARM_INTEGRATOR) += integrator-cpufreq.o obj-$(CONFIG_ARM_KIRKWOOD_CPUFREQ) += kirkwood-cpufreq.o +obj-$(CONFIG_ARM_MT7623_CPUFREQ) += mt7623-cpufreq.o obj-$(CONFIG_ARM_MT8173_CPUFREQ) += mt8173-cpufreq.o obj-$(CONFIG_ARM_OMAP2PLUS_CPUFREQ) += omap-cpufreq.o obj-$(CONFIG_ARM_PXA2xx_CPUFREQ) += pxa2xx-cpufreq.o diff --git a/drivers/cpufreq/mt7623-cpufreq.c b/drivers/cpufreq/mt7623-cpufreq.c new file mode 100644 index 0000000..8d154ce --- /dev/null +++ b/drivers/cpufreq/mt7623-cpufreq.c @@ -0,0 +1,389 @@ +/* + * Copyright (c) 2015 Linaro Ltd. + * Author: Pi-Cheng Chen + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define VOLT_TOL (10000) + +/* + * When scaling the clock frequency of a CPU clock domain, the clock source + * needs to be switched to another stable PLL clock temporarily until + * the original PLL becomes stable at target frequency. + */ +struct mtk_cpu_dvfs_info { + struct device *cpu_dev; + struct regulator *proc_reg; + struct clk *cpu_clk; + struct clk *inter_clk; + struct thermal_cooling_device *cdev; + int intermediate_voltage; +}; + +static int mtk_cpufreq_set_voltage(struct mtk_cpu_dvfs_info *info, int vproc) +{ + return regulator_set_voltage(info->proc_reg, vproc, + vproc + VOLT_TOL); +} + +static int mtk_cpufreq_set_target(struct cpufreq_policy *policy, + unsigned int index) +{ + struct cpufreq_frequency_table *freq_table = policy->freq_table; + struct clk *cpu_clk = policy->clk; + struct clk *armpll = clk_get_parent(cpu_clk); + struct mtk_cpu_dvfs_info *info = policy->driver_data; + struct device *cpu_dev = info->cpu_dev; + struct dev_pm_opp *opp; + long freq_hz, old_freq_hz; + int vproc, old_vproc, inter_vproc, target_vproc, ret; + + inter_vproc = info->intermediate_voltage; + + old_freq_hz = clk_get_rate(cpu_clk); + old_vproc = regulator_get_voltage(info->proc_reg); + + freq_hz = freq_table[index].frequency * 1000; + + rcu_read_lock(); + opp = dev_pm_opp_find_freq_ceil(cpu_dev, &freq_hz); + if (IS_ERR(opp)) { + rcu_read_unlock(); + pr_err("cpu%d: failed to find OPP for %ld\n", + policy->cpu, freq_hz); + return PTR_ERR(opp); + } + vproc = dev_pm_opp_get_voltage(opp); + rcu_read_unlock(); + + /* + * If the new voltage or the intermediate voltage is higher than the + * current voltage, scale up voltage first. + */ + target_vproc = (inter_vproc > vproc) ? inter_vproc : vproc; + if (old_vproc < target_vproc) { + ret = mtk_cpufreq_set_voltage(info, target_vproc); + if (ret) { + pr_err("cpu%d: failed to scale up voltage!\n", + policy->cpu); + mtk_cpufreq_set_voltage(info, old_vproc); + return ret; + } + } + + /* Reparent the CPU clock to intermediate clock. */ + ret = clk_set_parent(cpu_clk, info->inter_clk); + if (ret) { + pr_err("cpu%d: failed to re-parent cpu clock!\n", + policy->cpu); + mtk_cpufreq_set_voltage(info, old_vproc); + WARN_ON(1); + return ret; + } + + /* Set the original PLL to target rate. */ + ret = clk_set_rate(armpll, freq_hz); + if (ret) { + pr_err("cpu%d: failed to scale cpu clock rate!\n", + policy->cpu); + clk_set_parent(cpu_clk, armpll); + mtk_cpufreq_set_voltage(info, old_vproc); + return ret; + } + + /* Set parent of CPU clock back to the original PLL. */ + ret = clk_set_parent(cpu_clk, armpll); + if (ret) { + pr_err("cpu%d: failed to re-parent cpu clock!\n", + policy->cpu); + mtk_cpufreq_set_voltage(info, inter_vproc); + WARN_ON(1); + return ret; + } + + /* + * If the new voltage is lower than the intermediate voltage or the + * original voltage, scale down to the new voltage. + */ + if (vproc < inter_vproc || vproc < old_vproc) { + ret = mtk_cpufreq_set_voltage(info, vproc); + if (ret) { + pr_err("cpu%d: failed to scale down voltage!\n", + policy->cpu); + clk_set_parent(cpu_clk, info->inter_clk); + clk_set_rate(armpll, old_freq_hz); + clk_set_parent(cpu_clk, armpll); + return ret; + } + } + + return 0; +} + +static void mtk_cpufreq_ready(struct cpufreq_policy *policy) +{ + struct mtk_cpu_dvfs_info *info = policy->driver_data; + struct device_node *np = of_node_get(info->cpu_dev->of_node); + + if (WARN_ON(!np)) + return; + + if (of_find_property(np, "#cooling-cells", NULL)) { + info->cdev = of_cpufreq_cooling_register(np, + policy->related_cpus); + + if (IS_ERR(info->cdev)) { + dev_err(info->cpu_dev, + "running cpufreq without cooling device: %ld\n", + PTR_ERR(info->cdev)); + + info->cdev = NULL; + } + } + + of_node_put(np); +} + +static int mtk_cpu_dvfs_info_init(struct mtk_cpu_dvfs_info *info, int cpu) +{ + struct device *cpu_dev; + struct regulator *proc_reg = ERR_PTR(-ENODEV); + struct clk *cpu_clk = ERR_PTR(-ENODEV); + struct clk *inter_clk = ERR_PTR(-ENODEV); + struct dev_pm_opp *opp; + unsigned long rate; + int ret; + + cpu_dev = get_cpu_device(cpu); + if (!cpu_dev) { + pr_err("failed to get cpu%d device\n", cpu); + return -ENODEV; + } + + cpu_clk = clk_get(cpu_dev, "cpu"); + if (IS_ERR(cpu_clk)) { + if (PTR_ERR(cpu_clk) == -EPROBE_DEFER) + pr_warn("cpu clk for cpu%d not ready, retry.\n", cpu); + else + pr_err("failed to get cpu clk for cpu%d\n", cpu); + + ret = PTR_ERR(cpu_clk); + return ret; + } + + inter_clk = clk_get(cpu_dev, "intermediate"); + if (IS_ERR(inter_clk)) { + if (PTR_ERR(inter_clk) == -EPROBE_DEFER) + pr_warn("intermediate clk for cpu%d not ready, retry.\n", + cpu); + else + pr_err("failed to get intermediate clk for cpu%d\n", + cpu); + + ret = PTR_ERR(inter_clk); + goto out_free_resources; + } + + proc_reg = regulator_get_exclusive(cpu_dev, "proc"); + if (IS_ERR(proc_reg)) { + if (PTR_ERR(proc_reg) == -EPROBE_DEFER) + pr_warn("proc regulator for cpu%d not ready, retry.\n", + cpu); + else + pr_err("failed to get proc regulator for cpu%d\n", + cpu); + + ret = PTR_ERR(proc_reg); + goto out_free_resources; + } + + ret = dev_pm_opp_of_add_table(cpu_dev); + if (ret) { + pr_warn("no OPP table for cpu%d\n", cpu); + goto out_free_resources; + } + + /* Search a safe voltage for intermediate frequency. */ + rate = clk_get_rate(inter_clk); + rcu_read_lock(); + opp = dev_pm_opp_find_freq_ceil(cpu_dev, &rate); + if (IS_ERR(opp)) { + rcu_read_unlock(); + pr_err("failed to get intermediate opp for cpu%d\n", cpu); + ret = PTR_ERR(opp); + goto out_free_opp_table; + } + info->intermediate_voltage = dev_pm_opp_get_voltage(opp); + rcu_read_unlock(); + + info->cpu_dev = cpu_dev; + info->proc_reg = proc_reg; + info->cpu_clk = cpu_clk; + info->inter_clk = inter_clk; + + return 0; + +out_free_opp_table: + dev_pm_opp_of_remove_table(cpu_dev); + +out_free_resources: + if (!IS_ERR(proc_reg)) + regulator_put(proc_reg); + if (!IS_ERR(cpu_clk)) + clk_put(cpu_clk); + if (!IS_ERR(inter_clk)) + clk_put(inter_clk); + + return ret; +} + +static void mtk_cpu_dvfs_info_release(struct mtk_cpu_dvfs_info *info) +{ + if (!IS_ERR(info->proc_reg)) + regulator_put(info->proc_reg); + if (!IS_ERR(info->cpu_clk)) + clk_put(info->cpu_clk); + if (!IS_ERR(info->inter_clk)) + clk_put(info->inter_clk); + + dev_pm_opp_of_remove_table(info->cpu_dev); +} + +static int mtk_cpufreq_init(struct cpufreq_policy *policy) +{ + struct mtk_cpu_dvfs_info *info; + struct cpufreq_frequency_table *freq_table; + int ret; + + info = kzalloc(sizeof(*info), GFP_KERNEL); + if (!info) + return -ENOMEM; + + ret = mtk_cpu_dvfs_info_init(info, policy->cpu); + if (ret) { + pr_err("%s failed to initialize dvfs info for cpu%d\n", + __func__, policy->cpu); + goto out_free_dvfs_info; + } + + ret = dev_pm_opp_init_cpufreq_table(info->cpu_dev, &freq_table); + if (ret) { + pr_err("failed to init cpufreq table for cpu%d: %d\n", + policy->cpu, ret); + goto out_release_dvfs_info; + } + + ret = cpufreq_table_validate_and_show(policy, freq_table); + if (ret) { + pr_err("%s: invalid frequency table: %d\n", __func__, ret); + goto out_free_cpufreq_table; + } + + /* CPUs in the same cluster share a clock and power domain. */ + cpumask_setall(policy->cpus); + policy->driver_data = info; + policy->clk = info->cpu_clk; + + return 0; + +out_free_cpufreq_table: + dev_pm_opp_free_cpufreq_table(info->cpu_dev, &freq_table); + +out_release_dvfs_info: + mtk_cpu_dvfs_info_release(info); + +out_free_dvfs_info: + kfree(info); + + return ret; +} + +static int mtk_cpufreq_exit(struct cpufreq_policy *policy) +{ + struct mtk_cpu_dvfs_info *info = policy->driver_data; + + cpufreq_cooling_unregister(info->cdev); + dev_pm_opp_free_cpufreq_table(info->cpu_dev, &policy->freq_table); + mtk_cpu_dvfs_info_release(info); + kfree(info); + + return 0; +} + +static struct cpufreq_driver mt7623_cpufreq_driver = { + .flags = CPUFREQ_STICKY | CPUFREQ_NEED_INITIAL_FREQ_CHECK, + .verify = cpufreq_generic_frequency_table_verify, + .target_index = mtk_cpufreq_set_target, + .get = cpufreq_generic_get, + .init = mtk_cpufreq_init, + .exit = mtk_cpufreq_exit, + .ready = mtk_cpufreq_ready, + .name = "mtk-cpufreq", + .attr = cpufreq_generic_attr, +}; + +static int mt7623_cpufreq_probe(struct platform_device *pdev) +{ + int ret; + + ret = cpufreq_register_driver(&mt7623_cpufreq_driver); + if (ret) + pr_err("failed to register mtk cpufreq driver\n"); + + return ret; +} + +static struct platform_driver mt7623_cpufreq_platdrv = { + .driver = { + .name = "mt7623-cpufreq", + }, + .probe = mt7623_cpufreq_probe, +}; + +static int mt7623_cpufreq_driver_init(void) +{ + struct platform_device *pdev; + int err; + + if (!of_machine_is_compatible("mediatek,mt7623")) + return -ENODEV; + + err = platform_driver_register(&mt7623_cpufreq_platdrv); + if (err) + return err; + + /* + * Since there's no place to hold device registration code and no + * device tree based way to match cpufreq driver yet, both the driver + * and the device registration codes are put here to handle defer + * probing. + */ + pdev = platform_device_register_simple("mt7623-cpufreq", -1, NULL, 0); + if (IS_ERR(pdev)) { + pr_err("failed to register mtk-cpufreq platform device\n"); + return PTR_ERR(pdev); + } + + return 0; +} +device_initcall(mt7623_cpufreq_driver_init); -- 1.7.10.4