diff options
Diffstat (limited to 'meta-moblin/packages/linux/linux-moblin-2.6.33.2/linux-2.6.34-moorestown-platform-enabling.patch')
-rw-r--r-- | meta-moblin/packages/linux/linux-moblin-2.6.33.2/linux-2.6.34-moorestown-platform-enabling.patch | 13580 |
1 files changed, 0 insertions, 13580 deletions
diff --git a/meta-moblin/packages/linux/linux-moblin-2.6.33.2/linux-2.6.34-moorestown-platform-enabling.patch b/meta-moblin/packages/linux/linux-moblin-2.6.33.2/linux-2.6.34-moorestown-platform-enabling.patch deleted file mode 100644 index 7f81eb82f..000000000 --- a/meta-moblin/packages/linux/linux-moblin-2.6.33.2/linux-2.6.34-moorestown-platform-enabling.patch +++ /dev/null @@ -1,13580 +0,0 @@ -Index: linux-2.6.33/drivers/pci/pci.c -=================================================================== ---- linux-2.6.33.orig/drivers/pci/pci.c -+++ linux-2.6.33/drivers/pci/pci.c -@@ -297,6 +297,49 @@ int pci_find_ext_capability(struct pci_d - } - EXPORT_SYMBOL_GPL(pci_find_ext_capability); - -+/** -+ * pci_bus_find_ext_capability - find an extended capability -+ * @bus: the PCI bus to query -+ * @devfn: PCI device to query -+ * @cap: capability code -+ * -+ * Like pci_find_ext_capability() but works for pci devices that do not have a -+ * pci_dev structure set up yet. -+ * -+ * Returns the address of the requested capability structure within the -+ * device's PCI configuration space or 0 in case the device does not -+ * support it. -+ */ -+int pci_bus_find_ext_capability(struct pci_bus *bus, unsigned int devfn, -+ int cap) -+{ -+ u32 header; -+ int ttl; -+ int pos = PCI_CFG_SPACE_SIZE; -+ -+ /* minimum 8 bytes per capability */ -+ ttl = (PCI_CFG_SPACE_EXP_SIZE - PCI_CFG_SPACE_SIZE) / 8; -+ -+ if (!pci_bus_read_config_dword(bus, devfn, pos, &header)) -+ return 0; -+ if (header == 0xffffffff || header == 0) -+ return 0; -+ -+ while (ttl-- > 0) { -+ if (PCI_EXT_CAP_ID(header) == cap) -+ return pos; -+ -+ pos = PCI_EXT_CAP_NEXT(header); -+ if (pos < PCI_CFG_SPACE_SIZE) -+ break; -+ -+ if (!pci_bus_read_config_dword(bus, devfn, pos, &header)) -+ break; -+ } -+ -+ return 0; -+} -+ - static int __pci_find_next_ht_cap(struct pci_dev *dev, int pos, int ht_cap) - { - int rc, ttl = PCI_FIND_CAP_TTL; -Index: linux-2.6.33/include/linux/pci.h -=================================================================== ---- linux-2.6.33.orig/include/linux/pci.h -+++ linux-2.6.33/include/linux/pci.h -@@ -631,6 +631,8 @@ enum pci_lost_interrupt_reason pci_lost_ - int pci_find_capability(struct pci_dev *dev, int cap); - int pci_find_next_capability(struct pci_dev *dev, u8 pos, int cap); - int pci_find_ext_capability(struct pci_dev *dev, int cap); -+int pci_bus_find_ext_capability(struct pci_bus *bus, unsigned int devfn, -+ int cap); - int pci_find_ht_capability(struct pci_dev *dev, int ht_cap); - int pci_find_next_ht_capability(struct pci_dev *dev, int pos, int ht_cap); - struct pci_bus *pci_find_next_bus(const struct pci_bus *from); -Index: linux-2.6.33/arch/x86/include/asm/numaq.h -=================================================================== ---- linux-2.6.33.orig/arch/x86/include/asm/numaq.h -+++ linux-2.6.33/arch/x86/include/asm/numaq.h -@@ -30,6 +30,7 @@ - - extern int found_numaq; - extern int get_memcfg_numaq(void); -+extern int pci_numaq_init(void); - - extern void *xquad_portio; - -Index: linux-2.6.33/arch/x86/include/asm/pci.h -=================================================================== ---- linux-2.6.33.orig/arch/x86/include/asm/pci.h -+++ linux-2.6.33/arch/x86/include/asm/pci.h -@@ -45,8 +45,15 @@ static inline int pci_proc_domain(struct - - #ifdef CONFIG_PCI - extern unsigned int pcibios_assign_all_busses(void); -+extern int pci_legacy_init(void); -+# ifdef CONFIG_ACPI -+# define x86_default_pci_init pci_acpi_init -+# else -+# define x86_default_pci_init pci_legacy_init -+# endif - #else --#define pcibios_assign_all_busses() 0 -+# define pcibios_assign_all_busses() 0 -+# define x86_default_pci_init NULL - #endif - - extern unsigned long pci_mem_start; -Index: linux-2.6.33/arch/x86/include/asm/pci_x86.h -=================================================================== ---- linux-2.6.33.orig/arch/x86/include/asm/pci_x86.h -+++ linux-2.6.33/arch/x86/include/asm/pci_x86.h -@@ -82,7 +82,6 @@ struct irq_routing_table { - - extern unsigned int pcibios_irq_mask; - --extern int pcibios_scanned; - extern spinlock_t pci_config_lock; - - extern int (*pcibios_enable_irq)(struct pci_dev *dev); -@@ -111,10 +110,10 @@ extern void __init dmi_check_skip_isa_al - - /* some common used subsys_initcalls */ - extern int __init pci_acpi_init(void); --extern int __init pcibios_irq_init(void); --extern int __init pci_visws_init(void); --extern int __init pci_numaq_init(void); -+extern void __init pcibios_irq_init(void); - extern int __init pcibios_init(void); -+extern int pci_legacy_init(void); -+extern void pcibios_fixup_irqs(void); - - /* pci-mmconfig.c */ - -@@ -182,3 +181,17 @@ static inline void mmio_config_writel(vo - { - asm volatile("movl %%eax,(%1)" : : "a" (val), "r" (pos) : "memory"); - } -+ -+#ifdef CONFIG_PCI -+# ifdef CONFIG_ACPI -+# define x86_default_pci_init pci_acpi_init -+# else -+# define x86_default_pci_init pci_legacy_init -+# endif -+# define x86_default_pci_init_irq pcibios_irq_init -+# define x86_default_pci_fixup_irqs pcibios_fixup_irqs -+#else -+# define x86_default_pci_init NULL -+# define x86_default_pci_init_irq NULL -+# define x86_default_pci_fixup_irqs NULL -+#endif -Index: linux-2.6.33/arch/x86/include/asm/setup.h -=================================================================== ---- linux-2.6.33.orig/arch/x86/include/asm/setup.h -+++ linux-2.6.33/arch/x86/include/asm/setup.h -@@ -37,10 +37,8 @@ void setup_bios_corruption_check(void); - - #ifdef CONFIG_X86_VISWS - extern void visws_early_detect(void); --extern int is_visws_box(void); - #else - static inline void visws_early_detect(void) { } --static inline int is_visws_box(void) { return 0; } - #endif - - extern unsigned long saved_video_mode; -Index: linux-2.6.33/arch/x86/include/asm/visws/cobalt.h -=================================================================== ---- linux-2.6.33.orig/arch/x86/include/asm/visws/cobalt.h -+++ linux-2.6.33/arch/x86/include/asm/visws/cobalt.h -@@ -122,4 +122,6 @@ extern char visws_board_type; - - extern char visws_board_rev; - -+extern int pci_visws_init(void); -+ - #endif /* _ASM_X86_VISWS_COBALT_H */ -Index: linux-2.6.33/arch/x86/include/asm/x86_init.h -=================================================================== ---- linux-2.6.33.orig/arch/x86/include/asm/x86_init.h -+++ linux-2.6.33/arch/x86/include/asm/x86_init.h -@@ -99,6 +99,18 @@ struct x86_init_iommu { - }; - - /** -+ * struct x86_init_pci - platform specific pci init functions -+ * @init: platform specific pci init -+ * @init_irq: platform specific pci irq init -+ * @fixup_irqs: platform specific pci irq fixup -+ */ -+struct x86_init_pci { -+ int (*init)(void); -+ void (*init_irq)(void); -+ void (*fixup_irqs)(void); -+}; -+ -+/** - * struct x86_init_ops - functions for platform specific setup - * - */ -@@ -110,6 +122,7 @@ struct x86_init_ops { - struct x86_init_paging paging; - struct x86_init_timers timers; - struct x86_init_iommu iommu; -+ struct x86_init_pci pci; - }; - - /** -Index: linux-2.6.33/arch/x86/kernel/acpi/boot.c -=================================================================== ---- linux-2.6.33.orig/arch/x86/kernel/acpi/boot.c -+++ linux-2.6.33/arch/x86/kernel/acpi/boot.c -@@ -35,6 +35,7 @@ - #include <linux/ioport.h> - #include <linux/pci.h> - -+#include <asm/pci_x86.h> - #include <asm/pgtable.h> - #include <asm/io_apic.h> - #include <asm/apic.h> -@@ -1603,6 +1604,9 @@ int __init acpi_boot_init(void) - - acpi_table_parse(ACPI_SIG_HPET, acpi_parse_hpet); - -+ if (!acpi_noirq) -+ x86_init.pci.init = pci_acpi_init; -+ - return 0; - } - -Index: linux-2.6.33/arch/x86/kernel/apic/numaq_32.c -=================================================================== ---- linux-2.6.33.orig/arch/x86/kernel/apic/numaq_32.c -+++ linux-2.6.33/arch/x86/kernel/apic/numaq_32.c -@@ -277,6 +277,7 @@ static __init void early_check_numaq(voi - x86_init.mpparse.mpc_oem_pci_bus = mpc_oem_pci_bus; - x86_init.mpparse.mpc_oem_bus_info = mpc_oem_bus_info; - x86_init.timers.tsc_pre_init = numaq_tsc_init; -+ x86_init.pci.init = pci_numaq_init; - } - } - -Index: linux-2.6.33/arch/x86/kernel/visws_quirks.c -=================================================================== ---- linux-2.6.33.orig/arch/x86/kernel/visws_quirks.c -+++ linux-2.6.33/arch/x86/kernel/visws_quirks.c -@@ -49,11 +49,6 @@ extern int no_broadcast; - char visws_board_type = -1; - char visws_board_rev = -1; - --int is_visws_box(void) --{ -- return visws_board_type >= 0; --} -- - static void __init visws_time_init(void) - { - printk(KERN_INFO "Starting Cobalt Timer system clock\n"); -@@ -242,6 +237,8 @@ void __init visws_early_detect(void) - x86_init.irqs.pre_vector_init = visws_pre_intr_init; - x86_init.irqs.trap_init = visws_trap_init; - x86_init.timers.timer_init = visws_time_init; -+ x86_init.pci.init = pci_visws_init; -+ x86_init.pci.init_irq = x86_init_noop; - - /* - * Install reboot quirks: -@@ -508,7 +505,7 @@ static struct irq_chip cobalt_irq_type = - */ - static unsigned int startup_piix4_master_irq(unsigned int irq) - { -- init_8259A(0); -+ legacy_pic->init(0); - - return startup_cobalt_irq(irq); - } -@@ -531,10 +528,7 @@ static struct irq_chip piix4_master_irq_ - - - static struct irq_chip piix4_virtual_irq_type = { -- .name = "PIIX4-virtual", -- .shutdown = disable_8259A_irq, -- .enable = enable_8259A_irq, -- .disable = disable_8259A_irq, -+ .typename = "PIIX4-virtual", - }; - - -@@ -609,7 +603,7 @@ static irqreturn_t piix4_master_intr(int - handle_IRQ_event(realirq, desc->action); - - if (!(desc->status & IRQ_DISABLED)) -- enable_8259A_irq(realirq); -+ legacy_pic->chip->unmask(realirq); - - return IRQ_HANDLED; - -@@ -628,6 +622,12 @@ static struct irqaction cascade_action = - .name = "cascade", - }; - -+static inline void set_piix4_virtual_irq_type(void) -+{ -+ piix4_virtual_irq_type.shutdown = i8259A_chip.mask; -+ piix4_virtual_irq_type.enable = i8259A_chip.unmask; -+ piix4_virtual_irq_type.disable = i8259A_chip.mask; -+} - - void init_VISWS_APIC_irqs(void) - { -@@ -653,6 +653,7 @@ void init_VISWS_APIC_irqs(void) - desc->chip = &piix4_master_irq_type; - } - else if (i < CO_IRQ_APIC0) { -+ set_piix4_virtual_irq_type(); - desc->chip = &piix4_virtual_irq_type; - } - else if (IS_CO_APIC(i)) { -Index: linux-2.6.33/arch/x86/kernel/x86_init.c -=================================================================== ---- linux-2.6.33.orig/arch/x86/kernel/x86_init.c -+++ linux-2.6.33/arch/x86/kernel/x86_init.c -@@ -4,9 +4,11 @@ - * For licencing details see kernel-base/COPYING - */ - #include <linux/init.h> -+#include <linux/ioport.h> - - #include <asm/bios_ebda.h> - #include <asm/paravirt.h> -+#include <asm/pci_x86.h> - #include <asm/mpspec.h> - #include <asm/setup.h> - #include <asm/apic.h> -@@ -70,6 +72,12 @@ struct x86_init_ops x86_init __initdata - .iommu = { - .iommu_init = iommu_init_noop, - }, -+ -+ .pci = { -+ .init = x86_default_pci_init, -+ .init_irq = x86_default_pci_init_irq, -+ .fixup_irqs = x86_default_pci_fixup_irqs, -+ }, - }; - - struct x86_cpuinit_ops x86_cpuinit __cpuinitdata = { -Index: linux-2.6.33/arch/x86/pci/acpi.c -=================================================================== ---- linux-2.6.33.orig/arch/x86/pci/acpi.c -+++ linux-2.6.33/arch/x86/pci/acpi.c -@@ -282,17 +282,14 @@ int __init pci_acpi_init(void) - { - struct pci_dev *dev = NULL; - -- if (pcibios_scanned) -- return 0; -- - if (acpi_noirq) -- return 0; -+ return -ENODEV; - - printk(KERN_INFO "PCI: Using ACPI for IRQ routing\n"); - acpi_irq_penalty_init(); -- pcibios_scanned++; - pcibios_enable_irq = acpi_pci_irq_enable; - pcibios_disable_irq = acpi_pci_irq_disable; -+ x86_init.pci.init_irq = x86_init_noop; - - if (pci_routeirq) { - /* -Index: linux-2.6.33/arch/x86/pci/common.c -=================================================================== ---- linux-2.6.33.orig/arch/x86/pci/common.c -+++ linux-2.6.33/arch/x86/pci/common.c -@@ -72,12 +72,6 @@ struct pci_ops pci_root_ops = { - }; - - /* -- * legacy, numa, and acpi all want to call pcibios_scan_root -- * from their initcalls. This flag prevents that. -- */ --int pcibios_scanned; -- --/* - * This interrupt-safe spinlock protects all accesses to PCI - * configuration space. - */ -Index: linux-2.6.33/arch/x86/pci/legacy.c -=================================================================== ---- linux-2.6.33.orig/arch/x86/pci/legacy.c -+++ linux-2.6.33/arch/x86/pci/legacy.c -@@ -35,16 +35,13 @@ static void __devinit pcibios_fixup_peer - } - } - --static int __init pci_legacy_init(void) -+int __init pci_legacy_init(void) - { - if (!raw_pci_ops) { - printk("PCI: System does not support PCI\n"); - return 0; - } - -- if (pcibios_scanned++) -- return 0; -- - printk("PCI: Probing PCI hardware\n"); - pci_root_bus = pcibios_scan_root(0); - if (pci_root_bus) -@@ -55,18 +52,15 @@ static int __init pci_legacy_init(void) - - int __init pci_subsys_init(void) - { --#ifdef CONFIG_X86_NUMAQ -- pci_numaq_init(); --#endif --#ifdef CONFIG_ACPI -- pci_acpi_init(); --#endif --#ifdef CONFIG_X86_VISWS -- pci_visws_init(); --#endif -- pci_legacy_init(); -+ /* -+ * The init function returns an non zero value when -+ * pci_legacy_init should be invoked. -+ */ -+ if (x86_init.pci.init()) -+ pci_legacy_init(); -+ - pcibios_fixup_peer_bridges(); -- pcibios_irq_init(); -+ x86_init.pci.init_irq(); - pcibios_init(); - - return 0; -Index: linux-2.6.33/arch/x86/pci/numaq_32.c -=================================================================== ---- linux-2.6.33.orig/arch/x86/pci/numaq_32.c -+++ linux-2.6.33/arch/x86/pci/numaq_32.c -@@ -152,14 +152,8 @@ int __init pci_numaq_init(void) - { - int quad; - -- if (!found_numaq) -- return 0; -- - raw_pci_ops = &pci_direct_conf1_mq; - -- if (pcibios_scanned++) -- return 0; -- - pci_root_bus = pcibios_scan_root(0); - if (pci_root_bus) - pci_bus_add_devices(pci_root_bus); -Index: linux-2.6.33/arch/x86/pci/visws.c -=================================================================== ---- linux-2.6.33.orig/arch/x86/pci/visws.c -+++ linux-2.6.33/arch/x86/pci/visws.c -@@ -69,9 +69,6 @@ void __init pcibios_update_irq(struct pc - - int __init pci_visws_init(void) - { -- if (!is_visws_box()) -- return -1; -- - pcibios_enable_irq = &pci_visws_enable_irq; - pcibios_disable_irq = &pci_visws_disable_irq; - -@@ -90,5 +87,6 @@ int __init pci_visws_init(void) - pci_scan_bus_with_sysdata(pci_bus1); - pci_fixup_irqs(pci_common_swizzle, visws_map_irq); - pcibios_resource_survey(); -- return 0; -+ /* Request bus scan */ -+ return 1; - } -Index: linux-2.6.33/arch/x86/pci/irq.c -=================================================================== ---- linux-2.6.33.orig/arch/x86/pci/irq.c -+++ linux-2.6.33/arch/x86/pci/irq.c -@@ -53,7 +53,7 @@ struct irq_router_handler { - int (*probe)(struct irq_router *r, struct pci_dev *router, u16 device); - }; - --int (*pcibios_enable_irq)(struct pci_dev *dev) = NULL; -+int (*pcibios_enable_irq)(struct pci_dev *dev) = pirq_enable_irq; - void (*pcibios_disable_irq)(struct pci_dev *dev) = NULL; - - /* -@@ -1016,7 +1016,7 @@ static int pcibios_lookup_irq(struct pci - return 1; - } - --static void __init pcibios_fixup_irqs(void) -+void __init pcibios_fixup_irqs(void) - { - struct pci_dev *dev = NULL; - u8 pin; -@@ -1110,12 +1110,12 @@ static struct dmi_system_id __initdata p - { } - }; - --int __init pcibios_irq_init(void) -+void __init pcibios_irq_init(void) - { - DBG(KERN_DEBUG "PCI: IRQ init\n"); - -- if (pcibios_enable_irq || raw_pci_ops == NULL) -- return 0; -+ if (raw_pci_ops == NULL) -+ return; - - dmi_check_system(pciirq_dmi_table); - -@@ -1142,9 +1142,7 @@ int __init pcibios_irq_init(void) - pirq_table = NULL; - } - -- pcibios_enable_irq = pirq_enable_irq; -- -- pcibios_fixup_irqs(); -+ x86_init.pci.fixup_irqs(); - - if (io_apic_assign_pci_irqs && pci_routeirq) { - struct pci_dev *dev = NULL; -@@ -1157,8 +1155,6 @@ int __init pcibios_irq_init(void) - for_each_pci_dev(dev) - pirq_enable_irq(dev); - } -- -- return 0; - } - - static void pirq_penalize_isa_irq(int irq, int active) -Index: linux-2.6.33/arch/x86/kernel/apic/apic.c -=================================================================== ---- linux-2.6.33.orig/arch/x86/kernel/apic/apic.c -+++ linux-2.6.33/arch/x86/kernel/apic/apic.c -@@ -718,6 +718,9 @@ static int __init calibrate_APIC_clock(v - */ - void __init setup_boot_APIC_clock(void) - { -+ /* we rely on global clockevent for calibration */ -+ if (global_clock_event == NULL) -+ return; - /* - * The local apic timer can be disabled via the kernel - * commandline or from the CPU detection code. Register the lapic -@@ -1390,7 +1393,7 @@ void __init enable_IR_x2apic(void) - } - - local_irq_save(flags); -- mask_8259A(); -+ legacy_pic->mask_all(); - mask_IO_APIC_setup(ioapic_entries); - - if (dmar_table_init_ret) -@@ -1422,7 +1425,7 @@ void __init enable_IR_x2apic(void) - nox2apic: - if (!ret) /* IR enabling failed */ - restore_IO_APIC_setup(ioapic_entries); -- unmask_8259A(); -+ legacy_pic->restore_mask(); - local_irq_restore(flags); - - out: -@@ -2018,7 +2021,7 @@ static int lapic_resume(struct sys_devic - } - - mask_IO_APIC_setup(ioapic_entries); -- mask_8259A(); -+ legacy_pic->mask_all(); - } - - if (x2apic_mode) -@@ -2062,7 +2065,7 @@ static int lapic_resume(struct sys_devic - - if (intr_remapping_enabled) { - reenable_intr_remapping(x2apic_mode); -- unmask_8259A(); -+ legacy_pic->restore_mask(); - restore_IO_APIC_setup(ioapic_entries); - free_ioapic_entries(ioapic_entries); - } -Index: linux-2.6.33/arch/x86/kernel/apic/io_apic.c -=================================================================== ---- linux-2.6.33.orig/arch/x86/kernel/apic/io_apic.c -+++ linux-2.6.33/arch/x86/kernel/apic/io_apic.c -@@ -94,10 +94,8 @@ struct mpc_intsrc mp_irqs[MAX_IRQ_SOURCE - /* # of MP IRQ source entries */ - int mp_irq_entries; - --/* Number of legacy interrupts */ --static int nr_legacy_irqs __read_mostly = NR_IRQS_LEGACY; - /* GSI interrupts */ --static int nr_irqs_gsi = NR_IRQS_LEGACY; -+int nr_irqs_gsi = NR_IRQS_LEGACY; - - #if defined (CONFIG_MCA) || defined (CONFIG_EISA) - int mp_bus_id_to_type[MAX_MP_BUSSES]; -@@ -140,33 +138,10 @@ static struct irq_pin_list *get_one_free - - /* irq_cfg is indexed by the sum of all RTEs in all I/O APICs. */ - #ifdef CONFIG_SPARSE_IRQ --static struct irq_cfg irq_cfgx[] = { -+static struct irq_cfg irq_cfgx[NR_IRQS_LEGACY]; - #else --static struct irq_cfg irq_cfgx[NR_IRQS] = { -+static struct irq_cfg irq_cfgx[NR_IRQS]; - #endif -- [0] = { .vector = IRQ0_VECTOR, }, -- [1] = { .vector = IRQ1_VECTOR, }, -- [2] = { .vector = IRQ2_VECTOR, }, -- [3] = { .vector = IRQ3_VECTOR, }, -- [4] = { .vector = IRQ4_VECTOR, }, -- [5] = { .vector = IRQ5_VECTOR, }, -- [6] = { .vector = IRQ6_VECTOR, }, -- [7] = { .vector = IRQ7_VECTOR, }, -- [8] = { .vector = IRQ8_VECTOR, }, -- [9] = { .vector = IRQ9_VECTOR, }, -- [10] = { .vector = IRQ10_VECTOR, }, -- [11] = { .vector = IRQ11_VECTOR, }, -- [12] = { .vector = IRQ12_VECTOR, }, -- [13] = { .vector = IRQ13_VECTOR, }, -- [14] = { .vector = IRQ14_VECTOR, }, -- [15] = { .vector = IRQ15_VECTOR, }, --}; -- --void __init io_apic_disable_legacy(void) --{ -- nr_legacy_irqs = 0; -- nr_irqs_gsi = 0; --} - - int __init arch_early_irq_init(void) - { -@@ -176,16 +151,23 @@ int __init arch_early_irq_init(void) - int node; - int i; - -+ if (!legacy_pic->nr_legacy_irqs) { -+ nr_irqs_gsi = 0; -+ io_apic_irqs = ~0UL; -+ } -+ - cfg = irq_cfgx; - count = ARRAY_SIZE(irq_cfgx); - node= cpu_to_node(boot_cpu_id); - - for (i = 0; i < count; i++) { -+ if (i < legacy_pic->nr_legacy_irqs) -+ cfg[i].vector = IRQ0_VECTOR + i; - desc = irq_to_desc(i); - desc->chip_data = &cfg[i]; - zalloc_cpumask_var_node(&cfg[i].domain, GFP_NOWAIT, node); - zalloc_cpumask_var_node(&cfg[i].old_domain, GFP_NOWAIT, node); -- if (i < nr_legacy_irqs) -+ if (i < legacy_pic->nr_legacy_irqs) - cpumask_setall(cfg[i].domain); - } - -@@ -865,7 +847,7 @@ static int __init find_isa_irq_apic(int - */ - static int EISA_ELCR(unsigned int irq) - { -- if (irq < nr_legacy_irqs) { -+ if (irq < legacy_pic->nr_legacy_irqs) { - unsigned int port = 0x4d0 + (irq >> 3); - return (inb(port) >> (irq & 7)) & 1; - } -@@ -1461,8 +1443,8 @@ static void setup_IO_APIC_irq(int apic_i - } - - ioapic_register_intr(irq, desc, trigger); -- if (irq < nr_legacy_irqs) -- disable_8259A_irq(irq); -+ if (irq < legacy_pic->nr_legacy_irqs) -+ legacy_pic->chip->mask(irq); - - ioapic_write_entry(apic_id, pin, entry); - } -@@ -1875,7 +1857,7 @@ __apicdebuginit(void) print_PIC(void) - unsigned int v; - unsigned long flags; - -- if (!nr_legacy_irqs) -+ if (!legacy_pic->nr_legacy_irqs) - return; - - printk(KERN_DEBUG "\nprinting PIC contents\n"); -@@ -1959,7 +1941,7 @@ void __init enable_IO_APIC(void) - nr_ioapic_registers[apic] = reg_01.bits.entries+1; - } - -- if (!nr_legacy_irqs) -+ if (!legacy_pic->nr_legacy_irqs) - return; - - for(apic = 0; apic < nr_ioapics; apic++) { -@@ -2016,7 +1998,7 @@ void disable_IO_APIC(void) - */ - clear_IO_APIC(); - -- if (!nr_legacy_irqs) -+ if (!legacy_pic->nr_legacy_irqs) - return; - - /* -@@ -2249,9 +2231,9 @@ static unsigned int startup_ioapic_irq(u - struct irq_cfg *cfg; - - spin_lock_irqsave(&ioapic_lock, flags); -- if (irq < nr_legacy_irqs) { -- disable_8259A_irq(irq); -- if (i8259A_irq_pending(irq)) -+ if (irq < legacy_pic->nr_legacy_irqs) { -+ legacy_pic->chip->mask(irq); -+ if (legacy_pic->irq_pending(irq)) - was_pending = 1; - } - cfg = irq_cfg(irq); -@@ -2784,8 +2766,8 @@ static inline void init_IO_APIC_traps(vo - * so default to an old-fashioned 8259 - * interrupt if we can.. - */ -- if (irq < nr_legacy_irqs) -- make_8259A_irq(irq); -+ if (irq < legacy_pic->nr_legacy_irqs) -+ legacy_pic->make_irq(irq); - else - /* Strange. Oh, well.. */ - desc->chip = &no_irq_chip; -@@ -2942,7 +2924,7 @@ static inline void __init check_timer(vo - /* - * get/set the timer IRQ vector: - */ -- disable_8259A_irq(0); -+ legacy_pic->chip->mask(0); - assign_irq_vector(0, cfg, apic->target_cpus()); - - /* -@@ -2955,7 +2937,7 @@ static inline void __init check_timer(vo - * automatically. - */ - apic_write(APIC_LVT0, APIC_LVT_MASKED | APIC_DM_EXTINT); -- init_8259A(1); -+ legacy_pic->init(1); - #ifdef CONFIG_X86_32 - { - unsigned int ver; -@@ -3014,7 +2996,7 @@ static inline void __init check_timer(vo - if (timer_irq_works()) { - if (nmi_watchdog == NMI_IO_APIC) { - setup_nmi(); -- enable_8259A_irq(0); -+ legacy_pic->chip->unmask(0); - } - if (disable_timer_pin_1 > 0) - clear_IO_APIC_pin(0, pin1); -@@ -3037,14 +3019,14 @@ static inline void __init check_timer(vo - */ - replace_pin_at_irq_node(cfg, node, apic1, pin1, apic2, pin2); - setup_timer_IRQ0_pin(apic2, pin2, cfg->vector); -- enable_8259A_irq(0); -+ legacy_pic->chip->unmask(0); - if (timer_irq_works()) { - apic_printk(APIC_QUIET, KERN_INFO "....... works.\n"); - timer_through_8259 = 1; - if (nmi_watchdog == NMI_IO_APIC) { -- disable_8259A_irq(0); -+ legacy_pic->chip->mask(0); - setup_nmi(); -- enable_8259A_irq(0); -+ legacy_pic->chip->unmask(0); - } - goto out; - } -@@ -3052,7 +3034,7 @@ static inline void __init check_timer(vo - * Cleanup, just in case ... - */ - local_irq_disable(); -- disable_8259A_irq(0); -+ legacy_pic->chip->mask(0); - clear_IO_APIC_pin(apic2, pin2); - apic_printk(APIC_QUIET, KERN_INFO "....... failed.\n"); - } -@@ -3071,22 +3053,22 @@ static inline void __init check_timer(vo - - lapic_register_intr(0, desc); - apic_write(APIC_LVT0, APIC_DM_FIXED | cfg->vector); /* Fixed mode */ -- enable_8259A_irq(0); -+ legacy_pic->chip->unmask(0); - - if (timer_irq_works()) { - apic_printk(APIC_QUIET, KERN_INFO "..... works.\n"); - goto out; - } - local_irq_disable(); -- disable_8259A_irq(0); -+ legacy_pic->chip->mask(0); - apic_write(APIC_LVT0, APIC_LVT_MASKED | APIC_DM_FIXED | cfg->vector); - apic_printk(APIC_QUIET, KERN_INFO "..... failed.\n"); - - apic_printk(APIC_QUIET, KERN_INFO - "...trying to set up timer as ExtINT IRQ...\n"); - -- init_8259A(0); -- make_8259A_irq(0); -+ legacy_pic->init(0); -+ legacy_pic->make_irq(0); - apic_write(APIC_LVT0, APIC_DM_EXTINT); - - unlock_ExtINT_logic(); -@@ -3128,7 +3110,7 @@ void __init setup_IO_APIC(void) - /* - * calling enable_IO_APIC() is moved to setup_local_APIC for BP - */ -- io_apic_irqs = nr_legacy_irqs ? ~PIC_IRQS : ~0UL; -+ io_apic_irqs = legacy_pic->nr_legacy_irqs ? ~PIC_IRQS : ~0UL; - - apic_printk(APIC_VERBOSE, "ENABLING IO-APIC IRQs\n"); - /* -@@ -3139,7 +3121,7 @@ void __init setup_IO_APIC(void) - sync_Arb_IDs(); - setup_IO_APIC_irqs(); - init_IO_APIC_traps(); -- if (nr_legacy_irqs) -+ if (legacy_pic->nr_legacy_irqs) - check_timer(); - } - -@@ -3932,7 +3914,7 @@ static int __io_apic_set_pci_routing(str - /* - * IRQs < 16 are already in the irq_2_pin[] map - */ -- if (irq >= nr_legacy_irqs) { -+ if (irq >= legacy_pic->nr_legacy_irqs) { - cfg = desc->chip_data; - if (add_pin_to_irq_node_nopanic(cfg, node, ioapic, pin)) { - printk(KERN_INFO "can not add pin %d for irq %d\n", -@@ -4310,3 +4292,25 @@ void __init mp_register_ioapic(int id, u - - nr_ioapics++; - } -+ -+/* Enable IOAPIC early just for system timer */ -+void __init pre_init_apic_IRQ0(void) -+{ -+ struct irq_cfg *cfg; -+ struct irq_desc *desc; -+ -+ printk(KERN_INFO "Early APIC setup for system timer0\n"); -+#ifndef CONFIG_SMP -+ phys_cpu_present_map = physid_mask_of_physid(boot_cpu_physical_apicid); -+#endif -+ desc = irq_to_desc_alloc_node(0, 0); -+ -+ setup_local_APIC(); -+ -+ cfg = irq_cfg(0); -+ add_pin_to_irq_node(cfg, 0, 0, 0); -+ set_irq_chip_and_handler_name(0, &ioapic_chip, handle_edge_irq, "edge"); -+ -+ /* FIXME: get trigger and polarity from mp_irqs[] */ -+ setup_IO_APIC_irq(0, 0, 0, desc, 0, 0); -+} -Index: linux-2.6.33/arch/x86/kernel/smpboot.c -=================================================================== ---- linux-2.6.33.orig/arch/x86/kernel/smpboot.c -+++ linux-2.6.33/arch/x86/kernel/smpboot.c -@@ -48,6 +48,7 @@ - #include <linux/err.h> - #include <linux/nmi.h> - #include <linux/tboot.h> -+#include <linux/stackprotector.h> - - #include <asm/acpi.h> - #include <asm/desc.h> -@@ -67,6 +68,7 @@ - #include <linux/mc146818rtc.h> - - #include <asm/smpboot_hooks.h> -+#include <asm/i8259.h> - - #ifdef CONFIG_X86_32 - u8 apicid_2_node[MAX_APICID]; -@@ -286,9 +288,9 @@ notrace static void __cpuinit start_seco - check_tsc_sync_target(); - - if (nmi_watchdog == NMI_IO_APIC) { -- disable_8259A_irq(0); -+ legacy_pic->chip->mask(0); - enable_NMI_through_LVT0(); -- enable_8259A_irq(0); -+ legacy_pic->chip->unmask(0); - } - - #ifdef CONFIG_X86_32 -@@ -324,6 +326,9 @@ notrace static void __cpuinit start_seco - /* enable local interrupts */ - local_irq_enable(); - -+ /* to prevent fake stack check failure in clock setup */ -+ boot_init_stack_canary(); -+ - x86_cpuinit.setup_percpu_clockev(); - - wmb(); -Index: linux-2.6.33/Documentation/kernel-parameters.txt -=================================================================== ---- linux-2.6.33.orig/Documentation/kernel-parameters.txt -+++ linux-2.6.33/Documentation/kernel-parameters.txt -@@ -1738,6 +1738,12 @@ and is between 256 and 4096 characters. - nomfgpt [X86-32] Disable Multi-Function General Purpose - Timer usage (for AMD Geode machines). - -+ x86_mrst_timer [X86-32,APBT] -+ choose timer option for x86 moorestown mid platform. -+ two valid options are apbt timer only and lapic timer -+ plus one apbt timer for broadcast timer. -+ x86_mrst_timer=apbt_only | lapic_and_apbt -+ - norandmaps Don't use address space randomization. Equivalent to - echo 0 > /proc/sys/kernel/randomize_va_space - -Index: linux-2.6.33/arch/x86/Kconfig -=================================================================== ---- linux-2.6.33.orig/arch/x86/Kconfig -+++ linux-2.6.33/arch/x86/Kconfig -@@ -390,6 +390,7 @@ config X86_MRST - bool "Moorestown MID platform" - depends on X86_32 - depends on X86_EXTENDED_PLATFORM -+ select APB_TIMER - ---help--- - Moorestown is Intel's Low Power Intel Architecture (LPIA) based Moblin - Internet Device(MID) platform. Moorestown consists of two chips: -@@ -398,6 +399,14 @@ config X86_MRST - nor standard legacy replacement devices/features. e.g. Moorestown does - not contain i8259, i8254, HPET, legacy BIOS, most of the io ports. - -+config MRST_SPI_UART_BOOT_MSG -+ def_bool y -+ prompt "Moorestown SPI UART boot message" -+ depends on (X86_MRST && X86_32) -+ help -+ Enable this to see boot message during protected mode boot phase, such as -+ kernel decompression, BAUD rate is set at 115200 8n1 -+ - config X86_RDC321X - bool "RDC R-321x SoC" - depends on X86_32 -@@ -612,6 +621,24 @@ config HPET_EMULATE_RTC - def_bool y - depends on HPET_TIMER && (RTC=y || RTC=m || RTC_DRV_CMOS=m || RTC_DRV_CMOS=y) - -+config APB_TIMER -+ def_bool y if X86_MRST -+ prompt "Langwell APB Timer Support" if X86_MRST -+ help -+ APB timer is the replacement for 8254, HPET on X86 MID platforms. -+ The APBT provides a stable time base on SMP -+ systems, unlike the TSC, but it is more expensive to access, -+ as it is off-chip. APB timers are always running regardless of CPU -+ C states, they are used as per CPU clockevent device when possible. -+ -+config LNW_IPC -+ def_bool n -+ prompt "Langwell IPC Support" if (X86_32 || X86_MRST) -+ depends on X86_MRST -+ help -+ IPC unit is used on Moorestown to bridge the communications -+ between IA and SCU. -+ - # Mark as embedded because too many people got it wrong. - # The code disables itself when not needed. - config DMI -Index: linux-2.6.33/arch/x86/include/asm/apb_timer.h -=================================================================== ---- /dev/null -+++ linux-2.6.33/arch/x86/include/asm/apb_timer.h -@@ -0,0 +1,72 @@ -+/* -+ * apb_timer.h: Driver for Langwell APB timer based on Synopsis DesignWare -+ * -+ * (C) Copyright 2009 Intel Corporation -+ * Author: Jacob Pan (jacob.jun.pan@intel.com) -+ * -+ * This program is free software; you can redistribute it and/or -+ * modify it under the terms of the GNU General Public License -+ * as published by the Free Software Foundation; version 2 -+ * of the License. -+ * -+ * Note: -+ */ -+ -+#ifndef ASM_X86_APBT_H -+#define ASM_X86_APBT_H -+#include <linux/sfi.h> -+ -+#ifdef CONFIG_APB_TIMER -+ -+/* Langwell DW APB timer registers */ -+#define APBTMR_N_LOAD_COUNT 0x00 -+#define APBTMR_N_CURRENT_VALUE 0x04 -+#define APBTMR_N_CONTROL 0x08 -+#define APBTMR_N_EOI 0x0c -+#define APBTMR_N_INT_STATUS 0x10 -+ -+#define APBTMRS_INT_STATUS 0xa0 -+#define APBTMRS_EOI 0xa4 -+#define APBTMRS_RAW_INT_STATUS 0xa8 -+#define APBTMRS_COMP_VERSION 0xac -+#define APBTMRS_REG_SIZE 0x14 -+ -+/* register bits */ -+#define APBTMR_CONTROL_ENABLE (1<<0) -+#define APBTMR_CONTROL_MODE_PERIODIC (1<<1) /*1: periodic 0:free running */ -+#define APBTMR_CONTROL_INT (1<<2) -+ -+/* default memory mapped register base */ -+#define LNW_SCU_ADDR 0xFF100000 -+#define LNW_EXT_TIMER_OFFSET 0x1B800 -+#define APBT_DEFAULT_BASE (LNW_SCU_ADDR+LNW_EXT_TIMER_OFFSET) -+#define LNW_EXT_TIMER_PGOFFSET 0x800 -+ -+/* APBT clock speed range from PCLK to fabric base, 25-100MHz */ -+#define APBT_MAX_FREQ 50 -+#define APBT_MIN_FREQ 1 -+#define APBT_MMAP_SIZE 1024 -+ -+#define APBT_DEV_USED 1 -+ -+#define SFI_MTMR_MAX_NUM 8 -+ -+extern void apbt_time_init(void); -+extern struct clock_event_device *global_clock_event; -+extern unsigned long apbt_quick_calibrate(void); -+extern int arch_setup_apbt_irqs(int irq, int trigger, int mask, int cpu); -+extern void apbt_setup_secondary_clock(void); -+extern unsigned int boot_cpu_id; -+extern int disable_apbt_percpu; -+ -+extern struct sfi_timer_table_entry *sfi_get_mtmr(int hint); -+extern void sfi_free_mtmr(struct sfi_timer_table_entry *mtmr); -+extern int sfi_mtimer_num; -+ -+#else /* CONFIG_APB_TIMER */ -+ -+static inline unsigned long apbt_quick_calibrate(void) {return 0; } -+static inline void apbt_time_init(void) {return 0; } -+ -+#endif -+#endif /* ASM_X86_APBT_H */ -Index: linux-2.6.33/arch/x86/kernel/Makefile -=================================================================== ---- linux-2.6.33.orig/arch/x86/kernel/Makefile -+++ linux-2.6.33/arch/x86/kernel/Makefile -@@ -57,6 +57,12 @@ obj-$(CONFIG_STACKTRACE) += stacktrace.o - obj-y += cpu/ - obj-y += acpi/ - obj-$(CONFIG_SFI) += sfi.o -+sfi-processor-objs += sfi/sfi_processor_core.o -+sfi-processor-objs += sfi/sfi_processor_idle.o -+sfi-processor-objs += sfi/sfi_processor_perflib.o -+ -+obj-$(CONFIG_SFI_PROCESSOR_PM) += sfi-processor.o -+ - obj-y += reboot.o - obj-$(CONFIG_MCA) += mca_32.o - obj-$(CONFIG_X86_MSR) += msr.o -@@ -85,8 +91,11 @@ obj-$(CONFIG_DOUBLEFAULT) += doublefaul - obj-$(CONFIG_KGDB) += kgdb.o - obj-$(CONFIG_VM86) += vm86_32.o - obj-$(CONFIG_EARLY_PRINTK) += early_printk.o -+obj-$(CONFIG_X86_MRST_EARLY_PRINTK) += mrst_earlyprintk.o - - obj-$(CONFIG_HPET_TIMER) += hpet.o -+obj-$(CONFIG_APB_TIMER) += apb_timer.o -+obj-$(CONFIG_LNW_IPC) += ipc_mrst.o - - obj-$(CONFIG_K8_NB) += k8.o - obj-$(CONFIG_DEBUG_RODATA_TEST) += test_rodata.o -@@ -105,7 +114,7 @@ obj-$(CONFIG_SCx200) += scx200.o - scx200-y += scx200_32.o - - obj-$(CONFIG_OLPC) += olpc.o --obj-$(CONFIG_X86_MRST) += mrst.o -+obj-$(CONFIG_X86_MRST) += mrst.o vrtc.o - - microcode-y := microcode_core.o - microcode-$(CONFIG_MICROCODE_INTEL) += microcode_intel.o -Index: linux-2.6.33/arch/x86/kernel/apb_timer.c -=================================================================== ---- /dev/null -+++ linux-2.6.33/arch/x86/kernel/apb_timer.c -@@ -0,0 +1,765 @@ -+/* -+ * apb_timer.c: Driver for Langwell APB timers -+ * -+ * (C) Copyright 2009 Intel Corporation -+ * Author: Jacob Pan (jacob.jun.pan@intel.com) -+ * -+ * This program is free software; you can redistribute it and/or -+ * modify it under the terms of the GNU General Public License -+ * as published by the Free Software Foundation; version 2 -+ * of the License. -+ * -+ * Note: -+ * Langwell is the south complex of Intel Moorestown MID platform. There are -+ * eight external timers in total that can be used by the operating system. -+ * The timer information, such as frequency and addresses, is provided to the -+ * OS via SFI tables. -+ * Timer interrupts are routed via FW/HW emulated IOAPIC independently via -+ * individual redirection table entries (RTE). -+ * Unlike HPET, there is no master counter, therefore one of the timers are -+ * used as clocksource. The overall allocation looks like: -+ * - timer 0 - NR_CPUs for per cpu timer -+ * - one timer for clocksource -+ * - one timer for watchdog driver. -+ * It is also worth notice that APB timer does not support true one-shot mode, -+ * free-running mode will be used here to emulate one-shot mode. -+ * APB timer can also be used as broadcast timer along with per cpu local APIC -+ * timer, but by default APB timer has higher rating than local APIC timers. -+ */ -+ -+#include <linux/clocksource.h> -+#include <linux/clockchips.h> -+#include <linux/delay.h> -+#include <linux/errno.h> -+#include <linux/init.h> -+#include <linux/sysdev.h> -+#include <linux/pm.h> -+#include <linux/pci.h> -+#include <linux/sfi.h> -+#include <linux/interrupt.h> -+#include <linux/cpu.h> -+#include <linux/irq.h> -+ -+#include <asm/fixmap.h> -+#include <asm/apb_timer.h> -+ -+#define APBT_MASK CLOCKSOURCE_MASK(32) -+#define APBT_SHIFT 22 -+#define APBT_CLOCKEVENT_RATING 150 -+#define APBT_CLOCKSOURCE_RATING 250 -+#define APBT_MIN_DELTA_USEC 200 -+ -+#define EVT_TO_APBT_DEV(evt) container_of(evt, struct apbt_dev, evt) -+#define APBT_CLOCKEVENT0_NUM (0) -+#define APBT_CLOCKEVENT1_NUM (1) -+#define APBT_CLOCKSOURCE_NUM (2) -+ -+static unsigned long apbt_address; -+static int apb_timer_block_enabled; -+static void __iomem *apbt_virt_address; -+static int phy_cs_timer_id; -+ -+/* -+ * Common DW APB timer info -+ */ -+static uint64_t apbt_freq; -+ -+static void apbt_set_mode(enum clock_event_mode mode, -+ struct clock_event_device *evt); -+static int apbt_next_event(unsigned long delta, -+ struct clock_event_device *evt); -+static cycle_t apbt_read_clocksource(struct clocksource *cs); -+static void apbt_restart_clocksource(void); -+ -+struct apbt_dev { -+ struct clock_event_device evt; -+ unsigned int num; -+ int cpu; -+ unsigned int irq; -+ unsigned int tick; -+ unsigned int count; -+ unsigned int flags; -+ char name[10]; -+}; -+ -+int disable_apbt_percpu __cpuinitdata; -+ -+#ifdef CONFIG_SMP -+static unsigned int apbt_num_timers_used; -+static DEFINE_PER_CPU(struct apbt_dev, cpu_apbt_dev); -+static struct apbt_dev *apbt_devs; -+#endif -+ -+static inline unsigned long apbt_readl_reg(unsigned long a) -+{ -+ return readl(apbt_virt_address + a); -+} -+ -+static inline void apbt_writel_reg(unsigned long d, unsigned long a) -+{ -+ writel(d, apbt_virt_address + a); -+} -+ -+static inline unsigned long apbt_readl(int n, unsigned long a) -+{ -+ return readl(apbt_virt_address + a + n * APBTMRS_REG_SIZE); -+} -+ -+static inline void apbt_writel(int n, unsigned long d, unsigned long a) -+{ -+ writel(d, apbt_virt_address + a + n * APBTMRS_REG_SIZE); -+} -+ -+static inline void apbt_set_mapping(void) -+{ -+ struct sfi_timer_table_entry *mtmr; -+ -+ if (apbt_virt_address) { -+ pr_debug("APBT base already mapped\n"); -+ return; -+ } -+ mtmr = sfi_get_mtmr(APBT_CLOCKEVENT0_NUM); -+ if (mtmr == NULL) { -+ printk(KERN_ERR "Failed to get MTMR %d from SFI\n", -+ APBT_CLOCKEVENT0_NUM); -+ return; -+ } -+ apbt_address = (unsigned long)mtmr->phys_addr; -+ if (!apbt_address) { -+ printk(KERN_WARNING "No timer base from SFI, use default\n"); -+ apbt_address = APBT_DEFAULT_BASE; -+ } -+ apbt_virt_address = ioremap_nocache(apbt_address, APBT_MMAP_SIZE); -+ if (apbt_virt_address) { -+ pr_debug("Mapped APBT physical addr %p at virtual addr %p\n",\ -+ (void *)apbt_address, (void *)apbt_virt_address); -+ } else { -+ pr_debug("Failed mapping APBT phy address at %p\n",\ -+ (void *)apbt_address); -+ goto panic_noapbt; -+ } -+ apbt_freq = mtmr->freq_hz / USEC_PER_SEC; -+ sfi_free_mtmr(mtmr); -+ -+ /* Now figure out the physical timer id for clocksource device */ -+ mtmr = sfi_get_mtmr(APBT_CLOCKSOURCE_NUM); -+ if (mtmr == NULL) -+ goto panic_noapbt; -+ -+ /* Now figure out the physical timer id */ -+ phy_cs_timer_id = (unsigned int)(mtmr->phys_addr & 0xff) -+ / APBTMRS_REG_SIZE; -+ pr_debug("Use timer %d for clocksource\n", phy_cs_timer_id); -+ return; -+ -+panic_noapbt: -+ panic("Failed to setup APB system timer\n"); -+ -+} -+ -+static inline void apbt_clear_mapping(void) -+{ -+ iounmap(apbt_virt_address); -+ apbt_virt_address = NULL; -+} -+ -+/* -+ * APBT timer interrupt enable / disable -+ */ -+static inline int is_apbt_capable(void) -+{ -+ return apbt_virt_address ? 1 : 0; -+} -+ -+static struct clocksource clocksource_apbt = { -+ .name = "apbt", -+ .rating = APBT_CLOCKSOURCE_RATING, -+ .read = apbt_read_clocksource, -+ .mask = APBT_MASK, -+ .shift = APBT_SHIFT, -+ .flags = CLOCK_SOURCE_IS_CONTINUOUS, -+ .resume = apbt_restart_clocksource, -+}; -+ -+/* boot APB clock event device */ -+static struct clock_event_device apbt_clockevent = { -+ .name = "apbt0", -+ .features = CLOCK_EVT_FEAT_PERIODIC | CLOCK_EVT_FEAT_ONESHOT, -+ .set_mode = apbt_set_mode, -+ .set_next_event = apbt_next_event, -+ .shift = APBT_SHIFT, -+ .irq = 0, -+ .rating = APBT_CLOCKEVENT_RATING, -+}; -+ -+/* -+ * if user does not want to use per CPU apb timer, just give it a lower rating -+ * than local apic timer and skip the late per cpu timer init. -+ */ -+static inline int __init setup_x86_mrst_timer(char *arg) -+{ -+ if (!arg) -+ return -EINVAL; -+ -+ if (strcmp("apbt_only", arg) == 0) -+ disable_apbt_percpu = 0; -+ else if (strcmp("lapic_and_apbt", arg) == 0) -+ disable_apbt_percpu = 1; -+ else { -+ pr_warning("X86 MRST timer option %s not recognised" -+ " use x86_mrst_timer=apbt_only or lapic_and_apbt\n", -+ arg); -+ return -EINVAL; -+ } -+ return 0; -+} -+__setup("x86_mrst_timer=", setup_x86_mrst_timer); -+ -+/* -+ * start count down from 0xffff_ffff. this is done by toggling the enable bit -+ * then load initial load count to ~0. -+ */ -+static void apbt_start_counter(int n) -+{ -+ unsigned long ctrl = apbt_readl(n, APBTMR_N_CONTROL); -+ -+ ctrl &= ~APBTMR_CONTROL_ENABLE; -+ apbt_writel(n, ctrl, APBTMR_N_CONTROL); -+ apbt_writel(n, ~0, APBTMR_N_LOAD_COUNT); -+ /* enable, mask interrupt */ -+ ctrl &= ~APBTMR_CONTROL_MODE_PERIODIC; -+ ctrl |= (APBTMR_CONTROL_ENABLE | APBTMR_CONTROL_INT); -+ apbt_writel(n, ctrl, APBTMR_N_CONTROL); -+ /* read it once to get cached counter value initialized */ -+ apbt_read_clocksource(&clocksource_apbt); -+} -+ -+static irqreturn_t apbt_interrupt_handler(int irq, void *data) -+{ -+ struct apbt_dev *dev = (struct apbt_dev *)data; -+ struct clock_event_device *aevt = &dev->evt; -+ -+ if (!aevt->event_handler) { -+ printk(KERN_INFO "Spurious APBT timer interrupt on %d\n", -+ dev->num); -+ return IRQ_NONE; -+ } -+ aevt->event_handler(aevt); -+ return IRQ_HANDLED; -+} -+ -+static void apbt_restart_clocksource(void) -+{ -+ apbt_start_counter(phy_cs_timer_id); -+} -+ -+/* Setup IRQ routing via IOAPIC */ -+#ifdef CONFIG_SMP -+static void apbt_setup_irq(struct apbt_dev *adev) -+{ -+ struct irq_chip *chip; -+ struct irq_desc *desc; -+ -+ /* timer0 irq has been setup early */ -+ if (adev->irq == 0) -+ return; -+ desc = irq_to_desc(adev->irq); -+ chip = get_irq_chip(adev->irq); -+ disable_irq(adev->irq); -+ desc->status |= IRQ_MOVE_PCNTXT; -+ irq_set_affinity(adev->irq, cpumask_of(adev->cpu)); -+ /* APB timer irqs are set up as mp_irqs, timer is edge triggerred */ -+ set_irq_chip_and_handler_name(adev->irq, chip, handle_edge_irq, "edge"); -+ enable_irq(adev->irq); -+ if (system_state == SYSTEM_BOOTING) -+ if (request_irq(adev->irq, apbt_interrupt_handler, -+ IRQF_TIMER | IRQF_DISABLED|IRQF_NOBALANCING, adev->name, adev)) { -+ printk(KERN_ERR "Failed request IRQ for APBT%d\n", adev->num); -+ } -+} -+#endif -+ -+static void apbt_enable_int(int n) -+{ -+ unsigned long ctrl = apbt_readl(n, APBTMR_N_CONTROL); -+ /* clear pending intr */ -+ apbt_readl(n, APBTMR_N_EOI); -+ ctrl &= ~APBTMR_CONTROL_INT; -+ apbt_writel(n, ctrl, APBTMR_N_CONTROL); -+} -+ -+static void apbt_disable_int(int n) -+{ -+ unsigned long ctrl = apbt_readl(n, APBTMR_N_CONTROL); -+ -+ ctrl |= APBTMR_CONTROL_INT; -+ apbt_writel(n, ctrl, APBTMR_N_CONTROL); -+} -+ -+ -+static int apbt_clockevent_register(void) -+{ -+ struct sfi_timer_table_entry *mtmr; -+ -+ mtmr = sfi_get_mtmr(APBT_CLOCKEVENT0_NUM); -+ if (mtmr == NULL) { -+ printk(KERN_ERR "Failed to get MTMR %d from SFI\n", -+ APBT_CLOCKEVENT0_NUM); -+ return -ENODEV; -+ } -+ -+ /* -+ * We need to calculate the scaled math multiplication factor for -+ * nanosecond to apbt tick conversion. -+ * mult = (nsec/cycle)*2^APBT_SHIFT -+ */ -+ apbt_clockevent.mult = div_sc((unsigned long) mtmr->freq_hz -+ , NSEC_PER_SEC, APBT_SHIFT); -+ -+ /* Calculate the min / max delta */ -+ apbt_clockevent.max_delta_ns = clockevent_delta2ns(0x7FFFFFFF, -+ &apbt_clockevent); -+ apbt_clockevent.min_delta_ns = clockevent_delta2ns( -+ APBT_MIN_DELTA_USEC*apbt_freq, -+ &apbt_clockevent); -+ /* -+ * Start apbt with the boot cpu mask and make it -+ * global if not used for per cpu timer. -+ */ -+ apbt_clockevent.cpumask = cpumask_of(smp_processor_id()); -+ -+ if (disable_apbt_percpu) { -+ apbt_clockevent.rating = APBT_CLOCKEVENT_RATING - 100; -+ global_clock_event = &apbt_clockevent; -+ printk(KERN_DEBUG "%s clockevent registered as global\n", -+ global_clock_event->name); -+ } -+ if (request_irq(apbt_clockevent.irq, apbt_interrupt_handler, -+ IRQF_TIMER | IRQF_DISABLED | IRQF_NOBALANCING, -+ apbt_clockevent.name, &apbt_clockevent)) { -+ printk(KERN_ERR "Failed request IRQ for APBT%d\n", -+ apbt_clockevent.irq); -+ } -+ -+ clockevents_register_device(&apbt_clockevent); -+ /* Start APBT 0 interrupts */ -+ apbt_enable_int(APBT_CLOCKEVENT0_NUM); -+ -+ sfi_free_mtmr(mtmr); -+ return 0; -+} -+ -+#ifdef CONFIG_SMP -+/* Should be called with per cpu */ -+void apbt_setup_secondary_clock(void) -+{ -+ struct apbt_dev *adev; -+ struct clock_event_device *aevt; -+ int cpu; -+ -+ /* Don't register boot CPU clockevent */ -+ cpu = smp_processor_id(); -+ if (cpu == boot_cpu_id) -+ return; -+ /* -+ * We need to calculate the scaled math multiplication factor for -+ * nanosecond to apbt tick conversion. -+ * mult = (nsec/cycle)*2^APBT_SHIFT -+ */ -+ printk(KERN_INFO "Init per CPU clockevent %d\n", cpu); -+ adev = &per_cpu(cpu_apbt_dev, cpu); -+ aevt = &adev->evt; -+ -+ memcpy(aevt, &apbt_clockevent, sizeof(*aevt)); -+ aevt->cpumask = cpumask_of(cpu); -+ aevt->name = adev->name; -+ aevt->mode = CLOCK_EVT_MODE_UNUSED; -+ -+ printk(KERN_INFO "Registering CPU %d clockevent device %s, mask %08x\n", -+ cpu, aevt->name, *(u32 *)aevt->cpumask); -+ -+ apbt_setup_irq(adev); -+ -+ clockevents_register_device(aevt); -+ -+ apbt_enable_int(cpu); -+ -+ return; -+} -+ -+static int apbt_cpuhp_notify(struct notifier_block *n, -+ unsigned long action, void *hcpu) -+{ -+ unsigned long cpu = (unsigned long)hcpu; -+ struct apbt_dev *adev = &per_cpu(cpu_apbt_dev, cpu); -+ -+ switch (action & 0xf) { -+ case CPU_DEAD: -+ apbt_disable_int(cpu); -+ if (system_state == SYSTEM_RUNNING) -+ pr_debug("skipping APBT CPU %lu offline\n", cpu); -+ else if (adev) { -+ pr_debug("APBT clockevent for cpu %lu offline\n", cpu); -+ free_irq(adev->irq, adev); -+ } -+ break; -+ } -+ return NOTIFY_OK; -+} -+ -+static __init int apbt_late_init(void) -+{ -+ if (disable_apbt_percpu) -+ return 0; -+ /* This notifier should be called after workqueue is ready */ -+ hotcpu_notifier(apbt_cpuhp_notify, -20); -+ return 0; -+} -+fs_initcall(apbt_late_init); -+#else -+ -+void apbt_setup_secondary_clock(void) {} -+ -+#endif /* CONFIG_SMP */ -+ -+static void apbt_set_mode(enum clock_event_mode mode, -+ struct clock_event_device *evt) -+{ -+ unsigned long ctrl; -+ uint64_t delta; -+ int timer_num; -+ struct apbt_dev *adev = EVT_TO_APBT_DEV(evt); -+ -+ timer_num = adev->num; -+ pr_debug("%s CPU %d timer %d mode=%d\n", -+ __func__, first_cpu(*evt->cpumask), timer_num, mode); -+ -+ switch (mode) { -+ case CLOCK_EVT_MODE_PERIODIC: -+ delta = ((uint64_t)(NSEC_PER_SEC/HZ)) * apbt_clockevent.mult; -+ delta >>= apbt_clockevent.shift; -+ ctrl = apbt_readl(timer_num, APBTMR_N_CONTROL); -+ ctrl |= APBTMR_CONTROL_MODE_PERIODIC; -+ apbt_writel(timer_num, ctrl, APBTMR_N_CONTROL); -+ /* -+ * DW APB p. 46, have to disable timer before load counter, -+ * may cause sync problem. -+ */ -+ ctrl &= ~APBTMR_CONTROL_ENABLE; -+ apbt_writel(timer_num, ctrl, APBTMR_N_CONTROL); -+ udelay(1); -+ pr_debug("Setting clock period %d for HZ %d\n", (int)delta, HZ); -+ apbt_writel(timer_num, delta, APBTMR_N_LOAD_COUNT); -+ ctrl |= APBTMR_CONTROL_ENABLE; -+ apbt_writel(timer_num, ctrl, APBTMR_N_CONTROL); -+ break; -+ /* APB timer does not have one-shot mode, use free running mode */ -+ case CLOCK_EVT_MODE_ONESHOT: -+ ctrl = apbt_readl(timer_num, APBTMR_N_CONTROL); -+ /* -+ * set free running mode, this mode will let timer reload max -+ * timeout which will give time (3min on 25MHz clock) to rearm -+ * the next event, therefore emulate the one-shot mode. -+ */ -+ ctrl &= ~APBTMR_CONTROL_ENABLE; -+ ctrl &= ~APBTMR_CONTROL_MODE_PERIODIC; -+ -+ apbt_writel(timer_num, ctrl, APBTMR_N_CONTROL); -+ /* write again to set free running mode */ -+ apbt_writel(timer_num, ctrl, APBTMR_N_CONTROL); -+ -+ /* -+ * DW APB p. 46, load counter with all 1s before starting free -+ * running mode. -+ */ -+ apbt_writel(timer_num, ~0, APBTMR_N_LOAD_COUNT); -+ ctrl &= ~APBTMR_CONTROL_INT; -+ ctrl |= APBTMR_CONTROL_ENABLE; -+ apbt_writel(timer_num, ctrl, APBTMR_N_CONTROL); -+ break; -+ -+ case CLOCK_EVT_MODE_UNUSED: -+ case CLOCK_EVT_MODE_SHUTDOWN: -+ apbt_disable_int(timer_num); -+ ctrl = apbt_readl(timer_num, APBTMR_N_CONTROL); -+ ctrl &= ~APBTMR_CONTROL_ENABLE; -+ apbt_writel(timer_num, ctrl, APBTMR_N_CONTROL); -+ break; -+ -+ case CLOCK_EVT_MODE_RESUME: -+ apbt_enable_int(timer_num); -+ break; -+ } -+} -+ -+static int apbt_next_event(unsigned long delta, -+ struct clock_event_device *evt) -+{ -+ unsigned long ctrl; -+ int timer_num; -+ -+ struct apbt_dev *adev = EVT_TO_APBT_DEV(evt); -+ -+ timer_num = adev->num; -+ /* Disable timer */ -+ ctrl = apbt_readl(timer_num, APBTMR_N_CONTROL); -+ ctrl &= ~APBTMR_CONTROL_ENABLE; -+ apbt_writel(timer_num, ctrl, APBTMR_N_CONTROL); -+ /* write new count */ -+ apbt_writel(timer_num, delta, APBTMR_N_LOAD_COUNT); -+ ctrl |= APBTMR_CONTROL_ENABLE; -+ apbt_writel(timer_num, ctrl, APBTMR_N_CONTROL); -+ return 0; -+} -+ -+/* -+ * APB timer clock is not in sync with pclk on Langwell, which translates to -+ * unreliable read value caused by sampling error. the error does not add up -+ * overtime and only happens when sampling a 0 as a 1 by mistake. so the time -+ * would go backwards. the following code is trying to prevent time traveling -+ * backwards. little bit paranoid. -+ */ -+static cycle_t apbt_read_clocksource(struct clocksource *cs) -+{ -+ unsigned long t0, t1, t2; -+ static unsigned long last_read; -+ -+bad_count: -+ t1 = apbt_readl(phy_cs_timer_id, -+ APBTMR_N_CURRENT_VALUE); -+ t2 = apbt_readl(phy_cs_timer_id, -+ APBTMR_N_CURRENT_VALUE); -+ if (unlikely(t1 < t2)) { -+ pr_debug("APBT: read current count error %lx:%lx:%lx\n", -+ t1, t2, t2 - t1); -+ goto bad_count; -+ } -+ /* -+ * check against cached last read, makes sure time does not go back. -+ * it could be a normal rollover but we will do tripple check anyway -+ */ -+ if (unlikely(t2 > last_read)) { -+ /* check if we have a normal rollover */ -+ unsigned long raw_intr_status = -+ apbt_readl_reg(APBTMRS_RAW_INT_STATUS); -+ /* -+ * cs timer interrupt is masked but raw intr bit is set if -+ * rollover occurs. then we read EOI reg to clear it. -+ */ -+ if (raw_intr_status & (1 << phy_cs_timer_id)) { -+ apbt_readl(phy_cs_timer_id, APBTMR_N_EOI); -+ goto out; -+ } -+ pr_debug("APB CS going back %lx:%lx:%lx ", -+ t2, last_read, t2 - last_read); -+bad_count_x3: -+ pr_debug(KERN_INFO "tripple check enforced\n"); -+ t0 = apbt_readl(phy_cs_timer_id, -+ APBTMR_N_CURRENT_VALUE); -+ udelay(1); -+ t1 = apbt_readl(phy_cs_timer_id, -+ APBTMR_N_CURRENT_VALUE); -+ udelay(1); -+ t2 = apbt_readl(phy_cs_timer_id, -+ APBTMR_N_CURRENT_VALUE); -+ if ((t2 > t1) || (t1 > t0)) { -+ printk(KERN_ERR "Error: APB CS tripple check failed\n"); -+ goto bad_count_x3; -+ } -+ } -+out: -+ last_read = t2; -+ return (cycle_t)~t2; -+} -+ -+static int apbt_clocksource_register(void) -+{ -+ u64 start, now; -+ cycle_t t1; -+ -+ /* Start the counter, use timer 2 as source, timer 0/1 for event */ -+ apbt_start_counter(phy_cs_timer_id); -+ -+ /* Verify whether apbt counter works */ -+ t1 = apbt_read_clocksource(&clocksource_apbt); -+ rdtscll(start); -+ -+ /* -+ * We don't know the TSC frequency yet, but waiting for -+ * 200000 TSC cycles is safe: -+ * 4 GHz == 50us -+ * 1 GHz == 200us -+ */ -+ do { -+ rep_nop(); -+ rdtscll(now); -+ } while ((now - start) < 200000UL); -+ -+ /* APBT is the only always on clocksource, it has to work! */ -+ if (t1 == apbt_read_clocksource(&clocksource_apbt)) -+ panic("APBT counter not counting. APBT disabled\n"); -+ -+ /* -+ * initialize and register APBT clocksource -+ * convert that to ns/clock cycle -+ * mult = (ns/c) * 2^APBT_SHIFT -+ */ -+ clocksource_apbt.mult = div_sc(MSEC_PER_SEC, -+ (unsigned long) apbt_freq, APBT_SHIFT); -+ clocksource_register(&clocksource_apbt); -+ -+ return 0; -+} -+ -+/* -+ * Early setup the APBT timer, only use timer 0 for booting then switch to -+ * per CPU timer if possible. -+ * returns 1 if per cpu apbt is setup -+ * returns 0 if no per cpu apbt is chosen -+ * panic if set up failed, this is the only platform timer on Moorestown. -+ */ -+void __init apbt_time_init(void) -+{ -+#ifdef CONFIG_SMP -+ int i; -+ struct sfi_timer_table_entry *p_mtmr; -+ unsigned int percpu_timer; -+ struct apbt_dev *adev; -+#endif -+ -+ if (apb_timer_block_enabled) -+ return; -+ apbt_set_mapping(); -+ if (apbt_virt_address) { -+ pr_debug("Found APBT version 0x%lx\n",\ -+ apbt_readl_reg(APBTMRS_COMP_VERSION)); -+ } else -+ goto out_noapbt; -+ /* -+ * Read the frequency and check for a sane value, for ESL model -+ * we extend the possible clock range to allow time scaling. -+ */ -+ -+ if (apbt_freq < APBT_MIN_FREQ || apbt_freq > APBT_MAX_FREQ) { -+ pr_debug("APBT has invalid freq 0x%llx\n", apbt_freq); -+ goto out_noapbt; -+ } -+ if (apbt_clocksource_register()) { -+ pr_debug("APBT has failed to register clocksource\n"); -+ goto out_noapbt; -+ } -+ if (!apbt_clockevent_register()) -+ apb_timer_block_enabled = 1; -+ else { -+ pr_debug("APBT has failed to register clockevent\n"); -+ goto out_noapbt; -+ } -+#ifdef CONFIG_SMP -+ /* kernel cmdline disable apb timer, so we will use lapic timers */ -+ if (disable_apbt_percpu) { -+ printk(KERN_INFO "apbt: disabled per cpu timer\n"); -+ return; -+ } -+ pr_debug("%s: %d CPUs online\n", __func__, num_online_cpus()); -+ if (num_possible_cpus() <= sfi_mtimer_num) { -+ percpu_timer = 1; -+ apbt_num_timers_used = num_possible_cpus(); -+ } else { -+ percpu_timer = 0; -+ apbt_num_timers_used = 1; -+ adev = &per_cpu(cpu_apbt_dev, 0); -+ adev->flags &= ~APBT_DEV_USED; -+ } -+ pr_debug("%s: %d APB timers used\n", __func__, apbt_num_timers_used); -+ -+ /* here we set up per CPU timer data structure */ -+ apbt_devs = kzalloc(sizeof(struct apbt_dev) * apbt_num_timers_used, -+ GFP_KERNEL); -+ if (!apbt_devs) { -+ printk(KERN_ERR "Failed to allocate APB timer devices\n"); -+ return; -+ } -+ for (i = 0; i < apbt_num_timers_used; i++) { -+ adev = &per_cpu(cpu_apbt_dev, i); -+ adev->num = i; -+ adev->cpu = i; -+ p_mtmr = sfi_get_mtmr(i); -+ if (p_mtmr) { -+ adev->tick = p_mtmr->freq_hz; -+ adev->irq = p_mtmr->irq; -+ } else -+ printk(KERN_ERR "Failed to get timer for cpu %d\n", i); -+ adev->count = 0; -+ sprintf(adev->name, "apbt%d", i); -+ } -+#endif -+ -+ return; -+ -+out_noapbt: -+ apbt_clear_mapping(); -+ apb_timer_block_enabled = 0; -+ panic("failed to enable APB timer\n"); -+} -+ -+static inline void apbt_disable(int n) -+{ -+ if (is_apbt_capable()) { -+ unsigned long ctrl = apbt_readl(n, APBTMR_N_CONTROL); -+ ctrl &= ~APBTMR_CONTROL_ENABLE; -+ apbt_writel(n, ctrl, APBTMR_N_CONTROL); -+ } -+} -+ -+/* called before apb_timer_enable, use early map */ -+unsigned long apbt_quick_calibrate() -+{ -+ int i, scale; -+ u64 old, new; -+ cycle_t t1, t2; -+ unsigned long khz = 0; -+ u32 loop, shift; -+ -+ apbt_set_mapping(); -+ apbt_start_counter(phy_cs_timer_id); -+ -+ /* check if the timer can count down, otherwise return */ -+ old = apbt_read_clocksource(&clocksource_apbt); -+ i = 10000; -+ while (--i) { -+ if (old != apbt_read_clocksource(&clocksource_apbt)) -+ break; -+ } -+ if (!i) -+ goto failed; -+ -+ /* count 16 ms */ -+ loop = (apbt_freq * 1000) << 4; -+ -+ /* restart the timer to ensure it won't get to 0 in the calibration */ -+ apbt_start_counter(phy_cs_timer_id); -+ -+ old = apbt_read_clocksource(&clocksource_apbt); -+ old += loop; -+ -+ t1 = __native_read_tsc(); -+ -+ do { -+ new = apbt_read_clocksource(&clocksource_apbt); -+ } while (new < old); -+ -+ t2 = __native_read_tsc(); -+ -+ shift = 5; -+ if (unlikely(loop >> shift == 0)) { -+ printk(KERN_INFO -+ "APBT TSC calibration failed, not enough resolution\n"); -+ return 0; -+ } -+ scale = (int)div_u64((t2 - t1), loop >> shift); -+ khz = (scale * apbt_freq * 1000) >> shift; -+ printk(KERN_INFO "TSC freq calculated by APB timer is %lu khz\n", khz); -+ return khz; -+failed: -+ return 0; -+} -Index: linux-2.6.33/arch/x86/include/asm/mrst.h -=================================================================== ---- /dev/null -+++ linux-2.6.33/arch/x86/include/asm/mrst.h -@@ -0,0 +1,16 @@ -+/* -+ * mrst.h: Intel Moorestown platform specific setup code -+ * -+ * (C) Copyright 2009 Intel Corporation -+ * -+ * This program is free software; you can redistribute it and/or -+ * modify it under the terms of the GNU General Public License -+ * as published by the Free Software Foundation; version 2 -+ * of the License. -+ */ -+#ifndef _ASM_X86_MRST_H -+#define _ASM_X86_MRST_H -+extern int pci_mrst_init(void); -+int __init sfi_parse_mrtc(struct sfi_table_header *table); -+ -+#endif /* _ASM_X86_MRST_H */ -Index: linux-2.6.33/arch/x86/kernel/mrst.c -=================================================================== ---- linux-2.6.33.orig/arch/x86/kernel/mrst.c -+++ linux-2.6.33/arch/x86/kernel/mrst.c -@@ -2,16 +2,234 @@ - * mrst.c: Intel Moorestown platform specific setup code - * - * (C) Copyright 2008 Intel Corporation -- * Author: Jacob Pan (jacob.jun.pan@intel.com) - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * as published by the Free Software Foundation; version 2 - * of the License. - */ -+ - #include <linux/init.h> -+#include <linux/kernel.h> -+#include <linux/sfi.h> -+#include <linux/bitmap.h> -+#include <linux/threads.h> -+#include <linux/spi/spi.h> -+#include <linux/spi/langwell_pmic_gpio.h> -+#include <linux/i2c.h> -+#include <linux/sfi.h> -+#include <linux/i2c/pca953x.h> -+#include <linux/gpio_keys.h> -+#include <linux/input.h> -+#include <linux/platform_device.h> -+#include <linux/irq.h> - -+#include <asm/string.h> - #include <asm/setup.h> -+#include <asm/mpspec_def.h> -+#include <asm/hw_irq.h> -+#include <asm/apic.h> -+#include <asm/io_apic.h> -+#include <asm/apb_timer.h> -+#include <asm/io.h> -+#include <asm/mrst.h> -+#include <asm/vrtc.h> -+#include <asm/ipc_defs.h> -+#include <asm/reboot.h> -+#include <asm/i8259.h> -+ -+#define LANGWELL_GPIO_ALT_ADDR 0xff12c038 -+#define MRST_I2C_BUSNUM 3 -+#define SFI_MRTC_MAX 8 -+ -+static u32 sfi_mtimer_usage[SFI_MTMR_MAX_NUM]; -+static struct sfi_timer_table_entry sfi_mtimer_array[SFI_MTMR_MAX_NUM]; -+int sfi_mtimer_num; -+ -+struct sfi_rtc_table_entry sfi_mrtc_array[SFI_MRTC_MAX]; -+EXPORT_SYMBOL_GPL(sfi_mrtc_array); -+int sfi_mrtc_num; -+ -+static inline void assign_to_mp_irq(struct mpc_intsrc *m, -+ struct mpc_intsrc *mp_irq) -+{ -+ memcpy(mp_irq, m, sizeof(struct mpc_intsrc)); -+} -+ -+static inline int mp_irq_cmp(struct mpc_intsrc *mp_irq, -+ struct mpc_intsrc *m) -+{ -+ return memcmp(mp_irq, m, sizeof(struct mpc_intsrc)); -+} -+ -+static void save_mp_irq(struct mpc_intsrc *m) -+{ -+ int i; -+ -+ for (i = 0; i < mp_irq_entries; i++) { -+ if (!mp_irq_cmp(&mp_irqs[i], m)) -+ return; -+ } -+ -+ assign_to_mp_irq(m, &mp_irqs[mp_irq_entries]); -+ if (++mp_irq_entries == MAX_IRQ_SOURCES) -+ panic("Max # of irq sources exceeded!!\n"); -+} -+ -+/* parse all the mtimer info to a global mtimer array */ -+static int __init sfi_parse_mtmr(struct sfi_table_header *table) -+{ -+ struct sfi_table_simple *sb; -+ struct sfi_timer_table_entry *pentry; -+ struct mpc_intsrc mp_irq; -+ int totallen; -+ -+ sb = (struct sfi_table_simple *)table; -+ if (!sfi_mtimer_num) { -+ sfi_mtimer_num = SFI_GET_NUM_ENTRIES(sb, -+ struct sfi_timer_table_entry); -+ pentry = (struct sfi_timer_table_entry *) sb->pentry; -+ totallen = sfi_mtimer_num * sizeof(*pentry); -+ memcpy(sfi_mtimer_array, pentry, totallen); -+ } -+ -+ printk(KERN_INFO "SFI: MTIMER info (num = %d):\n", sfi_mtimer_num); -+ pentry = sfi_mtimer_array; -+ for (totallen = 0; totallen < sfi_mtimer_num; totallen++, pentry++) { -+ printk(KERN_INFO "timer[%d]: paddr = 0x%08x, freq = %dHz," -+ " irq = %d\n", totallen, (u32)pentry->phys_addr, -+ pentry->freq_hz, pentry->irq); -+ if (!pentry->irq) -+ continue; -+ mp_irq.type = MP_IOAPIC; -+ mp_irq.irqtype = mp_INT; -+ mp_irq.irqflag = 0; -+ mp_irq.srcbus = 0; -+ mp_irq.srcbusirq = pentry->irq; /* IRQ */ -+ mp_irq.dstapic = MP_APIC_ALL; -+ mp_irq.dstirq = pentry->irq; -+ save_mp_irq(&mp_irq); -+ } -+ -+ return 0; -+} -+ -+struct sfi_timer_table_entry *sfi_get_mtmr(int hint) -+{ -+ int i; -+ if (hint < sfi_mtimer_num) { -+ if (!sfi_mtimer_usage[hint]) { -+ printk(KERN_DEBUG "hint taken for timer %d irq %d\n",\ -+ hint, sfi_mtimer_array[hint].irq); -+ sfi_mtimer_usage[hint] = 1; -+ return &sfi_mtimer_array[hint]; -+ } -+ } -+ /* take the first timer available */ -+ for (i = 0; i < sfi_mtimer_num;) { -+ if (!sfi_mtimer_usage[i]) { -+ sfi_mtimer_usage[i] = 1; -+ return &sfi_mtimer_array[i]; -+ } -+ i++; -+ } -+ return NULL; -+} -+ -+void sfi_free_mtmr(struct sfi_timer_table_entry *mtmr) -+{ -+ int i; -+ for (i = 0; i < sfi_mtimer_num;) { -+ if (mtmr->irq == sfi_mtimer_array[i].irq) { -+ sfi_mtimer_usage[i] = 0; -+ return; -+ } -+ i++; -+ } -+} -+ -+/* parse all the mrtc info to a global mrtc array */ -+int __init sfi_parse_mrtc(struct sfi_table_header *table) -+{ -+ struct sfi_table_simple *sb; -+ struct sfi_rtc_table_entry *pentry; -+ struct mpc_intsrc mp_irq; -+ -+ int totallen; -+ -+ sb = (struct sfi_table_simple *)table; -+ if (!sfi_mrtc_num) { -+ sfi_mrtc_num = SFI_GET_NUM_ENTRIES(sb, -+ struct sfi_rtc_table_entry); -+ pentry = (struct sfi_rtc_table_entry *)sb->pentry; -+ totallen = sfi_mrtc_num * sizeof(*pentry); -+ memcpy(sfi_mrtc_array, pentry, totallen); -+ } -+ -+ printk(KERN_INFO "SFI: RTC info (num = %d):\n", sfi_mrtc_num); -+ pentry = sfi_mrtc_array; -+ for (totallen = 0; totallen < sfi_mrtc_num; totallen++, pentry++) { -+ printk(KERN_INFO "RTC[%d]: paddr = 0x%08x, irq = %d\n", -+ totallen, (u32)pentry->phys_addr, pentry->irq); -+ mp_irq.type = MP_IOAPIC; -+ mp_irq.irqtype = mp_INT; -+ mp_irq.irqflag = 0; -+ mp_irq.srcbus = 0; -+ mp_irq.srcbusirq = pentry->irq; /* IRQ */ -+ mp_irq.dstapic = MP_APIC_ALL; -+ mp_irq.dstirq = pentry->irq; -+ save_mp_irq(&mp_irq); -+ } -+ return 0; -+} -+ -+/* -+ * the secondary clock in Moorestown can be APBT or LAPIC clock, default to -+ * APBT but cmdline option can also override it. -+ */ -+static void __cpuinit mrst_setup_secondary_clock(void) -+{ -+ /* restore default lapic clock if disabled by cmdline */ -+ if (disable_apbt_percpu) -+ return setup_secondary_APIC_clock(); -+ apbt_setup_secondary_clock(); -+} -+ -+static unsigned long __init mrst_calibrate_tsc(void) -+{ -+ unsigned long flags, fast_calibrate; -+ -+ local_irq_save(flags); -+ fast_calibrate = apbt_quick_calibrate(); -+ local_irq_restore(flags); -+ -+ if (fast_calibrate) -+ return fast_calibrate; -+ -+ return 0; -+} -+ -+void __init mrst_time_init(void) -+{ -+ sfi_table_parse(SFI_SIG_MTMR, NULL, NULL, sfi_parse_mtmr); -+ pre_init_apic_IRQ0(); -+ apbt_time_init(); -+} -+ -+void __init mrst_rtc_init(void) -+{ -+ sfi_table_parse(SFI_SIG_MRTC, NULL, NULL, sfi_parse_mrtc); -+} -+ -+static void mrst_power_off(void) -+{ -+ lnw_ipc_single_cmd(0xf1, 1, 0, 0); -+} -+ -+static void mrst_reboot(void) -+{ -+ lnw_ipc_single_cmd(0xf1, 0, 0, 0); -+} - - /* - * Moorestown specific x86_init function overrides and early setup -@@ -21,4 +239,241 @@ void __init x86_mrst_early_setup(void) - { - x86_init.resources.probe_roms = x86_init_noop; - x86_init.resources.reserve_resources = x86_init_noop; -+ x86_init.timers.timer_init = mrst_time_init; -+ x86_init.irqs.pre_vector_init = x86_init_noop; -+ -+ x86_cpuinit.setup_percpu_clockev = mrst_setup_secondary_clock; -+ -+ x86_platform.calibrate_tsc = mrst_calibrate_tsc; -+ x86_platform.get_wallclock = vrtc_get_time; -+ x86_platform.set_wallclock = vrtc_set_mmss; -+ -+ x86_init.pci.init = pci_mrst_init; -+ x86_init.pci.fixup_irqs = x86_init_noop; -+ -+ x86_init.oem.banner = mrst_rtc_init; -+ legacy_pic = &null_legacy_pic; -+ -+ /* Moorestown specific power_off/restart method */ -+ pm_power_off = mrst_power_off; -+ machine_ops.emergency_restart = mrst_reboot; - } -+ -+/* -+ * the dummy SPI2 salves are in SPIB table with host_num = 0, but their -+ * chip_selects begin with MRST_SPI2_CS_START, this will save a dummy ugly -+ * SPI2 controller driver -+ */ -+#define MRST_SPI2_CS_START 4 -+static struct langwell_pmic_gpio_platform_data pmic_gpio_pdata; -+ -+static int __init sfi_parse_spib(struct sfi_table_header *table) -+{ -+ struct sfi_table_simple *sb; -+ struct sfi_spi_table_entry *pentry; -+ struct spi_board_info *info; -+ int num, i, j; -+ int ioapic; -+ struct io_apic_irq_attr irq_attr; -+ -+ sb = (struct sfi_table_simple *)table; -+ num = SFI_GET_NUM_ENTRIES(sb, struct sfi_spi_table_entry); -+ pentry = (struct sfi_spi_table_entry *) sb->pentry; -+ -+ info = kzalloc(num * sizeof(*info), GFP_KERNEL); -+ if (!info) { -+ pr_info("%s(): Error in kzalloc\n", __func__); -+ return -ENOMEM; -+ } -+ -+ if (num) -+ pr_info("Moorestown SPI devices info:\n"); -+ -+ for (i = 0, j = 0; i < num; i++, pentry++) { -+ strncpy(info[j].modalias, pentry->name, 16); -+ info[j].irq = pentry->irq_info; -+ info[j].bus_num = pentry->host_num; -+ info[j].chip_select = pentry->cs; -+ info[j].max_speed_hz = 3125000; /* hard coded */ -+ if (info[i].chip_select >= MRST_SPI2_CS_START) { -+ /* these SPI2 devices are not exposed to system as PCI -+ * devices, but they have separate RTE entry in IOAPIC -+ * so we have to enable them one by one here -+ */ -+ ioapic = mp_find_ioapic(info[j].irq); -+ irq_attr.ioapic = ioapic; -+ irq_attr.ioapic_pin = info[j].irq; -+ irq_attr.trigger = 1; -+ irq_attr.polarity = 1; -+ io_apic_set_pci_routing(NULL, info[j].irq, -+ &irq_attr); -+ } -+ info[j].platform_data = pentry->dev_info; -+ -+ if (!strcmp(pentry->name, "pmic_gpio")) { -+ memcpy(&pmic_gpio_pdata, pentry->dev_info, 8); -+ pmic_gpio_pdata.gpiointr = 0xffffeff8; -+ info[j].platform_data = &pmic_gpio_pdata; -+ } -+ pr_info("info[%d]: name = %16s, irq = 0x%04x, bus = %d, " -+ "cs = %d\n", j, info[j].modalias, info[j].irq, -+ info[j].bus_num, info[j].chip_select); -+ j++; -+ } -+ spi_register_board_info(info, j); -+ kfree(info); -+ return 0; -+} -+ -+static struct pca953x_platform_data max7315_pdata; -+static struct pca953x_platform_data max7315_pdata_2; -+ -+static int __init sfi_parse_i2cb(struct sfi_table_header *table) -+{ -+ struct sfi_table_simple *sb; -+ struct sfi_i2c_table_entry *pentry; -+ struct i2c_board_info *info[MRST_I2C_BUSNUM]; -+ int table_length[MRST_I2C_BUSNUM] = {0}; -+ int num, i, j, busnum; -+ -+ sb = (struct sfi_table_simple *)table; -+ num = SFI_GET_NUM_ENTRIES(sb, struct sfi_i2c_table_entry); -+ pentry = (struct sfi_i2c_table_entry *) sb->pentry; -+ -+ if (num <= 0) -+ return -ENODEV; -+ -+ for (busnum = 0; busnum < MRST_I2C_BUSNUM; busnum++) { -+ info[busnum] = kzalloc(num * sizeof(**info), GFP_KERNEL); -+ if (!info[busnum]) { -+ pr_info("%s(): Error in kzalloc\n", __func__); -+ while (busnum--) -+ kfree(info[busnum]); -+ return -ENOMEM; -+ } -+ } -+ -+ if (num) -+ pr_info("Moorestown I2C devices info:\n"); -+ -+ for (busnum = 0, j = 0; j < num; j++, pentry++) { -+ busnum = pentry->host_num; -+ if (busnum >= MRST_I2C_BUSNUM || busnum < 0) -+ continue; -+ -+ i = table_length[busnum]; -+ strncpy(info[busnum][i].type, pentry->name, 16); -+ info[busnum][i].irq = pentry->irq_info; -+ info[busnum][i].addr = pentry->addr; -+ info[busnum][i].platform_data = pentry->dev_info; -+ table_length[busnum]++; -+ -+ if (!strcmp(pentry->name, "i2c_max7315")) { -+ strcpy(info[busnum][i].type, "max7315"); -+ memcpy(&max7315_pdata, pentry->dev_info, 10); -+ info[busnum][i].platform_data = &max7315_pdata; -+ } -+ else if (!strcmp(pentry->name, "i2c_max7315_2")) { -+ strcpy(info[busnum][i].type, "max7315"); -+ memcpy(&max7315_pdata_2, pentry->dev_info, 10); -+ info[busnum][i].platform_data = &max7315_pdata_2; -+ } -+ -+ pr_info("info[%d]: bus = %d, name = %16s, irq = 0x%04x, addr = " -+ "0x%x\n", i, busnum, info[busnum][i].type, -+ info[busnum][i].irq, info[busnum][i].addr); -+ } -+ -+ for (busnum = 0; busnum < MRST_I2C_BUSNUM; busnum++) { -+ i2c_register_board_info(busnum, info[busnum], -+ table_length[busnum]); -+ } -+ -+ return 0; -+} -+ -+/* setting multi-function-pin */ -+static void set_alt_func(void) -+{ -+ u32 __iomem *mem = ioremap_nocache(LANGWELL_GPIO_ALT_ADDR, 16); -+ u32 value; -+ -+ if (!mem) { -+ pr_err("can not map GPIO controller address.\n"); -+ return; -+ } -+ value = (readl(mem + 1) & 0x0000ffff) | 0x55550000; -+ writel(value, mem + 1); -+ value = (readl(mem + 2) & 0xf0000000) | 0x05555555; -+ writel(value, mem + 2); -+ value = (readl(mem + 3) & 0xfff000ff) | 0x00055500; -+ writel(value, mem + 3); -+ -+ iounmap(mem); -+} -+ -+static int __init mrst_platform_init(void) -+{ -+ sfi_table_parse(SFI_SIG_SPIB, NULL, NULL, sfi_parse_spib); -+ sfi_table_parse(SFI_SIG_I2CB, NULL, NULL, sfi_parse_i2cb); -+ set_alt_func(); -+ return 0; -+} -+ -+arch_initcall(mrst_platform_init); -+ -+static struct gpio_keys_button gpio_button[] = { -+ [0] = { -+ .desc = "power button1", -+ .code = KEY_POWER, -+ .type = EV_KEY, -+ .active_low = 1, -+ .debounce_interval = 3000, /*soft debounce*/ -+ .gpio = 65, -+ }, -+ [1] = { -+ .desc = "programmable button1", -+ .code = KEY_PROG1, -+ .type = EV_KEY, -+ .active_low = 1, -+ .debounce_interval = 20, -+ .gpio = 66, -+ }, -+ [2] = { -+ .desc = "programmable button2", -+ .code = KEY_PROG2, -+ .type = EV_KEY, -+ .active_low = 1, -+ .debounce_interval = 20, -+ .gpio = 69 -+ }, -+ [3] = { -+ .desc = "lid switch", -+ .code = SW_LID, -+ .type = EV_SW, -+ .active_low = 1, -+ .debounce_interval = 20, -+ .gpio = 101 -+ }, -+}; -+ -+static struct gpio_keys_platform_data mrst_gpio_keys = { -+ .buttons = gpio_button, -+ .rep = 1, -+ .nbuttons = sizeof(gpio_button) / sizeof(struct gpio_keys_button), -+}; -+ -+static struct platform_device pb_device = { -+ .name = "gpio-keys", -+ .id = -1, -+ .dev = { -+ .platform_data = &mrst_gpio_keys, -+ }, -+}; -+ -+static int __init pb_keys_init(void) -+{ -+ return platform_device_register(&pb_device); -+} -+ -+late_initcall(pb_keys_init); -Index: linux-2.6.33/arch/x86/include/asm/io_apic.h -=================================================================== ---- linux-2.6.33.orig/arch/x86/include/asm/io_apic.h -+++ linux-2.6.33/arch/x86/include/asm/io_apic.h -@@ -143,8 +143,6 @@ extern int noioapicreroute; - /* 1 if the timer IRQ uses the '8259A Virtual Wire' mode */ - extern int timer_through_8259; - --extern void io_apic_disable_legacy(void); -- - /* - * If we use the IO-APIC for IRQ routing, disable automatic - * assignment of PCI IRQ's. -@@ -189,6 +187,7 @@ extern struct mp_ioapic_gsi mp_gsi_rout - int mp_find_ioapic(int gsi); - int mp_find_ioapic_pin(int ioapic, int gsi); - void __init mp_register_ioapic(int id, u32 address, u32 gsi_base); -+extern void __init pre_init_apic_IRQ0(void); - - #else /* !CONFIG_X86_IO_APIC */ - -Index: linux-2.6.33/arch/x86/pci/mmconfig-shared.c -=================================================================== ---- linux-2.6.33.orig/arch/x86/pci/mmconfig-shared.c -+++ linux-2.6.33/arch/x86/pci/mmconfig-shared.c -@@ -601,7 +601,8 @@ static void __init __pci_mmcfg_init(int - if (!known_bridge) - acpi_sfi_table_parse(ACPI_SIG_MCFG, pci_parse_mcfg); - -- pci_mmcfg_reject_broken(early); -+ if (!acpi_disabled) -+ pci_mmcfg_reject_broken(early); - - if (list_empty(&pci_mmcfg_list)) - return; -Index: linux-2.6.33/arch/x86/pci/Makefile -=================================================================== ---- linux-2.6.33.orig/arch/x86/pci/Makefile -+++ linux-2.6.33/arch/x86/pci/Makefile -@@ -13,7 +13,7 @@ obj-$(CONFIG_X86_VISWS) += visws.o - - obj-$(CONFIG_X86_NUMAQ) += numaq_32.o - --obj-y += common.o early.o -+obj-y += common.o early.o mrst.o - obj-y += amd_bus.o - obj-$(CONFIG_X86_64) += bus_numa.o - -Index: linux-2.6.33/arch/x86/pci/mrst.c -=================================================================== ---- /dev/null -+++ linux-2.6.33/arch/x86/pci/mrst.c -@@ -0,0 +1,262 @@ -+/* -+ * Moorestown PCI support -+ * Copyright (c) 2008 Intel Corporation -+ * Jesse Barnes <jesse.barnes@intel.com> -+ * -+ * Moorestown has an interesting PCI implementation: -+ * - configuration space is memory mapped (as defined by MCFG) -+ * - Lincroft devices also have a real, type 1 configuration space -+ * - Early Lincroft silicon has a type 1 access bug that will cause -+ * a hang if non-existent devices are accessed -+ * - some devices have the "fixed BAR" capability, which means -+ * they can't be relocated or modified; check for that during -+ * BAR sizing -+ * -+ * So, we use the MCFG space for all reads and writes, but also send -+ * Lincroft writes to type 1 space. But only read/write if the device -+ * actually exists, otherwise return all 1s for reads and bit bucket -+ * the writes. -+ */ -+ -+#include <linux/sched.h> -+#include <linux/pci.h> -+#include <linux/ioport.h> -+#include <linux/init.h> -+#include <linux/dmi.h> -+ -+#include <asm/acpi.h> -+#include <asm/segment.h> -+#include <asm/io.h> -+#include <asm/smp.h> -+#include <asm/pci_x86.h> -+#include <asm/hw_irq.h> -+ -+#define PCIE_CAP_OFFSET 0x100 -+ -+/* Fixed BAR fields */ -+#define PCIE_VNDR_CAP_ID_FIXED_BAR 0x00 /* Fixed BAR (TBD) */ -+#define PCI_FIXED_BAR_0_SIZE 0x04 -+#define PCI_FIXED_BAR_1_SIZE 0x08 -+#define PCI_FIXED_BAR_2_SIZE 0x0c -+#define PCI_FIXED_BAR_3_SIZE 0x10 -+#define PCI_FIXED_BAR_4_SIZE 0x14 -+#define PCI_FIXED_BAR_5_SIZE 0x1c -+ -+/** -+ * fixed_bar_cap - return the offset of the fixed BAR cap if found -+ * @bus: PCI bus -+ * @devfn: device in question -+ * -+ * Look for the fixed BAR cap on @bus and @devfn, returning its offset -+ * if found or 0 otherwise. -+ */ -+static int fixed_bar_cap(struct pci_bus *bus, unsigned int devfn) -+{ -+ int pos; -+ u32 pcie_cap = 0, cap_data; -+ if (!raw_pci_ext_ops) return 0; -+ -+ pos = PCIE_CAP_OFFSET; -+ while (pos) { -+ if (raw_pci_ext_ops->read(pci_domain_nr(bus), bus->number, -+ devfn, pos, 4, &pcie_cap)) -+ return 0; -+ -+ if (pcie_cap == 0xffffffff) -+ return 0; -+ -+ if (PCI_EXT_CAP_ID(pcie_cap) == PCI_EXT_CAP_ID_VNDR) { -+ raw_pci_ext_ops->read(pci_domain_nr(bus), bus->number, -+ devfn, pos + 4, 4, &cap_data); -+ if ((cap_data & 0xffff) == PCIE_VNDR_CAP_ID_FIXED_BAR) -+ return pos; -+ } -+ -+ pos = pcie_cap >> 20; -+ } -+ -+ return 0; -+} -+ -+static int pci_device_update_fixed(struct pci_bus *bus, unsigned int devfn, -+ int reg, int len, u32 val, int offset) -+{ -+ u32 size; -+ unsigned int domain, busnum; -+ int bar = (reg - PCI_BASE_ADDRESS_0) >> 2; -+ -+ domain = pci_domain_nr(bus); -+ busnum = bus->number; -+ -+ if (val == ~0 && len == 4) { -+ unsigned long decode; -+ -+ raw_pci_ext_ops->read(domain, busnum, devfn, -+ offset + 8 + (bar * 4), 4, &size); -+ -+ /* Turn the size into a decode pattern for the sizing code */ -+ if (size) { -+ decode = size - 1; -+ decode |= decode >> 1; -+ decode |= decode >> 2; -+ decode |= decode >> 4; -+ decode |= decode >> 8; -+ decode |= decode >> 16; -+ decode++; -+ decode = ~(decode - 1); -+ } else { -+ decode = ~0; -+ } -+ -+ /* -+ * If val is all ones, the core code is trying to size the reg, -+ * so update the mmconfig space with the real size. -+ * -+ * Note: this assumes the fixed size we got is a power of two. -+ */ -+ return raw_pci_ext_ops->write(domain, busnum, devfn, reg, 4, -+ decode); -+ } -+ -+ /* This is some other kind of BAR write, so just do it. */ -+ return raw_pci_ext_ops->write(domain, busnum, devfn, reg, len, val); -+} -+ -+/** -+ * type1_access_ok - check whether to use type 1 -+ * @bus: bus number -+ * @devfn: device & function in question -+ * -+ * If the bus is on a Lincroft chip and it exists, or is not on a Lincroft at -+ * all, the we can go ahead with any reads & writes. If it's on a Lincroft, -+ * but doesn't exist, avoid the access altogether to keep the chip from -+ * hanging. -+ */ -+static bool type1_access_ok(unsigned int bus, unsigned int devfn, int reg) -+{ -+ /* This is a workaround for A0 LNC bug where PCI status register does -+ * not have new CAP bit set. can not be written by SW either. -+ * -+ * PCI header type in real LNC indicates a single function device, this -+ * will prevent probing other devices under the same function in PCI -+ * shim. Therefore, use the header type in shim instead. -+ */ -+ if (reg >= 0x100 || reg == PCI_STATUS || reg == PCI_HEADER_TYPE) -+ return 0; -+ if (bus == 0 && (devfn == PCI_DEVFN(2, 0) || devfn == PCI_DEVFN(0, 0))) -+ return 1; -+ return 0; /* langwell on others */ -+} -+ -+static int pci_read(struct pci_bus *bus, unsigned int devfn, int where, -+ int size, u32 *value) -+{ -+ if (type1_access_ok(bus->number, devfn, where)) -+ return pci_direct_conf1.read(pci_domain_nr(bus), bus->number, -+ devfn, where, size, value); -+ return raw_pci_ext_ops->read(pci_domain_nr(bus), bus->number, -+ devfn, where, size, value); -+} -+ -+static int pci_write(struct pci_bus *bus, unsigned int devfn, int where, -+ int size, u32 value) -+{ -+ int offset; -+ -+ /* On MRST, there is no PCI ROM BAR, this will cause a subsequent read -+ * to ROM BAR return 0 then being ignored. -+ */ -+ if (where == PCI_ROM_ADDRESS) -+ return 0; -+ -+ /* -+ * Devices with fixed BARs need special handling: -+ * - BAR sizing code will save, write ~0, read size, restore -+ * - so writes to fixed BARs need special handling -+ * - other writes to fixed BAR devices should go through mmconfig -+ */ -+ offset = fixed_bar_cap(bus, devfn); -+ if (offset && -+ (where >= PCI_BASE_ADDRESS_0 && where <= PCI_BASE_ADDRESS_5)) { -+ return pci_device_update_fixed(bus, devfn, where, size, value, -+ offset); -+ } -+ -+ /* -+ * On Moorestown update both real & mmconfig space -+ * Note: early Lincroft silicon can't handle type 1 accesses to -+ * non-existent devices, so just eat the write in that case. -+ */ -+ if (type1_access_ok(bus->number, devfn, where)) -+ return pci_direct_conf1.write(pci_domain_nr(bus), bus->number, -+ devfn, where, size, value); -+ return raw_pci_ext_ops->write(pci_domain_nr(bus), bus->number, devfn, -+ where, size, value); -+} -+ -+static int mrst_pci_irq_enable(struct pci_dev *dev) -+{ -+ u8 pin; -+ struct io_apic_irq_attr irq_attr; -+ -+ if (!dev->irq) -+ return 0; -+ -+ pci_read_config_byte(dev, PCI_INTERRUPT_PIN, &pin); -+ -+ /* MRST only have IOAPIC, the PCI irq lines are 1:1 mapped to -+ * IOAPIC RTE entries, so we just enable RTE for the device. -+ */ -+ irq_attr.ioapic = mp_find_ioapic(dev->irq); -+ irq_attr.ioapic_pin = dev->irq; -+ irq_attr.trigger = 1; /* level */ -+ irq_attr.polarity = 1; /* active low */ -+ io_apic_set_pci_routing(&dev->dev, dev->irq, &irq_attr); -+ -+ return 0; -+} -+ -+struct pci_ops pci_mrst_ops = { -+ .read = pci_read, -+ .write = pci_write, -+}; -+ -+/** -+ * pci_mrst_init - installs pci_mrst_ops -+ * -+ * Moorestown has an interesting PCI implementation (see above). -+ * Called when the early platform detection installs it. -+ */ -+int __init pci_mrst_init(void) -+{ -+ printk(KERN_INFO "Moorestown platform detected, using MRST PCI ops\n"); -+ pci_mmcfg_late_init(); -+ pcibios_enable_irq = mrst_pci_irq_enable; -+ pci_root_ops = pci_mrst_ops; -+ /* Continue with standard init */ -+ return 1; -+} -+ -+/* -+ * Langwell devices reside at fixed offsets, don't try to move them. -+ */ -+static void __devinit pci_fixed_bar_fixup(struct pci_dev *dev) -+{ -+ unsigned long offset; -+ u32 size; -+ int i; -+ -+ /* Fixup the BAR sizes for fixed BAR devices and make them unmoveable */ -+ offset = fixed_bar_cap(dev->bus, dev->devfn); -+ if (!offset || PCI_DEVFN(2, 0) == dev->devfn || -+ PCI_DEVFN(2, 2) == dev->devfn) -+ return; -+ -+ for (i = 0; i < PCI_ROM_RESOURCE; i++) { -+ pci_read_config_dword(dev, offset + 8 + (i * 4), &size); -+ dev->resource[i].end = dev->resource[i].start + size - 1; -+ dev->resource[i].flags |= IORESOURCE_PCI_FIXED; -+ } -+} -+DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_INTEL, PCI_ANY_ID, pci_fixed_bar_fixup); -+ -Index: linux-2.6.33/include/linux/pci_regs.h -=================================================================== ---- linux-2.6.33.orig/include/linux/pci_regs.h -+++ linux-2.6.33/include/linux/pci_regs.h -@@ -507,6 +507,7 @@ - #define PCI_EXT_CAP_ID_VC 2 - #define PCI_EXT_CAP_ID_DSN 3 - #define PCI_EXT_CAP_ID_PWR 4 -+#define PCI_EXT_CAP_ID_VNDR 11 - #define PCI_EXT_CAP_ID_ACS 13 - #define PCI_EXT_CAP_ID_ARI 14 - #define PCI_EXT_CAP_ID_ATS 15 -Index: linux-2.6.33/arch/x86/include/asm/fixmap.h -=================================================================== ---- linux-2.6.33.orig/arch/x86/include/asm/fixmap.h -+++ linux-2.6.33/arch/x86/include/asm/fixmap.h -@@ -114,6 +114,10 @@ enum fixed_addresses { - FIX_TEXT_POKE1, /* reserve 2 pages for text_poke() */ - FIX_TEXT_POKE0, /* first page is last, because allocation is backward */ - __end_of_permanent_fixed_addresses, -+ -+#ifdef CONFIG_X86_MRST -+ FIX_LNW_VRTC, -+#endif - /* - * 256 temporary boot-time mappings, used by early_ioremap(), - * before ioremap() is functional. -Index: linux-2.6.33/arch/x86/include/asm/vrtc.h -=================================================================== ---- /dev/null -+++ linux-2.6.33/arch/x86/include/asm/vrtc.h -@@ -0,0 +1,30 @@ -+#ifndef _MRST_VRTC_H -+#define _MRST_VRTC_H -+ -+#ifdef CONFIG_X86_MRST -+extern unsigned char vrtc_cmos_read(unsigned char reg); -+extern void vrtc_cmos_write(unsigned char val, unsigned char reg); -+ -+extern struct sfi_rtc_table_entry sfi_mrtc_array[]; -+extern int sfi_mrtc_num; -+ -+extern unsigned long vrtc_get_time(void); -+extern int vrtc_set_mmss(unsigned long nowtime); -+ -+#define MRST_VRTC_PGOFFSET (0xc00) -+ -+#else -+static inline unsigned char vrtc_cmos_read(unsigned char reg) -+{ -+ return 0xff; -+} -+ -+static inline void vrtc_cmos_write(unsigned char val, unsigned char reg) -+{ -+ return; -+} -+#endif -+ -+#define MRST_VRTC_MAP_SZ (1024) -+ -+#endif -Index: linux-2.6.33/arch/x86/kernel/vrtc.c -=================================================================== ---- /dev/null -+++ linux-2.6.33/arch/x86/kernel/vrtc.c -@@ -0,0 +1,116 @@ -+/* -+ * vrtc.c: Driver for virtual RTC device on Intel MID platform -+ * -+ * (C) Copyright 2009 Intel Corporation -+ * -+ * This program is free software; you can redistribute it and/or -+ * modify it under the terms of the GNU General Public License -+ * as published by the Free Software Foundation; version 2 -+ * of the License. -+ * -+ * Note: -+ * VRTC is emulated by system controller firmware, the real HW -+ * RTC is located in the PMIC device. SCU FW shadows PMIC RTC -+ * in a memory mapped IO space that is visible to the host IA -+ * processor. However, any updates to VRTC requires an IPI call -+ * to the SCU FW. -+ * -+ * This driver is based on RTC CMOS driver. -+ */ -+ -+#include <linux/kernel.h> -+#include <linux/module.h> -+#include <linux/sfi.h> -+ -+#include <asm/vrtc.h> -+#include <asm/time.h> -+#include <asm/fixmap.h> -+ -+static unsigned char *vrtc_va __read_mostly; -+ -+static void vrtc_init_mmap(void) -+{ -+ unsigned long rtc_paddr = sfi_mrtc_array[0].phys_addr; -+ -+ BUG_ON(!rtc_paddr); -+ -+ /* vRTC's register address may not be page aligned */ -+ set_fixmap_nocache(FIX_LNW_VRTC, rtc_paddr); -+ vrtc_va = (unsigned char __iomem *)__fix_to_virt(FIX_LNW_VRTC); -+ vrtc_va += rtc_paddr & ~PAGE_MASK; -+} -+ -+unsigned char vrtc_cmos_read(unsigned char reg) -+{ -+ unsigned char retval; -+ -+ /* vRTC's registers range from 0x0 to 0xD */ -+ if (reg > 0xd) -+ return 0xff; -+ -+ if (unlikely(!vrtc_va)) -+ vrtc_init_mmap(); -+ -+ lock_cmos_prefix(reg); -+ retval = *(vrtc_va + (reg << 2)); -+ lock_cmos_suffix(reg); -+ return retval; -+} -+EXPORT_SYMBOL(vrtc_cmos_read); -+ -+void vrtc_cmos_write(unsigned char val, unsigned char reg) -+{ -+ if (reg > 0xd) -+ return; -+ -+ if (unlikely(!vrtc_va)) -+ vrtc_init_mmap(); -+ -+ lock_cmos_prefix(reg); -+ *(vrtc_va + (reg << 2)) = val; -+ lock_cmos_suffix(reg); -+} -+EXPORT_SYMBOL(vrtc_cmos_write); -+ -+unsigned long vrtc_get_time(void) -+{ -+ u8 sec, min, hour, mday, mon; -+ u32 year; -+ -+ while ((vrtc_cmos_read(RTC_FREQ_SELECT) & RTC_UIP)) -+ cpu_relax(); -+ -+ sec = vrtc_cmos_read(RTC_SECONDS); -+ min = vrtc_cmos_read(RTC_MINUTES); -+ hour = vrtc_cmos_read(RTC_HOURS); -+ mday = vrtc_cmos_read(RTC_DAY_OF_MONTH); -+ mon = vrtc_cmos_read(RTC_MONTH); -+ year = vrtc_cmos_read(RTC_YEAR); -+ -+ /* vRTC YEAR reg contains the offset to 1970 */ -+ year += 1970; -+ -+ printk(KERN_INFO "vRTC: sec: %d min: %d hour: %d day: %d " -+ "mon: %d year: %d\n", sec, min, hour, mday, mon, year); -+ -+ return mktime(year, mon, mday, hour, min, sec); -+} -+ -+/* Only care about the minutes and seconds */ -+int vrtc_set_mmss(unsigned long nowtime) -+{ -+ int real_sec, real_min; -+ int vrtc_min; -+ -+ vrtc_min = vrtc_cmos_read(RTC_MINUTES); -+ -+ real_sec = nowtime % 60; -+ real_min = nowtime / 60; -+ if (((abs(real_min - vrtc_min) + 15)/30) & 1) -+ real_min += 30; -+ real_min %= 60; -+ -+ vrtc_cmos_write(real_sec, RTC_SECONDS); -+ vrtc_cmos_write(real_min, RTC_MINUTES); -+ return 0; -+} -Index: linux-2.6.33/drivers/rtc/Kconfig -=================================================================== ---- linux-2.6.33.orig/drivers/rtc/Kconfig -+++ linux-2.6.33/drivers/rtc/Kconfig -@@ -423,6 +423,19 @@ config RTC_DRV_CMOS - This driver can also be built as a module. If so, the module - will be called rtc-cmos. - -+config RTC_DRV_VRTC -+ tristate "Virtual RTC for MRST" -+ depends on X86_MRST -+ default y if X86_MRST -+ -+ help -+ Say "yes" here to get direct support for the real time clock -+ found in Moorestown platform. The VRTC is a emulated RTC that -+ Derive its clock source from a realy RTC in PMIC. MC146818 -+ stype programming interface is most conserved other than any -+ updates is done via IPC calls to the system controller FW. -+ -+ - config RTC_DRV_DS1216 - tristate "Dallas DS1216" - depends on SNI_RM -Index: linux-2.6.33/drivers/rtc/Makefile -=================================================================== ---- linux-2.6.33.orig/drivers/rtc/Makefile -+++ linux-2.6.33/drivers/rtc/Makefile -@@ -28,6 +28,7 @@ obj-$(CONFIG_RTC_DRV_BQ4802) += rtc-bq48 - obj-$(CONFIG_RTC_DRV_CMOS) += rtc-cmos.o - obj-$(CONFIG_RTC_DRV_COH901331) += rtc-coh901331.o - obj-$(CONFIG_RTC_DRV_DM355EVM) += rtc-dm355evm.o -+obj-$(CONFIG_RTC_DRV_VRTC) += rtc-mrst.o - obj-$(CONFIG_RTC_DRV_DS1216) += rtc-ds1216.o - obj-$(CONFIG_RTC_DRV_DS1286) += rtc-ds1286.o - obj-$(CONFIG_RTC_DRV_DS1302) += rtc-ds1302.o -Index: linux-2.6.33/drivers/rtc/rtc-mrst.c -=================================================================== ---- /dev/null -+++ linux-2.6.33/drivers/rtc/rtc-mrst.c -@@ -0,0 +1,660 @@ -+/* -+ * rtc-mrst.c: Driver for Moorestown virtual RTC -+ * -+ * (C) Copyright 2009 Intel Corporation -+ * Author: Jacob Pan (jacob.jun.pan@intel.com) -+ * Feng Tang (feng.tang@intel.com) -+ * -+ * This program is free software; you can redistribute it and/or -+ * modify it under the terms of the GNU General Public License -+ * as published by the Free Software Foundation; version 2 -+ * of the License. -+ * -+ * Note: -+ * VRTC is emulated by system controller firmware, the real HW -+ * RTC is located in the PMIC device. SCU FW shadows PMIC RTC -+ * in a memory mapped IO space that is visible to the host IA -+ * processor. However, any updates to VRTC requires an IPI call -+ * to the SCU FW. -+ * -+ * This driver is based on RTC CMOS driver. -+ */ -+ -+/* -+ * Note: -+ * * MRST vRTC only support binary mode and 24H mode -+ * * MRST vRTC only support PIE and AIE, no UIE -+ * * its alarm function is also limited to hr/min/sec. -+ * * so far it doesn't support wake event func -+ */ -+ -+#include <linux/mod_devicetable.h> -+#include <linux/platform_device.h> -+#include <linux/interrupt.h> -+#include <linux/spinlock.h> -+#include <linux/kernel.h> -+#include <linux/module.h> -+#include <linux/init.h> -+#include <linux/sfi.h> -+ -+#include <asm-generic/rtc.h> -+ -+#include <asm/ipc_defs.h> -+#include <asm/vrtc.h> -+ -+struct mrst_rtc { -+ struct rtc_device *rtc; -+ struct device *dev; -+ int irq; -+ struct resource *iomem; -+ -+ void (*wake_on)(struct device *); -+ void (*wake_off)(struct device *); -+ -+ u8 enabled_wake; -+ u8 suspend_ctrl; -+ -+ /* Newer hardware extends the original register set */ -+ u8 day_alrm; -+ u8 mon_alrm; -+ u8 century; -+}; -+ -+/* both platform and pnp busses use negative numbers for invalid irqs */ -+#define is_valid_irq(n) ((n) >= 0) -+ -+static const char driver_name[] = "rtc_mrst"; -+ -+#define RTC_IRQMASK (RTC_PF | RTC_AF) -+ -+static inline int is_intr(u8 rtc_intr) -+{ -+ if (!(rtc_intr & RTC_IRQF)) -+ return 0; -+ return rtc_intr & RTC_IRQMASK; -+} -+ -+/* -+ * rtc_time's year contains the increment over 1900, but vRTC's YEAR -+ * register can't be programmed to value larger than 0x64, so vRTC -+ * driver chose to use 1970 (UNIX time start point) as the base, and -+ * do the translation in read/write time -+ */ -+static int mrst_read_time(struct device *dev, struct rtc_time *time) -+{ -+ unsigned long flags; -+ -+ if (rtc_is_updating()) -+ mdelay(20); -+ -+ spin_lock_irqsave(&rtc_lock, flags); -+ time->tm_sec = vrtc_cmos_read(RTC_SECONDS); -+ time->tm_min = vrtc_cmos_read(RTC_MINUTES); -+ time->tm_hour = vrtc_cmos_read(RTC_HOURS); -+ time->tm_mday = vrtc_cmos_read(RTC_DAY_OF_MONTH); -+ time->tm_mon = vrtc_cmos_read(RTC_MONTH); -+ time->tm_year = vrtc_cmos_read(RTC_YEAR); -+ spin_unlock_irqrestore(&rtc_lock, flags); -+ -+ /* Adjust for the 1970/1900 */ -+ time->tm_year += 70; -+ time->tm_mon--; -+ return RTC_24H; -+} -+ -+static int mrst_set_time(struct device *dev, struct rtc_time *time) -+{ -+ int ret; -+ unsigned long flags; -+ unsigned char mon, day, hrs, min, sec; -+ unsigned int yrs; -+ -+ yrs = time->tm_year; -+ mon = time->tm_mon + 1; /* tm_mon starts at zero */ -+ day = time->tm_mday; -+ hrs = time->tm_hour; -+ min = time->tm_min; -+ sec = time->tm_sec; -+ -+ if (yrs < 70 || yrs > 138) -+ return -EINVAL; -+ yrs -= 70; -+ -+ spin_lock_irqsave(&rtc_lock, flags); -+ -+ /* Need think about leap year */ -+ vrtc_cmos_write(yrs, RTC_YEAR); -+ vrtc_cmos_write(mon, RTC_MONTH); -+ vrtc_cmos_write(day, RTC_DAY_OF_MONTH); -+ vrtc_cmos_write(hrs, RTC_HOURS); -+ vrtc_cmos_write(min, RTC_MINUTES); -+ vrtc_cmos_write(sec, RTC_SECONDS); -+ -+ ret = lnw_ipc_single_cmd(IPC_VRTC_CMD, IPC_VRTC_SET_TIME, 0, 0); -+ spin_unlock_irqrestore(&rtc_lock, flags); -+ return ret; -+} -+ -+static int mrst_read_alarm(struct device *dev, struct rtc_wkalrm *t) -+{ -+ struct mrst_rtc *mrst = dev_get_drvdata(dev); -+ unsigned char rtc_control; -+ -+ if (!is_valid_irq(mrst->irq)) -+ return -EIO; -+ -+ /* Basic alarms only support hour, minute, and seconds fields. -+ * Some also support day and month, for alarms up to a year in -+ * the future. -+ */ -+ t->time.tm_mday = -1; -+ t->time.tm_mon = -1; -+ t->time.tm_year = -1; -+ -+ /* vRTC only supports binary mode */ -+ spin_lock_irq(&rtc_lock); -+ t->time.tm_sec = vrtc_cmos_read(RTC_SECONDS_ALARM); -+ t->time.tm_min = vrtc_cmos_read(RTC_MINUTES_ALARM); -+ t->time.tm_hour = vrtc_cmos_read(RTC_HOURS_ALARM); -+ -+ rtc_control = vrtc_cmos_read(RTC_CONTROL); -+ spin_unlock_irq(&rtc_lock); -+ -+ t->enabled = !!(rtc_control & RTC_AIE); -+ t->pending = 0; -+ -+ return 0; -+} -+ -+static void mrst_checkintr(struct mrst_rtc *mrst, unsigned char rtc_control) -+{ -+ unsigned char rtc_intr; -+ -+ /* -+ * NOTE after changing RTC_xIE bits we always read INTR_FLAGS; -+ * allegedly some older rtcs need that to handle irqs properly -+ */ -+ rtc_intr = vrtc_cmos_read(RTC_INTR_FLAGS); -+ rtc_intr &= (rtc_control & RTC_IRQMASK) | RTC_IRQF; -+ if (is_intr(rtc_intr)) -+ rtc_update_irq(mrst->rtc, 1, rtc_intr); -+} -+ -+static void mrst_irq_enable(struct mrst_rtc *mrst, unsigned char mask) -+{ -+ unsigned char rtc_control; -+ -+ /* -+ * Flush any pending IRQ status, notably for update irqs, -+ * before we enable new IRQs -+ */ -+ rtc_control = vrtc_cmos_read(RTC_CONTROL); -+ mrst_checkintr(mrst, rtc_control); -+ -+ rtc_control |= mask; -+ vrtc_cmos_write(rtc_control, RTC_CONTROL); -+ -+ mrst_checkintr(mrst, rtc_control); -+} -+ -+static void mrst_irq_disable(struct mrst_rtc *mrst, unsigned char mask) -+{ -+ unsigned char rtc_control; -+ -+ rtc_control = vrtc_cmos_read(RTC_CONTROL); -+ rtc_control &= ~mask; -+ vrtc_cmos_write(rtc_control, RTC_CONTROL); -+ mrst_checkintr(mrst, rtc_control); -+} -+ -+static int mrst_set_alarm(struct device *dev, struct rtc_wkalrm *t) -+{ -+ struct mrst_rtc *mrst = dev_get_drvdata(dev); -+ unsigned char hrs, min, sec; -+ int ret = 0; -+ -+ if (!is_valid_irq(mrst->irq)) -+ return -EIO; -+ -+ hrs = t->time.tm_hour; -+ min = t->time.tm_min; -+ sec = t->time.tm_sec; -+ -+ spin_lock_irq(&rtc_lock); -+ /* Next rtc irq must not be from previous alarm setting */ -+ mrst_irq_disable(mrst, RTC_AIE); -+ -+ /* Update alarm */ -+ vrtc_cmos_write(hrs, RTC_HOURS_ALARM); -+ vrtc_cmos_write(min, RTC_MINUTES_ALARM); -+ vrtc_cmos_write(sec, RTC_SECONDS_ALARM); -+ -+ ret = lnw_ipc_single_cmd(IPC_VRTC_CMD, IPC_VRTC_SET_ALARM, 0, 0); -+ spin_unlock_irq(&rtc_lock); -+ -+ if (ret) -+ return ret; -+ -+ spin_lock_irq(&rtc_lock); -+ if (t->enabled) -+ mrst_irq_enable(mrst, RTC_AIE); -+ -+ spin_unlock_irq(&rtc_lock); -+ -+ return 0; -+} -+ -+ -+static int mrst_irq_set_state(struct device *dev, int enabled) -+{ -+ struct mrst_rtc *mrst = dev_get_drvdata(dev); -+ unsigned long flags; -+ -+ if (!is_valid_irq(mrst->irq)) -+ return -ENXIO; -+ -+ spin_lock_irqsave(&rtc_lock, flags); -+ -+ if (enabled) -+ mrst_irq_enable(mrst, RTC_PIE); -+ else -+ mrst_irq_disable(mrst, RTC_PIE); -+ -+ spin_unlock_irqrestore(&rtc_lock, flags); -+ return 0; -+} -+ -+#if defined(CONFIG_RTC_INTF_DEV) || defined(CONFIG_RTC_INTF_DEV_MODULE) -+ -+/* Currently, the vRTC doesn't support UIE ON/OFF */ -+static int -+mrst_rtc_ioctl(struct device *dev, unsigned int cmd, unsigned long arg) -+{ -+ struct mrst_rtc *mrst = dev_get_drvdata(dev); -+ unsigned long flags; -+ -+ switch (cmd) { -+ case RTC_AIE_OFF: -+ case RTC_AIE_ON: -+ if (!is_valid_irq(mrst->irq)) -+ return -EINVAL; -+ break; -+ default: -+ /* PIE ON/OFF is handled by mrst_irq_set_state() */ -+ return -ENOIOCTLCMD; -+ } -+ -+ spin_lock_irqsave(&rtc_lock, flags); -+ switch (cmd) { -+ case RTC_AIE_OFF: /* alarm off */ -+ mrst_irq_disable(mrst, RTC_AIE); -+ break; -+ case RTC_AIE_ON: /* alarm on */ -+ mrst_irq_enable(mrst, RTC_AIE); -+ break; -+ } -+ spin_unlock_irqrestore(&rtc_lock, flags); -+ return 0; -+} -+ -+#else -+#define mrst_rtc_ioctl NULL -+#endif -+ -+#if defined(CONFIG_RTC_INTF_PROC) || defined(CONFIG_RTC_INTF_PROC_MODULE) -+ -+static int mrst_procfs(struct device *dev, struct seq_file *seq) -+{ -+ unsigned char rtc_control, valid; -+ -+ spin_lock_irq(&rtc_lock); -+ rtc_control = vrtc_cmos_read(RTC_CONTROL); -+ valid = vrtc_cmos_read(RTC_VALID); -+ spin_unlock_irq(&rtc_lock); -+ -+ return seq_printf(seq, -+ "periodic_IRQ\t: %s\n" -+ "square_wave\t: %s\n" -+ "BCD\t\t: %s\n" -+ "DST_enable\t: %s\n" -+ "periodic_freq\t: daily\n", -+ (rtc_control & RTC_PIE) ? "yes" : "no", -+ (rtc_control & RTC_SQWE) ? "yes" : "no", -+ (rtc_control & RTC_DM_BINARY) ? "no" : "yes", -+ (rtc_control & RTC_DST_EN) ? "yes" : "no"); -+} -+ -+#else -+#define mrst_procfs NULL -+#endif -+ -+static const struct rtc_class_ops mrst_rtc_ops = { -+ .ioctl = mrst_rtc_ioctl, -+ .read_time = mrst_read_time, -+ .set_time = mrst_set_time, -+ .read_alarm = mrst_read_alarm, -+ .set_alarm = mrst_set_alarm, -+ .proc = mrst_procfs, -+ .irq_set_freq = NULL, -+ .irq_set_state = mrst_irq_set_state, -+}; -+ -+static struct mrst_rtc mrst_rtc; -+ -+/* -+ * When vRTC IRQ is captured by SCU FW, FW will clear the AIE bit in -+ * Reg B, so no need for this driver to clear it -+ */ -+static irqreturn_t mrst_interrupt(int irq, void *p) -+{ -+ u8 irqstat; -+ -+ spin_lock(&rtc_lock); -+ /* This read will clear all IRQ flags inside Reg C */ -+ irqstat = vrtc_cmos_read(RTC_INTR_FLAGS); -+ spin_unlock(&rtc_lock); -+ -+ irqstat &= RTC_IRQMASK | RTC_IRQF; -+ if (is_intr(irqstat)) { -+ rtc_update_irq(p, 1, irqstat); -+ return IRQ_HANDLED; -+ } else { -+ printk(KERN_ERR "vRTC: error in IRQ handler\n"); -+ return IRQ_NONE; -+ } -+} -+ -+static int __init -+vrtc_mrst_do_probe(struct device *dev, struct resource *iomem, int rtc_irq) -+{ -+ int retval = 0; -+ unsigned char rtc_control; -+ -+ /* There can be only one ... */ -+ if (mrst_rtc.dev) -+ return -EBUSY; -+ -+ if (!iomem) -+ return -ENODEV; -+ -+ iomem = request_mem_region(iomem->start, -+ iomem->end + 1 - iomem->start, -+ driver_name); -+ if (!iomem) { -+ dev_dbg(dev, "i/o mem already in use.\n"); -+ return -EBUSY; -+ } -+ -+ mrst_rtc.irq = rtc_irq; -+ mrst_rtc.iomem = iomem; -+ -+ mrst_rtc.day_alrm = 0; -+ mrst_rtc.mon_alrm = 0; -+ mrst_rtc.century = 0; -+ mrst_rtc.wake_on = NULL; -+ mrst_rtc.wake_off = NULL; -+ -+ mrst_rtc.rtc = rtc_device_register(driver_name, dev, -+ &mrst_rtc_ops, THIS_MODULE); -+ if (IS_ERR(mrst_rtc.rtc)) { -+ retval = PTR_ERR(mrst_rtc.rtc); -+ goto cleanup0; -+ } -+ -+ mrst_rtc.dev = dev; -+ dev_set_drvdata(dev, &mrst_rtc); -+ rename_region(iomem, dev_name(&mrst_rtc.rtc->dev)); -+ -+ spin_lock_irq(&rtc_lock); -+ mrst_irq_disable(&mrst_rtc, RTC_PIE | RTC_AIE); -+ rtc_control = vrtc_cmos_read(RTC_CONTROL); -+ spin_unlock_irq(&rtc_lock); -+ -+ if (!(rtc_control & RTC_24H) || (rtc_control & (RTC_DM_BINARY))) -+ dev_dbg(dev, "TODO: support more than 24-hr BCD mode \n"); -+ -+ if (is_valid_irq(rtc_irq)) { -+ irq_handler_t rtc_mrst_int_handler; -+ rtc_mrst_int_handler = mrst_interrupt; -+ -+ retval = request_irq(rtc_irq, rtc_mrst_int_handler, -+ IRQF_DISABLED, dev_name(&mrst_rtc.rtc->dev), -+ mrst_rtc.rtc); -+ if (retval < 0) { -+ dev_dbg(dev, "IRQ %d is already in use, err %d\n", -+ rtc_irq, retval); -+ goto cleanup1; -+ } -+ } -+ -+ pr_info("vRTC driver for Moorewtown is initialized\n"); -+ return 0; -+ -+cleanup1: -+ mrst_rtc.dev = NULL; -+ rtc_device_unregister(mrst_rtc.rtc); -+cleanup0: -+ release_region(iomem->start, iomem->end + 1 - iomem->start); -+ pr_warning("vRTC driver for Moorewtown initialization Failed!!\n"); -+ return retval; -+} -+ -+static void rtc_mrst_do_shutdown(void) -+{ -+ spin_lock_irq(&rtc_lock); -+ mrst_irq_disable(&mrst_rtc, RTC_IRQMASK); -+ spin_unlock_irq(&rtc_lock); -+} -+ -+static void __exit rtc_mrst_do_remove(struct device *dev) -+{ -+ struct mrst_rtc *mrst = dev_get_drvdata(dev); -+ struct resource *iomem; -+ -+ rtc_mrst_do_shutdown(); -+ -+ if (is_valid_irq(mrst->irq)) -+ free_irq(mrst->irq, mrst->rtc); -+ -+ rtc_device_unregister(mrst->rtc); -+ mrst->rtc = NULL; -+ -+ iomem = mrst->iomem; -+ release_region(iomem->start, iomem->end + 1 - iomem->start); -+ mrst->iomem = NULL; -+ -+ mrst->dev = NULL; -+ dev_set_drvdata(dev, NULL); -+} -+ -+#ifdef CONFIG_PM -+ -+static int mrst_suspend(struct device *dev, pm_message_t mesg) -+{ -+ struct mrst_rtc *mrst = dev_get_drvdata(dev); -+ unsigned char tmp; -+ -+ /* Only the alarm might be a wakeup event source */ -+ spin_lock_irq(&rtc_lock); -+ mrst->suspend_ctrl = tmp = vrtc_cmos_read(RTC_CONTROL); -+ if (tmp & (RTC_PIE | RTC_AIE)) { -+ unsigned char mask; -+ -+ if (device_may_wakeup(dev)) -+ mask = RTC_IRQMASK & ~RTC_AIE; -+ else -+ mask = RTC_IRQMASK; -+ tmp &= ~mask; -+ vrtc_cmos_write(tmp, RTC_CONTROL); -+ -+ mrst_checkintr(mrst, tmp); -+ } -+ spin_unlock_irq(&rtc_lock); -+ -+ if (tmp & RTC_AIE) { -+ mrst->enabled_wake = 1; -+ if (mrst->wake_on) -+ mrst->wake_on(dev); -+ else -+ enable_irq_wake(mrst->irq); -+ } -+ -+ pr_debug("%s: suspend%s, ctrl %02x\n", -+ dev_name(&mrst_rtc.rtc->dev), -+ (tmp & RTC_AIE) ? ", alarm may wake" : "", -+ tmp); -+ -+ return 0; -+} -+ -+/* -+ * We want RTC alarms to wake us from e.g. ACPI G2/S5 "soft off", even -+ * after a detour through G3 "mechanical off", although the ACPI spec -+ * says wakeup should only work from G1/S4 "hibernate". To most users, -+ * distinctions between S4 and S5 are pointless. So when the hardware -+ * allows, don't draw that distinction. -+ */ -+static inline int mrst_poweroff(struct device *dev) -+{ -+ return mrst_suspend(dev, PMSG_HIBERNATE); -+} -+ -+static int mrst_resume(struct device *dev) -+{ -+ struct mrst_rtc *mrst = dev_get_drvdata(dev); -+ unsigned char tmp = mrst->suspend_ctrl; -+ -+ /* Re-enable any irqs previously active */ -+ if (tmp & RTC_IRQMASK) { -+ unsigned char mask; -+ -+ if (mrst->enabled_wake) { -+ if (mrst->wake_off) -+ mrst->wake_off(dev); -+ else -+ disable_irq_wake(mrst->irq); -+ mrst->enabled_wake = 0; -+ } -+ -+ spin_lock_irq(&rtc_lock); -+ do { -+ vrtc_cmos_write(tmp, RTC_CONTROL); -+ -+ mask = vrtc_cmos_read(RTC_INTR_FLAGS); -+ mask &= (tmp & RTC_IRQMASK) | RTC_IRQF; -+ if (!is_intr(mask)) -+ break; -+ -+ rtc_update_irq(mrst->rtc, 1, mask); -+ tmp &= ~RTC_AIE; -+ } while (mask & RTC_AIE); -+ spin_unlock_irq(&rtc_lock); -+ } -+ -+ pr_debug("%s: resume, ctrl %02x\n", -+ dev_name(&mrst_rtc.rtc->dev), -+ tmp); -+ -+ return 0; -+} -+ -+#else -+#define mrst_suspend NULL -+#define mrst_resume NULL -+ -+static inline int mrst_poweroff(struct device *dev) -+{ -+ return -ENOSYS; -+} -+ -+#endif -+ -+ -+/*----------------------------------------------------------------*/ -+ -+/* Platform setup should have set up an RTC device, when PNP is -+ * unavailable ... this could happen even on (older) PCs. -+ */ -+ -+static int __init vrtc_mrst_platform_probe(struct platform_device *pdev) -+{ -+ return vrtc_mrst_do_probe(&pdev->dev, -+ platform_get_resource(pdev, IORESOURCE_MEM, 0), -+ platform_get_irq(pdev, 0)); -+} -+ -+static int __exit vrtc_mrst_platform_remove(struct platform_device *pdev) -+{ -+ rtc_mrst_do_remove(&pdev->dev); -+ return 0; -+} -+ -+static void vrtc_mrst_platform_shutdown(struct platform_device *pdev) -+{ -+ if (system_state == SYSTEM_POWER_OFF && !mrst_poweroff(&pdev->dev)) -+ return; -+ -+ rtc_mrst_do_shutdown(); -+} -+ -+/* Work with hotplug and coldplug */ -+MODULE_ALIAS("platform:vrtc_mrst"); -+ -+static struct platform_driver vrtc_mrst_platform_driver = { -+ .remove = __exit_p(vrtc_mrst_platform_remove), -+ .shutdown = vrtc_mrst_platform_shutdown, -+ .driver = { -+ .name = (char *) driver_name, -+ .suspend = mrst_suspend, -+ .resume = mrst_resume, -+ } -+}; -+ -+/* -+ * Moorestown platform has memory mapped virtual RTC device that emulates -+ * the programming interface of the RTC. -+ */ -+ -+static struct resource vrtc_resources[] = { -+ [0] = { -+ .flags = IORESOURCE_MEM, -+ }, -+ [1] = { -+ .flags = IORESOURCE_IRQ, -+ } -+}; -+ -+static struct platform_device vrtc_device = { -+ .name = "rtc_mrst", -+ .id = -1, -+ .resource = vrtc_resources, -+ .num_resources = ARRAY_SIZE(vrtc_resources), -+}; -+ -+static int __init vrtc_mrst_init(void) -+{ -+ /* iomem resource */ -+ vrtc_resources[0].start = sfi_mrtc_array[0].phys_addr; -+ vrtc_resources[0].end = sfi_mrtc_array[0].phys_addr + -+ MRST_VRTC_MAP_SZ; -+ /* irq resource */ -+ vrtc_resources[1].start = sfi_mrtc_array[0].irq; -+ vrtc_resources[1].end = sfi_mrtc_array[0].irq; -+ -+ platform_device_register(&vrtc_device); -+ return platform_driver_probe(&vrtc_mrst_platform_driver, -+ vrtc_mrst_platform_probe); -+} -+ -+static void __exit vrtc_mrst_exit(void) -+{ -+ platform_driver_unregister(&vrtc_mrst_platform_driver); -+ platform_device_unregister(&vrtc_device); -+} -+ -+module_init(vrtc_mrst_init); -+module_exit(vrtc_mrst_exit); -+ -+MODULE_AUTHOR("Jacob Pan; Feng Tang"); -+MODULE_DESCRIPTION("Driver for Moorestown virtual RTC"); -+MODULE_LICENSE("GPL"); -Index: linux-2.6.33/drivers/spi/Kconfig -=================================================================== ---- linux-2.6.33.orig/drivers/spi/Kconfig -+++ linux-2.6.33/drivers/spi/Kconfig -@@ -302,6 +302,18 @@ config SPI_NUC900 - select SPI_BITBANG - help - SPI driver for Nuvoton NUC900 series ARM SoCs -+config SPI_MRST -+ tristate "SPI controller driver for Intel Moorestown platform " -+ depends on SPI_MASTER && PCI && X86_MRST -+ help -+ This is the SPI controller master driver for Intel Moorestown platform -+ -+config SPI_MRST_DMA -+ boolean "Enable DMA for MRST SPI0 controller" -+ default y -+ depends on SPI_MRST && INTEL_LNW_DMAC2 -+ help -+ This has to be enabled after Moorestown DMAC2 driver is enabled - - # - # Add new SPI master controllers in alphabetical order above this line -Index: linux-2.6.33/drivers/spi/Makefile -=================================================================== ---- linux-2.6.33.orig/drivers/spi/Makefile -+++ linux-2.6.33/drivers/spi/Makefile -@@ -42,6 +42,7 @@ obj-$(CONFIG_SPI_SH_SCI) += spi_sh_sci. - obj-$(CONFIG_SPI_SH_MSIOF) += spi_sh_msiof.o - obj-$(CONFIG_SPI_STMP3XXX) += spi_stmp.o - obj-$(CONFIG_SPI_NUC900) += spi_nuc900.o -+obj-$(CONFIG_SPI_MRST) += mrst_spi.o - - # special build for s3c24xx spi driver with fiq support - spi_s3c24xx_hw-y := spi_s3c24xx.o -Index: linux-2.6.33/drivers/spi/mrst_spi.c -=================================================================== ---- /dev/null -+++ linux-2.6.33/drivers/spi/mrst_spi.c -@@ -0,0 +1,1382 @@ -+/* -+ * mrst_spi.c - Moorestown SPI controller driver (referring pxa2xx_spi.c) -+ * -+ * Copyright (C) Intel 2008 Feng Tang <feng.tang@intel.com> -+ * -+ * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -+ * -+ * This program is free software; you can redistribute it and/or modify -+ * it under the terms of the GNU General Public License as published by -+ * the Free Software Foundation; either version 2 of the License, or -+ * (at your option) any later version. -+ * -+ * This program is distributed in the hope that it will be useful, -+ * but WITHOUT ANY WARRANTY; without even the implied warranty of -+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -+ * GNU General Public License for more details. -+ * -+ * You should have received a copy of the GNU General Public License -+ * along with this program; if not, write to the Free Software -+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA -+ * -+ * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -+ * -+ */ -+ -+/* Note: -+ * -+ * * FW will create a SPI device info block table, and driver need parse -+ * them out and use register_board_info to register them to kernel -+ */ -+ -+#include <linux/delay.h> -+#include <linux/highmem.h> -+#include <linux/pci.h> -+#include <linux/dma-mapping.h> -+#include <linux/interrupt.h> -+ -+#include <linux/spi/spi.h> -+#include <linux/spi/mrst_spi.h> -+ -+#define MRST_MAX_DMA_LEN 2047 -+#ifdef CONFIG_SPI_MRST_DMA -+#include <linux/lnw_dma.h> -+#endif -+ -+#ifdef CONFIG_DEBUG_FS -+#include <linux/debugfs.h> -+#endif -+ -+#define DRIVER_NAME "mrst_spi" -+ -+#define START_STATE ((void *)0) -+#define RUNNING_STATE ((void *)1) -+#define DONE_STATE ((void *)2) -+#define ERROR_STATE ((void *)-1) -+ -+#define QUEUE_RUNNING 0 -+#define QUEUE_STOPPED 1 -+ -+#define MRST_SPI_DEASSERT 0 -+#define MRST_SPI_ASSERT 1 -+ -+/* HW info for MRST CLk Control Unit, one 32b reg */ -+#define MRST_SPI_CLK_BASE 100000000 /* 100m */ -+#define MRST_CLK_SPI0_REG 0xff11d86c -+#define CLK_SPI_BDIV_OFFSET 0 -+#define CLK_SPI_BDIV_MASK 0x00000007 -+#define CLK_SPI_CDIV_OFFSET 9 -+#define CLK_SPI_CDIV_MASK 0x00000e00 -+#define CLK_SPI_CDIV_100M 0x0 -+#define CLK_SPI_CDIV_50M 0x1 -+#define CLK_SPI_CDIV_33M 0x2 -+#define CLK_SPI_CDIV_25M 0x3 -+#define CLK_SPI_DISABLE_OFFSET 8 -+ -+/* per controller struct */ -+struct driver_data { -+ /* Driver model hookup */ -+ struct pci_dev *pdev; -+ struct spi_master *master; -+ -+ struct spi_device *devices; -+ struct spi_device *cur_dev; -+ enum mrst_ssi_type type; -+ -+ /* phy and virtual register addresses */ -+ void *paddr; -+ void *vaddr; -+ u32 iolen; -+ int irq; -+ dma_addr_t dma_addr; -+ u32 freq; /* controller core clk freqency in Hz */ -+ -+ /* Driver message queue */ -+ struct workqueue_struct *workqueue; -+ struct work_struct pump_messages; -+ spinlock_t lock; -+ struct list_head queue; -+ int busy; -+ int run; -+ -+ /* Message Transfer pump */ -+ struct tasklet_struct pump_transfers; -+ -+ /* Current message transfer state info */ -+ struct spi_message *cur_msg; -+ struct spi_transfer *cur_transfer; -+ struct chip_data *cur_chip; -+ struct chip_data *prev_chip; -+ size_t len; -+ void *tx; -+ void *tx_end; -+ void *rx; -+ void *rx_end; -+ int dma_mapped; -+ dma_addr_t rx_dma; -+ dma_addr_t tx_dma; -+ size_t rx_map_len; -+ size_t tx_map_len; -+ u8 n_bytes; /* current is a 1/2 bytes op */ -+ u8 max_bits_per_word; /* SPI0's maxim width is 16 bits */ -+ u32 dma_width; -+ int cs_change; -+ int (*write)(struct driver_data *drv_data); -+ int (*read)(struct driver_data *drv_data); -+ irqreturn_t (*transfer_handler)(struct driver_data *drv_data); -+ void (*cs_control)(u32 command); -+ -+#ifdef CONFIG_DEBUG_FS -+ struct dentry *debugfs; -+#endif -+ -+ int dma_inited; -+ -+#ifdef CONFIG_SPI_MRST_DMA -+ struct lnw_dma_slave dmas_tx; -+ struct lnw_dma_slave dmas_rx; -+ struct dma_chan *txchan; -+ struct dma_chan *rxchan; -+ int txdma_done; -+ int rxdma_done; -+ -+ u64 tx_param; -+ u64 rx_param; -+ struct pci_dev *dma_dev; -+#endif -+}; -+ -+/* slave spi_dev related */ -+struct chip_data { -+ /* cr0 and cr1 are only 16b valid */ -+ u16 cr0; -+ u16 cr1; -+ -+ u8 cs; /* chip select pin */ -+ u8 n_bytes; /* current is a 1/2/4 byte op */ -+ u8 tmode; /* TR/TO/RO/EEPROM */ -+ u8 type; /* SPI/SSP/MicroWire */ -+ -+ u8 poll_mode; /* 1 means use poll mode */ -+ -+ u32 dma_width; -+ u32 rx_threshold; -+ u32 tx_threshold; -+ u8 enable_dma; -+ u8 bits_per_word; -+ u16 clk_div; /* baud rate divider */ -+ u32 speed_hz; /* baud rate */ -+ int (*write)(struct driver_data *drv_data); -+ int (*read)(struct driver_data *drv_data); -+ void (*cs_control)(u32 command); -+}; -+ -+#ifdef CONFIG_SPI_MRST_DMA -+static bool chan_filter(struct dma_chan *chan, void *param) -+{ -+ struct driver_data *drv_data = param; -+ bool ret = false; -+ -+ if (chan->device->dev == &drv_data->dma_dev->dev) -+ ret = true; -+ return ret; -+} -+ -+static void mrst_spi_dma_init(struct driver_data *drv_data) -+{ -+ struct lnw_dma_slave *rxs, *txs; -+ dma_cap_mask_t mask; -+ struct pci_dev *dmac2; -+ -+ drv_data->txchan = NULL; -+ drv_data->rxchan = NULL; -+ -+ /* mrst spi0 controller only work with mrst dma contrller 2 */ -+ dmac2 = pci_get_device(PCI_VENDOR_ID_INTEL, 0x0813, NULL); -+ if (!dmac2) { -+ printk(KERN_WARNING -+ "MRST SPI0: can't find DMAC2, dma init failed\n"); -+ return; -+ } else -+ drv_data->dma_dev = dmac2; -+ -+ /* 1. init rx channel */ -+ rxs = &drv_data->dmas_rx; -+ -+ rxs->dirn = DMA_FROM_DEVICE; -+ rxs->hs_mode = LNW_DMA_HW_HS; -+ rxs->cfg_mode = LNW_DMA_PER_TO_MEM; -+ rxs->src_width = LNW_DMA_WIDTH_16BIT; -+ rxs->dst_width = LNW_DMA_WIDTH_32BIT; -+ rxs->src_msize = LNW_DMA_MSIZE_16; -+ rxs->dst_msize = LNW_DMA_MSIZE_16; -+ -+ dma_cap_zero(mask); -+ dma_cap_set(DMA_MEMCPY, mask); -+ dma_cap_set(DMA_SLAVE, mask); -+ -+ drv_data->rxchan = dma_request_channel(mask, chan_filter, -+ drv_data); -+ if (!drv_data->rxchan) -+ goto err_exit; -+ drv_data->rxchan->private = rxs; -+ -+ /* 2. init tx channel */ -+ txs = &drv_data->dmas_tx; -+ -+ txs->dirn = DMA_TO_DEVICE; -+ txs->hs_mode = LNW_DMA_HW_HS; -+ txs->cfg_mode = LNW_DMA_MEM_TO_PER; -+ txs->src_width = LNW_DMA_WIDTH_32BIT; -+ txs->dst_width = LNW_DMA_WIDTH_16BIT; -+ txs->src_msize = LNW_DMA_MSIZE_16; -+ txs->dst_msize = LNW_DMA_MSIZE_16; -+ -+ dma_cap_set(DMA_SLAVE, mask); -+ dma_cap_set(DMA_MEMCPY, mask); -+ -+ drv_data->txchan = dma_request_channel(mask, chan_filter, -+ drv_data); -+ if (!drv_data->txchan) -+ goto free_rxchan; -+ drv_data->txchan->private = txs; -+ -+ /* set the dma done bit to 1 */ -+ drv_data->dma_inited = 1; -+ drv_data->txdma_done = 1; -+ drv_data->rxdma_done = 1; -+ -+ drv_data->tx_param = ((u64)(u32)drv_data << 32) -+ | (u32)(&drv_data->txdma_done); -+ drv_data->rx_param = ((u64)(u32)drv_data << 32) -+ | (u32)(&drv_data->rxdma_done); -+ return; -+ -+free_rxchan: -+ dma_release_channel(drv_data->rxchan); -+err_exit: -+ pci_dev_put(dmac2); -+ return; -+} -+ -+static void mrst_spi_dma_exit(struct driver_data *drv_data) -+{ -+ dma_release_channel(drv_data->txchan); -+ dma_release_channel(drv_data->rxchan); -+ pci_dev_put(drv_data->dma_dev); -+} -+ -+ -+static inline void unmap_dma_buffers(struct driver_data *drv_data); -+static void transfer_complete(struct driver_data *drv_data); -+ -+static void mrst_spi_dma_done(void *arg) -+{ -+ u64 *param = arg; -+ struct driver_data *drv_data; -+ int *done; -+ -+ drv_data = (struct driver_data *)(u32)(*param >> 32); -+ done = (int *)(u32)(*param & 0xffffffff); -+ -+ *done = 1; -+ /* wait till both tx/rx channels are done */ -+ if (!drv_data->txdma_done || !drv_data->rxdma_done) -+ return; -+ -+ transfer_complete(drv_data); -+} -+#endif -+ -+ -+#ifdef CONFIG_DEBUG_FS -+static int spi_show_regs_open(struct inode *inode, struct file *file) -+{ -+ file->private_data = inode->i_private; -+ return 0; -+} -+ -+#define SPI_REGS_BUFSIZE 1024 -+static ssize_t spi_show_regs(struct file *file, char __user *user_buf, -+ size_t count, loff_t *ppos) -+{ -+ char *buf; -+ u32 len = 0; -+ ssize_t ret; -+ struct driver_data *drv_data; -+ void *reg; -+ -+ drv_data = (struct driver_data *)file->private_data; -+ reg = drv_data->vaddr; -+ -+ buf = kzalloc(SPI_REGS_BUFSIZE, GFP_KERNEL); -+ if (!buf) -+ return 0; -+ -+ len += snprintf(buf + len, SPI_REGS_BUFSIZE - len, -+ "MRST SPI0 registers:\n"); -+ len += snprintf(buf + len, SPI_REGS_BUFSIZE - len, -+ "=================================\n"); -+ len += snprintf(buf + len, SPI_REGS_BUFSIZE - len, -+ "CTRL0: \t\t0x%08x\n", read_ctrl0(reg)); -+ len += snprintf(buf + len, SPI_REGS_BUFSIZE - len, -+ "CTRL1: \t\t0x%08x\n", read_ctrl1(reg)); -+ len += snprintf(buf + len, SPI_REGS_BUFSIZE - len, -+ "SSIENR: \t0x%08x\n", read_ssienr(reg)); -+ len += snprintf(buf + len, SPI_REGS_BUFSIZE - len, -+ "SER: \t\t0x%08x\n", read_ser(reg)); -+ len += snprintf(buf + len, SPI_REGS_BUFSIZE - len, -+ "BAUDR: \t\t0x%08x\n", read_baudr(reg)); -+ len += snprintf(buf + len, SPI_REGS_BUFSIZE - len, -+ "TXFTLR: \t0x%08x\n", read_txftlr(reg)); -+ len += snprintf(buf + len, SPI_REGS_BUFSIZE - len, -+ "RXFTLR: \t0x%08x\n", read_rxftlr(reg)); -+ len += snprintf(buf + len, SPI_REGS_BUFSIZE - len, -+ "TXFLR: \t\t0x%08x\n", read_txflr(reg)); -+ len += snprintf(buf + len, SPI_REGS_BUFSIZE - len, -+ "RXFLR: \t\t0x%08x\n", read_rxflr(reg)); -+ len += snprintf(buf + len, SPI_REGS_BUFSIZE - len, -+ "SR: \t\t0x%08x\n", read_sr(reg)); -+ len += snprintf(buf + len, SPI_REGS_BUFSIZE - len, -+ "IMR: \t\t0x%08x\n", read_imr(reg)); -+ len += snprintf(buf + len, SPI_REGS_BUFSIZE - len, -+ "ISR: \t\t0x%08x\n", read_isr(reg)); -+ len += snprintf(buf + len, SPI_REGS_BUFSIZE - len, -+ "DMACR: \t\t0x%08x\n", read_dmacr(reg)); -+ len += snprintf(buf + len, SPI_REGS_BUFSIZE - len, -+ "DMATDLR: \t0x%08x\n", read_dmatdlr(reg)); -+ len += snprintf(buf + len, SPI_REGS_BUFSIZE - len, -+ "DMARDLR: \t0x%08x\n", read_dmardlr(reg)); -+ len += snprintf(buf + len, SPI_REGS_BUFSIZE - len, -+ "=================================\n"); -+ -+ ret = simple_read_from_buffer(user_buf, count, ppos, buf, len); -+ kfree(buf); -+ return ret; -+} -+ -+static const struct file_operations mrst_spi_regs_ops = { -+ .owner = THIS_MODULE, -+ .open = spi_show_regs_open, -+ .read = spi_show_regs, -+}; -+ -+static int mrst_spi_debugfs_init(struct driver_data *drv_data) -+{ -+ drv_data->debugfs = debugfs_create_dir("mrst_spi", NULL); -+ if (!drv_data->debugfs) -+ return -ENOMEM; -+ -+ debugfs_create_file("registers", S_IFREG | S_IRUGO, -+ drv_data->debugfs, (void *)drv_data, &mrst_spi_regs_ops); -+ return 0; -+} -+ -+static void mrst_spi_debugfs_remove(struct driver_data *drv_data) -+{ -+ if (drv_data->debugfs) -+ debugfs_remove_recursive(drv_data->debugfs); -+} -+ -+#else -+static inline int mrst_spi_debugfs_init(struct driver_data *drv_data) -+{ -+} -+ -+static inline void mrst_spi_debugfs_remove(struct driver_data *drv_data) -+{ -+} -+#endif /* CONFIG_DEBUG_FS */ -+ -+static int flush(struct driver_data *drv_data) -+{ -+ unsigned long limit = loops_per_jiffy << 1; -+ void *reg = drv_data->vaddr; -+ -+ while (read_sr(reg) & SR_RF_NOT_EMPT) { -+ limit = loops_per_jiffy << 1; -+ while ((read_sr(reg) & SR_BUSY) && limit--) -+ ; -+ read_dr(reg); -+ } -+ return limit; -+} -+ -+static void null_cs_control(u32 command) -+{ -+} -+ -+static int null_writer(struct driver_data *drv_data) -+{ -+ void *reg = drv_data->vaddr; -+ u8 n_bytes = drv_data->n_bytes; -+ -+ if (!(read_sr(reg) & SR_TF_NOT_FULL) -+ || (drv_data->tx == drv_data->tx_end)) -+ return 0; -+ -+ write_dr(0, reg); -+ drv_data->tx += n_bytes; -+ return 1; -+} -+ -+static int null_reader(struct driver_data *drv_data) -+{ -+ void *reg = drv_data->vaddr; -+ u8 n_bytes = drv_data->n_bytes; -+ -+ while ((read_sr(reg) & SR_RF_NOT_EMPT) -+ && (drv_data->rx < drv_data->rx_end)) { -+ read_dr(reg); -+ drv_data->rx += n_bytes; -+ } -+ return drv_data->rx == drv_data->rx_end; -+} -+ -+static int u8_writer(struct driver_data *drv_data) -+{ -+ void *reg = drv_data->vaddr; -+ -+ if (!(read_sr(reg) & SR_TF_NOT_FULL) -+ || (drv_data->tx == drv_data->tx_end)) -+ return 0; -+ -+ write_dr(*(u8 *)(drv_data->tx), reg); -+ ++drv_data->tx; -+ -+ while (read_sr(reg) & SR_BUSY) -+ ; -+ return 1; -+} -+ -+static int u8_reader(struct driver_data *drv_data) -+{ -+ void *reg = drv_data->vaddr; -+ -+ while ((read_sr(reg) & SR_RF_NOT_EMPT) -+ && (drv_data->rx < drv_data->rx_end)) { -+ *(u8 *)(drv_data->rx) = read_dr(reg); -+ ++drv_data->rx; -+ } -+ -+ while (read_sr(reg) & SR_BUSY) -+ ; -+ return drv_data->rx == drv_data->rx_end; -+} -+ -+static int u16_writer(struct driver_data *drv_data) -+{ -+ void *reg = drv_data->vaddr; -+ -+ if (!(read_sr(reg) & SR_TF_NOT_FULL) -+ || (drv_data->tx == drv_data->tx_end)) -+ return 0; -+ -+ write_dr(*(u16 *)(drv_data->tx), reg); -+ drv_data->tx += 2; -+ while (read_sr(reg) & SR_BUSY) -+ ; -+ -+ return 1; -+} -+ -+static int u16_reader(struct driver_data *drv_data) -+{ -+ void *reg = drv_data->vaddr; -+ u16 temp; -+ -+ while ((read_sr(reg) & SR_RF_NOT_EMPT) -+ && (drv_data->rx < drv_data->rx_end)) { -+ temp = read_dr(reg); -+ *(u16 *)(drv_data->rx) = temp; -+ drv_data->rx += 2; -+ } -+ -+ while (read_sr(reg) & SR_BUSY) -+ ; -+ -+ return drv_data->rx == drv_data->rx_end; -+} -+ -+static void *next_transfer(struct driver_data *drv_data) -+{ -+ struct spi_message *msg = drv_data->cur_msg; -+ struct spi_transfer *trans = drv_data->cur_transfer; -+ -+ /* Move to next transfer */ -+ if (trans->transfer_list.next != &msg->transfers) { -+ drv_data->cur_transfer = -+ list_entry(trans->transfer_list.next, -+ struct spi_transfer, -+ transfer_list); -+ return RUNNING_STATE; -+ } else -+ return DONE_STATE; -+} -+ -+/* -+ * Note: first step is the protocol driver prepares -+ * a dma-capable memory, and this func just need translate -+ * the virt addr to physical -+ */ -+static int map_dma_buffers(struct driver_data *drv_data) -+{ -+ if (!drv_data->cur_msg->is_dma_mapped || !drv_data->dma_inited -+ || !drv_data->cur_chip->enable_dma) -+ return 0; -+ -+ if (drv_data->cur_transfer->tx_dma) -+ drv_data->tx_dma = drv_data->cur_transfer->tx_dma; -+ -+ if (drv_data->cur_transfer->rx_dma) -+ drv_data->rx_dma = drv_data->cur_transfer->rx_dma; -+ -+ return 1; -+} -+ -+static inline void unmap_dma_buffers(struct driver_data *drv_data) -+{ -+ if (!drv_data->dma_mapped) -+ return; -+ drv_data->dma_mapped = 0; -+} -+ -+/* caller already set message->status; dma and pio irqs are blocked */ -+static void giveback(struct driver_data *drv_data) -+{ -+ struct spi_transfer *last_transfer; -+ unsigned long flags; -+ struct spi_message *msg; -+ -+ spin_lock_irqsave(&drv_data->lock, flags); -+ msg = drv_data->cur_msg; -+ drv_data->cur_msg = NULL; -+ drv_data->cur_transfer = NULL; -+ drv_data->prev_chip = drv_data->cur_chip; -+ drv_data->cur_chip = NULL; -+ queue_work(drv_data->workqueue, &drv_data->pump_messages); -+ spin_unlock_irqrestore(&drv_data->lock, flags); -+ -+ last_transfer = list_entry(msg->transfers.prev, -+ struct spi_transfer, -+ transfer_list); -+ -+ if (!last_transfer->cs_change) -+ drv_data->cs_control(MRST_SPI_DEASSERT); -+ -+ msg->state = NULL; -+ if (msg->complete) -+ msg->complete(msg->context); -+} -+ -+static void dma_transfer(struct driver_data *drv_data, int cs_change) -+{ -+#ifdef CONFIG_SPI_MRST_DMA -+ void *reg = drv_data->vaddr; -+ struct dma_async_tx_descriptor *txdesc = NULL, *rxdesc = NULL; -+ struct dma_chan *txchan, *rxchan; -+ enum dma_ctrl_flags flag; -+ u16 dmacr = 0; -+ -+ /* 1. setup DMA related registers */ -+ if (cs_change) { -+ mrst_spi_enable(reg, 0); -+ -+ write_dmardlr(0xf, reg); -+ write_dmatdlr(0x10, reg); -+ -+ if (drv_data->tx_dma) -+ dmacr |= 0x2; -+ if (drv_data->rx_dma) -+ dmacr |= 0x1; -+ -+ write_dmacr(dmacr, reg); -+ mrst_spi_enable(reg, 1); -+ } -+ -+ if (drv_data->tx_dma) -+ drv_data->txdma_done = 0; -+ -+ if (drv_data->rx_dma) -+ drv_data->rxdma_done = 0; -+ -+ /* 2. start the TX dma transfer */ -+ txchan = drv_data->txchan; -+ rxchan = drv_data->rxchan; -+ -+ flag = DMA_PREP_INTERRUPT | DMA_CTRL_ACK; -+ -+ if (drv_data->tx_dma) { -+ txdesc = txchan->device->device_prep_dma_memcpy(txchan, -+ drv_data->dma_addr, drv_data->tx_dma, -+ drv_data->len, flag); -+ -+ txdesc->callback = mrst_spi_dma_done; -+ txdesc->callback_param = &drv_data->tx_param; -+ } -+ -+ /* 3. start the RX dma transfer */ -+ if (drv_data->rx_dma) { -+ rxdesc = rxchan->device->device_prep_dma_memcpy(rxchan, -+ drv_data->rx_dma, drv_data->dma_addr, -+ drv_data->len, flag); -+ -+ rxdesc->callback = mrst_spi_dma_done; -+ rxdesc->callback_param = &drv_data->rx_param; -+ } -+ -+ /* rx must be started before tx due to spi instinct */ -+ if (rxdesc) -+ rxdesc->tx_submit(rxdesc); -+ if (txdesc) -+ txdesc->tx_submit(txdesc); -+#endif -+} -+ -+static void int_error_stop(struct driver_data *drv_data, const char *msg) -+{ -+ void *reg = drv_data->vaddr; -+ -+ /* Stop and reset hw */ -+ flush(drv_data); -+ write_ssienr(0, reg); -+ -+ dev_err(&drv_data->pdev->dev, "%s\n", msg); -+ -+ drv_data->cur_msg->state = ERROR_STATE; -+ tasklet_schedule(&drv_data->pump_transfers); -+} -+ -+static void transfer_complete(struct driver_data *drv_data) -+{ -+ /* Update total byte transfered return count actual bytes read */ -+ drv_data->cur_msg->actual_length += drv_data->len; -+ -+ /* Move to next transfer */ -+ drv_data->cur_msg->state = next_transfer(drv_data); -+ -+ /* handle end of message */ -+ if (drv_data->cur_msg->state == DONE_STATE) { -+ drv_data->cur_msg->status = 0; -+ giveback(drv_data); -+ } else -+ tasklet_schedule(&drv_data->pump_transfers); -+} -+ -+static irqreturn_t interrupt_transfer(struct driver_data *drv_data) -+{ -+ void *reg = drv_data->vaddr; -+ u32 irq_status, irq_mask = 0x3f; -+ -+ irq_status = read_isr(reg) & irq_mask; -+ -+ /* error handling */ -+ if (irq_status & (SPI_INT_TXOI | SPI_INT_RXOI | SPI_INT_RXUI)) { -+ read_txoicr(reg); -+ read_rxoicr(reg); -+ read_rxuicr(reg); -+ int_error_stop(drv_data, "interrupt_transfer: fifo overrun"); -+ return IRQ_HANDLED; -+ } -+ -+ /* INT comes from tx */ -+ if (drv_data->tx && (irq_status & SPI_INT_TXEI)) -+ while (drv_data->tx < drv_data->tx_end) { -+ drv_data->write(drv_data); -+ -+ if (drv_data->tx == drv_data->tx_end) { -+ spi_mask_intr(reg, SPI_INT_TXEI); -+ transfer_complete(drv_data); -+ } -+ } -+ -+ /* INT comes from rx */ -+ if (drv_data->rx && (irq_status & SPI_INT_RXFI)) { -+ if (drv_data->read(drv_data)) -+ transfer_complete(drv_data); -+ } -+ -+ return IRQ_HANDLED; -+} -+ -+static irqreturn_t mrst_spi_irq(int irq, void *dev_id) -+{ -+ struct driver_data *drv_data = dev_id; -+ void *reg = drv_data->vaddr; -+ -+ if (!drv_data->cur_msg) { -+ spi_mask_intr(reg, SPI_INT_TXEI); -+ /* Never fail */ -+ return IRQ_HANDLED; -+ } -+ -+ return drv_data->transfer_handler(drv_data); -+} -+ -+/* must be called inside pump_transfers() */ -+static void poll_transfer(struct driver_data *drv_data) -+{ -+ if (drv_data->tx) -+ while (drv_data->write(drv_data)) -+ drv_data->read(drv_data); -+ -+ drv_data->read(drv_data); -+ transfer_complete(drv_data); -+} -+ -+static void pump_transfers(unsigned long data) -+{ -+ struct driver_data *drv_data = (struct driver_data *)data; -+ struct spi_message *message = NULL; -+ struct spi_transfer *transfer = NULL; -+ struct spi_transfer *previous = NULL; -+ struct spi_device *spi = NULL; -+ struct chip_data *chip = NULL; -+ void *reg = drv_data->vaddr; -+ u8 bits = 0; -+ u8 imask = 0; -+ u8 cs_change = 0; -+ u16 rxint_level = 0; -+ u16 txint_level = 0; -+ u16 clk_div = 0; -+ u32 speed = 0; -+ u32 cr0 = 0; -+ -+ /* get current state information */ -+ message = drv_data->cur_msg; -+ transfer = drv_data->cur_transfer; -+ chip = drv_data->cur_chip; -+ spi = message->spi; -+ -+ if (unlikely(!chip->clk_div)) { -+ /* default for 115200 UART device */ -+ if (chip->speed_hz) -+ chip->clk_div = drv_data->freq / chip->speed_hz; -+ else -+ chip->clk_div = drv_data->freq / 115200; -+ } -+ -+ /* handle for abort */ -+ if (message->state == ERROR_STATE) { -+ message->status = -EIO; -+ goto early_exit; -+ } -+ -+ /* handle end of message */ -+ if (message->state == DONE_STATE) { -+ message->status = 0; -+ goto early_exit; -+ } -+ -+ /* delay if requested at end of transfer*/ -+ if (message->state == RUNNING_STATE) { -+ previous = list_entry(transfer->transfer_list.prev, -+ struct spi_transfer, -+ transfer_list); -+ if (previous->delay_usecs) -+ udelay(previous->delay_usecs); -+ } -+ -+ drv_data->n_bytes = chip->n_bytes; -+ drv_data->dma_width = chip->dma_width; -+ drv_data->cs_control = chip->cs_control; -+ -+ drv_data->rx_dma = transfer->rx_dma; -+ drv_data->tx_dma = transfer->tx_dma; -+ drv_data->tx = (void *)transfer->tx_buf; -+ drv_data->tx_end = drv_data->tx + transfer->len; -+ drv_data->rx = transfer->rx_buf; -+ drv_data->rx_end = drv_data->rx + transfer->len; -+ drv_data->write = drv_data->tx ? chip->write : null_writer; -+ drv_data->read = drv_data->rx ? chip->read : null_reader; -+ drv_data->cs_change = transfer->cs_change; -+ drv_data->len = drv_data->cur_transfer->len; -+ if (chip != drv_data->prev_chip) -+ cs_change = 1; -+ -+ /* handle per transfer options for bpw and speed */ -+ cr0 = chip->cr0; -+ if (transfer->speed_hz) { -+ speed = chip->speed_hz; -+ -+ if (transfer->speed_hz != speed) { -+ speed = transfer->speed_hz; -+ if (speed > drv_data->freq) { -+ printk(KERN_ERR "MRST SPI0: unsupported" -+ "freq: %dHz\n", speed); -+ message->status = -EIO; -+ goto early_exit; -+ } -+ -+ /* clk_div doesn't support odd number */ -+ clk_div = (drv_data->freq + speed - 1) / speed; -+ clk_div = ((clk_div + 1) >> 1) << 1; -+ -+ chip->speed_hz = speed; -+ chip->clk_div = clk_div; -+ } -+ } -+ -+ if (transfer->bits_per_word) { -+ bits = transfer->bits_per_word; -+ -+ switch (bits) { -+ case 8: -+ drv_data->n_bytes = 1; -+ drv_data->dma_width = 1; -+ drv_data->read = drv_data->read != null_reader ? -+ u8_reader : null_reader; -+ drv_data->write = drv_data->write != null_writer ? -+ u8_writer : null_writer; -+ break; -+ case 16: -+ drv_data->n_bytes = 2; -+ drv_data->dma_width = 2; -+ drv_data->read = drv_data->read != null_reader ? -+ u16_reader : null_reader; -+ drv_data->write = drv_data->write != null_writer ? -+ u16_writer : null_writer; -+ break; -+ default: -+ printk(KERN_ERR "MRST SPI0: unsupported bits:" -+ "%db\n", bits); -+ message->status = -EIO; -+ goto early_exit; -+ } -+ -+ cr0 = (bits - 1) -+ | (chip->type << SPI_FRF_OFFSET) -+ | (spi->mode << SPI_MODE_OFFSET) -+ | (chip->tmode << SPI_TMOD_OFFSET); -+ } -+ -+ message->state = RUNNING_STATE; -+ -+ /* try to map dma buffer and do a dma transfer if successful */ -+ drv_data->dma_mapped = 0; -+ if (drv_data->len && (drv_data->len <= MRST_MAX_DMA_LEN)) -+ drv_data->dma_mapped = map_dma_buffers(drv_data); -+ -+ if (!drv_data->dma_mapped && !chip->poll_mode) { -+ if (drv_data->rx) { -+ if (drv_data->len >= SPI_INT_THRESHOLD) -+ rxint_level = SPI_INT_THRESHOLD; -+ else -+ rxint_level = drv_data->len; -+ imask |= SPI_INT_RXFI; -+ } -+ -+ if (drv_data->tx) -+ imask |= SPI_INT_TXEI; -+ drv_data->transfer_handler = interrupt_transfer; -+ } -+ -+ /* -+ * reprogram registers only if -+ * 1. chip select changes -+ * 2. clk_div is changes -+ * 3. control value changes -+ */ -+ if (read_ctrl0(reg) != cr0 || cs_change || clk_div) { -+ mrst_spi_enable(reg, 0); -+ -+ if (read_ctrl0(reg) != cr0) -+ write_ctrl0(cr0, reg); -+ -+ if (txint_level) -+ write_txftlr(txint_level, reg); -+ -+ if (rxint_level) -+ write_rxftlr(rxint_level, reg); -+ -+ /* set the interrupt mask, for poll mode just diable all int */ -+ spi_mask_intr(reg, 0xff); -+ if (!chip->poll_mode) -+ spi_umask_intr(reg, imask); -+ -+ spi_enable_clk(reg, clk_div ? clk_div : chip->clk_div); -+ spi_chip_sel(reg, spi->chip_select); -+ mrst_spi_enable(reg, 1); -+ -+ if (cs_change) -+ drv_data->prev_chip = chip; -+ } -+ -+ if (drv_data->dma_mapped) -+ dma_transfer(drv_data, cs_change); -+ -+ if (chip->poll_mode) -+ poll_transfer(drv_data); -+ -+ return; -+ -+early_exit: -+ giveback(drv_data); -+ return; -+} -+ -+static void pump_messages(struct work_struct *work) -+{ -+ struct driver_data *drv_data = -+ container_of(work, struct driver_data, pump_messages); -+ unsigned long flags; -+ -+ /* Lock queue and check for queue work */ -+ spin_lock_irqsave(&drv_data->lock, flags); -+ if (list_empty(&drv_data->queue) || drv_data->run == QUEUE_STOPPED) { -+ drv_data->busy = 0; -+ spin_unlock_irqrestore(&drv_data->lock, flags); -+ return; -+ } -+ -+ /* Make sure we are not already running a message */ -+ if (drv_data->cur_msg) { -+ spin_unlock_irqrestore(&drv_data->lock, flags); -+ return; -+ } -+ -+ /* Extract head of queue */ -+ drv_data->cur_msg = list_entry(drv_data->queue.next, -+ struct spi_message, queue); -+ list_del_init(&drv_data->cur_msg->queue); -+ -+ /* Initial message state*/ -+ drv_data->cur_msg->state = START_STATE; -+ drv_data->cur_transfer = list_entry(drv_data->cur_msg->transfers.next, -+ struct spi_transfer, -+ transfer_list); -+ drv_data->cur_chip = spi_get_ctldata(drv_data->cur_msg->spi); -+ -+ /* Mark as busy and launch transfers */ -+ tasklet_schedule(&drv_data->pump_transfers); -+ -+ drv_data->busy = 1; -+ spin_unlock_irqrestore(&drv_data->lock, flags); -+} -+ -+/* spi_device use this to queue in the their spi_msg */ -+static int mrst_spi_transfer(struct spi_device *spi, struct spi_message *msg) -+{ -+ struct driver_data *drv_data = spi_master_get_devdata(spi->master); -+ unsigned long flags; -+ -+ spin_lock_irqsave(&drv_data->lock, flags); -+ -+ if (drv_data->run == QUEUE_STOPPED) { -+ spin_unlock_irqrestore(&drv_data->lock, flags); -+ return -ESHUTDOWN; -+ } -+ -+ msg->actual_length = 0; -+ msg->status = -EINPROGRESS; -+ msg->state = START_STATE; -+ -+ list_add_tail(&msg->queue, &drv_data->queue); -+ -+ if (drv_data->run == QUEUE_RUNNING && !drv_data->busy) { -+ -+ if (drv_data->cur_transfer || drv_data->cur_msg) -+ queue_work(drv_data->workqueue, -+ &drv_data->pump_messages); -+ else { -+ /* if no other data transaction in air, just go */ -+ spin_unlock_irqrestore(&drv_data->lock, flags); -+ pump_messages(&drv_data->pump_messages); -+ return 0; -+ } -+ } -+ -+ spin_unlock_irqrestore(&drv_data->lock, flags); -+ return 0; -+} -+ -+/* this may be called twice for each spi dev */ -+static int mrst_spi_setup(struct spi_device *spi) -+{ -+ struct mrst_spi_chip *chip_info = NULL; -+ struct chip_data *chip; -+ -+ if (spi->bits_per_word != 8 && spi->bits_per_word != 16) -+ return -EINVAL; -+ -+ /* Only alloc on first setup */ -+ chip = spi_get_ctldata(spi); -+ if (!chip) { -+ chip = kzalloc(sizeof(struct chip_data), GFP_KERNEL); -+ if (!chip) -+ return -ENOMEM; -+ -+ chip->cs_control = null_cs_control; -+ chip->enable_dma = 0; -+ } -+ -+ /* protocol drivers may change the chip settings, so... -+ * if chip_info exists, use it */ -+ chip_info = spi->controller_data; -+ -+ /* chip_info doesn't always exist */ -+ if (chip_info) { -+ if (chip_info->cs_control) -+ chip->cs_control = chip_info->cs_control; -+ -+ chip->poll_mode = chip_info->poll_mode; -+ chip->type = chip_info->type; -+ -+ chip->rx_threshold = 0; -+ chip->tx_threshold = 0; -+ -+ chip->enable_dma = chip_info->enable_dma; -+ } -+ -+ if (spi->bits_per_word <= 8) { -+ chip->n_bytes = 1; -+ chip->dma_width = 1; -+ chip->read = u8_reader; -+ chip->write = u8_writer; -+ } else if (spi->bits_per_word <= 16) { -+ chip->n_bytes = 2; -+ chip->dma_width = 2; -+ chip->read = u16_reader; -+ chip->write = u16_writer; -+ } else { -+ /* never take >16b case for MRST SPIC */ -+ dev_err(&spi->dev, "invalid wordsize\n"); -+ return -ENODEV; -+ } -+ -+ chip->bits_per_word = spi->bits_per_word; -+ chip->speed_hz = spi->max_speed_hz; -+ chip->tmode = 0; /* Tx & Rx */ -+ /* default SPI mode is SCPOL = 0, SCPH = 0 */ -+ chip->cr0 = (chip->bits_per_word - 1) -+ | (chip->type << SPI_FRF_OFFSET) -+ | (spi->mode << SPI_MODE_OFFSET) -+ | (chip->tmode << SPI_TMOD_OFFSET); -+ -+ spi_set_ctldata(spi, chip); -+ return 0; -+} -+ -+static void mrst_spi_cleanup(struct spi_device *spi) -+{ -+ struct chip_data *chip = spi_get_ctldata(spi); -+ -+ kfree(chip); -+} -+ -+static int __init init_queue(struct driver_data *drv_data) -+{ -+ INIT_LIST_HEAD(&drv_data->queue); -+ spin_lock_init(&drv_data->lock); -+ -+ drv_data->run = QUEUE_STOPPED; -+ drv_data->busy = 0; -+ -+ tasklet_init(&drv_data->pump_transfers, -+ pump_transfers, (unsigned long)drv_data); -+ -+ INIT_WORK(&drv_data->pump_messages, pump_messages); -+ drv_data->workqueue = create_singlethread_workqueue( -+ dev_name(drv_data->master->dev.parent)); -+ if (drv_data->workqueue == NULL) -+ return -EBUSY; -+ -+ return 0; -+} -+ -+static int start_queue(struct driver_data *drv_data) -+{ -+ unsigned long flags; -+ -+ spin_lock_irqsave(&drv_data->lock, flags); -+ -+ if (drv_data->run == QUEUE_RUNNING || drv_data->busy) { -+ spin_unlock_irqrestore(&drv_data->lock, flags); -+ return -EBUSY; -+ } -+ -+ drv_data->run = QUEUE_RUNNING; -+ drv_data->cur_msg = NULL; -+ drv_data->cur_transfer = NULL; -+ drv_data->cur_chip = NULL; -+ drv_data->prev_chip = NULL; -+ spin_unlock_irqrestore(&drv_data->lock, flags); -+ -+ queue_work(drv_data->workqueue, &drv_data->pump_messages); -+ -+ return 0; -+} -+ -+static int stop_queue(struct driver_data *drv_data) -+{ -+ unsigned long flags; -+ unsigned limit = 500; -+ int status = 0; -+ -+ spin_lock_irqsave(&drv_data->lock, flags); -+ drv_data->run = QUEUE_STOPPED; -+ while (!list_empty(&drv_data->queue) && drv_data->busy && limit--) { -+ spin_unlock_irqrestore(&drv_data->lock, flags); -+ msleep(10); -+ spin_lock_irqsave(&drv_data->lock, flags); -+ } -+ -+ if (!list_empty(&drv_data->queue) || drv_data->busy) -+ status = -EBUSY; -+ spin_unlock_irqrestore(&drv_data->lock, flags); -+ -+ return status; -+} -+ -+static int destroy_queue(struct driver_data *drv_data) -+{ -+ int status; -+ -+ status = stop_queue(drv_data); -+ if (status != 0) -+ return status; -+ destroy_workqueue(drv_data->workqueue); -+ return 0; -+} -+ -+/* restart the spic, disable all interrupts, clean rx fifo */ -+static void spi_hw_init(struct driver_data *drv_data) -+{ -+ void *reg = drv_data->vaddr; -+ -+ mrst_spi_enable(reg, 0x0); -+ spi_mask_intr(reg, 0xff); -+ mrst_spi_enable(reg, 0x1); -+ -+ flush(drv_data); -+} -+ -+static int __devinit mrst_spi_probe(struct pci_dev *pdev, -+ const struct pci_device_id *ent) -+{ -+ int ret; -+ struct driver_data *drv_data; -+ struct spi_master *master; -+ struct device *dev = &pdev->dev; -+ u32 *clk_reg, clk_cdiv; -+ int pci_bar = 0; -+ -+ BUG_ON(pdev == NULL); -+ BUG_ON(ent == NULL); -+ -+ printk(KERN_INFO "MRST: found PCI SPI controller(ID: %04x:%04x)\n", -+ pdev->vendor, pdev->device); -+ -+ ret = pci_enable_device(pdev); -+ if (ret) -+ return ret; -+ -+ master = spi_alloc_master(dev, sizeof(struct driver_data)); -+ if (!master) { -+ ret = -ENOMEM; -+ goto exit; -+ } -+ -+ drv_data = spi_master_get_devdata(master); -+ drv_data->master = master; -+ drv_data->pdev = pdev; -+ drv_data->type = SSI_MOTO_SPI; -+ drv_data->prev_chip = NULL; -+ -+ /* get basic io resource and map it */ -+ drv_data->paddr = (void *)pci_resource_start(pdev, pci_bar); -+ drv_data->iolen = pci_resource_len(pdev, pci_bar); -+ drv_data->dma_addr = (dma_addr_t)(drv_data->paddr + 0x60); -+ -+ ret = pci_request_region(pdev, pci_bar, dev_name(&pdev->dev)); -+ if (ret) -+ goto err_free_master; -+ -+ drv_data->vaddr = ioremap_nocache((unsigned long)drv_data->paddr, -+ drv_data->iolen); -+ if (!drv_data->vaddr) { -+ ret = -ENOMEM; -+ goto err_free_pci; -+ } -+ -+ clk_reg = ioremap_nocache(MRST_CLK_SPI0_REG, 16); -+ if (!clk_reg) { -+ ret = -ENOMEM; -+ goto err_iounmap; -+ } -+ -+ /* get SPI controller operating freq info */ -+ clk_cdiv = ((*clk_reg) & CLK_SPI_CDIV_MASK) >> CLK_SPI_CDIV_OFFSET; -+ drv_data->freq = MRST_SPI_CLK_BASE / (clk_cdiv + 1); -+ iounmap(clk_reg); -+ -+ drv_data->irq = pdev->irq; -+ ret = request_irq(drv_data->irq, mrst_spi_irq, 0, -+ "mrst_spic0", drv_data); -+ if (ret < 0) { -+ dev_err(&pdev->dev, "can not get IRQ\n"); -+ goto err_iounmap; -+ } -+ -+ spin_lock_init(&drv_data->lock); -+ -+ master->mode_bits = SPI_CPOL | SPI_CPHA; -+ -+ master->bus_num = 0; -+ master->num_chipselect = 16; -+ master->cleanup = mrst_spi_cleanup; -+ master->setup = mrst_spi_setup; -+ master->transfer = mrst_spi_transfer; -+ -+ drv_data->dma_inited = 0; -+#ifdef CONFIG_SPI_MRST_DMA -+ mrst_spi_dma_init(drv_data); -+#endif -+ -+ /* basic HW init */ -+ spi_hw_init(drv_data); -+ -+ /* Initial and start queue */ -+ ret = init_queue(drv_data); -+ if (ret) { -+ dev_err(&pdev->dev, "problem initializing queue\n"); -+ goto err_diable_hw; -+ } -+ ret = start_queue(drv_data); -+ if (ret) { -+ dev_err(&pdev->dev, "problem starting queue\n"); -+ goto err_diable_hw; -+ } -+ -+ ret = spi_register_master(master); -+ if (ret) { -+ dev_err(&pdev->dev, "problem registering spi master\n"); -+ goto err_queue_alloc; -+ } -+ -+ /* PCI hook and SPI hook use the same drv data */ -+ pci_set_drvdata(pdev, drv_data); -+ mrst_spi_debugfs_init(drv_data); -+ -+ return 0; -+ -+err_queue_alloc: -+ destroy_queue(drv_data); -+#ifdef CONFIG_SPI_MRST_DMA -+ mrst_spi_dma_exit(drv_data); -+#endif -+err_diable_hw: -+ mrst_spi_enable(drv_data->vaddr, 0); -+ free_irq(drv_data->irq, drv_data); -+err_iounmap: -+ iounmap(drv_data->vaddr); -+err_free_pci: -+ pci_release_region(pdev, pci_bar); -+err_free_master: -+ spi_master_put(master); -+exit: -+ pci_disable_device(pdev); -+ return ret; -+} -+ -+static void __devexit mrst_spi_remove(struct pci_dev *pdev) -+{ -+ struct driver_data *drv_data = pci_get_drvdata(pdev); -+ void *reg; -+ int status = 0; -+ -+ if (!drv_data) -+ return; -+ -+ mrst_spi_debugfs_remove(drv_data); -+ pci_set_drvdata(pdev, NULL); -+ -+ /* remove the queue */ -+ status = destroy_queue(drv_data); -+ if (status != 0) -+ dev_err(&pdev->dev, "mrst_spi_remove: workqueue will not " -+ "complete, message memory not freed\n"); -+ -+#ifdef CONFIG_SPI_MRST_DMA -+ mrst_spi_dma_exit(drv_data); -+#endif -+ -+ reg = drv_data->vaddr; -+ mrst_spi_enable(reg, 0); -+ spi_disable_clk(reg); -+ -+ /* release IRQ */ -+ free_irq(drv_data->irq, drv_data); -+ -+ iounmap(drv_data->vaddr); -+ pci_release_region(pdev, 0); -+ -+ /* disconnect from the SPI framework */ -+ spi_unregister_master(drv_data->master); -+ pci_disable_device(pdev); -+} -+ -+#ifdef CONFIG_PM -+static int mrst_spi_suspend(struct pci_dev *pdev, pm_message_t state) -+{ -+ struct driver_data *drv_data = pci_get_drvdata(pdev); -+ void *reg = drv_data->vaddr; -+ int status = 0; -+ -+ status = stop_queue(drv_data); -+ if (status) -+ return status; -+ -+ mrst_spi_enable(reg, 0); -+ spi_disable_clk(reg); -+ return status; -+} -+ -+static int mrst_spi_resume(struct pci_dev *pdev) -+{ -+ struct driver_data *drv_data = pci_get_drvdata(pdev); -+ int status = 0; -+ -+ spi_hw_init(drv_data); -+ -+ /* Start the queue running */ -+ status = start_queue(drv_data); -+ if (status) -+ dev_err(&pdev->dev, "problem starting queue (%d)\n", status); -+ return status; -+} -+#else -+#define mrst_spi_suspend NULL -+#define mrst_spi_resume NULL -+#endif -+ -+static const struct pci_device_id pci_ids[] __devinitdata = { -+ { PCI_DEVICE(PCI_VENDOR_ID_INTEL, 0x0800) }, -+ {}, -+}; -+ -+static struct pci_driver mrst_spi_driver = { -+ .name = DRIVER_NAME, -+ .id_table = pci_ids, -+ .probe = mrst_spi_probe, -+ .remove = __devexit_p(mrst_spi_remove), -+ .suspend = mrst_spi_suspend, -+ .resume = mrst_spi_resume, -+}; -+ -+static int __init mrst_spi_init(void) -+{ -+ return pci_register_driver(&mrst_spi_driver); -+} -+ -+static void __exit mrst_spi_exit(void) -+{ -+ pci_unregister_driver(&mrst_spi_driver); -+} -+ -+module_init(mrst_spi_init); -+module_exit(mrst_spi_exit); -+ -+MODULE_AUTHOR("Feng Tang <feng.tang@intel.com>"); -+MODULE_DESCRIPTION("Intel Moorestown SPI controller driver"); -+MODULE_LICENSE("GPL v2"); -Index: linux-2.6.33/include/linux/spi/mrst_spi.h -=================================================================== ---- /dev/null -+++ linux-2.6.33/include/linux/spi/mrst_spi.h -@@ -0,0 +1,162 @@ -+#ifndef MRST_SPI_HEADER_H -+#define MRST_SPI_HEADER_H -+#include <linux/io.h> -+ -+/* bit fields in CTRLR0 */ -+#define SPI_DFS_OFFSET 0 -+ -+#define SPI_FRF_OFFSET 4 -+#define SPI_FRF_SPI 0x0 -+#define SPI_FRF_SSP 0x1 -+#define SPI_FRF_MICROWIRE 0x2 -+#define SPI_FRF_RESV 0x3 -+ -+#define SPI_MODE_OFFSET 6 -+#define SPI_SCPH_OFFSET 6 -+#define SPI_SCOL_OFFSET 7 -+#define SPI_TMOD_OFFSET 8 -+#define SPI_TMOD_TR 0x0 /* xmit & recv */ -+#define SPI_TMOD_TO 0x1 /* xmit only */ -+#define SPI_TMOD_RO 0x2 /* recv only */ -+#define SPI_TMOD_EPROMREAD 0x3 /* eeprom read mode */ -+ -+#define SPI_SLVOE_OFFSET 10 -+#define SPI_SRL_OFFSET 11 -+#define SPI_CFS_OFFSET 12 -+ -+/* bit fields in SR, 7 bits */ -+#define SR_MASK 0x7f /* cover 7 bits */ -+#define SR_BUSY (1 << 0) -+#define SR_TF_NOT_FULL (1 << 1) -+#define SR_TF_EMPT (1 << 2) -+#define SR_RF_NOT_EMPT (1 << 3) -+#define SR_RF_FULL (1 << 4) -+#define SR_TX_ERR (1 << 5) -+#define SR_DCOL (1 << 6) -+ -+/* bit fields in ISR, IMR, RISR, 7 bits */ -+#define SPI_INT_TXEI (1 << 0) -+#define SPI_INT_TXOI (1 << 1) -+#define SPI_INT_RXUI (1 << 2) -+#define SPI_INT_RXOI (1 << 3) -+#define SPI_INT_RXFI (1 << 4) -+#define SPI_INT_MSTI (1 << 5) -+ -+/* TX RX interrupt level threshhold, max can be 256 */ -+#define SPI_INT_THRESHOLD 32 -+ -+#define DEFINE_MRST_SPI_RW_REG(reg, off) \ -+static inline u32 read_##reg(void *p) \ -+{ return readl(p + (off)); } \ -+static inline void write_##reg(u32 v, void *p) \ -+{ writel(v, p + (off)); } -+ -+#define DEFINE_MRST_SPI_RO_REG(reg, off) \ -+static inline u32 read_##reg(void *p) \ -+{ return readl(p + (off)); } \ -+ -+DEFINE_MRST_SPI_RW_REG(ctrl0, 0x00) -+DEFINE_MRST_SPI_RW_REG(ctrl1, 0x04) -+DEFINE_MRST_SPI_RW_REG(ssienr, 0x08) -+DEFINE_MRST_SPI_RW_REG(mwcr, 0x0c) -+DEFINE_MRST_SPI_RW_REG(ser, 0x10) -+DEFINE_MRST_SPI_RW_REG(baudr, 0x14) -+DEFINE_MRST_SPI_RW_REG(txftlr, 0x18) -+DEFINE_MRST_SPI_RW_REG(rxftlr, 0x1c) -+DEFINE_MRST_SPI_RO_REG(txflr, 0x20) -+DEFINE_MRST_SPI_RO_REG(rxflr, 0x24) -+DEFINE_MRST_SPI_RO_REG(sr, 0x28) -+DEFINE_MRST_SPI_RW_REG(imr, 0x2c) -+DEFINE_MRST_SPI_RO_REG(isr, 0x30) -+DEFINE_MRST_SPI_RO_REG(risr, 0x34) -+DEFINE_MRST_SPI_RO_REG(txoicr, 0x38) -+DEFINE_MRST_SPI_RO_REG(rxoicr, 0x3c) -+DEFINE_MRST_SPI_RO_REG(rxuicr, 0x40) -+DEFINE_MRST_SPI_RO_REG(msticr, 0x44) -+DEFINE_MRST_SPI_RO_REG(icr, 0x48) -+DEFINE_MRST_SPI_RW_REG(dmacr, 0x4c) -+DEFINE_MRST_SPI_RW_REG(dmatdlr, 0x50) -+DEFINE_MRST_SPI_RW_REG(dmardlr, 0x54) -+DEFINE_MRST_SPI_RO_REG(idr, 0x58) -+DEFINE_MRST_SPI_RO_REG(version, 0x5c) -+DEFINE_MRST_SPI_RW_REG(dr, 0x60) -+ -+static inline void mrst_spi_enable(void *reg, int enable) -+{ -+ if (enable) -+ write_ssienr(0x1, reg); -+ else -+ write_ssienr(0x0, reg); -+} -+ -+static inline void spi_enable_clk(void *reg, u16 div) -+{ -+ write_baudr(div, reg); -+} -+ -+static inline void spi_chip_sel(void *reg, u16 cs) -+{ -+ if (cs > 4) -+ return; -+ write_ser((1 << cs), reg); -+} -+ -+static inline void spi_disable_clk(void *reg) -+{ -+ /* set the divider to 0 will diable the clock */ -+ write_baudr(0, reg); -+} -+ -+/* disable some INT */ -+static inline void spi_mask_intr(void *reg, u32 mask) -+{ -+ u32 imr; -+ imr = read_imr(reg) & ~mask; -+ write_imr(imr, reg); -+} -+ -+/* enable INT */ -+static inline void spi_umask_intr(void *reg, u32 mask) -+{ -+ u32 imr; -+ imr = read_imr(reg) | mask; -+ write_imr(imr, reg); -+} -+ -+enum mrst_ssi_type { -+ SSI_MOTO_SPI = 0, -+ SSI_TI_SSP, -+ SSI_NS_MICROWIRE, -+}; -+ -+/* usually will be controller_data for SPI slave devices */ -+struct mrst_spi_chip { -+ u8 poll_mode; /* 0 for contoller polling mode */ -+ u8 type; /* SPI/SSP/Micrwire */ -+ u8 enable_dma; -+ void (*cs_control)(u32 command); -+}; -+ -+#define SPI_DIB_NAME_LEN 16 -+#define SPI_DIB_SPEC_INFO_LEN 10 -+ -+#define MRST_GPE_IRQ_VIA_GPIO_BIT (1 << 15) -+/* SPI device info block related */ -+struct spi_dib_header { -+ u32 signature; -+ u32 length; -+ u8 rev; -+ u8 checksum; -+ u8 dib[0]; -+} __attribute__((packed)); -+ -+struct spi_dib { -+ u16 host_num; -+ u16 cs; -+ u16 irq; -+ char name[SPI_DIB_NAME_LEN]; -+ u8 dev_data[SPI_DIB_SPEC_INFO_LEN]; -+} __attribute__((packed)); -+ -+extern struct console early_mrst_console; -+#endif /* #ifndef MRST_SPI_HEADER_H */ -Index: linux-2.6.33/drivers/serial/Kconfig -=================================================================== ---- linux-2.6.33.orig/drivers/serial/Kconfig -+++ linux-2.6.33/drivers/serial/Kconfig -@@ -688,6 +688,27 @@ config SERIAL_SA1100_CONSOLE - your boot loader (lilo or loadlin) about how to pass options to the - kernel at boot time.) - -+config SERIAL_MAX3110 -+ tristate "SPI UART driver for Max3110" -+ depends on SPI_MRST -+ select SERIAL_CORE -+ select SERIAL_CORE_CONSOLE -+ help -+ This is the UART protocol driver for MAX3110 device on -+ Intel Moorestown platform -+ -+config MRST_MAX3110 -+ boolean "Add Max3110 support for Moorestown platform" -+ default y -+ depends on SERIAL_MAX3110 -+ -+config MRST_MAX3110_IRQ -+ boolean "Enable GPIO IRQ for Max3110 over Moorestown" -+ default n -+ depends on MRST_MAX3110 && GPIO_LANGWELL -+ help -+ This has to be enabled after Moorestown GPIO driver is loaded -+ - config SERIAL_BFIN - tristate "Blackfin serial port support" - depends on BLACKFIN -Index: linux-2.6.33/drivers/serial/Makefile -=================================================================== ---- linux-2.6.33.orig/drivers/serial/Makefile -+++ linux-2.6.33/drivers/serial/Makefile -@@ -82,3 +82,4 @@ obj-$(CONFIG_KGDB_SERIAL_CONSOLE) += kgd - obj-$(CONFIG_SERIAL_QE) += ucc_uart.o - obj-$(CONFIG_SERIAL_TIMBERDALE) += timbuart.o - obj-$(CONFIG_SERIAL_GRLIB_GAISLER_APBUART) += apbuart.o -+obj-$(CONFIG_SERIAL_MAX3110) += max3110.o -Index: linux-2.6.33/drivers/serial/max3110.c -=================================================================== ---- /dev/null -+++ linux-2.6.33/drivers/serial/max3110.c -@@ -0,0 +1,850 @@ -+/* -+ * max3110.c - spi uart protocol driver for Maxim 3110 on Moorestown -+ * -+ * Copyright (C) Intel 2008 Feng Tang <feng.tang@intel.com> -+ * -+ * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -+ * -+ * This program is free software; you can redistribute it and/or modify -+ * it under the terms of the GNU General Public License as published by -+ * the Free Software Foundation; either version 2 of the License, or -+ * (at your option) any later version. -+ * -+ * This program is distributed in the hope that it will be useful, -+ * but WITHOUT ANY WARRANTY; without even the implied warranty of -+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -+ * GNU General Public License for more details. -+ * -+ * You should have received a copy of the GNU General Public License -+ * along with this program; if not, write to the Free Software -+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA -+ * -+ * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -+ * -+ */ -+ -+/* -+ * Note: -+ * 1. From Max3110 spec, the Rx FIFO has 8 words, while the Tx FIFO only has -+ * 1 word. If SPI master controller doesn't support sclk frequency change, -+ * then the char need be sent out one by one with some delay -+ * -+ * 2. Currently only RX availabe interrrupt is used, no need for waiting TXE -+ * interrupt for a low speed UART device -+ */ -+ -+#include <linux/module.h> -+#include <linux/ioport.h> -+#include <linux/init.h> -+#include <linux/console.h> -+#include <linux/sysrq.h> -+#include <linux/platform_device.h> -+#include <linux/tty.h> -+#include <linux/tty_flip.h> -+#include <linux/serial_core.h> -+#include <linux/serial_reg.h> -+ -+#include <linux/kthread.h> -+#include <linux/delay.h> -+#include <asm/atomic.h> -+#include <linux/spi/spi.h> -+#include <linux/spi/mrst_spi.h> -+ -+#include "max3110.h" -+ -+#define PR_FMT "max3110: " -+ -+struct uart_max3110 { -+ struct uart_port port; -+ struct spi_device *spi; -+ char *name; -+ -+ wait_queue_head_t wq; -+ struct task_struct *main_thread; -+ struct task_struct *read_thread; -+ int mthread_up; -+ spinlock_t lock; -+ -+ u32 baud; -+ u16 cur_conf; -+ u8 clock; -+ u8 parity, word_7bits; -+ -+ atomic_t uart_tx_need; -+ -+ /* console related */ -+ struct circ_buf con_xmit; -+ atomic_t con_tx_need; -+ -+ /* irq related */ -+ u16 irq; -+ atomic_t irq_pending; -+}; -+ -+/* global data structure, may need be removed */ -+struct uart_max3110 *pmax; -+static inline void receive_char(struct uart_max3110 *max, u8 ch); -+static void receive_chars(struct uart_max3110 *max, -+ unsigned char *str, int len); -+static int max3110_read_multi(struct uart_max3110 *max, int len, u8 *buf); -+static void max3110_console_receive(struct uart_max3110 *max); -+ -+int max3110_write_then_read(struct uart_max3110 *max, -+ const u8 *txbuf, u8 *rxbuf, unsigned len, int always_fast) -+{ -+ struct spi_device *spi = max->spi; -+ struct spi_message message; -+ struct spi_transfer x; -+ int ret; -+ -+ if (!txbuf || !rxbuf) -+ return -EINVAL; -+ -+ spi_message_init(&message); -+ memset(&x, 0, sizeof x); -+ x.len = len; -+ x.tx_buf = txbuf; -+ x.rx_buf = rxbuf; -+ spi_message_add_tail(&x, &message); -+ -+ if (always_fast) -+ x.speed_hz = 3125000; -+ else if (max->baud) -+ x.speed_hz = max->baud; -+ -+ /* Do the i/o */ -+ ret = spi_sync(spi, &message); -+ return ret; -+} -+ -+/* Write a u16 to the device, and return one u16 read back */ -+int max3110_out(struct uart_max3110 *max, const u16 out) -+{ -+ u16 tmp; -+ int ret; -+ -+ ret = max3110_write_then_read(max, (u8 *)&out, (u8 *)&tmp, 2, 1); -+ if (ret) -+ return ret; -+ -+ /* If some valid data is read back */ -+ if (tmp & MAX3110_READ_DATA_AVAILABLE) -+ receive_char(max, (tmp & 0xff)); -+ -+ return ret; -+} -+ -+#define MAX_READ_LEN 20 -+/* -+ * This is usually used to read data from SPIC RX FIFO, which doesn't -+ * need any delay like flushing character out. It returns how many -+ * valide bytes are read back -+ */ -+static int max3110_read_multi(struct uart_max3110 *max, int len, u8 *buf) -+{ -+ u16 out[MAX_READ_LEN], in[MAX_READ_LEN]; -+ u8 *pbuf, valid_str[MAX_READ_LEN]; -+ int i, j, bytelen; -+ -+ if (len > MAX_READ_LEN) { -+ pr_err(PR_FMT "read len %d is too large\n", len); -+ return 0; -+ } -+ -+ bytelen = len * 2; -+ memset(out, 0, bytelen); -+ memset(in, 0, bytelen); -+ -+ if (max3110_write_then_read(max, (u8 *)out, (u8 *)in, bytelen, 1)) -+ return 0; -+ -+ /* If caller don't provide a buffer, then handle received char */ -+ pbuf = buf ? buf : valid_str; -+ -+ for (i = 0, j = 0; i < len; i++) { -+ if (in[i] & MAX3110_READ_DATA_AVAILABLE) -+ pbuf[j++] = (u8)(in[i] & 0xff); -+ } -+ -+ if (j && (pbuf == valid_str)) -+ receive_chars(max, valid_str, j); -+ -+ return j; -+} -+ -+static void serial_m3110_con_putchar(struct uart_port *port, int ch) -+{ -+ struct uart_max3110 *max = -+ container_of(port, struct uart_max3110, port); -+ struct circ_buf *xmit = &max->con_xmit; -+ -+ if (uart_circ_chars_free(xmit)) { -+ xmit->buf[xmit->head] = (char)ch; -+ xmit->head = (xmit->head + 1) & (PAGE_SIZE - 1); -+ } -+ -+ if (!atomic_read(&max->con_tx_need)) { -+ atomic_set(&max->con_tx_need, 1); -+ wake_up_process(max->main_thread); -+ } -+} -+ -+/* -+ * Print a string to the serial port trying not to disturb -+ * any possible real use of the port... -+ * -+ * The console_lock must be held when we get here. -+ */ -+static void serial_m3110_con_write(struct console *co, -+ const char *s, unsigned int count) -+{ -+ if (!pmax) -+ return; -+ -+ uart_console_write(&pmax->port, s, count, serial_m3110_con_putchar); -+} -+ -+static int __init -+serial_m3110_con_setup(struct console *co, char *options) -+{ -+ struct uart_max3110 *max = pmax; -+ int baud = 115200; -+ int bits = 8; -+ int parity = 'n'; -+ int flow = 'n'; -+ -+ pr_info(PR_FMT "setting up console\n"); -+ -+ if (!max) { -+ pr_err(PR_FMT "pmax is NULL, return"); -+ return -ENODEV; -+ } -+ -+ if (options) -+ uart_parse_options(options, &baud, &parity, &bits, &flow); -+ -+ return uart_set_options(&max->port, co, baud, parity, bits, flow); -+} -+ -+static struct tty_driver *serial_m3110_con_device(struct console *co, -+ int *index) -+{ -+ struct uart_driver *p = co->data; -+ *index = co->index; -+ return p->tty_driver; -+} -+ -+static struct uart_driver serial_m3110_reg; -+static struct console serial_m3110_console = { -+ .name = "ttyS", -+ .write = serial_m3110_con_write, -+ .device = serial_m3110_con_device, -+ .setup = serial_m3110_con_setup, -+ .flags = CON_PRINTBUFFER, -+ .index = -1, -+ .data = &serial_m3110_reg, -+}; -+ -+#define MRST_CONSOLE (&serial_m3110_console) -+ -+static unsigned int serial_m3110_tx_empty(struct uart_port *port) -+{ -+ return 1; -+} -+ -+static void serial_m3110_stop_tx(struct uart_port *port) -+{ -+ return; -+} -+ -+/* stop_rx will be called in spin_lock env */ -+static void serial_m3110_stop_rx(struct uart_port *port) -+{ -+ return; -+} -+ -+#define WORDS_PER_XFER 128 -+static inline void send_circ_buf(struct uart_max3110 *max, -+ struct circ_buf *xmit) -+{ -+ int len, left = 0; -+ u16 obuf[WORDS_PER_XFER], ibuf[WORDS_PER_XFER]; -+ u8 valid_str[WORDS_PER_XFER]; -+ int i, j; -+ -+ while (!uart_circ_empty(xmit)) { -+ left = uart_circ_chars_pending(xmit); -+ while (left) { -+ len = (left >= WORDS_PER_XFER) ? WORDS_PER_XFER : left; -+ -+ memset(obuf, 0, len * 2); -+ memset(ibuf, 0, len * 2); -+ for (i = 0; i < len; i++) { -+ obuf[i] = (u8)xmit->buf[xmit->tail] | WD_TAG; -+ xmit->tail = (xmit->tail + 1) & -+ (UART_XMIT_SIZE - 1); -+ } -+ max3110_write_then_read(max, (u8 *)obuf, -+ (u8 *)ibuf, len * 2, 0); -+ -+ for (i = 0, j = 0; i < len; i++) { -+ if (ibuf[i] & MAX3110_READ_DATA_AVAILABLE) -+ valid_str[j++] = (u8)(ibuf[i] & 0xff); -+ } -+ -+ if (j) -+ receive_chars(max, valid_str, j); -+ -+ max->port.icount.tx += len; -+ left -= len; -+ } -+ } -+} -+ -+static void transmit_char(struct uart_max3110 *max) -+{ -+ struct uart_port *port = &max->port; -+ struct circ_buf *xmit = &port->state->xmit; -+ -+ if (uart_circ_empty(xmit) || uart_tx_stopped(port)) -+ return; -+ -+ send_circ_buf(max, xmit); -+ -+ if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS) -+ uart_write_wakeup(port); -+ -+ if (uart_circ_empty(xmit)) -+ serial_m3110_stop_tx(port); -+} -+ -+/* This will be called by uart_write() and tty_write, can't -+ * go to sleep */ -+static void serial_m3110_start_tx(struct uart_port *port) -+{ -+ struct uart_max3110 *max = -+ container_of(port, struct uart_max3110, port); -+ -+ if (!atomic_read(&max->uart_tx_need)) { -+ atomic_set(&max->uart_tx_need, 1); -+ wake_up_process(max->main_thread); -+ } -+} -+ -+static void receive_chars(struct uart_max3110 *max, unsigned char *str, int len) -+{ -+ struct uart_port *port = &max->port; -+ struct tty_struct *tty; -+ int usable; -+ -+ /* If uart is not opened, just return */ -+ if (!port->state) -+ return; -+ -+ tty = port->state->port.tty; -+ if (!tty) -+ return; /* receive some char before the tty is opened */ -+ -+ while (len) { -+ usable = tty_buffer_request_room(tty, len); -+ if (usable) { -+ tty_insert_flip_string(tty, str, usable); -+ str += usable; -+ port->icount.rx += usable; -+ tty_flip_buffer_push(tty); -+ } -+ len -= usable; -+ } -+} -+ -+static inline void receive_char(struct uart_max3110 *max, u8 ch) -+{ -+ receive_chars(max, &ch, 1); -+} -+ -+static void max3110_console_receive(struct uart_max3110 *max) -+{ -+ int loop = 1, num, total = 0; -+ u8 recv_buf[512], *pbuf; -+ -+ pbuf = recv_buf; -+ do { -+ num = max3110_read_multi(max, 8, pbuf); -+ -+ if (num) { -+ loop = 10; -+ pbuf += num; -+ total += num; -+ -+ if (total >= 500) { -+ receive_chars(max, recv_buf, total); -+ pbuf = recv_buf; -+ total = 0; -+ } -+ } -+ } while (--loop); -+ -+ if (total) -+ receive_chars(max, recv_buf, total); -+} -+ -+static int max3110_main_thread(void *_max) -+{ -+ struct uart_max3110 *max = _max; -+ wait_queue_head_t *wq = &max->wq; -+ int ret = 0; -+ struct circ_buf *xmit = &max->con_xmit; -+ -+ init_waitqueue_head(wq); -+ pr_info(PR_FMT "start main thread\n"); -+ -+ do { -+ wait_event_interruptible(*wq, (atomic_read(&max->irq_pending) || -+ atomic_read(&max->con_tx_need) || -+ atomic_read(&max->uart_tx_need)) || -+ kthread_should_stop()); -+ max->mthread_up = 1; -+ -+#ifdef CONFIG_MRST_MAX3110_IRQ -+ if (atomic_read(&max->irq_pending)) { -+ max3110_console_receive(max); -+ atomic_set(&max->irq_pending, 0); -+ } -+#endif -+ -+ /* first handle console output */ -+ if (atomic_read(&max->con_tx_need)) { -+ send_circ_buf(max, xmit); -+ atomic_set(&max->con_tx_need, 0); -+ } -+ -+ /* handle uart output */ -+ if (atomic_read(&max->uart_tx_need)) { -+ transmit_char(max); -+ atomic_set(&max->uart_tx_need, 0); -+ } -+ max->mthread_up = 0; -+ } while (!kthread_should_stop()); -+ -+ return ret; -+} -+ -+#ifdef CONFIG_MRST_MAX3110_IRQ -+irqreturn_t static serial_m3110_irq(int irq, void *dev_id) -+{ -+ struct uart_max3110 *max = dev_id; -+ -+ /* max3110's irq is a falling edge, not level triggered, -+ * so no need to disable the irq */ -+ if (!atomic_read(&max->irq_pending)) { -+ atomic_inc(&max->irq_pending); -+ wake_up_process(max->main_thread); -+ } -+ return IRQ_HANDLED; -+} -+#else -+/* if don't use RX IRQ, then need a thread to polling read */ -+static int max3110_read_thread(void *_max) -+{ -+ struct uart_max3110 *max = _max; -+ -+ pr_info(PR_FMT "start read thread\n"); -+ do { -+ if (!max->mthread_up) -+ max3110_console_receive(max); -+ -+ set_current_state(TASK_INTERRUPTIBLE); -+ schedule_timeout(HZ / 20); -+ } while (!kthread_should_stop()); -+ -+ return 0; -+} -+#endif -+ -+static int serial_m3110_startup(struct uart_port *port) -+{ -+ struct uart_max3110 *max = -+ container_of(port, struct uart_max3110, port); -+ u16 config = 0; -+ int ret = 0; -+ -+ if (port->line != 0) -+ pr_err(PR_FMT "uart port startup failed\n"); -+ -+ /* firstly disable all IRQ and config it to 115200, 8n1 */ -+ config = WC_TAG | WC_FIFO_ENABLE -+ | WC_1_STOPBITS -+ | WC_8BIT_WORD -+ | WC_BAUD_DR2; -+ ret = max3110_out(max, config); -+ -+ /* as we use thread to handle tx/rx, need set low latency */ -+ port->state->port.tty->low_latency = 1; -+ -+#ifdef CONFIG_MRST_MAX3110_IRQ -+ ret = request_irq(max->irq, serial_m3110_irq, -+ IRQ_TYPE_EDGE_FALLING, "max3110", max); -+ if (ret) -+ return ret; -+ -+ /* enable RX IRQ only */ -+ config |= WC_RXA_IRQ_ENABLE; -+ max3110_out(max, config); -+#else -+ /* if IRQ is disabled, start a read thread for input data */ -+ max->read_thread = -+ kthread_run(max3110_read_thread, max, "max3110_read"); -+#endif -+ -+ max->cur_conf = config; -+ return 0; -+} -+ -+static void serial_m3110_shutdown(struct uart_port *port) -+{ -+ struct uart_max3110 *max = -+ container_of(port, struct uart_max3110, port); -+ u16 config; -+ -+ if (max->read_thread) { -+ kthread_stop(max->read_thread); -+ max->read_thread = NULL; -+ } -+ -+#ifdef CONFIG_MRST_MAX3110_IRQ -+ free_irq(max->irq, max); -+#endif -+ -+ /* Disable interrupts from this port */ -+ config = WC_TAG | WC_SW_SHDI; -+ max3110_out(max, config); -+} -+ -+static void serial_m3110_release_port(struct uart_port *port) -+{ -+} -+ -+static int serial_m3110_request_port(struct uart_port *port) -+{ -+ return 0; -+} -+ -+static void serial_m3110_config_port(struct uart_port *port, int flags) -+{ -+ /* give it fake type */ -+ port->type = PORT_PXA; -+} -+ -+static int -+serial_m3110_verify_port(struct uart_port *port, struct serial_struct *ser) -+{ -+ /* we don't want the core code to modify any port params */ -+ return -EINVAL; -+} -+ -+ -+static const char *serial_m3110_type(struct uart_port *port) -+{ -+ struct uart_max3110 *max = -+ container_of(port, struct uart_max3110, port); -+ return max->name; -+} -+ -+static void -+serial_m3110_set_termios(struct uart_port *port, struct ktermios *termios, -+ struct ktermios *old) -+{ -+ struct uart_max3110 *max = -+ container_of(port, struct uart_max3110, port); -+ unsigned char cval; -+ unsigned int baud, parity = 0; -+ int clk_div = -1; -+ u16 new_conf = max->cur_conf; -+ -+ switch (termios->c_cflag & CSIZE) { -+ case CS7: -+ cval = UART_LCR_WLEN7; -+ new_conf |= WC_7BIT_WORD; -+ break; -+ default: -+ case CS8: -+ cval = UART_LCR_WLEN8; -+ new_conf |= WC_8BIT_WORD; -+ break; -+ } -+ -+ baud = uart_get_baud_rate(port, termios, old, 0, 230400); -+ -+ /* first calc the div for 1.8MHZ clock case */ -+ switch (baud) { -+ case 300: -+ clk_div = WC_BAUD_DR384; -+ break; -+ case 600: -+ clk_div = WC_BAUD_DR192; -+ break; -+ case 1200: -+ clk_div = WC_BAUD_DR96; -+ break; -+ case 2400: -+ clk_div = WC_BAUD_DR48; -+ break; -+ case 4800: -+ clk_div = WC_BAUD_DR24; -+ break; -+ case 9600: -+ clk_div = WC_BAUD_DR12; -+ break; -+ case 19200: -+ clk_div = WC_BAUD_DR6; -+ break; -+ case 38400: -+ clk_div = WC_BAUD_DR3; -+ break; -+ case 57600: -+ clk_div = WC_BAUD_DR2; -+ break; -+ case 115200: -+ clk_div = WC_BAUD_DR1; -+ break; -+ default: -+ /* pick the previous baud rate */ -+ baud = max->baud; -+ clk_div = max->cur_conf & WC_BAUD_DIV_MASK; -+ tty_termios_encode_baud_rate(termios, baud, baud); -+ } -+ -+ if (max->clock & MAX3110_HIGH_CLK) { -+ clk_div += 1; -+ /* high clk version max3110 doesn't support B300 */ -+ if (baud == 300) -+ baud = 600; -+ if (baud == 230400) -+ clk_div = WC_BAUD_DR1; -+ tty_termios_encode_baud_rate(termios, baud, baud); -+ } -+ -+ new_conf = (new_conf & ~WC_BAUD_DIV_MASK) | clk_div; -+ if (termios->c_cflag & CSTOPB) -+ new_conf |= WC_2_STOPBITS; -+ else -+ new_conf &= ~WC_2_STOPBITS; -+ -+ if (termios->c_cflag & PARENB) { -+ new_conf |= WC_PARITY_ENABLE; -+ parity |= UART_LCR_PARITY; -+ } else -+ new_conf &= ~WC_PARITY_ENABLE; -+ -+ if (!(termios->c_cflag & PARODD)) -+ parity |= UART_LCR_EPAR; -+ max->parity = parity; -+ -+ uart_update_timeout(port, termios->c_cflag, baud); -+ -+ new_conf |= WC_TAG; -+ if (new_conf != max->cur_conf) { -+ max3110_out(max, new_conf); -+ max->cur_conf = new_conf; -+ max->baud = baud; -+ } -+} -+ -+/* don't handle hw handshaking */ -+static unsigned int serial_m3110_get_mctrl(struct uart_port *port) -+{ -+ return TIOCM_DSR | TIOCM_CAR | TIOCM_DSR; -+} -+ -+static void serial_m3110_set_mctrl(struct uart_port *port, unsigned int mctrl) -+{ -+} -+ -+static void serial_m3110_break_ctl(struct uart_port *port, int break_state) -+{ -+} -+ -+static void serial_m3110_pm(struct uart_port *port, unsigned int state, -+ unsigned int oldstate) -+{ -+} -+ -+static void serial_m3110_enable_ms(struct uart_port *port) -+{ -+} -+ -+struct uart_ops serial_m3110_ops = { -+ .tx_empty = serial_m3110_tx_empty, -+ .set_mctrl = serial_m3110_set_mctrl, -+ .get_mctrl = serial_m3110_get_mctrl, -+ .stop_tx = serial_m3110_stop_tx, -+ .start_tx = serial_m3110_start_tx, -+ .stop_rx = serial_m3110_stop_rx, -+ .enable_ms = serial_m3110_enable_ms, -+ .break_ctl = serial_m3110_break_ctl, -+ .startup = serial_m3110_startup, -+ .shutdown = serial_m3110_shutdown, -+ .set_termios = serial_m3110_set_termios, /* must have */ -+ .pm = serial_m3110_pm, -+ .type = serial_m3110_type, -+ .release_port = serial_m3110_release_port, -+ .request_port = serial_m3110_request_port, -+ .config_port = serial_m3110_config_port, -+ .verify_port = serial_m3110_verify_port, -+}; -+ -+static struct uart_driver serial_m3110_reg = { -+ .owner = THIS_MODULE, -+ .driver_name = "MRST serial", -+ .dev_name = "ttyS", -+ .major = TTY_MAJOR, -+ .minor = 64, -+ .nr = 1, -+ .cons = MRST_CONSOLE, -+}; -+ -+static int serial_m3110_suspend(struct spi_device *spi, pm_message_t state) -+{ -+ return 0; -+} -+ -+static int serial_m3110_resume(struct spi_device *spi) -+{ -+ return 0; -+} -+ -+#ifdef CONFIG_MRST_MAX3110 -+static struct mrst_spi_chip spi0_uart = { -+ .poll_mode = 1, -+ .enable_dma = 0, -+ .type = SPI_FRF_SPI, -+}; -+#endif -+ -+static int serial_m3110_probe(struct spi_device *spi) -+{ -+ struct uart_max3110 *max; -+ int ret; -+ unsigned char *buffer; -+ -+ max = kzalloc(sizeof(*max), GFP_KERNEL); -+ if (!max) -+ return -ENOMEM; -+ -+ /* set spi info */ -+ spi->mode = SPI_MODE_0; -+ spi->bits_per_word = 16; -+#ifdef CONFIG_MRST_MAX3110 -+ max->clock = MAX3110_HIGH_CLK; -+ spi->controller_data = &spi0_uart; -+#endif -+ spi_setup(spi); -+ -+ max->port.type = PORT_PXA; /* need apply for a max3110 type */ -+ max->port.fifosize = 2; /* only have 16b buffer */ -+ max->port.ops = &serial_m3110_ops; -+ max->port.line = 0; -+ max->port.dev = &spi->dev; -+ max->port.uartclk = 115200; -+ -+ max->spi = spi; -+ max->name = spi->modalias; /* use spi name as the name */ -+ max->irq = (u16)spi->irq; -+ -+ spin_lock_init(&max->lock); -+ -+ max->word_7bits = 0; -+ max->parity = 0; -+ max->baud = 0; -+ -+ max->cur_conf = 0; -+ atomic_set(&max->irq_pending, 0); -+ -+ buffer = (unsigned char *)__get_free_page(GFP_KERNEL); -+ if (!buffer) { -+ ret = -ENOMEM; -+ goto err_get_page; -+ } -+ max->con_xmit.buf = (unsigned char *)buffer; -+ max->con_xmit.head = max->con_xmit.tail = 0; -+ -+ max->main_thread = kthread_run(max3110_main_thread, -+ max, "max3110_main"); -+ if (IS_ERR(max->main_thread)) { -+ ret = PTR_ERR(max->main_thread); -+ goto err_kthread; -+ } -+ -+ pmax = max; -+ /* give membase a psudo value to pass serial_core's check */ -+ max->port.membase = (void *)0xff110000; -+ uart_add_one_port(&serial_m3110_reg, &max->port); -+ -+ return 0; -+ -+err_kthread: -+ free_page((unsigned long)buffer); -+err_get_page: -+ pmax = NULL; -+ kfree(max); -+ return ret; -+} -+ -+static int max3110_remove(struct spi_device *dev) -+{ -+ struct uart_max3110 *max = pmax; -+ -+ if (!pmax) -+ return 0; -+ -+ pmax = NULL; -+ uart_remove_one_port(&serial_m3110_reg, &max->port); -+ -+ free_page((unsigned long)max->con_xmit.buf); -+ -+ if (max->main_thread) -+ kthread_stop(max->main_thread); -+ -+ kfree(max); -+ return 0; -+} -+ -+static struct spi_driver uart_max3110_driver = { -+ .driver = { -+ .name = "spi_max3111", -+ .bus = &spi_bus_type, -+ .owner = THIS_MODULE, -+ }, -+ .probe = serial_m3110_probe, -+ .remove = __devexit_p(max3110_remove), -+ .suspend = serial_m3110_suspend, -+ .resume = serial_m3110_resume, -+}; -+ -+ -+int __init serial_m3110_init(void) -+{ -+ int ret = 0; -+ -+ ret = uart_register_driver(&serial_m3110_reg); -+ if (ret) -+ return ret; -+ -+ ret = spi_register_driver(&uart_max3110_driver); -+ if (ret) -+ uart_unregister_driver(&serial_m3110_reg); -+ -+ return ret; -+} -+ -+void __exit serial_m3110_exit(void) -+{ -+ spi_unregister_driver(&uart_max3110_driver); -+ uart_unregister_driver(&serial_m3110_reg); -+} -+ -+module_init(serial_m3110_init); -+module_exit(serial_m3110_exit); -+ -+MODULE_LICENSE("GPL"); -+MODULE_ALIAS("max3110-uart"); -Index: linux-2.6.33/drivers/serial/max3110.h -=================================================================== ---- /dev/null -+++ linux-2.6.33/drivers/serial/max3110.h -@@ -0,0 +1,59 @@ -+#ifndef _MAX3110_HEAD_FILE_ -+#define _MAX3110_HEAD_FILE_ -+ -+#define MAX3110_HIGH_CLK 0x1 /* 3.6864 MHZ */ -+#define MAX3110_LOW_CLK 0x0 /* 1.8432 MHZ */ -+ -+/* status bits for all 4 MAX3110 operate modes */ -+#define MAX3110_READ_DATA_AVAILABLE (1 << 15) -+#define MAX3110_WRITE_BUF_EMPTY (1 << 14) -+ -+#define WC_TAG (3 << 14) -+#define RC_TAG (1 << 14) -+#define WD_TAG (2 << 14) -+#define RD_TAG (0 << 14) -+ -+/* bits def for write configuration */ -+#define WC_FIFO_ENABLE_MASK (1 << 13) -+#define WC_FIFO_ENABLE (0 << 13) -+ -+#define WC_SW_SHDI (1 << 12) -+ -+#define WC_IRQ_MASK (0xF << 8) -+#define WC_TXE_IRQ_ENABLE (1 << 11) /* TX empty irq */ -+#define WC_RXA_IRQ_ENABLE (1 << 10) /* RX availabe irq */ -+#define WC_PAR_HIGH_IRQ_ENABLE (1 << 9) -+#define WC_REC_ACT_IRQ_ENABLE (1 << 8) -+ -+#define WC_IRDA_ENABLE (1 << 7) -+ -+#define WC_STOPBITS_MASK (1 << 6) -+#define WC_2_STOPBITS (1 << 6) -+#define WC_1_STOPBITS (0 << 6) -+ -+#define WC_PARITY_ENABLE_MASK (1 << 5) -+#define WC_PARITY_ENABLE (1 << 5) -+ -+#define WC_WORDLEN_MASK (1 << 4) -+#define WC_7BIT_WORD (1 << 4) -+#define WC_8BIT_WORD (0 << 4) -+ -+#define WC_BAUD_DIV_MASK (0xF) -+#define WC_BAUD_DR1 (0x0) -+#define WC_BAUD_DR2 (0x1) -+#define WC_BAUD_DR4 (0x2) -+#define WC_BAUD_DR8 (0x3) -+#define WC_BAUD_DR16 (0x4) -+#define WC_BAUD_DR32 (0x5) -+#define WC_BAUD_DR64 (0x6) -+#define WC_BAUD_DR128 (0x7) -+#define WC_BAUD_DR3 (0x8) -+#define WC_BAUD_DR6 (0x9) -+#define WC_BAUD_DR12 (0xA) -+#define WC_BAUD_DR24 (0xB) -+#define WC_BAUD_DR48 (0xC) -+#define WC_BAUD_DR96 (0xD) -+#define WC_BAUD_DR192 (0xE) -+#define WC_BAUD_DR384 (0xF) -+ -+#endif -Index: linux-2.6.33/arch/x86/Kconfig.debug -=================================================================== ---- linux-2.6.33.orig/arch/x86/Kconfig.debug -+++ linux-2.6.33/arch/x86/Kconfig.debug -@@ -43,6 +43,10 @@ config EARLY_PRINTK - with klogd/syslogd or the X server. You should normally N here, - unless you want to debug such a crash. - -+config X86_MRST_EARLY_PRINTK -+ bool "Early printk for MRST platform support" -+ depends on EARLY_PRINTK && X86_MRST -+ - config EARLY_PRINTK_DBGP - bool "Early printk via EHCI debug port" - default n -Index: linux-2.6.33/arch/x86/kernel/early_printk.c -=================================================================== ---- linux-2.6.33.orig/arch/x86/kernel/early_printk.c -+++ linux-2.6.33/arch/x86/kernel/early_printk.c -@@ -14,6 +14,7 @@ - #include <xen/hvc-console.h> - #include <asm/pci-direct.h> - #include <asm/fixmap.h> -+#include <linux/spi/mrst_spi.h> - #include <asm/pgtable.h> - #include <linux/usb/ehci_def.h> - -@@ -231,6 +232,10 @@ static int __init setup_early_printk(cha - if (!strncmp(buf, "xen", 3)) - early_console_register(&xenboot_console, keep); - #endif -+#ifdef CONFIG_X86_MRST_EARLY_PRINTK -+ if (!strncmp(buf, "mrst", 4)) -+ early_console_register(&early_mrst_console, keep); -+#endif - buf++; - } - return 0; -Index: linux-2.6.33/arch/x86/kernel/mrst_earlyprintk.c -=================================================================== ---- /dev/null -+++ linux-2.6.33/arch/x86/kernel/mrst_earlyprintk.c -@@ -0,0 +1,177 @@ -+/* -+ * mrst_earlyprintk.c - spi-uart early printk for Intel Moorestown platform -+ * -+ * Copyright (c) 2008 Intel Corporation -+ * Author: Feng Tang(feng.tang@intel.com) -+ * -+ * This program is free software; you can redistribute it and/or -+ * modify it under the terms of the GNU General Public License -+ * as published by the Free Software Foundation; version 2 -+ * of the License. -+ */ -+ -+#include <linux/console.h> -+#include <linux/kernel.h> -+#include <linux/init.h> -+#include <linux/spi/mrst_spi.h> -+ -+#include <asm/fixmap.h> -+#include <asm/pgtable.h> -+ -+#define MRST_SPI_TIMEOUT 0x200000 -+#define MRST_REGBASE_SPI0 0xff128000 -+#define MRST_CLK_SPI0_REG 0xff11d86c -+ -+/* use SPI0 register for MRST x86 core */ -+static unsigned long mrst_spi_paddr = MRST_REGBASE_SPI0; -+ -+/* always contains a accessable address, start with 0 */ -+static void *pspi; -+static u32 *pclk_spi0; -+static int mrst_spi_inited; -+ -+/* -+ * One trick for the early printk is that it could be called -+ * before and after the real page table is enabled for kernel, -+ * so the PHY IO registers should be mapped twice. And a flag -+ * "real_pgt_is_up" is used as an indicator -+ */ -+static int real_pgt_is_up; -+ -+static void early_mrst_spi_init(void) -+{ -+ u32 ctrlr0 = 0; -+ u32 spi0_cdiv; -+ static u32 freq; /* freq info only need be searched once */ -+ -+ if (pspi && mrst_spi_inited) -+ return; -+ -+ if (!freq) { -+ set_fixmap_nocache(FIX_EARLYCON_MEM_BASE, MRST_CLK_SPI0_REG); -+ pclk_spi0 = (void *)(__fix_to_virt(FIX_EARLYCON_MEM_BASE) + -+ (MRST_CLK_SPI0_REG & (PAGE_SIZE - 1))); -+ -+ spi0_cdiv = ((*pclk_spi0) & 0xe00) >> 9; -+ freq = 100000000 / (spi0_cdiv + 1); -+ } -+ -+ set_fixmap_nocache(FIX_EARLYCON_MEM_BASE, mrst_spi_paddr); -+ pspi = (void *)(__fix_to_virt(FIX_EARLYCON_MEM_BASE) + -+ (mrst_spi_paddr & (PAGE_SIZE - 1))); -+ -+ /* disable SPI controller */ -+ write_ssienr(0x0, pspi); -+ -+ /* set control param, 8 bits, transmit only mode */ -+ ctrlr0 = read_ctrl0(pspi); -+ -+ ctrlr0 &= 0xfcc0; -+ ctrlr0 |= 0xf | (SPI_FRF_SPI << SPI_FRF_OFFSET) -+ | (SPI_TMOD_TO << SPI_TMOD_OFFSET); -+ write_ctrl0(ctrlr0, pspi); -+ -+ /* change the spi0 clk to comply with 115200 bps */ -+ write_baudr(freq/115200, pspi); -+ -+ /* disable all INT for early phase */ -+ write_imr(0x0, pspi); -+ -+ /* set the cs to max3110 */ -+ write_ser(0x2, pspi); -+ -+ /* enable the HW, the last step for HW init */ -+ write_ssienr(0x1, pspi); -+ -+ mrst_spi_inited = 1; -+} -+ -+/* set the ratio rate, INT */ -+static void max3110_write_config(void) -+{ -+ u16 config; -+ -+ /* 115200, TM not set, no parity, 8bit word */ -+ config = 0xc001; -+ write_dr(config, pspi); -+} -+ -+/* transfer char to a eligibal word and send to max3110 */ -+static void max3110_write_data(char c) -+{ -+ u16 data; -+ -+ data = 0x8000 | c; -+ write_dr(data, pspi); -+} -+ -+/* slave select should be called in the read/write function */ -+static int early_mrst_spi_putc(char c) -+{ -+ unsigned int timeout; -+ u32 sr; -+ -+ timeout = MRST_SPI_TIMEOUT; -+ /* early putc need make sure the TX FIFO is not full*/ -+ while (timeout--) { -+ sr = read_sr(pspi); -+ if (!(sr & SR_TF_NOT_FULL)) -+ cpu_relax(); -+ else -+ break; -+ } -+ -+ if (timeout == 0xffffffff) { -+ printk(KERN_INFO "SPI: waiting timeout \n"); -+ return -1; -+ } -+ -+ max3110_write_data(c); -+ return 0; -+} -+ -+/* early SPI only use polling mode */ -+static void early_mrst_spi_write(struct console *con, -+ const char *str, unsigned n) -+{ -+ int i; -+ -+ if ((read_cr3() == __pa(swapper_pg_dir)) && !real_pgt_is_up) { -+ mrst_spi_inited = 0; -+ real_pgt_is_up = 1; -+ } -+ -+ if (!mrst_spi_inited) { -+ early_mrst_spi_init(); -+ max3110_write_config(); -+ } -+ -+ for (i = 0; i < n && *str; i++) { -+ if (*str == '\n') -+ early_mrst_spi_putc('\r'); -+ early_mrst_spi_putc(*str); -+ -+ str++; -+ } -+} -+ -+struct console early_mrst_console = { -+ .name = "earlymrst", -+ .write = early_mrst_spi_write, -+ .flags = CON_PRINTBUFFER, -+ .index = -1, -+}; -+ -+/* a debug function */ -+void mrst_early_printk(const char *fmt, ...) -+{ -+ char buf[512]; -+ int n; -+ va_list ap; -+ -+ va_start(ap, fmt); -+ n = vscnprintf(buf, 512, fmt, ap); -+ va_end(ap); -+ -+ early_mrst_console.write(&early_mrst_console, buf, n); -+} -Index: linux-2.6.33/arch/x86/include/asm/ipc_defs.h -=================================================================== ---- /dev/null -+++ linux-2.6.33/arch/x86/include/asm/ipc_defs.h -@@ -0,0 +1,217 @@ -+/* -+*ipc_defs.h - Header file defining data types and functions for ipc driver. -+* -+*Copyright (C) 2008 Intel Corp -+*Copyright (C) 2008 Sreenidhi Gurudatt <sreenidhi.b.gurudatt@intel.com> -+*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -+* -+*This program is free software; you can redistribute it and/or modify -+*it under the terms of the GNU General Public License as published by -+*the Free Software Foundation; version 2 of the License. -+* -+*This program is distributed in the hope that it will be useful, but -+*WITHOUT ANY WARRANTY; without even the implied warranty of -+*MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU -+*General Public License for more details. -+ * -+*You should have received a copy of the GNU General Public License along -+*with this program; if not, write to the Free Software Foundation, Inc., -+*59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. -+* -+*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -+* -+*This driver implements core IPC kernel functions to read/write and execute -+*various commands supported by System controller firmware for Moorestown -+*platform. -+*/ -+ -+#ifndef __IPC_DEFS_H__ -+#define __IPC_DEFS_H__ -+ -+#include <linux/init.h> -+#include <linux/module.h> -+ -+#define E_INVALID_CMD -249 -+#define E_READ_USER_CMD -250 -+#define E_READ_USER_DATA -251 -+#define E_WRITE_USER_DATA -252 -+#define E_PMIC_MALLOC -253 -+ -+#define MAX_PMICREGS 5 -+#define MAX_PMIC_MOD_REGS 4 -+ -+#ifndef FALSE -+#define FALSE 0 -+#define TRUE 1 -+#endif -+#define SUCCESS 0 -+ -+/* -+ * List of commands sent by calling host -+ * drivers to IPC_Driver -+*/ -+ -+/* CCA battery driver specific commands. -+ * Thise commands are shared across IPC driver -+ * and calling host driver -+ */ -+ -+#define IPC_WATCHDOG 0xA0 -+#define IPC_PROGRAM_BUS_MASTER 0xA1 -+#define DEVICE_FW_UPGRADE 0xA2 -+#define GET_FW_VERSION 0xA3 -+ -+#define IPC_BATT_CCA_READ 0xB0 -+#define IPC_BATT_CCA_WRITE 0xB1 -+#define IPC_BATT_GET_PROP 0xB2 -+ -+#define IPC_PMIC_REGISTER_READ_NON_BLOCKING 0xEB -+#define IPC_READ32 0xEC -+#define IPC_WRITE32 0xED -+#define IPC_LPE_READ 0xEE -+#define IPC_LPE_WRITE 0xEF -+#define IPC_SEND_COMMAND 0xFA -+#define IPC_PMIC_REGISTER_READ 0xFB -+#define IPC_PMIC_REGISTER_READ_MODIFY 0xFC -+#define IPC_PMIC_REGISTER_WRITE 0xFD -+#define IPC_CHECK_STATUS 0xFE -+#define GET_SCU_FIRMWARE_VERSION 0xFF -+ -+#define MAX_PMICREGS 5 -+#define MAX_PMIC_MOD_REGS 4 -+ -+/* Adding the error code*/ -+#define E_INVALID_PARAM -0xA0 -+#define E_NUM_ENTRIES_OUT_OF_RANGE -0xA1 -+#define E_CMD_FAILED -0xA2 -+#define E_NO_INTERRUPT_ON_IOC -0xA3 -+#define E_QUEUE_IS_FULL -0xA4 -+ -+/* VRTC IPC CMD ID and sub id */ -+#define IPC_VRTC_CMD 0xFA -+#define IPC_VRTC_SET_TIME 0x01 -+#define IPC_VRTC_SET_ALARM 0x02 -+ -+struct ipc_cmd_val { -+ /* -+ *More fields to be added for -+ *future enhancements -+ */ -+ u32 ipc_cmd_data; -+}; -+ -+struct ipc_cmd_type { -+ u8 cmd; -+ u32 data; -+ u8 value; -+ u8 ioc; -+}; -+ -+/* -+ * Structures defined for battery PMIC driver -+ * This structure is used by the following commands -+ * IPC_BATT_CCA_READ and IPC_BATT_CCA_WRITE -+ */ -+struct ipc_batt_cca_data { -+ int cca_val; -+}; -+ -+/* -+ * Structures defined for battery PMIC driver -+ * This structure is used by IPC_BATT_GET_PROP -+ */ -+struct ipc_batt_prop_data { -+ u32 batt_value1; -+ u8 batt_value2[5]; -+}; -+ -+struct ipc_reg_data { -+ u8 ioc; -+ u32 address; -+ u32 data; -+}; -+ -+struct ipc_cmd { -+ u8 cmd; -+ u32 data; -+}; -+ -+struct pmicmodreg { -+ u16 register_address; -+ u8 value; -+ u8 bit_map; -+}; -+ -+struct pmicreg { -+ u16 register_address; -+ u8 value; -+}; -+ -+struct ipc_pmic_reg_data { -+ bool ioc; -+ struct pmicreg pmic_reg_data[MAX_PMICREGS]; -+ u8 num_entries; -+}; -+ -+struct ipc_pmic_mod_reg_data { -+ bool ioc; -+ struct pmicmodreg pmic_mod_reg_data[MAX_PMIC_MOD_REGS]; -+ u8 num_entries; -+}; -+ -+/* Firmware ingredient version information. -+ * fw_data[0] = scu_rt_minor; -+ * fw_data[1] = scu_rt_major; -+ * fw_data[2] = scu_bs_minor; -+ * fw_data[3] = scu_bs_major; -+ * fw_data[4] = punit_minor; -+ * fw_data[5] = punit_major; -+ * fw_data[6] = x86_minor; -+ * fw_data[7] = x86_major; -+ * fw_data[8] = spectra_minor; -+ * fw_data[9] = spectra_major; -+ * fw_data[10] = val_hook_minor; -+ * fw_data[11] = val_hook_major; -+ * fw_data[12] = ifw_minor; -+ * fw_data[13] = ifw_major; -+ * fw_data[14] = rfu1; -+ * fw_data[15] = rfu2; -+*/ -+struct watchdog_reg_data { -+ int payload1; -+ int payload2; -+ bool ioc; -+}; -+ -+struct ipc_io_bus_master_regs { -+ u32 ctrl_reg_addr; -+ u32 ctrl_reg_data; -+}; -+ -+struct ipc_non_blocking_pmic_read{ -+ struct ipc_pmic_reg_data pmic_nb_read; -+ void *context; -+ int (*callback_host)(struct ipc_pmic_reg_data pmic_read_data, -+ void *context); -+}; -+ -+int ipc_check_status(void); -+int mrst_get_firmware_version(unsigned char *mrst_fw_ver_info); -+int ipc_config_cmd(struct ipc_cmd_type ipc_cmd, -+ u32 ipc_cmd_len, void *cmd_data); -+int ipc_pmic_register_write(struct ipc_pmic_reg_data *p_write_reg_data, -+ u8 ipc_blocking_flag); -+int ipc_pmic_register_read(struct ipc_pmic_reg_data *p_read_reg_data); -+int ipc_pmic_register_read_modify(struct ipc_pmic_mod_reg_data -+ *p_read_mod_reg_data); -+int mrst_ipc_read32(struct ipc_reg_data *p_reg_data); -+int mrst_ipc_write32(struct ipc_reg_data *p_reg_data); -+int ipc_set_watchdog(struct watchdog_reg_data *p_watchdog_data); -+int ipc_program_io_bus_master(struct ipc_io_bus_master_regs -+ *p_reg_data); -+int ipc_pmic_register_read_non_blocking(struct ipc_non_blocking_pmic_read -+ *p_nb_read); -+int ipc_device_fw_upgrade(u8 *cmd_data, u32 ipc_cmd_len); -+int lnw_ipc_single_cmd(u8 cmd_id, u8 sub_id, int size, int msi); -+ -+#endif -Index: linux-2.6.33/arch/x86/kernel/ipc_mrst.c -=================================================================== ---- /dev/null -+++ linux-2.6.33/arch/x86/kernel/ipc_mrst.c -@@ -0,0 +1,1612 @@ -+/* -+ * ipc_mrst.c: Driver for Langwell IPC1 -+ * -+ * (C) Copyright 2008 Intel Corporation -+ * Author: Sreenidhi Gurudatt (sreenidhi.b.gurudatt@intel.com) -+ * -+ * This program is free software; you can redistribute it and/or -+ * modify it under the terms of the GNU General Public License -+ * as published by the Free Software Foundation; version 2 -+ * of the License. -+ * -+ * Note: -+ * Langwell provides two IPC units to communicate with IA host. IPC1 is -+ * dedicated for IA. IPC commands results in LNW SCU interrupt. The -+ * initial implementation of this driver is platform specific. It will be -+ * converted to a PCI driver once SCU FW is in place. -+ * Log: Tested after submitting bugzilla patch - 24th December 08 -+ * Log: Implemented Error Handling features and resolved IPC driver sighting -+ * PMIC Read/Write calls now take 80 to 200usecs - March 09 09. -+ * Log: Adding the IO BUS Master programming support - March 09 09. -+ */ -+#include <linux/delay.h> -+#include <linux/errno.h> -+#include <linux/init.h> -+#include <linux/sysdev.h> -+#include <linux/pm.h> -+#include <linux/pci.h> -+#include <asm/ipc_defs.h> -+#include <linux/workqueue.h> -+#include <linux/sched.h> -+ -+#include "ipc_mrst.h" -+ -+#ifndef CONFIG_PCI -+#error "This file is PCI bus glue.CONFIG_PCI must be defined." -+#endif -+ -+/*virtual memory address for IPC base returned by IOREMAP().*/ -+void __iomem *p_ipc_base; -+void __iomem *p_i2c_ser_bus; -+void __iomem *p_dfu_fw_base; -+void __iomem *p_dfu_mailbox_base; -+static unsigned char fw_ver_data[16]; -+ -+static wait_queue_head_t wait; -+static struct semaphore sema_ipc; -+static int scu_cmd_completed = FALSE; -+static bool non_blocking_read_flag = FALSE; -+static struct ipc_work_struct ipc_wq; -+static struct ipc_non_blocking_pmic_read pmic_read_que[MAX_NB_BUF_SIZE]; -+static unsigned int cmd_id; -+static int (*callback)(struct ipc_pmic_reg_data pmic_read_data, void *context); -+static DEFINE_MUTEX(mrst_ipc_mutex); -+ -+#ifdef LNW_IPC_DEBUG -+ -+#define lnw_ipc_dbg(fmt, args...) \ -+ do { printk(fmt, ## args); } while (0) -+#else -+#define lnw_ipc_dbg(fmt, args...) do { } while (0) -+#endif -+static const char ipc_name[] = "ipc_mrst"; -+ -+unsigned long lnw_ipc_address; -+static void __iomem *lnw_ipc_virt_address; -+static unsigned short cmdid_pool = 0xffff; -+static inline int lnw_ipc_set_mapping(struct pci_dev *dev) -+{ -+ unsigned long cadr; -+ cadr = dev->resource[0].start; -+ cadr &= PCI_BASE_ADDRESS_MEM_MASK; -+ if (!cadr) { -+ printk(KERN_INFO "No PCI resource for IPC\n"); -+ return -ENODEV; -+ } -+ lnw_ipc_virt_address = ioremap_nocache(cadr, 0x1000); -+ if (lnw_ipc_virt_address != NULL) { -+ dev_info(&dev->dev, "lnw ipc base found 0x%lup: 0x%p\n", -+ cadr, lnw_ipc_virt_address); -+ return 0; -+ } -+ printk(KERN_INFO "Failed map LNW IPC1 phy address at %lu\n", cadr); -+ return -ENODEV; -+} -+ -+static inline void lnw_ipc_clear_mapping(void) -+{ -+ iounmap(lnw_ipc_virt_address); -+ lnw_ipc_virt_address = NULL; -+} -+ -+unsigned long lnw_ipc_readl(unsigned long a) -+{ -+ return readl(lnw_ipc_virt_address + a); -+} -+ -+static inline void lnw_ipc_writel(unsigned long d, unsigned long a) -+{ -+ writel(d, lnw_ipc_virt_address + a); -+} -+ -+static unsigned char lnw_ipc_assign_cmdid(void) -+{ -+ unsigned char cmdid = 0; -+ unsigned short thebit; -+ thebit = cmdid_pool&(~cmdid_pool + 1); -+ printk(KERN_INFO "pool=0x%04x thebit=0x%04x\n", -+ cmdid_pool, thebit); -+ while (thebit >> cmdid) -+ cmdid++; -+ printk(KERN_INFO "Allocate IPC cmd ID %d\n", cmdid); -+ cmdid_pool &= ~thebit; -+ return cmdid; -+} -+ -+int lnw_ipc_single_cmd(u8 cmd_id, u8 sub_id, int size, int msi) -+{ -+ unsigned long cmdreg, stsreg, retry; -+ -+ if (!lnw_ipc_virt_address) { -+ printk(KERN_ERR "No IPC mapping\n"); -+ goto err_ipccmd; -+ } -+ if (size >= 16) { -+ printk(KERN_ERR "IPC message size too big %d\n", size); -+ goto err_ipccmd; -+ } -+ -+ WARN_ON((msi != 0) && (msi != 1)); -+ -+ cmdreg = cmd_id -+ | (sub_id << 12) -+ | (size << 16) -+ | (msi << 8); -+ -+ lnw_ipc_writel(cmdreg, LNW_IPC_CMD); -+ -+ /* check status make sure the command is received by SCU */ -+ retry = 1000; -+ stsreg = lnw_ipc_readl(LNW_IPC_STS); -+ if (stsreg & LNW_IPC_STS_ERR) { -+ lnw_ipc_dbg("IPC command ID %d error\n", cmd_id); -+ goto err_ipccmd; -+ } -+ while ((stsreg & LNW_IPC_STS_BUSY) && retry) { -+ lnw_ipc_dbg("IPC command ID %d busy\n", cmd_id); -+ stsreg = lnw_ipc_readl(LNW_IPC_STS); -+ udelay(10); -+ retry--; -+ } -+ -+ if (!retry) -+ printk(KERN_ERR "IPC command ID %d failed/timeout", cmd_id); -+ else -+ lnw_ipc_dbg("IPC command ID %d completed\n", cmd_id); -+ -+ return 0; -+ -+err_ipccmd: -+ return -1; -+} -+EXPORT_SYMBOL(lnw_ipc_single_cmd); -+ -+int lnw_ipc_send_cmd(unsigned char cmd, int size, int msi) -+{ -+ unsigned long cmdreg, stsreg; -+ unsigned char cmdid, retry; -+ -+ if (!lnw_ipc_virt_address) { -+ printk(KERN_ERR "No IPC mapping\n"); -+ goto err_ipccmd; -+ } -+ if (size >= 16) { -+ printk(KERN_ERR "IPC message size too big %d\n", size); -+ goto err_ipccmd; -+ } -+ -+ cmdid = lnw_ipc_assign_cmdid(); -+ cmdreg = lnw_ipc_readl(LNW_IPC_CMD); -+ cmdreg |= cmdid << 12; -+ cmdreg |= size << 16; -+ if (msi) -+ cmdreg |= 1 << 8; -+ lnw_ipc_writel(cmdreg, LNW_IPC_CMD); -+ /* check status make sure the command is received by SCU */ -+ retry = 10; -+ stsreg = lnw_ipc_readl(LNW_IPC_STS); -+ if (stsreg&LNW_IPC_STS_ERR) { -+ lnw_ipc_dbg("IPC command ID %d error\n", cmdid); -+ goto err_ipccmd; -+ } -+ while ((stsreg&LNW_IPC_STS_BUSY) || retry) { -+ lnw_ipc_dbg("IPC command ID %d busy\n", cmdid); -+ stsreg = lnw_ipc_readl(LNW_IPC_STS); -+ udelay(10); -+ retry--; -+ } -+ if (!retry) -+ lnw_ipc_dbg("IPC command ID %d failed/timeout\n", cmdid); -+ else -+ lnw_ipc_dbg("IPC command ID %d completed\n", cmdid); -+ -+err_ipccmd: -+ return -1; -+} -+/* -+ * For IPC transfer modes except read DMA, there is no need for MSI, -+ * so the driver polls status after each IPC command is issued. -+ */ -+static irqreturn_t ipc_irq(int irq, void *dev_id) -+{ -+ union ipc_sts ipc_sts_reg; -+ -+ ipc_sts_reg.ipc_sts_data = -+ __raw_readl((p_ipc_base + IPC_STS)); -+ -+ if (!ipc_sts_reg.ipc_sts_parts.busy) { -+ /*Call on NON Blocking flag being set.*/ -+ if (non_blocking_read_flag == TRUE) { -+ schedule_work(&ipc_wq.ipc_work); -+ } else { -+ scu_cmd_completed = TRUE; -+ wake_up_interruptible(&wait); -+ } -+ } -+ return IRQ_HANDLED; -+} -+ -+static const struct ipc_driver ipc_mrst_driver = { -+ .name = "MRST IPC Controller", -+ /* -+ * generic hardware linkage -+ */ -+ .irq = ipc_irq, -+ .flags = 0, -+}; -+ -+static int ipc_mrst_pci_probe(struct pci_dev *dev, -+ const struct pci_device_id *id) -+{ -+ int err, retval, i; -+ lnw_ipc_dbg("Attempt to enable IPC irq 0x%x, pin %d\n", -+ dev->irq, dev->pin); -+ err = pci_enable_device(dev); -+ if (err) { -+ dev_err(&dev->dev, "Failed to enable MSRT IPC(%d)\n", -+ err); -+ goto exit; -+ } -+ retval = pci_request_regions(dev, "ipc_mrst"); -+ if (retval) -+ dev_err(&dev->dev, "Failed to allocate resource\ -+ for MRST IPC(%d)\n", retval); -+ -+ init_ipc_driver(); -+ -+ /* 0 means cmd ID is in use */ -+ cmdid_pool = 0xffff; -+ /* initialize mapping */ -+ retval = lnw_ipc_set_mapping(dev); -+ if (retval) -+ goto exit; -+ /* clear buffer */ -+ for (i = 0; i < LNW_IPC_RWBUF_SIZE; i = i + 4) { -+ lnw_ipc_writel(0, LNW_IPC_WBUF + i); -+ lnw_ipc_writel(0, LNW_IPC_RBUF + i); -+ } -+ retval = request_irq(dev->irq, ipc_irq, IRQF_SHARED, -+ "ipc_mrst", (void *)&ipc_mrst_driver); -+ if (retval) { -+ printk(KERN_ERR "ipc: cannot register ISR %p irq %d ret %d\n", -+ ipc_irq, dev->irq, retval); -+ return -EIO; -+ } -+exit: -+ return 0; -+} -+ -+void ipc_mrst_pci_remove(struct pci_dev *pdev) -+{ -+ pci_release_regions(pdev); -+} -+ -+/* PCI driver selection metadata; PCI hotplugging uses this */ -+static const struct pci_device_id pci_ids[] = { -+ {PCI_DEVICE(PCI_VENDOR_ID_INTEL, 0x080e)} -+}; -+ -+MODULE_DEVICE_TABLE(pci, pci_ids); -+ -+/* pci driver glue; this is a "new style" PCI driver module */ -+static struct pci_driver ipc_mrst_pci_driver = { -+ .name = (char *)ipc_name, -+ .id_table = pci_ids, -+ .probe = ipc_mrst_pci_probe, -+ .remove = ipc_mrst_pci_remove, -+}; -+ -+static int __init ipc_mrst_init(void) -+{ -+ int retval = 0; -+ lnw_ipc_dbg("%s\n", __func__); -+ retval = pci_register_driver(&ipc_mrst_pci_driver); -+ if (retval < 0) { -+ printk(KERN_CRIT "Failed to register %s\n", -+ ipc_mrst_pci_driver.name); -+ pci_unregister_driver(&ipc_mrst_pci_driver); -+ } else { -+ printk(KERN_CRIT "****Loaded %s driver version %s****\n", -+ ipc_mrst_pci_driver.name, MRST_IPC_DRIVER_VERSION); -+ cache_mrst_firmware_version(); -+ } -+ return retval; -+} -+ -+static void __exit ipc_mrst_exit(void) -+{ -+ iounmap(p_ipc_base); -+ iounmap(p_i2c_ser_bus); -+ pci_unregister_driver(&ipc_mrst_pci_driver); -+ de_init_ipc_driver(); -+} -+ -+/* -+ * Steps to read PMIC Register(Psuedocode) -+ * 1) Construct the SCU FW command structure with normal read -+ * 2) Fill the IPC_WBUF with the p_reg_data -+ * 3) write the command to(Memory Mapped address) IPC_CMD register -+ * 4) Wait for an interrupt from SCUFirmware or do a timeout. -+*/ -+int ipc_check_status(void) -+{ -+ if (down_interruptible(&sema_ipc)) { -+ printk(KERN_INFO "IPC_Driver module busy\n"); -+ return -EBUSY; -+ } -+ -+ lnw_ipc_dbg(KERN_INFO -+ "ipc_driver: in <%s> -><%s> file line = <%d>\n", -+ __func__, __FILE__, __LINE__); -+ up(&sema_ipc); -+ -+ return SUCCESS; -+} -+EXPORT_SYMBOL(ipc_check_status); -+ -+int ipc_config_cmd(struct ipc_cmd_type cca_cmd, u32 ipc_cmd_len, void *cmd_data) -+{ -+ -+ union ipc_fw_cmd ipc_cca_cmd; -+ union ipc_sts ipc_sts_reg; -+ u32 retry = MAX_RETRY_CNT; -+ u32 ipc_wbuf; -+ u8 cbuf[MAX_NUM_ENTRIES] = { '\0' }; -+ u32 rbuf_offset = 2; -+ u32 i = 0; -+ -+ if ((&cca_cmd == NULL) || (cmd_data == NULL)) { -+ printk(KERN_INFO "Invalid arguments recieved:\ -+ <%s> -> <%s> file line = <%d>\n", __func__, __FILE__, __LINE__); -+ return -EBUSY; -+ } -+ -+ if (ipc_cmd_len < 4) { -+ printk(KERN_INFO -+ "ipc_send_config: Invalid input param (size) recieved \n"); -+ return -EBUSY; -+ } -+ if (down_interruptible(&sema_ipc)) { -+ printk(KERN_INFO "IPC_Driver module busy\n"); -+ return -EBUSY; -+ } -+ lnw_ipc_dbg(KERN_INFO -+ "ipc_driver: in <%s> -> <%s> file at line no = <%d>\n", -+ __func__, __FILE__, __LINE__); -+ -+ switch (cca_cmd.cmd) { -+ case IPC_BATT_CCA_READ: -+ { -+ struct ipc_batt_cca_data *cca_data = -+ (struct ipc_batt_cca_data *)cmd_data; -+ -+ lnw_ipc_dbg(KERN_INFO "Recieved IPC_BATT_CCA_READ\n"); -+ ipc_cca_cmd.cmd_parts.cmd = IPC_CCA_CMD_READ_WRITE; -+ ipc_cca_cmd.cmd_parts.ioc = cca_cmd.ioc; -+ ipc_cca_cmd.cmd_parts.rfu1 = 0x0; -+ ipc_cca_cmd.cmd_parts.cmd_ID = CCA_REG_READ; -+ ipc_cca_cmd.cmd_parts.size = 0; -+ ipc_cca_cmd.cmd_parts.rfu2 = 0x0; -+ -+ lnw_ipc_dbg(KERN_INFO "ipc_cca_cmd.cmd_data = 0x%x\n", -+ ipc_cca_cmd.cmd_data); -+ /* Check for Status bit = 0 before sending an IPC command */ -+ while (retry--) { -+ ipc_sts_reg.ipc_sts_data = -+ __raw_readl((p_ipc_base + IPC_STS)); -+ if (!ipc_sts_reg.ipc_sts_parts.busy) -+ break; -+ udelay(USLEEP_STS_TIMEOUT); /*10usec*/ -+ } -+ if (ipc_sts_reg.ipc_sts_parts.busy) { -+ printk(KERN_CRIT "SCU is busy %d\n", -+ ipc_sts_reg.ipc_sts_parts.busy); -+ up(&sema_ipc); -+ return -EBUSY; -+ } -+ __raw_writel(ipc_cca_cmd.cmd_data, (p_ipc_base + IPC_CMD)); -+ -+ /* Wait for command completion from SCU firmware */ -+ scu_cmd_completed = FALSE; -+ wait_event_interruptible_timeout(wait, -+ scu_cmd_completed, IPC_TIMEOUT); -+ -+ /*Check for error in command processing*/ -+ ipc_sts_reg.ipc_sts_data = -+ __raw_readl((p_ipc_base + IPC_STS)); -+ if (ipc_sts_reg.ipc_sts_parts.error) { -+ printk(KERN_CRIT "IPC Command failed %d\n", -+ ipc_sts_reg.ipc_sts_parts.error); -+ up(&sema_ipc); -+ return E_CMD_FAILED; -+ } -+ -+ ipc_wbuf = -+ __raw_readl(p_ipc_base + IPC_RBUF); -+ cca_data->cca_val = ipc_wbuf; -+ lnw_ipc_dbg(KERN_INFO -+ "CCA Read at (0x%.8x) = 0x%.8x\n", -+ (u32) (p_ipc_base + IPC_RBUF), ipc_wbuf); -+ break; -+ } -+ case IPC_BATT_CCA_WRITE: -+ -+ ipc_cca_cmd.cmd_parts.cmd = IPC_CCA_CMD_READ_WRITE; -+ ipc_cca_cmd.cmd_parts.ioc = cca_cmd.ioc; -+ ipc_cca_cmd.cmd_parts.rfu1 = 0x0; -+ ipc_cca_cmd.cmd_parts.cmd_ID = CCA_REG_WRITE; -+ ipc_cca_cmd.cmd_parts.size = 0; -+ ipc_cca_cmd.cmd_parts.rfu2 = 0x0; -+ -+ lnw_ipc_dbg(KERN_INFO "ipc_cca_cmd.cmd_data = 0x%x\n", -+ ipc_cca_cmd.cmd_data); -+ -+ /* Check for Status bit = 0 before sending an IPC command */ -+ while (retry--) { -+ ipc_sts_reg.ipc_sts_data = -+ __raw_readl((p_ipc_base + IPC_STS)); -+ if (!ipc_sts_reg.ipc_sts_parts.busy) -+ break; -+ udelay(USLEEP_STS_TIMEOUT); /*10usec*/ -+ } -+ -+ if (ipc_sts_reg.ipc_sts_parts.busy) { -+ printk(KERN_CRIT "SCU is busy %d\n", -+ ipc_sts_reg.ipc_sts_parts.busy); -+ up(&sema_ipc); -+ return -EBUSY; -+ } -+ __raw_writel(cca_cmd.data, ((p_ipc_base + IPC_WBUF) + 4)); -+ __raw_writel(ipc_cca_cmd.cmd_data, (p_ipc_base + IPC_CMD)); -+ -+ /* Wait for command completion from SCU firmware */ -+ scu_cmd_completed = FALSE; -+ wait_event_interruptible_timeout(wait, -+ scu_cmd_completed, IPC_TIMEOUT); -+ -+ /*Check for error in command processing*/ -+ ipc_sts_reg.ipc_sts_data = -+ __raw_readl((p_ipc_base + IPC_STS)); -+ if (ipc_sts_reg.ipc_sts_parts.error) { -+ printk(KERN_CRIT "IPC Command failed %d\n", -+ ipc_sts_reg.ipc_sts_parts.error); -+ up(&sema_ipc); -+ return E_CMD_FAILED; -+ } -+ -+ break; -+ case IPC_BATT_GET_PROP: -+ { -+ struct ipc_batt_prop_data *prop_data = -+ (struct ipc_batt_prop_data *)cmd_data; -+ -+ lnw_ipc_dbg(KERN_CRIT "Recieved IPC_BATT_GET_PROP\n"); -+ -+ /* CCA Read Implementation here.*/ -+ ipc_cca_cmd.cmd_parts.cmd = IPC_CCA_CMD_READ_WRITE; -+ ipc_cca_cmd.cmd_parts.ioc = cca_cmd.ioc; -+ ipc_cca_cmd.cmd_parts.rfu1 = 0x0; -+ ipc_cca_cmd.cmd_parts.cmd_ID = CCA_REG_GET_PROP; -+ ipc_cca_cmd.cmd_parts.size = 0; -+ ipc_cca_cmd.cmd_parts.rfu2 = 0x0; -+ -+ lnw_ipc_dbg(KERN_CRIT "ipc_cca_cmd.cmd_data = 0x%x\n", -+ ipc_cca_cmd.cmd_data); -+ /* Check for Status bit = 0 before sending an IPC command */ -+ while (retry--) { -+ ipc_sts_reg.ipc_sts_data = -+ __raw_readl((p_ipc_base + IPC_STS)); -+ if (!ipc_sts_reg.ipc_sts_parts.busy) -+ break; -+ udelay(USLEEP_STS_TIMEOUT); /*10usec*/ -+ } -+ -+ if (ipc_sts_reg.ipc_sts_parts.busy) { -+ printk(KERN_CRIT "SCU is busy %d\n", -+ ipc_sts_reg.ipc_sts_parts.busy); -+ up(&sema_ipc); -+ return -EBUSY; -+ } -+ __raw_writel(ipc_cca_cmd.cmd_data, (p_ipc_base + IPC_CMD)); -+ -+ scu_cmd_completed = FALSE; -+ wait_event_interruptible_timeout(wait, -+ scu_cmd_completed, IPC_TIMEOUT); -+ -+ if (ipc_cca_cmd.cmd_parts.ioc == 0) { -+ /*Check for error in command processing*/ -+ ipc_sts_reg.ipc_sts_data = -+ __raw_readl((p_ipc_base + IPC_STS)); -+ if (ipc_sts_reg.ipc_sts_parts.error) { -+ printk(KERN_CRIT "IPC Command failed %d\n", -+ ipc_sts_reg.ipc_sts_parts.error); -+ up(&sema_ipc); -+ return E_CMD_FAILED; -+ } -+ } -+ -+ /* On wake-up fill the user buffer with IPC_RBUF data.*/ -+ rbuf_offset = 0; -+ if ((ipc_cmd_len < 4) || (ipc_cmd_len > 9)) { -+ lnw_ipc_dbg(KERN_CRIT -+ "ipc_send_config: Invalid input param\ -+ (size) recieved \n"); -+ up(&sema_ipc); -+ return -EBUSY; -+ } -+ -+ if (ipc_cmd_len >= 4) { -+ ipc_wbuf = __raw_readl(p_ipc_base + IPC_RBUF); -+ lnw_ipc_dbg(KERN_CRIT -+ "Read ipc_wbuf at (0x%.8x) = 0x%.8x\n", -+ (u32) (p_ipc_base + IPC_RBUF + rbuf_offset), -+ ipc_wbuf); -+ rbuf_offset += 4; -+ for (i = 0; i < (ipc_cmd_len - 4); i++) { -+ cbuf[i] = -+ __raw_readb((p_ipc_base + IPC_RBUF + -+ rbuf_offset)); -+ prop_data->batt_value2[i] = cbuf[i]; -+ lnw_ipc_dbg(KERN_CRIT -+ "Read cbuf[%d] at (0x%.8x) = 0x%.8x\n", -+ i, -+ (u32) (p_ipc_base + IPC_RBUF + -+ rbuf_offset), cbuf[i]); -+ rbuf_offset++; -+ } -+ -+ } -+ -+ break; -+ } -+ default: -+ printk(KERN_CRIT "Recieved unknown option\n"); -+ up(&sema_ipc); -+ return -ENODEV; -+ } -+ up(&sema_ipc); -+ -+ return SUCCESS; -+} -+EXPORT_SYMBOL(ipc_config_cmd); -+ -+int mrst_get_firmware_version(unsigned char *mrst_fw_ver_info) -+{ -+ int i = 0; -+ mutex_lock(&mrst_ipc_mutex); -+ -+ if (mrst_fw_ver_info == NULL) { -+ WARN_ON(1); -+ return -EINVAL; -+ } -+ for (i = 0; i < 16; i++) -+ mrst_fw_ver_info[i] = fw_ver_data[i]; -+ -+ mutex_unlock(&mrst_ipc_mutex); -+ return 0; -+} -+EXPORT_SYMBOL(mrst_get_firmware_version); -+ -+int init_ipc_driver(void) -+{ -+ init_waitqueue_head(&wait); -+ -+ sema_init(&sema_ipc, MAX_INSTANCES_ALLOWED); -+ if (down_interruptible(&sema_ipc)) { -+ printk(KERN_CRIT "IPC_Driver module busy\n"); -+ up(&sema_ipc); -+ return -EBUSY; -+ } -+ -+ INIT_WORK(&ipc_wq.ipc_work, mrst_pmic_read_handler); -+ -+ /* Map the memory of ipc1 PMIC reg base */ -+ p_ipc_base = ioremap_nocache(IPC_BASE_ADDRESS, IPC_MAX_ADDRESS); -+ if (p_ipc_base == NULL) { -+ printk(KERN_CRIT -+ "IPC Driver: unable to map the address of IPC 1 \n"); -+ up(&sema_ipc); -+ return E_PMIC_MALLOC; -+ } -+ -+ printk(KERN_CRIT "p_ipc_base = <0x%.8X>\ -+ IPC_BASE_ADDRESS = <0x%.8X>\n", (u32) p_ipc_base, IPC_BASE_ADDRESS); -+ p_i2c_ser_bus = ioremap_nocache(I2C_SER_BUS, I2C_MAX_ADDRESS); -+ if (p_i2c_ser_bus == NULL) { -+ printk(KERN_CRIT -+ "IPC Driver: unable to map the address of IPC 1 \n"); -+ up(&sema_ipc); -+ return E_PMIC_MALLOC; -+ } -+ -+ printk(KERN_CRIT "p_i2c_ser_bus = <0x%.8X>\ -+ I2C_SER_BUS = <0x%.8X>\n", (u32) p_i2c_ser_bus, I2C_SER_BUS); -+ up(&sema_ipc); -+ -+ return SUCCESS; -+} -+ -+int de_init_ipc_driver(void) -+{ -+ if (down_interruptible(&sema_ipc)) { -+ lnw_ipc_dbg(KERN_CRIT "IPC_Driver module busy\n"); -+ up(&sema_ipc); -+ return -EBUSY; -+ } -+ -+ lnw_ipc_dbg(KERN_CRIT -+ "ipc_driver: in <%s> -> <%s> file at line no = <%d>\n", -+ __func__, __FILE__, __LINE__); -+ IOUNMAP(p_ipc_base); -+ IOUNMAP(p_i2c_ser_bus); -+ up(&sema_ipc); -+ -+ return SUCCESS; -+} -+ -+int ipc_pmic_register_read(struct ipc_pmic_reg_data *p_read_reg_data) -+{ -+ union ipc_fw_cmd ipc_cmd; -+ union ipc_sts ipc_sts_reg; -+ u32 retry = MAX_RETRY_CNT; -+ u32 *ipc_wbuf; -+ u8 cbuf[IPC_BUF_LEN] = { '\0' }; -+ u32 cnt = 0; -+ u32 i = 0; -+ u32 rbuf_offset = 2; -+ u8 temp_value = 0; -+ u64 time_to_wait = 0; -+ -+ ipc_wbuf = (u32 *)&cbuf; -+ -+ if (p_read_reg_data == NULL) { -+ printk(KERN_CRIT "Invalid Input Param recieved in pmic read\n"); -+ return -E_INVALID_PARAM; -+ } -+ if (p_read_reg_data->num_entries > MAX_NUM_ENTRIES) { -+ printk(KERN_CRIT "Invalid Input Param recieved in pmic read\n"); -+ return -E_NUM_ENTRIES_OUT_OF_RANGE; -+ } -+ -+ if (down_interruptible(&sema_ipc)) { -+ printk(KERN_CRIT "IPC_Driver module busy\n"); -+ return -EBUSY; -+ } -+ -+ ipc_cmd.cmd_parts.cmd = IPC_PMIC_CMD_READ_WRITE; -+ ipc_cmd.cmd_parts.ioc = p_read_reg_data->ioc; -+ ipc_cmd.cmd_parts.rfu1 = 0x0; -+ ipc_cmd.cmd_parts.cmd_ID = PMIC_REG_READ; -+ ipc_cmd.cmd_parts.size = 3 * (p_read_reg_data->num_entries); -+ ipc_cmd.cmd_parts.rfu2 = 0x0; -+ -+ /* command is set. Fill the IPC_BUF */ -+ lnw_ipc_dbg(KERN_INFO "p_read_reg_data->num_entries <0x%X>\n", -+ p_read_reg_data->num_entries); -+ -+ lnw_ipc_dbg(KERN_INFO "p_read_reg_data->register_address <0x%X>\n", -+ p_read_reg_data->pmic_reg_data[0].register_address); -+ -+ for (i = 0; i < p_read_reg_data->num_entries; i++) { -+ cbuf[cnt] = p_read_reg_data->pmic_reg_data[i].register_address; -+ cbuf[(cnt) + 1] = -+ (p_read_reg_data->pmic_reg_data[i].register_address >> 8); -+ cbuf[(cnt) + 2] = p_read_reg_data->pmic_reg_data[i].value; -+ cnt = cnt + 3; -+ } -+ -+ rbuf_offset = 0; -+ for (i = 0; i < p_read_reg_data->num_entries; i++) { -+ __raw_writel(ipc_wbuf[i], ((p_ipc_base + IPC_WBUF) -+ + rbuf_offset)); -+ rbuf_offset += 4; -+ if (i >= 3) -+ break; -+ } -+ -+ /* Check for Status bit = 0 before sending an IPC command */ -+ while (retry--) { -+ ipc_sts_reg.ipc_sts_data = -+ __raw_readl((p_ipc_base + IPC_STS)); -+ if (!ipc_sts_reg.ipc_sts_parts.busy) -+ break; -+ udelay(USLEEP_STS_TIMEOUT); /*10usec*/ -+ } -+ -+ if (ipc_sts_reg.ipc_sts_parts.busy) { -+ printk(KERN_CRIT "SCU is busy %d\n", -+ ipc_sts_reg.ipc_sts_parts.busy); -+ up(&sema_ipc); -+ return -EBUSY; -+ } -+ -+ scu_cmd_completed = FALSE; -+ __raw_writel(ipc_cmd.cmd_data, (p_ipc_base + IPC_CMD)); -+ -+ /*wait for 10ms do not tie to kernel timer_ticks*/ -+ time_to_wait = msecs_to_jiffies(IPC_TIMEOUT); -+ -+ /* Wait for command completion from SCU firmware */ -+ wait_event_interruptible_timeout(wait, -+ scu_cmd_completed, time_to_wait); -+ -+ if (ipc_cmd.cmd_parts.ioc == 0) { -+ /*Check for error in command processing*/ -+ ipc_sts_reg.ipc_sts_data = -+ __raw_readl((p_ipc_base + IPC_STS)); -+ if (ipc_sts_reg.ipc_sts_parts.busy) { -+ printk(KERN_CRIT "Timeout occured for ioc=0 and SCU is busy%d\n", -+ ipc_sts_reg.ipc_sts_parts.busy); -+ up(&sema_ipc); -+ return -EBUSY; -+ } -+ if (ipc_sts_reg.ipc_sts_parts.error) { -+ printk(KERN_CRIT "IPC Command failed %d\n", -+ ipc_sts_reg.ipc_sts_parts.error); -+ up(&sema_ipc); -+ return E_CMD_FAILED; -+ } -+ } -+ /* IPC driver expects interrupt when IOC is set to 1.*/ -+ if ((ipc_cmd.cmd_parts.ioc == 1) && (scu_cmd_completed == FALSE)) { -+ up(&sema_ipc); -+ return E_NO_INTERRUPT_ON_IOC; -+ } -+ rbuf_offset = 2; -+ for (i = 0; i < p_read_reg_data->num_entries; i++) { -+ temp_value = readb((p_ipc_base + IPC_RBUF + rbuf_offset)); -+ p_read_reg_data->pmic_reg_data[i].value = temp_value; -+ rbuf_offset += 3; -+ } -+ -+ up(&sema_ipc); -+ -+ return SUCCESS; -+} -+EXPORT_SYMBOL(ipc_pmic_register_read); -+ -+int ipc_pmic_register_write(struct ipc_pmic_reg_data *p_write_reg_data, -+ u8 ipc_blocking_flag) -+{ -+ union ipc_fw_cmd ipc_cmd; -+ union ipc_sts ipc_sts_reg; -+ u32 retry = MAX_RETRY_CNT; -+ u32 *ipc_wbuf; -+ u8 cbuf[IPC_BUF_LEN] = { '\0' }; -+ u32 cnt = 0; -+ u32 i = 0; -+ u32 rbuf_offset = 2; -+ -+ ipc_wbuf = (u32 *)&cbuf; -+ -+ if (p_write_reg_data == NULL) { -+ printk(KERN_CRIT "Invalid Input Param recieved in pmic write\n"); -+ return -E_INVALID_PARAM; -+ } -+ if (p_write_reg_data->num_entries > MAX_NUM_ENTRIES) { -+ printk(KERN_CRIT "Invalid Input Param recieved in pmic write\n"); -+ return -E_NUM_ENTRIES_OUT_OF_RANGE; -+ } -+ -+ if (down_interruptible(&sema_ipc)) { -+ printk(KERN_INFO "IPC_Driver module busy\n"); -+ return -EBUSY; -+ } -+ -+ ipc_cmd.cmd_parts.cmd = IPC_PMIC_CMD_READ_WRITE; -+ ipc_cmd.cmd_parts.ioc = p_write_reg_data->ioc; -+ ipc_cmd.cmd_parts.rfu1 = 0x0; -+ ipc_cmd.cmd_parts.cmd_ID = PMIC_REG_WRITE; -+ ipc_cmd.cmd_parts.size = 3 * (p_write_reg_data->num_entries); -+ ipc_cmd.cmd_parts.rfu2 = 0x0; -+ -+ /* command is set. Fill the IPC_BUF */ -+ lnw_ipc_dbg(KERN_INFO "p_write_reg_data->num_entries 0x%X>\n", -+ p_write_reg_data->num_entries); -+ -+ lnw_ipc_dbg(KERN_INFO "p_write_reg_data->register_address 0x%X>\n", -+ p_write_reg_data->pmic_reg_data[0].register_address); -+ for (i = 0; i < p_write_reg_data->num_entries; i++) { -+ cbuf[cnt] = p_write_reg_data->pmic_reg_data[i].register_address; -+ cbuf[(cnt) + 1] = -+ (p_write_reg_data->pmic_reg_data[i].register_address >> 8); -+ cbuf[(cnt) + 2] = p_write_reg_data->pmic_reg_data[i].value; -+ cnt = cnt + 3; -+ } -+ -+ rbuf_offset = 0; -+ for (i = 0; i < p_write_reg_data->num_entries; i++) { -+ __raw_writel(ipc_wbuf[i], ((p_ipc_base + IPC_WBUF) -+ + rbuf_offset)); -+ rbuf_offset += 4; -+ if (i >= 3) -+ break; -+ } -+ /* Check for Status bit = 0 before sending an IPC command */ -+ while (retry--) { -+ ipc_sts_reg.ipc_sts_data = -+ __raw_readl((p_ipc_base + IPC_STS)); -+ if (!ipc_sts_reg.ipc_sts_parts.busy) -+ break; -+ udelay(USLEEP_STS_TIMEOUT); /*10usec*/ -+ } -+ -+ if (ipc_sts_reg.ipc_sts_parts.busy) { -+ printk(KERN_CRIT "IPC Command failed %d\n", -+ ipc_sts_reg.ipc_sts_parts.busy); -+ up(&sema_ipc); -+ return -EBUSY; -+ } -+ __raw_writel(ipc_cmd.cmd_data, (p_ipc_base + IPC_CMD)); -+ -+ /* Wait for command completion from SCU firmware */ -+ scu_cmd_completed = FALSE; -+ wait_event_interruptible_timeout(wait, -+ scu_cmd_completed, IPC_TIMEOUT); -+ -+ /*Check for error in command processing*/ -+ ipc_sts_reg.ipc_sts_data = -+ __raw_readl((p_ipc_base + IPC_STS)); -+ if (ipc_sts_reg.ipc_sts_parts.error) { -+ printk(KERN_CRIT "IPC Command failed %d\n", -+ ipc_sts_reg.ipc_sts_parts.error); -+ up(&sema_ipc); -+ return E_CMD_FAILED; -+ } -+ up(&sema_ipc); -+ -+ return SUCCESS; -+} -+EXPORT_SYMBOL(ipc_pmic_register_write); -+ -+int ipc_pmic_register_read_modify(struct ipc_pmic_mod_reg_data -+ *p_read_mod_reg_data) -+{ -+ union ipc_fw_cmd ipc_cmd; -+ union ipc_sts ipc_sts_reg; -+ u32 retry = MAX_RETRY_CNT; -+ u32 *ipc_wbuf; -+ u8 cbuf[IPC_BUF_LEN] = { '\0' }; -+ u32 cnt = 0; -+ u32 i = 0; -+ u32 rbuf_offset = 2; -+ ipc_wbuf = (u32 *)&cbuf; -+ -+ if (down_interruptible(&sema_ipc)) { -+ printk(KERN_INFO "IPC_Driver module busy\n"); -+ return -EBUSY; -+ } -+ -+ if (p_read_mod_reg_data == NULL) { -+ printk(KERN_CRIT "Invalid Input recieved pmic read modify\n"); -+ up(&sema_ipc); -+ return -E_INVALID_PARAM; -+ } -+ if (p_read_mod_reg_data->num_entries > MAX_NUM_ENTRIES) { -+ printk(KERN_CRIT "Invalid Input recieved pmic read modify\n"); -+ up(&sema_ipc); -+ return -E_NUM_ENTRIES_OUT_OF_RANGE; -+ } -+ -+ ipc_cmd.cmd_parts.cmd = IPC_PMIC_CMD_READ_WRITE; -+ ipc_cmd.cmd_parts.ioc = p_read_mod_reg_data->ioc; -+ ipc_cmd.cmd_parts.rfu1 = 0x0; -+ ipc_cmd.cmd_parts.cmd_ID = PMIC_REG_READ_MODIFY; -+ ipc_cmd.cmd_parts.size = 3 * (p_read_mod_reg_data->num_entries); -+ ipc_cmd.cmd_parts.rfu2 = 0x0; -+ -+ /* command is set. Fill the IPC_BUF */ -+ lnw_ipc_dbg(KERN_INFO "p_read_mod_reg_data->num_entries <0x%X> \n", -+ p_read_mod_reg_data->num_entries); -+ -+ for (i = 0; i < p_read_mod_reg_data->num_entries; i++) { -+ cbuf[cnt] = -+ p_read_mod_reg_data->pmic_mod_reg_data[i].register_address; -+ cbuf[(cnt) + 1] = -+ (p_read_mod_reg_data->pmic_mod_reg_data[i]. -+ register_address >> 8); -+ cbuf[(cnt) + 2] = -+ p_read_mod_reg_data->pmic_mod_reg_data[i].value; -+ cbuf[(cnt) + 3] = -+ p_read_mod_reg_data->pmic_mod_reg_data[i].bit_map; -+ cnt = cnt + 4; -+ } -+ -+ rbuf_offset = 0; -+ for (i = 0; i < p_read_mod_reg_data->num_entries; i++) { -+ __raw_writel(ipc_wbuf[i], -+ ((p_ipc_base + IPC_WBUF) + rbuf_offset)); -+ rbuf_offset += 4; -+ if (i >= 3) -+ break; -+ } -+ -+ /* Check for Status bit = 0 before sending an IPC command */ -+ while (retry--) { -+ ipc_sts_reg.ipc_sts_data = -+ __raw_readl((p_ipc_base + IPC_STS)); -+ if (!ipc_sts_reg.ipc_sts_parts.busy) -+ break; -+ udelay(USLEEP_STS_TIMEOUT); /*10usec*/ -+ } -+ if (ipc_sts_reg.ipc_sts_parts.busy) { -+ printk(KERN_CRIT "SCU is busy %d\n", -+ ipc_sts_reg.ipc_sts_parts.busy); -+ up(&sema_ipc); -+ return -EBUSY; -+ } -+ __raw_writel(ipc_cmd.cmd_data, (p_ipc_base + IPC_CMD)); -+ -+ /* Wait for command completion from SCU firmware */ -+ scu_cmd_completed = FALSE; -+ wait_event_interruptible_timeout(wait, -+ scu_cmd_completed, IPC_TIMEOUT); -+ -+ if (ipc_cmd.cmd_parts.ioc == 0) { -+ /*Check for error in command processing*/ -+ ipc_sts_reg.ipc_sts_data = -+ __raw_readl((p_ipc_base + IPC_STS)); -+ if (ipc_sts_reg.ipc_sts_parts.error) { -+ printk(KERN_CRIT "IPC Command failed %d\n", -+ ipc_sts_reg.ipc_sts_parts.error); -+ up(&sema_ipc); -+ return E_CMD_FAILED; -+ } -+ } -+ -+ /* IPC driver expects interrupt when IOC is set to 1.*/ -+ if ((ipc_cmd.cmd_parts.ioc == 1) && (scu_cmd_completed == FALSE)) { -+ up(&sema_ipc); -+ return E_NO_INTERRUPT_ON_IOC; -+ } -+ -+ /* On wake-up fill the user buffer with IPC_RBUF data.*/ -+ rbuf_offset = 0; -+ for (i = 0; i < p_read_mod_reg_data->num_entries; i++) { -+ ipc_wbuf[i] = -+ __raw_readl((p_ipc_base + IPC_RBUF + rbuf_offset)); -+ rbuf_offset += 4; -+ } -+ -+ rbuf_offset = 2; -+ for (i = 0; i < p_read_mod_reg_data->num_entries; i++) { -+ p_read_mod_reg_data->pmic_mod_reg_data[i].value = -+ __raw_readb((p_ipc_base + IPC_RBUF + rbuf_offset)); -+ rbuf_offset += 4; -+ } -+ up(&sema_ipc); -+ -+ return SUCCESS; -+} -+EXPORT_SYMBOL(ipc_pmic_register_read_modify); -+ -+int ipc_pmic_register_read_non_blocking( -+ struct ipc_non_blocking_pmic_read *p_nb_read) -+{ -+ union ipc_fw_cmd ipc_cmd; -+ union ipc_sts ipc_sts_reg; -+ u32 retry = MAX_RETRY_CNT; -+ u32 *ipc_wbuf; -+ u8 cbuf[IPC_BUF_LEN] = { '\0' }; -+ u32 cnt = 0; -+ u32 i = 0; -+ u32 rbuf_offset = 2; -+ ipc_wbuf = (u32 *)&cbuf; -+ -+ if (down_interruptible(&sema_ipc)) { -+ printk(KERN_CRIT "IPC_Driver module busy\n"); -+ return -EBUSY; -+ } -+ if (p_nb_read == NULL) { -+ printk(KERN_CRIT "Invalid Input Param recieved\ -+ in non blocking pmic read\n"); -+ up(&sema_ipc); -+ return -E_INVALID_PARAM; -+ } -+ if (p_nb_read->pmic_nb_read.num_entries > MAX_NUM_ENTRIES) { -+ printk(KERN_CRIT "Invalid Number Of Entries\ -+ - non blocking pmic read\n"); -+ up(&sema_ipc); -+ return -E_NUM_ENTRIES_OUT_OF_RANGE; -+ } -+ -+ if (cmd_id >= MAX_NB_BUF_SIZE) { -+ printk(KERN_CRIT "Queue is full!! cannot service request!\n"); -+ up(&sema_ipc); -+ return -E_QUEUE_IS_FULL; -+ } -+ -+ -+ non_blocking_read_flag = TRUE; -+ /*Copy the contents to this global structure for future use*/ -+ pmic_read_que[cmd_id] = *(p_nb_read); -+ ipc_wq.cmd_id = cmd_id++; -+ callback = p_nb_read->callback_host; -+ pmic_read_que[cmd_id].callback_host = p_nb_read->callback_host; -+ -+ ipc_cmd.cmd_parts.cmd = IPC_PMIC_CMD_READ_WRITE; -+ ipc_cmd.cmd_parts.ioc = 1; -+ ipc_cmd.cmd_parts.rfu1 = 0x0; -+ ipc_cmd.cmd_parts.cmd_ID = PMIC_REG_READ; -+ ipc_cmd.cmd_parts.size = 3 * (p_nb_read->pmic_nb_read.num_entries); -+ ipc_cmd.cmd_parts.rfu2 = 0x0; -+ -+ /* command is set. Fill the IPC_BUF */ -+ lnw_ipc_dbg(KERN_INFO "pmic_nb_read.num_entries <0x%X>\n", -+ p_nb_read->pmic_nb_read.num_entries); -+ -+ lnw_ipc_dbg(KERN_INFO "pmic_nb_read.register_address <0x%X>\n", -+ p_nb_read->pmic_nb_read.pmic_reg_data[0].register_address); -+ -+ for (i = 0; i < p_nb_read->pmic_nb_read.num_entries; i++) { -+ cbuf[cnt] = -+ p_nb_read->pmic_nb_read.pmic_reg_data[i].register_address; -+ cbuf[(cnt) + 1] = (p_nb_read->pmic_nb_read.pmic_reg_data[i]\ -+ .register_address >> 8); -+ cbuf[(cnt) + 2] = -+ p_nb_read->pmic_nb_read.pmic_reg_data[i].value; -+ cnt = cnt + 3; -+ } -+ rbuf_offset = 0; -+ for (i = 0; i < p_nb_read->pmic_nb_read.num_entries; i++) { -+ __raw_writel(ipc_wbuf[i], ((p_ipc_base + IPC_WBUF) -+ + rbuf_offset)); -+ rbuf_offset += 4; -+ if (i >= 3) -+ break; -+ } -+ /* Check for Status bit = 0 before sending an IPC command */ -+ while (retry--) { -+ ipc_sts_reg.ipc_sts_data = __raw_readl((p_ipc_base + IPC_STS)); -+ if (!ipc_sts_reg.ipc_sts_parts.busy) -+ break; -+ -+ udelay(USLEEP_STS_TIMEOUT); /*10usec*/ -+ } -+ if (ipc_sts_reg.ipc_sts_parts.busy) { -+ printk(KERN_CRIT "SCU is busy %d\n", -+ ipc_sts_reg.ipc_sts_parts.busy); -+ up(&sema_ipc); -+ return -EBUSY; -+ } -+ __raw_writel(ipc_cmd.cmd_data, (p_ipc_base + IPC_CMD)); -+ /*Control returns after issueing the command here*/ -+ /*Data is read asynchronously later*/ -+ up(&sema_ipc); -+ -+ return SUCCESS; -+} -+EXPORT_SYMBOL(ipc_pmic_register_read_non_blocking); -+ -+int mrst_ipc_read32(struct ipc_reg_data *p_reg_data) -+{ -+ union ipc_fw_cmd ipc_cmd; -+ union ipc_sts ipc_sts_reg; -+ u32 retry = MAX_RETRY_CNT; -+ -+ if (p_reg_data == NULL) { -+ printk(KERN_CRIT "Invalid Input Param recieved\ -+ in mrst_ipc_read32\n"); -+ return -E_INVALID_PARAM; -+ } -+ -+ if (down_interruptible(&sema_ipc)) { -+ printk(KERN_INFO "IPC_Driver module busy\n"); -+ return -EBUSY; -+ } -+ -+ lnw_ipc_dbg(KERN_INFO -+ "ipc_driver: Address = 0x%.8X\t: Data = 0x%.8X\n", -+ p_reg_data->address, p_reg_data->data); -+ -+ ipc_cmd.cmd_parts.cmd = INDIRECT_READ; -+ ipc_cmd.cmd_parts.ioc = p_reg_data->ioc; -+ ipc_cmd.cmd_parts.rfu1 = 0x0; -+ ipc_cmd.cmd_parts.cmd_ID = 0x00; -+ ipc_cmd.cmd_parts.size = 4; -+ ipc_cmd.cmd_parts.rfu2 = 0x0; -+ -+ lnw_ipc_dbg(KERN_INFO -+ "ipc_driver: IPC_CMD-> 0x%.8X\n", ipc_cmd.cmd_data); -+ /* Check for Status bit = 0 before sending an IPC command */ -+ while (retry--) { -+ ipc_sts_reg.ipc_sts_data = -+ __raw_readl((p_ipc_base + IPC_STS)); -+ if (!ipc_sts_reg.ipc_sts_parts.busy) -+ break; -+ udelay(USLEEP_STS_TIMEOUT); /*10usec*/ -+ } -+ -+ if (ipc_sts_reg.ipc_sts_parts.busy) { -+ printk(KERN_CRIT "SCU is busy %d\n", -+ ipc_sts_reg.ipc_sts_parts.busy); -+ up(&sema_ipc); -+ return -EBUSY; -+ } -+ /* -+ * Write the Address to IPC_SPTR -+ * Issue the command by writing to IPC_CMD -+ * Read the contents of IPC_RBUF to data -+ */ -+ -+ __raw_writel(p_reg_data->address, (p_ipc_base + IPC_SPTR)); -+ __raw_writel(ipc_cmd.cmd_data, (p_ipc_base + IPC_CMD)); -+ -+ scu_cmd_completed = FALSE; -+ wait_event_interruptible_timeout(wait, -+ scu_cmd_completed, IPC_TIMEOUT); -+ -+ if (ipc_cmd.cmd_parts.ioc == 0) { -+ /*Check for error in command processing*/ -+ ipc_sts_reg.ipc_sts_data = -+ __raw_readl((p_ipc_base + IPC_STS)); -+ if (ipc_sts_reg.ipc_sts_parts.error) { -+ printk(KERN_CRIT "IPC Command failed %d\n", -+ ipc_sts_reg.ipc_sts_parts.error); -+ up(&sema_ipc); -+ return E_CMD_FAILED; -+ } -+ } -+ /* IPC driver expects interrupt when IOC is set to 1.*/ -+ if ((ipc_cmd.cmd_parts.ioc == 1) && (scu_cmd_completed == FALSE)) { -+ up(&sema_ipc); -+ return E_NO_INTERRUPT_ON_IOC; -+ } -+ -+ /* Command completed successfully Read the data */ -+ p_reg_data->data = -+ __raw_readl(p_ipc_base + IPC_RBUF); -+ lnw_ipc_dbg(KERN_INFO -+ "ipc_driver: Data Recieved from IPC_RBUF = 0x%.8X\n", -+ p_reg_data->data); -+ -+ up(&sema_ipc); -+ -+ return SUCCESS; -+} -+EXPORT_SYMBOL(mrst_ipc_read32); -+ -+int mrst_ipc_write32(struct ipc_reg_data *p_reg_data) -+{ -+ union ipc_fw_cmd ipc_cmd; -+ union ipc_sts ipc_sts_reg; -+ u32 retry = MAX_RETRY_CNT; -+ -+ if (p_reg_data == NULL) { -+ printk(KERN_CRIT "Invalid Input Param recieved\ -+ in mrst_ipc_write32\n"); -+ return -E_INVALID_PARAM; -+ } -+ -+ if (down_interruptible(&sema_ipc)) { -+ printk(KERN_INFO "IPC_Driver module busy\n"); -+ return -EBUSY; -+ } -+ -+ lnw_ipc_dbg(KERN_INFO -+ "ipc_driver: in <%s> -> <%s> file at line no = <%d>\n", -+ __func__, __FILE__, __LINE__); -+ -+ ipc_cmd.cmd_parts.cmd = INDIRECT_WRITE; -+ ipc_cmd.cmd_parts.ioc = p_reg_data->ioc; -+ ipc_cmd.cmd_parts.rfu1 = 0x0; -+ ipc_cmd.cmd_parts.cmd_ID = 0x00; -+ ipc_cmd.cmd_parts.size = 4; -+ ipc_cmd.cmd_parts.rfu2 = 0x0; -+ -+ /* Check for Status bit = 0 before sending an IPC command */ -+ while (retry--) { -+ ipc_sts_reg.ipc_sts_data = -+ __raw_readl((p_ipc_base + IPC_STS)); -+ if (!ipc_sts_reg.ipc_sts_parts.busy) -+ break; -+ udelay(USLEEP_STS_TIMEOUT); /*10usec*/ -+ } -+ -+ if (ipc_sts_reg.ipc_sts_parts.busy) { -+ printk(KERN_CRIT "SCU is busy %d\n", -+ ipc_sts_reg.ipc_sts_parts.busy); -+ up(&sema_ipc); -+ return -EBUSY; -+ } -+ __raw_writel(p_reg_data->address, (p_ipc_base + IPC_DPTR)); -+ __raw_writel(p_reg_data->data, (p_ipc_base + IPC_WBUF)); -+ __raw_writel(ipc_cmd.cmd_data, (p_ipc_base + IPC_CMD)); -+ -+ scu_cmd_completed = FALSE; -+ wait_event_interruptible_timeout(wait, -+ scu_cmd_completed, IPC_TIMEOUT); -+ -+ /*Check for error in command processing*/ -+ ipc_sts_reg.ipc_sts_data = -+ __raw_readl((p_ipc_base + IPC_STS)); -+ if (ipc_sts_reg.ipc_sts_parts.error) { -+ printk(KERN_CRIT "IPC Command failed %d\n", -+ ipc_sts_reg.ipc_sts_parts.error); -+ up(&sema_ipc); -+ return E_CMD_FAILED; -+ } -+ up(&sema_ipc); -+ -+ return SUCCESS; -+} -+EXPORT_SYMBOL(mrst_ipc_write32); -+ -+int ipc_set_watchdog(struct watchdog_reg_data *p_watchdog_reg_data) -+{ -+ union ipc_fw_cmd ipc_cmd; -+ u32 *ipc_wbuf; -+ u8 cbuf[16] = { '\0' }; -+ u32 rbuf_offset = 2; -+ u32 retry = MAX_RETRY_CNT; -+ union ipc_sts ipc_sts_reg; -+ -+ ipc_wbuf = (u32 *)&cbuf; -+ -+ if (p_watchdog_reg_data == NULL) { -+ printk(KERN_CRIT "Invalid Input Param recieved in pmic read\n"); -+ return -E_INVALID_PARAM; -+ } -+ -+ if (down_interruptible(&sema_ipc)) { -+ printk(KERN_CRIT "IPC_Driver module busy\n"); -+ return -EBUSY; -+ } -+ -+ ipc_cmd.cmd_parts.cmd = IPC_SET_WATCHDOG_TIMER; -+ ipc_cmd.cmd_parts.ioc = p_watchdog_reg_data->ioc; -+ ipc_cmd.cmd_parts.rfu1 = 0x0; -+ ipc_cmd.cmd_parts.size = 2; -+ ipc_cmd.cmd_parts.rfu2 = 0x0; -+ -+ /* Check for Status bit = 0 before sending an IPC command */ -+ while (retry--) { -+ ipc_sts_reg.ipc_sts_data = -+ __raw_readl((p_ipc_base + IPC_STS)); -+ if (!ipc_sts_reg.ipc_sts_parts.busy) -+ break; -+ udelay(USLEEP_STS_TIMEOUT); /*10usec*/ -+ } -+ -+ ipc_wbuf[0] = p_watchdog_reg_data->payload1; -+ printk(KERN_INFO "p_watchdog_data->payload1 <0x%X>\n", -+ ipc_wbuf[0]); -+ __raw_writel(ipc_wbuf[0], ((p_ipc_base + IPC_WBUF) + rbuf_offset)); -+ -+ ipc_wbuf[1] = p_watchdog_reg_data->payload2; -+ lnw_ipc_dbg(KERN_INFO "p_watchdog_data->payload2 <0x%X>\n", -+ ipc_wbuf[1]); -+ __raw_writel(ipc_wbuf[1], ((p_ipc_base + IPC_WBUF) + rbuf_offset)); -+ -+ lnw_ipc_dbg(KERN_INFO "ipc_cmd.cmd_data is <0x%X>\n", -+ ipc_cmd.cmd_data); -+ /*execute the command by writing to IPC_CMD registers*/ -+ __raw_writel(ipc_cmd.cmd_data, (p_ipc_base + IPC_CMD)); -+ -+ /* Wait for command completion from SCU firmware and return */ -+ scu_cmd_completed = FALSE; -+ wait_event_interruptible_timeout(wait, -+ scu_cmd_completed, IPC_TIMEOUT); -+ -+ /* IPC driver expects interrupt when IOC is set to 1.*/ -+ if ((ipc_cmd.cmd_parts.ioc == 1) && (scu_cmd_completed == FALSE)) { -+ up(&sema_ipc); -+ return E_NO_INTERRUPT_ON_IOC; -+ } -+ -+ /*Check for error in command processing*/ -+ ipc_sts_reg.ipc_sts_data = -+ __raw_readl((p_ipc_base + IPC_STS)); -+ if (ipc_sts_reg.ipc_sts_parts.error) { -+ printk(KERN_CRIT "IPC Command failed %d\n", -+ ipc_sts_reg.ipc_sts_parts.error); -+ up(&sema_ipc); -+ return E_CMD_FAILED; -+ } -+ lnw_ipc_dbg(KERN_CRIT "IPC Command status = 0x%x\n", -+ ipc_sts_reg.ipc_sts_data); -+ up(&sema_ipc); -+ -+ return SUCCESS; -+} -+EXPORT_SYMBOL(ipc_set_watchdog); -+ -+int ipc_program_io_bus_master(struct ipc_io_bus_master_regs *p_reg_data) -+{ -+ u32 io_bus_master_cmd = 0; -+ if (down_interruptible(&sema_ipc)) { -+ printk(KERN_INFO "IPC_Driver module busy\n"); -+ return -EBUSY; -+ } -+ -+ if (p_reg_data == NULL) { -+ printk(KERN_CRIT "Invalid Input Param recieved in\ -+ <ipc_program_io_bus_master>\n"); -+ up(&sema_ipc); -+ return -E_INVALID_PARAM; -+ } -+ printk(KERN_CRIT "p_reg_data->ctrl_reg_addr = 0x%x\n",\ -+ p_reg_data->ctrl_reg_addr); -+ printk(KERN_CRIT "p_reg_data->ctrl_reg_data = 0x%x\n",\ -+ p_reg_data->ctrl_reg_data); -+ -+ /* Read the first byte for command*/ -+ io_bus_master_cmd = (p_reg_data->ctrl_reg_addr)&(0xFF000000); -+ io_bus_master_cmd = (io_bus_master_cmd >> 24); -+ -+ if (io_bus_master_cmd == NOP_CMD) { -+ printk(KERN_CRIT "NOP_CMD = 0x%x\n", io_bus_master_cmd); -+ } else if (io_bus_master_cmd == READ_CMD) { -+ lnw_ipc_dbg(KERN_CRIT "Address %#xp = data = %#x\n", -+ (unsigned int)(p_i2c_ser_bus + CTRL_REG_ADDR), -+ p_reg_data->ctrl_reg_addr); -+ __raw_writel(p_reg_data->ctrl_reg_addr, -+ (p_i2c_ser_bus + CTRL_REG_ADDR)); -+ udelay(1000);/*Write Not getting updated without delay*/ -+ p_reg_data->ctrl_reg_data = -+ __raw_readl(p_i2c_ser_bus + CTRL_REG_DATA); -+ lnw_ipc_dbg(KERN_CRIT "Data = %#x\n", -+ p_reg_data->ctrl_reg_data); -+ } else if (io_bus_master_cmd == WRITE_CMD) { -+ printk(KERN_CRIT"WRITE_CMD = 0x%x\n", io_bus_master_cmd); -+ -+ __raw_writel(p_reg_data->ctrl_reg_data, -+ (p_i2c_ser_bus + CTRL_REG_DATA)); -+ udelay(1000); -+ __raw_writel(p_reg_data->ctrl_reg_addr, -+ (p_i2c_ser_bus + CTRL_REG_ADDR)); -+ } else { -+ printk(KERN_CRIT "in INVALID_CMD = 0x%x\n", io_bus_master_cmd); -+ up(&sema_ipc); -+ return -E_INVALID_CMD; -+ } -+ up(&sema_ipc); -+ return SUCCESS; -+} -+EXPORT_SYMBOL(ipc_program_io_bus_master); -+ -+/*Work QUEUE Handler function: -+ *This function gets invoked by queue. -+ */ -+static void mrst_pmic_read_handler(struct work_struct *work) -+{ -+ static int i; -+ union ipc_sts ipc_sts_reg; -+ u32 retry = MAX_RETRY_CNT; -+ u32 rbuf_offset = 2; -+ -+ u8 pmic_data = 0; -+ -+ if (down_interruptible(&sema_ipc)) { -+ printk(KERN_CRIT "IPC_Driver non-blocking read handler\n"); -+ } else { -+ non_blocking_read_flag = FALSE; -+ pmic_data = __raw_readb((p_ipc_base + IPC_RBUF + 2)); -+ -+ while (retry--) { -+ ipc_sts_reg.ipc_sts_data = -+ __raw_readl((p_ipc_base + IPC_STS)); -+ if (!ipc_sts_reg.ipc_sts_parts.busy) -+ break; -+ udelay(USLEEP_STS_TIMEOUT); /*10usec*/ -+ } -+ if (ipc_sts_reg.ipc_sts_parts.busy) { -+ printk(KERN_CRIT "SCU is busy %d\n", -+ ipc_sts_reg.ipc_sts_parts.busy); -+ pmic_data = -1 /*Invalid data*/; -+ } else { -+ rbuf_offset = 2; -+ cmd_id--; -+ for (i = 0; i < pmic_read_que[cmd_id]. -+ pmic_nb_read.num_entries; i++) { -+ pmic_read_que[cmd_id].pmic_nb_read. -+ pmic_reg_data[i].value = -+ __raw_readb((p_ipc_base + IPC_RBUF -+ + rbuf_offset)); -+ rbuf_offset += 3; -+ } -+ } -+ } -+ up(&sema_ipc); -+ /*Call the call-back function. -+ *The host driver is responsible for reading valid data. -+ */ -+ pmic_read_que[cmd_id].callback_host(pmic_read_que[cmd_id].pmic_nb_read, -+ pmic_read_que[cmd_id].context); -+} -+ -+ -+/** -+ * int ipc_device_fw_upgrade() - API to upgrade the Integrated Firmware Image -+ * for Intel(R) Moorestown platform. -+ * @u8 *mrst_fw_buf: Command data. -+ * @u32 mrst_fw_buf_len: length of the command to be sent. -+ * -+ * This function provides and interface to send an IPC coulumb counter -+ * command to SCU Firmware and recieve a response. This is used by the -+ * PMIC battery driver on Moorestown platform. -+ */ -+int ipc_device_fw_upgrade(u8 *mrst_fw_buf, u32 mrst_fw_buf_len) -+{ -+ union ipc_fw_cmd ipc_dfu_cmd; -+ void __iomem *p_tmp_fw_base; -+ int retry_cnt = 0; -+ -+ MailBox_t *pMailBox = NULL; -+ -+ if (down_interruptible(&sema_ipc)) { -+ printk(KERN_ERR "IPC_Driver module busy\n"); -+ return -EBUSY; -+ } -+ -+ /* Map the memory of ipc1 PMIC reg base */ -+ p_dfu_fw_base = ioremap_nocache(DFU_LOAD_ADDR, MIP_HEADER_SIZE); -+ p_tmp_fw_base = p_dfu_fw_base; -+ if (p_dfu_fw_base == NULL) { -+ up(&sema_ipc); -+ return E_PMIC_MALLOC; -+ } -+ p_dfu_mailbox_base = ioremap_nocache(DFU_MAILBOX_ADDR, -+ sizeof(MailBox_t)); -+ if (p_dfu_mailbox_base == NULL) { -+ up(&sema_ipc); -+ return E_PMIC_MALLOC; -+ } -+ -+ pMailBox = (MailBox_t*)p_dfu_mailbox_base; -+ -+ ipc_dfu_cmd.cmd_data = FW_UPGRADE_READY_CMD; -+ writel(ipc_dfu_cmd.cmd_data, (p_ipc_base + IPC_CMD)); -+ -+ /*IA initializes both IAFlag and SCUFlag to zero*/ -+ pMailBox->SCUFlag = 0; -+ pMailBox->IAFlag = 0; -+ -+ /*IA copies the 2KB MIP header to SRAM at 0xFFFC0000*/ -+ memcpy((u8*)(p_dfu_fw_base), mrst_fw_buf, 0x800); -+ iounmap(p_tmp_fw_base); -+ -+ /* IA sends "FW Update" IPC command (CMD_ID 0xFE; MSG_ID 0x02). -+ * Upon receiving this command, SCU will write the 2K MIP header -+ * from 0xFFFC0000 into NAND. -+ * SCU will write a status code into the Mailbox, and then set SCUFlag. -+ */ -+ -+ ipc_dfu_cmd.cmd_data = FW_UPGRADE_GO_CMD; -+ writel(ipc_dfu_cmd.cmd_data, (p_ipc_base + IPC_CMD)); -+ -+ /*IA stalls until SCUFlag is set */ -+ while (pMailBox->SCUFlag != 1) -+ udelay(100); -+ -+ /* IA checks Mailbox status. -+ * If the status is 'BADN', then abort (bad NAND). -+ * If the status is 'TxLO', then continue. -+ */ -+ while (pMailBox->Mailbox != TxLO) -+ udelay(10000); -+ udelay(10000); -+ -+update_retry: -+ if (retry_cnt > 5) -+ goto exit_function; -+ -+ if (pMailBox->Mailbox == TxLO) { -+ /* Map the memory of ipc1 PMIC reg base */ -+ p_dfu_fw_base = ioremap_nocache(DFU_LOAD_ADDR, (128*1024)); -+ p_tmp_fw_base = p_dfu_fw_base; -+ if (p_dfu_fw_base == NULL) { -+ up(&sema_ipc); -+ iounmap(p_dfu_mailbox_base); -+ return E_PMIC_MALLOC; -+ } -+ -+ mrst_fw_buf = mrst_fw_buf+0x800; -+ memcpy((u8 *)(p_dfu_fw_base), mrst_fw_buf, 0x20000); -+ pMailBox->IAFlag = 0x1; -+ while (pMailBox->SCUFlag == 1) -+ udelay(100); -+ -+ /* check for 'BADN' */ -+ if (pMailBox->Mailbox == BADN) { -+ up(&sema_ipc); -+ iounmap(p_tmp_fw_base); -+ iounmap(p_dfu_mailbox_base); -+ return -1; -+ } -+ -+ iounmap(p_tmp_fw_base); -+ } else { -+ up(&sema_ipc); -+ iounmap(p_dfu_mailbox_base); -+ return -1; -+ } -+ -+ while (pMailBox->Mailbox != TxHI) -+ udelay(10000); -+ udelay(10000); -+ -+ if (pMailBox->Mailbox == TxHI) { -+ /* Map the memory of ipc1 PMIC reg base */ -+ p_dfu_fw_base = ioremap_nocache(DFU_LOAD_ADDR, (128*1024)); -+ p_tmp_fw_base = p_dfu_fw_base; -+ if (p_dfu_fw_base == NULL) { -+ up(&sema_ipc); -+ iounmap(p_dfu_mailbox_base); -+ return E_PMIC_MALLOC; -+ } -+ -+ mrst_fw_buf = mrst_fw_buf+0x20000; -+ memcpy((u8 *)(p_dfu_fw_base), mrst_fw_buf, 0x20000); -+ pMailBox->IAFlag = 0; -+ while (pMailBox->SCUFlag == 0) -+ udelay(100); -+ -+ /* check for 'BADN' */ -+ if (pMailBox->Mailbox == BADN) { -+ up(&sema_ipc); -+ iounmap(p_tmp_fw_base); -+ iounmap(p_dfu_mailbox_base); -+ return -1; -+ } -+ -+ iounmap(p_tmp_fw_base); -+ } else { -+ up(&sema_ipc); -+ iounmap(p_dfu_mailbox_base); -+ return -1; -+ } -+ -+ if (pMailBox->Mailbox == TxLO) { -+ ++retry_cnt; -+ goto update_retry; -+ } -+ -+ if (pMailBox->Mailbox == DONE) -+ printk(KERN_INFO "Firmware update completed!\n"); -+ -+exit_function: -+ iounmap(p_dfu_mailbox_base); -+ up(&sema_ipc); -+ -+ return SUCCESS; -+} -+EXPORT_SYMBOL(ipc_device_fw_upgrade); -+ -+static int cache_mrst_firmware_version(void) -+{ -+ union ipc_sts ipc_sts_reg; -+ int i = 0; -+ -+ mutex_lock(&mrst_ipc_mutex); -+ -+ /*execute the command by writing to IPC_CMD registers*/ -+ writel(IPC_GET_FW_VERSION, (p_ipc_base + IPC_CMD)); -+ udelay(1000); -+ -+ ipc_sts_reg.ipc_sts_data = readl(p_ipc_base + IPC_STS); -+ if (ipc_sts_reg.ipc_sts_parts.error) { -+ printk(KERN_ERR "IPC GetSCUFW Version Command failed %d\n", -+ ipc_sts_reg.ipc_sts_parts.error); -+ up(&sema_ipc); -+ return -EBUSY; -+ } -+ if (ipc_sts_reg.ipc_sts_parts.busy) { -+ printk(KERN_ERR "SCU is busy %d\n", -+ ipc_sts_reg.ipc_sts_parts.busy); -+ up(&sema_ipc); -+ return -EBUSY; -+ } -+ -+ for (i = 0; i < 16 ; i++) -+ fw_ver_data[i] = readb(p_ipc_base + IPC_RBUF + i); -+ mutex_unlock(&mrst_ipc_mutex); -+ return 0; -+} -+ -+MODULE_AUTHOR("Sreenidhi Gurudatt <sreenidhi.b.gurudatt@intel.com>"); -+MODULE_DESCRIPTION("Intel Moorestown IPC driver"); -+MODULE_LICENSE("GPL"); -+ -+module_init(ipc_mrst_init); -+module_exit(ipc_mrst_exit); -Index: linux-2.6.33/arch/x86/kernel/ipc_mrst.h -=================================================================== ---- /dev/null -+++ linux-2.6.33/arch/x86/kernel/ipc_mrst.h -@@ -0,0 +1,241 @@ -+/* -+ * ipc_mrst.h: Driver for Langwell IPC1 -+ * -+ * (C) Copyright 2008 Intel Corporation -+ * Author: Sreenidhi Gurudatt (sreenidhi.b.gurudatt@intel.com) -+ * -+ * This program is free software; you can redistribute it and/or -+ * modify it under the terms of the GNU General Public License -+ * as published by the Free Software Foundation; version 2 -+ * of the License. -+ * -+ * Note: -+ * Langwell provides two IPC units to communicate with IA host. IPC1 is -+ * dedicated for IA. IPC commands results in LNW SCU interrupt. The -+ * initial implementation of this driver is platform specific. It will be -+ * converted to a PCI driver once SCU FW is in place. -+ */ -+#ifndef __IPC_MRST_H__ -+#define __IPC_MRST_H__ -+ -+#include <linux/interrupt.h> -+#include <linux/workqueue.h> -+ -+#define MRST_IPC_DRIVER_VERSION "0.01.004" -+#define IPC_TIMEOUT 10 /*in msecs*/ -+#define MAX_RETRY_CNT 10 -+#define MAX_NB_BUF_SIZE 100 -+#define IPC_BUF_LEN 16 -+#define MAX_NUM_ENTRIES 5 -+#define USLEEP_STS_TIMEOUT 10 -+ -+#define LNW_IPC1_BASE 0xff11c000 -+#define LNW_IPC1_MMAP_SIZE 1024 -+ -+#define LNW_IPC1 -+#define LNW_IPC_CMD 0x00 -+#define LNW_IPC_STS 0x04 -+#define LNW_IPC_DPTR 0x08 -+#define LNW_IPC_WBUF 0x80 -+#define LNW_IPC_RBUF 0x90 -+#define LNW_IPC_RWBUF_SIZE 16 -+ -+/* IPC status register layout */ -+#define LNW_IPC_STS_BUSY (1<<0) -+#define LNW_IPC_STS_ERR (1<<1) -+#define LNW_IPC_STS_CMDID (0xF<<4) -+#define LNW_IPC_STS_INITID (0xFF<<8) -+#define LNW_IPC_STS_ERR_CODE (0xFF<<16) -+ -+/* IPC command register layout */ -+#define LNW_IPC_CMD_CMD (0xFF<<0) -+#define LNW_IPC_CMD_MSI (1<<8) -+#define LNW_IPC_CMD_ID (0xF<<12) -+#define LNW_IPC_CMD_SIZE (0xFF<<16) -+ -+#define FW_UPGRADE_READY_CMD 0x10FE -+#define FW_UPGRADE_GO_CMD 0x20FE -+#define DFU_MAILBOX_ADDR 0xFFFFDFF4 -+#define IPC_CMD_GO_TO_DFU_MODE 0x0001 -+#define IPC_CMD_UPDATE_FW 0x0002 -+#define IPC_CMD_FORCE_UPDATE_FW 0x0003 -+ -+/*256K storage size for loading the FW image.*/ -+#define MAX_FW_SIZE 262144 -+#define MIP_HEADER_SIZE 2048 -+#define DONE 0x444f4e45 -+#define BADN 0x4241444E -+#define TxHI 0x54784849 -+#define TxLO 0x54784c4f -+ -+typedef struct { -+ volatile unsigned int Mailbox; -+ volatile unsigned int SCUFlag; -+ volatile unsigned int IAFlag; -+} MailBox_t; -+ -+enum IPC_CMD { -+ NORMAL_WRITE, /*0x00 Normal Write */ -+ MSG_WRITE, /*0x01 Message Write */ -+ INDIRECT_READ, /*0x02 Indirect Read */ -+ RSVD, /*0x03 Reserved */ -+ READ_DMA, /*0x04 Read DMA */ -+ INDIRECT_WRITE, /*0x05 Indirect write */ -+}; -+ -+int lnw_ipc_send_cmd(unsigned char cmd, int size, int msi); -+ -+struct ipc_driver { -+ const char *name; -+ irqreturn_t(*irq) (int irq, void *ipc); -+ int flags; -+}; -+ -+/* -+ * defines specific to ipc_driver and -+ * not exposed outside -+ */ -+ -+/*cmd_ID fields for CCA Read/Writes*/ -+ -+#define CCA_REG_WRITE 0x0000 -+#define CCA_REG_READ 0x0001 -+#define CCA_REG_GET_PROP 0x0002 -+ -+#define IPC_SET_WATCHDOG_TIMER 0xF8 -+#define IPC_CCA_CMD_READ_WRITE 0xEF -+#define IPC_DEVICE_FIRMWARE_UPGRADE 0xFE -+#define IPC_PMIC_CMD_READ_WRITE 0xFF -+#define IPC_GET_FW_VERSION 0xF4 -+ -+/*cmd_ID fields for CCA Read/Writes*/ -+#define PMIC_REG_WRITE 0x0000 -+#define PMIC_REG_READ 0x0001 -+#define PMIC_REG_READ_MODIFY 0x0002 -+#define LPE_READ 0x0003 -+#define LPE_WRITE 0x0004 -+ -+#define IPC_CMD_GO_TO_DFU_MODE 0x0001 -+#define IPC_CMD_UPDATE_FW 0x0002 -+#define IPC_CMD_FORCE_UPDATE_FW 0x0003 -+ -+#define NORMAL_WRITE 0x00 -+#define MESSAGE_WRITE 0x01 -+#define INDIRECT_READ 0x02 -+#define INDIRECT_WRITE 0x05 -+#define READ_DMA 0x04 -+ -+ -+/* Used to override user option */ -+#define IOC 1 -+ -+#define IPC_REG_ISR_FAILED 0xFF -+ -+/* -+ * IO remap functions for PMIC Register reads -+ * and writes. -+ */ -+ -+#ifdef UNIT_TEST -+#define IOREMAP(x, y) \ -+ kmalloc((y), GFP_KERNEL); -+ -+#define IOUNMAP(x) \ -+ kfree((x)); -+ -+#define IOREAD32(x) \ -+ *(u32 *) (x); -+ -+#define IOWRITE32(x, y) \ -+ *(u32 *) (y) = x; -+#else -+ -+#define IOREMAP(x, y) \ -+ ioremap_nocache((x), (y)); -+ -+#define IOUNMAP(x) \ -+ iounmap((x)); -+ -+#define IOREAD32(x) \ -+ ioread32((x)); -+ -+#define IOWRITE32(x, y) \ -+ iowrite32((x), (y)); -+ -+#endif -+ -+/********************************************* -+ * Define IPC_Base_Address and offsets -+ ********************************************/ -+#define IPC_BASE_ADDRESS 0xFF11C000 -+#define I2C_SER_BUS 0xFF12B000 -+#define DFU_LOAD_ADDR 0xFFFC0000 -+/*256K storage size for loading the FW image.*/ -+#define MAX_FW_SIZE 262144 -+ -+#define NOP_CMD 0x00 -+#define WRITE_CMD 0x01 -+#define READ_CMD 0x02 -+ -+/* IPC2 offset addresses */ -+#define IPC_MAX_ADDRESS 0x100 -+/* I2C offset addresses - Confirm this */ -+#define I2C_MAX_ADDRESS 0x10 -+/* Offsets for CTRL_REG_ADDR and CTRL_REG_DATA */ -+#define CTRL_REG_ADDR 0x00 -+#define CTRL_REG_DATA 0x04 -+#define I2C_MAX_ADDRESS 0x10 -+ -+#define IPC_CMD 0x00 -+#define IPC_STS 0x04 -+#define IPC_SPTR 0x08 -+#define IPC_DPTR 0x0C -+#define IPC_WBUF 0x80 -+#define IPC_RBUF 0x90 -+ -+#define MAX_INSTANCES_ALLOWED 1 -+ -+union ipc_sts { -+ struct { -+ u32 busy:1; -+ u32 error:1; -+ u32 rfu1:2; -+ u32 cmd_id:4; -+ u32 initiator_id:8; -+ u32 error_code:8; -+ u32 rfu3:8; -+ } ipc_sts_parts; -+ u32 ipc_sts_data; -+}; -+ -+union ipc_fw_cmd { -+ struct { -+ u32 cmd:8; -+ u32 ioc:1; -+ u32 rfu1:3; -+ u32 cmd_ID:4; -+ u32 size:8; -+ u32 rfu2:8; -+ } cmd_parts; -+ u32 cmd_data; -+}; -+ -+struct ipc_intr { -+ u8 cmd; -+ u32 data; -+ -+}; -+ -+struct ipc_work_struct{ -+ struct work_struct ipc_work; -+ unsigned int cmd_id; -+}; -+ -+int ipc_process_interrupt(struct ipc_intr intr_data); -+int init_ipc_driver(void); -+int de_init_ipc_driver(void); -+static int cache_mrst_firmware_version(void); -+static void mrst_pmic_read_handler(struct work_struct *work); -+static DECLARE_DELAYED_WORK(mrst_ipc, mrst_pmic_read_handler); -+ -+#endif -Index: linux-2.6.33/drivers/input/keyboard/gpio_keys.c -=================================================================== ---- linux-2.6.33.orig/drivers/input/keyboard/gpio_keys.c -+++ linux-2.6.33/drivers/input/keyboard/gpio_keys.c -@@ -45,6 +45,9 @@ static void gpio_keys_report_event(struc - int state = (gpio_get_value(button->gpio) ? 1 : 0) ^ button->active_low; - - input_event(input, type, button->code, !!state); -+ /* if button disabled auto repeat */ -+ if (state && test_bit(EV_REP, input->evbit) && button->norep) -+ input_event(input, type, button->code, 0); - input_sync(input); - } - -Index: linux-2.6.33/include/linux/gpio_keys.h -=================================================================== ---- linux-2.6.33.orig/include/linux/gpio_keys.h -+++ linux-2.6.33/include/linux/gpio_keys.h -@@ -10,6 +10,7 @@ struct gpio_keys_button { - int type; /* input event type (EV_KEY, EV_SW) */ - int wakeup; /* configure the button as a wake-up source */ - int debounce_interval; /* debounce ticks interval in msecs */ -+ unsigned int norep:1; /* more precise auto repeat control */ - }; - - struct gpio_keys_platform_data { -Index: linux-2.6.33/drivers/gpio/Kconfig -=================================================================== ---- linux-2.6.33.orig/drivers/gpio/Kconfig -+++ linux-2.6.33/drivers/gpio/Kconfig -@@ -224,6 +224,12 @@ config GPIO_TIMBERDALE - - comment "SPI GPIO expanders:" - -+config GPIO_LANGWELL_PMIC -+ bool "Intel Moorestown Platform Langwell GPIO support" -+ depends on SPI_MASTER -+ help -+ Say Y here to support Intel Moorestown platform GPIO. -+ - config GPIO_MAX7301 - tristate "Maxim MAX7301 GPIO expander" - depends on SPI_MASTER -Index: linux-2.6.33/drivers/gpio/Makefile -=================================================================== ---- linux-2.6.33.orig/drivers/gpio/Makefile -+++ linux-2.6.33/drivers/gpio/Makefile -@@ -7,6 +7,7 @@ obj-$(CONFIG_GPIOLIB) += gpiolib.o - obj-$(CONFIG_GPIO_ADP5520) += adp5520-gpio.o - obj-$(CONFIG_GPIO_ADP5588) += adp5588-gpio.o - obj-$(CONFIG_GPIO_LANGWELL) += langwell_gpio.o -+obj-$(CONFIG_GPIO_LANGWELL_PMIC) += langwell_pmic_gpio.o - obj-$(CONFIG_GPIO_MAX7301) += max7301.o - obj-$(CONFIG_GPIO_MAX732X) += max732x.o - obj-$(CONFIG_GPIO_MC33880) += mc33880.o -Index: linux-2.6.33/drivers/gpio/langwell_pmic_gpio.c -=================================================================== ---- /dev/null -+++ linux-2.6.33/drivers/gpio/langwell_pmic_gpio.c -@@ -0,0 +1,331 @@ -+/* Moorestown PMIC GPIO (access through SPI and IPC) driver -+ * Copyright (c) 2008 - 2009, Intel Corporation. -+ * -+ * This program is free software; you can redistribute it and/or modify -+ * it under the terms of the GNU General Public License version 2 as -+ * published by the Free Software Foundation. -+ * -+ * This program is distributed in the hope that it will be useful, -+ * but WITHOUT ANY WARRANTY; without even the implied warranty of -+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -+ * GNU General Public License for more details. -+ * -+ * You should have received a copy of the GNU General Public License -+ * along with this program; if not, write to the Free Software -+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. -+ */ -+ -+/* Supports: -+ * Moorestown platform pmic chip -+ */ -+ -+#include <linux/module.h> -+#include <linux/kernel.h> -+#include <linux/interrupt.h> -+#include <linux/delay.h> -+#include <linux/stddef.h> -+#include <linux/ioport.h> -+#include <linux/init.h> -+#include <linux/io.h> -+#include <linux/device.h> -+#include <linux/pci.h> -+#include <linux/spi/spi.h> -+#include <linux/spi/langwell_pmic_gpio.h> -+#include <linux/gpio.h> -+#include <asm/ipc_defs.h> -+ -+/* register offset that IPC driver should use -+ * 8 GPIO + 8 GPOSW + 8GPO -+ */ -+enum pmic_gpio_register { -+ GPIO0 = 0xE0, -+ GPIO7 = 0xE7, -+ GPIOINT = 0xE8, -+ GPOSWCTL0 = 0xEC, -+ GPOSWCTL5 = 0xF1, -+ GPO = 0xF4, -+}; -+ -+/* bits definitions for GPIO & GPOSW */ -+#define GPIO_DRV 0x01 -+#define GPIO_DIR 0x02 -+#define GPIO_DIN 0x04 -+#define GPIO_DOU 0x08 -+#define GPIO_INTCTL 0x30 -+#define GPIO_DBC 0xc0 -+ -+#define GPOSW_DRV 0x01 -+#define GPOSW_DOU 0x08 -+#define GPOSW_RDRV 0x30 -+ -+/* to schedule ipc read_modify in work queue for irq context */ -+#define MAX_IPC_QUEUE 16 -+struct ipc_cmd_queue { -+ struct ipc_pmic_mod_reg_data cmd[MAX_IPC_QUEUE]; -+ struct work_struct work; -+}; -+ -+struct pmic_gpio { -+ struct gpio_chip chip; -+ struct ipc_cmd_queue cmd_queue; -+ void *gpiointr; -+ int irq; -+ struct spi_device *spi; -+ unsigned irq_base; -+}; -+ -+static int ipc_read_char(u16 offset) -+{ -+ struct ipc_pmic_reg_data tmp; -+ tmp.ioc = 0; -+ tmp.pmic_reg_data[0].register_address = offset; -+ tmp.num_entries = 1; -+ if (ipc_pmic_register_read(&tmp)) { -+ printk(KERN_ERR "%s: IPC read error\n", __func__); -+ return 0; -+ } -+ return tmp.pmic_reg_data[0].value; -+} -+ -+static int ipc_modify_char(u16 offset, u8 value, u8 mask) -+{ -+ struct ipc_pmic_mod_reg_data tmp; -+ -+ tmp.ioc = 0; -+ tmp.pmic_mod_reg_data[0].register_address = offset; -+ tmp.pmic_mod_reg_data[0].value = value; -+ tmp.pmic_mod_reg_data[0].bit_map = mask; -+ tmp.num_entries = 1; -+ return ipc_pmic_register_read_modify(&tmp); -+} -+ -+static int queue_ipc_modify_char(struct pmic_gpio *pg, -+ u16 offset, u8 value, u8 mask) -+{ -+ struct ipc_pmic_mod_reg_data *tmp; -+ int i; -+ -+ for (i = 0; i < MAX_IPC_QUEUE; i ++) { -+ tmp = &pg->cmd_queue.cmd[i]; -+ if (tmp->num_entries) -+ continue; -+ tmp->ioc = 0; -+ tmp->pmic_mod_reg_data[0].register_address = offset; -+ tmp->pmic_mod_reg_data[0].value = value; -+ tmp->pmic_mod_reg_data[0].bit_map = mask; -+ tmp->num_entries=1; -+ return i; -+ } -+ return -1; -+} -+ -+static void ipc_modify_char_work(struct work_struct *work) -+{ -+ struct pmic_gpio *pg = -+ container_of(work, struct pmic_gpio, cmd_queue.work); -+ struct ipc_pmic_mod_reg_data *tmp; -+ int i; -+ -+ for (i = 0; i < MAX_IPC_QUEUE; i ++) { -+ tmp = &pg->cmd_queue.cmd[i]; -+ if (tmp->num_entries) { -+ ipc_pmic_register_read_modify(tmp); -+ tmp->num_entries = 0; -+ } -+ } -+} -+ -+static int pmic_gpio_direction_input(struct gpio_chip *chip, unsigned offset) -+{ -+ if (offset > 8) { -+ printk(KERN_ERR -+ "%s: only pin 0-7 support input\n", __func__); -+ return -1;/* we only have 8 GPIO can use as input */ -+ } -+ return ipc_modify_char(GPIO0 + offset, GPIO_DIR, GPIO_DIR); -+} -+ -+static int pmic_gpio_direction_output(struct gpio_chip *chip, -+ unsigned offset, int value) -+{ -+ int rc = 0; -+ -+ if (offset < 8)/* it is GPIO */ -+ rc = ipc_modify_char(GPIO0 + offset, -+ GPIO_DRV | (value ? GPIO_DOU : 0), -+ GPIO_DRV | GPIO_DOU | GPIO_DIR); -+ else if (offset < 16)/* it is GPOSW */ -+ rc = ipc_modify_char(GPOSWCTL0 + offset - 8, -+ GPOSW_DRV | (value ? GPOSW_DOU : 0), -+ GPOSW_DRV | GPOSW_DOU | GPOSW_RDRV); -+ else if (offset < 24)/* it is GPO */ -+ rc = ipc_modify_char(GPO, value ? 1 << (offset - 16) : 0, -+ 1 << (offset - 16)); -+ -+ return rc; -+} -+ -+static int pmic_gpio_get(struct gpio_chip *chip, unsigned offset) -+{ -+ /* we only have 8 GPIO can use as input */ -+ if (offset > 8) { -+ printk(KERN_ERR -+ "%s: only pin 0-7 support input\n", __func__); -+ return -1; -+ } -+ return ipc_read_char(GPIO0 + offset) & GPIO_DIN; -+} -+ -+static void pmic_gpio_set(struct gpio_chip *chip, unsigned offset, int value) -+{ -+ if (offset < 8)/* it is GPIO */ -+ ipc_modify_char(GPIO0 + offset, -+ GPIO_DRV | (value ? GPIO_DOU : 0), -+ GPIO_DRV | GPIO_DOU); -+ else if (offset < 16)/* it is GPOSW */ -+ ipc_modify_char(GPOSWCTL0 + offset - 8, -+ GPOSW_DRV | (value ? GPOSW_DOU : 0), -+ GPOSW_DRV | GPOSW_DOU | GPOSW_RDRV); -+ else if (offset < 24)/* it is GPO */ -+ ipc_modify_char(GPO, value ? 1 << (offset - 16) : 0, -+ 1 << (offset - 16)); -+} -+ -+static int pmic_irq_type(unsigned irq, unsigned type) -+{ -+ struct pmic_gpio *pg = get_irq_chip_data(irq); -+ u32 gpio = irq - pg->irq_base; -+ -+ if (gpio < 0 || gpio > pg->chip.ngpio) -+ return -EINVAL; -+ -+ if (type & IRQ_TYPE_EDGE_RISING) -+ queue_ipc_modify_char(pg, GPIO0 + gpio, 0x20, 0x20); -+ else -+ queue_ipc_modify_char(pg, GPIO0 + gpio, 0x00, 0x20); -+ -+ if (type & IRQ_TYPE_EDGE_FALLING) -+ queue_ipc_modify_char(pg, GPIO0 + gpio, 0x10, 0x10); -+ else -+ queue_ipc_modify_char(pg, GPIO0 + gpio, 0x00, 0x10); -+ -+ schedule_work(&pg->cmd_queue.work); -+ return 0; -+}; -+ -+static int pmic_gpio_to_irq(struct gpio_chip *chip, unsigned offset) -+{ -+ struct pmic_gpio *pg = container_of(chip, struct pmic_gpio, chip); -+ -+ return pg->irq_base + offset; -+} -+ -+/* the gpiointr register is read-clear, so just do nothing. */ -+static void pmic_irq_unmask(unsigned irq) -+{ -+}; -+ -+static void pmic_irq_mask(unsigned irq) -+{ -+}; -+ -+static struct irq_chip pmic_irqchip = { -+ .name = "PMIC-GPIO", -+ .mask = pmic_irq_mask, -+ .unmask = pmic_irq_unmask, -+ .set_type = pmic_irq_type, -+}; -+ -+static void pmic_irq_handler(unsigned irq, struct irq_desc *desc) -+{ -+ struct pmic_gpio *pg = (struct pmic_gpio *)get_irq_data(irq); -+ u8 intsts = *((u8 *)pg->gpiointr + 4); -+ int gpio; -+ -+ for (gpio = 0; gpio < 8; gpio++) { -+ if (intsts & (1 << gpio)) { -+ pr_debug("pmic pin %d triggered\n", gpio); -+ generic_handle_irq(pg->irq_base + gpio); -+ } -+ } -+ desc->chip->eoi(irq); -+} -+ -+static int __devinit pmic_gpio_probe(struct spi_device *spi) -+{ -+ struct pmic_gpio *pg; -+ struct langwell_pmic_gpio_platform_data *pdata; -+ int retval; -+ int i; -+ -+ printk(KERN_INFO "%s: PMIC GPIO driver loaded.\n", __func__); -+ -+ pdata = spi->dev.platform_data; -+ if (!pdata || !pdata->gpio_base || !pdata->irq_base) { -+ dev_dbg(&spi->dev, "incorrect or missing platform data\n"); -+ return -EINVAL; -+ } -+ -+ pg = kzalloc(sizeof(*pg), GFP_KERNEL); -+ if (!pg) -+ return -ENOMEM; -+ -+ dev_set_drvdata(&spi->dev, pg); -+ -+ pg->irq = spi->irq; -+ /* setting up SRAM mapping for GPIOINT register */ -+ pg->gpiointr = ioremap_nocache(pdata->gpiointr, 8); -+ if (!pg->gpiointr) { -+ printk(KERN_ERR "%s: Can not map GPIOINT.\n", __func__); -+ retval = -EINVAL; -+ goto err2; -+ } -+ pg->irq_base = pdata->irq_base; -+ pg->chip.label = "langwell_pmic"; -+ pg->chip.direction_input = pmic_gpio_direction_input; -+ pg->chip.direction_output = pmic_gpio_direction_output; -+ pg->chip.get = pmic_gpio_get; -+ pg->chip.set = pmic_gpio_set; -+ pg->chip.to_irq = pmic_gpio_to_irq; -+ pg->chip.base = pdata->gpio_base; -+ pg->chip.ngpio = 24; -+ pg->chip.can_sleep = 1; -+ pg->chip.dev = &spi->dev; -+ retval = gpiochip_add(&pg->chip); -+ if (retval) { -+ printk(KERN_ERR "%s: Can not add pmic gpio chip.\n", __func__); -+ goto err; -+ } -+ set_irq_data(pg->irq, pg); -+ set_irq_chained_handler(pg->irq, pmic_irq_handler); -+ for (i = 0; i < 8; i++) { -+ set_irq_chip_and_handler_name(i + pg->irq_base, &pmic_irqchip, -+ handle_simple_irq, "demux"); -+ set_irq_chip_data(i + pg->irq_base, pg); -+ } -+ INIT_WORK(&pg->cmd_queue.work, ipc_modify_char_work); -+ return 0; -+err: -+ iounmap(pg->gpiointr); -+err2: -+ kfree(pg); -+ return retval; -+} -+ -+static struct spi_driver pmic_gpio_driver = { -+ .driver = { -+ .name = "pmic_gpio", -+ .owner = THIS_MODULE, -+ }, -+ .probe = pmic_gpio_probe, -+}; -+ -+static int __init pmic_gpio_init(void) -+{ -+ return spi_register_driver(&pmic_gpio_driver); -+} -+ -+/* register after spi postcore initcall and before -+ * subsys initcalls that may rely on these GPIOs -+ */ -+subsys_initcall(pmic_gpio_init); -Index: linux-2.6.33/include/linux/spi/langwell_pmic_gpio.h -=================================================================== ---- /dev/null -+++ linux-2.6.33/include/linux/spi/langwell_pmic_gpio.h -@@ -0,0 +1,15 @@ -+#ifndef LINUX_SPI_LANGWELL_PMIC_H -+#define LINUX_SPI_LANGWELL_PMIC_H -+ -+struct langwell_pmic_gpio_platform_data { -+ /* the first IRQ of the chip */ -+ unsigned irq_base; -+ /* number assigned to the first GPIO */ -+ unsigned gpio_base; -+ /* sram address for gpiointr register, the langwell chip will map -+ * the PMIC spi GPIO expander's GPIOINTR register in sram. -+ */ -+ unsigned gpiointr; -+}; -+ -+#endif -Index: linux-2.6.33/drivers/gpio/pca953x.c -=================================================================== ---- linux-2.6.33.orig/drivers/gpio/pca953x.c -+++ linux-2.6.33/drivers/gpio/pca953x.c -@@ -14,6 +14,7 @@ - #include <linux/module.h> - #include <linux/init.h> - #include <linux/gpio.h> -+#include <linux/interrupt.h> - #include <linux/i2c.h> - #include <linux/i2c/pca953x.h> - #ifdef CONFIG_OF_GPIO -@@ -50,6 +51,7 @@ MODULE_DEVICE_TABLE(i2c, pca953x_id); - - struct pca953x_chip { - unsigned gpio_start; -+ unsigned irq_base; - uint16_t reg_output; - uint16_t reg_direction; - -@@ -182,6 +184,13 @@ static void pca953x_gpio_set_value(struc - chip->reg_output = reg_val; - } - -+static int pca953x_gpio_to_irq(struct gpio_chip *gc, unsigned offset) -+{ -+ struct pca953x_chip *chip = container_of(gc, struct pca953x_chip, -+ gpio_chip); -+ return chip->irq_base + offset; -+} -+ - static void pca953x_setup_gpio(struct pca953x_chip *chip, int gpios) - { - struct gpio_chip *gc; -@@ -192,6 +201,7 @@ static void pca953x_setup_gpio(struct pc - gc->direction_output = pca953x_gpio_direction_output; - gc->get = pca953x_gpio_get_value; - gc->set = pca953x_gpio_set_value; -+ gc->to_irq = pca953x_gpio_to_irq; - gc->can_sleep = 1; - - gc->base = chip->gpio_start; -@@ -250,6 +260,39 @@ pca953x_get_alt_pdata(struct i2c_client - } - #endif - -+static void pca953x_irq_unmask(unsigned irq) -+{ -+} -+ -+static void pca953x_irq_mask(unsigned irq) -+{ -+} -+ -+static struct irq_chip pca953x_irqchip = { -+ .name = "pca953x", -+ .mask = pca953x_irq_mask, -+ .unmask = pca953x_irq_unmask, -+}; -+ -+static void pca953x_irq_handler(unsigned irq, struct irq_desc *desc) -+{ -+ struct pca953x_chip *chip = (struct pca953x_chip *)get_irq_data(irq); -+ int i; -+ -+ if (desc->chip->ack) -+ desc->chip->ack(irq); -+ /* we must call all sub-irqs, since there is no way to read -+ * I2C gpio expander's status in irq context. The driver itself -+ * would be reponsible to check if the irq is for him. -+ */ -+ for (i = 0; i < chip->gpio_chip.ngpio; i++) -+ if (chip->reg_direction & (1u << i)) -+ generic_handle_irq(chip->irq_base + i); -+ -+ if (desc->chip->unmask) -+ desc->chip->unmask(irq); -+} -+ - static int __devinit pca953x_probe(struct i2c_client *client, - const struct i2c_device_id *id) - { -@@ -283,6 +326,8 @@ static int __devinit pca953x_probe(struc - - chip->names = pdata->names; - -+ chip->irq_base = pdata->irq_base; -+ - /* initialize cached registers from their original values. - * we can't share this chip with another i2c master. - */ -@@ -314,6 +359,21 @@ static int __devinit pca953x_probe(struc - } - - i2c_set_clientdata(client, chip); -+ -+ if (chip->irq_base != (unsigned)-1) { -+ int i; -+ -+ set_irq_type(client->irq, -+ IRQ_TYPE_EDGE_FALLING | IRQ_TYPE_EDGE_RISING); -+ set_irq_data(client->irq, chip); -+ for (i = 0; i < chip->gpio_chip.ngpio; i++) { -+ set_irq_chip(i + chip->irq_base, &pca953x_irqchip); -+ __set_irq_handler(i + chip->irq_base, -+ handle_simple_irq, 0, "demux"); -+ set_irq_chip_data(i + chip->irq_base, chip); -+ } -+ set_irq_chained_handler(client->irq, pca953x_irq_handler); -+ } - return 0; - - out_failed: -Index: linux-2.6.33/include/linux/i2c/pca953x.h -=================================================================== ---- linux-2.6.33.orig/include/linux/i2c/pca953x.h -+++ linux-2.6.33/include/linux/i2c/pca953x.h -@@ -1,6 +1,8 @@ - /* platform data for the PCA9539 16-bit I/O expander driver */ - - struct pca953x_platform_data { -+ /* number of the first IRQ */ -+ unsigned irq_base; - /* number of the first GPIO */ - unsigned gpio_base; - -Index: linux-2.6.33/drivers/input/keyboard/Kconfig -=================================================================== ---- linux-2.6.33.orig/drivers/input/keyboard/Kconfig -+++ linux-2.6.33/drivers/input/keyboard/Kconfig -@@ -73,7 +73,7 @@ config KEYBOARD_ATKBD - default y - select SERIO - select SERIO_LIBPS2 -- select SERIO_I8042 if X86 -+ select SERIO_I8042 if X86 && !X86_MRST - select SERIO_GSCPS2 if GSC - help - Say Y here if you want to use a standard AT or PS/2 keyboard. Usually -Index: linux-2.6.33/drivers/input/mouse/Kconfig -=================================================================== ---- linux-2.6.33.orig/drivers/input/mouse/Kconfig -+++ linux-2.6.33/drivers/input/mouse/Kconfig -@@ -17,7 +17,7 @@ config MOUSE_PS2 - default y - select SERIO - select SERIO_LIBPS2 -- select SERIO_I8042 if X86 -+ select SERIO_I8042 if X86 && !X86_MRST - select SERIO_GSCPS2 if GSC - help - Say Y here if you have a PS/2 mouse connected to your system. This -Index: linux-2.6.33/kernel/time/tick-broadcast.c -=================================================================== ---- linux-2.6.33.orig/kernel/time/tick-broadcast.c -+++ linux-2.6.33/kernel/time/tick-broadcast.c -@@ -214,10 +214,13 @@ static void tick_do_broadcast_on_off(uns - - raw_spin_lock_irqsave(&tick_broadcast_lock, flags); - -+ bc = tick_broadcast_device.evtdev; -+ if (!bc) -+ goto out; -+ - cpu = smp_processor_id(); - td = &per_cpu(tick_cpu_device, cpu); - dev = td->evtdev; -- bc = tick_broadcast_device.evtdev; - - /* - * Is the device not affected by the powerstate ? -@@ -467,6 +470,9 @@ void tick_broadcast_oneshot_control(unsi - goto out; - - bc = tick_broadcast_device.evtdev; -+ if (!bc) -+ goto out; -+ - cpu = smp_processor_id(); - td = &per_cpu(tick_cpu_device, cpu); - dev = td->evtdev; -Index: linux-2.6.33/drivers/usb/core/hcd.h -=================================================================== ---- linux-2.6.33.orig/drivers/usb/core/hcd.h -+++ linux-2.6.33/drivers/usb/core/hcd.h -@@ -104,6 +104,9 @@ struct usb_hcd { - unsigned wireless:1; /* Wireless USB HCD */ - unsigned authorized_default:1; - unsigned has_tt:1; /* Integrated TT in root hub */ -+ unsigned has_sram:1; /* Local SRAM for caching */ -+ unsigned sram_no_payload:1; /* sram not for payload */ -+ unsigned lpm_cap:1; /* LPM capable */ - - int irq; /* irq allocated */ - void __iomem *regs; /* device memory/io */ -@@ -148,6 +151,13 @@ struct usb_hcd { - * (ohci 32, uhci 1024, ehci 256/512/1024). - */ - -+#ifdef CONFIG_USB_OTG -+ /* some otg HCDs need this to get USB_DEVICE_ADD and USB_DEVICE_REMOVE -+ * from root hub, we do not want to use USB notification chain, since -+ * it would be a over kill to use high level notification. -+ */ -+ void (*otg_notify) (struct usb_device *udev, unsigned action); -+#endif - /* The HC driver's private data is stored at the end of - * this structure. - */ -Index: linux-2.6.33/drivers/usb/core/hub.c -=================================================================== ---- linux-2.6.33.orig/drivers/usb/core/hub.c -+++ linux-2.6.33/drivers/usb/core/hub.c -@@ -1563,6 +1563,24 @@ static void hub_free_dev(struct usb_devi - hcd->driver->free_dev(hcd, udev); - } - -+#ifdef CONFIG_USB_OTG -+ -+static void otg_notify(struct usb_device *udev, unsigned action) -+{ -+ struct usb_hcd *hcd = bus_to_hcd(udev->bus); -+ -+ if (hcd->otg_notify) -+ hcd->otg_notify(udev, action); -+} -+ -+#else -+ -+static inline void otg_notify(struct usb_device *udev, unsigned action) -+{ -+} -+ -+#endif -+ - /** - * usb_disconnect - disconnect a device (usbcore-internal) - * @pdev: pointer to device being disconnected -@@ -1620,7 +1638,7 @@ void usb_disconnect(struct usb_device ** - * notifier chain (used by usbfs and possibly others). - */ - device_del(&udev->dev); -- -+ otg_notify(udev, USB_DEVICE_REMOVE); - /* Free the device number and delete the parent's children[] - * (or root_hub) pointer. - */ -@@ -1833,6 +1851,7 @@ int usb_new_device(struct usb_device *ud - * notifier chain (used by usbfs and possibly others). - */ - err = device_add(&udev->dev); -+ otg_notify(udev, USB_DEVICE_ADD); - if (err) { - dev_err(&udev->dev, "can't device_add, error %d\n", err); - goto fail; -Index: linux-2.6.33/drivers/usb/core/usb.h -=================================================================== ---- linux-2.6.33.orig/drivers/usb/core/usb.h -+++ linux-2.6.33/drivers/usb/core/usb.h -@@ -178,4 +178,3 @@ extern void usb_notify_add_device(struct - extern void usb_notify_remove_device(struct usb_device *udev); - extern void usb_notify_add_bus(struct usb_bus *ubus); - extern void usb_notify_remove_bus(struct usb_bus *ubus); -- -Index: linux-2.6.33/drivers/usb/host/ehci-hcd.c -=================================================================== ---- linux-2.6.33.orig/drivers/usb/host/ehci-hcd.c -+++ linux-2.6.33/drivers/usb/host/ehci-hcd.c -@@ -35,6 +35,7 @@ - #include <linux/moduleparam.h> - #include <linux/dma-mapping.h> - #include <linux/debugfs.h> -+#include <linux/uaccess.h> - - #include "../core/hcd.h" - -@@ -43,6 +44,8 @@ - #include <asm/irq.h> - #include <asm/system.h> - #include <asm/unaligned.h> -+#include <linux/usb/otg.h> -+#include <linux/usb/langwell_otg.h> - - /*-------------------------------------------------------------------------*/ - -@@ -101,6 +104,11 @@ static int ignore_oc = 0; - module_param (ignore_oc, bool, S_IRUGO); - MODULE_PARM_DESC (ignore_oc, "ignore bogus hardware overcurrent indications"); - -+/* for link power management(LPM) feature */ -+static unsigned int hird; -+module_param(hird, int, S_IRUGO); -+MODULE_PARM_DESC(hird, "host initiated resume duration, +1 for each 75us\n"); -+ - #define INTR_MASK (STS_IAA | STS_FATAL | STS_PCD | STS_ERR | STS_INT) - - /*-------------------------------------------------------------------------*/ -@@ -305,6 +313,7 @@ static void end_unlink_async(struct ehci - static void ehci_work(struct ehci_hcd *ehci); - - #include "ehci-hub.c" -+#include "ehci-lpm.c" - #include "ehci-mem.c" - #include "ehci-q.c" - #include "ehci-sched.c" -@@ -501,7 +510,8 @@ static void ehci_stop (struct usb_hcd *h - ehci_work (ehci); - spin_unlock_irq (&ehci->lock); - ehci_mem_cleanup (ehci); -- -+ if (hcd->has_sram) -+ sram_deinit(hcd); - #ifdef EHCI_STATS - ehci_dbg (ehci, "irq normal %ld err %ld reclaim %ld (lost %ld)\n", - ehci->stats.normal, ehci->stats.error, ehci->stats.reclaim, -@@ -577,6 +587,17 @@ static int ehci_init(struct usb_hcd *hcd - if (log2_irq_thresh < 0 || log2_irq_thresh > 6) - log2_irq_thresh = 0; - temp = 1 << (16 + log2_irq_thresh); -+ if (HCC_32FRAME_PERIODIC_LIST(hcc_params)) -+ ehci_dbg(ehci, "32 frame periodic list capable\n"); -+ if (HCC_PER_PORT_CHANGE_EVENT(hcc_params)) { -+ ehci_dbg(ehci, "enable per-port change event %d\n", park); -+ temp |= CMD_PPCEE; -+ } -+ if (HCC_HW_PREFETCH(hcc_params)) { -+ ehci_dbg(ehci, "HW prefetch capable %d\n", park); -+ temp |= (CMD_ASPE | CMD_PSPE); -+ } -+ - if (HCC_CANPARK(hcc_params)) { - /* HW default park == 3, on hardware that supports it (like - * NVidia and ALI silicon), maximizes throughput on the async -@@ -590,7 +611,7 @@ static int ehci_init(struct usb_hcd *hcd - temp |= CMD_PARK; - temp |= park << 8; - } -- ehci_dbg(ehci, "park %d\n", park); -+ ehci_dbg(ehci, "park %d ", park); - } - if (HCC_PGM_FRAMELISTLEN(hcc_params)) { - /* periodic schedule size can be smaller than default */ -@@ -603,6 +624,17 @@ static int ehci_init(struct usb_hcd *hcd - default: BUG(); - } - } -+ if (HCC_LPM(hcc_params)) { -+ /* support link power management EHCI 1.1 addendum */ -+ ehci_dbg(ehci, "lpm\n"); -+ hcd->lpm_cap = 1; -+ if (hird > 0xf) { -+ ehci_dbg(ehci, "hird %d invalid, use default 0", -+ hird); -+ hird = 0; -+ } -+ temp |= hird << 24; -+ } - ehci->command = temp; - - /* Accept arbitrarily long scatter-gather lists */ -@@ -840,6 +872,7 @@ static int ehci_urb_enqueue ( - ) { - struct ehci_hcd *ehci = hcd_to_ehci (hcd); - struct list_head qtd_list; -+ int status; - - INIT_LIST_HEAD (&qtd_list); - -@@ -855,7 +888,16 @@ static int ehci_urb_enqueue ( - default: - if (!qh_urb_transaction (ehci, urb, &qtd_list, mem_flags)) - return -ENOMEM; -- return submit_async(ehci, urb, &qtd_list, mem_flags); -+ status = submit_async(ehci, urb, &qtd_list, mem_flags); -+ -+ /* check device LPM cap after set address */ -+ if (usb_pipecontrol(urb->pipe)) { -+ if (((struct usb_ctrlrequest *)urb->setup_packet) -+ ->bRequest == USB_REQ_SET_ADDRESS && -+ ehci_to_hcd(ehci)->lpm_cap) -+ ehci_lpm_check(ehci, urb->dev->portnum); -+ } -+ return status; - - case PIPE_INTERRUPT: - if (!qh_urb_transaction (ehci, urb, &qtd_list, mem_flags)) -@@ -1101,6 +1143,10 @@ MODULE_LICENSE ("GPL"); - #ifdef CONFIG_PCI - #include "ehci-pci.c" - #define PCI_DRIVER ehci_pci_driver -+#ifdef CONFIG_USB_LANGWELL_OTG -+#include "ehci-langwell-pci.c" -+#define LNW_OTG_HOST_DRIVER ehci_otg_driver -+#endif - #endif - - #ifdef CONFIG_USB_EHCI_FSL -@@ -1213,8 +1259,19 @@ static int __init ehci_hcd_init(void) - if (retval < 0) - goto clean3; - #endif -+ -+#ifdef LNW_OTG_HOST_DRIVER -+ retval = langwell_register_host(&LNW_OTG_HOST_DRIVER); -+ if (retval < 0) -+ goto clean4; -+#endif - return retval; - -+#ifdef LNW_OTG_HOST_DRIVER -+clean4: -+ langwell_unregister_host(&LNW_OTG_HOST_DRIVER); -+#endif -+ - #ifdef OF_PLATFORM_DRIVER - /* of_unregister_platform_driver(&OF_PLATFORM_DRIVER); */ - clean3: -@@ -1255,6 +1312,9 @@ static void __exit ehci_hcd_cleanup(void - #ifdef PS3_SYSTEM_BUS_DRIVER - ps3_ehci_driver_unregister(&PS3_SYSTEM_BUS_DRIVER); - #endif -+#ifdef LNW_OTG_HOST_DRIVER -+ langwell_unregister_host(&LNW_OTG_HOST_DRIVER); -+#endif - #ifdef DEBUG - debugfs_remove(ehci_debug_root); - #endif -Index: linux-2.6.33/drivers/usb/host/ehci-hub.c -=================================================================== ---- linux-2.6.33.orig/drivers/usb/host/ehci-hub.c -+++ linux-2.6.33/drivers/usb/host/ehci-hub.c -@@ -112,6 +112,7 @@ static int ehci_bus_suspend (struct usb_ - int port; - int mask; - u32 __iomem *hostpc_reg = NULL; -+ int rc = 0; - - ehci_dbg(ehci, "suspend root hub\n"); - -@@ -228,13 +229,18 @@ static int ehci_bus_suspend (struct usb_ - ehci_readl(ehci, &ehci->regs->intr_enable); - - ehci->next_statechange = jiffies + msecs_to_jiffies(10); -+ -+#ifdef CONFIG_USB_OTG -+ if (ehci->has_otg && ehci->otg_suspend) -+ rc = ehci->otg_suspend(hcd); -+#endif - spin_unlock_irq (&ehci->lock); - - /* ehci_work() may have re-enabled the watchdog timer, which we do not - * want, and so we must delete any pending watchdog timer events. - */ - del_timer_sync(&ehci->watchdog); -- return 0; -+ return rc; - } - - -@@ -246,6 +252,7 @@ static int ehci_bus_resume (struct usb_h - u32 power_okay; - int i; - u8 resume_needed = 0; -+ int rc = 0; - - if (time_before (jiffies, ehci->next_statechange)) - msleep(5); -@@ -295,7 +302,11 @@ static int ehci_bus_resume (struct usb_h - i = HCS_N_PORTS (ehci->hcs_params); - while (i--) { - temp = ehci_readl(ehci, &ehci->regs->port_status [i]); -- temp &= ~(PORT_RWC_BITS | PORT_WAKE_BITS); -+ temp &= ~(PORT_RWC_BITS | PORT_WKDISC_E | PORT_WKOC_E); -+ if (temp & PORT_CONNECT) -+ temp |= PORT_WKOC_E | PORT_WKDISC_E; -+ else -+ temp |= PORT_WKOC_E | PORT_WKCONN_E; - if (test_bit(i, &ehci->bus_suspended) && - (temp & PORT_SUSPEND)) { - temp |= PORT_RESUME; -@@ -340,9 +351,13 @@ static int ehci_bus_resume (struct usb_h - /* Now we can safely re-enable irqs */ - ehci_writel(ehci, INTR_MASK, &ehci->regs->intr_enable); - -+#ifdef CONFIG_USB_OTG -+ if (ehci->has_otg && ehci->otg_resume) -+ rc = ehci->otg_resume(hcd); -+#endif - spin_unlock_irq (&ehci->lock); - ehci_handover_companion_ports(ehci); -- return 0; -+ return rc; - } - - #else -@@ -678,10 +693,20 @@ static int ehci_hub_control ( - if (temp & PORT_SUSPEND) { - if ((temp & PORT_PE) == 0) - goto error; -- /* resume signaling for 20 msec */ -- temp &= ~(PORT_RWC_BITS | PORT_WAKE_BITS); -+ /* clear phy low power mode before resume */ -+ if (hostpc_reg) { -+ temp1 = ehci_readl(ehci, hostpc_reg); -+ ehci_writel(ehci, temp1 & ~HOSTPC_PHCD, -+ hostpc_reg); -+ mdelay(5); -+ } -+ /* after PORT_PE check, the port must be -+ connected, set correct wakeup bits */ -+ temp &= ~PORT_WKCONN_E; -+ temp |= PORT_WKDISC_E | PORT_WKOC_E; - ehci_writel(ehci, temp | PORT_RESUME, - status_reg); -+ /* resume signaling for 20 msec */ - ehci->reset_done [wIndex] = jiffies - + msecs_to_jiffies (20); - } -@@ -696,6 +721,23 @@ static int ehci_hub_control ( - status_reg); - break; - case USB_PORT_FEAT_C_CONNECTION: -+ /* -+ * for connection change, we need to enable -+ * appropriate wake bits. -+ */ -+ temp |= PORT_WKOC_E; -+ if (temp & PORT_CONNECT) { -+ temp |= PORT_WKDISC_E; -+ temp &= ~PORT_WKCONN_E; -+ } else { -+ temp &= ~PORT_WKDISC_E; -+ temp |= PORT_WKCONN_E; -+ } -+ if (ehci_to_hcd(ehci)->lpm_cap) { -+ /* clear PORTSC bits on disconnect */ -+ temp &= ~PORT_LPM; -+ temp &= ~PORT_DEV_ADDR; -+ } - ehci_writel(ehci, (temp & ~PORT_RWC_BITS) | PORT_CSC, - status_reg); - break; -Index: linux-2.6.33/drivers/usb/host/ehci-langwell-pci.c -=================================================================== ---- /dev/null -+++ linux-2.6.33/drivers/usb/host/ehci-langwell-pci.c -@@ -0,0 +1,195 @@ -+/* -+ * Intel Moorestown Platform Langwell OTG EHCI Controller PCI Bus Glue. -+ * -+ * Copyright (c) 2008 - 2009, Intel Corporation. -+ * -+ * This program is free software; you can redistribute it and/or modify it -+ * under the terms of the GNU General Public License 2 as published by the -+ * Free Software Foundation. -+ * -+ * This program is distributed in the hope that it will be useful, but -+ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY -+ * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License -+ * for more details. -+ * -+ * You should have received a copy of the GNU General Public License -+ * along with this program; if not, write to the Free Software Foundation, -+ * Inc., 675 Mass Ave, Cambridge, MA 02139, USA. -+ */ -+ -+static int usb_otg_suspend(struct usb_hcd *hcd) -+{ -+ struct otg_transceiver *otg; -+ struct langwell_otg *iotg; -+ -+ otg = otg_get_transceiver(); -+ if (otg == NULL) { -+ printk(KERN_ERR "%s Failed to get otg transceiver\n", __func__); -+ return -EINVAL; -+ } -+ iotg = container_of(otg, struct langwell_otg, otg); -+ printk(KERN_INFO "%s OTG HNP update suspend\n", __func__); -+ if (iotg->otg.default_a) -+ iotg->hsm.a_suspend_req = 1; -+ else -+ iotg->hsm.b_bus_req = 0; -+ langwell_update_transceiver(); -+ otg_put_transceiver(otg); -+ return 0; -+} -+ -+static int usb_otg_resume(struct usb_hcd *hcd) -+{ -+ struct otg_transceiver *otg; -+ struct langwell_otg *iotg; -+ -+ otg = otg_get_transceiver(); -+ if (otg == NULL) { -+ printk(KERN_ERR "%s Failed to get otg transceiver\n", __func__); -+ return -EINVAL; -+ } -+ iotg = container_of(otg, struct langwell_otg, otg); -+ printk(KERN_INFO "%s OTG HNP update resume\n", __func__); -+ if (iotg->otg.default_a) { -+ iotg->hsm.b_bus_resume = 1; -+ langwell_update_transceiver(); -+ } -+ otg_put_transceiver(otg); -+ return 0; -+} -+ -+/* the root hub will call this callback when device added/removed */ -+static void otg_notify(struct usb_device *udev, unsigned action) -+{ -+ struct otg_transceiver *otg; -+ struct langwell_otg *iotg; -+ -+ otg = otg_get_transceiver(); -+ if (otg == NULL) { -+ printk(KERN_ERR "%s Failed to get otg transceiver\n", __func__); -+ return; -+ } -+ iotg = container_of(otg, struct langwell_otg, otg); -+ -+ switch (action) { -+ case USB_DEVICE_ADD: -+ pr_debug("Notify OTG HNP add device\n"); -+ if (iotg->otg.default_a == 1) -+ iotg->hsm.b_conn = 1; -+ else -+ iotg->hsm.a_conn = 1; -+ break; -+ case USB_DEVICE_REMOVE: -+ pr_debug("Notify OTG HNP delete device\n"); -+ if (iotg->otg.default_a == 1) -+ iotg->hsm.b_conn = 0; -+ else -+ iotg->hsm.a_conn = 0; -+ break; -+ default: -+ otg_put_transceiver(otg); -+ return ; -+ } -+ if (spin_trylock(&iotg->wq_lock)) { -+ langwell_update_transceiver(); -+ spin_unlock(&iotg->wq_lock); -+ } -+ otg_put_transceiver(otg); -+ return; -+} -+ -+static int ehci_langwell_probe(struct pci_dev *pdev, -+ const struct pci_device_id *id) -+{ -+ struct hc_driver *driver; -+ struct langwell_otg *iotg; -+ struct otg_transceiver *otg; -+ struct usb_hcd *hcd; -+ struct ehci_hcd *ehci; -+ int irq; -+ int retval; -+ -+ pr_debug("initializing Langwell USB OTG Host Controller\n"); -+ -+ /* we need not call pci_enable_dev since otg transceiver already take -+ * the control of this device and this probe actaully gets called by -+ * otg transceiver driver with HNP protocol. -+ */ -+ irq = pdev->irq; -+ -+ if (!id) -+ return -EINVAL; -+ driver = (struct hc_driver *)id->driver_data; -+ if (!driver) -+ return -EINVAL; -+ -+ hcd = usb_create_hcd(driver, &pdev->dev, dev_name(&pdev->dev)); -+ if (!hcd) { -+ retval = -ENOMEM; -+ goto err1; -+ } -+ -+ hcd->self.otg_port = 1; -+ ehci = hcd_to_ehci(hcd); -+ /* this will be called in ehci_bus_suspend and ehci_bus_resume */ -+ ehci->otg_suspend = usb_otg_suspend; -+ ehci->otg_resume = usb_otg_resume; -+ /* this will be called by root hub code */ -+ hcd->otg_notify = otg_notify; -+ otg = otg_get_transceiver(); -+ if (otg == NULL) { -+ printk(KERN_ERR "%s Failed to get otg transceiver\n", __func__); -+ retval = -EINVAL; -+ goto err1; -+ } -+ iotg = container_of(otg, struct langwell_otg, otg); -+ hcd->regs = iotg->regs; -+ hcd->rsrc_start = pci_resource_start(pdev, 0); -+ hcd->rsrc_len = pci_resource_len(pdev, 0); -+ -+ if (hcd->regs == NULL) { -+ dev_dbg(&pdev->dev, "error mapping memory\n"); -+ retval = -EFAULT; -+ goto err2; -+ } -+ retval = usb_add_hcd(hcd, irq, IRQF_DISABLED | IRQF_SHARED); -+ if (retval != 0) -+ goto err2; -+ retval = otg_set_host(otg, &hcd->self); -+ if (!otg->default_a) -+ hcd->self.is_b_host = 1; -+ otg_put_transceiver(otg); -+ return retval; -+ -+err2: -+ usb_put_hcd(hcd); -+err1: -+ dev_err(&pdev->dev, "init %s fail, %d\n", dev_name(&pdev->dev), retval); -+ return retval; -+} -+ -+void ehci_langwell_remove(struct pci_dev *dev) -+{ -+ struct usb_hcd *hcd = pci_get_drvdata(dev); -+ -+ if (!hcd) -+ return; -+ usb_remove_hcd(hcd); -+ usb_put_hcd(hcd); -+} -+ -+/* Langwell OTG EHCI driver */ -+static struct pci_driver ehci_otg_driver = { -+ .name = "ehci-langwell", -+ .id_table = pci_ids, -+ -+ .probe = ehci_langwell_probe, -+ .remove = ehci_langwell_remove, -+ -+#ifdef CONFIG_PM_SLEEP -+ .driver = { -+ .pm = &usb_hcd_pci_pm_ops -+ }, -+#endif -+ .shutdown = usb_hcd_pci_shutdown, -+}; -Index: linux-2.6.33/drivers/usb/host/ehci-pci.c -=================================================================== ---- linux-2.6.33.orig/drivers/usb/host/ehci-pci.c -+++ linux-2.6.33/drivers/usb/host/ehci-pci.c -@@ -41,6 +41,39 @@ static int ehci_pci_reinit(struct ehci_h - return 0; - } - -+/* enable SRAM if sram detected */ -+static void sram_init(struct usb_hcd *hcd) -+{ -+ struct ehci_hcd *ehci = hcd_to_ehci(hcd); -+ struct pci_dev *pdev = to_pci_dev(hcd->self.controller); -+ -+ if (!hcd->has_sram) -+ return; -+ ehci->sram_addr = pci_resource_start(pdev, 1); -+ ehci->sram_size = pci_resource_len(pdev, 1); -+ ehci_info(ehci, "Found HCD SRAM at %x size:%x\n", -+ ehci->sram_addr, ehci->sram_size); -+ if (pci_request_region(pdev, 1, kobject_name(&pdev->dev.kobj))) { -+ ehci_warn(ehci, "SRAM request failed\n"); -+ hcd->has_sram = 0; -+ } else if (!dma_declare_coherent_memory(&pdev->dev, ehci->sram_addr, -+ ehci->sram_addr, ehci->sram_size, DMA_MEMORY_MAP)) { -+ ehci_warn(ehci, "SRAM DMA declare failed\n"); -+ pci_release_region(pdev, 1); -+ hcd->has_sram = 0; -+ } -+} -+ -+static void sram_deinit(struct usb_hcd *hcd) -+{ -+ struct pci_dev *pdev = to_pci_dev(hcd->self.controller); -+ -+ if (!hcd->has_sram) -+ return; -+ dma_release_declared_memory(&pdev->dev); -+ pci_release_region(pdev, 1); -+} -+ - /* called during probe() after chip reset completes */ - static int ehci_pci_setup(struct usb_hcd *hcd) - { -@@ -50,6 +83,7 @@ static int ehci_pci_setup(struct usb_hcd - u8 rev; - u32 temp; - int retval; -+ int force_otg_hc_mode = 0; - - switch (pdev->vendor) { - case PCI_VENDOR_ID_TOSHIBA_2: -@@ -63,6 +97,26 @@ static int ehci_pci_setup(struct usb_hcd - #endif - } - break; -+ case PCI_VENDOR_ID_INTEL: -+ if (pdev->device == 0x0811) { -+ ehci_info(ehci, "Detected Langwell OTG HC\n"); -+ hcd->has_tt = 1; -+ ehci->has_hostpc = 1; -+#ifdef CONFIG_USB_OTG -+ ehci->has_otg = 1; -+#endif -+ force_otg_hc_mode = 1; -+ hcd->has_sram = 1; -+ hcd->sram_no_payload = 1; -+ sram_init(hcd); -+ } else if (pdev->device == 0x0806) { -+ ehci_info(ehci, "Detected Langwell MPH\n"); -+ hcd->has_tt = 1; -+ ehci->has_hostpc = 1; -+ hcd->has_sram = 1; -+ hcd->sram_no_payload = 1; -+ sram_init(hcd); -+ } - } - - ehci->caps = hcd->regs; -@@ -98,6 +152,8 @@ static int ehci_pci_setup(struct usb_hcd - - /* cache this readonly data; minimize chip reads */ - ehci->hcs_params = ehci_readl(ehci, &ehci->caps->hcs_params); -+ if (force_otg_hc_mode) -+ ehci_reset(ehci); - - retval = ehci_halt(ehci); - if (retval) -Index: linux-2.6.33/drivers/usb/host/ehci.h -=================================================================== ---- linux-2.6.33.orig/drivers/usb/host/ehci.h -+++ linux-2.6.33/drivers/usb/host/ehci.h -@@ -139,8 +139,15 @@ struct ehci_hcd { /* one per controlle - #define OHCI_HCCTRL_LEN 0x4 - __hc32 *ohci_hcctrl_reg; - unsigned has_hostpc:1; -- -+#ifdef CONFIG_USB_OTG -+ unsigned has_otg:1; /* if it is otg host*/ -+ /* otg host has additional bus_suspend and bus_resume */ -+ int (*otg_suspend)(struct usb_hcd *hcd); -+ int (*otg_resume)(struct usb_hcd *hcd); -+#endif - u8 sbrn; /* packed release number */ -+ unsigned int sram_addr; -+ unsigned int sram_size; - - /* irq statistics */ - #ifdef EHCI_STATS -@@ -156,6 +163,7 @@ struct ehci_hcd { /* one per controlle - struct dentry *debug_async; - struct dentry *debug_periodic; - struct dentry *debug_registers; -+ struct dentry *debug_lpm; - #endif - }; - -@@ -719,5 +727,10 @@ static inline u32 hc32_to_cpup (const st - #endif /* DEBUG */ - - /*-------------------------------------------------------------------------*/ -- -+#ifdef CONFIG_PCI -+static void sram_deinit(struct usb_hcd *hcd); -+#else -+static void sram_deinit(struct usb_hcd *hcd) { return; }; -+#endif -+static unsigned ehci_lpm_check(struct ehci_hcd *ehci, int port); - #endif /* __LINUX_EHCI_HCD_H */ -Index: linux-2.6.33/include/linux/usb.h -=================================================================== ---- linux-2.6.33.orig/include/linux/usb.h -+++ linux-2.6.33/include/linux/usb.h -@@ -1582,6 +1582,7 @@ usb_maxpacket(struct usb_device *udev, i - #define USB_DEVICE_REMOVE 0x0002 - #define USB_BUS_ADD 0x0003 - #define USB_BUS_REMOVE 0x0004 -+ - extern void usb_register_notify(struct notifier_block *nb); - extern void usb_unregister_notify(struct notifier_block *nb); - -Index: linux-2.6.33/drivers/usb/core/buffer.c -=================================================================== ---- linux-2.6.33.orig/drivers/usb/core/buffer.c -+++ linux-2.6.33/drivers/usb/core/buffer.c -@@ -115,6 +115,11 @@ void *hcd_buffer_alloc( - return kmalloc(size, mem_flags); - } - -+ /* we won't use internal SRAM as data payload, we can't get -+ any benefits from it */ -+ if (hcd->has_sram && hcd->sram_no_payload) -+ return dma_alloc_coherent(NULL, size, dma, mem_flags); -+ - for (i = 0; i < HCD_BUFFER_POOLS; i++) { - if (size <= pool_max [i]) - return dma_pool_alloc(hcd->pool [i], mem_flags, dma); -@@ -141,6 +146,11 @@ void hcd_buffer_free( - return; - } - -+ if (hcd->has_sram && hcd->sram_no_payload) { -+ dma_free_coherent(NULL, size, addr, dma); -+ return; -+ } -+ - for (i = 0; i < HCD_BUFFER_POOLS; i++) { - if (size <= pool_max [i]) { - dma_pool_free(hcd->pool [i], addr, dma); -Index: linux-2.6.33/drivers/usb/host/ehci-dbg.c -=================================================================== ---- linux-2.6.33.orig/drivers/usb/host/ehci-dbg.c -+++ linux-2.6.33/drivers/usb/host/ehci-dbg.c -@@ -98,13 +98,18 @@ static void dbg_hcc_params (struct ehci_ - HCC_64BIT_ADDR(params) ? " 64 bit addr" : ""); - } else { - ehci_dbg (ehci, -- "%s hcc_params %04x thresh %d uframes %s%s%s\n", -+ "%s hcc_params %04x thresh %d uframes %s%s%s%s%s%s%s\n", - label, - params, - HCC_ISOC_THRES(params), - HCC_PGM_FRAMELISTLEN(params) ? "256/512/1024" : "1024", - HCC_CANPARK(params) ? " park" : "", -- HCC_64BIT_ADDR(params) ? " 64 bit addr" : ""); -+ HCC_64BIT_ADDR(params) ? " 64 bit addr" : "", -+ HCC_LPM(params) ? " LPM" : "", -+ HCC_PER_PORT_CHANGE_EVENT(params) ? " ppce" : "", -+ HCC_HW_PREFETCH(params) ? " hw prefetch" : "", -+ HCC_32FRAME_PERIODIC_LIST(params) ? -+ " 32 peridic list" : ""); - } - } - #else -@@ -191,8 +196,9 @@ static int __maybe_unused - dbg_status_buf (char *buf, unsigned len, const char *label, u32 status) - { - return scnprintf (buf, len, -- "%s%sstatus %04x%s%s%s%s%s%s%s%s%s%s", -+ "%s%sstatus %04x%s%s%s%s%s%s%s%s%s%s%s", - label, label [0] ? " " : "", status, -+ (status & STS_PPCE_MASK) ? " PPCE" : "", - (status & STS_ASS) ? " Async" : "", - (status & STS_PSS) ? " Periodic" : "", - (status & STS_RECL) ? " Recl" : "", -@@ -210,8 +216,9 @@ static int __maybe_unused - dbg_intr_buf (char *buf, unsigned len, const char *label, u32 enable) - { - return scnprintf (buf, len, -- "%s%sintrenable %02x%s%s%s%s%s%s", -+ "%s%sintrenable %02x%s%s%s%s%s%s%s", - label, label [0] ? " " : "", enable, -+ (enable & STS_PPCE_MASK) ? " PPCE" : "", - (enable & STS_IAA) ? " IAA" : "", - (enable & STS_FATAL) ? " FATAL" : "", - (enable & STS_FLR) ? " FLR" : "", -@@ -228,9 +235,14 @@ static int - dbg_command_buf (char *buf, unsigned len, const char *label, u32 command) - { - return scnprintf (buf, len, -- "%s%scommand %06x %s=%d ithresh=%d%s%s%s%s period=%s%s %s", -+ "%s%scmd %07x %s%s%s%s%s%s=%d ithresh=%d%s%s%s%s prd=%s%s %s", - label, label [0] ? " " : "", command, -- (command & CMD_PARK) ? "park" : "(park)", -+ (command & CMD_HIRD) ? " HIRD" : "", -+ (command & CMD_PPCEE) ? " PPCEE" : "", -+ (command & CMD_FSP) ? " FSP" : "", -+ (command & CMD_ASPE) ? " ASPE" : "", -+ (command & CMD_PSPE) ? " PSPE" : "", -+ (command & CMD_PARK) ? " park" : "(park)", - CMD_PARK_CNT (command), - (command >> 16) & 0x3f, - (command & CMD_LRESET) ? " LReset" : "", -@@ -257,11 +269,21 @@ dbg_port_buf (char *buf, unsigned len, c - } - - return scnprintf (buf, len, -- "%s%sport %d status %06x%s%s sig=%s%s%s%s%s%s%s%s%s%s", -+ "%s%sp:%d sts %06x %d %s%s%s%s%s%s sig=%s%s%s%s%s%s%s%s%s%s%s", - label, label [0] ? " " : "", port, status, -+ status>>25,/*device address */ -+ (status & PORT_SSTS)>>23 == PORTSC_SUSPEND_STS_ACK ? -+ " ACK" : "", -+ (status & PORT_SSTS)>>23 == PORTSC_SUSPEND_STS_NYET ? -+ " NYET" : "", -+ (status & PORT_SSTS)>>23 == PORTSC_SUSPEND_STS_STALL ? -+ " STALL" : "", -+ (status & PORT_SSTS)>>23 == PORTSC_SUSPEND_STS_ERR ? -+ " ERR" : "", - (status & PORT_POWER) ? " POWER" : "", - (status & PORT_OWNER) ? " OWNER" : "", - sig, -+ (status & PORT_LPM) ? " LPM" : "", - (status & PORT_RESET) ? " RESET" : "", - (status & PORT_SUSPEND) ? " SUSPEND" : "", - (status & PORT_RESUME) ? " RESUME" : "", -@@ -330,6 +352,13 @@ static int debug_async_open(struct inode - static int debug_periodic_open(struct inode *, struct file *); - static int debug_registers_open(struct inode *, struct file *); - static int debug_async_open(struct inode *, struct file *); -+static int debug_lpm_open(struct inode *, struct file *); -+static ssize_t debug_lpm_read(struct file *file, char __user *user_buf, -+ size_t count, loff_t *ppos); -+static ssize_t debug_lpm_write(struct file *file, const char __user *buffer, -+ size_t count, loff_t *ppos); -+static int debug_lpm_close(struct inode *inode, struct file *file); -+ - static ssize_t debug_output(struct file*, char __user*, size_t, loff_t*); - static int debug_close(struct inode *, struct file *); - -@@ -351,6 +380,13 @@ static const struct file_operations debu - .read = debug_output, - .release = debug_close, - }; -+static const struct file_operations debug_lpm_fops = { -+ .owner = THIS_MODULE, -+ .open = debug_lpm_open, -+ .read = debug_lpm_read, -+ .write = debug_lpm_write, -+ .release = debug_lpm_close, -+}; - - static struct dentry *ehci_debug_root; - -@@ -917,6 +953,94 @@ static int debug_registers_open(struct i - return file->private_data ? 0 : -ENOMEM; - } - -+static int debug_lpm_open(struct inode *inode, struct file *file) -+{ -+ file->private_data = inode->i_private; -+ return 0; -+} -+static int debug_lpm_close(struct inode *inode, struct file *file) -+{ -+ return 0; -+} -+static ssize_t debug_lpm_read(struct file *file, char __user *user_buf, -+ size_t count, loff_t *ppos) -+{ -+ /* TODO: show lpm stats */ -+ return 0; -+} -+ -+ -+static -+ssize_t debug_lpm_write(struct file *file, const char __user *user_buf, -+ size_t count, loff_t *ppos) -+{ -+ struct usb_hcd *hcd; -+ struct ehci_hcd *ehci; -+ char buf[50]; -+ size_t len; -+ u32 temp; -+ unsigned long port; -+ u32 __iomem *portsc ; -+ u32 params; -+ -+ hcd = bus_to_hcd(file->private_data); -+ ehci = hcd_to_ehci(hcd); -+ -+ len = min(count, sizeof(buf) - 1); -+ if (copy_from_user(buf, user_buf, len)) -+ return -EFAULT; -+ buf[len] = '\0'; -+ if (len > 0 && buf[len - 1] == '\n') -+ buf[len - 1] = '\0'; -+ -+ if (strncmp(buf, "enable", 5) == 0) { -+ if (strict_strtoul(buf + 7, 10, &port)) -+ return -EINVAL; -+ params = ehci_readl(ehci, &ehci->caps->hcs_params); -+ if (port > HCS_N_PORTS(params)) { -+ ehci_dbg(ehci, "ERR: LPM on bad port %lu\n", port); -+ return -ENODEV; -+ } -+ portsc = &ehci->regs->port_status[port-1]; -+ temp = ehci_readl(ehci, portsc); -+ if (!(temp & PORT_DEV_ADDR)) { -+ ehci_dbg(ehci, "LPM: no device attached\n"); -+ return -ENODEV; -+ } -+ temp |= PORT_LPM; -+ ehci_writel(ehci, temp, portsc); -+ printk(KERN_INFO "force enable LPM for port %lu\n", port); -+ } else if (strncmp(buf, "hird=", 5) == 0) { -+ unsigned long hird; -+ if (strict_strtoul(buf + 5, 16, &hird)) -+ return -EINVAL; -+ printk(KERN_INFO " setting hird %s %lu \n", buf + 6, hird); -+ temp = ehci_readl(ehci, &ehci->regs->command); -+ temp &= ~CMD_HIRD; -+ temp |= hird << 24; -+ ehci_writel(ehci, temp, &ehci->regs->command); -+ } else if (strncmp(buf, "disable", 7) == 0) { -+ if (strict_strtoul(buf + 8, 10, &port)) -+ return -EINVAL; -+ params = ehci_readl(ehci, &ehci->caps->hcs_params); -+ if (port > HCS_N_PORTS(params)) { -+ ehci_dbg(ehci, "ERR: LPM off bad port %lu\n", port); -+ return -ENODEV; -+ } -+ portsc = &ehci->regs->port_status[port-1]; -+ temp = ehci_readl(ehci, portsc); -+ if (!(temp & PORT_DEV_ADDR)) { -+ ehci_dbg(ehci, "ERR: no device attached\n"); -+ return -ENODEV; -+ } -+ temp &= ~PORT_LPM; -+ ehci_writel(ehci, temp, portsc); -+ printk(KERN_INFO "disabled LPM for port %lu\n", port); -+ } else -+ return -EOPNOTSUPP; -+ return count; -+} -+ - static inline void create_debug_files (struct ehci_hcd *ehci) - { - struct usb_bus *bus = &ehci_to_hcd(ehci)->self; -@@ -940,6 +1064,10 @@ static inline void create_debug_files (s - ehci->debug_registers = debugfs_create_file("registers", S_IRUGO, - ehci->debug_dir, bus, - &debug_registers_fops); -+ -+ ehci->debug_registers = debugfs_create_file("lpm", S_IRUGO|S_IWUGO, -+ ehci->debug_dir, bus, -+ &debug_lpm_fops); - if (!ehci->debug_registers) - goto registers_error; - return; -Index: linux-2.6.33/drivers/usb/host/ehci-lpm.c -=================================================================== ---- /dev/null -+++ linux-2.6.33/drivers/usb/host/ehci-lpm.c -@@ -0,0 +1,90 @@ -+/* -+ * -+ * Author: Jacob Pan <jacob.jun.pan@intel.com> -+ * -+ * Copyright 2009- Intel Corp. -+ * -+ * This program is free software; you can redistribute it and/or modify it -+ * under the terms of the GNU General Public License as published by the -+ * Free Software Foundation; either version 2 of the License, or (at your -+ * option) any later version. -+ * -+ * This program is distributed in the hope that it will be useful, but -+ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY -+ * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License -+ * for more details. -+ * -+ * You should have received a copy of the GNU General Public License -+ * along with this program; if not, write to the Free Software Foundation, -+ * Inc., 675 Mass Ave, Cambridge, MA 02139, USA. -+ */ -+ -+/* this file is part of ehci-hcd.c */ -+static int ehci_lpm_set_da(struct ehci_hcd *ehci, int dev_addr, int port_num) -+{ -+ u32 __iomem portsc; -+ -+ ehci_dbg(ehci, "set dev address %d for port %d \n", dev_addr, port_num); -+ if (port_num > HCS_N_PORTS(ehci->hcs_params)) { -+ ehci_dbg(ehci, "invalid port number %d \n", port_num); -+ return -ENODEV; -+ } -+ portsc = ehci_readl(ehci, &ehci->regs->port_status[port_num-1]); -+ portsc &= ~PORT_DEV_ADDR; -+ portsc |= dev_addr<<25; -+ ehci_writel(ehci, portsc, &ehci->regs->port_status[port_num-1]); -+ return 0; -+} -+ -+/* -+ * this function is called to put a link into L1 state. the steps are: -+ * - verify HC supports LPM -+ * - make sure all pipe idle on the link -+ * - shutdown all qh on the pipe -+ * - send LPM packet -+ * - confirm device ack -+ */ -+static unsigned ehci_lpm_check(struct ehci_hcd *ehci, int port) -+{ -+ u32 __iomem *portsc ; -+ u32 val32; -+ int retval; -+ -+ portsc = &ehci->regs->port_status[port-1]; -+ val32 = ehci_readl(ehci, portsc); -+ if (!(val32 & PORT_DEV_ADDR)) { -+ ehci_dbg(ehci, "LPM: no device attached\n"); -+ return -ENODEV; -+ } -+ val32 |= PORT_LPM; -+ ehci_writel(ehci, val32, portsc); -+ mdelay(5); -+ val32 |= PORT_SUSPEND; -+ ehci_dbg(ehci, "Sending LPM 0x%08x to port %d\n", val32, port); -+ ehci_writel(ehci, val32, portsc); -+ /* wait for ACK */ -+ mdelay(10); -+ retval = handshake(ehci, &ehci->regs->port_status[port-1], PORT_SSTS, -+ PORTSC_SUSPEND_STS_ACK, 125); -+ dbg_port(ehci, "LPM", port, val32); -+ if (retval != -ETIMEDOUT) { -+ ehci_dbg(ehci, "LPM: device ACK for LPM\n"); -+ val32 |= PORT_LPM; -+ /* -+ * now device should be in L1 sleep, let's wake up the device -+ * so that we can complete enumeration. -+ */ -+ ehci_writel(ehci, val32, portsc); -+ mdelay(10); -+ val32 |= PORT_RESUME; -+ ehci_writel(ehci, val32, portsc); -+ } else { -+ ehci_dbg(ehci, "LPM: device does not ACK, disable LPM %d\n", -+ retval); -+ val32 &= ~PORT_LPM; -+ retval = -ETIMEDOUT; -+ ehci_writel(ehci, val32, portsc); -+ } -+ -+ return retval; -+} -Index: linux-2.6.33/drivers/usb/host/ehci-q.c -=================================================================== ---- linux-2.6.33.orig/drivers/usb/host/ehci-q.c -+++ linux-2.6.33/drivers/usb/host/ehci-q.c -@@ -643,6 +643,16 @@ qh_urb_transaction ( - sizeof (struct usb_ctrlrequest), - token | (2 /* "setup" */ << 8), 8); - -+ if (((struct usb_ctrlrequest *)urb->setup_packet)->bRequest -+ == USB_REQ_SET_ADDRESS) { -+ /* for LPM capable HC, set up device address*/ -+ int dev_address = ((struct usb_ctrlrequest *) -+ (urb->setup_packet))->wValue; -+ if (ehci_to_hcd(ehci)->lpm_cap) -+ ehci_lpm_set_da(ehci, dev_address, -+ urb->dev->portnum); -+ } -+ - /* ... and always at least one more pid */ - token ^= QTD_TOGGLE; - qtd_prev = qtd; -Index: linux-2.6.33/include/linux/usb/ehci_def.h -=================================================================== ---- linux-2.6.33.orig/include/linux/usb/ehci_def.h -+++ linux-2.6.33/include/linux/usb/ehci_def.h -@@ -39,6 +39,12 @@ struct ehci_caps { - #define HCS_N_PORTS(p) (((p)>>0)&0xf) /* bits 3:0, ports on HC */ - - u32 hcc_params; /* HCCPARAMS - offset 0x8 */ -+/* for 1.1 addendum */ -+#define HCC_32FRAME_PERIODIC_LIST(p) ((p)&(1 << 19)) -+#define HCC_PER_PORT_CHANGE_EVENT(p) ((p)&(1 << 18)) -+#define HCC_LPM(p) ((p)&(1 << 17)) -+#define HCC_HW_PREFETCH(p) ((p)&(1 << 16)) -+ - #define HCC_EXT_CAPS(p) (((p)>>8)&0xff) /* for pci extended caps */ - #define HCC_ISOC_CACHE(p) ((p)&(1 << 7)) /* true: can cache isoc frame */ - #define HCC_ISOC_THRES(p) (((p)>>4)&0x7) /* bits 6:4, uframes cached */ -@@ -54,6 +60,13 @@ struct ehci_regs { - - /* USBCMD: offset 0x00 */ - u32 command; -+ -+/* EHCI 1.1 addendum */ -+#define CMD_HIRD (0xf<<24) /* host initiated resume duration */ -+#define CMD_PPCEE (1<<15) /* per port change event enable */ -+#define CMD_FSP (1<<14) /* fully synchronized prefetch */ -+#define CMD_ASPE (1<<13) /* async schedule prefetch enable */ -+#define CMD_PSPE (1<<12) /* periodic schedule prefetch enable */ - /* 23:16 is r/w intr rate, in microframes; default "8" == 1/msec */ - #define CMD_PARK (1<<11) /* enable "park" on async qh */ - #define CMD_PARK_CNT(c) (((c)>>8)&3) /* how many transfers to park for */ -@@ -67,6 +80,7 @@ struct ehci_regs { - - /* USBSTS: offset 0x04 */ - u32 status; -+#define STS_PPCE_MASK (0xff<<16) /* Per-Port change event 1-16 */ - #define STS_ASS (1<<15) /* Async Schedule Status */ - #define STS_PSS (1<<14) /* Periodic Schedule Status */ - #define STS_RECL (1<<13) /* Reclamation */ -@@ -100,6 +114,14 @@ struct ehci_regs { - - /* PORTSC: offset 0x44 */ - u32 port_status [0]; /* up to N_PORTS */ -+/* EHCI 1.1 addendum */ -+#define PORTSC_SUSPEND_STS_ACK 0 -+#define PORTSC_SUSPEND_STS_NYET 1 -+#define PORTSC_SUSPEND_STS_STALL 2 -+#define PORTSC_SUSPEND_STS_ERR 3 -+ -+#define PORT_DEV_ADDR (0x7f<<25) /* device address */ -+#define PORT_SSTS (0x3<<23) /* suspend status */ - /* 31:23 reserved */ - #define PORT_WKOC_E (1<<22) /* wake on overcurrent (enable) */ - #define PORT_WKDISC_E (1<<21) /* wake on disconnect (enable) */ -@@ -115,6 +137,7 @@ struct ehci_regs { - #define PORT_USB11(x) (((x)&(3<<10)) == (1<<10)) /* USB 1.1 device */ - /* 11:10 for detecting lowspeed devices (reset vs release ownership) */ - /* 9 reserved */ -+#define PORT_LPM (1<<9) /* LPM transaction */ - #define PORT_RESET (1<<8) /* reset port */ - #define PORT_SUSPEND (1<<7) /* suspend port */ - #define PORT_RESUME (1<<6) /* resume it */ -Index: linux-2.6.33/arch/x86/include/asm/i8259.h -=================================================================== ---- linux-2.6.33.orig/arch/x86/include/asm/i8259.h -+++ linux-2.6.33/arch/x86/include/asm/i8259.h -@@ -26,11 +26,6 @@ extern unsigned int cached_irq_mask; - - extern spinlock_t i8259A_lock; - --extern void init_8259A(int auto_eoi); --extern void enable_8259A_irq(unsigned int irq); --extern void disable_8259A_irq(unsigned int irq); --extern unsigned int startup_8259A_irq(unsigned int irq); -- - /* the PIC may need a careful delay on some platforms, hence specific calls */ - static inline unsigned char inb_pic(unsigned int port) - { -@@ -57,7 +52,17 @@ static inline void outb_pic(unsigned cha - - extern struct irq_chip i8259A_chip; - --extern void mask_8259A(void); --extern void unmask_8259A(void); -+struct legacy_pic { -+ int nr_legacy_irqs; -+ struct irq_chip *chip; -+ void (*mask_all)(void); -+ void (*restore_mask)(void); -+ void (*init)(int auto_eoi); -+ int (*irq_pending)(unsigned int irq); -+ void (*make_irq)(unsigned int irq); -+}; -+ -+extern struct legacy_pic *legacy_pic; -+extern struct legacy_pic null_legacy_pic; - - #endif /* _ASM_X86_I8259_H */ -Index: linux-2.6.33/arch/x86/kernel/i8259.c -=================================================================== ---- linux-2.6.33.orig/arch/x86/kernel/i8259.c -+++ linux-2.6.33/arch/x86/kernel/i8259.c -@@ -34,6 +34,12 @@ - static int i8259A_auto_eoi; - DEFINE_SPINLOCK(i8259A_lock); - static void mask_and_ack_8259A(unsigned int); -+static void mask_8259A(void); -+static void unmask_8259A(void); -+static void disable_8259A_irq(unsigned int irq); -+static void enable_8259A_irq(unsigned int irq); -+static void init_8259A(int auto_eoi); -+static int i8259A_irq_pending(unsigned int irq); - - struct irq_chip i8259A_chip = { - .name = "XT-PIC", -@@ -63,7 +69,7 @@ unsigned int cached_irq_mask = 0xffff; - */ - unsigned long io_apic_irqs; - --void disable_8259A_irq(unsigned int irq) -+static void disable_8259A_irq(unsigned int irq) - { - unsigned int mask = 1 << irq; - unsigned long flags; -@@ -77,7 +83,7 @@ void disable_8259A_irq(unsigned int irq) - spin_unlock_irqrestore(&i8259A_lock, flags); - } - --void enable_8259A_irq(unsigned int irq) -+static void enable_8259A_irq(unsigned int irq) - { - unsigned int mask = ~(1 << irq); - unsigned long flags; -@@ -91,7 +97,7 @@ void enable_8259A_irq(unsigned int irq) - spin_unlock_irqrestore(&i8259A_lock, flags); - } - --int i8259A_irq_pending(unsigned int irq) -+static int i8259A_irq_pending(unsigned int irq) - { - unsigned int mask = 1<<irq; - unsigned long flags; -@@ -107,7 +113,7 @@ int i8259A_irq_pending(unsigned int irq) - return ret; - } - --void make_8259A_irq(unsigned int irq) -+static void make_8259A_irq(unsigned int irq) - { - disable_irq_nosync(irq); - io_apic_irqs &= ~(1<<irq); -@@ -281,7 +287,7 @@ static int __init i8259A_init_sysfs(void - - device_initcall(i8259A_init_sysfs); - --void mask_8259A(void) -+static void mask_8259A(void) - { - unsigned long flags; - -@@ -293,7 +299,7 @@ void mask_8259A(void) - spin_unlock_irqrestore(&i8259A_lock, flags); - } - --void unmask_8259A(void) -+static void unmask_8259A(void) - { - unsigned long flags; - -@@ -305,7 +311,7 @@ void unmask_8259A(void) - spin_unlock_irqrestore(&i8259A_lock, flags); - } - --void init_8259A(int auto_eoi) -+static void init_8259A(int auto_eoi) - { - unsigned long flags; - -@@ -358,3 +364,47 @@ void init_8259A(int auto_eoi) - - spin_unlock_irqrestore(&i8259A_lock, flags); - } -+ -+/* -+ * make i8259 a driver so that we can select pic functions at run time. the goal -+ * is to make x86 binary compatible among pc compatible and non-pc compatible -+ * platforms, such as x86 MID. -+ */ -+ -+static void __init legacy_pic_noop(void) { }; -+static void __init legacy_pic_uint_noop(unsigned int unused) { }; -+static void __init legacy_pic_int_noop(int unused) { }; -+ -+static struct irq_chip dummy_pic_chip = { -+ .name = "dummy pic", -+ .mask = legacy_pic_uint_noop, -+ .unmask = legacy_pic_uint_noop, -+ .disable = legacy_pic_uint_noop, -+ .mask_ack = legacy_pic_uint_noop, -+}; -+static int legacy_pic_irq_pending_noop(unsigned int irq) -+{ -+ return 0; -+} -+ -+struct legacy_pic null_legacy_pic = { -+ .nr_legacy_irqs = 0, -+ .chip = &dummy_pic_chip, -+ .mask_all = legacy_pic_noop, -+ .restore_mask = legacy_pic_noop, -+ .init = legacy_pic_int_noop, -+ .irq_pending = legacy_pic_irq_pending_noop, -+ .make_irq = legacy_pic_uint_noop, -+}; -+ -+struct legacy_pic default_legacy_pic = { -+ .nr_legacy_irqs = NR_IRQS_LEGACY, -+ .chip = &i8259A_chip, -+ .mask_all = mask_8259A, -+ .restore_mask = unmask_8259A, -+ .init = init_8259A, -+ .irq_pending = i8259A_irq_pending, -+ .make_irq = make_8259A_irq, -+}; -+ -+struct legacy_pic *legacy_pic = &default_legacy_pic; -Index: linux-2.6.33/arch/x86/include/asm/hw_irq.h -=================================================================== ---- linux-2.6.33.orig/arch/x86/include/asm/hw_irq.h -+++ linux-2.6.33/arch/x86/include/asm/hw_irq.h -@@ -53,13 +53,6 @@ extern void threshold_interrupt(void); - extern void call_function_interrupt(void); - extern void call_function_single_interrupt(void); - --/* PIC specific functions */ --extern void disable_8259A_irq(unsigned int irq); --extern void enable_8259A_irq(unsigned int irq); --extern int i8259A_irq_pending(unsigned int irq); --extern void make_8259A_irq(unsigned int irq); --extern void init_8259A(int aeoi); -- - /* IOAPIC */ - #define IO_APIC_IRQ(x) (((x) >= NR_IRQS_LEGACY) || ((1<<(x)) & io_apic_irqs)) - extern unsigned long io_apic_irqs; -Index: linux-2.6.33/arch/x86/kernel/apic/nmi.c -=================================================================== ---- linux-2.6.33.orig/arch/x86/kernel/apic/nmi.c -+++ linux-2.6.33/arch/x86/kernel/apic/nmi.c -@@ -177,7 +177,7 @@ int __init check_nmi_watchdog(void) - error: - if (nmi_watchdog == NMI_IO_APIC) { - if (!timer_through_8259) -- disable_8259A_irq(0); -+ legacy_pic->chip->mask(0); - on_each_cpu(__acpi_nmi_disable, NULL, 1); - } - -Index: linux-2.6.33/arch/x86/kernel/irqinit.c -=================================================================== ---- linux-2.6.33.orig/arch/x86/kernel/irqinit.c -+++ linux-2.6.33/arch/x86/kernel/irqinit.c -@@ -123,7 +123,7 @@ void __init init_ISA_irqs(void) - #if defined(CONFIG_X86_64) || defined(CONFIG_X86_LOCAL_APIC) - init_bsp_APIC(); - #endif -- init_8259A(0); -+ legacy_pic->init(0); - - /* - * 16 old-style INTA-cycle interrupts: -Index: linux-2.6.33/drivers/misc/Makefile -=================================================================== ---- linux-2.6.33.orig/drivers/misc/Makefile -+++ linux-2.6.33/drivers/misc/Makefile -@@ -20,6 +20,7 @@ obj-$(CONFIG_SGI_XP) += sgi-xp/ - obj-$(CONFIG_SGI_GRU) += sgi-gru/ - obj-$(CONFIG_CS5535_MFGPT) += cs5535-mfgpt.o - obj-$(CONFIG_HP_ILO) += hpilo.o -+obj-$(CONFIG_MRST) += intel_mrst.o - obj-$(CONFIG_ISL29003) += isl29003.o - obj-$(CONFIG_EP93XX_PWM) += ep93xx_pwm.o - obj-$(CONFIG_DS1682) += ds1682.o -Index: linux-2.6.33/drivers/misc/intel_mrst.c -=================================================================== ---- /dev/null -+++ linux-2.6.33/drivers/misc/intel_mrst.c -@@ -0,0 +1,216 @@ -+/* -+ * intel_mrst.c - Intel Moorestown Driver for misc functionality -+ * -+ * Copyright (C) 2009 Intel Corp -+ * Author: James Ausmus <james.ausmus@intel.com> -+ * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -+ * -+ * This program is free software; you can redistribute it and/or modify -+ * it under the terms of the GNU General Public License as published by -+ * the Free Software Foundation; version 2 of the License. -+ * -+ * This program is distributed in the hope that it will be useful, but -+ * WITHOUT ANY WARRANTY; without even the implied warranty of -+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU -+ * General Public License for more details. -+ * -+ * You should have received a copy of the GNU General Public License along -+ * with this program; if not, write to the Free Software Foundation, Inc., -+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. -+ * -+ * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -+ * -+ * This driver sets up initial PMIC register values for various voltage rails -+ * and GPIOs -+ */ -+ -+#include <linux/kernel.h> -+#include <linux/module.h> -+#include <linux/init.h> -+ -+#include <linux/delay.h> -+#include <asm/ipc_defs.h> -+ -+ -+MODULE_AUTHOR("James Ausmus"); -+MODULE_AUTHOR("German Monroy"); -+MODULE_DESCRIPTION("Intel MRST platform specific driver"); -+MODULE_LICENSE("GPL"); -+ -+static int intel_mrst_pmic_read(unsigned int reg, unsigned int *value) -+{ -+ struct ipc_pmic_reg_data pmic_data = { 0 }; -+ int ret = 0; -+ -+ pmic_data.pmic_reg_data[0].register_address = reg; -+ pmic_data.num_entries = 1; -+ ret = ipc_pmic_register_read(&pmic_data); -+ if (ret) -+ printk(KERN_ERR "intel_mrst_pmic_read: unable to read " -+ "PMIC register 0x%03x\n", reg); -+ else -+ *value = pmic_data.pmic_reg_data[0].value; -+ -+ return ret; -+} -+ -+static int intel_mrst_pmic_write(unsigned int reg, unsigned int value) -+{ -+ struct ipc_pmic_reg_data pmic_data = { 0 }; -+ int ret = 0; -+ -+ pmic_data.pmic_reg_data[0].register_address = reg; -+ pmic_data.pmic_reg_data[0].value = value; -+ pmic_data.num_entries = 1; -+ ret = ipc_pmic_register_write(&pmic_data, 0); -+ if (ret) { -+ printk(KERN_ERR "intel_mrst_pmic_write: register 0x%03x " -+ "failed ipc_pmic_register_write of value %02x, " -+ "retval %d\n", reg, value, ret); -+ } else { -+ printk(KERN_INFO "intel_mrst_pmic_write: register " -+ "0x%03x, now=0x%02x\n", -+ reg, value); -+ } -+ -+ return ret; -+} -+ -+static int intel_mrst_sdio_EVP_power_up(void) -+{ -+ intel_mrst_pmic_write(0xF4, 0x25); -+ intel_mrst_pmic_write(0x21, 0x00); -+ intel_mrst_pmic_write(0x4a, 0x7f); -+ intel_mrst_pmic_write(0x4b, 0x7f); -+ intel_mrst_pmic_write(0x4c, 0x3f); -+ -+ intel_mrst_pmic_write(0x3b, 0x3f); -+ intel_mrst_pmic_write(0x3c, 0x3f); -+ mdelay(1); -+ intel_mrst_pmic_write(0xF4, 0x05); -+ mdelay(12); -+ intel_mrst_pmic_write(0xF4, 0x21); -+ -+ return 0; -+ -+} -+ -+static int intel_mrst_sdio_EVP_power_down(void) -+{ -+ intel_mrst_pmic_write(0xF4, 0x25); -+ intel_mrst_pmic_write(0x21, 0x00); -+ -+ intel_mrst_pmic_write(0x4b, 0x00); -+ intel_mrst_pmic_write(0x4c, 0x00); -+ -+ intel_mrst_pmic_write(0x3b, 0x00); -+ intel_mrst_pmic_write(0x3c, 0x00); -+ intel_mrst_pmic_write(0x4a, 0x00); -+ -+ return 0; -+} -+ -+static int intel_mrst_sdio_8688_power_up(void) -+{ -+ intel_mrst_pmic_write(0x37, 0x3f); /* Set VDDQ for Marvell 8688 */ -+ intel_mrst_pmic_write(0x4a, 0x3f); /* Set GYMXIOCNT for Marvell 8688 */ -+ intel_mrst_pmic_write(0x4e, 0x3f); /* Set GYMX33CNT for Marvell 8688 */ -+ -+ intel_mrst_pmic_write(0x3a, 0x27); /* Enables the V3p3_FLASH line, -+ which routes to VIO_X1 and VIO_X2 -+ on the MRVL8688 */ -+ -+ intel_mrst_pmic_write(0x4b, 0x27); /* Enable V1p2_VWYMXA for MRVL8688 */ -+ intel_mrst_pmic_write(0x4c, 0x27); /* Enable V1p8_VWYMXARF for -+ MRVL8688 */ -+ -+ return 0; -+} -+ -+static int intel_mrst_bringup_8688_sdio2(void) -+{ -+ unsigned int temp = 0; -+ -+ /* Register 0xf4 has 2 GPIO lines connected to the MRVL 8688: -+ * bit 4: PDn -+ * bit 3: WiFi RESETn */ -+ -+ intel_mrst_pmic_read(0xf4, &temp); -+ temp = temp|0x8; -+ intel_mrst_pmic_write(0xf4, temp); -+ -+ temp = temp|0x10; -+ intel_mrst_pmic_write(0xf4, temp); -+ -+ return 0; -+} -+ -+static int intel_mrst_bringup_EVP_sdio2_Option_spi(void) -+{ -+ unsigned int temp = 0; -+ -+ /* Register 0xf4 has 3 GPIO lines connected to the EVP: -+ * bit 0: RF_KILL_N -+ * bit 2: H2D_INT -+ * bit 5: SYS_RST_N -+ */ -+ -+ /* Register 0xf4 has 2 GPIO lines connected to the Option: -+ * bit 0: GPO_WWAN_DISABLE -+ * bit 5: GPO_WWAN_RESET -+ */ -+ -+ intel_mrst_pmic_read(0xf4, &temp); -+ temp = temp|0x21; -+ temp = temp & 0xFB; -+ intel_mrst_pmic_write(0xf4, temp); /* Set RF_KILL_N & SYS_RST_N to -+ High. H2D_INT to LOW */ -+ -+ intel_mrst_pmic_read(0xf4, &temp); /* Set SYS_RST_N to Low */ -+ temp = temp & 0xDF; -+ mdelay(1); -+ intel_mrst_pmic_write(0xf4, temp); -+ -+ mdelay(12); /* Try to generate a 12mS delay here if possible */ -+ intel_mrst_pmic_read(0xf4, &temp); /* Set SYS_RST_N to High */ -+ temp = temp | 0x20; -+ intel_mrst_pmic_write(0xf4, temp); -+ -+ return 0; -+} -+ -+ -+static int __init intel_mrst_module_init(void) -+{ -+ int ret = 0; -+ -+/* We only need the following PMIC register initializations if -+ * we are using the Marvell 8688 WLAN card on the SDIO2 port */ -+ -+#ifdef CONFIG_8688_RC -+ -+ printk(KERN_INFO "intel_mrst_module_init: bringing up power for " -+ "8688 WLAN on SDIO2...\n"); -+ ret = intel_mrst_bringup_8688_sdio2(); -+ -+#endif /* CONFIG_8688_RC */ -+ -+/* We only need the following PMIC register initializations if -+ * we are using the EVP on SDIO2 port or Option on SPI port */ -+ -+#if defined(CONFIG_EVP_SDIO2) || defined(CONFIG_SPI_MRST_GTM501) -+ -+ printk(KERN_INFO "intel_mrst_module_init: bringing up power for " -+ "EvP on SDIO2 and Option on SPI...\n"); -+ ret = intel_mrst_bringup_EVP_sdio2_Option_spi(); -+ -+#endif /* CONFIG_EVP_SDIO2 || CONFIG_SPI_MRST_GTM501 */ -+ return ret; -+} -+ -+static void __exit intel_mrst_module_exit(void) -+{ -+} -+ -+module_init(intel_mrst_module_init); -+module_exit(intel_mrst_module_exit); -Index: linux-2.6.33/drivers/i2c/busses/Kconfig -=================================================================== ---- linux-2.6.33.orig/drivers/i2c/busses/Kconfig -+++ linux-2.6.33/drivers/i2c/busses/Kconfig -@@ -772,4 +772,14 @@ config SCx200_ACB - This support is also available as a module. If so, the module - will be called scx200_acb. - -+config I2C_MRST -+ tristate "Intel Moorestown I2C Controller" -+ depends on PCI && GPIOLIB && GPIO_LANGWELL -+ default y -+ help -+ If you say yes to this option, support will be included for the Intel -+ Moorestown chipset I2C controller. -+ This driver can also be built as a module. If so, the module -+ will be called i2c-mrst. -+ - endmenu -Index: linux-2.6.33/drivers/i2c/busses/Makefile -=================================================================== ---- linux-2.6.33.orig/drivers/i2c/busses/Makefile -+++ linux-2.6.33/drivers/i2c/busses/Makefile -@@ -72,6 +72,7 @@ obj-$(CONFIG_I2C_SIBYTE) += i2c-sibyte.o - obj-$(CONFIG_I2C_STUB) += i2c-stub.o - obj-$(CONFIG_SCx200_ACB) += scx200_acb.o - obj-$(CONFIG_SCx200_I2C) += scx200_i2c.o -+obj-$(CONFIG_I2C_MRST) += i2c-mrst.o - - ifeq ($(CONFIG_I2C_DEBUG_BUS),y) - EXTRA_CFLAGS += -DDEBUG -Index: linux-2.6.33/drivers/i2c/busses/i2c-mrst.c -=================================================================== ---- /dev/null -+++ linux-2.6.33/drivers/i2c/busses/i2c-mrst.c -@@ -0,0 +1,953 @@ -+/* -+ * Support for Moorestown Langwell I2C chip -+ * -+ * Copyright (c) 2009 Intel Corporation. -+ * Copyright (c) 2009 Synopsys. Inc. -+ * -+ * This program is free software; you can redistribute it and/or modify it -+ * under the terms and conditions of the GNU General Public License, version -+ * 2, as published by the Free Software Foundation. -+ * -+ * This program is distributed in the hope it will be useful, but WITHOUT ANY -+ * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS -+ * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more -+ * details. -+ * -+ * You should have received a copy of the GNU General Public License along -+ * with this program; if not, write to the Free Software Foundation, Inc., 51 -+ * Franklin St - Fifth Floor, Boston, MA 02110-1301 USA. -+ * -+ */ -+ -+#include <linux/module.h> -+#include <linux/moduleparam.h> -+#include <linux/version.h> -+#include <linux/kernel.h> -+#include <linux/err.h> -+#include <linux/slab.h> -+#include <linux/stat.h> -+#include <linux/types.h> -+#include <linux/delay.h> -+#include <linux/i2c.h> -+#include <linux/init.h> -+#include <linux/pci.h> -+#include <linux/gpio.h> -+ -+#include <linux/io.h> -+ -+#include "i2c-mrst.h" -+ -+#define MAX_T_POLL_COUNT 4000 /* FIXME */ -+#define DEF_BAR 0 -+#define VERSION "Version 0.5" -+ -+#define mrst_i2c_read(reg) __raw_readl(reg) -+#define mrst_i2c_write(reg, val) __raw_writel((val), (reg)) -+ -+static int speed_mode = STANDARD; -+module_param(speed_mode, int, S_IRUGO); -+ -+static int mrst_i2c_register_board_info(struct pci_dev *dev, int busnum) -+{ -+ struct mrst_i2c_private *mrst = (struct mrst_i2c_private *) -+ pci_get_drvdata(dev); -+ int err; -+ unsigned short addr, irq, host; -+ char *name = NULL; -+ struct i2c_board_info *info = NULL; -+ unsigned int addr_off, irq_off, name_off, data_off, host_off; -+ unsigned int table_len, block_num, block = 0; -+ int i, j; -+ unsigned int start, len; -+ void __iomem *base = NULL, *ptr = NULL; -+ -+ /* Determine the address of the I2C device info table area */ -+ start = pci_resource_start(dev, 1); -+ len = pci_resource_len(dev, 1); -+ if (!start || len <= 0) { -+ err = -ENODEV; -+ return err; -+ } -+ -+ err = pci_request_region(dev, 1, "mrst_i2c"); -+ if (err) { -+ dev_err(&dev->dev, "Failed to request I2C region " -+ "0x%1x-0x%Lx\n", start, -+ (unsigned long long)pci_resource_end(dev, 1)); -+ return err; -+ } -+ -+ ptr = ioremap(start, len); -+ if (!ptr) { -+ dev_err(&dev->dev, "I/O memory remapping failed\n"); -+ err = -ENOMEM; -+ goto err0; -+ } -+ -+ if (len == 8) { -+ start = ioread32(ptr); -+ len = ioread32(ptr + 4); -+ iounmap(ptr); -+ dev_dbg(&dev->dev, "New FW: start 0x%x 0x%x\n", start, len); -+ base = ioremap(start, len); -+ } else { -+ dev_dbg(&dev->dev, "this is an old FW\n"); -+ base = ptr; -+ } -+ -+ /* Initialization */ -+ name = kzalloc(sizeof(char) * NAME_LENGTH, GFP_KERNEL); -+ if (name == NULL) { -+ err = -ENOMEM; -+ goto err1; -+ } -+ -+ info = kzalloc(sizeof(struct i2c_board_info), GFP_KERNEL); -+ if (info == NULL) { -+ dev_err(&dev->dev, -+ "Can't allocate interface for i2c_board_info\n"); -+ err = -ENOMEM; -+ goto err2; -+ } -+ -+ /* Get I2C info table length */ -+ table_len = ioread32(base + I2C_INFO_TABLE_LENGTH); -+ -+ /* Calculate the number of I2C device */ -+ block_num = (table_len - HEAD_LENGTH)/BLOCK_LENGTH; -+ dev_dbg(&dev->dev, "the number of table is %d\n", block_num); -+ if (!block_num) -+ /* No I2C device info */ -+ goto err3; -+ -+ /* Initialize mrst_i2c_info array */ -+ mrst->mrst_i2c_info = kzalloc(sizeof(struct i2c_board_info) * -+ block_num, GFP_KERNEL); -+ if (mrst->mrst_i2c_info == NULL) { -+ dev_err(&dev->dev, -+ "Can't allocate interface for i2c_board_info\n"); -+ err = -ENOMEM; -+ goto err3; -+ } -+ -+ mrst->data = kzalloc(sizeof(*mrst->data) * block_num, GFP_KERNEL); -+ if (mrst->data == NULL) { -+ dev_err(&dev->dev, -+ "Can't allocate interface for per device data\n"); -+ err = -ENOMEM; -+ goto err4; -+ } -+ -+ for (i = 0; i < block_num; i++) { -+ /* I2C device info block offsets */ -+ host_off = I2C_INFO_DEV_BLOCK + BLOCK_LENGTH * i; -+ addr_off = I2C_INFO_DEV_BLOCK + BLOCK_LENGTH * i + I2C_DEV_ADDR; -+ irq_off = I2C_INFO_DEV_BLOCK + BLOCK_LENGTH * i + I2C_DEV_IRQ; -+ name_off = I2C_INFO_DEV_BLOCK + BLOCK_LENGTH * i + I2C_DEV_NAME; -+ data_off = I2C_INFO_DEV_BLOCK + BLOCK_LENGTH * i + I2C_DEV_INFO; -+ -+ /* Read PCI config table */ -+ host = ioread16(base + host_off); -+ if (host != busnum) -+ continue; -+ addr = ioread16(base + addr_off); -+ irq = ioread16(base + irq_off); -+ for (j = 0; j < NAME_LENGTH; j++) -+ name[j] = ioread8(base + name_off + j); -+ -+ for (j = 0; j < INFO_LENGTH; j++) -+ mrst->data[i][j] = ioread8(base + data_off + j); -+ dev_dbg(&dev->dev, "after read PCI config table: name = %s," -+ " address = %x\n", name, addr); -+ -+ /* Fill in i2c_board_info struct */ -+ memcpy(info->type, name, NAME_LENGTH); -+ info->platform_data = mrst->data[i]; -+ info->addr = addr; -+ info->irq = irq; -+ -+ /* Add to mrst_i2c_info array */ -+ memcpy(mrst->mrst_i2c_info + block, info, -+ sizeof(struct i2c_board_info)); -+ block++; -+ } -+ -+ /* Register i2c board info */ -+ err = i2c_register_board_info(busnum, mrst->mrst_i2c_info, block); -+ goto err3; -+ -+/* Clean up */ -+err4: -+ kfree(mrst->mrst_i2c_info); -+err3: -+ kfree(info); -+err2: -+ kfree(name); -+err1: -+ iounmap(base); -+err0: -+ pci_release_region(dev, 1); -+ return err; -+} -+/* End update */ -+ -+/** -+ * mrst_i2c_disable - Disable I2C controller -+ * @adap: struct pointer to i2c_adapter -+ * -+ * Return Value: -+ * 0 success -+ * -EBUSY if device is busy -+ * -ETIMEOUT if i2c cannot be disabled within the given time -+ * -+ * I2C bus state should be checked prior to disabling the hardware. If bus is -+ * not in idle state, an errno is returned. Write "0" to IC_ENABLE to disable -+ * I2C controller. -+ */ -+static int mrst_i2c_disable(struct i2c_adapter *adap) -+{ -+ struct mrst_i2c_private *i2c = -+ (struct mrst_i2c_private *)i2c_get_adapdata(adap); -+ -+ int count = 0; -+ -+ /* Check if device is busy */ -+ dev_dbg(&adap->dev, "mrst i2c disable\n"); -+ while (mrst_i2c_read(i2c->base + IC_STATUS) & 0x1) { -+ dev_dbg(&adap->dev, "i2c is busy, count is %d\n", count); -+ if (count++ > 10000) -+ return -EBUSY; -+ } -+ -+ /* Set IC_ENABLE to 0 */ -+ mrst_i2c_write(i2c->base + IC_ENABLE, 0); -+ -+ /* Disable all interupts */ -+ mrst_i2c_write(i2c->base + IC_INTR_MASK, 0x0000); -+ -+ /* Clear all interrupts */ -+ mrst_i2c_read(i2c->base + IC_CLR_INTR); -+ -+ return 0; -+} -+ -+/** -+ * mrst_i2c_hwinit - Initiate the I2C hardware registers. This function will -+ * be called in mrst_i2c_probe() before device registration. -+ * @dev: pci device struct pointer -+ * -+ * Return Values: -+ * 0 success -+ * -EBUSY i2c cannot be disabled -+ * -ETIMEDOUT i2c cannot be disabled -+ * -EFAULT If APB data width is not 32-bit wide -+ * -+ * I2C should be disabled prior to other register operation. If failed, an -+ * errno is returned. Mask and Clear all interrpts, this should be done at -+ * first. Set common registers which will not be modified during normal -+ * transfers, including: controll register, FIFO threshold and clock freq. -+ * Check APB data width at last. -+ */ -+static int __devinit mrst_i2c_hwinit(struct pci_dev *dev) -+{ -+ struct mrst_i2c_private *i2c = -+ (struct mrst_i2c_private *)pci_get_drvdata(dev); -+ int err = 0; -+ -+ /* Disable i2c first */ -+ err = mrst_i2c_disable(i2c->adap); -+ if (err) -+ return err; -+ -+ /* Disable all interupts */ -+ mrst_i2c_write(i2c->base + IC_INTR_MASK, 0x0000); -+ -+ /* Clear all interrupts */ -+ mrst_i2c_read(i2c->base + IC_CLR_INTR); -+ -+ /* -+ * Setup clock frequency and speed mode -+ * Enable restart condition, -+ * enable master FSM, disable slave FSM, -+ * use target address when initiating transfer -+ */ -+ switch (speed_mode) { -+ case STANDARD: -+ mrst_i2c_write(i2c->base + IC_CON, -+ SLV_DIS | RESTART | STANDARD_MODE | MASTER_EN); -+ mrst_i2c_write(i2c->base + IC_SS_SCL_HCNT, 0x75); -+ mrst_i2c_write(i2c->base + IC_SS_SCL_LCNT, 0x7c); -+ break; -+ case FAST: -+ mrst_i2c_write(i2c->base + IC_CON, -+ SLV_DIS | RESTART | FAST_MODE | MASTER_EN); -+ mrst_i2c_write(i2c->base + IC_SS_SCL_HCNT, 0x15); -+ mrst_i2c_write(i2c->base + IC_SS_SCL_LCNT, 0x21); -+ break; -+ case HIGH: -+ mrst_i2c_write(i2c->base + IC_CON, -+ SLV_DIS | RESTART | HIGH_MODE | MASTER_EN); -+ mrst_i2c_write(i2c->base + IC_SS_SCL_HCNT, 0x7); -+ mrst_i2c_write(i2c->base + IC_SS_SCL_LCNT, 0xE); -+ break; -+ default: -+ ; -+ } -+ -+ /* Set tranmit & receive FIFO threshold to zero */ -+ mrst_i2c_write(i2c->base + IC_RX_TL, 0x3); -+ mrst_i2c_write(i2c->base + IC_TX_TL, 0x3); -+ -+ mrst_i2c_write(i2c->base + IC_ENABLE, 1); -+ -+ return err; -+} -+ -+/** -+ * mrst_i2c_func - Return the supported three I2C operations. -+ * @adapter: i2c_adapter struct pointer -+ */ -+static u32 mrst_i2c_func(struct i2c_adapter *adapter) -+{ -+ return I2C_FUNC_I2C | I2C_FUNC_10BIT_ADDR | I2C_FUNC_SMBUS_EMUL; -+} -+ -+/** -+ * mrst_i2c_invalid_address - To check if the address in i2c message is -+ * correct. -+ * @p: i2c_msg struct pointer -+ * -+ * Return Values: -+ * 0 if the address is valid -+ * 1 if the address is invalid -+ */ -+static inline int mrst_i2c_invalid_address(const struct i2c_msg *p) -+{ -+ int ret = ((p->addr > 0x3ff) || (!(p->flags & I2C_M_TEN) -+ && (p->addr > 0x7f))); -+ return ret; -+} -+ -+/** -+ * mrst_i2c_address_neq - To check if the addresses for different i2c messages -+ * are equal. -+ * @p1: first i2c_msg -+ * @p2: second i2c_msg -+ * -+ * Return Values: -+ * 0 if addresse are equal -+ * 1 if not equal -+ * -+ * Within a single transfer, I2C client may need to send its address more -+ * than one time. So a check for the address equation is needed. -+ */ -+static inline int mrst_i2c_address_neq(const struct i2c_msg *p1, -+ const struct i2c_msg *p2) -+{ -+ int ret = ((p1->addr != p2->addr) || ((p1->flags & (I2C_M_TEN)) -+ != ((p2->flags) & (I2C_M_TEN)))); -+ return ret; -+} -+ -+/** -+ * mrst_i2c_abort - To handle transfer abortions and print error messages. -+ * @adap: i2c_adapter struct pointer -+ * -+ * By reading register IC_TX_ABRT_SOURCE, various transfer errors can be -+ * distingushed. At present, no circumstances have been found out that -+ * multiple errors would be occured simutaneously, so we simply use the -+ * register value directly. -+ * -+ * At last the error bits are cleared. (Note clear ABRT_SBYTE_NORSTRT bit need -+ * a few extra steps) -+ */ -+static void mrst_i2c_abort(struct i2c_adapter *adap) -+{ -+ struct mrst_i2c_private *i2c = (struct mrst_i2c_private *) -+ i2c_get_adapdata(adap); -+ -+ /* Read about source register */ -+ int abort = mrst_i2c_read(i2c->base + IC_TX_ABRT_SOURCE); -+ -+ dev_dbg(&adap->dev, "Abort: "); -+ -+ /* Single transfer error check: -+ * According to databook, TX/RX FIFOs would be flushed when -+ * the abort interrupt occured. -+ */ -+ switch (abort) { -+ case (ABRT_MASTER_DIS): -+ dev_err(&adap->dev, -+ "initiate Master operation with Master mode" -+ "disabled.\n"); -+ -+ break; -+ case (ABRT_10B_RD_NORSTRT): -+ dev_err(&adap->dev, -+ "RESTART disabled and master sends READ cmd in 10-BIT" -+ "addressing.\n"); -+ break; -+ case (ABRT_SBYTE_NORSTRT): -+ dev_err(&adap->dev, -+ "RESTART disabled and user is trying to send START" -+ "byte.\n"); -+ /* Page 141 data book */ -+ mrst_i2c_write(i2c->base + IC_TX_ABRT_SOURCE, -+ !(ABRT_SBYTE_NORSTRT)); -+ mrst_i2c_write(i2c->base + IC_CON, RESTART); -+ mrst_i2c_write(i2c->base + IC_TAR, !(IC_TAR_SPECIAL)); -+ break; -+ case (ABRT_SBYTE_ACKDET): -+ dev_err(&adap->dev, -+ "START byte was acknowledged.\n"); -+ break; -+ case (ABRT_TXDATA_NOACK): -+ dev_err(&adap->dev, -+ "No acknowledge received from slave.\n"); -+ break; -+ case (ABRT_10ADDR2_NOACK): -+ dev_err(&adap->dev, -+ "The 2nd address byte of 10-bit address not" -+ "acknowledged.\n"); -+ break; -+ case (ABRT_10ADDR1_NOACK): -+ dev_dbg(&adap->dev, -+ "The 1st address byte of 10-bit address not" -+ "acknowledged.\n"); -+ break; -+ case (ABRT_7B_ADDR_NOACK): -+ dev_err(&adap->dev, -+ "7-bit address not acknowledged.\n"); -+ break; -+ default: -+ ;; -+ } -+ -+ /* Clear TX_ABRT bit */ -+ mrst_i2c_read(i2c->base + IC_CLR_TX_ABRT); -+} -+ -+/** -+ * xfer_read - Internal function to implement master read transfer. -+ * @adap: i2c_adapter struct pointer -+ * @buf: buffer in i2c_msg -+ * @length: number of bytes to be read -+ * -+ * Return Values: -+ * 0 if the read transfer succeeds -+ * -ETIMEDOUT if cannot read the "raw" interrupt register -+ * -EINVAL if transfer abort occured -+ * -+ * For every byte, a "READ" command will be loaded into IC_DATA_CMD prior to -+ * data transfer. The actual "read" operation will be performed if the RX_FULL -+ * interrupt is occured. -+ * -+ * Note there may be two interrupt signals captured, one should read -+ * IC_RAW_INTR_STAT to seperate between errors and actual data. -+ */ -+static int xfer_read(struct i2c_adapter *adap, unsigned char *buf, int length) -+{ -+ struct mrst_i2c_private *i2c = (struct mrst_i2c_private *) -+ i2c_get_adapdata(adap); -+ uint32_t reg_val; -+ int i = length; -+ unsigned count = 0; -+ uint32_t bit_get = 1 << 3; /* receive fifo not empty */ -+ -+ while (i--) -+ mrst_i2c_write(i2c->base + IC_DATA_CMD, (uint16_t)0x100); -+ -+ i = length; -+ while (i--) { -+ count = 0; -+ reg_val = mrst_i2c_read(i2c->base + IC_STATUS); -+ while ((reg_val & bit_get) == 0) { -+ reg_val = mrst_i2c_read(i2c->base + IC_RAW_INTR_STAT); -+ if ((reg_val & 0x40) == 0x40) -+ goto read_abrt; -+ reg_val = mrst_i2c_read(i2c->base + IC_STATUS); -+ if (count++ > MAX_T_POLL_COUNT) -+ goto read_loop; -+ } -+ -+ reg_val = mrst_i2c_read(i2c->base + IC_DATA_CMD); -+ *buf++ = reg_val; -+ } -+ -+ return 0; -+ -+read_loop: -+ dev_err(&adap->dev, "Time out in read\n"); -+ return -ETIMEDOUT; -+read_abrt: -+ dev_err(&adap->dev, "Abort from read\n"); -+ mrst_i2c_abort(adap); -+ return -EINVAL; -+} -+ -+/** -+ * xfer_write - Internal function to implement master write transfer. -+ * @adap: i2c_adapter struct pointer -+ * @buf: buffer in i2c_msg -+ * @length: number of bytes to be read -+ * -+ * Return Values: -+ * 0 if the read transfer succeeds -+ * -ETIMEDOUT if cannot read the "raw" interrupt register -+ * -EINVAL if transfer abort occured -+ * -+ * For every byte, a "WRITE" command will be loaded into IC_DATA_CMD prior to -+ * data transfer. The actual "write" operation will be performed if the -+ * RX_FULL interrupt siganal is occured. -+ * -+ * Note there may be two interrupt signals captured, one should read -+ * IC_RAW_INTR_STAT to seperate between errors and actual data. -+ */ -+static int xfer_write(struct i2c_adapter *adap, -+ unsigned char *buf, int length) -+{ -+ struct mrst_i2c_private *i2c = (struct mrst_i2c_private *) -+ i2c_get_adapdata(adap); -+ -+ int i; -+ uint32_t reg_val; -+ unsigned count = 0; -+ uint32_t bit_get = 1 << 2; /* transmit fifo completely empty */ -+ -+ for (i = 0; i < length; i++) -+ mrst_i2c_write(i2c->base + IC_DATA_CMD, -+ (uint16_t)(*(buf + i))); -+ -+ reg_val = mrst_i2c_read(i2c->base + IC_STATUS); -+ while ((reg_val & bit_get) == 0) { -+ if (count++ > MAX_T_POLL_COUNT) -+ goto write_loop; -+ reg_val = mrst_i2c_read(i2c->base + IC_STATUS); -+ } -+ -+ udelay(100); -+ reg_val = mrst_i2c_read(i2c->base + IC_RAW_INTR_STAT); -+ if ((reg_val & 0x40) == 0x40) -+ goto write_abrt; -+ -+ return 0; -+ -+write_loop: -+ dev_err(&adap->dev, "Time out in write\n"); -+ return -ETIMEDOUT; -+write_abrt: -+ dev_err(&adap->dev, "Abort from write\n"); -+ mrst_i2c_abort(adap); -+ return -EINVAL; -+} -+ -+static int mrst_i2c_setup(struct i2c_adapter *adap, struct i2c_msg *pmsg) -+{ -+ struct mrst_i2c_private *i2c = -+ (struct mrst_i2c_private *)i2c_get_adapdata(adap); -+ int err; -+ uint32_t reg_val; -+ uint32_t bit_mask; -+ -+ /* Disable device first */ -+ err = mrst_i2c_disable(adap); -+ if (err) { -+ dev_err(&adap->dev, -+ "Cannot disable i2c controller, timeout!\n"); -+ return -ETIMEDOUT; -+ } -+ -+ -+ reg_val = mrst_i2c_read(i2c->base + IC_ENABLE); -+ if (reg_val & 0x1) { -+ dev_dbg(&adap->dev, "i2c busy, can't setup\n"); -+ return -EINVAL; -+ } -+ -+ /* set the speed mode to standard */ -+ reg_val = mrst_i2c_read(i2c->base + IC_CON); -+ if ((reg_val & (1<<1 | 1<<2)) != 1<<1) { -+ dev_dbg(&adap->dev, "set standard mode\n"); -+ mrst_i2c_write(i2c->base + IC_CON, (reg_val & (~0x6)) | 1<<1); -+ } -+ -+ reg_val = mrst_i2c_read(i2c->base + IC_CON); -+ /* use 7-bit addressing */ -+ if ((reg_val & (1<<4)) != 0x0) { -+ dev_dbg(&adap->dev, "set i2c 7 bit address mode\n"); -+ mrst_i2c_write(i2c->base + IC_CON, reg_val & (~(1<<4))); -+ } -+ -+ /*enable restart conditions */ -+ reg_val = mrst_i2c_read(i2c->base + IC_CON); -+ if ((reg_val & (1<<5)) != 1<<5) { -+ dev_dbg(&adap->dev, "enable restart conditions\n"); -+ mrst_i2c_write(i2c->base + IC_CON, (reg_val & (~(1 << 5))) -+ | 1 << 5); -+ } -+ -+ /* enable master FSM */ -+ reg_val = mrst_i2c_read(i2c->base + IC_CON); -+ dev_dbg(&adap->dev, "ic_con reg_val is 0x%x\n", reg_val); -+ if ((reg_val & (1<<6)) != 1<<6) { -+ dev_dbg(&adap->dev, "enable master FSM\n"); -+ mrst_i2c_write(i2c->base + IC_CON, (reg_val & (~(1 << 6))) -+ | 1<<6); -+ dev_dbg(&adap->dev, "ic_con reg_val is 0x%x\n", reg_val); -+ } -+ -+ /* use target address when initiating transfer */ -+ reg_val = mrst_i2c_read(i2c->base + IC_TAR); -+ bit_mask = 1 << 11 | 1 << 10; -+ -+ if ((reg_val & bit_mask) != 0x0) { -+ dev_dbg(&adap->dev, "WR: use target address when intiating" -+ "transfer, i2c_tx_target\n"); -+ mrst_i2c_write(i2c->base + IC_TAR, reg_val & ~bit_mask); -+ } -+ -+ /* set target address to the I2C slave address */ -+ dev_dbg(&adap->dev, "set target address to the I2C slave address," -+ "addr is %x\n", pmsg->addr); -+ mrst_i2c_write(i2c->base + IC_TAR, pmsg->addr -+ | (pmsg->flags & I2C_M_TEN ? IC_TAR_10BIT_ADDR : 0)); -+ -+ /* Enable I2C controller */ -+ mrst_i2c_write(i2c->base + IC_ENABLE, ENABLE); -+ -+ reg_val = mrst_i2c_read(i2c->base + IC_CON); -+ -+ return 0; -+} -+ -+/** -+ * mrst_i2c_xfer - Main master transfer routine. -+ * @adap: i2c_adapter struct pointer -+ * @pmsg: i2c_msg struct pointer -+ * @num: number of i2c_msg -+ * -+ * Return Values: -+ * + number of messages transfered -+ * -ETIMEDOUT If cannot disable I2C controller or read IC_STATUS -+ * -EINVAL If the address in i2c_msg is invalid -+ * -+ * This function will be registered in i2c-core and exposed to external -+ * I2C clients. -+ * 1. Disable I2C controller -+ * 2. Unmask three interrupts: RX_FULL, TX_EMPTY, TX_ABRT -+ * 3. Check if address in i2c_msg is valid -+ * 4. Enable I2C controller -+ * 5. Perform real transfer (call xfer_read or xfer_write) -+ * 6. Wait until the current transfer is finished(check bus state) -+ * 7. Mask and clear all interrupts -+ */ -+static int mrst_i2c_xfer(struct i2c_adapter *adap, -+ struct i2c_msg *pmsg, -+ int num) -+{ -+ struct mrst_i2c_private *i2c = -+ (struct mrst_i2c_private *)i2c_get_adapdata(adap); -+ int i, err; -+ -+ dev_dbg(&adap->dev, "mrst_i2c_xfer, process %d msg(s)\n", num); -+ dev_dbg(&adap->dev, KERN_INFO "slave address is %x\n", pmsg->addr); -+ -+ /* if number of messages equal 0*/ -+ if (num == 0) -+ return 0; -+ -+ /* Checked the sanity of passed messages. */ -+ if (unlikely(mrst_i2c_invalid_address(&pmsg[0]))) { -+ dev_err(&adap->dev, "Invalid address 0x%03x (%d-bit)\n", -+ pmsg[0].addr, pmsg[0].flags & I2C_M_TEN ? 10 : 7); -+ return -EINVAL; -+ } -+ for (i = 0; i < num; i++) { -+ /* Message address equal? */ -+ if (unlikely(mrst_i2c_address_neq(&pmsg[0], &pmsg[i]))) { -+ dev_err(&adap->dev, "Invalid address in msg[%d]\n", i); -+ return -EINVAL; -+ } -+ } -+ -+ if (mrst_i2c_setup(adap, pmsg)) -+ return -EINVAL; -+ -+ for (i = 0; i < num; i++) { -+ dev_dbg(&adap->dev, " #%d: %sing %d byte%s %s 0x%02x\n", i, -+ pmsg->flags & I2C_M_RD ? "read" : "writ", -+ pmsg->len, pmsg->len > 1 ? "s" : "", -+ pmsg->flags & I2C_M_RD ? "from" : "to", pmsg->addr); -+ -+ -+ /* Read or Write */ -+ if (pmsg->len && pmsg->buf) { -+ if (pmsg->flags & I2C_M_RD) { -+ dev_dbg(&adap->dev, "I2C_M_RD\n"); -+ err = xfer_read(adap, pmsg->buf, pmsg->len); -+ } else { -+ dev_dbg(&adap->dev, "I2C_M_WR\n"); -+ err = xfer_write(adap, pmsg->buf, pmsg->len); -+ } -+ if (err < 0) -+ goto err_1; -+ } -+ dev_dbg(&adap->dev, "msg[%d] transfer complete\n", i); -+ pmsg++; /* next message */ -+ } -+ goto exit; -+ -+err_1: -+ i = err; -+exit: -+ /* Mask interrupts */ -+ mrst_i2c_write(i2c->base + IC_INTR_MASK, 0x0000); -+ /* Clear all interrupts */ -+ mrst_i2c_read(i2c->base + IC_CLR_INTR); -+ -+ return i; -+} -+ -+static int mrst_gpio_init(int sda, int scl) -+{ -+ if (gpio_request(sda, "I2C_SDA")) -+ goto err_sda; -+ -+ if (gpio_request(scl, "I2C_SCL")) -+ goto err_scl; -+ -+ return 0; -+err_scl: -+ gpio_free(sda); -+err_sda: -+ return -1; -+} -+ -+static struct pci_device_id mrst_i2c_ids[] = { -+ {PCI_DEVICE(PCI_VENDOR_ID_INTEL, 0x0802)}, -+ {PCI_DEVICE(PCI_VENDOR_ID_INTEL, 0x0803)}, -+ {PCI_DEVICE(PCI_VENDOR_ID_INTEL, 0x0804)}, -+ {0,} -+}; -+MODULE_DEVICE_TABLE(pci, mrst_i2c_ids); -+ -+static struct i2c_algorithm mrst_i2c_algorithm = { -+ .master_xfer = mrst_i2c_xfer, -+ .functionality = mrst_i2c_func, -+}; -+ -+static struct pci_driver mrst_i2c_driver = { -+ .name = "mrst_i2c", -+ .id_table = mrst_i2c_ids, -+ .probe = mrst_i2c_probe, -+ .remove = __devexit_p(mrst_i2c_remove), -+ .suspend = NULL, -+ .resume = NULL, -+}; -+ -+/** -+ * mrst_i2c_probe - I2C controller initialization routine -+ * @dev: pci device -+ * @id: device id -+ * -+ * Return Values: -+ * 0 success -+ * -ENODEV If cannot allocate pci resource -+ * -ENOMEM If the register base remapping failed, or -+ * if kzalloc failed -+ * -+ * Initialization steps: -+ * 1. Request for PCI resource -+ * 2. Remap the start address of PCI resource to register base -+ * 3. Request for device memory region -+ * 4. Fill in the struct members of mrst_i2c_private -+ * 5. Call mrst_i2c_hwinit() for hardware initialization -+ * 6. Register I2C adapter in i2c-core -+ */ -+static int __devinit mrst_i2c_probe(struct pci_dev *dev, -+ const struct pci_device_id *id) -+{ -+ struct mrst_i2c_private *mrst; -+ struct i2c_adapter *adap; -+ unsigned int start, len; -+ int err, busnum = 0; -+ void __iomem *base = NULL; -+ int gpio_sda = 0, gpio_scl = 0; -+ -+ err = pci_enable_device(dev); -+ if (err) { -+ dev_err(&dev->dev, "Failed to enable I2C PCI device (%d)\n", -+ err); -+ goto exit; -+ } -+ -+ /* Determine the address of the I2C area */ -+ start = pci_resource_start(dev, DEF_BAR); -+ len = pci_resource_len(dev, DEF_BAR); -+ if (!start || len <= 0) { -+ dev_err(&dev->dev, "Base address initialization failed\n"); -+ err = -ENODEV; -+ goto exit; -+ } -+ dev_dbg(&dev->dev, "mrst i2c resource start %x, len=%d\n", -+ start, len); -+ err = pci_request_region(dev, DEF_BAR, mrst_i2c_driver.name); -+ if (err) { -+ dev_err(&dev->dev, "Failed to request I2C region " -+ "0x%1x-0x%Lx\n", start, -+ (unsigned long long)pci_resource_end(dev, DEF_BAR)); -+ goto exit; -+ } -+ -+ base = ioremap_nocache(start, len); -+ if (!base) { -+ dev_err(&dev->dev, "I/O memory remapping failed\n"); -+ err = -ENOMEM; -+ goto fail0; -+ } -+ -+ /* Allocate the per-device data structure, mrst_i2c_private */ -+ mrst = kzalloc(sizeof(struct mrst_i2c_private), GFP_KERNEL); -+ if (mrst == NULL) { -+ dev_err(&dev->dev, "Can't allocate interface!\n"); -+ err = -ENOMEM; -+ goto fail1; -+ } -+ -+ adap = kzalloc(sizeof(struct i2c_adapter), GFP_KERNEL); -+ if (adap == NULL) { -+ dev_err(&dev->dev, "Can't allocate interface!\n"); -+ err = -ENOMEM; -+ goto fail2; -+ } -+ -+ /* Initialize struct members */ -+ snprintf(adap->name, sizeof(adap->name), "mrst_i2c"); -+ adap->owner = THIS_MODULE; -+ adap->algo = &mrst_i2c_algorithm; -+ adap->class = I2C_CLASS_HWMON; -+ adap->dev.parent = &dev->dev; -+ mrst->adap = adap; -+ mrst->base = base; -+ mrst->speed = speed_mode; -+ -+ pci_set_drvdata(dev, mrst); -+ i2c_set_adapdata(adap, mrst); -+ -+ /* Initialize i2c controller */ -+ err = mrst_i2c_hwinit(dev); -+ if (err < 0) { -+ dev_err(&dev->dev, "I2C interface initialization failed\n"); -+ goto fail3; -+ } -+ -+ switch (id->device) { -+ case 0x0802: -+ dev_dbg(&adap->dev, KERN_INFO "I2C0\n"); -+ gpio_sda = GPIO_I2C_0_SDA; -+ gpio_scl = GPIO_I2C_0_SCL; -+ adap->nr = busnum = 0; -+ break; -+ case 0x0803: -+ dev_dbg(&adap->dev, KERN_INFO "I2C1\n"); -+ gpio_sda = GPIO_I2C_1_SDA; -+ gpio_scl = GPIO_I2C_1_SCL; -+ adap->nr = busnum = 1; -+ break; -+ case 0x0804: -+ dev_dbg(&adap->dev, KERN_INFO "I2C2\n"); -+ gpio_sda = GPIO_I2C_2_SDA; -+ gpio_scl = GPIO_I2C_2_SCL; -+ adap->nr = busnum = 2; -+ break; -+ default: -+ ; -+ } -+ -+ /* Config GPIO pin for I2C */ -+ err = mrst_gpio_init(gpio_sda, gpio_scl); -+ if (err) { -+ dev_err(&dev->dev, "GPIO %s registration failed\n", -+ adap->name); -+ goto fail3; -+ } -+ -+ /* Register i2c board info */ -+ /*mrst_i2c_register_board_info(dev, busnum);*/ -+ -+ /* Adapter registration */ -+ err = i2c_add_numbered_adapter(adap); -+ if (err) { -+ dev_err(&dev->dev, "Adapter %s registration failed\n", -+ adap->name); -+ goto fail3; -+ } -+ -+ dev_dbg(&dev->dev, "MRST I2C bus %d driver bind success.\n", busnum); -+ return 0; -+ -+fail3: -+ i2c_set_adapdata(adap, NULL); -+ pci_set_drvdata(dev, NULL); -+ kfree(adap); -+fail2: -+ kfree(mrst); -+fail1: -+ iounmap(base); -+fail0: -+ pci_release_region(dev, DEF_BAR); -+exit: -+ return err; -+} -+ -+static void __devexit mrst_i2c_remove(struct pci_dev *dev) -+{ -+ struct mrst_i2c_private *mrst = (struct mrst_i2c_private *) -+ pci_get_drvdata(dev); -+ if (i2c_del_adapter(mrst->adap)) -+ dev_err(&dev->dev, "Failed to delete i2c adapter"); -+ -+ kfree(mrst->mrst_i2c_info); -+ kfree(mrst->data); -+ -+ switch (dev->device) { -+ case 0x0802: -+ gpio_free(GPIO_I2C_0_SDA); -+ gpio_free(GPIO_I2C_0_SCL); -+ break; -+ case 0x0803: -+ gpio_free(GPIO_I2C_1_SDA); -+ gpio_free(GPIO_I2C_1_SCL); -+ break; -+ case 0x0804: -+ gpio_free(GPIO_I2C_2_SDA); -+ gpio_free(GPIO_I2C_2_SCL); -+ break; -+ default: -+ break; -+ } -+ -+ pci_set_drvdata(dev, NULL); -+ iounmap(mrst->base); -+ kfree(mrst); -+ pci_release_region(dev, DEF_BAR); -+} -+ -+static int __init mrst_i2c_init(void) -+{ -+ printk(KERN_NOTICE "Moorestown I2C driver %s\n", VERSION); -+ return pci_register_driver(&mrst_i2c_driver); -+} -+ -+static void __exit mrst_i2c_exit(void) -+{ -+ pci_unregister_driver(&mrst_i2c_driver); -+} -+ -+module_init(mrst_i2c_init); -+module_exit(mrst_i2c_exit); -+ -+MODULE_AUTHOR("Ba Zheng <zheng.ba@intel.com>"); -+MODULE_DESCRIPTION("I2C driver for Moorestown Platform"); -+MODULE_LICENSE("GPL"); -+MODULE_VERSION(VERSION); -Index: linux-2.6.33/drivers/i2c/busses/i2c-mrst.h -=================================================================== ---- /dev/null -+++ linux-2.6.33/drivers/i2c/busses/i2c-mrst.h -@@ -0,0 +1,282 @@ -+#ifndef __I2C_MRST_H -+#define __I2C_MRST_H -+ -+#include <linux/i2c.h> -+ -+/* Update for 2.6.27 kernel by Wen */ -+ -+/* PCI config table macros */ -+/* Offests */ -+#define I2C_INFO_TABLE_LENGTH 4 -+#define I2C_INFO_DEV_BLOCK 10 -+#define I2C_DEV_ADDR 2 -+#define I2C_DEV_IRQ 4 -+#define I2C_DEV_NAME 6 -+#define I2C_DEV_INFO 22 -+/* Length */ -+#define HEAD_LENGTH 10 -+#define BLOCK_LENGTH 32 -+#define ADDR_LENGTH 2 -+#define IRQ_LENGTH 2 -+#define NAME_LENGTH 16 -+#define INFO_LENGTH 10 -+ -+struct mrst_i2c_private { -+ struct i2c_adapter *adap; -+ /* Register base address */ -+ void __iomem *base; -+ /* Speed mode */ -+ int speed; -+ struct i2c_board_info *mrst_i2c_info; -+ char (*data)[INFO_LENGTH]; -+}; -+ -+/* Speed mode macros */ -+#define STANDARD 100 -+#define FAST 25 -+#define HIGH 3 -+ -+/* Control register */ -+#define IC_CON 0x00 -+#define SLV_DIS (1 << 6) /* Disable slave mode */ -+#define RESTART (1 << 5) /* Send a Restart condition */ -+#define ADDR_10BIT (1 << 4) /* 10-bit addressing */ -+#define STANDARD_MODE (1 << 1) /* standard mode */ -+#define FAST_MODE (2 << 1) /* fast mode */ -+#define HIGH_MODE (3 << 1) /* high speed mode */ -+#define MASTER_EN (1 << 0) /* Master mode */ -+ -+/* Target address register */ -+#define IC_TAR 0x04 -+#define IC_TAR_10BIT_ADDR (1 << 12) /* 10-bit addressing */ -+#define IC_TAR_SPECIAL (1 << 11) /* Perform special I2C cmd */ -+#define IC_TAR_GC_OR_START (1 << 10) /* 0: Gerneral Call Address */ -+ /* 1: START BYTE */ -+ -+/* Slave Address Register */ -+#define IC_SAR 0x08 /* Not used in Master mode */ -+ -+/* High Speed Master Mode Code Address Register */ -+#define IC_HS_MADDR 0x0c -+ -+/* Rx/Tx Data Buffer and Command Register */ -+#define IC_DATA_CMD 0x10 -+#define IC_RD (1 << 8) /* 1: Read 0: Write */ -+ -+/* Standard Speed Clock SCL High Count Register */ -+#define IC_SS_SCL_HCNT 0x14 -+ -+/* Standard Speed Clock SCL Low Count Register */ -+#define IC_SS_SCL_LCNT 0x18 -+ -+/* Fast Speed Clock SCL High Count Register */ -+#define IC_FS_SCL_HCNT 0x1c -+ -+/* Fast Spedd Clock SCL Low Count Register */ -+#define IC_FS_SCL_LCNT 0x20 -+ -+/* High Speed Clock SCL High Count Register */ -+#define IC_HS_SCL_HCNT 0x24 -+ -+/* High Speed Clock SCL Low Count Register */ -+#define IC_HS_SCL_LCNT 0x28 -+ -+/* Interrupt Status Register */ -+#define IC_INTR_STAT 0x2c /* Read only */ -+#define R_GEN_CALL (1 << 11) -+#define R_START_DET (1 << 10) -+#define R_STOP_DET (1 << 9) -+#define R_ACTIVITY (1 << 8) -+#define R_RX_DONE (1 << 7) -+#define R_TX_ABRT (1 << 6) -+#define R_RD_REQ (1 << 5) -+#define R_TX_EMPTY (1 << 4) -+#define R_TX_OVER (1 << 3) -+#define R_RX_FULL (1 << 2) -+#define R_RX_OVER (1 << 1) -+#define R_RX_UNDER (1 << 0) -+ -+/* Interrupt Mask Register */ -+#define IC_INTR_MASK 0x30 /* Read and Write */ -+#define M_GEN_CALL (1 << 11) -+#define M_START_DET (1 << 10) -+#define M_STOP_DET (1 << 9) -+#define M_ACTIVITY (1 << 8) -+#define M_RX_DONE (1 << 7) -+#define M_TX_ABRT (1 << 6) -+#define M_RD_REQ (1 << 5) -+#define M_TX_EMPTY (1 << 4) -+#define M_TX_OVER (1 << 3) -+#define M_RX_FULL (1 << 2) -+#define M_RX_OVER (1 << 1) -+#define M_RX_UNDER (1 << 0) -+ -+/* Raw Interrupt Status Register */ -+#define IC_RAW_INTR_STAT 0x34 /* Read Only */ -+#define GEN_CALL (1 << 11) /* General call */ -+#define START_DET (1 << 10) /* (RE)START occured */ -+#define STOP_DET (1 << 9) /* STOP occured */ -+#define ACTIVITY (1 << 8) /* Bus busy */ -+#define RX_DONE (1 << 7) /* Not used in Master mode */ -+#define TX_ABRT (1 << 6) /* Transmit Abort */ -+#define RD_REQ (1 << 5) /* Not used in Master mode */ -+#define TX_EMPTY (1 << 4) /* TX FIFO <= threshold */ -+#define TX_OVER (1 << 3) /* TX FIFO overflow */ -+#define RX_FULL (1 << 2) /* RX FIFO >= threshold */ -+#define RX_OVER (1 << 1) /* RX FIFO overflow */ -+#define RX_UNDER (1 << 0) /* RX FIFO empty */ -+ -+/* Receive FIFO Threshold Register */ -+#define IC_RX_TL 0x38 -+ -+/* Transmit FIFO Treshold Register */ -+#define IC_TX_TL 0x3c -+ -+/* Clear Combined and Individual Interrupt Register */ -+#define IC_CLR_INTR 0x40 -+#define CLR_INTR (1 << 0) -+ -+/* Clear RX_UNDER Interrupt Register */ -+#define IC_CLR_RX_UNDER 0x44 -+#define CLR_RX_UNDER (1 << 0) -+ -+/* Clear RX_OVER Interrupt Register */ -+#define IC_CLR_RX_OVER 0x48 -+#define CLR_RX_OVER (1 << 0) -+ -+/* Clear TX_OVER Interrupt Register */ -+#define IC_CLR_TX_OVER 0x4c -+#define CLR_TX_OVER (1 << 0) -+ -+#define IC_CLR_RD_REQ 0x50 -+ -+/* Clear TX_ABRT Interrupt Register */ -+#define IC_CLR_TX_ABRT 0x54 -+#define CLR_TX_ABRT (1 << 0) -+ -+#define IC_CLR_RX_DONE 0x58 -+ -+ -+/* Clear ACTIVITY Interrupt Register */ -+#define IC_CLR_ACTIVITY 0x5c -+#define CLR_ACTIVITY (1 << 0) -+ -+/* Clear STOP_DET Interrupt Register */ -+#define IC_CLR_STOP_DET 0x60 -+#define CLR_STOP_DET (1 << 0) -+ -+/* Clear START_DET Interrupt Register */ -+#define IC_CLR_START_DET 0x64 -+#define CLR_START_DET (1 << 0) -+ -+/* Clear GEN_CALL Interrupt Register */ -+#define IC_CLR_GEN_CALL 0x68 -+#define CLR_GEN_CALL (1 << 0) -+ -+/* Enable Register */ -+#define IC_ENABLE 0x6c -+#define ENABLE (1 << 0) -+ -+/* Status Register */ -+#define IC_STATUS 0x70 /* Read Only */ -+#define STAT_SLV_ACTIVITY (1 << 6) /* Slave not in idle */ -+#define STAT_MST_ACTIVITY (1 << 5) /* Master not in idle */ -+#define STAT_RFF (1 << 4) /* RX FIFO Full */ -+#define STAT_RFNE (1 << 3) /* RX FIFO Not Empty */ -+#define STAT_TFE (1 << 2) /* TX FIFO Empty */ -+#define STAT_TFNF (1 << 1) /* TX FIFO Not Full */ -+#define STAT_ACTIVITY (1 << 0) /* Activity Status */ -+ -+/* Transmit FIFO Level Register */ -+#define IC_TXFLR 0x74 /* Read Only */ -+#define TXFLR (1 << 0) /* TX FIFO level */ -+ -+/* Receive FIFO Level Register */ -+#define IC_RXFLR 0x78 /* Read Only */ -+#define RXFLR (1 << 0) /* RX FIFO level */ -+ -+/* Transmit Abort Source Register */ -+#define IC_TX_ABRT_SOURCE 0x80 -+#define ABRT_SLVRD_INTX (1 << 15) -+#define ABRT_SLV_ARBLOST (1 << 14) -+#define ABRT_SLVFLUSH_TXFIFO (1 << 13) -+#define ARB_LOST (1 << 12) -+#define ABRT_MASTER_DIS (1 << 11) -+#define ABRT_10B_RD_NORSTRT (1 << 10) -+#define ABRT_SBYTE_NORSTRT (1 << 9) -+#define ABRT_HS_NORSTRT (1 << 8) -+#define ABRT_SBYTE_ACKDET (1 << 7) -+#define ABRT_HS_ACKDET (1 << 6) -+#define ABRT_GCALL_READ (1 << 5) -+#define ABRT_GCALL_NOACK (1 << 4) -+#define ABRT_TXDATA_NOACK (1 << 3) -+#define ABRT_10ADDR2_NOACK (1 << 2) -+#define ABRT_10ADDR1_NOACK (1 << 1) -+#define ABRT_7B_ADDR_NOACK (1 << 0) -+ -+/* Enable Status Register */ -+#define IC_ENABLE_STATUS 0x9c -+#define IC_EN (1 << 0) /* I2C in an enabled state */ -+ -+/* Component Parameter Register 1*/ -+#define IC_COMP_PARAM_1 0xf4 -+#define APB_DATA_WIDTH (0x3 << 0) -+ -+/* GPIO_PINS */ -+#define GPIO_I2C_0_SDA 56 -+#define GPIO_I2C_0_SCL 57 -+ -+#define GPIO_I2C_1_SDA 54 -+#define GPIO_I2C_1_SCL 55 -+ -+#define GPIO_I2C_2_SDA 52 -+#define GPIO_I2C_2_SCL 53 -+ -+/* added by xiaolin --begin */ -+#define SS_MIN_SCL_HIGH 4000 -+#define SS_MIN_SCL_LOW 4700 -+#define FS_MIN_SCL_HIGH 600 -+#define FS_MIN_SCL_LOW 1300 -+#define HS_MIN_SCL_HIGH_100PF 60 -+#define HS_MIN_SCL_LOW_100PF 120 -+ -+enum mrst_i2c_irq { -+ i2c_irq_none = 0x000, -+ i2c_irq_rx_under = 0x001, -+ i2c_irq_rx_over = 0x002, -+ i2c_irq_rx_full = 0x004, -+ i2c_irq_tx_over = 0x008, -+ i2c_irq_tx_empty = 0x010, -+ i2c_irq_rd_req = 0x020, -+ i2c_irq_tx_abrt = 0x040, -+ i2c_irq_rx_done = 0x080, -+ i2c_irq_activity = 0x100, -+ i2c_irq_stop_det = 0x200, -+ i2c_irq_start_det = 0x400, -+ i2c_irq_gen_call = 0x800, -+ i2c_irq_all = 0xfff -+}; -+ -+/* added by xiaolin --end */ -+ -+/* Function declarations */ -+ -+static int mrst_i2c_disable(struct i2c_adapter *); -+static int __devinit mrst_i2c_hwinit(struct pci_dev *); -+static u32 mrst_i2c_func(struct i2c_adapter *); -+static inline int mrst_i2c_invalid_address(const struct i2c_msg *); -+static inline int mrst_i2c_address_neq(const struct i2c_msg *, -+ const struct i2c_msg *); -+static int mrst_i2c_xfer(struct i2c_adapter *, -+ struct i2c_msg *, -+ int); -+static int __devinit mrst_i2c_probe(struct pci_dev *, -+ const struct pci_device_id *); -+static void __devexit mrst_i2c_remove(struct pci_dev *); -+static int __init mrst_i2c_init(void); -+static void __exit mrst_i2c_exit(void); -+static int xfer_read(struct i2c_adapter *, -+ unsigned char *, int); -+static int xfer_write(struct i2c_adapter *, -+ unsigned char *, int); -+#endif /* __I2C_MRST_H */ -Index: linux-2.6.33/drivers/i2c/i2c-boardinfo.c -=================================================================== ---- linux-2.6.33.orig/drivers/i2c/i2c-boardinfo.c -+++ linux-2.6.33/drivers/i2c/i2c-boardinfo.c -@@ -58,11 +58,13 @@ EXPORT_SYMBOL_GPL(__i2c_first_dynamic_bu - * The board info passed can safely be __initdata, but be careful of embedded - * pointers (for platform_data, functions, etc) since that won't be copied. - */ --int __init -+int - i2c_register_board_info(int busnum, - struct i2c_board_info const *info, unsigned len) - { - int status; -+ int flag = 0; -+ struct i2c_devinfo *devinfo; - - down_write(&__i2c_board_lock); - -@@ -71,21 +73,32 @@ i2c_register_board_info(int busnum, - __i2c_first_dynamic_bus_num = busnum + 1; - - for (status = 0; len; len--, info++) { -- struct i2c_devinfo *devinfo; -- -- devinfo = kzalloc(sizeof(*devinfo), GFP_KERNEL); -- if (!devinfo) { -- pr_debug("i2c-core: can't register boardinfo!\n"); -- status = -ENOMEM; -- break; -+ list_for_each_entry(devinfo, &__i2c_board_list, list) { -+ if (devinfo->busnum == busnum -+ && devinfo->board_info.addr == info->addr) { -+ flag = 1; -+ break; -+ } - } -- -- devinfo->busnum = busnum; -- devinfo->board_info = *info; -- list_add_tail(&devinfo->list, &__i2c_board_list); -+ if (flag != 1) { -+ struct i2c_devinfo *dev; -+ dev = kzalloc(sizeof(*dev), GFP_KERNEL); -+ if (!dev) { -+ pr_debug("i2c-core: can't register" -+ "boardinfo!\n"); -+ status = -ENOMEM; -+ break; -+ } -+ -+ dev->busnum = busnum; -+ dev->board_info = *info; -+ list_add_tail(&dev->list, &__i2c_board_list); -+ } -+ flag = 0; - } - - up_write(&__i2c_board_lock); - - return status; - } -+EXPORT_SYMBOL_GPL(i2c_register_board_info); -Index: linux-2.6.33/arch/x86/kernel/cpu/cpufreq/Kconfig -=================================================================== ---- linux-2.6.33.orig/arch/x86/kernel/cpu/cpufreq/Kconfig -+++ linux-2.6.33/arch/x86/kernel/cpu/cpufreq/Kconfig -@@ -10,6 +10,22 @@ if CPU_FREQ - - comment "CPUFreq processor drivers" - -+config X86_SFI_CPUFREQ -+ tristate "SFI Processor P-States driver" -+ depends on SFI_PROCESSOR_PM -+ select CPU_FREQ_TABLE -+ help -+ This driver adds a CPUFreq driver which utilizes the SFI -+ Processor Performance States. -+ This driver also supports Intel Enhanced Speedstep. -+ -+ To compile this driver as a module, choose M here: the -+ module will be called sfi-cpufreq. -+ -+ For details, take a look at <file:Documentation/cpu-freq/>. -+ -+ If in doubt, say N. -+ - config X86_ACPI_CPUFREQ - tristate "ACPI Processor P-States driver" - select CPU_FREQ_TABLE -Index: linux-2.6.33/arch/x86/kernel/cpu/cpufreq/Makefile -=================================================================== ---- linux-2.6.33.orig/arch/x86/kernel/cpu/cpufreq/Makefile -+++ linux-2.6.33/arch/x86/kernel/cpu/cpufreq/Makefile -@@ -15,6 +15,7 @@ obj-$(CONFIG_X86_GX_SUSPMOD) += gx-susp - obj-$(CONFIG_X86_SPEEDSTEP_ICH) += speedstep-ich.o - obj-$(CONFIG_X86_SPEEDSTEP_LIB) += speedstep-lib.o - obj-$(CONFIG_X86_SPEEDSTEP_SMI) += speedstep-smi.o -+obj-$(CONFIG_X86_SFI_CPUFREQ) += sfi-cpufreq.o - obj-$(CONFIG_X86_SPEEDSTEP_CENTRINO) += speedstep-centrino.o - obj-$(CONFIG_X86_P4_CLOCKMOD) += p4-clockmod.o - obj-$(CONFIG_X86_CPUFREQ_NFORCE2) += cpufreq-nforce2.o -Index: linux-2.6.33/arch/x86/kernel/cpu/cpufreq/sfi-cpufreq.c -=================================================================== ---- /dev/null -+++ linux-2.6.33/arch/x86/kernel/cpu/cpufreq/sfi-cpufreq.c -@@ -0,0 +1,655 @@ -+/* -+ * sfi_cpufreq.c - sfi Processor P-States Driver -+ * -+ * -+ * -+ * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -+ * -+ * This program is free software; you can redistribute it and/or modify -+ * it under the terms of the GNU General Public License as published by -+ * the Free Software Foundation; either version 2 of the License, or (at -+ * your option) any later version. -+ * -+ * This program is distributed in the hope that it will be useful, but -+ * WITHOUT ANY WARRANTY; without even the implied warranty of -+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU -+ * General Public License for more details. -+ * You should have received a copy of the GNU General Public License along -+ * with this program; if not, write to the Free Software Foundation, Inc., -+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. -+ * -+ * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -+ * Author: Vishwesh M Rudramuni -+ * Contact information: Vishwesh Rudramuni <vishwesh.m.rudramuni@intel.com> -+ */ -+ -+/* -+ * This sfi Processor P-States Driver re-uses most part of the code available -+ * in acpi cpufreq driver. -+ */ -+ -+#include <linux/kernel.h> -+#include <linux/module.h> -+#include <linux/init.h> -+#include <linux/smp.h> -+#include <linux/sched.h> -+#include <linux/cpufreq.h> -+#include <linux/compiler.h> -+#include <linux/dmi.h> -+ -+#include <linux/sfi.h> -+#include <linux/sfi_processor.h> -+ -+#include <linux/io.h> -+#include <asm/msr.h> -+#include <asm/processor.h> -+#include <asm/cpufeature.h> -+#include <linux/delay.h> -+#include <linux/uaccess.h> -+ -+#define dprintk(msg...) cpufreq_debug_printk(CPUFREQ_DEBUG_DRIVER, \ -+ "sfi-cpufreq", msg) -+ -+MODULE_AUTHOR("Vishwesh Rudramuni"); -+MODULE_DESCRIPTION("SFI Processor P-States Driver"); -+MODULE_LICENSE("GPL"); -+#define SYSTEM_INTEL_MSR_CAPABLE 0x1 -+#define INTEL_MSR_RANGE (0xffff) -+#define CPUID_6_ECX_APERFMPERF_CAPABILITY (0x1) -+ -+struct sfi_cpufreq_data { -+ struct sfi_processor_performance *sfi_data; -+ struct cpufreq_frequency_table *freq_table; -+ unsigned int max_freq; -+ unsigned int resume; -+ unsigned int cpu_feature; -+}; -+ -+static DEFINE_PER_CPU(struct sfi_cpufreq_data *, drv_data); -+ -+/* sfi_perf_data is a pointer to percpu data. */ -+static struct sfi_processor_performance *sfi_perf_data; -+ -+static struct cpufreq_driver sfi_cpufreq_driver; -+ -+static unsigned int sfi_pstate_strict; -+ -+static int check_est_cpu(unsigned int cpuid) -+{ -+ struct cpuinfo_x86 *cpu = &cpu_data(cpuid); -+ -+ if (cpu->x86_vendor != X86_VENDOR_INTEL || -+ !cpu_has(cpu, X86_FEATURE_EST)) -+ return 0; -+ -+ return 1; -+} -+ -+static unsigned extract_freq(u32 msr, struct sfi_cpufreq_data *data) -+{ -+ int i; -+ struct sfi_processor_performance *perf; -+ -+ msr &= INTEL_MSR_RANGE; -+ perf = data->sfi_data; -+ -+ for (i = 0; data->freq_table[i].frequency != CPUFREQ_TABLE_END; i++) { -+ if (msr == perf->states[data->freq_table[i].index].status) -+ return data->freq_table[i].frequency; -+ } -+ return data->freq_table[0].frequency; -+} -+ -+ -+struct msr_addr { -+ u32 reg; -+}; -+ -+ -+struct drv_cmd { -+ unsigned int type; -+ cpumask_t mask; -+ u32 msr_reg; -+ u32 val; -+}; -+ -+static void do_drv_read(struct drv_cmd *cmd) -+{ -+ u32 h; -+ rdmsr(cmd->msr_reg, cmd->val, h); -+} -+ -+static void do_drv_write(struct drv_cmd *cmd) -+{ -+ u32 lo, hi; -+ -+ rdmsr(cmd->msr_reg, lo, hi); -+ lo = (lo & ~INTEL_MSR_RANGE) | (cmd->val & INTEL_MSR_RANGE); -+ wrmsr(cmd->msr_reg, lo, hi); -+} -+ -+static void drv_read(struct drv_cmd *cmd) -+{ -+ cpumask_t saved_mask = current->cpus_allowed; -+ cmd->val = 0; -+ -+ set_cpus_allowed(current, cmd->mask); -+ do_drv_read(cmd); -+ set_cpus_allowed(current, saved_mask); -+} -+ -+static void drv_write(struct drv_cmd *cmd) -+{ -+ cpumask_t saved_mask = current->cpus_allowed; -+ unsigned int i; -+ -+ for_each_cpu_mask(i, cmd->mask) { -+ set_cpus_allowed(current, cpumask_of_cpu(i)); -+ do_drv_write(cmd); -+ } -+ -+ set_cpus_allowed(current, saved_mask); -+ return; -+} -+ -+static u32 get_cur_val(cpumask_t mask) -+{ -+ struct drv_cmd cmd; -+ -+ if (unlikely(cpus_empty(mask))) -+ return 0; -+ -+ cmd.type = SYSTEM_INTEL_MSR_CAPABLE; -+ cmd.msr_reg = MSR_IA32_PERF_STATUS; -+ cmd.mask = mask; -+ -+ drv_read(&cmd); -+ -+ dprintk("get_cur_val = %u\n", cmd.val); -+ -+ return cmd.val; -+} -+ -+/* -+ * Return the measured active (C0) frequency on this CPU since last call -+ * to this function. -+ * Input: cpu number -+ * Return: Average CPU frequency in terms of max frequency (zero on error) -+ * -+ * We use IA32_MPERF and IA32_APERF MSRs to get the measured performance -+ * over a period of time, while CPU is in C0 state. -+ * IA32_MPERF counts at the rate of max advertised frequency -+ * IA32_APERF counts at the rate of actual CPU frequency -+ * Only IA32_APERF/IA32_MPERF ratio is architecturally defined and -+ * no meaning should be associated with absolute values of these MSRs. -+ */ -+static unsigned int get_measured_perf(struct cpufreq_policy *policy, -+ unsigned int cpu) -+{ -+ union { -+ struct { -+ u32 lo; -+ u32 hi; -+ } split; -+ u64 whole; -+ } aperf_cur, mperf_cur; -+ -+ cpumask_t saved_mask; -+ unsigned int perf_percent; -+ unsigned int retval; -+ -+ saved_mask = current->cpus_allowed; -+ set_cpus_allowed(current, cpumask_of_cpu(cpu)); -+ if (get_cpu() != cpu) { -+ /* We were not able to run on requested processor */ -+ put_cpu(); -+ return 0; -+ } -+ -+ rdmsr(MSR_IA32_APERF, aperf_cur.split.lo, aperf_cur.split.hi); -+ rdmsr(MSR_IA32_MPERF, mperf_cur.split.lo, mperf_cur.split.hi); -+ -+ wrmsr(MSR_IA32_APERF, 0, 0); -+ wrmsr(MSR_IA32_MPERF, 0, 0); -+ -+#ifdef __i386__ -+ /* -+ * We dont want to do 64 bit divide with 32 bit kernel -+ * Get an approximate value. Return failure in case we cannot get -+ * an approximate value. -+ */ -+ if (unlikely(aperf_cur.split.hi || mperf_cur.split.hi)) { -+ int shift_count; -+ u32 h; -+ -+ h = max_t(u32, aperf_cur.split.hi, mperf_cur.split.hi); -+ shift_count = fls(h); -+ -+ aperf_cur.whole >>= shift_count; -+ mperf_cur.whole >>= shift_count; -+ } -+ -+ if (((unsigned long)(-1) / 100) < aperf_cur.split.lo) { -+ int shift_count = 7; -+ aperf_cur.split.lo >>= shift_count; -+ mperf_cur.split.lo >>= shift_count; -+ } -+ -+ if (aperf_cur.split.lo && mperf_cur.split.lo) -+ perf_percent = (aperf_cur.split.lo * 100) / mperf_cur.split.lo; -+ else -+ perf_percent = 0; -+ -+#else -+ if (unlikely(((unsigned long)(-1) / 100) < aperf_cur.whole)) { -+ int shift_count = 7; -+ aperf_cur.whole >>= shift_count; -+ mperf_cur.whole >>= shift_count; -+ } -+ -+ if (aperf_cur.whole && mperf_cur.whole) -+ perf_percent = (aperf_cur.whole * 100) / mperf_cur.whole; -+ else -+ perf_percent = 0; -+ -+#endif -+ -+ retval = per_cpu(drv_data, cpu)->max_freq * perf_percent / 100; -+ -+ put_cpu(); -+ set_cpus_allowed(current, saved_mask); -+ -+ dprintk("cpu %d: performance percent %d\n", cpu, perf_percent); -+ return retval; -+} -+ -+ -+static unsigned int get_cur_freq_on_cpu(unsigned int cpu) -+{ -+ struct sfi_cpufreq_data *data = per_cpu(drv_data, cpu); -+ unsigned int freq; -+ -+ unsigned int cached_freq; -+ -+ dprintk("get_cur_freq_on_cpu (%d)\n", cpu); -+ -+ if (unlikely(data == NULL || -+ data->sfi_data == NULL || data->freq_table == NULL)) { -+ return 0; -+ } -+ cached_freq = data->freq_table[data->sfi_data->state].frequency; -+ freq = extract_freq(get_cur_val(cpumask_of_cpu(cpu)), data); -+ -+ if (freq != cached_freq) { -+ data->resume = 1; -+ return cached_freq; -+ } -+ -+ dprintk("cur freq = %u\n", freq); -+ -+ return freq; -+} -+ -+static unsigned int check_freqs(cpumask_t mask, unsigned int freq, -+ struct sfi_cpufreq_data *data) -+{ -+ unsigned int cur_freq; -+ unsigned int i; -+ -+ for (i = 0; i < 100; i++) { -+ cur_freq = extract_freq(get_cur_val(mask), data); -+ if (cur_freq == freq) -+ return 1; -+ udelay(10); -+ } -+ return 0; -+} -+ -+static int sfi_cpufreq_target(struct cpufreq_policy *policy, -+ unsigned int target_freq, unsigned int relation) -+{ -+ struct sfi_cpufreq_data *data = per_cpu(drv_data, policy->cpu); -+ struct sfi_processor_performance *perf; -+ struct cpufreq_freqs freqs; -+ cpumask_t online_policy_cpus; -+ struct drv_cmd cmd; -+ unsigned int next_state = 0; /* Index into freq_table */ -+ unsigned int next_perf_state = 0; /* Index into perf table */ -+ unsigned int i; -+ int result = 0; -+ -+ dprintk("sfi_cpufreq_target %d (%d)\n", target_freq, policy->cpu); -+ -+ if (unlikely(data == NULL || -+ data->sfi_data == NULL || data->freq_table == NULL)) { -+ return -ENODEV; -+ } -+ -+ perf = data->sfi_data; -+ result = cpufreq_frequency_table_target(policy, -+ data->freq_table, -+ target_freq, -+ relation, &next_state); -+ if (unlikely(result)) -+ return -ENODEV; -+ -+#ifdef CONFIG_HOTPLUG_CPU -+ /* cpufreq holds the hotplug lock, so we are safe from here on */ -+ cpus_and(online_policy_cpus, cpu_online_map, *policy->cpus); -+#else -+ online_policy_cpus = policy->cpus; -+#endif -+ -+ next_perf_state = data->freq_table[next_state].index; -+ if (perf->state == next_perf_state) { -+ if (unlikely(data->resume)) { -+ dprintk("Called after resume, resetting to P%d\n", -+ next_perf_state); -+ data->resume = 0; -+ } else { -+ dprintk("Already at target state (P%d)\n", -+ next_perf_state); -+ return 0; -+ } -+ } -+ -+ cmd.type = SYSTEM_INTEL_MSR_CAPABLE; -+ cmd.msr_reg = MSR_IA32_PERF_CTL; -+ cmd.val = (u32) perf->states[next_perf_state].control; -+ -+ cpus_clear(cmd.mask); -+ -+ if (policy->shared_type != CPUFREQ_SHARED_TYPE_ANY) -+ cmd.mask = online_policy_cpus; -+ else -+ cpu_set(policy->cpu, cmd.mask); -+ -+ freqs.old = perf->states[perf->state].core_frequency * 1000; -+ freqs.new = data->freq_table[next_state].frequency; -+ for_each_cpu_mask(i, cmd.mask) { -+ freqs.cpu = i; -+ cpufreq_notify_transition(&freqs, CPUFREQ_PRECHANGE); -+ } -+ -+ drv_write(&cmd); -+ -+ if (sfi_pstate_strict) { -+ if (!check_freqs(cmd.mask, freqs.new, data)) { -+ dprintk("sfi_cpufreq_target failed (%d)\n", -+ policy->cpu); -+ return -EAGAIN; -+ } -+ } -+ -+ for_each_cpu_mask(i, cmd.mask) { -+ freqs.cpu = i; -+ cpufreq_notify_transition(&freqs, CPUFREQ_POSTCHANGE); -+ } -+ perf->state = next_perf_state; -+ -+ return result; -+} -+ -+static int sfi_cpufreq_verify(struct cpufreq_policy *policy) -+{ -+ struct sfi_cpufreq_data *data = per_cpu(drv_data, policy->cpu); -+ -+ dprintk("sfi_cpufreq_verify\n"); -+ -+ return cpufreq_frequency_table_verify(policy, data->freq_table); -+} -+ -+/* -+ * sfi_cpufreq_early_init - initialize SFI P-States library -+ * -+ * Initialize the SFI P-States library (drivers/acpi/processor_perflib.c) -+ * in order to determine correct frequency and voltage pairings. We can -+ * do _PDC and _PSD and find out the processor dependency for the -+ * actual init that will happen later... -+ */ -+static int __init sfi_cpufreq_early_init(void) -+{ -+ int i; -+ struct sfi_processor *pr; -+ -+ dprintk("sfi_cpufreq_early_init\n"); -+ -+ sfi_perf_data = alloc_percpu(struct sfi_processor_performance); -+ if (!sfi_perf_data) { -+ dprintk("Memory allocation error for sfi_perf_data.\n"); -+ return -ENOMEM; -+ } -+ -+ for_each_possible_cpu(i) { -+ pr = per_cpu(sfi_processors, i); -+ if (!pr || !pr->performance) -+ continue; -+ -+ /* Assume no coordination on any error parsing domain info */ -+ cpus_clear(*pr->performance->shared_cpu_map); -+ cpu_set(i, *pr->performance->shared_cpu_map); -+ pr->performance->shared_type = CPUFREQ_SHARED_TYPE_ALL; -+ pr->performance = NULL; /* Will be set for real in register */ -+ } -+ -+ /* _PSD & _PDC is not supported in SFI.Its just a placeholder. -+ * sfi_processor_preregister_performance(sfi_perf_data); -+ * TBD: We need to study what we need to do here -+ */ -+ return 0; -+} -+ -+ -+static int sfi_cpufreq_cpu_init(struct cpufreq_policy *policy) -+{ -+ unsigned int i; -+ unsigned int valid_states = 0; -+ unsigned int cpu = policy->cpu; -+ struct sfi_cpufreq_data *data; -+ unsigned int result = 0; -+ struct cpuinfo_x86 *c = &cpu_data(policy->cpu); -+ struct sfi_processor_performance *perf; -+ -+ dprintk("sfi_cpufreq_cpu_init\n"); -+ -+ data = kzalloc(sizeof(struct sfi_cpufreq_data), GFP_KERNEL); -+ if (!data) -+ return -ENOMEM; -+ -+ data->sfi_data = per_cpu_ptr(sfi_perf_data, cpu); -+ per_cpu(drv_data, cpu) = data; -+ -+ if (cpu_has(c, X86_FEATURE_CONSTANT_TSC)) -+ sfi_cpufreq_driver.flags |= CPUFREQ_CONST_LOOPS; -+ -+ -+ result = sfi_processor_register_performance(data->sfi_data, cpu); -+ if (result) -+ goto err_free; -+ -+ perf = data->sfi_data; -+ policy->shared_type = perf->shared_type; -+ -+ /* -+ * Will let policy->cpus know about dependency only when software -+ * coordination is required. -+ */ -+ if (policy->shared_type == CPUFREQ_SHARED_TYPE_ALL || -+ policy->shared_type == CPUFREQ_SHARED_TYPE_ANY) { -+ memcpy(policy->cpus, perf->shared_cpu_map -+ , sizeof(cpumask_var_t)); -+ } -+ -+ /* capability check */ -+ if (perf->state_count <= 1) { -+ dprintk("No P-States\n"); -+ result = -ENODEV; -+ goto err_unreg; -+ } -+ -+ dprintk("HARDWARE addr space\n"); -+ if (!check_est_cpu(cpu)) { -+ result = -ENODEV; -+ goto err_unreg; -+ } -+ -+ data->cpu_feature = SYSTEM_INTEL_MSR_CAPABLE; -+ data->freq_table = kmalloc(sizeof(struct cpufreq_frequency_table) * -+ (perf->state_count+1), GFP_KERNEL); -+ if (!data->freq_table) { -+ result = -ENOMEM; -+ goto err_unreg; -+ } -+ -+ /* detect transition latency */ -+ policy->cpuinfo.transition_latency = 0; -+ for (i = 0; i < perf->state_count; i++) { -+ if ((perf->states[i].transition_latency * 1000) > -+ policy->cpuinfo.transition_latency) -+ policy->cpuinfo.transition_latency = -+ perf->states[i].transition_latency * 1000; -+ } -+ -+ data->max_freq = perf->states[0].core_frequency * 1000; -+ /* table init */ -+ for (i = 0; i < perf->state_count; i++) { -+ if (i > 0 && perf->states[i].core_frequency >= -+ data->freq_table[valid_states-1].frequency / 1000) -+ continue; -+ -+ data->freq_table[valid_states].index = i; -+ data->freq_table[valid_states].frequency = -+ perf->states[i].core_frequency * 1000; -+ valid_states++; -+ } -+ data->freq_table[valid_states].frequency = CPUFREQ_TABLE_END; -+ perf->state = 0; -+ -+ result = cpufreq_frequency_table_cpuinfo(policy, data->freq_table); -+ if (result) -+ goto err_freqfree; -+ -+ sfi_cpufreq_driver.get = get_cur_freq_on_cpu; -+ policy->cur = get_cur_freq_on_cpu(cpu); -+ -+ /* notify BIOS that we exist -+ * currently not being done. -+ */ -+ -+ /* Check for APERF/MPERF support in hardware */ -+ if (c->x86_vendor == X86_VENDOR_INTEL && c->cpuid_level >= 6) { -+ unsigned int ecx; -+ ecx = cpuid_ecx(6); -+ if (ecx & CPUID_6_ECX_APERFMPERF_CAPABILITY) -+ sfi_cpufreq_driver.getavg = get_measured_perf; -+ } -+ -+ dprintk("CPU%u - SFI performance management activated.\n", cpu); -+ for (i = 0; i < perf->state_count; i++) -+ dprintk(" %cP%d: %d MHz, %d uS\n", -+ (i == perf->state ? '*' : ' '), i, -+ (u32) perf->states[i].core_frequency, -+ (u32) perf->states[i].transition_latency); -+ -+ cpufreq_frequency_table_get_attr(data->freq_table, policy->cpu); -+ -+ /* -+ * the first call to ->target() should result in us actually -+ * writing something to the appropriate registers. -+ */ -+ data->resume = 1; -+ -+ return result; -+ -+err_freqfree: -+ kfree(data->freq_table); -+err_unreg: -+ sfi_processor_unregister_performance(perf, cpu); -+err_free: -+ kfree(data); -+ per_cpu(drv_data, cpu) = NULL; -+ -+ return result; -+} -+ -+static int sfi_cpufreq_cpu_exit(struct cpufreq_policy *policy) -+{ -+ struct sfi_cpufreq_data *data = per_cpu(drv_data, policy->cpu); -+ -+ dprintk("sfi_cpufreq_cpu_exit\n"); -+ -+ if (data) { -+ cpufreq_frequency_table_put_attr(policy->cpu); -+ per_cpu(drv_data, policy->cpu) = NULL; -+ /* acpi_processor_unregister_performance(data->acpi_data, -+ * policy->cpu); -+ * TBD: Need to study how do we do this -+ */ -+ sfi_processor_unregister_performance(data->sfi_data, -+ policy->cpu); -+ kfree(data); -+ } -+ -+ return 0; -+} -+ -+static int sfi_cpufreq_resume(struct cpufreq_policy *policy) -+{ -+ struct sfi_cpufreq_data *data = per_cpu(drv_data, policy->cpu); -+ -+ dprintk("sfi_cpufreq_resume\n"); -+ -+ data->resume = 1; -+ -+ return 0; -+} -+ -+static struct freq_attr *sfi_cpufreq_attr[] = { -+ &cpufreq_freq_attr_scaling_available_freqs, -+ NULL, -+}; -+ -+static struct cpufreq_driver sfi_cpufreq_driver = { -+ .verify = sfi_cpufreq_verify, -+ .target = sfi_cpufreq_target, -+ .init = sfi_cpufreq_cpu_init, -+ .exit = sfi_cpufreq_cpu_exit, -+ .resume = sfi_cpufreq_resume, -+ .name = "sfi-cpufreq", -+ .owner = THIS_MODULE, -+ .attr = sfi_cpufreq_attr, -+}; -+ -+static int __init sfi_cpufreq_init(void) -+{ -+ int ret; -+ -+ dprintk("sfi_cpufreq_init\n"); -+ -+ ret = sfi_cpufreq_early_init(); -+ if (ret) -+ return ret; -+ -+ return cpufreq_register_driver(&sfi_cpufreq_driver); -+} -+ -+static void __exit sfi_cpufreq_exit(void) -+{ -+ dprintk("sfi_cpufreq_exit\n"); -+ -+ cpufreq_unregister_driver(&sfi_cpufreq_driver); -+ -+ free_percpu(sfi_perf_data); -+ -+ return; -+} -+ -+module_param(sfi_pstate_strict, uint, 0644); -+MODULE_PARM_DESC(sfi_pstate_strict, -+ "value 0 or non-zero. non-zero -> strict sfi checks are " -+ "performed during frequency changes."); -+ -+late_initcall(sfi_cpufreq_init); -+module_exit(sfi_cpufreq_exit); -+ -+MODULE_ALIAS("sfi"); -Index: linux-2.6.33/arch/x86/kernel/sfi/sfi_processor_core.c -=================================================================== ---- /dev/null -+++ linux-2.6.33/arch/x86/kernel/sfi/sfi_processor_core.c -@@ -0,0 +1,134 @@ -+/* -+ * sfi_processor_core.c -+ * -+ * Copyright (C) 2008 Intel Corp -+ * -+ * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -+ * This program is free software; you can redistribute it and/or modify -+ * it under the terms of the GNU General Public License as published by -+ * the Free Software Foundation; version 2 of the License. -+ * -+ * This program is distributed in the hope that it will be useful, but -+ * WITHOUT ANY WARRANTY; without even the implied warranty of -+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU -+ * General Public License for more details. -+ * -+ * You should have received a copy of the GNU General Public License along -+ * with this program; if not, write to the Free Software Foundation, Inc., -+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. -+ * -+ * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -+ * Author: Sujith Thomas -+ * Contact information: Sujith Thomas <sujith.thomas@intel.com> -+ */ -+ -+#include <linux/module.h> -+#include <linux/init.h> -+#include <linux/types.h> -+#include <linux/sfi.h> -+#include <linux/cpu.h> -+#include <linux/sfi_processor.h> -+ -+MODULE_LICENSE("GPL"); -+MODULE_AUTHOR("Sujith Thomas"); -+MODULE_DESCRIPTION("Processor enumeration based on SFI table."); -+ -+DEFINE_PER_CPU(struct sfi_processor *, sfi_processors); -+ -+int sfi_cstate_num; -+struct sfi_cstate_table_entry sfi_cstate_array[SFI_C_STATES_MAX]; -+ -+static int __init sfi_parse_idle(struct sfi_table_header *table) -+{ -+ struct sfi_table_simple *sb; -+ struct sfi_cstate_table_entry *pentry; -+ int totallen; -+ -+ sb = (struct sfi_table_simple *)table; -+ if (!sb) { -+ printk(KERN_WARNING "SFI: Unable to map IDLE\n"); -+ return -ENODEV; -+ } -+ -+ if (!sfi_cstate_num) { -+ sfi_cstate_num = SFI_GET_NUM_ENTRIES(sb, struct sfi_cstate_table_entry); -+ pentry = (struct sfi_cstate_table_entry *)sb->pentry; -+ totallen = sfi_cstate_num * sizeof(*pentry); -+ memcpy(sfi_cstate_array, pentry, totallen); -+ } -+ -+ printk(KERN_INFO "SFI: IDLE C-state info (num = %d):\n", -+ sfi_cstate_num); -+ pentry = sfi_cstate_array; -+ for (totallen = 0; totallen < sfi_cstate_num; totallen++, pentry++) { -+ printk(KERN_INFO "Cstate[%d]: hint = 0x%08x, latency = %dms\n", -+ totallen, pentry->hint, pentry->latency); -+ } -+ -+ return 0; -+} -+ -+static int __init sfi_init_cpus(void/*struct sfi_table_header *table*/) -+{ -+ struct sfi_processor *pr; -+ int i; -+ int result = 0; -+ -+ -+ for (i = 0; i < num_processors; i++) { -+ pr = kzalloc(sizeof(struct sfi_processor), GFP_KERNEL); -+ pr->id = early_per_cpu(x86_cpu_to_apicid, i); -+//sfi_cpu_array[i].apicid; -+ per_cpu(sfi_processors, pr->id) = pr; -+ -+#ifdef CONFIG_SFI_CPUIDLE -+ result = sfi_processor_power_init(pr); -+#endif -+ } -+ return result; -+} -+ -+static int __init sfi_processor_init(void) -+{ -+ int result = 0; -+ -+ sfi_table_parse(SFI_SIG_IDLE, NULL, NULL, sfi_parse_idle); -+ -+#ifdef CONFIG_SFI_CPUIDLE -+ if (sfi_cstate_num > 0) -+ result = cpuidle_register_driver(&sfi_idle_driver); -+ if (result) -+ return result; -+#endif -+ result = sfi_init_cpus(); -+#ifdef CONFIG_SFI_CPUIDLE -+ if (result) -+ cpuidle_unregister_driver(&sfi_idle_driver); -+ -+#endif -+ return result; -+} -+ -+static void __exit sfi_processor_exit(void) -+{ -+ struct sfi_processor *pr; -+ int i; -+ for (i = 0; i < num_processors; i++) { -+ pr = per_cpu(sfi_processors, i); -+ if (pr) { -+#ifdef CONFIG_SFI_CPUIDLE -+ sfi_processor_power_exit(pr); -+#endif -+ kfree(pr); -+ per_cpu(sfi_processors, i) = NULL; -+ } -+ } -+ -+#ifdef CONFIG_SFI_CPUIDLE -+ cpuidle_unregister_driver(&sfi_idle_driver); -+#endif -+ -+} -+ -+module_init(sfi_processor_init); -+module_exit(sfi_processor_exit); -Index: linux-2.6.33/arch/x86/kernel/sfi/sfi_processor_idle.c -=================================================================== ---- /dev/null -+++ linux-2.6.33/arch/x86/kernel/sfi/sfi_processor_idle.c -@@ -0,0 +1,490 @@ -+/* -+ * sfi_processor_idle.c -+ * -+ * Copyright (C) 2009 Intel Corp -+ * -+ * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -+ * This program is free software; you can redistribute it and/or modify -+ * it under the terms of the GNU General Public License as published by -+ * the Free Software Foundation; version 2 of the License. -+ * -+ * This program is distributed in the hope that it will be useful, but -+ * WITHOUT ANY WARRANTY; without even the implied warranty of -+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU -+ * General Public License for more details. -+ * -+ * You should have received a copy of the GNU General Public License along -+ * with this program; if not, write to the Free Software Foundation, Inc., -+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. -+ * -+ * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -+ * Author: Sujith Thomas -+ * Contact information: Sujith Thomas <sujith.thomas@intel.com> -+ * Author: Vishwesh Rudramuni -+ * Contact information: Vishwesh M Rudramuni <vishwesh.m.rudramuni@intel.com> -+ */ -+ -+#include <asm/processor.h> -+#include <linux/sfi_processor.h> -+#include <linux/sched.h> -+#include <linux/clockchips.h> -+#include <linux/sfi.h> -+ -+#ifdef CONFIG_MSTWN_POWER_MGMT -+#include <linux/intel_mid.h> -+#endif -+ -+static short mwait_supported[SFI_PROCESSOR_MAX_POWER]; -+ -+#define MWAIT_SUBSTATE_MASK (0xf) -+#define MWAIT_SUBSTATE_SIZE (4) -+ -+#ifdef CONFIG_MSTWN_POWER_MGMT -+#define MID_S0I1_STATE 1 -+#define MID_S0I3_STATE 3 -+static int p1_c6; -+static int __init s0ix_latency_setup(char *str); -+static u32 s0ix_latency = 20000; -+__setup("s0ix_latency=", s0ix_latency_setup); -+#endif -+ -+#define CPUID_MWAIT_LEAF (5) -+#define CPUID5_ECX_EXTENSIONS_SUPPORTED (0x1) -+#define CPUID5_ECX_INTERRUPT_BREAK (0x2) -+ -+#define MWAIT_ECX_INTERRUPT_BREAK (0x1) -+ -+static unsigned int latency_factor __read_mostly = 4; -+module_param(latency_factor, uint, 0644); -+ -+static int sfi_idle_enter_bm(struct cpuidle_device *dev, -+ struct cpuidle_state *state); -+ -+struct cpuidle_driver sfi_idle_driver = { -+ .name = "sfi_idle", -+ .owner = THIS_MODULE, -+}; -+ -+/* -+ * Callers should disable interrupts before the call and enable -+ * interrupts after return. -+ */ -+static void sfi_safe_halt(void) -+{ -+ current_thread_info()->status &= ~TS_POLLING; -+ /* -+ * TS_POLLING-cleared state must be visible before we -+ * test NEED_RESCHED: -+ */ -+ smp_mb(); -+ if (!need_resched()) { -+ safe_halt(); -+ local_irq_disable(); -+ } -+ current_thread_info()->status |= TS_POLLING; -+} -+ -+static int sfi_idle_enter_c1(struct cpuidle_device *dev, -+ struct cpuidle_state *state) -+{ -+ ktime_t t1, t2; -+ s64 diff = 0; -+ -+ local_irq_disable(); -+ -+ t1 = ktime_get(); -+ sfi_safe_halt(); -+ t2 = ktime_get(); -+ -+ local_irq_enable(); -+ -+ diff = ktime_to_us(ktime_sub(t2, t1)); -+ -+ if (diff > INT_MAX) -+ diff = INT_MAX; -+ -+ return (int)diff; -+} -+ -+static int sfi_idle_enter_simple(struct cpuidle_device *dev, -+ struct cpuidle_state *state) -+{ -+ ktime_t t1, t2; -+ s64 diff = 0; -+ struct sfi_cstate_table_entry *data; -+ -+ data = (struct sfi_cstate_table_entry *)cpuidle_get_statedata(state); -+ if (unlikely(!data)) -+ return 0; -+ -+ -+ local_irq_disable(); -+ current_thread_info()->status &= ~TS_POLLING; -+ /* -+ * TS_POLLING-cleared state must be visible before we test -+ * NEED_RESCHED: -+ */ -+ smp_mb(); -+ -+ if (unlikely(need_resched())) { -+ current_thread_info()->status |= TS_POLLING; -+ local_irq_enable(); -+ return 0; -+ } -+ -+ t1 = ktime_get(); -+ mwait_idle_with_hints(data->hint, MWAIT_ECX_INTERRUPT_BREAK); -+ t2 = ktime_get(); -+ -+ local_irq_enable(); -+ current_thread_info()->status |= TS_POLLING; -+ -+ diff = ktime_to_us(ktime_sub(t2, t1)); -+ if (diff > INT_MAX) -+ diff = INT_MAX; -+ -+ return (int)diff; -+} -+ -+#ifdef CONFIG_MSTWN_POWER_MGMT -+static int __init s0ix_latency_setup(char *str) -+{ -+ u32 latency; -+ -+ latency = memparse(str, &str); -+ if (latency > 150) -+ s0ix_latency = latency; -+ -+ printk(KERN_INFO "latency for c7 is %x\n", latency); -+ return 1; -+} -+ -+static int s0i3_enter_bm(struct cpuidle_device *dev, -+ struct cpuidle_state *state) -+{ -+ ktime_t t1, t2; -+ s64 diff_us = 0; -+ s64 diff_ns = 0; -+ struct sfi_processor *pr; -+ struct cpuidle_state *next_state; -+ int pr_id; -+ int ret; -+ -+ pr_id = smp_processor_id(); -+ -+ pr = __get_cpu_var(sfi_processors); -+ if (unlikely(!pr)) -+ return 0; -+ -+ switch (g_ospm_base->platform_sx_state) { -+ case MID_S0I3_STATE: -+ if (pr_id == 0) { -+ t1 = ktime_get(); -+ -+ /* Tell the scheduler that we -+ * are going deep-idle: -+ */ -+ sched_clock_idle_sleep_event(); -+ -+ clockevents_notify(CLOCK_EVT_NOTIFY_BROADCAST_ENTER, -+ &pr->id); -+ -+ mid_suspend_enter(MID_S0I3_STATE); -+ -+ t2 = ktime_get(); -+ -+ diff_us = ktime_to_us(ktime_sub(t2, t1)); -+ diff_ns = ktime_to_ns(ktime_sub(t2, t1)); -+ -+ /* Tell the scheduler how much -+ * we idled: -+ */ -+ sched_clock_idle_wakeup_event(diff_ns); -+ clockevents_notify(CLOCK_EVT_NOTIFY_BROADCAST_EXIT, -+ &pr->id); -+ -+ if (diff_us > INT_MAX) -+ diff_us = INT_MAX; -+ -+ return (int)diff_us; -+ -+ } else { -+ ret = sfi_idle_enter_c1(dev, state); -+ return ret; -+ } -+ break; -+ case MID_S0I1_STATE: -+ if ((pr_id == 0) && (p1_c6 == 1)) { -+ /* pmu_issue_command(s0i1) only for thread 0 rest -+ * fall through -+ */ -+ mid_suspend_enter(MID_S0I1_STATE); -+ } -+ next_state = &dev->states[4]; -+ ret = sfi_idle_enter_bm(dev, next_state); -+ return ret; -+ break; -+ default: -+ next_state = &dev->states[4]; -+ ret = sfi_idle_enter_bm(dev, next_state); -+ dev->last_state = &dev->states[4]; -+ return ret; -+ break; -+ -+ } -+ -+ return 0; -+ -+} -+#endif -+ -+static int sfi_idle_enter_bm(struct cpuidle_device *dev, -+ struct cpuidle_state *state) -+{ -+ -+ ktime_t t1, t2; -+ s64 diff_us = 0; -+ s64 diff_ns = 0; -+ struct sfi_cstate_table_entry *data; -+ struct sfi_processor *pr; -+ -+ pr = __get_cpu_var(sfi_processors); -+ if (unlikely(!pr)) -+ return 0; -+ -+ data = (struct sfi_cstate_table_entry *)cpuidle_get_statedata(state); -+ if (unlikely(!data)) -+ return 0; -+ -+ local_irq_disable(); -+ current_thread_info()->status &= ~TS_POLLING; -+ /* -+ * TS_POLLING-cleared state must be visible before we test -+ * NEED_RESCHED: -+ */ -+ smp_mb(); -+ -+ if (unlikely(need_resched())) { -+ current_thread_info()->status |= TS_POLLING; -+ local_irq_enable(); -+ return 0; -+ } -+ -+ t1 = ktime_get(); -+ -+ /* Tell the scheduler that we are going deep-idle: */ -+ sched_clock_idle_sleep_event(); -+ -+ clockevents_notify(CLOCK_EVT_NOTIFY_BROADCAST_ENTER, &pr->id); -+ -+ -+#ifdef CONFIG_MSTWN_POWER_MGMT -+ if ((smp_processor_id() == 1) && (data->hint == 0x52)) -+ p1_c6 = 1; -+#endif -+ -+ mwait_idle_with_hints(data->hint, MWAIT_ECX_INTERRUPT_BREAK); -+ -+#ifdef CONFIG_MSTWN_POWER_MGMT -+ if ((smp_processor_id() == 1) && (data->hint == 0x52)) -+ p1_c6 = 0; -+#endif -+ -+ t2 = ktime_get(); -+ -+ diff_us = ktime_to_us(ktime_sub(t2, t1)); -+ diff_ns = ktime_to_ns(ktime_sub(t2, t1)); -+ -+ /* Tell the scheduler how much we idled: */ -+ sched_clock_idle_wakeup_event(diff_ns); -+ -+ local_irq_enable(); -+ current_thread_info()->status |= TS_POLLING; -+ -+ clockevents_notify(CLOCK_EVT_NOTIFY_BROADCAST_EXIT, &pr->id); -+ -+ if (diff_us > INT_MAX) -+ diff_us = INT_MAX; -+ -+ return (int)diff_us; -+ -+} -+ -+/** -+ * sfi_processor_setup_cpuidle - prepares and configures CPUIDLE -+ * @pr: the SFI processor -+ */ -+static int sfi_processor_setup_cpuidle(struct sfi_processor *pr) -+{ -+ int i; -+ int count = CPUIDLE_DRIVER_STATE_START; -+ struct cpuidle_state *state; -+ struct cpuidle_device *dev = &pr->power.dev; -+ -+ for (i = 0; i < CPUIDLE_STATE_MAX; i++) { -+ dev->states[i].name[0] = '\0'; -+ dev->states[i].desc[0] = '\0'; -+ } -+ -+ for (i = 1; i < SFI_PROCESSOR_MAX_POWER; i++) { -+ -+ /*Mwait not supported by processor */ -+ if (!mwait_supported[i]) -+ continue; -+ -+ state = &dev->states[count]; -+ -+ snprintf(state->name, CPUIDLE_NAME_LEN, "C%d", i); -+ snprintf(state->desc, CPUIDLE_DESC_LEN, "C%d", i); -+ -+ state->exit_latency = pr->power.states[count].exit_latency; -+ state->target_residency = state->exit_latency * latency_factor; -+ state->power_usage = pr->power.states[count].power_usage; -+ state->flags = 0; -+ cpuidle_set_statedata(state, &pr->power.sfi_cstates[count]); -+ -+ printk -+ (KERN_INFO "State details Name:%s, Desc:%s, \ -+ exit_latency:%d,target_residency%d,power_usage%d,hint%d", -+ state->name, state->desc, state->exit_latency, -+ state->target_residency, state->power_usage, -+ pr->power.sfi_cstates[count].hint); -+ -+ switch (i) { -+ case SFI_STATE_C1: -+ state->flags |= CPUIDLE_FLAG_SHALLOW; -+ state->enter = sfi_idle_enter_c1; -+ break; -+ -+ case SFI_STATE_C2: -+ state->flags |= CPUIDLE_FLAG_BALANCED; -+ state->flags |= CPUIDLE_FLAG_TIME_VALID; -+ state->enter = sfi_idle_enter_simple; -+ break; -+ -+ case SFI_STATE_C3: -+ case SFI_STATE_C4: -+ case SFI_STATE_C5: -+ case SFI_STATE_C6: -+ state->flags |= CPUIDLE_FLAG_DEEP; -+ state->flags |= CPUIDLE_FLAG_TIME_VALID; -+ state->flags |= CPUIDLE_FLAG_CHECK_BM; -+ state->enter = sfi_idle_enter_bm; -+ break; -+#ifdef CONFIG_MSTWN_POWER_MGMT -+ case STATE_S0IX: -+ state->flags |= CPUIDLE_FLAG_DEEP; -+ state->flags |= CPUIDLE_FLAG_TIME_VALID; -+ state->flags |= CPUIDLE_FLAG_CHECK_BM; -+ state->enter = s0i3_enter_bm; -+ break; -+#endif -+ } -+ -+ count++; -+ if (count == CPUIDLE_STATE_MAX) -+ break; -+ } -+ -+ dev->state_count = count; -+ if (!count) -+ return -EINVAL; -+ -+ return 0; -+} -+ -+int sfi_cstate_probe(unsigned int hint) -+{ -+ int retval; -+ unsigned int eax, ebx, ecx, edx; -+ unsigned int edx_part; -+ unsigned int cstate_type; -+ unsigned int num_cstate_subtype; -+ -+ cpuid(CPUID_MWAIT_LEAF, &eax, &ebx, &ecx, &edx); -+ -+ /* Check whether this particular CState is supported or not */ -+ cstate_type = (hint >> MWAIT_SUBSTATE_SIZE) + 1; -+ edx_part = edx >> (cstate_type * MWAIT_SUBSTATE_SIZE); -+ num_cstate_subtype = edx_part & MWAIT_SUBSTATE_MASK; -+ -+ retval = 0; -+ if (num_cstate_subtype < (hint & MWAIT_SUBSTATE_MASK)) { -+ retval = -1; -+ goto out; -+ } -+ -+ /* mwait ecx extensions INTERRUPT_BREAK should be supported for C2/C3 */ -+ if (!(ecx & CPUID5_ECX_EXTENSIONS_SUPPORTED) || -+ !(ecx & CPUID5_ECX_INTERRUPT_BREAK)) { -+ retval = -1; -+ goto out; -+ } -+ -+ if (!mwait_supported[cstate_type]) { -+ mwait_supported[cstate_type] = 1; -+ printk(KERN_DEBUG -+ "Monitor-Mwait will be used to enter C-%d state\n", -+ cstate_type); -+ } -+ -+out: -+ return retval; -+} -+ -+int sfi_processor_power_init(struct sfi_processor *pr) -+{ -+ -+ int totallen; -+ struct sfi_cstate_table_entry *pentry; -+ u32 sfi_max_states; -+ -+ pentry = sfi_cstate_array; -+ -+#ifdef CONFIG_MSTWN_POWER_MGMT -+ sfi_max_states = SFI_PROCESSOR_MAX_POWER - 1; -+#else -+ sfi_max_states = SFI_PROCESSOR_MAX_POWER; -+#endif -+ -+ for (totallen = 1; totallen <= sfi_cstate_num && -+ totallen < sfi_max_states; totallen++, pentry++) { -+ pr->power.states[totallen].power_usage = 0; -+ pr->power.states[totallen].exit_latency = pentry->latency; -+ -+ pr->power.sfi_cstates[totallen].hint = pentry->hint; -+ pr->power.sfi_cstates[totallen].latency = pentry->latency; -+ -+ sfi_cstate_probe(pentry->hint); -+ -+ printk(KERN_INFO "Cstate[%d]: hint = 0x%08x, latency = %dms\n", -+ totallen, pentry->hint, pentry->latency); -+ } -+ -+#ifdef CONFIG_MSTWN_POWER_MGMT -+ -+ p1_c6 = 0; -+ -+ /* this initialization is for the S0i3 state */ -+ pr->power.states[totallen].power_usage = 0; -+ pr->power.states[totallen].exit_latency = s0ix_latency; -+ -+ pr->power.sfi_cstates[totallen].hint = 0; -+ pr->power.sfi_cstates[totallen].latency = s0ix_latency; -+ -+ mwait_supported[STATE_S0IX] = 1; -+#endif -+ -+ sfi_processor_setup_cpuidle(pr); -+ pr->power.dev.cpu = pr->id; -+ if (cpuidle_register_device(&pr->power.dev)) -+ return -EIO; -+ -+ return 0; -+} -+ -+int sfi_processor_power_exit(struct sfi_processor *pr) -+{ -+ cpuidle_unregister_device(&pr->power.dev); -+ return 0; -+} -Index: linux-2.6.33/arch/x86/kernel/sfi/sfi_processor_perflib.c -=================================================================== ---- /dev/null -+++ linux-2.6.33/arch/x86/kernel/sfi/sfi_processor_perflib.c -@@ -0,0 +1,185 @@ -+/* -+ * sfi_Processor_perflib.c - sfi Processor P-States Library -+ * -+ * -+ * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -+ * -+ * This program is free software; you can redistribute it and/or modify -+ * it under the terms of the GNU General Public License as published by -+ * the Free Software Foundation; either version 2 of the License, or (at -+ * your option) any later version. -+ * -+ * This program is distributed in the hope that it will be useful, but -+ * WITHOUT ANY WARRANTY; without even the implied warranty of -+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU -+ * General Public License for more details. -+ * -+ * You should have received a copy of the GNU General Public License along -+ * with this program; if not, write to the Free Software Foundation, Inc., -+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. -+ * -+ * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -+ * Author: Vishwesh M Rudramuni -+ * Contact information: Vishwesh Rudramuni <vishwesh.m.rudramuni@intel.com> -+ */ -+ -+#include <linux/kernel.h> -+#include <linux/module.h> -+#include <linux/init.h> -+#include <linux/cpufreq.h> -+#include <linux/sfi.h> -+#include <linux/sfi_processor.h> -+ -+#define SFI_PROCESSOR_COMPONENT 0x01000000 -+#define SFI_PROCESSOR_CLASS "processor" -+#define SFI_PROCESSOR_FILE_PERFORMANCE "performance" -+#define _COMPONENT SFI_PROCESSOR_COMPONENT -+ -+static DEFINE_MUTEX(performance_mutex); -+ -+/* Use cpufreq debug layer for _PPC changes. */ -+#define cpufreq_printk(msg...) cpufreq_debug_printk(CPUFREQ_DEBUG_CORE, \ -+ "cpufreq-core", msg) -+ -+static void sfi_cpufreq_add_file(struct sfi_processor *pr) -+{ -+ return; -+} -+static void sfi_cpufreq_remove_file(struct sfi_processor *pr) -+{ -+ return; -+} -+ -+struct sfi_cpufreq_table_entry sfi_cpufreq_array[SFI_PROCESSOR_MAX_POWER]; -+EXPORT_SYMBOL_GPL(sfi_cpufreq_array); -+ -+int sfi_cpufreq_num; -+EXPORT_SYMBOL_GPL(sfi_cpufreq_num); -+ -+static int __init sfi_parse_freq(struct sfi_table_header *table) -+{ -+ struct sfi_table_simple *sb; -+ struct sfi_cpufreq_table_entry *pentry; -+ int totallen; -+ -+ sb = (struct sfi_table_simple *)table; -+ if (!sb) { -+ printk(KERN_WARNING "SFI: Unable to map FREQ\n"); -+ return -ENODEV; -+ } -+ -+ if (!sfi_cpufreq_num) { -+ sfi_cpufreq_num = SFI_GET_NUM_ENTRIES(sb, -+ struct sfi_cpufreq_table_entry); -+ pentry = (struct sfi_cpufreq_table_entry *)sb->pentry; -+ totallen = sfi_cpufreq_num * sizeof(*pentry); -+ memcpy(sfi_cpufreq_array, pentry, totallen); -+ } -+ -+ printk(KERN_INFO "SFI: P state info (num = %d):\n", sfi_cpufreq_num); -+ pentry = sfi_cpufreq_array; -+ for (totallen = 0; totallen < sfi_cpufreq_num; totallen++, pentry++) { -+ printk(KERN_INFO "Pstate[%d]: freq = %dMHz latency = %dms" -+ " ctrl = 0x%08x\n", totallen, pentry->freq, -+ pentry->latency, pentry->ctrl_val); -+ } -+ -+ return 0; -+} -+ -+ -+static int sfi_processor_get_performance_states(struct sfi_processor *pr) -+{ -+ int result = 0; -+ int i; -+ -+ sfi_table_parse(SFI_SIG_FREQ, NULL, NULL, sfi_parse_freq); -+ -+ -+ pr->performance->state_count = sfi_cpufreq_num; -+ pr->performance->states = -+ kmalloc(sizeof(struct sfi_processor_px) * sfi_cpufreq_num, -+ GFP_KERNEL); -+ if (!pr->performance->states) -+ result = -ENOMEM; -+ -+ printk(KERN_INFO "Num p-states %d\n", sfi_cpufreq_num); -+ -+ /* Populate the P-states info from the SFI table here */ -+ for (i = 0; i < sfi_cpufreq_num; i++) { -+ pr->performance->states[i].core_frequency = \ -+ sfi_cpufreq_array[i].freq; -+ pr->performance->states[i].transition_latency = \ -+ sfi_cpufreq_array[i].latency; -+ pr->performance->states[i].control = \ -+ sfi_cpufreq_array[i].ctrl_val; -+ printk(KERN_INFO "State [%d]: core_frequency[%d] \ -+ transition_latency[%d] \ -+ control[0x%x] status[0x%x]\n", i, -+ (u32) pr->performance->states[i].core_frequency, -+ (u32) pr->performance->states[i].transition_latency, -+ (u32) pr->performance->states[i].control, -+ (u32) pr->performance->states[i].status); -+ } -+ -+ return result; -+} -+ -+int -+sfi_processor_register_performance(struct sfi_processor_performance -+ *performance, unsigned int cpu) -+{ -+ struct sfi_processor *pr; -+ -+ mutex_lock(&performance_mutex); -+ -+ pr = per_cpu(sfi_processors, cpu); -+ if (!pr) { -+ mutex_unlock(&performance_mutex); -+ return -ENODEV; -+ } -+ -+ if (pr->performance) { -+ mutex_unlock(&performance_mutex); -+ return -EBUSY; -+ } -+ -+ WARN_ON(!performance); -+ -+ pr->performance = performance; -+ -+ sfi_processor_get_performance_states(pr); -+ -+ sfi_cpufreq_add_file(pr); -+ -+ mutex_unlock(&performance_mutex); -+ return 0; -+} -+EXPORT_SYMBOL(sfi_processor_register_performance); -+ -+void -+sfi_processor_unregister_performance(struct sfi_processor_performance -+ *performance, unsigned int cpu) -+{ -+ struct sfi_processor *pr; -+ -+ -+ mutex_lock(&performance_mutex); -+ -+ pr = per_cpu(sfi_processors, cpu); -+ if (!pr) { -+ mutex_unlock(&performance_mutex); -+ return; -+ } -+ -+ if (pr->performance) -+ kfree(pr->performance->states); -+ pr->performance = NULL; -+ -+ sfi_cpufreq_remove_file(pr); -+ -+ mutex_unlock(&performance_mutex); -+ -+ return; -+} -+EXPORT_SYMBOL(sfi_processor_unregister_performance); -Index: linux-2.6.33/drivers/sfi/Kconfig -=================================================================== ---- linux-2.6.33.orig/drivers/sfi/Kconfig -+++ linux-2.6.33/drivers/sfi/Kconfig -@@ -15,3 +15,13 @@ menuconfig SFI - For more information, see http://simplefirmware.org - - Say 'Y' here to enable the kernel to boot on SFI-only platforms. -+config SFI_PROCESSOR_PM -+ bool "SFI Processor Power Management" -+ depends on SFI && X86_LOCAL_APIC -+ default y -+ -+config SFI_CPUIDLE -+ bool "SFI Processor C-State driver" -+ depends on SFI_PROCESSOR_PM && CPU_IDLE -+ default y -+ -Index: linux-2.6.33/include/linux/sfi_processor.h -=================================================================== ---- /dev/null -+++ linux-2.6.33/include/linux/sfi_processor.h -@@ -0,0 +1,102 @@ -+/* -+ * sfi_processor.h -+ * -+ * Copyright (C) 2008 Intel Corp -+ * -+ * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -+ * This program is free software; you can redistribute it and/or modify -+ * it under the terms of the GNU General Public License as published by -+ * the Free Software Foundation; version 2 of the License. -+ * -+ * This program is distributed in the hope that it will be useful, but -+ * WITHOUT ANY WARRANTY; without even the implied warranty of -+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU -+ * General Public License for more details. -+ * -+ * You should have received a copy of the GNU General Public License along -+ * with this program; if not, write to the Free Software Foundation, Inc., -+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. -+ * -+ * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -+ * Author: Sujith Thomas -+ * Contact information: Sujith Thomas <sujith.thomas@intel.com> -+ */ -+ -+#ifndef __SFI_PROCESSOR_H__ -+#define __SFI_PROCESSOR_H__ -+#include <linux/sfi.h> -+#include <linux/cpuidle.h> -+ -+#define SFI_PROCESSOR_MAX_POWER 7 -+ -+#define CPU_SFI_GET_NUM(ptable, entry) \ -+ ((ptable->header.length - SFI_TBL_HEADER_LEN) / \ -+ (sizeof(struct entry))) -+ -+struct sfi_processor_power { -+ struct cpuidle_device dev; -+ u32 default_state; -+ int count; -+ struct cpuidle_state states[SFI_PROCESSOR_MAX_POWER]; -+ struct sfi_cstate_table_entry sfi_cstates[SFI_PROCESSOR_MAX_POWER]; -+}; -+ -+struct sfi_processor_flags { -+ u8 valid; -+ u8 power; -+}; -+ -+struct sfi_processor { -+ u32 id; -+ struct sfi_processor_flags flags; -+ struct sfi_processor_power power; -+ struct sfi_processor_performance *performance; -+}; -+ -+/* Performance management */ -+struct sfi_processor_px { -+ u32 core_frequency; /* megahertz */ -+ u32 transition_latency; /* microseconds */ -+ u32 control; /* control value */ -+ u32 status; /* success indicator */ -+}; -+ -+struct sfi_processor_performance { -+ unsigned int state; -+ unsigned int state_count; -+ struct sfi_processor_px *states; -+ cpumask_var_t shared_cpu_map; -+ unsigned int shared_type; -+}; -+ -+#define SFI_STATE_C0 (u8) 0 -+#define SFI_STATE_C1 (u8) 1 -+#define SFI_STATE_C2 (u8) 2 -+#define SFI_STATE_C3 (u8) 3 -+#define SFI_STATE_C4 (u8) 4 -+#define SFI_STATE_C5 (u8) 5 -+#define SFI_STATE_C6 (u8) 6 -+ -+#define SFI_C_STATES_MAX SFI_STATE_C6 -+#define SFI_C_STATE_COUNT 6 -+ -+extern struct cpuidle_driver sfi_idle_driver; -+ -+/* for communication between multiple parts of the processor kernel module */ -+DECLARE_PER_CPU(struct sfi_processor *, sfi_processors); -+ -+int sfi_processor_power_init(struct sfi_processor *pr); -+int sfi_processor_power_exit(struct sfi_processor *pr); -+extern int sfi_processor_register_performance(struct sfi_processor_performance -+ *performance, unsigned int cpu); -+extern void sfi_processor_unregister_performance(struct -+ sfi_processor_performance -+ *performance, -+ unsigned int cpu); -+extern struct sfi_cstate_table_entry sfi_cstate_array[SFI_C_STATES_MAX]; -+extern int sfi_cstate_num; -+ -+extern struct sfi_cstate_table_entry sfi_cstate_array[SFI_C_STATES_MAX]; -+extern int sfi_cstate_num; -+ -+#endif /*__SFI_PROCESSOR_H__*/ -Index: linux-2.6.33/include/linux/sfi.h -=================================================================== ---- linux-2.6.33.orig/include/linux/sfi.h -+++ linux-2.6.33/include/linux/sfi.h -@@ -120,6 +120,13 @@ struct sfi_cstate_table_entry { - u32 latency; /* latency in ms */ - } __packed; - -+ -+struct sfi_cpufreq_table_entry { -+ u32 freq; -+ u32 latency; /* transition latency in ms for this pstate */ -+ u32 ctrl_val; /* value to write to PERF_CTL to enter thisstate */ -+}__packed; -+ - struct sfi_apic_table_entry { - u64 phys_addr; /* phy base addr for APIC reg */ - } __packed; |