ARM: SoC driver changes for 3.17
A handful of driver-related changes. We've had a bunch of them going in through
other branches as well, so it's only a part of what we really have this release.
Larger pieces are:
* Removal of a now unused PWM driver for atmel
- This includes AVR32 changes that have been appropriately acked.
* Performance counter support for the arm CCN interconnect
* OMAP mailbox driver cleanups and consolidation
* PCI and SATA PHY drivers for SPEAr 13xx platforms
* Redefinition (with backwards compatibility!) of PCI DT bindings for Tegra to
better model regulators/power.
-----BEGIN PGP SIGNATURE-----
Version: GnuPG v1.4.14 (GNU/Linux)
iQIcBAABAgAGBQJT5DrJAAoJEIwa5zzehBx3wkkP/iwEfEK5mMon9KEe4DcKTKNq
Z6xyWuMQNHKdfBFpABs6AsHQCKDc7KK6gN3+2zLLHEJ4XGDPZ2g2NaX3oRPJlaay
BDK7rQfIZyi4tmbOnlEv1BDTYgirYBPMwk9RyNo/04Ug3W+Y67aSVo44zkNFBWaJ
GbcX/zYsrsfvdawuQMW6V/A835s3Kq5Zhv1ikPr8gDDjswZRBAT6i7FYpBSHQ8K8
bH6C1891Xit6rxXSLXJyrtM8CAet7PtLTqNr/IKdUaJnGD+fJm5EonxW+g8gvhN8
gOEkm3nM60++kdDlzZCQVNr0m1+ih6NNCr6bDLO6rIRpAJM2O+YrN1rWuZaJOu1A
pIvifk+wWHT+o52pXk8g9fK4n/ZJydK3IBzDePHMrIROOEiW5tLE3WA+u3NSfMfH
WegMt9E2dcB+5gXPeejZ9gFbAHnh2S1oVTZfCYXtuOHrYiEU9U0FA3eRYvJEE2po
k8sdiOn7Vc65O1QZ+xZNbLABpAHaye7X2evOJyhSutzHE/AtUvT4vuCAZ0tggXyD
E1qVKngVW/NvcoFbwYeidq4bOVgiAEn3idZgF5gEq1mq7LzetXUQAcZAOQfLWHLQ
RrXufS7Ez8pSCG74y0AFReVfQH2PgWHPqGUGj99NXgQauexc/vR1Hc5Iqb8liGNJ
n6i8RqvvQ4KYcmHEXDIT
=fsP6
-----END PGP SIGNATURE-----
Merge tag 'drivers-for-3.17' of git://git.kernel.org/pub/scm/linux/kernel/git/arm/arm-soc
Pull ARM SoC driver changes from Olof Johansson:
"A handful of driver-related changes. We've had a bunch of them going
in through other branches as well, so it's only a part of what we
really have this release.
Larger pieces are:
- Removal of a now unused PWM driver for atmel
[ This includes AVR32 changes that have been appropriately acked ]
- Performance counter support for the arm CCN interconnect
- OMAP mailbox driver cleanups and consolidation
- PCI and SATA PHY drivers for SPEAr 13xx platforms
- Redefinition (with backwards compatibility!) of PCI DT bindings for
Tegra to better model regulators/power"
Note: this merge also fixes up the semantic conflict with the new
calling convention for devm_phy_create(), see commit f0ed817638
("phy:
core: Let node ptr of PHY point to PHY and not of PHY provider") that
came in through Greg's USB tree.
Semantic merge patch by Stephen Rothwell <sfr@canb.auug.org.au> through
the next tree.
* tag 'drivers-for-3.17' of git://git.kernel.org/pub/scm/linux/kernel/git/arm/arm-soc: (38 commits)
bus: arm-ccn: Fix error handling at event allocation
mailbox/omap: add a parent structure for every IP instance
mailbox/omap: remove the private mailbox structure
mailbox/omap: consolidate OMAP mailbox driver
mailbox/omap: simplify the fifo assignment by using macros
mailbox/omap: remove omap_mbox_type_t from mailbox ops
mailbox/omap: remove OMAP1 mailbox driver
mailbox/omap: use devm_* interfaces
bus: ARM CCN: add PERF_EVENTS dependency
bus: ARM CCN PMU driver
PCI: spear: Remove spear13xx_pcie_remove()
PCI: spear: Fix Section mismatch compilation warning for probe()
ARM: tegra: Remove legacy PCIe power supply properties
PCI: tegra: Remove deprecated power supply properties
PCI: tegra: Implement accurate power supply scheme
ARM: SPEAr13xx: Update defconfigs
ARM: SPEAr13xx: Add pcie and miphy DT nodes
ARM: SPEAr13xx: Add bindings and dt node for misc block
ARM: SPEAr13xx: Fix static mapping table
phy: Add drivers for PCIe and SATA phy on SPEAr13xx
...
This commit is contained in:
commit
10c8e05620
|
@ -0,0 +1,52 @@
|
|||
ARM Cache Coherent Network
|
||||
==========================
|
||||
|
||||
CCN-504 is a ring-bus interconnect consisting of 11 crosspoints
|
||||
(XPs), with each crosspoint supporting up to two device ports,
|
||||
so nodes (devices) 0 and 1 are connected to crosspoint 0,
|
||||
nodes 2 and 3 to crosspoint 1 etc.
|
||||
|
||||
PMU (perf) driver
|
||||
-----------------
|
||||
|
||||
The CCN driver registers a perf PMU driver, which provides
|
||||
description of available events and configuration options
|
||||
in sysfs, see /sys/bus/event_source/devices/ccn*.
|
||||
|
||||
The "format" directory describes format of the config, config1
|
||||
and config2 fields of the perf_event_attr structure. The "events"
|
||||
directory provides configuration templates for all documented
|
||||
events, that can be used with perf tool. For example "xp_valid_flit"
|
||||
is an equivalent of "type=0x8,event=0x4". Other parameters must be
|
||||
explicitly specified. For events originating from device, "node"
|
||||
defines its index. All crosspoint events require "xp" (index),
|
||||
"port" (device port number) and "vc" (virtual channel ID) and
|
||||
"dir" (direction). Watchpoints (special "event" value 0xfe) also
|
||||
require comparator values ("cmp_l" and "cmp_h") and "mask", being
|
||||
index of the comparator mask.
|
||||
|
||||
Masks are defined separately from the event description
|
||||
(due to limited number of the config values) in the "cmp_mask"
|
||||
directory, with first 8 configurable by user and additional
|
||||
4 hardcoded for the most frequent use cases.
|
||||
|
||||
Cycle counter is described by a "type" value 0xff and does
|
||||
not require any other settings.
|
||||
|
||||
Example of perf tool use:
|
||||
|
||||
/ # perf list | grep ccn
|
||||
ccn/cycles/ [Kernel PMU event]
|
||||
<...>
|
||||
ccn/xp_valid_flit/ [Kernel PMU event]
|
||||
<...>
|
||||
|
||||
/ # perf stat -C 0 -e ccn/cycles/,ccn/xp_valid_flit,xp=1,port=0,vc=1,dir=1/ \
|
||||
sleep 1
|
||||
|
||||
The driver does not support sampling, therefore "perf record" will
|
||||
not work. Also notice that only single cpu is being selected
|
||||
("-C 0") - this is because perf framework does not support
|
||||
"non-CPU related" counters (yet?) so system-wide session ("-a")
|
||||
would try (and in most cases fail) to set up the same event
|
||||
per each CPU.
|
|
@ -0,0 +1,21 @@
|
|||
* ARM CCN (Cache Coherent Network)
|
||||
|
||||
Required properties:
|
||||
|
||||
- compatible: (standard compatible string) should be one of:
|
||||
"arm,ccn-504"
|
||||
"arm,ccn-508"
|
||||
|
||||
- reg: (standard registers property) physical address and size
|
||||
(16MB) of the configuration registers block
|
||||
|
||||
- interrupts: (standard interrupt property) single interrupt
|
||||
generated by the control block
|
||||
|
||||
Example:
|
||||
|
||||
ccn@0x2000000000 {
|
||||
compatible = "arm,ccn-504";
|
||||
reg = <0x20 0x00000000 0 0x1000000>;
|
||||
interrupts = <0 181 4>;
|
||||
};
|
|
@ -0,0 +1,9 @@
|
|||
SPEAr Misc configuration
|
||||
===========================
|
||||
SPEAr SOCs have some miscellaneous registers which are used to configure
|
||||
few properties of different peripheral controllers.
|
||||
|
||||
misc node required properties:
|
||||
|
||||
- compatible Should be "st,spear1340-misc", "syscon".
|
||||
- reg: Address range of misc space upto 8K
|
|
@ -14,9 +14,6 @@ Required properties:
|
|||
- interrupt-names: Must include the following entries:
|
||||
"intr": The Tegra interrupt that is asserted for controller interrupts
|
||||
"msi": The Tegra interrupt that is asserted when an MSI is received
|
||||
- pex-clk-supply: Supply voltage for internal reference clock
|
||||
- vdd-supply: Power supply for controller (1.05V)
|
||||
- avdd-supply: Power supply for controller (1.05V) (not required for Tegra20)
|
||||
- bus-range: Range of bus numbers associated with this controller
|
||||
- #address-cells: Address representation for root ports (must be 3)
|
||||
- cell 0 specifies the bus and device numbers of the root port:
|
||||
|
@ -60,6 +57,33 @@ Required properties:
|
|||
- afi
|
||||
- pcie_x
|
||||
|
||||
Power supplies for Tegra20:
|
||||
- avdd-pex-supply: Power supply for analog PCIe logic. Must supply 1.05 V.
|
||||
- vdd-pex-supply: Power supply for digital PCIe I/O. Must supply 1.05 V.
|
||||
- avdd-pex-pll-supply: Power supply for dedicated (internal) PCIe PLL. Must
|
||||
supply 1.05 V.
|
||||
- avdd-plle-supply: Power supply for PLLE, which is shared with SATA. Must
|
||||
supply 1.05 V.
|
||||
- vddio-pex-clk-supply: Power supply for PCIe clock. Must supply 3.3 V.
|
||||
|
||||
Power supplies for Tegra30:
|
||||
- Required:
|
||||
- avdd-pex-pll-supply: Power supply for dedicated (internal) PCIe PLL. Must
|
||||
supply 1.05 V.
|
||||
- avdd-plle-supply: Power supply for PLLE, which is shared with SATA. Must
|
||||
supply 1.05 V.
|
||||
- vddio-pex-ctl-supply: Power supply for PCIe control I/O partition. Must
|
||||
supply 1.8 V.
|
||||
- hvdd-pex-supply: High-voltage supply for PCIe I/O and PCIe output clocks.
|
||||
Must supply 3.3 V.
|
||||
- Optional:
|
||||
- If lanes 0 to 3 are used:
|
||||
- avdd-pexa-supply: Power supply for analog PCIe logic. Must supply 1.05 V.
|
||||
- vdd-pexa-supply: Power supply for digital PCIe I/O. Must supply 1.05 V.
|
||||
- If lanes 4 or 5 are used:
|
||||
- avdd-pexb-supply: Power supply for analog PCIe logic. Must supply 1.05 V.
|
||||
- vdd-pexb-supply: Power supply for digital PCIe I/O. Must supply 1.05 V.
|
||||
|
||||
Root ports are defined as subnodes of the PCIe controller node.
|
||||
|
||||
Required properties:
|
||||
|
|
|
@ -0,0 +1,14 @@
|
|||
SPEAr13XX PCIe DT detail:
|
||||
================================
|
||||
|
||||
SPEAr13XX uses synopsis designware PCIe controller and ST MiPHY as phy
|
||||
controller.
|
||||
|
||||
Required properties:
|
||||
- compatible : should be "st,spear1340-pcie", "snps,dw-pcie".
|
||||
- phys : phandle to phy node associated with pcie controller
|
||||
- phy-names : must be "pcie-phy"
|
||||
- All other definitions as per generic PCI bindings
|
||||
|
||||
Optional properties:
|
||||
- st,pcie-is-gen1 indicates that forced gen1 initialization is needed.
|
|
@ -0,0 +1,15 @@
|
|||
ST SPEAr miphy DT details
|
||||
=========================
|
||||
|
||||
ST Microelectronics SPEAr miphy is a phy controller supporting PCIe and SATA.
|
||||
|
||||
Required properties:
|
||||
- compatible : should be "st,spear1310-miphy" or "st,spear1340-miphy"
|
||||
- reg : offset and length of the PHY register set.
|
||||
- misc: phandle for the syscon node to access misc registers
|
||||
- #phy-cells : from the generic PHY bindings, must be 1.
|
||||
- cell[1]: 0 if phy used for SATA, 1 for PCIe.
|
||||
|
||||
Optional properties:
|
||||
- phy-id: Instance id of the phy. Only required when there are multiple phys
|
||||
present on a implementation.
|
|
@ -6902,6 +6902,12 @@ S: Maintained
|
|||
F: Documentation/devicetree/bindings/pci/host-generic-pci.txt
|
||||
F: drivers/pci/host/pci-host-generic.c
|
||||
|
||||
PCIE DRIVER FOR ST SPEAR13XX
|
||||
M: Mohit Kumar <mohit.kumar@st.com>
|
||||
L: linux-pci@vger.kernel.org
|
||||
S: Maintained
|
||||
F: drivers/pci/host/*spear*
|
||||
|
||||
PCMCIA SUBSYSTEM
|
||||
P: Linux PCMCIA Team
|
||||
L: linux-pcmcia@lists.infradead.org
|
||||
|
|
|
@ -106,6 +106,10 @@
|
|||
status = "okay";
|
||||
};
|
||||
|
||||
miphy@eb800000 {
|
||||
status = "okay";
|
||||
};
|
||||
|
||||
cf@b2800000 {
|
||||
status = "okay";
|
||||
};
|
||||
|
|
|
@ -29,24 +29,111 @@
|
|||
#gpio-cells = <2>;
|
||||
};
|
||||
|
||||
ahci@b1000000 {
|
||||
miphy0: miphy@eb800000 {
|
||||
compatible = "st,spear1310-miphy";
|
||||
reg = <0xeb800000 0x4000>;
|
||||
misc = <&misc>;
|
||||
phy-id = <0>;
|
||||
#phy-cells = <1>;
|
||||
status = "disabled";
|
||||
};
|
||||
|
||||
miphy1: miphy@eb804000 {
|
||||
compatible = "st,spear1310-miphy";
|
||||
reg = <0xeb804000 0x4000>;
|
||||
misc = <&misc>;
|
||||
phy-id = <1>;
|
||||
#phy-cells = <1>;
|
||||
status = "disabled";
|
||||
};
|
||||
|
||||
miphy2: miphy@eb808000 {
|
||||
compatible = "st,spear1310-miphy";
|
||||
reg = <0xeb808000 0x4000>;
|
||||
misc = <&misc>;
|
||||
phy-id = <2>;
|
||||
#phy-cells = <1>;
|
||||
status = "disabled";
|
||||
};
|
||||
|
||||
ahci0: ahci@b1000000 {
|
||||
compatible = "snps,spear-ahci";
|
||||
reg = <0xb1000000 0x10000>;
|
||||
interrupts = <0 68 0x4>;
|
||||
phys = <&miphy0 0>;
|
||||
phy-names = "sata-phy";
|
||||
status = "disabled";
|
||||
};
|
||||
|
||||
ahci@b1800000 {
|
||||
ahci1: ahci@b1800000 {
|
||||
compatible = "snps,spear-ahci";
|
||||
reg = <0xb1800000 0x10000>;
|
||||
interrupts = <0 69 0x4>;
|
||||
phys = <&miphy1 0>;
|
||||
phy-names = "sata-phy";
|
||||
status = "disabled";
|
||||
};
|
||||
|
||||
ahci@b4000000 {
|
||||
ahci2: ahci@b4000000 {
|
||||
compatible = "snps,spear-ahci";
|
||||
reg = <0xb4000000 0x10000>;
|
||||
interrupts = <0 70 0x4>;
|
||||
phys = <&miphy2 0>;
|
||||
phy-names = "sata-phy";
|
||||
status = "disabled";
|
||||
};
|
||||
|
||||
pcie0: pcie@b1000000 {
|
||||
compatible = "st,spear1340-pcie", "snps,dw-pcie";
|
||||
reg = <0xb1000000 0x4000>;
|
||||
interrupts = <0 68 0x4>;
|
||||
interrupt-map-mask = <0 0 0 0>;
|
||||
interrupt-map = <0x0 0 &gic 0 68 0x4>;
|
||||
num-lanes = <1>;
|
||||
phys = <&miphy0 1>;
|
||||
phy-names = "pcie-phy";
|
||||
#address-cells = <3>;
|
||||
#size-cells = <2>;
|
||||
device_type = "pci";
|
||||
ranges = <0x00000800 0 0x80000000 0x80000000 0 0x00020000 /* configuration space */
|
||||
0x81000000 0 0 0x80020000 0 0x00010000 /* downstream I/O */
|
||||
0x82000000 0 0x80030000 0xc0030000 0 0x0ffd0000>; /* non-prefetchable memory */
|
||||
status = "disabled";
|
||||
};
|
||||
|
||||
pcie1: pcie@b1800000 {
|
||||
compatible = "st,spear1340-pcie", "snps,dw-pcie";
|
||||
reg = <0xb1800000 0x4000>;
|
||||
interrupts = <0 69 0x4>;
|
||||
interrupt-map-mask = <0 0 0 0>;
|
||||
interrupt-map = <0x0 0 &gic 0 69 0x4>;
|
||||
num-lanes = <1>;
|
||||
phys = <&miphy1 1>;
|
||||
phy-names = "pcie-phy";
|
||||
#address-cells = <3>;
|
||||
#size-cells = <2>;
|
||||
device_type = "pci";
|
||||
ranges = <0x00000800 0 0x90000000 0x90000000 0 0x00020000 /* configuration space */
|
||||
0x81000000 0 0 0x90020000 0 0x00010000 /* downstream I/O */
|
||||
0x82000000 0 0x90030000 0x90030000 0 0x0ffd0000>; /* non-prefetchable memory */
|
||||
status = "disabled";
|
||||
};
|
||||
|
||||
pcie2: pcie@b4000000 {
|
||||
compatible = "st,spear1340-pcie", "snps,dw-pcie";
|
||||
reg = <0xb4000000 0x4000>;
|
||||
interrupts = <0 70 0x4>;
|
||||
interrupt-map-mask = <0 0 0 0>;
|
||||
interrupt-map = <0x0 0 &gic 0 70 0x4>;
|
||||
num-lanes = <1>;
|
||||
phys = <&miphy2 1>;
|
||||
phy-names = "pcie-phy";
|
||||
#address-cells = <3>;
|
||||
#size-cells = <2>;
|
||||
device_type = "pci";
|
||||
ranges = <0x00000800 0 0xc0000000 0xc0000000 0 0x00020000 /* configuration space */
|
||||
0x81000000 0 0 0xc0020000 0 0x00010000 /* downstream I/O */
|
||||
0x82000000 0 0xc0030000 0xc0030000 0 0x0ffd0000>; /* non-prefetchable memory */
|
||||
status = "disabled";
|
||||
};
|
||||
|
||||
|
|
|
@ -122,6 +122,10 @@
|
|||
status = "okay";
|
||||
};
|
||||
|
||||
miphy@eb800000 {
|
||||
status = "okay";
|
||||
};
|
||||
|
||||
dma@ea800000 {
|
||||
status = "okay";
|
||||
};
|
||||
|
|
|
@ -31,10 +31,38 @@
|
|||
status = "disabled";
|
||||
};
|
||||
|
||||
ahci@b1000000 {
|
||||
miphy0: miphy@eb800000 {
|
||||
compatible = "st,spear1340-miphy";
|
||||
reg = <0xeb800000 0x4000>;
|
||||
misc = <&misc>;
|
||||
#phy-cells = <1>;
|
||||
status = "disabled";
|
||||
};
|
||||
|
||||
ahci0: ahci@b1000000 {
|
||||
compatible = "snps,spear-ahci";
|
||||
reg = <0xb1000000 0x10000>;
|
||||
interrupts = <0 72 0x4>;
|
||||
phys = <&miphy0 0>;
|
||||
phy-names = "sata-phy";
|
||||
status = "disabled";
|
||||
};
|
||||
|
||||
pcie0: pcie@b1000000 {
|
||||
compatible = "st,spear1340-pcie", "snps,dw-pcie";
|
||||
reg = <0xb1000000 0x4000>;
|
||||
interrupts = <0 68 0x4>;
|
||||
interrupt-map-mask = <0 0 0 0>;
|
||||
interrupt-map = <0x0 0 &gic 0 68 0x4>;
|
||||
num-lanes = <1>;
|
||||
phys = <&miphy0 1>;
|
||||
phy-names = "pcie-phy";
|
||||
#address-cells = <3>;
|
||||
#size-cells = <2>;
|
||||
device_type = "pci";
|
||||
ranges = <0x00000800 0 0x80000000 0x80000000 0 0x00020000 /* configuration space */
|
||||
0x81000000 0 0 0x80020000 0 0x00010000 /* downstream I/O */
|
||||
0x82000000 0 0x80030000 0xc0030000 0 0x0ffd0000>; /* non-prefetchable memory */
|
||||
status = "disabled";
|
||||
};
|
||||
|
||||
|
|
|
@ -83,8 +83,8 @@
|
|||
#size-cells = <1>;
|
||||
compatible = "simple-bus";
|
||||
ranges = <0x50000000 0x50000000 0x10000000
|
||||
0xb0000000 0xb0000000 0x10000000
|
||||
0xd0000000 0xd0000000 0x02000000
|
||||
0x80000000 0x80000000 0x20000000
|
||||
0xb0000000 0xb0000000 0x22000000
|
||||
0xd8000000 0xd8000000 0x01000000
|
||||
0xe0000000 0xe0000000 0x10000000>;
|
||||
|
||||
|
@ -220,6 +220,11 @@
|
|||
0xd8000000 0xd8000000 0x01000000
|
||||
0xe0000000 0xe0000000 0x10000000>;
|
||||
|
||||
misc: syscon@e0700000 {
|
||||
compatible = "st,spear1340-misc", "syscon";
|
||||
reg = <0xe0700000 0x1000>;
|
||||
};
|
||||
|
||||
gpio0: gpio@e0600000 {
|
||||
compatible = "arm,pl061", "arm,primecell";
|
||||
reg = <0xe0600000 0x1000>;
|
||||
|
|
|
@ -562,10 +562,14 @@
|
|||
};
|
||||
|
||||
pcie-controller@80003000 {
|
||||
pex-clk-supply = <&pci_clk_reg>;
|
||||
vdd-supply = <&pci_vdd_reg>;
|
||||
status = "okay";
|
||||
|
||||
avdd-pex-supply = <&pci_vdd_reg>;
|
||||
vdd-pex-supply = <&pci_vdd_reg>;
|
||||
avdd-pex-pll-supply = <&pci_vdd_reg>;
|
||||
avdd-plle-supply = <&pci_vdd_reg>;
|
||||
vddio-pex-clk-supply = <&pci_clk_reg>;
|
||||
|
||||
pci@1,0 {
|
||||
status = "okay";
|
||||
};
|
||||
|
|
|
@ -474,8 +474,11 @@
|
|||
};
|
||||
|
||||
pcie-controller@80003000 {
|
||||
pex-clk-supply = <&pci_clk_reg>;
|
||||
vdd-supply = <&pci_vdd_reg>;
|
||||
avdd-pex-supply = <&pci_vdd_reg>;
|
||||
vdd-pex-supply = <&pci_vdd_reg>;
|
||||
avdd-pex-pll-supply = <&pci_vdd_reg>;
|
||||
avdd-plle-supply = <&pci_vdd_reg>;
|
||||
vddio-pex-clk-supply = <&pci_clk_reg>;
|
||||
};
|
||||
|
||||
usb@c5008000 {
|
||||
|
|
|
@ -318,8 +318,12 @@
|
|||
|
||||
pcie-controller@80003000 {
|
||||
status = "okay";
|
||||
pex-clk-supply = <&pci_clk_reg>;
|
||||
vdd-supply = <&pci_vdd_reg>;
|
||||
|
||||
avdd-pex-supply = <&pci_vdd_reg>;
|
||||
vdd-pex-supply = <&pci_vdd_reg>;
|
||||
avdd-pex-pll-supply = <&pci_vdd_reg>;
|
||||
avdd-plle-supply = <&pci_vdd_reg>;
|
||||
vddio-pex-clk-supply = <&pci_clk_reg>;
|
||||
|
||||
pci@1,0 {
|
||||
status = "okay";
|
||||
|
|
|
@ -17,9 +17,15 @@
|
|||
|
||||
pcie-controller@00003000 {
|
||||
status = "okay";
|
||||
pex-clk-supply = <&sys_3v3_pexs_reg>;
|
||||
vdd-supply = <&ldo1_reg>;
|
||||
avdd-supply = <&ldo2_reg>;
|
||||
|
||||
avdd-pexa-supply = <&ldo1_reg>;
|
||||
vdd-pexa-supply = <&ldo1_reg>;
|
||||
avdd-pexb-supply = <&ldo1_reg>;
|
||||
vdd-pexb-supply = <&ldo1_reg>;
|
||||
avdd-pex-pll-supply = <&ldo1_reg>;
|
||||
avdd-plle-supply = <&ldo1_reg>;
|
||||
vddio-pex-ctl-supply = <&sys_3v3_reg>;
|
||||
hvdd-pex-supply = <&sys_3v3_pexs_reg>;
|
||||
|
||||
pci@1,0 {
|
||||
status = "okay";
|
||||
|
|
|
@ -38,9 +38,14 @@
|
|||
|
||||
pcie-controller@00003000 {
|
||||
status = "okay";
|
||||
pex-clk-supply = <&pex_hvdd_3v3_reg>;
|
||||
vdd-supply = <&ldo1_reg>;
|
||||
avdd-supply = <&ldo2_reg>;
|
||||
|
||||
/* AVDD_PEXA and VDD_PEXA inputs are grounded on Cardhu. */
|
||||
avdd-pexb-supply = <&ldo1_reg>;
|
||||
vdd-pexb-supply = <&ldo1_reg>;
|
||||
avdd-pex-pll-supply = <&ldo1_reg>;
|
||||
hvdd-pex-supply = <&pex_hvdd_3v3_reg>;
|
||||
vddio-pex-ctl-supply = <&sys_3v3_reg>;
|
||||
avdd-plle-supply = <&ldo2_reg>;
|
||||
|
||||
pci@1,0 {
|
||||
nvidia,num-lanes = <4>;
|
||||
|
|
|
@ -26,8 +26,6 @@ CONFIG_ARCH_OMAP=y
|
|||
CONFIG_ARCH_OMAP1=y
|
||||
CONFIG_OMAP_RESET_CLOCKS=y
|
||||
# CONFIG_OMAP_MUX is not set
|
||||
CONFIG_MAILBOX=y
|
||||
CONFIG_OMAP1_MBOX=y
|
||||
CONFIG_OMAP_32K_TIMER=y
|
||||
CONFIG_OMAP_DM_TIMER=y
|
||||
CONFIG_ARCH_OMAP730=y
|
||||
|
|
|
@ -11,13 +11,24 @@ CONFIG_ARCH_SPEAR13XX=y
|
|||
CONFIG_MACH_SPEAR1310=y
|
||||
CONFIG_MACH_SPEAR1340=y
|
||||
# CONFIG_SWP_EMULATE is not set
|
||||
CONFIG_PCI=y
|
||||
CONFIG_PCI_MSI=y
|
||||
CONFIG_PCIE_SPEAR13XX=y
|
||||
CONFIG_SMP=y
|
||||
# CONFIG_SMP_ON_UP is not set
|
||||
# CONFIG_ARM_CPU_TOPOLOGY is not set
|
||||
CONFIG_AEABI=y
|
||||
CONFIG_ARM_APPENDED_DTB=y
|
||||
CONFIG_ARM_ATAG_DTB_COMPAT=y
|
||||
CONFIG_VFP=y
|
||||
CONFIG_BINFMT_MISC=y
|
||||
CONFIG_NET=y
|
||||
CONFIG_UNIX=y
|
||||
CONFIG_INET=y
|
||||
CONFIG_IP_PNP=y
|
||||
CONFIG_IP_PNP_DHCP=y
|
||||
CONFIG_IP_PNP_BOOTP=y
|
||||
CONFIG_NET_IPIP=y
|
||||
CONFIG_UEVENT_HELPER_PATH="/sbin/hotplug"
|
||||
CONFIG_MTD=y
|
||||
CONFIG_MTD_OF_PARTS=y
|
||||
|
@ -27,6 +38,7 @@ CONFIG_MTD_NAND=y
|
|||
CONFIG_MTD_NAND_FSMC=y
|
||||
CONFIG_BLK_DEV_RAM=y
|
||||
CONFIG_BLK_DEV_RAM_SIZE=16384
|
||||
CONFIG_BLK_DEV_SD=y
|
||||
CONFIG_ATA=y
|
||||
# CONFIG_SATA_PMP is not set
|
||||
CONFIG_SATA_AHCI_PLATFORM=y
|
||||
|
@ -66,6 +78,7 @@ CONFIG_USB=y
|
|||
# CONFIG_USB_DEVICE_CLASS is not set
|
||||
CONFIG_USB_EHCI_HCD=y
|
||||
CONFIG_USB_OHCI_HCD=y
|
||||
CONFIG_USB_STORAGE=y
|
||||
CONFIG_MMC=y
|
||||
CONFIG_MMC_SDHCI=y
|
||||
CONFIG_MMC_SDHCI_SPEAR=y
|
||||
|
@ -79,11 +92,14 @@ CONFIG_EXT2_FS_SECURITY=y
|
|||
CONFIG_EXT3_FS=y
|
||||
CONFIG_EXT3_FS_SECURITY=y
|
||||
CONFIG_AUTOFS4_FS=m
|
||||
CONFIG_FUSE_FS=y
|
||||
CONFIG_MSDOS_FS=m
|
||||
CONFIG_VFAT_FS=m
|
||||
CONFIG_FAT_DEFAULT_IOCHARSET="ascii"
|
||||
CONFIG_TMPFS=y
|
||||
CONFIG_JFFS2_FS=y
|
||||
CONFIG_NFS_FS=y
|
||||
CONFIG_ROOT_NFS=y
|
||||
CONFIG_NLS_DEFAULT="utf8"
|
||||
CONFIG_NLS_CODEPAGE_437=y
|
||||
CONFIG_NLS_ASCII=m
|
||||
|
|
|
@ -200,6 +200,7 @@ static struct clk_lookup periph_clocks_lookups[] = {
|
|||
CLKDEV_CON_DEV_ID("spi_clk", "atmel_spi.1", &spi1_clk),
|
||||
CLKDEV_CON_DEV_ID("t0_clk", "atmel_tcb.0", &tcb_clk),
|
||||
CLKDEV_CON_DEV_ID(NULL, "i2c-at91sam9260.0", &twi_clk),
|
||||
CLKDEV_CON_DEV_ID(NULL, "at91sam9rl-pwm", &pwm_clk),
|
||||
/* fake hclk clock */
|
||||
CLKDEV_CON_DEV_ID("hclk", "at91_ohci", &ohci_clk),
|
||||
CLKDEV_CON_ID("pioA", &pioA_clk),
|
||||
|
|
|
@ -1131,9 +1131,7 @@ static void __init at91_add_device_watchdog(void) {}
|
|||
* PWM
|
||||
* --------------------------------------------------------------------*/
|
||||
|
||||
#if defined(CONFIG_ATMEL_PWM)
|
||||
static u32 pwm_mask;
|
||||
|
||||
#if IS_ENABLED(CONFIG_PWM_ATMEL)
|
||||
static struct resource pwm_resources[] = {
|
||||
[0] = {
|
||||
.start = AT91SAM9263_BASE_PWMC,
|
||||
|
@ -1148,11 +1146,8 @@ static struct resource pwm_resources[] = {
|
|||
};
|
||||
|
||||
static struct platform_device at91sam9263_pwm0_device = {
|
||||
.name = "atmel_pwm",
|
||||
.name = "at91sam9rl-pwm",
|
||||
.id = -1,
|
||||
.dev = {
|
||||
.platform_data = &pwm_mask,
|
||||
},
|
||||
.resource = pwm_resources,
|
||||
.num_resources = ARRAY_SIZE(pwm_resources),
|
||||
};
|
||||
|
@ -1171,8 +1166,6 @@ void __init at91_add_device_pwm(u32 mask)
|
|||
if (mask & (1 << AT91_PWM3))
|
||||
at91_set_B_periph(AT91_PIN_PB29, 1); /* enable PWM3 */
|
||||
|
||||
pwm_mask = mask;
|
||||
|
||||
platform_device_register(&at91sam9263_pwm0_device);
|
||||
}
|
||||
#else
|
||||
|
|
|
@ -252,6 +252,7 @@ static struct clk_lookup periph_clocks_lookups[] = {
|
|||
CLKDEV_CON_DEV_ID(NULL, "atmel_sha", &aestdessha_clk),
|
||||
CLKDEV_CON_DEV_ID(NULL, "atmel_tdes", &aestdessha_clk),
|
||||
CLKDEV_CON_DEV_ID(NULL, "atmel_aes", &aestdessha_clk),
|
||||
CLKDEV_CON_DEV_ID(NULL, "at91sam9rl-pwm", &pwm_clk),
|
||||
/* more usart lookup table for DT entries */
|
||||
CLKDEV_CON_DEV_ID("usart", "ffffee00.serial", &mck),
|
||||
CLKDEV_CON_DEV_ID("usart", "fff8c000.serial", &usart0_clk),
|
||||
|
|
|
@ -1334,9 +1334,7 @@ static void __init at91_add_device_watchdog(void) {}
|
|||
* PWM
|
||||
* --------------------------------------------------------------------*/
|
||||
|
||||
#if defined(CONFIG_ATMEL_PWM) || defined(CONFIG_ATMEL_PWM_MODULE)
|
||||
static u32 pwm_mask;
|
||||
|
||||
#if IS_ENABLED(CONFIG_PWM_ATMEL)
|
||||
static struct resource pwm_resources[] = {
|
||||
[0] = {
|
||||
.start = AT91SAM9G45_BASE_PWMC,
|
||||
|
@ -1351,11 +1349,8 @@ static struct resource pwm_resources[] = {
|
|||
};
|
||||
|
||||
static struct platform_device at91sam9g45_pwm0_device = {
|
||||
.name = "atmel_pwm",
|
||||
.name = "at91sam9rl-pwm",
|
||||
.id = -1,
|
||||
.dev = {
|
||||
.platform_data = &pwm_mask,
|
||||
},
|
||||
.resource = pwm_resources,
|
||||
.num_resources = ARRAY_SIZE(pwm_resources),
|
||||
};
|
||||
|
@ -1374,8 +1369,6 @@ void __init at91_add_device_pwm(u32 mask)
|
|||
if (mask & (1 << AT91_PWM3))
|
||||
at91_set_B_periph(AT91_PIN_PD0, 1); /* enable PWM3 */
|
||||
|
||||
pwm_mask = mask;
|
||||
|
||||
platform_device_register(&at91sam9g45_pwm0_device);
|
||||
}
|
||||
#else
|
||||
|
|
|
@ -200,6 +200,7 @@ static struct clk_lookup periph_clocks_lookups[] = {
|
|||
CLKDEV_CON_DEV_ID("pclk", "fffc4000.ssc", &ssc1_clk),
|
||||
CLKDEV_CON_DEV_ID(NULL, "i2c-at91sam9g20.0", &twi0_clk),
|
||||
CLKDEV_CON_DEV_ID(NULL, "i2c-at91sam9g20.1", &twi1_clk),
|
||||
CLKDEV_CON_DEV_ID(NULL, "at91sam9rl-pwm", &pwm_clk),
|
||||
CLKDEV_CON_ID("pioA", &pioA_clk),
|
||||
CLKDEV_CON_ID("pioB", &pioB_clk),
|
||||
CLKDEV_CON_ID("pioC", &pioC_clk),
|
||||
|
|
|
@ -799,9 +799,7 @@ static void __init at91_add_device_watchdog(void) {}
|
|||
* PWM
|
||||
* --------------------------------------------------------------------*/
|
||||
|
||||
#if defined(CONFIG_ATMEL_PWM)
|
||||
static u32 pwm_mask;
|
||||
|
||||
#if IS_ENABLED(CONFIG_PWM_ATMEL)
|
||||
static struct resource pwm_resources[] = {
|
||||
[0] = {
|
||||
.start = AT91SAM9RL_BASE_PWMC,
|
||||
|
@ -816,11 +814,8 @@ static struct resource pwm_resources[] = {
|
|||
};
|
||||
|
||||
static struct platform_device at91sam9rl_pwm0_device = {
|
||||
.name = "atmel_pwm",
|
||||
.name = "at91sam9rl-pwm",
|
||||
.id = -1,
|
||||
.dev = {
|
||||
.platform_data = &pwm_mask,
|
||||
},
|
||||
.resource = pwm_resources,
|
||||
.num_resources = ARRAY_SIZE(pwm_resources),
|
||||
};
|
||||
|
@ -839,8 +834,6 @@ void __init at91_add_device_pwm(u32 mask)
|
|||
if (mask & (1 << AT91_PWM3))
|
||||
at91_set_B_periph(AT91_PIN_PD8, 1); /* enable PWM3 */
|
||||
|
||||
pwm_mask = mask;
|
||||
|
||||
platform_device_register(&at91sam9rl_pwm0_device);
|
||||
}
|
||||
#else
|
||||
|
|
|
@ -32,6 +32,8 @@
|
|||
#include <linux/gpio_keys.h>
|
||||
#include <linux/input.h>
|
||||
#include <linux/leds.h>
|
||||
#include <linux/pwm.h>
|
||||
#include <linux/leds_pwm.h>
|
||||
|
||||
#include <video/atmel_lcdc.h>
|
||||
|
||||
|
@ -369,22 +371,48 @@ static struct gpio_led ek_leds[] = {
|
|||
.name = "ds3",
|
||||
.gpio = AT91_PIN_PB7,
|
||||
.default_trigger = "heartbeat",
|
||||
},
|
||||
#if !IS_ENABLED(CONFIG_LEDS_PWM)
|
||||
{
|
||||
.name = "ds1",
|
||||
.gpio = AT91_PIN_PB8,
|
||||
.active_low = 1,
|
||||
.default_trigger = "none",
|
||||
}
|
||||
#endif
|
||||
};
|
||||
|
||||
/*
|
||||
* PWM Leds
|
||||
*/
|
||||
static struct gpio_led ek_pwm_led[] = {
|
||||
/* For now only DS1 is PWM-driven (by pwm1) */
|
||||
{
|
||||
.name = "ds1",
|
||||
.gpio = 1, /* is PWM channel number */
|
||||
.active_low = 1,
|
||||
.default_trigger = "none",
|
||||
}
|
||||
static struct pwm_lookup pwm_lookup[] = {
|
||||
PWM_LOOKUP("at91sam9rl-pwm", 1, "leds_pwm", "ds1",
|
||||
5000, PWM_POLARITY_INVERSED),
|
||||
};
|
||||
|
||||
#if IS_ENABLED(CONFIG_LEDS_PWM)
|
||||
static struct led_pwm pwm_leds[] = {
|
||||
{
|
||||
.name = "ds1",
|
||||
.max_brightness = 255,
|
||||
},
|
||||
};
|
||||
|
||||
static struct led_pwm_platform_data pwm_data = {
|
||||
.num_leds = ARRAY_SIZE(pwm_leds),
|
||||
.leds = pwm_leds,
|
||||
};
|
||||
|
||||
static struct platform_device leds_pwm = {
|
||||
.name = "leds_pwm",
|
||||
.id = -1,
|
||||
.dev = {
|
||||
.platform_data = &pwm_data,
|
||||
},
|
||||
};
|
||||
#endif
|
||||
|
||||
|
||||
/*
|
||||
* CAN
|
||||
*/
|
||||
|
@ -403,6 +431,12 @@ static struct at91_can_data ek_can_data = {
|
|||
.transceiver_switch = sam9263ek_transceiver_switch,
|
||||
};
|
||||
|
||||
static struct platform_device *devices[] __initdata = {
|
||||
#if IS_ENABLED(CONFIG_LEDS_PWM)
|
||||
&leds_pwm,
|
||||
#endif
|
||||
};
|
||||
|
||||
static void __init ek_board_init(void)
|
||||
{
|
||||
/* Serial */
|
||||
|
@ -437,9 +471,14 @@ static void __init ek_board_init(void)
|
|||
at91_add_device_ac97(&ek_ac97_data);
|
||||
/* LEDs */
|
||||
at91_gpio_leds(ek_leds, ARRAY_SIZE(ek_leds));
|
||||
at91_pwm_leds(ek_pwm_led, ARRAY_SIZE(ek_pwm_led));
|
||||
pwm_add_table(pwm_lookup, ARRAY_SIZE(pwm_lookup));
|
||||
#if IS_ENABLED(CONFIG_LEDS_PWM)
|
||||
at91_add_device_pwm(1 << AT91_PWM1);
|
||||
#endif
|
||||
/* CAN */
|
||||
at91_add_device_can(&ek_can_data);
|
||||
/* Other platform devices */
|
||||
platform_add_devices(devices, ARRAY_SIZE(devices));
|
||||
}
|
||||
|
||||
MACHINE_START(AT91SAM9263EK, "Atmel AT91SAM9263-EK")
|
||||
|
|
|
@ -26,6 +26,8 @@
|
|||
#include <linux/leds.h>
|
||||
#include <linux/atmel-mci.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/pwm.h>
|
||||
#include <linux/leds_pwm.h>
|
||||
|
||||
#include <linux/platform_data/at91_adc.h>
|
||||
|
||||
|
@ -416,7 +418,7 @@ static struct gpio_led ek_leds[] = {
|
|||
.active_low = 1,
|
||||
.default_trigger = "nand-disk",
|
||||
},
|
||||
#if !(defined(CONFIG_LEDS_ATMEL_PWM) || defined(CONFIG_LEDS_ATMEL_PWM_MODULE))
|
||||
#if !IS_ENABLED(CONFIG_LEDS_PWM)
|
||||
{ /* "right" led, green, userled1, pwm1 */
|
||||
.name = "d7",
|
||||
.gpio = AT91_PIN_PD31,
|
||||
|
@ -430,22 +432,41 @@ static struct gpio_led ek_leds[] = {
|
|||
/*
|
||||
* PWM Leds
|
||||
*/
|
||||
static struct gpio_led ek_pwm_led[] = {
|
||||
#if defined(CONFIG_LEDS_ATMEL_PWM) || defined(CONFIG_LEDS_ATMEL_PWM_MODULE)
|
||||
{ /* "right" led, green, userled1, pwm1 */
|
||||
.name = "d7",
|
||||
.gpio = 1, /* is PWM channel number */
|
||||
.active_low = 1,
|
||||
.default_trigger = "none",
|
||||
},
|
||||
#endif
|
||||
static struct pwm_lookup pwm_lookup[] = {
|
||||
PWM_LOOKUP("at91sam9rl-pwm", 1, "leds_pwm", "d7",
|
||||
5000, PWM_POLARITY_INVERSED),
|
||||
};
|
||||
|
||||
#if IS_ENABLED(CONFIG_LEDS_PWM)
|
||||
static struct led_pwm pwm_leds[] = {
|
||||
{ /* "right" led, green, userled1, pwm1 */
|
||||
.name = "d7",
|
||||
.max_brightness = 255,
|
||||
},
|
||||
};
|
||||
|
||||
static struct led_pwm_platform_data pwm_data = {
|
||||
.num_leds = ARRAY_SIZE(pwm_leds),
|
||||
.leds = pwm_leds,
|
||||
};
|
||||
|
||||
static struct platform_device leds_pwm = {
|
||||
.name = "leds_pwm",
|
||||
.id = -1,
|
||||
.dev = {
|
||||
.platform_data = &pwm_data,
|
||||
},
|
||||
};
|
||||
#endif
|
||||
|
||||
static struct platform_device *devices[] __initdata = {
|
||||
#if defined(CONFIG_SOC_CAMERA_OV2640) || \
|
||||
defined(CONFIG_SOC_CAMERA_OV2640_MODULE)
|
||||
&isi_ov2640,
|
||||
#endif
|
||||
#if IS_ENABLED(CONFIG_LEDS_PWM)
|
||||
&leds_pwm,
|
||||
#endif
|
||||
};
|
||||
|
||||
static void __init ek_board_init(void)
|
||||
|
@ -486,7 +507,10 @@ static void __init ek_board_init(void)
|
|||
at91_add_device_ac97(&ek_ac97_data);
|
||||
/* LEDs */
|
||||
at91_gpio_leds(ek_leds, ARRAY_SIZE(ek_leds));
|
||||
at91_pwm_leds(ek_pwm_led, ARRAY_SIZE(ek_pwm_led));
|
||||
pwm_add_table(pwm_lookup, ARRAY_SIZE(pwm_lookup));
|
||||
#if IS_ENABLED(CONFIG_LEDS_PWM)
|
||||
at91_add_device_pwm(1 << AT91_PWM1);
|
||||
#endif
|
||||
/* Other platform devices */
|
||||
platform_add_devices(devices, ARRAY_SIZE(devices));
|
||||
}
|
||||
|
|
|
@ -123,6 +123,5 @@ extern void __init at91_add_device_can(struct at91_can_data *data);
|
|||
|
||||
/* LEDs */
|
||||
extern void __init at91_gpio_leds(struct gpio_led *leds, int nr);
|
||||
extern void __init at91_pwm_leds(struct gpio_led *leds, int nr);
|
||||
|
||||
#endif
|
||||
|
|
|
@ -54,40 +54,3 @@ void __init at91_gpio_leds(struct gpio_led *leds, int nr)
|
|||
void __init at91_gpio_leds(struct gpio_led *leds, int nr) {}
|
||||
#endif
|
||||
|
||||
|
||||
/* ------------------------------------------------------------------------- */
|
||||
|
||||
#if defined (CONFIG_LEDS_ATMEL_PWM)
|
||||
|
||||
/*
|
||||
* PWM Leds
|
||||
*/
|
||||
|
||||
static struct gpio_led_platform_data pwm_led_data;
|
||||
|
||||
static struct platform_device at91_pwm_leds_device = {
|
||||
.name = "leds-atmel-pwm",
|
||||
.id = -1,
|
||||
.dev.platform_data = &pwm_led_data,
|
||||
};
|
||||
|
||||
void __init at91_pwm_leds(struct gpio_led *leds, int nr)
|
||||
{
|
||||
int i;
|
||||
u32 pwm_mask = 0;
|
||||
|
||||
if (!nr)
|
||||
return;
|
||||
|
||||
for (i = 0; i < nr; i++)
|
||||
pwm_mask |= (1 << leds[i].gpio);
|
||||
|
||||
pwm_led_data.leds = leds;
|
||||
pwm_led_data.num_leds = nr;
|
||||
|
||||
at91_add_device_pwm(pwm_mask);
|
||||
platform_device_register(&at91_pwm_leds_device);
|
||||
}
|
||||
#else
|
||||
void __init at91_pwm_leds(struct gpio_led *leds, int nr){}
|
||||
#endif
|
||||
|
|
|
@ -19,6 +19,8 @@ config ARCH_SPEAR13XX
|
|||
select HAVE_ARM_SCU if SMP
|
||||
select HAVE_ARM_TWD if SMP
|
||||
select PINCTRL
|
||||
select MFD_SYSCON
|
||||
select MIGHT_HAVE_PCI
|
||||
help
|
||||
Supports for ARM's SPEAR13XX family
|
||||
|
||||
|
@ -27,12 +29,14 @@ if ARCH_SPEAR13XX
|
|||
config MACH_SPEAR1310
|
||||
bool "SPEAr1310 Machine support with Device Tree"
|
||||
select PINCTRL_SPEAR1310
|
||||
select PHY_ST_SPEAR1310_MIPHY
|
||||
help
|
||||
Supports ST SPEAr1310 machine configured via the device-tree
|
||||
|
||||
config MACH_SPEAR1340
|
||||
bool "SPEAr1340 Machine support with Device Tree"
|
||||
select PINCTRL_SPEAR1340
|
||||
select PHY_ST_SPEAR1340_MIPHY
|
||||
help
|
||||
Supports ST SPEAr1340 machine configured via the device-tree
|
||||
|
||||
|
|
|
@ -52,10 +52,10 @@
|
|||
#ifdef CONFIG_ARCH_SPEAR13XX
|
||||
|
||||
#define PERIP_GRP2_BASE UL(0xB3000000)
|
||||
#define VA_PERIP_GRP2_BASE IOMEM(0xFE000000)
|
||||
#define VA_PERIP_GRP2_BASE IOMEM(0xF9000000)
|
||||
#define MCIF_SDHCI_BASE UL(0xB3000000)
|
||||
#define SYSRAM0_BASE UL(0xB3800000)
|
||||
#define VA_SYSRAM0_BASE IOMEM(0xFE800000)
|
||||
#define VA_SYSRAM0_BASE IOMEM(0xF9800000)
|
||||
#define SYS_LOCATION (VA_SYSRAM0_BASE + 0x600)
|
||||
|
||||
#define PERIP_GRP1_BASE UL(0xE0000000)
|
||||
|
|
|
@ -13,136 +13,13 @@
|
|||
|
||||
#define pr_fmt(fmt) "SPEAr1340: " fmt
|
||||
|
||||
#include <linux/ahci_platform.h>
|
||||
#include <linux/amba/serial.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/of_platform.h>
|
||||
#include <asm/mach/arch.h>
|
||||
#include "generic.h"
|
||||
#include <mach/spear.h>
|
||||
|
||||
/* FIXME: Move SATA PHY code into a standalone driver */
|
||||
|
||||
/* Base addresses */
|
||||
#define SPEAR1340_SATA_BASE UL(0xB1000000)
|
||||
|
||||
/* Power Management Registers */
|
||||
#define SPEAR1340_PCM_CFG (VA_MISC_BASE + 0x100)
|
||||
#define SPEAR1340_PCM_WKUP_CFG (VA_MISC_BASE + 0x104)
|
||||
#define SPEAR1340_SWITCH_CTR (VA_MISC_BASE + 0x108)
|
||||
|
||||
#define SPEAR1340_PERIP1_SW_RST (VA_MISC_BASE + 0x318)
|
||||
#define SPEAR1340_PERIP2_SW_RST (VA_MISC_BASE + 0x31C)
|
||||
#define SPEAR1340_PERIP3_SW_RST (VA_MISC_BASE + 0x320)
|
||||
|
||||
/* PCIE - SATA configuration registers */
|
||||
#define SPEAR1340_PCIE_SATA_CFG (VA_MISC_BASE + 0x424)
|
||||
/* PCIE CFG MASks */
|
||||
#define SPEAR1340_PCIE_CFG_DEVICE_PRESENT (1 << 11)
|
||||
#define SPEAR1340_PCIE_CFG_POWERUP_RESET (1 << 10)
|
||||
#define SPEAR1340_PCIE_CFG_CORE_CLK_EN (1 << 9)
|
||||
#define SPEAR1340_PCIE_CFG_AUX_CLK_EN (1 << 8)
|
||||
#define SPEAR1340_SATA_CFG_TX_CLK_EN (1 << 4)
|
||||
#define SPEAR1340_SATA_CFG_RX_CLK_EN (1 << 3)
|
||||
#define SPEAR1340_SATA_CFG_POWERUP_RESET (1 << 2)
|
||||
#define SPEAR1340_SATA_CFG_PM_CLK_EN (1 << 1)
|
||||
#define SPEAR1340_PCIE_SATA_SEL_PCIE (0)
|
||||
#define SPEAR1340_PCIE_SATA_SEL_SATA (1)
|
||||
#define SPEAR1340_SATA_PCIE_CFG_MASK 0xF1F
|
||||
#define SPEAR1340_PCIE_CFG_VAL (SPEAR1340_PCIE_SATA_SEL_PCIE | \
|
||||
SPEAR1340_PCIE_CFG_AUX_CLK_EN | \
|
||||
SPEAR1340_PCIE_CFG_CORE_CLK_EN | \
|
||||
SPEAR1340_PCIE_CFG_POWERUP_RESET | \
|
||||
SPEAR1340_PCIE_CFG_DEVICE_PRESENT)
|
||||
#define SPEAR1340_SATA_CFG_VAL (SPEAR1340_PCIE_SATA_SEL_SATA | \
|
||||
SPEAR1340_SATA_CFG_PM_CLK_EN | \
|
||||
SPEAR1340_SATA_CFG_POWERUP_RESET | \
|
||||
SPEAR1340_SATA_CFG_RX_CLK_EN | \
|
||||
SPEAR1340_SATA_CFG_TX_CLK_EN)
|
||||
|
||||
#define SPEAR1340_PCIE_MIPHY_CFG (VA_MISC_BASE + 0x428)
|
||||
#define SPEAR1340_MIPHY_OSC_BYPASS_EXT (1 << 31)
|
||||
#define SPEAR1340_MIPHY_CLK_REF_DIV2 (1 << 27)
|
||||
#define SPEAR1340_MIPHY_CLK_REF_DIV4 (2 << 27)
|
||||
#define SPEAR1340_MIPHY_CLK_REF_DIV8 (3 << 27)
|
||||
#define SPEAR1340_MIPHY_PLL_RATIO_TOP(x) (x << 0)
|
||||
#define SPEAR1340_PCIE_SATA_MIPHY_CFG_SATA \
|
||||
(SPEAR1340_MIPHY_OSC_BYPASS_EXT | \
|
||||
SPEAR1340_MIPHY_CLK_REF_DIV2 | \
|
||||
SPEAR1340_MIPHY_PLL_RATIO_TOP(60))
|
||||
#define SPEAR1340_PCIE_SATA_MIPHY_CFG_SATA_25M_CRYSTAL_CLK \
|
||||
(SPEAR1340_MIPHY_PLL_RATIO_TOP(120))
|
||||
#define SPEAR1340_PCIE_SATA_MIPHY_CFG_PCIE \
|
||||
(SPEAR1340_MIPHY_OSC_BYPASS_EXT | \
|
||||
SPEAR1340_MIPHY_PLL_RATIO_TOP(25))
|
||||
|
||||
/* SATA device registration */
|
||||
static int sata_miphy_init(struct device *dev, void __iomem *addr)
|
||||
{
|
||||
writel(SPEAR1340_SATA_CFG_VAL, SPEAR1340_PCIE_SATA_CFG);
|
||||
writel(SPEAR1340_PCIE_SATA_MIPHY_CFG_SATA_25M_CRYSTAL_CLK,
|
||||
SPEAR1340_PCIE_MIPHY_CFG);
|
||||
/* Switch on sata power domain */
|
||||
writel((readl(SPEAR1340_PCM_CFG) | (0x800)), SPEAR1340_PCM_CFG);
|
||||
msleep(20);
|
||||
/* Disable PCIE SATA Controller reset */
|
||||
writel((readl(SPEAR1340_PERIP1_SW_RST) & (~0x1000)),
|
||||
SPEAR1340_PERIP1_SW_RST);
|
||||
msleep(20);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void sata_miphy_exit(struct device *dev)
|
||||
{
|
||||
writel(0, SPEAR1340_PCIE_SATA_CFG);
|
||||
writel(0, SPEAR1340_PCIE_MIPHY_CFG);
|
||||
|
||||
/* Enable PCIE SATA Controller reset */
|
||||
writel((readl(SPEAR1340_PERIP1_SW_RST) | (0x1000)),
|
||||
SPEAR1340_PERIP1_SW_RST);
|
||||
msleep(20);
|
||||
/* Switch off sata power domain */
|
||||
writel((readl(SPEAR1340_PCM_CFG) & (~0x800)), SPEAR1340_PCM_CFG);
|
||||
msleep(20);
|
||||
}
|
||||
|
||||
static int sata_suspend(struct device *dev)
|
||||
{
|
||||
if (dev->power.power_state.event == PM_EVENT_FREEZE)
|
||||
return 0;
|
||||
|
||||
sata_miphy_exit(dev);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int sata_resume(struct device *dev)
|
||||
{
|
||||
if (dev->power.power_state.event == PM_EVENT_THAW)
|
||||
return 0;
|
||||
|
||||
return sata_miphy_init(dev, NULL);
|
||||
}
|
||||
|
||||
static struct ahci_platform_data sata_pdata = {
|
||||
.init = sata_miphy_init,
|
||||
.exit = sata_miphy_exit,
|
||||
.suspend = sata_suspend,
|
||||
.resume = sata_resume,
|
||||
};
|
||||
|
||||
/* Add SPEAr1340 auxdata to pass platform data */
|
||||
static struct of_dev_auxdata spear1340_auxdata_lookup[] __initdata = {
|
||||
OF_DEV_AUXDATA("snps,spear-ahci", SPEAR1340_SATA_BASE, NULL,
|
||||
&sata_pdata),
|
||||
{}
|
||||
};
|
||||
|
||||
static void __init spear1340_dt_init(void)
|
||||
{
|
||||
of_platform_populate(NULL, of_default_bus_match_table,
|
||||
spear1340_auxdata_lookup, NULL);
|
||||
of_platform_populate(NULL, of_default_bus_match_table, NULL, NULL);
|
||||
platform_device_register_simple("spear-cpufreq", -1, NULL, 0);
|
||||
}
|
||||
|
||||
|
|
|
@ -52,7 +52,7 @@ void __init spear13xx_l2x0_init(void)
|
|||
/*
|
||||
* Following will create 16MB static virtual/physical mappings
|
||||
* PHYSICAL VIRTUAL
|
||||
* 0xB3000000 0xFE000000
|
||||
* 0xB3000000 0xF9000000
|
||||
* 0xE0000000 0xFD000000
|
||||
* 0xEC000000 0xFC000000
|
||||
* 0xED000000 0xFB000000
|
||||
|
|
|
@ -17,6 +17,8 @@
|
|||
#include <linux/types.h>
|
||||
#include <linux/fb.h>
|
||||
#include <linux/leds.h>
|
||||
#include <linux/pwm.h>
|
||||
#include <linux/leds_pwm.h>
|
||||
#include <linux/input.h>
|
||||
#include <linux/gpio_keys.h>
|
||||
#include <linux/atmel_serial.h>
|
||||
|
@ -155,21 +157,28 @@ static struct platform_device rmt_ts_device = {
|
|||
|
||||
#ifdef CONFIG_BOARD_MRMT_BL_PWM
|
||||
/* PWM LEDs: LCD Backlight, etc */
|
||||
static struct gpio_led rmt_pwm_led[] = {
|
||||
/* here the "gpio" is actually a PWM channel */
|
||||
{ .name = "backlight", .gpio = PWM_CH_BL, },
|
||||
static struct pwm_lookup pwm_lookup[] = {
|
||||
PWM_LOOKUP("at91sam9rl-pwm", PWM_CH_BL, "leds_pwm", "ds1",
|
||||
5000, PWM_POLARITY_INVERSED),
|
||||
};
|
||||
|
||||
static struct gpio_led_platform_data rmt_pwm_led_data = {
|
||||
.num_leds = ARRAY_SIZE(rmt_pwm_led),
|
||||
.leds = rmt_pwm_led,
|
||||
static struct led_pwm pwm_leds[] = {
|
||||
{
|
||||
.name = "backlight",
|
||||
.max_brightness = 255,
|
||||
},
|
||||
};
|
||||
|
||||
static struct platform_device rmt_pwm_led_dev = {
|
||||
.name = "leds-atmel-pwm",
|
||||
.id = -1,
|
||||
.dev = {
|
||||
.platform_data = &rmt_pwm_led_data,
|
||||
static struct led_pwm_platform_data pwm_data = {
|
||||
.num_leds = ARRAY_SIZE(pwm_leds),
|
||||
.leds = pwm_leds,
|
||||
};
|
||||
|
||||
static struct platform_device leds_pwm = {
|
||||
.name = "leds_pwm",
|
||||
.id = -1,
|
||||
.dev = {
|
||||
.platform_data = &pwm_data,
|
||||
},
|
||||
};
|
||||
#endif
|
||||
|
@ -325,7 +334,8 @@ static int __init mrmt1_init(void)
|
|||
#ifdef CONFIG_BOARD_MRMT_BL_PWM
|
||||
/* Use PWM for Backlight controls */
|
||||
at32_add_device_pwm(1 << PWM_CH_BL);
|
||||
platform_device_register(&rmt_pwm_led_dev);
|
||||
pwm_add_table(pwm_lookup, ARRAY_SIZE(pwm_lookup));
|
||||
platform_device_register(&leds_pwm);
|
||||
#else
|
||||
/* Backlight always on */
|
||||
udelay( 1 );
|
||||
|
|
|
@ -18,7 +18,10 @@
|
|||
#include <linux/gpio.h>
|
||||
#include <linux/leds.h>
|
||||
#include <linux/atmel-mci.h>
|
||||
#include <linux/atmel-pwm-bl.h>
|
||||
#include <linux/pwm.h>
|
||||
#include <linux/pwm_backlight.h>
|
||||
#include <linux/regulator/fixed.h>
|
||||
#include <linux/regulator/machine.h>
|
||||
#include <linux/spi/spi.h>
|
||||
#include <linux/spi/ads7846.h>
|
||||
|
||||
|
@ -33,6 +36,8 @@
|
|||
#include <mach/board.h>
|
||||
#include <mach/portmux.h>
|
||||
|
||||
#define PWM_BL_CH 2
|
||||
|
||||
/* Oscillator frequencies. These are board-specific */
|
||||
unsigned long at32_board_osc_rates[3] = {
|
||||
[0] = 32768, /* 32.768 kHz on RTC osc */
|
||||
|
@ -227,29 +232,36 @@ void __init favr32_setup_leds(void)
|
|||
platform_device_register(&favr32_led_dev);
|
||||
}
|
||||
|
||||
static struct atmel_pwm_bl_platform_data atmel_pwm_bl_pdata = {
|
||||
.pwm_channel = 2,
|
||||
.pwm_frequency = 200000,
|
||||
.pwm_compare_max = 345,
|
||||
.pwm_duty_max = 345,
|
||||
.pwm_duty_min = 90,
|
||||
.pwm_active_low = 1,
|
||||
.gpio_on = GPIO_PIN_PA(28),
|
||||
.on_active_low = 0,
|
||||
static struct pwm_lookup pwm_lookup[] = {
|
||||
PWM_LOOKUP("at91sam9rl-pwm", PWM_BL_CH, "pwm-backlight.0", NULL,
|
||||
5000, PWM_POLARITY_INVERSED),
|
||||
};
|
||||
|
||||
static struct platform_device atmel_pwm_bl_dev = {
|
||||
.name = "atmel-pwm-bl",
|
||||
.id = 0,
|
||||
.dev = {
|
||||
.platform_data = &atmel_pwm_bl_pdata,
|
||||
static struct regulator_consumer_supply fixed_power_consumers[] = {
|
||||
REGULATOR_SUPPLY("power", "pwm-backlight.0"),
|
||||
};
|
||||
|
||||
static struct platform_pwm_backlight_data pwm_bl_data = {
|
||||
.enable_gpio = GPIO_PIN_PA(28),
|
||||
.max_brightness = 255,
|
||||
.dft_brightness = 255,
|
||||
.lth_brightness = 50,
|
||||
};
|
||||
|
||||
static struct platform_device pwm_bl_device = {
|
||||
.name = "pwm-backlight",
|
||||
.dev = {
|
||||
.platform_data = &pwm_bl_data,
|
||||
},
|
||||
};
|
||||
|
||||
static void __init favr32_setup_atmel_pwm_bl(void)
|
||||
{
|
||||
platform_device_register(&atmel_pwm_bl_dev);
|
||||
at32_select_gpio(atmel_pwm_bl_pdata.gpio_on, 0);
|
||||
pwm_add_table(pwm_lookup, ARRAY_SIZE(pwm_lookup));
|
||||
regulator_register_always_on(0, "fixed", fixed_power_consumers,
|
||||
ARRAY_SIZE(fixed_power_consumers), 3300000);
|
||||
platform_device_register(&pwm_bl_device);
|
||||
at32_select_gpio(pwm_bl_data.enable_gpio, 0);
|
||||
}
|
||||
|
||||
void __init setup_board(void)
|
||||
|
@ -339,7 +351,7 @@ static int __init favr32_init(void)
|
|||
|
||||
set_abdac_rate(at32_add_device_abdac(0, &abdac0_data));
|
||||
|
||||
at32_add_device_pwm(1 << atmel_pwm_bl_pdata.pwm_channel);
|
||||
at32_add_device_pwm(1 << PWM_BL_CH);
|
||||
at32_add_device_spi(1, spi1_board_info, ARRAY_SIZE(spi1_board_info));
|
||||
at32_add_device_mci(0, &mci0_data);
|
||||
at32_add_device_usba(0, NULL);
|
||||
|
|
|
@ -22,6 +22,8 @@
|
|||
#include <linux/irq.h>
|
||||
#include <linux/fb.h>
|
||||
#include <linux/atmel-mci.h>
|
||||
#include <linux/pwm.h>
|
||||
#include <linux/leds_pwm.h>
|
||||
|
||||
#include <asm/io.h>
|
||||
#include <asm/setup.h>
|
||||
|
@ -167,24 +169,29 @@ static struct i2c_board_info __initdata i2c_info[] = {
|
|||
},
|
||||
};
|
||||
|
||||
#ifdef CONFIG_LEDS_ATMEL_PWM
|
||||
static struct gpio_led stk_pwm_led[] = {
|
||||
#if IS_ENABLED(CONFIG_LEDS_PWM)
|
||||
static struct pwm_lookup pwm_lookup[] = {
|
||||
PWM_LOOKUP("at91sam9rl-pwm", 0, "leds_pwm", "backlight",
|
||||
5000, PWM_POLARITY_NORMAL),
|
||||
};
|
||||
|
||||
static struct led_pwm pwm_leds[] = {
|
||||
{
|
||||
.name = "backlight",
|
||||
.gpio = 0, /* PWM channel 0 (LCD backlight) */
|
||||
.max_brightness = 255,
|
||||
},
|
||||
};
|
||||
|
||||
static struct gpio_led_platform_data stk_pwm_led_data = {
|
||||
.num_leds = ARRAY_SIZE(stk_pwm_led),
|
||||
.leds = stk_pwm_led,
|
||||
static struct led_pwm_platform_data pwm_data = {
|
||||
.num_leds = ARRAY_SIZE(pwm_leds),
|
||||
.leds = pwm_leds,
|
||||
};
|
||||
|
||||
static struct platform_device stk_pwm_led_dev = {
|
||||
.name = "leds-atmel-pwm",
|
||||
.id = -1,
|
||||
.dev = {
|
||||
.platform_data = &stk_pwm_led_data,
|
||||
static struct platform_device leds_pwm = {
|
||||
.name = "leds_pwm",
|
||||
.id = -1,
|
||||
.dev = {
|
||||
.platform_data = &pwm_data,
|
||||
},
|
||||
};
|
||||
#endif
|
||||
|
@ -278,9 +285,10 @@ static int __init merisc_init(void)
|
|||
|
||||
at32_add_device_mci(0, &mci0_data);
|
||||
|
||||
#ifdef CONFIG_LEDS_ATMEL_PWM
|
||||
#if IS_ENABLED(CONFIG_LEDS_PWM)
|
||||
pwm_add_table(pwm_lookup, ARRAY_SIZE(pwm_lookup));
|
||||
at32_add_device_pwm((1 << 0) | (1 << 2));
|
||||
platform_device_register(&stk_pwm_led_dev);
|
||||
platform_device_register(&leds_pwm);
|
||||
#else
|
||||
at32_add_device_pwm((1 << 2));
|
||||
#endif
|
||||
|
|
|
@ -56,7 +56,6 @@ CONFIG_MTD_CFI_AMDSTD=y
|
|||
CONFIG_MTD_PHYSMAP=y
|
||||
CONFIG_MTD_DATAFLASH=y
|
||||
CONFIG_BLK_DEV_LOOP=y
|
||||
CONFIG_ATMEL_PWM=y
|
||||
CONFIG_NETDEVICES=y
|
||||
CONFIG_NET_ETHERNET=y
|
||||
CONFIG_MACB=y
|
||||
|
@ -104,8 +103,8 @@ CONFIG_MMC=y
|
|||
CONFIG_MMC_ATMELMCI=y
|
||||
CONFIG_NEW_LEDS=y
|
||||
CONFIG_LEDS_CLASS=y
|
||||
CONFIG_LEDS_ATMEL_PWM=y
|
||||
CONFIG_LEDS_GPIO=y
|
||||
CONFIG_LEDS_PWM=y
|
||||
CONFIG_LEDS_TRIGGERS=y
|
||||
CONFIG_LEDS_TRIGGER_TIMER=y
|
||||
CONFIG_LEDS_TRIGGER_HEARTBEAT=y
|
||||
|
@ -114,6 +113,8 @@ CONFIG_RTC_DRV_S35390A=m
|
|||
CONFIG_RTC_DRV_AT32AP700X=m
|
||||
CONFIG_DMADEVICES=y
|
||||
CONFIG_UIO=y
|
||||
CONFIG_PWM=y
|
||||
CONFIG_PWM_ATMEL=y
|
||||
CONFIG_EXT2_FS=y
|
||||
CONFIG_EXT2_FS_XATTR=y
|
||||
CONFIG_EXT3_FS=y
|
||||
|
|
|
@ -64,7 +64,6 @@ CONFIG_BLK_DEV_LOOP=m
|
|||
CONFIG_BLK_DEV_NBD=m
|
||||
CONFIG_BLK_DEV_RAM=m
|
||||
CONFIG_MISC_DEVICES=y
|
||||
CONFIG_ATMEL_PWM=m
|
||||
CONFIG_ATMEL_TCLIB=y
|
||||
CONFIG_ATMEL_SSC=m
|
||||
# CONFIG_SCSI_PROC_FS is not set
|
||||
|
@ -133,14 +132,16 @@ CONFIG_MMC_TEST=m
|
|||
CONFIG_MMC_ATMELMCI=y
|
||||
CONFIG_NEW_LEDS=y
|
||||
CONFIG_LEDS_CLASS=y
|
||||
CONFIG_LEDS_ATMEL_PWM=m
|
||||
CONFIG_LEDS_GPIO=m
|
||||
CONFIG_LEDS_PWM=m
|
||||
CONFIG_LEDS_TRIGGERS=y
|
||||
CONFIG_LEDS_TRIGGER_TIMER=m
|
||||
CONFIG_LEDS_TRIGGER_HEARTBEAT=m
|
||||
CONFIG_RTC_CLASS=y
|
||||
CONFIG_RTC_DRV_AT32AP700X=y
|
||||
CONFIG_DMADEVICES=y
|
||||
CONFIG_PWM=y
|
||||
CONFIG_PWM_ATMEL=m
|
||||
CONFIG_EXT2_FS=y
|
||||
CONFIG_EXT3_FS=y
|
||||
# CONFIG_EXT3_DEFAULTS_TO_ORDERED is not set
|
||||
|
|
|
@ -53,7 +53,6 @@ CONFIG_BLK_DEV_LOOP=m
|
|||
CONFIG_BLK_DEV_NBD=m
|
||||
CONFIG_BLK_DEV_RAM=m
|
||||
CONFIG_MISC_DEVICES=y
|
||||
CONFIG_ATMEL_PWM=m
|
||||
CONFIG_ATMEL_TCLIB=y
|
||||
CONFIG_ATMEL_SSC=m
|
||||
# CONFIG_SCSI_PROC_FS is not set
|
||||
|
@ -112,14 +111,16 @@ CONFIG_MMC_TEST=m
|
|||
CONFIG_MMC_ATMELMCI=y
|
||||
CONFIG_NEW_LEDS=y
|
||||
CONFIG_LEDS_CLASS=y
|
||||
CONFIG_LEDS_ATMEL_PWM=m
|
||||
CONFIG_LEDS_GPIO=m
|
||||
CONFIG_LEDS_PWM=m
|
||||
CONFIG_LEDS_TRIGGERS=y
|
||||
CONFIG_LEDS_TRIGGER_TIMER=m
|
||||
CONFIG_LEDS_TRIGGER_HEARTBEAT=m
|
||||
CONFIG_RTC_CLASS=y
|
||||
CONFIG_RTC_DRV_AT32AP700X=y
|
||||
CONFIG_DMADEVICES=y
|
||||
CONFIG_PWM=y
|
||||
CONFIG_PWM_ATMEL=m
|
||||
CONFIG_EXT2_FS=y
|
||||
CONFIG_EXT3_FS=y
|
||||
# CONFIG_EXT3_DEFAULTS_TO_ORDERED is not set
|
||||
|
|
|
@ -53,7 +53,6 @@ CONFIG_BLK_DEV_LOOP=m
|
|||
CONFIG_BLK_DEV_NBD=m
|
||||
CONFIG_BLK_DEV_RAM=m
|
||||
CONFIG_MISC_DEVICES=y
|
||||
CONFIG_ATMEL_PWM=m
|
||||
CONFIG_ATMEL_TCLIB=y
|
||||
CONFIG_ATMEL_SSC=m
|
||||
# CONFIG_SCSI_PROC_FS is not set
|
||||
|
@ -111,14 +110,16 @@ CONFIG_MMC_TEST=m
|
|||
CONFIG_MMC_ATMELMCI=y
|
||||
CONFIG_NEW_LEDS=y
|
||||
CONFIG_LEDS_CLASS=y
|
||||
CONFIG_LEDS_ATMEL_PWM=m
|
||||
CONFIG_LEDS_GPIO=m
|
||||
CONFIG_LEDS_PWM=m
|
||||
CONFIG_LEDS_TRIGGERS=y
|
||||
CONFIG_LEDS_TRIGGER_TIMER=m
|
||||
CONFIG_LEDS_TRIGGER_HEARTBEAT=m
|
||||
CONFIG_RTC_CLASS=y
|
||||
CONFIG_RTC_DRV_AT32AP700X=y
|
||||
CONFIG_DMADEVICES=y
|
||||
CONFIG_PWM=y
|
||||
CONFIG_PWM_ATMEL=m
|
||||
CONFIG_EXT2_FS=y
|
||||
CONFIG_EXT3_FS=y
|
||||
# CONFIG_EXT3_DEFAULTS_TO_ORDERED is not set
|
||||
|
|
|
@ -67,7 +67,6 @@ CONFIG_BLK_DEV_LOOP=m
|
|||
CONFIG_BLK_DEV_NBD=m
|
||||
CONFIG_BLK_DEV_RAM=m
|
||||
CONFIG_MISC_DEVICES=y
|
||||
CONFIG_ATMEL_PWM=m
|
||||
CONFIG_ATMEL_TCLIB=y
|
||||
CONFIG_ATMEL_SSC=m
|
||||
# CONFIG_SCSI_PROC_FS is not set
|
||||
|
@ -136,14 +135,16 @@ CONFIG_MMC_TEST=m
|
|||
CONFIG_MMC_ATMELMCI=y
|
||||
CONFIG_NEW_LEDS=y
|
||||
CONFIG_LEDS_CLASS=y
|
||||
CONFIG_LEDS_ATMEL_PWM=m
|
||||
CONFIG_LEDS_GPIO=m
|
||||
CONFIG_LEDS_PWM=m
|
||||
CONFIG_LEDS_TRIGGERS=y
|
||||
CONFIG_LEDS_TRIGGER_TIMER=m
|
||||
CONFIG_LEDS_TRIGGER_HEARTBEAT=m
|
||||
CONFIG_RTC_CLASS=y
|
||||
CONFIG_RTC_DRV_AT32AP700X=y
|
||||
CONFIG_DMADEVICES=y
|
||||
CONFIG_PWM=y
|
||||
CONFIG_PWM_ATMEL=m
|
||||
CONFIG_EXT2_FS=y
|
||||
CONFIG_EXT3_FS=y
|
||||
# CONFIG_EXT3_DEFAULTS_TO_ORDERED is not set
|
||||
|
|
|
@ -67,7 +67,6 @@ CONFIG_MTD_PHYSMAP=y
|
|||
CONFIG_BLK_DEV_LOOP=m
|
||||
CONFIG_BLK_DEV_NBD=m
|
||||
CONFIG_BLK_DEV_RAM=m
|
||||
CONFIG_ATMEL_PWM=m
|
||||
CONFIG_ATMEL_TCLIB=y
|
||||
CONFIG_ATMEL_SSC=m
|
||||
CONFIG_NETDEVICES=y
|
||||
|
@ -108,7 +107,7 @@ CONFIG_FB=y
|
|||
CONFIG_FB_ATMEL=y
|
||||
CONFIG_BACKLIGHT_LCD_SUPPORT=y
|
||||
# CONFIG_LCD_CLASS_DEVICE is not set
|
||||
CONFIG_BACKLIGHT_ATMEL_PWM=m
|
||||
CONFIG_BACKLIGHT_PWM=m
|
||||
CONFIG_SOUND=m
|
||||
CONFIG_SOUND_PRIME=m
|
||||
# CONFIG_HID_SUPPORT is not set
|
||||
|
@ -123,7 +122,6 @@ CONFIG_MMC=y
|
|||
CONFIG_MMC_ATMELMCI=y
|
||||
CONFIG_NEW_LEDS=y
|
||||
CONFIG_LEDS_CLASS=y
|
||||
CONFIG_LEDS_ATMEL_PWM=m
|
||||
CONFIG_LEDS_GPIO=y
|
||||
CONFIG_LEDS_TRIGGERS=y
|
||||
CONFIG_LEDS_TRIGGER_TIMER=y
|
||||
|
@ -132,6 +130,8 @@ CONFIG_LEDS_TRIGGER_DEFAULT_ON=y
|
|||
CONFIG_RTC_CLASS=y
|
||||
CONFIG_RTC_DRV_AT32AP700X=y
|
||||
CONFIG_DMADEVICES=y
|
||||
CONFIG_PWM=y
|
||||
CONFIG_PWM_ATMEL=y
|
||||
CONFIG_EXT2_FS=y
|
||||
CONFIG_EXT3_FS=y
|
||||
# CONFIG_EXT3_FS_XATTR is not set
|
||||
|
|
|
@ -55,7 +55,6 @@ CONFIG_MTD_ABSENT=y
|
|||
CONFIG_MTD_PHYSMAP=y
|
||||
CONFIG_MTD_BLOCK2MTD=y
|
||||
CONFIG_BLK_DEV_LOOP=y
|
||||
CONFIG_ATMEL_PWM=y
|
||||
CONFIG_ATMEL_SSC=y
|
||||
CONFIG_SCSI=y
|
||||
CONFIG_BLK_DEV_SD=y
|
||||
|
@ -103,12 +102,14 @@ CONFIG_MMC=y
|
|||
CONFIG_MMC_ATMELMCI=y
|
||||
CONFIG_NEW_LEDS=y
|
||||
CONFIG_LEDS_CLASS=y
|
||||
CONFIG_LEDS_ATMEL_PWM=y
|
||||
CONFIG_LEDS_PWM=y
|
||||
CONFIG_RTC_CLASS=y
|
||||
# CONFIG_RTC_HCTOSYS is not set
|
||||
CONFIG_RTC_DRV_PCF8563=y
|
||||
CONFIG_DMADEVICES=y
|
||||
CONFIG_UIO=y
|
||||
CONFIG_PWM=y
|
||||
CONFIG_PWM_ATMEL=m
|
||||
CONFIG_EXT2_FS=y
|
||||
# CONFIG_DNOTIFY is not set
|
||||
CONFIG_FUSE_FS=y
|
||||
|
|
|
@ -1553,7 +1553,7 @@ static struct resource atmel_pwm0_resource[] __initdata = {
|
|||
IRQ(24),
|
||||
};
|
||||
static struct clk atmel_pwm0_mck = {
|
||||
.name = "pwm_clk",
|
||||
.name = "at91sam9rl-pwm",
|
||||
.parent = &pbb_clk,
|
||||
.mode = pbb_clk_mode,
|
||||
.get_rate = pbb_clk_get_rate,
|
||||
|
@ -1568,7 +1568,7 @@ struct platform_device *__init at32_add_device_pwm(u32 mask)
|
|||
if (!mask)
|
||||
return NULL;
|
||||
|
||||
pdev = platform_device_alloc("atmel_pwm", 0);
|
||||
pdev = platform_device_alloc("at91sam9rl-pwm", 0);
|
||||
if (!pdev)
|
||||
return NULL;
|
||||
|
||||
|
@ -1576,9 +1576,6 @@ struct platform_device *__init at32_add_device_pwm(u32 mask)
|
|||
ARRAY_SIZE(atmel_pwm0_resource)))
|
||||
goto out_free_pdev;
|
||||
|
||||
if (platform_device_add_data(pdev, &mask, sizeof(mask)))
|
||||
goto out_free_pdev;
|
||||
|
||||
pin_mask = 0;
|
||||
if (mask & (1 << 0))
|
||||
pin_mask |= (1 << 28);
|
||||
|
|
|
@ -50,6 +50,14 @@ config ARM_CCI
|
|||
Driver supporting the CCI cache coherent interconnect for ARM
|
||||
platforms.
|
||||
|
||||
config ARM_CCN
|
||||
bool "ARM CCN driver support"
|
||||
depends on ARM || ARM64
|
||||
depends on PERF_EVENTS
|
||||
help
|
||||
PMU (perf) driver supporting the ARM CCN (Cache Coherent Network)
|
||||
interconnect.
|
||||
|
||||
config VEXPRESS_CONFIG
|
||||
bool "Versatile Express configuration bus"
|
||||
default y if ARCH_VEXPRESS
|
||||
|
|
|
@ -9,7 +9,9 @@ obj-$(CONFIG_OMAP_OCP2SCP) += omap-ocp2scp.o
|
|||
|
||||
# Interconnect bus driver for OMAP SoCs.
|
||||
obj-$(CONFIG_OMAP_INTERCONNECT) += omap_l3_smx.o omap_l3_noc.o
|
||||
# CCI cache coherent interconnect for ARM platforms
|
||||
|
||||
# Interconnect bus drivers for ARM platforms
|
||||
obj-$(CONFIG_ARM_CCI) += arm-cci.o
|
||||
obj-$(CONFIG_ARM_CCN) += arm-ccn.o
|
||||
|
||||
obj-$(CONFIG_VEXPRESS_CONFIG) += vexpress-config.o
|
||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -32,14 +32,6 @@ config LEDS_88PM860X
|
|||
This option enables support for on-chip LED drivers found on Marvell
|
||||
Semiconductor 88PM8606 PMIC.
|
||||
|
||||
config LEDS_ATMEL_PWM
|
||||
tristate "LED Support using Atmel PWM outputs"
|
||||
depends on LEDS_CLASS
|
||||
depends on ATMEL_PWM
|
||||
help
|
||||
This option enables support for LEDs driven using outputs
|
||||
of the dedicated PWM controller found on newer Atmel SOCs.
|
||||
|
||||
config LEDS_LM3530
|
||||
tristate "LCD Backlight driver for LM3530"
|
||||
depends on LEDS_CLASS
|
||||
|
|
|
@ -6,7 +6,6 @@ obj-$(CONFIG_LEDS_TRIGGERS) += led-triggers.o
|
|||
|
||||
# LED Platform Drivers
|
||||
obj-$(CONFIG_LEDS_88PM860X) += leds-88pm860x.o
|
||||
obj-$(CONFIG_LEDS_ATMEL_PWM) += leds-atmel-pwm.o
|
||||
obj-$(CONFIG_LEDS_BD2802) += leds-bd2802.o
|
||||
obj-$(CONFIG_LEDS_LOCOMO) += leds-locomo.o
|
||||
obj-$(CONFIG_LEDS_LM3530) += leds-lm3530.o
|
||||
|
|
|
@ -1,149 +0,0 @@
|
|||
#include <linux/kernel.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/leds.h>
|
||||
#include <linux/io.h>
|
||||
#include <linux/atmel_pwm.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/module.h>
|
||||
|
||||
|
||||
struct pwmled {
|
||||
struct led_classdev cdev;
|
||||
struct pwm_channel pwmc;
|
||||
struct gpio_led *desc;
|
||||
u32 mult;
|
||||
u8 active_low;
|
||||
};
|
||||
|
||||
|
||||
/*
|
||||
* For simplicity, we use "brightness" as if it were a linear function
|
||||
* of PWM duty cycle. However, a logarithmic function of duty cycle is
|
||||
* probably a better match for perceived brightness: two is half as bright
|
||||
* as four, four is half as bright as eight, etc
|
||||
*/
|
||||
static void pwmled_brightness(struct led_classdev *cdev, enum led_brightness b)
|
||||
{
|
||||
struct pwmled *led;
|
||||
|
||||
/* update the duty cycle for the *next* period */
|
||||
led = container_of(cdev, struct pwmled, cdev);
|
||||
pwm_channel_writel(&led->pwmc, PWM_CUPD, led->mult * (unsigned) b);
|
||||
}
|
||||
|
||||
/*
|
||||
* NOTE: we reuse the platform_data structure of GPIO leds,
|
||||
* but repurpose its "gpio" number as a PWM channel number.
|
||||
*/
|
||||
static int pwmled_probe(struct platform_device *pdev)
|
||||
{
|
||||
const struct gpio_led_platform_data *pdata;
|
||||
struct pwmled *leds;
|
||||
int i;
|
||||
int status;
|
||||
|
||||
pdata = dev_get_platdata(&pdev->dev);
|
||||
if (!pdata || pdata->num_leds < 1)
|
||||
return -ENODEV;
|
||||
|
||||
leds = devm_kzalloc(&pdev->dev, pdata->num_leds * sizeof(*leds),
|
||||
GFP_KERNEL);
|
||||
if (!leds)
|
||||
return -ENOMEM;
|
||||
|
||||
for (i = 0; i < pdata->num_leds; i++) {
|
||||
struct pwmled *led = leds + i;
|
||||
const struct gpio_led *dat = pdata->leds + i;
|
||||
u32 tmp;
|
||||
|
||||
led->cdev.name = dat->name;
|
||||
led->cdev.brightness = LED_OFF;
|
||||
led->cdev.brightness_set = pwmled_brightness;
|
||||
led->cdev.default_trigger = dat->default_trigger;
|
||||
|
||||
led->active_low = dat->active_low;
|
||||
|
||||
status = pwm_channel_alloc(dat->gpio, &led->pwmc);
|
||||
if (status < 0)
|
||||
goto err;
|
||||
|
||||
/*
|
||||
* Prescale clock by 2^x, so PWM counts in low MHz.
|
||||
* Start each cycle with the LED active, so increasing
|
||||
* the duty cycle gives us more time on (== brighter).
|
||||
*/
|
||||
tmp = 5;
|
||||
if (!led->active_low)
|
||||
tmp |= PWM_CPR_CPOL;
|
||||
pwm_channel_writel(&led->pwmc, PWM_CMR, tmp);
|
||||
|
||||
/*
|
||||
* Pick a period so PWM cycles at 100+ Hz; and a multiplier
|
||||
* for scaling duty cycle: brightness * mult.
|
||||
*/
|
||||
tmp = (led->pwmc.mck / (1 << 5)) / 100;
|
||||
tmp /= 255;
|
||||
led->mult = tmp;
|
||||
pwm_channel_writel(&led->pwmc, PWM_CDTY,
|
||||
led->cdev.brightness * 255);
|
||||
pwm_channel_writel(&led->pwmc, PWM_CPRD,
|
||||
LED_FULL * tmp);
|
||||
|
||||
pwm_channel_enable(&led->pwmc);
|
||||
|
||||
/* Hand it over to the LED framework */
|
||||
status = led_classdev_register(&pdev->dev, &led->cdev);
|
||||
if (status < 0) {
|
||||
pwm_channel_free(&led->pwmc);
|
||||
goto err;
|
||||
}
|
||||
}
|
||||
|
||||
platform_set_drvdata(pdev, leds);
|
||||
return 0;
|
||||
|
||||
err:
|
||||
if (i > 0) {
|
||||
for (i = i - 1; i >= 0; i--) {
|
||||
led_classdev_unregister(&leds[i].cdev);
|
||||
pwm_channel_free(&leds[i].pwmc);
|
||||
}
|
||||
}
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
static int pwmled_remove(struct platform_device *pdev)
|
||||
{
|
||||
const struct gpio_led_platform_data *pdata;
|
||||
struct pwmled *leds;
|
||||
unsigned i;
|
||||
|
||||
pdata = dev_get_platdata(&pdev->dev);
|
||||
leds = platform_get_drvdata(pdev);
|
||||
|
||||
for (i = 0; i < pdata->num_leds; i++) {
|
||||
struct pwmled *led = leds + i;
|
||||
|
||||
led_classdev_unregister(&led->cdev);
|
||||
pwm_channel_free(&led->pwmc);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct platform_driver pwmled_driver = {
|
||||
.driver = {
|
||||
.name = "leds-atmel-pwm",
|
||||
.owner = THIS_MODULE,
|
||||
},
|
||||
/* REVISIT add suspend() and resume() methods */
|
||||
.probe = pwmled_probe,
|
||||
.remove = pwmled_remove,
|
||||
};
|
||||
|
||||
module_platform_driver(pwmled_driver);
|
||||
|
||||
MODULE_DESCRIPTION("Driver for LEDs with PWM-controlled brightness");
|
||||
MODULE_LICENSE("GPL");
|
||||
MODULE_ALIAS("platform:leds-atmel-pwm");
|
|
@ -16,26 +16,9 @@ config PL320_MBOX
|
|||
Management Engine, primarily for cpufreq. Say Y here if you want
|
||||
to use the PL320 IPCM support.
|
||||
|
||||
config OMAP_MBOX
|
||||
tristate
|
||||
help
|
||||
This option is selected by any OMAP architecture specific mailbox
|
||||
driver such as CONFIG_OMAP1_MBOX or CONFIG_OMAP2PLUS_MBOX. This
|
||||
enables the common OMAP mailbox framework code.
|
||||
|
||||
config OMAP1_MBOX
|
||||
tristate "OMAP1 Mailbox framework support"
|
||||
depends on ARCH_OMAP1
|
||||
select OMAP_MBOX
|
||||
help
|
||||
Mailbox implementation for OMAP chips with hardware for
|
||||
interprocessor communication involving DSP in OMAP1. Say Y here
|
||||
if you want to use OMAP1 Mailbox framework support.
|
||||
|
||||
config OMAP2PLUS_MBOX
|
||||
tristate "OMAP2+ Mailbox framework support"
|
||||
depends on ARCH_OMAP2PLUS
|
||||
select OMAP_MBOX
|
||||
help
|
||||
Mailbox implementation for OMAP family chips with hardware for
|
||||
interprocessor communication involving DSP, IVA1.0 and IVA2 in
|
||||
|
@ -44,7 +27,7 @@ config OMAP2PLUS_MBOX
|
|||
|
||||
config OMAP_MBOX_KFIFO_SIZE
|
||||
int "Mailbox kfifo default buffer size (bytes)"
|
||||
depends on OMAP2PLUS_MBOX || OMAP1_MBOX
|
||||
depends on OMAP2PLUS_MBOX
|
||||
default 256
|
||||
help
|
||||
Specify the default size of mailbox's kfifo buffers (bytes).
|
||||
|
|
|
@ -1,7 +1,3 @@
|
|||
obj-$(CONFIG_PL320_MBOX) += pl320-ipc.o
|
||||
|
||||
obj-$(CONFIG_OMAP_MBOX) += omap-mailbox.o
|
||||
obj-$(CONFIG_OMAP1_MBOX) += mailbox_omap1.o
|
||||
mailbox_omap1-objs := mailbox-omap1.o
|
||||
obj-$(CONFIG_OMAP2PLUS_MBOX) += mailbox_omap2.o
|
||||
mailbox_omap2-objs := mailbox-omap2.o
|
||||
obj-$(CONFIG_OMAP2PLUS_MBOX) += omap-mailbox.o
|
||||
|
|
|
@ -1,203 +0,0 @@
|
|||
/*
|
||||
* Mailbox reservation modules for OMAP1
|
||||
*
|
||||
* Copyright (C) 2006-2009 Nokia Corporation
|
||||
* Written by: Hiroshi DOYU <Hiroshi.DOYU@nokia.com>
|
||||
*
|
||||
* This file is subject to the terms and conditions of the GNU General Public
|
||||
* License. See the file "COPYING" in the main directory of this archive
|
||||
* for more details.
|
||||
*/
|
||||
|
||||
#include <linux/module.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/io.h>
|
||||
|
||||
#include "omap-mbox.h"
|
||||
|
||||
#define MAILBOX_ARM2DSP1 0x00
|
||||
#define MAILBOX_ARM2DSP1b 0x04
|
||||
#define MAILBOX_DSP2ARM1 0x08
|
||||
#define MAILBOX_DSP2ARM1b 0x0c
|
||||
#define MAILBOX_DSP2ARM2 0x10
|
||||
#define MAILBOX_DSP2ARM2b 0x14
|
||||
#define MAILBOX_ARM2DSP1_Flag 0x18
|
||||
#define MAILBOX_DSP2ARM1_Flag 0x1c
|
||||
#define MAILBOX_DSP2ARM2_Flag 0x20
|
||||
|
||||
static void __iomem *mbox_base;
|
||||
|
||||
struct omap_mbox1_fifo {
|
||||
unsigned long cmd;
|
||||
unsigned long data;
|
||||
unsigned long flag;
|
||||
};
|
||||
|
||||
struct omap_mbox1_priv {
|
||||
struct omap_mbox1_fifo tx_fifo;
|
||||
struct omap_mbox1_fifo rx_fifo;
|
||||
};
|
||||
|
||||
static inline int mbox_read_reg(size_t ofs)
|
||||
{
|
||||
return __raw_readw(mbox_base + ofs);
|
||||
}
|
||||
|
||||
static inline void mbox_write_reg(u32 val, size_t ofs)
|
||||
{
|
||||
__raw_writew(val, mbox_base + ofs);
|
||||
}
|
||||
|
||||
/* msg */
|
||||
static mbox_msg_t omap1_mbox_fifo_read(struct omap_mbox *mbox)
|
||||
{
|
||||
struct omap_mbox1_fifo *fifo =
|
||||
&((struct omap_mbox1_priv *)mbox->priv)->rx_fifo;
|
||||
mbox_msg_t msg;
|
||||
|
||||
msg = mbox_read_reg(fifo->data);
|
||||
msg |= ((mbox_msg_t) mbox_read_reg(fifo->cmd)) << 16;
|
||||
|
||||
return msg;
|
||||
}
|
||||
|
||||
static void
|
||||
omap1_mbox_fifo_write(struct omap_mbox *mbox, mbox_msg_t msg)
|
||||
{
|
||||
struct omap_mbox1_fifo *fifo =
|
||||
&((struct omap_mbox1_priv *)mbox->priv)->tx_fifo;
|
||||
|
||||
mbox_write_reg(msg & 0xffff, fifo->data);
|
||||
mbox_write_reg(msg >> 16, fifo->cmd);
|
||||
}
|
||||
|
||||
static int omap1_mbox_fifo_empty(struct omap_mbox *mbox)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int omap1_mbox_fifo_full(struct omap_mbox *mbox)
|
||||
{
|
||||
struct omap_mbox1_fifo *fifo =
|
||||
&((struct omap_mbox1_priv *)mbox->priv)->rx_fifo;
|
||||
|
||||
return mbox_read_reg(fifo->flag);
|
||||
}
|
||||
|
||||
/* irq */
|
||||
static void
|
||||
omap1_mbox_enable_irq(struct omap_mbox *mbox, omap_mbox_irq_t irq)
|
||||
{
|
||||
if (irq == IRQ_RX)
|
||||
enable_irq(mbox->irq);
|
||||
}
|
||||
|
||||
static void
|
||||
omap1_mbox_disable_irq(struct omap_mbox *mbox, omap_mbox_irq_t irq)
|
||||
{
|
||||
if (irq == IRQ_RX)
|
||||
disable_irq(mbox->irq);
|
||||
}
|
||||
|
||||
static int
|
||||
omap1_mbox_is_irq(struct omap_mbox *mbox, omap_mbox_irq_t irq)
|
||||
{
|
||||
if (irq == IRQ_TX)
|
||||
return 0;
|
||||
return 1;
|
||||
}
|
||||
|
||||
static struct omap_mbox_ops omap1_mbox_ops = {
|
||||
.type = OMAP_MBOX_TYPE1,
|
||||
.fifo_read = omap1_mbox_fifo_read,
|
||||
.fifo_write = omap1_mbox_fifo_write,
|
||||
.fifo_empty = omap1_mbox_fifo_empty,
|
||||
.fifo_full = omap1_mbox_fifo_full,
|
||||
.enable_irq = omap1_mbox_enable_irq,
|
||||
.disable_irq = omap1_mbox_disable_irq,
|
||||
.is_irq = omap1_mbox_is_irq,
|
||||
};
|
||||
|
||||
/* FIXME: the following struct should be created automatically by the user id */
|
||||
|
||||
/* DSP */
|
||||
static struct omap_mbox1_priv omap1_mbox_dsp_priv = {
|
||||
.tx_fifo = {
|
||||
.cmd = MAILBOX_ARM2DSP1b,
|
||||
.data = MAILBOX_ARM2DSP1,
|
||||
.flag = MAILBOX_ARM2DSP1_Flag,
|
||||
},
|
||||
.rx_fifo = {
|
||||
.cmd = MAILBOX_DSP2ARM1b,
|
||||
.data = MAILBOX_DSP2ARM1,
|
||||
.flag = MAILBOX_DSP2ARM1_Flag,
|
||||
},
|
||||
};
|
||||
|
||||
static struct omap_mbox mbox_dsp_info = {
|
||||
.name = "dsp",
|
||||
.ops = &omap1_mbox_ops,
|
||||
.priv = &omap1_mbox_dsp_priv,
|
||||
};
|
||||
|
||||
static struct omap_mbox *omap1_mboxes[] = { &mbox_dsp_info, NULL };
|
||||
|
||||
static int omap1_mbox_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct resource *mem;
|
||||
int ret;
|
||||
struct omap_mbox **list;
|
||||
|
||||
list = omap1_mboxes;
|
||||
list[0]->irq = platform_get_irq_byname(pdev, "dsp");
|
||||
|
||||
mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
||||
if (!mem)
|
||||
return -ENOENT;
|
||||
|
||||
mbox_base = ioremap(mem->start, resource_size(mem));
|
||||
if (!mbox_base)
|
||||
return -ENOMEM;
|
||||
|
||||
ret = omap_mbox_register(&pdev->dev, list);
|
||||
if (ret) {
|
||||
iounmap(mbox_base);
|
||||
return ret;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int omap1_mbox_remove(struct platform_device *pdev)
|
||||
{
|
||||
omap_mbox_unregister();
|
||||
iounmap(mbox_base);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct platform_driver omap1_mbox_driver = {
|
||||
.probe = omap1_mbox_probe,
|
||||
.remove = omap1_mbox_remove,
|
||||
.driver = {
|
||||
.name = "omap-mailbox",
|
||||
},
|
||||
};
|
||||
|
||||
static int __init omap1_mbox_init(void)
|
||||
{
|
||||
return platform_driver_register(&omap1_mbox_driver);
|
||||
}
|
||||
|
||||
static void __exit omap1_mbox_exit(void)
|
||||
{
|
||||
platform_driver_unregister(&omap1_mbox_driver);
|
||||
}
|
||||
|
||||
module_init(omap1_mbox_init);
|
||||
module_exit(omap1_mbox_exit);
|
||||
|
||||
MODULE_LICENSE("GPL v2");
|
||||
MODULE_DESCRIPTION("omap mailbox: omap1 architecture specific functions");
|
||||
MODULE_AUTHOR("Hiroshi DOYU <Hiroshi.DOYU@nokia.com>");
|
||||
MODULE_ALIAS("platform:omap1-mailbox");
|
|
@ -1,357 +0,0 @@
|
|||
/*
|
||||
* Mailbox reservation modules for OMAP2/3
|
||||
*
|
||||
* Copyright (C) 2006-2009 Nokia Corporation
|
||||
* Written by: Hiroshi DOYU <Hiroshi.DOYU@nokia.com>
|
||||
* and Paul Mundt
|
||||
*
|
||||
* This file is subject to the terms and conditions of the GNU General Public
|
||||
* License. See the file "COPYING" in the main directory of this archive
|
||||
* for more details.
|
||||
*/
|
||||
|
||||
#include <linux/module.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/clk.h>
|
||||
#include <linux/err.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/io.h>
|
||||
#include <linux/pm_runtime.h>
|
||||
#include <linux/platform_data/mailbox-omap.h>
|
||||
|
||||
#include "omap-mbox.h"
|
||||
|
||||
#define MAILBOX_REVISION 0x000
|
||||
#define MAILBOX_MESSAGE(m) (0x040 + 4 * (m))
|
||||
#define MAILBOX_FIFOSTATUS(m) (0x080 + 4 * (m))
|
||||
#define MAILBOX_MSGSTATUS(m) (0x0c0 + 4 * (m))
|
||||
#define MAILBOX_IRQSTATUS(u) (0x100 + 8 * (u))
|
||||
#define MAILBOX_IRQENABLE(u) (0x104 + 8 * (u))
|
||||
|
||||
#define OMAP4_MAILBOX_IRQSTATUS(u) (0x104 + 0x10 * (u))
|
||||
#define OMAP4_MAILBOX_IRQENABLE(u) (0x108 + 0x10 * (u))
|
||||
#define OMAP4_MAILBOX_IRQENABLE_CLR(u) (0x10c + 0x10 * (u))
|
||||
|
||||
#define MAILBOX_IRQ_NEWMSG(m) (1 << (2 * (m)))
|
||||
#define MAILBOX_IRQ_NOTFULL(m) (1 << (2 * (m) + 1))
|
||||
|
||||
#define MBOX_REG_SIZE 0x120
|
||||
|
||||
#define OMAP4_MBOX_REG_SIZE 0x130
|
||||
|
||||
#define MBOX_NR_REGS (MBOX_REG_SIZE / sizeof(u32))
|
||||
#define OMAP4_MBOX_NR_REGS (OMAP4_MBOX_REG_SIZE / sizeof(u32))
|
||||
|
||||
static void __iomem *mbox_base;
|
||||
|
||||
struct omap_mbox2_fifo {
|
||||
unsigned long msg;
|
||||
unsigned long fifo_stat;
|
||||
unsigned long msg_stat;
|
||||
};
|
||||
|
||||
struct omap_mbox2_priv {
|
||||
struct omap_mbox2_fifo tx_fifo;
|
||||
struct omap_mbox2_fifo rx_fifo;
|
||||
unsigned long irqenable;
|
||||
unsigned long irqstatus;
|
||||
u32 newmsg_bit;
|
||||
u32 notfull_bit;
|
||||
u32 ctx[OMAP4_MBOX_NR_REGS];
|
||||
unsigned long irqdisable;
|
||||
u32 intr_type;
|
||||
};
|
||||
|
||||
static inline unsigned int mbox_read_reg(size_t ofs)
|
||||
{
|
||||
return __raw_readl(mbox_base + ofs);
|
||||
}
|
||||
|
||||
static inline void mbox_write_reg(u32 val, size_t ofs)
|
||||
{
|
||||
__raw_writel(val, mbox_base + ofs);
|
||||
}
|
||||
|
||||
/* Mailbox H/W preparations */
|
||||
static int omap2_mbox_startup(struct omap_mbox *mbox)
|
||||
{
|
||||
u32 l;
|
||||
|
||||
pm_runtime_enable(mbox->dev->parent);
|
||||
pm_runtime_get_sync(mbox->dev->parent);
|
||||
|
||||
l = mbox_read_reg(MAILBOX_REVISION);
|
||||
pr_debug("omap mailbox rev %d.%d\n", (l & 0xf0) >> 4, (l & 0x0f));
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void omap2_mbox_shutdown(struct omap_mbox *mbox)
|
||||
{
|
||||
pm_runtime_put_sync(mbox->dev->parent);
|
||||
pm_runtime_disable(mbox->dev->parent);
|
||||
}
|
||||
|
||||
/* Mailbox FIFO handle functions */
|
||||
static mbox_msg_t omap2_mbox_fifo_read(struct omap_mbox *mbox)
|
||||
{
|
||||
struct omap_mbox2_fifo *fifo =
|
||||
&((struct omap_mbox2_priv *)mbox->priv)->rx_fifo;
|
||||
return (mbox_msg_t) mbox_read_reg(fifo->msg);
|
||||
}
|
||||
|
||||
static void omap2_mbox_fifo_write(struct omap_mbox *mbox, mbox_msg_t msg)
|
||||
{
|
||||
struct omap_mbox2_fifo *fifo =
|
||||
&((struct omap_mbox2_priv *)mbox->priv)->tx_fifo;
|
||||
mbox_write_reg(msg, fifo->msg);
|
||||
}
|
||||
|
||||
static int omap2_mbox_fifo_empty(struct omap_mbox *mbox)
|
||||
{
|
||||
struct omap_mbox2_fifo *fifo =
|
||||
&((struct omap_mbox2_priv *)mbox->priv)->rx_fifo;
|
||||
return (mbox_read_reg(fifo->msg_stat) == 0);
|
||||
}
|
||||
|
||||
static int omap2_mbox_fifo_full(struct omap_mbox *mbox)
|
||||
{
|
||||
struct omap_mbox2_fifo *fifo =
|
||||
&((struct omap_mbox2_priv *)mbox->priv)->tx_fifo;
|
||||
return mbox_read_reg(fifo->fifo_stat);
|
||||
}
|
||||
|
||||
/* Mailbox IRQ handle functions */
|
||||
static void omap2_mbox_enable_irq(struct omap_mbox *mbox, omap_mbox_irq_t irq)
|
||||
{
|
||||
struct omap_mbox2_priv *p = mbox->priv;
|
||||
u32 l, bit = (irq == IRQ_TX) ? p->notfull_bit : p->newmsg_bit;
|
||||
|
||||
l = mbox_read_reg(p->irqenable);
|
||||
l |= bit;
|
||||
mbox_write_reg(l, p->irqenable);
|
||||
}
|
||||
|
||||
static void omap2_mbox_disable_irq(struct omap_mbox *mbox, omap_mbox_irq_t irq)
|
||||
{
|
||||
struct omap_mbox2_priv *p = mbox->priv;
|
||||
u32 bit = (irq == IRQ_TX) ? p->notfull_bit : p->newmsg_bit;
|
||||
|
||||
/*
|
||||
* Read and update the interrupt configuration register for pre-OMAP4.
|
||||
* OMAP4 and later SoCs have a dedicated interrupt disabling register.
|
||||
*/
|
||||
if (!p->intr_type)
|
||||
bit = mbox_read_reg(p->irqdisable) & ~bit;
|
||||
|
||||
mbox_write_reg(bit, p->irqdisable);
|
||||
}
|
||||
|
||||
static void omap2_mbox_ack_irq(struct omap_mbox *mbox, omap_mbox_irq_t irq)
|
||||
{
|
||||
struct omap_mbox2_priv *p = mbox->priv;
|
||||
u32 bit = (irq == IRQ_TX) ? p->notfull_bit : p->newmsg_bit;
|
||||
|
||||
mbox_write_reg(bit, p->irqstatus);
|
||||
|
||||
/* Flush posted write for irq status to avoid spurious interrupts */
|
||||
mbox_read_reg(p->irqstatus);
|
||||
}
|
||||
|
||||
static int omap2_mbox_is_irq(struct omap_mbox *mbox, omap_mbox_irq_t irq)
|
||||
{
|
||||
struct omap_mbox2_priv *p = mbox->priv;
|
||||
u32 bit = (irq == IRQ_TX) ? p->notfull_bit : p->newmsg_bit;
|
||||
u32 enable = mbox_read_reg(p->irqenable);
|
||||
u32 status = mbox_read_reg(p->irqstatus);
|
||||
|
||||
return (int)(enable & status & bit);
|
||||
}
|
||||
|
||||
static void omap2_mbox_save_ctx(struct omap_mbox *mbox)
|
||||
{
|
||||
int i;
|
||||
struct omap_mbox2_priv *p = mbox->priv;
|
||||
int nr_regs;
|
||||
|
||||
if (p->intr_type)
|
||||
nr_regs = OMAP4_MBOX_NR_REGS;
|
||||
else
|
||||
nr_regs = MBOX_NR_REGS;
|
||||
for (i = 0; i < nr_regs; i++) {
|
||||
p->ctx[i] = mbox_read_reg(i * sizeof(u32));
|
||||
|
||||
dev_dbg(mbox->dev, "%s: [%02x] %08x\n", __func__,
|
||||
i, p->ctx[i]);
|
||||
}
|
||||
}
|
||||
|
||||
static void omap2_mbox_restore_ctx(struct omap_mbox *mbox)
|
||||
{
|
||||
int i;
|
||||
struct omap_mbox2_priv *p = mbox->priv;
|
||||
int nr_regs;
|
||||
|
||||
if (p->intr_type)
|
||||
nr_regs = OMAP4_MBOX_NR_REGS;
|
||||
else
|
||||
nr_regs = MBOX_NR_REGS;
|
||||
for (i = 0; i < nr_regs; i++) {
|
||||
mbox_write_reg(p->ctx[i], i * sizeof(u32));
|
||||
|
||||
dev_dbg(mbox->dev, "%s: [%02x] %08x\n", __func__,
|
||||
i, p->ctx[i]);
|
||||
}
|
||||
}
|
||||
|
||||
static struct omap_mbox_ops omap2_mbox_ops = {
|
||||
.type = OMAP_MBOX_TYPE2,
|
||||
.startup = omap2_mbox_startup,
|
||||
.shutdown = omap2_mbox_shutdown,
|
||||
.fifo_read = omap2_mbox_fifo_read,
|
||||
.fifo_write = omap2_mbox_fifo_write,
|
||||
.fifo_empty = omap2_mbox_fifo_empty,
|
||||
.fifo_full = omap2_mbox_fifo_full,
|
||||
.enable_irq = omap2_mbox_enable_irq,
|
||||
.disable_irq = omap2_mbox_disable_irq,
|
||||
.ack_irq = omap2_mbox_ack_irq,
|
||||
.is_irq = omap2_mbox_is_irq,
|
||||
.save_ctx = omap2_mbox_save_ctx,
|
||||
.restore_ctx = omap2_mbox_restore_ctx,
|
||||
};
|
||||
|
||||
static int omap2_mbox_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct resource *mem;
|
||||
int ret;
|
||||
struct omap_mbox **list, *mbox, *mboxblk;
|
||||
struct omap_mbox2_priv *priv, *privblk;
|
||||
struct omap_mbox_pdata *pdata = pdev->dev.platform_data;
|
||||
struct omap_mbox_dev_info *info;
|
||||
int i;
|
||||
|
||||
if (!pdata || !pdata->info_cnt || !pdata->info) {
|
||||
pr_err("%s: platform not supported\n", __func__);
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
/* allocate one extra for marking end of list */
|
||||
list = kzalloc((pdata->info_cnt + 1) * sizeof(*list), GFP_KERNEL);
|
||||
if (!list)
|
||||
return -ENOMEM;
|
||||
|
||||
mboxblk = mbox = kzalloc(pdata->info_cnt * sizeof(*mbox), GFP_KERNEL);
|
||||
if (!mboxblk) {
|
||||
ret = -ENOMEM;
|
||||
goto free_list;
|
||||
}
|
||||
|
||||
privblk = priv = kzalloc(pdata->info_cnt * sizeof(*priv), GFP_KERNEL);
|
||||
if (!privblk) {
|
||||
ret = -ENOMEM;
|
||||
goto free_mboxblk;
|
||||
}
|
||||
|
||||
info = pdata->info;
|
||||
for (i = 0; i < pdata->info_cnt; i++, info++, priv++) {
|
||||
priv->tx_fifo.msg = MAILBOX_MESSAGE(info->tx_id);
|
||||
priv->tx_fifo.fifo_stat = MAILBOX_FIFOSTATUS(info->tx_id);
|
||||
priv->rx_fifo.msg = MAILBOX_MESSAGE(info->rx_id);
|
||||
priv->rx_fifo.msg_stat = MAILBOX_MSGSTATUS(info->rx_id);
|
||||
priv->notfull_bit = MAILBOX_IRQ_NOTFULL(info->tx_id);
|
||||
priv->newmsg_bit = MAILBOX_IRQ_NEWMSG(info->rx_id);
|
||||
if (pdata->intr_type) {
|
||||
priv->irqenable = OMAP4_MAILBOX_IRQENABLE(info->usr_id);
|
||||
priv->irqstatus = OMAP4_MAILBOX_IRQSTATUS(info->usr_id);
|
||||
priv->irqdisable =
|
||||
OMAP4_MAILBOX_IRQENABLE_CLR(info->usr_id);
|
||||
} else {
|
||||
priv->irqenable = MAILBOX_IRQENABLE(info->usr_id);
|
||||
priv->irqstatus = MAILBOX_IRQSTATUS(info->usr_id);
|
||||
priv->irqdisable = MAILBOX_IRQENABLE(info->usr_id);
|
||||
}
|
||||
priv->intr_type = pdata->intr_type;
|
||||
|
||||
mbox->priv = priv;
|
||||
mbox->name = info->name;
|
||||
mbox->ops = &omap2_mbox_ops;
|
||||
mbox->irq = platform_get_irq(pdev, info->irq_id);
|
||||
if (mbox->irq < 0) {
|
||||
ret = mbox->irq;
|
||||
goto free_privblk;
|
||||
}
|
||||
list[i] = mbox++;
|
||||
}
|
||||
|
||||
mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
||||
if (!mem) {
|
||||
ret = -ENOENT;
|
||||
goto free_privblk;
|
||||
}
|
||||
|
||||
mbox_base = ioremap(mem->start, resource_size(mem));
|
||||
if (!mbox_base) {
|
||||
ret = -ENOMEM;
|
||||
goto free_privblk;
|
||||
}
|
||||
|
||||
ret = omap_mbox_register(&pdev->dev, list);
|
||||
if (ret)
|
||||
goto unmap_mbox;
|
||||
platform_set_drvdata(pdev, list);
|
||||
|
||||
return 0;
|
||||
|
||||
unmap_mbox:
|
||||
iounmap(mbox_base);
|
||||
free_privblk:
|
||||
kfree(privblk);
|
||||
free_mboxblk:
|
||||
kfree(mboxblk);
|
||||
free_list:
|
||||
kfree(list);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int omap2_mbox_remove(struct platform_device *pdev)
|
||||
{
|
||||
struct omap_mbox2_priv *privblk;
|
||||
struct omap_mbox **list = platform_get_drvdata(pdev);
|
||||
struct omap_mbox *mboxblk = list[0];
|
||||
|
||||
privblk = mboxblk->priv;
|
||||
omap_mbox_unregister();
|
||||
iounmap(mbox_base);
|
||||
kfree(privblk);
|
||||
kfree(mboxblk);
|
||||
kfree(list);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct platform_driver omap2_mbox_driver = {
|
||||
.probe = omap2_mbox_probe,
|
||||
.remove = omap2_mbox_remove,
|
||||
.driver = {
|
||||
.name = "omap-mailbox",
|
||||
},
|
||||
};
|
||||
|
||||
static int __init omap2_mbox_init(void)
|
||||
{
|
||||
return platform_driver_register(&omap2_mbox_driver);
|
||||
}
|
||||
|
||||
static void __exit omap2_mbox_exit(void)
|
||||
{
|
||||
platform_driver_unregister(&omap2_mbox_driver);
|
||||
}
|
||||
|
||||
module_init(omap2_mbox_init);
|
||||
module_exit(omap2_mbox_exit);
|
||||
|
||||
MODULE_LICENSE("GPL v2");
|
||||
MODULE_DESCRIPTION("omap mailbox: omap2/3/4 architecture specific functions");
|
||||
MODULE_AUTHOR("Hiroshi DOYU <Hiroshi.DOYU@nokia.com>");
|
||||
MODULE_AUTHOR("Paul Mundt");
|
||||
MODULE_ALIAS("platform:omap2-mailbox");
|
|
@ -2,8 +2,10 @@
|
|||
* OMAP mailbox driver
|
||||
*
|
||||
* Copyright (C) 2006-2009 Nokia Corporation. All rights reserved.
|
||||
* Copyright (C) 2013-2014 Texas Instruments Inc.
|
||||
*
|
||||
* Contact: Hiroshi DOYU <Hiroshi.DOYU@nokia.com>
|
||||
* Suman Anna <s-anna@ti.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
|
@ -24,70 +26,164 @@
|
|||
#include <linux/interrupt.h>
|
||||
#include <linux/spinlock.h>
|
||||
#include <linux/mutex.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/kfifo.h>
|
||||
#include <linux/err.h>
|
||||
#include <linux/notifier.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/pm_runtime.h>
|
||||
#include <linux/platform_data/mailbox-omap.h>
|
||||
#include <linux/omap-mailbox.h>
|
||||
|
||||
#include "omap-mbox.h"
|
||||
#define MAILBOX_REVISION 0x000
|
||||
#define MAILBOX_MESSAGE(m) (0x040 + 4 * (m))
|
||||
#define MAILBOX_FIFOSTATUS(m) (0x080 + 4 * (m))
|
||||
#define MAILBOX_MSGSTATUS(m) (0x0c0 + 4 * (m))
|
||||
|
||||
static struct omap_mbox **mboxes;
|
||||
#define OMAP2_MAILBOX_IRQSTATUS(u) (0x100 + 8 * (u))
|
||||
#define OMAP2_MAILBOX_IRQENABLE(u) (0x104 + 8 * (u))
|
||||
|
||||
static int mbox_configured;
|
||||
static DEFINE_MUTEX(mbox_configured_lock);
|
||||
#define OMAP4_MAILBOX_IRQSTATUS(u) (0x104 + 0x10 * (u))
|
||||
#define OMAP4_MAILBOX_IRQENABLE(u) (0x108 + 0x10 * (u))
|
||||
#define OMAP4_MAILBOX_IRQENABLE_CLR(u) (0x10c + 0x10 * (u))
|
||||
|
||||
#define MAILBOX_IRQSTATUS(type, u) (type ? OMAP4_MAILBOX_IRQSTATUS(u) : \
|
||||
OMAP2_MAILBOX_IRQSTATUS(u))
|
||||
#define MAILBOX_IRQENABLE(type, u) (type ? OMAP4_MAILBOX_IRQENABLE(u) : \
|
||||
OMAP2_MAILBOX_IRQENABLE(u))
|
||||
#define MAILBOX_IRQDISABLE(type, u) (type ? OMAP4_MAILBOX_IRQENABLE_CLR(u) \
|
||||
: OMAP2_MAILBOX_IRQENABLE(u))
|
||||
|
||||
#define MAILBOX_IRQ_NEWMSG(m) (1 << (2 * (m)))
|
||||
#define MAILBOX_IRQ_NOTFULL(m) (1 << (2 * (m) + 1))
|
||||
|
||||
#define MBOX_REG_SIZE 0x120
|
||||
|
||||
#define OMAP4_MBOX_REG_SIZE 0x130
|
||||
|
||||
#define MBOX_NR_REGS (MBOX_REG_SIZE / sizeof(u32))
|
||||
#define OMAP4_MBOX_NR_REGS (OMAP4_MBOX_REG_SIZE / sizeof(u32))
|
||||
|
||||
struct omap_mbox_fifo {
|
||||
unsigned long msg;
|
||||
unsigned long fifo_stat;
|
||||
unsigned long msg_stat;
|
||||
unsigned long irqenable;
|
||||
unsigned long irqstatus;
|
||||
unsigned long irqdisable;
|
||||
u32 intr_bit;
|
||||
};
|
||||
|
||||
struct omap_mbox_queue {
|
||||
spinlock_t lock;
|
||||
struct kfifo fifo;
|
||||
struct work_struct work;
|
||||
struct tasklet_struct tasklet;
|
||||
struct omap_mbox *mbox;
|
||||
bool full;
|
||||
};
|
||||
|
||||
struct omap_mbox_device {
|
||||
struct device *dev;
|
||||
struct mutex cfg_lock;
|
||||
void __iomem *mbox_base;
|
||||
u32 num_users;
|
||||
u32 num_fifos;
|
||||
struct omap_mbox **mboxes;
|
||||
struct list_head elem;
|
||||
};
|
||||
|
||||
struct omap_mbox {
|
||||
const char *name;
|
||||
int irq;
|
||||
struct omap_mbox_queue *txq, *rxq;
|
||||
struct device *dev;
|
||||
struct omap_mbox_device *parent;
|
||||
struct omap_mbox_fifo tx_fifo;
|
||||
struct omap_mbox_fifo rx_fifo;
|
||||
u32 ctx[OMAP4_MBOX_NR_REGS];
|
||||
u32 intr_type;
|
||||
int use_count;
|
||||
struct blocking_notifier_head notifier;
|
||||
};
|
||||
|
||||
/* global variables for the mailbox devices */
|
||||
static DEFINE_MUTEX(omap_mbox_devices_lock);
|
||||
static LIST_HEAD(omap_mbox_devices);
|
||||
|
||||
static unsigned int mbox_kfifo_size = CONFIG_OMAP_MBOX_KFIFO_SIZE;
|
||||
module_param(mbox_kfifo_size, uint, S_IRUGO);
|
||||
MODULE_PARM_DESC(mbox_kfifo_size, "Size of omap's mailbox kfifo (bytes)");
|
||||
|
||||
static inline
|
||||
unsigned int mbox_read_reg(struct omap_mbox_device *mdev, size_t ofs)
|
||||
{
|
||||
return __raw_readl(mdev->mbox_base + ofs);
|
||||
}
|
||||
|
||||
static inline
|
||||
void mbox_write_reg(struct omap_mbox_device *mdev, u32 val, size_t ofs)
|
||||
{
|
||||
__raw_writel(val, mdev->mbox_base + ofs);
|
||||
}
|
||||
|
||||
/* Mailbox FIFO handle functions */
|
||||
static inline mbox_msg_t mbox_fifo_read(struct omap_mbox *mbox)
|
||||
static mbox_msg_t mbox_fifo_read(struct omap_mbox *mbox)
|
||||
{
|
||||
return mbox->ops->fifo_read(mbox);
|
||||
struct omap_mbox_fifo *fifo = &mbox->rx_fifo;
|
||||
return (mbox_msg_t) mbox_read_reg(mbox->parent, fifo->msg);
|
||||
}
|
||||
static inline void mbox_fifo_write(struct omap_mbox *mbox, mbox_msg_t msg)
|
||||
|
||||
static void mbox_fifo_write(struct omap_mbox *mbox, mbox_msg_t msg)
|
||||
{
|
||||
mbox->ops->fifo_write(mbox, msg);
|
||||
struct omap_mbox_fifo *fifo = &mbox->tx_fifo;
|
||||
mbox_write_reg(mbox->parent, msg, fifo->msg);
|
||||
}
|
||||
static inline int mbox_fifo_empty(struct omap_mbox *mbox)
|
||||
|
||||
static int mbox_fifo_empty(struct omap_mbox *mbox)
|
||||
{
|
||||
return mbox->ops->fifo_empty(mbox);
|
||||
struct omap_mbox_fifo *fifo = &mbox->rx_fifo;
|
||||
return (mbox_read_reg(mbox->parent, fifo->msg_stat) == 0);
|
||||
}
|
||||
static inline int mbox_fifo_full(struct omap_mbox *mbox)
|
||||
|
||||
static int mbox_fifo_full(struct omap_mbox *mbox)
|
||||
{
|
||||
return mbox->ops->fifo_full(mbox);
|
||||
struct omap_mbox_fifo *fifo = &mbox->tx_fifo;
|
||||
return mbox_read_reg(mbox->parent, fifo->fifo_stat);
|
||||
}
|
||||
|
||||
/* Mailbox IRQ handle functions */
|
||||
static inline void ack_mbox_irq(struct omap_mbox *mbox, omap_mbox_irq_t irq)
|
||||
static void ack_mbox_irq(struct omap_mbox *mbox, omap_mbox_irq_t irq)
|
||||
{
|
||||
if (mbox->ops->ack_irq)
|
||||
mbox->ops->ack_irq(mbox, irq);
|
||||
struct omap_mbox_fifo *fifo = (irq == IRQ_TX) ?
|
||||
&mbox->tx_fifo : &mbox->rx_fifo;
|
||||
u32 bit = fifo->intr_bit;
|
||||
u32 irqstatus = fifo->irqstatus;
|
||||
|
||||
mbox_write_reg(mbox->parent, bit, irqstatus);
|
||||
|
||||
/* Flush posted write for irq status to avoid spurious interrupts */
|
||||
mbox_read_reg(mbox->parent, irqstatus);
|
||||
}
|
||||
static inline int is_mbox_irq(struct omap_mbox *mbox, omap_mbox_irq_t irq)
|
||||
|
||||
static int is_mbox_irq(struct omap_mbox *mbox, omap_mbox_irq_t irq)
|
||||
{
|
||||
return mbox->ops->is_irq(mbox, irq);
|
||||
struct omap_mbox_fifo *fifo = (irq == IRQ_TX) ?
|
||||
&mbox->tx_fifo : &mbox->rx_fifo;
|
||||
u32 bit = fifo->intr_bit;
|
||||
u32 irqenable = fifo->irqenable;
|
||||
u32 irqstatus = fifo->irqstatus;
|
||||
|
||||
u32 enable = mbox_read_reg(mbox->parent, irqenable);
|
||||
u32 status = mbox_read_reg(mbox->parent, irqstatus);
|
||||
|
||||
return (int)(enable & status & bit);
|
||||
}
|
||||
|
||||
/*
|
||||
* message sender
|
||||
*/
|
||||
static int __mbox_poll_for_space(struct omap_mbox *mbox)
|
||||
{
|
||||
int ret = 0, i = 1000;
|
||||
|
||||
while (mbox_fifo_full(mbox)) {
|
||||
if (mbox->ops->type == OMAP_MBOX_TYPE2)
|
||||
return -1;
|
||||
if (--i == 0)
|
||||
return -1;
|
||||
udelay(1);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
int omap_mbox_msg_send(struct omap_mbox *mbox, mbox_msg_t msg)
|
||||
{
|
||||
struct omap_mbox_queue *mq = mbox->txq;
|
||||
|
@ -100,7 +196,7 @@ int omap_mbox_msg_send(struct omap_mbox *mbox, mbox_msg_t msg)
|
|||
goto out;
|
||||
}
|
||||
|
||||
if (kfifo_is_empty(&mq->fifo) && !__mbox_poll_for_space(mbox)) {
|
||||
if (kfifo_is_empty(&mq->fifo) && !mbox_fifo_full(mbox)) {
|
||||
mbox_fifo_write(mbox, msg);
|
||||
goto out;
|
||||
}
|
||||
|
@ -118,35 +214,69 @@ EXPORT_SYMBOL(omap_mbox_msg_send);
|
|||
|
||||
void omap_mbox_save_ctx(struct omap_mbox *mbox)
|
||||
{
|
||||
if (!mbox->ops->save_ctx) {
|
||||
dev_err(mbox->dev, "%s:\tno save\n", __func__);
|
||||
return;
|
||||
}
|
||||
int i;
|
||||
int nr_regs;
|
||||
|
||||
mbox->ops->save_ctx(mbox);
|
||||
if (mbox->intr_type)
|
||||
nr_regs = OMAP4_MBOX_NR_REGS;
|
||||
else
|
||||
nr_regs = MBOX_NR_REGS;
|
||||
for (i = 0; i < nr_regs; i++) {
|
||||
mbox->ctx[i] = mbox_read_reg(mbox->parent, i * sizeof(u32));
|
||||
|
||||
dev_dbg(mbox->dev, "%s: [%02x] %08x\n", __func__,
|
||||
i, mbox->ctx[i]);
|
||||
}
|
||||
}
|
||||
EXPORT_SYMBOL(omap_mbox_save_ctx);
|
||||
|
||||
void omap_mbox_restore_ctx(struct omap_mbox *mbox)
|
||||
{
|
||||
if (!mbox->ops->restore_ctx) {
|
||||
dev_err(mbox->dev, "%s:\tno restore\n", __func__);
|
||||
return;
|
||||
}
|
||||
int i;
|
||||
int nr_regs;
|
||||
|
||||
mbox->ops->restore_ctx(mbox);
|
||||
if (mbox->intr_type)
|
||||
nr_regs = OMAP4_MBOX_NR_REGS;
|
||||
else
|
||||
nr_regs = MBOX_NR_REGS;
|
||||
for (i = 0; i < nr_regs; i++) {
|
||||
mbox_write_reg(mbox->parent, mbox->ctx[i], i * sizeof(u32));
|
||||
|
||||
dev_dbg(mbox->dev, "%s: [%02x] %08x\n", __func__,
|
||||
i, mbox->ctx[i]);
|
||||
}
|
||||
}
|
||||
EXPORT_SYMBOL(omap_mbox_restore_ctx);
|
||||
|
||||
void omap_mbox_enable_irq(struct omap_mbox *mbox, omap_mbox_irq_t irq)
|
||||
{
|
||||
mbox->ops->enable_irq(mbox, irq);
|
||||
u32 l;
|
||||
struct omap_mbox_fifo *fifo = (irq == IRQ_TX) ?
|
||||
&mbox->tx_fifo : &mbox->rx_fifo;
|
||||
u32 bit = fifo->intr_bit;
|
||||
u32 irqenable = fifo->irqenable;
|
||||
|
||||
l = mbox_read_reg(mbox->parent, irqenable);
|
||||
l |= bit;
|
||||
mbox_write_reg(mbox->parent, l, irqenable);
|
||||
}
|
||||
EXPORT_SYMBOL(omap_mbox_enable_irq);
|
||||
|
||||
void omap_mbox_disable_irq(struct omap_mbox *mbox, omap_mbox_irq_t irq)
|
||||
{
|
||||
mbox->ops->disable_irq(mbox, irq);
|
||||
struct omap_mbox_fifo *fifo = (irq == IRQ_TX) ?
|
||||
&mbox->tx_fifo : &mbox->rx_fifo;
|
||||
u32 bit = fifo->intr_bit;
|
||||
u32 irqdisable = fifo->irqdisable;
|
||||
|
||||
/*
|
||||
* Read and update the interrupt configuration register for pre-OMAP4.
|
||||
* OMAP4 and later SoCs have a dedicated interrupt disabling register.
|
||||
*/
|
||||
if (!mbox->intr_type)
|
||||
bit = mbox_read_reg(mbox->parent, irqdisable) & ~bit;
|
||||
|
||||
mbox_write_reg(mbox->parent, bit, irqdisable);
|
||||
}
|
||||
EXPORT_SYMBOL(omap_mbox_disable_irq);
|
||||
|
||||
|
@ -158,7 +288,7 @@ static void mbox_tx_tasklet(unsigned long tx_data)
|
|||
int ret;
|
||||
|
||||
while (kfifo_len(&mq->fifo)) {
|
||||
if (__mbox_poll_for_space(mbox)) {
|
||||
if (mbox_fifo_full(mbox)) {
|
||||
omap_mbox_enable_irq(mbox, IRQ_TX);
|
||||
break;
|
||||
}
|
||||
|
@ -223,9 +353,6 @@ static void __mbox_rx_interrupt(struct omap_mbox *mbox)
|
|||
|
||||
len = kfifo_in(&mq->fifo, (unsigned char *)&msg, sizeof(msg));
|
||||
WARN_ON(len != sizeof(msg));
|
||||
|
||||
if (mbox->ops->type == OMAP_MBOX_TYPE1)
|
||||
break;
|
||||
}
|
||||
|
||||
/* no more messages in the fifo. clear IRQ source. */
|
||||
|
@ -283,16 +410,12 @@ static int omap_mbox_startup(struct omap_mbox *mbox)
|
|||
{
|
||||
int ret = 0;
|
||||
struct omap_mbox_queue *mq;
|
||||
struct omap_mbox_device *mdev = mbox->parent;
|
||||
|
||||
mutex_lock(&mbox_configured_lock);
|
||||
if (!mbox_configured++) {
|
||||
if (likely(mbox->ops->startup)) {
|
||||
ret = mbox->ops->startup(mbox);
|
||||
if (unlikely(ret))
|
||||
goto fail_startup;
|
||||
} else
|
||||
goto fail_startup;
|
||||
}
|
||||
mutex_lock(&mdev->cfg_lock);
|
||||
ret = pm_runtime_get_sync(mdev->dev);
|
||||
if (unlikely(ret < 0))
|
||||
goto fail_startup;
|
||||
|
||||
if (!mbox->use_count++) {
|
||||
mq = mbox_queue_alloc(mbox, NULL, mbox_tx_tasklet);
|
||||
|
@ -319,7 +442,7 @@ static int omap_mbox_startup(struct omap_mbox *mbox)
|
|||
|
||||
omap_mbox_enable_irq(mbox, IRQ_RX);
|
||||
}
|
||||
mutex_unlock(&mbox_configured_lock);
|
||||
mutex_unlock(&mdev->cfg_lock);
|
||||
return 0;
|
||||
|
||||
fail_request_irq:
|
||||
|
@ -327,18 +450,18 @@ fail_request_irq:
|
|||
fail_alloc_rxq:
|
||||
mbox_queue_free(mbox->txq);
|
||||
fail_alloc_txq:
|
||||
if (mbox->ops->shutdown)
|
||||
mbox->ops->shutdown(mbox);
|
||||
pm_runtime_put_sync(mdev->dev);
|
||||
mbox->use_count--;
|
||||
fail_startup:
|
||||
mbox_configured--;
|
||||
mutex_unlock(&mbox_configured_lock);
|
||||
mutex_unlock(&mdev->cfg_lock);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void omap_mbox_fini(struct omap_mbox *mbox)
|
||||
{
|
||||
mutex_lock(&mbox_configured_lock);
|
||||
struct omap_mbox_device *mdev = mbox->parent;
|
||||
|
||||
mutex_lock(&mdev->cfg_lock);
|
||||
|
||||
if (!--mbox->use_count) {
|
||||
omap_mbox_disable_irq(mbox, IRQ_RX);
|
||||
|
@ -349,28 +472,43 @@ static void omap_mbox_fini(struct omap_mbox *mbox)
|
|||
mbox_queue_free(mbox->rxq);
|
||||
}
|
||||
|
||||
if (likely(mbox->ops->shutdown)) {
|
||||
if (!--mbox_configured)
|
||||
mbox->ops->shutdown(mbox);
|
||||
}
|
||||
pm_runtime_put_sync(mdev->dev);
|
||||
|
||||
mutex_unlock(&mbox_configured_lock);
|
||||
mutex_unlock(&mdev->cfg_lock);
|
||||
}
|
||||
|
||||
struct omap_mbox *omap_mbox_get(const char *name, struct notifier_block *nb)
|
||||
static struct omap_mbox *omap_mbox_device_find(struct omap_mbox_device *mdev,
|
||||
const char *mbox_name)
|
||||
{
|
||||
struct omap_mbox *_mbox, *mbox = NULL;
|
||||
int i, ret;
|
||||
struct omap_mbox **mboxes = mdev->mboxes;
|
||||
int i;
|
||||
|
||||
if (!mboxes)
|
||||
return ERR_PTR(-EINVAL);
|
||||
return NULL;
|
||||
|
||||
for (i = 0; (_mbox = mboxes[i]); i++) {
|
||||
if (!strcmp(_mbox->name, name)) {
|
||||
if (!strcmp(_mbox->name, mbox_name)) {
|
||||
mbox = _mbox;
|
||||
break;
|
||||
}
|
||||
}
|
||||
return mbox;
|
||||
}
|
||||
|
||||
struct omap_mbox *omap_mbox_get(const char *name, struct notifier_block *nb)
|
||||
{
|
||||
struct omap_mbox *mbox = NULL;
|
||||
struct omap_mbox_device *mdev;
|
||||
int ret;
|
||||
|
||||
mutex_lock(&omap_mbox_devices_lock);
|
||||
list_for_each_entry(mdev, &omap_mbox_devices, elem) {
|
||||
mbox = omap_mbox_device_find(mdev, name);
|
||||
if (mbox)
|
||||
break;
|
||||
}
|
||||
mutex_unlock(&omap_mbox_devices_lock);
|
||||
|
||||
if (!mbox)
|
||||
return ERR_PTR(-ENOENT);
|
||||
|
@ -397,19 +535,20 @@ EXPORT_SYMBOL(omap_mbox_put);
|
|||
|
||||
static struct class omap_mbox_class = { .name = "mbox", };
|
||||
|
||||
int omap_mbox_register(struct device *parent, struct omap_mbox **list)
|
||||
static int omap_mbox_register(struct omap_mbox_device *mdev)
|
||||
{
|
||||
int ret;
|
||||
int i;
|
||||
struct omap_mbox **mboxes;
|
||||
|
||||
mboxes = list;
|
||||
if (!mboxes)
|
||||
if (!mdev || !mdev->mboxes)
|
||||
return -EINVAL;
|
||||
|
||||
mboxes = mdev->mboxes;
|
||||
for (i = 0; mboxes[i]; i++) {
|
||||
struct omap_mbox *mbox = mboxes[i];
|
||||
mbox->dev = device_create(&omap_mbox_class,
|
||||
parent, 0, mbox, "%s", mbox->name);
|
||||
mdev->dev, 0, mbox, "%s", mbox->name);
|
||||
if (IS_ERR(mbox->dev)) {
|
||||
ret = PTR_ERR(mbox->dev);
|
||||
goto err_out;
|
||||
|
@ -417,6 +556,11 @@ int omap_mbox_register(struct device *parent, struct omap_mbox **list)
|
|||
|
||||
BLOCKING_INIT_NOTIFIER_HEAD(&mbox->notifier);
|
||||
}
|
||||
|
||||
mutex_lock(&omap_mbox_devices_lock);
|
||||
list_add(&mdev->elem, &omap_mbox_devices);
|
||||
mutex_unlock(&omap_mbox_devices_lock);
|
||||
|
||||
return 0;
|
||||
|
||||
err_out:
|
||||
|
@ -424,21 +568,148 @@ err_out:
|
|||
device_unregister(mboxes[i]->dev);
|
||||
return ret;
|
||||
}
|
||||
EXPORT_SYMBOL(omap_mbox_register);
|
||||
|
||||
int omap_mbox_unregister(void)
|
||||
static int omap_mbox_unregister(struct omap_mbox_device *mdev)
|
||||
{
|
||||
int i;
|
||||
struct omap_mbox **mboxes;
|
||||
|
||||
if (!mboxes)
|
||||
if (!mdev || !mdev->mboxes)
|
||||
return -EINVAL;
|
||||
|
||||
mutex_lock(&omap_mbox_devices_lock);
|
||||
list_del(&mdev->elem);
|
||||
mutex_unlock(&omap_mbox_devices_lock);
|
||||
|
||||
mboxes = mdev->mboxes;
|
||||
for (i = 0; mboxes[i]; i++)
|
||||
device_unregister(mboxes[i]->dev);
|
||||
mboxes = NULL;
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL(omap_mbox_unregister);
|
||||
|
||||
static int omap_mbox_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct resource *mem;
|
||||
int ret;
|
||||
struct omap_mbox **list, *mbox, *mboxblk;
|
||||
struct omap_mbox_pdata *pdata = pdev->dev.platform_data;
|
||||
struct omap_mbox_dev_info *info;
|
||||
struct omap_mbox_device *mdev;
|
||||
struct omap_mbox_fifo *fifo;
|
||||
u32 intr_type;
|
||||
u32 l;
|
||||
int i;
|
||||
|
||||
if (!pdata || !pdata->info_cnt || !pdata->info) {
|
||||
pr_err("%s: platform not supported\n", __func__);
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
mdev = devm_kzalloc(&pdev->dev, sizeof(*mdev), GFP_KERNEL);
|
||||
if (!mdev)
|
||||
return -ENOMEM;
|
||||
|
||||
mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
||||
mdev->mbox_base = devm_ioremap_resource(&pdev->dev, mem);
|
||||
if (IS_ERR(mdev->mbox_base))
|
||||
return PTR_ERR(mdev->mbox_base);
|
||||
|
||||
/* allocate one extra for marking end of list */
|
||||
list = devm_kzalloc(&pdev->dev, (pdata->info_cnt + 1) * sizeof(*list),
|
||||
GFP_KERNEL);
|
||||
if (!list)
|
||||
return -ENOMEM;
|
||||
|
||||
mboxblk = devm_kzalloc(&pdev->dev, pdata->info_cnt * sizeof(*mbox),
|
||||
GFP_KERNEL);
|
||||
if (!mboxblk)
|
||||
return -ENOMEM;
|
||||
|
||||
info = pdata->info;
|
||||
intr_type = pdata->intr_type;
|
||||
mbox = mboxblk;
|
||||
for (i = 0; i < pdata->info_cnt; i++, info++) {
|
||||
fifo = &mbox->tx_fifo;
|
||||
fifo->msg = MAILBOX_MESSAGE(info->tx_id);
|
||||
fifo->fifo_stat = MAILBOX_FIFOSTATUS(info->tx_id);
|
||||
fifo->intr_bit = MAILBOX_IRQ_NOTFULL(info->tx_id);
|
||||
fifo->irqenable = MAILBOX_IRQENABLE(intr_type, info->usr_id);
|
||||
fifo->irqstatus = MAILBOX_IRQSTATUS(intr_type, info->usr_id);
|
||||
fifo->irqdisable = MAILBOX_IRQDISABLE(intr_type, info->usr_id);
|
||||
|
||||
fifo = &mbox->rx_fifo;
|
||||
fifo->msg = MAILBOX_MESSAGE(info->rx_id);
|
||||
fifo->msg_stat = MAILBOX_MSGSTATUS(info->rx_id);
|
||||
fifo->intr_bit = MAILBOX_IRQ_NEWMSG(info->rx_id);
|
||||
fifo->irqenable = MAILBOX_IRQENABLE(intr_type, info->usr_id);
|
||||
fifo->irqstatus = MAILBOX_IRQSTATUS(intr_type, info->usr_id);
|
||||
fifo->irqdisable = MAILBOX_IRQDISABLE(intr_type, info->usr_id);
|
||||
|
||||
mbox->intr_type = intr_type;
|
||||
|
||||
mbox->parent = mdev;
|
||||
mbox->name = info->name;
|
||||
mbox->irq = platform_get_irq(pdev, info->irq_id);
|
||||
if (mbox->irq < 0)
|
||||
return mbox->irq;
|
||||
list[i] = mbox++;
|
||||
}
|
||||
|
||||
mutex_init(&mdev->cfg_lock);
|
||||
mdev->dev = &pdev->dev;
|
||||
mdev->num_users = pdata->num_users;
|
||||
mdev->num_fifos = pdata->num_fifos;
|
||||
mdev->mboxes = list;
|
||||
ret = omap_mbox_register(mdev);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
platform_set_drvdata(pdev, mdev);
|
||||
pm_runtime_enable(mdev->dev);
|
||||
|
||||
ret = pm_runtime_get_sync(mdev->dev);
|
||||
if (ret < 0) {
|
||||
pm_runtime_put_noidle(mdev->dev);
|
||||
goto unregister;
|
||||
}
|
||||
|
||||
/*
|
||||
* just print the raw revision register, the format is not
|
||||
* uniform across all SoCs
|
||||
*/
|
||||
l = mbox_read_reg(mdev, MAILBOX_REVISION);
|
||||
dev_info(mdev->dev, "omap mailbox rev 0x%x\n", l);
|
||||
|
||||
ret = pm_runtime_put_sync(mdev->dev);
|
||||
if (ret < 0)
|
||||
goto unregister;
|
||||
|
||||
return 0;
|
||||
|
||||
unregister:
|
||||
pm_runtime_disable(mdev->dev);
|
||||
omap_mbox_unregister(mdev);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int omap_mbox_remove(struct platform_device *pdev)
|
||||
{
|
||||
struct omap_mbox_device *mdev = platform_get_drvdata(pdev);
|
||||
|
||||
pm_runtime_disable(mdev->dev);
|
||||
omap_mbox_unregister(mdev);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct platform_driver omap_mbox_driver = {
|
||||
.probe = omap_mbox_probe,
|
||||
.remove = omap_mbox_remove,
|
||||
.driver = {
|
||||
.name = "omap-mailbox",
|
||||
.owner = THIS_MODULE,
|
||||
},
|
||||
};
|
||||
|
||||
static int __init omap_mbox_init(void)
|
||||
{
|
||||
|
@ -453,12 +724,13 @@ static int __init omap_mbox_init(void)
|
|||
mbox_kfifo_size = max_t(unsigned int, mbox_kfifo_size,
|
||||
sizeof(mbox_msg_t));
|
||||
|
||||
return 0;
|
||||
return platform_driver_register(&omap_mbox_driver);
|
||||
}
|
||||
subsys_initcall(omap_mbox_init);
|
||||
|
||||
static void __exit omap_mbox_exit(void)
|
||||
{
|
||||
platform_driver_unregister(&omap_mbox_driver);
|
||||
class_unregister(&omap_mbox_class);
|
||||
}
|
||||
module_exit(omap_mbox_exit);
|
||||
|
|
|
@ -1,67 +0,0 @@
|
|||
/*
|
||||
* omap-mbox.h: OMAP mailbox internal definitions
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 as
|
||||
* published by the Free Software Foundation.
|
||||
*/
|
||||
|
||||
#ifndef OMAP_MBOX_H
|
||||
#define OMAP_MBOX_H
|
||||
|
||||
#include <linux/device.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/kfifo.h>
|
||||
#include <linux/spinlock.h>
|
||||
#include <linux/workqueue.h>
|
||||
#include <linux/omap-mailbox.h>
|
||||
|
||||
typedef int __bitwise omap_mbox_type_t;
|
||||
#define OMAP_MBOX_TYPE1 ((__force omap_mbox_type_t) 1)
|
||||
#define OMAP_MBOX_TYPE2 ((__force omap_mbox_type_t) 2)
|
||||
|
||||
struct omap_mbox_ops {
|
||||
omap_mbox_type_t type;
|
||||
int (*startup)(struct omap_mbox *mbox);
|
||||
void (*shutdown)(struct omap_mbox *mbox);
|
||||
/* fifo */
|
||||
mbox_msg_t (*fifo_read)(struct omap_mbox *mbox);
|
||||
void (*fifo_write)(struct omap_mbox *mbox, mbox_msg_t msg);
|
||||
int (*fifo_empty)(struct omap_mbox *mbox);
|
||||
int (*fifo_full)(struct omap_mbox *mbox);
|
||||
/* irq */
|
||||
void (*enable_irq)(struct omap_mbox *mbox,
|
||||
omap_mbox_irq_t irq);
|
||||
void (*disable_irq)(struct omap_mbox *mbox,
|
||||
omap_mbox_irq_t irq);
|
||||
void (*ack_irq)(struct omap_mbox *mbox, omap_mbox_irq_t irq);
|
||||
int (*is_irq)(struct omap_mbox *mbox, omap_mbox_irq_t irq);
|
||||
/* ctx */
|
||||
void (*save_ctx)(struct omap_mbox *mbox);
|
||||
void (*restore_ctx)(struct omap_mbox *mbox);
|
||||
};
|
||||
|
||||
struct omap_mbox_queue {
|
||||
spinlock_t lock;
|
||||
struct kfifo fifo;
|
||||
struct work_struct work;
|
||||
struct tasklet_struct tasklet;
|
||||
struct omap_mbox *mbox;
|
||||
bool full;
|
||||
};
|
||||
|
||||
struct omap_mbox {
|
||||
const char *name;
|
||||
int irq;
|
||||
struct omap_mbox_queue *txq, *rxq;
|
||||
struct omap_mbox_ops *ops;
|
||||
struct device *dev;
|
||||
void *priv;
|
||||
int use_count;
|
||||
struct blocking_notifier_head notifier;
|
||||
};
|
||||
|
||||
int omap_mbox_register(struct device *parent, struct omap_mbox **);
|
||||
int omap_mbox_unregister(void);
|
||||
|
||||
#endif /* OMAP_MBOX_H */
|
|
@ -51,16 +51,6 @@ config AD525X_DPOT_SPI
|
|||
To compile this driver as a module, choose M here: the
|
||||
module will be called ad525x_dpot-spi.
|
||||
|
||||
config ATMEL_PWM
|
||||
tristate "Atmel AT32/AT91 PWM support"
|
||||
depends on HAVE_CLK
|
||||
depends on AVR32 || ARCH_AT91SAM9263 || ARCH_AT91SAM9RL || ARCH_AT91SAM9G45
|
||||
help
|
||||
This option enables device driver support for the PWM channels
|
||||
on certain Atmel processors. Pulse Width Modulation is used for
|
||||
purposes including software controlled power-efficient backlights
|
||||
on LCD displays, motor control, and waveform generation.
|
||||
|
||||
config ATMEL_TCLIB
|
||||
bool "Atmel AT32/AT91 Timer/Counter Library"
|
||||
depends on (AVR32 || ARCH_AT91)
|
||||
|
|
|
@ -7,7 +7,6 @@ obj-$(CONFIG_AD525X_DPOT) += ad525x_dpot.o
|
|||
obj-$(CONFIG_AD525X_DPOT_I2C) += ad525x_dpot-i2c.o
|
||||
obj-$(CONFIG_AD525X_DPOT_SPI) += ad525x_dpot-spi.o
|
||||
obj-$(CONFIG_INTEL_MID_PTI) += pti.o
|
||||
obj-$(CONFIG_ATMEL_PWM) += atmel_pwm.o
|
||||
obj-$(CONFIG_ATMEL_SSC) += atmel-ssc.o
|
||||
obj-$(CONFIG_ATMEL_TCLIB) += atmel_tclib.o
|
||||
obj-$(CONFIG_BMP085) += bmp085.o
|
||||
|
|
|
@ -1,402 +0,0 @@
|
|||
#include <linux/module.h>
|
||||
#include <linux/clk.h>
|
||||
#include <linux/err.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/io.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/atmel_pwm.h>
|
||||
|
||||
|
||||
/*
|
||||
* This is a simple driver for the PWM controller found in various newer
|
||||
* Atmel SOCs, including the AVR32 series and the AT91sam9263.
|
||||
*
|
||||
* Chips with current Linux ports have only 4 PWM channels, out of max 32.
|
||||
* AT32UC3A and AT32UC3B chips have 7 channels (but currently no Linux).
|
||||
* Docs are inconsistent about the width of the channel counter registers;
|
||||
* it's at least 16 bits, but several places say 20 bits.
|
||||
*/
|
||||
#define PWM_NCHAN 4 /* max 32 */
|
||||
|
||||
struct pwm {
|
||||
spinlock_t lock;
|
||||
struct platform_device *pdev;
|
||||
u32 mask;
|
||||
int irq;
|
||||
void __iomem *base;
|
||||
struct clk *clk;
|
||||
struct pwm_channel *channel[PWM_NCHAN];
|
||||
void (*handler[PWM_NCHAN])(struct pwm_channel *);
|
||||
};
|
||||
|
||||
|
||||
/* global PWM controller registers */
|
||||
#define PWM_MR 0x00
|
||||
#define PWM_ENA 0x04
|
||||
#define PWM_DIS 0x08
|
||||
#define PWM_SR 0x0c
|
||||
#define PWM_IER 0x10
|
||||
#define PWM_IDR 0x14
|
||||
#define PWM_IMR 0x18
|
||||
#define PWM_ISR 0x1c
|
||||
|
||||
static inline void pwm_writel(const struct pwm *p, unsigned offset, u32 val)
|
||||
{
|
||||
__raw_writel(val, p->base + offset);
|
||||
}
|
||||
|
||||
static inline u32 pwm_readl(const struct pwm *p, unsigned offset)
|
||||
{
|
||||
return __raw_readl(p->base + offset);
|
||||
}
|
||||
|
||||
static inline void __iomem *pwmc_regs(const struct pwm *p, int index)
|
||||
{
|
||||
return p->base + 0x200 + index * 0x20;
|
||||
}
|
||||
|
||||
static struct pwm *pwm;
|
||||
|
||||
static void pwm_dumpregs(struct pwm_channel *ch, char *tag)
|
||||
{
|
||||
struct device *dev = &pwm->pdev->dev;
|
||||
|
||||
dev_dbg(dev, "%s: mr %08x, sr %08x, imr %08x\n",
|
||||
tag,
|
||||
pwm_readl(pwm, PWM_MR),
|
||||
pwm_readl(pwm, PWM_SR),
|
||||
pwm_readl(pwm, PWM_IMR));
|
||||
dev_dbg(dev,
|
||||
"pwm ch%d - mr %08x, dty %u, prd %u, cnt %u\n",
|
||||
ch->index,
|
||||
pwm_channel_readl(ch, PWM_CMR),
|
||||
pwm_channel_readl(ch, PWM_CDTY),
|
||||
pwm_channel_readl(ch, PWM_CPRD),
|
||||
pwm_channel_readl(ch, PWM_CCNT));
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* pwm_channel_alloc - allocate an unused PWM channel
|
||||
* @index: identifies the channel
|
||||
* @ch: structure to be initialized
|
||||
*
|
||||
* Drivers allocate PWM channels according to the board's wiring, and
|
||||
* matching board-specific setup code. Returns zero or negative errno.
|
||||
*/
|
||||
int pwm_channel_alloc(int index, struct pwm_channel *ch)
|
||||
{
|
||||
unsigned long flags;
|
||||
int status = 0;
|
||||
|
||||
if (!pwm)
|
||||
return -EPROBE_DEFER;
|
||||
|
||||
if (!(pwm->mask & 1 << index))
|
||||
return -ENODEV;
|
||||
|
||||
if (index < 0 || index >= PWM_NCHAN || !ch)
|
||||
return -EINVAL;
|
||||
memset(ch, 0, sizeof *ch);
|
||||
|
||||
spin_lock_irqsave(&pwm->lock, flags);
|
||||
if (pwm->channel[index])
|
||||
status = -EBUSY;
|
||||
else {
|
||||
clk_enable(pwm->clk);
|
||||
|
||||
ch->regs = pwmc_regs(pwm, index);
|
||||
ch->index = index;
|
||||
|
||||
/* REVISIT: ap7000 seems to go 2x as fast as we expect!! */
|
||||
ch->mck = clk_get_rate(pwm->clk);
|
||||
|
||||
pwm->channel[index] = ch;
|
||||
pwm->handler[index] = NULL;
|
||||
|
||||
/* channel and irq are always disabled when we return */
|
||||
pwm_writel(pwm, PWM_DIS, 1 << index);
|
||||
pwm_writel(pwm, PWM_IDR, 1 << index);
|
||||
}
|
||||
spin_unlock_irqrestore(&pwm->lock, flags);
|
||||
return status;
|
||||
}
|
||||
EXPORT_SYMBOL(pwm_channel_alloc);
|
||||
|
||||
static int pwmcheck(struct pwm_channel *ch)
|
||||
{
|
||||
int index;
|
||||
|
||||
if (!pwm)
|
||||
return -ENODEV;
|
||||
if (!ch)
|
||||
return -EINVAL;
|
||||
index = ch->index;
|
||||
if (index < 0 || index >= PWM_NCHAN || pwm->channel[index] != ch)
|
||||
return -EINVAL;
|
||||
|
||||
return index;
|
||||
}
|
||||
|
||||
/**
|
||||
* pwm_channel_free - release a previously allocated channel
|
||||
* @ch: the channel being released
|
||||
*
|
||||
* The channel is completely shut down (counter and IRQ disabled),
|
||||
* and made available for re-use. Returns zero, or negative errno.
|
||||
*/
|
||||
int pwm_channel_free(struct pwm_channel *ch)
|
||||
{
|
||||
unsigned long flags;
|
||||
int t;
|
||||
|
||||
spin_lock_irqsave(&pwm->lock, flags);
|
||||
t = pwmcheck(ch);
|
||||
if (t >= 0) {
|
||||
pwm->channel[t] = NULL;
|
||||
pwm->handler[t] = NULL;
|
||||
|
||||
/* channel and irq are always disabled when we return */
|
||||
pwm_writel(pwm, PWM_DIS, 1 << t);
|
||||
pwm_writel(pwm, PWM_IDR, 1 << t);
|
||||
|
||||
clk_disable(pwm->clk);
|
||||
t = 0;
|
||||
}
|
||||
spin_unlock_irqrestore(&pwm->lock, flags);
|
||||
return t;
|
||||
}
|
||||
EXPORT_SYMBOL(pwm_channel_free);
|
||||
|
||||
int __pwm_channel_onoff(struct pwm_channel *ch, int enabled)
|
||||
{
|
||||
unsigned long flags;
|
||||
int t;
|
||||
|
||||
/* OMITTED FUNCTIONALITY: starting several channels in synch */
|
||||
|
||||
spin_lock_irqsave(&pwm->lock, flags);
|
||||
t = pwmcheck(ch);
|
||||
if (t >= 0) {
|
||||
pwm_writel(pwm, enabled ? PWM_ENA : PWM_DIS, 1 << t);
|
||||
t = 0;
|
||||
pwm_dumpregs(ch, enabled ? "enable" : "disable");
|
||||
}
|
||||
spin_unlock_irqrestore(&pwm->lock, flags);
|
||||
|
||||
return t;
|
||||
}
|
||||
EXPORT_SYMBOL(__pwm_channel_onoff);
|
||||
|
||||
/**
|
||||
* pwm_clk_alloc - allocate and configure CLKA or CLKB
|
||||
* @prescale: from 0..10, the power of two used to divide MCK
|
||||
* @div: from 1..255, the linear divisor to use
|
||||
*
|
||||
* Returns PWM_CPR_CLKA, PWM_CPR_CLKB, or negative errno. The allocated
|
||||
* clock will run with a period of (2^prescale * div) / MCK, or twice as
|
||||
* long if center aligned PWM output is used. The clock must later be
|
||||
* deconfigured using pwm_clk_free().
|
||||
*/
|
||||
int pwm_clk_alloc(unsigned prescale, unsigned div)
|
||||
{
|
||||
unsigned long flags;
|
||||
u32 mr;
|
||||
u32 val = (prescale << 8) | div;
|
||||
int ret = -EBUSY;
|
||||
|
||||
if (prescale >= 10 || div == 0 || div > 255)
|
||||
return -EINVAL;
|
||||
|
||||
spin_lock_irqsave(&pwm->lock, flags);
|
||||
mr = pwm_readl(pwm, PWM_MR);
|
||||
if ((mr & 0xffff) == 0) {
|
||||
mr |= val;
|
||||
ret = PWM_CPR_CLKA;
|
||||
} else if ((mr & (0xffff << 16)) == 0) {
|
||||
mr |= val << 16;
|
||||
ret = PWM_CPR_CLKB;
|
||||
}
|
||||
if (ret > 0)
|
||||
pwm_writel(pwm, PWM_MR, mr);
|
||||
spin_unlock_irqrestore(&pwm->lock, flags);
|
||||
return ret;
|
||||
}
|
||||
EXPORT_SYMBOL(pwm_clk_alloc);
|
||||
|
||||
/**
|
||||
* pwm_clk_free - deconfigure and release CLKA or CLKB
|
||||
*
|
||||
* Reverses the effect of pwm_clk_alloc().
|
||||
*/
|
||||
void pwm_clk_free(unsigned clk)
|
||||
{
|
||||
unsigned long flags;
|
||||
u32 mr;
|
||||
|
||||
spin_lock_irqsave(&pwm->lock, flags);
|
||||
mr = pwm_readl(pwm, PWM_MR);
|
||||
if (clk == PWM_CPR_CLKA)
|
||||
pwm_writel(pwm, PWM_MR, mr & ~(0xffff << 0));
|
||||
if (clk == PWM_CPR_CLKB)
|
||||
pwm_writel(pwm, PWM_MR, mr & ~(0xffff << 16));
|
||||
spin_unlock_irqrestore(&pwm->lock, flags);
|
||||
}
|
||||
EXPORT_SYMBOL(pwm_clk_free);
|
||||
|
||||
/**
|
||||
* pwm_channel_handler - manage channel's IRQ handler
|
||||
* @ch: the channel
|
||||
* @handler: the handler to use, possibly NULL
|
||||
*
|
||||
* If the handler is non-null, the handler will be called after every
|
||||
* period of this PWM channel. If the handler is null, this channel
|
||||
* won't generate an IRQ.
|
||||
*/
|
||||
int pwm_channel_handler(struct pwm_channel *ch,
|
||||
void (*handler)(struct pwm_channel *ch))
|
||||
{
|
||||
unsigned long flags;
|
||||
int t;
|
||||
|
||||
spin_lock_irqsave(&pwm->lock, flags);
|
||||
t = pwmcheck(ch);
|
||||
if (t >= 0) {
|
||||
pwm->handler[t] = handler;
|
||||
pwm_writel(pwm, handler ? PWM_IER : PWM_IDR, 1 << t);
|
||||
t = 0;
|
||||
}
|
||||
spin_unlock_irqrestore(&pwm->lock, flags);
|
||||
|
||||
return t;
|
||||
}
|
||||
EXPORT_SYMBOL(pwm_channel_handler);
|
||||
|
||||
static irqreturn_t pwm_irq(int id, void *_pwm)
|
||||
{
|
||||
struct pwm *p = _pwm;
|
||||
irqreturn_t handled = IRQ_NONE;
|
||||
u32 irqstat;
|
||||
int index;
|
||||
|
||||
spin_lock(&p->lock);
|
||||
|
||||
/* ack irqs, then handle them */
|
||||
irqstat = pwm_readl(pwm, PWM_ISR);
|
||||
|
||||
while (irqstat) {
|
||||
struct pwm_channel *ch;
|
||||
void (*handler)(struct pwm_channel *ch);
|
||||
|
||||
index = ffs(irqstat) - 1;
|
||||
irqstat &= ~(1 << index);
|
||||
ch = pwm->channel[index];
|
||||
handler = pwm->handler[index];
|
||||
if (handler && ch) {
|
||||
spin_unlock(&p->lock);
|
||||
handler(ch);
|
||||
spin_lock(&p->lock);
|
||||
handled = IRQ_HANDLED;
|
||||
}
|
||||
}
|
||||
|
||||
spin_unlock(&p->lock);
|
||||
return handled;
|
||||
}
|
||||
|
||||
static int __init pwm_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct resource *r = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
||||
int irq = platform_get_irq(pdev, 0);
|
||||
u32 *mp = pdev->dev.platform_data;
|
||||
struct pwm *p;
|
||||
int status = -EIO;
|
||||
|
||||
if (pwm)
|
||||
return -EBUSY;
|
||||
if (!r || irq < 0 || !mp || !*mp)
|
||||
return -ENODEV;
|
||||
if (*mp & ~((1<<PWM_NCHAN)-1)) {
|
||||
dev_warn(&pdev->dev, "mask 0x%x ... more than %d channels\n",
|
||||
*mp, PWM_NCHAN);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
p = kzalloc(sizeof(*p), GFP_KERNEL);
|
||||
if (!p)
|
||||
return -ENOMEM;
|
||||
|
||||
spin_lock_init(&p->lock);
|
||||
p->pdev = pdev;
|
||||
p->mask = *mp;
|
||||
p->irq = irq;
|
||||
p->base = ioremap(r->start, resource_size(r));
|
||||
if (!p->base)
|
||||
goto fail;
|
||||
p->clk = clk_get(&pdev->dev, "pwm_clk");
|
||||
if (IS_ERR(p->clk)) {
|
||||
status = PTR_ERR(p->clk);
|
||||
p->clk = NULL;
|
||||
goto fail;
|
||||
}
|
||||
|
||||
status = request_irq(irq, pwm_irq, 0, pdev->name, p);
|
||||
if (status < 0)
|
||||
goto fail;
|
||||
|
||||
pwm = p;
|
||||
platform_set_drvdata(pdev, p);
|
||||
|
||||
return 0;
|
||||
|
||||
fail:
|
||||
if (p->clk)
|
||||
clk_put(p->clk);
|
||||
if (p->base)
|
||||
iounmap(p->base);
|
||||
|
||||
kfree(p);
|
||||
return status;
|
||||
}
|
||||
|
||||
static int __exit pwm_remove(struct platform_device *pdev)
|
||||
{
|
||||
struct pwm *p = platform_get_drvdata(pdev);
|
||||
|
||||
if (p != pwm)
|
||||
return -EINVAL;
|
||||
|
||||
clk_enable(pwm->clk);
|
||||
pwm_writel(pwm, PWM_DIS, (1 << PWM_NCHAN) - 1);
|
||||
pwm_writel(pwm, PWM_IDR, (1 << PWM_NCHAN) - 1);
|
||||
clk_disable(pwm->clk);
|
||||
|
||||
pwm = NULL;
|
||||
|
||||
free_irq(p->irq, p);
|
||||
clk_put(p->clk);
|
||||
iounmap(p->base);
|
||||
kfree(p);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct platform_driver atmel_pwm_driver = {
|
||||
.driver = {
|
||||
.name = "atmel_pwm",
|
||||
.owner = THIS_MODULE,
|
||||
},
|
||||
.remove = __exit_p(pwm_remove),
|
||||
|
||||
/* NOTE: PWM can keep running in AVR32 "idle" and "frozen" states;
|
||||
* and all AT91sam9263 states, albeit at reduced clock rate if
|
||||
* MCK becomes the slow clock (i.e. what Linux labels STR).
|
||||
*/
|
||||
};
|
||||
|
||||
module_platform_driver_probe(atmel_pwm_driver, pwm_probe);
|
||||
|
||||
MODULE_DESCRIPTION("Driver for AT32/AT91 PWM module");
|
||||
MODULE_LICENSE("GPL");
|
||||
MODULE_ALIAS("platform:atmel_pwm");
|
|
@ -46,4 +46,12 @@ config PCI_HOST_GENERIC
|
|||
Say Y here if you want to support a simple generic PCI host
|
||||
controller, such as the one emulated by kvmtool.
|
||||
|
||||
config PCIE_SPEAR13XX
|
||||
tristate "STMicroelectronics SPEAr PCIe controller"
|
||||
depends on ARCH_SPEAR13XX
|
||||
select PCIEPORTBUS
|
||||
select PCIE_DW
|
||||
help
|
||||
Say Y here if you want PCIe support on SPEAr13XX SoCs.
|
||||
|
||||
endmenu
|
||||
|
|
|
@ -6,3 +6,4 @@ obj-$(CONFIG_PCI_TEGRA) += pci-tegra.o
|
|||
obj-$(CONFIG_PCI_RCAR_GEN2) += pci-rcar-gen2.o
|
||||
obj-$(CONFIG_PCI_RCAR_GEN2_PCIE) += pcie-rcar.o
|
||||
obj-$(CONFIG_PCI_HOST_GENERIC) += pci-host-generic.o
|
||||
obj-$(CONFIG_PCIE_SPEAR13XX) += pcie-spear13xx.o
|
||||
|
|
|
@ -234,7 +234,6 @@ struct tegra_pcie_soc_data {
|
|||
bool has_pex_clkreq_en;
|
||||
bool has_pex_bias_ctrl;
|
||||
bool has_intr_prsnt_sense;
|
||||
bool has_avdd_supply;
|
||||
bool has_cml_clk;
|
||||
};
|
||||
|
||||
|
@ -273,9 +272,8 @@ struct tegra_pcie {
|
|||
unsigned int num_ports;
|
||||
u32 xbar_config;
|
||||
|
||||
struct regulator *pex_clk_supply;
|
||||
struct regulator *vdd_supply;
|
||||
struct regulator *avdd_supply;
|
||||
struct regulator_bulk_data *supplies;
|
||||
unsigned int num_supplies;
|
||||
|
||||
const struct tegra_pcie_soc_data *soc_data;
|
||||
};
|
||||
|
@ -895,7 +893,6 @@ static int tegra_pcie_enable_controller(struct tegra_pcie *pcie)
|
|||
|
||||
static void tegra_pcie_power_off(struct tegra_pcie *pcie)
|
||||
{
|
||||
const struct tegra_pcie_soc_data *soc = pcie->soc_data;
|
||||
int err;
|
||||
|
||||
/* TODO: disable and unprepare clocks? */
|
||||
|
@ -906,23 +903,9 @@ static void tegra_pcie_power_off(struct tegra_pcie *pcie)
|
|||
|
||||
tegra_powergate_power_off(TEGRA_POWERGATE_PCIE);
|
||||
|
||||
if (soc->has_avdd_supply) {
|
||||
err = regulator_disable(pcie->avdd_supply);
|
||||
if (err < 0)
|
||||
dev_warn(pcie->dev,
|
||||
"failed to disable AVDD regulator: %d\n",
|
||||
err);
|
||||
}
|
||||
|
||||
err = regulator_disable(pcie->pex_clk_supply);
|
||||
err = regulator_bulk_disable(pcie->num_supplies, pcie->supplies);
|
||||
if (err < 0)
|
||||
dev_warn(pcie->dev, "failed to disable pex-clk regulator: %d\n",
|
||||
err);
|
||||
|
||||
err = regulator_disable(pcie->vdd_supply);
|
||||
if (err < 0)
|
||||
dev_warn(pcie->dev, "failed to disable VDD regulator: %d\n",
|
||||
err);
|
||||
dev_warn(pcie->dev, "failed to disable regulators: %d\n", err);
|
||||
}
|
||||
|
||||
static int tegra_pcie_power_on(struct tegra_pcie *pcie)
|
||||
|
@ -937,28 +920,9 @@ static int tegra_pcie_power_on(struct tegra_pcie *pcie)
|
|||
tegra_powergate_power_off(TEGRA_POWERGATE_PCIE);
|
||||
|
||||
/* enable regulators */
|
||||
err = regulator_enable(pcie->vdd_supply);
|
||||
if (err < 0) {
|
||||
dev_err(pcie->dev, "failed to enable VDD regulator: %d\n", err);
|
||||
return err;
|
||||
}
|
||||
|
||||
err = regulator_enable(pcie->pex_clk_supply);
|
||||
if (err < 0) {
|
||||
dev_err(pcie->dev, "failed to enable pex-clk regulator: %d\n",
|
||||
err);
|
||||
return err;
|
||||
}
|
||||
|
||||
if (soc->has_avdd_supply) {
|
||||
err = regulator_enable(pcie->avdd_supply);
|
||||
if (err < 0) {
|
||||
dev_err(pcie->dev,
|
||||
"failed to enable AVDD regulator: %d\n",
|
||||
err);
|
||||
return err;
|
||||
}
|
||||
}
|
||||
err = regulator_bulk_enable(pcie->num_supplies, pcie->supplies);
|
||||
if (err < 0)
|
||||
dev_err(pcie->dev, "failed to enable regulators: %d\n", err);
|
||||
|
||||
err = tegra_powergate_sequence_power_up(TEGRA_POWERGATE_PCIE,
|
||||
pcie->pex_clk,
|
||||
|
@ -1395,14 +1359,157 @@ static int tegra_pcie_get_xbar_config(struct tegra_pcie *pcie, u32 lanes,
|
|||
return -EINVAL;
|
||||
}
|
||||
|
||||
/*
|
||||
* Check whether a given set of supplies is available in a device tree node.
|
||||
* This is used to check whether the new or the legacy device tree bindings
|
||||
* should be used.
|
||||
*/
|
||||
static bool of_regulator_bulk_available(struct device_node *np,
|
||||
struct regulator_bulk_data *supplies,
|
||||
unsigned int num_supplies)
|
||||
{
|
||||
char property[32];
|
||||
unsigned int i;
|
||||
|
||||
for (i = 0; i < num_supplies; i++) {
|
||||
snprintf(property, 32, "%s-supply", supplies[i].supply);
|
||||
|
||||
if (of_find_property(np, property, NULL) == NULL)
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/*
|
||||
* Old versions of the device tree binding for this device used a set of power
|
||||
* supplies that didn't match the hardware inputs. This happened to work for a
|
||||
* number of cases but is not future proof. However to preserve backwards-
|
||||
* compatibility with old device trees, this function will try to use the old
|
||||
* set of supplies.
|
||||
*/
|
||||
static int tegra_pcie_get_legacy_regulators(struct tegra_pcie *pcie)
|
||||
{
|
||||
struct device_node *np = pcie->dev->of_node;
|
||||
|
||||
if (of_device_is_compatible(np, "nvidia,tegra30-pcie"))
|
||||
pcie->num_supplies = 3;
|
||||
else if (of_device_is_compatible(np, "nvidia,tegra20-pcie"))
|
||||
pcie->num_supplies = 2;
|
||||
|
||||
if (pcie->num_supplies == 0) {
|
||||
dev_err(pcie->dev, "device %s not supported in legacy mode\n",
|
||||
np->full_name);
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
pcie->supplies = devm_kcalloc(pcie->dev, pcie->num_supplies,
|
||||
sizeof(*pcie->supplies),
|
||||
GFP_KERNEL);
|
||||
if (!pcie->supplies)
|
||||
return -ENOMEM;
|
||||
|
||||
pcie->supplies[0].supply = "pex-clk";
|
||||
pcie->supplies[1].supply = "vdd";
|
||||
|
||||
if (pcie->num_supplies > 2)
|
||||
pcie->supplies[2].supply = "avdd";
|
||||
|
||||
return devm_regulator_bulk_get(pcie->dev, pcie->num_supplies,
|
||||
pcie->supplies);
|
||||
}
|
||||
|
||||
/*
|
||||
* Obtains the list of regulators required for a particular generation of the
|
||||
* IP block.
|
||||
*
|
||||
* This would've been nice to do simply by providing static tables for use
|
||||
* with the regulator_bulk_*() API, but unfortunately Tegra30 is a bit quirky
|
||||
* in that it has two pairs or AVDD_PEX and VDD_PEX supplies (PEXA and PEXB)
|
||||
* and either seems to be optional depending on which ports are being used.
|
||||
*/
|
||||
static int tegra_pcie_get_regulators(struct tegra_pcie *pcie, u32 lane_mask)
|
||||
{
|
||||
struct device_node *np = pcie->dev->of_node;
|
||||
unsigned int i = 0;
|
||||
|
||||
if (of_device_is_compatible(np, "nvidia,tegra30-pcie")) {
|
||||
bool need_pexa = false, need_pexb = false;
|
||||
|
||||
/* VDD_PEXA and AVDD_PEXA supply lanes 0 to 3 */
|
||||
if (lane_mask & 0x0f)
|
||||
need_pexa = true;
|
||||
|
||||
/* VDD_PEXB and AVDD_PEXB supply lanes 4 to 5 */
|
||||
if (lane_mask & 0x30)
|
||||
need_pexb = true;
|
||||
|
||||
pcie->num_supplies = 4 + (need_pexa ? 2 : 0) +
|
||||
(need_pexb ? 2 : 0);
|
||||
|
||||
pcie->supplies = devm_kcalloc(pcie->dev, pcie->num_supplies,
|
||||
sizeof(*pcie->supplies),
|
||||
GFP_KERNEL);
|
||||
if (!pcie->supplies)
|
||||
return -ENOMEM;
|
||||
|
||||
pcie->supplies[i++].supply = "avdd-pex-pll";
|
||||
pcie->supplies[i++].supply = "hvdd-pex";
|
||||
pcie->supplies[i++].supply = "vddio-pex-ctl";
|
||||
pcie->supplies[i++].supply = "avdd-plle";
|
||||
|
||||
if (need_pexa) {
|
||||
pcie->supplies[i++].supply = "avdd-pexa";
|
||||
pcie->supplies[i++].supply = "vdd-pexa";
|
||||
}
|
||||
|
||||
if (need_pexb) {
|
||||
pcie->supplies[i++].supply = "avdd-pexb";
|
||||
pcie->supplies[i++].supply = "vdd-pexb";
|
||||
}
|
||||
} else if (of_device_is_compatible(np, "nvidia,tegra20-pcie")) {
|
||||
pcie->num_supplies = 5;
|
||||
|
||||
pcie->supplies = devm_kcalloc(pcie->dev, pcie->num_supplies,
|
||||
sizeof(*pcie->supplies),
|
||||
GFP_KERNEL);
|
||||
if (!pcie->supplies)
|
||||
return -ENOMEM;
|
||||
|
||||
pcie->supplies[0].supply = "avdd-pex";
|
||||
pcie->supplies[1].supply = "vdd-pex";
|
||||
pcie->supplies[2].supply = "avdd-pex-pll";
|
||||
pcie->supplies[3].supply = "avdd-plle";
|
||||
pcie->supplies[4].supply = "vddio-pex-clk";
|
||||
}
|
||||
|
||||
if (of_regulator_bulk_available(pcie->dev->of_node, pcie->supplies,
|
||||
pcie->num_supplies))
|
||||
return devm_regulator_bulk_get(pcie->dev, pcie->num_supplies,
|
||||
pcie->supplies);
|
||||
|
||||
/*
|
||||
* If not all regulators are available for this new scheme, assume
|
||||
* that the device tree complies with an older version of the device
|
||||
* tree binding.
|
||||
*/
|
||||
dev_info(pcie->dev, "using legacy DT binding for power supplies\n");
|
||||
|
||||
devm_kfree(pcie->dev, pcie->supplies);
|
||||
pcie->num_supplies = 0;
|
||||
|
||||
return tegra_pcie_get_legacy_regulators(pcie);
|
||||
}
|
||||
|
||||
static int tegra_pcie_parse_dt(struct tegra_pcie *pcie)
|
||||
{
|
||||
const struct tegra_pcie_soc_data *soc = pcie->soc_data;
|
||||
struct device_node *np = pcie->dev->of_node, *port;
|
||||
struct of_pci_range_parser parser;
|
||||
struct of_pci_range range;
|
||||
u32 lanes = 0, mask = 0;
|
||||
unsigned int lane = 0;
|
||||
struct resource res;
|
||||
u32 lanes = 0;
|
||||
int err;
|
||||
|
||||
if (of_pci_range_parser_init(&parser, np)) {
|
||||
|
@ -1410,20 +1517,6 @@ static int tegra_pcie_parse_dt(struct tegra_pcie *pcie)
|
|||
return -EINVAL;
|
||||
}
|
||||
|
||||
pcie->vdd_supply = devm_regulator_get(pcie->dev, "vdd");
|
||||
if (IS_ERR(pcie->vdd_supply))
|
||||
return PTR_ERR(pcie->vdd_supply);
|
||||
|
||||
pcie->pex_clk_supply = devm_regulator_get(pcie->dev, "pex-clk");
|
||||
if (IS_ERR(pcie->pex_clk_supply))
|
||||
return PTR_ERR(pcie->pex_clk_supply);
|
||||
|
||||
if (soc->has_avdd_supply) {
|
||||
pcie->avdd_supply = devm_regulator_get(pcie->dev, "avdd");
|
||||
if (IS_ERR(pcie->avdd_supply))
|
||||
return PTR_ERR(pcie->avdd_supply);
|
||||
}
|
||||
|
||||
for_each_of_pci_range(&parser, &range) {
|
||||
of_pci_range_to_resource(&range, np, &res);
|
||||
|
||||
|
@ -1491,8 +1584,13 @@ static int tegra_pcie_parse_dt(struct tegra_pcie *pcie)
|
|||
|
||||
lanes |= value << (index << 3);
|
||||
|
||||
if (!of_device_is_available(port))
|
||||
if (!of_device_is_available(port)) {
|
||||
lane += value;
|
||||
continue;
|
||||
}
|
||||
|
||||
mask |= ((1 << value) - 1) << lane;
|
||||
lane += value;
|
||||
|
||||
rp = devm_kzalloc(pcie->dev, sizeof(*rp), GFP_KERNEL);
|
||||
if (!rp)
|
||||
|
@ -1523,6 +1621,10 @@ static int tegra_pcie_parse_dt(struct tegra_pcie *pcie)
|
|||
return err;
|
||||
}
|
||||
|
||||
err = tegra_pcie_get_regulators(pcie, mask);
|
||||
if (err < 0)
|
||||
return err;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -1616,7 +1718,6 @@ static const struct tegra_pcie_soc_data tegra20_pcie_data = {
|
|||
.has_pex_clkreq_en = false,
|
||||
.has_pex_bias_ctrl = false,
|
||||
.has_intr_prsnt_sense = false,
|
||||
.has_avdd_supply = false,
|
||||
.has_cml_clk = false,
|
||||
};
|
||||
|
||||
|
@ -1628,7 +1729,6 @@ static const struct tegra_pcie_soc_data tegra30_pcie_data = {
|
|||
.has_pex_clkreq_en = true,
|
||||
.has_pex_bias_ctrl = true,
|
||||
.has_intr_prsnt_sense = true,
|
||||
.has_avdd_supply = true,
|
||||
.has_cml_clk = true,
|
||||
};
|
||||
|
||||
|
|
|
@ -0,0 +1,393 @@
|
|||
/*
|
||||
* PCIe host controller driver for ST Microelectronics SPEAr13xx SoCs
|
||||
*
|
||||
* SPEAr13xx PCIe Glue Layer Source Code
|
||||
*
|
||||
* Copyright (C) 2010-2014 ST Microelectronics
|
||||
* Pratyush Anand <pratyush.anand@st.com>
|
||||
* Mohit Kumar <mohit.kumar@st.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/clk.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/pci.h>
|
||||
#include <linux/phy/phy.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/resource.h>
|
||||
|
||||
#include "pcie-designware.h"
|
||||
|
||||
struct spear13xx_pcie {
|
||||
void __iomem *app_base;
|
||||
struct phy *phy;
|
||||
struct clk *clk;
|
||||
struct pcie_port pp;
|
||||
bool is_gen1;
|
||||
};
|
||||
|
||||
struct pcie_app_reg {
|
||||
u32 app_ctrl_0; /* cr0 */
|
||||
u32 app_ctrl_1; /* cr1 */
|
||||
u32 app_status_0; /* cr2 */
|
||||
u32 app_status_1; /* cr3 */
|
||||
u32 msg_status; /* cr4 */
|
||||
u32 msg_payload; /* cr5 */
|
||||
u32 int_sts; /* cr6 */
|
||||
u32 int_clr; /* cr7 */
|
||||
u32 int_mask; /* cr8 */
|
||||
u32 mst_bmisc; /* cr9 */
|
||||
u32 phy_ctrl; /* cr10 */
|
||||
u32 phy_status; /* cr11 */
|
||||
u32 cxpl_debug_info_0; /* cr12 */
|
||||
u32 cxpl_debug_info_1; /* cr13 */
|
||||
u32 ven_msg_ctrl_0; /* cr14 */
|
||||
u32 ven_msg_ctrl_1; /* cr15 */
|
||||
u32 ven_msg_data_0; /* cr16 */
|
||||
u32 ven_msg_data_1; /* cr17 */
|
||||
u32 ven_msi_0; /* cr18 */
|
||||
u32 ven_msi_1; /* cr19 */
|
||||
u32 mst_rmisc; /* cr20 */
|
||||
};
|
||||
|
||||
/* CR0 ID */
|
||||
#define RX_LANE_FLIP_EN_ID 0
|
||||
#define TX_LANE_FLIP_EN_ID 1
|
||||
#define SYS_AUX_PWR_DET_ID 2
|
||||
#define APP_LTSSM_ENABLE_ID 3
|
||||
#define SYS_ATTEN_BUTTON_PRESSED_ID 4
|
||||
#define SYS_MRL_SENSOR_STATE_ID 5
|
||||
#define SYS_PWR_FAULT_DET_ID 6
|
||||
#define SYS_MRL_SENSOR_CHGED_ID 7
|
||||
#define SYS_PRE_DET_CHGED_ID 8
|
||||
#define SYS_CMD_CPLED_INT_ID 9
|
||||
#define APP_INIT_RST_0_ID 11
|
||||
#define APP_REQ_ENTR_L1_ID 12
|
||||
#define APP_READY_ENTR_L23_ID 13
|
||||
#define APP_REQ_EXIT_L1_ID 14
|
||||
#define DEVICE_TYPE_EP (0 << 25)
|
||||
#define DEVICE_TYPE_LEP (1 << 25)
|
||||
#define DEVICE_TYPE_RC (4 << 25)
|
||||
#define SYS_INT_ID 29
|
||||
#define MISCTRL_EN_ID 30
|
||||
#define REG_TRANSLATION_ENABLE 31
|
||||
|
||||
/* CR1 ID */
|
||||
#define APPS_PM_XMT_TURNOFF_ID 2
|
||||
#define APPS_PM_XMT_PME_ID 5
|
||||
|
||||
/* CR3 ID */
|
||||
#define XMLH_LTSSM_STATE_DETECT_QUIET 0x00
|
||||
#define XMLH_LTSSM_STATE_DETECT_ACT 0x01
|
||||
#define XMLH_LTSSM_STATE_POLL_ACTIVE 0x02
|
||||
#define XMLH_LTSSM_STATE_POLL_COMPLIANCE 0x03
|
||||
#define XMLH_LTSSM_STATE_POLL_CONFIG 0x04
|
||||
#define XMLH_LTSSM_STATE_PRE_DETECT_QUIET 0x05
|
||||
#define XMLH_LTSSM_STATE_DETECT_WAIT 0x06
|
||||
#define XMLH_LTSSM_STATE_CFG_LINKWD_START 0x07
|
||||
#define XMLH_LTSSM_STATE_CFG_LINKWD_ACEPT 0x08
|
||||
#define XMLH_LTSSM_STATE_CFG_LANENUM_WAIT 0x09
|
||||
#define XMLH_LTSSM_STATE_CFG_LANENUM_ACEPT 0x0A
|
||||
#define XMLH_LTSSM_STATE_CFG_COMPLETE 0x0B
|
||||
#define XMLH_LTSSM_STATE_CFG_IDLE 0x0C
|
||||
#define XMLH_LTSSM_STATE_RCVRY_LOCK 0x0D
|
||||
#define XMLH_LTSSM_STATE_RCVRY_SPEED 0x0E
|
||||
#define XMLH_LTSSM_STATE_RCVRY_RCVRCFG 0x0F
|
||||
#define XMLH_LTSSM_STATE_RCVRY_IDLE 0x10
|
||||
#define XMLH_LTSSM_STATE_L0 0x11
|
||||
#define XMLH_LTSSM_STATE_L0S 0x12
|
||||
#define XMLH_LTSSM_STATE_L123_SEND_EIDLE 0x13
|
||||
#define XMLH_LTSSM_STATE_L1_IDLE 0x14
|
||||
#define XMLH_LTSSM_STATE_L2_IDLE 0x15
|
||||
#define XMLH_LTSSM_STATE_L2_WAKE 0x16
|
||||
#define XMLH_LTSSM_STATE_DISABLED_ENTRY 0x17
|
||||
#define XMLH_LTSSM_STATE_DISABLED_IDLE 0x18
|
||||
#define XMLH_LTSSM_STATE_DISABLED 0x19
|
||||
#define XMLH_LTSSM_STATE_LPBK_ENTRY 0x1A
|
||||
#define XMLH_LTSSM_STATE_LPBK_ACTIVE 0x1B
|
||||
#define XMLH_LTSSM_STATE_LPBK_EXIT 0x1C
|
||||
#define XMLH_LTSSM_STATE_LPBK_EXIT_TIMEOUT 0x1D
|
||||
#define XMLH_LTSSM_STATE_HOT_RESET_ENTRY 0x1E
|
||||
#define XMLH_LTSSM_STATE_HOT_RESET 0x1F
|
||||
#define XMLH_LTSSM_STATE_MASK 0x3F
|
||||
#define XMLH_LINK_UP (1 << 6)
|
||||
|
||||
/* CR4 ID */
|
||||
#define CFG_MSI_EN_ID 18
|
||||
|
||||
/* CR6 */
|
||||
#define INTA_CTRL_INT (1 << 7)
|
||||
#define INTB_CTRL_INT (1 << 8)
|
||||
#define INTC_CTRL_INT (1 << 9)
|
||||
#define INTD_CTRL_INT (1 << 10)
|
||||
#define MSI_CTRL_INT (1 << 26)
|
||||
|
||||
/* CR19 ID */
|
||||
#define VEN_MSI_REQ_ID 11
|
||||
#define VEN_MSI_FUN_NUM_ID 8
|
||||
#define VEN_MSI_TC_ID 5
|
||||
#define VEN_MSI_VECTOR_ID 0
|
||||
#define VEN_MSI_REQ_EN ((u32)0x1 << VEN_MSI_REQ_ID)
|
||||
#define VEN_MSI_FUN_NUM_MASK ((u32)0x7 << VEN_MSI_FUN_NUM_ID)
|
||||
#define VEN_MSI_TC_MASK ((u32)0x7 << VEN_MSI_TC_ID)
|
||||
#define VEN_MSI_VECTOR_MASK ((u32)0x1F << VEN_MSI_VECTOR_ID)
|
||||
|
||||
#define EXP_CAP_ID_OFFSET 0x70
|
||||
|
||||
#define to_spear13xx_pcie(x) container_of(x, struct spear13xx_pcie, pp)
|
||||
|
||||
static int spear13xx_pcie_establish_link(struct pcie_port *pp)
|
||||
{
|
||||
u32 val;
|
||||
int count = 0;
|
||||
struct spear13xx_pcie *spear13xx_pcie = to_spear13xx_pcie(pp);
|
||||
struct pcie_app_reg *app_reg = spear13xx_pcie->app_base;
|
||||
u32 exp_cap_off = EXP_CAP_ID_OFFSET;
|
||||
|
||||
if (dw_pcie_link_up(pp)) {
|
||||
dev_err(pp->dev, "link already up\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
dw_pcie_setup_rc(pp);
|
||||
|
||||
/*
|
||||
* this controller support only 128 bytes read size, however its
|
||||
* default value in capability register is 512 bytes. So force
|
||||
* it to 128 here.
|
||||
*/
|
||||
dw_pcie_cfg_read(pp->dbi_base, exp_cap_off + PCI_EXP_DEVCTL, 4, &val);
|
||||
val &= ~PCI_EXP_DEVCTL_READRQ;
|
||||
dw_pcie_cfg_write(pp->dbi_base, exp_cap_off + PCI_EXP_DEVCTL, 4, val);
|
||||
|
||||
dw_pcie_cfg_write(pp->dbi_base, PCI_VENDOR_ID, 2, 0x104A);
|
||||
dw_pcie_cfg_write(pp->dbi_base, PCI_DEVICE_ID, 2, 0xCD80);
|
||||
|
||||
/*
|
||||
* if is_gen1 is set then handle it, so that some buggy card
|
||||
* also works
|
||||
*/
|
||||
if (spear13xx_pcie->is_gen1) {
|
||||
dw_pcie_cfg_read(pp->dbi_base, exp_cap_off + PCI_EXP_LNKCAP, 4,
|
||||
&val);
|
||||
if ((val & PCI_EXP_LNKCAP_SLS) != PCI_EXP_LNKCAP_SLS_2_5GB) {
|
||||
val &= ~((u32)PCI_EXP_LNKCAP_SLS);
|
||||
val |= PCI_EXP_LNKCAP_SLS_2_5GB;
|
||||
dw_pcie_cfg_write(pp->dbi_base, exp_cap_off +
|
||||
PCI_EXP_LNKCAP, 4, val);
|
||||
}
|
||||
|
||||
dw_pcie_cfg_read(pp->dbi_base, exp_cap_off + PCI_EXP_LNKCTL2, 4,
|
||||
&val);
|
||||
if ((val & PCI_EXP_LNKCAP_SLS) != PCI_EXP_LNKCAP_SLS_2_5GB) {
|
||||
val &= ~((u32)PCI_EXP_LNKCAP_SLS);
|
||||
val |= PCI_EXP_LNKCAP_SLS_2_5GB;
|
||||
dw_pcie_cfg_write(pp->dbi_base, exp_cap_off +
|
||||
PCI_EXP_LNKCTL2, 4, val);
|
||||
}
|
||||
}
|
||||
|
||||
/* enable ltssm */
|
||||
writel(DEVICE_TYPE_RC | (1 << MISCTRL_EN_ID)
|
||||
| (1 << APP_LTSSM_ENABLE_ID)
|
||||
| ((u32)1 << REG_TRANSLATION_ENABLE),
|
||||
&app_reg->app_ctrl_0);
|
||||
|
||||
/* check if the link is up or not */
|
||||
while (!dw_pcie_link_up(pp)) {
|
||||
mdelay(100);
|
||||
count++;
|
||||
if (count == 10) {
|
||||
dev_err(pp->dev, "link Fail\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
}
|
||||
dev_info(pp->dev, "link up\n");
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static irqreturn_t spear13xx_pcie_irq_handler(int irq, void *arg)
|
||||
{
|
||||
struct pcie_port *pp = arg;
|
||||
struct spear13xx_pcie *spear13xx_pcie = to_spear13xx_pcie(pp);
|
||||
struct pcie_app_reg *app_reg = spear13xx_pcie->app_base;
|
||||
unsigned int status;
|
||||
|
||||
status = readl(&app_reg->int_sts);
|
||||
|
||||
if (status & MSI_CTRL_INT) {
|
||||
if (!IS_ENABLED(CONFIG_PCI_MSI))
|
||||
BUG();
|
||||
dw_handle_msi_irq(pp);
|
||||
}
|
||||
|
||||
writel(status, &app_reg->int_clr);
|
||||
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
static void spear13xx_pcie_enable_interrupts(struct pcie_port *pp)
|
||||
{
|
||||
struct spear13xx_pcie *spear13xx_pcie = to_spear13xx_pcie(pp);
|
||||
struct pcie_app_reg *app_reg = spear13xx_pcie->app_base;
|
||||
|
||||
/* Enable MSI interrupt */
|
||||
if (IS_ENABLED(CONFIG_PCI_MSI)) {
|
||||
dw_pcie_msi_init(pp);
|
||||
writel(readl(&app_reg->int_mask) |
|
||||
MSI_CTRL_INT, &app_reg->int_mask);
|
||||
}
|
||||
}
|
||||
|
||||
static int spear13xx_pcie_link_up(struct pcie_port *pp)
|
||||
{
|
||||
struct spear13xx_pcie *spear13xx_pcie = to_spear13xx_pcie(pp);
|
||||
struct pcie_app_reg *app_reg = spear13xx_pcie->app_base;
|
||||
|
||||
if (readl(&app_reg->app_status_1) & XMLH_LINK_UP)
|
||||
return 1;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void spear13xx_pcie_host_init(struct pcie_port *pp)
|
||||
{
|
||||
spear13xx_pcie_establish_link(pp);
|
||||
spear13xx_pcie_enable_interrupts(pp);
|
||||
}
|
||||
|
||||
static struct pcie_host_ops spear13xx_pcie_host_ops = {
|
||||
.link_up = spear13xx_pcie_link_up,
|
||||
.host_init = spear13xx_pcie_host_init,
|
||||
};
|
||||
|
||||
static int add_pcie_port(struct pcie_port *pp, struct platform_device *pdev)
|
||||
{
|
||||
struct device *dev = &pdev->dev;
|
||||
int ret;
|
||||
|
||||
pp->irq = platform_get_irq(pdev, 0);
|
||||
if (!pp->irq) {
|
||||
dev_err(dev, "failed to get irq\n");
|
||||
return -ENODEV;
|
||||
}
|
||||
ret = devm_request_irq(dev, pp->irq, spear13xx_pcie_irq_handler,
|
||||
IRQF_SHARED, "spear1340-pcie", pp);
|
||||
if (ret) {
|
||||
dev_err(dev, "failed to request irq %d\n", pp->irq);
|
||||
return ret;
|
||||
}
|
||||
|
||||
pp->root_bus_nr = -1;
|
||||
pp->ops = &spear13xx_pcie_host_ops;
|
||||
|
||||
ret = dw_pcie_host_init(pp);
|
||||
if (ret) {
|
||||
dev_err(dev, "failed to initialize host\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int __init spear13xx_pcie_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct spear13xx_pcie *spear13xx_pcie;
|
||||
struct pcie_port *pp;
|
||||
struct device *dev = &pdev->dev;
|
||||
struct device_node *np = pdev->dev.of_node;
|
||||
struct resource *dbi_base;
|
||||
int ret;
|
||||
|
||||
spear13xx_pcie = devm_kzalloc(dev, sizeof(*spear13xx_pcie), GFP_KERNEL);
|
||||
if (!spear13xx_pcie) {
|
||||
dev_err(dev, "no memory for SPEAr13xx pcie\n");
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
spear13xx_pcie->phy = devm_phy_get(dev, "pcie-phy");
|
||||
if (IS_ERR(spear13xx_pcie->phy)) {
|
||||
ret = PTR_ERR(spear13xx_pcie->phy);
|
||||
if (ret == -EPROBE_DEFER)
|
||||
dev_info(dev, "probe deferred\n");
|
||||
else
|
||||
dev_err(dev, "couldn't get pcie-phy\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
phy_init(spear13xx_pcie->phy);
|
||||
|
||||
spear13xx_pcie->clk = devm_clk_get(dev, NULL);
|
||||
if (IS_ERR(spear13xx_pcie->clk)) {
|
||||
dev_err(dev, "couldn't get clk for pcie\n");
|
||||
return PTR_ERR(spear13xx_pcie->clk);
|
||||
}
|
||||
ret = clk_prepare_enable(spear13xx_pcie->clk);
|
||||
if (ret) {
|
||||
dev_err(dev, "couldn't enable clk for pcie\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
pp = &spear13xx_pcie->pp;
|
||||
|
||||
pp->dev = dev;
|
||||
|
||||
dbi_base = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
||||
pp->dbi_base = devm_ioremap_resource(dev, dbi_base);
|
||||
if (IS_ERR(pp->dbi_base)) {
|
||||
dev_err(dev, "couldn't remap dbi base %p\n", dbi_base);
|
||||
ret = PTR_ERR(pp->dbi_base);
|
||||
goto fail_clk;
|
||||
}
|
||||
spear13xx_pcie->app_base = pp->dbi_base + 0x2000;
|
||||
|
||||
if (of_property_read_bool(np, "st,pcie-is-gen1"))
|
||||
spear13xx_pcie->is_gen1 = true;
|
||||
|
||||
ret = add_pcie_port(pp, pdev);
|
||||
if (ret < 0)
|
||||
goto fail_clk;
|
||||
|
||||
platform_set_drvdata(pdev, spear13xx_pcie);
|
||||
return 0;
|
||||
|
||||
fail_clk:
|
||||
clk_disable_unprepare(spear13xx_pcie->clk);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static const struct of_device_id spear13xx_pcie_of_match[] = {
|
||||
{ .compatible = "st,spear1340-pcie", },
|
||||
{},
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, spear13xx_pcie_of_match);
|
||||
|
||||
static struct platform_driver spear13xx_pcie_driver __initdata = {
|
||||
.probe = spear13xx_pcie_probe,
|
||||
.driver = {
|
||||
.name = "spear-pcie",
|
||||
.owner = THIS_MODULE,
|
||||
.of_match_table = of_match_ptr(spear13xx_pcie_of_match),
|
||||
},
|
||||
};
|
||||
|
||||
/* SPEAr13xx PCIe driver does not allow module unload */
|
||||
|
||||
static int __init pcie_init(void)
|
||||
{
|
||||
return platform_driver_register(&spear13xx_pcie_driver);
|
||||
}
|
||||
module_init(pcie_init);
|
||||
|
||||
MODULE_DESCRIPTION("ST Microelectronics SPEAr13xx PCIe host controller driver");
|
||||
MODULE_AUTHOR("Pratyush Anand <pratyush.anand@st.com>");
|
||||
MODULE_LICENSE("GPL v2");
|
|
@ -197,13 +197,6 @@ config PHY_EXYNOS5_USBDRD
|
|||
This driver provides PHY interface for USB 3.0 DRD controller
|
||||
present on Exynos5 SoC series.
|
||||
|
||||
config PHY_XGENE
|
||||
tristate "APM X-Gene 15Gbps PHY support"
|
||||
depends on HAS_IOMEM && OF && (ARM64 || COMPILE_TEST)
|
||||
select GENERIC_PHY
|
||||
help
|
||||
This option enables support for APM X-Gene SoC multi-purpose PHY.
|
||||
|
||||
config PHY_QCOM_APQ8064_SATA
|
||||
tristate "Qualcomm APQ8064 SATA SerDes/PHY driver"
|
||||
depends on ARCH_QCOM
|
||||
|
@ -218,4 +211,23 @@ config PHY_QCOM_IPQ806X_SATA
|
|||
depends on OF
|
||||
select GENERIC_PHY
|
||||
|
||||
config PHY_ST_SPEAR1310_MIPHY
|
||||
tristate "ST SPEAR1310-MIPHY driver"
|
||||
select GENERIC_PHY
|
||||
help
|
||||
Support for ST SPEAr1310 MIPHY which can be used for PCIe and SATA.
|
||||
|
||||
config PHY_ST_SPEAR1340_MIPHY
|
||||
tristate "ST SPEAR1340-MIPHY driver"
|
||||
select GENERIC_PHY
|
||||
help
|
||||
Support for ST SPEAr1340 MIPHY which can be used for PCIe and SATA.
|
||||
|
||||
config PHY_XGENE
|
||||
tristate "APM X-Gene 15Gbps PHY support"
|
||||
depends on HAS_IOMEM && OF && (ARM64 || COMPILE_TEST)
|
||||
select GENERIC_PHY
|
||||
help
|
||||
This option enables support for APM X-Gene SoC multi-purpose PHY.
|
||||
|
||||
endmenu
|
||||
|
|
|
@ -23,6 +23,8 @@ phy-exynos-usb2-$(CONFIG_PHY_EXYNOS4X12_USB2) += phy-exynos4x12-usb2.o
|
|||
phy-exynos-usb2-$(CONFIG_PHY_EXYNOS5250_USB2) += phy-exynos5250-usb2.o
|
||||
phy-exynos-usb2-$(CONFIG_PHY_S5PV210_USB2) += phy-s5pv210-usb2.o
|
||||
obj-$(CONFIG_PHY_EXYNOS5_USBDRD) += phy-exynos5-usbdrd.o
|
||||
obj-$(CONFIG_PHY_XGENE) += phy-xgene.o
|
||||
obj-$(CONFIG_PHY_QCOM_APQ8064_SATA) += phy-qcom-apq8064-sata.o
|
||||
obj-$(CONFIG_PHY_QCOM_IPQ806X_SATA) += phy-qcom-ipq806x-sata.o
|
||||
obj-$(CONFIG_PHY_ST_SPEAR1310_MIPHY) += phy-spear1310-miphy.o
|
||||
obj-$(CONFIG_PHY_ST_SPEAR1340_MIPHY) += phy-spear1340-miphy.o
|
||||
obj-$(CONFIG_PHY_XGENE) += phy-xgene.o
|
||||
|
|
|
@ -0,0 +1,274 @@
|
|||
/*
|
||||
* ST SPEAr1310-miphy driver
|
||||
*
|
||||
* Copyright (C) 2014 ST Microelectronics
|
||||
* Pratyush Anand <pratyush.anand@st.com>
|
||||
* Mohit Kumar <mohit.kumar@st.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
*/
|
||||
|
||||
#include <linux/bitops.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/dma-mapping.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/mfd/syscon.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/of_device.h>
|
||||
#include <linux/phy/phy.h>
|
||||
#include <linux/regmap.h>
|
||||
|
||||
/* SPEAr1310 Registers */
|
||||
#define SPEAR1310_PCIE_SATA_CFG 0x3A4
|
||||
#define SPEAR1310_PCIE_SATA2_SEL_PCIE (0 << 31)
|
||||
#define SPEAR1310_PCIE_SATA1_SEL_PCIE (0 << 30)
|
||||
#define SPEAR1310_PCIE_SATA0_SEL_PCIE (0 << 29)
|
||||
#define SPEAR1310_PCIE_SATA2_SEL_SATA BIT(31)
|
||||
#define SPEAR1310_PCIE_SATA1_SEL_SATA BIT(30)
|
||||
#define SPEAR1310_PCIE_SATA0_SEL_SATA BIT(29)
|
||||
#define SPEAR1310_SATA2_CFG_TX_CLK_EN BIT(27)
|
||||
#define SPEAR1310_SATA2_CFG_RX_CLK_EN BIT(26)
|
||||
#define SPEAR1310_SATA2_CFG_POWERUP_RESET BIT(25)
|
||||
#define SPEAR1310_SATA2_CFG_PM_CLK_EN BIT(24)
|
||||
#define SPEAR1310_SATA1_CFG_TX_CLK_EN BIT(23)
|
||||
#define SPEAR1310_SATA1_CFG_RX_CLK_EN BIT(22)
|
||||
#define SPEAR1310_SATA1_CFG_POWERUP_RESET BIT(21)
|
||||
#define SPEAR1310_SATA1_CFG_PM_CLK_EN BIT(20)
|
||||
#define SPEAR1310_SATA0_CFG_TX_CLK_EN BIT(19)
|
||||
#define SPEAR1310_SATA0_CFG_RX_CLK_EN BIT(18)
|
||||
#define SPEAR1310_SATA0_CFG_POWERUP_RESET BIT(17)
|
||||
#define SPEAR1310_SATA0_CFG_PM_CLK_EN BIT(16)
|
||||
#define SPEAR1310_PCIE2_CFG_DEVICE_PRESENT BIT(11)
|
||||
#define SPEAR1310_PCIE2_CFG_POWERUP_RESET BIT(10)
|
||||
#define SPEAR1310_PCIE2_CFG_CORE_CLK_EN BIT(9)
|
||||
#define SPEAR1310_PCIE2_CFG_AUX_CLK_EN BIT(8)
|
||||
#define SPEAR1310_PCIE1_CFG_DEVICE_PRESENT BIT(7)
|
||||
#define SPEAR1310_PCIE1_CFG_POWERUP_RESET BIT(6)
|
||||
#define SPEAR1310_PCIE1_CFG_CORE_CLK_EN BIT(5)
|
||||
#define SPEAR1310_PCIE1_CFG_AUX_CLK_EN BIT(4)
|
||||
#define SPEAR1310_PCIE0_CFG_DEVICE_PRESENT BIT(3)
|
||||
#define SPEAR1310_PCIE0_CFG_POWERUP_RESET BIT(2)
|
||||
#define SPEAR1310_PCIE0_CFG_CORE_CLK_EN BIT(1)
|
||||
#define SPEAR1310_PCIE0_CFG_AUX_CLK_EN BIT(0)
|
||||
|
||||
#define SPEAR1310_PCIE_CFG_MASK(x) ((0xF << (x * 4)) | BIT((x + 29)))
|
||||
#define SPEAR1310_SATA_CFG_MASK(x) ((0xF << (x * 4 + 16)) | \
|
||||
BIT((x + 29)))
|
||||
#define SPEAR1310_PCIE_CFG_VAL(x) \
|
||||
(SPEAR1310_PCIE_SATA##x##_SEL_PCIE | \
|
||||
SPEAR1310_PCIE##x##_CFG_AUX_CLK_EN | \
|
||||
SPEAR1310_PCIE##x##_CFG_CORE_CLK_EN | \
|
||||
SPEAR1310_PCIE##x##_CFG_POWERUP_RESET | \
|
||||
SPEAR1310_PCIE##x##_CFG_DEVICE_PRESENT)
|
||||
#define SPEAR1310_SATA_CFG_VAL(x) \
|
||||
(SPEAR1310_PCIE_SATA##x##_SEL_SATA | \
|
||||
SPEAR1310_SATA##x##_CFG_PM_CLK_EN | \
|
||||
SPEAR1310_SATA##x##_CFG_POWERUP_RESET | \
|
||||
SPEAR1310_SATA##x##_CFG_RX_CLK_EN | \
|
||||
SPEAR1310_SATA##x##_CFG_TX_CLK_EN)
|
||||
|
||||
#define SPEAR1310_PCIE_MIPHY_CFG_1 0x3A8
|
||||
#define SPEAR1310_MIPHY_DUAL_OSC_BYPASS_EXT BIT(31)
|
||||
#define SPEAR1310_MIPHY_DUAL_CLK_REF_DIV2 BIT(28)
|
||||
#define SPEAR1310_MIPHY_DUAL_PLL_RATIO_TOP(x) (x << 16)
|
||||
#define SPEAR1310_MIPHY_SINGLE_OSC_BYPASS_EXT BIT(15)
|
||||
#define SPEAR1310_MIPHY_SINGLE_CLK_REF_DIV2 BIT(12)
|
||||
#define SPEAR1310_MIPHY_SINGLE_PLL_RATIO_TOP(x) (x << 0)
|
||||
#define SPEAR1310_PCIE_SATA_MIPHY_CFG_SATA_MASK (0xFFFF)
|
||||
#define SPEAR1310_PCIE_SATA_MIPHY_CFG_PCIE_MASK (0xFFFF << 16)
|
||||
#define SPEAR1310_PCIE_SATA_MIPHY_CFG_SATA \
|
||||
(SPEAR1310_MIPHY_DUAL_OSC_BYPASS_EXT | \
|
||||
SPEAR1310_MIPHY_DUAL_CLK_REF_DIV2 | \
|
||||
SPEAR1310_MIPHY_DUAL_PLL_RATIO_TOP(60) | \
|
||||
SPEAR1310_MIPHY_SINGLE_OSC_BYPASS_EXT | \
|
||||
SPEAR1310_MIPHY_SINGLE_CLK_REF_DIV2 | \
|
||||
SPEAR1310_MIPHY_SINGLE_PLL_RATIO_TOP(60))
|
||||
#define SPEAR1310_PCIE_SATA_MIPHY_CFG_SATA_25M_CRYSTAL_CLK \
|
||||
(SPEAR1310_MIPHY_SINGLE_PLL_RATIO_TOP(120))
|
||||
#define SPEAR1310_PCIE_SATA_MIPHY_CFG_PCIE \
|
||||
(SPEAR1310_MIPHY_DUAL_OSC_BYPASS_EXT | \
|
||||
SPEAR1310_MIPHY_DUAL_PLL_RATIO_TOP(25) | \
|
||||
SPEAR1310_MIPHY_SINGLE_OSC_BYPASS_EXT | \
|
||||
SPEAR1310_MIPHY_SINGLE_PLL_RATIO_TOP(25))
|
||||
|
||||
#define SPEAR1310_PCIE_MIPHY_CFG_2 0x3AC
|
||||
|
||||
enum spear1310_miphy_mode {
|
||||
SATA,
|
||||
PCIE,
|
||||
};
|
||||
|
||||
struct spear1310_miphy_priv {
|
||||
/* instance id of this phy */
|
||||
u32 id;
|
||||
/* phy mode: 0 for SATA 1 for PCIe */
|
||||
enum spear1310_miphy_mode mode;
|
||||
/* regmap for any soc specific misc registers */
|
||||
struct regmap *misc;
|
||||
/* phy struct pointer */
|
||||
struct phy *phy;
|
||||
};
|
||||
|
||||
static int spear1310_miphy_pcie_init(struct spear1310_miphy_priv *priv)
|
||||
{
|
||||
u32 val;
|
||||
|
||||
regmap_update_bits(priv->misc, SPEAR1310_PCIE_MIPHY_CFG_1,
|
||||
SPEAR1310_PCIE_SATA_MIPHY_CFG_PCIE_MASK,
|
||||
SPEAR1310_PCIE_SATA_MIPHY_CFG_PCIE);
|
||||
|
||||
switch (priv->id) {
|
||||
case 0:
|
||||
val = SPEAR1310_PCIE_CFG_VAL(0);
|
||||
break;
|
||||
case 1:
|
||||
val = SPEAR1310_PCIE_CFG_VAL(1);
|
||||
break;
|
||||
case 2:
|
||||
val = SPEAR1310_PCIE_CFG_VAL(2);
|
||||
break;
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
regmap_update_bits(priv->misc, SPEAR1310_PCIE_SATA_CFG,
|
||||
SPEAR1310_PCIE_CFG_MASK(priv->id), val);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int spear1310_miphy_pcie_exit(struct spear1310_miphy_priv *priv)
|
||||
{
|
||||
regmap_update_bits(priv->misc, SPEAR1310_PCIE_SATA_CFG,
|
||||
SPEAR1310_PCIE_CFG_MASK(priv->id), 0);
|
||||
|
||||
regmap_update_bits(priv->misc, SPEAR1310_PCIE_MIPHY_CFG_1,
|
||||
SPEAR1310_PCIE_SATA_MIPHY_CFG_PCIE_MASK, 0);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int spear1310_miphy_init(struct phy *phy)
|
||||
{
|
||||
struct spear1310_miphy_priv *priv = phy_get_drvdata(phy);
|
||||
int ret = 0;
|
||||
|
||||
if (priv->mode == PCIE)
|
||||
ret = spear1310_miphy_pcie_init(priv);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int spear1310_miphy_exit(struct phy *phy)
|
||||
{
|
||||
struct spear1310_miphy_priv *priv = phy_get_drvdata(phy);
|
||||
int ret = 0;
|
||||
|
||||
if (priv->mode == PCIE)
|
||||
ret = spear1310_miphy_pcie_exit(priv);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static const struct of_device_id spear1310_miphy_of_match[] = {
|
||||
{ .compatible = "st,spear1310-miphy" },
|
||||
{ },
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, spear1310_miphy_of_match);
|
||||
|
||||
static struct phy_ops spear1310_miphy_ops = {
|
||||
.init = spear1310_miphy_init,
|
||||
.exit = spear1310_miphy_exit,
|
||||
.owner = THIS_MODULE,
|
||||
};
|
||||
|
||||
static struct phy *spear1310_miphy_xlate(struct device *dev,
|
||||
struct of_phandle_args *args)
|
||||
{
|
||||
struct spear1310_miphy_priv *priv = dev_get_drvdata(dev);
|
||||
|
||||
if (args->args_count < 1) {
|
||||
dev_err(dev, "DT did not pass correct no of args\n");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
priv->mode = args->args[0];
|
||||
|
||||
if (priv->mode != SATA && priv->mode != PCIE) {
|
||||
dev_err(dev, "DT did not pass correct phy mode\n");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return priv->phy;
|
||||
}
|
||||
|
||||
static int spear1310_miphy_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct device *dev = &pdev->dev;
|
||||
struct spear1310_miphy_priv *priv;
|
||||
struct phy_provider *phy_provider;
|
||||
|
||||
priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
|
||||
if (!priv) {
|
||||
dev_err(dev, "can't alloc spear1310_miphy private date memory\n");
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
priv->misc =
|
||||
syscon_regmap_lookup_by_phandle(dev->of_node, "misc");
|
||||
if (IS_ERR(priv->misc)) {
|
||||
dev_err(dev, "failed to find misc regmap\n");
|
||||
return PTR_ERR(priv->misc);
|
||||
}
|
||||
|
||||
if (of_property_read_u32(dev->of_node, "phy-id", &priv->id)) {
|
||||
dev_err(dev, "failed to find phy id\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
priv->phy = devm_phy_create(dev, NULL, &spear1310_miphy_ops, NULL);
|
||||
if (IS_ERR(priv->phy)) {
|
||||
dev_err(dev, "failed to create SATA PCIe PHY\n");
|
||||
return PTR_ERR(priv->phy);
|
||||
}
|
||||
|
||||
dev_set_drvdata(dev, priv);
|
||||
phy_set_drvdata(priv->phy, priv);
|
||||
|
||||
phy_provider =
|
||||
devm_of_phy_provider_register(dev, spear1310_miphy_xlate);
|
||||
if (IS_ERR(phy_provider)) {
|
||||
dev_err(dev, "failed to register phy provider\n");
|
||||
return PTR_ERR(phy_provider);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct platform_driver spear1310_miphy_driver = {
|
||||
.probe = spear1310_miphy_probe,
|
||||
.driver = {
|
||||
.name = "spear1310-miphy",
|
||||
.owner = THIS_MODULE,
|
||||
.of_match_table = of_match_ptr(spear1310_miphy_of_match),
|
||||
},
|
||||
};
|
||||
|
||||
static int __init spear1310_miphy_phy_init(void)
|
||||
{
|
||||
return platform_driver_register(&spear1310_miphy_driver);
|
||||
}
|
||||
module_init(spear1310_miphy_phy_init);
|
||||
|
||||
static void __exit spear1310_miphy_phy_exit(void)
|
||||
{
|
||||
platform_driver_unregister(&spear1310_miphy_driver);
|
||||
}
|
||||
module_exit(spear1310_miphy_phy_exit);
|
||||
|
||||
MODULE_DESCRIPTION("ST SPEAR1310-MIPHY driver");
|
||||
MODULE_AUTHOR("Pratyush Anand <pratyush.anand@st.com>");
|
||||
MODULE_LICENSE("GPL v2");
|
|
@ -0,0 +1,307 @@
|
|||
/*
|
||||
* ST spear1340-miphy driver
|
||||
*
|
||||
* Copyright (C) 2014 ST Microelectronics
|
||||
* Pratyush Anand <pratyush.anand@st.com>
|
||||
* Mohit Kumar <mohit.kumar@st.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
*/
|
||||
|
||||
#include <linux/bitops.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/dma-mapping.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/mfd/syscon.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/of_device.h>
|
||||
#include <linux/phy/phy.h>
|
||||
#include <linux/regmap.h>
|
||||
|
||||
/* SPEAr1340 Registers */
|
||||
/* Power Management Registers */
|
||||
#define SPEAR1340_PCM_CFG 0x100
|
||||
#define SPEAR1340_PCM_CFG_SATA_POWER_EN BIT(11)
|
||||
#define SPEAR1340_PCM_WKUP_CFG 0x104
|
||||
#define SPEAR1340_SWITCH_CTR 0x108
|
||||
|
||||
#define SPEAR1340_PERIP1_SW_RST 0x318
|
||||
#define SPEAR1340_PERIP1_SW_RSATA BIT(12)
|
||||
#define SPEAR1340_PERIP2_SW_RST 0x31C
|
||||
#define SPEAR1340_PERIP3_SW_RST 0x320
|
||||
|
||||
/* PCIE - SATA configuration registers */
|
||||
#define SPEAR1340_PCIE_SATA_CFG 0x424
|
||||
/* PCIE CFG MASks */
|
||||
#define SPEAR1340_PCIE_CFG_DEVICE_PRESENT BIT(11)
|
||||
#define SPEAR1340_PCIE_CFG_POWERUP_RESET BIT(10)
|
||||
#define SPEAR1340_PCIE_CFG_CORE_CLK_EN BIT(9)
|
||||
#define SPEAR1340_PCIE_CFG_AUX_CLK_EN BIT(8)
|
||||
#define SPEAR1340_SATA_CFG_TX_CLK_EN BIT(4)
|
||||
#define SPEAR1340_SATA_CFG_RX_CLK_EN BIT(3)
|
||||
#define SPEAR1340_SATA_CFG_POWERUP_RESET BIT(2)
|
||||
#define SPEAR1340_SATA_CFG_PM_CLK_EN BIT(1)
|
||||
#define SPEAR1340_PCIE_SATA_SEL_PCIE (0)
|
||||
#define SPEAR1340_PCIE_SATA_SEL_SATA (1)
|
||||
#define SPEAR1340_PCIE_SATA_CFG_MASK 0xF1F
|
||||
#define SPEAR1340_PCIE_CFG_VAL (SPEAR1340_PCIE_SATA_SEL_PCIE | \
|
||||
SPEAR1340_PCIE_CFG_AUX_CLK_EN | \
|
||||
SPEAR1340_PCIE_CFG_CORE_CLK_EN | \
|
||||
SPEAR1340_PCIE_CFG_POWERUP_RESET | \
|
||||
SPEAR1340_PCIE_CFG_DEVICE_PRESENT)
|
||||
#define SPEAR1340_SATA_CFG_VAL (SPEAR1340_PCIE_SATA_SEL_SATA | \
|
||||
SPEAR1340_SATA_CFG_PM_CLK_EN | \
|
||||
SPEAR1340_SATA_CFG_POWERUP_RESET | \
|
||||
SPEAR1340_SATA_CFG_RX_CLK_EN | \
|
||||
SPEAR1340_SATA_CFG_TX_CLK_EN)
|
||||
|
||||
#define SPEAR1340_PCIE_MIPHY_CFG 0x428
|
||||
#define SPEAR1340_MIPHY_OSC_BYPASS_EXT BIT(31)
|
||||
#define SPEAR1340_MIPHY_CLK_REF_DIV2 BIT(27)
|
||||
#define SPEAR1340_MIPHY_CLK_REF_DIV4 (2 << 27)
|
||||
#define SPEAR1340_MIPHY_CLK_REF_DIV8 (3 << 27)
|
||||
#define SPEAR1340_MIPHY_PLL_RATIO_TOP(x) (x << 0)
|
||||
#define SPEAR1340_PCIE_MIPHY_CFG_MASK 0xF80000FF
|
||||
#define SPEAR1340_PCIE_SATA_MIPHY_CFG_SATA \
|
||||
(SPEAR1340_MIPHY_OSC_BYPASS_EXT | \
|
||||
SPEAR1340_MIPHY_CLK_REF_DIV2 | \
|
||||
SPEAR1340_MIPHY_PLL_RATIO_TOP(60))
|
||||
#define SPEAR1340_PCIE_SATA_MIPHY_CFG_SATA_25M_CRYSTAL_CLK \
|
||||
(SPEAR1340_MIPHY_PLL_RATIO_TOP(120))
|
||||
#define SPEAR1340_PCIE_SATA_MIPHY_CFG_PCIE \
|
||||
(SPEAR1340_MIPHY_OSC_BYPASS_EXT | \
|
||||
SPEAR1340_MIPHY_PLL_RATIO_TOP(25))
|
||||
|
||||
enum spear1340_miphy_mode {
|
||||
SATA,
|
||||
PCIE,
|
||||
};
|
||||
|
||||
struct spear1340_miphy_priv {
|
||||
/* phy mode: 0 for SATA 1 for PCIe */
|
||||
enum spear1340_miphy_mode mode;
|
||||
/* regmap for any soc specific misc registers */
|
||||
struct regmap *misc;
|
||||
/* phy struct pointer */
|
||||
struct phy *phy;
|
||||
};
|
||||
|
||||
static int spear1340_miphy_sata_init(struct spear1340_miphy_priv *priv)
|
||||
{
|
||||
regmap_update_bits(priv->misc, SPEAR1340_PCIE_SATA_CFG,
|
||||
SPEAR1340_PCIE_SATA_CFG_MASK,
|
||||
SPEAR1340_SATA_CFG_VAL);
|
||||
regmap_update_bits(priv->misc, SPEAR1340_PCIE_MIPHY_CFG,
|
||||
SPEAR1340_PCIE_MIPHY_CFG_MASK,
|
||||
SPEAR1340_PCIE_SATA_MIPHY_CFG_SATA_25M_CRYSTAL_CLK);
|
||||
/* Switch on sata power domain */
|
||||
regmap_update_bits(priv->misc, SPEAR1340_PCM_CFG,
|
||||
SPEAR1340_PCM_CFG_SATA_POWER_EN,
|
||||
SPEAR1340_PCM_CFG_SATA_POWER_EN);
|
||||
/* Wait for SATA power domain on */
|
||||
msleep(20);
|
||||
|
||||
/* Disable PCIE SATA Controller reset */
|
||||
regmap_update_bits(priv->misc, SPEAR1340_PERIP1_SW_RST,
|
||||
SPEAR1340_PERIP1_SW_RSATA, 0);
|
||||
/* Wait for SATA reset de-assert completion */
|
||||
msleep(20);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int spear1340_miphy_sata_exit(struct spear1340_miphy_priv *priv)
|
||||
{
|
||||
regmap_update_bits(priv->misc, SPEAR1340_PCIE_SATA_CFG,
|
||||
SPEAR1340_PCIE_SATA_CFG_MASK, 0);
|
||||
regmap_update_bits(priv->misc, SPEAR1340_PCIE_MIPHY_CFG,
|
||||
SPEAR1340_PCIE_MIPHY_CFG_MASK, 0);
|
||||
|
||||
/* Enable PCIE SATA Controller reset */
|
||||
regmap_update_bits(priv->misc, SPEAR1340_PERIP1_SW_RST,
|
||||
SPEAR1340_PERIP1_SW_RSATA,
|
||||
SPEAR1340_PERIP1_SW_RSATA);
|
||||
/* Wait for SATA power domain off */
|
||||
msleep(20);
|
||||
/* Switch off sata power domain */
|
||||
regmap_update_bits(priv->misc, SPEAR1340_PCM_CFG,
|
||||
SPEAR1340_PCM_CFG_SATA_POWER_EN, 0);
|
||||
/* Wait for SATA reset assert completion */
|
||||
msleep(20);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int spear1340_miphy_pcie_init(struct spear1340_miphy_priv *priv)
|
||||
{
|
||||
regmap_update_bits(priv->misc, SPEAR1340_PCIE_MIPHY_CFG,
|
||||
SPEAR1340_PCIE_MIPHY_CFG_MASK,
|
||||
SPEAR1340_PCIE_SATA_MIPHY_CFG_PCIE);
|
||||
regmap_update_bits(priv->misc, SPEAR1340_PCIE_SATA_CFG,
|
||||
SPEAR1340_PCIE_SATA_CFG_MASK,
|
||||
SPEAR1340_PCIE_CFG_VAL);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int spear1340_miphy_pcie_exit(struct spear1340_miphy_priv *priv)
|
||||
{
|
||||
regmap_update_bits(priv->misc, SPEAR1340_PCIE_MIPHY_CFG,
|
||||
SPEAR1340_PCIE_MIPHY_CFG_MASK, 0);
|
||||
regmap_update_bits(priv->misc, SPEAR1340_PCIE_SATA_CFG,
|
||||
SPEAR1340_PCIE_SATA_CFG_MASK, 0);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int spear1340_miphy_init(struct phy *phy)
|
||||
{
|
||||
struct spear1340_miphy_priv *priv = phy_get_drvdata(phy);
|
||||
int ret = 0;
|
||||
|
||||
if (priv->mode == SATA)
|
||||
ret = spear1340_miphy_sata_init(priv);
|
||||
else if (priv->mode == PCIE)
|
||||
ret = spear1340_miphy_pcie_init(priv);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int spear1340_miphy_exit(struct phy *phy)
|
||||
{
|
||||
struct spear1340_miphy_priv *priv = phy_get_drvdata(phy);
|
||||
int ret = 0;
|
||||
|
||||
if (priv->mode == SATA)
|
||||
ret = spear1340_miphy_sata_exit(priv);
|
||||
else if (priv->mode == PCIE)
|
||||
ret = spear1340_miphy_pcie_exit(priv);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static const struct of_device_id spear1340_miphy_of_match[] = {
|
||||
{ .compatible = "st,spear1340-miphy" },
|
||||
{ },
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, spear1340_miphy_of_match);
|
||||
|
||||
static struct phy_ops spear1340_miphy_ops = {
|
||||
.init = spear1340_miphy_init,
|
||||
.exit = spear1340_miphy_exit,
|
||||
.owner = THIS_MODULE,
|
||||
};
|
||||
|
||||
#ifdef CONFIG_PM_SLEEP
|
||||
static int spear1340_miphy_suspend(struct device *dev)
|
||||
{
|
||||
struct spear1340_miphy_priv *priv = dev_get_drvdata(dev);
|
||||
int ret = 0;
|
||||
|
||||
if (priv->mode == SATA)
|
||||
ret = spear1340_miphy_sata_exit(priv);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int spear1340_miphy_resume(struct device *dev)
|
||||
{
|
||||
struct spear1340_miphy_priv *priv = dev_get_drvdata(dev);
|
||||
int ret = 0;
|
||||
|
||||
if (priv->mode == SATA)
|
||||
ret = spear1340_miphy_sata_init(priv);
|
||||
|
||||
return ret;
|
||||
}
|
||||
#endif
|
||||
|
||||
static SIMPLE_DEV_PM_OPS(spear1340_miphy_pm_ops, spear1340_miphy_suspend,
|
||||
spear1340_miphy_resume);
|
||||
|
||||
static struct phy *spear1340_miphy_xlate(struct device *dev,
|
||||
struct of_phandle_args *args)
|
||||
{
|
||||
struct spear1340_miphy_priv *priv = dev_get_drvdata(dev);
|
||||
|
||||
if (args->args_count < 1) {
|
||||
dev_err(dev, "DT did not pass correct no of args\n");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
priv->mode = args->args[0];
|
||||
|
||||
if (priv->mode != SATA && priv->mode != PCIE) {
|
||||
dev_err(dev, "DT did not pass correct phy mode\n");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return priv->phy;
|
||||
}
|
||||
|
||||
static int spear1340_miphy_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct device *dev = &pdev->dev;
|
||||
struct spear1340_miphy_priv *priv;
|
||||
struct phy_provider *phy_provider;
|
||||
|
||||
priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
|
||||
if (!priv) {
|
||||
dev_err(dev, "can't alloc spear1340_miphy private date memory\n");
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
priv->misc =
|
||||
syscon_regmap_lookup_by_phandle(dev->of_node, "misc");
|
||||
if (IS_ERR(priv->misc)) {
|
||||
dev_err(dev, "failed to find misc regmap\n");
|
||||
return PTR_ERR(priv->misc);
|
||||
}
|
||||
|
||||
priv->phy = devm_phy_create(dev, NULL, &spear1340_miphy_ops, NULL);
|
||||
if (IS_ERR(priv->phy)) {
|
||||
dev_err(dev, "failed to create SATA PCIe PHY\n");
|
||||
return PTR_ERR(priv->phy);
|
||||
}
|
||||
|
||||
dev_set_drvdata(dev, priv);
|
||||
phy_set_drvdata(priv->phy, priv);
|
||||
|
||||
phy_provider =
|
||||
devm_of_phy_provider_register(dev, spear1340_miphy_xlate);
|
||||
if (IS_ERR(phy_provider)) {
|
||||
dev_err(dev, "failed to register phy provider\n");
|
||||
return PTR_ERR(phy_provider);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct platform_driver spear1340_miphy_driver = {
|
||||
.probe = spear1340_miphy_probe,
|
||||
.driver = {
|
||||
.name = "spear1340-miphy",
|
||||
.owner = THIS_MODULE,
|
||||
.pm = &spear1340_miphy_pm_ops,
|
||||
.of_match_table = of_match_ptr(spear1340_miphy_of_match),
|
||||
},
|
||||
};
|
||||
|
||||
static int __init spear1340_miphy_phy_init(void)
|
||||
{
|
||||
return platform_driver_register(&spear1340_miphy_driver);
|
||||
}
|
||||
module_init(spear1340_miphy_phy_init);
|
||||
|
||||
static void __exit spear1340_miphy_phy_exit(void)
|
||||
{
|
||||
platform_driver_unregister(&spear1340_miphy_driver);
|
||||
}
|
||||
module_exit(spear1340_miphy_phy_exit);
|
||||
|
||||
MODULE_DESCRIPTION("ST SPEAR1340-MIPHY driver");
|
||||
MODULE_AUTHOR("Pratyush Anand <pratyush.anand@st.com>");
|
||||
MODULE_LICENSE("GPL v2");
|
|
@ -910,7 +910,7 @@ static int tegra_xusb_padctl_probe(struct platform_device *pdev)
|
|||
goto reset;
|
||||
}
|
||||
|
||||
phy = devm_phy_create(&pdev->dev, &pcie_phy_ops, NULL);
|
||||
phy = devm_phy_create(&pdev->dev, NULL, &pcie_phy_ops, NULL);
|
||||
if (IS_ERR(phy)) {
|
||||
err = PTR_ERR(phy);
|
||||
goto unregister;
|
||||
|
@ -919,7 +919,7 @@ static int tegra_xusb_padctl_probe(struct platform_device *pdev)
|
|||
padctl->phys[TEGRA_XUSB_PADCTL_PCIE] = phy;
|
||||
phy_set_drvdata(phy, padctl);
|
||||
|
||||
phy = devm_phy_create(&pdev->dev, &sata_phy_ops, NULL);
|
||||
phy = devm_phy_create(&pdev->dev, NULL, &sata_phy_ops, NULL);
|
||||
if (IS_ERR(phy)) {
|
||||
err = PTR_ERR(phy);
|
||||
goto unregister;
|
||||
|
|
|
@ -43,7 +43,7 @@ config PWM_AB8500
|
|||
|
||||
config PWM_ATMEL
|
||||
tristate "Atmel PWM support"
|
||||
depends on ARCH_AT91
|
||||
depends on ARCH_AT91 || AVR32
|
||||
help
|
||||
Generic PWM framework driver for Atmel SoC.
|
||||
|
||||
|
|
|
@ -178,17 +178,6 @@ config BACKLIGHT_ATMEL_LCDC
|
|||
If in doubt, it's safe to enable this option; it doesn't kick
|
||||
in unless the board's description says it's wired that way.
|
||||
|
||||
config BACKLIGHT_ATMEL_PWM
|
||||
tristate "Atmel PWM backlight control"
|
||||
depends on ATMEL_PWM
|
||||
help
|
||||
Say Y here if you want to use the PWM peripheral in Atmel AT91 and
|
||||
AVR32 devices. This driver will need additional platform data to know
|
||||
which PWM instance to use and how to configure it.
|
||||
|
||||
To compile this driver as a module, choose M here: the module will be
|
||||
called atmel-pwm-bl.
|
||||
|
||||
config BACKLIGHT_EP93XX
|
||||
tristate "Cirrus EP93xx Backlight Driver"
|
||||
depends on FB_EP93XX
|
||||
|
|
|
@ -25,7 +25,6 @@ obj-$(CONFIG_BACKLIGHT_ADP8860) += adp8860_bl.o
|
|||
obj-$(CONFIG_BACKLIGHT_ADP8870) += adp8870_bl.o
|
||||
obj-$(CONFIG_BACKLIGHT_APPLE) += apple_bl.o
|
||||
obj-$(CONFIG_BACKLIGHT_AS3711) += as3711_bl.o
|
||||
obj-$(CONFIG_BACKLIGHT_ATMEL_PWM) += atmel-pwm-bl.o
|
||||
obj-$(CONFIG_BACKLIGHT_BD6107) += bd6107.o
|
||||
obj-$(CONFIG_BACKLIGHT_CARILLO_RANCH) += cr_bllcd.o
|
||||
obj-$(CONFIG_BACKLIGHT_CLASS_DEVICE) += backlight.o
|
||||
|
|
|
@ -1,223 +0,0 @@
|
|||
/*
|
||||
* Copyright (C) 2008 Atmel Corporation
|
||||
*
|
||||
* Backlight driver using Atmel PWM peripheral.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 as published by
|
||||
* the Free Software Foundation.
|
||||
*/
|
||||
#include <linux/init.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/fb.h>
|
||||
#include <linux/gpio.h>
|
||||
#include <linux/backlight.h>
|
||||
#include <linux/atmel_pwm.h>
|
||||
#include <linux/atmel-pwm-bl.h>
|
||||
#include <linux/slab.h>
|
||||
|
||||
struct atmel_pwm_bl {
|
||||
const struct atmel_pwm_bl_platform_data *pdata;
|
||||
struct backlight_device *bldev;
|
||||
struct platform_device *pdev;
|
||||
struct pwm_channel pwmc;
|
||||
int gpio_on;
|
||||
};
|
||||
|
||||
static void atmel_pwm_bl_set_gpio_on(struct atmel_pwm_bl *pwmbl, int on)
|
||||
{
|
||||
if (!gpio_is_valid(pwmbl->gpio_on))
|
||||
return;
|
||||
|
||||
gpio_set_value(pwmbl->gpio_on, on ^ pwmbl->pdata->on_active_low);
|
||||
}
|
||||
|
||||
static int atmel_pwm_bl_set_intensity(struct backlight_device *bd)
|
||||
{
|
||||
struct atmel_pwm_bl *pwmbl = bl_get_data(bd);
|
||||
int intensity = bd->props.brightness;
|
||||
int pwm_duty;
|
||||
|
||||
if (bd->props.power != FB_BLANK_UNBLANK)
|
||||
intensity = 0;
|
||||
if (bd->props.fb_blank != FB_BLANK_UNBLANK)
|
||||
intensity = 0;
|
||||
|
||||
if (pwmbl->pdata->pwm_active_low)
|
||||
pwm_duty = pwmbl->pdata->pwm_duty_min + intensity;
|
||||
else
|
||||
pwm_duty = pwmbl->pdata->pwm_duty_max - intensity;
|
||||
|
||||
if (pwm_duty > pwmbl->pdata->pwm_duty_max)
|
||||
pwm_duty = pwmbl->pdata->pwm_duty_max;
|
||||
if (pwm_duty < pwmbl->pdata->pwm_duty_min)
|
||||
pwm_duty = pwmbl->pdata->pwm_duty_min;
|
||||
|
||||
if (!intensity) {
|
||||
atmel_pwm_bl_set_gpio_on(pwmbl, 0);
|
||||
pwm_channel_writel(&pwmbl->pwmc, PWM_CUPD, pwm_duty);
|
||||
pwm_channel_disable(&pwmbl->pwmc);
|
||||
} else {
|
||||
pwm_channel_enable(&pwmbl->pwmc);
|
||||
pwm_channel_writel(&pwmbl->pwmc, PWM_CUPD, pwm_duty);
|
||||
atmel_pwm_bl_set_gpio_on(pwmbl, 1);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int atmel_pwm_bl_get_intensity(struct backlight_device *bd)
|
||||
{
|
||||
struct atmel_pwm_bl *pwmbl = bl_get_data(bd);
|
||||
u32 cdty;
|
||||
u32 intensity;
|
||||
|
||||
cdty = pwm_channel_readl(&pwmbl->pwmc, PWM_CDTY);
|
||||
if (pwmbl->pdata->pwm_active_low)
|
||||
intensity = cdty - pwmbl->pdata->pwm_duty_min;
|
||||
else
|
||||
intensity = pwmbl->pdata->pwm_duty_max - cdty;
|
||||
|
||||
return intensity & 0xffff;
|
||||
}
|
||||
|
||||
static int atmel_pwm_bl_init_pwm(struct atmel_pwm_bl *pwmbl)
|
||||
{
|
||||
unsigned long pwm_rate = pwmbl->pwmc.mck;
|
||||
unsigned long prescale = DIV_ROUND_UP(pwm_rate,
|
||||
(pwmbl->pdata->pwm_frequency *
|
||||
pwmbl->pdata->pwm_compare_max)) - 1;
|
||||
|
||||
/*
|
||||
* Prescale must be power of two and maximum 0xf in size because of
|
||||
* hardware limit. PWM speed will be:
|
||||
* PWM module clock speed / (2 ^ prescale).
|
||||
*/
|
||||
prescale = fls(prescale);
|
||||
if (prescale > 0xf)
|
||||
prescale = 0xf;
|
||||
|
||||
pwm_channel_writel(&pwmbl->pwmc, PWM_CMR, prescale);
|
||||
pwm_channel_writel(&pwmbl->pwmc, PWM_CDTY,
|
||||
pwmbl->pdata->pwm_duty_min +
|
||||
pwmbl->bldev->props.brightness);
|
||||
pwm_channel_writel(&pwmbl->pwmc, PWM_CPRD,
|
||||
pwmbl->pdata->pwm_compare_max);
|
||||
|
||||
dev_info(&pwmbl->pdev->dev, "Atmel PWM backlight driver (%lu Hz)\n",
|
||||
pwmbl->pwmc.mck / pwmbl->pdata->pwm_compare_max /
|
||||
(1 << prescale));
|
||||
|
||||
return pwm_channel_enable(&pwmbl->pwmc);
|
||||
}
|
||||
|
||||
static const struct backlight_ops atmel_pwm_bl_ops = {
|
||||
.get_brightness = atmel_pwm_bl_get_intensity,
|
||||
.update_status = atmel_pwm_bl_set_intensity,
|
||||
};
|
||||
|
||||
static int atmel_pwm_bl_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct backlight_properties props;
|
||||
const struct atmel_pwm_bl_platform_data *pdata;
|
||||
struct backlight_device *bldev;
|
||||
struct atmel_pwm_bl *pwmbl;
|
||||
unsigned long flags;
|
||||
int retval;
|
||||
|
||||
pdata = dev_get_platdata(&pdev->dev);
|
||||
if (!pdata)
|
||||
return -ENODEV;
|
||||
|
||||
if (pdata->pwm_compare_max < pdata->pwm_duty_max ||
|
||||
pdata->pwm_duty_min > pdata->pwm_duty_max ||
|
||||
pdata->pwm_frequency == 0)
|
||||
return -EINVAL;
|
||||
|
||||
pwmbl = devm_kzalloc(&pdev->dev, sizeof(struct atmel_pwm_bl),
|
||||
GFP_KERNEL);
|
||||
if (!pwmbl)
|
||||
return -ENOMEM;
|
||||
|
||||
pwmbl->pdev = pdev;
|
||||
pwmbl->pdata = pdata;
|
||||
pwmbl->gpio_on = pdata->gpio_on;
|
||||
|
||||
retval = pwm_channel_alloc(pdata->pwm_channel, &pwmbl->pwmc);
|
||||
if (retval)
|
||||
return retval;
|
||||
|
||||
if (gpio_is_valid(pwmbl->gpio_on)) {
|
||||
/* Turn display off by default. */
|
||||
if (pdata->on_active_low)
|
||||
flags = GPIOF_OUT_INIT_HIGH;
|
||||
else
|
||||
flags = GPIOF_OUT_INIT_LOW;
|
||||
|
||||
retval = devm_gpio_request_one(&pdev->dev, pwmbl->gpio_on,
|
||||
flags, "gpio_atmel_pwm_bl");
|
||||
if (retval)
|
||||
goto err_free_pwm;
|
||||
}
|
||||
|
||||
memset(&props, 0, sizeof(struct backlight_properties));
|
||||
props.type = BACKLIGHT_RAW;
|
||||
props.max_brightness = pdata->pwm_duty_max - pdata->pwm_duty_min;
|
||||
bldev = devm_backlight_device_register(&pdev->dev, "atmel-pwm-bl",
|
||||
&pdev->dev, pwmbl, &atmel_pwm_bl_ops,
|
||||
&props);
|
||||
if (IS_ERR(bldev)) {
|
||||
retval = PTR_ERR(bldev);
|
||||
goto err_free_pwm;
|
||||
}
|
||||
|
||||
pwmbl->bldev = bldev;
|
||||
|
||||
platform_set_drvdata(pdev, pwmbl);
|
||||
|
||||
/* Power up the backlight by default at middle intesity. */
|
||||
bldev->props.power = FB_BLANK_UNBLANK;
|
||||
bldev->props.brightness = bldev->props.max_brightness / 2;
|
||||
|
||||
retval = atmel_pwm_bl_init_pwm(pwmbl);
|
||||
if (retval)
|
||||
goto err_free_pwm;
|
||||
|
||||
atmel_pwm_bl_set_intensity(bldev);
|
||||
|
||||
return 0;
|
||||
|
||||
err_free_pwm:
|
||||
pwm_channel_free(&pwmbl->pwmc);
|
||||
|
||||
return retval;
|
||||
}
|
||||
|
||||
static int atmel_pwm_bl_remove(struct platform_device *pdev)
|
||||
{
|
||||
struct atmel_pwm_bl *pwmbl = platform_get_drvdata(pdev);
|
||||
|
||||
atmel_pwm_bl_set_gpio_on(pwmbl, 0);
|
||||
pwm_channel_disable(&pwmbl->pwmc);
|
||||
pwm_channel_free(&pwmbl->pwmc);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct platform_driver atmel_pwm_bl_driver = {
|
||||
.driver = {
|
||||
.name = "atmel-pwm-bl",
|
||||
},
|
||||
/* REVISIT add suspend() and resume() */
|
||||
.probe = atmel_pwm_bl_probe,
|
||||
.remove = atmel_pwm_bl_remove,
|
||||
};
|
||||
|
||||
module_platform_driver(atmel_pwm_bl_driver);
|
||||
|
||||
MODULE_AUTHOR("Hans-Christian egtvedt <hans-christian.egtvedt@atmel.com>");
|
||||
MODULE_DESCRIPTION("Atmel PWM backlight driver");
|
||||
MODULE_LICENSE("GPL");
|
||||
MODULE_ALIAS("platform:atmel-pwm-bl");
|
|
@ -1,43 +0,0 @@
|
|||
/*
|
||||
* Copyright (C) 2007 Atmel Corporation
|
||||
*
|
||||
* Driver for the AT32AP700X PS/2 controller (PSIF).
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 as published
|
||||
* by the Free Software Foundation.
|
||||
*/
|
||||
|
||||
#ifndef __INCLUDE_ATMEL_PWM_BL_H
|
||||
#define __INCLUDE_ATMEL_PWM_BL_H
|
||||
|
||||
/**
|
||||
* struct atmel_pwm_bl_platform_data
|
||||
* @pwm_channel: which PWM channel in the PWM module to use.
|
||||
* @pwm_frequency: PWM frequency to generate, the driver will try to be as
|
||||
* close as the prescaler allows.
|
||||
* @pwm_compare_max: value to use in the PWM channel compare register.
|
||||
* @pwm_duty_max: maximum duty cycle value, must be less than or equal to
|
||||
* pwm_compare_max.
|
||||
* @pwm_duty_min: minimum duty cycle value, must be less than pwm_duty_max.
|
||||
* @pwm_active_low: set to one if the low part of the PWM signal increases the
|
||||
* brightness of the backlight.
|
||||
* @gpio_on: GPIO line to control the backlight on/off, set to -1 if not used.
|
||||
* @on_active_low: set to one if the on/off signal is on when GPIO is low.
|
||||
*
|
||||
* This struct must be added to the platform device in the board code. It is
|
||||
* used by the atmel-pwm-bl driver to setup the GPIO to control on/off and the
|
||||
* PWM device.
|
||||
*/
|
||||
struct atmel_pwm_bl_platform_data {
|
||||
unsigned int pwm_channel;
|
||||
unsigned int pwm_frequency;
|
||||
unsigned int pwm_compare_max;
|
||||
unsigned int pwm_duty_max;
|
||||
unsigned int pwm_duty_min;
|
||||
unsigned int pwm_active_low;
|
||||
int gpio_on;
|
||||
unsigned int on_active_low;
|
||||
};
|
||||
|
||||
#endif /* __INCLUDE_ATMEL_PWM_BL_H */
|
|
@ -1,70 +0,0 @@
|
|||
#ifndef __LINUX_ATMEL_PWM_H
|
||||
#define __LINUX_ATMEL_PWM_H
|
||||
|
||||
/**
|
||||
* struct pwm_channel - driver handle to a PWM channel
|
||||
* @regs: base of this channel's registers
|
||||
* @index: number of this channel (0..31)
|
||||
* @mck: base clock rate, which can be prescaled and maybe subdivided
|
||||
*
|
||||
* Drivers initialize a pwm_channel structure using pwm_channel_alloc().
|
||||
* Then they configure its clock rate (derived from MCK), alignment,
|
||||
* polarity, and duty cycle by writing directly to the channel registers,
|
||||
* before enabling the channel by calling pwm_channel_enable().
|
||||
*
|
||||
* After emitting a PWM signal for the desired length of time, drivers
|
||||
* may then pwm_channel_disable() or pwm_channel_free(). Both of these
|
||||
* disable the channel, but when it's freed the IRQ is deconfigured and
|
||||
* the channel must later be re-allocated and reconfigured.
|
||||
*
|
||||
* Note that if the period or duty cycle need to be changed while the
|
||||
* PWM channel is operating, drivers must use the PWM_CUPD double buffer
|
||||
* mechanism, either polling until they change or getting implicitly
|
||||
* notified through a once-per-period interrupt handler.
|
||||
*/
|
||||
struct pwm_channel {
|
||||
void __iomem *regs;
|
||||
unsigned index;
|
||||
unsigned long mck;
|
||||
};
|
||||
|
||||
extern int pwm_channel_alloc(int index, struct pwm_channel *ch);
|
||||
extern int pwm_channel_free(struct pwm_channel *ch);
|
||||
|
||||
extern int pwm_clk_alloc(unsigned prescale, unsigned div);
|
||||
extern void pwm_clk_free(unsigned clk);
|
||||
|
||||
extern int __pwm_channel_onoff(struct pwm_channel *ch, int enabled);
|
||||
|
||||
#define pwm_channel_enable(ch) __pwm_channel_onoff((ch), 1)
|
||||
#define pwm_channel_disable(ch) __pwm_channel_onoff((ch), 0)
|
||||
|
||||
/* periodic interrupts, mostly for CUPD changes to period or cycle */
|
||||
extern int pwm_channel_handler(struct pwm_channel *ch,
|
||||
void (*handler)(struct pwm_channel *ch));
|
||||
|
||||
/* per-channel registers (banked at pwm_channel->regs) */
|
||||
#define PWM_CMR 0x00 /* mode register */
|
||||
#define PWM_CPR_CPD (1 << 10) /* set: CUPD modifies period */
|
||||
#define PWM_CPR_CPOL (1 << 9) /* set: idle high */
|
||||
#define PWM_CPR_CALG (1 << 8) /* set: center align */
|
||||
#define PWM_CPR_CPRE (0xf << 0) /* mask: rate is mck/(2^pre) */
|
||||
#define PWM_CPR_CLKA (0xb << 0) /* rate CLKA */
|
||||
#define PWM_CPR_CLKB (0xc << 0) /* rate CLKB */
|
||||
#define PWM_CDTY 0x04 /* duty cycle (max of CPRD) */
|
||||
#define PWM_CPRD 0x08 /* period (count up from zero) */
|
||||
#define PWM_CCNT 0x0c /* counter (20 bits?) */
|
||||
#define PWM_CUPD 0x10 /* update CPRD (or CDTY) next period */
|
||||
|
||||
static inline void
|
||||
pwm_channel_writel(struct pwm_channel *pwmc, unsigned offset, u32 val)
|
||||
{
|
||||
__raw_writel(val, pwmc->regs + offset);
|
||||
}
|
||||
|
||||
static inline u32 pwm_channel_readl(struct pwm_channel *pwmc, unsigned offset)
|
||||
{
|
||||
return __raw_readl(pwmc->regs + offset);
|
||||
}
|
||||
|
||||
#endif /* __LINUX_ATMEL_PWM_H */
|
Loading…
Reference in New Issue