aboutsummaryrefslogtreecommitdiffstats
path: root/arch/powerpc/sysdev
diff options
context:
space:
mode:
authorLinus Torvalds <torvalds@g5.osdl.org>2006-06-29 14:32:34 -0400
committerLinus Torvalds <torvalds@g5.osdl.org>2006-06-29 14:32:34 -0400
commit3aa590c6b7c89d844f81c2e96f295cf2c6967773 (patch)
tree6f18b295b1ff4cd7fd1880db6f56721599d64439 /arch/powerpc/sysdev
parent4d3ce21fa9d2eaeda113aa2f9c2da80d972bef64 (diff)
parent339d76c54336443f5050b00172beb675f35e3be0 (diff)
Merge git://git.kernel.org/pub/scm/linux/kernel/git/paulus/powerpc
* git://git.kernel.org/pub/scm/linux/kernel/git/paulus/powerpc: (43 commits) [POWERPC] Use little-endian bit from firmware ibm,pa-features property [POWERPC] Make sure smp_processor_id works very early in boot [POWERPC] U4 DART improvements [POWERPC] todc: add support for Time-Of-Day-Clock [POWERPC] Make lparcfg.c work when both iseries and pseries are selected [POWERPC] Fix idr locking in init_new_context [POWERPC] mpc7448hpc2 (taiga) board config file [POWERPC] Add tsi108 pci and platform device data register function [POWERPC] Add general support for mpc7448hpc2 (Taiga) platform [POWERPC] Correct the MAX_CONTEXT definition powerpc: minor cleanups for mpc86xx [POWERPC] Make sure we select CONFIG_NEW_LEDS if ADB_PMU_LED is set [POWERPC] Simplify the code defining the 64-bit CPU features [POWERPC] powerpc: kconfig warning fix [POWERPC] Consolidate some of kernel/misc*.S [POWERPC] Remove unused function call_with_mmu_off [POWERPC] update asm-powerpc/time.h [POWERPC] Clean up it_lp_queue.h [POWERPC] Skip the "copy down" of the kernel if it is already at zero. [POWERPC] Add the use of the firmware soft-reset-nmi to kdump. ...
Diffstat (limited to 'arch/powerpc/sysdev')
-rw-r--r--arch/powerpc/sysdev/Makefile2
-rw-r--r--arch/powerpc/sysdev/dart.h6
-rw-r--r--arch/powerpc/sysdev/dart_iommu.c49
-rw-r--r--arch/powerpc/sysdev/todc.c392
-rw-r--r--arch/powerpc/sysdev/tsi108_dev.c145
-rw-r--r--arch/powerpc/sysdev/tsi108_pci.c412
6 files changed, 999 insertions, 7 deletions
diff --git a/arch/powerpc/sysdev/Makefile b/arch/powerpc/sysdev/Makefile
index cef95b023730..054bd8b41ef5 100644
--- a/arch/powerpc/sysdev/Makefile
+++ b/arch/powerpc/sysdev/Makefile
@@ -12,3 +12,5 @@ obj-$(CONFIG_U3_DART) += dart_iommu.o
12obj-$(CONFIG_MMIO_NVRAM) += mmio_nvram.o 12obj-$(CONFIG_MMIO_NVRAM) += mmio_nvram.o
13obj-$(CONFIG_PPC_83xx) += ipic.o 13obj-$(CONFIG_PPC_83xx) += ipic.o
14obj-$(CONFIG_FSL_SOC) += fsl_soc.o 14obj-$(CONFIG_FSL_SOC) += fsl_soc.o
15obj-$(CONFIG_PPC_TODC) += todc.o
16obj-$(CONFIG_TSI108_BRIDGE) += tsi108_pci.o tsi108_dev.o
diff --git a/arch/powerpc/sysdev/dart.h b/arch/powerpc/sysdev/dart.h
index c2d05763ccbe..1c8817c4835e 100644
--- a/arch/powerpc/sysdev/dart.h
+++ b/arch/powerpc/sysdev/dart.h
@@ -47,8 +47,12 @@
47/* U4 registers */ 47/* U4 registers */
48#define DART_BASE_U4_BASE_MASK 0xffffff 48#define DART_BASE_U4_BASE_MASK 0xffffff
49#define DART_BASE_U4_BASE_SHIFT 0 49#define DART_BASE_U4_BASE_SHIFT 0
50#define DART_CNTL_U4_FLUSHTLB 0x20000000
51#define DART_CNTL_U4_ENABLE 0x80000000 50#define DART_CNTL_U4_ENABLE 0x80000000
51#define DART_CNTL_U4_IONE 0x40000000
52#define DART_CNTL_U4_FLUSHTLB 0x20000000
53#define DART_CNTL_U4_IDLE 0x10000000
54#define DART_CNTL_U4_PAR_EN 0x08000000
55#define DART_CNTL_U4_IONE_MASK 0x07ffffff
52#define DART_SIZE_U4_SIZE_MASK 0x1fff 56#define DART_SIZE_U4_SIZE_MASK 0x1fff
53#define DART_SIZE_U4_SIZE_SHIFT 0 57#define DART_SIZE_U4_SIZE_SHIFT 0
54 58
diff --git a/arch/powerpc/sysdev/dart_iommu.c b/arch/powerpc/sysdev/dart_iommu.c
index 6232091cc72b..7c7f34ce4986 100644
--- a/arch/powerpc/sysdev/dart_iommu.c
+++ b/arch/powerpc/sysdev/dart_iommu.c
@@ -101,8 +101,8 @@ retry:
101 if (l == (1L << limit)) { 101 if (l == (1L << limit)) {
102 if (limit < 4) { 102 if (limit < 4) {
103 limit++; 103 limit++;
104 reg = DART_IN(DART_CNTL); 104 reg = DART_IN(DART_CNTL);
105 reg &= ~inv_bit; 105 reg &= ~inv_bit;
106 DART_OUT(DART_CNTL, reg); 106 DART_OUT(DART_CNTL, reg);
107 goto retry; 107 goto retry;
108 } else 108 } else
@@ -111,11 +111,39 @@ retry:
111 } 111 }
112} 112}
113 113
114static inline void dart_tlb_invalidate_one(unsigned long bus_rpn)
115{
116 unsigned int reg;
117 unsigned int l, limit;
118
119 reg = DART_CNTL_U4_ENABLE | DART_CNTL_U4_IONE |
120 (bus_rpn & DART_CNTL_U4_IONE_MASK);
121 DART_OUT(DART_CNTL, reg);
122
123 limit = 0;
124wait_more:
125 l = 0;
126 while ((DART_IN(DART_CNTL) & DART_CNTL_U4_IONE) && l < (1L << limit)) {
127 rmb();
128 l++;
129 }
130
131 if (l == (1L << limit)) {
132 if (limit < 4) {
133 limit++;
134 goto wait_more;
135 } else
136 panic("DART: TLB did not flush after waiting a long "
137 "time. Buggy U4 ?");
138 }
139}
140
114static void dart_flush(struct iommu_table *tbl) 141static void dart_flush(struct iommu_table *tbl)
115{ 142{
116 if (dart_dirty) 143 if (dart_dirty) {
117 dart_tlb_invalidate_all(); 144 dart_tlb_invalidate_all();
118 dart_dirty = 0; 145 dart_dirty = 0;
146 }
119} 147}
120 148
121static void dart_build(struct iommu_table *tbl, long index, 149static void dart_build(struct iommu_table *tbl, long index,
@@ -124,6 +152,7 @@ static void dart_build(struct iommu_table *tbl, long index,
124{ 152{
125 unsigned int *dp; 153 unsigned int *dp;
126 unsigned int rpn; 154 unsigned int rpn;
155 long l;
127 156
128 DBG("dart: build at: %lx, %lx, addr: %x\n", index, npages, uaddr); 157 DBG("dart: build at: %lx, %lx, addr: %x\n", index, npages, uaddr);
129 158
@@ -135,7 +164,8 @@ static void dart_build(struct iommu_table *tbl, long index,
135 /* On U3, all memory is contigous, so we can move this 164 /* On U3, all memory is contigous, so we can move this
136 * out of the loop. 165 * out of the loop.
137 */ 166 */
138 while (npages--) { 167 l = npages;
168 while (l--) {
139 rpn = virt_to_abs(uaddr) >> DART_PAGE_SHIFT; 169 rpn = virt_to_abs(uaddr) >> DART_PAGE_SHIFT;
140 170
141 *(dp++) = DARTMAP_VALID | (rpn & DARTMAP_RPNMASK); 171 *(dp++) = DARTMAP_VALID | (rpn & DARTMAP_RPNMASK);
@@ -143,7 +173,14 @@ static void dart_build(struct iommu_table *tbl, long index,
143 uaddr += DART_PAGE_SIZE; 173 uaddr += DART_PAGE_SIZE;
144 } 174 }
145 175
146 dart_dirty = 1; 176 if (dart_is_u4) {
177 rpn = index;
178 mb(); /* make sure all updates have reached memory */
179 while (npages--)
180 dart_tlb_invalidate_one(rpn++);
181 } else {
182 dart_dirty = 1;
183 }
147} 184}
148 185
149 186
diff --git a/arch/powerpc/sysdev/todc.c b/arch/powerpc/sysdev/todc.c
new file mode 100644
index 000000000000..0a65980efb50
--- /dev/null
+++ b/arch/powerpc/sysdev/todc.c
@@ -0,0 +1,392 @@
1/*
2 * Time of Day Clock support for the M48T35, M48T37, M48T59, and MC146818
3 * Real Time Clocks/Timekeepers.
4 *
5 * Author: Mark A. Greer <mgreer@mvista.com>
6 *
7 * 2001-2004 (c) MontaVista, Software, Inc. This file is licensed under
8 * the terms of the GNU General Public License version 2. This program
9 * is licensed "as is" without any warranty of any kind, whether express
10 * or implied.
11 */
12#include <linux/errno.h>
13#include <linux/init.h>
14#include <linux/kernel.h>
15#include <linux/time.h>
16#include <linux/timex.h>
17#include <linux/bcd.h>
18#include <linux/mc146818rtc.h>
19
20#include <asm/machdep.h>
21#include <asm/io.h>
22#include <asm/time.h>
23#include <asm/todc.h>
24
25/*
26 * Depending on the hardware on your board and your board design, the
27 * RTC/NVRAM may be accessed either directly (like normal memory) or via
28 * address/data registers. If your board uses the direct method, set
29 * 'nvram_data' to the base address of your nvram and leave 'nvram_as0' and
30 * 'nvram_as1' NULL. If your board uses address/data regs to access nvram,
31 * set 'nvram_as0' to the address of the lower byte, set 'nvram_as1' to the
32 * address of the upper byte (leave NULL if using mc146818), and set
33 * 'nvram_data' to the address of the 8-bit data register.
34 *
35 * Note: Even though the documentation for the various RTC chips say that it
36 * take up to a second before it starts updating once the 'R' bit is
37 * cleared, they always seem to update even though we bang on it many
38 * times a second. This is true, except for the Dallas Semi 1746/1747
39 * (possibly others). Those chips seem to have a real problem whenever
40 * we set the 'R' bit before reading them, they basically stop counting.
41 * --MAG
42 */
43
44/*
45 * 'todc_info' should be initialized in your *_setup.c file to
46 * point to a fully initialized 'todc_info_t' structure.
47 * This structure holds all the register offsets for your particular
48 * TODC/RTC chip.
49 * TODC_ALLOC()/TODC_INIT() will allocate and initialize this table for you.
50 */
51
52#ifdef RTC_FREQ_SELECT
53#undef RTC_FREQ_SELECT
54#define RTC_FREQ_SELECT control_b /* Register A */
55#endif
56
57#ifdef RTC_CONTROL
58#undef RTC_CONTROL
59#define RTC_CONTROL control_a /* Register B */
60#endif
61
62#ifdef RTC_INTR_FLAGS
63#undef RTC_INTR_FLAGS
64#define RTC_INTR_FLAGS watchdog /* Register C */
65#endif
66
67#ifdef RTC_VALID
68#undef RTC_VALID
69#define RTC_VALID interrupts /* Register D */
70#endif
71
72/* Access routines when RTC accessed directly (like normal memory) */
73u_char
74todc_direct_read_val(int addr)
75{
76 return readb((void __iomem *)(todc_info->nvram_data + addr));
77}
78
79void
80todc_direct_write_val(int addr, unsigned char val)
81{
82 writeb(val, (void __iomem *)(todc_info->nvram_data + addr));
83 return;
84}
85
86/* Access routines for accessing m48txx type chips via addr/data regs */
87u_char
88todc_m48txx_read_val(int addr)
89{
90 outb(addr, todc_info->nvram_as0);
91 outb(addr>>todc_info->as0_bits, todc_info->nvram_as1);
92 return inb(todc_info->nvram_data);
93}
94
95void
96todc_m48txx_write_val(int addr, unsigned char val)
97{
98 outb(addr, todc_info->nvram_as0);
99 outb(addr>>todc_info->as0_bits, todc_info->nvram_as1);
100 outb(val, todc_info->nvram_data);
101 return;
102}
103
104/* Access routines for accessing mc146818 type chips via addr/data regs */
105u_char
106todc_mc146818_read_val(int addr)
107{
108 outb_p(addr, todc_info->nvram_as0);
109 return inb_p(todc_info->nvram_data);
110}
111
112void
113todc_mc146818_write_val(int addr, unsigned char val)
114{
115 outb_p(addr, todc_info->nvram_as0);
116 outb_p(val, todc_info->nvram_data);
117}
118
119
120/*
121 * Routines to make RTC chips with NVRAM buried behind an addr/data pair
122 * have the NVRAM and clock regs appear at the same level.
123 * The NVRAM will appear to start at addr 0 and the clock regs will appear
124 * to start immediately after the NVRAM (actually, start at offset
125 * todc_info->nvram_size).
126 */
127static inline u_char
128todc_read_val(int addr)
129{
130 u_char val;
131
132 if (todc_info->sw_flags & TODC_FLAG_2_LEVEL_NVRAM) {
133 if (addr < todc_info->nvram_size) { /* NVRAM */
134 ppc_md.rtc_write_val(todc_info->nvram_addr_reg, addr);
135 val = ppc_md.rtc_read_val(todc_info->nvram_data_reg);
136 } else { /* Clock Reg */
137 addr -= todc_info->nvram_size;
138 val = ppc_md.rtc_read_val(addr);
139 }
140 } else
141 val = ppc_md.rtc_read_val(addr);
142
143 return val;
144}
145
146static inline void
147todc_write_val(int addr, u_char val)
148{
149 if (todc_info->sw_flags & TODC_FLAG_2_LEVEL_NVRAM) {
150 if (addr < todc_info->nvram_size) { /* NVRAM */
151 ppc_md.rtc_write_val(todc_info->nvram_addr_reg, addr);
152 ppc_md.rtc_write_val(todc_info->nvram_data_reg, val);
153 } else { /* Clock Reg */
154 addr -= todc_info->nvram_size;
155 ppc_md.rtc_write_val(addr, val);
156 }
157 } else
158 ppc_md.rtc_write_val(addr, val);
159}
160
161/*
162 * TODC routines
163 *
164 * There is some ugly stuff in that there are assumptions for the mc146818.
165 *
166 * Assumptions:
167 * - todc_info->control_a has the offset as mc146818 Register B reg
168 * - todc_info->control_b has the offset as mc146818 Register A reg
169 * - m48txx control reg's write enable or 'W' bit is same as
170 * mc146818 Register B 'SET' bit (i.e., 0x80)
171 *
172 * These assumptions were made to make the code simpler.
173 */
174long __init
175todc_time_init(void)
176{
177 u_char cntl_b;
178
179 if (!ppc_md.rtc_read_val)
180 ppc_md.rtc_read_val = ppc_md.nvram_read_val;
181 if (!ppc_md.rtc_write_val)
182 ppc_md.rtc_write_val = ppc_md.nvram_write_val;
183
184 cntl_b = todc_read_val(todc_info->control_b);
185
186 if (todc_info->rtc_type == TODC_TYPE_MC146818) {
187 if ((cntl_b & 0x70) != 0x20) {
188 printk(KERN_INFO "TODC real-time-clock was stopped."
189 " Now starting...");
190 cntl_b &= ~0x70;
191 cntl_b |= 0x20;
192 }
193
194 todc_write_val(todc_info->control_b, cntl_b);
195 } else if (todc_info->rtc_type == TODC_TYPE_DS17285) {
196 u_char mode;
197
198 mode = todc_read_val(TODC_TYPE_DS17285_CNTL_A);
199 /* Make sure countdown clear is not set */
200 mode &= ~0x40;
201 /* Enable oscillator, extended register set */
202 mode |= 0x30;
203 todc_write_val(TODC_TYPE_DS17285_CNTL_A, mode);
204
205 } else if (todc_info->rtc_type == TODC_TYPE_DS1501) {
206 u_char month;
207
208 todc_info->enable_read = TODC_DS1501_CNTL_B_TE;
209 todc_info->enable_write = TODC_DS1501_CNTL_B_TE;
210
211 month = todc_read_val(todc_info->month);
212
213 if ((month & 0x80) == 0x80) {
214 printk(KERN_INFO "TODC %s %s\n",
215 "real-time-clock was stopped.",
216 "Now starting...");
217 month &= ~0x80;
218 todc_write_val(todc_info->month, month);
219 }
220
221 cntl_b &= ~TODC_DS1501_CNTL_B_TE;
222 todc_write_val(todc_info->control_b, cntl_b);
223 } else { /* must be a m48txx type */
224 u_char cntl_a;
225
226 todc_info->enable_read = TODC_MK48TXX_CNTL_A_R;
227 todc_info->enable_write = TODC_MK48TXX_CNTL_A_W;
228
229 cntl_a = todc_read_val(todc_info->control_a);
230
231 /* Check & clear STOP bit in control B register */
232 if (cntl_b & TODC_MK48TXX_DAY_CB) {
233 printk(KERN_INFO "TODC %s %s\n",
234 "real-time-clock was stopped.",
235 "Now starting...");
236
237 cntl_a |= todc_info->enable_write;
238 cntl_b &= ~TODC_MK48TXX_DAY_CB;/* Start Oscil */
239
240 todc_write_val(todc_info->control_a, cntl_a);
241 todc_write_val(todc_info->control_b, cntl_b);
242 }
243
244 /* Make sure READ & WRITE bits are cleared. */
245 cntl_a &= ~(todc_info->enable_write | todc_info->enable_read);
246 todc_write_val(todc_info->control_a, cntl_a);
247 }
248
249 return 0;
250}
251
252/*
253 * There is some ugly stuff in that there are assumptions that for a mc146818,
254 * the todc_info->control_a has the offset of the mc146818 Register B reg and
255 * that the register'ss 'SET' bit is the same as the m48txx's write enable
256 * bit in the control register of the m48txx (i.e., 0x80).
257 *
258 * It was done to make the code look simpler.
259 */
260void
261todc_get_rtc_time(struct rtc_time *tm)
262{
263 uint year = 0, mon = 0, mday = 0, hour = 0, min = 0, sec = 0;
264 uint limit, i;
265 u_char save_control, uip = 0;
266 extern void GregorianDay(struct rtc_time *);
267
268 spin_lock(&rtc_lock);
269 save_control = todc_read_val(todc_info->control_a);
270
271 if (todc_info->rtc_type != TODC_TYPE_MC146818) {
272 limit = 1;
273
274 switch (todc_info->rtc_type) {
275 case TODC_TYPE_DS1553:
276 case TODC_TYPE_DS1557:
277 case TODC_TYPE_DS1743:
278 case TODC_TYPE_DS1746: /* XXXX BAD HACK -> FIX */
279 case TODC_TYPE_DS1747:
280 case TODC_TYPE_DS17285:
281 break;
282 default:
283 todc_write_val(todc_info->control_a,
284 (save_control | todc_info->enable_read));
285 }
286 } else
287 limit = 100000000;
288
289 for (i=0; i<limit; i++) {
290 if (todc_info->rtc_type == TODC_TYPE_MC146818)
291 uip = todc_read_val(todc_info->RTC_FREQ_SELECT);
292
293 sec = todc_read_val(todc_info->seconds) & 0x7f;
294 min = todc_read_val(todc_info->minutes) & 0x7f;
295 hour = todc_read_val(todc_info->hours) & 0x3f;
296 mday = todc_read_val(todc_info->day_of_month) & 0x3f;
297 mon = todc_read_val(todc_info->month) & 0x1f;
298 year = todc_read_val(todc_info->year) & 0xff;
299
300 if (todc_info->rtc_type == TODC_TYPE_MC146818) {
301 uip |= todc_read_val(todc_info->RTC_FREQ_SELECT);
302 if ((uip & RTC_UIP) == 0)
303 break;
304 }
305 }
306
307 if (todc_info->rtc_type != TODC_TYPE_MC146818) {
308 switch (todc_info->rtc_type) {
309 case TODC_TYPE_DS1553:
310 case TODC_TYPE_DS1557:
311 case TODC_TYPE_DS1743:
312 case TODC_TYPE_DS1746: /* XXXX BAD HACK -> FIX */
313 case TODC_TYPE_DS1747:
314 case TODC_TYPE_DS17285:
315 break;
316 default:
317 save_control &= ~(todc_info->enable_read);
318 todc_write_val(todc_info->control_a, save_control);
319 }
320 }
321 spin_unlock(&rtc_lock);
322
323 if ((todc_info->rtc_type != TODC_TYPE_MC146818)
324 || ((save_control & RTC_DM_BINARY) == 0)
325 || RTC_ALWAYS_BCD) {
326 BCD_TO_BIN(sec);
327 BCD_TO_BIN(min);
328 BCD_TO_BIN(hour);
329 BCD_TO_BIN(mday);
330 BCD_TO_BIN(mon);
331 BCD_TO_BIN(year);
332 }
333
334 if ((year + 1900) < 1970) {
335 year += 100;
336 }
337
338 tm->tm_sec = sec;
339 tm->tm_min = min;
340 tm->tm_hour = hour;
341 tm->tm_mday = mday;
342 tm->tm_mon = mon;
343 tm->tm_year = year;
344
345 GregorianDay(tm);
346}
347
348int
349todc_set_rtc_time(struct rtc_time *tm)
350{
351 u_char save_control, save_freq_select = 0;
352
353 spin_lock(&rtc_lock);
354 save_control = todc_read_val(todc_info->control_a);
355
356 /* Assuming MK48T59_RTC_CA_WRITE & RTC_SET are equal */
357 todc_write_val(todc_info->control_a,
358 (save_control | todc_info->enable_write));
359 save_control &= ~(todc_info->enable_write); /* in case it was set */
360
361 if (todc_info->rtc_type == TODC_TYPE_MC146818) {
362 save_freq_select = todc_read_val(todc_info->RTC_FREQ_SELECT);
363 todc_write_val(todc_info->RTC_FREQ_SELECT,
364 save_freq_select | RTC_DIV_RESET2);
365 }
366
367 if ((todc_info->rtc_type != TODC_TYPE_MC146818)
368 || ((save_control & RTC_DM_BINARY) == 0)
369 || RTC_ALWAYS_BCD) {
370 BIN_TO_BCD(tm->tm_sec);
371 BIN_TO_BCD(tm->tm_min);
372 BIN_TO_BCD(tm->tm_hour);
373 BIN_TO_BCD(tm->tm_mon);
374 BIN_TO_BCD(tm->tm_mday);
375 BIN_TO_BCD(tm->tm_year);
376 }
377
378 todc_write_val(todc_info->seconds, tm->tm_sec);
379 todc_write_val(todc_info->minutes, tm->tm_min);
380 todc_write_val(todc_info->hours, tm->tm_hour);
381 todc_write_val(todc_info->month, tm->tm_mon);
382 todc_write_val(todc_info->day_of_month, tm->tm_mday);
383 todc_write_val(todc_info->year, tm->tm_year);
384
385 todc_write_val(todc_info->control_a, save_control);
386
387 if (todc_info->rtc_type == TODC_TYPE_MC146818)
388 todc_write_val(todc_info->RTC_FREQ_SELECT, save_freq_select);
389
390 spin_unlock(&rtc_lock);
391 return 0;
392}
diff --git a/arch/powerpc/sysdev/tsi108_dev.c b/arch/powerpc/sysdev/tsi108_dev.c
new file mode 100644
index 000000000000..26a0cc820cde
--- /dev/null
+++ b/arch/powerpc/sysdev/tsi108_dev.c
@@ -0,0 +1,145 @@
1/*
2 * tsi108/109 device setup code
3 *
4 * Maintained by Roy Zang < tie-fei.zang@freescale.com >
5 *
6 * This program is free software; you can redistribute it and/or modify it
7 * under the terms of the GNU General Public License as published by the
8 * Free Software Foundation; either version 2 of the License, or (at your
9 * option) any later version.
10 */
11
12#include <linux/config.h>
13#include <linux/stddef.h>
14#include <linux/kernel.h>
15#include <linux/init.h>
16#include <linux/errno.h>
17#include <linux/major.h>
18#include <linux/delay.h>
19#include <linux/irq.h>
20#include <linux/module.h>
21#include <linux/device.h>
22#include <linux/platform_device.h>
23#include <asm/tsi108.h>
24
25#include <asm/system.h>
26#include <asm/atomic.h>
27#include <asm/io.h>
28#include <asm/irq.h>
29#include <asm/prom.h>
30#include <mm/mmu_decl.h>
31
32#undef DEBUG
33
34#ifdef DEBUG
35#define DBG(fmt...) do { printk(fmt); } while(0)
36#else
37#define DBG(fmt...) do { } while(0)
38#endif
39
40static phys_addr_t tsi108_csr_base = -1;
41
42phys_addr_t get_csrbase(void)
43{
44 struct device_node *tsi;
45
46 if (tsi108_csr_base != -1)
47 return tsi108_csr_base;
48
49 tsi = of_find_node_by_type(NULL, "tsi-bridge");
50 if (tsi) {
51 unsigned int size;
52 void *prop = get_property(tsi, "reg", &size);
53 tsi108_csr_base = of_translate_address(tsi, prop);
54 of_node_put(tsi);
55 };
56 return tsi108_csr_base;
57}
58
59u32 get_vir_csrbase(void)
60{
61 return (u32) (ioremap(get_csrbase(), 0x10000));
62}
63
64EXPORT_SYMBOL(get_csrbase);
65EXPORT_SYMBOL(get_vir_csrbase);
66
67static int __init tsi108_eth_of_init(void)
68{
69 struct device_node *np;
70 unsigned int i;
71 struct platform_device *tsi_eth_dev;
72 struct resource res;
73 int ret;
74
75 for (np = NULL, i = 0;
76 (np = of_find_compatible_node(np, "network", "tsi-ethernet")) != NULL;
77 i++) {
78 struct resource r[2];
79 struct device_node *phy;
80 hw_info tsi_eth_data;
81 unsigned int *id;
82 unsigned int *phy_id;
83 void *mac_addr;
84 phandle *ph;
85
86 memset(r, 0, sizeof(r));
87 memset(&tsi_eth_data, 0, sizeof(tsi_eth_data));
88
89 ret = of_address_to_resource(np, 0, &r[0]);
90 DBG("%s: name:start->end = %s:0x%lx-> 0x%lx\n",
91 __FUNCTION__,r[0].name, r[0].start, r[0].end);
92 if (ret)
93 goto err;
94
95 r[1].name = "tx";
96 r[1].start = np->intrs[0].line;
97 r[1].end = np->intrs[0].line;
98 r[1].flags = IORESOURCE_IRQ;
99
100 tsi_eth_dev =
101 platform_device_register_simple("tsi-ethernet", i, &r[0],
102 np->n_intrs + 1);
103
104 if (IS_ERR(tsi_eth_dev)) {
105 ret = PTR_ERR(tsi_eth_dev);
106 goto err;
107 }
108
109 mac_addr = get_property(np, "address", NULL);
110 memcpy(tsi_eth_data.mac_addr, mac_addr, 6);
111
112 ph = (phandle *) get_property(np, "phy-handle", NULL);
113 phy = of_find_node_by_phandle(*ph);
114
115 if (phy == NULL) {
116 ret = -ENODEV;
117 goto unreg;
118 }
119
120 id = (u32 *) get_property(phy, "reg", NULL);
121 phy_id = (u32 *) get_property(phy, "phy-id", NULL);
122 ret = of_address_to_resource(phy, 0, &res);
123 if (ret) {
124 of_node_put(phy);
125 goto unreg;
126 }
127 tsi_eth_data.regs = r[0].start;
128 tsi_eth_data.phyregs = res.start;
129 tsi_eth_data.phy = *phy_id;
130 tsi_eth_data.irq_num = np->intrs[0].line;
131 of_node_put(phy);
132 ret =
133 platform_device_add_data(tsi_eth_dev, &tsi_eth_data,
134 sizeof(hw_info));
135 if (ret)
136 goto unreg;
137 }
138 return 0;
139unreg:
140 platform_device_unregister(tsi_eth_dev);
141err:
142 return ret;
143}
144
145arch_initcall(tsi108_eth_of_init);
diff --git a/arch/powerpc/sysdev/tsi108_pci.c b/arch/powerpc/sysdev/tsi108_pci.c
new file mode 100644
index 000000000000..3265d54c82ed
--- /dev/null
+++ b/arch/powerpc/sysdev/tsi108_pci.c
@@ -0,0 +1,412 @@
1/*
2 * Common routines for Tundra Semiconductor TSI108 host bridge.
3 *
4 * 2004-2005 (c) Tundra Semiconductor Corp.
5 * Author: Alex Bounine (alexandreb@tundra.com)
6 *
7 * This program is free software; you can redistribute it and/or modify it
8 * under the terms of the GNU General Public License as published by the Free
9 * Software Foundation; either version 2 of the License, or (at your option)
10 * any later version.
11 *
12 * This program is distributed in the hope that it will be useful, but WITHOUT
13 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
14 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
15 * more details.
16 *
17 * You should have received a copy of the GNU General Public License along with
18 * this program; if not, write to the Free Software Foundation, Inc., 59
19 * Temple Place - Suite 330, Boston, MA 02111-1307, USA.
20 */
21
22#include <linux/kernel.h>
23#include <linux/init.h>
24#include <linux/pci.h>
25#include <linux/slab.h>
26#include <linux/irq.h>
27#include <linux/interrupt.h>
28
29
30#include <asm/byteorder.h>
31#include <asm/io.h>
32#include <asm/irq.h>
33#include <asm/uaccess.h>
34#include <asm/machdep.h>
35#include <asm/pci-bridge.h>
36#include <asm/tsi108.h>
37#include <asm/tsi108_irq.h>
38#include <asm/prom.h>
39
40#undef DEBUG
41#ifdef DEBUG
42#define DBG(x...) printk(x)
43#else
44#define DBG(x...)
45#endif
46
47#define tsi_mk_config_addr(bus, devfunc, offset) \
48 ((((bus)<<16) | ((devfunc)<<8) | (offset & 0xfc)) + tsi108_pci_cfg_base)
49
50u32 tsi108_pci_cfg_base;
51u32 tsi108_csr_vir_base;
52
53extern u32 get_vir_csrbase(void);
54extern u32 tsi108_read_reg(u32 reg_offset);
55extern void tsi108_write_reg(u32 reg_offset, u32 val);
56
57int
58tsi108_direct_write_config(struct pci_bus *bus, unsigned int devfunc,
59 int offset, int len, u32 val)
60{
61 volatile unsigned char *cfg_addr;
62
63 if (ppc_md.pci_exclude_device)
64 if (ppc_md.pci_exclude_device(bus->number, devfunc))
65 return PCIBIOS_DEVICE_NOT_FOUND;
66
67 cfg_addr = (unsigned char *)(tsi_mk_config_addr(bus->number,
68 devfunc, offset) |
69 (offset & 0x03));
70
71#ifdef DEBUG
72 printk("PCI CFG write : ");
73 printk("%d:0x%x:0x%x ", bus->number, devfunc, offset);
74 printk("%d ADDR=0x%08x ", len, (uint) cfg_addr);
75 printk("data = 0x%08x\n", val);
76#endif
77
78 switch (len) {
79 case 1:
80 out_8((u8 *) cfg_addr, val);
81 break;
82 case 2:
83 out_le16((u16 *) cfg_addr, val);
84 break;
85 default:
86 out_le32((u32 *) cfg_addr, val);
87 break;
88 }
89
90 return PCIBIOS_SUCCESSFUL;
91}
92
93void tsi108_clear_pci_error(u32 pci_cfg_base)
94{
95 u32 err_stat, err_addr, pci_stat;
96
97 /*
98 * Quietly clear PB and PCI error flags set as result
99 * of PCI/X configuration read requests.
100 */
101
102 /* Read PB Error Log Registers */
103
104 err_stat = tsi108_read_reg(TSI108_PB_OFFSET + TSI108_PB_ERRCS);
105 err_addr = tsi108_read_reg(TSI108_PB_OFFSET + TSI108_PB_AERR);
106
107 if (err_stat & TSI108_PB_ERRCS_ES) {
108 /* Clear error flag */
109 tsi108_write_reg(TSI108_PB_OFFSET + TSI108_PB_ERRCS,
110 TSI108_PB_ERRCS_ES);
111
112 /* Clear read error reported in PB_ISR */
113 tsi108_write_reg(TSI108_PB_OFFSET + TSI108_PB_ISR,
114 TSI108_PB_ISR_PBS_RD_ERR);
115
116 /* Clear PCI/X bus cfg errors if applicable */
117 if ((err_addr & 0xFF000000) == pci_cfg_base) {
118 pci_stat =
119 tsi108_read_reg(TSI108_PCI_OFFSET + TSI108_PCI_CSR);
120 tsi108_write_reg(TSI108_PCI_OFFSET + TSI108_PCI_CSR,
121 pci_stat);
122 }
123 }
124
125 return;
126}
127
128#define __tsi108_read_pci_config(x, addr, op) \
129 __asm__ __volatile__( \
130 " "op" %0,0,%1\n" \
131 "1: eieio\n" \
132 "2:\n" \
133 ".section .fixup,\"ax\"\n" \
134 "3: li %0,-1\n" \
135 " b 2b\n" \
136 ".section __ex_table,\"a\"\n" \
137 " .align 2\n" \
138 " .long 1b,3b\n" \
139 ".text" \
140 : "=r"(x) : "r"(addr))
141
142int
143tsi108_direct_read_config(struct pci_bus *bus, unsigned int devfn, int offset,
144 int len, u32 * val)
145{
146 volatile unsigned char *cfg_addr;
147 u32 temp;
148
149 if (ppc_md.pci_exclude_device)
150 if (ppc_md.pci_exclude_device(bus->number, devfn))
151 return PCIBIOS_DEVICE_NOT_FOUND;
152
153 cfg_addr = (unsigned char *)(tsi_mk_config_addr(bus->number,
154 devfn,
155 offset) | (offset &
156 0x03));
157
158 switch (len) {
159 case 1:
160 __tsi108_read_pci_config(temp, cfg_addr, "lbzx");
161 break;
162 case 2:
163 __tsi108_read_pci_config(temp, cfg_addr, "lhbrx");
164 break;
165 default:
166 __tsi108_read_pci_config(temp, cfg_addr, "lwbrx");
167 break;
168 }
169
170 *val = temp;
171
172#ifdef DEBUG
173 if ((0xFFFFFFFF != temp) && (0xFFFF != temp) && (0xFF != temp)) {
174 printk("PCI CFG read : ");
175 printk("%d:0x%x:0x%x ", bus->number, devfn, offset);
176 printk("%d ADDR=0x%08x ", len, (uint) cfg_addr);
177 printk("data = 0x%x\n", *val);
178 }
179#endif
180 return PCIBIOS_SUCCESSFUL;
181}
182
183void tsi108_clear_pci_cfg_error(void)
184{
185 tsi108_clear_pci_error(TSI108_PCI_CFG_BASE_PHYS);
186}
187
188static struct pci_ops tsi108_direct_pci_ops = {
189 tsi108_direct_read_config,
190 tsi108_direct_write_config
191};
192
193int __init tsi108_setup_pci(struct device_node *dev)
194{
195 int len;
196 struct pci_controller *hose;
197 struct resource rsrc;
198 int *bus_range;
199 int primary = 0, has_address = 0;
200
201 /* PCI Config mapping */
202 tsi108_pci_cfg_base = (u32)ioremap(TSI108_PCI_CFG_BASE_PHYS,
203 TSI108_PCI_CFG_SIZE);
204 DBG("TSI_PCI: %s tsi108_pci_cfg_base=0x%x\n", __FUNCTION__,
205 tsi108_pci_cfg_base);
206
207 /* Fetch host bridge registers address */
208 has_address = (of_address_to_resource(dev, 0, &rsrc) == 0);
209
210 /* Get bus range if any */
211 bus_range = (int *)get_property(dev, "bus-range", &len);
212 if (bus_range == NULL || len < 2 * sizeof(int)) {
213 printk(KERN_WARNING "Can't get bus-range for %s, assume"
214 " bus 0\n", dev->full_name);
215 }
216
217 hose = pcibios_alloc_controller();
218
219 if (!hose) {
220 printk("PCI Host bridge init failed\n");
221 return -ENOMEM;
222 }
223 hose->arch_data = dev;
224 hose->set_cfg_type = 1;
225
226 hose->first_busno = bus_range ? bus_range[0] : 0;
227 hose->last_busno = bus_range ? bus_range[1] : 0xff;
228
229 (hose)->ops = &tsi108_direct_pci_ops;
230
231 printk(KERN_INFO "Found tsi108 PCI host bridge at 0x%08lx. "
232 "Firmware bus number: %d->%d\n",
233 rsrc.start, hose->first_busno, hose->last_busno);
234
235 /* Interpret the "ranges" property */
236 /* This also maps the I/O region and sets isa_io/mem_base */
237 pci_process_bridge_OF_ranges(hose, dev, primary);
238 return 0;
239}
240
241/*
242 * Low level utility functions
243 */
244
245static void tsi108_pci_int_mask(u_int irq)
246{
247 u_int irp_cfg;
248 int int_line = (irq - IRQ_PCI_INTAD_BASE);
249
250 irp_cfg = tsi108_read_reg(TSI108_PCI_OFFSET + TSI108_PCI_IRP_CFG_CTL);
251 mb();
252 irp_cfg |= (1 << int_line); /* INTx_DIR = output */
253 irp_cfg &= ~(3 << (8 + (int_line * 2))); /* INTx_TYPE = unused */
254 tsi108_write_reg(TSI108_PCI_OFFSET + TSI108_PCI_IRP_CFG_CTL, irp_cfg);
255 mb();
256 irp_cfg = tsi108_read_reg(TSI108_PCI_OFFSET + TSI108_PCI_IRP_CFG_CTL);
257}
258
259static void tsi108_pci_int_unmask(u_int irq)
260{
261 u_int irp_cfg;
262 int int_line = (irq - IRQ_PCI_INTAD_BASE);
263
264 irp_cfg = tsi108_read_reg(TSI108_PCI_OFFSET + TSI108_PCI_IRP_CFG_CTL);
265 mb();
266 irp_cfg &= ~(1 << int_line);
267 irp_cfg |= (3 << (8 + (int_line * 2)));
268 tsi108_write_reg(TSI108_PCI_OFFSET + TSI108_PCI_IRP_CFG_CTL, irp_cfg);
269 mb();
270}
271
272static void init_pci_source(void)
273{
274 tsi108_write_reg(TSI108_PCI_OFFSET + TSI108_PCI_IRP_CFG_CTL,
275 0x0000ff00);
276 tsi108_write_reg(TSI108_PCI_OFFSET + TSI108_PCI_IRP_ENABLE,
277 TSI108_PCI_IRP_ENABLE_P_INT);
278 mb();
279}
280
281static inline int get_pci_source(void)
282{
283 u_int temp = 0;
284 int irq = -1;
285 int i;
286 u_int pci_irp_stat;
287 static int mask = 0;
288
289 /* Read PCI/X block interrupt status register */
290 pci_irp_stat = tsi108_read_reg(TSI108_PCI_OFFSET + TSI108_PCI_IRP_STAT);
291 mb();
292
293 if (pci_irp_stat & TSI108_PCI_IRP_STAT_P_INT) {
294 /* Process Interrupt from PCI bus INTA# - INTD# lines */
295 temp =
296 tsi108_read_reg(TSI108_PCI_OFFSET +
297 TSI108_PCI_IRP_INTAD) & 0xf;
298 mb();
299 for (i = 0; i < 4; i++, mask++) {
300 if (temp & (1 << mask % 4)) {
301 irq = IRQ_PCI_INTA + mask % 4;
302 mask++;
303 break;
304 }
305 }
306
307 /* Disable interrupts from PCI block */
308 temp = tsi108_read_reg(TSI108_PCI_OFFSET + TSI108_PCI_IRP_ENABLE);
309 tsi108_write_reg(TSI108_PCI_OFFSET + TSI108_PCI_IRP_ENABLE,
310 temp & ~TSI108_PCI_IRP_ENABLE_P_INT);
311 mb();
312 (void)tsi108_read_reg(TSI108_PCI_OFFSET + TSI108_PCI_IRP_ENABLE);
313 mb();
314 }
315#ifdef DEBUG
316 else {
317 printk("TSI108_PIC: error in TSI108_PCI_IRP_STAT\n");
318 pci_irp_stat =
319 tsi108_read_reg(TSI108_PCI_OFFSET + TSI108_PCI_IRP_STAT);
320 temp =
321 tsi108_read_reg(TSI108_PCI_OFFSET + TSI108_PCI_IRP_INTAD);
322 mb();
323 printk(">> stat=0x%08x intad=0x%08x ", pci_irp_stat, temp);
324 temp =
325 tsi108_read_reg(TSI108_PCI_OFFSET + TSI108_PCI_IRP_CFG_CTL);
326 mb();
327 printk("cfg_ctl=0x%08x ", temp);
328 temp =
329 tsi108_read_reg(TSI108_PCI_OFFSET + TSI108_PCI_IRP_ENABLE);
330 mb();
331 printk("irp_enable=0x%08x\n", temp);
332 }
333#endif /* end of DEBUG */
334
335 return irq;
336}
337
338
339/*
340 * Linux descriptor level callbacks
341 */
342
343static void tsi108_pci_irq_enable(u_int irq)
344{
345 tsi108_pci_int_unmask(irq);
346}
347
348static void tsi108_pci_irq_disable(u_int irq)
349{
350 tsi108_pci_int_mask(irq);
351}
352
353static void tsi108_pci_irq_ack(u_int irq)
354{
355 tsi108_pci_int_mask(irq);
356}
357
358static void tsi108_pci_irq_end(u_int irq)
359{
360 tsi108_pci_int_unmask(irq);
361
362 /* Enable interrupts from PCI block */
363 tsi108_write_reg(TSI108_PCI_OFFSET + TSI108_PCI_IRP_ENABLE,
364 tsi108_read_reg(TSI108_PCI_OFFSET +
365 TSI108_PCI_IRP_ENABLE) |
366 TSI108_PCI_IRP_ENABLE_P_INT);
367 mb();
368}
369
370/*
371 * Interrupt controller descriptor for cascaded PCI interrupt controller.
372 */
373
374struct hw_interrupt_type tsi108_pci_irq = {
375 .typename = "tsi108_PCI_int",
376 .enable = tsi108_pci_irq_enable,
377 .disable = tsi108_pci_irq_disable,
378 .ack = tsi108_pci_irq_ack,
379 .end = tsi108_pci_irq_end,
380};
381
382/*
383 * Exported functions
384 */
385
386/*
387 * The Tsi108 PCI interrupts initialization routine.
388 *
389 * The INTA# - INTD# interrupts on the PCI bus are reported by the PCI block
390 * to the MPIC using single interrupt source (IRQ_TSI108_PCI). Therefore the
391 * PCI block has to be treated as a cascaded interrupt controller connected
392 * to the MPIC.
393 */
394
395void __init tsi108_pci_int_init(void)
396{
397 u_int i;
398
399 DBG("Tsi108_pci_int_init: initializing PCI interrupts\n");
400
401 for (i = 0; i < NUM_PCI_IRQS; i++) {
402 irq_desc[i + IRQ_PCI_INTAD_BASE].handler = &tsi108_pci_irq;
403 irq_desc[i + IRQ_PCI_INTAD_BASE].status |= IRQ_LEVEL;
404 }
405
406 init_pci_source();
407}
408
409int tsi108_irq_cascade(struct pt_regs *regs, void *unused)
410{
411 return get_pci_source();
412}