dect
/
linux-2.6
Archived
13
0
Fork 0

ARM: arm-soc: Updates for Marvell mvebu/kirkwood

This is a branch with updates for Marvell's mvebu/kirkwood platforms. They
 came in late-ish, and were heavily interdependent such that it didn't
 make sense to split them up across the cross-platform topic branches. So
 here they are (for the second release in a row) in a branch on their own.
 -----BEGIN PGP SIGNATURE-----
 Version: GnuPG v1.4.11 (GNU/Linux)
 
 iQIcBAABAgAGBQJQy5i9AAoJEIwa5zzehBx3ZskP/2wxjbwEaNdnR+7j8595bTaa
 GYq8qJ4lUCOKmSqp3bQkg/Plm2D88p78BO5qTm2io527gl10HemzCiGaejclujIw
 sDFZPAE8K0Z8p0gQcBNlRZNuI3J1N6IKRqYH5SIJ2vWmBMfO7nKRR9nmTiDpm5bx
 IcuKX2u/mhyXWN+F0EcHqcupH1K+mdzyGdIQk80Tyqni+UTN+pd0efLM6WL4SFJM
 5fj64dDFpVDA8t+O2Avz8p+lx07vkSy2wIXWt7Ik9BVtsyZQecn+9lpl8FvcrSK/
 MgL3QO4kqDpJDs88M7DJURU1/EdsWZc32M63avctaWnGWItQAbOJYBDmZTlng08x
 ZGrKOgf/I6le7wEpnzdag9ymI/rAL8I0755FkfXxf1R7/X40b+t8/61J/ddOKTDs
 1sTVt+eKyyIMWle4V4zENa03goVBApCIEXcmnuFisFNbBY6azV31inJEp/3PvpgE
 GeMBfxBDkvn+03LkRFcZlhTeDsNTdctD+sfgrNPaQf5bZGIvEz87vgfNTIiaU3GA
 Vd5aiainVDQgmpoFfRG6391gdFlF2l9d67LoG4ClCjn4WL+UxcTRuzBW/liORpUO
 E7CwMHtPq6eoGKywiKMFRzY2QRIKZRkxrC2PCJ/1V9mbIGwgaD/3BQ2/czwrnc8q
 1gnxWx8E5SKEGcDJXD+6
 =7luC
 -----END PGP SIGNATURE-----

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

Pull ARM SoC updates for Marvell mvebu/kirkwood from Olof Johansson:
 "This is a branch with updates for Marvell's mvebu/kirkwood platforms.
  They came in late-ish, and were heavily interdependent such that it
  didn't make sense to split them up across the cross-platform topic
  branches.  So here they are (for the second release in a row) in a
  branch on their own."

* tag 'mvebu' of git://git.kernel.org/pub/scm/linux/kernel/git/arm/arm-soc: (88 commits)
  arm: l2x0: add aurora related properties to OF binding
  arm: mvebu: add Aurora L2 Cache Controller to the DT
  arm: mvebu: add L2 cache support
  dma: mv_xor: fix error handling path
  dma: mv_xor: fix error checking of irq_of_parse_and_map()
  dma: mv_xor: use request_irq() instead of devm_request_irq()
  dma: mv_xor: clear the window override control registers
  arm: mvebu: fix address decoding armada_cfg_base() function
  ARM: mvebu: update defconfig with I2C and RTC support
  ARM: mvebu: Add SATA support for OpenBlocks AX3-4
  ARM: mvebu: Add support for the RTC in OpenBlocks AX3-4
  ARM: mvebu: Add support for I2C on OpenBlocks AX3-4
  ARM: mvebu: Add support for I2C controllers in Armada 370/XP
  arm: mvebu: Add hardware I/O Coherency support
  arm: plat-orion: Add coherency attribute when setup mbus target
  arm: dma mapping: Export a dma ops function arm_dma_set_mask
  arm: mvebu: Add SMP support for Armada XP
  arm: mm: Add support for PJ4B cpu and init routines
  arm: mvebu: Add IPI support via doorbells
  arm: mvebu: Add initial support for power managmement service unit
  ...
This commit is contained in:
Linus Torvalds 2012-12-14 14:54:26 -08:00
commit c2714334b9
76 changed files with 6531 additions and 415 deletions

View File

@ -6,9 +6,15 @@ Required properties:
- interrupt-controller: Identifies the node as an interrupt controller.
- #interrupt-cells: The number of cells to define the interrupts. Should be 1.
The cell is the IRQ number
- reg: Should contain PMIC registers location and length. First pair
for the main interrupt registers, second pair for the per-CPU
interrupt registers
interrupt registers. For this last pair, to be compliant with SMP
support, the "virtual" must be use (For the record, these registers
automatically map to the interrupt controller registers of the
current CPU)
Example:
@ -18,6 +24,6 @@ Example:
#address-cells = <1>;
#size-cells = <1>;
interrupt-controller;
reg = <0xd0020000 0x1000>,
<0xd0021000 0x1000>;
reg = <0xd0020a00 0x1d0>,
<0xd0021070 0x58>;
};

View File

@ -0,0 +1,20 @@
Power Management Service Unit(PMSU)
-----------------------------------
Available on Marvell SOCs: Armada 370 and Armada XP
Required properties:
- compatible: "marvell,armada-370-xp-pmsu"
- reg: Should contain PMSU registers location and length. First pair
for the per-CPU SW Reset Control registers, second pair for the
Power Management Service Unit.
Example:
armada-370-xp-pmsu@d0022000 {
compatible = "marvell,armada-370-xp-pmsu";
reg = <0xd0022100 0x430>,
<0xd0020800 0x20>;
};

View File

@ -5,6 +5,7 @@ Required properties:
- compatible: Should be "marvell,armada-370-xp-timer"
- interrupts: Should contain the list of Global Timer interrupts
- reg: Should contain the base address of the Global Timer registers
- clocks: clock driving the timer hardware
Optional properties:
- marvell,timer-25Mhz: Tells whether the Global timer supports the 25

View File

@ -0,0 +1,21 @@
Coherency fabric
----------------
Available on Marvell SOCs: Armada 370 and Armada XP
Required properties:
- compatible: "marvell,coherency-fabric"
- reg: Should contain coherency fabric registers location and
length. First pair for the coherency fabric registers, second pair
for the per-CPU fabric registers registers.
Example:
coherency-fabric@d0020200 {
compatible = "marvell,coherency-fabric";
reg = <0xd0020200 0xb0>,
<0xd0021810 0x1c>;
};

View File

@ -10,6 +10,12 @@ Required properties:
"arm,pl310-cache"
"arm,l220-cache"
"arm,l210-cache"
"marvell,aurora-system-cache": Marvell Controller designed to be
compatible with the ARM one, with system cache mode (meaning
maintenance operations on L1 are broadcasted to the L2 and L2
performs the same operation).
"marvell,"aurora-outer-cache: Marvell Controller designed to be
compatible with the ARM one with outer cache mode.
- cache-unified : Specifies the cache is a unified cache.
- cache-level : Should be set to 2 for a level 2 cache.
- reg : Physical base address and size of cache controller's memory mapped
@ -29,6 +35,9 @@ Optional properties:
filter. Addresses in the filter window are directed to the M1 port. Other
addresses will go to the M0 port.
- interrupts : 1 combined interrupt.
- cache-id-part: cache id part number to be used if it is not present
on hardware
- wt-override: If present then L2 is forced to Write through mode
Example:

View File

@ -0,0 +1,47 @@
* Core Clock bindings for Marvell MVEBU SoCs
Marvell MVEBU SoCs usually allow to determine core clock frequencies by
reading the Sample-At-Reset (SAR) register. The core clock consumer should
specify the desired clock by having the clock ID in its "clocks" phandle cell.
The following is a list of provided IDs and clock names on Armada 370/XP:
0 = tclk (Internal Bus clock)
1 = cpuclk (CPU clock)
2 = nbclk (L2 Cache clock)
3 = hclk (DRAM control clock)
4 = dramclk (DDR clock)
The following is a list of provided IDs and clock names on Kirkwood and Dove:
0 = tclk (Internal Bus clock)
1 = cpuclk (CPU0 clock)
2 = l2clk (L2 Cache clock derived from CPU0 clock)
3 = ddrclk (DDR controller clock derived from CPU0 clock)
Required properties:
- compatible : shall be one of the following:
"marvell,armada-370-core-clock" - For Armada 370 SoC core clocks
"marvell,armada-xp-core-clock" - For Armada XP SoC core clocks
"marvell,dove-core-clock" - for Dove SoC core clocks
"marvell,kirkwood-core-clock" - for Kirkwood SoC (except mv88f6180)
"marvell,mv88f6180-core-clock" - for Kirkwood MV88f6180 SoC
- reg : shall be the register address of the Sample-At-Reset (SAR) register
- #clock-cells : from common clock binding; shall be set to 1
Optional properties:
- clock-output-names : from common clock binding; allows overwrite default clock
output names ("tclk", "cpuclk", "l2clk", "ddrclk")
Example:
core_clk: core-clocks@d0214 {
compatible = "marvell,dove-core-clock";
reg = <0xd0214 0x4>;
#clock-cells = <1>;
};
spi0: spi@10600 {
compatible = "marvell,orion-spi";
/* ... */
/* get tclk from core clock provider */
clocks = <&core_clk 0>;
};

View File

@ -0,0 +1,21 @@
Device Tree Clock bindings for cpu clock of Marvell EBU platforms
Required properties:
- compatible : shall be one of the following:
"marvell,armada-xp-cpu-clock" - cpu clocks for Armada XP
- reg : Address and length of the clock complex register set
- #clock-cells : should be set to 1.
- clocks : shall be the input parent clock phandle for the clock.
cpuclk: clock-complex@d0018700 {
#clock-cells = <1>;
compatible = "marvell,armada-xp-cpu-clock";
reg = <0xd0018700 0xA0>;
clocks = <&coreclk 1>;
}
cpu@0 {
compatible = "marvell,sheeva-v7";
reg = <0>;
clocks = <&cpuclk 0>;
};

View File

@ -0,0 +1,119 @@
* Gated Clock bindings for Marvell Orion SoCs
Marvell Dove and Kirkwood allow some peripheral clocks to be gated to save
some power. The clock consumer should specify the desired clock by having
the clock ID in its "clocks" phandle cell. The clock ID is directly mapped to
the corresponding clock gating control bit in HW to ease manual clock lookup
in datasheet.
The following is a list of provided IDs for Armada 370:
ID Clock Peripheral
-----------------------------------
0 Audio AC97 Cntrl
1 pex0_en PCIe 0 Clock out
2 pex1_en PCIe 1 Clock out
3 ge1 Gigabit Ethernet 1
4 ge0 Gigabit Ethernet 0
5 pex0 PCIe Cntrl 0
9 pex1 PCIe Cntrl 1
15 sata0 SATA Host 0
17 sdio SDHCI Host
25 tdm Time Division Mplx
28 ddr DDR Cntrl
30 sata1 SATA Host 0
The following is a list of provided IDs for Armada XP:
ID Clock Peripheral
-----------------------------------
0 audio Audio Cntrl
1 ge3 Gigabit Ethernet 3
2 ge2 Gigabit Ethernet 2
3 ge1 Gigabit Ethernet 1
4 ge0 Gigabit Ethernet 0
5 pex0 PCIe Cntrl 0
6 pex1 PCIe Cntrl 1
7 pex2 PCIe Cntrl 2
8 pex3 PCIe Cntrl 3
13 bp
14 sata0lnk
15 sata0 SATA Host 0
16 lcd LCD Cntrl
17 sdio SDHCI Host
18 usb0 USB Host 0
19 usb1 USB Host 1
20 usb2 USB Host 2
22 xor0 XOR DMA 0
23 crypto CESA engine
25 tdm Time Division Mplx
28 xor1 XOR DMA 1
29 sata1lnk
30 sata1 SATA Host 0
The following is a list of provided IDs for Dove:
ID Clock Peripheral
-----------------------------------
0 usb0 USB Host 0
1 usb1 USB Host 1
2 ge Gigabit Ethernet
3 sata SATA Host
4 pex0 PCIe Cntrl 0
5 pex1 PCIe Cntrl 1
8 sdio0 SDHCI Host 0
9 sdio1 SDHCI Host 1
10 nand NAND Cntrl
11 camera Camera Cntrl
12 i2s0 I2S Cntrl 0
13 i2s1 I2S Cntrl 1
15 crypto CESA engine
21 ac97 AC97 Cntrl
22 pdma Peripheral DMA
23 xor0 XOR DMA 0
24 xor1 XOR DMA 1
30 gephy Gigabit Ethernel PHY
Note: gephy(30) is implemented as a parent clock of ge(2)
The following is a list of provided IDs for Kirkwood:
ID Clock Peripheral
-----------------------------------
0 ge0 Gigabit Ethernet 0
2 pex0 PCIe Cntrl 0
3 usb0 USB Host 0
4 sdio SDIO Cntrl
5 tsu Transp. Stream Unit
6 dunit SDRAM Cntrl
7 runit Runit
8 xor0 XOR DMA 0
9 audio I2S Cntrl 0
14 sata0 SATA Host 0
15 sata1 SATA Host 1
16 xor1 XOR DMA 1
17 crypto CESA engine
18 pex1 PCIe Cntrl 1
19 ge1 Gigabit Ethernet 0
20 tdm Time Division Mplx
Required properties:
- compatible : shall be one of the following:
"marvell,dove-gating-clock" - for Dove SoC clock gating
"marvell,kirkwood-gating-clock" - for Kirkwood SoC clock gating
- reg : shall be the register address of the Clock Gating Control register
- #clock-cells : from common clock binding; shall be set to 1
Optional properties:
- clocks : default parent clock phandle (e.g. tclk)
Example:
gate_clk: clock-gating-control@d0038 {
compatible = "marvell,dove-gating-clock";
reg = <0xd0038 0x4>;
/* default parent clock is tclk */
clocks = <&core_clk 0>;
#clock-cells = <1>;
};
sdio0: sdio@92000 {
compatible = "marvell,dove-sdhci";
/* get clk gate bit 8 (sdio0) */
clocks = <&gate_clk 8>;
};

View File

@ -0,0 +1,40 @@
* Marvell XOR engines
Required properties:
- compatible: Should be "marvell,orion-xor"
- reg: Should contain registers location and length (two sets)
the first set is the low registers, the second set the high
registers for the XOR engine.
- clocks: pointer to the reference clock
The DT node must also contains sub-nodes for each XOR channel that the
XOR engine has. Those sub-nodes have the following required
properties:
- interrupts: interrupt of the XOR channel
And the following optional properties:
- dmacap,memcpy to indicate that the XOR channel is capable of memcpy operations
- dmacap,memset to indicate that the XOR channel is capable of memset operations
- dmacap,xor to indicate that the XOR channel is capable of xor operations
Example:
xor@d0060900 {
compatible = "marvell,orion-xor";
reg = <0xd0060900 0x100
0xd0060b00 0x100>;
clocks = <&coreclk 0>;
status = "okay";
xor00 {
interrupts = <51>;
dmacap,memcpy;
dmacap,xor;
};
xor01 {
interrupts = <52>;
dmacap,memcpy;
dmacap,xor;
dmacap,memset;
};
};

View File

@ -0,0 +1,23 @@
* Marvell Armada 370 / Armada XP Ethernet Controller (NETA)
Required properties:
- compatible: should be "marvell,armada-370-neta".
- reg: address and length of the register set for the device.
- interrupts: interrupt for the device
- phy: A phandle to a phy node defining the PHY address (as the reg
property, a single integer).
- phy-mode: The interface between the SoC and the PHY (a string that
of_get_phy_mode() can understand)
- clocks: a pointer to the reference clock for this device.
Example:
ethernet@d0070000 {
compatible = "marvell,armada-370-neta";
reg = <0xd0070000 0x2500>;
interrupts = <8>;
clocks = <&gate_clk 4>;
status = "okay";
phy = <&phy0>;
phy-mode = "rgmii-id";
};

View File

@ -0,0 +1,35 @@
* Marvell MDIO Ethernet Controller interface
The Ethernet controllers of the Marvel Kirkwood, Dove, Orion5x,
MV78xx0, Armada 370 and Armada XP have an identical unit that provides
an interface with the MDIO bus. This driver handles this MDIO
interface.
Required properties:
- compatible: "marvell,orion-mdio"
- reg: address and length of the SMI register
The child nodes of the MDIO driver are the individual PHY devices
connected to this MDIO bus. They must have a "reg" property given the
PHY address on the MDIO bus.
Example at the SoC level:
mdio {
#address-cells = <1>;
#size-cells = <0>;
compatible = "marvell,orion-mdio";
reg = <0xd0072004 0x4>;
};
And at the board level:
mdio {
phy0: ethernet-phy@0 {
reg = <0>;
};
phy1: ethernet-phy@1 {
reg = <1>;
};
}

View File

@ -4871,6 +4871,12 @@ S: Maintained
F: drivers/net/ethernet/marvell/mv643xx_eth.*
F: include/linux/mv643xx.h
MARVELL MVNETA ETHERNET DRIVER
M: Thomas Petazzoni <thomas.petazzoni@free-electrons.com>
L: netdev@vger.kernel.org
S: Maintained
F: drivers/net/ethernet/marvell/mvneta.*
MARVELL MWIFIEX WIRELESS DRIVER
M: Bing Zhao <bzhao@marvell.com>
L: linux-wireless@vger.kernel.org

View File

@ -533,6 +533,7 @@ config ARCH_IXP4XX
config ARCH_DOVE
bool "Marvell Dove"
select ARCH_REQUIRE_GPIOLIB
select COMMON_CLK_DOVE
select CPU_V7
select GENERIC_CLOCKEVENTS
select MIGHT_HAVE_PCI

View File

@ -77,7 +77,9 @@ dtb-$(CONFIG_ARCH_KIRKWOOD) += kirkwood-dns320.dtb \
dtb-$(CONFIG_ARCH_MSM) += msm8660-surf.dtb \
msm8960-cdp.dtb
dtb-$(CONFIG_ARCH_MVEBU) += armada-370-db.dtb \
armada-xp-db.dtb
armada-370-mirabox.dtb \
armada-xp-db.dtb \
armada-xp-openblocks-ax3-4.dtb
dtb-$(CONFIG_ARCH_MXC) += imx51-babbage.dtb \
imx53-ard.dtb \
imx53-evk.dtb \

View File

@ -34,9 +34,30 @@
clock-frequency = <200000000>;
status = "okay";
};
timer@d0020300 {
clock-frequency = <600000000>;
sata@d00a0000 {
nr-ports = <2>;
status = "okay";
};
mdio {
phy0: ethernet-phy@0 {
reg = <0>;
};
phy1: ethernet-phy@1 {
reg = <1>;
};
};
ethernet@d0070000 {
status = "okay";
phy = <&phy0>;
phy-mode = "rgmii-id";
};
ethernet@d0074000 {
status = "okay";
phy = <&phy1>;
phy-mode = "rgmii-id";
};
};
};

View File

@ -0,0 +1,56 @@
/*
* Device Tree file for Globalscale Mirabox
*
* Gregory CLEMENT <gregory.clement@free-electrons.com>
*
* This file is licensed under the terms of the GNU General Public
* License version 2. This program is licensed "as is" without any
* warranty of any kind, whether express or implied.
*/
/dts-v1/;
/include/ "armada-370.dtsi"
/ {
model = "Globalscale Mirabox";
compatible = "globalscale,mirabox", "marvell,armada370", "marvell,armada-370-xp";
chosen {
bootargs = "console=ttyS0,115200 earlyprintk";
};
memory {
device_type = "memory";
reg = <0x00000000 0x20000000>; /* 512 MB */
};
soc {
serial@d0012000 {
clock-frequency = <200000000>;
status = "okay";
};
timer@d0020300 {
clock-frequency = <600000000>;
status = "okay";
};
mdio {
phy0: ethernet-phy@0 {
reg = <0>;
};
phy1: ethernet-phy@1 {
reg = <1>;
};
};
ethernet@d0070000 {
status = "okay";
phy = <&phy0>;
phy-mode = "rgmii-id";
};
ethernet@d0074000 {
status = "okay";
phy = <&phy1>;
phy-mode = "rgmii-id";
};
};
};

View File

@ -20,7 +20,7 @@
/ {
model = "Marvell Armada 370 and XP SoC";
compatible = "marvell,armada_370_xp";
compatible = "marvell,armada-370-xp";
cpus {
cpu@0 {
@ -36,6 +36,12 @@
interrupt-controller;
};
coherency-fabric@d0020200 {
compatible = "marvell,coherency-fabric";
reg = <0xd0020200 0xb0>,
<0xd0021810 0x1c>;
};
soc {
#address-cells = <1>;
#size-cells = <1>;
@ -62,12 +68,67 @@
compatible = "marvell,armada-370-xp-timer";
reg = <0xd0020300 0x30>;
interrupts = <37>, <38>, <39>, <40>;
clocks = <&coreclk 2>;
};
addr-decoding@d0020000 {
compatible = "marvell,armada-addr-decoding-controller";
reg = <0xd0020000 0x258>;
};
sata@d00a0000 {
compatible = "marvell,orion-sata";
reg = <0xd00a0000 0x2400>;
interrupts = <55>;
clocks = <&gateclk 15>, <&gateclk 30>;
clock-names = "0", "1";
status = "disabled";
};
mdio {
#address-cells = <1>;
#size-cells = <0>;
compatible = "marvell,orion-mdio";
reg = <0xd0072004 0x4>;
};
ethernet@d0070000 {
compatible = "marvell,armada-370-neta";
reg = <0xd0070000 0x2500>;
interrupts = <8>;
clocks = <&gateclk 4>;
status = "disabled";
};
ethernet@d0074000 {
compatible = "marvell,armada-370-neta";
reg = <0xd0074000 0x2500>;
interrupts = <10>;
clocks = <&gateclk 3>;
status = "disabled";
};
i2c0: i2c@d0011000 {
compatible = "marvell,mv64xxx-i2c";
reg = <0xd0011000 0x20>;
#address-cells = <1>;
#size-cells = <0>;
interrupts = <31>;
timeout-ms = <1000>;
clocks = <&coreclk 0>;
status = "disabled";
};
i2c1: i2c@d0011100 {
compatible = "marvell,mv64xxx-i2c";
reg = <0xd0011100 0x20>;
#address-cells = <1>;
#size-cells = <0>;
interrupts = <32>;
timeout-ms = <1000>;
clocks = <&coreclk 0>;
status = "disabled";
};
};
};

View File

