linux/arch/arm/mach-imx/mach-imx6q.c
Linus Torvalds 8b3c8ba3d8 ARM: SoC late changes for v4.1
We were expecting to sit on this branch through most of the merge window since
 the contents was merged into our tree late, but we ended up sitting on all of
 our contents so it can go in with the rest.
 
 The contents here is:
 
 - A large branch of cleanups of the CM/PRM blocks on OMAP.
 - A couple of patches plumbing up CM/PRM on OMAP5 and DRA7.
 - A branch with DT updates for Freescale i.MX. including some shuffling from
   .dts to .dtsi (include) files that causes a little churn.
 -----BEGIN PGP SIGNATURE-----
 Version: GnuPG v1
 
 iQIcBAABAgAGBQJVNy2KAAoJEIwa5zzehBx3mE4P/A+Wk2ElKl0FH6Kz4Wmt5MOY
 GPbIyd6jXTN/zbyAQxdPEQM7VvBh6GfgGTmNmcmfv2kacGJveAc7+UV0GSfW0XEO
 haOIwSRvfCIW1d2pyphrFlRqzQsDDzJkVuiRo1DkFwICyKEabXNqGu1zjNaLmN3j
 zw1DiAhe9ymywxayT5GBMevKU2a16Jgbzie6UfKKI5YO8Nqug13YI1as7n9SKrU4
 wdi5b7kecgcfVlmYUrN9iqKg3oKTqRNSZDk/WsGvO/L5Mks0Xoc9v/K6rifNUMdd
 CoigEznE1xgDvwPbAeXn4JiF/+JLVnDTZorsINQFIIAzHa2cZM1fMjT3x56IT0Y0
 iIU3uWh8B/L2/qTPsqEBDFd/lBX/E3cND7lCIWCU0vwGWRzAh/Q+vRwdFfLoMOXh
 npcw0hGS4KEWJ0sEX0xU9EvBUa5fb/CXT2xWBPVMV1Wb1QZLcquBRxFFNgh+GK2X
 nmoZFiqfJDQWrMoNySo+MGyBzIYLtwxkRF0rsUvJ47cW2/+KXSHflTgllvEpQ/38
 Ew3QmzCPlFuP7G1xiim9zSGvKIYhWV1fRUix1+FIE+on2d0TmdhqISHzCVU6ePxB
 MZC8GwUww57i0hXXgirgrlN6moKaUC1DN7AwNrHQsJIi8aFXuFWbZAufRrV36Kwg
 zsADWvSeOSWwea04MtkL
 =ssbK
 -----END PGP SIGNATURE-----

Merge tag 'armsoc-late' of git://git.kernel.org/pub/scm/linux/kernel/git/arm/arm-soc

Pull ARM SoC late changes from Olof Johansson:
 "We were expecting to sit on this branch through most of the merge
  window since the contents was merged into our tree late, but we ended
  up sitting on all of our contents so it can go in with the rest.

  The contents here is:

   - a large branch of cleanups of the CM/PRM blocks on OMAP.

   - a couple of patches plumbing up CM/PRM on OMAP5 and DRA7.

   - a branch with DT updates for Freescale i.MX.  including some
     shuffling from .dts to .dtsi (include) files that causes a little
     churn"

