Commit ce53044c authored by Linus Torvalds's avatar Linus Torvalds

Merge tag 'drivers' of git://git.kernel.org/pub/scm/linux/kernel/git/arm/arm-soc

Pull arm-soc driver specific updates from Olof Johansson:
 "These changes are specific to some driver that may be used by multiple
  boards or socs.  The most significant change in here is the move of
  the samsung iommu code from a platform specific in-kernel interface to
  the generic iommu subsystem."

Fix up trivial conflicts in arch/arm/mach-exynos/Kconfig

* tag 'drivers' of git://git.kernel.org/pub/scm/linux/kernel/git/arm/arm-soc: (28 commits)
  mmc: dt: Consolidate DT bindings
  iommu/exynos: Add iommu driver for EXYNOS Platforms
  ARM: davinci: optimize the DMA ISR
  ARM: davinci: implement DEBUG_LL port choice
  ARM: tegra: Add SMMU enabler in AHB
  ARM: tegra: Add Tegra AHB driver
  Input: pxa27x_keypad add choice to set direct_key_mask
  Input: pxa27x_keypad direct key may be low active
  Input: pxa27x_keypad bug fix for direct_key_mask
  Input: pxa27x_keypad keep clock on as wakeup source
  ARM: dt: tegra: pinmux changes for USB ULPI
  ARM: tegra: add USB ULPI PHY reset GPIO to device tree
  ARM: tegra: don't hard-code USB ULPI PHY reset_gpio
  ARM: tegra: change pll_p_out4's rate to 24MHz
  ARM: tegra: fix pclk rate
  ARM: tegra: reparent sclk to pll_c_out1
  ARM: tegra: Add pllc clock init table
  ARM: dt: tegra cardhu: basic audio support
  ARM: dt: tegra30.dtsi: Add audio-related nodes
  ARM: tegra: add AUXDATA required for audio
  ...
