* [PATCH 0/5 RFC] R-Car Gen2 PCIe host driver
@ 2014-02-26 16:08 ` Phil Edworthy
0 siblings, 0 replies; 16+ messages in thread
From: Phil Edworthy @ 2014-02-26 16:08 UTC (permalink / raw)
To: linux-pci
Cc: linux-sh, Bjorn Helgaas, Valentine Barshak, Simon Horman,
Magnus Damm, Phil Edworthy
This is an initial version of a PCIe Host driver for the R-Car Gen2 devices,
i.e. R-Car H2 (r8a7790) and R-Car M2 (r8a7791).
This is RFC as there is some work required for DT support.
All feedback welcome!
Phil
root@koelsch:~# lspci -vv
00:00.0 PCI bridge: Renesas Technology Corp. Device 001f (prog-if 00 [Normal decode])
Control: I/O+ Mem+ BusMaster+ SpecCycle- MemWINV- VGASnoop- ParErr+ Stepping- SERR+ FastB2B- DisINTx-
Status: Cap+ 66MHz- UDF- FastB2B- ParErr- DEVSEL=fast >TAbort- <TAbort- <MAbort- >SERR- <PERR- INTx-
Latency: 0, Cache Line Size: 64 bytes
Bus: primary=00, secondary=01, subordinate=01, sec-latency=0
I/O behind bridge: 00000000-00000fff
Memory behind bridge: 00000000-000fffff
Prefetchable memory behind bridge: 00000000-000fffff
Secondary status: 66MHz- FastB2B- ParErr- DEVSEL=fast >TAbort- <TAbort- <MAbort- <SERR- <PERR-
BridgeCtl: Parity+ SERR- NoISA- VGA- MAbort- >Reset- FastB2B-
PriDiscTmr- SecDiscTmr- DiscTmrStat- DiscTmrSERREn-
Capabilities: [40] Power Management version 3
Flags: PMEClk- DSI- D1- D2- AuxCurrent=0mA PME(D0+,D1-,D2-,D3hot+,D3cold+)
Status: D0 NoSoftRst- PME-Enable- DSel=0 DScale=0 PME-
Capabilities: [50] MSI: Enable- Count=1/1 Maskable+ 64bit+
Address: 0000000000000000 Data: 0000
Masking: fffffffe Pending: 00000000
Capabilities: [70] Express (v2) Root Port (Slot-), MSI 00
DevCap: MaxPayload 128 bytes, PhantFunc 0, Latency L0s <64ns, L1 <1us
ExtTag+ RBE+ FLReset-
DevCtl: Report errors: Correctable- Non-Fatal- Fatal- Unsupported-
RlxdOrd+ ExtTag- PhantFunc- AuxPwr- NoSnoop+
MaxPayload 128 bytes, MaxReadReq 512 bytes
DevSta: CorrErr- UncorrErr- FatalErr- UnsuppReq- AuxPwr- TransPend-
LnkCap: Port #0, Speed 5GT/s, Width x1, ASPM L0s, Latency L0 unlimited, L1 unlimited
ClockPM- Surprise- LLActRep+ BwNot-
LnkCtl: ASPM Disabled; RCB 64 bytes Disabled- Retrain- CommClk-
ExtSynch- ClockPM- AutWidDis- BWInt- AutBWInt-
LnkSta: Speed 5GT/s, Width x1, TrErr- Train- SlotClk- DLActive+ BWMgmt- ABWMgmt-
RootCtl: ErrCorrectable- ErrNon-Fatal- ErrFatal- PMEIntEna- CRSVisible-
RootCap: CRSVisible-
RootSta: PME ReqID 0000, PMEStatus- PMEPending-
DevCap2: Completion Timeout: Not Supported, TimeoutDis+, LTR-, OBFF Not Supported ARIFwd-
DevCtl2: Completion Timeout: 50us to 50ms, TimeoutDis-, LTR-, OBFF Disabled ARIFwd-
LnkCtl2: Target Link Speed: 5GT/s, EnterCompliance- SpeedDis-
Transmit Margin: Normal Operating Range, EnterModifiedCompliance- ComplianceSOS-
Compliance De-emphasis: -6dB
LnkSta2: Current De-emphasis Level: -6dB, EqualizationComplete-, EqualizationPhase1-
EqualizationPhase2-, EqualizationPhase3-, LinkEqualizationRequest-
Capabilities: [100 v1] Virtual Channel
Caps: LPEVC=0 RefClk=100ns PATEntryBits=1
Arb: Fixed- WRR32- WRR64- WRR128-
Ctrl: ArbSelect=Fixed
Status: InProgress-
VC0: Caps: PATOffset=00 MaxTimeSlots=1 RejSnoopTrans-
Arb: Fixed- WRR32- WRR64- WRR128- TWRR128- WRR256-
Ctrl: Enable+ ID=0 ArbSelect=Fixed TC/VC=ff
Status: NegoPending- InProgress-
Capabilities: [1b0 v1] Device Serial Number 00-00-00-00-00-00-00-00
Kernel driver in use: pcieport
01:00.0 Ethernet controller: Intel Corporation I350 Gigabit Network Connection (rev 01)
Subsystem: Intel Corporation Ethernet Server Adapter I350-T2
Control: I/O- Mem+ BusMaster+ SpecCycle- MemWINV- VGASnoop- ParErr+ Stepping- SERR+ FastB2B- DisINTx-
Status: Cap+ 66MHz- UDF- FastB2B- ParErr- DEVSEL=fast >TAbort- <TAbort- <MAbort- >SERR- <PERR- INTx-
Latency: 0, Cache Line Size: 64 bytes
Interrupt: pin A routed to IRQ 148
Region 0: Memory at 30000000 (32-bit, non-prefetchable) [size=1M]
Region 3: Memory at 30200000 (32-bit, non-prefetchable) [size=16K]
Capabilities: [40] Power Management version 3
Flags: PMEClk- DSI+ D1- D2- AuxCurrent=0mA PME(D0+,D1-,D2-,D3hot+,D3cold+)
Status: D0 NoSoftRst+ PME-Enable- DSel=0 DScale=1 PME-
Capabilities: [50] MSI: Enable- Count=1/1 Maskable+ 64bit+
Address: 0000000000000000 Data: 0000
Masking: 00000000 Pending: 00000000
Capabilities: [70] MSI-X: Enable- Count=10 Masked-
Vector table: BAR=3 offset=00000000
PBA: BAR=3 offset=00002000
Capabilities: [a0] Express (v2) Endpoint, MSI 00
DevCap: MaxPayload 512 bytes, PhantFunc 0, Latency L0s <512ns, L1 <64us
ExtTag- AttnBtn- AttnInd- PwrInd- RBE+ FLReset+
DevCtl: Report errors: Correctable+ Non-Fatal+ Fatal+ Unsupported+
RlxdOrd+ ExtTag- PhantFunc- AuxPwr- NoSnoop+ FLReset-
MaxPayload 128 bytes, MaxReadReq 512 bytes
DevSta: CorrErr- UncorrErr- FatalErr- UnsuppReq- AuxPwr+ TransPend-
LnkCap: Port #0, Speed 5GT/s, Width x4, ASPM L0s L1, Latency L0 <4us, L1 <32us
ClockPM- Surprise- LLActRep- BwNot-
LnkCtl: ASPM Disabled; RCB 64 bytes Disabled- Retrain- CommClk-
ExtSynch- ClockPM- AutWidDis- BWInt- AutBWInt-
LnkSta: Speed 5GT/s, Width x1, TrErr- Train- SlotClk+ DLActive- BWMgmt- ABWMgmt-
DevCap2: Completion Timeout: Range ABCD, TimeoutDis+, LTR+, OBFF Not Supported
DevCtl2: Completion Timeout: 50us to 50ms, TimeoutDis-, LTR-, OBFF Disabled
LnkCtl2: Target Link Speed: 5GT/s, EnterCompliance- SpeedDis-
Transmit Margin: Normal Operating Range, EnterModifiedCompliance- ComplianceSOS-
Compliance De-emphasis: -6dB
LnkSta2: Current De-emphasis Level: -6dB, EqualizationComplete-, EqualizationPhase1-
EqualizationPhase2-, EqualizationPhase3-, LinkEqualizationRequest-
Capabilities: [100 v2] Advanced Error Reporting
UESta: DLP- SDES- TLP- FCP- CmpltTO- CmpltAbrt- UnxCmplt- RxOF- MalfTLP- ECRC- UnsupReq- ACSViol-
UEMsk: DLP- SDES- TLP- FCP- CmpltTO- CmpltAbrt- UnxCmplt- RxOF- MalfTLP- ECRC- UnsupReq- ACSViol-
UESvrt: DLP+ SDES+ TLP- FCP+ CmpltTO- CmpltAbrt- UnxCmplt- RxOF+ MalfTLP+ ECRC- UnsupReq- ACSViol-
CESta: RxErr- BadTLP- BadDLLP- Rollover- Timeout- NonFatalErr+
CEMsk: RxErr- BadTLP- BadDLLP- Rollover- Timeout- NonFatalErr+
AERCap: First Error Pointer: 00, GenCap+ CGenEn- ChkCap+ ChkEn-
Capabilities: [140 v1] Device Serial Number a0-36-9f-ff-ff-23-18-c0
Capabilities: [150 v1] Alternative Routing-ID Interpretation (ARI)
ARICap: MFVC- ACS-, Next Function: 1
ARICtl: MFVC- ACS-, Function Group: 0
Capabilities: [160 v1] Single Root I/O Virtualization (SR-IOV)
IOVCap: Migration-, Interrupt Message Number: 000
IOVCtl: Enable- Migration- Interrupt- MSE- ARIHierarchy-
IOVSta: Migration-
Initial VFs: 8, Total VFs: 8, Number of VFs: 0, Function Dependency Link: 00
VF offset: 384, stride: 4, Device ID: 1520
Supported Page Size: 00000553, System Page Size: 00000001
Region 0: Memory at 0000000000000000 (64-bit, prefetchable)
Region 3: Memory at 0000000000000000 (64-bit, prefetchable)
VF Migration: offset: 00000000, BIR: 0
Capabilities: [1a0 v1] Transaction Processing Hints
Device specific mode supported
Steering table in TPH capability structure
Capabilities: [1c0 v1] Latency Tolerance Reporting
Max snoop latency: 0ns
Max no snoop latency: 0ns
Capabilities: [1d0 v1] Access Control Services
ACSCap: SrcValid- TransBlk- ReqRedir- CmpltRedir- UpstreamFwd- EgressCtrl- DirectTrans-
ACSCtl: SrcValid- TransBlk- ReqRedir- CmpltRedir- UpstreamFwd- EgressCtrl- DirectTrans-
Kernel driver in use: igb
Kernel modules: igb
01:00.1 Ethernet controller: Intel Corporation I350 Gigabit Network Connection (rev 01)
Subsystem: Intel Corporation Ethernet Server Adapter I350-T2
Control: I/O- Mem+ BusMaster+ SpecCycle- MemWINV- VGASnoop- ParErr+ Stepping- SERR+ FastB2B- DisINTx-
Status: Cap+ 66MHz- UDF- FastB2B- ParErr- DEVSEL=fast >TAbort- <TAbort- <MAbort- >SERR- <PERR- INTx-
Latency: 0, Cache Line Size: 64 bytes
Interrupt: pin B routed to IRQ 148
Region 0: Memory at 30100000 (32-bit, non-prefetchable) [size=1M]
Region 3: Memory at 30204000 (32-bit, non-prefetchable) [size=16K]
Capabilities: [40] Power Management version 3
Flags: PMEClk- DSI+ D1- D2- AuxCurrent=0mA PME(D0+,D1-,D2-,D3hot+,D3cold+)
Status: D0 NoSoftRst+ PME-Enable- DSel=0 DScale=1 PME-
Capabilities: [50] MSI: Enable- Count=1/1 Maskable+ 64bit+
Address: 0000000000000000 Data: 0000
Masking: 00000000 Pending: 00000000
Capabilities: [70] MSI-X: Enable- Count=10 Masked-
Vector table: BAR=3 offset=00000000
PBA: BAR=3 offset=00002000
Capabilities: [a0] Express (v2) Endpoint, MSI 00
DevCap: MaxPayload 512 bytes, PhantFunc 0, Latency L0s <512ns, L1 <64us
ExtTag- AttnBtn- AttnInd- PwrInd- RBE+ FLReset+
DevCtl: Report errors: Correctable+ Non-Fatal+ Fatal+ Unsupported+
RlxdOrd+ ExtTag- PhantFunc- AuxPwr- NoSnoop+ FLReset-
MaxPayload 128 bytes, MaxReadReq 512 bytes
DevSta: CorrErr- UncorrErr- FatalErr- UnsuppReq- AuxPwr+ TransPend-
LnkCap: Port #0, Speed 5GT/s, Width x4, ASPM L0s L1, Latency L0 <4us, L1 <32us
ClockPM- Surprise- LLActRep- BwNot-
LnkCtl: ASPM Disabled; RCB 64 bytes Disabled- Retrain- CommClk-
ExtSynch- ClockPM- AutWidDis- BWInt- AutBWInt-
LnkSta: Speed 5GT/s, Width x1, TrErr- Train- SlotClk+ DLActive- BWMgmt- ABWMgmt-
DevCap2: Completion Timeout: Range ABCD, TimeoutDis+, LTR+, OBFF Not Supported
DevCtl2: Completion Timeout: 50us to 50ms, TimeoutDis-, LTR-, OBFF Disabled
LnkSta2: Current De-emphasis Level: -6dB, EqualizationComplete-, EqualizationPhase1-
EqualizationPhase2-, EqualizationPhase3-, LinkEqualizationRequest-
Capabilities: [100 v2] Advanced Error Reporting
UESta: DLP- SDES- TLP- FCP- CmpltTO- CmpltAbrt- UnxCmplt- RxOF- MalfTLP- ECRC- UnsupReq- ACSViol-
UEMsk: DLP- SDES- TLP- FCP- CmpltTO- CmpltAbrt- UnxCmplt- RxOF- MalfTLP- ECRC- UnsupReq- ACSViol-
UESvrt: DLP+ SDES+ TLP- FCP+ CmpltTO- CmpltAbrt- UnxCmplt- RxOF+ MalfTLP+ ECRC- UnsupReq- ACSViol-
CESta: RxErr- BadTLP- BadDLLP- Rollover- Timeout- NonFatalErr+
CEMsk: RxErr- BadTLP- BadDLLP- Rollover- Timeout- NonFatalErr+
AERCap: First Error Pointer: 00, GenCap+ CGenEn- ChkCap+ ChkEn-
Capabilities: [140 v1] Device Serial Number a0-36-9f-ff-ff-23-18-c0
Capabilities: [150 v1] Alternative Routing-ID Interpretation (ARI)
ARICap: MFVC- ACS-, Next Function: 0
ARICtl: MFVC- ACS-, Function Group: 0
Capabilities: [160 v1] Single Root I/O Virtualization (SR-IOV)
IOVCap: Migration-, Interrupt Message Number: 000
IOVCtl: Enable- Migration- Interrupt- MSE- ARIHierarchy-
IOVSta: Migration-
Initial VFs: 8, Total VFs: 8, Number of VFs: 0, Function Dependency Link: 01
VF offset: 384, stride: 4, Device ID: 1520
Supported Page Size: 00000553, System Page Size: 00000001
Region 0: Memory at 0000000000000000 (64-bit, prefetchable)
Region 3: Memory at 0000000000000000 (64-bit, prefetchable)
VF Migration: offset: 00000000, BIR: 0
Capabilities: [1a0 v1] Transaction Processing Hints
Device specific mode supported
Steering table in TPH capability structure
Capabilities: [1d0 v1] Access Control Services
ACSCap: SrcValid- TransBlk- ReqRedir- CmpltRedir- UpstreamFwd- EgressCtrl- DirectTrans-
ACSCtl: SrcValid- TransBlk- ReqRedir- CmpltRedir- UpstreamFwd- EgressCtrl- DirectTrans-
Kernel driver in use: igb
Kernel modules: igb
Phil Edworthy (5):
PCI: host: Add Renesas R-Car PCIe driver
ARM: shmobile: r8a7791: Add PCIe clock
ARM: shmobile: Add PCIe resources to r8a7791 device
ARM: shmobile: Add PCIe to r8a7791 device Kconfig
ARM: koelsch: Add PCIe to defconfig
arch/arm/configs/koelsch_defconfig | 3 +
arch/arm/mach-shmobile/Kconfig | 2 +
arch/arm/mach-shmobile/clock-r8a7791.c | 3 +
arch/arm/mach-shmobile/setup-r8a7791.c | 13 +
drivers/pci/host/Kconfig | 6 +
drivers/pci/host/Makefile | 1 +
drivers/pci/host/pcie-rcar.c | 614 ++++++++++++++++++++++++++++++++
drivers/pci/host/pcie-rcar.h | 84 +++++
10 files changed, 744 insertions(+), 1 deletion(-)
create mode 100644 drivers/pci/host/pcie-rcar.c
create mode 100644 drivers/pci/host/pcie-rcar.h
--
1.7.9.5
^ permalink raw reply [flat|nested] 16+ messages in thread* [PATCH 0/5 RFC] R-Car Gen2 PCIe host driver @ 2014-02-26 16:08 ` Phil Edworthy 0 siblings, 0 replies; 16+ messages in thread From: Phil Edworthy @ 2014-02-26 16:08 UTC (permalink / raw) To: linux-pci Cc: linux-sh, Bjorn Helgaas, Valentine Barshak, Simon Horman, Magnus Damm, Phil Edworthy This is an initial version of a PCIe Host driver for the R-Car Gen2 devices, i.e. R-Car H2 (r8a7790) and R-Car M2 (r8a7791). This is RFC as there is some work required for DT support. All feedback welcome! Phil root@koelsch:~# lspci -vv 00:00.0 PCI bridge: Renesas Technology Corp. Device 001f (prog-if 00 [Normal decode]) Control: I/O+ Mem+ BusMaster+ SpecCycle- MemWINV- VGASnoop- ParErr+ Stepping- SERR+ FastB2B- DisINTx- Status: Cap+ 66MHz- UDF- FastB2B- ParErr- DEVSELúst >TAbort- <TAbort- <MAbort- >SERR- <PERR- INTx- Latency: 0, Cache Line Size: 64 bytes Bus: primary\0, secondary\x01, subordinate\x01, sec-latency=0 I/O behind bridge: 00000000-00000fff Memory behind bridge: 00000000-000fffff Prefetchable memory behind bridge: 00000000-000fffff Secondary status: 66MHz- FastB2B- ParErr- DEVSELúst >TAbort- <TAbort- <MAbort- <SERR- <PERR- BridgeCtl: Parity+ SERR- NoISA- VGA- MAbort- >Reset- FastB2B- PriDiscTmr- SecDiscTmr- DiscTmrStat- DiscTmrSERREn- Capabilities: [40] Power Management version 3 Flags: PMEClk- DSI- D1- D2- AuxCurrent=0mA PME(D0+,D1-,D2-,D3hot+,D3cold+) Status: D0 NoSoftRst- PME-Enable- DSel=0 DScale=0 PME- Capabilities: [50] MSI: Enable- Count=1/1 Maskable+ 64bit+ Address: 0000000000000000 Data: 0000 Masking: fffffffe Pending: 00000000 Capabilities: [70] Express (v2) Root Port (Slot-), MSI 00 DevCap: MaxPayload 128 bytes, PhantFunc 0, Latency L0s <64ns, L1 <1us ExtTag+ RBE+ FLReset- DevCtl: Report errors: Correctable- Non-Fatal- Fatal- Unsupported- RlxdOrd+ ExtTag- PhantFunc- AuxPwr- NoSnoop+ MaxPayload 128 bytes, MaxReadReq 512 bytes DevSta: CorrErr- UncorrErr- FatalErr- UnsuppReq- AuxPwr- TransPend- LnkCap: Port #0, Speed 5GT/s, Width x1, ASPM L0s, Latency L0 unlimited, L1 unlimited ClockPM- Surprise- LLActRep+ BwNot- LnkCtl: ASPM Disabled; RCB 64 bytes Disabled- Retrain- CommClk- ExtSynch- ClockPM- AutWidDis- BWInt- AutBWInt- LnkSta: Speed 5GT/s, Width x1, TrErr- Train- SlotClk- DLActive+ BWMgmt- ABWMgmt- RootCtl: ErrCorrectable- ErrNon-Fatal- ErrFatal- PMEIntEna- CRSVisible- RootCap: CRSVisible- RootSta: PME ReqID 0000, PMEStatus- PMEPending- DevCap2: Completion Timeout: Not Supported, TimeoutDis+, LTR-, OBFF Not Supported ARIFwd- DevCtl2: Completion Timeout: 50us to 50ms, TimeoutDis-, LTR-, OBFF Disabled ARIFwd- LnkCtl2: Target Link Speed: 5GT/s, EnterCompliance- SpeedDis- Transmit Margin: Normal Operating Range, EnterModifiedCompliance- ComplianceSOS- Compliance De-emphasis: -6dB LnkSta2: Current De-emphasis Level: -6dB, EqualizationComplete-, EqualizationPhase1- EqualizationPhase2-, EqualizationPhase3-, LinkEqualizationRequest- Capabilities: [100 v1] Virtual Channel Caps: LPEVC=0 RefClk\x100ns PATEntryBits=1 Arb: Fixed- WRR32- WRR64- WRR128- Ctrl: ArbSelect=Fixed Status: InProgress- VC0: Caps: PATOffset\0 MaxTimeSlots=1 RejSnoopTrans- Arb: Fixed- WRR32- WRR64- WRR128- TWRR128- WRR256- Ctrl: Enable+ ID=0 ArbSelect=Fixed TC/VCÿ Status: NegoPending- InProgress- Capabilities: [1b0 v1] Device Serial Number 00-00-00-00-00-00-00-00 Kernel driver in use: pcieport 01:00.0 Ethernet controller: Intel Corporation I350 Gigabit Network Connection (rev 01) Subsystem: Intel Corporation Ethernet Server Adapter I350-T2 Control: I/O- Mem+ BusMaster+ SpecCycle- MemWINV- VGASnoop- ParErr+ Stepping- SERR+ FastB2B- DisINTx- Status: Cap+ 66MHz- UDF- FastB2B- ParErr- DEVSELúst >TAbort- <TAbort- <MAbort- >SERR- <PERR- INTx- Latency: 0, Cache Line Size: 64 bytes Interrupt: pin A routed to IRQ 148 Region 0: Memory at 30000000 (32-bit, non-prefetchable) [size=1M] Region 3: Memory at 30200000 (32-bit, non-prefetchable) [size\x16K] Capabilities: [40] Power Management version 3 Flags: PMEClk- DSI+ D1- D2- AuxCurrent=0mA PME(D0+,D1-,D2-,D3hot+,D3cold+) Status: D0 NoSoftRst+ PME-Enable- DSel=0 DScale=1 PME- Capabilities: [50] MSI: Enable- Count=1/1 Maskable+ 64bit+ Address: 0000000000000000 Data: 0000 Masking: 00000000 Pending: 00000000 Capabilities: [70] MSI-X: Enable- Count\x10 Masked- Vector table: BAR=3 offset\0000000 PBA: BAR=3 offset\0002000 Capabilities: [a0] Express (v2) Endpoint, MSI 00 DevCap: MaxPayload 512 bytes, PhantFunc 0, Latency L0s <512ns, L1 <64us ExtTag- AttnBtn- AttnInd- PwrInd- RBE+ FLReset+ DevCtl: Report errors: Correctable+ Non-Fatal+ Fatal+ Unsupported+ RlxdOrd+ ExtTag- PhantFunc- AuxPwr- NoSnoop+ FLReset- MaxPayload 128 bytes, MaxReadReq 512 bytes DevSta: CorrErr- UncorrErr- FatalErr- UnsuppReq- AuxPwr+ TransPend- LnkCap: Port #0, Speed 5GT/s, Width x4, ASPM L0s L1, Latency L0 <4us, L1 <32us ClockPM- Surprise- LLActRep- BwNot- LnkCtl: ASPM Disabled; RCB 64 bytes Disabled- Retrain- CommClk- ExtSynch- ClockPM- AutWidDis- BWInt- AutBWInt- LnkSta: Speed 5GT/s, Width x1, TrErr- Train- SlotClk+ DLActive- BWMgmt- ABWMgmt- DevCap2: Completion Timeout: Range ABCD, TimeoutDis+, LTR+, OBFF Not Supported DevCtl2: Completion Timeout: 50us to 50ms, TimeoutDis-, LTR-, OBFF Disabled LnkCtl2: Target Link Speed: 5GT/s, EnterCompliance- SpeedDis- Transmit Margin: Normal Operating Range, EnterModifiedCompliance- ComplianceSOS- Compliance De-emphasis: -6dB LnkSta2: Current De-emphasis Level: -6dB, EqualizationComplete-, EqualizationPhase1- EqualizationPhase2-, EqualizationPhase3-, LinkEqualizationRequest- Capabilities: [100 v2] Advanced Error Reporting UESta: DLP- SDES- TLP- FCP- CmpltTO- CmpltAbrt- UnxCmplt- RxOF- MalfTLP- ECRC- UnsupReq- ACSViol- UEMsk: DLP- SDES- TLP- FCP- CmpltTO- CmpltAbrt- UnxCmplt- RxOF- MalfTLP- ECRC- UnsupReq- ACSViol- UESvrt: DLP+ SDES+ TLP- FCP+ CmpltTO- CmpltAbrt- UnxCmplt- RxOF+ MalfTLP+ ECRC- UnsupReq- ACSViol- CESta: RxErr- BadTLP- BadDLLP- Rollover- Timeout- NonFatalErr+ CEMsk: RxErr- BadTLP- BadDLLP- Rollover- Timeout- NonFatalErr+ AERCap: First Error Pointer: 00, GenCap+ CGenEn- ChkCap+ ChkEn- Capabilities: [140 v1] Device Serial Number a0-36-9f-ff-ff-23-18-c0 Capabilities: [150 v1] Alternative Routing-ID Interpretation (ARI) ARICap: MFVC- ACS-, Next Function: 1 ARICtl: MFVC- ACS-, Function Group: 0 Capabilities: [160 v1] Single Root I/O Virtualization (SR-IOV) IOVCap: Migration-, Interrupt Message Number: 000 IOVCtl: Enable- Migration- Interrupt- MSE- ARIHierarchy- IOVSta: Migration- Initial VFs: 8, Total VFs: 8, Number of VFs: 0, Function Dependency Link: 00 VF offset: 384, stride: 4, Device ID: 1520 Supported Page Size: 00000553, System Page Size: 00000001 Region 0: Memory at 0000000000000000 (64-bit, prefetchable) Region 3: Memory at 0000000000000000 (64-bit, prefetchable) VF Migration: offset: 00000000, BIR: 0 Capabilities: [1a0 v1] Transaction Processing Hints Device specific mode supported Steering table in TPH capability structure Capabilities: [1c0 v1] Latency Tolerance Reporting Max snoop latency: 0ns Max no snoop latency: 0ns Capabilities: [1d0 v1] Access Control Services ACSCap: SrcValid- TransBlk- ReqRedir- CmpltRedir- UpstreamFwd- EgressCtrl- DirectTrans- ACSCtl: SrcValid- TransBlk- ReqRedir- CmpltRedir- UpstreamFwd- EgressCtrl- DirectTrans- Kernel driver in use: igb Kernel modules: igb 01:00.1 Ethernet controller: Intel Corporation I350 Gigabit Network Connection (rev 01) Subsystem: Intel Corporation Ethernet Server Adapter I350-T2 Control: I/O- Mem+ BusMaster+ SpecCycle- MemWINV- VGASnoop- ParErr+ Stepping- SERR+ FastB2B- DisINTx- Status: Cap+ 66MHz- UDF- FastB2B- ParErr- DEVSELúst >TAbort- <TAbort- <MAbort- >SERR- <PERR- INTx- Latency: 0, Cache Line Size: 64 bytes Interrupt: pin B routed to IRQ 148 Region 0: Memory at 30100000 (32-bit, non-prefetchable) [size=1M] Region 3: Memory at 30204000 (32-bit, non-prefetchable) [size\x16K] Capabilities: [40] Power Management version 3 Flags: PMEClk- DSI+ D1- D2- AuxCurrent=0mA PME(D0+,D1-,D2-,D3hot+,D3cold+) Status: D0 NoSoftRst+ PME-Enable- DSel=0 DScale=1 PME- Capabilities: [50] MSI: Enable- Count=1/1 Maskable+ 64bit+ Address: 0000000000000000 Data: 0000 Masking: 00000000 Pending: 00000000 Capabilities: [70] MSI-X: Enable- Count\x10 Masked- Vector table: BAR=3 offset\0000000 PBA: BAR=3 offset\0002000 Capabilities: [a0] Express (v2) Endpoint, MSI 00 DevCap: MaxPayload 512 bytes, PhantFunc 0, Latency L0s <512ns, L1 <64us ExtTag- AttnBtn- AttnInd- PwrInd- RBE+ FLReset+ DevCtl: Report errors: Correctable+ Non-Fatal+ Fatal+ Unsupported+ RlxdOrd+ ExtTag- PhantFunc- AuxPwr- NoSnoop+ FLReset- MaxPayload 128 bytes, MaxReadReq 512 bytes DevSta: CorrErr- UncorrErr- FatalErr- UnsuppReq- AuxPwr+ TransPend- LnkCap: Port #0, Speed 5GT/s, Width x4, ASPM L0s L1, Latency L0 <4us, L1 <32us ClockPM- Surprise- LLActRep- BwNot- LnkCtl: ASPM Disabled; RCB 64 bytes Disabled- Retrain- CommClk- ExtSynch- ClockPM- AutWidDis- BWInt- AutBWInt- LnkSta: Speed 5GT/s, Width x1, TrErr- Train- SlotClk+ DLActive- BWMgmt- ABWMgmt- DevCap2: Completion Timeout: Range ABCD, TimeoutDis+, LTR+, OBFF Not Supported DevCtl2: Completion Timeout: 50us to 50ms, TimeoutDis-, LTR-, OBFF Disabled LnkSta2: Current De-emphasis Level: -6dB, EqualizationComplete-, EqualizationPhase1- EqualizationPhase2-, EqualizationPhase3-, LinkEqualizationRequest- Capabilities: [100 v2] Advanced Error Reporting UESta: DLP- SDES- TLP- FCP- CmpltTO- CmpltAbrt- UnxCmplt- RxOF- MalfTLP- ECRC- UnsupReq- ACSViol- UEMsk: DLP- SDES- TLP- FCP- CmpltTO- CmpltAbrt- UnxCmplt- RxOF- MalfTLP- ECRC- UnsupReq- ACSViol- UESvrt: DLP+ SDES+ TLP- FCP+ CmpltTO- CmpltAbrt- UnxCmplt- RxOF+ MalfTLP+ ECRC- UnsupReq- ACSViol- CESta: RxErr- BadTLP- BadDLLP- Rollover- Timeout- NonFatalErr+ CEMsk: RxErr- BadTLP- BadDLLP- Rollover- Timeout- NonFatalErr+ AERCap: First Error Pointer: 00, GenCap+ CGenEn- ChkCap+ ChkEn- Capabilities: [140 v1] Device Serial Number a0-36-9f-ff-ff-23-18-c0 Capabilities: [150 v1] Alternative Routing-ID Interpretation (ARI) ARICap: MFVC- ACS-, Next Function: 0 ARICtl: MFVC- ACS-, Function Group: 0 Capabilities: [160 v1] Single Root I/O Virtualization (SR-IOV) IOVCap: Migration-, Interrupt Message Number: 000 IOVCtl: Enable- Migration- Interrupt- MSE- ARIHierarchy- IOVSta: Migration- Initial VFs: 8, Total VFs: 8, Number of VFs: 0, Function Dependency Link: 01 VF offset: 384, stride: 4, Device ID: 1520 Supported Page Size: 00000553, System Page Size: 00000001 Region 0: Memory at 0000000000000000 (64-bit, prefetchable) Region 3: Memory at 0000000000000000 (64-bit, prefetchable) VF Migration: offset: 00000000, BIR: 0 Capabilities: [1a0 v1] Transaction Processing Hints Device specific mode supported Steering table in TPH capability structure Capabilities: [1d0 v1] Access Control Services ACSCap: SrcValid- TransBlk- ReqRedir- CmpltRedir- UpstreamFwd- EgressCtrl- DirectTrans- ACSCtl: SrcValid- TransBlk- ReqRedir- CmpltRedir- UpstreamFwd- EgressCtrl- DirectTrans- Kernel driver in use: igb Kernel modules: igb Phil Edworthy (5): PCI: host: Add Renesas R-Car PCIe driver ARM: shmobile: r8a7791: Add PCIe clock ARM: shmobile: Add PCIe resources to r8a7791 device ARM: shmobile: Add PCIe to r8a7791 device Kconfig ARM: koelsch: Add PCIe to defconfig arch/arm/configs/koelsch_defconfig | 3 + arch/arm/mach-shmobile/Kconfig | 2 + arch/arm/mach-shmobile/clock-r8a7791.c | 3 + arch/arm/mach-shmobile/setup-r8a7791.c | 13 + drivers/pci/host/Kconfig | 6 + drivers/pci/host/Makefile | 1 + drivers/pci/host/pcie-rcar.c | 614 ++++++++++++++++++++++++++++++++ drivers/pci/host/pcie-rcar.h | 84 +++++ 10 files changed, 744 insertions(+), 1 deletion(-) create mode 100644 drivers/pci/host/pcie-rcar.c create mode 100644 drivers/pci/host/pcie-rcar.h -- 1.7.9.5 ^ permalink raw reply [flat|nested] 16+ messages in thread
* [PATCH 1/5] PCI: host: Add Renesas R-Car PCIe driver 2014-02-26 16:08 ` Phil Edworthy @ 2014-02-26 16:08 ` Phil Edworthy -1 siblings, 0 replies; 16+ messages in thread From: Phil Edworthy @ 2014-02-26 16:08 UTC (permalink / raw) To: linux-pci Cc: linux-sh, Bjorn Helgaas, Valentine Barshak, Simon Horman, Magnus Damm, Phil Edworthy This PCIe Host driver currently does not support MSI, so cards fall back to INTx interrupts. Signed-off-by: Phil Edworthy <phil.edworthy@renesas.com> --- drivers/pci/host/Kconfig | 6 + drivers/pci/host/Makefile | 1 + drivers/pci/host/pcie-rcar.c | 614 ++++++++++++++++++++++++++++++++++++++++++ drivers/pci/host/pcie-rcar.h | 84 ++++++ 4 files changed, 705 insertions(+) create mode 100644 drivers/pci/host/pcie-rcar.c create mode 100644 drivers/pci/host/pcie-rcar.h diff --git a/drivers/pci/host/Kconfig b/drivers/pci/host/Kconfig index 47d46c6..b1c1787 100644 --- a/drivers/pci/host/Kconfig +++ b/drivers/pci/host/Kconfig @@ -33,4 +33,10 @@ config PCI_RCAR_GEN2 There are 3 internal PCI controllers available with a single built-in EHCI/OHCI host controller present on each one. +config PCI_RCAR_GEN2_PCIE + bool "Renesas R-Car PCIe controller" + depends on ARM && (ARCH_R8A7790 || ARCH_R8A7791 || COMPILE_TEST) + help + Say Y here if you want PCIe controller support on R-Car Gen2 SoCs. + endmenu diff --git a/drivers/pci/host/Makefile b/drivers/pci/host/Makefile index 13fb333..19946f9 100644 --- a/drivers/pci/host/Makefile +++ b/drivers/pci/host/Makefile @@ -4,3 +4,4 @@ obj-$(CONFIG_PCI_IMX6) += pci-imx6.o obj-$(CONFIG_PCI_MVEBU) += pci-mvebu.o 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 diff --git a/drivers/pci/host/pcie-rcar.c b/drivers/pci/host/pcie-rcar.c new file mode 100644 index 0000000..d9a315f --- /dev/null +++ b/drivers/pci/host/pcie-rcar.c @@ -0,0 +1,614 @@ +/* + * PCIe driver for Renesas R-Car SoCs + * Copyright (C) 2013 Renesas Electronics Europe Ltd + * + * Based on: + * arch/sh/drivers/pci/pcie-sh7786.c + * arch/sh/drivers/pci/ops-sh7786.c + * Copyright (C) 2009 - 2011 Paul Mundt + * + * 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/module.h> +#include <linux/kernel.h> +#include <linux/of_address.h> +#include <linux/of_irq.h> +#include <linux/of_pci.h> +#include <linux/of_platform.h> +#include <linux/pci.h> +#include <linux/platform_device.h> +#include <linux/slab.h> +#include "pcie-rcar.h" + +#define DRV_NAME "rcar-pcie" + +enum chip_id { + RCAR_GENERIC, + RCAR_H1, +}; + +#define RCONF(x) (PCICONF(0)+(x)) +#define REXPCAP(x) (EXPCAP(0)+(x)) +#define RVCCAP(x) (VCCAP(0)+(x)) + +#define PCIE_CONF_BUS(b) (((b) & 0xff) << 24) +#define PCIE_CONF_DEV(d) (((d) & 0x1f) << 19) +#define PCIE_CONF_FUNC(f) (((f) & 0x7) << 16) + +#define NR_PCI_RESOURCES 4 + +/* Structure representing the PCIe interface */ +struct rcar_pcie { + struct device *dev; + void __iomem *base; + int irq; + struct clk *clk; + struct resource *res[NR_PCI_RESOURCES]; + int haslink; + enum chip_id chip; + u8 root_bus_nr; +}; + +static inline struct rcar_pcie *sys_to_pcie(struct pci_sys_data *sys) +{ + return sys->private_data; +} + +static void +pci_write_reg(struct rcar_pcie *pcie, unsigned long val, unsigned long reg) +{ + writel(val, pcie->base + reg); +} + +static unsigned long +pci_read_reg(struct rcar_pcie *pcie, unsigned long reg) +{ + return readl(pcie->base + reg); +} + +enum { + PCI_ACCESS_READ, + PCI_ACCESS_WRITE, +}; + +static void rcar_rmw32(struct rcar_pcie *pcie, + int where, u32 mask, u32 data) +{ + int shift = 8 * (where & 3); + u32 val = pci_read_reg(pcie, where & ~3); + val &= ~(mask << shift); + val |= data << shift; + pci_write_reg(pcie, val, where & ~3); +} + +static u32 rcar_read_conf(struct rcar_pcie *pcie, int where) +{ + int shift = 8 * (where & 3); + u32 val = pci_read_reg(pcie, where & ~3); + return val >> shift; +} + +static int rcar_pcie_config_access(struct rcar_pcie *pcie, + unsigned char access_type, struct pci_bus *bus, + unsigned int devfn, int where, u32 *data) +{ + int dev, func, reg, index; + + dev = PCI_SLOT(devfn); + func = PCI_FUNC(devfn); + reg = where & ~3; + index = reg / 4; + + if (bus->number > 255 || dev > 31 || func > 7) + return PCIBIOS_FUNC_NOT_SUPPORTED; + + /* + * While each channel has its own memory-mapped extended config + * space, it's generally only accessible when in endpoint mode. + * When in root complex mode, the controller is unable to target + * itself with either type 0 or type 1 accesses, and indeed, any + * controller initiated target transfer to its own config space + * result in a completer abort. + * + * Each channel effectively only supports a single device, but as + * the same channel <-> device access works for any PCI_SLOT() + * value, we cheat a bit here and bind the controller's config + * space to devfn 0 in order to enable self-enumeration. In this + * case the regular ECAR/ECDR path is sidelined and the mangled + * config access itself is initiated as an internal bus transaction. + */ + if (pci_is_root_bus(bus)) { + if (dev != 0) + return PCIBIOS_DEVICE_NOT_FOUND; + + if (access_type == PCI_ACCESS_READ) + *data = pci_read_reg(pcie, PCICONF(index)); + else + pci_write_reg(pcie, *data, PCICONF(index)); + + return PCIBIOS_SUCCESSFUL; + } + + /* Clear errors */ + pci_write_reg(pcie, pci_read_reg(pcie, PCIEERRFR), PCIEERRFR); + + /* Set the PIO address */ + pci_write_reg(pcie, PCIE_CONF_BUS(bus->number) | PCIE_CONF_DEV(dev) | + PCIE_CONF_FUNC(func) | reg, PCIECAR); + + /* Enable the configuration access */ + if (bus->parent->number == pcie->root_bus_nr) + pci_write_reg(pcie, CONFIG_SEND_ENABLE | TYPE0, PCIECCTLR); + else + pci_write_reg(pcie, CONFIG_SEND_ENABLE | TYPE1, PCIECCTLR); + + /* Check for errors */ + if (pci_read_reg(pcie, PCIEERRFR) & UNSUPPORTED_REQUEST) + return PCIBIOS_DEVICE_NOT_FOUND; + + /* Check for master and target aborts */ + if (rcar_read_conf(pcie, RCONF(PCI_STATUS)) & + (PCI_STATUS_REC_MASTER_ABORT | PCI_STATUS_REC_TARGET_ABORT)) + return PCIBIOS_DEVICE_NOT_FOUND; + + if (access_type == PCI_ACCESS_READ) + *data = pci_read_reg(pcie, PCIECDR); + else + pci_write_reg(pcie, *data, PCIECDR); + + /* Disable the configuration access */ + pci_write_reg(pcie, 0, PCIECCTLR); + + return PCIBIOS_SUCCESSFUL; +} + +static int rcar_pcie_read_conf(struct pci_bus *bus, unsigned int devfn, + int where, int size, u32 *val) +{ + struct rcar_pcie *pcie = sys_to_pcie(bus->sysdata); + int ret; + + if ((size == 2) && (where & 1)) + return PCIBIOS_BAD_REGISTER_NUMBER; + else if ((size == 4) && (where & 3)) + return PCIBIOS_BAD_REGISTER_NUMBER; + + ret = rcar_pcie_config_access(pcie, PCI_ACCESS_READ, + bus, devfn, where, val); + if (ret != PCIBIOS_SUCCESSFUL) { + *val = 0xffffffff; + return ret; + } + + if (size == 1) + *val = (*val >> (8 * (where & 3))) & 0xff; + else if (size == 2) + *val = (*val >> (8 * (where & 2))) & 0xffff; + + dev_dbg(&bus->dev, "pcie-config-read: bus=%3d devfn=0x%04x " + "where=0x%04x size=%d val=0x%08lx\n", bus->number, + devfn, where, size, (unsigned long)*val); + + return ret; +} + +static int rcar_pcie_write_conf(struct pci_bus *bus, unsigned int devfn, + int where, int size, u32 val) +{ + struct rcar_pcie *pcie = sys_to_pcie(bus->sysdata); + int shift, ret; + u32 data; + + if ((size == 2) && (where & 1)) + return PCIBIOS_BAD_REGISTER_NUMBER; + else if ((size == 4) && (where & 3)) + return PCIBIOS_BAD_REGISTER_NUMBER; + + ret = rcar_pcie_config_access(pcie, PCI_ACCESS_READ, + bus, devfn, where, &data); + if (ret != PCIBIOS_SUCCESSFUL) + return ret; + + dev_dbg(&bus->dev, "pcie-config-write: bus=%3d devfn=0x%04x " + "where=0x%04x size=%d val=0x%08lx\n", bus->number, + devfn, where, size, (unsigned long)val); + + if (size == 1) { + shift = 8 * (where & 3); + data &= ~(0xff << shift); + data |= ((val & 0xff) << shift); + } else if (size == 2) { + shift = 8 * (where & 2); + data &= ~(0xffff << shift); + data |= ((val & 0xffff) << shift); + } else + data = val; + + ret = rcar_pcie_config_access(pcie, PCI_ACCESS_WRITE, + bus, devfn, where, &data); + + return ret; +} + +static struct pci_ops rcar_pcie_ops = { + .read = rcar_pcie_read_conf, + .write = rcar_pcie_write_conf, +}; + +static int rcar_pcie_setup(int nr, struct pci_sys_data *sys) +{ + struct rcar_pcie *pcie = sys_to_pcie(sys); + int ret, i, win; + + pcie->root_bus_nr = sys->busnr; + + /* Setup IO mapped memory accesses */ + ret = pci_ioremap_io(0, pcie->res[0]->start); + if (ret) + return ret; + + /* Setup PCI resources */ + /* Ignore first resource, as it's for IO */ + for (i = 1; i < NR_PCI_RESOURCES; i++) { + struct resource *res = pcie->res[i]; + pci_add_resource_offset(&sys->resources, res, sys->mem_offset); + } + + /* Setup PCIe address space mappings for each resource */ + for (i = win = 0; i < NR_PCI_RESOURCES; i++) { + struct resource *res = pcie->res[i]; + resource_size_t size, bus_addr; + u32 mask; + + pci_write_reg(pcie, 0x00000000, PCIEPTCTLR(win)); + + /* + * The PAMR mask is calculated in units of 128Bytes, which + * keeps things pretty simple. + */ + size = resource_size(res); + mask = (roundup_pow_of_two(size) / SZ_128) - 1; + pci_write_reg(pcie, mask << 7, PCIEPAMR(win)); + + bus_addr = i ? res->start : 0; + pci_write_reg(pcie, upper_32_bits(bus_addr), PCIEPARH(win)); + pci_write_reg(pcie, lower_32_bits(bus_addr), PCIEPARL(win)); + + mask = PAR_ENABLE; + /* First resource is for IO */ + if (i == 0) + mask |= IO_SPACE; + + pci_write_reg(pcie, mask, PCIEPTCTLR(win)); + + win++; + } + + return 1; +} + +static int rcar_pcie_map_irq(const struct pci_dev *dev, u8 slot, u8 pin) +{ + struct rcar_pcie *pcie = sys_to_pcie(dev->bus->sysdata); + return pcie->irq; +} + +static void __init rcar_pcie_enable(struct rcar_pcie *pcie) +{ + struct hw_pci hw; + + memset(&hw, 0, sizeof(hw)); + + hw.nr_controllers = 1; + hw.private_data = (void **)&pcie; + hw.setup = rcar_pcie_setup, + hw.map_irq = rcar_pcie_map_irq, + hw.ops = &rcar_pcie_ops, + + pci_common_init(&hw); +} + +static int __init phy_wait_for_ack(struct rcar_pcie *pcie) +{ + unsigned int timeout = 100; + + while (timeout--) { + if (pci_read_reg(pcie, H1_PCIEPHYADRR) & PHY_ACK) + return 0; + + udelay(100); + } + + dev_err(pcie->dev, "Access to PCIe phy timed out\n"); + + return -ETIMEDOUT; +} + +static void __init phy_write_reg(struct rcar_pcie *pcie, + unsigned int rate, unsigned int addr, + unsigned int lane, unsigned int data) +{ + unsigned long phyaddr; + + phyaddr = WRITE_CMD | + ((rate & 1) << RATE_POS) | + ((lane & 0xf) << LANE_POS) | + ((addr & 0xff) << ADR_POS); + + /* Set write data */ + pci_write_reg(pcie, data, H1_PCIEPHYDOUTR); + pci_write_reg(pcie, phyaddr, H1_PCIEPHYADRR); + + /* Ignore errors as they will be dealt with if the data link is down */ + phy_wait_for_ack(pcie); + + /* Clear command */ + pci_write_reg(pcie, 0, H1_PCIEPHYDOUTR); + pci_write_reg(pcie, 0, H1_PCIEPHYADRR); + + /* Ignore errors as they will be dealt with if the data link is down */ + phy_wait_for_ack(pcie); +} + +static int __init rcar_pcie_phy_init_rcar_h1(struct rcar_pcie *pcie) +{ + unsigned int timeout = 100; + + /* Initialize the phy */ + phy_write_reg(pcie, 0, 0x42, 0x1, 0x0EC34191); + phy_write_reg(pcie, 1, 0x42, 0x1, 0x0EC34180); + phy_write_reg(pcie, 0, 0x43, 0x1, 0x00210188); + phy_write_reg(pcie, 1, 0x43, 0x1, 0x00210188); + phy_write_reg(pcie, 0, 0x44, 0x1, 0x015C0014); + phy_write_reg(pcie, 1, 0x44, 0x1, 0x015C0014); + phy_write_reg(pcie, 1, 0x4C, 0x1, 0x786174A0); + phy_write_reg(pcie, 1, 0x4D, 0x1, 0x048000BB); + phy_write_reg(pcie, 0, 0x51, 0x1, 0x079EC062); + phy_write_reg(pcie, 0, 0x52, 0x1, 0x20000000); + phy_write_reg(pcie, 1, 0x52, 0x1, 0x20000000); + phy_write_reg(pcie, 1, 0x56, 0x1, 0x00003806); + + phy_write_reg(pcie, 0, 0x60, 0x1, 0x004B03A5); + phy_write_reg(pcie, 0, 0x64, 0x1, 0x3F0F1F0F); + phy_write_reg(pcie, 0, 0x66, 0x1, 0x00008000); + + while (timeout--) { + if (pci_read_reg(pcie, H1_PCIEPHYSR)) + return 0; + + udelay(100); + } + + return -ETIMEDOUT; +} + +static int __init rcar_pcie_wait_for_dl(struct rcar_pcie *pcie) +{ + unsigned int timeout = 100; + + while (timeout--) { + if ((pci_read_reg(pcie, PCIETSTR) & DATA_LINK_ACTIVE)) + return 0; + + udelay(100); + } + + return -ETIMEDOUT; +} + +static void __init rcar_pcie_hw_init(struct rcar_pcie *pcie) +{ + /* Initialise R-Car H1 PHY & wait for it to be ready */ + if (pcie->chip == RCAR_H1) + if (rcar_pcie_phy_init_rcar_h1(pcie)) + return; + + /* Begin initialization */ + pci_write_reg(pcie, 0, PCIETCTLR); + + /* Set mode */ + pci_write_reg(pcie, 1, PCIEMSR); + + /* + * For target transfers, setup a single 64-bit 4GB mapping at address + * 0. The hardware can only map memory that starts on a power of two + * boundary, and size is power of 2, so best to ignore it. + */ + pci_write_reg(pcie, 0, PCIEPRAR(0)); + pci_write_reg(pcie, 0, PCIELAR(0)); + pci_write_reg(pcie, 0xfffffff0UL | LAM_PREFETCH | + LAM_64BIT | LAR_ENABLE, PCIELAMR(0)); + pci_write_reg(pcie, 0, PCIELAR(1)); + pci_write_reg(pcie, 0, PCIEPRAR(1)); + pci_write_reg(pcie, 0, PCIELAMR(1)); + + /* + * Initial header for port config space is type 1, set the device + * class to match. Hardware takes care of propagating the IDSETR + * settings, so there is no need to bother with a quirk. + */ + pci_write_reg(pcie, PCI_CLASS_BRIDGE_PCI << 16, IDSETR1); + + /* + * Setup Secondary Bus Number & Subordinate Bus Number, even though + * they aren't used, to avoid bridge being detected as broken. + */ + rcar_rmw32(pcie, RCONF(PCI_SECONDARY_BUS), 0xff, 1); + rcar_rmw32(pcie, RCONF(PCI_SUBORDINATE_BUS), 0xff, 1); + + /* Initialize default capabilities. */ + rcar_rmw32(pcie, REXPCAP(0), 0, PCI_CAP_ID_EXP); + rcar_rmw32(pcie, REXPCAP(PCI_EXP_FLAGS), + PCI_EXP_FLAGS_TYPE, PCI_EXP_TYPE_ROOT_PORT << 4); + rcar_rmw32(pcie, RCONF(PCI_HEADER_TYPE), 0x7f, + PCI_HEADER_TYPE_BRIDGE); + + /* Enable data link layer active state reporting */ + rcar_rmw32(pcie, REXPCAP(PCI_EXP_LNKCAP), 0, PCI_EXP_LNKCAP_DLLLARC); + + /* Write out the physical slot number = 0 */ + rcar_rmw32(pcie, REXPCAP(PCI_EXP_SLTCAP), PCI_EXP_SLTCAP_PSN, 0); + + /* Set the completion timer timeout to the maximum 50ms. */ + rcar_rmw32(pcie, TLCTLR+1, 0x3f, 50); + + /* Terminate list of capabilities (Next Capability Offset=0) */ + rcar_rmw32(pcie, RVCCAP(0), 0xfff0, 0); + + /* Enable MAC data scrambling. */ + rcar_rmw32(pcie, MACCTLR, SCRAMBLE_DISABLE, 0); + + /* Finish initialization - establish a PCI Express link */ + pci_write_reg(pcie, CFINIT, PCIETCTLR); + + /* This will timeout if we don't have a link. */ + pcie->haslink = !rcar_pcie_wait_for_dl(pcie); + + /* Enable INTx interrupts */ + rcar_rmw32(pcie, RCONF(PCI_INTERRUPT_LINE), 0xff, 0); + rcar_rmw32(pcie, RCONF(PCI_INTERRUPT_PIN), 0xff, 1); + rcar_rmw32(pcie, PCIEINTXR, 0, 0xF << 8); + + /* Enable slave Bus Mastering */ + rcar_rmw32(pcie, RCONF(PCI_STATUS), PCI_STATUS_DEVSEL_MASK, + PCI_COMMAND_IO | PCI_COMMAND_MEMORY | PCI_COMMAND_MASTER | + PCI_STATUS_CAP_LIST | PCI_STATUS_DEVSEL_FAST); + + wmb(); +} + +static int rcar_pcie_clocks_get(struct rcar_pcie *pcie) +{ + int err = 0; + + pcie->clk = clk_get(pcie->dev, NULL); + if (IS_ERR(pcie->clk)) + err = PTR_ERR(pcie->clk); + else + clk_enable(pcie->clk); + + return err; +} + +static void rcar_pcie_clocks_put(struct rcar_pcie *pcie) +{ + clk_put(pcie->clk); +} + +struct rcar_pcie_errs { + int bit; + const char *description; +}; + +static int __init rcar_pcie_get_resources(struct platform_device *pdev, + struct rcar_pcie *pcie) +{ + struct resource *res; + int i; + int err; + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + + i = platform_get_irq(pdev, 0); + if (!res || i < 0) { + dev_err(pcie->dev, "cannot get platform resources\n"); + return -ENOENT; + } + pcie->irq = i; + + err = rcar_pcie_clocks_get(pcie); + if (err) { + dev_err(&pdev->dev, "failed to get clocks: %d\n", err); + return err; + } + + pcie->base = devm_request_and_ioremap(&pdev->dev, res); + if (!pcie->base) { + dev_err(&pdev->dev, "failed to map PCIe registers: %d\n", err); + err = -ENOMEM; + goto err_map_reg; + } + + return 0; + +err_map_reg: + rcar_pcie_clocks_put(pcie); + + return err; +} + +static struct platform_device_id rcar_pcie_id_table[] = { + { "r8a7779-pcie", RCAR_H1 }, + { "r8a7790-pcie", RCAR_GENERIC }, + { "r8a7791-pcie", RCAR_GENERIC }, + {}, +}; +MODULE_DEVICE_TABLE(platform, rcar_pcie_id_table); + +static int __init rcar_pcie_probe(struct platform_device *pdev) +{ + struct rcar_pcie *pcie; + struct resource *res; + unsigned int data; + int i, err; + + pcie = devm_kzalloc(&pdev->dev, sizeof(*pcie), GFP_KERNEL); + if (!pcie) + return -ENOMEM; + + pcie->dev = &pdev->dev; + pcie->chip = pdev->id_entry->driver_data; + + /* Get resources */ + err = rcar_pcie_get_resources(pdev, pcie); + if (err < 0) { + dev_err(&pdev->dev, "failed to request resources: %d\n", err); + return err; + } + + /* Get the I/O and memory ranges */ + for (i = 0; i < NR_PCI_RESOURCES; i++) { + res = platform_get_resource(pdev, IORESOURCE_MEM, i+1); + if (!res) { + dev_err(&pdev->dev, "cannot get MEM%d resources\n", i); + return -ENOENT; + } + pcie->res[i] = res; + } + + rcar_pcie_hw_init(pcie); + + if (!pcie->haslink) { + dev_info(&pdev->dev, "PCI: PCIe link down\n"); + return 0; + } + + data = pci_read_reg(pcie, MACSR); + dev_info(&pdev->dev, "PCIe x%d: link up\n", (data >> 20) & 0x3f); + + rcar_pcie_enable(pcie); + + platform_set_drvdata(pdev, pcie); + return 0; +} + +static struct platform_driver rcar_pcie_driver = { + .probe = rcar_pcie_probe, + .driver = { + .name = DRV_NAME, + .owner = THIS_MODULE, + }, + .id_table = rcar_pcie_id_table, +}; + +module_platform_driver(rcar_pcie_driver); + +MODULE_AUTHOR("Phil Edworthy <phil.edworthy@renesas.com>"); +MODULE_DESCRIPTION("Renesas R-Car PCIe driver"); +MODULE_LICENSE("GPLv2"); diff --git a/drivers/pci/host/pcie-rcar.h b/drivers/pci/host/pcie-rcar.h new file mode 100644 index 0000000..a6b407f --- /dev/null +++ b/drivers/pci/host/pcie-rcar.h @@ -0,0 +1,84 @@ +/* + * PCI Express definitions for Renesas R-Car SoCs + */ +#ifndef __PCI_RCAR_H +#define __PCI_RCAR_H + +#define PCI_DEVICE_ID_RENESAS_RCAR 0x0018 + +#define PCIECAR 0x000010 +#define PCIECCTLR 0x000018 +#define CONFIG_SEND_ENABLE (1 << 31) +#define TYPE0 (0 << 8) +#define TYPE1 (1 << 8) +#define PCIECDR 0x000020 +#define PCIEMSR 0x000028 +#define PCIEINTXR 0x000400 +#define PCIEPHYSR 0x0007f0 + +/* Transfer control */ +#define PCIETCTLR 0x02000 +#define CFINIT 1 +#define PCIETSTR 0x02004 +#define DATA_LINK_ACTIVE 1 +#define PCIEINTR 0x02008 +#define PCIEINTER 0x0200c +#define PCIEERRFR 0x02020 +#define UNSUPPORTED_REQUEST (1 << 4) +#define PCIEERRFER 0x02024 +#define PCIEERRFR2 0x02028 +#define PCIEPMSR 0x02034 +#define PCIEPMSCIER 0x02038 +#define PCIEMSIFR 0x02044 + +/* root port address */ +#define PCIEPRAR(x) (0x02080 + ((x) * 0x4)) + +/* local address reg & mask */ +#define PCIELAR(x) (0x02200 + ((x) * 0x20)) +#define PCIELAMR(x) (0x02208 + ((x) * 0x20)) +#define LAM_PMIOLAMnB3 (1 << 3) +#define LAM_PMIOLAMnB2 (1 << 2) +#define LAM_PREFETCH (1 << 3) +#define LAM_64BIT (1 << 2) +#define LAR_ENABLE (1 << 1) + +/* PCIe address reg & mask */ +#define PCIEPARL(x) (0x03400 + ((x) * 0x20)) +#define PCIEPARH(x) (0x03404 + ((x) * 0x20)) +#define PCIEPAMR(x) (0x03408 + ((x) * 0x20)) +#define PCIEPTCTLR(x) (0x0340c + ((x) * 0x20)) +#define PAR_ENABLE (1 << 31) +#define IO_SPACE (1 << 8) + +/* Configuration */ +#define PCICONF(x) (0x010000 + ((x) * 0x4)) +#define PMCAP(x) (0x010040 + ((x) * 0x4)) +#define MSICAP(x) (0x010050 + ((x) * 0x4)) +#define EXPCAP(x) (0x010070 + ((x) * 0x4)) +#define VCCAP(x) (0x010100 + ((x) * 0x4)) +#define SERNUMCAP(x) (0x0101b0 + ((x) * 0x4)) + +/* link layer */ +#define IDSETR0 0x011000 +#define IDSETR1 0x011004 +#define SUBIDSETR 0x011024 +#define DSERSETR0 0x01102c +#define DSERSETR1 0x011030 +#define TLCTLR 0x011048 +#define MACSR 0x011054 +#define MACCTLR 0x011058 +#define SCRAMBLE_DISABLE (1 << 27) + +/* R-Car H1 PHY */ +#define H1_PCIEPHYCTLR 0x040008 +#define H1_PCIEPHYADRR 0x04000c +#define WRITE_CMD (1 << 16) +#define PHY_ACK (1 << 24) +#define RATE_POS 12 +#define LANE_POS 8 +#define ADR_POS 0 +#define H1_PCIEPHYDOUTR 0x040014 +#define H1_PCIEPHYSR 0x040018 + +#endif /* __PCI_RCAR_H */ -- 1.7.9.5 ^ permalink raw reply related [flat|nested] 16+ messages in thread
* [PATCH 1/5] PCI: host: Add Renesas R-Car PCIe driver @ 2014-02-26 16:08 ` Phil Edworthy 0 siblings, 0 replies; 16+ messages in thread From: Phil Edworthy @ 2014-02-26 16:08 UTC (permalink / raw) To: linux-pci Cc: linux-sh, Bjorn Helgaas, Valentine Barshak, Simon Horman, Magnus Damm, Phil Edworthy This PCIe Host driver currently does not support MSI, so cards fall back to INTx interrupts. Signed-off-by: Phil Edworthy <phil.edworthy@renesas.com> --- drivers/pci/host/Kconfig | 6 + drivers/pci/host/Makefile | 1 + drivers/pci/host/pcie-rcar.c | 614 ++++++++++++++++++++++++++++++++++++++++++ drivers/pci/host/pcie-rcar.h | 84 ++++++ 4 files changed, 705 insertions(+) create mode 100644 drivers/pci/host/pcie-rcar.c create mode 100644 drivers/pci/host/pcie-rcar.h diff --git a/drivers/pci/host/Kconfig b/drivers/pci/host/Kconfig index 47d46c6..b1c1787 100644 --- a/drivers/pci/host/Kconfig +++ b/drivers/pci/host/Kconfig @@ -33,4 +33,10 @@ config PCI_RCAR_GEN2 There are 3 internal PCI controllers available with a single built-in EHCI/OHCI host controller present on each one. +config PCI_RCAR_GEN2_PCIE + bool "Renesas R-Car PCIe controller" + depends on ARM && (ARCH_R8A7790 || ARCH_R8A7791 || COMPILE_TEST) + help + Say Y here if you want PCIe controller support on R-Car Gen2 SoCs. + endmenu diff --git a/drivers/pci/host/Makefile b/drivers/pci/host/Makefile index 13fb333..19946f9 100644 --- a/drivers/pci/host/Makefile +++ b/drivers/pci/host/Makefile @@ -4,3 +4,4 @@ obj-$(CONFIG_PCI_IMX6) += pci-imx6.o obj-$(CONFIG_PCI_MVEBU) += pci-mvebu.o 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 diff --git a/drivers/pci/host/pcie-rcar.c b/drivers/pci/host/pcie-rcar.c new file mode 100644 index 0000000..d9a315f --- /dev/null +++ b/drivers/pci/host/pcie-rcar.c @@ -0,0 +1,614 @@ +/* + * PCIe driver for Renesas R-Car SoCs + * Copyright (C) 2013 Renesas Electronics Europe Ltd + * + * Based on: + * arch/sh/drivers/pci/pcie-sh7786.c + * arch/sh/drivers/pci/ops-sh7786.c + * Copyright (C) 2009 - 2011 Paul Mundt + * + * 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/module.h> +#include <linux/kernel.h> +#include <linux/of_address.h> +#include <linux/of_irq.h> +#include <linux/of_pci.h> +#include <linux/of_platform.h> +#include <linux/pci.h> +#include <linux/platform_device.h> +#include <linux/slab.h> +#include "pcie-rcar.h" + +#define DRV_NAME "rcar-pcie" + +enum chip_id { + RCAR_GENERIC, + RCAR_H1, +}; + +#define RCONF(x) (PCICONF(0)+(x)) +#define REXPCAP(x) (EXPCAP(0)+(x)) +#define RVCCAP(x) (VCCAP(0)+(x)) + +#define PCIE_CONF_BUS(b) (((b) & 0xff) << 24) +#define PCIE_CONF_DEV(d) (((d) & 0x1f) << 19) +#define PCIE_CONF_FUNC(f) (((f) & 0x7) << 16) + +#define NR_PCI_RESOURCES 4 + +/* Structure representing the PCIe interface */ +struct rcar_pcie { + struct device *dev; + void __iomem *base; + int irq; + struct clk *clk; + struct resource *res[NR_PCI_RESOURCES]; + int haslink; + enum chip_id chip; + u8 root_bus_nr; +}; + +static inline struct rcar_pcie *sys_to_pcie(struct pci_sys_data *sys) +{ + return sys->private_data; +} + +static void +pci_write_reg(struct rcar_pcie *pcie, unsigned long val, unsigned long reg) +{ + writel(val, pcie->base + reg); +} + +static unsigned long +pci_read_reg(struct rcar_pcie *pcie, unsigned long reg) +{ + return readl(pcie->base + reg); +} + +enum { + PCI_ACCESS_READ, + PCI_ACCESS_WRITE, +}; + +static void rcar_rmw32(struct rcar_pcie *pcie, + int where, u32 mask, u32 data) +{ + int shift = 8 * (where & 3); + u32 val = pci_read_reg(pcie, where & ~3); + val &= ~(mask << shift); + val |= data << shift; + pci_write_reg(pcie, val, where & ~3); +} + +static u32 rcar_read_conf(struct rcar_pcie *pcie, int where) +{ + int shift = 8 * (where & 3); + u32 val = pci_read_reg(pcie, where & ~3); + return val >> shift; +} + +static int rcar_pcie_config_access(struct rcar_pcie *pcie, + unsigned char access_type, struct pci_bus *bus, + unsigned int devfn, int where, u32 *data) +{ + int dev, func, reg, index; + + dev = PCI_SLOT(devfn); + func = PCI_FUNC(devfn); + reg = where & ~3; + index = reg / 4; + + if (bus->number > 255 || dev > 31 || func > 7) + return PCIBIOS_FUNC_NOT_SUPPORTED; + + /* + * While each channel has its own memory-mapped extended config + * space, it's generally only accessible when in endpoint mode. + * When in root complex mode, the controller is unable to target + * itself with either type 0 or type 1 accesses, and indeed, any + * controller initiated target transfer to its own config space + * result in a completer abort. + * + * Each channel effectively only supports a single device, but as + * the same channel <-> device access works for any PCI_SLOT() + * value, we cheat a bit here and bind the controller's config + * space to devfn 0 in order to enable self-enumeration. In this + * case the regular ECAR/ECDR path is sidelined and the mangled + * config access itself is initiated as an internal bus transaction. + */ + if (pci_is_root_bus(bus)) { + if (dev != 0) + return PCIBIOS_DEVICE_NOT_FOUND; + + if (access_type = PCI_ACCESS_READ) + *data = pci_read_reg(pcie, PCICONF(index)); + else + pci_write_reg(pcie, *data, PCICONF(index)); + + return PCIBIOS_SUCCESSFUL; + } + + /* Clear errors */ + pci_write_reg(pcie, pci_read_reg(pcie, PCIEERRFR), PCIEERRFR); + + /* Set the PIO address */ + pci_write_reg(pcie, PCIE_CONF_BUS(bus->number) | PCIE_CONF_DEV(dev) | + PCIE_CONF_FUNC(func) | reg, PCIECAR); + + /* Enable the configuration access */ + if (bus->parent->number = pcie->root_bus_nr) + pci_write_reg(pcie, CONFIG_SEND_ENABLE | TYPE0, PCIECCTLR); + else + pci_write_reg(pcie, CONFIG_SEND_ENABLE | TYPE1, PCIECCTLR); + + /* Check for errors */ + if (pci_read_reg(pcie, PCIEERRFR) & UNSUPPORTED_REQUEST) + return PCIBIOS_DEVICE_NOT_FOUND; + + /* Check for master and target aborts */ + if (rcar_read_conf(pcie, RCONF(PCI_STATUS)) & + (PCI_STATUS_REC_MASTER_ABORT | PCI_STATUS_REC_TARGET_ABORT)) + return PCIBIOS_DEVICE_NOT_FOUND; + + if (access_type = PCI_ACCESS_READ) + *data = pci_read_reg(pcie, PCIECDR); + else + pci_write_reg(pcie, *data, PCIECDR); + + /* Disable the configuration access */ + pci_write_reg(pcie, 0, PCIECCTLR); + + return PCIBIOS_SUCCESSFUL; +} + +static int rcar_pcie_read_conf(struct pci_bus *bus, unsigned int devfn, + int where, int size, u32 *val) +{ + struct rcar_pcie *pcie = sys_to_pcie(bus->sysdata); + int ret; + + if ((size = 2) && (where & 1)) + return PCIBIOS_BAD_REGISTER_NUMBER; + else if ((size = 4) && (where & 3)) + return PCIBIOS_BAD_REGISTER_NUMBER; + + ret = rcar_pcie_config_access(pcie, PCI_ACCESS_READ, + bus, devfn, where, val); + if (ret != PCIBIOS_SUCCESSFUL) { + *val = 0xffffffff; + return ret; + } + + if (size = 1) + *val = (*val >> (8 * (where & 3))) & 0xff; + else if (size = 2) + *val = (*val >> (8 * (where & 2))) & 0xffff; + + dev_dbg(&bus->dev, "pcie-config-read: bus=%3d devfn=0x%04x " + "where=0x%04x size=%d val=0x%08lx\n", bus->number, + devfn, where, size, (unsigned long)*val); + + return ret; +} + +static int rcar_pcie_write_conf(struct pci_bus *bus, unsigned int devfn, + int where, int size, u32 val) +{ + struct rcar_pcie *pcie = sys_to_pcie(bus->sysdata); + int shift, ret; + u32 data; + + if ((size = 2) && (where & 1)) + return PCIBIOS_BAD_REGISTER_NUMBER; + else if ((size = 4) && (where & 3)) + return PCIBIOS_BAD_REGISTER_NUMBER; + + ret = rcar_pcie_config_access(pcie, PCI_ACCESS_READ, + bus, devfn, where, &data); + if (ret != PCIBIOS_SUCCESSFUL) + return ret; + + dev_dbg(&bus->dev, "pcie-config-write: bus=%3d devfn=0x%04x " + "where=0x%04x size=%d val=0x%08lx\n", bus->number, + devfn, where, size, (unsigned long)val); + + if (size = 1) { + shift = 8 * (where & 3); + data &= ~(0xff << shift); + data |= ((val & 0xff) << shift); + } else if (size = 2) { + shift = 8 * (where & 2); + data &= ~(0xffff << shift); + data |= ((val & 0xffff) << shift); + } else + data = val; + + ret = rcar_pcie_config_access(pcie, PCI_ACCESS_WRITE, + bus, devfn, where, &data); + + return ret; +} + +static struct pci_ops rcar_pcie_ops = { + .read = rcar_pcie_read_conf, + .write = rcar_pcie_write_conf, +}; + +static int rcar_pcie_setup(int nr, struct pci_sys_data *sys) +{ + struct rcar_pcie *pcie = sys_to_pcie(sys); + int ret, i, win; + + pcie->root_bus_nr = sys->busnr; + + /* Setup IO mapped memory accesses */ + ret = pci_ioremap_io(0, pcie->res[0]->start); + if (ret) + return ret; + + /* Setup PCI resources */ + /* Ignore first resource, as it's for IO */ + for (i = 1; i < NR_PCI_RESOURCES; i++) { + struct resource *res = pcie->res[i]; + pci_add_resource_offset(&sys->resources, res, sys->mem_offset); + } + + /* Setup PCIe address space mappings for each resource */ + for (i = win = 0; i < NR_PCI_RESOURCES; i++) { + struct resource *res = pcie->res[i]; + resource_size_t size, bus_addr; + u32 mask; + + pci_write_reg(pcie, 0x00000000, PCIEPTCTLR(win)); + + /* + * The PAMR mask is calculated in units of 128Bytes, which + * keeps things pretty simple. + */ + size = resource_size(res); + mask = (roundup_pow_of_two(size) / SZ_128) - 1; + pci_write_reg(pcie, mask << 7, PCIEPAMR(win)); + + bus_addr = i ? res->start : 0; + pci_write_reg(pcie, upper_32_bits(bus_addr), PCIEPARH(win)); + pci_write_reg(pcie, lower_32_bits(bus_addr), PCIEPARL(win)); + + mask = PAR_ENABLE; + /* First resource is for IO */ + if (i = 0) + mask |= IO_SPACE; + + pci_write_reg(pcie, mask, PCIEPTCTLR(win)); + + win++; + } + + return 1; +} + +static int rcar_pcie_map_irq(const struct pci_dev *dev, u8 slot, u8 pin) +{ + struct rcar_pcie *pcie = sys_to_pcie(dev->bus->sysdata); + return pcie->irq; +} + +static void __init rcar_pcie_enable(struct rcar_pcie *pcie) +{ + struct hw_pci hw; + + memset(&hw, 0, sizeof(hw)); + + hw.nr_controllers = 1; + hw.private_data = (void **)&pcie; + hw.setup = rcar_pcie_setup, + hw.map_irq = rcar_pcie_map_irq, + hw.ops = &rcar_pcie_ops, + + pci_common_init(&hw); +} + +static int __init phy_wait_for_ack(struct rcar_pcie *pcie) +{ + unsigned int timeout = 100; + + while (timeout--) { + if (pci_read_reg(pcie, H1_PCIEPHYADRR) & PHY_ACK) + return 0; + + udelay(100); + } + + dev_err(pcie->dev, "Access to PCIe phy timed out\n"); + + return -ETIMEDOUT; +} + +static void __init phy_write_reg(struct rcar_pcie *pcie, + unsigned int rate, unsigned int addr, + unsigned int lane, unsigned int data) +{ + unsigned long phyaddr; + + phyaddr = WRITE_CMD | + ((rate & 1) << RATE_POS) | + ((lane & 0xf) << LANE_POS) | + ((addr & 0xff) << ADR_POS); + + /* Set write data */ + pci_write_reg(pcie, data, H1_PCIEPHYDOUTR); + pci_write_reg(pcie, phyaddr, H1_PCIEPHYADRR); + + /* Ignore errors as they will be dealt with if the data link is down */ + phy_wait_for_ack(pcie); + + /* Clear command */ + pci_write_reg(pcie, 0, H1_PCIEPHYDOUTR); + pci_write_reg(pcie, 0, H1_PCIEPHYADRR); + + /* Ignore errors as they will be dealt with if the data link is down */ + phy_wait_for_ack(pcie); +} + +static int __init rcar_pcie_phy_init_rcar_h1(struct rcar_pcie *pcie) +{ + unsigned int timeout = 100; + + /* Initialize the phy */ + phy_write_reg(pcie, 0, 0x42, 0x1, 0x0EC34191); + phy_write_reg(pcie, 1, 0x42, 0x1, 0x0EC34180); + phy_write_reg(pcie, 0, 0x43, 0x1, 0x00210188); + phy_write_reg(pcie, 1, 0x43, 0x1, 0x00210188); + phy_write_reg(pcie, 0, 0x44, 0x1, 0x015C0014); + phy_write_reg(pcie, 1, 0x44, 0x1, 0x015C0014); + phy_write_reg(pcie, 1, 0x4C, 0x1, 0x786174A0); + phy_write_reg(pcie, 1, 0x4D, 0x1, 0x048000BB); + phy_write_reg(pcie, 0, 0x51, 0x1, 0x079EC062); + phy_write_reg(pcie, 0, 0x52, 0x1, 0x20000000); + phy_write_reg(pcie, 1, 0x52, 0x1, 0x20000000); + phy_write_reg(pcie, 1, 0x56, 0x1, 0x00003806); + + phy_write_reg(pcie, 0, 0x60, 0x1, 0x004B03A5); + phy_write_reg(pcie, 0, 0x64, 0x1, 0x3F0F1F0F); + phy_write_reg(pcie, 0, 0x66, 0x1, 0x00008000); + + while (timeout--) { + if (pci_read_reg(pcie, H1_PCIEPHYSR)) + return 0; + + udelay(100); + } + + return -ETIMEDOUT; +} + +static int __init rcar_pcie_wait_for_dl(struct rcar_pcie *pcie) +{ + unsigned int timeout = 100; + + while (timeout--) { + if ((pci_read_reg(pcie, PCIETSTR) & DATA_LINK_ACTIVE)) + return 0; + + udelay(100); + } + + return -ETIMEDOUT; +} + +static void __init rcar_pcie_hw_init(struct rcar_pcie *pcie) +{ + /* Initialise R-Car H1 PHY & wait for it to be ready */ + if (pcie->chip = RCAR_H1) + if (rcar_pcie_phy_init_rcar_h1(pcie)) + return; + + /* Begin initialization */ + pci_write_reg(pcie, 0, PCIETCTLR); + + /* Set mode */ + pci_write_reg(pcie, 1, PCIEMSR); + + /* + * For target transfers, setup a single 64-bit 4GB mapping at address + * 0. The hardware can only map memory that starts on a power of two + * boundary, and size is power of 2, so best to ignore it. + */ + pci_write_reg(pcie, 0, PCIEPRAR(0)); + pci_write_reg(pcie, 0, PCIELAR(0)); + pci_write_reg(pcie, 0xfffffff0UL | LAM_PREFETCH | + LAM_64BIT | LAR_ENABLE, PCIELAMR(0)); + pci_write_reg(pcie, 0, PCIELAR(1)); + pci_write_reg(pcie, 0, PCIEPRAR(1)); + pci_write_reg(pcie, 0, PCIELAMR(1)); + + /* + * Initial header for port config space is type 1, set the device + * class to match. Hardware takes care of propagating the IDSETR + * settings, so there is no need to bother with a quirk. + */ + pci_write_reg(pcie, PCI_CLASS_BRIDGE_PCI << 16, IDSETR1); + + /* + * Setup Secondary Bus Number & Subordinate Bus Number, even though + * they aren't used, to avoid bridge being detected as broken. + */ + rcar_rmw32(pcie, RCONF(PCI_SECONDARY_BUS), 0xff, 1); + rcar_rmw32(pcie, RCONF(PCI_SUBORDINATE_BUS), 0xff, 1); + + /* Initialize default capabilities. */ + rcar_rmw32(pcie, REXPCAP(0), 0, PCI_CAP_ID_EXP); + rcar_rmw32(pcie, REXPCAP(PCI_EXP_FLAGS), + PCI_EXP_FLAGS_TYPE, PCI_EXP_TYPE_ROOT_PORT << 4); + rcar_rmw32(pcie, RCONF(PCI_HEADER_TYPE), 0x7f, + PCI_HEADER_TYPE_BRIDGE); + + /* Enable data link layer active state reporting */ + rcar_rmw32(pcie, REXPCAP(PCI_EXP_LNKCAP), 0, PCI_EXP_LNKCAP_DLLLARC); + + /* Write out the physical slot number = 0 */ + rcar_rmw32(pcie, REXPCAP(PCI_EXP_SLTCAP), PCI_EXP_SLTCAP_PSN, 0); + + /* Set the completion timer timeout to the maximum 50ms. */ + rcar_rmw32(pcie, TLCTLR+1, 0x3f, 50); + + /* Terminate list of capabilities (Next Capability Offset=0) */ + rcar_rmw32(pcie, RVCCAP(0), 0xfff0, 0); + + /* Enable MAC data scrambling. */ + rcar_rmw32(pcie, MACCTLR, SCRAMBLE_DISABLE, 0); + + /* Finish initialization - establish a PCI Express link */ + pci_write_reg(pcie, CFINIT, PCIETCTLR); + + /* This will timeout if we don't have a link. */ + pcie->haslink = !rcar_pcie_wait_for_dl(pcie); + + /* Enable INTx interrupts */ + rcar_rmw32(pcie, RCONF(PCI_INTERRUPT_LINE), 0xff, 0); + rcar_rmw32(pcie, RCONF(PCI_INTERRUPT_PIN), 0xff, 1); + rcar_rmw32(pcie, PCIEINTXR, 0, 0xF << 8); + + /* Enable slave Bus Mastering */ + rcar_rmw32(pcie, RCONF(PCI_STATUS), PCI_STATUS_DEVSEL_MASK, + PCI_COMMAND_IO | PCI_COMMAND_MEMORY | PCI_COMMAND_MASTER | + PCI_STATUS_CAP_LIST | PCI_STATUS_DEVSEL_FAST); + + wmb(); +} + +static int rcar_pcie_clocks_get(struct rcar_pcie *pcie) +{ + int err = 0; + + pcie->clk = clk_get(pcie->dev, NULL); + if (IS_ERR(pcie->clk)) + err = PTR_ERR(pcie->clk); + else + clk_enable(pcie->clk); + + return err; +} + +static void rcar_pcie_clocks_put(struct rcar_pcie *pcie) +{ + clk_put(pcie->clk); +} + +struct rcar_pcie_errs { + int bit; + const char *description; +}; + +static int __init rcar_pcie_get_resources(struct platform_device *pdev, + struct rcar_pcie *pcie) +{ + struct resource *res; + int i; + int err; + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + + i = platform_get_irq(pdev, 0); + if (!res || i < 0) { + dev_err(pcie->dev, "cannot get platform resources\n"); + return -ENOENT; + } + pcie->irq = i; + + err = rcar_pcie_clocks_get(pcie); + if (err) { + dev_err(&pdev->dev, "failed to get clocks: %d\n", err); + return err; + } + + pcie->base = devm_request_and_ioremap(&pdev->dev, res); + if (!pcie->base) { + dev_err(&pdev->dev, "failed to map PCIe registers: %d\n", err); + err = -ENOMEM; + goto err_map_reg; + } + + return 0; + +err_map_reg: + rcar_pcie_clocks_put(pcie); + + return err; +} + +static struct platform_device_id rcar_pcie_id_table[] = { + { "r8a7779-pcie", RCAR_H1 }, + { "r8a7790-pcie", RCAR_GENERIC }, + { "r8a7791-pcie", RCAR_GENERIC }, + {}, +}; +MODULE_DEVICE_TABLE(platform, rcar_pcie_id_table); + +static int __init rcar_pcie_probe(struct platform_device *pdev) +{ + struct rcar_pcie *pcie; + struct resource *res; + unsigned int data; + int i, err; + + pcie = devm_kzalloc(&pdev->dev, sizeof(*pcie), GFP_KERNEL); + if (!pcie) + return -ENOMEM; + + pcie->dev = &pdev->dev; + pcie->chip = pdev->id_entry->driver_data; + + /* Get resources */ + err = rcar_pcie_get_resources(pdev, pcie); + if (err < 0) { + dev_err(&pdev->dev, "failed to request resources: %d\n", err); + return err; + } + + /* Get the I/O and memory ranges */ + for (i = 0; i < NR_PCI_RESOURCES; i++) { + res = platform_get_resource(pdev, IORESOURCE_MEM, i+1); + if (!res) { + dev_err(&pdev->dev, "cannot get MEM%d resources\n", i); + return -ENOENT; + } + pcie->res[i] = res; + } + + rcar_pcie_hw_init(pcie); + + if (!pcie->haslink) { + dev_info(&pdev->dev, "PCI: PCIe link down\n"); + return 0; + } + + data = pci_read_reg(pcie, MACSR); + dev_info(&pdev->dev, "PCIe x%d: link up\n", (data >> 20) & 0x3f); + + rcar_pcie_enable(pcie); + + platform_set_drvdata(pdev, pcie); + return 0; +} + +static struct platform_driver rcar_pcie_driver = { + .probe = rcar_pcie_probe, + .driver = { + .name = DRV_NAME, + .owner = THIS_MODULE, + }, + .id_table = rcar_pcie_id_table, +}; + +module_platform_driver(rcar_pcie_driver); + +MODULE_AUTHOR("Phil Edworthy <phil.edworthy@renesas.com>"); +MODULE_DESCRIPTION("Renesas R-Car PCIe driver"); +MODULE_LICENSE("GPLv2"); diff --git a/drivers/pci/host/pcie-rcar.h b/drivers/pci/host/pcie-rcar.h new file mode 100644 index 0000000..a6b407f --- /dev/null +++ b/drivers/pci/host/pcie-rcar.h @@ -0,0 +1,84 @@ +/* + * PCI Express definitions for Renesas R-Car SoCs + */ +#ifndef __PCI_RCAR_H +#define __PCI_RCAR_H + +#define PCI_DEVICE_ID_RENESAS_RCAR 0x0018 + +#define PCIECAR 0x000010 +#define PCIECCTLR 0x000018 +#define CONFIG_SEND_ENABLE (1 << 31) +#define TYPE0 (0 << 8) +#define TYPE1 (1 << 8) +#define PCIECDR 0x000020 +#define PCIEMSR 0x000028 +#define PCIEINTXR 0x000400 +#define PCIEPHYSR 0x0007f0 + +/* Transfer control */ +#define PCIETCTLR 0x02000 +#define CFINIT 1 +#define PCIETSTR 0x02004 +#define DATA_LINK_ACTIVE 1 +#define PCIEINTR 0x02008 +#define PCIEINTER 0x0200c +#define PCIEERRFR 0x02020 +#define UNSUPPORTED_REQUEST (1 << 4) +#define PCIEERRFER 0x02024 +#define PCIEERRFR2 0x02028 +#define PCIEPMSR 0x02034 +#define PCIEPMSCIER 0x02038 +#define PCIEMSIFR 0x02044 + +/* root port address */ +#define PCIEPRAR(x) (0x02080 + ((x) * 0x4)) + +/* local address reg & mask */ +#define PCIELAR(x) (0x02200 + ((x) * 0x20)) +#define PCIELAMR(x) (0x02208 + ((x) * 0x20)) +#define LAM_PMIOLAMnB3 (1 << 3) +#define LAM_PMIOLAMnB2 (1 << 2) +#define LAM_PREFETCH (1 << 3) +#define LAM_64BIT (1 << 2) +#define LAR_ENABLE (1 << 1) + +/* PCIe address reg & mask */ +#define PCIEPARL(x) (0x03400 + ((x) * 0x20)) +#define PCIEPARH(x) (0x03404 + ((x) * 0x20)) +#define PCIEPAMR(x) (0x03408 + ((x) * 0x20)) +#define PCIEPTCTLR(x) (0x0340c + ((x) * 0x20)) +#define PAR_ENABLE (1 << 31) +#define IO_SPACE (1 << 8) + +/* Configuration */ +#define PCICONF(x) (0x010000 + ((x) * 0x4)) +#define PMCAP(x) (0x010040 + ((x) * 0x4)) +#define MSICAP(x) (0x010050 + ((x) * 0x4)) +#define EXPCAP(x) (0x010070 + ((x) * 0x4)) +#define VCCAP(x) (0x010100 + ((x) * 0x4)) +#define SERNUMCAP(x) (0x0101b0 + ((x) * 0x4)) + +/* link layer */ +#define IDSETR0 0x011000 +#define IDSETR1 0x011004 +#define SUBIDSETR 0x011024 +#define DSERSETR0 0x01102c +#define DSERSETR1 0x011030 +#define TLCTLR 0x011048 +#define MACSR 0x011054 +#define MACCTLR 0x011058 +#define SCRAMBLE_DISABLE (1 << 27) + +/* R-Car H1 PHY */ +#define H1_PCIEPHYCTLR 0x040008 +#define H1_PCIEPHYADRR 0x04000c +#define WRITE_CMD (1 << 16) +#define PHY_ACK (1 << 24) +#define RATE_POS 12 +#define LANE_POS 8 +#define ADR_POS 0 +#define H1_PCIEPHYDOUTR 0x040014 +#define H1_PCIEPHYSR 0x040018 + +#endif /* __PCI_RCAR_H */ -- 1.7.9.5 ^ permalink raw reply related [flat|nested] 16+ messages in thread
* Re: [PATCH 1/5] PCI: host: Add Renesas R-Car PCIe driver 2014-02-26 16:08 ` Phil Edworthy @ 2014-02-26 16:34 ` Ben Dooks -1 siblings, 0 replies; 16+ messages in thread From: Ben Dooks @ 2014-02-26 16:34 UTC (permalink / raw) To: Phil Edworthy Cc: linux-pci, linux-sh, Bjorn Helgaas, Valentine Barshak, Simon Horman, Magnus Damm On 26/02/14 16:08, Phil Edworthy wrote: > This PCIe Host driver currently does not support MSI, so cards > fall back to INTx interrupts. > > Signed-off-by: Phil Edworthy <phil.edworthy@renesas.com> > + bool "Renesas R-Car PCIe controller" > + depends on ARM && (ARCH_R8A7790 || ARCH_R8A7791 || COMPILE_TEST) > + help > + Say Y here if you want PCIe controller support on R-Car Gen2 SoCs. > + > endmenu > diff --git a/drivers/pci/host/Makefile b/drivers/pci/host/Makefile > index 13fb333..19946f9 100644 > --- a/drivers/pci/host/Makefile > +++ b/drivers/pci/host/Makefile > @@ -4,3 +4,4 @@ obj-$(CONFIG_PCI_IMX6) += pci-imx6.o > obj-$(CONFIG_PCI_MVEBU) += pci-mvebu.o > 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 > diff --git a/drivers/pci/host/pcie-rcar.c b/drivers/pci/host/pcie-rcar.c > new file mode 100644 > index 0000000..d9a315f > --- /dev/null > +++ b/drivers/pci/host/pcie-rcar.c > @@ -0,0 +1,614 @@ > +/* > + * PCIe driver for Renesas R-Car SoCs > + * Copyright (C) 2013 Renesas Electronics Europe Ltd > + * > + * Based on: > + * arch/sh/drivers/pci/pcie-sh7786.c > + * arch/sh/drivers/pci/ops-sh7786.c > + * Copyright (C) 2009 - 2011 Paul Mundt > + * > + * 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/module.h> > +#include <linux/kernel.h> > +#include <linux/of_address.h> > +#include <linux/of_irq.h> > +#include <linux/of_pci.h> > +#include <linux/of_platform.h> > +#include <linux/pci.h> > +#include <linux/platform_device.h> > +#include <linux/slab.h> > +#include "pcie-rcar.h" > + > +#define DRV_NAME "rcar-pcie" > + > +enum chip_id { > + RCAR_GENERIC, > + RCAR_H1, > +}; > + > +#define RCONF(x) (PCICONF(0)+(x)) > +#define REXPCAP(x) (EXPCAP(0)+(x)) > +#define RVCCAP(x) (VCCAP(0)+(x)) > + > +#define PCIE_CONF_BUS(b) (((b) & 0xff) << 24) > +#define PCIE_CONF_DEV(d) (((d) & 0x1f) << 19) > +#define PCIE_CONF_FUNC(f) (((f) & 0x7) << 16) > + > +#define NR_PCI_RESOURCES 4 > + > +/* Structure representing the PCIe interface */ > +struct rcar_pcie { > + struct device *dev; > + void __iomem *base; > + int irq; > + struct clk *clk; > + struct resource *res[NR_PCI_RESOURCES]; > + int haslink; > + enum chip_id chip; > + u8 root_bus_nr; > +}; > + > +static inline struct rcar_pcie *sys_to_pcie(struct pci_sys_data *sys) > +{ > + return sys->private_data; > +} > + > +static void > +pci_write_reg(struct rcar_pcie *pcie, unsigned long val, unsigned long reg) > +{ > + writel(val, pcie->base + reg); > +} Do we really need wrappers like these? > +static unsigned long > +pci_read_reg(struct rcar_pcie *pcie, unsigned long reg) > +{ > + return readl(pcie->base + reg); > +} > + > +enum { > + PCI_ACCESS_READ, > + PCI_ACCESS_WRITE, > +}; > +static int rcar_pcie_map_irq(const struct pci_dev *dev, u8 slot, u8 pin) > +{ > + struct rcar_pcie *pcie = sys_to_pcie(dev->bus->sysdata); > + return pcie->irq; > +} > + > +static void __init rcar_pcie_enable(struct rcar_pcie *pcie) > +{ > + struct hw_pci hw; > + > + memset(&hw, 0, sizeof(hw)); > + > + hw.nr_controllers = 1; > + hw.private_data = (void **)&pcie; > + hw.setup = rcar_pcie_setup, > + hw.map_irq = rcar_pcie_map_irq, > + hw.ops = &rcar_pcie_ops, > + > + pci_common_init(&hw); > +} > + > +static int __init phy_wait_for_ack(struct rcar_pcie *pcie) > +{ > + unsigned int timeout = 100; > + > + while (timeout--) { > + if (pci_read_reg(pcie, H1_PCIEPHYADRR) & PHY_ACK) > + return 0; > + > + udelay(100); > + } > + > + dev_err(pcie->dev, "Access to PCIe phy timed out\n"); > + > + return -ETIMEDOUT; > +} Could this be done with some sort of sleep, instead of having to keep delaying? How long is the average wait for pci to finish a write? > + > +static void __init phy_write_reg(struct rcar_pcie *pcie, > + unsigned int rate, unsigned int addr, > + unsigned int lane, unsigned int data) > +{ > + unsigned long phyaddr; > + > + phyaddr = WRITE_CMD | > + ((rate & 1) << RATE_POS) | > + ((lane & 0xf) << LANE_POS) | > + ((addr & 0xff) << ADR_POS); > + > + /* Set write data */ > + pci_write_reg(pcie, data, H1_PCIEPHYDOUTR); > + pci_write_reg(pcie, phyaddr, H1_PCIEPHYADRR); > + > + /* Ignore errors as they will be dealt with if the data link is down */ > + phy_wait_for_ack(pcie); > + > + /* Clear command */ > + pci_write_reg(pcie, 0, H1_PCIEPHYDOUTR); > + pci_write_reg(pcie, 0, H1_PCIEPHYADRR); > + > + /* Ignore errors as they will be dealt with if the data link is down */ > + phy_wait_for_ack(pcie); > +} > + > > +static int __init rcar_pcie_wait_for_dl(struct rcar_pcie *pcie) > +{ > + unsigned int timeout = 100; > + > + while (timeout--) { > + if ((pci_read_reg(pcie, PCIETSTR) & DATA_LINK_ACTIVE)) > + return 0; > + > + udelay(100); > + } > + > + return -ETIMEDOUT; > +} Same comments as for previous delay loop > +static void __init rcar_pcie_hw_init(struct rcar_pcie *pcie) > +{ > + /* Initialise R-Car H1 PHY & wait for it to be ready */ > + if (pcie->chip == RCAR_H1) > + if (rcar_pcie_phy_init_rcar_h1(pcie)) > + return; > + > + /* Begin initialization */ > + pci_write_reg(pcie, 0, PCIETCTLR); > + > + /* Set mode */ > + pci_write_reg(pcie, 1, PCIEMSR); > + > + /* > + * For target transfers, setup a single 64-bit 4GB mapping at address > + * 0. The hardware can only map memory that starts on a power of two > + * boundary, and size is power of 2, so best to ignore it. > + */ > + pci_write_reg(pcie, 0, PCIEPRAR(0)); > + pci_write_reg(pcie, 0, PCIELAR(0)); > + pci_write_reg(pcie, 0xfffffff0UL | LAM_PREFETCH | > + LAM_64BIT | LAR_ENABLE, PCIELAMR(0)); > + pci_write_reg(pcie, 0, PCIELAR(1)); > + pci_write_reg(pcie, 0, PCIEPRAR(1)); > + pci_write_reg(pcie, 0, PCIELAMR(1)); > + > + /* > + * Initial header for port config space is type 1, set the device > + * class to match. Hardware takes care of propagating the IDSETR > + * settings, so there is no need to bother with a quirk. > + */ > + pci_write_reg(pcie, PCI_CLASS_BRIDGE_PCI << 16, IDSETR1); > + > + /* > + * Setup Secondary Bus Number & Subordinate Bus Number, even though > + * they aren't used, to avoid bridge being detected as broken. > + */ > + rcar_rmw32(pcie, RCONF(PCI_SECONDARY_BUS), 0xff, 1); > + rcar_rmw32(pcie, RCONF(PCI_SUBORDINATE_BUS), 0xff, 1); > + > + /* Initialize default capabilities. */ > + rcar_rmw32(pcie, REXPCAP(0), 0, PCI_CAP_ID_EXP); > + rcar_rmw32(pcie, REXPCAP(PCI_EXP_FLAGS), > + PCI_EXP_FLAGS_TYPE, PCI_EXP_TYPE_ROOT_PORT << 4); > + rcar_rmw32(pcie, RCONF(PCI_HEADER_TYPE), 0x7f, > + PCI_HEADER_TYPE_BRIDGE); > + > + /* Enable data link layer active state reporting */ > + rcar_rmw32(pcie, REXPCAP(PCI_EXP_LNKCAP), 0, PCI_EXP_LNKCAP_DLLLARC); > + > + /* Write out the physical slot number = 0 */ > + rcar_rmw32(pcie, REXPCAP(PCI_EXP_SLTCAP), PCI_EXP_SLTCAP_PSN, 0); > + > + /* Set the completion timer timeout to the maximum 50ms. */ > + rcar_rmw32(pcie, TLCTLR+1, 0x3f, 50); > + > + /* Terminate list of capabilities (Next Capability Offset=0) */ > + rcar_rmw32(pcie, RVCCAP(0), 0xfff0, 0); > + > + /* Enable MAC data scrambling. */ > + rcar_rmw32(pcie, MACCTLR, SCRAMBLE_DISABLE, 0); > + > + /* Finish initialization - establish a PCI Express link */ > + pci_write_reg(pcie, CFINIT, PCIETCTLR); > + > + /* This will timeout if we don't have a link. */ > + pcie->haslink = !rcar_pcie_wait_for_dl(pcie); > + > + /* Enable INTx interrupts */ > + rcar_rmw32(pcie, RCONF(PCI_INTERRUPT_LINE), 0xff, 0); > + rcar_rmw32(pcie, RCONF(PCI_INTERRUPT_PIN), 0xff, 1); > + rcar_rmw32(pcie, PCIEINTXR, 0, 0xF << 8); > + > + /* Enable slave Bus Mastering */ > + rcar_rmw32(pcie, RCONF(PCI_STATUS), PCI_STATUS_DEVSEL_MASK, > + PCI_COMMAND_IO | PCI_COMMAND_MEMORY | PCI_COMMAND_MASTER | > + PCI_STATUS_CAP_LIST | PCI_STATUS_DEVSEL_FAST); > + > + wmb(); > +} > + > +static int rcar_pcie_clocks_get(struct rcar_pcie *pcie) > +{ > + int err = 0; > + > + pcie->clk = clk_get(pcie->dev, NULL); > + if (IS_ERR(pcie->clk)) > + err = PTR_ERR(pcie->clk); > + else > + clk_enable(pcie->clk); > + > + return err; > +} Shouldn't you be using pm_runtime for this? > +static void rcar_pcie_clocks_put(struct rcar_pcie *pcie) > +{ > + clk_put(pcie->clk); > +} > + > +struct rcar_pcie_errs { > + int bit; > + const char *description; > +}; > + > +static int __init rcar_pcie_get_resources(struct platform_device *pdev, > + struct rcar_pcie *pcie) > +{ > + struct resource *res; > + int i; > + int err; > + > + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); > + > + i = platform_get_irq(pdev, 0); > + if (!res || i < 0) { > + dev_err(pcie->dev, "cannot get platform resources\n"); > + return -ENOENT; > + } > + pcie->irq = i; > + > + err = rcar_pcie_clocks_get(pcie); > + if (err) { > + dev_err(&pdev->dev, "failed to get clocks: %d\n", err); > + return err; > + } > + > + pcie->base = devm_request_and_ioremap(&pdev->dev, res); > + if (!pcie->base) { > + dev_err(&pdev->dev, "failed to map PCIe registers: %d\n", err); > + err = -ENOMEM; > + goto err_map_reg; > + } > + > + return 0; > + > +err_map_reg: > + rcar_pcie_clocks_put(pcie); > + > + return err; > +} > + > +static struct platform_device_id rcar_pcie_id_table[] = { > + { "r8a7779-pcie", RCAR_H1 }, > + { "r8a7790-pcie", RCAR_GENERIC }, > + { "r8a7791-pcie", RCAR_GENERIC }, > + {}, > +}; > +MODULE_DEVICE_TABLE(platform, rcar_pcie_id_table); Really, are you still going to submit drivers without OF bindings? NAK! > +static int __init rcar_pcie_probe(struct platform_device *pdev) > +{ > + struct rcar_pcie *pcie; > + struct resource *res; > + unsigned int data; > + int i, err; > + > + pcie = devm_kzalloc(&pdev->dev, sizeof(*pcie), GFP_KERNEL); > + if (!pcie) > + return -ENOMEM; > + > + pcie->dev = &pdev->dev; > + pcie->chip = pdev->id_entry->driver_data; > + > + /* Get resources */ > + err = rcar_pcie_get_resources(pdev, pcie); > + if (err < 0) { > + dev_err(&pdev->dev, "failed to request resources: %d\n", err); > + return err; > + } > + > + /* Get the I/O and memory ranges */ > + for (i = 0; i < NR_PCI_RESOURCES; i++) { > + res = platform_get_resource(pdev, IORESOURCE_MEM, i+1); > + if (!res) { > + dev_err(&pdev->dev, "cannot get MEM%d resources\n", i); > + return -ENOENT; > + } > + pcie->res[i] = res; > + } > + > + rcar_pcie_hw_init(pcie); > + > + if (!pcie->haslink) { > + dev_info(&pdev->dev, "PCI: PCIe link down\n"); > + return 0; > + } > + > + data = pci_read_reg(pcie, MACSR); > + dev_info(&pdev->dev, "PCIe x%d: link up\n", (data >> 20) & 0x3f); > + > + rcar_pcie_enable(pcie); > + > + platform_set_drvdata(pdev, pcie); > + return 0; > +} > + > +static struct platform_driver rcar_pcie_driver = { > + .probe = rcar_pcie_probe, > + .driver = { > + .name = DRV_NAME, > + .owner = THIS_MODULE, > + }, > + .id_table = rcar_pcie_id_table, > +}; > + > +module_platform_driver(rcar_pcie_driver); > + > +MODULE_AUTHOR("Phil Edworthy <phil.edworthy@renesas.com>"); > +MODULE_DESCRIPTION("Renesas R-Car PCIe driver"); > +MODULE_LICENSE("GPLv2"); > diff --git a/drivers/pci/host/pcie-rcar.h b/drivers/pci/host/pcie-rcar.h > new file mode 100644 > index 0000000..a6b407f > --- /dev/null > +++ b/drivers/pci/host/pcie-rcar.h > @@ -0,0 +1,84 @@ > +/* > + * PCI Express definitions for Renesas R-Car SoCs > + */ > +#ifndef __PCI_RCAR_H > +#define __PCI_RCAR_H > + > +#define PCI_DEVICE_ID_RENESAS_RCAR 0x0018 should this go into pci ids header? > +#define PCIECAR 0x000010 > +#define PCIECCTLR 0x000018 > +#define CONFIG_SEND_ENABLE (1 << 31) > +#define TYPE0 (0 << 8) > +#define TYPE1 (1 << 8) > +#define PCIECDR 0x000020 > +#define PCIEMSR 0x000028 > +#define PCIEINTXR 0x000400 > +#define PCIEPHYSR 0x0007f0 > + > +/* Transfer control */ > +#define PCIETCTLR 0x02000 > +#define CFINIT 1 > +#define PCIETSTR 0x02004 > +#define DATA_LINK_ACTIVE 1 > +#define PCIEINTR 0x02008 > +#define PCIEINTER 0x0200c > +#define PCIEERRFR 0x02020 > +#define UNSUPPORTED_REQUEST (1 << 4) > +#define PCIEERRFER 0x02024 > +#define PCIEERRFR2 0x02028 > +#define PCIEPMSR 0x02034 > +#define PCIEPMSCIER 0x02038 > +#define PCIEMSIFR 0x02044 > + > +/* root port address */ > +#define PCIEPRAR(x) (0x02080 + ((x) * 0x4)) > + > +/* local address reg & mask */ > +#define PCIELAR(x) (0x02200 + ((x) * 0x20)) > +#define PCIELAMR(x) (0x02208 + ((x) * 0x20)) > +#define LAM_PMIOLAMnB3 (1 << 3) > +#define LAM_PMIOLAMnB2 (1 << 2) > +#define LAM_PREFETCH (1 << 3) > +#define LAM_64BIT (1 << 2) > +#define LAR_ENABLE (1 << 1) > + > +/* PCIe address reg & mask */ > +#define PCIEPARL(x) (0x03400 + ((x) * 0x20)) > +#define PCIEPARH(x) (0x03404 + ((x) * 0x20)) > +#define PCIEPAMR(x) (0x03408 + ((x) * 0x20)) > +#define PCIEPTCTLR(x) (0x0340c + ((x) * 0x20)) > +#define PAR_ENABLE (1 << 31) > +#define IO_SPACE (1 << 8) > + > +/* Configuration */ > +#define PCICONF(x) (0x010000 + ((x) * 0x4)) > +#define PMCAP(x) (0x010040 + ((x) * 0x4)) > +#define MSICAP(x) (0x010050 + ((x) * 0x4)) > +#define EXPCAP(x) (0x010070 + ((x) * 0x4)) > +#define VCCAP(x) (0x010100 + ((x) * 0x4)) > +#define SERNUMCAP(x) (0x0101b0 + ((x) * 0x4)) > + > +/* link layer */ > +#define IDSETR0 0x011000 > +#define IDSETR1 0x011004 > +#define SUBIDSETR 0x011024 > +#define DSERSETR0 0x01102c > +#define DSERSETR1 0x011030 > +#define TLCTLR 0x011048 > +#define MACSR 0x011054 > +#define MACCTLR 0x011058 > +#define SCRAMBLE_DISABLE (1 << 27) > + > +/* R-Car H1 PHY */ > +#define H1_PCIEPHYCTLR 0x040008 > +#define H1_PCIEPHYADRR 0x04000c > +#define WRITE_CMD (1 << 16) > +#define PHY_ACK (1 << 24) > +#define RATE_POS 12 > +#define LANE_POS 8 > +#define ADR_POS 0 > +#define H1_PCIEPHYDOUTR 0x040014 > +#define H1_PCIEPHYSR 0x040018 > + > +#endif /* __PCI_RCAR_H */ > -- Ben Dooks http://www.codethink.co.uk/ Senior Engineer Codethink - Providing Genius ^ permalink raw reply [flat|nested] 16+ messages in thread
* Re: [PATCH 1/5] PCI: host: Add Renesas R-Car PCIe driver @ 2014-02-26 16:34 ` Ben Dooks 0 siblings, 0 replies; 16+ messages in thread From: Ben Dooks @ 2014-02-26 16:34 UTC (permalink / raw) To: Phil Edworthy Cc: linux-pci, linux-sh, Bjorn Helgaas, Valentine Barshak, Simon Horman, Magnus Damm On 26/02/14 16:08, Phil Edworthy wrote: > This PCIe Host driver currently does not support MSI, so cards > fall back to INTx interrupts. > > Signed-off-by: Phil Edworthy <phil.edworthy@renesas.com> > + bool "Renesas R-Car PCIe controller" > + depends on ARM && (ARCH_R8A7790 || ARCH_R8A7791 || COMPILE_TEST) > + help > + Say Y here if you want PCIe controller support on R-Car Gen2 SoCs. > + > endmenu > diff --git a/drivers/pci/host/Makefile b/drivers/pci/host/Makefile > index 13fb333..19946f9 100644 > --- a/drivers/pci/host/Makefile > +++ b/drivers/pci/host/Makefile > @@ -4,3 +4,4 @@ obj-$(CONFIG_PCI_IMX6) += pci-imx6.o > obj-$(CONFIG_PCI_MVEBU) += pci-mvebu.o > 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 > diff --git a/drivers/pci/host/pcie-rcar.c b/drivers/pci/host/pcie-rcar.c > new file mode 100644 > index 0000000..d9a315f > --- /dev/null > +++ b/drivers/pci/host/pcie-rcar.c > @@ -0,0 +1,614 @@ > +/* > + * PCIe driver for Renesas R-Car SoCs > + * Copyright (C) 2013 Renesas Electronics Europe Ltd > + * > + * Based on: > + * arch/sh/drivers/pci/pcie-sh7786.c > + * arch/sh/drivers/pci/ops-sh7786.c > + * Copyright (C) 2009 - 2011 Paul Mundt > + * > + * 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/module.h> > +#include <linux/kernel.h> > +#include <linux/of_address.h> > +#include <linux/of_irq.h> > +#include <linux/of_pci.h> > +#include <linux/of_platform.h> > +#include <linux/pci.h> > +#include <linux/platform_device.h> > +#include <linux/slab.h> > +#include "pcie-rcar.h" > + > +#define DRV_NAME "rcar-pcie" > + > +enum chip_id { > + RCAR_GENERIC, > + RCAR_H1, > +}; > + > +#define RCONF(x) (PCICONF(0)+(x)) > +#define REXPCAP(x) (EXPCAP(0)+(x)) > +#define RVCCAP(x) (VCCAP(0)+(x)) > + > +#define PCIE_CONF_BUS(b) (((b) & 0xff) << 24) > +#define PCIE_CONF_DEV(d) (((d) & 0x1f) << 19) > +#define PCIE_CONF_FUNC(f) (((f) & 0x7) << 16) > + > +#define NR_PCI_RESOURCES 4 > + > +/* Structure representing the PCIe interface */ > +struct rcar_pcie { > + struct device *dev; > + void __iomem *base; > + int irq; > + struct clk *clk; > + struct resource *res[NR_PCI_RESOURCES]; > + int haslink; > + enum chip_id chip; > + u8 root_bus_nr; > +}; > + > +static inline struct rcar_pcie *sys_to_pcie(struct pci_sys_data *sys) > +{ > + return sys->private_data; > +} > + > +static void > +pci_write_reg(struct rcar_pcie *pcie, unsigned long val, unsigned long reg) > +{ > + writel(val, pcie->base + reg); > +} Do we really need wrappers like these? > +static unsigned long > +pci_read_reg(struct rcar_pcie *pcie, unsigned long reg) > +{ > + return readl(pcie->base + reg); > +} > + > +enum { > + PCI_ACCESS_READ, > + PCI_ACCESS_WRITE, > +}; > +static int rcar_pcie_map_irq(const struct pci_dev *dev, u8 slot, u8 pin) > +{ > + struct rcar_pcie *pcie = sys_to_pcie(dev->bus->sysdata); > + return pcie->irq; > +} > + > +static void __init rcar_pcie_enable(struct rcar_pcie *pcie) > +{ > + struct hw_pci hw; > + > + memset(&hw, 0, sizeof(hw)); > + > + hw.nr_controllers = 1; > + hw.private_data = (void **)&pcie; > + hw.setup = rcar_pcie_setup, > + hw.map_irq = rcar_pcie_map_irq, > + hw.ops = &rcar_pcie_ops, > + > + pci_common_init(&hw); > +} > + > +static int __init phy_wait_for_ack(struct rcar_pcie *pcie) > +{ > + unsigned int timeout = 100; > + > + while (timeout--) { > + if (pci_read_reg(pcie, H1_PCIEPHYADRR) & PHY_ACK) > + return 0; > + > + udelay(100); > + } > + > + dev_err(pcie->dev, "Access to PCIe phy timed out\n"); > + > + return -ETIMEDOUT; > +} Could this be done with some sort of sleep, instead of having to keep delaying? How long is the average wait for pci to finish a write? > + > +static void __init phy_write_reg(struct rcar_pcie *pcie, > + unsigned int rate, unsigned int addr, > + unsigned int lane, unsigned int data) > +{ > + unsigned long phyaddr; > + > + phyaddr = WRITE_CMD | > + ((rate & 1) << RATE_POS) | > + ((lane & 0xf) << LANE_POS) | > + ((addr & 0xff) << ADR_POS); > + > + /* Set write data */ > + pci_write_reg(pcie, data, H1_PCIEPHYDOUTR); > + pci_write_reg(pcie, phyaddr, H1_PCIEPHYADRR); > + > + /* Ignore errors as they will be dealt with if the data link is down */ > + phy_wait_for_ack(pcie); > + > + /* Clear command */ > + pci_write_reg(pcie, 0, H1_PCIEPHYDOUTR); > + pci_write_reg(pcie, 0, H1_PCIEPHYADRR); > + > + /* Ignore errors as they will be dealt with if the data link is down */ > + phy_wait_for_ack(pcie); > +} > + > > +static int __init rcar_pcie_wait_for_dl(struct rcar_pcie *pcie) > +{ > + unsigned int timeout = 100; > + > + while (timeout--) { > + if ((pci_read_reg(pcie, PCIETSTR) & DATA_LINK_ACTIVE)) > + return 0; > + > + udelay(100); > + } > + > + return -ETIMEDOUT; > +} Same comments as for previous delay loop > +static void __init rcar_pcie_hw_init(struct rcar_pcie *pcie) > +{ > + /* Initialise R-Car H1 PHY & wait for it to be ready */ > + if (pcie->chip = RCAR_H1) > + if (rcar_pcie_phy_init_rcar_h1(pcie)) > + return; > + > + /* Begin initialization */ > + pci_write_reg(pcie, 0, PCIETCTLR); > + > + /* Set mode */ > + pci_write_reg(pcie, 1, PCIEMSR); > + > + /* > + * For target transfers, setup a single 64-bit 4GB mapping at address > + * 0. The hardware can only map memory that starts on a power of two > + * boundary, and size is power of 2, so best to ignore it. > + */ > + pci_write_reg(pcie, 0, PCIEPRAR(0)); > + pci_write_reg(pcie, 0, PCIELAR(0)); > + pci_write_reg(pcie, 0xfffffff0UL | LAM_PREFETCH | > + LAM_64BIT | LAR_ENABLE, PCIELAMR(0)); > + pci_write_reg(pcie, 0, PCIELAR(1)); > + pci_write_reg(pcie, 0, PCIEPRAR(1)); > + pci_write_reg(pcie, 0, PCIELAMR(1)); > + > + /* > + * Initial header for port config space is type 1, set the device > + * class to match. Hardware takes care of propagating the IDSETR > + * settings, so there is no need to bother with a quirk. > + */ > + pci_write_reg(pcie, PCI_CLASS_BRIDGE_PCI << 16, IDSETR1); > + > + /* > + * Setup Secondary Bus Number & Subordinate Bus Number, even though > + * they aren't used, to avoid bridge being detected as broken. > + */ > + rcar_rmw32(pcie, RCONF(PCI_SECONDARY_BUS), 0xff, 1); > + rcar_rmw32(pcie, RCONF(PCI_SUBORDINATE_BUS), 0xff, 1); > + > + /* Initialize default capabilities. */ > + rcar_rmw32(pcie, REXPCAP(0), 0, PCI_CAP_ID_EXP); > + rcar_rmw32(pcie, REXPCAP(PCI_EXP_FLAGS), > + PCI_EXP_FLAGS_TYPE, PCI_EXP_TYPE_ROOT_PORT << 4); > + rcar_rmw32(pcie, RCONF(PCI_HEADER_TYPE), 0x7f, > + PCI_HEADER_TYPE_BRIDGE); > + > + /* Enable data link layer active state reporting */ > + rcar_rmw32(pcie, REXPCAP(PCI_EXP_LNKCAP), 0, PCI_EXP_LNKCAP_DLLLARC); > + > + /* Write out the physical slot number = 0 */ > + rcar_rmw32(pcie, REXPCAP(PCI_EXP_SLTCAP), PCI_EXP_SLTCAP_PSN, 0); > + > + /* Set the completion timer timeout to the maximum 50ms. */ > + rcar_rmw32(pcie, TLCTLR+1, 0x3f, 50); > + > + /* Terminate list of capabilities (Next Capability Offset=0) */ > + rcar_rmw32(pcie, RVCCAP(0), 0xfff0, 0); > + > + /* Enable MAC data scrambling. */ > + rcar_rmw32(pcie, MACCTLR, SCRAMBLE_DISABLE, 0); > + > + /* Finish initialization - establish a PCI Express link */ > + pci_write_reg(pcie, CFINIT, PCIETCTLR); > + > + /* This will timeout if we don't have a link. */ > + pcie->haslink = !rcar_pcie_wait_for_dl(pcie); > + > + /* Enable INTx interrupts */ > + rcar_rmw32(pcie, RCONF(PCI_INTERRUPT_LINE), 0xff, 0); > + rcar_rmw32(pcie, RCONF(PCI_INTERRUPT_PIN), 0xff, 1); > + rcar_rmw32(pcie, PCIEINTXR, 0, 0xF << 8); > + > + /* Enable slave Bus Mastering */ > + rcar_rmw32(pcie, RCONF(PCI_STATUS), PCI_STATUS_DEVSEL_MASK, > + PCI_COMMAND_IO | PCI_COMMAND_MEMORY | PCI_COMMAND_MASTER | > + PCI_STATUS_CAP_LIST | PCI_STATUS_DEVSEL_FAST); > + > + wmb(); > +} > + > +static int rcar_pcie_clocks_get(struct rcar_pcie *pcie) > +{ > + int err = 0; > + > + pcie->clk = clk_get(pcie->dev, NULL); > + if (IS_ERR(pcie->clk)) > + err = PTR_ERR(pcie->clk); > + else > + clk_enable(pcie->clk); > + > + return err; > +} Shouldn't you be using pm_runtime for this? > +static void rcar_pcie_clocks_put(struct rcar_pcie *pcie) > +{ > + clk_put(pcie->clk); > +} > + > +struct rcar_pcie_errs { > + int bit; > + const char *description; > +}; > + > +static int __init rcar_pcie_get_resources(struct platform_device *pdev, > + struct rcar_pcie *pcie) > +{ > + struct resource *res; > + int i; > + int err; > + > + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); > + > + i = platform_get_irq(pdev, 0); > + if (!res || i < 0) { > + dev_err(pcie->dev, "cannot get platform resources\n"); > + return -ENOENT; > + } > + pcie->irq = i; > + > + err = rcar_pcie_clocks_get(pcie); > + if (err) { > + dev_err(&pdev->dev, "failed to get clocks: %d\n", err); > + return err; > + } > + > + pcie->base = devm_request_and_ioremap(&pdev->dev, res); > + if (!pcie->base) { > + dev_err(&pdev->dev, "failed to map PCIe registers: %d\n", err); > + err = -ENOMEM; > + goto err_map_reg; > + } > + > + return 0; > + > +err_map_reg: > + rcar_pcie_clocks_put(pcie); > + > + return err; > +} > + > +static struct platform_device_id rcar_pcie_id_table[] = { > + { "r8a7779-pcie", RCAR_H1 }, > + { "r8a7790-pcie", RCAR_GENERIC }, > + { "r8a7791-pcie", RCAR_GENERIC }, > + {}, > +}; > +MODULE_DEVICE_TABLE(platform, rcar_pcie_id_table); Really, are you still going to submit drivers without OF bindings? NAK! > +static int __init rcar_pcie_probe(struct platform_device *pdev) > +{ > + struct rcar_pcie *pcie; > + struct resource *res; > + unsigned int data; > + int i, err; > + > + pcie = devm_kzalloc(&pdev->dev, sizeof(*pcie), GFP_KERNEL); > + if (!pcie) > + return -ENOMEM; > + > + pcie->dev = &pdev->dev; > + pcie->chip = pdev->id_entry->driver_data; > + > + /* Get resources */ > + err = rcar_pcie_get_resources(pdev, pcie); > + if (err < 0) { > + dev_err(&pdev->dev, "failed to request resources: %d\n", err); > + return err; > + } > + > + /* Get the I/O and memory ranges */ > + for (i = 0; i < NR_PCI_RESOURCES; i++) { > + res = platform_get_resource(pdev, IORESOURCE_MEM, i+1); > + if (!res) { > + dev_err(&pdev->dev, "cannot get MEM%d resources\n", i); > + return -ENOENT; > + } > + pcie->res[i] = res; > + } > + > + rcar_pcie_hw_init(pcie); > + > + if (!pcie->haslink) { > + dev_info(&pdev->dev, "PCI: PCIe link down\n"); > + return 0; > + } > + > + data = pci_read_reg(pcie, MACSR); > + dev_info(&pdev->dev, "PCIe x%d: link up\n", (data >> 20) & 0x3f); > + > + rcar_pcie_enable(pcie); > + > + platform_set_drvdata(pdev, pcie); > + return 0; > +} > + > +static struct platform_driver rcar_pcie_driver = { > + .probe = rcar_pcie_probe, > + .driver = { > + .name = DRV_NAME, > + .owner = THIS_MODULE, > + }, > + .id_table = rcar_pcie_id_table, > +}; > + > +module_platform_driver(rcar_pcie_driver); > + > +MODULE_AUTHOR("Phil Edworthy <phil.edworthy@renesas.com>"); > +MODULE_DESCRIPTION("Renesas R-Car PCIe driver"); > +MODULE_LICENSE("GPLv2"); > diff --git a/drivers/pci/host/pcie-rcar.h b/drivers/pci/host/pcie-rcar.h > new file mode 100644 > index 0000000..a6b407f > --- /dev/null > +++ b/drivers/pci/host/pcie-rcar.h > @@ -0,0 +1,84 @@ > +/* > + * PCI Express definitions for Renesas R-Car SoCs > + */ > +#ifndef __PCI_RCAR_H > +#define __PCI_RCAR_H > + > +#define PCI_DEVICE_ID_RENESAS_RCAR 0x0018 should this go into pci ids header? > +#define PCIECAR 0x000010 > +#define PCIECCTLR 0x000018 > +#define CONFIG_SEND_ENABLE (1 << 31) > +#define TYPE0 (0 << 8) > +#define TYPE1 (1 << 8) > +#define PCIECDR 0x000020 > +#define PCIEMSR 0x000028 > +#define PCIEINTXR 0x000400 > +#define PCIEPHYSR 0x0007f0 > + > +/* Transfer control */ > +#define PCIETCTLR 0x02000 > +#define CFINIT 1 > +#define PCIETSTR 0x02004 > +#define DATA_LINK_ACTIVE 1 > +#define PCIEINTR 0x02008 > +#define PCIEINTER 0x0200c > +#define PCIEERRFR 0x02020 > +#define UNSUPPORTED_REQUEST (1 << 4) > +#define PCIEERRFER 0x02024 > +#define PCIEERRFR2 0x02028 > +#define PCIEPMSR 0x02034 > +#define PCIEPMSCIER 0x02038 > +#define PCIEMSIFR 0x02044 > + > +/* root port address */ > +#define PCIEPRAR(x) (0x02080 + ((x) * 0x4)) > + > +/* local address reg & mask */ > +#define PCIELAR(x) (0x02200 + ((x) * 0x20)) > +#define PCIELAMR(x) (0x02208 + ((x) * 0x20)) > +#define LAM_PMIOLAMnB3 (1 << 3) > +#define LAM_PMIOLAMnB2 (1 << 2) > +#define LAM_PREFETCH (1 << 3) > +#define LAM_64BIT (1 << 2) > +#define LAR_ENABLE (1 << 1) > + > +/* PCIe address reg & mask */ > +#define PCIEPARL(x) (0x03400 + ((x) * 0x20)) > +#define PCIEPARH(x) (0x03404 + ((x) * 0x20)) > +#define PCIEPAMR(x) (0x03408 + ((x) * 0x20)) > +#define PCIEPTCTLR(x) (0x0340c + ((x) * 0x20)) > +#define PAR_ENABLE (1 << 31) > +#define IO_SPACE (1 << 8) > + > +/* Configuration */ > +#define PCICONF(x) (0x010000 + ((x) * 0x4)) > +#define PMCAP(x) (0x010040 + ((x) * 0x4)) > +#define MSICAP(x) (0x010050 + ((x) * 0x4)) > +#define EXPCAP(x) (0x010070 + ((x) * 0x4)) > +#define VCCAP(x) (0x010100 + ((x) * 0x4)) > +#define SERNUMCAP(x) (0x0101b0 + ((x) * 0x4)) > + > +/* link layer */ > +#define IDSETR0 0x011000 > +#define IDSETR1 0x011004 > +#define SUBIDSETR 0x011024 > +#define DSERSETR0 0x01102c > +#define DSERSETR1 0x011030 > +#define TLCTLR 0x011048 > +#define MACSR 0x011054 > +#define MACCTLR 0x011058 > +#define SCRAMBLE_DISABLE (1 << 27) > + > +/* R-Car H1 PHY */ > +#define H1_PCIEPHYCTLR 0x040008 > +#define H1_PCIEPHYADRR 0x04000c > +#define WRITE_CMD (1 << 16) > +#define PHY_ACK (1 << 24) > +#define RATE_POS 12 > +#define LANE_POS 8 > +#define ADR_POS 0 > +#define H1_PCIEPHYDOUTR 0x040014 > +#define H1_PCIEPHYSR 0x040018 > + > +#endif /* __PCI_RCAR_H */ > -- Ben Dooks http://www.codethink.co.uk/ Senior Engineer Codethink - Providing Genius ^ permalink raw reply [flat|nested] 16+ messages in thread
* Re: [PATCH 1/5] PCI: host: Add Renesas R-Car PCIe driver 2014-02-26 16:34 ` Ben Dooks @ 2014-02-26 17:13 ` Phil.Edworthy -1 siblings, 0 replies; 16+ messages in thread From: Phil.Edworthy @ 2014-02-26 17:13 UTC (permalink / raw) To: Ben Dooks Cc: Bjorn Helgaas, Simon Horman, linux-pci, linux-sh, Magnus Damm, Valentine Barshak Hi Ben, Thanks for your comments. On 26/02/2014 16:33, Ben Dooks wrote: > On 26/02/14 16:08, Phil Edworthy wrote: > > This PCIe Host driver currently does not support MSI, so cards > > fall back to INTx interrupts. > > > > Signed-off-by: Phil Edworthy <phil.edworthy@renesas.com> > > > + bool "Renesas R-Car PCIe controller" > > + depends on ARM && (ARCH_R8A7790 || ARCH_R8A7791 || COMPILE_TEST) > > + help > > + Say Y here if you want PCIe controller support on R-Car Gen2 SoCs. > > + > > endmenu > > diff --git a/drivers/pci/host/Makefile b/drivers/pci/host/Makefile > > index 13fb333..19946f9 100644 > > --- a/drivers/pci/host/Makefile > > +++ b/drivers/pci/host/Makefile > > @@ -4,3 +4,4 @@ obj-$(CONFIG_PCI_IMX6) += pci-imx6.o > > obj-$(CONFIG_PCI_MVEBU) += pci-mvebu.o > > 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 > > diff --git a/drivers/pci/host/pcie-rcar.c b/drivers/pci/host/pcie-rcar.c > > new file mode 100644 > > index 0000000..d9a315f > > --- /dev/null > > +++ b/drivers/pci/host/pcie-rcar.c > > @@ -0,0 +1,614 @@ > > +/* > > + * PCIe driver for Renesas R-Car SoCs > > + * Copyright (C) 2013 Renesas Electronics Europe Ltd > > + * > > + * Based on: > > + * arch/sh/drivers/pci/pcie-sh7786.c > > + * arch/sh/drivers/pci/ops-sh7786.c > > + * Copyright (C) 2009 - 2011 Paul Mundt > > + * > > + * 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/module.h> > > +#include <linux/kernel.h> > > +#include <linux/of_address.h> > > +#include <linux/of_irq.h> > > +#include <linux/of_pci.h> > > +#include <linux/of_platform.h> > > +#include <linux/pci.h> > > +#include <linux/platform_device.h> > > +#include <linux/slab.h> > > +#include "pcie-rcar.h" > > + > > +#define DRV_NAME "rcar-pcie" > > + > > +enum chip_id { > > + RCAR_GENERIC, > > + RCAR_H1, > > +}; > > + > > +#define RCONF(x) (PCICONF(0)+(x)) > > +#define REXPCAP(x) (EXPCAP(0)+(x)) > > +#define RVCCAP(x) (VCCAP(0)+(x)) > > + > > +#define PCIE_CONF_BUS(b) (((b) & 0xff) << 24) > > +#define PCIE_CONF_DEV(d) (((d) & 0x1f) << 19) > > +#define PCIE_CONF_FUNC(f) (((f) & 0x7) << 16) > > + > > +#define NR_PCI_RESOURCES 4 > > + > > +/* Structure representing the PCIe interface */ > > +struct rcar_pcie { > > + struct device *dev; > > + void __iomem *base; > > + int irq; > > + struct clk *clk; > > + struct resource *res[NR_PCI_RESOURCES]; > > + int haslink; > > + enum chip_id chip; > > + u8 root_bus_nr; > > +}; > > + > > +static inline struct rcar_pcie *sys_to_pcie(struct pci_sys_data *sys) > > +{ > > + return sys->private_data; > > +} > > + > > +static void > > +pci_write_reg(struct rcar_pcie *pcie, unsigned long val, unsigned long reg) > > +{ > > + writel(val, pcie->base + reg); > > +} > > Do we really need wrappers like these? They were handy during development, but I guess not. > > +static unsigned long > > +pci_read_reg(struct rcar_pcie *pcie, unsigned long reg) > > +{ > > + return readl(pcie->base + reg); > > +} > > + > > +enum { > > + PCI_ACCESS_READ, > > + PCI_ACCESS_WRITE, > > +}; > > > +static int rcar_pcie_map_irq(const struct pci_dev *dev, u8 slot, u8 pin) > > +{ > > + struct rcar_pcie *pcie = sys_to_pcie(dev->bus->sysdata); > > + return pcie->irq; > > +} > > + > > +static void __init rcar_pcie_enable(struct rcar_pcie *pcie) > > +{ > > + struct hw_pci hw; > > + > > + memset(&hw, 0, sizeof(hw)); > > + > > + hw.nr_controllers = 1; > > + hw.private_data = (void **)&pcie; > > + hw.setup = rcar_pcie_setup, > > + hw.map_irq = rcar_pcie_map_irq, > > + hw.ops = &rcar_pcie_ops, > > + > > + pci_common_init(&hw); > > +} > > + > > +static int __init phy_wait_for_ack(struct rcar_pcie *pcie) > > +{ > > + unsigned int timeout = 100; > > + > > + while (timeout--) { > > + if (pci_read_reg(pcie, H1_PCIEPHYADRR) & PHY_ACK) > > + return 0; > > + > > + udelay(100); > > + } > > + > > + dev_err(pcie->dev, "Access to PCIe phy timed out\n"); > > + > > + return -ETIMEDOUT; > > +} > > Could this be done with some sort of sleep, instead of having > to keep delaying? How long is the average wait for pci to finish > a write? I haven't measured how long, but yes, I can use mdelay instead. > > + > > +static void __init phy_write_reg(struct rcar_pcie *pcie, > > + unsigned int rate, unsigned int addr, > > + unsigned int lane, unsigned int data) > > +{ > > + unsigned long phyaddr; > > + > > + phyaddr = WRITE_CMD | > > + ((rate & 1) << RATE_POS) | > > + ((lane & 0xf) << LANE_POS) | > > + ((addr & 0xff) << ADR_POS); > > + > > + /* Set write data */ > > + pci_write_reg(pcie, data, H1_PCIEPHYDOUTR); > > + pci_write_reg(pcie, phyaddr, H1_PCIEPHYADRR); > > + > > + /* Ignore errors as they will be dealt with if the data link is down */ > > + phy_wait_for_ack(pcie); > > + > > + /* Clear command */ > > + pci_write_reg(pcie, 0, H1_PCIEPHYDOUTR); > > + pci_write_reg(pcie, 0, H1_PCIEPHYADRR); > > + > > + /* Ignore errors as they will be dealt with if the data link is down */ > > + phy_wait_for_ack(pcie); > > +} > > + > > > > > +static int __init rcar_pcie_wait_for_dl(struct rcar_pcie *pcie) > > +{ > > + unsigned int timeout = 100; > > + > > + while (timeout--) { > > + if ((pci_read_reg(pcie, PCIETSTR) & DATA_LINK_ACTIVE)) > > + return 0; > > + > > + udelay(100); > > + } > > + > > + return -ETIMEDOUT; > > +} > > Same comments as for previous delay loop > > > > +static void __init rcar_pcie_hw_init(struct rcar_pcie *pcie) > > +{ > > + /* Initialise R-Car H1 PHY & wait for it to be ready */ > > + if (pcie->chip == RCAR_H1) > > + if (rcar_pcie_phy_init_rcar_h1(pcie)) > > + return; > > + > > + /* Begin initialization */ > > + pci_write_reg(pcie, 0, PCIETCTLR); > > + > > + /* Set mode */ > > + pci_write_reg(pcie, 1, PCIEMSR); > > + > > + /* > > + * For target transfers, setup a single 64-bit 4GB mapping at address > > + * 0. The hardware can only map memory that starts on a power of two > > + * boundary, and size is power of 2, so best to ignore it. > > + */ > > + pci_write_reg(pcie, 0, PCIEPRAR(0)); > > + pci_write_reg(pcie, 0, PCIELAR(0)); > > + pci_write_reg(pcie, 0xfffffff0UL | LAM_PREFETCH | > > + LAM_64BIT | LAR_ENABLE, PCIELAMR(0)); > > + pci_write_reg(pcie, 0, PCIELAR(1)); > > + pci_write_reg(pcie, 0, PCIEPRAR(1)); > > + pci_write_reg(pcie, 0, PCIELAMR(1)); > > + > > + /* > > + * Initial header for port config space is type 1, set the device > > + * class to match. Hardware takes care of propagating the IDSETR > > + * settings, so there is no need to bother with a quirk. > > + */ > > + pci_write_reg(pcie, PCI_CLASS_BRIDGE_PCI << 16, IDSETR1); > > + > > + /* > > + * Setup Secondary Bus Number & Subordinate Bus Number, even though > > + * they aren't used, to avoid bridge being detected as broken. > > + */ > > + rcar_rmw32(pcie, RCONF(PCI_SECONDARY_BUS), 0xff, 1); > > + rcar_rmw32(pcie, RCONF(PCI_SUBORDINATE_BUS), 0xff, 1); > > + > > + /* Initialize default capabilities. */ > > + rcar_rmw32(pcie, REXPCAP(0), 0, PCI_CAP_ID_EXP); > > + rcar_rmw32(pcie, REXPCAP(PCI_EXP_FLAGS), > > + PCI_EXP_FLAGS_TYPE, PCI_EXP_TYPE_ROOT_PORT << 4); > > + rcar_rmw32(pcie, RCONF(PCI_HEADER_TYPE), 0x7f, > > + PCI_HEADER_TYPE_BRIDGE); > > + > > + /* Enable data link layer active state reporting */ > > + rcar_rmw32(pcie, REXPCAP(PCI_EXP_LNKCAP), 0, PCI_EXP_LNKCAP_DLLLARC); > > + > > + /* Write out the physical slot number = 0 */ > > + rcar_rmw32(pcie, REXPCAP(PCI_EXP_SLTCAP), PCI_EXP_SLTCAP_PSN, 0); > > + > > + /* Set the completion timer timeout to the maximum 50ms. */ > > + rcar_rmw32(pcie, TLCTLR+1, 0x3f, 50); > > + > > + /* Terminate list of capabilities (Next Capability Offset=0) */ > > + rcar_rmw32(pcie, RVCCAP(0), 0xfff0, 0); > > + > > + /* Enable MAC data scrambling. */ > > + rcar_rmw32(pcie, MACCTLR, SCRAMBLE_DISABLE, 0); > > + > > + /* Finish initialization - establish a PCI Express link */ > > + pci_write_reg(pcie, CFINIT, PCIETCTLR); > > + > > + /* This will timeout if we don't have a link. */ > > + pcie->haslink = !rcar_pcie_wait_for_dl(pcie); > > + > > + /* Enable INTx interrupts */ > > + rcar_rmw32(pcie, RCONF(PCI_INTERRUPT_LINE), 0xff, 0); > > + rcar_rmw32(pcie, RCONF(PCI_INTERRUPT_PIN), 0xff, 1); > > + rcar_rmw32(pcie, PCIEINTXR, 0, 0xF << 8); > > + > > + /* Enable slave Bus Mastering */ > > + rcar_rmw32(pcie, RCONF(PCI_STATUS), PCI_STATUS_DEVSEL_MASK, > > + PCI_COMMAND_IO | PCI_COMMAND_MEMORY | PCI_COMMAND_MASTER | > > + PCI_STATUS_CAP_LIST | PCI_STATUS_DEVSEL_FAST); > > + > > + wmb(); > > +} > > + > > +static int rcar_pcie_clocks_get(struct rcar_pcie *pcie) > > +{ > > + int err = 0; > > + > > + pcie->clk = clk_get(pcie->dev, NULL); > > + if (IS_ERR(pcie->clk)) > > + err = PTR_ERR(pcie->clk); > > + else > > + clk_enable(pcie->clk); > > + > > + return err; > > +} > > Shouldn't you be using pm_runtime for this? Yes, I'll fix this > > +static void rcar_pcie_clocks_put(struct rcar_pcie *pcie) > > +{ > > + clk_put(pcie->clk); > > +} > > + > > +struct rcar_pcie_errs { > > + int bit; > > + const char *description; > > +}; > > + > > +static int __init rcar_pcie_get_resources(struct platform_device *pdev, > > + struct rcar_pcie *pcie) > > +{ > > + struct resource *res; > > + int i; > > + int err; > > + > > + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); > > + > > + i = platform_get_irq(pdev, 0); > > + if (!res || i < 0) { > > + dev_err(pcie->dev, "cannot get platform resources\n"); > > + return -ENOENT; > > + } > > + pcie->irq = i; > > + > > + err = rcar_pcie_clocks_get(pcie); > > + if (err) { > > + dev_err(&pdev->dev, "failed to get clocks: %d\n", err); > > + return err; > > + } > > + > > + pcie->base = devm_request_and_ioremap(&pdev->dev, res); > > + if (!pcie->base) { > > + dev_err(&pdev->dev, "failed to map PCIe registers: %d\n", err); > > + err = -ENOMEM; > > + goto err_map_reg; > > + } > > + > > + return 0; > > + > > +err_map_reg: > > + rcar_pcie_clocks_put(pcie); > > + > > + return err; > > +} > > + > > +static struct platform_device_id rcar_pcie_id_table[] = { > > + { "r8a7779-pcie", RCAR_H1 }, > > + { "r8a7790-pcie", RCAR_GENERIC }, > > + { "r8a7791-pcie", RCAR_GENERIC }, > > + {}, > > +}; > > +MODULE_DEVICE_TABLE(platform, rcar_pcie_id_table); > > Really, are you still going to submit drivers without OF bindings? > > NAK! I totally agree, hence my comment in the cover email: "This is RFC as there is some work required for DT support." Perhaps I should have marked each patch as RFC. > > +static int __init rcar_pcie_probe(struct platform_device *pdev) > > +{ > > + struct rcar_pcie *pcie; > > + struct resource *res; > > + unsigned int data; > > + int i, err; > > + > > + pcie = devm_kzalloc(&pdev->dev, sizeof(*pcie), GFP_KERNEL); > > + if (!pcie) > > + return -ENOMEM; > > + > > + pcie->dev = &pdev->dev; > > + pcie->chip = pdev->id_entry->driver_data; > > + > > + /* Get resources */ > > + err = rcar_pcie_get_resources(pdev, pcie); > > + if (err < 0) { > > + dev_err(&pdev->dev, "failed to request resources: %d\n", err); > > + return err; > > + } > > + > > + /* Get the I/O and memory ranges */ > > + for (i = 0; i < NR_PCI_RESOURCES; i++) { > > + res = platform_get_resource(pdev, IORESOURCE_MEM, i+1); > > + if (!res) { > > + dev_err(&pdev->dev, "cannot get MEM%d resources\n", i); > > + return -ENOENT; > > + } > > + pcie->res[i] = res; > > + } > > + > > + rcar_pcie_hw_init(pcie); > > + > > + if (!pcie->haslink) { > > + dev_info(&pdev->dev, "PCI: PCIe link down\n"); > > + return 0; > > + } > > + > > + data = pci_read_reg(pcie, MACSR); > > + dev_info(&pdev->dev, "PCIe x%d: link up\n", (data >> 20) & 0x3f); > > + > > + rcar_pcie_enable(pcie); > > + > > + platform_set_drvdata(pdev, pcie); > > + return 0; > > +} > > + > > +static struct platform_driver rcar_pcie_driver = { > > + .probe = rcar_pcie_probe, > > + .driver = { > > + .name = DRV_NAME, > > + .owner = THIS_MODULE, > > + }, > > + .id_table = rcar_pcie_id_table, > > +}; > > + > > +module_platform_driver(rcar_pcie_driver); > > + > > +MODULE_AUTHOR("Phil Edworthy <phil.edworthy@renesas.com>"); > > +MODULE_DESCRIPTION("Renesas R-Car PCIe driver"); > > +MODULE_LICENSE("GPLv2"); > > diff --git a/drivers/pci/host/pcie-rcar.h b/drivers/pci/host/pcie-rcar.h > > new file mode 100644 > > index 0000000..a6b407f > > --- /dev/null > > +++ b/drivers/pci/host/pcie-rcar.h > > @@ -0,0 +1,84 @@ > > +/* > > + * PCI Express definitions for Renesas R-Car SoCs > > + */ > > +#ifndef __PCI_RCAR_H > > +#define __PCI_RCAR_H > > + > > +#define PCI_DEVICE_ID_RENESAS_RCAR 0x0018 > > should this go into pci ids header? This is not used so I'll remove it. > > +#define PCIECAR 0x000010 > > +#define PCIECCTLR 0x000018 > > +#define CONFIG_SEND_ENABLE (1 << 31) > > +#define TYPE0 (0 << 8) > > +#define TYPE1 (1 << 8) > > +#define PCIECDR 0x000020 > > +#define PCIEMSR 0x000028 > > +#define PCIEINTXR 0x000400 > > +#define PCIEPHYSR 0x0007f0 > > + > > +/* Transfer control */ > > +#define PCIETCTLR 0x02000 > > +#define CFINIT 1 > > +#define PCIETSTR 0x02004 > > +#define DATA_LINK_ACTIVE 1 > > +#define PCIEINTR 0x02008 > > +#define PCIEINTER 0x0200c > > +#define PCIEERRFR 0x02020 > > +#define UNSUPPORTED_REQUEST (1 << 4) > > +#define PCIEERRFER 0x02024 > > +#define PCIEERRFR2 0x02028 > > +#define PCIEPMSR 0x02034 > > +#define PCIEPMSCIER 0x02038 > > +#define PCIEMSIFR 0x02044 > > + > > +/* root port address */ > > +#define PCIEPRAR(x) (0x02080 + ((x) * 0x4)) > > + > > +/* local address reg & mask */ > > +#define PCIELAR(x) (0x02200 + ((x) * 0x20)) > > +#define PCIELAMR(x) (0x02208 + ((x) * 0x20)) > > +#define LAM_PMIOLAMnB3 (1 << 3) > > +#define LAM_PMIOLAMnB2 (1 << 2) > > +#define LAM_PREFETCH (1 << 3) > > +#define LAM_64BIT (1 << 2) > > +#define LAR_ENABLE (1 << 1) > > + > > +/* PCIe address reg & mask */ > > +#define PCIEPARL(x) (0x03400 + ((x) * 0x20)) > > +#define PCIEPARH(x) (0x03404 + ((x) * 0x20)) > > +#define PCIEPAMR(x) (0x03408 + ((x) * 0x20)) > > +#define PCIEPTCTLR(x) (0x0340c + ((x) * 0x20)) > > +#define PAR_ENABLE (1 << 31) > > +#define IO_SPACE (1 << 8) > > + > > +/* Configuration */ > > +#define PCICONF(x) (0x010000 + ((x) * 0x4)) > > +#define PMCAP(x) (0x010040 + ((x) * 0x4)) > > +#define MSICAP(x) (0x010050 + ((x) * 0x4)) > > +#define EXPCAP(x) (0x010070 + ((x) * 0x4)) > > +#define VCCAP(x) (0x010100 + ((x) * 0x4)) > > +#define SERNUMCAP(x) (0x0101b0 + ((x) * 0x4)) > > + > > +/* link layer */ > > +#define IDSETR0 0x011000 > > +#define IDSETR1 0x011004 > > +#define SUBIDSETR 0x011024 > > +#define DSERSETR0 0x01102c > > +#define DSERSETR1 0x011030 > > +#define TLCTLR 0x011048 > > +#define MACSR 0x011054 > > +#define MACCTLR 0x011058 > > +#define SCRAMBLE_DISABLE (1 << 27) > > + > > +/* R-Car H1 PHY */ > > +#define H1_PCIEPHYCTLR 0x040008 > > +#define H1_PCIEPHYADRR 0x04000c > > +#define WRITE_CMD (1 << 16) > > +#define PHY_ACK (1 << 24) > > +#define RATE_POS 12 > > +#define LANE_POS 8 > > +#define ADR_POS 0 > > +#define H1_PCIEPHYDOUTR 0x040014 > > +#define H1_PCIEPHYSR 0x040018 > > + > > +#endif /* __PCI_RCAR_H */ > > > > > -- > Ben Dooks http://www.codethink.co.uk/ > Senior Engineer Codethink - Providing Genius Thanks Phil ^ permalink raw reply [flat|nested] 16+ messages in thread
* Re: [PATCH 1/5] PCI: host: Add Renesas R-Car PCIe driver @ 2014-02-26 17:13 ` Phil.Edworthy 0 siblings, 0 replies; 16+ messages in thread From: Phil.Edworthy @ 2014-02-26 17:13 UTC (permalink / raw) To: Ben Dooks Cc: Bjorn Helgaas, Simon Horman, linux-pci, linux-sh, Magnus Damm, Valentine Barshak Hi Ben, Thanks for your comments. On 26/02/2014 16:33, Ben Dooks wrote: > On 26/02/14 16:08, Phil Edworthy wrote: > > This PCIe Host driver currently does not support MSI, so cards > > fall back to INTx interrupts. > > > > Signed-off-by: Phil Edworthy <phil.edworthy@renesas.com> > > > + bool "Renesas R-Car PCIe controller" > > + depends on ARM && (ARCH_R8A7790 || ARCH_R8A7791 || COMPILE_TEST) > > + help > > + Say Y here if you want PCIe controller support on R-Car Gen2 SoCs. > > + > > endmenu > > diff --git a/drivers/pci/host/Makefile b/drivers/pci/host/Makefile > > index 13fb333..19946f9 100644 > > --- a/drivers/pci/host/Makefile > > +++ b/drivers/pci/host/Makefile > > @@ -4,3 +4,4 @@ obj-$(CONFIG_PCI_IMX6) += pci-imx6.o > > obj-$(CONFIG_PCI_MVEBU) += pci-mvebu.o > > 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 > > diff --git a/drivers/pci/host/pcie-rcar.c b/drivers/pci/host/pcie-rcar.c > > new file mode 100644 > > index 0000000..d9a315f > > --- /dev/null > > +++ b/drivers/pci/host/pcie-rcar.c > > @@ -0,0 +1,614 @@ > > +/* > > + * PCIe driver for Renesas R-Car SoCs > > + * Copyright (C) 2013 Renesas Electronics Europe Ltd > > + * > > + * Based on: > > + * arch/sh/drivers/pci/pcie-sh7786.c > > + * arch/sh/drivers/pci/ops-sh7786.c > > + * Copyright (C) 2009 - 2011 Paul Mundt > > + * > > + * 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/module.h> > > +#include <linux/kernel.h> > > +#include <linux/of_address.h> > > +#include <linux/of_irq.h> > > +#include <linux/of_pci.h> > > +#include <linux/of_platform.h> > > +#include <linux/pci.h> > > +#include <linux/platform_device.h> > > +#include <linux/slab.h> > > +#include "pcie-rcar.h" > > + > > +#define DRV_NAME "rcar-pcie" > > + > > +enum chip_id { > > + RCAR_GENERIC, > > + RCAR_H1, > > +}; > > + > > +#define RCONF(x) (PCICONF(0)+(x)) > > +#define REXPCAP(x) (EXPCAP(0)+(x)) > > +#define RVCCAP(x) (VCCAP(0)+(x)) > > + > > +#define PCIE_CONF_BUS(b) (((b) & 0xff) << 24) > > +#define PCIE_CONF_DEV(d) (((d) & 0x1f) << 19) > > +#define PCIE_CONF_FUNC(f) (((f) & 0x7) << 16) > > + > > +#define NR_PCI_RESOURCES 4 > > + > > +/* Structure representing the PCIe interface */ > > +struct rcar_pcie { > > + struct device *dev; > > + void __iomem *base; > > + int irq; > > + struct clk *clk; > > + struct resource *res[NR_PCI_RESOURCES]; > > + int haslink; > > + enum chip_id chip; > > + u8 root_bus_nr; > > +}; > > + > > +static inline struct rcar_pcie *sys_to_pcie(struct pci_sys_data *sys) > > +{ > > + return sys->private_data; > > +} > > + > > +static void > > +pci_write_reg(struct rcar_pcie *pcie, unsigned long val, unsigned long reg) > > +{ > > + writel(val, pcie->base + reg); > > +} > > Do we really need wrappers like these? They were handy during development, but I guess not. > > +static unsigned long > > +pci_read_reg(struct rcar_pcie *pcie, unsigned long reg) > > +{ > > + return readl(pcie->base + reg); > > +} > > + > > +enum { > > + PCI_ACCESS_READ, > > + PCI_ACCESS_WRITE, > > +}; > > > +static int rcar_pcie_map_irq(const struct pci_dev *dev, u8 slot, u8 pin) > > +{ > > + struct rcar_pcie *pcie = sys_to_pcie(dev->bus->sysdata); > > + return pcie->irq; > > +} > > + > > +static void __init rcar_pcie_enable(struct rcar_pcie *pcie) > > +{ > > + struct hw_pci hw; > > + > > + memset(&hw, 0, sizeof(hw)); > > + > > + hw.nr_controllers = 1; > > + hw.private_data = (void **)&pcie; > > + hw.setup = rcar_pcie_setup, > > + hw.map_irq = rcar_pcie_map_irq, > > + hw.ops = &rcar_pcie_ops, > > + > > + pci_common_init(&hw); > > +} > > + > > +static int __init phy_wait_for_ack(struct rcar_pcie *pcie) > > +{ > > + unsigned int timeout = 100; > > + > > + while (timeout--) { > > + if (pci_read_reg(pcie, H1_PCIEPHYADRR) & PHY_ACK) > > + return 0; > > + > > + udelay(100); > > + } > > + > > + dev_err(pcie->dev, "Access to PCIe phy timed out\n"); > > + > > + return -ETIMEDOUT; > > +} > > Could this be done with some sort of sleep, instead of having > to keep delaying? How long is the average wait for pci to finish > a write? I haven't measured how long, but yes, I can use mdelay instead. > > + > > +static void __init phy_write_reg(struct rcar_pcie *pcie, > > + unsigned int rate, unsigned int addr, > > + unsigned int lane, unsigned int data) > > +{ > > + unsigned long phyaddr; > > + > > + phyaddr = WRITE_CMD | > > + ((rate & 1) << RATE_POS) | > > + ((lane & 0xf) << LANE_POS) | > > + ((addr & 0xff) << ADR_POS); > > + > > + /* Set write data */ > > + pci_write_reg(pcie, data, H1_PCIEPHYDOUTR); > > + pci_write_reg(pcie, phyaddr, H1_PCIEPHYADRR); > > + > > + /* Ignore errors as they will be dealt with if the data link is down */ > > + phy_wait_for_ack(pcie); > > + > > + /* Clear command */ > > + pci_write_reg(pcie, 0, H1_PCIEPHYDOUTR); > > + pci_write_reg(pcie, 0, H1_PCIEPHYADRR); > > + > > + /* Ignore errors as they will be dealt with if the data link is down */ > > + phy_wait_for_ack(pcie); > > +} > > + > > > > > +static int __init rcar_pcie_wait_for_dl(struct rcar_pcie *pcie) > > +{ > > + unsigned int timeout = 100; > > + > > + while (timeout--) { > > + if ((pci_read_reg(pcie, PCIETSTR) & DATA_LINK_ACTIVE)) > > + return 0; > > + > > + udelay(100); > > + } > > + > > + return -ETIMEDOUT; > > +} > > Same comments as for previous delay loop > > > > +static void __init rcar_pcie_hw_init(struct rcar_pcie *pcie) > > +{ > > + /* Initialise R-Car H1 PHY & wait for it to be ready */ > > + if (pcie->chip = RCAR_H1) > > + if (rcar_pcie_phy_init_rcar_h1(pcie)) > > + return; > > + > > + /* Begin initialization */ > > + pci_write_reg(pcie, 0, PCIETCTLR); > > + > > + /* Set mode */ > > + pci_write_reg(pcie, 1, PCIEMSR); > > + > > + /* > > + * For target transfers, setup a single 64-bit 4GB mapping at address > > + * 0. The hardware can only map memory that starts on a power of two > > + * boundary, and size is power of 2, so best to ignore it. > > + */ > > + pci_write_reg(pcie, 0, PCIEPRAR(0)); > > + pci_write_reg(pcie, 0, PCIELAR(0)); > > + pci_write_reg(pcie, 0xfffffff0UL | LAM_PREFETCH | > > + LAM_64BIT | LAR_ENABLE, PCIELAMR(0)); > > + pci_write_reg(pcie, 0, PCIELAR(1)); > > + pci_write_reg(pcie, 0, PCIEPRAR(1)); > > + pci_write_reg(pcie, 0, PCIELAMR(1)); > > + > > + /* > > + * Initial header for port config space is type 1, set the device > > + * class to match. Hardware takes care of propagating the IDSETR > > + * settings, so there is no need to bother with a quirk. > > + */ > > + pci_write_reg(pcie, PCI_CLASS_BRIDGE_PCI << 16, IDSETR1); > > + > > + /* > > + * Setup Secondary Bus Number & Subordinate Bus Number, even though > > + * they aren't used, to avoid bridge being detected as broken. > > + */ > > + rcar_rmw32(pcie, RCONF(PCI_SECONDARY_BUS), 0xff, 1); > > + rcar_rmw32(pcie, RCONF(PCI_SUBORDINATE_BUS), 0xff, 1); > > + > > + /* Initialize default capabilities. */ > > + rcar_rmw32(pcie, REXPCAP(0), 0, PCI_CAP_ID_EXP); > > + rcar_rmw32(pcie, REXPCAP(PCI_EXP_FLAGS), > > + PCI_EXP_FLAGS_TYPE, PCI_EXP_TYPE_ROOT_PORT << 4); > > + rcar_rmw32(pcie, RCONF(PCI_HEADER_TYPE), 0x7f, > > + PCI_HEADER_TYPE_BRIDGE); > > + > > + /* Enable data link layer active state reporting */ > > + rcar_rmw32(pcie, REXPCAP(PCI_EXP_LNKCAP), 0, PCI_EXP_LNKCAP_DLLLARC); > > + > > + /* Write out the physical slot number = 0 */ > > + rcar_rmw32(pcie, REXPCAP(PCI_EXP_SLTCAP), PCI_EXP_SLTCAP_PSN, 0); > > + > > + /* Set the completion timer timeout to the maximum 50ms. */ > > + rcar_rmw32(pcie, TLCTLR+1, 0x3f, 50); > > + > > + /* Terminate list of capabilities (Next Capability Offset=0) */ > > + rcar_rmw32(pcie, RVCCAP(0), 0xfff0, 0); > > + > > + /* Enable MAC data scrambling. */ > > + rcar_rmw32(pcie, MACCTLR, SCRAMBLE_DISABLE, 0); > > + > > + /* Finish initialization - establish a PCI Express link */ > > + pci_write_reg(pcie, CFINIT, PCIETCTLR); > > + > > + /* This will timeout if we don't have a link. */ > > + pcie->haslink = !rcar_pcie_wait_for_dl(pcie); > > + > > + /* Enable INTx interrupts */ > > + rcar_rmw32(pcie, RCONF(PCI_INTERRUPT_LINE), 0xff, 0); > > + rcar_rmw32(pcie, RCONF(PCI_INTERRUPT_PIN), 0xff, 1); > > + rcar_rmw32(pcie, PCIEINTXR, 0, 0xF << 8); > > + > > + /* Enable slave Bus Mastering */ > > + rcar_rmw32(pcie, RCONF(PCI_STATUS), PCI_STATUS_DEVSEL_MASK, > > + PCI_COMMAND_IO | PCI_COMMAND_MEMORY | PCI_COMMAND_MASTER | > > + PCI_STATUS_CAP_LIST | PCI_STATUS_DEVSEL_FAST); > > + > > + wmb(); > > +} > > + > > +static int rcar_pcie_clocks_get(struct rcar_pcie *pcie) > > +{ > > + int err = 0; > > + > > + pcie->clk = clk_get(pcie->dev, NULL); > > + if (IS_ERR(pcie->clk)) > > + err = PTR_ERR(pcie->clk); > > + else > > + clk_enable(pcie->clk); > > + > > + return err; > > +} > > Shouldn't you be using pm_runtime for this? Yes, I'll fix this > > +static void rcar_pcie_clocks_put(struct rcar_pcie *pcie) > > +{ > > + clk_put(pcie->clk); > > +} > > + > > +struct rcar_pcie_errs { > > + int bit; > > + const char *description; > > +}; > > + > > +static int __init rcar_pcie_get_resources(struct platform_device *pdev, > > + struct rcar_pcie *pcie) > > +{ > > + struct resource *res; > > + int i; > > + int err; > > + > > + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); > > + > > + i = platform_get_irq(pdev, 0); > > + if (!res || i < 0) { > > + dev_err(pcie->dev, "cannot get platform resources\n"); > > + return -ENOENT; > > + } > > + pcie->irq = i; > > + > > + err = rcar_pcie_clocks_get(pcie); > > + if (err) { > > + dev_err(&pdev->dev, "failed to get clocks: %d\n", err); > > + return err; > > + } > > + > > + pcie->base = devm_request_and_ioremap(&pdev->dev, res); > > + if (!pcie->base) { > > + dev_err(&pdev->dev, "failed to map PCIe registers: %d\n", err); > > + err = -ENOMEM; > > + goto err_map_reg; > > + } > > + > > + return 0; > > + > > +err_map_reg: > > + rcar_pcie_clocks_put(pcie); > > + > > + return err; > > +} > > + > > +static struct platform_device_id rcar_pcie_id_table[] = { > > + { "r8a7779-pcie", RCAR_H1 }, > > + { "r8a7790-pcie", RCAR_GENERIC }, > > + { "r8a7791-pcie", RCAR_GENERIC }, > > + {}, > > +}; > > +MODULE_DEVICE_TABLE(platform, rcar_pcie_id_table); > > Really, are you still going to submit drivers without OF bindings? > > NAK! I totally agree, hence my comment in the cover email: "This is RFC as there is some work required for DT support." Perhaps I should have marked each patch as RFC. > > +static int __init rcar_pcie_probe(struct platform_device *pdev) > > +{ > > + struct rcar_pcie *pcie; > > + struct resource *res; > > + unsigned int data; > > + int i, err; > > + > > + pcie = devm_kzalloc(&pdev->dev, sizeof(*pcie), GFP_KERNEL); > > + if (!pcie) > > + return -ENOMEM; > > + > > + pcie->dev = &pdev->dev; > > + pcie->chip = pdev->id_entry->driver_data; > > + > > + /* Get resources */ > > + err = rcar_pcie_get_resources(pdev, pcie); > > + if (err < 0) { > > + dev_err(&pdev->dev, "failed to request resources: %d\n", err); > > + return err; > > + } > > + > > + /* Get the I/O and memory ranges */ > > + for (i = 0; i < NR_PCI_RESOURCES; i++) { > > + res = platform_get_resource(pdev, IORESOURCE_MEM, i+1); > > + if (!res) { > > + dev_err(&pdev->dev, "cannot get MEM%d resources\n", i); > > + return -ENOENT; > > + } > > + pcie->res[i] = res; > > + } > > + > > + rcar_pcie_hw_init(pcie); > > + > > + if (!pcie->haslink) { > > + dev_info(&pdev->dev, "PCI: PCIe link down\n"); > > + return 0; > > + } > > + > > + data = pci_read_reg(pcie, MACSR); > > + dev_info(&pdev->dev, "PCIe x%d: link up\n", (data >> 20) & 0x3f); > > + > > + rcar_pcie_enable(pcie); > > + > > + platform_set_drvdata(pdev, pcie); > > + return 0; > > +} > > + > > +static struct platform_driver rcar_pcie_driver = { > > + .probe = rcar_pcie_probe, > > + .driver = { > > + .name = DRV_NAME, > > + .owner = THIS_MODULE, > > + }, > > + .id_table = rcar_pcie_id_table, > > +}; > > + > > +module_platform_driver(rcar_pcie_driver); > > + > > +MODULE_AUTHOR("Phil Edworthy <phil.edworthy@renesas.com>"); > > +MODULE_DESCRIPTION("Renesas R-Car PCIe driver"); > > +MODULE_LICENSE("GPLv2"); > > diff --git a/drivers/pci/host/pcie-rcar.h b/drivers/pci/host/pcie-rcar.h > > new file mode 100644 > > index 0000000..a6b407f > > --- /dev/null > > +++ b/drivers/pci/host/pcie-rcar.h > > @@ -0,0 +1,84 @@ > > +/* > > + * PCI Express definitions for Renesas R-Car SoCs > > + */ > > +#ifndef __PCI_RCAR_H > > +#define __PCI_RCAR_H > > + > > +#define PCI_DEVICE_ID_RENESAS_RCAR 0x0018 > > should this go into pci ids header? This is not used so I'll remove it. > > +#define PCIECAR 0x000010 > > +#define PCIECCTLR 0x000018 > > +#define CONFIG_SEND_ENABLE (1 << 31) > > +#define TYPE0 (0 << 8) > > +#define TYPE1 (1 << 8) > > +#define PCIECDR 0x000020 > > +#define PCIEMSR 0x000028 > > +#define PCIEINTXR 0x000400 > > +#define PCIEPHYSR 0x0007f0 > > + > > +/* Transfer control */ > > +#define PCIETCTLR 0x02000 > > +#define CFINIT 1 > > +#define PCIETSTR 0x02004 > > +#define DATA_LINK_ACTIVE 1 > > +#define PCIEINTR 0x02008 > > +#define PCIEINTER 0x0200c > > +#define PCIEERRFR 0x02020 > > +#define UNSUPPORTED_REQUEST (1 << 4) > > +#define PCIEERRFER 0x02024 > > +#define PCIEERRFR2 0x02028 > > +#define PCIEPMSR 0x02034 > > +#define PCIEPMSCIER 0x02038 > > +#define PCIEMSIFR 0x02044 > > + > > +/* root port address */ > > +#define PCIEPRAR(x) (0x02080 + ((x) * 0x4)) > > + > > +/* local address reg & mask */ > > +#define PCIELAR(x) (0x02200 + ((x) * 0x20)) > > +#define PCIELAMR(x) (0x02208 + ((x) * 0x20)) > > +#define LAM_PMIOLAMnB3 (1 << 3) > > +#define LAM_PMIOLAMnB2 (1 << 2) > > +#define LAM_PREFETCH (1 << 3) > > +#define LAM_64BIT (1 << 2) > > +#define LAR_ENABLE (1 << 1) > > + > > +/* PCIe address reg & mask */ > > +#define PCIEPARL(x) (0x03400 + ((x) * 0x20)) > > +#define PCIEPARH(x) (0x03404 + ((x) * 0x20)) > > +#define PCIEPAMR(x) (0x03408 + ((x) * 0x20)) > > +#define PCIEPTCTLR(x) (0x0340c + ((x) * 0x20)) > > +#define PAR_ENABLE (1 << 31) > > +#define IO_SPACE (1 << 8) > > + > > +/* Configuration */ > > +#define PCICONF(x) (0x010000 + ((x) * 0x4)) > > +#define PMCAP(x) (0x010040 + ((x) * 0x4)) > > +#define MSICAP(x) (0x010050 + ((x) * 0x4)) > > +#define EXPCAP(x) (0x010070 + ((x) * 0x4)) > > +#define VCCAP(x) (0x010100 + ((x) * 0x4)) > > +#define SERNUMCAP(x) (0x0101b0 + ((x) * 0x4)) > > + > > +/* link layer */ > > +#define IDSETR0 0x011000 > > +#define IDSETR1 0x011004 > > +#define SUBIDSETR 0x011024 > > +#define DSERSETR0 0x01102c > > +#define DSERSETR1 0x011030 > > +#define TLCTLR 0x011048 > > +#define MACSR 0x011054 > > +#define MACCTLR 0x011058 > > +#define SCRAMBLE_DISABLE (1 << 27) > > + > > +/* R-Car H1 PHY */ > > +#define H1_PCIEPHYCTLR 0x040008 > > +#define H1_PCIEPHYADRR 0x04000c > > +#define WRITE_CMD (1 << 16) > > +#define PHY_ACK (1 << 24) > > +#define RATE_POS 12 > > +#define LANE_POS 8 > > +#define ADR_POS 0 > > +#define H1_PCIEPHYDOUTR 0x040014 > > +#define H1_PCIEPHYSR 0x040018 > > + > > +#endif /* __PCI_RCAR_H */ > > > > > -- > Ben Dooks http://www.codethink.co.uk/ > Senior Engineer Codethink - Providing Genius Thanks Phil ^ permalink raw reply [flat|nested] 16+ messages in thread
* Re: [PATCH 1/5] PCI: host: Add Renesas R-Car PCIe driver 2014-02-26 17:13 ` Phil.Edworthy @ 2014-02-26 17:19 ` Ben Dooks -1 siblings, 0 replies; 16+ messages in thread From: Ben Dooks @ 2014-02-26 17:19 UTC (permalink / raw) To: Phil.Edworthy Cc: Bjorn Helgaas, Simon Horman, linux-pci, linux-sh, Magnus Damm, Valentine Barshak On 26/02/14 17:13, Phil.Edworthy@renesas.com wrote: > Hi Ben, > > Thanks for your comments. > > On 26/02/2014 16:33, Ben Dooks wrote: >> On 26/02/14 16:08, Phil Edworthy wrote: >>> This PCIe Host driver currently does not support MSI, so cards >>> fall back to INTx interrupts. >>> >>> Signed-off-by: Phil Edworthy <phil.edworthy@renesas.com> >> >>> + bool "Renesas R-Car PCIe controller" >>> + depends on ARM && (ARCH_R8A7790 || ARCH_R8A7791 || COMPILE_TEST) >>> + help >>> + Say Y here if you want PCIe controller support on R-Car Gen2 > SoCs. >>> + >>> endmenu >>> diff --git a/drivers/pci/host/Makefile b/drivers/pci/host/Makefile >>> index 13fb333..19946f9 100644 >>> --- a/drivers/pci/host/Makefile >>> +++ b/drivers/pci/host/Makefile >>> @@ -4,3 +4,4 @@ obj-$(CONFIG_PCI_IMX6) += pci-imx6.o >>> obj-$(CONFIG_PCI_MVEBU) += pci-mvebu.o >>> 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 >>> diff --git a/drivers/pci/host/pcie-rcar.c > b/drivers/pci/host/pcie-rcar.c >>> new file mode 100644 >>> index 0000000..d9a315f >>> --- /dev/null >>> +++ b/drivers/pci/host/pcie-rcar.c >>> @@ -0,0 +1,614 @@ >>> +/* >>> + * PCIe driver for Renesas R-Car SoCs >>> + * Copyright (C) 2013 Renesas Electronics Europe Ltd >>> + * >>> + * Based on: >>> + * arch/sh/drivers/pci/pcie-sh7786.c >>> + * arch/sh/drivers/pci/ops-sh7786.c >>> + * Copyright (C) 2009 - 2011 Paul Mundt >>> + * >>> + * 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/module.h> >>> +#include <linux/kernel.h> >>> +#include <linux/of_address.h> >>> +#include <linux/of_irq.h> >>> +#include <linux/of_pci.h> >>> +#include <linux/of_platform.h> >>> +#include <linux/pci.h> >>> +#include <linux/platform_device.h> >>> +#include <linux/slab.h> >>> +#include "pcie-rcar.h" >>> + >>> +#define DRV_NAME "rcar-pcie" >>> + >>> +enum chip_id { >>> + RCAR_GENERIC, >>> + RCAR_H1, >>> +}; >>> + >>> +#define RCONF(x) (PCICONF(0)+(x)) >>> +#define REXPCAP(x) (EXPCAP(0)+(x)) >>> +#define RVCCAP(x) (VCCAP(0)+(x)) >>> + >>> +#define PCIE_CONF_BUS(b) (((b) & 0xff) << 24) >>> +#define PCIE_CONF_DEV(d) (((d) & 0x1f) << 19) >>> +#define PCIE_CONF_FUNC(f) (((f) & 0x7) << 16) >>> + >>> +#define NR_PCI_RESOURCES 4 >>> + >>> +/* Structure representing the PCIe interface */ >>> +struct rcar_pcie { >>> + struct device *dev; >>> + void __iomem *base; >>> + int irq; >>> + struct clk *clk; >>> + struct resource *res[NR_PCI_RESOURCES]; >>> + int haslink; >>> + enum chip_id chip; >>> + u8 root_bus_nr; >>> +}; >>> + >>> +static inline struct rcar_pcie *sys_to_pcie(struct pci_sys_data *sys) >>> +{ >>> + return sys->private_data; >>> +} >>> + >>> +static void >>> +pci_write_reg(struct rcar_pcie *pcie, unsigned long val, unsigned > long reg) >>> +{ >>> + writel(val, pcie->base + reg); >>> +} >> >> Do we really need wrappers like these? > They were handy during development, but I guess not. Actually, not that big of a deal, ignore me on this. >>> +static unsigned long >>> +pci_read_reg(struct rcar_pcie *pcie, unsigned long reg) >>> +{ >>> + return readl(pcie->base + reg); >>> +} >>> + >>> +enum { >>> + PCI_ACCESS_READ, >>> + PCI_ACCESS_WRITE, >>> +}; >> >>> +static int rcar_pcie_map_irq(const struct pci_dev *dev, u8 slot, u8 > pin) >>> +{ >>> + struct rcar_pcie *pcie = sys_to_pcie(dev->bus->sysdata); >>> + return pcie->irq; >>> +} >>> + >>> +static void __init rcar_pcie_enable(struct rcar_pcie *pcie) >>> +{ >>> + struct hw_pci hw; >>> + >>> + memset(&hw, 0, sizeof(hw)); >>> + >>> + hw.nr_controllers = 1; >>> + hw.private_data = (void **)&pcie; >>> + hw.setup = rcar_pcie_setup, >>> + hw.map_irq = rcar_pcie_map_irq, >>> + hw.ops = &rcar_pcie_ops, >>> + >>> + pci_common_init(&hw); >>> +} >>> + >>> +static int __init phy_wait_for_ack(struct rcar_pcie *pcie) >>> +{ >>> + unsigned int timeout = 100; >>> + >>> + while (timeout--) { >>> + if (pci_read_reg(pcie, H1_PCIEPHYADRR) & PHY_ACK) >>> + return 0; >>> + >>> + udelay(100); >>> + } >>> + >>> + dev_err(pcie->dev, "Access to PCIe phy timed out\n"); >>> + >>> + return -ETIMEDOUT; >>> +} >> >> Could this be done with some sort of sleep, instead of having >> to keep delaying? How long is the average wait for pci to finish >> a write? > I haven't measured how long, but yes, I can use mdelay instead. How about something that sleeps? Does the host have a completion IRQ you could sleep on? >>> + >>> +static void __init phy_write_reg(struct rcar_pcie *pcie, >>> + unsigned int rate, unsigned int addr, >>> + unsigned int lane, unsigned int data) >>> +{ >>> + unsigned long phyaddr; >>> + >>> + phyaddr = WRITE_CMD | >>> + ((rate & 1) << RATE_POS) | >>> + ((lane & 0xf) << LANE_POS) | >>> + ((addr & 0xff) << ADR_POS); >>> + >>> + /* Set write data */ >>> + pci_write_reg(pcie, data, H1_PCIEPHYDOUTR); >>> + pci_write_reg(pcie, phyaddr, H1_PCIEPHYADRR); >>> + >>> + /* Ignore errors as they will be dealt with if the data link is > down */ >>> + phy_wait_for_ack(pcie); >>> + >>> + /* Clear command */ >>> + pci_write_reg(pcie, 0, H1_PCIEPHYDOUTR); >>> + pci_write_reg(pcie, 0, H1_PCIEPHYADRR); >>> + >>> + /* Ignore errors as they will be dealt with if the data link is > down */ >>> + phy_wait_for_ack(pcie); >>> +} >>> + >>> >> >>> +static int __init rcar_pcie_wait_for_dl(struct rcar_pcie *pcie) >>> +{ >>> + unsigned int timeout = 100; >>> + >>> + while (timeout--) { >>> + if ((pci_read_reg(pcie, PCIETSTR) & DATA_LINK_ACTIVE)) >>> + return 0; >>> + >>> + udelay(100); >>> + } >>> + >>> + return -ETIMEDOUT; >>> +} >> >> Same comments as for previous delay loop >> >> >>> +static void __init rcar_pcie_hw_init(struct rcar_pcie *pcie) >>> +{ >>> + /* Initialise R-Car H1 PHY & wait for it to be ready */ >>> + if (pcie->chip == RCAR_H1) >>> + if (rcar_pcie_phy_init_rcar_h1(pcie)) >>> + return; >>> + >>> + /* Begin initialization */ >>> + pci_write_reg(pcie, 0, PCIETCTLR); >>> + >>> + /* Set mode */ >>> + pci_write_reg(pcie, 1, PCIEMSR); >>> + >>> + /* >>> + * For target transfers, setup a single 64-bit 4GB mapping at > address >>> + * 0. The hardware can only map memory that starts on a power of > two >>> + * boundary, and size is power of 2, so best to ignore it. >>> + */ >>> + pci_write_reg(pcie, 0, PCIEPRAR(0)); >>> + pci_write_reg(pcie, 0, PCIELAR(0)); >>> + pci_write_reg(pcie, 0xfffffff0UL | LAM_PREFETCH | >>> + LAM_64BIT | LAR_ENABLE, PCIELAMR(0)); >>> + pci_write_reg(pcie, 0, PCIELAR(1)); >>> + pci_write_reg(pcie, 0, PCIEPRAR(1)); >>> + pci_write_reg(pcie, 0, PCIELAMR(1)); >>> + >>> + /* >>> + * Initial header for port config space is type 1, set the device >>> + * class to match. Hardware takes care of propagating the IDSETR >>> + * settings, so there is no need to bother with a quirk. >>> + */ >>> + pci_write_reg(pcie, PCI_CLASS_BRIDGE_PCI << 16, IDSETR1); >>> + >>> + /* >>> + * Setup Secondary Bus Number & Subordinate Bus Number, even > though >>> + * they aren't used, to avoid bridge being detected as broken. >>> + */ >>> + rcar_rmw32(pcie, RCONF(PCI_SECONDARY_BUS), 0xff, 1); >>> + rcar_rmw32(pcie, RCONF(PCI_SUBORDINATE_BUS), 0xff, 1); >>> + >>> + /* Initialize default capabilities. */ >>> + rcar_rmw32(pcie, REXPCAP(0), 0, PCI_CAP_ID_EXP); >>> + rcar_rmw32(pcie, REXPCAP(PCI_EXP_FLAGS), >>> + PCI_EXP_FLAGS_TYPE, PCI_EXP_TYPE_ROOT_PORT << 4); >>> + rcar_rmw32(pcie, RCONF(PCI_HEADER_TYPE), 0x7f, >>> + PCI_HEADER_TYPE_BRIDGE); >>> + >>> + /* Enable data link layer active state reporting */ >>> + rcar_rmw32(pcie, REXPCAP(PCI_EXP_LNKCAP), 0, > PCI_EXP_LNKCAP_DLLLARC); >>> + >>> + /* Write out the physical slot number = 0 */ >>> + rcar_rmw32(pcie, REXPCAP(PCI_EXP_SLTCAP), PCI_EXP_SLTCAP_PSN, 0); >>> + >>> + /* Set the completion timer timeout to the maximum 50ms. */ >>> + rcar_rmw32(pcie, TLCTLR+1, 0x3f, 50); >>> + >>> + /* Terminate list of capabilities (Next Capability Offset=0) */ >>> + rcar_rmw32(pcie, RVCCAP(0), 0xfff0, 0); >>> + >>> + /* Enable MAC data scrambling. */ >>> + rcar_rmw32(pcie, MACCTLR, SCRAMBLE_DISABLE, 0); >>> + >>> + /* Finish initialization - establish a PCI Express link */ >>> + pci_write_reg(pcie, CFINIT, PCIETCTLR); >>> + >>> + /* This will timeout if we don't have a link. */ >>> + pcie->haslink = !rcar_pcie_wait_for_dl(pcie); >>> + >>> + /* Enable INTx interrupts */ >>> + rcar_rmw32(pcie, RCONF(PCI_INTERRUPT_LINE), 0xff, 0); >>> + rcar_rmw32(pcie, RCONF(PCI_INTERRUPT_PIN), 0xff, 1); >>> + rcar_rmw32(pcie, PCIEINTXR, 0, 0xF << 8); >>> + >>> + /* Enable slave Bus Mastering */ >>> + rcar_rmw32(pcie, RCONF(PCI_STATUS), PCI_STATUS_DEVSEL_MASK, >>> + PCI_COMMAND_IO | PCI_COMMAND_MEMORY | PCI_COMMAND_MASTER | >>> + PCI_STATUS_CAP_LIST | PCI_STATUS_DEVSEL_FAST); >>> + >>> + wmb(); >>> +} >>> + >>> +static int rcar_pcie_clocks_get(struct rcar_pcie *pcie) >>> +{ >>> + int err = 0; >>> + >>> + pcie->clk = clk_get(pcie->dev, NULL); >>> + if (IS_ERR(pcie->clk)) >>> + err = PTR_ERR(pcie->clk); >>> + else >>> + clk_enable(pcie->clk); >>> + >>> + return err; >>> +} >> >> Shouldn't you be using pm_runtime for this? > Yes, I'll fix this Ta. I'm still trying to fix pm_runtime issues with devicetree. >>> +static void rcar_pcie_clocks_put(struct rcar_pcie *pcie) >>> +{ >>> + clk_put(pcie->clk); >>> +} >>> + >>> +struct rcar_pcie_errs { >>> + int bit; >>> + const char *description; >>> +}; >>> + >>> +static int __init rcar_pcie_get_resources(struct platform_device > *pdev, >>> + struct rcar_pcie *pcie) >>> +{ >>> + struct resource *res; >>> + int i; >>> + int err; >>> + >>> + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); >>> + >>> + i = platform_get_irq(pdev, 0); >>> + if (!res || i < 0) { >>> + dev_err(pcie->dev, "cannot get platform resources\n"); >>> + return -ENOENT; >>> + } >>> + pcie->irq = i; >>> + >>> + err = rcar_pcie_clocks_get(pcie); >>> + if (err) { >>> + dev_err(&pdev->dev, "failed to get clocks: %d\n", err); >>> + return err; >>> + } >>> + >>> + pcie->base = devm_request_and_ioremap(&pdev->dev, res); >>> + if (!pcie->base) { >>> + dev_err(&pdev->dev, "failed to map PCIe registers: %d\n", err); >>> + err = -ENOMEM; >>> + goto err_map_reg; >>> + } >>> + >>> + return 0; >>> + >>> +err_map_reg: >>> + rcar_pcie_clocks_put(pcie); >>> + >>> + return err; >>> +} >>> + >>> +static struct platform_device_id rcar_pcie_id_table[] = { >>> + { "r8a7779-pcie", RCAR_H1 }, >>> + { "r8a7790-pcie", RCAR_GENERIC }, >>> + { "r8a7791-pcie", RCAR_GENERIC }, >>> + {}, >>> +}; >>> +MODULE_DEVICE_TABLE(platform, rcar_pcie_id_table); >> >> Really, are you still going to submit drivers without OF bindings? >> >> NAK! > I totally agree, hence my comment in the cover email: > "This is RFC as there is some work required for DT support." > Perhaps I should have marked each patch as RFC. Actually, it could be applied as-is, but I would prefer DT. I didn't notice, I only reviewed this bet, so yes marking the lot as RFC would have been better. >>> +static int __init rcar_pcie_probe(struct platform_device *pdev) >>> +{ >>> + struct rcar_pcie *pcie; >>> + struct resource *res; >>> + unsigned int data; >>> + int i, err; >>> + >>> + pcie = devm_kzalloc(&pdev->dev, sizeof(*pcie), GFP_KERNEL); >>> + if (!pcie) >>> + return -ENOMEM; >>> + >>> + pcie->dev = &pdev->dev; >>> + pcie->chip = pdev->id_entry->driver_data; >>> + >>> + /* Get resources */ >>> + err = rcar_pcie_get_resources(pdev, pcie); >>> + if (err < 0) { >>> + dev_err(&pdev->dev, "failed to request resources: %d\n", err); >>> + return err; >>> + } >>> + >>> + /* Get the I/O and memory ranges */ >>> + for (i = 0; i < NR_PCI_RESOURCES; i++) { >>> + res = platform_get_resource(pdev, IORESOURCE_MEM, i+1); >>> + if (!res) { >>> + dev_err(&pdev->dev, "cannot get MEM%d resources\n", i); >>> + return -ENOENT; >>> + } >>> + pcie->res[i] = res; >>> + } >>> + >>> + rcar_pcie_hw_init(pcie); >>> + >>> + if (!pcie->haslink) { >>> + dev_info(&pdev->dev, "PCI: PCIe link down\n"); >>> + return 0; >>> + } >>> + >>> + data = pci_read_reg(pcie, MACSR); >>> + dev_info(&pdev->dev, "PCIe x%d: link up\n", (data >> 20) & 0x3f); >>> + >>> + rcar_pcie_enable(pcie); >>> + >>> + platform_set_drvdata(pdev, pcie); >>> + return 0; >>> +} >>> + >>> +static struct platform_driver rcar_pcie_driver = { >>> + .probe = rcar_pcie_probe, >>> + .driver = { >>> + .name = DRV_NAME, >>> + .owner = THIS_MODULE, >>> + }, >>> + .id_table = rcar_pcie_id_table, >>> +}; >>> + >>> +module_platform_driver(rcar_pcie_driver); >>> + >>> +MODULE_AUTHOR("Phil Edworthy <phil.edworthy@renesas.com>"); >>> +MODULE_DESCRIPTION("Renesas R-Car PCIe driver"); >>> +MODULE_LICENSE("GPLv2"); >>> diff --git a/drivers/pci/host/pcie-rcar.h > b/drivers/pci/host/pcie-rcar.h >>> new file mode 100644 >>> index 0000000..a6b407f >>> --- /dev/null >>> +++ b/drivers/pci/host/pcie-rcar.h >>> @@ -0,0 +1,84 @@ >>> +/* >>> + * PCI Express definitions for Renesas R-Car SoCs >>> + */ >>> +#ifndef __PCI_RCAR_H >>> +#define __PCI_RCAR_H >>> + >>> +#define PCI_DEVICE_ID_RENESAS_RCAR 0x0018 >> >> should this go into pci ids header? > This is not used so I'll remove it. > >>> +#define PCIECAR 0x000010 >>> +#define PCIECCTLR 0x000018 >>> +#define CONFIG_SEND_ENABLE (1 << 31) >>> +#define TYPE0 (0 << 8) >>> +#define TYPE1 (1 << 8) >>> +#define PCIECDR 0x000020 >>> +#define PCIEMSR 0x000028 >>> +#define PCIEINTXR 0x000400 >>> +#define PCIEPHYSR 0x0007f0 >>> + >>> +/* Transfer control */ >>> +#define PCIETCTLR 0x02000 >>> +#define CFINIT 1 >>> +#define PCIETSTR 0x02004 >>> +#define DATA_LINK_ACTIVE 1 >>> +#define PCIEINTR 0x02008 >>> +#define PCIEINTER 0x0200c >>> +#define PCIEERRFR 0x02020 >>> +#define UNSUPPORTED_REQUEST (1 << 4) >>> +#define PCIEERRFER 0x02024 >>> +#define PCIEERRFR2 0x02028 >>> +#define PCIEPMSR 0x02034 >>> +#define PCIEPMSCIER 0x02038 >>> +#define PCIEMSIFR 0x02044 >>> + >>> +/* root port address */ >>> +#define PCIEPRAR(x) (0x02080 + ((x) * 0x4)) >>> + >>> +/* local address reg & mask */ >>> +#define PCIELAR(x) (0x02200 + ((x) * 0x20)) >>> +#define PCIELAMR(x) (0x02208 + ((x) * 0x20)) >>> +#define LAM_PMIOLAMnB3 (1 << 3) >>> +#define LAM_PMIOLAMnB2 (1 << 2) >>> +#define LAM_PREFETCH (1 << 3) >>> +#define LAM_64BIT (1 << 2) >>> +#define LAR_ENABLE (1 << 1) >>> + >>> +/* PCIe address reg & mask */ >>> +#define PCIEPARL(x) (0x03400 + ((x) * 0x20)) >>> +#define PCIEPARH(x) (0x03404 + ((x) * 0x20)) >>> +#define PCIEPAMR(x) (0x03408 + ((x) * 0x20)) >>> +#define PCIEPTCTLR(x) (0x0340c + ((x) * 0x20)) >>> +#define PAR_ENABLE (1 << 31) >>> +#define IO_SPACE (1 << 8) >>> + >>> +/* Configuration */ >>> +#define PCICONF(x) (0x010000 + ((x) * 0x4)) >>> +#define PMCAP(x) (0x010040 + ((x) * 0x4)) >>> +#define MSICAP(x) (0x010050 + ((x) * 0x4)) >>> +#define EXPCAP(x) (0x010070 + ((x) * 0x4)) >>> +#define VCCAP(x) (0x010100 + ((x) * 0x4)) >>> +#define SERNUMCAP(x) (0x0101b0 + ((x) * 0x4)) >>> + >>> +/* link layer */ >>> +#define IDSETR0 0x011000 >>> +#define IDSETR1 0x011004 >>> +#define SUBIDSETR 0x011024 >>> +#define DSERSETR0 0x01102c >>> +#define DSERSETR1 0x011030 >>> +#define TLCTLR 0x011048 >>> +#define MACSR 0x011054 >>> +#define MACCTLR 0x011058 >>> +#define SCRAMBLE_DISABLE (1 << 27) >>> + >>> +/* R-Car H1 PHY */ >>> +#define H1_PCIEPHYCTLR 0x040008 >>> +#define H1_PCIEPHYADRR 0x04000c >>> +#define WRITE_CMD (1 << 16) >>> +#define PHY_ACK (1 << 24) >>> +#define RATE_POS 12 >>> +#define LANE_POS 8 >>> +#define ADR_POS 0 >>> +#define H1_PCIEPHYDOUTR 0x040014 >>> +#define H1_PCIEPHYSR 0x040018 >>> + >>> +#endif /* __PCI_RCAR_H */ >>> >> >> >> -- >> Ben Dooks http://www.codethink.co.uk/ >> Senior Engineer Codethink - Providing Genius > > Thanks > Phil > -- > To unsubscribe from this list: send the line "unsubscribe linux-sh" in > the body of a message to majordomo@vger.kernel.org > More majordomo info at http://vger.kernel.org/majordomo-info.html > -- Ben Dooks http://www.codethink.co.uk/ Senior Engineer Codethink - Providing Genius ^ permalink raw reply [flat|nested] 16+ messages in thread
* Re: [PATCH 1/5] PCI: host: Add Renesas R-Car PCIe driver @ 2014-02-26 17:19 ` Ben Dooks 0 siblings, 0 replies; 16+ messages in thread From: Ben Dooks @ 2014-02-26 17:19 UTC (permalink / raw) To: Phil.Edworthy Cc: Bjorn Helgaas, Simon Horman, linux-pci, linux-sh, Magnus Damm, Valentine Barshak On 26/02/14 17:13, Phil.Edworthy@renesas.com wrote: > Hi Ben, > > Thanks for your comments. > > On 26/02/2014 16:33, Ben Dooks wrote: >> On 26/02/14 16:08, Phil Edworthy wrote: >>> This PCIe Host driver currently does not support MSI, so cards >>> fall back to INTx interrupts. >>> >>> Signed-off-by: Phil Edworthy <phil.edworthy@renesas.com> >> >>> + bool "Renesas R-Car PCIe controller" >>> + depends on ARM && (ARCH_R8A7790 || ARCH_R8A7791 || COMPILE_TEST) >>> + help >>> + Say Y here if you want PCIe controller support on R-Car Gen2 > SoCs. >>> + >>> endmenu >>> diff --git a/drivers/pci/host/Makefile b/drivers/pci/host/Makefile >>> index 13fb333..19946f9 100644 >>> --- a/drivers/pci/host/Makefile >>> +++ b/drivers/pci/host/Makefile >>> @@ -4,3 +4,4 @@ obj-$(CONFIG_PCI_IMX6) += pci-imx6.o >>> obj-$(CONFIG_PCI_MVEBU) += pci-mvebu.o >>> 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 >>> diff --git a/drivers/pci/host/pcie-rcar.c > b/drivers/pci/host/pcie-rcar.c >>> new file mode 100644 >>> index 0000000..d9a315f >>> --- /dev/null >>> +++ b/drivers/pci/host/pcie-rcar.c >>> @@ -0,0 +1,614 @@ >>> +/* >>> + * PCIe driver for Renesas R-Car SoCs >>> + * Copyright (C) 2013 Renesas Electronics Europe Ltd >>> + * >>> + * Based on: >>> + * arch/sh/drivers/pci/pcie-sh7786.c >>> + * arch/sh/drivers/pci/ops-sh7786.c >>> + * Copyright (C) 2009 - 2011 Paul Mundt >>> + * >>> + * 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/module.h> >>> +#include <linux/kernel.h> >>> +#include <linux/of_address.h> >>> +#include <linux/of_irq.h> >>> +#include <linux/of_pci.h> >>> +#include <linux/of_platform.h> >>> +#include <linux/pci.h> >>> +#include <linux/platform_device.h> >>> +#include <linux/slab.h> >>> +#include "pcie-rcar.h" >>> + >>> +#define DRV_NAME "rcar-pcie" >>> + >>> +enum chip_id { >>> + RCAR_GENERIC, >>> + RCAR_H1, >>> +}; >>> + >>> +#define RCONF(x) (PCICONF(0)+(x)) >>> +#define REXPCAP(x) (EXPCAP(0)+(x)) >>> +#define RVCCAP(x) (VCCAP(0)+(x)) >>> + >>> +#define PCIE_CONF_BUS(b) (((b) & 0xff) << 24) >>> +#define PCIE_CONF_DEV(d) (((d) & 0x1f) << 19) >>> +#define PCIE_CONF_FUNC(f) (((f) & 0x7) << 16) >>> + >>> +#define NR_PCI_RESOURCES 4 >>> + >>> +/* Structure representing the PCIe interface */ >>> +struct rcar_pcie { >>> + struct device *dev; >>> + void __iomem *base; >>> + int irq; >>> + struct clk *clk; >>> + struct resource *res[NR_PCI_RESOURCES]; >>> + int haslink; >>> + enum chip_id chip; >>> + u8 root_bus_nr; >>> +}; >>> + >>> +static inline struct rcar_pcie *sys_to_pcie(struct pci_sys_data *sys) >>> +{ >>> + return sys->private_data; >>> +} >>> + >>> +static void >>> +pci_write_reg(struct rcar_pcie *pcie, unsigned long val, unsigned > long reg) >>> +{ >>> + writel(val, pcie->base + reg); >>> +} >> >> Do we really need wrappers like these? > They were handy during development, but I guess not. Actually, not that big of a deal, ignore me on this. >>> +static unsigned long >>> +pci_read_reg(struct rcar_pcie *pcie, unsigned long reg) >>> +{ >>> + return readl(pcie->base + reg); >>> +} >>> + >>> +enum { >>> + PCI_ACCESS_READ, >>> + PCI_ACCESS_WRITE, >>> +}; >> >>> +static int rcar_pcie_map_irq(const struct pci_dev *dev, u8 slot, u8 > pin) >>> +{ >>> + struct rcar_pcie *pcie = sys_to_pcie(dev->bus->sysdata); >>> + return pcie->irq; >>> +} >>> + >>> +static void __init rcar_pcie_enable(struct rcar_pcie *pcie) >>> +{ >>> + struct hw_pci hw; >>> + >>> + memset(&hw, 0, sizeof(hw)); >>> + >>> + hw.nr_controllers = 1; >>> + hw.private_data = (void **)&pcie; >>> + hw.setup = rcar_pcie_setup, >>> + hw.map_irq = rcar_pcie_map_irq, >>> + hw.ops = &rcar_pcie_ops, >>> + >>> + pci_common_init(&hw); >>> +} >>> + >>> +static int __init phy_wait_for_ack(struct rcar_pcie *pcie) >>> +{ >>> + unsigned int timeout = 100; >>> + >>> + while (timeout--) { >>> + if (pci_read_reg(pcie, H1_PCIEPHYADRR) & PHY_ACK) >>> + return 0; >>> + >>> + udelay(100); >>> + } >>> + >>> + dev_err(pcie->dev, "Access to PCIe phy timed out\n"); >>> + >>> + return -ETIMEDOUT; >>> +} >> >> Could this be done with some sort of sleep, instead of having >> to keep delaying? How long is the average wait for pci to finish >> a write? > I haven't measured how long, but yes, I can use mdelay instead. How about something that sleeps? Does the host have a completion IRQ you could sleep on? >>> + >>> +static void __init phy_write_reg(struct rcar_pcie *pcie, >>> + unsigned int rate, unsigned int addr, >>> + unsigned int lane, unsigned int data) >>> +{ >>> + unsigned long phyaddr; >>> + >>> + phyaddr = WRITE_CMD | >>> + ((rate & 1) << RATE_POS) | >>> + ((lane & 0xf) << LANE_POS) | >>> + ((addr & 0xff) << ADR_POS); >>> + >>> + /* Set write data */ >>> + pci_write_reg(pcie, data, H1_PCIEPHYDOUTR); >>> + pci_write_reg(pcie, phyaddr, H1_PCIEPHYADRR); >>> + >>> + /* Ignore errors as they will be dealt with if the data link is > down */ >>> + phy_wait_for_ack(pcie); >>> + >>> + /* Clear command */ >>> + pci_write_reg(pcie, 0, H1_PCIEPHYDOUTR); >>> + pci_write_reg(pcie, 0, H1_PCIEPHYADRR); >>> + >>> + /* Ignore errors as they will be dealt with if the data link is > down */ >>> + phy_wait_for_ack(pcie); >>> +} >>> + >>> >> >>> +static int __init rcar_pcie_wait_for_dl(struct rcar_pcie *pcie) >>> +{ >>> + unsigned int timeout = 100; >>> + >>> + while (timeout--) { >>> + if ((pci_read_reg(pcie, PCIETSTR) & DATA_LINK_ACTIVE)) >>> + return 0; >>> + >>> + udelay(100); >>> + } >>> + >>> + return -ETIMEDOUT; >>> +} >> >> Same comments as for previous delay loop >> >> >>> +static void __init rcar_pcie_hw_init(struct rcar_pcie *pcie) >>> +{ >>> + /* Initialise R-Car H1 PHY & wait for it to be ready */ >>> + if (pcie->chip = RCAR_H1) >>> + if (rcar_pcie_phy_init_rcar_h1(pcie)) >>> + return; >>> + >>> + /* Begin initialization */ >>> + pci_write_reg(pcie, 0, PCIETCTLR); >>> + >>> + /* Set mode */ >>> + pci_write_reg(pcie, 1, PCIEMSR); >>> + >>> + /* >>> + * For target transfers, setup a single 64-bit 4GB mapping at > address >>> + * 0. The hardware can only map memory that starts on a power of > two >>> + * boundary, and size is power of 2, so best to ignore it. >>> + */ >>> + pci_write_reg(pcie, 0, PCIEPRAR(0)); >>> + pci_write_reg(pcie, 0, PCIELAR(0)); >>> + pci_write_reg(pcie, 0xfffffff0UL | LAM_PREFETCH | >>> + LAM_64BIT | LAR_ENABLE, PCIELAMR(0)); >>> + pci_write_reg(pcie, 0, PCIELAR(1)); >>> + pci_write_reg(pcie, 0, PCIEPRAR(1)); >>> + pci_write_reg(pcie, 0, PCIELAMR(1)); >>> + >>> + /* >>> + * Initial header for port config space is type 1, set the device >>> + * class to match. Hardware takes care of propagating the IDSETR >>> + * settings, so there is no need to bother with a quirk. >>> + */ >>> + pci_write_reg(pcie, PCI_CLASS_BRIDGE_PCI << 16, IDSETR1); >>> + >>> + /* >>> + * Setup Secondary Bus Number & Subordinate Bus Number, even > though >>> + * they aren't used, to avoid bridge being detected as broken. >>> + */ >>> + rcar_rmw32(pcie, RCONF(PCI_SECONDARY_BUS), 0xff, 1); >>> + rcar_rmw32(pcie, RCONF(PCI_SUBORDINATE_BUS), 0xff, 1); >>> + >>> + /* Initialize default capabilities. */ >>> + rcar_rmw32(pcie, REXPCAP(0), 0, PCI_CAP_ID_EXP); >>> + rcar_rmw32(pcie, REXPCAP(PCI_EXP_FLAGS), >>> + PCI_EXP_FLAGS_TYPE, PCI_EXP_TYPE_ROOT_PORT << 4); >>> + rcar_rmw32(pcie, RCONF(PCI_HEADER_TYPE), 0x7f, >>> + PCI_HEADER_TYPE_BRIDGE); >>> + >>> + /* Enable data link layer active state reporting */ >>> + rcar_rmw32(pcie, REXPCAP(PCI_EXP_LNKCAP), 0, > PCI_EXP_LNKCAP_DLLLARC); >>> + >>> + /* Write out the physical slot number = 0 */ >>> + rcar_rmw32(pcie, REXPCAP(PCI_EXP_SLTCAP), PCI_EXP_SLTCAP_PSN, 0); >>> + >>> + /* Set the completion timer timeout to the maximum 50ms. */ >>> + rcar_rmw32(pcie, TLCTLR+1, 0x3f, 50); >>> + >>> + /* Terminate list of capabilities (Next Capability Offset=0) */ >>> + rcar_rmw32(pcie, RVCCAP(0), 0xfff0, 0); >>> + >>> + /* Enable MAC data scrambling. */ >>> + rcar_rmw32(pcie, MACCTLR, SCRAMBLE_DISABLE, 0); >>> + >>> + /* Finish initialization - establish a PCI Express link */ >>> + pci_write_reg(pcie, CFINIT, PCIETCTLR); >>> + >>> + /* This will timeout if we don't have a link. */ >>> + pcie->haslink = !rcar_pcie_wait_for_dl(pcie); >>> + >>> + /* Enable INTx interrupts */ >>> + rcar_rmw32(pcie, RCONF(PCI_INTERRUPT_LINE), 0xff, 0); >>> + rcar_rmw32(pcie, RCONF(PCI_INTERRUPT_PIN), 0xff, 1); >>> + rcar_rmw32(pcie, PCIEINTXR, 0, 0xF << 8); >>> + >>> + /* Enable slave Bus Mastering */ >>> + rcar_rmw32(pcie, RCONF(PCI_STATUS), PCI_STATUS_DEVSEL_MASK, >>> + PCI_COMMAND_IO | PCI_COMMAND_MEMORY | PCI_COMMAND_MASTER | >>> + PCI_STATUS_CAP_LIST | PCI_STATUS_DEVSEL_FAST); >>> + >>> + wmb(); >>> +} >>> + >>> +static int rcar_pcie_clocks_get(struct rcar_pcie *pcie) >>> +{ >>> + int err = 0; >>> + >>> + pcie->clk = clk_get(pcie->dev, NULL); >>> + if (IS_ERR(pcie->clk)) >>> + err = PTR_ERR(pcie->clk); >>> + else >>> + clk_enable(pcie->clk); >>> + >>> + return err; >>> +} >> >> Shouldn't you be using pm_runtime for this? > Yes, I'll fix this Ta. I'm still trying to fix pm_runtime issues with devicetree. >>> +static void rcar_pcie_clocks_put(struct rcar_pcie *pcie) >>> +{ >>> + clk_put(pcie->clk); >>> +} >>> + >>> +struct rcar_pcie_errs { >>> + int bit; >>> + const char *description; >>> +}; >>> + >>> +static int __init rcar_pcie_get_resources(struct platform_device > *pdev, >>> + struct rcar_pcie *pcie) >>> +{ >>> + struct resource *res; >>> + int i; >>> + int err; >>> + >>> + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); >>> + >>> + i = platform_get_irq(pdev, 0); >>> + if (!res || i < 0) { >>> + dev_err(pcie->dev, "cannot get platform resources\n"); >>> + return -ENOENT; >>> + } >>> + pcie->irq = i; >>> + >>> + err = rcar_pcie_clocks_get(pcie); >>> + if (err) { >>> + dev_err(&pdev->dev, "failed to get clocks: %d\n", err); >>> + return err; >>> + } >>> + >>> + pcie->base = devm_request_and_ioremap(&pdev->dev, res); >>> + if (!pcie->base) { >>> + dev_err(&pdev->dev, "failed to map PCIe registers: %d\n", err); >>> + err = -ENOMEM; >>> + goto err_map_reg; >>> + } >>> + >>> + return 0; >>> + >>> +err_map_reg: >>> + rcar_pcie_clocks_put(pcie); >>> + >>> + return err; >>> +} >>> + >>> +static struct platform_device_id rcar_pcie_id_table[] = { >>> + { "r8a7779-pcie", RCAR_H1 }, >>> + { "r8a7790-pcie", RCAR_GENERIC }, >>> + { "r8a7791-pcie", RCAR_GENERIC }, >>> + {}, >>> +}; >>> +MODULE_DEVICE_TABLE(platform, rcar_pcie_id_table); >> >> Really, are you still going to submit drivers without OF bindings? >> >> NAK! > I totally agree, hence my comment in the cover email: > "This is RFC as there is some work required for DT support." > Perhaps I should have marked each patch as RFC. Actually, it could be applied as-is, but I would prefer DT. I didn't notice, I only reviewed this bet, so yes marking the lot as RFC would have been better. >>> +static int __init rcar_pcie_probe(struct platform_device *pdev) >>> +{ >>> + struct rcar_pcie *pcie; >>> + struct resource *res; >>> + unsigned int data; >>> + int i, err; >>> + >>> + pcie = devm_kzalloc(&pdev->dev, sizeof(*pcie), GFP_KERNEL); >>> + if (!pcie) >>> + return -ENOMEM; >>> + >>> + pcie->dev = &pdev->dev; >>> + pcie->chip = pdev->id_entry->driver_data; >>> + >>> + /* Get resources */ >>> + err = rcar_pcie_get_resources(pdev, pcie); >>> + if (err < 0) { >>> + dev_err(&pdev->dev, "failed to request resources: %d\n", err); >>> + return err; >>> + } >>> + >>> + /* Get the I/O and memory ranges */ >>> + for (i = 0; i < NR_PCI_RESOURCES; i++) { >>> + res = platform_get_resource(pdev, IORESOURCE_MEM, i+1); >>> + if (!res) { >>> + dev_err(&pdev->dev, "cannot get MEM%d resources\n", i); >>> + return -ENOENT; >>> + } >>> + pcie->res[i] = res; >>> + } >>> + >>> + rcar_pcie_hw_init(pcie); >>> + >>> + if (!pcie->haslink) { >>> + dev_info(&pdev->dev, "PCI: PCIe link down\n"); >>> + return 0; >>> + } >>> + >>> + data = pci_read_reg(pcie, MACSR); >>> + dev_info(&pdev->dev, "PCIe x%d: link up\n", (data >> 20) & 0x3f); >>> + >>> + rcar_pcie_enable(pcie); >>> + >>> + platform_set_drvdata(pdev, pcie); >>> + return 0; >>> +} >>> + >>> +static struct platform_driver rcar_pcie_driver = { >>> + .probe = rcar_pcie_probe, >>> + .driver = { >>> + .name = DRV_NAME, >>> + .owner = THIS_MODULE, >>> + }, >>> + .id_table = rcar_pcie_id_table, >>> +}; >>> + >>> +module_platform_driver(rcar_pcie_driver); >>> + >>> +MODULE_AUTHOR("Phil Edworthy <phil.edworthy@renesas.com>"); >>> +MODULE_DESCRIPTION("Renesas R-Car PCIe driver"); >>> +MODULE_LICENSE("GPLv2"); >>> diff --git a/drivers/pci/host/pcie-rcar.h > b/drivers/pci/host/pcie-rcar.h >>> new file mode 100644 >>> index 0000000..a6b407f >>> --- /dev/null >>> +++ b/drivers/pci/host/pcie-rcar.h >>> @@ -0,0 +1,84 @@ >>> +/* >>> + * PCI Express definitions for Renesas R-Car SoCs >>> + */ >>> +#ifndef __PCI_RCAR_H >>> +#define __PCI_RCAR_H >>> + >>> +#define PCI_DEVICE_ID_RENESAS_RCAR 0x0018 >> >> should this go into pci ids header? > This is not used so I'll remove it. > >>> +#define PCIECAR 0x000010 >>> +#define PCIECCTLR 0x000018 >>> +#define CONFIG_SEND_ENABLE (1 << 31) >>> +#define TYPE0 (0 << 8) >>> +#define TYPE1 (1 << 8) >>> +#define PCIECDR 0x000020 >>> +#define PCIEMSR 0x000028 >>> +#define PCIEINTXR 0x000400 >>> +#define PCIEPHYSR 0x0007f0 >>> + >>> +/* Transfer control */ >>> +#define PCIETCTLR 0x02000 >>> +#define CFINIT 1 >>> +#define PCIETSTR 0x02004 >>> +#define DATA_LINK_ACTIVE 1 >>> +#define PCIEINTR 0x02008 >>> +#define PCIEINTER 0x0200c >>> +#define PCIEERRFR 0x02020 >>> +#define UNSUPPORTED_REQUEST (1 << 4) >>> +#define PCIEERRFER 0x02024 >>> +#define PCIEERRFR2 0x02028 >>> +#define PCIEPMSR 0x02034 >>> +#define PCIEPMSCIER 0x02038 >>> +#define PCIEMSIFR 0x02044 >>> + >>> +/* root port address */ >>> +#define PCIEPRAR(x) (0x02080 + ((x) * 0x4)) >>> + >>> +/* local address reg & mask */ >>> +#define PCIELAR(x) (0x02200 + ((x) * 0x20)) >>> +#define PCIELAMR(x) (0x02208 + ((x) * 0x20)) >>> +#define LAM_PMIOLAMnB3 (1 << 3) >>> +#define LAM_PMIOLAMnB2 (1 << 2) >>> +#define LAM_PREFETCH (1 << 3) >>> +#define LAM_64BIT (1 << 2) >>> +#define LAR_ENABLE (1 << 1) >>> + >>> +/* PCIe address reg & mask */ >>> +#define PCIEPARL(x) (0x03400 + ((x) * 0x20)) >>> +#define PCIEPARH(x) (0x03404 + ((x) * 0x20)) >>> +#define PCIEPAMR(x) (0x03408 + ((x) * 0x20)) >>> +#define PCIEPTCTLR(x) (0x0340c + ((x) * 0x20)) >>> +#define PAR_ENABLE (1 << 31) >>> +#define IO_SPACE (1 << 8) >>> + >>> +/* Configuration */ >>> +#define PCICONF(x) (0x010000 + ((x) * 0x4)) >>> +#define PMCAP(x) (0x010040 + ((x) * 0x4)) >>> +#define MSICAP(x) (0x010050 + ((x) * 0x4)) >>> +#define EXPCAP(x) (0x010070 + ((x) * 0x4)) >>> +#define VCCAP(x) (0x010100 + ((x) * 0x4)) >>> +#define SERNUMCAP(x) (0x0101b0 + ((x) * 0x4)) >>> + >>> +/* link layer */ >>> +#define IDSETR0 0x011000 >>> +#define IDSETR1 0x011004 >>> +#define SUBIDSETR 0x011024 >>> +#define DSERSETR0 0x01102c >>> +#define DSERSETR1 0x011030 >>> +#define TLCTLR 0x011048 >>> +#define MACSR 0x011054 >>> +#define MACCTLR 0x011058 >>> +#define SCRAMBLE_DISABLE (1 << 27) >>> + >>> +/* R-Car H1 PHY */ >>> +#define H1_PCIEPHYCTLR 0x040008 >>> +#define H1_PCIEPHYADRR 0x04000c >>> +#define WRITE_CMD (1 << 16) >>> +#define PHY_ACK (1 << 24) >>> +#define RATE_POS 12 >>> +#define LANE_POS 8 >>> +#define ADR_POS 0 >>> +#define H1_PCIEPHYDOUTR 0x040014 >>> +#define H1_PCIEPHYSR 0x040018 >>> + >>> +#endif /* __PCI_RCAR_H */ >>> >> >> >> -- >> Ben Dooks http://www.codethink.co.uk/ >> Senior Engineer Codethink - Providing Genius > > Thanks > Phil > -- > To unsubscribe from this list: send the line "unsubscribe linux-sh" in > the body of a message to majordomo@vger.kernel.org > More majordomo info at http://vger.kernel.org/majordomo-info.html > -- Ben Dooks http://www.codethink.co.uk/ Senior Engineer Codethink - Providing Genius ^ permalink raw reply [flat|nested] 16+ messages in thread
* Re: [PATCH 1/5] PCI: host: Add Renesas R-Car PCIe driver 2014-02-26 17:19 ` Ben Dooks @ 2014-02-26 17:34 ` Phil.Edworthy -1 siblings, 0 replies; 16+ messages in thread From: Phil.Edworthy @ 2014-02-26 17:34 UTC (permalink / raw) To: Ben Dooks Cc: Bjorn Helgaas, Simon Horman, linux-pci, linux-sh, Magnus Damm, Valentine Barshak Hi Ben, On 26/02/2014 17:19, Ben Dooks wrote: > On 26/02/14 17:13, Phil.Edworthy@renesas.com wrote: > > Hi Ben, > > > > Thanks for your comments. > > > > On 26/02/2014 16:33, Ben Dooks wrote: > >> On 26/02/14 16:08, Phil Edworthy wrote: > >>> This PCIe Host driver currently does not support MSI, so cards > >>> fall back to INTx interrupts. > >>> > >>> Signed-off-by: Phil Edworthy <phil.edworthy@renesas.com> > >> > >>> + bool "Renesas R-Car PCIe controller" > >>> + depends on ARM && (ARCH_R8A7790 || ARCH_R8A7791 || COMPILE_TEST) > >>> + help > >>> + Say Y here if you want PCIe controller support on R-Car Gen2 > > SoCs. > >>> + > >>> endmenu > >>> diff --git a/drivers/pci/host/Makefile b/drivers/pci/host/Makefile > >>> index 13fb333..19946f9 100644 > >>> --- a/drivers/pci/host/Makefile > >>> +++ b/drivers/pci/host/Makefile > >>> @@ -4,3 +4,4 @@ obj-$(CONFIG_PCI_IMX6) += pci-imx6.o > >>> obj-$(CONFIG_PCI_MVEBU) += pci-mvebu.o > >>> 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 > >>> diff --git a/drivers/pci/host/pcie-rcar.c > > b/drivers/pci/host/pcie-rcar.c > >>> new file mode 100644 > >>> index 0000000..d9a315f > >>> --- /dev/null > >>> +++ b/drivers/pci/host/pcie-rcar.c > >>> @@ -0,0 +1,614 @@ > >>> +/* > >>> + * PCIe driver for Renesas R-Car SoCs > >>> + * Copyright (C) 2013 Renesas Electronics Europe Ltd > >>> + * > >>> + * Based on: > >>> + * arch/sh/drivers/pci/pcie-sh7786.c > >>> + * arch/sh/drivers/pci/ops-sh7786.c > >>> + * Copyright (C) 2009 - 2011 Paul Mundt > >>> + * > >>> + * 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/module.h> > >>> +#include <linux/kernel.h> > >>> +#include <linux/of_address.h> > >>> +#include <linux/of_irq.h> > >>> +#include <linux/of_pci.h> > >>> +#include <linux/of_platform.h> > >>> +#include <linux/pci.h> > >>> +#include <linux/platform_device.h> > >>> +#include <linux/slab.h> > >>> +#include "pcie-rcar.h" > >>> + > >>> +#define DRV_NAME "rcar-pcie" > >>> + > >>> +enum chip_id { > >>> + RCAR_GENERIC, > >>> + RCAR_H1, > >>> +}; > >>> + > >>> +#define RCONF(x) (PCICONF(0)+(x)) > >>> +#define REXPCAP(x) (EXPCAP(0)+(x)) > >>> +#define RVCCAP(x) (VCCAP(0)+(x)) > >>> + > >>> +#define PCIE_CONF_BUS(b) (((b) & 0xff) << 24) > >>> +#define PCIE_CONF_DEV(d) (((d) & 0x1f) << 19) > >>> +#define PCIE_CONF_FUNC(f) (((f) & 0x7) << 16) > >>> + > >>> +#define NR_PCI_RESOURCES 4 > >>> + > >>> +/* Structure representing the PCIe interface */ > >>> +struct rcar_pcie { > >>> + struct device *dev; > >>> + void __iomem *base; > >>> + int irq; > >>> + struct clk *clk; > >>> + struct resource *res[NR_PCI_RESOURCES]; > >>> + int haslink; > >>> + enum chip_id chip; > >>> + u8 root_bus_nr; > >>> +}; > >>> + > >>> +static inline struct rcar_pcie *sys_to_pcie(struct pci_sys_data *sys) > >>> +{ > >>> + return sys->private_data; > >>> +} > >>> + > >>> +static void > >>> +pci_write_reg(struct rcar_pcie *pcie, unsigned long val, unsigned > > long reg) > >>> +{ > >>> + writel(val, pcie->base + reg); > >>> +} > >> > >> Do we really need wrappers like these? > > They were handy during development, but I guess not. > > Actually, not that big of a deal, ignore me on this. > > >>> +static unsigned long > >>> +pci_read_reg(struct rcar_pcie *pcie, unsigned long reg) > >>> +{ > >>> + return readl(pcie->base + reg); > >>> +} > >>> + > >>> +enum { > >>> + PCI_ACCESS_READ, > >>> + PCI_ACCESS_WRITE, > >>> +}; > >> > >>> +static int rcar_pcie_map_irq(const struct pci_dev *dev, u8 slot, u8 > > pin) > >>> +{ > >>> + struct rcar_pcie *pcie = sys_to_pcie(dev->bus->sysdata); > >>> + return pcie->irq; > >>> +} > >>> + > >>> +static void __init rcar_pcie_enable(struct rcar_pcie *pcie) > >>> +{ > >>> + struct hw_pci hw; > >>> + > >>> + memset(&hw, 0, sizeof(hw)); > >>> + > >>> + hw.nr_controllers = 1; > >>> + hw.private_data = (void **)&pcie; > >>> + hw.setup = rcar_pcie_setup, > >>> + hw.map_irq = rcar_pcie_map_irq, > >>> + hw.ops = &rcar_pcie_ops, > >>> + > >>> + pci_common_init(&hw); > >>> +} > >>> + > >>> +static int __init phy_wait_for_ack(struct rcar_pcie *pcie) > >>> +{ > >>> + unsigned int timeout = 100; > >>> + > >>> + while (timeout--) { > >>> + if (pci_read_reg(pcie, H1_PCIEPHYADRR) & PHY_ACK) > >>> + return 0; > >>> + > >>> + udelay(100); > >>> + } > >>> + > >>> + dev_err(pcie->dev, "Access to PCIe phy timed out\n"); > >>> + > >>> + return -ETIMEDOUT; > >>> +} > >> > >> Could this be done with some sort of sleep, instead of having > >> to keep delaying? How long is the average wait for pci to finish > >> a write? > > I haven't measured how long, but yes, I can use mdelay instead. > > How about something that sleeps? > Does the host have a completion IRQ you could sleep on? Not that I can see... The delay in phy_wait_for_ack() should be pretty small (and only applies to R-Car H1), so maybe we can live with a udelay there. For the other waits we can use msleep. > >>> + > >>> +static void __init phy_write_reg(struct rcar_pcie *pcie, > >>> + unsigned int rate, unsigned int addr, > >>> + unsigned int lane, unsigned int data) > >>> +{ > >>> + unsigned long phyaddr; > >>> + > >>> + phyaddr = WRITE_CMD | > >>> + ((rate & 1) << RATE_POS) | > >>> + ((lane & 0xf) << LANE_POS) | > >>> + ((addr & 0xff) << ADR_POS); > >>> + > >>> + /* Set write data */ > >>> + pci_write_reg(pcie, data, H1_PCIEPHYDOUTR); > >>> + pci_write_reg(pcie, phyaddr, H1_PCIEPHYADRR); > >>> + > >>> + /* Ignore errors as they will be dealt with if the data link is > > down */ > >>> + phy_wait_for_ack(pcie); > >>> + > >>> + /* Clear command */ > >>> + pci_write_reg(pcie, 0, H1_PCIEPHYDOUTR); > >>> + pci_write_reg(pcie, 0, H1_PCIEPHYADRR); > >>> + > >>> + /* Ignore errors as they will be dealt with if the data link is > > down */ > >>> + phy_wait_for_ack(pcie); > >>> +} > >>> + > >>> > >> > >>> +static int __init rcar_pcie_wait_for_dl(struct rcar_pcie *pcie) > >>> +{ > >>> + unsigned int timeout = 100; > >>> + > >>> + while (timeout--) { > >>> + if ((pci_read_reg(pcie, PCIETSTR) & DATA_LINK_ACTIVE)) > >>> + return 0; > >>> + > >>> + udelay(100); > >>> + } > >>> + > >>> + return -ETIMEDOUT; > >>> +} > >> > >> Same comments as for previous delay loop > >> > >> > >>> +static void __init rcar_pcie_hw_init(struct rcar_pcie *pcie) > >>> +{ > >>> + /* Initialise R-Car H1 PHY & wait for it to be ready */ > >>> + if (pcie->chip == RCAR_H1) > >>> + if (rcar_pcie_phy_init_rcar_h1(pcie)) > >>> + return; > >>> + > >>> + /* Begin initialization */ > >>> + pci_write_reg(pcie, 0, PCIETCTLR); > >>> + > >>> + /* Set mode */ > >>> + pci_write_reg(pcie, 1, PCIEMSR); > >>> + > >>> + /* > >>> + * For target transfers, setup a single 64-bit 4GB mapping at > > address > >>> + * 0. The hardware can only map memory that starts on a power of > > two > >>> + * boundary, and size is power of 2, so best to ignore it. > >>> + */ > >>> + pci_write_reg(pcie, 0, PCIEPRAR(0)); > >>> + pci_write_reg(pcie, 0, PCIELAR(0)); > >>> + pci_write_reg(pcie, 0xfffffff0UL | LAM_PREFETCH | > >>> + LAM_64BIT | LAR_ENABLE, PCIELAMR(0)); > >>> + pci_write_reg(pcie, 0, PCIELAR(1)); > >>> + pci_write_reg(pcie, 0, PCIEPRAR(1)); > >>> + pci_write_reg(pcie, 0, PCIELAMR(1)); > >>> + > >>> + /* > >>> + * Initial header for port config space is type 1, set the device > >>> + * class to match. Hardware takes care of propagating the IDSETR > >>> + * settings, so there is no need to bother with a quirk. > >>> + */ > >>> + pci_write_reg(pcie, PCI_CLASS_BRIDGE_PCI << 16, IDSETR1); > >>> + > >>> + /* > >>> + * Setup Secondary Bus Number & Subordinate Bus Number, even > > though > >>> + * they aren't used, to avoid bridge being detected as broken. > >>> + */ > >>> + rcar_rmw32(pcie, RCONF(PCI_SECONDARY_BUS), 0xff, 1); > >>> + rcar_rmw32(pcie, RCONF(PCI_SUBORDINATE_BUS), 0xff, 1); > >>> + > >>> + /* Initialize default capabilities. */ > >>> + rcar_rmw32(pcie, REXPCAP(0), 0, PCI_CAP_ID_EXP); > >>> + rcar_rmw32(pcie, REXPCAP(PCI_EXP_FLAGS), > >>> + PCI_EXP_FLAGS_TYPE, PCI_EXP_TYPE_ROOT_PORT << 4); > >>> + rcar_rmw32(pcie, RCONF(PCI_HEADER_TYPE), 0x7f, > >>> + PCI_HEADER_TYPE_BRIDGE); > >>> + > >>> + /* Enable data link layer active state reporting */ > >>> + rcar_rmw32(pcie, REXPCAP(PCI_EXP_LNKCAP), 0, > > PCI_EXP_LNKCAP_DLLLARC); > >>> + > >>> + /* Write out the physical slot number = 0 */ > >>> + rcar_rmw32(pcie, REXPCAP(PCI_EXP_SLTCAP), PCI_EXP_SLTCAP_PSN, 0); > >>> + > >>> + /* Set the completion timer timeout to the maximum 50ms. */ > >>> + rcar_rmw32(pcie, TLCTLR+1, 0x3f, 50); > >>> + > >>> + /* Terminate list of capabilities (Next Capability Offset=0) */ > >>> + rcar_rmw32(pcie, RVCCAP(0), 0xfff0, 0); > >>> + > >>> + /* Enable MAC data scrambling. */ > >>> + rcar_rmw32(pcie, MACCTLR, SCRAMBLE_DISABLE, 0); > >>> + > >>> + /* Finish initialization - establish a PCI Express link */ > >>> + pci_write_reg(pcie, CFINIT, PCIETCTLR); > >>> + > >>> + /* This will timeout if we don't have a link. */ > >>> + pcie->haslink = !rcar_pcie_wait_for_dl(pcie); > >>> + > >>> + /* Enable INTx interrupts */ > >>> + rcar_rmw32(pcie, RCONF(PCI_INTERRUPT_LINE), 0xff, 0); > >>> + rcar_rmw32(pcie, RCONF(PCI_INTERRUPT_PIN), 0xff, 1); > >>> + rcar_rmw32(pcie, PCIEINTXR, 0, 0xF << 8); > >>> + > >>> + /* Enable slave Bus Mastering */ > >>> + rcar_rmw32(pcie, RCONF(PCI_STATUS), PCI_STATUS_DEVSEL_MASK, > >>> + PCI_COMMAND_IO | PCI_COMMAND_MEMORY | PCI_COMMAND_MASTER | > >>> + PCI_STATUS_CAP_LIST | PCI_STATUS_DEVSEL_FAST); > >>> + > >>> + wmb(); > >>> +} > >>> + > >>> +static int rcar_pcie_clocks_get(struct rcar_pcie *pcie) > >>> +{ > >>> + int err = 0; > >>> + > >>> + pcie->clk = clk_get(pcie->dev, NULL); > >>> + if (IS_ERR(pcie->clk)) > >>> + err = PTR_ERR(pcie->clk); > >>> + else > >>> + clk_enable(pcie->clk); > >>> + > >>> + return err; > >>> +} > >> > >> Shouldn't you be using pm_runtime for this? > > Yes, I'll fix this > > Ta. I'm still trying to fix pm_runtime issues with devicetree. > > >>> +static void rcar_pcie_clocks_put(struct rcar_pcie *pcie) > >>> +{ > >>> + clk_put(pcie->clk); > >>> +} > >>> + > >>> +struct rcar_pcie_errs { > >>> + int bit; > >>> + const char *description; > >>> +}; > >>> + > >>> +static int __init rcar_pcie_get_resources(struct platform_device > > *pdev, > >>> + struct rcar_pcie *pcie) > >>> +{ > >>> + struct resource *res; > >>> + int i; > >>> + int err; > >>> + > >>> + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); > >>> + > >>> + i = platform_get_irq(pdev, 0); > >>> + if (!res || i < 0) { > >>> + dev_err(pcie->dev, "cannot get platform resources\n"); > >>> + return -ENOENT; > >>> + } > >>> + pcie->irq = i; > >>> + > >>> + err = rcar_pcie_clocks_get(pcie); > >>> + if (err) { > >>> + dev_err(&pdev->dev, "failed to get clocks: %d\n", err); > >>> + return err; > >>> + } > >>> + > >>> + pcie->base = devm_request_and_ioremap(&pdev->dev, res); > >>> + if (!pcie->base) { > >>> + dev_err(&pdev->dev, "failed to map PCIe registers: %d\n", err); > >>> + err = -ENOMEM; > >>> + goto err_map_reg; > >>> + } > >>> + > >>> + return 0; > >>> + > >>> +err_map_reg: > >>> + rcar_pcie_clocks_put(pcie); > >>> + > >>> + return err; > >>> +} > >>> + > >>> +static struct platform_device_id rcar_pcie_id_table[] = { > >>> + { "r8a7779-pcie", RCAR_H1 }, > >>> + { "r8a7790-pcie", RCAR_GENERIC }, > >>> + { "r8a7791-pcie", RCAR_GENERIC }, > >>> + {}, > >>> +}; > >>> +MODULE_DEVICE_TABLE(platform, rcar_pcie_id_table); > >> > >> Really, are you still going to submit drivers without OF bindings? > >> > >> NAK! > > I totally agree, hence my comment in the cover email: > > "This is RFC as there is some work required for DT support." > > Perhaps I should have marked each patch as RFC. > > Actually, it could be applied as-is, but I would prefer DT. > > I didn't notice, I only reviewed this bet, so yes marking the > lot as RFC would have been better. I'll try to remember next time! > >>> +static int __init rcar_pcie_probe(struct platform_device *pdev) > >>> +{ > >>> + struct rcar_pcie *pcie; > >>> + struct resource *res; > >>> + unsigned int data; > >>> + int i, err; > >>> + > >>> + pcie = devm_kzalloc(&pdev->dev, sizeof(*pcie), GFP_KERNEL); > >>> + if (!pcie) > >>> + return -ENOMEM; > >>> + > >>> + pcie->dev = &pdev->dev; > >>> + pcie->chip = pdev->id_entry->driver_data; > >>> + > >>> + /* Get resources */ > >>> + err = rcar_pcie_get_resources(pdev, pcie); > >>> + if (err < 0) { > >>> + dev_err(&pdev->dev, "failed to request resources: %d\n", err); > >>> + return err; > >>> + } > >>> + > >>> + /* Get the I/O and memory ranges */ > >>> + for (i = 0; i < NR_PCI_RESOURCES; i++) { > >>> + res = platform_get_resource(pdev, IORESOURCE_MEM, i+1); > >>> + if (!res) { > >>> + dev_err(&pdev->dev, "cannot get MEM%d resources\n", i); > >>> + return -ENOENT; > >>> + } > >>> + pcie->res[i] = res; > >>> + } > >>> + > >>> + rcar_pcie_hw_init(pcie); > >>> + > >>> + if (!pcie->haslink) { > >>> + dev_info(&pdev->dev, "PCI: PCIe link down\n"); > >>> + return 0; > >>> + } > >>> + > >>> + data = pci_read_reg(pcie, MACSR); > >>> + dev_info(&pdev->dev, "PCIe x%d: link up\n", (data >> 20) & 0x3f); > >>> + > >>> + rcar_pcie_enable(pcie); > >>> + > >>> + platform_set_drvdata(pdev, pcie); > >>> + return 0; > >>> +} > >>> + > >>> +static struct platform_driver rcar_pcie_driver = { > >>> + .probe = rcar_pcie_probe, > >>> + .driver = { > >>> + .name = DRV_NAME, > >>> + .owner = THIS_MODULE, > >>> + }, > >>> + .id_table = rcar_pcie_id_table, > >>> +}; > >>> + > >>> +module_platform_driver(rcar_pcie_driver); > >>> + > >>> +MODULE_AUTHOR("Phil Edworthy <phil.edworthy@renesas.com>"); > >>> +MODULE_DESCRIPTION("Renesas R-Car PCIe driver"); > >>> +MODULE_LICENSE("GPLv2"); > >>> diff --git a/drivers/pci/host/pcie-rcar.h > > b/drivers/pci/host/pcie-rcar.h > >>> new file mode 100644 > >>> index 0000000..a6b407f > >>> --- /dev/null > >>> +++ b/drivers/pci/host/pcie-rcar.h > >>> @@ -0,0 +1,84 @@ > >>> +/* > >>> + * PCI Express definitions for Renesas R-Car SoCs > >>> + */ > >>> +#ifndef __PCI_RCAR_H > >>> +#define __PCI_RCAR_H > >>> + > >>> +#define PCI_DEVICE_ID_RENESAS_RCAR 0x0018 > >> > >> should this go into pci ids header? > > This is not used so I'll remove it. > > > >>> +#define PCIECAR 0x000010 > >>> +#define PCIECCTLR 0x000018 > >>> +#define CONFIG_SEND_ENABLE (1 << 31) > >>> +#define TYPE0 (0 << 8) > >>> +#define TYPE1 (1 << 8) > >>> +#define PCIECDR 0x000020 > >>> +#define PCIEMSR 0x000028 > >>> +#define PCIEINTXR 0x000400 > >>> +#define PCIEPHYSR 0x0007f0 > >>> + > >>> +/* Transfer control */ > >>> +#define PCIETCTLR 0x02000 > >>> +#define CFINIT 1 > >>> +#define PCIETSTR 0x02004 > >>> +#define DATA_LINK_ACTIVE 1 > >>> +#define PCIEINTR 0x02008 > >>> +#define PCIEINTER 0x0200c > >>> +#define PCIEERRFR 0x02020 > >>> +#define UNSUPPORTED_REQUEST (1 << 4) > >>> +#define PCIEERRFER 0x02024 > >>> +#define PCIEERRFR2 0x02028 > >>> +#define PCIEPMSR 0x02034 > >>> +#define PCIEPMSCIER 0x02038 > >>> +#define PCIEMSIFR 0x02044 > >>> + > >>> +/* root port address */ > >>> +#define PCIEPRAR(x) (0x02080 + ((x) * 0x4)) > >>> + > >>> +/* local address reg & mask */ > >>> +#define PCIELAR(x) (0x02200 + ((x) * 0x20)) > >>> +#define PCIELAMR(x) (0x02208 + ((x) * 0x20)) > >>> +#define LAM_PMIOLAMnB3 (1 << 3) > >>> +#define LAM_PMIOLAMnB2 (1 << 2) > >>> +#define LAM_PREFETCH (1 << 3) > >>> +#define LAM_64BIT (1 << 2) > >>> +#define LAR_ENABLE (1 << 1) > >>> + > >>> +/* PCIe address reg & mask */ > >>> +#define PCIEPARL(x) (0x03400 + ((x) * 0x20)) > >>> +#define PCIEPARH(x) (0x03404 + ((x) * 0x20)) > >>> +#define PCIEPAMR(x) (0x03408 + ((x) * 0x20)) > >>> +#define PCIEPTCTLR(x) (0x0340c + ((x) * 0x20)) > >>> +#define PAR_ENABLE (1 << 31) > >>> +#define IO_SPACE (1 << 8) > >>> + > >>> +/* Configuration */ > >>> +#define PCICONF(x) (0x010000 + ((x) * 0x4)) > >>> +#define PMCAP(x) (0x010040 + ((x) * 0x4)) > >>> +#define MSICAP(x) (0x010050 + ((x) * 0x4)) > >>> +#define EXPCAP(x) (0x010070 + ((x) * 0x4)) > >>> +#define VCCAP(x) (0x010100 + ((x) * 0x4)) > >>> +#define SERNUMCAP(x) (0x0101b0 + ((x) * 0x4)) > >>> + > >>> +/* link layer */ > >>> +#define IDSETR0 0x011000 > >>> +#define IDSETR1 0x011004 > >>> +#define SUBIDSETR 0x011024 > >>> +#define DSERSETR0 0x01102c > >>> +#define DSERSETR1 0x011030 > >>> +#define TLCTLR 0x011048 > >>> +#define MACSR 0x011054 > >>> +#define MACCTLR 0x011058 > >>> +#define SCRAMBLE_DISABLE (1 << 27) > >>> + > >>> +/* R-Car H1 PHY */ > >>> +#define H1_PCIEPHYCTLR 0x040008 > >>> +#define H1_PCIEPHYADRR 0x04000c > >>> +#define WRITE_CMD (1 << 16) > >>> +#define PHY_ACK (1 << 24) > >>> +#define RATE_POS 12 > >>> +#define LANE_POS 8 > >>> +#define ADR_POS 0 > >>> +#define H1_PCIEPHYDOUTR 0x040014 > >>> +#define H1_PCIEPHYSR 0x040018 > >>> + > >>> +#endif /* __PCI_RCAR_H */ > >>> > >> > >> > >> -- > >> Ben Dooks http://www.codethink.co.uk/ > >> Senior Engineer Codethink - Providing Genius > > > > Thanks > > Phil > > -- > > To unsubscribe from this list: send the line "unsubscribe linux-sh" in > > the body of a message to majordomo@vger.kernel.org > > More majordomo info at http://vger.kernel.org/majordomo-info.html > > > > > -- > Ben Dooks http://www.codethink.co.uk/ > Senior Engineer Codethink - Providing Genius Thanks Phil ^ permalink raw reply [flat|nested] 16+ messages in thread
* Re: [PATCH 1/5] PCI: host: Add Renesas R-Car PCIe driver @ 2014-02-26 17:34 ` Phil.Edworthy 0 siblings, 0 replies; 16+ messages in thread From: Phil.Edworthy @ 2014-02-26 17:34 UTC (permalink / raw) To: Ben Dooks Cc: Bjorn Helgaas, Simon Horman, linux-pci, linux-sh, Magnus Damm, Valentine Barshak Hi Ben, On 26/02/2014 17:19, Ben Dooks wrote: > On 26/02/14 17:13, Phil.Edworthy@renesas.com wrote: > > Hi Ben, > > > > Thanks for your comments. > > > > On 26/02/2014 16:33, Ben Dooks wrote: > >> On 26/02/14 16:08, Phil Edworthy wrote: > >>> This PCIe Host driver currently does not support MSI, so cards > >>> fall back to INTx interrupts. > >>> > >>> Signed-off-by: Phil Edworthy <phil.edworthy@renesas.com> > >> > >>> + bool "Renesas R-Car PCIe controller" > >>> + depends on ARM && (ARCH_R8A7790 || ARCH_R8A7791 || COMPILE_TEST) > >>> + help > >>> + Say Y here if you want PCIe controller support on R-Car Gen2 > > SoCs. > >>> + > >>> endmenu > >>> diff --git a/drivers/pci/host/Makefile b/drivers/pci/host/Makefile > >>> index 13fb333..19946f9 100644 > >>> --- a/drivers/pci/host/Makefile > >>> +++ b/drivers/pci/host/Makefile > >>> @@ -4,3 +4,4 @@ obj-$(CONFIG_PCI_IMX6) += pci-imx6.o > >>> obj-$(CONFIG_PCI_MVEBU) += pci-mvebu.o > >>> 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 > >>> diff --git a/drivers/pci/host/pcie-rcar.c > > b/drivers/pci/host/pcie-rcar.c > >>> new file mode 100644 > >>> index 0000000..d9a315f > >>> --- /dev/null > >>> +++ b/drivers/pci/host/pcie-rcar.c > >>> @@ -0,0 +1,614 @@ > >>> +/* > >>> + * PCIe driver for Renesas R-Car SoCs > >>> + * Copyright (C) 2013 Renesas Electronics Europe Ltd > >>> + * > >>> + * Based on: > >>> + * arch/sh/drivers/pci/pcie-sh7786.c > >>> + * arch/sh/drivers/pci/ops-sh7786.c > >>> + * Copyright (C) 2009 - 2011 Paul Mundt > >>> + * > >>> + * 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/module.h> > >>> +#include <linux/kernel.h> > >>> +#include <linux/of_address.h> > >>> +#include <linux/of_irq.h> > >>> +#include <linux/of_pci.h> > >>> +#include <linux/of_platform.h> > >>> +#include <linux/pci.h> > >>> +#include <linux/platform_device.h> > >>> +#include <linux/slab.h> > >>> +#include "pcie-rcar.h" > >>> + > >>> +#define DRV_NAME "rcar-pcie" > >>> + > >>> +enum chip_id { > >>> + RCAR_GENERIC, > >>> + RCAR_H1, > >>> +}; > >>> + > >>> +#define RCONF(x) (PCICONF(0)+(x)) > >>> +#define REXPCAP(x) (EXPCAP(0)+(x)) > >>> +#define RVCCAP(x) (VCCAP(0)+(x)) > >>> + > >>> +#define PCIE_CONF_BUS(b) (((b) & 0xff) << 24) > >>> +#define PCIE_CONF_DEV(d) (((d) & 0x1f) << 19) > >>> +#define PCIE_CONF_FUNC(f) (((f) & 0x7) << 16) > >>> + > >>> +#define NR_PCI_RESOURCES 4 > >>> + > >>> +/* Structure representing the PCIe interface */ > >>> +struct rcar_pcie { > >>> + struct device *dev; > >>> + void __iomem *base; > >>> + int irq; > >>> + struct clk *clk; > >>> + struct resource *res[NR_PCI_RESOURCES]; > >>> + int haslink; > >>> + enum chip_id chip; > >>> + u8 root_bus_nr; > >>> +}; > >>> + > >>> +static inline struct rcar_pcie *sys_to_pcie(struct pci_sys_data *sys) > >>> +{ > >>> + return sys->private_data; > >>> +} > >>> + > >>> +static void > >>> +pci_write_reg(struct rcar_pcie *pcie, unsigned long val, unsigned > > long reg) > >>> +{ > >>> + writel(val, pcie->base + reg); > >>> +} > >> > >> Do we really need wrappers like these? > > They were handy during development, but I guess not. > > Actually, not that big of a deal, ignore me on this. > > >>> +static unsigned long > >>> +pci_read_reg(struct rcar_pcie *pcie, unsigned long reg) > >>> +{ > >>> + return readl(pcie->base + reg); > >>> +} > >>> + > >>> +enum { > >>> + PCI_ACCESS_READ, > >>> + PCI_ACCESS_WRITE, > >>> +}; > >> > >>> +static int rcar_pcie_map_irq(const struct pci_dev *dev, u8 slot, u8 > > pin) > >>> +{ > >>> + struct rcar_pcie *pcie = sys_to_pcie(dev->bus->sysdata); > >>> + return pcie->irq; > >>> +} > >>> + > >>> +static void __init rcar_pcie_enable(struct rcar_pcie *pcie) > >>> +{ > >>> + struct hw_pci hw; > >>> + > >>> + memset(&hw, 0, sizeof(hw)); > >>> + > >>> + hw.nr_controllers = 1; > >>> + hw.private_data = (void **)&pcie; > >>> + hw.setup = rcar_pcie_setup, > >>> + hw.map_irq = rcar_pcie_map_irq, > >>> + hw.ops = &rcar_pcie_ops, > >>> + > >>> + pci_common_init(&hw); > >>> +} > >>> + > >>> +static int __init phy_wait_for_ack(struct rcar_pcie *pcie) > >>> +{ > >>> + unsigned int timeout = 100; > >>> + > >>> + while (timeout--) { > >>> + if (pci_read_reg(pcie, H1_PCIEPHYADRR) & PHY_ACK) > >>> + return 0; > >>> + > >>> + udelay(100); > >>> + } > >>> + > >>> + dev_err(pcie->dev, "Access to PCIe phy timed out\n"); > >>> + > >>> + return -ETIMEDOUT; > >>> +} > >> > >> Could this be done with some sort of sleep, instead of having > >> to keep delaying? How long is the average wait for pci to finish > >> a write? > > I haven't measured how long, but yes, I can use mdelay instead. > > How about something that sleeps? > Does the host have a completion IRQ you could sleep on? Not that I can see... The delay in phy_wait_for_ack() should be pretty small (and only applies to R-Car H1), so maybe we can live with a udelay there. For the other waits we can use msleep. > >>> + > >>> +static void __init phy_write_reg(struct rcar_pcie *pcie, > >>> + unsigned int rate, unsigned int addr, > >>> + unsigned int lane, unsigned int data) > >>> +{ > >>> + unsigned long phyaddr; > >>> + > >>> + phyaddr = WRITE_CMD | > >>> + ((rate & 1) << RATE_POS) | > >>> + ((lane & 0xf) << LANE_POS) | > >>> + ((addr & 0xff) << ADR_POS); > >>> + > >>> + /* Set write data */ > >>> + pci_write_reg(pcie, data, H1_PCIEPHYDOUTR); > >>> + pci_write_reg(pcie, phyaddr, H1_PCIEPHYADRR); > >>> + > >>> + /* Ignore errors as they will be dealt with if the data link is > > down */ > >>> + phy_wait_for_ack(pcie); > >>> + > >>> + /* Clear command */ > >>> + pci_write_reg(pcie, 0, H1_PCIEPHYDOUTR); > >>> + pci_write_reg(pcie, 0, H1_PCIEPHYADRR); > >>> + > >>> + /* Ignore errors as they will be dealt with if the data link is > > down */ > >>> + phy_wait_for_ack(pcie); > >>> +} > >>> + > >>> > >> > >>> +static int __init rcar_pcie_wait_for_dl(struct rcar_pcie *pcie) > >>> +{ > >>> + unsigned int timeout = 100; > >>> + > >>> + while (timeout--) { > >>> + if ((pci_read_reg(pcie, PCIETSTR) & DATA_LINK_ACTIVE)) > >>> + return 0; > >>> + > >>> + udelay(100); > >>> + } > >>> + > >>> + return -ETIMEDOUT; > >>> +} > >> > >> Same comments as for previous delay loop > >> > >> > >>> +static void __init rcar_pcie_hw_init(struct rcar_pcie *pcie) > >>> +{ > >>> + /* Initialise R-Car H1 PHY & wait for it to be ready */ > >>> + if (pcie->chip = RCAR_H1) > >>> + if (rcar_pcie_phy_init_rcar_h1(pcie)) > >>> + return; > >>> + > >>> + /* Begin initialization */ > >>> + pci_write_reg(pcie, 0, PCIETCTLR); > >>> + > >>> + /* Set mode */ > >>> + pci_write_reg(pcie, 1, PCIEMSR); > >>> + > >>> + /* > >>> + * For target transfers, setup a single 64-bit 4GB mapping at > > address > >>> + * 0. The hardware can only map memory that starts on a power of > > two > >>> + * boundary, and size is power of 2, so best to ignore it. > >>> + */ > >>> + pci_write_reg(pcie, 0, PCIEPRAR(0)); > >>> + pci_write_reg(pcie, 0, PCIELAR(0)); > >>> + pci_write_reg(pcie, 0xfffffff0UL | LAM_PREFETCH | > >>> + LAM_64BIT | LAR_ENABLE, PCIELAMR(0)); > >>> + pci_write_reg(pcie, 0, PCIELAR(1)); > >>> + pci_write_reg(pcie, 0, PCIEPRAR(1)); > >>> + pci_write_reg(pcie, 0, PCIELAMR(1)); > >>> + > >>> + /* > >>> + * Initial header for port config space is type 1, set the device > >>> + * class to match. Hardware takes care of propagating the IDSETR > >>> + * settings, so there is no need to bother with a quirk. > >>> + */ > >>> + pci_write_reg(pcie, PCI_CLASS_BRIDGE_PCI << 16, IDSETR1); > >>> + > >>> + /* > >>> + * Setup Secondary Bus Number & Subordinate Bus Number, even > > though > >>> + * they aren't used, to avoid bridge being detected as broken. > >>> + */ > >>> + rcar_rmw32(pcie, RCONF(PCI_SECONDARY_BUS), 0xff, 1); > >>> + rcar_rmw32(pcie, RCONF(PCI_SUBORDINATE_BUS), 0xff, 1); > >>> + > >>> + /* Initialize default capabilities. */ > >>> + rcar_rmw32(pcie, REXPCAP(0), 0, PCI_CAP_ID_EXP); > >>> + rcar_rmw32(pcie, REXPCAP(PCI_EXP_FLAGS), > >>> + PCI_EXP_FLAGS_TYPE, PCI_EXP_TYPE_ROOT_PORT << 4); > >>> + rcar_rmw32(pcie, RCONF(PCI_HEADER_TYPE), 0x7f, > >>> + PCI_HEADER_TYPE_BRIDGE); > >>> + > >>> + /* Enable data link layer active state reporting */ > >>> + rcar_rmw32(pcie, REXPCAP(PCI_EXP_LNKCAP), 0, > > PCI_EXP_LNKCAP_DLLLARC); > >>> + > >>> + /* Write out the physical slot number = 0 */ > >>> + rcar_rmw32(pcie, REXPCAP(PCI_EXP_SLTCAP), PCI_EXP_SLTCAP_PSN, 0); > >>> + > >>> + /* Set the completion timer timeout to the maximum 50ms. */ > >>> + rcar_rmw32(pcie, TLCTLR+1, 0x3f, 50); > >>> + > >>> + /* Terminate list of capabilities (Next Capability Offset=0) */ > >>> + rcar_rmw32(pcie, RVCCAP(0), 0xfff0, 0); > >>> + > >>> + /* Enable MAC data scrambling. */ > >>> + rcar_rmw32(pcie, MACCTLR, SCRAMBLE_DISABLE, 0); > >>> + > >>> + /* Finish initialization - establish a PCI Express link */ > >>> + pci_write_reg(pcie, CFINIT, PCIETCTLR); > >>> + > >>> + /* This will timeout if we don't have a link. */ > >>> + pcie->haslink = !rcar_pcie_wait_for_dl(pcie); > >>> + > >>> + /* Enable INTx interrupts */ > >>> + rcar_rmw32(pcie, RCONF(PCI_INTERRUPT_LINE), 0xff, 0); > >>> + rcar_rmw32(pcie, RCONF(PCI_INTERRUPT_PIN), 0xff, 1); > >>> + rcar_rmw32(pcie, PCIEINTXR, 0, 0xF << 8); > >>> + > >>> + /* Enable slave Bus Mastering */ > >>> + rcar_rmw32(pcie, RCONF(PCI_STATUS), PCI_STATUS_DEVSEL_MASK, > >>> + PCI_COMMAND_IO | PCI_COMMAND_MEMORY | PCI_COMMAND_MASTER | > >>> + PCI_STATUS_CAP_LIST | PCI_STATUS_DEVSEL_FAST); > >>> + > >>> + wmb(); > >>> +} > >>> + > >>> +static int rcar_pcie_clocks_get(struct rcar_pcie *pcie) > >>> +{ > >>> + int err = 0; > >>> + > >>> + pcie->clk = clk_get(pcie->dev, NULL); > >>> + if (IS_ERR(pcie->clk)) > >>> + err = PTR_ERR(pcie->clk); > >>> + else > >>> + clk_enable(pcie->clk); > >>> + > >>> + return err; > >>> +} > >> > >> Shouldn't you be using pm_runtime for this? > > Yes, I'll fix this > > Ta. I'm still trying to fix pm_runtime issues with devicetree. > > >>> +static void rcar_pcie_clocks_put(struct rcar_pcie *pcie) > >>> +{ > >>> + clk_put(pcie->clk); > >>> +} > >>> + > >>> +struct rcar_pcie_errs { > >>> + int bit; > >>> + const char *description; > >>> +}; > >>> + > >>> +static int __init rcar_pcie_get_resources(struct platform_device > > *pdev, > >>> + struct rcar_pcie *pcie) > >>> +{ > >>> + struct resource *res; > >>> + int i; > >>> + int err; > >>> + > >>> + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); > >>> + > >>> + i = platform_get_irq(pdev, 0); > >>> + if (!res || i < 0) { > >>> + dev_err(pcie->dev, "cannot get platform resources\n"); > >>> + return -ENOENT; > >>> + } > >>> + pcie->irq = i; > >>> + > >>> + err = rcar_pcie_clocks_get(pcie); > >>> + if (err) { > >>> + dev_err(&pdev->dev, "failed to get clocks: %d\n", err); > >>> + return err; > >>> + } > >>> + > >>> + pcie->base = devm_request_and_ioremap(&pdev->dev, res); > >>> + if (!pcie->base) { > >>> + dev_err(&pdev->dev, "failed to map PCIe registers: %d\n", err); > >>> + err = -ENOMEM; > >>> + goto err_map_reg; > >>> + } > >>> + > >>> + return 0; > >>> + > >>> +err_map_reg: > >>> + rcar_pcie_clocks_put(pcie); > >>> + > >>> + return err; > >>> +} > >>> + > >>> +static struct platform_device_id rcar_pcie_id_table[] = { > >>> + { "r8a7779-pcie", RCAR_H1 }, > >>> + { "r8a7790-pcie", RCAR_GENERIC }, > >>> + { "r8a7791-pcie", RCAR_GENERIC }, > >>> + {}, > >>> +}; > >>> +MODULE_DEVICE_TABLE(platform, rcar_pcie_id_table); > >> > >> Really, are you still going to submit drivers without OF bindings? > >> > >> NAK! > > I totally agree, hence my comment in the cover email: > > "This is RFC as there is some work required for DT support." > > Perhaps I should have marked each patch as RFC. > > Actually, it could be applied as-is, but I would prefer DT. > > I didn't notice, I only reviewed this bet, so yes marking the > lot as RFC would have been better. I'll try to remember next time! > >>> +static int __init rcar_pcie_probe(struct platform_device *pdev) > >>> +{ > >>> + struct rcar_pcie *pcie; > >>> + struct resource *res; > >>> + unsigned int data; > >>> + int i, err; > >>> + > >>> + pcie = devm_kzalloc(&pdev->dev, sizeof(*pcie), GFP_KERNEL); > >>> + if (!pcie) > >>> + return -ENOMEM; > >>> + > >>> + pcie->dev = &pdev->dev; > >>> + pcie->chip = pdev->id_entry->driver_data; > >>> + > >>> + /* Get resources */ > >>> + err = rcar_pcie_get_resources(pdev, pcie); > >>> + if (err < 0) { > >>> + dev_err(&pdev->dev, "failed to request resources: %d\n", err); > >>> + return err; > >>> + } > >>> + > >>> + /* Get the I/O and memory ranges */ > >>> + for (i = 0; i < NR_PCI_RESOURCES; i++) { > >>> + res = platform_get_resource(pdev, IORESOURCE_MEM, i+1); > >>> + if (!res) { > >>> + dev_err(&pdev->dev, "cannot get MEM%d resources\n", i); > >>> + return -ENOENT; > >>> + } > >>> + pcie->res[i] = res; > >>> + } > >>> + > >>> + rcar_pcie_hw_init(pcie); > >>> + > >>> + if (!pcie->haslink) { > >>> + dev_info(&pdev->dev, "PCI: PCIe link down\n"); > >>> + return 0; > >>> + } > >>> + > >>> + data = pci_read_reg(pcie, MACSR); > >>> + dev_info(&pdev->dev, "PCIe x%d: link up\n", (data >> 20) & 0x3f); > >>> + > >>> + rcar_pcie_enable(pcie); > >>> + > >>> + platform_set_drvdata(pdev, pcie); > >>> + return 0; > >>> +} > >>> + > >>> +static struct platform_driver rcar_pcie_driver = { > >>> + .probe = rcar_pcie_probe, > >>> + .driver = { > >>> + .name = DRV_NAME, > >>> + .owner = THIS_MODULE, > >>> + }, > >>> + .id_table = rcar_pcie_id_table, > >>> +}; > >>> + > >>> +module_platform_driver(rcar_pcie_driver); > >>> + > >>> +MODULE_AUTHOR("Phil Edworthy <phil.edworthy@renesas.com>"); > >>> +MODULE_DESCRIPTION("Renesas R-Car PCIe driver"); > >>> +MODULE_LICENSE("GPLv2"); > >>> diff --git a/drivers/pci/host/pcie-rcar.h > > b/drivers/pci/host/pcie-rcar.h > >>> new file mode 100644 > >>> index 0000000..a6b407f > >>> --- /dev/null > >>> +++ b/drivers/pci/host/pcie-rcar.h > >>> @@ -0,0 +1,84 @@ > >>> +/* > >>> + * PCI Express definitions for Renesas R-Car SoCs > >>> + */ > >>> +#ifndef __PCI_RCAR_H > >>> +#define __PCI_RCAR_H > >>> + > >>> +#define PCI_DEVICE_ID_RENESAS_RCAR 0x0018 > >> > >> should this go into pci ids header? > > This is not used so I'll remove it. > > > >>> +#define PCIECAR 0x000010 > >>> +#define PCIECCTLR 0x000018 > >>> +#define CONFIG_SEND_ENABLE (1 << 31) > >>> +#define TYPE0 (0 << 8) > >>> +#define TYPE1 (1 << 8) > >>> +#define PCIECDR 0x000020 > >>> +#define PCIEMSR 0x000028 > >>> +#define PCIEINTXR 0x000400 > >>> +#define PCIEPHYSR 0x0007f0 > >>> + > >>> +/* Transfer control */ > >>> +#define PCIETCTLR 0x02000 > >>> +#define CFINIT 1 > >>> +#define PCIETSTR 0x02004 > >>> +#define DATA_LINK_ACTIVE 1 > >>> +#define PCIEINTR 0x02008 > >>> +#define PCIEINTER 0x0200c > >>> +#define PCIEERRFR 0x02020 > >>> +#define UNSUPPORTED_REQUEST (1 << 4) > >>> +#define PCIEERRFER 0x02024 > >>> +#define PCIEERRFR2 0x02028 > >>> +#define PCIEPMSR 0x02034 > >>> +#define PCIEPMSCIER 0x02038 > >>> +#define PCIEMSIFR 0x02044 > >>> + > >>> +/* root port address */ > >>> +#define PCIEPRAR(x) (0x02080 + ((x) * 0x4)) > >>> + > >>> +/* local address reg & mask */ > >>> +#define PCIELAR(x) (0x02200 + ((x) * 0x20)) > >>> +#define PCIELAMR(x) (0x02208 + ((x) * 0x20)) > >>> +#define LAM_PMIOLAMnB3 (1 << 3) > >>> +#define LAM_PMIOLAMnB2 (1 << 2) > >>> +#define LAM_PREFETCH (1 << 3) > >>> +#define LAM_64BIT (1 << 2) > >>> +#define LAR_ENABLE (1 << 1) > >>> + > >>> +/* PCIe address reg & mask */ > >>> +#define PCIEPARL(x) (0x03400 + ((x) * 0x20)) > >>> +#define PCIEPARH(x) (0x03404 + ((x) * 0x20)) > >>> +#define PCIEPAMR(x) (0x03408 + ((x) * 0x20)) > >>> +#define PCIEPTCTLR(x) (0x0340c + ((x) * 0x20)) > >>> +#define PAR_ENABLE (1 << 31) > >>> +#define IO_SPACE (1 << 8) > >>> + > >>> +/* Configuration */ > >>> +#define PCICONF(x) (0x010000 + ((x) * 0x4)) > >>> +#define PMCAP(x) (0x010040 + ((x) * 0x4)) > >>> +#define MSICAP(x) (0x010050 + ((x) * 0x4)) > >>> +#define EXPCAP(x) (0x010070 + ((x) * 0x4)) > >>> +#define VCCAP(x) (0x010100 + ((x) * 0x4)) > >>> +#define SERNUMCAP(x) (0x0101b0 + ((x) * 0x4)) > >>> + > >>> +/* link layer */ > >>> +#define IDSETR0 0x011000 > >>> +#define IDSETR1 0x011004 > >>> +#define SUBIDSETR 0x011024 > >>> +#define DSERSETR0 0x01102c > >>> +#define DSERSETR1 0x011030 > >>> +#define TLCTLR 0x011048 > >>> +#define MACSR 0x011054 > >>> +#define MACCTLR 0x011058 > >>> +#define SCRAMBLE_DISABLE (1 << 27) > >>> + > >>> +/* R-Car H1 PHY */ > >>> +#define H1_PCIEPHYCTLR 0x040008 > >>> +#define H1_PCIEPHYADRR 0x04000c > >>> +#define WRITE_CMD (1 << 16) > >>> +#define PHY_ACK (1 << 24) > >>> +#define RATE_POS 12 > >>> +#define LANE_POS 8 > >>> +#define ADR_POS 0 > >>> +#define H1_PCIEPHYDOUTR 0x040014 > >>> +#define H1_PCIEPHYSR 0x040018 > >>> + > >>> +#endif /* __PCI_RCAR_H */ > >>> > >> > >> > >> -- > >> Ben Dooks http://www.codethink.co.uk/ > >> Senior Engineer Codethink - Providing Genius > > > > Thanks > > Phil > > -- > > To unsubscribe from this list: send the line "unsubscribe linux-sh" in > > the body of a message to majordomo@vger.kernel.org > > More majordomo info at http://vger.kernel.org/majordomo-info.html > > > > > -- > Ben Dooks http://www.codethink.co.uk/ > Senior Engineer Codethink - Providing Genius Thanks Phil ^ permalink raw reply [flat|nested] 16+ messages in thread
* [PATCH 2/5] ARM: shmobile: r8a7791: Add PCIe clock 2014-02-26 16:08 ` Phil Edworthy (?) (?) @ 2014-02-26 16:08 ` Phil Edworthy -1 siblings, 0 replies; 16+ messages in thread From: Phil Edworthy @ 2014-02-26 16:08 UTC (permalink / raw) To: linux-pci Cc: linux-sh, Bjorn Helgaas, Valentine Barshak, Simon Horman, Magnus Damm, Phil Edworthy Signed-off-by: Phil Edworthy <phil.edworthy@renesas.com> --- arch/arm/mach-shmobile/clock-r8a7791.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/arch/arm/mach-shmobile/clock-r8a7791.c b/arch/arm/mach-shmobile/clock-r8a7791.c index c574292..cba9b3c 100644 --- a/arch/arm/mach-shmobile/clock-r8a7791.c +++ b/arch/arm/mach-shmobile/clock-r8a7791.c @@ -123,6 +123,7 @@ enum { MSTP813, MSTP721, MSTP720, MSTP719, MSTP718, MSTP715, MSTP714, + MSTP319, MSTP216, MSTP207, MSTP206, MSTP204, MSTP203, MSTP202, MSTP1105, MSTP1106, MSTP1107, MSTP124, @@ -137,6 +138,7 @@ static struct clk mstp_clks[MSTP_NR] = { [MSTP718] = SH_CLK_MSTP32(&p_clk, SMSTPCR7, 18, 0), /* SCIF3 */ [MSTP715] = SH_CLK_MSTP32(&p_clk, SMSTPCR7, 15, 0), /* SCIF4 */ [MSTP714] = SH_CLK_MSTP32(&p_clk, SMSTPCR7, 14, 0), /* SCIF5 */ + [MSTP319] = SH_CLK_MSTP32(&mp_clk, SMSTPCR3, 19, 0), /* PCIEC */ [MSTP216] = SH_CLK_MSTP32(&mp_clk, SMSTPCR2, 16, 0), /* SCIFB2 */ [MSTP207] = SH_CLK_MSTP32(&mp_clk, SMSTPCR2, 7, 0), /* SCIFB1 */ [MSTP206] = SH_CLK_MSTP32(&mp_clk, SMSTPCR2, 6, 0), /* SCIFB0 */ @@ -166,6 +168,7 @@ static struct clk_lookup lookups[] = { CLKDEV_CON_ID("peripheral_clk", &hp_clk), /* MSTP */ + CLKDEV_DEV_ID("r8a7791-pcie", &mstp_clks[MSTP319]), CLKDEV_DEV_ID("sh-sci.0", &mstp_clks[MSTP204]), /* SCIFA0 */ CLKDEV_DEV_ID("sh-sci.1", &mstp_clks[MSTP203]), /* SCIFA1 */ CLKDEV_DEV_ID("sh-sci.2", &mstp_clks[MSTP206]), /* SCIFB0 */ -- 1.7.9.5 ^ permalink raw reply related [flat|nested] 16+ messages in thread
* [PATCH 3/5] ARM: shmobile: Add PCIe resources to r8a7791 device 2014-02-26 16:08 ` Phil Edworthy ` (2 preceding siblings ...) (?) @ 2014-02-26 16:08 ` Phil Edworthy -1 siblings, 0 replies; 16+ messages in thread From: Phil Edworthy @ 2014-02-26 16:08 UTC (permalink / raw) To: linux-pci Cc: linux-sh, Bjorn Helgaas, Valentine Barshak, Simon Horman, Magnus Damm, Phil Edworthy Signed-off-by: Phil Edworthy <phil.edworthy@renesas.com> --- arch/arm/mach-shmobile/setup-r8a7791.c | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/arch/arm/mach-shmobile/setup-r8a7791.c b/arch/arm/mach-shmobile/setup-r8a7791.c index d9393d6..7751aae 100644 --- a/arch/arm/mach-shmobile/setup-r8a7791.c +++ b/arch/arm/mach-shmobile/setup-r8a7791.c @@ -136,6 +136,16 @@ static struct resource irqc0_resources[] = { &irqc##idx##_data, \ sizeof(struct renesas_irqc_config)) +/* PCI Express */ +static struct resource pcie_resources[] = { + DEFINE_RES_MEM(0xfe000000, SZ_512K), + DEFINE_RES_MEM(0xfe100000, SZ_1M), + DEFINE_RES_MEM(0xfe200000, SZ_2M), + DEFINE_RES_MEM(0x30000000, SZ_128M), + DEFINE_RES_MEM(0x38000000, SZ_128M), + DEFINE_RES_IRQ(gic_spi(116)), +}; + void __init r8a7791_add_dt_devices(void) { r8a7791_register_scif(SCIFA0); @@ -160,6 +170,9 @@ void __init r8a7791_add_standard_devices(void) { r8a7791_add_dt_devices(); r8a7791_register_irqc(0); + platform_device_register_simple("r8a7791-pcie", -1, + pcie_resources, + ARRAY_SIZE(pcie_resources)); } void __init r8a7791_init_early(void) -- 1.7.9.5 ^ permalink raw reply related [flat|nested] 16+ messages in thread
* [PATCH 4/5] ARM: shmobile: Add PCIe to r8a7791 device Kconfig 2014-02-26 16:08 ` Phil Edworthy ` (3 preceding siblings ...) (?) @ 2014-02-26 16:08 ` Phil Edworthy -1 siblings, 0 replies; 16+ messages in thread From: Phil Edworthy @ 2014-02-26 16:08 UTC (permalink / raw) To: linux-pci Cc: linux-sh, Bjorn Helgaas, Valentine Barshak, Simon Horman, Magnus Damm, Phil Edworthy Signed-off-by: Phil Edworthy <phil.edworthy@renesas.com> --- arch/arm/mach-shmobile/Kconfig | 2 ++ 1 file changed, 2 insertions(+) diff --git a/arch/arm/mach-shmobile/Kconfig b/arch/arm/mach-shmobile/Kconfig index a4a4b75..c623633 100644 --- a/arch/arm/mach-shmobile/Kconfig +++ b/arch/arm/mach-shmobile/Kconfig @@ -100,6 +100,8 @@ config ARCH_R8A7791 select ARM_GIC select CPU_V7 select SH_CLK_CPG + select MIGHT_HAVE_PCI + select PCI_QUIRKS if PCI config ARCH_EMEV2 bool "Emma Mobile EV2" -- 1.7.9.5 ^ permalink raw reply related [flat|nested] 16+ messages in thread
* [PATCH 5/5] ARM: koelsch: Add PCIe to defconfig 2014-02-26 16:08 ` Phil Edworthy ` (4 preceding siblings ...) (?) @ 2014-02-26 16:08 ` Phil Edworthy -1 siblings, 0 replies; 16+ messages in thread From: Phil Edworthy @ 2014-02-26 16:08 UTC (permalink / raw) To: linux-pci Cc: linux-sh, Bjorn Helgaas, Valentine Barshak, Simon Horman, Magnus Damm, Phil Edworthy Signed-off-by: Phil Edworthy <phil.edworthy@renesas.com> --- arch/arm/configs/koelsch_defconfig | 3 +++ 1 file changed, 3 insertions(+) diff --git a/arch/arm/configs/koelsch_defconfig b/arch/arm/configs/koelsch_defconfig index c59768e..adb67d5 100644 --- a/arch/arm/configs/koelsch_defconfig +++ b/arch/arm/configs/koelsch_defconfig @@ -20,6 +20,9 @@ CONFIG_MACH_KOELSCH=y CONFIG_CPU_BPREDICT_DISABLE=y CONFIG_PL310_ERRATA_588369=y CONFIG_ARM_ERRATA_754322=y +CONFIG_PCI=y +CONFIG_PCI_RCAR_GEN2_PCIE=y +CONFIG_PCIEPORTBUS=y CONFIG_SMP=y CONFIG_SCHED_MC=y CONFIG_NR_CPUS=8 -- 1.7.9.5 ^ permalink raw reply related [flat|nested] 16+ messages in thread
end of thread, other threads:[~2014-02-26 17:35 UTC | newest] Thread overview: 16+ messages (download: mbox.gz follow: Atom feed -- links below jump to the message on this page -- 2014-02-26 16:08 [PATCH 0/5 RFC] R-Car Gen2 PCIe host driver Phil Edworthy 2014-02-26 16:08 ` Phil Edworthy 2014-02-26 16:08 ` [PATCH 1/5] PCI: host: Add Renesas R-Car PCIe driver Phil Edworthy 2014-02-26 16:08 ` Phil Edworthy 2014-02-26 16:34 ` Ben Dooks 2014-02-26 16:34 ` Ben Dooks 2014-02-26 17:13 ` Phil.Edworthy 2014-02-26 17:13 ` Phil.Edworthy 2014-02-26 17:19 ` Ben Dooks 2014-02-26 17:19 ` Ben Dooks 2014-02-26 17:34 ` Phil.Edworthy 2014-02-26 17:34 ` Phil.Edworthy 2014-02-26 16:08 ` [PATCH 2/5] ARM: shmobile: r8a7791: Add PCIe clock Phil Edworthy 2014-02-26 16:08 ` [PATCH 3/5] ARM: shmobile: Add PCIe resources to r8a7791 device Phil Edworthy 2014-02-26 16:08 ` [PATCH 4/5] ARM: shmobile: Add PCIe to r8a7791 device Kconfig Phil Edworthy 2014-02-26 16:08 ` [PATCH 5/5] ARM: koelsch: Add PCIe to defconfig Phil Edworthy
This is an external index of several public inboxes, see mirroring instructions on how to clone and mirror all data and code used by this external index.