* tag 'armsoc-late' of git://git.kernel.org/pub/scm/linux/kernel/git/arm/arm-soc: (78 commits)
  ARM: OMAP2+: Fix booting with configs that don't have MFD_SYSCON
  ARM: OMAP4+: control: add support for initializing control module via DT
  ARM: dts: dra7: add minimal l4 bus layout with control module support
  ARM: dts: omap5: add minimal l4 bus layout with control module support
  ARM: OMAP4+: control: remove support for legacy pad read/write
  ARM: OMAP4: display: convert display to use syscon for dsi muxing
  ARM: dts: omap4: add minimal l4 bus layout with control module support
  ARM: dts: am4372: add minimal l4 bus layout with control module support
  ARM: dts: am43xx-epos-evm: fix pinmux node layout
  ARM: dts: am33xx: add minimal l4 bus layout with control module support
  ARM: dts: omap3: add minimal l4 bus layout with control module support
  ARM: dts: omap24xx: add minimal l4 bus layout with control module support
  ARM: OMAP2+: control: add syscon support for register accesses
  ARM: OMAP2+: id: cache omap_type value
  ARM: OMAP2+: control: remove API for getting control module base address
  ARM: OMAP2+: clock: add low-level support for regmap
  ARM: OMAP4+: PRM: get rid of cpu_is_omap44xx calls from interrupt init
  ARM: OMAP4+: PRM: setup prm_features from the PRM init time flags
  ARM: OMAP2+: CM: move SoC specific init calls within a generic API
  ARM: OMAP4+: PRM: determine prm_device_inst based on DT compatibility
  ...
2015-04-22 09:24:55 -07:00

412 lines
9.9 KiB
C

