summaryrefslogtreecommitdiffstats
path: root/kernel/arch/mn10300/unit-asb2364/include/unit/smsc911x.h
blob: 4c1ede535fa962a3516ac6b9392e44ea8612c367 (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
/* Support for the SMSC911x NIC
 *
 * Copyright (C) 2006 Matsushita Electric Industrial Co., Ltd.
 * All Rights Reserved.
 *
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License
 * as published by the Free Software Foundation; either version
 * 2 of the License, or (at your option) any later version.
 */
#ifndef _ASM_UNIT_SMSC911X_H
#define _ASM_UNIT_SMSC911X_H

#include <linux/netdevice.h>
#include <proc/irq.h>
#include <unit/fpga-regs.h>

#define MN10300_USE_EXT_EEPROM


#define SMSC911X_BASE		0xA8000000UL
#define SMSC911X_BASE_END	0xA8000100UL
#define SMSC911X_IRQ		FPGA_LAN_IRQ

/*
 * Allow the FPGA to be initialised by the SMSC911x driver
 */
#undef SMSC_INITIALIZE
#define SMSC_INITIALIZE()					\
do {								\
	/* release reset */					\
	ASB2364_FPGA_REG_RESET_LAN = 0x0001;			\
	SyncExBus();						\
} while (0)

#ifdef MN10300_USE_EXT_EEPROM
#include <linux/delay.h>
#include <unit/clock.h>

#define EEPROM_ADDRESS	0xA0
#define MAC_OFFSET	0x0008
#define USE_IIC_CH	0	/* 0 or 1 */
#define IIC_OFFSET	(0x80000 * USE_IIC_CH)
#define IIC_DTRM	__SYSREG(0xd8400000 + IIC_OFFSET, u32)
#define IIC_DREC	__SYSREG(0xd8400004 + IIC_OFFSET, u32)
#define IIC_MYADD	__SYSREG(0xd8400008 + IIC_OFFSET, u32)
#define IIC_CLK		__SYSREG(0xd840000c + IIC_OFFSET, u32)
#define IIC_BRST	__SYSREG(0xd8400010 + IIC_OFFSET, u32)
#define IIC_HOLD	__SYSREG(0xd8400014 + IIC_OFFSET, u32)
#define IIC_BSTS	__SYSREG(0xd8400018 + IIC_OFFSET, u32)
#define IIC_ICR		__SYSREG(0xd4000080 + 4 * USE_IIC_CH, u16)

#define IIC_CLK_PLS	((unsigned short)(MN10300_IOCLK / 100000 - 1))
#define IIC_CLK_LOW	((unsigned short)(IIC_CLK_PLS / 2))

#define SYS_IIC_DTRM_Bit_STA	((unsigned short)0x0400)
#define SYS_IIC_DTRM_Bit_STO	((unsigned short)0x0200)
#define SYS_IIC_DTRM_Bit_ACK	((unsigned short)0x0100)
#define SYS_IIC_DTRM_Bit_DATA	((unsigned short)0x00FF)

static inline void POLL_INT_REQ(volatile u16 *icr)
{
	unsigned long flags;
	u16 tmp;

	while (!(*icr & GxICR_REQUEST))
		;
	flags = arch_local_cli_save();
	tmp = *icr;
	*icr = (tmp & GxICR_LEVEL) | GxICR_DETECT;
	tmp = *icr;
	arch_local_irq_restore(flags);
}

/*
 * Implement the SMSC911x hook for MAC address retrieval
 */
#undef smsc_get_mac
static inline int smsc_get_mac(struct net_device *dev)
{
	unsigned char *mac_buf = dev->dev_addr;
	int i;
	unsigned short value;
	unsigned int data;
	int mac_length = 6;
	int check;
	u16 orig_gicr, tmp;
	unsigned long flags;

	/* save original GnICR and clear GnICR.IE */
	flags = arch_local_cli_save();
	orig_gicr = IIC_ICR;
	IIC_ICR = orig_gicr & GxICR_LEVEL;
	tmp = IIC_ICR;
	arch_local_irq_restore(flags);

	IIC_MYADD = 0x00000008;
	IIC_CLK = (IIC_CLK_LOW << 16) + (IIC_CLK_PLS);
	/* bus hung recovery */

	while (1) {
		check = 0;
		for (i = 0; i < 3; i++) {
			if ((IIC_BSTS & 0x00000003) == 0x00000003)
				check++;
			udelay(3);
		}

		if (check == 3) {
			IIC_BRST = 0x00000003;
			break;
		} else {
			for (i = 0; i < 3; i++) {
				IIC_BRST = 0x00000002;
				udelay(8);
				IIC_BRST = 0x00000003;
				udelay(8);
			}
		}
	}

	IIC_BRST = 0x00000002;
	IIC_BRST = 0x00000003;

	value	=  SYS_IIC_DTRM_Bit_STA | SYS_IIC_DTRM_Bit_ACK;
	value	|= (((unsigned short)EEPROM_ADDRESS & SYS_IIC_DTRM_Bit_DATA) |
		    (unsigned short)0x0000);
	IIC_DTRM = value;
	POLL_INT_REQ(&IIC_ICR);

	/** send offset of MAC address in EEPROM **/
	IIC_DTRM = (unsigned char)((MAC_OFFSET & 0xFF00) >> 8);
	POLL_INT_REQ(&IIC_ICR);

	IIC_DTRM = (unsigned char)(MAC_OFFSET & 0x00FF);
	POLL_INT_REQ(&IIC_ICR);

	udelay(1000);

	value	=  SYS_IIC_DTRM_Bit_STA;
	value	|= (((unsigned short)EEPROM_ADDRESS & SYS_IIC_DTRM_Bit_DATA) |
		    (unsigned short)0x0001);
	IIC_DTRM = value;
	POLL_INT_REQ(&IIC_ICR);

	IIC_DTRM = 0x00000000;
	while (mac_length > 0) {
		POLL_INT_REQ(&IIC_ICR);

		data = IIC_DREC;
		mac_length--;
		if (mac_length == 0)
			value = 0x00000300;	/* stop IIC bus */
		else if (mac_length == 1)
			value = 0x00000100;	/* no ack */
		else
			value = 0x00000000;	/* ack */
		IIC_DTRM = value;
		*mac_buf++ = (unsigned char)(data & 0xff);
	}

	/* restore GnICR.LV and GnICR.IE */
	flags = arch_local_cli_save();
	IIC_ICR = (orig_gicr & (GxICR_LEVEL | GxICR_ENABLE));
	tmp = IIC_ICR;
	arch_local_irq_restore(flags);

	return 0;
}
#endif /* MN10300_USE_EXT_EEPROM */
#endif /* _ASM_UNIT_SMSC911X_H */