diff options
28 files changed, 1934 insertions, 18 deletions
diff --git a/Documentation/x86/boot.txt b/Documentation/x86/boot.txt index 30b43e1b269..bdeb81ccb5f 100644 --- a/Documentation/x86/boot.txt +++ b/Documentation/x86/boot.txt | |||
| @@ -600,6 +600,7 @@ Protocol: 2.07+ | |||
| 600 | 0x00000001 lguest | 600 | 0x00000001 lguest |
| 601 | 0x00000002 Xen | 601 | 0x00000002 Xen |
| 602 | 0x00000003 Moorestown MID | 602 | 0x00000003 Moorestown MID |
| 603 | 0x00000004 CE4100 TV Platform | ||
| 603 | 604 | ||
| 604 | Field name: hardware_subarch_data | 605 | Field name: hardware_subarch_data |
| 605 | Type: write (subarch-dependent) | 606 | Type: write (subarch-dependent) |
diff --git a/arch/x86/Kconfig b/arch/x86/Kconfig index 08993a38b11..58ecad57aad 100644 --- a/arch/x86/Kconfig +++ b/arch/x86/Kconfig | |||
| @@ -377,6 +377,18 @@ config X86_ELAN | |||
| 377 | 377 | ||
| 378 | If unsure, choose "PC-compatible" instead. | 378 | If unsure, choose "PC-compatible" instead. |
| 379 | 379 | ||
| 380 | config X86_INTEL_CE | ||
| 381 | bool "CE4100 TV platform" | ||
| 382 | depends on PCI | ||
| 383 | depends on PCI_GODIRECT | ||
| 384 | depends on X86_32 | ||
| 385 | depends on X86_EXTENDED_PLATFORM | ||
| 386 | select X86_REBOOTFIXUPS | ||
| 387 | ---help--- | ||
| 388 | Select for the Intel CE media processor (CE4100) SOC. | ||
| 389 | This option compiles in support for the CE4100 SOC for settop | ||
| 390 | boxes and media devices. | ||
| 391 | |||
| 380 | config X86_MRST | 392 | config X86_MRST |
| 381 | bool "Moorestown MID platform" | 393 | bool "Moorestown MID platform" |
| 382 | depends on PCI | 394 | depends on PCI |
| @@ -385,6 +397,10 @@ config X86_MRST | |||
| 385 | depends on X86_EXTENDED_PLATFORM | 397 | depends on X86_EXTENDED_PLATFORM |
| 386 | depends on X86_IO_APIC | 398 | depends on X86_IO_APIC |
| 387 | select APB_TIMER | 399 | select APB_TIMER |
| 400 | select I2C | ||
| 401 | select SPI | ||
| 402 | select INTEL_SCU_IPC | ||
| 403 | select X86_PLATFORM_DEVICES | ||
| 388 | ---help--- | 404 | ---help--- |
| 389 | Moorestown is Intel's Low Power Intel Architecture (LPIA) based Moblin | 405 | Moorestown is Intel's Low Power Intel Architecture (LPIA) based Moblin |
| 390 | Internet Device(MID) platform. Moorestown consists of two chips: | 406 | Internet Device(MID) platform. Moorestown consists of two chips: |
| @@ -466,6 +482,19 @@ config X86_ES7000 | |||
| 466 | Support for Unisys ES7000 systems. Say 'Y' here if this kernel is | 482 | Support for Unisys ES7000 systems. Say 'Y' here if this kernel is |
| 467 | supposed to run on an IA32-based Unisys ES7000 system. | 483 | supposed to run on an IA32-based Unisys ES7000 system. |
| 468 | 484 | ||
| 485 | config X86_32_IRIS | ||
| 486 | tristate "Eurobraille/Iris poweroff module" | ||
| 487 | depends on X86_32 | ||
| 488 | ---help--- | ||
| 489 | The Iris machines from EuroBraille do not have APM or ACPI support | ||
| 490 | to shut themselves down properly. A special I/O sequence is | ||
| 491 | needed to do so, which is what this module does at | ||
| 492 | kernel shutdown. | ||
| 493 | |||
| 494 | This is only for Iris machines from EuroBraille. | ||
| 495 | |||
| 496 | If unused, say N. | ||
| 497 | |||
| 469 | config SCHED_OMIT_FRAME_POINTER | 498 | config SCHED_OMIT_FRAME_POINTER |
| 470 | def_bool y | 499 | def_bool y |
| 471 | prompt "Single-depth WCHAN output" | 500 | prompt "Single-depth WCHAN output" |
diff --git a/arch/x86/include/asm/bootparam.h b/arch/x86/include/asm/bootparam.h index 8e6218550e7..c8bfe63a06d 100644 --- a/arch/x86/include/asm/bootparam.h +++ b/arch/x86/include/asm/bootparam.h | |||
| @@ -124,6 +124,7 @@ enum { | |||
| 124 | X86_SUBARCH_LGUEST, | 124 | X86_SUBARCH_LGUEST, |
| 125 | X86_SUBARCH_XEN, | 125 | X86_SUBARCH_XEN, |
| 126 | X86_SUBARCH_MRST, | 126 | X86_SUBARCH_MRST, |
| 127 | X86_SUBARCH_CE4100, | ||
| 127 | X86_NR_SUBARCHS, | 128 | X86_NR_SUBARCHS, |
| 128 | }; | 129 | }; |
| 129 | 130 | ||
diff --git a/arch/x86/include/asm/fixmap.h b/arch/x86/include/asm/fixmap.h index 4d293dced62..139591a933f 100644 --- a/arch/x86/include/asm/fixmap.h +++ b/arch/x86/include/asm/fixmap.h | |||
| @@ -117,6 +117,10 @@ enum fixed_addresses { | |||
| 117 | FIX_TEXT_POKE1, /* reserve 2 pages for text_poke() */ | 117 | FIX_TEXT_POKE1, /* reserve 2 pages for text_poke() */ |
| 118 | FIX_TEXT_POKE0, /* first page is last, because allocation is backward */ | 118 | FIX_TEXT_POKE0, /* first page is last, because allocation is backward */ |
| 119 | __end_of_permanent_fixed_addresses, | 119 | __end_of_permanent_fixed_addresses, |
| 120 | |||
| 121 | #ifdef CONFIG_X86_MRST | ||
| 122 | FIX_LNW_VRTC, | ||
| 123 | #endif | ||
| 120 | /* | 124 | /* |
| 121 | * 256 temporary boot-time mappings, used by early_ioremap(), | 125 | * 256 temporary boot-time mappings, used by early_ioremap(), |
| 122 | * before ioremap() is functional. | 126 | * before ioremap() is functional. |
diff --git a/arch/x86/include/asm/mrst-vrtc.h b/arch/x86/include/asm/mrst-vrtc.h new file mode 100644 index 00000000000..73668abdbed --- /dev/null +++ b/arch/x86/include/asm/mrst-vrtc.h | |||
| @@ -0,0 +1,9 @@ | |||
| 1 | #ifndef _MRST_VRTC_H | ||
| 2 | #define _MRST_VRTC_H | ||
| 3 | |||
| 4 | extern unsigned char vrtc_cmos_read(unsigned char reg); | ||
| 5 | extern void vrtc_cmos_write(unsigned char val, unsigned char reg); | ||
| 6 | extern unsigned long vrtc_get_time(void); | ||
| 7 | extern int vrtc_set_mmss(unsigned long nowtime); | ||
| 8 | |||
| 9 | #endif | ||
diff --git a/arch/x86/include/asm/mrst.h b/arch/x86/include/asm/mrst.h index 4a711a684b1..719f00b28ff 100644 --- a/arch/x86/include/asm/mrst.h +++ b/arch/x86/include/asm/mrst.h | |||
| @@ -14,7 +14,9 @@ | |||
| 14 | #include <linux/sfi.h> | 14 | #include <linux/sfi.h> |
| 15 | 15 | ||
| 16 | extern int pci_mrst_init(void); | 16 | extern int pci_mrst_init(void); |
| 17 | int __init sfi_parse_mrtc(struct sfi_table_header *table); | 17 | extern int __init sfi_parse_mrtc(struct sfi_table_header *table); |
| 18 | extern int sfi_mrtc_num; | ||
| 19 | extern struct sfi_rtc_table_entry sfi_mrtc_array[]; | ||
| 18 | 20 | ||
| 19 | /* | 21 | /* |
| 20 | * Medfield is the follow-up of Moorestown, it combines two chip solution into | 22 | * Medfield is the follow-up of Moorestown, it combines two chip solution into |
| @@ -50,4 +52,14 @@ extern void mrst_early_console_init(void); | |||
| 50 | 52 | ||
| 51 | extern struct console early_hsu_console; | 53 | extern struct console early_hsu_console; |
| 52 | extern void hsu_early_console_init(void); | 54 | extern void hsu_early_console_init(void); |
| 55 | |||
| 56 | extern void intel_scu_devices_create(void); | ||
| 57 | extern void intel_scu_devices_destroy(void); | ||
| 58 | |||
| 59 | /* VRTC timer */ | ||
| 60 | #define MRST_VRTC_MAP_SZ (1024) | ||
| 61 | /*#define MRST_VRTC_PGOFFSET (0xc00) */ | ||
| 62 | |||
| 63 | extern void mrst_rtc_init(void); | ||
| 64 | |||
| 53 | #endif /* _ASM_X86_MRST_H */ | 65 | #endif /* _ASM_X86_MRST_H */ |
diff --git a/arch/x86/include/asm/setup.h b/arch/x86/include/asm/setup.h index d6763b139a8..db8aa19a08a 100644 --- a/arch/x86/include/asm/setup.h +++ b/arch/x86/include/asm/setup.h | |||
| @@ -53,6 +53,12 @@ extern void x86_mrst_early_setup(void); | |||
| 53 | static inline void x86_mrst_early_setup(void) { } | 53 | static inline void x86_mrst_early_setup(void) { } |
| 54 | #endif | 54 | #endif |
| 55 | 55 | ||
| 56 | #ifdef CONFIG_X86_INTEL_CE | ||
| 57 | extern void x86_ce4100_early_setup(void); | ||
| 58 | #else | ||
| 59 | static inline void x86_ce4100_early_setup(void) { } | ||
| 60 | #endif | ||
| 61 | |||
| 56 | #ifndef _SETUP | 62 | #ifndef _SETUP |
| 57 | 63 | ||
| 58 | /* | 64 | /* |
diff --git a/arch/x86/kernel/Makefile b/arch/x86/kernel/Makefile index 9e13763b609..f60153d5de5 100644 --- a/arch/x86/kernel/Makefile +++ b/arch/x86/kernel/Makefile | |||
| @@ -84,7 +84,6 @@ obj-$(CONFIG_DOUBLEFAULT) += doublefault_32.o | |||
| 84 | obj-$(CONFIG_KGDB) += kgdb.o | 84 | obj-$(CONFIG_KGDB) += kgdb.o |
| 85 | obj-$(CONFIG_VM86) += vm86_32.o | 85 | obj-$(CONFIG_VM86) += vm86_32.o |
| 86 | obj-$(CONFIG_EARLY_PRINTK) += early_printk.o | 86 | obj-$(CONFIG_EARLY_PRINTK) += early_printk.o |
| 87 | obj-$(CONFIG_EARLY_PRINTK_MRST) += early_printk_mrst.o | ||
| 88 | 87 | ||
| 89 | obj-$(CONFIG_HPET_TIMER) += hpet.o | 88 | obj-$(CONFIG_HPET_TIMER) += hpet.o |
| 90 | obj-$(CONFIG_APB_TIMER) += apb_timer.o | 89 | obj-$(CONFIG_APB_TIMER) += apb_timer.o |
diff --git a/arch/x86/kernel/apb_timer.c b/arch/x86/kernel/apb_timer.c index 92543c73cf8..7c9ab59653e 100644 --- a/arch/x86/kernel/apb_timer.c +++ b/arch/x86/kernel/apb_timer.c | |||
| @@ -315,6 +315,7 @@ static void apbt_setup_irq(struct apbt_dev *adev) | |||
| 315 | 315 | ||
| 316 | if (system_state == SYSTEM_BOOTING) { | 316 | if (system_state == SYSTEM_BOOTING) { |
| 317 | irq_modify_status(adev->irq, 0, IRQ_MOVE_PCNTXT); | 317 | irq_modify_status(adev->irq, 0, IRQ_MOVE_PCNTXT); |
| 318 | irq_set_affinity(adev->irq, cpumask_of(adev->cpu)); | ||
| 318 | /* APB timer irqs are set up as mp_irqs, timer is edge type */ | 319 | /* APB timer irqs are set up as mp_irqs, timer is edge type */ |
| 319 | __set_irq_handler(adev->irq, handle_edge_irq, 0, "edge"); | 320 | __set_irq_handler(adev->irq, handle_edge_irq, 0, "edge"); |
| 320 | if (request_irq(adev->irq, apbt_interrupt_handler, | 321 | if (request_irq(adev->irq, apbt_interrupt_handler, |
diff --git a/arch/x86/kernel/early_printk.c b/arch/x86/kernel/early_printk.c index 4572f25f932..cd28a350f7f 100644 --- a/arch/x86/kernel/early_printk.c +++ b/arch/x86/kernel/early_printk.c | |||
| @@ -240,7 +240,7 @@ static int __init setup_early_printk(char *buf) | |||
| 240 | if (!strncmp(buf, "xen", 3)) | 240 | if (!strncmp(buf, "xen", 3)) |
| 241 | early_console_register(&xenboot_console, keep); | 241 | early_console_register(&xenboot_console, keep); |
| 242 | #endif | 242 | #endif |
| 243 | #ifdef CONFIG_X86_MRST_EARLY_PRINTK | 243 | #ifdef CONFIG_EARLY_PRINTK_MRST |
| 244 | if (!strncmp(buf, "mrst", 4)) { | 244 | if (!strncmp(buf, "mrst", 4)) { |
| 245 | mrst_early_console_init(); | 245 | mrst_early_console_init(); |
| 246 | early_console_register(&early_mrst_console, keep); | 246 | early_console_register(&early_mrst_console, keep); |
| @@ -250,7 +250,6 @@ static int __init setup_early_printk(char *buf) | |||
| 250 | hsu_early_console_init(); | 250 | hsu_early_console_init(); |
| 251 | early_console_register(&early_hsu_console, keep); | 251 | early_console_register(&early_hsu_console, keep); |
| 252 | } | 252 | } |
| 253 | |||
| 254 | #endif | 253 | #endif |
| 255 | buf++; | 254 | buf++; |
| 256 | } | 255 | } |
diff --git a/arch/x86/kernel/head32.c b/arch/x86/kernel/head32.c index 763310165fa..7f138b3c3c5 100644 --- a/arch/x86/kernel/head32.c +++ b/arch/x86/kernel/head32.c | |||
| @@ -61,6 +61,9 @@ void __init i386_start_kernel(void) | |||
| 61 | case X86_SUBARCH_MRST: | 61 | case X86_SUBARCH_MRST: |
| 62 | x86_mrst_early_setup(); | 62 | x86_mrst_early_setup(); |
| 63 | break; | 63 | break; |
| 64 | case X86_SUBARCH_CE4100: | ||
| 65 | x86_ce4100_early_setup(); | ||
| 66 | break; | ||
| 64 | default: | 67 | default: |
| 65 | i386_default_early_setup(); | 68 | i386_default_early_setup(); |
| 66 | break; | 69 | break; |
diff --git a/arch/x86/kernel/reboot_fixups_32.c b/arch/x86/kernel/reboot_fixups_32.c index fda313ebbb0..c8e41e90f59 100644 --- a/arch/x86/kernel/reboot_fixups_32.c +++ b/arch/x86/kernel/reboot_fixups_32.c | |||
| @@ -43,17 +43,33 @@ static void rdc321x_reset(struct pci_dev *dev) | |||
| 43 | outb(1, 0x92); | 43 | outb(1, 0x92); |
| 44 | } | 44 | } |
| 45 | 45 | ||
| 46 | static void ce4100_reset(struct pci_dev *dev) | ||
| 47 | { | ||
| 48 | int i; | ||
| 49 | |||
| 50 | for (i = 0; i < 10; i++) { | ||
| 51 | outb(0x2, 0xcf9); | ||
| 52 | udelay(50); | ||
| 53 | } | ||
| 54 | } | ||
| 55 | |||
| 46 | struct device_fixup { | 56 | struct device_fixup { |
| 47 | unsigned int vendor; | 57 | unsigned int vendor; |
| 48 | unsigned int device; | 58 | unsigned int device; |
| 49 | void (*reboot_fixup)(struct pci_dev *); | 59 | void (*reboot_fixup)(struct pci_dev *); |
| 50 | }; | 60 | }; |
| 51 | 61 | ||
| 62 | /* | ||
| 63 | * PCI ids solely used for fixups_table go here | ||
| 64 | */ | ||
| 65 | #define PCI_DEVICE_ID_INTEL_CE4100 0x0708 | ||
| 66 | |||
| 52 | static const struct device_fixup fixups_table[] = { | 67 | static const struct device_fixup fixups_table[] = { |
| 53 | { PCI_VENDOR_ID_CYRIX, PCI_DEVICE_ID_CYRIX_5530_LEGACY, cs5530a_warm_reset }, | 68 | { PCI_VENDOR_ID_CYRIX, PCI_DEVICE_ID_CYRIX_5530_LEGACY, cs5530a_warm_reset }, |
| 54 | { PCI_VENDOR_ID_AMD, PCI_DEVICE_ID_AMD_CS5536_ISA, cs5536_warm_reset }, | 69 | { PCI_VENDOR_ID_AMD, PCI_DEVICE_ID_AMD_CS5536_ISA, cs5536_warm_reset }, |
| 55 | { PCI_VENDOR_ID_NS, PCI_DEVICE_ID_NS_SC1100_BRIDGE, cs5530a_warm_reset }, | 70 | { PCI_VENDOR_ID_NS, PCI_DEVICE_ID_NS_SC1100_BRIDGE, cs5530a_warm_reset }, |
| 56 | { PCI_VENDOR_ID_RDC, PCI_DEVICE_ID_RDC_R6030, rdc321x_reset }, | 71 | { PCI_VENDOR_ID_RDC, PCI_DEVICE_ID_RDC_R6030, rdc321x_reset }, |
| 72 | { PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_CE4100, ce4100_reset }, | ||
| 57 | }; | 73 | }; |
| 58 | 74 | ||
| 59 | /* | 75 | /* |
diff --git a/arch/x86/pci/Makefile b/arch/x86/pci/Makefile index effd96e33f1..6b8759f7634 100644 --- a/arch/x86/pci/Makefile +++ b/arch/x86/pci/Makefile | |||
| @@ -7,6 +7,7 @@ obj-$(CONFIG_PCI_OLPC) += olpc.o | |||
| 7 | obj-$(CONFIG_PCI_XEN) += xen.o | 7 | obj-$(CONFIG_PCI_XEN) += xen.o |
| 8 | 8 | ||
| 9 | obj-y += fixup.o | 9 | obj-y += fixup.o |
| 10 | obj-$(CONFIG_X86_INTEL_CE) += ce4100.o | ||
| 10 | obj-$(CONFIG_ACPI) += acpi.o | 11 | obj-$(CONFIG_ACPI) += acpi.o |
| 11 | obj-y += legacy.o irq.o | 12 | obj-y += legacy.o irq.o |
| 12 | 13 | ||
diff --git a/arch/x86/pci/ce4100.c b/arch/x86/pci/ce4100.c new file mode 100644 index 00000000000..85b68ef5e80 --- /dev/null +++ b/arch/x86/pci/ce4100.c | |||
| @@ -0,0 +1,315 @@ | |||
| 1 | /* | ||
| 2 | * GPL LICENSE SUMMARY | ||
| 3 | * | ||
| 4 | * Copyright(c) 2010 Intel Corporation. All rights reserved. | ||
| 5 | * | ||
| 6 | * This program is free software; you can redistribute it and/or modify | ||
| 7 | * it under the terms of version 2 of the GNU General Public License as | ||
| 8 | * published by the Free Software Foundation. | ||
| 9 | * | ||
| 10 | * This program is distributed in the hope that it will be useful, but | ||
| 11 | * WITHOUT ANY WARRANTY; without even the implied warranty of | ||
| 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | ||
| 13 | * General Public License for more details. | ||
| 14 | * | ||
| 15 | * You should have received a copy of the GNU General Public License | ||
| 16 | * along with this program; if not, write to the Free Software | ||
| 17 | * Foundation, Inc., 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA. | ||
| 18 | * The full GNU General Public License is included in this distribution | ||
| 19 | * in the file called LICENSE.GPL. | ||
| 20 | * | ||
| 21 | * Contact Information: | ||
| 22 | * Intel Corporation | ||
| 23 | * 2200 Mission College Blvd. | ||
| 24 | * Santa Clara, CA 97052 | ||
| 25 | * | ||
| 26 | * This provides access methods for PCI registers that mis-behave on | ||
| 27 | * the CE4100. Each register can be assigned a private init, read and | ||
| 28 | * write routine. The exception to this is the bridge device. The | ||
| 29 | * bridge device is the only device on bus zero (0) that requires any | ||
| 30 | * fixup so it is a special case ATM | ||
| 31 | */ | ||
| 32 | |||
| 33 | #include <linux/kernel.h> | ||
| 34 | #include <linux/pci.h> | ||
| 35 | #include <linux/init.h> | ||
| 36 | |||
| 37 | #include <asm/pci_x86.h> | ||
| 38 | |||
| 39 | struct sim_reg { | ||
| 40 | u32 value; | ||
| 41 | u32 mask; | ||
| 42 | }; | ||
| 43 | |||
| 44 | struct sim_dev_reg { | ||
| 45 | int dev_func; | ||
| 46 | int reg; | ||
| 47 | void (*init)(struct sim_dev_reg *reg); | ||
| 48 | void (*read)(struct sim_dev_reg *reg, u32 *value); | ||
| 49 | void (*write)(struct sim_dev_reg *reg, u32 value); | ||
| 50 | struct sim_reg sim_reg; | ||
| 51 | }; | ||
| 52 | |||
| 53 | struct sim_reg_op { | ||
| 54 | void (*init)(struct sim_dev_reg *reg); | ||
| 55 | void (*read)(struct sim_dev_reg *reg, u32 value); | ||
| 56 | void (*write)(struct sim_dev_reg *reg, u32 value); | ||
| 57 | }; | ||
| 58 | |||
| 59 | #define MB (1024 * 1024) | ||
| 60 | #define KB (1024) | ||
| 61 | #define SIZE_TO_MASK(size) (~(size - 1)) | ||
| 62 | |||
| 63 | #define DEFINE_REG(device, func, offset, size, init_op, read_op, write_op)\ | ||
| 64 | { PCI_DEVFN(device, func), offset, init_op, read_op, write_op,\ | ||
| 65 | {0, SIZE_TO_MASK(size)} }, | ||
| 66 | |||
| 67 | static void reg_init(struct sim_dev_reg *reg) | ||
| 68 | { | ||
| 69 | pci_direct_conf1.read(0, 1, reg->dev_func, reg->reg, 4, | ||
| 70 | ®->sim_reg.value); | ||
| 71 | } | ||
| 72 | |||
| 73 | static void reg_read(struct sim_dev_reg *reg, u32 *value) | ||
| 74 | { | ||
| 75 | unsigned long flags; | ||
| 76 | |||
| 77 | raw_spin_lock_irqsave(&pci_config_lock, flags); | ||
| 78 | *value = reg->sim_reg.value; | ||
| 79 | raw_spin_unlock_irqrestore(&pci_config_lock, flags); | ||
| 80 | } | ||
| 81 | |||
| 82 | static void reg_write(struct sim_dev_reg *reg, u32 value) | ||
| 83 | { | ||
| 84 | unsigned long flags; | ||
| 85 | |||
| 86 | raw_spin_lock_irqsave(&pci_config_lock, flags); | ||
| 87 | reg->sim_reg.value = (value & reg->sim_reg.mask) | | ||
| 88 | (reg->sim_reg.value & ~reg->sim_reg.mask); | ||
| 89 | raw_spin_unlock_irqrestore(&pci_config_lock, flags); | ||
| 90 | } | ||
| 91 | |||
| 92 | static void sata_reg_init(struct sim_dev_reg *reg) | ||
| 93 | { | ||
| 94 | pci_direct_conf1.read(0, 1, PCI_DEVFN(14, 0), 0x10, 4, | ||
| 95 | ®->sim_reg.value); | ||
| 96 | reg->sim_reg.value += 0x400; | ||
| 97 | } | ||
| 98 | |||
| 99 | static void ehci_reg_read(struct sim_dev_reg *reg, u32 *value) | ||
| 100 | { | ||
| 101 | reg_read(reg, value); | ||
| 102 | if (*value != reg->sim_reg.mask) | ||
| 103 | *value |= 0x100; | ||
| 104 | } | ||
| 105 | |||
| 106 | void sata_revid_init(struct sim_dev_reg *reg) | ||
| 107 | { | ||
| 108 | reg->sim_reg.value = 0x01060100; | ||
| 109 | reg->sim_reg.mask = 0; | ||
| 110 | } | ||
| 111 | |||
| 112 | static void sata_revid_read(struct sim_dev_reg *reg, u32 *value) | ||
| 113 | { | ||
| 114 | reg_read(reg, value); | ||
| 115 | } | ||
| 116 | |||
| 117 | static struct sim_dev_reg bus1_fixups[] = { | ||
| 118 | DEFINE_REG(2, 0, 0x10, (16*MB), reg_init, reg_read, reg_write) | ||
| 119 | DEFINE_REG(2, 0, 0x14, (256), reg_init, reg_read, reg_write) | ||
| 120 | DEFINE_REG(2, 1, 0x10, (64*KB), reg_init, reg_read, reg_write) | ||
| 121 | DEFINE_REG(3, 0, 0x10, (64*KB), reg_init, reg_read, reg_write) | ||
| 122 | DEFINE_REG(4, 0, 0x10, (128*KB), reg_init, reg_read, reg_write) | ||
| 123 | DEFINE_REG(4, 1, 0x10, (128*KB), reg_init, reg_read, reg_write) | ||
| 124 | DEFINE_REG(6, 0, 0x10, (512*KB), reg_init, reg_read, reg_write) | ||
| 125 | DEFINE_REG(6, 1, 0x10, (512*KB), reg_init, reg_read, reg_write) | ||
| 126 | DEFINE_REG(6, 2, 0x10, (64*KB), reg_init, reg_read, reg_write) | ||
| 127 | DEFINE_REG(8, 0, 0x10, (1*MB), reg_init, reg_read, reg_write) | ||
| 128 | DEFINE_REG(8, 1, 0x10, (64*KB), reg_init, reg_read, reg_write) | ||
| 129 | DEFINE_REG(8, 2, 0x10, (64*KB), reg_init, reg_read, reg_write) | ||
| 130 | DEFINE_REG(9, 0, 0x10 , (1*MB), reg_init, reg_read, reg_write) | ||
| 131 | DEFINE_REG(9, 0, 0x14, (64*KB), reg_init, reg_read, reg_write) | ||
| 132 | DEFINE_REG(10, 0, 0x10, (256), reg_init, reg_read, reg_write) | ||
| 133 | DEFINE_REG(10, 0, 0x14, (256*MB), reg_init, reg_read, reg_write) | ||
| 134 | DEFINE_REG(11, 0, 0x10, (256), reg_init, reg_read, reg_write) | ||
| 135 | DEFINE_REG(11, 0, 0x14, (256), reg_init, reg_read, reg_write) | ||
| 136 | DEFINE_REG(11, 1, 0x10, (256), reg_init, reg_read, reg_write) | ||
| 137 | DEFINE_REG(11, 2, 0x10, (256), reg_init, reg_read, reg_write) | ||
| 138 | DEFINE_REG(11, 2, 0x14, (256), reg_init, reg_read, reg_write) | ||
| 139 | DEFINE_REG(11, 2, 0x18, (256), reg_init, reg_read, reg_write) | ||
| 140 | DEFINE_REG(11, 3, 0x10, (256), reg_init, reg_read, reg_write) | ||
| 141 | DEFINE_REG(11, 3, 0x14, (256), reg_init, reg_read, reg_write) | ||
| 142 | DEFINE_REG(11, 4, 0x10, (256), reg_init, reg_read, reg_write) | ||
| 143 | DEFINE_REG(11, 5, 0x10, (64*KB), reg_init, reg_read, reg_write) | ||
| 144 | DEFINE_REG(11, 6, 0x10, (256), reg_init, reg_read, reg_write) | ||
| 145 | DEFINE_REG(11, 7, 0x10, (64*KB), reg_init, reg_read, reg_write) | ||
| 146 | DEFINE_REG(12, 0, 0x10, (128*KB), reg_init, reg_read, reg_write) | ||
| 147 | DEFINE_REG(12, 0, 0x14, (256), reg_init, reg_read, reg_write) | ||
| 148 | DEFINE_REG(12, 1, 0x10, (1024), reg_init, reg_read, reg_write) | ||
| 149 | DEFINE_REG(13, 0, 0x10, (32*KB), reg_init, ehci_reg_read, reg_write) | ||
| 150 | DEFINE_REG(13, 1, 0x10, (32*KB), reg_init, ehci_reg_read, reg_write) | ||
| 151 | DEFINE_REG(14, 0, 0x8, 0, sata_revid_init, sata_revid_read, 0) | ||
| 152 | DEFINE_REG(14, 0, 0x10, 0, reg_init, reg_read, reg_write) | ||
| 153 | DEFINE_REG(14, 0, 0x14, 0, reg_init, reg_read, reg_write) | ||
| 154 | DEFINE_REG(14, 0, 0x18, 0, reg_init, reg_read, reg_write) | ||
| 155 | DEFINE_REG(14, 0, 0x1C, 0, reg_init, reg_read, reg_write) | ||
| 156 | DEFINE_REG(14, 0, 0x20, 0, reg_init, reg_read, reg_write) | ||
| 157 | DEFINE_REG(14, 0, 0x24, (0x200), sata_reg_init, reg_read, reg_write) | ||
| 158 | DEFINE_REG(15, 0, 0x10, (64*KB), reg_init, reg_read, reg_write) | ||
| 159 | DEFINE_REG(15, 0, 0x14, (64*KB), reg_init, reg_read, reg_write) | ||
| 160 | DEFINE_REG(16, 0, 0x10, (64*KB), reg_init, reg_read, reg_write) | ||
| 161 | DEFINE_REG(16, 0, 0x14, (64*MB), reg_init, reg_read, reg_write) | ||
| 162 | DEFINE_REG(16, 0, 0x18, (64*MB), reg_init, reg_read, reg_write) | ||
| 163 | DEFINE_REG(17, 0, 0x10, (128*KB), reg_init, reg_read, reg_write) | ||
| 164 | DEFINE_REG(18, 0, 0x10, (1*KB), reg_init, reg_read, reg_write) | ||
| 165 | }; | ||
| 166 | |||
| 167 | static void __init init_sim_regs(void) | ||
| 168 | { | ||
| 169 | int i; | ||
| 170 | |||
| 171 | for (i = 0; i < ARRAY_SIZE(bus1_fixups); i++) { | ||
| 172 | if (bus1_fixups[i].init) | ||
| 173 | bus1_fixups[i].init(&bus1_fixups[i]); | ||
| 174 | } | ||
| 175 | } | ||
| 176 | |||
| 177 | static inline void extract_bytes(u32 *value, int reg, int len) | ||
| 178 | { | ||
| 179 | uint32_t mask; | ||
| 180 | |||
| 181 | *value >>= ((reg & 3) * 8); | ||
| 182 | mask = 0xFFFFFFFF >> ((4 - len) * 8); | ||
| 183 | *value &= mask; | ||
| 184 | } | ||
| 185 | |||
| 186 | int bridge_read(unsigned int devfn, int reg, int len, u32 *value) | ||
| 187 | { | ||
| 188 | u32 av_bridge_base, av_bridge_limit; | ||
| 189 | int retval = 0; | ||
| 190 | |||
| 191 | switch (reg) { | ||
| 192 | /* Make BARs appear to not request any memory. */ | ||
| 193 | case PCI_BASE_ADDRESS_0: | ||
| 194 | case PCI_BASE_ADDRESS_0 + 1: | ||
| 195 | case PCI_BASE_ADDRESS_0 + 2: | ||
| 196 | case PCI_BASE_ADDRESS_0 + 3: | ||
| 197 | *value = 0; | ||
| 198 | break; | ||
| 199 | |||
| 200 | /* Since subordinate bus number register is hardwired | ||
| 201 | * to zero and read only, so do the simulation. | ||
| 202 | */ | ||
| 203 | case PCI_PRIMARY_BUS: | ||
| 204 | if (len == 4) | ||
| 205 | *value = 0x00010100; | ||
| 206 | break; | ||
| 207 | |||
| 208 | case PCI_SUBORDINATE_BUS: | ||
| 209 | *value = 1; | ||
| 210 | break; | ||
| 211 | |||
| 212 | case PCI_MEMORY_BASE: | ||
| 213 | case PCI_MEMORY_LIMIT: | ||
| 214 | /* Get the A/V bridge base address. */ | ||
| 215 | pci_direct_conf1.read(0, 0, devfn, | ||
| 216 | PCI_BASE_ADDRESS_0, 4, &av_bridge_base); | ||
| 217 | |||
| 218 | av_bridge_limit = av_bridge_base + (512*MB - 1); | ||
| 219 | av_bridge_limit >>= 16; | ||
| 220 | av_bridge_limit &= 0xFFF0; | ||
| 221 | |||
| 222 | av_bridge_base >>= 16; | ||
| 223 | av_bridge_base &= 0xFFF0; | ||
| 224 | |||
| 225 | if (reg == PCI_MEMORY_LIMIT) | ||
| 226 | *value = av_bridge_limit; | ||
| 227 | else if (len == 2) | ||
| 228 | *value = av_bridge_base; | ||
| 229 | else | ||
| 230 | *value = (av_bridge_limit << 16) | av_bridge_base; | ||
| 231 | break; | ||
| 232 | /* Make prefetchable memory limit smaller than prefetchable | ||
| 233 | * memory base, so not claim prefetchable memory space. | ||
| 234 | */ | ||
| 235 | case PCI_PREF_MEMORY_BASE: | ||
| 236 | *value = 0xFFF0; | ||
| 237 | break; | ||
| 238 | case PCI_PREF_MEMORY_LIMIT: | ||
| 239 | *value = 0x0; | ||
| 240 | break; | ||
| 241 | /* Make IO limit smaller than IO base, so not claim IO space. */ | ||
| 242 | case PCI_IO_BASE: | ||
| 243 | *value = 0xF0; | ||
| 244 | break; | ||
| 245 | case PCI_IO_LIMIT: | ||
| 246 | *value = 0; | ||
| 247 | break; | ||
| 248 | default: | ||
| 249 | retval = 1; | ||
| 250 | } | ||
| 251 | return retval; | ||
| 252 | } | ||
| 253 | |||
| 254 | static int ce4100_conf_read(unsigned int seg, unsigned int bus, | ||
| 255 | unsigned int devfn, int reg, int len, u32 *value) | ||
| 256 | { | ||
| 257 | int i, retval = 1; | ||
| 258 | |||
| 259 | if (bus == 1) { | ||
| 260 | for (i = 0; i < ARRAY_SIZE(bus1_fixups); i++) { | ||
| 261 | if (bus1_fixups[i].dev_func == devfn && | ||
| 262 | bus1_fixups[i].reg == (reg & ~3) && | ||
| 263 | bus1_fixups[i].read) { | ||
| 264 | bus1_fixups[i].read(&(bus1_fixups[i]), | ||
| 265 | value); | ||
| 266 | extract_bytes(value, reg, len); | ||
| 267 | return 0; | ||
| 268 | } | ||
| 269 | } | ||
| 270 | } | ||
| 271 | |||
| 272 | if (bus == 0 && (PCI_DEVFN(1, 0) == devfn) && | ||
| 273 | !bridge_read(devfn, reg, len, value)) | ||
| 274 | return 0; | ||
| 275 | |||
| 276 | return pci_direct_conf1.read(seg, bus, devfn, reg, len, value); | ||
| 277 | } | ||
| 278 | |||
| 279 | static int ce4100_conf_write(unsigned int seg, unsigned int bus, | ||
| 280 | unsigned int devfn, int reg, int len, u32 value) | ||
| 281 | { | ||
| 282 | int i; | ||
| 283 | |||
| 284 | if (bus == 1) { | ||
| 285 | for (i = 0; i < ARRAY_SIZE(bus1_fixups); i++) { | ||
| 286 | if (bus1_fixups[i].dev_func == devfn && | ||
| 287 | bus1_fixups[i].reg == (reg & ~3) && | ||
| 288 | bus1_fixups[i].write) { | ||
| 289 | bus1_fixups[i].write(&(bus1_fixups[i]), | ||
| 290 | value); | ||
| 291 | return 0; | ||
| 292 | } | ||
| 293 | } | ||
| 294 | } | ||
| 295 | |||
| 296 | /* Discard writes to A/V bridge BAR. */ | ||
| 297 | if (bus == 0 && PCI_DEVFN(1, 0) == devfn && | ||
| 298 | ((reg & ~3) == PCI_BASE_ADDRESS_0)) | ||
| 299 | return 0; | ||
| 300 | |||
| 301 | return pci_direct_conf1.write(seg, bus, devfn, reg, len, value); | ||
| 302 | } | ||
| 303 | |||
| 304 | struct pci_raw_ops ce4100_pci_conf = { | ||
| 305 | .read = ce4100_conf_read, | ||
| 306 | .write = ce4100_conf_write, | ||
| 307 | }; | ||
| 308 | |||
| 309 | static int __init ce4100_pci_init(void) | ||
| 310 | { | ||
| 311 | init_sim_regs(); | ||
| 312 | raw_pci_ops = &ce4100_pci_conf; | ||
| 313 | return 0; | ||
| 314 | } | ||
| 315 | subsys_initcall(ce4100_pci_init); | ||
diff --git a/arch/x86/platform/Makefile b/arch/x86/platform/Makefile index 7bf70b812fa..021eee91c05 100644 --- a/arch/x86/platform/Makefile +++ b/arch/x86/platform/Makefile | |||
| @@ -1,5 +1,7 @@ | |||
| 1 | # Platform specific code goes here | 1 | # Platform specific code goes here |
| 2 | obj-y += ce4100/ | ||
| 2 | obj-y += efi/ | 3 | obj-y += efi/ |
| 4 | obj-y += iris/ | ||
| 3 | obj-y += mrst/ | 5 | obj-y += mrst/ |
| 4 | obj-y += olpc/ | 6 | obj-y += olpc/ |
| 5 | obj-y += scx200/ | 7 | obj-y += scx200/ |
diff --git a/arch/x86/platform/ce4100/Makefile b/arch/x86/platform/ce4100/Makefile new file mode 100644 index 00000000000..91fc92971d9 --- /dev/null +++ b/arch/x86/platform/ce4100/Makefile | |||
| @@ -0,0 +1 @@ | |||
| obj-$(CONFIG_X86_INTEL_CE) += ce4100.o | |||
diff --git a/arch/x86/platform/ce4100/ce4100.c b/arch/x86/platform/ce4100/ce4100.c new file mode 100644 index 00000000000..d2c0d51a717 --- /dev/null +++ b/arch/x86/platform/ce4100/ce4100.c | |||
| @@ -0,0 +1,132 @@ | |||
| 1 | /* | ||
| 2 | * Intel CE4100 platform specific setup code | ||
| 3 | * | ||
| 4 | * (C) Copyright 2010 Intel Corporation | ||
| 5 | * | ||
| 6 | * This program is free software; you can redistribute it and/or | ||
| 7 | * modify it under the terms of the GNU General Public License | ||
| 8 | * as published by the Free Software Foundation; version 2 | ||
| 9 | * of the License. | ||
| 10 | */ | ||
| 11 | #include <linux/init.h> | ||
| 12 | #include <linux/kernel.h> | ||
| 13 | #include <linux/irq.h> | ||
| 14 | #include <linux/module.h> | ||
| 15 | #include <linux/serial_reg.h> | ||
| 16 | #include <linux/serial_8250.h> | ||
| 17 | |||
| 18 | #include <asm/setup.h> | ||
| 19 | #include <asm/io.h> | ||
| 20 | |||
| 21 | static int ce4100_i8042_detect(void) | ||
| 22 | { | ||
| 23 | return 0; | ||
| 24 | } | ||
| 25 | |||
| 26 | static void __init sdv_find_smp_config(void) | ||
| 27 | { | ||
| 28 | } | ||
| 29 | |||
| 30 | #ifdef CONFIG_SERIAL_8250 | ||
| 31 | |||
| 32 | |||
| 33 | static unsigned int mem_serial_in(struct uart_port *p, int offset) | ||
| 34 | { | ||
| 35 | offset = offset << p->regshift; | ||
| 36 | return readl(p->membase + offset); | ||
| 37 | } | ||
| 38 | |||
| 39 | /* | ||
| 40 | * The UART Tx interrupts are not set under some conditions and therefore serial | ||
| 41 | * transmission hangs. This is a silicon issue and has not been root caused. The | ||
| 42 | * workaround for this silicon issue checks UART_LSR_THRE bit and UART_LSR_TEMT | ||
| 43 | * bit of LSR register in interrupt handler to see whether at least one of these | ||
| 44 | * two bits is set, if so then process the transmit request. If this workaround | ||
| 45 | * is not applied, then the serial transmission may hang. This workaround is for | ||
| 46 | * errata number 9 in Errata - B step. | ||
| 47 | */ | ||
| 48 | |||
| 49 | static unsigned int ce4100_mem_serial_in(struct uart_port *p, int offset) | ||
| 50 | { | ||
| 51 | unsigned int ret, ier, lsr; | ||
| 52 | |||
| 53 | if (offset == UART_IIR) { | ||
| 54 | offset = offset << p->regshift; | ||
| 55 | ret = readl(p->membase + offset); | ||
| 56 | if (ret & UART_IIR_NO_INT) { | ||
| 57 | /* see if the TX interrupt should have really set */ | ||
| 58 | ier = mem_serial_in(p, UART_IER); | ||
| 59 | /* see if the UART's XMIT interrupt is enabled */ | ||
| 60 | if (ier & UART_IER_THRI) { | ||
| 61 | lsr = mem_serial_in(p, UART_LSR); | ||
| 62 | /* now check to see if the UART should be | ||
| 63 | generating an interrupt (but isn't) */ | ||
| 64 | if (lsr & (UART_LSR_THRE | UART_LSR_TEMT)) | ||
| 65 | ret &= ~UART_IIR_NO_INT; | ||
| 66 | } | ||
| 67 | } | ||
| 68 | } else | ||
| 69 | ret = mem_serial_in(p, offset); | ||
| 70 | return ret; | ||
| 71 | } | ||
| 72 | |||
| 73 | static void ce4100_mem_serial_out(struct uart_port *p, int offset, int value) | ||
| 74 | { | ||
| 75 | offset = offset << p->regshift; | ||
| 76 | writel(value, p->membase + offset); | ||
| 77 | } | ||
| 78 | |||
| 79 | static void ce4100_serial_fixup(int port, struct uart_port *up, | ||
| 80 | unsigned short *capabilites) | ||
| 81 | { | ||
| 82 | #ifdef CONFIG_EARLY_PRINTK | ||
| 83 | /* | ||
| 84 | * Over ride the legacy port configuration that comes from | ||
| 85 | * asm/serial.h. Using the ioport driver then switching to the | ||
| 86 | * PCI memmaped driver hangs the IOAPIC | ||
| 87 | */ | ||
| 88 | if (up->iotype != UPIO_MEM32) { | ||
| 89 | up->uartclk = 14745600; | ||
| 90 | up->mapbase = 0xdffe0200; | ||
| 91 | set_fixmap_nocache(FIX_EARLYCON_MEM_BASE, | ||
| 92 | up->mapbase & PAGE_MASK); | ||
| 93 | up->membase = | ||
| 94 | (void __iomem *)__fix_to_virt(FIX_EARLYCON_MEM_BASE); | ||
| 95 | up->membase += up->mapbase & ~PAGE_MASK; | ||
| 96 | up->iotype = UPIO_MEM32; | ||
| 97 | up->regshift = 2; | ||
| 98 | } | ||
| 99 | #endif | ||
| 100 | up->iobase = 0; | ||
| 101 | up->serial_in = ce4100_mem_serial_in; | ||
| 102 | up->serial_out = ce4100_mem_serial_out; | ||
| 103 | |||
| 104 | *capabilites |= (1 << 12); | ||
| 105 | } | ||
| 106 | |||
| 107 | static __init void sdv_serial_fixup(void) | ||
| 108 | { | ||
| 109 | serial8250_set_isa_configurator(ce4100_serial_fixup); | ||
| 110 | } | ||
| 111 | |||
| 112 | #else | ||
| 113 | static inline void sdv_serial_fixup(void); | ||
| 114 | #endif | ||
| 115 | |||
| 116 | static void __init sdv_arch_setup(void) | ||
| 117 | { | ||
| 118 | sdv_serial_fixup(); | ||
| 119 | } | ||
| 120 | |||
| 121 | /* | ||
| 122 | * CE4100 specific x86_init function overrides and early setup | ||
| 123 | * calls. | ||
| 124 | */ | ||
| 125 | void __init x86_ce4100_early_setup(void) | ||
| 126 | { | ||
| 127 | x86_init.oem.arch_setup = sdv_arch_setup; | ||
| 128 | x86_platform.i8042_detect = ce4100_i8042_detect; | ||
| 129 | x86_init.resources.probe_roms = x86_init_noop; | ||
| 130 | x86_init.mpparse.get_smp_config = x86_init_uint_noop; | ||
| 131 | x86_init.mpparse.find_smp_config = sdv_find_smp_config; | ||
| 132 | } | ||
diff --git a/arch/x86/platform/iris/Makefile b/arch/x86/platform/iris/Makefile new file mode 100644 index 00000000000..db921983a10 --- /dev/null +++ b/arch/x86/platform/iris/Makefile | |||
| @@ -0,0 +1 @@ | |||
| obj-$(CONFIG_X86_32_IRIS) += iris.o | |||
diff --git a/arch/x86/platform/iris/iris.c b/arch/x86/platform/iris/iris.c new file mode 100644 index 00000000000..1ba7f5ed8c9 --- /dev/null +++ b/arch/x86/platform/iris/iris.c | |||
| @@ -0,0 +1,91 @@ | |||
| 1 | /* | ||
| 2 | * Eurobraille/Iris power off support. | ||
| 3 | * | ||
| 4 | * Eurobraille's Iris machine is a PC with no APM or ACPI support. | ||
| 5 | * It is shutdown by a special I/O sequence which this module provides. | ||
| 6 | * | ||
| 7 | * Copyright (C) Shérab <Sebastien.Hinderer@ens-lyon.org> | ||
| 8 | * | ||
| 9 | * This program is free software ; you can redistribute it and/or modify | ||
| 10 | * it under the terms of the GNU General Public License as published by | ||
| 11 | * the Free Software Foundation ; either version 2 of the License, or | ||
| 12 | * (at your option) any later version. | ||
| 13 | * | ||
| 14 | * This program is distributed in the hope that it will be useful, | ||
| 15 | * but WITHOUT ANY WARRANTY ; without even the implied warranty of | ||
| 16 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
| 17 | * GNU General Public License for more details. | ||
| 18 | * | ||
| 19 | * You should have received a copy of the GNU General Public License | ||
| 20 | * along with the program ; if not, write to the Free Software | ||
| 21 | * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. | ||
| 22 | */ | ||
| 23 | |||
| 24 | #include <linux/moduleparam.h> | ||
| 25 | #include <linux/module.h> | ||
| 26 | #include <linux/kernel.h> | ||
| 27 | #include <linux/errno.h> | ||
| 28 | #include <linux/delay.h> | ||
| 29 | #include <linux/init.h> | ||
| 30 | #include <linux/pm.h> | ||
| 31 | #include <asm/io.h> | ||
| 32 | |||
| 33 | #define IRIS_GIO_BASE 0x340 | ||
| 34 | #define IRIS_GIO_INPUT IRIS_GIO_BASE | ||
| 35 | #define IRIS_GIO_OUTPUT (IRIS_GIO_BASE + 1) | ||
| 36 | #define IRIS_GIO_PULSE 0x80 /* First byte to send */ | ||
| 37 | #define IRIS_GIO_REST 0x00 /* Second byte to send */ | ||
| 38 | #define IRIS_GIO_NODEV 0xff /* Likely not an Iris */ | ||
| 39 | |||
| 40 | MODULE_LICENSE("GPL"); | ||
| 41 | MODULE_AUTHOR("Sébastien Hinderer <Sebastien.Hinderer@ens-lyon.org>"); | ||
| 42 | MODULE_DESCRIPTION("A power_off handler for Iris devices from EuroBraille"); | ||
| 43 | MODULE_SUPPORTED_DEVICE("Eurobraille/Iris"); | ||
| 44 | |||
| 45 | static int force; | ||
| 46 | |||
| 47 | module_param(force, bool, 0); | ||
| 48 | MODULE_PARM_DESC(force, "Set to one to force poweroff handler installation."); | ||
| 49 | |||
| 50 | static void (*old_pm_power_off)(void); | ||
| 51 | |||
| 52 | static void iris_power_off(void) | ||
| 53 | { | ||
| 54 | outb(IRIS_GIO_PULSE, IRIS_GIO_OUTPUT); | ||
| 55 | msleep(850); | ||
| 56 | outb(IRIS_GIO_REST, IRIS_GIO_OUTPUT); | ||
| 57 | } | ||
| 58 | |||
| 59 | /* | ||
| 60 | * Before installing the power_off handler, try to make sure the OS is | ||
| 61 | * running on an Iris. Since Iris does not support DMI, this is done | ||
| 62 | * by reading its input port and seeing whether the read value is | ||
| 63 | * meaningful. | ||
| 64 | */ | ||
| 65 | static int iris_init(void) | ||
| 66 | { | ||
| 67 | unsigned char status; | ||
| 68 | if (force != 1) { | ||
| 69 | printk(KERN_ERR "The force parameter has not been set to 1 so the Iris poweroff handler will not be installed.\n"); | ||
| 70 | return -ENODEV; | ||
| 71 | } | ||
| 72 | status = inb(IRIS_GIO_INPUT); | ||
| 73 | if (status == IRIS_GIO_NODEV) { | ||
| 74 | printk(KERN_ERR "This machine does not seem to be an Iris. Power_off handler not installed.\n"); | ||
| 75 | return -ENODEV; | ||
| 76 | } | ||
| 77 | old_pm_power_off = pm_power_off; | ||
| 78 | pm_power_off = &iris_power_off; | ||
| 79 | printk(KERN_INFO "Iris power_off handler installed.\n"); | ||
| 80 | |||
| 81 | return 0; | ||
| 82 | } | ||
| 83 | |||
| 84 | static void iris_exit(void) | ||
| 85 | { | ||
| 86 | pm_power_off = old_pm_power_off; | ||
| 87 | printk(KERN_INFO "Iris power_off handler uninstalled.\n"); | ||
| 88 | } | ||
| 89 | |||
| 90 | module_init(iris_init); | ||
| 91 | module_exit(iris_exit); | ||
diff --git a/arch/x86/platform/mrst/Makefile b/arch/x86/platform/mrst/Makefile index efbbc552fa9..f61ccdd4934 100644 --- a/arch/x86/platform/mrst/Makefile +++ b/arch/x86/platform/mrst/Makefile | |||
| @@ -1 +1,3 @@ | |||
| 1 | obj-$(CONFIG_X86_MRST) += mrst.o | 1 | obj-$(CONFIG_X86_MRST) += mrst.o |
| 2 | obj-$(CONFIG_X86_MRST) += vrtc.o | ||
| 3 | obj-$(CONFIG_EARLY_PRINTK_MRST) += early_printk_mrst.o | ||
diff --git a/arch/x86/kernel/early_printk_mrst.c b/arch/x86/platform/mrst/early_printk_mrst.c index 65df603622b..65df603622b 100644 --- a/arch/x86/kernel/early_printk_mrst.c +++ b/arch/x86/platform/mrst/early_printk_mrst.c | |||
diff --git a/arch/x86/platform/mrst/mrst.c b/arch/x86/platform/mrst/mrst.c index 79ae68154e8..fee0b4914e0 100644 --- a/arch/x86/platform/mrst/mrst.c +++ b/arch/x86/platform/mrst/mrst.c | |||
| @@ -9,9 +9,19 @@ | |||
| 9 | * as published by the Free Software Foundation; version 2 | 9 | * as published by the Free Software Foundation; version 2 |
| 10 | * of the License. | 10 | * of the License. |
| 11 | */ | 11 | */ |
| 12 | |||
| 13 | #define pr_fmt(fmt) "mrst: " fmt | ||
| 14 | |||
| 12 | #include <linux/init.h> | 15 | #include <linux/init.h> |
| 13 | #include <linux/kernel.h> | 16 | #include <linux/kernel.h> |
| 14 | #include <linux/sfi.h> | 17 | #include <linux/sfi.h> |
| 18 | #include <linux/intel_pmic_gpio.h> | ||
| 19 | #include <linux/spi/spi.h> | ||
| 20 | #include <linux/i2c.h> | ||
| 21 | #include <linux/i2c/pca953x.h> | ||
| 22 | #include <linux/gpio_keys.h> | ||
| 23 | #include <linux/input.h> | ||
| 24 | #include <linux/platform_device.h> | ||
| 15 | #include <linux/irq.h> | 25 | #include <linux/irq.h> |
| 16 | #include <linux/module.h> | 26 | #include <linux/module.h> |
| 17 | 27 | ||
| @@ -23,7 +33,9 @@ | |||
| 23 | #include <asm/mrst.h> | 33 | #include <asm/mrst.h> |
| 24 | #include <asm/io.h> | 34 | #include <asm/io.h> |
| 25 | #include <asm/i8259.h> | 35 | #include <asm/i8259.h> |
| 36 | #include <asm/intel_scu_ipc.h> | ||
| 26 | #include <asm/apb_timer.h> | 37 | #include <asm/apb_timer.h> |
| 38 | #include <asm/reboot.h> | ||
| 27 | 39 | ||
| 28 | /* | 40 | /* |
| 29 | * the clockevent devices on Moorestown/Medfield can be APBT or LAPIC clock, | 41 | * the clockevent devices on Moorestown/Medfield can be APBT or LAPIC clock, |
| @@ -102,10 +114,10 @@ static int __init sfi_parse_mtmr(struct sfi_table_header *table) | |||
| 102 | memcpy(sfi_mtimer_array, pentry, totallen); | 114 | memcpy(sfi_mtimer_array, pentry, totallen); |
| 103 | } | 115 | } |
| 104 | 116 | ||
| 105 | printk(KERN_INFO "SFI: MTIMER info (num = %d):\n", sfi_mtimer_num); | 117 | pr_debug("SFI MTIMER info (num = %d):\n", sfi_mtimer_num); |
| 106 | pentry = sfi_mtimer_array; | 118 | pentry = sfi_mtimer_array; |
| 107 | for (totallen = 0; totallen < sfi_mtimer_num; totallen++, pentry++) { | 119 | for (totallen = 0; totallen < sfi_mtimer_num; totallen++, pentry++) { |
| 108 | printk(KERN_INFO "timer[%d]: paddr = 0x%08x, freq = %dHz," | 120 | pr_debug("timer[%d]: paddr = 0x%08x, freq = %dHz," |
| 109 | " irq = %d\n", totallen, (u32)pentry->phys_addr, | 121 | " irq = %d\n", totallen, (u32)pentry->phys_addr, |
| 110 | pentry->freq_hz, pentry->irq); | 122 | pentry->freq_hz, pentry->irq); |
| 111 | if (!pentry->irq) | 123 | if (!pentry->irq) |
| @@ -176,14 +188,14 @@ int __init sfi_parse_mrtc(struct sfi_table_header *table) | |||
| 176 | memcpy(sfi_mrtc_array, pentry, totallen); | 188 | memcpy(sfi_mrtc_array, pentry, totallen); |
| 177 | } | 189 | } |
| 178 | 190 | ||
| 179 | printk(KERN_INFO "SFI: RTC info (num = %d):\n", sfi_mrtc_num); | 191 | pr_debug("SFI RTC info (num = %d):\n", sfi_mrtc_num); |
| 180 | pentry = sfi_mrtc_array; | 192 | pentry = sfi_mrtc_array; |
| 181 | for (totallen = 0; totallen < sfi_mrtc_num; totallen++, pentry++) { | 193 | for (totallen = 0; totallen < sfi_mrtc_num; totallen++, pentry++) { |
| 182 | printk(KERN_INFO "RTC[%d]: paddr = 0x%08x, irq = %d\n", | 194 | pr_debug("RTC[%d]: paddr = 0x%08x, irq = %d\n", |
| 183 | totallen, (u32)pentry->phys_addr, pentry->irq); | 195 | totallen, (u32)pentry->phys_addr, pentry->irq); |
| 184 | mp_irq.type = MP_IOAPIC; | 196 | mp_irq.type = MP_IOAPIC; |
| 185 | mp_irq.irqtype = mp_INT; | 197 | mp_irq.irqtype = mp_INT; |
| 186 | mp_irq.irqflag = 0; | 198 | mp_irq.irqflag = 0xf; /* level trigger and active low */ |
| 187 | mp_irq.srcbus = 0; | 199 | mp_irq.srcbus = 0; |
| 188 | mp_irq.srcbusirq = pentry->irq; /* IRQ */ | 200 | mp_irq.srcbusirq = pentry->irq; /* IRQ */ |
| 189 | mp_irq.dstapic = MP_APIC_ALL; | 201 | mp_irq.dstapic = MP_APIC_ALL; |
| @@ -209,6 +221,7 @@ static unsigned long __init mrst_calibrate_tsc(void) | |||
| 209 | 221 | ||
| 210 | void __init mrst_time_init(void) | 222 | void __init mrst_time_init(void) |
| 211 | { | 223 | { |
| 224 | sfi_table_parse(SFI_SIG_MTMR, NULL, NULL, sfi_parse_mtmr); | ||
| 212 | switch (mrst_timer_options) { | 225 | switch (mrst_timer_options) { |
| 213 | case MRST_TIMER_APBT_ONLY: | 226 | case MRST_TIMER_APBT_ONLY: |
| 214 | break; | 227 | break; |
| @@ -224,16 +237,10 @@ void __init mrst_time_init(void) | |||
| 224 | return; | 237 | return; |
| 225 | } | 238 | } |
| 226 | /* we need at least one APB timer */ | 239 | /* we need at least one APB timer */ |
| 227 | sfi_table_parse(SFI_SIG_MTMR, NULL, NULL, sfi_parse_mtmr); | ||
| 228 | pre_init_apic_IRQ0(); | 240 | pre_init_apic_IRQ0(); |
| 229 | apbt_time_init(); | 241 | apbt_time_init(); |
| 230 | } | 242 | } |
| 231 | 243 | ||
| 232 | void __init mrst_rtc_init(void) | ||
| 233 | { | ||
| 234 | sfi_table_parse(SFI_SIG_MRTC, NULL, NULL, sfi_parse_mrtc); | ||
| 235 | } | ||
| 236 | |||
| 237 | void __cpuinit mrst_arch_setup(void) | 244 | void __cpuinit mrst_arch_setup(void) |
| 238 | { | 245 | { |
| 239 | if (boot_cpu_data.x86 == 6 && boot_cpu_data.x86_model == 0x27) | 246 | if (boot_cpu_data.x86 == 6 && boot_cpu_data.x86_model == 0x27) |
| @@ -256,6 +263,17 @@ static int mrst_i8042_detect(void) | |||
| 256 | return 0; | 263 | return 0; |
| 257 | } | 264 | } |
| 258 | 265 | ||
| 266 | /* Reboot and power off are handled by the SCU on a MID device */ | ||
| 267 | static void mrst_power_off(void) | ||
| 268 | { | ||
| 269 | intel_scu_ipc_simple_command(0xf1, 1); | ||
| 270 | } | ||
| 271 | |||
| 272 | static void mrst_reboot(void) | ||
| 273 | { | ||
| 274 | intel_scu_ipc_simple_command(0xf1, 0); | ||
| 275 | } | ||
| 276 | |||
| 259 | /* | 277 | /* |
| 260 | * Moorestown specific x86_init function overrides and early setup | 278 | * Moorestown specific x86_init function overrides and early setup |
| 261 | * calls. | 279 | * calls. |
| @@ -281,6 +299,10 @@ void __init x86_mrst_early_setup(void) | |||
| 281 | 299 | ||
| 282 | legacy_pic = &null_legacy_pic; | 300 | legacy_pic = &null_legacy_pic; |
| 283 | 301 | ||
| 302 | /* Moorestown specific power_off/restart method */ | ||
| 303 | pm_power_off = mrst_power_off; | ||
| 304 | machine_ops.emergency_restart = mrst_reboot; | ||
| 305 | |||
| 284 | /* Avoid searching for BIOS MP tables */ | 306 | /* Avoid searching for BIOS MP tables */ |
| 285 | x86_init.mpparse.find_smp_config = x86_init_noop; | 307 | x86_init.mpparse.find_smp_config = x86_init_noop; |
| 286 | x86_init.mpparse.get_smp_config = x86_init_uint_noop; | 308 | x86_init.mpparse.get_smp_config = x86_init_uint_noop; |
| @@ -309,3 +331,505 @@ static inline int __init setup_x86_mrst_timer(char *arg) | |||
| 309 | return 0; | 331 | return 0; |
| 310 | } | 332 | } |
| 311 | __setup("x86_mrst_timer=", setup_x86_mrst_timer); | 333 | __setup("x86_mrst_timer=", setup_x86_mrst_timer); |
| 334 | |||
| 335 | /* | ||
| 336 | * Parsing GPIO table first, since the DEVS table will need this table | ||
| 337 | * to map the pin name to the actual pin. | ||
| 338 | */ | ||
| 339 | static struct sfi_gpio_table_entry *gpio_table; | ||
| 340 | static int gpio_num_entry; | ||
| 341 | |||
| 342 | static int __init sfi_parse_gpio(struct sfi_table_header *table) | ||
| 343 | { | ||
| 344 | struct sfi_table_simple *sb; | ||
| 345 | struct sfi_gpio_table_entry *pentry; | ||
| 346 | int num, i; | ||
| 347 | |||
| 348 | if (gpio_table) | ||
| 349 | return 0; | ||
| 350 | sb = (struct sfi_table_simple *)table; | ||
| 351 | num = SFI_GET_NUM_ENTRIES(sb, struct sfi_gpio_table_entry); | ||
| 352 | pentry = (struct sfi_gpio_table_entry *)sb->pentry; | ||
| 353 | |||
| 354 | gpio_table = (struct sfi_gpio_table_entry *) | ||
| 355 | kmalloc(num * sizeof(*pentry), GFP_KERNEL); | ||
| 356 | if (!gpio_table) | ||
| 357 | return -1; | ||
| 358 | memcpy(gpio_table, pentry, num * sizeof(*pentry)); | ||
| 359 | gpio_num_entry = num; | ||
| 360 | |||
| 361 | pr_debug("GPIO pin info:\n"); | ||
| 362 | for (i = 0; i < num; i++, pentry++) | ||
| 363 | pr_debug("info[%2d]: controller = %16.16s, pin_name = %16.16s," | ||
| 364 | " pin = %d\n", i, | ||
| 365 | pentry->controller_name, | ||
| 366 | pentry->pin_name, | ||
| 367 | pentry->pin_no); | ||
| 368 | return 0; | ||
| 369 | } | ||
| 370 | |||
| 371 | static int get_gpio_by_name(const char *name) | ||
| 372 | { | ||
| 373 | struct sfi_gpio_table_entry *pentry = gpio_table; | ||
| 374 | int i; | ||
| 375 | |||
| 376 | if (!pentry) | ||
| 377 | return -1; | ||
| 378 | for (i = 0; i < gpio_num_entry; i++, pentry++) { | ||
| 379 | if (!strncmp(name, pentry->pin_name, SFI_NAME_LEN)) | ||
| 380 | return pentry->pin_no; | ||
| 381 | } | ||
| 382 | return -1; | ||
| 383 | } | ||
| 384 | |||
| 385 | /* | ||
| 386 | * Here defines the array of devices platform data that IAFW would export | ||
| 387 | * through SFI "DEVS" table, we use name and type to match the device and | ||
| 388 | * its platform data. | ||
| 389 | */ | ||
| 390 | struct devs_id { | ||
| 391 | char name[SFI_NAME_LEN + 1]; | ||
| 392 | u8 type; | ||
| 393 | u8 delay; | ||
| 394 | void *(*get_platform_data)(void *info); | ||
| 395 | }; | ||
| 396 | |||
| 397 | /* the offset for the mapping of global gpio pin to irq */ | ||
| 398 | #define MRST_IRQ_OFFSET 0x100 | ||
| 399 | |||
| 400 | static void __init *pmic_gpio_platform_data(void *info) | ||
| 401 | { | ||
| 402 | static struct intel_pmic_gpio_platform_data pmic_gpio_pdata; | ||
| 403 | int gpio_base = get_gpio_by_name("pmic_gpio_base"); | ||
| 404 | |||
| 405 | if (gpio_base == -1) | ||
| 406 | gpio_base = 64; | ||
| 407 | pmic_gpio_pdata.gpio_base = gpio_base; | ||
| 408 | pmic_gpio_pdata.irq_base = gpio_base + MRST_IRQ_OFFSET; | ||
| 409 | pmic_gpio_pdata.gpiointr = 0xffffeff8; | ||
| 410 | |||
| 411 | return &pmic_gpio_pdata; | ||
| 412 | } | ||
| 413 | |||
| 414 | static void __init *max3111_platform_data(void *info) | ||
| 415 | { | ||
| 416 | struct spi_board_info *spi_info = info; | ||
| 417 | int intr = get_gpio_by_name("max3111_int"); | ||
| 418 | |||
| 419 | if (intr == -1) | ||
| 420 | return NULL; | ||
| 421 | spi_info->irq = intr + MRST_IRQ_OFFSET; | ||
| 422 | return NULL; | ||
| 423 | } | ||
| 424 | |||
| 425 | /* we have multiple max7315 on the board ... */ | ||
| 426 | #define MAX7315_NUM 2 | ||
| 427 | static void __init *max7315_platform_data(void *info) | ||
| 428 | { | ||
| 429 | static struct pca953x_platform_data max7315_pdata[MAX7315_NUM]; | ||
| 430 | static int nr; | ||
| 431 | struct pca953x_platform_data *max7315 = &max7315_pdata[nr]; | ||
| 432 | struct i2c_board_info *i2c_info = info; | ||
| 433 | int gpio_base, intr; | ||
| 434 | char base_pin_name[SFI_NAME_LEN + 1]; | ||
| 435 | char intr_pin_name[SFI_NAME_LEN + 1]; | ||
| 436 | |||
| 437 | if (nr == MAX7315_NUM) { | ||
| 438 | pr_err("too many max7315s, we only support %d\n", | ||
| 439 | MAX7315_NUM); | ||
| 440 | return NULL; | ||
| 441 | } | ||
| 442 | /* we have several max7315 on the board, we only need load several | ||
| 443 | * instances of the same pca953x driver to cover them | ||
| 444 | */ | ||
| 445 | strcpy(i2c_info->type, "max7315"); | ||
| 446 | if (nr++) { | ||
| 447 | sprintf(base_pin_name, "max7315_%d_base", nr); | ||
| 448 | sprintf(intr_pin_name, "max7315_%d_int", nr); | ||
| 449 | } else { | ||
| 450 | strcpy(base_pin_name, "max7315_base"); | ||
| 451 | strcpy(intr_pin_name, "max7315_int"); | ||
| 452 | } | ||
| 453 | |||
| 454 | gpio_base = get_gpio_by_name(base_pin_name); | ||
| 455 | intr = get_gpio_by_name(intr_pin_name); | ||
| 456 | |||
| 457 | if (gpio_base == -1) | ||
| 458 | return NULL; | ||
| 459 | max7315->gpio_base = gpio_base; | ||
| 460 | if (intr != -1) { | ||
| 461 | i2c_info->irq = intr + MRST_IRQ_OFFSET; | ||
| 462 | max7315->irq_base = gpio_base + MRST_IRQ_OFFSET; | ||
| 463 | } else { | ||
| 464 | i2c_info->irq = -1; | ||
| 465 | max7315->irq_base = -1; | ||
| 466 | } | ||
| 467 | return max7315; | ||
| 468 | } | ||
| 469 | |||
| 470 | static void __init *emc1403_platform_data(void *info) | ||
| 471 | { | ||
| 472 | static short intr2nd_pdata; | ||
| 473 | struct i2c_board_info *i2c_info = info; | ||
| 474 | int intr = get_gpio_by_name("thermal_int"); | ||
| 475 | int intr2nd = get_gpio_by_name("thermal_alert"); | ||
| 476 | |||
| 477 | if (intr == -1 || intr2nd == -1) | ||
| 478 | return NULL; | ||
| 479 | |||
| 480 | i2c_info->irq = intr + MRST_IRQ_OFFSET; | ||
| 481 | intr2nd_pdata = intr2nd + MRST_IRQ_OFFSET; | ||
| 482 | |||
| 483 | return &intr2nd_pdata; | ||
| 484 | } | ||
| 485 | |||
| 486 | static void __init *lis331dl_platform_data(void *info) | ||
| 487 | { | ||
| 488 | static short intr2nd_pdata; | ||
| 489 | struct i2c_board_info *i2c_info = info; | ||
| 490 | int intr = get_gpio_by_name("accel_int"); | ||
| 491 | int intr2nd = get_gpio_by_name("accel_2"); | ||
| 492 | |||
| 493 | if (intr == -1 || intr2nd == -1) | ||
| 494 | return NULL; | ||
| 495 | |||
| 496 | i2c_info->irq = intr + MRST_IRQ_OFFSET; | ||
| 497 | intr2nd_pdata = intr2nd + MRST_IRQ_OFFSET; | ||
| 498 | |||
| 499 | return &intr2nd_pdata; | ||
| 500 | } | ||
| 501 | |||
| 502 | static void __init *no_platform_data(void *info) | ||
| 503 | { | ||
| 504 | return NULL; | ||
| 505 | } | ||
| 506 | |||
| 507 | static const struct devs_id __initconst device_ids[] = { | ||
| 508 | {"pmic_gpio", SFI_DEV_TYPE_SPI, 1, &pmic_gpio_platform_data}, | ||
| 509 | {"spi_max3111", SFI_DEV_TYPE_SPI, 0, &max3111_platform_data}, | ||
| 510 | {"i2c_max7315", SFI_DEV_TYPE_I2C, 1, &max7315_platform_data}, | ||
| 511 | {"i2c_max7315_2", SFI_DEV_TYPE_I2C, 1, &max7315_platform_data}, | ||
| 512 | {"emc1403", SFI_DEV_TYPE_I2C, 1, &emc1403_platform_data}, | ||
| 513 | {"i2c_accel", SFI_DEV_TYPE_I2C, 0, &lis331dl_platform_data}, | ||
| 514 | {"pmic_audio", SFI_DEV_TYPE_IPC, 1, &no_platform_data}, | ||
| 515 | {"msic_audio", SFI_DEV_TYPE_IPC, 1, &no_platform_data}, | ||
| 516 | {}, | ||
| 517 | }; | ||
| 518 | |||
| 519 | #define MAX_IPCDEVS 24 | ||
| 520 | static struct platform_device *ipc_devs[MAX_IPCDEVS]; | ||
| 521 | static int ipc_next_dev; | ||
| 522 | |||
| 523 | #define MAX_SCU_SPI 24 | ||
| 524 | static struct spi_board_info *spi_devs[MAX_SCU_SPI]; | ||
| 525 | static int spi_next_dev; | ||
| 526 | |||
| 527 | #define MAX_SCU_I2C 24 | ||
| 528 | static struct i2c_board_info *i2c_devs[MAX_SCU_I2C]; | ||
| 529 | static int i2c_bus[MAX_SCU_I2C]; | ||
| 530 | static int i2c_next_dev; | ||
| 531 | |||
| 532 | static void __init intel_scu_device_register(struct platform_device *pdev) | ||
| 533 | { | ||
| 534 | if(ipc_next_dev == MAX_IPCDEVS) | ||
| 535 | pr_err("too many SCU IPC devices"); | ||
| 536 | else | ||
| 537 | ipc_devs[ipc_next_dev++] = pdev; | ||
| 538 | } | ||
| 539 | |||
| 540 | static void __init intel_scu_spi_device_register(struct spi_board_info *sdev) | ||
| 541 | { | ||
| 542 | struct spi_board_info *new_dev; | ||
| 543 | |||
| 544 | if (spi_next_dev == MAX_SCU_SPI) { | ||
| 545 | pr_err("too many SCU SPI devices"); | ||
| 546 | return; | ||
| 547 | } | ||
| 548 | |||
| 549 | new_dev = kzalloc(sizeof(*sdev), GFP_KERNEL); | ||
| 550 | if (!new_dev) { | ||
| 551 | pr_err("failed to alloc mem for delayed spi dev %s\n", | ||
| 552 | sdev->modalias); | ||
| 553 | return; | ||
| 554 | } | ||
| 555 | memcpy(new_dev, sdev, sizeof(*sdev)); | ||
| 556 | |||
| 557 | spi_devs[spi_next_dev++] = new_dev; | ||
| 558 | } | ||
| 559 | |||
| 560 | static void __init intel_scu_i2c_device_register(int bus, | ||
| 561 | struct i2c_board_info *idev) | ||
| 562 | { | ||
| 563 | struct i2c_board_info *new_dev; | ||
| 564 | |||
| 565 | if (i2c_next_dev == MAX_SCU_I2C) { | ||
| 566 | pr_err("too many SCU I2C devices"); | ||
| 567 | return; | ||
| 568 | } | ||
| 569 | |||
| 570 | new_dev = kzalloc(sizeof(*idev), GFP_KERNEL); | ||
| 571 | if (!new_dev) { | ||
| 572 | pr_err("failed to alloc mem for delayed i2c dev %s\n", | ||
| 573 | idev->type); | ||
| 574 | return; | ||
| 575 | } | ||
| 576 | memcpy(new_dev, idev, sizeof(*idev)); | ||
| 577 | |||
| 578 | i2c_bus[i2c_next_dev] = bus; | ||
| 579 | i2c_devs[i2c_next_dev++] = new_dev; | ||
| 580 | } | ||
| 581 | |||
| 582 | /* Called by IPC driver */ | ||
| 583 | void intel_scu_devices_create(void) | ||
| 584 | { | ||
| 585 | int i; | ||
| 586 | |||
| 587 | for (i = 0; i < ipc_next_dev; i++) | ||
| 588 | platform_device_add(ipc_devs[i]); | ||
| 589 | |||
| 590 | for (i = 0; i < spi_next_dev; i++) | ||
| 591 | spi_register_board_info(spi_devs[i], 1); | ||
| 592 | |||
| 593 | for (i = 0; i < i2c_next_dev; i++) { | ||
| 594 | struct i2c_adapter *adapter; | ||
| 595 | struct i2c_client *client; | ||
| 596 | |||
| 597 | adapter = i2c_get_adapter(i2c_bus[i]); | ||
| 598 | if (adapter) { | ||
| 599 | client = i2c_new_device(adapter, i2c_devs[i]); | ||
| 600 | if (!client) | ||
| 601 | pr_err("can't create i2c device %s\n", | ||
| 602 | i2c_devs[i]->type); | ||
| 603 | } else | ||
| 604 | i2c_register_board_info(i2c_bus[i], i2c_devs[i], 1); | ||
| 605 | } | ||
| 606 | } | ||
| 607 | EXPORT_SYMBOL_GPL(intel_scu_devices_create); | ||
| 608 | |||
| 609 | /* Called by IPC driver */ | ||
| 610 | void intel_scu_devices_destroy(void) | ||
| 611 | { | ||
| 612 | int i; | ||
| 613 | |||
| 614 | for (i = 0; i < ipc_next_dev; i++) | ||
| 615 | platform_device_del(ipc_devs[i]); | ||
| 616 | } | ||
| 617 | EXPORT_SYMBOL_GPL(intel_scu_devices_destroy); | ||
| 618 | |||
| 619 | static void __init install_irq_resource(struct platform_device *pdev, int irq) | ||
| 620 | { | ||
| 621 | /* Single threaded */ | ||
| 622 | static struct resource __initdata res = { | ||
| 623 | .name = "IRQ", | ||
| 624 | .flags = IORESOURCE_IRQ, | ||
| 625 | }; | ||
| 626 | res.start = irq; | ||
| 627 | platform_device_add_resources(pdev, &res, 1); | ||
| 628 | } | ||
| 629 | |||
| 630 | static void __init sfi_handle_ipc_dev(struct platform_device *pdev) | ||
| 631 | { | ||
| 632 | const struct devs_id *dev = device_ids; | ||
| 633 | void *pdata = NULL; | ||
| 634 | |||
| 635 | while (dev->name[0]) { | ||
| 636 | if (dev->type == SFI_DEV_TYPE_IPC && | ||
| 637 | !strncmp(dev->name, pdev->name, SFI_NAME_LEN)) { | ||
| 638 | pdata = dev->get_platform_data(pdev); | ||
| 639 | break; | ||
| 640 | } | ||
| 641 | dev++; | ||
| 642 | } | ||
| 643 | pdev->dev.platform_data = pdata; | ||
| 644 | intel_scu_device_register(pdev); | ||
| 645 | } | ||
| 646 | |||
| 647 | static void __init sfi_handle_spi_dev(struct spi_board_info *spi_info) | ||
| 648 | { | ||
| 649 | const struct devs_id *dev = device_ids; | ||
| 650 | void *pdata = NULL; | ||
| 651 | |||
| 652 | while (dev->name[0]) { | ||
| 653 | if (dev->type == SFI_DEV_TYPE_SPI && | ||
| 654 | !strncmp(dev->name, spi_info->modalias, SFI_NAME_LEN)) { | ||
| 655 | pdata = dev->get_platform_data(spi_info); | ||
| 656 | break; | ||
| 657 | } | ||
| 658 | dev++; | ||
| 659 | } | ||
| 660 | spi_info->platform_data = pdata; | ||
| 661 | if (dev->delay) | ||
| 662 | intel_scu_spi_device_register(spi_info); | ||
| 663 | else | ||
| 664 | spi_register_board_info(spi_info, 1); | ||
| 665 | } | ||
| 666 | |||
| 667 | static void __init sfi_handle_i2c_dev(int bus, struct i2c_board_info *i2c_info) | ||
| 668 | { | ||
| 669 | const struct devs_id *dev = device_ids; | ||
| 670 | void *pdata = NULL; | ||
| 671 | |||
| 672 | while (dev->name[0]) { | ||
| 673 | if (dev->type == SFI_DEV_TYPE_I2C && | ||
| 674 | !strncmp(dev->name, i2c_info->type, SFI_NAME_LEN)) { | ||
| 675 | pdata = dev->get_platform_data(i2c_info); | ||
| 676 | break; | ||
| 677 | } | ||
| 678 | dev++; | ||
| 679 | } | ||
| 680 | i2c_info->platform_data = pdata; | ||
| 681 | |||
| 682 | if (dev->delay) | ||
| 683 | intel_scu_i2c_device_register(bus, i2c_info); | ||
| 684 | else | ||
| 685 | i2c_register_board_info(bus, i2c_info, 1); | ||
| 686 | } | ||
| 687 | |||
| 688 | |||
| 689 | static int __init sfi_parse_devs(struct sfi_table_header *table) | ||
| 690 | { | ||
| 691 | struct sfi_table_simple *sb; | ||
| 692 | struct sfi_device_table_entry *pentry; | ||
| 693 | struct spi_board_info spi_info; | ||
| 694 | struct i2c_board_info i2c_info; | ||
| 695 | struct platform_device *pdev; | ||
| 696 | int num, i, bus; | ||
| 697 | int ioapic; | ||
| 698 | struct io_apic_irq_attr irq_attr; | ||
| 699 | |||
| 700 | sb = (struct sfi_table_simple *)table; | ||
| 701 | num = SFI_GET_NUM_ENTRIES(sb, struct sfi_device_table_entry); | ||
| 702 | pentry = (struct sfi_device_table_entry *)sb->pentry; | ||
| 703 | |||
| 704 | for (i = 0; i < num; i++, pentry++) { | ||
| 705 | if (pentry->irq != (u8)0xff) { /* native RTE case */ | ||
| 706 | /* these SPI2 devices are not exposed to system as PCI | ||
| 707 | * devices, but they have separate RTE entry in IOAPIC | ||
| 708 | * so we have to enable them one by one here | ||
| 709 | */ | ||
| 710 | ioapic = mp_find_ioapic(pentry->irq); | ||
| 711 | irq_attr.ioapic = ioapic; | ||
| 712 | irq_attr.ioapic_pin = pentry->irq; | ||
| 713 | irq_attr.trigger = 1; | ||
| 714 | irq_attr.polarity = 1; | ||
| 715 | io_apic_set_pci_routing(NULL, pentry->irq, &irq_attr); | ||
| 716 | } | ||
| 717 | switch (pentry->type) { | ||
| 718 | case SFI_DEV_TYPE_IPC: | ||
| 719 | /* ID as IRQ is a hack that will go away */ | ||
| 720 | pdev = platform_device_alloc(pentry->name, pentry->irq); | ||
| 721 | if (pdev == NULL) { | ||
| 722 | pr_err("out of memory for SFI platform device '%s'.\n", | ||
| 723 | pentry->name); | ||
| 724 | continue; | ||
| 725 | } | ||
| 726 | install_irq_resource(pdev, pentry->irq); | ||
| 727 | pr_debug("info[%2d]: IPC bus, name = %16.16s, " | ||
| 728 | "irq = 0x%2x\n", i, pentry->name, pentry->irq); | ||
| 729 | sfi_handle_ipc_dev(pdev); | ||
| 730 | break; | ||
| 731 | case SFI_DEV_TYPE_SPI: | ||
| 732 | memset(&spi_info, 0, sizeof(spi_info)); | ||
| 733 | strncpy(spi_info.modalias, pentry->name, SFI_NAME_LEN); | ||
| 734 | spi_info.irq = pentry->irq; | ||
| 735 | spi_info.bus_num = pentry->host_num; | ||
| 736 | spi_info.chip_select = pentry->addr; | ||
| 737 | spi_info.max_speed_hz = pentry->max_freq; | ||
| 738 | pr_debug("info[%2d]: SPI bus = %d, name = %16.16s, " | ||
| 739 | "irq = 0x%2x, max_freq = %d, cs = %d\n", i, | ||
| 740 | spi_info.bus_num, | ||
| 741 | spi_info.modalias, | ||
| 742 | spi_info.irq, | ||
| 743 | spi_info.max_speed_hz, | ||
| 744 | spi_info.chip_select); | ||
| 745 | sfi_handle_spi_dev(&spi_info); | ||
| 746 | break; | ||
| 747 | case SFI_DEV_TYPE_I2C: | ||
| 748 | memset(&i2c_info, 0, sizeof(i2c_info)); | ||
| 749 | bus = pentry->host_num; | ||
| 750 | strncpy(i2c_info.type, pentry->name, SFI_NAME_LEN); | ||
| 751 | i2c_info.irq = pentry->irq; | ||
| 752 | i2c_info.addr = pentry->addr; | ||
| 753 | pr_debug("info[%2d]: I2C bus = %d, name = %16.16s, " | ||
| 754 | "irq = 0x%2x, addr = 0x%x\n", i, bus, | ||
| 755 | i2c_info.type, | ||
| 756 | i2c_info.irq, | ||
| 757 | i2c_info.addr); | ||
| 758 | sfi_handle_i2c_dev(bus, &i2c_info); | ||
| 759 | break; | ||
| 760 | case SFI_DEV_TYPE_UART: | ||
| 761 | case SFI_DEV_TYPE_HSI: | ||
| 762 | default: | ||
| 763 | ; | ||
| 764 | } | ||
| 765 | } | ||
| 766 | return 0; | ||
| 767 | } | ||
| 768 | |||
| 769 | static int __init mrst_platform_init(void) | ||
| 770 | { | ||
| 771 | sfi_table_parse(SFI_SIG_GPIO, NULL, NULL, sfi_parse_gpio); | ||
| 772 | sfi_table_parse(SFI_SIG_DEVS, NULL, NULL, sfi_parse_devs); | ||
| 773 | return 0; | ||
| 774 | } | ||
| 775 | arch_initcall(mrst_platform_init); | ||
| 776 | |||
| 777 | /* | ||
| 778 | * we will search these buttons in SFI GPIO table (by name) | ||
| 779 | * and register them dynamically. Please add all possible | ||
| 780 | * buttons here, we will shrink them if no GPIO found. | ||
| 781 | */ | ||
| 782 | static struct gpio_keys_button gpio_button[] = { | ||
| 783 | {KEY_POWER, -1, 1, "power_btn", EV_KEY, 0, 3000}, | ||
| 784 | {KEY_PROG1, -1, 1, "prog_btn1", EV_KEY, 0, 20}, | ||
| 785 | {KEY_PROG2, -1, 1, "prog_btn2", EV_KEY, 0, 20}, | ||
| 786 | {SW_LID, -1, 1, "lid_switch", EV_SW, 0, 20}, | ||
| 787 | {KEY_VOLUMEUP, -1, 1, "vol_up", EV_KEY, 0, 20}, | ||
| 788 | {KEY_VOLUMEDOWN, -1, 1, "vol_down", EV_KEY, 0, 20}, | ||
| 789 | {KEY_CAMERA, -1, 1, "camera_full", EV_KEY, 0, 20}, | ||
| 790 | {KEY_CAMERA_FOCUS, -1, 1, "camera_half", EV_KEY, 0, 20}, | ||
| 791 | {SW_KEYPAD_SLIDE, -1, 1, "MagSw1", EV_SW, 0, 20}, | ||
| 792 | {SW_KEYPAD_SLIDE, -1, 1, "MagSw2", EV_SW, 0, 20}, | ||
| 793 | }; | ||
| 794 | |||
| 795 | static struct gpio_keys_platform_data mrst_gpio_keys = { | ||
| 796 | .buttons = gpio_button, | ||
| 797 | .rep = 1, | ||
| 798 | .nbuttons = -1, /* will fill it after search */ | ||
| 799 | }; | ||
| 800 | |||
| 801 | static struct platform_device pb_device = { | ||
| 802 | .name = "gpio-keys", | ||
| 803 | .id = -1, | ||
| 804 | .dev = { | ||
| 805 | .platform_data = &mrst_gpio_keys, | ||
| 806 | }, | ||
| 807 | }; | ||
| 808 | |||
| 809 | /* | ||
| 810 | * Shrink the non-existent buttons, register the gpio button | ||
| 811 | * device if there is some | ||
| 812 | */ | ||
| 813 | static int __init pb_keys_init(void) | ||
| 814 | { | ||
| 815 | struct gpio_keys_button *gb = gpio_button; | ||
| 816 | int i, num, good = 0; | ||
| 817 | |||
| 818 | num = sizeof(gpio_button) / sizeof(struct gpio_keys_button); | ||
| 819 | for (i = 0; i < num; i++) { | ||
| 820 | gb[i].gpio = get_gpio_by_name(gb[i].desc); | ||
| 821 | if (gb[i].gpio == -1) | ||
| 822 | continue; | ||
| 823 | |||
| 824 | if (i != good) | ||
| 825 | gb[good] = gb[i]; | ||
| 826 | good++; | ||
| 827 | } | ||
| 828 | |||
| 829 | if (good) { | ||
| 830 | mrst_gpio_keys.nbuttons = good; | ||
| 831 | return platform_device_register(&pb_device); | ||
| 832 | } | ||
| 833 | return 0; | ||
| 834 | } | ||
| 835 | late_initcall(pb_keys_init); | ||
diff --git a/arch/x86/platform/mrst/vrtc.c b/arch/x86/platform/mrst/vrtc.c new file mode 100644 index 00000000000..32cd7edd71a --- /dev/null +++ b/arch/x86/platform/mrst/vrtc.c | |||
| @@ -0,0 +1,165 @@ | |||
| 1 | /* | ||
| 2 | * vrtc.c: Driver for virtual RTC device on Intel MID platform | ||
| 3 | * | ||
| 4 | * (C) Copyright 2009 Intel Corporation | ||
| 5 | * | ||
| 6 | * This program is free software; you can redistribute it and/or | ||
| 7 | * modify it under the terms of the GNU General Public License | ||
| 8 | * as published by the Free Software Foundation; version 2 | ||
| 9 | * of the License. | ||
| 10 | * | ||
| 11 | * Note: | ||
| 12 | * VRTC is emulated by system controller firmware, the real HW | ||
| 13 | * RTC is located in the PMIC device. SCU FW shadows PMIC RTC | ||
| 14 | * in a memory mapped IO space that is visible to the host IA | ||
| 15 | * processor. | ||
| 16 | * | ||
| 17 | * This driver is based on RTC CMOS driver. | ||
| 18 | */ | ||
| 19 | |||
| 20 | #include <linux/kernel.h> | ||
| 21 | #include <linux/init.h> | ||
| 22 | #include <linux/sfi.h> | ||
| 23 | #include <linux/platform_device.h> | ||
| 24 | |||
| 25 | #include <asm/mrst.h> | ||
| 26 | #include <asm/mrst-vrtc.h> | ||
| 27 | #include <asm/time.h> | ||
| 28 | #include <asm/fixmap.h> | ||
| 29 | |||
| 30 | static unsigned char __iomem *vrtc_virt_base; | ||
| 31 | |||
| 32 | unsigned char vrtc_cmos_read(unsigned char reg) | ||
| 33 | { | ||
| 34 | unsigned char retval; | ||
| 35 | |||
| 36 | /* vRTC's registers range from 0x0 to 0xD */ | ||
| 37 | if (reg > 0xd || !vrtc_virt_base) | ||
| 38 | return 0xff; | ||
| 39 | |||
| 40 | lock_cmos_prefix(reg); | ||
| 41 | retval = __raw_readb(vrtc_virt_base + (reg << 2)); | ||
| 42 | lock_cmos_suffix(reg); | ||
| 43 | return retval; | ||
| 44 | } | ||
| 45 | EXPORT_SYMBOL_GPL(vrtc_cmos_read); | ||
| 46 | |||
| 47 | void vrtc_cmos_write(unsigned char val, unsigned char reg) | ||
| 48 | { | ||
| 49 | if (reg > 0xd || !vrtc_virt_base) | ||
| 50 | return; | ||
| 51 | |||
| 52 | lock_cmos_prefix(reg); | ||
| 53 | __raw_writeb(val, vrtc_virt_base + (reg << 2)); | ||
| 54 | lock_cmos_suffix(reg); | ||
| 55 | } | ||
| 56 | EXPORT_SYMBOL_GPL(vrtc_cmos_write); | ||
| 57 | |||
| 58 | unsigned long vrtc_get_time(void) | ||
| 59 | { | ||
| 60 | u8 sec, min, hour, mday, mon; | ||
| 61 | u32 year; | ||
| 62 | |||
| 63 | while ((vrtc_cmos_read(RTC_FREQ_SELECT) & RTC_UIP)) | ||
| 64 | cpu_relax(); | ||
| 65 | |||
| 66 | sec = vrtc_cmos_read(RTC_SECONDS); | ||
| 67 | min = vrtc_cmos_read(RTC_MINUTES); | ||
| 68 | hour = vrtc_cmos_read(RTC_HOURS); | ||
| 69 | mday = vrtc_cmos_read(RTC_DAY_OF_MONTH); | ||
| 70 | mon = vrtc_cmos_read(RTC_MONTH); | ||
| 71 | year = vrtc_cmos_read(RTC_YEAR); | ||
| 72 | |||
| 73 | /* vRTC YEAR reg contains the offset to 1960 */ | ||
| 74 | year += 1960; | ||
| 75 | |||
| 76 | printk(KERN_INFO "vRTC: sec: %d min: %d hour: %d day: %d " | ||
| 77 | "mon: %d year: %d\n", sec, min, hour, mday, mon, year); | ||
| 78 | |||
| 79 | return mktime(year, mon, mday, hour, min, sec); | ||
| 80 | } | ||
| 81 | |||
| 82 | /* Only care about the minutes and seconds */ | ||
| 83 | int vrtc_set_mmss(unsigned long nowtime) | ||
| 84 | { | ||
| 85 | int real_sec, real_min; | ||
| 86 | int vrtc_min; | ||
| 87 | |||
| 88 | vrtc_min = vrtc_cmos_read(RTC_MINUTES); | ||
| 89 | |||
| 90 | real_sec = nowtime % 60; | ||
| 91 | real_min = nowtime / 60; | ||
| 92 | if (((abs(real_min - vrtc_min) + 15)/30) & 1) | ||
| 93 | real_min += 30; | ||
| 94 | real_min %= 60; | ||
| 95 | |||
| 96 | vrtc_cmos_write(real_sec, RTC_SECONDS); | ||
| 97 | vrtc_cmos_write(real_min, RTC_MINUTES); | ||
| 98 | return 0; | ||
| 99 | } | ||
| 100 | |||
| 101 | void __init mrst_rtc_init(void) | ||
| 102 | { | ||
| 103 | unsigned long rtc_paddr; | ||
| 104 | void __iomem *virt_base; | ||
| 105 | |||
| 106 | sfi_table_parse(SFI_SIG_MRTC, NULL, NULL, sfi_parse_mrtc); | ||
| 107 | if (!sfi_mrtc_num) | ||
| 108 | return; | ||
| 109 | |||
| 110 | rtc_paddr = sfi_mrtc_array[0].phys_addr; | ||
| 111 | |||
| 112 | /* vRTC's register address may not be page aligned */ | ||
| 113 | set_fixmap_nocache(FIX_LNW_VRTC, rtc_paddr); | ||
| 114 | |||
| 115 | virt_base = (void __iomem *)__fix_to_virt(FIX_LNW_VRTC); | ||
| 116 | virt_base += rtc_paddr & ~PAGE_MASK; | ||
| 117 | vrtc_virt_base = virt_base; | ||
| 118 | |||
| 119 | x86_platform.get_wallclock = vrtc_get_time; | ||
| 120 | x86_platform.set_wallclock = vrtc_set_mmss; | ||
| 121 | } | ||
| 122 | |||
| 123 | /* | ||
| 124 | * The Moorestown platform has a memory mapped virtual RTC device that emulates | ||
| 125 | * the programming interface of the RTC. | ||
| 126 | */ | ||
| 127 | |||
| 128 | static struct resource vrtc_resources[] = { | ||
| 129 | [0] = { | ||
| 130 | .flags = IORESOURCE_MEM, | ||
| 131 | }, | ||
| 132 | [1] = { | ||
| 133 | .flags = IORESOURCE_IRQ, | ||
| 134 | } | ||
| 135 | }; | ||
| 136 | |||
| 137 | static struct platform_device vrtc_device = { | ||
| 138 | .name = "rtc_mrst", | ||
| 139 | .id = -1, | ||
| 140 | .resource = vrtc_resources, | ||
| 141 | .num_resources = ARRAY_SIZE(vrtc_resources), | ||
| 142 | }; | ||
| 143 | |||
| 144 | /* Register the RTC device if appropriate */ | ||
| 145 | static int __init mrst_device_create(void) | ||
| 146 | { | ||
| 147 | /* No Moorestown, no device */ | ||
| 148 | if (!mrst_identify_cpu()) | ||
| 149 | return -ENODEV; | ||
| 150 | /* No timer, no device */ | ||
| 151 | if (!sfi_mrtc_num) | ||
| 152 | return -ENODEV; | ||
| 153 | |||
| 154 | /* iomem resource */ | ||
| 155 | vrtc_resources[0].start = sfi_mrtc_array[0].phys_addr; | ||
| 156 | vrtc_resources[0].end = sfi_mrtc_array[0].phys_addr + | ||
| 157 | MRST_VRTC_MAP_SZ; | ||
| 158 | /* irq resource */ | ||
| 159 | vrtc_resources[1].start = sfi_mrtc_array[0].irq; | ||
| 160 | vrtc_resources[1].end = sfi_mrtc_array[0].irq; | ||
| 161 | |||
| 162 | return platform_device_register(&vrtc_device); | ||
| 163 | } | ||
| 164 | |||
| 165 | module_init(mrst_device_create); | ||
diff --git a/drivers/platform/x86/intel_scu_ipc.c b/drivers/platform/x86/intel_scu_ipc.c index 41a9e34899a..ca35b0ce944 100644 --- a/drivers/platform/x86/intel_scu_ipc.c +++ b/drivers/platform/x86/intel_scu_ipc.c | |||
| @@ -26,6 +26,7 @@ | |||
| 26 | #include <linux/sfi.h> | 26 | #include <linux/sfi.h> |
| 27 | #include <asm/mrst.h> | 27 | #include <asm/mrst.h> |
| 28 | #include <asm/intel_scu_ipc.h> | 28 | #include <asm/intel_scu_ipc.h> |
| 29 | #include <asm/mrst.h> | ||
| 29 | 30 | ||
| 30 | /* IPC defines the following message types */ | 31 | /* IPC defines the following message types */ |
| 31 | #define IPCMSG_WATCHDOG_TIMER 0xF8 /* Set Kernel Watchdog Threshold */ | 32 | #define IPCMSG_WATCHDOG_TIMER 0xF8 /* Set Kernel Watchdog Threshold */ |
| @@ -699,6 +700,9 @@ static int ipc_probe(struct pci_dev *dev, const struct pci_device_id *id) | |||
| 699 | iounmap(ipcdev.ipc_base); | 700 | iounmap(ipcdev.ipc_base); |
| 700 | return -ENOMEM; | 701 | return -ENOMEM; |
| 701 | } | 702 | } |
| 703 | |||
| 704 | intel_scu_devices_create(); | ||
| 705 | |||
| 702 | return 0; | 706 | return 0; |
| 703 | } | 707 | } |
| 704 | 708 | ||
| @@ -720,6 +724,7 @@ static void ipc_remove(struct pci_dev *pdev) | |||
| 720 | iounmap(ipcdev.ipc_base); | 724 | iounmap(ipcdev.ipc_base); |
| 721 | iounmap(ipcdev.i2c_base); | 725 | iounmap(ipcdev.i2c_base); |
| 722 | ipcdev.pdev = NULL; | 726 | ipcdev.pdev = NULL; |
| 727 | intel_scu_devices_destroy(); | ||
| 723 | } | 728 | } |
| 724 | 729 | ||
| 725 | static const struct pci_device_id pci_ids[] = { | 730 | static const struct pci_device_id pci_ids[] = { |
diff --git a/drivers/rtc/Kconfig b/drivers/rtc/Kconfig index 2883428d5ac..4941cade319 100644 --- a/drivers/rtc/Kconfig +++ b/drivers/rtc/Kconfig | |||
| @@ -463,6 +463,18 @@ config RTC_DRV_CMOS | |||
| 463 | This driver can also be built as a module. If so, the module | 463 | This driver can also be built as a module. If so, the module |
| 464 | will be called rtc-cmos. | 464 | will be called rtc-cmos. |
| 465 | 465 | ||
| 466 | config RTC_DRV_VRTC | ||
| 467 | tristate "Virtual RTC for Moorestown platforms" | ||
| 468 | depends on X86_MRST | ||
| 469 | default y if X86_MRST | ||
| 470 | |||
| 471 | help | ||
| 472 | Say "yes" here to get direct support for the real time clock | ||
| 473 | found on Moorestown platforms. The VRTC is a emulated RTC that | ||
| 474 | derives its clock source from a real RTC in the PMIC. The MC146818 | ||
| 475 | style programming interface is mostly conserved, but any | ||
| 476 | updates are done via IPC calls to the system controller FW. | ||
| 477 | |||
| 466 | config RTC_DRV_DS1216 | 478 | config RTC_DRV_DS1216 |
| 467 | tristate "Dallas DS1216" | 479 | tristate "Dallas DS1216" |
| 468 | depends on SNI_RM | 480 | depends on SNI_RM |
diff --git a/drivers/rtc/Makefile b/drivers/rtc/Makefile index 4c2832df469..2afdaf3ff98 100644 --- a/drivers/rtc/Makefile +++ b/drivers/rtc/Makefile | |||
| @@ -30,6 +30,7 @@ obj-$(CONFIG_RTC_DRV_CMOS) += rtc-cmos.o | |||
| 30 | obj-$(CONFIG_RTC_DRV_COH901331) += rtc-coh901331.o | 30 | obj-$(CONFIG_RTC_DRV_COH901331) += rtc-coh901331.o |
| 31 | obj-$(CONFIG_RTC_DRV_DAVINCI) += rtc-davinci.o | 31 | obj-$(CONFIG_RTC_DRV_DAVINCI) += rtc-davinci.o |
| 32 | obj-$(CONFIG_RTC_DRV_DM355EVM) += rtc-dm355evm.o | 32 | obj-$(CONFIG_RTC_DRV_DM355EVM) += rtc-dm355evm.o |
| 33 | obj-$(CONFIG_RTC_DRV_VRTC) += rtc-mrst.o | ||
| 33 | obj-$(CONFIG_RTC_DRV_DS1216) += rtc-ds1216.o | 34 | obj-$(CONFIG_RTC_DRV_DS1216) += rtc-ds1216.o |
| 34 | obj-$(CONFIG_RTC_DRV_DS1286) += rtc-ds1286.o | 35 | obj-$(CONFIG_RTC_DRV_DS1286) += rtc-ds1286.o |
| 35 | obj-$(CONFIG_RTC_DRV_DS1302) += rtc-ds1302.o | 36 | obj-$(CONFIG_RTC_DRV_DS1302) += rtc-ds1302.o |
diff --git a/drivers/rtc/rtc-mrst.c b/drivers/rtc/rtc-mrst.c new file mode 100644 index 00000000000..bcd0cf63eb1 --- /dev/null +++ b/drivers/rtc/rtc-mrst.c | |||
| @@ -0,0 +1,582 @@ | |||
| 1 | /* | ||
| 2 | * rtc-mrst.c: Driver for Moorestown virtual RTC | ||
| 3 | * | ||
| 4 | * (C) Copyright 2009 Intel Corporation | ||
| 5 | * Author: Jacob Pan (jacob.jun.pan@intel.com) | ||
| 6 | * Feng Tang (feng.tang@intel.com) | ||
| 7 | * | ||
| 8 | * This program is free software; you can redistribute it and/or | ||
| 9 | * modify it under the terms of the GNU General Public License | ||
| 10 | * as published by the Free Software Foundation; version 2 | ||
| 11 | * of the License. | ||
| 12 | * | ||
| 13 | * Note: | ||
| 14 | * VRTC is emulated by system controller firmware, the real HW | ||
| 15 | * RTC is located in the PMIC device. SCU FW shadows PMIC RTC | ||
| 16 | * in a memory mapped IO space that is visible to the host IA | ||
| 17 | * processor. | ||
| 18 | * | ||
| 19 | * This driver is based upon drivers/rtc/rtc-cmos.c | ||
| 20 | */ | ||
| 21 | |||
| 22 | /* | ||
| 23 | * Note: | ||
| 24 | * * vRTC only supports binary mode and 24H mode | ||
| 25 | * * vRTC only support PIE and AIE, no UIE, and its PIE only happens | ||
| 26 | * at 23:59:59pm everyday, no support for adjustable frequency | ||
| 27 | * * Alarm function is also limited to hr/min/sec. | ||
| 28 | */ | ||
| 29 | |||
| 30 | #include <linux/mod_devicetable.h> | ||
| 31 | #include <linux/platform_device.h> | ||
| 32 | #include <linux/interrupt.h> | ||
| 33 | #include <linux/spinlock.h> | ||
| 34 | #include <linux/kernel.h> | ||
| 35 | #include <linux/module.h> | ||
| 36 | #include <linux/init.h> | ||
| 37 | #include <linux/sfi.h> | ||
| 38 | |||
| 39 | #include <asm-generic/rtc.h> | ||
| 40 | #include <asm/intel_scu_ipc.h> | ||
| 41 | #include <asm/mrst.h> | ||
| 42 | #include <asm/mrst-vrtc.h> | ||
| 43 | |||
| 44 | struct mrst_rtc { | ||
| 45 | struct rtc_device *rtc; | ||
| 46 | struct device *dev; | ||
| 47 | int irq; | ||
| 48 | struct resource *iomem; | ||
| 49 | |||
| 50 | u8 enabled_wake; | ||
| 51 | u8 suspend_ctrl; | ||
| 52 | }; | ||
| 53 | |||
| 54 | static const char driver_name[] = "rtc_mrst"; | ||
| 55 | |||
| 56 | #define RTC_IRQMASK (RTC_PF | RTC_AF) | ||
| 57 | |||
| 58 | static inline int is_intr(u8 rtc_intr) | ||
| 59 | { | ||
| 60 | if (!(rtc_intr & RTC_IRQF)) | ||
| 61 | return 0; | ||
| 62 | return rtc_intr & RTC_IRQMASK; | ||
| 63 | } | ||
| 64 | |||
| 65 | /* | ||
| 66 | * rtc_time's year contains the increment over 1900, but vRTC's YEAR | ||
| 67 | * register can't be programmed to value larger than 0x64, so vRTC | ||
| 68 | * driver chose to use 1960 (1970 is UNIX time start point) as the base, | ||
| 69 | * and does the translation at read/write time. | ||
| 70 | * | ||
| 71 | * Why not just use 1970 as the offset? it's because using 1960 will | ||
| 72 | * make it consistent in leap year setting for both vrtc and low-level | ||
| 73 | * physical rtc devices. | ||
| 74 | */ | ||
| 75 | static int mrst_read_time(struct device *dev, struct rtc_time *time) | ||
| 76 | { | ||
| 77 | unsigned long flags; | ||
| 78 | |||
| 79 | if (rtc_is_updating()) | ||
| 80 | mdelay(20); | ||
| 81 | |||
| 82 | spin_lock_irqsave(&rtc_lock, flags); | ||
| 83 | time->tm_sec = vrtc_cmos_read(RTC_SECONDS); | ||
| 84 | time->tm_min = vrtc_cmos_read(RTC_MINUTES); | ||
| 85 | time->tm_hour = vrtc_cmos_read(RTC_HOURS); | ||
| 86 | time->tm_mday = vrtc_cmos_read(RTC_DAY_OF_MONTH); | ||
| 87 | time->tm_mon = vrtc_cmos_read(RTC_MONTH); | ||
| 88 | time->tm_year = vrtc_cmos_read(RTC_YEAR); | ||
| 89 | spin_unlock_irqrestore(&rtc_lock, flags); | ||
| 90 | |||
| 91 | /* Adjust for the 1960/1900 */ | ||
| 92 | time->tm_year += 60; | ||
| 93 | time->tm_mon--; | ||
| 94 | return RTC_24H; | ||
| 95 | } | ||
| 96 | |||
| 97 | static int mrst_set_time(struct device *dev, struct rtc_time *time) | ||
| 98 | { | ||
| 99 | int ret; | ||
| 100 | unsigned long flags; | ||
| 101 | unsigned char mon, day, hrs, min, sec; | ||
| 102 | unsigned int yrs; | ||
| 103 | |||
| 104 | yrs = time->tm_year; | ||
| 105 | mon = time->tm_mon + 1; /* tm_mon starts at zero */ | ||
| 106 | day = time->tm_mday; | ||
| 107 | hrs = time->tm_hour; | ||
| 108 | min = time->tm_min; | ||
| 109 | sec = time->tm_sec; | ||
| 110 | |||
| 111 | if (yrs < 70 || yrs > 138) | ||
| 112 | return -EINVAL; | ||
| 113 | yrs -= 60; | ||
| 114 | |||
| 115 | spin_lock_irqsave(&rtc_lock, flags); | ||
| 116 | |||
| 117 | vrtc_cmos_write(yrs, RTC_YEAR); | ||
| 118 | vrtc_cmos_write(mon, RTC_MONTH); | ||
| 119 | vrtc_cmos_write(day, RTC_DAY_OF_MONTH); | ||
| 120 | vrtc_cmos_write(hrs, RTC_HOURS); | ||
| 121 | vrtc_cmos_write(min, RTC_MINUTES); | ||
| 122 | vrtc_cmos_write(sec, RTC_SECONDS); | ||
| 123 | |||
| 124 | spin_unlock_irqrestore(&rtc_lock, flags); | ||
| 125 | |||
| 126 | ret = intel_scu_ipc_simple_command(IPCMSG_VRTC, IPC_CMD_VRTC_SETTIME); | ||
| 127 | return ret; | ||
| 128 | } | ||
| 129 | |||
| 130 | static int mrst_read_alarm(struct device *dev, struct rtc_wkalrm *t) | ||
| 131 | { | ||
| 132 | struct mrst_rtc *mrst = dev_get_drvdata(dev); | ||
| 133 | unsigned char rtc_control; | ||
| 134 | |||
| 135 | if (mrst->irq <= 0) | ||
| 136 | return -EIO; | ||
| 137 | |||
| 138 | /* Basic alarms only support hour, minute, and seconds fields. | ||
| 139 | * Some also support day and month, for alarms up to a year in | ||
| 140 | * the future. | ||
| 141 | */ | ||
| 142 | t->time.tm_mday = -1; | ||
| 143 | t->time.tm_mon = -1; | ||
| 144 | t->time.tm_year = -1; | ||
| 145 | |||
| 146 | /* vRTC only supports binary mode */ | ||
| 147 | spin_lock_irq(&rtc_lock); | ||
| 148 | t->time.tm_sec = vrtc_cmos_read(RTC_SECONDS_ALARM); | ||
| 149 | t->time.tm_min = vrtc_cmos_read(RTC_MINUTES_ALARM); | ||
| 150 | t->time.tm_hour = vrtc_cmos_read(RTC_HOURS_ALARM); | ||
| 151 | |||
| 152 | rtc_control = vrtc_cmos_read(RTC_CONTROL); | ||
| 153 | spin_unlock_irq(&rtc_lock); | ||
| 154 | |||
| 155 | t->enabled = !!(rtc_control & RTC_AIE); | ||
| 156 | t->pending = 0; | ||
| 157 | |||
| 158 | return 0; | ||
| 159 | } | ||
| 160 | |||
| 161 | static void mrst_checkintr(struct mrst_rtc *mrst, unsigned char rtc_control) | ||
| 162 | { | ||
| 163 | unsigned char rtc_intr; | ||
| 164 | |||
| 165 | /* | ||
| 166 | * NOTE after changing RTC_xIE bits we always read INTR_FLAGS; | ||
| 167 | * allegedly some older rtcs need that to handle irqs properly | ||
| 168 | */ | ||
| 169 | rtc_intr = vrtc_cmos_read(RTC_INTR_FLAGS); | ||
| 170 | rtc_intr &= (rtc_control & RTC_IRQMASK) | RTC_IRQF; | ||
| 171 | if (is_intr(rtc_intr)) | ||
| 172 | rtc_update_irq(mrst->rtc, 1, rtc_intr); | ||
| 173 | } | ||
| 174 | |||
| 175 | static void mrst_irq_enable(struct mrst_rtc *mrst, unsigned char mask) | ||
| 176 | { | ||
| 177 | unsigned char rtc_control; | ||
| 178 | |||
| 179 | /* | ||
| 180 | * Flush any pending IRQ status, notably for update irqs, | ||
| 181 | * before we enable new IRQs | ||
| 182 | */ | ||
| 183 | rtc_control = vrtc_cmos_read(RTC_CONTROL); | ||
| 184 | mrst_checkintr(mrst, rtc_control); | ||
| 185 | |||
| 186 | rtc_control |= mask; | ||
| 187 | vrtc_cmos_write(rtc_control, RTC_CONTROL); | ||
| 188 | |||
| 189 | mrst_checkintr(mrst, rtc_control); | ||
| 190 | } | ||
| 191 | |||
| 192 | static void mrst_irq_disable(struct mrst_rtc *mrst, unsigned char mask) | ||
| 193 | { | ||
| 194 | unsigned char rtc_control; | ||
| 195 | |||
| 196 | rtc_control = vrtc_cmos_read(RTC_CONTROL); | ||
| 197 | rtc_control &= ~mask; | ||
| 198 | vrtc_cmos_write(rtc_control, RTC_CONTROL); | ||
| 199 | mrst_checkintr(mrst, rtc_control); | ||
| 200 | } | ||
| 201 | |||
| 202 | static int mrst_set_alarm(struct device *dev, struct rtc_wkalrm *t) | ||
| 203 | { | ||
| 204 | struct mrst_rtc *mrst = dev_get_drvdata(dev); | ||
| 205 | unsigned char hrs, min, sec; | ||
| 206 | int ret = 0; | ||
| 207 | |||
| 208 | if (!mrst->irq) | ||
| 209 | return -EIO; | ||
| 210 | |||
| 211 | hrs = t->time.tm_hour; | ||
| 212 | min = t->time.tm_min; | ||
| 213 | sec = t->time.tm_sec; | ||
| 214 | |||
| 215 | spin_lock_irq(&rtc_lock); | ||
| 216 | /* Next rtc irq must not be from previous alarm setting */ | ||
| 217 | mrst_irq_disable(mrst, RTC_AIE); | ||
| 218 | |||
| 219 | /* Update alarm */ | ||
| 220 | vrtc_cmos_write(hrs, RTC_HOURS_ALARM); | ||
| 221 | vrtc_cmos_write(min, RTC_MINUTES_ALARM); | ||
| 222 | vrtc_cmos_write(sec, RTC_SECONDS_ALARM); | ||
| 223 | |||
| 224 | spin_unlock_irq(&rtc_lock); | ||
| 225 | |||
| 226 | ret = intel_scu_ipc_simple_command(IPCMSG_VRTC, IPC_CMD_VRTC_SETALARM); | ||
| 227 | if (ret) | ||
| 228 | return ret; | ||
| 229 | |||
| 230 | spin_lock_irq(&rtc_lock); | ||
| 231 | if (t->enabled) | ||
| 232 | mrst_irq_enable(mrst, RTC_AIE); | ||
| 233 | |||
| 234 | spin_unlock_irq(&rtc_lock); | ||
| 235 | |||
| 236 | return 0; | ||
| 237 | } | ||
| 238 | |||
| 239 | static int mrst_irq_set_state(struct device *dev, int enabled) | ||
| 240 | { | ||
| 241 | struct mrst_rtc *mrst = dev_get_drvdata(dev); | ||
| 242 | unsigned long flags; | ||
| 243 | |||
| 244 | if (!mrst->irq) | ||
| 245 | return -ENXIO; | ||
| 246 | |||
| 247 | spin_lock_irqsave(&rtc_lock, flags); | ||
| 248 | |||
| 249 | if (enabled) | ||
| 250 | mrst_irq_enable(mrst, RTC_PIE); | ||
| 251 | else | ||
| 252 | mrst_irq_disable(mrst, RTC_PIE); | ||
| 253 | |||
| 254 | spin_unlock_irqrestore(&rtc_lock, flags); | ||
| 255 | return 0; | ||
| 256 | } | ||
| 257 | |||
| 258 | #if defined(CONFIG_RTC_INTF_DEV) || defined(CONFIG_RTC_INTF_DEV_MODULE) | ||
| 259 | |||
| 260 | /* Currently, the vRTC doesn't support UIE ON/OFF */ | ||
| 261 | static int | ||
| 262 | mrst_rtc_ioctl(struct device *dev, unsigned int cmd, unsigned long arg) | ||
| 263 | { | ||
| 264 | struct mrst_rtc *mrst = dev_get_drvdata(dev); | ||
| 265 | unsigned long flags; | ||
| 266 | |||
| 267 | switch (cmd) { | ||
| 268 | case RTC_AIE_OFF: | ||
| 269 | case RTC_AIE_ON: | ||
| 270 | if (!mrst->irq) | ||
| 271 | return -EINVAL; | ||
| 272 | break; | ||
| 273 | default: | ||
| 274 | /* PIE ON/OFF is handled by mrst_irq_set_state() */ | ||
| 275 | return -ENOIOCTLCMD; | ||
| 276 | } | ||
| 277 | |||
| 278 | spin_lock_irqsave(&rtc_lock, flags); | ||
| 279 | switch (cmd) { | ||
| 280 | case RTC_AIE_OFF: /* alarm off */ | ||
| 281 | mrst_irq_disable(mrst, RTC_AIE); | ||
| 282 | break; | ||
| 283 | case RTC_AIE_ON: /* alarm on */ | ||
| 284 | mrst_irq_enable(mrst, RTC_AIE); | ||
| 285 | break; | ||
| 286 | } | ||
| 287 | spin_unlock_irqrestore(&rtc_lock, flags); | ||
| 288 | return 0; | ||
| 289 | } | ||
| 290 | |||
| 291 | #else | ||
| 292 | #define mrst_rtc_ioctl NULL | ||
| 293 | #endif | ||
| 294 | |||
| 295 | #if defined(CONFIG_RTC_INTF_PROC) || defined(CONFIG_RTC_INTF_PROC_MODULE) | ||
| 296 | |||
| 297 | static int mrst_procfs(struct device *dev, struct seq_file *seq) | ||
| 298 | { | ||
| 299 | unsigned char rtc_control, valid; | ||
| 300 | |||
| 301 | spin_lock_irq(&rtc_lock); | ||
| 302 | rtc_control = vrtc_cmos_read(RTC_CONTROL); | ||
| 303 | valid = vrtc_cmos_read(RTC_VALID); | ||
| 304 | spin_unlock_irq(&rtc_lock); | ||
| 305 | |||
| 306 | return seq_printf(seq, | ||
| 307 | "periodic_IRQ\t: %s\n" | ||
| 308 | "alarm\t\t: %s\n" | ||
| 309 | "BCD\t\t: no\n" | ||
| 310 | "periodic_freq\t: daily (not adjustable)\n", | ||
| 311 | (rtc_control & RTC_PIE) ? "on" : "off", | ||
| 312 | (rtc_control & RTC_AIE) ? "on" : "off"); | ||
| 313 | } | ||
| 314 | |||
| 315 | #else | ||
| 316 | #define mrst_procfs NULL | ||
| 317 | #endif | ||
| 318 | |||
| 319 | static const struct rtc_class_ops mrst_rtc_ops = { | ||
| 320 | .ioctl = mrst_rtc_ioctl, | ||
| 321 | .read_time = mrst_read_time, | ||
| 322 | .set_time = mrst_set_time, | ||
| 323 | .read_alarm = mrst_read_alarm, | ||
| 324 | .set_alarm = mrst_set_alarm, | ||
| 325 | .proc = mrst_procfs, | ||
| 326 | .irq_set_state = mrst_irq_set_state, | ||
| 327 | }; | ||
| 328 | |||
| 329 | static struct mrst_rtc mrst_rtc; | ||
| 330 | |||
| 331 | /* | ||
| 332 | * When vRTC IRQ is captured by SCU FW, FW will clear the AIE bit in | ||
| 333 | * Reg B, so no need for this driver to clear it | ||
| 334 | */ | ||
| 335 | static irqreturn_t mrst_rtc_irq(int irq, void *p) | ||
| 336 | { | ||
| 337 | u8 irqstat; | ||
| 338 | |||
| 339 | spin_lock(&rtc_lock); | ||
| 340 | /* This read will clear all IRQ flags inside Reg C */ | ||
| 341 | irqstat = vrtc_cmos_read(RTC_INTR_FLAGS); | ||
| 342 | spin_unlock(&rtc_lock); | ||
| 343 | |||
| 344 | irqstat &= RTC_IRQMASK | RTC_IRQF; | ||
| 345 | if (is_intr(irqstat)) { | ||
| 346 | rtc_update_irq(p, 1, irqstat); | ||
| 347 | return IRQ_HANDLED; | ||
| 348 | } | ||
| 349 | return IRQ_NONE; | ||
| 350 | } | ||
| 351 | |||
| 352 | static int __init | ||
| 353 | vrtc_mrst_do_probe(struct device *dev, struct resource *iomem, int rtc_irq) | ||
| 354 | { | ||
| 355 | int retval = 0; | ||
| 356 | unsigned char rtc_control; | ||
| 357 | |||
| 358 | /* There can be only one ... */ | ||
| 359 | if (mrst_rtc.dev) | ||
| 360 | return -EBUSY; | ||
| 361 | |||
| 362 | if (!iomem) | ||
| 363 | return -ENODEV; | ||
| 364 | |||
| 365 | iomem = request_mem_region(iomem->start, | ||
| 366 | iomem->end + 1 - iomem->start, | ||
| 367 | driver_name); | ||
| 368 | if (!iomem) { | ||
| 369 | dev_dbg(dev, "i/o mem already in use.\n"); | ||
| 370 | return -EBUSY; | ||
| 371 | } | ||
| 372 | |||
| 373 | mrst_rtc.irq = rtc_irq; | ||
| 374 | mrst_rtc.iomem = iomem; | ||
| 375 | |||
| 376 | mrst_rtc.rtc = rtc_device_register(driver_name, dev, | ||
| 377 | &mrst_rtc_ops, THIS_MODULE); | ||
| 378 | if (IS_ERR(mrst_rtc.rtc)) { | ||
| 379 | retval = PTR_ERR(mrst_rtc.rtc); | ||
| 380 | goto cleanup0; | ||
| 381 | } | ||
| 382 | |||
| 383 | mrst_rtc.dev = dev; | ||
| 384 | dev_set_drvdata(dev, &mrst_rtc); | ||
| 385 | rename_region(iomem, dev_name(&mrst_rtc.rtc->dev)); | ||
| 386 | |||
| 387 | spin_lock_irq(&rtc_lock); | ||
| 388 | mrst_irq_disable(&mrst_rtc, RTC_PIE | RTC_AIE); | ||
| 389 | rtc_control = vrtc_cmos_read(RTC_CONTROL); | ||
| 390 | spin_unlock_irq(&rtc_lock); | ||
| 391 | |||
| 392 | if (!(rtc_control & RTC_24H) || (rtc_control & (RTC_DM_BINARY))) | ||
| 393 | dev_dbg(dev, "TODO: support more than 24-hr BCD mode\n"); | ||
| 394 | |||
| 395 | if (rtc_irq) { | ||
| 396 | retval = request_irq(rtc_irq, mrst_rtc_irq, | ||
| 397 | IRQF_DISABLED, dev_name(&mrst_rtc.rtc->dev), | ||
| 398 | mrst_rtc.rtc); | ||
| 399 | if (retval < 0) { | ||
| 400 | dev_dbg(dev, "IRQ %d is already in use, err %d\n", | ||
| 401 | rtc_irq, retval); | ||
| 402 | goto cleanup1; | ||
| 403 | } | ||
| 404 | } | ||
| 405 | dev_dbg(dev, "initialised\n"); | ||
| 406 | return 0; | ||
| 407 | |||
| 408 | cleanup1: | ||
| 409 | mrst_rtc.dev = NULL; | ||
| 410 | rtc_device_unregister(mrst_rtc.rtc); | ||
| 411 | cleanup0: | ||
| 412 | release_region(iomem->start, iomem->end + 1 - iomem->start); | ||
| 413 | dev_err(dev, "rtc-mrst: unable to initialise\n"); | ||
| 414 | return retval; | ||
| 415 | } | ||
| 416 | |||
| 417 | static void rtc_mrst_do_shutdown(void) | ||
| 418 | { | ||
| 419 | spin_lock_irq(&rtc_lock); | ||
| 420 | mrst_irq_disable(&mrst_rtc, RTC_IRQMASK); | ||
| 421 | spin_unlock_irq(&rtc_lock); | ||
| 422 | } | ||
| 423 | |||
| 424 | static void __exit rtc_mrst_do_remove(struct device *dev) | ||
| 425 | { | ||
| 426 | struct mrst_rtc *mrst = dev_get_drvdata(dev); | ||
| 427 | struct resource *iomem; | ||
| 428 | |||
| 429 | rtc_mrst_do_shutdown(); | ||
| 430 | |||
| 431 | if (mrst->irq) | ||
| 432 | free_irq(mrst->irq, mrst->rtc); | ||
| 433 | |||
| 434 | rtc_device_unregister(mrst->rtc); | ||
| 435 | mrst->rtc = NULL; | ||
| 436 | |||
| 437 | iomem = mrst->iomem; | ||
| 438 | release_region(iomem->start, iomem->end + 1 - iomem->start); | ||
| 439 | mrst->iomem = NULL; | ||
| 440 | |||
| 441 | mrst->dev = NULL; | ||
| 442 | dev_set_drvdata(dev, NULL); | ||
| 443 | } | ||
| 444 | |||
| 445 | #ifdef CONFIG_PM | ||
| 446 | static int mrst_suspend(struct device *dev, pm_message_t mesg) | ||
| 447 | { | ||
| 448 | struct mrst_rtc *mrst = dev_get_drvdata(dev); | ||
| 449 | unsigned char tmp; | ||
| 450 | |||
| 451 | /* Only the alarm might be a wakeup event source */ | ||
| 452 | spin_lock_irq(&rtc_lock); | ||
| 453 | mrst->suspend_ctrl = tmp = vrtc_cmos_read(RTC_CONTROL); | ||
| 454 | if (tmp & (RTC_PIE | RTC_AIE)) { | ||
| 455 | unsigned char mask; | ||
| 456 | |||
| 457 | if (device_may_wakeup(dev)) | ||
| 458 | mask = RTC_IRQMASK & ~RTC_AIE; | ||
| 459 | else | ||
| 460 | mask = RTC_IRQMASK; | ||
| 461 | tmp &= ~mask; | ||
| 462 | vrtc_cmos_write(tmp, RTC_CONTROL); | ||
| 463 | |||
| 464 | mrst_checkintr(mrst, tmp); | ||
| 465 | } | ||
| 466 | spin_unlock_irq(&rtc_lock); | ||
| 467 | |||
| 468 | if (tmp & RTC_AIE) { | ||
| 469 | mrst->enabled_wake = 1; | ||
| 470 | enable_irq_wake(mrst->irq); | ||
| 471 | } | ||
| 472 | |||
| 473 | dev_dbg(&mrst_rtc.rtc->dev, "suspend%s, ctrl %02x\n", | ||
| 474 | (tmp & RTC_AIE) ? ", alarm may wake" : "", | ||
| 475 | tmp); | ||
| 476 | |||
| 477 | return 0; | ||
| 478 | } | ||
| 479 | |||
| 480 | /* | ||
| 481 | * We want RTC alarms to wake us from the deep power saving state | ||
| 482 | */ | ||
| 483 | static inline int mrst_poweroff(struct device *dev) | ||
| 484 | { | ||
| 485 | return mrst_suspend(dev, PMSG_HIBERNATE); | ||
| 486 | } | ||
| 487 | |||
| 488 | static int mrst_resume(struct device *dev) | ||
| 489 | { | ||
| 490 | struct mrst_rtc *mrst = dev_get_drvdata(dev); | ||
| 491 | unsigned char tmp = mrst->suspend_ctrl; | ||
| 492 | |||
| 493 | /* Re-enable any irqs previously active */ | ||
| 494 | if (tmp & RTC_IRQMASK) { | ||
| 495 | unsigned char mask; | ||
| 496 | |||
| 497 | if (mrst->enabled_wake) { | ||
| 498 | disable_irq_wake(mrst->irq); | ||
| 499 | mrst->enabled_wake = 0; | ||
| 500 | } | ||
| 501 | |||
| 502 | spin_lock_irq(&rtc_lock); | ||
| 503 | do { | ||
| 504 | vrtc_cmos_write(tmp, RTC_CONTROL); | ||
| 505 | |||
| 506 | mask = vrtc_cmos_read(RTC_INTR_FLAGS); | ||
| 507 | mask &= (tmp & RTC_IRQMASK) | RTC_IRQF; | ||
| 508 | if (!is_intr(mask)) | ||
| 509 | break; | ||
| 510 | |||
| 511 | rtc_update_irq(mrst->rtc, 1, mask); | ||
| 512 | tmp &= ~RTC_AIE; | ||
| 513 | } while (mask & RTC_AIE); | ||
| 514 | spin_unlock_irq(&rtc_lock); | ||
| 515 | } | ||
| 516 | |||
| 517 | dev_dbg(&mrst_rtc.rtc->dev, "resume, ctrl %02x\n", tmp); | ||
| 518 | |||
| 519 | return 0; | ||
| 520 | } | ||
| 521 | |||
| 522 | #else | ||
| 523 | #define mrst_suspend NULL | ||
| 524 | #define mrst_resume NULL | ||
| 525 | |||
| 526 | static inline int mrst_poweroff(struct device *dev) | ||
| 527 | { | ||
| 528 | return -ENOSYS; | ||
| 529 | } | ||
| 530 | |||
| 531 | #endif | ||
| 532 | |||
| 533 | static int __init vrtc_mrst_platform_probe(struct platform_device *pdev) | ||
| 534 | { | ||
| 535 | return vrtc_mrst_do_probe(&pdev->dev, | ||
| 536 | platform_get_resource(pdev, IORESOURCE_MEM, 0), | ||
| 537 | platform_get_irq(pdev, 0)); | ||
| 538 | } | ||
| 539 | |||
| 540 | static int __exit vrtc_mrst_platform_remove(struct platform_device *pdev) | ||
| 541 | { | ||
| 542 | rtc_mrst_do_remove(&pdev->dev); | ||
| 543 | return 0; | ||
| 544 | } | ||
| 545 | |||
| 546 | static void vrtc_mrst_platform_shutdown(struct platform_device *pdev) | ||
| 547 | { | ||
| 548 | if (system_state == SYSTEM_POWER_OFF && !mrst_poweroff(&pdev->dev)) | ||
| 549 | return; | ||
| 550 | |||
| 551 | rtc_mrst_do_shutdown(); | ||
| 552 | } | ||
| 553 | |||
| 554 | MODULE_ALIAS("platform:vrtc_mrst"); | ||
| 555 | |||
| 556 | static struct platform_driver vrtc_mrst_platform_driver = { | ||
| 557 | .probe = vrtc_mrst_platform_probe, | ||
| 558 | .remove = __exit_p(vrtc_mrst_platform_remove), | ||
| 559 | .shutdown = vrtc_mrst_platform_shutdown, | ||
| 560 | .driver = { | ||
| 561 | .name = (char *) driver_name, | ||
| 562 | .suspend = mrst_suspend, | ||
| 563 | .resume = mrst_resume, | ||
| 564 | } | ||
| 565 | }; | ||
| 566 | |||
| 567 | static int __init vrtc_mrst_init(void) | ||
| 568 | { | ||
| 569 | return platform_driver_register(&vrtc_mrst_platform_driver); | ||
| 570 | } | ||
| 571 | |||
| 572 | static void __exit vrtc_mrst_exit(void) | ||
| 573 | { | ||
| 574 | platform_driver_unregister(&vrtc_mrst_platform_driver); | ||
| 575 | } | ||
| 576 | |||
| 577 | module_init(vrtc_mrst_init); | ||
| 578 | module_exit(vrtc_mrst_exit); | ||
| 579 | |||
| 580 | MODULE_AUTHOR("Jacob Pan; Feng Tang"); | ||
| 581 | MODULE_DESCRIPTION("Driver for Moorestown virtual RTC"); | ||
| 582 | MODULE_LICENSE("GPL"); | ||
diff --git a/include/linux/sfi.h b/include/linux/sfi.h index 7f770c638e9..fe817918b30 100644 --- a/include/linux/sfi.h +++ b/include/linux/sfi.h | |||
| @@ -77,6 +77,8 @@ | |||
| 77 | #define SFI_OEM_ID_SIZE 6 | 77 | #define SFI_OEM_ID_SIZE 6 |
| 78 | #define SFI_OEM_TABLE_ID_SIZE 8 | 78 | #define SFI_OEM_TABLE_ID_SIZE 8 |
| 79 | 79 | ||
| 80 | #define SFI_NAME_LEN 16 | ||
| 81 | |||
| 80 | #define SFI_SYST_SEARCH_BEGIN 0x000E0000 | 82 | #define SFI_SYST_SEARCH_BEGIN 0x000E0000 |
| 81 | #define SFI_SYST_SEARCH_END 0x000FFFFF | 83 | #define SFI_SYST_SEARCH_END 0x000FFFFF |
| 82 | 84 | ||
| @@ -156,13 +158,13 @@ struct sfi_device_table_entry { | |||
| 156 | u16 addr; | 158 | u16 addr; |
| 157 | u8 irq; | 159 | u8 irq; |
| 158 | u32 max_freq; | 160 | u32 max_freq; |
| 159 | char name[16]; | 161 | char name[SFI_NAME_LEN]; |
| 160 | } __packed; | 162 | } __packed; |
| 161 | 163 | ||
| 162 | struct sfi_gpio_table_entry { | 164 | struct sfi_gpio_table_entry { |
| 163 | char controller_name[16]; | 165 | char controller_name[SFI_NAME_LEN]; |
| 164 | u16 pin_no; | 166 | u16 pin_no; |
| 165 | char pin_name[16]; | 167 | char pin_name[SFI_NAME_LEN]; |
| 166 | } __packed; | 168 | } __packed; |
| 167 | 169 | ||
| 168 | typedef int (*sfi_table_handler) (struct sfi_table_header *table); | 170 | typedef int (*sfi_table_handler) (struct sfi_table_header *table); |
