1 | // SPDX-License-Identifier: GPL-2.0-or-later |
2 | /* |
3 | * Copyright (C) 2013-2015 Freescale Semiconductor, Inc. |
4 | * Copyright 2017-2018 NXP. |
5 | */ |
6 | |
7 | #include <linux/err.h> |
8 | #include <linux/io.h> |
9 | #include <linux/of.h> |
10 | #include <linux/of_address.h> |
11 | #include <linux/mfd/syscon.h> |
12 | #include <linux/regmap.h> |
13 | #include "common.h" |
14 | #include "hardware.h" |
15 | |
16 | #define REG_SET 0x4 |
17 | #define REG_CLR 0x8 |
18 | |
19 | #define ANADIG_REG_2P5 0x130 |
20 | #define ANADIG_REG_CORE 0x140 |
21 | #define ANADIG_ANA_MISC0 0x150 |
22 | #define ANADIG_DIGPROG 0x260 |
23 | #define ANADIG_DIGPROG_IMX6SL 0x280 |
24 | #define ANADIG_DIGPROG_IMX7D 0x800 |
25 | |
26 | #define SRC_SBMR2 0x1c |
27 | |
28 | #define BM_ANADIG_REG_2P5_ENABLE_WEAK_LINREG 0x40000 |
29 | #define BM_ANADIG_REG_2P5_ENABLE_PULLDOWN 0x8 |
30 | #define BM_ANADIG_REG_CORE_FET_ODRIVE 0x20000000 |
31 | #define BM_ANADIG_ANA_MISC0_STOP_MODE_CONFIG 0x1000 |
32 | /* Below MISC0_DISCON_HIGH_SNVS is only for i.MX6SL */ |
33 | #define BM_ANADIG_ANA_MISC0_DISCON_HIGH_SNVS 0x2000 |
34 | |
35 | static struct regmap *anatop; |
36 | |
37 | static void imx_anatop_enable_weak2p5(bool enable) |
38 | { |
39 | u32 reg, val; |
40 | |
41 | regmap_read(map: anatop, ANADIG_ANA_MISC0, val: &val); |
42 | |
43 | /* can only be enabled when stop_mode_config is clear. */ |
44 | reg = ANADIG_REG_2P5; |
45 | reg += (enable && (val & BM_ANADIG_ANA_MISC0_STOP_MODE_CONFIG) == 0) ? |
46 | REG_SET : REG_CLR; |
47 | regmap_write(map: anatop, reg, BM_ANADIG_REG_2P5_ENABLE_WEAK_LINREG); |
48 | } |
49 | |
50 | static void imx_anatop_enable_fet_odrive(bool enable) |
51 | { |
52 | regmap_write(map: anatop, ANADIG_REG_CORE + (enable ? REG_SET : REG_CLR), |
53 | BM_ANADIG_REG_CORE_FET_ODRIVE); |
54 | } |
55 | |
56 | static inline void imx_anatop_enable_2p5_pulldown(bool enable) |
57 | { |
58 | regmap_write(map: anatop, ANADIG_REG_2P5 + (enable ? REG_SET : REG_CLR), |
59 | BM_ANADIG_REG_2P5_ENABLE_PULLDOWN); |
60 | } |
61 | |
62 | static inline void imx_anatop_disconnect_high_snvs(bool enable) |
63 | { |
64 | regmap_write(map: anatop, ANADIG_ANA_MISC0 + (enable ? REG_SET : REG_CLR), |
65 | BM_ANADIG_ANA_MISC0_DISCON_HIGH_SNVS); |
66 | } |
67 | |
68 | void imx_anatop_pre_suspend(void) |
69 | { |
70 | if (imx_mmdc_get_ddr_type() == IMX_DDR_TYPE_LPDDR2) |
71 | imx_anatop_enable_2p5_pulldown(enable: true); |
72 | else |
73 | imx_anatop_enable_weak2p5(enable: true); |
74 | |
75 | imx_anatop_enable_fet_odrive(enable: true); |
76 | |
77 | if (cpu_is_imx6sl()) |
78 | imx_anatop_disconnect_high_snvs(enable: true); |
79 | } |
80 | |
81 | void imx_anatop_post_resume(void) |
82 | { |
83 | if (imx_mmdc_get_ddr_type() == IMX_DDR_TYPE_LPDDR2) |
84 | imx_anatop_enable_2p5_pulldown(enable: false); |
85 | else |
86 | imx_anatop_enable_weak2p5(enable: false); |
87 | |
88 | imx_anatop_enable_fet_odrive(enable: false); |
89 | |
90 | if (cpu_is_imx6sl()) |
91 | imx_anatop_disconnect_high_snvs(enable: false); |
92 | } |
93 | |
94 | void __init imx_init_revision_from_anatop(void) |
95 | { |
96 | struct device_node *np, *src_np; |
97 | void __iomem *anatop_base; |
98 | unsigned int revision; |
99 | u32 digprog; |
100 | u16 offset = ANADIG_DIGPROG; |
101 | u8 major_part, minor_part; |
102 | |
103 | np = of_find_compatible_node(NULL, NULL, compat: "fsl,imx6q-anatop" ); |
104 | anatop_base = of_iomap(node: np, index: 0); |
105 | WARN_ON(!anatop_base); |
106 | if (of_device_is_compatible(device: np, "fsl,imx6sl-anatop" )) |
107 | offset = ANADIG_DIGPROG_IMX6SL; |
108 | if (of_device_is_compatible(device: np, "fsl,imx7d-anatop" )) |
109 | offset = ANADIG_DIGPROG_IMX7D; |
110 | digprog = readl_relaxed(anatop_base + offset); |
111 | iounmap(addr: anatop_base); |
112 | |
113 | /* |
114 | * On i.MX7D digprog value match linux version format, so |
115 | * it needn't map again and we can use register value directly. |
116 | */ |
117 | if (of_device_is_compatible(device: np, "fsl,imx7d-anatop" )) { |
118 | revision = digprog & 0xff; |
119 | } else { |
120 | /* |
121 | * MAJOR: [15:8], the major silicon revison; |
122 | * MINOR: [7: 0], the minor silicon revison; |
123 | * |
124 | * please refer to the i.MX RM for the detailed |
125 | * silicon revison bit define. |
126 | * format the major part and minor part to match the |
127 | * linux kernel soc version format. |
128 | */ |
129 | major_part = (digprog >> 8) & 0xf; |
130 | minor_part = digprog & 0xf; |
131 | revision = ((major_part + 1) << 4) | minor_part; |
132 | |
133 | if ((digprog >> 16) == MXC_CPU_IMX6ULL) { |
134 | void __iomem *src_base; |
135 | u32 sbmr2; |
136 | |
137 | src_np = of_find_compatible_node(NULL, NULL, |
138 | compat: "fsl,imx6ul-src" ); |
139 | src_base = of_iomap(node: src_np, index: 0); |
140 | of_node_put(node: src_np); |
141 | WARN_ON(!src_base); |
142 | sbmr2 = readl_relaxed(src_base + SRC_SBMR2); |
143 | iounmap(addr: src_base); |
144 | |
145 | /* src_sbmr2 bit 6 is to identify if it is i.MX6ULZ */ |
146 | if (sbmr2 & (1 << 6)) { |
147 | digprog &= ~(0xff << 16); |
148 | digprog |= (MXC_CPU_IMX6ULZ << 16); |
149 | } |
150 | } |
151 | } |
152 | of_node_put(node: np); |
153 | |
154 | mxc_set_cpu_type(type: digprog >> 16 & 0xff); |
155 | imx_set_soc_revision(rev: revision); |
156 | } |
157 | |
158 | void __init imx_anatop_init(void) |
159 | { |
160 | anatop = syscon_regmap_lookup_by_compatible(s: "fsl,imx6q-anatop" ); |
161 | if (IS_ERR(ptr: anatop)) |
162 | pr_err("%s: failed to find imx6q-anatop regmap!\n" , __func__); |
163 | } |
164 | |