linux/arch/arm/mach-imx/mach-imx6q.c
<<
>>
Prefs
   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 */
  24static 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 */
  47static 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}
  67DECLARE_PCI_FIXUP_EARLY(PCI_VENDOR_ID_PLX, 0x8609, ventana_pciesw_early_fixup);
  68DECLARE_PCI_FIXUP_EARLY(PCI_VENDOR_ID_PLX, 0x8606, ventana_pciesw_early_fixup);
  69DECLARE_PCI_FIXUP_EARLY(PCI_VENDOR_ID_PLX, 0x8604, ventana_pciesw_early_fixup);
  70
  71static 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
  79static 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);
 122put_ptp_clk:
 123        clk_put(ptp_clk);
 124put_node:
 125        of_node_put(np);
 126}
 127
 128static 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
 161static 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
 184static 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
 200static void __init imx6q_map_io(void)
 201{
 202        debug_ll_io_init();
 203        imx_scu_map_io();
 204}
 205
 206static 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
 216static const char * const imx6q_dt_compat[] __initconst = {
 217        "fsl,imx6dl",
 218        "fsl,imx6q",
 219        "fsl,imx6qp",
 220        NULL,
 221};
 222
 223DT_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,
 232MACHINE_END
 233