All of lore.kernel.org
 help / color / mirror / Atom feed
* [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

* [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

* 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

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.