io.c 9.5 KB
Newer Older
L
Linus Torvalds 已提交
1
/*
2
 * linux/arch/sh/boards/superh/microdev/io.c
L
Linus Torvalds 已提交
3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
 *
 * Copyright (C) 2003 Sean McGoogan (Sean.McGoogan@superh.com)
 * Copyright (C) 2003, 2004 SuperH, Inc.
 * Copyright (C) 2004 Paul Mundt
 *
 * SuperH SH4-202 MicroDev board support.
 *
 * May be copied or modified under the terms of the GNU General Public
 * License.  See linux/COPYING for more information.
 */

#include <linux/init.h>
#include <linux/pci.h>
#include <linux/wait.h>
#include <asm/io.h>
18
#include <asm/microdev.h>
L
Linus Torvalds 已提交
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

	/*
	 *	we need to have a 'safe' address to re-direct all I/O requests
	 *	that we do not explicitly wish to handle. This safe address
	 *	must have the following properies:
	 *
	 *		* writes are ignored (no exception)
	 *		* reads are benign (no side-effects)
	 *		* accesses of width 1, 2 and 4-bytes are all valid.
	 *
	 *	The Processor Version Register (PVR) has these properties.
	 */
#define	PVR	0xff000030	/* Processor Version Register */


#define	IO_IDE2_BASE		0x170ul	/* I/O base for SMSC FDC37C93xAPM IDE #2 */
#define	IO_IDE1_BASE		0x1f0ul	/* I/O base for SMSC FDC37C93xAPM IDE #1 */
#define IO_ISP1161_BASE		0x290ul /* I/O port for Philips ISP1161x USB chip */
#define IO_SERIAL2_BASE		0x2f8ul /* I/O base for SMSC FDC37C93xAPM Serial #2 */
#define	IO_LAN91C111_BASE	0x300ul	/* I/O base for SMSC LAN91C111 Ethernet chip */
#define	IO_IDE2_MISC		0x376ul	/* I/O misc for SMSC FDC37C93xAPM IDE #2 */
#define IO_SUPERIO_BASE		0x3f0ul /* I/O base for SMSC FDC37C93xAPM SuperIO chip */
#define	IO_IDE1_MISC		0x3f6ul	/* I/O misc for SMSC FDC37C93xAPM IDE #1 */
#define IO_SERIAL1_BASE		0x3f8ul /* I/O base for SMSC FDC37C93xAPM Serial #1 */

#define	IO_ISP1161_EXTENT	0x04ul	/* I/O extent for Philips ISP1161x USB chip */
#define	IO_LAN91C111_EXTENT	0x10ul	/* I/O extent for SMSC LAN91C111 Ethernet chip */
#define	IO_SUPERIO_EXTENT	0x02ul	/* I/O extent for SMSC FDC37C93xAPM SuperIO chip */
#define	IO_IDE_EXTENT		0x08ul	/* I/O extent for IDE Task Register set */
#define IO_SERIAL_EXTENT	0x10ul

#define	IO_LAN91C111_PHYS	0xa7500000ul	/* Physical address of SMSC LAN91C111 Ethernet chip */
#define	IO_ISP1161_PHYS		0xa7700000ul	/* Physical address of Philips ISP1161x USB chip */
#define	IO_SUPERIO_PHYS		0xa7800000ul	/* Physical address of SMSC FDC37C93xAPM SuperIO chip */

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
/*
 * map I/O ports to memory-mapped addresses
 */
