summaryrefslogtreecommitdiffstats
path: root/target/linux/ramips/files/drivers/net/ramips_esw.c
blob: 18008919c642998b6fb56bf5ba2424e6be9a1ff7 (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
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
#include <linux/ioport.h>

#include <rt305x_regs.h>
#include <rt305x_esw_platform.h>

#define RT305X_ESW_REG_FCT0		0x08
#define RT305X_ESW_REG_PFC1		0x14
#define RT305X_ESW_REG_PVIDC(_n)	(0x48 + 4 * (_n))
#define RT305X_ESW_REG_VLANI(_n)	(0x50 + 4 * (_n))
#define RT305X_ESW_REG_VMSC(_n)		(0x70 + 4 * (_n))
#define RT305X_ESW_REG_FPA		0x84
#define RT305X_ESW_REG_SOCPC		0x8c
#define RT305X_ESW_REG_POC1		0x90
#define RT305X_ESW_REG_POC2		0x94
#define RT305X_ESW_REG_POC3		0x98
#define RT305X_ESW_REG_SGC		0x9c
#define RT305X_ESW_REG_PCR0		0xc0
#define RT305X_ESW_REG_PCR1		0xc4
#define RT305X_ESW_REG_FPA2		0xc8
#define RT305X_ESW_REG_FCT2		0xcc
#define RT305X_ESW_REG_SGC2		0xe4

#define RT305X_ESW_PCR0_WT_NWAY_DATA_S	16
#define RT305X_ESW_PCR0_WT_PHY_CMD	BIT(13)
#define RT305X_ESW_PCR0_CPU_PHY_REG_S	8

#define RT305X_ESW_PCR1_WT_DONE		BIT(0)

#define RT305X_ESW_PHY_TIMEOUT		(5 * HZ)

struct rt305x_esw {
	void __iomem *base;
	struct rt305x_esw_platform_data *pdata;
};

static inline void
ramips_esw_wr(struct rt305x_esw *esw, u32 val, unsigned reg)
{
	__raw_writel(val, esw->base + reg);
}

static inline u32
ramips_esw_rr(struct rt305x_esw *esw, unsigned reg)
{
	return __raw_readl(esw->base + reg);
}

static u32
mii_mgr_write(struct rt305x_esw *esw, u32 phy_addr, u32 phy_register,
	      u32 write_data)
{
	unsigned long t_start = jiffies;
	int ret = 0;

	while (1) {
		if (!(ramips_esw_rr(esw, RT305X_ESW_REG_PCR1) &
		      RT305X_ESW_PCR1_WT_DONE))
			break;
		if (time_after(jiffies, t_start + RT305X_ESW_PHY_TIMEOUT)) {
			ret = 1;
			goto out;
		}
	}

	write_data &= 0xffff;
	ramips_esw_wr(esw,
		      (write_data << RT305X_ESW_PCR0_WT_NWAY_DATA_S) |
		      (phy_register << RT305X_ESW_PCR0_CPU_PHY_REG_S) |
		      (phy_addr) | RT305X_ESW_PCR0_WT_PHY_CMD,
		      RT305X_ESW_REG_PCR0);

	t_start = jiffies;
	while (1) {
		if (ramips_esw_rr(esw, RT305X_ESW_REG_PCR1) &
		    RT305X_ESW_PCR1_WT_DONE)
			break;

		if (time_after(jiffies, t_start + RT305X_ESW_PHY_TIMEOUT)) {
			ret = 1;
			break;
		}
	}
out:
	if (ret)
		printk(KERN_ERR "ramips_eth: MDIO timeout\n");
	return ret;
}

static void
rt305x_esw_hw_init(struct rt305x_esw *esw)
{
	int i;

	/* vodoo from original driver */
	ramips_esw_wr(esw, 0xC8A07850, RT305X_ESW_REG_FCT0);
	ramips_esw_wr(esw, 0x00000000, RT305X_ESW_REG_SGC2);
	ramips_esw_wr(esw, 0x00405555, RT305X_ESW_REG_PFC1);
	ramips_esw_wr(esw, 0x00002001, RT305X_ESW_REG_VLANI(0));
	ramips_esw_wr(esw, 0x00007f7f, RT305X_ESW_REG_POC1);
	ramips_esw_wr(esw, 0x00007f3f, RT305X_ESW_REG_POC3);
	ramips_esw_wr(esw, 0x00d6500c, RT305X_ESW_REG_FCT2);
	ramips_esw_wr(esw, 0x0008a301, RT305X_ESW_REG_SGC);
	ramips_esw_wr(esw, 0x02404040, RT305X_ESW_REG_SOCPC);
	ramips_esw_wr(esw, 0x00001002, RT305X_ESW_REG_PVIDC(2));
	ramips_esw_wr(esw, 0x3f502b28, RT305X_ESW_REG_FPA2);
	ramips_esw_wr(esw, 0x00000000, RT305X_ESW_REG_FPA);

	mii_mgr_write(esw, 0, 31, 0x8000);
	for (i = 0; i < 5; i++) {
		/* TX10 waveform coefficient */
		mii_mgr_write(esw, i, 0, 0x3100);
		/* TX10 waveform coefficient */
		mii_mgr_write(esw, i, 26, 0x1601);
		/* TX100/TX10 AD/DA current bias */
		mii_mgr_write(esw, i, 29, 0x7058);
		/* TX100 slew rate control */
		mii_mgr_write(esw, i, 30, 0x0018);
	}

	/* PHY IOT */
	/* select global register */
	mii_mgr_write(esw, 0, 31, 0x0);
	/* tune TP_IDL tail and head waveform */
	mii_mgr_write(esw, 0, 22, 0x052f);
	/* set TX10 signal amplitude threshold to minimum */
	mii_mgr_write(esw, 0, 17, 0x0fe0);
	/* set squelch amplitude to higher threshold */
	mii_mgr_write(esw, 0, 18, 0x40ba);
	/* longer TP_IDL tail length */
	mii_mgr_write(esw, 0, 14, 0x65);
	/* select local register */
	mii_mgr_write(esw, 0, 31, 0x8000);

	/* set default vlan */
	ramips_esw_wr(esw, 0x2001, RT305X_ESW_REG_VLANI(0));
	ramips_esw_wr(esw, 0x504f, RT305X_ESW_REG_VMSC(0));
}

static int
rt305x_esw_probe(struct platform_device *pdev)
{
	struct rt305x_esw_platform_data *pdata;
	struct rt305x_esw *esw;
	struct resource *res;
	int err;

	pdata = pdev->dev.platform_data;
	if (!pdata)
		return -EINVAL;

	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
	if (!res) {
		dev_err(&pdev->dev, "no memory resource found\n");
		return -ENOMEM;
	}

	esw = kzalloc(sizeof(struct rt305x_esw), GFP_KERNEL);
	if (!esw) {
		dev_err(&pdev->dev, "no memory for private data\n");
		return -ENOMEM;
	}

	esw->base = ioremap(res->start, resource_size(res));
	if (!esw->base) {
		dev_err(&pdev->dev, "ioremap failed\n");
		err = -ENOMEM;
		goto free_esw;
	}

	platform_set_drvdata(pdev, esw);

	esw->pdata = pdata;
	rt305x_esw_hw_init(esw);

	return 0;

free_esw:
	kfree(esw);
	return err;
}

static int
rt305x_esw_remove(struct platform_device *pdev)
{
	struct rt305x_esw *esw;

	esw = platform_get_drvdata(pdev);
	if (esw) {
		platform_set_drvdata(pdev, NULL);
		iounmap(esw->base);
		kfree(esw);
	}

	return 0;
}

static struct platform_driver rt305x_esw_driver = {
	.probe = rt305x_esw_probe,
	.remove = rt305x_esw_remove,
	.driver = {
		.name = "rt305x-esw",
		.owner = THIS_MODULE,
	},
};

static int __init
rt305x_esw_init(void)
{
	return platform_driver_register(&rt305x_esw_driver);
}

static void __exit
rt305x_esw_exit(void)
{
	platform_driver_unregister(&rt305x_esw_driver);
}