From 239a368d5715d8f5b7733f9400339c2350c49369 Mon Sep 17 00:00:00 2001 From: Saul Wold Date: Fri, 24 Sep 2010 15:36:24 -0700 Subject: netbook: Correct netbook build by moving netbook configuration from moblin to meta Signed-off-by: Saul Wold --- ...linux-2.6.34-moorestown-platform-enabling.patch | 13580 +++++++++++++++++++ 1 file changed, 13580 insertions(+) create mode 100644 meta/recipes-kernel/linux/linux-netbook-2.6.33.2/linux-2.6.34-moorestown-platform-enabling.patch (limited to 'meta/recipes-kernel/linux/linux-netbook-2.6.33.2/linux-2.6.34-moorestown-platform-enabling.patch') diff --git a/meta/recipes-kernel/linux/linux-netbook-2.6.33.2/linux-2.6.34-moorestown-platform-enabling.patch b/meta/recipes-kernel/linux/linux-netbook-2.6.33.2/linux-2.6.34-moorestown-platform-enabling.patch new file mode 100644 index 000000000..7f81eb82f --- /dev/null +++ b/meta/recipes-kernel/linux/linux-netbook-2.6.33.2/linux-2.6.34-moorestown-platform-enabling.patch @@ -0,0 +1,13580 @@ +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 + #include + ++#include + #include + #include + #include +@@ -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 ++#include + + #include + #include ++#include + #include + #include + #include +@@ -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 + #include + #include ++#include + + #include + #include +@@ -67,6 +68,7 @@ + #include + + #include ++#include + + #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 ++ ++#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 ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include ++#include ++ ++#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 ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include + ++#include + #include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#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 ++ * ++ * 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 ++#include ++#include ++#include ++#include ++ ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#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 ++#include ++#include ++ ++#include ++#include ++#include ++ ++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 ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include ++ ++#include ++#include ++ ++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 ++ * ++ * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ ++ * ++ * 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 ++#include ++#include ++#include ++#include ++ ++#include ++#include ++ ++#define MRST_MAX_DMA_LEN 2047 ++#ifdef CONFIG_SPI_MRST_DMA ++#include ++#endif ++ ++#ifdef CONFIG_DEBUG_FS ++#include ++#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 "); ++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 ++ ++/* 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 ++ * ++ * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ ++ * ++ * 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 ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include ++#include ++#include ++#include ++#include ++ ++#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 + #include + #include ++#include + #include + #include + +@@ -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 ++#include ++#include ++#include ++ ++#include ++#include ++ ++#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 ++*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ ++* ++*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 ++#include ++ ++#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 ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#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\ ++ \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 "); ++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 ++#include ++ ++#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 ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++/* 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 + #include + #include ++#include + #include + #include + #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 + #include + #include ++#include + + #include "../core/hcd.h" + +@@ -43,6 +44,8 @@ + #include + #include + #include ++#include ++#include + + /*-------------------------------------------------------------------------*/ + +@@ -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 ++ * ++ * 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<= 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 ++ * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ ++ * ++ * 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 ++#include ++#include ++ ++#include ++#include ++ ++ ++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 ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include ++ ++#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 "); ++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 ++ ++/* 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 . ++ ++ 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 ++ */ ++ ++/* ++ * This sfi Processor P-States Driver re-uses most part of the code available ++ * in acpi cpufreq driver. ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include ++#include ++ ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#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 ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++ ++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 ++ * Author: Vishwesh Rudramuni ++ * Contact information: Vishwesh M Rudramuni ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++ ++#ifdef CONFIG_MSTWN_POWER_MGMT ++#include ++#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 ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#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 ++ */ ++ ++#ifndef __SFI_PROCESSOR_H__ ++#define __SFI_PROCESSOR_H__ ++#include ++#include ++ ++#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; -- cgit v1.2.3