static unsigned long microdev_isa_port2addr(unsigned long offset)
{
	unsigned long result;

	if ((offset >= IO_LAN91C111_BASE) &&
	    (offset <  IO_LAN91C111_BASE + IO_LAN91C111_EXTENT)) {
			/*
			 *	SMSC LAN91C111 Ethernet chip
			 */
		result = IO_LAN91C111_PHYS + offset - IO_LAN91C111_BASE;
	} else if ((offset >= IO_SUPERIO_BASE) &&
		   (offset <  IO_SUPERIO_BASE + IO_SUPERIO_EXTENT)) {
			/*
			 *	SMSC FDC37C93xAPM SuperIO chip
			 *
			 *	Configuration Registers
			 */
		result = IO_SUPERIO_PHYS + (offset << 1);
#if 0
	} else if (offset == KBD_DATA_REG || offset == KBD_CNTL_REG ||
		   offset == KBD_STATUS_REG) {
			/*
			 *	SMSC FDC37C93xAPM SuperIO chip
			 *
			 *	PS/2 Keyboard + Mouse (ports 0x60 and 0x64).
			 */
	        result = IO_SUPERIO_PHYS + (offset << 1);
#endif
	} else if (((offset >= IO_IDE1_BASE) &&
		    (offset <  IO_IDE1_BASE + IO_IDE_EXTENT)) ||
		    (offset == IO_IDE1_MISC)) {
			/*
			 *	SMSC FDC37C93xAPM SuperIO chip
			 *
			 *	IDE #1
			 */
	        result = IO_SUPERIO_PHYS + (offset << 1);
	} else if (((offset >= IO_IDE2_BASE) &&
		    (offset <  IO_IDE2_BASE + IO_IDE_EXTENT)) ||
		    (offset == IO_IDE2_MISC)) {
			/*
			 *	SMSC FDC37C93xAPM SuperIO chip
			 *
			 *	IDE #2
			 */
	        result = IO_SUPERIO_PHYS + (offset << 1);
	} else if ((offset >= IO_SERIAL1_BASE) &&
		   (offset <  IO_SERIAL1_BASE + IO_SERIAL_EXTENT)) {
			/*
			 *	SMSC FDC37C93xAPM SuperIO chip
			 *
			 *	Serial #1
			 */
		result = IO_SUPERIO_PHYS + (offset << 1);
	} else if ((offset >= IO_SERIAL2_BASE) &&
		   (offset <  IO_SERIAL2_BASE + IO_SERIAL_EXTENT)) {
			/*
			 *	SMSC FDC37C93xAPM SuperIO chip
			 *
			 *	Serial #2
			 */
		result = IO_SUPERIO_PHYS + (offset << 1);
	} else if ((offset >= IO_ISP1161_BASE) &&
		   (offset < IO_ISP1161_BASE + IO_ISP1161_EXTENT)) {
			/*
			 *	Philips USB ISP1161x chip
			 */
		result = IO_ISP1161_PHYS + offset - IO_ISP1161_BASE;
	} else {
			/*
			 *	safe default.
			 */
		printk("Warning: unexpected port in %s( offset = 0x%lx )\n",
130
		       __func__, offset);
131 132 133 134 135
		result = PVR;
	}

	return result;
}
L
Linus Torvalds 已提交
136

137
#define PORT2ADDR(x) (microdev_isa_port2addr(x))
L
Linus Torvalds 已提交
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

static inline void delay(void)
{
#if defined(CONFIG_PCI)
	/* System board present, just make a dummy SRAM access.  (CS0 will be
	   mapped to PCI memory, probably good to avoid it.) */
	ctrl_inw(0xa6800000);
#else
	/* CS0 will be mapped to flash, ROM etc so safe to access it. */
	ctrl_inw(0xa0000000);
#endif
}

unsigned char microdev_inb(unsigned long port)
{
#ifdef CONFIG_PCI
	if (port >= PCIBIOS_MIN_IO)
		return microdev_pci_inb(port);
#endif
	return *(volatile unsigned char*)PORT2ADDR(port);
}

unsigned short microdev_inw(unsigned long port)
{
#ifdef CONFIG_PCI
	if (port >= PCIBIOS_MIN_IO)
		return microdev_pci_inw(port);
#endif
	return *(volatile unsigned short*)PORT2ADDR(port);
}

unsigned int microdev_inl(unsigned long port)
{
#ifdef CONFIG_PCI
	if (port >= PCIBIOS_MIN_IO)
		return microdev_pci_inl(port);
#endif
	return *(volatile unsigned int*)PORT2ADDR(port);
}

178 179 180 181 182 183 184 185 186 187 188
void microdev_outw(unsigned short b, unsigned long port)
{
#ifdef CONFIG_PCI
	if (port >= PCIBIOS_MIN_IO) {
		microdev_pci_outw(b, port);
		return;
	}
#endif
	*(volatile unsigned short*)PORT2ADDR(port) = b;
}

