diff options
author | Arnd Bergmann <arnd@arndb.de> | 2012-03-04 16:00:27 -0500 |
---|---|---|
committer | Arnd Bergmann <arnd@arndb.de> | 2012-03-04 16:03:30 -0500 |
commit | 7cb7f82611dddb4b471d42d0fad645dd0dc3f360 (patch) | |
tree | 61cb56ea1a885c96e6cb78fcd12e6abddd77e27c /arch/arm/mach-tegra | |
parent | 7169ff4a0942adf524f25713eaed30599d438926 (diff) | |
parent | e77a6b313fdfe4faa8f9a8edf919c7eb8d095fb5 (diff) |
Merge tag 'tegra-soc-drivers' of git://git.kernel.org/pub/scm/linux/kernel/git/olof/tegra into tegra/soc-drivers
Tegra SoC driver support.
Some device tree conversions, some new drivers. and a fix for an issue
introduced in Grant Likely's irq_domain conversion in his tree. Because
of that, this branch depends on his branch to build (but not to merge):
git://git.secretlab.ca/git/linux-2.6.git irqdomain/next
* tag 'tegra-soc-drivers' of git://git.kernel.org/pub/scm/linux/kernel/git/olof/tegra: (34 commits)
ARM: tegra: uncompress.h: Don't depend on kernel headers
gpio: tegra: Fix build issue due to irq_domain rework.
ARM: tegra: Remove duplicate PMU interrupt inversion code
ARM: tegra: Add a simple PMC driver
ARM: tegra: dma: not required to move requestor when stopping.
ARM: tegra: Fix EMC pdata initialization from registers
gpio: tegra: Parameterize the number of banks
gpio: tegra: Dynamically allocate IRQ base, and support DT
ARM: tegra: Remove use of TEGRA_GPIO_TO_IRQ
ARM: tegra: Pass uncompress.h UART selection to DEBUG_LL
ARM: tegra: uncompress.h: Choose a UART at runtime
ARM: tegra: uncompress.h: Store UART address in a variable
ARM: tegra: Introduce define DEBUG_UART_SHIFT
ARM: tegra: Support Tegra30 in decompressor UART setup
ARM: tegra: Pause DMA when reading transfer count
ARM: tegra: emc: device tree support
ARM: tegra: emc: convert tegra2_emc to a platform driver
ARM: tegra: fuse: add bct strapping reading
ARM: tegra: fuse: add functions to access chip revision
ARM: tegra: fuse: use apbio dma for register access
...
Signed-off-by: Arnd Bergmann <arnd@arndb.de>
Diffstat (limited to 'arch/arm/mach-tegra')
-rw-r--r-- | arch/arm/mach-tegra/Makefile | 3 | ||||
-rw-r--r-- | arch/arm/mach-tegra/apbio.c | 145 | ||||
-rw-r--r-- | arch/arm/mach-tegra/apbio.h | 39 | ||||
-rw-r--r-- | arch/arm/mach-tegra/board-harmony-power.c | 15 | ||||
-rw-r--r-- | arch/arm/mach-tegra/board-harmony.c | 2 | ||||
-rw-r--r-- | arch/arm/mach-tegra/board-seaboard.c | 5 | ||||
-rw-r--r-- | arch/arm/mach-tegra/common.c | 20 | ||||
-rw-r--r-- | arch/arm/mach-tegra/dma.c | 128 | ||||
-rw-r--r-- | arch/arm/mach-tegra/fuse.c | 107 | ||||
-rw-r--r-- | arch/arm/mach-tegra/fuse.h | 34 | ||||
-rw-r--r-- | arch/arm/mach-tegra/include/mach/debug-macro.S | 88 | ||||
-rw-r--r-- | arch/arm/mach-tegra/include/mach/gpio-tegra.h | 2 | ||||
-rw-r--r-- | arch/arm/mach-tegra/include/mach/irammap.h | 35 | ||||
-rw-r--r-- | arch/arm/mach-tegra/include/mach/uncompress.h | 120 | ||||
-rw-r--r-- | arch/arm/mach-tegra/pmc.c | 76 | ||||
-rw-r--r-- | arch/arm/mach-tegra/pmc.h | 23 | ||||
-rw-r--r-- | arch/arm/mach-tegra/tegra2_clocks.c | 2 | ||||
-rw-r--r-- | arch/arm/mach-tegra/tegra2_emc.c | 224 | ||||
-rw-r--r-- | arch/arm/mach-tegra/tegra2_emc.h | 11 |
19 files changed, 916 insertions, 163 deletions
diff --git a/arch/arm/mach-tegra/Makefile b/arch/arm/mach-tegra/Makefile index e120ff54f663..e0b7a4d32599 100644 --- a/arch/arm/mach-tegra/Makefile +++ b/arch/arm/mach-tegra/Makefile | |||
@@ -7,6 +7,7 @@ obj-y += clock.o | |||
7 | obj-y += timer.o | 7 | obj-y += timer.o |
8 | obj-y += pinmux.o | 8 | obj-y += pinmux.o |
9 | obj-y += fuse.o | 9 | obj-y += fuse.o |
10 | obj-y += pmc.o | ||
10 | obj-$(CONFIG_ARCH_TEGRA_2x_SOC) += powergate.o | 11 | obj-$(CONFIG_ARCH_TEGRA_2x_SOC) += powergate.o |
11 | obj-$(CONFIG_ARCH_TEGRA_2x_SOC) += tegra2_clocks.o | 12 | obj-$(CONFIG_ARCH_TEGRA_2x_SOC) += tegra2_clocks.o |
12 | obj-$(CONFIG_ARCH_TEGRA_2x_SOC) += tegra2_emc.o | 13 | obj-$(CONFIG_ARCH_TEGRA_2x_SOC) += tegra2_emc.o |
@@ -15,7 +16,7 @@ obj-$(CONFIG_ARCH_TEGRA_3x_SOC) += pinmux-tegra30-tables.o | |||
15 | obj-$(CONFIG_ARCH_TEGRA_3x_SOC) += board-dt-tegra30.o | 16 | obj-$(CONFIG_ARCH_TEGRA_3x_SOC) += board-dt-tegra30.o |
16 | obj-$(CONFIG_SMP) += platsmp.o localtimer.o headsmp.o | 17 | obj-$(CONFIG_SMP) += platsmp.o localtimer.o headsmp.o |
17 | obj-$(CONFIG_HOTPLUG_CPU) += hotplug.o | 18 | obj-$(CONFIG_HOTPLUG_CPU) += hotplug.o |
18 | obj-$(CONFIG_TEGRA_SYSTEM_DMA) += dma.o | 19 | obj-$(CONFIG_TEGRA_SYSTEM_DMA) += dma.o apbio.o |
19 | obj-$(CONFIG_CPU_FREQ) += cpu-tegra.o | 20 | obj-$(CONFIG_CPU_FREQ) += cpu-tegra.o |
20 | obj-$(CONFIG_TEGRA_PCI) += pcie.o | 21 | obj-$(CONFIG_TEGRA_PCI) += pcie.o |
21 | obj-$(CONFIG_USB_SUPPORT) += usb_phy.o | 22 | obj-$(CONFIG_USB_SUPPORT) += usb_phy.o |
diff --git a/arch/arm/mach-tegra/apbio.c b/arch/arm/mach-tegra/apbio.c new file mode 100644 index 000000000000..e75451e517bd --- /dev/null +++ b/arch/arm/mach-tegra/apbio.c | |||
@@ -0,0 +1,145 @@ | |||
1 | /* | ||
2 | * Copyright (C) 2010 NVIDIA Corporation. | ||
3 | * Copyright (C) 2010 Google, Inc. | ||
4 | * | ||
5 | * This software is licensed under the terms of the GNU General Public | ||
6 | * License version 2, as published by the Free Software Foundation, and | ||
7 | * may be copied, distributed, and modified under those terms. | ||
8 | * | ||
9 | * This program is distributed in the hope that it will be useful, | ||
10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
12 | * GNU General Public License for more details. | ||
13 | * | ||
14 | */ | ||
15 | |||
16 | #include <linux/kernel.h> | ||
17 | #include <linux/io.h> | ||
18 | #include <linux/dma-mapping.h> | ||
19 | #include <linux/spinlock.h> | ||
20 | #include <linux/completion.h> | ||
21 | #include <linux/sched.h> | ||
22 | #include <linux/mutex.h> | ||
23 | |||
24 | #include <mach/dma.h> | ||
25 | #include <mach/iomap.h> | ||
26 | |||
27 | #include "apbio.h" | ||
28 | |||
29 | static DEFINE_MUTEX(tegra_apb_dma_lock); | ||
30 | |||
31 | static struct tegra_dma_channel *tegra_apb_dma; | ||
32 | static u32 *tegra_apb_bb; | ||
33 | static dma_addr_t tegra_apb_bb_phys; | ||
34 | static DECLARE_COMPLETION(tegra_apb_wait); | ||
35 | |||
36 | bool tegra_apb_init(void) | ||
37 | { | ||
38 | struct tegra_dma_channel *ch; | ||
39 | |||
40 | mutex_lock(&tegra_apb_dma_lock); | ||
41 | |||
42 | /* Check to see if we raced to setup */ | ||
43 | if (tegra_apb_dma) | ||
44 | goto out; | ||
45 | |||
46 | ch = tegra_dma_allocate_channel(TEGRA_DMA_MODE_ONESHOT | | ||
47 | TEGRA_DMA_SHARED); | ||
48 | |||
49 | if (!ch) | ||
50 | goto out_fail; | ||
51 | |||
52 | tegra_apb_bb = dma_alloc_coherent(NULL, sizeof(u32), | ||
53 | &tegra_apb_bb_phys, GFP_KERNEL); | ||
54 | if (!tegra_apb_bb) { | ||
55 | pr_err("%s: can not allocate bounce buffer\n", __func__); | ||
56 | tegra_dma_free_channel(ch); | ||
57 | goto out_fail; | ||
58 | } | ||
59 | |||
60 | tegra_apb_dma = ch; | ||
61 | out: | ||
62 | mutex_unlock(&tegra_apb_dma_lock); | ||
63 | return true; | ||
64 | |||
65 | out_fail: | ||
66 | mutex_unlock(&tegra_apb_dma_lock); | ||
67 | return false; | ||
68 | } | ||
69 | |||
70 | static void apb_dma_complete(struct tegra_dma_req *req) | ||
71 | { | ||
72 | complete(&tegra_apb_wait); | ||
73 | } | ||
74 | |||
75 | u32 tegra_apb_readl(unsigned long offset) | ||
76 | { | ||
77 | struct tegra_dma_req req; | ||
78 | int ret; | ||
79 | |||
80 | if (!tegra_apb_dma && !tegra_apb_init()) | ||
81 | return readl(IO_TO_VIRT(offset)); | ||
82 | |||
83 | mutex_lock(&tegra_apb_dma_lock); | ||
84 | req.complete = apb_dma_complete; | ||
85 | req.to_memory = 1; | ||
86 | req.dest_addr = tegra_apb_bb_phys; | ||
87 | req.dest_bus_width = 32; | ||
88 | req.dest_wrap = 1; | ||
89 | req.source_addr = offset; | ||
90 | req.source_bus_width = 32; | ||
91 | req.source_wrap = 4; | ||
92 | req.req_sel = TEGRA_DMA_REQ_SEL_CNTR; | ||
93 | req.size = 4; | ||
94 | |||
95 | INIT_COMPLETION(tegra_apb_wait); | ||
96 | |||
97 | tegra_dma_enqueue_req(tegra_apb_dma, &req); | ||
98 | |||
99 | ret = wait_for_completion_timeout(&tegra_apb_wait, | ||
100 | msecs_to_jiffies(50)); | ||
101 | |||
102 | if (WARN(ret == 0, "apb read dma timed out")) { | ||
103 | tegra_dma_dequeue_req(tegra_apb_dma, &req); | ||
104 | *(u32 *)tegra_apb_bb = 0; | ||
105 | } | ||
106 | |||
107 | mutex_unlock(&tegra_apb_dma_lock); | ||
108 | return *((u32 *)tegra_apb_bb); | ||
109 | } | ||
110 | |||
111 | void tegra_apb_writel(u32 value, unsigned long offset) | ||
112 | { | ||
113 | struct tegra_dma_req req; | ||
114 | int ret; | ||
115 | |||
116 | if (!tegra_apb_dma && !tegra_apb_init()) { | ||
117 | writel(value, IO_TO_VIRT(offset)); | ||
118 | return; | ||
119 | } | ||
120 | |||
121 | mutex_lock(&tegra_apb_dma_lock); | ||
122 | *((u32 *)tegra_apb_bb) = value; | ||
123 | req.complete = apb_dma_complete; | ||
124 | req.to_memory = 0; | ||
125 | req.dest_addr = offset; | ||
126 | req.dest_wrap = 4; | ||
127 | req.dest_bus_width = 32; | ||
128 | req.source_addr = tegra_apb_bb_phys; | ||
129 | req.source_bus_width = 32; | ||
130 | req.source_wrap = 1; | ||
131 | req.req_sel = TEGRA_DMA_REQ_SEL_CNTR; | ||
132 | req.size = 4; | ||
133 | |||
134 | INIT_COMPLETION(tegra_apb_wait); | ||
135 | |||
136 | tegra_dma_enqueue_req(tegra_apb_dma, &req); | ||
137 | |||
138 | ret = wait_for_completion_timeout(&tegra_apb_wait, | ||
139 | msecs_to_jiffies(50)); | ||
140 | |||
141 | if (WARN(ret == 0, "apb write dma timed out")) | ||
142 | tegra_dma_dequeue_req(tegra_apb_dma, &req); | ||
143 | |||
144 | mutex_unlock(&tegra_apb_dma_lock); | ||
145 | } | ||
diff --git a/arch/arm/mach-tegra/apbio.h b/arch/arm/mach-tegra/apbio.h new file mode 100644 index 000000000000..8b49e8c89a64 --- /dev/null +++ b/arch/arm/mach-tegra/apbio.h | |||
@@ -0,0 +1,39 @@ | |||
1 | /* | ||
2 | * Copyright (C) 2010 NVIDIA Corporation. | ||
3 | * Copyright (C) 2010 Google, Inc. | ||
4 | * | ||
5 | * This software is licensed under the terms of the GNU General Public | ||
6 | * License version 2, as published by the Free Software Foundation, and | ||
7 | * may be copied, distributed, and modified under those terms. | ||
8 | * | ||
9 | * This program is distributed in the hope that it will be useful, | ||
10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
12 | * GNU General Public License for more details. | ||
13 | * | ||
14 | */ | ||
15 | |||
16 | #ifndef __MACH_TEGRA_APBIO_H | ||
17 | #define __MACH_TEGRA_APBIO_H | ||
18 | |||
19 | #ifdef CONFIG_TEGRA_SYSTEM_DMA | ||
20 | |||
21 | u32 tegra_apb_readl(unsigned long offset); | ||
22 | void tegra_apb_writel(u32 value, unsigned long offset); | ||
23 | |||
24 | #else | ||
25 | #include <asm/io.h> | ||
26 | #include <mach/io.h> | ||
27 | |||
28 | static inline u32 tegra_apb_readl(unsigned long offset) | ||
29 | { | ||
30 | return readl(IO_TO_VIRT(offset)); | ||
31 | } | ||
32 | |||
33 | static inline void tegra_apb_writel(u32 value, unsigned long offset) | ||
34 | { | ||
35 | writel(value, IO_TO_VIRT(offset)); | ||
36 | } | ||
37 | #endif | ||
38 | |||
39 | #endif | ||
diff --git a/arch/arm/mach-tegra/board-harmony-power.c b/arch/arm/mach-tegra/board-harmony-power.c index 21d1285731b3..976edfb05912 100644 --- a/arch/arm/mach-tegra/board-harmony-power.c +++ b/arch/arm/mach-tegra/board-harmony-power.c | |||
@@ -18,18 +18,13 @@ | |||
18 | #include <linux/i2c.h> | 18 | #include <linux/i2c.h> |
19 | #include <linux/platform_device.h> | 19 | #include <linux/platform_device.h> |
20 | #include <linux/gpio.h> | 20 | #include <linux/gpio.h> |
21 | #include <linux/io.h> | ||
22 | #include <linux/regulator/machine.h> | 21 | #include <linux/regulator/machine.h> |
23 | #include <linux/mfd/tps6586x.h> | 22 | #include <linux/mfd/tps6586x.h> |
24 | 23 | ||
25 | #include <mach/iomap.h> | ||
26 | #include <mach/irqs.h> | 24 | #include <mach/irqs.h> |
27 | 25 | ||
28 | #include "board-harmony.h" | 26 | #include "board-harmony.h" |
29 | 27 | ||
30 | #define PMC_CTRL 0x0 | ||
31 | #define PMC_CTRL_INTR_LOW (1 << 17) | ||
32 | |||
33 | static struct regulator_consumer_supply tps658621_ldo0_supply[] = { | 28 | static struct regulator_consumer_supply tps658621_ldo0_supply[] = { |
34 | REGULATOR_SUPPLY("pex_clk", NULL), | 29 | REGULATOR_SUPPLY("pex_clk", NULL), |
35 | }; | 30 | }; |
@@ -114,16 +109,6 @@ static struct i2c_board_info __initdata harmony_regulators[] = { | |||
114 | 109 | ||
115 | int __init harmony_regulator_init(void) | 110 | int __init harmony_regulator_init(void) |
116 | { | 111 | { |
117 | void __iomem *pmc = IO_ADDRESS(TEGRA_PMC_BASE); | ||
118 | u32 pmc_ctrl; | ||
119 | |||
120 | /* | ||
121 | * Configure the power management controller to trigger PMU | ||
122 | * interrupts when low | ||
123 | */ | ||
124 | pmc_ctrl = readl(pmc + PMC_CTRL); | ||
125 | writel(pmc_ctrl | PMC_CTRL_INTR_LOW, pmc + PMC_CTRL); | ||
126 | |||
127 | i2c_register_board_info(3, harmony_regulators, 1); | 112 | i2c_register_board_info(3, harmony_regulators, 1); |
128 | 113 | ||
129 | return 0; | 114 | return 0; |
diff --git a/arch/arm/mach-tegra/board-harmony.c b/arch/arm/mach-tegra/board-harmony.c index 789bdc9e8f91..c00aadb01e09 100644 --- a/arch/arm/mach-tegra/board-harmony.c +++ b/arch/arm/mach-tegra/board-harmony.c | |||
@@ -101,7 +101,6 @@ static struct wm8903_platform_data harmony_wm8903_pdata = { | |||
101 | static struct i2c_board_info __initdata wm8903_board_info = { | 101 | static struct i2c_board_info __initdata wm8903_board_info = { |
102 | I2C_BOARD_INFO("wm8903", 0x1a), | 102 | I2C_BOARD_INFO("wm8903", 0x1a), |
103 | .platform_data = &harmony_wm8903_pdata, | 103 | .platform_data = &harmony_wm8903_pdata, |
104 | .irq = TEGRA_GPIO_TO_IRQ(TEGRA_GPIO_CDC_IRQ), | ||
105 | }; | 104 | }; |
106 | 105 | ||
107 | static void __init harmony_i2c_init(void) | 106 | static void __init harmony_i2c_init(void) |
@@ -111,6 +110,7 @@ static void __init harmony_i2c_init(void) | |||
111 | platform_device_register(&tegra_i2c_device3); | 110 | platform_device_register(&tegra_i2c_device3); |
112 | platform_device_register(&tegra_i2c_device4); | 111 | platform_device_register(&tegra_i2c_device4); |
113 | 112 | ||
113 | wm8903_board_info.irq = gpio_to_irq(TEGRA_GPIO_CDC_IRQ); | ||
114 | i2c_register_board_info(0, &wm8903_board_info, 1); | 114 | i2c_register_board_info(0, &wm8903_board_info, 1); |
115 | } | 115 | } |
116 | 116 | ||
diff --git a/arch/arm/mach-tegra/board-seaboard.c b/arch/arm/mach-tegra/board-seaboard.c index ebac65f52510..d669847f0485 100644 --- a/arch/arm/mach-tegra/board-seaboard.c +++ b/arch/arm/mach-tegra/board-seaboard.c | |||
@@ -159,7 +159,6 @@ static struct platform_device *seaboard_devices[] __initdata = { | |||
159 | 159 | ||
160 | static struct i2c_board_info __initdata isl29018_device = { | 160 | static struct i2c_board_info __initdata isl29018_device = { |
161 | I2C_BOARD_INFO("isl29018", 0x44), | 161 | I2C_BOARD_INFO("isl29018", 0x44), |
162 | .irq = TEGRA_GPIO_TO_IRQ(TEGRA_GPIO_ISL29018_IRQ), | ||
163 | }; | 162 | }; |
164 | 163 | ||
165 | static struct i2c_board_info __initdata adt7461_device = { | 164 | static struct i2c_board_info __initdata adt7461_device = { |
@@ -183,7 +182,6 @@ static struct wm8903_platform_data wm8903_pdata = { | |||
183 | static struct i2c_board_info __initdata wm8903_device = { | 182 | static struct i2c_board_info __initdata wm8903_device = { |
184 | I2C_BOARD_INFO("wm8903", 0x1a), | 183 | I2C_BOARD_INFO("wm8903", 0x1a), |
185 | .platform_data = &wm8903_pdata, | 184 | .platform_data = &wm8903_pdata, |
186 | .irq = TEGRA_GPIO_TO_IRQ(TEGRA_GPIO_CDC_IRQ), | ||
187 | }; | 185 | }; |
188 | 186 | ||
189 | static int seaboard_ehci_init(void) | 187 | static int seaboard_ehci_init(void) |
@@ -214,7 +212,10 @@ static void __init seaboard_i2c_init(void) | |||
214 | gpio_request(TEGRA_GPIO_ISL29018_IRQ, "isl29018"); | 212 | gpio_request(TEGRA_GPIO_ISL29018_IRQ, "isl29018"); |
215 | gpio_direction_input(TEGRA_GPIO_ISL29018_IRQ); | 213 | gpio_direction_input(TEGRA_GPIO_ISL29018_IRQ); |
216 | 214 | ||
215 | isl29018_device.irq = gpio_to_irq(TEGRA_GPIO_ISL29018_IRQ); | ||
217 | i2c_register_board_info(0, &isl29018_device, 1); | 216 | i2c_register_board_info(0, &isl29018_device, 1); |
217 | |||
218 | wm8903_device.irq = gpio_to_irq(TEGRA_GPIO_CDC_IRQ); | ||
218 | i2c_register_board_info(0, &wm8903_device, 1); | 219 | i2c_register_board_info(0, &wm8903_device, 1); |
219 | 220 | ||
220 | i2c_register_board_info(3, &adt7461_device, 1); | 221 | i2c_register_board_info(3, &adt7461_device, 1); |
diff --git a/arch/arm/mach-tegra/common.c b/arch/arm/mach-tegra/common.c index 2db20da1d585..47ad750209ae 100644 --- a/arch/arm/mach-tegra/common.c +++ b/arch/arm/mach-tegra/common.c | |||
@@ -31,6 +31,24 @@ | |||
31 | #include "board.h" | 31 | #include "board.h" |
32 | #include "clock.h" | 32 | #include "clock.h" |
33 | #include "fuse.h" | 33 | #include "fuse.h" |
34 | #include "pmc.h" | ||
35 | |||
36 | /* | ||
37 | * Storage for debug-macro.S's state. | ||
38 | * | ||
39 | * This must be in .data not .bss so that it gets initialized each time the | ||
40 | * kernel is loaded. The data is declared here rather than debug-macro.S so | ||
41 | * that multiple inclusions of debug-macro.S point at the same data. | ||
42 | */ | ||
43 | #define TEGRA_DEBUG_UART_OFFSET (TEGRA_DEBUG_UART_BASE & 0xFFFF) | ||
44 | u32 tegra_uart_config[3] = { | ||
45 | /* Debug UART initialization required */ | ||
46 | 1, | ||
47 | /* Debug UART physical address */ | ||
48 | (u32)(IO_APB_PHYS + TEGRA_DEBUG_UART_OFFSET), | ||
49 | /* Debug UART virtual address */ | ||
50 | (u32)(IO_APB_VIRT + TEGRA_DEBUG_UART_OFFSET), | ||
51 | }; | ||
34 | 52 | ||
35 | #ifdef CONFIG_OF | 53 | #ifdef CONFIG_OF |
36 | static const struct of_device_id tegra_dt_irq_match[] __initconst = { | 54 | static const struct of_device_id tegra_dt_irq_match[] __initconst = { |
@@ -101,11 +119,13 @@ void __init tegra20_init_early(void) | |||
101 | tegra2_init_clocks(); | 119 | tegra2_init_clocks(); |
102 | tegra_clk_init_from_table(tegra20_clk_init_table); | 120 | tegra_clk_init_from_table(tegra20_clk_init_table); |
103 | tegra_init_cache(0x331, 0x441); | 121 | tegra_init_cache(0x331, 0x441); |
122 | tegra_pmc_init(); | ||
104 | } | 123 | } |
105 | #endif | 124 | #endif |
106 | #ifdef CONFIG_ARCH_TEGRA_3x_SOC | 125 | #ifdef CONFIG_ARCH_TEGRA_3x_SOC |
107 | void __init tegra30_init_early(void) | 126 | void __init tegra30_init_early(void) |
108 | { | 127 | { |
109 | tegra_init_cache(0x441, 0x551); | 128 | tegra_init_cache(0x441, 0x551); |
129 | tegra_pmc_init(); | ||
110 | } | 130 | } |
111 | #endif | 131 | #endif |
diff --git a/arch/arm/mach-tegra/dma.c b/arch/arm/mach-tegra/dma.c index c0cf967e47d3..abea4f6e2dd5 100644 --- a/arch/arm/mach-tegra/dma.c +++ b/arch/arm/mach-tegra/dma.c | |||
@@ -33,6 +33,8 @@ | |||
33 | #include <mach/iomap.h> | 33 | #include <mach/iomap.h> |
34 | #include <mach/suspend.h> | 34 | #include <mach/suspend.h> |
35 | 35 | ||
36 | #include "apbio.h" | ||
37 | |||
36 | #define APB_DMA_GEN 0x000 | 38 | #define APB_DMA_GEN 0x000 |
37 | #define GEN_ENABLE (1<<31) | 39 | #define GEN_ENABLE (1<<31) |
38 | 40 | ||
@@ -50,8 +52,6 @@ | |||
50 | #define CSR_ONCE (1<<27) | 52 | #define CSR_ONCE (1<<27) |
51 | #define CSR_FLOW (1<<21) | 53 | #define CSR_FLOW (1<<21) |
52 | #define CSR_REQ_SEL_SHIFT 16 | 54 | #define CSR_REQ_SEL_SHIFT 16 |
53 | #define CSR_REQ_SEL_MASK (0x1F<<CSR_REQ_SEL_SHIFT) | ||
54 | #define CSR_REQ_SEL_INVALID (31<<CSR_REQ_SEL_SHIFT) | ||
55 | #define CSR_WCOUNT_SHIFT 2 | 55 | #define CSR_WCOUNT_SHIFT 2 |
56 | #define CSR_WCOUNT_MASK 0xFFFC | 56 | #define CSR_WCOUNT_MASK 0xFFFC |
57 | 57 | ||
@@ -133,6 +133,7 @@ struct tegra_dma_channel { | |||
133 | 133 | ||
134 | static bool tegra_dma_initialized; | 134 | static bool tegra_dma_initialized; |
135 | static DEFINE_MUTEX(tegra_dma_lock); | 135 | static DEFINE_MUTEX(tegra_dma_lock); |
136 | static DEFINE_SPINLOCK(enable_lock); | ||
136 | 137 | ||
137 | static DECLARE_BITMAP(channel_usage, NV_DMA_MAX_CHANNELS); | 138 | static DECLARE_BITMAP(channel_usage, NV_DMA_MAX_CHANNELS); |
138 | static struct tegra_dma_channel dma_channels[NV_DMA_MAX_CHANNELS]; | 139 | static struct tegra_dma_channel dma_channels[NV_DMA_MAX_CHANNELS]; |
@@ -180,36 +181,94 @@ static void tegra_dma_stop(struct tegra_dma_channel *ch) | |||
180 | 181 | ||
181 | static int tegra_dma_cancel(struct tegra_dma_channel *ch) | 182 | static int tegra_dma_cancel(struct tegra_dma_channel *ch) |
182 | { | 183 | { |
183 | u32 csr; | ||
184 | unsigned long irq_flags; | 184 | unsigned long irq_flags; |
185 | 185 | ||
186 | spin_lock_irqsave(&ch->lock, irq_flags); | 186 | spin_lock_irqsave(&ch->lock, irq_flags); |
187 | while (!list_empty(&ch->list)) | 187 | while (!list_empty(&ch->list)) |
188 | list_del(ch->list.next); | 188 | list_del(ch->list.next); |
189 | 189 | ||
190 | csr = readl(ch->addr + APB_DMA_CHAN_CSR); | ||
191 | csr &= ~CSR_REQ_SEL_MASK; | ||
192 | csr |= CSR_REQ_SEL_INVALID; | ||
193 | writel(csr, ch->addr + APB_DMA_CHAN_CSR); | ||
194 | |||
195 | tegra_dma_stop(ch); | 190 | tegra_dma_stop(ch); |
196 | 191 | ||
197 | spin_unlock_irqrestore(&ch->lock, irq_flags); | 192 | spin_unlock_irqrestore(&ch->lock, irq_flags); |
198 | return 0; | 193 | return 0; |
199 | } | 194 | } |
200 | 195 | ||
196 | static unsigned int get_channel_status(struct tegra_dma_channel *ch, | ||
197 | struct tegra_dma_req *req, bool is_stop_dma) | ||
198 | { | ||
199 | void __iomem *addr = IO_ADDRESS(TEGRA_APB_DMA_BASE); | ||
200 | unsigned int status; | ||
201 | |||
202 | if (is_stop_dma) { | ||
203 | /* | ||
204 | * STOP the DMA and get the transfer count. | ||
205 | * Getting the transfer count is tricky. | ||
206 | * - Globally disable DMA on all channels | ||
207 | * - Read the channel's status register to know the number | ||
208 | * of pending bytes to be transfered. | ||
209 | * - Stop the dma channel | ||
210 | * - Globally re-enable DMA to resume other transfers | ||
211 | */ | ||
212 | spin_lock(&enable_lock); | ||
213 | writel(0, addr + APB_DMA_GEN); | ||
214 | udelay(20); | ||
215 | status = readl(ch->addr + APB_DMA_CHAN_STA); | ||
216 | tegra_dma_stop(ch); | ||
217 | writel(GEN_ENABLE, addr + APB_DMA_GEN); | ||
218 | spin_unlock(&enable_lock); | ||
219 | if (status & STA_ISE_EOC) { | ||
220 | pr_err("Got Dma Int here clearing"); | ||
221 | writel(status, ch->addr + APB_DMA_CHAN_STA); | ||
222 | } | ||
223 | req->status = TEGRA_DMA_REQ_ERROR_ABORTED; | ||
224 | } else { | ||
225 | status = readl(ch->addr + APB_DMA_CHAN_STA); | ||
226 | } | ||
227 | return status; | ||
228 | } | ||
229 | |||
230 | /* should be called with the channel lock held */ | ||
231 | static unsigned int dma_active_count(struct tegra_dma_channel *ch, | ||
232 | struct tegra_dma_req *req, unsigned int status) | ||
233 | { | ||
234 | unsigned int to_transfer; | ||
235 | unsigned int req_transfer_count; | ||
236 | unsigned int bytes_transferred; | ||
237 | |||
238 | to_transfer = ((status & STA_COUNT_MASK) >> STA_COUNT_SHIFT) + 1; | ||
239 | req_transfer_count = ch->req_transfer_count + 1; | ||
240 | bytes_transferred = req_transfer_count; | ||
241 | if (status & STA_BUSY) | ||
242 | bytes_transferred -= to_transfer; | ||
243 | /* | ||
244 | * In continuous transfer mode, DMA only tracks the count of the | ||
245 | * half DMA buffer. So, if the DMA already finished half the DMA | ||
246 | * then add the half buffer to the completed count. | ||
247 | */ | ||
248 | if (ch->mode & TEGRA_DMA_MODE_CONTINOUS) { | ||
249 | if (req->buffer_status == TEGRA_DMA_REQ_BUF_STATUS_HALF_FULL) | ||
250 | bytes_transferred += req_transfer_count; | ||
251 | if (status & STA_ISE_EOC) | ||
252 | bytes_transferred += req_transfer_count; | ||
253 | } | ||
254 | bytes_transferred *= 4; | ||
255 | return bytes_transferred; | ||
256 | } | ||
257 | |||
201 | int tegra_dma_dequeue_req(struct tegra_dma_channel *ch, | 258 | int tegra_dma_dequeue_req(struct tegra_dma_channel *ch, |
202 | struct tegra_dma_req *_req) | 259 | struct tegra_dma_req *_req) |
203 | { | 260 | { |
204 | unsigned int csr; | ||
205 | unsigned int status; | 261 | unsigned int status; |
206 | struct tegra_dma_req *req = NULL; | 262 | struct tegra_dma_req *req = NULL; |
207 | int found = 0; | 263 | int found = 0; |
208 | unsigned long irq_flags; | 264 | unsigned long irq_flags; |
209 | int to_transfer; | 265 | int stop = 0; |
210 | int req_transfer_count; | ||
211 | 266 | ||
212 | spin_lock_irqsave(&ch->lock, irq_flags); | 267 | spin_lock_irqsave(&ch->lock, irq_flags); |
268 | |||
269 | if (list_entry(ch->list.next, struct tegra_dma_req, node) == _req) | ||
270 | stop = 1; | ||
271 | |||
213 | list_for_each_entry(req, &ch->list, node) { | 272 | list_for_each_entry(req, &ch->list, node) { |
214 | if (req == _req) { | 273 | if (req == _req) { |
215 | list_del(&req->node); | 274 | list_del(&req->node); |
@@ -222,47 +281,12 @@ int tegra_dma_dequeue_req(struct tegra_dma_channel *ch, | |||
222 | return 0; | 281 | return 0; |
223 | } | 282 | } |
224 | 283 | ||
225 | /* STOP the DMA and get the transfer count. | 284 | if (!stop) |
226 | * Getting the transfer count is tricky. | 285 | goto skip_stop_dma; |
227 | * - Change the source selector to invalid to stop the DMA from | ||
228 | * FIFO to memory. | ||
229 | * - Read the status register to know the number of pending | ||
230 | * bytes to be transferred. | ||
231 | * - Finally stop or program the DMA to the next buffer in the | ||
232 | * list. | ||
233 | */ | ||
234 | csr = readl(ch->addr + APB_DMA_CHAN_CSR); | ||
235 | csr &= ~CSR_REQ_SEL_MASK; | ||
236 | csr |= CSR_REQ_SEL_INVALID; | ||
237 | writel(csr, ch->addr + APB_DMA_CHAN_CSR); | ||
238 | |||
239 | /* Get the transfer count */ | ||
240 | status = readl(ch->addr + APB_DMA_CHAN_STA); | ||
241 | to_transfer = (status & STA_COUNT_MASK) >> STA_COUNT_SHIFT; | ||
242 | req_transfer_count = ch->req_transfer_count; | ||
243 | req_transfer_count += 1; | ||
244 | to_transfer += 1; | ||
245 | |||
246 | req->bytes_transferred = req_transfer_count; | ||
247 | |||
248 | if (status & STA_BUSY) | ||
249 | req->bytes_transferred -= to_transfer; | ||
250 | |||
251 | /* In continuous transfer mode, DMA only tracks the count of the | ||
252 | * half DMA buffer. So, if the DMA already finished half the DMA | ||
253 | * then add the half buffer to the completed count. | ||
254 | * | ||
255 | * FIXME: There can be a race here. What if the req to | ||
256 | * dequue happens at the same time as the DMA just moved to | ||
257 | * the new buffer and SW didn't yet received the interrupt? | ||
258 | */ | ||
259 | if (ch->mode & TEGRA_DMA_MODE_CONTINOUS) | ||
260 | if (req->buffer_status == TEGRA_DMA_REQ_BUF_STATUS_HALF_FULL) | ||
261 | req->bytes_transferred += req_transfer_count; | ||
262 | 286 | ||
263 | req->bytes_transferred *= 4; | 287 | status = get_channel_status(ch, req, true); |
288 | req->bytes_transferred = dma_active_count(ch, req, status); | ||
264 | 289 | ||
265 | tegra_dma_stop(ch); | ||
266 | if (!list_empty(&ch->list)) { | 290 | if (!list_empty(&ch->list)) { |
267 | /* if the list is not empty, queue the next request */ | 291 | /* if the list is not empty, queue the next request */ |
268 | struct tegra_dma_req *next_req; | 292 | struct tegra_dma_req *next_req; |
@@ -270,6 +294,8 @@ int tegra_dma_dequeue_req(struct tegra_dma_channel *ch, | |||
270 | typeof(*next_req), node); | 294 | typeof(*next_req), node); |
271 | tegra_dma_update_hw(ch, next_req); | 295 | tegra_dma_update_hw(ch, next_req); |
272 | } | 296 | } |
297 | |||
298 | skip_stop_dma: | ||
273 | req->status = -TEGRA_DMA_REQ_ERROR_ABORTED; | 299 | req->status = -TEGRA_DMA_REQ_ERROR_ABORTED; |
274 | 300 | ||
275 | spin_unlock_irqrestore(&ch->lock, irq_flags); | 301 | spin_unlock_irqrestore(&ch->lock, irq_flags); |
@@ -357,7 +383,7 @@ struct tegra_dma_channel *tegra_dma_allocate_channel(int mode) | |||
357 | int channel; | 383 | int channel; |
358 | struct tegra_dma_channel *ch = NULL; | 384 | struct tegra_dma_channel *ch = NULL; |
359 | 385 | ||
360 | if (WARN_ON(!tegra_dma_initialized)) | 386 | if (!tegra_dma_initialized) |
361 | return NULL; | 387 | return NULL; |
362 | 388 | ||
363 | mutex_lock(&tegra_dma_lock); | 389 | mutex_lock(&tegra_dma_lock); |
diff --git a/arch/arm/mach-tegra/fuse.c b/arch/arm/mach-tegra/fuse.c index 1fa26d9a1a68..17fdd4086e6f 100644 --- a/arch/arm/mach-tegra/fuse.c +++ b/arch/arm/mach-tegra/fuse.c | |||
@@ -23,20 +23,70 @@ | |||
23 | #include <mach/iomap.h> | 23 | #include <mach/iomap.h> |
24 | 24 | ||
25 | #include "fuse.h" | 25 | #include "fuse.h" |
26 | #include "apbio.h" | ||
26 | 27 | ||
27 | #define FUSE_UID_LOW 0x108 | 28 | #define FUSE_UID_LOW 0x108 |
28 | #define FUSE_UID_HIGH 0x10c | 29 | #define FUSE_UID_HIGH 0x10c |
29 | #define FUSE_SKU_INFO 0x110 | 30 | #define FUSE_SKU_INFO 0x110 |
30 | #define FUSE_SPARE_BIT 0x200 | 31 | #define FUSE_SPARE_BIT 0x200 |
31 | 32 | ||
32 | static inline u32 fuse_readl(unsigned long offset) | 33 | int tegra_sku_id; |
34 | int tegra_cpu_process_id; | ||
35 | int tegra_core_process_id; | ||
36 | enum tegra_revision tegra_revision; | ||
37 | |||
38 | /* The BCT to use at boot is specified by board straps that can be read | ||
39 | * through a APB misc register and decoded. 2 bits, i.e. 4 possible BCTs. | ||
40 | */ | ||
41 | int tegra_bct_strapping; | ||
42 | |||
43 | #define STRAP_OPT 0x008 | ||
44 | #define GMI_AD0 (1 << 4) | ||
45 | #define GMI_AD1 (1 << 5) | ||
46 | #define RAM_ID_MASK (GMI_AD0 | GMI_AD1) | ||
47 | #define RAM_CODE_SHIFT 4 | ||
48 | |||
49 | static const char *tegra_revision_name[TEGRA_REVISION_MAX] = { | ||
50 | [TEGRA_REVISION_UNKNOWN] = "unknown", | ||
51 | [TEGRA_REVISION_A01] = "A01", | ||
52 | [TEGRA_REVISION_A02] = "A02", | ||
53 | [TEGRA_REVISION_A03] = "A03", | ||
54 | [TEGRA_REVISION_A03p] = "A03 prime", | ||
55 | [TEGRA_REVISION_A04] = "A04", | ||
56 | }; | ||
57 | |||
58 | static inline u32 tegra_fuse_readl(unsigned long offset) | ||
59 | { | ||
60 | return tegra_apb_readl(TEGRA_FUSE_BASE + offset); | ||
61 | } | ||
62 | |||
63 | static inline bool get_spare_fuse(int bit) | ||
33 | { | 64 | { |
34 | return readl(IO_TO_VIRT(TEGRA_FUSE_BASE + offset)); | 65 | return tegra_fuse_readl(FUSE_SPARE_BIT + bit * 4); |
35 | } | 66 | } |
36 | 67 | ||
37 | static inline void fuse_writel(u32 value, unsigned long offset) | 68 | static enum tegra_revision tegra_get_revision(void) |
38 | { | 69 | { |
39 | writel(value, IO_TO_VIRT(TEGRA_FUSE_BASE + offset)); | 70 | void __iomem *chip_id = IO_ADDRESS(TEGRA_APB_MISC_BASE) + 0x804; |
71 | u32 id = readl(chip_id); | ||
72 | u32 minor_rev = (id >> 16) & 0xf; | ||
73 | u32 chipid = (id >> 8) & 0xff; | ||
74 | |||
75 | switch (minor_rev) { | ||
76 | case 1: | ||
77 | return TEGRA_REVISION_A01; | ||
78 | case 2: | ||
79 | return TEGRA_REVISION_A02; | ||
80 | case 3: | ||
81 | if (chipid == 0x20 && (get_spare_fuse(18) || get_spare_fuse(19))) | ||
82 | return TEGRA_REVISION_A03p; | ||
83 | else | ||
84 | return TEGRA_REVISION_A03; | ||
85 | case 4: | ||
86 | return TEGRA_REVISION_A04; | ||
87 | default: | ||
88 | return TEGRA_REVISION_UNKNOWN; | ||
89 | } | ||
40 | } | 90 | } |
41 | 91 | ||
42 | void tegra_init_fuse(void) | 92 | void tegra_init_fuse(void) |
@@ -45,40 +95,31 @@ void tegra_init_fuse(void) | |||
45 | reg |= 1 << 28; | 95 | reg |= 1 << 28; |
46 | writel(reg, IO_TO_VIRT(TEGRA_CLK_RESET_BASE + 0x48)); | 96 | writel(reg, IO_TO_VIRT(TEGRA_CLK_RESET_BASE + 0x48)); |
47 | 97 | ||
48 | pr_info("Tegra SKU: %d CPU Process: %d Core Process: %d\n", | 98 | reg = tegra_fuse_readl(FUSE_SKU_INFO); |
49 | tegra_sku_id(), tegra_cpu_process_id(), | 99 | tegra_sku_id = reg & 0xFF; |
50 | tegra_core_process_id()); | ||
51 | } | ||
52 | 100 | ||
53 | unsigned long long tegra_chip_uid(void) | 101 | reg = tegra_fuse_readl(FUSE_SPARE_BIT); |
54 | { | 102 | tegra_cpu_process_id = (reg >> 6) & 3; |
55 | unsigned long long lo, hi; | ||
56 | 103 | ||
57 | lo = fuse_readl(FUSE_UID_LOW); | 104 | reg = tegra_fuse_readl(FUSE_SPARE_BIT); |
58 | hi = fuse_readl(FUSE_UID_HIGH); | 105 | tegra_core_process_id = (reg >> 12) & 3; |
59 | return (hi << 32ull) | lo; | ||
60 | } | ||
61 | 106 | ||
62 | int tegra_sku_id(void) | 107 | reg = tegra_apb_readl(TEGRA_APB_MISC_BASE + STRAP_OPT); |
63 | { | 108 | tegra_bct_strapping = (reg & RAM_ID_MASK) >> RAM_CODE_SHIFT; |
64 | int sku_id; | ||
65 | u32 reg = fuse_readl(FUSE_SKU_INFO); | ||
66 | sku_id = reg & 0xFF; | ||
67 | return sku_id; | ||
68 | } | ||
69 | 109 | ||
70 | int tegra_cpu_process_id(void) | 110 | tegra_revision = tegra_get_revision(); |
71 | { | 111 | |
72 | int cpu_process_id; | 112 | pr_info("Tegra Revision: %s SKU: %d CPU Process: %d Core Process: %d\n", |
73 | u32 reg = fuse_readl(FUSE_SPARE_BIT); | 113 | tegra_revision_name[tegra_get_revision()], |
74 | cpu_process_id = (reg >> 6) & 3; | 114 | tegra_sku_id, tegra_cpu_process_id, |
75 | return cpu_process_id; | 115 | tegra_core_process_id); |
76 | } | 116 | } |
77 | 117 | ||
78 | int tegra_core_process_id(void) | 118 | unsigned long long tegra_chip_uid(void) |
79 | { | 119 | { |
80 | int core_process_id; | 120 | unsigned long long lo, hi; |
81 | u32 reg = fuse_readl(FUSE_SPARE_BIT); | 121 | |
82 | core_process_id = (reg >> 12) & 3; | 122 | lo = tegra_fuse_readl(FUSE_UID_LOW); |
83 | return core_process_id; | 123 | hi = tegra_fuse_readl(FUSE_UID_HIGH); |
124 | return (hi << 32ull) | lo; | ||
84 | } | 125 | } |
diff --git a/arch/arm/mach-tegra/fuse.h b/arch/arm/mach-tegra/fuse.h index 584b2e27dbda..d65d2abf803b 100644 --- a/arch/arm/mach-tegra/fuse.h +++ b/arch/arm/mach-tegra/fuse.h | |||
@@ -1,6 +1,4 @@ | |||
1 | /* | 1 | /* |
2 | * arch/arm/mach-tegra/fuse.c | ||
3 | * | ||
4 | * Copyright (C) 2010 Google, Inc. | 2 | * Copyright (C) 2010 Google, Inc. |
5 | * | 3 | * |
6 | * Author: | 4 | * Author: |
@@ -17,8 +15,34 @@ | |||
17 | * | 15 | * |
18 | */ | 16 | */ |
19 | 17 | ||
18 | #ifndef __MACH_TEGRA_FUSE_H | ||
19 | #define __MACH_TEGRA_FUSE_H | ||
20 | |||
21 | enum tegra_revision { | ||
22 | TEGRA_REVISION_UNKNOWN = 0, | ||
23 | TEGRA_REVISION_A01, | ||
24 | TEGRA_REVISION_A02, | ||
25 | TEGRA_REVISION_A03, | ||
26 | TEGRA_REVISION_A03p, | ||
27 | TEGRA_REVISION_A04, | ||
28 | TEGRA_REVISION_MAX, | ||
29 | }; | ||
30 | |||
31 | #define SKU_ID_T20 8 | ||
32 | #define SKU_ID_T25SE 20 | ||
33 | #define SKU_ID_AP25 23 | ||
34 | #define SKU_ID_T25 24 | ||
35 | #define SKU_ID_AP25E 27 | ||
36 | #define SKU_ID_T25E 28 | ||
37 | |||
38 | extern int tegra_sku_id; | ||
39 | extern int tegra_cpu_process_id; | ||
40 | extern int tegra_core_process_id; | ||
41 | extern enum tegra_revision tegra_revision; | ||
42 | |||
43 | extern int tegra_bct_strapping; | ||
44 | |||
20 | unsigned long long tegra_chip_uid(void); | 45 | unsigned long long tegra_chip_uid(void); |
21 | int tegra_sku_id(void); | ||
22 | int tegra_cpu_process_id(void); | ||
23 | int tegra_core_process_id(void); | ||
24 | void tegra_init_fuse(void); | 46 | void tegra_init_fuse(void); |
47 | |||
48 | #endif | ||
diff --git a/arch/arm/mach-tegra/include/mach/debug-macro.S b/arch/arm/mach-tegra/include/mach/debug-macro.S index 619abc63aee8..90069abd37bd 100644 --- a/arch/arm/mach-tegra/include/mach/debug-macro.S +++ b/arch/arm/mach-tegra/include/mach/debug-macro.S | |||
@@ -1,11 +1,17 @@ | |||
1 | /* | 1 | /* |
2 | * arch/arm/mach-tegra/include/mach/debug-macro.S | 2 | * arch/arm/mach-tegra/include/mach/debug-macro.S |
3 | * | 3 | * |
4 | * Copyright (C) 2010 Google, Inc. | 4 | * Copyright (C) 2010,2011 Google, Inc. |
5 | * Copyright (C) 2011-2012 NVIDIA CORPORATION. All Rights Reserved. | ||
5 | * | 6 | * |
6 | * Author: | 7 | * Author: |
7 | * Colin Cross <ccross@google.com> | 8 | * Colin Cross <ccross@google.com> |
8 | * Erik Gilling <konkers@google.com> | 9 | * Erik Gilling <konkers@google.com> |
10 | * Doug Anderson <dianders@chromium.org> | ||
11 | * Stephen Warren <swarren@nvidia.com> | ||
12 | * | ||
13 | * Portions based on mach-omap2's debug-macro.S | ||
14 | * Copyright (C) 1994-1999 Russell King | ||
9 | * | 15 | * |
10 | * This software is licensed under the terms of the GNU General Public | 16 | * This software is licensed under the terms of the GNU General Public |
11 | * License version 2, as published by the Free Software Foundation, and | 17 | * License version 2, as published by the Free Software Foundation, and |
@@ -18,18 +24,78 @@ | |||
18 | * | 24 | * |
19 | */ | 25 | */ |
20 | 26 | ||
27 | #include <linux/serial_reg.h> | ||
28 | |||
21 | #include <mach/io.h> | 29 | #include <mach/io.h> |
22 | #include <mach/iomap.h> | 30 | #include <mach/iomap.h> |
31 | #include <mach/irammap.h> | ||
32 | |||
33 | .macro addruart, rp, rv, tmp | ||
34 | adr \rp, 99f @ actual addr of 99f | ||
35 | ldr \rv, [\rp] @ linked addr is stored there | ||
36 | sub \rv, \rv, \rp @ offset between the two | ||
37 | ldr \rp, [\rp, #4] @ linked tegra_uart_config | ||
38 | sub \tmp, \rp, \rv @ actual tegra_uart_config | ||
39 | ldr \rp, [\tmp] @ Load tegra_uart_config | ||
40 | cmp \rp, #1 @ needs intitialization? | ||
41 | bne 100f @ no; go load the addresses | ||
42 | mov \rv, #0 @ yes; record init is done | ||
43 | str \rv, [\tmp] | ||
44 | mov \rp, #TEGRA_IRAM_BASE @ See if cookie is in IRAM | ||
45 | ldr \rv, [\rp, #TEGRA_IRAM_DEBUG_UART_OFFSET] | ||
46 | movw \rp, #TEGRA_IRAM_DEBUG_UART_COOKIE & 0xffff | ||
47 | movt \rp, #TEGRA_IRAM_DEBUG_UART_COOKIE >> 16 | ||
48 | cmp \rv, \rp @ Cookie present? | ||
49 | bne 100f @ No, use default UART | ||
50 | mov \rp, #TEGRA_IRAM_BASE @ Load UART address from IRAM | ||
51 | ldr \rv, [\rp, #TEGRA_IRAM_DEBUG_UART_OFFSET + 4] | ||
52 | str \rv, [\tmp, #4] @ Store in tegra_uart_phys | ||
53 | sub \rv, \rv, #IO_APB_PHYS @ Calculate virt address | ||
54 | add \rv, \rv, #IO_APB_VIRT | ||
55 | str \rv, [\tmp, #8] @ Store in tegra_uart_virt | ||
56 | b 100f | ||
57 | |||
58 | .align | ||
59 | 99: .word . | ||
60 | .word tegra_uart_config | ||
61 | .ltorg | ||
62 | |||
63 | 100: ldr \rp, [\tmp, #4] @ Load tegra_uart_phys | ||
64 | ldr \rv, [\tmp, #8] @ Load tegra_uart_virt | ||
65 | .endm | ||
66 | |||
67 | #define UART_SHIFT 2 | ||
68 | |||
69 | /* | ||
70 | * Code below is swiped from <asm/hardware/debug-8250.S>, but add an extra | ||
71 | * check to make sure that we aren't in the CONFIG_TEGRA_DEBUG_UART_NONE case. | ||
72 | * We use the fact that all 5 valid UART addresses all have something in the | ||
73 | * 2nd-to-lowest byte. | ||
74 | */ | ||
23 | 75 | ||
24 | .macro addruart, rp, rv, tmp | 76 | .macro senduart, rd, rx |
25 | ldr \rp, =IO_APB_PHYS @ physical | 77 | tst \rx, #0x0000ff00 |
26 | ldr \rv, =IO_APB_VIRT @ virtual | 78 | strneb \rd, [\rx, #UART_TX << UART_SHIFT] |
27 | orr \rp, \rp, #(TEGRA_DEBUG_UART_BASE & 0xFF) | 79 | 1001: |
28 | orr \rp, \rp, #(TEGRA_DEBUG_UART_BASE & 0xFF00) | 80 | .endm |
29 | orr \rv, \rv, #(TEGRA_DEBUG_UART_BASE & 0xFF) | ||
30 | orr \rv, \rv, #(TEGRA_DEBUG_UART_BASE & 0xFF00) | ||
31 | .endm | ||
32 | 81 | ||
33 | #define UART_SHIFT 2 | 82 | .macro busyuart, rd, rx |
34 | #include <asm/hardware/debug-8250.S> | 83 | tst \rx, #0x0000ff00 |
84 | beq 1002f | ||
85 | 1001: ldrb \rd, [\rx, #UART_LSR << UART_SHIFT] | ||
86 | and \rd, \rd, #UART_LSR_TEMT | UART_LSR_THRE | ||
87 | teq \rd, #UART_LSR_TEMT | UART_LSR_THRE | ||
88 | bne 1001b | ||
89 | 1002: | ||
90 | .endm | ||
35 | 91 | ||
92 | .macro waituart, rd, rx | ||
93 | #ifdef FLOW_CONTROL | ||
94 | tst \rx, #0x0000ff00 | ||
95 | beq 1002f | ||
96 | 1001: ldrb \rd, [\rx, #UART_MSR << UART_SHIFT] | ||
97 | tst \rd, #UART_MSR_CTS | ||
98 | beq 1001b | ||
99 | 1002: | ||
100 | #endif | ||
101 | .endm | ||
diff --git a/arch/arm/mach-tegra/include/mach/gpio-tegra.h b/arch/arm/mach-tegra/include/mach/gpio-tegra.h index 87d37fdf5084..6140820555e1 100644 --- a/arch/arm/mach-tegra/include/mach/gpio-tegra.h +++ b/arch/arm/mach-tegra/include/mach/gpio-tegra.h | |||
@@ -25,8 +25,6 @@ | |||
25 | 25 | ||
26 | #define TEGRA_NR_GPIOS INT_GPIO_NR | 26 | #define TEGRA_NR_GPIOS INT_GPIO_NR |
27 | 27 | ||
28 | #define TEGRA_GPIO_TO_IRQ(gpio) (INT_GPIO_BASE + (gpio)) | ||
29 | |||
30 | struct tegra_gpio_table { | 28 | struct tegra_gpio_table { |
31 | int gpio; /* GPIO number */ | 29 | int gpio; /* GPIO number */ |
32 | bool enable; /* Enable for GPIO at init? */ | 30 | bool enable; /* Enable for GPIO at init? */ |
diff --git a/arch/arm/mach-tegra/include/mach/irammap.h b/arch/arm/mach-tegra/include/mach/irammap.h new file mode 100644 index 000000000000..0cbe63261854 --- /dev/null +++ b/arch/arm/mach-tegra/include/mach/irammap.h | |||
@@ -0,0 +1,35 @@ | |||
1 | /* | ||
2 | * Copyright (c) 2012, NVIDIA CORPORATION. All rights reserved. | ||
3 | * | ||
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, | ||
6 | * version 2, as published by the Free Software Foundation. | ||
7 | * | ||
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 | ||
10 | * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for | ||
11 | * more details. | ||
12 | * | ||
13 | * You should have received a copy of the GNU General Public License | ||
14 | * along with this program. If not, see <http://www.gnu.org/licenses/>. | ||
15 | */ | ||
16 | |||
17 | #ifndef __MACH_TEGRA_IRAMMAP_H | ||
18 | #define __MACH_TEGRA_IRAMMAP_H | ||
19 | |||
20 | #include <asm/sizes.h> | ||
21 | |||
22 | /* The first 1K of IRAM is permanently reserved for the CPU reset handler */ | ||
23 | #define TEGRA_IRAM_RESET_HANDLER_OFFSET 0 | ||
24 | #define TEGRA_IRAM_RESET_HANDLER_SIZE SZ_1K | ||
25 | |||
26 | /* | ||
27 | * These locations are written to by uncompress.h, and read by debug-macro.S. | ||
28 | * The first word holds the cookie value if the data is valid. The second | ||
29 | * word holds the UART physical address. | ||
30 | */ | ||
31 | #define TEGRA_IRAM_DEBUG_UART_OFFSET SZ_1K | ||
32 | #define TEGRA_IRAM_DEBUG_UART_SIZE 8 | ||
33 | #define TEGRA_IRAM_DEBUG_UART_COOKIE 0x55415254 | ||
34 | |||
35 | #endif | ||
diff --git a/arch/arm/mach-tegra/include/mach/uncompress.h b/arch/arm/mach-tegra/include/mach/uncompress.h index 4e8323770c79..5a440f315e57 100644 --- a/arch/arm/mach-tegra/include/mach/uncompress.h +++ b/arch/arm/mach-tegra/include/mach/uncompress.h | |||
@@ -2,10 +2,14 @@ | |||
2 | * arch/arm/mach-tegra/include/mach/uncompress.h | 2 | * arch/arm/mach-tegra/include/mach/uncompress.h |
3 | * | 3 | * |
4 | * Copyright (C) 2010 Google, Inc. | 4 | * Copyright (C) 2010 Google, Inc. |
5 | * Copyright (C) 2011 Google, Inc. | ||
6 | * Copyright (C) 2011-2012 NVIDIA CORPORATION. All Rights Reserved. | ||
5 | * | 7 | * |
6 | * Author: | 8 | * Author: |
7 | * Colin Cross <ccross@google.com> | 9 | * Colin Cross <ccross@google.com> |
8 | * Erik Gilling <konkers@google.com> | 10 | * Erik Gilling <konkers@google.com> |
11 | * Doug Anderson <dianders@chromium.org> | ||
12 | * Stephen Warren <swarren@nvidia.com> | ||
9 | * | 13 | * |
10 | * This software is licensed under the terms of the GNU General Public | 14 | * This software is licensed under the terms of the GNU General Public |
11 | * License version 2, as published by the Free Software Foundation, and | 15 | * License version 2, as published by the Free Software Foundation, and |
@@ -25,36 +29,130 @@ | |||
25 | #include <linux/serial_reg.h> | 29 | #include <linux/serial_reg.h> |
26 | 30 | ||
27 | #include <mach/iomap.h> | 31 | #include <mach/iomap.h> |
32 | #include <mach/irammap.h> | ||
33 | |||
34 | #define BIT(x) (1 << (x)) | ||
35 | #define ARRAY_SIZE(arr) (sizeof(arr) / sizeof((arr)[0])) | ||
36 | |||
37 | #define DEBUG_UART_SHIFT 2 | ||
38 | |||
39 | volatile u8 *uart; | ||
28 | 40 | ||
29 | static void putc(int c) | 41 | static void putc(int c) |
30 | { | 42 | { |
31 | volatile u8 *uart = (volatile u8 *)TEGRA_DEBUG_UART_BASE; | ||
32 | int shift = 2; | ||
33 | |||
34 | if (uart == NULL) | 43 | if (uart == NULL) |
35 | return; | 44 | return; |
36 | 45 | ||
37 | while (!(uart[UART_LSR << shift] & UART_LSR_THRE)) | 46 | while (!(uart[UART_LSR << DEBUG_UART_SHIFT] & UART_LSR_THRE)) |
38 | barrier(); | 47 | barrier(); |
39 | uart[UART_TX << shift] = c; | 48 | uart[UART_TX << DEBUG_UART_SHIFT] = c; |
40 | } | 49 | } |
41 | 50 | ||
42 | static inline void flush(void) | 51 | static inline void flush(void) |
43 | { | 52 | { |
44 | } | 53 | } |
45 | 54 | ||
55 | static inline void save_uart_address(void) | ||
56 | { | ||
57 | u32 *buf = (u32 *)(TEGRA_IRAM_BASE + TEGRA_IRAM_DEBUG_UART_OFFSET); | ||
58 | |||
59 | if (uart) { | ||
60 | buf[0] = TEGRA_IRAM_DEBUG_UART_COOKIE; | ||
61 | buf[1] = (u32)uart; | ||
62 | } else | ||
63 | buf[0] = 0; | ||
64 | } | ||
65 | |||
66 | /* | ||
67 | * Setup before decompression. This is where we do UART selection for | ||
68 | * earlyprintk and init the uart_base register. | ||
69 | */ | ||
46 | static inline void arch_decomp_setup(void) | 70 | static inline void arch_decomp_setup(void) |
47 | { | 71 | { |
48 | volatile u8 *uart = (volatile u8 *)TEGRA_DEBUG_UART_BASE; | 72 | static const struct { |
49 | int shift = 2; | 73 | u32 base; |
74 | u32 reset_reg; | ||
75 | u32 clock_reg; | ||
76 | u32 bit; | ||
77 | } uarts[] = { | ||
78 | { | ||
79 | TEGRA_UARTA_BASE, | ||
80 | TEGRA_CLK_RESET_BASE + 0x04, | ||
81 | TEGRA_CLK_RESET_BASE + 0x10, | ||
82 | 6, | ||
83 | }, | ||
84 | { | ||
85 | TEGRA_UARTB_BASE, | ||
86 | TEGRA_CLK_RESET_BASE + 0x04, | ||
87 | TEGRA_CLK_RESET_BASE + 0x10, | ||
88 | 7, | ||
89 | }, | ||
90 | { | ||
91 | TEGRA_UARTC_BASE, | ||
92 | TEGRA_CLK_RESET_BASE + 0x08, | ||
93 | TEGRA_CLK_RESET_BASE + 0x14, | ||
94 | 23, | ||
95 | }, | ||
96 | { | ||
97 | TEGRA_UARTD_BASE, | ||
98 | TEGRA_CLK_RESET_BASE + 0x0c, | ||
99 | TEGRA_CLK_RESET_BASE + 0x18, | ||
100 | 1, | ||
101 | }, | ||
102 | { | ||
103 | TEGRA_UARTE_BASE, | ||
104 | TEGRA_CLK_RESET_BASE + 0x0c, | ||
105 | TEGRA_CLK_RESET_BASE + 0x18, | ||
106 | 2, | ||
107 | }, | ||
108 | }; | ||
109 | int i; | ||
110 | volatile u32 *apb_misc = (volatile u32 *)TEGRA_APB_MISC_BASE; | ||
111 | u32 chip, div; | ||
112 | |||
113 | /* | ||
114 | * Look for the first UART that: | ||
115 | * a) Is not in reset. | ||
116 | * b) Is clocked. | ||
117 | * c) Has a 'D' in the scratchpad register. | ||
118 | * | ||
119 | * Note that on Tegra30, the first two conditions are required, since | ||
120 | * if not true, accesses to the UART scratch register will hang. | ||
121 | * Tegra20 doesn't have this issue. | ||
122 | * | ||
123 | * The intent is that the bootloader will tell the kernel which UART | ||
124 | * to use by setting up those conditions. If nothing found, we'll fall | ||
125 | * back to what's specified in TEGRA_DEBUG_UART_BASE. | ||
126 | */ | ||
127 | for (i = 0; i < ARRAY_SIZE(uarts); i++) { | ||
128 | if (*(u8 *)uarts[i].reset_reg & BIT(uarts[i].bit)) | ||
129 | continue; | ||
50 | 130 | ||
131 | if (!(*(u8 *)uarts[i].clock_reg & BIT(uarts[i].bit))) | ||
132 | continue; | ||
133 | |||
134 | uart = (volatile u8 *)uarts[i].base; | ||
135 | if (uart[UART_SCR << DEBUG_UART_SHIFT] != 'D') | ||
136 | continue; | ||
137 | |||
138 | break; | ||
139 | } | ||
140 | if (i == ARRAY_SIZE(uarts)) | ||
141 | uart = (volatile u8 *)TEGRA_DEBUG_UART_BASE; | ||
142 | save_uart_address(); | ||
51 | if (uart == NULL) | 143 | if (uart == NULL) |
52 | return; | 144 | return; |
53 | 145 | ||
54 | uart[UART_LCR << shift] |= UART_LCR_DLAB; | 146 | chip = (apb_misc[0x804 / 4] >> 8) & 0xff; |
55 | uart[UART_DLL << shift] = 0x75; | 147 | if (chip == 0x20) |
56 | uart[UART_DLM << shift] = 0x0; | 148 | div = 0x0075; |
57 | uart[UART_LCR << shift] = 3; | 149 | else |
150 | div = 0x00dd; | ||
151 | |||
152 | uart[UART_LCR << DEBUG_UART_SHIFT] |= UART_LCR_DLAB; | ||
153 | uart[UART_DLL << DEBUG_UART_SHIFT] = div & 0xff; | ||
154 | uart[UART_DLM << DEBUG_UART_SHIFT] = div >> 8; | ||
155 | uart[UART_LCR << DEBUG_UART_SHIFT] = 3; | ||
58 | } | 156 | } |
59 | 157 | ||
60 | static inline void arch_decomp_wdog(void) | 158 | static inline void arch_decomp_wdog(void) |
diff --git a/arch/arm/mach-tegra/pmc.c b/arch/arm/mach-tegra/pmc.c new file mode 100644 index 000000000000..7af6a54404be --- /dev/null +++ b/arch/arm/mach-tegra/pmc.c | |||
@@ -0,0 +1,76 @@ | |||
1 | /* | ||
2 | * Copyright (C) 2012 NVIDIA CORPORATION. All rights reserved. | ||
3 | * | ||
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, | ||
6 | * version 2, as published by the Free Software Foundation. | ||
7 | * | ||
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 | ||
10 | * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for | ||
11 | * more details. | ||
12 | * | ||
13 | * You should have received a copy of the GNU General Public License | ||
14 | * along with this program. If not, see <http://www.gnu.org/licenses/>. | ||
15 | * | ||
16 | */ | ||
17 | |||
18 | #include <linux/kernel.h> | ||
19 | #include <linux/io.h> | ||
20 | #include <linux/of.h> | ||
21 | |||
22 | #include <mach/iomap.h> | ||
23 | |||
24 | #define PMC_CTRL 0x0 | ||
25 | #define PMC_CTRL_INTR_LOW (1 << 17) | ||
26 | |||
27 | static inline u32 tegra_pmc_readl(u32 reg) | ||
28 | { | ||
29 | return readl(IO_ADDRESS(TEGRA_PMC_BASE + reg)); | ||
30 | } | ||
31 | |||
32 | static inline void tegra_pmc_writel(u32 val, u32 reg) | ||
33 | { | ||
34 | writel(val, IO_ADDRESS(TEGRA_PMC_BASE + reg)); | ||
35 | } | ||
36 | |||
37 | #ifdef CONFIG_OF | ||
38 | static const struct of_device_id matches[] __initconst = { | ||
39 | { .compatible = "nvidia,tegra20-pmc" }, | ||
40 | { } | ||
41 | }; | ||
42 | #endif | ||
43 | |||
44 | void __init tegra_pmc_init(void) | ||
45 | { | ||
46 | /* | ||
47 | * For now, Harmony is the only board that uses the PMC, and it wants | ||
48 | * the signal inverted. Seaboard would too if it used the PMC. | ||
49 | * Hopefully by the time other boards want to use the PMC, everything | ||
50 | * will be device-tree, or they also want it inverted. | ||
51 | */ | ||
52 | bool invert_interrupt = true; | ||
53 | u32 val; | ||
54 | |||
55 | #ifdef CONFIG_OF | ||
56 | if (of_have_populated_dt()) { | ||
57 | struct device_node *np; | ||
58 | |||
59 | invert_interrupt = false; | ||
60 | |||
61 | np = of_find_matching_node(NULL, matches); | ||
62 | if (np) { | ||
63 | if (of_find_property(np, "nvidia,invert-interrupt", | ||
64 | NULL)) | ||
65 | invert_interrupt = true; | ||
66 | } | ||
67 | } | ||
68 | #endif | ||
69 | |||
70 | val = tegra_pmc_readl(PMC_CTRL); | ||
71 | if (invert_interrupt) | ||
72 | val |= PMC_CTRL_INTR_LOW; | ||
73 | else | ||
74 | val &= ~PMC_CTRL_INTR_LOW; | ||
75 | tegra_pmc_writel(val, PMC_CTRL); | ||
76 | } | ||
diff --git a/arch/arm/mach-tegra/pmc.h b/arch/arm/mach-tegra/pmc.h new file mode 100644 index 000000000000..8995ee4a8768 --- /dev/null +++ b/arch/arm/mach-tegra/pmc.h | |||
@@ -0,0 +1,23 @@ | |||
1 | /* | ||
2 | * Copyright (C) 2012 NVIDIA CORPORATION. All rights reserved. | ||
3 | * | ||
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, | ||
6 | * version 2, as published by the Free Software Foundation. | ||
7 | * | ||
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 | ||
10 | * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for | ||
11 | * more details. | ||
12 | * | ||
13 | * You should have received a copy of the GNU General Public License | ||
14 | * along with this program. If not, see <http://www.gnu.org/licenses/>. | ||
15 | * | ||
16 | */ | ||
17 | |||
18 | #ifndef __MACH_TEGRA_PMC_H | ||
19 | #define __MACH_TEGRA_PMC_H | ||
20 | |||
21 | void tegra_pmc_init(void); | ||
22 | |||
23 | #endif | ||
diff --git a/arch/arm/mach-tegra/tegra2_clocks.c b/arch/arm/mach-tegra/tegra2_clocks.c index ff9e6b6c0460..74d314fdf2f9 100644 --- a/arch/arm/mach-tegra/tegra2_clocks.c +++ b/arch/arm/mach-tegra/tegra2_clocks.c | |||
@@ -720,7 +720,7 @@ static void tegra2_pllx_clk_init(struct clk *c) | |||
720 | { | 720 | { |
721 | tegra2_pll_clk_init(c); | 721 | tegra2_pll_clk_init(c); |
722 | 722 | ||
723 | if (tegra_sku_id() == 7) | 723 | if (tegra_sku_id == 7) |
724 | c->max_rate = 750000000; | 724 | c->max_rate = 750000000; |
725 | } | 725 | } |
726 | 726 | ||
diff --git a/arch/arm/mach-tegra/tegra2_emc.c b/arch/arm/mach-tegra/tegra2_emc.c index 0f7ae6e90b55..5070d833bdd1 100644 --- a/arch/arm/mach-tegra/tegra2_emc.c +++ b/arch/arm/mach-tegra/tegra2_emc.c | |||
@@ -16,14 +16,19 @@ | |||
16 | */ | 16 | */ |
17 | 17 | ||
18 | #include <linux/kernel.h> | 18 | #include <linux/kernel.h> |
19 | #include <linux/device.h> | ||
19 | #include <linux/clk.h> | 20 | #include <linux/clk.h> |
20 | #include <linux/err.h> | 21 | #include <linux/err.h> |
21 | #include <linux/io.h> | 22 | #include <linux/io.h> |
22 | #include <linux/module.h> | 23 | #include <linux/module.h> |
24 | #include <linux/of.h> | ||
25 | #include <linux/platform_device.h> | ||
26 | #include <linux/platform_data/tegra_emc.h> | ||
23 | 27 | ||
24 | #include <mach/iomap.h> | 28 | #include <mach/iomap.h> |
25 | 29 | ||
26 | #include "tegra2_emc.h" | 30 | #include "tegra2_emc.h" |
31 | #include "fuse.h" | ||
27 | 32 | ||
28 | #ifdef CONFIG_TEGRA_EMC_SCALING_ENABLE | 33 | #ifdef CONFIG_TEGRA_EMC_SCALING_ENABLE |
29 | static bool emc_enable = true; | 34 | static bool emc_enable = true; |
@@ -32,18 +37,17 @@ static bool emc_enable; | |||
32 | #endif | 37 | #endif |
33 | module_param(emc_enable, bool, 0644); | 38 | module_param(emc_enable, bool, 0644); |
34 | 39 | ||
35 | static void __iomem *emc = IO_ADDRESS(TEGRA_EMC_BASE); | 40 | static struct platform_device *emc_pdev; |
36 | static const struct tegra_emc_table *tegra_emc_table; | 41 | static void __iomem *emc_regbase; |
37 | static int tegra_emc_table_size; | ||
38 | 42 | ||
39 | static inline void emc_writel(u32 val, unsigned long addr) | 43 | static inline void emc_writel(u32 val, unsigned long addr) |
40 | { | 44 | { |
41 | writel(val, emc + addr); | 45 | writel(val, emc_regbase + addr); |
42 | } | 46 | } |
43 | 47 | ||
44 | static inline u32 emc_readl(unsigned long addr) | 48 | static inline u32 emc_readl(unsigned long addr) |
45 | { | 49 | { |
46 | return readl(emc + addr); | 50 | return readl(emc_regbase + addr); |
47 | } | 51 | } |
48 | 52 | ||
49 | static const unsigned long emc_reg_addr[TEGRA_EMC_NUM_REGS] = { | 53 | static const unsigned long emc_reg_addr[TEGRA_EMC_NUM_REGS] = { |
@@ -98,15 +102,15 @@ static const unsigned long emc_reg_addr[TEGRA_EMC_NUM_REGS] = { | |||
98 | /* Select the closest EMC rate that is higher than the requested rate */ | 102 | /* Select the closest EMC rate that is higher than the requested rate */ |
99 | long tegra_emc_round_rate(unsigned long rate) | 103 | long tegra_emc_round_rate(unsigned long rate) |
100 | { | 104 | { |
105 | struct tegra_emc_pdata *pdata; | ||
101 | int i; | 106 | int i; |
102 | int best = -1; | 107 | int best = -1; |
103 | unsigned long distance = ULONG_MAX; | 108 | unsigned long distance = ULONG_MAX; |
104 | 109 | ||
105 | if (!tegra_emc_table) | 110 | if (!emc_pdev) |
106 | return -EINVAL; | 111 | return -EINVAL; |
107 | 112 | ||
108 | if (!emc_enable) | 113 | pdata = emc_pdev->dev.platform_data; |
109 | return -EINVAL; | ||
110 | 114 | ||
111 | pr_debug("%s: %lu\n", __func__, rate); | 115 | pr_debug("%s: %lu\n", __func__, rate); |
112 | 116 | ||
@@ -116,10 +120,10 @@ long tegra_emc_round_rate(unsigned long rate) | |||
116 | */ | 120 | */ |
117 | rate = rate / 2 / 1000; | 121 | rate = rate / 2 / 1000; |
118 | 122 | ||
119 | for (i = 0; i < tegra_emc_table_size; i++) { | 123 | for (i = 0; i < pdata->num_tables; i++) { |
120 | if (tegra_emc_table[i].rate >= rate && | 124 | if (pdata->tables[i].rate >= rate && |
121 | (tegra_emc_table[i].rate - rate) < distance) { | 125 | (pdata->tables[i].rate - rate) < distance) { |
122 | distance = tegra_emc_table[i].rate - rate; | 126 | distance = pdata->tables[i].rate - rate; |
123 | best = i; | 127 | best = i; |
124 | } | 128 | } |
125 | } | 129 | } |
@@ -127,9 +131,9 @@ long tegra_emc_round_rate(unsigned long rate) | |||
127 | if (best < 0) | 131 | if (best < 0) |
128 | return -EINVAL; | 132 | return -EINVAL; |
129 | 133 | ||
130 | pr_debug("%s: using %lu\n", __func__, tegra_emc_table[best].rate); | 134 | pr_debug("%s: using %lu\n", __func__, pdata->tables[best].rate); |
131 | 135 | ||
132 | return tegra_emc_table[best].rate * 2 * 1000; | 136 | return pdata->tables[best].rate * 2 * 1000; |
133 | } | 137 | } |
134 | 138 | ||
135 | /* | 139 | /* |
@@ -142,37 +146,211 @@ long tegra_emc_round_rate(unsigned long rate) | |||
142 | */ | 146 | */ |
143 | int tegra_emc_set_rate(unsigned long rate) | 147 | int tegra_emc_set_rate(unsigned long rate) |
144 | { | 148 | { |
149 | struct tegra_emc_pdata *pdata; | ||
145 | int i; | 150 | int i; |
146 | int j; | 151 | int j; |
147 | 152 | ||
148 | if (!tegra_emc_table) | 153 | if (!emc_pdev) |
149 | return -EINVAL; | 154 | return -EINVAL; |
150 | 155 | ||
156 | pdata = emc_pdev->dev.platform_data; | ||
157 | |||
151 | /* | 158 | /* |
152 | * The EMC clock rate is twice the bus rate, and the bus rate is | 159 | * The EMC clock rate is twice the bus rate, and the bus rate is |
153 | * measured in kHz | 160 | * measured in kHz |
154 | */ | 161 | */ |
155 | rate = rate / 2 / 1000; | 162 | rate = rate / 2 / 1000; |
156 | 163 | ||
157 | for (i = 0; i < tegra_emc_table_size; i++) | 164 | for (i = 0; i < pdata->num_tables; i++) |
158 | if (tegra_emc_table[i].rate == rate) | 165 | if (pdata->tables[i].rate == rate) |
159 | break; | 166 | break; |
160 | 167 | ||
161 | if (i >= tegra_emc_table_size) | 168 | if (i >= pdata->num_tables) |
162 | return -EINVAL; | 169 | return -EINVAL; |
163 | 170 | ||
164 | pr_debug("%s: setting to %lu\n", __func__, rate); | 171 | pr_debug("%s: setting to %lu\n", __func__, rate); |
165 | 172 | ||
166 | for (j = 0; j < TEGRA_EMC_NUM_REGS; j++) | 173 | for (j = 0; j < TEGRA_EMC_NUM_REGS; j++) |
167 | emc_writel(tegra_emc_table[i].regs[j], emc_reg_addr[j]); | 174 | emc_writel(pdata->tables[i].regs[j], emc_reg_addr[j]); |
168 | 175 | ||
169 | emc_readl(tegra_emc_table[i].regs[TEGRA_EMC_NUM_REGS - 1]); | 176 | emc_readl(pdata->tables[i].regs[TEGRA_EMC_NUM_REGS - 1]); |
170 | 177 | ||
171 | return 0; | 178 | return 0; |
172 | } | 179 | } |
173 | 180 | ||
174 | void tegra_init_emc(const struct tegra_emc_table *table, int table_size) | 181 | #ifdef CONFIG_OF |
182 | static struct device_node *tegra_emc_ramcode_devnode(struct device_node *np) | ||
183 | { | ||
184 | struct device_node *iter; | ||
185 | u32 reg; | ||
186 | |||
187 | for_each_child_of_node(np, iter) { | ||
188 | if (of_property_read_u32(np, "nvidia,ram-code", ®)) | ||
189 | continue; | ||
190 | if (reg == tegra_bct_strapping) | ||
191 | return of_node_get(iter); | ||
192 | } | ||
193 | |||
194 | return NULL; | ||
195 | } | ||
196 | |||
197 | static struct tegra_emc_pdata *tegra_emc_dt_parse_pdata( | ||
198 | struct platform_device *pdev) | ||
199 | { | ||
200 | struct device_node *np = pdev->dev.of_node; | ||
201 | struct device_node *tnp, *iter; | ||
202 | struct tegra_emc_pdata *pdata; | ||
203 | int ret, i, num_tables; | ||
204 | |||
205 | if (!np) | ||
206 | return NULL; | ||
207 | |||
208 | if (of_find_property(np, "nvidia,use-ram-code", NULL)) { | ||
209 | tnp = tegra_emc_ramcode_devnode(np); | ||
210 | if (!tnp) | ||
211 | dev_warn(&pdev->dev, | ||
212 | "can't find emc table for ram-code 0x%02x\n", | ||
213 | tegra_bct_strapping); | ||
214 | } else | ||
215 | tnp = of_node_get(np); | ||
216 | |||
217 | if (!tnp) | ||
218 | return NULL; | ||
219 | |||
220 | num_tables = 0; | ||
221 | for_each_child_of_node(tnp, iter) | ||
222 | if (of_device_is_compatible(iter, "nvidia,tegra20-emc-table")) | ||
223 | num_tables++; | ||
224 | |||
225 | if (!num_tables) { | ||
226 | pdata = NULL; | ||
227 | goto out; | ||
228 | } | ||
229 | |||
230 | pdata = devm_kzalloc(&pdev->dev, sizeof(*pdata), GFP_KERNEL); | ||
231 | pdata->tables = devm_kzalloc(&pdev->dev, | ||
232 | sizeof(*pdata->tables) * num_tables, | ||
233 | GFP_KERNEL); | ||
234 | |||
235 | i = 0; | ||
236 | for_each_child_of_node(tnp, iter) { | ||
237 | u32 prop; | ||
238 | |||
239 | ret = of_property_read_u32(iter, "clock-frequency", &prop); | ||
240 | if (ret) { | ||
241 | dev_err(&pdev->dev, "no clock-frequency in %s\n", | ||
242 | iter->full_name); | ||
243 | continue; | ||
244 | } | ||
245 | pdata->tables[i].rate = prop; | ||
246 | |||
247 | ret = of_property_read_u32_array(iter, "nvidia,emc-registers", | ||
248 | pdata->tables[i].regs, | ||
249 | TEGRA_EMC_NUM_REGS); | ||
250 | if (ret) { | ||
251 | dev_err(&pdev->dev, | ||
252 | "malformed emc-registers property in %s\n", | ||
253 | iter->full_name); | ||
254 | continue; | ||
255 | } | ||
256 | |||
257 | i++; | ||
258 | } | ||
259 | pdata->num_tables = i; | ||
260 | |||
261 | out: | ||
262 | of_node_put(tnp); | ||
263 | return pdata; | ||
264 | } | ||
265 | #else | ||
266 | static struct tegra_emc_pdata *tegra_emc_dt_parse_pdata( | ||
267 | struct platform_device *pdev) | ||
268 | { | ||
269 | return NULL; | ||
270 | } | ||
271 | #endif | ||
272 | |||
273 | static struct tegra_emc_pdata __devinit *tegra_emc_fill_pdata(struct platform_device *pdev) | ||
274 | { | ||
275 | struct clk *c = clk_get_sys(NULL, "emc"); | ||
276 | struct tegra_emc_pdata *pdata; | ||
277 | unsigned long khz; | ||
278 | int i; | ||
279 | |||
280 | WARN_ON(pdev->dev.platform_data); | ||
281 | BUG_ON(IS_ERR_OR_NULL(c)); | ||
282 | |||
283 | pdata = devm_kzalloc(&pdev->dev, sizeof(*pdata), GFP_KERNEL); | ||
284 | pdata->tables = devm_kzalloc(&pdev->dev, sizeof(*pdata->tables), | ||
285 | GFP_KERNEL); | ||
286 | |||
287 | pdata->tables[0].rate = clk_get_rate(c) / 2 / 1000; | ||
288 | |||
289 | for (i = 0; i < TEGRA_EMC_NUM_REGS; i++) | ||
290 | pdata->tables[0].regs[i] = emc_readl(emc_reg_addr[i]); | ||
291 | |||
292 | pdata->num_tables = 1; | ||
293 | |||
294 | khz = pdata->tables[0].rate; | ||
295 | dev_info(&pdev->dev, "no tables provided, using %ld kHz emc, " | ||
296 | "%ld kHz mem\n", khz * 2, khz); | ||
297 | |||
298 | return pdata; | ||
299 | } | ||
300 | |||
301 | static int __devinit tegra_emc_probe(struct platform_device *pdev) | ||
302 | { | ||
303 | struct tegra_emc_pdata *pdata; | ||
304 | struct resource *res; | ||
305 | |||
306 | if (!emc_enable) { | ||
307 | dev_err(&pdev->dev, "disabled per module parameter\n"); | ||
308 | return -ENODEV; | ||
309 | } | ||
310 | |||
311 | res = platform_get_resource(pdev, IORESOURCE_MEM, 0); | ||
312 | if (!res) { | ||
313 | dev_err(&pdev->dev, "missing register base\n"); | ||
314 | return -ENOMEM; | ||
315 | } | ||
316 | |||
317 | emc_regbase = devm_request_and_ioremap(&pdev->dev, res); | ||
318 | if (!emc_regbase) { | ||
319 | dev_err(&pdev->dev, "failed to remap registers\n"); | ||
320 | return -ENOMEM; | ||
321 | } | ||
322 | |||
323 | pdata = pdev->dev.platform_data; | ||
324 | |||
325 | if (!pdata) | ||
326 | pdata = tegra_emc_dt_parse_pdata(pdev); | ||
327 | |||
328 | if (!pdata) | ||
329 | pdata = tegra_emc_fill_pdata(pdev); | ||
330 | |||
331 | pdev->dev.platform_data = pdata; | ||
332 | |||
333 | emc_pdev = pdev; | ||
334 | |||
335 | return 0; | ||
336 | } | ||
337 | |||
338 | static struct of_device_id tegra_emc_of_match[] __devinitdata = { | ||
339 | { .compatible = "nvidia,tegra20-emc", }, | ||
340 | { }, | ||
341 | }; | ||
342 | |||
343 | static struct platform_driver tegra_emc_driver = { | ||
344 | .driver = { | ||
345 | .name = "tegra-emc", | ||
346 | .owner = THIS_MODULE, | ||
347 | .of_match_table = tegra_emc_of_match, | ||
348 | }, | ||
349 | .probe = tegra_emc_probe, | ||
350 | }; | ||
351 | |||
352 | static int __init tegra_emc_init(void) | ||
175 | { | 353 | { |
176 | tegra_emc_table = table; | 354 | return platform_driver_register(&tegra_emc_driver); |
177 | tegra_emc_table_size = table_size; | ||
178 | } | 355 | } |
356 | device_initcall(tegra_emc_init); | ||
diff --git a/arch/arm/mach-tegra/tegra2_emc.h b/arch/arm/mach-tegra/tegra2_emc.h index 19f08cb31603..f61409b54cb7 100644 --- a/arch/arm/mach-tegra/tegra2_emc.h +++ b/arch/arm/mach-tegra/tegra2_emc.h | |||
@@ -15,13 +15,10 @@ | |||
15 | * | 15 | * |
16 | */ | 16 | */ |
17 | 17 | ||
18 | #define TEGRA_EMC_NUM_REGS 46 | 18 | #ifndef __MACH_TEGRA_TEGRA2_EMC_H_ |
19 | 19 | #define __MACH_TEGRA_TEGRA2_EMC_H | |
20 | struct tegra_emc_table { | ||
21 | unsigned long rate; | ||
22 | u32 regs[TEGRA_EMC_NUM_REGS]; | ||
23 | }; | ||
24 | 20 | ||
25 | int tegra_emc_set_rate(unsigned long rate); | 21 | int tegra_emc_set_rate(unsigned long rate); |
26 | long tegra_emc_round_rate(unsigned long rate); | 22 | long tegra_emc_round_rate(unsigned long rate); |
27 | void tegra_init_emc(const struct tegra_emc_table *table, int table_size); | 23 | |
24 | #endif | ||