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. ...
此提交包含在:
@@ -12,3 +12,5 @@ obj-$(CONFIG_U3_DART) += dart_iommu.o
|
||||
obj-$(CONFIG_MMIO_NVRAM) += mmio_nvram.o
|
||||
obj-$(CONFIG_PPC_83xx) += ipic.o
|
||||
obj-$(CONFIG_FSL_SOC) += fsl_soc.o
|
||||
obj-$(CONFIG_PPC_TODC) += todc.o
|
||||
obj-$(CONFIG_TSI108_BRIDGE) += tsi108_pci.o tsi108_dev.o
|
||||
|
@@ -47,8 +47,12 @@
|
||||
/* U4 registers */
|
||||
#define DART_BASE_U4_BASE_MASK 0xffffff
|
||||
#define DART_BASE_U4_BASE_SHIFT 0
|
||||
#define DART_CNTL_U4_FLUSHTLB 0x20000000
|
||||
#define DART_CNTL_U4_ENABLE 0x80000000
|
||||
#define DART_CNTL_U4_IONE 0x40000000
|
||||
#define DART_CNTL_U4_FLUSHTLB 0x20000000
|
||||
#define DART_CNTL_U4_IDLE 0x10000000
|
||||
#define DART_CNTL_U4_PAR_EN 0x08000000
|
||||
#define DART_CNTL_U4_IONE_MASK 0x07ffffff
|
||||
#define DART_SIZE_U4_SIZE_MASK 0x1fff
|
||||
#define DART_SIZE_U4_SIZE_SHIFT 0
|
||||
|
||||
|
@@ -101,8 +101,8 @@ retry:
|
||||
if (l == (1L << limit)) {
|
||||
if (limit < 4) {
|
||||
limit++;
|
||||
reg = DART_IN(DART_CNTL);
|
||||
reg &= ~inv_bit;
|
||||
reg = DART_IN(DART_CNTL);
|
||||
reg &= ~inv_bit;
|
||||
DART_OUT(DART_CNTL, reg);
|
||||
goto retry;
|
||||
} else
|
||||
@@ -111,11 +111,39 @@ retry:
|
||||
}
|
||||
}
|
||||
|
||||
static inline void dart_tlb_invalidate_one(unsigned long bus_rpn)
|
||||
{
|
||||
unsigned int reg;
|
||||
unsigned int l, limit;
|
||||
|
||||
reg = DART_CNTL_U4_ENABLE | DART_CNTL_U4_IONE |
|
||||
(bus_rpn & DART_CNTL_U4_IONE_MASK);
|
||||
DART_OUT(DART_CNTL, reg);
|
||||
|
||||
limit = 0;
|
||||
wait_more:
|
||||
l = 0;
|
||||
while ((DART_IN(DART_CNTL) & DART_CNTL_U4_IONE) && l < (1L << limit)) {
|
||||
rmb();
|
||||
l++;
|
||||
}
|
||||
|
||||
if (l == (1L << limit)) {
|
||||
if (limit < 4) {
|
||||
limit++;
|
||||
goto wait_more;
|
||||
} else
|
||||
panic("DART: TLB did not flush after waiting a long "
|
||||
"time. Buggy U4 ?");
|
||||
}
|
||||
}
|
||||
|
||||
static void dart_flush(struct iommu_table *tbl)
|
||||
{
|
||||
if (dart_dirty)
|
||||
if (dart_dirty) {
|
||||
dart_tlb_invalidate_all();
|
||||
dart_dirty = 0;
|
||||
dart_dirty = 0;
|
||||
}
|
||||
}
|
||||
|
||||
static void dart_build(struct iommu_table *tbl, long index,
|
||||
@@ -124,6 +152,7 @@ static void dart_build(struct iommu_table *tbl, long index,
|
||||
{
|
||||
unsigned int *dp;
|
||||
unsigned int rpn;
|
||||
long l;
|
||||
|
||||
DBG("dart: build at: %lx, %lx, addr: %x\n", index, npages, uaddr);
|
||||
|
||||
@@ -135,7 +164,8 @@ static void dart_build(struct iommu_table *tbl, long index,
|
||||
/* On U3, all memory is contigous, so we can move this
|
||||
* out of the loop.
|
||||
*/
|
||||
while (npages--) {
|
||||
l = npages;
|
||||
while (l--) {
|
||||
rpn = virt_to_abs(uaddr) >> DART_PAGE_SHIFT;
|
||||
|
||||
*(dp++) = DARTMAP_VALID | (rpn & DARTMAP_RPNMASK);
|
||||
@@ -143,7 +173,14 @@ static void dart_build(struct iommu_table *tbl, long index,
|
||||
uaddr += DART_PAGE_SIZE;
|
||||
}
|
||||
|
||||
dart_dirty = 1;
|
||||
if (dart_is_u4) {
|
||||
rpn = index;
|
||||
mb(); /* make sure all updates have reached memory */
|
||||
while (npages--)
|
||||
dart_tlb_invalidate_one(rpn++);
|
||||
} else {
|
||||
dart_dirty = 1;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
392
arch/powerpc/sysdev/todc.c
一般檔案
392
arch/powerpc/sysdev/todc.c
一般檔案
@@ -0,0 +1,392 @@
|
||||
/*
|
||||
* Time of Day Clock support for the M48T35, M48T37, M48T59, and MC146818
|
||||
* Real Time Clocks/Timekeepers.
|
||||
*
|
||||
* Author: Mark A. Greer <mgreer@mvista.com>
|
||||
*
|
||||
* 2001-2004 (c) MontaVista, Software, Inc. This file is licensed under
|
||||
* the terms of the GNU General Public License version 2. This program
|
||||
* is licensed "as is" without any warranty of any kind, whether express
|
||||
* or implied.
|
||||
*/
|
||||
#include <linux/errno.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/time.h>
|
||||
#include <linux/timex.h>
|
||||
#include <linux/bcd.h>
|
||||
#include <linux/mc146818rtc.h>
|
||||
|
||||
#include <asm/machdep.h>
|
||||
#include <asm/io.h>
|
||||
#include <asm/time.h>
|
||||
#include <asm/todc.h>
|
||||
|
||||
/*
|
||||
* Depending on the hardware on your board and your board design, the
|
||||
* RTC/NVRAM may be accessed either directly (like normal memory) or via
|
||||
* address/data registers. If your board uses the direct method, set
|
||||
* 'nvram_data' to the base address of your nvram and leave 'nvram_as0' and
|
||||
* 'nvram_as1' NULL. If your board uses address/data regs to access nvram,
|
||||
* set 'nvram_as0' to the address of the lower byte, set 'nvram_as1' to the
|
||||
* address of the upper byte (leave NULL if using mc146818), and set
|
||||
* 'nvram_data' to the address of the 8-bit data register.
|
||||
*
|
||||
* Note: Even though the documentation for the various RTC chips say that it
|
||||
* take up to a second before it starts updating once the 'R' bit is
|
||||
* cleared, they always seem to update even though we bang on it many
|
||||
* times a second. This is true, except for the Dallas Semi 1746/1747
|
||||
* (possibly others). Those chips seem to have a real problem whenever
|
||||
* we set the 'R' bit before reading them, they basically stop counting.
|
||||
* --MAG
|
||||
*/
|
||||
|
||||
/*
|
||||
* 'todc_info' should be initialized in your *_setup.c file to
|
||||
* point to a fully initialized 'todc_info_t' structure.
|
||||
* This structure holds all the register offsets for your particular
|
||||
* TODC/RTC chip.
|
||||
* TODC_ALLOC()/TODC_INIT() will allocate and initialize this table for you.
|
||||
*/
|
||||
|
||||
#ifdef RTC_FREQ_SELECT
|
||||
#undef RTC_FREQ_SELECT
|
||||
#define RTC_FREQ_SELECT control_b /* Register A */
|
||||
#endif
|
||||
|
||||
#ifdef RTC_CONTROL
|
||||
#undef RTC_CONTROL
|
||||
#define RTC_CONTROL control_a /* Register B */
|
||||
#endif
|
||||
|
||||
#ifdef RTC_INTR_FLAGS
|
||||
#undef RTC_INTR_FLAGS
|
||||
#define RTC_INTR_FLAGS watchdog /* Register C */
|
||||
#endif
|
||||
|
||||
#ifdef RTC_VALID
|
||||
#undef RTC_VALID
|
||||
#define RTC_VALID interrupts /* Register D */
|
||||
#endif
|
||||
|
||||
/* Access routines when RTC accessed directly (like normal memory) */
|
||||
u_char
|
||||
todc_direct_read_val(int addr)
|
||||
{
|
||||
return readb((void __iomem *)(todc_info->nvram_data + addr));
|
||||
}
|
||||
|
||||
void
|
||||
todc_direct_write_val(int addr, unsigned char val)
|
||||
{
|
||||
writeb(val, (void __iomem *)(todc_info->nvram_data + addr));
|
||||
return;
|
||||
}
|
||||
|
||||
/* Access routines for accessing m48txx type chips via addr/data regs */
|
||||
u_char
|
||||
todc_m48txx_read_val(int addr)
|
||||
{
|
||||
outb(addr, todc_info->nvram_as0);
|
||||
outb(addr>>todc_info->as0_bits, todc_info->nvram_as1);
|
||||
return inb(todc_info->nvram_data);
|
||||
}
|
||||
|
||||
void
|
||||
todc_m48txx_write_val(int addr, unsigned char val)
|
||||
{
|
||||
outb(addr, todc_info->nvram_as0);
|
||||
outb(addr>>todc_info->as0_bits, todc_info->nvram_as1);
|
||||
outb(val, todc_info->nvram_data);
|
||||
return;
|
||||
}
|
||||
|
||||
/* Access routines for accessing mc146818 type chips via addr/data regs */
|
||||
u_char
|
||||
todc_mc146818_read_val(int addr)
|
||||
{
|
||||
outb_p(addr, todc_info->nvram_as0);
|
||||
return inb_p(todc_info->nvram_data);
|
||||
}
|
||||
|
||||
void
|
||||
todc_mc146818_write_val(int addr, unsigned char val)
|
||||
{
|
||||
outb_p(addr, todc_info->nvram_as0);
|
||||
outb_p(val, todc_info->nvram_data);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Routines to make RTC chips with NVRAM buried behind an addr/data pair
|
||||
* have the NVRAM and clock regs appear at the same level.
|
||||
* The NVRAM will appear to start at addr 0 and the clock regs will appear
|
||||
* to start immediately after the NVRAM (actually, start at offset
|
||||
* todc_info->nvram_size).
|
||||
*/
|
||||
static inline u_char
|
||||
todc_read_val(int addr)
|
||||
{
|
||||
u_char val;
|
||||
|
||||
if (todc_info->sw_flags & TODC_FLAG_2_LEVEL_NVRAM) {
|
||||
if (addr < todc_info->nvram_size) { /* NVRAM */
|
||||
ppc_md.rtc_write_val(todc_info->nvram_addr_reg, addr);
|
||||
val = ppc_md.rtc_read_val(todc_info->nvram_data_reg);
|
||||
} else { /* Clock Reg */
|
||||
addr -= todc_info->nvram_size;
|
||||
val = ppc_md.rtc_read_val(addr);
|
||||
}
|
||||
} else
|
||||
val = ppc_md.rtc_read_val(addr);
|
||||
|
||||
return val;
|
||||
}
|
||||
|
||||
static inline void
|
||||
todc_write_val(int addr, u_char val)
|
||||
{
|
||||
if (todc_info->sw_flags & TODC_FLAG_2_LEVEL_NVRAM) {
|
||||
if (addr < todc_info->nvram_size) { /* NVRAM */
|
||||
ppc_md.rtc_write_val(todc_info->nvram_addr_reg, addr);
|
||||
ppc_md.rtc_write_val(todc_info->nvram_data_reg, val);
|
||||
} else { /* Clock Reg */
|
||||
addr -= todc_info->nvram_size;
|
||||
ppc_md.rtc_write_val(addr, val);
|
||||
}
|
||||
} else
|
||||
ppc_md.rtc_write_val(addr, val);
|
||||
}
|
||||
|
||||
/*
|
||||
* TODC routines
|
||||
*
|
||||
* There is some ugly stuff in that there are assumptions for the mc146818.
|
||||
*
|
||||
* Assumptions:
|
||||
* - todc_info->control_a has the offset as mc146818 Register B reg
|
||||
* - todc_info->control_b has the offset as mc146818 Register A reg
|
||||
* - m48txx control reg's write enable or 'W' bit is same as
|
||||
* mc146818 Register B 'SET' bit (i.e., 0x80)
|
||||
*
|
||||
* These assumptions were made to make the code simpler.
|
||||
*/
|
||||
long __init
|
||||
todc_time_init(void)
|
||||
{
|
||||
u_char cntl_b;
|
||||
|
||||
if (!ppc_md.rtc_read_val)
|
||||
ppc_md.rtc_read_val = ppc_md.nvram_read_val;
|
||||
if (!ppc_md.rtc_write_val)
|
||||
ppc_md.rtc_write_val = ppc_md.nvram_write_val;
|
||||
|
||||
cntl_b = todc_read_val(todc_info->control_b);
|
||||
|
||||
if (todc_info->rtc_type == TODC_TYPE_MC146818) {
|
||||
if ((cntl_b & 0x70) != 0x20) {
|
||||
printk(KERN_INFO "TODC real-time-clock was stopped."
|
||||
" Now starting...");
|
||||
cntl_b &= ~0x70;
|
||||
cntl_b |= 0x20;
|
||||
}
|
||||
|
||||
todc_write_val(todc_info->control_b, cntl_b);
|
||||
} else if (todc_info->rtc_type == TODC_TYPE_DS17285) {
|
||||
u_char mode;
|
||||
|
||||
mode = todc_read_val(TODC_TYPE_DS17285_CNTL_A);
|
||||
/* Make sure countdown clear is not set */
|
||||
mode &= ~0x40;
|
||||
/* Enable oscillator, extended register set */
|
||||
mode |= 0x30;
|
||||
todc_write_val(TODC_TYPE_DS17285_CNTL_A, mode);
|
||||
|
||||
} else if (todc_info->rtc_type == TODC_TYPE_DS1501) {
|
||||
u_char month;
|
||||
|
||||
todc_info->enable_read = TODC_DS1501_CNTL_B_TE;
|
||||
todc_info->enable_write = TODC_DS1501_CNTL_B_TE;
|
||||
|
||||
month = todc_read_val(todc_info->month);
|
||||
|
||||
if ((month & 0x80) == 0x80) {
|
||||
printk(KERN_INFO "TODC %s %s\n",
|
||||
"real-time-clock was stopped.",
|
||||
"Now starting...");
|
||||
month &= ~0x80;
|
||||
todc_write_val(todc_info->month, month);
|
||||
}
|
||||
|
||||
cntl_b &= ~TODC_DS1501_CNTL_B_TE;
|
||||
todc_write_val(todc_info->control_b, cntl_b);
|
||||
} else { /* must be a m48txx type */
|
||||
u_char cntl_a;
|
||||
|
||||
todc_info->enable_read = TODC_MK48TXX_CNTL_A_R;
|
||||
todc_info->enable_write = TODC_MK48TXX_CNTL_A_W;
|
||||
|
||||
cntl_a = todc_read_val(todc_info->control_a);
|
||||
|
||||
/* Check & clear STOP bit in control B register */
|
||||
if (cntl_b & TODC_MK48TXX_DAY_CB) {
|
||||
printk(KERN_INFO "TODC %s %s\n",
|
||||
"real-time-clock was stopped.",
|
||||
"Now starting...");
|
||||
|
||||
cntl_a |= todc_info->enable_write;
|
||||
cntl_b &= ~TODC_MK48TXX_DAY_CB;/* Start Oscil */
|
||||
|
||||
todc_write_val(todc_info->control_a, cntl_a);
|
||||
todc_write_val(todc_info->control_b, cntl_b);
|
||||
}
|
||||
|
||||
/* Make sure READ & WRITE bits are cleared. */
|
||||
cntl_a &= ~(todc_info->enable_write | todc_info->enable_read);
|
||||
todc_write_val(todc_info->control_a, cntl_a);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* There is some ugly stuff in that there are assumptions that for a mc146818,
|
||||
* the todc_info->control_a has the offset of the mc146818 Register B reg and
|
||||
* that the register'ss 'SET' bit is the same as the m48txx's write enable
|
||||
* bit in the control register of the m48txx (i.e., 0x80).
|
||||
*
|
||||
* It was done to make the code look simpler.
|
||||
*/
|
||||
void
|
||||
todc_get_rtc_time(struct rtc_time *tm)
|
||||
{
|
||||
uint year = 0, mon = 0, mday = 0, hour = 0, min = 0, sec = 0;
|
||||
uint limit, i;
|
||||
u_char save_control, uip = 0;
|
||||
extern void GregorianDay(struct rtc_time *);
|
||||
|
||||
spin_lock(&rtc_lock);
|
||||
save_control = todc_read_val(todc_info->control_a);
|
||||
|
||||
if (todc_info->rtc_type != TODC_TYPE_MC146818) {
|
||||
limit = 1;
|
||||
|
||||
switch (todc_info->rtc_type) {
|
||||
case TODC_TYPE_DS1553:
|
||||
case TODC_TYPE_DS1557:
|
||||
case TODC_TYPE_DS1743:
|
||||
case TODC_TYPE_DS1746: /* XXXX BAD HACK -> FIX */
|
||||
case TODC_TYPE_DS1747:
|
||||
case TODC_TYPE_DS17285:
|
||||
break;
|
||||
default:
|
||||
todc_write_val(todc_info->control_a,
|
||||
(save_control | todc_info->enable_read));
|
||||
}
|
||||
} else
|
||||
limit = 100000000;
|
||||
|
||||
for (i=0; i<limit; i++) {
|
||||
if (todc_info->rtc_type == TODC_TYPE_MC146818)
|
||||
uip = todc_read_val(todc_info->RTC_FREQ_SELECT);
|
||||
|
||||
sec = todc_read_val(todc_info->seconds) & 0x7f;
|
||||
min = todc_read_val(todc_info->minutes) & 0x7f;
|
||||
hour = todc_read_val(todc_info->hours) & 0x3f;
|
||||
mday = todc_read_val(todc_info->day_of_month) & 0x3f;
|
||||
mon = todc_read_val(todc_info->month) & 0x1f;
|
||||
year = todc_read_val(todc_info->year) & 0xff;
|
||||
|
||||
if (todc_info->rtc_type == TODC_TYPE_MC146818) {
|
||||
uip |= todc_read_val(todc_info->RTC_FREQ_SELECT);
|
||||
if ((uip & RTC_UIP) == 0)
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (todc_info->rtc_type != TODC_TYPE_MC146818) {
|
||||
switch (todc_info->rtc_type) {
|
||||
case TODC_TYPE_DS1553:
|
||||
case TODC_TYPE_DS1557:
|
||||
case TODC_TYPE_DS1743:
|
||||
case TODC_TYPE_DS1746: /* XXXX BAD HACK -> FIX */
|
||||
case TODC_TYPE_DS1747:
|
||||
case TODC_TYPE_DS17285:
|
||||
break;
|
||||
default:
|
||||
save_control &= ~(todc_info->enable_read);
|
||||
todc_write_val(todc_info->control_a, save_control);
|
||||
}
|
||||
}
|
||||
spin_unlock(&rtc_lock);
|
||||
|
||||
if ((todc_info->rtc_type != TODC_TYPE_MC146818)
|
||||
|| ((save_control & RTC_DM_BINARY) == 0)
|
||||
|| RTC_ALWAYS_BCD) {
|
||||
BCD_TO_BIN(sec);
|
||||
BCD_TO_BIN(min);
|
||||
BCD_TO_BIN(hour);
|
||||
BCD_TO_BIN(mday);
|
||||
BCD_TO_BIN(mon);
|
||||
BCD_TO_BIN(year);
|
||||
}
|
||||
|
||||
if ((year + 1900) < 1970) {
|
||||
year += 100;
|
||||
}
|
||||
|
||||
tm->tm_sec = sec;
|
||||
tm->tm_min = min;
|
||||
tm->tm_hour = hour;
|
||||
tm->tm_mday = mday;
|
||||
tm->tm_mon = mon;
|
||||
tm->tm_year = year;
|
||||
|
||||
GregorianDay(tm);
|
||||
}
|
||||
|
||||
int
|
||||
todc_set_rtc_time(struct rtc_time *tm)
|
||||
{
|
||||
u_char save_control, save_freq_select = 0;
|
||||
|
||||
spin_lock(&rtc_lock);
|
||||
save_control = todc_read_val(todc_info->control_a);
|
||||
|
||||
/* Assuming MK48T59_RTC_CA_WRITE & RTC_SET are equal */
|
||||
todc_write_val(todc_info->control_a,
|
||||
(save_control | todc_info->enable_write));
|
||||
save_control &= ~(todc_info->enable_write); /* in case it was set */
|
||||
|
||||
if (todc_info->rtc_type == TODC_TYPE_MC146818) {
|
||||
save_freq_select = todc_read_val(todc_info->RTC_FREQ_SELECT);
|
||||
todc_write_val(todc_info->RTC_FREQ_SELECT,
|
||||
save_freq_select | RTC_DIV_RESET2);
|
||||
}
|
||||
|
||||
if ((todc_info->rtc_type != TODC_TYPE_MC146818)
|
||||
|| ((save_control & RTC_DM_BINARY) == 0)
|
||||
|| RTC_ALWAYS_BCD) {
|
||||
BIN_TO_BCD(tm->tm_sec);
|
||||
BIN_TO_BCD(tm->tm_min);
|
||||
BIN_TO_BCD(tm->tm_hour);
|
||||
BIN_TO_BCD(tm->tm_mon);
|
||||
BIN_TO_BCD(tm->tm_mday);
|
||||
BIN_TO_BCD(tm->tm_year);
|
||||
}
|
||||
|
||||
todc_write_val(todc_info->seconds, tm->tm_sec);
|
||||
todc_write_val(todc_info->minutes, tm->tm_min);
|
||||
todc_write_val(todc_info->hours, tm->tm_hour);
|
||||
todc_write_val(todc_info->month, tm->tm_mon);
|
||||
todc_write_val(todc_info->day_of_month, tm->tm_mday);
|
||||
todc_write_val(todc_info->year, tm->tm_year);
|
||||
|
||||
todc_write_val(todc_info->control_a, save_control);
|
||||
|
||||
if (todc_info->rtc_type == TODC_TYPE_MC146818)
|
||||
todc_write_val(todc_info->RTC_FREQ_SELECT, save_freq_select);
|
||||
|
||||
spin_unlock(&rtc_lock);
|
||||
return 0;
|
||||
}
|
145
arch/powerpc/sysdev/tsi108_dev.c
一般檔案
145
arch/powerpc/sysdev/tsi108_dev.c
一般檔案
@@ -0,0 +1,145 @@
|
||||
/*
|
||||
* tsi108/109 device setup code
|
||||
*
|
||||
* Maintained by Roy Zang < tie-fei.zang@freescale.com >
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License as published by the
|
||||
* Free Software Foundation; either version 2 of the License, or (at your
|
||||
* option) any later version.
|
||||
*/
|
||||
|
||||
#include <linux/config.h>
|
||||
#include <linux/stddef.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/errno.h>
|
||||
#include <linux/major.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/irq.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/device.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <asm/tsi108.h>
|
||||
|
||||
#include <asm/system.h>
|
||||
#include <asm/atomic.h>
|
||||
#include <asm/io.h>
|
||||
#include <asm/irq.h>
|
||||
#include <asm/prom.h>
|
||||
#include <mm/mmu_decl.h>
|
||||
|
||||
#undef DEBUG
|
||||
|
||||
#ifdef DEBUG
|
||||
#define DBG(fmt...) do { printk(fmt); } while(0)
|
||||
#else
|
||||
#define DBG(fmt...) do { } while(0)
|
||||
#endif
|
||||
|
||||
static phys_addr_t tsi108_csr_base = -1;
|
||||
|
||||
phys_addr_t get_csrbase(void)
|
||||
{
|
||||
struct device_node *tsi;
|
||||
|
||||
if (tsi108_csr_base != -1)
|
||||
return tsi108_csr_base;
|
||||
|
||||
tsi = of_find_node_by_type(NULL, "tsi-bridge");
|
||||
if (tsi) {
|
||||
unsigned int size;
|
||||
void *prop = get_property(tsi, "reg", &size);
|
||||
tsi108_csr_base = of_translate_address(tsi, prop);
|
||||
of_node_put(tsi);
|
||||
};
|
||||
return tsi108_csr_base;
|
||||
}
|
||||
|
||||
u32 get_vir_csrbase(void)
|
||||
{
|
||||
return (u32) (ioremap(get_csrbase(), 0x10000));
|
||||
}
|
||||
|
||||
EXPORT_SYMBOL(get_csrbase);
|
||||
EXPORT_SYMBOL(get_vir_csrbase);
|
||||
|
||||
static int __init tsi108_eth_of_init(void)
|
||||
{
|
||||
struct device_node *np;
|
||||
unsigned int i;
|
||||
struct platform_device *tsi_eth_dev;
|
||||
struct resource res;
|
||||
int ret;
|
||||
|
||||
for (np = NULL, i = 0;
|
||||
(np = of_find_compatible_node(np, "network", "tsi-ethernet")) != NULL;
|
||||
i++) {
|
||||
struct resource r[2];
|
||||
struct device_node *phy;
|
||||
hw_info tsi_eth_data;
|
||||
unsigned int *id;
|
||||
unsigned int *phy_id;
|
||||
void *mac_addr;
|
||||
phandle *ph;
|
||||
|
||||
memset(r, 0, sizeof(r));
|
||||
memset(&tsi_eth_data, 0, sizeof(tsi_eth_data));
|
||||
|
||||
ret = of_address_to_resource(np, 0, &r[0]);
|
||||
DBG("%s: name:start->end = %s:0x%lx-> 0x%lx\n",
|
||||
__FUNCTION__,r[0].name, r[0].start, r[0].end);
|
||||
if (ret)
|
||||
goto err;
|
||||
|
||||
r[1].name = "tx";
|
||||
r[1].start = np->intrs[0].line;
|
||||
r[1].end = np->intrs[0].line;
|
||||
r[1].flags = IORESOURCE_IRQ;
|
||||
|
||||
tsi_eth_dev =
|
||||
platform_device_register_simple("tsi-ethernet", i, &r[0],
|
||||
np->n_intrs + 1);
|
||||
|
||||
if (IS_ERR(tsi_eth_dev)) {
|
||||
ret = PTR_ERR(tsi_eth_dev);
|
||||
goto err;
|
||||
}
|
||||
|
||||
mac_addr = get_property(np, "address", NULL);
|
||||
memcpy(tsi_eth_data.mac_addr, mac_addr, 6);
|
||||
|
||||
ph = (phandle *) get_property(np, "phy-handle", NULL);
|
||||
phy = of_find_node_by_phandle(*ph);
|
||||
|
||||
if (phy == NULL) {
|
||||
ret = -ENODEV;
|
||||
goto unreg;
|
||||
}
|
||||
|
||||
id = (u32 *) get_property(phy, "reg", NULL);
|
||||
phy_id = (u32 *) get_property(phy, "phy-id", NULL);
|
||||
ret = of_address_to_resource(phy, 0, &res);
|
||||
if (ret) {
|
||||
of_node_put(phy);
|
||||
goto unreg;
|
||||
}
|
||||
tsi_eth_data.regs = r[0].start;
|
||||
tsi_eth_data.phyregs = res.start;
|
||||
tsi_eth_data.phy = *phy_id;
|
||||
tsi_eth_data.irq_num = np->intrs[0].line;
|
||||
of_node_put(phy);
|
||||
ret =
|
||||
platform_device_add_data(tsi_eth_dev, &tsi_eth_data,
|
||||
sizeof(hw_info));
|
||||
if (ret)
|
||||
goto unreg;
|
||||
}
|
||||
return 0;
|
||||
unreg:
|
||||
platform_device_unregister(tsi_eth_dev);
|
||||
err:
|
||||
return ret;
|
||||
}
|
||||
|
||||
arch_initcall(tsi108_eth_of_init);
|
412
arch/powerpc/sysdev/tsi108_pci.c
一般檔案
412
arch/powerpc/sysdev/tsi108_pci.c
一般檔案
@@ -0,0 +1,412 @@
|
||||
/*
|
||||
* Common routines for Tundra Semiconductor TSI108 host bridge.
|
||||
*
|
||||
* 2004-2005 (c) Tundra Semiconductor Corp.
|
||||
* Author: Alex Bounine (alexandreb@tundra.com)
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License as published by the Free
|
||||
* Software Foundation; either version 2 of the License, or (at your option)
|
||||
* any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along with
|
||||
* this program; if not, write to the Free Software Foundation, Inc., 59
|
||||
* Temple Place - Suite 330, Boston, MA 02111-1307, USA.
|
||||
*/
|
||||
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/pci.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/irq.h>
|
||||
#include <linux/interrupt.h>
|
||||
|
||||
|
||||
#include <asm/byteorder.h>
|
||||
#include <asm/io.h>
|
||||
#include <asm/irq.h>
|
||||
#include <asm/uaccess.h>
|
||||
#include <asm/machdep.h>
|
||||
#include <asm/pci-bridge.h>
|
||||
#include <asm/tsi108.h>
|
||||
#include <asm/tsi108_irq.h>
|
||||
#include <asm/prom.h>
|
||||
|
||||
#undef DEBUG
|
||||
#ifdef DEBUG
|
||||
#define DBG(x...) printk(x)
|
||||
#else
|
||||
#define DBG(x...)
|
||||
#endif
|
||||
|
||||
#define tsi_mk_config_addr(bus, devfunc, offset) \
|
||||
((((bus)<<16) | ((devfunc)<<8) | (offset & 0xfc)) + tsi108_pci_cfg_base)
|
||||
|
||||
u32 tsi108_pci_cfg_base;
|
||||
u32 tsi108_csr_vir_base;
|
||||
|
||||
extern u32 get_vir_csrbase(void);
|
||||
extern u32 tsi108_read_reg(u32 reg_offset);
|
||||
extern void tsi108_write_reg(u32 reg_offset, u32 val);
|
||||
|
||||
int
|
||||
tsi108_direct_write_config(struct pci_bus *bus, unsigned int devfunc,
|
||||
int offset, int len, u32 val)
|
||||
{
|
||||
volatile unsigned char *cfg_addr;
|
||||
|
||||
if (ppc_md.pci_exclude_device)
|
||||
if (ppc_md.pci_exclude_device(bus->number, devfunc))
|
||||
return PCIBIOS_DEVICE_NOT_FOUND;
|
||||
|
||||
cfg_addr = (unsigned char *)(tsi_mk_config_addr(bus->number,
|
||||
devfunc, offset) |
|
||||
(offset & 0x03));
|
||||
|
||||
#ifdef DEBUG
|
||||
printk("PCI CFG write : ");
|
||||
printk("%d:0x%x:0x%x ", bus->number, devfunc, offset);
|
||||
printk("%d ADDR=0x%08x ", len, (uint) cfg_addr);
|
||||
printk("data = 0x%08x\n", val);
|
||||
#endif
|
||||
|
||||
switch (len) {
|
||||
case 1:
|
||||
out_8((u8 *) cfg_addr, val);
|
||||
break;
|
||||
case 2:
|
||||
out_le16((u16 *) cfg_addr, val);
|
||||
break;
|
||||
default:
|
||||
out_le32((u32 *) cfg_addr, val);
|
||||
break;
|
||||
}
|
||||
|
||||
return PCIBIOS_SUCCESSFUL;
|
||||
}
|
||||
|
||||
void tsi108_clear_pci_error(u32 pci_cfg_base)
|
||||
{
|
||||
u32 err_stat, err_addr, pci_stat;
|
||||
|
||||
/*
|
||||
* Quietly clear PB and PCI error flags set as result
|
||||
* of PCI/X configuration read requests.
|
||||
*/
|
||||
|
||||
/* Read PB Error Log Registers */
|
||||
|
||||
err_stat = tsi108_read_reg(TSI108_PB_OFFSET + TSI108_PB_ERRCS);
|
||||
err_addr = tsi108_read_reg(TSI108_PB_OFFSET + TSI108_PB_AERR);
|
||||
|
||||
if (err_stat & TSI108_PB_ERRCS_ES) {
|
||||
/* Clear error flag */
|
||||
tsi108_write_reg(TSI108_PB_OFFSET + TSI108_PB_ERRCS,
|
||||
TSI108_PB_ERRCS_ES);
|
||||
|
||||
/* Clear read error reported in PB_ISR */
|
||||
tsi108_write_reg(TSI108_PB_OFFSET + TSI108_PB_ISR,
|
||||
TSI108_PB_ISR_PBS_RD_ERR);
|
||||
|
||||
/* Clear PCI/X bus cfg errors if applicable */
|
||||
if ((err_addr & 0xFF000000) == pci_cfg_base) {
|
||||
pci_stat =
|
||||
tsi108_read_reg(TSI108_PCI_OFFSET + TSI108_PCI_CSR);
|
||||
tsi108_write_reg(TSI108_PCI_OFFSET + TSI108_PCI_CSR,
|
||||
pci_stat);
|
||||
}
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
#define __tsi108_read_pci_config(x, addr, op) \
|
||||
__asm__ __volatile__( \
|
||||
" "op" %0,0,%1\n" \
|
||||
"1: eieio\n" \
|
||||
"2:\n" \
|
||||
".section .fixup,\"ax\"\n" \
|
||||
"3: li %0,-1\n" \
|
||||
" b 2b\n" \
|
||||
".section __ex_table,\"a\"\n" \
|
||||
" .align 2\n" \
|
||||
" .long 1b,3b\n" \
|
||||
".text" \
|
||||
: "=r"(x) : "r"(addr))
|
||||
|
||||
int
|
||||
tsi108_direct_read_config(struct pci_bus *bus, unsigned int devfn, int offset,
|
||||
int len, u32 * val)
|
||||
{
|
||||
volatile unsigned char *cfg_addr;
|
||||
u32 temp;
|
||||
|
||||
if (ppc_md.pci_exclude_device)
|
||||
if (ppc_md.pci_exclude_device(bus->number, devfn))
|
||||
return PCIBIOS_DEVICE_NOT_FOUND;
|
||||
|
||||
cfg_addr = (unsigned char *)(tsi_mk_config_addr(bus->number,
|
||||
devfn,
|
||||
offset) | (offset &
|
||||
0x03));
|
||||
|
||||
switch (len) {
|
||||
case 1:
|
||||
__tsi108_read_pci_config(temp, cfg_addr, "lbzx");
|
||||
break;
|
||||
case 2:
|
||||
__tsi108_read_pci_config(temp, cfg_addr, "lhbrx");
|
||||
break;
|
||||
default:
|
||||
__tsi108_read_pci_config(temp, cfg_addr, "lwbrx");
|
||||
break;
|
||||
}
|
||||
|
||||
*val = temp;
|
||||
|
||||
#ifdef DEBUG
|
||||
if ((0xFFFFFFFF != temp) && (0xFFFF != temp) && (0xFF != temp)) {
|
||||
printk("PCI CFG read : ");
|
||||
printk("%d:0x%x:0x%x ", bus->number, devfn, offset);
|
||||
printk("%d ADDR=0x%08x ", len, (uint) cfg_addr);
|
||||
printk("data = 0x%x\n", *val);
|
||||
}
|
||||
#endif
|
||||
return PCIBIOS_SUCCESSFUL;
|
||||
}
|
||||
|
||||
void tsi108_clear_pci_cfg_error(void)
|
||||
{
|
||||
tsi108_clear_pci_error(TSI108_PCI_CFG_BASE_PHYS);
|
||||
}
|
||||
|
||||
static struct pci_ops tsi108_direct_pci_ops = {
|
||||
tsi108_direct_read_config,
|
||||
tsi108_direct_write_config
|
||||
};
|
||||
|
||||
int __init tsi108_setup_pci(struct device_node *dev)
|
||||
{
|
||||
int len;
|
||||
struct pci_controller *hose;
|
||||
struct resource rsrc;
|
||||
int *bus_range;
|
||||
int primary = 0, has_address = 0;
|
||||
|
||||
/* PCI Config mapping */
|
||||
tsi108_pci_cfg_base = (u32)ioremap(TSI108_PCI_CFG_BASE_PHYS,
|
||||
TSI108_PCI_CFG_SIZE);
|
||||
DBG("TSI_PCI: %s tsi108_pci_cfg_base=0x%x\n", __FUNCTION__,
|
||||
tsi108_pci_cfg_base);
|
||||
|
||||
/* Fetch host bridge registers address */
|
||||
has_address = (of_address_to_resource(dev, 0, &rsrc) == 0);
|
||||
|
||||
/* Get bus range if any */
|
||||
bus_range = (int *)get_property(dev, "bus-range", &len);
|
||||
if (bus_range == NULL || len < 2 * sizeof(int)) {
|
||||
printk(KERN_WARNING "Can't get bus-range for %s, assume"
|
||||
" bus 0\n", dev->full_name);
|
||||
}
|
||||
|
||||
hose = pcibios_alloc_controller();
|
||||
|
||||
if (!hose) {
|
||||
printk("PCI Host bridge init failed\n");
|
||||
return -ENOMEM;
|
||||
}
|
||||
hose->arch_data = dev;
|
||||
hose->set_cfg_type = 1;
|
||||
|
||||
hose->first_busno = bus_range ? bus_range[0] : 0;
|
||||
hose->last_busno = bus_range ? bus_range[1] : 0xff;
|
||||
|
||||
(hose)->ops = &tsi108_direct_pci_ops;
|
||||
|
||||
printk(KERN_INFO "Found tsi108 PCI host bridge at 0x%08lx. "
|
||||
"Firmware bus number: %d->%d\n",
|
||||
rsrc.start, hose->first_busno, hose->last_busno);
|
||||
|
||||
/* Interpret the "ranges" property */
|
||||
/* This also maps the I/O region and sets isa_io/mem_base */
|
||||
pci_process_bridge_OF_ranges(hose, dev, primary);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Low level utility functions
|
||||
*/
|
||||
|
||||
static void tsi108_pci_int_mask(u_int irq)
|
||||
{
|
||||
u_int irp_cfg;
|
||||
int int_line = (irq - IRQ_PCI_INTAD_BASE);
|
||||
|
||||
irp_cfg = tsi108_read_reg(TSI108_PCI_OFFSET + TSI108_PCI_IRP_CFG_CTL);
|
||||
mb();
|
||||
irp_cfg |= (1 << int_line); /* INTx_DIR = output */
|
||||
irp_cfg &= ~(3 << (8 + (int_line * 2))); /* INTx_TYPE = unused */
|
||||
tsi108_write_reg(TSI108_PCI_OFFSET + TSI108_PCI_IRP_CFG_CTL, irp_cfg);
|
||||
mb();
|
||||
irp_cfg = tsi108_read_reg(TSI108_PCI_OFFSET + TSI108_PCI_IRP_CFG_CTL);
|
||||
}
|
||||
|
||||
static void tsi108_pci_int_unmask(u_int irq)
|
||||
{
|
||||
u_int irp_cfg;
|
||||
int int_line = (irq - IRQ_PCI_INTAD_BASE);
|
||||
|
||||
irp_cfg = tsi108_read_reg(TSI108_PCI_OFFSET + TSI108_PCI_IRP_CFG_CTL);
|
||||
mb();
|
||||
irp_cfg &= ~(1 << int_line);
|
||||
irp_cfg |= (3 << (8 + (int_line * 2)));
|
||||
tsi108_write_reg(TSI108_PCI_OFFSET + TSI108_PCI_IRP_CFG_CTL, irp_cfg);
|
||||
mb();
|
||||
}
|
||||
|
||||
static void init_pci_source(void)
|
||||
{
|
||||
tsi108_write_reg(TSI108_PCI_OFFSET + TSI108_PCI_IRP_CFG_CTL,
|
||||
0x0000ff00);
|
||||
tsi108_write_reg(TSI108_PCI_OFFSET + TSI108_PCI_IRP_ENABLE,
|
||||
TSI108_PCI_IRP_ENABLE_P_INT);
|
||||
mb();
|
||||
}
|
||||
|
||||
static inline int get_pci_source(void)
|
||||
{
|
||||
u_int temp = 0;
|
||||
int irq = -1;
|
||||
int i;
|
||||
u_int pci_irp_stat;
|
||||
static int mask = 0;
|
||||
|
||||
/* Read PCI/X block interrupt status register */
|
||||
pci_irp_stat = tsi108_read_reg(TSI108_PCI_OFFSET + TSI108_PCI_IRP_STAT);
|
||||
mb();
|
||||
|
||||
if (pci_irp_stat & TSI108_PCI_IRP_STAT_P_INT) {
|
||||
/* Process Interrupt from PCI bus INTA# - INTD# lines */
|
||||
temp =
|
||||
tsi108_read_reg(TSI108_PCI_OFFSET +
|
||||
TSI108_PCI_IRP_INTAD) & 0xf;
|
||||
mb();
|
||||
for (i = 0; i < 4; i++, mask++) {
|
||||
if (temp & (1 << mask % 4)) {
|
||||
irq = IRQ_PCI_INTA + mask % 4;
|
||||
mask++;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/* Disable interrupts from PCI block */
|
||||
temp = tsi108_read_reg(TSI108_PCI_OFFSET + TSI108_PCI_IRP_ENABLE);
|
||||
tsi108_write_reg(TSI108_PCI_OFFSET + TSI108_PCI_IRP_ENABLE,
|
||||
temp & ~TSI108_PCI_IRP_ENABLE_P_INT);
|
||||
mb();
|
||||
(void)tsi108_read_reg(TSI108_PCI_OFFSET + TSI108_PCI_IRP_ENABLE);
|
||||
mb();
|
||||
}
|
||||
#ifdef DEBUG
|
||||
else {
|
||||
printk("TSI108_PIC: error in TSI108_PCI_IRP_STAT\n");
|
||||
pci_irp_stat =
|
||||
tsi108_read_reg(TSI108_PCI_OFFSET + TSI108_PCI_IRP_STAT);
|
||||
temp =
|
||||
tsi108_read_reg(TSI108_PCI_OFFSET + TSI108_PCI_IRP_INTAD);
|
||||
mb();
|
||||
printk(">> stat=0x%08x intad=0x%08x ", pci_irp_stat, temp);
|
||||
temp =
|
||||
tsi108_read_reg(TSI108_PCI_OFFSET + TSI108_PCI_IRP_CFG_CTL);
|
||||
mb();
|
||||
printk("cfg_ctl=0x%08x ", temp);
|
||||
temp =
|
||||
tsi108_read_reg(TSI108_PCI_OFFSET + TSI108_PCI_IRP_ENABLE);
|
||||
mb();
|
||||
printk("irp_enable=0x%08x\n", temp);
|
||||
}
|
||||
#endif /* end of DEBUG */
|
||||
|
||||
return irq;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Linux descriptor level callbacks
|
||||
*/
|
||||
|
||||
static void tsi108_pci_irq_enable(u_int irq)
|
||||
{
|
||||
tsi108_pci_int_unmask(irq);
|
||||
}
|
||||
|
||||
static void tsi108_pci_irq_disable(u_int irq)
|
||||
{
|
||||
tsi108_pci_int_mask(irq);
|
||||
}
|
||||
|
||||
static void tsi108_pci_irq_ack(u_int irq)
|
||||
{
|
||||
tsi108_pci_int_mask(irq);
|
||||
}
|
||||
|
||||
static void tsi108_pci_irq_end(u_int irq)
|
||||
{
|
||||
tsi108_pci_int_unmask(irq);
|
||||
|
||||
/* Enable interrupts from PCI block */
|
||||
tsi108_write_reg(TSI108_PCI_OFFSET + TSI108_PCI_IRP_ENABLE,
|
||||
tsi108_read_reg(TSI108_PCI_OFFSET +
|
||||
TSI108_PCI_IRP_ENABLE) |
|
||||
TSI108_PCI_IRP_ENABLE_P_INT);
|
||||
mb();
|
||||
}
|
||||
|
||||
/*
|
||||
* Interrupt controller descriptor for cascaded PCI interrupt controller.
|
||||
*/
|
||||
|
||||
struct hw_interrupt_type tsi108_pci_irq = {
|
||||
.typename = "tsi108_PCI_int",
|
||||
.enable = tsi108_pci_irq_enable,
|
||||
.disable = tsi108_pci_irq_disable,
|
||||
.ack = tsi108_pci_irq_ack,
|
||||
.end = tsi108_pci_irq_end,
|
||||
};
|
||||
|
||||
/*
|
||||
* Exported functions
|
||||
*/
|
||||
|
||||
/*
|
||||
* The Tsi108 PCI interrupts initialization routine.
|
||||
*
|
||||
* The INTA# - INTD# interrupts on the PCI bus are reported by the PCI block
|
||||
* to the MPIC using single interrupt source (IRQ_TSI108_PCI). Therefore the
|
||||
* PCI block has to be treated as a cascaded interrupt controller connected
|
||||
* to the MPIC.
|
||||
*/
|
||||
|
||||
void __init tsi108_pci_int_init(void)
|
||||
{
|
||||
u_int i;
|
||||
|
||||
DBG("Tsi108_pci_int_init: initializing PCI interrupts\n");
|
||||
|
||||
for (i = 0; i < NUM_PCI_IRQS; i++) {
|
||||
irq_desc[i + IRQ_PCI_INTAD_BASE].handler = &tsi108_pci_irq;
|
||||
irq_desc[i + IRQ_PCI_INTAD_BASE].status |= IRQ_LEVEL;
|
||||
}
|
||||
|
||||
init_pci_source();
|
||||
}
|
||||
|
||||
int tsi108_irq_cascade(struct pt_regs *regs, void *unused)
|
||||
{
|
||||
return get_pci_source();
|
||||
}
|
新增問題並參考
封鎖使用者