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 217 218 219
|
/*
* linux/arch/sh/kernel/io_microdev.c
*
* Copyright (C) 2003 Sean McGoogan (Sean.McGoogan@superh.com)
*
* 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/config.h>
#include <linux/init.h>
#include <asm/io.h>
/*
* 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_LAN91C111 0x300ul /* I/O port for SMSC LAN91C111 Ethernet chip */
#define PORT2ADDR(x) (microdev_isa_port2addr(x))
static inline void delay(void)
{
ctrl_inw(0xa0000000);
}
unsigned char microdev_inb(unsigned long port)
{
return *(volatile unsigned char*)PORT2ADDR(port);
}
unsigned short microdev_inw(unsigned long port)
{
return *(volatile unsigned short*)PORT2ADDR(port);
}
unsigned int microdev_inl(unsigned long port)
{
return *(volatile unsigned int*)PORT2ADDR(port);
}
void microdev_outb(unsigned char b, unsigned long port)
{
/*
* There is a board feature with the current SH4-202 MicroDev
* in that the 2 byte enables (nBE0 and nBE1) are tied together (and to the
* Chip Select Line (Ethernet_CS)). Due to this conectivity, 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 written to impliment 16-bit read/modify/write
* for all byte-wide acceses.
*
* Note: there is no problem with byte READS (even or odd).
*
* Sean McGoogan - 16th June 2003.
*/
if ( (port>=IO_LAN91C111) && (port<IO_LAN91C111+0x10ul) )
{
/*
* Then are trying to perform a byte-write to the LAN91C111.
* This needs special care.
*/
if (port%2==1) /* is the port odd ? */
{
const unsigned long evenPort = port-1; /* unset bit-0, i.e. make even */
unsigned short word; /* temp variable */
/*
* 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; /* temp variable */
/*
* 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_outw(unsigned short b, unsigned long port)
{
*(volatile unsigned short*)PORT2ADDR(port) = b;
}
void microdev_outl(unsigned int b, unsigned long port)
{
*(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)
{
unsigned char *buf=buffer;
while(count--) *buf++=microdev_inb(port);
}
void microdev_insw(unsigned long port, void *buffer, unsigned long count)
{
unsigned short *buf=buffer;
while(count--) *buf++=microdev_inw(port);
}
void microdev_insl(unsigned long port, void *buffer, unsigned long count)
{
unsigned int *buf=buffer;
while(count--) *buf++=microdev_inl(port);
}
void microdev_outsb(unsigned long port, const void *buffer, unsigned long count)
{
const unsigned char *buf=buffer;
while(count--) microdev_outb(*buf++, port);
}
void microdev_outsw(unsigned long port, const void *buffer, unsigned long count)
{
const unsigned short *buf=buffer;
while(count--) microdev_outw(*buf++, port);
}
void microdev_outsl(unsigned long port, const void *buffer, unsigned long count)
{
const unsigned int *buf=buffer;
while(count--) microdev_outl(*buf++, port);
}
/*
* map I/O ports to memory-mapped addresses
*/
unsigned long microdev_isa_port2addr(unsigned long offset)
{
unsigned long result;
if ( (offset>=IO_LAN91C111) && (offset<IO_LAN91C111+0x10ul) )
{
/*
* SMSC LAN91C111 Ethernet chip
*/
result = 0xa7500000ul + offset - IO_LAN91C111;
}
else /* if (offset <= 0xfffful) */
{
/*
* safe default.
*/
result = PVR;
}
return result;
}
|