@ -20,6 +20,12 @@
/ {
model = "Marvell Armada 370 family SoC";
compatible = "marvell,armada370", "marvell,armada-370-xp";
L2: l2-cache {
compatible = "marvell,aurora-outer-cache";
reg = <0xd0008000 0x1000>;
cache-id-part = <0x100>;
wt-override;
};
aliases {
gpio0 = &gpio0;
@ -75,5 +81,56 @@
#interrupts-cells = <2>;
interrupts = <91>;
};
coreclk: mvebu-sar@d0018230 {
compatible = "marvell,armada-370-core-clock";
reg = <0xd0018230 0x08>;
#clock-cells = <1>;
};
gateclk: clock-gating-control@d0018220 {
compatible = "marvell,armada-370-gating-clock";
reg = <0xd0018220 0x4>;
clocks = <&coreclk 0>;
#clock-cells = <1>;
};
xor@d0060800 {
compatible = "marvell,orion-xor";
reg = <0xd0060800 0x100
0xd0060A00 0x100>;
status = "okay";
xor00 {
interrupts = <51>;
dmacap,memcpy;
dmacap,xor;
};
xor01 {
interrupts = <52>;
dmacap,memcpy;
dmacap,xor;
dmacap,memset;
};
};
xor@d0060900 {
compatible = "marvell,orion-xor";
reg = <0xd0060900 0x100
0xd0060b00 0x100>;
status = "okay";
xor10 {
interrupts = <94>;
dmacap,memcpy;
dmacap,xor;
};
xor11 {
interrupts = <95>;
dmacap,memcpy;
dmacap,xor;
dmacap,memset;
};
};
};
};

View File

@ -46,5 +46,49 @@
clock-frequency = <250000000>;
status = "okay";
};
sata@d00a0000 {
nr-ports = <2>;
status = "okay";
};
mdio {
phy0: ethernet-phy@0 {
reg = <0>;
};
phy1: ethernet-phy@1 {
reg = <1>;
};
phy2: ethernet-phy@2 {
reg = <25>;
};
phy3: ethernet-phy@3 {
reg = <27>;
};
};
ethernet@d0070000 {
status = "okay";
phy = <&phy0>;
phy-mode = "rgmii-id";
};
ethernet@d0074000 {
status = "okay";
phy = <&phy1>;
phy-mode = "rgmii-id";
};
ethernet@d0030000 {
status = "okay";
phy = <&phy2>;
phy-mode = "sgmii";
};
ethernet@d0034000 {
status = "okay";
phy = <&phy3>;
phy-mode = "sgmii";
};
};
};

View File

@ -24,6 +24,18 @@
gpio1 = &gpio1;
};
cpus {
#address-cells = <1>;
#size-cells = <0>;
cpu@0 {
device_type = "cpu";
compatible = "marvell,sheeva-v7";
reg = <0>;
clocks = <&cpuclk 0>;
};
}
soc {
pinctrl {
compatible = "marvell,mv78230-pinctrl";

View File

@ -25,6 +25,25 @@
gpio2 = &gpio2;
};
cpus {
#address-cells = <1>;
#size-cells = <0>;
cpu@0 {
device_type = "cpu";
compatible = "marvell,sheeva-v7";
reg = <0>;
clocks = <&cpuclk 0>;
};
cpu@1 {
device_type = "cpu";
compatible = "marvell,sheeva-v7";
reg = <1>;
clocks = <&cpuclk 1>;
};
};
soc {
pinctrl {
compatible = "marvell,mv78260-pinctrl";

View File

@ -25,6 +25,40 @@
gpio2 = &gpio2;
};
cpus {
#address-cells = <1>;
#size-cells = <0>;
cpu@0 {
device_type = "cpu";
compatible = "marvell,sheeva-v7";
reg = <0>;
clocks = <&cpuclk 0>;
};
cpu@1 {
device_type = "cpu";
compatible = "marvell,sheeva-v7";
reg = <1>;
clocks = <&cpuclk 1>;
};
cpu@2 {
device_type = "cpu";
compatible = "marvell,sheeva-v7";
reg = <2>;
clocks = <&cpuclk 2>;
};
cpu@3 {
device_type = "cpu";
compatible = "marvell,sheeva-v7";
reg = <3>;
clocks = <&cpuclk 3>;
};
};
soc {
pinctrl {
compatible = "marvell,mv78460-pinctrl";

View File

@ -0,0 +1,125 @@
/*
* Device Tree file for OpenBlocks AX3-4 board
*
* Copyright (C) 2012 Marvell
*
* Thomas Petazzoni <thomas.petazzoni@free-electrons.com>
*
* This file is licensed under the terms of the GNU General Public
* License version 2. This program is licensed "as is" without any
* warranty of any kind, whether express or implied.
*/
/dts-v1/;
/include/ "armada-xp-mv78260.dtsi"
/ {
model = "PlatHome OpenBlocks AX3-4 board";
compatible = "plathome,openblocks-ax3-4", "marvell,armadaxp-mv78260", "marvell,armadaxp", "marvell,armada-370-xp";
chosen {
bootargs = "console=ttyS0,115200 earlyprintk";
};
memory {
device_type = "memory";
reg = <0x00000000 0xC0000000>; /* 3 GB */
};
soc {
serial@d0012000 {
clock-frequency = <250000000>;
status = "okay";
};
serial@d0012100 {
clock-frequency = <250000000>;
status = "okay";
};
pinctrl {
led_pins: led-pins-0 {
marvell,pins = "mpp49", "mpp51", "mpp53";
marvell,function = "gpio";
};
};
leds {
compatible = "gpio-leds";
pinctrl-names = "default";
pinctrl-0 = <&led_pins>;
red_led {
label = "red_led";
gpios = <&gpio1 17 1>;
default-state = "off";
};
yellow_led {
label = "yellow_led";
gpios = <&gpio1 19 1>;
default-state = "off";
};
green_led {
label = "green_led";
gpios = <&gpio1 21 1>;
default-state = "off";
linux,default-trigger = "heartbeat";
};
};
mdio {
phy0: ethernet-phy@0 {
reg = <0>;
};
phy1: ethernet-phy@1 {
reg = <1>;
};
phy2: ethernet-phy@2 {
reg = <2>;
};
phy3: ethernet-phy@3 {
reg = <3>;
};
};
ethernet@d0070000 {
status = "okay";
phy = <&phy0>;
phy-mode = "sgmii";
};
ethernet@d0074000 {
status = "okay";
phy = <&phy1>;
phy-mode = "sgmii";
};
ethernet@d0030000 {
status = "okay";
phy = <&phy2>;
phy-mode = "sgmii";
};
ethernet@d0034000 {
status = "okay";
phy = <&phy3>;
phy-mode = "sgmii";
};
i2c@d0011000 {
status = "okay";
clock-frequency = <400000>;
};
i2c@d0011100 {
status = "okay";
clock-frequency = <400000>;
s35390a: s35390a@30 {
compatible = "s35390a";
reg = <0x30>;
};
};
sata@d00a0000 {
nr-ports = <2>;
status = "okay";
};
};
};

View File

@ -22,9 +22,22 @@
model = "Marvell Armada XP family SoC";
compatible = "marvell,armadaxp", "marvell,armada-370-xp";
L2: l2-cache {
compatible = "marvell,aurora-system-cache";
reg = <0xd0008000 0x1000>;
cache-id-part = <0x100>;
wt-override;
};
mpic: interrupt-controller@d0020000 {
reg = <0xd0020a00 0x1d0>,
<0xd0021870 0x58>;
<0xd0021070 0x58>;
};
armada-370-xp-pmsu@d0022000 {
compatible = "marvell,armada-370-xp-pmsu";
reg = <0xd0022100 0x430>,
<0xd0020800 0x20>;
};
soc {
@ -47,9 +60,85 @@
marvell,timer-25Mhz;
};
coreclk: mvebu-sar@d0018230 {
compatible = "marvell,armada-xp-core-clock";
reg = <0xd0018230 0x08>;
#clock-cells = <1>;
};
cpuclk: clock-complex@d0018700 {
#clock-cells = <1>;
compatible = "marvell,armada-xp-cpu-clock";
reg = <0xd0018700 0xA0>;
clocks = <&coreclk 1>;
};
gateclk: clock-gating-control@d0018220 {
compatible = "marvell,armada-xp-gating-clock";
reg = <0xd0018220 0x4>;
clocks = <&coreclk 0>;
#clock-cells = <1>;
};
system-controller@d0018200 {
compatible = "marvell,armada-370-xp-system-controller";
reg = <0xd0018200 0x500>;
};
ethernet@d0030000 {
compatible = "marvell,armada-370-neta";
reg = <0xd0030000 0x2500>;
interrupts = <12>;
clocks = <&gateclk 2>;
status = "disabled";
};
ethernet@d0034000 {
compatible = "marvell,armada-370-neta";
reg = <0xd0034000 0x2500>;
interrupts = <14>;
clocks = <&gateclk 1>;
status = "disabled";
};
xor@d0060900 {
compatible = "marvell,orion-xor";
reg = <0xd0060900 0x100
0xd0060b00 0x100>;
clocks = <&gateclk 22>;
status = "okay";
xor10 {
interrupts = <51>;
dmacap,memcpy;
dmacap,xor;
};
xor11 {
interrupts = <52>;
dmacap,memcpy;
dmacap,xor;
dmacap,memset;
};
};
xor@d00f0900 {
compatible = "marvell,orion-xor";
reg = <0xd00F0900 0x100
0xd00F0B00 0x100>;
clocks = <&gateclk 28>;
status = "okay";
xor00 {
interrupts = <94>;
dmacap,memcpy;
dmacap,xor;
};
xor01 {
interrupts = <95>;
dmacap,memcpy;
dmacap,xor;
dmacap,memset;
};
};
};
};

View File

@ -37,6 +37,19 @@
reg = <0x20204 0x04>, <0x20214 0x04>;
};
core_clk: core-clocks@d0214 {
compatible = "marvell,dove-core-clock";
reg = <0xd0214 0x4>;
#clock-cells = <1>;
};
gate_clk: clock-gating-control@d0038 {
compatible = "marvell,dove-gating-clock";
reg = <0xd0038 0x4>;
clocks = <&core_clk 0>;
#clock-cells = <1>;
};
uart0: serial@12000 {
compatible = "ns16550a";
reg = <0x12000 0x100>;
@ -113,6 +126,7 @@
cell-index = <0>;
interrupts = <6>;
reg = <0x10600 0x28>;
clocks = <&core_clk 0>;
status = "disabled";
};
@ -123,6 +137,7 @@
cell-index = <1>;
interrupts = <5>;
reg = <0x14600 0x28>;
clocks = <&core_clk 0>;
status = "disabled";
};
@ -134,6 +149,7 @@
interrupts = <11>;
clock-frequency = <400000>;
timeout-ms = <1000>;
clocks = <&core_clk 0>;
status = "disabled";
};
@ -141,6 +157,7 @@
compatible = "marvell,dove-sdhci";
reg = <0x92000 0x100>;
interrupts = <35>, <37>;
clocks = <&gate_clk 8>;
status = "disabled";
};
@ -148,6 +165,7 @@
compatible = "marvell,dove-sdhci";
reg = <0x90000 0x100>;
interrupts = <36>, <38>;
clocks = <&gate_clk 9>;
status = "disabled";
};
@ -155,6 +173,7 @@
compatible = "marvell,orion-sata";
reg = <0xa0000 0x2400>;
interrupts = <62>;
clocks = <&gate_clk 3>;
nr-ports = <1>;
status = "disabled";
};
@ -165,7 +184,50 @@
<0xc8000000 0x800>;
reg-names = "regs", "sram";
interrupts = <31>;
clocks = <&gate_clk 15>;
status = "okay";
};
xor0: dma-engine@60800 {
compatible = "marvell,orion-xor";
reg = <0x60800 0x100
0x60a00 0x100>;
clocks = <&gate_clk 23>;
status = "okay";
channel0 {
interrupts = <39>;
dmacap,memcpy;
dmacap,xor;
};
channel1 {
interrupts = <40>;
dmacap,memset;
dmacap,memcpy;
dmacap,xor;
};
};
xor1: dma-engine@60900 {
compatible = "marvell,orion-xor";
reg = <0x60900 0x100
0x60b00 0x100>;
clocks = <&gate_clk 24>;
status = "okay";
channel0 {
interrupts = <42>;
dmacap,memcpy;
dmacap,xor;
};
channel1 {
interrupts = <43>;
dmacap,memset;
dmacap,memcpy;
dmacap,xor;
};
};
};
};

View File

@ -23,6 +23,12 @@
#address-cells = <1>;
#size-cells = <1>;
core_clk: core-clocks@10030 {
compatible = "marvell,kirkwood-core-clock";
reg = <0x10030 0x4>;
#clock-cells = <1>;
};
gpio0: gpio@10100 {
compatible = "marvell,orion-gpio";
#gpio-cells = <2>;
@ -48,6 +54,7 @@
reg = <0x12000 0x100>;
reg-shift = <2>;
interrupts = <33>;
clocks = <&gate_clk 7>;
/* set clock-frequency in board dts */
status = "disabled";
};
@ -57,6 +64,7 @@
reg = <0x12100 0x100>;
reg-shift = <2>;
interrupts = <34>;
clocks = <&gate_clk 7>;
/* set clock-frequency in board dts */
status = "disabled";
};
@ -74,15 +82,64 @@
cell-index = <0>;
interrupts = <23>;
reg = <0x10600 0x28>;
clocks = <&gate_clk 7>;
status = "disabled";
};
gate_clk: clock-gating-control@2011c {
compatible = "marvell,kirkwood-gating-clock";
reg = <0x2011c 0x4>;
clocks = <&core_clk 0>;
#clock-cells = <1>;
};
wdt@20300 {
compatible = "marvell,orion-wdt";
reg = <0x20300 0x28>;
clocks = <&gate_clk 7>;
status = "okay";
};
xor@60800 {
compatible = "marvell,orion-xor";
reg = <0x60800 0x100
0x60A00 0x100>;
status = "okay";
clocks = <&gate_clk 8>;
xor00 {
interrupts = <5>;
dmacap,memcpy;
dmacap,xor;
};
xor01 {
interrupts = <6>;
dmacap,memcpy;
dmacap,xor;
dmacap,memset;
};
};
xor@60900 {
compatible = "marvell,orion-xor";
reg = <0x60900 0x100
0xd0B00 0x100>;
status = "okay";
clocks = <&gate_clk 16>;
xor00 {
interrupts = <7>;
dmacap,memcpy;
dmacap,xor;
};
xor01 {
interrupts = <8>;
dmacap,memcpy;
dmacap,xor;
dmacap,memset;
};
};
ehci@50000 {
compatible = "marvell,orion-ehci";
reg = <0x50000 0x1000>;
@ -94,6 +151,8 @@
compatible = "marvell,orion-sata";
reg = <0x80000 0x5000>;
interrupts = <21>;
clocks = <&gate_clk 14>, <&gate_clk 15>;
clock-names = "0", "1";
status = "disabled";
};
@ -107,6 +166,7 @@
reg = <0x3000000 0x400>;
chip-delay = <25>;
/* set partition map and/or chip-delay in board dts */
clocks = <&gate_clk 7>;
status = "disabled";
};
@ -117,6 +177,7 @@
#size-cells = <0>;
interrupts = <29>;
clock-frequency = <100000>;
clocks = <&gate_clk 7>;
status = "disabled";
};
@ -126,6 +187,7 @@
<0xf5000000 0x800>;
reg-names = "regs", "sram";
interrupts = <22>;
clocks = <&gate_clk 17>;
status = "okay";
};
};

View File

@ -17,8 +17,10 @@ CONFIG_ARM_APPENDED_DTB=y
CONFIG_VFP=y
CONFIG_NEON=y
CONFIG_NET=y
CONFIG_BLK_DEV_SD=y
CONFIG_ATA=y
CONFIG_SATA_HIGHBANK=y
CONFIG_SATA_MV=y
CONFIG_NETDEVICES=y
CONFIG_NET_CALXEDA_XGMAC=y
CONFIG_SMSC911X=y

View File

@ -12,6 +12,9 @@ CONFIG_ARCH_MVEBU=y
CONFIG_MACH_ARMADA_370=y
CONFIG_MACH_ARMADA_XP=y
# CONFIG_CACHE_L2X0 is not set
# CONFIG_SWP_EMULATE is not set
CONFIG_SMP=y
# CONFIG_LOCAL_TIMERS is not set
CONFIG_AEABI=y
CONFIG_HIGHMEM=y
# CONFIG_COMPACTION is not set
@ -19,13 +22,27 @@ CONFIG_ZBOOT_ROM_TEXT=0x0
CONFIG_ZBOOT_ROM_BSS=0x0
CONFIG_ARM_APPENDED_DTB=y
CONFIG_VFP=y
CONFIG_NET=y
CONFIG_INET=y
CONFIG_UEVENT_HELPER_PATH="/sbin/hotplug"
CONFIG_BLK_DEV_SD=y
CONFIG_ATA=y
CONFIG_SATA_MV=y
CONFIG_NETDEVICES=y
CONFIG_MVNETA=y
CONFIG_MARVELL_PHY=y
CONFIG_SERIAL_8250=y
CONFIG_SERIAL_8250_CONSOLE=y
CONFIG_SERIAL_OF_PLATFORM=y
CONFIG_I2C=y
CONFIG_I2C_MV64XXX=y
CONFIG_GPIOLIB=y
CONFIG_GPIO_SYSFS=y
# CONFIG_USB_SUPPORT is not set
CONFIG_RTC_CLASS=y
CONFIG_RTC_DRV_S35390A=y
CONFIG_DMADEVICES=y
CONFIG_MV_XOR=y
# CONFIG_IOMMU_SUPPORT is not set
CONFIG_EXT2_FS=y
CONFIG_EXT3_FS=y

View File