L
Linus Torvalds 已提交
189 190 191 192 193 194 195 196 197 198 199 200
void microdev_outb(unsigned char b, unsigned long port)
{
#ifdef CONFIG_PCI
	if (port >= PCIBIOS_MIN_IO) {
		microdev_pci_outb(b, port);
		return;
	}
#endif

	/*
	 *	There is a board feature with the current SH4-202 MicroDev in
	 *	that the 2 byte enables (nBE0 and nBE1) are tied together (and
S
Simon Arlott 已提交
201
	 *	to the Chip Select Line (Ethernet_CS)). Due to this connectivity,
L
Linus Torvalds 已提交
202 203 204
	 *	it is not possible to safely perform 8-bit writes to the
	 *	Ethernet registers, as 16-bits will be consumed from the Data
	 *	lines (corrupting the other byte).  Hence, this function is
S
Simon Arlott 已提交
205 206
	 *	written to implement 16-bit read/modify/write for all byte-wide
	 *	accesses.
L
Linus Torvalds 已提交
207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367
	 *
	 *	Note: there is no problem with byte READS (even or odd).
	 *
	 *			Sean McGoogan - 16th June 2003.
	 */
	if ((port >= IO_LAN91C111_BASE) &&
	    (port <  IO_LAN91C111_BASE + IO_LAN91C111_EXTENT)) {
			/*
			 * Then are trying to perform a byte-write to the
			 * LAN91C111.  This needs special care.
			 */
		if (port % 2 == 1) {	/* is the port odd ? */
			/* unset bit-0, i.e. make even */
			const unsigned long evenPort = port-1;
			unsigned short word;

			/*
			 * do a 16-bit read/write to write to 'port',
			 * preserving even byte.
			 *
			 *	Even addresses are bits 0-7
			 *	Odd  addresses are bits 8-15
			 */
			word = microdev_inw(evenPort);
			word = (word & 0xffu) | (b << 8);
			microdev_outw(word, evenPort);
		} else {
			/* else, we are trying to do an even byte write */
			unsigned short word;

			/*
			 * do a 16-bit read/write to write to 'port',
			 * preserving odd byte.
			 *
			 *	Even addresses are bits 0-7
			 *	Odd  addresses are bits 8-15
			 */
			word = microdev_inw(port);
			word = (word & 0xff00u) | (b);
			microdev_outw(word, port);
		}
	} else {
		*(volatile unsigned char*)PORT2ADDR(port) = b;
	}
}

void microdev_outl(unsigned int b, unsigned long port)
{
#ifdef CONFIG_PCI
	if (port >= PCIBIOS_MIN_IO) {
		microdev_pci_outl(b, port);
		return;
	}
#endif
	*(volatile unsigned int*)PORT2ADDR(port) = b;
}

unsigned char microdev_inb_p(unsigned long port)
{
	unsigned char v = microdev_inb(port);
	delay();
	return v;
}

unsigned short microdev_inw_p(unsigned long port)
{
	unsigned short v = microdev_inw(port);
	delay();
	return v;
}

unsigned int microdev_inl_p(unsigned long port)
{
	unsigned int v = microdev_inl(port);
	delay();
	return v;
}

void microdev_outb_p(unsigned char b, unsigned long port)
{
	microdev_outb(b, port);
	delay();
}

void microdev_outw_p(unsigned short b, unsigned long port)
{
	microdev_outw(b, port);
	delay();
}

void microdev_outl_p(unsigned int b, unsigned long port)
{
	microdev_outl(b, port);
	delay();
}

void microdev_insb(unsigned long port, void *buffer, unsigned long count)
{
	volatile unsigned char *port_addr;
	unsigned char *buf = buffer;

	port_addr = (volatile unsigned char *)PORT2ADDR(port);

	while (count--)
		*buf++ = *port_addr;
}

void microdev_insw(unsigned long port, void *buffer, unsigned long count)
{
	volatile unsigned short *port_addr;
	unsigned short *buf = buffer;

	port_addr = (volatile unsigned short *)PORT2ADDR(port);

	while (count--)
		*buf++ = *port_addr;
}

void microdev_insl(unsigned long port, void *buffer, unsigned long count)
{
	volatile unsigned long *port_addr;
	unsigned int *buf = buffer;

	port_addr = (volatile unsigned long *)PORT2ADDR(port);

	while (count--)
		*buf++ = *port_addr;
}

void microdev_outsb(unsigned long port, const void *buffer, unsigned long count)
{
	volatile unsigned char *port_addr;
	const unsigned char *buf = buffer;

	port_addr = (volatile unsigned char *)PORT2ADDR(port);

	while (count--)
		*port_addr = *buf++;
}

void microdev_outsw(unsigned long port, const void *buffer, unsigned long count)
{
	volatile unsigned short *port_addr;
	const unsigned short *buf = buffer;

	port_addr = (volatile unsigned short *)PORT2ADDR(port);

	while (count--)
		*port_addr = *buf++;
}

void microdev_outsl(unsigned long port, const void *buffer, unsigned long count)
{
	volatile unsigned long *port_addr;
	const unsigned int *buf = buffer;

	port_addr = (volatile unsigned long *)PORT2ADDR(port);

	while (count--)
		*port_addr = *buf++;
}