Orange Pi5 kernel

Deprecated Linux kernel 5.10.110 for OrangePi 5/5B/5+ boards

3 Commits   0 Branches   0 Tags
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300   1) // SPDX-License-Identifier: GPL-2.0
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300   2) /*
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300   3)  * Copyright 2019 NXP
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300   4)  */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300   5) 
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300   6) #include <linux/module.h>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300   7) #include <linux/device.h>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300   8) #include <linux/of_device.h>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300   9) #include <linux/platform_device.h>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  10) #include <linux/devfreq.h>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  11) #include <linux/pm_opp.h>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  12) #include <linux/clk.h>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  13) #include <linux/clk-provider.h>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  14) #include <linux/arm-smccc.h>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  15) 
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  16) #define IMX_SIP_DDR_DVFS			0xc2000004
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  17) 
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  18) /* Query available frequencies. */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  19) #define IMX_SIP_DDR_DVFS_GET_FREQ_COUNT		0x10
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  20) #define IMX_SIP_DDR_DVFS_GET_FREQ_INFO		0x11
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  21) 
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  22) /*
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  23)  * This should be in a 1:1 mapping with devicetree OPPs but
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  24)  * firmware provides additional info.
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  25)  */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  26) struct imx8m_ddrc_freq {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  27) 	unsigned long rate;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  28) 	unsigned long smcarg;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  29) 	int dram_core_parent_index;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  30) 	int dram_alt_parent_index;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  31) 	int dram_apb_parent_index;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  32) };
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  33) 
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  34) /* Hardware limitation */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  35) #define IMX8M_DDRC_MAX_FREQ_COUNT 4
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  36) 
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  37) /*
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  38)  * i.MX8M DRAM Controller clocks have the following structure (abridged):
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  39)  *
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  40)  * +----------+       |\            +------+
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  41)  * | dram_pll |-------|M| dram_core |      |
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  42)  * +----------+       |U|---------->| D    |
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  43)  *                 /--|X|           |  D   |
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  44)  *   dram_alt_root |  |/            |   R  |
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  45)  *                 |                |    C |
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  46)  *            +---------+           |      |
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  47)  *            |FIX DIV/4|           |      |
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  48)  *            +---------+           |      |
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  49)  *  composite:     |                |      |
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  50)  * +----------+    |                |      |
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  51)  * | dram_alt |----/                |      |
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  52)  * +----------+                     |      |
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  53)  * | dram_apb |-------------------->|      |
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  54)  * +----------+                     +------+
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  55)  *
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  56)  * The dram_pll is used for higher rates and dram_alt is used for lower rates.
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  57)  *
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  58)  * Frequency switching is implemented in TF-A (via SMC call) and can change the
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  59)  * configuration of the clocks, including mux parents. The dram_alt and
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  60)  * dram_apb clocks are "imx composite" and their parent can change too.
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  61)  *
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  62)  * We need to prepare/enable the new mux parents head of switching and update
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  63)  * their information afterwards.
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  64)  */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  65) struct imx8m_ddrc {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  66) 	struct devfreq_dev_profile profile;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  67) 	struct devfreq *devfreq;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  68) 
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  69) 	/* For frequency switching: */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  70) 	struct clk *dram_core;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  71) 	struct clk *dram_pll;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  72) 	struct clk *dram_alt;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  73) 	struct clk *dram_apb;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  74) 
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  75) 	int freq_count;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  76) 	struct imx8m_ddrc_freq freq_table[IMX8M_DDRC_MAX_FREQ_COUNT];
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  77) };
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  78) 
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  79) static struct imx8m_ddrc_freq *imx8m_ddrc_find_freq(struct imx8m_ddrc *priv,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  80) 						    unsigned long rate)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  81) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  82) 	struct imx8m_ddrc_freq *freq;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  83) 	int i;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  84) 
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  85) 	/*
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  86) 	 * Firmware reports values in MT/s, so we round-down from Hz
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  87) 	 * Rounding is extra generous to ensure a match.
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  88) 	 */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  89) 	rate = DIV_ROUND_CLOSEST(rate, 250000);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  90) 	for (i = 0; i < priv->freq_count; ++i) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  91) 		freq = &priv->freq_table[i];
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  92) 		if (freq->rate == rate ||
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  93) 				freq->rate + 1 == rate ||
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  94) 				freq->rate - 1 == rate)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  95) 			return freq;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  96) 	}
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  97) 
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  98) 	return NULL;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  99) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 100) 
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 101) static void imx8m_ddrc_smc_set_freq(int target_freq)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 102) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 103) 	struct arm_smccc_res res;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 104) 	u32 online_cpus = 0;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 105) 	int cpu;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 106) 
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 107) 	local_irq_disable();
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 108) 
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 109) 	for_each_online_cpu(cpu)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 110) 		online_cpus |= (1 << (cpu * 8));
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 111) 
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 112) 	/* change the ddr freqency */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 113) 	arm_smccc_smc(IMX_SIP_DDR_DVFS, target_freq, online_cpus,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 114) 			0, 0, 0, 0, 0, &res);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 115) 
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 116) 	local_irq_enable();
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 117) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 118) 
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 119) static struct clk *clk_get_parent_by_index(struct clk *clk, int index)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 120) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 121) 	struct clk_hw *hw;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 122) 
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 123) 	hw = clk_hw_get_parent_by_index(__clk_get_hw(clk), index);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 124) 
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 125) 	return hw ? hw->clk : NULL;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 126) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 127) 
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 128) static int imx8m_ddrc_set_freq(struct device *dev, struct imx8m_ddrc_freq *freq)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 129) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 130) 	struct imx8m_ddrc *priv = dev_get_drvdata(dev);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 131) 	struct clk *new_dram_core_parent;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 132) 	struct clk *new_dram_alt_parent;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 133) 	struct clk *new_dram_apb_parent;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 134) 	int ret;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 135) 
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 136) 	/*
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 137) 	 * Fetch new parents
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 138) 	 *
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 139) 	 * new_dram_alt_parent and new_dram_apb_parent are optional but
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 140) 	 * new_dram_core_parent is not.
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 141) 	 */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 142) 	new_dram_core_parent = clk_get_parent_by_index(
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 143) 			priv->dram_core, freq->dram_core_parent_index - 1);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 144) 	if (!new_dram_core_parent) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 145) 		dev_err(dev, "failed to fetch new dram_core parent\n");
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 146) 		return -EINVAL;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 147) 	}
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 148) 	if (freq->dram_alt_parent_index) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 149) 		new_dram_alt_parent = clk_get_parent_by_index(
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 150) 				priv->dram_alt,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 151) 				freq->dram_alt_parent_index - 1);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 152) 		if (!new_dram_alt_parent) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 153) 			dev_err(dev, "failed to fetch new dram_alt parent\n");
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 154) 			return -EINVAL;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 155) 		}
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 156) 	} else
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 157) 		new_dram_alt_parent = NULL;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 158) 
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 159) 	if (freq->dram_apb_parent_index) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 160) 		new_dram_apb_parent = clk_get_parent_by_index(
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 161) 				priv->dram_apb,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 162) 				freq->dram_apb_parent_index - 1);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 163) 		if (!new_dram_apb_parent) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 164) 			dev_err(dev, "failed to fetch new dram_apb parent\n");
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 165) 			return -EINVAL;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 166) 		}
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 167) 	} else
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 168) 		new_dram_apb_parent = NULL;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 169) 
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 170) 	/* increase reference counts and ensure clks are ON before switch */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 171) 	ret = clk_prepare_enable(new_dram_core_parent);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 172) 	if (ret) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 173) 		dev_err(dev, "failed to enable new dram_core parent: %d\n",
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 174) 			ret);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 175) 		goto out;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 176) 	}
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 177) 	ret = clk_prepare_enable(new_dram_alt_parent);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 178) 	if (ret) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 179) 		dev_err(dev, "failed to enable new dram_alt parent: %d\n",
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 180) 			ret);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 181) 		goto out_disable_core_parent;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 182) 	}
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 183) 	ret = clk_prepare_enable(new_dram_apb_parent);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 184) 	if (ret) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 185) 		dev_err(dev, "failed to enable new dram_apb parent: %d\n",
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 186) 			ret);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 187) 		goto out_disable_alt_parent;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 188) 	}
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 189) 
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 190) 	imx8m_ddrc_smc_set_freq(freq->smcarg);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 191) 
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 192) 	/* update parents in clk tree after switch. */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 193) 	ret = clk_set_parent(priv->dram_core, new_dram_core_parent);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 194) 	if (ret)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 195) 		dev_warn(dev, "failed to set dram_core parent: %d\n", ret);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 196) 	if (new_dram_alt_parent) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 197) 		ret = clk_set_parent(priv->dram_alt, new_dram_alt_parent);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 198) 		if (ret)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 199) 			dev_warn(dev, "failed to set dram_alt parent: %d\n",
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 200) 				 ret);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 201) 	}
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 202) 	if (new_dram_apb_parent) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 203) 		ret = clk_set_parent(priv->dram_apb, new_dram_apb_parent);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 204) 		if (ret)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 205) 			dev_warn(dev, "failed to set dram_apb parent: %d\n",
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 206) 				 ret);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 207) 	}
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 208) 
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 209) 	/*
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 210) 	 * Explicitly refresh dram PLL rate.
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 211) 	 *
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 212) 	 * Even if it's marked with CLK_GET_RATE_NOCACHE the rate will not be
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 213) 	 * automatically refreshed when clk_get_rate is called on children.
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 214) 	 */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 215) 	clk_get_rate(priv->dram_pll);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 216) 
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 217) 	/*
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 218) 	 * clk_set_parent transfer the reference count from old parent.
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 219) 	 * now we drop extra reference counts used during the switch
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 220) 	 */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 221) 	clk_disable_unprepare(new_dram_apb_parent);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 222) out_disable_alt_parent:
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 223) 	clk_disable_unprepare(new_dram_alt_parent);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 224) out_disable_core_parent:
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 225) 	clk_disable_unprepare(new_dram_core_parent);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 226) out:
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 227) 	return ret;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 228) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 229) 
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 230) static int imx8m_ddrc_target(struct device *dev, unsigned long *freq, u32 flags)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 231) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 232) 	struct imx8m_ddrc *priv = dev_get_drvdata(dev);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 233) 	struct imx8m_ddrc_freq *freq_info;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 234) 	struct dev_pm_opp *new_opp;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 235) 	unsigned long old_freq, new_freq;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 236) 	int ret;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 237) 
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 238) 	new_opp = devfreq_recommended_opp(dev, freq, flags);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 239) 	if (IS_ERR(new_opp)) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 240) 		ret = PTR_ERR(new_opp);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 241) 		dev_err(dev, "failed to get recommended opp: %d\n", ret);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 242) 		return ret;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 243) 	}
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 244) 	dev_pm_opp_put(new_opp);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 245) 
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 246) 	old_freq = clk_get_rate(priv->dram_core);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 247) 	if (*freq == old_freq)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 248) 		return 0;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 249) 
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 250) 	freq_info = imx8m_ddrc_find_freq(priv, *freq);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 251) 	if (!freq_info)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 252) 		return -EINVAL;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 253) 
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 254) 	/*
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 255) 	 * Read back the clk rate to verify switch was correct and so that
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 256) 	 * we can report it on all error paths.
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 257) 	 */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 258) 	ret = imx8m_ddrc_set_freq(dev, freq_info);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 259) 
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 260) 	new_freq = clk_get_rate(priv->dram_core);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 261) 	if (ret)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 262) 		dev_err(dev, "ddrc failed freq switch to %lu from %lu: error %d. now at %lu\n",
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 263) 			*freq, old_freq, ret, new_freq);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 264) 	else if (*freq != new_freq)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 265) 		dev_err(dev, "ddrc failed freq update to %lu from %lu, now at %lu\n",
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 266) 			*freq, old_freq, new_freq);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 267) 	else
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 268) 		dev_dbg(dev, "ddrc freq set to %lu (was %lu)\n",
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 269) 			*freq, old_freq);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 270) 
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 271) 	return ret;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 272) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 273) 
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 274) static int imx8m_ddrc_get_cur_freq(struct device *dev, unsigned long *freq)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 275) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 276) 	struct imx8m_ddrc *priv = dev_get_drvdata(dev);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 277) 
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 278) 	*freq = clk_get_rate(priv->dram_core);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 279) 
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 280) 	return 0;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 281) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 282) 
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 283) static int imx8m_ddrc_get_dev_status(struct device *dev,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 284) 				     struct devfreq_dev_status *stat)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 285) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 286) 	struct imx8m_ddrc *priv = dev_get_drvdata(dev);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 287) 
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 288) 	stat->busy_time = 0;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 289) 	stat->total_time = 0;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 290) 	stat->current_frequency = clk_get_rate(priv->dram_core);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 291) 
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 292) 	return 0;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 293) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 294) 
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 295) static int imx8m_ddrc_init_freq_info(struct device *dev)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 296) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 297) 	struct imx8m_ddrc *priv = dev_get_drvdata(dev);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 298) 	struct arm_smccc_res res;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 299) 	int index;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 300) 
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 301) 	/* An error here means DDR DVFS API not supported by firmware */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 302) 	arm_smccc_smc(IMX_SIP_DDR_DVFS, IMX_SIP_DDR_DVFS_GET_FREQ_COUNT,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 303) 			0, 0, 0, 0, 0, 0, &res);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 304) 	priv->freq_count = res.a0;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 305) 	if (priv->freq_count <= 0 ||
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 306) 			priv->freq_count > IMX8M_DDRC_MAX_FREQ_COUNT)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 307) 		return -ENODEV;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 308) 
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 309) 	for (index = 0; index < priv->freq_count; ++index) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 310) 		struct imx8m_ddrc_freq *freq = &priv->freq_table[index];
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 311) 
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 312) 		arm_smccc_smc(IMX_SIP_DDR_DVFS, IMX_SIP_DDR_DVFS_GET_FREQ_INFO,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 313) 			      index, 0, 0, 0, 0, 0, &res);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 314) 		/* Result should be strictly positive */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 315) 		if ((long)res.a0 <= 0)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 316) 			return -ENODEV;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 317) 
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 318) 		freq->rate = res.a0;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 319) 		freq->smcarg = index;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 320) 		freq->dram_core_parent_index = res.a1;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 321) 		freq->dram_alt_parent_index = res.a2;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 322) 		freq->dram_apb_parent_index = res.a3;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 323) 
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 324) 		/* dram_core has 2 options: dram_pll or dram_alt_root */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 325) 		if (freq->dram_core_parent_index != 1 &&
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 326) 				freq->dram_core_parent_index != 2)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 327) 			return -ENODEV;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 328) 		/* dram_apb and dram_alt have exactly 8 possible parents */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 329) 		if (freq->dram_alt_parent_index > 8 ||
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 330) 				freq->dram_apb_parent_index > 8)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 331) 			return -ENODEV;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 332) 		/* dram_core from alt requires explicit dram_alt parent */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 333) 		if (freq->dram_core_parent_index == 2 &&
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 334) 				freq->dram_alt_parent_index == 0)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 335) 			return -ENODEV;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 336) 	}
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 337) 
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 338) 	return 0;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 339) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 340) 
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 341) static int imx8m_ddrc_check_opps(struct device *dev)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 342) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 343) 	struct imx8m_ddrc *priv = dev_get_drvdata(dev);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 344) 	struct imx8m_ddrc_freq *freq_info;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 345) 	struct dev_pm_opp *opp;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 346) 	unsigned long freq;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 347) 	int i, opp_count;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 348) 
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 349) 	/* Enumerate DT OPPs and disable those not supported by firmware */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 350) 	opp_count = dev_pm_opp_get_opp_count(dev);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 351) 	if (opp_count < 0)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 352) 		return opp_count;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 353) 	for (i = 0, freq = 0; i < opp_count; ++i, ++freq) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 354) 		opp = dev_pm_opp_find_freq_ceil(dev, &freq);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 355) 		if (IS_ERR(opp)) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 356) 			dev_err(dev, "Failed enumerating OPPs: %ld\n",
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 357) 				PTR_ERR(opp));
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 358) 			return PTR_ERR(opp);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 359) 		}
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 360) 		dev_pm_opp_put(opp);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 361) 
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 362) 		freq_info = imx8m_ddrc_find_freq(priv, freq);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 363) 		if (!freq_info) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 364) 			dev_info(dev, "Disable unsupported OPP %luHz %luMT/s\n",
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 365) 					freq, DIV_ROUND_CLOSEST(freq, 250000));
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 366) 			dev_pm_opp_disable(dev, freq);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 367) 		}
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 368) 	}
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 369) 
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 370) 	return 0;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 371) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 372) 
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 373) static void imx8m_ddrc_exit(struct device *dev)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 374) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 375) 	dev_pm_opp_of_remove_table(dev);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 376) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 377) 
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 378) static int imx8m_ddrc_probe(struct platform_device *pdev)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 379) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 380) 	struct device *dev = &pdev->dev;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 381) 	struct imx8m_ddrc *priv;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 382) 	const char *gov = DEVFREQ_GOV_USERSPACE;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 383) 	int ret;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 384) 
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 385) 	priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 386) 	if (!priv)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 387) 		return -ENOMEM;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 388) 
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 389) 	platform_set_drvdata(pdev, priv);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 390) 
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 391) 	ret = imx8m_ddrc_init_freq_info(dev);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 392) 	if (ret) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 393) 		dev_err(dev, "failed to init firmware freq info: %d\n", ret);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 394) 		return ret;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 395) 	}
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 396) 
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 397) 	priv->dram_core = devm_clk_get(dev, "core");
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 398) 	if (IS_ERR(priv->dram_core)) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 399) 		ret = PTR_ERR(priv->dram_core);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 400) 		dev_err(dev, "failed to fetch core clock: %d\n", ret);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 401) 		return ret;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 402) 	}
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 403) 	priv->dram_pll = devm_clk_get(dev, "pll");
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 404) 	if (IS_ERR(priv->dram_pll)) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 405) 		ret = PTR_ERR(priv->dram_pll);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 406) 		dev_err(dev, "failed to fetch pll clock: %d\n", ret);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 407) 		return ret;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 408) 	}
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 409) 	priv->dram_alt = devm_clk_get(dev, "alt");
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 410) 	if (IS_ERR(priv->dram_alt)) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 411) 		ret = PTR_ERR(priv->dram_alt);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 412) 		dev_err(dev, "failed to fetch alt clock: %d\n", ret);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 413) 		return ret;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 414) 	}
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 415) 	priv->dram_apb = devm_clk_get(dev, "apb");
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 416) 	if (IS_ERR(priv->dram_apb)) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 417) 		ret = PTR_ERR(priv->dram_apb);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 418) 		dev_err(dev, "failed to fetch apb clock: %d\n", ret);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 419) 		return ret;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 420) 	}
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 421) 
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 422) 	ret = dev_pm_opp_of_add_table(dev);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 423) 	if (ret < 0) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 424) 		dev_err(dev, "failed to get OPP table\n");
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 425) 		return ret;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 426) 	}
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 427) 
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 428) 	ret = imx8m_ddrc_check_opps(dev);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 429) 	if (ret < 0)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 430) 		goto err;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 431) 
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 432) 	priv->profile.polling_ms = 1000;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 433) 	priv->profile.target = imx8m_ddrc_target;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 434) 	priv->profile.get_dev_status = imx8m_ddrc_get_dev_status;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 435) 	priv->profile.exit = imx8m_ddrc_exit;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 436) 	priv->profile.get_cur_freq = imx8m_ddrc_get_cur_freq;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 437) 	priv->profile.initial_freq = clk_get_rate(priv->dram_core);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 438) 
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 439) 	priv->devfreq = devm_devfreq_add_device(dev, &priv->profile,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 440) 						gov, NULL);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 441) 	if (IS_ERR(priv->devfreq)) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 442) 		ret = PTR_ERR(priv->devfreq);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 443) 		dev_err(dev, "failed to add devfreq device: %d\n", ret);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 444) 		goto err;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 445) 	}
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 446) 
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 447) 	return 0;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 448) 
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 449) err:
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 450) 	dev_pm_opp_of_remove_table(dev);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 451) 	return ret;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 452) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 453) 
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 454) static const struct of_device_id imx8m_ddrc_of_match[] = {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 455) 	{ .compatible = "fsl,imx8m-ddrc", },
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 456) 	{ /* sentinel */ },
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 457) };
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 458) MODULE_DEVICE_TABLE(of, imx8m_ddrc_of_match);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 459) 
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 460) static struct platform_driver imx8m_ddrc_platdrv = {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 461) 	.probe		= imx8m_ddrc_probe,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 462) 	.driver = {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 463) 		.name	= "imx8m-ddrc-devfreq",
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 464) 		.of_match_table = of_match_ptr(imx8m_ddrc_of_match),
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 465) 	},
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 466) };
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 467) module_platform_driver(imx8m_ddrc_platdrv);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 468) 
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 469) MODULE_DESCRIPTION("i.MX8M DDR Controller frequency driver");
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 470) MODULE_AUTHOR("Leonard Crestez <leonard.crestez@nxp.com>");
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 471) MODULE_LICENSE("GPL v2");