Commit | Line | Data |
---|---|---|
73b4390f RB |
1 | /* |
2 | * BRIEF MODULE DESCRIPTION | |
3 | * pci_ops for IDT EB434 board | |
4 | * | |
5 | * Copyright 2004 IDT Inc. (rischelp@idt.com) | |
6 | * Copyright 2006 Felix Fietkau <nbd@openwrt.org> | |
7 | * | |
8 | * This program is free software; you can redistribute it and/or modify it | |
9 | * under the terms of the GNU General Public License as published by the | |
10 | * Free Software Foundation; either version 2 of the License, or (at your | |
11 | * option) any later version. | |
12 | * | |
13 | * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED | |
14 | * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF | |
15 | * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN | |
16 | * NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, | |
17 | * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT | |
18 | * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF | |
19 | * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON | |
20 | * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT | |
21 | * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF | |
22 | * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | |
23 | * | |
24 | * You should have received a copy of the GNU General Public License along | |
25 | * with this program; if not, write to the Free Software Foundation, Inc., | |
26 | * 675 Mass Ave, Cambridge, MA 02139, USA. | |
27 | */ | |
28 | #include <linux/delay.h> | |
29 | #include <linux/init.h> | |
30 | #include <linux/io.h> | |
31 | #include <linux/pci.h> | |
32 | #include <linux/types.h> | |
33 | ||
34 | #include <asm/cpu.h> | |
35 | #include <asm/mach-rc32434/rc32434.h> | |
36 | #include <asm/mach-rc32434/pci.h> | |
37 | ||
38 | #define PCI_ACCESS_READ 0 | |
39 | #define PCI_ACCESS_WRITE 1 | |
40 | ||
41 | ||
42 | #define PCI_CFG_SET(bus, slot, func, off) \ | |
43 | (rc32434_pci->pcicfga = (0x80000000 | \ | |
44 | ((bus) << 16) | ((slot)<<11) | \ | |
45 | ((func)<<8) | (off))) | |
46 | ||
47 | static inline int config_access(unsigned char access_type, | |
48 | struct pci_bus *bus, unsigned int devfn, | |
49 | unsigned char where, u32 *data) | |
50 | { | |
51 | unsigned int slot = PCI_SLOT(devfn); | |
52 | u8 func = PCI_FUNC(devfn); | |
53 | ||
54 | /* Setup address */ | |
55 | PCI_CFG_SET(bus->number, slot, func, where); | |
56 | rc32434_sync(); | |
57 | ||
58 | if (access_type == PCI_ACCESS_WRITE) | |
59 | rc32434_pci->pcicfgd = *data; | |
60 | else | |
61 | *data = rc32434_pci->pcicfgd; | |
62 | ||
63 | rc32434_sync(); | |
64 | ||
65 | return 0; | |
66 | } | |
67 | ||
68 | ||
69 | /* | |
70 | * We can't address 8 and 16 bit words directly. Instead we have to | |
71 | * read/write a 32bit word and mask/modify the data we actually want. | |
72 | */ | |
73 | static int read_config_byte(struct pci_bus *bus, unsigned int devfn, | |
74 | int where, u8 *val) | |
75 | { | |
76 | u32 data; | |
77 | int ret; | |
78 | ||
79 | ret = config_access(PCI_ACCESS_READ, bus, devfn, where, &data); | |
80 | *val = (data >> ((where & 3) << 3)) & 0xff; | |
81 | return ret; | |
82 | } | |
83 | ||
84 | static int read_config_word(struct pci_bus *bus, unsigned int devfn, | |
85 | int where, u16 *val) | |
86 | { | |
87 | u32 data; | |
88 | int ret; | |
89 | ||
90 | ret = config_access(PCI_ACCESS_READ, bus, devfn, where, &data); | |
91 | *val = (data >> ((where & 3) << 3)) & 0xffff; | |
92 | return ret; | |
93 | } | |
94 | ||
95 | static int read_config_dword(struct pci_bus *bus, unsigned int devfn, | |
96 | int where, u32 *val) | |
97 | { | |
98 | int ret; | |
99 | int delay = 1; | |
100 | ||
101 | /* | |
102 | * Don't scan too far, else there will be errors with plugged in | |
103 | * daughterboard (rb564). | |
104 | */ | |
105 | if (bus->number == 0 && PCI_SLOT(devfn) > 21) | |
106 | return 0; | |
107 | ||
108 | retry: | |
109 | ret = config_access(PCI_ACCESS_READ, bus, devfn, where, val); | |
110 | ||
111 | /* | |
112 | * Certain devices react delayed at device scan time, this | |
113 | * gives them time to settle | |
114 | */ | |
115 | if (where == PCI_VENDOR_ID) { | |
116 | if (ret == 0xffffffff || ret == 0x00000000 || | |
117 | ret == 0x0000ffff || ret == 0xffff0000) { | |
118 | if (delay > 4) | |
119 | return 0; | |
120 | delay *= 2; | |
121 | msleep(delay); | |
122 | goto retry; | |
123 | } | |
124 | } | |
125 | ||
126 | return ret; | |
127 | } | |
128 | ||
129 | static int | |
130 | write_config_byte(struct pci_bus *bus, unsigned int devfn, int where, | |
131 | u8 val) | |
132 | { | |
133 | u32 data = 0; | |
134 | ||
135 | if (config_access(PCI_ACCESS_READ, bus, devfn, where, &data)) | |
136 | return -1; | |
137 | ||
138 | data = (data & ~(0xff << ((where & 3) << 3))) | | |
139 | (val << ((where & 3) << 3)); | |
140 | ||
141 | if (config_access(PCI_ACCESS_WRITE, bus, devfn, where, &data)) | |
142 | return -1; | |
143 | ||
144 | return PCIBIOS_SUCCESSFUL; | |
145 | } | |
146 | ||
147 | ||
148 | static int | |
149 | write_config_word(struct pci_bus *bus, unsigned int devfn, int where, | |
150 | u16 val) | |
151 | { | |
152 | u32 data = 0; | |
153 | ||
154 | if (config_access(PCI_ACCESS_READ, bus, devfn, where, &data)) | |
155 | return -1; | |
156 | ||
157 | data = (data & ~(0xffff << ((where & 3) << 3))) | | |
158 | (val << ((where & 3) << 3)); | |
159 | ||
160 | if (config_access(PCI_ACCESS_WRITE, bus, devfn, where, &data)) | |
161 | return -1; | |
162 | ||
163 | ||
164 | return PCIBIOS_SUCCESSFUL; | |
165 | } | |
166 | ||
167 | ||
168 | static int | |
169 | write_config_dword(struct pci_bus *bus, unsigned int devfn, int where, | |
170 | u32 val) | |
171 | { | |
172 | if (config_access(PCI_ACCESS_WRITE, bus, devfn, where, &val)) | |
173 | return -1; | |
174 | ||
175 | return PCIBIOS_SUCCESSFUL; | |
176 | } | |
177 | ||
178 | static int pci_config_read(struct pci_bus *bus, unsigned int devfn, | |
179 | int where, int size, u32 *val) | |
180 | { | |
181 | switch (size) { | |
182 | case 1: | |
183 | return read_config_byte(bus, devfn, where, (u8 *) val); | |
184 | case 2: | |
185 | return read_config_word(bus, devfn, where, (u16 *) val); | |
186 | default: | |
187 | return read_config_dword(bus, devfn, where, val); | |
188 | } | |
189 | } | |
190 | ||
191 | static int pci_config_write(struct pci_bus *bus, unsigned int devfn, | |
192 | int where, int size, u32 val) | |
193 | { | |
194 | switch (size) { | |
195 | case 1: | |
196 | return write_config_byte(bus, devfn, where, (u8) val); | |
197 | case 2: | |
198 | return write_config_word(bus, devfn, where, (u16) val); | |
199 | default: | |
200 | return write_config_dword(bus, devfn, where, val); | |
201 | } | |
202 | } | |
203 | ||
204 | struct pci_ops rc32434_pci_ops = { | |
205 | .read = pci_config_read, | |
206 | .write = pci_config_write, | |
207 | }; |