1 // SPDX-License-Identifier: GPL-2.0-or-later 2 /* 3 * Copyright 2011-2013 Freescale Semiconductor, Inc. 4 * Copyright 2011 Linaro Ltd. 5 */ 6 7 #include <linux/clk.h> 8 #include <linux/irqchip.h> 9 #include <linux/of_platform.h> 10 #include <linux/pci.h> 11 #include <linux/phy.h> 12 #include <linux/regmap.h> 13 #include <linux/micrel_phy.h> 14 #include <linux/mfd/syscon.h> 15 #include <linux/mfd/syscon/imx6q-iomuxc-gpr.h> 16 #include <asm/mach/arch.h> 17 #include <asm/mach/map.h> 18 19 #include "common.h" 20 #include "cpuidle.h" 21 #include "hardware.h" 22 23 /* For imx6q sabrelite board: set KSZ9021RN RGMII pad skew */ 24 static int ksz9021rn_phy_fixup(struct phy_device *phydev) 25 { 26 if (IS_BUILTIN(CONFIG_PHYLIB)) { 27 /* min rx data delay */ 28 phy_write(phydev, MICREL_KSZ9021_EXTREG_CTRL, 29 0x8000 | MICREL_KSZ9021_RGMII_RX_DATA_PAD_SCEW); 30 phy_write(phydev, MICREL_KSZ9021_EXTREG_DATA_WRITE, 0x0000); 31 32 /* max rx/tx clock delay, min rx/tx control delay */ 33 phy_write(phydev, MICREL_KSZ9021_EXTREG_CTRL, 34 0x8000 | MICREL_KSZ9021_RGMII_CLK_CTRL_PAD_SCEW); 35 phy_write(phydev, MICREL_KSZ9021_EXTREG_DATA_WRITE, 0xf0f0); 36 phy_write(phydev, MICREL_KSZ9021_EXTREG_CTRL, 37 MICREL_KSZ9021_RGMII_CLK_CTRL_PAD_SCEW); 38 } 39 40 return 0; 41 } 42 43 /* 44 * fixup for PLX PEX8909 bridge to configure GPIO1-7 as output High 45 * as they are used for slots1-7 PERST# 46 */ 47 static void ventana_pciesw_early_fixup(struct pci_dev *dev) 48 { 49 u32 dw; 50 51 if (!of_machine_is_compatible("gw,ventana")) 52 return; 53 54 if (dev->devfn != 0) 55 return; 56 57 pci_read_config_dword(dev, 0x62c, &dw); 58 dw |= 0xaaa8; // GPIO1-7 outputs 59 pci_write_config_dword(dev, 0x62c, dw); 60 61 pci_read_config_dword(dev, 0x644, &dw); 62 dw |= 0xfe; // GPIO1-7 output high 63 pci_write_config_dword(dev, 0x644, dw); 64 65 msleep(100); 66 } 67 DECLARE_PCI_FIXUP_EARLY(PCI_VENDOR_ID_PLX, 0x8609, ventana_pciesw_early_fixup); 68 DECLARE_PCI_FIXUP_EARLY(PCI_VENDOR_ID_PLX, 0x8606, ventana_pciesw_early_fixup); 69 DECLARE_PCI_FIXUP_EARLY(PCI_VENDOR_ID_PLX, 0x8604, ventana_pciesw_early_fixup); 70 71 static void __init imx6q_enet_phy_init(void) 72 { 73 if (IS_BUILTIN(CONFIG_PHYLIB)) { 74 phy_register_fixup_for_uid(PHY_ID_KSZ9021, MICREL_PHY_ID_MASK, 75 ksz9021rn_phy_fixup); 76 } 77 } 78 79 static void __init imx6q_1588_init(void) 80 { 81 struct device_node *np; 82 struct clk *ptp_clk; 83 struct clk *enet_ref; 84 struct regmap *gpr; 85 u32 clksel; 86 87 np = of_find_compatible_node(NULL, NULL, "fsl,imx6q-fec"); 88 if (!np) { 89 pr_warn("%s: failed to find fec node\n", __func__); 90 return; 91 } 92 93 ptp_clk = of_clk_get(np, 2); 94 if (IS_ERR(ptp_clk)) { 95 pr_warn("%s: failed to get ptp clock\n", __func__); 96 goto put_node; 97 } 98 99 enet_ref = clk_get_sys(NULL, "enet_ref"); 100 if (IS_ERR(enet_ref)) { 101 pr_warn("%s: failed to get enet clock\n", __func__); 102 goto put_ptp_clk; 103 } 104 105 /* 106 * If enet_ref from ANATOP/CCM is the PTP clock source, we need to 107 * set bit IOMUXC_GPR1[21]. Or the PTP clock must be from pad 108 * (external OSC), and we need to clear the bit. 109 */ 110 clksel = clk_is_match(ptp_clk, enet_ref) ? 111 IMX6Q_GPR1_ENET_CLK_SEL_ANATOP : 112 IMX6Q_GPR1_ENET_CLK_SEL_PAD; 113 gpr = syscon_regmap_lookup_by_compatible("fsl,imx6q-iomuxc-gpr"); 114 if (!IS_ERR(gpr)) 115 regmap_update_bits(gpr, IOMUXC_GPR1, 116 IMX6Q_GPR1_ENET_CLK_SEL_MASK, 117 clksel); 118 else 119 pr_err("failed to find fsl,imx6q-iomuxc-gpr regmap\n"); 120 121 clk_put(enet_ref); 122 put_ptp_clk: 123 clk_put(ptp_clk); 124 put_node: 125 of_node_put(np); 126 } 127 128 static void __init imx6q_axi_init(void) 129 { 130 struct regmap *gpr; 131 unsigned int mask; 132 133 gpr = syscon_regmap_lookup_by_compatible("fsl,imx6q-iomuxc-gpr"); 134 if (!IS_ERR(gpr)) { 135 /* 136 * Enable the cacheable attribute of VPU and IPU 137 * AXI transactions. 138 */ 139 mask = IMX6Q_GPR4_VPU_WR_CACHE_SEL | 140 IMX6Q_GPR4_VPU_RD_CACHE_SEL | 141 IMX6Q_GPR4_VPU_P_WR_CACHE_VAL | 142 IMX6Q_GPR4_VPU_P_RD_CACHE_VAL_MASK | 143 IMX6Q_GPR4_IPU_WR_CACHE_CTL | 144 IMX6Q_GPR4_IPU_RD_CACHE_CTL; 145 regmap_update_bits(gpr, IOMUXC_GPR4, mask, mask); 146 147 /* Increase IPU read QoS priority */ 148 regmap_update_bits(gpr, IOMUXC_GPR6, 149 IMX6Q_GPR6_IPU1_ID00_RD_QOS_MASK | 150 IMX6Q_GPR6_IPU1_ID01_RD_QOS_MASK, 151 (0xf << 16) | (0x7 << 20)); 152 regmap_update_bits(gpr, IOMUXC_GPR7, 153 IMX6Q_GPR7_IPU2_ID00_RD_QOS_MASK | 154 IMX6Q_GPR7_IPU2_ID01_RD_QOS_MASK, 155 (0xf << 16) | (0x7 << 20)); 156 } else { 157 pr_warn("failed to find fsl,imx6q-iomuxc-gpr regmap\n"); 158 } 159 } 160 161 static void __init imx6q_init_machine(void) 162 { 163 if (cpu_is_imx6q() && imx_get_soc_revision() >= IMX_CHIP_REVISION_2_0) 164 /* 165 * SoCs that identify as i.MX6Q >= rev 2.0 are really i.MX6QP. 166 * Quirk: i.MX6QP revision = i.MX6Q revision - (1, 0), 167 * e.g. i.MX6QP rev 1.1 identifies as i.MX6Q rev 2.1. 168 */ 169 imx_print_silicon_rev("i.MX6QP", imx_get_soc_revision() - 0x10); 170 else 171 imx_print_silicon_rev(cpu_is_imx6dl() ? "i.MX6DL" : "i.MX6Q", 172 imx_get_soc_revision()); 173 174 imx6q_enet_phy_init(); 175 176 of_platform_default_populate(NULL, NULL, NULL); 177 178 imx_anatop_init(); 179 cpu_is_imx6q() ? imx6q_pm_init() : imx6dl_pm_init(); 180 imx6q_1588_init(); 181 imx6q_axi_init(); 182 } 183 184 static void __init imx6q_init_late(void) 185 { 186 /* 187 * WAIT mode is broken on imx6 Dual/Quad revision 1.0 and 1.1 so 188 * there is no point to run cpuidle on them. 189 * 190 * It does work on imx6 Solo/DualLite starting from 1.1 191 */ 192 if ((cpu_is_imx6q() && imx_get_soc_revision() > IMX_CHIP_REVISION_1_1) || 193 (cpu_is_imx6dl() && imx_get_soc_revision() > IMX_CHIP_REVISION_1_0)) 194 imx6q_cpuidle_init(); 195 196 if (IS_ENABLED(CONFIG_ARM_IMX6Q_CPUFREQ)) 197 platform_device_register_simple("imx6q-cpufreq", -1, NULL, 0); 198 } 199 200 static void __init imx6q_map_io(void) 201 { 202 debug_ll_io_init(); 203 imx_scu_map_io(); 204 } 205 206 static void __init imx6q_init_irq(void) 207 { 208 imx_gpc_check_dt(); 209 imx_init_revision_from_anatop(); 210 imx_init_l2cache(); 211 imx_src_init(); 212 irqchip_init(); 213 imx6_pm_ccm_init("fsl,imx6q-ccm"); 214 } 215 216 static const char * const imx6q_dt_compat[] __initconst = { 217 "fsl,imx6dl", 218 "fsl,imx6q", 219 "fsl,imx6qp", 220 NULL, 221 }; 222 223 DT_MACHINE_START(IMX6Q, "Freescale i.MX6 Quad/DualLite (Device Tree)") 224 .l2c_aux_val = 0, 225 .l2c_aux_mask = ~0, 226 .smp = smp_ops(imx_smp_ops), 227 .map_io = imx6q_map_io, 228 .init_irq = imx6q_init_irq, 229 .init_machine = imx6q_init_machine, 230 .init_late = imx6q_init_late, 231 .dt_compat = imx6q_dt_compat, 232 MACHINE_END 233