summaryrefslogtreecommitdiff
path: root/arch/arm/cpu/armv7/bcm281xx/clk-eth.c
blob: 618af709fa5a9f5a07258567bd81d7353f7f3a1e (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
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
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
// SPDX-License-Identifier: GPL-2.0+
/*
 * Copyright 2014 Broadcom Corporation.
 */

#include <common.h>
#include <asm/io.h>
#include <linux/errno.h>
#include <asm/arch/sysmap.h>
#include <asm/kona-common/clk.h>
#include "clk-core.h"

#define WR_ACCESS_ADDR			ESUB_CLK_BASE_ADDR
#define WR_ACCESS_PASSWORD				0xA5A500

#define PLLE_POST_RESETB_ADDR		(ESUB_CLK_BASE_ADDR + 0x00000C00)

#define PLLE_RESETB_ADDR		(ESUB_CLK_BASE_ADDR + 0x00000C58)
#define PLLE_RESETB_I_PLL_RESETB_PLLE_MASK		0x00010000
#define PLLE_POST_RESETB_I_POST_RESETB_PLLE_MASK	0x00000001

#define PLL_LOCK_ADDR			(ESUB_CLK_BASE_ADDR + 0x00000C38)
#define PLL_LOCK_PLL_LOCK_PLLE_MASK			0x00000001

#define ESW_SYS_DIV_ADDR		(ESUB_CLK_BASE_ADDR + 0x00000A04)
#define ESW_SYS_DIV_PLL_SELECT_MASK			0x00000300
#define ESW_SYS_DIV_DIV_MASK				0x0000001C
#define ESW_SYS_DIV_PLL_VAR_208M_CLK_SELECT		0x00000100
#define ESW_SYS_DIV_DIV_SELECT				0x4
#define ESW_SYS_DIV_TRIGGER_MASK			0x00000001

#define ESUB_AXI_DIV_DEBUG_ADDR		(ESUB_CLK_BASE_ADDR + 0x00000E04)
#define ESUB_AXI_DIV_DEBUG_PLL_SELECT_MASK		0x0000001C
#define ESUB_AXI_DIV_DEBUG_PLL_SELECT_OVERRIDE_MASK	0x00000040
#define ESUB_AXI_DIV_DEBUG_PLL_VAR_208M_CLK_SELECT	0x0
#define ESUB_AXI_DIV_DEBUG_TRIGGER_MASK			0x00000001

#define PLL_MAX_RETRY	100

/* Enable appropriate clocks for Ethernet */
int clk_eth_enable(void)
{
	int rc = -1;
	int retry_count = 0;
	rc = clk_get_and_enable("esub_ccu_clk");

	/* Enable Access to CCU registers */
	writel((1 | WR_ACCESS_PASSWORD), WR_ACCESS_ADDR);

	writel(readl(PLLE_POST_RESETB_ADDR) &
	       ~PLLE_POST_RESETB_I_POST_RESETB_PLLE_MASK,
	       PLLE_POST_RESETB_ADDR);

	/* Take PLL out of reset and put into normal mode */
	writel(readl(PLLE_RESETB_ADDR) | PLLE_RESETB_I_PLL_RESETB_PLLE_MASK,
	       PLLE_RESETB_ADDR);

	/* Wait for PLL lock */
	rc = -1;
	while (retry_count < PLL_MAX_RETRY) {
		udelay(100);
		if (readl(PLL_LOCK_ADDR) & PLL_LOCK_PLL_LOCK_PLLE_MASK) {
			rc = 0;
			break;
		}
		retry_count++;
	}

	if (rc == -1) {
		printf("%s: ETH-PLL lock timeout, Ethernet is not enabled!\n",
		       __func__);
		return -1;
	}

	writel(readl(PLLE_POST_RESETB_ADDR) |
	       PLLE_POST_RESETB_I_POST_RESETB_PLLE_MASK,
	       PLLE_POST_RESETB_ADDR);

	/* Switch esw_sys_clk to use 104MHz(208MHz/2) clock */
	writel((readl(ESW_SYS_DIV_ADDR) &
		~(ESW_SYS_DIV_PLL_SELECT_MASK | ESW_SYS_DIV_DIV_MASK)) |
	       ESW_SYS_DIV_PLL_VAR_208M_CLK_SELECT | ESW_SYS_DIV_DIV_SELECT,
	       ESW_SYS_DIV_ADDR);

	writel(readl(ESW_SYS_DIV_ADDR) | ESW_SYS_DIV_TRIGGER_MASK,
	       ESW_SYS_DIV_ADDR);

	/* Wait for trigger complete */
	rc = -1;
	retry_count = 0;
	while (retry_count < PLL_MAX_RETRY) {
		udelay(100);
		if (!(readl(ESW_SYS_DIV_ADDR) & ESW_SYS_DIV_TRIGGER_MASK)) {
			rc = 0;
			break;
		}
		retry_count++;
	}

	if (rc == -1) {
		printf("%s: SYS CLK Trigger timeout, Ethernet is not enabled!\n",
		       __func__);
		return -1;
	}

	/* switch Esub AXI clock to 208MHz */
	writel((readl(ESUB_AXI_DIV_DEBUG_ADDR) &
		~(ESUB_AXI_DIV_DEBUG_PLL_SELECT_MASK |
		  ESUB_AXI_DIV_DEBUG_PLL_SELECT_OVERRIDE_MASK |
		  ESUB_AXI_DIV_DEBUG_TRIGGER_MASK)) |
	       ESUB_AXI_DIV_DEBUG_PLL_VAR_208M_CLK_SELECT |
	       ESUB_AXI_DIV_DEBUG_PLL_SELECT_OVERRIDE_MASK,
	       ESUB_AXI_DIV_DEBUG_ADDR);

	writel(readl(ESUB_AXI_DIV_DEBUG_ADDR) |
	       ESUB_AXI_DIV_DEBUG_TRIGGER_MASK,
	       ESUB_AXI_DIV_DEBUG_ADDR);

	/* Wait for trigger complete */
	rc = -1;
	retry_count = 0;
	while (retry_count < PLL_MAX_RETRY) {
		udelay(100);
		if (!(readl(ESUB_AXI_DIV_DEBUG_ADDR) &
		      ESUB_AXI_DIV_DEBUG_TRIGGER_MASK)) {
			rc = 0;
			break;
		}
		retry_count++;
	}

	if (rc == -1) {
		printf("%s: AXI CLK Trigger timeout, Ethernet is not enabled!\n",
		       __func__);
		return -1;
	}

	/* Disable Access to CCU registers */
	writel(WR_ACCESS_PASSWORD, WR_ACCESS_ADDR);

	return rc;
}