parents 0877aa39 046fae44
NVIDIA Tegra AHB
Required properties:
- compatible : "nvidia,tegra20-ahb" or "nvidia,tegra30-ahb"
- reg : Should contain 1 register ranges(address and length)
Example:
ahb: ahb@6000c004 {
compatible = "nvidia,tegra20-ahb";
reg = <0x6000c004 0x10c>; /* AHB Arbitration + Gizmo Controller */
};
......@@ -11,9 +11,11 @@ Required properties:
- interrupt-parent : interrupt source phandle.
- clock-frequency : specifies eSDHC base clock frequency.
- sdhci,wp-inverted : (optional) specifies that eSDHC controller
reports inverted write-protect state;
reports inverted write-protect state; New devices should use
the generic "wp-inverted" property.
- sdhci,1-bit-only : (optional) specifies that a controller can
only handle 1-bit data transfers.
only handle 1-bit data transfers. New devices should use the
generic "bus-width = <1>" property.
- sdhci,auto-cmd12: (optional) specifies that a controller can
only handle auto CMD12.
......
......@@ -9,7 +9,7 @@ Required properties:
- interrupts : Should contain eSDHC interrupt
Optional properties:
- fsl,card-wired : Indicate the card is wired to host permanently
- non-removable : Indicate the card is wired to host permanently
- fsl,cd-internal : Indicate to use controller internal card detection
- fsl,wp-internal : Indicate to use controller internal write protection
- cd-gpios : Specify GPIOs for card detection
......
......@@ -10,7 +10,8 @@ Required properties:
Optional properties:
- gpios : may specify GPIOs in this order: Card-Detect GPIO,
Write-Protect GPIO.
Write-Protect GPIO. Note that this does not follow the
binding from mmc.txt, for historic reasons.
- interrupts : the interrupt of a card detect interrupt.
- interrupt-parent : the phandle for the interrupt controller that
services interrupts for this device.
......
These properties are common to multiple MMC host controllers. Any host
that requires the respective functionality should implement them using
these definitions.
Required properties:
- bus-width: Number of data lines, can be <1>, <4>, or <8>
Optional properties:
- cd-gpios : Specify GPIOs for card detection, see gpio binding
- wp-gpios : Specify GPIOs for write protection, see gpio binding
- cd-inverted: when present, polarity on the wp gpio line is inverted
- wp-inverted: when present, polarity on the wp gpio line is inverted
- non-removable: non-removable slot (like eMMC)
- max-frequency: maximum operating clock frequency
Example:
sdhci@ab000000 {
compatible = "sdhci";
reg = <0xab000000 0x200>;
interrupts = <23>;
bus-width = <4>;
cd-gpios = <&gpio 69 0>;
cd-inverted;
wp-gpios = <&gpio 70 0>;
max-frequency = <50000000>;
}
......@@ -7,12 +7,12 @@ Required properties:
- compatible : Should be "nvidia,<chip>-sdhci"
- reg : Should contain SD/MMC registers location and length
- interrupts : Should contain SD/MMC interrupt
- bus-width : Number of data lines, can be <1>, <4>, or <8>
Optional properties:
- cd-gpios : Specify GPIOs for card detection
- wp-gpios : Specify GPIOs for write protection
- power-gpios : Specify GPIOs for power control
- support-8bit : Boolean, indicates if 8-bit mode should be used.
Example:
......@@ -23,5 +23,5 @@ sdhci@c8000200 {
cd-gpios = <&gpio 69 0>; /* gpio PI5 */
wp-gpios = <&gpio 57 0>; /* gpio PH1 */
power-gpios = <&gpio 155 0>; /* gpio PT3 */
support-8bit;
bus-width = <8>;
};
......@@ -15,7 +15,7 @@ Optional properties:
ti,dual-volt: boolean, supports dual voltage cards
<supply-name>-supply: phandle to the regulator device tree node
"supply-name" examples are "vmmc", "vmmc_aux" etc
ti,bus-width: Number of data lines, default assumed is 1 if the property is missing.
bus-width: Number of data lines, default assumed is 1 if the property is missing.
cd-gpios: GPIOs for card detection
wp-gpios: GPIOs for write protection
ti,non-removable: non-removable slot (like eMMC)
......@@ -27,7 +27,7 @@ Example:
reg = <0x4809c000 0x400>;
ti,hwmods = "mmc1";
ti,dual-volt;
ti,bus-width = <4>;
bus-width = <4>;
vmmc-supply = <&vmmc>; /* phandle to regulator node */
ti,non-removable;
};
......@@ -12,6 +12,9 @@ Required properties :
- nvidia,vbus-gpio : If present, specifies a gpio that needs to be
activated for the bus to be powered.
Required properties for phy_type == ulpi:
- nvidia,phy-reset-gpio : The GPIO used to reset the PHY.
Optional properties:
- dr_mode : dual role mode. Indicates the working mode for
nvidia,tegra20-ehci compatible controllers. Can be "host", "peripheral",
......
......@@ -103,6 +103,35 @@ choice
Say Y here if you want the debug print routines to direct
their output to the second serial port on these devices.
config DEBUG_DAVINCI_DA8XX_UART1
bool "Kernel low-level debugging on DaVinci DA8XX using UART1"
depends on ARCH_DAVINCI_DA8XX
help
Say Y here if you want the debug print routines to direct
their output to UART1 serial port on DaVinci DA8XX devices.
config DEBUG_DAVINCI_DA8XX_UART2
bool "Kernel low-level debugging on DaVinci DA8XX using UART2"
depends on ARCH_DAVINCI_DA8XX
help
Say Y here if you want the debug print routines to direct
their output to UART2 serial port on DaVinci DA8XX devices.
config DEBUG_DAVINCI_DMx_UART0
bool "Kernel low-level debugging on DaVinci DMx using UART0"
depends on ARCH_DAVINCI_DMx
help
Say Y here if you want the debug print routines to direct
their output to UART0 serial port on DaVinci DMx devices.
config DEBUG_DAVINCI_TNETV107X_UART1
bool "Kernel low-level debugging on DaVinci TNETV107x using UART1"
depends on ARCH_DAVINCI_TNETV107X
help
Say Y here if you want the debug print routines to direct
their output to UART1 serial port on DaVinci TNETV107X
devices.
config DEBUG_DC21285_PORT
bool "Kernel low-level debugging messages via footbridge serial port"
depends on FOOTBRIDGE
......
......@@ -35,7 +35,7 @@ esdhc@50004000 { /* ESDHC1 */
};
esdhc@50008000 { /* ESDHC2 */
fsl,card-wired;
non-removable;
status = "okay";
};
......@@ -76,7 +76,7 @@ partition@40000 {
};
esdhc@50020000 { /* ESDHC3 */
fsl,card-wired;
non-removable;
status = "okay";
};
};
......
......@@ -41,7 +41,7 @@ usdhc@02198000 { /* uSDHC3 */
};
usdhc@0219c000 { /* uSDHC4 */
fsl,card-wired;
non-removable;
vmmc-supply = <&reg_3p3v>;
status = "okay";
};
......
......@@ -57,7 +57,7 @@ eeprom@50 {
&mmc1 {
vmmc-supply = <&vmmc1>;
vmmc_aux-supply = <&vsim>;
ti,bus-width = <8>;
bus-width = <8>;
};
&mmc2 {
......
......@@ -70,7 +70,7 @@ &i2c4 {
&mmc1 {
vmmc-supply = <&vmmc>;
ti,bus-width = <8>;
bus-width = <8>;
};
&mmc2 {
......@@ -87,5 +87,5 @@ &mmc4 {
&mmc5 {
ti,non-removable;
ti,bus-width = <4>;
bus-width = <4>;
};
......@@ -137,12 +137,12 @@ eth@0 {
&mmc1 {
vmmc-supply = <&vmmc>;
ti,bus-width = <8>;
bus-width = <8>;
};
&mmc2 {
vmmc-supply = <&vaux1>;
ti,bus-width = <8>;
bus-width = <8>;
ti,non-removable;
};
......@@ -155,6 +155,6 @@ &mmc4 {
};
&mmc5 {
ti,bus-width = <4>;
bus-width = <4>;
ti,non-removable;
};
......@@ -51,6 +51,15 @@ sdmmc4_dat0_paa0 {
nvidia,pull = <2>;
nvidia,tristate = <0>;
};
dap2_fs_pa2 {
nvidia,pins = "dap2_fs_pa2",
"dap2_sclk_pa3",
"dap2_din_pa4",
"dap2_dout_pa5";
nvidia,function = "i2s1";
nvidia,pull = <0>;
nvidia,tristate = <0>;
};
};
};
......@@ -92,12 +101,27 @@ i2c@7000c700 {
i2c@7000d000 {
clock-frequency = <100000>;
wm8903: wm8903@1a {
compatible = "wlf,wm8903";
reg = <0x1a>;
interrupt-parent = <&gpio>;
interrupts = <179 0x04>; /* gpio PW3 */
gpio-controller;
#gpio-cells = <2>;
micdet-cfg = <0>;
micdet-delay = <100>;
gpio-cfg = <0xffffffff 0xffffffff 0 0xffffffff 0xffffffff>;
};
};
sdhci@78000000 {
cd-gpios = <&gpio 69 0>; /* gpio PI5 */
wp-gpios = <&gpio 155 0>; /* gpio PT3 */
power-gpios = <&gpio 31 0>; /* gpio PD7 */
bus-width = <4>;
};
sdhci@78000200 {
......@@ -110,5 +134,46 @@ sdhci@78000400 {
sdhci@78000400 {
support-8bit;
bus-width = <8>;
};
ahub@70080000 {
i2s@70080300 {
status = "disable";
};
i2s@70080500 {
status = "disable";
};
i2s@70080600 {
status = "disable";
};
i2s@70080700 {
status = "disable";
};
};
sound {
compatible = "nvidia,tegra-audio-wm8903-cardhu",
"nvidia,tegra-audio-wm8903";
nvidia,model = "NVIDIA Tegra Cardhu";
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 */
};
};
......@@ -167,28 +167,28 @@ uca {
};
conf_ata {
nvidia,pins = "ata", "atb", "atc", "atd", "ate",
"cdev1", "dap1", "dtb", "gma", "gmb",
"gmc", "gmd", "gme", "gpu7", "gpv",
"i2cp", "pta", "rm", "slxa", "slxk",
"spia", "spib";
"cdev1", "cdev2", "dap1", "dtb", "gma",
"gmb", "gmc", "gmd", "gme", "gpu7",
"gpv", "i2cp", "pta", "rm", "slxa",
"slxk", "spia", "spib", "uac";
nvidia,pull = <0>;
nvidia,tristate = <0>;
};
conf_cdev2 {
nvidia,pins = "cdev2", "csus", "spid", "spif";
nvidia,pull = <1>;
nvidia,tristate = <1>;
};
conf_ck32 {
nvidia,pins = "ck32", "ddrc", "pmca", "pmcb",
"pmcc", "pmcd", "pmce", "xm2c", "xm2d";
nvidia,pull = <0>;
};
conf_csus {
nvidia,pins = "csus", "spid", "spif";
nvidia,pull = <1>;
nvidia,tristate = <1>;
};
conf_crtp {
nvidia,pins = "crtp", "dap2", "dap3", "dap4",
"dtc", "dte", "dtf", "gpu", "sdio1",
"slxc", "slxd", "spdi", "spdo", "spig",
"uac", "uda";
"uda";
nvidia,pull = <0>;
nvidia,tristate = <1>;
};
......@@ -324,6 +324,7 @@ sdhci@c8000200 {
cd-gpios = <&gpio 69 0>; /* gpio PI5 */
wp-gpios = <&gpio 57 0>; /* gpio PH1 */
power-gpios = <&gpio 155 0>; /* gpio PT3 */
bus-width = <4>;
};
sdhci@c8000400 {
......@@ -335,5 +336,10 @@ sdhci@c8000600 {
wp-gpios = <&gpio 59 0>; /* gpio PH3 */
power-gpios = <&gpio 70 0>; /* gpio PI6 */
support-8bit;
bus-width = <8>;
};
usb@c5004000 {
nvidia,phy-reset-gpio = <&gpio 169 0>; /* gpio PV1 */
};
};
......@@ -159,18 +159,14 @@ uca {
};
conf_ata {
nvidia,pins = "ata", "atb", "atc", "atd", "ate",
"cdev1", "dap1", "dap2", "dtf", "gma",
"gmb", "gmc", "gmd", "gme", "gpu",
"gpu7", "gpv", "i2cp", "pta", "rm",
"sdio1", "slxk", "spdo", "uac", "uda";
"cdev1", "cdev2", "dap1", "dap2", "dtf",
"gma", "gmb", "gmc", "gmd", "gme",
"gpu", "gpu7", "gpv", "i2cp", "pta",
"rm", "sdio1", "slxk", "spdo", "uac",
"uda";
nvidia,pull = <0>;
nvidia,tristate = <0>;
};
conf_cdev2 {
nvidia,pins = "cdev2";
nvidia,pull = <1>;
nvidia,tristate = <0>;
};
conf_ck32 {
nvidia,pins = "ck32", "ddrc", "pmca", "pmcb",
"pmcc", "pmcd", "pmce", "xm2c", "xm2d";
......@@ -317,6 +313,7 @@ sdhci@c8000000 {
cd-gpios = <&gpio 173 0>; /* gpio PV5 */
wp-gpios = <&gpio 57 0>; /* gpio PH1 */
power-gpios = <&gpio 169 0>; /* gpio PV1 */
bus-width = <4>;
};
sdhci@c8000200 {
......@@ -329,6 +326,7 @@ sdhci@c8000400 {
sdhci@c8000600 {
support-8bit;
bus-width = <8>;
};
gpio-keys {
......@@ -351,4 +349,8 @@ wifi {
linux,default-trigger = "rfkill0";
};
};
usb@c5004000 {
nvidia,phy-reset-gpio = <&gpio 168 0>; /* gpio PV0 */
};
};
......@@ -347,10 +347,12 @@ sdhci@c8000400 {
cd-gpios = <&gpio 69 0>; /* gpio PI5 */
wp-gpios = <&gpio 57 0>; /* gpio PH1 */
power-gpios = <&gpio 70 0>; /* gpio PI6 */
bus-width = <4>;
};
sdhci@c8000600 {
support-8bit;
bus-width = <8>;
};
usb@c5000000 {
......@@ -415,4 +417,8 @@ emc-table@380000 {
0x00000000 0x00000000 0x00000000 0x00000000 >;
};
};
usb@c5004000 {
nvidia,phy-reset-gpio = <&gpio 169 0>; /* gpio PV1 */
};
};
......@@ -182,23 +182,23 @@ conf_ata {
nvidia,tristate = <1>;
};
conf_atb {
nvidia,pins = "atb", "cdev1", "dap1", "gma",
"gmc", "gmd", "gpu", "gpu7", "gpv",
"sdio1", "slxa", "slxk", "uac";
nvidia,pins = "atb", "cdev1", "cdev2", "dap1",
"gma", "gmc", "gmd", "gpu", "gpu7",
"gpv", "sdio1", "slxa", "slxk", "uac";
nvidia,pull = <0>;
nvidia,tristate = <0>;
};
conf_cdev2 {
nvidia,pins = "cdev2", "csus", "spia", "spib",
"spid", "spif";
nvidia,pull = <1>;
nvidia,tristate = <1>;
};
conf_ck32 {
nvidia,pins = "ck32", "ddrc", "pmca", "pmcb",
"pmcc", "pmcd", "pmce", "xm2c", "xm2d";
nvidia,pull = <0>;
};
conf_csus {
nvidia,pins = "csus", "spia", "spib",
"spid", "spif";
nvidia,pull = <1>;
nvidia,tristate = <1>;
};
conf_ddc {
nvidia,pins = "ddc", "dtf", "rm", "sdc", "sdd";
nvidia,pull = <2>;
......@@ -304,4 +304,8 @@ sdhci@c8000600 {
cd-gpios = <&gpio 121 0>;
wp-gpios = <&gpio 122 0>;
};
usb@c5004000 {
nvidia,phy-reset-gpio = <&gpio 168 0>; /* gpio PV0 */
};
};
......@@ -330,9 +330,15 @@ sdhci@c8000400 {
cd-gpios = <&gpio 69 0>; /* gpio PI5 */
wp-gpios = <&gpio 57 0>; /* gpio PH1 */
power-gpios = <&gpio 70 0>; /* gpio PI6 */
bus-width = <4>;
};
sdhci@c8000600 {
support-8bit;
bus-width = <8>;
};
usb@c5004000 {
nvidia,phy-reset-gpio = <&gpio 169 0>; /* gpio PV1 */
};
};
......@@ -183,4 +183,45 @@ pinmux: pinmux@70000000 {
reg = < 0x70000868 0xd0 /* Pad control registers */
0x70003000 0x3e0 >; /* Mux registers */
};
ahub {
compatible = "nvidia,tegra30-ahub";
reg = <0x70080000 0x200 0x70080200 0x100>;
interrupts = < 0 103 0x04 >;
nvidia,dma-request-selector = <&apbdma 1>;
ranges;
#address-cells = <1>;
#size-cells = <1>;
tegra_i2s0: i2s@70080300 {
compatible = "nvidia,tegra30-i2s";
reg = <0x70080300 0x100>;
nvidia,ahub-cif-ids = <4 4>;
};
tegra_i2s1: i2s@70080400 {
compatible = "nvidia,tegra30-i2s";
reg = <0x70080400 0x100>;
nvidia,ahub-cif-ids = <5 5>;
};
tegra_i2s2: i2s@70080500 {
compatible = "nvidia,tegra30-i2s";
reg = <0x70080500 0x100>;
nvidia,ahub-cif-ids = <6 6>;
};
tegra_i2s3: i2s@70080600 {
compatible = "nvidia,tegra30-i2s";
reg = <0x70080600 0x100>;
nvidia,ahub-cif-ids = <7 7>;
};
tegra_i2s4: i2s@70080700 {
compatible = "nvidia,tegra30-i2s";
reg = <0x70080700 0x100>;
nvidia,ahub-cif-ids = <8 8>;
};
};
};
......@@ -353,9 +353,10 @@ static int irq2ctlr(int irq)
*****************************************************************************/
static irqreturn_t dma_irq_handler(int irq, void *data)
{
int i;
int ctlr;
unsigned int cnt = 0;
u32 sh_ier;
u32 sh_ipr;
u32 bank;
ctlr = irq2ctlr(irq);
if (ctlr < 0)
......@@ -363,41 +364,39 @@ static irqreturn_t dma_irq_handler(int irq, void *data)
dev_dbg(data, "dma_irq_handler\n");
if ((edma_shadow0_read_array(ctlr, SH_IPR, 0) == 0) &&
(edma_shadow0_read_array(ctlr, SH_IPR, 1) == 0))
return IRQ_NONE;
sh_ipr = edma_shadow0_read_array(ctlr, SH_IPR, 0);
if (!sh_ipr) {
sh_ipr = edma_shadow0_read_array(ctlr, SH_IPR, 1);
if (!sh_ipr)
return IRQ_NONE;
sh_ier = edma_shadow0_read_array(ctlr, SH_IER, 1);
bank = 1;
} else {
sh_ier = edma_shadow0_read_array(ctlr, SH_IER, 0);
bank = 0;
}
while (1) {
int j;
if (edma_shadow0_read_array(ctlr, SH_IPR, 0) &
edma_shadow0_read_array(ctlr, SH_IER, 0))
j = 0;
else if (edma_shadow0_read_array(ctlr, SH_IPR, 1) &
edma_shadow0_read_array(ctlr, SH_IER, 1))
j = 1;
else
break;
dev_dbg(data, "IPR%d %08x\n", j,
edma_shadow0_read_array(ctlr, SH_IPR, j));
for (i = 0; i < 32; i++) {
int k = (j << 5) + i;
if ((edma_shadow0_read_array(ctlr, SH_IPR, j) & BIT(i))
&& (edma_shadow0_read_array(ctlr,
SH_IER, j) & BIT(i))) {
/* Clear the corresponding IPR bits */
edma_shadow0_write_array(ctlr, SH_ICR, j,
BIT(i));
if (edma_cc[ctlr]->intr_data[k].callback)
edma_cc[ctlr]->intr_data[k].callback(
k, DMA_COMPLETE,
edma_cc[ctlr]->intr_data[k].
data);
}
do {
u32 slot;
u32 channel;
dev_dbg(data, "IPR%d %08x\n", bank, sh_ipr);
slot = __ffs(sh_ipr);
sh_ipr &= ~(BIT(slot));
if (sh_ier & BIT(slot)) {
channel = (bank << 5) | slot;
/* Clear the corresponding IPR bits */
edma_shadow0_write_array(ctlr, SH_ICR, bank,
BIT(slot));
if (edma_cc[ctlr]->intr_data[channel].callback)
edma_cc[ctlr]->intr_data[channel].callback(
channel, DMA_COMPLETE,
edma_cc[ctlr]->intr_data[channel].data);
}
cnt++;
if (cnt > 10)
break;
}
} while (sh_ipr);
edma_shadow0_write(ctlr, SH_IEVAL, 1);
return IRQ_HANDLED;
}
......
......@@ -22,46 +22,28 @@
#define UART_SHIFT 2
.pushsection .data
davinci_uart_phys: .word 0
davinci_uart_virt: .word 0
.popsection
.macro addruart, rp, rv, tmp
/* Use davinci_uart_phys/virt if already configured */
10: adr \rp, 99f @ get effective addr of 99f
ldr \rv, [\rp] @ get absolute addr of 99f
sub \rv, \rv, \rp @ offset between the two
ldr \rp, [\rp, #4] @ abs addr of omap_uart_phys
sub \tmp, \rp, \rv @ make it effective
ldr \rp, [\tmp, #0] @ davinci_uart_phys
ldr \rv, [\tmp, #4] @ davinci_uart_virt
cmp \rp, #0 @ is port configured?
cmpne \rv, #0
bne 100f @ already configured
/* Check the debug UART address set in uncompress.h */
and \rp, pc, #0xff000000
ldr \rv, =DAVINCI_UART_INFO_OFS
add \rp, \rp, \rv
/* Copy uart phys address from decompressor uart info */
ldr \rv, [\rp, #0]
str \rv, [\tmp, #0]
/* Copy uart virt address from decompressor uart info */
ldr \rv, [\rp, #4]
str \rv, [\tmp, #4]
b 10b
#if defined(CONFIG_DEBUG_DAVINCI_DMx_UART0)
#define UART_BASE DAVINCI_UART0_BASE
#elif defined(CONFIG_DEBUG_DAVINCI_DA8XX_UART0)
#define UART_BASE DA8XX_UART0_BASE
#elif defined(CONFIG_DEBUG_DAVINCI_DA8XX_UART1)
#define UART_BASE DA8XX_UART1_BASE
#elif defined(CONFIG_DEBUG_DAVINCI_DA8XX_UART2)
#define UART_BASE DA8XX_UART2_BASE
#elif defined(CONFIG_DEBUG_DAVINCI_TNETV107X_UART1)
#define UART_BASE TNETV107X_UART2_BASE
#define UART_VIRTBASE TNETV107X_UART2_VIRT
#else
#error "Select a specifc port for DEBUG_LL"
#endif
.align
99: .word .
.word davinci_uart_phys
.ltorg
#ifndef UART_VIRTBASE
#define UART_VIRTBASE IO_ADDRESS(UART_BASE)
#endif
100:
.macro addruart, rp, rv, tmp
ldr \rp, =UART_BASE
ldr \rv, =UART_VIRTBASE
.endm
.macro senduart,rd,rx
......
......@@ -22,7 +22,7 @@
/*
* I/O mapping
*/
#define IO_PHYS 0x01c00000UL
#define IO_PHYS UL(0x01c00000)
#define IO_OFFSET 0xfd000000 /* Virtual IO = 0xfec00000 */
#define IO_SIZE 0x00400000
#define IO_VIRT (IO_PHYS + IO_OFFSET)
......
......@@ -15,16 +15,6 @@
#include <mach/hardware.h>
/*
* Stolen area that contains debug uart physical and virtual addresses. These
* addresses are filled in by the uncompress.h code, and are used by the debug
* macros in debug-macro.S.
*
* This area sits just below the page tables (see arch/arm/kernel/head.S).
* We define it as a relative offset from start of usable RAM.
*/
#define DAVINCI_UART_INFO_OFS 0x3ff8
#define DAVINCI_UART0_BASE (IO_PHYS + 0x20000)
#define DAVINCI_UART1_BASE (IO_PHYS + 0x20400)
#define DAVINCI_UART2_BASE (IO_PHYS + 0x20800)
......
......@@ -43,37 +43,27 @@ static inline void flush(void)
barrier();
}
static inline void set_uart_info(u32 phys, void * __iomem virt)
static inline void set_uart_info(u32 phys)
{
/*
* Get address of some.bss variable and round it down
* a la CONFIG_AUTO_ZRELADDR.
*/
u32 ram_start = (u32)&uart & 0xf8000000;
u32 *uart_info = (u32 *)(ram_start + DAVINCI_UART_INFO_OFS);
uart = (u32 *)phys;
uart_info[0] = phys;
uart_info[1] = (u32)virt;
}
#define _DEBUG_LL_ENTRY(machine, phys, virt) \
if (machine_is_##machine()) { \
set_uart_info(phys, virt); \
break; \
#define _DEBUG_LL_ENTRY(machine, phys) \
{ \
if (machine_is_##machine()) { \
set_uart_info(phys); \
break; \
} \
}
#define DEBUG_LL_DAVINCI(machine, port) \
_DEBUG_LL_ENTRY(machine, DAVINCI_UART##port##_BASE, \
IO_ADDRESS(DAVINCI_UART##port##_BASE))
_DEBUG_LL_ENTRY(machine, DAVINCI_UART##port##_BASE)
#define DEBUG_LL_DA8XX(machine, port) \
_DEBUG_LL_ENTRY(machine, DA8XX_UART##port##_BASE, \
IO_ADDRESS(DA8XX_UART##port##_BASE))
_DEBUG_LL_ENTRY(machine, DA8XX_UART##port##_BASE)
#define DEBUG_LL_TNETV107X(machine, port) \
_DEBUG_LL_ENTRY(machine, TNETV107X_UART##port##_BASE, \
TNETV107X_UART##port##_VIRT)
_DEBUG_LL_ENTRY(machine, TNETV107X_UART##port##_BASE)
static inline void __arch_decomp_setup(unsigned long arch_id)
{
......
......@@ -675,7 +675,7 @@ int ep93xx_keypad_acquire_gpio(struct platform_device *pdev)
fail_gpio_d:
gpio_free(EP93XX_GPIO_LINE_C(i));
fail_gpio_c:
for ( ; i >= 0; --i) {
for (--i; i >= 0; --i) {
gpio_free(EP93XX_GPIO_LINE_C(i));
gpio_free(EP93XX_GPIO_LINE_D(i));
}
......
......@@ -85,10 +85,10 @@ config EXYNOS4_SETUP_FIMD0
help
Common setup code for FIMD0.
config EXYNOS4_DEV_SYSMMU
config EXYNOS_DEV_SYSMMU
bool
help
Common setup code for SYSTEM MMU in EXYNOS4
Common setup code for SYSTEM MMU in EXYNOS platforms
config EXYNOS4_DEV_DWMCI
bool
......@@ -201,12 +201,12 @@ config MACH_SMDKV310
select S3C_DEV_HSMMC3
select SAMSUNG_DEV_BACKLIGHT
select EXYNOS_DEV_DRM
select EXYNOS_DEV_SYSMMU
select EXYNOS4_DEV_AHCI
select SAMSUNG_DEV_KEYPAD
select EXYNOS4_DEV_DMA
select SAMSUNG_DEV_PWM
select EXYNOS4_DEV_USB_OHCI
select EXYNOS4_DEV_SYSMMU
select EXYNOS4_SETUP_FIMD0
select EXYNOS4_SETUP_I2C1
select EXYNOS4_SETUP_KEYPAD
......@@ -225,7 +225,6 @@ config MACH_ARMLEX4210
select S3C_DEV_HSMMC3
select EXYNOS4_DEV_AHCI
select EXYNOS4_DEV_DMA
select EXYNOS4_DEV_SYSMMU
select EXYNOS4_SETUP_SDHCI
help
Machine support for Samsung ARMLEX4210 based on EXYNOS4210
......@@ -256,6 +255,7 @@ config MACH_UNIVERSAL_C210
select S5P_DEV_MFC
select S5P_DEV_ONENAND
select S5P_DEV_TV
select EXYNOS_DEV_SYSMMU
select EXYNOS4_DEV_DMA
select EXYNOS_DEV_DRM
select EXYNOS4_SETUP_FIMD0
......@@ -332,6 +332,7 @@ config MACH_ORIGEN
select SAMSUNG_DEV_BACKLIGHT
select SAMSUNG_DEV_PWM
select EXYNOS_DEV_DRM
select EXYNOS_DEV_SYSMMU
select EXYNOS4_DEV_DMA
select EXYNOS4_DEV_USB_OHCI
select EXYNOS4_SETUP_FIMD0
......@@ -360,6 +361,7 @@ config MACH_SMDK4212
select SAMSUNG_DEV_BACKLIGHT
select SAMSUNG_DEV_KEYPAD
select SAMSUNG_DEV_PWM
select EXYNOS_DEV_SYSMMU
select EXYNOS4_DEV_DMA
select EXYNOS4_SETUP_I2C1
select EXYNOS4_SETUP_I2C3
......
......@@ -50,7 +50,7 @@ obj-$(CONFIG_MACH_EXYNOS5_DT) += mach-exynos5-dt.o
obj-y += dev-uart.o
obj-$(CONFIG_ARCH_EXYNOS4) += dev-audio.o
obj-$(CONFIG_EXYNOS4_DEV_AHCI) += dev-ahci.o
obj-$(CONFIG_EXYNOS4_DEV_SYSMMU) += dev-sysmmu.o
obj-$(CONFIG_EXYNOS_DEV_SYSMMU) += dev-sysmmu.o
obj-$(CONFIG_EXYNOS4_DEV_DWMCI) += dev-dwmci.o
obj-$(CONFIG_EXYNOS4_DEV_DMA) += dma.o
obj-$(CONFIG_EXYNOS4_DEV_USB_OHCI) += dev-ohci.o
......
......@@ -168,7 +168,7 @@ static int exynos4_clk_ip_tv_ctrl(struct clk *clk, int enable)
return s5p_gatectrl(EXYNOS4_CLKGATE_IP_TV, clk, enable);
}
static int exynos4_clk_ip_image_ctrl(struct clk *clk, int enable)
int exynos4_clk_ip_image_ctrl(struct clk *clk, int enable)
{
return s5p_gatectrl(EXYNOS4_CLKGATE_IP_IMAGE, clk, enable);
}
......@@ -198,6 +198,11 @@ static int exynos4_clk_ip_perir_ctrl(struct clk *clk, int enable)
return s5p_gatectrl(EXYNOS4_CLKGATE_IP_PERIR, clk, enable);
}
int exynos4_clk_ip_dmc_ctrl(struct clk *clk, int enable)
{
return s5p_gatectrl(EXYNOS4_CLKGATE_IP_DMC, clk, enable);
}
static int exynos4_clk_hdmiphy_ctrl(struct clk *clk, int enable)
{
return s5p_gatectrl(S5P_HDMI_PHY_CONTROL, clk, enable);
......@@ -678,61 +683,55 @@ static struct clk exynos4_init_clocks_off[] = {
.enable = exynos4_clk_ip_peril_ctrl,
.ctrlbit = (1 << 14),
}, {
.name = "SYSMMU_MDMA",
.name = SYSMMU_CLOCK_NAME,
.devname = SYSMMU_CLOCK_DEVNAME(mfc_l, 0),
.enable = exynos4_clk_ip_mfc_ctrl,
.ctrlbit = (1 << 1),
}, {
.name = SYSMMU_CLOCK_NAME,
.devname = SYSMMU_CLOCK_DEVNAME(mfc_r, 1),
.enable = exynos4_clk_ip_mfc_ctrl,
.ctrlbit = (1 << 2),
}, {
.name = SYSMMU_CLOCK_NAME,
.devname = SYSMMU_CLOCK_DEVNAME(tv, 2),
.enable = exynos4_clk_ip_tv_ctrl,
.ctrlbit = (1 << 4),
}, {
.name = SYSMMU_CLOCK_NAME,
.devname = SYSMMU_CLOCK_DEVNAME(jpeg, 3),
.enable = exynos4_clk_ip_cam_ctrl,
.ctrlbit = (1 << 11),
}, {
.name = SYSMMU_CLOCK_NAME,
.devname = SYSMMU_CLOCK_DEVNAME(rot, 4),
.enable = exynos4_clk_ip_image_ctrl,
.ctrlbit = (1 << 5),
.ctrlbit = (1 << 4),
}, {
.name = "SYSMMU_FIMC0",
.name = SYSMMU_CLOCK_NAME,
.devname = SYSMMU_CLOCK_DEVNAME(fimc0, 5),
.enable = exynos4_clk_ip_cam_ctrl,
.ctrlbit = (1 << 7),
}, {
.name = "SYSMMU_FIMC1",
.name = SYSMMU_CLOCK_NAME,
.devname = SYSMMU_CLOCK_DEVNAME(fimc1, 6),
.enable = exynos4_clk_ip_cam_ctrl,
.ctrlbit = (1 << 8),
}, {
.name = "SYSMMU_FIMC2",
.name = SYSMMU_CLOCK_NAME,
.devname = SYSMMU_CLOCK_DEVNAME(fimc2, 7),
.enable = exynos4_clk_ip_cam_ctrl,
.ctrlbit = (1 << 9),
}, {
.name = "SYSMMU_FIMC3",
.name = SYSMMU_CLOCK_NAME,
.devname = SYSMMU_CLOCK_DEVNAME(fimc3, 8),
.enable = exynos4_clk_ip_cam_ctrl,
.ctrlbit = (1 << 10),
}, {
.name = "SYSMMU_JPEG",
.enable = exynos4_clk_ip_cam_ctrl,
.ctrlbit = (1 << 11),
}, {
.name = "SYSMMU_FIMD0",
.name = SYSMMU_CLOCK_NAME,
.devname = SYSMMU_CLOCK_DEVNAME(fimd0, 10),
.enable = exynos4_clk_ip_lcd0_ctrl,
.ctrlbit = (1 << 4),
}, {
.name = "SYSMMU_FIMD1",
.enable = exynos4_clk_ip_lcd1_ctrl,
.ctrlbit = (1 << 4),
}, {
.name = "SYSMMU_PCIe",
.enable = exynos4_clk_ip_fsys_ctrl,
.ctrlbit = (1 << 18),
}, {
.name = "SYSMMU_G2D",
.enable = exynos4_clk_ip_image_ctrl,
.ctrlbit = (1 << 3),
}, {
.name = "SYSMMU_ROTATOR",
.enable = exynos4_clk_ip_image_ctrl,
.ctrlbit = (1 << 4),
}, {
.name = "SYSMMU_TV",
.enable = exynos4_clk_ip_tv_ctrl,
.ctrlbit = (1 << 4),
}, {
.name = "SYSMMU_MFC_L",
.enable = exynos4_clk_ip_mfc_ctrl,
.ctrlbit = (1 << 1),
}, {
.name = "SYSMMU_MFC_R",
.enable = exynos4_clk_ip_mfc_ctrl,
.ctrlbit = (1 << 2),
}
};
......
......@@ -26,5 +26,7 @@ extern struct clk *exynos4_clkset_group_list[];
extern int exynos4_clksrc_mask_fsys_ctrl(struct clk *clk, int enable);
extern int exynos4_clk_ip_fsys_ctrl(struct clk *clk, int enable);
extern int exynos4_clk_ip_lcd1_ctrl(struct clk *clk, int enable);
extern int exynos4_clk_ip_image_ctrl(struct clk *clk, int enable);
extern int exynos4_clk_ip_dmc_ctrl(struct clk *clk, int enable);
#endif /* __ASM_ARCH_CLOCK_H */
......@@ -26,6 +26,7 @@
#include <mach/hardware.h>
#include <mach/map.h>
#include <mach/regs-clock.h>
#include <mach/sysmmu.h>
#include "common.h"
#include "clock-exynos4.h"
......@@ -94,6 +95,16 @@ static struct clk init_clocks_off[] = {
.devname = "exynos4-fb.1",
.enable = exynos4_clk_ip_lcd1_ctrl,
.ctrlbit = (1 << 0),
}, {
.name = SYSMMU_CLOCK_NAME,
.devname = SYSMMU_CLOCK_DEVNAME(2d, 14),
.enable = exynos4_clk_ip_image_ctrl,
.ctrlbit = (1 << 3),
}, {
.name = SYSMMU_CLOCK_NAME,
.devname = SYSMMU_CLOCK_DEVNAME(fimd1, 11),
.enable = exynos4_clk_ip_lcd1_ctrl,
.ctrlbit = (1 << 4),
},
};
......
......@@ -26,6 +26,7 @@
#include <mach/hardware.h>
#include <mach/map.h>
#include <mach/regs-clock.h>
#include <mach/sysmmu.h>
#include "common.h"
#include "clock-exynos4.h"
......@@ -39,6 +40,16 @@ static struct sleep_save exynos4212_clock_save[] = {
};
#endif
static int exynos4212_clk_ip_isp0_ctrl(struct clk *clk, int enable)
{
return s5p_gatectrl(EXYNOS4_CLKGATE_IP_ISP0, clk, enable);
}
static int exynos4212_clk_ip_isp1_ctrl(struct clk *clk, int enable)
{
return s5p_gatectrl(EXYNOS4_CLKGATE_IP_ISP1, clk, enable);
}
static struct clk *clk_src_mpll_user_list[] = {
[0] = &clk_fin_mpll,
[1] = &exynos4_clk_mout_mpll.clk,
......@@ -66,7 +77,22 @@ static struct clksrc_clk clksrcs[] = {
};
static struct clk init_clocks_off[] = {
/* nothing here yet */
{
.name = SYSMMU_CLOCK_NAME,
.devname = SYSMMU_CLOCK_DEVNAME(2d, 14),
.enable = exynos4_clk_ip_dmc_ctrl,
.ctrlbit = (1 << 24),
}, {
.name = SYSMMU_CLOCK_NAME,
.devname = SYSMMU_CLOCK_DEVNAME(isp, 9),
.enable = exynos4212_clk_ip_isp0_ctrl,
.ctrlbit = (7 << 8),
}, {
.name = SYSMMU_CLOCK_NAME2,
.devname = SYSMMU_CLOCK_DEVNAME(isp, 9),
.enable = exynos4212_clk_ip_isp1_ctrl,
.ctrlbit = (1 << 4),
}
};
#ifdef CONFIG_PM_SLEEP
......
......@@ -82,6 +82,11 @@ static int exynos5_clksrc_mask_peric0_ctrl(struct clk *clk, int enable)
return s5p_gatectrl(EXYNOS5_CLKSRC_MASK_PERIC0, clk, enable);
}
static int exynos5_clk_ip_acp_ctrl(struct clk *clk, int enable)
{
return s5p_gatectrl(EXYNOS5_CLKGATE_IP_ACP, clk, enable);
}
static int exynos5_clk_ip_core_ctrl(struct clk *clk, int enable)
{
return s5p_gatectrl(EXYNOS5_CLKGATE_IP_CORE, clk, enable);
......@@ -127,6 +132,21 @@ static int exynos5_clk_ip_peris_ctrl(struct clk *clk, int enable)
return s5p_gatectrl(EXYNOS5_CLKGATE_IP_PERIS, clk, enable);
}
static int exynos5_clk_ip_gscl_ctrl(struct clk *clk, int enable)
{
return s5p_gatectrl(EXYNOS5_CLKGATE_IP_GSCL, clk, enable);
}
static int exynos5_clk_ip_isp0_ctrl(struct clk *clk, int enable)
{
return s5p_gatectrl(EXYNOS5_CLKGATE_IP_ISP0, clk, enable);
}
static int exynos5_clk_ip_isp1_ctrl(struct clk *clk, int enable)
{
return s5p_gatectrl(EXYNOS5_CLKGATE_IP_ISP1, clk, enable);
}
/* Core list of CMU_CPU side */
static struct clksrc_clk exynos5_clk_mout_apll = {
......@@ -630,6 +650,76 @@ static struct clk exynos5_init_clocks_off[] = {
.parent = &exynos5_clk_aclk_66.clk,
.enable = exynos5_clk_ip_peric_ctrl,
.ctrlbit = (1 << 14),
}, {
.name = SYSMMU_CLOCK_NAME,
.devname = SYSMMU_CLOCK_DEVNAME(mfc_l, 0),
.enable = &exynos5_clk_ip_mfc_ctrl,
.ctrlbit = (1 << 1),
}, {
.name = SYSMMU_CLOCK_NAME,
.devname = SYSMMU_CLOCK_DEVNAME(mfc_r, 1),
.enable = &exynos5_clk_ip_mfc_ctrl,
.ctrlbit = (1 << 2),
}, {
.name = SYSMMU_CLOCK_NAME,
.devname = SYSMMU_CLOCK_DEVNAME(tv, 2),
.enable = &exynos5_clk_ip_disp1_ctrl,
.ctrlbit = (1 << 9)
}, {
.name = SYSMMU_CLOCK_NAME,
.devname = SYSMMU_CLOCK_DEVNAME(jpeg, 3),
.enable = &exynos5_clk_ip_gen_ctrl,
.ctrlbit = (1 << 7),
}, {
.name = SYSMMU_CLOCK_NAME,
.devname = SYSMMU_CLOCK_DEVNAME(rot, 4),
.enable = &exynos5_clk_ip_gen_ctrl,
.ctrlbit = (1 << 6)
}, {
.name = SYSMMU_CLOCK_NAME,
.devname = SYSMMU_CLOCK_DEVNAME(gsc0, 5),
.enable = &exynos5_clk_ip_gscl_ctrl,
.ctrlbit = (1 << 7),
}, {
.name = SYSMMU_CLOCK_NAME,
.devname = SYSMMU_CLOCK_DEVNAME(gsc1, 6),
.enable = &exynos5_clk_ip_gscl_ctrl,
.ctrlbit = (1 << 8),
}, {
.name = SYSMMU_CLOCK_NAME,
.devname = SYSMMU_CLOCK_DEVNAME(gsc2, 7),
.enable = &exynos5_clk_ip_gscl_ctrl,
.ctrlbit = (1 << 9),
}, {
.name = SYSMMU_CLOCK_NAME,
.devname = SYSMMU_CLOCK_DEVNAME(gsc3, 8),
.enable = &exynos5_clk_ip_gscl_ctrl,
.ctrlbit = (1 << 10),
}, {
.name = SYSMMU_CLOCK_NAME,
.devname = SYSMMU_CLOCK_DEVNAME(isp, 9),
.enable = &exynos5_clk_ip_isp0_ctrl,
.ctrlbit = (0x3F << 8),
}, {
.name = SYSMMU_CLOCK_NAME2,
.devname = SYSMMU_CLOCK_DEVNAME(isp, 9),
.enable = &exynos5_clk_ip_isp1_ctrl,
.ctrlbit = (0xF << 4),
}, {
.name = SYSMMU_CLOCK_NAME,
.devname = SYSMMU_CLOCK_DEVNAME(camif0, 12),
.enable = &exynos5_clk_ip_gscl_ctrl,
.ctrlbit = (1 << 11),
}, {
.name = SYSMMU_CLOCK_NAME,
.devname = SYSMMU_CLOCK_DEVNAME(camif1, 13),
.enable = &exynos5_clk_ip_gscl_ctrl,
.ctrlbit = (1 << 12),
}, {
.name = SYSMMU_CLOCK_NAME,
.devname = SYSMMU_CLOCK_DEVNAME(2d, 14),
.enable = &exynos5_clk_ip_acp_ctrl,
.ctrlbit = (1 << 7)
}
};
......
/* linux/arch/arm/mach-exynos4/dev-sysmmu.c
/* linux/arch/arm/mach-exynos/dev-sysmmu.c
*
* Copyright (c) 2010 Samsung Electronics Co., Ltd.
* Copyright (c) 2010-2012 Samsung Electronics Co., Ltd.
* http://www.samsung.com
*
* EXYNOS4 - System MMU support
* EXYNOS - System MMU support
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
......@@ -12,222 +12,263 @@
#include <linux/platform_device.h>
#include <linux/dma-mapping.h>
#include <linux/export.h>
#include <plat/cpu.h>
#include <mach/map.h>
#include <mach/irqs.h>
#include <mach/sysmmu.h>
#include <plat/s5p-clock.h>
/* These names must be equal to the clock names in mach-exynos4/clock.c */
const char *sysmmu_ips_name[EXYNOS4_SYSMMU_TOTAL_IPNUM] = {
"SYSMMU_MDMA" ,
"SYSMMU_SSS" ,
"SYSMMU_FIMC0" ,
"SYSMMU_FIMC1" ,
"SYSMMU_FIMC2" ,
"SYSMMU_FIMC3" ,
"SYSMMU_JPEG" ,
"SYSMMU_FIMD0" ,
"SYSMMU_FIMD1" ,
"SYSMMU_PCIe" ,
"SYSMMU_G2D" ,
"SYSMMU_ROTATOR",
"SYSMMU_MDMA2" ,
"SYSMMU_TV" ,
"SYSMMU_MFC_L" ,
"SYSMMU_MFC_R" ,
};
static struct resource exynos4_sysmmu_resource[] = {
[0] = {
.start = EXYNOS4_PA_SYSMMU_MDMA,
.end = EXYNOS4_PA_SYSMMU_MDMA + SZ_64K - 1,
.flags = IORESOURCE_MEM,
},
[1] = {
.start = IRQ_SYSMMU_MDMA0_0,
.end = IRQ_SYSMMU_MDMA0_0,
.flags = IORESOURCE_IRQ,
},
[2] = {
.start = EXYNOS4_PA_SYSMMU_SSS,
.end = EXYNOS4_PA_SYSMMU_SSS + SZ_64K - 1,
.flags = IORESOURCE_MEM,
},
[3] = {
.start = IRQ_SYSMMU_SSS_0,
.end = IRQ_SYSMMU_SSS_0,
.flags = IORESOURCE_IRQ,
},
[4] = {
.start = EXYNOS4_PA_SYSMMU_FIMC0,
.end = EXYNOS4_PA_SYSMMU_FIMC0 + SZ_64K - 1,
.flags = IORESOURCE_MEM,
},
[5] = {
.start = IRQ_SYSMMU_FIMC0_0,
.end = IRQ_SYSMMU_FIMC0_0,
.flags = IORESOURCE_IRQ,
},
[6] = {
.start = EXYNOS4_PA_SYSMMU_FIMC1,
.end = EXYNOS4_PA_SYSMMU_FIMC1 + SZ_64K - 1,
.flags = IORESOURCE_MEM,
},
[7] = {
.start = IRQ_SYSMMU_FIMC1_0,
.end = IRQ_SYSMMU_FIMC1_0,
.flags = IORESOURCE_IRQ,
},
[8] = {
.start = EXYNOS4_PA_SYSMMU_FIMC2,
.end = EXYNOS4_PA_SYSMMU_FIMC2 + SZ_64K - 1,
.flags = IORESOURCE_MEM,
},
[9] = {
.start = IRQ_SYSMMU_FIMC2_0,
.end = IRQ_SYSMMU_FIMC2_0,
.flags = IORESOURCE_IRQ,
},
[10] = {
.start = EXYNOS4_PA_SYSMMU_FIMC3,
.end = EXYNOS4_PA_SYSMMU_FIMC3 + SZ_64K - 1,
.flags = IORESOURCE_MEM,
},
[11] = {
.start = IRQ_SYSMMU_FIMC3_0,
.end = IRQ_SYSMMU_FIMC3_0,
.flags = IORESOURCE_IRQ,
},
[12] = {
.start = EXYNOS4_PA_SYSMMU_JPEG,
.end = EXYNOS4_PA_SYSMMU_JPEG + SZ_64K - 1,
.flags = IORESOURCE_MEM,
},
[13] = {
.start = IRQ_SYSMMU_JPEG_0,
.end = IRQ_SYSMMU_JPEG_0,
.flags = IORESOURCE_IRQ,
},
[14] = {
.start = EXYNOS4_PA_SYSMMU_FIMD0,
.end = EXYNOS4_PA_SYSMMU_FIMD0 + SZ_64K - 1,
.flags = IORESOURCE_MEM,
},
[15] = {
.start = IRQ_SYSMMU_LCD0_M0_0,
.end = IRQ_SYSMMU_LCD0_M0_0,
.flags = IORESOURCE_IRQ,
},
[16] = {
.start = EXYNOS4_PA_SYSMMU_FIMD1,
.end = EXYNOS4_PA_SYSMMU_FIMD1 + SZ_64K - 1,
.flags = IORESOURCE_MEM,
},
[17] = {
.start = IRQ_SYSMMU_LCD1_M1_0,
.end = IRQ_SYSMMU_LCD1_M1_0,
.flags = IORESOURCE_IRQ,
},
[18] = {
.start = EXYNOS4_PA_SYSMMU_PCIe,
.end = EXYNOS4_PA_SYSMMU_PCIe + SZ_64K - 1,
.flags = IORESOURCE_MEM,
},
[19] = {
.start = IRQ_SYSMMU_PCIE_0,
.end = IRQ_SYSMMU_PCIE_0,
.flags = IORESOURCE_IRQ,
},
[20] = {
.start = EXYNOS4_PA_SYSMMU_G2D,
.end = EXYNOS4_PA_SYSMMU_G2D + SZ_64K - 1,
.flags = IORESOURCE_MEM,
},
[21] = {
.start = IRQ_SYSMMU_2D_0,
.end = IRQ_SYSMMU_2D_0,
.flags = IORESOURCE_IRQ,
},
[22] = {
.start = EXYNOS4_PA_SYSMMU_ROTATOR,
.end = EXYNOS4_PA_SYSMMU_ROTATOR + SZ_64K - 1,
.flags = IORESOURCE_MEM,
},
[23] = {
.start = IRQ_SYSMMU_ROTATOR_0,
.end = IRQ_SYSMMU_ROTATOR_0,
.flags = IORESOURCE_IRQ,
},
[24] = {
.start = EXYNOS4_PA_SYSMMU_MDMA2,
.end = EXYNOS4_PA_SYSMMU_MDMA2 + SZ_64K - 1,
.flags = IORESOURCE_MEM,
},
[25] = {
.start = IRQ_SYSMMU_MDMA1_0,
.end = IRQ_SYSMMU_MDMA1_0,
.flags = IORESOURCE_IRQ,
},
[26] = {
.start = EXYNOS4_PA_SYSMMU_TV,
.end = EXYNOS4_PA_SYSMMU_TV + SZ_64K - 1,
.flags = IORESOURCE_MEM,
},
[27] = {
.start = IRQ_SYSMMU_TV_M0_0,
.end = IRQ_SYSMMU_TV_M0_0,
.flags = IORESOURCE_IRQ,
},
[28] = {
.start = EXYNOS4_PA_SYSMMU_MFC_L,
.end = EXYNOS4_PA_SYSMMU_MFC_L + SZ_64K - 1,
.flags = IORESOURCE_MEM,
},
[29] = {
.start = IRQ_SYSMMU_MFC_M0_0,
.end = IRQ_SYSMMU_MFC_M0_0,
.flags = IORESOURCE_IRQ,
},
[30] = {
.start = EXYNOS4_PA_SYSMMU_MFC_R,
.end = EXYNOS4_PA_SYSMMU_MFC_R + SZ_64K - 1,
.flags = IORESOURCE_MEM,
},
[31] = {
.start = IRQ_SYSMMU_MFC_M1_0,
.end = IRQ_SYSMMU_MFC_M1_0,
.flags = IORESOURCE_IRQ,
},
};
static u64 exynos_sysmmu_dma_mask = DMA_BIT_MASK(32);
#define SYSMMU_PLATFORM_DEVICE(ipname, devid) \
static struct sysmmu_platform_data platdata_##ipname = { \
.dbgname = #ipname, \
}; \
struct platform_device SYSMMU_PLATDEV(ipname) = \
{ \
.name = SYSMMU_DEVNAME_BASE, \
.id = devid, \
.dev = { \
.dma_mask = &exynos_sysmmu_dma_mask, \
.coherent_dma_mask = DMA_BIT_MASK(32), \
.platform_data = &platdata_##ipname, \
}, \
}
SYSMMU_PLATFORM_DEVICE(mfc_l, 0);
SYSMMU_PLATFORM_DEVICE(mfc_r, 1);
SYSMMU_PLATFORM_DEVICE(tv, 2);
SYSMMU_PLATFORM_DEVICE(jpeg, 3);
SYSMMU_PLATFORM_DEVICE(rot, 4);
SYSMMU_PLATFORM_DEVICE(fimc0, 5); /* fimc* and gsc* exist exclusively */
SYSMMU_PLATFORM_DEVICE(fimc1, 6);
SYSMMU_PLATFORM_DEVICE(fimc2, 7);
SYSMMU_PLATFORM_DEVICE(fimc3, 8);
SYSMMU_PLATFORM_DEVICE(gsc0, 5);
SYSMMU_PLATFORM_DEVICE(gsc1, 6);
SYSMMU_PLATFORM_DEVICE(gsc2, 7);
SYSMMU_PLATFORM_DEVICE(gsc3, 8);
SYSMMU_PLATFORM_DEVICE(isp, 9);
SYSMMU_PLATFORM_DEVICE(fimd0, 10);
SYSMMU_PLATFORM_DEVICE(fimd1, 11);
SYSMMU_PLATFORM_DEVICE(camif0, 12);
SYSMMU_PLATFORM_DEVICE(camif1, 13);
SYSMMU_PLATFORM_DEVICE(2d, 14);
#define SYSMMU_RESOURCE_NAME(core, ipname) sysmmures_##core##_##ipname
#define SYSMMU_RESOURCE(core, ipname) \
static struct resource SYSMMU_RESOURCE_NAME(core, ipname)[] __initdata =
#define DEFINE_SYSMMU_RESOURCE(core, mem, irq) \
DEFINE_RES_MEM_NAMED(core##_PA_SYSMMU_##mem, SZ_4K, #mem), \
DEFINE_RES_IRQ_NAMED(core##_IRQ_SYSMMU_##irq##_0, #mem)
#define SYSMMU_RESOURCE_DEFINE(core, ipname, mem, irq) \
SYSMMU_RESOURCE(core, ipname) { \
DEFINE_SYSMMU_RESOURCE(core, mem, irq) \
}
struct platform_device exynos4_device_sysmmu = {
.name = "s5p-sysmmu",
.id = 32,
.num_resources = ARRAY_SIZE(exynos4_sysmmu_resource),
.resource = exynos4_sysmmu_resource,
struct sysmmu_resource_map {
struct platform_device *pdev;
struct resource *res;
u32 rnum;
struct device *pdd;
char *clocknames;
};
EXPORT_SYMBOL(exynos4_device_sysmmu);
static struct clk *sysmmu_clk[S5P_SYSMMU_TOTAL_IPNUM];
void sysmmu_clk_init(struct device *dev, sysmmu_ips ips)
{
sysmmu_clk[ips] = clk_get(dev, sysmmu_ips_name[ips]);
if (IS_ERR(sysmmu_clk[ips]))
sysmmu_clk[ips] = NULL;
else
clk_put(sysmmu_clk[ips]);
#define SYSMMU_RESOURCE_MAPPING(core, ipname, resname) { \
.pdev = &SYSMMU_PLATDEV(ipname), \
.res = SYSMMU_RESOURCE_NAME(EXYNOS##core, resname), \
.rnum = ARRAY_SIZE(SYSMMU_RESOURCE_NAME(EXYNOS##core, resname)),\
.clocknames = SYSMMU_CLOCK_NAME, \
}
void sysmmu_clk_enable(sysmmu_ips ips)
{
if (sysmmu_clk[ips])
clk_enable(sysmmu_clk[ips]);
#define SYSMMU_RESOURCE_MAPPING_MC(core, ipname, resname, pdata) { \
.pdev = &SYSMMU_PLATDEV(ipname), \
.res = SYSMMU_RESOURCE_NAME(EXYNOS##core, resname), \
.rnum = ARRAY_SIZE(SYSMMU_RESOURCE_NAME(EXYNOS##core, resname)),\
.clocknames = SYSMMU_CLOCK_NAME "," SYSMMU_CLOCK_NAME2, \
}
#ifdef CONFIG_EXYNOS_DEV_PD
#define SYSMMU_RESOURCE_MAPPING_PD(core, ipname, resname, pd) { \
.pdev = &SYSMMU_PLATDEV(ipname), \
.res = &SYSMMU_RESOURCE_NAME(EXYNOS##core, resname), \
.rnum = ARRAY_SIZE(SYSMMU_RESOURCE_NAME(EXYNOS##core, resname)),\
.clocknames = SYSMMU_CLOCK_NAME, \
.pdd = &exynos##core##_device_pd[pd].dev, \
}
#define SYSMMU_RESOURCE_MAPPING_MCPD(core, ipname, resname, pd, pdata) {\
.pdev = &SYSMMU_PLATDEV(ipname), \
.res = &SYSMMU_RESOURCE_NAME(EXYNOS##core, resname), \
.rnum = ARRAY_SIZE(SYSMMU_RESOURCE_NAME(EXYNOS##core, resname)),\
.clocknames = SYSMMU_CLOCK_NAME "," SYSMMU_CLOCK_NAME2, \
.pdd = &exynos##core##_device_pd[pd].dev, \
}
#else
#define SYSMMU_RESOURCE_MAPPING_PD(core, ipname, resname, pd) \
SYSMMU_RESOURCE_MAPPING(core, ipname, resname)
#define SYSMMU_RESOURCE_MAPPING_MCPD(core, ipname, resname, pd, pdata) \
SYSMMU_RESOURCE_MAPPING_MC(core, ipname, resname, pdata)
#endif /* CONFIG_EXYNOS_DEV_PD */
#ifdef CONFIG_ARCH_EXYNOS4
SYSMMU_RESOURCE_DEFINE(EXYNOS4, fimc0, FIMC0, FIMC0);
SYSMMU_RESOURCE_DEFINE(EXYNOS4, fimc1, FIMC1, FIMC1);
SYSMMU_RESOURCE_DEFINE(EXYNOS4, fimc2, FIMC2, FIMC2);
SYSMMU_RESOURCE_DEFINE(EXYNOS4, fimc3, FIMC3, FIMC3);
SYSMMU_RESOURCE_DEFINE(EXYNOS4, jpeg, JPEG, JPEG);
SYSMMU_RESOURCE_DEFINE(EXYNOS4, 2d, G2D, 2D);
SYSMMU_RESOURCE_DEFINE(EXYNOS4, tv, TV, TV_M0);
SYSMMU_RESOURCE_DEFINE(EXYNOS4, 2d_acp, 2D_ACP, 2D);
SYSMMU_RESOURCE_DEFINE(EXYNOS4, rot, ROTATOR, ROTATOR);
SYSMMU_RESOURCE_DEFINE(EXYNOS4, fimd0, FIMD0, LCD0_M0);
SYSMMU_RESOURCE_DEFINE(EXYNOS4, fimd1, FIMD1, LCD1_M1);
SYSMMU_RESOURCE_DEFINE(EXYNOS4, flite0, FIMC_LITE0, FIMC_LITE0);
SYSMMU_RESOURCE_DEFINE(EXYNOS4, flite1, FIMC_LITE1, FIMC_LITE1);
SYSMMU_RESOURCE_DEFINE(EXYNOS4, mfc_r, MFC_R, MFC_M0);
SYSMMU_RESOURCE_DEFINE(EXYNOS4, mfc_l, MFC_L, MFC_M1);
SYSMMU_RESOURCE(EXYNOS4, isp) {
DEFINE_SYSMMU_RESOURCE(EXYNOS4, FIMC_ISP, FIMC_ISP),
DEFINE_SYSMMU_RESOURCE(EXYNOS4, FIMC_DRC, FIMC_DRC),
DEFINE_SYSMMU_RESOURCE(EXYNOS4, FIMC_FD, FIMC_FD),
DEFINE_SYSMMU_RESOURCE(EXYNOS4, ISPCPU, FIMC_CX),
};
static struct sysmmu_resource_map sysmmu_resmap4[] __initdata = {
SYSMMU_RESOURCE_MAPPING_PD(4, fimc0, fimc0, PD_CAM),
SYSMMU_RESOURCE_MAPPING_PD(4, fimc1, fimc1, PD_CAM),
SYSMMU_RESOURCE_MAPPING_PD(4, fimc2, fimc2, PD_CAM),
SYSMMU_RESOURCE_MAPPING_PD(4, fimc3, fimc3, PD_CAM),
SYSMMU_RESOURCE_MAPPING_PD(4, tv, tv, PD_TV),
SYSMMU_RESOURCE_MAPPING_PD(4, mfc_r, mfc_r, PD_MFC),
SYSMMU_RESOURCE_MAPPING_PD(4, mfc_l, mfc_l, PD_MFC),
SYSMMU_RESOURCE_MAPPING_PD(4, rot, rot, PD_LCD0),
SYSMMU_RESOURCE_MAPPING_PD(4, jpeg, jpeg, PD_CAM),
SYSMMU_RESOURCE_MAPPING_PD(4, fimd0, fimd0, PD_LCD0),
};
static struct sysmmu_resource_map sysmmu_resmap4210[] __initdata = {
SYSMMU_RESOURCE_MAPPING_PD(4, 2d, 2d, PD_LCD0),
SYSMMU_RESOURCE_MAPPING_PD(4, fimd1, fimd1, PD_LCD1),
};
static struct sysmmu_resource_map sysmmu_resmap4212[] __initdata = {
SYSMMU_RESOURCE_MAPPING(4, 2d, 2d_acp),
SYSMMU_RESOURCE_MAPPING_PD(4, camif0, flite0, PD_ISP),
SYSMMU_RESOURCE_MAPPING_PD(4, camif1, flite1, PD_ISP),
SYSMMU_RESOURCE_MAPPING_PD(4, isp, isp, PD_ISP),
};
#endif /* CONFIG_ARCH_EXYNOS4 */
void sysmmu_clk_disable(sysmmu_ips ips)
#ifdef CONFIG_ARCH_EXYNOS5
SYSMMU_RESOURCE_DEFINE(EXYNOS5, jpeg, JPEG, JPEG);
SYSMMU_RESOURCE_DEFINE(EXYNOS5, fimd1, FIMD1, FIMD1);
SYSMMU_RESOURCE_DEFINE(EXYNOS5, 2d, 2D, 2D);
SYSMMU_RESOURCE_DEFINE(EXYNOS5, rot, ROTATOR, ROTATOR);
SYSMMU_RESOURCE_DEFINE(EXYNOS5, tv, TV, TV);
SYSMMU_RESOURCE_DEFINE(EXYNOS5, flite0, LITE0, LITE0);
SYSMMU_RESOURCE_DEFINE(EXYNOS5, flite1, LITE1, LITE1);
SYSMMU_RESOURCE_DEFINE(EXYNOS5, gsc0, GSC0, GSC0);
SYSMMU_RESOURCE_DEFINE(EXYNOS5, gsc1, GSC1, GSC1);
SYSMMU_RESOURCE_DEFINE(EXYNOS5, gsc2, GSC2, GSC2);
SYSMMU_RESOURCE_DEFINE(EXYNOS5, gsc3, GSC3, GSC3);
SYSMMU_RESOURCE_DEFINE(EXYNOS5, mfc_r, MFC_R, MFC_R);
SYSMMU_RESOURCE_DEFINE(EXYNOS5, mfc_l, MFC_L, MFC_L);
SYSMMU_RESOURCE(EXYNOS5, isp) {
DEFINE_SYSMMU_RESOURCE(EXYNOS5, ISP, ISP),
DEFINE_SYSMMU_RESOURCE(EXYNOS5, DRC, DRC),
DEFINE_SYSMMU_RESOURCE(EXYNOS5, FD, FD),
DEFINE_SYSMMU_RESOURCE(EXYNOS5, ISPCPU, MCUISP),
DEFINE_SYSMMU_RESOURCE(EXYNOS5, SCALERC, SCALERCISP),
DEFINE_SYSMMU_RESOURCE(EXYNOS5, SCALERP, SCALERPISP),
DEFINE_SYSMMU_RESOURCE(EXYNOS5, ODC, ODC),
DEFINE_SYSMMU_RESOURCE(EXYNOS5, DIS0, DIS0),
DEFINE_SYSMMU_RESOURCE(EXYNOS5, DIS1, DIS1),
DEFINE_SYSMMU_RESOURCE(EXYNOS5, 3DNR, 3DNR),
};
static struct sysmmu_resource_map sysmmu_resmap5[] __initdata = {
SYSMMU_RESOURCE_MAPPING(5, jpeg, jpeg),
SYSMMU_RESOURCE_MAPPING(5, fimd1, fimd1),
SYSMMU_RESOURCE_MAPPING(5, 2d, 2d),
SYSMMU_RESOURCE_MAPPING(5, rot, rot),
SYSMMU_RESOURCE_MAPPING_PD(5, tv, tv, PD_DISP1),
SYSMMU_RESOURCE_MAPPING_PD(5, camif0, flite0, PD_GSCL),
SYSMMU_RESOURCE_MAPPING_PD(5, camif1, flite1, PD_GSCL),
SYSMMU_RESOURCE_MAPPING_PD(5, gsc0, gsc0, PD_GSCL),
SYSMMU_RESOURCE_MAPPING_PD(5, gsc1, gsc1, PD_GSCL),
SYSMMU_RESOURCE_MAPPING_PD(5, gsc2, gsc2, PD_GSCL),
SYSMMU_RESOURCE_MAPPING_PD(5, gsc3, gsc3, PD_GSCL),
SYSMMU_RESOURCE_MAPPING_PD(5, mfc_r, mfc_r, PD_MFC),
SYSMMU_RESOURCE_MAPPING_PD(5, mfc_l, mfc_l, PD_MFC),
SYSMMU_RESOURCE_MAPPING_MCPD(5, isp, isp, PD_ISP, mc_platdata),
};
#endif /* CONFIG_ARCH_EXYNOS5 */
static int __init init_sysmmu_platform_device(void)
{
if (sysmmu_clk[ips])
clk_disable(sysmmu_clk[ips]);
int i, j;
struct sysmmu_resource_map *resmap[2] = {NULL, NULL};
int nmap[2] = {0, 0};
#ifdef CONFIG_ARCH_EXYNOS5
if (soc_is_exynos5250()) {
resmap[0] = sysmmu_resmap5;
nmap[0] = ARRAY_SIZE(sysmmu_resmap5);
nmap[1] = 0;
}
#endif
#ifdef CONFIG_ARCH_EXYNOS4
if (resmap[0] == NULL) {
resmap[0] = sysmmu_resmap4;
nmap[0] = ARRAY_SIZE(sysmmu_resmap4);
}
if (soc_is_exynos4210()) {
resmap[1] = sysmmu_resmap4210;
nmap[1] = ARRAY_SIZE(sysmmu_resmap4210);
}
if (soc_is_exynos4412() || soc_is_exynos4212()) {
resmap[1] = sysmmu_resmap4212;
nmap[1] = ARRAY_SIZE(sysmmu_resmap4212);
}
#endif
for (j = 0; j < 2; j++) {
for (i = 0; i < nmap[j]; i++) {
struct sysmmu_resource_map *map;
struct sysmmu_platform_data *platdata;
map = &resmap[j][i];
map->pdev->dev.parent = map->pdd;
platdata = map->pdev->dev.platform_data;
platdata->clockname = map->clocknames;
if (platform_device_add_resources(map->pdev, map->res,
map->rnum)) {
pr_err("%s: Failed to add device resources for "
"%s.%d\n", __func__,
map->pdev->name, map->pdev->id);
continue;
}
if (platform_device_register(map->pdev)) {
pr_err("%s: Failed to register %s.%d\n",
__func__, map->pdev->name,
map->pdev->id);
}
}
}
return 0;
}
arch_initcall(init_sysmmu_platform_device);
......@@ -154,6 +154,13 @@
#define EXYNOS4_IRQ_SYSMMU_MFC_M1_0 COMBINER_IRQ(5, 6)
#define EXYNOS4_IRQ_SYSMMU_PCIE_0 COMBINER_IRQ(5, 7)
#define EXYNOS4_IRQ_SYSMMU_FIMC_LITE0_0 COMBINER_IRQ(16, 0)
#define EXYNOS4_IRQ_SYSMMU_FIMC_LITE1_0 COMBINER_IRQ(16, 1)
#define EXYNOS4_IRQ_SYSMMU_FIMC_ISP_0 COMBINER_IRQ(16, 2)
#define EXYNOS4_IRQ_SYSMMU_FIMC_DRC_0 COMBINER_IRQ(16, 3)
#define EXYNOS4_IRQ_SYSMMU_FIMC_FD_0 COMBINER_IRQ(16, 4)
#define EXYNOS4_IRQ_SYSMMU_FIMC_CX_0 COMBINER_IRQ(16, 5)
#define EXYNOS4_IRQ_FIMD0_FIFO COMBINER_IRQ(11, 0)
#define EXYNOS4_IRQ_FIMD0_VSYNC COMBINER_IRQ(11, 1)
#define EXYNOS4_IRQ_FIMD0_SYSTEM COMBINER_IRQ(11, 2)
......@@ -221,24 +228,6 @@
#define IRQ_KEYPAD EXYNOS4_IRQ_KEYPAD
#define IRQ_PMU EXYNOS4_IRQ_PMU
#define IRQ_SYSMMU_MDMA0_0 EXYNOS4_IRQ_SYSMMU_MDMA0_0
#define IRQ_SYSMMU_SSS_0 EXYNOS4_IRQ_SYSMMU_SSS_0
#define IRQ_SYSMMU_FIMC0_0 EXYNOS4_IRQ_SYSMMU_FIMC0_0
#define IRQ_SYSMMU_FIMC1_0 EXYNOS4_IRQ_SYSMMU_FIMC1_0
#define IRQ_SYSMMU_FIMC2_0 EXYNOS4_IRQ_SYSMMU_FIMC2_0
#define IRQ_SYSMMU_FIMC3_0 EXYNOS4_IRQ_SYSMMU_FIMC3_0
#define IRQ_SYSMMU_JPEG_0 EXYNOS4_IRQ_SYSMMU_JPEG_0
#define IRQ_SYSMMU_2D_0 EXYNOS4_IRQ_SYSMMU_2D_0
#define IRQ_SYSMMU_ROTATOR_0 EXYNOS4_IRQ_SYSMMU_ROTATOR_0
#define IRQ_SYSMMU_MDMA1_0 EXYNOS4_IRQ_SYSMMU_MDMA1_0
#define IRQ_SYSMMU_LCD0_M0_0 EXYNOS4_IRQ_SYSMMU_LCD0_M0_0
#define IRQ_SYSMMU_LCD1_M1_0 EXYNOS4_IRQ_SYSMMU_LCD1_M1_0
#define IRQ_SYSMMU_TV_M0_0 EXYNOS4_IRQ_SYSMMU_TV_M0_0
#define IRQ_SYSMMU_MFC_M0_0 EXYNOS4_IRQ_SYSMMU_MFC_M0_0
#define IRQ_SYSMMU_MFC_M1_0 EXYNOS4_IRQ_SYSMMU_MFC_M1_0
#define IRQ_SYSMMU_PCIE_0 EXYNOS4_IRQ_SYSMMU_PCIE_0
#define IRQ_FIMD0_FIFO EXYNOS4_IRQ_FIMD0_FIFO
#define IRQ_FIMD0_VSYNC EXYNOS4_IRQ_FIMD0_VSYNC
#define IRQ_FIMD0_SYSTEM EXYNOS4_IRQ_FIMD0_SYSTEM
......
......@@ -95,6 +95,7 @@
#define EXYNOS5_PA_PDMA1 0x121B0000
#define EXYNOS4_PA_SYSMMU_MDMA 0x10A40000
#define EXYNOS4_PA_SYSMMU_2D_ACP 0x10A40000
#define EXYNOS4_PA_SYSMMU_SSS 0x10A50000
#define EXYNOS4_PA_SYSMMU_FIMC0 0x11A20000
#define EXYNOS4_PA_SYSMMU_FIMC1 0x11A30000
......@@ -103,6 +104,12 @@
#define EXYNOS4_PA_SYSMMU_JPEG 0x11A60000
#define EXYNOS4_PA_SYSMMU_FIMD0 0x11E20000
#define EXYNOS4_PA_SYSMMU_FIMD1 0x12220000
#define EXYNOS4_PA_SYSMMU_FIMC_ISP 0x12260000
#define EXYNOS4_PA_SYSMMU_FIMC_DRC 0x12270000
#define EXYNOS4_PA_SYSMMU_FIMC_FD 0x122A0000
#define EXYNOS4_PA_SYSMMU_ISPCPU 0x122B0000
#define EXYNOS4_PA_SYSMMU_FIMC_LITE0 0x123B0000
#define EXYNOS4_PA_SYSMMU_FIMC_LITE1 0x123C0000
#define EXYNOS4_PA_SYSMMU_PCIe 0x12620000
#define EXYNOS4_PA_SYSMMU_G2D 0x12A20000
#define EXYNOS4_PA_SYSMMU_ROTATOR 0x12A30000
......@@ -110,6 +117,37 @@
#define EXYNOS4_PA_SYSMMU_TV 0x12E20000
#define EXYNOS4_PA_SYSMMU_MFC_L 0x13620000
#define EXYNOS4_PA_SYSMMU_MFC_R 0x13630000
#define EXYNOS5_PA_SYSMMU_MDMA1 0x10A40000
#define EXYNOS5_PA_SYSMMU_SSS 0x10A50000
#define EXYNOS5_PA_SYSMMU_2D 0x10A60000
#define EXYNOS5_PA_SYSMMU_MFC_L 0x11200000
#define EXYNOS5_PA_SYSMMU_MFC_R 0x11210000
#define EXYNOS5_PA_SYSMMU_ROTATOR 0x11D40000
#define EXYNOS5_PA_SYSMMU_MDMA2 0x11D50000
#define EXYNOS5_PA_SYSMMU_JPEG 0x11F20000
#define EXYNOS5_PA_SYSMMU_IOP 0x12360000
#define EXYNOS5_PA_SYSMMU_RTIC 0x12370000
#define EXYNOS5_PA_SYSMMU_GPS 0x12630000
#define EXYNOS5_PA_SYSMMU_ISP 0x13260000
#define EXYNOS5_PA_SYSMMU_DRC 0x12370000
#define EXYNOS5_PA_SYSMMU_SCALERC 0x13280000
#define EXYNOS5_PA_SYSMMU_SCALERP 0x13290000
#define EXYNOS5_PA_SYSMMU_FD 0x132A0000
#define EXYNOS5_PA_SYSMMU_ISPCPU 0x132B0000
#define EXYNOS5_PA_SYSMMU_ODC 0x132C0000
#define EXYNOS5_PA_SYSMMU_DIS0 0x132D0000
#define EXYNOS5_PA_SYSMMU_DIS1 0x132E0000
#define EXYNOS5_PA_SYSMMU_3DNR 0x132F0000
#define EXYNOS5_PA_SYSMMU_LITE0 0x13C40000
#define EXYNOS5_PA_SYSMMU_LITE1 0x13C50000
#define EXYNOS5_PA_SYSMMU_GSC0 0x13E80000
#define EXYNOS5_PA_SYSMMU_GSC1 0x13E90000
#define EXYNOS5_PA_SYSMMU_GSC2 0x13EA0000
#define EXYNOS5_PA_SYSMMU_GSC3 0x13EB0000
#define EXYNOS5_PA_SYSMMU_FIMD1 0x14640000
#define EXYNOS5_PA_SYSMMU_TV 0x14650000
#define EXYNOS4_PA_SPI0 0x13920000
#define EXYNOS4_PA_SPI1 0x13930000
#define EXYNOS4_PA_SPI2 0x13940000
......
......@@ -135,6 +135,9 @@
#define EXYNOS4_CLKGATE_SCLKCPU EXYNOS_CLKREG(0x14800)
#define EXYNOS4_CLKGATE_IP_CPU EXYNOS_CLKREG(0x14900)
#define EXYNOS4_CLKGATE_IP_ISP0 EXYNOS_CLKREG(0x18800)
#define EXYNOS4_CLKGATE_IP_ISP1 EXYNOS_CLKREG(0x18804)
#define EXYNOS4_APLL_LOCKTIME (0x1C20) /* 300us */
#define EXYNOS4_APLLCON0_ENABLE_SHIFT (31)
......@@ -303,6 +306,8 @@
#define EXYNOS5_CLKDIV_PERIC0 EXYNOS_CLKREG(0x10558)
#define EXYNOS5_CLKGATE_IP_ACP EXYNOS_CLKREG(0x08800)
#define EXYNOS5_CLKGATE_IP_ISP0 EXYNOS_CLKREG(0x0C800)
#define EXYNOS5_CLKGATE_IP_ISP1 EXYNOS_CLKREG(0x0C804)
#define EXYNOS5_CLKGATE_IP_GSCL EXYNOS_CLKREG(0x10920)
#define EXYNOS5_CLKGATE_IP_DISP1 EXYNOS_CLKREG(0x10928)
#define EXYNOS5_CLKGATE_IP_MFC EXYNOS_CLKREG(0x1092C)
......
/* linux/arch/arm/mach-exynos4/include/mach/regs-sysmmu.h
*
* Copyright (c) 2010-2011 Samsung Electronics Co., Ltd.
* http://www.samsung.com
*
* EXYNOS4 - System MMU register
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*/
#ifndef __ASM_ARCH_REGS_SYSMMU_H
#define __ASM_ARCH_REGS_SYSMMU_H __FILE__
#define S5P_MMU_CTRL 0x000
#define S5P_MMU_CFG 0x004
#define S5P_MMU_STATUS 0x008
#define S5P_MMU_FLUSH 0x00C
#define S5P_PT_BASE_ADDR 0x014
#define S5P_INT_STATUS 0x018
#define S5P_INT_CLEAR 0x01C
#define S5P_PAGE_FAULT_ADDR 0x024
#define S5P_AW_FAULT_ADDR 0x028
#define S5P_AR_FAULT_ADDR 0x02C
#define S5P_DEFAULT_SLAVE_ADDR 0x030
#endif /* __ASM_ARCH_REGS_SYSMMU_H */
/* linux/arch/arm/mach-exynos4/include/mach/sysmmu.h
*
* Copyright (c) 2010-2011 Samsung Electronics Co., Ltd.
/*
* Copyright (c) 2011-2012 Samsung Electronics Co., Ltd.
* http://www.samsung.com
*
* Samsung sysmmu driver for EXYNOS4
* EXYNOS - System MMU support
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*/
#ifndef __ASM_ARM_ARCH_SYSMMU_H
#define __ASM_ARM_ARCH_SYSMMU_H __FILE__
enum exynos4_sysmmu_ips {
SYSMMU_MDMA,
SYSMMU_SSS,
SYSMMU_FIMC0,
SYSMMU_FIMC1,
SYSMMU_FIMC2,
SYSMMU_FIMC3,
SYSMMU_JPEG,
SYSMMU_FIMD0,
SYSMMU_FIMD1,
SYSMMU_PCIe,
SYSMMU_G2D,
SYSMMU_ROTATOR,
SYSMMU_MDMA2,
SYSMMU_TV,
SYSMMU_MFC_L,
SYSMMU_MFC_R,
EXYNOS4_SYSMMU_TOTAL_IPNUM,
*/
#ifndef _ARM_MACH_EXYNOS_SYSMMU_H_
#define _ARM_MACH_EXYNOS_SYSMMU_H_
struct sysmmu_platform_data {
char *dbgname;
/* comma(,) separated list of clock names for clock gating */
char *clockname;
};
#define S5P_SYSMMU_TOTAL_IPNUM EXYNOS4_SYSMMU_TOTAL_IPNUM
#define SYSMMU_DEVNAME_BASE "exynos-sysmmu"
#define SYSMMU_CLOCK_NAME "sysmmu"
#define SYSMMU_CLOCK_NAME2 "sysmmu_mc"
#ifdef CONFIG_EXYNOS_DEV_SYSMMU
#include <linux/device.h>
struct platform_device;
#define SYSMMU_PLATDEV(ipname) exynos_device_sysmmu_##ipname
extern struct platform_device SYSMMU_PLATDEV(mfc_l);
extern struct platform_device SYSMMU_PLATDEV(mfc_r);
extern struct platform_device SYSMMU_PLATDEV(tv);
extern struct platform_device SYSMMU_PLATDEV(jpeg);
extern struct platform_device SYSMMU_PLATDEV(rot);
extern struct platform_device SYSMMU_PLATDEV(fimc0);
extern struct platform_device SYSMMU_PLATDEV(fimc1);
extern struct platform_device SYSMMU_PLATDEV(fimc2);
extern struct platform_device SYSMMU_PLATDEV(fimc3);
extern struct platform_device SYSMMU_PLATDEV(gsc0);
extern struct platform_device SYSMMU_PLATDEV(gsc1);
extern struct platform_device SYSMMU_PLATDEV(gsc2);
extern struct platform_device SYSMMU_PLATDEV(gsc3);
extern struct platform_device SYSMMU_PLATDEV(isp);
extern struct platform_device SYSMMU_PLATDEV(fimd0);
extern struct platform_device SYSMMU_PLATDEV(fimd1);
extern struct platform_device SYSMMU_PLATDEV(camif0);
extern struct platform_device SYSMMU_PLATDEV(camif1);
extern struct platform_device SYSMMU_PLATDEV(2d);
extern const char *sysmmu_ips_name[EXYNOS4_SYSMMU_TOTAL_IPNUM];
#ifdef CONFIG_IOMMU_API
static inline void platform_set_sysmmu(
struct device *sysmmu, struct device *dev)
{
dev->archdata.iommu = sysmmu;
}
#endif
typedef enum exynos4_sysmmu_ips sysmmu_ips;
#else /* !CONFIG_EXYNOS_DEV_SYSMMU */
#define platform_set_sysmmu(dev, sysmmu) do { } while (0)
#endif
void sysmmu_clk_init(struct device *dev, sysmmu_ips ips);
void sysmmu_clk_enable(sysmmu_ips ips);
void sysmmu_clk_disable(sysmmu_ips ips);
#define SYSMMU_CLOCK_DEVNAME(ipname, id) (SYSMMU_DEVNAME_BASE "." #id)
#endif /* __ASM_ARM_ARCH_SYSMMU_H */
#endif /* _ARM_MACH_EXYNOS_SYSMMU_H_ */
......@@ -147,7 +147,6 @@ static struct platform_device *armlex4210_devices[] __initdata = {
&s3c_device_hsmmc3,
&s3c_device_rtc,
&s3c_device_wdt,
&exynos4_device_sysmmu,
&samsung_asoc_dma,
&armlex4210_smsc911x,
&exynos4_device_ahci,
......
......@@ -295,7 +295,6 @@ static struct platform_device *smdkv310_devices[] __initdata = {
&s5p_device_mfc_l,
&s5p_device_mfc_r,
&exynos4_device_spdif,
&exynos4_device_sysmmu,
&samsung_asoc_dma,
&samsung_asoc_idma,
&s5p_device_fimd0,
......
......@@ -50,6 +50,14 @@ config TEGRA_PCI
depends on ARCH_TEGRA_2x_SOC
select PCI
config TEGRA_AHB
bool "Enable AHB driver for NVIDIA Tegra SoCs"
default y
help
Adds AHB configuration functionality for NVIDIA Tegra SoCs,
which controls AHB bus master arbitration and some
perfomance parameters(priority, prefech size).
comment "Tegra board type"
config MACH_HARMONY
......@@ -111,7 +119,7 @@ config MACH_VENTANA
Support for the nVidia Ventana development platform
choice
prompt "Low-level debug console UART"
prompt "Default low-level debug console UART"
default TEGRA_DEBUG_UART_NONE
config TEGRA_DEBUG_UART_NONE
......@@ -134,6 +142,33 @@ config TEGRA_DEBUG_UARTE
endchoice
choice
prompt "Automatic low-level debug console UART"
default TEGRA_DEBUG_UART_AUTO_NONE
config TEGRA_DEBUG_UART_AUTO_NONE
bool "None"
config TEGRA_DEBUG_UART_AUTO_ODMDATA
bool "Via ODMDATA"
help
Automatically determines which UART to use for low-level debug based
on the ODMDATA value. This value is part of the BCT, and is written
to the boot memory device using nvflash, or other flashing tool.
When bits 19:18 are 3, then bits 17:15 indicate which UART to use;
0/1/2/3/4 are UART A/B/C/D/E.
config TEGRA_DEBUG_UART_AUTO_SCRATCH
bool "Via UART scratch register"
help
Automatically determines which UART to use for low-level debug based
on the UART scratch register value. Some bootloaders put ASCII 'D'
in this register when they initialize their own console UART output.
Using this option allows the kernel to automatically pick the same
UART.
endchoice
config TEGRA_SYSTEM_DMA
bool "Enable system DMA driver for NVIDIA Tegra SoCs"
default y
......
......@@ -51,12 +51,22 @@ struct of_dev_auxdata tegra30_auxdata_lookup[] __initdata = {
OF_DEV_AUXDATA("nvidia,tegra20-i2c", 0x7000C500, "tegra-i2c.2", NULL),
OF_DEV_AUXDATA("nvidia,tegra20-i2c", 0x7000C700, "tegra-i2c.3", NULL),
OF_DEV_AUXDATA("nvidia,tegra20-i2c", 0x7000D000, "tegra-i2c.4", NULL),
OF_DEV_AUXDATA("nvidia,tegra30-ahub", 0x70080000, "tegra30-ahub", NULL),
{}
};
static __initdata struct tegra_clk_init_table tegra_dt_clk_init_table[] = {
/* name parent rate enabled */
{ "uarta", "pll_p", 408000000, true },
{ "pll_a", "pll_p_out1", 564480000, true },
{ "pll_a_out0", "pll_a", 11289600, true },
{ "extern1", "pll_a_out0", 0, true },
{ "clk_out_1", "extern1", 0, true },
{ "i2s0", "pll_a_out0", 11289600, false},
{ "i2s1", "pll_a_out0", 11289600, false},
{ "i2s2", "pll_a_out0", 11289600, false},
{ "i2s3", "pll_a_out0", 11289600, false},
{ "i2s4", "pll_a_out0", 11289600, false},
{ NULL, NULL, 0, 0},
};
......
......@@ -162,6 +162,8 @@ static void paz00_i2c_init(void)
static void paz00_usb_init(void)
{
tegra_ehci2_ulpi_phy_config.reset_gpio = TEGRA_ULPI_RST;
platform_device_register(&tegra_ehci2_device);
platform_device_register(&tegra_ehci3_device);
}
......@@ -179,7 +181,6 @@ static __initdata struct tegra_clk_init_table paz00_clk_init_table[] = {
{ "uarta", "pll_p", 216000000, true },
{ "uartc", "pll_p", 216000000, true },
{ "pll_p_out4", "pll_p", 24000000, true },
{ "usbd", "clk_m", 12000000, false },
{ "usb2", "clk_m", 12000000, false },
{ "usb3", "clk_m", 12000000, false },
......
......@@ -118,6 +118,8 @@ static void trimslice_usb_init(void)
pdata = tegra_ehci1_device.dev.platform_data;
pdata->vbus_gpio = TRIMSLICE_GPIO_USB1_MODE;
tegra_ehci2_ulpi_phy_config.reset_gpio = TEGRA_GPIO_PV0;
platform_device_register(&tegra_ehci3_device);
platform_device_register(&tegra_ehci2_device);
platform_device_register(&tegra_ehci1_device);
......
......@@ -82,10 +82,12 @@ static __initdata struct tegra_clk_init_table tegra20_clk_init_table[] = {
{ "pll_p_out1", "pll_p", 28800000, true },
{ "pll_p_out2", "pll_p", 48000000, true },
{ "pll_p_out3", "pll_p", 72000000, true },
{ "pll_p_out4", "pll_p", 108000000, true },
{ "sclk", "pll_p_out4", 108000000, true },
{ "hclk", "sclk", 108000000, true },
{ "pclk", "hclk", 54000000, true },
{ "pll_p_out4", "pll_p", 24000000, true },
{ "pll_c", "clk_m", 600000000, true },
{ "pll_c_out1", "pll_c", 120000000, true },
{ "sclk", "pll_c_out1", 120000000, true },
{ "hclk", "sclk", 120000000, true },
{ "pclk", "hclk", 60000000, true },
{ "csite", NULL, 0, true },
{ "emc", NULL, 0, true },
{ "cpu", NULL, 0, true },
......@@ -93,6 +95,17 @@ static __initdata struct tegra_clk_init_table tegra20_clk_init_table[] = {
};
#endif
#ifdef CONFIG_ARCH_TEGRA_3x_SOC
static __initdata struct tegra_clk_init_table tegra30_clk_init_table[] = {
/* name parent rate enabled */
{ "clk_m", NULL, 0, true },
{ "pll_p", "clk_m", 408000000, true },
{ "pll_p_out1", "pll_p", 9600000, true },
{ NULL, NULL, 0, 0},
};
#endif
static void __init tegra_init_cache(u32 tag_latency, u32 data_latency)
{
#ifdef CONFIG_CACHE_L2X0
......@@ -127,6 +140,7 @@ void __init tegra30_init_early(void)
{
tegra_init_fuse();
tegra30_init_clocks();
tegra_clk_init_from_table(tegra30_clk_init_table);
tegra_init_cache(0x441, 0x551);
tegra_pmc_init();
tegra_powergate_init();
......
......@@ -439,9 +439,8 @@ static struct resource tegra_usb3_resources[] = {
},
};
static struct tegra_ulpi_config tegra_ehci2_ulpi_phy_config = {
/* All existing boards use GPIO PV0 for phy reset */
.reset_gpio = TEGRA_GPIO_PV0,
struct tegra_ulpi_config tegra_ehci2_ulpi_phy_config = {
.reset_gpio = -1,
.clk = "cdev2",
};
......
......@@ -22,6 +22,10 @@
#include <linux/platform_device.h>
#include <linux/platform_data/tegra_usb.h>
#include <mach/usb_phy.h>
extern struct tegra_ulpi_config tegra_ehci2_ulpi_phy_config;
extern struct tegra_ehci_platform_data tegra_ehci1_pdata;
extern struct tegra_ehci_platform_data tegra_ehci2_pdata;
extern struct tegra_ehci_platform_data tegra_ehci3_pdata;
......
/*
* 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.
*/
#ifndef __MACH_TEGRA_AHB_H__
#define __MACH_TEGRA_AHB_H__
extern int tegra_ahb_enable_smmu(struct device_node *ahb);
#endif /* __MACH_TEGRA_AHB_H__ */
......@@ -63,52 +63,86 @@ static inline void save_uart_address(void)
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 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,
},
};
static inline bool uart_clocked(int i)
{
if (*(u8 *)uarts[i].reset_reg & BIT(uarts[i].bit))
return false;
if (!(*(u8 *)uarts[i].clock_reg & BIT(uarts[i].bit)))
return false;
return true;
}
#ifdef CONFIG_TEGRA_DEBUG_UART_AUTO_ODMDATA
int auto_odmdata(void)
{
volatile u32 *pmc = (volatile u32 *)TEGRA_PMC_BASE;
u32 odmdata = pmc[0xa0 / 4];
/*
* Bits 19:18 are the console type: 0=default, 1=none, 2==DCC, 3==UART
* Some boards apparently swap the last two values, but we don't have
* any way of catering for that here, so we just accept either. If this
* doesn't make sense for your board, just don't enable this feature.
*
* Bits 17:15 indicate the UART to use, 0/1/2/3/4 are UART A/B/C/D/E.
*/
switch ((odmdata >> 18) & 3) {
case 2:
case 3:
break;
default:
return -1;
}
return (odmdata >> 15) & 7;
}
#endif
#ifdef CONFIG_TEGRA_DEBUG_UART_AUTO_SCRATCH
int auto_scratch(void)
{
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:
......@@ -125,20 +159,60 @@ static inline void arch_decomp_setup(void)
* 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)))
if (!uart_clocked(i))
continue;
uart = (volatile u8 *)uarts[i].base;
if (uart[UART_SCR << DEBUG_UART_SHIFT] != 'D')
continue;
break;
return i;
}
if (i == ARRAY_SIZE(uarts))
uart = (volatile u8 *)TEGRA_DEBUG_UART_BASE;
return -1;
}
#endif
/*
* 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)
{
int uart_id, auto_uart_id;
volatile u32 *apb_misc = (volatile u32 *)TEGRA_APB_MISC_BASE;
u32 chip, div;
#if defined(CONFIG_TEGRA_DEBUG_UARTA)
uart_id = 0;
#elif defined(CONFIG_TEGRA_DEBUG_UARTB)
uart_id = 1;
#elif defined(CONFIG_TEGRA_DEBUG_UARTC)
uart_id = 2;
#elif defined(CONFIG_TEGRA_DEBUG_UARTD)
uart_id = 3;
#elif defined(CONFIG_TEGRA_DEBUG_UARTE)
uart_id = 4;
#else
uart_id = -1;
#endif
#if defined(CONFIG_TEGRA_DEBUG_UART_AUTO_ODMDATA)
auto_uart_id = auto_odmdata();
#elif defined(CONFIG_TEGRA_DEBUG_UART_AUTO_SCRATCH)
auto_uart_id = auto_scratch();
#else
auto_uart_id = -1;
#endif
if (auto_uart_id != -1)
uart_id = auto_uart_id;
if (uart_id < 0 || uart_id >= ARRAY_SIZE(uarts) ||
!uart_clocked(uart_id))
uart = NULL;
else
uart = (volatile u8 *)uarts[uart_id].base;
save_uart_address();
if (uart == NULL)
return;
......
......@@ -61,8 +61,8 @@ struct tegra_usb_phy {
struct usb_phy *ulpi;
};
struct tegra_usb_phy *tegra_usb_phy_open(int instance, void __iomem *regs,
void *config, enum tegra_usb_phy_mode phy_mode);
struct tegra_usb_phy *tegra_usb_phy_open(struct device *dev, int instance,
void __iomem *regs, void *config, enum tegra_usb_phy_mode phy_mode);
int tegra_usb_phy_power_on(struct tegra_usb_phy *phy);
......
......@@ -1486,6 +1486,10 @@ static struct clk tegra_clk_m = {
};
static struct clk_pll_freq_table tegra_pll_c_freq_table[] = {
{ 12000000, 600000000, 600, 12, 1, 8 },
{ 13000000, 600000000, 600, 13, 1, 8 },
{ 19200000, 600000000, 500, 16, 1, 6 },
{ 26000000, 600000000, 600, 26, 1, 8 },
{ 0, 0, 0, 0, 0, 0 },
};
......
......@@ -3015,6 +3015,15 @@ struct clk_duplicate tegra_clk_duplicates[] = {
CLK_DUPLICATE("sbc6", "spi_slave_tegra.5", NULL),
CLK_DUPLICATE("twd", "smp_twd", NULL),
CLK_DUPLICATE("vcp", "nvavp", "vcp"),
CLK_DUPLICATE("i2s0", NULL, "i2s0"),
CLK_DUPLICATE("i2s1", NULL, "i2s1"),
CLK_DUPLICATE("i2s2", NULL, "i2s2"),
CLK_DUPLICATE("i2s3", NULL, "i2s3"),
CLK_DUPLICATE("i2s4", NULL, "i2s4"),
CLK_DUPLICATE("dam0", NULL, "dam0"),
CLK_DUPLICATE("dam1", NULL, "dam1"),
CLK_DUPLICATE("dam2", NULL, "dam2"),
CLK_DUPLICATE("spdif_in", NULL, "spdif_in"),
};
struct clk *tegra_ptr_clks[] = {
......
......@@ -26,6 +26,7 @@
#include <linux/platform_device.h>
#include <linux/io.h>
#include <linux/gpio.h>
#include <linux/of_gpio.h>
#include <linux/usb/otg.h>
#include <linux/usb/ulpi.h>
#include <asm/mach-types.h>
......@@ -654,8 +655,8 @@ static void ulpi_phy_power_off(struct tegra_usb_phy *phy)
clk_disable(phy->clk);
}
struct tegra_usb_phy *tegra_usb_phy_open(int instance, void __iomem *regs,
void *config, enum tegra_usb_phy_mode phy_mode)
struct tegra_usb_phy *tegra_usb_phy_open(struct device *dev, int instance,
void __iomem *regs, void *config, enum tegra_usb_phy_mode phy_mode)
{
struct tegra_usb_phy *phy;
struct tegra_ulpi_config *ulpi_config;
......@@ -711,6 +712,16 @@ struct tegra_usb_phy *tegra_usb_phy_open(int instance, void __iomem *regs,
err = -ENXIO;
goto err1;
}
if (!gpio_is_valid(ulpi_config->reset_gpio))
ulpi_config->reset_gpio =
of_get_named_gpio(dev->of_node,
"nvidia,phy-reset-gpio", 0);
if (!gpio_is_valid(ulpi_config->reset_gpio)) {
pr_err("%s: invalid reset gpio: %d\n", __func__,
ulpi_config->reset_gpio);
err = -EINVAL;
goto err1;
}
gpio_request(ulpi_config->reset_gpio, "ulpi_phy_reset_b");
gpio_direction_output(ulpi_config->reset_gpio, 0);
phy->ulpi = otg_ulpi_create(&ulpi_viewport_access_ops, 0);
......
......@@ -44,6 +44,10 @@ struct pxa27x_keypad_platform_data {
/* direct keys */
int direct_key_num;
unsigned int direct_key_map[MAX_DIRECT_KEY_NUM];
/* the key output may be low active */
int direct_key_low_active;
/* give board a chance to choose the start direct key */
unsigned int direct_key_mask;
/* rotary encoders 0 */
int enable_rotary0;
......
......@@ -50,14 +50,6 @@ config S5P_PM
Common code for power management support on S5P and newer SoCs
Note: Do not select this for S5P6440 and S5P6450.
comment "System MMU"
config S5P_SYSTEM_MMU
bool "S5P SYSTEM MMU"
depends on ARCH_EXYNOS4
help
Say Y here if you want to enable System MMU
config S5P_SLEEP
bool
help
......
......@@ -16,7 +16,6 @@ obj-y += clock.o
obj-y += irq.o
obj-$(CONFIG_S5P_EXT_INT) += irq-eint.o
obj-$(CONFIG_S5P_GPIO_INT) += irq-gpioint.o
obj-$(CONFIG_S5P_SYSTEM_MMU) += sysmmu.o
obj-$(CONFIG_S5P_PM) += pm.o irq-pm.o
obj-$(CONFIG_S5P_SLEEP) += sleep.o
obj-$(CONFIG_S5P_HRT) += s5p-time.o
......
/* linux/arch/arm/plat-s5p/sysmmu.c
*
* Copyright (c) 2010 Samsung Electronics Co., Ltd.
* http://www.samsung.com
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*/
#include <linux/io.h>
#include <linux/interrupt.h>
#include <linux/platform_device.h>
#include <linux/export.h>
#include <asm/pgtable.h>
#include <mach/map.h>
#include <mach/regs-sysmmu.h>
#include <plat/sysmmu.h>
#define CTRL_ENABLE 0x5
#define CTRL_BLOCK 0x7
#define CTRL_DISABLE 0x0
static struct device *dev;
static unsigned short fault_reg_offset[SYSMMU_FAULTS_NUM] = {
S5P_PAGE_FAULT_ADDR,
S5P_AR_FAULT_ADDR,
S5P_AW_FAULT_ADDR,
S5P_DEFAULT_SLAVE_ADDR,
S5P_AR_FAULT_ADDR,
S5P_AR_FAULT_ADDR,
S5P_AW_FAULT_ADDR,
S5P_AW_FAULT_ADDR
};
static char *sysmmu_fault_name[SYSMMU_FAULTS_NUM] = {
"PAGE FAULT",
"AR MULTI-HIT FAULT",
"AW MULTI-HIT FAULT",
"BUS ERROR",
"AR SECURITY PROTECTION FAULT",
"AR ACCESS PROTECTION FAULT",
"AW SECURITY PROTECTION FAULT",
"AW ACCESS PROTECTION FAULT"
};
static int (*fault_handlers[S5P_SYSMMU_TOTAL_IPNUM])(
enum S5P_SYSMMU_INTERRUPT_TYPE itype,
unsigned long pgtable_base,
unsigned long fault_addr);
/*
* If adjacent 2 bits are true, the system MMU is enabled.
* The system MMU is disabled, otherwise.
*/
static unsigned long sysmmu_states;
static inline void set_sysmmu_active(sysmmu_ips ips)
{
sysmmu_states |= 3 << (ips * 2);
}
static inline void set_sysmmu_inactive(sysmmu_ips ips)
{
sysmmu_states &= ~(3 << (ips * 2));
}
static inline int is_sysmmu_active(sysmmu_ips ips)
{
return sysmmu_states & (3 << (ips * 2));
}
static void __iomem *sysmmusfrs[S5P_SYSMMU_TOTAL_IPNUM];
static inline void sysmmu_block(sysmmu_ips ips)
{
__raw_writel(CTRL_BLOCK, sysmmusfrs[ips] + S5P_MMU_CTRL);
dev_dbg(dev, "%s is blocked.\n", sysmmu_ips_name[ips]);
}
static inline void sysmmu_unblock(sysmmu_ips ips)
{
__raw_writel(CTRL_ENABLE, sysmmusfrs[ips] + S5P_MMU_CTRL);
dev_dbg(dev, "%s is unblocked.\n", sysmmu_ips_name[ips]);
}
static inline void __sysmmu_tlb_invalidate(sysmmu_ips ips)
{
__raw_writel(0x1, sysmmusfrs[ips] + S5P_MMU_FLUSH);
dev_dbg(dev, "TLB of %s is invalidated.\n", sysmmu_ips_name[ips]);
}
static inline void __sysmmu_set_ptbase(sysmmu_ips ips, unsigned long pgd)
{
if (unlikely(pgd == 0)) {
pgd = (unsigned long)ZERO_PAGE(0);
__raw_writel(0x20, sysmmusfrs[ips] + S5P_MMU_CFG); /* 4KB LV1 */
} else {
__raw_writel(0x0, sysmmusfrs[ips] + S5P_MMU_CFG); /* 16KB LV1 */
}
__raw_writel(pgd, sysmmusfrs[ips] + S5P_PT_BASE_ADDR);
dev_dbg(dev, "Page table base of %s is initialized with 0x%08lX.\n",
sysmmu_ips_name[ips], pgd);
__sysmmu_tlb_invalidate(ips);
}
void sysmmu_set_fault_handler(sysmmu_ips ips,
int (*handler)(enum S5P_SYSMMU_INTERRUPT_TYPE itype,
unsigned long pgtable_base,
unsigned long fault_addr))
{
BUG_ON(!((ips >= SYSMMU_MDMA) && (ips < S5P_SYSMMU_TOTAL_IPNUM)));
fault_handlers[ips] = handler;
}
static irqreturn_t s5p_sysmmu_irq(int irq, void *dev_id)
{
/* SYSMMU is in blocked when interrupt occurred. */
unsigned long base = 0;
sysmmu_ips ips = (sysmmu_ips)dev_id;
enum S5P_SYSMMU_INTERRUPT_TYPE itype;
itype = (enum S5P_SYSMMU_INTERRUPT_TYPE)
__ffs(__raw_readl(sysmmusfrs[ips] + S5P_INT_STATUS));
BUG_ON(!((itype >= 0) && (itype < 8)));
dev_alert(dev, "%s occurred by %s.\n", sysmmu_fault_name[itype],
sysmmu_ips_name[ips]);
if (fault_handlers[ips]) {
unsigned long addr;
base = __raw_readl(sysmmusfrs[ips] + S5P_PT_BASE_ADDR);
addr = __raw_readl(sysmmusfrs[ips] + fault_reg_offset[itype]);
if (fault_handlers[ips](itype, base, addr)) {
__raw_writel(1 << itype,
sysmmusfrs[ips] + S5P_INT_CLEAR);
dev_notice(dev, "%s from %s is resolved."
" Retrying translation.\n",
sysmmu_fault_name[itype], sysmmu_ips_name[ips]);
} else {
base = 0;
}
}
sysmmu_unblock(ips);
if (!base)
dev_notice(dev, "%s from %s is not handled.\n",
sysmmu_fault_name[itype], sysmmu_ips_name[ips]);
return IRQ_HANDLED;
}
void s5p_sysmmu_set_tablebase_pgd(sysmmu_ips ips, unsigned long pgd)
{
if (is_sysmmu_active(ips)) {
sysmmu_block(ips);
__sysmmu_set_ptbase(ips, pgd);
sysmmu_unblock(ips);
} else {
dev_dbg(dev, "%s is disabled. "
"Skipping initializing page table base.\n",
sysmmu_ips_name[ips]);
}
}
void s5p_sysmmu_enable(sysmmu_ips ips, unsigned long pgd)
{
if (!is_sysmmu_active(ips)) {
sysmmu_clk_enable(ips);
__sysmmu_set_ptbase(ips, pgd);
__raw_writel(CTRL_ENABLE, sysmmusfrs[ips] + S5P_MMU_CTRL);
set_sysmmu_active(ips);
dev_dbg(dev, "%s is enabled.\n", sysmmu_ips_name[ips]);
} else {
dev_dbg(dev, "%s is already enabled.\n", sysmmu_ips_name[ips]);
}
}
void s5p_sysmmu_disable(sysmmu_ips ips)
{
if (is_sysmmu_active(ips)) {
__raw_writel(CTRL_DISABLE, sysmmusfrs[ips] + S5P_MMU_CTRL);
set_sysmmu_inactive(ips);
sysmmu_clk_disable(ips);
dev_dbg(dev, "%s is disabled.\n", sysmmu_ips_name[ips]);
} else {
dev_dbg(dev, "%s is already disabled.\n", sysmmu_ips_name[ips]);
}
}
void s5p_sysmmu_tlb_invalidate(sysmmu_ips ips)
{
if (is_sysmmu_active(ips)) {
sysmmu_block(ips);
__sysmmu_tlb_invalidate(ips);
sysmmu_unblock(ips);
} else {
dev_dbg(dev, "%s is disabled. "
"Skipping invalidating TLB.\n", sysmmu_ips_name[ips]);
}
}
static int s5p_sysmmu_probe(struct platform_device *pdev)
{
int i, ret;
struct resource *res, *mem;
dev = &pdev->dev;
for (i = 0; i < S5P_SYSMMU_TOTAL_IPNUM; i++) {
int irq;
sysmmu_clk_init(dev, i);
sysmmu_clk_disable(i);
res = platform_get_resource(pdev, IORESOURCE_MEM, i);
if (!res) {
dev_err(dev, "Failed to get the resource of %s.\n",
sysmmu_ips_name[i]);
ret = -ENODEV;
goto err_res;
}
mem = request_mem_region(res->start, resource_size(res),
pdev->name);
if (!mem) {
dev_err(dev, "Failed to request the memory region of %s.\n",
sysmmu_ips_name[i]);
ret = -EBUSY;
goto err_res;
}
sysmmusfrs[i] = ioremap(res->start, resource_size(res));
if (!sysmmusfrs[i]) {
dev_err(dev, "Failed to ioremap() for %s.\n",
sysmmu_ips_name[i]);
ret = -ENXIO;
goto err_reg;
}
irq = platform_get_irq(pdev, i);
if (irq <= 0) {
dev_err(dev, "Failed to get the IRQ resource of %s.\n",
sysmmu_ips_name[i]);
ret = -ENOENT;
goto err_map;
}
if (request_irq(irq, s5p_sysmmu_irq, IRQF_DISABLED,
pdev->name, (void *)i)) {
dev_err(dev, "Failed to request IRQ for %s.\n",
sysmmu_ips_name[i]);
ret = -ENOENT;
goto err_map;
}
}
return 0;
err_map:
iounmap(sysmmusfrs[i]);
err_reg:
release_mem_region(mem->start, resource_size(mem));
err_res:
return ret;
}
static int s5p_sysmmu_remove(struct platform_device *pdev)
{
return 0;
}
int s5p_sysmmu_runtime_suspend(struct device *dev)
{
return 0;
}
int s5p_sysmmu_runtime_resume(struct device *dev)
{
return 0;
}
const struct dev_pm_ops s5p_sysmmu_pm_ops = {
.runtime_suspend = s5p_sysmmu_runtime_suspend,
.runtime_resume = s5p_sysmmu_runtime_resume,
};
static struct platform_driver s5p_sysmmu_driver = {
.probe = s5p_sysmmu_probe,
.remove = s5p_sysmmu_remove,
.driver = {
.owner = THIS_MODULE,
.name = "s5p-sysmmu",
.pm = &s5p_sysmmu_pm_ops,
}
};
static int __init s5p_sysmmu_init(void)
{
return platform_driver_register(&s5p_sysmmu_driver);
}
arch_initcall(s5p_sysmmu_init);
......@@ -133,7 +133,6 @@ extern struct platform_device exynos4_device_pcm1;
extern struct platform_device exynos4_device_pcm2;
extern struct platform_device exynos4_device_pd[];
extern struct platform_device exynos4_device_spdif;
extern struct platform_device exynos4_device_sysmmu;
extern struct platform_device samsung_asoc_dma;
extern struct platform_device samsung_asoc_idma;
......
/* linux/arch/arm/plat-samsung/include/plat/sysmmu.h
*
* Copyright (c) 2010-2011 Samsung Electronics Co., Ltd.
* http://www.samsung.com
*
* Samsung System MMU driver for S5P platform
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*/
#ifndef __PLAT_SAMSUNG_SYSMMU_H
#define __PLAT_SAMSUNG_SYSMMU_H __FILE__
enum S5P_SYSMMU_INTERRUPT_TYPE {
SYSMMU_PAGEFAULT,
SYSMMU_AR_MULTIHIT,
SYSMMU_AW_MULTIHIT,
SYSMMU_BUSERROR,
SYSMMU_AR_SECURITY,
SYSMMU_AR_ACCESS,
SYSMMU_AW_SECURITY,
SYSMMU_AW_PROTECTION, /* 7 */
SYSMMU_FAULTS_NUM
};
#ifdef CONFIG_S5P_SYSTEM_MMU
#include <mach/sysmmu.h>
/**
* s5p_sysmmu_enable() - enable system mmu of ip
* @ips: The ip connected system mmu.
* #pgd: Base physical address of the 1st level page table
*
* This function enable system mmu to transfer address
* from virtual address to physical address
*/
void s5p_sysmmu_enable(sysmmu_ips ips, unsigned long pgd);
/**
* s5p_sysmmu_disable() - disable sysmmu mmu of ip
* @ips: The ip connected system mmu.
*
* This function disable system mmu to transfer address
* from virtual address to physical address
*/
void s5p_sysmmu_disable(sysmmu_ips ips);
/**
* s5p_sysmmu_set_tablebase_pgd() - set page table base address to refer page table
* @ips: The ip connected system mmu.
* @pgd: The page table base address.
*
* This function set page table base address
* When system mmu transfer address from virtaul address to physical address,
* system mmu refer address information from page table
*/
void s5p_sysmmu_set_tablebase_pgd(sysmmu_ips ips, unsigned long pgd);
/**
* s5p_sysmmu_tlb_invalidate() - flush all TLB entry in system mmu
* @ips: The ip connected system mmu.
*
* This function flush all TLB entry in system mmu
*/
void s5p_sysmmu_tlb_invalidate(sysmmu_ips ips);
/** s5p_sysmmu_set_fault_handler() - Fault handler for System MMUs
* @itype: type of fault.
* @pgtable_base: the physical address of page table base. This is 0 if @ips is
* SYSMMU_BUSERROR.
* @fault_addr: the device (virtual) address that the System MMU tried to
* translated. This is 0 if @ips is SYSMMU_BUSERROR.
* Called when interrupt occurred by the System MMUs
* The device drivers of peripheral devices that has a System MMU can implement
* a fault handler to resolve address translation fault by System MMU.
* The meanings of return value and parameters are described below.
* return value: non-zero if the fault is correctly resolved.
* zero if the fault is not handled.
*/
void s5p_sysmmu_set_fault_handler(sysmmu_ips ips,
int (*handler)(enum S5P_SYSMMU_INTERRUPT_TYPE itype,
unsigned long pgtable_base,
unsigned long fault_addr));
#else
#define s5p_sysmmu_enable(ips, pgd) do { } while (0)
#define s5p_sysmmu_disable(ips) do { } while (0)
#define s5p_sysmmu_set_tablebase_pgd(ips, pgd) do { } while (0)
#define s5p_sysmmu_tlb_invalidate(ips) do { } while (0)
#define s5p_sysmmu_set_fault_handler(ips, handler) do { } while (0)
#endif
#endif /* __ASM_PLAT_SYSMMU_H */
......@@ -119,6 +119,7 @@ rtc@68 {
sdhc@2e000 {
status = "disabled";
sdhci,1-bit-only;
bus-width = <1>;
};
par_io@e0100 {
......
......@@ -18,7 +18,7 @@ obj-$(CONFIG_SFI) += sfi/
# PnP must come after ACPI since it will eventually need to check if acpi
# was used and do nothing if so
obj-$(CONFIG_PNP) += pnp/
obj-$(CONFIG_ARM_AMBA) += amba/
obj-y += amba/
# Many drivers will want to use DMA so this has to be made available
# really early.
obj-$(CONFIG_DMA_ENGINE) += dma/
......
obj-y += bus.o
obj-$(CONFIG_ARM_AMBA) += bus.o
obj-$(CONFIG_TEGRA_AHB) += tegra-ahb.o
/*
* Copyright (c) 2012, NVIDIA CORPORATION. All rights reserved.
* Copyright (C) 2011 Google, Inc.
*
* Author:
* Jay Cheng <jacheng@nvidia.com>
* James Wylder <james.wylder@motorola.com>
* Benoit Goby <benoit@android.com>
* Colin Cross <ccross@android.com>
* Hiroshi DOYU <hdoyu@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
* 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/module.h>
#include <linux/platform_device.h>
#include <linux/io.h>
#define DRV_NAME "tegra-ahb"
#define AHB_ARBITRATION_DISABLE 0x00
#define AHB_ARBITRATION_PRIORITY_CTRL 0x04
#define AHB_PRIORITY_WEIGHT(x) (((x) & 0x7) << 29)
#define PRIORITY_SELECT_USB BIT(6)
#define PRIORITY_SELECT_USB2 BIT(18)
#define PRIORITY_SELECT_USB3 BIT(17)
#define AHB_GIZMO_AHB_MEM 0x0c
#define ENB_FAST_REARBITRATE BIT(2)
#define DONT_SPLIT_AHB_WR BIT(7)
#define AHB_GIZMO_APB_DMA 0x10
#define AHB_GIZMO_IDE 0x18
#define AHB_GIZMO_USB 0x1c
#define AHB_GIZMO_AHB_XBAR_BRIDGE 0x20
#define AHB_GIZMO_CPU_AHB_BRIDGE 0x24
#define AHB_GIZMO_COP_AHB_BRIDGE 0x28
#define AHB_GIZMO_XBAR_APB_CTLR 0x2c
#define AHB_GIZMO_VCP_AHB_BRIDGE 0x30
#define AHB_GIZMO_NAND 0x3c
#define AHB_GIZMO_SDMMC4 0x44
#define AHB_GIZMO_XIO 0x48
#define AHB_GIZMO_BSEV 0x60
#define AHB_GIZMO_BSEA 0x70
#define AHB_GIZMO_NOR 0x74
#define AHB_GIZMO_USB2 0x78
#define AHB_GIZMO_USB3 0x7c
#define IMMEDIATE BIT(18)
#define AHB_GIZMO_SDMMC1 0x80
#define AHB_GIZMO_SDMMC2 0x84
#define AHB_GIZMO_SDMMC3 0x88
#define AHB_MEM_PREFETCH_CFG_X 0xd8
#define AHB_ARBITRATION_XBAR_CTRL 0xdc
#define AHB_MEM_PREFETCH_CFG3 0xe0
#define AHB_MEM_PREFETCH_CFG4 0xe4
#define AHB_MEM_PREFETCH_CFG1 0xec
#define AHB_MEM_PREFETCH_CFG2 0xf0
#define PREFETCH_ENB BIT(31)
#define MST_ID(x) (((x) & 0x1f) << 26)
#define AHBDMA_MST_ID MST_ID(5)
#define USB_MST_ID MST_ID(6)
#define USB2_MST_ID MST_ID(18)
#define USB3_MST_ID MST_ID(17)
#define ADDR_BNDRY(x) (((x) & 0xf) << 21)
#define INACTIVITY_TIMEOUT(x) (((x) & 0xffff) << 0)
#define AHB_ARBITRATION_AHB_MEM_WRQUE_MST_ID 0xf8
#define AHB_ARBITRATION_XBAR_CTRL_SMMU_INIT_DONE BIT(17)
static struct platform_driver tegra_ahb_driver;
static const u32 tegra_ahb_gizmo[] = {
AHB_ARBITRATION_DISABLE,
AHB_ARBITRATION_PRIORITY_CTRL,
AHB_GIZMO_AHB_MEM,
AHB_GIZMO_APB_DMA,
AHB_GIZMO_IDE,
AHB_GIZMO_USB,
AHB_GIZMO_AHB_XBAR_BRIDGE,
AHB_GIZMO_CPU_AHB_BRIDGE,
AHB_GIZMO_COP_AHB_BRIDGE,
AHB_GIZMO_XBAR_APB_CTLR,
AHB_GIZMO_VCP_AHB_BRIDGE,
AHB_GIZMO_NAND,
AHB_GIZMO_SDMMC4,
AHB_GIZMO_XIO,
AHB_GIZMO_BSEV,
AHB_GIZMO_BSEA,
AHB_GIZMO_NOR,
AHB_GIZMO_USB2,
AHB_GIZMO_USB3,
AHB_GIZMO_SDMMC1,
AHB_GIZMO_SDMMC2,
AHB_GIZMO_SDMMC3,
AHB_MEM_PREFETCH_CFG_X,
AHB_ARBITRATION_XBAR_CTRL,
AHB_MEM_PREFETCH_CFG3,
AHB_MEM_PREFETCH_CFG4,
AHB_MEM_PREFETCH_CFG1,
AHB_MEM_PREFETCH_CFG2,
AHB_ARBITRATION_AHB_MEM_WRQUE_MST_ID,
};
struct tegra_ahb {
void __iomem *regs;
struct device *dev;
u32 ctx[0];
};
static inline u32 gizmo_readl(struct tegra_ahb *ahb, u32 offset)
{
return readl(ahb->regs + offset);
}
static inline void gizmo_writel(struct tegra_ahb *ahb, u32 value, u32 offset)
{
writel(value, ahb->regs + offset);
}
#ifdef CONFIG_ARCH_TEGRA_3x_SOC
static int tegra_ahb_match_by_smmu(struct device *dev, void *data)
{
struct tegra_ahb *ahb = dev_get_drvdata(dev);
struct device_node *dn = data;
return (ahb->dev->of_node == dn) ? 1 : 0;
}
int tegra_ahb_enable_smmu(struct device_node *dn)
{
struct device *dev;
u32 val;
struct tegra_ahb *ahb;
dev = driver_find_device(&tegra_ahb_driver.driver, NULL, dn,
tegra_ahb_match_by_smmu);
if (!dev)
return -EPROBE_DEFER;
ahb = dev_get_drvdata(dev);
val = gizmo_readl(ahb, AHB_ARBITRATION_XBAR_CTRL);
val |= AHB_ARBITRATION_XBAR_CTRL_SMMU_INIT_DONE;
gizmo_writel(ahb, val, AHB_ARBITRATION_XBAR_CTRL);
return 0;
}
EXPORT_SYMBOL(tegra_ahb_enable_smmu);
#endif
static int tegra_ahb_suspend(struct device *dev)
{
int i;
struct tegra_ahb *ahb = dev_get_drvdata(dev);
for (i = 0; i < ARRAY_SIZE(tegra_ahb_gizmo); i++)
ahb->ctx[i] = gizmo_readl(ahb, tegra_ahb_gizmo[i]);
return 0;
}
static int tegra_ahb_resume(struct device *dev)
{
int i;
struct tegra_ahb *ahb = dev_get_drvdata(dev);
for (i = 0; i < ARRAY_SIZE(tegra_ahb_gizmo); i++)
gizmo_writel(ahb, ahb->ctx[i], tegra_ahb_gizmo[i]);
return 0;
}
static UNIVERSAL_DEV_PM_OPS(tegra_ahb_pm,
tegra_ahb_suspend,
tegra_ahb_resume, NULL);
static void tegra_ahb_gizmo_init(struct tegra_ahb *ahb)
{
u32 val;
val = gizmo_readl(ahb, AHB_GIZMO_AHB_MEM);
val |= ENB_FAST_REARBITRATE | IMMEDIATE | DONT_SPLIT_AHB_WR;
gizmo_writel(ahb, val, AHB_GIZMO_AHB_MEM);
val = gizmo_readl(ahb, AHB_GIZMO_USB);
val |= IMMEDIATE;
gizmo_writel(ahb, val, AHB_GIZMO_USB);
val = gizmo_readl(ahb, AHB_GIZMO_USB2);
val |= IMMEDIATE;
gizmo_writel(ahb, val, AHB_GIZMO_USB2);
val = gizmo_readl(ahb, AHB_GIZMO_USB3);
val |= IMMEDIATE;
gizmo_writel(ahb, val, AHB_GIZMO_USB3);
val = gizmo_readl(ahb, AHB_ARBITRATION_PRIORITY_CTRL);
val |= PRIORITY_SELECT_USB |
PRIORITY_SELECT_USB2 |
PRIORITY_SELECT_USB3 |
AHB_PRIORITY_WEIGHT(7);
gizmo_writel(ahb, val, AHB_ARBITRATION_PRIORITY_CTRL);
val = gizmo_readl(ahb, AHB_MEM_PREFETCH_CFG1);
val &= ~MST_ID(~0);
val |= PREFETCH_ENB |
AHBDMA_MST_ID |
ADDR_BNDRY(0xc) |
INACTIVITY_TIMEOUT(0x1000);
gizmo_writel(ahb, val, AHB_MEM_PREFETCH_CFG1);
val = gizmo_readl(ahb, AHB_MEM_PREFETCH_CFG2);
val &= ~MST_ID(~0);
val |= PREFETCH_ENB |
USB_MST_ID |
ADDR_BNDRY(0xc) |
INACTIVITY_TIMEOUT(0x1000);
gizmo_writel(ahb, val, AHB_MEM_PREFETCH_CFG2);
val = gizmo_readl(ahb, AHB_MEM_PREFETCH_CFG3);
val &= ~MST_ID(~0);
val |= PREFETCH_ENB |
USB3_MST_ID |
ADDR_BNDRY(0xc) |
INACTIVITY_TIMEOUT(0x1000);
gizmo_writel(ahb, val, AHB_MEM_PREFETCH_CFG3);
val = gizmo_readl(ahb, AHB_MEM_PREFETCH_CFG4);
val &= ~MST_ID(~0);
val |= PREFETCH_ENB |
USB2_MST_ID |
ADDR_BNDRY(0xc) |
INACTIVITY_TIMEOUT(0x1000);
gizmo_writel(ahb, val, AHB_MEM_PREFETCH_CFG4);
}
static int __devinit tegra_ahb_probe(struct platform_device *pdev)
{
struct resource *res;
struct tegra_ahb *ahb;
size_t bytes;
bytes = sizeof(*ahb) + sizeof(u32) * ARRAY_SIZE(tegra_ahb_gizmo);
ahb = devm_kzalloc(&pdev->dev, bytes, GFP_KERNEL);
if (!ahb)
return -ENOMEM;
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
if (!res)
return -ENODEV;
ahb->regs = devm_request_and_ioremap(&pdev->dev, res);
if (!ahb->regs)
return -EBUSY;
ahb->dev = &pdev->dev;
platform_set_drvdata(pdev, ahb);
tegra_ahb_gizmo_init(ahb);
return 0;
}
static int __devexit tegra_ahb_remove(struct platform_device *pdev)
{
return 0;
}
static const struct of_device_id tegra_ahb_of_match[] __devinitconst = {
{ .compatible = "nvidia,tegra30-ahb", },
{ .compatible = "nvidia,tegra20-ahb", },
{},
};
static struct platform_driver tegra_ahb_driver = {
.probe = tegra_ahb_probe,
.remove = __devexit_p(tegra_ahb_remove),
.driver = {
.name = DRV_NAME,
.owner = THIS_MODULE,
.of_match_table = tegra_ahb_of_match,
.pm = &tegra_ahb_pm,
},
};
module_platform_driver(tegra_ahb_driver);
MODULE_AUTHOR("Hiroshi DOYU <hdoyu@nvidia.com>");
MODULE_DESCRIPTION("Tegra AHB driver");
MODULE_LICENSE("GPL v2");
MODULE_ALIAS("platform:" DRV_NAME);
......@@ -71,6 +71,7 @@
#define M2M_CONTROL_TM_SHIFT 13
#define M2M_CONTROL_TM_TX (1 << M2M_CONTROL_TM_SHIFT)
#define M2M_CONTROL_TM_RX (2 << M2M_CONTROL_TM_SHIFT)
#define M2M_CONTROL_NFBINT BIT(21)
#define M2M_CONTROL_RSS_SHIFT 22
#define M2M_CONTROL_RSS_SSPRX (1 << M2M_CONTROL_RSS_SHIFT)
#define M2M_CONTROL_RSS_SSPTX (2 << M2M_CONTROL_RSS_SHIFT)
......@@ -79,7 +80,22 @@
#define M2M_CONTROL_PWSC_SHIFT 25
#define M2M_INTERRUPT 0x0004
#define M2M_INTERRUPT_DONEINT BIT(1)
#define M2M_INTERRUPT_MASK 6
#define M2M_STATUS 0x000c
#define M2M_STATUS_CTL_SHIFT 1
#define M2M_STATUS_CTL_IDLE (0 << M2M_STATUS_CTL_SHIFT)
#define M2M_STATUS_CTL_STALL (1 << M2M_STATUS_CTL_SHIFT)
#define M2M_STATUS_CTL_MEMRD (2 << M2M_STATUS_CTL_SHIFT)
#define M2M_STATUS_CTL_MEMWR (3 << M2M_STATUS_CTL_SHIFT)
#define M2M_STATUS_CTL_BWCWAIT (4 << M2M_STATUS_CTL_SHIFT)
#define M2M_STATUS_CTL_MASK (7 << M2M_STATUS_CTL_SHIFT)
#define M2M_STATUS_BUF_SHIFT 4
#define M2M_STATUS_BUF_NO (0 << M2M_STATUS_BUF_SHIFT)
#define M2M_STATUS_BUF_ON (1 << M2M_STATUS_BUF_SHIFT)
#define M2M_STATUS_BUF_NEXT (2 << M2M_STATUS_BUF_SHIFT)
#define M2M_STATUS_BUF_MASK (3 << M2M_STATUS_BUF_SHIFT)
#define M2M_STATUS_DONE BIT(6)
#define M2M_BCR0 0x0010
#define M2M_BCR1 0x0014
......@@ -426,15 +442,6 @@ static int m2p_hw_interrupt(struct ep93xx_dma_chan *edmac)
/*
* M2M DMA implementation
*
* For the M2M transfers we don't use NFB at all. This is because it simply
* doesn't work well with memcpy transfers. When you submit both buffers it is
* extremely unlikely that you get an NFB interrupt, but it instead reports
* DONE interrupt and both buffers are already transferred which means that we
* weren't able to update the next buffer.
*
* So for now we "simulate" NFB by just submitting buffer after buffer
* without double buffering.
*/
static int m2m_hw_setup(struct ep93xx_dma_chan *edmac)
......@@ -543,6 +550,11 @@ static void m2m_hw_submit(struct ep93xx_dma_chan *edmac)
m2m_fill_desc(edmac);
control |= M2M_CONTROL_DONEINT;
if (ep93xx_dma_advance_active(edmac)) {
m2m_fill_desc(edmac);
control |= M2M_CONTROL_NFBINT;
}
/*
* Now we can finally enable the channel. For M2M channel this must be
* done _after_ the BCRx registers are programmed.
......@@ -560,32 +572,89 @@ static void m2m_hw_submit(struct ep93xx_dma_chan *edmac)
}
}
/*
* According to EP93xx User's Guide, we should receive DONE interrupt when all
* M2M DMA controller transactions complete normally. This is not always the
* case - sometimes EP93xx M2M DMA asserts DONE interrupt when the DMA channel
* is still running (channel Buffer FSM in DMA_BUF_ON state, and channel
* Control FSM in DMA_MEM_RD state, observed at least in IDE-DMA operation).
* In effect, disabling the channel when only DONE bit is set could stop
* currently running DMA transfer. To avoid this, we use Buffer FSM and
* Control FSM to check current state of DMA channel.
*/
static int m2m_hw_interrupt(struct ep93xx_dma_chan *edmac)
{
u32 status = readl(edmac->regs + M2M_STATUS);
u32 ctl_fsm = status & M2M_STATUS_CTL_MASK;
u32 buf_fsm = status & M2M_STATUS_BUF_MASK;
bool done = status & M2M_STATUS_DONE;
bool last_done;
u32 control;
struct ep93xx_dma_desc *desc;
if (!(readl(edmac->regs + M2M_INTERRUPT) & M2M_INTERRUPT_DONEINT))
/* Accept only DONE and NFB interrupts */
if (!(readl(edmac->regs + M2M_INTERRUPT) & M2M_INTERRUPT_MASK))
return INTERRUPT_UNKNOWN;
/* Clear the DONE bit */
writel(0, edmac->regs + M2M_INTERRUPT);
if (done) {
/* Clear the DONE bit */
writel(0, edmac->regs + M2M_INTERRUPT);
}
/* Disable interrupts and the channel */
control = readl(edmac->regs + M2M_CONTROL);
control &= ~(M2M_CONTROL_DONEINT | M2M_CONTROL_ENABLE);
writel(control, edmac->regs + M2M_CONTROL);
/*
* Check whether we are done with descriptors or not. This, together
* with DMA channel state, determines action to take in interrupt.
*/
desc = ep93xx_dma_get_active(edmac);
last_done = !desc || desc->txd.cookie;
/*
* Since we only get DONE interrupt we have to find out ourselves
* whether there still is something to process. So we try to advance
* the chain an see whether it succeeds.
* Use M2M DMA Buffer FSM and Control FSM to check current state of
* DMA channel. Using DONE and NFB bits from channel status register
* or bits from channel interrupt register is not reliable.
*/
if (ep93xx_dma_advance_active(edmac)) {
edmac->edma->hw_submit(edmac);
return INTERRUPT_NEXT_BUFFER;
if (!last_done &&
(buf_fsm == M2M_STATUS_BUF_NO ||
buf_fsm == M2M_STATUS_BUF_ON)) {
/*
* Two buffers are ready for update when Buffer FSM is in
* DMA_NO_BUF state. Only one buffer can be prepared without
* disabling the channel or polling the DONE bit.
* To simplify things, always prepare only one buffer.
*/
if (ep93xx_dma_advance_active(edmac)) {
m2m_fill_desc(edmac);
if (done && !edmac->chan.private) {
/* Software trigger for memcpy channel */
control = readl(edmac->regs + M2M_CONTROL);
control |= M2M_CONTROL_START;
writel(control, edmac->regs + M2M_CONTROL);
}
return INTERRUPT_NEXT_BUFFER;
} else {
last_done = true;
}
}
/*
* Disable the channel only when Buffer FSM is in DMA_NO_BUF state
* and Control FSM is in DMA_STALL state.
*/
if (last_done &&
buf_fsm == M2M_STATUS_BUF_NO &&
ctl_fsm == M2M_STATUS_CTL_STALL) {
/* Disable interrupts and the channel */
control = readl(edmac->regs + M2M_CONTROL);
control &= ~(M2M_CONTROL_DONEINT | M2M_CONTROL_NFBINT
| M2M_CONTROL_ENABLE);
writel(control, edmac->regs + M2M_CONTROL);
return INTERRUPT_DONE;
}
return INTERRUPT_DONE;
/*
* Nothing to do this time.
*/
return INTERRUPT_NEXT_BUFFER;
}
/*
......
......@@ -311,7 +311,15 @@ static void pxa27x_keypad_scan_direct(struct pxa27x_keypad *keypad)
if (pdata->enable_rotary0 || pdata->enable_rotary1)
pxa27x_keypad_scan_rotary(keypad);
new_state = KPDK_DK(kpdk) & keypad->direct_key_mask;
/*
* The KPDR_DK only output the key pin level, so it relates to board,
* and low level may be active.
*/
if (pdata->direct_key_low_active)
new_state = ~KPDK_DK(kpdk) & keypad->direct_key_mask;
else
new_state = KPDK_DK(kpdk) & keypad->direct_key_mask;
bits_changed = keypad->direct_key_state ^ new_state;
if (bits_changed == 0)
......@@ -383,7 +391,14 @@ static void pxa27x_keypad_config(struct pxa27x_keypad *keypad)
if (pdata->direct_key_num > direct_key_num)
direct_key_num = pdata->direct_key_num;
keypad->direct_key_mask = ((2 << direct_key_num) - 1) & ~mask;
/*
* Direct keys usage may not start from KP_DKIN0, check the platfrom
* mask data to config the specific.
*/
if (pdata->direct_key_mask)
keypad->direct_key_mask = pdata->direct_key_mask;
else
keypad->direct_key_mask = ((1 << direct_key_num) - 1) & ~mask;
/* enable direct key */
if (direct_key_num)
......@@ -399,7 +414,7 @@ static int pxa27x_keypad_open(struct input_dev *dev)
struct pxa27x_keypad *keypad = input_get_drvdata(dev);
/* Enable unit clock */
clk_enable(keypad->clk);
clk_prepare_enable(keypad->clk);
pxa27x_keypad_config(keypad);
return 0;
......@@ -410,7 +425,7 @@ static void pxa27x_keypad_close(struct input_dev *dev)
struct pxa27x_keypad *keypad = input_get_drvdata(dev);
/* Disable clock unit */
clk_disable(keypad->clk);
clk_disable_unprepare(keypad->clk);
}
#ifdef CONFIG_PM
......@@ -419,10 +434,14 @@ static int pxa27x_keypad_suspend(struct device *dev)
struct platform_device *pdev = to_platform_device(dev);
struct pxa27x_keypad *keypad = platform_get_drvdata(pdev);
clk_disable(keypad->clk);
/*
* If the keypad is used a wake up source, clock can not be disabled.
* Or it can not detect the key pressing.
*/
if (device_may_wakeup(&pdev->dev))
enable_irq_wake(keypad->irq);
else
clk_disable_unprepare(keypad->clk);
return 0;
}
......@@ -433,19 +452,24 @@ static int pxa27x_keypad_resume(struct device *dev)
struct pxa27x_keypad *keypad = platform_get_drvdata(pdev);
struct input_dev *input_dev = keypad->input_dev;
if (device_may_wakeup(&pdev->dev))
/*
* If the keypad is used as wake up source, the clock is not turned
* off. So do not need configure it again.
*/
if (device_may_wakeup(&pdev->dev)) {
disable_irq_wake(keypad->irq);
} else {
mutex_lock(&input_dev->mutex);
mutex_lock(&input_dev->mutex);
if (input_dev->users) {
/* Enable unit clock */
clk_prepare_enable(keypad->clk);
pxa27x_keypad_config(keypad);
}
if (input_dev->users) {
/* Enable unit clock */
clk_enable(keypad->clk);
pxa27x_keypad_config(keypad);
mutex_unlock(&input_dev->mutex);
}
mutex_unlock(&input_dev->mutex);
return 0;
}
......
......@@ -162,4 +162,25 @@ config TEGRA_IOMMU_SMMU
space through the SMMU (System Memory Management Unit)
hardware included on Tegra SoCs.
config EXYNOS_IOMMU
bool "Exynos IOMMU Support"
depends on ARCH_EXYNOS && EXYNOS_DEV_SYSMMU
select IOMMU_API
help
Support for the IOMMU(System MMU) of Samsung Exynos application
processor family. This enables H/W multimedia accellerators to see
non-linear physical memory chunks as a linear memory in their
address spaces
If unsure, say N here.
config EXYNOS_IOMMU_DEBUG
bool "Debugging log for Exynos IOMMU"
depends on EXYNOS_IOMMU
help
Select this to see the detailed log message that shows what
happens in the IOMMU driver
Say N unless you need kernel log message for IOMMU debugging
endif # IOMMU_SUPPORT
......@@ -10,3 +10,4 @@ obj-$(CONFIG_OMAP_IOVMM) += omap-iovmm.o
obj-$(CONFIG_OMAP_IOMMU_DEBUG) += omap-iommu-debug.o
obj-$(CONFIG_TEGRA_IOMMU_GART) += tegra-gart.o
obj-$(CONFIG_TEGRA_IOMMU_SMMU) += tegra-smmu.o
obj-$(CONFIG_EXYNOS_IOMMU) += exynos-iommu.o
/* linux/drivers/iommu/exynos_iommu.c
*
* Copyright (c) 2011 Samsung Electronics Co., Ltd.
* http://www.samsung.com
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*/
#ifdef CONFIG_EXYNOS_IOMMU_DEBUG
#define DEBUG
#endif
#include <linux/io.h>
#include <linux/interrupt.h>
#include <linux/platform_device.h>
#include <linux/slab.h>
#include <linux/pm_runtime.h>
#include <linux/clk.h>
#include <linux/err.h>
#include <linux/mm.h>
#include <linux/iommu.h>
#include <linux/errno.h>
#include <linux/list.h>
#include <linux/memblock.h>
#include <linux/export.h>
#include <asm/cacheflush.h>
#include <asm/pgtable.h>
#include <mach/sysmmu.h>
/* We does not consider super section mapping (16MB) */
#define SECT_ORDER 20
#define LPAGE_ORDER 16
#define SPAGE_ORDER 12
#define SECT_SIZE (1 << SECT_ORDER)
#define LPAGE_SIZE (1 << LPAGE_ORDER)
#define SPAGE_SIZE (1 << SPAGE_ORDER)
#define SECT_MASK (~(SECT_SIZE - 1))
#define LPAGE_MASK (~(LPAGE_SIZE - 1))
#define SPAGE_MASK (~(SPAGE_SIZE - 1))
#define lv1ent_fault(sent) (((*(sent) & 3) == 0) || ((*(sent) & 3) == 3))
#define lv1ent_page(sent) ((*(sent) & 3) == 1)
#define lv1ent_section(sent) ((*(sent) & 3) == 2)
#define lv2ent_fault(pent) ((*(pent) & 3) == 0)
#define lv2ent_small(pent) ((*(pent) & 2) == 2)
#define lv2ent_large(pent) ((*(pent) & 3) == 1)
#define section_phys(sent) (*(sent) & SECT_MASK)
#define section_offs(iova) ((iova) & 0xFFFFF)
#define lpage_phys(pent) (*(pent) & LPAGE_MASK)
#define lpage_offs(iova) ((iova) & 0xFFFF)
#define spage_phys(pent) (*(pent) & SPAGE_MASK)
#define spage_offs(iova) ((iova) & 0xFFF)
#define lv1ent_offset(iova) ((iova) >> SECT_ORDER)
#define lv2ent_offset(iova) (((iova) & 0xFF000) >> SPAGE_ORDER)
#define NUM_LV1ENTRIES 4096
#define NUM_LV2ENTRIES 256
#define LV2TABLE_SIZE (NUM_LV2ENTRIES * sizeof(long))
#define SPAGES_PER_LPAGE (LPAGE_SIZE / SPAGE_SIZE)
#define lv2table_base(sent) (*(sent) & 0xFFFFFC00)
#define mk_lv1ent_sect(pa) ((pa) | 2)
#define mk_lv1ent_page(pa) ((pa) | 1)
#define mk_lv2ent_lpage(pa) ((pa) | 1)
#define mk_lv2ent_spage(pa) ((pa) | 2)
#define CTRL_ENABLE 0x5
#define CTRL_BLOCK 0x7
#define CTRL_DISABLE 0x0
#define REG_MMU_CTRL 0x000
#define REG_MMU_CFG 0x004
#define REG_MMU_STATUS 0x008
#define REG_MMU_FLUSH 0x00C
#define REG_MMU_FLUSH_ENTRY 0x010
#define REG_PT_BASE_ADDR 0x014
#define REG_INT_STATUS 0x018
#define REG_INT_CLEAR 0x01C
#define REG_PAGE_FAULT_ADDR 0x024
#define REG_AW_FAULT_ADDR 0x028
#define REG_AR_FAULT_ADDR 0x02C
#define REG_DEFAULT_SLAVE_ADDR 0x030
#define REG_MMU_VERSION 0x034
#define REG_PB0_SADDR 0x04C
#define REG_PB0_EADDR 0x050
#define REG_PB1_SADDR 0x054
#define REG_PB1_EADDR 0x058
static unsigned long *section_entry(unsigned long *pgtable, unsigned long iova)
{
return pgtable + lv1ent_offset(iova);
}
static unsigned long *page_entry(unsigned long *sent, unsigned long iova)
{
return (unsigned long *)__va(lv2table_base(sent)) + lv2ent_offset(iova);
}
enum exynos_sysmmu_inttype {
SYSMMU_PAGEFAULT,
SYSMMU_AR_MULTIHIT,
SYSMMU_AW_MULTIHIT,
SYSMMU_BUSERROR,
SYSMMU_AR_SECURITY,
SYSMMU_AR_ACCESS,
SYSMMU_AW_SECURITY,
SYSMMU_AW_PROTECTION, /* 7 */
SYSMMU_FAULT_UNKNOWN,
SYSMMU_FAULTS_NUM
};
/*
* @itype: type of fault.
* @pgtable_base: the physical address of page table base. This is 0 if @itype
* is SYSMMU_BUSERROR.
* @fault_addr: the device (virtual) address that the System MMU tried to
* translated. This is 0 if @itype is SYSMMU_BUSERROR.
*/
typedef int (*sysmmu_fault_handler_t)(enum exynos_sysmmu_inttype itype,
unsigned long pgtable_base, unsigned long fault_addr);
static unsigned short fault_reg_offset[SYSMMU_FAULTS_NUM] = {
REG_PAGE_FAULT_ADDR,
REG_AR_FAULT_ADDR,
REG_AW_FAULT_ADDR,
REG_DEFAULT_SLAVE_ADDR,
REG_AR_FAULT_ADDR,
REG_AR_FAULT_ADDR,
REG_AW_FAULT_ADDR,
REG_AW_FAULT_ADDR
};
static char *sysmmu_fault_name[SYSMMU_FAULTS_NUM] = {
"PAGE FAULT",
"AR MULTI-HIT FAULT",
"AW MULTI-HIT FAULT",
"BUS ERROR",
"AR SECURITY PROTECTION FAULT",
"AR ACCESS PROTECTION FAULT",
"AW SECURITY PROTECTION FAULT",
"AW ACCESS PROTECTION FAULT",
"UNKNOWN FAULT"
};
struct exynos_iommu_domain {
struct list_head clients; /* list of sysmmu_drvdata.node */
unsigned long *pgtable; /* lv1 page table, 16KB */
short *lv2entcnt; /* free lv2 entry counter for each section */
spinlock_t lock; /* lock for this structure */
spinlock_t pgtablelock; /* lock for modifying page table @ pgtable */
};
struct sysmmu_drvdata {
struct list_head node; /* entry of exynos_iommu_domain.clients */
struct device *sysmmu; /* System MMU's device descriptor */
struct device *dev; /* Owner of system MMU */
char *dbgname;
int nsfrs;
void __iomem **sfrbases;
struct clk *clk[2];
int activations;
rwlock_t lock;
struct iommu_domain *domain;
sysmmu_fault_handler_t fault_handler;
unsigned long pgtable;
};
static bool set_sysmmu_active(struct sysmmu_drvdata *data)
{
/* return true if the System MMU was not active previously
and it needs to be initialized */
return ++data->activations == 1;
}
static bool set_sysmmu_inactive(struct sysmmu_drvdata *data)
{
/* return true if the System MMU is needed to be disabled */
BUG_ON(data->activations < 1);
return --data->activations == 0;
}
static bool is_sysmmu_active(struct sysmmu_drvdata *data)
{
return data->activations > 0;
}
static void sysmmu_unblock(void __iomem *sfrbase)
{
__raw_writel(CTRL_ENABLE, sfrbase + REG_MMU_CTRL);
}
static bool sysmmu_block(void __iomem *sfrbase)
{
int i = 120;
__raw_writel(CTRL_BLOCK, sfrbase + REG_MMU_CTRL);
while ((i > 0) && !(__raw_readl(sfrbase + REG_MMU_STATUS) & 1))
--i;
if (!(__raw_readl(sfrbase + REG_MMU_STATUS) & 1)) {
sysmmu_unblock(sfrbase);
return false;
}
return true;
}
static void __sysmmu_tlb_invalidate(void __iomem *sfrbase)
{
__raw_writel(0x1, sfrbase + REG_MMU_FLUSH);
}
static void __sysmmu_tlb_invalidate_entry(void __iomem *sfrbase,
unsigned long iova)
{
__raw_writel((iova & SPAGE_MASK) | 1, sfrbase + REG_MMU_FLUSH_ENTRY);
}
static void __sysmmu_set_ptbase(void __iomem *sfrbase,
unsigned long pgd)
{
__raw_writel(0x1, sfrbase + REG_MMU_CFG); /* 16KB LV1, LRU */
__raw_writel(pgd, sfrbase + REG_PT_BASE_ADDR);
__sysmmu_tlb_invalidate(sfrbase);
}
static void __sysmmu_set_prefbuf(void __iomem *sfrbase, unsigned long base,
unsigned long size, int idx)
{
__raw_writel(base, sfrbase + REG_PB0_SADDR + idx * 8);
__raw_writel(size - 1 + base, sfrbase + REG_PB0_EADDR + idx * 8);
}
void exynos_sysmmu_set_prefbuf(struct device *dev,
unsigned long base0, unsigned long size0,
unsigned long base1, unsigned long size1)
{
struct sysmmu_drvdata *data = dev_get_drvdata(dev->archdata.iommu);
unsigned long flags;
int i;
BUG_ON((base0 + size0) <= base0);
BUG_ON((size1 > 0) && ((base1 + size1) <= base1));
read_lock_irqsave(&data->lock, flags);
if (!is_sysmmu_active(data))
goto finish;
for (i = 0; i < data->nsfrs; i++) {
if ((readl(data->sfrbases[i] + REG_MMU_VERSION) >> 28) == 3) {
if (!sysmmu_block(data->sfrbases[i]))
continue;
if (size1 == 0) {
if (size0 <= SZ_128K) {
base1 = base0;
size1 = size0;
} else {
size1 = size0 -
ALIGN(size0 / 2, SZ_64K);
size0 = size0 - size1;
base1 = base0 + size0;
}
}
__sysmmu_set_prefbuf(
data->sfrbases[i], base0, size0, 0);
__sysmmu_set_prefbuf(
data->sfrbases[i], base1, size1, 1);
sysmmu_unblock(data->sfrbases[i]);
}
}
finish:
read_unlock_irqrestore(&data->lock, flags);
}
static void __set_fault_handler(struct sysmmu_drvdata *data,
sysmmu_fault_handler_t handler)
{
unsigned long flags;
write_lock_irqsave(&data->lock, flags);
data->fault_handler = handler;
write_unlock_irqrestore(&data->lock, flags);
}
void exynos_sysmmu_set_fault_handler(struct device *dev,
sysmmu_fault_handler_t handler)
{
struct sysmmu_drvdata *data = dev_get_drvdata(dev->archdata.iommu);
__set_fault_handler(data, handler);
}
static int default_fault_handler(enum exynos_sysmmu_inttype itype,
unsigned long pgtable_base, unsigned long fault_addr)
{
unsigned long *ent;
if ((itype >= SYSMMU_FAULTS_NUM) || (itype < SYSMMU_PAGEFAULT))
itype = SYSMMU_FAULT_UNKNOWN;
pr_err("%s occured at 0x%lx(Page table base: 0x%lx)\n",
sysmmu_fault_name[itype], fault_addr, pgtable_base);
ent = section_entry(__va(pgtable_base), fault_addr);
pr_err("\tLv1 entry: 0x%lx\n", *ent);
if (lv1ent_page(ent)) {
ent = page_entry(ent, fault_addr);
pr_err("\t Lv2 entry: 0x%lx\n", *ent);
}
pr_err("Generating Kernel OOPS... because it is unrecoverable.\n");
BUG();
return 0;
}
static irqreturn_t exynos_sysmmu_irq(int irq, void *dev_id)
{
/* SYSMMU is in blocked when interrupt occurred. */
struct sysmmu_drvdata *data = dev_id;
struct resource *irqres;
struct platform_device *pdev;
enum exynos_sysmmu_inttype itype;
unsigned long addr = -1;
int i, ret = -ENOSYS;
read_lock(&data->lock);
WARN_ON(!is_sysmmu_active(data));
pdev = to_platform_device(data->sysmmu);
for (i = 0; i < (pdev->num_resources / 2); i++) {
irqres = platform_get_resource(pdev, IORESOURCE_IRQ, i);
if (irqres && ((int)irqres->start == irq))
break;
}
if (i == pdev->num_resources) {
itype = SYSMMU_FAULT_UNKNOWN;
} else {
itype = (enum exynos_sysmmu_inttype)
__ffs(__raw_readl(data->sfrbases[i] + REG_INT_STATUS));
if (WARN_ON(!((itype >= 0) && (itype < SYSMMU_FAULT_UNKNOWN))))
itype = SYSMMU_FAULT_UNKNOWN;
else
addr = __raw_readl(
data->sfrbases[i] + fault_reg_offset[itype]);
}
if (data->domain)
ret = report_iommu_fault(data->domain, data->dev,
addr, itype);
if ((ret == -ENOSYS) && data->fault_handler) {
unsigned long base = data->pgtable;
if (itype != SYSMMU_FAULT_UNKNOWN)
base = __raw_readl(
data->sfrbases[i] + REG_PT_BASE_ADDR);
ret = data->fault_handler(itype, base, addr);
}
if (!ret && (itype != SYSMMU_FAULT_UNKNOWN))
__raw_writel(1 << itype, data->sfrbases[i] + REG_INT_CLEAR);
else
dev_dbg(data->sysmmu, "(%s) %s is not handled.\n",
data->dbgname, sysmmu_fault_name[itype]);
if (itype != SYSMMU_FAULT_UNKNOWN)
sysmmu_unblock(data->sfrbases[i]);
read_unlock(&data->lock);
return IRQ_HANDLED;
}
static bool __exynos_sysmmu_disable(struct sysmmu_drvdata *data)
{
unsigned long flags;
bool disabled = false;
int i;
write_lock_irqsave(&data->lock, flags);
if (!set_sysmmu_inactive(data))
goto finish;
for (i = 0; i < data->nsfrs; i++)
__raw_writel(CTRL_DISABLE, data->sfrbases[i] + REG_MMU_CTRL);
if (data->clk[1])
clk_disable(data->clk[1]);
if (data->clk[0])
clk_disable(data->clk[0]);
disabled = true;
data->pgtable = 0;
data->domain = NULL;
finish:
write_unlock_irqrestore(&data->lock, flags);
if (disabled)
dev_dbg(data->sysmmu, "(%s) Disabled\n", data->dbgname);
else
dev_dbg(data->sysmmu, "(%s) %d times left to be disabled\n",
data->dbgname, data->activations);
return disabled;
}
/* __exynos_sysmmu_enable: Enables System MMU
*
* returns -error if an error occurred and System MMU is not enabled,
* 0 if the System MMU has been just enabled and 1 if System MMU was already
* enabled before.
*/
static int __exynos_sysmmu_enable(struct sysmmu_drvdata *data,
unsigned long pgtable, struct iommu_domain *domain)
{
int i, ret = 0;
unsigned long flags;
write_lock_irqsave(&data->lock, flags);
if (!set_sysmmu_active(data)) {
if (WARN_ON(pgtable != data->pgtable)) {
ret = -EBUSY;
set_sysmmu_inactive(data);
} else {
ret = 1;
}
dev_dbg(data->sysmmu, "(%s) Already enabled\n", data->dbgname);
goto finish;
}
if (data->clk[0])
clk_enable(data->clk[0]);
if (data->clk[1])
clk_enable(data->clk[1]);
data->pgtable = pgtable;
for (i = 0; i < data->nsfrs; i++) {
__sysmmu_set_ptbase(data->sfrbases[i], pgtable);
if ((readl(data->sfrbases[i] + REG_MMU_VERSION) >> 28) == 3) {
/* System MMU version is 3.x */
__raw_writel((1 << 12) | (2 << 28),
data->sfrbases[i] + REG_MMU_CFG);
__sysmmu_set_prefbuf(data->sfrbases[i], 0, -1, 0);
__sysmmu_set_prefbuf(data->sfrbases[i], 0, -1, 1);
}
__raw_writel(CTRL_ENABLE, data->sfrbases[i] + REG_MMU_CTRL);
}
data->domain = domain;
dev_dbg(data->sysmmu, "(%s) Enabled\n", data->dbgname);
finish:
write_unlock_irqrestore(&data->lock, flags);
return ret;
}
int exynos_sysmmu_enable(struct device *dev, unsigned long pgtable)
{
struct sysmmu_drvdata *data = dev_get_drvdata(dev->archdata.iommu);
int ret;
BUG_ON(!memblock_is_memory(pgtable));
ret = pm_runtime_get_sync(data->sysmmu);
if (ret < 0) {
dev_dbg(data->sysmmu, "(%s) Failed to enable\n", data->dbgname);
return ret;
}
ret = __exynos_sysmmu_enable(data, pgtable, NULL);
if (WARN_ON(ret < 0)) {
pm_runtime_put(data->sysmmu);
dev_err(data->sysmmu,
"(%s) Already enabled with page table %#lx\n",
data->dbgname, data->pgtable);
} else {
data->dev = dev;
}
return ret;
}
bool exynos_sysmmu_disable(struct device *dev)
{
struct sysmmu_drvdata *data = dev_get_drvdata(dev->archdata.iommu);
bool disabled;
disabled = __exynos_sysmmu_disable(data);
pm_runtime_put(data->sysmmu);
return disabled;
}
static void sysmmu_tlb_invalidate_entry(struct device *dev, unsigned long iova)
{
unsigned long flags;
struct sysmmu_drvdata *data = dev_get_drvdata(dev->archdata.iommu);
read_lock_irqsave(&data->lock, flags);
if (is_sysmmu_active(data)) {
int i;
for (i = 0; i < data->nsfrs; i++) {
if (sysmmu_block(data->sfrbases[i])) {
__sysmmu_tlb_invalidate_entry(
data->sfrbases[i], iova);
sysmmu_unblock(data->sfrbases[i]);
}
}
} else {
dev_dbg(data->sysmmu,
"(%s) Disabled. Skipping invalidating TLB.\n",
data->dbgname);
}
read_unlock_irqrestore(&data->lock, flags);
}
void exynos_sysmmu_tlb_invalidate(struct device *dev)
{
unsigned long flags;
struct sysmmu_drvdata *data = dev_get_drvdata(dev->archdata.iommu);
read_lock_irqsave(&data->lock, flags);
if (is_sysmmu_active(data)) {
int i;
for (i = 0; i < data->nsfrs; i++) {
if (sysmmu_block(data->sfrbases[i])) {
__sysmmu_tlb_invalidate(data->sfrbases[i]);
sysmmu_unblock(data->sfrbases[i]);
}
}
} else {
dev_dbg(data->sysmmu,
"(%s) Disabled. Skipping invalidating TLB.\n",
data->dbgname);
}
read_unlock_irqrestore(&data->lock, flags);
}
static int exynos_sysmmu_probe(struct platform_device *pdev)
{
int i, ret;
struct device *dev;
struct sysmmu_drvdata *data;
dev = &pdev->dev;
data = kzalloc(sizeof(*data), GFP_KERNEL);
if (!data) {
dev_dbg(dev, "Not enough memory\n");
ret = -ENOMEM;
goto err_alloc;
}
ret = dev_set_drvdata(dev, data);
if (ret) {
dev_dbg(dev, "Unabled to initialize driver data\n");
goto err_init;
}
data->nsfrs = pdev->num_resources / 2;
data->sfrbases = kmalloc(sizeof(*data->sfrbases) * data->nsfrs,
GFP_KERNEL);
if (data->sfrbases == NULL) {
dev_dbg(dev, "Not enough memory\n");
ret = -ENOMEM;
goto err_init;
}
for (i = 0; i < data->nsfrs; i++) {
struct resource *res;
res = platform_get_resource(pdev, IORESOURCE_MEM, i);
if (!res) {
dev_dbg(dev, "Unable to find IOMEM region\n");
ret = -ENOENT;
goto err_res;
}
data->sfrbases[i] = ioremap(res->start, resource_size(res));
if (!data->sfrbases[i]) {
dev_dbg(dev, "Unable to map IOMEM @ PA:%#x\n",
res->start);
ret = -ENOENT;
goto err_res;
}
}
for (i = 0; i < data->nsfrs; i++) {
ret = platform_get_irq(pdev, i);
if (ret <= 0) {
dev_dbg(dev, "Unable to find IRQ resource\n");
goto err_irq;
}
ret = request_irq(ret, exynos_sysmmu_irq, 0,
dev_name(dev), data);
if (ret) {
dev_dbg(dev, "Unabled to register interrupt handler\n");
goto err_irq;
}
}
if (dev_get_platdata(dev)) {
char *deli, *beg;
struct sysmmu_platform_data *platdata = dev_get_platdata(dev);
beg = platdata->clockname;
for (deli = beg; (*deli != '\0') && (*deli != ','); deli++)
/* NOTHING */;
if (*deli == '\0')
deli = NULL;
else
*deli = '\0';
data->clk[0] = clk_get(dev, beg);
if (IS_ERR(data->clk[0])) {
data->clk[0] = NULL;
dev_dbg(dev, "No clock descriptor registered\n");
}
if (data->clk[0] && deli) {
*deli = ',';
data->clk[1] = clk_get(dev, deli + 1);
if (IS_ERR(data->clk[1]))
data->clk[1] = NULL;
}
data->dbgname = platdata->dbgname;
}
data->sysmmu = dev;
rwlock_init(&data->lock);
INIT_LIST_HEAD(&data->node);
__set_fault_handler(data, &default_fault_handler);
if (dev->parent)
pm_runtime_enable(dev);
dev_dbg(dev, "(%s) Initialized\n", data->dbgname);
return 0;
err_irq:
while (i-- > 0) {
int irq;
irq = platform_get_irq(pdev, i);
free_irq(irq, data);
}
err_res:
while (data->nsfrs-- > 0)
iounmap(data->sfrbases[data->nsfrs]);
kfree(data->sfrbases);
err_init:
kfree(data);
err_alloc:
dev_err(dev, "Failed to initialize\n");
return ret;
}
static struct platform_driver exynos_sysmmu_driver = {
.probe = exynos_sysmmu_probe,
.driver = {
.owner = THIS_MODULE,
.name = "exynos-sysmmu",
}
};
static inline void pgtable_flush(void *vastart, void *vaend)
{
dmac_flush_range(vastart, vaend);
outer_flush_range(virt_to_phys(vastart),
virt_to_phys(vaend));
}
static int exynos_iommu_domain_init(struct iommu_domain *domain)
{
struct exynos_iommu_domain *priv;
priv = kzalloc(sizeof(*priv), GFP_KERNEL);
if (!priv)
return -ENOMEM;
priv->pgtable = (unsigned long *)__get_free_pages(
GFP_KERNEL | __GFP_ZERO, 2);
if (!priv->pgtable)
goto err_pgtable;
priv->lv2entcnt = (short *)__get_free_pages(
GFP_KERNEL | __GFP_ZERO, 1);
if (!priv->lv2entcnt)
goto err_counter;
pgtable_flush(priv->pgtable, priv->pgtable + NUM_LV1ENTRIES);
spin_lock_init(&priv->lock);
spin_lock_init(&priv->pgtablelock);
INIT_LIST_HEAD(&priv->clients);
domain->priv = priv;
return 0;
err_counter:
free_pages((unsigned long)priv->pgtable, 2);
err_pgtable:
kfree(priv);
return -ENOMEM;
}
static void exynos_iommu_domain_destroy(struct iommu_domain *domain)
{
struct exynos_iommu_domain *priv = domain->priv;
struct sysmmu_drvdata *data;
unsigned long flags;
int i;
WARN_ON(!list_empty(&priv->clients));
spin_lock_irqsave(&priv->lock, flags);
list_for_each_entry(data, &priv->clients, node) {
while (!exynos_sysmmu_disable(data->dev))
; /* until System MMU is actually disabled */
}
spin_unlock_irqrestore(&priv->lock, flags);
for (i = 0; i < NUM_LV1ENTRIES; i++)
if (lv1ent_page(priv->pgtable + i))
kfree(__va(lv2table_base(priv->pgtable + i)));
free_pages((unsigned long)priv->pgtable, 2);
free_pages((unsigned long)priv->lv2entcnt, 1);
kfree(domain->priv);
domain->priv = NULL;
}
static int exynos_iommu_attach_device(struct iommu_domain *domain,
struct device *dev)
{
struct sysmmu_drvdata *data = dev_get_drvdata(dev->archdata.iommu);
struct exynos_iommu_domain *priv = domain->priv;
unsigned long flags;
int ret;
ret = pm_runtime_get_sync(data->sysmmu);
if (ret < 0)
return ret;
ret = 0;
spin_lock_irqsave(&priv->lock, flags);
ret = __exynos_sysmmu_enable(data, __pa(priv->pgtable), domain);
if (ret == 0) {
/* 'data->node' must not be appeared in priv->clients */
BUG_ON(!list_empty(&data->node));
data->dev = dev;
list_add_tail(&data->node, &priv->clients);
}
spin_unlock_irqrestore(&priv->lock, flags);
if (ret < 0) {
dev_err(dev, "%s: Failed to attach IOMMU with pgtable %#lx\n",
__func__, __pa(priv->pgtable));
pm_runtime_put(data->sysmmu);
} else if (ret > 0) {
dev_dbg(dev, "%s: IOMMU with pgtable 0x%lx already attached\n",
__func__, __pa(priv->pgtable));
} else {
dev_dbg(dev, "%s: Attached new IOMMU with pgtable 0x%lx\n",
__func__, __pa(priv->pgtable));
}
return ret;
}
static void exynos_iommu_detach_device(struct iommu_domain *domain,
struct device *dev)
{
struct sysmmu_drvdata *data = dev_get_drvdata(dev->archdata.iommu);
struct exynos_iommu_domain *priv = domain->priv;
struct list_head *pos;
unsigned long flags;
bool found = false;
spin_lock_irqsave(&priv->lock, flags);
list_for_each(pos, &priv->clients) {
if (list_entry(pos, struct sysmmu_drvdata, node) == data) {
found = true;
break;
}
}
if (!found)
goto finish;
if (__exynos_sysmmu_disable(data)) {
dev_dbg(dev, "%s: Detached IOMMU with pgtable %#lx\n",
__func__, __pa(priv->pgtable));
list_del(&data->node);
INIT_LIST_HEAD(&data->node);
} else {
dev_dbg(dev, "%s: Detaching IOMMU with pgtable %#lx delayed",
__func__, __pa(priv->pgtable));
}
finish:
spin_unlock_irqrestore(&priv->lock, flags);
if (found)
pm_runtime_put(data->sysmmu);
}
static unsigned long *alloc_lv2entry(unsigned long *sent, unsigned long iova,
short *pgcounter)
{
if (lv1ent_fault(sent)) {
unsigned long *pent;
pent = kzalloc(LV2TABLE_SIZE, GFP_ATOMIC);
BUG_ON((unsigned long)pent & (LV2TABLE_SIZE - 1));
if (!pent)
return NULL;
*sent = mk_lv1ent_page(__pa(pent));
*pgcounter = NUM_LV2ENTRIES;
pgtable_flush(pent, pent + NUM_LV2ENTRIES);
pgtable_flush(sent, sent + 1);
}
return page_entry(sent, iova);
}
static int lv1set_section(unsigned long *sent, phys_addr_t paddr, short *pgcnt)
{
if (lv1ent_section(sent))
return -EADDRINUSE;
if (lv1ent_page(sent)) {
if (*pgcnt != NUM_LV2ENTRIES)
return -EADDRINUSE;
kfree(page_entry(sent, 0));
*pgcnt = 0;
}
*sent = mk_lv1ent_sect(paddr);
pgtable_flush(sent, sent + 1);
return 0;
}
static int lv2set_page(unsigned long *pent, phys_addr_t paddr, size_t size,
short *pgcnt)
{
if (size == SPAGE_SIZE) {
if (!lv2ent_fault(pent))
return -EADDRINUSE;
*pent = mk_lv2ent_spage(paddr);
pgtable_flush(pent, pent + 1);
*pgcnt -= 1;
} else { /* size == LPAGE_SIZE */
int i;
for (i = 0; i < SPAGES_PER_LPAGE; i++, pent++) {
if (!lv2ent_fault(pent)) {
memset(pent, 0, sizeof(*pent) * i);
return -EADDRINUSE;
}
*pent = mk_lv2ent_lpage(paddr);
}
pgtable_flush(pent - SPAGES_PER_LPAGE, pent);
*pgcnt -= SPAGES_PER_LPAGE;
}
return 0;
}
static int exynos_iommu_map(struct iommu_domain *domain, unsigned long iova,
phys_addr_t paddr, size_t size, int prot)
{
struct exynos_iommu_domain *priv = domain->priv;
unsigned long *entry;
unsigned long flags;
int ret = -ENOMEM;
BUG_ON(priv->pgtable == NULL);
spin_lock_irqsave(&priv->pgtablelock, flags);
entry = section_entry(priv->pgtable, iova);
if (size == SECT_SIZE) {
ret = lv1set_section(entry, paddr,
&priv->lv2entcnt[lv1ent_offset(iova)]);
} else {
unsigned long *pent;
pent = alloc_lv2entry(entry, iova,
&priv->lv2entcnt[lv1ent_offset(iova)]);
if (!pent)
ret = -ENOMEM;
else
ret = lv2set_page(pent, paddr, size,
&priv->lv2entcnt[lv1ent_offset(iova)]);
}
if (ret) {
pr_debug("%s: Failed to map iova 0x%lx/0x%x bytes\n",
__func__, iova, size);
}
spin_unlock_irqrestore(&priv->pgtablelock, flags);
return ret;
}
static size_t exynos_iommu_unmap(struct iommu_domain *domain,
unsigned long iova, size_t size)
{
struct exynos_iommu_domain *priv = domain->priv;
struct sysmmu_drvdata *data;
unsigned long flags;
unsigned long *ent;
BUG_ON(priv->pgtable == NULL);
spin_lock_irqsave(&priv->pgtablelock, flags);
ent = section_entry(priv->pgtable, iova);
if (lv1ent_section(ent)) {
BUG_ON(size < SECT_SIZE);
*ent = 0;
pgtable_flush(ent, ent + 1);
size = SECT_SIZE;
goto done;
}
if (unlikely(lv1ent_fault(ent))) {
if (size > SECT_SIZE)
size = SECT_SIZE;
goto done;
}
/* lv1ent_page(sent) == true here */
ent = page_entry(ent, iova);
if (unlikely(lv2ent_fault(ent))) {
size = SPAGE_SIZE;
goto done;
}
if (lv2ent_small(ent)) {
*ent = 0;
size = SPAGE_SIZE;
priv->lv2entcnt[lv1ent_offset(iova)] += 1;
goto done;
}
/* lv1ent_large(ent) == true here */
BUG_ON(size < LPAGE_SIZE);
memset(ent, 0, sizeof(*ent) * SPAGES_PER_LPAGE);
size = LPAGE_SIZE;
priv->lv2entcnt[lv1ent_offset(iova)] += SPAGES_PER_LPAGE;
done:
spin_unlock_irqrestore(&priv->pgtablelock, flags);
spin_lock_irqsave(&priv->lock, flags);
list_for_each_entry(data, &priv->clients, node)
sysmmu_tlb_invalidate_entry(data->dev, iova);
spin_unlock_irqrestore(&priv->lock, flags);
return size;
}
static phys_addr_t exynos_iommu_iova_to_phys(struct iommu_domain *domain,
unsigned long iova)
{
struct exynos_iommu_domain *priv = domain->priv;
unsigned long *entry;
unsigned long flags;
phys_addr_t phys = 0;
spin_lock_irqsave(&priv->pgtablelock, flags);
entry = section_entry(priv->pgtable, iova);
if (lv1ent_section(entry)) {
phys = section_phys(entry) + section_offs(iova);
} else if (lv1ent_page(entry)) {
entry = page_entry(entry, iova);
if (lv2ent_large(entry))
phys = lpage_phys(entry) + lpage_offs(iova);
else if (lv2ent_small(entry))
phys = spage_phys(entry) + spage_offs(iova);
}
spin_unlock_irqrestore(&priv->pgtablelock, flags);
return phys;
}
static struct iommu_ops exynos_iommu_ops = {
.domain_init = &exynos_iommu_domain_init,
.domain_destroy = &exynos_iommu_domain_destroy,
.attach_dev = &exynos_iommu_attach_device,
.detach_dev = &exynos_iommu_detach_device,
.map = &exynos_iommu_map,
.unmap = &exynos_iommu_unmap,
.iova_to_phys = &exynos_iommu_iova_to_phys,
.pgsize_bitmap = SECT_SIZE | LPAGE_SIZE | SPAGE_SIZE,
};
static int __init exynos_iommu_init(void)
{
int ret;
ret = platform_driver_register(&exynos_sysmmu_driver);
if (ret == 0)
bus_set_iommu(&platform_bus_type, &exynos_iommu_ops);
return ret;
}
subsys_initcall(exynos_iommu_init);
......@@ -1781,7 +1781,7 @@ static struct omap_mmc_platform_data *of_get_hsmmc_pdata(struct device *dev)
pdata->slots[0].nonremovable = true;
pdata->slots[0].no_regulator_off_init = true;
}
of_property_read_u32(np, "ti,bus-width", &bus_width);
of_property_read_u32(np, "bus-width", &bus_width);
if (bus_width == 4)
pdata->slots[0].caps |= MMC_CAP_4_BIT_DATA;
else if (bus_width == 8)
......
......@@ -404,7 +404,7 @@ sdhci_esdhc_imx_probe_dt(struct platform_device *pdev,
if (!np)
return -ENODEV;
if (of_get_property(np, "fsl,card-wired", NULL))
if (of_get_property(np, "non-removable", NULL))
boarddata->cd_type = ESDHC_CD_PERMANENT;
if (of_get_property(np, "fsl,cd-controller", NULL))
......
......@@ -42,7 +42,8 @@ static struct sdhci_ops sdhci_pltfm_ops = {
#ifdef CONFIG_OF
static bool sdhci_of_wp_inverted(struct device_node *np)
{
if (of_get_property(np, "sdhci,wp-inverted", NULL))
if (of_get_property(np, "sdhci,wp-inverted", NULL) ||
of_get_property(np, "wp-inverted", NULL))
return true;
/* Old device trees don't have the wp-inverted property. */
......@@ -59,13 +60,16 @@ void sdhci_get_of_property(struct platform_device *pdev)
struct sdhci_host *host = platform_get_drvdata(pdev);
struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
const __be32 *clk;
u32 bus_width;
int size;
if (of_device_is_available(np)) {
if (of_get_property(np, "sdhci,auto-cmd12", NULL))
host->quirks |= SDHCI_QUIRK_MULTIBLOCK_READ_ACMD12;
if (of_get_property(np, "sdhci,1-bit-only", NULL))
if (of_get_property(np, "sdhci,1-bit-only", NULL) ||
(of_property_read_u32(np, "bus-width", &bus_width) == 0 &&
bus_width == 1))
host->quirks |= SDHCI_QUIRK_FORCE_1_BIT_DATA;
if (sdhci_of_wp_inverted(np))
......
......@@ -722,8 +722,9 @@ static int tegra_ehci_probe(struct platform_device *pdev)
}
}
tegra->phy = tegra_usb_phy_open(instance, hcd->regs, pdata->phy_config,
TEGRA_USB_PHY_MODE_HOST);
tegra->phy = tegra_usb_phy_open(&pdev->dev, instance, hcd->regs,
pdata->phy_config,
TEGRA_USB_PHY_MODE_HOST);
if (IS_ERR(tegra->phy)) {
dev_err(&pdev->dev, "Failed to open USB phy\n");
err = -ENXIO;
......
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