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>
This commit is contained in:
Arnd Bergmann 2012-03-04 21:00:27 +00:00
commit 7cb7f82611
32 changed files with 1467 additions and 193 deletions

View File

@ -0,0 +1,100 @@
Embedded Memory Controller
Properties:
- name : Should be emc
- #address-cells : Should be 1
- #size-cells : Should be 0
- compatible : Should contain "nvidia,tegra20-emc".
- reg : Offset and length of the register set for the device
- nvidia,use-ram-code : If present, the sub-nodes will be addressed
and chosen using the ramcode board selector. If omitted, only one
set of tables can be present and said tables will be used
irrespective of ram-code configuration.
Child device nodes describe the memory settings for different configurations and clock rates.
Example:
emc@7000f400 {
#address-cells = < 1 >;
#size-cells = < 0 >;
compatible = "nvidia,tegra20-emc";
reg = <0x7000f4000 0x200>;
}
Embedded Memory Controller ram-code table
If the emc node has the nvidia,use-ram-code property present, then the
next level of nodes below the emc table are used to specify which settings
apply for which ram-code settings.
If the emc node lacks the nvidia,use-ram-code property, this level is omitted
and the tables are stored directly under the emc node (see below).
Properties:
- name : Should be emc-tables
- nvidia,ram-code : the binary representation of the ram-code board strappings
for which this node (and children) are valid.
Embedded Memory Controller configuration table
This is a table containing the EMC register settings for the various
operating speeds of the memory controller. They are always located as
subnodes of the emc controller node.
There are two ways of specifying which tables to use:
* The simplest is if there is just one set of tables in the device tree,
and they will always be used (based on which frequency is used).
This is the preferred method, especially when firmware can fill in
this information based on the specific system information and just
pass it on to the kernel.
* The slightly more complex one is when more than one memory configuration
might exist on the system. The Tegra20 platform handles this during
early boot by selecting one out of possible 4 memory settings based
on a 2-pin "ram code" bootstrap setting on the board. The values of
these strappings can be read through a register in the SoC, and thus
used to select which tables to use.
Properties:
- name : Should be emc-table
- compatible : Should contain "nvidia,tegra20-emc-table".
- reg : either an opaque enumerator to tell different tables apart, or
the valid frequency for which the table should be used (in kHz).
- clock-frequency : the clock frequency for the EMC at which this
table should be used (in kHz).
- nvidia,emc-registers : a 46 word array of EMC registers to be programmed
for operation at the 'clock-frequency' setting.
The order and contents of the registers are:
RC, RFC, RAS, RP, R2W, W2R, R2P, W2P, RD_RCD, WR_RCD, RRD, REXT,
WDV, QUSE, QRST, QSAFE, RDV, REFRESH, BURST_REFRESH_NUM, PDEX2WR,
PDEX2RD, PCHG2PDEN, ACT2PDEN, AR2PDEN, RW2PDEN, TXSR, TCKE, TFAW,
TRPAB, TCLKSTABLE, TCLKSTOP, TREFBW, QUSE_EXTRA, FBIO_CFG6, ODT_WRITE,
ODT_READ, FBIO_CFG5, CFG_DIG_DLL, DLL_XFORM_DQS, DLL_XFORM_QUSE,
ZCAL_REF_CNT, ZCAL_WAIT_CNT, AUTO_CAL_INTERVAL, CFG_CLKTRIM_0,
CFG_CLKTRIM_1, CFG_CLKTRIM_2
emc-table@166000 {
reg = <166000>;
compatible = "nvidia,tegra20-emc-table";
clock-frequency = < 166000 >;
nvidia,emc-registers = < 0 0 0 0 0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0 0 0 0 0
0 0 0 0 >;
};
emc-table@333000 {
reg = <333000>;
compatible = "nvidia,tegra20-emc-table";
clock-frequency = < 333000 >;
nvidia,emc-registers = < 0 0 0 0 0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0 0 0 0 0
0 0 0 0 >;
};

View File

@ -0,0 +1,19 @@
NVIDIA Tegra Power Management Controller (PMC)
Properties:
- name : Should be pmc
- compatible : Should contain "nvidia,tegra<chip>-pmc".
- reg : Offset and length of the register set for the device
- nvidia,invert-interrupt : If present, inverts the PMU interrupt signal.
The PMU is an external Power Management Unit, whose interrupt output
signal is fed into the PMC. This signal is optionally inverted, and then
fed into the ARM GIC. The PMC is not involved in the detection or
handling of this interrupt signal, merely its inversion.
Example:
pmc@7000f400 {
compatible = "nvidia,tegra20-pmc";
reg = <0x7000e400 0x400>;
nvidia,invert-interrupt;
};

View File

@ -0,0 +1,30 @@
* NVIDIA Tegra APB DMA controller
Required properties:
- compatible: Should be "nvidia,<chip>-apbdma"
- reg: Should contain DMA registers location and length. This shuld include
all of the per-channel registers.
- interrupts: Should contain all of the per-channel DMA interrupts.
Examples:
apbdma: dma@6000a000 {
compatible = "nvidia,tegra20-apbdma";
reg = <0x6000a000 0x1200>;
interrupts = < 0 136 0x04
0 137 0x04
0 138 0x04
0 139 0x04
0 140 0x04
0 141 0x04
0 142 0x04
0 143 0x04
0 144 0x04
0 145 0x04
0 146 0x04
0 147 0x04
0 148 0x04
0 149 0x04
0 150 0x04
0 151 0x04 >;
};

View File

@ -1,8 +1,40 @@
NVIDIA Tegra 2 GPIO controller
NVIDIA Tegra GPIO controller
Required properties:
- compatible : "nvidia,tegra20-gpio"
- compatible : "nvidia,tegra<chip>-gpio"
- reg : Physical base address and length of the controller's registers.
- interrupts : The interrupt outputs from the controller. For Tegra20,
there should be 7 interrupts specified, and for Tegra30, there should
be 8 interrupts specified.
- #gpio-cells : Should be two. The first cell is the pin number and the
second cell is used to specify optional parameters:
- bit 0 specifies polarity (0 for normal, 1 for inverted)
- gpio-controller : Marks the device node as a GPIO controller.
- #interrupt-cells : Should be 2.
The first cell is the GPIO number.
The second cell is used to specify flags:
bits[3:0] trigger type and level flags:
1 = low-to-high edge triggered.
2 = high-to-low edge triggered.
4 = active high level-sensitive.
8 = active low level-sensitive.
Valid combinations are 1, 2, 3, 4, 8.
- interrupt-controller : Marks the device node as an interrupt controller.
Example:
gpio: gpio@6000d000 {
compatible = "nvidia,tegra20-gpio";
reg = < 0x6000d000 0x1000 >;
interrupts = < 0 32 0x04
0 33 0x04
0 34 0x04
0 35 0x04
0 55 0x04
0 87 0x04
0 89 0x04 >;
#gpio-cells = <2>;
gpio-controller;
#interrupt-cells = <2>;
interrupt-controller;
};

View File

