Commit 7cb7f826 authored by Arnd Bergmann's avatar Arnd Bergmann

Merge tag 'tegra-soc-drivers' of...

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: default avatarArnd Bergmann <arnd@arndb.de>
parents 7169ff4a e77a6b31
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 >;
};
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;
};
* 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 >;
};
NVIDIA Tegra 2 GPIO controller NVIDIA Tegra GPIO controller
Required properties: 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 - #gpio-cells : Should be two. The first cell is the pin number and the
second cell is used to specify optional parameters: second cell is used to specify optional parameters:
- bit 0 specifies polarity (0 for normal, 1 for inverted) - bit 0 specifies polarity (0 for normal, 1 for inverted)
- gpio-controller : Marks the device node as a GPIO controller. - 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;
};
...@@ -10,19 +10,25 @@ memory@0 { ...@@ -10,19 +10,25 @@ memory@0 {
reg = < 0x00000000 0x40000000 >; reg = < 0x00000000 0x40000000 >;
}; };
pmc@7000f400 {
nvidia,invert-interrupt;
};
i2c@7000c000 { i2c@7000c000 {
clock-frequency = <400000>; clock-frequency = <400000>;
codec: wm8903@1a { wm8903: wm8903@1a {
compatible = "wlf,wm8903"; compatible = "wlf,wm8903";
reg = <0x1a>; reg = <0x1a>;
interrupts = < 347 >; interrupt-parent = <&gpio>;
interrupts = < 187 0x04 >;
gpio-controller; gpio-controller;
#gpio-cells = <2>; #gpio-cells = <2>;
/* 0x8000 = Not configured */ micdet-cfg = <0>;
gpio-cfg = < 0x8000 0x8000 0 0x8000 0x8000 >; micdet-delay = <100>;
gpio-cfg = < 0xffffffff 0xffffffff 0 0xffffffff 0xffffffff >;
}; };
}; };
...@@ -38,13 +44,32 @@ i2c@7000d000 { ...@@ -38,13 +44,32 @@ i2c@7000d000 {
clock-frequency = <400000>; clock-frequency = <400000>;
}; };
sound { i2s@70002a00 {
compatible = "nvidia,harmony-sound", "nvidia,tegra-wm8903"; status = "disable";
};
spkr-en-gpios = <&codec 2 0>; sound {
hp-det-gpios = <&gpio 178 0>; compatible = "nvidia,tegra-audio-wm8903-harmony",
int-mic-en-gpios = <&gpio 184 0>; "nvidia,tegra-audio-wm8903";
ext-mic-en-gpios = <&gpio 185 0>; 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 { serial@70006000 {
......
...@@ -12,6 +12,13 @@ memory@0 { ...@@ -12,6 +12,13 @@ memory@0 {
i2c@7000c000 { i2c@7000c000 {
clock-frequency = <400000>; clock-frequency = <400000>;
alc5632: alc5632@1e {
compatible = "realtek,alc5632";
reg = <0x1e>;
gpio-controller;
#gpio-cells = <2>;
};
}; };
i2c@7000c400 { i2c@7000c400 {
...@@ -37,6 +44,29 @@ i2c@7000d000 { ...@@ -37,6 +44,29 @@ i2c@7000d000 {
clock-frequency = <400000>; 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 { serial@70006000 {
clock-frequency = <216000000>; clock-frequency = <216000000>;
}; };
......
...@@ -13,6 +13,20 @@ memory { ...@@ -13,6 +13,20 @@ memory {
i2c@7000c000 { i2c@7000c000 {
clock-frequency = <400000>; 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 { i2c@7000c400 {
...@@ -32,6 +46,32 @@ adt7461@4c { ...@@ -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 { serial@70006000 {
status = "disable"; status = "disable";
}; };
...@@ -93,4 +133,42 @@ lid { ...@@ -93,4 +133,42 @@ lid {
gpio-key,wakeup; 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 >;
};
};
}; };
...@@ -26,6 +26,18 @@ i2c@7000d000 { ...@@ -26,6 +26,18 @@ i2c@7000d000 {
status = "disable"; status = "disable";
}; };
i2s@70002800 {
status = "disable";
};
i2s@70002a00 {
status = "disable";
};
das@70000c00 {
status = "disable";
};
serial@70006000 { serial@70006000 {
clock-frequency = < 216000000 >; clock-frequency = < 216000000 >;
}; };
......
...@@ -12,6 +12,20 @@ memory { ...@@ -12,6 +12,20 @@ memory {
i2c@7000c000 { i2c@7000c000 {
clock-frequency = <400000>; 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 { i2c@7000c400 {
...@@ -26,6 +40,34 @@ i2c@7000d000 { ...@@ -26,6 +40,34 @@ i2c@7000d000 {
clock-frequency = <400000>; 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 { serial@70006000 {
status = "disable"; status = "disable";
}; };
......
...@@ -4,6 +4,11 @@ / { ...@@ -4,6 +4,11 @@ / {
compatible = "nvidia,tegra20"; compatible = "nvidia,tegra20";
interrupt-parent = <&intc>; interrupt-parent = <&intc>;
pmc@7000f400 {
compatible = "nvidia,tegra20-pmc";
reg = <0x7000e400 0x400>;
};
intc: interrupt-controller@50041000 { intc: interrupt-controller@50041000 {
compatible = "arm,cortex-a9-gic"; compatible = "arm,cortex-a9-gic";
interrupt-controller; interrupt-controller;
...@@ -12,6 +17,27 @@ intc: interrupt-controller@50041000 { ...@@ -12,6 +17,27 @@ intc: interrupt-controller@50041000 {
< 0x50040100 0x0100 >; < 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 { i2c@7000c000 {
#address-cells = <1>; #address-cells = <1>;
#size-cells = <0>; #size-cells = <0>;
...@@ -44,18 +70,18 @@ i2c@7000d000 { ...@@ -44,18 +70,18 @@ i2c@7000d000 {
interrupts = < 0 53 0x04 >; interrupts = < 0 53 0x04 >;
}; };
i2s@70002800 { tegra_i2s1: i2s@70002800 {
compatible = "nvidia,tegra20-i2s"; compatible = "nvidia,tegra20-i2s";
reg = <0x70002800 0x200>; reg = <0x70002800 0x200>;
interrupts = < 0 13 0x04 >; interrupts = < 0 13 0x04 >;
dma-channel = < 2 >; nvidia,dma-request-selector = < &apbdma 2 >;
}; };
i2s@70002a00 { tegra_i2s2: i2s@70002a00 {
compatible = "nvidia,tegra20-i2s"; compatible = "nvidia,tegra20-i2s";
reg = <0x70002a00 0x200>; reg = <0x70002a00 0x200>;
interrupts = < 0 3 0x04 >; interrupts = < 0 3 0x04 >;
dma-channel = < 1 >; nvidia,dma-request-selector = < &apbdma 1 >;
}; };
das@70000c00 { das@70000c00 {
...@@ -75,6 +101,8 @@ gpio: gpio@6000d000 { ...@@ -75,6 +101,8 @@ gpio: gpio@6000d000 {
0 89 0x04 >; 0 89 0x04 >;
#gpio-cells = <2>; #gpio-cells = <2>;
gpio-controller; gpio-controller;
#interrupt-cells = <2>;
interrupt-controller;
}; };
pinmux: pinmux@70000000 { pinmux: pinmux@70000000 {
...@@ -120,6 +148,13 @@ serial@70006400 { ...@@ -120,6 +148,13 @@ serial@70006400 {
interrupts = < 0 91 0x04 >; interrupts = < 0 91 0x04 >;
}; };
emc@7000f400 {
#address-cells = <1>;
#size-cells = <0>;
compatible = "nvidia,tegra20-emc";
reg = <0x7000f400 0x200>;
};
sdhci@c8000000 { sdhci@c8000000 {
compatible = "nvidia,tegra20-sdhci"; compatible = "nvidia,tegra20-sdhci";
reg = <0xc8000000 0x200>; reg = <0xc8000000 0x200>;
......
...@@ -4,6 +4,11 @@ / { ...@@ -4,6 +4,11 @@ / {
compatible = "nvidia,tegra30"; compatible = "nvidia,tegra30";
interrupt-parent = <&intc>; interrupt-parent = <&intc>;
pmc@7000f400 {
compatible = "nvidia,tegra20-pmc", "nvidia,tegra30-pmc";
reg = <0x7000e400 0x400>;
};
intc: interrupt-controller@50041000 { intc: interrupt-controller@50041000 {
compatible = "arm,cortex-a9-gic"; compatible = "arm,cortex-a9-gic";
interrupt-controller; interrupt-controller;
...@@ -12,6 +17,43 @@ intc: interrupt-controller@50041000 { ...@@ -12,6 +17,43 @@ intc: interrupt-controller@50041000 {
< 0x50040100 0x0100 >; < 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 { i2c@7000c000 {
#address-cells = <1>; #address-cells = <1>;
#size-cells = <0>; #size-cells = <0>;
...@@ -55,9 +97,18 @@ i2c@7000d000 { ...@@ -55,9 +97,18 @@ i2c@7000d000 {
gpio: gpio@6000d000 { gpio: gpio@6000d000 {
compatible = "nvidia,tegra30-gpio", "nvidia,tegra20-gpio"; compatible = "nvidia,tegra30-gpio", "nvidia,tegra20-gpio";
reg = < 0x6000d000 0x1000 >; 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-cells = <2>;
gpio-controller; gpio-controller;
#interrupt-cells = <2>;
interrupt-controller;
}; };
serial@70006000 { serial@70006000 {
......
...@@ -7,6 +7,7 @@ obj-y += clock.o ...@@ -7,6 +7,7 @@ obj-y += clock.o
obj-y += timer.o obj-y += timer.o
obj-y += pinmux.o obj-y += pinmux.o
obj-y += fuse.o obj-y += fuse.o
obj-y += pmc.o
obj-$(CONFIG_ARCH_TEGRA_2x_SOC) += powergate.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_clocks.o
obj-$(CONFIG_ARCH_TEGRA_2x_SOC) += tegra2_emc.o obj-$(CONFIG_ARCH_TEGRA_2x_SOC) += tegra2_emc.o
...@@ -15,7 +16,7 @@ obj-$(CONFIG_ARCH_TEGRA_3x_SOC) += pinmux-tegra30-tables.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_ARCH_TEGRA_3x_SOC) += board-dt-tegra30.o
obj-$(CONFIG_SMP) += platsmp.o localtimer.o headsmp.o obj-$(CONFIG_SMP) += platsmp.o localtimer.o headsmp.o
obj-$(CONFIG_HOTPLUG_CPU) += hotplug.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_CPU_FREQ) += cpu-tegra.o
obj-$(CONFIG_TEGRA_PCI) += pcie.o obj-$(CONFIG_TEGRA_PCI) += pcie.o
obj-$(CONFIG_USB_SUPPORT) += usb_phy.o obj-$(CONFIG_USB_SUPPORT) += usb_phy.o
......
/*
* 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);
}
/*
* 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
...@@ -18,18 +18,13 @@ ...@@ -18,18 +18,13 @@
#include <linux/i2c.h> #include <linux/i2c.h>
#include <linux/platform_device.h> #include <linux/platform_device.h>
#include <linux/gpio.h> #include <linux/gpio.h>
#include <linux/io.h>
#include <linux/regulator/machine.h> #include <linux/regulator/machine.h>
#include <linux/mfd/tps6586x.h> #include <linux/mfd/tps6586x.h>
#include <mach/iomap.h>
#include <mach/irqs.h> #include <mach/irqs.h>
#include "board-harmony.h" #include "board-harmony.h"
#define PMC_CTRL 0x0
#define PMC_CTRL_INTR_LOW (1 << 17)
static struct regulator_consumer_supply tps658621_ldo0_supply[] = { static struct regulator_consumer_supply tps658621_ldo0_supply[] = {
REGULATOR_SUPPLY("pex_clk", NULL), REGULATOR_SUPPLY("pex_clk", NULL),
}; };
...@@ -114,16 +109,6 @@ static struct i2c_board_info __initdata harmony_regulators[] = { ...@@ -114,16 +109,6 @@ static struct i2c_board_info __initdata harmony_regulators[] = {
int __init harmony_regulator_init(void) 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); i2c_register_board_info(3, harmony_regulators, 1);
return 0; return 0;
......
...@@ -101,7 +101,6 @@ static struct wm8903_platform_data harmony_wm8903_pdata = { ...@@ -101,7 +101,6 @@ static struct wm8903_platform_data harmony_wm8903_pdata = {
static struct i2c_board_info __initdata wm8903_board_info = { static struct i2c_board_info __initdata wm8903_board_info = {
I2C_BOARD_INFO("wm8903", 0x1a), I2C_BOARD_INFO("wm8903", 0x1a),
.platform_data = &harmony_wm8903_pdata, .platform_data = &harmony_wm8903_pdata,
.irq = TEGRA_GPIO_TO_IRQ(TEGRA_GPIO_CDC_IRQ),
}; };
static void __init harmony_i2c_init(void) static void __init harmony_i2c_init(void)
...@@ -111,6 +110,7 @@ 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_device3);
platform_device_register(&tegra_i2c_device4); 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); i2c_register_board_info(0, &wm8903_board_info, 1);
} }
......
...@@ -159,7 +159,6 @@ static struct platform_device *seaboard_devices[] __initdata = { ...@@ -159,7 +159,6 @@ static struct platform_device *seaboard_devices[] __initdata = {
static struct i2c_board_info __initdata isl29018_device = { static struct i2c_board_info __initdata isl29018_device = {
I2C_BOARD_INFO("isl29018", 0x44), I2C_BOARD_INFO("isl29018", 0x44),
.irq = TEGRA_GPIO_TO_IRQ(TEGRA_GPIO_ISL29018_IRQ),
}; };
static struct i2c_board_info __initdata adt7461_device = { static struct i2c_board_info __initdata adt7461_device = {
...@@ -183,7 +182,6 @@ static struct wm8903_platform_data wm8903_pdata = { ...@@ -183,7 +182,6 @@ static struct wm8903_platform_data wm8903_pdata = {
static struct i2c_board_info __initdata wm8903_device = { static struct i2c_board_info __initdata wm8903_device = {
I2C_BOARD_INFO("wm8903", 0x1a), I2C_BOARD_INFO("wm8903", 0x1a),
.platform_data = &wm8903_pdata, .platform_data = &wm8903_pdata,
.irq = TEGRA_GPIO_TO_IRQ(TEGRA_GPIO_CDC_IRQ),
}; };
static int seaboard_ehci_init(void) static int seaboard_ehci_init(void)
...@@ -214,7 +212,10 @@ static void __init seaboard_i2c_init(void) ...@@ -214,7 +212,10 @@ static void __init seaboard_i2c_init(void)
gpio_request(TEGRA_GPIO_ISL29018_IRQ, "isl29018"); gpio_request(TEGRA_GPIO_ISL29018_IRQ, "isl29018");
gpio_direction_input(TEGRA_GPIO_ISL29018_IRQ); 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); 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(0, &wm8903_device, 1);
i2c_register_board_info(3, &adt7461_device, 1); i2c_register_board_info(3, &adt7461_device, 1);
......
...@@ -31,6 +31,24 @@ ...@@ -31,6 +31,24 @@
#include "board.h" #include "board.h"
#include "clock.h" #include "clock.h"
#include "fuse.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 #ifdef CONFIG_OF
static const struct of_device_id tegra_dt_irq_match[] __initconst = { static const struct of_device_id tegra_dt_irq_match[] __initconst = {
...@@ -101,11 +119,13 @@ void __init tegra20_init_early(void) ...@@ -101,11 +119,13 @@ void __init tegra20_init_early(void)
tegra2_init_clocks(); tegra2_init_clocks();
tegra_clk_init_from_table(tegra20_clk_init_table); tegra_clk_init_from_table(tegra20_clk_init_table);
tegra_init_cache(0x331, 0x441); tegra_init_cache(0x331, 0x441);
tegra_pmc_init();
} }
#endif #endif
#ifdef CONFIG_ARCH_TEGRA_3x_SOC #ifdef CONFIG_ARCH_TEGRA_3x_SOC
void __init tegra30_init_early(void) void __init tegra30_init_early(void)
{ {
tegra_init_cache(0x441, 0x551); tegra_init_cache(0x441, 0x551);
tegra_pmc_init();
} }
#endif #endif
...@@ -33,6 +33,8 @@ ...@@ -33,6 +33,8 @@
#include <mach/iomap.h> #include <mach/iomap.h>
#include <mach/suspend.h> #include <mach/suspend.h>
#include "apbio.h"
#define APB_DMA_GEN 0x000 #define APB_DMA_GEN 0x000
#define GEN_ENABLE (1<<31) #define GEN_ENABLE (1<<31)
...@@ -50,8 +52,6 @@ ...@@ -50,8 +52,6 @@
#define CSR_ONCE (1<<27) #define CSR_ONCE (1<<27)
#define CSR_FLOW (1<<21) #define CSR_FLOW (1<<21)
#define CSR_REQ_SEL_SHIFT 16 #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_SHIFT 2
#define CSR_WCOUNT_MASK 0xFFFC #define CSR_WCOUNT_MASK 0xFFFC
...@@ -133,6 +133,7 @@ struct tegra_dma_channel { ...@@ -133,6 +133,7 @@ struct tegra_dma_channel {
static bool tegra_dma_initialized; static bool tegra_dma_initialized;
static DEFINE_MUTEX(tegra_dma_lock); static DEFINE_MUTEX(tegra_dma_lock);
static DEFINE_SPINLOCK(enable_lock);
static DECLARE_BITMAP(channel_usage, NV_DMA_MAX_CHANNELS); static DECLARE_BITMAP(channel_usage, NV_DMA_MAX_CHANNELS);
static struct tegra_dma_channel dma_channels[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) ...@@ -180,36 +181,94 @@ static void tegra_dma_stop(struct tegra_dma_channel *ch)
static int tegra_dma_cancel(struct tegra_dma_channel *ch) static int tegra_dma_cancel(struct tegra_dma_channel *ch)
{ {
u32 csr;
unsigned long irq_flags; unsigned long irq_flags;
spin_lock_irqsave(&ch->lock, irq_flags); spin_lock_irqsave(&ch->lock, irq_flags);
while (!list_empty(&ch->list)) while (!list_empty(&ch->list))
list_del(ch->list.next); 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); tegra_dma_stop(ch);
spin_unlock_irqrestore(&ch->lock, irq_flags); spin_unlock_irqrestore(&ch->lock, irq_flags);
return 0; 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, int tegra_dma_dequeue_req(struct tegra_dma_channel *ch,
struct tegra_dma_req *_req) struct tegra_dma_req *_req)
{ {
unsigned int csr;
unsigned int status; unsigned int status;
struct tegra_dma_req *req = NULL; struct tegra_dma_req *req = NULL;
int found = 0; int found = 0;
unsigned long irq_flags; unsigned long irq_flags;
int to_transfer; int stop = 0;
int req_transfer_count;
spin_lock_irqsave(&ch->lock, irq_flags); 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) { list_for_each_entry(req, &ch->list, node) {
if (req == _req) { if (req == _req) {
list_del(&req->node); list_del(&req->node);
...@@ -222,47 +281,12 @@ int tegra_dma_dequeue_req(struct tegra_dma_channel *ch, ...@@ -222,47 +281,12 @@ int tegra_dma_dequeue_req(struct tegra_dma_channel *ch,
return 0; return 0;
} }
/* STOP the DMA and get the transfer count. if (!stop)
* Getting the transfer count is tricky. goto skip_stop_dma;
* - 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);
/* 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;
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; status = get_channel_status(ch, req, true);
req->bytes_transferred = dma_active_count(ch, req, status);
tegra_dma_stop(ch);
if (!list_empty(&ch->list)) { if (!list_empty(&ch->list)) {
/* if the list is not empty, queue the next request */ /* if the list is not empty, queue the next request */
struct tegra_dma_req *next_req; struct tegra_dma_req *next_req;
...@@ -270,6 +294,8 @@ int tegra_dma_dequeue_req(struct tegra_dma_channel *ch, ...@@ -270,6 +294,8 @@ int tegra_dma_dequeue_req(struct tegra_dma_channel *ch,
typeof(*next_req), node); typeof(*next_req), node);
tegra_dma_update_hw(ch, next_req); tegra_dma_update_hw(ch, next_req);
} }
skip_stop_dma:
req->status = -TEGRA_DMA_REQ_ERROR_ABORTED; req->status = -TEGRA_DMA_REQ_ERROR_ABORTED;
spin_unlock_irqrestore(&ch->lock, irq_flags); spin_unlock_irqrestore(&ch->lock, irq_flags);
...@@ -357,7 +383,7 @@ struct tegra_dma_channel *tegra_dma_allocate_channel(int mode) ...@@ -357,7 +383,7 @@ struct tegra_dma_channel *tegra_dma_allocate_channel(int mode)
int channel; int channel;
struct tegra_dma_channel *ch = NULL; struct tegra_dma_channel *ch = NULL;
if (WARN_ON(!tegra_dma_initialized)) if (!tegra_dma_initialized)
return NULL; return NULL;
mutex_lock(&tegra_dma_lock); mutex_lock(&tegra_dma_lock);
......
...@@ -23,20 +23,70 @@ ...@@ -23,20 +23,70 @@
#include <mach/iomap.h> #include <mach/iomap.h>
#include "fuse.h" #include "fuse.h"
#include "apbio.h"
#define FUSE_UID_LOW 0x108 #define FUSE_UID_LOW 0x108
#define FUSE_UID_HIGH 0x10c #define FUSE_UID_HIGH 0x10c
#define FUSE_SKU_INFO 0x110 #define FUSE_SKU_INFO 0x110
#define FUSE_SPARE_BIT 0x200 #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 tegra_apb_readl(TEGRA_FUSE_BASE + offset);
}
static inline bool get_spare_fuse(int bit)
{ {
return readl(IO_TO_VIRT(TEGRA_FUSE_BASE + offset)); return tegra_fuse_readl(FUSE_SPARE_BIT + bit * 4);
} }
static inline void fuse_writel(u32 value, unsigned long offset) static enum tegra_revision tegra_get_revision(void)
{ {
writel(value, IO_TO_VIRT(TEGRA_FUSE_BASE + offset)); 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) void tegra_init_fuse(void)
...@@ -45,40 +95,31 @@ void tegra_init_fuse(void) ...@@ -45,40 +95,31 @@ void tegra_init_fuse(void)
reg |= 1 << 28; reg |= 1 << 28;
writel(reg, IO_TO_VIRT(TEGRA_CLK_RESET_BASE + 0x48)); writel(reg, IO_TO_VIRT(TEGRA_CLK_RESET_BASE + 0x48));
pr_info("Tegra SKU: %d CPU Process: %d Core Process: %d\n", reg = tegra_fuse_readl(FUSE_SKU_INFO);
tegra_sku_id(), tegra_cpu_process_id(), tegra_sku_id = reg & 0xFF;
tegra_core_process_id());
}
unsigned long long tegra_chip_uid(void) reg = tegra_fuse_readl(FUSE_SPARE_BIT);
{ tegra_cpu_process_id = (reg >> 6) & 3;
unsigned long long lo, hi;
lo = fuse_readl(FUSE_UID_LOW); reg = tegra_fuse_readl(FUSE_SPARE_BIT);
hi = fuse_readl(FUSE_UID_HIGH); tegra_core_process_id = (reg >> 12) & 3;
return (hi << 32ull) | lo;
}
int tegra_sku_id(void) reg = tegra_apb_readl(TEGRA_APB_MISC_BASE + STRAP_OPT);
{ tegra_bct_strapping = (reg & RAM_ID_MASK) >> RAM_CODE_SHIFT;
int sku_id;
u32 reg = fuse_readl(FUSE_SKU_INFO);
sku_id = reg & 0xFF;
return sku_id;
}
int tegra_cpu_process_id(void) tegra_revision = tegra_get_revision();
{
int cpu_process_id; pr_info("Tegra Revision: %s SKU: %d CPU Process: %d Core Process: %d\n",
u32 reg = fuse_readl(FUSE_SPARE_BIT); tegra_revision_name[tegra_get_revision()],
cpu_process_id = (reg >> 6) & 3; tegra_sku_id, tegra_cpu_process_id,
return cpu_process_id; tegra_core_process_id);
} }
int tegra_core_process_id(void) unsigned long long tegra_chip_uid(void)
{ {
int core_process_id; unsigned long long lo, hi;
u32 reg = fuse_readl(FUSE_SPARE_BIT);
core_process_id = (reg >> 12) & 3; lo = tegra_fuse_readl(FUSE_UID_LOW);
return core_process_id; hi = tegra_fuse_readl(FUSE_UID_HIGH);
return (hi << 32ull) | lo;
} }
/* /*
* arch/arm/mach-tegra/fuse.c
*
* Copyright (C) 2010 Google, Inc. * Copyright (C) 2010 Google, Inc.
* *
* Author: * Author:
...@@ -17,8 +15,34 @@ ...@@ -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); 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); void tegra_init_fuse(void);
#endif
/* /*
* arch/arm/mach-tegra/include/mach/debug-macro.S * 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: * Author:
* Colin Cross <ccross@google.com> * Colin Cross <ccross@google.com>
* Erik Gilling <konkers@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 * This software is licensed under the terms of the GNU General Public
* License version 2, as published by the Free Software Foundation, and * License version 2, as published by the Free Software Foundation, and
...@@ -18,18 +24,78 @@ ...@@ -18,18 +24,78 @@
* *
*/ */
#include <linux/serial_reg.h>
#include <mach/io.h> #include <mach/io.h>
#include <mach/iomap.h> #include <mach/iomap.h>
#include <mach/irammap.h>
.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
.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 addruart, rp, rv, tmp .macro senduart, rd, rx
ldr \rp, =IO_APB_PHYS @ physical tst \rx, #0x0000ff00
ldr \rv, =IO_APB_VIRT @ virtual strneb \rd, [\rx, #UART_TX << UART_SHIFT]
orr \rp, \rp, #(TEGRA_DEBUG_UART_BASE & 0xFF) 1001:
orr \rp, \rp, #(TEGRA_DEBUG_UART_BASE & 0xFF00) .endm
orr \rv, \rv, #(TEGRA_DEBUG_UART_BASE & 0xFF)
orr \rv, \rv, #(TEGRA_DEBUG_UART_BASE & 0xFF00)
.endm
#define UART_SHIFT 2 .macro busyuart, rd, rx
#include <asm/hardware/debug-8250.S> 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
...@@ -25,8 +25,6 @@ ...@@ -25,8 +25,6 @@
#define TEGRA_NR_GPIOS INT_GPIO_NR #define TEGRA_NR_GPIOS INT_GPIO_NR
#define TEGRA_GPIO_TO_IRQ(gpio) (INT_GPIO_BASE + (gpio))
struct tegra_gpio_table { struct tegra_gpio_table {
int gpio; /* GPIO number */ int gpio; /* GPIO number */
bool enable; /* Enable for GPIO at init? */ bool enable; /* Enable for GPIO at init? */
......
/*
* 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
...@@ -2,10 +2,14 @@ ...@@ -2,10 +2,14 @@
* arch/arm/mach-tegra/include/mach/uncompress.h * arch/arm/mach-tegra/include/mach/uncompress.h
* *
* Copyright (C) 2010 Google, Inc. * Copyright (C) 2010 Google, Inc.
* Copyright (C) 2011 Google, Inc.
* Copyright (C) 2011-2012 NVIDIA CORPORATION. All Rights Reserved.
* *
* Author: * Author:
* Colin Cross <ccross@google.com> * Colin Cross <ccross@google.com>
* Erik Gilling <konkers@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 * This software is licensed under the terms of the GNU General Public
* License version 2, as published by the Free Software Foundation, and * License version 2, as published by the Free Software Foundation, and
...@@ -25,36 +29,130 @@ ...@@ -25,36 +29,130 @@
#include <linux/serial_reg.h> #include <linux/serial_reg.h>
#include <mach/iomap.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) static void putc(int c)
{ {
volatile u8 *uart = (volatile u8 *)TEGRA_DEBUG_UART_BASE;
int shift = 2;
if (uart == NULL) if (uart == NULL)
return; return;
while (!(uart[UART_LSR << shift] & UART_LSR_THRE)) while (!(uart[UART_LSR << DEBUG_UART_SHIFT] & UART_LSR_THRE))
barrier(); barrier();
uart[UART_TX << shift] = c; uart[UART_TX << DEBUG_UART_SHIFT] = c;
} }
static inline void flush(void) 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) static inline void arch_decomp_setup(void)
{ {
volatile u8 *uart = (volatile u8 *)TEGRA_DEBUG_UART_BASE; static const struct {
int shift = 2; 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) if (uart == NULL)
return; return;
uart[UART_LCR << shift] |= UART_LCR_DLAB; chip = (apb_misc[0x804 / 4] >> 8) & 0xff;
uart[UART_DLL << shift] = 0x75; if (chip == 0x20)
uart[UART_DLM << shift] = 0x0; div = 0x0075;
uart[UART_LCR << shift] = 3; 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) static inline void arch_decomp_wdog(void)
......
/*
* 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);
}
/*
* 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
...@@ -720,7 +720,7 @@ static void tegra2_pllx_clk_init(struct clk *c) ...@@ -720,7 +720,7 @@ static void tegra2_pllx_clk_init(struct clk *c)
{ {
tegra2_pll_clk_init(c); tegra2_pll_clk_init(c);
if (tegra_sku_id() == 7) if (tegra_sku_id == 7)
c->max_rate = 750000000; c->max_rate = 750000000;
} }
......
...@@ -16,14 +16,19 @@ ...@@ -16,14 +16,19 @@
*/ */
#include <linux/kernel.h> #include <linux/kernel.h>
#include <linux/device.h>
#include <linux/clk.h> #include <linux/clk.h>
#include <linux/err.h> #include <linux/err.h>
#include <linux/io.h> #include <linux/io.h>
#include <linux/module.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 <mach/iomap.h>
#include "tegra2_emc.h" #include "tegra2_emc.h"
#include "fuse.h"
#ifdef CONFIG_TEGRA_EMC_SCALING_ENABLE #ifdef CONFIG_TEGRA_EMC_SCALING_ENABLE
static bool emc_enable = true; static bool emc_enable = true;
...@@ -32,18 +37,17 @@ static bool emc_enable; ...@@ -32,18 +37,17 @@ static bool emc_enable;
#endif #endif
module_param(emc_enable, bool, 0644); module_param(emc_enable, bool, 0644);
static void __iomem *emc = IO_ADDRESS(TEGRA_EMC_BASE); static struct platform_device *emc_pdev;
static const struct tegra_emc_table *tegra_emc_table; static void __iomem *emc_regbase;
static int tegra_emc_table_size;
static inline void emc_writel(u32 val, unsigned long addr) 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) 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] = { 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,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 */ /* Select the closest EMC rate that is higher than the requested rate */
long tegra_emc_round_rate(unsigned long rate) long tegra_emc_round_rate(unsigned long rate)
{ {
struct tegra_emc_pdata *pdata;
int i; int i;
int best = -1; int best = -1;
unsigned long distance = ULONG_MAX; unsigned long distance = ULONG_MAX;
if (!tegra_emc_table) if (!emc_pdev)
return -EINVAL; return -EINVAL;
if (!emc_enable) pdata = emc_pdev->dev.platform_data;
return -EINVAL;
pr_debug("%s: %lu\n", __func__, rate); pr_debug("%s: %lu\n", __func__, rate);
...@@ -116,10 +120,10 @@ long tegra_emc_round_rate(unsigned long rate) ...@@ -116,10 +120,10 @@ long tegra_emc_round_rate(unsigned long rate)
*/ */
rate = rate / 2 / 1000; rate = rate / 2 / 1000;
for (i = 0; i < tegra_emc_table_size; i++) { for (i = 0; i < pdata->num_tables; i++) {
if (tegra_emc_table[i].rate >= rate && if (pdata->tables[i].rate >= rate &&
(tegra_emc_table[i].rate - rate) < distance) { (pdata->tables[i].rate - rate) < distance) {
distance = tegra_emc_table[i].rate - rate; distance = pdata->tables[i].rate - rate;
best = i; best = i;
} }
} }
...@@ -127,9 +131,9 @@ long tegra_emc_round_rate(unsigned long rate) ...@@ -127,9 +131,9 @@ long tegra_emc_round_rate(unsigned long rate)
if (best < 0) if (best < 0)
return -EINVAL; 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) ...@@ -142,37 +146,211 @@ long tegra_emc_round_rate(unsigned long rate)
*/ */
int tegra_emc_set_rate(unsigned long rate) int tegra_emc_set_rate(unsigned long rate)
{ {
struct tegra_emc_pdata *pdata;
int i; int i;
int j; int j;
if (!tegra_emc_table) if (!emc_pdev)
return -EINVAL; return -EINVAL;
pdata = emc_pdev->dev.platform_data;
/* /*
* The EMC clock rate is twice the bus rate, and the bus rate is * The EMC clock rate is twice the bus rate, and the bus rate is
* measured in kHz * measured in kHz
*/ */
rate = rate / 2 / 1000; rate = rate / 2 / 1000;
for (i = 0; i < tegra_emc_table_size; i++) for (i = 0; i < pdata->num_tables; i++)
if (tegra_emc_table[i].rate == rate) if (pdata->tables[i].rate == rate)
break; break;
if (i >= tegra_emc_table_size) if (i >= pdata->num_tables)
return -EINVAL; return -EINVAL;
pr_debug("%s: setting to %lu\n", __func__, rate); pr_debug("%s: setting to %lu\n", __func__, rate);
for (j = 0; j < TEGRA_EMC_NUM_REGS; j++) 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; 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)
{
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)
{ {
tegra_emc_table = table; return platform_driver_register(&tegra_emc_driver);
tegra_emc_table_size = table_size;
} }
device_initcall(tegra_emc_init);
...@@ -15,13 +15,10 @@ ...@@ -15,13 +15,10 @@
* *
*/ */
#define TEGRA_EMC_NUM_REGS 46 #ifndef __MACH_TEGRA_TEGRA2_EMC_H_
#define __MACH_TEGRA_TEGRA2_EMC_H
struct tegra_emc_table {
unsigned long rate;
u32 regs[TEGRA_EMC_NUM_REGS];
};
int tegra_emc_set_rate(unsigned long rate); int tegra_emc_set_rate(unsigned long rate);
long tegra_emc_round_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
...@@ -25,6 +25,7 @@ ...@@ -25,6 +25,7 @@
#include <linux/of.h> #include <linux/of.h>
#include <linux/platform_device.h> #include <linux/platform_device.h>
#include <linux/module.h> #include <linux/module.h>
#include <linux/irqdomain.h>
#include <asm/mach/irq.h> #include <asm/mach/irq.h>
...@@ -74,9 +75,10 @@ struct tegra_gpio_bank { ...@@ -74,9 +75,10 @@ struct tegra_gpio_bank {
#endif #endif
}; };
static struct irq_domain *irq_domain;
static void __iomem *regs; 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) 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, ...@@ -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) 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 = { static struct gpio_chip tegra_gpio_chip = {
...@@ -155,28 +157,28 @@ 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) 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)); tegra_gpio_writel(1 << GPIO_BIT(gpio), GPIO_INT_CLR(gpio));
} }
static void tegra_gpio_irq_mask(struct irq_data *d) 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); tegra_gpio_mask_write(GPIO_MSK_INT_ENB(gpio), gpio, 0);
} }
static void tegra_gpio_irq_unmask(struct irq_data *d) 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); 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) 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); struct tegra_gpio_bank *bank = irq_data_get_irq_chip_data(d);
int port = GPIO_PORT(gpio); int port = GPIO_PORT(gpio);
int lvl_type; int lvl_type;
...@@ -273,7 +275,7 @@ void tegra_gpio_resume(void) ...@@ -273,7 +275,7 @@ void tegra_gpio_resume(void)
local_irq_save(flags); 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]; struct tegra_gpio_bank *bank = &tegra_gpio_banks[b];
for (p = 0; p < ARRAY_SIZE(bank->oe); p++) { for (p = 0; p < ARRAY_SIZE(bank->oe); p++) {
...@@ -296,7 +298,7 @@ void tegra_gpio_suspend(void) ...@@ -296,7 +298,7 @@ void tegra_gpio_suspend(void)
int p; int p;
local_irq_save(flags); 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]; struct tegra_gpio_bank *bank = &tegra_gpio_banks[b];
for (p = 0; p < ARRAY_SIZE(bank->oe); p++) { for (p = 0; p < ARRAY_SIZE(bank->oe); p++) {
...@@ -337,13 +339,44 @@ static struct lock_class_key gpio_lock_class; ...@@ -337,13 +339,44 @@ static struct lock_class_key gpio_lock_class;
static int __devinit tegra_gpio_probe(struct platform_device *pdev) static int __devinit tegra_gpio_probe(struct platform_device *pdev)
{ {
int irq_base;
struct resource *res; struct resource *res;
struct tegra_gpio_bank *bank; struct tegra_gpio_bank *bank;
int gpio; int gpio;
int i; int i;
int j; 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); res = platform_get_resource(pdev, IORESOURCE_IRQ, i);
if (!res) { if (!res) {
dev_err(&pdev->dev, "Missing IRQ resource\n"); dev_err(&pdev->dev, "Missing IRQ resource\n");
...@@ -380,8 +413,8 @@ static int __devinit tegra_gpio_probe(struct platform_device *pdev) ...@@ -380,8 +413,8 @@ static int __devinit tegra_gpio_probe(struct platform_device *pdev)
gpiochip_add(&tegra_gpio_chip); gpiochip_add(&tegra_gpio_chip);
for (gpio = 0; gpio < TEGRA_NR_GPIOS; gpio++) { for (gpio = 0; gpio < tegra_gpio_chip.ngpio; gpio++) {
int irq = TEGRA_GPIO_TO_IRQ(gpio); int irq = irq_find_mapping(irq_domain, gpio);
/* No validity check; all Tegra GPIOs are valid IRQs */ /* No validity check; all Tegra GPIOs are valid IRQs */
bank = &tegra_gpio_banks[GPIO_BANK(gpio)]; bank = &tegra_gpio_banks[GPIO_BANK(gpio)];
...@@ -393,7 +426,7 @@ static int __devinit tegra_gpio_probe(struct platform_device *pdev) ...@@ -393,7 +426,7 @@ static int __devinit tegra_gpio_probe(struct platform_device *pdev)
set_irq_flags(irq, IRQF_VALID); 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]; bank = &tegra_gpio_banks[i];
irq_set_chained_handler(bank->irq, tegra_gpio_irq_handler); irq_set_chained_handler(bank->irq, tegra_gpio_irq_handler);
......
/*
* 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
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment