1 /*
2  * linux/arch/sh/kernel/io_microdev.c
3  *
4  * Copyright (C) 2003 Sean McGoogan (Sean.McGoogan@superh.com)
5  *
6  * SuperH SH4-202 MicroDev board support.
7  *
8  * May be copied or modified under the terms of the GNU General Public
9  * License.  See linux/COPYING for more information.
10  */
11 
12 #include <linux/config.h>
13 #include <linux/init.h>
14 #include <asm/io.h>
15 
16 
17 	/*
18 	 *	we need to have a 'safe' address to re-direct all I/O requests
19 	 *	that we do not explicitly wish to handle. This safe address
20 	 *	must have the following properies:
21 	 *
22 	 *		* writes are ignored (no exception)
23 	 *		* reads are benign (no side-effects)
24 	 *		* accesses of width 1, 2 and 4-bytes are all valid.
25 	 *
26 	 *	The Processor Version Register (PVR) has these properties.
27 	 */
28 #define	PVR	0xff000030	/* Processor Version Register */
29 
30 
31 #define	IO_LAN91C111	0x300ul	/* I/O port for SMSC  LAN91C111 Ethernet chip */
32 
33 #define PORT2ADDR(x) (microdev_isa_port2addr(x))
34 
35 
delay(void)36 static inline void delay(void)
37 {
38 	ctrl_inw(0xa0000000);
39 }
40 
microdev_inb(unsigned long port)41 unsigned char microdev_inb(unsigned long port)
42 {
43 	return *(volatile unsigned char*)PORT2ADDR(port);
44 }
45 
microdev_inw(unsigned long port)46 unsigned short microdev_inw(unsigned long port)
47 {
48 	return *(volatile unsigned short*)PORT2ADDR(port);
49 }
50 
microdev_inl(unsigned long port)51 unsigned int microdev_inl(unsigned long port)
52 {
53 	return *(volatile unsigned int*)PORT2ADDR(port);
54 }
55 
microdev_outb(unsigned char b,unsigned long port)56 void microdev_outb(unsigned char b, unsigned long port)
57 {
58 		/*
59 		 *	There is a board feature with the current SH4-202 MicroDev
60 		 *	in that the 2 byte enables (nBE0 and nBE1) are tied together (and to the
61 		 *	Chip Select Line (Ethernet_CS)). Due to this conectivity, it is not possible
62 		 *	to safely perform 8-bit writes to the Ethernet registers, as 16-bits
63 		 *	will be consumed from the Data lines (corrupting the other byte).
64 		 *	Hence, this function is written to impliment 16-bit read/modify/write
65 		 *	for all byte-wide acceses.
66 		 *
67 		 *	Note: there is no problem with byte READS (even or odd).
68 		 *
69 		 *			Sean McGoogan - 16th June 2003.
70 		 */
71 	if ( (port>=IO_LAN91C111) && (port<IO_LAN91C111+0x10ul) )
72 	{
73 			/*
74 			 * Then are trying to perform a byte-write to the LAN91C111.
75 			 * This needs special care.
76 			 */
77 		if (port%2==1)					/* is the port odd ? */
78 		{
79 			const unsigned long evenPort = port-1;	/* unset bit-0, i.e. make even */
80 			unsigned short word;			/* temp variable */
81 				/*
82 				 * do a 16-bit read/write to write to 'port', preserving even byte.
83 				 *	Even addresses are bits 0-7
84 				 *	Odd  addresses are bits 8-15
85 				 */
86 			word = microdev_inw(evenPort);
87 			word = (word & 0xffu) | (b << 8);
88 			microdev_outw(word, evenPort);
89 		}
90 		else	/* else, we are trying to do an even byte write */
91 		{
92 			unsigned short word;			/* temp variable */
93 				/*
94 				 * do a 16-bit read/write to write to 'port', preserving odd byte.
95 				 *	Even addresses are bits 0-7
96 				 *	Odd  addresses are bits 8-15
97 				 */
98 			word = microdev_inw(port);
99 			word = (word & 0xff00u) | (b);
100 			microdev_outw(word, port);
101 		}
102 	}
103 	else
104 	{
105 		*(volatile unsigned char*)PORT2ADDR(port) = b;
106 	}
107 }
108 
microdev_outw(unsigned short b,unsigned long port)109 void microdev_outw(unsigned short b, unsigned long port)
110 {
111 	*(volatile unsigned short*)PORT2ADDR(port) = b;
112 }
113 
microdev_outl(unsigned int b,unsigned long port)114 void microdev_outl(unsigned int b, unsigned long port)
115 {
116         *(volatile unsigned int*)PORT2ADDR(port) = b;
117 }
118 
microdev_inb_p(unsigned long port)119 unsigned char microdev_inb_p(unsigned long port)
120 {
121 	unsigned char v = microdev_inb(port);
122 	delay();
123 	return v;
124 }
125 
microdev_inw_p(unsigned long port)126 unsigned short microdev_inw_p(unsigned long port)
127 {
128 	unsigned short v = microdev_inw(port);
129 	delay();
130 	return v;
131 }
132 
microdev_inl_p(unsigned long port)133 unsigned int microdev_inl_p(unsigned long port)
134 {
135 	unsigned int v = microdev_inl(port);
136 	delay();
137 	return v;
138 }
139 
microdev_outb_p(unsigned char b,unsigned long port)140 void microdev_outb_p(unsigned char b, unsigned long port)
141 {
142 	microdev_outb(b, port);
143 	delay();
144 }
145 
microdev_outw_p(unsigned short b,unsigned long port)146 void microdev_outw_p(unsigned short b, unsigned long port)
147 {
148 	microdev_outw(b, port);
149 	delay();
150 }
151 
microdev_outl_p(unsigned int b,unsigned long port)152 void microdev_outl_p(unsigned int b, unsigned long port)
153 {
154 	microdev_outl(b, port);
155 	delay();
156 }
157 
microdev_insb(unsigned long port,void * buffer,unsigned long count)158 void microdev_insb(unsigned long port, void *buffer, unsigned long count)
159 {
160 	unsigned char *buf=buffer;
161 	while(count--) *buf++=microdev_inb(port);
162 }
163 
microdev_insw(unsigned long port,void * buffer,unsigned long count)164 void microdev_insw(unsigned long port, void *buffer, unsigned long count)
165 {
166 	unsigned short *buf=buffer;
167 	while(count--) *buf++=microdev_inw(port);
168 }
169 
microdev_insl(unsigned long port,void * buffer,unsigned long count)170 void microdev_insl(unsigned long port, void *buffer, unsigned long count)
171 {
172 	unsigned int *buf=buffer;
173 	while(count--) *buf++=microdev_inl(port);
174 }
175 
microdev_outsb(unsigned long port,const void * buffer,unsigned long count)176 void microdev_outsb(unsigned long port, const void *buffer, unsigned long count)
177 {
178 	const unsigned char *buf=buffer;
179 	while(count--) microdev_outb(*buf++, port);
180 }
181 
microdev_outsw(unsigned long port,const void * buffer,unsigned long count)182 void microdev_outsw(unsigned long port, const void *buffer, unsigned long count)
183 {
184 	const unsigned short *buf=buffer;
185 	while(count--) microdev_outw(*buf++, port);
186 }
187 
microdev_outsl(unsigned long port,const void * buffer,unsigned long count)188 void microdev_outsl(unsigned long port, const void *buffer, unsigned long count)
189 {
190 	const unsigned int *buf=buffer;
191 	while(count--) microdev_outl(*buf++, port);
192 }
193 
194 
195 /*
196  * map I/O ports to memory-mapped addresses
197  */
microdev_isa_port2addr(unsigned long offset)198 unsigned long microdev_isa_port2addr(unsigned long offset)
199 {
200 	unsigned long result;
201 
202 	if ( (offset>=IO_LAN91C111) && (offset<IO_LAN91C111+0x10ul) )
203 	{
204 			/*
205 			 *	SMSC LAN91C111 Ethernet chip
206 			 */
207 		result = 0xa7500000ul + offset - IO_LAN91C111;
208 	}
209 	else /* if (offset <= 0xfffful) */
210 	{
211 			/*
212 			 *	safe default.
213 			 */
214 		result = PVR;
215 	}
216 
217 	return result;
218 }
219 
220