@ -10,19 +10,25 @@ memory@0 {
reg = < 0x00000000 0x40000000 >;
};
pmc@7000f400 {
nvidia,invert-interrupt;
};
i2c@7000c000 {
clock-frequency = <400000>;
codec: wm8903@1a {
wm8903: wm8903@1a {
compatible = "wlf,wm8903";
reg = <0x1a>;
interrupts = < 347 >;
interrupt-parent = <&gpio>;
interrupts = < 187 0x04 >;
gpio-controller;
#gpio-cells = <2>;
/* 0x8000 = Not configured */
gpio-cfg = < 0x8000 0x8000 0 0x8000 0x8000 >;
micdet-cfg = <0>;
micdet-delay = <100>;
gpio-cfg = < 0xffffffff 0xffffffff 0 0xffffffff 0xffffffff >;
};
};
@ -38,13 +44,32 @@ i2c@7000d000 {
clock-frequency = <400000>;
};
sound {
compatible = "nvidia,harmony-sound", "nvidia,tegra-wm8903";
i2s@70002a00 {
status = "disable";
};
spkr-en-gpios = <&codec 2 0>;
hp-det-gpios = <&gpio 178 0>;
int-mic-en-gpios = <&gpio 184 0>;
ext-mic-en-gpios = <&gpio 185 0>;
sound {
compatible = "nvidia,tegra-audio-wm8903-harmony",
"nvidia,tegra-audio-wm8903";
nvidia,model = "NVIDIA Tegra Harmony";
nvidia,audio-routing =
"Headphone Jack", "HPOUTR",
"Headphone Jack", "HPOUTL",
"Int Spk", "ROP",
"Int Spk", "RON",
"Int Spk", "LOP",
"Int Spk", "LON",
"Mic Jack", "MICBIAS",
"IN1L", "Mic Jack";
nvidia,i2s-controller = <&tegra_i2s1>;
nvidia,audio-codec = <&wm8903>;
nvidia,spkr-en-gpios = <&wm8903 2 0>;
nvidia,hp-det-gpios = <&gpio 178 0>; /* gpio PW2 */
nvidia,int-mic-en-gpios = <&gpio 184 0>; /*gpio PX0 */
nvidia,ext-mic-en-gpios = <&gpio 185 0>; /* gpio PX1 */
};
serial@70006000 {

View File

@ -12,6 +12,13 @@ memory@0 {
i2c@7000c000 {
clock-frequency = <400000>;
alc5632: alc5632@1e {
compatible = "realtek,alc5632";
reg = <0x1e>;
gpio-controller;
#gpio-cells = <2>;
};
};
i2c@7000c400 {
@ -37,6 +44,29 @@ i2c@7000d000 {
clock-frequency = <400000>;
};
i2s@70002a00 {
status = "disable";
};
sound {
compatible = "nvidia,tegra-audio-alc5632-paz00",
"nvidia,tegra-audio-alc5632";
nvidia,model = "Compal PAZ00";
nvidia,audio-routing =
"Int Spk", "SPKOUT",
"Int Spk", "SPKOUTN",
"Headset Mic", "MICBIAS1",
"MIC1", "Headset Mic",
"Headset Stereophone", "HPR",
"Headset Stereophone", "HPL";
nvidia,audio-codec = <&alc5632>;
nvidia,i2s-controller = <&tegra_i2s1>;
nvidia,hp-det-gpios = <&gpio 178 0>; /* gpio PW2 */
};
serial@70006000 {
clock-frequency = <216000000>;
};

View File

@ -13,6 +13,20 @@ memory {
i2c@7000c000 {
clock-frequency = <400000>;
wm8903: wm8903@1a {
compatible = "wlf,wm8903";
reg = <0x1a>;
interrupt-parent = <&gpio>;
interrupts = < 187 0x04 >;
gpio-controller;
#gpio-cells = <2>;
micdet-cfg = <0>;
micdet-delay = <100>;
gpio-cfg = < 0xffffffff 0xffffffff 0 0xffffffff 0xffffffff >;
};
};
i2c@7000c400 {
@ -32,6 +46,32 @@ adt7461@4c {
};
};
i2s@70002a00 {
status = "disable";
};
sound {
compatible = "nvidia,tegra-audio-wm8903-seaboard",
"nvidia,tegra-audio-wm8903";
nvidia,model = "NVIDIA Tegra Seaboard";
nvidia,audio-routing =
"Headphone Jack", "HPOUTR",
"Headphone Jack", "HPOUTL",
"Int Spk", "ROP",
"Int Spk", "RON",
"Int Spk", "LOP",
"Int Spk", "LON",
"Mic Jack", "MICBIAS",
"IN1R", "Mic Jack";
nvidia,i2s-controller = <&tegra_i2s1>;
nvidia,audio-codec = <&wm8903>;
nvidia,spkr-en-gpios = <&wm8903 2 0>;
nvidia,hp-det-gpios = <&gpio 185 0>; /* gpio PX1 */
};
serial@70006000 {
status = "disable";
};
@ -93,4 +133,42 @@ lid {
gpio-key,wakeup;
};
};
emc@7000f400 {
emc-table@190000 {
reg = < 190000 >;
compatible = "nvidia,tegra20-emc-table";
clock-frequency = < 190000 >;
nvidia,emc-registers = < 0x0000000c 0x00000026
0x00000009 0x00000003 0x00000004 0x00000004
0x00000002 0x0000000c 0x00000003 0x00000003
0x00000002 0x00000001 0x00000004 0x00000005
0x00000004 0x00000009 0x0000000d 0x0000059f
0x00000000 0x00000003 0x00000003 0x00000003
0x00000003 0x00000001 0x0000000b 0x000000c8
0x00000003 0x00000007 0x00000004 0x0000000f
0x00000002 0x00000000 0x00000000 0x00000002
0x00000000 0x00000000 0x00000083 0xa06204ae
0x007dc010 0x00000000 0x00000000 0x00000000
0x00000000 0x00000000 0x00000000 0x00000000 >;
};
emc-table@380000 {
reg = < 380000 >;
compatible = "nvidia,tegra20-emc-table";
clock-frequency = < 380000 >;
nvidia,emc-registers = < 0x00000017 0x0000004b
0x00000012 0x00000006 0x00000004 0x00000005
0x00000003 0x0000000c 0x00000006 0x00000006
0x00000003 0x00000001 0x00000004 0x00000005
0x00000004 0x00000009 0x0000000d 0x00000b5f
0x00000000 0x00000003 0x00000003 0x00000006
0x00000006 0x00000001 0x00000011 0x000000c8
0x00000003 0x0000000e 0x00000007 0x0000000f
0x00000002 0x00000000 0x00000000 0x00000002
0x00000000 0x00000000 0x00000083 0xe044048b
0x007d8010 0x00000000 0x00000000 0x00000000
0x00000000 0x00000000 0x00000000 0x00000000 >;
};
};
};

View File

@ -26,6 +26,18 @@ i2c@7000d000 {
status = "disable";
};
i2s@70002800 {
status = "disable";
};
i2s@70002a00 {
status = "disable";
};
das@70000c00 {
status = "disable";
};
serial@70006000 {
clock-frequency = < 216000000 >;
};

View File

@ -12,6 +12,20 @@ memory {
i2c@7000c000 {
clock-frequency = <400000>;
wm8903: wm8903@1a {
compatible = "wlf,wm8903";
reg = <0x1a>;
interrupt-parent = <&gpio>;
interrupts = < 187 0x04 >;
gpio-controller;
#gpio-cells = <2>;
micdet-cfg = <0>;
micdet-delay = <100>;
gpio-cfg = < 0xffffffff 0xffffffff 0 0xffffffff 0xffffffff >;
};
};
i2c@7000c400 {
@ -26,6 +40,34 @@ i2c@7000d000 {
clock-frequency = <400000>;
};
i2s@70002a00 {
status = "disable";
};
sound {
compatible = "nvidia,tegra-audio-wm8903-ventana",
"nvidia,tegra-audio-wm8903";
nvidia,model = "NVIDIA Tegra Ventana";
nvidia,audio-routing =
"Headphone Jack", "HPOUTR",
"Headphone Jack", "HPOUTL",
"Int Spk", "ROP",
"Int Spk", "RON",
"Int Spk", "LOP",
"Int Spk", "LON",
"Mic Jack", "MICBIAS",
"IN1L", "Mic Jack";
nvidia,i2s-controller = <&tegra_i2s1>;
nvidia,audio-codec = <&wm8903>;
nvidia,spkr-en-gpios = <&wm8903 2 0>;
nvidia,hp-det-gpios = <&gpio 178 0>; /* gpio PW2 */
nvidia,int-mic-en-gpios = <&gpio 184 0>; /*gpio PX0 */
nvidia,ext-mic-en-gpios = <&gpio 185 0>; /* gpio PX1 */
};
serial@70006000 {
status = "disable";
};

View File

@ -4,6 +4,11 @@ / {
compatible = "nvidia,tegra20";
interrupt-parent = <&intc>;
pmc@7000f400 {
compatible = "nvidia,tegra20-pmc";
reg = <0x7000e400 0x400>;
};
intc: interrupt-controller@50041000 {
compatible = "arm,cortex-a9-gic";
interrupt-controller;
@ -12,6 +17,27 @@ intc: interrupt-controller@50041000 {
< 0x50040100 0x0100 >;
};
apbdma: dma@6000a000 {
compatible = "nvidia,tegra20-apbdma";
reg = <0x6000a000 0x1200>;
interrupts = < 0 104 0x04
0 105 0x04
0 106 0x04
0 107 0x04
0 108 0x04
0 109 0x04
0 110 0x04
0 111 0x04
0 112 0x04
0 113 0x04
0 114 0x04
0 115 0x04
0 116 0x04
0 117 0x04
0 118 0x04
0 119 0x04 >;
};
i2c@7000c000 {
#address-cells = <1>;
#size-cells = <0>;
@ -44,18 +70,18 @@ i2c@7000d000 {
interrupts = < 0 53 0x04 >;
};
i2s@70002800 {
tegra_i2s1: i2s@70002800 {
compatible = "nvidia,tegra20-i2s";
reg = <0x70002800 0x200>;
interrupts = < 0 13 0x04 >;
dma-channel = < 2 >;
nvidia,dma-request-selector = < &apbdma 2 >;
};
i2s@70002a00 {
tegra_i2s2: i2s@70002a00 {
compatible = "nvidia,tegra20-i2s";
reg = <0x70002a00 0x200>;
interrupts = < 0 3 0x04 >;
dma-channel = < 1 >;
nvidia,dma-request-selector = < &apbdma 1 >;
};
das@70000c00 {
@ -75,6 +101,8 @@ gpio: gpio@6000d000 {
0 89 0x04 >;
#gpio-cells = <2>;
gpio-controller;
#interrupt-cells = <2>;
interrupt-controller;
};
pinmux: pinmux@70000000 {
@ -120,6 +148,13 @@ serial@70006400 {
interrupts = < 0 91 0x04 >;
};
emc@7000f400 {
#address-cells = <1>;
#size-cells = <0>;
compatible = "nvidia,tegra20-emc";
reg = <0x7000f400 0x200>;
};
sdhci@c8000000 {
compatible = "nvidia,tegra20-sdhci";
reg = <0xc8000000 0x200>;

View File

@ -4,6 +4,11 @@ / {
compatible = "nvidia,tegra30";
interrupt-parent = <&intc>;
pmc@7000f400 {
compatible = "nvidia,tegra20-pmc", "nvidia,tegra30-pmc";
reg = <0x7000e400 0x400>;
};
intc: interrupt-controller@50041000 {
compatible = "arm,cortex-a9-gic";
interrupt-controller;
@ -12,6 +17,43 @@ intc: interrupt-controller@50041000 {
< 0x50040100 0x0100 >;
};
apbdma: dma@6000a000 {
compatible = "nvidia,tegra30-apbdma", "nvidia,tegra20-apbdma";
reg = <0x6000a000 0x1400>;
interrupts = < 0 104 0x04
0 105 0x04
0 106 0x04
0 107 0x04
0 108 0x04
0 109 0x04
0 110 0x04
0 111 0x04
0 112 0x04
0 113 0x04
0 114 0x04
0 115 0x04
0 116 0x04
0 117 0x04
0 118 0x04
0 119 0x04
0 128 0x04
0 129 0x04
0 130 0x04
0 131 0x04
0 132 0x04
0 133 0x04
0 134 0x04
0 135 0x04
0 136 0x04
0 137 0x04
0 138 0x04
0 139 0x04
0 140 0x04
0 141 0x04
0 142 0x04
0 143 0x04 >;
};
i2c@7000c000 {
#address-cells = <1>;
#size-cells = <0>;
@ -55,9 +97,18 @@ i2c@7000d000 {
gpio: gpio@6000d000 {
compatible = "nvidia,tegra30-gpio", "nvidia,tegra20-gpio";
reg = < 0x6000d000 0x1000 >;
interrupts = < 0 32 0x04 0 33 0x04 0 34 0x04 0 35 0x04 0 55 0x04 0 87 0x04 0 89 0x04 >;
interrupts = < 0 32 0x04
0 33 0x04
0 34 0x04
0 35 0x04
0 55 0x04
0 87 0x04
0 89 0x04
0 125 0x04 >;
#gpio-cells = <2>;
gpio-controller;
#interrupt-cells = <2>;
interrupt-controller;
};
serial@70006000 {

View File

@ -7,6 +7,7 @@ obj-y += clock.o
obj-y += timer.o
obj-y += pinmux.o
obj-y += fuse.o
obj-y += pmc.o
obj-$(CONFIG_ARCH_TEGRA_2x_SOC) += powergate.o
obj-$(CONFIG_ARCH_TEGRA_2x_SOC) += tegra2_clocks.o
obj-$(CONFIG_ARCH_TEGRA_2x_SOC) += tegra2_emc.o
@ -15,7 +16,7 @@ obj-$(CONFIG_ARCH_TEGRA_3x_SOC) += pinmux-tegra30-tables.o
obj-$(CONFIG_ARCH_TEGRA_3x_SOC) += board-dt-tegra30.o
obj-$(CONFIG_SMP) += platsmp.o localtimer.o headsmp.o
obj-$(CONFIG_HOTPLUG_CPU) += hotplug.o
obj-$(CONFIG_TEGRA_SYSTEM_DMA) += dma.o
obj-$(CONFIG_TEGRA_SYSTEM_DMA) += dma.o apbio.o
obj-$(CONFIG_CPU_FREQ) += cpu-tegra.o
obj-$(CONFIG_TEGRA_PCI) += pcie.o
obj-$(CONFIG_USB_SUPPORT) += usb_phy.o

145
arch/arm/mach-tegra/apbio.c Normal file
View File

@ -0,0 +1,145 @@
/*
* Copyright (C) 2010 NVIDIA Corporation.
* Copyright (C) 2010 Google, Inc.
*
* This software is licensed under the terms of the GNU General Public
* License version 2, as published by the Free Software Foundation, and
* may be copied, distributed, and modified under those terms.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
*/
#include <linux/kernel.h>
#include <linux/io.h>
#include <linux/dma-mapping.h>
#include <linux/spinlock.h>
#include <linux/completion.h>
#include <linux/sched.h>
#include <linux/mutex.h>
#include <mach/dma.h>
#include <mach/iomap.h>
#include "apbio.h"
static DEFINE_MUTEX(tegra_apb_dma_lock);
static struct tegra_dma_channel *tegra_apb_dma;
static u32 *tegra_apb_bb;
static dma_addr_t tegra_apb_bb_phys;
static DECLARE_COMPLETION(tegra_apb_wait);
bool tegra_apb_init(void)
{
struct tegra_dma_channel *ch;
mutex_lock(&tegra_apb_dma_lock);
/* Check to see if we raced to setup */
if (tegra_apb_dma)
goto out;
ch = tegra_dma_allocate_channel(TEGRA_DMA_MODE_ONESHOT |
TEGRA_DMA_SHARED);
if (!ch)
goto out_fail;
tegra_apb_bb = dma_alloc_coherent(NULL, sizeof(u32),
&tegra_apb_bb_phys, GFP_KERNEL);
if (!tegra_apb_bb) {
pr_err("%s: can not allocate bounce buffer\n", __func__);
tegra_dma_free_channel(ch);
goto out_fail;
}
tegra_apb_dma = ch;
out:
mutex_unlock(&tegra_apb_dma_lock);
return true;
out_fail:
mutex_unlock(&tegra_apb_dma_lock);
return false;
}
static void apb_dma_complete(struct tegra_dma_req *req)
{
complete(&tegra_apb_wait);
}
u32 tegra_apb_readl(unsigned long offset)
{
struct tegra_dma_req req;
int ret;
if (!tegra_apb_dma && !tegra_apb_init())
return readl(IO_TO_VIRT(offset));
mutex_lock(&tegra_apb_dma_lock);
req.complete = apb_dma_complete;
req.to_memory = 1;
req.dest_addr = tegra_apb_bb_phys;
req.dest_bus_width = 32;
req.dest_wrap = 1;
req.source_addr = offset;
req.source_bus_width = 32;
req.source_wrap = 4;
req.req_sel = TEGRA_DMA_REQ_SEL_CNTR;
req.size = 4;
INIT_COMPLETION(tegra_apb_wait);
tegra_dma_enqueue_req(tegra_apb_dma, &req);
ret = wait_for_completion_timeout(&tegra_apb_wait,
msecs_to_jiffies(50));
if (WARN(ret == 0, "apb read dma timed out")) {
tegra_dma_dequeue_req(tegra_apb_dma, &req);
*(u32 *)tegra_apb_bb = 0;
}
mutex_unlock(&tegra_apb_dma_lock);
return *((u32 *)tegra_apb_bb);
}
void tegra_apb_writel(u32 value, unsigned long offset)
{
struct tegra_dma_req req;
int ret;
if (!tegra_apb_dma && !tegra_apb_init()) {
writel(value, IO_TO_VIRT(offset));
return;
}
mutex_lock(&tegra_apb_dma_lock);
*((u32 *)tegra_apb_bb) = value;
req.complete = apb_dma_complete;
req.to_memory = 0;
req.dest_addr = offset;
req.dest_wrap = 4;
req.dest_bus_width = 32;
req.source_addr = tegra_apb_bb_phys;
req.source_bus_width = 32;
req.source_wrap = 1;
req.req_sel = TEGRA_DMA_REQ_SEL_CNTR;
req.size = 4;
INIT_COMPLETION(tegra_apb_wait);
tegra_dma_enqueue_req(tegra_apb_dma, &req);
ret = wait_for_completion_timeout(&tegra_apb_wait,
msecs_to_jiffies(50));
if (WARN(ret == 0, "apb write dma timed out"))
tegra_dma_dequeue_req(tegra_apb_dma, &req);
mutex_unlock(&tegra_apb_dma_lock);
}

View File

@ -0,0 +1,39 @@
/*
* Copyright (C) 2010 NVIDIA Corporation.
* Copyright (C) 2010 Google, Inc.
*
* This software is licensed under the terms of the GNU General Public
* License version 2, as published by the Free Software Foundation, and
* may be copied, distributed, and modified under those terms.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
*/
#ifndef __MACH_TEGRA_APBIO_H
#define __MACH_TEGRA_APBIO_H
#ifdef CONFIG_TEGRA_SYSTEM_DMA
u32 tegra_apb_readl(unsigned long offset);
void tegra_apb_writel(u32 value, unsigned long offset);
#else
#include <asm/io.h>
#include <mach/io.h>
static inline u32 tegra_apb_readl(unsigned long offset)
{
return readl(IO_TO_VIRT(offset));
}
static inline void tegra_apb_writel(u32 value, unsigned long offset)
{
writel(value, IO_TO_VIRT(offset));
}
#endif
#endif

View File

@ -18,18 +18,13 @@
#include <linux/i2c.h>
#include <linux/platform_device.h>
#include <linux/gpio.h>
#include <linux/io.h>
#include <linux/regulator/machine.h>
#include <linux/mfd/tps6586x.h>
#include <mach/iomap.h>
#include <mach/irqs.h>
#include "board-harmony.h"
#define PMC_CTRL 0x0
#define PMC_CTRL_INTR_LOW (1 << 17)
static struct regulator_consumer_supply tps658621_ldo0_supply[] = {
REGULATOR_SUPPLY("pex_clk", NULL),
};
@ -114,16 +109,6 @@ static struct i2c_board_info __initdata harmony_regulators[] = {
int __init harmony_regulator_init(void)
{
void __iomem *pmc = IO_ADDRESS(TEGRA_PMC_BASE);
u32 pmc_ctrl;
/*
* Configure the power management controller to trigger PMU
* interrupts when low
*/
pmc_ctrl = readl(pmc + PMC_CTRL);
writel(pmc_ctrl | PMC_CTRL_INTR_LOW, pmc + PMC_CTRL);
i2c_register_board_info(3, harmony_regulators, 1);
return 0;

View File

@ -101,7 +101,6 @@ static struct wm8903_platform_data harmony_wm8903_pdata = {
static struct i2c_board_info __initdata wm8903_board_info = {
I2C_BOARD_INFO("wm8903", 0x1a),
.platform_data = &harmony_wm8903_pdata,
.irq = TEGRA_GPIO_TO_IRQ(TEGRA_GPIO_CDC_IRQ),
};
static void __init harmony_i2c_init(void)
@ -111,6 +110,7 @@ static void __init harmony_i2c_init(void)
platform_device_register(&tegra_i2c_device3);
platform_device_register(&tegra_i2c_device4);
wm8903_board_info.irq = gpio_to_irq(TEGRA_GPIO_CDC_IRQ);
i2c_register_board_info(0, &wm8903_board_info, 1);
}

View File

@ -159,7 +159,6 @@ static struct platform_device *seaboard_devices[] __initdata = {
static struct i2c_board_info __initdata isl29018_device = {
I2C_BOARD_INFO("isl29018", 0x44),
.irq = TEGRA_GPIO_TO_IRQ(TEGRA_GPIO_ISL29018_IRQ),
};
static struct i2c_board_info __initdata adt7461_device = {
@ -183,7 +182,6 @@ static struct wm8903_platform_data wm8903_pdata = {
static struct i2c_board_info __initdata wm8903_device = {
I2C_BOARD_INFO("wm8903", 0x1a),
.platform_data = &wm8903_pdata,
.irq = TEGRA_GPIO_TO_IRQ(TEGRA_GPIO_CDC_IRQ),
};
static int seaboard_ehci_init(void)
@ -214,7 +212,10 @@ static void __init seaboard_i2c_init(void)
gpio_request(TEGRA_GPIO_ISL29018_IRQ, "isl29018");
gpio_direction_input(TEGRA_GPIO_ISL29018_IRQ);
isl29018_device.irq = gpio_to_irq(TEGRA_GPIO_ISL29018_IRQ);
i2c_register_board_info(0, &isl29018_device, 1);
wm8903_device.irq = gpio_to_irq(TEGRA_GPIO_CDC_IRQ);
i2c_register_board_info(0, &wm8903_device, 1);
i2c_register_board_info(3, &adt7461_device, 1);

View File

@ -31,6 +31,24 @@
#include "board.h"
#include "clock.h"
#include "fuse.h"
#include "pmc.h"
/*
* Storage for debug-macro.S's state.
*
* This must be in .data not .bss so that it gets initialized each time the
* kernel is loaded. The data is declared here rather than debug-macro.S so
* that multiple inclusions of debug-macro.S point at the same data.
*/
#define TEGRA_DEBUG_UART_OFFSET (TEGRA_DEBUG_UART_BASE & 0xFFFF)
u32 tegra_uart_config[3] = {
/* Debug UART initialization required */
1,
/* Debug UART physical address */
(u32)(IO_APB_PHYS + TEGRA_DEBUG_UART_OFFSET),
/* Debug UART virtual address */
(u32)(IO_APB_VIRT + TEGRA_DEBUG_UART_OFFSET),
};
#ifdef CONFIG_OF
static const struct of_device_id tegra_dt_irq_match[] __initconst = {
@ -101,11 +119,13 @@ void __init tegra20_init_early(void)
tegra2_init_clocks();
tegra_clk_init_from_table(tegra20_clk_init_table);
tegra_init_cache(0x331, 0x441);
tegra_pmc_init();
}
#endif
#ifdef CONFIG_ARCH_TEGRA_3x_SOC
void __init tegra30_init_early(void)
{
tegra_init_cache(0x441, 0x551);
tegra_pmc_init();
}
#endif

View File

@ -33,6 +33,8 @@
#include <mach/iomap.h>
#include <mach/suspend.h>
#include "apbio.h"
#define APB_DMA_GEN 0x000
#define GEN_ENABLE (1<<31)
@ -50,8 +52,6 @@
#define CSR_ONCE (1<<27)
#define CSR_FLOW (1<<21)
#define CSR_REQ_SEL_SHIFT 16
#define CSR_REQ_SEL_MASK (0x1F<<CSR_REQ_SEL_SHIFT)
#define CSR_REQ_SEL_INVALID (31<<CSR_REQ_SEL_SHIFT)
#define CSR_WCOUNT_SHIFT 2
#define CSR_WCOUNT_MASK 0xFFFC
@ -133,6 +133,7 @@ struct tegra_dma_channel {
static bool tegra_dma_initialized;
static DEFINE_MUTEX(tegra_dma_lock);
static DEFINE_SPINLOCK(enable_lock);
static DECLARE_BITMAP(channel_usage, NV_DMA_MAX_CHANNELS);
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)
static int tegra_dma_cancel(struct tegra_dma_channel *ch)
{
u32 csr;
unsigned long irq_flags;
spin_lock_irqsave(&ch->lock, irq_flags);
while (!list_empty(&ch->list))
list_del(ch->list.next);
csr = readl(ch->addr + APB_DMA_CHAN_CSR);
csr &= ~CSR_REQ_SEL_MASK;
csr |= CSR_REQ_SEL_INVALID;
writel(csr, ch->addr + APB_DMA_CHAN_CSR);
tegra_dma_stop(ch);
spin_unlock_irqrestore(&ch->lock, irq_flags);
return 0;
}
static unsigned int get_channel_status(struct tegra_dma_channel *ch,
struct tegra_dma_req *req, bool is_stop_dma)
{
void __iomem *addr = IO_ADDRESS(TEGRA_APB_DMA_BASE);
unsigned int status;
if (is_stop_dma) {
/*
* STOP the DMA and get the transfer count.
* Getting the transfer count is tricky.
* - Globally disable DMA on all channels
* - Read the channel's status register to know the number
* of pending bytes to be transfered.
* - Stop the dma channel
* - Globally re-enable DMA to resume other transfers
*/
spin_lock(&enable_lock);
writel(0, addr + APB_DMA_GEN);
udelay(20);
status = readl(ch->addr + APB_DMA_CHAN_STA);
tegra_dma_stop(ch);
writel(GEN_ENABLE, addr + APB_DMA_GEN);
spin_unlock(&enable_lock);
if (status & STA_ISE_EOC) {
pr_err("Got Dma Int here clearing");
writel(status, ch->addr + APB_DMA_CHAN_STA);
}
req->status = TEGRA_DMA_REQ_ERROR_ABORTED;
} else {
status = readl(ch->addr + APB_DMA_CHAN_STA);
}
return status;
}
/* should be called with the channel lock held */
static unsigned int dma_active_count(struct tegra_dma_channel *ch,
struct tegra_dma_req *req, unsigned int status)
{
unsigned int to_transfer;
unsigned int req_transfer_count;
unsigned int bytes_transferred;
to_transfer = ((status & STA_COUNT_MASK) >> STA_COUNT_SHIFT) + 1;
req_transfer_count = ch->req_transfer_count + 1;
bytes_transferred = req_transfer_count;
if (status & STA_BUSY)
bytes_transferred -= to_transfer;
/*
* In continuous transfer mode, DMA only tracks the count of the
* half DMA buffer. So, if the DMA already finished half the DMA
* then add the half buffer to the completed count.
*/
if (ch->mode & TEGRA_DMA_MODE_CONTINOUS) {
if (req->buffer_status == TEGRA_DMA_REQ_BUF_STATUS_HALF_FULL)
bytes_transferred += req_transfer_count;
if (status & STA_ISE_EOC)
bytes_transferred += req_transfer_count;
}
bytes_transferred *= 4;
return bytes_transferred;
}
int tegra_dma_dequeue_req(struct tegra_dma_channel *ch,
struct tegra_dma_req *_req)
{
unsigned int csr;
unsigned int status;
struct tegra_dma_req *req = NULL;
int found = 0;
unsigned long irq_flags;
int to_transfer;
int req_transfer_count;
int stop = 0;
spin_lock_irqsave(&ch->lock, irq_flags);
if (list_entry(ch->list.next, struct tegra_dma_req, node) == _req)
stop = 1;
list_for_each_entry(req, &ch->list, node) {
if (req == _req) {
list_del(&req->node);
@ -222,47 +281,12 @@ int tegra_dma_dequeue_req(struct tegra_dma_channel *ch,
return 0;
}
/* STOP the DMA and get the transfer count.
* Getting the transfer count is tricky.
* - Change the source selector to invalid to stop the DMA from
* FIFO to memory.
* - Read the status register to know the number of pending
* bytes to be transferred.
* - Finally stop or program the DMA to the next buffer in the
* list.
*/
csr = readl(ch->addr + APB_DMA_CHAN_CSR);
csr &= ~CSR_REQ_SEL_MASK;
csr |= CSR_REQ_SEL_INVALID;
writel(csr, ch->addr + APB_DMA_CHAN_CSR);
if (!stop)
goto skip_stop_dma;
/* Get the transfer count */
status = readl(ch->addr + APB_DMA_CHAN_STA);
to_transfer = (status & STA_COUNT_MASK) >> STA_COUNT_SHIFT;
req_transfer_count = ch->req_transfer_count;
req_transfer_count += 1;
to_transfer += 1;
status = get_channel_status(ch, req, true);
req->bytes_transferred = dma_active_count(ch, req, status);
req->bytes_transferred = req_transfer_count;
if (status & STA_BUSY)
req->bytes_transferred -= to_transfer;
/* In continuous transfer mode, DMA only tracks the count of the
* half DMA buffer. So, if the DMA already finished half the DMA
* then add the half buffer to the completed count.
*
* FIXME: There can be a race here. What if the req to
* dequue happens at the same time as the DMA just moved to
* the new buffer and SW didn't yet received the interrupt?
*/
if (ch->mode & TEGRA_DMA_MODE_CONTINOUS)
if (req->buffer_status == TEGRA_DMA_REQ_BUF_STATUS_HALF_FULL)
req->bytes_transferred += req_transfer_count;
req->bytes_transferred *= 4;
tegra_dma_stop(ch);
if (!list_empty(&ch->list)) {
/* if the list is not empty, queue the next request */
struct tegra_dma_req *next_req;
@ -270,6 +294,8 @@ int tegra_dma_dequeue_req(struct tegra_dma_channel *ch,
typeof(*next_req), node);
tegra_dma_update_hw(ch, next_req);
}
skip_stop_dma:
req->status = -TEGRA_DMA_REQ_ERROR_ABORTED;
spin_unlock_irqrestore(&ch->lock, irq_flags);
@ -357,7 +383,7 @@ struct tegra_dma_channel *tegra_dma_allocate_channel(int mode)
int channel;
struct tegra_dma_channel *ch = NULL;
if (WARN_ON(!tegra_dma_initialized))
if (!tegra_dma_initialized)
return NULL;
mutex_lock(&tegra_dma_lock);

View File

@ -23,20 +23,70 @@
#include <mach/iomap.h>
#include "fuse.h"
#include "apbio.h"
#define FUSE_UID_LOW 0x108
#define FUSE_UID_HIGH 0x10c
#define FUSE_SKU_INFO 0x110
#define FUSE_SPARE_BIT 0x200
static inline u32 fuse_readl(unsigned long offset)
int tegra_sku_id;
int tegra_cpu_process_id;
int tegra_core_process_id;
enum tegra_revision tegra_revision;
/* The BCT to use at boot is specified by board straps that can be read
* through a APB misc register and decoded. 2 bits, i.e. 4 possible BCTs.
*/
int tegra_bct_strapping;
#define STRAP_OPT 0x008
#define GMI_AD0 (1 << 4)
#define GMI_AD1 (1 << 5)
#define RAM_ID_MASK (GMI_AD0 | GMI_AD1)
#define RAM_CODE_SHIFT 4
static const char *tegra_revision_name[TEGRA_REVISION_MAX] = {
[TEGRA_REVISION_UNKNOWN] = "unknown",
[TEGRA_REVISION_A01] = "A01",
[TEGRA_REVISION_A02] = "A02",
[TEGRA_REVISION_A03] = "A03",
[TEGRA_REVISION_A03p] = "A03 prime",
[TEGRA_REVISION_A04] = "A04",
};
static inline u32 tegra_fuse_readl(unsigned long offset)
{
return readl(IO_TO_VIRT(TEGRA_FUSE_BASE + offset));
return tegra_apb_readl(TEGRA_FUSE_BASE + offset);
}
static inline void fuse_writel(u32 value, unsigned long offset)
static inline bool get_spare_fuse(int bit)
{
writel(value, IO_TO_VIRT(TEGRA_FUSE_BASE + offset));
return tegra_fuse_readl(FUSE_SPARE_BIT + bit * 4);
}
static enum tegra_revision tegra_get_revision(void)
{
void __iomem *chip_id = IO_ADDRESS(TEGRA_APB_MISC_BASE) + 0x804;
u32 id = readl(chip_id);
u32 minor_rev = (id >> 16) & 0xf;
u32 chipid = (id >> 8) & 0xff;
switch (minor_rev) {
case 1:
return TEGRA_REVISION_A01;
case 2:
return TEGRA_REVISION_A02;
case 3:
if (chipid == 0x20 && (get_spare_fuse(18) || get_spare_fuse(19)))
return TEGRA_REVISION_A03p;
else
return TEGRA_REVISION_A03;
case 4:
return TEGRA_REVISION_A04;
default:
return TEGRA_REVISION_UNKNOWN;
}
}
void tegra_init_fuse(void)
@ -45,40 +95,31 @@ void tegra_init_fuse(void)
reg |= 1 << 28;
writel(reg, IO_TO_VIRT(TEGRA_CLK_RESET_BASE + 0x48));
pr_info("Tegra SKU: %d CPU Process: %d Core Process: %d\n",
tegra_sku_id(), tegra_cpu_process_id(),
tegra_core_process_id());
reg = tegra_fuse_readl(FUSE_SKU_INFO);
tegra_sku_id = reg & 0xFF;
reg = tegra_fuse_readl(FUSE_SPARE_BIT);
tegra_cpu_process_id = (reg >> 6) & 3;
reg = tegra_fuse_readl(FUSE_SPARE_BIT);
tegra_core_process_id = (reg >> 12) & 3;
reg = tegra_apb_readl(TEGRA_APB_MISC_BASE + STRAP_OPT);
tegra_bct_strapping = (reg & RAM_ID_MASK) >> RAM_CODE_SHIFT;
tegra_revision = tegra_get_revision();
pr_info("Tegra Revision: %s SKU: %d CPU Process: %d Core Process: %d\n",
tegra_revision_name[tegra_get_revision()],
tegra_sku_id, tegra_cpu_process_id,
tegra_core_process_id);
}
unsigned long long tegra_chip_uid(void)
{
unsigned long long lo, hi;
lo = fuse_readl(FUSE_UID_LOW);
hi = fuse_readl(FUSE_UID_HIGH);
lo = tegra_fuse_readl(FUSE_UID_LOW);
hi = tegra_fuse_readl(FUSE_UID_HIGH);
return (hi << 32ull) | lo;
}
int tegra_sku_id(void)
{
int sku_id;
u32 reg = fuse_readl(FUSE_SKU_INFO);
sku_id = reg & 0xFF;
return sku_id;
}
int tegra_cpu_process_id(void)
{
int cpu_process_id;
u32 reg = fuse_readl(FUSE_SPARE_BIT);
cpu_process_id = (reg >> 6) & 3;
return cpu_process_id;
}
int tegra_core_process_id(void)
{
int core_process_id;
u32 reg = fuse_readl(FUSE_SPARE_BIT);
core_process_id = (reg >> 12) & 3;
return core_process_id;
}

View File

@ -1,6 +1,4 @@
/*
* arch/arm/mach-tegra/fuse.c
*
* Copyright (C) 2010 Google, Inc.
*
* Author:
@ -17,8 +15,34 @@
*
*/
#ifndef __MACH_TEGRA_FUSE_H
#define __MACH_TEGRA_FUSE_H
enum tegra_revision {
TEGRA_REVISION_UNKNOWN = 0,
TEGRA_REVISION_A01,
TEGRA_REVISION_A02,
TEGRA_REVISION_A03,
TEGRA_REVISION_A03p,
TEGRA_REVISION_A04,
TEGRA_REVISION_MAX,
};
#define SKU_ID_T20 8
#define SKU_ID_T25SE 20
#define SKU_ID_AP25 23
#define SKU_ID_T25 24
#define SKU_ID_AP25E 27
#define SKU_ID_T25E 28
extern int tegra_sku_id;
extern int tegra_cpu_process_id;
extern int tegra_core_process_id;
extern enum tegra_revision tegra_revision;
extern int tegra_bct_strapping;
unsigned long long tegra_chip_uid(void);
int tegra_sku_id(void);
int tegra_cpu_process_id(void);
int tegra_core_process_id(void);
void tegra_init_fuse(void);
#endif

View File

@ -1,11 +1,17 @@
/*
* arch/arm/mach-tegra/include/mach/debug-macro.S
*
* Copyright (C) 2010 Google, Inc.
* Copyright (C) 2010,2011 Google, Inc.
* Copyright (C) 2011-2012 NVIDIA CORPORATION. All Rights Reserved.
*
* Author:
* Colin Cross <ccross@google.com>
* Erik Gilling <konkers@google.com>
* Doug Anderson <dianders@chromium.org>
* Stephen Warren <swarren@nvidia.com>
*
* Portions based on mach-omap2's debug-macro.S
* Copyright (C) 1994-1999 Russell King
*
* This software is licensed under the terms of the GNU General Public
* License version 2, as published by the Free Software Foundation, and
@ -18,18 +24,78 @@
*
*/
#include <linux/serial_reg.h>
#include <mach/io.h>
#include <mach/iomap.h>
#include <mach/irammap.h>
.macro addruart, rp, rv, tmp
ldr \rp, =IO_APB_PHYS @ physical
ldr \rv, =IO_APB_VIRT @ virtual
orr \rp, \rp, #(TEGRA_DEBUG_UART_BASE & 0xFF)
orr \rp, \rp, #(TEGRA_DEBUG_UART_BASE & 0xFF00)
orr \rv, \rv, #(TEGRA_DEBUG_UART_BASE & 0xFF)
orr \rv, \rv, #(TEGRA_DEBUG_UART_BASE & 0xFF00)
.endm
.macro addruart, rp, rv, tmp
adr \rp, 99f @ actual addr of 99f
ldr \rv, [\rp] @ linked addr is stored there
sub \rv, \rv, \rp @ offset between the two
ldr \rp, [\rp, #4] @ linked tegra_uart_config
sub \tmp, \rp, \rv @ actual tegra_uart_config
ldr \rp, [\tmp] @ Load tegra_uart_config
cmp \rp, #1 @ needs intitialization?
bne 100f @ no; go load the addresses
mov \rv, #0 @ yes; record init is done
str \rv, [\tmp]
mov \rp, #TEGRA_IRAM_BASE @ See if cookie is in IRAM
ldr \rv, [\rp, #TEGRA_IRAM_DEBUG_UART_OFFSET]
movw \rp, #TEGRA_IRAM_DEBUG_UART_COOKIE & 0xffff
movt \rp, #TEGRA_IRAM_DEBUG_UART_COOKIE >> 16
cmp \rv, \rp @ Cookie present?
bne 100f @ No, use default UART
mov \rp, #TEGRA_IRAM_BASE @ Load UART address from IRAM
ldr \rv, [\rp, #TEGRA_IRAM_DEBUG_UART_OFFSET + 4]
str \rv, [\tmp, #4] @ Store in tegra_uart_phys
sub \rv, \rv, #IO_APB_PHYS @ Calculate virt address
add \rv, \rv, #IO_APB_VIRT
str \rv, [\tmp, #8] @ Store in tegra_uart_virt
b 100f
#define UART_SHIFT 2
#include <asm/hardware/debug-8250.S>
.align
99: .word .
.word tegra_uart_config
.ltorg
100: ldr \rp, [\tmp, #4] @ Load tegra_uart_phys
ldr \rv, [\tmp, #8] @ Load tegra_uart_virt
.endm
#define UART_SHIFT 2
/*
* Code below is swiped from <asm/hardware/debug-8250.S>, but add an extra
* check to make sure that we aren't in the CONFIG_TEGRA_DEBUG_UART_NONE case.
* We use the fact that all 5 valid UART addresses all have something in the
* 2nd-to-lowest byte.
*/
.macro senduart, rd, rx
tst \rx, #0x0000ff00
strneb \rd, [\rx, #UART_TX << UART_SHIFT]
1001:
.endm
.macro busyuart, rd, rx
tst \rx, #0x0000ff00
beq 1002f
1001: ldrb \rd, [\rx, #UART_LSR << UART_SHIFT]
and \rd, \rd, #UART_LSR_TEMT | UART_LSR_THRE
teq \rd, #UART_LSR_TEMT | UART_LSR_THRE
bne 1001b
1002:
.endm
.macro waituart, rd, rx
#ifdef FLOW_CONTROL
tst \rx, #0x0000ff00
beq 1002f
1001: ldrb \rd, [\rx, #UART_MSR << UART_SHIFT]
tst \rd, #UART_MSR_CTS
beq 1001b
1002:
#endif
.endm

View File

@ -25,8 +25,6 @@
#define TEGRA_NR_GPIOS INT_GPIO_NR
#define TEGRA_GPIO_TO_IRQ(gpio) (INT_GPIO_BASE + (gpio))
struct tegra_gpio_table {
int gpio; /* GPIO number */
bool enable; /* Enable for GPIO at init? */

View File

@ -0,0 +1,35 @@
/*
* Copyright (c) 2012, NVIDIA CORPORATION. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef __MACH_TEGRA_IRAMMAP_H
#define __MACH_TEGRA_IRAMMAP_H
#include <asm/sizes.h>
/* The first 1K of IRAM is permanently reserved for the CPU reset handler */
#define TEGRA_IRAM_RESET_HANDLER_OFFSET 0
#define TEGRA_IRAM_RESET_HANDLER_SIZE SZ_1K
/*
* These locations are written to by uncompress.h, and read by debug-macro.S.
* The first word holds the cookie value if the data is valid. The second
* word holds the UART physical address.
*/
#define TEGRA_IRAM_DEBUG_UART_OFFSET SZ_1K
#define TEGRA_IRAM_DEBUG_UART_SIZE 8
#define TEGRA_IRAM_DEBUG_UART_COOKIE 0x55415254
#endif

View File

@ -2,10 +2,14 @@
* arch/arm/mach-tegra/include/mach/uncompress.h
*
* Copyright (C) 2010 Google, Inc.
* Copyright (C) 2011 Google, Inc.
* Copyright (C) 2011-2012 NVIDIA CORPORATION. All Rights Reserved.
*
* Author:
* Colin Cross <ccross@google.com>
* Erik Gilling <konkers@google.com>
* Doug Anderson <dianders@chromium.org>
* Stephen Warren <swarren@nvidia.com>
*
* This software is licensed under the terms of the GNU General Public
* License version 2, as published by the Free Software Foundation, and
@ -25,36 +29,130 @@
#include <linux/serial_reg.h>
#include <mach/iomap.h>
#include <mach/irammap.h>
#define BIT(x) (1 << (x))
#define ARRAY_SIZE(arr) (sizeof(arr) / sizeof((arr)[0]))
#define DEBUG_UART_SHIFT 2
volatile u8 *uart;
static void putc(int c)
{
volatile u8 *uart = (volatile u8 *)TEGRA_DEBUG_UART_BASE;
int shift = 2;
if (uart == NULL)
return;
while (!(uart[UART_LSR << shift] & UART_LSR_THRE))
while (!(uart[UART_LSR << DEBUG_UART_SHIFT] & UART_LSR_THRE))
barrier();
uart[UART_TX << shift] = c;
uart[UART_TX << DEBUG_UART_SHIFT] = c;
}
static inline void flush(void)
{
}
static inline void save_uart_address(void)
{
u32 *buf = (u32 *)(TEGRA_IRAM_BASE + TEGRA_IRAM_DEBUG_UART_OFFSET);
if (uart) {
buf[0] = TEGRA_IRAM_DEBUG_UART_COOKIE;
buf[1] = (u32)uart;
} else
buf[0] = 0;
}
/*
* Setup before decompression. This is where we do UART selection for
* earlyprintk and init the uart_base register.
*/
static inline void arch_decomp_setup(void)
{
volatile u8 *uart = (volatile u8 *)TEGRA_DEBUG_UART_BASE;
int shift = 2;
static const struct {
u32 base;
u32 reset_reg;
u32 clock_reg;
u32 bit;
} uarts[] = {
{
TEGRA_UARTA_BASE,
TEGRA_CLK_RESET_BASE + 0x04,
TEGRA_CLK_RESET_BASE + 0x10,
6,
},
{
TEGRA_UARTB_BASE,
TEGRA_CLK_RESET_BASE + 0x04,
TEGRA_CLK_RESET_BASE + 0x10,
7,
},
{
TEGRA_UARTC_BASE,
TEGRA_CLK_RESET_BASE + 0x08,
TEGRA_CLK_RESET_BASE + 0x14,
23,
},
{
TEGRA_UARTD_BASE,
TEGRA_CLK_RESET_BASE + 0x0c,
TEGRA_CLK_RESET_BASE + 0x18,
1,
},
{
TEGRA_UARTE_BASE,
TEGRA_CLK_RESET_BASE + 0x0c,
TEGRA_CLK_RESET_BASE + 0x18,
2,
},
};
int i;
volatile u32 *apb_misc = (volatile u32 *)TEGRA_APB_MISC_BASE;
u32 chip, div;
/*
* Look for the first UART that:
* a) Is not in reset.
* b) Is clocked.
* c) Has a 'D' in the scratchpad register.
*
* Note that on Tegra30, the first two conditions are required, since
* if not true, accesses to the UART scratch register will hang.
* Tegra20 doesn't have this issue.
*
* The intent is that the bootloader will tell the kernel which UART
* to use by setting up those conditions. If nothing found, we'll fall
* back to what's specified in TEGRA_DEBUG_UART_BASE.
*/
for (i = 0; i < ARRAY_SIZE(uarts); i++) {
if (*(u8 *)uarts[i].reset_reg & BIT(uarts[i].bit))
continue;
if (!(*(u8 *)uarts[i].clock_reg & BIT(uarts[i].bit)))
continue;
uart = (volatile u8 *)uarts[i].base;
if (uart[UART_SCR << DEBUG_UART_SHIFT] != 'D')
continue;
break;
}
if (i == ARRAY_SIZE(uarts))
uart = (volatile u8 *)TEGRA_DEBUG_UART_BASE;
save_uart_address();
if (uart == NULL)
return;
uart[UART_LCR << shift] |= UART_LCR_DLAB;
uart[UART_DLL << shift] = 0x75;
uart[UART_DLM << shift] = 0x0;
uart[UART_LCR << shift] = 3;
chip = (apb_misc[0x804 / 4] >> 8) & 0xff;
if (chip == 0x20)
div = 0x0075;
else
div = 0x00dd;
uart[UART_LCR << DEBUG_UART_SHIFT] |= UART_LCR_DLAB;
uart[UART_DLL << DEBUG_UART_SHIFT] = div & 0xff;
uart[UART_DLM << DEBUG_UART_SHIFT] = div >> 8;
uart[UART_LCR << DEBUG_UART_SHIFT] = 3;
}
static inline void arch_decomp_wdog(void)

76
arch/arm/mach-tegra/pmc.c Normal file
View File

@ -0,0 +1,76 @@
/*
* Copyright (C) 2012 NVIDIA CORPORATION. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
#include <linux/kernel.h>
#include <linux/io.h>
#include <linux/of.h>
#include <mach/iomap.h>
#define PMC_CTRL 0x0
#define PMC_CTRL_INTR_LOW (1 << 17)
static inline u32 tegra_pmc_readl(u32 reg)
{
return readl(IO_ADDRESS(TEGRA_PMC_BASE + reg));
}
static inline void tegra_pmc_writel(u32 val, u32 reg)
{
writel(val, IO_ADDRESS(TEGRA_PMC_BASE + reg));
}
#ifdef CONFIG_OF
static const struct of_device_id matches[] __initconst = {
{ .compatible = "nvidia,tegra20-pmc" },
{ }
};
#endif
void __init tegra_pmc_init(void)
{
/*
* For now, Harmony is the only board that uses the PMC, and it wants
* the signal inverted. Seaboard would too if it used the PMC.
* Hopefully by the time other boards want to use the PMC, everything
* will be device-tree, or they also want it inverted.
*/
bool invert_interrupt = true;
u32 val;
#ifdef CONFIG_OF
if (of_have_populated_dt()) {
struct device_node *np;
invert_interrupt = false;
np = of_find_matching_node(NULL, matches);
if (np) {
if (of_find_property(np, "nvidia,invert-interrupt",
NULL))
invert_interrupt = true;
}
}
#endif
val = tegra_pmc_readl(PMC_CTRL);
if (invert_interrupt)
val |= PMC_CTRL_INTR_LOW;
else
val &= ~PMC_CTRL_INTR_LOW;
tegra_pmc_writel(val, PMC_CTRL);
}

23
arch/arm/mach-tegra/pmc.h Normal file
View File

@ -0,0 +1,23 @@
/*
* Copyright (C) 2012 NVIDIA CORPORATION. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
#ifndef __MACH_TEGRA_PMC_H
#define __MACH_TEGRA_PMC_H
void tegra_pmc_init(void);
#endif

View File

@ -720,7 +720,7 @@ static void tegra2_pllx_clk_init(struct clk *c)
{
tegra2_pll_clk_init(c);
if (tegra_sku_id() == 7)
if (tegra_sku_id == 7)
c->max_rate = 750000000;
}

View File

@ -16,14 +16,19 @@
*/
#include <linux/kernel.h>
#include <linux/device.h>
#include <linux/clk.h>
#include <linux/err.h>
#include <linux/io.h>
#include <linux/module.h>
#include <linux/of.h>
#include <linux/platform_device.h>
#include <linux/platform_data/tegra_emc.h>
#include <mach/iomap.h>
#include "tegra2_emc.h"
#include "fuse.h"
#ifdef CONFIG_TEGRA_EMC_SCALING_ENABLE
static bool emc_enable = true;
@ -32,18 +37,17 @@ static bool emc_enable;
#endif
module_param(emc_enable, bool, 0644);
static void __iomem *emc = IO_ADDRESS(TEGRA_EMC_BASE);
static const struct tegra_emc_table *tegra_emc_table;
static int tegra_emc_table_size;
static struct platform_device *emc_pdev;
static void __iomem *emc_regbase;
static inline void emc_writel(u32 val, unsigned long addr)
{
writel(val, emc + addr);
writel(val, emc_regbase + addr);
}
static inline u32 emc_readl(unsigned long addr)
{
return readl(emc + addr);
return readl(emc_regbase + addr);
}
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] = {
/* Select the closest EMC rate that is higher than the requested rate */
long tegra_emc_round_rate(unsigned long rate)
{
struct tegra_emc_pdata *pdata;
int i;
int best = -1;
unsigned long distance = ULONG_MAX;
if (!tegra_emc_table)
if (!emc_pdev)
return -EINVAL;
if (!emc_enable)
return -EINVAL;
pdata = emc_pdev->dev.platform_data;
pr_debug("%s: %lu\n", __func__, rate);
@ -116,10 +120,10 @@ long tegra_emc_round_rate(unsigned long rate)
*/
rate = rate / 2 / 1000;
for (i = 0; i < tegra_emc_table_size; i++) {
if (tegra_emc_table[i].rate >= rate &&
(tegra_emc_table[i].rate - rate) < distance) {
distance = tegra_emc_table[i].rate - rate;
for (i = 0; i < pdata->num_tables; i++) {
if (pdata->tables[i].rate >= rate &&
(pdata->tables[i].rate - rate) < distance) {
distance = pdata->tables[i].rate - rate;
best = i;
}
}
@ -127,9 +131,9 @@ long tegra_emc_round_rate(unsigned long rate)
if (best < 0)
return -EINVAL;
pr_debug("%s: using %lu\n", __func__, tegra_emc_table[best].rate);
pr_debug("%s: using %lu\n", __func__, pdata->tables[best].rate);
return tegra_emc_table[best].rate * 2 * 1000;
return pdata->tables[best].rate * 2 * 1000;
}
/*
@ -142,37 +146,211 @@ long tegra_emc_round_rate(unsigned long rate)
*/
int tegra_emc_set_rate(unsigned long rate)
{
struct tegra_emc_pdata *pdata;
int i;
int j;
if (!tegra_emc_table)
if (!emc_pdev)
return -EINVAL;
pdata = emc_pdev->dev.platform_data;
/*
* The EMC clock rate is twice the bus rate, and the bus rate is
* measured in kHz
*/
rate = rate / 2 / 1000;
for (i = 0; i < tegra_emc_table_size; i++)
if (tegra_emc_table[i].rate == rate)
for (i = 0; i < pdata->num_tables; i++)
if (pdata->tables[i].rate == rate)
break;
if (i >= tegra_emc_table_size)
if (i >= pdata->num_tables)
return -EINVAL;
pr_debug("%s: setting to %lu\n", __func__, rate);
for (j = 0; j < TEGRA_EMC_NUM_REGS; j++)
emc_writel(tegra_emc_table[i].regs[j], emc_reg_addr[j]);
emc_writel(pdata->tables[i].regs[j], emc_reg_addr[j]);
emc_readl(tegra_emc_table[i].regs[TEGRA_EMC_NUM_REGS - 1]);
emc_readl(pdata->tables[i].regs[TEGRA_EMC_NUM_REGS - 1]);
return 0;
}
void tegra_init_emc(const struct tegra_emc_table *table, int table_size)
#ifdef CONFIG_OF
static struct device_node *tegra_emc_ramcode_devnode(struct device_node *np)
{
tegra_emc_table = table;
tegra_emc_table_size = table_size;
struct device_node *iter;
u32 reg;
for_each_child_of_node(np, iter) {
if (of_property_read_u32(np, "nvidia,ram-code", &reg))
continue;
if (reg == tegra_bct_strapping)
return of_node_get(iter);
}
return NULL;
}
static struct tegra_emc_pdata *tegra_emc_dt_parse_pdata(
struct platform_device *pdev)
{
struct device_node *np = pdev->dev.of_node;
struct device_node *tnp, *iter;
struct tegra_emc_pdata *pdata;
int ret, i, num_tables;
if (!np)
return NULL;
if (of_find_property(np, "nvidia,use-ram-code", NULL)) {
tnp = tegra_emc_ramcode_devnode(np);
if (!tnp)
dev_warn(&pdev->dev,
"can't find emc table for ram-code 0x%02x\n",
tegra_bct_strapping);
} else
tnp = of_node_get(np);
if (!tnp)
return NULL;
num_tables = 0;
for_each_child_of_node(tnp, iter)
if (of_device_is_compatible(iter, "nvidia,tegra20-emc-table"))
num_tables++;
if (!num_tables) {
pdata = NULL;
goto out;
}
pdata = devm_kzalloc(&pdev->dev, sizeof(*pdata), GFP_KERNEL);
pdata->tables = devm_kzalloc(&pdev->dev,
sizeof(*pdata->tables) * num_tables,
GFP_KERNEL);
i = 0;
for_each_child_of_node(tnp, iter) {
u32 prop;
ret = of_property_read_u32(iter, "clock-frequency", &prop);
if (ret) {
dev_err(&pdev->dev, "no clock-frequency in %s\n",
iter->full_name);
continue;
}
pdata->tables[i].rate = prop;
ret = of_property_read_u32_array(iter, "nvidia,emc-registers",
pdata->tables[i].regs,
TEGRA_EMC_NUM_REGS);
if (ret) {
dev_err(&pdev->dev,
"malformed emc-registers property in %s\n",
iter->full_name);
continue;
}
i++;
}
pdata->num_tables = i;
out:
of_node_put(tnp);
return pdata;
}
#else
static struct tegra_emc_pdata *tegra_emc_dt_parse_pdata(
struct platform_device *pdev)
{
return NULL;
}
#endif
static struct tegra_emc_pdata __devinit *tegra_emc_fill_pdata(struct platform_device *pdev)
{
struct clk *c = clk_get_sys(NULL, "emc");
struct tegra_emc_pdata *pdata;
unsigned long khz;
int i;
WARN_ON(pdev->dev.platform_data);
BUG_ON(IS_ERR_OR_NULL(c));
pdata = devm_kzalloc(&pdev->dev, sizeof(*pdata), GFP_KERNEL);
pdata->tables = devm_kzalloc(&pdev->dev, sizeof(*pdata->tables),
GFP_KERNEL);
pdata->tables[0].rate = clk_get_rate(c) / 2 / 1000;
for (i = 0; i < TEGRA_EMC_NUM_REGS; i++)
pdata->tables[0].regs[i] = emc_readl(emc_reg_addr[i]);
pdata->num_tables = 1;
khz = pdata->tables[0].rate;
dev_info(&pdev->dev, "no tables provided, using %ld kHz emc, "
"%ld kHz mem\n", khz * 2, khz);
return pdata;
}
static int __devinit tegra_emc_probe(struct platform_device *pdev)
{
struct tegra_emc_pdata *pdata;
struct resource *res;
if (!emc_enable) {
dev_err(&pdev->dev, "disabled per module parameter\n");
return -ENODEV;
}
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
if (!res) {
dev_err(&pdev->dev, "missing register base\n");
return -ENOMEM;
}
emc_regbase = devm_request_and_ioremap(&pdev->dev, res);
if (!emc_regbase) {
dev_err(&pdev->dev, "failed to remap registers\n");
return -ENOMEM;
}
pdata = pdev->dev.platform_data;
if (!pdata)
pdata = tegra_emc_dt_parse_pdata(pdev);
if (!pdata)
pdata = tegra_emc_fill_pdata(pdev);
pdev->dev.platform_data = pdata;
emc_pdev = pdev;
return 0;
}
static struct of_device_id tegra_emc_of_match[] __devinitdata = {
{ .compatible = "nvidia,tegra20-emc", },
{ },
};
static struct platform_driver tegra_emc_driver = {
.driver = {
.name = "tegra-emc",
.owner = THIS_MODULE,
.of_match_table = tegra_emc_of_match,
},
.probe = tegra_emc_probe,
};
static int __init tegra_emc_init(void)
{
return platform_driver_register(&tegra_emc_driver);
}
device_initcall(tegra_emc_init);

View File

@ -15,13 +15,10 @@
*
*/
#define TEGRA_EMC_NUM_REGS 46
struct tegra_emc_table {
unsigned long rate;
u32 regs[TEGRA_EMC_NUM_REGS];
};
#ifndef __MACH_TEGRA_TEGRA2_EMC_H_
#define __MACH_TEGRA_TEGRA2_EMC_H
int tegra_emc_set_rate(unsigned long rate);
long tegra_emc_round_rate(unsigned long rate);
void tegra_init_emc(const struct tegra_emc_table *table, int table_size);
#endif

View File

@ -25,6 +25,7 @@
#include <linux/of.h>
#include <linux/platform_device.h>
#include <linux/module.h>
#include <linux/irqdomain.h>
#include <asm/mach/irq.h>
@ -74,9 +75,10 @@ struct tegra_gpio_bank {
#endif
};
static struct irq_domain *irq_domain;
static void __iomem *regs;
static struct tegra_gpio_bank tegra_gpio_banks[7];
static u32 tegra_gpio_bank_count;
static struct tegra_gpio_bank *tegra_gpio_banks;
static inline void tegra_gpio_writel(u32 val, u32 reg)
{
@ -139,7 +141,7 @@ static int tegra_gpio_direction_output(struct gpio_chip *chip, unsigned offset,
static int tegra_gpio_to_irq(struct gpio_chip *chip, unsigned offset)
{
return TEGRA_GPIO_TO_IRQ(offset);
return irq_find_mapping(irq_domain, offset);
}
static struct gpio_chip tegra_gpio_chip = {
@ -155,28 +157,28 @@ static struct gpio_chip tegra_gpio_chip = {
static void tegra_gpio_irq_ack(struct irq_data *d)
{
int gpio = d->irq - INT_GPIO_BASE;
int gpio = d->hwirq;
tegra_gpio_writel(1 << GPIO_BIT(gpio), GPIO_INT_CLR(gpio));
}
static void tegra_gpio_irq_mask(struct irq_data *d)
{
int gpio = d->irq - INT_GPIO_BASE;
int gpio = d->hwirq;
tegra_gpio_mask_write(GPIO_MSK_INT_ENB(gpio), gpio, 0);
}
static void tegra_gpio_irq_unmask(struct irq_data *d)
{
int gpio = d->irq - INT_GPIO_BASE;
int gpio = d->hwirq;
tegra_gpio_mask_write(GPIO_MSK_INT_ENB(gpio), gpio, 1);
}
static int tegra_gpio_irq_set_type(struct irq_data *d, unsigned int type)
{
int gpio = d->irq - INT_GPIO_BASE;
int gpio = d->hwirq;
struct tegra_gpio_bank *bank = irq_data_get_irq_chip_data(d);
int port = GPIO_PORT(gpio);
int lvl_type;
@ -273,7 +275,7 @@ void tegra_gpio_resume(void)
local_irq_save(flags);
for (b = 0; b < ARRAY_SIZE(tegra_gpio_banks); b++) {
for (b = 0; b < tegra_gpio_bank_count; b++) {
struct tegra_gpio_bank *bank = &tegra_gpio_banks[b];
for (p = 0; p < ARRAY_SIZE(bank->oe); p++) {
@ -296,7 +298,7 @@ void tegra_gpio_suspend(void)
int p;
local_irq_save(flags);
for (b = 0; b < ARRAY_SIZE(tegra_gpio_banks); b++) {
for (b = 0; b < tegra_gpio_bank_count; b++) {
struct tegra_gpio_bank *bank = &tegra_gpio_banks[b];
for (p = 0; p < ARRAY_SIZE(bank->oe); p++) {
@ -337,13 +339,44 @@ static struct lock_class_key gpio_lock_class;
static int __devinit tegra_gpio_probe(struct platform_device *pdev)
{
int irq_base;
struct resource *res;
struct tegra_gpio_bank *bank;
int gpio;
int i;
int j;
for (i = 0; i < ARRAY_SIZE(tegra_gpio_banks); i++) {
for (;;) {
res = platform_get_resource(pdev, IORESOURCE_IRQ, tegra_gpio_bank_count);
if (!res)
break;
tegra_gpio_bank_count++;
}
if (!tegra_gpio_bank_count) {
dev_err(&pdev->dev, "Missing IRQ resource\n");
return -ENODEV;
}
tegra_gpio_chip.ngpio = tegra_gpio_bank_count * 32;
tegra_gpio_banks = devm_kzalloc(&pdev->dev,
tegra_gpio_bank_count * sizeof(*tegra_gpio_banks),
GFP_KERNEL);
if (!tegra_gpio_banks) {
dev_err(&pdev->dev, "Couldn't allocate bank structure\n");
return -ENODEV;
}
irq_base = irq_alloc_descs(-1, 0, tegra_gpio_chip.ngpio, 0);
if (irq_base < 0) {
dev_err(&pdev->dev, "Couldn't allocate IRQ numbers\n");
return -ENODEV;
}
irq_domain = irq_domain_add_legacy(pdev->dev.of_node,
tegra_gpio_chip.ngpio, irq_base, 0,
&irq_domain_simple_ops, NULL);
for (i = 0; i < tegra_gpio_bank_count; i++) {
res = platform_get_resource(pdev, IORESOURCE_IRQ, i);
if (!res) {
dev_err(&pdev->dev, "Missing IRQ resource\n");
@ -380,8 +413,8 @@ static int __devinit tegra_gpio_probe(struct platform_device *pdev)
gpiochip_add(&tegra_gpio_chip);
for (gpio = 0; gpio < TEGRA_NR_GPIOS; gpio++) {
int irq = TEGRA_GPIO_TO_IRQ(gpio);
for (gpio = 0; gpio < tegra_gpio_chip.ngpio; gpio++) {
int irq = irq_find_mapping(irq_domain, gpio);
/* No validity check; all Tegra GPIOs are valid IRQs */
bank = &tegra_gpio_banks[GPIO_BANK(gpio)];
@ -393,7 +426,7 @@ static int __devinit tegra_gpio_probe(struct platform_device *pdev)
set_irq_flags(irq, IRQF_VALID);
}
for (i = 0; i < ARRAY_SIZE(tegra_gpio_banks); i++) {
for (i = 0; i < tegra_gpio_bank_count; i++) {
bank = &tegra_gpio_banks[i];
irq_set_chained_handler(bank->irq, tegra_gpio_irq_handler);

View File

@ -0,0 +1,34 @@
/*
* Copyright (C) 2011 Google, Inc.
*
* Author:
* Colin Cross <ccross@android.com>
* Olof Johansson <olof@lixom.net>
*
* This software is licensed under the terms of the GNU General Public
* License version 2, as published by the Free Software Foundation, and
* may be copied, distributed, and modified under those terms.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
*/
#ifndef __TEGRA_EMC_H_
#define __TEGRA_EMC_H_
#define TEGRA_EMC_NUM_REGS 46
struct tegra_emc_table {
unsigned long rate;
u32 regs[TEGRA_EMC_NUM_REGS];
};
struct tegra_emc_pdata {
int num_tables;
struct tegra_emc_table *tables;
};
#endif