From 4b2131f81af18cd75780a21b822b019205706d4e Mon Sep 17 00:00:00 2001 From: Benjamin Herrenschmidt Date: Fri, 6 Feb 2004 12:47:11 +1100 Subject: ppc32: Update/cleanup low level POWER4 & G5 CPU support --- include/asm-ppc/reg.h | 4 ++++ 1 file changed, 4 insertions(+) (limited to 'include') diff --git a/include/asm-ppc/reg.h b/include/asm-ppc/reg.h index 20953f3bca1e..bc57d5f388e2 100644 --- a/include/asm-ppc/reg.h +++ b/include/asm-ppc/reg.h @@ -91,6 +91,7 @@ #define SPRN_TBRU 0x10D /* Time Base Read Upper Register (user, R/O) */ #define SPRN_TBWL 0x11C /* Time Base Lower Register (super, R/W) */ #define SPRN_TBWU 0x11D /* Time Base Upper Register (super, R/W) */ +#define SPRN_HIOR 0x137 /* 970 Hypervisor interrupt offset */ #define SPRN_DBAT0L 0x219 /* Data BAT 0 Lower Register */ #define SPRN_DBAT0U 0x218 /* Data BAT 0 Upper Register */ #define SPRN_DBAT1L 0x21B /* Data BAT 1 Lower Register */ @@ -179,7 +180,10 @@ #define HID1_PC3 (1<<13) /* 7450 PLL_CFG[3] */ #define HID1_SYNCBE (1<<11) /* 7450 ABE for sync, eieio */ #define HID1_ABE (1<<10) /* 7450 Address Broadcast Enable */ +#define SPRN_HID2 0x3F8 /* Hardware Implementation Register 2 */ #define SPRN_IABR 0x3F2 /* Instruction Address Breakpoint Register */ +#define SPRN_HID4 0x3F4 /* 970 HID4 */ +#define SPRN_HID5 0x3F6 /* 970 HID5 */ #if !defined(SPRN_IAC1) && !defined(SPRN_IAC2) #define SPRN_IAC1 0x3F4 /* Instruction Address Compare 1 */ #define SPRN_IAC2 0x3F5 /* Instruction Address Compare 2 */ -- cgit v1.2.3 From cf9e8745e9c7b0b94927d5052912acbc170fd708 Mon Sep 17 00:00:00 2001 From: Benjamin Herrenschmidt Date: Fri, 6 Feb 2004 13:29:11 +1100 Subject: Move pmac-specific PCI quirks to pmac_pci.c, update cardbus one for new TI controller, add some for fixing up ATA & SATA controllers (switch normal ATA to fully native mode and disable unused function on G5 K2 SATA) --- arch/ppc/kernel/pci.c | 44 +++--------------- arch/ppc/platforms/pmac_pci.c | 105 ++++++++++++++++++++++++++++++++++++++++++ include/linux/pci_ids.h | 1 + 3 files changed, 112 insertions(+), 38 deletions(-) (limited to 'include') diff --git a/arch/ppc/kernel/pci.c b/arch/ppc/kernel/pci.c index dcc82d592f03..8b3d3dfcb66b 100644 --- a/arch/ppc/kernel/pci.c +++ b/arch/ppc/kernel/pci.c @@ -46,7 +46,9 @@ static int reparent_resources(struct resource *parent, struct resource *res); static void fixup_rev1_53c810(struct pci_dev* dev); static void fixup_cpc710_pci64(struct pci_dev* dev); #ifdef CONFIG_PPC_PMAC -static void pcibios_fixup_cardbus(struct pci_dev* dev); +extern void pmac_pci_fixup_cardbus(struct pci_dev* dev); +extern void pmac_pci_fixup_pciata(struct pci_dev* dev); +extern void pmac_pci_fixup_k2_sata(struct pci_dev* dev); #endif #ifdef CONFIG_PPC_OF static u8* pci_to_OF_bus_map; @@ -69,7 +71,9 @@ struct pci_fixup pcibios_fixups[] = { { PCI_FIXUP_HEADER, PCI_ANY_ID, PCI_ANY_ID, pcibios_fixup_resources }, #ifdef CONFIG_PPC_PMAC /* We should add per-machine fixup support in xxx_setup.c or xxx_pci.c */ - { PCI_FIXUP_FINAL, PCI_VENDOR_ID_TI, PCI_ANY_ID, pcibios_fixup_cardbus }, + { PCI_FIXUP_FINAL, PCI_VENDOR_ID_TI, PCI_ANY_ID, pmac_pci_fixup_cardbus }, + { PCI_FIXUP_FINAL, PCI_ANY_ID, PCI_ANY_ID, pmac_pci_fixup_pciata }, + { PCI_FIXUP_HEADER, PCI_VENDOR_ID_SERVERWORKS, 0x0240, pmac_pci_fixup_k2_sata }, #endif /* CONFIG_PPC_PMAC */ { 0 } }; @@ -155,42 +159,6 @@ pcibios_fixup_resources(struct pci_dev *dev) ppc_md.pcibios_fixup_resources(dev); } -#ifdef CONFIG_PPC_PMAC -static void -pcibios_fixup_cardbus(struct pci_dev* dev) -{ - if (_machine != _MACH_Pmac) - return; - /* - * Fix the interrupt routing on the various cardbus bridges - * used on powerbooks - */ - if (dev->vendor != PCI_VENDOR_ID_TI) - return; - if (dev->device == PCI_DEVICE_ID_TI_1130 || - dev->device == PCI_DEVICE_ID_TI_1131) { - u8 val; - /* Enable PCI interrupt */ - if (pci_read_config_byte(dev, 0x91, &val) == 0) - pci_write_config_byte(dev, 0x91, val | 0x30); - /* Disable ISA interrupt mode */ - if (pci_read_config_byte(dev, 0x92, &val) == 0) - pci_write_config_byte(dev, 0x92, val & ~0x06); - } - if (dev->device == PCI_DEVICE_ID_TI_1210 || - dev->device == PCI_DEVICE_ID_TI_1211 || - dev->device == PCI_DEVICE_ID_TI_1410) { - u8 val; - /* 0x8c == TI122X_IRQMUX, 2 says to route the INTA - signal out the MFUNC0 pin */ - if (pci_read_config_byte(dev, 0x8c, &val) == 0) - pci_write_config_byte(dev, 0x8c, (val & ~0x0f) | 2); - /* Disable ISA interrupt mode */ - if (pci_read_config_byte(dev, 0x92, &val) == 0) - pci_write_config_byte(dev, 0x92, val & ~0x06); - } -} -#endif /* CONFIG_PPC_PMAC */ void pcibios_resource_to_bus(struct pci_dev *dev, struct pci_bus_region *region, diff --git a/arch/ppc/platforms/pmac_pci.c b/arch/ppc/platforms/pmac_pci.c index de1c6ec6b03e..d4008da62dc8 100644 --- a/arch/ppc/platforms/pmac_pci.c +++ b/arch/ppc/platforms/pmac_pci.c @@ -628,3 +628,108 @@ pmac_pcibios_after_init(void) } } +void pmac_pci_fixup_cardbus(struct pci_dev* dev) +{ + if (_machine != _MACH_Pmac) + return; + /* + * Fix the interrupt routing on the various cardbus bridges + * used on powerbooks + */ + if (dev->vendor != PCI_VENDOR_ID_TI) + return; + if (dev->device == PCI_DEVICE_ID_TI_1130 || + dev->device == PCI_DEVICE_ID_TI_1131) { + u8 val; + /* Enable PCI interrupt */ + if (pci_read_config_byte(dev, 0x91, &val) == 0) + pci_write_config_byte(dev, 0x91, val | 0x30); + /* Disable ISA interrupt mode */ + if (pci_read_config_byte(dev, 0x92, &val) == 0) + pci_write_config_byte(dev, 0x92, val & ~0x06); + } + if (dev->device == PCI_DEVICE_ID_TI_1210 || + dev->device == PCI_DEVICE_ID_TI_1211 || + dev->device == PCI_DEVICE_ID_TI_1410 || + dev->device == PCI_DEVICE_ID_TI_1510) { + u8 val; + /* 0x8c == TI122X_IRQMUX, 2 says to route the INTA + signal out the MFUNC0 pin */ + if (pci_read_config_byte(dev, 0x8c, &val) == 0) + pci_write_config_byte(dev, 0x8c, (val & ~0x0f) | 2); + /* Disable ISA interrupt mode */ + if (pci_read_config_byte(dev, 0x92, &val) == 0) + pci_write_config_byte(dev, 0x92, val & ~0x06); + } +} + +void pmac_pci_fixup_pciata(struct pci_dev* dev) +{ + u8 progif = 0; + + /* + * On PowerMacs, we try to switch any PCI ATA controller to + * fully native mode + */ + if (_machine != _MACH_Pmac) + return; + /* Some controllers don't have the class IDE */ + if (dev->vendor == PCI_VENDOR_ID_PROMISE) + switch(dev->device) { + case PCI_DEVICE_ID_PROMISE_20246: + case PCI_DEVICE_ID_PROMISE_20262: + case PCI_DEVICE_ID_PROMISE_20263: + case PCI_DEVICE_ID_PROMISE_20265: + case PCI_DEVICE_ID_PROMISE_20267: + case PCI_DEVICE_ID_PROMISE_20268: + case PCI_DEVICE_ID_PROMISE_20269: + case PCI_DEVICE_ID_PROMISE_20270: + case PCI_DEVICE_ID_PROMISE_20271: + case PCI_DEVICE_ID_PROMISE_20275: + case PCI_DEVICE_ID_PROMISE_20276: + case PCI_DEVICE_ID_PROMISE_20277: + goto good; + } + /* Others, check PCI class */ + if ((dev->class >> 8) != PCI_CLASS_STORAGE_IDE) + return; + good: + pci_read_config_byte(dev, PCI_CLASS_PROG, &progif); + if ((progif & 5) != 5) { + printk(KERN_INFO "Forcing PCI IDE into native mode: %s\n", pci_name(dev)); + (void) pci_write_config_byte(dev, PCI_CLASS_PROG, progif|5); + if (pci_read_config_byte(dev, PCI_CLASS_PROG, &progif) || + (progif & 5) != 5) + printk(KERN_ERR "Rewrite of PROGIF failed !\n"); + } +} + +/* + * Disable second function on K2-SATA, it's broken + * and disable IO BARs on first one + */ +void __pmac pmac_pci_fixup_k2_sata(struct pci_dev* dev) +{ + int i; + u16 cmd; + + if (PCI_FUNC(dev->devfn) > 0) { + pci_read_config_word(dev, PCI_COMMAND, &cmd); + cmd &= ~(PCI_COMMAND_IO | PCI_COMMAND_MEMORY); + pci_write_config_word(dev, PCI_COMMAND, cmd); + for (i = 0; i < 6; i++) { + dev->resource[i].start = dev->resource[i].end = 0; + dev->resource[i].flags = 0; + pci_write_config_dword(dev, PCI_BASE_ADDRESS_0 + 4 * i, 0); + } + } else { + pci_read_config_word(dev, PCI_COMMAND, &cmd); + cmd &= ~PCI_COMMAND_IO; + pci_write_config_word(dev, PCI_COMMAND, cmd); + for (i = 0; i < 5; i++) { + dev->resource[i].start = dev->resource[i].end = 0; + dev->resource[i].flags = 0; + pci_write_config_dword(dev, PCI_BASE_ADDRESS_0 + 4 * i, 0); + } + } +} diff --git a/include/linux/pci_ids.h b/include/linux/pci_ids.h index 4dba6ec06e3a..ad515074fc85 100644 --- a/include/linux/pci_ids.h +++ b/include/linux/pci_ids.h @@ -709,6 +709,7 @@ #define PCI_DEVICE_ID_TI_1410 0xac50 #define PCI_DEVICE_ID_TI_1420 0xac51 #define PCI_DEVICE_ID_TI_1520 0xac55 +#define PCI_DEVICE_ID_TI_1510 0xac56 #define PCI_VENDOR_ID_SONY 0x104d #define PCI_DEVICE_ID_SONY_CXD3222 0x8039 -- cgit v1.2.3 From f74710a0bfb50dedac27e544faf8b7b418ff9008 Mon Sep 17 00:00:00 2001 From: Benjamin Herrenschmidt Date: Fri, 6 Feb 2004 14:00:29 +1100 Subject: ppc32: Flush the Hash PTE in ptep_test_and_clear_young() Without this, page aging is broken on ppc32 --- arch/ppc/mm/tlb.c | 20 ++++++++++++++++++++ include/asm-ppc/pgtable.h | 14 +++++++++++++- 2 files changed, 33 insertions(+), 1 deletion(-) (limited to 'include') diff --git a/arch/ppc/mm/tlb.c b/arch/ppc/mm/tlb.c index 832d2d2bd0c2..3bf70f65f655 100644 --- a/arch/ppc/mm/tlb.c +++ b/arch/ppc/mm/tlb.c @@ -46,6 +46,26 @@ void flush_hash_entry(struct mm_struct *mm, pte_t *ptep, unsigned long addr) } } +/* + * Called by ptep_test_and_clear_young() + */ +void flush_hash_one_pte(pte_t *ptep) +{ + struct page *ptepage; + struct mm_struct *mm; + unsigned long ptephys; + unsigned long addr; + + if (Hash == 0) + return; + + ptepage = virt_to_page(ptep); + mm = (struct mm_struct *) ptepage->mapping; + ptephys = __pa(ptep) & PAGE_MASK; + addr = ptepage->index + (((unsigned long)ptep & ~PAGE_MASK) << 9); + flush_hash_pages(mm->context, addr, ptephys, 1); +} + /* * Called at the end of a mmu_gather operation to make sure the * TLB flush is completely done. diff --git a/include/asm-ppc/pgtable.h b/include/asm-ppc/pgtable.h index 0cf6aab40dfb..8ac2de22eadd 100644 --- a/include/asm-ppc/pgtable.h +++ b/include/asm-ppc/pgtable.h @@ -511,9 +511,21 @@ static inline void set_pte(pte_t *ptep, pte_t pte) #endif } +extern void flush_hash_one_pte(pte_t *ptep); + +/* + * 2.6 calles this without flushing the TLB entry, this is wrong + * for our hash-based implementation, we fix that up here + */ static inline int ptep_test_and_clear_young(pte_t *ptep) { - return (pte_update(ptep, _PAGE_ACCESSED, 0) & _PAGE_ACCESSED) != 0; + unsigned long old; + old = (pte_update(ptep, _PAGE_ACCESSED, 0) & _PAGE_ACCESSED); +#if _PAGE_HASHPTE != 0 + if (old & _PAGE_HASHPTE) + flush_hash_one_pte(ptep); +#endif + return old != 0; } static inline int ptep_test_and_clear_dirty(pte_t *ptep) -- cgit v1.2.3 From 76869d088bd89ef03af0caaf3a30d4469b631354 Mon Sep 17 00:00:00 2001 From: Benjamin Herrenschmidt Date: Fri, 6 Feb 2004 14:14:19 +1100 Subject: ppc32: Update register definitions for Apple chipsets --- include/asm-ppc/keylargo.h | 45 +++++++++++++++++++++++++++++++++++++++------ include/asm-ppc/uninorth.h | 36 +++++++++++++++++++++++++++++++++++- 2 files changed, 74 insertions(+), 7 deletions(-) (limited to 'include') diff --git a/include/asm-ppc/keylargo.h b/include/asm-ppc/keylargo.h index 383b00df2c6b..457c75a4622f 100644 --- a/include/asm-ppc/keylargo.h +++ b/include/asm-ppc/keylargo.h @@ -18,6 +18,13 @@ #define KEYLARGO_FCR4 0x48 #define KEYLARGO_FCR5 0x4c /* Pangea only */ +/* K2 aditional FCRs */ +#define K2_FCR6 0x34 +#define K2_FCR7 0x30 +#define K2_FCR8 0x2c +#define K2_FCR9 0x28 +#define K2_FCR10 0x24 + /* GPIO registers */ #define KEYLARGO_GPIO_LEVELS0 0x50 #define KEYLARGO_GPIO_LEVELS1 0x54 @@ -30,6 +37,10 @@ #define KEYLARGO_GPIO_OUTOUT_DATA 0x01 #define KEYLARGO_GPIO_INPUT_DATA 0x02 +/* K2 does only extint GPIOs and does 51 of them */ +#define K2_GPIO_EXTINT_0 0x58 +#define K2_GPIO_EXTINT_CNT 51 + /* Specific GPIO regs */ #define KL_GPIO_MODEM_RESET (KEYLARGO_GPIO_0+0x03) @@ -67,7 +78,8 @@ #define KL_GPIO_AIRPORT_4 (KEYLARGO_GPIO_0+0x0f) /* - * Bits in feature control register + * Bits in feature control register. Those bits different for K2 are + * listed separately */ #define KL_MBCR_MB0_PCI_ENABLE 0x00000800 /* exist ? */ #define KL_MBCR_MB0_IDE_ENABLE 0x00001000 @@ -202,9 +214,30 @@ #define KL4_PORT_DISCONNECT_STAT(p) (0x00000010 << ((p)<<3)) /* Pangea and Intrepid only */ -#define KL5_VIA_USE_CLK31 0x000000001 /* Pangea Only */ -#define KL5_SCC_USE_CLK31 0x000000002 /* Pangea Only */ -#define KL5_PWM_CLK32_EN 0x000000004 -#define KL5_CLK3_68_EN 0x000000010 -#define KL5_CLK32_EN 0x000000020 +#define KL5_VIA_USE_CLK31 0000000001 /* Pangea Only */ +#define KL5_SCC_USE_CLK31 0x00000002 /* Pangea Only */ +#define KL5_PWM_CLK32_EN 0x00000004 +#define KL5_CLK3_68_EN 0x00000010 +#define KL5_CLK32_EN 0x00000020 + + +/* K2 definitions */ +#define K2_FCR0_USB0_SWRESET 0x00200000 +#define K2_FCR0_USB1_SWRESET 0x02000000 +#define K2_FCR0_RING_PME_DISABLE 0x08000000 + +#define K2_FCR1_PCI1_BUS_RESET_N 0x00000010 +#define K2_FCR1_PCI1_SLEEP_RESET_EN 0x00000020 +#define K2_FCR1_PCI1_CLK_ENABLE 0x00004000 +#define K2_FCR1_FW_CLK_ENABLE 0x00008000 +#define K2_FCR1_FW_RESET_N 0x00010000 +#define K2_FCR1_GMAC_CLK_ENABLE 0x00400000 +#define K2_FCR1_GMAC_POWER_DOWN 0x00800000 +#define K2_FCR1_GMAC_RESET_N 0x01000000 +#define K2_FCR1_SATA_CLK_ENABLE 0x02000000 +#define K2_FCR1_SATA_POWER_DOWN 0x04000000 +#define K2_FCR1_SATA_RESET_N 0x08000000 +#define K2_FCR1_UATA_CLK_ENABLE 0x10000000 +#define K2_FCR1_UATA_RESET_N 0x40000000 +#define K2_FCR1_UATA_CHOOSE_CLK66 0x80000000 diff --git a/include/asm-ppc/uninorth.h b/include/asm-ppc/uninorth.h index f203be83ec4f..eeff122f365b 100644 --- a/include/asm-ppc/uninorth.h +++ b/include/asm-ppc/uninorth.h @@ -1,6 +1,8 @@ /* * uninorth.h: definitions for using the "UniNorth" host bridge chip * from Apple. This chip is used on "Core99" machines + * This also includes U2 used on more recent MacRISC2/3 + * machines and U3 (G5) * */ #ifdef __KERNEL__ @@ -8,23 +10,26 @@ #define __ASM_UNINORTH_H__ /* - * Uni-N config space reg. definitions + * Uni-N and U3 config space reg. definitions * * (Little endian) */ /* Address ranges selection. This one should work with Bandit too */ +/* Not U3 */ #define UNI_N_ADDR_SELECT 0x48 #define UNI_N_ADDR_COARSE_MASK 0xffff0000 /* 256Mb regions at *0000000 */ #define UNI_N_ADDR_FINE_MASK 0x0000ffff /* 16Mb regions at f*000000 */ /* AGP registers */ +/* Not U3 */ #define UNI_N_CFG_GART_BASE 0x8c #define UNI_N_CFG_AGP_BASE 0x90 #define UNI_N_CFG_GART_CTRL 0x94 #define UNI_N_CFG_INTERNAL_STATUS 0x98 /* UNI_N_CFG_GART_CTRL bits definitions */ +/* Not U3 */ #define UNI_N_CFG_GART_INVAL 0x00000001 #define UNI_N_CFG_GART_ENABLE 0x00000100 #define UNI_N_CFG_GART_2xRESET 0x00010000 @@ -90,6 +95,14 @@ /* Version of the UniNorth chip */ #define UNI_N_VERSION 0x0000 /* Known versions: 3,7 and 8 */ +#define UNI_N_VERSION_107 0x0003 /* 1.0.7 */ +#define UNI_N_VERSION_10A 0x0007 /* 1.0.10 */ +#define UNI_N_VERSION_150 0x0011 /* 1.5 */ +#define UNI_N_VERSION_200 0x0024 /* 2.0 */ +#define UNI_N_VERSION_PANGEA 0x00C0 /* Integrated U1 + K */ +#define UNI_N_VERSION_INTREPID 0x00D2 /* Integrated U2 + K */ +#define UNI_N_VERSION_300 0x0030 /* 3.0 (U3 on G5) */ + /* This register is used to enable/disable various clocks */ #define UNI_N_CLOCK_CNTL 0x0020 #define UNI_N_CLOCK_CNTL_PCI 0x00000001 /* PCI2 clock control */ @@ -131,5 +144,26 @@ /* Uninorth 1.5 rev. has additional perf. monitor registers at 0xf00-0xf50 */ + +/* + * U3 specific registers + */ + + +/* U3 Toggle */ +#define U3_TOGGLE_REG 0x00e0 +#define U3_PMC_START_STOP 0x0001 +#define U3_MPIC_RESET 0x0002 +#define U3_MPIC_OUTPUT_ENABLE 0x0004 + +/* U3 API PHY Config 1 */ +#define U3_API_PHY_CONFIG_1 0x23030 + +/* U3 HyperTransport registers */ +#define U3_HT_CONFIG_BASE 0x70000 +#define U3_HT_LINK_COMMAND 0x100 +#define U3_HT_LINK_CONFIG 0x110 +#define U3_HT_LINK_FREQ 0x120 + #endif /* __ASM_UNINORTH_H__ */ #endif /* __KERNEL__ */ -- cgit v1.2.3 From fea796e8f4af85f7c384f80985c5656577dc5d84 Mon Sep 17 00:00:00 2001 From: Benjamin Herrenschmidt Date: Fri, 6 Feb 2004 14:20:56 +1100 Subject: ppc32: Rework nvram management move drivers/macintosh/nvram.c to drivers/char/generic_nvram.c, update platform hooks, fix powermac nvram driver for newer machines --- arch/ppc/Kconfig | 4 + arch/ppc/kernel/ppc_ksyms.c | 8 - arch/ppc/kernel/setup.c | 34 ++- arch/ppc/platforms/pmac_nvram.c | 523 ++++++++++++++++++++++++++-------------- drivers/char/Makefile | 4 +- drivers/char/generic_nvram.c | 145 +++++++++++ drivers/macintosh/Makefile | 3 - include/asm-ppc/machdep.h | 1 + include/asm-ppc/nvram.h | 25 +- 9 files changed, 547 insertions(+), 200 deletions(-) create mode 100644 drivers/char/generic_nvram.c (limited to 'include') diff --git a/arch/ppc/Kconfig b/arch/ppc/Kconfig index dda6dc91e1b7..df86732b720c 100644 --- a/arch/ppc/Kconfig +++ b/arch/ppc/Kconfig @@ -30,6 +30,10 @@ config PPC32 bool default y +# All PPCs use generic nvram driver through ppc_md +config GENERIC_NVRAM + bool + default y source "init/Kconfig" diff --git a/arch/ppc/kernel/ppc_ksyms.c b/arch/ppc/kernel/ppc_ksyms.c index 8d0360f8d911..6cf2478be1a4 100644 --- a/arch/ppc/kernel/ppc_ksyms.c +++ b/arch/ppc/kernel/ppc_ksyms.c @@ -283,14 +283,6 @@ EXPORT_SYMBOL(note_scsi_host); #ifdef CONFIG_VT EXPORT_SYMBOL(kd_mksound); #endif -#ifdef CONFIG_NVRAM -EXPORT_SYMBOL(nvram_read_byte); -EXPORT_SYMBOL(nvram_write_byte); -#ifdef CONFIG_PPC_PMAC -EXPORT_SYMBOL(pmac_xpram_read); -EXPORT_SYMBOL(pmac_xpram_write); -#endif -#endif /* CONFIG_NVRAM */ EXPORT_SYMBOL(to_tm); EXPORT_SYMBOL(pm_power_off); diff --git a/arch/ppc/kernel/setup.c b/arch/ppc/kernel/setup.c index 94c00edb40bf..7d1ecb35a619 100644 --- a/arch/ppc/kernel/setup.c +++ b/arch/ppc/kernel/setup.c @@ -35,6 +35,7 @@ #include #include #include +#include #include #if defined CONFIG_KGDB @@ -111,6 +112,9 @@ struct screen_info screen_info = { void machine_restart(char *cmd) { +#ifdef CONFIG_NVRAM + nvram_sync(); +#endif ppc_md.restart(cmd); } @@ -118,6 +122,9 @@ EXPORT_SYMBOL(machine_restart); void machine_power_off(void) { +#ifdef CONFIG_NVRAM + nvram_sync(); +#endif ppc_md.power_off(); } @@ -125,6 +132,9 @@ EXPORT_SYMBOL(machine_power_off); void machine_halt(void) { +#ifdef CONFIG_NVRAM + nvram_sync(); +#endif ppc_md.halt(); } @@ -558,24 +568,30 @@ int __init ppc_setup_l2cr(char *str) __setup("l2cr=", ppc_setup_l2cr); #ifdef CONFIG_NVRAM -/* Generic nvram hooks we now look into ppc_md.nvram_read_val - * on pmac too ;) - * //XX Those 2 could be moved to headers - */ -unsigned char -nvram_read_byte(int addr) + +/* Generic nvram hooks used by drivers/char/gen_nvram.c */ +unsigned char nvram_read_byte(int addr) { if (ppc_md.nvram_read_val) return ppc_md.nvram_read_val(addr); return 0xff; } +EXPORT_SYMBOL(nvram_read_byte); -void -nvram_write_byte(unsigned char val, int addr) +void nvram_write_byte(unsigned char val, int addr) { if (ppc_md.nvram_write_val) - ppc_md.nvram_write_val(val, addr); + ppc_md.nvram_write_val(addr, val); +} +EXPORT_SYMBOL(nvram_write_byte); + +void nvram_sync(void) +{ + if (ppc_md.nvram_sync) + ppc_md.nvram_sync(); } +EXPORT_SYMBOL(nvram_sync); + #endif /* CONFIG_NVRAM */ static struct cpu cpu_devices[NR_CPUS]; diff --git a/arch/ppc/platforms/pmac_nvram.c b/arch/ppc/platforms/pmac_nvram.c index d11d2445b54e..f381f3f745f9 100644 --- a/arch/ppc/platforms/pmac_nvram.c +++ b/arch/ppc/platforms/pmac_nvram.c @@ -8,8 +8,7 @@ * as published by the Free Software Foundation; either version * 2 of the License, or (at your option) any later version. * - * Todo: - cleanup some coding horrors in the flash code - * - add support for the OF persistent properties + * Todo: - add support for the OF persistent properties */ #include #include @@ -21,29 +20,40 @@ #include #include #include +#include +#include +#include +#include +#include #include #include #include #include #include #include -#include -#include -#undef DEBUG +#define DEBUG + +#ifdef DEBUG +#define DBG(x...) printk(x) +#else +#define DBG(x...) +#endif #define NVRAM_SIZE 0x2000 /* 8kB of non-volatile RAM */ #define CORE99_SIGNATURE 0x5a #define CORE99_ADLER_START 0x14 -/* Core99 nvram is a flash */ -#define CORE99_FLASH_STATUS_DONE 0x80 -#define CORE99_FLASH_STATUS_ERR 0x38 -#define CORE99_FLASH_CMD_ERASE_CONFIRM 0xd0 -#define CORE99_FLASH_CMD_ERASE_SETUP 0x20 -#define CORE99_FLASH_CMD_RESET 0xff -#define CORE99_FLASH_CMD_WRITE_SETUP 0x40 +/* On Core99, nvram is either a sharp, a micron or an AMD flash */ +#define SM_FLASH_STATUS_DONE 0x80 +#define SM_FLASH_STATUS_ERR 0x38 +#define SM_FLASH_CMD_ERASE_CONFIRM 0xd0 +#define SM_FLASH_CMD_ERASE_SETUP 0x20 +#define SM_FLASH_CMD_RESET 0xff +#define SM_FLASH_CMD_WRITE_SETUP 0x40 +#define SM_FLASH_CMD_CLEAR_STATUS 0x50 +#define SM_FLASH_CMD_READ_STATUS 0x70 /* CHRP NVRAM header */ struct chrp_header { @@ -70,21 +80,110 @@ static volatile unsigned char *nvram_data; static int nvram_mult, is_core_99; static int core99_bank = 0; static int nvram_partitions[3]; - -/* FIXME: kmalloc fails to allocate the image now that I had to move it - * before time_init(). For now, I allocate a static buffer here - * but it's a waste of space on all but core99 machines - */ -#if 0 -static char* nvram_image; -#else -static char nvram_image[NVRAM_SIZE] __pmacdata; -#endif +static spinlock_t nv_lock = SPIN_LOCK_UNLOCKED; extern int pmac_newworld; +extern int system_running; + +static int (*core99_write_bank)(int bank, u8* datas); +static int (*core99_erase_bank)(int bank); + +static char *nvram_image __pmacdata; + + +static unsigned char __pmac core99_nvram_read_byte(int addr) +{ + if (nvram_image == NULL) + return 0xff; + return nvram_image[addr]; +} + +static void __pmac core99_nvram_write_byte(int addr, unsigned char val) +{ + if (nvram_image == NULL) + return; + nvram_image[addr] = val; +} + + +static unsigned char __openfirmware direct_nvram_read_byte(int addr) +{ + return in_8(&nvram_data[(addr & (NVRAM_SIZE - 1)) * nvram_mult]); +} + +static void __openfirmware direct_nvram_write_byte(int addr, unsigned char val) +{ + out_8(&nvram_data[(addr & (NVRAM_SIZE - 1)) * nvram_mult], val); +} + + +static unsigned char __pmac indirect_nvram_read_byte(int addr) +{ + unsigned char val; + unsigned long flags; + + spin_lock_irqsave(&nv_lock, flags); + out_8(nvram_addr, addr >> 5); + val = in_8(&nvram_data[(addr & 0x1f) << 4]); + spin_unlock_irqrestore(&nv_lock, flags); + + return val; +} + +static void __pmac indirect_nvram_write_byte(int addr, unsigned char val) +{ + unsigned long flags; + + spin_lock_irqsave(&nv_lock, flags); + out_8(nvram_addr, addr >> 5); + out_8(&nvram_data[(addr & 0x1f) << 4], val); + spin_unlock_irqrestore(&nv_lock, flags); +} + + +#ifdef CONFIG_ADB_PMU + +static void __pmac pmu_nvram_complete(struct adb_request *req) +{ + if (req->arg) + complete((struct completion *)req->arg); +} -static u8 __pmac -chrp_checksum(struct chrp_header* hdr) +static unsigned char __pmac pmu_nvram_read_byte(int addr) +{ + struct adb_request req; + DECLARE_COMPLETION(req_complete); + + req.arg = system_running ? &req_complete : NULL; + if (pmu_request(&req, pmu_nvram_complete, 3, PMU_READ_NVRAM, + (addr >> 8) & 0xff, addr & 0xff)) + return 0xff; + if (system_running) + wait_for_completion(&req_complete); + while (!req.complete) + pmu_poll(); + return req.reply[0]; +} + +static void __pmac pmu_nvram_write_byte(int addr, unsigned char val) +{ + struct adb_request req; + DECLARE_COMPLETION(req_complete); + + req.arg = system_running ? &req_complete : NULL; + if (pmu_request(&req, pmu_nvram_complete, 4, PMU_WRITE_NVRAM, + (addr >> 8) & 0xff, addr & 0xff, val)) + return; + if (system_running) + wait_for_completion(&req_complete); + while (!req.complete) + pmu_poll(); +} + +#endif /* CONFIG_ADB_PMU */ + + +static u8 __pmac chrp_checksum(struct chrp_header* hdr) { u8 *ptr; u16 sum = hdr->signature; @@ -95,8 +194,7 @@ chrp_checksum(struct chrp_header* hdr) return sum; } -static u32 __pmac -core99_calc_adler(u8 *buffer) +static u32 __pmac core99_calc_adler(u8 *buffer) { int cnt; u32 low, high; @@ -118,86 +216,186 @@ core99_calc_adler(u8 *buffer) return (high << 16) | low; } -static u32 __pmac -core99_check(u8* datas) +static u32 __pmac core99_check(u8* datas) { struct core99_header* hdr99 = (struct core99_header*)datas; if (hdr99->hdr.signature != CORE99_SIGNATURE) { -#ifdef DEBUG - printk("Invalid signature\n"); -#endif + DBG("Invalid signature\n"); return 0; } if (hdr99->hdr.cksum != chrp_checksum(&hdr99->hdr)) { -#ifdef DEBUG - printk("Invalid checksum\n"); -#endif + DBG("Invalid checksum\n"); return 0; } if (hdr99->adler != core99_calc_adler(datas)) { -#ifdef DEBUG - printk("Invalid adler\n"); -#endif + DBG("Invalid adler\n"); return 0; } return hdr99->generation; } -static int __pmac -core99_erase_bank(int bank) +static int __pmac sm_erase_bank(int bank) { int stat, i; + unsigned long timeout; u8* base = (u8 *)nvram_data + core99_bank*NVRAM_SIZE; - out_8(base, CORE99_FLASH_CMD_ERASE_SETUP); - out_8(base, CORE99_FLASH_CMD_ERASE_CONFIRM); - do { stat = in_8(base); } - while(!(stat & CORE99_FLASH_STATUS_DONE)); - out_8(base, CORE99_FLASH_CMD_RESET); - if (stat & CORE99_FLASH_STATUS_ERR) { - printk("nvram: flash error 0x%02x on erase !\n", stat); - return -ENXIO; - } + DBG("nvram: Sharp/Micron Erasing bank %d...\n", bank); + + out_8(base, SM_FLASH_CMD_ERASE_SETUP); + out_8(base, SM_FLASH_CMD_ERASE_CONFIRM); + timeout = 0; + do { + if (++timeout > 1000000) { + printk(KERN_ERR "nvram: Sharp/Miron flash erase timeout !\n"); + break; + } + out_8(base, SM_FLASH_CMD_READ_STATUS); + stat = in_8(base); + } while (!(stat & SM_FLASH_STATUS_DONE)); + + out_8(base, SM_FLASH_CMD_CLEAR_STATUS); + out_8(base, SM_FLASH_CMD_RESET); + for (i=0; i 1000000) { + printk(KERN_ERR "nvram: Sharp/Micron flash write timeout !\n"); + break; + } + out_8(base, SM_FLASH_CMD_READ_STATUS); + stat = in_8(base); + } while (!(stat & SM_FLASH_STATUS_DONE)); + if (!(stat & SM_FLASH_STATUS_DONE)) break; } - out_8(base, CORE99_FLASH_CMD_RESET); - if (stat & CORE99_FLASH_STATUS_ERR) { - printk("nvram: flash error 0x%02x on write !\n", stat); - return -ENXIO; + out_8(base, SM_FLASH_CMD_CLEAR_STATUS); + out_8(base, SM_FLASH_CMD_RESET); + for (i=0; i 1000000) { + printk(KERN_ERR "nvram: AMD flash erase timeout !\n"); + break; + } + stat = in_8(base) ^ in_8(base); + } while (stat != 0); + + /* Reset */ + out_8(base, 0xf0); + udelay(1); + + for (i=0; i 1000000) { + printk(KERN_ERR "nvram: AMD flash write timeout !\n"); + break; + } + stat = in_8(base) ^ in_8(base); + } while (stat != 0); + if (stat != 0) + break; } + + /* Reset */ + out_8(base, 0xf0); + udelay(1); + for (i=0; igeneration++; + hdr99->hdr.signature = CORE99_SIGNATURE; + hdr99->hdr.cksum = chrp_checksum(&hdr99->hdr); + hdr99->adler = core99_calc_adler(nvram_image); + core99_bank = core99_bank ? 0 : 1; + if (core99_erase_bank) + if (core99_erase_bank(core99_bank)) { + printk("nvram: Error erasing bank %d\n", core99_bank); + goto bail; + } + if (core99_write_bank) + if (core99_write_bank(core99_bank, nvram_image)) + printk("nvram: Error writing bank %d\n", core99_bank); + bail: + spin_unlock_irqrestore(&nv_lock, flags); + #ifdef DEBUG - printk("nvram: OF partition at 0x%x\n", nvram_partitions[pmac_nvram_OF]); - printk("nvram: XP partition at 0x%x\n", nvram_partitions[pmac_nvram_XPRAM]); - printk("nvram: NR partition at 0x%x\n", nvram_partitions[pmac_nvram_NR]); + mdelay(2000); #endif } -void __init -pmac_nvram_init(void) +void __init pmac_nvram_init(void) { struct device_node *dp; @@ -256,38 +488,65 @@ pmac_nvram_init(void) printk(KERN_ERR "nvram: no address\n"); return; } -#if 0 - nvram_image = kmalloc(NVRAM_SIZE, GFP_KERNEL); - if (!nvram_image) { - printk(KERN_ERR "nvram: can't allocate image\n"); + nvram_image = alloc_bootmem(NVRAM_SIZE); + if (nvram_image == NULL) { + printk(KERN_ERR "nvram: can't allocate ram image\n"); return; } -#endif nvram_data = ioremap(dp->addrs[0].address, NVRAM_SIZE*2); -#ifdef DEBUG - printk("nvram: Checking bank 0...\n"); -#endif + nvram_naddrs = 1; /* Make sure we get the correct case */ + + DBG("nvram: Checking bank 0...\n"); + gen_bank0 = core99_check((u8 *)nvram_data); gen_bank1 = core99_check((u8 *)nvram_data + NVRAM_SIZE); core99_bank = (gen_bank0 < gen_bank1) ? 1 : 0; -#ifdef DEBUG - printk("nvram: gen0=%d, gen1=%d\n", gen_bank0, gen_bank1); - printk("nvram: Active bank is: %d\n", core99_bank); -#endif + + DBG("nvram: gen0=%d, gen1=%d\n", gen_bank0, gen_bank1); + DBG("nvram: Active bank is: %d\n", core99_bank); + for (i=0; iaddrs[0].address + isa_mem_base, dp->addrs[0].size); nvram_mult = 1; + ppc_md.nvram_read_val = direct_nvram_read_byte; + ppc_md.nvram_write_val = direct_nvram_write_byte; } else if (nvram_naddrs == 1) { nvram_data = ioremap(dp->addrs[0].address, dp->addrs[0].size); nvram_mult = (dp->addrs[0].size + NVRAM_SIZE - 1) / NVRAM_SIZE; + ppc_md.nvram_read_val = direct_nvram_read_byte; + ppc_md.nvram_write_val = direct_nvram_write_byte; } else if (nvram_naddrs == 2) { nvram_addr = ioremap(dp->addrs[0].address, dp->addrs[0].size); nvram_data = ioremap(dp->addrs[1].address, dp->addrs[1].size); + ppc_md.nvram_read_val = indirect_nvram_read_byte; + ppc_md.nvram_write_val = indirect_nvram_write_byte; } else if (nvram_naddrs == 0 && sys_ctrler == SYS_CTRLER_PMU) { +#ifdef CONFIG_ADB_PMU nvram_naddrs = -1; + ppc_md.nvram_read_val = pmu_nvram_read_byte; + ppc_md.nvram_write_val = pmu_nvram_write_byte; +#endif /* CONFIG_ADB_PMU */ } else { printk(KERN_ERR "Don't know how to access NVRAM with %d addresses\n", nvram_naddrs); @@ -295,117 +554,31 @@ pmac_nvram_init(void) lookup_partitions(); } -void __pmac -pmac_nvram_update(void) -{ - struct core99_header* hdr99; - - if (!is_core_99 || !nvram_data || !nvram_image) - return; - if (!memcmp(nvram_image, (u8*)nvram_data + core99_bank*NVRAM_SIZE, - NVRAM_SIZE)) - return; -#ifdef DEBUG - printk("Updating nvram...\n"); -#endif - hdr99 = (struct core99_header*)nvram_image; - hdr99->generation++; - hdr99->hdr.signature = CORE99_SIGNATURE; - hdr99->hdr.cksum = chrp_checksum(&hdr99->hdr); - hdr99->adler = core99_calc_adler(nvram_image); - core99_bank = core99_bank ? 0 : 1; - if (core99_erase_bank(core99_bank)) { - printk("nvram: Error erasing bank %d\n", core99_bank); - return; - } - if (core99_write_bank(core99_bank, nvram_image)) - printk("nvram: Error writing bank %d\n", core99_bank); -} - -unsigned char __pmac -pmac_nvram_read_byte(int addr) -{ - switch (nvram_naddrs) { -#ifdef CONFIG_ADB_PMU - case -1: { - struct adb_request req; - - if (pmu_request(&req, NULL, 3, PMU_READ_NVRAM, - (addr >> 8) & 0xff, addr & 0xff)) - break; - while (!req.complete) - pmu_poll(); - return req.reply[0]; - } -#endif - case 1: - if (is_core_99) - return nvram_image[addr]; - return nvram_data[(addr & (NVRAM_SIZE - 1)) * nvram_mult]; - case 2: - *nvram_addr = addr >> 5; - eieio(); - return nvram_data[(addr & 0x1f) << 4]; - } - return 0; -} - -void __pmac -pmac_nvram_write_byte(int addr, unsigned char val) -{ - switch (nvram_naddrs) { -#ifdef CONFIG_ADB_PMU - case -1: { - struct adb_request req; - - if (pmu_request(&req, NULL, 4, PMU_WRITE_NVRAM, - (addr >> 8) & 0xff, addr & 0xff, val)) - break; - while (!req.complete) - pmu_poll(); - break; - } -#endif - case 1: - if (is_core_99) { - nvram_image[addr] = val; - break; - } - nvram_data[(addr & (NVRAM_SIZE - 1)) * nvram_mult] = val; - break; - case 2: - *nvram_addr = addr >> 5; - eieio(); - nvram_data[(addr & 0x1f) << 4] = val; - break; - } - eieio(); -} - -int __pmac -pmac_get_partition(int partition) +int __pmac pmac_get_partition(int partition) { return nvram_partitions[partition]; } -u8 __pmac -pmac_xpram_read(int xpaddr) +u8 __pmac pmac_xpram_read(int xpaddr) { int offset = nvram_partitions[pmac_nvram_XPRAM]; if (offset < 0) - return 0; + return 0xff; - return pmac_nvram_read_byte(xpaddr + offset); + return ppc_md.nvram_read_val(xpaddr + offset); } -void __pmac -pmac_xpram_write(int xpaddr, u8 data) +void __pmac pmac_xpram_write(int xpaddr, u8 data) { int offset = nvram_partitions[pmac_nvram_XPRAM]; if (offset < 0) return; - pmac_nvram_write_byte(data, xpaddr + offset); + ppc_md.nvram_write_val(xpaddr + offset, data); } + +EXPORT_SYMBOL(pmac_get_partition); +EXPORT_SYMBOL(pmac_xpram_read); +EXPORT_SYMBOL(pmac_xpram_write); diff --git a/drivers/char/Makefile b/drivers/char/Makefile index ca8aa47ef43e..302afc35f077 100644 --- a/drivers/char/Makefile +++ b/drivers/char/Makefile @@ -57,7 +57,9 @@ obj-$(CONFIG_SONYPI) += sonypi.o obj-$(CONFIG_RTC) += rtc.o obj-$(CONFIG_GEN_RTC) += genrtc.o obj-$(CONFIG_EFI_RTC) += efirtc.o -ifeq ($(CONFIG_PPC),) +ifeq ($(CONFIG_GENERIC_NVRAM),y) + obj-$(CONFIG_NVRAM) += generic_nvram.o +else obj-$(CONFIG_NVRAM) += nvram.o endif obj-$(CONFIG_TOSHIBA) += toshiba.o diff --git a/drivers/char/generic_nvram.c b/drivers/char/generic_nvram.c new file mode 100644 index 000000000000..b4d657d02d44 --- /dev/null +++ b/drivers/char/generic_nvram.c @@ -0,0 +1,145 @@ +/* + * Generic /dev/nvram driver for architectures providing some + * "generic" hooks, that is : + * + * nvram_read_byte, nvram_write_byte, nvram_sync + * + * Note that an additional hook is supported for PowerMac only + * for getting the nvram "partition" informations + * + */ + +#define NVRAM_VERSION "1.1" + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define NVRAM_SIZE 8192 + +static loff_t nvram_llseek(struct file *file, loff_t offset, int origin) +{ + lock_kernel(); + switch (origin) { + case 1: + offset += file->f_pos; + break; + case 2: + offset += NVRAM_SIZE; + break; + } + if (offset < 0) { + unlock_kernel(); + return -EINVAL; + } + file->f_pos = offset; + unlock_kernel(); + return file->f_pos; +} + +static ssize_t read_nvram(struct file *file, char __user *buf, + size_t count, loff_t *ppos) +{ + unsigned int i; + char __user *p = buf; + + if (verify_area(VERIFY_WRITE, buf, count)) + return -EFAULT; + if (*ppos >= NVRAM_SIZE) + return 0; + for (i = *ppos; count > 0 && i < NVRAM_SIZE; ++i, ++p, --count) + if (__put_user(nvram_read_byte(i), p)) + return -EFAULT; + *ppos = i; + return p - buf; +} + +static ssize_t write_nvram(struct file *file, const char __user *buf, + size_t count, loff_t *ppos) +{ + unsigned int i; + const char __user *p = buf; + char c; + + if (verify_area(VERIFY_READ, buf, count)) + return -EFAULT; + if (*ppos >= NVRAM_SIZE) + return 0; + for (i = *ppos; count > 0 && i < NVRAM_SIZE; ++i, ++p, --count) { + if (__get_user(c, p)) + return -EFAULT; + nvram_write_byte(c, i); + } + *ppos = i; + return p - buf; +} + +static int nvram_ioctl(struct inode *inode, struct file *file, + unsigned int cmd, unsigned long arg) +{ + switch(cmd) { +#ifdef CONFIG_PPC_PMAC + case OBSOLETE_PMAC_NVRAM_GET_OFFSET: + printk(KERN_WARNING "nvram: Using obsolete PMAC_NVRAM_GET_OFFSET ioctl\n"); + case IOC_NVRAM_GET_OFFSET: { + int part, offset; + + if (_machine != _MACH_Pmac) + return -EINVAL; + if (copy_from_user(&part, (void __user*)arg, sizeof(part)) != 0) + return -EFAULT; + if (part < pmac_nvram_OF || part > pmac_nvram_NR) + return -EINVAL; + offset = pmac_get_partition(part); + if (copy_to_user((void __user*)arg, &offset, sizeof(offset)) != 0) + return -EFAULT; + break; + } +#endif /* CONFIG_PPC_PMAC */ + case IOC_NVRAM_SYNC: + nvram_sync(); + break; + default: + return -EINVAL; + } + + return 0; +} + +struct file_operations nvram_fops = { + .owner = THIS_MODULE, + .llseek = nvram_llseek, + .read = read_nvram, + .write = write_nvram, + .ioctl = nvram_ioctl, +}; + +static struct miscdevice nvram_dev = { + NVRAM_MINOR, + "nvram", + &nvram_fops +}; + +int __init nvram_init(void) +{ + printk(KERN_INFO "Macintosh non-volatile memory driver v%s\n", + NVRAM_VERSION); + return misc_register(&nvram_dev); +} + +void __exit nvram_cleanup(void) +{ + misc_deregister( &nvram_dev ); +} + +module_init(nvram_init); +module_exit(nvram_cleanup); +MODULE_LICENSE("GPL"); diff --git a/drivers/macintosh/Makefile b/drivers/macintosh/Makefile index da5aadbd07da..d8755852471e 100644 --- a/drivers/macintosh/Makefile +++ b/drivers/macintosh/Makefile @@ -8,9 +8,6 @@ obj-$(CONFIG_PPC_PMAC) += macio_asic.o obj-$(CONFIG_PMAC_PBOOK) += mediabay.o obj-$(CONFIG_MAC_SERIAL) += macserial.o -ifneq ($(CONFIG_MAC),y) - obj-$(CONFIG_NVRAM) += nvram.o -endif obj-$(CONFIG_MAC_EMUMOUSEBTN) += mac_hid.o obj-$(CONFIG_INPUT_ADBHID) += adbhid.o obj-$(CONFIG_ANSLCD) += ans-lcd.o diff --git a/include/asm-ppc/machdep.h b/include/asm-ppc/machdep.h index 54cb5a93d508..c241973ff78e 100644 --- a/include/asm-ppc/machdep.h +++ b/include/asm-ppc/machdep.h @@ -57,6 +57,7 @@ struct machdep_calls { unsigned char (*nvram_read_val)(int addr); void (*nvram_write_val)(int addr, unsigned char val); + void (*nvram_sync)(void); /* * optional PCI "hooks" diff --git a/include/asm-ppc/nvram.h b/include/asm-ppc/nvram.h index d65bb55baf9d..31ef16e3fc4f 100644 --- a/include/asm-ppc/nvram.h +++ b/include/asm-ppc/nvram.h @@ -34,23 +34,40 @@ enum { /* Return partition offset in nvram */ extern int pmac_get_partition(int partition); -/* Direct access to XPRAM */ +/* Direct access to XPRAM on PowerMacs */ extern u8 pmac_xpram_read(int xpaddr); extern void pmac_xpram_write(int xpaddr, u8 data); +/* Synchronize NVRAM */ +extern void nvram_sync(void); + +/* Normal access to NVRAM */ +extern unsigned char nvram_read_byte(int i); +extern void nvram_write_byte(unsigned char c, int i); + /* Some offsets in XPRAM */ #define PMAC_XPRAM_MACHINE_LOC 0xe4 #define PMAC_XPRAM_SOUND_VOLUME 0x08 -/* Machine location structure in XPRAM */ +/* Machine location structure in PowerMac XPRAM */ struct pmac_machine_location { unsigned int latitude; /* 2+30 bit Fractional number */ unsigned int longitude; /* 2+30 bit Fractional number */ unsigned int delta; /* mix of GMT delta and DLS */ }; -/* /dev/nvram ioctls */ -#define PMAC_NVRAM_GET_OFFSET _IOWR('p', 0x40, int) /* Get NVRAM partition offset */ +/* + * /dev/nvram ioctls + * + * Note that PMAC_NVRAM_GET_OFFSET is still supported, but is + * definitely obsolete. Do not use it if you can avoid it + */ + +#define OBSOLETE_PMAC_NVRAM_GET_OFFSET \ + _IOWR('p', 0x40, int) + +#define IOC_NVRAM_GET_OFFSET _IOWR('p', 0x42, int) /* Get NVRAM partition offset */ +#define IOC_NVRAM_SYNC _IO('p', 0x43) /* Sync NVRAM image */ #endif #endif /* __KERNEL__ */ -- cgit v1.2.3 From e37fe183048631cec55e010bdc9372ab046c4dad Mon Sep 17 00:00:00 2001 From: Benjamin Herrenschmidt Date: Fri, 6 Feb 2004 14:22:25 +1100 Subject: ppc32: Update PowerMac motherboard support add support for newer laptops and G5 desktops --- arch/ppc/platforms/pmac_feature.c | 586 ++++++++++++++++++++++++++++++-------- include/asm-ppc/machdep.h | 2 +- include/asm-ppc/pmac_feature.h | 17 +- 3 files changed, 478 insertions(+), 127 deletions(-) (limited to 'include') diff --git a/arch/ppc/platforms/pmac_feature.c b/arch/ppc/platforms/pmac_feature.c index e64779c8cc48..693d8648ffc8 100644 --- a/arch/ppc/platforms/pmac_feature.c +++ b/arch/ppc/platforms/pmac_feature.c @@ -50,9 +50,13 @@ #define DBG(fmt,...) #endif -/* Exported from arch/ppc/kernel/idle.c */ +#ifdef CONFIG_6xx extern int powersave_lowspeed; +#endif + extern int powersave_nap; +extern struct pci_dev *k2_skiplist[2]; + /* * We use a single global lock to protect accesses. Each driver has @@ -95,7 +99,8 @@ static const char* macio_names[] __pmacdata = "Paddington", "Keylargo", "Pangea", - "Intrepid" + "Intrepid", + "K2" }; @@ -113,14 +118,15 @@ static const char* macio_names[] __pmacdata = static struct device_node* uninorth_node __pmacdata; static u32* uninorth_base __pmacdata; static u32 uninorth_rev __pmacdata; - +static int uninorth_u3 __pmacdata; +static void *u3_ht; /* * For each motherboard family, we have a table of functions pointers * that handle the various features. */ -typedef int (*feature_call)(struct device_node* node, int param, int value); +typedef long (*feature_call)(struct device_node* node, long param, long value); struct feature_table_entry { unsigned int selector; @@ -161,8 +167,10 @@ simple_feature_tweak(struct device_node* node, int type, int reg, u32 mask, int return 0; } -static int __pmac -ohare_htw_scc_enable(struct device_node* node, int param, int value) +#ifndef CONFIG_POWER4 + +static long __pmac +ohare_htw_scc_enable(struct device_node* node, long param, long value) { struct macio_chip* macio; unsigned long chan_mask; @@ -254,22 +262,22 @@ ohare_htw_scc_enable(struct device_node* node, int param, int value) return 0; } -static int __pmac -ohare_floppy_enable(struct device_node* node, int param, int value) +static long __pmac +ohare_floppy_enable(struct device_node* node, long param, long value) { return simple_feature_tweak(node, macio_ohare, OHARE_FCR, OH_FLOPPY_ENABLE, value); } -static int __pmac -ohare_mesh_enable(struct device_node* node, int param, int value) +static long __pmac +ohare_mesh_enable(struct device_node* node, long param, long value) { return simple_feature_tweak(node, macio_ohare, OHARE_FCR, OH_MESH_ENABLE, value); } -static int __pmac -ohare_ide_enable(struct device_node* node, int param, int value) +static long __pmac +ohare_ide_enable(struct device_node* node, long param, long value) { switch(param) { case 0: @@ -289,8 +297,8 @@ ohare_ide_enable(struct device_node* node, int param, int value) } } -static int __pmac -ohare_ide_reset(struct device_node* node, int param, int value) +static long __pmac +ohare_ide_reset(struct device_node* node, long param, long value) { switch(param) { case 0: @@ -304,8 +312,8 @@ ohare_ide_reset(struct device_node* node, int param, int value) } } -static int __pmac -ohare_sleep_state(struct device_node* node, int param, int value) +static long __pmac +ohare_sleep_state(struct device_node* node, long param, long value) { struct macio_chip* macio = &macio_chips[0]; @@ -320,8 +328,8 @@ ohare_sleep_state(struct device_node* node, int param, int value) return 0; } -static int __pmac -heathrow_modem_enable(struct device_node* node, int param, int value) +static long __pmac +heathrow_modem_enable(struct device_node* node, long param, long value) { struct macio_chip* macio; u8 gpio; @@ -364,8 +372,8 @@ heathrow_modem_enable(struct device_node* node, int param, int value) return 0; } -static int __pmac -heathrow_floppy_enable(struct device_node* node, int param, int value) +static long __pmac +heathrow_floppy_enable(struct device_node* node, long param, long value) { return simple_feature_tweak(node, macio_unknown, HEATHROW_FCR, @@ -373,8 +381,8 @@ heathrow_floppy_enable(struct device_node* node, int param, int value) value); } -static int __pmac -heathrow_mesh_enable(struct device_node* node, int param, int value) +static long __pmac +heathrow_mesh_enable(struct device_node* node, long param, long value) { struct macio_chip* macio; unsigned long flags; @@ -390,22 +398,11 @@ heathrow_mesh_enable(struct device_node* node, int param, int value) MACIO_BIC(HEATHROW_FCR, HRW_MESH_ENABLE); (void)MACIO_IN32(HEATHROW_FCR); udelay(10); - /* Set/Clear termination power (todo: test ! the bit value - * used by Darwin doesn't seem to match what we used so - * far. If you experience problems, turn #if 1 into #if 0 - * and tell me about it --BenH. - */ -#if 1 - if (value) - MACIO_BIC(HEATHROW_MBCR, 0x00000004); - else - MACIO_BIS(HEATHROW_MBCR, 0x00000004); -#else + /* Set/Clear termination power */ if (value) - MACIO_BIC(HEATHROW_MBCR, 0x00040000); + MACIO_BIC(HEATHROW_MBCR, 0x04000000); else - MACIO_BIS(HEATHROW_MBCR, 0x00040000); -#endif + MACIO_BIS(HEATHROW_MBCR, 0x04000000); (void)MACIO_IN32(HEATHROW_MBCR); udelay(10); UNLOCK(flags); @@ -413,8 +410,8 @@ heathrow_mesh_enable(struct device_node* node, int param, int value) return 0; } -static int __pmac -heathrow_ide_enable(struct device_node* node, int param, int value) +static long __pmac +heathrow_ide_enable(struct device_node* node, long param, long value) { switch(param) { case 0: @@ -428,8 +425,8 @@ heathrow_ide_enable(struct device_node* node, int param, int value) } } -static int __pmac -heathrow_ide_reset(struct device_node* node, int param, int value) +static long __pmac +heathrow_ide_reset(struct device_node* node, long param, long value) { switch(param) { case 0: @@ -443,8 +440,8 @@ heathrow_ide_reset(struct device_node* node, int param, int value) } } -static int __pmac -heathrow_bmac_enable(struct device_node* node, int param, int value) +static long __pmac +heathrow_bmac_enable(struct device_node* node, long param, long value) { struct macio_chip* macio; unsigned long flags; @@ -472,8 +469,8 @@ heathrow_bmac_enable(struct device_node* node, int param, int value) return 0; } -static int __pmac -heathrow_sound_enable(struct device_node* node, int param, int value) +static long __pmac +heathrow_sound_enable(struct device_node* node, long param, long value) { struct macio_chip* macio; unsigned long flags; @@ -608,8 +605,8 @@ heathrow_wakeup(struct macio_chip* macio, int secondary) } } -static int __pmac -heathrow_sleep_state(struct device_node* node, int param, int value) +static long __pmac +heathrow_sleep_state(struct device_node* node, long param, long value) { if ((pmac_mb.board_flags & PMAC_MB_CAN_SLEEP) == 0) return -EPERM; @@ -625,8 +622,8 @@ heathrow_sleep_state(struct device_node* node, int param, int value) return 0; } -static int __pmac -core99_scc_enable(struct device_node* node, int param, int value) +static long __pmac +core99_scc_enable(struct device_node* node, long param, long value) { struct macio_chip* macio; unsigned long flags; @@ -726,8 +723,8 @@ core99_scc_enable(struct device_node* node, int param, int value) return 0; } -static int __pmac -core99_modem_enable(struct device_node* node, int param, int value) +static long __pmac +core99_modem_enable(struct device_node* node, long param, long value) { struct macio_chip* macio; u8 gpio; @@ -778,8 +775,8 @@ core99_modem_enable(struct device_node* node, int param, int value) return 0; } -static int __pmac -pangea_modem_enable(struct device_node* node, int param, int value) +static long __pmac +pangea_modem_enable(struct device_node* node, long param, long value) { struct macio_chip* macio; u8 gpio; @@ -833,8 +830,8 @@ pangea_modem_enable(struct device_node* node, int param, int value) return 0; } -static int __pmac -core99_ata100_enable(struct device_node* node, int value) +static long __pmac +core99_ata100_enable(struct device_node* node, long value) { unsigned long flags; struct pci_dev *pdev = NULL; @@ -863,8 +860,8 @@ core99_ata100_enable(struct device_node* node, int value) return 0; } -static int __pmac -core99_ide_enable(struct device_node* node, int param, int value) +static long __pmac +core99_ide_enable(struct device_node* node, long param, long value) { /* Bus ID 0 to 2 are KeyLargo based IDE, busID 3 is U2 * based ata-100 @@ -886,8 +883,8 @@ core99_ide_enable(struct device_node* node, int param, int value) } } -static int __pmac -core99_ide_reset(struct device_node* node, int param, int value) +static long __pmac +core99_ide_reset(struct device_node* node, long param, long value) { switch(param) { case 0: @@ -904,8 +901,8 @@ core99_ide_reset(struct device_node* node, int param, int value) } } -static int __pmac -core99_gmac_enable(struct device_node* node, int param, int value) +static long __pmac +core99_gmac_enable(struct device_node* node, long param, long value) { unsigned long flags; @@ -921,8 +918,8 @@ core99_gmac_enable(struct device_node* node, int param, int value) return 0; } -static int __pmac -core99_gmac_phy_reset(struct device_node* node, int param, int value) +static long __pmac +core99_gmac_phy_reset(struct device_node* node, long param, long value) { unsigned long flags; struct macio_chip* macio; @@ -938,16 +935,16 @@ core99_gmac_phy_reset(struct device_node* node, int param, int value) UNLOCK(flags); mdelay(10); LOCK(flags); - MACIO_OUT8(KL_GPIO_ETH_PHY_RESET, KEYLARGO_GPIO_OUTPUT_ENABLE - | KEYLARGO_GPIO_OUTOUT_DATA); + MACIO_OUT8(KL_GPIO_ETH_PHY_RESET, /*KEYLARGO_GPIO_OUTPUT_ENABLE | */ + KEYLARGO_GPIO_OUTOUT_DATA); UNLOCK(flags); mdelay(10); return 0; } -static int __pmac -core99_sound_chip_enable(struct device_node* node, int param, int value) +static long __pmac +core99_sound_chip_enable(struct device_node* node, long param, long value) { struct macio_chip* macio; unsigned long flags; @@ -976,8 +973,8 @@ core99_sound_chip_enable(struct device_node* node, int param, int value) return 0; } -static int __pmac -core99_airport_enable(struct device_node* node, int param, int value) +static long __pmac +core99_airport_enable(struct device_node* node, long param, long value) { struct macio_chip* macio; unsigned long flags; @@ -1063,8 +1060,8 @@ core99_airport_enable(struct device_node* node, int param, int value) } #ifdef CONFIG_SMP -static int __pmac -core99_reset_cpu(struct device_node* node, int param, int value) +static long __pmac +core99_reset_cpu(struct device_node* node, long param, long value) { unsigned int reset_io = 0; unsigned long flags; @@ -1099,7 +1096,7 @@ core99_reset_cpu(struct device_node* node, int param, int value) MACIO_OUT8(reset_io, KEYLARGO_GPIO_OUTPUT_ENABLE); (void)MACIO_IN8(reset_io); udelay(1); - MACIO_OUT8(reset_io, KEYLARGO_GPIO_OUTOUT_DATA | KEYLARGO_GPIO_OUTPUT_ENABLE); + MACIO_OUT8(reset_io, 0); (void)MACIO_IN8(reset_io); UNLOCK(flags); @@ -1107,8 +1104,8 @@ core99_reset_cpu(struct device_node* node, int param, int value) } #endif /* CONFIG_SMP */ -static int __pmac -core99_usb_enable(struct device_node* node, int param, int value) +static long __pmac +core99_usb_enable(struct device_node* node, long param, long value) { struct macio_chip* macio; unsigned long flags; @@ -1121,9 +1118,6 @@ core99_usb_enable(struct device_node* node, int param, int value) macio->type != macio_intrepid) return -ENODEV; - /* XXX Fix handling of 3rd USB controller in Intrepid, move the - * port connect stuff (KL4_*) to the sleep code eventually - */ prop = (char *)get_property(node, "AAPL,clock-id", NULL); if (!prop) return -ENODEV; @@ -1131,6 +1125,8 @@ core99_usb_enable(struct device_node* node, int param, int value) number = 0; else if (strncmp(prop, "usb1u148", 8) == 0) number = 2; + else if (strncmp(prop, "usb2u248", 8) == 0) + number = 4; else return -ENODEV; @@ -1147,44 +1143,79 @@ core99_usb_enable(struct device_node* node, int param, int value) mdelay(1); LOCK(flags); MACIO_BIS(KEYLARGO_FCR0, KL0_USB0_CELL_ENABLE); - } else { + } else if (number == 2) { MACIO_BIC(KEYLARGO_FCR0, (KL0_USB1_PAD_SUSPEND0 | KL0_USB1_PAD_SUSPEND1)); UNLOCK(flags); (void)MACIO_IN32(KEYLARGO_FCR0); mdelay(1); LOCK(flags); MACIO_BIS(KEYLARGO_FCR0, KL0_USB1_CELL_ENABLE); + } else if (number == 4) { + MACIO_BIC(KEYLARGO_FCR1, (KL1_USB2_PAD_SUSPEND0 | KL1_USB2_PAD_SUSPEND1)); + UNLOCK(flags); + (void)MACIO_IN32(KEYLARGO_FCR1); + mdelay(1); + LOCK(flags); + MACIO_BIS(KEYLARGO_FCR0, KL1_USB2_CELL_ENABLE); + } + if (number < 4) { + reg = MACIO_IN32(KEYLARGO_FCR4); + reg &= ~(KL4_PORT_WAKEUP_ENABLE(number) | KL4_PORT_RESUME_WAKE_EN(number) | + KL4_PORT_CONNECT_WAKE_EN(number) | KL4_PORT_DISCONNECT_WAKE_EN(number)); + reg &= ~(KL4_PORT_WAKEUP_ENABLE(number+1) | KL4_PORT_RESUME_WAKE_EN(number+1) | + KL4_PORT_CONNECT_WAKE_EN(number+1) | KL4_PORT_DISCONNECT_WAKE_EN(number+1)); + MACIO_OUT32(KEYLARGO_FCR4, reg); + (void)MACIO_IN32(KEYLARGO_FCR4); + udelay(10); + } else { + reg = MACIO_IN32(KEYLARGO_FCR3); + reg &= ~(KL3_IT_PORT_WAKEUP_ENABLE(0) | KL3_IT_PORT_RESUME_WAKE_EN(0) | + KL3_IT_PORT_CONNECT_WAKE_EN(0) | KL3_IT_PORT_DISCONNECT_WAKE_EN(0)); + reg &= ~(KL3_IT_PORT_WAKEUP_ENABLE(1) | KL3_IT_PORT_RESUME_WAKE_EN(1) | + KL3_IT_PORT_CONNECT_WAKE_EN(1) | KL3_IT_PORT_DISCONNECT_WAKE_EN(1)); + MACIO_OUT32(KEYLARGO_FCR3, reg); + (void)MACIO_IN32(KEYLARGO_FCR3); + udelay(10); } - reg = MACIO_IN32(KEYLARGO_FCR4); - reg &= ~(KL4_PORT_WAKEUP_ENABLE(number) | KL4_PORT_RESUME_WAKE_EN(number) | - KL4_PORT_CONNECT_WAKE_EN(number) | KL4_PORT_DISCONNECT_WAKE_EN(number)); - reg &= ~(KL4_PORT_WAKEUP_ENABLE(number+1) | KL4_PORT_RESUME_WAKE_EN(number+1) | - KL4_PORT_CONNECT_WAKE_EN(number+1) | KL4_PORT_DISCONNECT_WAKE_EN(number+1)); - MACIO_OUT32(KEYLARGO_FCR4, reg); - (void)MACIO_IN32(KEYLARGO_FCR4); - udelay(10); } else { /* Turn OFF */ - reg = MACIO_IN32(KEYLARGO_FCR4); - reg |= KL4_PORT_WAKEUP_ENABLE(number) | KL4_PORT_RESUME_WAKE_EN(number) | - KL4_PORT_CONNECT_WAKE_EN(number) | KL4_PORT_DISCONNECT_WAKE_EN(number); - reg |= KL4_PORT_WAKEUP_ENABLE(number+1) | KL4_PORT_RESUME_WAKE_EN(number+1) | - KL4_PORT_CONNECT_WAKE_EN(number+1) | KL4_PORT_DISCONNECT_WAKE_EN(number+1); - MACIO_OUT32(KEYLARGO_FCR4, reg); - (void)MACIO_IN32(KEYLARGO_FCR4); - udelay(1); + if (number < 4) { + reg = MACIO_IN32(KEYLARGO_FCR4); + reg |= KL4_PORT_WAKEUP_ENABLE(number) | KL4_PORT_RESUME_WAKE_EN(number) | + KL4_PORT_CONNECT_WAKE_EN(number) | KL4_PORT_DISCONNECT_WAKE_EN(number); + reg |= KL4_PORT_WAKEUP_ENABLE(number+1) | KL4_PORT_RESUME_WAKE_EN(number+1) | + KL4_PORT_CONNECT_WAKE_EN(number+1) | KL4_PORT_DISCONNECT_WAKE_EN(number+1); + MACIO_OUT32(KEYLARGO_FCR4, reg); + (void)MACIO_IN32(KEYLARGO_FCR4); + udelay(1); + } else { + reg = MACIO_IN32(KEYLARGO_FCR3); + reg |= KL3_IT_PORT_WAKEUP_ENABLE(0) | KL3_IT_PORT_RESUME_WAKE_EN(0) | + KL3_IT_PORT_CONNECT_WAKE_EN(0) | KL3_IT_PORT_DISCONNECT_WAKE_EN(0); + reg |= KL3_IT_PORT_WAKEUP_ENABLE(1) | KL3_IT_PORT_RESUME_WAKE_EN(1) | + KL3_IT_PORT_CONNECT_WAKE_EN(1) | KL3_IT_PORT_DISCONNECT_WAKE_EN(1); + MACIO_OUT32(KEYLARGO_FCR3, reg); + (void)MACIO_IN32(KEYLARGO_FCR3); + udelay(1); + } if (number == 0) { MACIO_BIC(KEYLARGO_FCR0, KL0_USB0_CELL_ENABLE); (void)MACIO_IN32(KEYLARGO_FCR0); udelay(1); MACIO_BIS(KEYLARGO_FCR0, (KL0_USB0_PAD_SUSPEND0 | KL0_USB0_PAD_SUSPEND1)); (void)MACIO_IN32(KEYLARGO_FCR0); - } else { + } else if (number == 2) { MACIO_BIC(KEYLARGO_FCR0, KL0_USB1_CELL_ENABLE); (void)MACIO_IN32(KEYLARGO_FCR0); udelay(1); MACIO_BIS(KEYLARGO_FCR0, (KL0_USB1_PAD_SUSPEND0 | KL0_USB1_PAD_SUSPEND1)); (void)MACIO_IN32(KEYLARGO_FCR0); + } else if (number == 4) { + MACIO_BIC(KEYLARGO_FCR1, KL1_USB2_CELL_ENABLE); + (void)MACIO_IN32(KEYLARGO_FCR1); + udelay(1); + MACIO_BIS(KEYLARGO_FCR1, (KL1_USB2_PAD_SUSPEND0 | KL1_USB2_PAD_SUSPEND1)); + (void)MACIO_IN32(KEYLARGO_FCR1); } udelay(1); } @@ -1193,8 +1224,8 @@ core99_usb_enable(struct device_node* node, int param, int value) return 0; } -static int __pmac -core99_firewire_enable(struct device_node* node, int param, int value) +static long __pmac +core99_firewire_enable(struct device_node* node, long param, long value) { unsigned long flags; struct macio_chip* macio; @@ -1220,8 +1251,8 @@ core99_firewire_enable(struct device_node* node, int param, int value) return 0; } -static int __pmac -core99_firewire_cable_power(struct device_node* node, int param, int value) +static long __pmac +core99_firewire_cable_power(struct device_node* node, long param, long value) { unsigned long flags; struct macio_chip* macio; @@ -1251,8 +1282,10 @@ core99_firewire_cable_power(struct device_node* node, int param, int value) return 0; } -static int __pmac -core99_read_gpio(struct device_node* node, int param, int value) +#endif /* CONFIG_POWER4 */ + +static long __pmac +core99_read_gpio(struct device_node* node, long param, long value) { struct macio_chip* macio = &macio_chips[0]; @@ -1260,8 +1293,8 @@ core99_read_gpio(struct device_node* node, int param, int value) } -static int __pmac -core99_write_gpio(struct device_node* node, int param, int value) +static long __pmac +core99_write_gpio(struct device_node* node, long param, long value) { struct macio_chip* macio = &macio_chips[0]; @@ -1269,6 +1302,145 @@ core99_write_gpio(struct device_node* node, int param, int value) return 0; } +#ifdef CONFIG_POWER4 + +static long __pmac +g5_gmac_enable(struct device_node* node, long param, long value) +{ + struct macio_chip* macio = &macio_chips[0]; + unsigned long flags; + struct pci_dev *pdev; + u8 pbus, pid; + + /* XXX FIXME: We should fix pci_device_from_OF_node here, and + * get to a real pci_dev or we'll get into trouble with PCI + * domains the day we get overlapping numbers (like if we ever + * decide to show the HT root + */ + if (pci_device_from_OF_node(node, &pbus, &pid) == 0) + pdev = pci_find_slot(pbus, pid); + + LOCK(flags); + if (value) { + MACIO_BIS(KEYLARGO_FCR1, K2_FCR1_GMAC_CLK_ENABLE); + mb(); + k2_skiplist[0] = NULL; + } else { + k2_skiplist[0] = pdev; + mb(); + MACIO_BIC(KEYLARGO_FCR1, K2_FCR1_GMAC_CLK_ENABLE); + } + + UNLOCK(flags); + mdelay(1); + + return 0; +} + +static long __pmac +g5_fw_enable(struct device_node* node, long param, long value) +{ + struct macio_chip* macio = &macio_chips[0]; + unsigned long flags; + struct pci_dev *pdev; + u8 pbus, pid; + + /* XXX FIXME: We should fix pci_device_from_OF_node here, and + * get to a real pci_dev or we'll get into trouble with PCI + * domains the day we get overlapping numbers (like if we ever + * decide to show the HT root + */ + if (pci_device_from_OF_node(node, &pbus, &pid) == 0) + pdev = pci_find_slot(pbus, pid); + + LOCK(flags); + if (value) { + MACIO_BIS(KEYLARGO_FCR1, K2_FCR1_FW_CLK_ENABLE); + mb(); + k2_skiplist[1] = NULL; + } else { + k2_skiplist[0] = pdev; + mb(); + MACIO_BIC(KEYLARGO_FCR1, K2_FCR1_FW_CLK_ENABLE); + } + + UNLOCK(flags); + mdelay(1); + + return 0; +} + +static long __pmac +g5_mpic_enable(struct device_node* node, long param, long value) +{ + unsigned long flags; + + if (node->parent == NULL || strcmp(node->parent->name, "u3")) + return 0; + + LOCK(flags); + UN_BIS(U3_TOGGLE_REG, U3_MPIC_RESET | U3_MPIC_OUTPUT_ENABLE); + UNLOCK(flags); + + return 0; +} + +#ifdef CONFIG_SMP +static long __pmac +g5_reset_cpu(struct device_node* node, long param, long value) +{ + unsigned int reset_io = 0; + unsigned long flags; + struct macio_chip* macio; + struct device_node* np; + + macio = &macio_chips[0]; + if (macio->type != macio_keylargo2) + return -ENODEV; + + np = find_path_device("/cpus"); + if (np == NULL) + return -ENODEV; + for (np = np->child; np != NULL; np = np->sibling) { + u32* num = (u32 *)get_property(np, "reg", NULL); + u32* rst = (u32 *)get_property(np, "soft-reset", NULL); + if (num == NULL || rst == NULL) + continue; + if (param == *num) { + reset_io = *rst; + break; + } + } + if (np == NULL || reset_io == 0) + return -ENODEV; + + LOCK(flags); + MACIO_OUT8(reset_io, KEYLARGO_GPIO_OUTPUT_ENABLE); + (void)MACIO_IN8(reset_io); + udelay(1); + MACIO_OUT8(reset_io, 0); + (void)MACIO_IN8(reset_io); + UNLOCK(flags); + + return 0; +} +#endif /* CONFIG_SMP */ + +/* + * This can be called from pmac_smp so isn't static + * + * This takes the second CPU off the bus on dual CPU machines + * running UP + */ +void __pmac g5_phy_disable_cpu1(void) +{ + UN_OUT(U3_API_PHY_CONFIG_1, 0); +} + +#endif /* CONFIG_POWER4 */ + +#ifndef CONFIG_POWER4 + static void __pmac keylargo_shutdown(struct macio_chip* macio, int sleep_mode) { @@ -1541,8 +1713,8 @@ core99_wake_up(void) return 0; } -static int __pmac -core99_sleep_state(struct device_node* node, int param, int value) +static long __pmac +core99_sleep_state(struct device_node* node, long param, long value) { /* Param == 1 means to enter the "fake sleep" mode that is * used for CPU speed switch @@ -1568,8 +1740,10 @@ core99_sleep_state(struct device_node* node, int param, int value) return 0; } -static int __pmac -generic_get_mb_info(struct device_node* node, int param, int value) +#endif /* CONFIG_POWER4 */ + +static long __pmac +generic_get_mb_info(struct device_node* node, long param, long value) { switch(param) { case PMAC_MB_INFO_MODEL: @@ -1579,9 +1753,9 @@ generic_get_mb_info(struct device_node* node, int param, int value) case PMAC_MB_INFO_NAME: /* hack hack hack... but should work */ *((const char **)value) = pmac_mb.model_name; - break; + return 0; } - return 0; + return -EINVAL; } @@ -1596,6 +1770,8 @@ static struct feature_table_entry any_features[] __pmacdata = { { 0, NULL } }; +#ifndef CONFIG_POWER4 + /* OHare based motherboards. Currently, we only use these on the * 2400,3400 and 3500 series powerbooks. Some older desktops seem * to have issues with turning on/off those asic cells @@ -1741,10 +1917,29 @@ static struct feature_table_entry intrepid_features[] __pmacdata = { { 0, NULL } }; +#else /* CONFIG_POWER4 */ + +/* G5 features + */ +static struct feature_table_entry g5_features[] __pmacdata = { + { PMAC_FTR_GMAC_ENABLE, g5_gmac_enable }, + { PMAC_FTR_1394_ENABLE, g5_fw_enable }, + { PMAC_FTR_ENABLE_MPIC, g5_mpic_enable }, +#ifdef CONFIG_SMP + { PMAC_FTR_RESET_CPU, g5_reset_cpu }, +#endif /* CONFIG_SMP */ + { PMAC_FTR_READ_GPIO, core99_read_gpio }, + { PMAC_FTR_WRITE_GPIO, core99_write_gpio }, + { 0, NULL } +}; + +#endif /* CONFIG_POWER4 */ + static struct pmac_mb_def pmac_mb_defs[] __pmacdata = { /* Warning: ordering is important as some models may claim * beeing compatible with several types */ +#ifndef CONFIG_POWER4 { "AAPL,8500", "PowerMac 8500/8600", PMAC_TYPE_PSURGE, NULL, 0 @@ -1753,6 +1948,14 @@ static struct pmac_mb_def pmac_mb_defs[] __pmacdata = { PMAC_TYPE_PSURGE, NULL, 0 }, + { "AAPL,7200", "PowerMac 7200", + PMAC_TYPE_PSURGE, NULL, + 0 + }, + { "AAPL,7300", "PowerMac 7200/7300", + PMAC_TYPE_PSURGE, NULL, + 0 + }, { "AAPL,7500", "PowerMac 7500", PMAC_TYPE_PSURGE, NULL, 0 @@ -1905,20 +2108,43 @@ static struct pmac_mb_def pmac_mb_defs[] __pmacdata = { PMAC_TYPE_UNKNOWN_INTREPID, intrepid_features, PMAC_MB_HAS_FW_POWER | PMAC_MB_MOBILE, }, + { "PowerBook5,2", "PowerBook G4 15\"", + PMAC_TYPE_UNKNOWN_INTREPID, intrepid_features, + PMAC_MB_HAS_FW_POWER | PMAC_MB_MOBILE, + }, + { "PowerBook5,3", "PowerBook G4 17\"", + PMAC_TYPE_UNKNOWN_INTREPID, intrepid_features, + PMAC_MB_HAS_FW_POWER | PMAC_MB_MOBILE, + }, { "PowerBook6,1", "PowerBook G4 12\"", PMAC_TYPE_UNKNOWN_INTREPID, intrepid_features, PMAC_MB_HAS_FW_POWER | PMAC_MB_MOBILE, }, + { "PowerBook6,2", "PowerBook G4", + PMAC_TYPE_UNKNOWN_INTREPID, intrepid_features, + PMAC_MB_HAS_FW_POWER | PMAC_MB_MOBILE, + }, + { "PowerBook6,3", "iBook G4", + PMAC_TYPE_UNKNOWN_INTREPID, intrepid_features, + PMAC_MB_HAS_FW_POWER | PMAC_MB_MOBILE, + }, +#else /* CONFIG_POWER4 */ + { "PowerMac7,2", "PowerMac G5", + PMAC_TYPE_POWERMAC_G5, g5_features, + 0, + }, +#endif /* CONFIG_POWER4 */ }; /* * The toplevel feature_call callback */ -int __pmac +long __pmac pmac_do_feature_call(unsigned int selector, ...) { struct device_node* node; - int param, value, i; + long param, value; + int i; feature_call func = NULL; va_list args; @@ -1939,8 +2165,8 @@ pmac_do_feature_call(unsigned int selector, ...) va_start(args, selector); node = (struct device_node*)va_arg(args, void*); - param = va_arg(args, int); - value = va_arg(args, int); + param = va_arg(args, long); + value = va_arg(args, long); va_end(args); return func(node, param, value); @@ -1976,6 +2202,7 @@ probe_motherboard(void) /* Fallback to selection depending on mac-io chip type */ switch(macio->type) { +#ifndef CONFIG_POWER4 case macio_grand_central: pmac_mb.model_id = PMAC_TYPE_PSURGE; pmac_mb.model_name = "Unknown PowerSurge"; @@ -2009,10 +2236,18 @@ probe_motherboard(void) pmac_mb.model_name = "Unknown Intrepid-based"; pmac_mb.features = intrepid_features; break; +#else /* CONFIG_POWER4 */ + case macio_keylargo2: + pmac_mb.model_id = PMAC_TYPE_POWERMAC_G5; + pmac_mb.model_name = "Unknown G5"; + pmac_mb.features = g5_features; + break; +#endif /* CONFIG_POWER4 */ default: return -ENODEV; } found: +#ifndef CONFIG_POWER4 /* Fixup Hooper vs. Comet */ if (pmac_mb.model_id == PMAC_TYPE_HOOPER) { u32* mach_id_ptr = (u32*)ioremap(0xf3000034, 4); @@ -2026,6 +2261,7 @@ found: pmac_mb.model_id = PMAC_TYPE_COMET; iounmap(mach_id_ptr); } +#endif /* CONFIG_POWER4 */ #ifdef CONFIG_6xx /* Set default value of powersave_nap on machines that support it. @@ -2057,7 +2293,9 @@ found: */ powersave_lowspeed = 1; #endif /* CONFIG_6xx */ - +#ifdef CONFIG_POWER4 + powersave_nap = 1; +#endif /* Check for "mobile" machine */ if (model && (strncmp(model, "PowerBook", 9) == 0 || strncmp(model, "iBook", 5) == 0)) @@ -2076,18 +2314,26 @@ probe_uninorth(void) unsigned long actrl; /* Locate core99 Uni-N */ - uninorth_node = find_devices("uni-n"); + uninorth_node = of_find_node_by_name(NULL, "uni-n"); + /* Locate G5 u3 */ + if (uninorth_node == NULL) { + uninorth_node = of_find_node_by_name(NULL, "u3"); + uninorth_u3 = 1; + } if (uninorth_node && uninorth_node->n_addrs > 0) { - uninorth_base = ioremap(uninorth_node->addrs[0].address, 0x4000); + unsigned long address = uninorth_node->addrs[0].address; + uninorth_base = ioremap(address, 0x40000); uninorth_rev = in_be32(UN_REG(UNI_N_VERSION)); + if (uninorth_u3) + u3_ht = ioremap(address + U3_HT_CONFIG_BASE, 0x1000); } else uninorth_node = NULL; if (!uninorth_node) return; - printk(KERN_INFO "Found Uninorth memory controller & host bridge, revision: %d\n", - uninorth_rev); + printk(KERN_INFO "Found %s memory controller & host bridge, revision: %d\n", + uninorth_u3 ? "U3" : "UniNorth", uninorth_rev); printk(KERN_INFO "Mapped at 0x%08lx\n", (unsigned long)uninorth_base); /* Set the arbitrer QAck delay according to what Apple does @@ -2172,6 +2418,7 @@ probe_macios(void) probe_one_macio("mac-io", "paddington", macio_paddington); probe_one_macio("mac-io", "gatwick", macio_gatwick); probe_one_macio("mac-io", "heathrow", macio_heathrow); + probe_one_macio("mac-io", "K2-Keylargo", macio_keylargo2); /* Make sure the "main" macio chip appear first */ if (macio_chips[0].type == macio_gatwick @@ -2244,19 +2491,60 @@ set_initial_features(void) MACIO_BIS(OHARE_FCR, OH_IOBUS_ENABLE); } +#ifdef CONFIG_POWER4 + if (macio_chips[0].type == macio_keylargo2) { +#ifndef CONFIG_SMP + /* On SMP machines running UP, we have the second CPU eating + * bus cycles. We need to take it off the bus. This is done + * from pmac_smp for SMP kernels running on one CPU + */ + np = of_find_node_by_type(NULL, "cpu"); + if (np != NULL) + np = of_find_node_by_type(np, "cpu"); + if (np != NULL) { + g5_phy_disable_cpu1(); + of_node_put(np); + } +#endif /* CONFIG_SMP */ + /* Enable GMAC for now for PCI probing. It will be disabled + * later on after PCI probe + */ + np = of_find_node_by_name(NULL, "ethernet"); + while(np) { + if (device_is_compatible(np, "K2-GMAC")) + g5_gmac_enable(np, 0, 1); + np = of_find_node_by_name(np, "ethernet"); + } + + /* Enable FW before PCI probe. Will be disabled later on + * Note: We should have a batter way to check that we are + * dealing with uninorth internal cell and not a PCI cell + * on the external PCI. The code below works though. + */ + np = of_find_node_by_name(NULL, "firewire"); + while(np) { + if (device_is_compatible(np, "pci106b,5811")) { + macio_chips[0].flags |= MACIO_FLAG_FW_SUPPORTED; + g5_fw_enable(np, 0, 1); + } + np = of_find_node_by_name(np, "firewire"); + } + } +#else /* CONFIG_POWER4 */ + if (macio_chips[0].type == macio_keylargo || macio_chips[0].type == macio_pangea || macio_chips[0].type == macio_intrepid) { /* Enable GMAC for now for PCI probing. It will be disabled * later on after PCI probe */ - np = find_devices("ethernet"); + np = of_find_node_by_name(NULL, "ethernet"); while(np) { if (np->parent && device_is_compatible(np->parent, "uni-north") && device_is_compatible(np, "gmac")) core99_gmac_enable(np, 0, 1); - np = np->next; + np = of_find_node_by_name(np, "ethernet"); } /* Enable FW before PCI probe. Will be disabled later on @@ -2264,7 +2552,7 @@ set_initial_features(void) * dealing with uninorth internal cell and not a PCI cell * on the external PCI. The code below works though. */ - np = find_devices("firewire"); + np = of_find_node_by_name(NULL, "firewire"); while(np) { if (np->parent && device_is_compatible(np->parent, "uni-north") @@ -2274,18 +2562,18 @@ set_initial_features(void) macio_chips[0].flags |= MACIO_FLAG_FW_SUPPORTED; core99_firewire_enable(np, 0, 1); } - np = np->next; + np = of_find_node_by_name(np, "firewire"); } /* Enable ATA-100 before PCI probe. */ - np = find_devices("ata-6"); + np = of_find_node_by_name(NULL, "ata-6"); while(np) { if (np->parent && device_is_compatible(np->parent, "uni-north") && device_is_compatible(np, "kauai-ata")) { core99_ata100_enable(np, 1); } - np = np->next; + np = of_find_node_by_name(np, "ata-6"); } /* Switch airport off */ @@ -2367,3 +2655,55 @@ pmac_feature_late_init(void) } device_initcall(pmac_feature_late_init); + +#ifdef CONFIG_POWER4 + +static void dump_HT_speeds(char *name, u32 cfg, u32 frq) +{ + int freqs[16] = { 200,300,400,500,600,800,1000,0,0,0,0,0,0,0,0,0 }; + int bits[8] = { 8,16,0,32,2,4,0,0 }; + int freq = (frq >> 8) & 0xf; + + if (freqs[freq] == 0) + printk("%s: Unknown HT link frequency %x\n", name, freq); + else + printk("%s: %d MHz on main link, (%d in / %d out) bits width\n", + name, freqs[freq], + bits[(cfg >> 28) & 0x7], bits[(cfg >> 24) & 0x7]); +} + +void __init pmac_check_ht_link(void) +{ + u32 ufreq, freq, ucfg, cfg; + struct device_node *pcix_node; + u8 px_bus, px_devfn; + struct pci_controller *px_hose; + + (void)in_be32(u3_ht + U3_HT_LINK_COMMAND); + ucfg = cfg = in_be32(u3_ht + U3_HT_LINK_CONFIG); + ufreq = freq = in_be32(u3_ht + U3_HT_LINK_FREQ); + dump_HT_speeds("U3 HyperTransport", cfg, freq); + + pcix_node = of_find_compatible_node(NULL, "pci", "pci-x"); + if (pcix_node == NULL) { + printk("No PCI-X bridge found\n"); + return; + } + if (pci_device_from_OF_node(pcix_node, &px_bus, &px_devfn) != 0) { + printk("PCI-X bridge found but not matched to pci\n"); + return; + } + px_hose = pci_find_hose_for_OF_device(pcix_node); + if (px_hose == NULL) { + printk("PCI-X bridge found but not matched to host\n"); + return; + } + early_read_config_dword(px_hose, px_bus, px_devfn, 0xc4, &cfg); + early_read_config_dword(px_hose, px_bus, px_devfn, 0xcc, &freq); + dump_HT_speeds("PCI-X HT Uplink", cfg, freq); + early_read_config_dword(px_hose, px_bus, px_devfn, 0xc8, &cfg); + early_read_config_dword(px_hose, px_bus, px_devfn, 0xd0, &freq); + dump_HT_speeds("PCI-X HT Downlink", cfg, freq); +} + +#endif /* CONFIG_POWER4 */ diff --git a/include/asm-ppc/machdep.h b/include/asm-ppc/machdep.h index c241973ff78e..054e9db2e226 100644 --- a/include/asm-ppc/machdep.h +++ b/include/asm-ppc/machdep.h @@ -95,7 +95,7 @@ struct machdep_calls { * hook used to control some machine specific features (like reset * lines, chip power control, etc...). */ - int (*feature_call)(unsigned int feature, ...); + long (*feature_call)(unsigned int feature, ...); #ifdef CONFIG_SMP /* functions for dealing with other cpus */ diff --git a/include/asm-ppc/pmac_feature.h b/include/asm-ppc/pmac_feature.h index 1c97c90068e9..83cc2ea442c4 100644 --- a/include/asm-ppc/pmac_feature.h +++ b/include/asm-ppc/pmac_feature.h @@ -112,6 +112,10 @@ */ #define PMAC_TYPE_UNKNOWN_INTREPID 0x11f /* Generic */ +/* MacRISC4 / G5 machines + */ +#define PMAC_TYPE_POWERMAC_G5 0x150 /* First tower */ + /* * Motherboard flags */ @@ -131,8 +135,8 @@ */ struct device_node; -static inline int pmac_call_feature(int selector, struct device_node* node, - int param, int value) +static inline long pmac_call_feature(int selector, struct device_node* node, + long param, long value) { if (!ppc_md.feature_call) return -ENODEV; @@ -262,9 +266,15 @@ static inline int pmac_call_feature(int selector, struct device_node* node, */ #define PMAC_FTR_WRITE_GPIO PMAC_FTR_DEF(18) +/* PMAC_FTR_ENABLE_MPIC + * + * Enable the MPIC cell + */ +#define PMAC_FTR_ENABLE_MPIC PMAC_FTR_DEF(19) + /* Don't use those directly, they are for the sake of pmac_setup.c */ -extern int pmac_do_feature_call(unsigned int selector, ...); +extern long pmac_do_feature_call(unsigned int selector, ...); extern void pmac_feature_init(void); #define PMAC_FTR_DEF(x) ((_MACH_Pmac << 16) | (x)) @@ -289,6 +299,7 @@ enum { macio_keylargo, macio_pangea, macio_intrepid, + macio_keylargo2, }; struct macio_chip -- cgit v1.2.3 From 0c7bd075ffee8a8ef1b720c77d36672d379d30fc Mon Sep 17 00:00:00 2001 From: Benjamin Herrenschmidt Date: Fri, 6 Feb 2004 14:29:33 +1100 Subject: ppc32: Update PowerMac i2c management Create a low-level synchronous implementation suitable for use by the early boot platform code or other places where the asynchronous driver isn't useable. This also exports the locks used by the real driver to avoid collisions. Use this new implementation to properly setup the clock chip at boot on Apple latest laptops --- arch/ppc/platforms/Makefile | 3 +- arch/ppc/platforms/pmac_feature.c | 97 +++++++ arch/ppc/platforms/pmac_low_i2c.c | 513 ++++++++++++++++++++++++++++++++++++++ arch/ppc/platforms/pmac_setup.c | 21 +- drivers/i2c/busses/i2c-keywest.c | 371 ++++++++++++++++++--------- drivers/i2c/busses/i2c-keywest.h | 10 +- include/asm-ppc/pmac_low_i2c.h | 43 ++++ 7 files changed, 915 insertions(+), 143 deletions(-) create mode 100644 arch/ppc/platforms/pmac_low_i2c.c create mode 100644 include/asm-ppc/pmac_low_i2c.h (limited to 'include') diff --git a/arch/ppc/platforms/Makefile b/arch/ppc/platforms/Makefile index 9eb9829f3109..44026cb400ff 100644 --- a/arch/ppc/platforms/Makefile +++ b/arch/ppc/platforms/Makefile @@ -17,7 +17,8 @@ ifeq ($(CONFIG_APUS),y) obj-$(CONFIG_PCI) += apus_pci.o endif obj-$(CONFIG_PPC_PMAC) += pmac_pic.o pmac_setup.o pmac_time.o \ - pmac_feature.o pmac_pci.o pmac_sleep.o + pmac_feature.o pmac_pci.o pmac_sleep.o \ + pmac_low_i2c.o obj-$(CONFIG_PPC_CHRP) += chrp_setup.o chrp_time.o chrp_pci.o obj-$(CONFIG_PPC_PREP) += prep_pci.o prep_time.o prep_setup.o ifeq ($(CONFIG_PPC_PMAC),y) diff --git a/arch/ppc/platforms/pmac_feature.c b/arch/ppc/platforms/pmac_feature.c index 693d8648ffc8..cde0f17a0b2e 100644 --- a/arch/ppc/platforms/pmac_feature.c +++ b/arch/ppc/platforms/pmac_feature.c @@ -41,6 +41,7 @@ #include #include #include +#include #undef DEBUG_FEATURE @@ -2601,6 +2602,99 @@ set_initial_features(void) MACIO_BIC(HEATHROW_FCR, HRW_SOUND_POWER_N); } + /* Hack for bumping clock speed on the new PowerBooks and the + * iBook G4. This implements the "platform-do-clockspreading" OF + * property. For safety, we also check the product ID in the + * device-tree to make reasonably sure we won't set wrong values + * in the clock chip. + * + * Of course, ultimately, we have to implement a real parser for + * the platform-do-* stuff... + */ + while (machine_is_compatible("PowerBook5,2") || + machine_is_compatible("PowerBook5,3") || + machine_is_compatible("PowerBook6,2") || + machine_is_compatible("PowerBook6,3")) { + struct device_node *ui2c = of_find_node_by_type(NULL, "i2c"); + struct device_node *dt = of_find_node_by_name(NULL, "device-tree"); + u8 buffer[9]; + u32 *productID; + int i, rc, changed = 0; + + if (dt == NULL) + break; + productID = (u32 *)get_property(dt, "pid#", NULL); + if (productID == NULL) + break; + while(ui2c) { + struct device_node *p = of_get_parent(ui2c); + if (p && !strcmp(p->name, "uni-n")) + break; + ui2c = of_find_node_by_type(np, "i2c"); + } + if (ui2c == NULL) + break; + DBG("Trying to bump clock speed for PID: %08x...\n", *productID); + rc = pmac_low_i2c_open(ui2c, 1); + if (rc != 0) + break; + pmac_low_i2c_setmode(ui2c, pmac_low_i2c_mode_combined); + rc = pmac_low_i2c_xfer(ui2c, 0xd2 | pmac_low_i2c_read, 0x80, buffer, 9); + DBG("read result: %d,", rc); + if (rc != 0) { + pmac_low_i2c_close(ui2c); + break; + } + for (i=0; i<9; i++) + DBG(" %02x", buffer[i]); + DBG("\n"); + + switch(*productID) { + case 0x1182: /* AlBook 12" rev 2 */ + case 0x1183: /* iBook G4 12" */ + buffer[0] = (buffer[0] & 0x8f) | 0x70; + buffer[2] = (buffer[2] & 0x7f) | 0x00; + buffer[5] = (buffer[5] & 0x80) | 0x31; + buffer[6] = (buffer[6] & 0x40) | 0xb0; + buffer[7] = (buffer[7] & 0x00) | 0xc0; + buffer[8] = (buffer[8] & 0x00) | 0x30; + changed = 1; + break; + case 0x3142: /* AlBook 15" (ATI M10) */ + case 0x3143: /* AlBook 17" (ATI M10) */ + buffer[0] = (buffer[0] & 0xaf) | 0x50; + buffer[2] = (buffer[2] & 0x7f) | 0x00; + buffer[5] = (buffer[5] & 0x80) | 0x31; + buffer[6] = (buffer[6] & 0x40) | 0xb0; + buffer[7] = (buffer[7] & 0x00) | 0xd0; + buffer[8] = (buffer[8] & 0x00) | 0x30; + changed = 1; + break; + default: + DBG("i2c-hwclock: Machine model not handled\n"); + break; + } + if (!changed) { + pmac_low_i2c_close(ui2c); + break; + } + pmac_low_i2c_setmode(ui2c, pmac_low_i2c_mode_stdsub); + rc = pmac_low_i2c_xfer(ui2c, 0xd2 | pmac_low_i2c_write, 0x80, buffer, 9); + DBG("write result: %d,", rc); + pmac_low_i2c_setmode(ui2c, pmac_low_i2c_mode_combined); + rc = pmac_low_i2c_xfer(ui2c, 0xd2 | pmac_low_i2c_read, 0x80, buffer, 9); + DBG("read result: %d,", rc); + if (rc != 0) { + pmac_low_i2c_close(ui2c); + break; + } + for (i=0; i<9; i++) + DBG(" %02x", buffer[i]); + pmac_low_i2c_close(ui2c); + break; + } + +#endif /* CONFIG_POWER4 */ /* On all machines, switch modem & serial ports off */ np = find_devices("ch-a"); @@ -2627,6 +2721,9 @@ pmac_feature_init(void) return; } + /* Setup low-level i2c stuffs */ + pmac_init_low_i2c(); + /* Probe machine type */ if (probe_motherboard()) printk(KERN_WARNING "Unknown PowerMac !\n"); diff --git a/arch/ppc/platforms/pmac_low_i2c.c b/arch/ppc/platforms/pmac_low_i2c.c new file mode 100644 index 000000000000..d07579f2b8b9 --- /dev/null +++ b/arch/ppc/platforms/pmac_low_i2c.c @@ -0,0 +1,513 @@ +/* + * arch/ppc/platforms/pmac_low_i2c.c + * + * Copyright (C) 2003 Ben. Herrenschmidt (benh@kernel.crashing.org) + * + * 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 file contains some low-level i2c access routines that + * need to be used by various bits of the PowerMac platform code + * at times where the real asynchronous & interrupt driven driver + * cannot be used. The API borrows some semantics from the darwin + * driver in order to ease the implementation of the platform + * properties parser + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define MAX_LOW_I2C_HOST 4 + +#if 1 +#define DBG(x...) do {\ + printk(KERN_DEBUG "KW:" x); \ + } while(0) +#else +#define DBGG(x...) +#endif + +struct low_i2c_host; + +typedef int (*low_i2c_func_t)(struct low_i2c_host *host, u8 addr, u8 sub, u8 *data, int len); + +struct low_i2c_host +{ + struct device_node *np; /* OF device node */ + struct semaphore mutex; /* Access mutex for use by i2c-keywest */ + low_i2c_func_t func; /* Access function */ + int is_open : 1; /* Poor man's access control */ + int mode; /* Current mode */ + int channel; /* Current channel */ + int num_channels; /* Number of channels */ + unsigned long base; /* For keywest-i2c, base address */ + int bsteps; /* And register stepping */ + int speed; /* And speed */ +}; + +static struct low_i2c_host low_i2c_hosts[MAX_LOW_I2C_HOST]; + +/* No locking is necessary on allocation, we are running way before + * anything can race with us + */ +static struct low_i2c_host *find_low_i2c_host(struct device_node *np) +{ + int i; + + for (i = 0; i < MAX_LOW_I2C_HOST; i++) + if (low_i2c_hosts[i].np == np) + return &low_i2c_hosts[i]; + return NULL; +} + +/* + * + * i2c-keywest implementation (UniNorth, U2, U3, Keylargo's) + * + */ + +/* + * Keywest i2c definitions borrowed from drivers/i2c/i2c-keywest.h, + * should be moved somewhere in include/asm-ppc/ + */ +/* Register indices */ +typedef enum { + reg_mode = 0, + reg_control, + reg_status, + reg_isr, + reg_ier, + reg_addr, + reg_subaddr, + reg_data +} reg_t; + + +/* Mode register */ +#define KW_I2C_MODE_100KHZ 0x00 +#define KW_I2C_MODE_50KHZ 0x01 +#define KW_I2C_MODE_25KHZ 0x02 +#define KW_I2C_MODE_DUMB 0x00 +#define KW_I2C_MODE_STANDARD 0x04 +#define KW_I2C_MODE_STANDARDSUB 0x08 +#define KW_I2C_MODE_COMBINED 0x0C +#define KW_I2C_MODE_MODE_MASK 0x0C +#define KW_I2C_MODE_CHAN_MASK 0xF0 + +/* Control register */ +#define KW_I2C_CTL_AAK 0x01 +#define KW_I2C_CTL_XADDR 0x02 +#define KW_I2C_CTL_STOP 0x04 +#define KW_I2C_CTL_START 0x08 + +/* Status register */ +#define KW_I2C_STAT_BUSY 0x01 +#define KW_I2C_STAT_LAST_AAK 0x02 +#define KW_I2C_STAT_LAST_RW 0x04 +#define KW_I2C_STAT_SDA 0x08 +#define KW_I2C_STAT_SCL 0x10 + +/* IER & ISR registers */ +#define KW_I2C_IRQ_DATA 0x01 +#define KW_I2C_IRQ_ADDR 0x02 +#define KW_I2C_IRQ_STOP 0x04 +#define KW_I2C_IRQ_START 0x08 +#define KW_I2C_IRQ_MASK 0x0F + +/* State machine states */ +enum { + state_idle, + state_addr, + state_read, + state_write, + state_stop, + state_dead +}; + +#define WRONG_STATE(name) do {\ + printk(KERN_DEBUG "KW: wrong state. Got %s, state: %s (isr: %02x)\n", \ + name, __kw_state_names[state], isr); \ + } while(0) + +static const char *__kw_state_names[] = { + "state_idle", + "state_addr", + "state_read", + "state_write", + "state_stop", + "state_dead" +}; + +static inline u8 __kw_read_reg(struct low_i2c_host *host, reg_t reg) +{ + return in_8(((volatile u8 *)host->base) + + (((unsigned)reg) << host->bsteps)); +} + +static inline void __kw_write_reg(struct low_i2c_host *host, reg_t reg, u8 val) +{ + out_8(((volatile u8 *)host->base) + + (((unsigned)reg) << host->bsteps), val); + (void)__kw_read_reg(host, reg_subaddr); +} + +#define kw_write_reg(reg, val) __kw_write_reg(host, reg, val) +#define kw_read_reg(reg) __kw_read_reg(host, reg) + + +/* Don't schedule, the g5 fan controller is too + * timing sensitive + */ +static u8 kw_wait_interrupt(struct low_i2c_host* host) +{ + int i; + u8 isr; + + for (i = 0; i < 200000; i++) { + isr = kw_read_reg(reg_isr) & KW_I2C_IRQ_MASK; + if (isr != 0) + return isr; + udelay(1); + } + return isr; +} + +static int kw_handle_interrupt(struct low_i2c_host *host, int state, int rw, int *rc, u8 **data, int *len, u8 isr) +{ + u8 ack; + + if (isr == 0) { + if (state != state_stop) { + DBG("KW: Timeout !\n"); + *rc = -EIO; + goto stop; + } + if (state == state_stop) { + ack = kw_read_reg(reg_status); + if (!(ack & KW_I2C_STAT_BUSY)) { + state = state_idle; + kw_write_reg(reg_ier, 0x00); + } + } + return state; + } + + if (isr & KW_I2C_IRQ_ADDR) { + ack = kw_read_reg(reg_status); + if (state != state_addr) { + kw_write_reg(reg_isr, KW_I2C_IRQ_ADDR); + WRONG_STATE("KW_I2C_IRQ_ADDR"); + *rc = -EIO; + goto stop; + } + if ((ack & KW_I2C_STAT_LAST_AAK) == 0) { + *rc = -ENODEV; + DBG("KW: NAK on address\n"); + return state_stop; + } else { + if (rw) { + state = state_read; + if (*len > 1) + kw_write_reg(reg_control, KW_I2C_CTL_AAK); + } else { + state = state_write; + kw_write_reg(reg_data, **data); + (*data)++; (*len)--; + } + } + kw_write_reg(reg_isr, KW_I2C_IRQ_ADDR); + } + + if (isr & KW_I2C_IRQ_DATA) { + if (state == state_read) { + **data = kw_read_reg(reg_data); + (*data)++; (*len)--; + kw_write_reg(reg_isr, KW_I2C_IRQ_DATA); + if ((*len) == 0) + state = state_stop; + else if ((*len) == 1) + kw_write_reg(reg_control, 0); + } else if (state == state_write) { + ack = kw_read_reg(reg_status); + if ((ack & KW_I2C_STAT_LAST_AAK) == 0) { + DBG("KW: nack on data write\n"); + *rc = -EIO; + goto stop; + } else if (*len) { + kw_write_reg(reg_data, **data); + (*data)++; (*len)--; + } else { + kw_write_reg(reg_control, KW_I2C_CTL_STOP); + state = state_stop; + *rc = 0; + } + kw_write_reg(reg_isr, KW_I2C_IRQ_DATA); + } else { + kw_write_reg(reg_isr, KW_I2C_IRQ_DATA); + WRONG_STATE("KW_I2C_IRQ_DATA"); + if (state != state_stop) { + *rc = -EIO; + goto stop; + } + } + } + + if (isr & KW_I2C_IRQ_STOP) { + kw_write_reg(reg_isr, KW_I2C_IRQ_STOP); + if (state != state_stop) { + WRONG_STATE("KW_I2C_IRQ_STOP"); + *rc = -EIO; + } + return state_idle; + } + + if (isr & KW_I2C_IRQ_START) + kw_write_reg(reg_isr, KW_I2C_IRQ_START); + + return state; + + stop: + kw_write_reg(reg_control, KW_I2C_CTL_STOP); + return state_stop; +} + +static int keywest_low_i2c_func(struct low_i2c_host *host, u8 addr, u8 subaddr, u8 *data, int len) +{ + u8 mode_reg = host->speed; + int state = state_addr; + int rc = 0; + + /* Setup mode & subaddress if any */ + switch(host->mode) { + case pmac_low_i2c_mode_dumb: + printk(KERN_ERR "low_i2c: Dumb mode not supported !\n"); + return -EINVAL; + case pmac_low_i2c_mode_std: + mode_reg |= KW_I2C_MODE_STANDARD; + break; + case pmac_low_i2c_mode_stdsub: + mode_reg |= KW_I2C_MODE_STANDARDSUB; + kw_write_reg(reg_subaddr, subaddr); + break; + case pmac_low_i2c_mode_combined: + mode_reg |= KW_I2C_MODE_COMBINED; + kw_write_reg(reg_subaddr, subaddr); + break; + } + + /* Setup channel & clear pending irqs */ + kw_write_reg(reg_isr, kw_read_reg(reg_isr)); + kw_write_reg(reg_mode, mode_reg | (host->channel << 4)); + kw_write_reg(reg_status, 0); + + /* Set up address and r/w bit */ + kw_write_reg(reg_addr, addr); + + /* Start sending address & disable interrupt*/ + kw_write_reg(reg_ier, 0 /*KW_I2C_IRQ_MASK*/); + kw_write_reg(reg_control, KW_I2C_CTL_XADDR); + + /* State machine, to turn into an interrupt handler */ + while(state != state_idle) { + u8 isr = kw_wait_interrupt(host); + state = kw_handle_interrupt(host, state, addr & 1, &rc, &data, &len, isr); + } + + return rc; +} + +static void keywest_low_i2c_add(struct device_node *np) +{ + struct low_i2c_host *host = find_low_i2c_host(NULL); + unsigned long *psteps, *prate, steps, aoffset = 0; + struct device_node *parent; + + if (host == NULL) { + printk(KERN_ERR "low_i2c: Can't allocate host for %s\n", + np->full_name); + return; + } + memset(host, 0, sizeof(*host)); + + init_MUTEX(&host->mutex); + host->np = of_node_get(np); + psteps = (unsigned long *)get_property(np, "AAPL,address-step", NULL); + steps = psteps ? (*psteps) : 0x10; + for (host->bsteps = 0; (steps & 0x01) == 0; host->bsteps++) + steps >>= 1; + parent = of_get_parent(np); + host->num_channels = 1; + if (parent && parent->name[0] == 'u') { + host->num_channels = 2; + aoffset = 3; + } + /* Select interface rate */ + host->speed = KW_I2C_MODE_100KHZ; + prate = (unsigned long *)get_property(np, "AAPL,i2c-rate", NULL); + if (prate) switch(*prate) { + case 100: + host->speed = KW_I2C_MODE_100KHZ; + break; + case 50: + host->speed = KW_I2C_MODE_50KHZ; + break; + case 25: + host->speed = KW_I2C_MODE_25KHZ; + break; + } + host->mode = pmac_low_i2c_mode_std; + host->base = (unsigned long)ioremap(np->addrs[0].address + aoffset, + np->addrs[0].size); + host->func = keywest_low_i2c_func; +} + +/* + * + * PMU implementation + * + */ + + +#ifdef CONFIG_ADB_PMU + +static int pmu_low_i2c_func(struct low_i2c_host *host, u8 addr, u8 sub, u8 *data, int len) +{ + // TODO + return -ENODEV; +} + +static void pmu_low_i2c_add(struct device_node *np) +{ + struct low_i2c_host *host = find_low_i2c_host(NULL); + + if (host == NULL) { + printk(KERN_ERR "low_i2c: Can't allocate host for %s\n", + np->full_name); + return; + } + memset(host, 0, sizeof(*host)); + + init_MUTEX(&host->mutex); + host->np = of_node_get(np); + host->num_channels = 3; + host->mode = pmac_low_i2c_mode_std; + host->func = pmu_low_i2c_func; +} + +#endif /* CONFIG_ADB_PMU */ + +void __init pmac_init_low_i2c(void) +{ + struct device_node *np; + + /* Probe keywest-i2c busses */ + np = of_find_compatible_node(NULL, "i2c", "keywest-i2c"); + while(np) { + keywest_low_i2c_add(np); + np = of_find_compatible_node(np, "i2c", "keywest-i2c"); + } + +#ifdef CONFIG_ADB_PMU + /* Probe PMU busses */ + np = of_find_node_by_name(NULL, "via-pmu"); + if (np) + pmu_low_i2c_add(np); +#endif /* CONFIG_ADB_PMU */ + + /* TODO: Add CUDA support as well */ +} + +int pmac_low_i2c_lock(struct device_node *np) +{ + struct low_i2c_host *host = find_low_i2c_host(np); + + if (!host) + return -ENODEV; + down(&host->mutex); + return 0; +} +EXPORT_SYMBOL(pmac_low_i2c_lock); + +int pmac_low_i2c_unlock(struct device_node *np) +{ + struct low_i2c_host *host = find_low_i2c_host(np); + + if (!host) + return -ENODEV; + up(&host->mutex); + return 0; +} +EXPORT_SYMBOL(pmac_low_i2c_unlock); + + +int pmac_low_i2c_open(struct device_node *np, int channel) +{ + struct low_i2c_host *host = find_low_i2c_host(np); + + if (!host) + return -ENODEV; + + if (channel >= host->num_channels) + return -EINVAL; + + down(&host->mutex); + host->is_open = 1; + host->channel = channel; + + return 0; +} +EXPORT_SYMBOL(pmac_low_i2c_open); + +int pmac_low_i2c_close(struct device_node *np) +{ + struct low_i2c_host *host = find_low_i2c_host(np); + + if (!host) + return -ENODEV; + + host->is_open = 0; + up(&host->mutex); + + return 0; +} +EXPORT_SYMBOL(pmac_low_i2c_close); + +int pmac_low_i2c_setmode(struct device_node *np, int mode) +{ + struct low_i2c_host *host = find_low_i2c_host(np); + + if (!host) + return -ENODEV; + WARN_ON(!host->is_open); + host->mode = mode; + + return 0; +} +EXPORT_SYMBOL(pmac_low_i2c_setmode); + +int pmac_low_i2c_xfer(struct device_node *np, u8 addrdir, u8 subaddr, u8 *data, int len) +{ + struct low_i2c_host *host = find_low_i2c_host(np); + + if (!host) + return -ENODEV; + WARN_ON(!host->is_open); + + return host->func(host, addrdir, subaddr, data, len); +} +EXPORT_SYMBOL(pmac_low_i2c_xfer); + diff --git a/arch/ppc/platforms/pmac_setup.c b/arch/ppc/platforms/pmac_setup.c index 69bd381b43f6..345bdec1801f 100644 --- a/arch/ppc/platforms/pmac_setup.c +++ b/arch/ppc/platforms/pmac_setup.c @@ -469,10 +469,6 @@ pmac_restart(char *cmd) struct adb_request req; #endif /* CONFIG_ADB_CUDA */ -#ifdef CONFIG_NVRAM - pmac_nvram_update(); -#endif - switch (sys_ctrler) { #ifdef CONFIG_ADB_CUDA case SYS_CTRLER_CUDA: @@ -498,10 +494,6 @@ pmac_power_off(void) struct adb_request req; #endif /* CONFIG_ADB_CUDA */ -#ifdef CONFIG_NVRAM - pmac_nvram_update(); -#endif - switch (sys_ctrler) { #ifdef CONFIG_ADB_CUDA case SYS_CTRLER_CUDA: @@ -637,11 +629,6 @@ pmac_init(unsigned long r3, unsigned long r4, unsigned long r5, ppc_md.get_rtc_time = pmac_get_rtc_time; ppc_md.calibrate_decr = pmac_calibrate_decr; -#ifdef CONFIG_NVRAM - ppc_md.nvram_read_val = pmac_nvram_read_byte; - ppc_md.nvram_write_val = pmac_nvram_write_byte; -#endif - ppc_md.find_end_of_memory = pmac_find_end_of_memory; ppc_md.feature_call = pmac_do_feature_call; @@ -685,6 +672,14 @@ pmac_declare_of_platform_devices(void) break; } } + np = find_devices("u3"); + if (np) { + for (np = np->child; np != NULL; np = np->sibling) + if (strncmp(np->name, "i2c", 3) == 0) { + of_platform_device_create(np, "u3-i2c"); + break; + } + } np = find_devices("valkyrie"); if (np) diff --git a/drivers/i2c/busses/i2c-keywest.c b/drivers/i2c/busses/i2c-keywest.c index 64819889dd45..9d9aa44fefeb 100644 --- a/drivers/i2c/busses/i2c-keywest.c +++ b/drivers/i2c/busses/i2c-keywest.c @@ -26,6 +26,9 @@ 2001/12/13 BenH New implementation 2001/12/15 BenH Add support for "byte" and "quick" transfers. Add i2c_xfer routine. + 2003/09/21 BenH Rework state machine with Paulus help + 2004/01/21 BenH Merge in Greg KH changes, polled mode is back + 2004/02/05 BenH Merge 64 bits fixes from the g5 ppc64 tree My understanding of the various modes supported by keywest are: @@ -67,9 +70,35 @@ #include #include #include +#include #include "i2c-keywest.h" +/* Currently, we don't deal with the weird interrupt cascade of the G5 + * machines with the ppc64 kernel, so use Polled mode on these + */ +#ifdef CONFIG_PPC64 +#define POLLED_MODE +#else +#undef POLLED_MODE +#endif + +/* Some debug macros */ +#define WRONG_STATE(name) do {\ + pr_debug("KW: wrong state. Got %s, state: %s (isr: %02x)\n", \ + name, __kw_state_names[iface->state], isr); \ + } while(0) + +#ifdef DEBUG +static const char *__kw_state_names[] = { + "state_idle", + "state_addr", + "state_read", + "state_write", + "state_stop", + "state_dead" +}; +#endif /* DEBUG */ MODULE_AUTHOR("Benjamin Herrenschmidt "); MODULE_DESCRIPTION("I2C driver for Apple's Keywest"); @@ -78,121 +107,154 @@ MODULE_PARM(probe, "i"); static int probe = 0; +#ifdef POLLED_MODE +/* Don't schedule, the g5 fan controller is too + * timing sensitive + */ +static u8 +wait_interrupt(struct keywest_iface* iface) +{ + int i; + u8 isr; + + for (i = 0; i < 200000; i++) { + isr = read_reg(reg_isr) & KW_I2C_IRQ_MASK; + if (isr != 0) + return isr; + udelay(10); + } + return isr; +} +#endif /* POLLED_MODE */ + static void do_stop(struct keywest_iface* iface, int result) { - write_reg(reg_control, read_reg(reg_control) | KW_I2C_CTL_STOP); + write_reg(reg_control, KW_I2C_CTL_STOP); iface->state = state_stop; iface->result = result; } /* Main state machine for standard & standard sub mode */ -static int +static void handle_interrupt(struct keywest_iface *iface, u8 isr) { int ack; - int rearm_timer = 1; - pr_debug("handle_interrupt(), got: %x, status: %x, state: %d\n", - isr, read_reg(reg_status), iface->state); - if (isr == 0 && iface->state != state_stop) { - do_stop(iface, -1); - return rearm_timer; - } - if (isr & KW_I2C_IRQ_STOP && iface->state != state_stop) { - iface->result = -1; - iface->state = state_stop; - } - switch(iface->state) { - case state_addr: - if (!(isr & KW_I2C_IRQ_ADDR)) { - do_stop(iface, -1); - break; - } - ack = read_reg(reg_status); - pr_debug("ack on set address: %x\n", ack); - if ((ack & KW_I2C_STAT_LAST_AAK) == 0) { - do_stop(iface, -1); - break; + if (isr == 0) { + if (iface->state != state_stop) { + pr_debug("KW: Timeout !\n"); + do_stop(iface, -EIO); } - /* Handle rw "quick" mode */ - if (iface->datalen == 0) - do_stop(iface, 0); - else if (iface->read_write == I2C_SMBUS_READ) { - iface->state = state_read; - if (iface->datalen > 1) - write_reg(reg_control, read_reg(reg_control) - | KW_I2C_CTL_AAK); - } else { - iface->state = state_write; - pr_debug("write byte: %x\n", *(iface->data)); - write_reg(reg_data, *(iface->data++)); - iface->datalen--; - } - - break; - case state_read: - if (!(isr & KW_I2C_IRQ_DATA)) { - do_stop(iface, -1); - break; - } - *(iface->data++) = read_reg(reg_data); - pr_debug("read byte: %x\n", *(iface->data-1)); - iface->datalen--; - if (iface->datalen == 0) - iface->state = state_stop; - else - write_reg(reg_control, 0); - break; - case state_write: - if (!(isr & KW_I2C_IRQ_DATA)) { - do_stop(iface, -1); - break; + if (iface->state == state_stop) { + ack = read_reg(reg_status); + if (!(ack & KW_I2C_STAT_BUSY)) { + iface->state = state_idle; + write_reg(reg_ier, 0x00); +#ifndef POLLED_MODE + complete(&iface->complete); +#endif /* POLLED_MODE */ + } } - /* Check ack status */ + return; + } + + if (isr & KW_I2C_IRQ_ADDR) { ack = read_reg(reg_status); - pr_debug("ack on data write: %x\n", ack); + if (iface->state != state_addr) { + write_reg(reg_isr, KW_I2C_IRQ_ADDR); + WRONG_STATE("KW_I2C_IRQ_ADDR"); + do_stop(iface, -EIO); + return; + } if ((ack & KW_I2C_STAT_LAST_AAK) == 0) { - do_stop(iface, -1); - break; + iface->state = state_stop; + iface->result = -ENODEV; + pr_debug("KW: NAK on address\n"); + } else { + /* Handle rw "quick" mode */ + if (iface->datalen == 0) { + do_stop(iface, 0); + } else if (iface->read_write == I2C_SMBUS_READ) { + iface->state = state_read; + if (iface->datalen > 1) + write_reg(reg_control, KW_I2C_CTL_AAK); + } else { + iface->state = state_write; + write_reg(reg_data, *(iface->data++)); + iface->datalen--; + } } - if (iface->datalen) { - pr_debug("write byte: %x\n", *(iface->data)); - write_reg(reg_data, *(iface->data++)); + write_reg(reg_isr, KW_I2C_IRQ_ADDR); + } + + if (isr & KW_I2C_IRQ_DATA) { + if (iface->state == state_read) { + *(iface->data++) = read_reg(reg_data); + write_reg(reg_isr, KW_I2C_IRQ_DATA); iface->datalen--; - } else - do_stop(iface, 0); - break; - - case state_stop: - if (!(isr & KW_I2C_IRQ_STOP) && (++iface->stopretry) < 10) - do_stop(iface, -1); - else { - rearm_timer = 0; - iface->state = state_idle; - write_reg(reg_control, 0x00); - write_reg(reg_ier, 0x00); - complete(&iface->complete); + if (iface->datalen == 0) + iface->state = state_stop; + else if (iface->datalen == 1) + write_reg(reg_control, 0); + } else if (iface->state == state_write) { + /* Check ack status */ + ack = read_reg(reg_status); + if ((ack & KW_I2C_STAT_LAST_AAK) == 0) { + pr_debug("KW: nack on data write (%x): %x\n", + iface->data[-1], ack); + do_stop(iface, -EIO); + } else if (iface->datalen) { + write_reg(reg_data, *(iface->data++)); + iface->datalen--; + } else { + write_reg(reg_control, KW_I2C_CTL_STOP); + iface->state = state_stop; + iface->result = 0; + } + write_reg(reg_isr, KW_I2C_IRQ_DATA); + } else { + write_reg(reg_isr, KW_I2C_IRQ_DATA); + WRONG_STATE("KW_I2C_IRQ_DATA"); + if (iface->state != state_stop) + do_stop(iface, -EIO); } - break; } - - write_reg(reg_isr, isr); - return rearm_timer; + if (isr & KW_I2C_IRQ_STOP) { + write_reg(reg_isr, KW_I2C_IRQ_STOP); + if (iface->state != state_stop) { + WRONG_STATE("KW_I2C_IRQ_STOP"); + iface->result = -EIO; + } + iface->state = state_idle; + write_reg(reg_ier, 0x00); +#ifndef POLLED_MODE + complete(&iface->complete); +#endif /* POLLED_MODE */ + } + + if (isr & KW_I2C_IRQ_START) + write_reg(reg_isr, KW_I2C_IRQ_START); } +#ifndef POLLED_MODE + /* Interrupt handler */ static irqreturn_t keywest_irq(int irq, void *dev_id, struct pt_regs *regs) { struct keywest_iface *iface = (struct keywest_iface *)dev_id; + unsigned long flags; - spin_lock(&iface->lock); + spin_lock_irqsave(&iface->lock, flags); del_timer(&iface->timeout_timer); - if (handle_interrupt(iface, read_reg(reg_isr))) - mod_timer(&iface->timeout_timer, jiffies + POLL_TIMEOUT); - spin_unlock(&iface->lock); + handle_interrupt(iface, read_reg(reg_isr)); + if (iface->state != state_idle) { + iface->timeout_timer.expires = jiffies + POLL_TIMEOUT; + add_timer(&iface->timeout_timer); + } + spin_unlock_irqrestore(&iface->lock, flags); return IRQ_HANDLED; } @@ -200,14 +262,20 @@ static void keywest_timeout(unsigned long data) { struct keywest_iface *iface = (struct keywest_iface *)data; + unsigned long flags; pr_debug("timeout !\n"); - spin_lock_irq(&iface->lock); - if (handle_interrupt(iface, read_reg(reg_isr))) - mod_timer(&iface->timeout_timer, jiffies + POLL_TIMEOUT); - spin_unlock(&iface->lock); + spin_lock_irqsave(&iface->lock, flags); + handle_interrupt(iface, read_reg(reg_isr)); + if (iface->state != state_idle) { + iface->timeout_timer.expires = jiffies + POLL_TIMEOUT; + add_timer(&iface->timeout_timer); + } + spin_unlock_irqrestore(&iface->lock, flags); } +#endif /* POLLED_MODE */ + /* * SMBUS-type transfer entrypoint */ @@ -228,46 +296,54 @@ keywest_smbus_xfer( struct i2c_adapter* adap, int rc = 0; if (iface->state == state_dead) - return -1; + return -ENXIO; /* Prepare datas & select mode */ iface->cur_mode &= ~KW_I2C_MODE_MODE_MASK; switch (size) { - case I2C_SMBUS_QUICK: + case I2C_SMBUS_QUICK: len = 0; buffer = NULL; iface->cur_mode |= KW_I2C_MODE_STANDARD; break; - case I2C_SMBUS_BYTE: + case I2C_SMBUS_BYTE: len = 1; buffer = &data->byte; iface->cur_mode |= KW_I2C_MODE_STANDARD; break; - case I2C_SMBUS_BYTE_DATA: + case I2C_SMBUS_BYTE_DATA: len = 1; buffer = &data->byte; iface->cur_mode |= KW_I2C_MODE_STANDARDSUB; break; - case I2C_SMBUS_WORD_DATA: + case I2C_SMBUS_WORD_DATA: len = 2; cur_word = cpu_to_le16(data->word); buffer = (u8 *)&cur_word; iface->cur_mode |= KW_I2C_MODE_STANDARDSUB; break; - case I2C_SMBUS_BLOCK_DATA: + case I2C_SMBUS_BLOCK_DATA: len = data->block[0]; buffer = &data->block[1]; iface->cur_mode |= KW_I2C_MODE_STANDARDSUB; break; - default: + default: return -1; } + /* Turn a standardsub read into a combined mode access */ + if (read_write == I2C_SMBUS_READ + && (iface->cur_mode & KW_I2C_MODE_MODE_MASK) == KW_I2C_MODE_STANDARDSUB) { + iface->cur_mode &= ~KW_I2C_MODE_MODE_MASK; + iface->cur_mode |= KW_I2C_MODE_COMBINED; + } + /* Original driver had this limitation */ if (len > 32) len = 32; - down(&iface->sem); + if (pmac_low_i2c_lock(iface->node)) + return -ENXIO; pr_debug("chan: %d, addr: 0x%x, transfer len: %d, read: %d\n", chan->chan_no, addr, len, read_write == I2C_SMBUS_READ); @@ -276,12 +352,11 @@ keywest_smbus_xfer( struct i2c_adapter* adap, iface->datalen = len; iface->state = state_addr; iface->result = 0; - iface->stopretry = 0; iface->read_write = read_write; /* Setup channel & clear pending irqs */ - write_reg(reg_mode, iface->cur_mode | (chan->chan_no << 4)); write_reg(reg_isr, read_reg(reg_isr)); + write_reg(reg_mode, iface->cur_mode | (chan->chan_no << 4)); write_reg(reg_status, 0); /* Set up address and r/w bit */ @@ -293,15 +368,31 @@ keywest_smbus_xfer( struct i2c_adapter* adap, || (iface->cur_mode & KW_I2C_MODE_MODE_MASK) == KW_I2C_MODE_COMBINED) write_reg(reg_subaddr, command); +#ifndef POLLED_MODE /* Arm timeout */ - mod_timer(&iface->timeout_timer, jiffies + POLL_TIMEOUT); + iface->timeout_timer.expires = jiffies + POLL_TIMEOUT; + add_timer(&iface->timeout_timer); +#endif /* Start sending address & enable interrupt*/ - write_reg(reg_control, read_reg(reg_control) | KW_I2C_CTL_XADDR); + write_reg(reg_control, KW_I2C_CTL_XADDR); write_reg(reg_ier, KW_I2C_IRQ_MASK); - /* Wait interrupt operations completion */ +#ifdef POLLED_MODE + pr_debug("using polled mode...\n"); + /* State machine, to turn into an interrupt handler */ + while(iface->state != state_idle) { + unsigned long flags; + + u8 isr = wait_interrupt(iface); + spin_lock_irqsave(&iface->lock, flags); + handle_interrupt(iface, isr); + spin_unlock_irqrestore(&iface->lock, flags); + } +#else /* POLLED_MODE */ + pr_debug("using interrupt mode...\n"); wait_for_completion(&iface->complete); +#endif /* POLLED_MODE */ rc = iface->result; pr_debug("transfer done, result: %d\n", rc); @@ -310,7 +401,7 @@ keywest_smbus_xfer( struct i2c_adapter* adap, data->word = le16_to_cpu(cur_word); /* Release sem */ - up(&iface->sem); + pmac_low_i2c_unlock(iface->node); return rc; } @@ -329,7 +420,11 @@ keywest_xfer( struct i2c_adapter *adap, int i, completed; int rc = 0; - down(&iface->sem); + if (iface->state == state_dead) + return -ENXIO; + + if (pmac_low_i2c_lock(iface->node)) + return -ENXIO; /* Set adapter to standard mode */ iface->cur_mode &= ~KW_I2C_MODE_MODE_MASK; @@ -360,7 +455,6 @@ keywest_xfer( struct i2c_adapter *adap, iface->datalen = pmsg->len; iface->state = state_addr; iface->result = 0; - iface->stopretry = 0; if (pmsg->flags & I2C_M_RD) iface->read_write = I2C_SMBUS_READ; else @@ -373,15 +467,27 @@ keywest_xfer( struct i2c_adapter *adap, (addr << 1) | ((iface->read_write == I2C_SMBUS_READ) ? 0x01 : 0x00)); +#ifndef POLLED_MODE /* Arm timeout */ - mod_timer(&iface->timeout_timer, jiffies + POLL_TIMEOUT); + iface->timeout_timer.expires = jiffies + POLL_TIMEOUT; + add_timer(&iface->timeout_timer); +#endif /* Start sending address & enable interrupt*/ - write_reg(reg_control, read_reg(reg_control) | KW_I2C_CTL_XADDR); write_reg(reg_ier, KW_I2C_IRQ_MASK); - - /* Wait interrupt operations completion */ + write_reg(reg_control, KW_I2C_CTL_XADDR); + +#ifdef POLLED_MODE + pr_debug("using polled mode...\n"); + /* State machine, to turn into an interrupt handler */ + while(iface->state != state_idle) { + u8 isr = wait_interrupt(iface); + handle_interrupt(iface, isr); + } +#else /* POLLED_MODE */ + pr_debug("using interrupt mode...\n"); wait_for_completion(&iface->complete); +#endif /* POLLED_MODE */ rc = iface->result; if (rc == 0) @@ -390,7 +496,7 @@ keywest_xfer( struct i2c_adapter *adap, } /* Release sem */ - up(&iface->sem); + pmac_low_i2c_unlock(iface->node); return completed; } @@ -416,19 +522,23 @@ static struct i2c_algorithm keywest_algorithm = { static int create_iface(struct device_node *np, struct device *dev) { - unsigned long steps, *psteps, *prate; + unsigned long steps; unsigned bsteps, tsize, i, nchan, addroffset; struct keywest_iface* iface; + u32 *psteps, *prate; int rc; - psteps = (unsigned long *)get_property(np, "AAPL,address-step", NULL); + if (pmac_low_i2c_lock(np)) + return -ENODEV; + + psteps = (u32 *)get_property(np, "AAPL,address-step", NULL); steps = psteps ? (*psteps) : 0x10; /* Hrm... maybe we can be smarter here */ for (bsteps = 0; (steps & 0x01) == 0; bsteps++) steps >>= 1; - if (!strcmp(np->parent->name, "uni-n")) { + if (np->parent->name[0] == 'u') { nchan = 2; addroffset = 3; } else { @@ -441,12 +551,13 @@ create_iface(struct device_node *np, struct device *dev) iface = (struct keywest_iface *) kmalloc(tsize, GFP_KERNEL); if (iface == NULL) { printk(KERN_ERR "i2c-keywest: can't allocate inteface !\n"); + pmac_low_i2c_unlock(np); return -ENOMEM; } memset(iface, 0, tsize); - init_MUTEX(&iface->sem); spin_lock_init(&iface->lock); init_completion(&iface->complete); + iface->node = of_node_get(np); iface->bsteps = bsteps; iface->chan_count = nchan; iface->state = state_idle; @@ -458,16 +569,19 @@ create_iface(struct device_node *np, struct device *dev) if (iface->base == 0) { printk(KERN_ERR "i2c-keywest: can't map inteface !\n"); kfree(iface); + pmac_low_i2c_unlock(np); return -ENOMEM; } +#ifndef POLLED_MODE init_timer(&iface->timeout_timer); iface->timeout_timer.function = keywest_timeout; iface->timeout_timer.data = (unsigned long)iface; +#endif /* Select interface rate */ iface->cur_mode = KW_I2C_MODE_100KHZ; - prate = (unsigned long *)get_property(np, "AAPL,i2c-rate", NULL); + prate = (u32 *)get_property(np, "AAPL,i2c-rate", NULL); if (prate) switch(*prate) { case 100: iface->cur_mode = KW_I2C_MODE_100KHZ; @@ -480,11 +594,11 @@ create_iface(struct device_node *np, struct device *dev) break; default: printk(KERN_WARNING "i2c-keywest: unknown rate %ldKhz, using 100KHz\n", - *prate); + (long)*prate); } - /* Select standard sub mode */ - iface->cur_mode |= KW_I2C_MODE_STANDARDSUB; + /* Select standard mode by default */ + iface->cur_mode |= KW_I2C_MODE_STANDARD; /* Write mode */ write_reg(reg_mode, iface->cur_mode); @@ -493,14 +607,17 @@ create_iface(struct device_node *np, struct device *dev) write_reg(reg_ier, 0x00); write_reg(reg_isr, KW_I2C_IRQ_MASK); +#ifndef POLLED_MODE /* Request chip interrupt */ - rc = request_irq(iface->irq, keywest_irq, 0, "keywest i2c", iface); + rc = request_irq(iface->irq, keywest_irq, SA_INTERRUPT, "keywest i2c", iface); if (rc) { printk(KERN_ERR "i2c-keywest: can't get IRQ %d !\n", iface->irq); iounmap((void *)iface->base); kfree(iface); + pmac_low_i2c_unlock(np); return -ENODEV; } +#endif /* POLLED_MODE */ dev_set_drvdata(dev, iface); @@ -539,6 +656,7 @@ create_iface(struct device_node *np, struct device *dev) printk(KERN_INFO "Found KeyWest i2c on \"%s\", %d channel%s, stepping: %d bits\n", np->parent->name, nchan, nchan > 1 ? "s" : "", bsteps); + pmac_low_i2c_unlock(np); return 0; } @@ -549,8 +667,10 @@ dispose_iface(struct device *dev) int i, rc; /* Make sure we stop all activity */ - down(&iface->sem); + if (pmac_low_i2c_lock(iface->node)) + return -ENODEV; +#ifndef POLLED_MODE spin_lock_irq(&iface->lock); while (iface->state != state_idle) { spin_unlock_irq(&iface->lock); @@ -558,10 +678,14 @@ dispose_iface(struct device *dev) schedule_timeout(HZ/10); spin_lock_irq(&iface->lock); } +#endif /* POLLED_MODE */ iface->state = state_dead; +#ifndef POLLED_MODE spin_unlock_irq(&iface->lock); free_irq(iface->irq, iface); - up(&iface->sem); +#endif /* POLLED_MODE */ + + pmac_low_i2c_unlock(iface->node); /* Release all channels */ for (i=0; ichan_count; i++) { @@ -576,6 +700,7 @@ dispose_iface(struct device *dev) } iounmap((void *)iface->base); dev_set_drvdata(dev, NULL); + of_node_put(iface->node); kfree(iface); return 0; @@ -634,8 +759,8 @@ static struct of_platform_driver i2c_keywest_of_platform_driver = static int __init i2c_keywest_init(void) { - macio_register_driver(&i2c_keywest_macio_driver); of_register_driver(&i2c_keywest_of_platform_driver); + macio_register_driver(&i2c_keywest_macio_driver); return 0; } @@ -643,8 +768,8 @@ i2c_keywest_init(void) static void __exit i2c_keywest_cleanup(void) { - macio_unregister_driver(&i2c_keywest_macio_driver); of_unregister_driver(&i2c_keywest_of_platform_driver); + macio_unregister_driver(&i2c_keywest_macio_driver); } module_init(i2c_keywest_init); diff --git a/drivers/i2c/busses/i2c-keywest.h b/drivers/i2c/busses/i2c-keywest.h index 8f6e975dc27b..ecc279d5ccae 100644 --- a/drivers/i2c/busses/i2c-keywest.h +++ b/drivers/i2c/busses/i2c-keywest.h @@ -51,20 +51,19 @@ typedef enum { /* Physical interface */ struct keywest_iface { + struct device_node *node; unsigned long base; unsigned bsteps; int irq; - struct semaphore sem; spinlock_t lock; - struct keywest_chan* channels; + struct keywest_chan *channels; unsigned chan_count; u8 cur_mode; char read_write; - u8* data; + u8 *data; unsigned datalen; int state; int result; - int stopretry; struct timer_list timeout_timer; struct completion complete; }; @@ -98,8 +97,7 @@ static inline void __write_reg(struct keywest_iface *iface, reg_t reg, u8 val) { out_8(((volatile u8 *)iface->base) + (((unsigned)reg) << iface->bsteps), val); - (void)__read_reg(iface, reg); - udelay(10); + (void)__read_reg(iface, reg_subaddr); } #define write_reg(reg, val) __write_reg(iface, reg, val) diff --git a/include/asm-ppc/pmac_low_i2c.h b/include/asm-ppc/pmac_low_i2c.h new file mode 100644 index 000000000000..809a5963d5e7 --- /dev/null +++ b/include/asm-ppc/pmac_low_i2c.h @@ -0,0 +1,43 @@ +/* + * include/asm-ppc/pmac_low_i2c.h + * + * Copyright (C) 2003 Ben. Herrenschmidt (benh@kernel.crashing.org) + * + * 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. + * + */ +#ifndef __PMAC_LOW_I2C_H__ +#define __PMAC_LOW_I2C_H__ + +/* i2c mode (based on the platform functions format) */ +enum { + pmac_low_i2c_mode_dumb = 1, + pmac_low_i2c_mode_std = 2, + pmac_low_i2c_mode_stdsub = 3, + pmac_low_i2c_mode_combined = 4, +}; + +/* RW bit in address */ +enum { + pmac_low_i2c_read = 0x01, + pmac_low_i2c_write = 0x00 +}; + +/* Init, called early during boot */ +extern void pmac_init_low_i2c(void); + +/* Locking functions exposed to i2c-keywest */ +int pmac_low_i2c_lock(struct device_node *np); +int pmac_low_i2c_unlock(struct device_node *np); + +/* Access functions for platform code */ +int pmac_low_i2c_open(struct device_node *np, int channel); +int pmac_low_i2c_close(struct device_node *np); +int pmac_low_i2c_setmode(struct device_node *np, int mode); +int pmac_low_i2c_xfer(struct device_node *np, u8 addrdir, u8 subaddr, u8 *data, int len); + + +#endif /* __PMAC_LOW_I2C_H__ */ -- cgit v1.2.3 From 3e74492e6c6cee6a96dadebda156982776ba4d5c Mon Sep 17 00:00:00 2001 From: Benjamin Herrenschmidt Date: Fri, 6 Feb 2004 14:34:35 +1100 Subject: ppc32: PowerMac G5 interrupt management The cascaded northbridge MPIC is currently dealt with a special modified version of open_pic.c (open_pic2.c). A better mecanism will have to be found for 2.7 --- arch/ppc/platforms/pmac_pic.c | 61 +++- arch/ppc/syslib/Makefile | 1 + arch/ppc/syslib/open_pic.c | 17 +- arch/ppc/syslib/open_pic2.c | 716 ++++++++++++++++++++++++++++++++++++++++++ include/asm-ppc/open_pic.h | 27 +- 5 files changed, 809 insertions(+), 13 deletions(-) create mode 100644 arch/ppc/syslib/open_pic2.c (limited to 'include') diff --git a/arch/ppc/platforms/pmac_pic.c b/arch/ppc/platforms/pmac_pic.c index 6ef68ec1d909..901566d3b22d 100644 --- a/arch/ppc/platforms/pmac_pic.c +++ b/arch/ppc/platforms/pmac_pic.c @@ -34,6 +34,7 @@ #include #include #include +#include #include "pmac_pic.h" @@ -363,32 +364,76 @@ static int __init enable_second_ohare(void) return irqctrler->intrs[0].line; } -void __init -pmac_pic_init(void) +#ifdef CONFIG_POWER4 +static irqreturn_t k2u3_action(int cpl, void *dev_id, struct pt_regs *regs) +{ + int irq; + + irq = openpic2_get_irq(regs); + if (irq != -1) + ppc_irq_dispatch_handler(regs, irq); + return IRQ_HANDLED; +} +#endif /* CONFIG_POWER4 */ + +void __init pmac_pic_init(void) { int i; - struct device_node *irqctrler; + struct device_node *irqctrler = NULL; + struct device_node *irqctrler2 = NULL; + struct device_node *np; unsigned long addr; int irq_cascade = -1; /* We first try to detect Apple's new Core99 chipset, since mac-io * is quite different on those machines and contains an IBM MPIC2. */ - irqctrler = find_type_devices("open-pic"); + np = find_type_devices("open-pic"); + while(np) { + if (np->parent && !strcmp(np->parent->name, "u3")) + irqctrler2 = np; + else + irqctrler = np; + np = np->next; + } if (irqctrler != NULL) { - printk("PowerMac using OpenPIC irq controller\n"); if (irqctrler->n_addrs > 0) { - unsigned char senses[NR_IRQS]; + unsigned char senses[128]; + + printk(KERN_INFO "PowerMac using OpenPIC irq controller at 0x%08x\n", + irqctrler->addrs[0].address); - prom_get_irq_senses(senses, 0, NR_IRQS); + prom_get_irq_senses(senses, 0, 128); OpenPIC_InitSenses = senses; - OpenPIC_NumInitSenses = NR_IRQS; + OpenPIC_NumInitSenses = 128; ppc_md.get_irq = openpic_get_irq; + pmac_call_feature(PMAC_FTR_ENABLE_MPIC, irqctrler, 0, 0); OpenPIC_Addr = ioremap(irqctrler->addrs[0].address, irqctrler->addrs[0].size); openpic_init(0); + +#ifdef CONFIG_POWER4 + if (irqctrler2 != NULL && irqctrler2->n_intrs > 0 && + irqctrler2->n_addrs > 0) { + printk(KERN_INFO "Slave OpenPIC at 0x%08x hooked on IRQ %d\n", + irqctrler2->addrs[0].address, + irqctrler2->intrs[0].line); + pmac_call_feature(PMAC_FTR_ENABLE_MPIC, irqctrler2, 0, 0); + OpenPIC2_Addr = ioremap(irqctrler2->addrs[0].address, + irqctrler2->addrs[0].size); + prom_get_irq_senses(senses, PMAC_OPENPIC2_OFFSET, + PMAC_OPENPIC2_OFFSET+128); + OpenPIC_InitSenses = senses; + OpenPIC_NumInitSenses = 128; + openpic2_init(PMAC_OPENPIC2_OFFSET); + if (request_irq(irqctrler2->intrs[0].line, k2u3_action, 0, + "U3->K2 Cascade", NULL)) + printk("Unable to get OpenPIC IRQ for cascade\n"); + } +#endif /* CONFIG_POWER4 */ + #ifdef CONFIG_XMON { struct device_node* pswitch; diff --git a/arch/ppc/syslib/Makefile b/arch/ppc/syslib/Makefile index 9c5faac97350..b7ebf316b533 100644 --- a/arch/ppc/syslib/Makefile +++ b/arch/ppc/syslib/Makefile @@ -31,6 +31,7 @@ obj-$(CONFIG_PCI) += qspan_pci.o i8259.o endif obj-$(CONFIG_PPC_OF) += prom_init.o prom.o of_device.o obj-$(CONFIG_PPC_PMAC) += open_pic.o indirect_pci.o +obj-$(CONFIG_POWER4) += open_pic2.o obj-$(CONFIG_PPC_CHRP) += open_pic.o indirect_pci.o i8259.o obj-$(CONFIG_PPC_PREP) += open_pic.o indirect_pci.o i8259.o obj-$(CONFIG_ADIR) += i8259.o indirect_pci.o pci_auto.o \ diff --git a/arch/ppc/syslib/open_pic.c b/arch/ppc/syslib/open_pic.c index e1dcfea8ba02..2a922f21a449 100644 --- a/arch/ppc/syslib/open_pic.c +++ b/arch/ppc/syslib/open_pic.c @@ -610,12 +610,15 @@ void openpic_request_IPIs(void) void __devinit do_openpic_setup_cpu(void) { +#ifdef CONFIG_IRQ_ALL_CPUS int i; - u32 msk = 1 << smp_hw_index[smp_processor_id()]; - + u32 msk; +#endif spin_lock(&openpic_setup_lock); #ifdef CONFIG_IRQ_ALL_CPUS + msk = 1 << smp_hw_index[smp_processor_id()]; + /* let the openpic know we want intrs. default affinity * is 0xffffffff until changed via /proc * That's how it's done on x86. If we want it differently, then @@ -788,15 +791,25 @@ static void openpic_set_sense(u_int irq, int sense) */ static void openpic_ack_irq(unsigned int irq_nr) { +#ifdef __SLOW_VERSION__ openpic_disable_irq(irq_nr); openpic_eoi(); +#else + if ((irq_desc[irq_nr].status & IRQ_LEVEL) == 0) + openpic_eoi(); +#endif } static void openpic_end_irq(unsigned int irq_nr) { +#ifdef __SLOW_VERSION__ if (!(irq_desc[irq_nr].status & (IRQ_DISABLED|IRQ_INPROGRESS)) && irq_desc[irq_nr].action) openpic_enable_irq(irq_nr); +#else + if ((irq_desc[irq_nr].status & IRQ_LEVEL) != 0) + openpic_eoi(); +#endif } static void openpic_set_affinity(unsigned int irq_nr, unsigned long cpumask) diff --git a/arch/ppc/syslib/open_pic2.c b/arch/ppc/syslib/open_pic2.c new file mode 100644 index 000000000000..96ebd2389c9d --- /dev/null +++ b/arch/ppc/syslib/open_pic2.c @@ -0,0 +1,716 @@ +/* + * arch/ppc/kernel/open_pic.c -- OpenPIC Interrupt Handling + * + * Copyright (C) 1997 Geert Uytterhoeven + * + * This file is subject to the terms and conditions of the GNU General Public + * License. See the file COPYING in the main directory of this archive + * for more details. + * + * This is a duplicate of open_pic.c that deals with U3s MPIC on + * G5 PowerMacs. It's the same file except it's using big endian + * register accesses + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "open_pic_defs.h" + +void *OpenPIC2_Addr; +static volatile struct OpenPIC *OpenPIC2 = NULL; +/* + * We define OpenPIC_InitSenses table thusly: + * bit 0x1: sense, 0 for edge and 1 for level. + * bit 0x2: polarity, 0 for negative, 1 for positive. + */ +extern u_int OpenPIC_NumInitSenses; +extern u_char *OpenPIC_InitSenses; +extern int use_of_interrupt_tree; + +static u_int NumProcessors; +static u_int NumSources; +static int open_pic2_irq_offset; +static volatile OpenPIC_Source *ISR[NR_IRQS]; + +/* Global Operations */ +static void openpic2_disable_8259_pass_through(void); +static void openpic2_set_priority(u_int pri); +static void openpic2_set_spurious(u_int vector); + +/* Timer Interrupts */ +static void openpic2_inittimer(u_int timer, u_int pri, u_int vector); +static void openpic2_maptimer(u_int timer, u_int cpumask); + +/* Interrupt Sources */ +static void openpic2_enable_irq(u_int irq); +static void openpic2_disable_irq(u_int irq); +static void openpic2_initirq(u_int irq, u_int pri, u_int vector, int polarity, + int is_level); +static void openpic2_mapirq(u_int irq, u_int cpumask, u_int keepmask); + +/* + * These functions are not used but the code is kept here + * for completeness and future reference. + */ +static void openpic2_reset(void); +#ifdef notused +static void openpic2_enable_8259_pass_through(void); +static u_int openpic2_get_priority(void); +static u_int openpic2_get_spurious(void); +static void openpic2_set_sense(u_int irq, int sense); +#endif /* notused */ + +/* + * Description of the openpic for the higher-level irq code + */ +static void openpic2_end_irq(unsigned int irq_nr); +static void openpic2_ack_irq(unsigned int irq_nr); + +struct hw_interrupt_type open_pic2 = { + " OpenPIC2 ", + NULL, + NULL, + openpic2_enable_irq, + openpic2_disable_irq, + openpic2_ack_irq, + openpic2_end_irq, +}; + +/* + * Accesses to the current processor's openpic registers + * On cascaded controller, this is only CPU 0 + */ +#define THIS_CPU Processor[0] +#define DECL_THIS_CPU +#define CHECK_THIS_CPU + +#if 1 +#define check_arg_ipi(ipi) \ + if (ipi < 0 || ipi >= OPENPIC_NUM_IPI) \ + printk("open_pic.c:%d: illegal ipi %d\n", __LINE__, ipi); +#define check_arg_timer(timer) \ + if (timer < 0 || timer >= OPENPIC_NUM_TIMERS) \ + printk("open_pic.c:%d: illegal timer %d\n", __LINE__, timer); +#define check_arg_vec(vec) \ + if (vec < 0 || vec >= OPENPIC_NUM_VECTORS) \ + printk("open_pic.c:%d: illegal vector %d\n", __LINE__, vec); +#define check_arg_pri(pri) \ + if (pri < 0 || pri >= OPENPIC_NUM_PRI) \ + printk("open_pic.c:%d: illegal priority %d\n", __LINE__, pri); +/* + * Print out a backtrace if it's out of range, since if it's larger than NR_IRQ's + * data has probably been corrupted and we're going to panic or deadlock later + * anyway --Troy + */ +extern unsigned long* _get_SP(void); +#define check_arg_irq(irq) \ + if (irq < open_pic2_irq_offset || irq >= NumSources+open_pic2_irq_offset \ + || ISR[irq - open_pic2_irq_offset] == 0) { \ + printk("open_pic.c:%d: illegal irq %d\n", __LINE__, irq); \ + /*print_backtrace(_get_SP());*/ } +#define check_arg_cpu(cpu) \ + if (cpu < 0 || cpu >= NumProcessors){ \ + printk("open_pic2.c:%d: illegal cpu %d\n", __LINE__, cpu); \ + /*print_backtrace(_get_SP());*/ } +#else +#define check_arg_ipi(ipi) do {} while (0) +#define check_arg_timer(timer) do {} while (0) +#define check_arg_vec(vec) do {} while (0) +#define check_arg_pri(pri) do {} while (0) +#define check_arg_irq(irq) do {} while (0) +#define check_arg_cpu(cpu) do {} while (0) +#endif + +static u_int openpic2_read(volatile u_int *addr) +{ + u_int val; + + val = in_be32(addr); + return val; +} + +static inline void openpic2_write(volatile u_int *addr, u_int val) +{ + out_be32(addr, val); +} + +static inline u_int openpic2_readfield(volatile u_int *addr, u_int mask) +{ + u_int val = openpic2_read(addr); + return val & mask; +} + +inline void openpic2_writefield(volatile u_int *addr, u_int mask, + u_int field) +{ + u_int val = openpic2_read(addr); + openpic2_write(addr, (val & ~mask) | (field & mask)); +} + +static inline void openpic2_clearfield(volatile u_int *addr, u_int mask) +{ + openpic2_writefield(addr, mask, 0); +} + +static inline void openpic2_setfield(volatile u_int *addr, u_int mask) +{ + openpic2_writefield(addr, mask, mask); +} + +static void openpic2_safe_writefield(volatile u_int *addr, u_int mask, + u_int field) +{ + openpic2_setfield(addr, OPENPIC_MASK); + while (openpic2_read(addr) & OPENPIC_ACTIVITY); + openpic2_writefield(addr, mask | OPENPIC_MASK, field | OPENPIC_MASK); +} + +static void openpic2_reset(void) +{ + openpic2_setfield(&OpenPIC2->Global.Global_Configuration0, + OPENPIC_CONFIG_RESET); + while (openpic2_readfield(&OpenPIC2->Global.Global_Configuration0, + OPENPIC_CONFIG_RESET)) + mb(); +} + +void __init openpic2_set_sources(int first_irq, int num_irqs, void *first_ISR) +{ + volatile OpenPIC_Source *src = first_ISR; + int i, last_irq; + + last_irq = first_irq + num_irqs; + if (last_irq > NumSources) + NumSources = last_irq; + if (src == 0) + src = &((struct OpenPIC *)OpenPIC2_Addr)->Source[first_irq]; + for (i = first_irq; i < last_irq; ++i, ++src) + ISR[i] = src; +} + +/* + * The `offset' parameter defines where the interrupts handled by the + * OpenPIC start in the space of interrupt numbers that the kernel knows + * about. In other words, the OpenPIC's IRQ0 is numbered `offset' in the + * kernel's interrupt numbering scheme. + * We assume there is only one OpenPIC. + */ +void __init openpic2_init(int offset) +{ + u_int t, i; + u_int timerfreq; + const char *version; + + if (!OpenPIC2_Addr) { + printk("No OpenPIC2 found !\n"); + return; + } + OpenPIC2 = (volatile struct OpenPIC *)OpenPIC2_Addr; + + if (ppc_md.progress) ppc_md.progress("openpic: enter", 0x122); + + t = openpic2_read(&OpenPIC2->Global.Feature_Reporting0); + switch (t & OPENPIC_FEATURE_VERSION_MASK) { + case 1: + version = "1.0"; + break; + case 2: + version = "1.2"; + break; + case 3: + version = "1.3"; + break; + default: + version = "?"; + break; + } + NumProcessors = ((t & OPENPIC_FEATURE_LAST_PROCESSOR_MASK) >> + OPENPIC_FEATURE_LAST_PROCESSOR_SHIFT) + 1; + if (NumSources == 0) + openpic2_set_sources(0, + ((t & OPENPIC_FEATURE_LAST_SOURCE_MASK) >> + OPENPIC_FEATURE_LAST_SOURCE_SHIFT) + 1, + NULL); + printk("OpenPIC (2) Version %s (%d CPUs and %d IRQ sources) at %p\n", + version, NumProcessors, NumSources, OpenPIC2); + timerfreq = openpic2_read(&OpenPIC2->Global.Timer_Frequency); + if (timerfreq) + printk("OpenPIC timer frequency is %d.%06d MHz\n", + timerfreq / 1000000, timerfreq % 1000000); + + open_pic2_irq_offset = offset; + + /* Initialize timer interrupts */ + if ( ppc_md.progress ) ppc_md.progress("openpic2: timer",0x3ba); + for (i = 0; i < OPENPIC_NUM_TIMERS; i++) { + /* Disabled, Priority 0 */ + openpic2_inittimer(i, 0, OPENPIC2_VEC_TIMER+i+offset); + /* No processor */ + openpic2_maptimer(i, 0); + } + + /* Initialize external interrupts */ + if (ppc_md.progress) ppc_md.progress("openpic2: external",0x3bc); + + openpic2_set_priority(0xf); + + /* Init all external sources, including possibly the cascade. */ + for (i = 0; i < NumSources; i++) { + int sense; + + if (ISR[i] == 0) + continue; + + /* the bootloader may have left it enabled (bad !) */ + openpic2_disable_irq(i+offset); + + sense = (i < OpenPIC_NumInitSenses)? OpenPIC_InitSenses[i]: \ + (IRQ_SENSE_LEVEL | IRQ_POLARITY_NEGATIVE); + + if (sense & IRQ_SENSE_MASK) + irq_desc[i+offset].status = IRQ_LEVEL; + + /* Enabled, Priority 8 */ + openpic2_initirq(i, 8, i+offset, (sense & IRQ_POLARITY_MASK), + (sense & IRQ_SENSE_MASK)); + /* Processor 0 */ + openpic2_mapirq(i, 1<<0, 0); + } + + /* Init descriptors */ + for (i = offset; i < NumSources + offset; i++) + irq_desc[i].handler = &open_pic2; + + /* Initialize the spurious interrupt */ + if (ppc_md.progress) ppc_md.progress("openpic2: spurious",0x3bd); + openpic2_set_spurious(OPENPIC2_VEC_SPURIOUS+offset); + + openpic2_disable_8259_pass_through(); + openpic2_set_priority(0); + + if (ppc_md.progress) ppc_md.progress("openpic2: exit",0x222); +} + +#ifdef notused +static void openpic2_enable_8259_pass_through(void) +{ + openpic2_clearfield(&OpenPIC2->Global.Global_Configuration0, + OPENPIC_CONFIG_8259_PASSTHROUGH_DISABLE); +} +#endif /* notused */ + +/* This can't be __init, it is used in openpic_sleep_restore_intrs */ +static void openpic2_disable_8259_pass_through(void) +{ + openpic2_setfield(&OpenPIC2->Global.Global_Configuration0, + OPENPIC_CONFIG_8259_PASSTHROUGH_DISABLE); +} + +/* + * Find out the current interrupt + */ +u_int openpic2_irq(void) +{ + u_int vec; + DECL_THIS_CPU; + + CHECK_THIS_CPU; + vec = openpic2_readfield(&OpenPIC2->THIS_CPU.Interrupt_Acknowledge, + OPENPIC_VECTOR_MASK); + return vec; +} + +void openpic2_eoi(void) +{ + DECL_THIS_CPU; + + CHECK_THIS_CPU; + openpic2_write(&OpenPIC2->THIS_CPU.EOI, 0); + /* Handle PCI write posting */ + (void)openpic2_read(&OpenPIC2->THIS_CPU.EOI); +} + +#ifdef notused +static u_int openpic2_get_priority(void) +{ + DECL_THIS_CPU; + + CHECK_THIS_CPU; + return openpic2_readfield(&OpenPIC2->THIS_CPU.Current_Task_Priority, + OPENPIC_CURRENT_TASK_PRIORITY_MASK); +} +#endif /* notused */ + +static void __init openpic2_set_priority(u_int pri) +{ + DECL_THIS_CPU; + + CHECK_THIS_CPU; + check_arg_pri(pri); + openpic2_writefield(&OpenPIC2->THIS_CPU.Current_Task_Priority, + OPENPIC_CURRENT_TASK_PRIORITY_MASK, pri); +} + +/* + * Get/set the spurious vector + */ +#ifdef notused +static u_int openpic2_get_spurious(void) +{ + return openpic2_readfield(&OpenPIC2->Global.Spurious_Vector, + OPENPIC_VECTOR_MASK); +} +#endif /* notused */ + +/* This can't be __init, it is used in openpic_sleep_restore_intrs */ +static void openpic2_set_spurious(u_int vec) +{ + check_arg_vec(vec); + openpic2_writefield(&OpenPIC2->Global.Spurious_Vector, OPENPIC_VECTOR_MASK, + vec); +} + +static spinlock_t openpic2_setup_lock = SPIN_LOCK_UNLOCKED; + +/* + * Initialize a timer interrupt (and disable it) + * + * timer: OpenPIC timer number + * pri: interrupt source priority + * vec: the vector it will produce + */ +static void __init openpic2_inittimer(u_int timer, u_int pri, u_int vec) +{ + check_arg_timer(timer); + check_arg_pri(pri); + check_arg_vec(vec); + openpic2_safe_writefield(&OpenPIC2->Global.Timer[timer].Vector_Priority, + OPENPIC_PRIORITY_MASK | OPENPIC_VECTOR_MASK, + (pri << OPENPIC_PRIORITY_SHIFT) | vec); +} + +/* + * Map a timer interrupt to one or more CPUs + */ +static void __init openpic2_maptimer(u_int timer, u_int cpumask) +{ + check_arg_timer(timer); + openpic2_write(&OpenPIC2->Global.Timer[timer].Destination, + cpumask); +} + +/* + * Initalize the interrupt source which will generate an NMI. + * This raises the interrupt's priority from 8 to 9. + * + * irq: The logical IRQ which generates an NMI. + */ +void __init +openpic2_init_nmi_irq(u_int irq) +{ + check_arg_irq(irq); + openpic2_safe_writefield(&ISR[irq - open_pic2_irq_offset]->Vector_Priority, + OPENPIC_PRIORITY_MASK, + 9 << OPENPIC_PRIORITY_SHIFT); +} + +/* + * + * All functions below take an offset'ed irq argument + * + */ + + +/* + * Enable/disable an external interrupt source + * + * Externally called, irq is an offseted system-wide interrupt number + */ +static void openpic2_enable_irq(u_int irq) +{ + volatile u_int *vpp; + + check_arg_irq(irq); + vpp = &ISR[irq - open_pic2_irq_offset]->Vector_Priority; + openpic2_clearfield(vpp, OPENPIC_MASK); + /* make sure mask gets to controller before we return to user */ + do { + mb(); /* sync is probably useless here */ + } while (openpic2_readfield(vpp, OPENPIC_MASK)); +} + +static void openpic2_disable_irq(u_int irq) +{ + volatile u_int *vpp; + u32 vp; + + check_arg_irq(irq); + vpp = &ISR[irq - open_pic2_irq_offset]->Vector_Priority; + openpic2_setfield(vpp, OPENPIC_MASK); + /* make sure mask gets to controller before we return to user */ + do { + mb(); /* sync is probably useless here */ + vp = openpic2_readfield(vpp, OPENPIC_MASK | OPENPIC_ACTIVITY); + } while((vp & OPENPIC_ACTIVITY) && !(vp & OPENPIC_MASK)); +} + + +/* + * Initialize an interrupt source (and disable it!) + * + * irq: OpenPIC interrupt number + * pri: interrupt source priority + * vec: the vector it will produce + * pol: polarity (1 for positive, 0 for negative) + * sense: 1 for level, 0 for edge + */ +static void __init +openpic2_initirq(u_int irq, u_int pri, u_int vec, int pol, int sense) +{ + openpic2_safe_writefield(&ISR[irq]->Vector_Priority, + OPENPIC_PRIORITY_MASK | OPENPIC_VECTOR_MASK | + OPENPIC_SENSE_MASK | OPENPIC_POLARITY_MASK, + (pri << OPENPIC_PRIORITY_SHIFT) | vec | + (pol ? OPENPIC_POLARITY_POSITIVE : + OPENPIC_POLARITY_NEGATIVE) | + (sense ? OPENPIC_SENSE_LEVEL : OPENPIC_SENSE_EDGE)); +} + +/* + * Map an interrupt source to one or more CPUs + */ +static void openpic2_mapirq(u_int irq, u_int physmask, u_int keepmask) +{ + if (ISR[irq] == 0) + return; + if (keepmask != 0) + physmask |= openpic2_read(&ISR[irq]->Destination) & keepmask; + openpic2_write(&ISR[irq]->Destination, physmask); +} + +#ifdef notused +/* + * Set the sense for an interrupt source (and disable it!) + * + * sense: 1 for level, 0 for edge + */ +static void openpic2_set_sense(u_int irq, int sense) +{ + if (ISR[irq] != 0) + openpic2_safe_writefield(&ISR[irq]->Vector_Priority, + OPENPIC_SENSE_LEVEL, + (sense ? OPENPIC_SENSE_LEVEL : 0)); +} +#endif /* notused */ + +/* No spinlocks, should not be necessary with the OpenPIC + * (1 register = 1 interrupt and we have the desc lock). + */ +static void openpic2_ack_irq(unsigned int irq_nr) +{ + openpic2_disable_irq(irq_nr); + openpic2_eoi(); +} + +static void openpic2_end_irq(unsigned int irq_nr) +{ + if (!(irq_desc[irq_nr].status & (IRQ_DISABLED|IRQ_INPROGRESS))) + openpic2_enable_irq(irq_nr); +} + +int +openpic2_get_irq(struct pt_regs *regs) +{ + int irq = openpic2_irq(); + + if (irq == (OPENPIC2_VEC_SPURIOUS + open_pic2_irq_offset)) + irq = -1; + return irq; +} + +#ifdef CONFIG_PM + +/* + * We implement the IRQ controller as a sysdev and put it + * to sleep at powerdown stage (the callback is named suspend, + * but it's old semantics, for the Device Model, it's really + * powerdown). The possible problem is that another sysdev that + * happens to be suspend after this one will have interrupts off, + * that may be an issue... For now, this isn't an issue on pmac + * though... + */ + +static u32 save_ipi_vp[OPENPIC_NUM_IPI]; +static u32 save_irq_src_vp[OPENPIC_MAX_SOURCES]; +static u32 save_irq_src_dest[OPENPIC_MAX_SOURCES]; +static u32 save_cpu_task_pri[OPENPIC_MAX_PROCESSORS]; +static int openpic_suspend_count; + +static void openpic2_cached_enable_irq(u_int irq) +{ + check_arg_irq(irq); + save_irq_src_vp[irq - open_pic2_irq_offset] &= ~OPENPIC_MASK; +} + +static void openpic2_cached_disable_irq(u_int irq) +{ + check_arg_irq(irq); + save_irq_src_vp[irq - open_pic2_irq_offset] |= OPENPIC_MASK; +} + +/* WARNING: Can be called directly by the cpufreq code with NULL parameter, + * we need something better to deal with that... Maybe switch to S1 for + * cpufreq changes + */ +int openpic2_suspend(struct sys_device *sysdev, u32 state) +{ + int i; + unsigned long flags; + + spin_lock_irqsave(&openpic2_setup_lock, flags); + + if (openpic_suspend_count++ > 0) { + spin_unlock_irqrestore(&openpic2_setup_lock, flags); + return 0; + } + + open_pic2.enable = openpic2_cached_enable_irq; + open_pic2.disable = openpic2_cached_disable_irq; + + for (i=0; iProcessor[i].Current_Task_Priority); + openpic2_writefield(&OpenPIC2->Processor[i].Current_Task_Priority, + OPENPIC_CURRENT_TASK_PRIORITY_MASK, 0xf); + } + + for (i=0; iGlobal.IPI_Vector_Priority(i)); + for (i=0; iVector_Priority) & ~OPENPIC_ACTIVITY; + save_irq_src_dest[i] = openpic2_read(&ISR[i]->Destination); + } + + spin_unlock_irqrestore(&openpic2_setup_lock, flags); + + return 0; +} + +/* WARNING: Can be called directly by the cpufreq code with NULL parameter, + * we need something better to deal with that... Maybe switch to S1 for + * cpufreq changes + */ +int openpic2_resume(struct sys_device *sysdev) +{ + int i; + unsigned long flags; + u32 vppmask = OPENPIC_PRIORITY_MASK | OPENPIC_VECTOR_MASK | + OPENPIC_SENSE_MASK | OPENPIC_POLARITY_MASK | + OPENPIC_MASK; + + spin_lock_irqsave(&openpic2_setup_lock, flags); + + if ((--openpic_suspend_count) > 0) { + spin_unlock_irqrestore(&openpic2_setup_lock, flags); + return 0; + } + + openpic2_reset(); + + /* OpenPIC sometimes seem to need some time to be fully back up... */ + do { + openpic2_set_spurious(OPENPIC2_VEC_SPURIOUS+open_pic2_irq_offset); + } while(openpic2_readfield(&OpenPIC2->Global.Spurious_Vector, OPENPIC_VECTOR_MASK) + != (OPENPIC2_VEC_SPURIOUS + open_pic2_irq_offset)); + + openpic2_disable_8259_pass_through(); + + for (i=0; iGlobal.IPI_Vector_Priority(i), + save_ipi_vp[i]); + for (i=0; iDestination, save_irq_src_dest[i]); + openpic2_write(&ISR[i]->Vector_Priority, save_irq_src_vp[i]); + /* make sure mask gets to controller before we return to user */ + do { + openpic2_write(&ISR[i]->Vector_Priority, save_irq_src_vp[i]); + } while (openpic2_readfield(&ISR[i]->Vector_Priority, vppmask) + != (save_irq_src_vp[i] & vppmask)); + } + for (i=0; iProcessor[i].Current_Task_Priority, + save_cpu_task_pri[i]); + + open_pic2.enable = openpic2_enable_irq; + open_pic2.disable = openpic2_disable_irq; + + spin_unlock_irqrestore(&openpic2_setup_lock, flags); + + return 0; +} + +#endif /* CONFIG_PM */ + +/* HACK ALERT */ +static struct sysdev_class openpic2_sysclass = { + set_kset_name("openpic2"), +}; + +static struct sys_device device_openpic2 = { + .id = 0, + .cls = &openpic2_sysclass, +}; + +static struct sysdev_driver driver_openpic2 = { +#ifdef CONFIG_PM + .suspend = &openpic2_suspend, + .resume = &openpic2_resume, +#endif /* CONFIG_PM */ +}; + +static int __init init_openpic2_sysfs(void) +{ + int rc; + + if (!OpenPIC2_Addr) + return -ENODEV; + printk(KERN_DEBUG "Registering openpic2 with sysfs...\n"); + rc = sysdev_class_register(&openpic2_sysclass); + if (rc) { + printk(KERN_ERR "Failed registering openpic sys class\n"); + return -ENODEV; + } + rc = sys_device_register(&device_openpic2); + if (rc) { + printk(KERN_ERR "Failed registering openpic sys device\n"); + return -ENODEV; + } + rc = sysdev_driver_register(&openpic2_sysclass, &driver_openpic2); + if (rc) { + printk(KERN_ERR "Failed registering openpic sys driver\n"); + return -ENODEV; + } + return 0; +} + +subsys_initcall(init_openpic2_sysfs); + diff --git a/include/asm-ppc/open_pic.h b/include/asm-ppc/open_pic.h index 1986cddd13e4..094f45e9be6c 100644 --- a/include/asm-ppc/open_pic.h +++ b/include/asm-ppc/open_pic.h @@ -21,8 +21,8 @@ * Non-offset'ed vector numbers */ -#define OPENPIC_VEC_TIMER 64 /* and up */ -#define OPENPIC_VEC_IPI 72 /* and up */ +#define OPENPIC_VEC_TIMER 110 /* and up */ +#define OPENPIC_VEC_IPI 118 /* and up */ #define OPENPIC_VEC_SPURIOUS 127 /* OpenPIC IRQ controller structure */ @@ -51,6 +51,7 @@ extern void openpic_setup_ISU(int isu_num, unsigned long addr); extern void openpic_cause_IPI(u_int ipi, u_int cpumask); extern void smp_openpic_message_pass(int target, int msg, unsigned long data, int wait); +extern void openpic_set_k2_cascade(int irq); extern inline int openpic_to_irq(int irq) { @@ -64,5 +65,25 @@ extern inline int openpic_to_irq(int irq) return 0; } } -/*extern int open_pic_irq_offset;*/ +/* Support for second openpic on G5 macs */ + +// FIXME: To be replaced by sane cascaded controller management */ + +#define PMAC_OPENPIC2_OFFSET 128 + +#define OPENPIC2_VEC_TIMER 110 /* and up */ +#define OPENPIC2_VEC_IPI 118 /* and up */ +#define OPENPIC2_VEC_SPURIOUS 127 + + +extern void* OpenPIC2_Addr; + +/* Exported functions */ +extern void openpic2_set_sources(int first_irq, int num_irqs, void *isr); +extern void openpic2_init(int linux_irq_offset); +extern void openpic2_init_nmi_irq(u_int irq); +extern u_int openpic2_irq(void); +extern void openpic2_eoi(void); +extern int openpic2_get_irq(struct pt_regs *regs); +extern void openpic2_setup_ISU(int isu_num, unsigned long addr); #endif /* _PPC_KERNEL_OPEN_PIC_H */ -- cgit v1.2.3 From 2767017a466a8c511a3f05994acbe1faeecc74ad Mon Sep 17 00:00:00 2001 From: Benjamin Herrenschmidt Date: Fri, 6 Feb 2004 14:36:42 +1100 Subject: ppc32: Fix a warning with some usages of udelay --- include/asm-ppc/delay.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'include') diff --git a/include/asm-ppc/delay.h b/include/asm-ppc/delay.h index dc3715bfeff8..badde6845af2 100644 --- a/include/asm-ppc/delay.h +++ b/include/asm-ppc/delay.h @@ -30,8 +30,8 @@ extern void __delay(unsigned int loops); * (which corresponds to ~3800 bogomips at HZ = 100). * -- paulus */ -#define __MAX_UDELAY (226050910/HZ) /* maximum udelay argument */ -#define __MAX_NDELAY (2147483647/HZ) /* maximum ndelay argument */ +#define __MAX_UDELAY (226050910UL/HZ) /* maximum udelay argument */ +#define __MAX_NDELAY (4294967295UL/HZ) /* maximum ndelay argument */ extern __inline__ void __udelay(unsigned int x) { -- cgit v1.2.3 From f3f367c3d2bd3932bbff41d98c7a616a1eb2294e Mon Sep 17 00:00:00 2001 From: Benjamin Herrenschmidt Date: Fri, 6 Feb 2004 14:37:52 +1100 Subject: ppc32: Add some PowerMac specific PCI IDs --- include/linux/pci_ids.h | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) (limited to 'include') diff --git a/include/linux/pci_ids.h b/include/linux/pci_ids.h index ad515074fc85..8637b5f81670 100644 --- a/include/linux/pci_ids.h +++ b/include/linux/pci_ids.h @@ -805,9 +805,12 @@ #define PCI_DEVICE_ID_APPLE_UNI_N_AGP15 0x002d #define PCI_DEVICE_ID_APPLE_UNI_N_FW2 0x0030 #define PCI_DEVICE_ID_APPLE_UNI_N_GMAC2 0x0032 +#define PCI_DEVIEC_ID_APPLE_UNI_N_ATA 0x0033 #define PCI_DEVICE_ID_APPLE_UNI_N_AGP2 0x0034 -#define PCI_DEVICE_ID_APPLE_KAUAI_ATA 0x003b +#define PCI_DEVICE_ID_APPLE_IPID_ATA100 0x003b #define PCI_DEVICE_ID_APPLE_KEYLARGO_I 0x003e +#define PCI_DEVICE_ID_APPLE_K2_ATA100 0x0043 +#define PCI_DEVICE_ID_APPLE_K2_GMAC 0x004c #define PCI_DEVICE_ID_APPLE_TIGON3 0x1645 #define PCI_VENDOR_ID_YAMAHA 0x1073 -- cgit v1.2.3 From 3d8e5084980a5f9ca9289c88747a8ceb2fb84857 Mon Sep 17 00:00:00 2001 From: Benjamin Herrenschmidt Date: Fri, 6 Feb 2004 14:38:55 +1100 Subject: ppc32: Update macio_asic, add some resource management --- drivers/macintosh/macio_asic.c | 281 ++++++++++++++++++++++++++++++++++++----- include/asm-ppc/macio.h | 71 ++++++++++- 2 files changed, 318 insertions(+), 34 deletions(-) (limited to 'include') diff --git a/drivers/macintosh/macio_asic.c b/drivers/macintosh/macio_asic.c index c8d7d791aaec..a35d44c77f30 100644 --- a/drivers/macintosh/macio_asic.c +++ b/drivers/macintosh/macio_asic.c @@ -23,10 +23,13 @@ #include #include +#define DEBUG + +#define MAX_NODE_NAME_SIZE (BUS_ID_SIZE - 12) + static struct macio_chip *macio_on_hold; -static int -macio_bus_match(struct device *dev, struct device_driver *drv) +static int macio_bus_match(struct device *dev, struct device_driver *drv) { struct macio_dev * macio_dev = to_macio_device(dev); struct macio_driver * macio_drv = to_macio_driver(drv); @@ -85,41 +88,42 @@ static int macio_device_probe(struct device *dev) static int macio_device_remove(struct device *dev) { struct macio_dev * macio_dev = to_macio_device(dev); - struct macio_driver * drv = to_macio_driver(macio_dev->ofdev.dev.driver); + struct macio_driver * drv = to_macio_driver(dev->driver); - if (drv && drv->remove) + if (dev->driver && drv->remove) drv->remove(macio_dev); macio_dev_put(macio_dev); return 0; } +static void macio_device_shutdown(struct device *dev) +{ + struct macio_dev * macio_dev = to_macio_device(dev); + struct macio_driver * drv = to_macio_driver(dev->driver); + + if (dev->driver && drv->shutdown) + drv->shutdown(macio_dev); +} + static int macio_device_suspend(struct device *dev, u32 state) { struct macio_dev * macio_dev = to_macio_device(dev); - struct macio_driver * drv; - int error = 0; + struct macio_driver * drv = to_macio_driver(dev->driver); - if (macio_dev->ofdev.dev.driver == NULL) - return 0; - drv = to_macio_driver(macio_dev->ofdev.dev.driver); - if (drv->suspend) - error = drv->suspend(macio_dev, state); - return error; + if (dev->driver && drv->suspend) + return drv->suspend(macio_dev, state); + return 0; } static int macio_device_resume(struct device * dev) { struct macio_dev * macio_dev = to_macio_device(dev); - struct macio_driver * drv; - int error = 0; + struct macio_driver * drv = to_macio_driver(dev->driver); - if (macio_dev->ofdev.dev.driver == NULL) - return 0; - drv = to_macio_driver(macio_dev->ofdev.dev.driver); - if (drv->resume) - error = drv->resume(macio_dev); - return error; + if (dev->driver && drv->resume) + return drv->resume(macio_dev); + return 0; } struct bus_type macio_bus_type = { @@ -129,8 +133,7 @@ struct bus_type macio_bus_type = { .resume = macio_device_resume, }; -static int __init -macio_bus_driver_init(void) +static int __init macio_bus_driver_init(void) { return bus_register(&macio_bus_type); } @@ -154,6 +157,58 @@ static void macio_release_dev(struct device *dev) kfree(mdev); } +/** + * macio_resource_quirks - tweak or skip some resources for a device + * @np: pointer to the device node + * @res: resulting resource + * @index: index of resource in node + * + * If this routine returns non-null, then the resource is completely + * skipped. + */ +static int macio_resource_quirks(struct device_node *np, struct resource *res, int index) +{ + if (res->flags & IORESOURCE_MEM) { + /* Grand Central has too large resource 0 on some machines */ + if (index == 0 && !strcmp(np->name, "gc")) { + np->addrs[0].size = 0x20000; + res->end = res->start + 0x1ffff; + } + /* Airport has bogus resource 2 */ + if (index >= 2 && !strcmp(np->name, "radio")) + return 1; + /* DBDMAs may have bogus sizes */ + if ((res->start & 0x0001f000) == 0x00008000) { + np->addrs[index].size = 0x100; + res->end = res->start + 0xff; + } + /* ESCC parent eats child resources. We could have added a level of hierarchy, + * but I don't really feel the need for it */ + if (!strcmp(np->name, "escc")) + return 1; + /* ESCC has bogus resources >= 3 */ + if (index >= 3 && !(strcmp(np->name, "ch-a") && strcmp(np->name, "ch-b"))) + return 1; + /* Media bay has too many resources, keep only first one */ + if (index > 0 && !strcmp(np->name, "media-bay")) + return 1; + /* Some older IDE resources have bogus sizes */ + if (!(strcmp(np->name, "IDE") && strcmp(np->name, "ATA") && + strcmp(np->type, "ide") && strcmp(np->type, "ata"))) { + if (index == 0 && np->addrs[0].size > 0x1000) { + np->addrs[0].size = 0x1000; + res->end = res->start + 0xfff; + } + if (index == 1 && np->addrs[1].size > 0x100) { + np->addrs[1].size = 0x100; + res->end = res->start + 0xff; + } + } + } + return 0; +} + + /** * macio_add_one_device - Add one device from OF node to the device tree * @chip: pointer to the macio_chip holding the device @@ -164,9 +219,11 @@ static void macio_release_dev(struct device *dev) * be exposed to the bay driver some way... */ static struct macio_dev * macio_add_one_device(struct macio_chip *chip, struct device *parent, - struct device_node *np, struct macio_dev *in_bay) + struct device_node *np, struct macio_dev *in_bay, + struct resource *parent_res) { struct macio_dev *dev; + int i, j; u32 *reg; if (np == NULL) @@ -186,22 +243,76 @@ static struct macio_dev * macio_add_one_device(struct macio_chip *chip, struct d dev->ofdev.dev.bus = &macio_bus_type; dev->ofdev.dev.release = macio_release_dev; +#ifdef DEBUG + printk("preparing mdev @%p, ofdev @%p, dev @%p, kobj @%p\n", + dev, &dev->ofdev, &dev->ofdev.dev, &dev->ofdev.dev.kobj); +#endif + /* MacIO itself has a different reg, we use it's PCI base */ if (np == chip->of_node) { - sprintf(dev->ofdev.dev.bus_id, "%1d.%08lx:%.8s", chip->lbus.index, + sprintf(dev->ofdev.dev.bus_id, "%1d.%08lx:%.*s", chip->lbus.index, #ifdef CONFIG_PCI pci_resource_start(chip->lbus.pdev, 0), #else 0, /* NuBus may want to do something better here */ #endif - np->name); + MAX_NODE_NAME_SIZE, np->name); } else { reg = (u32 *)get_property(np, "reg", NULL); - sprintf(dev->ofdev.dev.bus_id, "%1d.%08x:%.8s", chip->lbus.index, - reg ? *reg : 0, np->name); + sprintf(dev->ofdev.dev.bus_id, "%1d.%08x:%.*s", chip->lbus.index, + reg ? *reg : 0, MAX_NODE_NAME_SIZE, np->name); } + /* For now, we use pre-parsed entries in the device-tree for + * interrupt routing and addresses, but we should change that + * to dynamically parsed entries and so get rid of most of the + * clutter in struct device_node + */ + for (i = j = 0; i < np->n_intrs; i++) { + struct resource *res = &dev->interrupt[j]; + + if (j >= MACIO_DEV_COUNT_IRQS) + break; + res->start = np->intrs[i].line; + res->flags = IORESOURCE_IO; + if (np->intrs[j].sense) + res->flags |= IORESOURCE_IRQ_LOWLEVEL; + else + res->flags |= IORESOURCE_IRQ_HIGHEDGE; + res->name = dev->ofdev.dev.bus_id; + if (macio_resource_quirks(np, res, i)) + memset(res, 0, sizeof(struct resource)); + else + j++; + } + dev->n_interrupts = j; + for (i = j = 0; i < np->n_addrs; i++) { + struct resource *res = &dev->resource[j]; + + if (j >= MACIO_DEV_COUNT_RESOURCES) + break; + res->start = np->addrs[i].address; + res->end = np->addrs[i].address + np->addrs[i].size - 1; + res->flags = IORESOURCE_MEM; + res->name = dev->ofdev.dev.bus_id; + if (macio_resource_quirks(np, res, i)) + memset(res, 0, sizeof(struct resource)); + else { + j++; + /* Currently, we consider failure as harmless, this may + * change in the future, once I've found all the device + * tree bugs in older machines & worked around them + */ + if (insert_resource(parent_res, res)) + printk(KERN_WARNING "Can't request resource %d for MacIO" + " device %s\n", i, dev->ofdev.dev.bus_id); + } + } + dev->n_resources = j; + if (of_device_register(&dev->ofdev) != 0) { + printk(KERN_DEBUG"macio: device registration error for %s!\n", + dev->ofdev.dev.bus_id); kfree(dev); return NULL; } @@ -234,25 +345,30 @@ static void macio_pci_add_devices(struct macio_chip *chip) struct device_node *np, *pnode; struct macio_dev *rdev, *mdev, *mbdev = NULL, *sdev = NULL; struct device *parent = NULL; + struct resource *root_res = &iomem_resource; /* Add a node for the macio bus itself */ #ifdef CONFIG_PCI - if (chip->lbus.pdev) + if (chip->lbus.pdev) { parent = &chip->lbus.pdev->dev; + root_res = &chip->lbus.pdev->resource[0]; + } #endif pnode = of_node_get(chip->of_node); if (pnode == NULL) return; - rdev = macio_add_one_device(chip, parent, pnode, NULL); + /* Add macio itself to hierarchy */ + rdev = macio_add_one_device(chip, parent, pnode, NULL, root_res); if (rdev == NULL) return; + root_res = &rdev->resource[0]; /* First scan 1st level */ for (np = NULL; (np = of_get_next_child(pnode, np)) != NULL;) { if (!macio_skip_device(np)) { of_node_get(np); - mdev = macio_add_one_device(chip, &rdev->ofdev.dev, np, NULL); + mdev = macio_add_one_device(chip, &rdev->ofdev.dev, np, NULL, root_res); if (mdev == NULL) of_node_put(np); else if (strncmp(np->name, "media-bay", 9) == 0) @@ -267,17 +383,20 @@ static void macio_pci_add_devices(struct macio_chip *chip) for (np = NULL; (np = of_get_next_child(mbdev->ofdev.node, np)) != NULL;) if (!macio_skip_device(np)) { of_node_get(np); - if (macio_add_one_device(chip, &mbdev->ofdev.dev, np, mbdev) == NULL) + if (macio_add_one_device(chip, &mbdev->ofdev.dev, np, mbdev, + root_res) == NULL) of_node_put(np); } /* Add serial ports if any */ - if (sdev) + if (sdev) { for (np = NULL; (np = of_get_next_child(sdev->ofdev.node, np)) != NULL;) if (!macio_skip_device(np)) { of_node_get(np); - if (macio_add_one_device(chip, &sdev->ofdev.dev, np, NULL) == NULL) + if (macio_add_one_device(chip, &sdev->ofdev.dev, np, NULL, + root_res) == NULL) of_node_put(np); } + } } @@ -294,6 +413,7 @@ int macio_register_driver(struct macio_driver *drv) drv->driver.bus = &macio_bus_type; drv->driver.probe = macio_device_probe; drv->driver.remove = macio_device_remove; + drv->driver.shutdown = macio_device_shutdown; /* register with core */ count = driver_register(&drv->driver); @@ -309,6 +429,97 @@ void macio_unregister_driver(struct macio_driver *drv) driver_unregister(&drv->driver); } +/** + * macio_request_resource - Request an MMIO resource + * @dev: pointer to the device holding the resource + * @resource_no: resource number to request + * @name: resource name + * + * Mark memory region number @resource_no associated with MacIO + * device @dev as being reserved by owner @name. Do not access + * any address inside the memory regions unless this call returns + * successfully. + * + * Returns 0 on success, or %EBUSY on error. A warning + * message is also printed on failure. + */ +int macio_request_resource(struct macio_dev *dev, int resource_no, const char *name) +{ + if (macio_resource_len(dev, resource_no) == 0) + return 0; + + if (!request_mem_region(macio_resource_start(dev, resource_no), + macio_resource_len(dev, resource_no), + name)) + goto err_out; + + return 0; + +err_out: + printk (KERN_WARNING "MacIO: Unable to reserve resource #%d:%lx@%lx" + " for device %s\n", + resource_no, + macio_resource_len(dev, resource_no), + macio_resource_start(dev, resource_no), + dev->ofdev.dev.bus_id); + return -EBUSY; +} + +/** + * macio_release_resource - Release an MMIO resource + * @dev: pointer to the device holding the resource + * @resource_no: resource number to release + */ +void macio_release_resource(struct macio_dev *dev, int resource_no) +{ + if (macio_resource_len(dev, resource_no) == 0) + return; + release_mem_region(macio_resource_start(dev, resource_no), + macio_resource_len(dev, resource_no)); +} + +/** + * macio_request_resources - Reserve all memory resources + * @dev: MacIO device whose resources are to be reserved + * @name: Name to be associated with resource. + * + * Mark all memory regions associated with MacIO device @dev as + * being reserved by owner @name. Do not access any address inside + * the memory regions unless this call returns successfully. + * + * Returns 0 on success, or %EBUSY on error. A warning + * message is also printed on failure. + */ +int macio_request_resources(struct macio_dev *dev, const char *name) +{ + int i; + + for (i = 0; i < dev->n_resources; i++) + if (macio_request_resource(dev, i, name)) + goto err_out; + return 0; + +err_out: + while(--i >= 0) + macio_release_resource(dev, i); + + return -EBUSY; +} + +/** + * macio_release_resources - Release reserved memory resources + * @dev: MacIO device whose resources were previously reserved + */ + +void macio_release_resources(struct macio_dev *dev) +{ + int i; + + for (i = 0; i < dev->n_resources; i++) + macio_release_resource(dev, i); +} + + #ifdef CONFIG_PCI static int __devinit macio_pci_probe(struct pci_dev *pdev, const struct pci_device_id *ent) @@ -416,3 +627,7 @@ EXPORT_SYMBOL(macio_register_driver); EXPORT_SYMBOL(macio_unregister_driver); EXPORT_SYMBOL(macio_dev_get); EXPORT_SYMBOL(macio_dev_put); +EXPORT_SYMBOL(macio_request_resource); +EXPORT_SYMBOL(macio_release_resource); +EXPORT_SYMBOL(macio_request_resources); +EXPORT_SYMBOL(macio_release_resources); diff --git a/include/asm-ppc/macio.h b/include/asm-ppc/macio.h index 25381dc34fcd..00605bb88b2a 100644 --- a/include/asm-ppc/macio.h +++ b/include/asm-ppc/macio.h @@ -9,7 +9,7 @@ extern struct bus_type macio_bus_type; struct macio_driver; struct macio_chip; -#define MACIO_DEV_COUNT_RESOURCE 8 +#define MACIO_DEV_COUNT_RESOURCES 8 #define MACIO_DEV_COUNT_IRQS 8 /* @@ -38,6 +38,10 @@ struct macio_dev struct macio_bus *bus; /* macio bus this device is on */ struct macio_dev *media_bay; /* Device is part of a media bay */ struct of_device ofdev; + int n_resources; + struct resource resource[MACIO_DEV_COUNT_RESOURCES]; + int n_interrupts; + struct resource interrupt[MACIO_DEV_COUNT_IRQS]; }; #define to_macio_device(d) container_of(d, struct macio_dev, ofdev.dev) #define of_to_macio_device(d) container_of(d, struct macio_dev, ofdev) @@ -45,6 +49,71 @@ struct macio_dev extern struct macio_dev *macio_dev_get(struct macio_dev *dev); extern void macio_dev_put(struct macio_dev *dev); +/* + * Accessors to resources & interrupts and other device + * fields + */ + +static inline int macio_resource_count(struct macio_dev *dev) +{ + return dev->n_resources; +} + +static inline unsigned long macio_resource_start(struct macio_dev *dev, int resource_no) +{ + return dev->resource[resource_no].start; +} + +static inline unsigned long macio_resource_end(struct macio_dev *dev, int resource_no) +{ + return dev->resource[resource_no].end; +} + +static inline unsigned long macio_resource_len(struct macio_dev *dev, int resource_no) +{ + struct resource *res = &dev->resource[resource_no]; + if (res->start == 0 || res->end == 0 || res->end < res->start) + return 0; + return res->end - res->start + 1; +} + +extern int macio_request_resource(struct macio_dev *dev, int resource_no, const char *name); +extern void macio_release_resource(struct macio_dev *dev, int resource_no); +extern int macio_request_resources(struct macio_dev *dev, const char *name); +extern void macio_release_resources(struct macio_dev *dev); + +static inline int macio_irq_count(struct macio_dev *dev) +{ + return dev->n_interrupts; +} + +static inline int macio_irq(struct macio_dev *dev, int irq_no) +{ + return dev->interrupt[irq_no].start; +} + +static inline void macio_set_drvdata(struct macio_dev *dev, void *data) +{ + dev_set_drvdata(&dev->ofdev.dev, data); +} + +static inline void* macio_get_drvdata(struct macio_dev *dev) +{ + return dev_get_drvdata(&dev->ofdev.dev); +} + +static inline struct device_node *macio_get_of_node(struct macio_dev *mdev) +{ + return mdev->ofdev.node; +} + +#ifdef CONFIG_PCI +static inline struct pci_dev *macio_get_pci_dev(struct macio_dev *mdev) +{ + return mdev->bus->pdev; +} +#endif + /* * A driver for a mac-io chip based device */ -- cgit v1.2.3