/*
* Copyright 2011-2013 Freescale Semiconductor, Inc.
* Copyright 2011 Linaro Ltd.
*
* The code contained herein is licensed under the GNU General Public
* License. You may obtain a copy of the GNU General Public License
* Version 2 or later at the following locations:
*
* http://www.opensource.org/licenses/gpl-license.html
* http://www.gnu.org/copyleft/gpl.html
*/
#include <linux/clk.h>
#include <linux/clkdev.h>
#include <linux/cpu.h>
#include <linux/delay.h>
#include <linux/export.h>
#include <linux/init.h>
#include <linux/io.h>
#include <linux/irq.h>
#include <linux/irqchip.h>
#include <linux/of.h>
#include <linux/of_address.h>
#include <linux/of_irq.h>
#include <linux/of_platform.h>
#include <linux/pm_opp.h>
#include <linux/pci.h>
#include <linux/phy.h>
#include <linux/reboot.h>
#include <linux/regmap.h>
#include <linux/micrel_phy.h>
#include <linux/mfd/syscon.h>
#include <linux/mfd/syscon/imx6q-iomuxc-gpr.h>
#include <asm/mach/arch.h>
#include <asm/mach/map.h>
#include <asm/system_misc.h>
#include "common.h"
#include "cpuidle.h"
#include "hardware.h"
/* For imx6q sabrelite board: set KSZ9021RN RGMII pad skew */
static int ksz9021rn_phy_fixup(struct phy_device *phydev)
{
if (IS_BUILTIN(CONFIG_PHYLIB)) {
/* min rx data delay */
phy_write(phydev, MICREL_KSZ9021_EXTREG_CTRL,
0x8000 | MICREL_KSZ9021_RGMII_RX_DATA_PAD_SCEW);
phy_write(phydev, MICREL_KSZ9021_EXTREG_DATA_WRITE, 0x0000);
/* max rx/tx clock delay, min rx/tx control delay */
phy_write(phydev, MICREL_KSZ9021_EXTREG_CTRL,
0x8000 | MICREL_KSZ9021_RGMII_CLK_CTRL_PAD_SCEW);
phy_write(phydev, MICREL_KSZ9021_EXTREG_DATA_WRITE, 0xf0f0);
phy_write(phydev, MICREL_KSZ9021_EXTREG_CTRL,
MICREL_KSZ9021_RGMII_CLK_CTRL_PAD_SCEW);
}
return 0;
}
static void mmd_write_reg(struct phy_device *dev, int device, int reg, int val)
{
phy_write(dev, 0x0d, device);
phy_write(dev, 0x0e, reg);
phy_write(dev, 0x0d, (1 << 14) | device);
phy_write(dev, 0x0e, val);
}
static int ksz9031rn_phy_fixup(struct phy_device *dev)
{
/*
* min rx data delay, max rx/tx clock delay,
* min rx/tx control delay
*/
mmd_write_reg(dev, 2, 4, 0);
mmd_write_reg(dev, 2, 5, 0);
mmd_write_reg(dev, 2, 8, 0x003ff);
return 0;
}
/*
* fixup for PLX PEX8909 bridge to configure GPIO1-7 as output High
* as they are used for slots1-7 PERST#
*/
static void ventana_pciesw_early_fixup(struct pci_dev *dev)
{
u32 dw;
if (!of_machine_is_compatible("gw,ventana"))
return;
if (dev->devfn != 0)
return;
pci_read_config_dword(dev, 0x62c, &dw);
dw |= 0xaaa8; // GPIO1-7 outputs
pci_write_config_dword(dev, 0x62c, dw);
pci_read_config_dword(dev, 0x644, &dw);
dw |= 0xfe; // GPIO1-7 output high
pci_write_config_dword(dev, 0x644, dw);
msleep(100);
}
DECLARE_PCI_FIXUP_EARLY(PCI_VENDOR_ID_PLX, 0x8609, ventana_pciesw_early_fixup);
DECLARE_PCI_FIXUP_EARLY(PCI_VENDOR_ID_PLX, 0x8606, ventana_pciesw_early_fixup);
DECLARE_PCI_FIXUP_EARLY(PCI_VENDOR_ID_PLX, 0x8604, ventana_pciesw_early_fixup);
static int ar8031_phy_fixup(struct phy_device *dev)
{
u16 val;
/* To enable AR8031 output a 125MHz clk from CLK_25M */
phy_write(dev, 0xd, 0x7);
phy_write(dev, 0xe, 0x8016);
phy_write(dev, 0xd, 0x4007);
val = phy_read(dev, 0xe);
val &= 0xffe3;
val |= 0x18;
phy_write(dev, 0xe, val);
/* introduce tx clock delay */
phy_write(dev, 0x1d, 0x5);
val = phy_read(dev, 0x1e);
val |= 0x0100;
phy_write(dev, 0x1e, val);
return 0;
}
#define PHY_ID_AR8031 0x004dd074
static int ar8035_phy_fixup(struct phy_device *dev)
{
u16 val;
/* Ar803x phy SmartEEE feature cause link status generates glitch,
* which cause ethernet link down/up issue, so disable SmartEEE
*/
phy_write(dev, 0xd, 0x3);
phy_write(dev, 0xe, 0x805d);
phy_write(dev, 0xd, 0x4003);
val = phy_read(dev, 0xe);
phy_write(dev, 0xe, val & ~(1 << 8));
/*
* Enable 125MHz clock from CLK_25M on the AR8031. This
* is fed in to the IMX6 on the ENET_REF_CLK (V22) pad.
* Also, introduce a tx clock delay.
*
* This is the same as is the AR8031 fixup.
*/
ar8031_phy_fixup(dev);
/*check phy power*/
val = phy_read(dev, 0x0);
if (val & BMCR_PDOWN)
phy_write(dev, 0x0, val & ~BMCR_PDOWN);
return 0;
}
#define PHY_ID_AR8035 0x004dd072
static void __init imx6q_enet_phy_init(void)
{
if (IS_BUILTIN(CONFIG_PHYLIB)) {
phy_register_fixup_for_uid(PHY_ID_KSZ9021, MICREL_PHY_ID_MASK,
ksz9021rn_phy_fixup);
phy_register_fixup_for_uid(PHY_ID_KSZ9031, MICREL_PHY_ID_MASK,
ksz9031rn_phy_fixup);
phy_register_fixup_for_uid(PHY_ID_AR8031, 0xffffffff,
ar8031_phy_fixup);
phy_register_fixup_for_uid(PHY_ID_AR8035, 0xffffffef,
ar8035_phy_fixup);
}
}
static void __init imx6q_1588_init(void)
{
struct device_node *np;
struct clk *ptp_clk;
struct clk *enet_ref;
struct regmap *gpr;
u32 clksel;
np = of_find_compatible_node(NULL, NULL, "fsl,imx6q-fec");
if (!np) {
pr_warn("%s: failed to find fec node\n", __func__);
return;
}
ptp_clk = of_clk_get(np, 2);
if (IS_ERR(ptp_clk)) {
pr_warn("%s: failed to get ptp clock\n", __func__);
goto put_node;
}
enet_ref = clk_get_sys(NULL, "enet_ref");
if (IS_ERR(enet_ref)) {
pr_warn("%s: failed to get enet clock\n", __func__);
goto put_ptp_clk;
}
/*
* If enet_ref from ANATOP/CCM is the PTP clock source, we need to
* set bit IOMUXC_GPR1[21]. Or the PTP clock must be from pad
* (external OSC), and we need to clear the bit.
*/
clksel = clk_is_match(ptp_clk, enet_ref) ?
IMX6Q_GPR1_ENET_CLK_SEL_ANATOP :
IMX6Q_GPR1_ENET_CLK_SEL_PAD;
gpr = syscon_regmap_lookup_by_compatible("fsl,imx6q-iomuxc-gpr");
if (!IS_ERR(gpr))
regmap_update_bits(gpr, IOMUXC_GPR1,
IMX6Q_GPR1_ENET_CLK_SEL_MASK,
clksel);
else
pr_err("failed to find fsl,imx6q-iomux-gpr regmap\n");
clk_put(enet_ref);
put_ptp_clk:
clk_put(ptp_clk);
put_node:
of_node_put(np);
}
static void __init imx6q_axi_init(void)
{
struct regmap *gpr;
unsigned int mask;
gpr = syscon_regmap_lookup_by_compatible("fsl,imx6q-iomuxc-gpr");
if (!IS_ERR(gpr)) {
/*
* Enable the cacheable attribute of VPU and IPU
* AXI transactions.
*/
mask = IMX6Q_GPR4_VPU_WR_CACHE_SEL |
IMX6Q_GPR4_VPU_RD_CACHE_SEL |
IMX6Q_GPR4_VPU_P_WR_CACHE_VAL |
IMX6Q_GPR4_VPU_P_RD_CACHE_VAL_MASK |
IMX6Q_GPR4_IPU_WR_CACHE_CTL |
IMX6Q_GPR4_IPU_RD_CACHE_CTL;
regmap_update_bits(gpr, IOMUXC_GPR4, mask, mask);
/* Increase IPU read QoS priority */
regmap_update_bits(gpr, IOMUXC_GPR6,
IMX6Q_GPR6_IPU1_ID00_RD_QOS_MASK |
IMX6Q_GPR6_IPU1_ID01_RD_QOS_MASK,
(0xf << 16) | (0x7 << 20));
regmap_update_bits(gpr, IOMUXC_GPR7,
IMX6Q_GPR7_IPU2_ID00_RD_QOS_MASK |
IMX6Q_GPR7_IPU2_ID01_RD_QOS_MASK,
(0xf << 16) | (0x7 << 20));
} else {
pr_warn("failed to find fsl,imx6q-iomuxc-gpr regmap\n");
}
}
static void __init imx6q_init_machine(void)
{
struct device *parent;
imx_print_silicon_rev(cpu_is_imx6dl() ? "i.MX6DL" : "i.MX6Q",
imx_get_soc_revision());
parent = imx_soc_device_init();
if (parent == NULL)
pr_warn("failed to initialize soc device\n");
imx6q_enet_phy_init();
of_platform_populate(NULL, of_default_bus_match_table, NULL, parent);
imx_anatop_init();
cpu_is_imx6q() ? imx6q_pm_init() : imx6dl_pm_init();
imx6q_1588_init();
imx6q_axi_init();
}
#define OCOTP_CFG3 0x440
#define OCOTP_CFG3_SPEED_SHIFT 16
#define OCOTP_CFG3_SPEED_1P2GHZ 0x3
#define OCOTP_CFG3_SPEED_996MHZ 0x2
#define OCOTP_CFG3_SPEED_852MHZ 0x1
static void __init imx6q_opp_check_speed_grading(struct device *cpu_dev)
{
struct device_node *np;
void __iomem *base;
u32 val;
np = of_find_compatible_node(NULL, NULL, "fsl,imx6q-ocotp");
if (!np) {
pr_warn("failed to find ocotp node\n");
return;
}
base = of_iomap(np, 0);
if (!base) {
pr_warn("failed to map ocotp\n");
goto put_node;
}
/*
* SPEED_GRADING[1:0] defines the max speed of ARM:
* 2b'11: 1200000000Hz;
* 2b'10: 996000000Hz;
* 2b'01: 852000000Hz; -- i.MX6Q Only, exclusive with 996MHz.
* 2b'00: 792000000Hz;
* We need to set the max speed of ARM according to fuse map.
*/
val = readl_relaxed(base + OCOTP_CFG3);
val >>= OCOTP_CFG3_SPEED_SHIFT;
val &= 0x3;
if ((val != OCOTP_CFG3_SPEED_1P2GHZ) && cpu_is_imx6q())
if (dev_pm_opp_disable(cpu_dev, 1200000000))
pr_warn("failed to disable 1.2 GHz OPP\n");
if (val < OCOTP_CFG3_SPEED_996MHZ)
if (dev_pm_opp_disable(cpu_dev, 996000000))
pr_warn("failed to disable 996 MHz OPP\n");
if (cpu_is_imx6q()) {
if (val != OCOTP_CFG3_SPEED_852MHZ)
if (dev_pm_opp_disable(cpu_dev, 852000000))
pr_warn("failed to disable 852 MHz OPP\n");
}
iounmap(base);
put_node:
of_node_put(np);
}
static void __init imx6q_opp_init(void)
{
struct device_node *np;
struct device *cpu_dev = get_cpu_device(0);
if (!cpu_dev) {
pr_warn("failed to get cpu0 device\n");
return;
}
np = of_node_get(cpu_dev->of_node);
if (!np) {
pr_warn("failed to find cpu0 node\n");
return;
}
if (of_init_opp_table(cpu_dev)) {
pr_warn("failed to init OPP table\n");
goto put_node;
}
imx6q_opp_check_speed_grading(cpu_dev);
put_node:
of_node_put(np);
}
static struct platform_device imx6q_cpufreq_pdev = {
.name = "imx6q-cpufreq",
};
static void __init imx6q_init_late(void)
{
/*
* WAIT mode is broken on TO 1.0 and 1.1, so there is no point
* to run cpuidle on them.
*/
if (imx_get_soc_revision() > IMX_CHIP_REVISION_1_1)
imx6q_cpuidle_init();
if (IS_ENABLED(CONFIG_ARM_IMX6Q_CPUFREQ)) {
imx6q_opp_init();
platform_device_register(&imx6q_cpufreq_pdev);
}
}
static void __init imx6q_map_io(void)
{
debug_ll_io_init();
imx_scu_map_io();
}
static void __init imx6q_init_irq(void)
{
imx_gpc_check_dt();
imx_init_revision_from_anatop();
imx_init_l2cache();
imx_src_init();
irqchip_init();
}
static const char * const imx6q_dt_compat[] __initconst = {
"fsl,imx6dl",
"fsl,imx6q",
NULL,
};
DT_MACHINE_START(IMX6Q, "Freescale i.MX6 Quad/DualLite (Device Tree)")
.smp = smp_ops(imx_smp_ops),
.map_io = imx6q_map_io,
.init_irq = imx6q_init_irq,
.init_machine = imx6q_init_machine,
.init_late = imx6q_init_late,
.dt_compat = imx6q_dt_compat,
MACHINE_END