From patchwork Mon Nov 23 14:07:49 2015 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Bai Ping X-Patchwork-Id: 7677551 Return-Path: X-Original-To: patchwork-linux-pm@patchwork.kernel.org Delivered-To: patchwork-parsemail@patchwork2.web.kernel.org Received: from mail.kernel.org (mail.kernel.org [198.145.29.136]) by patchwork2.web.kernel.org (Postfix) with ESMTP id B33F4BF90C for ; Mon, 23 Nov 2015 06:04:14 +0000 (UTC) Received: from mail.kernel.org (localhost [127.0.0.1]) by mail.kernel.org (Postfix) with ESMTP id 7BA2520670 for ; Mon, 23 Nov 2015 06:04:13 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by mail.kernel.org (Postfix) with ESMTP id 1EB72204AF for ; Mon, 23 Nov 2015 06:04:12 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1752718AbbKWGEL (ORCPT ); Mon, 23 Nov 2015 01:04:11 -0500 Received: from mail-bn1on0138.outbound.protection.outlook.com ([157.56.110.138]:14377 "EHLO na01-bn1-obe.outbound.protection.outlook.com" rhost-flags-OK-OK-OK-FAIL) by vger.kernel.org with ESMTP id S1752459AbbKWGEJ (ORCPT ); Mon, 23 Nov 2015 01:04:09 -0500 Received: from BY2PR03CA007.namprd03.prod.outlook.com (10.255.93.24) by CY1PR0301MB1227.namprd03.prod.outlook.com (10.161.212.149) with Microsoft SMTP Server (TLS) id 15.1.331.20; Mon, 23 Nov 2015 06:04:06 +0000 Received: from BN1BFFO11FD029.protection.gbl (10.255.93.4) by BY2PR03CA007.outlook.office365.com (10.255.93.24) with Microsoft SMTP Server (TLS) id 15.1.331.20 via Frontend Transport; Mon, 23 Nov 2015 06:04:05 +0000 Authentication-Results: spf=permerror (sender IP is 192.88.158.2) smtp.mailfrom=freescale.com; vger.kernel.org; dkim=none (message not signed) header.d=none;vger.kernel.org; dmarc=none action=none header.from=freescale.com; Received-SPF: PermError (protection.outlook.com: domain of freescale.com used an invalid SPF mechanism) Received: from az84smr01.freescale.net (192.88.158.2) by BN1BFFO11FD029.mail.protection.outlook.com (10.58.144.92) with Microsoft SMTP Server (TLS) id 15.1.331.11 via Frontend Transport; Mon, 23 Nov 2015 06:04:05 +0000 Received: from b51503-01.ap.freescale.net (b51503-01.ap.freescale.net [10.193.102.227]) by az84smr01.freescale.net (8.14.3/8.14.0) with ESMTP id tAN641V7021819; Sun, 22 Nov 2015 23:04:02 -0700 From: Bai Ping To: , CC: , Subject: [PATCH] cpufreq: imx: Add cpufreq driver for imx7D/Solo SOC Date: Mon, 23 Nov 2015 22:07:49 +0800 Message-ID: <1448287669-3600-1-git-send-email-b51503@freescale.com> X-Mailer: git-send-email 1.9.1 X-EOPAttributedMessage: 0 X-Microsoft-Exchange-Diagnostics: 1; BN1BFFO11FD029; 1:AMTOD5+asxmOZfhq0Xp2ZFDel2CEg7CPVu5Xl+CrgS15Bgq5toRZ+RomqZ/BjbV6n3tH0lyXn73HTCObXJbp11LDeBDE1ouSz+iOuYbT0P37y2vjTU06IcfN34JGCdCjE7T9DwQnBJwpczWk125ypzDqY3xzpNb/jkcKsZAX7Ars+iXp0Fb1OABV2Gxw+j5+AH8nZHMJlwTe3kAQSuukE8q+AmDVjSsU5s9oY/YxnFOh3ojQPpzEHDgUuP9A3s0YjeY8AO9gx//uU2RAUnVks+YWsrLDbrIOYWAmBq4/+J1CO2577WiC78v55/sml2MD2DAx58xFXKZdpiiCl9vy5gjYXP+uyqU5Hn/u6s6blvaNjFKHKTg/bkJYCKzt/rwQSPoBZ3Qm/IVN4EoBBRhQww== X-Forefront-Antispam-Report: CIP:192.88.158.2; CTRY:US; IPV:NLI; EFV:NLI; SFV:NSPM; SFS:(10019020)(6009001)(2980300002)(448002)(189002)(199003)(5003940100001)(575784001)(87936001)(33646002)(92566002)(85326001)(5001770100001)(11100500001)(6806005)(36756003)(586003)(50466002)(48376002)(106466001)(5007970100001)(104016004)(189998001)(19580395003)(81156007)(50226001)(77096005)(5001960100002)(229853001)(5008740100001)(50986999)(47776003)(5001920100001)(19580405001)(97736004)(69596002)(2004002); DIR:OUT; SFP:1102; SCL:1; SRVR:CY1PR0301MB1227; H:az84smr01.freescale.net; FPR:; SPF:PermError; PTR:InfoDomainNonexistent; MX:1; A:1; LANG:en; MIME-Version: 1.0 X-Microsoft-Exchange-Diagnostics: 1; CY1PR0301MB1227; 2:DxNRhAYlSZaPfZkl4zIsc8dy1CoDQ7VrV29cahCAwIbzpWx3UhDiLUJPc/jEndH8XStSL97jQcW69AlZ1TZVubNLJ9Ok4+9FveaAbkbUHQ/KaL82dBlVSpKt1FBxcd4EOzl4gKxcSZWX5xrpjvDzfg==; 3:ezlZ5F84hBL7O0ODvFyfEUSZjd0h6hxpMaU0Dy4AMK5aeAnC9iCTZIry/o5fnqaw9LZu6wRxWPDJHEoccf57EzFTwUll78AaR/y3lqQVxfBqZ51lkHWhfMpxWEkPJKaxJRGLp2DIHZNscuu+cFuoggMfef+OwyYiO8m9OxY8iinVv9Nn/Ca/zAaRK2wY/pttb7zfXJFPw/VT+wOavcjYWm7Nj5k1hGkGNM7YzT4isdA=; 25:DO4E++dal4mM0y+M+MbqwTRY4/yktL6wrD8nLa5YVWJ5uewJqNubzcQNLoxGX8ttsSyrVCVdVaMRG6Vndi5O/h8oAT1VIE8l/HHhnLJZ4coSMgxc11TJ9EPLLPaWpHj941ipFigJhro0jDh9zRYAAMNJB8o7Fz+SSuwSd1Ll2torW+fde6BZvPP7EOpoBRqW9v7+FKkFapkKn3YSTjwmUr3ZnEUPFBeASmC/UT928RXeU4QLGr8+3vGj7tC2vewT0i4YbheSxhbuiI32qheeXA== X-Microsoft-Antispam: UriScan:;BCL:0;PCL:0;RULEID:;SRVR:CY1PR0301MB1227; X-Microsoft-Exchange-Diagnostics: 1; CY1PR0301MB1227; 20:DpHJ8fYdaHe/KnJXwADUCqbPo9eHmdFyVXAXE9sMaSxmcL3hFhdXix0HOxKWhw0mPrV0zF6LK8abwN41TbirpCDt1tQTCF4A4FfcxVaz8u2BaiMPYAK0Ke8eRChjtkNv0JvgBF9fAyWtRi648R9Qx5bxpyU7BK2rFCcH1/HS/K98HrOislGjfUFEDEJHWkpbFUN4yBNXUMpCMRJ4de0MDNnLPnt8sHyMCZgzvwNvWHgAhCeTICu7Mr7Tlxrx/d4abPW8Ypt65y/kzC+oe4dvmvLF9m31+LtGIKDDgNYSGggrYZFkHVYFdzZqf6wYvVFgsngafe5zHFd3skKxjF3gbQPkqeWcG5Vv13q1N8Obhuo=; 4:JMkSCffe5BzShE5qtzONDCJ40NjZPRmSRoRGTORkoL/98POoU1+/lqQRGxjsOaIr9/qkdgNzh32JOixdghdBRwwMMJ1Drta+1x55Mtjiklwo/TIdBBrNlRHN6gdPCUT83tmcw2DEz+Gz4u2hX1e/dL77TPHp/SHb89B++pCqCFvcfSosYiXY3QjJy/kTvBXMxjZ3+CpjLOsToW5jpRFpoEETcy7VYEu1YoYrg/k73x+qbxfigJBIXRfV7iTRggk+2LKS5PZ2wACDxC0ar0zRCp1jBecqADNXNAdVisGIcTai5YZ5QIJXC1i5aYxCu+yVGa/iPK2axkV5vMd/IvSTcqpOFmeQfzk0+u5vCdvqcezBALtqiUIGcCWAXsrzjVPxtSCnyjCQx+gmChBlAPBUrJ41VG7bIx8SSV0u85+hX76Nkw8/rlTLJB/QpckeBQBp X-Microsoft-Antispam-PRVS: X-Exchange-Antispam-Report-Test: UriScan:(101931422205132); X-Exchange-Antispam-Report-CFA-Test: BCL:0; PCL:0; RULEID:(601004)(2401047)(5005006)(520078)(8121501046)(3002001)(10201501046); SRVR:CY1PR0301MB1227; BCL:0; PCL:0; RULEID:; SRVR:CY1PR0301MB1227; X-Forefront-PRVS: 07697999E6 X-Microsoft-Exchange-Diagnostics: =?us-ascii?Q?1; CY1PR0301MB1227; 23:SelYhH+SNOay2HgvDqfTrrlRWtXjUbdO6llQmE/?= =?us-ascii?Q?4ZAW05E8Q2BYmDAWAEhEwwj/jHbLwSeiDSTZoc0ornV4VGdgr7WNtQNk5KQB?= =?us-ascii?Q?4jentMuojtfs5NiaDtW4oKNCNYnspLBvLo58jlPPzNCL56eJvyHOUqwI0E7d?= =?us-ascii?Q?2Xb/xJk3uw+AfDMkcg5s/i3h1ZEpu0P3ua0IoE+/JMrDpwEgktZ0rP3zV70A?= =?us-ascii?Q?n80yXdN0wLsr3Co6KGhopPaw4baFD5e3S4JKaR1ML+a1N3oCKPrEsKiptlBU?= =?us-ascii?Q?ZIql7qKObro7UjQTfuM7O0WObBnA/kfnE3vmtB7U+PjTs2M7cIAgUyw6B36a?= =?us-ascii?Q?AZPLN2oHTWMeCU9oz/V/oBO7DP4aUsXKtJ25LpdrrXzVHxudYAfmc2mgAiNm?= =?us-ascii?Q?A1TcvSErXyjr1HbkBQ00urEUblB3JnJAkzF2e4BFbl3/rGnOk2jrXOszpAwn?= =?us-ascii?Q?gk1Lv09l2SG2TAGcW6I8TCvfy2iZJ4etfglYoWQcCqLfFiQ0zUM4f7GxINrd?= =?us-ascii?Q?6WDKWfXv4lrohPo46P/KO16/D1A7BArXyqIUy+uONw43+jI5cvI09oXVZn0H?= =?us-ascii?Q?QfCklZ0bz4MqcTQl/YCaG/To8XKjyPVkJVqNoPOjRejy9kaTLTZ1t3GcLsmB?= =?us-ascii?Q?kVGkntxmkXcf4cs+EZVjjho4b5IInfWulfIUllGg+7T3q7XvlA1nnfyMI1Vm?= =?us-ascii?Q?7m2kKdRHwC1a8qlMoRcvJCn+qpTfy897hgo03fp2lKJN9pt6YUF7IONFrPyU?= =?us-ascii?Q?YL/8k+bEygoB8ienVEes04C3wmjLPC0ezC1cZBA04ZgaQeptRnldV3cFykgR?= =?us-ascii?Q?jkW5VxeepV1AQob3iBRx9XmbzdqSE3HgOM4HsTtpljLL3j0j2Ety/pjmPApL?= =?us-ascii?Q?C3MapWvUSMUktp7BL1zo1LDInu2SmCc6cYGpl8egcV2yGtlkqc5SfHaNJcUF?= =?us-ascii?Q?1LHhw7UUtXia56rFRr17Dn4V6Vci405DfzCI/KjjILym3AtkKA0uzrZLEACa?= =?us-ascii?Q?lDzaFv1WAd8UIL+fRRYh/hLpc?= X-Microsoft-Exchange-Diagnostics: 1; CY1PR0301MB1227; 5:wo0Lw0ZNXH+FlLsIeBMivasOfUvtx/Z0J/jxNt1foNoAA/wjChnnmMdjNylpH4/nie6O5b+AQs0laYOrvBJIRwHH0ZfnhXANfDwQbxlNRabyxPGtJarP9t8XL4XOnTu3rwV9nupvrkmFo8i5dccL0Q==; 24:e9757m9fyFsV8withf+dqJs3UZRksUafJPG0NHulyrxDE09NMHI6ZFHqaV/IK74hStPdA464/odTO4Da5m2mKW1KW8jlUYkNEj7slcymJmc= SpamDiagnosticOutput: 1:23 SpamDiagnosticMetadata: NSPM X-OriginatorOrg: freescale.com X-MS-Exchange-CrossTenant-OriginalArrivalTime: 23 Nov 2015 06:04:05.2099 (UTC) X-MS-Exchange-CrossTenant-Id: 710a03f5-10f6-4d38-9ff4-a80b81da590d X-MS-Exchange-CrossTenant-OriginalAttributedTenantConnectingIp: TenantId=710a03f5-10f6-4d38-9ff4-a80b81da590d; Ip=[192.88.158.2]; Helo=[az84smr01.freescale.net] X-MS-Exchange-CrossTenant-FromEntityHeader: HybridOnPrem X-MS-Exchange-Transport-CrossTenantHeadersStamped: CY1PR0301MB1227 Sender: linux-pm-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-pm@vger.kernel.org X-Spam-Status: No, score=-5.5 required=5.0 tests=BAYES_00, DATE_IN_FUTURE_06_12, RCVD_IN_DNSWL_HI, RP_MATCHES_RCVD, UNPARSEABLE_RELAY autolearn=ham version=3.3.1 X-Spam-Checker-Version: SpamAssassin 3.3.1 (2010-03-16) on mail.kernel.org X-Virus-Scanned: ClamAV using ClamSMTP The i.MX7Dual/Solo is a new series of the i.MX SOC family. The existing cpufreq driver for 'i.MX6' or 'cpufreq-dt' can NOT match the requirement of this new SOC. This patch adds the cpufreq driver for i.MX7Dual/Solo. Signed-off-by: Bai Ping --- drivers/cpufreq/Kconfig.arm | 10 ++ drivers/cpufreq/Makefile | 1 + drivers/cpufreq/imx7d-cpufreq.c | 257 ++++++++++++++++++++++++++++++++++++++++ 3 files changed, 268 insertions(+) create mode 100644 drivers/cpufreq/imx7d-cpufreq.c diff --git a/drivers/cpufreq/Kconfig.arm b/drivers/cpufreq/Kconfig.arm index 8014c23..adddfd7 100644 --- a/drivers/cpufreq/Kconfig.arm +++ b/drivers/cpufreq/Kconfig.arm @@ -67,6 +67,16 @@ config ARM_IMX6Q_CPUFREQ If in doubt, say N. +config ARM_IMX7D_CPUFREQ + tristate "Freescale i.MX7 cpufreq support" + depends on ARCH_MXC + select PM_OPP + help + This adds cpufreq driver support for Freescale i.MX7Dual/Solo + series SoCs. + + If in doubt, say N. + config ARM_INTEGRATOR tristate "CPUfreq driver for ARM Integrator CPUs" depends on ARCH_INTEGRATOR diff --git a/drivers/cpufreq/Makefile b/drivers/cpufreq/Makefile index c0af1a1..66723a0 100644 --- a/drivers/cpufreq/Makefile +++ b/drivers/cpufreq/Makefile @@ -55,6 +55,7 @@ obj-$(CONFIG_ARM_EXYNOS5440_CPUFREQ) += exynos5440-cpufreq.o obj-$(CONFIG_ARM_HIGHBANK_CPUFREQ) += highbank-cpufreq.o obj-$(CONFIG_ARM_HISI_ACPU_CPUFREQ) += hisi-acpu-cpufreq.o obj-$(CONFIG_ARM_IMX6Q_CPUFREQ) += imx6q-cpufreq.o +obj-$(CONFIG_ARM_IMX7D_CPUFREQ) += imx7d-cpufreq.o obj-$(CONFIG_ARM_INTEGRATOR) += integrator-cpufreq.o obj-$(CONFIG_ARM_KIRKWOOD_CPUFREQ) += kirkwood-cpufreq.o obj-$(CONFIG_ARM_MT8173_CPUFREQ) += mt8173-cpufreq.o diff --git a/drivers/cpufreq/imx7d-cpufreq.c b/drivers/cpufreq/imx7d-cpufreq.c new file mode 100644 index 0000000..7506702 --- /dev/null +++ b/drivers/cpufreq/imx7d-cpufreq.c @@ -0,0 +1,257 @@ +/* + * Copyright (C) 2015 Freescale Semiconductor, Inc. + * + * 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. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +static struct clk *arm_clk; +static struct clk *pll_arm; +static struct clk *arm_src; +static struct clk *pll_sys_main; + +static struct regulator *arm_reg; + +static struct device *cpu_dev; +static bool free_opp; +static struct cpufreq_frequency_table *freq_table; +static unsigned int transition_latency; + +static int imx7d_set_target(struct cpufreq_policy *policy, unsigned int index) +{ + struct dev_pm_opp *opp; + unsigned long freq_hz, volt, volt_old; + unsigned int old_freq, new_freq; + int ret; + + new_freq = freq_table[index].frequency; + freq_hz = new_freq * 1000; + old_freq = clk_get_rate(arm_clk) / 1000; + + rcu_read_lock(); + opp = dev_pm_opp_find_freq_ceil(cpu_dev, &freq_hz); + if (IS_ERR(opp)) { + rcu_read_unlock(); + dev_err(cpu_dev, "failed to find OPP for %ld\n", freq_hz); + return PTR_ERR(opp); + } + volt = dev_pm_opp_get_voltage(opp); + rcu_read_unlock(); + volt_old = regulator_get_voltage(arm_reg); + + dev_dbg(cpu_dev, "%u MHz, %ld mV --> %u MHz, %ld mV\n", + old_freq / 1000, volt_old / 1000, + new_freq / 1000, volt / 1000); + + /* Scaling up? scale voltage before frequency */ + if (new_freq > old_freq) { + ret = regulator_set_voltage_tol(arm_reg, volt, 0); + if (ret) { + dev_err(cpu_dev, "failed to scale vddarm up: %d\n", ret); + return ret; + } + } + + /* before changing pll_arm rate, change the arm_src's soure + * to pll_sys_main clk first. + */ + clk_set_parent(arm_src, pll_sys_main); + clk_set_rate(pll_arm, new_freq * 1000); + clk_set_parent(arm_src, pll_arm); + + /* change the cpu frequency */ + ret = clk_set_rate(arm_clk, new_freq * 1000); + if (ret) { + dev_err(cpu_dev, " failed to set clock rate: %d\n", ret); + regulator_set_voltage_tol(arm_reg, volt_old, 0); + return ret; + } + + /* scaling down? scaling voltage after frequency */ + if (new_freq < old_freq) { + ret = regulator_set_voltage_tol(arm_reg, volt, 0); + if (ret) { + dev_warn(cpu_dev, "failed to scale vddarm down: %d\n", ret); + ret = 0; + } + } + + return 0; +} + +static int imx7d_cpufreq_init(struct cpufreq_policy *policy) +{ + policy->clk = arm_clk; + policy->cur = clk_get_rate(arm_clk) / 1000; + + return cpufreq_generic_init(policy, freq_table, transition_latency); +} + +static struct cpufreq_driver imx7d_cpufreq_driver = { + .flags = CPUFREQ_NEED_INITIAL_FREQ_CHECK, + .verify = cpufreq_generic_frequency_table_verify, + .target_index = imx7d_set_target, + .get = cpufreq_generic_get, + .init = imx7d_cpufreq_init, + .name = "imx7d-cpufreq", + .attr = cpufreq_generic_attr, +}; + +static int imx7d_cpufreq_probe(struct platform_device *pdev) +{ + struct device_node *np; + struct dev_pm_opp *opp; + unsigned long min_volt, max_volt; + int num, ret; + + cpu_dev = get_cpu_device(0); + if (!cpu_dev) { + pr_err("failed to get cpu0 device\n"); + return -ENODEV; + } + + np = of_node_get(cpu_dev->of_node); + if (!np) { + dev_err(cpu_dev, "failed to find the cpu0 node\n"); + return -ENOENT; + } + + arm_clk = clk_get(cpu_dev, "arm"); + arm_src = clk_get(cpu_dev, "arm_root_src"); + pll_arm = clk_get(cpu_dev, "pll_arm"); + pll_sys_main = clk_get(cpu_dev, "pll_sys_main"); + + if (IS_ERR(arm_clk) || IS_ERR(arm_src) || IS_ERR(pll_arm) || + IS_ERR(pll_sys_main)) { + dev_err(cpu_dev, "failed to get clocks\n"); + ret = -ENOENT; + goto put_clk; + } + + arm_reg = devm_regulator_get(cpu_dev, "arm"); + if (IS_ERR(arm_reg)) { + ret = PTR_ERR(arm_reg); + if (ret != -EPROBE_DEFER) + dev_err(cpu_dev, "failed to get the regulator\n"); + + goto put_clk; + } + + /* We expect an OPP table supplied by platform. + * Just incase the platform did not supply the OPP + * table, it will try to get it. + */ + num = dev_pm_opp_get_opp_count(cpu_dev); + if (num < 0) { + ret = dev_pm_opp_of_add_table(cpu_dev); + if (ret < 0) { + dev_err(cpu_dev, "failed to init OPP table: %d\n", ret); + goto put_reg; + } + + free_opp = true; + + num = dev_pm_opp_get_opp_count(cpu_dev); + if (num < 0) { + ret = num; + dev_err(cpu_dev, "no OPP table is found: %d\n", ret); + goto out_free_opp; + } + } + + ret = dev_pm_opp_init_cpufreq_table(cpu_dev, &freq_table); + if (ret) { + dev_err(cpu_dev, "failed to init cpufreq table: %d\n", ret); + goto out_free_opp; + } + + if (of_property_read_u32(np, "clock-latency", &transition_latency)) + transition_latency = CPUFREQ_ETERNAL; + + /* OPP is maintained in order of increasing frequency, and + * freq_table initialized from OPP is therefore sorted in the + * same order + */ + rcu_read_lock(); + opp = dev_pm_opp_find_freq_exact(cpu_dev, + freq_table[0].frequency * 1000, true); + min_volt = dev_pm_opp_get_voltage(opp); + opp = dev_pm_opp_find_freq_exact(cpu_dev, + freq_table[--num].frequency * 1000, true); + max_volt = dev_pm_opp_get_voltage(opp); + rcu_read_unlock(); + ret = regulator_set_voltage_time(arm_reg, min_volt, max_volt); + if (ret > 0) + transition_latency += ret * 1000; + + ret = cpufreq_register_driver(&imx7d_cpufreq_driver); + if (ret) { + dev_err(cpu_dev, "failed register driver: %d\n", ret); + goto free_freq_table; + } + + of_node_put(np); + return 0; + +free_freq_table: + dev_pm_opp_free_cpufreq_table(cpu_dev, &freq_table); +out_free_opp: + if (free_opp) + dev_pm_opp_of_remove_table(cpu_dev); +put_reg: + if (!IS_ERR(arm_reg)) + regulator_put(arm_reg); +put_clk: + if (!IS_ERR(arm_clk)) + clk_put(arm_clk); + if (!IS_ERR(arm_src)) + clk_put(arm_src); + if (!IS_ERR(pll_arm)) + clk_put(pll_arm); + if (!IS_ERR(pll_sys_main)) + clk_put(pll_sys_main); + of_node_put(np); + + return ret; +} + +static int imx7d_cpufreq_remove(struct platform_device *pdev) +{ + cpufreq_unregister_driver(&imx7d_cpufreq_driver); + dev_pm_opp_free_cpufreq_table(cpu_dev, &freq_table); + dev_pm_opp_of_remove_table(cpu_dev); + + regulator_put(arm_reg); + + clk_put(arm_clk); + clk_put(arm_src); + clk_put(pll_arm); + clk_put(pll_sys_main); + + return 0; +} + +static struct platform_driver imx7d_cpufreq_platdrv = { + .driver = { + .name = "imx7d-cpufreq", + .owner = THIS_MODULE, + }, + .probe = imx7d_cpufreq_probe, + .remove = imx7d_cpufreq_remove, +}; + +module_platform_driver(imx7d_cpufreq_platdrv); + +MODULE_DESCRIPTION("Freescale i.MX7D cpufreq driver"); +MODULE_LICENSE("GPL");