mach-imx6q.c 7.0 KB
Newer Older
1
/*
2
 * Copyright 2011-2013 Freescale Semiconductor, Inc.
3 4 5 6 7 8 9 10 11 12
 * 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
 */

13
#include <linux/clk.h>
14
#include <linux/clk-provider.h>
15
#include <linux/clkdev.h>
16
#include <linux/clocksource.h>
17
#include <linux/cpu.h>
18
#include <linux/delay.h>
R
Robert Lee 已提交
19
#include <linux/export.h>
20
#include <linux/init.h>
21
#include <linux/io.h>
22
#include <linux/irq.h>
23
#include <linux/irqchip.h>
24
#include <linux/of.h>
25
#include <linux/of_address.h>
26 27
#include <linux/of_irq.h>
#include <linux/of_platform.h>
28
#include <linux/pm_opp.h>
29
#include <linux/phy.h>
30
#include <linux/reboot.h>
31
#include <linux/regmap.h>
32
#include <linux/micrel_phy.h>
33
#include <linux/mfd/syscon.h>
34
#include <linux/mfd/syscon/imx6q-iomuxc-gpr.h>
35
#include <asm/mach/arch.h>
36
#include <asm/mach/map.h>
37
#include <asm/system_misc.h>
38

39
#include "common.h"
40
#include "cpuidle.h"
41
#include "hardware.h"
R
Robert Lee 已提交
42

S
Shawn Guo 已提交
43
static u32 chip_revision;
44

45
int imx6q_revision(void)
46
{
S
Shawn Guo 已提交
47 48
	return chip_revision;
}
49

S
Shawn Guo 已提交
50 51 52
static void __init imx6q_init_revision(void)
{
	u32 rev = imx_anatop_get_digprog();
53 54 55

	switch (rev & 0xff) {
	case 0:
S
Shawn Guo 已提交
56 57
		chip_revision = IMX_CHIP_REVISION_1_0;
		break;
58
	case 1:
S
Shawn Guo 已提交
59 60
		chip_revision = IMX_CHIP_REVISION_1_1;
		break;
61
	case 2:
S
Shawn Guo 已提交
62 63
		chip_revision = IMX_CHIP_REVISION_1_2;
		break;
64
	default:
S
Shawn Guo 已提交
65
		chip_revision = IMX_CHIP_REVISION_UNKNOWN;
66
	}
S
Shawn Guo 已提交
67 68

	mxc_set_cpu_type(rev >> 16 & 0xff);
69 70
}

71
static void imx6q_restart(enum reboot_mode mode, const char *cmd)
72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100
{
	struct device_node *np;
	void __iomem *wdog_base;

	np = of_find_compatible_node(NULL, NULL, "fsl,imx6q-wdt");
	wdog_base = of_iomap(np, 0);
	if (!wdog_base)
		goto soft;

	imx_src_prepare_restart();

	/* enable wdog */
	writew_relaxed(1 << 2, wdog_base);
	/* write twice to ensure the request will not get ignored */
	writew_relaxed(1 << 2, wdog_base);

	/* wait for reset to assert ... */
	mdelay(500);

	pr_err("Watchdog reset failed to assert reset\n");

	/* delay to allow the serial port to show the message */
	mdelay(50);

soft:
	/* we'll take a jump through zero as a poor second */
	soft_restart(0);
}

101 102 103
/* For imx6q sabrelite board: set KSZ9021RN RGMII pad skew */
static int ksz9021rn_phy_fixup(struct phy_device *phydev)
{
104
	if (IS_BUILTIN(CONFIG_PHYLIB)) {
105
		/* min rx data delay */
106 107 108
		phy_write(phydev, MICREL_KSZ9021_EXTREG_CTRL,
			0x8000 | MICREL_KSZ9021_RGMII_RX_DATA_PAD_SCEW);
		phy_write(phydev, MICREL_KSZ9021_EXTREG_DATA_WRITE, 0x0000);
109

110
		/* max rx/tx clock delay, min rx/tx control delay */
111 112 113 114 115
		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);
116
	}
117 118 119 120

	return 0;
}

121
static void mmd_write_reg(struct phy_device *dev, int device, int reg, int val)
122
{
123 124 125 126
	phy_write(dev, 0x0d, device);
	phy_write(dev, 0x0e, reg);
	phy_write(dev, 0x0d, (1 << 14) | device);
	phy_write(dev, 0x0e, val);
127 128
}