@ -111,6 +111,8 @@ static inline void dma_free_noncoherent(struct device *dev, size_t size,
extern int dma_supported(struct device *dev, u64 mask);
extern int arm_dma_set_mask(struct device *dev, u64 dma_mask);
/**
* arm_dma_alloc - allocate consistent memory for DMA
* @dev: valid struct device pointer, or NULL for ISA and EISA-like devices

View File

@ -17,6 +17,8 @@ config MACH_CM_A510
config MACH_DOVE_DT
bool "Marvell Dove Flattened Device Tree"
select MVEBU_CLK_CORE
select MVEBU_CLK_GATING
select USE_OF
help
Say 'Y' here if you want your kernel to support the

View File

@ -14,6 +14,7 @@
#include <linux/platform_device.h>
#include <linux/pci.h>
#include <linux/clk-provider.h>
#include <linux/clk/mvebu.h>
#include <linux/ata_platform.h>
#include <linux/gpio.h>
#include <linux/of.h>
@ -32,6 +33,7 @@
#include <linux/irq.h>
#include <plat/time.h>
#include <linux/platform_data/usb-ehci-orion.h>
#include <linux/platform_data/dma-mv_xor.h>
#include <plat/irq.h>
#include <plat/common.h>
#include <plat/addr-map.h>
@ -123,8 +125,8 @@ static void __init dove_clk_init(void)
orion_clkdev_add(NULL, "mv_crypto", crypto);
orion_clkdev_add(NULL, "dove-ac97", ac97);
orion_clkdev_add(NULL, "dove-pdma", pdma);
orion_clkdev_add(NULL, "mv_xor_shared.0", xor0);
orion_clkdev_add(NULL, "mv_xor_shared.1", xor1);
orion_clkdev_add(NULL, MV_XOR_NAME ".0", xor0);
orion_clkdev_add(NULL, MV_XOR_NAME ".1", xor1);
}
/*****************************************************************************
@ -376,19 +378,44 @@ void dove_restart(char mode, const char *cmd)
#if defined(CONFIG_MACH_DOVE_DT)
/*
* Auxdata required until real OF clock provider
* There are still devices that doesn't even know about DT,
* get clock gates here and add a clock lookup.
*/
struct of_dev_auxdata dove_auxdata_lookup[] __initdata = {
OF_DEV_AUXDATA("marvell,orion-spi", 0xf1010600, "orion_spi.0", NULL),
OF_DEV_AUXDATA("marvell,orion-spi", 0xf1014600, "orion_spi.1", NULL),
OF_DEV_AUXDATA("marvell,orion-wdt", 0xf1020300, "orion_wdt", NULL),
OF_DEV_AUXDATA("marvell,mv64xxx-i2c", 0xf1011000, "mv64xxx_i2c.0",
NULL),
OF_DEV_AUXDATA("marvell,orion-sata", 0xf10a0000, "sata_mv.0", NULL),
OF_DEV_AUXDATA("marvell,dove-sdhci", 0xf1092000, "sdhci-dove.0", NULL),
OF_DEV_AUXDATA("marvell,dove-sdhci", 0xf1090000, "sdhci-dove.1", NULL),
{},
};
static void __init dove_legacy_clk_init(void)
{
struct device_node *np = of_find_compatible_node(NULL, NULL,
"marvell,dove-gating-clock");
struct of_phandle_args clkspec;
clkspec.np = np;
clkspec.args_count = 1;
clkspec.args[0] = CLOCK_GATING_BIT_USB0;
orion_clkdev_add(NULL, "orion-ehci.0",
of_clk_get_from_provider(&clkspec));
clkspec.args[0] = CLOCK_GATING_BIT_USB1;
orion_clkdev_add(NULL, "orion-ehci.1",
of_clk_get_from_provider(&clkspec));
clkspec.args[0] = CLOCK_GATING_BIT_GBE;
orion_clkdev_add(NULL, "mv643xx_eth_port.0",
of_clk_get_from_provider(&clkspec));
clkspec.args[0] = CLOCK_GATING_BIT_PCIE0;
orion_clkdev_add("0", "pcie",
of_clk_get_from_provider(&clkspec));
clkspec.args[0] = CLOCK_GATING_BIT_PCIE1;
orion_clkdev_add("1", "pcie",
of_clk_get_from_provider(&clkspec));
}
static void __init dove_of_clk_init(void)
{
mvebu_clocks_init();
dove_legacy_clk_init();
}
static struct mv643xx_eth_platform_data dove_dt_ge00_data = {
.phy_addr = MV643XX_ETH_PHY_ADDR_DEFAULT,
@ -405,20 +432,17 @@ static void __init dove_dt_init(void)
dove_setup_cpu_mbus();
/* Setup root of clk tree */
dove_clk_init();
dove_of_clk_init();
/* Internal devices not ported to DT yet */
dove_rtc_init();
dove_xor0_init();
dove_xor1_init();
dove_ge00_init(&dove_dt_ge00_data);
dove_ehci0_init();
dove_ehci1_init();
dove_pcie_init(1, 1);
of_platform_populate(NULL, of_default_bus_match_table,
dove_auxdata_lookup, NULL);
of_platform_populate(NULL, of_default_bus_match_table, NULL, NULL);
}
static const char * const dove_dt_board_compat[] = {

View File

@ -51,6 +51,8 @@ config ARCH_KIRKWOOD_DT
select POWER_RESET_GPIO
select REGULATOR
select REGULATOR_FIXED_VOLTAGE
select MVEBU_CLK_CORE
select MVEBU_CLK_GATING
select USE_OF
help
Say 'Y' here if you want your kernel to support the

View File

@ -14,11 +14,15 @@
#include <linux/init.h>
#include <linux/of.h>
#include <linux/of_platform.h>
#include <linux/clk-provider.h>
#include <linux/clk/mvebu.h>
#include <linux/kexec.h>
#include <asm/mach/arch.h>
#include <asm/mach/map.h>
#include <mach/bridge-regs.h>
#include <linux/platform_data/usb-ehci-orion.h>
#include <plat/irq.h>
#include <plat/common.h>
#include "common.h"
static struct of_device_id kirkwood_dt_match_table[] __initdata = {
@ -26,18 +30,50 @@ static struct of_device_id kirkwood_dt_match_table[] __initdata = {
{ }
};
static struct of_dev_auxdata kirkwood_auxdata_lookup[] __initdata = {
OF_DEV_AUXDATA("marvell,orion-spi", 0xf1010600, "orion_spi.0", NULL),
OF_DEV_AUXDATA("marvell,mv64xxx-i2c", 0xf1011000, "mv64xxx_i2c.0",
NULL),
OF_DEV_AUXDATA("marvell,mv64xxx-i2c", 0xf1011100, "mv64xxx_i2c.1",
NULL),
OF_DEV_AUXDATA("marvell,orion-wdt", 0xf1020300, "orion_wdt", NULL),
OF_DEV_AUXDATA("marvell,orion-sata", 0xf1080000, "sata_mv.0", NULL),
OF_DEV_AUXDATA("marvell,orion-nand", 0xf4000000, "orion_nand", NULL),
OF_DEV_AUXDATA("marvell,orion-crypto", 0xf1030000, "mv_crypto", NULL),
{},
};
/*
* There are still devices that doesn't know about DT yet. Get clock
* gates here and add a clock lookup alias, so that old platform
* devices still work.
*/
static void __init kirkwood_legacy_clk_init(void)
{
struct device_node *np = of_find_compatible_node(
NULL, NULL, "marvell,kirkwood-gating-clock");
struct of_phandle_args clkspec;
clkspec.np = np;
clkspec.args_count = 1;
clkspec.args[0] = CGC_BIT_GE0;
orion_clkdev_add(NULL, "mv643xx_eth_port.0",
of_clk_get_from_provider(&clkspec));
clkspec.args[0] = CGC_BIT_PEX0;
orion_clkdev_add("0", "pcie",
of_clk_get_from_provider(&clkspec));
clkspec.args[0] = CGC_BIT_USB0;
orion_clkdev_add(NULL, "orion-ehci.0",
of_clk_get_from_provider(&clkspec));
clkspec.args[0] = CGC_BIT_PEX1;
orion_clkdev_add("1", "pcie",
of_clk_get_from_provider(&clkspec));
clkspec.args[0] = CGC_BIT_GE1;
orion_clkdev_add(NULL, "mv643xx_eth_port.1",
of_clk_get_from_provider(&clkspec));
}
static void __init kirkwood_of_clk_init(void)
{
mvebu_clocks_init();
kirkwood_legacy_clk_init();
}
static void __init kirkwood_dt_init(void)
{
@ -56,11 +92,7 @@ static void __init kirkwood_dt_init(void)
kirkwood_l2_init();
/* Setup root of clk tree */
kirkwood_clk_init();
/* internal devices that every board has */
kirkwood_xor0_init();
kirkwood_xor1_init();
kirkwood_of_clk_init();
#ifdef CONFIG_KEXEC
kexec_reinit = kirkwood_enable_pcie;
@ -115,8 +147,7 @@ static void __init kirkwood_dt_init(void)
if (of_machine_is_compatible("zyxel,nsa310"))
nsa310_init();
of_platform_populate(NULL, kirkwood_dt_match_table,
kirkwood_auxdata_lookup, NULL);
of_platform_populate(NULL, kirkwood_dt_match_table, NULL, NULL);
}
static const char * const kirkwood_dt_board_compat[] = {

View File

@ -260,8 +260,8 @@ void __init kirkwood_clk_init(void)
orion_clkdev_add(NULL, "orion_nand", runit);
orion_clkdev_add(NULL, "mvsdio", sdio);
orion_clkdev_add(NULL, "mv_crypto", crypto);
orion_clkdev_add(NULL, MV_XOR_SHARED_NAME ".0", xor0);
orion_clkdev_add(NULL, MV_XOR_SHARED_NAME ".1", xor1);
orion_clkdev_add(NULL, MV_XOR_NAME ".0", xor0);
orion_clkdev_add(NULL, MV_XOR_NAME ".1", xor1);
orion_clkdev_add("0", "pcie", pex0);
orion_clkdev_add("1", "pcie", pex1);
orion_clkdev_add(NULL, "kirkwood-i2s", audio);

View File

@ -9,6 +9,10 @@ config ARCH_MVEBU
select PINCTRL
select PLAT_ORION
select SPARSE_IRQ
select CLKDEV_LOOKUP
select MVEBU_CLK_CORE
select MVEBU_CLK_CPU
select MVEBU_CLK_GATING
if ARCH_MVEBU
@ -17,7 +21,9 @@ menu "Marvell SOC with device tree"
config MACH_ARMADA_370_XP
bool
select ARMADA_370_XP_TIMER
select CPU_V7
select HAVE_SMP
select CACHE_L2X0
select CPU_PJ4B
config MACH_ARMADA_370
bool "Marvell Armada 370 boards"

View File

@ -2,4 +2,6 @@ ccflags-$(CONFIG_ARCH_MULTIPLATFORM) := -I$(srctree)/$(src)/include \
-I$(srctree)/arch/arm/plat-orion/include
obj-y += system-controller.o
obj-$(CONFIG_MACH_ARMADA_370_XP) += armada-370-xp.o irq-armada-370-xp.o addr-map.o
obj-$(CONFIG_MACH_ARMADA_370_XP) += armada-370-xp.o irq-armada-370-xp.o addr-map.o coherency.o coherency_ll.o pmsu.o
obj-$(CONFIG_SMP) += platsmp.o headsmp.o
obj-$(CONFIG_HOTPLUG_CPU) += hotplug.o

View File

@ -78,7 +78,7 @@ armada_cfg_base(const struct orion_addr_map_cfg *cfg, int win)
if (win < 8)
offset = (win << 4);
else
offset = ARMADA_WINDOW_8_PLUS_OFFSET + (win << 3);
offset = ARMADA_WINDOW_8_PLUS_OFFSET + ((win - 8) << 3);
return cfg->bridge_virt_base + offset;
}
@ -108,6 +108,9 @@ static int __init armada_setup_cpu_mbus(void)
addr_map_cfg.bridge_virt_base = mbus_unit_addr_decoding_base;
if (of_find_compatible_node(NULL, NULL, "marvell,coherency-fabric"))
addr_map_cfg.hw_io_coherency = 1;
/*
* Disable, clear and configure windows.
*/

View File

@ -17,11 +17,14 @@
#include <linux/of_platform.h>
#include <linux/io.h>
#include <linux/time-armada-370-xp.h>
#include <linux/clk/mvebu.h>
#include <linux/dma-mapping.h>
#include <asm/mach/arch.h>
#include <asm/mach/map.h>
#include <asm/mach/time.h>
#include "armada-370-xp.h"
#include "common.h"
#include "coherency.h"
static struct map_desc armada_370_xp_io_desc[] __initdata = {
{
@ -37,27 +40,45 @@ void __init armada_370_xp_map_io(void)
iotable_init(armada_370_xp_io_desc, ARRAY_SIZE(armada_370_xp_io_desc));
}
void __init armada_370_xp_timer_and_clk_init(void)
{
mvebu_clocks_init();
armada_370_xp_timer_init();
}
void __init armada_370_xp_init_early(void)
{
/*
* Some Armada 370/XP devices allocate their coherent buffers
* from atomic context. Increase size of atomic coherent pool
* to make sure such the allocations won't fail.
*/
init_dma_coherent_pool_size(SZ_1M);
}
struct sys_timer armada_370_xp_timer = {
.init = armada_370_xp_timer_init,
.init = armada_370_xp_timer_and_clk_init,
};
static void __init armada_370_xp_dt_init(void)
{
of_platform_populate(NULL, of_default_bus_match_table, NULL, NULL);
coherency_init();
}
static const char * const armada_370_xp_dt_board_dt_compat[] = {
"marvell,a370-db",
"marvell,axp-db",
static const char * const armada_370_xp_dt_compat[] = {
"marvell,armada-370-xp",
NULL,
};
DT_MACHINE_START(ARMADA_XP_DT, "Marvell Aramada 370/XP (Device Tree)")
DT_MACHINE_START(ARMADA_XP_DT, "Marvell Armada 370/XP (Device Tree)")
.smp = smp_ops(armada_xp_smp_ops),
.init_machine = armada_370_xp_dt_init,
.map_io = armada_370_xp_map_io,
.init_early = armada_370_xp_init_early,
.init_irq = armada_370_xp_init_irq,
.handle_irq = armada_370_xp_handle_irq,
.timer = &armada_370_xp_timer,
.restart = mvebu_restart,
.dt_compat = armada_370_xp_dt_board_dt_compat,
.dt_compat = armada_370_xp_dt_compat,
MACHINE_END

View File

@ -19,4 +19,11 @@
#define ARMADA_370_XP_REGS_VIRT_BASE IOMEM(0xfeb00000)
#define ARMADA_370_XP_REGS_SIZE SZ_1M
#ifdef CONFIG_SMP
#include <linux/cpumask.h>
void armada_mpic_send_doorbell(const struct cpumask *mask, unsigned int irq);
void armada_xp_mpic_smp_cpu_init(void);
#endif
#endif /* __MACH_ARMADA_370_XP_H */

View File

@ -0,0 +1,155 @@
/*
* Coherency fabric (Aurora) support for Armada 370 and XP platforms.
*
* Copyright (C) 2012 Marvell
*
* Yehuda Yitschak <yehuday@marvell.com>
* Gregory Clement <gregory.clement@free-electrons.com>
* Thomas Petazzoni <thomas.petazzoni@free-electrons.com>
*
* This file is licensed under the terms of the GNU General Public
* License version 2. This program is licensed "as is" without any
* warranty of any kind, whether express or implied.
*
* The Armada 370 and Armada XP SOCs have a coherency fabric which is
* responsible for ensuring hardware coherency between all CPUs and between
* CPUs and I/O masters. This file initializes the coherency fabric and
* supplies basic routines for configuring and controlling hardware coherency
*/
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/of_address.h>
#include <linux/io.h>
#include <linux/smp.h>
#include <linux/dma-mapping.h>
#include <linux/platform_device.h>
#include <asm/smp_plat.h>
#include "armada-370-xp.h"
/*
* Some functions in this file are called very early during SMP
* initialization. At that time the device tree framework is not yet
* ready, and it is not possible to get the register address to
* ioremap it. That's why the pointer below is given with an initial
* value matching its virtual mapping
*/
static void __iomem *coherency_base = ARMADA_370_XP_REGS_VIRT_BASE + 0x20200;
static void __iomem *coherency_cpu_base;
/* Coherency fabric registers */
#define COHERENCY_FABRIC_CFG_OFFSET 0x4
#define IO_SYNC_BARRIER_CTL_OFFSET 0x0
static struct of_device_id of_coherency_table[] = {
{.compatible = "marvell,coherency-fabric"},
{ /* end of list */ },
};
#ifdef CONFIG_SMP
int coherency_get_cpu_count(void)
{
int reg, cnt;
reg = readl(coherency_base + COHERENCY_FABRIC_CFG_OFFSET);
cnt = (reg & 0xF) + 1;
return cnt;
}
#endif
/* Function defined in coherency_ll.S */
int ll_set_cpu_coherent(void __iomem *base_addr, unsigned int hw_cpu_id);
int set_cpu_coherent(unsigned int hw_cpu_id, int smp_group_id)
{
if (!coherency_base) {
pr_warn("Can't make CPU %d cache coherent.\n", hw_cpu_id);
pr_warn("Coherency fabric is not initialized\n");
return 1;
}
return ll_set_cpu_coherent(coherency_base, hw_cpu_id);
}
static inline void mvebu_hwcc_sync_io_barrier(void)
{
writel(0x1, coherency_cpu_base + IO_SYNC_BARRIER_CTL_OFFSET);
while (readl(coherency_cpu_base + IO_SYNC_BARRIER_CTL_OFFSET) & 0x1);
}
static dma_addr_t mvebu_hwcc_dma_map_page(struct device *dev, struct page *page,
unsigned long offset, size_t size,
enum dma_data_direction dir,
struct dma_attrs *attrs)
{
if (dir != DMA_TO_DEVICE)
mvebu_hwcc_sync_io_barrier();
return pfn_to_dma(dev, page_to_pfn(page)) + offset;
}
static void mvebu_hwcc_dma_unmap_page(struct device *dev, dma_addr_t dma_handle,
size_t size, enum dma_data_direction dir,
struct dma_attrs *attrs)
{
if (dir != DMA_TO_DEVICE)
mvebu_hwcc_sync_io_barrier();
}
static void mvebu_hwcc_dma_sync(struct device *dev, dma_addr_t dma_handle,
size_t size, enum dma_data_direction dir)
{
if (dir != DMA_TO_DEVICE)
mvebu_hwcc_sync_io_barrier();
}
static struct dma_map_ops mvebu_hwcc_dma_ops = {
.alloc = arm_dma_alloc,
.free = arm_dma_free,
.mmap = arm_dma_mmap,
.map_page = mvebu_hwcc_dma_map_page,
.unmap_page = mvebu_hwcc_dma_unmap_page,
.get_sgtable = arm_dma_get_sgtable,
.map_sg = arm_dma_map_sg,
.unmap_sg = arm_dma_unmap_sg,
.sync_single_for_cpu = mvebu_hwcc_dma_sync,
.sync_single_for_device = mvebu_hwcc_dma_sync,
.sync_sg_for_cpu = arm_dma_sync_sg_for_cpu,
.sync_sg_for_device = arm_dma_sync_sg_for_device,
.set_dma_mask = arm_dma_set_mask,
};
static int mvebu_hwcc_platform_notifier(struct notifier_block *nb,
unsigned long event, void *__dev)
{
struct device *dev = __dev;
if (event != BUS_NOTIFY_ADD_DEVICE)
return NOTIFY_DONE;
set_dma_ops(dev, &mvebu_hwcc_dma_ops);
return NOTIFY_OK;
}
static struct notifier_block mvebu_hwcc_platform_nb = {
.notifier_call = mvebu_hwcc_platform_notifier,
};
int __init coherency_init(void)
{
struct device_node *np;
np = of_find_matching_node(NULL, of_coherency_table);
if (np) {
pr_info("Initializing Coherency fabric\n");
coherency_base = of_iomap(np, 0);
coherency_cpu_base = of_iomap(np, 1);
set_cpu_coherent(cpu_logical_map(smp_processor_id()), 0);
bus_register_notifier(&platform_bus_type,
&mvebu_hwcc_platform_nb);
}
return 0;
}

View File

@ -0,0 +1,24 @@
/*
* arch/arm/mach-mvebu/include/mach/coherency.h
*
*
* Coherency fabric (Aurora) support for Armada 370 and XP platforms.
*
* Copyright (C) 2012 Marvell
*
* This file is licensed under the terms of the GNU General Public
* License version 2. This program is licensed "as is" without any
* warranty of any kind, whether express or implied.
*/
#ifndef __MACH_370_XP_COHERENCY_H
#define __MACH_370_XP_COHERENCY_H
#ifdef CONFIG_SMP
int coherency_get_cpu_count(void);
#endif
int set_cpu_coherent(int cpu_id, int smp_group_id);
int coherency_init(void);
#endif /* __MACH_370_XP_COHERENCY_H */

View File

@ -0,0 +1,49 @@
/*
* Coherency fabric: low level functions
*
* Copyright (C) 2012 Marvell
*
* Gregory CLEMENT <gregory.clement@free-electrons.com>
*
* This file is licensed under the terms of the GNU General Public
* License version 2. This program is licensed "as is" without any
* warranty of any kind, whether express or implied.
*
* This file implements the assembly function to add a CPU to the
* coherency fabric. This function is called by each of the secondary
* CPUs during their early boot in an SMP kernel, this why this
* function have to callable from assembly. It can also be called by a
* primary CPU from C code during its boot.
*/
#include <linux/linkage.h>
#define ARMADA_XP_CFB_CTL_REG_OFFSET 0x0
#define ARMADA_XP_CFB_CFG_REG_OFFSET 0x4
.text
/*
* r0: Coherency fabric base register address
* r1: HW CPU id
*/
ENTRY(ll_set_cpu_coherent)
/* Create bit by cpu index */
mov r3, #(1 << 24)
lsl r1, r3, r1
/* Add CPU to SMP group - Atomic */
add r3, r0, #ARMADA_XP_CFB_CTL_REG_OFFSET
ldr r2, [r3]
orr r2, r2, r1
str r2, [r3]
/* Enable coherency on CPU - Atomic */
add r3, r0, #ARMADA_XP_CFB_CFG_REG_OFFSET
ldr r2, [r3]
orr r2, r2, r1
str r2, [r3]
dsb
mov r0, #0
mov pc, lr
ENDPROC(ll_set_cpu_coherent)

View File

@ -20,4 +20,9 @@ void mvebu_restart(char mode, const char *cmd);
void armada_370_xp_init_irq(void);
void armada_370_xp_handle_irq(struct pt_regs *regs);
void armada_xp_cpu_die(unsigned int cpu);
int armada_370_xp_coherency_init(void);
int armada_370_xp_pmsu_init(void);
void armada_xp_secondary_startup(void);
extern struct smp_operations armada_xp_smp_ops;
#endif

View File

@ -0,0 +1,49 @@
/*
* SMP support: Entry point for secondary CPUs
*
* Copyright (C) 2012 Marvell
*
* Yehuda Yitschak <yehuday@marvell.com>
* Gregory CLEMENT <gregory.clement@free-electrons.com>
* Thomas Petazzoni <thomas.petazzoni@free-electrons.com>
*
* This file is licensed under the terms of the GNU General Public
* License version 2. This program is licensed "as is" without any
* warranty of any kind, whether express or implied.
*
* This file implements the assembly entry point for secondary CPUs in
* an SMP kernel. The only thing we need to do is to add the CPU to
* the coherency fabric by writing to 2 registers. Currently the base
* register addresses are hard coded due to the early initialisation
* problems.
*/
#include <linux/linkage.h>
#include <linux/init.h>
/*
* At this stage the secondary CPUs don't have acces yet to the MMU, so
* we have to provide physical addresses
*/
#define ARMADA_XP_CFB_BASE 0xD0020200
__CPUINIT
/*
* Armada XP specific entry point for secondary CPUs.
* We add the CPU to the coherency fabric and then jump to secondary
* startup
*/
ENTRY(armada_xp_secondary_startup)
/* Read CPU id */
mrc p15, 0, r1, c0, c0, 5
and r1, r1, #0xF
/* Add CPU to coherency fabric */
ldr r0, =ARMADA_XP_CFB_BASE
bl ll_set_cpu_coherent
b secondary_startup
ENDPROC(armada_xp_secondary_startup)

View File

@ -0,0 +1,30 @@
/*
* Symmetric Multi Processing (SMP) support for Armada XP
*
* Copyright (C) 2012 Marvell
*
* Lior Amsalem <alior@marvell.com>
* Gregory CLEMENT <gregory.clement@free-electrons.com>
* Thomas Petazzoni <thomas.petazzoni@free-electrons.com>
*
* This file is licensed under the terms of the GNU General Public
* License version 2. This program is licensed "as is" without any
* warranty of any kind, whether express or implied.
*/
#include <linux/kernel.h>
#include <linux/errno.h>
#include <linux/smp.h>
#include <asm/proc-fns.h>
/*
* platform-specific code to shutdown a CPU
*
* Called with IRQs disabled
*/
void __ref armada_xp_cpu_die(unsigned int cpu)
{
cpu_do_idle();
/* We should never return from idle */
panic("mvebu: cpu %d unexpectedly exit from shutdown\n", cpu);
}

View File

@ -24,6 +24,8 @@
#include <linux/irqdomain.h>
#include <asm/mach/arch.h>
#include <asm/exception.h>
#include <asm/smp_plat.h>
#include <asm/hardware/cache-l2x0.h>
/* Interrupt Controller Registers Map */
#define ARMADA_370_XP_INT_SET_MASK_OFFS (0x48)
@ -35,6 +37,12 @@
#define ARMADA_370_XP_CPU_INTACK_OFFS (0x44)
#define ARMADA_370_XP_SW_TRIG_INT_OFFS (0x4)
#define ARMADA_370_XP_IN_DRBEL_MSK_OFFS (0xc)
#define ARMADA_370_XP_IN_DRBEL_CAUSE_OFFS (0x8)
#define ACTIVE_DOORBELLS (8)
static void __iomem *per_cpu_int_base;
static void __iomem *main_int_base;
static struct irq_domain *armada_370_xp_mpic_domain;
@ -51,11 +59,22 @@ static void armada_370_xp_irq_unmask(struct irq_data *d)
per_cpu_int_base + ARMADA_370_XP_INT_CLEAR_MASK_OFFS);
}
#ifdef CONFIG_SMP
static int armada_xp_set_affinity(struct irq_data *d,
const struct cpumask *mask_val, bool force)
{
return 0;
}
#endif
static struct irq_chip armada_370_xp_irq_chip = {
.name = "armada_370_xp_irq",
.irq_mask = armada_370_xp_irq_mask,
.irq_mask_ack = armada_370_xp_irq_mask,
.irq_unmask = armada_370_xp_irq_unmask,
#ifdef CONFIG_SMP
.irq_set_affinity = armada_xp_set_affinity,
#endif
};
static int armada_370_xp_mpic_irq_map(struct irq_domain *h,
@ -72,6 +91,41 @@ static int armada_370_xp_mpic_irq_map(struct irq_domain *h,
return 0;
}
#ifdef CONFIG_SMP
void armada_mpic_send_doorbell(const struct cpumask *mask, unsigned int irq)
{
int cpu;
unsigned long map = 0;
/* Convert our logical CPU mask into a physical one. */
for_each_cpu(cpu, mask)
map |= 1 << cpu_logical_map(cpu);
/*
* Ensure that stores to Normal memory are visible to the
* other CPUs before issuing the IPI.
*/
dsb();
/* submit softirq */
writel((map << 8) | irq, main_int_base +
ARMADA_370_XP_SW_TRIG_INT_OFFS);
}
void armada_xp_mpic_smp_cpu_init(void)
{
/* Clear pending IPIs */
writel(0, per_cpu_int_base + ARMADA_370_XP_IN_DRBEL_CAUSE_OFFS);
/* Enable first 8 IPIs */
writel((1 << ACTIVE_DOORBELLS) - 1, per_cpu_int_base +
ARMADA_370_XP_IN_DRBEL_MSK_OFFS);
/* Unmask IPI interrupt */
writel(0, per_cpu_int_base + ARMADA_370_XP_INT_CLEAR_MASK_OFFS);
}
#endif /* CONFIG_SMP */
static struct irq_domain_ops armada_370_xp_mpic_irq_ops = {
.map = armada_370_xp_mpic_irq_map,
.xlate = irq_domain_xlate_onecell,
@ -91,13 +145,18 @@ static int __init armada_370_xp_mpic_of_init(struct device_node *node,
control = readl(main_int_base + ARMADA_370_XP_INT_CONTROL);
armada_370_xp_mpic_domain =
irq_domain_add_linear(node, (control >> 2) & 0x3ff,
&armada_370_xp_mpic_irq_ops, NULL);
irq_domain_add_linear(node, (control >> 2) & 0x3ff,
&armada_370_xp_mpic_irq_ops, NULL);
if (!armada_370_xp_mpic_domain)
panic("Unable to add Armada_370_Xp MPIC irq domain (DT)\n");
irq_set_default_host(armada_370_xp_mpic_domain);
#ifdef CONFIG_SMP
armada_xp_mpic_smp_cpu_init();
#endif
return 0;
}
@ -111,14 +170,36 @@ asmlinkage void __exception_irq_entry armada_370_xp_handle_irq(struct pt_regs
ARMADA_370_XP_CPU_INTACK_OFFS);
irqnr = irqstat & 0x3FF;
if (irqnr < 1023) {
irqnr =
irq_find_mapping(armada_370_xp_mpic_domain, irqnr);
if (irqnr > 1022)
break;
if (irqnr >= 8) {
irqnr = irq_find_mapping(armada_370_xp_mpic_domain,
irqnr);
handle_IRQ(irqnr, regs);
continue;
}
#ifdef CONFIG_SMP
/* IPI Handling */
if (irqnr == 0) {
u32 ipimask, ipinr;
ipimask = readl_relaxed(per_cpu_int_base +
ARMADA_370_XP_IN_DRBEL_CAUSE_OFFS)
& 0xFF;
writel(0x0, per_cpu_int_base +
ARMADA_370_XP_IN_DRBEL_CAUSE_OFFS);
/* Handle all pending doorbells */
for (ipinr = 0; ipinr < ACTIVE_DOORBELLS; ipinr++) {
if (ipimask & (0x1 << ipinr))
handle_IPI(ipinr, regs);
}
continue;
}
#endif
break;
} while (1);
}
@ -130,4 +211,7 @@ static const struct of_device_id mpic_of_match[] __initconst = {
void __init armada_370_xp_init_irq(void)
{
of_irq_init(mpic_of_match);
#ifdef CONFIG_CACHE_L2X0
l2x0_of_init(0, ~0UL);
#endif
}

View File

@ -0,0 +1,122 @@
/*
* Symmetric Multi Processing (SMP) support for Armada XP
*
* Copyright (C) 2012 Marvell
*
* Lior Amsalem <alior@marvell.com>
* Yehuda Yitschak <yehuday@marvell.com>
* Gregory CLEMENT <gregory.clement@free-electrons.com>
* Thomas Petazzoni <thomas.petazzoni@free-electrons.com>
*
* This file is licensed under the terms of the GNU General Public
* License version 2. This program is licensed "as is" without any
* warranty of any kind, whether express or implied.
*
* The Armada XP SoC has 4 ARMv7 PJ4B CPUs running in full HW coherency
* This file implements the routines for preparing the SMP infrastructure
* and waking up the secondary CPUs
*/
#include <linux/init.h>
#include <linux/smp.h>
#include <linux/clk.h>
#include <linux/of.h>
#include <asm/cacheflush.h>
#include <asm/smp_plat.h>
#include "common.h"
#include "armada-370-xp.h"
#include "pmsu.h"
#include "coherency.h"
void __init set_secondary_cpus_clock(void)
{
int thiscpu;
unsigned long rate;
struct clk *cpu_clk = NULL;
struct device_node *np = NULL;
thiscpu = smp_processor_id();
for_each_node_by_type(np, "cpu") {
int err;
int cpu;
err = of_property_read_u32(np, "reg", &cpu);
if (WARN_ON(err))
return;
if (cpu == thiscpu) {
cpu_clk = of_clk_get(np, 0);
break;
}
}
if (WARN_ON(IS_ERR(cpu_clk)))
return;
clk_prepare_enable(cpu_clk);
rate = clk_get_rate(cpu_clk);
/* set all the other CPU clk to the same rate than the boot CPU */
for_each_node_by_type(np, "cpu") {
int err;
int cpu;
err = of_property_read_u32(np, "reg", &cpu);
if (WARN_ON(err))
return;
if (cpu != thiscpu) {
cpu_clk = of_clk_get(np, 0);
clk_set_rate(cpu_clk, rate);
}
}
}
static void __cpuinit armada_xp_secondary_init(unsigned int cpu)
{
armada_xp_mpic_smp_cpu_init();
}
static int __cpuinit armada_xp_boot_secondary(unsigned int cpu,
struct task_struct *idle)
{
pr_info("Booting CPU %d\n", cpu);
armada_xp_boot_cpu(cpu, armada_xp_secondary_startup);
return 0;
}
static void __init armada_xp_smp_init_cpus(void)
{
unsigned int i, ncores;
ncores = coherency_get_cpu_count();
/* Limit possible CPUs to defconfig */
if (ncores > nr_cpu_ids) {
pr_warn("SMP: %d CPUs physically present. Only %d configured.",
ncores, nr_cpu_ids);
pr_warn("Clipping CPU count to %d\n", nr_cpu_ids);
ncores = nr_cpu_ids;
}
for (i = 0; i < ncores; i++)
set_cpu_possible(i, true);
set_smp_cross_call(armada_mpic_send_doorbell);
}
void __init armada_xp_smp_prepare_cpus(unsigned int max_cpus)
{
set_secondary_cpus_clock();
flush_cache_all();
set_cpu_coherent(cpu_logical_map(smp_processor_id()), 0);
}
struct smp_operations armada_xp_smp_ops __initdata = {
.smp_init_cpus = armada_xp_smp_init_cpus,
.smp_prepare_cpus = armada_xp_smp_prepare_cpus,
.smp_secondary_init = armada_xp_secondary_init,
.smp_boot_secondary = armada_xp_boot_secondary,
#ifdef CONFIG_HOTPLUG_CPU
.cpu_die = armada_xp_cpu_die,
#endif
};

View File

@ -0,0 +1,75 @@
/*
* Power Management Service Unit(PMSU) support for Armada 370/XP platforms.
*
* Copyright (C) 2012 Marvell
*
* Yehuda Yitschak <yehuday@marvell.com>
* Gregory Clement <gregory.clement@free-electrons.com>
* Thomas Petazzoni <thomas.petazzoni@free-electrons.com>
*
* This file is licensed under the terms of the GNU General Public
* License version 2. This program is licensed "as is" without any
* warranty of any kind, whether express or implied.
*
* The Armada 370 and Armada XP SOCs have a power management service
* unit which is responsible for powering down and waking up CPUs and
* other SOC units
*/
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/of_address.h>
#include <linux/io.h>
#include <linux/smp.h>
#include <asm/smp_plat.h>
static void __iomem *pmsu_mp_base;
static void __iomem *pmsu_reset_base;
#define PMSU_BOOT_ADDR_REDIRECT_OFFSET(cpu) ((cpu * 0x100) + 0x24)
#define PMSU_RESET_CTL_OFFSET(cpu) (cpu * 0x8)
static struct of_device_id of_pmsu_table[] = {
{.compatible = "marvell,armada-370-xp-pmsu"},
{ /* end of list */ },
};
#ifdef CONFIG_SMP
int armada_xp_boot_cpu(unsigned int cpu_id, void *boot_addr)
{
int reg, hw_cpu;
if (!pmsu_mp_base || !pmsu_reset_base) {
pr_warn("Can't boot CPU. PMSU is uninitialized\n");
return 1;
}
hw_cpu = cpu_logical_map(cpu_id);
writel(virt_to_phys(boot_addr), pmsu_mp_base +
PMSU_BOOT_ADDR_REDIRECT_OFFSET(hw_cpu));
/* Release CPU from reset by clearing reset bit*/
reg = readl(pmsu_reset_base + PMSU_RESET_CTL_OFFSET(hw_cpu));
reg &= (~0x1);
writel(reg, pmsu_reset_base + PMSU_RESET_CTL_OFFSET(hw_cpu));
return 0;
}
#endif
int __init armada_370_xp_pmsu_init(void)
{
struct device_node *np;
np = of_find_matching_node(NULL, of_pmsu_table);
if (np) {
pr_info("Initializing Power Management Service Unit\n");
pmsu_mp_base = of_iomap(np, 0);
pmsu_reset_base = of_iomap(np, 1);
}
return 0;
}
early_initcall(armada_370_xp_pmsu_init);

View File

@ -0,0 +1,16 @@
/*
* Power Management Service Unit (PMSU) support for Armada 370/XP platforms.
*
* Copyright (C) 2012 Marvell
*
* This file is licensed under the terms of the GNU General Public
* License version 2. This program is licensed "as is" without any
* warranty of any kind, whether express or implied.
*/
#ifndef __MACH_MVEBU_PMSU_H
#define __MACH_MVEBU_PMSU_H
int armada_xp_boot_cpu(unsigned int cpu_id, void *phys_addr);
#endif /* __MACH_370_XP_PMSU_H */

View File

@ -352,6 +352,10 @@ config CPU_PJ4
select ARM_THUMBEE
select CPU_V7
config CPU_PJ4B
bool
select CPU_V7
# ARMv6
config CPU_V6
bool "Support ARM V6 processor" if ARCH_INTEGRATOR || MACH_REALVIEW_EB || MACH_REALVIEW_PBX

View File

@ -124,8 +124,6 @@ static void arm_dma_sync_single_for_device(struct device *dev,
__dma_page_cpu_to_dev(page, offset, size, dir);
}
static int arm_dma_set_mask(struct device *dev, u64 dma_mask);
struct dma_map_ops arm_dma_ops = {
.alloc = arm_dma_alloc,
.free = arm_dma_free,
@ -971,7 +969,7 @@ int dma_supported(struct device *dev, u64 mask)
}
EXPORT_SYMBOL(dma_supported);
static int arm_dma_set_mask(struct device *dev, u64 dma_mask)
int arm_dma_set_mask(struct device *dev, u64 dma_mask)
{
if (!dev->dma_mask || !dma_supported(dev, dma_mask))
return -EIO;

View File

@ -169,6 +169,63 @@ __v7_ca15mp_setup:
orreq r0, r0, r10 @ Enable CPU-specific SMP bits
mcreq p15, 0, r0, c1, c0, 1
#endif
__v7_pj4b_setup:
#ifdef CONFIG_CPU_PJ4B
/* Auxiliary Debug Modes Control 1 Register */
#define PJ4B_STATIC_BP (1 << 2) /* Enable Static BP */
#define PJ4B_INTER_PARITY (1 << 8) /* Disable Internal Parity Handling */
#define PJ4B_BCK_OFF_STREX (1 << 5) /* Enable the back off of STREX instr */
#define PJ4B_CLEAN_LINE (1 << 16) /* Disable data transfer for clean line */
/* Auxiliary Debug Modes Control 2 Register */
#define PJ4B_FAST_LDR (1 << 23) /* Disable fast LDR */
#define PJ4B_SNOOP_DATA (1 << 25) /* Do not interleave write and snoop data */
#define PJ4B_CWF (1 << 27) /* Disable Critical Word First feature */
#define PJ4B_OUTSDNG_NC (1 << 29) /* Disable outstanding non cacheable rqst */
#define PJ4B_L1_REP_RR (1 << 30) /* L1 replacement - Strict round robin */
#define PJ4B_AUX_DBG_CTRL2 (PJ4B_SNOOP_DATA | PJ4B_CWF |\
PJ4B_OUTSDNG_NC | PJ4B_L1_REP_RR)
/* Auxiliary Functional Modes Control Register 0 */
#define PJ4B_SMP_CFB (1 << 1) /* Set SMP mode. Join the coherency fabric */
#define PJ4B_L1_PAR_CHK (1 << 2) /* Support L1 parity checking */
#define PJ4B_BROADCAST_CACHE (1 << 8) /* Broadcast Cache and TLB maintenance */
/* Auxiliary Debug Modes Control 0 Register */
#define PJ4B_WFI_WFE (1 << 22) /* WFI/WFE - serve the DVM and back to idle */
/* Auxiliary Debug Modes Control 1 Register */
mrc p15, 1, r0, c15, c1, 1
orr r0, r0, #PJ4B_CLEAN_LINE
orr r0, r0, #PJ4B_BCK_OFF_STREX
orr r0, r0, #PJ4B_INTER_PARITY
bic r0, r0, #PJ4B_STATIC_BP
mcr p15, 1, r0, c15, c1, 1
/* Auxiliary Debug Modes Control 2 Register */
mrc p15, 1, r0, c15, c1, 2
bic r0, r0, #PJ4B_FAST_LDR
orr r0, r0, #PJ4B_AUX_DBG_CTRL2
mcr p15, 1, r0, c15, c1, 2
/* Auxiliary Functional Modes Control Register 0 */
mrc p15, 1, r0, c15, c2, 0
#ifdef CONFIG_SMP
orr r0, r0, #PJ4B_SMP_CFB
#endif
orr r0, r0, #PJ4B_L1_PAR_CHK
orr r0, r0, #PJ4B_BROADCAST_CACHE
mcr p15, 1, r0, c15, c2, 0
/* Auxiliary Debug Modes Control 0 Register */
mrc p15, 1, r0, c15, c1, 0
orr r0, r0, #PJ4B_WFI_WFE
mcr p15, 1, r0, c15, c1, 0
#endif /* CONFIG_CPU_PJ4B */
__v7_setup:
adr r12, __v7_setup_stack @ the local stack
stmia r12, {r0-r5, r7, r9, r11, lr}
@ -342,6 +399,16 @@ __v7_ca9mp_proc_info:
.long 0xff0ffff0
__v7_proc __v7_ca9mp_setup
.size __v7_ca9mp_proc_info, . - __v7_ca9mp_proc_info
/*
* Marvell PJ4B processor.
*/
.type __v7_pj4b_proc_info, #object
__v7_pj4b_proc_info:
.long 0x562f5840
.long 0xfffffff0
__v7_proc __v7_pj4b_setup
.size __v7_pj4b_proc_info, . - __v7_pj4b_proc_info
#endif /* CONFIG_ARM_LPAE */
/*

View File

@ -42,6 +42,8 @@ EXPORT_SYMBOL_GPL(mv_mbus_dram_info);
#define WIN_REMAP_LO_OFF 0x0008
#define WIN_REMAP_HI_OFF 0x000c
#define ATTR_HW_COHERENCY (0x1 << 4)
/*
* Default implementation
*/
@ -163,6 +165,8 @@ void __init orion_setup_cpu_mbus_target(const struct orion_addr_map_cfg *cfg,
w = &orion_mbus_dram_info.cs[cs++];
w->cs_index = i;
w->mbus_attr = 0xf & ~(1 << i);
if (cfg->hw_io_coherency)
w->mbus_attr |= ATTR_HW_COHERENCY;
w->base = base & 0xffff0000;
w->size = (size | 0x0000ffff) + 1;
}

View File

@ -606,26 +606,6 @@ void __init orion_wdt_init(void)
****************************************************************************/
static u64 orion_xor_dmamask = DMA_BIT_MASK(32);
void __init orion_xor_init_channels(
struct mv_xor_platform_data *orion_xor0_data,
struct platform_device *orion_xor0_channel,
struct mv_xor_platform_data *orion_xor1_data,
struct platform_device *orion_xor1_channel)
{
/*
* two engines can't do memset simultaneously, this limitation
* satisfied by removing memset support from one of the engines.
*/
dma_cap_set(DMA_MEMCPY, orion_xor0_data->cap_mask);
dma_cap_set(DMA_XOR, orion_xor0_data->cap_mask);
platform_device_register(orion_xor0_channel);
dma_cap_set(DMA_MEMCPY, orion_xor1_data->cap_mask);
dma_cap_set(DMA_MEMSET, orion_xor1_data->cap_mask);
dma_cap_set(DMA_XOR, orion_xor1_data->cap_mask);
platform_device_register(orion_xor1_channel);
}
/*****************************************************************************
* XOR0
****************************************************************************/
@ -636,61 +616,30 @@ static struct resource orion_xor0_shared_resources[] = {
}, {
.name = "xor 0 high",
.flags = IORESOURCE_MEM,
}, {
.name = "irq channel 0",
.flags = IORESOURCE_IRQ,
}, {
.name = "irq channel 1",
.flags = IORESOURCE_IRQ,
},
};
static struct mv_xor_channel_data orion_xor0_channels_data[2];
static struct mv_xor_platform_data orion_xor0_pdata = {
.channels = orion_xor0_channels_data,
};
static struct platform_device orion_xor0_shared = {
.name = MV_XOR_SHARED_NAME,
.name = MV_XOR_NAME,
.id = 0,
.num_resources = ARRAY_SIZE(orion_xor0_shared_resources),
.resource = orion_xor0_shared_resources,
};
static struct resource orion_xor00_resources[] = {
[0] = {
.flags = IORESOURCE_IRQ,
},
};
static struct mv_xor_platform_data orion_xor00_data = {
.shared = &orion_xor0_shared,
.hw_id = 0,
.pool_size = PAGE_SIZE,
};
static struct platform_device orion_xor00_channel = {
.name = MV_XOR_NAME,
.id = 0,
.num_resources = ARRAY_SIZE(orion_xor00_resources),
.resource = orion_xor00_resources,
.dev = {
.dma_mask = &orion_xor_dmamask,
.coherent_dma_mask = DMA_BIT_MASK(64),
.platform_data = &orion_xor00_data,
},
};
static struct resource orion_xor01_resources[] = {
[0] = {
.flags = IORESOURCE_IRQ,
},
};
static struct mv_xor_platform_data orion_xor01_data = {
.shared = &orion_xor0_shared,
.hw_id = 1,
.pool_size = PAGE_SIZE,
};
static struct platform_device orion_xor01_channel = {
.name = MV_XOR_NAME,
.id = 1,
.num_resources = ARRAY_SIZE(orion_xor01_resources),
.resource = orion_xor01_resources,
.dev = {
.dma_mask = &orion_xor_dmamask,
.coherent_dma_mask = DMA_BIT_MASK(64),
.platform_data = &orion_xor01_data,
.dev = {
.dma_mask = &orion_xor_dmamask,
.coherent_dma_mask = DMA_BIT_MASK(64),
.platform_data = &orion_xor0_pdata,
},
};
@ -704,15 +653,23 @@ void __init orion_xor0_init(unsigned long mapbase_low,
orion_xor0_shared_resources[1].start = mapbase_high;
orion_xor0_shared_resources[1].end = mapbase_high + 0xff;
orion_xor00_resources[0].start = irq_0;
orion_xor00_resources[0].end = irq_0;
orion_xor01_resources[0].start = irq_1;
orion_xor01_resources[0].end = irq_1;
orion_xor0_shared_resources[2].start = irq_0;
orion_xor0_shared_resources[2].end = irq_0;
orion_xor0_shared_resources[3].start = irq_1;
orion_xor0_shared_resources[3].end = irq_1;
/*
* two engines can't do memset simultaneously, this limitation
* satisfied by removing memset support from one of the engines.
*/
dma_cap_set(DMA_MEMCPY, orion_xor0_channels_data[0].cap_mask);
dma_cap_set(DMA_XOR, orion_xor0_channels_data[0].cap_mask);
dma_cap_set(DMA_MEMSET, orion_xor0_channels_data[1].cap_mask);
dma_cap_set(DMA_MEMCPY, orion_xor0_channels_data[1].cap_mask);
dma_cap_set(DMA_XOR, orion_xor0_channels_data[1].cap_mask);
platform_device_register(&orion_xor0_shared);
orion_xor_init_channels(&orion_xor00_data, &orion_xor00_channel,
&orion_xor01_data, &orion_xor01_channel);
}
/*****************************************************************************
@ -725,61 +682,30 @@ static struct resource orion_xor1_shared_resources[] = {
}, {
.name = "xor 1 high",
.flags = IORESOURCE_MEM,
}, {
.name = "irq channel 0",
.flags = IORESOURCE_IRQ,
}, {
.name = "irq channel 1",
.flags = IORESOURCE_IRQ,
},
};
static struct mv_xor_channel_data orion_xor1_channels_data[2];
static struct mv_xor_platform_data orion_xor1_pdata = {
.channels = orion_xor1_channels_data,
};
static struct platform_device orion_xor1_shared = {
.name = MV_XOR_SHARED_NAME,
.name = MV_XOR_NAME,
.id = 1,
.num_resources = ARRAY_SIZE(orion_xor1_shared_resources),
.resource = orion_xor1_shared_resources,
};
static struct resource orion_xor10_resources[] = {
[0] = {
.flags = IORESOURCE_IRQ,
},
};
static struct mv_xor_platform_data orion_xor10_data = {
.shared = &orion_xor1_shared,
.hw_id = 0,
.pool_size = PAGE_SIZE,
};
static struct platform_device orion_xor10_channel = {
.name = MV_XOR_NAME,
.id = 2,
.num_resources = ARRAY_SIZE(orion_xor10_resources),
.resource = orion_xor10_resources,
.dev = {
.dma_mask = &orion_xor_dmamask,
.coherent_dma_mask = DMA_BIT_MASK(64),
.platform_data = &orion_xor10_data,
},
};
static struct resource orion_xor11_resources[] = {
[0] = {
.flags = IORESOURCE_IRQ,
},
};
static struct mv_xor_platform_data orion_xor11_data = {
.shared = &orion_xor1_shared,
.hw_id = 1,
.pool_size = PAGE_SIZE,
};
static struct platform_device orion_xor11_channel = {
.name = MV_XOR_NAME,
.id = 3,
.num_resources = ARRAY_SIZE(orion_xor11_resources),
.resource = orion_xor11_resources,
.dev = {
.dma_mask = &orion_xor_dmamask,
.coherent_dma_mask = DMA_BIT_MASK(64),
.platform_data = &orion_xor11_data,
.dev = {
.dma_mask = &orion_xor_dmamask,
.coherent_dma_mask = DMA_BIT_MASK(64),
.platform_data = &orion_xor1_pdata,
},
};
@ -793,15 +719,23 @@ void __init orion_xor1_init(unsigned long mapbase_low,
orion_xor1_shared_resources[1].start = mapbase_high;
orion_xor1_shared_resources[1].end = mapbase_high + 0xff;
orion_xor10_resources[0].start = irq_0;
orion_xor10_resources[0].end = irq_0;
orion_xor11_resources[0].start = irq_1;
orion_xor11_resources[0].end = irq_1;
orion_xor1_shared_resources[2].start = irq_0;
orion_xor1_shared_resources[2].end = irq_0;
orion_xor1_shared_resources[3].start = irq_1;
orion_xor1_shared_resources[3].end = irq_1;
/*
* two engines can't do memset simultaneously, this limitation
* satisfied by removing memset support from one of the engines.
*/
dma_cap_set(DMA_MEMCPY, orion_xor1_channels_data[0].cap_mask);
dma_cap_set(DMA_XOR, orion_xor1_channels_data[0].cap_mask);
dma_cap_set(DMA_MEMSET, orion_xor1_channels_data[1].cap_mask);
dma_cap_set(DMA_MEMCPY, orion_xor1_channels_data[1].cap_mask);
dma_cap_set(DMA_XOR, orion_xor1_channels_data[1].cap_mask);
platform_device_register(&orion_xor1_shared);
orion_xor_init_channels(&orion_xor10_data, &orion_xor10_channel,
&orion_xor11_data, &orion_xor11_channel);
}
/*****************************************************************************

View File

@ -17,6 +17,7 @@ struct orion_addr_map_cfg {
const int num_wins; /* Total number of windows */
const int remappable_wins;
void __iomem *bridge_virt_base;
int hw_io_coherency;
/* If NULL, the default cpu_win_can_remap will be used, using
the value in remappable_wins */

View File

@ -12,6 +12,7 @@
#include <linux/mv643xx_eth.h>
struct dsa_platform_data;
struct mv_sata_platform_data;
void __init orion_uart0_init(void __iomem *membase,
resource_size_t mapbase,

View File

@ -64,3 +64,5 @@ config CLK_TWL6040
as functional clock.
endmenu
source "drivers/clk/mvebu/Kconfig"

View File

@ -13,6 +13,7 @@ obj-$(CONFIG_PLAT_SPEAR) += spear/
obj-$(CONFIG_ARCH_U300) += clk-u300.o
obj-$(CONFIG_COMMON_CLK_VERSATILE) += versatile/
obj-$(CONFIG_ARCH_PRIMA2) += clk-prima2.o
obj-$(CONFIG_PLAT_ORION) += mvebu/
ifeq ($(CONFIG_COMMON_CLK), y)
obj-$(CONFIG_ARCH_MMP) += mmp/
endif

View File

@ -0,0 +1,8 @@
config MVEBU_CLK_CORE
bool
config MVEBU_CLK_CPU
bool
config MVEBU_CLK_GATING
bool

View File

@ -0,0 +1,3 @@
obj-$(CONFIG_MVEBU_CLK_CORE) += clk.o clk-core.o
obj-$(CONFIG_MVEBU_CLK_CPU) += clk-cpu.o
obj-$(CONFIG_MVEBU_CLK_GATING) += clk-gating-ctrl.o

View File

@ -0,0 +1,675 @@
/*
* Marvell EBU clock core handling defined at reset
*
* Copyright (C) 2012 Marvell
*
* Gregory CLEMENT <gregory.clement@free-electrons.com>
* Sebastian Hesselbarth <sebastian.hesselbarth@gmail.com>
*
* This file is licensed under the terms of the GNU General Public
* License version 2. This program is licensed "as is" without any
* warranty of any kind, whether express or implied.
*/
#include <linux/kernel.h>
#include <linux/clk.h>
#include <linux/clkdev.h>
#include <linux/clk-provider.h>
#include <linux/of_address.h>
#include <linux/io.h>
#include <linux/of.h>
#include "clk-core.h"
struct core_ratio {
int id;
const char *name;
};
struct core_clocks {
u32 (*get_tclk_freq)(void __iomem *sar);
u32 (*get_cpu_freq)(void __iomem *sar);
void (*get_clk_ratio)(void __iomem *sar, int id, int *mult, int *div);
const struct core_ratio *ratios;
int num_ratios;
};
static struct clk_onecell_data clk_data;
static void __init mvebu_clk_core_setup(struct device_node *np,
struct core_clocks *coreclk)
{
const char *tclk_name = "tclk";
const char *cpuclk_name = "cpuclk";
void __iomem *base;
unsigned long rate;
int n;
base = of_iomap(np, 0);
if (WARN_ON(!base))
return;
/*
* Allocate struct for TCLK, cpu clk, and core ratio clocks
*/
clk_data.clk_num = 2 + coreclk->num_ratios;
clk_data.clks = kzalloc(clk_data.clk_num * sizeof(struct clk *),
GFP_KERNEL);
if (WARN_ON(!clk_data.clks))
return;
/*
* Register TCLK
*/
of_property_read_string_index(np, "clock-output-names", 0,
&tclk_name);
rate = coreclk->get_tclk_freq(base);
clk_data.clks[0] = clk_register_fixed_rate(NULL, tclk_name, NULL,
CLK_IS_ROOT, rate);
WARN_ON(IS_ERR(clk_data.clks[0]));
/*
* Register CPU clock
*/
of_property_read_string_index(np, "clock-output-names", 1,
&cpuclk_name);
rate = coreclk->get_cpu_freq(base);
clk_data.clks[1] = clk_register_fixed_rate(NULL, cpuclk_name, NULL,
CLK_IS_ROOT, rate);
WARN_ON(IS_ERR(clk_data.clks[1]));
/*
* Register fixed-factor clocks derived from CPU clock
*/
for (n = 0; n < coreclk->num_ratios; n++) {
const char *rclk_name = coreclk->ratios[n].name;
int mult, div;
of_property_read_string_index(np, "clock-output-names",
2+n, &rclk_name);
coreclk->get_clk_ratio(base, coreclk->ratios[n].id,
&mult, &div);
clk_data.clks[2+n] = clk_register_fixed_factor(NULL, rclk_name,
cpuclk_name, 0, mult, div);
WARN_ON(IS_ERR(clk_data.clks[2+n]));
};
/*
* SAR register isn't needed anymore
*/
iounmap(base);
of_clk_add_provider(np, of_clk_src_onecell_get, &clk_data);
}
#ifdef CONFIG_MACH_ARMADA_370_XP
/*
* Armada 370/XP Sample At Reset is a 64 bit bitfiled split in two
* register of 32 bits
*/
#define SARL 0 /* Low part [0:31] */
#define SARL_AXP_PCLK_FREQ_OPT 21
#define SARL_AXP_PCLK_FREQ_OPT_MASK 0x7
#define SARL_A370_PCLK_FREQ_OPT 11
#define SARL_A370_PCLK_FREQ_OPT_MASK 0xF
#define SARL_AXP_FAB_FREQ_OPT 24
#define SARL_AXP_FAB_FREQ_OPT_MASK 0xF
#define SARL_A370_FAB_FREQ_OPT 15
#define SARL_A370_FAB_FREQ_OPT_MASK 0x1F
#define SARL_A370_TCLK_FREQ_OPT 20
#define SARL_A370_TCLK_FREQ_OPT_MASK 0x1
#define SARH 4 /* High part [32:63] */
#define SARH_AXP_PCLK_FREQ_OPT (52-32)
#define SARH_AXP_PCLK_FREQ_OPT_MASK 0x1
#define SARH_AXP_PCLK_FREQ_OPT_SHIFT 3
#define SARH_AXP_FAB_FREQ_OPT (51-32)
#define SARH_AXP_FAB_FREQ_OPT_MASK 0x1
#define SARH_AXP_FAB_FREQ_OPT_SHIFT 4
static const u32 __initconst armada_370_tclk_frequencies[] = {
16600000,
20000000,
};
static u32 __init armada_370_get_tclk_freq(void __iomem *sar)
{
u8 tclk_freq_select = 0;
tclk_freq_select = ((readl(sar) >> SARL_A370_TCLK_FREQ_OPT) &
SARL_A370_TCLK_FREQ_OPT_MASK);
return armada_370_tclk_frequencies[tclk_freq_select];
}
static const u32 __initconst armada_370_cpu_frequencies[] = {
400000000,
533000000,
667000000,
800000000,
1000000000,
1067000000,
1200000000,
};
static u32 __init armada_370_get_cpu_freq(void __iomem *sar)
{
u32 cpu_freq;
u8 cpu_freq_select = 0;
cpu_freq_select = ((readl(sar) >> SARL_A370_PCLK_FREQ_OPT) &
SARL_A370_PCLK_FREQ_OPT_MASK);
if (cpu_freq_select > ARRAY_SIZE(armada_370_cpu_frequencies)) {
pr_err("CPU freq select unsuported %d\n", cpu_freq_select);
cpu_freq = 0;
} else
cpu_freq = armada_370_cpu_frequencies[cpu_freq_select];
return cpu_freq;
}
enum { A370_XP_NBCLK, A370_XP_HCLK, A370_XP_DRAMCLK };
static const struct core_ratio __initconst armada_370_xp_core_ratios[] = {
{ .id = A370_XP_NBCLK, .name = "nbclk" },
{ .id = A370_XP_HCLK, .name = "hclk" },
{ .id = A370_XP_DRAMCLK, .name = "dramclk" },
};
static const int __initconst armada_370_xp_nbclk_ratios[32][2] = {
{0, 1}, {1, 2}, {2, 2}, {2, 2},
{1, 2}, {1, 2}, {1, 1}, {2, 3},
{0, 1}, {1, 2}, {2, 4}, {0, 1},
{1, 2}, {0, 1}, {0, 1}, {2, 2},
{0, 1}, {0, 1}, {0, 1}, {1, 1},
{2, 3}, {0, 1}, {0, 1}, {0, 1},
{0, 1}, {0, 1}, {0, 1}, {1, 1},
{0, 1}, {0, 1}, {0, 1}, {0, 1},
};
static const int __initconst armada_370_xp_hclk_ratios[32][2] = {
{0, 1}, {1, 2}, {2, 6}, {2, 3},
{1, 3}, {1, 4}, {1, 2}, {2, 6},
{0, 1}, {1, 6}, {2, 10}, {0, 1},
{1, 4}, {0, 1}, {0, 1}, {2, 5},
{0, 1}, {0, 1}, {0, 1}, {1, 2},
{2, 6}, {0, 1}, {0, 1}, {0, 1},
{0, 1}, {0, 1}, {0, 1}, {1, 1},
{0, 1}, {0, 1}, {0, 1}, {0, 1},
};
static const int __initconst armada_370_xp_dramclk_ratios[32][2] = {
{0, 1}, {1, 2}, {2, 3}, {2, 3},
{1, 3}, {1, 2}, {1, 2}, {2, 6},
{0, 1}, {1, 3}, {2, 5}, {0, 1},
{1, 4}, {0, 1}, {0, 1}, {2, 5},
{0, 1}, {0, 1}, {0, 1}, {1, 1},
{2, 3}, {0, 1}, {0, 1}, {0, 1},
{0, 1}, {0, 1}, {0, 1}, {1, 1},
{0, 1}, {0, 1}, {0, 1}, {0, 1},
};
static void __init armada_370_xp_get_clk_ratio(u32 opt,
void __iomem *sar, int id, int *mult, int *div)
{
switch (id) {
case A370_XP_NBCLK:
*mult = armada_370_xp_nbclk_ratios[opt][0];
*div = armada_370_xp_nbclk_ratios[opt][1];
break;
case A370_XP_HCLK:
*mult = armada_370_xp_hclk_ratios[opt][0];
*div = armada_370_xp_hclk_ratios[opt][1];
break;
case A370_XP_DRAMCLK:
*mult = armada_370_xp_dramclk_ratios[opt][0];
*div = armada_370_xp_dramclk_ratios[opt][1];
break;
}
}
static void __init armada_370_get_clk_ratio(
void __iomem *sar, int id, int *mult, int *div)
{
u32 opt = ((readl(sar) >> SARL_A370_FAB_FREQ_OPT) &
SARL_A370_FAB_FREQ_OPT_MASK);
armada_370_xp_get_clk_ratio(opt, sar, id, mult, div);
}
static const struct core_clocks armada_370_core_clocks = {
.get_tclk_freq = armada_370_get_tclk_freq,
.get_cpu_freq = armada_370_get_cpu_freq,
.get_clk_ratio = armada_370_get_clk_ratio,
.ratios = armada_370_xp_core_ratios,
.num_ratios = ARRAY_SIZE(armada_370_xp_core_ratios),
};
static const u32 __initconst armada_xp_cpu_frequencies[] = {
1000000000,
1066000000,
1200000000,
1333000000,
1500000000,
1666000000,
1800000000,
2000000000,
667000000,
0,
800000000,
1600000000,
};
/* For Armada XP TCLK frequency is fix: 250MHz */
static u32 __init armada_xp_get_tclk_freq(void __iomem *sar)
{
return 250 * 1000 * 1000;
}
static u32 __init armada_xp_get_cpu_freq(void __iomem *sar)
{
u32 cpu_freq;
u8 cpu_freq_select = 0;
cpu_freq_select = ((readl(sar) >> SARL_AXP_PCLK_FREQ_OPT) &
SARL_AXP_PCLK_FREQ_OPT_MASK);
/*
* The upper bit is not contiguous to the other ones and
* located in the high part of the SAR registers
*/
cpu_freq_select |= (((readl(sar+4) >> SARH_AXP_PCLK_FREQ_OPT) &
SARH_AXP_PCLK_FREQ_OPT_MASK)
<< SARH_AXP_PCLK_FREQ_OPT_SHIFT);
if (cpu_freq_select > ARRAY_SIZE(armada_xp_cpu_frequencies)) {
pr_err("CPU freq select unsuported: %d\n", cpu_freq_select);
cpu_freq = 0;
} else
cpu_freq = armada_xp_cpu_frequencies[cpu_freq_select];
return cpu_freq;
}
static void __init armada_xp_get_clk_ratio(
void __iomem *sar, int id, int *mult, int *div)
{
u32 opt = ((readl(sar) >> SARL_AXP_FAB_FREQ_OPT) &
SARL_AXP_FAB_FREQ_OPT_MASK);
/*
* The upper bit is not contiguous to the other ones and
* located in the high part of the SAR registers
*/
opt |= (((readl(sar+4) >> SARH_AXP_FAB_FREQ_OPT) &
SARH_AXP_FAB_FREQ_OPT_MASK)
<< SARH_AXP_FAB_FREQ_OPT_SHIFT);
armada_370_xp_get_clk_ratio(opt, sar, id, mult, div);
}
static const struct core_clocks armada_xp_core_clocks = {
.get_tclk_freq = armada_xp_get_tclk_freq,
.get_cpu_freq = armada_xp_get_cpu_freq,
.get_clk_ratio = armada_xp_get_clk_ratio,
.ratios = armada_370_xp_core_ratios,
.num_ratios = ARRAY_SIZE(armada_370_xp_core_ratios),
};
#endif /* CONFIG_MACH_ARMADA_370_XP */
/*
* Dove PLL sample-at-reset configuration
*
* SAR0[8:5] : CPU frequency
* 5 = 1000 MHz
* 6 = 933 MHz
* 7 = 933 MHz
* 8 = 800 MHz
* 9 = 800 MHz
* 10 = 800 MHz
* 11 = 1067 MHz
* 12 = 667 MHz
* 13 = 533 MHz
* 14 = 400 MHz
* 15 = 333 MHz
* others reserved.
*
* SAR0[11:9] : CPU to L2 Clock divider ratio
* 0 = (1/1) * CPU
* 2 = (1/2) * CPU
* 4 = (1/3) * CPU
* 6 = (1/4) * CPU
* others reserved.
*
* SAR0[15:12] : CPU to DDR DRAM Clock divider ratio
* 0 = (1/1) * CPU
* 2 = (1/2) * CPU
* 3 = (2/5) * CPU
* 4 = (1/3) * CPU
* 6 = (1/4) * CPU
* 8 = (1/5) * CPU
* 10 = (1/6) * CPU
* 12 = (1/7) * CPU
* 14 = (1/8) * CPU
* 15 = (1/10) * CPU
* others reserved.
*
* SAR0[24:23] : TCLK frequency
* 0 = 166 MHz
* 1 = 125 MHz
* others reserved.
*/
#ifdef CONFIG_ARCH_DOVE
#define SAR_DOVE_CPU_FREQ 5
#define SAR_DOVE_CPU_FREQ_MASK 0xf
#define SAR_DOVE_L2_RATIO 9
#define SAR_DOVE_L2_RATIO_MASK 0x7
#define SAR_DOVE_DDR_RATIO 12
#define SAR_DOVE_DDR_RATIO_MASK 0xf
#define SAR_DOVE_TCLK_FREQ 23
#define SAR_DOVE_TCLK_FREQ_MASK 0x3
static const u32 __initconst dove_tclk_frequencies[] = {
166666667,
125000000,
0, 0
};
static u32 __init dove_get_tclk_freq(void __iomem *sar)
{
u32 opt = (readl(sar) >> SAR_DOVE_TCLK_FREQ) &
SAR_DOVE_TCLK_FREQ_MASK;
return dove_tclk_frequencies[opt];
}
static const u32 __initconst dove_cpu_frequencies[] = {
0, 0, 0, 0, 0,
1000000000,
933333333, 933333333,
800000000, 800000000, 800000000,
1066666667,
666666667,
533333333,
400000000,
333333333
};
static u32 __init dove_get_cpu_freq(void __iomem *sar)
{
u32 opt = (readl(sar) >> SAR_DOVE_CPU_FREQ) &
SAR_DOVE_CPU_FREQ_MASK;
return dove_cpu_frequencies[opt];
}
enum { DOVE_CPU_TO_L2, DOVE_CPU_TO_DDR };
static const struct core_ratio __initconst dove_core_ratios[] = {
{ .id = DOVE_CPU_TO_L2, .name = "l2clk", },
{ .id = DOVE_CPU_TO_DDR, .name = "ddrclk", }
};
static const int __initconst dove_cpu_l2_ratios[8][2] = {
{ 1, 1 }, { 0, 1 }, { 1, 2 }, { 0, 1 },
{ 1, 3 }, { 0, 1 }, { 1, 4 }, { 0, 1 }
};
static const int __initconst dove_cpu_ddr_ratios[16][2] = {
{ 1, 1 }, { 0, 1 }, { 1, 2 }, { 2, 5 },
{ 1, 3 }, { 0, 1 }, { 1, 4 }, { 0, 1 },
{ 1, 5 }, { 0, 1 }, { 1, 6 }, { 0, 1 },
{ 1, 7 }, { 0, 1 }, { 1, 8 }, { 1, 10 }
};
static void __init dove_get_clk_ratio(
void __iomem *sar, int id, int *mult, int *div)
{
switch (id) {
case DOVE_CPU_TO_L2:
{
u32 opt = (readl(sar) >> SAR_DOVE_L2_RATIO) &
SAR_DOVE_L2_RATIO_MASK;
*mult = dove_cpu_l2_ratios[opt][0];
*div = dove_cpu_l2_ratios[opt][1];
break;
}
case DOVE_CPU_TO_DDR:
{
u32 opt = (readl(sar) >> SAR_DOVE_DDR_RATIO) &
SAR_DOVE_DDR_RATIO_MASK;
*mult = dove_cpu_ddr_ratios[opt][0];
*div = dove_cpu_ddr_ratios[opt][1];
break;
}
}
}
static const struct core_clocks dove_core_clocks = {
.get_tclk_freq = dove_get_tclk_freq,
.get_cpu_freq = dove_get_cpu_freq,
.get_clk_ratio = dove_get_clk_ratio,
.ratios = dove_core_ratios,
.num_ratios = ARRAY_SIZE(dove_core_ratios),
};
#endif /* CONFIG_ARCH_DOVE */
/*
* Kirkwood PLL sample-at-reset configuration
* (6180 has different SAR layout than other Kirkwood SoCs)
*
* SAR0[4:3,22,1] : CPU frequency (6281,6292,6282)
* 4 = 600 MHz
* 6 = 800 MHz
* 7 = 1000 MHz
* 9 = 1200 MHz
* 12 = 1500 MHz
* 13 = 1600 MHz
* 14 = 1800 MHz
* 15 = 2000 MHz
* others reserved.
*
* SAR0[19,10:9] : CPU to L2 Clock divider ratio (6281,6292,6282)
* 1 = (1/2) * CPU
* 3 = (1/3) * CPU
* 5 = (1/4) * CPU
* others reserved.
*
* SAR0[8:5] : CPU to DDR DRAM Clock divider ratio (6281,6292,6282)
* 2 = (1/2) * CPU
* 4 = (1/3) * CPU
* 6 = (1/4) * CPU
* 7 = (2/9) * CPU
* 8 = (1/5) * CPU
* 9 = (1/6) * CPU
* others reserved.
*
* SAR0[4:2] : Kirkwood 6180 cpu/l2/ddr clock configuration (6180 only)
* 5 = [CPU = 600 MHz, L2 = (1/2) * CPU, DDR = 200 MHz = (1/3) * CPU]
* 6 = [CPU = 800 MHz, L2 = (1/2) * CPU, DDR = 200 MHz = (1/4) * CPU]
* 7 = [CPU = 1000 MHz, L2 = (1/2) * CPU, DDR = 200 MHz = (1/5) * CPU]
* others reserved.
*
* SAR0[21] : TCLK frequency
* 0 = 200 MHz
* 1 = 166 MHz
* others reserved.
*/
#ifdef CONFIG_ARCH_KIRKWOOD
#define SAR_KIRKWOOD_CPU_FREQ(x) \
(((x & (1 << 1)) >> 1) | \
((x & (1 << 22)) >> 21) | \
((x & (3 << 3)) >> 1))
#define SAR_KIRKWOOD_L2_RATIO(x) \
(((x & (3 << 9)) >> 9) | \
(((x & (1 << 19)) >> 17)))
#define SAR_KIRKWOOD_DDR_RATIO 5
#define SAR_KIRKWOOD_DDR_RATIO_MASK 0xf
#define SAR_MV88F6180_CLK 2
#define SAR_MV88F6180_CLK_MASK 0x7
#define SAR_KIRKWOOD_TCLK_FREQ 21
#define SAR_KIRKWOOD_TCLK_FREQ_MASK 0x1
enum { KIRKWOOD_CPU_TO_L2, KIRKWOOD_CPU_TO_DDR };
static const struct core_ratio __initconst kirkwood_core_ratios[] = {
{ .id = KIRKWOOD_CPU_TO_L2, .name = "l2clk", },
{ .id = KIRKWOOD_CPU_TO_DDR, .name = "ddrclk", }
};
static u32 __init kirkwood_get_tclk_freq(void __iomem *sar)
{
u32 opt = (readl(sar) >> SAR_KIRKWOOD_TCLK_FREQ) &
SAR_KIRKWOOD_TCLK_FREQ_MASK;
return (opt) ? 166666667 : 200000000;
}
static const u32 __initconst kirkwood_cpu_frequencies[] = {
0, 0, 0, 0,
600000000,
0,
800000000,
1000000000,
0,
1200000000,
0, 0,
1500000000,
1600000000,
1800000000,
2000000000
};
static u32 __init kirkwood_get_cpu_freq(void __iomem *sar)
{
u32 opt = SAR_KIRKWOOD_CPU_FREQ(readl(sar));
return kirkwood_cpu_frequencies[opt];
}
static const int __initconst kirkwood_cpu_l2_ratios[8][2] = {
{ 0, 1 }, { 1, 2 }, { 0, 1 }, { 1, 3 },
{ 0, 1 }, { 1, 4 }, { 0, 1 }, { 0, 1 }
};
static const int __initconst kirkwood_cpu_ddr_ratios[16][2] = {
{ 0, 1 }, { 0, 1 }, { 1, 2 }, { 0, 1 },
{ 1, 3 }, { 0, 1 }, { 1, 4 }, { 2, 9 },
{ 1, 5 }, { 1, 6 }, { 0, 1 }, { 0, 1 },
{ 0, 1 }, { 0, 1 }, { 0, 1 }, { 0, 1 }
};
static void __init kirkwood_get_clk_ratio(
void __iomem *sar, int id, int *mult, int *div)
{
switch (id) {
case KIRKWOOD_CPU_TO_L2:
{
u32 opt = SAR_KIRKWOOD_L2_RATIO(readl(sar));
*mult = kirkwood_cpu_l2_ratios[opt][0];
*div = kirkwood_cpu_l2_ratios[opt][1];
break;
}
case KIRKWOOD_CPU_TO_DDR:
{
u32 opt = (readl(sar) >> SAR_KIRKWOOD_DDR_RATIO) &
SAR_KIRKWOOD_DDR_RATIO_MASK;
*mult = kirkwood_cpu_ddr_ratios[opt][0];
*div = kirkwood_cpu_ddr_ratios[opt][1];
break;
}
}
}
static const struct core_clocks kirkwood_core_clocks = {
.get_tclk_freq = kirkwood_get_tclk_freq,
.get_cpu_freq = kirkwood_get_cpu_freq,
.get_clk_ratio = kirkwood_get_clk_ratio,
.ratios = kirkwood_core_ratios,
.num_ratios = ARRAY_SIZE(kirkwood_core_ratios),
};
static const u32 __initconst mv88f6180_cpu_frequencies[] = {
0, 0, 0, 0, 0,
600000000,
800000000,
1000000000
};
static u32 __init mv88f6180_get_cpu_freq(void __iomem *sar)
{
u32 opt = (readl(sar) >> SAR_MV88F6180_CLK) & SAR_MV88F6180_CLK_MASK;
return mv88f6180_cpu_frequencies[opt];
}
static const int __initconst mv88f6180_cpu_ddr_ratios[8][2] = {
{ 0, 1 }, { 0, 1 }, { 0, 1 }, { 0, 1 },
{ 0, 1 }, { 1, 3 }, { 1, 4 }, { 1, 5 }
};
static void __init mv88f6180_get_clk_ratio(
void __iomem *sar, int id, int *mult, int *div)
{
switch (id) {
case KIRKWOOD_CPU_TO_L2:
{
/* mv88f6180 has a fixed 1:2 CPU-to-L2 ratio */
*mult = 1;
*div = 2;
break;
}
case KIRKWOOD_CPU_TO_DDR:
{
u32 opt = (readl(sar) >> SAR_MV88F6180_CLK) &
SAR_MV88F6180_CLK_MASK;
*mult = mv88f6180_cpu_ddr_ratios[opt][0];
*div = mv88f6180_cpu_ddr_ratios[opt][1];
break;
}
}
}
static const struct core_clocks mv88f6180_core_clocks = {
.get_tclk_freq = kirkwood_get_tclk_freq,
.get_cpu_freq = mv88f6180_get_cpu_freq,
.get_clk_ratio = mv88f6180_get_clk_ratio,
.ratios = kirkwood_core_ratios,
.num_ratios = ARRAY_SIZE(kirkwood_core_ratios),
};
#endif /* CONFIG_ARCH_KIRKWOOD */
static const __initdata struct of_device_id clk_core_match[] = {
#ifdef CONFIG_MACH_ARMADA_370_XP
{
.compatible = "marvell,armada-370-core-clock",
.data = &armada_370_core_clocks,
},
{
.compatible = "marvell,armada-xp-core-clock",
.data = &armada_xp_core_clocks,
},
#endif
#ifdef CONFIG_ARCH_DOVE
{
.compatible = "marvell,dove-core-clock",
.data = &dove_core_clocks,
},
#endif
#ifdef CONFIG_ARCH_KIRKWOOD
{
.compatible = "marvell,kirkwood-core-clock",
.data = &kirkwood_core_clocks,
},
{
.compatible = "marvell,mv88f6180-core-clock",
.data = &mv88f6180_core_clocks,
},
#endif
{ }
};
void __init mvebu_core_clk_init(void)
{
struct device_node *np;
for_each_matching_node(np, clk_core_match) {
const struct of_device_id *match =
of_match_node(clk_core_match, np);
mvebu_clk_core_setup(np, (struct core_clocks *)match->data);
}
}

View File

@ -0,0 +1,18 @@
/*
* * Marvell EBU clock core handling defined at reset
*
* Copyright (C) 2012 Marvell
*
* Gregory CLEMENT <gregory.clement@free-electrons.com>
*
* This file is licensed under the terms of the GNU General Public
* License version 2. This program is licensed "as is" without any
* warranty of any kind, whether express or implied.
*/
#ifndef __MVEBU_CLK_CORE_H
#define __MVEBU_CLK_CORE_H
void __init mvebu_core_clk_init(void);
#endif

186
drivers/clk/mvebu/clk-cpu.c Normal file
View File

@ -0,0 +1,186 @@
/*
* Marvell MVEBU CPU clock handling.
*
* Copyright (C) 2012 Marvell
*
* Gregory CLEMENT <gregory.clement@free-electrons.com>
*
* This file is licensed under the terms of the GNU General Public
* License version 2. This program is licensed "as is" without any
* warranty of any kind, whether express or implied.
*/
#include <linux/kernel.h>
#include <linux/clkdev.h>
#include <linux/clk-provider.h>
#include <linux/of_address.h>
#include <linux/io.h>
#include <linux/of.h>
#include <linux/delay.h>
#include "clk-cpu.h"
#define SYS_CTRL_CLK_DIVIDER_CTRL_OFFSET 0x0
#define SYS_CTRL_CLK_DIVIDER_VALUE_OFFSET 0xC
#define SYS_CTRL_CLK_DIVIDER_MASK 0x3F
#define MAX_CPU 4
struct cpu_clk {
struct clk_hw hw;
int cpu;
const char *clk_name;
const char *parent_name;
void __iomem *reg_base;
};
static struct clk **clks;
static struct clk_onecell_data clk_data;
#define to_cpu_clk(p) container_of(p, struct cpu_clk, hw)
static unsigned long clk_cpu_recalc_rate(struct clk_hw *hwclk,
unsigned long parent_rate)
{
struct cpu_clk *cpuclk = to_cpu_clk(hwclk);
u32 reg, div;
reg = readl(cpuclk->reg_base + SYS_CTRL_CLK_DIVIDER_VALUE_OFFSET);
div = (reg >> (cpuclk->cpu * 8)) & SYS_CTRL_CLK_DIVIDER_MASK;
return parent_rate / div;
}
static long clk_cpu_round_rate(struct clk_hw *hwclk, unsigned long rate,
unsigned long *parent_rate)
{
/* Valid ratio are 1:1, 1:2 and 1:3 */
u32 div;
div = *parent_rate / rate;
if (div == 0)
div = 1;
else if (div > 3)
div = 3;
return *parent_rate / div;
}
static int clk_cpu_set_rate(struct clk_hw *hwclk, unsigned long rate,
unsigned long parent_rate)
{
struct cpu_clk *cpuclk = to_cpu_clk(hwclk);
u32 reg, div;
u32 reload_mask;
div = parent_rate / rate;
reg = (readl(cpuclk->reg_base + SYS_CTRL_CLK_DIVIDER_VALUE_OFFSET)
& (~(SYS_CTRL_CLK_DIVIDER_MASK << (cpuclk->cpu * 8))))
| (div << (cpuclk->cpu * 8));
writel(reg, cpuclk->reg_base + SYS_CTRL_CLK_DIVIDER_VALUE_OFFSET);
/* Set clock divider reload smooth bit mask */
reload_mask = 1 << (20 + cpuclk->cpu);
reg = readl(cpuclk->reg_base + SYS_CTRL_CLK_DIVIDER_CTRL_OFFSET)
| reload_mask;
writel(reg, cpuclk->reg_base + SYS_CTRL_CLK_DIVIDER_CTRL_OFFSET);
/* Now trigger the clock update */
reg = readl(cpuclk->reg_base + SYS_CTRL_CLK_DIVIDER_CTRL_OFFSET)
| 1 << 24;
writel(reg, cpuclk->reg_base + SYS_CTRL_CLK_DIVIDER_CTRL_OFFSET);
/* Wait for clocks to settle down then clear reload request */
udelay(1000);
reg &= ~(reload_mask | 1 << 24);
writel(reg, cpuclk->reg_base + SYS_CTRL_CLK_DIVIDER_CTRL_OFFSET);
udelay(1000);
return 0;
}
static const struct clk_ops cpu_ops = {
.recalc_rate = clk_cpu_recalc_rate,
.round_rate = clk_cpu_round_rate,
.set_rate = clk_cpu_set_rate,
};
void __init of_cpu_clk_setup(struct device_node *node)
{
struct cpu_clk *cpuclk;
void __iomem *clock_complex_base = of_iomap(node, 0);
int ncpus = 0;
struct device_node *dn;
if (clock_complex_base == NULL) {
pr_err("%s: clock-complex base register not set\n",
__func__);
return;
}
for_each_node_by_type(dn, "cpu")
ncpus++;
cpuclk = kzalloc(ncpus * sizeof(*cpuclk), GFP_KERNEL);
if (WARN_ON(!cpuclk))
return;
clks = kzalloc(ncpus * sizeof(*clks), GFP_KERNEL);
if (WARN_ON(!clks))
return;
for_each_node_by_type(dn, "cpu") {
struct clk_init_data init;
struct clk *clk;
struct clk *parent_clk;
char *clk_name = kzalloc(5, GFP_KERNEL);
int cpu, err;
if (WARN_ON(!clk_name))
return;
err = of_property_read_u32(dn, "reg", &cpu);
if (WARN_ON(err))
return;
sprintf(clk_name, "cpu%d", cpu);
parent_clk = of_clk_get(node, 0);
cpuclk[cpu].parent_name = __clk_get_name(parent_clk);
cpuclk[cpu].clk_name = clk_name;
cpuclk[cpu].cpu = cpu;
cpuclk[cpu].reg_base = clock_complex_base;
cpuclk[cpu].hw.init = &init;
init.name = cpuclk[cpu].clk_name;
init.ops = &cpu_ops;
init.flags = 0;
init.parent_names = &cpuclk[cpu].parent_name;
init.num_parents = 1;
clk = clk_register(NULL, &cpuclk[cpu].hw);
if (WARN_ON(IS_ERR(clk)))
goto bail_out;
clks[cpu] = clk;
}
clk_data.clk_num = MAX_CPU;
clk_data.clks = clks;
of_clk_add_provider(node, of_clk_src_onecell_get, &clk_data);
return;
bail_out:
kfree(clks);
kfree(cpuclk);
}
static const __initconst struct of_device_id clk_cpu_match[] = {
{
.compatible = "marvell,armada-xp-cpu-clock",
.data = of_cpu_clk_setup,
},
{
/* sentinel */
},
};
void __init mvebu_cpu_clk_init(void)
{
of_clk_init(clk_cpu_match);
}

View File

@ -0,0 +1,22 @@
/*
* Marvell MVEBU CPU clock handling.
*
* Copyright (C) 2012 Marvell
*
* Gregory CLEMENT <gregory.clement@free-electrons.com>
*
* This file is licensed under the terms of the GNU General Public
* License version 2. This program is licensed "as is" without any
* warranty of any kind, whether express or implied.
*/
#ifndef __MVEBU_CLK_CPU_H
#define __MVEBU_CLK_CPU_H
#ifdef CONFIG_MVEBU_CLK_CPU
void __init mvebu_cpu_clk_init(void);
#else
static inline void mvebu_cpu_clk_init(void) {}
#endif
#endif

View File

@ -0,0 +1,249 @@
/*
* Marvell MVEBU clock gating control.
*
* Sebastian Hesselbarth <sebastian.hesselbarth@gmail.com>
* Andrew Lunn <andrew@lunn.ch>
*
* This file is licensed under the terms of the GNU General Public
* License version 2. This program is licensed "as is" without any
* warranty of any kind, whether express or implied.
*/
#include <linux/kernel.h>
#include <linux/bitops.h>
#include <linux/io.h>
#include <linux/clk.h>
#include <linux/clkdev.h>
#include <linux/clk-provider.h>
#include <linux/clk/mvebu.h>
#include <linux/of.h>
#include <linux/of_address.h>
struct mvebu_gating_ctrl {
spinlock_t lock;
struct clk **gates;
int num_gates;
};
struct mvebu_soc_descr {
const char *name;
const char *parent;
int bit_idx;
};
#define to_clk_gate(_hw) container_of(_hw, struct clk_gate, hw)
static struct clk __init *mvebu_clk_gating_get_src(
struct of_phandle_args *clkspec, void *data)
{
struct mvebu_gating_ctrl *ctrl = (struct mvebu_gating_ctrl *)data;
int n;
if (clkspec->args_count < 1)
return ERR_PTR(-EINVAL);
for (n = 0; n < ctrl->num_gates; n++) {
struct clk_gate *gate =
to_clk_gate(__clk_get_hw(ctrl->gates[n]));
if (clkspec->args[0] == gate->bit_idx)
return ctrl->gates[n];
}
return ERR_PTR(-ENODEV);
}
static void __init mvebu_clk_gating_setup(
struct device_node *np, const struct mvebu_soc_descr *descr)
{
struct mvebu_gating_ctrl *ctrl;
struct clk *clk;
void __iomem *base;
const char *default_parent = NULL;
int n;
base = of_iomap(np, 0);
clk = of_clk_get(np, 0);
if (!IS_ERR(clk)) {
default_parent = __clk_get_name(clk);
clk_put(clk);
}
ctrl = kzalloc(sizeof(struct mvebu_gating_ctrl), GFP_KERNEL);
if (WARN_ON(!ctrl))
return;
spin_lock_init(&ctrl->lock);
/*
* Count, allocate, and register clock gates
*/
for (n = 0; descr[n].name;)
n++;
ctrl->num_gates = n;
ctrl->gates = kzalloc(ctrl->num_gates * sizeof(struct clk *),
GFP_KERNEL);
if (WARN_ON(!ctrl->gates)) {
kfree(ctrl);
return;
}
for (n = 0; n < ctrl->num_gates; n++) {
u8 flags = 0;
const char *parent =
(descr[n].parent) ? descr[n].parent : default_parent;
/*
* On Armada 370, the DDR clock is a special case: it
* isn't taken by any driver, but should anyway be
* kept enabled, so we mark it as IGNORE_UNUSED for
* now.
*/
if (!strcmp(descr[n].name, "ddr"))
flags |= CLK_IGNORE_UNUSED;
ctrl->gates[n] = clk_register_gate(NULL, descr[n].name, parent,
flags, base, descr[n].bit_idx, 0, &ctrl->lock);
WARN_ON(IS_ERR(ctrl->gates[n]));
}
of_clk_add_provider(np, mvebu_clk_gating_get_src, ctrl);
}
/*
* SoC specific clock gating control
*/
#ifdef CONFIG_MACH_ARMADA_370
static const struct mvebu_soc_descr __initconst armada_370_gating_descr[] = {
{ "audio", NULL, 0 },
{ "pex0_en", NULL, 1 },
{ "pex1_en", NULL, 2 },
{ "ge1", NULL, 3 },
{ "ge0", NULL, 4 },
{ "pex0", NULL, 5 },
{ "pex1", NULL, 9 },
{ "sata0", NULL, 15 },
{ "sdio", NULL, 17 },
{ "tdm", NULL, 25 },
{ "ddr", NULL, 28 },
{ "sata1", NULL, 30 },
{ }
};
#endif
#ifdef CONFIG_MACH_ARMADA_XP
static const struct mvebu_soc_descr __initconst armada_xp_gating_descr[] = {
{ "audio", NULL, 0 },
{ "ge3", NULL, 1 },
{ "ge2", NULL, 2 },
{ "ge1", NULL, 3 },
{ "ge0", NULL, 4 },
{ "pex0", NULL, 5 },
{ "pex1", NULL, 6 },
{ "pex2", NULL, 7 },
{ "pex3", NULL, 8 },
{ "bp", NULL, 13 },
{ "sata0lnk", NULL, 14 },
{ "sata0", "sata0lnk", 15 },
{ "lcd", NULL, 16 },
{ "sdio", NULL, 17 },
{ "usb0", NULL, 18 },
{ "usb1", NULL, 19 },
{ "usb2", NULL, 20 },
{ "xor0", NULL, 22 },
{ "crypto", NULL, 23 },
{ "tdm", NULL, 25 },
{ "xor1", NULL, 28 },
{ "sata1lnk", NULL, 29 },
{ "sata1", "sata1lnk", 30 },
{ }
};
#endif
#ifdef CONFIG_ARCH_DOVE
static const struct mvebu_soc_descr __initconst dove_gating_descr[] = {
{ "usb0", NULL, 0 },
{ "usb1", NULL, 1 },
{ "ge", "gephy", 2 },
{ "sata", NULL, 3 },
{ "pex0", NULL, 4 },
{ "pex1", NULL, 5 },
{ "sdio0", NULL, 8 },
{ "sdio1", NULL, 9 },
{ "nand", NULL, 10 },
{ "camera", NULL, 11 },
{ "i2s0", NULL, 12 },
{ "i2s1", NULL, 13 },
{ "crypto", NULL, 15 },
{ "ac97", NULL, 21 },
{ "pdma", NULL, 22 },
{ "xor0", NULL, 23 },
{ "xor1", NULL, 24 },
{ "gephy", NULL, 30 },
{ }
};
#endif
#ifdef CONFIG_ARCH_KIRKWOOD
static const struct mvebu_soc_descr __initconst kirkwood_gating_descr[] = {
{ "ge0", NULL, 0 },
{ "pex0", NULL, 2 },
{ "usb0", NULL, 3 },
{ "sdio", NULL, 4 },
{ "tsu", NULL, 5 },
{ "runit", NULL, 7 },
{ "xor0", NULL, 8 },
{ "audio", NULL, 9 },
{ "sata0", NULL, 14 },
{ "sata1", NULL, 15 },
{ "xor1", NULL, 16 },
{ "crypto", NULL, 17 },
{ "pex1", NULL, 18 },
{ "ge1", NULL, 19 },
{ "tdm", NULL, 20 },
{ }
};
#endif
static const __initdata struct of_device_id clk_gating_match[] = {
#ifdef CONFIG_MACH_ARMADA_370
{
.compatible = "marvell,armada-370-gating-clock",
.data = armada_370_gating_descr,
},
#endif
#ifdef CONFIG_MACH_ARMADA_XP
{
.compatible = "marvell,armada-xp-gating-clock",
.data = armada_xp_gating_descr,
},
#endif
#ifdef CONFIG_ARCH_DOVE
{
.compatible = "marvell,dove-gating-clock",
.data = dove_gating_descr,
},
#endif
#ifdef CONFIG_ARCH_KIRKWOOD
{
.compatible = "marvell,kirkwood-gating-clock",
.data = kirkwood_gating_descr,
},
#endif
{ }
};
void __init mvebu_gating_clk_init(void)
{
struct device_node *np;
for_each_matching_node(np, clk_gating_match) {
const struct of_device_id *match =
of_match_node(clk_gating_match, np);
mvebu_clk_gating_setup(np,
(const struct mvebu_soc_descr *)match->data);
}
}

View File

@ -0,0 +1,22 @@
/*
* Marvell EBU gating clock handling
*
* Copyright (C) 2012 Marvell
*
* Thomas Petazzoni <thomas.petazzoni@free-electrons.com>
*
* This file is licensed under the terms of the GNU General Public
* License version 2. This program is licensed "as is" without any
* warranty of any kind, whether express or implied.
*/
#ifndef __MVEBU_CLK_GATING_H
#define __MVEBU_CLK_GATING_H
#ifdef CONFIG_MVEBU_CLK_GATING
void __init mvebu_gating_clk_init(void);
#else
void mvebu_gating_clk_init(void) {}
#endif
#endif

27
drivers/clk/mvebu/clk.c Normal file
View File

@ -0,0 +1,27 @@
/*
* Marvell EBU SoC clock handling.
*
* Copyright (C) 2012 Marvell
*
* Gregory CLEMENT <gregory.clement@free-electrons.com>
*
* This file is licensed under the terms of the GNU General Public
* License version 2. This program is licensed "as is" without any
* warranty of any kind, whether express or implied.
*/
#include <linux/kernel.h>
#include <linux/clk.h>
#include <linux/clk-provider.h>
#include <linux/of_address.h>
#include <linux/clk/mvebu.h>
#include <linux/of.h>
#include "clk-core.h"
#include "clk-cpu.h"
#include "clk-gating-ctrl.h"
void __init mvebu_clocks_init(void)
{
mvebu_core_clk_init();
mvebu_gating_clk_init();
mvebu_cpu_clk_init();
}

View File

@ -18,6 +18,7 @@
#include <linux/init.h>
#include <linux/platform_device.h>
#include <linux/kernel.h>
#include <linux/clk.h>
#include <linux/timer.h>
#include <linux/clockchips.h>
#include <linux/interrupt.h>
@ -167,7 +168,6 @@ void __init armada_370_xp_timer_init(void)
u32 u;
struct device_node *np;
unsigned int timer_clk;
int ret;
np = of_find_compatible_node(NULL, NULL, "marvell,armada-370-xp-timer");
timer_base = of_iomap(np, 0);
WARN_ON(!timer_base);
@ -179,13 +179,14 @@ void __init armada_370_xp_timer_init(void)
timer_base + TIMER_CTRL_OFF);
timer_clk = 25000000;
} else {
u32 clk = 0;
ret = of_property_read_u32(np, "clock-frequency", &clk);
WARN_ON(!clk || ret < 0);
unsigned long rate = 0;
struct clk *clk = of_clk_get(np, 0);
WARN_ON(IS_ERR(clk));
rate = clk_get_rate(clk);
u = readl(timer_base + TIMER_CTRL_OFF);
writel(u & ~(TIMER0_25MHZ | TIMER1_25MHZ),
timer_base + TIMER_CTRL_OFF);
timer_clk = clk / TIMER_DIVIDER;
timer_clk = rate / TIMER_DIVIDER;
}
/* We use timer 0 as clocksource, and timer 1 for

View File

@ -26,6 +26,9 @@
#include <linux/platform_device.h>
#include <linux/memory.h>
#include <linux/clk.h>
#include <linux/of.h>
#include <linux/of_irq.h>
#include <linux/irqdomain.h>
#include <linux/platform_data/dma-mv_xor.h>
#include "dmaengine.h"
@ -34,14 +37,14 @@
static void mv_xor_issue_pending(struct dma_chan *chan);
#define to_mv_xor_chan(chan) \
container_of(chan, struct mv_xor_chan, common)
#define to_mv_xor_device(dev) \
container_of(dev, struct mv_xor_device, common)
container_of(chan, struct mv_xor_chan, dmachan)
#define to_mv_xor_slot(tx) \
container_of(tx, struct mv_xor_desc_slot, async_tx)
#define mv_chan_to_devp(chan) \
((chan)->dmadev.dev)
static void mv_desc_init(struct mv_xor_desc_slot *desc, unsigned long flags)
{
struct mv_xor_desc *hw_desc = desc->hw_desc;
@ -166,7 +169,7 @@ static int mv_is_err_intr(u32 intr_cause)
static void mv_xor_device_clear_eoc_cause(struct mv_xor_chan *chan)
{
u32 val = ~(1 << (chan->idx * 16));
dev_dbg(chan->device->common.dev, "%s, val 0x%08x\n", __func__, val);
dev_dbg(mv_chan_to_devp(chan), "%s, val 0x%08x\n", __func__, val);
__raw_writel(val, XOR_INTR_CAUSE(chan));
}
@ -206,9 +209,9 @@ static void mv_set_mode(struct mv_xor_chan *chan,
op_mode = XOR_OPERATION_MODE_MEMSET;
break;
default:
dev_printk(KERN_ERR, chan->device->common.dev,
"error: unsupported operation %d.\n",
type);
dev_err(mv_chan_to_devp(chan),
"error: unsupported operation %d.\n",
type);
BUG();
return;
}
@ -223,7 +226,7 @@ static void mv_chan_activate(struct mv_xor_chan *chan)
{
u32 activation;
dev_dbg(chan->device->common.dev, " activate chan.\n");
dev_dbg(mv_chan_to_devp(chan), " activate chan.\n");
activation = __raw_readl(XOR_ACTIVATION(chan));
activation |= 0x1;
__raw_writel(activation, XOR_ACTIVATION(chan));
@ -251,7 +254,7 @@ static int mv_chan_xor_slot_count(size_t len, int src_cnt)
static void mv_xor_free_slots(struct mv_xor_chan *mv_chan,
struct mv_xor_desc_slot *slot)
{
dev_dbg(mv_chan->device->common.dev, "%s %d slot %p\n",
dev_dbg(mv_chan_to_devp(mv_chan), "%s %d slot %p\n",
__func__, __LINE__, slot);
slot->slots_per_op = 0;
@ -266,7 +269,7 @@ static void mv_xor_free_slots(struct mv_xor_chan *mv_chan,
static void mv_xor_start_new_chain(struct mv_xor_chan *mv_chan,
struct mv_xor_desc_slot *sw_desc)
{
dev_dbg(mv_chan->device->common.dev, "%s %d: sw_desc %p\n",
dev_dbg(mv_chan_to_devp(mv_chan), "%s %d: sw_desc %p\n",
__func__, __LINE__, sw_desc);
if (sw_desc->type != mv_chan->current_type)
mv_set_mode(mv_chan, sw_desc->type);
@ -284,7 +287,7 @@ static void mv_xor_start_new_chain(struct mv_xor_chan *mv_chan,
mv_chan_set_next_descriptor(mv_chan, sw_desc->async_tx.phys);
}
mv_chan->pending += sw_desc->slot_cnt;
mv_xor_issue_pending(&mv_chan->common);
mv_xor_issue_pending(&mv_chan->dmachan);
}
static dma_cookie_t
@ -308,8 +311,7 @@ mv_xor_run_tx_complete_actions(struct mv_xor_desc_slot *desc,
*/
if (desc->group_head && desc->unmap_len) {
struct mv_xor_desc_slot *unmap = desc->group_head;
struct device *dev =
&mv_chan->device->pdev->dev;
struct device *dev = mv_chan_to_devp(mv_chan);
u32 len = unmap->unmap_len;
enum dma_ctrl_flags flags = desc->async_tx.flags;
u32 src_cnt;
@ -353,7 +355,7 @@ mv_xor_clean_completed_slots(struct mv_xor_chan *mv_chan)
{
struct mv_xor_desc_slot *iter, *_iter;
dev_dbg(mv_chan->device->common.dev, "%s %d\n", __func__, __LINE__);
dev_dbg(mv_chan_to_devp(mv_chan), "%s %d\n", __func__, __LINE__);
list_for_each_entry_safe(iter, _iter, &mv_chan->completed_slots,
completed_node) {
@ -369,7 +371,7 @@ static int
mv_xor_clean_slot(struct mv_xor_desc_slot *desc,
struct mv_xor_chan *mv_chan)
{
dev_dbg(mv_chan->device->common.dev, "%s %d: desc %p flags %d\n",
dev_dbg(mv_chan_to_devp(mv_chan), "%s %d: desc %p flags %d\n",
__func__, __LINE__, desc, desc->async_tx.flags);
list_del(&desc->chain_node);
/* the client is allowed to attach dependent operations
@ -393,8 +395,8 @@ static void __mv_xor_slot_cleanup(struct mv_xor_chan *mv_chan)
u32 current_desc = mv_chan_get_current_desc(mv_chan);
int seen_current = 0;
dev_dbg(mv_chan->device->common.dev, "%s %d\n", __func__, __LINE__);
dev_dbg(mv_chan->device->common.dev, "current_desc %x\n", current_desc);
dev_dbg(mv_chan_to_devp(mv_chan), "%s %d\n", __func__, __LINE__);
dev_dbg(mv_chan_to_devp(mv_chan), "current_desc %x\n", current_desc);
mv_xor_clean_completed_slots(mv_chan);
/* free completed slots from the chain starting with
@ -438,7 +440,7 @@ static void __mv_xor_slot_cleanup(struct mv_xor_chan *mv_chan)
}
if (cookie > 0)
mv_chan->common.completed_cookie = cookie;
mv_chan->dmachan.completed_cookie = cookie;
}
static void
@ -547,7 +549,7 @@ mv_xor_tx_submit(struct dma_async_tx_descriptor *tx)
dma_cookie_t cookie;
int new_hw_chain = 1;
dev_dbg(mv_chan->device->common.dev,
dev_dbg(mv_chan_to_devp(mv_chan),
"%s sw_desc %p: async_tx %p\n",
__func__, sw_desc, &sw_desc->async_tx);
@ -570,7 +572,7 @@ mv_xor_tx_submit(struct dma_async_tx_descriptor *tx)
if (!mv_can_chain(grp_start))
goto submit_done;
dev_dbg(mv_chan->device->common.dev, "Append to last desc %x\n",
dev_dbg(mv_chan_to_devp(mv_chan), "Append to last desc %x\n",
old_chain_tail->async_tx.phys);
/* fix up the hardware chain */
@ -604,9 +606,7 @@ static int mv_xor_alloc_chan_resources(struct dma_chan *chan)
int idx;
struct mv_xor_chan *mv_chan = to_mv_xor_chan(chan);
struct mv_xor_desc_slot *slot = NULL;
struct mv_xor_platform_data *plat_data =
mv_chan->device->pdev->dev.platform_data;
int num_descs_in_pool = plat_data->pool_size/MV_XOR_SLOT_SIZE;
int num_descs_in_pool = MV_XOR_POOL_SIZE/MV_XOR_SLOT_SIZE;
/* Allocate descriptor slots */
idx = mv_chan->slots_allocated;
@ -617,7 +617,7 @@ static int mv_xor_alloc_chan_resources(struct dma_chan *chan)
" %d descriptor slots", idx);
break;
}
hw_desc = (char *) mv_chan->device->dma_desc_pool_virt;
hw_desc = (char *) mv_chan->dma_desc_pool_virt;
slot->hw_desc = (void *) &hw_desc[idx * MV_XOR_SLOT_SIZE];
dma_async_tx_descriptor_init(&slot->async_tx, chan);
@ -625,7 +625,7 @@ static int mv_xor_alloc_chan_resources(struct dma_chan *chan)
INIT_LIST_HEAD(&slot->chain_node);
INIT_LIST_HEAD(&slot->slot_node);
INIT_LIST_HEAD(&slot->tx_list);
hw_desc = (char *) mv_chan->device->dma_desc_pool;
hw_desc = (char *) mv_chan->dma_desc_pool;
slot->async_tx.phys =
(dma_addr_t) &hw_desc[idx * MV_XOR_SLOT_SIZE];
slot->idx = idx++;
@ -641,7 +641,7 @@ static int mv_xor_alloc_chan_resources(struct dma_chan *chan)
struct mv_xor_desc_slot,
slot_node);
dev_dbg(mv_chan->device->common.dev,
dev_dbg(mv_chan_to_devp(mv_chan),
"allocated %d descriptor slots last_used: %p\n",
mv_chan->slots_allocated, mv_chan->last_used);
@ -656,7 +656,7 @@ mv_xor_prep_dma_memcpy(struct dma_chan *chan, dma_addr_t dest, dma_addr_t src,
struct mv_xor_desc_slot *sw_desc, *grp_start;
int slot_cnt;
dev_dbg(mv_chan->device->common.dev,
dev_dbg(mv_chan_to_devp(mv_chan),
"%s dest: %x src %x len: %u flags: %ld\n",
__func__, dest, src, len, flags);
if (unlikely(len < MV_XOR_MIN_BYTE_COUNT))
@ -680,7 +680,7 @@ mv_xor_prep_dma_memcpy(struct dma_chan *chan, dma_addr_t dest, dma_addr_t src,
}
spin_unlock_bh(&mv_chan->lock);
dev_dbg(mv_chan->device->common.dev,
dev_dbg(mv_chan_to_devp(mv_chan),
"%s sw_desc %p async_tx %p\n",
__func__, sw_desc, sw_desc ? &sw_desc->async_tx : 0);
@ -695,7 +695,7 @@ mv_xor_prep_dma_memset(struct dma_chan *chan, dma_addr_t dest, int value,
struct mv_xor_desc_slot *sw_desc, *grp_start;
int slot_cnt;
dev_dbg(mv_chan->device->common.dev,
dev_dbg(mv_chan_to_devp(mv_chan),
"%s dest: %x len: %u flags: %ld\n",
__func__, dest, len, flags);
if (unlikely(len < MV_XOR_MIN_BYTE_COUNT))
@ -718,7 +718,7 @@ mv_xor_prep_dma_memset(struct dma_chan *chan, dma_addr_t dest, int value,
sw_desc->unmap_len = len;
}
spin_unlock_bh(&mv_chan->lock);
dev_dbg(mv_chan->device->common.dev,
dev_dbg(mv_chan_to_devp(mv_chan),
"%s sw_desc %p async_tx %p \n",
__func__, sw_desc, &sw_desc->async_tx);
return sw_desc ? &sw_desc->async_tx : NULL;
@ -737,7 +737,7 @@ mv_xor_prep_dma_xor(struct dma_chan *chan, dma_addr_t dest, dma_addr_t *src,
BUG_ON(len > MV_XOR_MAX_BYTE_COUNT);
dev_dbg(mv_chan->device->common.dev,
dev_dbg(mv_chan_to_devp(mv_chan),
"%s src_cnt: %d len: dest %x %u flags: %ld\n",
__func__, src_cnt, len, dest, flags);
@ -758,7 +758,7 @@ mv_xor_prep_dma_xor(struct dma_chan *chan, dma_addr_t dest, dma_addr_t *src,
mv_desc_set_src_addr(grp_start, src_cnt, src[src_cnt]);
}
spin_unlock_bh(&mv_chan->lock);
dev_dbg(mv_chan->device->common.dev,
dev_dbg(mv_chan_to_devp(mv_chan),
"%s sw_desc %p async_tx %p \n",
__func__, sw_desc, &sw_desc->async_tx);
return sw_desc ? &sw_desc->async_tx : NULL;
@ -791,12 +791,12 @@ static void mv_xor_free_chan_resources(struct dma_chan *chan)
}
mv_chan->last_used = NULL;
dev_dbg(mv_chan->device->common.dev, "%s slots_allocated %d\n",
dev_dbg(mv_chan_to_devp(mv_chan), "%s slots_allocated %d\n",
__func__, mv_chan->slots_allocated);
spin_unlock_bh(&mv_chan->lock);
if (in_use_descs)
dev_err(mv_chan->device->common.dev,
dev_err(mv_chan_to_devp(mv_chan),
"freeing %d in use descriptors!\n", in_use_descs);
}
@ -828,42 +828,42 @@ static void mv_dump_xor_regs(struct mv_xor_chan *chan)
u32 val;
val = __raw_readl(XOR_CONFIG(chan));
dev_printk(KERN_ERR, chan->device->common.dev,
"config 0x%08x.\n", val);
dev_err(mv_chan_to_devp(chan),
"config 0x%08x.\n", val);
val = __raw_readl(XOR_ACTIVATION(chan));
dev_printk(KERN_ERR, chan->device->common.dev,
"activation 0x%08x.\n", val);
dev_err(mv_chan_to_devp(chan),
"activation 0x%08x.\n", val);
val = __raw_readl(XOR_INTR_CAUSE(chan));
dev_printk(KERN_ERR, chan->device->common.dev,
"intr cause 0x%08x.\n", val);
dev_err(mv_chan_to_devp(chan),
"intr cause 0x%08x.\n", val);
val = __raw_readl(XOR_INTR_MASK(chan));
dev_printk(KERN_ERR, chan->device->common.dev,
"intr mask 0x%08x.\n", val);
dev_err(mv_chan_to_devp(chan),
"intr mask 0x%08x.\n", val);
val = __raw_readl(XOR_ERROR_CAUSE(chan));
dev_printk(KERN_ERR, chan->device->common.dev,
"error cause 0x%08x.\n", val);
dev_err(mv_chan_to_devp(chan),
"error cause 0x%08x.\n", val);
val = __raw_readl(XOR_ERROR_ADDR(chan));
dev_printk(KERN_ERR, chan->device->common.dev,
"error addr 0x%08x.\n", val);
dev_err(mv_chan_to_devp(chan),
"error addr 0x%08x.\n", val);
}
static void mv_xor_err_interrupt_handler(struct mv_xor_chan *chan,
u32 intr_cause)
{
if (intr_cause & (1 << 4)) {
dev_dbg(chan->device->common.dev,
dev_dbg(mv_chan_to_devp(chan),
"ignore this error\n");
return;
}
dev_printk(KERN_ERR, chan->device->common.dev,
"error on chan %d. intr cause 0x%08x.\n",
chan->idx, intr_cause);
dev_err(mv_chan_to_devp(chan),
"error on chan %d. intr cause 0x%08x.\n",
chan->idx, intr_cause);
mv_dump_xor_regs(chan);
BUG();
@ -874,7 +874,7 @@ static irqreturn_t mv_xor_interrupt_handler(int irq, void *data)
struct mv_xor_chan *chan = data;
u32 intr_cause = mv_chan_get_intr_cause(chan);
dev_dbg(chan->device->common.dev, "intr cause %x\n", intr_cause);
dev_dbg(mv_chan_to_devp(chan), "intr cause %x\n", intr_cause);
if (mv_is_err_intr(intr_cause))
mv_xor_err_interrupt_handler(chan, intr_cause);
@ -901,7 +901,7 @@ static void mv_xor_issue_pending(struct dma_chan *chan)
*/
#define MV_XOR_TEST_SIZE 2000
static int mv_xor_memcpy_self_test(struct mv_xor_device *device)
static int mv_xor_memcpy_self_test(struct mv_xor_chan *mv_chan)
{
int i;
void *src, *dest;
@ -910,7 +910,6 @@ static int mv_xor_memcpy_self_test(struct mv_xor_device *device)
dma_cookie_t cookie;
struct dma_async_tx_descriptor *tx;
int err = 0;
struct mv_xor_chan *mv_chan;
src = kmalloc(sizeof(u8) * MV_XOR_TEST_SIZE, GFP_KERNEL);
if (!src)
@ -926,10 +925,7 @@ static int mv_xor_memcpy_self_test(struct mv_xor_device *device)
for (i = 0; i < MV_XOR_TEST_SIZE; i++)
((u8 *) src)[i] = (u8)i;
/* Start copy, using first DMA channel */
dma_chan = container_of(device->common.channels.next,
struct dma_chan,
device_node);
dma_chan = &mv_chan->dmachan;
if (mv_xor_alloc_chan_resources(dma_chan) < 1) {
err = -ENODEV;
goto out;
@ -950,18 +946,17 @@ static int mv_xor_memcpy_self_test(struct mv_xor_device *device)
if (mv_xor_status(dma_chan, cookie, NULL) !=
DMA_SUCCESS) {
dev_printk(KERN_ERR, dma_chan->device->dev,
"Self-test copy timed out, disabling\n");
dev_err(dma_chan->device->dev,
"Self-test copy timed out, disabling\n");
err = -ENODEV;
goto free_resources;
}
mv_chan = to_mv_xor_chan(dma_chan);
dma_sync_single_for_cpu(&mv_chan->device->pdev->dev, dest_dma,
dma_sync_single_for_cpu(dma_chan->device->dev, dest_dma,
MV_XOR_TEST_SIZE, DMA_FROM_DEVICE);
if (memcmp(src, dest, MV_XOR_TEST_SIZE)) {
dev_printk(KERN_ERR, dma_chan->device->dev,
"Self-test copy failed compare, disabling\n");
dev_err(dma_chan->device->dev,
"Self-test copy failed compare, disabling\n");
err = -ENODEV;
goto free_resources;
}
@ -976,7 +971,7 @@ out:
#define MV_XOR_NUM_SRC_TEST 4 /* must be <= 15 */
static int
mv_xor_xor_self_test(struct mv_xor_device *device)
mv_xor_xor_self_test(struct mv_xor_chan *mv_chan)
{
int i, src_idx;
struct page *dest;
@ -989,7 +984,6 @@ mv_xor_xor_self_test(struct mv_xor_device *device)
u8 cmp_byte = 0;
u32 cmp_word;
int err = 0;
struct mv_xor_chan *mv_chan;
for (src_idx = 0; src_idx < MV_XOR_NUM_SRC_TEST; src_idx++) {
xor_srcs[src_idx] = alloc_page(GFP_KERNEL);
@ -1022,9 +1016,7 @@ mv_xor_xor_self_test(struct mv_xor_device *device)
memset(page_address(dest), 0, PAGE_SIZE);
dma_chan = container_of(device->common.channels.next,
struct dma_chan,
device_node);
dma_chan = &mv_chan->dmachan;
if (mv_xor_alloc_chan_resources(dma_chan) < 1) {
err = -ENODEV;
goto out;
@ -1048,22 +1040,21 @@ mv_xor_xor_self_test(struct mv_xor_device *device)
if (mv_xor_status(dma_chan, cookie, NULL) !=
DMA_SUCCESS) {
dev_printk(KERN_ERR, dma_chan->device->dev,
"Self-test xor timed out, disabling\n");
dev_err(dma_chan->device->dev,
"Self-test xor timed out, disabling\n");
err = -ENODEV;
goto free_resources;
}
mv_chan = to_mv_xor_chan(dma_chan);
dma_sync_single_for_cpu(&mv_chan->device->pdev->dev, dest_dma,
dma_sync_single_for_cpu(dma_chan->device->dev, dest_dma,
PAGE_SIZE, DMA_FROM_DEVICE);
for (i = 0; i < (PAGE_SIZE / sizeof(u32)); i++) {
u32 *ptr = page_address(dest);
if (ptr[i] != cmp_word) {
dev_printk(KERN_ERR, dma_chan->device->dev,
"Self-test xor failed compare, disabling."
" index %d, data %x, expected %x\n", i,
ptr[i], cmp_word);
dev_err(dma_chan->device->dev,
"Self-test xor failed compare, disabling."
" index %d, data %x, expected %x\n", i,
ptr[i], cmp_word);
err = -ENODEV;
goto free_resources;
}
@ -1079,62 +1070,66 @@ out:
return err;
}
static int __devexit mv_xor_remove(struct platform_device *dev)
/* This driver does not implement any of the optional DMA operations. */
static int
mv_xor_control(struct dma_chan *chan, enum dma_ctrl_cmd cmd,
unsigned long arg)
{
return -ENOSYS;
}
static int mv_xor_channel_remove(struct mv_xor_chan *mv_chan)
{
struct mv_xor_device *device = platform_get_drvdata(dev);
struct dma_chan *chan, *_chan;
struct mv_xor_chan *mv_chan;
struct mv_xor_platform_data *plat_data = dev->dev.platform_data;
struct device *dev = mv_chan->dmadev.dev;
dma_async_device_unregister(&device->common);
dma_async_device_unregister(&mv_chan->dmadev);
dma_free_coherent(&dev->dev, plat_data->pool_size,
device->dma_desc_pool_virt, device->dma_desc_pool);
dma_free_coherent(dev, MV_XOR_POOL_SIZE,
mv_chan->dma_desc_pool_virt, mv_chan->dma_desc_pool);
list_for_each_entry_safe(chan, _chan, &device->common.channels,
device_node) {
mv_chan = to_mv_xor_chan(chan);
list_for_each_entry_safe(chan, _chan, &mv_chan->dmadev.channels,
device_node) {
list_del(&chan->device_node);
}
free_irq(mv_chan->irq, mv_chan);
return 0;
}
static int mv_xor_probe(struct platform_device *pdev)
static struct mv_xor_chan *
mv_xor_channel_add(struct mv_xor_device *xordev,
struct platform_device *pdev,
int idx, dma_cap_mask_t cap_mask, int irq)
{
int ret = 0;
int irq;
struct mv_xor_device *adev;
struct mv_xor_chan *mv_chan;
struct dma_device *dma_dev;
struct mv_xor_platform_data *plat_data = pdev->dev.platform_data;
mv_chan = devm_kzalloc(&pdev->dev, sizeof(*mv_chan), GFP_KERNEL);
if (!mv_chan) {
ret = -ENOMEM;
goto err_free_dma;
}
adev = devm_kzalloc(&pdev->dev, sizeof(*adev), GFP_KERNEL);
if (!adev)
return -ENOMEM;
mv_chan->idx = idx;
mv_chan->irq = irq;
dma_dev = &adev->common;
dma_dev = &mv_chan->dmadev;
/* allocate coherent memory for hardware descriptors
* note: writecombine gives slightly better performance, but
* requires that we explicitly flush the writes
*/
adev->dma_desc_pool_virt = dma_alloc_writecombine(&pdev->dev,
plat_data->pool_size,
&adev->dma_desc_pool,
GFP_KERNEL);
if (!adev->dma_desc_pool_virt)
return -ENOMEM;
adev->id = plat_data->hw_id;
mv_chan->dma_desc_pool_virt =
dma_alloc_writecombine(&pdev->dev, MV_XOR_POOL_SIZE,
&mv_chan->dma_desc_pool, GFP_KERNEL);
if (!mv_chan->dma_desc_pool_virt)
return ERR_PTR(-ENOMEM);
/* discover transaction capabilites from the platform data */
dma_dev->cap_mask = plat_data->cap_mask;
adev->pdev = pdev;
platform_set_drvdata(pdev, adev);
adev->shared = platform_get_drvdata(plat_data->shared);
dma_dev->cap_mask = cap_mask;
INIT_LIST_HEAD(&dma_dev->channels);
@ -1143,6 +1138,7 @@ static int mv_xor_probe(struct platform_device *pdev)
dma_dev->device_free_chan_resources = mv_xor_free_chan_resources;
dma_dev->device_tx_status = mv_xor_status;
dma_dev->device_issue_pending = mv_xor_issue_pending;
dma_dev->device_control = mv_xor_control;
dma_dev->dev = &pdev->dev;
/* set prep routines based on capability */
@ -1155,15 +1151,7 @@ static int mv_xor_probe(struct platform_device *pdev)
dma_dev->device_prep_dma_xor = mv_xor_prep_dma_xor;
}
mv_chan = devm_kzalloc(&pdev->dev, sizeof(*mv_chan), GFP_KERNEL);
if (!mv_chan) {
ret = -ENOMEM;
goto err_free_dma;
}
mv_chan->device = adev;
mv_chan->idx = plat_data->hw_id;
mv_chan->mmr_base = adev->shared->xor_base;
mv_chan->mmr_base = xordev->xor_base;
if (!mv_chan->mmr_base) {
ret = -ENOMEM;
goto err_free_dma;
@ -1174,14 +1162,8 @@ static int mv_xor_probe(struct platform_device *pdev)
/* clear errors before enabling interrupts */
mv_xor_device_clear_err_status(mv_chan);
irq = platform_get_irq(pdev, 0);
if (irq < 0) {
ret = irq;
goto err_free_dma;
}
ret = devm_request_irq(&pdev->dev, irq,
mv_xor_interrupt_handler,
0, dev_name(&pdev->dev), mv_chan);
ret = request_irq(mv_chan->irq, mv_xor_interrupt_handler,
0, dev_name(&pdev->dev), mv_chan);
if (ret)
goto err_free_dma;
@ -1193,26 +1175,26 @@ static int mv_xor_probe(struct platform_device *pdev)
INIT_LIST_HEAD(&mv_chan->chain);
INIT_LIST_HEAD(&mv_chan->completed_slots);
INIT_LIST_HEAD(&mv_chan->all_slots);
mv_chan->common.device = dma_dev;
dma_cookie_init(&mv_chan->common);
mv_chan->dmachan.device = dma_dev;
dma_cookie_init(&mv_chan->dmachan);
list_add_tail(&mv_chan->common.device_node, &dma_dev->channels);
list_add_tail(&mv_chan->dmachan.device_node, &dma_dev->channels);
if (dma_has_cap(DMA_MEMCPY, dma_dev->cap_mask)) {
ret = mv_xor_memcpy_self_test(adev);
ret = mv_xor_memcpy_self_test(mv_chan);
dev_dbg(&pdev->dev, "memcpy self test returned %d\n", ret);
if (ret)
goto err_free_dma;
goto err_free_irq;
}
if (dma_has_cap(DMA_XOR, dma_dev->cap_mask)) {
ret = mv_xor_xor_self_test(adev);
ret = mv_xor_xor_self_test(mv_chan);
dev_dbg(&pdev->dev, "xor self test returned %d\n", ret);
if (ret)
goto err_free_dma;
goto err_free_irq;
}
dev_printk(KERN_INFO, &pdev->dev, "Marvell XOR: "
dev_info(&pdev->dev, "Marvell XOR: "
"( %s%s%s%s)\n",
dma_has_cap(DMA_XOR, dma_dev->cap_mask) ? "xor " : "",
dma_has_cap(DMA_MEMSET, dma_dev->cap_mask) ? "fill " : "",
@ -1220,20 +1202,21 @@ static int mv_xor_probe(struct platform_device *pdev)
dma_has_cap(DMA_INTERRUPT, dma_dev->cap_mask) ? "intr " : "");
dma_async_device_register(dma_dev);
goto out;
return mv_chan;
err_free_irq:
free_irq(mv_chan->irq, mv_chan);
err_free_dma:
dma_free_coherent(&adev->pdev->dev, plat_data->pool_size,
adev->dma_desc_pool_virt, adev->dma_desc_pool);
out:
return ret;
dma_free_coherent(&pdev->dev, MV_XOR_POOL_SIZE,
mv_chan->dma_desc_pool_virt, mv_chan->dma_desc_pool);
return ERR_PTR(ret);
}
static void
mv_xor_conf_mbus_windows(struct mv_xor_shared_private *msp,
mv_xor_conf_mbus_windows(struct mv_xor_device *xordev,
const struct mbus_dram_target_info *dram)
{
void __iomem *base = msp->xor_base;
void __iomem *base = xordev->xor_base;
u32 win_enable = 0;
int i;
@ -1258,99 +1241,176 @@ mv_xor_conf_mbus_windows(struct mv_xor_shared_private *msp,
writel(win_enable, base + WINDOW_BAR_ENABLE(0));
writel(win_enable, base + WINDOW_BAR_ENABLE(1));
writel(0, base + WINDOW_OVERRIDE_CTRL(0));
writel(0, base + WINDOW_OVERRIDE_CTRL(1));
}
static struct platform_driver mv_xor_driver = {
.probe = mv_xor_probe,
.remove = mv_xor_remove,
.driver = {
.owner = THIS_MODULE,
.name = MV_XOR_NAME,
},
};
static int mv_xor_shared_probe(struct platform_device *pdev)
static int mv_xor_probe(struct platform_device *pdev)
{
const struct mbus_dram_target_info *dram;
struct mv_xor_shared_private *msp;
struct mv_xor_device *xordev;
struct mv_xor_platform_data *pdata = pdev->dev.platform_data;
struct resource *res;
int i, ret;
dev_printk(KERN_NOTICE, &pdev->dev, "Marvell shared XOR driver\n");
dev_notice(&pdev->dev, "Marvell XOR driver\n");
msp = devm_kzalloc(&pdev->dev, sizeof(*msp), GFP_KERNEL);
if (!msp)
xordev = devm_kzalloc(&pdev->dev, sizeof(*xordev), GFP_KERNEL);
if (!xordev)
return -ENOMEM;
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
if (!res)
return -ENODEV;
msp->xor_base = devm_ioremap(&pdev->dev, res->start,
resource_size(res));
if (!msp->xor_base)
xordev->xor_base = devm_ioremap(&pdev->dev, res->start,
resource_size(res));
if (!xordev->xor_base)
return -EBUSY;
res = platform_get_resource(pdev, IORESOURCE_MEM, 1);
if (!res)
return -ENODEV;
msp->xor_high_base = devm_ioremap(&pdev->dev, res->start,
resource_size(res));
if (!msp->xor_high_base)
xordev->xor_high_base = devm_ioremap(&pdev->dev, res->start,
resource_size(res));
if (!xordev->xor_high_base)
return -EBUSY;
platform_set_drvdata(pdev, msp);
platform_set_drvdata(pdev, xordev);
/*
* (Re-)program MBUS remapping windows if we are asked to.
*/
dram = mv_mbus_dram_info();
if (dram)
mv_xor_conf_mbus_windows(msp, dram);
mv_xor_conf_mbus_windows(xordev, dram);
/* Not all platforms can gate the clock, so it is not
* an error if the clock does not exists.
*/
msp->clk = clk_get(&pdev->dev, NULL);
if (!IS_ERR(msp->clk))
clk_prepare_enable(msp->clk);
xordev->clk = clk_get(&pdev->dev, NULL);
if (!IS_ERR(xordev->clk))
clk_prepare_enable(xordev->clk);
if (pdev->dev.of_node) {
struct device_node *np;
int i = 0;
for_each_child_of_node(pdev->dev.of_node, np) {
dma_cap_mask_t cap_mask;
int irq;
dma_cap_zero(cap_mask);
if (of_property_read_bool(np, "dmacap,memcpy"))
dma_cap_set(DMA_MEMCPY, cap_mask);
if (of_property_read_bool(np, "dmacap,xor"))
dma_cap_set(DMA_XOR, cap_mask);
if (of_property_read_bool(np, "dmacap,memset"))
dma_cap_set(DMA_MEMSET, cap_mask);
if (of_property_read_bool(np, "dmacap,interrupt"))
dma_cap_set(DMA_INTERRUPT, cap_mask);
irq = irq_of_parse_and_map(np, 0);
if (!irq) {
ret = -ENODEV;
goto err_channel_add;
}
xordev->channels[i] =
mv_xor_channel_add(xordev, pdev, i,
cap_mask, irq);
if (IS_ERR(xordev->channels[i])) {
ret = PTR_ERR(xordev->channels[i]);
xordev->channels[i] = NULL;
irq_dispose_mapping(irq);
goto err_channel_add;
}
i++;
}
} else if (pdata && pdata->channels) {
for (i = 0; i < MV_XOR_MAX_CHANNELS; i++) {
struct mv_xor_channel_data *cd;
int irq;
cd = &pdata->channels[i];
if (!cd) {
ret = -ENODEV;
goto err_channel_add;
}
irq = platform_get_irq(pdev, i);
if (irq < 0) {
ret = irq;
goto err_channel_add;
}
xordev->channels[i] =
mv_xor_channel_add(xordev, pdev, i,
cd->cap_mask, irq);
if (IS_ERR(xordev->channels[i])) {
ret = PTR_ERR(xordev->channels[i]);
goto err_channel_add;
}
}
}
return 0;
err_channel_add:
for (i = 0; i < MV_XOR_MAX_CHANNELS; i++)
if (xordev->channels[i]) {
if (pdev->dev.of_node)
irq_dispose_mapping(xordev->channels[i]->irq);
mv_xor_channel_remove(xordev->channels[i]);
}
clk_disable_unprepare(xordev->clk);
clk_put(xordev->clk);
return ret;
}
static int mv_xor_shared_remove(struct platform_device *pdev)
static int mv_xor_remove(struct platform_device *pdev)
{
struct mv_xor_shared_private *msp = platform_get_drvdata(pdev);
struct mv_xor_device *xordev = platform_get_drvdata(pdev);
int i;
if (!IS_ERR(msp->clk)) {
clk_disable_unprepare(msp->clk);
clk_put(msp->clk);
for (i = 0; i < MV_XOR_MAX_CHANNELS; i++) {
if (xordev->channels[i])
mv_xor_channel_remove(xordev->channels[i]);
}
if (!IS_ERR(xordev->clk)) {
clk_disable_unprepare(xordev->clk);
clk_put(xordev->clk);
}
return 0;
}
static struct platform_driver mv_xor_shared_driver = {
.probe = mv_xor_shared_probe,
.remove = mv_xor_shared_remove,
#ifdef CONFIG_OF
static struct of_device_id mv_xor_dt_ids[] = {
{ .compatible = "marvell,orion-xor", },
{},
};
MODULE_DEVICE_TABLE(of, mv_xor_dt_ids);
#endif
static struct platform_driver mv_xor_driver = {
.probe = mv_xor_probe,
.remove = mv_xor_remove,
.driver = {
.owner = THIS_MODULE,
.name = MV_XOR_SHARED_NAME,
.owner = THIS_MODULE,
.name = MV_XOR_NAME,
.of_match_table = of_match_ptr(mv_xor_dt_ids),
},
};
static int __init mv_xor_init(void)
{
int rc;
rc = platform_driver_register(&mv_xor_shared_driver);
if (!rc) {
rc = platform_driver_register(&mv_xor_driver);
if (rc)
platform_driver_unregister(&mv_xor_shared_driver);
}
return rc;
return platform_driver_register(&mv_xor_driver);
}
module_init(mv_xor_init);
@ -1359,7 +1419,6 @@ module_init(mv_xor_init);
static void __exit mv_xor_exit(void)
{
platform_driver_unregister(&mv_xor_driver);
platform_driver_unregister(&mv_xor_shared_driver);
return;
}

View File

@ -24,8 +24,10 @@
#include <linux/interrupt.h>
#define USE_TIMER
#define MV_XOR_POOL_SIZE PAGE_SIZE
#define MV_XOR_SLOT_SIZE 64
#define MV_XOR_THRESHOLD 1
#define MV_XOR_MAX_CHANNELS 2
#define XOR_OPERATION_MODE_XOR 0
#define XOR_OPERATION_MODE_MEMCPY 2
@ -51,29 +53,13 @@
#define WINDOW_SIZE(w) (0x270 + ((w) << 2))
#define WINDOW_REMAP_HIGH(w) (0x290 + ((w) << 2))
#define WINDOW_BAR_ENABLE(chan) (0x240 + ((chan) << 2))
#define WINDOW_OVERRIDE_CTRL(chan) (0x2A0 + ((chan) << 2))
struct mv_xor_shared_private {
void __iomem *xor_base;
void __iomem *xor_high_base;
struct clk *clk;
};
/**
* struct mv_xor_device - internal representation of a XOR device
* @pdev: Platform device
* @id: HW XOR Device selector
* @dma_desc_pool: base of DMA descriptor region (DMA address)
* @dma_desc_pool_virt: base of DMA descriptor region (CPU address)
* @common: embedded struct dma_device
*/
struct mv_xor_device {
struct platform_device *pdev;
int id;
dma_addr_t dma_desc_pool;
void *dma_desc_pool_virt;
struct dma_device common;
struct mv_xor_shared_private *shared;
void __iomem *xor_base;
void __iomem *xor_high_base;
struct clk *clk;
struct mv_xor_chan *channels[MV_XOR_MAX_CHANNELS];
};
/**
@ -96,11 +82,15 @@ struct mv_xor_chan {
spinlock_t lock; /* protects the descriptor slot pool */
void __iomem *mmr_base;
unsigned int idx;
int irq;
enum dma_transaction_type current_type;
struct list_head chain;
struct list_head completed_slots;
struct mv_xor_device *device;
struct dma_chan common;
dma_addr_t dma_desc_pool;
void *dma_desc_pool_virt;
size_t pool_size;
struct dma_device dmadev;
struct dma_chan dmachan;
struct mv_xor_desc_slot *last_used;
struct list_head all_slots;
int slots_allocated;

View File

@ -31,6 +31,30 @@ config MV643XX_ETH
Some boards that use the Discovery chipset are the Momenco
Ocelot C and Jaguar ATX and Pegasos II.
config MVMDIO
tristate "Marvell MDIO interface support"
---help---
This driver supports the MDIO interface found in the network
interface units of the Marvell EBU SoCs (Kirkwood, Orion5x,
Dove, Armada 370 and Armada XP).
For now, this driver is only needed for the MVNETA driver
(used on Armada 370 and XP), but it could be used in the
future by the MV643XX_ETH driver.
config MVNETA
tristate "Marvell Armada 370/XP network interface support"
depends on MACH_ARMADA_370_XP
select PHYLIB
select MVMDIO
---help---
This driver supports the network interface units in the
Marvell ARMADA XP and ARMADA 370 SoC family.
Note that this driver is distinct from the mv643xx_eth
driver, which should be used for the older Marvell SoCs
(Dove, Orion, Discovery, Kirkwood).
config PXA168_ETH
tristate "Marvell pxa168 ethernet support"
depends on CPU_PXA168

View File

@ -3,6 +3,8 @@
#
obj-$(CONFIG_MV643XX_ETH) += mv643xx_eth.o
obj-$(CONFIG_MVMDIO) += mvmdio.o
obj-$(CONFIG_MVNETA) += mvneta.o
obj-$(CONFIG_PXA168_ETH) += pxa168_eth.o
obj-$(CONFIG_SKGE) += skge.o
obj-$(CONFIG_SKY2) += sky2.o

View File

@ -0,0 +1,228 @@
/*
* Driver for the MDIO interface of Marvell network interfaces.
*
* Since the MDIO interface of Marvell network interfaces is shared
* between all network interfaces, having a single driver allows to
* handle concurrent accesses properly (you may have four Ethernet
* ports, but they in fact share the same SMI interface to access the
* MDIO bus). Moreover, this MDIO interface code is similar between
* the mv643xx_eth driver and the mvneta driver. For now, it is only
* used by the mvneta driver, but it could later be used by the
* mv643xx_eth driver as well.
*
* Copyright (C) 2012 Marvell
*
* Thomas Petazzoni <thomas.petazzoni@free-electrons.com>
*
* This file is licensed under the terms of the GNU General Public
* License version 2. This program is licensed "as is" without any
* warranty of any kind, whether express or implied.
*/
#include <linux/init.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/mutex.h>
#include <linux/phy.h>
#include <linux/of_address.h>
#include <linux/of_mdio.h>
#include <linux/platform_device.h>
#include <linux/delay.h>
#define MVMDIO_SMI_DATA_SHIFT 0
#define MVMDIO_SMI_PHY_ADDR_SHIFT 16
#define MVMDIO_SMI_PHY_REG_SHIFT 21
#define MVMDIO_SMI_READ_OPERATION BIT(26)
#define MVMDIO_SMI_WRITE_OPERATION 0
#define MVMDIO_SMI_READ_VALID BIT(27)
#define MVMDIO_SMI_BUSY BIT(28)
struct orion_mdio_dev {
struct mutex lock;
void __iomem *smireg;
};
/* Wait for the SMI unit to be ready for another operation
*/
static int orion_mdio_wait_ready(struct mii_bus *bus)
{
struct orion_mdio_dev *dev = bus->priv;
int count;
u32 val;
count = 0;
while (1) {
val = readl(dev->smireg);
if (!(val & MVMDIO_SMI_BUSY))
break;
if (count > 100) {
dev_err(bus->parent, "Timeout: SMI busy for too long\n");
return -ETIMEDOUT;
}
udelay(10);
count++;
}
return 0;
}
static int orion_mdio_read(struct mii_bus *bus, int mii_id,
int regnum)
{
struct orion_mdio_dev *dev = bus->priv;
int count;
u32 val;
int ret;
mutex_lock(&dev->lock);
ret = orion_mdio_wait_ready(bus);
if (ret < 0) {
mutex_unlock(&dev->lock);
return ret;
}
writel(((mii_id << MVMDIO_SMI_PHY_ADDR_SHIFT) |
(regnum << MVMDIO_SMI_PHY_REG_SHIFT) |
MVMDIO_SMI_READ_OPERATION),
dev->smireg);
/* Wait for the value to become available */
count = 0;
while (1) {
val = readl(dev->smireg);
if (val & MVMDIO_SMI_READ_VALID)
break;
if (count > 100) {
dev_err(bus->parent, "Timeout when reading PHY\n");
mutex_unlock(&dev->lock);
return -ETIMEDOUT;
}
udelay(10);
count++;
}
mutex_unlock(&dev->lock);
return val & 0xFFFF;
}
static int orion_mdio_write(struct mii_bus *bus, int mii_id,
int regnum, u16 value)
{
struct orion_mdio_dev *dev = bus->priv;
int ret;
mutex_lock(&dev->lock);
ret = orion_mdio_wait_ready(bus);
if (ret < 0) {
mutex_unlock(&dev->lock);
return ret;
}
writel(((mii_id << MVMDIO_SMI_PHY_ADDR_SHIFT) |
(regnum << MVMDIO_SMI_PHY_REG_SHIFT) |
MVMDIO_SMI_WRITE_OPERATION |
(value << MVMDIO_SMI_DATA_SHIFT)),
dev->smireg);
mutex_unlock(&dev->lock);
return 0;
}
static int orion_mdio_reset(struct mii_bus *bus)
{
return 0;
}
static int __devinit orion_mdio_probe(struct platform_device *pdev)
{
struct device_node *np = pdev->dev.of_node;
struct mii_bus *bus;
struct orion_mdio_dev *dev;
int i, ret;
bus = mdiobus_alloc_size(sizeof(struct orion_mdio_dev));
if (!bus) {
dev_err(&pdev->dev, "Cannot allocate MDIO bus\n");
return -ENOMEM;
}
bus->name = "orion_mdio_bus";
bus->read = orion_mdio_read;
bus->write = orion_mdio_write;
bus->reset = orion_mdio_reset;
snprintf(bus->id, MII_BUS_ID_SIZE, "%s-mii",
dev_name(&pdev->dev));
bus->parent = &pdev->dev;
bus->irq = kmalloc(sizeof(int) * PHY_MAX_ADDR, GFP_KERNEL);
if (!bus->irq) {
dev_err(&pdev->dev, "Cannot allocate PHY IRQ array\n");
mdiobus_free(bus);
return -ENOMEM;
}
for (i = 0; i < PHY_MAX_ADDR; i++)
bus->irq[i] = PHY_POLL;
dev = bus->priv;
dev->smireg = of_iomap(pdev->dev.of_node, 0);
if (!dev->smireg) {
dev_err(&pdev->dev, "No SMI register address given in DT\n");
kfree(bus->irq);
mdiobus_free(bus);
return -ENODEV;
}
mutex_init(&dev->lock);
ret = of_mdiobus_register(bus, np);
if (ret < 0) {
dev_err(&pdev->dev, "Cannot register MDIO bus (%d)\n", ret);
iounmap(dev->smireg);
kfree(bus->irq);
mdiobus_free(bus);
return ret;
}
platform_set_drvdata(pdev, bus);
return 0;
}
static int __devexit orion_mdio_remove(struct platform_device *pdev)
{
struct mii_bus *bus = platform_get_drvdata(pdev);
mdiobus_unregister(bus);
kfree(bus->irq);
mdiobus_free(bus);
return 0;
}
static const struct of_device_id orion_mdio_match[] = {
{ .compatible = "marvell,orion-mdio" },
{ }
};
MODULE_DEVICE_TABLE(of, orion_mdio_match);
static struct platform_driver orion_mdio_driver = {
.probe = orion_mdio_probe,
.remove = __devexit_p(orion_mdio_remove),
.driver = {
.name = "orion-mdio",
.of_match_table = orion_mdio_match,
},
};
module_platform_driver(orion_mdio_driver);
MODULE_DESCRIPTION("Marvell MDIO interface driver");
MODULE_AUTHOR("Thomas Petazzoni <thomas.petazzoni@free-electrons.com>");
MODULE_LICENSE("GPL");

File diff suppressed because it is too large Load Diff

22
include/linux/clk/mvebu.h Normal file
View File

@ -0,0 +1,22 @@
/*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* 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.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#ifndef __CLK_MVEBU_H_
#define __CLK_MVEBU_H_
void __init mvebu_clocks_init(void);
#endif

View File

@ -10,15 +10,14 @@
#include <linux/dmaengine.h>
#include <linux/mbus.h>
#define MV_XOR_SHARED_NAME "mv_xor_shared"
#define MV_XOR_NAME "mv_xor"
#define MV_XOR_NAME "mv_xor"
struct mv_xor_platform_data {
struct platform_device *shared;
int hw_id;
struct mv_xor_channel_data {
dma_cap_mask_t cap_mask;
size_t pool_size;
};
struct mv_xor_platform_data {
struct mv_xor_channel_data *channels;
};
#endif