Commit 05454c26eb3587b56abc5eb139797ac5afb6d77a
Committed by
H. Peter Anvin
1 parent
d8059302b3
Exists in
smarc-imx_3.14.28_1.0.0_ga
and in
1 other branch
intel_mid: Renamed *mrst* to *intel_mid*
Following files contains code that is common to all intel mid soc's. So renamed them as below. mrst/mrst.c -> intel-mid/intel-mid.c mrst/vrtc.c -> intel-mid/intel_mid_vrtc.c mrst/early_printk_mrst.c -> intel-mid/intel_mid_vrtc.c pci/mrst.c -> pci/intel_mid_pci.c Also, renamed the corresponding header files and made changes to the driver files that included these header files. To ensure that there are no functional changes, I have compared the objdump of renamed files before and after rename and found that the only difference is file name change. Signed-off-by: Kuppuswamy Sathyanarayanan <sathyanarayanan.kuppuswamy@linux.intel.com> Link: http://lkml.kernel.org/r/1382049336-21316-4-git-send-email-david.a.cohen@linux.intel.com Signed-off-by: David Cohen <david.a.cohen@linux.intel.com> Signed-off-by: H. Peter Anvin <hpa@linux.intel.com>
Showing 25 changed files with 1972 additions and 1971 deletions Inline Diff
- arch/x86/include/asm/intel-mid.h
- arch/x86/include/asm/intel_mid_vrtc.h
- arch/x86/include/asm/mrst-vrtc.h
- arch/x86/include/asm/mrst.h
- arch/x86/kernel/apb_timer.c
- arch/x86/kernel/early_printk.c
- arch/x86/kernel/rtc.c
- arch/x86/pci/Makefile
- arch/x86/pci/intel_mid_pci.c
- arch/x86/pci/mrst.c
- arch/x86/platform/Makefile
- arch/x86/platform/intel-mid/Makefile
- arch/x86/platform/intel-mid/early_printk_intel_mid.c
- arch/x86/platform/intel-mid/intel-mid.c
- arch/x86/platform/intel-mid/intel_mid_vrtc.c
- arch/x86/platform/mrst/Makefile
- arch/x86/platform/mrst/early_printk_mrst.c
- arch/x86/platform/mrst/mrst.c
- arch/x86/platform/mrst/vrtc.c
- drivers/gpu/drm/gma500/mdfld_dsi_output.h
- drivers/gpu/drm/gma500/oaktrail_device.c
- drivers/gpu/drm/gma500/oaktrail_lvds.c
- drivers/platform/x86/intel_scu_ipc.c
- drivers/rtc/rtc-mrst.c
- drivers/watchdog/intel_scu_watchdog.c
arch/x86/include/asm/intel-mid.h
File was created | 1 | /* | |
2 | * intel-mid.h: Intel MID specific setup code | ||
3 | * | ||
4 | * (C) Copyright 2009 Intel Corporation | ||
5 | * | ||
6 | * This program is free software; you can redistribute it and/or | ||
7 | * modify it under the terms of the GNU General Public License | ||
8 | * as published by the Free Software Foundation; version 2 | ||
9 | * of the License. | ||
10 | */ | ||
11 | #ifndef _ASM_X86_INTEL_MID_H | ||
12 | #define _ASM_X86_INTEL_MID_H | ||
13 | |||
14 | #include <linux/sfi.h> | ||
15 | |||
16 | extern int pci_mrst_init(void); | ||
17 | extern int __init sfi_parse_mrtc(struct sfi_table_header *table); | ||
18 | extern int sfi_mrtc_num; | ||
19 | extern struct sfi_rtc_table_entry sfi_mrtc_array[]; | ||
20 | |||
21 | /* | ||
22 | * Medfield is the follow-up of Moorestown, it combines two chip solution into | ||
23 | * one. Other than that it also added always-on and constant tsc and lapic | ||
24 | * timers. Medfield is the platform name, and the chip name is called Penwell | ||
25 | * we treat Medfield/Penwell as a variant of Moorestown. Penwell can be | ||
26 | * identified via MSRs. | ||
27 | */ | ||
28 | enum mrst_cpu_type { | ||
29 | /* 1 was Moorestown */ | ||
30 | MRST_CPU_CHIP_PENWELL = 2, | ||
31 | }; | ||
32 | |||
33 | extern enum mrst_cpu_type __mrst_cpu_chip; | ||
34 | |||
35 | #ifdef CONFIG_X86_INTEL_MID | ||
36 | |||
37 | static inline enum mrst_cpu_type mrst_identify_cpu(void) | ||
38 | { | ||
39 | return __mrst_cpu_chip; | ||
40 | } | ||
41 | |||
42 | #else /* !CONFIG_X86_INTEL_MID */ | ||
43 | |||
44 | #define mrst_identify_cpu() (0) | ||
45 | |||
46 | #endif /* !CONFIG_X86_INTEL_MID */ | ||
47 | |||
48 | enum mrst_timer_options { | ||
49 | MRST_TIMER_DEFAULT, | ||
50 | MRST_TIMER_APBT_ONLY, | ||
51 | MRST_TIMER_LAPIC_APBT, | ||
52 | }; | ||
53 | |||
54 | extern enum mrst_timer_options mrst_timer_options; | ||
55 | |||
56 | /* | ||
57 | * Penwell uses spread spectrum clock, so the freq number is not exactly | ||
58 | * the same as reported by MSR based on SDM. | ||
59 | */ | ||
60 | #define PENWELL_FSB_FREQ_83SKU 83200 | ||
61 | #define PENWELL_FSB_FREQ_100SKU 99840 | ||
62 | |||
63 | #define SFI_MTMR_MAX_NUM 8 | ||
64 | #define SFI_MRTC_MAX 8 | ||
65 | |||
66 | extern struct console early_mrst_console; | ||
67 | extern void mrst_early_console_init(void); | ||
68 | |||
69 | extern struct console early_hsu_console; | ||
70 | extern void hsu_early_console_init(const char *); | ||
71 | |||
72 | extern void intel_scu_devices_create(void); | ||
73 | extern void intel_scu_devices_destroy(void); | ||
74 | |||
75 | /* VRTC timer */ | ||
76 | #define MRST_VRTC_MAP_SZ (1024) | ||
77 | /*#define MRST_VRTC_PGOFFSET (0xc00) */ | ||
78 | |||
79 | extern void mrst_rtc_init(void); | ||
80 | |||
81 | #endif /* _ASM_X86_INTEL_MID_H */ | ||
82 |
arch/x86/include/asm/intel_mid_vrtc.h
File was created | 1 | #ifndef _INTEL_MID_VRTC_H | |
2 | #define _INTEL_MID_VRTC_H | ||
3 | |||
4 | extern unsigned char vrtc_cmos_read(unsigned char reg); | ||
5 | extern void vrtc_cmos_write(unsigned char val, unsigned char reg); | ||
6 | extern void vrtc_get_time(struct timespec *now); | ||
7 | extern int vrtc_set_mmss(const struct timespec *now); | ||
8 | |||
9 | #endif | ||
10 |
arch/x86/include/asm/mrst-vrtc.h
1 | #ifndef _MRST_VRTC_H | File was deleted | |
2 | #define _MRST_VRTC_H | ||
3 | |||
4 | extern unsigned char vrtc_cmos_read(unsigned char reg); | ||
5 | extern void vrtc_cmos_write(unsigned char val, unsigned char reg); | ||
6 | extern void vrtc_get_time(struct timespec *now); | ||
7 | extern int vrtc_set_mmss(const struct timespec *now); | ||
8 | |||
9 | #endif | ||
10 | 1 | #ifndef _MRST_VRTC_H |
arch/x86/include/asm/mrst.h
1 | /* | File was deleted | |
2 | * mrst.h: Intel Moorestown platform specific setup code | ||
3 | * | ||
4 | * (C) Copyright 2009 Intel Corporation | ||
5 | * | ||
6 | * This program is free software; you can redistribute it and/or | ||
7 | * modify it under the terms of the GNU General Public License | ||
8 | * as published by the Free Software Foundation; version 2 | ||
9 | * of the License. | ||
10 | */ | ||
11 | #ifndef _ASM_X86_MRST_H | ||
12 | #define _ASM_X86_MRST_H | ||
13 | |||
14 | #include <linux/sfi.h> | ||
15 | |||
16 | extern int pci_mrst_init(void); | ||
17 | extern int __init sfi_parse_mrtc(struct sfi_table_header *table); | ||
18 | extern int sfi_mrtc_num; | ||
19 | extern struct sfi_rtc_table_entry sfi_mrtc_array[]; | ||
20 | |||
21 | /* | ||
22 | * Medfield is the follow-up of Moorestown, it combines two chip solution into | ||
23 | * one. Other than that it also added always-on and constant tsc and lapic | ||
24 | * timers. Medfield is the platform name, and the chip name is called Penwell | ||
25 | * we treat Medfield/Penwell as a variant of Moorestown. Penwell can be | ||
26 | * identified via MSRs. | ||
27 | */ | ||
28 | enum mrst_cpu_type { | ||
29 | /* 1 was Moorestown */ | ||
30 | MRST_CPU_CHIP_PENWELL = 2, | ||
31 | }; | ||
32 | |||
33 | extern enum mrst_cpu_type __mrst_cpu_chip; | ||
34 | |||
35 | #ifdef CONFIG_X86_INTEL_MID | ||
36 | |||
37 | static inline enum mrst_cpu_type mrst_identify_cpu(void) | ||
38 | { | ||
39 | return __mrst_cpu_chip; | ||
40 | } | ||
41 | |||
42 | #else /* !CONFIG_X86_INTEL_MID */ | ||
43 | |||
44 | #define mrst_identify_cpu() (0) | ||
45 | |||
46 | #endif /* !CONFIG_X86_INTEL_MID */ | ||
47 | |||
48 | enum mrst_timer_options { | ||
49 | MRST_TIMER_DEFAULT, | ||
50 | MRST_TIMER_APBT_ONLY, | ||
51 | MRST_TIMER_LAPIC_APBT, | ||
52 | }; | ||
53 | |||
54 | extern enum mrst_timer_options mrst_timer_options; | ||
55 | |||
56 | /* | ||
57 | * Penwell uses spread spectrum clock, so the freq number is not exactly | ||
58 | * the same as reported by MSR based on SDM. | ||
59 | */ | ||
60 | #define PENWELL_FSB_FREQ_83SKU 83200 | ||
61 | #define PENWELL_FSB_FREQ_100SKU 99840 | ||
62 | |||
63 | #define SFI_MTMR_MAX_NUM 8 | ||
64 | #define SFI_MRTC_MAX 8 | ||
65 | |||
66 | extern struct console early_mrst_console; | ||
67 | extern void mrst_early_console_init(void); | ||
68 | |||
69 | extern struct console early_hsu_console; | ||
70 | extern void hsu_early_console_init(const char *); | ||
71 | |||
72 | extern void intel_scu_devices_create(void); | ||
73 | extern void intel_scu_devices_destroy(void); | ||
74 | |||
75 | /* VRTC timer */ | ||
76 | #define MRST_VRTC_MAP_SZ (1024) | ||
77 | /*#define MRST_VRTC_PGOFFSET (0xc00) */ | ||
78 | |||
79 | extern void mrst_rtc_init(void); | ||
80 | |||
81 | #endif /* _ASM_X86_MRST_H */ | ||
82 | 1 | /* |
arch/x86/kernel/apb_timer.c
1 | /* | 1 | /* |
2 | * apb_timer.c: Driver for Langwell APB timers | 2 | * apb_timer.c: Driver for Langwell APB timers |
3 | * | 3 | * |
4 | * (C) Copyright 2009 Intel Corporation | 4 | * (C) Copyright 2009 Intel Corporation |
5 | * Author: Jacob Pan (jacob.jun.pan@intel.com) | 5 | * Author: Jacob Pan (jacob.jun.pan@intel.com) |
6 | * | 6 | * |
7 | * This program is free software; you can redistribute it and/or | 7 | * This program is free software; you can redistribute it and/or |
8 | * modify it under the terms of the GNU General Public License | 8 | * modify it under the terms of the GNU General Public License |
9 | * as published by the Free Software Foundation; version 2 | 9 | * as published by the Free Software Foundation; version 2 |
10 | * of the License. | 10 | * of the License. |
11 | * | 11 | * |
12 | * Note: | 12 | * Note: |
13 | * Langwell is the south complex of Intel Moorestown MID platform. There are | 13 | * Langwell is the south complex of Intel Moorestown MID platform. There are |
14 | * eight external timers in total that can be used by the operating system. | 14 | * eight external timers in total that can be used by the operating system. |
15 | * The timer information, such as frequency and addresses, is provided to the | 15 | * The timer information, such as frequency and addresses, is provided to the |
16 | * OS via SFI tables. | 16 | * OS via SFI tables. |
17 | * Timer interrupts are routed via FW/HW emulated IOAPIC independently via | 17 | * Timer interrupts are routed via FW/HW emulated IOAPIC independently via |
18 | * individual redirection table entries (RTE). | 18 | * individual redirection table entries (RTE). |
19 | * Unlike HPET, there is no master counter, therefore one of the timers are | 19 | * Unlike HPET, there is no master counter, therefore one of the timers are |
20 | * used as clocksource. The overall allocation looks like: | 20 | * used as clocksource. The overall allocation looks like: |
21 | * - timer 0 - NR_CPUs for per cpu timer | 21 | * - timer 0 - NR_CPUs for per cpu timer |
22 | * - one timer for clocksource | 22 | * - one timer for clocksource |
23 | * - one timer for watchdog driver. | 23 | * - one timer for watchdog driver. |
24 | * It is also worth notice that APB timer does not support true one-shot mode, | 24 | * It is also worth notice that APB timer does not support true one-shot mode, |
25 | * free-running mode will be used here to emulate one-shot mode. | 25 | * free-running mode will be used here to emulate one-shot mode. |
26 | * APB timer can also be used as broadcast timer along with per cpu local APIC | 26 | * APB timer can also be used as broadcast timer along with per cpu local APIC |
27 | * timer, but by default APB timer has higher rating than local APIC timers. | 27 | * timer, but by default APB timer has higher rating than local APIC timers. |
28 | */ | 28 | */ |
29 | 29 | ||
30 | #include <linux/delay.h> | 30 | #include <linux/delay.h> |
31 | #include <linux/dw_apb_timer.h> | 31 | #include <linux/dw_apb_timer.h> |
32 | #include <linux/errno.h> | 32 | #include <linux/errno.h> |
33 | #include <linux/init.h> | 33 | #include <linux/init.h> |
34 | #include <linux/slab.h> | 34 | #include <linux/slab.h> |
35 | #include <linux/pm.h> | 35 | #include <linux/pm.h> |
36 | #include <linux/sfi.h> | 36 | #include <linux/sfi.h> |
37 | #include <linux/interrupt.h> | 37 | #include <linux/interrupt.h> |
38 | #include <linux/cpu.h> | 38 | #include <linux/cpu.h> |
39 | #include <linux/irq.h> | 39 | #include <linux/irq.h> |
40 | 40 | ||
41 | #include <asm/fixmap.h> | 41 | #include <asm/fixmap.h> |
42 | #include <asm/apb_timer.h> | 42 | #include <asm/apb_timer.h> |
43 | #include <asm/mrst.h> | 43 | #include <asm/intel-mid.h> |
44 | #include <asm/time.h> | 44 | #include <asm/time.h> |
45 | 45 | ||
46 | #define APBT_CLOCKEVENT_RATING 110 | 46 | #define APBT_CLOCKEVENT_RATING 110 |
47 | #define APBT_CLOCKSOURCE_RATING 250 | 47 | #define APBT_CLOCKSOURCE_RATING 250 |
48 | 48 | ||
49 | #define APBT_CLOCKEVENT0_NUM (0) | 49 | #define APBT_CLOCKEVENT0_NUM (0) |
50 | #define APBT_CLOCKSOURCE_NUM (2) | 50 | #define APBT_CLOCKSOURCE_NUM (2) |
51 | 51 | ||
52 | static phys_addr_t apbt_address; | 52 | static phys_addr_t apbt_address; |
53 | static int apb_timer_block_enabled; | 53 | static int apb_timer_block_enabled; |
54 | static void __iomem *apbt_virt_address; | 54 | static void __iomem *apbt_virt_address; |
55 | 55 | ||
56 | /* | 56 | /* |
57 | * Common DW APB timer info | 57 | * Common DW APB timer info |
58 | */ | 58 | */ |
59 | static unsigned long apbt_freq; | 59 | static unsigned long apbt_freq; |
60 | 60 | ||
61 | struct apbt_dev { | 61 | struct apbt_dev { |
62 | struct dw_apb_clock_event_device *timer; | 62 | struct dw_apb_clock_event_device *timer; |
63 | unsigned int num; | 63 | unsigned int num; |
64 | int cpu; | 64 | int cpu; |
65 | unsigned int irq; | 65 | unsigned int irq; |
66 | char name[10]; | 66 | char name[10]; |
67 | }; | 67 | }; |
68 | 68 | ||
69 | static struct dw_apb_clocksource *clocksource_apbt; | 69 | static struct dw_apb_clocksource *clocksource_apbt; |
70 | 70 | ||
71 | static inline void __iomem *adev_virt_addr(struct apbt_dev *adev) | 71 | static inline void __iomem *adev_virt_addr(struct apbt_dev *adev) |
72 | { | 72 | { |
73 | return apbt_virt_address + adev->num * APBTMRS_REG_SIZE; | 73 | return apbt_virt_address + adev->num * APBTMRS_REG_SIZE; |
74 | } | 74 | } |
75 | 75 | ||
76 | static DEFINE_PER_CPU(struct apbt_dev, cpu_apbt_dev); | 76 | static DEFINE_PER_CPU(struct apbt_dev, cpu_apbt_dev); |
77 | 77 | ||
78 | #ifdef CONFIG_SMP | 78 | #ifdef CONFIG_SMP |
79 | static unsigned int apbt_num_timers_used; | 79 | static unsigned int apbt_num_timers_used; |
80 | #endif | 80 | #endif |
81 | 81 | ||
82 | static inline void apbt_set_mapping(void) | 82 | static inline void apbt_set_mapping(void) |
83 | { | 83 | { |
84 | struct sfi_timer_table_entry *mtmr; | 84 | struct sfi_timer_table_entry *mtmr; |
85 | int phy_cs_timer_id = 0; | 85 | int phy_cs_timer_id = 0; |
86 | 86 | ||
87 | if (apbt_virt_address) { | 87 | if (apbt_virt_address) { |
88 | pr_debug("APBT base already mapped\n"); | 88 | pr_debug("APBT base already mapped\n"); |
89 | return; | 89 | return; |
90 | } | 90 | } |
91 | mtmr = sfi_get_mtmr(APBT_CLOCKEVENT0_NUM); | 91 | mtmr = sfi_get_mtmr(APBT_CLOCKEVENT0_NUM); |
92 | if (mtmr == NULL) { | 92 | if (mtmr == NULL) { |
93 | printk(KERN_ERR "Failed to get MTMR %d from SFI\n", | 93 | printk(KERN_ERR "Failed to get MTMR %d from SFI\n", |
94 | APBT_CLOCKEVENT0_NUM); | 94 | APBT_CLOCKEVENT0_NUM); |
95 | return; | 95 | return; |
96 | } | 96 | } |
97 | apbt_address = (phys_addr_t)mtmr->phys_addr; | 97 | apbt_address = (phys_addr_t)mtmr->phys_addr; |
98 | if (!apbt_address) { | 98 | if (!apbt_address) { |
99 | printk(KERN_WARNING "No timer base from SFI, use default\n"); | 99 | printk(KERN_WARNING "No timer base from SFI, use default\n"); |
100 | apbt_address = APBT_DEFAULT_BASE; | 100 | apbt_address = APBT_DEFAULT_BASE; |
101 | } | 101 | } |
102 | apbt_virt_address = ioremap_nocache(apbt_address, APBT_MMAP_SIZE); | 102 | apbt_virt_address = ioremap_nocache(apbt_address, APBT_MMAP_SIZE); |
103 | if (!apbt_virt_address) { | 103 | if (!apbt_virt_address) { |
104 | pr_debug("Failed mapping APBT phy address at %lu\n",\ | 104 | pr_debug("Failed mapping APBT phy address at %lu\n",\ |
105 | (unsigned long)apbt_address); | 105 | (unsigned long)apbt_address); |
106 | goto panic_noapbt; | 106 | goto panic_noapbt; |
107 | } | 107 | } |
108 | apbt_freq = mtmr->freq_hz; | 108 | apbt_freq = mtmr->freq_hz; |
109 | sfi_free_mtmr(mtmr); | 109 | sfi_free_mtmr(mtmr); |
110 | 110 | ||
111 | /* Now figure out the physical timer id for clocksource device */ | 111 | /* Now figure out the physical timer id for clocksource device */ |
112 | mtmr = sfi_get_mtmr(APBT_CLOCKSOURCE_NUM); | 112 | mtmr = sfi_get_mtmr(APBT_CLOCKSOURCE_NUM); |
113 | if (mtmr == NULL) | 113 | if (mtmr == NULL) |
114 | goto panic_noapbt; | 114 | goto panic_noapbt; |
115 | 115 | ||
116 | /* Now figure out the physical timer id */ | 116 | /* Now figure out the physical timer id */ |
117 | pr_debug("Use timer %d for clocksource\n", | 117 | pr_debug("Use timer %d for clocksource\n", |
118 | (int)(mtmr->phys_addr & 0xff) / APBTMRS_REG_SIZE); | 118 | (int)(mtmr->phys_addr & 0xff) / APBTMRS_REG_SIZE); |
119 | phy_cs_timer_id = (unsigned int)(mtmr->phys_addr & 0xff) / | 119 | phy_cs_timer_id = (unsigned int)(mtmr->phys_addr & 0xff) / |
120 | APBTMRS_REG_SIZE; | 120 | APBTMRS_REG_SIZE; |
121 | 121 | ||
122 | clocksource_apbt = dw_apb_clocksource_init(APBT_CLOCKSOURCE_RATING, | 122 | clocksource_apbt = dw_apb_clocksource_init(APBT_CLOCKSOURCE_RATING, |
123 | "apbt0", apbt_virt_address + phy_cs_timer_id * | 123 | "apbt0", apbt_virt_address + phy_cs_timer_id * |
124 | APBTMRS_REG_SIZE, apbt_freq); | 124 | APBTMRS_REG_SIZE, apbt_freq); |
125 | return; | 125 | return; |
126 | 126 | ||
127 | panic_noapbt: | 127 | panic_noapbt: |
128 | panic("Failed to setup APB system timer\n"); | 128 | panic("Failed to setup APB system timer\n"); |
129 | 129 | ||
130 | } | 130 | } |
131 | 131 | ||
132 | static inline void apbt_clear_mapping(void) | 132 | static inline void apbt_clear_mapping(void) |
133 | { | 133 | { |
134 | iounmap(apbt_virt_address); | 134 | iounmap(apbt_virt_address); |
135 | apbt_virt_address = NULL; | 135 | apbt_virt_address = NULL; |
136 | } | 136 | } |
137 | 137 | ||
138 | /* | 138 | /* |
139 | * APBT timer interrupt enable / disable | 139 | * APBT timer interrupt enable / disable |
140 | */ | 140 | */ |
141 | static inline int is_apbt_capable(void) | 141 | static inline int is_apbt_capable(void) |
142 | { | 142 | { |
143 | return apbt_virt_address ? 1 : 0; | 143 | return apbt_virt_address ? 1 : 0; |
144 | } | 144 | } |
145 | 145 | ||
146 | static int __init apbt_clockevent_register(void) | 146 | static int __init apbt_clockevent_register(void) |
147 | { | 147 | { |
148 | struct sfi_timer_table_entry *mtmr; | 148 | struct sfi_timer_table_entry *mtmr; |
149 | struct apbt_dev *adev = &__get_cpu_var(cpu_apbt_dev); | 149 | struct apbt_dev *adev = &__get_cpu_var(cpu_apbt_dev); |
150 | 150 | ||
151 | mtmr = sfi_get_mtmr(APBT_CLOCKEVENT0_NUM); | 151 | mtmr = sfi_get_mtmr(APBT_CLOCKEVENT0_NUM); |
152 | if (mtmr == NULL) { | 152 | if (mtmr == NULL) { |
153 | printk(KERN_ERR "Failed to get MTMR %d from SFI\n", | 153 | printk(KERN_ERR "Failed to get MTMR %d from SFI\n", |
154 | APBT_CLOCKEVENT0_NUM); | 154 | APBT_CLOCKEVENT0_NUM); |
155 | return -ENODEV; | 155 | return -ENODEV; |
156 | } | 156 | } |
157 | 157 | ||
158 | adev->num = smp_processor_id(); | 158 | adev->num = smp_processor_id(); |
159 | adev->timer = dw_apb_clockevent_init(smp_processor_id(), "apbt0", | 159 | adev->timer = dw_apb_clockevent_init(smp_processor_id(), "apbt0", |
160 | mrst_timer_options == MRST_TIMER_LAPIC_APBT ? | 160 | mrst_timer_options == MRST_TIMER_LAPIC_APBT ? |
161 | APBT_CLOCKEVENT_RATING - 100 : APBT_CLOCKEVENT_RATING, | 161 | APBT_CLOCKEVENT_RATING - 100 : APBT_CLOCKEVENT_RATING, |
162 | adev_virt_addr(adev), 0, apbt_freq); | 162 | adev_virt_addr(adev), 0, apbt_freq); |
163 | /* Firmware does EOI handling for us. */ | 163 | /* Firmware does EOI handling for us. */ |
164 | adev->timer->eoi = NULL; | 164 | adev->timer->eoi = NULL; |
165 | 165 | ||
166 | if (mrst_timer_options == MRST_TIMER_LAPIC_APBT) { | 166 | if (mrst_timer_options == MRST_TIMER_LAPIC_APBT) { |
167 | global_clock_event = &adev->timer->ced; | 167 | global_clock_event = &adev->timer->ced; |
168 | printk(KERN_DEBUG "%s clockevent registered as global\n", | 168 | printk(KERN_DEBUG "%s clockevent registered as global\n", |
169 | global_clock_event->name); | 169 | global_clock_event->name); |
170 | } | 170 | } |
171 | 171 | ||
172 | dw_apb_clockevent_register(adev->timer); | 172 | dw_apb_clockevent_register(adev->timer); |
173 | 173 | ||
174 | sfi_free_mtmr(mtmr); | 174 | sfi_free_mtmr(mtmr); |
175 | return 0; | 175 | return 0; |
176 | } | 176 | } |
177 | 177 | ||
178 | #ifdef CONFIG_SMP | 178 | #ifdef CONFIG_SMP |
179 | 179 | ||
180 | static void apbt_setup_irq(struct apbt_dev *adev) | 180 | static void apbt_setup_irq(struct apbt_dev *adev) |
181 | { | 181 | { |
182 | /* timer0 irq has been setup early */ | 182 | /* timer0 irq has been setup early */ |
183 | if (adev->irq == 0) | 183 | if (adev->irq == 0) |
184 | return; | 184 | return; |
185 | 185 | ||
186 | irq_modify_status(adev->irq, 0, IRQ_MOVE_PCNTXT); | 186 | irq_modify_status(adev->irq, 0, IRQ_MOVE_PCNTXT); |
187 | irq_set_affinity(adev->irq, cpumask_of(adev->cpu)); | 187 | irq_set_affinity(adev->irq, cpumask_of(adev->cpu)); |
188 | /* APB timer irqs are set up as mp_irqs, timer is edge type */ | 188 | /* APB timer irqs are set up as mp_irqs, timer is edge type */ |
189 | __irq_set_handler(adev->irq, handle_edge_irq, 0, "edge"); | 189 | __irq_set_handler(adev->irq, handle_edge_irq, 0, "edge"); |
190 | } | 190 | } |
191 | 191 | ||
192 | /* Should be called with per cpu */ | 192 | /* Should be called with per cpu */ |
193 | void apbt_setup_secondary_clock(void) | 193 | void apbt_setup_secondary_clock(void) |
194 | { | 194 | { |
195 | struct apbt_dev *adev; | 195 | struct apbt_dev *adev; |
196 | int cpu; | 196 | int cpu; |
197 | 197 | ||
198 | /* Don't register boot CPU clockevent */ | 198 | /* Don't register boot CPU clockevent */ |
199 | cpu = smp_processor_id(); | 199 | cpu = smp_processor_id(); |
200 | if (!cpu) | 200 | if (!cpu) |
201 | return; | 201 | return; |
202 | 202 | ||
203 | adev = &__get_cpu_var(cpu_apbt_dev); | 203 | adev = &__get_cpu_var(cpu_apbt_dev); |
204 | if (!adev->timer) { | 204 | if (!adev->timer) { |
205 | adev->timer = dw_apb_clockevent_init(cpu, adev->name, | 205 | adev->timer = dw_apb_clockevent_init(cpu, adev->name, |
206 | APBT_CLOCKEVENT_RATING, adev_virt_addr(adev), | 206 | APBT_CLOCKEVENT_RATING, adev_virt_addr(adev), |
207 | adev->irq, apbt_freq); | 207 | adev->irq, apbt_freq); |
208 | adev->timer->eoi = NULL; | 208 | adev->timer->eoi = NULL; |
209 | } else { | 209 | } else { |
210 | dw_apb_clockevent_resume(adev->timer); | 210 | dw_apb_clockevent_resume(adev->timer); |
211 | } | 211 | } |
212 | 212 | ||
213 | printk(KERN_INFO "Registering CPU %d clockevent device %s, cpu %08x\n", | 213 | printk(KERN_INFO "Registering CPU %d clockevent device %s, cpu %08x\n", |
214 | cpu, adev->name, adev->cpu); | 214 | cpu, adev->name, adev->cpu); |
215 | 215 | ||
216 | apbt_setup_irq(adev); | 216 | apbt_setup_irq(adev); |
217 | dw_apb_clockevent_register(adev->timer); | 217 | dw_apb_clockevent_register(adev->timer); |
218 | 218 | ||
219 | return; | 219 | return; |
220 | } | 220 | } |
221 | 221 | ||
222 | /* | 222 | /* |
223 | * this notify handler process CPU hotplug events. in case of S0i3, nonboot | 223 | * this notify handler process CPU hotplug events. in case of S0i3, nonboot |
224 | * cpus are disabled/enabled frequently, for performance reasons, we keep the | 224 | * cpus are disabled/enabled frequently, for performance reasons, we keep the |
225 | * per cpu timer irq registered so that we do need to do free_irq/request_irq. | 225 | * per cpu timer irq registered so that we do need to do free_irq/request_irq. |
226 | * | 226 | * |
227 | * TODO: it might be more reliable to directly disable percpu clockevent device | 227 | * TODO: it might be more reliable to directly disable percpu clockevent device |
228 | * without the notifier chain. currently, cpu 0 may get interrupts from other | 228 | * without the notifier chain. currently, cpu 0 may get interrupts from other |
229 | * cpu timers during the offline process due to the ordering of notification. | 229 | * cpu timers during the offline process due to the ordering of notification. |
230 | * the extra interrupt is harmless. | 230 | * the extra interrupt is harmless. |
231 | */ | 231 | */ |
232 | static int apbt_cpuhp_notify(struct notifier_block *n, | 232 | static int apbt_cpuhp_notify(struct notifier_block *n, |
233 | unsigned long action, void *hcpu) | 233 | unsigned long action, void *hcpu) |
234 | { | 234 | { |
235 | unsigned long cpu = (unsigned long)hcpu; | 235 | unsigned long cpu = (unsigned long)hcpu; |
236 | struct apbt_dev *adev = &per_cpu(cpu_apbt_dev, cpu); | 236 | struct apbt_dev *adev = &per_cpu(cpu_apbt_dev, cpu); |
237 | 237 | ||
238 | switch (action & 0xf) { | 238 | switch (action & 0xf) { |
239 | case CPU_DEAD: | 239 | case CPU_DEAD: |
240 | dw_apb_clockevent_pause(adev->timer); | 240 | dw_apb_clockevent_pause(adev->timer); |
241 | if (system_state == SYSTEM_RUNNING) { | 241 | if (system_state == SYSTEM_RUNNING) { |
242 | pr_debug("skipping APBT CPU %lu offline\n", cpu); | 242 | pr_debug("skipping APBT CPU %lu offline\n", cpu); |
243 | } else { | 243 | } else { |
244 | pr_debug("APBT clockevent for cpu %lu offline\n", cpu); | 244 | pr_debug("APBT clockevent for cpu %lu offline\n", cpu); |
245 | dw_apb_clockevent_stop(adev->timer); | 245 | dw_apb_clockevent_stop(adev->timer); |
246 | } | 246 | } |
247 | break; | 247 | break; |
248 | default: | 248 | default: |
249 | pr_debug("APBT notified %lu, no action\n", action); | 249 | pr_debug("APBT notified %lu, no action\n", action); |
250 | } | 250 | } |
251 | return NOTIFY_OK; | 251 | return NOTIFY_OK; |
252 | } | 252 | } |
253 | 253 | ||
254 | static __init int apbt_late_init(void) | 254 | static __init int apbt_late_init(void) |
255 | { | 255 | { |
256 | if (mrst_timer_options == MRST_TIMER_LAPIC_APBT || | 256 | if (mrst_timer_options == MRST_TIMER_LAPIC_APBT || |
257 | !apb_timer_block_enabled) | 257 | !apb_timer_block_enabled) |
258 | return 0; | 258 | return 0; |
259 | /* This notifier should be called after workqueue is ready */ | 259 | /* This notifier should be called after workqueue is ready */ |
260 | hotcpu_notifier(apbt_cpuhp_notify, -20); | 260 | hotcpu_notifier(apbt_cpuhp_notify, -20); |
261 | return 0; | 261 | return 0; |
262 | } | 262 | } |
263 | fs_initcall(apbt_late_init); | 263 | fs_initcall(apbt_late_init); |
264 | #else | 264 | #else |
265 | 265 | ||
266 | void apbt_setup_secondary_clock(void) {} | 266 | void apbt_setup_secondary_clock(void) {} |
267 | 267 | ||
268 | #endif /* CONFIG_SMP */ | 268 | #endif /* CONFIG_SMP */ |
269 | 269 | ||
270 | static int apbt_clocksource_register(void) | 270 | static int apbt_clocksource_register(void) |
271 | { | 271 | { |
272 | u64 start, now; | 272 | u64 start, now; |
273 | cycle_t t1; | 273 | cycle_t t1; |
274 | 274 | ||
275 | /* Start the counter, use timer 2 as source, timer 0/1 for event */ | 275 | /* Start the counter, use timer 2 as source, timer 0/1 for event */ |
276 | dw_apb_clocksource_start(clocksource_apbt); | 276 | dw_apb_clocksource_start(clocksource_apbt); |
277 | 277 | ||
278 | /* Verify whether apbt counter works */ | 278 | /* Verify whether apbt counter works */ |
279 | t1 = dw_apb_clocksource_read(clocksource_apbt); | 279 | t1 = dw_apb_clocksource_read(clocksource_apbt); |
280 | rdtscll(start); | 280 | rdtscll(start); |
281 | 281 | ||
282 | /* | 282 | /* |
283 | * We don't know the TSC frequency yet, but waiting for | 283 | * We don't know the TSC frequency yet, but waiting for |
284 | * 200000 TSC cycles is safe: | 284 | * 200000 TSC cycles is safe: |
285 | * 4 GHz == 50us | 285 | * 4 GHz == 50us |
286 | * 1 GHz == 200us | 286 | * 1 GHz == 200us |
287 | */ | 287 | */ |
288 | do { | 288 | do { |
289 | rep_nop(); | 289 | rep_nop(); |
290 | rdtscll(now); | 290 | rdtscll(now); |
291 | } while ((now - start) < 200000UL); | 291 | } while ((now - start) < 200000UL); |
292 | 292 | ||
293 | /* APBT is the only always on clocksource, it has to work! */ | 293 | /* APBT is the only always on clocksource, it has to work! */ |
294 | if (t1 == dw_apb_clocksource_read(clocksource_apbt)) | 294 | if (t1 == dw_apb_clocksource_read(clocksource_apbt)) |
295 | panic("APBT counter not counting. APBT disabled\n"); | 295 | panic("APBT counter not counting. APBT disabled\n"); |
296 | 296 | ||
297 | dw_apb_clocksource_register(clocksource_apbt); | 297 | dw_apb_clocksource_register(clocksource_apbt); |
298 | 298 | ||
299 | return 0; | 299 | return 0; |
300 | } | 300 | } |
301 | 301 | ||
302 | /* | 302 | /* |
303 | * Early setup the APBT timer, only use timer 0 for booting then switch to | 303 | * Early setup the APBT timer, only use timer 0 for booting then switch to |
304 | * per CPU timer if possible. | 304 | * per CPU timer if possible. |
305 | * returns 1 if per cpu apbt is setup | 305 | * returns 1 if per cpu apbt is setup |
306 | * returns 0 if no per cpu apbt is chosen | 306 | * returns 0 if no per cpu apbt is chosen |
307 | * panic if set up failed, this is the only platform timer on Moorestown. | 307 | * panic if set up failed, this is the only platform timer on Moorestown. |
308 | */ | 308 | */ |
309 | void __init apbt_time_init(void) | 309 | void __init apbt_time_init(void) |
310 | { | 310 | { |
311 | #ifdef CONFIG_SMP | 311 | #ifdef CONFIG_SMP |
312 | int i; | 312 | int i; |
313 | struct sfi_timer_table_entry *p_mtmr; | 313 | struct sfi_timer_table_entry *p_mtmr; |
314 | struct apbt_dev *adev; | 314 | struct apbt_dev *adev; |
315 | #endif | 315 | #endif |
316 | 316 | ||
317 | if (apb_timer_block_enabled) | 317 | if (apb_timer_block_enabled) |
318 | return; | 318 | return; |
319 | apbt_set_mapping(); | 319 | apbt_set_mapping(); |
320 | if (!apbt_virt_address) | 320 | if (!apbt_virt_address) |
321 | goto out_noapbt; | 321 | goto out_noapbt; |
322 | /* | 322 | /* |
323 | * Read the frequency and check for a sane value, for ESL model | 323 | * Read the frequency and check for a sane value, for ESL model |
324 | * we extend the possible clock range to allow time scaling. | 324 | * we extend the possible clock range to allow time scaling. |
325 | */ | 325 | */ |
326 | 326 | ||
327 | if (apbt_freq < APBT_MIN_FREQ || apbt_freq > APBT_MAX_FREQ) { | 327 | if (apbt_freq < APBT_MIN_FREQ || apbt_freq > APBT_MAX_FREQ) { |
328 | pr_debug("APBT has invalid freq 0x%lx\n", apbt_freq); | 328 | pr_debug("APBT has invalid freq 0x%lx\n", apbt_freq); |
329 | goto out_noapbt; | 329 | goto out_noapbt; |
330 | } | 330 | } |
331 | if (apbt_clocksource_register()) { | 331 | if (apbt_clocksource_register()) { |
332 | pr_debug("APBT has failed to register clocksource\n"); | 332 | pr_debug("APBT has failed to register clocksource\n"); |
333 | goto out_noapbt; | 333 | goto out_noapbt; |
334 | } | 334 | } |
335 | if (!apbt_clockevent_register()) | 335 | if (!apbt_clockevent_register()) |
336 | apb_timer_block_enabled = 1; | 336 | apb_timer_block_enabled = 1; |
337 | else { | 337 | else { |
338 | pr_debug("APBT has failed to register clockevent\n"); | 338 | pr_debug("APBT has failed to register clockevent\n"); |
339 | goto out_noapbt; | 339 | goto out_noapbt; |
340 | } | 340 | } |
341 | #ifdef CONFIG_SMP | 341 | #ifdef CONFIG_SMP |
342 | /* kernel cmdline disable apb timer, so we will use lapic timers */ | 342 | /* kernel cmdline disable apb timer, so we will use lapic timers */ |
343 | if (mrst_timer_options == MRST_TIMER_LAPIC_APBT) { | 343 | if (mrst_timer_options == MRST_TIMER_LAPIC_APBT) { |
344 | printk(KERN_INFO "apbt: disabled per cpu timer\n"); | 344 | printk(KERN_INFO "apbt: disabled per cpu timer\n"); |
345 | return; | 345 | return; |
346 | } | 346 | } |
347 | pr_debug("%s: %d CPUs online\n", __func__, num_online_cpus()); | 347 | pr_debug("%s: %d CPUs online\n", __func__, num_online_cpus()); |
348 | if (num_possible_cpus() <= sfi_mtimer_num) | 348 | if (num_possible_cpus() <= sfi_mtimer_num) |
349 | apbt_num_timers_used = num_possible_cpus(); | 349 | apbt_num_timers_used = num_possible_cpus(); |
350 | else | 350 | else |
351 | apbt_num_timers_used = 1; | 351 | apbt_num_timers_used = 1; |
352 | pr_debug("%s: %d APB timers used\n", __func__, apbt_num_timers_used); | 352 | pr_debug("%s: %d APB timers used\n", __func__, apbt_num_timers_used); |
353 | 353 | ||
354 | /* here we set up per CPU timer data structure */ | 354 | /* here we set up per CPU timer data structure */ |
355 | for (i = 0; i < apbt_num_timers_used; i++) { | 355 | for (i = 0; i < apbt_num_timers_used; i++) { |
356 | adev = &per_cpu(cpu_apbt_dev, i); | 356 | adev = &per_cpu(cpu_apbt_dev, i); |
357 | adev->num = i; | 357 | adev->num = i; |
358 | adev->cpu = i; | 358 | adev->cpu = i; |
359 | p_mtmr = sfi_get_mtmr(i); | 359 | p_mtmr = sfi_get_mtmr(i); |
360 | if (p_mtmr) | 360 | if (p_mtmr) |
361 | adev->irq = p_mtmr->irq; | 361 | adev->irq = p_mtmr->irq; |
362 | else | 362 | else |
363 | printk(KERN_ERR "Failed to get timer for cpu %d\n", i); | 363 | printk(KERN_ERR "Failed to get timer for cpu %d\n", i); |
364 | snprintf(adev->name, sizeof(adev->name) - 1, "apbt%d", i); | 364 | snprintf(adev->name, sizeof(adev->name) - 1, "apbt%d", i); |
365 | } | 365 | } |
366 | #endif | 366 | #endif |
367 | 367 | ||
368 | return; | 368 | return; |
369 | 369 | ||
370 | out_noapbt: | 370 | out_noapbt: |
371 | apbt_clear_mapping(); | 371 | apbt_clear_mapping(); |
372 | apb_timer_block_enabled = 0; | 372 | apb_timer_block_enabled = 0; |
373 | panic("failed to enable APB timer\n"); | 373 | panic("failed to enable APB timer\n"); |
374 | } | 374 | } |
375 | 375 | ||
376 | /* called before apb_timer_enable, use early map */ | 376 | /* called before apb_timer_enable, use early map */ |
377 | unsigned long apbt_quick_calibrate(void) | 377 | unsigned long apbt_quick_calibrate(void) |
378 | { | 378 | { |
379 | int i, scale; | 379 | int i, scale; |
380 | u64 old, new; | 380 | u64 old, new; |
381 | cycle_t t1, t2; | 381 | cycle_t t1, t2; |
382 | unsigned long khz = 0; | 382 | unsigned long khz = 0; |
383 | u32 loop, shift; | 383 | u32 loop, shift; |
384 | 384 | ||
385 | apbt_set_mapping(); | 385 | apbt_set_mapping(); |
386 | dw_apb_clocksource_start(clocksource_apbt); | 386 | dw_apb_clocksource_start(clocksource_apbt); |
387 | 387 | ||
388 | /* check if the timer can count down, otherwise return */ | 388 | /* check if the timer can count down, otherwise return */ |
389 | old = dw_apb_clocksource_read(clocksource_apbt); | 389 | old = dw_apb_clocksource_read(clocksource_apbt); |
390 | i = 10000; | 390 | i = 10000; |
391 | while (--i) { | 391 | while (--i) { |
392 | if (old != dw_apb_clocksource_read(clocksource_apbt)) | 392 | if (old != dw_apb_clocksource_read(clocksource_apbt)) |
393 | break; | 393 | break; |
394 | } | 394 | } |
395 | if (!i) | 395 | if (!i) |
396 | goto failed; | 396 | goto failed; |
397 | 397 | ||
398 | /* count 16 ms */ | 398 | /* count 16 ms */ |
399 | loop = (apbt_freq / 1000) << 4; | 399 | loop = (apbt_freq / 1000) << 4; |
400 | 400 | ||
401 | /* restart the timer to ensure it won't get to 0 in the calibration */ | 401 | /* restart the timer to ensure it won't get to 0 in the calibration */ |
402 | dw_apb_clocksource_start(clocksource_apbt); | 402 | dw_apb_clocksource_start(clocksource_apbt); |
403 | 403 | ||
404 | old = dw_apb_clocksource_read(clocksource_apbt); | 404 | old = dw_apb_clocksource_read(clocksource_apbt); |
405 | old += loop; | 405 | old += loop; |
406 | 406 | ||
407 | t1 = __native_read_tsc(); | 407 | t1 = __native_read_tsc(); |
408 | 408 | ||
409 | do { | 409 | do { |
410 | new = dw_apb_clocksource_read(clocksource_apbt); | 410 | new = dw_apb_clocksource_read(clocksource_apbt); |
411 | } while (new < old); | 411 | } while (new < old); |
412 | 412 | ||
413 | t2 = __native_read_tsc(); | 413 | t2 = __native_read_tsc(); |
414 | 414 | ||
415 | shift = 5; | 415 | shift = 5; |
416 | if (unlikely(loop >> shift == 0)) { | 416 | if (unlikely(loop >> shift == 0)) { |
417 | printk(KERN_INFO | 417 | printk(KERN_INFO |
418 | "APBT TSC calibration failed, not enough resolution\n"); | 418 | "APBT TSC calibration failed, not enough resolution\n"); |
419 | return 0; | 419 | return 0; |
420 | } | 420 | } |
421 | scale = (int)div_u64((t2 - t1), loop >> shift); | 421 | scale = (int)div_u64((t2 - t1), loop >> shift); |
422 | khz = (scale * (apbt_freq / 1000)) >> shift; | 422 | khz = (scale * (apbt_freq / 1000)) >> shift; |
423 | printk(KERN_INFO "TSC freq calculated by APB timer is %lu khz\n", khz); | 423 | printk(KERN_INFO "TSC freq calculated by APB timer is %lu khz\n", khz); |
424 | return khz; | 424 | return khz; |
425 | failed: | 425 | failed: |
426 | return 0; | 426 | return 0; |
427 | } | 427 | } |
428 | 428 |
arch/x86/kernel/early_printk.c
1 | #include <linux/console.h> | 1 | #include <linux/console.h> |
2 | #include <linux/kernel.h> | 2 | #include <linux/kernel.h> |
3 | #include <linux/init.h> | 3 | #include <linux/init.h> |
4 | #include <linux/string.h> | 4 | #include <linux/string.h> |
5 | #include <linux/screen_info.h> | 5 | #include <linux/screen_info.h> |
6 | #include <linux/usb/ch9.h> | 6 | #include <linux/usb/ch9.h> |
7 | #include <linux/pci_regs.h> | 7 | #include <linux/pci_regs.h> |
8 | #include <linux/pci_ids.h> | 8 | #include <linux/pci_ids.h> |
9 | #include <linux/errno.h> | 9 | #include <linux/errno.h> |
10 | #include <asm/io.h> | 10 | #include <asm/io.h> |
11 | #include <asm/processor.h> | 11 | #include <asm/processor.h> |
12 | #include <asm/fcntl.h> | 12 | #include <asm/fcntl.h> |
13 | #include <asm/setup.h> | 13 | #include <asm/setup.h> |
14 | #include <xen/hvc-console.h> | 14 | #include <xen/hvc-console.h> |
15 | #include <asm/pci-direct.h> | 15 | #include <asm/pci-direct.h> |
16 | #include <asm/fixmap.h> | 16 | #include <asm/fixmap.h> |
17 | #include <asm/mrst.h> | 17 | #include <asm/intel-mid.h> |
18 | #include <asm/pgtable.h> | 18 | #include <asm/pgtable.h> |
19 | #include <linux/usb/ehci_def.h> | 19 | #include <linux/usb/ehci_def.h> |
20 | 20 | ||
21 | /* Simple VGA output */ | 21 | /* Simple VGA output */ |
22 | #define VGABASE (__ISA_IO_base + 0xb8000) | 22 | #define VGABASE (__ISA_IO_base + 0xb8000) |
23 | 23 | ||
24 | static int max_ypos = 25, max_xpos = 80; | 24 | static int max_ypos = 25, max_xpos = 80; |
25 | static int current_ypos = 25, current_xpos; | 25 | static int current_ypos = 25, current_xpos; |
26 | 26 | ||
27 | static void early_vga_write(struct console *con, const char *str, unsigned n) | 27 | static void early_vga_write(struct console *con, const char *str, unsigned n) |
28 | { | 28 | { |
29 | char c; | 29 | char c; |
30 | int i, k, j; | 30 | int i, k, j; |
31 | 31 | ||
32 | while ((c = *str++) != '\0' && n-- > 0) { | 32 | while ((c = *str++) != '\0' && n-- > 0) { |
33 | if (current_ypos >= max_ypos) { | 33 | if (current_ypos >= max_ypos) { |
34 | /* scroll 1 line up */ | 34 | /* scroll 1 line up */ |
35 | for (k = 1, j = 0; k < max_ypos; k++, j++) { | 35 | for (k = 1, j = 0; k < max_ypos; k++, j++) { |
36 | for (i = 0; i < max_xpos; i++) { | 36 | for (i = 0; i < max_xpos; i++) { |
37 | writew(readw(VGABASE+2*(max_xpos*k+i)), | 37 | writew(readw(VGABASE+2*(max_xpos*k+i)), |
38 | VGABASE + 2*(max_xpos*j + i)); | 38 | VGABASE + 2*(max_xpos*j + i)); |
39 | } | 39 | } |
40 | } | 40 | } |
41 | for (i = 0; i < max_xpos; i++) | 41 | for (i = 0; i < max_xpos; i++) |
42 | writew(0x720, VGABASE + 2*(max_xpos*j + i)); | 42 | writew(0x720, VGABASE + 2*(max_xpos*j + i)); |
43 | current_ypos = max_ypos-1; | 43 | current_ypos = max_ypos-1; |
44 | } | 44 | } |
45 | #ifdef CONFIG_KGDB_KDB | 45 | #ifdef CONFIG_KGDB_KDB |
46 | if (c == '\b') { | 46 | if (c == '\b') { |
47 | if (current_xpos > 0) | 47 | if (current_xpos > 0) |
48 | current_xpos--; | 48 | current_xpos--; |
49 | } else if (c == '\r') { | 49 | } else if (c == '\r') { |
50 | current_xpos = 0; | 50 | current_xpos = 0; |
51 | } else | 51 | } else |
52 | #endif | 52 | #endif |
53 | if (c == '\n') { | 53 | if (c == '\n') { |
54 | current_xpos = 0; | 54 | current_xpos = 0; |
55 | current_ypos++; | 55 | current_ypos++; |
56 | } else if (c != '\r') { | 56 | } else if (c != '\r') { |
57 | writew(((0x7 << 8) | (unsigned short) c), | 57 | writew(((0x7 << 8) | (unsigned short) c), |
58 | VGABASE + 2*(max_xpos*current_ypos + | 58 | VGABASE + 2*(max_xpos*current_ypos + |
59 | current_xpos++)); | 59 | current_xpos++)); |
60 | if (current_xpos >= max_xpos) { | 60 | if (current_xpos >= max_xpos) { |
61 | current_xpos = 0; | 61 | current_xpos = 0; |
62 | current_ypos++; | 62 | current_ypos++; |
63 | } | 63 | } |
64 | } | 64 | } |
65 | } | 65 | } |
66 | } | 66 | } |
67 | 67 | ||
68 | static struct console early_vga_console = { | 68 | static struct console early_vga_console = { |
69 | .name = "earlyvga", | 69 | .name = "earlyvga", |
70 | .write = early_vga_write, | 70 | .write = early_vga_write, |
71 | .flags = CON_PRINTBUFFER, | 71 | .flags = CON_PRINTBUFFER, |
72 | .index = -1, | 72 | .index = -1, |
73 | }; | 73 | }; |
74 | 74 | ||
75 | /* Serial functions loosely based on a similar package from Klaus P. Gerlicher */ | 75 | /* Serial functions loosely based on a similar package from Klaus P. Gerlicher */ |
76 | 76 | ||
77 | static int early_serial_base = 0x3f8; /* ttyS0 */ | 77 | static int early_serial_base = 0x3f8; /* ttyS0 */ |
78 | 78 | ||
79 | #define XMTRDY 0x20 | 79 | #define XMTRDY 0x20 |
80 | 80 | ||
81 | #define DLAB 0x80 | 81 | #define DLAB 0x80 |
82 | 82 | ||
83 | #define TXR 0 /* Transmit register (WRITE) */ | 83 | #define TXR 0 /* Transmit register (WRITE) */ |
84 | #define RXR 0 /* Receive register (READ) */ | 84 | #define RXR 0 /* Receive register (READ) */ |
85 | #define IER 1 /* Interrupt Enable */ | 85 | #define IER 1 /* Interrupt Enable */ |
86 | #define IIR 2 /* Interrupt ID */ | 86 | #define IIR 2 /* Interrupt ID */ |
87 | #define FCR 2 /* FIFO control */ | 87 | #define FCR 2 /* FIFO control */ |
88 | #define LCR 3 /* Line control */ | 88 | #define LCR 3 /* Line control */ |
89 | #define MCR 4 /* Modem control */ | 89 | #define MCR 4 /* Modem control */ |
90 | #define LSR 5 /* Line Status */ | 90 | #define LSR 5 /* Line Status */ |
91 | #define MSR 6 /* Modem Status */ | 91 | #define MSR 6 /* Modem Status */ |
92 | #define DLL 0 /* Divisor Latch Low */ | 92 | #define DLL 0 /* Divisor Latch Low */ |
93 | #define DLH 1 /* Divisor latch High */ | 93 | #define DLH 1 /* Divisor latch High */ |
94 | 94 | ||
95 | static int early_serial_putc(unsigned char ch) | 95 | static int early_serial_putc(unsigned char ch) |
96 | { | 96 | { |
97 | unsigned timeout = 0xffff; | 97 | unsigned timeout = 0xffff; |
98 | 98 | ||
99 | while ((inb(early_serial_base + LSR) & XMTRDY) == 0 && --timeout) | 99 | while ((inb(early_serial_base + LSR) & XMTRDY) == 0 && --timeout) |
100 | cpu_relax(); | 100 | cpu_relax(); |
101 | outb(ch, early_serial_base + TXR); | 101 | outb(ch, early_serial_base + TXR); |
102 | return timeout ? 0 : -1; | 102 | return timeout ? 0 : -1; |
103 | } | 103 | } |
104 | 104 | ||
105 | static void early_serial_write(struct console *con, const char *s, unsigned n) | 105 | static void early_serial_write(struct console *con, const char *s, unsigned n) |
106 | { | 106 | { |
107 | while (*s && n-- > 0) { | 107 | while (*s && n-- > 0) { |
108 | if (*s == '\n') | 108 | if (*s == '\n') |
109 | early_serial_putc('\r'); | 109 | early_serial_putc('\r'); |
110 | early_serial_putc(*s); | 110 | early_serial_putc(*s); |
111 | s++; | 111 | s++; |
112 | } | 112 | } |
113 | } | 113 | } |
114 | 114 | ||
115 | #define DEFAULT_BAUD 9600 | 115 | #define DEFAULT_BAUD 9600 |
116 | 116 | ||
117 | static __init void early_serial_init(char *s) | 117 | static __init void early_serial_init(char *s) |
118 | { | 118 | { |
119 | unsigned char c; | 119 | unsigned char c; |
120 | unsigned divisor; | 120 | unsigned divisor; |
121 | unsigned baud = DEFAULT_BAUD; | 121 | unsigned baud = DEFAULT_BAUD; |
122 | char *e; | 122 | char *e; |
123 | 123 | ||
124 | if (*s == ',') | 124 | if (*s == ',') |
125 | ++s; | 125 | ++s; |
126 | 126 | ||
127 | if (*s) { | 127 | if (*s) { |
128 | unsigned port; | 128 | unsigned port; |
129 | if (!strncmp(s, "0x", 2)) { | 129 | if (!strncmp(s, "0x", 2)) { |
130 | early_serial_base = simple_strtoul(s, &e, 16); | 130 | early_serial_base = simple_strtoul(s, &e, 16); |
131 | } else { | 131 | } else { |
132 | static const int __initconst bases[] = { 0x3f8, 0x2f8 }; | 132 | static const int __initconst bases[] = { 0x3f8, 0x2f8 }; |
133 | 133 | ||
134 | if (!strncmp(s, "ttyS", 4)) | 134 | if (!strncmp(s, "ttyS", 4)) |
135 | s += 4; | 135 | s += 4; |
136 | port = simple_strtoul(s, &e, 10); | 136 | port = simple_strtoul(s, &e, 10); |
137 | if (port > 1 || s == e) | 137 | if (port > 1 || s == e) |
138 | port = 0; | 138 | port = 0; |
139 | early_serial_base = bases[port]; | 139 | early_serial_base = bases[port]; |
140 | } | 140 | } |
141 | s += strcspn(s, ","); | 141 | s += strcspn(s, ","); |
142 | if (*s == ',') | 142 | if (*s == ',') |
143 | s++; | 143 | s++; |
144 | } | 144 | } |
145 | 145 | ||
146 | outb(0x3, early_serial_base + LCR); /* 8n1 */ | 146 | outb(0x3, early_serial_base + LCR); /* 8n1 */ |
147 | outb(0, early_serial_base + IER); /* no interrupt */ | 147 | outb(0, early_serial_base + IER); /* no interrupt */ |
148 | outb(0, early_serial_base + FCR); /* no fifo */ | 148 | outb(0, early_serial_base + FCR); /* no fifo */ |
149 | outb(0x3, early_serial_base + MCR); /* DTR + RTS */ | 149 | outb(0x3, early_serial_base + MCR); /* DTR + RTS */ |
150 | 150 | ||
151 | if (*s) { | 151 | if (*s) { |
152 | baud = simple_strtoul(s, &e, 0); | 152 | baud = simple_strtoul(s, &e, 0); |
153 | if (baud == 0 || s == e) | 153 | if (baud == 0 || s == e) |
154 | baud = DEFAULT_BAUD; | 154 | baud = DEFAULT_BAUD; |
155 | } | 155 | } |
156 | 156 | ||
157 | divisor = 115200 / baud; | 157 | divisor = 115200 / baud; |
158 | c = inb(early_serial_base + LCR); | 158 | c = inb(early_serial_base + LCR); |
159 | outb(c | DLAB, early_serial_base + LCR); | 159 | outb(c | DLAB, early_serial_base + LCR); |
160 | outb(divisor & 0xff, early_serial_base + DLL); | 160 | outb(divisor & 0xff, early_serial_base + DLL); |
161 | outb((divisor >> 8) & 0xff, early_serial_base + DLH); | 161 | outb((divisor >> 8) & 0xff, early_serial_base + DLH); |
162 | outb(c & ~DLAB, early_serial_base + LCR); | 162 | outb(c & ~DLAB, early_serial_base + LCR); |
163 | } | 163 | } |
164 | 164 | ||
165 | static struct console early_serial_console = { | 165 | static struct console early_serial_console = { |
166 | .name = "earlyser", | 166 | .name = "earlyser", |
167 | .write = early_serial_write, | 167 | .write = early_serial_write, |
168 | .flags = CON_PRINTBUFFER, | 168 | .flags = CON_PRINTBUFFER, |
169 | .index = -1, | 169 | .index = -1, |
170 | }; | 170 | }; |
171 | 171 | ||
172 | static inline void early_console_register(struct console *con, int keep_early) | 172 | static inline void early_console_register(struct console *con, int keep_early) |
173 | { | 173 | { |
174 | if (con->index != -1) { | 174 | if (con->index != -1) { |
175 | printk(KERN_CRIT "ERROR: earlyprintk= %s already used\n", | 175 | printk(KERN_CRIT "ERROR: earlyprintk= %s already used\n", |
176 | con->name); | 176 | con->name); |
177 | return; | 177 | return; |
178 | } | 178 | } |
179 | early_console = con; | 179 | early_console = con; |
180 | if (keep_early) | 180 | if (keep_early) |
181 | early_console->flags &= ~CON_BOOT; | 181 | early_console->flags &= ~CON_BOOT; |
182 | else | 182 | else |
183 | early_console->flags |= CON_BOOT; | 183 | early_console->flags |= CON_BOOT; |
184 | register_console(early_console); | 184 | register_console(early_console); |
185 | } | 185 | } |
186 | 186 | ||
187 | static int __init setup_early_printk(char *buf) | 187 | static int __init setup_early_printk(char *buf) |
188 | { | 188 | { |
189 | int keep; | 189 | int keep; |
190 | 190 | ||
191 | if (!buf) | 191 | if (!buf) |
192 | return 0; | 192 | return 0; |
193 | 193 | ||
194 | if (early_console) | 194 | if (early_console) |
195 | return 0; | 195 | return 0; |
196 | 196 | ||
197 | keep = (strstr(buf, "keep") != NULL); | 197 | keep = (strstr(buf, "keep") != NULL); |
198 | 198 | ||
199 | while (*buf != '\0') { | 199 | while (*buf != '\0') { |
200 | if (!strncmp(buf, "serial", 6)) { | 200 | if (!strncmp(buf, "serial", 6)) { |
201 | buf += 6; | 201 | buf += 6; |
202 | early_serial_init(buf); | 202 | early_serial_init(buf); |
203 | early_console_register(&early_serial_console, keep); | 203 | early_console_register(&early_serial_console, keep); |
204 | if (!strncmp(buf, ",ttyS", 5)) | 204 | if (!strncmp(buf, ",ttyS", 5)) |
205 | buf += 5; | 205 | buf += 5; |
206 | } | 206 | } |
207 | if (!strncmp(buf, "ttyS", 4)) { | 207 | if (!strncmp(buf, "ttyS", 4)) { |
208 | early_serial_init(buf + 4); | 208 | early_serial_init(buf + 4); |
209 | early_console_register(&early_serial_console, keep); | 209 | early_console_register(&early_serial_console, keep); |
210 | } | 210 | } |
211 | if (!strncmp(buf, "vga", 3) && | 211 | if (!strncmp(buf, "vga", 3) && |
212 | boot_params.screen_info.orig_video_isVGA == 1) { | 212 | boot_params.screen_info.orig_video_isVGA == 1) { |
213 | max_xpos = boot_params.screen_info.orig_video_cols; | 213 | max_xpos = boot_params.screen_info.orig_video_cols; |
214 | max_ypos = boot_params.screen_info.orig_video_lines; | 214 | max_ypos = boot_params.screen_info.orig_video_lines; |
215 | current_ypos = boot_params.screen_info.orig_y; | 215 | current_ypos = boot_params.screen_info.orig_y; |
216 | early_console_register(&early_vga_console, keep); | 216 | early_console_register(&early_vga_console, keep); |
217 | } | 217 | } |
218 | #ifdef CONFIG_EARLY_PRINTK_DBGP | 218 | #ifdef CONFIG_EARLY_PRINTK_DBGP |
219 | if (!strncmp(buf, "dbgp", 4) && !early_dbgp_init(buf + 4)) | 219 | if (!strncmp(buf, "dbgp", 4) && !early_dbgp_init(buf + 4)) |
220 | early_console_register(&early_dbgp_console, keep); | 220 | early_console_register(&early_dbgp_console, keep); |
221 | #endif | 221 | #endif |
222 | #ifdef CONFIG_HVC_XEN | 222 | #ifdef CONFIG_HVC_XEN |
223 | if (!strncmp(buf, "xen", 3)) | 223 | if (!strncmp(buf, "xen", 3)) |
224 | early_console_register(&xenboot_console, keep); | 224 | early_console_register(&xenboot_console, keep); |
225 | #endif | 225 | #endif |
226 | #ifdef CONFIG_EARLY_PRINTK_INTEL_MID | 226 | #ifdef CONFIG_EARLY_PRINTK_INTEL_MID |
227 | if (!strncmp(buf, "mrst", 4)) { | 227 | if (!strncmp(buf, "mrst", 4)) { |
228 | mrst_early_console_init(); | 228 | mrst_early_console_init(); |
229 | early_console_register(&early_mrst_console, keep); | 229 | early_console_register(&early_mrst_console, keep); |
230 | } | 230 | } |
231 | 231 | ||
232 | if (!strncmp(buf, "hsu", 3)) { | 232 | if (!strncmp(buf, "hsu", 3)) { |
233 | hsu_early_console_init(buf + 3); | 233 | hsu_early_console_init(buf + 3); |
234 | early_console_register(&early_hsu_console, keep); | 234 | early_console_register(&early_hsu_console, keep); |
235 | } | 235 | } |
236 | #endif | 236 | #endif |
237 | buf++; | 237 | buf++; |
238 | } | 238 | } |
239 | return 0; | 239 | return 0; |
240 | } | 240 | } |
241 | 241 | ||
242 | early_param("earlyprintk", setup_early_printk); | 242 | early_param("earlyprintk", setup_early_printk); |
243 | 243 |
arch/x86/kernel/rtc.c
1 | /* | 1 | /* |
2 | * RTC related functions | 2 | * RTC related functions |
3 | */ | 3 | */ |
4 | #include <linux/platform_device.h> | 4 | #include <linux/platform_device.h> |
5 | #include <linux/mc146818rtc.h> | 5 | #include <linux/mc146818rtc.h> |
6 | #include <linux/acpi.h> | 6 | #include <linux/acpi.h> |
7 | #include <linux/bcd.h> | 7 | #include <linux/bcd.h> |
8 | #include <linux/export.h> | 8 | #include <linux/export.h> |
9 | #include <linux/pnp.h> | 9 | #include <linux/pnp.h> |
10 | #include <linux/of.h> | 10 | #include <linux/of.h> |
11 | 11 | ||
12 | #include <asm/vsyscall.h> | 12 | #include <asm/vsyscall.h> |
13 | #include <asm/x86_init.h> | 13 | #include <asm/x86_init.h> |
14 | #include <asm/time.h> | 14 | #include <asm/time.h> |
15 | #include <asm/mrst.h> | 15 | #include <asm/intel-mid.h> |
16 | #include <asm/rtc.h> | 16 | #include <asm/rtc.h> |
17 | 17 | ||
18 | #ifdef CONFIG_X86_32 | 18 | #ifdef CONFIG_X86_32 |
19 | /* | 19 | /* |
20 | * This is a special lock that is owned by the CPU and holds the index | 20 | * This is a special lock that is owned by the CPU and holds the index |
21 | * register we are working with. It is required for NMI access to the | 21 | * register we are working with. It is required for NMI access to the |
22 | * CMOS/RTC registers. See include/asm-i386/mc146818rtc.h for details. | 22 | * CMOS/RTC registers. See include/asm-i386/mc146818rtc.h for details. |
23 | */ | 23 | */ |
24 | volatile unsigned long cmos_lock; | 24 | volatile unsigned long cmos_lock; |
25 | EXPORT_SYMBOL(cmos_lock); | 25 | EXPORT_SYMBOL(cmos_lock); |
26 | #endif /* CONFIG_X86_32 */ | 26 | #endif /* CONFIG_X86_32 */ |
27 | 27 | ||
28 | /* For two digit years assume time is always after that */ | 28 | /* For two digit years assume time is always after that */ |
29 | #define CMOS_YEARS_OFFS 2000 | 29 | #define CMOS_YEARS_OFFS 2000 |
30 | 30 | ||
31 | DEFINE_SPINLOCK(rtc_lock); | 31 | DEFINE_SPINLOCK(rtc_lock); |
32 | EXPORT_SYMBOL(rtc_lock); | 32 | EXPORT_SYMBOL(rtc_lock); |
33 | 33 | ||
34 | /* | 34 | /* |
35 | * In order to set the CMOS clock precisely, set_rtc_mmss has to be | 35 | * In order to set the CMOS clock precisely, set_rtc_mmss has to be |
36 | * called 500 ms after the second nowtime has started, because when | 36 | * called 500 ms after the second nowtime has started, because when |
37 | * nowtime is written into the registers of the CMOS clock, it will | 37 | * nowtime is written into the registers of the CMOS clock, it will |
38 | * jump to the next second precisely 500 ms later. Check the Motorola | 38 | * jump to the next second precisely 500 ms later. Check the Motorola |
39 | * MC146818A or Dallas DS12887 data sheet for details. | 39 | * MC146818A or Dallas DS12887 data sheet for details. |
40 | */ | 40 | */ |
41 | int mach_set_rtc_mmss(const struct timespec *now) | 41 | int mach_set_rtc_mmss(const struct timespec *now) |
42 | { | 42 | { |
43 | unsigned long nowtime = now->tv_sec; | 43 | unsigned long nowtime = now->tv_sec; |
44 | struct rtc_time tm; | 44 | struct rtc_time tm; |
45 | int retval = 0; | 45 | int retval = 0; |
46 | 46 | ||
47 | rtc_time_to_tm(nowtime, &tm); | 47 | rtc_time_to_tm(nowtime, &tm); |
48 | if (!rtc_valid_tm(&tm)) { | 48 | if (!rtc_valid_tm(&tm)) { |
49 | retval = set_rtc_time(&tm); | 49 | retval = set_rtc_time(&tm); |
50 | if (retval) | 50 | if (retval) |
51 | printk(KERN_ERR "%s: RTC write failed with error %d\n", | 51 | printk(KERN_ERR "%s: RTC write failed with error %d\n", |
52 | __FUNCTION__, retval); | 52 | __FUNCTION__, retval); |
53 | } else { | 53 | } else { |
54 | printk(KERN_ERR | 54 | printk(KERN_ERR |
55 | "%s: Invalid RTC value: write of %lx to RTC failed\n", | 55 | "%s: Invalid RTC value: write of %lx to RTC failed\n", |
56 | __FUNCTION__, nowtime); | 56 | __FUNCTION__, nowtime); |
57 | retval = -EINVAL; | 57 | retval = -EINVAL; |
58 | } | 58 | } |
59 | return retval; | 59 | return retval; |
60 | } | 60 | } |
61 | 61 | ||
62 | void mach_get_cmos_time(struct timespec *now) | 62 | void mach_get_cmos_time(struct timespec *now) |
63 | { | 63 | { |
64 | unsigned int status, year, mon, day, hour, min, sec, century = 0; | 64 | unsigned int status, year, mon, day, hour, min, sec, century = 0; |
65 | unsigned long flags; | 65 | unsigned long flags; |
66 | 66 | ||
67 | spin_lock_irqsave(&rtc_lock, flags); | 67 | spin_lock_irqsave(&rtc_lock, flags); |
68 | 68 | ||
69 | /* | 69 | /* |
70 | * If UIP is clear, then we have >= 244 microseconds before | 70 | * If UIP is clear, then we have >= 244 microseconds before |
71 | * RTC registers will be updated. Spec sheet says that this | 71 | * RTC registers will be updated. Spec sheet says that this |
72 | * is the reliable way to read RTC - registers. If UIP is set | 72 | * is the reliable way to read RTC - registers. If UIP is set |
73 | * then the register access might be invalid. | 73 | * then the register access might be invalid. |
74 | */ | 74 | */ |
75 | while ((CMOS_READ(RTC_FREQ_SELECT) & RTC_UIP)) | 75 | while ((CMOS_READ(RTC_FREQ_SELECT) & RTC_UIP)) |
76 | cpu_relax(); | 76 | cpu_relax(); |
77 | 77 | ||
78 | sec = CMOS_READ(RTC_SECONDS); | 78 | sec = CMOS_READ(RTC_SECONDS); |
79 | min = CMOS_READ(RTC_MINUTES); | 79 | min = CMOS_READ(RTC_MINUTES); |
80 | hour = CMOS_READ(RTC_HOURS); | 80 | hour = CMOS_READ(RTC_HOURS); |
81 | day = CMOS_READ(RTC_DAY_OF_MONTH); | 81 | day = CMOS_READ(RTC_DAY_OF_MONTH); |
82 | mon = CMOS_READ(RTC_MONTH); | 82 | mon = CMOS_READ(RTC_MONTH); |
83 | year = CMOS_READ(RTC_YEAR); | 83 | year = CMOS_READ(RTC_YEAR); |
84 | 84 | ||
85 | #ifdef CONFIG_ACPI | 85 | #ifdef CONFIG_ACPI |
86 | if (acpi_gbl_FADT.header.revision >= FADT2_REVISION_ID && | 86 | if (acpi_gbl_FADT.header.revision >= FADT2_REVISION_ID && |
87 | acpi_gbl_FADT.century) | 87 | acpi_gbl_FADT.century) |
88 | century = CMOS_READ(acpi_gbl_FADT.century); | 88 | century = CMOS_READ(acpi_gbl_FADT.century); |
89 | #endif | 89 | #endif |
90 | 90 | ||
91 | status = CMOS_READ(RTC_CONTROL); | 91 | status = CMOS_READ(RTC_CONTROL); |
92 | WARN_ON_ONCE(RTC_ALWAYS_BCD && (status & RTC_DM_BINARY)); | 92 | WARN_ON_ONCE(RTC_ALWAYS_BCD && (status & RTC_DM_BINARY)); |
93 | 93 | ||
94 | spin_unlock_irqrestore(&rtc_lock, flags); | 94 | spin_unlock_irqrestore(&rtc_lock, flags); |
95 | 95 | ||
96 | if (RTC_ALWAYS_BCD || !(status & RTC_DM_BINARY)) { | 96 | if (RTC_ALWAYS_BCD || !(status & RTC_DM_BINARY)) { |
97 | sec = bcd2bin(sec); | 97 | sec = bcd2bin(sec); |
98 | min = bcd2bin(min); | 98 | min = bcd2bin(min); |
99 | hour = bcd2bin(hour); | 99 | hour = bcd2bin(hour); |
100 | day = bcd2bin(day); | 100 | day = bcd2bin(day); |
101 | mon = bcd2bin(mon); | 101 | mon = bcd2bin(mon); |
102 | year = bcd2bin(year); | 102 | year = bcd2bin(year); |
103 | } | 103 | } |
104 | 104 | ||
105 | if (century) { | 105 | if (century) { |
106 | century = bcd2bin(century); | 106 | century = bcd2bin(century); |
107 | year += century * 100; | 107 | year += century * 100; |
108 | } else | 108 | } else |
109 | year += CMOS_YEARS_OFFS; | 109 | year += CMOS_YEARS_OFFS; |
110 | 110 | ||
111 | now->tv_sec = mktime(year, mon, day, hour, min, sec); | 111 | now->tv_sec = mktime(year, mon, day, hour, min, sec); |
112 | now->tv_nsec = 0; | 112 | now->tv_nsec = 0; |
113 | } | 113 | } |
114 | 114 | ||
115 | /* Routines for accessing the CMOS RAM/RTC. */ | 115 | /* Routines for accessing the CMOS RAM/RTC. */ |
116 | unsigned char rtc_cmos_read(unsigned char addr) | 116 | unsigned char rtc_cmos_read(unsigned char addr) |
117 | { | 117 | { |
118 | unsigned char val; | 118 | unsigned char val; |
119 | 119 | ||
120 | lock_cmos_prefix(addr); | 120 | lock_cmos_prefix(addr); |
121 | outb(addr, RTC_PORT(0)); | 121 | outb(addr, RTC_PORT(0)); |
122 | val = inb(RTC_PORT(1)); | 122 | val = inb(RTC_PORT(1)); |
123 | lock_cmos_suffix(addr); | 123 | lock_cmos_suffix(addr); |
124 | 124 | ||
125 | return val; | 125 | return val; |
126 | } | 126 | } |
127 | EXPORT_SYMBOL(rtc_cmos_read); | 127 | EXPORT_SYMBOL(rtc_cmos_read); |
128 | 128 | ||
129 | void rtc_cmos_write(unsigned char val, unsigned char addr) | 129 | void rtc_cmos_write(unsigned char val, unsigned char addr) |
130 | { | 130 | { |
131 | lock_cmos_prefix(addr); | 131 | lock_cmos_prefix(addr); |
132 | outb(addr, RTC_PORT(0)); | 132 | outb(addr, RTC_PORT(0)); |
133 | outb(val, RTC_PORT(1)); | 133 | outb(val, RTC_PORT(1)); |
134 | lock_cmos_suffix(addr); | 134 | lock_cmos_suffix(addr); |
135 | } | 135 | } |
136 | EXPORT_SYMBOL(rtc_cmos_write); | 136 | EXPORT_SYMBOL(rtc_cmos_write); |
137 | 137 | ||
138 | int update_persistent_clock(struct timespec now) | 138 | int update_persistent_clock(struct timespec now) |
139 | { | 139 | { |
140 | return x86_platform.set_wallclock(&now); | 140 | return x86_platform.set_wallclock(&now); |
141 | } | 141 | } |
142 | 142 | ||
143 | /* not static: needed by APM */ | 143 | /* not static: needed by APM */ |
144 | void read_persistent_clock(struct timespec *ts) | 144 | void read_persistent_clock(struct timespec *ts) |
145 | { | 145 | { |
146 | x86_platform.get_wallclock(ts); | 146 | x86_platform.get_wallclock(ts); |
147 | } | 147 | } |
148 | 148 | ||
149 | 149 | ||
150 | static struct resource rtc_resources[] = { | 150 | static struct resource rtc_resources[] = { |
151 | [0] = { | 151 | [0] = { |
152 | .start = RTC_PORT(0), | 152 | .start = RTC_PORT(0), |
153 | .end = RTC_PORT(1), | 153 | .end = RTC_PORT(1), |
154 | .flags = IORESOURCE_IO, | 154 | .flags = IORESOURCE_IO, |
155 | }, | 155 | }, |
156 | [1] = { | 156 | [1] = { |
157 | .start = RTC_IRQ, | 157 | .start = RTC_IRQ, |
158 | .end = RTC_IRQ, | 158 | .end = RTC_IRQ, |
159 | .flags = IORESOURCE_IRQ, | 159 | .flags = IORESOURCE_IRQ, |
160 | } | 160 | } |
161 | }; | 161 | }; |
162 | 162 | ||
163 | static struct platform_device rtc_device = { | 163 | static struct platform_device rtc_device = { |
164 | .name = "rtc_cmos", | 164 | .name = "rtc_cmos", |
165 | .id = -1, | 165 | .id = -1, |
166 | .resource = rtc_resources, | 166 | .resource = rtc_resources, |
167 | .num_resources = ARRAY_SIZE(rtc_resources), | 167 | .num_resources = ARRAY_SIZE(rtc_resources), |
168 | }; | 168 | }; |
169 | 169 | ||
170 | static __init int add_rtc_cmos(void) | 170 | static __init int add_rtc_cmos(void) |
171 | { | 171 | { |
172 | #ifdef CONFIG_PNP | 172 | #ifdef CONFIG_PNP |
173 | static const char * const const ids[] __initconst = | 173 | static const char * const const ids[] __initconst = |
174 | { "PNP0b00", "PNP0b01", "PNP0b02", }; | 174 | { "PNP0b00", "PNP0b01", "PNP0b02", }; |
175 | struct pnp_dev *dev; | 175 | struct pnp_dev *dev; |
176 | struct pnp_id *id; | 176 | struct pnp_id *id; |
177 | int i; | 177 | int i; |
178 | 178 | ||
179 | pnp_for_each_dev(dev) { | 179 | pnp_for_each_dev(dev) { |
180 | for (id = dev->id; id; id = id->next) { | 180 | for (id = dev->id; id; id = id->next) { |
181 | for (i = 0; i < ARRAY_SIZE(ids); i++) { | 181 | for (i = 0; i < ARRAY_SIZE(ids); i++) { |
182 | if (compare_pnp_id(id, ids[i]) != 0) | 182 | if (compare_pnp_id(id, ids[i]) != 0) |
183 | return 0; | 183 | return 0; |
184 | } | 184 | } |
185 | } | 185 | } |
186 | } | 186 | } |
187 | #endif | 187 | #endif |
188 | if (of_have_populated_dt()) | 188 | if (of_have_populated_dt()) |
189 | return 0; | 189 | return 0; |
190 | 190 | ||
191 | /* Intel MID platforms don't have ioport rtc */ | 191 | /* Intel MID platforms don't have ioport rtc */ |
192 | if (mrst_identify_cpu()) | 192 | if (mrst_identify_cpu()) |
193 | return -ENODEV; | 193 | return -ENODEV; |
194 | 194 | ||
195 | platform_device_register(&rtc_device); | 195 | platform_device_register(&rtc_device); |
196 | dev_info(&rtc_device.dev, | 196 | dev_info(&rtc_device.dev, |
197 | "registered platform RTC device (no PNP device found)\n"); | 197 | "registered platform RTC device (no PNP device found)\n"); |
198 | 198 | ||
199 | return 0; | 199 | return 0; |
200 | } | 200 | } |
201 | device_initcall(add_rtc_cmos); | 201 | device_initcall(add_rtc_cmos); |
202 | 202 |
arch/x86/pci/Makefile
1 | obj-y := i386.o init.o | 1 | obj-y := i386.o init.o |
2 | 2 | ||
3 | obj-$(CONFIG_PCI_BIOS) += pcbios.o | 3 | obj-$(CONFIG_PCI_BIOS) += pcbios.o |
4 | obj-$(CONFIG_PCI_MMCONFIG) += mmconfig_$(BITS).o direct.o mmconfig-shared.o | 4 | obj-$(CONFIG_PCI_MMCONFIG) += mmconfig_$(BITS).o direct.o mmconfig-shared.o |
5 | obj-$(CONFIG_PCI_DIRECT) += direct.o | 5 | obj-$(CONFIG_PCI_DIRECT) += direct.o |
6 | obj-$(CONFIG_PCI_OLPC) += olpc.o | 6 | obj-$(CONFIG_PCI_OLPC) += olpc.o |
7 | obj-$(CONFIG_PCI_XEN) += xen.o | 7 | obj-$(CONFIG_PCI_XEN) += xen.o |
8 | 8 | ||
9 | obj-y += fixup.o | 9 | obj-y += fixup.o |
10 | obj-$(CONFIG_X86_INTEL_CE) += ce4100.o | 10 | obj-$(CONFIG_X86_INTEL_CE) += ce4100.o |
11 | obj-$(CONFIG_ACPI) += acpi.o | 11 | obj-$(CONFIG_ACPI) += acpi.o |
12 | obj-y += legacy.o irq.o | 12 | obj-y += legacy.o irq.o |
13 | 13 | ||
14 | obj-$(CONFIG_STA2X11) += sta2x11-fixup.o | 14 | obj-$(CONFIG_STA2X11) += sta2x11-fixup.o |
15 | 15 | ||
16 | obj-$(CONFIG_X86_VISWS) += visws.o | 16 | obj-$(CONFIG_X86_VISWS) += visws.o |
17 | 17 | ||
18 | obj-$(CONFIG_X86_NUMAQ) += numaq_32.o | 18 | obj-$(CONFIG_X86_NUMAQ) += numaq_32.o |
19 | obj-$(CONFIG_X86_NUMACHIP) += numachip.o | 19 | obj-$(CONFIG_X86_NUMACHIP) += numachip.o |
20 | 20 | ||
21 | obj-$(CONFIG_X86_INTEL_MID) += mrst.o | 21 | obj-$(CONFIG_X86_INTEL_MID) += intel_mid_pci.o |
22 | 22 | ||
23 | obj-y += common.o early.o | 23 | obj-y += common.o early.o |
24 | obj-y += bus_numa.o | 24 | obj-y += bus_numa.o |
25 | 25 | ||
26 | obj-$(CONFIG_AMD_NB) += amd_bus.o | 26 | obj-$(CONFIG_AMD_NB) += amd_bus.o |
27 | obj-$(CONFIG_PCI_CNB20LE_QUIRK) += broadcom_bus.o | 27 | obj-$(CONFIG_PCI_CNB20LE_QUIRK) += broadcom_bus.o |
28 | 28 | ||
29 | ifeq ($(CONFIG_PCI_DEBUG),y) | 29 | ifeq ($(CONFIG_PCI_DEBUG),y) |
30 | EXTRA_CFLAGS += -DDEBUG | 30 | EXTRA_CFLAGS += -DDEBUG |
31 | endif | 31 | endif |
32 | 32 |
arch/x86/pci/intel_mid_pci.c
File was created | 1 | /* | |
2 | * Intel MID PCI support | ||
3 | * Copyright (c) 2008 Intel Corporation | ||
4 | * Jesse Barnes <jesse.barnes@intel.com> | ||
5 | * | ||
6 | * Moorestown has an interesting PCI implementation: | ||
7 | * - configuration space is memory mapped (as defined by MCFG) | ||
8 | * - Lincroft devices also have a real, type 1 configuration space | ||
9 | * - Early Lincroft silicon has a type 1 access bug that will cause | ||
10 | * a hang if non-existent devices are accessed | ||
11 | * - some devices have the "fixed BAR" capability, which means | ||
12 | * they can't be relocated or modified; check for that during | ||
13 | * BAR sizing | ||
14 | * | ||
15 | * So, we use the MCFG space for all reads and writes, but also send | ||
16 | * Lincroft writes to type 1 space. But only read/write if the device | ||
17 | * actually exists, otherwise return all 1s for reads and bit bucket | ||
18 | * the writes. | ||
19 | */ | ||
20 | |||
21 | #include <linux/sched.h> | ||
22 | #include <linux/pci.h> | ||
23 | #include <linux/ioport.h> | ||
24 | #include <linux/init.h> | ||
25 | #include <linux/dmi.h> | ||
26 | #include <linux/acpi.h> | ||
27 | #include <linux/io.h> | ||
28 | #include <linux/smp.h> | ||
29 | |||
30 | #include <asm/segment.h> | ||
31 | #include <asm/pci_x86.h> | ||
32 | #include <asm/hw_irq.h> | ||
33 | #include <asm/io_apic.h> | ||
34 | |||
35 | #define PCIE_CAP_OFFSET 0x100 | ||
36 | |||
37 | /* Fixed BAR fields */ | ||
38 | #define PCIE_VNDR_CAP_ID_FIXED_BAR 0x00 /* Fixed BAR (TBD) */ | ||
39 | #define PCI_FIXED_BAR_0_SIZE 0x04 | ||
40 | #define PCI_FIXED_BAR_1_SIZE 0x08 | ||
41 | #define PCI_FIXED_BAR_2_SIZE 0x0c | ||
42 | #define PCI_FIXED_BAR_3_SIZE 0x10 | ||
43 | #define PCI_FIXED_BAR_4_SIZE 0x14 | ||
44 | #define PCI_FIXED_BAR_5_SIZE 0x1c | ||
45 | |||
46 | static int pci_soc_mode; | ||
47 | |||
48 | /** | ||
49 | * fixed_bar_cap - return the offset of the fixed BAR cap if found | ||
50 | * @bus: PCI bus | ||
51 | * @devfn: device in question | ||
52 | * | ||
53 | * Look for the fixed BAR cap on @bus and @devfn, returning its offset | ||
54 | * if found or 0 otherwise. | ||
55 | */ | ||
56 | static int fixed_bar_cap(struct pci_bus *bus, unsigned int devfn) | ||
57 | { | ||
58 | int pos; | ||
59 | u32 pcie_cap = 0, cap_data; | ||
60 | |||
61 | pos = PCIE_CAP_OFFSET; | ||
62 | |||
63 | if (!raw_pci_ext_ops) | ||
64 | return 0; | ||
65 | |||
66 | while (pos) { | ||
67 | if (raw_pci_ext_ops->read(pci_domain_nr(bus), bus->number, | ||
68 | devfn, pos, 4, &pcie_cap)) | ||
69 | return 0; | ||
70 | |||
71 | if (PCI_EXT_CAP_ID(pcie_cap) == 0x0000 || | ||
72 | PCI_EXT_CAP_ID(pcie_cap) == 0xffff) | ||
73 | break; | ||
74 | |||
75 | if (PCI_EXT_CAP_ID(pcie_cap) == PCI_EXT_CAP_ID_VNDR) { | ||
76 | raw_pci_ext_ops->read(pci_domain_nr(bus), bus->number, | ||
77 | devfn, pos + 4, 4, &cap_data); | ||
78 | if ((cap_data & 0xffff) == PCIE_VNDR_CAP_ID_FIXED_BAR) | ||
79 | return pos; | ||
80 | } | ||
81 | |||
82 | pos = PCI_EXT_CAP_NEXT(pcie_cap); | ||
83 | } | ||
84 | |||
85 | return 0; | ||
86 | } | ||
87 | |||
88 | static int pci_device_update_fixed(struct pci_bus *bus, unsigned int devfn, | ||
89 | int reg, int len, u32 val, int offset) | ||
90 | { | ||
91 | u32 size; | ||
92 | unsigned int domain, busnum; | ||
93 | int bar = (reg - PCI_BASE_ADDRESS_0) >> 2; | ||
94 | |||
95 | domain = pci_domain_nr(bus); | ||
96 | busnum = bus->number; | ||
97 | |||
98 | if (val == ~0 && len == 4) { | ||
99 | unsigned long decode; | ||
100 | |||
101 | raw_pci_ext_ops->read(domain, busnum, devfn, | ||
102 | offset + 8 + (bar * 4), 4, &size); | ||
103 | |||
104 | /* Turn the size into a decode pattern for the sizing code */ | ||
105 | if (size) { | ||
106 | decode = size - 1; | ||
107 | decode |= decode >> 1; | ||
108 | decode |= decode >> 2; | ||
109 | decode |= decode >> 4; | ||
110 | decode |= decode >> 8; | ||
111 | decode |= decode >> 16; | ||
112 | decode++; | ||
113 | decode = ~(decode - 1); | ||
114 | } else { | ||
115 | decode = 0; | ||
116 | } | ||
117 | |||
118 | /* | ||
119 | * If val is all ones, the core code is trying to size the reg, | ||
120 | * so update the mmconfig space with the real size. | ||
121 | * | ||
122 | * Note: this assumes the fixed size we got is a power of two. | ||
123 | */ | ||
124 | return raw_pci_ext_ops->write(domain, busnum, devfn, reg, 4, | ||
125 | decode); | ||
126 | } | ||
127 | |||
128 | /* This is some other kind of BAR write, so just do it. */ | ||
129 | return raw_pci_ext_ops->write(domain, busnum, devfn, reg, len, val); | ||
130 | } | ||
131 | |||
132 | /** | ||
133 | * type1_access_ok - check whether to use type 1 | ||
134 | * @bus: bus number | ||
135 | * @devfn: device & function in question | ||
136 | * | ||
137 | * If the bus is on a Lincroft chip and it exists, or is not on a Lincroft at | ||
138 | * all, the we can go ahead with any reads & writes. If it's on a Lincroft, | ||
139 | * but doesn't exist, avoid the access altogether to keep the chip from | ||
140 | * hanging. | ||
141 | */ | ||
142 | static bool type1_access_ok(unsigned int bus, unsigned int devfn, int reg) | ||
143 | { | ||
144 | /* | ||
145 | * This is a workaround for A0 LNC bug where PCI status register does | ||
146 | * not have new CAP bit set. can not be written by SW either. | ||
147 | * | ||
148 | * PCI header type in real LNC indicates a single function device, this | ||
149 | * will prevent probing other devices under the same function in PCI | ||
150 | * shim. Therefore, use the header type in shim instead. | ||
151 | */ | ||
152 | if (reg >= 0x100 || reg == PCI_STATUS || reg == PCI_HEADER_TYPE) | ||
153 | return 0; | ||
154 | if (bus == 0 && (devfn == PCI_DEVFN(2, 0) | ||
155 | || devfn == PCI_DEVFN(0, 0) | ||
156 | || devfn == PCI_DEVFN(3, 0))) | ||
157 | return 1; | ||
158 | return 0; /* Langwell on others */ | ||
159 | } | ||
160 | |||
161 | static int pci_read(struct pci_bus *bus, unsigned int devfn, int where, | ||
162 | int size, u32 *value) | ||
163 | { | ||
164 | if (type1_access_ok(bus->number, devfn, where)) | ||
165 | return pci_direct_conf1.read(pci_domain_nr(bus), bus->number, | ||
166 | devfn, where, size, value); | ||
167 | return raw_pci_ext_ops->read(pci_domain_nr(bus), bus->number, | ||
168 | devfn, where, size, value); | ||
169 | } | ||
170 | |||
171 | static int pci_write(struct pci_bus *bus, unsigned int devfn, int where, | ||
172 | int size, u32 value) | ||
173 | { | ||
174 | int offset; | ||
175 | |||
176 | /* | ||
177 | * On MRST, there is no PCI ROM BAR, this will cause a subsequent read | ||
178 | * to ROM BAR return 0 then being ignored. | ||
179 | */ | ||
180 | if (where == PCI_ROM_ADDRESS) | ||
181 | return 0; | ||
182 | |||
183 | /* | ||
184 | * Devices with fixed BARs need special handling: | ||
185 | * - BAR sizing code will save, write ~0, read size, restore | ||
186 | * - so writes to fixed BARs need special handling | ||
187 | * - other writes to fixed BAR devices should go through mmconfig | ||
188 | */ | ||
189 | offset = fixed_bar_cap(bus, devfn); | ||
190 | if (offset && | ||
191 | (where >= PCI_BASE_ADDRESS_0 && where <= PCI_BASE_ADDRESS_5)) { | ||
192 | return pci_device_update_fixed(bus, devfn, where, size, value, | ||
193 | offset); | ||
194 | } | ||
195 | |||
196 | /* | ||
197 | * On Moorestown update both real & mmconfig space | ||
198 | * Note: early Lincroft silicon can't handle type 1 accesses to | ||
199 | * non-existent devices, so just eat the write in that case. | ||
200 | */ | ||
201 | if (type1_access_ok(bus->number, devfn, where)) | ||
202 | return pci_direct_conf1.write(pci_domain_nr(bus), bus->number, | ||
203 | devfn, where, size, value); | ||
204 | return raw_pci_ext_ops->write(pci_domain_nr(bus), bus->number, devfn, | ||
205 | where, size, value); | ||
206 | } | ||
207 | |||
208 | static int mrst_pci_irq_enable(struct pci_dev *dev) | ||
209 | { | ||
210 | u8 pin; | ||
211 | struct io_apic_irq_attr irq_attr; | ||
212 | |||
213 | pci_read_config_byte(dev, PCI_INTERRUPT_PIN, &pin); | ||
214 | |||
215 | /* | ||
216 | * MRST only have IOAPIC, the PCI irq lines are 1:1 mapped to | ||
217 | * IOAPIC RTE entries, so we just enable RTE for the device. | ||
218 | */ | ||
219 | irq_attr.ioapic = mp_find_ioapic(dev->irq); | ||
220 | irq_attr.ioapic_pin = dev->irq; | ||
221 | irq_attr.trigger = 1; /* level */ | ||
222 | irq_attr.polarity = 1; /* active low */ | ||
223 | io_apic_set_pci_routing(&dev->dev, dev->irq, &irq_attr); | ||
224 | |||
225 | return 0; | ||
226 | } | ||
227 | |||
228 | struct pci_ops pci_mrst_ops = { | ||
229 | .read = pci_read, | ||
230 | .write = pci_write, | ||
231 | }; | ||
232 | |||
233 | /** | ||
234 | * pci_mrst_init - installs pci_mrst_ops | ||
235 | * | ||
236 | * Moorestown has an interesting PCI implementation (see above). | ||
237 | * Called when the early platform detection installs it. | ||
238 | */ | ||
239 | int __init pci_mrst_init(void) | ||
240 | { | ||
241 | pr_info("Intel MID platform detected, using MID PCI ops\n"); | ||
242 | pci_mmcfg_late_init(); | ||
243 | pcibios_enable_irq = mrst_pci_irq_enable; | ||
244 | pci_root_ops = pci_mrst_ops; | ||
245 | pci_soc_mode = 1; | ||
246 | /* Continue with standard init */ | ||
247 | return 1; | ||
248 | } | ||
249 | |||
250 | /* | ||
251 | * Langwell devices are not true PCI devices; they are not subject to 10 ms | ||
252 | * d3 to d0 delay required by PCI spec. | ||
253 | */ | ||
254 | static void pci_d3delay_fixup(struct pci_dev *dev) | ||
255 | { | ||
256 | /* | ||
257 | * PCI fixups are effectively decided compile time. If we have a dual | ||
258 | * SoC/non-SoC kernel we don't want to mangle d3 on non-SoC devices. | ||
259 | */ | ||
260 | if (!pci_soc_mode) | ||
261 | return; | ||
262 | /* | ||
263 | * True PCI devices in Lincroft should allow type 1 access, the rest | ||
264 | * are Langwell fake PCI devices. | ||
265 | */ | ||
266 | if (type1_access_ok(dev->bus->number, dev->devfn, PCI_DEVICE_ID)) | ||
267 | return; | ||
268 | dev->d3_delay = 0; | ||
269 | } | ||
270 | DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_INTEL, PCI_ANY_ID, pci_d3delay_fixup); | ||
271 | |||
272 | static void mrst_power_off_unused_dev(struct pci_dev *dev) | ||
273 | { | ||
274 | pci_set_power_state(dev, PCI_D3hot); | ||
275 | } | ||
276 | DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_INTEL, 0x0801, mrst_power_off_unused_dev); | ||
277 | DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_INTEL, 0x0809, mrst_power_off_unused_dev); | ||
278 | DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_INTEL, 0x080C, mrst_power_off_unused_dev); | ||
279 | DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_INTEL, 0x0812, mrst_power_off_unused_dev); | ||
280 | DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_INTEL, 0x0815, mrst_power_off_unused_dev); | ||
281 | |||
282 | /* | ||
283 | * Langwell devices reside at fixed offsets, don't try to move them. | ||
284 | */ | ||
285 | static void pci_fixed_bar_fixup(struct pci_dev *dev) | ||
286 | { | ||
287 | unsigned long offset; | ||
288 | u32 size; | ||
289 | int i; | ||
290 | |||
291 | if (!pci_soc_mode) | ||
292 | return; | ||
293 | |||
294 | /* Must have extended configuration space */ | ||
295 | if (dev->cfg_size < PCIE_CAP_OFFSET + 4) | ||
296 | return; | ||
297 | |||
298 | /* Fixup the BAR sizes for fixed BAR devices and make them unmoveable */ | ||
299 | offset = fixed_bar_cap(dev->bus, dev->devfn); | ||
300 | if (!offset || PCI_DEVFN(2, 0) == dev->devfn || | ||
301 | PCI_DEVFN(2, 2) == dev->devfn) | ||
302 | return; | ||
303 | |||
304 | for (i = 0; i < PCI_ROM_RESOURCE; i++) { | ||
305 | pci_read_config_dword(dev, offset + 8 + (i * 4), &size); | ||
306 | dev->resource[i].end = dev->resource[i].start + size - 1; | ||
307 | dev->resource[i].flags |= IORESOURCE_PCI_FIXED; | ||
308 | } | ||
309 | } | ||
310 | DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_INTEL, PCI_ANY_ID, pci_fixed_bar_fixup); | ||
311 |
arch/x86/pci/mrst.c
1 | /* | File was deleted | |
2 | * Moorestown PCI support | ||
3 | * Copyright (c) 2008 Intel Corporation | ||
4 | * Jesse Barnes <jesse.barnes@intel.com> | ||
5 | * | ||
6 | * Moorestown has an interesting PCI implementation: | ||
7 | * - configuration space is memory mapped (as defined by MCFG) | ||
8 | * - Lincroft devices also have a real, type 1 configuration space | ||
9 | * - Early Lincroft silicon has a type 1 access bug that will cause | ||
10 | * a hang if non-existent devices are accessed | ||
11 | * - some devices have the "fixed BAR" capability, which means | ||
12 | * they can't be relocated or modified; check for that during | ||
13 | * BAR sizing | ||
14 | * | ||
15 | * So, we use the MCFG space for all reads and writes, but also send | ||
16 | * Lincroft writes to type 1 space. But only read/write if the device | ||
17 | * actually exists, otherwise return all 1s for reads and bit bucket | ||
18 | * the writes. | ||
19 | */ | ||
20 | |||
21 | #include <linux/sched.h> | ||
22 | #include <linux/pci.h> | ||
23 | #include <linux/ioport.h> | ||
24 | #include <linux/init.h> | ||
25 | #include <linux/dmi.h> | ||
26 | #include <linux/acpi.h> | ||
27 | #include <linux/io.h> | ||
28 | #include <linux/smp.h> | ||
29 | |||
30 | #include <asm/segment.h> | ||
31 | #include <asm/pci_x86.h> | ||
32 | #include <asm/hw_irq.h> | ||
33 | #include <asm/io_apic.h> | ||
34 | |||
35 | #define PCIE_CAP_OFFSET 0x100 | ||
36 | |||
37 | /* Fixed BAR fields */ | ||
38 | #define PCIE_VNDR_CAP_ID_FIXED_BAR 0x00 /* Fixed BAR (TBD) */ | ||
39 | #define PCI_FIXED_BAR_0_SIZE 0x04 | ||
40 | #define PCI_FIXED_BAR_1_SIZE 0x08 | ||
41 | #define PCI_FIXED_BAR_2_SIZE 0x0c | ||
42 | #define PCI_FIXED_BAR_3_SIZE 0x10 | ||
43 | #define PCI_FIXED_BAR_4_SIZE 0x14 | ||
44 | #define PCI_FIXED_BAR_5_SIZE 0x1c | ||
45 | |||
46 | static int pci_soc_mode; | ||
47 | |||
48 | /** | ||
49 | * fixed_bar_cap - return the offset of the fixed BAR cap if found | ||
50 | * @bus: PCI bus | ||
51 | * @devfn: device in question | ||
52 | * | ||
53 | * Look for the fixed BAR cap on @bus and @devfn, returning its offset | ||
54 | * if found or 0 otherwise. | ||
55 | */ | ||
56 | static int fixed_bar_cap(struct pci_bus *bus, unsigned int devfn) | ||
57 | { | ||
58 | int pos; | ||
59 | u32 pcie_cap = 0, cap_data; | ||
60 | |||
61 | pos = PCIE_CAP_OFFSET; | ||
62 | |||
63 | if (!raw_pci_ext_ops) | ||
64 | return 0; | ||
65 | |||
66 | while (pos) { | ||
67 | if (raw_pci_ext_ops->read(pci_domain_nr(bus), bus->number, | ||
68 | devfn, pos, 4, &pcie_cap)) | ||
69 | return 0; | ||
70 | |||
71 | if (PCI_EXT_CAP_ID(pcie_cap) == 0x0000 || | ||
72 | PCI_EXT_CAP_ID(pcie_cap) == 0xffff) | ||
73 | break; | ||
74 | |||
75 | if (PCI_EXT_CAP_ID(pcie_cap) == PCI_EXT_CAP_ID_VNDR) { | ||
76 | raw_pci_ext_ops->read(pci_domain_nr(bus), bus->number, | ||
77 | devfn, pos + 4, 4, &cap_data); | ||
78 | if ((cap_data & 0xffff) == PCIE_VNDR_CAP_ID_FIXED_BAR) | ||
79 | return pos; | ||
80 | } | ||
81 | |||
82 | pos = PCI_EXT_CAP_NEXT(pcie_cap); | ||
83 | } | ||
84 | |||
85 | return 0; | ||
86 | } | ||
87 | |||
88 | static int pci_device_update_fixed(struct pci_bus *bus, unsigned int devfn, | ||
89 | int reg, int len, u32 val, int offset) | ||
90 | { | ||
91 | u32 size; | ||
92 | unsigned int domain, busnum; | ||
93 | int bar = (reg - PCI_BASE_ADDRESS_0) >> 2; | ||
94 | |||
95 | domain = pci_domain_nr(bus); | ||
96 | busnum = bus->number; | ||
97 | |||
98 | if (val == ~0 && len == 4) { | ||
99 | unsigned long decode; | ||
100 | |||
101 | raw_pci_ext_ops->read(domain, busnum, devfn, | ||
102 | offset + 8 + (bar * 4), 4, &size); | ||
103 | |||
104 | /* Turn the size into a decode pattern for the sizing code */ | ||
105 | if (size) { | ||
106 | decode = size - 1; | ||
107 | decode |= decode >> 1; | ||
108 | decode |= decode >> 2; | ||
109 | decode |= decode >> 4; | ||
110 | decode |= decode >> 8; | ||
111 | decode |= decode >> 16; | ||
112 | decode++; | ||
113 | decode = ~(decode - 1); | ||
114 | } else { | ||
115 | decode = 0; | ||
116 | } | ||
117 | |||
118 | /* | ||
119 | * If val is all ones, the core code is trying to size the reg, | ||
120 | * so update the mmconfig space with the real size. | ||
121 | * | ||
122 | * Note: this assumes the fixed size we got is a power of two. | ||
123 | */ | ||
124 | return raw_pci_ext_ops->write(domain, busnum, devfn, reg, 4, | ||
125 | decode); | ||
126 | } | ||
127 | |||
128 | /* This is some other kind of BAR write, so just do it. */ | ||
129 | return raw_pci_ext_ops->write(domain, busnum, devfn, reg, len, val); | ||
130 | } | ||
131 | |||
132 | /** | ||
133 | * type1_access_ok - check whether to use type 1 | ||
134 | * @bus: bus number | ||
135 | * @devfn: device & function in question | ||
136 | * | ||
137 | * If the bus is on a Lincroft chip and it exists, or is not on a Lincroft at | ||
138 | * all, the we can go ahead with any reads & writes. If it's on a Lincroft, | ||
139 | * but doesn't exist, avoid the access altogether to keep the chip from | ||
140 | * hanging. | ||
141 | */ | ||
142 | static bool type1_access_ok(unsigned int bus, unsigned int devfn, int reg) | ||
143 | { | ||
144 | /* | ||
145 | * This is a workaround for A0 LNC bug where PCI status register does | ||
146 | * not have new CAP bit set. can not be written by SW either. | ||
147 | * | ||
148 | * PCI header type in real LNC indicates a single function device, this | ||
149 | * will prevent probing other devices under the same function in PCI | ||
150 | * shim. Therefore, use the header type in shim instead. | ||
151 | */ | ||
152 | if (reg >= 0x100 || reg == PCI_STATUS || reg == PCI_HEADER_TYPE) | ||
153 | return 0; | ||
154 | if (bus == 0 && (devfn == PCI_DEVFN(2, 0) | ||
155 | || devfn == PCI_DEVFN(0, 0) | ||
156 | || devfn == PCI_DEVFN(3, 0))) | ||
157 | return 1; | ||
158 | return 0; /* Langwell on others */ | ||
159 | } | ||
160 | |||
161 | static int pci_read(struct pci_bus *bus, unsigned int devfn, int where, | ||
162 | int size, u32 *value) | ||
163 | { | ||
164 | if (type1_access_ok(bus->number, devfn, where)) | ||
165 | return pci_direct_conf1.read(pci_domain_nr(bus), bus->number, | ||
166 | devfn, where, size, value); | ||
167 | return raw_pci_ext_ops->read(pci_domain_nr(bus), bus->number, | ||
168 | devfn, where, size, value); | ||
169 | } | ||
170 | |||
171 | static int pci_write(struct pci_bus *bus, unsigned int devfn, int where, | ||
172 | int size, u32 value) | ||
173 | { | ||
174 | int offset; | ||
175 | |||
176 | /* | ||
177 | * On MRST, there is no PCI ROM BAR, this will cause a subsequent read | ||
178 | * to ROM BAR return 0 then being ignored. | ||
179 | */ | ||
180 | if (where == PCI_ROM_ADDRESS) | ||
181 | return 0; | ||
182 | |||
183 | /* | ||
184 | * Devices with fixed BARs need special handling: | ||
185 | * - BAR sizing code will save, write ~0, read size, restore | ||
186 | * - so writes to fixed BARs need special handling | ||
187 | * - other writes to fixed BAR devices should go through mmconfig | ||
188 | */ | ||
189 | offset = fixed_bar_cap(bus, devfn); | ||
190 | if (offset && | ||
191 | (where >= PCI_BASE_ADDRESS_0 && where <= PCI_BASE_ADDRESS_5)) { | ||
192 | return pci_device_update_fixed(bus, devfn, where, size, value, | ||
193 | offset); | ||
194 | } | ||
195 | |||
196 | /* | ||
197 | * On Moorestown update both real & mmconfig space | ||
198 | * Note: early Lincroft silicon can't handle type 1 accesses to | ||
199 | * non-existent devices, so just eat the write in that case. | ||
200 | */ | ||
201 | if (type1_access_ok(bus->number, devfn, where)) | ||
202 | return pci_direct_conf1.write(pci_domain_nr(bus), bus->number, | ||
203 | devfn, where, size, value); | ||
204 | return raw_pci_ext_ops->write(pci_domain_nr(bus), bus->number, devfn, | ||
205 | where, size, value); | ||
206 | } | ||
207 | |||
208 | static int mrst_pci_irq_enable(struct pci_dev *dev) | ||
209 | { | ||
210 | u8 pin; | ||
211 | struct io_apic_irq_attr irq_attr; | ||
212 | |||
213 | pci_read_config_byte(dev, PCI_INTERRUPT_PIN, &pin); | ||
214 | |||
215 | /* | ||
216 | * MRST only have IOAPIC, the PCI irq lines are 1:1 mapped to | ||
217 | * IOAPIC RTE entries, so we just enable RTE for the device. | ||
218 | */ | ||
219 | irq_attr.ioapic = mp_find_ioapic(dev->irq); | ||
220 | irq_attr.ioapic_pin = dev->irq; | ||
221 | irq_attr.trigger = 1; /* level */ | ||
222 | irq_attr.polarity = 1; /* active low */ | ||
223 | io_apic_set_pci_routing(&dev->dev, dev->irq, &irq_attr); | ||
224 | |||
225 | return 0; | ||
226 | } | ||
227 | |||
228 | struct pci_ops pci_mrst_ops = { | ||
229 | .read = pci_read, | ||
230 | .write = pci_write, | ||
231 | }; | ||
232 | |||
233 | /** | ||
234 | * pci_mrst_init - installs pci_mrst_ops | ||
235 | * | ||
236 | * Moorestown has an interesting PCI implementation (see above). | ||
237 | * Called when the early platform detection installs it. | ||
238 | */ | ||
239 | int __init pci_mrst_init(void) | ||
240 | { | ||
241 | pr_info("Intel MID platform detected, using MID PCI ops\n"); | ||
242 | pci_mmcfg_late_init(); | ||
243 | pcibios_enable_irq = mrst_pci_irq_enable; | ||
244 | pci_root_ops = pci_mrst_ops; | ||
245 | pci_soc_mode = 1; | ||
246 | /* Continue with standard init */ | ||
247 | return 1; | ||
248 | } | ||
249 | |||
250 | /* | ||
251 | * Langwell devices are not true PCI devices; they are not subject to 10 ms | ||
252 | * d3 to d0 delay required by PCI spec. | ||
253 | */ | ||
254 | static void pci_d3delay_fixup(struct pci_dev *dev) | ||
255 | { | ||
256 | /* | ||
257 | * PCI fixups are effectively decided compile time. If we have a dual | ||
258 | * SoC/non-SoC kernel we don't want to mangle d3 on non-SoC devices. | ||
259 | */ | ||
260 | if (!pci_soc_mode) | ||
261 | return; | ||
262 | /* | ||
263 | * True PCI devices in Lincroft should allow type 1 access, the rest | ||
264 | * are Langwell fake PCI devices. | ||
265 | */ | ||
266 | if (type1_access_ok(dev->bus->number, dev->devfn, PCI_DEVICE_ID)) | ||
267 | return; | ||
268 | dev->d3_delay = 0; | ||
269 | } | ||
270 | DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_INTEL, PCI_ANY_ID, pci_d3delay_fixup); | ||
271 | |||
272 | static void mrst_power_off_unused_dev(struct pci_dev *dev) | ||
273 | { | ||
274 | pci_set_power_state(dev, PCI_D3hot); | ||
275 | } | ||
276 | DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_INTEL, 0x0801, mrst_power_off_unused_dev); | ||
277 | DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_INTEL, 0x0809, mrst_power_off_unused_dev); | ||
278 | DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_INTEL, 0x080C, mrst_power_off_unused_dev); | ||
279 | DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_INTEL, 0x0812, mrst_power_off_unused_dev); | ||
280 | DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_INTEL, 0x0815, mrst_power_off_unused_dev); | ||
281 | |||
282 | /* | ||
283 | * Langwell devices reside at fixed offsets, don't try to move them. | ||
284 | */ | ||
285 | static void pci_fixed_bar_fixup(struct pci_dev *dev) | ||
286 | { | ||
287 | unsigned long offset; | ||
288 | u32 size; | ||
289 | int i; | ||
290 | |||
291 | if (!pci_soc_mode) | ||
292 | return; | ||
293 | |||
294 | /* Must have extended configuration space */ | ||
295 | if (dev->cfg_size < PCIE_CAP_OFFSET + 4) | ||
296 | return; | ||
297 | |||
298 | /* Fixup the BAR sizes for fixed BAR devices and make them unmoveable */ | ||
299 | offset = fixed_bar_cap(dev->bus, dev->devfn); | ||
300 | if (!offset || PCI_DEVFN(2, 0) == dev->devfn || | ||
301 | PCI_DEVFN(2, 2) == dev->devfn) | ||
302 | return; | ||
303 | |||
304 | for (i = 0; i < PCI_ROM_RESOURCE; i++) { | ||
305 | pci_read_config_dword(dev, offset + 8 + (i * 4), &size); | ||
306 | dev->resource[i].end = dev->resource[i].start + size - 1; | ||
307 | dev->resource[i].flags |= IORESOURCE_PCI_FIXED; | ||
308 | } | ||
309 | } | ||
310 | DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_INTEL, PCI_ANY_ID, pci_fixed_bar_fixup); | ||
311 | 1 | /* |
arch/x86/platform/Makefile
1 | # Platform specific code goes here | 1 | # Platform specific code goes here |
2 | obj-y += ce4100/ | 2 | obj-y += ce4100/ |
3 | obj-y += efi/ | 3 | obj-y += efi/ |
4 | obj-y += geode/ | 4 | obj-y += geode/ |
5 | obj-y += goldfish/ | 5 | obj-y += goldfish/ |
6 | obj-y += iris/ | 6 | obj-y += iris/ |
7 | obj-y += mrst/ | 7 | obj-y += intel-mid/ |
8 | obj-y += olpc/ | 8 | obj-y += olpc/ |
9 | obj-y += scx200/ | 9 | obj-y += scx200/ |
10 | obj-y += sfi/ | 10 | obj-y += sfi/ |
11 | obj-y += ts5500/ | 11 | obj-y += ts5500/ |
12 | obj-y += visws/ | 12 | obj-y += visws/ |
13 | obj-y += uv/ | 13 | obj-y += uv/ |
14 | 14 |
arch/x86/platform/intel-mid/Makefile
File was created | 1 | obj-$(CONFIG_X86_INTEL_MID) += intel-mid.o | |
2 | obj-$(CONFIG_X86_INTEL_MID) += intel_mid_vrtc.o | ||
3 | obj-$(CONFIG_EARLY_PRINTK_INTEL_MID) += early_printk_intel_mid.o | ||
4 |
arch/x86/platform/intel-mid/early_printk_intel_mid.c
File was created | 1 | /* | |
2 | * early_printk_intel_mid.c - early consoles for Intel MID platforms | ||
3 | * | ||
4 | * Copyright (c) 2008-2010, Intel Corporation | ||
5 | * | ||
6 | * This program is free software; you can redistribute it and/or | ||
7 | * modify it under the terms of the GNU General Public License | ||
8 | * as published by the Free Software Foundation; version 2 | ||
9 | * of the License. | ||
10 | */ | ||
11 | |||
12 | /* | ||
13 | * This file implements two early consoles named mrst and hsu. | ||
14 | * mrst is based on Maxim3110 spi-uart device, it exists in both | ||
15 | * Moorestown and Medfield platforms, while hsu is based on a High | ||
16 | * Speed UART device which only exists in the Medfield platform | ||
17 | */ | ||
18 | |||
19 | #include <linux/serial_reg.h> | ||
20 | #include <linux/serial_mfd.h> | ||
21 | #include <linux/kmsg_dump.h> | ||
22 | #include <linux/console.h> | ||
23 | #include <linux/kernel.h> | ||
24 | #include <linux/delay.h> | ||
25 | #include <linux/init.h> | ||
26 | #include <linux/io.h> | ||
27 | |||
28 | #include <asm/fixmap.h> | ||
29 | #include <asm/pgtable.h> | ||
30 | #include <asm/intel-mid.h> | ||
31 | |||
32 | #define MRST_SPI_TIMEOUT 0x200000 | ||
33 | #define MRST_REGBASE_SPI0 0xff128000 | ||
34 | #define MRST_REGBASE_SPI1 0xff128400 | ||
35 | #define MRST_CLK_SPI0_REG 0xff11d86c | ||
36 | |||
37 | /* Bit fields in CTRLR0 */ | ||
38 | #define SPI_DFS_OFFSET 0 | ||
39 | |||
40 | #define SPI_FRF_OFFSET 4 | ||
41 | #define SPI_FRF_SPI 0x0 | ||
42 | #define SPI_FRF_SSP 0x1 | ||
43 | #define SPI_FRF_MICROWIRE 0x2 | ||
44 | #define SPI_FRF_RESV 0x3 | ||
45 | |||
46 | #define SPI_MODE_OFFSET 6 | ||
47 | #define SPI_SCPH_OFFSET 6 | ||
48 | #define SPI_SCOL_OFFSET 7 | ||
49 | #define SPI_TMOD_OFFSET 8 | ||
50 | #define SPI_TMOD_TR 0x0 /* xmit & recv */ | ||
51 | #define SPI_TMOD_TO 0x1 /* xmit only */ | ||
52 | #define SPI_TMOD_RO 0x2 /* recv only */ | ||
53 | #define SPI_TMOD_EPROMREAD 0x3 /* eeprom read mode */ | ||
54 | |||
55 | #define SPI_SLVOE_OFFSET 10 | ||
56 | #define SPI_SRL_OFFSET 11 | ||
57 | #define SPI_CFS_OFFSET 12 | ||
58 | |||
59 | /* Bit fields in SR, 7 bits */ | ||
60 | #define SR_MASK 0x7f /* cover 7 bits */ | ||
61 | #define SR_BUSY (1 << 0) | ||
62 | #define SR_TF_NOT_FULL (1 << 1) | ||
63 | #define SR_TF_EMPT (1 << 2) | ||
64 | #define SR_RF_NOT_EMPT (1 << 3) | ||
65 | #define SR_RF_FULL (1 << 4) | ||
66 | #define SR_TX_ERR (1 << 5) | ||
67 | #define SR_DCOL (1 << 6) | ||
68 | |||
69 | struct dw_spi_reg { | ||
70 | u32 ctrl0; | ||
71 | u32 ctrl1; | ||
72 | u32 ssienr; | ||
73 | u32 mwcr; | ||
74 | u32 ser; | ||
75 | u32 baudr; | ||
76 | u32 txfltr; | ||
77 | u32 rxfltr; | ||
78 | u32 txflr; | ||
79 | u32 rxflr; | ||
80 | u32 sr; | ||
81 | u32 imr; | ||
82 | u32 isr; | ||
83 | u32 risr; | ||
84 | u32 txoicr; | ||
85 | u32 rxoicr; | ||
86 | u32 rxuicr; | ||
87 | u32 msticr; | ||
88 | u32 icr; | ||
89 | u32 dmacr; | ||
90 | u32 dmatdlr; | ||
91 | u32 dmardlr; | ||
92 | u32 idr; | ||
93 | u32 version; | ||
94 | |||
95 | /* Currently operates as 32 bits, though only the low 16 bits matter */ | ||
96 | u32 dr; | ||
97 | } __packed; | ||
98 | |||
99 | #define dw_readl(dw, name) __raw_readl(&(dw)->name) | ||
100 | #define dw_writel(dw, name, val) __raw_writel((val), &(dw)->name) | ||
101 | |||
102 | /* Default use SPI0 register for mrst, we will detect Penwell and use SPI1 */ | ||
103 | static unsigned long mrst_spi_paddr = MRST_REGBASE_SPI0; | ||
104 | |||
105 | static u32 *pclk_spi0; | ||
106 | /* Always contains an accessible address, start with 0 */ | ||
107 | static struct dw_spi_reg *pspi; | ||
108 | |||
109 | static struct kmsg_dumper dw_dumper; | ||
110 | static int dumper_registered; | ||
111 | |||
112 | static void dw_kmsg_dump(struct kmsg_dumper *dumper, | ||
113 | enum kmsg_dump_reason reason) | ||
114 | { | ||
115 | static char line[1024]; | ||
116 | size_t len; | ||
117 | |||
118 | /* When run to this, we'd better re-init the HW */ | ||
119 | mrst_early_console_init(); | ||
120 | |||
121 | while (kmsg_dump_get_line(dumper, true, line, sizeof(line), &len)) | ||
122 | early_mrst_console.write(&early_mrst_console, line, len); | ||
123 | } | ||
124 | |||
125 | /* Set the ratio rate to 115200, 8n1, IRQ disabled */ | ||
126 | static void max3110_write_config(void) | ||
127 | { | ||
128 | u16 config; | ||
129 | |||
130 | config = 0xc001; | ||
131 | dw_writel(pspi, dr, config); | ||
132 | } | ||
133 | |||
134 | /* Translate char to a eligible word and send to max3110 */ | ||
135 | static void max3110_write_data(char c) | ||
136 | { | ||
137 | u16 data; | ||
138 | |||
139 | data = 0x8000 | c; | ||
140 | dw_writel(pspi, dr, data); | ||
141 | } | ||
142 | |||
143 | void mrst_early_console_init(void) | ||
144 | { | ||
145 | u32 ctrlr0 = 0; | ||
146 | u32 spi0_cdiv; | ||
147 | u32 freq; /* Freqency info only need be searched once */ | ||
148 | |||
149 | /* Base clk is 100 MHz, the actual clk = 100M / (clk_divider + 1) */ | ||
150 | pclk_spi0 = (void *)set_fixmap_offset_nocache(FIX_EARLYCON_MEM_BASE, | ||
151 | MRST_CLK_SPI0_REG); | ||
152 | spi0_cdiv = ((*pclk_spi0) & 0xe00) >> 9; | ||
153 | freq = 100000000 / (spi0_cdiv + 1); | ||
154 | |||
155 | if (mrst_identify_cpu() == MRST_CPU_CHIP_PENWELL) | ||
156 | mrst_spi_paddr = MRST_REGBASE_SPI1; | ||
157 | |||
158 | pspi = (void *)set_fixmap_offset_nocache(FIX_EARLYCON_MEM_BASE, | ||
159 | mrst_spi_paddr); | ||
160 | |||
161 | /* Disable SPI controller */ | ||
162 | dw_writel(pspi, ssienr, 0); | ||
163 | |||
164 | /* Set control param, 8 bits, transmit only mode */ | ||
165 | ctrlr0 = dw_readl(pspi, ctrl0); | ||
166 | |||
167 | ctrlr0 &= 0xfcc0; | ||
168 | ctrlr0 |= 0xf | (SPI_FRF_SPI << SPI_FRF_OFFSET) | ||
169 | | (SPI_TMOD_TO << SPI_TMOD_OFFSET); | ||
170 | dw_writel(pspi, ctrl0, ctrlr0); | ||
171 | |||
172 | /* | ||
173 | * Change the spi0 clk to comply with 115200 bps, use 100000 to | ||
174 | * calculate the clk dividor to make the clock a little slower | ||
175 | * than real baud rate. | ||
176 | */ | ||
177 | dw_writel(pspi, baudr, freq/100000); | ||
178 | |||
179 | /* Disable all INT for early phase */ | ||
180 | dw_writel(pspi, imr, 0x0); | ||
181 | |||
182 | /* Set the cs to spi-uart */ | ||
183 | dw_writel(pspi, ser, 0x2); | ||
184 | |||
185 | /* Enable the HW, the last step for HW init */ | ||
186 | dw_writel(pspi, ssienr, 0x1); | ||
187 | |||
188 | /* Set the default configuration */ | ||
189 | max3110_write_config(); | ||
190 | |||
191 | /* Register the kmsg dumper */ | ||
192 | if (!dumper_registered) { | ||
193 | dw_dumper.dump = dw_kmsg_dump; | ||
194 | kmsg_dump_register(&dw_dumper); | ||
195 | dumper_registered = 1; | ||
196 | } | ||
197 | } | ||
198 | |||
199 | /* Slave select should be called in the read/write function */ | ||
200 | static void early_mrst_spi_putc(char c) | ||
201 | { | ||
202 | unsigned int timeout; | ||
203 | u32 sr; | ||
204 | |||
205 | timeout = MRST_SPI_TIMEOUT; | ||
206 | /* Early putc needs to make sure the TX FIFO is not full */ | ||
207 | while (--timeout) { | ||
208 | sr = dw_readl(pspi, sr); | ||
209 | if (!(sr & SR_TF_NOT_FULL)) | ||
210 | cpu_relax(); | ||
211 | else | ||
212 | break; | ||
213 | } | ||
214 | |||
215 | if (!timeout) | ||
216 | pr_warn("MRST earlycon: timed out\n"); | ||
217 | else | ||
218 | max3110_write_data(c); | ||
219 | } | ||
220 | |||
221 | /* Early SPI only uses polling mode */ | ||
222 | static void early_mrst_spi_write(struct console *con, const char *str, | ||
223 | unsigned n) | ||
224 | { | ||
225 | int i; | ||
226 | |||
227 | for (i = 0; i < n && *str; i++) { | ||
228 | if (*str == '\n') | ||
229 | early_mrst_spi_putc('\r'); | ||
230 | early_mrst_spi_putc(*str); | ||
231 | str++; | ||
232 | } | ||
233 | } | ||
234 | |||
235 | struct console early_mrst_console = { | ||
236 | .name = "earlymrst", | ||
237 | .write = early_mrst_spi_write, | ||
238 | .flags = CON_PRINTBUFFER, | ||
239 | .index = -1, | ||
240 | }; | ||
241 | |||
242 | /* | ||
243 | * Following is the early console based on Medfield HSU (High | ||
244 | * Speed UART) device. | ||
245 | */ | ||
246 | #define HSU_PORT_BASE 0xffa28080 | ||
247 | |||
248 | static void __iomem *phsu; | ||
249 | |||
250 | void hsu_early_console_init(const char *s) | ||
251 | { | ||
252 | unsigned long paddr, port = 0; | ||
253 | u8 lcr; | ||
254 | |||
255 | /* | ||
256 | * Select the early HSU console port if specified by user in the | ||
257 | * kernel command line. | ||
258 | */ | ||
259 | if (*s && !kstrtoul(s, 10, &port)) | ||
260 | port = clamp_val(port, 0, 2); | ||
261 | |||
262 | paddr = HSU_PORT_BASE + port * 0x80; | ||
263 | phsu = (void *)set_fixmap_offset_nocache(FIX_EARLYCON_MEM_BASE, paddr); | ||
264 | |||
265 | /* Disable FIFO */ | ||
266 | writeb(0x0, phsu + UART_FCR); | ||
267 | |||
268 | /* Set to default 115200 bps, 8n1 */ | ||
269 | lcr = readb(phsu + UART_LCR); | ||
270 | writeb((0x80 | lcr), phsu + UART_LCR); | ||
271 | writeb(0x18, phsu + UART_DLL); | ||
272 | writeb(lcr, phsu + UART_LCR); | ||
273 | writel(0x3600, phsu + UART_MUL*4); | ||
274 | |||
275 | writeb(0x8, phsu + UART_MCR); | ||
276 | writeb(0x7, phsu + UART_FCR); | ||
277 | writeb(0x3, phsu + UART_LCR); | ||
278 | |||
279 | /* Clear IRQ status */ | ||
280 | readb(phsu + UART_LSR); | ||
281 | readb(phsu + UART_RX); | ||
282 | readb(phsu + UART_IIR); | ||
283 | readb(phsu + UART_MSR); | ||
284 | |||
285 | /* Enable FIFO */ | ||
286 | writeb(0x7, phsu + UART_FCR); | ||
287 | } | ||
288 | |||
289 | #define BOTH_EMPTY (UART_LSR_TEMT | UART_LSR_THRE) | ||
290 | |||
291 | static void early_hsu_putc(char ch) | ||
292 | { | ||
293 | unsigned int timeout = 10000; /* 10ms */ | ||
294 | u8 status; | ||
295 | |||
296 | while (--timeout) { | ||
297 | status = readb(phsu + UART_LSR); | ||
298 | if (status & BOTH_EMPTY) | ||
299 | break; | ||
300 | udelay(1); | ||
301 | } | ||
302 | |||
303 | /* Only write the char when there was no timeout */ | ||
304 | if (timeout) | ||
305 | writeb(ch, phsu + UART_TX); | ||
306 | } | ||
307 | |||
308 | static void early_hsu_write(struct console *con, const char *str, unsigned n) | ||
309 | { | ||
310 | int i; | ||
311 | |||
312 | for (i = 0; i < n && *str; i++) { | ||
313 | if (*str == '\n') | ||
314 | early_hsu_putc('\r'); | ||
315 | early_hsu_putc(*str); | ||
316 | str++; | ||
317 | } | ||
318 | } | ||
319 | |||
320 | struct console early_hsu_console = { | ||
321 | .name = "earlyhsu", | ||
322 | .write = early_hsu_write, | ||
323 | .flags = CON_PRINTBUFFER, | ||
324 | .index = -1, | ||
325 | }; | ||
326 |
arch/x86/platform/intel-mid/intel-mid.c
File was created | 1 | /* | |
2 | * intel-mid.c: Intel MID platform setup code | ||
3 | * | ||
4 | * (C) Copyright 2008, 2012 Intel Corporation | ||
5 | * Author: Jacob Pan (jacob.jun.pan@intel.com) | ||
6 | * Author: Sathyanarayanan Kuppuswamy <sathyanarayanan.kuppuswamy@intel.com> | ||
7 | * | ||
8 | * This program is free software; you can redistribute it and/or | ||
9 | * modify it under the terms of the GNU General Public License | ||
10 | * as published by the Free Software Foundation; version 2 | ||
11 | * of the License. | ||
12 | */ | ||
13 | |||
14 | #define pr_fmt(fmt) "mrst: " fmt | ||
15 | |||
16 | #include <linux/init.h> | ||
17 | #include <linux/kernel.h> | ||
18 | #include <linux/interrupt.h> | ||
19 | #include <linux/scatterlist.h> | ||
20 | #include <linux/sfi.h> | ||
21 | #include <linux/intel_pmic_gpio.h> | ||
22 | #include <linux/spi/spi.h> | ||
23 | #include <linux/i2c.h> | ||
24 | #include <linux/platform_data/pca953x.h> | ||
25 | #include <linux/gpio_keys.h> | ||
26 | #include <linux/input.h> | ||
27 | #include <linux/platform_device.h> | ||
28 | #include <linux/irq.h> | ||
29 | #include <linux/module.h> | ||
30 | #include <linux/notifier.h> | ||
31 | #include <linux/mfd/intel_msic.h> | ||
32 | #include <linux/gpio.h> | ||
33 | #include <linux/i2c/tc35876x.h> | ||
34 | |||
35 | #include <asm/setup.h> | ||
36 | #include <asm/mpspec_def.h> | ||
37 | #include <asm/hw_irq.h> | ||
38 | #include <asm/apic.h> | ||
39 | #include <asm/io_apic.h> | ||
40 | #include <asm/intel-mid.h> | ||
41 | #include <asm/intel_mid_vrtc.h> | ||
42 | #include <asm/io.h> | ||
43 | #include <asm/i8259.h> | ||
44 | #include <asm/intel_scu_ipc.h> | ||
45 | #include <asm/apb_timer.h> | ||
46 | #include <asm/reboot.h> | ||
47 | |||
48 | /* | ||
49 | * the clockevent devices on Moorestown/Medfield can be APBT or LAPIC clock, | ||
50 | * cmdline option x86_mrst_timer can be used to override the configuration | ||
51 | * to prefer one or the other. | ||
52 | * at runtime, there are basically three timer configurations: | ||
53 | * 1. per cpu apbt clock only | ||
54 | * 2. per cpu always-on lapic clocks only, this is Penwell/Medfield only | ||
55 | * 3. per cpu lapic clock (C3STOP) and one apbt clock, with broadcast. | ||
56 | * | ||
57 | * by default (without cmdline option), platform code first detects cpu type | ||
58 | * to see if we are on lincroft or penwell, then set up both lapic or apbt | ||
59 | * clocks accordingly. | ||
60 | * i.e. by default, medfield uses configuration #2, moorestown uses #1. | ||
61 | * config #3 is supported but not recommended on medfield. | ||
62 | * | ||
63 | * rating and feature summary: | ||
64 | * lapic (with C3STOP) --------- 100 | ||
65 | * apbt (always-on) ------------ 110 | ||
66 | * lapic (always-on,ARAT) ------ 150 | ||
67 | */ | ||
68 | |||
69 | enum mrst_timer_options mrst_timer_options; | ||
70 | |||
71 | static u32 sfi_mtimer_usage[SFI_MTMR_MAX_NUM]; | ||
72 | static struct sfi_timer_table_entry sfi_mtimer_array[SFI_MTMR_MAX_NUM]; | ||
73 | enum mrst_cpu_type __mrst_cpu_chip; | ||
74 | EXPORT_SYMBOL_GPL(__mrst_cpu_chip); | ||
75 | |||
76 | int sfi_mtimer_num; | ||
77 | |||
78 | struct sfi_rtc_table_entry sfi_mrtc_array[SFI_MRTC_MAX]; | ||
79 | EXPORT_SYMBOL_GPL(sfi_mrtc_array); | ||
80 | int sfi_mrtc_num; | ||
81 | |||
82 | static void mrst_power_off(void) | ||
83 | { | ||
84 | } | ||
85 | |||
86 | static void mrst_reboot(void) | ||
87 | { | ||
88 | intel_scu_ipc_simple_command(IPCMSG_COLD_BOOT, 0); | ||
89 | } | ||
90 | |||
91 | /* parse all the mtimer info to a static mtimer array */ | ||
92 | static int __init sfi_parse_mtmr(struct sfi_table_header *table) | ||
93 | { | ||
94 | struct sfi_table_simple *sb; | ||
95 | struct sfi_timer_table_entry *pentry; | ||
96 | struct mpc_intsrc mp_irq; | ||
97 | int totallen; | ||
98 | |||
99 | sb = (struct sfi_table_simple *)table; | ||
100 | if (!sfi_mtimer_num) { | ||
101 | sfi_mtimer_num = SFI_GET_NUM_ENTRIES(sb, | ||
102 | struct sfi_timer_table_entry); | ||
103 | pentry = (struct sfi_timer_table_entry *) sb->pentry; | ||
104 | totallen = sfi_mtimer_num * sizeof(*pentry); | ||
105 | memcpy(sfi_mtimer_array, pentry, totallen); | ||
106 | } | ||
107 | |||
108 | pr_debug("SFI MTIMER info (num = %d):\n", sfi_mtimer_num); | ||
109 | pentry = sfi_mtimer_array; | ||
110 | for (totallen = 0; totallen < sfi_mtimer_num; totallen++, pentry++) { | ||
111 | pr_debug("timer[%d]: paddr = 0x%08x, freq = %dHz," | ||
112 | " irq = %d\n", totallen, (u32)pentry->phys_addr, | ||
113 | pentry->freq_hz, pentry->irq); | ||
114 | if (!pentry->irq) | ||
115 | continue; | ||
116 | mp_irq.type = MP_INTSRC; | ||
117 | mp_irq.irqtype = mp_INT; | ||
118 | /* triggering mode edge bit 2-3, active high polarity bit 0-1 */ | ||
119 | mp_irq.irqflag = 5; | ||
120 | mp_irq.srcbus = MP_BUS_ISA; | ||
121 | mp_irq.srcbusirq = pentry->irq; /* IRQ */ | ||
122 | mp_irq.dstapic = MP_APIC_ALL; | ||
123 | mp_irq.dstirq = pentry->irq; | ||
124 | mp_save_irq(&mp_irq); | ||
125 | } | ||
126 | |||
127 | return 0; | ||
128 | } | ||
129 | |||
130 | struct sfi_timer_table_entry *sfi_get_mtmr(int hint) | ||
131 | { | ||
132 | int i; | ||
133 | if (hint < sfi_mtimer_num) { | ||
134 | if (!sfi_mtimer_usage[hint]) { | ||
135 | pr_debug("hint taken for timer %d irq %d\n", | ||
136 | hint, sfi_mtimer_array[hint].irq); | ||
137 | sfi_mtimer_usage[hint] = 1; | ||
138 | return &sfi_mtimer_array[hint]; | ||
139 | } | ||
140 | } | ||
141 | /* take the first timer available */ | ||
142 | for (i = 0; i < sfi_mtimer_num;) { | ||
143 | if (!sfi_mtimer_usage[i]) { | ||
144 | sfi_mtimer_usage[i] = 1; | ||
145 | return &sfi_mtimer_array[i]; | ||
146 | } | ||
147 | i++; | ||
148 | } | ||
149 | return NULL; | ||
150 | } | ||
151 | |||
152 | void sfi_free_mtmr(struct sfi_timer_table_entry *mtmr) | ||
153 | { | ||
154 | int i; | ||
155 | for (i = 0; i < sfi_mtimer_num;) { | ||
156 | if (mtmr->irq == sfi_mtimer_array[i].irq) { | ||
157 | sfi_mtimer_usage[i] = 0; | ||
158 | return; | ||
159 | } | ||
160 | i++; | ||
161 | } | ||
162 | } | ||
163 | |||
164 | /* parse all the mrtc info to a global mrtc array */ | ||
165 | int __init sfi_parse_mrtc(struct sfi_table_header *table) | ||
166 | { | ||
167 | struct sfi_table_simple *sb; | ||
168 | struct sfi_rtc_table_entry *pentry; | ||
169 | struct mpc_intsrc mp_irq; | ||
170 | |||
171 | int totallen; | ||
172 | |||
173 | sb = (struct sfi_table_simple *)table; | ||
174 | if (!sfi_mrtc_num) { | ||
175 | sfi_mrtc_num = SFI_GET_NUM_ENTRIES(sb, | ||
176 | struct sfi_rtc_table_entry); | ||
177 | pentry = (struct sfi_rtc_table_entry *)sb->pentry; | ||
178 | totallen = sfi_mrtc_num * sizeof(*pentry); | ||
179 | memcpy(sfi_mrtc_array, pentry, totallen); | ||
180 | } | ||
181 | |||
182 | pr_debug("SFI RTC info (num = %d):\n", sfi_mrtc_num); | ||
183 | pentry = sfi_mrtc_array; | ||
184 | for (totallen = 0; totallen < sfi_mrtc_num; totallen++, pentry++) { | ||
185 | pr_debug("RTC[%d]: paddr = 0x%08x, irq = %d\n", | ||
186 | totallen, (u32)pentry->phys_addr, pentry->irq); | ||
187 | mp_irq.type = MP_INTSRC; | ||
188 | mp_irq.irqtype = mp_INT; | ||
189 | mp_irq.irqflag = 0xf; /* level trigger and active low */ | ||
190 | mp_irq.srcbus = MP_BUS_ISA; | ||
191 | mp_irq.srcbusirq = pentry->irq; /* IRQ */ | ||
192 | mp_irq.dstapic = MP_APIC_ALL; | ||
193 | mp_irq.dstirq = pentry->irq; | ||
194 | mp_save_irq(&mp_irq); | ||
195 | } | ||
196 | return 0; | ||
197 | } | ||
198 | |||
199 | static unsigned long __init mrst_calibrate_tsc(void) | ||
200 | { | ||
201 | unsigned long fast_calibrate; | ||
202 | u32 lo, hi, ratio, fsb; | ||
203 | |||
204 | rdmsr(MSR_IA32_PERF_STATUS, lo, hi); | ||
205 | pr_debug("IA32 perf status is 0x%x, 0x%0x\n", lo, hi); | ||
206 | ratio = (hi >> 8) & 0x1f; | ||
207 | pr_debug("ratio is %d\n", ratio); | ||
208 | if (!ratio) { | ||
209 | pr_err("read a zero ratio, should be incorrect!\n"); | ||
210 | pr_err("force tsc ratio to 16 ...\n"); | ||
211 | ratio = 16; | ||
212 | } | ||
213 | rdmsr(MSR_FSB_FREQ, lo, hi); | ||
214 | if ((lo & 0x7) == 0x7) | ||
215 | fsb = PENWELL_FSB_FREQ_83SKU; | ||
216 | else | ||
217 | fsb = PENWELL_FSB_FREQ_100SKU; | ||
218 | fast_calibrate = ratio * fsb; | ||
219 | pr_debug("read penwell tsc %lu khz\n", fast_calibrate); | ||
220 | lapic_timer_frequency = fsb * 1000 / HZ; | ||
221 | /* mark tsc clocksource as reliable */ | ||
222 | set_cpu_cap(&boot_cpu_data, X86_FEATURE_TSC_RELIABLE); | ||
223 | |||
224 | if (fast_calibrate) | ||
225 | return fast_calibrate; | ||
226 | |||
227 | return 0; | ||
228 | } | ||
229 | |||
230 | static void __init mrst_time_init(void) | ||
231 | { | ||
232 | sfi_table_parse(SFI_SIG_MTMR, NULL, NULL, sfi_parse_mtmr); | ||
233 | switch (mrst_timer_options) { | ||
234 | case MRST_TIMER_APBT_ONLY: | ||
235 | break; | ||
236 | case MRST_TIMER_LAPIC_APBT: | ||
237 | x86_init.timers.setup_percpu_clockev = setup_boot_APIC_clock; | ||
238 | x86_cpuinit.setup_percpu_clockev = setup_secondary_APIC_clock; | ||
239 | break; | ||
240 | default: | ||
241 | if (!boot_cpu_has(X86_FEATURE_ARAT)) | ||
242 | break; | ||
243 | x86_init.timers.setup_percpu_clockev = setup_boot_APIC_clock; | ||
244 | x86_cpuinit.setup_percpu_clockev = setup_secondary_APIC_clock; | ||
245 | return; | ||
246 | } | ||
247 | /* we need at least one APB timer */ | ||
248 | pre_init_apic_IRQ0(); | ||
249 | apbt_time_init(); | ||
250 | } | ||
251 | |||
252 | static void mrst_arch_setup(void) | ||
253 | { | ||
254 | if (boot_cpu_data.x86 == 6 && boot_cpu_data.x86_model == 0x27) | ||
255 | __mrst_cpu_chip = MRST_CPU_CHIP_PENWELL; | ||
256 | else { | ||
257 | pr_err("Unknown Intel MID CPU (%d:%d), default to Penwell\n", | ||
258 | boot_cpu_data.x86, boot_cpu_data.x86_model); | ||
259 | __mrst_cpu_chip = MRST_CPU_CHIP_PENWELL; | ||
260 | } | ||
261 | } | ||
262 | |||
263 | /* MID systems don't have i8042 controller */ | ||
264 | static int mrst_i8042_detect(void) | ||
265 | { | ||
266 | return 0; | ||
267 | } | ||
268 | |||
269 | /* | ||
270 | * Moorestown does not have external NMI source nor port 0x61 to report | ||
271 | * NMI status. The possible NMI sources are from pmu as a result of NMI | ||
272 | * watchdog or lock debug. Reading io port 0x61 results in 0xff which | ||
273 | * misled NMI handler. | ||
274 | */ | ||
275 | static unsigned char mrst_get_nmi_reason(void) | ||
276 | { | ||
277 | return 0; | ||
278 | } | ||
279 | |||
280 | /* | ||
281 | * Moorestown specific x86_init function overrides and early setup | ||
282 | * calls. | ||
283 | */ | ||
284 | void __init x86_mrst_early_setup(void) | ||
285 | { | ||
286 | x86_init.resources.probe_roms = x86_init_noop; | ||
287 | x86_init.resources.reserve_resources = x86_init_noop; | ||
288 | |||
289 | x86_init.timers.timer_init = mrst_time_init; | ||
290 | x86_init.timers.setup_percpu_clockev = x86_init_noop; | ||
291 | |||
292 | x86_init.irqs.pre_vector_init = x86_init_noop; | ||
293 | |||
294 | x86_init.oem.arch_setup = mrst_arch_setup; | ||
295 | |||
296 | x86_cpuinit.setup_percpu_clockev = apbt_setup_secondary_clock; | ||
297 | |||
298 | x86_platform.calibrate_tsc = mrst_calibrate_tsc; | ||
299 | x86_platform.i8042_detect = mrst_i8042_detect; | ||
300 | x86_init.timers.wallclock_init = mrst_rtc_init; | ||
301 | x86_platform.get_nmi_reason = mrst_get_nmi_reason; | ||
302 | |||
303 | x86_init.pci.init = pci_mrst_init; | ||
304 | x86_init.pci.fixup_irqs = x86_init_noop; | ||
305 | |||
306 | legacy_pic = &null_legacy_pic; | ||
307 | |||
308 | /* Moorestown specific power_off/restart method */ | ||
309 | pm_power_off = mrst_power_off; | ||
310 | machine_ops.emergency_restart = mrst_reboot; | ||
311 | |||
312 | /* Avoid searching for BIOS MP tables */ | ||
313 | x86_init.mpparse.find_smp_config = x86_init_noop; | ||
314 | x86_init.mpparse.get_smp_config = x86_init_uint_noop; | ||
315 | set_bit(MP_BUS_ISA, mp_bus_not_pci); | ||
316 | } | ||
317 | |||
318 | /* | ||
319 | * if user does not want to use per CPU apb timer, just give it a lower rating | ||
320 | * than local apic timer and skip the late per cpu timer init. | ||
321 | */ | ||
322 | static inline int __init setup_x86_mrst_timer(char *arg) | ||
323 | { | ||
324 | if (!arg) | ||
325 | return -EINVAL; | ||
326 | |||
327 | if (strcmp("apbt_only", arg) == 0) | ||
328 | mrst_timer_options = MRST_TIMER_APBT_ONLY; | ||
329 | else if (strcmp("lapic_and_apbt", arg) == 0) | ||
330 | mrst_timer_options = MRST_TIMER_LAPIC_APBT; | ||
331 | else { | ||
332 | pr_warn("X86 MRST timer option %s not recognised" | ||
333 | " use x86_mrst_timer=apbt_only or lapic_and_apbt\n", | ||
334 | arg); | ||
335 | return -EINVAL; | ||
336 | } | ||
337 | return 0; | ||
338 | } | ||
339 | __setup("x86_mrst_timer=", setup_x86_mrst_timer); | ||
340 | |||
341 | /* | ||
342 | * Parsing GPIO table first, since the DEVS table will need this table | ||
343 | * to map the pin name to the actual pin. | ||
344 | */ | ||
345 | static struct sfi_gpio_table_entry *gpio_table; | ||
346 | static int gpio_num_entry; | ||
347 | |||
348 | static int __init sfi_parse_gpio(struct sfi_table_header *table) | ||
349 | { | ||
350 | struct sfi_table_simple *sb; | ||
351 | struct sfi_gpio_table_entry *pentry; | ||
352 | int num, i; | ||
353 | |||
354 | if (gpio_table) | ||
355 | return 0; | ||
356 | sb = (struct sfi_table_simple *)table; | ||
357 | num = SFI_GET_NUM_ENTRIES(sb, struct sfi_gpio_table_entry); | ||
358 | pentry = (struct sfi_gpio_table_entry *)sb->pentry; | ||
359 | |||
360 | gpio_table = kmalloc(num * sizeof(*pentry), GFP_KERNEL); | ||
361 | if (!gpio_table) | ||
362 | return -1; | ||
363 | memcpy(gpio_table, pentry, num * sizeof(*pentry)); | ||
364 | gpio_num_entry = num; | ||
365 | |||
366 | pr_debug("GPIO pin info:\n"); | ||
367 | for (i = 0; i < num; i++, pentry++) | ||
368 | pr_debug("info[%2d]: controller = %16.16s, pin_name = %16.16s," | ||
369 | " pin = %d\n", i, | ||
370 | pentry->controller_name, | ||
371 | pentry->pin_name, | ||
372 | pentry->pin_no); | ||
373 | return 0; | ||
374 | } | ||
375 | |||
376 | static int get_gpio_by_name(const char *name) | ||
377 | { | ||
378 | struct sfi_gpio_table_entry *pentry = gpio_table; | ||
379 | int i; | ||
380 | |||
381 | if (!pentry) | ||
382 | return -1; | ||
383 | for (i = 0; i < gpio_num_entry; i++, pentry++) { | ||
384 | if (!strncmp(name, pentry->pin_name, SFI_NAME_LEN)) | ||
385 | return pentry->pin_no; | ||
386 | } | ||
387 | return -1; | ||
388 | } | ||
389 | |||
390 | /* | ||
391 | * Here defines the array of devices platform data that IAFW would export | ||
392 | * through SFI "DEVS" table, we use name and type to match the device and | ||
393 | * its platform data. | ||
394 | */ | ||
395 | struct devs_id { | ||
396 | char name[SFI_NAME_LEN + 1]; | ||
397 | u8 type; | ||
398 | u8 delay; | ||
399 | void *(*get_platform_data)(void *info); | ||
400 | }; | ||
401 | |||
402 | /* the offset for the mapping of global gpio pin to irq */ | ||
403 | #define MRST_IRQ_OFFSET 0x100 | ||
404 | |||
405 | static void __init *pmic_gpio_platform_data(void *info) | ||
406 | { | ||
407 | static struct intel_pmic_gpio_platform_data pmic_gpio_pdata; | ||
408 | int gpio_base = get_gpio_by_name("pmic_gpio_base"); | ||
409 | |||
410 | if (gpio_base == -1) | ||
411 | gpio_base = 64; | ||
412 | pmic_gpio_pdata.gpio_base = gpio_base; | ||
413 | pmic_gpio_pdata.irq_base = gpio_base + MRST_IRQ_OFFSET; | ||
414 | pmic_gpio_pdata.gpiointr = 0xffffeff8; | ||
415 | |||
416 | return &pmic_gpio_pdata; | ||
417 | } | ||
418 | |||
419 | static void __init *max3111_platform_data(void *info) | ||
420 | { | ||
421 | struct spi_board_info *spi_info = info; | ||
422 | int intr = get_gpio_by_name("max3111_int"); | ||
423 | |||
424 | spi_info->mode = SPI_MODE_0; | ||
425 | if (intr == -1) | ||
426 | return NULL; | ||
427 | spi_info->irq = intr + MRST_IRQ_OFFSET; | ||
428 | return NULL; | ||
429 | } | ||
430 | |||
431 | /* we have multiple max7315 on the board ... */ | ||
432 | #define MAX7315_NUM 2 | ||
433 | static void __init *max7315_platform_data(void *info) | ||
434 | { | ||
435 | static struct pca953x_platform_data max7315_pdata[MAX7315_NUM]; | ||
436 | static int nr; | ||
437 | struct pca953x_platform_data *max7315 = &max7315_pdata[nr]; | ||
438 | struct i2c_board_info *i2c_info = info; | ||
439 | int gpio_base, intr; | ||
440 | char base_pin_name[SFI_NAME_LEN + 1]; | ||
441 | char intr_pin_name[SFI_NAME_LEN + 1]; | ||
442 | |||
443 | if (nr == MAX7315_NUM) { | ||
444 | pr_err("too many max7315s, we only support %d\n", | ||
445 | MAX7315_NUM); | ||
446 | return NULL; | ||
447 | } | ||
448 | /* we have several max7315 on the board, we only need load several | ||
449 | * instances of the same pca953x driver to cover them | ||
450 | */ | ||
451 | strcpy(i2c_info->type, "max7315"); | ||
452 | if (nr++) { | ||
453 | sprintf(base_pin_name, "max7315_%d_base", nr); | ||
454 | sprintf(intr_pin_name, "max7315_%d_int", nr); | ||
455 | } else { | ||
456 | strcpy(base_pin_name, "max7315_base"); | ||
457 | strcpy(intr_pin_name, "max7315_int"); | ||
458 | } | ||
459 | |||
460 | gpio_base = get_gpio_by_name(base_pin_name); | ||
461 | intr = get_gpio_by_name(intr_pin_name); | ||
462 | |||
463 | if (gpio_base == -1) | ||
464 | return NULL; | ||
465 | max7315->gpio_base = gpio_base; | ||
466 | if (intr != -1) { | ||
467 | i2c_info->irq = intr + MRST_IRQ_OFFSET; | ||
468 | max7315->irq_base = gpio_base + MRST_IRQ_OFFSET; | ||
469 | } else { | ||
470 | i2c_info->irq = -1; | ||
471 | max7315->irq_base = -1; | ||
472 | } | ||
473 | return max7315; | ||
474 | } | ||
475 | |||
476 | static void *tca6416_platform_data(void *info) | ||
477 | { | ||
478 | static struct pca953x_platform_data tca6416; | ||
479 | struct i2c_board_info *i2c_info = info; | ||
480 | int gpio_base, intr; | ||
481 | char base_pin_name[SFI_NAME_LEN + 1]; | ||
482 | char intr_pin_name[SFI_NAME_LEN + 1]; | ||
483 | |||
484 | strcpy(i2c_info->type, "tca6416"); | ||
485 | strcpy(base_pin_name, "tca6416_base"); | ||
486 | strcpy(intr_pin_name, "tca6416_int"); | ||
487 | |||
488 | gpio_base = get_gpio_by_name(base_pin_name); | ||
489 | intr = get_gpio_by_name(intr_pin_name); | ||
490 | |||
491 | if (gpio_base == -1) | ||
492 | return NULL; | ||
493 | tca6416.gpio_base = gpio_base; | ||
494 | if (intr != -1) { | ||
495 | i2c_info->irq = intr + MRST_IRQ_OFFSET; | ||
496 | tca6416.irq_base = gpio_base + MRST_IRQ_OFFSET; | ||
497 | } else { | ||
498 | i2c_info->irq = -1; | ||
499 | tca6416.irq_base = -1; | ||
500 | } | ||
501 | return &tca6416; | ||
502 | } | ||
503 | |||
504 | static void *mpu3050_platform_data(void *info) | ||
505 | { | ||
506 | struct i2c_board_info *i2c_info = info; | ||
507 | int intr = get_gpio_by_name("mpu3050_int"); | ||
508 | |||
509 | if (intr == -1) | ||
510 | return NULL; | ||
511 | |||
512 | i2c_info->irq = intr + MRST_IRQ_OFFSET; | ||
513 | return NULL; | ||
514 | } | ||
515 | |||
516 | static void __init *emc1403_platform_data(void *info) | ||
517 | { | ||
518 | static short intr2nd_pdata; | ||
519 | struct i2c_board_info *i2c_info = info; | ||
520 | int intr = get_gpio_by_name("thermal_int"); | ||
521 | int intr2nd = get_gpio_by_name("thermal_alert"); | ||
522 | |||
523 | if (intr == -1 || intr2nd == -1) | ||
524 | return NULL; | ||
525 | |||
526 | i2c_info->irq = intr + MRST_IRQ_OFFSET; | ||
527 | intr2nd_pdata = intr2nd + MRST_IRQ_OFFSET; | ||
528 | |||
529 | return &intr2nd_pdata; | ||
530 | } | ||
531 | |||
532 | static void __init *lis331dl_platform_data(void *info) | ||
533 | { | ||
534 | static short intr2nd_pdata; | ||
535 | struct i2c_board_info *i2c_info = info; | ||
536 | int intr = get_gpio_by_name("accel_int"); | ||
537 | int intr2nd = get_gpio_by_name("accel_2"); | ||
538 | |||
539 | if (intr == -1 || intr2nd == -1) | ||
540 | return NULL; | ||
541 | |||
542 | i2c_info->irq = intr + MRST_IRQ_OFFSET; | ||
543 | intr2nd_pdata = intr2nd + MRST_IRQ_OFFSET; | ||
544 | |||
545 | return &intr2nd_pdata; | ||
546 | } | ||
547 | |||
548 | static void __init *no_platform_data(void *info) | ||
549 | { | ||
550 | return NULL; | ||
551 | } | ||
552 | |||
553 | static struct resource msic_resources[] = { | ||
554 | { | ||
555 | .start = INTEL_MSIC_IRQ_PHYS_BASE, | ||
556 | .end = INTEL_MSIC_IRQ_PHYS_BASE + 64 - 1, | ||
557 | .flags = IORESOURCE_MEM, | ||
558 | }, | ||
559 | }; | ||
560 | |||
561 | static struct intel_msic_platform_data msic_pdata; | ||
562 | |||
563 | static struct platform_device msic_device = { | ||
564 | .name = "intel_msic", | ||
565 | .id = -1, | ||
566 | .dev = { | ||
567 | .platform_data = &msic_pdata, | ||
568 | }, | ||
569 | .num_resources = ARRAY_SIZE(msic_resources), | ||
570 | .resource = msic_resources, | ||
571 | }; | ||
572 | |||
573 | static inline bool mrst_has_msic(void) | ||
574 | { | ||
575 | return mrst_identify_cpu() == MRST_CPU_CHIP_PENWELL; | ||
576 | } | ||
577 | |||
578 | static int msic_scu_status_change(struct notifier_block *nb, | ||
579 | unsigned long code, void *data) | ||
580 | { | ||
581 | if (code == SCU_DOWN) { | ||
582 | platform_device_unregister(&msic_device); | ||
583 | return 0; | ||
584 | } | ||
585 | |||
586 | return platform_device_register(&msic_device); | ||
587 | } | ||
588 | |||
589 | static int __init msic_init(void) | ||
590 | { | ||
591 | static struct notifier_block msic_scu_notifier = { | ||
592 | .notifier_call = msic_scu_status_change, | ||
593 | }; | ||
594 | |||
595 | /* | ||
596 | * We need to be sure that the SCU IPC is ready before MSIC device | ||
597 | * can be registered. | ||
598 | */ | ||
599 | if (mrst_has_msic()) | ||
600 | intel_scu_notifier_add(&msic_scu_notifier); | ||
601 | |||
602 | return 0; | ||
603 | } | ||
604 | arch_initcall(msic_init); | ||
605 | |||
606 | /* | ||
607 | * msic_generic_platform_data - sets generic platform data for the block | ||
608 | * @info: pointer to the SFI device table entry for this block | ||
609 | * @block: MSIC block | ||
610 | * | ||
611 | * Function sets IRQ number from the SFI table entry for given device to | ||
612 | * the MSIC platform data. | ||
613 | */ | ||
614 | static void *msic_generic_platform_data(void *info, enum intel_msic_block block) | ||
615 | { | ||
616 | struct sfi_device_table_entry *entry = info; | ||
617 | |||
618 | BUG_ON(block < 0 || block >= INTEL_MSIC_BLOCK_LAST); | ||
619 | msic_pdata.irq[block] = entry->irq; | ||
620 | |||
621 | return no_platform_data(info); | ||
622 | } | ||
623 | |||
624 | static void *msic_battery_platform_data(void *info) | ||
625 | { | ||
626 | return msic_generic_platform_data(info, INTEL_MSIC_BLOCK_BATTERY); | ||
627 | } | ||
628 | |||
629 | static void *msic_gpio_platform_data(void *info) | ||
630 | { | ||
631 | static struct intel_msic_gpio_pdata pdata; | ||
632 | int gpio = get_gpio_by_name("msic_gpio_base"); | ||
633 | |||
634 | if (gpio < 0) | ||
635 | return NULL; | ||
636 | |||
637 | pdata.gpio_base = gpio; | ||
638 | msic_pdata.gpio = &pdata; | ||
639 | |||
640 | return msic_generic_platform_data(info, INTEL_MSIC_BLOCK_GPIO); | ||
641 | } | ||
642 | |||
643 | static void *msic_audio_platform_data(void *info) | ||
644 | { | ||
645 | struct platform_device *pdev; | ||
646 | |||
647 | pdev = platform_device_register_simple("sst-platform", -1, NULL, 0); | ||
648 | if (IS_ERR(pdev)) { | ||
649 | pr_err("failed to create audio platform device\n"); | ||
650 | return NULL; | ||
651 | } | ||
652 | |||
653 | return msic_generic_platform_data(info, INTEL_MSIC_BLOCK_AUDIO); | ||
654 | } | ||
655 | |||
656 | static void *msic_power_btn_platform_data(void *info) | ||
657 | { | ||
658 | return msic_generic_platform_data(info, INTEL_MSIC_BLOCK_POWER_BTN); | ||
659 | } | ||
660 | |||
661 | static void *msic_ocd_platform_data(void *info) | ||
662 | { | ||
663 | static struct intel_msic_ocd_pdata pdata; | ||
664 | int gpio = get_gpio_by_name("ocd_gpio"); | ||
665 | |||
666 | if (gpio < 0) | ||
667 | return NULL; | ||
668 | |||
669 | pdata.gpio = gpio; | ||
670 | msic_pdata.ocd = &pdata; | ||
671 | |||
672 | return msic_generic_platform_data(info, INTEL_MSIC_BLOCK_OCD); | ||
673 | } | ||
674 | |||
675 | static void *msic_thermal_platform_data(void *info) | ||
676 | { | ||
677 | return msic_generic_platform_data(info, INTEL_MSIC_BLOCK_THERMAL); | ||
678 | } | ||
679 | |||
680 | /* tc35876x DSI-LVDS bridge chip and panel platform data */ | ||
681 | static void *tc35876x_platform_data(void *data) | ||
682 | { | ||
683 | static struct tc35876x_platform_data pdata; | ||
684 | |||
685 | /* gpio pins set to -1 will not be used by the driver */ | ||
686 | pdata.gpio_bridge_reset = get_gpio_by_name("LCMB_RXEN"); | ||
687 | pdata.gpio_panel_bl_en = get_gpio_by_name("6S6P_BL_EN"); | ||
688 | pdata.gpio_panel_vadd = get_gpio_by_name("EN_VREG_LCD_V3P3"); | ||
689 | |||
690 | return &pdata; | ||
691 | } | ||
692 | |||
693 | static const struct devs_id __initconst device_ids[] = { | ||
694 | {"bma023", SFI_DEV_TYPE_I2C, 1, &no_platform_data}, | ||
695 | {"pmic_gpio", SFI_DEV_TYPE_SPI, 1, &pmic_gpio_platform_data}, | ||
696 | {"pmic_gpio", SFI_DEV_TYPE_IPC, 1, &pmic_gpio_platform_data}, | ||
697 | {"spi_max3111", SFI_DEV_TYPE_SPI, 0, &max3111_platform_data}, | ||
698 | {"i2c_max7315", SFI_DEV_TYPE_I2C, 1, &max7315_platform_data}, | ||
699 | {"i2c_max7315_2", SFI_DEV_TYPE_I2C, 1, &max7315_platform_data}, | ||
700 | {"tca6416", SFI_DEV_TYPE_I2C, 1, &tca6416_platform_data}, | ||
701 | {"emc1403", SFI_DEV_TYPE_I2C, 1, &emc1403_platform_data}, | ||
702 | {"i2c_accel", SFI_DEV_TYPE_I2C, 0, &lis331dl_platform_data}, | ||
703 | {"pmic_audio", SFI_DEV_TYPE_IPC, 1, &no_platform_data}, | ||
704 | {"mpu3050", SFI_DEV_TYPE_I2C, 1, &mpu3050_platform_data}, | ||
705 | {"i2c_disp_brig", SFI_DEV_TYPE_I2C, 0, &tc35876x_platform_data}, | ||
706 | |||
707 | /* MSIC subdevices */ | ||
708 | {"msic_battery", SFI_DEV_TYPE_IPC, 1, &msic_battery_platform_data}, | ||
709 | {"msic_gpio", SFI_DEV_TYPE_IPC, 1, &msic_gpio_platform_data}, | ||
710 | {"msic_audio", SFI_DEV_TYPE_IPC, 1, &msic_audio_platform_data}, | ||
711 | {"msic_power_btn", SFI_DEV_TYPE_IPC, 1, &msic_power_btn_platform_data}, | ||
712 | {"msic_ocd", SFI_DEV_TYPE_IPC, 1, &msic_ocd_platform_data}, | ||
713 | {"msic_thermal", SFI_DEV_TYPE_IPC, 1, &msic_thermal_platform_data}, | ||
714 | |||
715 | {}, | ||
716 | }; | ||
717 | |||
718 | #define MAX_IPCDEVS 24 | ||
719 | static struct platform_device *ipc_devs[MAX_IPCDEVS]; | ||
720 | static int ipc_next_dev; | ||
721 | |||
722 | #define MAX_SCU_SPI 24 | ||
723 | static struct spi_board_info *spi_devs[MAX_SCU_SPI]; | ||
724 | static int spi_next_dev; | ||
725 | |||
726 | #define MAX_SCU_I2C 24 | ||
727 | static struct i2c_board_info *i2c_devs[MAX_SCU_I2C]; | ||
728 | static int i2c_bus[MAX_SCU_I2C]; | ||
729 | static int i2c_next_dev; | ||
730 | |||
731 | static void __init intel_scu_device_register(struct platform_device *pdev) | ||
732 | { | ||
733 | if (ipc_next_dev == MAX_IPCDEVS) | ||
734 | pr_err("too many SCU IPC devices"); | ||
735 | else | ||
736 | ipc_devs[ipc_next_dev++] = pdev; | ||
737 | } | ||
738 | |||
739 | static void __init intel_scu_spi_device_register(struct spi_board_info *sdev) | ||
740 | { | ||
741 | struct spi_board_info *new_dev; | ||
742 | |||
743 | if (spi_next_dev == MAX_SCU_SPI) { | ||
744 | pr_err("too many SCU SPI devices"); | ||
745 | return; | ||
746 | } | ||
747 | |||
748 | new_dev = kzalloc(sizeof(*sdev), GFP_KERNEL); | ||
749 | if (!new_dev) { | ||
750 | pr_err("failed to alloc mem for delayed spi dev %s\n", | ||
751 | sdev->modalias); | ||
752 | return; | ||
753 | } | ||
754 | memcpy(new_dev, sdev, sizeof(*sdev)); | ||
755 | |||
756 | spi_devs[spi_next_dev++] = new_dev; | ||
757 | } | ||
758 | |||
759 | static void __init intel_scu_i2c_device_register(int bus, | ||
760 | struct i2c_board_info *idev) | ||
761 | { | ||
762 | struct i2c_board_info *new_dev; | ||
763 | |||
764 | if (i2c_next_dev == MAX_SCU_I2C) { | ||
765 | pr_err("too many SCU I2C devices"); | ||
766 | return; | ||
767 | } | ||
768 | |||
769 | new_dev = kzalloc(sizeof(*idev), GFP_KERNEL); | ||
770 | if (!new_dev) { | ||
771 | pr_err("failed to alloc mem for delayed i2c dev %s\n", | ||
772 | idev->type); | ||
773 | return; | ||
774 | } | ||
775 | memcpy(new_dev, idev, sizeof(*idev)); | ||
776 | |||
777 | i2c_bus[i2c_next_dev] = bus; | ||
778 | i2c_devs[i2c_next_dev++] = new_dev; | ||
779 | } | ||
780 | |||
781 | BLOCKING_NOTIFIER_HEAD(intel_scu_notifier); | ||
782 | EXPORT_SYMBOL_GPL(intel_scu_notifier); | ||
783 | |||
784 | /* Called by IPC driver */ | ||
785 | void intel_scu_devices_create(void) | ||
786 | { | ||
787 | int i; | ||
788 | |||
789 | for (i = 0; i < ipc_next_dev; i++) | ||
790 | platform_device_add(ipc_devs[i]); | ||
791 | |||
792 | for (i = 0; i < spi_next_dev; i++) | ||
793 | spi_register_board_info(spi_devs[i], 1); | ||
794 | |||
795 | for (i = 0; i < i2c_next_dev; i++) { | ||
796 | struct i2c_adapter *adapter; | ||
797 | struct i2c_client *client; | ||
798 | |||
799 | adapter = i2c_get_adapter(i2c_bus[i]); | ||
800 | if (adapter) { | ||
801 | client = i2c_new_device(adapter, i2c_devs[i]); | ||
802 | if (!client) | ||
803 | pr_err("can't create i2c device %s\n", | ||
804 | i2c_devs[i]->type); | ||
805 | } else | ||
806 | i2c_register_board_info(i2c_bus[i], i2c_devs[i], 1); | ||
807 | } | ||
808 | intel_scu_notifier_post(SCU_AVAILABLE, NULL); | ||
809 | } | ||
810 | EXPORT_SYMBOL_GPL(intel_scu_devices_create); | ||
811 | |||
812 | /* Called by IPC driver */ | ||
813 | void intel_scu_devices_destroy(void) | ||
814 | { | ||
815 | int i; | ||
816 | |||
817 | intel_scu_notifier_post(SCU_DOWN, NULL); | ||
818 | |||
819 | for (i = 0; i < ipc_next_dev; i++) | ||
820 | platform_device_del(ipc_devs[i]); | ||
821 | } | ||
822 | EXPORT_SYMBOL_GPL(intel_scu_devices_destroy); | ||
823 | |||
824 | static void __init install_irq_resource(struct platform_device *pdev, int irq) | ||
825 | { | ||
826 | /* Single threaded */ | ||
827 | static struct resource __initdata res = { | ||
828 | .name = "IRQ", | ||
829 | .flags = IORESOURCE_IRQ, | ||
830 | }; | ||
831 | res.start = irq; | ||
832 | platform_device_add_resources(pdev, &res, 1); | ||
833 | } | ||
834 | |||
835 | static void __init sfi_handle_ipc_dev(struct sfi_device_table_entry *entry) | ||
836 | { | ||
837 | const struct devs_id *dev = device_ids; | ||
838 | struct platform_device *pdev; | ||
839 | void *pdata = NULL; | ||
840 | |||
841 | while (dev->name[0]) { | ||
842 | if (dev->type == SFI_DEV_TYPE_IPC && | ||
843 | !strncmp(dev->name, entry->name, SFI_NAME_LEN)) { | ||
844 | pdata = dev->get_platform_data(entry); | ||
845 | break; | ||
846 | } | ||
847 | dev++; | ||
848 | } | ||
849 | |||
850 | /* | ||
851 | * On Medfield the platform device creation is handled by the MSIC | ||
852 | * MFD driver so we don't need to do it here. | ||
853 | */ | ||
854 | if (mrst_has_msic()) | ||
855 | return; | ||
856 | |||
857 | pdev = platform_device_alloc(entry->name, 0); | ||
858 | if (pdev == NULL) { | ||
859 | pr_err("out of memory for SFI platform device '%s'.\n", | ||
860 | entry->name); | ||
861 | return; | ||
862 | } | ||
863 | install_irq_resource(pdev, entry->irq); | ||
864 | |||
865 | pdev->dev.platform_data = pdata; | ||
866 | intel_scu_device_register(pdev); | ||
867 | } | ||
868 | |||
869 | static void __init sfi_handle_spi_dev(struct spi_board_info *spi_info) | ||
870 | { | ||
871 | const struct devs_id *dev = device_ids; | ||
872 | void *pdata = NULL; | ||
873 | |||
874 | while (dev->name[0]) { | ||
875 | if (dev->type == SFI_DEV_TYPE_SPI && | ||
876 | !strncmp(dev->name, spi_info->modalias, | ||
877 | SFI_NAME_LEN)) { | ||
878 | pdata = dev->get_platform_data(spi_info); | ||
879 | break; | ||
880 | } | ||
881 | dev++; | ||
882 | } | ||
883 | spi_info->platform_data = pdata; | ||
884 | if (dev->delay) | ||
885 | intel_scu_spi_device_register(spi_info); | ||
886 | else | ||
887 | spi_register_board_info(spi_info, 1); | ||
888 | } | ||
889 | |||
890 | static void __init sfi_handle_i2c_dev(int bus, struct i2c_board_info *i2c_info) | ||
891 | { | ||
892 | const struct devs_id *dev = device_ids; | ||
893 | void *pdata = NULL; | ||
894 | |||
895 | while (dev->name[0]) { | ||
896 | if (dev->type == SFI_DEV_TYPE_I2C && | ||
897 | !strncmp(dev->name, i2c_info->type, SFI_NAME_LEN)) { | ||
898 | pdata = dev->get_platform_data(i2c_info); | ||
899 | break; | ||
900 | } | ||
901 | dev++; | ||
902 | } | ||
903 | i2c_info->platform_data = pdata; | ||
904 | |||
905 | if (dev->delay) | ||
906 | intel_scu_i2c_device_register(bus, i2c_info); | ||
907 | else | ||
908 | i2c_register_board_info(bus, i2c_info, 1); | ||
909 | } | ||
910 | |||
911 | |||
912 | static int __init sfi_parse_devs(struct sfi_table_header *table) | ||
913 | { | ||
914 | struct sfi_table_simple *sb; | ||
915 | struct sfi_device_table_entry *pentry; | ||
916 | struct spi_board_info spi_info; | ||
917 | struct i2c_board_info i2c_info; | ||
918 | int num, i, bus; | ||
919 | int ioapic; | ||
920 | struct io_apic_irq_attr irq_attr; | ||
921 | |||
922 | sb = (struct sfi_table_simple *)table; | ||
923 | num = SFI_GET_NUM_ENTRIES(sb, struct sfi_device_table_entry); | ||
924 | pentry = (struct sfi_device_table_entry *)sb->pentry; | ||
925 | |||
926 | for (i = 0; i < num; i++, pentry++) { | ||
927 | int irq = pentry->irq; | ||
928 | |||
929 | if (irq != (u8)0xff) { /* native RTE case */ | ||
930 | /* these SPI2 devices are not exposed to system as PCI | ||
931 | * devices, but they have separate RTE entry in IOAPIC | ||
932 | * so we have to enable them one by one here | ||
933 | */ | ||
934 | ioapic = mp_find_ioapic(irq); | ||
935 | irq_attr.ioapic = ioapic; | ||
936 | irq_attr.ioapic_pin = irq; | ||
937 | irq_attr.trigger = 1; | ||
938 | irq_attr.polarity = 1; | ||
939 | io_apic_set_pci_routing(NULL, irq, &irq_attr); | ||
940 | } else | ||
941 | irq = 0; /* No irq */ | ||
942 | |||
943 | switch (pentry->type) { | ||
944 | case SFI_DEV_TYPE_IPC: | ||
945 | pr_debug("info[%2d]: IPC bus, name = %16.16s, " | ||
946 | "irq = 0x%2x\n", i, pentry->name, pentry->irq); | ||
947 | sfi_handle_ipc_dev(pentry); | ||
948 | break; | ||
949 | case SFI_DEV_TYPE_SPI: | ||
950 | memset(&spi_info, 0, sizeof(spi_info)); | ||
951 | strncpy(spi_info.modalias, pentry->name, SFI_NAME_LEN); | ||
952 | spi_info.irq = irq; | ||
953 | spi_info.bus_num = pentry->host_num; | ||
954 | spi_info.chip_select = pentry->addr; | ||
955 | spi_info.max_speed_hz = pentry->max_freq; | ||
956 | pr_debug("info[%2d]: SPI bus = %d, name = %16.16s, " | ||
957 | "irq = 0x%2x, max_freq = %d, cs = %d\n", i, | ||
958 | spi_info.bus_num, | ||
959 | spi_info.modalias, | ||
960 | spi_info.irq, | ||
961 | spi_info.max_speed_hz, | ||
962 | spi_info.chip_select); | ||
963 | sfi_handle_spi_dev(&spi_info); | ||
964 | break; | ||
965 | case SFI_DEV_TYPE_I2C: | ||
966 | memset(&i2c_info, 0, sizeof(i2c_info)); | ||
967 | bus = pentry->host_num; | ||
968 | strncpy(i2c_info.type, pentry->name, SFI_NAME_LEN); | ||
969 | i2c_info.irq = irq; | ||
970 | i2c_info.addr = pentry->addr; | ||
971 | pr_debug("info[%2d]: I2C bus = %d, name = %16.16s, " | ||
972 | "irq = 0x%2x, addr = 0x%x\n", i, bus, | ||
973 | i2c_info.type, | ||
974 | i2c_info.irq, | ||
975 | i2c_info.addr); | ||
976 | sfi_handle_i2c_dev(bus, &i2c_info); | ||
977 | break; | ||
978 | case SFI_DEV_TYPE_UART: | ||
979 | case SFI_DEV_TYPE_HSI: | ||
980 | default: | ||
981 | ; | ||
982 | } | ||
983 | } | ||
984 | return 0; | ||
985 | } | ||
986 | |||
987 | static int __init mrst_platform_init(void) | ||
988 | { | ||
989 | sfi_table_parse(SFI_SIG_GPIO, NULL, NULL, sfi_parse_gpio); | ||
990 | sfi_table_parse(SFI_SIG_DEVS, NULL, NULL, sfi_parse_devs); | ||
991 | return 0; | ||
992 | } | ||
993 | arch_initcall(mrst_platform_init); | ||
994 | |||
995 | /* | ||
996 | * we will search these buttons in SFI GPIO table (by name) | ||
997 | * and register them dynamically. Please add all possible | ||
998 | * buttons here, we will shrink them if no GPIO found. | ||
999 | */ | ||
1000 | static struct gpio_keys_button gpio_button[] = { | ||
1001 | {KEY_POWER, -1, 1, "power_btn", EV_KEY, 0, 3000}, | ||
1002 | {KEY_PROG1, -1, 1, "prog_btn1", EV_KEY, 0, 20}, | ||
1003 | {KEY_PROG2, -1, 1, "prog_btn2", EV_KEY, 0, 20}, | ||
1004 | {SW_LID, -1, 1, "lid_switch", EV_SW, 0, 20}, | ||
1005 | {KEY_VOLUMEUP, -1, 1, "vol_up", EV_KEY, 0, 20}, | ||
1006 | {KEY_VOLUMEDOWN, -1, 1, "vol_down", EV_KEY, 0, 20}, | ||
1007 | {KEY_CAMERA, -1, 1, "camera_full", EV_KEY, 0, 20}, | ||
1008 | {KEY_CAMERA_FOCUS, -1, 1, "camera_half", EV_KEY, 0, 20}, | ||
1009 | {SW_KEYPAD_SLIDE, -1, 1, "MagSw1", EV_SW, 0, 20}, | ||
1010 | {SW_KEYPAD_SLIDE, -1, 1, "MagSw2", EV_SW, 0, 20}, | ||
1011 | }; | ||
1012 | |||
1013 | static struct gpio_keys_platform_data mrst_gpio_keys = { | ||
1014 | .buttons = gpio_button, | ||
1015 | .rep = 1, | ||
1016 | .nbuttons = -1, /* will fill it after search */ | ||
1017 | }; | ||
1018 | |||
1019 | static struct platform_device pb_device = { | ||
1020 | .name = "gpio-keys", | ||
1021 | .id = -1, | ||
1022 | .dev = { | ||
1023 | .platform_data = &mrst_gpio_keys, | ||
1024 | }, | ||
1025 | }; | ||
1026 | |||
1027 | /* | ||
1028 | * Shrink the non-existent buttons, register the gpio button | ||
1029 | * device if there is some | ||
1030 | */ | ||
1031 | static int __init pb_keys_init(void) | ||
1032 | { | ||
1033 | struct gpio_keys_button *gb = gpio_button; | ||
1034 | int i, num, good = 0; | ||
1035 | |||
1036 | num = sizeof(gpio_button) / sizeof(struct gpio_keys_button); | ||
1037 | for (i = 0; i < num; i++) { | ||
1038 | gb[i].gpio = get_gpio_by_name(gb[i].desc); | ||
1039 | pr_debug("info[%2d]: name = %s, gpio = %d\n", i, gb[i].desc, | ||
1040 | gb[i].gpio); | ||
1041 | if (gb[i].gpio == -1) | ||
1042 | continue; | ||
1043 | |||
1044 | if (i != good) | ||
1045 | gb[good] = gb[i]; | ||
1046 | good++; | ||
1047 | } | ||
1048 | |||
1049 | if (good) { | ||
1050 | mrst_gpio_keys.nbuttons = good; | ||
1051 | return platform_device_register(&pb_device); | ||
1052 | } | ||
1053 | return 0; | ||
1054 | } | ||
1055 | late_initcall(pb_keys_init); | ||
1056 |
arch/x86/platform/intel-mid/intel_mid_vrtc.c
File was created | 1 | /* | |
2 | * intel_mid_vrtc.c: Driver for virtual RTC device on Intel MID platform | ||
3 | * | ||
4 | * (C) Copyright 2009 Intel Corporation | ||
5 | * | ||
6 | * This program is free software; you can redistribute it and/or | ||
7 | * modify it under the terms of the GNU General Public License | ||
8 | * as published by the Free Software Foundation; version 2 | ||
9 | * of the License. | ||
10 | * | ||
11 | * Note: | ||
12 | * VRTC is emulated by system controller firmware, the real HW | ||
13 | * RTC is located in the PMIC device. SCU FW shadows PMIC RTC | ||
14 | * in a memory mapped IO space that is visible to the host IA | ||
15 | * processor. | ||
16 | * | ||
17 | * This driver is based on RTC CMOS driver. | ||
18 | */ | ||
19 | |||
20 | #include <linux/kernel.h> | ||
21 | #include <linux/export.h> | ||
22 | #include <linux/init.h> | ||
23 | #include <linux/sfi.h> | ||
24 | #include <linux/platform_device.h> | ||
25 | |||
26 | #include <asm/intel-mid.h> | ||
27 | #include <asm/intel_mid_vrtc.h> | ||
28 | #include <asm/time.h> | ||
29 | #include <asm/fixmap.h> | ||
30 | |||
31 | static unsigned char __iomem *vrtc_virt_base; | ||
32 | |||
33 | unsigned char vrtc_cmos_read(unsigned char reg) | ||
34 | { | ||
35 | unsigned char retval; | ||
36 | |||
37 | /* vRTC's registers range from 0x0 to 0xD */ | ||
38 | if (reg > 0xd || !vrtc_virt_base) | ||
39 | return 0xff; | ||
40 | |||
41 | lock_cmos_prefix(reg); | ||
42 | retval = __raw_readb(vrtc_virt_base + (reg << 2)); | ||
43 | lock_cmos_suffix(reg); | ||
44 | return retval; | ||
45 | } | ||
46 | EXPORT_SYMBOL_GPL(vrtc_cmos_read); | ||
47 | |||
48 | void vrtc_cmos_write(unsigned char val, unsigned char reg) | ||
49 | { | ||
50 | if (reg > 0xd || !vrtc_virt_base) | ||
51 | return; | ||
52 | |||
53 | lock_cmos_prefix(reg); | ||
54 | __raw_writeb(val, vrtc_virt_base + (reg << 2)); | ||
55 | lock_cmos_suffix(reg); | ||
56 | } | ||
57 | EXPORT_SYMBOL_GPL(vrtc_cmos_write); | ||
58 | |||
59 | void vrtc_get_time(struct timespec *now) | ||
60 | { | ||
61 | u8 sec, min, hour, mday, mon; | ||
62 | unsigned long flags; | ||
63 | u32 year; | ||
64 | |||
65 | spin_lock_irqsave(&rtc_lock, flags); | ||
66 | |||
67 | while ((vrtc_cmos_read(RTC_FREQ_SELECT) & RTC_UIP)) | ||
68 | cpu_relax(); | ||
69 | |||
70 | sec = vrtc_cmos_read(RTC_SECONDS); | ||
71 | min = vrtc_cmos_read(RTC_MINUTES); | ||
72 | hour = vrtc_cmos_read(RTC_HOURS); | ||
73 | mday = vrtc_cmos_read(RTC_DAY_OF_MONTH); | ||
74 | mon = vrtc_cmos_read(RTC_MONTH); | ||
75 | year = vrtc_cmos_read(RTC_YEAR); | ||
76 | |||
77 | spin_unlock_irqrestore(&rtc_lock, flags); | ||
78 | |||
79 | /* vRTC YEAR reg contains the offset to 1972 */ | ||
80 | year += 1972; | ||
81 | |||
82 | pr_info("vRTC: sec: %d min: %d hour: %d day: %d " | ||
83 | "mon: %d year: %d\n", sec, min, hour, mday, mon, year); | ||
84 | |||
85 | now->tv_sec = mktime(year, mon, mday, hour, min, sec); | ||
86 | now->tv_nsec = 0; | ||
87 | } | ||
88 | |||
89 | int vrtc_set_mmss(const struct timespec *now) | ||
90 | { | ||
91 | unsigned long flags; | ||
92 | struct rtc_time tm; | ||
93 | int year; | ||
94 | int retval = 0; | ||
95 | |||
96 | rtc_time_to_tm(now->tv_sec, &tm); | ||
97 | if (!rtc_valid_tm(&tm) && tm.tm_year >= 72) { | ||
98 | /* | ||
99 | * tm.year is the number of years since 1900, and the | ||
100 | * vrtc need the years since 1972. | ||
101 | */ | ||
102 | year = tm.tm_year - 72; | ||
103 | spin_lock_irqsave(&rtc_lock, flags); | ||
104 | vrtc_cmos_write(year, RTC_YEAR); | ||
105 | vrtc_cmos_write(tm.tm_mon, RTC_MONTH); | ||
106 | vrtc_cmos_write(tm.tm_mday, RTC_DAY_OF_MONTH); | ||
107 | vrtc_cmos_write(tm.tm_hour, RTC_HOURS); | ||
108 | vrtc_cmos_write(tm.tm_min, RTC_MINUTES); | ||
109 | vrtc_cmos_write(tm.tm_sec, RTC_SECONDS); | ||
110 | spin_unlock_irqrestore(&rtc_lock, flags); | ||
111 | } else { | ||
112 | pr_err("%s: Invalid vRTC value: write of %lx to vRTC failed\n", | ||
113 | __FUNCTION__, now->tv_sec); | ||
114 | retval = -EINVAL; | ||
115 | } | ||
116 | return retval; | ||
117 | } | ||
118 | |||
119 | void __init mrst_rtc_init(void) | ||
120 | { | ||
121 | unsigned long vrtc_paddr; | ||
122 | |||
123 | sfi_table_parse(SFI_SIG_MRTC, NULL, NULL, sfi_parse_mrtc); | ||
124 | |||
125 | vrtc_paddr = sfi_mrtc_array[0].phys_addr; | ||
126 | if (!sfi_mrtc_num || !vrtc_paddr) | ||
127 | return; | ||
128 | |||
129 | vrtc_virt_base = (void __iomem *)set_fixmap_offset_nocache(FIX_LNW_VRTC, | ||
130 | vrtc_paddr); | ||
131 | x86_platform.get_wallclock = vrtc_get_time; | ||
132 | x86_platform.set_wallclock = vrtc_set_mmss; | ||
133 | } | ||
134 | |||
135 | /* | ||
136 | * The Moorestown platform has a memory mapped virtual RTC device that emulates | ||
137 | * the programming interface of the RTC. | ||
138 | */ | ||
139 | |||
140 | static struct resource vrtc_resources[] = { | ||
141 | [0] = { | ||
142 | .flags = IORESOURCE_MEM, | ||
143 | }, | ||
144 | [1] = { | ||
145 | .flags = IORESOURCE_IRQ, | ||
146 | } | ||
147 | }; | ||
148 | |||
149 | static struct platform_device vrtc_device = { | ||
150 | .name = "rtc_mrst", | ||
151 | .id = -1, | ||
152 | .resource = vrtc_resources, | ||
153 | .num_resources = ARRAY_SIZE(vrtc_resources), | ||
154 | }; | ||
155 | |||
156 | /* Register the RTC device if appropriate */ | ||
157 | static int __init mrst_device_create(void) | ||
158 | { | ||
159 | /* No Moorestown, no device */ | ||
160 | if (!mrst_identify_cpu()) | ||
161 | return -ENODEV; | ||
162 | /* No timer, no device */ | ||
163 | if (!sfi_mrtc_num) | ||
164 | return -ENODEV; | ||
165 | |||
166 | /* iomem resource */ | ||
167 | vrtc_resources[0].start = sfi_mrtc_array[0].phys_addr; | ||
168 | vrtc_resources[0].end = sfi_mrtc_array[0].phys_addr + | ||
169 | MRST_VRTC_MAP_SZ; | ||
170 | /* irq resource */ | ||
171 | vrtc_resources[1].start = sfi_mrtc_array[0].irq; | ||
172 | vrtc_resources[1].end = sfi_mrtc_array[0].irq; | ||
173 | |||
174 | return platform_device_register(&vrtc_device); | ||
175 | } | ||
176 | |||
177 | module_init(mrst_device_create); | ||
178 |
arch/x86/platform/mrst/Makefile
1 | obj-$(CONFIG_X86_INTEL_MID) += mrst.o | File was deleted | |
2 | obj-$(CONFIG_X86_INTEL_MID) += vrtc.o | ||
3 | obj-$(CONFIG_EARLY_PRINTK_INTEL_MID) += early_printk_mrst.o | ||
4 | 1 | obj-$(CONFIG_X86_INTEL_MID) += mrst.o |
arch/x86/platform/mrst/early_printk_mrst.c
1 | /* | File was deleted | |
2 | * early_printk_mrst.c - early consoles for Intel MID platforms | ||
3 | * | ||
4 | * Copyright (c) 2008-2010, Intel Corporation | ||
5 | * | ||
6 | * This program is free software; you can redistribute it and/or | ||
7 | * modify it under the terms of the GNU General Public License | ||
8 | * as published by the Free Software Foundation; version 2 | ||
9 | * of the License. | ||
10 | */ | ||
11 | |||
12 | /* | ||
13 | * This file implements two early consoles named mrst and hsu. | ||
14 | * mrst is based on Maxim3110 spi-uart device, it exists in both | ||
15 | * Moorestown and Medfield platforms, while hsu is based on a High | ||
16 | * Speed UART device which only exists in the Medfield platform | ||
17 | */ | ||
18 | |||
19 | #include <linux/serial_reg.h> | ||
20 | #include <linux/serial_mfd.h> | ||
21 | #include <linux/kmsg_dump.h> | ||
22 | #include <linux/console.h> | ||
23 | #include <linux/kernel.h> | ||
24 | #include <linux/delay.h> | ||
25 | #include <linux/init.h> | ||
26 | #include <linux/io.h> | ||
27 | |||
28 | #include <asm/fixmap.h> | ||
29 | #include <asm/pgtable.h> | ||
30 | #include <asm/mrst.h> | ||
31 | |||
32 | #define MRST_SPI_TIMEOUT 0x200000 | ||
33 | #define MRST_REGBASE_SPI0 0xff128000 | ||
34 | #define MRST_REGBASE_SPI1 0xff128400 | ||
35 | #define MRST_CLK_SPI0_REG 0xff11d86c | ||
36 | |||
37 | /* Bit fields in CTRLR0 */ | ||
38 | #define SPI_DFS_OFFSET 0 | ||
39 | |||
40 | #define SPI_FRF_OFFSET 4 | ||
41 | #define SPI_FRF_SPI 0x0 | ||
42 | #define SPI_FRF_SSP 0x1 | ||
43 | #define SPI_FRF_MICROWIRE 0x2 | ||
44 | #define SPI_FRF_RESV 0x3 | ||
45 | |||
46 | #define SPI_MODE_OFFSET 6 | ||
47 | #define SPI_SCPH_OFFSET 6 | ||
48 | #define SPI_SCOL_OFFSET 7 | ||
49 | #define SPI_TMOD_OFFSET 8 | ||
50 | #define SPI_TMOD_TR 0x0 /* xmit & recv */ | ||
51 | #define SPI_TMOD_TO 0x1 /* xmit only */ | ||
52 | #define SPI_TMOD_RO 0x2 /* recv only */ | ||
53 | #define SPI_TMOD_EPROMREAD 0x3 /* eeprom read mode */ | ||
54 | |||
55 | #define SPI_SLVOE_OFFSET 10 | ||
56 | #define SPI_SRL_OFFSET 11 | ||
57 | #define SPI_CFS_OFFSET 12 | ||
58 | |||
59 | /* Bit fields in SR, 7 bits */ | ||
60 | #define SR_MASK 0x7f /* cover 7 bits */ | ||
61 | #define SR_BUSY (1 << 0) | ||
62 | #define SR_TF_NOT_FULL (1 << 1) | ||
63 | #define SR_TF_EMPT (1 << 2) | ||
64 | #define SR_RF_NOT_EMPT (1 << 3) | ||
65 | #define SR_RF_FULL (1 << 4) | ||
66 | #define SR_TX_ERR (1 << 5) | ||
67 | #define SR_DCOL (1 << 6) | ||
68 | |||
69 | struct dw_spi_reg { | ||
70 | u32 ctrl0; | ||
71 | u32 ctrl1; | ||
72 | u32 ssienr; | ||
73 | u32 mwcr; | ||
74 | u32 ser; | ||
75 | u32 baudr; | ||
76 | u32 txfltr; | ||
77 | u32 rxfltr; | ||
78 | u32 txflr; | ||
79 | u32 rxflr; | ||
80 | u32 sr; | ||
81 | u32 imr; | ||
82 | u32 isr; | ||
83 | u32 risr; | ||
84 | u32 txoicr; | ||
85 | u32 rxoicr; | ||
86 | u32 rxuicr; | ||
87 | u32 msticr; | ||
88 | u32 icr; | ||
89 | u32 dmacr; | ||
90 | u32 dmatdlr; | ||
91 | u32 dmardlr; | ||
92 | u32 idr; | ||
93 | u32 version; | ||
94 | |||
95 | /* Currently operates as 32 bits, though only the low 16 bits matter */ | ||
96 | u32 dr; | ||
97 | } __packed; | ||
98 | |||
99 | #define dw_readl(dw, name) __raw_readl(&(dw)->name) | ||
100 | #define dw_writel(dw, name, val) __raw_writel((val), &(dw)->name) | ||
101 | |||
102 | /* Default use SPI0 register for mrst, we will detect Penwell and use SPI1 */ | ||
103 | static unsigned long mrst_spi_paddr = MRST_REGBASE_SPI0; | ||
104 | |||
105 | static u32 *pclk_spi0; | ||
106 | /* Always contains an accessible address, start with 0 */ | ||
107 | static struct dw_spi_reg *pspi; | ||
108 | |||
109 | static struct kmsg_dumper dw_dumper; | ||
110 | static int dumper_registered; | ||
111 | |||
112 | static void dw_kmsg_dump(struct kmsg_dumper *dumper, | ||
113 | enum kmsg_dump_reason reason) | ||
114 | { | ||
115 | static char line[1024]; | ||
116 | size_t len; | ||
117 | |||
118 | /* When run to this, we'd better re-init the HW */ | ||
119 | mrst_early_console_init(); | ||
120 | |||
121 | while (kmsg_dump_get_line(dumper, true, line, sizeof(line), &len)) | ||
122 | early_mrst_console.write(&early_mrst_console, line, len); | ||
123 | } | ||
124 | |||
125 | /* Set the ratio rate to 115200, 8n1, IRQ disabled */ | ||
126 | static void max3110_write_config(void) | ||
127 | { | ||
128 | u16 config; | ||
129 | |||
130 | config = 0xc001; | ||
131 | dw_writel(pspi, dr, config); | ||
132 | } | ||
133 | |||
134 | /* Translate char to a eligible word and send to max3110 */ | ||
135 | static void max3110_write_data(char c) | ||
136 | { | ||
137 | u16 data; | ||
138 | |||
139 | data = 0x8000 | c; | ||
140 | dw_writel(pspi, dr, data); | ||
141 | } | ||
142 | |||
143 | void mrst_early_console_init(void) | ||
144 | { | ||
145 | u32 ctrlr0 = 0; | ||
146 | u32 spi0_cdiv; | ||
147 | u32 freq; /* Freqency info only need be searched once */ | ||
148 | |||
149 | /* Base clk is 100 MHz, the actual clk = 100M / (clk_divider + 1) */ | ||
150 | pclk_spi0 = (void *)set_fixmap_offset_nocache(FIX_EARLYCON_MEM_BASE, | ||
151 | MRST_CLK_SPI0_REG); | ||
152 | spi0_cdiv = ((*pclk_spi0) & 0xe00) >> 9; | ||
153 | freq = 100000000 / (spi0_cdiv + 1); | ||
154 | |||
155 | if (mrst_identify_cpu() == MRST_CPU_CHIP_PENWELL) | ||
156 | mrst_spi_paddr = MRST_REGBASE_SPI1; | ||
157 | |||
158 | pspi = (void *)set_fixmap_offset_nocache(FIX_EARLYCON_MEM_BASE, | ||
159 | mrst_spi_paddr); | ||
160 | |||
161 | /* Disable SPI controller */ | ||
162 | dw_writel(pspi, ssienr, 0); | ||
163 | |||
164 | /* Set control param, 8 bits, transmit only mode */ | ||
165 | ctrlr0 = dw_readl(pspi, ctrl0); | ||
166 | |||
167 | ctrlr0 &= 0xfcc0; | ||
168 | ctrlr0 |= 0xf | (SPI_FRF_SPI << SPI_FRF_OFFSET) | ||
169 | | (SPI_TMOD_TO << SPI_TMOD_OFFSET); | ||
170 | dw_writel(pspi, ctrl0, ctrlr0); | ||
171 | |||
172 | /* | ||
173 | * Change the spi0 clk to comply with 115200 bps, use 100000 to | ||
174 | * calculate the clk dividor to make the clock a little slower | ||
175 | * than real baud rate. | ||
176 | */ | ||
177 | dw_writel(pspi, baudr, freq/100000); | ||
178 | |||
179 | /* Disable all INT for early phase */ | ||
180 | dw_writel(pspi, imr, 0x0); | ||
181 | |||
182 | /* Set the cs to spi-uart */ | ||
183 | dw_writel(pspi, ser, 0x2); | ||
184 | |||
185 | /* Enable the HW, the last step for HW init */ | ||
186 | dw_writel(pspi, ssienr, 0x1); | ||
187 | |||
188 | /* Set the default configuration */ | ||
189 | max3110_write_config(); | ||
190 | |||
191 | /* Register the kmsg dumper */ | ||
192 | if (!dumper_registered) { | ||
193 | dw_dumper.dump = dw_kmsg_dump; | ||
194 | kmsg_dump_register(&dw_dumper); | ||
195 | dumper_registered = 1; | ||
196 | } | ||
197 | } | ||
198 | |||
199 | /* Slave select should be called in the read/write function */ | ||
200 | static void early_mrst_spi_putc(char c) | ||
201 | { | ||
202 | unsigned int timeout; | ||
203 | u32 sr; | ||
204 | |||
205 | timeout = MRST_SPI_TIMEOUT; | ||
206 | /* Early putc needs to make sure the TX FIFO is not full */ | ||
207 | while (--timeout) { | ||
208 | sr = dw_readl(pspi, sr); | ||
209 | if (!(sr & SR_TF_NOT_FULL)) | ||
210 | cpu_relax(); | ||
211 | else | ||
212 | break; | ||
213 | } | ||
214 | |||
215 | if (!timeout) | ||
216 | pr_warn("MRST earlycon: timed out\n"); | ||
217 | else | ||
218 | max3110_write_data(c); | ||
219 | } | ||
220 | |||
221 | /* Early SPI only uses polling mode */ | ||
222 | static void early_mrst_spi_write(struct console *con, const char *str, | ||
223 | unsigned n) | ||
224 | { | ||
225 | int i; | ||
226 | |||
227 | for (i = 0; i < n && *str; i++) { | ||
228 | if (*str == '\n') | ||
229 | early_mrst_spi_putc('\r'); | ||
230 | early_mrst_spi_putc(*str); | ||
231 | str++; | ||
232 | } | ||
233 | } | ||
234 | |||
235 | struct console early_mrst_console = { | ||
236 | .name = "earlymrst", | ||
237 | .write = early_mrst_spi_write, | ||
238 | .flags = CON_PRINTBUFFER, | ||
239 | .index = -1, | ||
240 | }; | ||
241 | |||
242 | /* | ||
243 | * Following is the early console based on Medfield HSU (High | ||
244 | * Speed UART) device. | ||
245 | */ | ||
246 | #define HSU_PORT_BASE 0xffa28080 | ||
247 | |||
248 | static void __iomem *phsu; | ||
249 | |||
250 | void hsu_early_console_init(const char *s) | ||
251 | { | ||
252 | unsigned long paddr, port = 0; | ||
253 | u8 lcr; | ||
254 | |||
255 | /* | ||
256 | * Select the early HSU console port if specified by user in the | ||
257 | * kernel command line. | ||
258 | */ | ||
259 | if (*s && !kstrtoul(s, 10, &port)) | ||
260 | port = clamp_val(port, 0, 2); | ||
261 | |||
262 | paddr = HSU_PORT_BASE + port * 0x80; | ||
263 | phsu = (void *)set_fixmap_offset_nocache(FIX_EARLYCON_MEM_BASE, paddr); | ||
264 | |||
265 | /* Disable FIFO */ | ||
266 | writeb(0x0, phsu + UART_FCR); | ||
267 | |||
268 | /* Set to default 115200 bps, 8n1 */ | ||
269 | lcr = readb(phsu + UART_LCR); | ||
270 | writeb((0x80 | lcr), phsu + UART_LCR); | ||
271 | writeb(0x18, phsu + UART_DLL); | ||
272 | writeb(lcr, phsu + UART_LCR); | ||
273 | writel(0x3600, phsu + UART_MUL*4); | ||
274 | |||
275 | writeb(0x8, phsu + UART_MCR); | ||
276 | writeb(0x7, phsu + UART_FCR); | ||
277 | writeb(0x3, phsu + UART_LCR); | ||
278 | |||
279 | /* Clear IRQ status */ | ||
280 | readb(phsu + UART_LSR); | ||
281 | readb(phsu + UART_RX); | ||
282 | readb(phsu + UART_IIR); | ||
283 | readb(phsu + UART_MSR); | ||
284 | |||
285 | /* Enable FIFO */ | ||
286 | writeb(0x7, phsu + UART_FCR); | ||
287 | } | ||
288 | |||
289 | #define BOTH_EMPTY (UART_LSR_TEMT | UART_LSR_THRE) | ||
290 | |||
291 | static void early_hsu_putc(char ch) | ||
292 | { | ||
293 | unsigned int timeout = 10000; /* 10ms */ | ||
294 | u8 status; | ||
295 | |||
296 | while (--timeout) { | ||
297 | status = readb(phsu + UART_LSR); | ||
298 | if (status & BOTH_EMPTY) | ||
299 | break; | ||
300 | udelay(1); | ||
301 | } | ||
302 | |||
303 | /* Only write the char when there was no timeout */ | ||
304 | if (timeout) | ||
305 | writeb(ch, phsu + UART_TX); | ||
306 | } | ||
307 | |||
308 | static void early_hsu_write(struct console *con, const char *str, unsigned n) | ||
309 | { | ||
310 | int i; | ||
311 | |||
312 | for (i = 0; i < n && *str; i++) { | ||
313 | if (*str == '\n') | ||
314 | early_hsu_putc('\r'); | ||
315 | early_hsu_putc(*str); | ||
316 | str++; | ||
317 | } | ||
318 | } | ||
319 | |||
320 | struct console early_hsu_console = { | ||
321 | .name = "earlyhsu", | ||
322 | .write = early_hsu_write, | ||
323 | .flags = CON_PRINTBUFFER, | ||
324 | .index = -1, | ||
325 | }; | ||
326 | 1 | /* |
arch/x86/platform/mrst/mrst.c
1 | /* | File was deleted | |
2 | * mrst.c: Intel Moorestown platform specific setup code | ||
3 | * | ||
4 | * (C) Copyright 2008 Intel Corporation | ||
5 | * Author: Jacob Pan (jacob.jun.pan@intel.com) | ||
6 | * | ||
7 | * This program is free software; you can redistribute it and/or | ||
8 | * modify it under the terms of the GNU General Public License | ||
9 | * as published by the Free Software Foundation; version 2 | ||
10 | * of the License. | ||
11 | */ | ||
12 | |||
13 | #define pr_fmt(fmt) "mrst: " fmt | ||
14 | |||
15 | #include <linux/init.h> | ||
16 | #include <linux/kernel.h> | ||
17 | #include <linux/interrupt.h> | ||
18 | #include <linux/scatterlist.h> | ||
19 | #include <linux/sfi.h> | ||
20 | #include <linux/intel_pmic_gpio.h> | ||
21 | #include <linux/spi/spi.h> | ||
22 | #include <linux/i2c.h> | ||
23 | #include <linux/platform_data/pca953x.h> | ||
24 | #include <linux/gpio_keys.h> | ||
25 | #include <linux/input.h> | ||
26 | #include <linux/platform_device.h> | ||
27 | #include <linux/irq.h> | ||
28 | #include <linux/module.h> | ||
29 | #include <linux/notifier.h> | ||
30 | #include <linux/mfd/intel_msic.h> | ||
31 | #include <linux/gpio.h> | ||
32 | #include <linux/i2c/tc35876x.h> | ||
33 | |||
34 | #include <asm/setup.h> | ||
35 | #include <asm/mpspec_def.h> | ||
36 | #include <asm/hw_irq.h> | ||
37 | #include <asm/apic.h> | ||
38 | #include <asm/io_apic.h> | ||
39 | #include <asm/mrst.h> | ||
40 | #include <asm/mrst-vrtc.h> | ||
41 | #include <asm/io.h> | ||
42 | #include <asm/i8259.h> | ||
43 | #include <asm/intel_scu_ipc.h> | ||
44 | #include <asm/apb_timer.h> | ||
45 | #include <asm/reboot.h> | ||
46 | |||
47 | /* | ||
48 | * the clockevent devices on Moorestown/Medfield can be APBT or LAPIC clock, | ||
49 | * cmdline option x86_mrst_timer can be used to override the configuration | ||
50 | * to prefer one or the other. | ||
51 | * at runtime, there are basically three timer configurations: | ||
52 | * 1. per cpu apbt clock only | ||
53 | * 2. per cpu always-on lapic clocks only, this is Penwell/Medfield only | ||
54 | * 3. per cpu lapic clock (C3STOP) and one apbt clock, with broadcast. | ||
55 | * | ||
56 | * by default (without cmdline option), platform code first detects cpu type | ||
57 | * to see if we are on lincroft or penwell, then set up both lapic or apbt | ||
58 | * clocks accordingly. | ||
59 | * i.e. by default, medfield uses configuration #2, moorestown uses #1. | ||
60 | * config #3 is supported but not recommended on medfield. | ||
61 | * | ||
62 | * rating and feature summary: | ||
63 | * lapic (with C3STOP) --------- 100 | ||
64 | * apbt (always-on) ------------ 110 | ||
65 | * lapic (always-on,ARAT) ------ 150 | ||
66 | */ | ||
67 | |||
68 | enum mrst_timer_options mrst_timer_options; | ||
69 | |||
70 | static u32 sfi_mtimer_usage[SFI_MTMR_MAX_NUM]; | ||
71 | static struct sfi_timer_table_entry sfi_mtimer_array[SFI_MTMR_MAX_NUM]; | ||
72 | enum mrst_cpu_type __mrst_cpu_chip; | ||
73 | EXPORT_SYMBOL_GPL(__mrst_cpu_chip); | ||
74 | |||
75 | int sfi_mtimer_num; | ||
76 | |||
77 | struct sfi_rtc_table_entry sfi_mrtc_array[SFI_MRTC_MAX]; | ||
78 | EXPORT_SYMBOL_GPL(sfi_mrtc_array); | ||
79 | int sfi_mrtc_num; | ||
80 | |||
81 | static void mrst_power_off(void) | ||
82 | { | ||
83 | } | ||
84 | |||
85 | static void mrst_reboot(void) | ||
86 | { | ||
87 | intel_scu_ipc_simple_command(IPCMSG_COLD_BOOT, 0); | ||
88 | } | ||
89 | |||
90 | /* parse all the mtimer info to a static mtimer array */ | ||
91 | static int __init sfi_parse_mtmr(struct sfi_table_header *table) | ||
92 | { | ||
93 | struct sfi_table_simple *sb; | ||
94 | struct sfi_timer_table_entry *pentry; | ||
95 | struct mpc_intsrc mp_irq; | ||
96 | int totallen; | ||
97 | |||
98 | sb = (struct sfi_table_simple *)table; | ||
99 | if (!sfi_mtimer_num) { | ||
100 | sfi_mtimer_num = SFI_GET_NUM_ENTRIES(sb, | ||
101 | struct sfi_timer_table_entry); | ||
102 | pentry = (struct sfi_timer_table_entry *) sb->pentry; | ||
103 | totallen = sfi_mtimer_num * sizeof(*pentry); | ||
104 | memcpy(sfi_mtimer_array, pentry, totallen); | ||
105 | } | ||
106 | |||
107 | pr_debug("SFI MTIMER info (num = %d):\n", sfi_mtimer_num); | ||
108 | pentry = sfi_mtimer_array; | ||
109 | for (totallen = 0; totallen < sfi_mtimer_num; totallen++, pentry++) { | ||
110 | pr_debug("timer[%d]: paddr = 0x%08x, freq = %dHz," | ||
111 | " irq = %d\n", totallen, (u32)pentry->phys_addr, | ||
112 | pentry->freq_hz, pentry->irq); | ||
113 | if (!pentry->irq) | ||
114 | continue; | ||
115 | mp_irq.type = MP_INTSRC; | ||
116 | mp_irq.irqtype = mp_INT; | ||
117 | /* triggering mode edge bit 2-3, active high polarity bit 0-1 */ | ||
118 | mp_irq.irqflag = 5; | ||
119 | mp_irq.srcbus = MP_BUS_ISA; | ||
120 | mp_irq.srcbusirq = pentry->irq; /* IRQ */ | ||
121 | mp_irq.dstapic = MP_APIC_ALL; | ||
122 | mp_irq.dstirq = pentry->irq; | ||
123 | mp_save_irq(&mp_irq); | ||
124 | } | ||
125 | |||
126 | return 0; | ||
127 | } | ||
128 | |||
129 | struct sfi_timer_table_entry *sfi_get_mtmr(int hint) | ||
130 | { | ||
131 | int i; | ||
132 | if (hint < sfi_mtimer_num) { | ||
133 | if (!sfi_mtimer_usage[hint]) { | ||
134 | pr_debug("hint taken for timer %d irq %d\n", | ||
135 | hint, sfi_mtimer_array[hint].irq); | ||
136 | sfi_mtimer_usage[hint] = 1; | ||
137 | return &sfi_mtimer_array[hint]; | ||
138 | } | ||
139 | } | ||
140 | /* take the first timer available */ | ||
141 | for (i = 0; i < sfi_mtimer_num;) { | ||
142 | if (!sfi_mtimer_usage[i]) { | ||
143 | sfi_mtimer_usage[i] = 1; | ||
144 | return &sfi_mtimer_array[i]; | ||
145 | } | ||
146 | i++; | ||
147 | } | ||
148 | return NULL; | ||
149 | } | ||
150 | |||
151 | void sfi_free_mtmr(struct sfi_timer_table_entry *mtmr) | ||
152 | { | ||
153 | int i; | ||
154 | for (i = 0; i < sfi_mtimer_num;) { | ||
155 | if (mtmr->irq == sfi_mtimer_array[i].irq) { | ||
156 | sfi_mtimer_usage[i] = 0; | ||
157 | return; | ||
158 | } | ||
159 | i++; | ||
160 | } | ||
161 | } | ||
162 | |||
163 | /* parse all the mrtc info to a global mrtc array */ | ||
164 | int __init sfi_parse_mrtc(struct sfi_table_header *table) | ||
165 | { | ||
166 | struct sfi_table_simple *sb; | ||
167 | struct sfi_rtc_table_entry *pentry; | ||
168 | struct mpc_intsrc mp_irq; | ||
169 | |||
170 | int totallen; | ||
171 | |||
172 | sb = (struct sfi_table_simple *)table; | ||
173 | if (!sfi_mrtc_num) { | ||
174 | sfi_mrtc_num = SFI_GET_NUM_ENTRIES(sb, | ||
175 | struct sfi_rtc_table_entry); | ||
176 | pentry = (struct sfi_rtc_table_entry *)sb->pentry; | ||
177 | totallen = sfi_mrtc_num * sizeof(*pentry); | ||
178 | memcpy(sfi_mrtc_array, pentry, totallen); | ||
179 | } | ||
180 | |||
181 | pr_debug("SFI RTC info (num = %d):\n", sfi_mrtc_num); | ||
182 | pentry = sfi_mrtc_array; | ||
183 | for (totallen = 0; totallen < sfi_mrtc_num; totallen++, pentry++) { | ||
184 | pr_debug("RTC[%d]: paddr = 0x%08x, irq = %d\n", | ||
185 | totallen, (u32)pentry->phys_addr, pentry->irq); | ||
186 | mp_irq.type = MP_INTSRC; | ||
187 | mp_irq.irqtype = mp_INT; | ||
188 | mp_irq.irqflag = 0xf; /* level trigger and active low */ | ||
189 | mp_irq.srcbus = MP_BUS_ISA; | ||
190 | mp_irq.srcbusirq = pentry->irq; /* IRQ */ | ||
191 | mp_irq.dstapic = MP_APIC_ALL; | ||
192 | mp_irq.dstirq = pentry->irq; | ||
193 | mp_save_irq(&mp_irq); | ||
194 | } | ||
195 | return 0; | ||
196 | } | ||
197 | |||
198 | static unsigned long __init mrst_calibrate_tsc(void) | ||
199 | { | ||
200 | unsigned long fast_calibrate; | ||
201 | u32 lo, hi, ratio, fsb; | ||
202 | |||
203 | rdmsr(MSR_IA32_PERF_STATUS, lo, hi); | ||
204 | pr_debug("IA32 perf status is 0x%x, 0x%0x\n", lo, hi); | ||
205 | ratio = (hi >> 8) & 0x1f; | ||
206 | pr_debug("ratio is %d\n", ratio); | ||
207 | if (!ratio) { | ||
208 | pr_err("read a zero ratio, should be incorrect!\n"); | ||
209 | pr_err("force tsc ratio to 16 ...\n"); | ||
210 | ratio = 16; | ||
211 | } | ||
212 | rdmsr(MSR_FSB_FREQ, lo, hi); | ||
213 | if ((lo & 0x7) == 0x7) | ||
214 | fsb = PENWELL_FSB_FREQ_83SKU; | ||
215 | else | ||
216 | fsb = PENWELL_FSB_FREQ_100SKU; | ||
217 | fast_calibrate = ratio * fsb; | ||
218 | pr_debug("read penwell tsc %lu khz\n", fast_calibrate); | ||
219 | lapic_timer_frequency = fsb * 1000 / HZ; | ||
220 | /* mark tsc clocksource as reliable */ | ||
221 | set_cpu_cap(&boot_cpu_data, X86_FEATURE_TSC_RELIABLE); | ||
222 | |||
223 | if (fast_calibrate) | ||
224 | return fast_calibrate; | ||
225 | |||
226 | return 0; | ||
227 | } | ||
228 | |||
229 | static void __init mrst_time_init(void) | ||
230 | { | ||
231 | sfi_table_parse(SFI_SIG_MTMR, NULL, NULL, sfi_parse_mtmr); | ||
232 | switch (mrst_timer_options) { | ||
233 | case MRST_TIMER_APBT_ONLY: | ||
234 | break; | ||
235 | case MRST_TIMER_LAPIC_APBT: | ||
236 | x86_init.timers.setup_percpu_clockev = setup_boot_APIC_clock; | ||
237 | x86_cpuinit.setup_percpu_clockev = setup_secondary_APIC_clock; | ||
238 | break; | ||
239 | default: | ||
240 | if (!boot_cpu_has(X86_FEATURE_ARAT)) | ||
241 | break; | ||
242 | x86_init.timers.setup_percpu_clockev = setup_boot_APIC_clock; | ||
243 | x86_cpuinit.setup_percpu_clockev = setup_secondary_APIC_clock; | ||
244 | return; | ||
245 | } | ||
246 | /* we need at least one APB timer */ | ||
247 | pre_init_apic_IRQ0(); | ||
248 | apbt_time_init(); | ||
249 | } | ||
250 | |||
251 | static void mrst_arch_setup(void) | ||
252 | { | ||
253 | if (boot_cpu_data.x86 == 6 && boot_cpu_data.x86_model == 0x27) | ||
254 | __mrst_cpu_chip = MRST_CPU_CHIP_PENWELL; | ||
255 | else { | ||
256 | pr_err("Unknown Intel MID CPU (%d:%d), default to Penwell\n", | ||
257 | boot_cpu_data.x86, boot_cpu_data.x86_model); | ||
258 | __mrst_cpu_chip = MRST_CPU_CHIP_PENWELL; | ||
259 | } | ||
260 | } | ||
261 | |||
262 | /* MID systems don't have i8042 controller */ | ||
263 | static int mrst_i8042_detect(void) | ||
264 | { | ||
265 | return 0; | ||
266 | } | ||
267 | |||
268 | /* | ||
269 | * Moorestown does not have external NMI source nor port 0x61 to report | ||
270 | * NMI status. The possible NMI sources are from pmu as a result of NMI | ||
271 | * watchdog or lock debug. Reading io port 0x61 results in 0xff which | ||
272 | * misled NMI handler. | ||
273 | */ | ||
274 | static unsigned char mrst_get_nmi_reason(void) | ||
275 | { | ||
276 | return 0; | ||
277 | } | ||
278 | |||
279 | /* | ||
280 | * Moorestown specific x86_init function overrides and early setup | ||
281 | * calls. | ||
282 | */ | ||
283 | void __init x86_mrst_early_setup(void) | ||
284 | { | ||
285 | x86_init.resources.probe_roms = x86_init_noop; | ||
286 | x86_init.resources.reserve_resources = x86_init_noop; | ||
287 | |||
288 | x86_init.timers.timer_init = mrst_time_init; | ||
289 | x86_init.timers.setup_percpu_clockev = x86_init_noop; | ||
290 | |||
291 | x86_init.irqs.pre_vector_init = x86_init_noop; | ||
292 | |||
293 | x86_init.oem.arch_setup = mrst_arch_setup; | ||
294 | |||
295 | x86_cpuinit.setup_percpu_clockev = apbt_setup_secondary_clock; | ||
296 | |||
297 | x86_platform.calibrate_tsc = mrst_calibrate_tsc; | ||
298 | x86_platform.i8042_detect = mrst_i8042_detect; | ||
299 | x86_init.timers.wallclock_init = mrst_rtc_init; | ||
300 | x86_platform.get_nmi_reason = mrst_get_nmi_reason; | ||
301 | |||
302 | x86_init.pci.init = pci_mrst_init; | ||
303 | x86_init.pci.fixup_irqs = x86_init_noop; | ||
304 | |||
305 | legacy_pic = &null_legacy_pic; | ||
306 | |||
307 | /* Moorestown specific power_off/restart method */ | ||
308 | pm_power_off = mrst_power_off; | ||
309 | machine_ops.emergency_restart = mrst_reboot; | ||
310 | |||
311 | /* Avoid searching for BIOS MP tables */ | ||
312 | x86_init.mpparse.find_smp_config = x86_init_noop; | ||
313 | x86_init.mpparse.get_smp_config = x86_init_uint_noop; | ||
314 | set_bit(MP_BUS_ISA, mp_bus_not_pci); | ||
315 | } | ||
316 | |||
317 | /* | ||
318 | * if user does not want to use per CPU apb timer, just give it a lower rating | ||
319 | * than local apic timer and skip the late per cpu timer init. | ||
320 | */ | ||
321 | static inline int __init setup_x86_mrst_timer(char *arg) | ||
322 | { | ||
323 | if (!arg) | ||
324 | return -EINVAL; | ||
325 | |||
326 | if (strcmp("apbt_only", arg) == 0) | ||
327 | mrst_timer_options = MRST_TIMER_APBT_ONLY; | ||
328 | else if (strcmp("lapic_and_apbt", arg) == 0) | ||
329 | mrst_timer_options = MRST_TIMER_LAPIC_APBT; | ||
330 | else { | ||
331 | pr_warn("X86 MRST timer option %s not recognised" | ||
332 | " use x86_mrst_timer=apbt_only or lapic_and_apbt\n", | ||
333 | arg); | ||
334 | return -EINVAL; | ||
335 | } | ||
336 | return 0; | ||
337 | } | ||
338 | __setup("x86_mrst_timer=", setup_x86_mrst_timer); | ||
339 | |||
340 | /* | ||
341 | * Parsing GPIO table first, since the DEVS table will need this table | ||
342 | * to map the pin name to the actual pin. | ||
343 | */ | ||
344 | static struct sfi_gpio_table_entry *gpio_table; | ||
345 | static int gpio_num_entry; | ||
346 | |||
347 | static int __init sfi_parse_gpio(struct sfi_table_header *table) | ||
348 | { | ||
349 | struct sfi_table_simple *sb; | ||
350 | struct sfi_gpio_table_entry *pentry; | ||
351 | int num, i; | ||
352 | |||
353 | if (gpio_table) | ||
354 | return 0; | ||
355 | sb = (struct sfi_table_simple *)table; | ||
356 | num = SFI_GET_NUM_ENTRIES(sb, struct sfi_gpio_table_entry); | ||
357 | pentry = (struct sfi_gpio_table_entry *)sb->pentry; | ||
358 | |||
359 | gpio_table = kmalloc(num * sizeof(*pentry), GFP_KERNEL); | ||
360 | if (!gpio_table) | ||
361 | return -1; | ||
362 | memcpy(gpio_table, pentry, num * sizeof(*pentry)); | ||
363 | gpio_num_entry = num; | ||
364 | |||
365 | pr_debug("GPIO pin info:\n"); | ||
366 | for (i = 0; i < num; i++, pentry++) | ||
367 | pr_debug("info[%2d]: controller = %16.16s, pin_name = %16.16s," | ||
368 | " pin = %d\n", i, | ||
369 | pentry->controller_name, | ||
370 | pentry->pin_name, | ||
371 | pentry->pin_no); | ||
372 | return 0; | ||
373 | } | ||
374 | |||
375 | static int get_gpio_by_name(const char *name) | ||
376 | { | ||
377 | struct sfi_gpio_table_entry *pentry = gpio_table; | ||
378 | int i; | ||
379 | |||
380 | if (!pentry) | ||
381 | return -1; | ||
382 | for (i = 0; i < gpio_num_entry; i++, pentry++) { | ||
383 | if (!strncmp(name, pentry->pin_name, SFI_NAME_LEN)) | ||
384 | return pentry->pin_no; | ||
385 | } | ||
386 | return -1; | ||
387 | } | ||
388 | |||
389 | /* | ||
390 | * Here defines the array of devices platform data that IAFW would export | ||
391 | * through SFI "DEVS" table, we use name and type to match the device and | ||
392 | * its platform data. | ||
393 | */ | ||
394 | struct devs_id { | ||
395 | char name[SFI_NAME_LEN + 1]; | ||
396 | u8 type; | ||
397 | u8 delay; | ||
398 | void *(*get_platform_data)(void *info); | ||
399 | }; | ||
400 | |||
401 | /* the offset for the mapping of global gpio pin to irq */ | ||
402 | #define MRST_IRQ_OFFSET 0x100 | ||
403 | |||
404 | static void __init *pmic_gpio_platform_data(void *info) | ||
405 | { | ||
406 | static struct intel_pmic_gpio_platform_data pmic_gpio_pdata; | ||
407 | int gpio_base = get_gpio_by_name("pmic_gpio_base"); | ||
408 | |||
409 | if (gpio_base == -1) | ||
410 | gpio_base = 64; | ||
411 | pmic_gpio_pdata.gpio_base = gpio_base; | ||
412 | pmic_gpio_pdata.irq_base = gpio_base + MRST_IRQ_OFFSET; | ||
413 | pmic_gpio_pdata.gpiointr = 0xffffeff8; | ||
414 | |||
415 | return &pmic_gpio_pdata; | ||
416 | } | ||
417 | |||
418 | static void __init *max3111_platform_data(void *info) | ||
419 | { | ||
420 | struct spi_board_info *spi_info = info; | ||
421 | int intr = get_gpio_by_name("max3111_int"); | ||
422 | |||
423 | spi_info->mode = SPI_MODE_0; | ||
424 | if (intr == -1) | ||
425 | return NULL; | ||
426 | spi_info->irq = intr + MRST_IRQ_OFFSET; | ||
427 | return NULL; | ||
428 | } | ||
429 | |||
430 | /* we have multiple max7315 on the board ... */ | ||
431 | #define MAX7315_NUM 2 | ||
432 | static void __init *max7315_platform_data(void *info) | ||
433 | { | ||
434 | static struct pca953x_platform_data max7315_pdata[MAX7315_NUM]; | ||
435 | static int nr; | ||
436 | struct pca953x_platform_data *max7315 = &max7315_pdata[nr]; | ||
437 | struct i2c_board_info *i2c_info = info; | ||
438 | int gpio_base, intr; | ||
439 | char base_pin_name[SFI_NAME_LEN + 1]; | ||
440 | char intr_pin_name[SFI_NAME_LEN + 1]; | ||
441 | |||
442 | if (nr == MAX7315_NUM) { | ||
443 | pr_err("too many max7315s, we only support %d\n", | ||
444 | MAX7315_NUM); | ||
445 | return NULL; | ||
446 | } | ||
447 | /* we have several max7315 on the board, we only need load several | ||
448 | * instances of the same pca953x driver to cover them | ||
449 | */ | ||
450 | strcpy(i2c_info->type, "max7315"); | ||
451 | if (nr++) { | ||
452 | sprintf(base_pin_name, "max7315_%d_base", nr); | ||
453 | sprintf(intr_pin_name, "max7315_%d_int", nr); | ||
454 | } else { | ||
455 | strcpy(base_pin_name, "max7315_base"); | ||
456 | strcpy(intr_pin_name, "max7315_int"); | ||
457 | } | ||
458 | |||
459 | gpio_base = get_gpio_by_name(base_pin_name); | ||
460 | intr = get_gpio_by_name(intr_pin_name); | ||
461 | |||
462 | if (gpio_base == -1) | ||
463 | return NULL; | ||
464 | max7315->gpio_base = gpio_base; | ||
465 | if (intr != -1) { | ||
466 | i2c_info->irq = intr + MRST_IRQ_OFFSET; | ||
467 | max7315->irq_base = gpio_base + MRST_IRQ_OFFSET; | ||
468 | } else { | ||
469 | i2c_info->irq = -1; | ||
470 | max7315->irq_base = -1; | ||
471 | } | ||
472 | return max7315; | ||
473 | } | ||
474 | |||
475 | static void *tca6416_platform_data(void *info) | ||
476 | { | ||
477 | static struct pca953x_platform_data tca6416; | ||
478 | struct i2c_board_info *i2c_info = info; | ||
479 | int gpio_base, intr; | ||
480 | char base_pin_name[SFI_NAME_LEN + 1]; | ||
481 | char intr_pin_name[SFI_NAME_LEN + 1]; | ||
482 | |||
483 | strcpy(i2c_info->type, "tca6416"); | ||
484 | strcpy(base_pin_name, "tca6416_base"); | ||
485 | strcpy(intr_pin_name, "tca6416_int"); | ||
486 | |||
487 | gpio_base = get_gpio_by_name(base_pin_name); | ||
488 | intr = get_gpio_by_name(intr_pin_name); | ||
489 | |||
490 | if (gpio_base == -1) | ||
491 | return NULL; | ||
492 | tca6416.gpio_base = gpio_base; | ||
493 | if (intr != -1) { | ||
494 | i2c_info->irq = intr + MRST_IRQ_OFFSET; | ||
495 | tca6416.irq_base = gpio_base + MRST_IRQ_OFFSET; | ||
496 | } else { | ||
497 | i2c_info->irq = -1; | ||
498 | tca6416.irq_base = -1; | ||
499 | } | ||
500 | return &tca6416; | ||
501 | } | ||
502 | |||
503 | static void *mpu3050_platform_data(void *info) | ||
504 | { | ||
505 | struct i2c_board_info *i2c_info = info; | ||
506 | int intr = get_gpio_by_name("mpu3050_int"); | ||
507 | |||
508 | if (intr == -1) | ||
509 | return NULL; | ||
510 | |||
511 | i2c_info->irq = intr + MRST_IRQ_OFFSET; | ||
512 | return NULL; | ||
513 | } | ||
514 | |||
515 | static void __init *emc1403_platform_data(void *info) | ||
516 | { | ||
517 | static short intr2nd_pdata; | ||
518 | struct i2c_board_info *i2c_info = info; | ||
519 | int intr = get_gpio_by_name("thermal_int"); | ||
520 | int intr2nd = get_gpio_by_name("thermal_alert"); | ||
521 | |||
522 | if (intr == -1 || intr2nd == -1) | ||
523 | return NULL; | ||
524 | |||
525 | i2c_info->irq = intr + MRST_IRQ_OFFSET; | ||
526 | intr2nd_pdata = intr2nd + MRST_IRQ_OFFSET; | ||
527 | |||
528 | return &intr2nd_pdata; | ||
529 | } | ||
530 | |||
531 | static void __init *lis331dl_platform_data(void *info) | ||
532 | { | ||
533 | static short intr2nd_pdata; | ||
534 | struct i2c_board_info *i2c_info = info; | ||
535 | int intr = get_gpio_by_name("accel_int"); | ||
536 | int intr2nd = get_gpio_by_name("accel_2"); | ||
537 | |||
538 | if (intr == -1 || intr2nd == -1) | ||
539 | return NULL; | ||
540 | |||
541 | i2c_info->irq = intr + MRST_IRQ_OFFSET; | ||
542 | intr2nd_pdata = intr2nd + MRST_IRQ_OFFSET; | ||
543 | |||
544 | return &intr2nd_pdata; | ||
545 | } | ||
546 | |||
547 | static void __init *no_platform_data(void *info) | ||
548 | { | ||
549 | return NULL; | ||
550 | } | ||
551 | |||
552 | static struct resource msic_resources[] = { | ||
553 | { | ||
554 | .start = INTEL_MSIC_IRQ_PHYS_BASE, | ||
555 | .end = INTEL_MSIC_IRQ_PHYS_BASE + 64 - 1, | ||
556 | .flags = IORESOURCE_MEM, | ||
557 | }, | ||
558 | }; | ||
559 | |||
560 | static struct intel_msic_platform_data msic_pdata; | ||
561 | |||
562 | static struct platform_device msic_device = { | ||
563 | .name = "intel_msic", | ||
564 | .id = -1, | ||
565 | .dev = { | ||
566 | .platform_data = &msic_pdata, | ||
567 | }, | ||
568 | .num_resources = ARRAY_SIZE(msic_resources), | ||
569 | .resource = msic_resources, | ||
570 | }; | ||
571 | |||
572 | static inline bool mrst_has_msic(void) | ||
573 | { | ||
574 | return mrst_identify_cpu() == MRST_CPU_CHIP_PENWELL; | ||
575 | } | ||
576 | |||
577 | static int msic_scu_status_change(struct notifier_block *nb, | ||
578 | unsigned long code, void *data) | ||
579 | { | ||
580 | if (code == SCU_DOWN) { | ||
581 | platform_device_unregister(&msic_device); | ||
582 | return 0; | ||
583 | } | ||
584 | |||
585 | return platform_device_register(&msic_device); | ||
586 | } | ||
587 | |||
588 | static int __init msic_init(void) | ||
589 | { | ||
590 | static struct notifier_block msic_scu_notifier = { | ||
591 | .notifier_call = msic_scu_status_change, | ||
592 | }; | ||
593 | |||
594 | /* | ||
595 | * We need to be sure that the SCU IPC is ready before MSIC device | ||
596 | * can be registered. | ||
597 | */ | ||
598 | if (mrst_has_msic()) | ||
599 | intel_scu_notifier_add(&msic_scu_notifier); | ||
600 | |||
601 | return 0; | ||
602 | } | ||
603 | arch_initcall(msic_init); | ||
604 | |||
605 | /* | ||
606 | * msic_generic_platform_data - sets generic platform data for the block | ||
607 | * @info: pointer to the SFI device table entry for this block | ||
608 | * @block: MSIC block | ||
609 | * | ||
610 | * Function sets IRQ number from the SFI table entry for given device to | ||
611 | * the MSIC platform data. | ||
612 | */ | ||
613 | static void *msic_generic_platform_data(void *info, enum intel_msic_block block) | ||
614 | { | ||
615 | struct sfi_device_table_entry *entry = info; | ||
616 | |||
617 | BUG_ON(block < 0 || block >= INTEL_MSIC_BLOCK_LAST); | ||
618 | msic_pdata.irq[block] = entry->irq; | ||
619 | |||
620 | return no_platform_data(info); | ||
621 | } | ||
622 | |||
623 | static void *msic_battery_platform_data(void *info) | ||
624 | { | ||
625 | return msic_generic_platform_data(info, INTEL_MSIC_BLOCK_BATTERY); | ||
626 | } | ||
627 | |||
628 | static void *msic_gpio_platform_data(void *info) | ||
629 | { | ||
630 | static struct intel_msic_gpio_pdata pdata; | ||
631 | int gpio = get_gpio_by_name("msic_gpio_base"); | ||
632 | |||
633 | if (gpio < 0) | ||
634 | return NULL; | ||
635 | |||
636 | pdata.gpio_base = gpio; | ||
637 | msic_pdata.gpio = &pdata; | ||
638 | |||
639 | return msic_generic_platform_data(info, INTEL_MSIC_BLOCK_GPIO); | ||
640 | } | ||
641 | |||
642 | static void *msic_audio_platform_data(void *info) | ||
643 | { | ||
644 | struct platform_device *pdev; | ||
645 | |||
646 | pdev = platform_device_register_simple("sst-platform", -1, NULL, 0); | ||
647 | if (IS_ERR(pdev)) { | ||
648 | pr_err("failed to create audio platform device\n"); | ||
649 | return NULL; | ||
650 | } | ||
651 | |||
652 | return msic_generic_platform_data(info, INTEL_MSIC_BLOCK_AUDIO); | ||
653 | } | ||
654 | |||
655 | static void *msic_power_btn_platform_data(void *info) | ||
656 | { | ||
657 | return msic_generic_platform_data(info, INTEL_MSIC_BLOCK_POWER_BTN); | ||
658 | } | ||
659 | |||
660 | static void *msic_ocd_platform_data(void *info) | ||
661 | { | ||
662 | static struct intel_msic_ocd_pdata pdata; | ||
663 | int gpio = get_gpio_by_name("ocd_gpio"); | ||
664 | |||
665 | if (gpio < 0) | ||
666 | return NULL; | ||
667 | |||
668 | pdata.gpio = gpio; | ||
669 | msic_pdata.ocd = &pdata; | ||
670 | |||
671 | return msic_generic_platform_data(info, INTEL_MSIC_BLOCK_OCD); | ||
672 | } | ||
673 | |||
674 | static void *msic_thermal_platform_data(void *info) | ||
675 | { | ||
676 | return msic_generic_platform_data(info, INTEL_MSIC_BLOCK_THERMAL); | ||
677 | } | ||
678 | |||
679 | /* tc35876x DSI-LVDS bridge chip and panel platform data */ | ||
680 | static void *tc35876x_platform_data(void *data) | ||
681 | { | ||
682 | static struct tc35876x_platform_data pdata; | ||
683 | |||
684 | /* gpio pins set to -1 will not be used by the driver */ | ||
685 | pdata.gpio_bridge_reset = get_gpio_by_name("LCMB_RXEN"); | ||
686 | pdata.gpio_panel_bl_en = get_gpio_by_name("6S6P_BL_EN"); | ||
687 | pdata.gpio_panel_vadd = get_gpio_by_name("EN_VREG_LCD_V3P3"); | ||
688 | |||
689 | return &pdata; | ||
690 | } | ||
691 | |||
692 | static const struct devs_id __initconst device_ids[] = { | ||
693 | {"bma023", SFI_DEV_TYPE_I2C, 1, &no_platform_data}, | ||
694 | {"pmic_gpio", SFI_DEV_TYPE_SPI, 1, &pmic_gpio_platform_data}, | ||
695 | {"pmic_gpio", SFI_DEV_TYPE_IPC, 1, &pmic_gpio_platform_data}, | ||
696 | {"spi_max3111", SFI_DEV_TYPE_SPI, 0, &max3111_platform_data}, | ||
697 | {"i2c_max7315", SFI_DEV_TYPE_I2C, 1, &max7315_platform_data}, | ||
698 | {"i2c_max7315_2", SFI_DEV_TYPE_I2C, 1, &max7315_platform_data}, | ||
699 | {"tca6416", SFI_DEV_TYPE_I2C, 1, &tca6416_platform_data}, | ||
700 | {"emc1403", SFI_DEV_TYPE_I2C, 1, &emc1403_platform_data}, | ||
701 | {"i2c_accel", SFI_DEV_TYPE_I2C, 0, &lis331dl_platform_data}, | ||
702 | {"pmic_audio", SFI_DEV_TYPE_IPC, 1, &no_platform_data}, | ||
703 | {"mpu3050", SFI_DEV_TYPE_I2C, 1, &mpu3050_platform_data}, | ||
704 | {"i2c_disp_brig", SFI_DEV_TYPE_I2C, 0, &tc35876x_platform_data}, | ||
705 | |||
706 | /* MSIC subdevices */ | ||
707 | {"msic_battery", SFI_DEV_TYPE_IPC, 1, &msic_battery_platform_data}, | ||
708 | {"msic_gpio", SFI_DEV_TYPE_IPC, 1, &msic_gpio_platform_data}, | ||
709 | {"msic_audio", SFI_DEV_TYPE_IPC, 1, &msic_audio_platform_data}, | ||
710 | {"msic_power_btn", SFI_DEV_TYPE_IPC, 1, &msic_power_btn_platform_data}, | ||
711 | {"msic_ocd", SFI_DEV_TYPE_IPC, 1, &msic_ocd_platform_data}, | ||
712 | {"msic_thermal", SFI_DEV_TYPE_IPC, 1, &msic_thermal_platform_data}, | ||
713 | |||
714 | {}, | ||
715 | }; | ||
716 | |||
717 | #define MAX_IPCDEVS 24 | ||
718 | static struct platform_device *ipc_devs[MAX_IPCDEVS]; | ||
719 | static int ipc_next_dev; | ||
720 | |||
721 | #define MAX_SCU_SPI 24 | ||
722 | static struct spi_board_info *spi_devs[MAX_SCU_SPI]; | ||
723 | static int spi_next_dev; | ||
724 | |||
725 | #define MAX_SCU_I2C 24 | ||
726 | static struct i2c_board_info *i2c_devs[MAX_SCU_I2C]; | ||
727 | static int i2c_bus[MAX_SCU_I2C]; | ||
728 | static int i2c_next_dev; | ||
729 | |||
730 | static void __init intel_scu_device_register(struct platform_device *pdev) | ||
731 | { | ||
732 | if (ipc_next_dev == MAX_IPCDEVS) | ||
733 | pr_err("too many SCU IPC devices"); | ||
734 | else | ||
735 | ipc_devs[ipc_next_dev++] = pdev; | ||
736 | } | ||
737 | |||
738 | static void __init intel_scu_spi_device_register(struct spi_board_info *sdev) | ||
739 | { | ||
740 | struct spi_board_info *new_dev; | ||
741 | |||
742 | if (spi_next_dev == MAX_SCU_SPI) { | ||
743 | pr_err("too many SCU SPI devices"); | ||
744 | return; | ||
745 | } | ||
746 | |||
747 | new_dev = kzalloc(sizeof(*sdev), GFP_KERNEL); | ||
748 | if (!new_dev) { | ||
749 | pr_err("failed to alloc mem for delayed spi dev %s\n", | ||
750 | sdev->modalias); | ||
751 | return; | ||
752 | } | ||
753 | memcpy(new_dev, sdev, sizeof(*sdev)); | ||
754 | |||
755 | spi_devs[spi_next_dev++] = new_dev; | ||
756 | } | ||
757 | |||
758 | static void __init intel_scu_i2c_device_register(int bus, | ||
759 | struct i2c_board_info *idev) | ||
760 | { | ||
761 | struct i2c_board_info *new_dev; | ||
762 | |||
763 | if (i2c_next_dev == MAX_SCU_I2C) { | ||
764 | pr_err("too many SCU I2C devices"); | ||
765 | return; | ||
766 | } | ||
767 | |||
768 | new_dev = kzalloc(sizeof(*idev), GFP_KERNEL); | ||
769 | if (!new_dev) { | ||
770 | pr_err("failed to alloc mem for delayed i2c dev %s\n", | ||
771 | idev->type); | ||
772 | return; | ||
773 | } | ||
774 | memcpy(new_dev, idev, sizeof(*idev)); | ||
775 | |||
776 | i2c_bus[i2c_next_dev] = bus; | ||
777 | i2c_devs[i2c_next_dev++] = new_dev; | ||
778 | } | ||
779 | |||
780 | BLOCKING_NOTIFIER_HEAD(intel_scu_notifier); | ||
781 | EXPORT_SYMBOL_GPL(intel_scu_notifier); | ||
782 | |||
783 | /* Called by IPC driver */ | ||
784 | void intel_scu_devices_create(void) | ||
785 | { | ||
786 | int i; | ||
787 | |||
788 | for (i = 0; i < ipc_next_dev; i++) | ||
789 | platform_device_add(ipc_devs[i]); | ||
790 | |||
791 | for (i = 0; i < spi_next_dev; i++) | ||
792 | spi_register_board_info(spi_devs[i], 1); | ||
793 | |||
794 | for (i = 0; i < i2c_next_dev; i++) { | ||
795 | struct i2c_adapter *adapter; | ||
796 | struct i2c_client *client; | ||
797 | |||
798 | adapter = i2c_get_adapter(i2c_bus[i]); | ||
799 | if (adapter) { | ||
800 | client = i2c_new_device(adapter, i2c_devs[i]); | ||
801 | if (!client) | ||
802 | pr_err("can't create i2c device %s\n", | ||
803 | i2c_devs[i]->type); | ||
804 | } else | ||
805 | i2c_register_board_info(i2c_bus[i], i2c_devs[i], 1); | ||
806 | } | ||
807 | intel_scu_notifier_post(SCU_AVAILABLE, NULL); | ||
808 | } | ||
809 | EXPORT_SYMBOL_GPL(intel_scu_devices_create); | ||
810 | |||
811 | /* Called by IPC driver */ | ||
812 | void intel_scu_devices_destroy(void) | ||
813 | { | ||
814 | int i; | ||
815 | |||
816 | intel_scu_notifier_post(SCU_DOWN, NULL); | ||
817 | |||
818 | for (i = 0; i < ipc_next_dev; i++) | ||
819 | platform_device_del(ipc_devs[i]); | ||
820 | } | ||
821 | EXPORT_SYMBOL_GPL(intel_scu_devices_destroy); | ||
822 | |||
823 | static void __init install_irq_resource(struct platform_device *pdev, int irq) | ||
824 | { | ||
825 | /* Single threaded */ | ||
826 | static struct resource __initdata res = { | ||
827 | .name = "IRQ", | ||
828 | .flags = IORESOURCE_IRQ, | ||
829 | }; | ||
830 | res.start = irq; | ||
831 | platform_device_add_resources(pdev, &res, 1); | ||
832 | } | ||
833 | |||
834 | static void __init sfi_handle_ipc_dev(struct sfi_device_table_entry *entry) | ||
835 | { | ||
836 | const struct devs_id *dev = device_ids; | ||
837 | struct platform_device *pdev; | ||
838 | void *pdata = NULL; | ||
839 | |||
840 | while (dev->name[0]) { | ||
841 | if (dev->type == SFI_DEV_TYPE_IPC && | ||
842 | !strncmp(dev->name, entry->name, SFI_NAME_LEN)) { | ||
843 | pdata = dev->get_platform_data(entry); | ||
844 | break; | ||
845 | } | ||
846 | dev++; | ||
847 | } | ||
848 | |||
849 | /* | ||
850 | * On Medfield the platform device creation is handled by the MSIC | ||
851 | * MFD driver so we don't need to do it here. | ||
852 | */ | ||
853 | if (mrst_has_msic()) | ||
854 | return; | ||
855 | |||
856 | pdev = platform_device_alloc(entry->name, 0); | ||
857 | if (pdev == NULL) { | ||
858 | pr_err("out of memory for SFI platform device '%s'.\n", | ||
859 | entry->name); | ||
860 | return; | ||
861 | } | ||
862 | install_irq_resource(pdev, entry->irq); | ||
863 | |||
864 | pdev->dev.platform_data = pdata; | ||
865 | intel_scu_device_register(pdev); | ||
866 | } | ||
867 | |||
868 | static void __init sfi_handle_spi_dev(struct spi_board_info *spi_info) | ||
869 | { | ||
870 | const struct devs_id *dev = device_ids; | ||
871 | void *pdata = NULL; | ||
872 | |||
873 | while (dev->name[0]) { | ||
874 | if (dev->type == SFI_DEV_TYPE_SPI && | ||
875 | !strncmp(dev->name, spi_info->modalias, | ||
876 | SFI_NAME_LEN)) { | ||
877 | pdata = dev->get_platform_data(spi_info); | ||
878 | break; | ||
879 | } | ||
880 | dev++; | ||
881 | } | ||
882 | spi_info->platform_data = pdata; | ||
883 | if (dev->delay) | ||
884 | intel_scu_spi_device_register(spi_info); | ||
885 | else | ||
886 | spi_register_board_info(spi_info, 1); | ||
887 | } | ||
888 | |||
889 | static void __init sfi_handle_i2c_dev(int bus, struct i2c_board_info *i2c_info) | ||
890 | { | ||
891 | const struct devs_id *dev = device_ids; | ||
892 | void *pdata = NULL; | ||
893 | |||
894 | while (dev->name[0]) { | ||
895 | if (dev->type == SFI_DEV_TYPE_I2C && | ||
896 | !strncmp(dev->name, i2c_info->type, SFI_NAME_LEN)) { | ||
897 | pdata = dev->get_platform_data(i2c_info); | ||
898 | break; | ||
899 | } | ||
900 | dev++; | ||
901 | } | ||
902 | i2c_info->platform_data = pdata; | ||
903 | |||
904 | if (dev->delay) | ||
905 | intel_scu_i2c_device_register(bus, i2c_info); | ||
906 | else | ||
907 | i2c_register_board_info(bus, i2c_info, 1); | ||
908 | } | ||
909 | |||
910 | |||
911 | static int __init sfi_parse_devs(struct sfi_table_header *table) | ||
912 | { | ||
913 | struct sfi_table_simple *sb; | ||
914 | struct sfi_device_table_entry *pentry; | ||
915 | struct spi_board_info spi_info; | ||
916 | struct i2c_board_info i2c_info; | ||
917 | int num, i, bus; | ||
918 | int ioapic; | ||
919 | struct io_apic_irq_attr irq_attr; | ||
920 | |||
921 | sb = (struct sfi_table_simple *)table; | ||
922 | num = SFI_GET_NUM_ENTRIES(sb, struct sfi_device_table_entry); | ||
923 | pentry = (struct sfi_device_table_entry *)sb->pentry; | ||
924 | |||
925 | for (i = 0; i < num; i++, pentry++) { | ||
926 | int irq = pentry->irq; | ||
927 | |||
928 | if (irq != (u8)0xff) { /* native RTE case */ | ||
929 | /* these SPI2 devices are not exposed to system as PCI | ||
930 | * devices, but they have separate RTE entry in IOAPIC | ||
931 | * so we have to enable them one by one here | ||
932 | */ | ||
933 | ioapic = mp_find_ioapic(irq); | ||
934 | irq_attr.ioapic = ioapic; | ||
935 | irq_attr.ioapic_pin = irq; | ||
936 | irq_attr.trigger = 1; | ||
937 | irq_attr.polarity = 1; | ||
938 | io_apic_set_pci_routing(NULL, irq, &irq_attr); | ||
939 | } else | ||
940 | irq = 0; /* No irq */ | ||
941 | |||
942 | switch (pentry->type) { | ||
943 | case SFI_DEV_TYPE_IPC: | ||
944 | pr_debug("info[%2d]: IPC bus, name = %16.16s, " | ||
945 | "irq = 0x%2x\n", i, pentry->name, pentry->irq); | ||
946 | sfi_handle_ipc_dev(pentry); | ||
947 | break; | ||
948 | case SFI_DEV_TYPE_SPI: | ||
949 | memset(&spi_info, 0, sizeof(spi_info)); | ||
950 | strncpy(spi_info.modalias, pentry->name, SFI_NAME_LEN); | ||
951 | spi_info.irq = irq; | ||
952 | spi_info.bus_num = pentry->host_num; | ||
953 | spi_info.chip_select = pentry->addr; | ||
954 | spi_info.max_speed_hz = pentry->max_freq; | ||
955 | pr_debug("info[%2d]: SPI bus = %d, name = %16.16s, " | ||
956 | "irq = 0x%2x, max_freq = %d, cs = %d\n", i, | ||
957 | spi_info.bus_num, | ||
958 | spi_info.modalias, | ||
959 | spi_info.irq, | ||
960 | spi_info.max_speed_hz, | ||
961 | spi_info.chip_select); | ||
962 | sfi_handle_spi_dev(&spi_info); | ||
963 | break; | ||
964 | case SFI_DEV_TYPE_I2C: | ||
965 | memset(&i2c_info, 0, sizeof(i2c_info)); | ||
966 | bus = pentry->host_num; | ||
967 | strncpy(i2c_info.type, pentry->name, SFI_NAME_LEN); | ||
968 | i2c_info.irq = irq; | ||
969 | i2c_info.addr = pentry->addr; | ||
970 | pr_debug("info[%2d]: I2C bus = %d, name = %16.16s, " | ||
971 | "irq = 0x%2x, addr = 0x%x\n", i, bus, | ||
972 | i2c_info.type, | ||
973 | i2c_info.irq, | ||
974 | i2c_info.addr); | ||
975 | sfi_handle_i2c_dev(bus, &i2c_info); | ||
976 | break; | ||
977 | case SFI_DEV_TYPE_UART: | ||
978 | case SFI_DEV_TYPE_HSI: | ||
979 | default: | ||
980 | ; | ||
981 | } | ||
982 | } | ||
983 | return 0; | ||
984 | } | ||
985 | |||
986 | static int __init mrst_platform_init(void) | ||
987 | { | ||
988 | sfi_table_parse(SFI_SIG_GPIO, NULL, NULL, sfi_parse_gpio); | ||
989 | sfi_table_parse(SFI_SIG_DEVS, NULL, NULL, sfi_parse_devs); | ||
990 | return 0; | ||
991 | } | ||
992 | arch_initcall(mrst_platform_init); | ||
993 | |||
994 | /* | ||
995 | * we will search these buttons in SFI GPIO table (by name) | ||
996 | * and register them dynamically. Please add all possible | ||
997 | * buttons here, we will shrink them if no GPIO found. | ||
998 | */ | ||
999 | static struct gpio_keys_button gpio_button[] = { | ||
1000 | {KEY_POWER, -1, 1, "power_btn", EV_KEY, 0, 3000}, | ||
1001 | {KEY_PROG1, -1, 1, "prog_btn1", EV_KEY, 0, 20}, | ||
1002 | {KEY_PROG2, -1, 1, "prog_btn2", EV_KEY, 0, 20}, | ||
1003 | {SW_LID, -1, 1, "lid_switch", EV_SW, 0, 20}, | ||
1004 | {KEY_VOLUMEUP, -1, 1, "vol_up", EV_KEY, 0, 20}, | ||
1005 | {KEY_VOLUMEDOWN, -1, 1, "vol_down", EV_KEY, 0, 20}, | ||
1006 | {KEY_CAMERA, -1, 1, "camera_full", EV_KEY, 0, 20}, | ||
1007 | {KEY_CAMERA_FOCUS, -1, 1, "camera_half", EV_KEY, 0, 20}, | ||
1008 | {SW_KEYPAD_SLIDE, -1, 1, "MagSw1", EV_SW, 0, 20}, | ||
1009 | {SW_KEYPAD_SLIDE, -1, 1, "MagSw2", EV_SW, 0, 20}, | ||
1010 | }; | ||
1011 | |||
1012 | static struct gpio_keys_platform_data mrst_gpio_keys = { | ||
1013 | .buttons = gpio_button, | ||
1014 | .rep = 1, | ||
1015 | .nbuttons = -1, /* will fill it after search */ | ||
1016 | }; | ||
1017 | |||
1018 | static struct platform_device pb_device = { | ||
1019 | .name = "gpio-keys", | ||
1020 | .id = -1, | ||
1021 | .dev = { | ||
1022 | .platform_data = &mrst_gpio_keys, | ||
1023 | }, | ||
1024 | }; | ||
1025 | |||
1026 | /* | ||
1027 | * Shrink the non-existent buttons, register the gpio button | ||
1028 | * device if there is some | ||
1029 | */ | ||
1030 | static int __init pb_keys_init(void) | ||
1031 | { | ||
1032 | struct gpio_keys_button *gb = gpio_button; | ||
1033 | int i, num, good = 0; | ||
1034 | |||
1035 | num = sizeof(gpio_button) / sizeof(struct gpio_keys_button); | ||
1036 | for (i = 0; i < num; i++) { | ||
1037 | gb[i].gpio = get_gpio_by_name(gb[i].desc); | ||
1038 | pr_debug("info[%2d]: name = %s, gpio = %d\n", i, gb[i].desc, | ||
1039 | gb[i].gpio); | ||
1040 | if (gb[i].gpio == -1) | ||
1041 | continue; | ||
1042 | |||
1043 | if (i != good) | ||
1044 | gb[good] = gb[i]; | ||
1045 | good++; | ||
1046 | } | ||
1047 | |||
1048 | if (good) { | ||
1049 | mrst_gpio_keys.nbuttons = good; | ||
1050 | return platform_device_register(&pb_device); | ||
1051 | } | ||
1052 | return 0; | ||
1053 | } | ||
1054 | late_initcall(pb_keys_init); | ||
1055 | 1 | /* |
arch/x86/platform/mrst/vrtc.c
1 | /* | File was deleted | |
2 | * vrtc.c: Driver for virtual RTC device on Intel MID platform | ||
3 | * | ||
4 | * (C) Copyright 2009 Intel Corporation | ||
5 | * | ||
6 | * This program is free software; you can redistribute it and/or | ||
7 | * modify it under the terms of the GNU General Public License | ||
8 | * as published by the Free Software Foundation; version 2 | ||
9 | * of the License. | ||
10 | * | ||
11 | * Note: | ||
12 | * VRTC is emulated by system controller firmware, the real HW | ||
13 | * RTC is located in the PMIC device. SCU FW shadows PMIC RTC | ||
14 | * in a memory mapped IO space that is visible to the host IA | ||
15 | * processor. | ||
16 | * | ||
17 | * This driver is based on RTC CMOS driver. | ||
18 | */ | ||
19 | |||
20 | #include <linux/kernel.h> | ||
21 | #include <linux/export.h> | ||
22 | #include <linux/init.h> | ||
23 | #include <linux/sfi.h> | ||
24 | #include <linux/platform_device.h> | ||
25 | |||
26 | #include <asm/mrst.h> | ||
27 | #include <asm/mrst-vrtc.h> | ||
28 | #include <asm/time.h> | ||
29 | #include <asm/fixmap.h> | ||
30 | |||
31 | static unsigned char __iomem *vrtc_virt_base; | ||
32 | |||
33 | unsigned char vrtc_cmos_read(unsigned char reg) | ||
34 | { | ||
35 | unsigned char retval; | ||
36 | |||
37 | /* vRTC's registers range from 0x0 to 0xD */ | ||
38 | if (reg > 0xd || !vrtc_virt_base) | ||
39 | return 0xff; | ||
40 | |||
41 | lock_cmos_prefix(reg); | ||
42 | retval = __raw_readb(vrtc_virt_base + (reg << 2)); | ||
43 | lock_cmos_suffix(reg); | ||
44 | return retval; | ||
45 | } | ||
46 | EXPORT_SYMBOL_GPL(vrtc_cmos_read); | ||
47 | |||
48 | void vrtc_cmos_write(unsigned char val, unsigned char reg) | ||
49 | { | ||
50 | if (reg > 0xd || !vrtc_virt_base) | ||
51 | return; | ||
52 | |||
53 | lock_cmos_prefix(reg); | ||
54 | __raw_writeb(val, vrtc_virt_base + (reg << 2)); | ||
55 | lock_cmos_suffix(reg); | ||
56 | } | ||
57 | EXPORT_SYMBOL_GPL(vrtc_cmos_write); | ||
58 | |||
59 | void vrtc_get_time(struct timespec *now) | ||
60 | { | ||
61 | u8 sec, min, hour, mday, mon; | ||
62 | unsigned long flags; | ||
63 | u32 year; | ||
64 | |||
65 | spin_lock_irqsave(&rtc_lock, flags); | ||
66 | |||
67 | while ((vrtc_cmos_read(RTC_FREQ_SELECT) & RTC_UIP)) | ||
68 | cpu_relax(); | ||
69 | |||
70 | sec = vrtc_cmos_read(RTC_SECONDS); | ||
71 | min = vrtc_cmos_read(RTC_MINUTES); | ||
72 | hour = vrtc_cmos_read(RTC_HOURS); | ||
73 | mday = vrtc_cmos_read(RTC_DAY_OF_MONTH); | ||
74 | mon = vrtc_cmos_read(RTC_MONTH); | ||
75 | year = vrtc_cmos_read(RTC_YEAR); | ||
76 | |||
77 | spin_unlock_irqrestore(&rtc_lock, flags); | ||
78 | |||
79 | /* vRTC YEAR reg contains the offset to 1972 */ | ||
80 | year += 1972; | ||
81 | |||
82 | pr_info("vRTC: sec: %d min: %d hour: %d day: %d " | ||
83 | "mon: %d year: %d\n", sec, min, hour, mday, mon, year); | ||
84 | |||
85 | now->tv_sec = mktime(year, mon, mday, hour, min, sec); | ||
86 | now->tv_nsec = 0; | ||
87 | } | ||
88 | |||
89 | int vrtc_set_mmss(const struct timespec *now) | ||
90 | { | ||
91 | unsigned long flags; | ||
92 | struct rtc_time tm; | ||
93 | int year; | ||
94 | int retval = 0; | ||
95 | |||
96 | rtc_time_to_tm(now->tv_sec, &tm); | ||
97 | if (!rtc_valid_tm(&tm) && tm.tm_year >= 72) { | ||
98 | /* | ||
99 | * tm.year is the number of years since 1900, and the | ||
100 | * vrtc need the years since 1972. | ||
101 | */ | ||
102 | year = tm.tm_year - 72; | ||
103 | spin_lock_irqsave(&rtc_lock, flags); | ||
104 | vrtc_cmos_write(year, RTC_YEAR); | ||
105 | vrtc_cmos_write(tm.tm_mon, RTC_MONTH); | ||
106 | vrtc_cmos_write(tm.tm_mday, RTC_DAY_OF_MONTH); | ||
107 | vrtc_cmos_write(tm.tm_hour, RTC_HOURS); | ||
108 | vrtc_cmos_write(tm.tm_min, RTC_MINUTES); | ||
109 | vrtc_cmos_write(tm.tm_sec, RTC_SECONDS); | ||
110 | spin_unlock_irqrestore(&rtc_lock, flags); | ||
111 | } else { | ||
112 | pr_err("%s: Invalid vRTC value: write of %lx to vRTC failed\n", | ||
113 | __FUNCTION__, now->tv_sec); | ||
114 | retval = -EINVAL; | ||
115 | } | ||
116 | return retval; | ||
117 | } | ||
118 | |||
119 | void __init mrst_rtc_init(void) | ||
120 | { | ||
121 | unsigned long vrtc_paddr; | ||
122 | |||
123 | sfi_table_parse(SFI_SIG_MRTC, NULL, NULL, sfi_parse_mrtc); | ||
124 | |||
125 | vrtc_paddr = sfi_mrtc_array[0].phys_addr; | ||
126 | if (!sfi_mrtc_num || !vrtc_paddr) | ||
127 | return; | ||
128 | |||
129 | vrtc_virt_base = (void __iomem *)set_fixmap_offset_nocache(FIX_LNW_VRTC, | ||
130 | vrtc_paddr); | ||
131 | x86_platform.get_wallclock = vrtc_get_time; | ||
132 | x86_platform.set_wallclock = vrtc_set_mmss; | ||
133 | } | ||
134 | |||
135 | /* | ||
136 | * The Moorestown platform has a memory mapped virtual RTC device that emulates | ||
137 | * the programming interface of the RTC. | ||
138 | */ | ||
139 | |||
140 | static struct resource vrtc_resources[] = { | ||
141 | [0] = { | ||
142 | .flags = IORESOURCE_MEM, | ||
143 | }, | ||
144 | [1] = { | ||
145 | .flags = IORESOURCE_IRQ, | ||
146 | } | ||
147 | }; | ||
148 | |||
149 | static struct platform_device vrtc_device = { | ||
150 | .name = "rtc_mrst", | ||
151 | .id = -1, | ||
152 | .resource = vrtc_resources, | ||
153 | .num_resources = ARRAY_SIZE(vrtc_resources), | ||
154 | }; | ||
155 | |||
156 | /* Register the RTC device if appropriate */ | ||
157 | static int __init mrst_device_create(void) | ||
158 | { | ||
159 | /* No Moorestown, no device */ | ||
160 | if (!mrst_identify_cpu()) | ||
161 | return -ENODEV; | ||
162 | /* No timer, no device */ | ||
163 | if (!sfi_mrtc_num) | ||
164 | return -ENODEV; | ||
165 | |||
166 | /* iomem resource */ | ||
167 | vrtc_resources[0].start = sfi_mrtc_array[0].phys_addr; | ||
168 | vrtc_resources[0].end = sfi_mrtc_array[0].phys_addr + | ||
169 | MRST_VRTC_MAP_SZ; | ||
170 | /* irq resource */ | ||
171 | vrtc_resources[1].start = sfi_mrtc_array[0].irq; | ||
172 | vrtc_resources[1].end = sfi_mrtc_array[0].irq; | ||
173 | |||
174 | return platform_device_register(&vrtc_device); | ||
175 | } | ||
176 | |||
177 | module_init(mrst_device_create); | ||
178 | 1 | /* |
drivers/gpu/drm/gma500/mdfld_dsi_output.h
1 | /* | 1 | /* |
2 | * Copyright ยฉ 2010 Intel Corporation | 2 | * Copyright ยฉ 2010 Intel Corporation |
3 | * | 3 | * |
4 | * Permission is hereby granted, free of charge, to any person obtaining a | 4 | * Permission is hereby granted, free of charge, to any person obtaining a |
5 | * copy of this software and associated documentation files (the "Software"), | 5 | * copy of this software and associated documentation files (the "Software"), |
6 | * to deal in the Software without restriction, including without limitation | 6 | * to deal in the Software without restriction, including without limitation |
7 | * the rights to use, copy, modify, merge, publish, distribute, sublicense, | 7 | * the rights to use, copy, modify, merge, publish, distribute, sublicense, |
8 | * and/or sell copies of the Software, and to permit persons to whom the | 8 | * and/or sell copies of the Software, and to permit persons to whom the |
9 | * Software is furnished to do so, subject to the following conditions: | 9 | * Software is furnished to do so, subject to the following conditions: |
10 | * | 10 | * |
11 | * The above copyright notice and this permission notice (including the next | 11 | * The above copyright notice and this permission notice (including the next |
12 | * paragraph) shall be included in all copies or substantial portions of the | 12 | * paragraph) shall be included in all copies or substantial portions of the |
13 | * Software. | 13 | * Software. |
14 | * | 14 | * |
15 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | 15 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR |
16 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | 16 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, |
17 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL | 17 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL |
18 | * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | 18 | * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER |
19 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING | 19 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING |
20 | * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER | 20 | * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER |
21 | * DEALINGS IN THE SOFTWARE. | 21 | * DEALINGS IN THE SOFTWARE. |
22 | * | 22 | * |
23 | * Authors: | 23 | * Authors: |
24 | * jim liu <jim.liu@intel.com> | 24 | * jim liu <jim.liu@intel.com> |
25 | * Jackie Li<yaodong.li@intel.com> | 25 | * Jackie Li<yaodong.li@intel.com> |
26 | */ | 26 | */ |
27 | 27 | ||
28 | #ifndef __MDFLD_DSI_OUTPUT_H__ | 28 | #ifndef __MDFLD_DSI_OUTPUT_H__ |
29 | #define __MDFLD_DSI_OUTPUT_H__ | 29 | #define __MDFLD_DSI_OUTPUT_H__ |
30 | 30 | ||
31 | #include <linux/backlight.h> | 31 | #include <linux/backlight.h> |
32 | #include <drm/drmP.h> | 32 | #include <drm/drmP.h> |
33 | #include <drm/drm.h> | 33 | #include <drm/drm.h> |
34 | #include <drm/drm_crtc.h> | 34 | #include <drm/drm_crtc.h> |
35 | #include <drm/drm_edid.h> | 35 | #include <drm/drm_edid.h> |
36 | 36 | ||
37 | #include "psb_drv.h" | 37 | #include "psb_drv.h" |
38 | #include "psb_intel_drv.h" | 38 | #include "psb_intel_drv.h" |
39 | #include "psb_intel_reg.h" | 39 | #include "psb_intel_reg.h" |
40 | #include "mdfld_output.h" | 40 | #include "mdfld_output.h" |
41 | 41 | ||
42 | #include <asm/mrst.h> | 42 | #include <asm/intel-mid.h> |
43 | 43 | ||
44 | #define FLD_MASK(start, end) (((1 << ((start) - (end) + 1)) - 1) << (end)) | 44 | #define FLD_MASK(start, end) (((1 << ((start) - (end) + 1)) - 1) << (end)) |
45 | #define FLD_VAL(val, start, end) (((val) << (end)) & FLD_MASK(start, end)) | 45 | #define FLD_VAL(val, start, end) (((val) << (end)) & FLD_MASK(start, end)) |
46 | #define FLD_GET(val, start, end) (((val) & FLD_MASK(start, end)) >> (end)) | 46 | #define FLD_GET(val, start, end) (((val) & FLD_MASK(start, end)) >> (end)) |
47 | #define FLD_MOD(orig, val, start, end) \ | 47 | #define FLD_MOD(orig, val, start, end) \ |
48 | (((orig) & ~FLD_MASK(start, end)) | FLD_VAL(val, start, end)) | 48 | (((orig) & ~FLD_MASK(start, end)) | FLD_VAL(val, start, end)) |
49 | 49 | ||
50 | #define REG_FLD_MOD(reg, val, start, end) \ | 50 | #define REG_FLD_MOD(reg, val, start, end) \ |
51 | REG_WRITE(reg, FLD_MOD(REG_READ(reg), val, start, end)) | 51 | REG_WRITE(reg, FLD_MOD(REG_READ(reg), val, start, end)) |
52 | 52 | ||
53 | static inline int REGISTER_FLD_WAIT(struct drm_device *dev, u32 reg, | 53 | static inline int REGISTER_FLD_WAIT(struct drm_device *dev, u32 reg, |
54 | u32 val, int start, int end) | 54 | u32 val, int start, int end) |
55 | { | 55 | { |
56 | int t = 100000; | 56 | int t = 100000; |
57 | 57 | ||
58 | while (FLD_GET(REG_READ(reg), start, end) != val) { | 58 | while (FLD_GET(REG_READ(reg), start, end) != val) { |
59 | if (--t == 0) | 59 | if (--t == 0) |
60 | return 1; | 60 | return 1; |
61 | } | 61 | } |
62 | 62 | ||
63 | return 0; | 63 | return 0; |
64 | } | 64 | } |
65 | 65 | ||
66 | #define REG_FLD_WAIT(reg, val, start, end) \ | 66 | #define REG_FLD_WAIT(reg, val, start, end) \ |
67 | REGISTER_FLD_WAIT(dev, reg, val, start, end) | 67 | REGISTER_FLD_WAIT(dev, reg, val, start, end) |
68 | 68 | ||
69 | #define REG_BIT_WAIT(reg, val, bitnum) \ | 69 | #define REG_BIT_WAIT(reg, val, bitnum) \ |
70 | REGISTER_FLD_WAIT(dev, reg, val, bitnum, bitnum) | 70 | REGISTER_FLD_WAIT(dev, reg, val, bitnum, bitnum) |
71 | 71 | ||
72 | #define MDFLD_DSI_BRIGHTNESS_MAX_LEVEL 100 | 72 | #define MDFLD_DSI_BRIGHTNESS_MAX_LEVEL 100 |
73 | 73 | ||
74 | #ifdef DEBUG | 74 | #ifdef DEBUG |
75 | #define CHECK_PIPE(pipe) ({ \ | 75 | #define CHECK_PIPE(pipe) ({ \ |
76 | const typeof(pipe) __pipe = (pipe); \ | 76 | const typeof(pipe) __pipe = (pipe); \ |
77 | BUG_ON(__pipe != 0 && __pipe != 2); \ | 77 | BUG_ON(__pipe != 0 && __pipe != 2); \ |
78 | __pipe; }) | 78 | __pipe; }) |
79 | #else | 79 | #else |
80 | #define CHECK_PIPE(pipe) (pipe) | 80 | #define CHECK_PIPE(pipe) (pipe) |
81 | #endif | 81 | #endif |
82 | 82 | ||
83 | /* | 83 | /* |
84 | * Actual MIPIA->MIPIC reg offset is 0x800, value 0x400 is valid for 0 and 2 | 84 | * Actual MIPIA->MIPIC reg offset is 0x800, value 0x400 is valid for 0 and 2 |
85 | */ | 85 | */ |
86 | #define REG_OFFSET(pipe) (CHECK_PIPE(pipe) * 0x400) | 86 | #define REG_OFFSET(pipe) (CHECK_PIPE(pipe) * 0x400) |
87 | 87 | ||
88 | /* mdfld DSI controller registers */ | 88 | /* mdfld DSI controller registers */ |
89 | #define MIPI_DEVICE_READY_REG(pipe) (0xb000 + REG_OFFSET(pipe)) | 89 | #define MIPI_DEVICE_READY_REG(pipe) (0xb000 + REG_OFFSET(pipe)) |
90 | #define MIPI_INTR_STAT_REG(pipe) (0xb004 + REG_OFFSET(pipe)) | 90 | #define MIPI_INTR_STAT_REG(pipe) (0xb004 + REG_OFFSET(pipe)) |
91 | #define MIPI_INTR_EN_REG(pipe) (0xb008 + REG_OFFSET(pipe)) | 91 | #define MIPI_INTR_EN_REG(pipe) (0xb008 + REG_OFFSET(pipe)) |
92 | #define MIPI_DSI_FUNC_PRG_REG(pipe) (0xb00c + REG_OFFSET(pipe)) | 92 | #define MIPI_DSI_FUNC_PRG_REG(pipe) (0xb00c + REG_OFFSET(pipe)) |
93 | #define MIPI_HS_TX_TIMEOUT_REG(pipe) (0xb010 + REG_OFFSET(pipe)) | 93 | #define MIPI_HS_TX_TIMEOUT_REG(pipe) (0xb010 + REG_OFFSET(pipe)) |
94 | #define MIPI_LP_RX_TIMEOUT_REG(pipe) (0xb014 + REG_OFFSET(pipe)) | 94 | #define MIPI_LP_RX_TIMEOUT_REG(pipe) (0xb014 + REG_OFFSET(pipe)) |
95 | #define MIPI_TURN_AROUND_TIMEOUT_REG(pipe) (0xb018 + REG_OFFSET(pipe)) | 95 | #define MIPI_TURN_AROUND_TIMEOUT_REG(pipe) (0xb018 + REG_OFFSET(pipe)) |
96 | #define MIPI_DEVICE_RESET_TIMER_REG(pipe) (0xb01c + REG_OFFSET(pipe)) | 96 | #define MIPI_DEVICE_RESET_TIMER_REG(pipe) (0xb01c + REG_OFFSET(pipe)) |
97 | #define MIPI_DPI_RESOLUTION_REG(pipe) (0xb020 + REG_OFFSET(pipe)) | 97 | #define MIPI_DPI_RESOLUTION_REG(pipe) (0xb020 + REG_OFFSET(pipe)) |
98 | #define MIPI_DBI_FIFO_THROTTLE_REG(pipe) (0xb024 + REG_OFFSET(pipe)) | 98 | #define MIPI_DBI_FIFO_THROTTLE_REG(pipe) (0xb024 + REG_OFFSET(pipe)) |
99 | #define MIPI_HSYNC_COUNT_REG(pipe) (0xb028 + REG_OFFSET(pipe)) | 99 | #define MIPI_HSYNC_COUNT_REG(pipe) (0xb028 + REG_OFFSET(pipe)) |
100 | #define MIPI_HBP_COUNT_REG(pipe) (0xb02c + REG_OFFSET(pipe)) | 100 | #define MIPI_HBP_COUNT_REG(pipe) (0xb02c + REG_OFFSET(pipe)) |
101 | #define MIPI_HFP_COUNT_REG(pipe) (0xb030 + REG_OFFSET(pipe)) | 101 | #define MIPI_HFP_COUNT_REG(pipe) (0xb030 + REG_OFFSET(pipe)) |
102 | #define MIPI_HACTIVE_COUNT_REG(pipe) (0xb034 + REG_OFFSET(pipe)) | 102 | #define MIPI_HACTIVE_COUNT_REG(pipe) (0xb034 + REG_OFFSET(pipe)) |
103 | #define MIPI_VSYNC_COUNT_REG(pipe) (0xb038 + REG_OFFSET(pipe)) | 103 | #define MIPI_VSYNC_COUNT_REG(pipe) (0xb038 + REG_OFFSET(pipe)) |
104 | #define MIPI_VBP_COUNT_REG(pipe) (0xb03c + REG_OFFSET(pipe)) | 104 | #define MIPI_VBP_COUNT_REG(pipe) (0xb03c + REG_OFFSET(pipe)) |
105 | #define MIPI_VFP_COUNT_REG(pipe) (0xb040 + REG_OFFSET(pipe)) | 105 | #define MIPI_VFP_COUNT_REG(pipe) (0xb040 + REG_OFFSET(pipe)) |
106 | #define MIPI_HIGH_LOW_SWITCH_COUNT_REG(pipe) (0xb044 + REG_OFFSET(pipe)) | 106 | #define MIPI_HIGH_LOW_SWITCH_COUNT_REG(pipe) (0xb044 + REG_OFFSET(pipe)) |
107 | #define MIPI_DPI_CONTROL_REG(pipe) (0xb048 + REG_OFFSET(pipe)) | 107 | #define MIPI_DPI_CONTROL_REG(pipe) (0xb048 + REG_OFFSET(pipe)) |
108 | #define MIPI_DPI_DATA_REG(pipe) (0xb04c + REG_OFFSET(pipe)) | 108 | #define MIPI_DPI_DATA_REG(pipe) (0xb04c + REG_OFFSET(pipe)) |
109 | #define MIPI_INIT_COUNT_REG(pipe) (0xb050 + REG_OFFSET(pipe)) | 109 | #define MIPI_INIT_COUNT_REG(pipe) (0xb050 + REG_OFFSET(pipe)) |
110 | #define MIPI_MAX_RETURN_PACK_SIZE_REG(pipe) (0xb054 + REG_OFFSET(pipe)) | 110 | #define MIPI_MAX_RETURN_PACK_SIZE_REG(pipe) (0xb054 + REG_OFFSET(pipe)) |
111 | #define MIPI_VIDEO_MODE_FORMAT_REG(pipe) (0xb058 + REG_OFFSET(pipe)) | 111 | #define MIPI_VIDEO_MODE_FORMAT_REG(pipe) (0xb058 + REG_OFFSET(pipe)) |
112 | #define MIPI_EOT_DISABLE_REG(pipe) (0xb05c + REG_OFFSET(pipe)) | 112 | #define MIPI_EOT_DISABLE_REG(pipe) (0xb05c + REG_OFFSET(pipe)) |
113 | #define MIPI_LP_BYTECLK_REG(pipe) (0xb060 + REG_OFFSET(pipe)) | 113 | #define MIPI_LP_BYTECLK_REG(pipe) (0xb060 + REG_OFFSET(pipe)) |
114 | #define MIPI_LP_GEN_DATA_REG(pipe) (0xb064 + REG_OFFSET(pipe)) | 114 | #define MIPI_LP_GEN_DATA_REG(pipe) (0xb064 + REG_OFFSET(pipe)) |
115 | #define MIPI_HS_GEN_DATA_REG(pipe) (0xb068 + REG_OFFSET(pipe)) | 115 | #define MIPI_HS_GEN_DATA_REG(pipe) (0xb068 + REG_OFFSET(pipe)) |
116 | #define MIPI_LP_GEN_CTRL_REG(pipe) (0xb06c + REG_OFFSET(pipe)) | 116 | #define MIPI_LP_GEN_CTRL_REG(pipe) (0xb06c + REG_OFFSET(pipe)) |
117 | #define MIPI_HS_GEN_CTRL_REG(pipe) (0xb070 + REG_OFFSET(pipe)) | 117 | #define MIPI_HS_GEN_CTRL_REG(pipe) (0xb070 + REG_OFFSET(pipe)) |
118 | #define MIPI_GEN_FIFO_STAT_REG(pipe) (0xb074 + REG_OFFSET(pipe)) | 118 | #define MIPI_GEN_FIFO_STAT_REG(pipe) (0xb074 + REG_OFFSET(pipe)) |
119 | #define MIPI_HS_LS_DBI_ENABLE_REG(pipe) (0xb078 + REG_OFFSET(pipe)) | 119 | #define MIPI_HS_LS_DBI_ENABLE_REG(pipe) (0xb078 + REG_OFFSET(pipe)) |
120 | #define MIPI_DPHY_PARAM_REG(pipe) (0xb080 + REG_OFFSET(pipe)) | 120 | #define MIPI_DPHY_PARAM_REG(pipe) (0xb080 + REG_OFFSET(pipe)) |
121 | #define MIPI_DBI_BW_CTRL_REG(pipe) (0xb084 + REG_OFFSET(pipe)) | 121 | #define MIPI_DBI_BW_CTRL_REG(pipe) (0xb084 + REG_OFFSET(pipe)) |
122 | #define MIPI_CLK_LANE_SWITCH_TIME_CNT_REG(pipe) (0xb088 + REG_OFFSET(pipe)) | 122 | #define MIPI_CLK_LANE_SWITCH_TIME_CNT_REG(pipe) (0xb088 + REG_OFFSET(pipe)) |
123 | 123 | ||
124 | #define MIPI_CTRL_REG(pipe) (0xb104 + REG_OFFSET(pipe)) | 124 | #define MIPI_CTRL_REG(pipe) (0xb104 + REG_OFFSET(pipe)) |
125 | #define MIPI_DATA_ADD_REG(pipe) (0xb108 + REG_OFFSET(pipe)) | 125 | #define MIPI_DATA_ADD_REG(pipe) (0xb108 + REG_OFFSET(pipe)) |
126 | #define MIPI_DATA_LEN_REG(pipe) (0xb10c + REG_OFFSET(pipe)) | 126 | #define MIPI_DATA_LEN_REG(pipe) (0xb10c + REG_OFFSET(pipe)) |
127 | #define MIPI_CMD_ADD_REG(pipe) (0xb110 + REG_OFFSET(pipe)) | 127 | #define MIPI_CMD_ADD_REG(pipe) (0xb110 + REG_OFFSET(pipe)) |
128 | #define MIPI_CMD_LEN_REG(pipe) (0xb114 + REG_OFFSET(pipe)) | 128 | #define MIPI_CMD_LEN_REG(pipe) (0xb114 + REG_OFFSET(pipe)) |
129 | 129 | ||
130 | /* non-uniform reg offset */ | 130 | /* non-uniform reg offset */ |
131 | #define MIPI_PORT_CONTROL(pipe) (CHECK_PIPE(pipe) ? MIPI_C : MIPI) | 131 | #define MIPI_PORT_CONTROL(pipe) (CHECK_PIPE(pipe) ? MIPI_C : MIPI) |
132 | 132 | ||
133 | #define DSI_DEVICE_READY (0x1) | 133 | #define DSI_DEVICE_READY (0x1) |
134 | #define DSI_POWER_STATE_ULPS_ENTER (0x2 << 1) | 134 | #define DSI_POWER_STATE_ULPS_ENTER (0x2 << 1) |
135 | #define DSI_POWER_STATE_ULPS_EXIT (0x1 << 1) | 135 | #define DSI_POWER_STATE_ULPS_EXIT (0x1 << 1) |
136 | #define DSI_POWER_STATE_ULPS_OFFSET (0x1) | 136 | #define DSI_POWER_STATE_ULPS_OFFSET (0x1) |
137 | 137 | ||
138 | 138 | ||
139 | #define DSI_ONE_DATA_LANE (0x1) | 139 | #define DSI_ONE_DATA_LANE (0x1) |
140 | #define DSI_TWO_DATA_LANE (0x2) | 140 | #define DSI_TWO_DATA_LANE (0x2) |
141 | #define DSI_THREE_DATA_LANE (0X3) | 141 | #define DSI_THREE_DATA_LANE (0X3) |
142 | #define DSI_FOUR_DATA_LANE (0x4) | 142 | #define DSI_FOUR_DATA_LANE (0x4) |
143 | #define DSI_DPI_VIRT_CHANNEL_OFFSET (0x3) | 143 | #define DSI_DPI_VIRT_CHANNEL_OFFSET (0x3) |
144 | #define DSI_DBI_VIRT_CHANNEL_OFFSET (0x5) | 144 | #define DSI_DBI_VIRT_CHANNEL_OFFSET (0x5) |
145 | #define DSI_DPI_COLOR_FORMAT_RGB565 (0x01 << 7) | 145 | #define DSI_DPI_COLOR_FORMAT_RGB565 (0x01 << 7) |
146 | #define DSI_DPI_COLOR_FORMAT_RGB666 (0x02 << 7) | 146 | #define DSI_DPI_COLOR_FORMAT_RGB666 (0x02 << 7) |
147 | #define DSI_DPI_COLOR_FORMAT_RGB666_UNPACK (0x03 << 7) | 147 | #define DSI_DPI_COLOR_FORMAT_RGB666_UNPACK (0x03 << 7) |
148 | #define DSI_DPI_COLOR_FORMAT_RGB888 (0x04 << 7) | 148 | #define DSI_DPI_COLOR_FORMAT_RGB888 (0x04 << 7) |
149 | #define DSI_DBI_COLOR_FORMAT_OPTION2 (0x05 << 13) | 149 | #define DSI_DBI_COLOR_FORMAT_OPTION2 (0x05 << 13) |
150 | 150 | ||
151 | #define DSI_INTR_STATE_RXSOTERROR BIT(0) | 151 | #define DSI_INTR_STATE_RXSOTERROR BIT(0) |
152 | 152 | ||
153 | #define DSI_INTR_STATE_SPL_PKG_SENT BIT(30) | 153 | #define DSI_INTR_STATE_SPL_PKG_SENT BIT(30) |
154 | #define DSI_INTR_STATE_TE BIT(31) | 154 | #define DSI_INTR_STATE_TE BIT(31) |
155 | 155 | ||
156 | #define DSI_HS_TX_TIMEOUT_MASK (0xffffff) | 156 | #define DSI_HS_TX_TIMEOUT_MASK (0xffffff) |
157 | 157 | ||
158 | #define DSI_LP_RX_TIMEOUT_MASK (0xffffff) | 158 | #define DSI_LP_RX_TIMEOUT_MASK (0xffffff) |
159 | 159 | ||
160 | #define DSI_TURN_AROUND_TIMEOUT_MASK (0x3f) | 160 | #define DSI_TURN_AROUND_TIMEOUT_MASK (0x3f) |
161 | 161 | ||
162 | #define DSI_RESET_TIMER_MASK (0xffff) | 162 | #define DSI_RESET_TIMER_MASK (0xffff) |
163 | 163 | ||
164 | #define DSI_DBI_FIFO_WM_HALF (0x0) | 164 | #define DSI_DBI_FIFO_WM_HALF (0x0) |
165 | #define DSI_DBI_FIFO_WM_QUARTER (0x1) | 165 | #define DSI_DBI_FIFO_WM_QUARTER (0x1) |
166 | #define DSI_DBI_FIFO_WM_LOW (0x2) | 166 | #define DSI_DBI_FIFO_WM_LOW (0x2) |
167 | 167 | ||
168 | #define DSI_DPI_TIMING_MASK (0xffff) | 168 | #define DSI_DPI_TIMING_MASK (0xffff) |
169 | 169 | ||
170 | #define DSI_INIT_TIMER_MASK (0xffff) | 170 | #define DSI_INIT_TIMER_MASK (0xffff) |
171 | 171 | ||
172 | #define DSI_DBI_RETURN_PACK_SIZE_MASK (0x3ff) | 172 | #define DSI_DBI_RETURN_PACK_SIZE_MASK (0x3ff) |
173 | 173 | ||
174 | #define DSI_LP_BYTECLK_MASK (0x0ffff) | 174 | #define DSI_LP_BYTECLK_MASK (0x0ffff) |
175 | 175 | ||
176 | #define DSI_HS_CTRL_GEN_SHORT_W0 (0x03) | 176 | #define DSI_HS_CTRL_GEN_SHORT_W0 (0x03) |
177 | #define DSI_HS_CTRL_GEN_SHORT_W1 (0x13) | 177 | #define DSI_HS_CTRL_GEN_SHORT_W1 (0x13) |
178 | #define DSI_HS_CTRL_GEN_SHORT_W2 (0x23) | 178 | #define DSI_HS_CTRL_GEN_SHORT_W2 (0x23) |
179 | #define DSI_HS_CTRL_GEN_R0 (0x04) | 179 | #define DSI_HS_CTRL_GEN_R0 (0x04) |
180 | #define DSI_HS_CTRL_GEN_R1 (0x14) | 180 | #define DSI_HS_CTRL_GEN_R1 (0x14) |
181 | #define DSI_HS_CTRL_GEN_R2 (0x24) | 181 | #define DSI_HS_CTRL_GEN_R2 (0x24) |
182 | #define DSI_HS_CTRL_GEN_LONG_W (0x29) | 182 | #define DSI_HS_CTRL_GEN_LONG_W (0x29) |
183 | #define DSI_HS_CTRL_MCS_SHORT_W0 (0x05) | 183 | #define DSI_HS_CTRL_MCS_SHORT_W0 (0x05) |
184 | #define DSI_HS_CTRL_MCS_SHORT_W1 (0x15) | 184 | #define DSI_HS_CTRL_MCS_SHORT_W1 (0x15) |
185 | #define DSI_HS_CTRL_MCS_R0 (0x06) | 185 | #define DSI_HS_CTRL_MCS_R0 (0x06) |
186 | #define DSI_HS_CTRL_MCS_LONG_W (0x39) | 186 | #define DSI_HS_CTRL_MCS_LONG_W (0x39) |
187 | #define DSI_HS_CTRL_VC_OFFSET (0x06) | 187 | #define DSI_HS_CTRL_VC_OFFSET (0x06) |
188 | #define DSI_HS_CTRL_WC_OFFSET (0x08) | 188 | #define DSI_HS_CTRL_WC_OFFSET (0x08) |
189 | 189 | ||
190 | #define DSI_FIFO_GEN_HS_DATA_FULL BIT(0) | 190 | #define DSI_FIFO_GEN_HS_DATA_FULL BIT(0) |
191 | #define DSI_FIFO_GEN_HS_DATA_HALF_EMPTY BIT(1) | 191 | #define DSI_FIFO_GEN_HS_DATA_HALF_EMPTY BIT(1) |
192 | #define DSI_FIFO_GEN_HS_DATA_EMPTY BIT(2) | 192 | #define DSI_FIFO_GEN_HS_DATA_EMPTY BIT(2) |
193 | #define DSI_FIFO_GEN_LP_DATA_FULL BIT(8) | 193 | #define DSI_FIFO_GEN_LP_DATA_FULL BIT(8) |
194 | #define DSI_FIFO_GEN_LP_DATA_HALF_EMPTY BIT(9) | 194 | #define DSI_FIFO_GEN_LP_DATA_HALF_EMPTY BIT(9) |
195 | #define DSI_FIFO_GEN_LP_DATA_EMPTY BIT(10) | 195 | #define DSI_FIFO_GEN_LP_DATA_EMPTY BIT(10) |
196 | #define DSI_FIFO_GEN_HS_CTRL_FULL BIT(16) | 196 | #define DSI_FIFO_GEN_HS_CTRL_FULL BIT(16) |
197 | #define DSI_FIFO_GEN_HS_CTRL_HALF_EMPTY BIT(17) | 197 | #define DSI_FIFO_GEN_HS_CTRL_HALF_EMPTY BIT(17) |
198 | #define DSI_FIFO_GEN_HS_CTRL_EMPTY BIT(18) | 198 | #define DSI_FIFO_GEN_HS_CTRL_EMPTY BIT(18) |
199 | #define DSI_FIFO_GEN_LP_CTRL_FULL BIT(24) | 199 | #define DSI_FIFO_GEN_LP_CTRL_FULL BIT(24) |
200 | #define DSI_FIFO_GEN_LP_CTRL_HALF_EMPTY BIT(25) | 200 | #define DSI_FIFO_GEN_LP_CTRL_HALF_EMPTY BIT(25) |
201 | #define DSI_FIFO_GEN_LP_CTRL_EMPTY BIT(26) | 201 | #define DSI_FIFO_GEN_LP_CTRL_EMPTY BIT(26) |
202 | #define DSI_FIFO_DBI_EMPTY BIT(27) | 202 | #define DSI_FIFO_DBI_EMPTY BIT(27) |
203 | #define DSI_FIFO_DPI_EMPTY BIT(28) | 203 | #define DSI_FIFO_DPI_EMPTY BIT(28) |
204 | 204 | ||
205 | #define DSI_DBI_HS_LP_SWITCH_MASK (0x1) | 205 | #define DSI_DBI_HS_LP_SWITCH_MASK (0x1) |
206 | 206 | ||
207 | #define DSI_HS_LP_SWITCH_COUNTER_OFFSET (0x0) | 207 | #define DSI_HS_LP_SWITCH_COUNTER_OFFSET (0x0) |
208 | #define DSI_LP_HS_SWITCH_COUNTER_OFFSET (0x16) | 208 | #define DSI_LP_HS_SWITCH_COUNTER_OFFSET (0x16) |
209 | 209 | ||
210 | #define DSI_DPI_CTRL_HS_SHUTDOWN (0x00000001) | 210 | #define DSI_DPI_CTRL_HS_SHUTDOWN (0x00000001) |
211 | #define DSI_DPI_CTRL_HS_TURN_ON (0x00000002) | 211 | #define DSI_DPI_CTRL_HS_TURN_ON (0x00000002) |
212 | 212 | ||
213 | /*dsi power modes*/ | 213 | /*dsi power modes*/ |
214 | #define DSI_POWER_MODE_DISPLAY_ON BIT(2) | 214 | #define DSI_POWER_MODE_DISPLAY_ON BIT(2) |
215 | #define DSI_POWER_MODE_NORMAL_ON BIT(3) | 215 | #define DSI_POWER_MODE_NORMAL_ON BIT(3) |
216 | #define DSI_POWER_MODE_SLEEP_OUT BIT(4) | 216 | #define DSI_POWER_MODE_SLEEP_OUT BIT(4) |
217 | #define DSI_POWER_MODE_PARTIAL_ON BIT(5) | 217 | #define DSI_POWER_MODE_PARTIAL_ON BIT(5) |
218 | #define DSI_POWER_MODE_IDLE_ON BIT(6) | 218 | #define DSI_POWER_MODE_IDLE_ON BIT(6) |
219 | 219 | ||
220 | enum { | 220 | enum { |
221 | MDFLD_DSI_VIDEO_NON_BURST_MODE_SYNC_PULSE = 1, | 221 | MDFLD_DSI_VIDEO_NON_BURST_MODE_SYNC_PULSE = 1, |
222 | MDFLD_DSI_VIDEO_NON_BURST_MODE_SYNC_EVENTS = 2, | 222 | MDFLD_DSI_VIDEO_NON_BURST_MODE_SYNC_EVENTS = 2, |
223 | MDFLD_DSI_VIDEO_BURST_MODE = 3, | 223 | MDFLD_DSI_VIDEO_BURST_MODE = 3, |
224 | }; | 224 | }; |
225 | 225 | ||
226 | #define DSI_DPI_COMPLETE_LAST_LINE BIT(2) | 226 | #define DSI_DPI_COMPLETE_LAST_LINE BIT(2) |
227 | #define DSI_DPI_DISABLE_BTA BIT(3) | 227 | #define DSI_DPI_DISABLE_BTA BIT(3) |
228 | 228 | ||
229 | struct mdfld_dsi_connector { | 229 | struct mdfld_dsi_connector { |
230 | struct gma_connector base; | 230 | struct gma_connector base; |
231 | 231 | ||
232 | int pipe; | 232 | int pipe; |
233 | void *private; | 233 | void *private; |
234 | void *pkg_sender; | 234 | void *pkg_sender; |
235 | 235 | ||
236 | /* Connection status */ | 236 | /* Connection status */ |
237 | enum drm_connector_status status; | 237 | enum drm_connector_status status; |
238 | }; | 238 | }; |
239 | 239 | ||
240 | struct mdfld_dsi_encoder { | 240 | struct mdfld_dsi_encoder { |
241 | struct gma_encoder base; | 241 | struct gma_encoder base; |
242 | void *private; | 242 | void *private; |
243 | }; | 243 | }; |
244 | 244 | ||
245 | /* | 245 | /* |
246 | * DSI config, consists of one DSI connector, two DSI encoders. | 246 | * DSI config, consists of one DSI connector, two DSI encoders. |
247 | * DRM will pick up on DSI encoder basing on differents configs. | 247 | * DRM will pick up on DSI encoder basing on differents configs. |
248 | */ | 248 | */ |
249 | struct mdfld_dsi_config { | 249 | struct mdfld_dsi_config { |
250 | struct drm_device *dev; | 250 | struct drm_device *dev; |
251 | struct drm_display_mode *fixed_mode; | 251 | struct drm_display_mode *fixed_mode; |
252 | struct drm_display_mode *mode; | 252 | struct drm_display_mode *mode; |
253 | 253 | ||
254 | struct mdfld_dsi_connector *connector; | 254 | struct mdfld_dsi_connector *connector; |
255 | struct mdfld_dsi_encoder *encoder; | 255 | struct mdfld_dsi_encoder *encoder; |
256 | 256 | ||
257 | int changed; | 257 | int changed; |
258 | 258 | ||
259 | int bpp; | 259 | int bpp; |
260 | int lane_count; | 260 | int lane_count; |
261 | /*Virtual channel number for this encoder*/ | 261 | /*Virtual channel number for this encoder*/ |
262 | int channel_num; | 262 | int channel_num; |
263 | /*video mode configure*/ | 263 | /*video mode configure*/ |
264 | int video_mode; | 264 | int video_mode; |
265 | 265 | ||
266 | int dvr_ic_inited; | 266 | int dvr_ic_inited; |
267 | }; | 267 | }; |
268 | 268 | ||
269 | static inline struct mdfld_dsi_connector *mdfld_dsi_connector( | 269 | static inline struct mdfld_dsi_connector *mdfld_dsi_connector( |
270 | struct drm_connector *connector) | 270 | struct drm_connector *connector) |
271 | { | 271 | { |
272 | struct gma_connector *gma_connector; | 272 | struct gma_connector *gma_connector; |
273 | 273 | ||
274 | gma_connector = to_gma_connector(connector); | 274 | gma_connector = to_gma_connector(connector); |
275 | 275 | ||
276 | return container_of(gma_connector, struct mdfld_dsi_connector, base); | 276 | return container_of(gma_connector, struct mdfld_dsi_connector, base); |
277 | } | 277 | } |
278 | 278 | ||
279 | static inline struct mdfld_dsi_encoder *mdfld_dsi_encoder( | 279 | static inline struct mdfld_dsi_encoder *mdfld_dsi_encoder( |
280 | struct drm_encoder *encoder) | 280 | struct drm_encoder *encoder) |
281 | { | 281 | { |
282 | struct gma_encoder *gma_encoder; | 282 | struct gma_encoder *gma_encoder; |
283 | 283 | ||
284 | gma_encoder = to_gma_encoder(encoder); | 284 | gma_encoder = to_gma_encoder(encoder); |
285 | 285 | ||
286 | return container_of(gma_encoder, struct mdfld_dsi_encoder, base); | 286 | return container_of(gma_encoder, struct mdfld_dsi_encoder, base); |
287 | } | 287 | } |
288 | 288 | ||
289 | static inline struct mdfld_dsi_config * | 289 | static inline struct mdfld_dsi_config * |
290 | mdfld_dsi_get_config(struct mdfld_dsi_connector *connector) | 290 | mdfld_dsi_get_config(struct mdfld_dsi_connector *connector) |
291 | { | 291 | { |
292 | if (!connector) | 292 | if (!connector) |
293 | return NULL; | 293 | return NULL; |
294 | return (struct mdfld_dsi_config *)connector->private; | 294 | return (struct mdfld_dsi_config *)connector->private; |
295 | } | 295 | } |
296 | 296 | ||
297 | static inline void *mdfld_dsi_get_pkg_sender(struct mdfld_dsi_config *config) | 297 | static inline void *mdfld_dsi_get_pkg_sender(struct mdfld_dsi_config *config) |
298 | { | 298 | { |
299 | struct mdfld_dsi_connector *dsi_connector; | 299 | struct mdfld_dsi_connector *dsi_connector; |
300 | 300 | ||
301 | if (!config) | 301 | if (!config) |
302 | return NULL; | 302 | return NULL; |
303 | 303 | ||
304 | dsi_connector = config->connector; | 304 | dsi_connector = config->connector; |
305 | 305 | ||
306 | if (!dsi_connector) | 306 | if (!dsi_connector) |
307 | return NULL; | 307 | return NULL; |
308 | 308 | ||
309 | return dsi_connector->pkg_sender; | 309 | return dsi_connector->pkg_sender; |
310 | } | 310 | } |
311 | 311 | ||
312 | static inline struct mdfld_dsi_config * | 312 | static inline struct mdfld_dsi_config * |
313 | mdfld_dsi_encoder_get_config(struct mdfld_dsi_encoder *encoder) | 313 | mdfld_dsi_encoder_get_config(struct mdfld_dsi_encoder *encoder) |
314 | { | 314 | { |
315 | if (!encoder) | 315 | if (!encoder) |
316 | return NULL; | 316 | return NULL; |
317 | return (struct mdfld_dsi_config *)encoder->private; | 317 | return (struct mdfld_dsi_config *)encoder->private; |
318 | } | 318 | } |
319 | 319 | ||
320 | static inline struct mdfld_dsi_connector * | 320 | static inline struct mdfld_dsi_connector * |
321 | mdfld_dsi_encoder_get_connector(struct mdfld_dsi_encoder *encoder) | 321 | mdfld_dsi_encoder_get_connector(struct mdfld_dsi_encoder *encoder) |
322 | { | 322 | { |
323 | struct mdfld_dsi_config *config; | 323 | struct mdfld_dsi_config *config; |
324 | 324 | ||
325 | if (!encoder) | 325 | if (!encoder) |
326 | return NULL; | 326 | return NULL; |
327 | 327 | ||
328 | config = mdfld_dsi_encoder_get_config(encoder); | 328 | config = mdfld_dsi_encoder_get_config(encoder); |
329 | if (!config) | 329 | if (!config) |
330 | return NULL; | 330 | return NULL; |
331 | 331 | ||
332 | return config->connector; | 332 | return config->connector; |
333 | } | 333 | } |
334 | 334 | ||
335 | static inline void *mdfld_dsi_encoder_get_pkg_sender( | 335 | static inline void *mdfld_dsi_encoder_get_pkg_sender( |
336 | struct mdfld_dsi_encoder *encoder) | 336 | struct mdfld_dsi_encoder *encoder) |
337 | { | 337 | { |
338 | struct mdfld_dsi_config *dsi_config; | 338 | struct mdfld_dsi_config *dsi_config; |
339 | 339 | ||
340 | dsi_config = mdfld_dsi_encoder_get_config(encoder); | 340 | dsi_config = mdfld_dsi_encoder_get_config(encoder); |
341 | if (!dsi_config) | 341 | if (!dsi_config) |
342 | return NULL; | 342 | return NULL; |
343 | 343 | ||
344 | return mdfld_dsi_get_pkg_sender(dsi_config); | 344 | return mdfld_dsi_get_pkg_sender(dsi_config); |
345 | } | 345 | } |
346 | 346 | ||
347 | static inline int mdfld_dsi_encoder_get_pipe(struct mdfld_dsi_encoder *encoder) | 347 | static inline int mdfld_dsi_encoder_get_pipe(struct mdfld_dsi_encoder *encoder) |
348 | { | 348 | { |
349 | struct mdfld_dsi_connector *connector; | 349 | struct mdfld_dsi_connector *connector; |
350 | 350 | ||
351 | if (!encoder) | 351 | if (!encoder) |
352 | return -1; | 352 | return -1; |
353 | 353 | ||
354 | connector = mdfld_dsi_encoder_get_connector(encoder); | 354 | connector = mdfld_dsi_encoder_get_connector(encoder); |
355 | if (!connector) | 355 | if (!connector) |
356 | return -1; | 356 | return -1; |
357 | return connector->pipe; | 357 | return connector->pipe; |
358 | } | 358 | } |
359 | 359 | ||
360 | /* Export functions */ | 360 | /* Export functions */ |
361 | extern void mdfld_dsi_gen_fifo_ready(struct drm_device *dev, | 361 | extern void mdfld_dsi_gen_fifo_ready(struct drm_device *dev, |
362 | u32 gen_fifo_stat_reg, u32 fifo_stat); | 362 | u32 gen_fifo_stat_reg, u32 fifo_stat); |
363 | extern void mdfld_dsi_brightness_init(struct mdfld_dsi_config *dsi_config, | 363 | extern void mdfld_dsi_brightness_init(struct mdfld_dsi_config *dsi_config, |
364 | int pipe); | 364 | int pipe); |
365 | extern void mdfld_dsi_brightness_control(struct drm_device *dev, int pipe, | 365 | extern void mdfld_dsi_brightness_control(struct drm_device *dev, int pipe, |
366 | int level); | 366 | int level); |
367 | extern void mdfld_dsi_output_init(struct drm_device *dev, | 367 | extern void mdfld_dsi_output_init(struct drm_device *dev, |
368 | int pipe, | 368 | int pipe, |
369 | const struct panel_funcs *p_vid_funcs); | 369 | const struct panel_funcs *p_vid_funcs); |
370 | extern void mdfld_dsi_controller_init(struct mdfld_dsi_config *dsi_config, | 370 | extern void mdfld_dsi_controller_init(struct mdfld_dsi_config *dsi_config, |
371 | int pipe); | 371 | int pipe); |
372 | 372 | ||
373 | extern int mdfld_dsi_get_power_mode(struct mdfld_dsi_config *dsi_config, | 373 | extern int mdfld_dsi_get_power_mode(struct mdfld_dsi_config *dsi_config, |
374 | u32 *mode, bool hs); | 374 | u32 *mode, bool hs); |
375 | extern int mdfld_dsi_panel_reset(int pipe); | 375 | extern int mdfld_dsi_panel_reset(int pipe); |
376 | 376 | ||
377 | #endif /*__MDFLD_DSI_OUTPUT_H__*/ | 377 | #endif /*__MDFLD_DSI_OUTPUT_H__*/ |
378 | 378 |
drivers/gpu/drm/gma500/oaktrail_device.c
1 | /************************************************************************** | 1 | /************************************************************************** |
2 | * Copyright (c) 2011, Intel Corporation. | 2 | * Copyright (c) 2011, Intel Corporation. |
3 | * All Rights Reserved. | 3 | * All Rights Reserved. |
4 | * | 4 | * |
5 | * This program is free software; you can redistribute it and/or modify it | 5 | * This program is free software; you can redistribute it and/or modify it |
6 | * under the terms and conditions of the GNU General Public License, | 6 | * under the terms and conditions of the GNU General Public License, |
7 | * version 2, as published by the Free Software Foundation. | 7 | * version 2, as published by the Free Software Foundation. |
8 | * | 8 | * |
9 | * This program is distributed in the hope it will be useful, but WITHOUT | 9 | * This program is distributed in the hope it will be useful, but WITHOUT |
10 | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or | 10 | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or |
11 | * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for | 11 | * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for |
12 | * more details. | 12 | * more details. |
13 | * | 13 | * |
14 | * You should have received a copy of the GNU General Public License along with | 14 | * You should have received a copy of the GNU General Public License along with |
15 | * this program; if not, write to the Free Software Foundation, Inc., | 15 | * this program; if not, write to the Free Software Foundation, Inc., |
16 | * 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA. | 16 | * 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA. |
17 | * | 17 | * |
18 | **************************************************************************/ | 18 | **************************************************************************/ |
19 | 19 | ||
20 | #include <linux/backlight.h> | 20 | #include <linux/backlight.h> |
21 | #include <linux/module.h> | 21 | #include <linux/module.h> |
22 | #include <linux/dmi.h> | 22 | #include <linux/dmi.h> |
23 | #include <drm/drmP.h> | 23 | #include <drm/drmP.h> |
24 | #include <drm/drm.h> | 24 | #include <drm/drm.h> |
25 | #include <drm/gma_drm.h> | 25 | #include <drm/gma_drm.h> |
26 | #include "psb_drv.h" | 26 | #include "psb_drv.h" |
27 | #include "psb_reg.h" | 27 | #include "psb_reg.h" |
28 | #include "psb_intel_reg.h" | 28 | #include "psb_intel_reg.h" |
29 | #include <asm/mrst.h> | 29 | #include <asm/intel-mid.h> |
30 | #include <asm/intel_scu_ipc.h> | 30 | #include <asm/intel_scu_ipc.h> |
31 | #include "mid_bios.h" | 31 | #include "mid_bios.h" |
32 | #include "intel_bios.h" | 32 | #include "intel_bios.h" |
33 | 33 | ||
34 | static int oaktrail_output_init(struct drm_device *dev) | 34 | static int oaktrail_output_init(struct drm_device *dev) |
35 | { | 35 | { |
36 | struct drm_psb_private *dev_priv = dev->dev_private; | 36 | struct drm_psb_private *dev_priv = dev->dev_private; |
37 | if (dev_priv->iLVDS_enable) | 37 | if (dev_priv->iLVDS_enable) |
38 | oaktrail_lvds_init(dev, &dev_priv->mode_dev); | 38 | oaktrail_lvds_init(dev, &dev_priv->mode_dev); |
39 | else | 39 | else |
40 | dev_err(dev->dev, "DSI is not supported\n"); | 40 | dev_err(dev->dev, "DSI is not supported\n"); |
41 | if (dev_priv->hdmi_priv) | 41 | if (dev_priv->hdmi_priv) |
42 | oaktrail_hdmi_init(dev, &dev_priv->mode_dev); | 42 | oaktrail_hdmi_init(dev, &dev_priv->mode_dev); |
43 | return 0; | 43 | return 0; |
44 | } | 44 | } |
45 | 45 | ||
46 | /* | 46 | /* |
47 | * Provide the low level interfaces for the Moorestown backlight | 47 | * Provide the low level interfaces for the Moorestown backlight |
48 | */ | 48 | */ |
49 | 49 | ||
50 | #ifdef CONFIG_BACKLIGHT_CLASS_DEVICE | 50 | #ifdef CONFIG_BACKLIGHT_CLASS_DEVICE |
51 | 51 | ||
52 | #define MRST_BLC_MAX_PWM_REG_FREQ 0xFFFF | 52 | #define MRST_BLC_MAX_PWM_REG_FREQ 0xFFFF |
53 | #define BLC_PWM_PRECISION_FACTOR 100 /* 10000000 */ | 53 | #define BLC_PWM_PRECISION_FACTOR 100 /* 10000000 */ |
54 | #define BLC_PWM_FREQ_CALC_CONSTANT 32 | 54 | #define BLC_PWM_FREQ_CALC_CONSTANT 32 |
55 | #define MHz 1000000 | 55 | #define MHz 1000000 |
56 | #define BLC_ADJUSTMENT_MAX 100 | 56 | #define BLC_ADJUSTMENT_MAX 100 |
57 | 57 | ||
58 | static struct backlight_device *oaktrail_backlight_device; | 58 | static struct backlight_device *oaktrail_backlight_device; |
59 | static int oaktrail_brightness; | 59 | static int oaktrail_brightness; |
60 | 60 | ||
61 | static int oaktrail_set_brightness(struct backlight_device *bd) | 61 | static int oaktrail_set_brightness(struct backlight_device *bd) |
62 | { | 62 | { |
63 | struct drm_device *dev = bl_get_data(oaktrail_backlight_device); | 63 | struct drm_device *dev = bl_get_data(oaktrail_backlight_device); |
64 | struct drm_psb_private *dev_priv = dev->dev_private; | 64 | struct drm_psb_private *dev_priv = dev->dev_private; |
65 | int level = bd->props.brightness; | 65 | int level = bd->props.brightness; |
66 | u32 blc_pwm_ctl; | 66 | u32 blc_pwm_ctl; |
67 | u32 max_pwm_blc; | 67 | u32 max_pwm_blc; |
68 | 68 | ||
69 | /* Percentage 1-100% being valid */ | 69 | /* Percentage 1-100% being valid */ |
70 | if (level < 1) | 70 | if (level < 1) |
71 | level = 1; | 71 | level = 1; |
72 | 72 | ||
73 | if (gma_power_begin(dev, 0)) { | 73 | if (gma_power_begin(dev, 0)) { |
74 | /* Calculate and set the brightness value */ | 74 | /* Calculate and set the brightness value */ |
75 | max_pwm_blc = REG_READ(BLC_PWM_CTL) >> 16; | 75 | max_pwm_blc = REG_READ(BLC_PWM_CTL) >> 16; |
76 | blc_pwm_ctl = level * max_pwm_blc / 100; | 76 | blc_pwm_ctl = level * max_pwm_blc / 100; |
77 | 77 | ||
78 | /* Adjust the backlight level with the percent in | 78 | /* Adjust the backlight level with the percent in |
79 | * dev_priv->blc_adj1; | 79 | * dev_priv->blc_adj1; |
80 | */ | 80 | */ |
81 | blc_pwm_ctl = blc_pwm_ctl * dev_priv->blc_adj1; | 81 | blc_pwm_ctl = blc_pwm_ctl * dev_priv->blc_adj1; |
82 | blc_pwm_ctl = blc_pwm_ctl / 100; | 82 | blc_pwm_ctl = blc_pwm_ctl / 100; |
83 | 83 | ||
84 | /* Adjust the backlight level with the percent in | 84 | /* Adjust the backlight level with the percent in |
85 | * dev_priv->blc_adj2; | 85 | * dev_priv->blc_adj2; |
86 | */ | 86 | */ |
87 | blc_pwm_ctl = blc_pwm_ctl * dev_priv->blc_adj2; | 87 | blc_pwm_ctl = blc_pwm_ctl * dev_priv->blc_adj2; |
88 | blc_pwm_ctl = blc_pwm_ctl / 100; | 88 | blc_pwm_ctl = blc_pwm_ctl / 100; |
89 | 89 | ||
90 | /* force PWM bit on */ | 90 | /* force PWM bit on */ |
91 | REG_WRITE(BLC_PWM_CTL2, (0x80000000 | REG_READ(BLC_PWM_CTL2))); | 91 | REG_WRITE(BLC_PWM_CTL2, (0x80000000 | REG_READ(BLC_PWM_CTL2))); |
92 | REG_WRITE(BLC_PWM_CTL, (max_pwm_blc << 16) | blc_pwm_ctl); | 92 | REG_WRITE(BLC_PWM_CTL, (max_pwm_blc << 16) | blc_pwm_ctl); |
93 | gma_power_end(dev); | 93 | gma_power_end(dev); |
94 | } | 94 | } |
95 | oaktrail_brightness = level; | 95 | oaktrail_brightness = level; |
96 | return 0; | 96 | return 0; |
97 | } | 97 | } |
98 | 98 | ||
99 | static int oaktrail_get_brightness(struct backlight_device *bd) | 99 | static int oaktrail_get_brightness(struct backlight_device *bd) |
100 | { | 100 | { |
101 | /* return locally cached var instead of HW read (due to DPST etc.) */ | 101 | /* return locally cached var instead of HW read (due to DPST etc.) */ |
102 | /* FIXME: ideally return actual value in case firmware fiddled with | 102 | /* FIXME: ideally return actual value in case firmware fiddled with |
103 | it */ | 103 | it */ |
104 | return oaktrail_brightness; | 104 | return oaktrail_brightness; |
105 | } | 105 | } |
106 | 106 | ||
107 | static int device_backlight_init(struct drm_device *dev) | 107 | static int device_backlight_init(struct drm_device *dev) |
108 | { | 108 | { |
109 | struct drm_psb_private *dev_priv = dev->dev_private; | 109 | struct drm_psb_private *dev_priv = dev->dev_private; |
110 | unsigned long core_clock; | 110 | unsigned long core_clock; |
111 | u16 bl_max_freq; | 111 | u16 bl_max_freq; |
112 | uint32_t value; | 112 | uint32_t value; |
113 | uint32_t blc_pwm_precision_factor; | 113 | uint32_t blc_pwm_precision_factor; |
114 | 114 | ||
115 | dev_priv->blc_adj1 = BLC_ADJUSTMENT_MAX; | 115 | dev_priv->blc_adj1 = BLC_ADJUSTMENT_MAX; |
116 | dev_priv->blc_adj2 = BLC_ADJUSTMENT_MAX; | 116 | dev_priv->blc_adj2 = BLC_ADJUSTMENT_MAX; |
117 | bl_max_freq = 256; | 117 | bl_max_freq = 256; |
118 | /* this needs to be set elsewhere */ | 118 | /* this needs to be set elsewhere */ |
119 | blc_pwm_precision_factor = BLC_PWM_PRECISION_FACTOR; | 119 | blc_pwm_precision_factor = BLC_PWM_PRECISION_FACTOR; |
120 | 120 | ||
121 | core_clock = dev_priv->core_freq; | 121 | core_clock = dev_priv->core_freq; |
122 | 122 | ||
123 | value = (core_clock * MHz) / BLC_PWM_FREQ_CALC_CONSTANT; | 123 | value = (core_clock * MHz) / BLC_PWM_FREQ_CALC_CONSTANT; |
124 | value *= blc_pwm_precision_factor; | 124 | value *= blc_pwm_precision_factor; |
125 | value /= bl_max_freq; | 125 | value /= bl_max_freq; |
126 | value /= blc_pwm_precision_factor; | 126 | value /= blc_pwm_precision_factor; |
127 | 127 | ||
128 | if (value > (unsigned long long)MRST_BLC_MAX_PWM_REG_FREQ) | 128 | if (value > (unsigned long long)MRST_BLC_MAX_PWM_REG_FREQ) |
129 | return -ERANGE; | 129 | return -ERANGE; |
130 | 130 | ||
131 | if (gma_power_begin(dev, false)) { | 131 | if (gma_power_begin(dev, false)) { |
132 | REG_WRITE(BLC_PWM_CTL2, (0x80000000 | REG_READ(BLC_PWM_CTL2))); | 132 | REG_WRITE(BLC_PWM_CTL2, (0x80000000 | REG_READ(BLC_PWM_CTL2))); |
133 | REG_WRITE(BLC_PWM_CTL, value | (value << 16)); | 133 | REG_WRITE(BLC_PWM_CTL, value | (value << 16)); |
134 | gma_power_end(dev); | 134 | gma_power_end(dev); |
135 | } | 135 | } |
136 | return 0; | 136 | return 0; |
137 | } | 137 | } |
138 | 138 | ||
139 | static const struct backlight_ops oaktrail_ops = { | 139 | static const struct backlight_ops oaktrail_ops = { |
140 | .get_brightness = oaktrail_get_brightness, | 140 | .get_brightness = oaktrail_get_brightness, |
141 | .update_status = oaktrail_set_brightness, | 141 | .update_status = oaktrail_set_brightness, |
142 | }; | 142 | }; |
143 | 143 | ||
144 | static int oaktrail_backlight_init(struct drm_device *dev) | 144 | static int oaktrail_backlight_init(struct drm_device *dev) |
145 | { | 145 | { |
146 | struct drm_psb_private *dev_priv = dev->dev_private; | 146 | struct drm_psb_private *dev_priv = dev->dev_private; |
147 | int ret; | 147 | int ret; |
148 | struct backlight_properties props; | 148 | struct backlight_properties props; |
149 | 149 | ||
150 | memset(&props, 0, sizeof(struct backlight_properties)); | 150 | memset(&props, 0, sizeof(struct backlight_properties)); |
151 | props.max_brightness = 100; | 151 | props.max_brightness = 100; |
152 | props.type = BACKLIGHT_PLATFORM; | 152 | props.type = BACKLIGHT_PLATFORM; |
153 | 153 | ||
154 | oaktrail_backlight_device = backlight_device_register("oaktrail-bl", | 154 | oaktrail_backlight_device = backlight_device_register("oaktrail-bl", |
155 | NULL, (void *)dev, &oaktrail_ops, &props); | 155 | NULL, (void *)dev, &oaktrail_ops, &props); |
156 | 156 | ||
157 | if (IS_ERR(oaktrail_backlight_device)) | 157 | if (IS_ERR(oaktrail_backlight_device)) |
158 | return PTR_ERR(oaktrail_backlight_device); | 158 | return PTR_ERR(oaktrail_backlight_device); |
159 | 159 | ||
160 | ret = device_backlight_init(dev); | 160 | ret = device_backlight_init(dev); |
161 | if (ret < 0) { | 161 | if (ret < 0) { |
162 | backlight_device_unregister(oaktrail_backlight_device); | 162 | backlight_device_unregister(oaktrail_backlight_device); |
163 | return ret; | 163 | return ret; |
164 | } | 164 | } |
165 | oaktrail_backlight_device->props.brightness = 100; | 165 | oaktrail_backlight_device->props.brightness = 100; |
166 | oaktrail_backlight_device->props.max_brightness = 100; | 166 | oaktrail_backlight_device->props.max_brightness = 100; |
167 | backlight_update_status(oaktrail_backlight_device); | 167 | backlight_update_status(oaktrail_backlight_device); |
168 | dev_priv->backlight_device = oaktrail_backlight_device; | 168 | dev_priv->backlight_device = oaktrail_backlight_device; |
169 | return 0; | 169 | return 0; |
170 | } | 170 | } |
171 | 171 | ||
172 | #endif | 172 | #endif |
173 | 173 | ||
174 | /* | 174 | /* |
175 | * Provide the Moorestown specific chip logic and low level methods | 175 | * Provide the Moorestown specific chip logic and low level methods |
176 | * for power management | 176 | * for power management |
177 | */ | 177 | */ |
178 | 178 | ||
179 | /** | 179 | /** |
180 | * oaktrail_save_display_registers - save registers lost on suspend | 180 | * oaktrail_save_display_registers - save registers lost on suspend |
181 | * @dev: our DRM device | 181 | * @dev: our DRM device |
182 | * | 182 | * |
183 | * Save the state we need in order to be able to restore the interface | 183 | * Save the state we need in order to be able to restore the interface |
184 | * upon resume from suspend | 184 | * upon resume from suspend |
185 | */ | 185 | */ |
186 | static int oaktrail_save_display_registers(struct drm_device *dev) | 186 | static int oaktrail_save_display_registers(struct drm_device *dev) |
187 | { | 187 | { |
188 | struct drm_psb_private *dev_priv = dev->dev_private; | 188 | struct drm_psb_private *dev_priv = dev->dev_private; |
189 | struct psb_save_area *regs = &dev_priv->regs; | 189 | struct psb_save_area *regs = &dev_priv->regs; |
190 | struct psb_pipe *p = ®s->pipe[0]; | 190 | struct psb_pipe *p = ®s->pipe[0]; |
191 | int i; | 191 | int i; |
192 | u32 pp_stat; | 192 | u32 pp_stat; |
193 | 193 | ||
194 | /* Display arbitration control + watermarks */ | 194 | /* Display arbitration control + watermarks */ |
195 | regs->psb.saveDSPARB = PSB_RVDC32(DSPARB); | 195 | regs->psb.saveDSPARB = PSB_RVDC32(DSPARB); |
196 | regs->psb.saveDSPFW1 = PSB_RVDC32(DSPFW1); | 196 | regs->psb.saveDSPFW1 = PSB_RVDC32(DSPFW1); |
197 | regs->psb.saveDSPFW2 = PSB_RVDC32(DSPFW2); | 197 | regs->psb.saveDSPFW2 = PSB_RVDC32(DSPFW2); |
198 | regs->psb.saveDSPFW3 = PSB_RVDC32(DSPFW3); | 198 | regs->psb.saveDSPFW3 = PSB_RVDC32(DSPFW3); |
199 | regs->psb.saveDSPFW4 = PSB_RVDC32(DSPFW4); | 199 | regs->psb.saveDSPFW4 = PSB_RVDC32(DSPFW4); |
200 | regs->psb.saveDSPFW5 = PSB_RVDC32(DSPFW5); | 200 | regs->psb.saveDSPFW5 = PSB_RVDC32(DSPFW5); |
201 | regs->psb.saveDSPFW6 = PSB_RVDC32(DSPFW6); | 201 | regs->psb.saveDSPFW6 = PSB_RVDC32(DSPFW6); |
202 | regs->psb.saveCHICKENBIT = PSB_RVDC32(DSPCHICKENBIT); | 202 | regs->psb.saveCHICKENBIT = PSB_RVDC32(DSPCHICKENBIT); |
203 | 203 | ||
204 | /* Pipe & plane A info */ | 204 | /* Pipe & plane A info */ |
205 | p->conf = PSB_RVDC32(PIPEACONF); | 205 | p->conf = PSB_RVDC32(PIPEACONF); |
206 | p->src = PSB_RVDC32(PIPEASRC); | 206 | p->src = PSB_RVDC32(PIPEASRC); |
207 | p->fp0 = PSB_RVDC32(MRST_FPA0); | 207 | p->fp0 = PSB_RVDC32(MRST_FPA0); |
208 | p->fp1 = PSB_RVDC32(MRST_FPA1); | 208 | p->fp1 = PSB_RVDC32(MRST_FPA1); |
209 | p->dpll = PSB_RVDC32(MRST_DPLL_A); | 209 | p->dpll = PSB_RVDC32(MRST_DPLL_A); |
210 | p->htotal = PSB_RVDC32(HTOTAL_A); | 210 | p->htotal = PSB_RVDC32(HTOTAL_A); |
211 | p->hblank = PSB_RVDC32(HBLANK_A); | 211 | p->hblank = PSB_RVDC32(HBLANK_A); |
212 | p->hsync = PSB_RVDC32(HSYNC_A); | 212 | p->hsync = PSB_RVDC32(HSYNC_A); |
213 | p->vtotal = PSB_RVDC32(VTOTAL_A); | 213 | p->vtotal = PSB_RVDC32(VTOTAL_A); |
214 | p->vblank = PSB_RVDC32(VBLANK_A); | 214 | p->vblank = PSB_RVDC32(VBLANK_A); |
215 | p->vsync = PSB_RVDC32(VSYNC_A); | 215 | p->vsync = PSB_RVDC32(VSYNC_A); |
216 | regs->psb.saveBCLRPAT_A = PSB_RVDC32(BCLRPAT_A); | 216 | regs->psb.saveBCLRPAT_A = PSB_RVDC32(BCLRPAT_A); |
217 | p->cntr = PSB_RVDC32(DSPACNTR); | 217 | p->cntr = PSB_RVDC32(DSPACNTR); |
218 | p->stride = PSB_RVDC32(DSPASTRIDE); | 218 | p->stride = PSB_RVDC32(DSPASTRIDE); |
219 | p->addr = PSB_RVDC32(DSPABASE); | 219 | p->addr = PSB_RVDC32(DSPABASE); |
220 | p->surf = PSB_RVDC32(DSPASURF); | 220 | p->surf = PSB_RVDC32(DSPASURF); |
221 | p->linoff = PSB_RVDC32(DSPALINOFF); | 221 | p->linoff = PSB_RVDC32(DSPALINOFF); |
222 | p->tileoff = PSB_RVDC32(DSPATILEOFF); | 222 | p->tileoff = PSB_RVDC32(DSPATILEOFF); |
223 | 223 | ||
224 | /* Save cursor regs */ | 224 | /* Save cursor regs */ |
225 | regs->psb.saveDSPACURSOR_CTRL = PSB_RVDC32(CURACNTR); | 225 | regs->psb.saveDSPACURSOR_CTRL = PSB_RVDC32(CURACNTR); |
226 | regs->psb.saveDSPACURSOR_BASE = PSB_RVDC32(CURABASE); | 226 | regs->psb.saveDSPACURSOR_BASE = PSB_RVDC32(CURABASE); |
227 | regs->psb.saveDSPACURSOR_POS = PSB_RVDC32(CURAPOS); | 227 | regs->psb.saveDSPACURSOR_POS = PSB_RVDC32(CURAPOS); |
228 | 228 | ||
229 | /* Save palette (gamma) */ | 229 | /* Save palette (gamma) */ |
230 | for (i = 0; i < 256; i++) | 230 | for (i = 0; i < 256; i++) |
231 | p->palette[i] = PSB_RVDC32(PALETTE_A + (i << 2)); | 231 | p->palette[i] = PSB_RVDC32(PALETTE_A + (i << 2)); |
232 | 232 | ||
233 | if (dev_priv->hdmi_priv) | 233 | if (dev_priv->hdmi_priv) |
234 | oaktrail_hdmi_save(dev); | 234 | oaktrail_hdmi_save(dev); |
235 | 235 | ||
236 | /* Save performance state */ | 236 | /* Save performance state */ |
237 | regs->psb.savePERF_MODE = PSB_RVDC32(MRST_PERF_MODE); | 237 | regs->psb.savePERF_MODE = PSB_RVDC32(MRST_PERF_MODE); |
238 | 238 | ||
239 | /* LVDS state */ | 239 | /* LVDS state */ |
240 | regs->psb.savePP_CONTROL = PSB_RVDC32(PP_CONTROL); | 240 | regs->psb.savePP_CONTROL = PSB_RVDC32(PP_CONTROL); |
241 | regs->psb.savePFIT_PGM_RATIOS = PSB_RVDC32(PFIT_PGM_RATIOS); | 241 | regs->psb.savePFIT_PGM_RATIOS = PSB_RVDC32(PFIT_PGM_RATIOS); |
242 | regs->psb.savePFIT_AUTO_RATIOS = PSB_RVDC32(PFIT_AUTO_RATIOS); | 242 | regs->psb.savePFIT_AUTO_RATIOS = PSB_RVDC32(PFIT_AUTO_RATIOS); |
243 | regs->saveBLC_PWM_CTL = PSB_RVDC32(BLC_PWM_CTL); | 243 | regs->saveBLC_PWM_CTL = PSB_RVDC32(BLC_PWM_CTL); |
244 | regs->saveBLC_PWM_CTL2 = PSB_RVDC32(BLC_PWM_CTL2); | 244 | regs->saveBLC_PWM_CTL2 = PSB_RVDC32(BLC_PWM_CTL2); |
245 | regs->psb.saveLVDS = PSB_RVDC32(LVDS); | 245 | regs->psb.saveLVDS = PSB_RVDC32(LVDS); |
246 | regs->psb.savePFIT_CONTROL = PSB_RVDC32(PFIT_CONTROL); | 246 | regs->psb.savePFIT_CONTROL = PSB_RVDC32(PFIT_CONTROL); |
247 | regs->psb.savePP_ON_DELAYS = PSB_RVDC32(LVDSPP_ON); | 247 | regs->psb.savePP_ON_DELAYS = PSB_RVDC32(LVDSPP_ON); |
248 | regs->psb.savePP_OFF_DELAYS = PSB_RVDC32(LVDSPP_OFF); | 248 | regs->psb.savePP_OFF_DELAYS = PSB_RVDC32(LVDSPP_OFF); |
249 | regs->psb.savePP_DIVISOR = PSB_RVDC32(PP_CYCLE); | 249 | regs->psb.savePP_DIVISOR = PSB_RVDC32(PP_CYCLE); |
250 | 250 | ||
251 | /* HW overlay */ | 251 | /* HW overlay */ |
252 | regs->psb.saveOV_OVADD = PSB_RVDC32(OV_OVADD); | 252 | regs->psb.saveOV_OVADD = PSB_RVDC32(OV_OVADD); |
253 | regs->psb.saveOV_OGAMC0 = PSB_RVDC32(OV_OGAMC0); | 253 | regs->psb.saveOV_OGAMC0 = PSB_RVDC32(OV_OGAMC0); |
254 | regs->psb.saveOV_OGAMC1 = PSB_RVDC32(OV_OGAMC1); | 254 | regs->psb.saveOV_OGAMC1 = PSB_RVDC32(OV_OGAMC1); |
255 | regs->psb.saveOV_OGAMC2 = PSB_RVDC32(OV_OGAMC2); | 255 | regs->psb.saveOV_OGAMC2 = PSB_RVDC32(OV_OGAMC2); |
256 | regs->psb.saveOV_OGAMC3 = PSB_RVDC32(OV_OGAMC3); | 256 | regs->psb.saveOV_OGAMC3 = PSB_RVDC32(OV_OGAMC3); |
257 | regs->psb.saveOV_OGAMC4 = PSB_RVDC32(OV_OGAMC4); | 257 | regs->psb.saveOV_OGAMC4 = PSB_RVDC32(OV_OGAMC4); |
258 | regs->psb.saveOV_OGAMC5 = PSB_RVDC32(OV_OGAMC5); | 258 | regs->psb.saveOV_OGAMC5 = PSB_RVDC32(OV_OGAMC5); |
259 | 259 | ||
260 | /* DPST registers */ | 260 | /* DPST registers */ |
261 | regs->psb.saveHISTOGRAM_INT_CONTROL_REG = | 261 | regs->psb.saveHISTOGRAM_INT_CONTROL_REG = |
262 | PSB_RVDC32(HISTOGRAM_INT_CONTROL); | 262 | PSB_RVDC32(HISTOGRAM_INT_CONTROL); |
263 | regs->psb.saveHISTOGRAM_LOGIC_CONTROL_REG = | 263 | regs->psb.saveHISTOGRAM_LOGIC_CONTROL_REG = |
264 | PSB_RVDC32(HISTOGRAM_LOGIC_CONTROL); | 264 | PSB_RVDC32(HISTOGRAM_LOGIC_CONTROL); |
265 | regs->psb.savePWM_CONTROL_LOGIC = PSB_RVDC32(PWM_CONTROL_LOGIC); | 265 | regs->psb.savePWM_CONTROL_LOGIC = PSB_RVDC32(PWM_CONTROL_LOGIC); |
266 | 266 | ||
267 | if (dev_priv->iLVDS_enable) { | 267 | if (dev_priv->iLVDS_enable) { |
268 | /* Shut down the panel */ | 268 | /* Shut down the panel */ |
269 | PSB_WVDC32(0, PP_CONTROL); | 269 | PSB_WVDC32(0, PP_CONTROL); |
270 | 270 | ||
271 | do { | 271 | do { |
272 | pp_stat = PSB_RVDC32(PP_STATUS); | 272 | pp_stat = PSB_RVDC32(PP_STATUS); |
273 | } while (pp_stat & 0x80000000); | 273 | } while (pp_stat & 0x80000000); |
274 | 274 | ||
275 | /* Turn off the plane */ | 275 | /* Turn off the plane */ |
276 | PSB_WVDC32(0x58000000, DSPACNTR); | 276 | PSB_WVDC32(0x58000000, DSPACNTR); |
277 | /* Trigger the plane disable */ | 277 | /* Trigger the plane disable */ |
278 | PSB_WVDC32(0, DSPASURF); | 278 | PSB_WVDC32(0, DSPASURF); |
279 | 279 | ||
280 | /* Wait ~4 ticks */ | 280 | /* Wait ~4 ticks */ |
281 | msleep(4); | 281 | msleep(4); |
282 | 282 | ||
283 | /* Turn off pipe */ | 283 | /* Turn off pipe */ |
284 | PSB_WVDC32(0x0, PIPEACONF); | 284 | PSB_WVDC32(0x0, PIPEACONF); |
285 | /* Wait ~8 ticks */ | 285 | /* Wait ~8 ticks */ |
286 | msleep(8); | 286 | msleep(8); |
287 | 287 | ||
288 | /* Turn off PLLs */ | 288 | /* Turn off PLLs */ |
289 | PSB_WVDC32(0, MRST_DPLL_A); | 289 | PSB_WVDC32(0, MRST_DPLL_A); |
290 | } | 290 | } |
291 | return 0; | 291 | return 0; |
292 | } | 292 | } |
293 | 293 | ||
294 | /** | 294 | /** |
295 | * oaktrail_restore_display_registers - restore lost register state | 295 | * oaktrail_restore_display_registers - restore lost register state |
296 | * @dev: our DRM device | 296 | * @dev: our DRM device |
297 | * | 297 | * |
298 | * Restore register state that was lost during suspend and resume. | 298 | * Restore register state that was lost during suspend and resume. |
299 | */ | 299 | */ |
300 | static int oaktrail_restore_display_registers(struct drm_device *dev) | 300 | static int oaktrail_restore_display_registers(struct drm_device *dev) |
301 | { | 301 | { |
302 | struct drm_psb_private *dev_priv = dev->dev_private; | 302 | struct drm_psb_private *dev_priv = dev->dev_private; |
303 | struct psb_save_area *regs = &dev_priv->regs; | 303 | struct psb_save_area *regs = &dev_priv->regs; |
304 | struct psb_pipe *p = ®s->pipe[0]; | 304 | struct psb_pipe *p = ®s->pipe[0]; |
305 | u32 pp_stat; | 305 | u32 pp_stat; |
306 | int i; | 306 | int i; |
307 | 307 | ||
308 | /* Display arbitration + watermarks */ | 308 | /* Display arbitration + watermarks */ |
309 | PSB_WVDC32(regs->psb.saveDSPARB, DSPARB); | 309 | PSB_WVDC32(regs->psb.saveDSPARB, DSPARB); |
310 | PSB_WVDC32(regs->psb.saveDSPFW1, DSPFW1); | 310 | PSB_WVDC32(regs->psb.saveDSPFW1, DSPFW1); |
311 | PSB_WVDC32(regs->psb.saveDSPFW2, DSPFW2); | 311 | PSB_WVDC32(regs->psb.saveDSPFW2, DSPFW2); |
312 | PSB_WVDC32(regs->psb.saveDSPFW3, DSPFW3); | 312 | PSB_WVDC32(regs->psb.saveDSPFW3, DSPFW3); |
313 | PSB_WVDC32(regs->psb.saveDSPFW4, DSPFW4); | 313 | PSB_WVDC32(regs->psb.saveDSPFW4, DSPFW4); |
314 | PSB_WVDC32(regs->psb.saveDSPFW5, DSPFW5); | 314 | PSB_WVDC32(regs->psb.saveDSPFW5, DSPFW5); |
315 | PSB_WVDC32(regs->psb.saveDSPFW6, DSPFW6); | 315 | PSB_WVDC32(regs->psb.saveDSPFW6, DSPFW6); |
316 | PSB_WVDC32(regs->psb.saveCHICKENBIT, DSPCHICKENBIT); | 316 | PSB_WVDC32(regs->psb.saveCHICKENBIT, DSPCHICKENBIT); |
317 | 317 | ||
318 | /* Make sure VGA plane is off. it initializes to on after reset!*/ | 318 | /* Make sure VGA plane is off. it initializes to on after reset!*/ |
319 | PSB_WVDC32(0x80000000, VGACNTRL); | 319 | PSB_WVDC32(0x80000000, VGACNTRL); |
320 | 320 | ||
321 | /* set the plls */ | 321 | /* set the plls */ |
322 | PSB_WVDC32(p->fp0, MRST_FPA0); | 322 | PSB_WVDC32(p->fp0, MRST_FPA0); |
323 | PSB_WVDC32(p->fp1, MRST_FPA1); | 323 | PSB_WVDC32(p->fp1, MRST_FPA1); |
324 | 324 | ||
325 | /* Actually enable it */ | 325 | /* Actually enable it */ |
326 | PSB_WVDC32(p->dpll, MRST_DPLL_A); | 326 | PSB_WVDC32(p->dpll, MRST_DPLL_A); |
327 | DRM_UDELAY(150); | 327 | DRM_UDELAY(150); |
328 | 328 | ||
329 | /* Restore mode */ | 329 | /* Restore mode */ |
330 | PSB_WVDC32(p->htotal, HTOTAL_A); | 330 | PSB_WVDC32(p->htotal, HTOTAL_A); |
331 | PSB_WVDC32(p->hblank, HBLANK_A); | 331 | PSB_WVDC32(p->hblank, HBLANK_A); |
332 | PSB_WVDC32(p->hsync, HSYNC_A); | 332 | PSB_WVDC32(p->hsync, HSYNC_A); |
333 | PSB_WVDC32(p->vtotal, VTOTAL_A); | 333 | PSB_WVDC32(p->vtotal, VTOTAL_A); |
334 | PSB_WVDC32(p->vblank, VBLANK_A); | 334 | PSB_WVDC32(p->vblank, VBLANK_A); |
335 | PSB_WVDC32(p->vsync, VSYNC_A); | 335 | PSB_WVDC32(p->vsync, VSYNC_A); |
336 | PSB_WVDC32(p->src, PIPEASRC); | 336 | PSB_WVDC32(p->src, PIPEASRC); |
337 | PSB_WVDC32(regs->psb.saveBCLRPAT_A, BCLRPAT_A); | 337 | PSB_WVDC32(regs->psb.saveBCLRPAT_A, BCLRPAT_A); |
338 | 338 | ||
339 | /* Restore performance mode*/ | 339 | /* Restore performance mode*/ |
340 | PSB_WVDC32(regs->psb.savePERF_MODE, MRST_PERF_MODE); | 340 | PSB_WVDC32(regs->psb.savePERF_MODE, MRST_PERF_MODE); |
341 | 341 | ||
342 | /* Enable the pipe*/ | 342 | /* Enable the pipe*/ |
343 | if (dev_priv->iLVDS_enable) | 343 | if (dev_priv->iLVDS_enable) |
344 | PSB_WVDC32(p->conf, PIPEACONF); | 344 | PSB_WVDC32(p->conf, PIPEACONF); |
345 | 345 | ||
346 | /* Set up the plane*/ | 346 | /* Set up the plane*/ |
347 | PSB_WVDC32(p->linoff, DSPALINOFF); | 347 | PSB_WVDC32(p->linoff, DSPALINOFF); |
348 | PSB_WVDC32(p->stride, DSPASTRIDE); | 348 | PSB_WVDC32(p->stride, DSPASTRIDE); |
349 | PSB_WVDC32(p->tileoff, DSPATILEOFF); | 349 | PSB_WVDC32(p->tileoff, DSPATILEOFF); |
350 | 350 | ||
351 | /* Enable the plane */ | 351 | /* Enable the plane */ |
352 | PSB_WVDC32(p->cntr, DSPACNTR); | 352 | PSB_WVDC32(p->cntr, DSPACNTR); |
353 | PSB_WVDC32(p->surf, DSPASURF); | 353 | PSB_WVDC32(p->surf, DSPASURF); |
354 | 354 | ||
355 | /* Enable Cursor A */ | 355 | /* Enable Cursor A */ |
356 | PSB_WVDC32(regs->psb.saveDSPACURSOR_CTRL, CURACNTR); | 356 | PSB_WVDC32(regs->psb.saveDSPACURSOR_CTRL, CURACNTR); |
357 | PSB_WVDC32(regs->psb.saveDSPACURSOR_POS, CURAPOS); | 357 | PSB_WVDC32(regs->psb.saveDSPACURSOR_POS, CURAPOS); |
358 | PSB_WVDC32(regs->psb.saveDSPACURSOR_BASE, CURABASE); | 358 | PSB_WVDC32(regs->psb.saveDSPACURSOR_BASE, CURABASE); |
359 | 359 | ||
360 | /* Restore palette (gamma) */ | 360 | /* Restore palette (gamma) */ |
361 | for (i = 0; i < 256; i++) | 361 | for (i = 0; i < 256; i++) |
362 | PSB_WVDC32(p->palette[i], PALETTE_A + (i << 2)); | 362 | PSB_WVDC32(p->palette[i], PALETTE_A + (i << 2)); |
363 | 363 | ||
364 | if (dev_priv->hdmi_priv) | 364 | if (dev_priv->hdmi_priv) |
365 | oaktrail_hdmi_restore(dev); | 365 | oaktrail_hdmi_restore(dev); |
366 | 366 | ||
367 | if (dev_priv->iLVDS_enable) { | 367 | if (dev_priv->iLVDS_enable) { |
368 | PSB_WVDC32(regs->saveBLC_PWM_CTL2, BLC_PWM_CTL2); | 368 | PSB_WVDC32(regs->saveBLC_PWM_CTL2, BLC_PWM_CTL2); |
369 | PSB_WVDC32(regs->psb.saveLVDS, LVDS); /*port 61180h*/ | 369 | PSB_WVDC32(regs->psb.saveLVDS, LVDS); /*port 61180h*/ |
370 | PSB_WVDC32(regs->psb.savePFIT_CONTROL, PFIT_CONTROL); | 370 | PSB_WVDC32(regs->psb.savePFIT_CONTROL, PFIT_CONTROL); |
371 | PSB_WVDC32(regs->psb.savePFIT_PGM_RATIOS, PFIT_PGM_RATIOS); | 371 | PSB_WVDC32(regs->psb.savePFIT_PGM_RATIOS, PFIT_PGM_RATIOS); |
372 | PSB_WVDC32(regs->psb.savePFIT_AUTO_RATIOS, PFIT_AUTO_RATIOS); | 372 | PSB_WVDC32(regs->psb.savePFIT_AUTO_RATIOS, PFIT_AUTO_RATIOS); |
373 | PSB_WVDC32(regs->saveBLC_PWM_CTL, BLC_PWM_CTL); | 373 | PSB_WVDC32(regs->saveBLC_PWM_CTL, BLC_PWM_CTL); |
374 | PSB_WVDC32(regs->psb.savePP_ON_DELAYS, LVDSPP_ON); | 374 | PSB_WVDC32(regs->psb.savePP_ON_DELAYS, LVDSPP_ON); |
375 | PSB_WVDC32(regs->psb.savePP_OFF_DELAYS, LVDSPP_OFF); | 375 | PSB_WVDC32(regs->psb.savePP_OFF_DELAYS, LVDSPP_OFF); |
376 | PSB_WVDC32(regs->psb.savePP_DIVISOR, PP_CYCLE); | 376 | PSB_WVDC32(regs->psb.savePP_DIVISOR, PP_CYCLE); |
377 | PSB_WVDC32(regs->psb.savePP_CONTROL, PP_CONTROL); | 377 | PSB_WVDC32(regs->psb.savePP_CONTROL, PP_CONTROL); |
378 | } | 378 | } |
379 | 379 | ||
380 | /* Wait for cycle delay */ | 380 | /* Wait for cycle delay */ |
381 | do { | 381 | do { |
382 | pp_stat = PSB_RVDC32(PP_STATUS); | 382 | pp_stat = PSB_RVDC32(PP_STATUS); |
383 | } while (pp_stat & 0x08000000); | 383 | } while (pp_stat & 0x08000000); |
384 | 384 | ||
385 | /* Wait for panel power up */ | 385 | /* Wait for panel power up */ |
386 | do { | 386 | do { |
387 | pp_stat = PSB_RVDC32(PP_STATUS); | 387 | pp_stat = PSB_RVDC32(PP_STATUS); |
388 | } while (pp_stat & 0x10000000); | 388 | } while (pp_stat & 0x10000000); |
389 | 389 | ||
390 | /* Restore HW overlay */ | 390 | /* Restore HW overlay */ |
391 | PSB_WVDC32(regs->psb.saveOV_OVADD, OV_OVADD); | 391 | PSB_WVDC32(regs->psb.saveOV_OVADD, OV_OVADD); |
392 | PSB_WVDC32(regs->psb.saveOV_OGAMC0, OV_OGAMC0); | 392 | PSB_WVDC32(regs->psb.saveOV_OGAMC0, OV_OGAMC0); |
393 | PSB_WVDC32(regs->psb.saveOV_OGAMC1, OV_OGAMC1); | 393 | PSB_WVDC32(regs->psb.saveOV_OGAMC1, OV_OGAMC1); |
394 | PSB_WVDC32(regs->psb.saveOV_OGAMC2, OV_OGAMC2); | 394 | PSB_WVDC32(regs->psb.saveOV_OGAMC2, OV_OGAMC2); |
395 | PSB_WVDC32(regs->psb.saveOV_OGAMC3, OV_OGAMC3); | 395 | PSB_WVDC32(regs->psb.saveOV_OGAMC3, OV_OGAMC3); |
396 | PSB_WVDC32(regs->psb.saveOV_OGAMC4, OV_OGAMC4); | 396 | PSB_WVDC32(regs->psb.saveOV_OGAMC4, OV_OGAMC4); |
397 | PSB_WVDC32(regs->psb.saveOV_OGAMC5, OV_OGAMC5); | 397 | PSB_WVDC32(regs->psb.saveOV_OGAMC5, OV_OGAMC5); |
398 | 398 | ||
399 | /* DPST registers */ | 399 | /* DPST registers */ |
400 | PSB_WVDC32(regs->psb.saveHISTOGRAM_INT_CONTROL_REG, | 400 | PSB_WVDC32(regs->psb.saveHISTOGRAM_INT_CONTROL_REG, |
401 | HISTOGRAM_INT_CONTROL); | 401 | HISTOGRAM_INT_CONTROL); |
402 | PSB_WVDC32(regs->psb.saveHISTOGRAM_LOGIC_CONTROL_REG, | 402 | PSB_WVDC32(regs->psb.saveHISTOGRAM_LOGIC_CONTROL_REG, |
403 | HISTOGRAM_LOGIC_CONTROL); | 403 | HISTOGRAM_LOGIC_CONTROL); |
404 | PSB_WVDC32(regs->psb.savePWM_CONTROL_LOGIC, PWM_CONTROL_LOGIC); | 404 | PSB_WVDC32(regs->psb.savePWM_CONTROL_LOGIC, PWM_CONTROL_LOGIC); |
405 | 405 | ||
406 | return 0; | 406 | return 0; |
407 | } | 407 | } |
408 | 408 | ||
409 | /** | 409 | /** |
410 | * oaktrail_power_down - power down the display island | 410 | * oaktrail_power_down - power down the display island |
411 | * @dev: our DRM device | 411 | * @dev: our DRM device |
412 | * | 412 | * |
413 | * Power down the display interface of our device | 413 | * Power down the display interface of our device |
414 | */ | 414 | */ |
415 | static int oaktrail_power_down(struct drm_device *dev) | 415 | static int oaktrail_power_down(struct drm_device *dev) |
416 | { | 416 | { |
417 | struct drm_psb_private *dev_priv = dev->dev_private; | 417 | struct drm_psb_private *dev_priv = dev->dev_private; |
418 | u32 pwr_mask ; | 418 | u32 pwr_mask ; |
419 | u32 pwr_sts; | 419 | u32 pwr_sts; |
420 | 420 | ||
421 | pwr_mask = PSB_PWRGT_DISPLAY_MASK; | 421 | pwr_mask = PSB_PWRGT_DISPLAY_MASK; |
422 | outl(pwr_mask, dev_priv->ospm_base + PSB_PM_SSC); | 422 | outl(pwr_mask, dev_priv->ospm_base + PSB_PM_SSC); |
423 | 423 | ||
424 | while (true) { | 424 | while (true) { |
425 | pwr_sts = inl(dev_priv->ospm_base + PSB_PM_SSS); | 425 | pwr_sts = inl(dev_priv->ospm_base + PSB_PM_SSS); |
426 | if ((pwr_sts & pwr_mask) == pwr_mask) | 426 | if ((pwr_sts & pwr_mask) == pwr_mask) |
427 | break; | 427 | break; |
428 | else | 428 | else |
429 | udelay(10); | 429 | udelay(10); |
430 | } | 430 | } |
431 | return 0; | 431 | return 0; |
432 | } | 432 | } |
433 | 433 | ||
434 | /* | 434 | /* |
435 | * oaktrail_power_up | 435 | * oaktrail_power_up |
436 | * | 436 | * |
437 | * Restore power to the specified island(s) (powergating) | 437 | * Restore power to the specified island(s) (powergating) |
438 | */ | 438 | */ |
439 | static int oaktrail_power_up(struct drm_device *dev) | 439 | static int oaktrail_power_up(struct drm_device *dev) |
440 | { | 440 | { |
441 | struct drm_psb_private *dev_priv = dev->dev_private; | 441 | struct drm_psb_private *dev_priv = dev->dev_private; |
442 | u32 pwr_mask = PSB_PWRGT_DISPLAY_MASK; | 442 | u32 pwr_mask = PSB_PWRGT_DISPLAY_MASK; |
443 | u32 pwr_sts, pwr_cnt; | 443 | u32 pwr_sts, pwr_cnt; |
444 | 444 | ||
445 | pwr_cnt = inl(dev_priv->ospm_base + PSB_PM_SSC); | 445 | pwr_cnt = inl(dev_priv->ospm_base + PSB_PM_SSC); |
446 | pwr_cnt &= ~pwr_mask; | 446 | pwr_cnt &= ~pwr_mask; |
447 | outl(pwr_cnt, (dev_priv->ospm_base + PSB_PM_SSC)); | 447 | outl(pwr_cnt, (dev_priv->ospm_base + PSB_PM_SSC)); |
448 | 448 | ||
449 | while (true) { | 449 | while (true) { |
450 | pwr_sts = inl(dev_priv->ospm_base + PSB_PM_SSS); | 450 | pwr_sts = inl(dev_priv->ospm_base + PSB_PM_SSS); |
451 | if ((pwr_sts & pwr_mask) == 0) | 451 | if ((pwr_sts & pwr_mask) == 0) |
452 | break; | 452 | break; |
453 | else | 453 | else |
454 | udelay(10); | 454 | udelay(10); |
455 | } | 455 | } |
456 | return 0; | 456 | return 0; |
457 | } | 457 | } |
458 | 458 | ||
459 | /* Oaktrail */ | 459 | /* Oaktrail */ |
460 | static const struct psb_offset oaktrail_regmap[2] = { | 460 | static const struct psb_offset oaktrail_regmap[2] = { |
461 | { | 461 | { |
462 | .fp0 = MRST_FPA0, | 462 | .fp0 = MRST_FPA0, |
463 | .fp1 = MRST_FPA1, | 463 | .fp1 = MRST_FPA1, |
464 | .cntr = DSPACNTR, | 464 | .cntr = DSPACNTR, |
465 | .conf = PIPEACONF, | 465 | .conf = PIPEACONF, |
466 | .src = PIPEASRC, | 466 | .src = PIPEASRC, |
467 | .dpll = MRST_DPLL_A, | 467 | .dpll = MRST_DPLL_A, |
468 | .htotal = HTOTAL_A, | 468 | .htotal = HTOTAL_A, |
469 | .hblank = HBLANK_A, | 469 | .hblank = HBLANK_A, |
470 | .hsync = HSYNC_A, | 470 | .hsync = HSYNC_A, |
471 | .vtotal = VTOTAL_A, | 471 | .vtotal = VTOTAL_A, |
472 | .vblank = VBLANK_A, | 472 | .vblank = VBLANK_A, |
473 | .vsync = VSYNC_A, | 473 | .vsync = VSYNC_A, |
474 | .stride = DSPASTRIDE, | 474 | .stride = DSPASTRIDE, |
475 | .size = DSPASIZE, | 475 | .size = DSPASIZE, |
476 | .pos = DSPAPOS, | 476 | .pos = DSPAPOS, |
477 | .surf = DSPASURF, | 477 | .surf = DSPASURF, |
478 | .addr = MRST_DSPABASE, | 478 | .addr = MRST_DSPABASE, |
479 | .base = MRST_DSPABASE, | 479 | .base = MRST_DSPABASE, |
480 | .status = PIPEASTAT, | 480 | .status = PIPEASTAT, |
481 | .linoff = DSPALINOFF, | 481 | .linoff = DSPALINOFF, |
482 | .tileoff = DSPATILEOFF, | 482 | .tileoff = DSPATILEOFF, |
483 | .palette = PALETTE_A, | 483 | .palette = PALETTE_A, |
484 | }, | 484 | }, |
485 | { | 485 | { |
486 | .fp0 = FPB0, | 486 | .fp0 = FPB0, |
487 | .fp1 = FPB1, | 487 | .fp1 = FPB1, |
488 | .cntr = DSPBCNTR, | 488 | .cntr = DSPBCNTR, |
489 | .conf = PIPEBCONF, | 489 | .conf = PIPEBCONF, |
490 | .src = PIPEBSRC, | 490 | .src = PIPEBSRC, |
491 | .dpll = DPLL_B, | 491 | .dpll = DPLL_B, |
492 | .htotal = HTOTAL_B, | 492 | .htotal = HTOTAL_B, |
493 | .hblank = HBLANK_B, | 493 | .hblank = HBLANK_B, |
494 | .hsync = HSYNC_B, | 494 | .hsync = HSYNC_B, |
495 | .vtotal = VTOTAL_B, | 495 | .vtotal = VTOTAL_B, |
496 | .vblank = VBLANK_B, | 496 | .vblank = VBLANK_B, |
497 | .vsync = VSYNC_B, | 497 | .vsync = VSYNC_B, |
498 | .stride = DSPBSTRIDE, | 498 | .stride = DSPBSTRIDE, |
499 | .size = DSPBSIZE, | 499 | .size = DSPBSIZE, |
500 | .pos = DSPBPOS, | 500 | .pos = DSPBPOS, |
501 | .surf = DSPBSURF, | 501 | .surf = DSPBSURF, |
502 | .addr = DSPBBASE, | 502 | .addr = DSPBBASE, |
503 | .base = DSPBBASE, | 503 | .base = DSPBBASE, |
504 | .status = PIPEBSTAT, | 504 | .status = PIPEBSTAT, |
505 | .linoff = DSPBLINOFF, | 505 | .linoff = DSPBLINOFF, |
506 | .tileoff = DSPBTILEOFF, | 506 | .tileoff = DSPBTILEOFF, |
507 | .palette = PALETTE_B, | 507 | .palette = PALETTE_B, |
508 | }, | 508 | }, |
509 | }; | 509 | }; |
510 | 510 | ||
511 | static int oaktrail_chip_setup(struct drm_device *dev) | 511 | static int oaktrail_chip_setup(struct drm_device *dev) |
512 | { | 512 | { |
513 | struct drm_psb_private *dev_priv = dev->dev_private; | 513 | struct drm_psb_private *dev_priv = dev->dev_private; |
514 | int ret; | 514 | int ret; |
515 | 515 | ||
516 | if (pci_enable_msi(dev->pdev)) | 516 | if (pci_enable_msi(dev->pdev)) |
517 | dev_warn(dev->dev, "Enabling MSI failed!\n"); | 517 | dev_warn(dev->dev, "Enabling MSI failed!\n"); |
518 | 518 | ||
519 | dev_priv->regmap = oaktrail_regmap; | 519 | dev_priv->regmap = oaktrail_regmap; |
520 | 520 | ||
521 | ret = mid_chip_setup(dev); | 521 | ret = mid_chip_setup(dev); |
522 | if (ret < 0) | 522 | if (ret < 0) |
523 | return ret; | 523 | return ret; |
524 | if (!dev_priv->has_gct) { | 524 | if (!dev_priv->has_gct) { |
525 | /* Now pull the BIOS data */ | 525 | /* Now pull the BIOS data */ |
526 | psb_intel_opregion_init(dev); | 526 | psb_intel_opregion_init(dev); |
527 | psb_intel_init_bios(dev); | 527 | psb_intel_init_bios(dev); |
528 | } | 528 | } |
529 | oaktrail_hdmi_setup(dev); | 529 | oaktrail_hdmi_setup(dev); |
530 | return 0; | 530 | return 0; |
531 | } | 531 | } |
532 | 532 | ||
533 | static void oaktrail_teardown(struct drm_device *dev) | 533 | static void oaktrail_teardown(struct drm_device *dev) |
534 | { | 534 | { |
535 | struct drm_psb_private *dev_priv = dev->dev_private; | 535 | struct drm_psb_private *dev_priv = dev->dev_private; |
536 | 536 | ||
537 | oaktrail_hdmi_teardown(dev); | 537 | oaktrail_hdmi_teardown(dev); |
538 | if (!dev_priv->has_gct) | 538 | if (!dev_priv->has_gct) |
539 | psb_intel_destroy_bios(dev); | 539 | psb_intel_destroy_bios(dev); |
540 | } | 540 | } |
541 | 541 | ||
542 | const struct psb_ops oaktrail_chip_ops = { | 542 | const struct psb_ops oaktrail_chip_ops = { |
543 | .name = "Oaktrail", | 543 | .name = "Oaktrail", |
544 | .accel_2d = 1, | 544 | .accel_2d = 1, |
545 | .pipes = 2, | 545 | .pipes = 2, |
546 | .crtcs = 2, | 546 | .crtcs = 2, |
547 | .hdmi_mask = (1 << 1), | 547 | .hdmi_mask = (1 << 1), |
548 | .lvds_mask = (1 << 0), | 548 | .lvds_mask = (1 << 0), |
549 | .cursor_needs_phys = 0, | 549 | .cursor_needs_phys = 0, |
550 | .sgx_offset = MRST_SGX_OFFSET, | 550 | .sgx_offset = MRST_SGX_OFFSET, |
551 | 551 | ||
552 | .chip_setup = oaktrail_chip_setup, | 552 | .chip_setup = oaktrail_chip_setup, |
553 | .chip_teardown = oaktrail_teardown, | 553 | .chip_teardown = oaktrail_teardown, |
554 | .crtc_helper = &oaktrail_helper_funcs, | 554 | .crtc_helper = &oaktrail_helper_funcs, |
555 | .crtc_funcs = &psb_intel_crtc_funcs, | 555 | .crtc_funcs = &psb_intel_crtc_funcs, |
556 | 556 | ||
557 | .output_init = oaktrail_output_init, | 557 | .output_init = oaktrail_output_init, |
558 | 558 | ||
559 | #ifdef CONFIG_BACKLIGHT_CLASS_DEVICE | 559 | #ifdef CONFIG_BACKLIGHT_CLASS_DEVICE |
560 | .backlight_init = oaktrail_backlight_init, | 560 | .backlight_init = oaktrail_backlight_init, |
561 | #endif | 561 | #endif |
562 | 562 | ||
563 | .save_regs = oaktrail_save_display_registers, | 563 | .save_regs = oaktrail_save_display_registers, |
564 | .restore_regs = oaktrail_restore_display_registers, | 564 | .restore_regs = oaktrail_restore_display_registers, |
565 | .power_down = oaktrail_power_down, | 565 | .power_down = oaktrail_power_down, |
566 | .power_up = oaktrail_power_up, | 566 | .power_up = oaktrail_power_up, |
567 | 567 | ||
568 | .i2c_bus = 1, | 568 | .i2c_bus = 1, |
569 | }; | 569 | }; |
570 | 570 |
drivers/gpu/drm/gma500/oaktrail_lvds.c
1 | /* | 1 | /* |
2 | * Copyright ยฉ 2006-2009 Intel Corporation | 2 | * Copyright ยฉ 2006-2009 Intel Corporation |
3 | * | 3 | * |
4 | * This program is free software; you can redistribute it and/or modify it | 4 | * This program is free software; you can redistribute it and/or modify it |
5 | * under the terms and conditions of the GNU General Public License, | 5 | * under the terms and conditions of the GNU General Public License, |
6 | * version 2, as published by the Free Software Foundation. | 6 | * version 2, as published by the Free Software Foundation. |
7 | * | 7 | * |
8 | * This program is distributed in the hope it will be useful, but WITHOUT | 8 | * This program is distributed in the hope it will be useful, but WITHOUT |
9 | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or | 9 | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or |
10 | * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for | 10 | * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for |
11 | * more details. | 11 | * more details. |
12 | * | 12 | * |
13 | * You should have received a copy of the GNU General Public License along with | 13 | * You should have received a copy of the GNU General Public License along with |
14 | * this program; if not, write to the Free Software Foundation, Inc., | 14 | * this program; if not, write to the Free Software Foundation, Inc., |
15 | * 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA. | 15 | * 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA. |
16 | * | 16 | * |
17 | * Authors: | 17 | * Authors: |
18 | * Eric Anholt <eric@anholt.net> | 18 | * Eric Anholt <eric@anholt.net> |
19 | * Dave Airlie <airlied@linux.ie> | 19 | * Dave Airlie <airlied@linux.ie> |
20 | * Jesse Barnes <jesse.barnes@intel.com> | 20 | * Jesse Barnes <jesse.barnes@intel.com> |
21 | */ | 21 | */ |
22 | 22 | ||
23 | #include <linux/i2c.h> | 23 | #include <linux/i2c.h> |
24 | #include <drm/drmP.h> | 24 | #include <drm/drmP.h> |
25 | #include <asm/mrst.h> | 25 | #include <asm/intel-mid.h> |
26 | 26 | ||
27 | #include "intel_bios.h" | 27 | #include "intel_bios.h" |
28 | #include "psb_drv.h" | 28 | #include "psb_drv.h" |
29 | #include "psb_intel_drv.h" | 29 | #include "psb_intel_drv.h" |
30 | #include "psb_intel_reg.h" | 30 | #include "psb_intel_reg.h" |
31 | #include "power.h" | 31 | #include "power.h" |
32 | #include <linux/pm_runtime.h> | 32 | #include <linux/pm_runtime.h> |
33 | 33 | ||
34 | /* The max/min PWM frequency in BPCR[31:17] - */ | 34 | /* The max/min PWM frequency in BPCR[31:17] - */ |
35 | /* The smallest number is 1 (not 0) that can fit in the | 35 | /* The smallest number is 1 (not 0) that can fit in the |
36 | * 15-bit field of the and then*/ | 36 | * 15-bit field of the and then*/ |
37 | /* shifts to the left by one bit to get the actual 16-bit | 37 | /* shifts to the left by one bit to get the actual 16-bit |
38 | * value that the 15-bits correspond to.*/ | 38 | * value that the 15-bits correspond to.*/ |
39 | #define MRST_BLC_MAX_PWM_REG_FREQ 0xFFFF | 39 | #define MRST_BLC_MAX_PWM_REG_FREQ 0xFFFF |
40 | #define BRIGHTNESS_MAX_LEVEL 100 | 40 | #define BRIGHTNESS_MAX_LEVEL 100 |
41 | 41 | ||
42 | /** | 42 | /** |
43 | * Sets the power state for the panel. | 43 | * Sets the power state for the panel. |
44 | */ | 44 | */ |
45 | static void oaktrail_lvds_set_power(struct drm_device *dev, | 45 | static void oaktrail_lvds_set_power(struct drm_device *dev, |
46 | struct gma_encoder *gma_encoder, | 46 | struct gma_encoder *gma_encoder, |
47 | bool on) | 47 | bool on) |
48 | { | 48 | { |
49 | u32 pp_status; | 49 | u32 pp_status; |
50 | struct drm_psb_private *dev_priv = dev->dev_private; | 50 | struct drm_psb_private *dev_priv = dev->dev_private; |
51 | 51 | ||
52 | if (!gma_power_begin(dev, true)) | 52 | if (!gma_power_begin(dev, true)) |
53 | return; | 53 | return; |
54 | 54 | ||
55 | if (on) { | 55 | if (on) { |
56 | REG_WRITE(PP_CONTROL, REG_READ(PP_CONTROL) | | 56 | REG_WRITE(PP_CONTROL, REG_READ(PP_CONTROL) | |
57 | POWER_TARGET_ON); | 57 | POWER_TARGET_ON); |
58 | do { | 58 | do { |
59 | pp_status = REG_READ(PP_STATUS); | 59 | pp_status = REG_READ(PP_STATUS); |
60 | } while ((pp_status & (PP_ON | PP_READY)) == PP_READY); | 60 | } while ((pp_status & (PP_ON | PP_READY)) == PP_READY); |
61 | dev_priv->is_lvds_on = true; | 61 | dev_priv->is_lvds_on = true; |
62 | if (dev_priv->ops->lvds_bl_power) | 62 | if (dev_priv->ops->lvds_bl_power) |
63 | dev_priv->ops->lvds_bl_power(dev, true); | 63 | dev_priv->ops->lvds_bl_power(dev, true); |
64 | } else { | 64 | } else { |
65 | if (dev_priv->ops->lvds_bl_power) | 65 | if (dev_priv->ops->lvds_bl_power) |
66 | dev_priv->ops->lvds_bl_power(dev, false); | 66 | dev_priv->ops->lvds_bl_power(dev, false); |
67 | REG_WRITE(PP_CONTROL, REG_READ(PP_CONTROL) & | 67 | REG_WRITE(PP_CONTROL, REG_READ(PP_CONTROL) & |
68 | ~POWER_TARGET_ON); | 68 | ~POWER_TARGET_ON); |
69 | do { | 69 | do { |
70 | pp_status = REG_READ(PP_STATUS); | 70 | pp_status = REG_READ(PP_STATUS); |
71 | } while (pp_status & PP_ON); | 71 | } while (pp_status & PP_ON); |
72 | dev_priv->is_lvds_on = false; | 72 | dev_priv->is_lvds_on = false; |
73 | pm_request_idle(&dev->pdev->dev); | 73 | pm_request_idle(&dev->pdev->dev); |
74 | } | 74 | } |
75 | gma_power_end(dev); | 75 | gma_power_end(dev); |
76 | } | 76 | } |
77 | 77 | ||
78 | static void oaktrail_lvds_dpms(struct drm_encoder *encoder, int mode) | 78 | static void oaktrail_lvds_dpms(struct drm_encoder *encoder, int mode) |
79 | { | 79 | { |
80 | struct drm_device *dev = encoder->dev; | 80 | struct drm_device *dev = encoder->dev; |
81 | struct gma_encoder *gma_encoder = to_gma_encoder(encoder); | 81 | struct gma_encoder *gma_encoder = to_gma_encoder(encoder); |
82 | 82 | ||
83 | if (mode == DRM_MODE_DPMS_ON) | 83 | if (mode == DRM_MODE_DPMS_ON) |
84 | oaktrail_lvds_set_power(dev, gma_encoder, true); | 84 | oaktrail_lvds_set_power(dev, gma_encoder, true); |
85 | else | 85 | else |
86 | oaktrail_lvds_set_power(dev, gma_encoder, false); | 86 | oaktrail_lvds_set_power(dev, gma_encoder, false); |
87 | 87 | ||
88 | /* XXX: We never power down the LVDS pairs. */ | 88 | /* XXX: We never power down the LVDS pairs. */ |
89 | } | 89 | } |
90 | 90 | ||
91 | static void oaktrail_lvds_mode_set(struct drm_encoder *encoder, | 91 | static void oaktrail_lvds_mode_set(struct drm_encoder *encoder, |
92 | struct drm_display_mode *mode, | 92 | struct drm_display_mode *mode, |
93 | struct drm_display_mode *adjusted_mode) | 93 | struct drm_display_mode *adjusted_mode) |
94 | { | 94 | { |
95 | struct drm_device *dev = encoder->dev; | 95 | struct drm_device *dev = encoder->dev; |
96 | struct drm_psb_private *dev_priv = dev->dev_private; | 96 | struct drm_psb_private *dev_priv = dev->dev_private; |
97 | struct psb_intel_mode_device *mode_dev = &dev_priv->mode_dev; | 97 | struct psb_intel_mode_device *mode_dev = &dev_priv->mode_dev; |
98 | struct drm_mode_config *mode_config = &dev->mode_config; | 98 | struct drm_mode_config *mode_config = &dev->mode_config; |
99 | struct drm_connector *connector = NULL; | 99 | struct drm_connector *connector = NULL; |
100 | struct drm_crtc *crtc = encoder->crtc; | 100 | struct drm_crtc *crtc = encoder->crtc; |
101 | u32 lvds_port; | 101 | u32 lvds_port; |
102 | uint64_t v = DRM_MODE_SCALE_FULLSCREEN; | 102 | uint64_t v = DRM_MODE_SCALE_FULLSCREEN; |
103 | 103 | ||
104 | if (!gma_power_begin(dev, true)) | 104 | if (!gma_power_begin(dev, true)) |
105 | return; | 105 | return; |
106 | 106 | ||
107 | /* | 107 | /* |
108 | * The LVDS pin pair will already have been turned on in the | 108 | * The LVDS pin pair will already have been turned on in the |
109 | * psb_intel_crtc_mode_set since it has a large impact on the DPLL | 109 | * psb_intel_crtc_mode_set since it has a large impact on the DPLL |
110 | * settings. | 110 | * settings. |
111 | */ | 111 | */ |
112 | lvds_port = (REG_READ(LVDS) & | 112 | lvds_port = (REG_READ(LVDS) & |
113 | (~LVDS_PIPEB_SELECT)) | | 113 | (~LVDS_PIPEB_SELECT)) | |
114 | LVDS_PORT_EN | | 114 | LVDS_PORT_EN | |
115 | LVDS_BORDER_EN; | 115 | LVDS_BORDER_EN; |
116 | 116 | ||
117 | /* If the firmware says dither on Moorestown, or the BIOS does | 117 | /* If the firmware says dither on Moorestown, or the BIOS does |
118 | on Oaktrail then enable dithering */ | 118 | on Oaktrail then enable dithering */ |
119 | if (mode_dev->panel_wants_dither || dev_priv->lvds_dither) | 119 | if (mode_dev->panel_wants_dither || dev_priv->lvds_dither) |
120 | lvds_port |= MRST_PANEL_8TO6_DITHER_ENABLE; | 120 | lvds_port |= MRST_PANEL_8TO6_DITHER_ENABLE; |
121 | 121 | ||
122 | REG_WRITE(LVDS, lvds_port); | 122 | REG_WRITE(LVDS, lvds_port); |
123 | 123 | ||
124 | /* Find the connector we're trying to set up */ | 124 | /* Find the connector we're trying to set up */ |
125 | list_for_each_entry(connector, &mode_config->connector_list, head) { | 125 | list_for_each_entry(connector, &mode_config->connector_list, head) { |
126 | if (!connector->encoder || connector->encoder->crtc != crtc) | 126 | if (!connector->encoder || connector->encoder->crtc != crtc) |
127 | continue; | 127 | continue; |
128 | } | 128 | } |
129 | 129 | ||
130 | if (!connector) { | 130 | if (!connector) { |
131 | DRM_ERROR("Couldn't find connector when setting mode"); | 131 | DRM_ERROR("Couldn't find connector when setting mode"); |
132 | return; | 132 | return; |
133 | } | 133 | } |
134 | 134 | ||
135 | drm_object_property_get_value( | 135 | drm_object_property_get_value( |
136 | &connector->base, | 136 | &connector->base, |
137 | dev->mode_config.scaling_mode_property, | 137 | dev->mode_config.scaling_mode_property, |
138 | &v); | 138 | &v); |
139 | 139 | ||
140 | if (v == DRM_MODE_SCALE_NO_SCALE) | 140 | if (v == DRM_MODE_SCALE_NO_SCALE) |
141 | REG_WRITE(PFIT_CONTROL, 0); | 141 | REG_WRITE(PFIT_CONTROL, 0); |
142 | else if (v == DRM_MODE_SCALE_ASPECT) { | 142 | else if (v == DRM_MODE_SCALE_ASPECT) { |
143 | if ((mode->vdisplay != adjusted_mode->crtc_vdisplay) || | 143 | if ((mode->vdisplay != adjusted_mode->crtc_vdisplay) || |
144 | (mode->hdisplay != adjusted_mode->crtc_hdisplay)) { | 144 | (mode->hdisplay != adjusted_mode->crtc_hdisplay)) { |
145 | if ((adjusted_mode->crtc_hdisplay * mode->vdisplay) == | 145 | if ((adjusted_mode->crtc_hdisplay * mode->vdisplay) == |
146 | (mode->hdisplay * adjusted_mode->crtc_vdisplay)) | 146 | (mode->hdisplay * adjusted_mode->crtc_vdisplay)) |
147 | REG_WRITE(PFIT_CONTROL, PFIT_ENABLE); | 147 | REG_WRITE(PFIT_CONTROL, PFIT_ENABLE); |
148 | else if ((adjusted_mode->crtc_hdisplay * | 148 | else if ((adjusted_mode->crtc_hdisplay * |
149 | mode->vdisplay) > (mode->hdisplay * | 149 | mode->vdisplay) > (mode->hdisplay * |
150 | adjusted_mode->crtc_vdisplay)) | 150 | adjusted_mode->crtc_vdisplay)) |
151 | REG_WRITE(PFIT_CONTROL, PFIT_ENABLE | | 151 | REG_WRITE(PFIT_CONTROL, PFIT_ENABLE | |
152 | PFIT_SCALING_MODE_PILLARBOX); | 152 | PFIT_SCALING_MODE_PILLARBOX); |
153 | else | 153 | else |
154 | REG_WRITE(PFIT_CONTROL, PFIT_ENABLE | | 154 | REG_WRITE(PFIT_CONTROL, PFIT_ENABLE | |
155 | PFIT_SCALING_MODE_LETTERBOX); | 155 | PFIT_SCALING_MODE_LETTERBOX); |
156 | } else | 156 | } else |
157 | REG_WRITE(PFIT_CONTROL, PFIT_ENABLE); | 157 | REG_WRITE(PFIT_CONTROL, PFIT_ENABLE); |
158 | } else /*(v == DRM_MODE_SCALE_FULLSCREEN)*/ | 158 | } else /*(v == DRM_MODE_SCALE_FULLSCREEN)*/ |
159 | REG_WRITE(PFIT_CONTROL, PFIT_ENABLE); | 159 | REG_WRITE(PFIT_CONTROL, PFIT_ENABLE); |
160 | 160 | ||
161 | gma_power_end(dev); | 161 | gma_power_end(dev); |
162 | } | 162 | } |
163 | 163 | ||
164 | static void oaktrail_lvds_prepare(struct drm_encoder *encoder) | 164 | static void oaktrail_lvds_prepare(struct drm_encoder *encoder) |
165 | { | 165 | { |
166 | struct drm_device *dev = encoder->dev; | 166 | struct drm_device *dev = encoder->dev; |
167 | struct drm_psb_private *dev_priv = dev->dev_private; | 167 | struct drm_psb_private *dev_priv = dev->dev_private; |
168 | struct gma_encoder *gma_encoder = to_gma_encoder(encoder); | 168 | struct gma_encoder *gma_encoder = to_gma_encoder(encoder); |
169 | struct psb_intel_mode_device *mode_dev = &dev_priv->mode_dev; | 169 | struct psb_intel_mode_device *mode_dev = &dev_priv->mode_dev; |
170 | 170 | ||
171 | if (!gma_power_begin(dev, true)) | 171 | if (!gma_power_begin(dev, true)) |
172 | return; | 172 | return; |
173 | 173 | ||
174 | mode_dev->saveBLC_PWM_CTL = REG_READ(BLC_PWM_CTL); | 174 | mode_dev->saveBLC_PWM_CTL = REG_READ(BLC_PWM_CTL); |
175 | mode_dev->backlight_duty_cycle = (mode_dev->saveBLC_PWM_CTL & | 175 | mode_dev->backlight_duty_cycle = (mode_dev->saveBLC_PWM_CTL & |
176 | BACKLIGHT_DUTY_CYCLE_MASK); | 176 | BACKLIGHT_DUTY_CYCLE_MASK); |
177 | oaktrail_lvds_set_power(dev, gma_encoder, false); | 177 | oaktrail_lvds_set_power(dev, gma_encoder, false); |
178 | gma_power_end(dev); | 178 | gma_power_end(dev); |
179 | } | 179 | } |
180 | 180 | ||
181 | static u32 oaktrail_lvds_get_max_backlight(struct drm_device *dev) | 181 | static u32 oaktrail_lvds_get_max_backlight(struct drm_device *dev) |
182 | { | 182 | { |
183 | struct drm_psb_private *dev_priv = dev->dev_private; | 183 | struct drm_psb_private *dev_priv = dev->dev_private; |
184 | u32 ret; | 184 | u32 ret; |
185 | 185 | ||
186 | if (gma_power_begin(dev, false)) { | 186 | if (gma_power_begin(dev, false)) { |
187 | ret = ((REG_READ(BLC_PWM_CTL) & | 187 | ret = ((REG_READ(BLC_PWM_CTL) & |
188 | BACKLIGHT_MODULATION_FREQ_MASK) >> | 188 | BACKLIGHT_MODULATION_FREQ_MASK) >> |
189 | BACKLIGHT_MODULATION_FREQ_SHIFT) * 2; | 189 | BACKLIGHT_MODULATION_FREQ_SHIFT) * 2; |
190 | 190 | ||
191 | gma_power_end(dev); | 191 | gma_power_end(dev); |
192 | } else | 192 | } else |
193 | ret = ((dev_priv->regs.saveBLC_PWM_CTL & | 193 | ret = ((dev_priv->regs.saveBLC_PWM_CTL & |
194 | BACKLIGHT_MODULATION_FREQ_MASK) >> | 194 | BACKLIGHT_MODULATION_FREQ_MASK) >> |
195 | BACKLIGHT_MODULATION_FREQ_SHIFT) * 2; | 195 | BACKLIGHT_MODULATION_FREQ_SHIFT) * 2; |
196 | 196 | ||
197 | return ret; | 197 | return ret; |
198 | } | 198 | } |
199 | 199 | ||
200 | static void oaktrail_lvds_commit(struct drm_encoder *encoder) | 200 | static void oaktrail_lvds_commit(struct drm_encoder *encoder) |
201 | { | 201 | { |
202 | struct drm_device *dev = encoder->dev; | 202 | struct drm_device *dev = encoder->dev; |
203 | struct drm_psb_private *dev_priv = dev->dev_private; | 203 | struct drm_psb_private *dev_priv = dev->dev_private; |
204 | struct gma_encoder *gma_encoder = to_gma_encoder(encoder); | 204 | struct gma_encoder *gma_encoder = to_gma_encoder(encoder); |
205 | struct psb_intel_mode_device *mode_dev = &dev_priv->mode_dev; | 205 | struct psb_intel_mode_device *mode_dev = &dev_priv->mode_dev; |
206 | 206 | ||
207 | if (mode_dev->backlight_duty_cycle == 0) | 207 | if (mode_dev->backlight_duty_cycle == 0) |
208 | mode_dev->backlight_duty_cycle = | 208 | mode_dev->backlight_duty_cycle = |
209 | oaktrail_lvds_get_max_backlight(dev); | 209 | oaktrail_lvds_get_max_backlight(dev); |
210 | oaktrail_lvds_set_power(dev, gma_encoder, true); | 210 | oaktrail_lvds_set_power(dev, gma_encoder, true); |
211 | } | 211 | } |
212 | 212 | ||
213 | static const struct drm_encoder_helper_funcs oaktrail_lvds_helper_funcs = { | 213 | static const struct drm_encoder_helper_funcs oaktrail_lvds_helper_funcs = { |
214 | .dpms = oaktrail_lvds_dpms, | 214 | .dpms = oaktrail_lvds_dpms, |
215 | .mode_fixup = psb_intel_lvds_mode_fixup, | 215 | .mode_fixup = psb_intel_lvds_mode_fixup, |
216 | .prepare = oaktrail_lvds_prepare, | 216 | .prepare = oaktrail_lvds_prepare, |
217 | .mode_set = oaktrail_lvds_mode_set, | 217 | .mode_set = oaktrail_lvds_mode_set, |
218 | .commit = oaktrail_lvds_commit, | 218 | .commit = oaktrail_lvds_commit, |
219 | }; | 219 | }; |
220 | 220 | ||
221 | static struct drm_display_mode lvds_configuration_modes[] = { | 221 | static struct drm_display_mode lvds_configuration_modes[] = { |
222 | /* hard coded fixed mode for TPO LTPS LPJ040K001A */ | 222 | /* hard coded fixed mode for TPO LTPS LPJ040K001A */ |
223 | { DRM_MODE("800x480", DRM_MODE_TYPE_DRIVER, 33264, 800, 836, | 223 | { DRM_MODE("800x480", DRM_MODE_TYPE_DRIVER, 33264, 800, 836, |
224 | 846, 1056, 0, 480, 489, 491, 525, 0, 0) }, | 224 | 846, 1056, 0, 480, 489, 491, 525, 0, 0) }, |
225 | /* hard coded fixed mode for LVDS 800x480 */ | 225 | /* hard coded fixed mode for LVDS 800x480 */ |
226 | { DRM_MODE("800x480", DRM_MODE_TYPE_DRIVER, 30994, 800, 801, | 226 | { DRM_MODE("800x480", DRM_MODE_TYPE_DRIVER, 30994, 800, 801, |
227 | 802, 1024, 0, 480, 481, 482, 525, 0, 0) }, | 227 | 802, 1024, 0, 480, 481, 482, 525, 0, 0) }, |
228 | /* hard coded fixed mode for Samsung 480wsvga LVDS 1024x600@75 */ | 228 | /* hard coded fixed mode for Samsung 480wsvga LVDS 1024x600@75 */ |
229 | { DRM_MODE("1024x600", DRM_MODE_TYPE_DRIVER, 53990, 1024, 1072, | 229 | { DRM_MODE("1024x600", DRM_MODE_TYPE_DRIVER, 53990, 1024, 1072, |
230 | 1104, 1184, 0, 600, 603, 604, 608, 0, 0) }, | 230 | 1104, 1184, 0, 600, 603, 604, 608, 0, 0) }, |
231 | /* hard coded fixed mode for Samsung 480wsvga LVDS 1024x600@75 */ | 231 | /* hard coded fixed mode for Samsung 480wsvga LVDS 1024x600@75 */ |
232 | { DRM_MODE("1024x600", DRM_MODE_TYPE_DRIVER, 53990, 1024, 1104, | 232 | { DRM_MODE("1024x600", DRM_MODE_TYPE_DRIVER, 53990, 1024, 1104, |
233 | 1136, 1184, 0, 600, 603, 604, 608, 0, 0) }, | 233 | 1136, 1184, 0, 600, 603, 604, 608, 0, 0) }, |
234 | /* hard coded fixed mode for Sharp wsvga LVDS 1024x600 */ | 234 | /* hard coded fixed mode for Sharp wsvga LVDS 1024x600 */ |
235 | { DRM_MODE("1024x600", DRM_MODE_TYPE_DRIVER, 48885, 1024, 1124, | 235 | { DRM_MODE("1024x600", DRM_MODE_TYPE_DRIVER, 48885, 1024, 1124, |
236 | 1204, 1312, 0, 600, 607, 610, 621, 0, 0) }, | 236 | 1204, 1312, 0, 600, 607, 610, 621, 0, 0) }, |
237 | /* hard coded fixed mode for LVDS 1024x768 */ | 237 | /* hard coded fixed mode for LVDS 1024x768 */ |
238 | { DRM_MODE("1024x768", DRM_MODE_TYPE_DRIVER, 65000, 1024, 1048, | 238 | { DRM_MODE("1024x768", DRM_MODE_TYPE_DRIVER, 65000, 1024, 1048, |
239 | 1184, 1344, 0, 768, 771, 777, 806, 0, 0) }, | 239 | 1184, 1344, 0, 768, 771, 777, 806, 0, 0) }, |
240 | /* hard coded fixed mode for LVDS 1366x768 */ | 240 | /* hard coded fixed mode for LVDS 1366x768 */ |
241 | { DRM_MODE("1366x768", DRM_MODE_TYPE_DRIVER, 77500, 1366, 1430, | 241 | { DRM_MODE("1366x768", DRM_MODE_TYPE_DRIVER, 77500, 1366, 1430, |
242 | 1558, 1664, 0, 768, 769, 770, 776, 0, 0) }, | 242 | 1558, 1664, 0, 768, 769, 770, 776, 0, 0) }, |
243 | }; | 243 | }; |
244 | 244 | ||
245 | /* Returns the panel fixed mode from configuration. */ | 245 | /* Returns the panel fixed mode from configuration. */ |
246 | 246 | ||
247 | static void oaktrail_lvds_get_configuration_mode(struct drm_device *dev, | 247 | static void oaktrail_lvds_get_configuration_mode(struct drm_device *dev, |
248 | struct psb_intel_mode_device *mode_dev) | 248 | struct psb_intel_mode_device *mode_dev) |
249 | { | 249 | { |
250 | struct drm_display_mode *mode = NULL; | 250 | struct drm_display_mode *mode = NULL; |
251 | struct drm_psb_private *dev_priv = dev->dev_private; | 251 | struct drm_psb_private *dev_priv = dev->dev_private; |
252 | struct oaktrail_timing_info *ti = &dev_priv->gct_data.DTD; | 252 | struct oaktrail_timing_info *ti = &dev_priv->gct_data.DTD; |
253 | 253 | ||
254 | mode_dev->panel_fixed_mode = NULL; | 254 | mode_dev->panel_fixed_mode = NULL; |
255 | 255 | ||
256 | /* Use the firmware provided data on Moorestown */ | 256 | /* Use the firmware provided data on Moorestown */ |
257 | if (dev_priv->has_gct) { | 257 | if (dev_priv->has_gct) { |
258 | mode = kzalloc(sizeof(*mode), GFP_KERNEL); | 258 | mode = kzalloc(sizeof(*mode), GFP_KERNEL); |
259 | if (!mode) | 259 | if (!mode) |
260 | return; | 260 | return; |
261 | 261 | ||
262 | mode->hdisplay = (ti->hactive_hi << 8) | ti->hactive_lo; | 262 | mode->hdisplay = (ti->hactive_hi << 8) | ti->hactive_lo; |
263 | mode->vdisplay = (ti->vactive_hi << 8) | ti->vactive_lo; | 263 | mode->vdisplay = (ti->vactive_hi << 8) | ti->vactive_lo; |
264 | mode->hsync_start = mode->hdisplay + \ | 264 | mode->hsync_start = mode->hdisplay + \ |
265 | ((ti->hsync_offset_hi << 8) | \ | 265 | ((ti->hsync_offset_hi << 8) | \ |
266 | ti->hsync_offset_lo); | 266 | ti->hsync_offset_lo); |
267 | mode->hsync_end = mode->hsync_start + \ | 267 | mode->hsync_end = mode->hsync_start + \ |
268 | ((ti->hsync_pulse_width_hi << 8) | \ | 268 | ((ti->hsync_pulse_width_hi << 8) | \ |
269 | ti->hsync_pulse_width_lo); | 269 | ti->hsync_pulse_width_lo); |
270 | mode->htotal = mode->hdisplay + ((ti->hblank_hi << 8) | \ | 270 | mode->htotal = mode->hdisplay + ((ti->hblank_hi << 8) | \ |
271 | ti->hblank_lo); | 271 | ti->hblank_lo); |
272 | mode->vsync_start = \ | 272 | mode->vsync_start = \ |
273 | mode->vdisplay + ((ti->vsync_offset_hi << 4) | \ | 273 | mode->vdisplay + ((ti->vsync_offset_hi << 4) | \ |
274 | ti->vsync_offset_lo); | 274 | ti->vsync_offset_lo); |
275 | mode->vsync_end = \ | 275 | mode->vsync_end = \ |
276 | mode->vsync_start + ((ti->vsync_pulse_width_hi << 4) | \ | 276 | mode->vsync_start + ((ti->vsync_pulse_width_hi << 4) | \ |
277 | ti->vsync_pulse_width_lo); | 277 | ti->vsync_pulse_width_lo); |
278 | mode->vtotal = mode->vdisplay + \ | 278 | mode->vtotal = mode->vdisplay + \ |
279 | ((ti->vblank_hi << 8) | ti->vblank_lo); | 279 | ((ti->vblank_hi << 8) | ti->vblank_lo); |
280 | mode->clock = ti->pixel_clock * 10; | 280 | mode->clock = ti->pixel_clock * 10; |
281 | #if 0 | 281 | #if 0 |
282 | printk(KERN_INFO "hdisplay is %d\n", mode->hdisplay); | 282 | printk(KERN_INFO "hdisplay is %d\n", mode->hdisplay); |
283 | printk(KERN_INFO "vdisplay is %d\n", mode->vdisplay); | 283 | printk(KERN_INFO "vdisplay is %d\n", mode->vdisplay); |
284 | printk(KERN_INFO "HSS is %d\n", mode->hsync_start); | 284 | printk(KERN_INFO "HSS is %d\n", mode->hsync_start); |
285 | printk(KERN_INFO "HSE is %d\n", mode->hsync_end); | 285 | printk(KERN_INFO "HSE is %d\n", mode->hsync_end); |
286 | printk(KERN_INFO "htotal is %d\n", mode->htotal); | 286 | printk(KERN_INFO "htotal is %d\n", mode->htotal); |
287 | printk(KERN_INFO "VSS is %d\n", mode->vsync_start); | 287 | printk(KERN_INFO "VSS is %d\n", mode->vsync_start); |
288 | printk(KERN_INFO "VSE is %d\n", mode->vsync_end); | 288 | printk(KERN_INFO "VSE is %d\n", mode->vsync_end); |
289 | printk(KERN_INFO "vtotal is %d\n", mode->vtotal); | 289 | printk(KERN_INFO "vtotal is %d\n", mode->vtotal); |
290 | printk(KERN_INFO "clock is %d\n", mode->clock); | 290 | printk(KERN_INFO "clock is %d\n", mode->clock); |
291 | #endif | 291 | #endif |
292 | mode_dev->panel_fixed_mode = mode; | 292 | mode_dev->panel_fixed_mode = mode; |
293 | } | 293 | } |
294 | 294 | ||
295 | /* Use the BIOS VBT mode if available */ | 295 | /* Use the BIOS VBT mode if available */ |
296 | if (mode_dev->panel_fixed_mode == NULL && mode_dev->vbt_mode) | 296 | if (mode_dev->panel_fixed_mode == NULL && mode_dev->vbt_mode) |
297 | mode_dev->panel_fixed_mode = drm_mode_duplicate(dev, | 297 | mode_dev->panel_fixed_mode = drm_mode_duplicate(dev, |
298 | mode_dev->vbt_mode); | 298 | mode_dev->vbt_mode); |
299 | 299 | ||
300 | /* Then try the LVDS VBT mode */ | 300 | /* Then try the LVDS VBT mode */ |
301 | if (mode_dev->panel_fixed_mode == NULL) | 301 | if (mode_dev->panel_fixed_mode == NULL) |
302 | if (dev_priv->lfp_lvds_vbt_mode) | 302 | if (dev_priv->lfp_lvds_vbt_mode) |
303 | mode_dev->panel_fixed_mode = | 303 | mode_dev->panel_fixed_mode = |
304 | drm_mode_duplicate(dev, | 304 | drm_mode_duplicate(dev, |
305 | dev_priv->lfp_lvds_vbt_mode); | 305 | dev_priv->lfp_lvds_vbt_mode); |
306 | /* Then guess */ | 306 | /* Then guess */ |
307 | if (mode_dev->panel_fixed_mode == NULL) | 307 | if (mode_dev->panel_fixed_mode == NULL) |
308 | mode_dev->panel_fixed_mode | 308 | mode_dev->panel_fixed_mode |
309 | = drm_mode_duplicate(dev, &lvds_configuration_modes[2]); | 309 | = drm_mode_duplicate(dev, &lvds_configuration_modes[2]); |
310 | 310 | ||
311 | drm_mode_set_name(mode_dev->panel_fixed_mode); | 311 | drm_mode_set_name(mode_dev->panel_fixed_mode); |
312 | drm_mode_set_crtcinfo(mode_dev->panel_fixed_mode, 0); | 312 | drm_mode_set_crtcinfo(mode_dev->panel_fixed_mode, 0); |
313 | } | 313 | } |
314 | 314 | ||
315 | /** | 315 | /** |
316 | * oaktrail_lvds_init - setup LVDS connectors on this device | 316 | * oaktrail_lvds_init - setup LVDS connectors on this device |
317 | * @dev: drm device | 317 | * @dev: drm device |
318 | * | 318 | * |
319 | * Create the connector, register the LVDS DDC bus, and try to figure out what | 319 | * Create the connector, register the LVDS DDC bus, and try to figure out what |
320 | * modes we can display on the LVDS panel (if present). | 320 | * modes we can display on the LVDS panel (if present). |
321 | */ | 321 | */ |
322 | void oaktrail_lvds_init(struct drm_device *dev, | 322 | void oaktrail_lvds_init(struct drm_device *dev, |
323 | struct psb_intel_mode_device *mode_dev) | 323 | struct psb_intel_mode_device *mode_dev) |
324 | { | 324 | { |
325 | struct gma_encoder *gma_encoder; | 325 | struct gma_encoder *gma_encoder; |
326 | struct gma_connector *gma_connector; | 326 | struct gma_connector *gma_connector; |
327 | struct drm_connector *connector; | 327 | struct drm_connector *connector; |
328 | struct drm_encoder *encoder; | 328 | struct drm_encoder *encoder; |
329 | struct drm_psb_private *dev_priv = dev->dev_private; | 329 | struct drm_psb_private *dev_priv = dev->dev_private; |
330 | struct edid *edid; | 330 | struct edid *edid; |
331 | struct i2c_adapter *i2c_adap; | 331 | struct i2c_adapter *i2c_adap; |
332 | struct drm_display_mode *scan; /* *modes, *bios_mode; */ | 332 | struct drm_display_mode *scan; /* *modes, *bios_mode; */ |
333 | 333 | ||
334 | gma_encoder = kzalloc(sizeof(struct gma_encoder), GFP_KERNEL); | 334 | gma_encoder = kzalloc(sizeof(struct gma_encoder), GFP_KERNEL); |
335 | if (!gma_encoder) | 335 | if (!gma_encoder) |
336 | return; | 336 | return; |
337 | 337 | ||
338 | gma_connector = kzalloc(sizeof(struct gma_connector), GFP_KERNEL); | 338 | gma_connector = kzalloc(sizeof(struct gma_connector), GFP_KERNEL); |
339 | if (!gma_connector) | 339 | if (!gma_connector) |
340 | goto failed_connector; | 340 | goto failed_connector; |
341 | 341 | ||
342 | connector = &gma_connector->base; | 342 | connector = &gma_connector->base; |
343 | encoder = &gma_encoder->base; | 343 | encoder = &gma_encoder->base; |
344 | dev_priv->is_lvds_on = true; | 344 | dev_priv->is_lvds_on = true; |
345 | drm_connector_init(dev, connector, | 345 | drm_connector_init(dev, connector, |
346 | &psb_intel_lvds_connector_funcs, | 346 | &psb_intel_lvds_connector_funcs, |
347 | DRM_MODE_CONNECTOR_LVDS); | 347 | DRM_MODE_CONNECTOR_LVDS); |
348 | 348 | ||
349 | drm_encoder_init(dev, encoder, &psb_intel_lvds_enc_funcs, | 349 | drm_encoder_init(dev, encoder, &psb_intel_lvds_enc_funcs, |
350 | DRM_MODE_ENCODER_LVDS); | 350 | DRM_MODE_ENCODER_LVDS); |
351 | 351 | ||
352 | gma_connector_attach_encoder(gma_connector, gma_encoder); | 352 | gma_connector_attach_encoder(gma_connector, gma_encoder); |
353 | gma_encoder->type = INTEL_OUTPUT_LVDS; | 353 | gma_encoder->type = INTEL_OUTPUT_LVDS; |
354 | 354 | ||
355 | drm_encoder_helper_add(encoder, &oaktrail_lvds_helper_funcs); | 355 | drm_encoder_helper_add(encoder, &oaktrail_lvds_helper_funcs); |
356 | drm_connector_helper_add(connector, | 356 | drm_connector_helper_add(connector, |
357 | &psb_intel_lvds_connector_helper_funcs); | 357 | &psb_intel_lvds_connector_helper_funcs); |
358 | connector->display_info.subpixel_order = SubPixelHorizontalRGB; | 358 | connector->display_info.subpixel_order = SubPixelHorizontalRGB; |
359 | connector->interlace_allowed = false; | 359 | connector->interlace_allowed = false; |
360 | connector->doublescan_allowed = false; | 360 | connector->doublescan_allowed = false; |
361 | 361 | ||
362 | drm_object_attach_property(&connector->base, | 362 | drm_object_attach_property(&connector->base, |
363 | dev->mode_config.scaling_mode_property, | 363 | dev->mode_config.scaling_mode_property, |
364 | DRM_MODE_SCALE_FULLSCREEN); | 364 | DRM_MODE_SCALE_FULLSCREEN); |
365 | drm_object_attach_property(&connector->base, | 365 | drm_object_attach_property(&connector->base, |
366 | dev_priv->backlight_property, | 366 | dev_priv->backlight_property, |
367 | BRIGHTNESS_MAX_LEVEL); | 367 | BRIGHTNESS_MAX_LEVEL); |
368 | 368 | ||
369 | mode_dev->panel_wants_dither = false; | 369 | mode_dev->panel_wants_dither = false; |
370 | if (dev_priv->has_gct) | 370 | if (dev_priv->has_gct) |
371 | mode_dev->panel_wants_dither = (dev_priv->gct_data. | 371 | mode_dev->panel_wants_dither = (dev_priv->gct_data. |
372 | Panel_Port_Control & MRST_PANEL_8TO6_DITHER_ENABLE); | 372 | Panel_Port_Control & MRST_PANEL_8TO6_DITHER_ENABLE); |
373 | if (dev_priv->lvds_dither) | 373 | if (dev_priv->lvds_dither) |
374 | mode_dev->panel_wants_dither = 1; | 374 | mode_dev->panel_wants_dither = 1; |
375 | 375 | ||
376 | /* | 376 | /* |
377 | * LVDS discovery: | 377 | * LVDS discovery: |
378 | * 1) check for EDID on DDC | 378 | * 1) check for EDID on DDC |
379 | * 2) check for VBT data | 379 | * 2) check for VBT data |
380 | * 3) check to see if LVDS is already on | 380 | * 3) check to see if LVDS is already on |
381 | * if none of the above, no panel | 381 | * if none of the above, no panel |
382 | * 4) make sure lid is open | 382 | * 4) make sure lid is open |
383 | * if closed, act like it's not there for now | 383 | * if closed, act like it's not there for now |
384 | */ | 384 | */ |
385 | 385 | ||
386 | i2c_adap = i2c_get_adapter(dev_priv->ops->i2c_bus); | 386 | i2c_adap = i2c_get_adapter(dev_priv->ops->i2c_bus); |
387 | if (i2c_adap == NULL) | 387 | if (i2c_adap == NULL) |
388 | dev_err(dev->dev, "No ddc adapter available!\n"); | 388 | dev_err(dev->dev, "No ddc adapter available!\n"); |
389 | /* | 389 | /* |
390 | * Attempt to get the fixed panel mode from DDC. Assume that the | 390 | * Attempt to get the fixed panel mode from DDC. Assume that the |
391 | * preferred mode is the right one. | 391 | * preferred mode is the right one. |
392 | */ | 392 | */ |
393 | if (i2c_adap) { | 393 | if (i2c_adap) { |
394 | edid = drm_get_edid(connector, i2c_adap); | 394 | edid = drm_get_edid(connector, i2c_adap); |
395 | if (edid) { | 395 | if (edid) { |
396 | drm_mode_connector_update_edid_property(connector, | 396 | drm_mode_connector_update_edid_property(connector, |
397 | edid); | 397 | edid); |
398 | drm_add_edid_modes(connector, edid); | 398 | drm_add_edid_modes(connector, edid); |
399 | kfree(edid); | 399 | kfree(edid); |
400 | } | 400 | } |
401 | 401 | ||
402 | list_for_each_entry(scan, &connector->probed_modes, head) { | 402 | list_for_each_entry(scan, &connector->probed_modes, head) { |
403 | if (scan->type & DRM_MODE_TYPE_PREFERRED) { | 403 | if (scan->type & DRM_MODE_TYPE_PREFERRED) { |
404 | mode_dev->panel_fixed_mode = | 404 | mode_dev->panel_fixed_mode = |
405 | drm_mode_duplicate(dev, scan); | 405 | drm_mode_duplicate(dev, scan); |
406 | goto out; /* FIXME: check for quirks */ | 406 | goto out; /* FIXME: check for quirks */ |
407 | } | 407 | } |
408 | } | 408 | } |
409 | } | 409 | } |
410 | /* | 410 | /* |
411 | * If we didn't get EDID, try geting panel timing | 411 | * If we didn't get EDID, try geting panel timing |
412 | * from configuration data | 412 | * from configuration data |
413 | */ | 413 | */ |
414 | oaktrail_lvds_get_configuration_mode(dev, mode_dev); | 414 | oaktrail_lvds_get_configuration_mode(dev, mode_dev); |
415 | 415 | ||
416 | if (mode_dev->panel_fixed_mode) { | 416 | if (mode_dev->panel_fixed_mode) { |
417 | mode_dev->panel_fixed_mode->type |= DRM_MODE_TYPE_PREFERRED; | 417 | mode_dev->panel_fixed_mode->type |= DRM_MODE_TYPE_PREFERRED; |
418 | goto out; /* FIXME: check for quirks */ | 418 | goto out; /* FIXME: check for quirks */ |
419 | } | 419 | } |
420 | 420 | ||
421 | /* If we still don't have a mode after all that, give up. */ | 421 | /* If we still don't have a mode after all that, give up. */ |
422 | if (!mode_dev->panel_fixed_mode) { | 422 | if (!mode_dev->panel_fixed_mode) { |
423 | dev_err(dev->dev, "Found no modes on the lvds, ignoring the LVDS\n"); | 423 | dev_err(dev->dev, "Found no modes on the lvds, ignoring the LVDS\n"); |
424 | goto failed_find; | 424 | goto failed_find; |
425 | } | 425 | } |
426 | 426 | ||
427 | out: | 427 | out: |
428 | drm_sysfs_connector_add(connector); | 428 | drm_sysfs_connector_add(connector); |
429 | return; | 429 | return; |
430 | 430 | ||
431 | failed_find: | 431 | failed_find: |
432 | dev_dbg(dev->dev, "No LVDS modes found, disabling.\n"); | 432 | dev_dbg(dev->dev, "No LVDS modes found, disabling.\n"); |
433 | if (gma_encoder->ddc_bus) | 433 | if (gma_encoder->ddc_bus) |
434 | psb_intel_i2c_destroy(gma_encoder->ddc_bus); | 434 | psb_intel_i2c_destroy(gma_encoder->ddc_bus); |
435 | 435 | ||
436 | /* failed_ddc: */ | 436 | /* failed_ddc: */ |
437 | 437 | ||
438 | drm_encoder_cleanup(encoder); | 438 | drm_encoder_cleanup(encoder); |
439 | drm_connector_cleanup(connector); | 439 | drm_connector_cleanup(connector); |
440 | kfree(gma_connector); | 440 | kfree(gma_connector); |
441 | failed_connector: | 441 | failed_connector: |
442 | kfree(gma_encoder); | 442 | kfree(gma_encoder); |
443 | } | 443 | } |
444 | 444 | ||
445 | 445 |
drivers/platform/x86/intel_scu_ipc.c
1 | /* | 1 | /* |
2 | * intel_scu_ipc.c: Driver for the Intel SCU IPC mechanism | 2 | * intel_scu_ipc.c: Driver for the Intel SCU IPC mechanism |
3 | * | 3 | * |
4 | * (C) Copyright 2008-2010 Intel Corporation | 4 | * (C) Copyright 2008-2010 Intel Corporation |
5 | * Author: Sreedhara DS (sreedhara.ds@intel.com) | 5 | * Author: Sreedhara DS (sreedhara.ds@intel.com) |
6 | * | 6 | * |
7 | * This program is free software; you can redistribute it and/or | 7 | * This program is free software; you can redistribute it and/or |
8 | * modify it under the terms of the GNU General Public License | 8 | * modify it under the terms of the GNU General Public License |
9 | * as published by the Free Software Foundation; version 2 | 9 | * as published by the Free Software Foundation; version 2 |
10 | * of the License. | 10 | * of the License. |
11 | * | 11 | * |
12 | * SCU running in ARC processor communicates with other entity running in IA | 12 | * SCU running in ARC processor communicates with other entity running in IA |
13 | * core through IPC mechanism which in turn messaging between IA core ad SCU. | 13 | * core through IPC mechanism which in turn messaging between IA core ad SCU. |
14 | * SCU has two IPC mechanism IPC-1 and IPC-2. IPC-1 is used between IA32 and | 14 | * SCU has two IPC mechanism IPC-1 and IPC-2. IPC-1 is used between IA32 and |
15 | * SCU where IPC-2 is used between P-Unit and SCU. This driver delas with | 15 | * SCU where IPC-2 is used between P-Unit and SCU. This driver delas with |
16 | * IPC-1 Driver provides an API for power control unit registers (e.g. MSIC) | 16 | * IPC-1 Driver provides an API for power control unit registers (e.g. MSIC) |
17 | * along with other APIs. | 17 | * along with other APIs. |
18 | */ | 18 | */ |
19 | #include <linux/delay.h> | 19 | #include <linux/delay.h> |
20 | #include <linux/errno.h> | 20 | #include <linux/errno.h> |
21 | #include <linux/init.h> | 21 | #include <linux/init.h> |
22 | #include <linux/device.h> | 22 | #include <linux/device.h> |
23 | #include <linux/pm.h> | 23 | #include <linux/pm.h> |
24 | #include <linux/pci.h> | 24 | #include <linux/pci.h> |
25 | #include <linux/interrupt.h> | 25 | #include <linux/interrupt.h> |
26 | #include <linux/sfi.h> | 26 | #include <linux/sfi.h> |
27 | #include <linux/module.h> | 27 | #include <linux/module.h> |
28 | #include <asm/mrst.h> | 28 | #include <asm/intel-mid.h> |
29 | #include <asm/intel_scu_ipc.h> | 29 | #include <asm/intel_scu_ipc.h> |
30 | 30 | ||
31 | /* IPC defines the following message types */ | 31 | /* IPC defines the following message types */ |
32 | #define IPCMSG_WATCHDOG_TIMER 0xF8 /* Set Kernel Watchdog Threshold */ | 32 | #define IPCMSG_WATCHDOG_TIMER 0xF8 /* Set Kernel Watchdog Threshold */ |
33 | #define IPCMSG_BATTERY 0xEF /* Coulomb Counter Accumulator */ | 33 | #define IPCMSG_BATTERY 0xEF /* Coulomb Counter Accumulator */ |
34 | #define IPCMSG_FW_UPDATE 0xFE /* Firmware update */ | 34 | #define IPCMSG_FW_UPDATE 0xFE /* Firmware update */ |
35 | #define IPCMSG_PCNTRL 0xFF /* Power controller unit read/write */ | 35 | #define IPCMSG_PCNTRL 0xFF /* Power controller unit read/write */ |
36 | #define IPCMSG_FW_REVISION 0xF4 /* Get firmware revision */ | 36 | #define IPCMSG_FW_REVISION 0xF4 /* Get firmware revision */ |
37 | 37 | ||
38 | /* Command id associated with message IPCMSG_PCNTRL */ | 38 | /* Command id associated with message IPCMSG_PCNTRL */ |
39 | #define IPC_CMD_PCNTRL_W 0 /* Register write */ | 39 | #define IPC_CMD_PCNTRL_W 0 /* Register write */ |
40 | #define IPC_CMD_PCNTRL_R 1 /* Register read */ | 40 | #define IPC_CMD_PCNTRL_R 1 /* Register read */ |
41 | #define IPC_CMD_PCNTRL_M 2 /* Register read-modify-write */ | 41 | #define IPC_CMD_PCNTRL_M 2 /* Register read-modify-write */ |
42 | 42 | ||
43 | /* | 43 | /* |
44 | * IPC register summary | 44 | * IPC register summary |
45 | * | 45 | * |
46 | * IPC register blocks are memory mapped at fixed address of 0xFF11C000 | 46 | * IPC register blocks are memory mapped at fixed address of 0xFF11C000 |
47 | * To read or write information to the SCU, driver writes to IPC-1 memory | 47 | * To read or write information to the SCU, driver writes to IPC-1 memory |
48 | * mapped registers (base address 0xFF11C000). The following is the IPC | 48 | * mapped registers (base address 0xFF11C000). The following is the IPC |
49 | * mechanism | 49 | * mechanism |
50 | * | 50 | * |
51 | * 1. IA core cDMI interface claims this transaction and converts it to a | 51 | * 1. IA core cDMI interface claims this transaction and converts it to a |
52 | * Transaction Layer Packet (TLP) message which is sent across the cDMI. | 52 | * Transaction Layer Packet (TLP) message which is sent across the cDMI. |
53 | * | 53 | * |
54 | * 2. South Complex cDMI block receives this message and writes it to | 54 | * 2. South Complex cDMI block receives this message and writes it to |
55 | * the IPC-1 register block, causing an interrupt to the SCU | 55 | * the IPC-1 register block, causing an interrupt to the SCU |
56 | * | 56 | * |
57 | * 3. SCU firmware decodes this interrupt and IPC message and the appropriate | 57 | * 3. SCU firmware decodes this interrupt and IPC message and the appropriate |
58 | * message handler is called within firmware. | 58 | * message handler is called within firmware. |
59 | */ | 59 | */ |
60 | 60 | ||
61 | #define IPC_BASE_ADDR 0xFF11C000 /* IPC1 base register address */ | 61 | #define IPC_BASE_ADDR 0xFF11C000 /* IPC1 base register address */ |
62 | #define IPC_MAX_ADDR 0x100 /* Maximum IPC regisers */ | 62 | #define IPC_MAX_ADDR 0x100 /* Maximum IPC regisers */ |
63 | #define IPC_WWBUF_SIZE 20 /* IPC Write buffer Size */ | 63 | #define IPC_WWBUF_SIZE 20 /* IPC Write buffer Size */ |
64 | #define IPC_RWBUF_SIZE 20 /* IPC Read buffer Size */ | 64 | #define IPC_RWBUF_SIZE 20 /* IPC Read buffer Size */ |
65 | #define IPC_I2C_BASE 0xFF12B000 /* I2C control register base address */ | 65 | #define IPC_I2C_BASE 0xFF12B000 /* I2C control register base address */ |
66 | #define IPC_I2C_MAX_ADDR 0x10 /* Maximum I2C regisers */ | 66 | #define IPC_I2C_MAX_ADDR 0x10 /* Maximum I2C regisers */ |
67 | 67 | ||
68 | static int ipc_probe(struct pci_dev *dev, const struct pci_device_id *id); | 68 | static int ipc_probe(struct pci_dev *dev, const struct pci_device_id *id); |
69 | static void ipc_remove(struct pci_dev *pdev); | 69 | static void ipc_remove(struct pci_dev *pdev); |
70 | 70 | ||
71 | struct intel_scu_ipc_dev { | 71 | struct intel_scu_ipc_dev { |
72 | struct pci_dev *pdev; | 72 | struct pci_dev *pdev; |
73 | void __iomem *ipc_base; | 73 | void __iomem *ipc_base; |
74 | void __iomem *i2c_base; | 74 | void __iomem *i2c_base; |
75 | }; | 75 | }; |
76 | 76 | ||
77 | static struct intel_scu_ipc_dev ipcdev; /* Only one for now */ | 77 | static struct intel_scu_ipc_dev ipcdev; /* Only one for now */ |
78 | 78 | ||
79 | static int platform; /* Platform type */ | 79 | static int platform; /* Platform type */ |
80 | 80 | ||
81 | /* | 81 | /* |
82 | * IPC Read Buffer (Read Only): | 82 | * IPC Read Buffer (Read Only): |
83 | * 16 byte buffer for receiving data from SCU, if IPC command | 83 | * 16 byte buffer for receiving data from SCU, if IPC command |
84 | * processing results in response data | 84 | * processing results in response data |
85 | */ | 85 | */ |
86 | #define IPC_READ_BUFFER 0x90 | 86 | #define IPC_READ_BUFFER 0x90 |
87 | 87 | ||
88 | #define IPC_I2C_CNTRL_ADDR 0 | 88 | #define IPC_I2C_CNTRL_ADDR 0 |
89 | #define I2C_DATA_ADDR 0x04 | 89 | #define I2C_DATA_ADDR 0x04 |
90 | 90 | ||
91 | static DEFINE_MUTEX(ipclock); /* lock used to prevent multiple call to SCU */ | 91 | static DEFINE_MUTEX(ipclock); /* lock used to prevent multiple call to SCU */ |
92 | 92 | ||
93 | /* | 93 | /* |
94 | * Command Register (Write Only): | 94 | * Command Register (Write Only): |
95 | * A write to this register results in an interrupt to the SCU core processor | 95 | * A write to this register results in an interrupt to the SCU core processor |
96 | * Format: | 96 | * Format: |
97 | * |rfu2(8) | size(8) | command id(4) | rfu1(3) | ioc(1) | command(8)| | 97 | * |rfu2(8) | size(8) | command id(4) | rfu1(3) | ioc(1) | command(8)| |
98 | */ | 98 | */ |
99 | static inline void ipc_command(u32 cmd) /* Send ipc command */ | 99 | static inline void ipc_command(u32 cmd) /* Send ipc command */ |
100 | { | 100 | { |
101 | writel(cmd, ipcdev.ipc_base); | 101 | writel(cmd, ipcdev.ipc_base); |
102 | } | 102 | } |
103 | 103 | ||
104 | /* | 104 | /* |
105 | * IPC Write Buffer (Write Only): | 105 | * IPC Write Buffer (Write Only): |
106 | * 16-byte buffer for sending data associated with IPC command to | 106 | * 16-byte buffer for sending data associated with IPC command to |
107 | * SCU. Size of the data is specified in the IPC_COMMAND_REG register | 107 | * SCU. Size of the data is specified in the IPC_COMMAND_REG register |
108 | */ | 108 | */ |
109 | static inline void ipc_data_writel(u32 data, u32 offset) /* Write ipc data */ | 109 | static inline void ipc_data_writel(u32 data, u32 offset) /* Write ipc data */ |
110 | { | 110 | { |
111 | writel(data, ipcdev.ipc_base + 0x80 + offset); | 111 | writel(data, ipcdev.ipc_base + 0x80 + offset); |
112 | } | 112 | } |
113 | 113 | ||
114 | /* | 114 | /* |
115 | * Status Register (Read Only): | 115 | * Status Register (Read Only): |
116 | * Driver will read this register to get the ready/busy status of the IPC | 116 | * Driver will read this register to get the ready/busy status of the IPC |
117 | * block and error status of the IPC command that was just processed by SCU | 117 | * block and error status of the IPC command that was just processed by SCU |
118 | * Format: | 118 | * Format: |
119 | * |rfu3(8)|error code(8)|initiator id(8)|cmd id(4)|rfu1(2)|error(1)|busy(1)| | 119 | * |rfu3(8)|error code(8)|initiator id(8)|cmd id(4)|rfu1(2)|error(1)|busy(1)| |
120 | */ | 120 | */ |
121 | 121 | ||
122 | static inline u8 ipc_read_status(void) | 122 | static inline u8 ipc_read_status(void) |
123 | { | 123 | { |
124 | return __raw_readl(ipcdev.ipc_base + 0x04); | 124 | return __raw_readl(ipcdev.ipc_base + 0x04); |
125 | } | 125 | } |
126 | 126 | ||
127 | static inline u8 ipc_data_readb(u32 offset) /* Read ipc byte data */ | 127 | static inline u8 ipc_data_readb(u32 offset) /* Read ipc byte data */ |
128 | { | 128 | { |
129 | return readb(ipcdev.ipc_base + IPC_READ_BUFFER + offset); | 129 | return readb(ipcdev.ipc_base + IPC_READ_BUFFER + offset); |
130 | } | 130 | } |
131 | 131 | ||
132 | static inline u32 ipc_data_readl(u32 offset) /* Read ipc u32 data */ | 132 | static inline u32 ipc_data_readl(u32 offset) /* Read ipc u32 data */ |
133 | { | 133 | { |
134 | return readl(ipcdev.ipc_base + IPC_READ_BUFFER + offset); | 134 | return readl(ipcdev.ipc_base + IPC_READ_BUFFER + offset); |
135 | } | 135 | } |
136 | 136 | ||
137 | static inline int busy_loop(void) /* Wait till scu status is busy */ | 137 | static inline int busy_loop(void) /* Wait till scu status is busy */ |
138 | { | 138 | { |
139 | u32 status = 0; | 139 | u32 status = 0; |
140 | u32 loop_count = 0; | 140 | u32 loop_count = 0; |
141 | 141 | ||
142 | status = ipc_read_status(); | 142 | status = ipc_read_status(); |
143 | while (status & 1) { | 143 | while (status & 1) { |
144 | udelay(1); /* scu processing time is in few u secods */ | 144 | udelay(1); /* scu processing time is in few u secods */ |
145 | status = ipc_read_status(); | 145 | status = ipc_read_status(); |
146 | loop_count++; | 146 | loop_count++; |
147 | /* break if scu doesn't reset busy bit after huge retry */ | 147 | /* break if scu doesn't reset busy bit after huge retry */ |
148 | if (loop_count > 100000) { | 148 | if (loop_count > 100000) { |
149 | dev_err(&ipcdev.pdev->dev, "IPC timed out"); | 149 | dev_err(&ipcdev.pdev->dev, "IPC timed out"); |
150 | return -ETIMEDOUT; | 150 | return -ETIMEDOUT; |
151 | } | 151 | } |
152 | } | 152 | } |
153 | if ((status >> 1) & 1) | 153 | if ((status >> 1) & 1) |
154 | return -EIO; | 154 | return -EIO; |
155 | 155 | ||
156 | return 0; | 156 | return 0; |
157 | } | 157 | } |
158 | 158 | ||
159 | /* Read/Write power control(PMIC in Langwell, MSIC in PenWell) registers */ | 159 | /* Read/Write power control(PMIC in Langwell, MSIC in PenWell) registers */ |
160 | static int pwr_reg_rdwr(u16 *addr, u8 *data, u32 count, u32 op, u32 id) | 160 | static int pwr_reg_rdwr(u16 *addr, u8 *data, u32 count, u32 op, u32 id) |
161 | { | 161 | { |
162 | int nc; | 162 | int nc; |
163 | u32 offset = 0; | 163 | u32 offset = 0; |
164 | int err; | 164 | int err; |
165 | u8 cbuf[IPC_WWBUF_SIZE] = { }; | 165 | u8 cbuf[IPC_WWBUF_SIZE] = { }; |
166 | u32 *wbuf = (u32 *)&cbuf; | 166 | u32 *wbuf = (u32 *)&cbuf; |
167 | 167 | ||
168 | mutex_lock(&ipclock); | 168 | mutex_lock(&ipclock); |
169 | 169 | ||
170 | memset(cbuf, 0, sizeof(cbuf)); | 170 | memset(cbuf, 0, sizeof(cbuf)); |
171 | 171 | ||
172 | if (ipcdev.pdev == NULL) { | 172 | if (ipcdev.pdev == NULL) { |
173 | mutex_unlock(&ipclock); | 173 | mutex_unlock(&ipclock); |
174 | return -ENODEV; | 174 | return -ENODEV; |
175 | } | 175 | } |
176 | 176 | ||
177 | for (nc = 0; nc < count; nc++, offset += 2) { | 177 | for (nc = 0; nc < count; nc++, offset += 2) { |
178 | cbuf[offset] = addr[nc]; | 178 | cbuf[offset] = addr[nc]; |
179 | cbuf[offset + 1] = addr[nc] >> 8; | 179 | cbuf[offset + 1] = addr[nc] >> 8; |
180 | } | 180 | } |
181 | 181 | ||
182 | if (id == IPC_CMD_PCNTRL_R) { | 182 | if (id == IPC_CMD_PCNTRL_R) { |
183 | for (nc = 0, offset = 0; nc < count; nc++, offset += 4) | 183 | for (nc = 0, offset = 0; nc < count; nc++, offset += 4) |
184 | ipc_data_writel(wbuf[nc], offset); | 184 | ipc_data_writel(wbuf[nc], offset); |
185 | ipc_command((count*2) << 16 | id << 12 | 0 << 8 | op); | 185 | ipc_command((count*2) << 16 | id << 12 | 0 << 8 | op); |
186 | } else if (id == IPC_CMD_PCNTRL_W) { | 186 | } else if (id == IPC_CMD_PCNTRL_W) { |
187 | for (nc = 0; nc < count; nc++, offset += 1) | 187 | for (nc = 0; nc < count; nc++, offset += 1) |
188 | cbuf[offset] = data[nc]; | 188 | cbuf[offset] = data[nc]; |
189 | for (nc = 0, offset = 0; nc < count; nc++, offset += 4) | 189 | for (nc = 0, offset = 0; nc < count; nc++, offset += 4) |
190 | ipc_data_writel(wbuf[nc], offset); | 190 | ipc_data_writel(wbuf[nc], offset); |
191 | ipc_command((count*3) << 16 | id << 12 | 0 << 8 | op); | 191 | ipc_command((count*3) << 16 | id << 12 | 0 << 8 | op); |
192 | } else if (id == IPC_CMD_PCNTRL_M) { | 192 | } else if (id == IPC_CMD_PCNTRL_M) { |
193 | cbuf[offset] = data[0]; | 193 | cbuf[offset] = data[0]; |
194 | cbuf[offset + 1] = data[1]; | 194 | cbuf[offset + 1] = data[1]; |
195 | ipc_data_writel(wbuf[0], 0); /* Write wbuff */ | 195 | ipc_data_writel(wbuf[0], 0); /* Write wbuff */ |
196 | ipc_command(4 << 16 | id << 12 | 0 << 8 | op); | 196 | ipc_command(4 << 16 | id << 12 | 0 << 8 | op); |
197 | } | 197 | } |
198 | 198 | ||
199 | err = busy_loop(); | 199 | err = busy_loop(); |
200 | if (id == IPC_CMD_PCNTRL_R) { /* Read rbuf */ | 200 | if (id == IPC_CMD_PCNTRL_R) { /* Read rbuf */ |
201 | /* Workaround: values are read as 0 without memcpy_fromio */ | 201 | /* Workaround: values are read as 0 without memcpy_fromio */ |
202 | memcpy_fromio(cbuf, ipcdev.ipc_base + 0x90, 16); | 202 | memcpy_fromio(cbuf, ipcdev.ipc_base + 0x90, 16); |
203 | for (nc = 0; nc < count; nc++) | 203 | for (nc = 0; nc < count; nc++) |
204 | data[nc] = ipc_data_readb(nc); | 204 | data[nc] = ipc_data_readb(nc); |
205 | } | 205 | } |
206 | mutex_unlock(&ipclock); | 206 | mutex_unlock(&ipclock); |
207 | return err; | 207 | return err; |
208 | } | 208 | } |
209 | 209 | ||
210 | /** | 210 | /** |
211 | * intel_scu_ipc_ioread8 - read a word via the SCU | 211 | * intel_scu_ipc_ioread8 - read a word via the SCU |
212 | * @addr: register on SCU | 212 | * @addr: register on SCU |
213 | * @data: return pointer for read byte | 213 | * @data: return pointer for read byte |
214 | * | 214 | * |
215 | * Read a single register. Returns 0 on success or an error code. All | 215 | * Read a single register. Returns 0 on success or an error code. All |
216 | * locking between SCU accesses is handled for the caller. | 216 | * locking between SCU accesses is handled for the caller. |
217 | * | 217 | * |
218 | * This function may sleep. | 218 | * This function may sleep. |
219 | */ | 219 | */ |
220 | int intel_scu_ipc_ioread8(u16 addr, u8 *data) | 220 | int intel_scu_ipc_ioread8(u16 addr, u8 *data) |
221 | { | 221 | { |
222 | return pwr_reg_rdwr(&addr, data, 1, IPCMSG_PCNTRL, IPC_CMD_PCNTRL_R); | 222 | return pwr_reg_rdwr(&addr, data, 1, IPCMSG_PCNTRL, IPC_CMD_PCNTRL_R); |
223 | } | 223 | } |
224 | EXPORT_SYMBOL(intel_scu_ipc_ioread8); | 224 | EXPORT_SYMBOL(intel_scu_ipc_ioread8); |
225 | 225 | ||
226 | /** | 226 | /** |
227 | * intel_scu_ipc_ioread16 - read a word via the SCU | 227 | * intel_scu_ipc_ioread16 - read a word via the SCU |
228 | * @addr: register on SCU | 228 | * @addr: register on SCU |
229 | * @data: return pointer for read word | 229 | * @data: return pointer for read word |
230 | * | 230 | * |
231 | * Read a register pair. Returns 0 on success or an error code. All | 231 | * Read a register pair. Returns 0 on success or an error code. All |
232 | * locking between SCU accesses is handled for the caller. | 232 | * locking between SCU accesses is handled for the caller. |
233 | * | 233 | * |
234 | * This function may sleep. | 234 | * This function may sleep. |
235 | */ | 235 | */ |
236 | int intel_scu_ipc_ioread16(u16 addr, u16 *data) | 236 | int intel_scu_ipc_ioread16(u16 addr, u16 *data) |
237 | { | 237 | { |
238 | u16 x[2] = {addr, addr + 1 }; | 238 | u16 x[2] = {addr, addr + 1 }; |
239 | return pwr_reg_rdwr(x, (u8 *)data, 2, IPCMSG_PCNTRL, IPC_CMD_PCNTRL_R); | 239 | return pwr_reg_rdwr(x, (u8 *)data, 2, IPCMSG_PCNTRL, IPC_CMD_PCNTRL_R); |
240 | } | 240 | } |
241 | EXPORT_SYMBOL(intel_scu_ipc_ioread16); | 241 | EXPORT_SYMBOL(intel_scu_ipc_ioread16); |
242 | 242 | ||
243 | /** | 243 | /** |
244 | * intel_scu_ipc_ioread32 - read a dword via the SCU | 244 | * intel_scu_ipc_ioread32 - read a dword via the SCU |
245 | * @addr: register on SCU | 245 | * @addr: register on SCU |
246 | * @data: return pointer for read dword | 246 | * @data: return pointer for read dword |
247 | * | 247 | * |
248 | * Read four registers. Returns 0 on success or an error code. All | 248 | * Read four registers. Returns 0 on success or an error code. All |
249 | * locking between SCU accesses is handled for the caller. | 249 | * locking between SCU accesses is handled for the caller. |
250 | * | 250 | * |
251 | * This function may sleep. | 251 | * This function may sleep. |
252 | */ | 252 | */ |
253 | int intel_scu_ipc_ioread32(u16 addr, u32 *data) | 253 | int intel_scu_ipc_ioread32(u16 addr, u32 *data) |
254 | { | 254 | { |
255 | u16 x[4] = {addr, addr + 1, addr + 2, addr + 3}; | 255 | u16 x[4] = {addr, addr + 1, addr + 2, addr + 3}; |
256 | return pwr_reg_rdwr(x, (u8 *)data, 4, IPCMSG_PCNTRL, IPC_CMD_PCNTRL_R); | 256 | return pwr_reg_rdwr(x, (u8 *)data, 4, IPCMSG_PCNTRL, IPC_CMD_PCNTRL_R); |
257 | } | 257 | } |
258 | EXPORT_SYMBOL(intel_scu_ipc_ioread32); | 258 | EXPORT_SYMBOL(intel_scu_ipc_ioread32); |
259 | 259 | ||
260 | /** | 260 | /** |
261 | * intel_scu_ipc_iowrite8 - write a byte via the SCU | 261 | * intel_scu_ipc_iowrite8 - write a byte via the SCU |
262 | * @addr: register on SCU | 262 | * @addr: register on SCU |
263 | * @data: byte to write | 263 | * @data: byte to write |
264 | * | 264 | * |
265 | * Write a single register. Returns 0 on success or an error code. All | 265 | * Write a single register. Returns 0 on success or an error code. All |
266 | * locking between SCU accesses is handled for the caller. | 266 | * locking between SCU accesses is handled for the caller. |
267 | * | 267 | * |
268 | * This function may sleep. | 268 | * This function may sleep. |
269 | */ | 269 | */ |
270 | int intel_scu_ipc_iowrite8(u16 addr, u8 data) | 270 | int intel_scu_ipc_iowrite8(u16 addr, u8 data) |
271 | { | 271 | { |
272 | return pwr_reg_rdwr(&addr, &data, 1, IPCMSG_PCNTRL, IPC_CMD_PCNTRL_W); | 272 | return pwr_reg_rdwr(&addr, &data, 1, IPCMSG_PCNTRL, IPC_CMD_PCNTRL_W); |
273 | } | 273 | } |
274 | EXPORT_SYMBOL(intel_scu_ipc_iowrite8); | 274 | EXPORT_SYMBOL(intel_scu_ipc_iowrite8); |
275 | 275 | ||
276 | /** | 276 | /** |
277 | * intel_scu_ipc_iowrite16 - write a word via the SCU | 277 | * intel_scu_ipc_iowrite16 - write a word via the SCU |
278 | * @addr: register on SCU | 278 | * @addr: register on SCU |
279 | * @data: word to write | 279 | * @data: word to write |
280 | * | 280 | * |
281 | * Write two registers. Returns 0 on success or an error code. All | 281 | * Write two registers. Returns 0 on success or an error code. All |
282 | * locking between SCU accesses is handled for the caller. | 282 | * locking between SCU accesses is handled for the caller. |
283 | * | 283 | * |
284 | * This function may sleep. | 284 | * This function may sleep. |
285 | */ | 285 | */ |
286 | int intel_scu_ipc_iowrite16(u16 addr, u16 data) | 286 | int intel_scu_ipc_iowrite16(u16 addr, u16 data) |
287 | { | 287 | { |
288 | u16 x[2] = {addr, addr + 1 }; | 288 | u16 x[2] = {addr, addr + 1 }; |
289 | return pwr_reg_rdwr(x, (u8 *)&data, 2, IPCMSG_PCNTRL, IPC_CMD_PCNTRL_W); | 289 | return pwr_reg_rdwr(x, (u8 *)&data, 2, IPCMSG_PCNTRL, IPC_CMD_PCNTRL_W); |
290 | } | 290 | } |
291 | EXPORT_SYMBOL(intel_scu_ipc_iowrite16); | 291 | EXPORT_SYMBOL(intel_scu_ipc_iowrite16); |
292 | 292 | ||
293 | /** | 293 | /** |
294 | * intel_scu_ipc_iowrite32 - write a dword via the SCU | 294 | * intel_scu_ipc_iowrite32 - write a dword via the SCU |
295 | * @addr: register on SCU | 295 | * @addr: register on SCU |
296 | * @data: dword to write | 296 | * @data: dword to write |
297 | * | 297 | * |
298 | * Write four registers. Returns 0 on success or an error code. All | 298 | * Write four registers. Returns 0 on success or an error code. All |
299 | * locking between SCU accesses is handled for the caller. | 299 | * locking between SCU accesses is handled for the caller. |
300 | * | 300 | * |
301 | * This function may sleep. | 301 | * This function may sleep. |
302 | */ | 302 | */ |
303 | int intel_scu_ipc_iowrite32(u16 addr, u32 data) | 303 | int intel_scu_ipc_iowrite32(u16 addr, u32 data) |
304 | { | 304 | { |
305 | u16 x[4] = {addr, addr + 1, addr + 2, addr + 3}; | 305 | u16 x[4] = {addr, addr + 1, addr + 2, addr + 3}; |
306 | return pwr_reg_rdwr(x, (u8 *)&data, 4, IPCMSG_PCNTRL, IPC_CMD_PCNTRL_W); | 306 | return pwr_reg_rdwr(x, (u8 *)&data, 4, IPCMSG_PCNTRL, IPC_CMD_PCNTRL_W); |
307 | } | 307 | } |
308 | EXPORT_SYMBOL(intel_scu_ipc_iowrite32); | 308 | EXPORT_SYMBOL(intel_scu_ipc_iowrite32); |
309 | 309 | ||
310 | /** | 310 | /** |
311 | * intel_scu_ipc_readvv - read a set of registers | 311 | * intel_scu_ipc_readvv - read a set of registers |
312 | * @addr: register list | 312 | * @addr: register list |
313 | * @data: bytes to return | 313 | * @data: bytes to return |
314 | * @len: length of array | 314 | * @len: length of array |
315 | * | 315 | * |
316 | * Read registers. Returns 0 on success or an error code. All | 316 | * Read registers. Returns 0 on success or an error code. All |
317 | * locking between SCU accesses is handled for the caller. | 317 | * locking between SCU accesses is handled for the caller. |
318 | * | 318 | * |
319 | * The largest array length permitted by the hardware is 5 items. | 319 | * The largest array length permitted by the hardware is 5 items. |
320 | * | 320 | * |
321 | * This function may sleep. | 321 | * This function may sleep. |
322 | */ | 322 | */ |
323 | int intel_scu_ipc_readv(u16 *addr, u8 *data, int len) | 323 | int intel_scu_ipc_readv(u16 *addr, u8 *data, int len) |
324 | { | 324 | { |
325 | return pwr_reg_rdwr(addr, data, len, IPCMSG_PCNTRL, IPC_CMD_PCNTRL_R); | 325 | return pwr_reg_rdwr(addr, data, len, IPCMSG_PCNTRL, IPC_CMD_PCNTRL_R); |
326 | } | 326 | } |
327 | EXPORT_SYMBOL(intel_scu_ipc_readv); | 327 | EXPORT_SYMBOL(intel_scu_ipc_readv); |
328 | 328 | ||
329 | /** | 329 | /** |
330 | * intel_scu_ipc_writev - write a set of registers | 330 | * intel_scu_ipc_writev - write a set of registers |
331 | * @addr: register list | 331 | * @addr: register list |
332 | * @data: bytes to write | 332 | * @data: bytes to write |
333 | * @len: length of array | 333 | * @len: length of array |
334 | * | 334 | * |
335 | * Write registers. Returns 0 on success or an error code. All | 335 | * Write registers. Returns 0 on success or an error code. All |
336 | * locking between SCU accesses is handled for the caller. | 336 | * locking between SCU accesses is handled for the caller. |
337 | * | 337 | * |
338 | * The largest array length permitted by the hardware is 5 items. | 338 | * The largest array length permitted by the hardware is 5 items. |
339 | * | 339 | * |
340 | * This function may sleep. | 340 | * This function may sleep. |
341 | * | 341 | * |
342 | */ | 342 | */ |
343 | int intel_scu_ipc_writev(u16 *addr, u8 *data, int len) | 343 | int intel_scu_ipc_writev(u16 *addr, u8 *data, int len) |
344 | { | 344 | { |
345 | return pwr_reg_rdwr(addr, data, len, IPCMSG_PCNTRL, IPC_CMD_PCNTRL_W); | 345 | return pwr_reg_rdwr(addr, data, len, IPCMSG_PCNTRL, IPC_CMD_PCNTRL_W); |
346 | } | 346 | } |
347 | EXPORT_SYMBOL(intel_scu_ipc_writev); | 347 | EXPORT_SYMBOL(intel_scu_ipc_writev); |
348 | 348 | ||
349 | 349 | ||
350 | /** | 350 | /** |
351 | * intel_scu_ipc_update_register - r/m/w a register | 351 | * intel_scu_ipc_update_register - r/m/w a register |
352 | * @addr: register address | 352 | * @addr: register address |
353 | * @bits: bits to update | 353 | * @bits: bits to update |
354 | * @mask: mask of bits to update | 354 | * @mask: mask of bits to update |
355 | * | 355 | * |
356 | * Read-modify-write power control unit register. The first data argument | 356 | * Read-modify-write power control unit register. The first data argument |
357 | * must be register value and second is mask value | 357 | * must be register value and second is mask value |
358 | * mask is a bitmap that indicates which bits to update. | 358 | * mask is a bitmap that indicates which bits to update. |
359 | * 0 = masked. Don't modify this bit, 1 = modify this bit. | 359 | * 0 = masked. Don't modify this bit, 1 = modify this bit. |
360 | * returns 0 on success or an error code. | 360 | * returns 0 on success or an error code. |
361 | * | 361 | * |
362 | * This function may sleep. Locking between SCU accesses is handled | 362 | * This function may sleep. Locking between SCU accesses is handled |
363 | * for the caller. | 363 | * for the caller. |
364 | */ | 364 | */ |
365 | int intel_scu_ipc_update_register(u16 addr, u8 bits, u8 mask) | 365 | int intel_scu_ipc_update_register(u16 addr, u8 bits, u8 mask) |
366 | { | 366 | { |
367 | u8 data[2] = { bits, mask }; | 367 | u8 data[2] = { bits, mask }; |
368 | return pwr_reg_rdwr(&addr, data, 1, IPCMSG_PCNTRL, IPC_CMD_PCNTRL_M); | 368 | return pwr_reg_rdwr(&addr, data, 1, IPCMSG_PCNTRL, IPC_CMD_PCNTRL_M); |
369 | } | 369 | } |
370 | EXPORT_SYMBOL(intel_scu_ipc_update_register); | 370 | EXPORT_SYMBOL(intel_scu_ipc_update_register); |
371 | 371 | ||
372 | /** | 372 | /** |
373 | * intel_scu_ipc_simple_command - send a simple command | 373 | * intel_scu_ipc_simple_command - send a simple command |
374 | * @cmd: command | 374 | * @cmd: command |
375 | * @sub: sub type | 375 | * @sub: sub type |
376 | * | 376 | * |
377 | * Issue a simple command to the SCU. Do not use this interface if | 377 | * Issue a simple command to the SCU. Do not use this interface if |
378 | * you must then access data as any data values may be overwritten | 378 | * you must then access data as any data values may be overwritten |
379 | * by another SCU access by the time this function returns. | 379 | * by another SCU access by the time this function returns. |
380 | * | 380 | * |
381 | * This function may sleep. Locking for SCU accesses is handled for | 381 | * This function may sleep. Locking for SCU accesses is handled for |
382 | * the caller. | 382 | * the caller. |
383 | */ | 383 | */ |
384 | int intel_scu_ipc_simple_command(int cmd, int sub) | 384 | int intel_scu_ipc_simple_command(int cmd, int sub) |
385 | { | 385 | { |
386 | int err; | 386 | int err; |
387 | 387 | ||
388 | mutex_lock(&ipclock); | 388 | mutex_lock(&ipclock); |
389 | if (ipcdev.pdev == NULL) { | 389 | if (ipcdev.pdev == NULL) { |
390 | mutex_unlock(&ipclock); | 390 | mutex_unlock(&ipclock); |
391 | return -ENODEV; | 391 | return -ENODEV; |
392 | } | 392 | } |
393 | ipc_command(sub << 12 | cmd); | 393 | ipc_command(sub << 12 | cmd); |
394 | err = busy_loop(); | 394 | err = busy_loop(); |
395 | mutex_unlock(&ipclock); | 395 | mutex_unlock(&ipclock); |
396 | return err; | 396 | return err; |
397 | } | 397 | } |
398 | EXPORT_SYMBOL(intel_scu_ipc_simple_command); | 398 | EXPORT_SYMBOL(intel_scu_ipc_simple_command); |
399 | 399 | ||
400 | /** | 400 | /** |
401 | * intel_scu_ipc_command - command with data | 401 | * intel_scu_ipc_command - command with data |
402 | * @cmd: command | 402 | * @cmd: command |
403 | * @sub: sub type | 403 | * @sub: sub type |
404 | * @in: input data | 404 | * @in: input data |
405 | * @inlen: input length in dwords | 405 | * @inlen: input length in dwords |
406 | * @out: output data | 406 | * @out: output data |
407 | * @outlein: output length in dwords | 407 | * @outlein: output length in dwords |
408 | * | 408 | * |
409 | * Issue a command to the SCU which involves data transfers. Do the | 409 | * Issue a command to the SCU which involves data transfers. Do the |
410 | * data copies under the lock but leave it for the caller to interpret | 410 | * data copies under the lock but leave it for the caller to interpret |
411 | */ | 411 | */ |
412 | 412 | ||
413 | int intel_scu_ipc_command(int cmd, int sub, u32 *in, int inlen, | 413 | int intel_scu_ipc_command(int cmd, int sub, u32 *in, int inlen, |
414 | u32 *out, int outlen) | 414 | u32 *out, int outlen) |
415 | { | 415 | { |
416 | int i, err; | 416 | int i, err; |
417 | 417 | ||
418 | mutex_lock(&ipclock); | 418 | mutex_lock(&ipclock); |
419 | if (ipcdev.pdev == NULL) { | 419 | if (ipcdev.pdev == NULL) { |
420 | mutex_unlock(&ipclock); | 420 | mutex_unlock(&ipclock); |
421 | return -ENODEV; | 421 | return -ENODEV; |
422 | } | 422 | } |
423 | 423 | ||
424 | for (i = 0; i < inlen; i++) | 424 | for (i = 0; i < inlen; i++) |
425 | ipc_data_writel(*in++, 4 * i); | 425 | ipc_data_writel(*in++, 4 * i); |
426 | 426 | ||
427 | ipc_command((inlen << 16) | (sub << 12) | cmd); | 427 | ipc_command((inlen << 16) | (sub << 12) | cmd); |
428 | err = busy_loop(); | 428 | err = busy_loop(); |
429 | 429 | ||
430 | for (i = 0; i < outlen; i++) | 430 | for (i = 0; i < outlen; i++) |
431 | *out++ = ipc_data_readl(4 * i); | 431 | *out++ = ipc_data_readl(4 * i); |
432 | 432 | ||
433 | mutex_unlock(&ipclock); | 433 | mutex_unlock(&ipclock); |
434 | return err; | 434 | return err; |
435 | } | 435 | } |
436 | EXPORT_SYMBOL(intel_scu_ipc_command); | 436 | EXPORT_SYMBOL(intel_scu_ipc_command); |
437 | 437 | ||
438 | /*I2C commands */ | 438 | /*I2C commands */ |
439 | #define IPC_I2C_WRITE 1 /* I2C Write command */ | 439 | #define IPC_I2C_WRITE 1 /* I2C Write command */ |
440 | #define IPC_I2C_READ 2 /* I2C Read command */ | 440 | #define IPC_I2C_READ 2 /* I2C Read command */ |
441 | 441 | ||
442 | /** | 442 | /** |
443 | * intel_scu_ipc_i2c_cntrl - I2C read/write operations | 443 | * intel_scu_ipc_i2c_cntrl - I2C read/write operations |
444 | * @addr: I2C address + command bits | 444 | * @addr: I2C address + command bits |
445 | * @data: data to read/write | 445 | * @data: data to read/write |
446 | * | 446 | * |
447 | * Perform an an I2C read/write operation via the SCU. All locking is | 447 | * Perform an an I2C read/write operation via the SCU. All locking is |
448 | * handled for the caller. This function may sleep. | 448 | * handled for the caller. This function may sleep. |
449 | * | 449 | * |
450 | * Returns an error code or 0 on success. | 450 | * Returns an error code or 0 on success. |
451 | * | 451 | * |
452 | * This has to be in the IPC driver for the locking. | 452 | * This has to be in the IPC driver for the locking. |
453 | */ | 453 | */ |
454 | int intel_scu_ipc_i2c_cntrl(u32 addr, u32 *data) | 454 | int intel_scu_ipc_i2c_cntrl(u32 addr, u32 *data) |
455 | { | 455 | { |
456 | u32 cmd = 0; | 456 | u32 cmd = 0; |
457 | 457 | ||
458 | mutex_lock(&ipclock); | 458 | mutex_lock(&ipclock); |
459 | if (ipcdev.pdev == NULL) { | 459 | if (ipcdev.pdev == NULL) { |
460 | mutex_unlock(&ipclock); | 460 | mutex_unlock(&ipclock); |
461 | return -ENODEV; | 461 | return -ENODEV; |
462 | } | 462 | } |
463 | cmd = (addr >> 24) & 0xFF; | 463 | cmd = (addr >> 24) & 0xFF; |
464 | if (cmd == IPC_I2C_READ) { | 464 | if (cmd == IPC_I2C_READ) { |
465 | writel(addr, ipcdev.i2c_base + IPC_I2C_CNTRL_ADDR); | 465 | writel(addr, ipcdev.i2c_base + IPC_I2C_CNTRL_ADDR); |
466 | /* Write not getting updated without delay */ | 466 | /* Write not getting updated without delay */ |
467 | mdelay(1); | 467 | mdelay(1); |
468 | *data = readl(ipcdev.i2c_base + I2C_DATA_ADDR); | 468 | *data = readl(ipcdev.i2c_base + I2C_DATA_ADDR); |
469 | } else if (cmd == IPC_I2C_WRITE) { | 469 | } else if (cmd == IPC_I2C_WRITE) { |
470 | writel(*data, ipcdev.i2c_base + I2C_DATA_ADDR); | 470 | writel(*data, ipcdev.i2c_base + I2C_DATA_ADDR); |
471 | mdelay(1); | 471 | mdelay(1); |
472 | writel(addr, ipcdev.i2c_base + IPC_I2C_CNTRL_ADDR); | 472 | writel(addr, ipcdev.i2c_base + IPC_I2C_CNTRL_ADDR); |
473 | } else { | 473 | } else { |
474 | dev_err(&ipcdev.pdev->dev, | 474 | dev_err(&ipcdev.pdev->dev, |
475 | "intel_scu_ipc: I2C INVALID_CMD = 0x%x\n", cmd); | 475 | "intel_scu_ipc: I2C INVALID_CMD = 0x%x\n", cmd); |
476 | 476 | ||
477 | mutex_unlock(&ipclock); | 477 | mutex_unlock(&ipclock); |
478 | return -EIO; | 478 | return -EIO; |
479 | } | 479 | } |
480 | mutex_unlock(&ipclock); | 480 | mutex_unlock(&ipclock); |
481 | return 0; | 481 | return 0; |
482 | } | 482 | } |
483 | EXPORT_SYMBOL(intel_scu_ipc_i2c_cntrl); | 483 | EXPORT_SYMBOL(intel_scu_ipc_i2c_cntrl); |
484 | 484 | ||
485 | /* | 485 | /* |
486 | * Interrupt handler gets called when ioc bit of IPC_COMMAND_REG set to 1 | 486 | * Interrupt handler gets called when ioc bit of IPC_COMMAND_REG set to 1 |
487 | * When ioc bit is set to 1, caller api must wait for interrupt handler called | 487 | * When ioc bit is set to 1, caller api must wait for interrupt handler called |
488 | * which in turn unlocks the caller api. Currently this is not used | 488 | * which in turn unlocks the caller api. Currently this is not used |
489 | * | 489 | * |
490 | * This is edge triggered so we need take no action to clear anything | 490 | * This is edge triggered so we need take no action to clear anything |
491 | */ | 491 | */ |
492 | static irqreturn_t ioc(int irq, void *dev_id) | 492 | static irqreturn_t ioc(int irq, void *dev_id) |
493 | { | 493 | { |
494 | return IRQ_HANDLED; | 494 | return IRQ_HANDLED; |
495 | } | 495 | } |
496 | 496 | ||
497 | /** | 497 | /** |
498 | * ipc_probe - probe an Intel SCU IPC | 498 | * ipc_probe - probe an Intel SCU IPC |
499 | * @dev: the PCI device matching | 499 | * @dev: the PCI device matching |
500 | * @id: entry in the match table | 500 | * @id: entry in the match table |
501 | * | 501 | * |
502 | * Enable and install an intel SCU IPC. This appears in the PCI space | 502 | * Enable and install an intel SCU IPC. This appears in the PCI space |
503 | * but uses some hard coded addresses as well. | 503 | * but uses some hard coded addresses as well. |
504 | */ | 504 | */ |
505 | static int ipc_probe(struct pci_dev *dev, const struct pci_device_id *id) | 505 | static int ipc_probe(struct pci_dev *dev, const struct pci_device_id *id) |
506 | { | 506 | { |
507 | int err; | 507 | int err; |
508 | resource_size_t pci_resource; | 508 | resource_size_t pci_resource; |
509 | 509 | ||
510 | if (ipcdev.pdev) /* We support only one SCU */ | 510 | if (ipcdev.pdev) /* We support only one SCU */ |
511 | return -EBUSY; | 511 | return -EBUSY; |
512 | 512 | ||
513 | ipcdev.pdev = pci_dev_get(dev); | 513 | ipcdev.pdev = pci_dev_get(dev); |
514 | 514 | ||
515 | err = pci_enable_device(dev); | 515 | err = pci_enable_device(dev); |
516 | if (err) | 516 | if (err) |
517 | return err; | 517 | return err; |
518 | 518 | ||
519 | err = pci_request_regions(dev, "intel_scu_ipc"); | 519 | err = pci_request_regions(dev, "intel_scu_ipc"); |
520 | if (err) | 520 | if (err) |
521 | return err; | 521 | return err; |
522 | 522 | ||
523 | pci_resource = pci_resource_start(dev, 0); | 523 | pci_resource = pci_resource_start(dev, 0); |
524 | if (!pci_resource) | 524 | if (!pci_resource) |
525 | return -ENOMEM; | 525 | return -ENOMEM; |
526 | 526 | ||
527 | if (request_irq(dev->irq, ioc, 0, "intel_scu_ipc", &ipcdev)) | 527 | if (request_irq(dev->irq, ioc, 0, "intel_scu_ipc", &ipcdev)) |
528 | return -EBUSY; | 528 | return -EBUSY; |
529 | 529 | ||
530 | ipcdev.ipc_base = ioremap_nocache(IPC_BASE_ADDR, IPC_MAX_ADDR); | 530 | ipcdev.ipc_base = ioremap_nocache(IPC_BASE_ADDR, IPC_MAX_ADDR); |
531 | if (!ipcdev.ipc_base) | 531 | if (!ipcdev.ipc_base) |
532 | return -ENOMEM; | 532 | return -ENOMEM; |
533 | 533 | ||
534 | ipcdev.i2c_base = ioremap_nocache(IPC_I2C_BASE, IPC_I2C_MAX_ADDR); | 534 | ipcdev.i2c_base = ioremap_nocache(IPC_I2C_BASE, IPC_I2C_MAX_ADDR); |
535 | if (!ipcdev.i2c_base) { | 535 | if (!ipcdev.i2c_base) { |
536 | iounmap(ipcdev.ipc_base); | 536 | iounmap(ipcdev.ipc_base); |
537 | return -ENOMEM; | 537 | return -ENOMEM; |
538 | } | 538 | } |
539 | 539 | ||
540 | intel_scu_devices_create(); | 540 | intel_scu_devices_create(); |
541 | 541 | ||
542 | return 0; | 542 | return 0; |
543 | } | 543 | } |
544 | 544 | ||
545 | /** | 545 | /** |
546 | * ipc_remove - remove a bound IPC device | 546 | * ipc_remove - remove a bound IPC device |
547 | * @pdev: PCI device | 547 | * @pdev: PCI device |
548 | * | 548 | * |
549 | * In practice the SCU is not removable but this function is also | 549 | * In practice the SCU is not removable but this function is also |
550 | * called for each device on a module unload or cleanup which is the | 550 | * called for each device on a module unload or cleanup which is the |
551 | * path that will get used. | 551 | * path that will get used. |
552 | * | 552 | * |
553 | * Free up the mappings and release the PCI resources | 553 | * Free up the mappings and release the PCI resources |
554 | */ | 554 | */ |
555 | static void ipc_remove(struct pci_dev *pdev) | 555 | static void ipc_remove(struct pci_dev *pdev) |
556 | { | 556 | { |
557 | free_irq(pdev->irq, &ipcdev); | 557 | free_irq(pdev->irq, &ipcdev); |
558 | pci_release_regions(pdev); | 558 | pci_release_regions(pdev); |
559 | pci_dev_put(ipcdev.pdev); | 559 | pci_dev_put(ipcdev.pdev); |
560 | iounmap(ipcdev.ipc_base); | 560 | iounmap(ipcdev.ipc_base); |
561 | iounmap(ipcdev.i2c_base); | 561 | iounmap(ipcdev.i2c_base); |
562 | ipcdev.pdev = NULL; | 562 | ipcdev.pdev = NULL; |
563 | intel_scu_devices_destroy(); | 563 | intel_scu_devices_destroy(); |
564 | } | 564 | } |
565 | 565 | ||
566 | static DEFINE_PCI_DEVICE_TABLE(pci_ids) = { | 566 | static DEFINE_PCI_DEVICE_TABLE(pci_ids) = { |
567 | {PCI_DEVICE(PCI_VENDOR_ID_INTEL, 0x082a)}, | 567 | {PCI_DEVICE(PCI_VENDOR_ID_INTEL, 0x082a)}, |
568 | { 0,} | 568 | { 0,} |
569 | }; | 569 | }; |
570 | MODULE_DEVICE_TABLE(pci, pci_ids); | 570 | MODULE_DEVICE_TABLE(pci, pci_ids); |
571 | 571 | ||
572 | static struct pci_driver ipc_driver = { | 572 | static struct pci_driver ipc_driver = { |
573 | .name = "intel_scu_ipc", | 573 | .name = "intel_scu_ipc", |
574 | .id_table = pci_ids, | 574 | .id_table = pci_ids, |
575 | .probe = ipc_probe, | 575 | .probe = ipc_probe, |
576 | .remove = ipc_remove, | 576 | .remove = ipc_remove, |
577 | }; | 577 | }; |
578 | 578 | ||
579 | 579 | ||
580 | static int __init intel_scu_ipc_init(void) | 580 | static int __init intel_scu_ipc_init(void) |
581 | { | 581 | { |
582 | platform = mrst_identify_cpu(); | 582 | platform = mrst_identify_cpu(); |
583 | if (platform == 0) | 583 | if (platform == 0) |
584 | return -ENODEV; | 584 | return -ENODEV; |
585 | return pci_register_driver(&ipc_driver); | 585 | return pci_register_driver(&ipc_driver); |
586 | } | 586 | } |
587 | 587 | ||
588 | static void __exit intel_scu_ipc_exit(void) | 588 | static void __exit intel_scu_ipc_exit(void) |
589 | { | 589 | { |
590 | pci_unregister_driver(&ipc_driver); | 590 | pci_unregister_driver(&ipc_driver); |
591 | } | 591 | } |
592 | 592 | ||
593 | MODULE_AUTHOR("Sreedhara DS <sreedhara.ds@intel.com>"); | 593 | MODULE_AUTHOR("Sreedhara DS <sreedhara.ds@intel.com>"); |
594 | MODULE_DESCRIPTION("Intel SCU IPC driver"); | 594 | MODULE_DESCRIPTION("Intel SCU IPC driver"); |
595 | MODULE_LICENSE("GPL"); | 595 | MODULE_LICENSE("GPL"); |
596 | 596 | ||
597 | module_init(intel_scu_ipc_init); | 597 | module_init(intel_scu_ipc_init); |
598 | module_exit(intel_scu_ipc_exit); | 598 | module_exit(intel_scu_ipc_exit); |
599 | 599 |
drivers/rtc/rtc-mrst.c
1 | /* | 1 | /* |
2 | * rtc-mrst.c: Driver for Moorestown virtual RTC | 2 | * rtc-mrst.c: Driver for Moorestown virtual RTC |
3 | * | 3 | * |
4 | * (C) Copyright 2009 Intel Corporation | 4 | * (C) Copyright 2009 Intel Corporation |
5 | * Author: Jacob Pan (jacob.jun.pan@intel.com) | 5 | * Author: Jacob Pan (jacob.jun.pan@intel.com) |
6 | * Feng Tang (feng.tang@intel.com) | 6 | * Feng Tang (feng.tang@intel.com) |
7 | * | 7 | * |
8 | * This program is free software; you can redistribute it and/or | 8 | * This program is free software; you can redistribute it and/or |
9 | * modify it under the terms of the GNU General Public License | 9 | * modify it under the terms of the GNU General Public License |
10 | * as published by the Free Software Foundation; version 2 | 10 | * as published by the Free Software Foundation; version 2 |
11 | * of the License. | 11 | * of the License. |
12 | * | 12 | * |
13 | * Note: | 13 | * Note: |
14 | * VRTC is emulated by system controller firmware, the real HW | 14 | * VRTC is emulated by system controller firmware, the real HW |
15 | * RTC is located in the PMIC device. SCU FW shadows PMIC RTC | 15 | * RTC is located in the PMIC device. SCU FW shadows PMIC RTC |
16 | * in a memory mapped IO space that is visible to the host IA | 16 | * in a memory mapped IO space that is visible to the host IA |
17 | * processor. | 17 | * processor. |
18 | * | 18 | * |
19 | * This driver is based upon drivers/rtc/rtc-cmos.c | 19 | * This driver is based upon drivers/rtc/rtc-cmos.c |
20 | */ | 20 | */ |
21 | 21 | ||
22 | /* | 22 | /* |
23 | * Note: | 23 | * Note: |
24 | * * vRTC only supports binary mode and 24H mode | 24 | * * vRTC only supports binary mode and 24H mode |
25 | * * vRTC only support PIE and AIE, no UIE, and its PIE only happens | 25 | * * vRTC only support PIE and AIE, no UIE, and its PIE only happens |
26 | * at 23:59:59pm everyday, no support for adjustable frequency | 26 | * at 23:59:59pm everyday, no support for adjustable frequency |
27 | * * Alarm function is also limited to hr/min/sec. | 27 | * * Alarm function is also limited to hr/min/sec. |
28 | */ | 28 | */ |
29 | 29 | ||
30 | #include <linux/mod_devicetable.h> | 30 | #include <linux/mod_devicetable.h> |
31 | #include <linux/platform_device.h> | 31 | #include <linux/platform_device.h> |
32 | #include <linux/interrupt.h> | 32 | #include <linux/interrupt.h> |
33 | #include <linux/spinlock.h> | 33 | #include <linux/spinlock.h> |
34 | #include <linux/kernel.h> | 34 | #include <linux/kernel.h> |
35 | #include <linux/module.h> | 35 | #include <linux/module.h> |
36 | #include <linux/init.h> | 36 | #include <linux/init.h> |
37 | #include <linux/sfi.h> | 37 | #include <linux/sfi.h> |
38 | 38 | ||
39 | #include <asm-generic/rtc.h> | 39 | #include <asm-generic/rtc.h> |
40 | #include <asm/intel_scu_ipc.h> | 40 | #include <asm/intel_scu_ipc.h> |
41 | #include <asm/mrst.h> | 41 | #include <asm/intel-mid.h> |
42 | #include <asm/mrst-vrtc.h> | 42 | #include <asm/intel_mid_vrtc.h> |
43 | 43 | ||
44 | struct mrst_rtc { | 44 | struct mrst_rtc { |
45 | struct rtc_device *rtc; | 45 | struct rtc_device *rtc; |
46 | struct device *dev; | 46 | struct device *dev; |
47 | int irq; | 47 | int irq; |
48 | struct resource *iomem; | 48 | struct resource *iomem; |
49 | 49 | ||
50 | u8 enabled_wake; | 50 | u8 enabled_wake; |
51 | u8 suspend_ctrl; | 51 | u8 suspend_ctrl; |
52 | }; | 52 | }; |
53 | 53 | ||
54 | static const char driver_name[] = "rtc_mrst"; | 54 | static const char driver_name[] = "rtc_mrst"; |
55 | 55 | ||
56 | #define RTC_IRQMASK (RTC_PF | RTC_AF) | 56 | #define RTC_IRQMASK (RTC_PF | RTC_AF) |
57 | 57 | ||
58 | static inline int is_intr(u8 rtc_intr) | 58 | static inline int is_intr(u8 rtc_intr) |
59 | { | 59 | { |
60 | if (!(rtc_intr & RTC_IRQF)) | 60 | if (!(rtc_intr & RTC_IRQF)) |
61 | return 0; | 61 | return 0; |
62 | return rtc_intr & RTC_IRQMASK; | 62 | return rtc_intr & RTC_IRQMASK; |
63 | } | 63 | } |
64 | 64 | ||
65 | static inline unsigned char vrtc_is_updating(void) | 65 | static inline unsigned char vrtc_is_updating(void) |
66 | { | 66 | { |
67 | unsigned char uip; | 67 | unsigned char uip; |
68 | unsigned long flags; | 68 | unsigned long flags; |
69 | 69 | ||
70 | spin_lock_irqsave(&rtc_lock, flags); | 70 | spin_lock_irqsave(&rtc_lock, flags); |
71 | uip = (vrtc_cmos_read(RTC_FREQ_SELECT) & RTC_UIP); | 71 | uip = (vrtc_cmos_read(RTC_FREQ_SELECT) & RTC_UIP); |
72 | spin_unlock_irqrestore(&rtc_lock, flags); | 72 | spin_unlock_irqrestore(&rtc_lock, flags); |
73 | return uip; | 73 | return uip; |
74 | } | 74 | } |
75 | 75 | ||
76 | /* | 76 | /* |
77 | * rtc_time's year contains the increment over 1900, but vRTC's YEAR | 77 | * rtc_time's year contains the increment over 1900, but vRTC's YEAR |
78 | * register can't be programmed to value larger than 0x64, so vRTC | 78 | * register can't be programmed to value larger than 0x64, so vRTC |
79 | * driver chose to use 1972 (1970 is UNIX time start point) as the base, | 79 | * driver chose to use 1972 (1970 is UNIX time start point) as the base, |
80 | * and does the translation at read/write time. | 80 | * and does the translation at read/write time. |
81 | * | 81 | * |
82 | * Why not just use 1970 as the offset? it's because using 1972 will | 82 | * Why not just use 1970 as the offset? it's because using 1972 will |
83 | * make it consistent in leap year setting for both vrtc and low-level | 83 | * make it consistent in leap year setting for both vrtc and low-level |
84 | * physical rtc devices. Then why not use 1960 as the offset? If we use | 84 | * physical rtc devices. Then why not use 1960 as the offset? If we use |
85 | * 1960, for a device's first use, its YEAR register is 0 and the system | 85 | * 1960, for a device's first use, its YEAR register is 0 and the system |
86 | * year will be parsed as 1960 which is not a valid UNIX time and will | 86 | * year will be parsed as 1960 which is not a valid UNIX time and will |
87 | * cause many applications to fail mysteriously. | 87 | * cause many applications to fail mysteriously. |
88 | */ | 88 | */ |
89 | static int mrst_read_time(struct device *dev, struct rtc_time *time) | 89 | static int mrst_read_time(struct device *dev, struct rtc_time *time) |
90 | { | 90 | { |
91 | unsigned long flags; | 91 | unsigned long flags; |
92 | 92 | ||
93 | if (vrtc_is_updating()) | 93 | if (vrtc_is_updating()) |
94 | mdelay(20); | 94 | mdelay(20); |
95 | 95 | ||
96 | spin_lock_irqsave(&rtc_lock, flags); | 96 | spin_lock_irqsave(&rtc_lock, flags); |
97 | time->tm_sec = vrtc_cmos_read(RTC_SECONDS); | 97 | time->tm_sec = vrtc_cmos_read(RTC_SECONDS); |
98 | time->tm_min = vrtc_cmos_read(RTC_MINUTES); | 98 | time->tm_min = vrtc_cmos_read(RTC_MINUTES); |
99 | time->tm_hour = vrtc_cmos_read(RTC_HOURS); | 99 | time->tm_hour = vrtc_cmos_read(RTC_HOURS); |
100 | time->tm_mday = vrtc_cmos_read(RTC_DAY_OF_MONTH); | 100 | time->tm_mday = vrtc_cmos_read(RTC_DAY_OF_MONTH); |
101 | time->tm_mon = vrtc_cmos_read(RTC_MONTH); | 101 | time->tm_mon = vrtc_cmos_read(RTC_MONTH); |
102 | time->tm_year = vrtc_cmos_read(RTC_YEAR); | 102 | time->tm_year = vrtc_cmos_read(RTC_YEAR); |
103 | spin_unlock_irqrestore(&rtc_lock, flags); | 103 | spin_unlock_irqrestore(&rtc_lock, flags); |
104 | 104 | ||
105 | /* Adjust for the 1972/1900 */ | 105 | /* Adjust for the 1972/1900 */ |
106 | time->tm_year += 72; | 106 | time->tm_year += 72; |
107 | time->tm_mon--; | 107 | time->tm_mon--; |
108 | return rtc_valid_tm(time); | 108 | return rtc_valid_tm(time); |
109 | } | 109 | } |
110 | 110 | ||
111 | static int mrst_set_time(struct device *dev, struct rtc_time *time) | 111 | static int mrst_set_time(struct device *dev, struct rtc_time *time) |
112 | { | 112 | { |
113 | int ret; | 113 | int ret; |
114 | unsigned long flags; | 114 | unsigned long flags; |
115 | unsigned char mon, day, hrs, min, sec; | 115 | unsigned char mon, day, hrs, min, sec; |
116 | unsigned int yrs; | 116 | unsigned int yrs; |
117 | 117 | ||
118 | yrs = time->tm_year; | 118 | yrs = time->tm_year; |
119 | mon = time->tm_mon + 1; /* tm_mon starts at zero */ | 119 | mon = time->tm_mon + 1; /* tm_mon starts at zero */ |
120 | day = time->tm_mday; | 120 | day = time->tm_mday; |
121 | hrs = time->tm_hour; | 121 | hrs = time->tm_hour; |
122 | min = time->tm_min; | 122 | min = time->tm_min; |
123 | sec = time->tm_sec; | 123 | sec = time->tm_sec; |
124 | 124 | ||
125 | if (yrs < 72 || yrs > 138) | 125 | if (yrs < 72 || yrs > 138) |
126 | return -EINVAL; | 126 | return -EINVAL; |
127 | yrs -= 72; | 127 | yrs -= 72; |
128 | 128 | ||
129 | spin_lock_irqsave(&rtc_lock, flags); | 129 | spin_lock_irqsave(&rtc_lock, flags); |
130 | 130 | ||
131 | vrtc_cmos_write(yrs, RTC_YEAR); | 131 | vrtc_cmos_write(yrs, RTC_YEAR); |
132 | vrtc_cmos_write(mon, RTC_MONTH); | 132 | vrtc_cmos_write(mon, RTC_MONTH); |
133 | vrtc_cmos_write(day, RTC_DAY_OF_MONTH); | 133 | vrtc_cmos_write(day, RTC_DAY_OF_MONTH); |
134 | vrtc_cmos_write(hrs, RTC_HOURS); | 134 | vrtc_cmos_write(hrs, RTC_HOURS); |
135 | vrtc_cmos_write(min, RTC_MINUTES); | 135 | vrtc_cmos_write(min, RTC_MINUTES); |
136 | vrtc_cmos_write(sec, RTC_SECONDS); | 136 | vrtc_cmos_write(sec, RTC_SECONDS); |
137 | 137 | ||
138 | spin_unlock_irqrestore(&rtc_lock, flags); | 138 | spin_unlock_irqrestore(&rtc_lock, flags); |
139 | 139 | ||
140 | ret = intel_scu_ipc_simple_command(IPCMSG_VRTC, IPC_CMD_VRTC_SETTIME); | 140 | ret = intel_scu_ipc_simple_command(IPCMSG_VRTC, IPC_CMD_VRTC_SETTIME); |
141 | return ret; | 141 | return ret; |
142 | } | 142 | } |
143 | 143 | ||
144 | static int mrst_read_alarm(struct device *dev, struct rtc_wkalrm *t) | 144 | static int mrst_read_alarm(struct device *dev, struct rtc_wkalrm *t) |
145 | { | 145 | { |
146 | struct mrst_rtc *mrst = dev_get_drvdata(dev); | 146 | struct mrst_rtc *mrst = dev_get_drvdata(dev); |
147 | unsigned char rtc_control; | 147 | unsigned char rtc_control; |
148 | 148 | ||
149 | if (mrst->irq <= 0) | 149 | if (mrst->irq <= 0) |
150 | return -EIO; | 150 | return -EIO; |
151 | 151 | ||
152 | /* Basic alarms only support hour, minute, and seconds fields. | 152 | /* Basic alarms only support hour, minute, and seconds fields. |
153 | * Some also support day and month, for alarms up to a year in | 153 | * Some also support day and month, for alarms up to a year in |
154 | * the future. | 154 | * the future. |
155 | */ | 155 | */ |
156 | t->time.tm_mday = -1; | 156 | t->time.tm_mday = -1; |
157 | t->time.tm_mon = -1; | 157 | t->time.tm_mon = -1; |
158 | t->time.tm_year = -1; | 158 | t->time.tm_year = -1; |
159 | 159 | ||
160 | /* vRTC only supports binary mode */ | 160 | /* vRTC only supports binary mode */ |
161 | spin_lock_irq(&rtc_lock); | 161 | spin_lock_irq(&rtc_lock); |
162 | t->time.tm_sec = vrtc_cmos_read(RTC_SECONDS_ALARM); | 162 | t->time.tm_sec = vrtc_cmos_read(RTC_SECONDS_ALARM); |
163 | t->time.tm_min = vrtc_cmos_read(RTC_MINUTES_ALARM); | 163 | t->time.tm_min = vrtc_cmos_read(RTC_MINUTES_ALARM); |
164 | t->time.tm_hour = vrtc_cmos_read(RTC_HOURS_ALARM); | 164 | t->time.tm_hour = vrtc_cmos_read(RTC_HOURS_ALARM); |
165 | 165 | ||
166 | rtc_control = vrtc_cmos_read(RTC_CONTROL); | 166 | rtc_control = vrtc_cmos_read(RTC_CONTROL); |
167 | spin_unlock_irq(&rtc_lock); | 167 | spin_unlock_irq(&rtc_lock); |
168 | 168 | ||
169 | t->enabled = !!(rtc_control & RTC_AIE); | 169 | t->enabled = !!(rtc_control & RTC_AIE); |
170 | t->pending = 0; | 170 | t->pending = 0; |
171 | 171 | ||
172 | return 0; | 172 | return 0; |
173 | } | 173 | } |
174 | 174 | ||
175 | static void mrst_checkintr(struct mrst_rtc *mrst, unsigned char rtc_control) | 175 | static void mrst_checkintr(struct mrst_rtc *mrst, unsigned char rtc_control) |
176 | { | 176 | { |
177 | unsigned char rtc_intr; | 177 | unsigned char rtc_intr; |
178 | 178 | ||
179 | /* | 179 | /* |
180 | * NOTE after changing RTC_xIE bits we always read INTR_FLAGS; | 180 | * NOTE after changing RTC_xIE bits we always read INTR_FLAGS; |
181 | * allegedly some older rtcs need that to handle irqs properly | 181 | * allegedly some older rtcs need that to handle irqs properly |
182 | */ | 182 | */ |
183 | rtc_intr = vrtc_cmos_read(RTC_INTR_FLAGS); | 183 | rtc_intr = vrtc_cmos_read(RTC_INTR_FLAGS); |
184 | rtc_intr &= (rtc_control & RTC_IRQMASK) | RTC_IRQF; | 184 | rtc_intr &= (rtc_control & RTC_IRQMASK) | RTC_IRQF; |
185 | if (is_intr(rtc_intr)) | 185 | if (is_intr(rtc_intr)) |
186 | rtc_update_irq(mrst->rtc, 1, rtc_intr); | 186 | rtc_update_irq(mrst->rtc, 1, rtc_intr); |
187 | } | 187 | } |
188 | 188 | ||
189 | static void mrst_irq_enable(struct mrst_rtc *mrst, unsigned char mask) | 189 | static void mrst_irq_enable(struct mrst_rtc *mrst, unsigned char mask) |
190 | { | 190 | { |
191 | unsigned char rtc_control; | 191 | unsigned char rtc_control; |
192 | 192 | ||
193 | /* | 193 | /* |
194 | * Flush any pending IRQ status, notably for update irqs, | 194 | * Flush any pending IRQ status, notably for update irqs, |
195 | * before we enable new IRQs | 195 | * before we enable new IRQs |
196 | */ | 196 | */ |
197 | rtc_control = vrtc_cmos_read(RTC_CONTROL); | 197 | rtc_control = vrtc_cmos_read(RTC_CONTROL); |
198 | mrst_checkintr(mrst, rtc_control); | 198 | mrst_checkintr(mrst, rtc_control); |
199 | 199 | ||
200 | rtc_control |= mask; | 200 | rtc_control |= mask; |
201 | vrtc_cmos_write(rtc_control, RTC_CONTROL); | 201 | vrtc_cmos_write(rtc_control, RTC_CONTROL); |
202 | 202 | ||
203 | mrst_checkintr(mrst, rtc_control); | 203 | mrst_checkintr(mrst, rtc_control); |
204 | } | 204 | } |
205 | 205 | ||
206 | static void mrst_irq_disable(struct mrst_rtc *mrst, unsigned char mask) | 206 | static void mrst_irq_disable(struct mrst_rtc *mrst, unsigned char mask) |
207 | { | 207 | { |
208 | unsigned char rtc_control; | 208 | unsigned char rtc_control; |
209 | 209 | ||
210 | rtc_control = vrtc_cmos_read(RTC_CONTROL); | 210 | rtc_control = vrtc_cmos_read(RTC_CONTROL); |
211 | rtc_control &= ~mask; | 211 | rtc_control &= ~mask; |
212 | vrtc_cmos_write(rtc_control, RTC_CONTROL); | 212 | vrtc_cmos_write(rtc_control, RTC_CONTROL); |
213 | mrst_checkintr(mrst, rtc_control); | 213 | mrst_checkintr(mrst, rtc_control); |
214 | } | 214 | } |
215 | 215 | ||
216 | static int mrst_set_alarm(struct device *dev, struct rtc_wkalrm *t) | 216 | static int mrst_set_alarm(struct device *dev, struct rtc_wkalrm *t) |
217 | { | 217 | { |
218 | struct mrst_rtc *mrst = dev_get_drvdata(dev); | 218 | struct mrst_rtc *mrst = dev_get_drvdata(dev); |
219 | unsigned char hrs, min, sec; | 219 | unsigned char hrs, min, sec; |
220 | int ret = 0; | 220 | int ret = 0; |
221 | 221 | ||
222 | if (!mrst->irq) | 222 | if (!mrst->irq) |
223 | return -EIO; | 223 | return -EIO; |
224 | 224 | ||
225 | hrs = t->time.tm_hour; | 225 | hrs = t->time.tm_hour; |
226 | min = t->time.tm_min; | 226 | min = t->time.tm_min; |
227 | sec = t->time.tm_sec; | 227 | sec = t->time.tm_sec; |
228 | 228 | ||
229 | spin_lock_irq(&rtc_lock); | 229 | spin_lock_irq(&rtc_lock); |
230 | /* Next rtc irq must not be from previous alarm setting */ | 230 | /* Next rtc irq must not be from previous alarm setting */ |
231 | mrst_irq_disable(mrst, RTC_AIE); | 231 | mrst_irq_disable(mrst, RTC_AIE); |
232 | 232 | ||
233 | /* Update alarm */ | 233 | /* Update alarm */ |
234 | vrtc_cmos_write(hrs, RTC_HOURS_ALARM); | 234 | vrtc_cmos_write(hrs, RTC_HOURS_ALARM); |
235 | vrtc_cmos_write(min, RTC_MINUTES_ALARM); | 235 | vrtc_cmos_write(min, RTC_MINUTES_ALARM); |
236 | vrtc_cmos_write(sec, RTC_SECONDS_ALARM); | 236 | vrtc_cmos_write(sec, RTC_SECONDS_ALARM); |
237 | 237 | ||
238 | spin_unlock_irq(&rtc_lock); | 238 | spin_unlock_irq(&rtc_lock); |
239 | 239 | ||
240 | ret = intel_scu_ipc_simple_command(IPCMSG_VRTC, IPC_CMD_VRTC_SETALARM); | 240 | ret = intel_scu_ipc_simple_command(IPCMSG_VRTC, IPC_CMD_VRTC_SETALARM); |
241 | if (ret) | 241 | if (ret) |
242 | return ret; | 242 | return ret; |
243 | 243 | ||
244 | spin_lock_irq(&rtc_lock); | 244 | spin_lock_irq(&rtc_lock); |
245 | if (t->enabled) | 245 | if (t->enabled) |
246 | mrst_irq_enable(mrst, RTC_AIE); | 246 | mrst_irq_enable(mrst, RTC_AIE); |
247 | 247 | ||
248 | spin_unlock_irq(&rtc_lock); | 248 | spin_unlock_irq(&rtc_lock); |
249 | 249 | ||
250 | return 0; | 250 | return 0; |
251 | } | 251 | } |
252 | 252 | ||
253 | /* Currently, the vRTC doesn't support UIE ON/OFF */ | 253 | /* Currently, the vRTC doesn't support UIE ON/OFF */ |
254 | static int mrst_rtc_alarm_irq_enable(struct device *dev, unsigned int enabled) | 254 | static int mrst_rtc_alarm_irq_enable(struct device *dev, unsigned int enabled) |
255 | { | 255 | { |
256 | struct mrst_rtc *mrst = dev_get_drvdata(dev); | 256 | struct mrst_rtc *mrst = dev_get_drvdata(dev); |
257 | unsigned long flags; | 257 | unsigned long flags; |
258 | 258 | ||
259 | spin_lock_irqsave(&rtc_lock, flags); | 259 | spin_lock_irqsave(&rtc_lock, flags); |
260 | if (enabled) | 260 | if (enabled) |
261 | mrst_irq_enable(mrst, RTC_AIE); | 261 | mrst_irq_enable(mrst, RTC_AIE); |
262 | else | 262 | else |
263 | mrst_irq_disable(mrst, RTC_AIE); | 263 | mrst_irq_disable(mrst, RTC_AIE); |
264 | spin_unlock_irqrestore(&rtc_lock, flags); | 264 | spin_unlock_irqrestore(&rtc_lock, flags); |
265 | return 0; | 265 | return 0; |
266 | } | 266 | } |
267 | 267 | ||
268 | 268 | ||
269 | #if defined(CONFIG_RTC_INTF_PROC) || defined(CONFIG_RTC_INTF_PROC_MODULE) | 269 | #if defined(CONFIG_RTC_INTF_PROC) || defined(CONFIG_RTC_INTF_PROC_MODULE) |
270 | 270 | ||
271 | static int mrst_procfs(struct device *dev, struct seq_file *seq) | 271 | static int mrst_procfs(struct device *dev, struct seq_file *seq) |
272 | { | 272 | { |
273 | unsigned char rtc_control, valid; | 273 | unsigned char rtc_control, valid; |
274 | 274 | ||
275 | spin_lock_irq(&rtc_lock); | 275 | spin_lock_irq(&rtc_lock); |
276 | rtc_control = vrtc_cmos_read(RTC_CONTROL); | 276 | rtc_control = vrtc_cmos_read(RTC_CONTROL); |
277 | valid = vrtc_cmos_read(RTC_VALID); | 277 | valid = vrtc_cmos_read(RTC_VALID); |
278 | spin_unlock_irq(&rtc_lock); | 278 | spin_unlock_irq(&rtc_lock); |
279 | 279 | ||
280 | return seq_printf(seq, | 280 | return seq_printf(seq, |
281 | "periodic_IRQ\t: %s\n" | 281 | "periodic_IRQ\t: %s\n" |
282 | "alarm\t\t: %s\n" | 282 | "alarm\t\t: %s\n" |
283 | "BCD\t\t: no\n" | 283 | "BCD\t\t: no\n" |
284 | "periodic_freq\t: daily (not adjustable)\n", | 284 | "periodic_freq\t: daily (not adjustable)\n", |
285 | (rtc_control & RTC_PIE) ? "on" : "off", | 285 | (rtc_control & RTC_PIE) ? "on" : "off", |
286 | (rtc_control & RTC_AIE) ? "on" : "off"); | 286 | (rtc_control & RTC_AIE) ? "on" : "off"); |
287 | } | 287 | } |
288 | 288 | ||
289 | #else | 289 | #else |
290 | #define mrst_procfs NULL | 290 | #define mrst_procfs NULL |
291 | #endif | 291 | #endif |
292 | 292 | ||
293 | static const struct rtc_class_ops mrst_rtc_ops = { | 293 | static const struct rtc_class_ops mrst_rtc_ops = { |
294 | .read_time = mrst_read_time, | 294 | .read_time = mrst_read_time, |
295 | .set_time = mrst_set_time, | 295 | .set_time = mrst_set_time, |
296 | .read_alarm = mrst_read_alarm, | 296 | .read_alarm = mrst_read_alarm, |
297 | .set_alarm = mrst_set_alarm, | 297 | .set_alarm = mrst_set_alarm, |
298 | .proc = mrst_procfs, | 298 | .proc = mrst_procfs, |
299 | .alarm_irq_enable = mrst_rtc_alarm_irq_enable, | 299 | .alarm_irq_enable = mrst_rtc_alarm_irq_enable, |
300 | }; | 300 | }; |
301 | 301 | ||
302 | static struct mrst_rtc mrst_rtc; | 302 | static struct mrst_rtc mrst_rtc; |
303 | 303 | ||
304 | /* | 304 | /* |
305 | * When vRTC IRQ is captured by SCU FW, FW will clear the AIE bit in | 305 | * When vRTC IRQ is captured by SCU FW, FW will clear the AIE bit in |
306 | * Reg B, so no need for this driver to clear it | 306 | * Reg B, so no need for this driver to clear it |
307 | */ | 307 | */ |
308 | static irqreturn_t mrst_rtc_irq(int irq, void *p) | 308 | static irqreturn_t mrst_rtc_irq(int irq, void *p) |
309 | { | 309 | { |
310 | u8 irqstat; | 310 | u8 irqstat; |
311 | 311 | ||
312 | spin_lock(&rtc_lock); | 312 | spin_lock(&rtc_lock); |
313 | /* This read will clear all IRQ flags inside Reg C */ | 313 | /* This read will clear all IRQ flags inside Reg C */ |
314 | irqstat = vrtc_cmos_read(RTC_INTR_FLAGS); | 314 | irqstat = vrtc_cmos_read(RTC_INTR_FLAGS); |
315 | spin_unlock(&rtc_lock); | 315 | spin_unlock(&rtc_lock); |
316 | 316 | ||
317 | irqstat &= RTC_IRQMASK | RTC_IRQF; | 317 | irqstat &= RTC_IRQMASK | RTC_IRQF; |
318 | if (is_intr(irqstat)) { | 318 | if (is_intr(irqstat)) { |
319 | rtc_update_irq(p, 1, irqstat); | 319 | rtc_update_irq(p, 1, irqstat); |
320 | return IRQ_HANDLED; | 320 | return IRQ_HANDLED; |
321 | } | 321 | } |
322 | return IRQ_NONE; | 322 | return IRQ_NONE; |
323 | } | 323 | } |
324 | 324 | ||
325 | static int vrtc_mrst_do_probe(struct device *dev, struct resource *iomem, | 325 | static int vrtc_mrst_do_probe(struct device *dev, struct resource *iomem, |
326 | int rtc_irq) | 326 | int rtc_irq) |
327 | { | 327 | { |
328 | int retval = 0; | 328 | int retval = 0; |
329 | unsigned char rtc_control; | 329 | unsigned char rtc_control; |
330 | 330 | ||
331 | /* There can be only one ... */ | 331 | /* There can be only one ... */ |
332 | if (mrst_rtc.dev) | 332 | if (mrst_rtc.dev) |
333 | return -EBUSY; | 333 | return -EBUSY; |
334 | 334 | ||
335 | if (!iomem) | 335 | if (!iomem) |
336 | return -ENODEV; | 336 | return -ENODEV; |
337 | 337 | ||
338 | iomem = request_mem_region(iomem->start, resource_size(iomem), | 338 | iomem = request_mem_region(iomem->start, resource_size(iomem), |
339 | driver_name); | 339 | driver_name); |
340 | if (!iomem) { | 340 | if (!iomem) { |
341 | dev_dbg(dev, "i/o mem already in use.\n"); | 341 | dev_dbg(dev, "i/o mem already in use.\n"); |
342 | return -EBUSY; | 342 | return -EBUSY; |
343 | } | 343 | } |
344 | 344 | ||
345 | mrst_rtc.irq = rtc_irq; | 345 | mrst_rtc.irq = rtc_irq; |
346 | mrst_rtc.iomem = iomem; | 346 | mrst_rtc.iomem = iomem; |
347 | mrst_rtc.dev = dev; | 347 | mrst_rtc.dev = dev; |
348 | dev_set_drvdata(dev, &mrst_rtc); | 348 | dev_set_drvdata(dev, &mrst_rtc); |
349 | 349 | ||
350 | mrst_rtc.rtc = rtc_device_register(driver_name, dev, | 350 | mrst_rtc.rtc = rtc_device_register(driver_name, dev, |
351 | &mrst_rtc_ops, THIS_MODULE); | 351 | &mrst_rtc_ops, THIS_MODULE); |
352 | if (IS_ERR(mrst_rtc.rtc)) { | 352 | if (IS_ERR(mrst_rtc.rtc)) { |
353 | retval = PTR_ERR(mrst_rtc.rtc); | 353 | retval = PTR_ERR(mrst_rtc.rtc); |
354 | goto cleanup0; | 354 | goto cleanup0; |
355 | } | 355 | } |
356 | 356 | ||
357 | rename_region(iomem, dev_name(&mrst_rtc.rtc->dev)); | 357 | rename_region(iomem, dev_name(&mrst_rtc.rtc->dev)); |
358 | 358 | ||
359 | spin_lock_irq(&rtc_lock); | 359 | spin_lock_irq(&rtc_lock); |
360 | mrst_irq_disable(&mrst_rtc, RTC_PIE | RTC_AIE); | 360 | mrst_irq_disable(&mrst_rtc, RTC_PIE | RTC_AIE); |
361 | rtc_control = vrtc_cmos_read(RTC_CONTROL); | 361 | rtc_control = vrtc_cmos_read(RTC_CONTROL); |
362 | spin_unlock_irq(&rtc_lock); | 362 | spin_unlock_irq(&rtc_lock); |
363 | 363 | ||
364 | if (!(rtc_control & RTC_24H) || (rtc_control & (RTC_DM_BINARY))) | 364 | if (!(rtc_control & RTC_24H) || (rtc_control & (RTC_DM_BINARY))) |
365 | dev_dbg(dev, "TODO: support more than 24-hr BCD mode\n"); | 365 | dev_dbg(dev, "TODO: support more than 24-hr BCD mode\n"); |
366 | 366 | ||
367 | if (rtc_irq) { | 367 | if (rtc_irq) { |
368 | retval = request_irq(rtc_irq, mrst_rtc_irq, | 368 | retval = request_irq(rtc_irq, mrst_rtc_irq, |
369 | 0, dev_name(&mrst_rtc.rtc->dev), | 369 | 0, dev_name(&mrst_rtc.rtc->dev), |
370 | mrst_rtc.rtc); | 370 | mrst_rtc.rtc); |
371 | if (retval < 0) { | 371 | if (retval < 0) { |
372 | dev_dbg(dev, "IRQ %d is already in use, err %d\n", | 372 | dev_dbg(dev, "IRQ %d is already in use, err %d\n", |
373 | rtc_irq, retval); | 373 | rtc_irq, retval); |
374 | goto cleanup1; | 374 | goto cleanup1; |
375 | } | 375 | } |
376 | } | 376 | } |
377 | dev_dbg(dev, "initialised\n"); | 377 | dev_dbg(dev, "initialised\n"); |
378 | return 0; | 378 | return 0; |
379 | 379 | ||
380 | cleanup1: | 380 | cleanup1: |
381 | rtc_device_unregister(mrst_rtc.rtc); | 381 | rtc_device_unregister(mrst_rtc.rtc); |
382 | cleanup0: | 382 | cleanup0: |
383 | dev_set_drvdata(dev, NULL); | 383 | dev_set_drvdata(dev, NULL); |
384 | mrst_rtc.dev = NULL; | 384 | mrst_rtc.dev = NULL; |
385 | release_mem_region(iomem->start, resource_size(iomem)); | 385 | release_mem_region(iomem->start, resource_size(iomem)); |
386 | dev_err(dev, "rtc-mrst: unable to initialise\n"); | 386 | dev_err(dev, "rtc-mrst: unable to initialise\n"); |
387 | return retval; | 387 | return retval; |
388 | } | 388 | } |
389 | 389 | ||
390 | static void rtc_mrst_do_shutdown(void) | 390 | static void rtc_mrst_do_shutdown(void) |
391 | { | 391 | { |
392 | spin_lock_irq(&rtc_lock); | 392 | spin_lock_irq(&rtc_lock); |
393 | mrst_irq_disable(&mrst_rtc, RTC_IRQMASK); | 393 | mrst_irq_disable(&mrst_rtc, RTC_IRQMASK); |
394 | spin_unlock_irq(&rtc_lock); | 394 | spin_unlock_irq(&rtc_lock); |
395 | } | 395 | } |
396 | 396 | ||
397 | static void rtc_mrst_do_remove(struct device *dev) | 397 | static void rtc_mrst_do_remove(struct device *dev) |
398 | { | 398 | { |
399 | struct mrst_rtc *mrst = dev_get_drvdata(dev); | 399 | struct mrst_rtc *mrst = dev_get_drvdata(dev); |
400 | struct resource *iomem; | 400 | struct resource *iomem; |
401 | 401 | ||
402 | rtc_mrst_do_shutdown(); | 402 | rtc_mrst_do_shutdown(); |
403 | 403 | ||
404 | if (mrst->irq) | 404 | if (mrst->irq) |
405 | free_irq(mrst->irq, mrst->rtc); | 405 | free_irq(mrst->irq, mrst->rtc); |
406 | 406 | ||
407 | rtc_device_unregister(mrst->rtc); | 407 | rtc_device_unregister(mrst->rtc); |
408 | mrst->rtc = NULL; | 408 | mrst->rtc = NULL; |
409 | 409 | ||
410 | iomem = mrst->iomem; | 410 | iomem = mrst->iomem; |
411 | release_mem_region(iomem->start, resource_size(iomem)); | 411 | release_mem_region(iomem->start, resource_size(iomem)); |
412 | mrst->iomem = NULL; | 412 | mrst->iomem = NULL; |
413 | 413 | ||
414 | mrst->dev = NULL; | 414 | mrst->dev = NULL; |
415 | dev_set_drvdata(dev, NULL); | 415 | dev_set_drvdata(dev, NULL); |
416 | } | 416 | } |
417 | 417 | ||
418 | #ifdef CONFIG_PM | 418 | #ifdef CONFIG_PM |
419 | static int mrst_suspend(struct device *dev, pm_message_t mesg) | 419 | static int mrst_suspend(struct device *dev, pm_message_t mesg) |
420 | { | 420 | { |
421 | struct mrst_rtc *mrst = dev_get_drvdata(dev); | 421 | struct mrst_rtc *mrst = dev_get_drvdata(dev); |
422 | unsigned char tmp; | 422 | unsigned char tmp; |
423 | 423 | ||
424 | /* Only the alarm might be a wakeup event source */ | 424 | /* Only the alarm might be a wakeup event source */ |
425 | spin_lock_irq(&rtc_lock); | 425 | spin_lock_irq(&rtc_lock); |
426 | mrst->suspend_ctrl = tmp = vrtc_cmos_read(RTC_CONTROL); | 426 | mrst->suspend_ctrl = tmp = vrtc_cmos_read(RTC_CONTROL); |
427 | if (tmp & (RTC_PIE | RTC_AIE)) { | 427 | if (tmp & (RTC_PIE | RTC_AIE)) { |
428 | unsigned char mask; | 428 | unsigned char mask; |
429 | 429 | ||
430 | if (device_may_wakeup(dev)) | 430 | if (device_may_wakeup(dev)) |
431 | mask = RTC_IRQMASK & ~RTC_AIE; | 431 | mask = RTC_IRQMASK & ~RTC_AIE; |
432 | else | 432 | else |
433 | mask = RTC_IRQMASK; | 433 | mask = RTC_IRQMASK; |
434 | tmp &= ~mask; | 434 | tmp &= ~mask; |
435 | vrtc_cmos_write(tmp, RTC_CONTROL); | 435 | vrtc_cmos_write(tmp, RTC_CONTROL); |
436 | 436 | ||
437 | mrst_checkintr(mrst, tmp); | 437 | mrst_checkintr(mrst, tmp); |
438 | } | 438 | } |
439 | spin_unlock_irq(&rtc_lock); | 439 | spin_unlock_irq(&rtc_lock); |
440 | 440 | ||
441 | if (tmp & RTC_AIE) { | 441 | if (tmp & RTC_AIE) { |
442 | mrst->enabled_wake = 1; | 442 | mrst->enabled_wake = 1; |
443 | enable_irq_wake(mrst->irq); | 443 | enable_irq_wake(mrst->irq); |
444 | } | 444 | } |
445 | 445 | ||
446 | dev_dbg(&mrst_rtc.rtc->dev, "suspend%s, ctrl %02x\n", | 446 | dev_dbg(&mrst_rtc.rtc->dev, "suspend%s, ctrl %02x\n", |
447 | (tmp & RTC_AIE) ? ", alarm may wake" : "", | 447 | (tmp & RTC_AIE) ? ", alarm may wake" : "", |
448 | tmp); | 448 | tmp); |
449 | 449 | ||
450 | return 0; | 450 | return 0; |
451 | } | 451 | } |
452 | 452 | ||
453 | /* | 453 | /* |
454 | * We want RTC alarms to wake us from the deep power saving state | 454 | * We want RTC alarms to wake us from the deep power saving state |
455 | */ | 455 | */ |
456 | static inline int mrst_poweroff(struct device *dev) | 456 | static inline int mrst_poweroff(struct device *dev) |
457 | { | 457 | { |
458 | return mrst_suspend(dev, PMSG_HIBERNATE); | 458 | return mrst_suspend(dev, PMSG_HIBERNATE); |
459 | } | 459 | } |
460 | 460 | ||
461 | static int mrst_resume(struct device *dev) | 461 | static int mrst_resume(struct device *dev) |
462 | { | 462 | { |
463 | struct mrst_rtc *mrst = dev_get_drvdata(dev); | 463 | struct mrst_rtc *mrst = dev_get_drvdata(dev); |
464 | unsigned char tmp = mrst->suspend_ctrl; | 464 | unsigned char tmp = mrst->suspend_ctrl; |
465 | 465 | ||
466 | /* Re-enable any irqs previously active */ | 466 | /* Re-enable any irqs previously active */ |
467 | if (tmp & RTC_IRQMASK) { | 467 | if (tmp & RTC_IRQMASK) { |
468 | unsigned char mask; | 468 | unsigned char mask; |
469 | 469 | ||
470 | if (mrst->enabled_wake) { | 470 | if (mrst->enabled_wake) { |
471 | disable_irq_wake(mrst->irq); | 471 | disable_irq_wake(mrst->irq); |
472 | mrst->enabled_wake = 0; | 472 | mrst->enabled_wake = 0; |
473 | } | 473 | } |
474 | 474 | ||
475 | spin_lock_irq(&rtc_lock); | 475 | spin_lock_irq(&rtc_lock); |
476 | do { | 476 | do { |
477 | vrtc_cmos_write(tmp, RTC_CONTROL); | 477 | vrtc_cmos_write(tmp, RTC_CONTROL); |
478 | 478 | ||
479 | mask = vrtc_cmos_read(RTC_INTR_FLAGS); | 479 | mask = vrtc_cmos_read(RTC_INTR_FLAGS); |
480 | mask &= (tmp & RTC_IRQMASK) | RTC_IRQF; | 480 | mask &= (tmp & RTC_IRQMASK) | RTC_IRQF; |
481 | if (!is_intr(mask)) | 481 | if (!is_intr(mask)) |
482 | break; | 482 | break; |
483 | 483 | ||
484 | rtc_update_irq(mrst->rtc, 1, mask); | 484 | rtc_update_irq(mrst->rtc, 1, mask); |
485 | tmp &= ~RTC_AIE; | 485 | tmp &= ~RTC_AIE; |
486 | } while (mask & RTC_AIE); | 486 | } while (mask & RTC_AIE); |
487 | spin_unlock_irq(&rtc_lock); | 487 | spin_unlock_irq(&rtc_lock); |
488 | } | 488 | } |
489 | 489 | ||
490 | dev_dbg(&mrst_rtc.rtc->dev, "resume, ctrl %02x\n", tmp); | 490 | dev_dbg(&mrst_rtc.rtc->dev, "resume, ctrl %02x\n", tmp); |
491 | 491 | ||
492 | return 0; | 492 | return 0; |
493 | } | 493 | } |
494 | 494 | ||
495 | #else | 495 | #else |
496 | #define mrst_suspend NULL | 496 | #define mrst_suspend NULL |
497 | #define mrst_resume NULL | 497 | #define mrst_resume NULL |
498 | 498 | ||
499 | static inline int mrst_poweroff(struct device *dev) | 499 | static inline int mrst_poweroff(struct device *dev) |
500 | { | 500 | { |
501 | return -ENOSYS; | 501 | return -ENOSYS; |
502 | } | 502 | } |
503 | 503 | ||
504 | #endif | 504 | #endif |
505 | 505 | ||
506 | static int vrtc_mrst_platform_probe(struct platform_device *pdev) | 506 | static int vrtc_mrst_platform_probe(struct platform_device *pdev) |
507 | { | 507 | { |
508 | return vrtc_mrst_do_probe(&pdev->dev, | 508 | return vrtc_mrst_do_probe(&pdev->dev, |
509 | platform_get_resource(pdev, IORESOURCE_MEM, 0), | 509 | platform_get_resource(pdev, IORESOURCE_MEM, 0), |
510 | platform_get_irq(pdev, 0)); | 510 | platform_get_irq(pdev, 0)); |
511 | } | 511 | } |
512 | 512 | ||
513 | static int vrtc_mrst_platform_remove(struct platform_device *pdev) | 513 | static int vrtc_mrst_platform_remove(struct platform_device *pdev) |
514 | { | 514 | { |
515 | rtc_mrst_do_remove(&pdev->dev); | 515 | rtc_mrst_do_remove(&pdev->dev); |
516 | return 0; | 516 | return 0; |
517 | } | 517 | } |
518 | 518 | ||
519 | static void vrtc_mrst_platform_shutdown(struct platform_device *pdev) | 519 | static void vrtc_mrst_platform_shutdown(struct platform_device *pdev) |
520 | { | 520 | { |
521 | if (system_state == SYSTEM_POWER_OFF && !mrst_poweroff(&pdev->dev)) | 521 | if (system_state == SYSTEM_POWER_OFF && !mrst_poweroff(&pdev->dev)) |
522 | return; | 522 | return; |
523 | 523 | ||
524 | rtc_mrst_do_shutdown(); | 524 | rtc_mrst_do_shutdown(); |
525 | } | 525 | } |
526 | 526 | ||
527 | MODULE_ALIAS("platform:vrtc_mrst"); | 527 | MODULE_ALIAS("platform:vrtc_mrst"); |
528 | 528 | ||
529 | static struct platform_driver vrtc_mrst_platform_driver = { | 529 | static struct platform_driver vrtc_mrst_platform_driver = { |
530 | .probe = vrtc_mrst_platform_probe, | 530 | .probe = vrtc_mrst_platform_probe, |
531 | .remove = vrtc_mrst_platform_remove, | 531 | .remove = vrtc_mrst_platform_remove, |
532 | .shutdown = vrtc_mrst_platform_shutdown, | 532 | .shutdown = vrtc_mrst_platform_shutdown, |
533 | .driver = { | 533 | .driver = { |
534 | .name = (char *) driver_name, | 534 | .name = (char *) driver_name, |
535 | .suspend = mrst_suspend, | 535 | .suspend = mrst_suspend, |
536 | .resume = mrst_resume, | 536 | .resume = mrst_resume, |
537 | } | 537 | } |
538 | }; | 538 | }; |
539 | 539 | ||
540 | module_platform_driver(vrtc_mrst_platform_driver); | 540 | module_platform_driver(vrtc_mrst_platform_driver); |
541 | 541 | ||
542 | MODULE_AUTHOR("Jacob Pan; Feng Tang"); | 542 | MODULE_AUTHOR("Jacob Pan; Feng Tang"); |
543 | MODULE_DESCRIPTION("Driver for Moorestown virtual RTC"); | 543 | MODULE_DESCRIPTION("Driver for Moorestown virtual RTC"); |
544 | MODULE_LICENSE("GPL"); | 544 | MODULE_LICENSE("GPL"); |
545 | 545 |
drivers/watchdog/intel_scu_watchdog.c
1 | /* | 1 | /* |
2 | * Intel_SCU 0.2: An Intel SCU IOH Based Watchdog Device | 2 | * Intel_SCU 0.2: An Intel SCU IOH Based Watchdog Device |
3 | * for Intel part #(s): | 3 | * for Intel part #(s): |
4 | * - AF82MP20 PCH | 4 | * - AF82MP20 PCH |
5 | * | 5 | * |
6 | * Copyright (C) 2009-2010 Intel Corporation. All rights reserved. | 6 | * Copyright (C) 2009-2010 Intel Corporation. All rights reserved. |
7 | * | 7 | * |
8 | * This program is free software; you can redistribute it and/or | 8 | * This program is free software; you can redistribute it and/or |
9 | * modify it under the terms of version 2 of the GNU General | 9 | * modify it under the terms of version 2 of the GNU General |
10 | * Public License as published by the Free Software Foundation. | 10 | * Public License as published by the Free Software Foundation. |
11 | * | 11 | * |
12 | * This program is distributed in the hope that it will be | 12 | * This program is distributed in the hope that it will be |
13 | * useful, but WITHOUT ANY WARRANTY; without even the implied | 13 | * useful, but WITHOUT ANY WARRANTY; without even the implied |
14 | * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR | 14 | * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR |
15 | * PURPOSE. See the GNU General Public License for more details. | 15 | * PURPOSE. See the GNU General Public License for more details. |
16 | * You should have received a copy of the GNU General Public | 16 | * You should have received a copy of the GNU General Public |
17 | * License along with this program; if not, write to the Free | 17 | * License along with this program; if not, write to the Free |
18 | * Software Foundation, Inc., 59 Temple Place - Suite 330, | 18 | * Software Foundation, Inc., 59 Temple Place - Suite 330, |
19 | * Boston, MA 02111-1307, USA. | 19 | * Boston, MA 02111-1307, USA. |
20 | * The full GNU General Public License is included in this | 20 | * The full GNU General Public License is included in this |
21 | * distribution in the file called COPYING. | 21 | * distribution in the file called COPYING. |
22 | * | 22 | * |
23 | */ | 23 | */ |
24 | 24 | ||
25 | #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt | 25 | #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt |
26 | 26 | ||
27 | #include <linux/compiler.h> | 27 | #include <linux/compiler.h> |
28 | #include <linux/module.h> | 28 | #include <linux/module.h> |
29 | #include <linux/kernel.h> | 29 | #include <linux/kernel.h> |
30 | #include <linux/moduleparam.h> | 30 | #include <linux/moduleparam.h> |
31 | #include <linux/types.h> | 31 | #include <linux/types.h> |
32 | #include <linux/miscdevice.h> | 32 | #include <linux/miscdevice.h> |
33 | #include <linux/watchdog.h> | 33 | #include <linux/watchdog.h> |
34 | #include <linux/fs.h> | 34 | #include <linux/fs.h> |
35 | #include <linux/notifier.h> | 35 | #include <linux/notifier.h> |
36 | #include <linux/reboot.h> | 36 | #include <linux/reboot.h> |
37 | #include <linux/init.h> | 37 | #include <linux/init.h> |
38 | #include <linux/jiffies.h> | 38 | #include <linux/jiffies.h> |
39 | #include <linux/uaccess.h> | 39 | #include <linux/uaccess.h> |
40 | #include <linux/slab.h> | 40 | #include <linux/slab.h> |
41 | #include <linux/io.h> | 41 | #include <linux/io.h> |
42 | #include <linux/interrupt.h> | 42 | #include <linux/interrupt.h> |
43 | #include <linux/delay.h> | 43 | #include <linux/delay.h> |
44 | #include <linux/sched.h> | 44 | #include <linux/sched.h> |
45 | #include <linux/signal.h> | 45 | #include <linux/signal.h> |
46 | #include <linux/sfi.h> | 46 | #include <linux/sfi.h> |
47 | #include <asm/irq.h> | 47 | #include <asm/irq.h> |
48 | #include <linux/atomic.h> | 48 | #include <linux/atomic.h> |
49 | #include <asm/intel_scu_ipc.h> | 49 | #include <asm/intel_scu_ipc.h> |
50 | #include <asm/apb_timer.h> | 50 | #include <asm/apb_timer.h> |
51 | #include <asm/mrst.h> | 51 | #include <asm/intel-mid.h> |
52 | 52 | ||
53 | #include "intel_scu_watchdog.h" | 53 | #include "intel_scu_watchdog.h" |
54 | 54 | ||
55 | /* Bounds number of times we will retry loading time count */ | 55 | /* Bounds number of times we will retry loading time count */ |
56 | /* This retry is a work around for a silicon bug. */ | 56 | /* This retry is a work around for a silicon bug. */ |
57 | #define MAX_RETRY 16 | 57 | #define MAX_RETRY 16 |
58 | 58 | ||
59 | #define IPC_SET_WATCHDOG_TIMER 0xF8 | 59 | #define IPC_SET_WATCHDOG_TIMER 0xF8 |
60 | 60 | ||
61 | static int timer_margin = DEFAULT_SOFT_TO_HARD_MARGIN; | 61 | static int timer_margin = DEFAULT_SOFT_TO_HARD_MARGIN; |
62 | module_param(timer_margin, int, 0); | 62 | module_param(timer_margin, int, 0); |
63 | MODULE_PARM_DESC(timer_margin, | 63 | MODULE_PARM_DESC(timer_margin, |
64 | "Watchdog timer margin" | 64 | "Watchdog timer margin" |
65 | "Time between interrupt and resetting the system" | 65 | "Time between interrupt and resetting the system" |
66 | "The range is from 1 to 160" | 66 | "The range is from 1 to 160" |
67 | "This is the time for all keep alives to arrive"); | 67 | "This is the time for all keep alives to arrive"); |
68 | 68 | ||
69 | static int timer_set = DEFAULT_TIME; | 69 | static int timer_set = DEFAULT_TIME; |
70 | module_param(timer_set, int, 0); | 70 | module_param(timer_set, int, 0); |
71 | MODULE_PARM_DESC(timer_set, | 71 | MODULE_PARM_DESC(timer_set, |
72 | "Default Watchdog timer setting" | 72 | "Default Watchdog timer setting" |
73 | "Complete cycle time" | 73 | "Complete cycle time" |
74 | "The range is from 1 to 170" | 74 | "The range is from 1 to 170" |
75 | "This is the time for all keep alives to arrive"); | 75 | "This is the time for all keep alives to arrive"); |
76 | 76 | ||
77 | /* After watchdog device is closed, check force_boot. If: | 77 | /* After watchdog device is closed, check force_boot. If: |
78 | * force_boot == 0, then force boot on next watchdog interrupt after close, | 78 | * force_boot == 0, then force boot on next watchdog interrupt after close, |
79 | * force_boot == 1, then force boot immediately when device is closed. | 79 | * force_boot == 1, then force boot immediately when device is closed. |
80 | */ | 80 | */ |
81 | static int force_boot; | 81 | static int force_boot; |
82 | module_param(force_boot, int, 0); | 82 | module_param(force_boot, int, 0); |
83 | MODULE_PARM_DESC(force_boot, | 83 | MODULE_PARM_DESC(force_boot, |
84 | "A value of 1 means that the driver will reboot" | 84 | "A value of 1 means that the driver will reboot" |
85 | "the system immediately if the /dev/watchdog device is closed" | 85 | "the system immediately if the /dev/watchdog device is closed" |
86 | "A value of 0 means that when /dev/watchdog device is closed" | 86 | "A value of 0 means that when /dev/watchdog device is closed" |
87 | "the watchdog timer will be refreshed for one more interval" | 87 | "the watchdog timer will be refreshed for one more interval" |
88 | "of length: timer_set. At the end of this interval, the" | 88 | "of length: timer_set. At the end of this interval, the" |
89 | "watchdog timer will reset the system." | 89 | "watchdog timer will reset the system." |
90 | ); | 90 | ); |
91 | 91 | ||
92 | /* there is only one device in the system now; this can be made into | 92 | /* there is only one device in the system now; this can be made into |
93 | * an array in the future if we have more than one device */ | 93 | * an array in the future if we have more than one device */ |
94 | 94 | ||
95 | static struct intel_scu_watchdog_dev watchdog_device; | 95 | static struct intel_scu_watchdog_dev watchdog_device; |
96 | 96 | ||
97 | /* Forces restart, if force_reboot is set */ | 97 | /* Forces restart, if force_reboot is set */ |
98 | static void watchdog_fire(void) | 98 | static void watchdog_fire(void) |
99 | { | 99 | { |
100 | if (force_boot) { | 100 | if (force_boot) { |
101 | pr_crit("Initiating system reboot\n"); | 101 | pr_crit("Initiating system reboot\n"); |
102 | emergency_restart(); | 102 | emergency_restart(); |
103 | pr_crit("Reboot didn't ?????\n"); | 103 | pr_crit("Reboot didn't ?????\n"); |
104 | } | 104 | } |
105 | 105 | ||
106 | else { | 106 | else { |
107 | pr_crit("Immediate Reboot Disabled\n"); | 107 | pr_crit("Immediate Reboot Disabled\n"); |
108 | pr_crit("System will reset when watchdog timer times out!\n"); | 108 | pr_crit("System will reset when watchdog timer times out!\n"); |
109 | } | 109 | } |
110 | } | 110 | } |
111 | 111 | ||
112 | static int check_timer_margin(int new_margin) | 112 | static int check_timer_margin(int new_margin) |
113 | { | 113 | { |
114 | if ((new_margin < MIN_TIME_CYCLE) || | 114 | if ((new_margin < MIN_TIME_CYCLE) || |
115 | (new_margin > MAX_TIME - timer_set)) { | 115 | (new_margin > MAX_TIME - timer_set)) { |
116 | pr_debug("value of new_margin %d is out of the range %d to %d\n", | 116 | pr_debug("value of new_margin %d is out of the range %d to %d\n", |
117 | new_margin, MIN_TIME_CYCLE, MAX_TIME - timer_set); | 117 | new_margin, MIN_TIME_CYCLE, MAX_TIME - timer_set); |
118 | return -EINVAL; | 118 | return -EINVAL; |
119 | } | 119 | } |
120 | return 0; | 120 | return 0; |
121 | } | 121 | } |
122 | 122 | ||
123 | /* | 123 | /* |
124 | * IPC operations | 124 | * IPC operations |
125 | */ | 125 | */ |
126 | static int watchdog_set_ipc(int soft_threshold, int threshold) | 126 | static int watchdog_set_ipc(int soft_threshold, int threshold) |
127 | { | 127 | { |
128 | u32 *ipc_wbuf; | 128 | u32 *ipc_wbuf; |
129 | u8 cbuf[16] = { '\0' }; | 129 | u8 cbuf[16] = { '\0' }; |
130 | int ipc_ret = 0; | 130 | int ipc_ret = 0; |
131 | 131 | ||
132 | ipc_wbuf = (u32 *)&cbuf; | 132 | ipc_wbuf = (u32 *)&cbuf; |
133 | ipc_wbuf[0] = soft_threshold; | 133 | ipc_wbuf[0] = soft_threshold; |
134 | ipc_wbuf[1] = threshold; | 134 | ipc_wbuf[1] = threshold; |
135 | 135 | ||
136 | ipc_ret = intel_scu_ipc_command( | 136 | ipc_ret = intel_scu_ipc_command( |
137 | IPC_SET_WATCHDOG_TIMER, | 137 | IPC_SET_WATCHDOG_TIMER, |
138 | 0, | 138 | 0, |
139 | ipc_wbuf, | 139 | ipc_wbuf, |
140 | 2, | 140 | 2, |
141 | NULL, | 141 | NULL, |
142 | 0); | 142 | 0); |
143 | 143 | ||
144 | if (ipc_ret != 0) | 144 | if (ipc_ret != 0) |
145 | pr_err("Error setting SCU watchdog timer: %x\n", ipc_ret); | 145 | pr_err("Error setting SCU watchdog timer: %x\n", ipc_ret); |
146 | 146 | ||
147 | return ipc_ret; | 147 | return ipc_ret; |
148 | }; | 148 | }; |
149 | 149 | ||
150 | /* | 150 | /* |
151 | * Intel_SCU operations | 151 | * Intel_SCU operations |
152 | */ | 152 | */ |
153 | 153 | ||
154 | /* timer interrupt handler */ | 154 | /* timer interrupt handler */ |
155 | static irqreturn_t watchdog_timer_interrupt(int irq, void *dev_id) | 155 | static irqreturn_t watchdog_timer_interrupt(int irq, void *dev_id) |
156 | { | 156 | { |
157 | int int_status; | 157 | int int_status; |
158 | int_status = ioread32(watchdog_device.timer_interrupt_status_addr); | 158 | int_status = ioread32(watchdog_device.timer_interrupt_status_addr); |
159 | 159 | ||
160 | pr_debug("irq, int_status: %x\n", int_status); | 160 | pr_debug("irq, int_status: %x\n", int_status); |
161 | 161 | ||
162 | if (int_status != 0) | 162 | if (int_status != 0) |
163 | return IRQ_NONE; | 163 | return IRQ_NONE; |
164 | 164 | ||
165 | /* has the timer been started? If not, then this is spurious */ | 165 | /* has the timer been started? If not, then this is spurious */ |
166 | if (watchdog_device.timer_started == 0) { | 166 | if (watchdog_device.timer_started == 0) { |
167 | pr_debug("spurious interrupt received\n"); | 167 | pr_debug("spurious interrupt received\n"); |
168 | return IRQ_HANDLED; | 168 | return IRQ_HANDLED; |
169 | } | 169 | } |
170 | 170 | ||
171 | /* temporarily disable the timer */ | 171 | /* temporarily disable the timer */ |
172 | iowrite32(0x00000002, watchdog_device.timer_control_addr); | 172 | iowrite32(0x00000002, watchdog_device.timer_control_addr); |
173 | 173 | ||
174 | /* set the timer to the threshold */ | 174 | /* set the timer to the threshold */ |
175 | iowrite32(watchdog_device.threshold, | 175 | iowrite32(watchdog_device.threshold, |
176 | watchdog_device.timer_load_count_addr); | 176 | watchdog_device.timer_load_count_addr); |
177 | 177 | ||
178 | /* allow the timer to run */ | 178 | /* allow the timer to run */ |
179 | iowrite32(0x00000003, watchdog_device.timer_control_addr); | 179 | iowrite32(0x00000003, watchdog_device.timer_control_addr); |
180 | 180 | ||
181 | return IRQ_HANDLED; | 181 | return IRQ_HANDLED; |
182 | } | 182 | } |
183 | 183 | ||
184 | static int intel_scu_keepalive(void) | 184 | static int intel_scu_keepalive(void) |
185 | { | 185 | { |
186 | 186 | ||
187 | /* read eoi register - clears interrupt */ | 187 | /* read eoi register - clears interrupt */ |
188 | ioread32(watchdog_device.timer_clear_interrupt_addr); | 188 | ioread32(watchdog_device.timer_clear_interrupt_addr); |
189 | 189 | ||
190 | /* temporarily disable the timer */ | 190 | /* temporarily disable the timer */ |
191 | iowrite32(0x00000002, watchdog_device.timer_control_addr); | 191 | iowrite32(0x00000002, watchdog_device.timer_control_addr); |
192 | 192 | ||
193 | /* set the timer to the soft_threshold */ | 193 | /* set the timer to the soft_threshold */ |
194 | iowrite32(watchdog_device.soft_threshold, | 194 | iowrite32(watchdog_device.soft_threshold, |
195 | watchdog_device.timer_load_count_addr); | 195 | watchdog_device.timer_load_count_addr); |
196 | 196 | ||
197 | /* allow the timer to run */ | 197 | /* allow the timer to run */ |
198 | iowrite32(0x00000003, watchdog_device.timer_control_addr); | 198 | iowrite32(0x00000003, watchdog_device.timer_control_addr); |
199 | 199 | ||
200 | return 0; | 200 | return 0; |
201 | } | 201 | } |
202 | 202 | ||
203 | static int intel_scu_stop(void) | 203 | static int intel_scu_stop(void) |
204 | { | 204 | { |
205 | iowrite32(0, watchdog_device.timer_control_addr); | 205 | iowrite32(0, watchdog_device.timer_control_addr); |
206 | return 0; | 206 | return 0; |
207 | } | 207 | } |
208 | 208 | ||
209 | static int intel_scu_set_heartbeat(u32 t) | 209 | static int intel_scu_set_heartbeat(u32 t) |
210 | { | 210 | { |
211 | int ipc_ret; | 211 | int ipc_ret; |
212 | int retry_count; | 212 | int retry_count; |
213 | u32 soft_value; | 213 | u32 soft_value; |
214 | u32 hw_pre_value; | 214 | u32 hw_pre_value; |
215 | u32 hw_value; | 215 | u32 hw_value; |
216 | 216 | ||
217 | watchdog_device.timer_set = t; | 217 | watchdog_device.timer_set = t; |
218 | watchdog_device.threshold = | 218 | watchdog_device.threshold = |
219 | timer_margin * watchdog_device.timer_tbl_ptr->freq_hz; | 219 | timer_margin * watchdog_device.timer_tbl_ptr->freq_hz; |
220 | watchdog_device.soft_threshold = | 220 | watchdog_device.soft_threshold = |
221 | (watchdog_device.timer_set - timer_margin) | 221 | (watchdog_device.timer_set - timer_margin) |
222 | * watchdog_device.timer_tbl_ptr->freq_hz; | 222 | * watchdog_device.timer_tbl_ptr->freq_hz; |
223 | 223 | ||
224 | pr_debug("set_heartbeat: timer freq is %d\n", | 224 | pr_debug("set_heartbeat: timer freq is %d\n", |
225 | watchdog_device.timer_tbl_ptr->freq_hz); | 225 | watchdog_device.timer_tbl_ptr->freq_hz); |
226 | pr_debug("set_heartbeat: timer_set is %x (hex)\n", | 226 | pr_debug("set_heartbeat: timer_set is %x (hex)\n", |
227 | watchdog_device.timer_set); | 227 | watchdog_device.timer_set); |
228 | pr_debug("set_hearbeat: timer_margin is %x (hex)\n", timer_margin); | 228 | pr_debug("set_hearbeat: timer_margin is %x (hex)\n", timer_margin); |
229 | pr_debug("set_heartbeat: threshold is %x (hex)\n", | 229 | pr_debug("set_heartbeat: threshold is %x (hex)\n", |
230 | watchdog_device.threshold); | 230 | watchdog_device.threshold); |
231 | pr_debug("set_heartbeat: soft_threshold is %x (hex)\n", | 231 | pr_debug("set_heartbeat: soft_threshold is %x (hex)\n", |
232 | watchdog_device.soft_threshold); | 232 | watchdog_device.soft_threshold); |
233 | 233 | ||
234 | /* Adjust thresholds by FREQ_ADJUSTMENT factor, to make the */ | 234 | /* Adjust thresholds by FREQ_ADJUSTMENT factor, to make the */ |
235 | /* watchdog timing come out right. */ | 235 | /* watchdog timing come out right. */ |
236 | watchdog_device.threshold = | 236 | watchdog_device.threshold = |
237 | watchdog_device.threshold / FREQ_ADJUSTMENT; | 237 | watchdog_device.threshold / FREQ_ADJUSTMENT; |
238 | watchdog_device.soft_threshold = | 238 | watchdog_device.soft_threshold = |
239 | watchdog_device.soft_threshold / FREQ_ADJUSTMENT; | 239 | watchdog_device.soft_threshold / FREQ_ADJUSTMENT; |
240 | 240 | ||
241 | /* temporarily disable the timer */ | 241 | /* temporarily disable the timer */ |
242 | iowrite32(0x00000002, watchdog_device.timer_control_addr); | 242 | iowrite32(0x00000002, watchdog_device.timer_control_addr); |
243 | 243 | ||
244 | /* send the threshold and soft_threshold via IPC to the processor */ | 244 | /* send the threshold and soft_threshold via IPC to the processor */ |
245 | ipc_ret = watchdog_set_ipc(watchdog_device.soft_threshold, | 245 | ipc_ret = watchdog_set_ipc(watchdog_device.soft_threshold, |
246 | watchdog_device.threshold); | 246 | watchdog_device.threshold); |
247 | 247 | ||
248 | if (ipc_ret != 0) { | 248 | if (ipc_ret != 0) { |
249 | /* Make sure the watchdog timer is stopped */ | 249 | /* Make sure the watchdog timer is stopped */ |
250 | intel_scu_stop(); | 250 | intel_scu_stop(); |
251 | return ipc_ret; | 251 | return ipc_ret; |
252 | } | 252 | } |
253 | 253 | ||
254 | /* Soft Threshold set loop. Early versions of silicon did */ | 254 | /* Soft Threshold set loop. Early versions of silicon did */ |
255 | /* not always set this count correctly. This loop checks */ | 255 | /* not always set this count correctly. This loop checks */ |
256 | /* the value and retries if it was not set correctly. */ | 256 | /* the value and retries if it was not set correctly. */ |
257 | 257 | ||
258 | retry_count = 0; | 258 | retry_count = 0; |
259 | soft_value = watchdog_device.soft_threshold & 0xFFFF0000; | 259 | soft_value = watchdog_device.soft_threshold & 0xFFFF0000; |
260 | do { | 260 | do { |
261 | 261 | ||
262 | /* Make sure timer is stopped */ | 262 | /* Make sure timer is stopped */ |
263 | intel_scu_stop(); | 263 | intel_scu_stop(); |
264 | 264 | ||
265 | if (MAX_RETRY < retry_count++) { | 265 | if (MAX_RETRY < retry_count++) { |
266 | /* Unable to set timer value */ | 266 | /* Unable to set timer value */ |
267 | pr_err("Unable to set timer\n"); | 267 | pr_err("Unable to set timer\n"); |
268 | return -ENODEV; | 268 | return -ENODEV; |
269 | } | 269 | } |
270 | 270 | ||
271 | /* set the timer to the soft threshold */ | 271 | /* set the timer to the soft threshold */ |
272 | iowrite32(watchdog_device.soft_threshold, | 272 | iowrite32(watchdog_device.soft_threshold, |
273 | watchdog_device.timer_load_count_addr); | 273 | watchdog_device.timer_load_count_addr); |
274 | 274 | ||
275 | /* read count value before starting timer */ | 275 | /* read count value before starting timer */ |
276 | hw_pre_value = ioread32(watchdog_device.timer_load_count_addr); | 276 | hw_pre_value = ioread32(watchdog_device.timer_load_count_addr); |
277 | hw_pre_value = hw_pre_value & 0xFFFF0000; | 277 | hw_pre_value = hw_pre_value & 0xFFFF0000; |
278 | 278 | ||
279 | /* Start the timer */ | 279 | /* Start the timer */ |
280 | iowrite32(0x00000003, watchdog_device.timer_control_addr); | 280 | iowrite32(0x00000003, watchdog_device.timer_control_addr); |
281 | 281 | ||
282 | /* read the value the time loaded into its count reg */ | 282 | /* read the value the time loaded into its count reg */ |
283 | hw_value = ioread32(watchdog_device.timer_load_count_addr); | 283 | hw_value = ioread32(watchdog_device.timer_load_count_addr); |
284 | hw_value = hw_value & 0xFFFF0000; | 284 | hw_value = hw_value & 0xFFFF0000; |
285 | 285 | ||
286 | 286 | ||
287 | } while (soft_value != hw_value); | 287 | } while (soft_value != hw_value); |
288 | 288 | ||
289 | watchdog_device.timer_started = 1; | 289 | watchdog_device.timer_started = 1; |
290 | 290 | ||
291 | return 0; | 291 | return 0; |
292 | } | 292 | } |
293 | 293 | ||
294 | /* | 294 | /* |
295 | * /dev/watchdog handling | 295 | * /dev/watchdog handling |
296 | */ | 296 | */ |
297 | 297 | ||
298 | static int intel_scu_open(struct inode *inode, struct file *file) | 298 | static int intel_scu_open(struct inode *inode, struct file *file) |
299 | { | 299 | { |
300 | 300 | ||
301 | /* Set flag to indicate that watchdog device is open */ | 301 | /* Set flag to indicate that watchdog device is open */ |
302 | if (test_and_set_bit(0, &watchdog_device.driver_open)) | 302 | if (test_and_set_bit(0, &watchdog_device.driver_open)) |
303 | return -EBUSY; | 303 | return -EBUSY; |
304 | 304 | ||
305 | /* Check for reopen of driver. Reopens are not allowed */ | 305 | /* Check for reopen of driver. Reopens are not allowed */ |
306 | if (watchdog_device.driver_closed) | 306 | if (watchdog_device.driver_closed) |
307 | return -EPERM; | 307 | return -EPERM; |
308 | 308 | ||
309 | return nonseekable_open(inode, file); | 309 | return nonseekable_open(inode, file); |
310 | } | 310 | } |
311 | 311 | ||
312 | static int intel_scu_release(struct inode *inode, struct file *file) | 312 | static int intel_scu_release(struct inode *inode, struct file *file) |
313 | { | 313 | { |
314 | /* | 314 | /* |
315 | * This watchdog should not be closed, after the timer | 315 | * This watchdog should not be closed, after the timer |
316 | * is started with the WDIPC_SETTIMEOUT ioctl | 316 | * is started with the WDIPC_SETTIMEOUT ioctl |
317 | * If force_boot is set watchdog_fire() will cause an | 317 | * If force_boot is set watchdog_fire() will cause an |
318 | * immediate reset. If force_boot is not set, the watchdog | 318 | * immediate reset. If force_boot is not set, the watchdog |
319 | * timer is refreshed for one more interval. At the end | 319 | * timer is refreshed for one more interval. At the end |
320 | * of that interval, the watchdog timer will reset the system. | 320 | * of that interval, the watchdog timer will reset the system. |
321 | */ | 321 | */ |
322 | 322 | ||
323 | if (!test_and_clear_bit(0, &watchdog_device.driver_open)) { | 323 | if (!test_and_clear_bit(0, &watchdog_device.driver_open)) { |
324 | pr_debug("intel_scu_release, without open\n"); | 324 | pr_debug("intel_scu_release, without open\n"); |
325 | return -ENOTTY; | 325 | return -ENOTTY; |
326 | } | 326 | } |
327 | 327 | ||
328 | if (!watchdog_device.timer_started) { | 328 | if (!watchdog_device.timer_started) { |
329 | /* Just close, since timer has not been started */ | 329 | /* Just close, since timer has not been started */ |
330 | pr_debug("closed, without starting timer\n"); | 330 | pr_debug("closed, without starting timer\n"); |
331 | return 0; | 331 | return 0; |
332 | } | 332 | } |
333 | 333 | ||
334 | pr_crit("Unexpected close of /dev/watchdog!\n"); | 334 | pr_crit("Unexpected close of /dev/watchdog!\n"); |
335 | 335 | ||
336 | /* Since the timer was started, prevent future reopens */ | 336 | /* Since the timer was started, prevent future reopens */ |
337 | watchdog_device.driver_closed = 1; | 337 | watchdog_device.driver_closed = 1; |
338 | 338 | ||
339 | /* Refresh the timer for one more interval */ | 339 | /* Refresh the timer for one more interval */ |
340 | intel_scu_keepalive(); | 340 | intel_scu_keepalive(); |
341 | 341 | ||
342 | /* Reboot system (if force_boot is set) */ | 342 | /* Reboot system (if force_boot is set) */ |
343 | watchdog_fire(); | 343 | watchdog_fire(); |
344 | 344 | ||
345 | /* We should only reach this point if force_boot is not set */ | 345 | /* We should only reach this point if force_boot is not set */ |
346 | return 0; | 346 | return 0; |
347 | } | 347 | } |
348 | 348 | ||
349 | static ssize_t intel_scu_write(struct file *file, | 349 | static ssize_t intel_scu_write(struct file *file, |
350 | char const *data, | 350 | char const *data, |
351 | size_t len, | 351 | size_t len, |
352 | loff_t *ppos) | 352 | loff_t *ppos) |
353 | { | 353 | { |
354 | 354 | ||
355 | if (watchdog_device.timer_started) | 355 | if (watchdog_device.timer_started) |
356 | /* Watchdog already started, keep it alive */ | 356 | /* Watchdog already started, keep it alive */ |
357 | intel_scu_keepalive(); | 357 | intel_scu_keepalive(); |
358 | else | 358 | else |
359 | /* Start watchdog with timer value set by init */ | 359 | /* Start watchdog with timer value set by init */ |
360 | intel_scu_set_heartbeat(watchdog_device.timer_set); | 360 | intel_scu_set_heartbeat(watchdog_device.timer_set); |
361 | 361 | ||
362 | return len; | 362 | return len; |
363 | } | 363 | } |
364 | 364 | ||
365 | static long intel_scu_ioctl(struct file *file, | 365 | static long intel_scu_ioctl(struct file *file, |
366 | unsigned int cmd, | 366 | unsigned int cmd, |
367 | unsigned long arg) | 367 | unsigned long arg) |
368 | { | 368 | { |
369 | void __user *argp = (void __user *)arg; | 369 | void __user *argp = (void __user *)arg; |
370 | u32 __user *p = argp; | 370 | u32 __user *p = argp; |
371 | u32 new_margin; | 371 | u32 new_margin; |
372 | 372 | ||
373 | 373 | ||
374 | static const struct watchdog_info ident = { | 374 | static const struct watchdog_info ident = { |
375 | .options = WDIOF_SETTIMEOUT | 375 | .options = WDIOF_SETTIMEOUT |
376 | | WDIOF_KEEPALIVEPING, | 376 | | WDIOF_KEEPALIVEPING, |
377 | .firmware_version = 0, /* @todo Get from SCU via | 377 | .firmware_version = 0, /* @todo Get from SCU via |
378 | ipc_get_scu_fw_version()? */ | 378 | ipc_get_scu_fw_version()? */ |
379 | .identity = "Intel_SCU IOH Watchdog" /* len < 32 */ | 379 | .identity = "Intel_SCU IOH Watchdog" /* len < 32 */ |
380 | }; | 380 | }; |
381 | 381 | ||
382 | switch (cmd) { | 382 | switch (cmd) { |
383 | case WDIOC_GETSUPPORT: | 383 | case WDIOC_GETSUPPORT: |
384 | return copy_to_user(argp, | 384 | return copy_to_user(argp, |
385 | &ident, | 385 | &ident, |
386 | sizeof(ident)) ? -EFAULT : 0; | 386 | sizeof(ident)) ? -EFAULT : 0; |
387 | case WDIOC_GETSTATUS: | 387 | case WDIOC_GETSTATUS: |
388 | case WDIOC_GETBOOTSTATUS: | 388 | case WDIOC_GETBOOTSTATUS: |
389 | return put_user(0, p); | 389 | return put_user(0, p); |
390 | case WDIOC_KEEPALIVE: | 390 | case WDIOC_KEEPALIVE: |
391 | intel_scu_keepalive(); | 391 | intel_scu_keepalive(); |
392 | 392 | ||
393 | return 0; | 393 | return 0; |
394 | case WDIOC_SETTIMEOUT: | 394 | case WDIOC_SETTIMEOUT: |
395 | if (get_user(new_margin, p)) | 395 | if (get_user(new_margin, p)) |
396 | return -EFAULT; | 396 | return -EFAULT; |
397 | 397 | ||
398 | if (check_timer_margin(new_margin)) | 398 | if (check_timer_margin(new_margin)) |
399 | return -EINVAL; | 399 | return -EINVAL; |
400 | 400 | ||
401 | if (intel_scu_set_heartbeat(new_margin)) | 401 | if (intel_scu_set_heartbeat(new_margin)) |
402 | return -EINVAL; | 402 | return -EINVAL; |
403 | return 0; | 403 | return 0; |
404 | case WDIOC_GETTIMEOUT: | 404 | case WDIOC_GETTIMEOUT: |
405 | return put_user(watchdog_device.soft_threshold, p); | 405 | return put_user(watchdog_device.soft_threshold, p); |
406 | 406 | ||
407 | default: | 407 | default: |
408 | return -ENOTTY; | 408 | return -ENOTTY; |
409 | } | 409 | } |
410 | } | 410 | } |
411 | 411 | ||
412 | /* | 412 | /* |
413 | * Notifier for system down | 413 | * Notifier for system down |
414 | */ | 414 | */ |
415 | static int intel_scu_notify_sys(struct notifier_block *this, | 415 | static int intel_scu_notify_sys(struct notifier_block *this, |
416 | unsigned long code, | 416 | unsigned long code, |
417 | void *another_unused) | 417 | void *another_unused) |
418 | { | 418 | { |
419 | if (code == SYS_DOWN || code == SYS_HALT) | 419 | if (code == SYS_DOWN || code == SYS_HALT) |
420 | /* Turn off the watchdog timer. */ | 420 | /* Turn off the watchdog timer. */ |
421 | intel_scu_stop(); | 421 | intel_scu_stop(); |
422 | return NOTIFY_DONE; | 422 | return NOTIFY_DONE; |
423 | } | 423 | } |
424 | 424 | ||
425 | /* | 425 | /* |
426 | * Kernel Interfaces | 426 | * Kernel Interfaces |
427 | */ | 427 | */ |
428 | static const struct file_operations intel_scu_fops = { | 428 | static const struct file_operations intel_scu_fops = { |
429 | .owner = THIS_MODULE, | 429 | .owner = THIS_MODULE, |
430 | .llseek = no_llseek, | 430 | .llseek = no_llseek, |
431 | .write = intel_scu_write, | 431 | .write = intel_scu_write, |
432 | .unlocked_ioctl = intel_scu_ioctl, | 432 | .unlocked_ioctl = intel_scu_ioctl, |
433 | .open = intel_scu_open, | 433 | .open = intel_scu_open, |
434 | .release = intel_scu_release, | 434 | .release = intel_scu_release, |
435 | }; | 435 | }; |
436 | 436 | ||
437 | static int __init intel_scu_watchdog_init(void) | 437 | static int __init intel_scu_watchdog_init(void) |
438 | { | 438 | { |
439 | int ret; | 439 | int ret; |
440 | u32 __iomem *tmp_addr; | 440 | u32 __iomem *tmp_addr; |
441 | 441 | ||
442 | /* | 442 | /* |
443 | * We don't really need to check this as the SFI timer get will fail | 443 | * We don't really need to check this as the SFI timer get will fail |
444 | * but if we do so we can exit with a clearer reason and no noise. | 444 | * but if we do so we can exit with a clearer reason and no noise. |
445 | * | 445 | * |
446 | * If it isn't an intel MID device then it doesn't have this watchdog | 446 | * If it isn't an intel MID device then it doesn't have this watchdog |
447 | */ | 447 | */ |
448 | if (!mrst_identify_cpu()) | 448 | if (!mrst_identify_cpu()) |
449 | return -ENODEV; | 449 | return -ENODEV; |
450 | 450 | ||
451 | /* Check boot parameters to verify that their initial values */ | 451 | /* Check boot parameters to verify that their initial values */ |
452 | /* are in range. */ | 452 | /* are in range. */ |
453 | /* Check value of timer_set boot parameter */ | 453 | /* Check value of timer_set boot parameter */ |
454 | if ((timer_set < MIN_TIME_CYCLE) || | 454 | if ((timer_set < MIN_TIME_CYCLE) || |
455 | (timer_set > MAX_TIME - MIN_TIME_CYCLE)) { | 455 | (timer_set > MAX_TIME - MIN_TIME_CYCLE)) { |
456 | pr_err("value of timer_set %x (hex) is out of range from %x to %x (hex)\n", | 456 | pr_err("value of timer_set %x (hex) is out of range from %x to %x (hex)\n", |
457 | timer_set, MIN_TIME_CYCLE, MAX_TIME - MIN_TIME_CYCLE); | 457 | timer_set, MIN_TIME_CYCLE, MAX_TIME - MIN_TIME_CYCLE); |
458 | return -EINVAL; | 458 | return -EINVAL; |
459 | } | 459 | } |
460 | 460 | ||
461 | /* Check value of timer_margin boot parameter */ | 461 | /* Check value of timer_margin boot parameter */ |
462 | if (check_timer_margin(timer_margin)) | 462 | if (check_timer_margin(timer_margin)) |
463 | return -EINVAL; | 463 | return -EINVAL; |
464 | 464 | ||
465 | watchdog_device.timer_tbl_ptr = sfi_get_mtmr(sfi_mtimer_num-1); | 465 | watchdog_device.timer_tbl_ptr = sfi_get_mtmr(sfi_mtimer_num-1); |
466 | 466 | ||
467 | if (watchdog_device.timer_tbl_ptr == NULL) { | 467 | if (watchdog_device.timer_tbl_ptr == NULL) { |
468 | pr_debug("timer is not available\n"); | 468 | pr_debug("timer is not available\n"); |
469 | return -ENODEV; | 469 | return -ENODEV; |
470 | } | 470 | } |
471 | /* make sure the timer exists */ | 471 | /* make sure the timer exists */ |
472 | if (watchdog_device.timer_tbl_ptr->phys_addr == 0) { | 472 | if (watchdog_device.timer_tbl_ptr->phys_addr == 0) { |
473 | pr_debug("timer %d does not have valid physical memory\n", | 473 | pr_debug("timer %d does not have valid physical memory\n", |
474 | sfi_mtimer_num); | 474 | sfi_mtimer_num); |
475 | return -ENODEV; | 475 | return -ENODEV; |
476 | } | 476 | } |
477 | 477 | ||
478 | if (watchdog_device.timer_tbl_ptr->irq == 0) { | 478 | if (watchdog_device.timer_tbl_ptr->irq == 0) { |
479 | pr_debug("timer %d invalid irq\n", sfi_mtimer_num); | 479 | pr_debug("timer %d invalid irq\n", sfi_mtimer_num); |
480 | return -ENODEV; | 480 | return -ENODEV; |
481 | } | 481 | } |
482 | 482 | ||
483 | tmp_addr = ioremap_nocache(watchdog_device.timer_tbl_ptr->phys_addr, | 483 | tmp_addr = ioremap_nocache(watchdog_device.timer_tbl_ptr->phys_addr, |
484 | 20); | 484 | 20); |
485 | 485 | ||
486 | if (tmp_addr == NULL) { | 486 | if (tmp_addr == NULL) { |
487 | pr_debug("timer unable to ioremap\n"); | 487 | pr_debug("timer unable to ioremap\n"); |
488 | return -ENOMEM; | 488 | return -ENOMEM; |
489 | } | 489 | } |
490 | 490 | ||
491 | watchdog_device.timer_load_count_addr = tmp_addr++; | 491 | watchdog_device.timer_load_count_addr = tmp_addr++; |
492 | watchdog_device.timer_current_value_addr = tmp_addr++; | 492 | watchdog_device.timer_current_value_addr = tmp_addr++; |
493 | watchdog_device.timer_control_addr = tmp_addr++; | 493 | watchdog_device.timer_control_addr = tmp_addr++; |
494 | watchdog_device.timer_clear_interrupt_addr = tmp_addr++; | 494 | watchdog_device.timer_clear_interrupt_addr = tmp_addr++; |
495 | watchdog_device.timer_interrupt_status_addr = tmp_addr++; | 495 | watchdog_device.timer_interrupt_status_addr = tmp_addr++; |
496 | 496 | ||
497 | /* Set the default time values in device structure */ | 497 | /* Set the default time values in device structure */ |
498 | 498 | ||
499 | watchdog_device.timer_set = timer_set; | 499 | watchdog_device.timer_set = timer_set; |
500 | watchdog_device.threshold = | 500 | watchdog_device.threshold = |
501 | timer_margin * watchdog_device.timer_tbl_ptr->freq_hz; | 501 | timer_margin * watchdog_device.timer_tbl_ptr->freq_hz; |
502 | watchdog_device.soft_threshold = | 502 | watchdog_device.soft_threshold = |
503 | (watchdog_device.timer_set - timer_margin) | 503 | (watchdog_device.timer_set - timer_margin) |
504 | * watchdog_device.timer_tbl_ptr->freq_hz; | 504 | * watchdog_device.timer_tbl_ptr->freq_hz; |
505 | 505 | ||
506 | 506 | ||
507 | watchdog_device.intel_scu_notifier.notifier_call = | 507 | watchdog_device.intel_scu_notifier.notifier_call = |
508 | intel_scu_notify_sys; | 508 | intel_scu_notify_sys; |
509 | 509 | ||
510 | ret = register_reboot_notifier(&watchdog_device.intel_scu_notifier); | 510 | ret = register_reboot_notifier(&watchdog_device.intel_scu_notifier); |
511 | if (ret) { | 511 | if (ret) { |
512 | pr_err("cannot register notifier %d)\n", ret); | 512 | pr_err("cannot register notifier %d)\n", ret); |
513 | goto register_reboot_error; | 513 | goto register_reboot_error; |
514 | } | 514 | } |
515 | 515 | ||
516 | watchdog_device.miscdev.minor = WATCHDOG_MINOR; | 516 | watchdog_device.miscdev.minor = WATCHDOG_MINOR; |
517 | watchdog_device.miscdev.name = "watchdog"; | 517 | watchdog_device.miscdev.name = "watchdog"; |
518 | watchdog_device.miscdev.fops = &intel_scu_fops; | 518 | watchdog_device.miscdev.fops = &intel_scu_fops; |
519 | 519 | ||
520 | ret = misc_register(&watchdog_device.miscdev); | 520 | ret = misc_register(&watchdog_device.miscdev); |
521 | if (ret) { | 521 | if (ret) { |
522 | pr_err("cannot register miscdev %d err =%d\n", | 522 | pr_err("cannot register miscdev %d err =%d\n", |
523 | WATCHDOG_MINOR, ret); | 523 | WATCHDOG_MINOR, ret); |
524 | goto misc_register_error; | 524 | goto misc_register_error; |
525 | } | 525 | } |
526 | 526 | ||
527 | ret = request_irq((unsigned int)watchdog_device.timer_tbl_ptr->irq, | 527 | ret = request_irq((unsigned int)watchdog_device.timer_tbl_ptr->irq, |
528 | watchdog_timer_interrupt, | 528 | watchdog_timer_interrupt, |
529 | IRQF_SHARED, "watchdog", | 529 | IRQF_SHARED, "watchdog", |
530 | &watchdog_device.timer_load_count_addr); | 530 | &watchdog_device.timer_load_count_addr); |
531 | if (ret) { | 531 | if (ret) { |
532 | pr_err("error requesting irq %d\n", ret); | 532 | pr_err("error requesting irq %d\n", ret); |
533 | goto request_irq_error; | 533 | goto request_irq_error; |
534 | } | 534 | } |
535 | /* Make sure timer is disabled before returning */ | 535 | /* Make sure timer is disabled before returning */ |
536 | intel_scu_stop(); | 536 | intel_scu_stop(); |
537 | return 0; | 537 | return 0; |
538 | 538 | ||
539 | /* error cleanup */ | 539 | /* error cleanup */ |
540 | 540 | ||
541 | request_irq_error: | 541 | request_irq_error: |
542 | misc_deregister(&watchdog_device.miscdev); | 542 | misc_deregister(&watchdog_device.miscdev); |
543 | misc_register_error: | 543 | misc_register_error: |
544 | unregister_reboot_notifier(&watchdog_device.intel_scu_notifier); | 544 | unregister_reboot_notifier(&watchdog_device.intel_scu_notifier); |
545 | register_reboot_error: | 545 | register_reboot_error: |
546 | intel_scu_stop(); | 546 | intel_scu_stop(); |
547 | iounmap(watchdog_device.timer_load_count_addr); | 547 | iounmap(watchdog_device.timer_load_count_addr); |
548 | return ret; | 548 | return ret; |
549 | } | 549 | } |
550 | 550 | ||
551 | static void __exit intel_scu_watchdog_exit(void) | 551 | static void __exit intel_scu_watchdog_exit(void) |
552 | { | 552 | { |
553 | 553 | ||
554 | misc_deregister(&watchdog_device.miscdev); | 554 | misc_deregister(&watchdog_device.miscdev); |
555 | unregister_reboot_notifier(&watchdog_device.intel_scu_notifier); | 555 | unregister_reboot_notifier(&watchdog_device.intel_scu_notifier); |
556 | /* disable the timer */ | 556 | /* disable the timer */ |
557 | iowrite32(0x00000002, watchdog_device.timer_control_addr); | 557 | iowrite32(0x00000002, watchdog_device.timer_control_addr); |
558 | iounmap(watchdog_device.timer_load_count_addr); | 558 | iounmap(watchdog_device.timer_load_count_addr); |
559 | } | 559 | } |
560 | 560 | ||
561 | late_initcall(intel_scu_watchdog_init); | 561 | late_initcall(intel_scu_watchdog_init); |
562 | module_exit(intel_scu_watchdog_exit); | 562 | module_exit(intel_scu_watchdog_exit); |
563 | 563 | ||
564 | MODULE_AUTHOR("Intel Corporation"); | 564 | MODULE_AUTHOR("Intel Corporation"); |
565 | MODULE_DESCRIPTION("Intel SCU Watchdog Device Driver"); | 565 | MODULE_DESCRIPTION("Intel SCU Watchdog Device Driver"); |
566 | MODULE_LICENSE("GPL"); | 566 | MODULE_LICENSE("GPL"); |
567 | MODULE_ALIAS_MISCDEV(WATCHDOG_MINOR); | 567 | MODULE_ALIAS_MISCDEV(WATCHDOG_MINOR); |
568 | MODULE_VERSION(WDT_VER); | 568 | MODULE_VERSION(WDT_VER); |
569 | 569 |