129
static int ksz9031rn_phy_fixup(struct phy_device *dev)
130
{
131 132 133 134 135 136 137 138 139
	/*
	 * 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;
140 141
}

142
static int ar8031_phy_fixup(struct phy_device *dev)
143
{
144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162
	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;
163 164
}

165 166
#define PHY_ID_AR8031	0x004dd074

167
static void __init imx6q_enet_phy_init(void)
168
{
169
	if (IS_BUILTIN(CONFIG_PHYLIB)) {
170
		phy_register_fixup_for_uid(PHY_ID_KSZ9021, MICREL_PHY_ID_MASK,
171
				ksz9021rn_phy_fixup);
172 173
		phy_register_fixup_for_uid(PHY_ID_KSZ9031, MICREL_PHY_ID_MASK,
				ksz9031rn_phy_fixup);
174 175
		phy_register_fixup_for_uid(PHY_ID_AR8031, 0xffffffff,
				ar8031_phy_fixup);
176
	}
177 178
}

179 180 181 182 183 184
static void __init imx6q_1588_init(void)
{
	struct regmap *gpr;

	gpr = syscon_regmap_lookup_by_compatible("fsl,imx6q-iomuxc-gpr");
	if (!IS_ERR(gpr))
185 186 187
		regmap_update_bits(gpr, IOMUXC_GPR1,
				IMX6Q_GPR1_ENET_CLK_SEL_MASK,
				IMX6Q_GPR1_ENET_CLK_SEL_ANATOP);
188 189 190 191
	else
		pr_err("failed to find fsl,imx6q-iomux-gpr regmap\n");

}
192

193 194
static void __init imx6q_init_machine(void)
{
195
	imx6q_enet_phy_init();
196

197 198
	of_platform_populate(NULL, of_default_bus_match_table, NULL, NULL);

199
	imx_anatop_init();
200
	imx6q_pm_init();
201
	imx6q_1588_init();
202 203
}

204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228
#define OCOTP_CFG3			0x440
#define OCOTP_CFG3_SPEED_SHIFT		16
#define OCOTP_CFG3_SPEED_1P2GHZ		0x3

static void __init imx6q_opp_check_1p2ghz(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;
	}

	val = readl_relaxed(base + OCOTP_CFG3);
	val >>= OCOTP_CFG3_SPEED_SHIFT;
	if ((val & 0x3) != OCOTP_CFG3_SPEED_1P2GHZ)
229
		if (dev_pm_opp_disable(cpu_dev, 1200000000))
230 231 232 233 234 235
			pr_warn("failed to disable 1.2 GHz OPP\n");

put_node:
	of_node_put(np);
}

236
static void __init imx6q_opp_init(void)
237 238
{
	struct device_node *np;
239
	struct device *cpu_dev = get_cpu_device(0);
240

241 242 243 244
	if (!cpu_dev) {
		pr_warn("failed to get cpu0 device\n");
		return;
	}
245
	np = of_node_get(cpu_dev->of_node);
246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261
	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_1p2ghz(cpu_dev);

put_node:
	of_node_put(np);
}

262
static struct platform_device imx6q_cpufreq_pdev = {
263 264 265
	.name = "imx6q-cpufreq",
};

R
Robert Lee 已提交
266 267
static void __init imx6q_init_late(void)
{
268 269 270 271 272 273
	/*
	 * WAIT mode is broken on TO 1.0 and 1.1, so there is no point
	 * to run cpuidle on them.
	 */
	if (imx6q_revision() > IMX_CHIP_REVISION_1_1)
		imx6q_cpuidle_init();
274 275

	if (IS_ENABLED(CONFIG_ARM_IMX6Q_CPUFREQ)) {
276
		imx6q_opp_init();
277 278
		platform_device_register(&imx6q_cpufreq_pdev);
	}
R
Robert Lee 已提交
279 280
}

281 282
static void __init imx6q_map_io(void)
{
283
	debug_ll_io_init();
284 285 286 287 288
	imx_scu_map_io();
}

static void __init imx6q_init_irq(void)
{
S
Shawn Guo 已提交
289
	imx6q_init_revision();
290
	imx_init_l2cache();
291 292
	imx_src_init();
	imx_gpc_init();
293
	irqchip_init();
294 295 296 297
}

static void __init imx6q_timer_init(void)
{
298
	of_clk_init(NULL);
299
	clocksource_of_init();
S
Shawn Guo 已提交
300 301
	imx_print_silicon_rev(cpu_is_imx6dl() ? "i.MX6DL" : "i.MX6Q",
			      imx6q_revision());
302 303 304
}

static const char *imx6q_dt_compat[] __initdata = {
S
Shawn Guo 已提交
305
	"fsl,imx6dl",
306
	"fsl,imx6q",
307 308 309
	NULL,
};

S
Shawn Guo 已提交
310
DT_MACHINE_START(IMX6Q, "Freescale i.MX6 Quad/DualLite (Device Tree)")
311
	.smp		= smp_ops(imx_smp_ops),
312 313
	.map_io		= imx6q_map_io,
	.init_irq	= imx6q_init_irq,
S
Stephen Warren 已提交
314
	.init_time	= imx6q_timer_init,
315
	.init_machine	= imx6q_init_machine,
R
Robert Lee 已提交
316
	.init_late      = imx6q_init_late,
317
	.dt_compat	= imx6q_dt_compat,
318
	.restart	= imx6q_restart,
319
MACHINE_END