← HardwareDUJINLABS.COM

Hardware 02 · PCIe · Linux v6.18.37

Synopsys DWC PCIe: iATU, Enumeration, DMA와 MSI

DWC link-up 이후 DBI와 iATU가 CPU·PCIe 주소를 연결하고 Linux가 BDF와 BAR를 배치하며 bus mastering, DMA mapping과 MSI/MSI-X를 활성화하는 순서를 추적합니다.

검증 깊이
board → Linux 8 layers
그림
7 boundary diagrams
원본 코드
13 excerpts · 945 lines
기준
Linux v6.18.37

01 · QUESTION

무엇을 증명할 것인가

장치가 `lspci`에는 보이는데 driver의 MMIO, DMA 또는 interrupt만 실패할 때 어느 resource ownership 단계에서 멈춘 것인가?

DWC link-up 이후 DBI와 iATU가 CPU·PCIe 주소를 연결하고 Linux가 BDF와 BAR를 배치하며 bus mastering, DMA mapping과 MSI/MSI-X를 활성화하는 순서를 추적합니다. 이 글은 Linux 로그를 출발점으로 삼지 않습니다. 같은 사건이 보드, controller, firmware, kernel에서 어떤 이름과 번호로 바뀌는지 아래에서 위로 연결합니다.

02 · CONTRACT

입력과 완료 조건

구분확인할 내용
입력L0 link, 정상 configuration access, firmware host windows와 interrupt/IOMMU description
출력driver가 BAR를 매핑하고 DMA queue와 MSI vector를 사용해 transaction을 완료하는 상태
소유권PCI core는 bus/resource, driver는 device register와 queue, DMA/IOMMU core는 IOVA lifetime, IRQ core는 vector lifetime을 소유
완료 조건probe 성공뿐 아니라 MMIO readback, DMA completion, interrupt counter와 teardown까지 검증

03 · BOTTOM-UP

완전히 아래에서 시작하는 8단계

그림 1. 아래에서 Linux까지 올라오는 8개 검증 경계위 계층의 로그보다 아래 계층의 물리 증거를 먼저 확정합니다.
01
0. Discover · BDF와 multifunction/bridge 탐색

PCI core는 bus 0의 device/function을 읽고 Header Type과 class code로 endpoint와 bridge를 구분한다. bridge의 Primary/Secondary/Subordinate Bus Number가 뒤쪽 hierarchy를 연다. ARI가 켜진 경우 function numbering과 forwarding 규칙이 달라진다.

증거: `lspci -tv`, config header, bridge bus-number register

START
02
1. Size · BAR sizing과 prefetchable 속성

BAR에 all-ones를 쓰고 readback mask를 해석해 required size와 alignment를 얻는다. 64-bit BAR는 두 DWORD가 한 resource이며 prefetchable은 CPU cacheable이라는 뜻이 아니라 read side effect 없이 bridge가 prefetch할 수 있다는 PCI 속성이다.

증거: raw BAR, resource flags, size와 alignment, bridge window requirement

CHECKPOINT
03
2. Assign · host aperture와 bridge window 배치

Linux는 firmware가 준 주소를 보존하거나 unassigned resource를 host `ranges/_CRS` 안에 배치한다. bridge I/O, non-prefetchable memory, prefetchable memory window가 child BAR 전체를 포함해야 한다. 32-bit endpoint 제약과 64-bit host aperture를 혼동하면 allocation이 실패한다.

증거: `/proc/iomem`, sysfs resource, boot log의 BAR assignment, bridge window

CHECKPOINT
04
3. Enable · Command register와 MMIO mapping

`pci_enable_device()`가 필요한 bridge/device state를 활성화하고 driver가 region을 request한 뒤 `pci_iomap()`으로 BAR를 kernel virtual address에 연결한다. Memory Space Enable과 Bus Master Enable은 다르며 DMA 전에 `pci_set_master()`가 필요하다.

증거: PCI_COMMAND, `/proc/iomem`, requested region owner, mapped BAR virtual/physical address

CHECKPOINT
05
4. DMA · DMA mask, IOVA와 descriptor publication

driver가 DMA mask를 합의한 뒤 coherent ring 또는 streaming buffer를 map한다. IOMMU가 켜지면 device가 보는 DMA address는 IOVA이고 physical address와 같지 않다. descriptor 내용과 ownership bit를 쓴 뒤 `dma_wmb()`를 거쳐 doorbell을 울려야 한다.

증거: DMA mask, IOVA/PA mapping, IOMMU group/domain, descriptor dump, fault log

CHECKPOINT
06
5. MSI · MSI/MSI-X vector와 message routing

MSI는 device가 특정 address/data로 Memory Write TLP를 보내 interrupt를 만든다. MSI-X는 vector별 table entry와 mask를 가진다. GIC ITS, x86 interrupt remapping, RISC-V IMSIC가 message를 target CPU interrupt로 변환할 수 있다.

증거: MSI capability/table, allocated vector, `/proc/interrupts`, irq affinity, remapper fault

CHECKPOINT
07
6. Driver · probe, queue start와 error unwind

probe는 enable, region request, DMA setup, IRQ allocation, hardware reset, queue start, net/block/core registration 순으로 진행한다. 중간 실패 시 역순으로 해제해야 한다. probe 성공 log만으로 runtime datapath가 검증된 것은 아니다.

증거: dynamic debug, probe 단계별 trace, devres 상태와 remove callback

CHECKPOINT
08
7. Runtime · MMIO doorbell → DMA → MSI completion

CPU가 descriptor를 채우고 doorbell MMIO를 쓰면 endpoint가 PCIe Memory Read로 descriptor/data를 가져간다. 완료 후 endpoint가 host memory status를 쓰고 MSI를 보낸다. interrupt handler는 completion ring을 회수해 upper layer에 완료를 게시한다.

증거: MMIO write trace, PCIe TLP, DMA/IOMMU fault, interrupt timestamp, completion index

LINUX / RESULT
01

0. Discover · BDF와 multifunction/bridge 탐색

PCI core는 bus 0의 device/function을 읽고 Header Type과 class code로 endpoint와 bridge를 구분한다. bridge의 Primary/Secondary/Subordinate Bus Number가 뒤쪽 hierarchy를 연다. ARI가 켜진 경우 function numbering과 forwarding 규칙이 달라진다.

남길 증거: `lspci -tv`, config header, bridge bus-number register

bridge 아래 BDF가 안 보이면 endpoint driver보다 bus-number와 Type-1 routing을 먼저 본다.

02

1. Size · BAR sizing과 prefetchable 속성

BAR에 all-ones를 쓰고 readback mask를 해석해 required size와 alignment를 얻는다. 64-bit BAR는 두 DWORD가 한 resource이며 prefetchable은 CPU cacheable이라는 뜻이 아니라 read side effect 없이 bridge가 prefetch할 수 있다는 PCI 속성이다.

남길 증거: raw BAR, resource flags, size와 alignment, bridge window requirement

device decode를 끈 상태에서 sizing하고 원래 값을 복원해야 한다.

03

2. Assign · host aperture와 bridge window 배치

Linux는 firmware가 준 주소를 보존하거나 unassigned resource를 host `ranges/_CRS` 안에 배치한다. bridge I/O, non-prefetchable memory, prefetchable memory window가 child BAR 전체를 포함해야 한다. 32-bit endpoint 제약과 64-bit host aperture를 혼동하면 allocation이 실패한다.

남길 증거: `/proc/iomem`, sysfs resource, boot log의 BAR assignment, bridge window

resource collision이나 no space가 있으면 driver probe에서 BAR를 만지기 전에 해결한다.

04

3. Enable · Command register와 MMIO mapping

`pci_enable_device()`가 필요한 bridge/device state를 활성화하고 driver가 region을 request한 뒤 `pci_iomap()`으로 BAR를 kernel virtual address에 연결한다. Memory Space Enable과 Bus Master Enable은 다르며 DMA 전에 `pci_set_master()`가 필요하다.

남길 증거: PCI_COMMAND, `/proc/iomem`, requested region owner, mapped BAR virtual/physical address

MMIO read가 all-ones이면 power state, BAR decode, bridge window와 endpoint reset을 재확인한다.

05

4. DMA · DMA mask, IOVA와 descriptor publication

driver가 DMA mask를 합의한 뒤 coherent ring 또는 streaming buffer를 map한다. IOMMU가 켜지면 device가 보는 DMA address는 IOVA이고 physical address와 같지 않다. descriptor 내용과 ownership bit를 쓴 뒤 `dma_wmb()`를 거쳐 doorbell을 울려야 한다.

남길 증거: DMA mask, IOVA/PA mapping, IOMMU group/domain, descriptor dump, fault log

completion 전에 unmap/free하면 device가 재사용 memory를 덮을 수 있다.

06

5. MSI · MSI/MSI-X vector와 message routing

MSI는 device가 특정 address/data로 Memory Write TLP를 보내 interrupt를 만든다. MSI-X는 vector별 table entry와 mask를 가진다. GIC ITS, x86 interrupt remapping, RISC-V IMSIC가 message를 target CPU interrupt로 변환할 수 있다.

남길 증거: MSI capability/table, allocated vector, `/proc/interrupts`, irq affinity, remapper fault

interrupt vector를 해제하기 전에 device MSI generation과 DMA queue를 먼저 멈춘다.

07

6. Driver · probe, queue start와 error unwind

probe는 enable, region request, DMA setup, IRQ allocation, hardware reset, queue start, net/block/core registration 순으로 진행한다. 중간 실패 시 역순으로 해제해야 한다. probe 성공 log만으로 runtime datapath가 검증된 것은 아니다.

남길 증거: dynamic debug, probe 단계별 trace, devres 상태와 remove callback

각 resource의 획득과 실패 label을 표로 만들어 leak와 double-free를 확인한다.

08

7. Runtime · MMIO doorbell → DMA → MSI completion

CPU가 descriptor를 채우고 doorbell MMIO를 쓰면 endpoint가 PCIe Memory Read로 descriptor/data를 가져간다. 완료 후 endpoint가 host memory status를 쓰고 MSI를 보낸다. interrupt handler는 completion ring을 회수해 upper layer에 완료를 게시한다.

남길 증거: MMIO write trace, PCIe TLP, DMA/IOMMU fault, interrupt timestamp, completion index

한 transaction ID를 descriptor index, DMA address, interrupt vector와 끝까지 연결한다.

04 · DIAGRAMS

주소와 상태가 이동하는 모습

그림 2. PCIe 실행 경로왼쪽에서 오른쪽으로 실제 소유권과 관찰 지점이 이동합니다.
01 Cfg scan
02 BAR sizing
03 Window assignment
04 Device enable
05 BAR iomap
06 Driver probe
그림 3. firmware와 Linux 번호 공간의 연결왼쪽에서 오른쪽으로 실제 소유권과 관찰 지점이 이동합니다.
01 Descriptor write
02 dma_wmb
03 Doorbell
04 PCIe DMA
05 Completion write
06 MSI
07 IRQ handler
그림 4. 입력, 소유권, 완료 조건의 경계완료 조건을 충족하기 전에는 다음 계층이 해당 자원을 재사용하면 안 됩니다.
INPUT

L0 link, 정상 configuration access, firmware host windows와 interrupt/IOMMU description

OWNER

PCI core는 bus/resource, driver는 device register와 queue, DMA/IOMMU core는 IOVA lifetime, IRQ core는 vector lifetime을 소유

OUTPUT

driver가 BAR를 매핑하고 DMA queue와 MSI vector를 사용해 transaction을 완료하는 상태

DONE

probe 성공뿐 아니라 MMIO readback, DMA completion, interrupt counter와 teardown까지 검증

05 · SYNOPSYS DESIGNWARE IP

Synopsys DWC Controller programming: DBI, iATU, AXI와 MSI

link가 L0에 들어온 뒤에는 PHY보다 DWC digital controller의 주소 변환과 Root Port programming이 중심이 됩니다. CPU가 보는 DBI/AXI 주소, PCIe bus address, endpoint BAR address, DMA IOVA를 서로 다른 번호 공간으로 두고 DWC iATU region이 어느 두 공간을 연결하는지 계산합니다.

그림 5. Synopsys DWC PCIe subsystem의 실제 분할SoC glue, digital controller, PIPE, PHY를 서로 다른 register bank로 봅니다.
01

DBI / DBI2

RC Type-1 configuration, Port Logic와 일부 EP shadow/BAR programming에 접근합니다.

관찰
dbi_base와 dbi_base2, RO write enable 구간
대표 실패
DBI와 application register base를 바꿔 읽어 전혀 다른 값을 정상으로 오판
02

Config aperture

PCI core의 BDF access를 DWC config-space window로 받습니다.

관찰
child bus 여부에 따라 CFG0 또는 CFG1
대표 실패
bus 1 endpoint에 CFG1을 쓰거나 bridge 뒤 장치에 CFG0을 써 Completion이 없음
03

Outbound iATU

CPU AXI base/limit를 PCIe target address와 TLP type으로 변환합니다.

관찰
MEM/IO/CFG0/CFG1/MSG region
대표 실패
base·limit inclusive 계산 또는 upper address 오류로 일부 BAR만 접근 실패
04

Inbound iATU

Endpoint가 보낸 Memory Read/Write를 SoC physical aperture로 변환합니다.

관찰
BAR match 또는 address match, target base
대표 실패
DMA address와 inbound target/IOMMU 구성이 맞지 않아 AXI abort
05

AXI master/slave

CPU MMIO/config 요청과 inbound DMA request를 outstanding transaction으로 운반합니다.

관찰
AXI ID/width/burst/ordering와 error response
대표 실패
PCIe Completion Timeout과 SoC interconnect timeout을 같은 오류로 취급
06

Root Port setup

class code, BAR, bus number, command, lane width와 speed capability를 RC configuration에 맞춥니다.

관찰
dw_pcie_setup_rc()와 host_ops
대표 실패
DBI RO-write override를 닫지 않아 runtime write protection이 깨짐
07

MSI controller

DWC 내부 MSI address, enable/mask/status를 irqdomain으로 연결하거나 platform 외부 MSI domain을 사용합니다.

관찰
PCIE_MSI_ADDR/ENABLE/MASK/STATUS
대표 실패
내부 MSI와 GIC ITS 경로를 동시에 구성하거나 둘 다 구성하지 않음
08

Optional eDMA

일부 DWC configuration은 embedded DMA read/write channel과 linked-list engine을 포함합니다.

관찰
channel control, doorbell, LLP, interrupt status
대표 실패
일반 endpoint bus-master DMA와 DWC eDMA를 같은 엔진으로 오해

PHY와 Controller가 만나는 PIPE 경계

한쪽 register만 보고 link 상태를 판정하지 않습니다. 동일 사건에 대해 PHY evidence와 controller evidence를 같은 행에 놓고 처음 불일치하는 구간을 찾습니다.

단계PHY 쪽에서 확인DWC Controller 쪽에서 확인경계 판정
Configuration readPHY: L0와 DLL active를 유지Controller: CFG0/CFG1 iATU와 BDF target 생성0xffff이면 LNKSTA와 iATU region을 같은 순간에 저장
BAR MMIOPHY: TLP 전송 품질과 replay를 보장Controller: outbound MEM base/limit/target 변환CPU physical, PCI bus address, BAR assigned base를 세 열로 계산
Endpoint DMA readPHY: Memory Read TLP와 Completion 전달Controller/SoC: inbound routing 또는 IOMMU requester contextIOMMU fault의 requester ID와 BDF를 연결
Endpoint DMA writePHY: posted Memory Write 전달Controller/SoC: inbound target와 cache coherencyposted이므로 CPU memory 관찰과 device completion을 따로 확인
MSIPHY: MSI Memory Write TLP 전달Controller: internal MSI status 또는 external MSI domain routeDMA completion write가 보인 뒤 MSI가 오는 순서를 timestamp로 검증
TeardownPHY: 새 TLP 차단 및 link/power transitionController: bus master off, IRQ mask, ATU/queue 해제device가 더 이상 DMA하지 않는 증거 뒤 mapping을 반환

DWC 공개 register와 읽는 목적

Register/영역역할검증 시 주의점
PCIE_MISC_CONTROL_1_OFF 0x8bcDBI read-only write enableRC setup 중에만 PCIE_DBI_RO_WR_EN을 set하고 반드시 clear
PCIE_ATU_VIEWPORT 0x900v4.80a 이전 region selectordirection과 index를 선택한 뒤 공용 register window programming
PCIE_ATU_UNROLL_BASEv4.80a 이후 direct region baseoutbound/inbound와 region index가 address에 포함됨
REGION_CTRL1TLP type MEM/IO/CFG0/CFG1/MSG주소만 맞아도 type이 틀리면 상대편 routing이 다름
REGION_CTRL2enable, BAR mode, config shift, function matchenable readback과 LINK_WAIT_IATU retry를 확인
LOWER/UPPER_BASE + LIMITCPU 측 source aperturelimit가 inclusive이며 4GiB 경계를 넘는 region은 upper limit 지원 확인
LOWER/UPPER_TARGETPCIe 측 target address 또는 BDFCFG region은 bus/device/function field encoding 확인
MSI_ADDR/ENABLE/MASK/STATUSDWC internal MSI blockirqdomain vector와 status bit group을 맞춤
그림 6. DWC와 PHY 초기화 순서왼쪽에서 오른쪽으로 실제 소유권과 관찰 지점이 이동합니다.
01 dw_pcie_host_init
02 platform host_init
03 dw_pcie_setup_rc
04 DBI RC programming
05 iATU config/MEM windows
06 start_link
07 wait_for_link
08 pci_host_probe
그림 7. 주소 또는 상태가 DWC를 통과하는 경로왼쪽에서 오른쪽으로 실제 소유권과 관찰 지점이 이동합니다.
01 CPU PA
02 AXI request
03 outbound iATU base/limit match
04 target+TLP type
05 PCIe BAR/BDF
06 Completion or posted finish

CFG0와 CFG1의 차이

Root Port 바로 아래 bus의 endpoint에는 Type 0 Configuration Request가 필요하고, bridge 너머에는 Type 1 routing이 필요합니다. DWC host driver는 bus topology에 따라 iATU TLP type과 target BDF를 바꾸므로 bus number가 잘못되면 PHY가 완전히 정상이어도 enumeration이 끊깁니다.

Viewport와 unrolled iATU

DWC v4.80a 이전에는 PCIE_ATU_VIEWPORT로 region과 방향을 고른 뒤 하나의 register 창을 재사용합니다. 이후 IP는 region마다 직접 접근 가능한 unrolled 공간을 둘 수 있습니다. Linux는 version/capability로 방식을 결정하므로 bring-up dump도 같은 방식을 따라야 합니다.

DMA 주소는 iATU 하나로 끝나지 않는다

Endpoint가 보는 PCIe address가 SoC interconnect address로 변환된 뒤 IOMMU가 다시 IOVA를 physical page로 변환할 수 있습니다. DWC inbound iATU, SMMU stream ID, DMA API가 각각 한 번씩 주소 공간을 바꿀 수 있으므로 descriptor에 적힌 값만 보고 PA라고 부르면 안 됩니다.

DWC eDMA와 endpoint DMA 구분

DWC eDMA는 controller IP에 선택적으로 합성되는 별도 엔진입니다. NVMe나 Ethernet endpoint가 자체 DMA engine으로 bus master transaction을 만드는 것과 다릅니다. Linux config와 register presence를 확인한 뒤 eDMA channel register를 해석합니다.

06 · DETAILED MANUAL

DBI, ECAM, iATU, BAR, DMA와 MSI를 주소 단위로 추적하는 상세 매뉴얼

이 장은 link가 L0에 들어온 이후를 다룹니다. 같은 숫자를 CPU physical address, PCI bus address, endpoint BAR offset, DMA address와 IOVA로 번갈아 부르는 실수를 막기 위해 모든 transaction을 '누가 생성했고 어느 translation을 통과했는가'로 기록합니다. 예시 주소는 계산 방법을 보여 주기 위한 가상 값이며 실제 board의 DT ranges, iommu-map와 DWC region capability로 다시 계산해야 합니다.

공개적으로 다시 확인할 수 있는 자료

공개되지 않은 vendor register나 integration별 offset을 추측해서 채우지 않았습니다. 아래 제조사 자료, architecture 문서와 Linux v6.18.37 원본에서 다시 확인할 수 있는 범위만 사용했습니다.

07 · REGISTERS

읽어야 할 상태와 해석

값 하나만 떼어 보지 말고 동일 사건의 enable, status, ownership, completion register를 한 줄에 기록합니다. read-to-clear나 write-one-to-clear 속성은 반드시 datasheet에서 먼저 확인합니다.

레지스터/상태소유 블록검증할 의미
BAR0..5endpoint aperturesize/type/prefetch와 assigned base
PCI_COMMANDdecode controlMEM/IO/BUS_MASTER bit를 단계별로 확인
Bridge windowsType-1 headerchild BAR 전체를 포함하는지 확인
MSI-X Table/PBABAR 내부vector control mask와 pending bit
IOMMU contextplatform specificrequester ID가 올바른 domain에 연결됐는지 확인
AER statusRoot/EndpointUnsupported Request, Completion Timeout, Poisoned TLP

08 · LINUX SOURCE

Linux v6.18.37 원본 코드와 줄별 해설

라인 번호를 고정해 외우는 대신 함수 선언을 기준으로 Linux v6.18.37 tree에서 다시 찾았습니다. 로컬 tree에 있는 파일은 실제 코드를 직접 싣고, 나머지는 원본 파일과 읽을 함수 좌표를 연결했습니다.

Linux v6.18.37 · 실제 원본 코드

DWC outbound iATU region programming과 enable 확인

drivers/pci/controller/dwc/pcie-designware.c:517-613

517	 *                "TLP Digest Rules", when an endpoint receives TLP
518	 *                Digest when its ECRC check functionality is disabled
519	 *                in AER registers, received TLP Digest is just ignored.
520	 * Since there is no issue or error reported either side, best way to
521	 * handle the scenario is to program TD bit by default.
522	 */
523 
524	return val | PCIE_ATU_TD;
525}
526 
527int dw_pcie_prog_outbound_atu(struct dw_pcie *pci,
528			      const struct dw_pcie_ob_atu_cfg *atu)
529{
530	u64 parent_bus_addr = atu->parent_bus_addr;
531	u32 retries, val;
532	u64 limit_addr;
533 
534	limit_addr = parent_bus_addr + atu->size - 1;
535 
536	if ((limit_addr & ~pci->region_limit) != (parent_bus_addr & ~pci->region_limit) ||
537	    !IS_ALIGNED(parent_bus_addr, pci->region_align) ||
538	    !IS_ALIGNED(atu->pci_addr, pci->region_align) || !atu->size) {
539		return -EINVAL;
540	}
541 
542	dw_pcie_writel_atu_ob(pci, atu->index, PCIE_ATU_LOWER_BASE,
543			      lower_32_bits(parent_bus_addr));
544	dw_pcie_writel_atu_ob(pci, atu->index, PCIE_ATU_UPPER_BASE,
545			      upper_32_bits(parent_bus_addr));
546 
547	dw_pcie_writel_atu_ob(pci, atu->index, PCIE_ATU_LIMIT,
548			      lower_32_bits(limit_addr));
549	if (dw_pcie_ver_is_ge(pci, 460A))
550		dw_pcie_writel_atu_ob(pci, atu->index, PCIE_ATU_UPPER_LIMIT,
551				      upper_32_bits(limit_addr));
552 
553	dw_pcie_writel_atu_ob(pci, atu->index, PCIE_ATU_LOWER_TARGET,
554			      lower_32_bits(atu->pci_addr));
555	dw_pcie_writel_atu_ob(pci, atu->index, PCIE_ATU_UPPER_TARGET,
556			      upper_32_bits(atu->pci_addr));
557 
558	val = atu->type | atu->routing | PCIE_ATU_FUNC_NUM(atu->func_no);
559	if (upper_32_bits(limit_addr) > upper_32_bits(parent_bus_addr) &&
560	    dw_pcie_ver_is_ge(pci, 460A))
561		val |= PCIE_ATU_INCREASE_REGION_SIZE;
562	if (dw_pcie_ver_is(pci, 490A) || dw_pcie_ver_is(pci, 500A))
563		val = dw_pcie_enable_ecrc(val);
564	dw_pcie_writel_atu_ob(pci, atu->index, PCIE_ATU_REGION_CTRL1, val);
565 
566	val = PCIE_ATU_ENABLE | atu->ctrl2;
567	if (atu->type == PCIE_ATU_TYPE_MSG) {
568		/* The data-less messages only for now */
569		val |= PCIE_ATU_INHIBIT_PAYLOAD | atu->code;
570	}
571	dw_pcie_writel_atu_ob(pci, atu->index, PCIE_ATU_REGION_CTRL2, val);
572 
573	/*
574	 * Make sure ATU enable takes effect before any subsequent config
575	 * and I/O accesses.
576	 */
577	for (retries = 0; retries < LINK_WAIT_MAX_IATU_RETRIES; retries++) {
578		val = dw_pcie_readl_atu_ob(pci, atu->index, PCIE_ATU_REGION_CTRL2);
579		if (val & PCIE_ATU_ENABLE)
580			return 0;
581 
582		mdelay(LINK_WAIT_IATU);
583	}
584 
585	dev_err(pci->dev, "Outbound iATU is not being enabled\n");
586 
587	return -ETIMEDOUT;
588}
589 
590static inline u32 dw_pcie_readl_atu_ib(struct dw_pcie *pci, u32 index, u32 reg)
591{
592	return dw_pcie_readl_atu(pci, PCIE_ATU_REGION_DIR_IB, index, reg);
593}
594 
595static inline void dw_pcie_writel_atu_ib(struct dw_pcie *pci, u32 index, u32 reg,
596					 u32 val)
597{
598	dw_pcie_writel_atu(pci, PCIE_ATU_REGION_DIR_IB, index, reg, val);
599}
600 
601int dw_pcie_prog_inbound_atu(struct dw_pcie *pci, int index, int type,
602			     u64 parent_bus_addr, u64 pci_addr, u64 size)
603{
604	u64 limit_addr = pci_addr + size - 1;
605	u32 retries, val;
606 
607	if ((limit_addr & ~pci->region_limit) != (pci_addr & ~pci->region_limit) ||
608	    !IS_ALIGNED(parent_bus_addr, pci->region_align) ||
609	    !IS_ALIGNED(pci_addr, pci->region_align) || !size) {
610		return -EINVAL;
611	}
612 
613	dw_pcie_writel_atu_ib(pci, index, PCIE_ATU_LOWER_BASE,
L517 * "TLP Digest Rules", when an endpoint receives TLP

원본 주석입니다. DWC outbound iATU region programming과 enable 확인에서 뒤따르는 코드가 전제하는 하드웨어 조건이나 예외를 먼저 확인합니다.

L518 * Digest when its ECRC check functionality is disabled

원본 주석입니다. DWC outbound iATU region programming과 enable 확인에서 뒤따르는 코드가 전제하는 하드웨어 조건이나 예외를 먼저 확인합니다.

L519 * in AER registers, received TLP Digest is just ignored.

원본 주석입니다. DWC outbound iATU region programming과 enable 확인에서 뒤따르는 코드가 전제하는 하드웨어 조건이나 예외를 먼저 확인합니다.

L520 * Since there is no issue or error reported either side, best way to

원본 주석입니다. DWC outbound iATU region programming과 enable 확인에서 뒤따르는 코드가 전제하는 하드웨어 조건이나 예외를 먼저 확인합니다.

L521 * handle the scenario is to program TD bit by default.

원본 주석입니다. DWC outbound iATU region programming과 enable 확인에서 뒤따르는 코드가 전제하는 하드웨어 조건이나 예외를 먼저 확인합니다.

L522 */

원본 주석입니다. DWC outbound iATU region programming과 enable 확인에서 뒤따르는 코드가 전제하는 하드웨어 조건이나 예외를 먼저 확인합니다.

L523 (blank)

논리 구간을 나누는 빈 줄입니다. 바로 위에서 준비한 상태와 다음 제어 흐름을 분리해 읽습니다.

L524 return val | PCIE_ATU_TD;

호출자에게 상태를 반환합니다. 반환값만 보지 말고 그 전에 controller ownership이나 active 상태가 정리되었는지 확인합니다.

L525 }

제어 블록의 범위를 표시합니다. 이 괄호 안에서만 유효한 lock, CPU context, resource lifetime을 함께 표시해 둡니다.

L526 (blank)

논리 구간을 나누는 빈 줄입니다. 바로 위에서 준비한 상태와 다음 제어 흐름을 분리해 읽습니다.

L527 int dw_pcie_prog_outbound_atu(struct dw_pcie *pci,

함수 경계입니다. 호출자가 넘긴 번호 공간과 현재 CPU/controller context가 DWC outbound iATU region programming과 enable 확인의 입력 계약을 만족하는지 확인합니다.

L528 const struct dw_pcie_ob_atu_cfg *atu)

DWC outbound iATU region programming과 enable 확인의 한 단계입니다. 이 줄이 바꾸는 software 상태를 대응하는 controller register나 trace checkpoint와 연결해 확인합니다.

L529 {

제어 블록의 범위를 표시합니다. 이 괄호 안에서만 유효한 lock, CPU context, resource lifetime을 함께 표시해 둡니다.

L530 u64 parent_bus_addr = atu->parent_bus_addr;

DWC outbound iATU region programming과 enable 확인의 한 단계입니다. 이 줄이 바꾸는 software 상태를 대응하는 controller register나 trace checkpoint와 연결해 확인합니다.

L531 u32 retries, val;

DWC outbound iATU region programming과 enable 확인의 한 단계입니다. 이 줄이 바꾸는 software 상태를 대응하는 controller register나 trace checkpoint와 연결해 확인합니다.

L532 u64 limit_addr;

DWC outbound iATU region programming과 enable 확인의 한 단계입니다. 이 줄이 바꾸는 software 상태를 대응하는 controller register나 trace checkpoint와 연결해 확인합니다.

L533 (blank)

논리 구간을 나누는 빈 줄입니다. 바로 위에서 준비한 상태와 다음 제어 흐름을 분리해 읽습니다.

L534 limit_addr = parent_bus_addr + atu->size - 1;

DWC outbound iATU region programming과 enable 확인의 한 단계입니다. 이 줄이 바꾸는 software 상태를 대응하는 controller register나 trace checkpoint와 연결해 확인합니다.

L535 (blank)

논리 구간을 나누는 빈 줄입니다. 바로 위에서 준비한 상태와 다음 제어 흐름을 분리해 읽습니다.

L536 if ((limit_addr & ~pci->region_limit) != (parent_bus_addr & ~pci->region_limit) ||

상태나 ID를 분류하는 분기입니다. 이 조건의 참·거짓은 장치 register, firmware 기술 또는 CPU mode 중 하나에서 결정됩니다.

L537 !IS_ALIGNED(parent_bus_addr, pci->region_align) ||

DWC outbound iATU region programming과 enable 확인의 한 단계입니다. 이 줄이 바꾸는 software 상태를 대응하는 controller register나 trace checkpoint와 연결해 확인합니다.

L538 !IS_ALIGNED(atu->pci_addr, pci->region_align) || !atu->size) {

DWC outbound iATU region programming과 enable 확인의 한 단계입니다. 이 줄이 바꾸는 software 상태를 대응하는 controller register나 trace checkpoint와 연결해 확인합니다.

L539 return -EINVAL;

호출자에게 상태를 반환합니다. 반환값만 보지 말고 그 전에 controller ownership이나 active 상태가 정리되었는지 확인합니다.

L540 }

제어 블록의 범위를 표시합니다. 이 괄호 안에서만 유효한 lock, CPU context, resource lifetime을 함께 표시해 둡니다.

L541 (blank)

논리 구간을 나누는 빈 줄입니다. 바로 위에서 준비한 상태와 다음 제어 흐름을 분리해 읽습니다.

L542 dw_pcie_writel_atu_ob(pci, atu->index, PCIE_ATU_LOWER_BASE,

hardware 또는 공유 메모리에 상태를 게시합니다. write가 완료되는 지점과 다음 read/doorbell 사이의 ordering 조건을 확인합니다.

L543 lower_32_bits(parent_bus_addr));

DWC outbound iATU region programming과 enable 확인의 한 단계입니다. 이 줄이 바꾸는 software 상태를 대응하는 controller register나 trace checkpoint와 연결해 확인합니다.

L544 dw_pcie_writel_atu_ob(pci, atu->index, PCIE_ATU_UPPER_BASE,

hardware 또는 공유 메모리에 상태를 게시합니다. write가 완료되는 지점과 다음 read/doorbell 사이의 ordering 조건을 확인합니다.

L545 upper_32_bits(parent_bus_addr));

DWC outbound iATU region programming과 enable 확인의 한 단계입니다. 이 줄이 바꾸는 software 상태를 대응하는 controller register나 trace checkpoint와 연결해 확인합니다.

L546 (blank)

논리 구간을 나누는 빈 줄입니다. 바로 위에서 준비한 상태와 다음 제어 흐름을 분리해 읽습니다.

L547 dw_pcie_writel_atu_ob(pci, atu->index, PCIE_ATU_LIMIT,

hardware 또는 공유 메모리에 상태를 게시합니다. write가 완료되는 지점과 다음 read/doorbell 사이의 ordering 조건을 확인합니다.

L548 lower_32_bits(limit_addr));

DWC outbound iATU region programming과 enable 확인의 한 단계입니다. 이 줄이 바꾸는 software 상태를 대응하는 controller register나 trace checkpoint와 연결해 확인합니다.

L549 if (dw_pcie_ver_is_ge(pci, 460A))

상태나 ID를 분류하는 분기입니다. 이 조건의 참·거짓은 장치 register, firmware 기술 또는 CPU mode 중 하나에서 결정됩니다.

L550 dw_pcie_writel_atu_ob(pci, atu->index, PCIE_ATU_UPPER_LIMIT,

hardware 또는 공유 메모리에 상태를 게시합니다. write가 완료되는 지점과 다음 read/doorbell 사이의 ordering 조건을 확인합니다.

L551 upper_32_bits(limit_addr));

DWC outbound iATU region programming과 enable 확인의 한 단계입니다. 이 줄이 바꾸는 software 상태를 대응하는 controller register나 trace checkpoint와 연결해 확인합니다.

L552 (blank)

논리 구간을 나누는 빈 줄입니다. 바로 위에서 준비한 상태와 다음 제어 흐름을 분리해 읽습니다.

L553 dw_pcie_writel_atu_ob(pci, atu->index, PCIE_ATU_LOWER_TARGET,

hardware 또는 공유 메모리에 상태를 게시합니다. write가 완료되는 지점과 다음 read/doorbell 사이의 ordering 조건을 확인합니다.

L554 lower_32_bits(atu->pci_addr));

DWC outbound iATU region programming과 enable 확인의 한 단계입니다. 이 줄이 바꾸는 software 상태를 대응하는 controller register나 trace checkpoint와 연결해 확인합니다.

L555 dw_pcie_writel_atu_ob(pci, atu->index, PCIE_ATU_UPPER_TARGET,

hardware 또는 공유 메모리에 상태를 게시합니다. write가 완료되는 지점과 다음 read/doorbell 사이의 ordering 조건을 확인합니다.

L556 upper_32_bits(atu->pci_addr));

DWC outbound iATU region programming과 enable 확인의 한 단계입니다. 이 줄이 바꾸는 software 상태를 대응하는 controller register나 trace checkpoint와 연결해 확인합니다.

L557 (blank)

논리 구간을 나누는 빈 줄입니다. 바로 위에서 준비한 상태와 다음 제어 흐름을 분리해 읽습니다.

L558 val = atu->type | atu->routing | PCIE_ATU_FUNC_NUM(atu->func_no);

DWC outbound iATU region programming과 enable 확인의 한 단계입니다. 이 줄이 바꾸는 software 상태를 대응하는 controller register나 trace checkpoint와 연결해 확인합니다.

L559 if (upper_32_bits(limit_addr) > upper_32_bits(parent_bus_addr) &&

상태나 ID를 분류하는 분기입니다. 이 조건의 참·거짓은 장치 register, firmware 기술 또는 CPU mode 중 하나에서 결정됩니다.

L560 dw_pcie_ver_is_ge(pci, 460A))

DWC outbound iATU region programming과 enable 확인의 한 단계입니다. 이 줄이 바꾸는 software 상태를 대응하는 controller register나 trace checkpoint와 연결해 확인합니다.

L561 val |= PCIE_ATU_INCREASE_REGION_SIZE;

DWC outbound iATU region programming과 enable 확인의 한 단계입니다. 이 줄이 바꾸는 software 상태를 대응하는 controller register나 trace checkpoint와 연결해 확인합니다.

L562 if (dw_pcie_ver_is(pci, 490A) || dw_pcie_ver_is(pci, 500A))

상태나 ID를 분류하는 분기입니다. 이 조건의 참·거짓은 장치 register, firmware 기술 또는 CPU mode 중 하나에서 결정됩니다.

L563 val = dw_pcie_enable_ecrc(val);

DWC outbound iATU region programming과 enable 확인의 한 단계입니다. 이 줄이 바꾸는 software 상태를 대응하는 controller register나 trace checkpoint와 연결해 확인합니다.

L564 dw_pcie_writel_atu_ob(pci, atu->index, PCIE_ATU_REGION_CTRL1, val);

hardware 또는 공유 메모리에 상태를 게시합니다. write가 완료되는 지점과 다음 read/doorbell 사이의 ordering 조건을 확인합니다.

L565 (blank)

논리 구간을 나누는 빈 줄입니다. 바로 위에서 준비한 상태와 다음 제어 흐름을 분리해 읽습니다.

L566 val = PCIE_ATU_ENABLE | atu->ctrl2;

DWC outbound iATU region programming과 enable 확인의 한 단계입니다. 이 줄이 바꾸는 software 상태를 대응하는 controller register나 trace checkpoint와 연결해 확인합니다.

L567 if (atu->type == PCIE_ATU_TYPE_MSG) {

상태나 ID를 분류하는 분기입니다. 이 조건의 참·거짓은 장치 register, firmware 기술 또는 CPU mode 중 하나에서 결정됩니다.

L568 /* The data-less messages only for now */

원본 주석입니다. DWC outbound iATU region programming과 enable 확인에서 뒤따르는 코드가 전제하는 하드웨어 조건이나 예외를 먼저 확인합니다.

L569 val |= PCIE_ATU_INHIBIT_PAYLOAD | atu->code;

DWC outbound iATU region programming과 enable 확인의 한 단계입니다. 이 줄이 바꾸는 software 상태를 대응하는 controller register나 trace checkpoint와 연결해 확인합니다.

L570 }

제어 블록의 범위를 표시합니다. 이 괄호 안에서만 유효한 lock, CPU context, resource lifetime을 함께 표시해 둡니다.

L571 dw_pcie_writel_atu_ob(pci, atu->index, PCIE_ATU_REGION_CTRL2, val);

hardware 또는 공유 메모리에 상태를 게시합니다. write가 완료되는 지점과 다음 read/doorbell 사이의 ordering 조건을 확인합니다.

L572 (blank)

논리 구간을 나누는 빈 줄입니다. 바로 위에서 준비한 상태와 다음 제어 흐름을 분리해 읽습니다.

L573 /*

원본 주석입니다. DWC outbound iATU region programming과 enable 확인에서 뒤따르는 코드가 전제하는 하드웨어 조건이나 예외를 먼저 확인합니다.

L574 * Make sure ATU enable takes effect before any subsequent config

원본 주석입니다. DWC outbound iATU region programming과 enable 확인에서 뒤따르는 코드가 전제하는 하드웨어 조건이나 예외를 먼저 확인합니다.

L575 * and I/O accesses.

원본 주석입니다. DWC outbound iATU region programming과 enable 확인에서 뒤따르는 코드가 전제하는 하드웨어 조건이나 예외를 먼저 확인합니다.

L576 */

원본 주석입니다. DWC outbound iATU region programming과 enable 확인에서 뒤따르는 코드가 전제하는 하드웨어 조건이나 예외를 먼저 확인합니다.

L577 for (retries = 0; retries < LINK_WAIT_MAX_IATU_RETRIES; retries++) {

여러 pending source 또는 resource를 순회하는 구간입니다. 한 번의 진입에서 처리 가능한 양과 종료 조건이 interrupt latency에 영향을 줍니다.

L578 val = dw_pcie_readl_atu_ob(pci, atu->index, PCIE_ATU_REGION_CTRL2);

공유 상태나 MMIO를 읽습니다. 이 값은 단순 변수라기보다 hardware가 갱신한 시점과 read ordering을 포함해 해석해야 합니다.

L579 if (val & PCIE_ATU_ENABLE)

상태나 ID를 분류하는 분기입니다. 이 조건의 참·거짓은 장치 register, firmware 기술 또는 CPU mode 중 하나에서 결정됩니다.

L580 return 0;

호출자에게 상태를 반환합니다. 반환값만 보지 말고 그 전에 controller ownership이나 active 상태가 정리되었는지 확인합니다.

L581 (blank)

논리 구간을 나누는 빈 줄입니다. 바로 위에서 준비한 상태와 다음 제어 흐름을 분리해 읽습니다.

L582 mdelay(LINK_WAIT_IATU);

DWC outbound iATU region programming과 enable 확인의 한 단계입니다. 이 줄이 바꾸는 software 상태를 대응하는 controller register나 trace checkpoint와 연결해 확인합니다.

L583 }

제어 블록의 범위를 표시합니다. 이 괄호 안에서만 유효한 lock, CPU context, resource lifetime을 함께 표시해 둡니다.

L584 (blank)

논리 구간을 나누는 빈 줄입니다. 바로 위에서 준비한 상태와 다음 제어 흐름을 분리해 읽습니다.

L585 dev_err(pci->dev, "Outbound iATU is not being enabled\n");

DWC outbound iATU region programming과 enable 확인의 한 단계입니다. 이 줄이 바꾸는 software 상태를 대응하는 controller register나 trace checkpoint와 연결해 확인합니다.

L586 (blank)

논리 구간을 나누는 빈 줄입니다. 바로 위에서 준비한 상태와 다음 제어 흐름을 분리해 읽습니다.

L587 return -ETIMEDOUT;

호출자에게 상태를 반환합니다. 반환값만 보지 말고 그 전에 controller ownership이나 active 상태가 정리되었는지 확인합니다.

L588 }

제어 블록의 범위를 표시합니다. 이 괄호 안에서만 유효한 lock, CPU context, resource lifetime을 함께 표시해 둡니다.

L589 (blank)

논리 구간을 나누는 빈 줄입니다. 바로 위에서 준비한 상태와 다음 제어 흐름을 분리해 읽습니다.

L590 static inline u32 dw_pcie_readl_atu_ib(struct dw_pcie *pci, u32 index, u32 reg)

공유 상태나 MMIO를 읽습니다. 이 값은 단순 변수라기보다 hardware가 갱신한 시점과 read ordering을 포함해 해석해야 합니다.

L591 {

제어 블록의 범위를 표시합니다. 이 괄호 안에서만 유효한 lock, CPU context, resource lifetime을 함께 표시해 둡니다.

L592 return dw_pcie_readl_atu(pci, PCIE_ATU_REGION_DIR_IB, index, reg);

공유 상태나 MMIO를 읽습니다. 이 값은 단순 변수라기보다 hardware가 갱신한 시점과 read ordering을 포함해 해석해야 합니다.

L593 }

제어 블록의 범위를 표시합니다. 이 괄호 안에서만 유효한 lock, CPU context, resource lifetime을 함께 표시해 둡니다.

L594 (blank)

논리 구간을 나누는 빈 줄입니다. 바로 위에서 준비한 상태와 다음 제어 흐름을 분리해 읽습니다.

L595 static inline void dw_pcie_writel_atu_ib(struct dw_pcie *pci, u32 index, u32 reg,

hardware 또는 공유 메모리에 상태를 게시합니다. write가 완료되는 지점과 다음 read/doorbell 사이의 ordering 조건을 확인합니다.

L596 u32 val)

DWC outbound iATU region programming과 enable 확인의 한 단계입니다. 이 줄이 바꾸는 software 상태를 대응하는 controller register나 trace checkpoint와 연결해 확인합니다.

L597 {

제어 블록의 범위를 표시합니다. 이 괄호 안에서만 유효한 lock, CPU context, resource lifetime을 함께 표시해 둡니다.

L598 dw_pcie_writel_atu(pci, PCIE_ATU_REGION_DIR_IB, index, reg, val);

hardware 또는 공유 메모리에 상태를 게시합니다. write가 완료되는 지점과 다음 read/doorbell 사이의 ordering 조건을 확인합니다.

L599 }

제어 블록의 범위를 표시합니다. 이 괄호 안에서만 유효한 lock, CPU context, resource lifetime을 함께 표시해 둡니다.

L600 (blank)

논리 구간을 나누는 빈 줄입니다. 바로 위에서 준비한 상태와 다음 제어 흐름을 분리해 읽습니다.

L601 int dw_pcie_prog_inbound_atu(struct dw_pcie *pci, int index, int type,

함수 경계입니다. 호출자가 넘긴 번호 공간과 현재 CPU/controller context가 DWC outbound iATU region programming과 enable 확인의 입력 계약을 만족하는지 확인합니다.

L602 u64 parent_bus_addr, u64 pci_addr, u64 size)

DWC outbound iATU region programming과 enable 확인의 한 단계입니다. 이 줄이 바꾸는 software 상태를 대응하는 controller register나 trace checkpoint와 연결해 확인합니다.

L603 {

제어 블록의 범위를 표시합니다. 이 괄호 안에서만 유효한 lock, CPU context, resource lifetime을 함께 표시해 둡니다.

L604 u64 limit_addr = pci_addr + size - 1;

DWC outbound iATU region programming과 enable 확인의 한 단계입니다. 이 줄이 바꾸는 software 상태를 대응하는 controller register나 trace checkpoint와 연결해 확인합니다.

L605 u32 retries, val;

DWC outbound iATU region programming과 enable 확인의 한 단계입니다. 이 줄이 바꾸는 software 상태를 대응하는 controller register나 trace checkpoint와 연결해 확인합니다.

L606 (blank)

논리 구간을 나누는 빈 줄입니다. 바로 위에서 준비한 상태와 다음 제어 흐름을 분리해 읽습니다.

L607 if ((limit_addr & ~pci->region_limit) != (pci_addr & ~pci->region_limit) ||

상태나 ID를 분류하는 분기입니다. 이 조건의 참·거짓은 장치 register, firmware 기술 또는 CPU mode 중 하나에서 결정됩니다.

L608 !IS_ALIGNED(parent_bus_addr, pci->region_align) ||

DWC outbound iATU region programming과 enable 확인의 한 단계입니다. 이 줄이 바꾸는 software 상태를 대응하는 controller register나 trace checkpoint와 연결해 확인합니다.

L609 !IS_ALIGNED(pci_addr, pci->region_align) || !size) {

DWC outbound iATU region programming과 enable 확인의 한 단계입니다. 이 줄이 바꾸는 software 상태를 대응하는 controller register나 trace checkpoint와 연결해 확인합니다.

L610 return -EINVAL;

호출자에게 상태를 반환합니다. 반환값만 보지 말고 그 전에 controller ownership이나 active 상태가 정리되었는지 확인합니다.

L611 }

제어 블록의 범위를 표시합니다. 이 괄호 안에서만 유효한 lock, CPU context, resource lifetime을 함께 표시해 둡니다.

L612 (blank)

논리 구간을 나누는 빈 줄입니다. 바로 위에서 준비한 상태와 다음 제어 흐름을 분리해 읽습니다.

L613 dw_pcie_writel_atu_ib(pci, index, PCIE_ATU_LOWER_BASE,

hardware 또는 공유 메모리에 상태를 게시합니다. write가 완료되는 지점과 다음 read/doorbell 사이의 ordering 조건을 확인합니다.

Linux v6.18.37 · 실제 원본 코드

DWC inbound iATU의 PCI address, parent address와 inclusive limit programming

drivers/pci/controller/dwc/pcie-designware.c:593-677

593}
594 
595static inline void dw_pcie_writel_atu_ib(struct dw_pcie *pci, u32 index, u32 reg,
596					 u32 val)
597{
598	dw_pcie_writel_atu(pci, PCIE_ATU_REGION_DIR_IB, index, reg, val);
599}
600 
601int dw_pcie_prog_inbound_atu(struct dw_pcie *pci, int index, int type,
602			     u64 parent_bus_addr, u64 pci_addr, u64 size)
603{
604	u64 limit_addr = pci_addr + size - 1;
605	u32 retries, val;
606 
607	if ((limit_addr & ~pci->region_limit) != (pci_addr & ~pci->region_limit) ||
608	    !IS_ALIGNED(parent_bus_addr, pci->region_align) ||
609	    !IS_ALIGNED(pci_addr, pci->region_align) || !size) {
610		return -EINVAL;
611	}
612 
613	dw_pcie_writel_atu_ib(pci, index, PCIE_ATU_LOWER_BASE,
614			      lower_32_bits(pci_addr));
615	dw_pcie_writel_atu_ib(pci, index, PCIE_ATU_UPPER_BASE,
616			      upper_32_bits(pci_addr));
617 
618	dw_pcie_writel_atu_ib(pci, index, PCIE_ATU_LIMIT,
619			      lower_32_bits(limit_addr));
620	if (dw_pcie_ver_is_ge(pci, 460A))
621		dw_pcie_writel_atu_ib(pci, index, PCIE_ATU_UPPER_LIMIT,
622				      upper_32_bits(limit_addr));
623 
624	dw_pcie_writel_atu_ib(pci, index, PCIE_ATU_LOWER_TARGET,
625			      lower_32_bits(parent_bus_addr));
626	dw_pcie_writel_atu_ib(pci, index, PCIE_ATU_UPPER_TARGET,
627			      upper_32_bits(parent_bus_addr));
628 
629	val = type;
630	if (upper_32_bits(limit_addr) > upper_32_bits(pci_addr) &&
631	    dw_pcie_ver_is_ge(pci, 460A))
632		val |= PCIE_ATU_INCREASE_REGION_SIZE;
633	dw_pcie_writel_atu_ib(pci, index, PCIE_ATU_REGION_CTRL1, val);
634	dw_pcie_writel_atu_ib(pci, index, PCIE_ATU_REGION_CTRL2, PCIE_ATU_ENABLE);
635 
636	/*
637	 * Make sure ATU enable takes effect before any subsequent config
638	 * and I/O accesses.
639	 */
640	for (retries = 0; retries < LINK_WAIT_MAX_IATU_RETRIES; retries++) {
641		val = dw_pcie_readl_atu_ib(pci, index, PCIE_ATU_REGION_CTRL2);
642		if (val & PCIE_ATU_ENABLE)
643			return 0;
644 
645		mdelay(LINK_WAIT_IATU);
646	}
647 
648	dev_err(pci->dev, "Inbound iATU is not being enabled\n");
649 
650	return -ETIMEDOUT;
651}
652 
653int dw_pcie_prog_ep_inbound_atu(struct dw_pcie *pci, u8 func_no, int index,
654				int type, u64 parent_bus_addr, u8 bar, size_t size)
655{
656	u32 retries, val;
657 
658	if (!IS_ALIGNED(parent_bus_addr, pci->region_align) ||
659	    !IS_ALIGNED(parent_bus_addr, size))
660		return -EINVAL;
661 
662	dw_pcie_writel_atu_ib(pci, index, PCIE_ATU_LOWER_TARGET,
663			      lower_32_bits(parent_bus_addr));
664	dw_pcie_writel_atu_ib(pci, index, PCIE_ATU_UPPER_TARGET,
665			      upper_32_bits(parent_bus_addr));
666 
667	dw_pcie_writel_atu_ib(pci, index, PCIE_ATU_REGION_CTRL1, type |
668			      PCIE_ATU_FUNC_NUM(func_no));
669	dw_pcie_writel_atu_ib(pci, index, PCIE_ATU_REGION_CTRL2,
670			      PCIE_ATU_ENABLE | PCIE_ATU_FUNC_NUM_MATCH_EN |
671			      PCIE_ATU_BAR_MODE_ENABLE | (bar << 8));
672 
673	/*
674	 * Make sure ATU enable takes effect before any subsequent config
675	 * and I/O accesses.
676	 */
677	for (retries = 0; retries < LINK_WAIT_MAX_IATU_RETRIES; retries++) {
L593 }

제어 블록의 범위를 표시합니다. 이 괄호 안에서만 유효한 lock, CPU context, resource lifetime을 함께 표시해 둡니다.

L594 (blank)

논리 구간을 나누는 빈 줄입니다. 바로 위에서 준비한 상태와 다음 제어 흐름을 분리해 읽습니다.

L595 static inline void dw_pcie_writel_atu_ib(struct dw_pcie *pci, u32 index, u32 reg,

hardware 또는 공유 메모리에 상태를 게시합니다. write가 완료되는 지점과 다음 read/doorbell 사이의 ordering 조건을 확인합니다.

L596 u32 val)

DWC inbound iATU의 PCI address, parent address와 inclusive limit programming의 한 단계입니다. 이 줄이 바꾸는 software 상태를 대응하는 controller register나 trace checkpoint와 연결해 확인합니다.

L597 {

제어 블록의 범위를 표시합니다. 이 괄호 안에서만 유효한 lock, CPU context, resource lifetime을 함께 표시해 둡니다.

L598 dw_pcie_writel_atu(pci, PCIE_ATU_REGION_DIR_IB, index, reg, val);

hardware 또는 공유 메모리에 상태를 게시합니다. write가 완료되는 지점과 다음 read/doorbell 사이의 ordering 조건을 확인합니다.

L599 }

제어 블록의 범위를 표시합니다. 이 괄호 안에서만 유효한 lock, CPU context, resource lifetime을 함께 표시해 둡니다.

L600 (blank)

논리 구간을 나누는 빈 줄입니다. 바로 위에서 준비한 상태와 다음 제어 흐름을 분리해 읽습니다.

L601 int dw_pcie_prog_inbound_atu(struct dw_pcie *pci, int index, int type,

함수 경계입니다. 호출자가 넘긴 번호 공간과 현재 CPU/controller context가 DWC inbound iATU의 PCI address, parent address와 inclusive limit programming의 입력 계약을 만족하는지 확인합니다.

L602 u64 parent_bus_addr, u64 pci_addr, u64 size)

DWC inbound iATU의 PCI address, parent address와 inclusive limit programming의 한 단계입니다. 이 줄이 바꾸는 software 상태를 대응하는 controller register나 trace checkpoint와 연결해 확인합니다.

L603 {

제어 블록의 범위를 표시합니다. 이 괄호 안에서만 유효한 lock, CPU context, resource lifetime을 함께 표시해 둡니다.

L604 u64 limit_addr = pci_addr + size - 1;

DWC inbound iATU의 PCI address, parent address와 inclusive limit programming의 한 단계입니다. 이 줄이 바꾸는 software 상태를 대응하는 controller register나 trace checkpoint와 연결해 확인합니다.

L605 u32 retries, val;

DWC inbound iATU의 PCI address, parent address와 inclusive limit programming의 한 단계입니다. 이 줄이 바꾸는 software 상태를 대응하는 controller register나 trace checkpoint와 연결해 확인합니다.

L606 (blank)

논리 구간을 나누는 빈 줄입니다. 바로 위에서 준비한 상태와 다음 제어 흐름을 분리해 읽습니다.

L607 if ((limit_addr & ~pci->region_limit) != (pci_addr & ~pci->region_limit) ||

상태나 ID를 분류하는 분기입니다. 이 조건의 참·거짓은 장치 register, firmware 기술 또는 CPU mode 중 하나에서 결정됩니다.

L608 !IS_ALIGNED(parent_bus_addr, pci->region_align) ||

DWC inbound iATU의 PCI address, parent address와 inclusive limit programming의 한 단계입니다. 이 줄이 바꾸는 software 상태를 대응하는 controller register나 trace checkpoint와 연결해 확인합니다.

L609 !IS_ALIGNED(pci_addr, pci->region_align) || !size) {

DWC inbound iATU의 PCI address, parent address와 inclusive limit programming의 한 단계입니다. 이 줄이 바꾸는 software 상태를 대응하는 controller register나 trace checkpoint와 연결해 확인합니다.

L610 return -EINVAL;

호출자에게 상태를 반환합니다. 반환값만 보지 말고 그 전에 controller ownership이나 active 상태가 정리되었는지 확인합니다.

L611 }

제어 블록의 범위를 표시합니다. 이 괄호 안에서만 유효한 lock, CPU context, resource lifetime을 함께 표시해 둡니다.

L612 (blank)

논리 구간을 나누는 빈 줄입니다. 바로 위에서 준비한 상태와 다음 제어 흐름을 분리해 읽습니다.

L613 dw_pcie_writel_atu_ib(pci, index, PCIE_ATU_LOWER_BASE,

hardware 또는 공유 메모리에 상태를 게시합니다. write가 완료되는 지점과 다음 read/doorbell 사이의 ordering 조건을 확인합니다.

L614 lower_32_bits(pci_addr));

DWC inbound iATU의 PCI address, parent address와 inclusive limit programming의 한 단계입니다. 이 줄이 바꾸는 software 상태를 대응하는 controller register나 trace checkpoint와 연결해 확인합니다.

L615 dw_pcie_writel_atu_ib(pci, index, PCIE_ATU_UPPER_BASE,

hardware 또는 공유 메모리에 상태를 게시합니다. write가 완료되는 지점과 다음 read/doorbell 사이의 ordering 조건을 확인합니다.

L616 upper_32_bits(pci_addr));

DWC inbound iATU의 PCI address, parent address와 inclusive limit programming의 한 단계입니다. 이 줄이 바꾸는 software 상태를 대응하는 controller register나 trace checkpoint와 연결해 확인합니다.

L617 (blank)

논리 구간을 나누는 빈 줄입니다. 바로 위에서 준비한 상태와 다음 제어 흐름을 분리해 읽습니다.

L618 dw_pcie_writel_atu_ib(pci, index, PCIE_ATU_LIMIT,

hardware 또는 공유 메모리에 상태를 게시합니다. write가 완료되는 지점과 다음 read/doorbell 사이의 ordering 조건을 확인합니다.

L619 lower_32_bits(limit_addr));

DWC inbound iATU의 PCI address, parent address와 inclusive limit programming의 한 단계입니다. 이 줄이 바꾸는 software 상태를 대응하는 controller register나 trace checkpoint와 연결해 확인합니다.

L620 if (dw_pcie_ver_is_ge(pci, 460A))

상태나 ID를 분류하는 분기입니다. 이 조건의 참·거짓은 장치 register, firmware 기술 또는 CPU mode 중 하나에서 결정됩니다.

L621 dw_pcie_writel_atu_ib(pci, index, PCIE_ATU_UPPER_LIMIT,

hardware 또는 공유 메모리에 상태를 게시합니다. write가 완료되는 지점과 다음 read/doorbell 사이의 ordering 조건을 확인합니다.

L622 upper_32_bits(limit_addr));

DWC inbound iATU의 PCI address, parent address와 inclusive limit programming의 한 단계입니다. 이 줄이 바꾸는 software 상태를 대응하는 controller register나 trace checkpoint와 연결해 확인합니다.

L623 (blank)

논리 구간을 나누는 빈 줄입니다. 바로 위에서 준비한 상태와 다음 제어 흐름을 분리해 읽습니다.

L624 dw_pcie_writel_atu_ib(pci, index, PCIE_ATU_LOWER_TARGET,

hardware 또는 공유 메모리에 상태를 게시합니다. write가 완료되는 지점과 다음 read/doorbell 사이의 ordering 조건을 확인합니다.

L625 lower_32_bits(parent_bus_addr));

DWC inbound iATU의 PCI address, parent address와 inclusive limit programming의 한 단계입니다. 이 줄이 바꾸는 software 상태를 대응하는 controller register나 trace checkpoint와 연결해 확인합니다.

L626 dw_pcie_writel_atu_ib(pci, index, PCIE_ATU_UPPER_TARGET,

hardware 또는 공유 메모리에 상태를 게시합니다. write가 완료되는 지점과 다음 read/doorbell 사이의 ordering 조건을 확인합니다.

L627 upper_32_bits(parent_bus_addr));

DWC inbound iATU의 PCI address, parent address와 inclusive limit programming의 한 단계입니다. 이 줄이 바꾸는 software 상태를 대응하는 controller register나 trace checkpoint와 연결해 확인합니다.

L628 (blank)

논리 구간을 나누는 빈 줄입니다. 바로 위에서 준비한 상태와 다음 제어 흐름을 분리해 읽습니다.

L629 val = type;

DWC inbound iATU의 PCI address, parent address와 inclusive limit programming의 한 단계입니다. 이 줄이 바꾸는 software 상태를 대응하는 controller register나 trace checkpoint와 연결해 확인합니다.

L630 if (upper_32_bits(limit_addr) > upper_32_bits(pci_addr) &&

상태나 ID를 분류하는 분기입니다. 이 조건의 참·거짓은 장치 register, firmware 기술 또는 CPU mode 중 하나에서 결정됩니다.

L631 dw_pcie_ver_is_ge(pci, 460A))

DWC inbound iATU의 PCI address, parent address와 inclusive limit programming의 한 단계입니다. 이 줄이 바꾸는 software 상태를 대응하는 controller register나 trace checkpoint와 연결해 확인합니다.

L632 val |= PCIE_ATU_INCREASE_REGION_SIZE;

DWC inbound iATU의 PCI address, parent address와 inclusive limit programming의 한 단계입니다. 이 줄이 바꾸는 software 상태를 대응하는 controller register나 trace checkpoint와 연결해 확인합니다.

L633 dw_pcie_writel_atu_ib(pci, index, PCIE_ATU_REGION_CTRL1, val);

hardware 또는 공유 메모리에 상태를 게시합니다. write가 완료되는 지점과 다음 read/doorbell 사이의 ordering 조건을 확인합니다.

L634 dw_pcie_writel_atu_ib(pci, index, PCIE_ATU_REGION_CTRL2, PCIE_ATU_ENABLE);

hardware 또는 공유 메모리에 상태를 게시합니다. write가 완료되는 지점과 다음 read/doorbell 사이의 ordering 조건을 확인합니다.

L635 (blank)

논리 구간을 나누는 빈 줄입니다. 바로 위에서 준비한 상태와 다음 제어 흐름을 분리해 읽습니다.

L636 /*

원본 주석입니다. DWC inbound iATU의 PCI address, parent address와 inclusive limit programming에서 뒤따르는 코드가 전제하는 하드웨어 조건이나 예외를 먼저 확인합니다.

L637 * Make sure ATU enable takes effect before any subsequent config

원본 주석입니다. DWC inbound iATU의 PCI address, parent address와 inclusive limit programming에서 뒤따르는 코드가 전제하는 하드웨어 조건이나 예외를 먼저 확인합니다.

L638 * and I/O accesses.

원본 주석입니다. DWC inbound iATU의 PCI address, parent address와 inclusive limit programming에서 뒤따르는 코드가 전제하는 하드웨어 조건이나 예외를 먼저 확인합니다.

L639 */

원본 주석입니다. DWC inbound iATU의 PCI address, parent address와 inclusive limit programming에서 뒤따르는 코드가 전제하는 하드웨어 조건이나 예외를 먼저 확인합니다.

L640 for (retries = 0; retries < LINK_WAIT_MAX_IATU_RETRIES; retries++) {

여러 pending source 또는 resource를 순회하는 구간입니다. 한 번의 진입에서 처리 가능한 양과 종료 조건이 interrupt latency에 영향을 줍니다.

L641 val = dw_pcie_readl_atu_ib(pci, index, PCIE_ATU_REGION_CTRL2);

공유 상태나 MMIO를 읽습니다. 이 값은 단순 변수라기보다 hardware가 갱신한 시점과 read ordering을 포함해 해석해야 합니다.

L642 if (val & PCIE_ATU_ENABLE)

상태나 ID를 분류하는 분기입니다. 이 조건의 참·거짓은 장치 register, firmware 기술 또는 CPU mode 중 하나에서 결정됩니다.

L643 return 0;

호출자에게 상태를 반환합니다. 반환값만 보지 말고 그 전에 controller ownership이나 active 상태가 정리되었는지 확인합니다.

L644 (blank)

논리 구간을 나누는 빈 줄입니다. 바로 위에서 준비한 상태와 다음 제어 흐름을 분리해 읽습니다.

L645 mdelay(LINK_WAIT_IATU);

DWC inbound iATU의 PCI address, parent address와 inclusive limit programming의 한 단계입니다. 이 줄이 바꾸는 software 상태를 대응하는 controller register나 trace checkpoint와 연결해 확인합니다.

L646 }

제어 블록의 범위를 표시합니다. 이 괄호 안에서만 유효한 lock, CPU context, resource lifetime을 함께 표시해 둡니다.

L647 (blank)

논리 구간을 나누는 빈 줄입니다. 바로 위에서 준비한 상태와 다음 제어 흐름을 분리해 읽습니다.

L648 dev_err(pci->dev, "Inbound iATU is not being enabled\n");

DWC inbound iATU의 PCI address, parent address와 inclusive limit programming의 한 단계입니다. 이 줄이 바꾸는 software 상태를 대응하는 controller register나 trace checkpoint와 연결해 확인합니다.

L649 (blank)

논리 구간을 나누는 빈 줄입니다. 바로 위에서 준비한 상태와 다음 제어 흐름을 분리해 읽습니다.

L650 return -ETIMEDOUT;

호출자에게 상태를 반환합니다. 반환값만 보지 말고 그 전에 controller ownership이나 active 상태가 정리되었는지 확인합니다.

L651 }

제어 블록의 범위를 표시합니다. 이 괄호 안에서만 유효한 lock, CPU context, resource lifetime을 함께 표시해 둡니다.

L652 (blank)

논리 구간을 나누는 빈 줄입니다. 바로 위에서 준비한 상태와 다음 제어 흐름을 분리해 읽습니다.

L653 int dw_pcie_prog_ep_inbound_atu(struct dw_pcie *pci, u8 func_no, int index,

함수 경계입니다. 호출자가 넘긴 번호 공간과 현재 CPU/controller context가 DWC inbound iATU의 PCI address, parent address와 inclusive limit programming의 입력 계약을 만족하는지 확인합니다.

L654 int type, u64 parent_bus_addr, u8 bar, size_t size)

DWC inbound iATU의 PCI address, parent address와 inclusive limit programming의 한 단계입니다. 이 줄이 바꾸는 software 상태를 대응하는 controller register나 trace checkpoint와 연결해 확인합니다.

L655 {

제어 블록의 범위를 표시합니다. 이 괄호 안에서만 유효한 lock, CPU context, resource lifetime을 함께 표시해 둡니다.

L656 u32 retries, val;

DWC inbound iATU의 PCI address, parent address와 inclusive limit programming의 한 단계입니다. 이 줄이 바꾸는 software 상태를 대응하는 controller register나 trace checkpoint와 연결해 확인합니다.

L657 (blank)

논리 구간을 나누는 빈 줄입니다. 바로 위에서 준비한 상태와 다음 제어 흐름을 분리해 읽습니다.

L658 if (!IS_ALIGNED(parent_bus_addr, pci->region_align) ||

상태나 ID를 분류하는 분기입니다. 이 조건의 참·거짓은 장치 register, firmware 기술 또는 CPU mode 중 하나에서 결정됩니다.

L659 !IS_ALIGNED(parent_bus_addr, size))

DWC inbound iATU의 PCI address, parent address와 inclusive limit programming의 한 단계입니다. 이 줄이 바꾸는 software 상태를 대응하는 controller register나 trace checkpoint와 연결해 확인합니다.

L660 return -EINVAL;

호출자에게 상태를 반환합니다. 반환값만 보지 말고 그 전에 controller ownership이나 active 상태가 정리되었는지 확인합니다.

L661 (blank)

논리 구간을 나누는 빈 줄입니다. 바로 위에서 준비한 상태와 다음 제어 흐름을 분리해 읽습니다.

L662 dw_pcie_writel_atu_ib(pci, index, PCIE_ATU_LOWER_TARGET,

hardware 또는 공유 메모리에 상태를 게시합니다. write가 완료되는 지점과 다음 read/doorbell 사이의 ordering 조건을 확인합니다.

L663 lower_32_bits(parent_bus_addr));

DWC inbound iATU의 PCI address, parent address와 inclusive limit programming의 한 단계입니다. 이 줄이 바꾸는 software 상태를 대응하는 controller register나 trace checkpoint와 연결해 확인합니다.

L664 dw_pcie_writel_atu_ib(pci, index, PCIE_ATU_UPPER_TARGET,

hardware 또는 공유 메모리에 상태를 게시합니다. write가 완료되는 지점과 다음 read/doorbell 사이의 ordering 조건을 확인합니다.

L665 upper_32_bits(parent_bus_addr));

DWC inbound iATU의 PCI address, parent address와 inclusive limit programming의 한 단계입니다. 이 줄이 바꾸는 software 상태를 대응하는 controller register나 trace checkpoint와 연결해 확인합니다.

L666 (blank)

논리 구간을 나누는 빈 줄입니다. 바로 위에서 준비한 상태와 다음 제어 흐름을 분리해 읽습니다.

L667 dw_pcie_writel_atu_ib(pci, index, PCIE_ATU_REGION_CTRL1, type |

hardware 또는 공유 메모리에 상태를 게시합니다. write가 완료되는 지점과 다음 read/doorbell 사이의 ordering 조건을 확인합니다.

L668 PCIE_ATU_FUNC_NUM(func_no));

DWC inbound iATU의 PCI address, parent address와 inclusive limit programming의 한 단계입니다. 이 줄이 바꾸는 software 상태를 대응하는 controller register나 trace checkpoint와 연결해 확인합니다.

L669 dw_pcie_writel_atu_ib(pci, index, PCIE_ATU_REGION_CTRL2,

hardware 또는 공유 메모리에 상태를 게시합니다. write가 완료되는 지점과 다음 read/doorbell 사이의 ordering 조건을 확인합니다.

L670 PCIE_ATU_ENABLE | PCIE_ATU_FUNC_NUM_MATCH_EN |

DWC inbound iATU의 PCI address, parent address와 inclusive limit programming의 한 단계입니다. 이 줄이 바꾸는 software 상태를 대응하는 controller register나 trace checkpoint와 연결해 확인합니다.

L671 PCIE_ATU_BAR_MODE_ENABLE | (bar << 8));

DWC inbound iATU의 PCI address, parent address와 inclusive limit programming의 한 단계입니다. 이 줄이 바꾸는 software 상태를 대응하는 controller register나 trace checkpoint와 연결해 확인합니다.

L672 (blank)

논리 구간을 나누는 빈 줄입니다. 바로 위에서 준비한 상태와 다음 제어 흐름을 분리해 읽습니다.

L673 /*

원본 주석입니다. DWC inbound iATU의 PCI address, parent address와 inclusive limit programming에서 뒤따르는 코드가 전제하는 하드웨어 조건이나 예외를 먼저 확인합니다.

L674 * Make sure ATU enable takes effect before any subsequent config

원본 주석입니다. DWC inbound iATU의 PCI address, parent address와 inclusive limit programming에서 뒤따르는 코드가 전제하는 하드웨어 조건이나 예외를 먼저 확인합니다.

L675 * and I/O accesses.

원본 주석입니다. DWC inbound iATU의 PCI address, parent address와 inclusive limit programming에서 뒤따르는 코드가 전제하는 하드웨어 조건이나 예외를 먼저 확인합니다.

L676 */

원본 주석입니다. DWC inbound iATU의 PCI address, parent address와 inclusive limit programming에서 뒤따르는 코드가 전제하는 하드웨어 조건이나 예외를 먼저 확인합니다.

L677 for (retries = 0; retries < LINK_WAIT_MAX_IATU_RETRIES; retries++) {

여러 pending source 또는 resource를 순회하는 구간입니다. 한 번의 진입에서 처리 가능한 양과 종료 조건이 interrupt latency에 영향을 줍니다.

Linux v6.18.37 · 실제 원본 코드

ECAM mode에서 immediate bus CFG0와 나머지 bus CFG1 region을 나누는 코드

drivers/pci/controller/dwc/pcie-designware-host.c:412-482

412		res->name = "msg";
413		res->flags = win->res->flags | IORESOURCE_BUSY;
414 
415		if (!devm_request_resource(pci->dev, win->res, res))
416			pp->msg_res = res;
417	}
418}
419 
420static int dw_pcie_config_ecam_iatu(struct dw_pcie_rp *pp)
421{
422	struct dw_pcie *pci = to_dw_pcie_from_pp(pp);
423	struct dw_pcie_ob_atu_cfg atu = {0};
424	resource_size_t bus_range_max;
425	struct resource_entry *bus;
426	int ret;
427 
428	bus = resource_list_first_type(&pp->bridge->windows, IORESOURCE_BUS);
429 
430	/*
431	 * Root bus under the host bridge doesn't require any iATU configuration
432	 * as DBI region will be used to access root bus config space.
433	 * Immediate bus under Root Bus, needs type 0 iATU configuration and
434	 * remaining buses need type 1 iATU configuration.
435	 */
436	atu.index = 0;
437	atu.type = PCIE_ATU_TYPE_CFG0;
438	atu.parent_bus_addr = pp->cfg0_base + SZ_1M;
439	/* 1MiB is to cover 1 (bus) * 32 (devices) * 8 (functions) */
440	atu.size = SZ_1M;
441	atu.ctrl2 = PCIE_ATU_CFG_SHIFT_MODE_ENABLE;
442	ret = dw_pcie_prog_outbound_atu(pci, &atu);
443	if (ret)
444		return ret;
445 
446	bus_range_max = resource_size(bus->res);
447 
448	if (bus_range_max < 2)
449		return 0;
450 
451	/* Configure remaining buses in type 1 iATU configuration */
452	atu.index = 1;
453	atu.type = PCIE_ATU_TYPE_CFG1;
454	atu.parent_bus_addr = pp->cfg0_base + SZ_2M;
455	atu.size = (SZ_1M * bus_range_max) - SZ_2M;
456	atu.ctrl2 = PCIE_ATU_CFG_SHIFT_MODE_ENABLE;
457 
458	return dw_pcie_prog_outbound_atu(pci, &atu);
459}
460 
461static int dw_pcie_create_ecam_window(struct dw_pcie_rp *pp, struct resource *res)
462{
463	struct dw_pcie *pci = to_dw_pcie_from_pp(pp);
464	struct device *dev = pci->dev;
465	struct resource_entry *bus;
466 
467	bus = resource_list_first_type(&pp->bridge->windows, IORESOURCE_BUS);
468	if (!bus)
469		return -ENODEV;
470 
471	pp->cfg = pci_ecam_create(dev, res, bus->res, &pci_generic_ecam_ops);
472	if (IS_ERR(pp->cfg))
473		return PTR_ERR(pp->cfg);
474 
475	return 0;
476}
477 
478static bool dw_pcie_ecam_enabled(struct dw_pcie_rp *pp, struct resource *config_res)
479{
480	struct resource *bus_range;
481	u64 nr_buses;
482 
L412 res->name = "msg";

ECAM mode에서 immediate bus CFG0와 나머지 bus CFG1 region을 나누는 코드의 한 단계입니다. 이 줄이 바꾸는 software 상태를 대응하는 controller register나 trace checkpoint와 연결해 확인합니다.

L413 res->flags = win->res->flags | IORESOURCE_BUSY;

ECAM mode에서 immediate bus CFG0와 나머지 bus CFG1 region을 나누는 코드의 한 단계입니다. 이 줄이 바꾸는 software 상태를 대응하는 controller register나 trace checkpoint와 연결해 확인합니다.

L414 (blank)

논리 구간을 나누는 빈 줄입니다. 바로 위에서 준비한 상태와 다음 제어 흐름을 분리해 읽습니다.

L415 if (!devm_request_resource(pci->dev, win->res, res))

상태나 ID를 분류하는 분기입니다. 이 조건의 참·거짓은 장치 register, firmware 기술 또는 CPU mode 중 하나에서 결정됩니다.

L416 pp->msg_res = res;

ECAM mode에서 immediate bus CFG0와 나머지 bus CFG1 region을 나누는 코드의 한 단계입니다. 이 줄이 바꾸는 software 상태를 대응하는 controller register나 trace checkpoint와 연결해 확인합니다.

L417 }

제어 블록의 범위를 표시합니다. 이 괄호 안에서만 유효한 lock, CPU context, resource lifetime을 함께 표시해 둡니다.

L418 }

제어 블록의 범위를 표시합니다. 이 괄호 안에서만 유효한 lock, CPU context, resource lifetime을 함께 표시해 둡니다.

L419 (blank)

논리 구간을 나누는 빈 줄입니다. 바로 위에서 준비한 상태와 다음 제어 흐름을 분리해 읽습니다.

L420 static int dw_pcie_config_ecam_iatu(struct dw_pcie_rp *pp)

함수 경계입니다. 호출자가 넘긴 번호 공간과 현재 CPU/controller context가 ECAM mode에서 immediate bus CFG0와 나머지 bus CFG1 region을 나누는 코드의 입력 계약을 만족하는지 확인합니다.

L421 {

제어 블록의 범위를 표시합니다. 이 괄호 안에서만 유효한 lock, CPU context, resource lifetime을 함께 표시해 둡니다.

L422 struct dw_pcie *pci = to_dw_pcie_from_pp(pp);

ECAM mode에서 immediate bus CFG0와 나머지 bus CFG1 region을 나누는 코드의 한 단계입니다. 이 줄이 바꾸는 software 상태를 대응하는 controller register나 trace checkpoint와 연결해 확인합니다.

L423 struct dw_pcie_ob_atu_cfg atu = {0};

ECAM mode에서 immediate bus CFG0와 나머지 bus CFG1 region을 나누는 코드의 한 단계입니다. 이 줄이 바꾸는 software 상태를 대응하는 controller register나 trace checkpoint와 연결해 확인합니다.

L424 resource_size_t bus_range_max;

ECAM mode에서 immediate bus CFG0와 나머지 bus CFG1 region을 나누는 코드의 한 단계입니다. 이 줄이 바꾸는 software 상태를 대응하는 controller register나 trace checkpoint와 연결해 확인합니다.

L425 struct resource_entry *bus;

ECAM mode에서 immediate bus CFG0와 나머지 bus CFG1 region을 나누는 코드의 한 단계입니다. 이 줄이 바꾸는 software 상태를 대응하는 controller register나 trace checkpoint와 연결해 확인합니다.

L426 int ret;

ECAM mode에서 immediate bus CFG0와 나머지 bus CFG1 region을 나누는 코드의 한 단계입니다. 이 줄이 바꾸는 software 상태를 대응하는 controller register나 trace checkpoint와 연결해 확인합니다.

L427 (blank)

논리 구간을 나누는 빈 줄입니다. 바로 위에서 준비한 상태와 다음 제어 흐름을 분리해 읽습니다.

L428 bus = resource_list_first_type(&pp->bridge->windows, IORESOURCE_BUS);

ECAM mode에서 immediate bus CFG0와 나머지 bus CFG1 region을 나누는 코드의 한 단계입니다. 이 줄이 바꾸는 software 상태를 대응하는 controller register나 trace checkpoint와 연결해 확인합니다.

L429 (blank)

논리 구간을 나누는 빈 줄입니다. 바로 위에서 준비한 상태와 다음 제어 흐름을 분리해 읽습니다.

L430 /*

원본 주석입니다. ECAM mode에서 immediate bus CFG0와 나머지 bus CFG1 region을 나누는 코드에서 뒤따르는 코드가 전제하는 하드웨어 조건이나 예외를 먼저 확인합니다.

L431 * Root bus under the host bridge doesn't require any iATU configuration

원본 주석입니다. ECAM mode에서 immediate bus CFG0와 나머지 bus CFG1 region을 나누는 코드에서 뒤따르는 코드가 전제하는 하드웨어 조건이나 예외를 먼저 확인합니다.

L432 * as DBI region will be used to access root bus config space.

원본 주석입니다. ECAM mode에서 immediate bus CFG0와 나머지 bus CFG1 region을 나누는 코드에서 뒤따르는 코드가 전제하는 하드웨어 조건이나 예외를 먼저 확인합니다.

L433 * Immediate bus under Root Bus, needs type 0 iATU configuration and

원본 주석입니다. ECAM mode에서 immediate bus CFG0와 나머지 bus CFG1 region을 나누는 코드에서 뒤따르는 코드가 전제하는 하드웨어 조건이나 예외를 먼저 확인합니다.

L434 * remaining buses need type 1 iATU configuration.

원본 주석입니다. ECAM mode에서 immediate bus CFG0와 나머지 bus CFG1 region을 나누는 코드에서 뒤따르는 코드가 전제하는 하드웨어 조건이나 예외를 먼저 확인합니다.

L435 */

원본 주석입니다. ECAM mode에서 immediate bus CFG0와 나머지 bus CFG1 region을 나누는 코드에서 뒤따르는 코드가 전제하는 하드웨어 조건이나 예외를 먼저 확인합니다.

L436 atu.index = 0;

ECAM mode에서 immediate bus CFG0와 나머지 bus CFG1 region을 나누는 코드의 한 단계입니다. 이 줄이 바꾸는 software 상태를 대응하는 controller register나 trace checkpoint와 연결해 확인합니다.

L437 atu.type = PCIE_ATU_TYPE_CFG0;

ECAM mode에서 immediate bus CFG0와 나머지 bus CFG1 region을 나누는 코드의 한 단계입니다. 이 줄이 바꾸는 software 상태를 대응하는 controller register나 trace checkpoint와 연결해 확인합니다.

L438 atu.parent_bus_addr = pp->cfg0_base + SZ_1M;

ECAM mode에서 immediate bus CFG0와 나머지 bus CFG1 region을 나누는 코드의 한 단계입니다. 이 줄이 바꾸는 software 상태를 대응하는 controller register나 trace checkpoint와 연결해 확인합니다.

L439 /* 1MiB is to cover 1 (bus) * 32 (devices) * 8 (functions) */

원본 주석입니다. ECAM mode에서 immediate bus CFG0와 나머지 bus CFG1 region을 나누는 코드에서 뒤따르는 코드가 전제하는 하드웨어 조건이나 예외를 먼저 확인합니다.

L440 atu.size = SZ_1M;

ECAM mode에서 immediate bus CFG0와 나머지 bus CFG1 region을 나누는 코드의 한 단계입니다. 이 줄이 바꾸는 software 상태를 대응하는 controller register나 trace checkpoint와 연결해 확인합니다.

L441 atu.ctrl2 = PCIE_ATU_CFG_SHIFT_MODE_ENABLE;

ECAM mode에서 immediate bus CFG0와 나머지 bus CFG1 region을 나누는 코드의 한 단계입니다. 이 줄이 바꾸는 software 상태를 대응하는 controller register나 trace checkpoint와 연결해 확인합니다.

L442 ret = dw_pcie_prog_outbound_atu(pci, &atu);

ECAM mode에서 immediate bus CFG0와 나머지 bus CFG1 region을 나누는 코드의 한 단계입니다. 이 줄이 바꾸는 software 상태를 대응하는 controller register나 trace checkpoint와 연결해 확인합니다.

L443 if (ret)

상태나 ID를 분류하는 분기입니다. 이 조건의 참·거짓은 장치 register, firmware 기술 또는 CPU mode 중 하나에서 결정됩니다.

L444 return ret;

호출자에게 상태를 반환합니다. 반환값만 보지 말고 그 전에 controller ownership이나 active 상태가 정리되었는지 확인합니다.

L445 (blank)

논리 구간을 나누는 빈 줄입니다. 바로 위에서 준비한 상태와 다음 제어 흐름을 분리해 읽습니다.

L446 bus_range_max = resource_size(bus->res);

ECAM mode에서 immediate bus CFG0와 나머지 bus CFG1 region을 나누는 코드의 한 단계입니다. 이 줄이 바꾸는 software 상태를 대응하는 controller register나 trace checkpoint와 연결해 확인합니다.

L447 (blank)

논리 구간을 나누는 빈 줄입니다. 바로 위에서 준비한 상태와 다음 제어 흐름을 분리해 읽습니다.

L448 if (bus_range_max < 2)

상태나 ID를 분류하는 분기입니다. 이 조건의 참·거짓은 장치 register, firmware 기술 또는 CPU mode 중 하나에서 결정됩니다.

L449 return 0;

호출자에게 상태를 반환합니다. 반환값만 보지 말고 그 전에 controller ownership이나 active 상태가 정리되었는지 확인합니다.

L450 (blank)

논리 구간을 나누는 빈 줄입니다. 바로 위에서 준비한 상태와 다음 제어 흐름을 분리해 읽습니다.

L451 /* Configure remaining buses in type 1 iATU configuration */

원본 주석입니다. ECAM mode에서 immediate bus CFG0와 나머지 bus CFG1 region을 나누는 코드에서 뒤따르는 코드가 전제하는 하드웨어 조건이나 예외를 먼저 확인합니다.

L452 atu.index = 1;

ECAM mode에서 immediate bus CFG0와 나머지 bus CFG1 region을 나누는 코드의 한 단계입니다. 이 줄이 바꾸는 software 상태를 대응하는 controller register나 trace checkpoint와 연결해 확인합니다.

L453 atu.type = PCIE_ATU_TYPE_CFG1;

ECAM mode에서 immediate bus CFG0와 나머지 bus CFG1 region을 나누는 코드의 한 단계입니다. 이 줄이 바꾸는 software 상태를 대응하는 controller register나 trace checkpoint와 연결해 확인합니다.

L454 atu.parent_bus_addr = pp->cfg0_base + SZ_2M;

ECAM mode에서 immediate bus CFG0와 나머지 bus CFG1 region을 나누는 코드의 한 단계입니다. 이 줄이 바꾸는 software 상태를 대응하는 controller register나 trace checkpoint와 연결해 확인합니다.

L455 atu.size = (SZ_1M * bus_range_max) - SZ_2M;

ECAM mode에서 immediate bus CFG0와 나머지 bus CFG1 region을 나누는 코드의 한 단계입니다. 이 줄이 바꾸는 software 상태를 대응하는 controller register나 trace checkpoint와 연결해 확인합니다.

L456 atu.ctrl2 = PCIE_ATU_CFG_SHIFT_MODE_ENABLE;

ECAM mode에서 immediate bus CFG0와 나머지 bus CFG1 region을 나누는 코드의 한 단계입니다. 이 줄이 바꾸는 software 상태를 대응하는 controller register나 trace checkpoint와 연결해 확인합니다.

L457 (blank)

논리 구간을 나누는 빈 줄입니다. 바로 위에서 준비한 상태와 다음 제어 흐름을 분리해 읽습니다.

L458 return dw_pcie_prog_outbound_atu(pci, &atu);

호출자에게 상태를 반환합니다. 반환값만 보지 말고 그 전에 controller ownership이나 active 상태가 정리되었는지 확인합니다.

L459 }

제어 블록의 범위를 표시합니다. 이 괄호 안에서만 유효한 lock, CPU context, resource lifetime을 함께 표시해 둡니다.

L460 (blank)

논리 구간을 나누는 빈 줄입니다. 바로 위에서 준비한 상태와 다음 제어 흐름을 분리해 읽습니다.

L461 static int dw_pcie_create_ecam_window(struct dw_pcie_rp *pp, struct resource *res)

함수 경계입니다. 호출자가 넘긴 번호 공간과 현재 CPU/controller context가 ECAM mode에서 immediate bus CFG0와 나머지 bus CFG1 region을 나누는 코드의 입력 계약을 만족하는지 확인합니다.

L462 {

제어 블록의 범위를 표시합니다. 이 괄호 안에서만 유효한 lock, CPU context, resource lifetime을 함께 표시해 둡니다.

L463 struct dw_pcie *pci = to_dw_pcie_from_pp(pp);

ECAM mode에서 immediate bus CFG0와 나머지 bus CFG1 region을 나누는 코드의 한 단계입니다. 이 줄이 바꾸는 software 상태를 대응하는 controller register나 trace checkpoint와 연결해 확인합니다.

L464 struct device *dev = pci->dev;

ECAM mode에서 immediate bus CFG0와 나머지 bus CFG1 region을 나누는 코드의 한 단계입니다. 이 줄이 바꾸는 software 상태를 대응하는 controller register나 trace checkpoint와 연결해 확인합니다.

L465 struct resource_entry *bus;

ECAM mode에서 immediate bus CFG0와 나머지 bus CFG1 region을 나누는 코드의 한 단계입니다. 이 줄이 바꾸는 software 상태를 대응하는 controller register나 trace checkpoint와 연결해 확인합니다.

L466 (blank)

논리 구간을 나누는 빈 줄입니다. 바로 위에서 준비한 상태와 다음 제어 흐름을 분리해 읽습니다.

L467 bus = resource_list_first_type(&pp->bridge->windows, IORESOURCE_BUS);

ECAM mode에서 immediate bus CFG0와 나머지 bus CFG1 region을 나누는 코드의 한 단계입니다. 이 줄이 바꾸는 software 상태를 대응하는 controller register나 trace checkpoint와 연결해 확인합니다.

L468 if (!bus)

상태나 ID를 분류하는 분기입니다. 이 조건의 참·거짓은 장치 register, firmware 기술 또는 CPU mode 중 하나에서 결정됩니다.

L469 return -ENODEV;

호출자에게 상태를 반환합니다. 반환값만 보지 말고 그 전에 controller ownership이나 active 상태가 정리되었는지 확인합니다.

L470 (blank)

논리 구간을 나누는 빈 줄입니다. 바로 위에서 준비한 상태와 다음 제어 흐름을 분리해 읽습니다.

L471 pp->cfg = pci_ecam_create(dev, res, bus->res, &pci_generic_ecam_ops);

ECAM mode에서 immediate bus CFG0와 나머지 bus CFG1 region을 나누는 코드의 한 단계입니다. 이 줄이 바꾸는 software 상태를 대응하는 controller register나 trace checkpoint와 연결해 확인합니다.

L472 if (IS_ERR(pp->cfg))

상태나 ID를 분류하는 분기입니다. 이 조건의 참·거짓은 장치 register, firmware 기술 또는 CPU mode 중 하나에서 결정됩니다.

L473 return PTR_ERR(pp->cfg);

호출자에게 상태를 반환합니다. 반환값만 보지 말고 그 전에 controller ownership이나 active 상태가 정리되었는지 확인합니다.

L474 (blank)

논리 구간을 나누는 빈 줄입니다. 바로 위에서 준비한 상태와 다음 제어 흐름을 분리해 읽습니다.

L475 return 0;

호출자에게 상태를 반환합니다. 반환값만 보지 말고 그 전에 controller ownership이나 active 상태가 정리되었는지 확인합니다.

L476 }

제어 블록의 범위를 표시합니다. 이 괄호 안에서만 유효한 lock, CPU context, resource lifetime을 함께 표시해 둡니다.

L477 (blank)

논리 구간을 나누는 빈 줄입니다. 바로 위에서 준비한 상태와 다음 제어 흐름을 분리해 읽습니다.

L478 static bool dw_pcie_ecam_enabled(struct dw_pcie_rp *pp, struct resource *config_res)

함수 경계입니다. 호출자가 넘긴 번호 공간과 현재 CPU/controller context가 ECAM mode에서 immediate bus CFG0와 나머지 bus CFG1 region을 나누는 코드의 입력 계약을 만족하는지 확인합니다.

L479 {

제어 블록의 범위를 표시합니다. 이 괄호 안에서만 유효한 lock, CPU context, resource lifetime을 함께 표시해 둡니다.

L480 struct resource *bus_range;

ECAM mode에서 immediate bus CFG0와 나머지 bus CFG1 region을 나누는 코드의 한 단계입니다. 이 줄이 바꾸는 software 상태를 대응하는 controller register나 trace checkpoint와 연결해 확인합니다.

L481 u64 nr_buses;

ECAM mode에서 immediate bus CFG0와 나머지 bus CFG1 region을 나누는 코드의 한 단계입니다. 이 줄이 바꾸는 software 상태를 대응하는 controller register나 trace checkpoint와 연결해 확인합니다.

L482 (blank)

논리 구간을 나누는 빈 줄입니다. 바로 위에서 준비한 상태와 다음 제어 흐름을 분리해 읽습니다.

Linux v6.18.37 · 실제 원본 코드

DWC Root Port DBI와 outbound window 설정

drivers/pci/controller/dwc/pcie-designware-host.c:1037-1119

1037 
1038	if (speed >= PCIE_SPEED_32_0GT)
1039		dw_pcie_program_presets(pp, PCIE_SPEED_32_0GT);
1040 
1041	if (speed >= PCIE_SPEED_64_0GT)
1042		dw_pcie_program_presets(pp, PCIE_SPEED_64_0GT);
1043}
1044 
1045int dw_pcie_setup_rc(struct dw_pcie_rp *pp)
1046{
1047	struct dw_pcie *pci = to_dw_pcie_from_pp(pp);
1048	u32 val;
1049	int ret;
1050 
1051	/*
1052	 * Enable DBI read-only registers for writing/updating configuration.
1053	 * Write permission gets disabled towards the end of this function.
1054	 */
1055	dw_pcie_dbi_ro_wr_en(pci);
1056 
1057	dw_pcie_setup(pci);
1058 
1059	dw_pcie_msi_init(pp);
1060 
1061	/* Setup RC BARs */
1062	dw_pcie_writel_dbi(pci, PCI_BASE_ADDRESS_0, 0x00000004);
1063	dw_pcie_writel_dbi(pci, PCI_BASE_ADDRESS_1, 0x00000000);
1064 
1065	/* Setup interrupt pins */
1066	val = dw_pcie_readl_dbi(pci, PCI_INTERRUPT_LINE);
1067	val &= 0xffff00ff;
1068	val |= 0x00000100;
1069	dw_pcie_writel_dbi(pci, PCI_INTERRUPT_LINE, val);
1070 
1071	/* Setup bus numbers */
1072	val = dw_pcie_readl_dbi(pci, PCI_PRIMARY_BUS);
1073	val &= 0xff000000;
1074	val |= 0x00ff0100;
1075	dw_pcie_writel_dbi(pci, PCI_PRIMARY_BUS, val);
1076 
1077	/* Setup command register */
1078	val = dw_pcie_readl_dbi(pci, PCI_COMMAND);
1079	val &= 0xffff0000;
1080	val |= PCI_COMMAND_IO | PCI_COMMAND_MEMORY |
1081		PCI_COMMAND_MASTER | PCI_COMMAND_SERR;
1082	dw_pcie_writel_dbi(pci, PCI_COMMAND, val);
1083 
1084	dw_pcie_hide_unsupported_l1ss(pci);
1085 
1086	dw_pcie_config_presets(pp);
1087	/*
1088	 * If the platform provides its own child bus config accesses, it means
1089	 * the platform uses its own address translation component rather than
1090	 * ATU, so we should not program the ATU here.
1091	 */
1092	if (pp->bridge->child_ops == &dw_child_pcie_ops) {
1093		ret = dw_pcie_iatu_setup(pp);
1094		if (ret)
1095			return ret;
1096	}
1097 
1098	dw_pcie_writel_dbi(pci, PCI_BASE_ADDRESS_0, 0);
1099 
1100	/* Program correct class for RC */
1101	dw_pcie_writew_dbi(pci, PCI_CLASS_DEVICE, PCI_CLASS_BRIDGE_PCI);
1102 
1103	val = dw_pcie_readl_dbi(pci, PCIE_LINK_WIDTH_SPEED_CONTROL);
1104	val |= PORT_LOGIC_SPEED_CHANGE;
1105	dw_pcie_writel_dbi(pci, PCIE_LINK_WIDTH_SPEED_CONTROL, val);
1106 
1107	dw_pcie_dbi_ro_wr_dis(pci);
1108 
1109	return 0;
1110}
1111EXPORT_SYMBOL_GPL(dw_pcie_setup_rc);
1112 
1113static int dw_pcie_pme_turn_off(struct dw_pcie *pci)
1114{
1115	struct dw_pcie_ob_atu_cfg atu = { 0 };
1116	void __iomem *mem;
1117	int ret;
1118 
1119	if (pci->num_ob_windows <= pci->pp.msg_atu_index)
L1037 (blank)

논리 구간을 나누는 빈 줄입니다. 바로 위에서 준비한 상태와 다음 제어 흐름을 분리해 읽습니다.

L1038 if (speed >= PCIE_SPEED_32_0GT)

상태나 ID를 분류하는 분기입니다. 이 조건의 참·거짓은 장치 register, firmware 기술 또는 CPU mode 중 하나에서 결정됩니다.

L1039 dw_pcie_program_presets(pp, PCIE_SPEED_32_0GT);

DWC Root Port DBI와 outbound window 설정의 한 단계입니다. 이 줄이 바꾸는 software 상태를 대응하는 controller register나 trace checkpoint와 연결해 확인합니다.

L1040 (blank)

논리 구간을 나누는 빈 줄입니다. 바로 위에서 준비한 상태와 다음 제어 흐름을 분리해 읽습니다.

L1041 if (speed >= PCIE_SPEED_64_0GT)

상태나 ID를 분류하는 분기입니다. 이 조건의 참·거짓은 장치 register, firmware 기술 또는 CPU mode 중 하나에서 결정됩니다.

L1042 dw_pcie_program_presets(pp, PCIE_SPEED_64_0GT);

DWC Root Port DBI와 outbound window 설정의 한 단계입니다. 이 줄이 바꾸는 software 상태를 대응하는 controller register나 trace checkpoint와 연결해 확인합니다.

L1043 }

제어 블록의 범위를 표시합니다. 이 괄호 안에서만 유효한 lock, CPU context, resource lifetime을 함께 표시해 둡니다.

L1044 (blank)

논리 구간을 나누는 빈 줄입니다. 바로 위에서 준비한 상태와 다음 제어 흐름을 분리해 읽습니다.

L1045 int dw_pcie_setup_rc(struct dw_pcie_rp *pp)

함수 경계입니다. 호출자가 넘긴 번호 공간과 현재 CPU/controller context가 DWC Root Port DBI와 outbound window 설정의 입력 계약을 만족하는지 확인합니다.

L1046 {

제어 블록의 범위를 표시합니다. 이 괄호 안에서만 유효한 lock, CPU context, resource lifetime을 함께 표시해 둡니다.

L1047 struct dw_pcie *pci = to_dw_pcie_from_pp(pp);

DWC Root Port DBI와 outbound window 설정의 한 단계입니다. 이 줄이 바꾸는 software 상태를 대응하는 controller register나 trace checkpoint와 연결해 확인합니다.

L1048 u32 val;

DWC Root Port DBI와 outbound window 설정의 한 단계입니다. 이 줄이 바꾸는 software 상태를 대응하는 controller register나 trace checkpoint와 연결해 확인합니다.

L1049 int ret;

DWC Root Port DBI와 outbound window 설정의 한 단계입니다. 이 줄이 바꾸는 software 상태를 대응하는 controller register나 trace checkpoint와 연결해 확인합니다.

L1050 (blank)

논리 구간을 나누는 빈 줄입니다. 바로 위에서 준비한 상태와 다음 제어 흐름을 분리해 읽습니다.

L1051 /*

원본 주석입니다. DWC Root Port DBI와 outbound window 설정에서 뒤따르는 코드가 전제하는 하드웨어 조건이나 예외를 먼저 확인합니다.

L1052 * Enable DBI read-only registers for writing/updating configuration.

원본 주석입니다. DWC Root Port DBI와 outbound window 설정에서 뒤따르는 코드가 전제하는 하드웨어 조건이나 예외를 먼저 확인합니다.

L1053 * Write permission gets disabled towards the end of this function.

원본 주석입니다. DWC Root Port DBI와 outbound window 설정에서 뒤따르는 코드가 전제하는 하드웨어 조건이나 예외를 먼저 확인합니다.

L1054 */

원본 주석입니다. DWC Root Port DBI와 outbound window 설정에서 뒤따르는 코드가 전제하는 하드웨어 조건이나 예외를 먼저 확인합니다.

L1055 dw_pcie_dbi_ro_wr_en(pci);

DWC Root Port DBI와 outbound window 설정의 한 단계입니다. 이 줄이 바꾸는 software 상태를 대응하는 controller register나 trace checkpoint와 연결해 확인합니다.

L1056 (blank)

논리 구간을 나누는 빈 줄입니다. 바로 위에서 준비한 상태와 다음 제어 흐름을 분리해 읽습니다.

L1057 dw_pcie_setup(pci);

DWC Root Port DBI와 outbound window 설정의 한 단계입니다. 이 줄이 바꾸는 software 상태를 대응하는 controller register나 trace checkpoint와 연결해 확인합니다.

L1058 (blank)

논리 구간을 나누는 빈 줄입니다. 바로 위에서 준비한 상태와 다음 제어 흐름을 분리해 읽습니다.

L1059 dw_pcie_msi_init(pp);

DWC Root Port DBI와 outbound window 설정의 한 단계입니다. 이 줄이 바꾸는 software 상태를 대응하는 controller register나 trace checkpoint와 연결해 확인합니다.

L1060 (blank)

논리 구간을 나누는 빈 줄입니다. 바로 위에서 준비한 상태와 다음 제어 흐름을 분리해 읽습니다.

L1061 /* Setup RC BARs */

원본 주석입니다. DWC Root Port DBI와 outbound window 설정에서 뒤따르는 코드가 전제하는 하드웨어 조건이나 예외를 먼저 확인합니다.

L1062 dw_pcie_writel_dbi(pci, PCI_BASE_ADDRESS_0, 0x00000004);

hardware 또는 공유 메모리에 상태를 게시합니다. write가 완료되는 지점과 다음 read/doorbell 사이의 ordering 조건을 확인합니다.

L1063 dw_pcie_writel_dbi(pci, PCI_BASE_ADDRESS_1, 0x00000000);

hardware 또는 공유 메모리에 상태를 게시합니다. write가 완료되는 지점과 다음 read/doorbell 사이의 ordering 조건을 확인합니다.

L1064 (blank)

논리 구간을 나누는 빈 줄입니다. 바로 위에서 준비한 상태와 다음 제어 흐름을 분리해 읽습니다.

L1065 /* Setup interrupt pins */

원본 주석입니다. DWC Root Port DBI와 outbound window 설정에서 뒤따르는 코드가 전제하는 하드웨어 조건이나 예외를 먼저 확인합니다.

L1066 val = dw_pcie_readl_dbi(pci, PCI_INTERRUPT_LINE);

공유 상태나 MMIO를 읽습니다. 이 값은 단순 변수라기보다 hardware가 갱신한 시점과 read ordering을 포함해 해석해야 합니다.

L1067 val &= 0xffff00ff;

DWC Root Port DBI와 outbound window 설정의 한 단계입니다. 이 줄이 바꾸는 software 상태를 대응하는 controller register나 trace checkpoint와 연결해 확인합니다.

L1068 val |= 0x00000100;

DWC Root Port DBI와 outbound window 설정의 한 단계입니다. 이 줄이 바꾸는 software 상태를 대응하는 controller register나 trace checkpoint와 연결해 확인합니다.

L1069 dw_pcie_writel_dbi(pci, PCI_INTERRUPT_LINE, val);

hardware 또는 공유 메모리에 상태를 게시합니다. write가 완료되는 지점과 다음 read/doorbell 사이의 ordering 조건을 확인합니다.

L1070 (blank)

논리 구간을 나누는 빈 줄입니다. 바로 위에서 준비한 상태와 다음 제어 흐름을 분리해 읽습니다.

L1071 /* Setup bus numbers */

원본 주석입니다. DWC Root Port DBI와 outbound window 설정에서 뒤따르는 코드가 전제하는 하드웨어 조건이나 예외를 먼저 확인합니다.

L1072 val = dw_pcie_readl_dbi(pci, PCI_PRIMARY_BUS);

공유 상태나 MMIO를 읽습니다. 이 값은 단순 변수라기보다 hardware가 갱신한 시점과 read ordering을 포함해 해석해야 합니다.

L1073 val &= 0xff000000;

DWC Root Port DBI와 outbound window 설정의 한 단계입니다. 이 줄이 바꾸는 software 상태를 대응하는 controller register나 trace checkpoint와 연결해 확인합니다.

L1074 val |= 0x00ff0100;

DWC Root Port DBI와 outbound window 설정의 한 단계입니다. 이 줄이 바꾸는 software 상태를 대응하는 controller register나 trace checkpoint와 연결해 확인합니다.

L1075 dw_pcie_writel_dbi(pci, PCI_PRIMARY_BUS, val);

hardware 또는 공유 메모리에 상태를 게시합니다. write가 완료되는 지점과 다음 read/doorbell 사이의 ordering 조건을 확인합니다.

L1076 (blank)

논리 구간을 나누는 빈 줄입니다. 바로 위에서 준비한 상태와 다음 제어 흐름을 분리해 읽습니다.

L1077 /* Setup command register */

원본 주석입니다. DWC Root Port DBI와 outbound window 설정에서 뒤따르는 코드가 전제하는 하드웨어 조건이나 예외를 먼저 확인합니다.

L1078 val = dw_pcie_readl_dbi(pci, PCI_COMMAND);

공유 상태나 MMIO를 읽습니다. 이 값은 단순 변수라기보다 hardware가 갱신한 시점과 read ordering을 포함해 해석해야 합니다.

L1079 val &= 0xffff0000;

DWC Root Port DBI와 outbound window 설정의 한 단계입니다. 이 줄이 바꾸는 software 상태를 대응하는 controller register나 trace checkpoint와 연결해 확인합니다.

L1080 val |= PCI_COMMAND_IO | PCI_COMMAND_MEMORY |

DWC Root Port DBI와 outbound window 설정의 한 단계입니다. 이 줄이 바꾸는 software 상태를 대응하는 controller register나 trace checkpoint와 연결해 확인합니다.

L1081 PCI_COMMAND_MASTER | PCI_COMMAND_SERR;

DWC Root Port DBI와 outbound window 설정의 한 단계입니다. 이 줄이 바꾸는 software 상태를 대응하는 controller register나 trace checkpoint와 연결해 확인합니다.

L1082 dw_pcie_writel_dbi(pci, PCI_COMMAND, val);

hardware 또는 공유 메모리에 상태를 게시합니다. write가 완료되는 지점과 다음 read/doorbell 사이의 ordering 조건을 확인합니다.

L1083 (blank)

논리 구간을 나누는 빈 줄입니다. 바로 위에서 준비한 상태와 다음 제어 흐름을 분리해 읽습니다.

L1084 dw_pcie_hide_unsupported_l1ss(pci);

DWC Root Port DBI와 outbound window 설정의 한 단계입니다. 이 줄이 바꾸는 software 상태를 대응하는 controller register나 trace checkpoint와 연결해 확인합니다.

L1085 (blank)

논리 구간을 나누는 빈 줄입니다. 바로 위에서 준비한 상태와 다음 제어 흐름을 분리해 읽습니다.

L1086 dw_pcie_config_presets(pp);

DWC Root Port DBI와 outbound window 설정의 한 단계입니다. 이 줄이 바꾸는 software 상태를 대응하는 controller register나 trace checkpoint와 연결해 확인합니다.

L1087 /*

원본 주석입니다. DWC Root Port DBI와 outbound window 설정에서 뒤따르는 코드가 전제하는 하드웨어 조건이나 예외를 먼저 확인합니다.

L1088 * If the platform provides its own child bus config accesses, it means

원본 주석입니다. DWC Root Port DBI와 outbound window 설정에서 뒤따르는 코드가 전제하는 하드웨어 조건이나 예외를 먼저 확인합니다.

L1089 * the platform uses its own address translation component rather than

원본 주석입니다. DWC Root Port DBI와 outbound window 설정에서 뒤따르는 코드가 전제하는 하드웨어 조건이나 예외를 먼저 확인합니다.

L1090 * ATU, so we should not program the ATU here.

원본 주석입니다. DWC Root Port DBI와 outbound window 설정에서 뒤따르는 코드가 전제하는 하드웨어 조건이나 예외를 먼저 확인합니다.

L1091 */

원본 주석입니다. DWC Root Port DBI와 outbound window 설정에서 뒤따르는 코드가 전제하는 하드웨어 조건이나 예외를 먼저 확인합니다.

L1092 if (pp->bridge->child_ops == &dw_child_pcie_ops) {

상태나 ID를 분류하는 분기입니다. 이 조건의 참·거짓은 장치 register, firmware 기술 또는 CPU mode 중 하나에서 결정됩니다.

L1093 ret = dw_pcie_iatu_setup(pp);

DWC Root Port DBI와 outbound window 설정의 한 단계입니다. 이 줄이 바꾸는 software 상태를 대응하는 controller register나 trace checkpoint와 연결해 확인합니다.

L1094 if (ret)

상태나 ID를 분류하는 분기입니다. 이 조건의 참·거짓은 장치 register, firmware 기술 또는 CPU mode 중 하나에서 결정됩니다.

L1095 return ret;

호출자에게 상태를 반환합니다. 반환값만 보지 말고 그 전에 controller ownership이나 active 상태가 정리되었는지 확인합니다.

L1096 }

제어 블록의 범위를 표시합니다. 이 괄호 안에서만 유효한 lock, CPU context, resource lifetime을 함께 표시해 둡니다.

L1097 (blank)

논리 구간을 나누는 빈 줄입니다. 바로 위에서 준비한 상태와 다음 제어 흐름을 분리해 읽습니다.

L1098 dw_pcie_writel_dbi(pci, PCI_BASE_ADDRESS_0, 0);

hardware 또는 공유 메모리에 상태를 게시합니다. write가 완료되는 지점과 다음 read/doorbell 사이의 ordering 조건을 확인합니다.

L1099 (blank)

논리 구간을 나누는 빈 줄입니다. 바로 위에서 준비한 상태와 다음 제어 흐름을 분리해 읽습니다.

L1100 /* Program correct class for RC */

원본 주석입니다. DWC Root Port DBI와 outbound window 설정에서 뒤따르는 코드가 전제하는 하드웨어 조건이나 예외를 먼저 확인합니다.

L1101 dw_pcie_writew_dbi(pci, PCI_CLASS_DEVICE, PCI_CLASS_BRIDGE_PCI);

hardware 또는 공유 메모리에 상태를 게시합니다. write가 완료되는 지점과 다음 read/doorbell 사이의 ordering 조건을 확인합니다.

L1102 (blank)

논리 구간을 나누는 빈 줄입니다. 바로 위에서 준비한 상태와 다음 제어 흐름을 분리해 읽습니다.

L1103 val = dw_pcie_readl_dbi(pci, PCIE_LINK_WIDTH_SPEED_CONTROL);

공유 상태나 MMIO를 읽습니다. 이 값은 단순 변수라기보다 hardware가 갱신한 시점과 read ordering을 포함해 해석해야 합니다.

L1104 val |= PORT_LOGIC_SPEED_CHANGE;

DWC Root Port DBI와 outbound window 설정의 한 단계입니다. 이 줄이 바꾸는 software 상태를 대응하는 controller register나 trace checkpoint와 연결해 확인합니다.

L1105 dw_pcie_writel_dbi(pci, PCIE_LINK_WIDTH_SPEED_CONTROL, val);

hardware 또는 공유 메모리에 상태를 게시합니다. write가 완료되는 지점과 다음 read/doorbell 사이의 ordering 조건을 확인합니다.

L1106 (blank)

논리 구간을 나누는 빈 줄입니다. 바로 위에서 준비한 상태와 다음 제어 흐름을 분리해 읽습니다.

L1107 dw_pcie_dbi_ro_wr_dis(pci);

DWC Root Port DBI와 outbound window 설정의 한 단계입니다. 이 줄이 바꾸는 software 상태를 대응하는 controller register나 trace checkpoint와 연결해 확인합니다.

L1108 (blank)

논리 구간을 나누는 빈 줄입니다. 바로 위에서 준비한 상태와 다음 제어 흐름을 분리해 읽습니다.

L1109 return 0;

호출자에게 상태를 반환합니다. 반환값만 보지 말고 그 전에 controller ownership이나 active 상태가 정리되었는지 확인합니다.

L1110 }

제어 블록의 범위를 표시합니다. 이 괄호 안에서만 유효한 lock, CPU context, resource lifetime을 함께 표시해 둡니다.

L1111 EXPORT_SYMBOL_GPL(dw_pcie_setup_rc);

DWC Root Port DBI와 outbound window 설정의 한 단계입니다. 이 줄이 바꾸는 software 상태를 대응하는 controller register나 trace checkpoint와 연결해 확인합니다.

L1112 (blank)

논리 구간을 나누는 빈 줄입니다. 바로 위에서 준비한 상태와 다음 제어 흐름을 분리해 읽습니다.

L1113 static int dw_pcie_pme_turn_off(struct dw_pcie *pci)

함수 경계입니다. 호출자가 넘긴 번호 공간과 현재 CPU/controller context가 DWC Root Port DBI와 outbound window 설정의 입력 계약을 만족하는지 확인합니다.

L1114 {

제어 블록의 범위를 표시합니다. 이 괄호 안에서만 유효한 lock, CPU context, resource lifetime을 함께 표시해 둡니다.

L1115 struct dw_pcie_ob_atu_cfg atu = { 0 };

DWC Root Port DBI와 outbound window 설정의 한 단계입니다. 이 줄이 바꾸는 software 상태를 대응하는 controller register나 trace checkpoint와 연결해 확인합니다.

L1116 void __iomem *mem;

DWC Root Port DBI와 outbound window 설정의 한 단계입니다. 이 줄이 바꾸는 software 상태를 대응하는 controller register나 trace checkpoint와 연결해 확인합니다.

L1117 int ret;

DWC Root Port DBI와 outbound window 설정의 한 단계입니다. 이 줄이 바꾸는 software 상태를 대응하는 controller register나 trace checkpoint와 연결해 확인합니다.

L1118 (blank)

논리 구간을 나누는 빈 줄입니다. 바로 위에서 준비한 상태와 다음 제어 흐름을 분리해 읽습니다.

L1119 if (pci->num_ob_windows <= pci->pp.msg_atu_index)

상태나 ID를 분류하는 분기입니다. 이 조건의 참·거짓은 장치 register, firmware 기술 또는 CPU mode 중 하나에서 결정됩니다.

Linux v6.18.37 · 실제 원본 코드

DWC 내부 MSI status block을 읽고 irqdomain으로 전달하는 handler

drivers/pci/controller/dwc/pcie-designware-host.c:41-107

41	.supported_flags	= DW_PCIE_MSI_FLAGS_SUPPORTED,
42	.bus_select_token	= DOMAIN_BUS_PCI_MSI,
43	.chip_flags		= MSI_CHIP_FLAG_SET_ACK,
44	.prefix			= "DW-",
45	.init_dev_msi_info	= msi_lib_init_dev_msi_info,
46};
47 
48/* MSI int handler */
49irqreturn_t dw_handle_msi_irq(struct dw_pcie_rp *pp)
50{
51	int i, pos;
52	unsigned long val;
53	u32 status, num_ctrls;
54	irqreturn_t ret = IRQ_NONE;
55	struct dw_pcie *pci = to_dw_pcie_from_pp(pp);
56 
57	num_ctrls = pp->num_vectors / MAX_MSI_IRQS_PER_CTRL;
58 
59	for (i = 0; i < num_ctrls; i++) {
60		status = dw_pcie_readl_dbi(pci, PCIE_MSI_INTR0_STATUS +
61					   (i * MSI_REG_CTRL_BLOCK_SIZE));
62		if (!status)
63			continue;
64 
65		ret = IRQ_HANDLED;
66		val = status;
67		pos = 0;
68		while ((pos = find_next_bit(&val, MAX_MSI_IRQS_PER_CTRL,
69					    pos)) != MAX_MSI_IRQS_PER_CTRL) {
70			generic_handle_domain_irq(pp->irq_domain,
71						  (i * MAX_MSI_IRQS_PER_CTRL) +
72						  pos);
73			pos++;
74		}
75	}
76 
77	return ret;
78}
79 
80/* Chained MSI interrupt service routine */
81static void dw_chained_msi_isr(struct irq_desc *desc)
82{
83	struct irq_chip *chip = irq_desc_get_chip(desc);
84	struct dw_pcie_rp *pp;
85 
86	chained_irq_enter(chip, desc);
87 
88	pp = irq_desc_get_handler_data(desc);
89	dw_handle_msi_irq(pp);
90 
91	chained_irq_exit(chip, desc);
92}
93 
94static void dw_pci_setup_msi_msg(struct irq_data *d, struct msi_msg *msg)
95{
96	struct dw_pcie_rp *pp = irq_data_get_irq_chip_data(d);
97	struct dw_pcie *pci = to_dw_pcie_from_pp(pp);
98	u64 msi_target;
99 
100	msi_target = (u64)pp->msi_data;
101 
102	msg->address_lo = lower_32_bits(msi_target);
103	msg->address_hi = upper_32_bits(msi_target);
104 
105	msg->data = d->hwirq;
106 
107	dev_dbg(pci->dev, "msi#%d address_hi %#x address_lo %#x\n",
L41 .supported_flags = DW_PCIE_MSI_FLAGS_SUPPORTED,

DWC 내부 MSI status block을 읽고 irqdomain으로 전달하는 handler의 한 단계입니다. 이 줄이 바꾸는 software 상태를 대응하는 controller register나 trace checkpoint와 연결해 확인합니다.

L42 .bus_select_token = DOMAIN_BUS_PCI_MSI,

DWC 내부 MSI status block을 읽고 irqdomain으로 전달하는 handler의 한 단계입니다. 이 줄이 바꾸는 software 상태를 대응하는 controller register나 trace checkpoint와 연결해 확인합니다.

L43 .chip_flags = MSI_CHIP_FLAG_SET_ACK,

DWC 내부 MSI status block을 읽고 irqdomain으로 전달하는 handler의 한 단계입니다. 이 줄이 바꾸는 software 상태를 대응하는 controller register나 trace checkpoint와 연결해 확인합니다.

L44 .prefix = "DW-",

DWC 내부 MSI status block을 읽고 irqdomain으로 전달하는 handler의 한 단계입니다. 이 줄이 바꾸는 software 상태를 대응하는 controller register나 trace checkpoint와 연결해 확인합니다.

L45 .init_dev_msi_info = msi_lib_init_dev_msi_info,

DWC 내부 MSI status block을 읽고 irqdomain으로 전달하는 handler의 한 단계입니다. 이 줄이 바꾸는 software 상태를 대응하는 controller register나 trace checkpoint와 연결해 확인합니다.

L46 };

DWC 내부 MSI status block을 읽고 irqdomain으로 전달하는 handler의 한 단계입니다. 이 줄이 바꾸는 software 상태를 대응하는 controller register나 trace checkpoint와 연결해 확인합니다.

L47 (blank)

논리 구간을 나누는 빈 줄입니다. 바로 위에서 준비한 상태와 다음 제어 흐름을 분리해 읽습니다.

L48 /* MSI int handler */

원본 주석입니다. DWC 내부 MSI status block을 읽고 irqdomain으로 전달하는 handler에서 뒤따르는 코드가 전제하는 하드웨어 조건이나 예외를 먼저 확인합니다.

L49 irqreturn_t dw_handle_msi_irq(struct dw_pcie_rp *pp)

함수 경계입니다. 호출자가 넘긴 번호 공간과 현재 CPU/controller context가 DWC 내부 MSI status block을 읽고 irqdomain으로 전달하는 handler의 입력 계약을 만족하는지 확인합니다.

L50 {

제어 블록의 범위를 표시합니다. 이 괄호 안에서만 유효한 lock, CPU context, resource lifetime을 함께 표시해 둡니다.

L51 int i, pos;

DWC 내부 MSI status block을 읽고 irqdomain으로 전달하는 handler의 한 단계입니다. 이 줄이 바꾸는 software 상태를 대응하는 controller register나 trace checkpoint와 연결해 확인합니다.

L52 unsigned long val;

DWC 내부 MSI status block을 읽고 irqdomain으로 전달하는 handler의 한 단계입니다. 이 줄이 바꾸는 software 상태를 대응하는 controller register나 trace checkpoint와 연결해 확인합니다.

L53 u32 status, num_ctrls;

DWC 내부 MSI status block을 읽고 irqdomain으로 전달하는 handler의 한 단계입니다. 이 줄이 바꾸는 software 상태를 대응하는 controller register나 trace checkpoint와 연결해 확인합니다.

L54 irqreturn_t ret = IRQ_NONE;

DWC 내부 MSI status block을 읽고 irqdomain으로 전달하는 handler의 한 단계입니다. 이 줄이 바꾸는 software 상태를 대응하는 controller register나 trace checkpoint와 연결해 확인합니다.

L55 struct dw_pcie *pci = to_dw_pcie_from_pp(pp);

DWC 내부 MSI status block을 읽고 irqdomain으로 전달하는 handler의 한 단계입니다. 이 줄이 바꾸는 software 상태를 대응하는 controller register나 trace checkpoint와 연결해 확인합니다.

L56 (blank)

논리 구간을 나누는 빈 줄입니다. 바로 위에서 준비한 상태와 다음 제어 흐름을 분리해 읽습니다.

L57 num_ctrls = pp->num_vectors / MAX_MSI_IRQS_PER_CTRL;

DWC 내부 MSI status block을 읽고 irqdomain으로 전달하는 handler의 한 단계입니다. 이 줄이 바꾸는 software 상태를 대응하는 controller register나 trace checkpoint와 연결해 확인합니다.

L58 (blank)

논리 구간을 나누는 빈 줄입니다. 바로 위에서 준비한 상태와 다음 제어 흐름을 분리해 읽습니다.

L59 for (i = 0; i < num_ctrls; i++) {

여러 pending source 또는 resource를 순회하는 구간입니다. 한 번의 진입에서 처리 가능한 양과 종료 조건이 interrupt latency에 영향을 줍니다.

L60 status = dw_pcie_readl_dbi(pci, PCIE_MSI_INTR0_STATUS +

공유 상태나 MMIO를 읽습니다. 이 값은 단순 변수라기보다 hardware가 갱신한 시점과 read ordering을 포함해 해석해야 합니다.

L61 (i * MSI_REG_CTRL_BLOCK_SIZE));

DWC 내부 MSI status block을 읽고 irqdomain으로 전달하는 handler의 한 단계입니다. 이 줄이 바꾸는 software 상태를 대응하는 controller register나 trace checkpoint와 연결해 확인합니다.

L62 if (!status)

상태나 ID를 분류하는 분기입니다. 이 조건의 참·거짓은 장치 register, firmware 기술 또는 CPU mode 중 하나에서 결정됩니다.

L63 continue;

DWC 내부 MSI status block을 읽고 irqdomain으로 전달하는 handler의 한 단계입니다. 이 줄이 바꾸는 software 상태를 대응하는 controller register나 trace checkpoint와 연결해 확인합니다.

L64 (blank)

논리 구간을 나누는 빈 줄입니다. 바로 위에서 준비한 상태와 다음 제어 흐름을 분리해 읽습니다.

L65 ret = IRQ_HANDLED;

DWC 내부 MSI status block을 읽고 irqdomain으로 전달하는 handler의 한 단계입니다. 이 줄이 바꾸는 software 상태를 대응하는 controller register나 trace checkpoint와 연결해 확인합니다.

L66 val = status;

DWC 내부 MSI status block을 읽고 irqdomain으로 전달하는 handler의 한 단계입니다. 이 줄이 바꾸는 software 상태를 대응하는 controller register나 trace checkpoint와 연결해 확인합니다.

L67 pos = 0;

DWC 내부 MSI status block을 읽고 irqdomain으로 전달하는 handler의 한 단계입니다. 이 줄이 바꾸는 software 상태를 대응하는 controller register나 trace checkpoint와 연결해 확인합니다.

L68 while ((pos = find_next_bit(&val, MAX_MSI_IRQS_PER_CTRL,

여러 pending source 또는 resource를 순회하는 구간입니다. 한 번의 진입에서 처리 가능한 양과 종료 조건이 interrupt latency에 영향을 줍니다.

L69 pos)) != MAX_MSI_IRQS_PER_CTRL) {

DWC 내부 MSI status block을 읽고 irqdomain으로 전달하는 handler의 한 단계입니다. 이 줄이 바꾸는 software 상태를 대응하는 controller register나 trace checkpoint와 연결해 확인합니다.

L70 generic_handle_domain_irq(pp->irq_domain,

hardware ID를 Linux IRQ core로 넘기는 경계입니다. 여기부터 hwirq, virq, irq_desc, irqaction의 번호 공간을 구분해 추적합니다.

L71 (i * MAX_MSI_IRQS_PER_CTRL) +

DWC 내부 MSI status block을 읽고 irqdomain으로 전달하는 handler의 한 단계입니다. 이 줄이 바꾸는 software 상태를 대응하는 controller register나 trace checkpoint와 연결해 확인합니다.

L72 pos);

DWC 내부 MSI status block을 읽고 irqdomain으로 전달하는 handler의 한 단계입니다. 이 줄이 바꾸는 software 상태를 대응하는 controller register나 trace checkpoint와 연결해 확인합니다.

L73 pos++;

DWC 내부 MSI status block을 읽고 irqdomain으로 전달하는 handler의 한 단계입니다. 이 줄이 바꾸는 software 상태를 대응하는 controller register나 trace checkpoint와 연결해 확인합니다.

L74 }

제어 블록의 범위를 표시합니다. 이 괄호 안에서만 유효한 lock, CPU context, resource lifetime을 함께 표시해 둡니다.

L75 }

제어 블록의 범위를 표시합니다. 이 괄호 안에서만 유효한 lock, CPU context, resource lifetime을 함께 표시해 둡니다.

L76 (blank)

논리 구간을 나누는 빈 줄입니다. 바로 위에서 준비한 상태와 다음 제어 흐름을 분리해 읽습니다.

L77 return ret;

호출자에게 상태를 반환합니다. 반환값만 보지 말고 그 전에 controller ownership이나 active 상태가 정리되었는지 확인합니다.

L78 }

제어 블록의 범위를 표시합니다. 이 괄호 안에서만 유효한 lock, CPU context, resource lifetime을 함께 표시해 둡니다.

L79 (blank)

논리 구간을 나누는 빈 줄입니다. 바로 위에서 준비한 상태와 다음 제어 흐름을 분리해 읽습니다.

L80 /* Chained MSI interrupt service routine */

원본 주석입니다. DWC 내부 MSI status block을 읽고 irqdomain으로 전달하는 handler에서 뒤따르는 코드가 전제하는 하드웨어 조건이나 예외를 먼저 확인합니다.

L81 static void dw_chained_msi_isr(struct irq_desc *desc)

함수 경계입니다. 호출자가 넘긴 번호 공간과 현재 CPU/controller context가 DWC 내부 MSI status block을 읽고 irqdomain으로 전달하는 handler의 입력 계약을 만족하는지 확인합니다.

L82 {

제어 블록의 범위를 표시합니다. 이 괄호 안에서만 유효한 lock, CPU context, resource lifetime을 함께 표시해 둡니다.

L83 struct irq_chip *chip = irq_desc_get_chip(desc);

DWC 내부 MSI status block을 읽고 irqdomain으로 전달하는 handler의 한 단계입니다. 이 줄이 바꾸는 software 상태를 대응하는 controller register나 trace checkpoint와 연결해 확인합니다.

L84 struct dw_pcie_rp *pp;

DWC 내부 MSI status block을 읽고 irqdomain으로 전달하는 handler의 한 단계입니다. 이 줄이 바꾸는 software 상태를 대응하는 controller register나 trace checkpoint와 연결해 확인합니다.

L85 (blank)

논리 구간을 나누는 빈 줄입니다. 바로 위에서 준비한 상태와 다음 제어 흐름을 분리해 읽습니다.

L86 chained_irq_enter(chip, desc);

DWC 내부 MSI status block을 읽고 irqdomain으로 전달하는 handler의 한 단계입니다. 이 줄이 바꾸는 software 상태를 대응하는 controller register나 trace checkpoint와 연결해 확인합니다.

L87 (blank)

논리 구간을 나누는 빈 줄입니다. 바로 위에서 준비한 상태와 다음 제어 흐름을 분리해 읽습니다.

L88 pp = irq_desc_get_handler_data(desc);

DWC 내부 MSI status block을 읽고 irqdomain으로 전달하는 handler의 한 단계입니다. 이 줄이 바꾸는 software 상태를 대응하는 controller register나 trace checkpoint와 연결해 확인합니다.

L89 dw_handle_msi_irq(pp);

DWC 내부 MSI status block을 읽고 irqdomain으로 전달하는 handler의 한 단계입니다. 이 줄이 바꾸는 software 상태를 대응하는 controller register나 trace checkpoint와 연결해 확인합니다.

L90 (blank)

논리 구간을 나누는 빈 줄입니다. 바로 위에서 준비한 상태와 다음 제어 흐름을 분리해 읽습니다.

L91 chained_irq_exit(chip, desc);

DWC 내부 MSI status block을 읽고 irqdomain으로 전달하는 handler의 한 단계입니다. 이 줄이 바꾸는 software 상태를 대응하는 controller register나 trace checkpoint와 연결해 확인합니다.

L92 }

제어 블록의 범위를 표시합니다. 이 괄호 안에서만 유효한 lock, CPU context, resource lifetime을 함께 표시해 둡니다.

L93 (blank)

논리 구간을 나누는 빈 줄입니다. 바로 위에서 준비한 상태와 다음 제어 흐름을 분리해 읽습니다.

L94 static void dw_pci_setup_msi_msg(struct irq_data *d, struct msi_msg *msg)

함수 경계입니다. 호출자가 넘긴 번호 공간과 현재 CPU/controller context가 DWC 내부 MSI status block을 읽고 irqdomain으로 전달하는 handler의 입력 계약을 만족하는지 확인합니다.

L95 {

제어 블록의 범위를 표시합니다. 이 괄호 안에서만 유효한 lock, CPU context, resource lifetime을 함께 표시해 둡니다.

L96 struct dw_pcie_rp *pp = irq_data_get_irq_chip_data(d);

DWC 내부 MSI status block을 읽고 irqdomain으로 전달하는 handler의 한 단계입니다. 이 줄이 바꾸는 software 상태를 대응하는 controller register나 trace checkpoint와 연결해 확인합니다.

L97 struct dw_pcie *pci = to_dw_pcie_from_pp(pp);

DWC 내부 MSI status block을 읽고 irqdomain으로 전달하는 handler의 한 단계입니다. 이 줄이 바꾸는 software 상태를 대응하는 controller register나 trace checkpoint와 연결해 확인합니다.

L98 u64 msi_target;

DWC 내부 MSI status block을 읽고 irqdomain으로 전달하는 handler의 한 단계입니다. 이 줄이 바꾸는 software 상태를 대응하는 controller register나 trace checkpoint와 연결해 확인합니다.

L99 (blank)

논리 구간을 나누는 빈 줄입니다. 바로 위에서 준비한 상태와 다음 제어 흐름을 분리해 읽습니다.

L100 msi_target = (u64)pp->msi_data;

DWC 내부 MSI status block을 읽고 irqdomain으로 전달하는 handler의 한 단계입니다. 이 줄이 바꾸는 software 상태를 대응하는 controller register나 trace checkpoint와 연결해 확인합니다.

L101 (blank)

논리 구간을 나누는 빈 줄입니다. 바로 위에서 준비한 상태와 다음 제어 흐름을 분리해 읽습니다.

L102 msg->address_lo = lower_32_bits(msi_target);

DWC 내부 MSI status block을 읽고 irqdomain으로 전달하는 handler의 한 단계입니다. 이 줄이 바꾸는 software 상태를 대응하는 controller register나 trace checkpoint와 연결해 확인합니다.

L103 msg->address_hi = upper_32_bits(msi_target);

DWC 내부 MSI status block을 읽고 irqdomain으로 전달하는 handler의 한 단계입니다. 이 줄이 바꾸는 software 상태를 대응하는 controller register나 trace checkpoint와 연결해 확인합니다.

L104 (blank)

논리 구간을 나누는 빈 줄입니다. 바로 위에서 준비한 상태와 다음 제어 흐름을 분리해 읽습니다.

L105 msg->data = d->hwirq;

DWC 내부 MSI status block을 읽고 irqdomain으로 전달하는 handler의 한 단계입니다. 이 줄이 바꾸는 software 상태를 대응하는 controller register나 trace checkpoint와 연결해 확인합니다.

L106 (blank)

논리 구간을 나누는 빈 줄입니다. 바로 위에서 준비한 상태와 다음 제어 흐름을 분리해 읽습니다.

L107 dev_dbg(pci->dev, "msi#%d address_hi %#x address_lo %#x\n",

DWC 내부 MSI status block을 읽고 irqdomain으로 전달하는 handler의 한 단계입니다. 이 줄이 바꾸는 software 상태를 대응하는 controller register나 trace checkpoint와 연결해 확인합니다.

Linux v6.18.37 · 실제 원본 코드

DWC MSI target address, enable과 mask register 초기화

drivers/pci/controller/dwc/pcie-designware-host.c:243-293

243			irq_set_chained_handler_and_data(pp->msi_irq[ctrl],
244							 NULL, NULL);
245	}
246 
247	irq_domain_remove(pp->irq_domain);
248}
249EXPORT_SYMBOL_GPL(dw_pcie_free_msi);
250 
251void dw_pcie_msi_init(struct dw_pcie_rp *pp)
252{
253	struct dw_pcie *pci = to_dw_pcie_from_pp(pp);
254	u64 msi_target = (u64)pp->msi_data;
255	u32 ctrl, num_ctrls;
256 
257	if (!pci_msi_enabled() || !pp->has_msi_ctrl)
258		return;
259 
260	num_ctrls = pp->num_vectors / MAX_MSI_IRQS_PER_CTRL;
261 
262	/* Initialize IRQ Status array */
263	for (ctrl = 0; ctrl < num_ctrls; ctrl++) {
264		dw_pcie_writel_dbi(pci, PCIE_MSI_INTR0_MASK +
265				    (ctrl * MSI_REG_CTRL_BLOCK_SIZE),
266				    pp->irq_mask[ctrl]);
267		dw_pcie_writel_dbi(pci, PCIE_MSI_INTR0_ENABLE +
268				    (ctrl * MSI_REG_CTRL_BLOCK_SIZE),
269				    ~0);
270	}
271 
272	/* Program the msi_data */
273	dw_pcie_writel_dbi(pci, PCIE_MSI_ADDR_LO, lower_32_bits(msi_target));
274	dw_pcie_writel_dbi(pci, PCIE_MSI_ADDR_HI, upper_32_bits(msi_target));
275}
276EXPORT_SYMBOL_GPL(dw_pcie_msi_init);
277 
278static int dw_pcie_parse_split_msi_irq(struct dw_pcie_rp *pp)
279{
280	struct dw_pcie *pci = to_dw_pcie_from_pp(pp);
281	struct device *dev = pci->dev;
282	struct platform_device *pdev = to_platform_device(dev);
283	u32 ctrl, max_vectors;
284	int irq;
285 
286	/* Parse any "msiX" IRQs described in the devicetree */
287	for (ctrl = 0; ctrl < MAX_MSI_CTRLS; ctrl++) {
288		char msi_name[] = "msiX";
289 
290		msi_name[3] = '0' + ctrl;
291		irq = platform_get_irq_byname_optional(pdev, msi_name);
292		if (irq == -ENXIO)
293			break;
L243 irq_set_chained_handler_and_data(pp->msi_irq[ctrl],

DWC MSI target address, enable과 mask register 초기화의 한 단계입니다. 이 줄이 바꾸는 software 상태를 대응하는 controller register나 trace checkpoint와 연결해 확인합니다.

L244 NULL, NULL);

DWC MSI target address, enable과 mask register 초기화의 한 단계입니다. 이 줄이 바꾸는 software 상태를 대응하는 controller register나 trace checkpoint와 연결해 확인합니다.

L245 }

제어 블록의 범위를 표시합니다. 이 괄호 안에서만 유효한 lock, CPU context, resource lifetime을 함께 표시해 둡니다.

L246 (blank)

논리 구간을 나누는 빈 줄입니다. 바로 위에서 준비한 상태와 다음 제어 흐름을 분리해 읽습니다.

L247 irq_domain_remove(pp->irq_domain);

DWC MSI target address, enable과 mask register 초기화의 한 단계입니다. 이 줄이 바꾸는 software 상태를 대응하는 controller register나 trace checkpoint와 연결해 확인합니다.

L248 }

제어 블록의 범위를 표시합니다. 이 괄호 안에서만 유효한 lock, CPU context, resource lifetime을 함께 표시해 둡니다.

L249 EXPORT_SYMBOL_GPL(dw_pcie_free_msi);

DWC MSI target address, enable과 mask register 초기화의 한 단계입니다. 이 줄이 바꾸는 software 상태를 대응하는 controller register나 trace checkpoint와 연결해 확인합니다.

L250 (blank)

논리 구간을 나누는 빈 줄입니다. 바로 위에서 준비한 상태와 다음 제어 흐름을 분리해 읽습니다.

L251 void dw_pcie_msi_init(struct dw_pcie_rp *pp)

함수 경계입니다. 호출자가 넘긴 번호 공간과 현재 CPU/controller context가 DWC MSI target address, enable과 mask register 초기화의 입력 계약을 만족하는지 확인합니다.

L252 {

제어 블록의 범위를 표시합니다. 이 괄호 안에서만 유효한 lock, CPU context, resource lifetime을 함께 표시해 둡니다.

L253 struct dw_pcie *pci = to_dw_pcie_from_pp(pp);

DWC MSI target address, enable과 mask register 초기화의 한 단계입니다. 이 줄이 바꾸는 software 상태를 대응하는 controller register나 trace checkpoint와 연결해 확인합니다.

L254 u64 msi_target = (u64)pp->msi_data;

함수 경계입니다. 호출자가 넘긴 번호 공간과 현재 CPU/controller context가 DWC MSI target address, enable과 mask register 초기화의 입력 계약을 만족하는지 확인합니다.

L255 u32 ctrl, num_ctrls;

DWC MSI target address, enable과 mask register 초기화의 한 단계입니다. 이 줄이 바꾸는 software 상태를 대응하는 controller register나 trace checkpoint와 연결해 확인합니다.

L256 (blank)

논리 구간을 나누는 빈 줄입니다. 바로 위에서 준비한 상태와 다음 제어 흐름을 분리해 읽습니다.

L257 if (!pci_msi_enabled() || !pp->has_msi_ctrl)

상태나 ID를 분류하는 분기입니다. 이 조건의 참·거짓은 장치 register, firmware 기술 또는 CPU mode 중 하나에서 결정됩니다.

L258 return;

호출자에게 상태를 반환합니다. 반환값만 보지 말고 그 전에 controller ownership이나 active 상태가 정리되었는지 확인합니다.

L259 (blank)

논리 구간을 나누는 빈 줄입니다. 바로 위에서 준비한 상태와 다음 제어 흐름을 분리해 읽습니다.

L260 num_ctrls = pp->num_vectors / MAX_MSI_IRQS_PER_CTRL;

DWC MSI target address, enable과 mask register 초기화의 한 단계입니다. 이 줄이 바꾸는 software 상태를 대응하는 controller register나 trace checkpoint와 연결해 확인합니다.

L261 (blank)

논리 구간을 나누는 빈 줄입니다. 바로 위에서 준비한 상태와 다음 제어 흐름을 분리해 읽습니다.

L262 /* Initialize IRQ Status array */

원본 주석입니다. DWC MSI target address, enable과 mask register 초기화에서 뒤따르는 코드가 전제하는 하드웨어 조건이나 예외를 먼저 확인합니다.

L263 for (ctrl = 0; ctrl < num_ctrls; ctrl++) {

여러 pending source 또는 resource를 순회하는 구간입니다. 한 번의 진입에서 처리 가능한 양과 종료 조건이 interrupt latency에 영향을 줍니다.

L264 dw_pcie_writel_dbi(pci, PCIE_MSI_INTR0_MASK +

hardware 또는 공유 메모리에 상태를 게시합니다. write가 완료되는 지점과 다음 read/doorbell 사이의 ordering 조건을 확인합니다.

L265 (ctrl * MSI_REG_CTRL_BLOCK_SIZE),

DWC MSI target address, enable과 mask register 초기화의 한 단계입니다. 이 줄이 바꾸는 software 상태를 대응하는 controller register나 trace checkpoint와 연결해 확인합니다.

L266 pp->irq_mask[ctrl]);

DWC MSI target address, enable과 mask register 초기화의 한 단계입니다. 이 줄이 바꾸는 software 상태를 대응하는 controller register나 trace checkpoint와 연결해 확인합니다.

L267 dw_pcie_writel_dbi(pci, PCIE_MSI_INTR0_ENABLE +

hardware 또는 공유 메모리에 상태를 게시합니다. write가 완료되는 지점과 다음 read/doorbell 사이의 ordering 조건을 확인합니다.

L268 (ctrl * MSI_REG_CTRL_BLOCK_SIZE),

DWC MSI target address, enable과 mask register 초기화의 한 단계입니다. 이 줄이 바꾸는 software 상태를 대응하는 controller register나 trace checkpoint와 연결해 확인합니다.

L269 ~0);

DWC MSI target address, enable과 mask register 초기화의 한 단계입니다. 이 줄이 바꾸는 software 상태를 대응하는 controller register나 trace checkpoint와 연결해 확인합니다.

L270 }

제어 블록의 범위를 표시합니다. 이 괄호 안에서만 유효한 lock, CPU context, resource lifetime을 함께 표시해 둡니다.

L271 (blank)

논리 구간을 나누는 빈 줄입니다. 바로 위에서 준비한 상태와 다음 제어 흐름을 분리해 읽습니다.

L272 /* Program the msi_data */

원본 주석입니다. DWC MSI target address, enable과 mask register 초기화에서 뒤따르는 코드가 전제하는 하드웨어 조건이나 예외를 먼저 확인합니다.

L273 dw_pcie_writel_dbi(pci, PCIE_MSI_ADDR_LO, lower_32_bits(msi_target));

hardware 또는 공유 메모리에 상태를 게시합니다. write가 완료되는 지점과 다음 read/doorbell 사이의 ordering 조건을 확인합니다.

L274 dw_pcie_writel_dbi(pci, PCIE_MSI_ADDR_HI, upper_32_bits(msi_target));

hardware 또는 공유 메모리에 상태를 게시합니다. write가 완료되는 지점과 다음 read/doorbell 사이의 ordering 조건을 확인합니다.

L275 }

제어 블록의 범위를 표시합니다. 이 괄호 안에서만 유효한 lock, CPU context, resource lifetime을 함께 표시해 둡니다.

L276 EXPORT_SYMBOL_GPL(dw_pcie_msi_init);

DWC MSI target address, enable과 mask register 초기화의 한 단계입니다. 이 줄이 바꾸는 software 상태를 대응하는 controller register나 trace checkpoint와 연결해 확인합니다.

L277 (blank)

논리 구간을 나누는 빈 줄입니다. 바로 위에서 준비한 상태와 다음 제어 흐름을 분리해 읽습니다.

L278 static int dw_pcie_parse_split_msi_irq(struct dw_pcie_rp *pp)

함수 경계입니다. 호출자가 넘긴 번호 공간과 현재 CPU/controller context가 DWC MSI target address, enable과 mask register 초기화의 입력 계약을 만족하는지 확인합니다.

L279 {

제어 블록의 범위를 표시합니다. 이 괄호 안에서만 유효한 lock, CPU context, resource lifetime을 함께 표시해 둡니다.

L280 struct dw_pcie *pci = to_dw_pcie_from_pp(pp);

DWC MSI target address, enable과 mask register 초기화의 한 단계입니다. 이 줄이 바꾸는 software 상태를 대응하는 controller register나 trace checkpoint와 연결해 확인합니다.

L281 struct device *dev = pci->dev;

DWC MSI target address, enable과 mask register 초기화의 한 단계입니다. 이 줄이 바꾸는 software 상태를 대응하는 controller register나 trace checkpoint와 연결해 확인합니다.

L282 struct platform_device *pdev = to_platform_device(dev);

DWC MSI target address, enable과 mask register 초기화의 한 단계입니다. 이 줄이 바꾸는 software 상태를 대응하는 controller register나 trace checkpoint와 연결해 확인합니다.

L283 u32 ctrl, max_vectors;

DWC MSI target address, enable과 mask register 초기화의 한 단계입니다. 이 줄이 바꾸는 software 상태를 대응하는 controller register나 trace checkpoint와 연결해 확인합니다.

L284 int irq;

DWC MSI target address, enable과 mask register 초기화의 한 단계입니다. 이 줄이 바꾸는 software 상태를 대응하는 controller register나 trace checkpoint와 연결해 확인합니다.

L285 (blank)

논리 구간을 나누는 빈 줄입니다. 바로 위에서 준비한 상태와 다음 제어 흐름을 분리해 읽습니다.

L286 /* Parse any "msiX" IRQs described in the devicetree */

원본 주석입니다. DWC MSI target address, enable과 mask register 초기화에서 뒤따르는 코드가 전제하는 하드웨어 조건이나 예외를 먼저 확인합니다.

L287 for (ctrl = 0; ctrl < MAX_MSI_CTRLS; ctrl++) {

여러 pending source 또는 resource를 순회하는 구간입니다. 한 번의 진입에서 처리 가능한 양과 종료 조건이 interrupt latency에 영향을 줍니다.

L288 char msi_name[] = "msiX";

DWC MSI target address, enable과 mask register 초기화의 한 단계입니다. 이 줄이 바꾸는 software 상태를 대응하는 controller register나 trace checkpoint와 연결해 확인합니다.

L289 (blank)

논리 구간을 나누는 빈 줄입니다. 바로 위에서 준비한 상태와 다음 제어 흐름을 분리해 읽습니다.

L290 msi_name[3] = '0' + ctrl;

DWC MSI target address, enable과 mask register 초기화의 한 단계입니다. 이 줄이 바꾸는 software 상태를 대응하는 controller register나 trace checkpoint와 연결해 확인합니다.

L291 irq = platform_get_irq_byname_optional(pdev, msi_name);

DWC MSI target address, enable과 mask register 초기화의 한 단계입니다. 이 줄이 바꾸는 software 상태를 대응하는 controller register나 trace checkpoint와 연결해 확인합니다.

L292 if (irq == -ENXIO)

상태나 ID를 분류하는 분기입니다. 이 조건의 참·거짓은 장치 register, firmware 기술 또는 CPU mode 중 하나에서 결정됩니다.

L293 break;

DWC MSI target address, enable과 mask register 초기화의 한 단계입니다. 이 줄이 바꾸는 software 상태를 대응하는 controller register나 trace checkpoint와 연결해 확인합니다.

Linux v6.18.37 · 실제 원본 코드

DWC eDMA mode, channel, IRQ와 linked-list resource detection

drivers/pci/controller/dwc/pcie-designware.c:1088-1150

1088						    &paddr, GFP_KERNEL);
1089		if (!ll->vaddr.mem)
1090			return -ENOMEM;
1091 
1092		ll->paddr = paddr;
1093	}
1094 
1095	return 0;
1096}
1097 
1098int dw_pcie_edma_detect(struct dw_pcie *pci)
1099{
1100	int ret;
1101 
1102	/* Don't fail if no eDMA was found (for the backward compatibility) */
1103	ret = dw_pcie_edma_find_chip(pci);
1104	if (ret)
1105		return 0;
1106 
1107	/* Don't fail on the IRQs verification (for the backward compatibility) */
1108	ret = dw_pcie_edma_irq_verify(pci);
1109	if (ret) {
1110		dev_err(pci->dev, "Invalid eDMA IRQs found\n");
1111		return 0;
1112	}
1113 
1114	ret = dw_pcie_edma_ll_alloc(pci);
1115	if (ret) {
1116		dev_err(pci->dev, "Couldn't allocate LLP memory\n");
1117		return ret;
1118	}
1119 
1120	/* Don't fail if the DW eDMA driver can't find the device */
1121	ret = dw_edma_probe(&pci->edma);
1122	if (ret && ret != -ENODEV) {
1123		dev_err(pci->dev, "Couldn't register eDMA device\n");
1124		return ret;
1125	}
1126 
1127	dev_info(pci->dev, "eDMA: unroll %s, %hu wr, %hu rd\n",
1128		 pci->edma.mf == EDMA_MF_EDMA_UNROLL ? "T" : "F",
1129		 pci->edma.ll_wr_cnt, pci->edma.ll_rd_cnt);
1130 
1131	return 0;
1132}
1133 
1134void dw_pcie_edma_remove(struct dw_pcie *pci)
1135{
1136	dw_edma_remove(&pci->edma);
1137}
1138 
1139void dw_pcie_hide_unsupported_l1ss(struct dw_pcie *pci)
1140{
1141	u16 l1ss;
1142	u32 l1ss_cap;
1143 
1144	if (pci->l1ss_support)
1145		return;
1146 
1147	l1ss = dw_pcie_find_ext_capability(pci, PCI_EXT_CAP_ID_L1SS);
1148	if (!l1ss)
1149		return;
1150 
L1088 &paddr, GFP_KERNEL);

DWC eDMA mode, channel, IRQ와 linked-list resource detection의 한 단계입니다. 이 줄이 바꾸는 software 상태를 대응하는 controller register나 trace checkpoint와 연결해 확인합니다.

L1089 if (!ll->vaddr.mem)

상태나 ID를 분류하는 분기입니다. 이 조건의 참·거짓은 장치 register, firmware 기술 또는 CPU mode 중 하나에서 결정됩니다.

L1090 return -ENOMEM;

호출자에게 상태를 반환합니다. 반환값만 보지 말고 그 전에 controller ownership이나 active 상태가 정리되었는지 확인합니다.

L1091 (blank)

논리 구간을 나누는 빈 줄입니다. 바로 위에서 준비한 상태와 다음 제어 흐름을 분리해 읽습니다.

L1092 ll->paddr = paddr;

DWC eDMA mode, channel, IRQ와 linked-list resource detection의 한 단계입니다. 이 줄이 바꾸는 software 상태를 대응하는 controller register나 trace checkpoint와 연결해 확인합니다.

L1093 }

제어 블록의 범위를 표시합니다. 이 괄호 안에서만 유효한 lock, CPU context, resource lifetime을 함께 표시해 둡니다.

L1094 (blank)

논리 구간을 나누는 빈 줄입니다. 바로 위에서 준비한 상태와 다음 제어 흐름을 분리해 읽습니다.

L1095 return 0;

호출자에게 상태를 반환합니다. 반환값만 보지 말고 그 전에 controller ownership이나 active 상태가 정리되었는지 확인합니다.

L1096 }

제어 블록의 범위를 표시합니다. 이 괄호 안에서만 유효한 lock, CPU context, resource lifetime을 함께 표시해 둡니다.

L1097 (blank)

논리 구간을 나누는 빈 줄입니다. 바로 위에서 준비한 상태와 다음 제어 흐름을 분리해 읽습니다.

L1098 int dw_pcie_edma_detect(struct dw_pcie *pci)

함수 경계입니다. 호출자가 넘긴 번호 공간과 현재 CPU/controller context가 DWC eDMA mode, channel, IRQ와 linked-list resource detection의 입력 계약을 만족하는지 확인합니다.

L1099 {

제어 블록의 범위를 표시합니다. 이 괄호 안에서만 유효한 lock, CPU context, resource lifetime을 함께 표시해 둡니다.

L1100 int ret;

DWC eDMA mode, channel, IRQ와 linked-list resource detection의 한 단계입니다. 이 줄이 바꾸는 software 상태를 대응하는 controller register나 trace checkpoint와 연결해 확인합니다.

L1101 (blank)

논리 구간을 나누는 빈 줄입니다. 바로 위에서 준비한 상태와 다음 제어 흐름을 분리해 읽습니다.

L1102 /* Don't fail if no eDMA was found (for the backward compatibility) */

원본 주석입니다. DWC eDMA mode, channel, IRQ와 linked-list resource detection에서 뒤따르는 코드가 전제하는 하드웨어 조건이나 예외를 먼저 확인합니다.

L1103 ret = dw_pcie_edma_find_chip(pci);

DWC eDMA mode, channel, IRQ와 linked-list resource detection의 한 단계입니다. 이 줄이 바꾸는 software 상태를 대응하는 controller register나 trace checkpoint와 연결해 확인합니다.

L1104 if (ret)

상태나 ID를 분류하는 분기입니다. 이 조건의 참·거짓은 장치 register, firmware 기술 또는 CPU mode 중 하나에서 결정됩니다.

L1105 return 0;

호출자에게 상태를 반환합니다. 반환값만 보지 말고 그 전에 controller ownership이나 active 상태가 정리되었는지 확인합니다.

L1106 (blank)

논리 구간을 나누는 빈 줄입니다. 바로 위에서 준비한 상태와 다음 제어 흐름을 분리해 읽습니다.

L1107 /* Don't fail on the IRQs verification (for the backward compatibility) */

원본 주석입니다. DWC eDMA mode, channel, IRQ와 linked-list resource detection에서 뒤따르는 코드가 전제하는 하드웨어 조건이나 예외를 먼저 확인합니다.

L1108 ret = dw_pcie_edma_irq_verify(pci);

DWC eDMA mode, channel, IRQ와 linked-list resource detection의 한 단계입니다. 이 줄이 바꾸는 software 상태를 대응하는 controller register나 trace checkpoint와 연결해 확인합니다.

L1109 if (ret) {

상태나 ID를 분류하는 분기입니다. 이 조건의 참·거짓은 장치 register, firmware 기술 또는 CPU mode 중 하나에서 결정됩니다.

L1110 dev_err(pci->dev, "Invalid eDMA IRQs found\n");

DWC eDMA mode, channel, IRQ와 linked-list resource detection의 한 단계입니다. 이 줄이 바꾸는 software 상태를 대응하는 controller register나 trace checkpoint와 연결해 확인합니다.

L1111 return 0;

호출자에게 상태를 반환합니다. 반환값만 보지 말고 그 전에 controller ownership이나 active 상태가 정리되었는지 확인합니다.

L1112 }

제어 블록의 범위를 표시합니다. 이 괄호 안에서만 유효한 lock, CPU context, resource lifetime을 함께 표시해 둡니다.

L1113 (blank)

논리 구간을 나누는 빈 줄입니다. 바로 위에서 준비한 상태와 다음 제어 흐름을 분리해 읽습니다.

L1114 ret = dw_pcie_edma_ll_alloc(pci);

DWC eDMA mode, channel, IRQ와 linked-list resource detection의 한 단계입니다. 이 줄이 바꾸는 software 상태를 대응하는 controller register나 trace checkpoint와 연결해 확인합니다.

L1115 if (ret) {

상태나 ID를 분류하는 분기입니다. 이 조건의 참·거짓은 장치 register, firmware 기술 또는 CPU mode 중 하나에서 결정됩니다.

L1116 dev_err(pci->dev, "Couldn't allocate LLP memory\n");

DWC eDMA mode, channel, IRQ와 linked-list resource detection의 한 단계입니다. 이 줄이 바꾸는 software 상태를 대응하는 controller register나 trace checkpoint와 연결해 확인합니다.

L1117 return ret;

호출자에게 상태를 반환합니다. 반환값만 보지 말고 그 전에 controller ownership이나 active 상태가 정리되었는지 확인합니다.

L1118 }

제어 블록의 범위를 표시합니다. 이 괄호 안에서만 유효한 lock, CPU context, resource lifetime을 함께 표시해 둡니다.

L1119 (blank)

논리 구간을 나누는 빈 줄입니다. 바로 위에서 준비한 상태와 다음 제어 흐름을 분리해 읽습니다.

L1120 /* Don't fail if the DW eDMA driver can't find the device */

원본 주석입니다. DWC eDMA mode, channel, IRQ와 linked-list resource detection에서 뒤따르는 코드가 전제하는 하드웨어 조건이나 예외를 먼저 확인합니다.

L1121 ret = dw_edma_probe(&pci->edma);

DWC eDMA mode, channel, IRQ와 linked-list resource detection의 한 단계입니다. 이 줄이 바꾸는 software 상태를 대응하는 controller register나 trace checkpoint와 연결해 확인합니다.

L1122 if (ret && ret != -ENODEV) {

상태나 ID를 분류하는 분기입니다. 이 조건의 참·거짓은 장치 register, firmware 기술 또는 CPU mode 중 하나에서 결정됩니다.

L1123 dev_err(pci->dev, "Couldn't register eDMA device\n");

DWC eDMA mode, channel, IRQ와 linked-list resource detection의 한 단계입니다. 이 줄이 바꾸는 software 상태를 대응하는 controller register나 trace checkpoint와 연결해 확인합니다.

L1124 return ret;

호출자에게 상태를 반환합니다. 반환값만 보지 말고 그 전에 controller ownership이나 active 상태가 정리되었는지 확인합니다.

L1125 }

제어 블록의 범위를 표시합니다. 이 괄호 안에서만 유효한 lock, CPU context, resource lifetime을 함께 표시해 둡니다.

L1126 (blank)

논리 구간을 나누는 빈 줄입니다. 바로 위에서 준비한 상태와 다음 제어 흐름을 분리해 읽습니다.

L1127 dev_info(pci->dev, "eDMA: unroll %s, %hu wr, %hu rd\n",

DWC eDMA mode, channel, IRQ와 linked-list resource detection의 한 단계입니다. 이 줄이 바꾸는 software 상태를 대응하는 controller register나 trace checkpoint와 연결해 확인합니다.

L1128 pci->edma.mf == EDMA_MF_EDMA_UNROLL ? "T" : "F",

DWC eDMA mode, channel, IRQ와 linked-list resource detection의 한 단계입니다. 이 줄이 바꾸는 software 상태를 대응하는 controller register나 trace checkpoint와 연결해 확인합니다.

L1129 pci->edma.ll_wr_cnt, pci->edma.ll_rd_cnt);

DWC eDMA mode, channel, IRQ와 linked-list resource detection의 한 단계입니다. 이 줄이 바꾸는 software 상태를 대응하는 controller register나 trace checkpoint와 연결해 확인합니다.

L1130 (blank)

논리 구간을 나누는 빈 줄입니다. 바로 위에서 준비한 상태와 다음 제어 흐름을 분리해 읽습니다.

L1131 return 0;

호출자에게 상태를 반환합니다. 반환값만 보지 말고 그 전에 controller ownership이나 active 상태가 정리되었는지 확인합니다.

L1132 }

제어 블록의 범위를 표시합니다. 이 괄호 안에서만 유효한 lock, CPU context, resource lifetime을 함께 표시해 둡니다.

L1133 (blank)

논리 구간을 나누는 빈 줄입니다. 바로 위에서 준비한 상태와 다음 제어 흐름을 분리해 읽습니다.

L1134 void dw_pcie_edma_remove(struct dw_pcie *pci)

함수 경계입니다. 호출자가 넘긴 번호 공간과 현재 CPU/controller context가 DWC eDMA mode, channel, IRQ와 linked-list resource detection의 입력 계약을 만족하는지 확인합니다.

L1135 {

제어 블록의 범위를 표시합니다. 이 괄호 안에서만 유효한 lock, CPU context, resource lifetime을 함께 표시해 둡니다.

L1136 dw_edma_remove(&pci->edma);

DWC eDMA mode, channel, IRQ와 linked-list resource detection의 한 단계입니다. 이 줄이 바꾸는 software 상태를 대응하는 controller register나 trace checkpoint와 연결해 확인합니다.

L1137 }

제어 블록의 범위를 표시합니다. 이 괄호 안에서만 유효한 lock, CPU context, resource lifetime을 함께 표시해 둡니다.

L1138 (blank)

논리 구간을 나누는 빈 줄입니다. 바로 위에서 준비한 상태와 다음 제어 흐름을 분리해 읽습니다.

L1139 void dw_pcie_hide_unsupported_l1ss(struct dw_pcie *pci)

함수 경계입니다. 호출자가 넘긴 번호 공간과 현재 CPU/controller context가 DWC eDMA mode, channel, IRQ와 linked-list resource detection의 입력 계약을 만족하는지 확인합니다.

L1140 {

제어 블록의 범위를 표시합니다. 이 괄호 안에서만 유효한 lock, CPU context, resource lifetime을 함께 표시해 둡니다.

L1141 u16 l1ss;

DWC eDMA mode, channel, IRQ와 linked-list resource detection의 한 단계입니다. 이 줄이 바꾸는 software 상태를 대응하는 controller register나 trace checkpoint와 연결해 확인합니다.

L1142 u32 l1ss_cap;

DWC eDMA mode, channel, IRQ와 linked-list resource detection의 한 단계입니다. 이 줄이 바꾸는 software 상태를 대응하는 controller register나 trace checkpoint와 연결해 확인합니다.

L1143 (blank)

논리 구간을 나누는 빈 줄입니다. 바로 위에서 준비한 상태와 다음 제어 흐름을 분리해 읽습니다.

L1144 if (pci->l1ss_support)

상태나 ID를 분류하는 분기입니다. 이 조건의 참·거짓은 장치 register, firmware 기술 또는 CPU mode 중 하나에서 결정됩니다.

L1145 return;

호출자에게 상태를 반환합니다. 반환값만 보지 말고 그 전에 controller ownership이나 active 상태가 정리되었는지 확인합니다.

L1146 (blank)

논리 구간을 나누는 빈 줄입니다. 바로 위에서 준비한 상태와 다음 제어 흐름을 분리해 읽습니다.

L1147 l1ss = dw_pcie_find_ext_capability(pci, PCI_EXT_CAP_ID_L1SS);

DWC eDMA mode, channel, IRQ와 linked-list resource detection의 한 단계입니다. 이 줄이 바꾸는 software 상태를 대응하는 controller register나 trace checkpoint와 연결해 확인합니다.

L1148 if (!l1ss)

상태나 ID를 분류하는 분기입니다. 이 조건의 참·거짓은 장치 register, firmware 기술 또는 CPU mode 중 하나에서 결정됩니다.

L1149 return;

호출자에게 상태를 반환합니다. 반환값만 보지 말고 그 전에 controller ownership이나 active 상태가 정리되었는지 확인합니다.

L1150 (blank)

논리 구간을 나누는 빈 줄입니다. 바로 위에서 준비한 상태와 다음 제어 흐름을 분리해 읽습니다.

Linux v6.18.37 · 실제 원본 코드

Vendor ID를 읽어 pci_dev를 만들고 중복 scan을 방지하는 경계

drivers/pci/probe.c:2612-2668

2612	return NULL;
2613}
2614#endif
2615 
2616/*
2617 * Read the config data for a PCI device, sanity-check it,
2618 * and fill in the dev structure.
2619 */
2620static struct pci_dev *pci_scan_device(struct pci_bus *bus, int devfn)
2621{
2622	struct pci_dev *dev;
2623	u32 l;
2624 
2625	/*
2626	 * Create pwrctrl device (if required) for the PCI device to handle the
2627	 * power state. If the pwrctrl device is created, then skip scanning
2628	 * further as the pwrctrl core will rescan the bus after powering on
2629	 * the device.
2630	 */
2631	if (pci_pwrctrl_create_device(bus, devfn))
2632		return NULL;
2633 
2634	if (!pci_bus_read_dev_vendor_id(bus, devfn, &l, 60*1000))
2635		return NULL;
2636 
2637	dev = pci_alloc_dev(bus);
2638	if (!dev)
2639		return NULL;
2640 
2641	dev->devfn = devfn;
2642	dev->vendor = l & 0xffff;
2643	dev->device = (l >> 16) & 0xffff;
2644 
2645	if (pci_setup_device(dev)) {
2646		pci_bus_put(dev->bus);
2647		kfree(dev);
2648		return NULL;
2649	}
2650 
2651	return dev;
2652}
2653 
2654void pcie_report_downtraining(struct pci_dev *dev)
2655{
2656	if (!pci_is_pcie(dev))
2657		return;
2658 
2659	/* Look from the device up to avoid downstream ports with no devices */
2660	if ((pci_pcie_type(dev) != PCI_EXP_TYPE_ENDPOINT) &&
2661	    (pci_pcie_type(dev) != PCI_EXP_TYPE_LEG_END) &&
2662	    (pci_pcie_type(dev) != PCI_EXP_TYPE_UPSTREAM))
2663		return;
2664 
2665	/* Multi-function PCIe devices share the same link/status */
2666	if (PCI_FUNC(dev->devfn) != 0 || dev->is_virtfn)
2667		return;
2668 
L2612 return NULL;

호출자에게 상태를 반환합니다. 반환값만 보지 말고 그 전에 controller ownership이나 active 상태가 정리되었는지 확인합니다.

L2613 }

제어 블록의 범위를 표시합니다. 이 괄호 안에서만 유효한 lock, CPU context, resource lifetime을 함께 표시해 둡니다.

L2614 #endif

빌드 설정에 따라 포함되는 경로입니다. 실행 중 분기가 아니므로 실제 커널의 .config와 전처리 결과를 함께 봐야 합니다.

L2615 (blank)

논리 구간을 나누는 빈 줄입니다. 바로 위에서 준비한 상태와 다음 제어 흐름을 분리해 읽습니다.

L2616 /*

원본 주석입니다. Vendor ID를 읽어 pci_dev를 만들고 중복 scan을 방지하는 경계에서 뒤따르는 코드가 전제하는 하드웨어 조건이나 예외를 먼저 확인합니다.

L2617 * Read the config data for a PCI device, sanity-check it,

원본 주석입니다. Vendor ID를 읽어 pci_dev를 만들고 중복 scan을 방지하는 경계에서 뒤따르는 코드가 전제하는 하드웨어 조건이나 예외를 먼저 확인합니다.

L2618 * and fill in the dev structure.

원본 주석입니다. Vendor ID를 읽어 pci_dev를 만들고 중복 scan을 방지하는 경계에서 뒤따르는 코드가 전제하는 하드웨어 조건이나 예외를 먼저 확인합니다.

L2619 */

원본 주석입니다. Vendor ID를 읽어 pci_dev를 만들고 중복 scan을 방지하는 경계에서 뒤따르는 코드가 전제하는 하드웨어 조건이나 예외를 먼저 확인합니다.

L2620 static struct pci_dev *pci_scan_device(struct pci_bus *bus, int devfn)

Vendor ID를 읽어 pci_dev를 만들고 중복 scan을 방지하는 경계의 한 단계입니다. 이 줄이 바꾸는 software 상태를 대응하는 controller register나 trace checkpoint와 연결해 확인합니다.

L2621 {

제어 블록의 범위를 표시합니다. 이 괄호 안에서만 유효한 lock, CPU context, resource lifetime을 함께 표시해 둡니다.

L2622 struct pci_dev *dev;

Vendor ID를 읽어 pci_dev를 만들고 중복 scan을 방지하는 경계의 한 단계입니다. 이 줄이 바꾸는 software 상태를 대응하는 controller register나 trace checkpoint와 연결해 확인합니다.

L2623 u32 l;

Vendor ID를 읽어 pci_dev를 만들고 중복 scan을 방지하는 경계의 한 단계입니다. 이 줄이 바꾸는 software 상태를 대응하는 controller register나 trace checkpoint와 연결해 확인합니다.

L2624 (blank)

논리 구간을 나누는 빈 줄입니다. 바로 위에서 준비한 상태와 다음 제어 흐름을 분리해 읽습니다.

L2625 /*

원본 주석입니다. Vendor ID를 읽어 pci_dev를 만들고 중복 scan을 방지하는 경계에서 뒤따르는 코드가 전제하는 하드웨어 조건이나 예외를 먼저 확인합니다.

L2626 * Create pwrctrl device (if required) for the PCI device to handle the

원본 주석입니다. Vendor ID를 읽어 pci_dev를 만들고 중복 scan을 방지하는 경계에서 뒤따르는 코드가 전제하는 하드웨어 조건이나 예외를 먼저 확인합니다.

L2627 * power state. If the pwrctrl device is created, then skip scanning

원본 주석입니다. Vendor ID를 읽어 pci_dev를 만들고 중복 scan을 방지하는 경계에서 뒤따르는 코드가 전제하는 하드웨어 조건이나 예외를 먼저 확인합니다.

L2628 * further as the pwrctrl core will rescan the bus after powering on

원본 주석입니다. Vendor ID를 읽어 pci_dev를 만들고 중복 scan을 방지하는 경계에서 뒤따르는 코드가 전제하는 하드웨어 조건이나 예외를 먼저 확인합니다.

L2629 * the device.

원본 주석입니다. Vendor ID를 읽어 pci_dev를 만들고 중복 scan을 방지하는 경계에서 뒤따르는 코드가 전제하는 하드웨어 조건이나 예외를 먼저 확인합니다.

L2630 */

원본 주석입니다. Vendor ID를 읽어 pci_dev를 만들고 중복 scan을 방지하는 경계에서 뒤따르는 코드가 전제하는 하드웨어 조건이나 예외를 먼저 확인합니다.

L2631 if (pci_pwrctrl_create_device(bus, devfn))

상태나 ID를 분류하는 분기입니다. 이 조건의 참·거짓은 장치 register, firmware 기술 또는 CPU mode 중 하나에서 결정됩니다.

L2632 return NULL;

호출자에게 상태를 반환합니다. 반환값만 보지 말고 그 전에 controller ownership이나 active 상태가 정리되었는지 확인합니다.

L2633 (blank)

논리 구간을 나누는 빈 줄입니다. 바로 위에서 준비한 상태와 다음 제어 흐름을 분리해 읽습니다.

L2634 if (!pci_bus_read_dev_vendor_id(bus, devfn, &l, 60*1000))

상태나 ID를 분류하는 분기입니다. 이 조건의 참·거짓은 장치 register, firmware 기술 또는 CPU mode 중 하나에서 결정됩니다.

L2635 return NULL;

호출자에게 상태를 반환합니다. 반환값만 보지 말고 그 전에 controller ownership이나 active 상태가 정리되었는지 확인합니다.

L2636 (blank)

논리 구간을 나누는 빈 줄입니다. 바로 위에서 준비한 상태와 다음 제어 흐름을 분리해 읽습니다.

L2637 dev = pci_alloc_dev(bus);

Vendor ID를 읽어 pci_dev를 만들고 중복 scan을 방지하는 경계의 한 단계입니다. 이 줄이 바꾸는 software 상태를 대응하는 controller register나 trace checkpoint와 연결해 확인합니다.

L2638 if (!dev)

상태나 ID를 분류하는 분기입니다. 이 조건의 참·거짓은 장치 register, firmware 기술 또는 CPU mode 중 하나에서 결정됩니다.

L2639 return NULL;

호출자에게 상태를 반환합니다. 반환값만 보지 말고 그 전에 controller ownership이나 active 상태가 정리되었는지 확인합니다.

L2640 (blank)

논리 구간을 나누는 빈 줄입니다. 바로 위에서 준비한 상태와 다음 제어 흐름을 분리해 읽습니다.

L2641 dev->devfn = devfn;

Vendor ID를 읽어 pci_dev를 만들고 중복 scan을 방지하는 경계의 한 단계입니다. 이 줄이 바꾸는 software 상태를 대응하는 controller register나 trace checkpoint와 연결해 확인합니다.

L2642 dev->vendor = l & 0xffff;

Vendor ID를 읽어 pci_dev를 만들고 중복 scan을 방지하는 경계의 한 단계입니다. 이 줄이 바꾸는 software 상태를 대응하는 controller register나 trace checkpoint와 연결해 확인합니다.

L2643 dev->device = (l >> 16) & 0xffff;

Vendor ID를 읽어 pci_dev를 만들고 중복 scan을 방지하는 경계의 한 단계입니다. 이 줄이 바꾸는 software 상태를 대응하는 controller register나 trace checkpoint와 연결해 확인합니다.

L2644 (blank)

논리 구간을 나누는 빈 줄입니다. 바로 위에서 준비한 상태와 다음 제어 흐름을 분리해 읽습니다.

L2645 if (pci_setup_device(dev)) {

상태나 ID를 분류하는 분기입니다. 이 조건의 참·거짓은 장치 register, firmware 기술 또는 CPU mode 중 하나에서 결정됩니다.

L2646 pci_bus_put(dev->bus);

Vendor ID를 읽어 pci_dev를 만들고 중복 scan을 방지하는 경계의 한 단계입니다. 이 줄이 바꾸는 software 상태를 대응하는 controller register나 trace checkpoint와 연결해 확인합니다.

L2647 kfree(dev);

Vendor ID를 읽어 pci_dev를 만들고 중복 scan을 방지하는 경계의 한 단계입니다. 이 줄이 바꾸는 software 상태를 대응하는 controller register나 trace checkpoint와 연결해 확인합니다.

L2648 return NULL;

호출자에게 상태를 반환합니다. 반환값만 보지 말고 그 전에 controller ownership이나 active 상태가 정리되었는지 확인합니다.

L2649 }

제어 블록의 범위를 표시합니다. 이 괄호 안에서만 유효한 lock, CPU context, resource lifetime을 함께 표시해 둡니다.

L2650 (blank)

논리 구간을 나누는 빈 줄입니다. 바로 위에서 준비한 상태와 다음 제어 흐름을 분리해 읽습니다.

L2651 return dev;

호출자에게 상태를 반환합니다. 반환값만 보지 말고 그 전에 controller ownership이나 active 상태가 정리되었는지 확인합니다.

L2652 }

제어 블록의 범위를 표시합니다. 이 괄호 안에서만 유효한 lock, CPU context, resource lifetime을 함께 표시해 둡니다.

L2653 (blank)

논리 구간을 나누는 빈 줄입니다. 바로 위에서 준비한 상태와 다음 제어 흐름을 분리해 읽습니다.

L2654 void pcie_report_downtraining(struct pci_dev *dev)

함수 경계입니다. 호출자가 넘긴 번호 공간과 현재 CPU/controller context가 Vendor ID를 읽어 pci_dev를 만들고 중복 scan을 방지하는 경계의 입력 계약을 만족하는지 확인합니다.

L2655 {

제어 블록의 범위를 표시합니다. 이 괄호 안에서만 유효한 lock, CPU context, resource lifetime을 함께 표시해 둡니다.

L2656 if (!pci_is_pcie(dev))

상태나 ID를 분류하는 분기입니다. 이 조건의 참·거짓은 장치 register, firmware 기술 또는 CPU mode 중 하나에서 결정됩니다.

L2657 return;

호출자에게 상태를 반환합니다. 반환값만 보지 말고 그 전에 controller ownership이나 active 상태가 정리되었는지 확인합니다.

L2658 (blank)

논리 구간을 나누는 빈 줄입니다. 바로 위에서 준비한 상태와 다음 제어 흐름을 분리해 읽습니다.

L2659 /* Look from the device up to avoid downstream ports with no devices */

원본 주석입니다. Vendor ID를 읽어 pci_dev를 만들고 중복 scan을 방지하는 경계에서 뒤따르는 코드가 전제하는 하드웨어 조건이나 예외를 먼저 확인합니다.

L2660 if ((pci_pcie_type(dev) != PCI_EXP_TYPE_ENDPOINT) &&

상태나 ID를 분류하는 분기입니다. 이 조건의 참·거짓은 장치 register, firmware 기술 또는 CPU mode 중 하나에서 결정됩니다.

L2661 (pci_pcie_type(dev) != PCI_EXP_TYPE_LEG_END) &&

Vendor ID를 읽어 pci_dev를 만들고 중복 scan을 방지하는 경계의 한 단계입니다. 이 줄이 바꾸는 software 상태를 대응하는 controller register나 trace checkpoint와 연결해 확인합니다.

L2662 (pci_pcie_type(dev) != PCI_EXP_TYPE_UPSTREAM))

Vendor ID를 읽어 pci_dev를 만들고 중복 scan을 방지하는 경계의 한 단계입니다. 이 줄이 바꾸는 software 상태를 대응하는 controller register나 trace checkpoint와 연결해 확인합니다.

L2663 return;

호출자에게 상태를 반환합니다. 반환값만 보지 말고 그 전에 controller ownership이나 active 상태가 정리되었는지 확인합니다.

L2664 (blank)

논리 구간을 나누는 빈 줄입니다. 바로 위에서 준비한 상태와 다음 제어 흐름을 분리해 읽습니다.

L2665 /* Multi-function PCIe devices share the same link/status */

원본 주석입니다. Vendor ID를 읽어 pci_dev를 만들고 중복 scan을 방지하는 경계에서 뒤따르는 코드가 전제하는 하드웨어 조건이나 예외를 먼저 확인합니다.

L2666 if (PCI_FUNC(dev->devfn) != 0 || dev->is_virtfn)

상태나 ID를 분류하는 분기입니다. 이 조건의 참·거짓은 장치 register, firmware 기술 또는 CPU mode 중 하나에서 결정됩니다.

L2667 return;

호출자에게 상태를 반환합니다. 반환값만 보지 말고 그 전에 controller ownership이나 active 상태가 정리되었는지 확인합니다.

L2668 (blank)

논리 구간을 나누는 빈 줄입니다. 바로 위에서 준비한 상태와 다음 제어 흐름을 분리해 읽습니다.

Linux v6.18.37 · 실제 원본 코드

Config header, class, BAR와 subsystem 정보를 pci_dev에 채우는 과정

drivers/pci/probe.c:1987-2091

1987/**
1988 * pci_setup_device - Fill in class and map information of a device
1989 * @dev: the device structure to fill
1990 *
1991 * Initialize the device structure with information about the device's
1992 * vendor,class,memory and IO-space addresses, IRQ lines etc.
1993 * Called at initialisation of the PCI subsystem and by CardBus services.
1994 * Returns 0 on success and negative if unknown type of device (not normal,
1995 * bridge or CardBus).
1996 */
1997int pci_setup_device(struct pci_dev *dev)
1998{
1999	u32 class;
2000	u16 cmd;
2001	u8 hdr_type;
2002	int err, pos = 0;
2003	struct pci_bus_region region;
2004	struct resource *res;
2005 
2006	hdr_type = pci_hdr_type(dev);
2007 
2008	dev->sysdata = dev->bus->sysdata;
2009	dev->dev.parent = dev->bus->bridge;
2010	dev->dev.bus = &pci_bus_type;
2011	dev->hdr_type = FIELD_GET(PCI_HEADER_TYPE_MASK, hdr_type);
2012	dev->multifunction = FIELD_GET(PCI_HEADER_TYPE_MFD, hdr_type);
2013	dev->error_state = pci_channel_io_normal;
2014	set_pcie_port_type(dev);
2015 
2016	err = pci_set_of_node(dev);
2017	if (err)
2018		return err;
2019	pci_set_acpi_fwnode(dev);
2020 
2021	pci_dev_assign_slot(dev);
2022 
2023	/*
2024	 * Assume 32-bit PCI; let 64-bit PCI cards (which are far rarer)
2025	 * set this higher, assuming the system even supports it.
2026	 */
2027	dev->dma_mask = 0xffffffff;
2028 
2029	dev_set_name(&dev->dev, "%04x:%02x:%02x.%d", pci_domain_nr(dev->bus),
2030		     dev->bus->number, PCI_SLOT(dev->devfn),
2031		     PCI_FUNC(dev->devfn));
2032 
2033	class = pci_class(dev);
2034 
2035	dev->revision = class & 0xff;
2036	dev->class = class >> 8;		    /* upper 3 bytes */
2037 
2038	if (pci_early_dump)
2039		early_dump_pci_device(dev);
2040 
2041	/* Need to have dev->class ready */
2042	dev->cfg_size = pci_cfg_space_size(dev);
2043 
2044	/* Need to have dev->cfg_size ready */
2045	set_pcie_thunderbolt(dev);
2046 
2047	set_pcie_untrusted(dev);
2048 
2049	if (pci_is_pcie(dev))
2050		dev->supported_speeds = pcie_get_supported_speeds(dev);
2051 
2052	/* "Unknown power state" */
2053	dev->current_state = PCI_UNKNOWN;
2054 
2055	/* Early fixups, before probing the BARs */
2056	pci_fixup_device(pci_fixup_early, dev);
2057 
2058	pci_set_removable(dev);
2059 
2060	pci_info(dev, "[%04x:%04x] type %02x class %#08x %s\n",
2061		 dev->vendor, dev->device, dev->hdr_type, dev->class,
2062		 pci_type_str(dev));
2063 
2064	/* Device class may be changed after fixup */
2065	class = dev->class >> 8;
2066 
2067	if (dev->non_compliant_bars && !dev->mmio_always_on) {
2068		pci_read_config_word(dev, PCI_COMMAND, &cmd);
2069		if (cmd & (PCI_COMMAND_IO | PCI_COMMAND_MEMORY)) {
2070			pci_info(dev, "device has non-compliant BARs; disabling IO/MEM decoding\n");
2071			cmd &= ~PCI_COMMAND_IO;
2072			cmd &= ~PCI_COMMAND_MEMORY;
2073			pci_write_config_word(dev, PCI_COMMAND, cmd);
2074		}
2075	}
2076 
2077	dev->broken_intx_masking = pci_intx_mask_broken(dev);
2078 
2079	switch (dev->hdr_type) {		    /* header type */
2080	case PCI_HEADER_TYPE_NORMAL:		    /* standard header */
2081		if (class == PCI_CLASS_BRIDGE_PCI)
2082			goto bad;
2083		pci_read_irq(dev);
2084		pci_read_bases(dev, PCI_STD_NUM_BARS, PCI_ROM_ADDRESS);
2085 
2086		pci_subsystem_ids(dev, &dev->subsystem_vendor, &dev->subsystem_device);
2087 
2088		/*
2089		 * Do the ugly legacy mode stuff here rather than broken chip
2090		 * quirk code. Legacy mode ATA controllers have fixed
2091		 * addresses. These are not always echoed in BAR0-3, and
L1987 /**

원본 주석입니다. Config header, class, BAR와 subsystem 정보를 pci_dev에 채우는 과정에서 뒤따르는 코드가 전제하는 하드웨어 조건이나 예외를 먼저 확인합니다.

L1988 * pci_setup_device - Fill in class and map information of a device

원본 주석입니다. Config header, class, BAR와 subsystem 정보를 pci_dev에 채우는 과정에서 뒤따르는 코드가 전제하는 하드웨어 조건이나 예외를 먼저 확인합니다.

L1989 * @dev: the device structure to fill

원본 주석입니다. Config header, class, BAR와 subsystem 정보를 pci_dev에 채우는 과정에서 뒤따르는 코드가 전제하는 하드웨어 조건이나 예외를 먼저 확인합니다.

L1990 *

원본 주석입니다. Config header, class, BAR와 subsystem 정보를 pci_dev에 채우는 과정에서 뒤따르는 코드가 전제하는 하드웨어 조건이나 예외를 먼저 확인합니다.

L1991 * Initialize the device structure with information about the device's

원본 주석입니다. Config header, class, BAR와 subsystem 정보를 pci_dev에 채우는 과정에서 뒤따르는 코드가 전제하는 하드웨어 조건이나 예외를 먼저 확인합니다.

L1992 * vendor,class,memory and IO-space addresses, IRQ lines etc.

원본 주석입니다. Config header, class, BAR와 subsystem 정보를 pci_dev에 채우는 과정에서 뒤따르는 코드가 전제하는 하드웨어 조건이나 예외를 먼저 확인합니다.

L1993 * Called at initialisation of the PCI subsystem and by CardBus services.

원본 주석입니다. Config header, class, BAR와 subsystem 정보를 pci_dev에 채우는 과정에서 뒤따르는 코드가 전제하는 하드웨어 조건이나 예외를 먼저 확인합니다.

L1994 * Returns 0 on success and negative if unknown type of device (not normal,

원본 주석입니다. Config header, class, BAR와 subsystem 정보를 pci_dev에 채우는 과정에서 뒤따르는 코드가 전제하는 하드웨어 조건이나 예외를 먼저 확인합니다.

L1995 * bridge or CardBus).

원본 주석입니다. Config header, class, BAR와 subsystem 정보를 pci_dev에 채우는 과정에서 뒤따르는 코드가 전제하는 하드웨어 조건이나 예외를 먼저 확인합니다.

L1996 */

원본 주석입니다. Config header, class, BAR와 subsystem 정보를 pci_dev에 채우는 과정에서 뒤따르는 코드가 전제하는 하드웨어 조건이나 예외를 먼저 확인합니다.

L1997 int pci_setup_device(struct pci_dev *dev)

함수 경계입니다. 호출자가 넘긴 번호 공간과 현재 CPU/controller context가 Config header, class, BAR와 subsystem 정보를 pci_dev에 채우는 과정의 입력 계약을 만족하는지 확인합니다.

L1998 {

제어 블록의 범위를 표시합니다. 이 괄호 안에서만 유효한 lock, CPU context, resource lifetime을 함께 표시해 둡니다.

L1999 u32 class;

Config header, class, BAR와 subsystem 정보를 pci_dev에 채우는 과정의 한 단계입니다. 이 줄이 바꾸는 software 상태를 대응하는 controller register나 trace checkpoint와 연결해 확인합니다.

L2000 u16 cmd;

Config header, class, BAR와 subsystem 정보를 pci_dev에 채우는 과정의 한 단계입니다. 이 줄이 바꾸는 software 상태를 대응하는 controller register나 trace checkpoint와 연결해 확인합니다.

L2001 u8 hdr_type;

Config header, class, BAR와 subsystem 정보를 pci_dev에 채우는 과정의 한 단계입니다. 이 줄이 바꾸는 software 상태를 대응하는 controller register나 trace checkpoint와 연결해 확인합니다.

L2002 int err, pos = 0;

Config header, class, BAR와 subsystem 정보를 pci_dev에 채우는 과정의 한 단계입니다. 이 줄이 바꾸는 software 상태를 대응하는 controller register나 trace checkpoint와 연결해 확인합니다.

L2003 struct pci_bus_region region;

Config header, class, BAR와 subsystem 정보를 pci_dev에 채우는 과정의 한 단계입니다. 이 줄이 바꾸는 software 상태를 대응하는 controller register나 trace checkpoint와 연결해 확인합니다.

L2004 struct resource *res;

Config header, class, BAR와 subsystem 정보를 pci_dev에 채우는 과정의 한 단계입니다. 이 줄이 바꾸는 software 상태를 대응하는 controller register나 trace checkpoint와 연결해 확인합니다.

L2005 (blank)

논리 구간을 나누는 빈 줄입니다. 바로 위에서 준비한 상태와 다음 제어 흐름을 분리해 읽습니다.

L2006 hdr_type = pci_hdr_type(dev);

Config header, class, BAR와 subsystem 정보를 pci_dev에 채우는 과정의 한 단계입니다. 이 줄이 바꾸는 software 상태를 대응하는 controller register나 trace checkpoint와 연결해 확인합니다.

L2007 (blank)

논리 구간을 나누는 빈 줄입니다. 바로 위에서 준비한 상태와 다음 제어 흐름을 분리해 읽습니다.

L2008 dev->sysdata = dev->bus->sysdata;

Config header, class, BAR와 subsystem 정보를 pci_dev에 채우는 과정의 한 단계입니다. 이 줄이 바꾸는 software 상태를 대응하는 controller register나 trace checkpoint와 연결해 확인합니다.

L2009 dev->dev.parent = dev->bus->bridge;

Config header, class, BAR와 subsystem 정보를 pci_dev에 채우는 과정의 한 단계입니다. 이 줄이 바꾸는 software 상태를 대응하는 controller register나 trace checkpoint와 연결해 확인합니다.

L2010 dev->dev.bus = &pci_bus_type;

Config header, class, BAR와 subsystem 정보를 pci_dev에 채우는 과정의 한 단계입니다. 이 줄이 바꾸는 software 상태를 대응하는 controller register나 trace checkpoint와 연결해 확인합니다.

L2011 dev->hdr_type = FIELD_GET(PCI_HEADER_TYPE_MASK, hdr_type);

Config header, class, BAR와 subsystem 정보를 pci_dev에 채우는 과정의 한 단계입니다. 이 줄이 바꾸는 software 상태를 대응하는 controller register나 trace checkpoint와 연결해 확인합니다.

L2012 dev->multifunction = FIELD_GET(PCI_HEADER_TYPE_MFD, hdr_type);

Config header, class, BAR와 subsystem 정보를 pci_dev에 채우는 과정의 한 단계입니다. 이 줄이 바꾸는 software 상태를 대응하는 controller register나 trace checkpoint와 연결해 확인합니다.

L2013 dev->error_state = pci_channel_io_normal;

Config header, class, BAR와 subsystem 정보를 pci_dev에 채우는 과정의 한 단계입니다. 이 줄이 바꾸는 software 상태를 대응하는 controller register나 trace checkpoint와 연결해 확인합니다.

L2014 set_pcie_port_type(dev);

Config header, class, BAR와 subsystem 정보를 pci_dev에 채우는 과정의 한 단계입니다. 이 줄이 바꾸는 software 상태를 대응하는 controller register나 trace checkpoint와 연결해 확인합니다.

L2015 (blank)

논리 구간을 나누는 빈 줄입니다. 바로 위에서 준비한 상태와 다음 제어 흐름을 분리해 읽습니다.

L2016 err = pci_set_of_node(dev);

Config header, class, BAR와 subsystem 정보를 pci_dev에 채우는 과정의 한 단계입니다. 이 줄이 바꾸는 software 상태를 대응하는 controller register나 trace checkpoint와 연결해 확인합니다.

L2017 if (err)

상태나 ID를 분류하는 분기입니다. 이 조건의 참·거짓은 장치 register, firmware 기술 또는 CPU mode 중 하나에서 결정됩니다.

L2018 return err;

호출자에게 상태를 반환합니다. 반환값만 보지 말고 그 전에 controller ownership이나 active 상태가 정리되었는지 확인합니다.

L2019 pci_set_acpi_fwnode(dev);

Config header, class, BAR와 subsystem 정보를 pci_dev에 채우는 과정의 한 단계입니다. 이 줄이 바꾸는 software 상태를 대응하는 controller register나 trace checkpoint와 연결해 확인합니다.

L2020 (blank)

논리 구간을 나누는 빈 줄입니다. 바로 위에서 준비한 상태와 다음 제어 흐름을 분리해 읽습니다.

L2021 pci_dev_assign_slot(dev);

Config header, class, BAR와 subsystem 정보를 pci_dev에 채우는 과정의 한 단계입니다. 이 줄이 바꾸는 software 상태를 대응하는 controller register나 trace checkpoint와 연결해 확인합니다.

L2022 (blank)

논리 구간을 나누는 빈 줄입니다. 바로 위에서 준비한 상태와 다음 제어 흐름을 분리해 읽습니다.

L2023 /*

원본 주석입니다. Config header, class, BAR와 subsystem 정보를 pci_dev에 채우는 과정에서 뒤따르는 코드가 전제하는 하드웨어 조건이나 예외를 먼저 확인합니다.

L2024 * Assume 32-bit PCI; let 64-bit PCI cards (which are far rarer)

원본 주석입니다. Config header, class, BAR와 subsystem 정보를 pci_dev에 채우는 과정에서 뒤따르는 코드가 전제하는 하드웨어 조건이나 예외를 먼저 확인합니다.

L2025 * set this higher, assuming the system even supports it.

원본 주석입니다. Config header, class, BAR와 subsystem 정보를 pci_dev에 채우는 과정에서 뒤따르는 코드가 전제하는 하드웨어 조건이나 예외를 먼저 확인합니다.

L2026 */

원본 주석입니다. Config header, class, BAR와 subsystem 정보를 pci_dev에 채우는 과정에서 뒤따르는 코드가 전제하는 하드웨어 조건이나 예외를 먼저 확인합니다.

L2027 dev->dma_mask = 0xffffffff;

Config header, class, BAR와 subsystem 정보를 pci_dev에 채우는 과정의 한 단계입니다. 이 줄이 바꾸는 software 상태를 대응하는 controller register나 trace checkpoint와 연결해 확인합니다.

L2028 (blank)

논리 구간을 나누는 빈 줄입니다. 바로 위에서 준비한 상태와 다음 제어 흐름을 분리해 읽습니다.

L2029 dev_set_name(&dev->dev, "%04x:%02x:%02x.%d", pci_domain_nr(dev->bus),

Config header, class, BAR와 subsystem 정보를 pci_dev에 채우는 과정의 한 단계입니다. 이 줄이 바꾸는 software 상태를 대응하는 controller register나 trace checkpoint와 연결해 확인합니다.

L2030 dev->bus->number, PCI_SLOT(dev->devfn),

Config header, class, BAR와 subsystem 정보를 pci_dev에 채우는 과정의 한 단계입니다. 이 줄이 바꾸는 software 상태를 대응하는 controller register나 trace checkpoint와 연결해 확인합니다.

L2031 PCI_FUNC(dev->devfn));

Config header, class, BAR와 subsystem 정보를 pci_dev에 채우는 과정의 한 단계입니다. 이 줄이 바꾸는 software 상태를 대응하는 controller register나 trace checkpoint와 연결해 확인합니다.

L2032 (blank)

논리 구간을 나누는 빈 줄입니다. 바로 위에서 준비한 상태와 다음 제어 흐름을 분리해 읽습니다.

L2033 class = pci_class(dev);

Config header, class, BAR와 subsystem 정보를 pci_dev에 채우는 과정의 한 단계입니다. 이 줄이 바꾸는 software 상태를 대응하는 controller register나 trace checkpoint와 연결해 확인합니다.

L2034 (blank)

논리 구간을 나누는 빈 줄입니다. 바로 위에서 준비한 상태와 다음 제어 흐름을 분리해 읽습니다.

L2035 dev->revision = class & 0xff;

Config header, class, BAR와 subsystem 정보를 pci_dev에 채우는 과정의 한 단계입니다. 이 줄이 바꾸는 software 상태를 대응하는 controller register나 trace checkpoint와 연결해 확인합니다.

L2036 dev->class = class >> 8; /* upper 3 bytes */

Config header, class, BAR와 subsystem 정보를 pci_dev에 채우는 과정의 한 단계입니다. 이 줄이 바꾸는 software 상태를 대응하는 controller register나 trace checkpoint와 연결해 확인합니다.

L2037 (blank)

논리 구간을 나누는 빈 줄입니다. 바로 위에서 준비한 상태와 다음 제어 흐름을 분리해 읽습니다.

L2038 if (pci_early_dump)

상태나 ID를 분류하는 분기입니다. 이 조건의 참·거짓은 장치 register, firmware 기술 또는 CPU mode 중 하나에서 결정됩니다.

L2039 early_dump_pci_device(dev);

Config header, class, BAR와 subsystem 정보를 pci_dev에 채우는 과정의 한 단계입니다. 이 줄이 바꾸는 software 상태를 대응하는 controller register나 trace checkpoint와 연결해 확인합니다.

L2040 (blank)

논리 구간을 나누는 빈 줄입니다. 바로 위에서 준비한 상태와 다음 제어 흐름을 분리해 읽습니다.

L2041 /* Need to have dev->class ready */

원본 주석입니다. Config header, class, BAR와 subsystem 정보를 pci_dev에 채우는 과정에서 뒤따르는 코드가 전제하는 하드웨어 조건이나 예외를 먼저 확인합니다.

L2042 dev->cfg_size = pci_cfg_space_size(dev);

Config header, class, BAR와 subsystem 정보를 pci_dev에 채우는 과정의 한 단계입니다. 이 줄이 바꾸는 software 상태를 대응하는 controller register나 trace checkpoint와 연결해 확인합니다.

L2043 (blank)

논리 구간을 나누는 빈 줄입니다. 바로 위에서 준비한 상태와 다음 제어 흐름을 분리해 읽습니다.

L2044 /* Need to have dev->cfg_size ready */

원본 주석입니다. Config header, class, BAR와 subsystem 정보를 pci_dev에 채우는 과정에서 뒤따르는 코드가 전제하는 하드웨어 조건이나 예외를 먼저 확인합니다.

L2045 set_pcie_thunderbolt(dev);

Config header, class, BAR와 subsystem 정보를 pci_dev에 채우는 과정의 한 단계입니다. 이 줄이 바꾸는 software 상태를 대응하는 controller register나 trace checkpoint와 연결해 확인합니다.

L2046 (blank)

논리 구간을 나누는 빈 줄입니다. 바로 위에서 준비한 상태와 다음 제어 흐름을 분리해 읽습니다.

L2047 set_pcie_untrusted(dev);

Config header, class, BAR와 subsystem 정보를 pci_dev에 채우는 과정의 한 단계입니다. 이 줄이 바꾸는 software 상태를 대응하는 controller register나 trace checkpoint와 연결해 확인합니다.

L2048 (blank)

논리 구간을 나누는 빈 줄입니다. 바로 위에서 준비한 상태와 다음 제어 흐름을 분리해 읽습니다.

L2049 if (pci_is_pcie(dev))

상태나 ID를 분류하는 분기입니다. 이 조건의 참·거짓은 장치 register, firmware 기술 또는 CPU mode 중 하나에서 결정됩니다.

L2050 dev->supported_speeds = pcie_get_supported_speeds(dev);

Config header, class, BAR와 subsystem 정보를 pci_dev에 채우는 과정의 한 단계입니다. 이 줄이 바꾸는 software 상태를 대응하는 controller register나 trace checkpoint와 연결해 확인합니다.

L2051 (blank)

논리 구간을 나누는 빈 줄입니다. 바로 위에서 준비한 상태와 다음 제어 흐름을 분리해 읽습니다.

L2052 /* "Unknown power state" */

원본 주석입니다. Config header, class, BAR와 subsystem 정보를 pci_dev에 채우는 과정에서 뒤따르는 코드가 전제하는 하드웨어 조건이나 예외를 먼저 확인합니다.

L2053 dev->current_state = PCI_UNKNOWN;

Config header, class, BAR와 subsystem 정보를 pci_dev에 채우는 과정의 한 단계입니다. 이 줄이 바꾸는 software 상태를 대응하는 controller register나 trace checkpoint와 연결해 확인합니다.

L2054 (blank)

논리 구간을 나누는 빈 줄입니다. 바로 위에서 준비한 상태와 다음 제어 흐름을 분리해 읽습니다.

L2055 /* Early fixups, before probing the BARs */

원본 주석입니다. Config header, class, BAR와 subsystem 정보를 pci_dev에 채우는 과정에서 뒤따르는 코드가 전제하는 하드웨어 조건이나 예외를 먼저 확인합니다.

L2056 pci_fixup_device(pci_fixup_early, dev);

Config header, class, BAR와 subsystem 정보를 pci_dev에 채우는 과정의 한 단계입니다. 이 줄이 바꾸는 software 상태를 대응하는 controller register나 trace checkpoint와 연결해 확인합니다.

L2057 (blank)

논리 구간을 나누는 빈 줄입니다. 바로 위에서 준비한 상태와 다음 제어 흐름을 분리해 읽습니다.

L2058 pci_set_removable(dev);

Config header, class, BAR와 subsystem 정보를 pci_dev에 채우는 과정의 한 단계입니다. 이 줄이 바꾸는 software 상태를 대응하는 controller register나 trace checkpoint와 연결해 확인합니다.

L2059 (blank)

논리 구간을 나누는 빈 줄입니다. 바로 위에서 준비한 상태와 다음 제어 흐름을 분리해 읽습니다.

L2060 pci_info(dev, "[%04x:%04x] type %02x class %#08x %s\n",

Config header, class, BAR와 subsystem 정보를 pci_dev에 채우는 과정의 한 단계입니다. 이 줄이 바꾸는 software 상태를 대응하는 controller register나 trace checkpoint와 연결해 확인합니다.

L2061 dev->vendor, dev->device, dev->hdr_type, dev->class,

Config header, class, BAR와 subsystem 정보를 pci_dev에 채우는 과정의 한 단계입니다. 이 줄이 바꾸는 software 상태를 대응하는 controller register나 trace checkpoint와 연결해 확인합니다.

L2062 pci_type_str(dev));

Config header, class, BAR와 subsystem 정보를 pci_dev에 채우는 과정의 한 단계입니다. 이 줄이 바꾸는 software 상태를 대응하는 controller register나 trace checkpoint와 연결해 확인합니다.

L2063 (blank)

논리 구간을 나누는 빈 줄입니다. 바로 위에서 준비한 상태와 다음 제어 흐름을 분리해 읽습니다.

L2064 /* Device class may be changed after fixup */

원본 주석입니다. Config header, class, BAR와 subsystem 정보를 pci_dev에 채우는 과정에서 뒤따르는 코드가 전제하는 하드웨어 조건이나 예외를 먼저 확인합니다.

L2065 class = dev->class >> 8;

Config header, class, BAR와 subsystem 정보를 pci_dev에 채우는 과정의 한 단계입니다. 이 줄이 바꾸는 software 상태를 대응하는 controller register나 trace checkpoint와 연결해 확인합니다.

L2066 (blank)

논리 구간을 나누는 빈 줄입니다. 바로 위에서 준비한 상태와 다음 제어 흐름을 분리해 읽습니다.

L2067 if (dev->non_compliant_bars && !dev->mmio_always_on) {

상태나 ID를 분류하는 분기입니다. 이 조건의 참·거짓은 장치 register, firmware 기술 또는 CPU mode 중 하나에서 결정됩니다.

L2068 pci_read_config_word(dev, PCI_COMMAND, &cmd);

공유 상태나 MMIO를 읽습니다. 이 값은 단순 변수라기보다 hardware가 갱신한 시점과 read ordering을 포함해 해석해야 합니다.

L2069 if (cmd & (PCI_COMMAND_IO | PCI_COMMAND_MEMORY)) {

상태나 ID를 분류하는 분기입니다. 이 조건의 참·거짓은 장치 register, firmware 기술 또는 CPU mode 중 하나에서 결정됩니다.

L2070 pci_info(dev, "device has non-compliant BARs; disabling IO/MEM decoding\n");

Config header, class, BAR와 subsystem 정보를 pci_dev에 채우는 과정의 한 단계입니다. 이 줄이 바꾸는 software 상태를 대응하는 controller register나 trace checkpoint와 연결해 확인합니다.

L2071 cmd &= ~PCI_COMMAND_IO;

Config header, class, BAR와 subsystem 정보를 pci_dev에 채우는 과정의 한 단계입니다. 이 줄이 바꾸는 software 상태를 대응하는 controller register나 trace checkpoint와 연결해 확인합니다.

L2072 cmd &= ~PCI_COMMAND_MEMORY;

Config header, class, BAR와 subsystem 정보를 pci_dev에 채우는 과정의 한 단계입니다. 이 줄이 바꾸는 software 상태를 대응하는 controller register나 trace checkpoint와 연결해 확인합니다.

L2073 pci_write_config_word(dev, PCI_COMMAND, cmd);

hardware 또는 공유 메모리에 상태를 게시합니다. write가 완료되는 지점과 다음 read/doorbell 사이의 ordering 조건을 확인합니다.

L2074 }

제어 블록의 범위를 표시합니다. 이 괄호 안에서만 유효한 lock, CPU context, resource lifetime을 함께 표시해 둡니다.

L2075 }

제어 블록의 범위를 표시합니다. 이 괄호 안에서만 유효한 lock, CPU context, resource lifetime을 함께 표시해 둡니다.

L2076 (blank)

논리 구간을 나누는 빈 줄입니다. 바로 위에서 준비한 상태와 다음 제어 흐름을 분리해 읽습니다.

L2077 dev->broken_intx_masking = pci_intx_mask_broken(dev);

Config header, class, BAR와 subsystem 정보를 pci_dev에 채우는 과정의 한 단계입니다. 이 줄이 바꾸는 software 상태를 대응하는 controller register나 trace checkpoint와 연결해 확인합니다.

L2078 (blank)

논리 구간을 나누는 빈 줄입니다. 바로 위에서 준비한 상태와 다음 제어 흐름을 분리해 읽습니다.

L2079 switch (dev->hdr_type) { /* header type */

상태나 ID를 분류하는 분기입니다. 이 조건의 참·거짓은 장치 register, firmware 기술 또는 CPU mode 중 하나에서 결정됩니다.

L2080 case PCI_HEADER_TYPE_NORMAL: /* standard header */

Config header, class, BAR와 subsystem 정보를 pci_dev에 채우는 과정의 한 단계입니다. 이 줄이 바꾸는 software 상태를 대응하는 controller register나 trace checkpoint와 연결해 확인합니다.

L2081 if (class == PCI_CLASS_BRIDGE_PCI)

상태나 ID를 분류하는 분기입니다. 이 조건의 참·거짓은 장치 register, firmware 기술 또는 CPU mode 중 하나에서 결정됩니다.

L2082 goto bad;

공통 정리 또는 오류 경로로 이동합니다. 이미 얻은 clock, mapping, IRQ, DMA 자원의 역순 해제가 맞는지 확인합니다.

L2083 pci_read_irq(dev);

공유 상태나 MMIO를 읽습니다. 이 값은 단순 변수라기보다 hardware가 갱신한 시점과 read ordering을 포함해 해석해야 합니다.

L2084 pci_read_bases(dev, PCI_STD_NUM_BARS, PCI_ROM_ADDRESS);

공유 상태나 MMIO를 읽습니다. 이 값은 단순 변수라기보다 hardware가 갱신한 시점과 read ordering을 포함해 해석해야 합니다.

L2085 (blank)

논리 구간을 나누는 빈 줄입니다. 바로 위에서 준비한 상태와 다음 제어 흐름을 분리해 읽습니다.

L2086 pci_subsystem_ids(dev, &dev->subsystem_vendor, &dev->subsystem_device);

Config header, class, BAR와 subsystem 정보를 pci_dev에 채우는 과정의 한 단계입니다. 이 줄이 바꾸는 software 상태를 대응하는 controller register나 trace checkpoint와 연결해 확인합니다.

L2087 (blank)

논리 구간을 나누는 빈 줄입니다. 바로 위에서 준비한 상태와 다음 제어 흐름을 분리해 읽습니다.

L2088 /*

원본 주석입니다. Config header, class, BAR와 subsystem 정보를 pci_dev에 채우는 과정에서 뒤따르는 코드가 전제하는 하드웨어 조건이나 예외를 먼저 확인합니다.

L2089 * Do the ugly legacy mode stuff here rather than broken chip

원본 주석입니다. Config header, class, BAR와 subsystem 정보를 pci_dev에 채우는 과정에서 뒤따르는 코드가 전제하는 하드웨어 조건이나 예외를 먼저 확인합니다.

L2090 * quirk code. Legacy mode ATA controllers have fixed

원본 주석입니다. Config header, class, BAR와 subsystem 정보를 pci_dev에 채우는 과정에서 뒤따르는 코드가 전제하는 하드웨어 조건이나 예외를 먼저 확인합니다.

L2091 * addresses. These are not always echoed in BAR0-3, and

원본 주석입니다. Config header, class, BAR와 subsystem 정보를 pci_dev에 채우는 과정에서 뒤따르는 코드가 전제하는 하드웨어 조건이나 예외를 먼저 확인합니다.

Linux v6.18.37 · 실제 원본 코드

BAR all-ones sizing, type, 64-bit pair와 resource flag 해석

drivers/pci/probe.c:335-439

335	res->flags = 0;
336out:
337	if (res->flags)
338		pci_info(dev, "%s %pR\n", res_name, res);
339 
340	return (res->flags & IORESOURCE_MEM_64) ? 1 : 0;
341}
342 
343static __always_inline void pci_read_bases(struct pci_dev *dev,
344					   unsigned int howmany, int rom)
345{
346	u32 rombar, stdbars[PCI_STD_NUM_BARS];
347	unsigned int pos, reg;
348	u16 orig_cmd;
349 
350	BUILD_BUG_ON(statically_true(howmany > PCI_STD_NUM_BARS));
351 
352	if (dev->non_compliant_bars)
353		return;
354 
355	/* Per PCIe r4.0, sec 9.3.4.1.11, the VF BARs are all RO Zero */
356	if (dev->is_virtfn)
357		return;
358 
359	/* No printks while decoding is disabled! */
360	if (!dev->mmio_always_on) {
361		pci_read_config_word(dev, PCI_COMMAND, &orig_cmd);
362		if (orig_cmd & PCI_COMMAND_DECODE_ENABLE) {
363			pci_write_config_word(dev, PCI_COMMAND,
364				orig_cmd & ~PCI_COMMAND_DECODE_ENABLE);
365		}
366	}
367 
368	__pci_size_stdbars(dev, howmany, PCI_BASE_ADDRESS_0, stdbars);
369	if (rom)
370		__pci_size_rom(dev, rom, &rombar);
371 
372	if (!dev->mmio_always_on &&
373	    (orig_cmd & PCI_COMMAND_DECODE_ENABLE))
374		pci_write_config_word(dev, PCI_COMMAND, orig_cmd);
375 
376	for (pos = 0; pos < howmany; pos++) {
377		struct resource *res = &dev->resource[pos];
378		reg = PCI_BASE_ADDRESS_0 + (pos << 2);
379		pos += __pci_read_base(dev, pci_bar_unknown,
380				       res, reg, &stdbars[pos]);
381	}
382 
383	if (rom) {
384		struct resource *res = &dev->resource[PCI_ROM_RESOURCE];
385		dev->rom_base_reg = rom;
386		res->flags = IORESOURCE_MEM | IORESOURCE_PREFETCH |
387				IORESOURCE_READONLY | IORESOURCE_SIZEALIGN;
388		__pci_read_base(dev, pci_bar_mem32, res, rom, &rombar);
389	}
390}
391 
392static void pci_read_bridge_io(struct pci_dev *dev, struct resource *res,
393			       bool log)
394{
395	u8 io_base_lo, io_limit_lo;
396	unsigned long io_mask, io_granularity, base, limit;
397	struct pci_bus_region region;
398 
399	io_mask = PCI_IO_RANGE_MASK;
400	io_granularity = 0x1000;
401	if (dev->io_window_1k) {
402		/* Support 1K I/O space granularity */
403		io_mask = PCI_IO_1K_RANGE_MASK;
404		io_granularity = 0x400;
405	}
406 
407	pci_read_config_byte(dev, PCI_IO_BASE, &io_base_lo);
408	pci_read_config_byte(dev, PCI_IO_LIMIT, &io_limit_lo);
409	base = (io_base_lo & io_mask) << 8;
410	limit = (io_limit_lo & io_mask) << 8;
411 
412	if ((io_base_lo & PCI_IO_RANGE_TYPE_MASK) == PCI_IO_RANGE_TYPE_32) {
413		u16 io_base_hi, io_limit_hi;
414 
415		pci_read_config_word(dev, PCI_IO_BASE_UPPER16, &io_base_hi);
416		pci_read_config_word(dev, PCI_IO_LIMIT_UPPER16, &io_limit_hi);
417		base |= ((unsigned long) io_base_hi << 16);
418		limit |= ((unsigned long) io_limit_hi << 16);
419	}
420 
421	res->flags = (io_base_lo & PCI_IO_RANGE_TYPE_MASK) | IORESOURCE_IO;
422 
423	if (base <= limit) {
424		region.start = base;
425		region.end = limit + io_granularity - 1;
426		pcibios_bus_to_resource(dev->bus, res, &region);
427		if (log)
428			pci_info(dev, "  bridge window %pR\n", res);
429	} else {
430		resource_set_range(res, 0, 0);
431		res->flags |= IORESOURCE_UNSET | IORESOURCE_DISABLED;
432	}
433}
434 
435static void pci_read_bridge_mmio(struct pci_dev *dev, struct resource *res,
436				 bool log)
437{
438	u16 mem_base_lo, mem_limit_lo;
439	unsigned long base, limit;
L335 res->flags = 0;

BAR all-ones sizing, type, 64-bit pair와 resource flag 해석의 한 단계입니다. 이 줄이 바꾸는 software 상태를 대응하는 controller register나 trace checkpoint와 연결해 확인합니다.

L336 out:

BAR all-ones sizing, type, 64-bit pair와 resource flag 해석의 한 단계입니다. 이 줄이 바꾸는 software 상태를 대응하는 controller register나 trace checkpoint와 연결해 확인합니다.

L337 if (res->flags)

상태나 ID를 분류하는 분기입니다. 이 조건의 참·거짓은 장치 register, firmware 기술 또는 CPU mode 중 하나에서 결정됩니다.

L338 pci_info(dev, "%s %pR\n", res_name, res);

BAR all-ones sizing, type, 64-bit pair와 resource flag 해석의 한 단계입니다. 이 줄이 바꾸는 software 상태를 대응하는 controller register나 trace checkpoint와 연결해 확인합니다.

L339 (blank)

논리 구간을 나누는 빈 줄입니다. 바로 위에서 준비한 상태와 다음 제어 흐름을 분리해 읽습니다.

L340 return (res->flags & IORESOURCE_MEM_64) ? 1 : 0;

호출자에게 상태를 반환합니다. 반환값만 보지 말고 그 전에 controller ownership이나 active 상태가 정리되었는지 확인합니다.

L341 }

제어 블록의 범위를 표시합니다. 이 괄호 안에서만 유효한 lock, CPU context, resource lifetime을 함께 표시해 둡니다.

L342 (blank)

논리 구간을 나누는 빈 줄입니다. 바로 위에서 준비한 상태와 다음 제어 흐름을 분리해 읽습니다.

L343 static __always_inline void pci_read_bases(struct pci_dev *dev,

공유 상태나 MMIO를 읽습니다. 이 값은 단순 변수라기보다 hardware가 갱신한 시점과 read ordering을 포함해 해석해야 합니다.

L344 unsigned int howmany, int rom)

BAR all-ones sizing, type, 64-bit pair와 resource flag 해석의 한 단계입니다. 이 줄이 바꾸는 software 상태를 대응하는 controller register나 trace checkpoint와 연결해 확인합니다.

L345 {

제어 블록의 범위를 표시합니다. 이 괄호 안에서만 유효한 lock, CPU context, resource lifetime을 함께 표시해 둡니다.

L346 u32 rombar, stdbars[PCI_STD_NUM_BARS];

BAR all-ones sizing, type, 64-bit pair와 resource flag 해석의 한 단계입니다. 이 줄이 바꾸는 software 상태를 대응하는 controller register나 trace checkpoint와 연결해 확인합니다.

L347 unsigned int pos, reg;

BAR all-ones sizing, type, 64-bit pair와 resource flag 해석의 한 단계입니다. 이 줄이 바꾸는 software 상태를 대응하는 controller register나 trace checkpoint와 연결해 확인합니다.

L348 u16 orig_cmd;

BAR all-ones sizing, type, 64-bit pair와 resource flag 해석의 한 단계입니다. 이 줄이 바꾸는 software 상태를 대응하는 controller register나 trace checkpoint와 연결해 확인합니다.

L349 (blank)

논리 구간을 나누는 빈 줄입니다. 바로 위에서 준비한 상태와 다음 제어 흐름을 분리해 읽습니다.

L350 BUILD_BUG_ON(statically_true(howmany > PCI_STD_NUM_BARS));

BAR all-ones sizing, type, 64-bit pair와 resource flag 해석의 한 단계입니다. 이 줄이 바꾸는 software 상태를 대응하는 controller register나 trace checkpoint와 연결해 확인합니다.

L351 (blank)

논리 구간을 나누는 빈 줄입니다. 바로 위에서 준비한 상태와 다음 제어 흐름을 분리해 읽습니다.

L352 if (dev->non_compliant_bars)

상태나 ID를 분류하는 분기입니다. 이 조건의 참·거짓은 장치 register, firmware 기술 또는 CPU mode 중 하나에서 결정됩니다.

L353 return;

호출자에게 상태를 반환합니다. 반환값만 보지 말고 그 전에 controller ownership이나 active 상태가 정리되었는지 확인합니다.

L354 (blank)

논리 구간을 나누는 빈 줄입니다. 바로 위에서 준비한 상태와 다음 제어 흐름을 분리해 읽습니다.

L355 /* Per PCIe r4.0, sec 9.3.4.1.11, the VF BARs are all RO Zero */

원본 주석입니다. BAR all-ones sizing, type, 64-bit pair와 resource flag 해석에서 뒤따르는 코드가 전제하는 하드웨어 조건이나 예외를 먼저 확인합니다.

L356 if (dev->is_virtfn)

상태나 ID를 분류하는 분기입니다. 이 조건의 참·거짓은 장치 register, firmware 기술 또는 CPU mode 중 하나에서 결정됩니다.

L357 return;

호출자에게 상태를 반환합니다. 반환값만 보지 말고 그 전에 controller ownership이나 active 상태가 정리되었는지 확인합니다.

L358 (blank)

논리 구간을 나누는 빈 줄입니다. 바로 위에서 준비한 상태와 다음 제어 흐름을 분리해 읽습니다.

L359 /* No printks while decoding is disabled! */

원본 주석입니다. BAR all-ones sizing, type, 64-bit pair와 resource flag 해석에서 뒤따르는 코드가 전제하는 하드웨어 조건이나 예외를 먼저 확인합니다.

L360 if (!dev->mmio_always_on) {

상태나 ID를 분류하는 분기입니다. 이 조건의 참·거짓은 장치 register, firmware 기술 또는 CPU mode 중 하나에서 결정됩니다.

L361 pci_read_config_word(dev, PCI_COMMAND, &orig_cmd);

공유 상태나 MMIO를 읽습니다. 이 값은 단순 변수라기보다 hardware가 갱신한 시점과 read ordering을 포함해 해석해야 합니다.

L362 if (orig_cmd & PCI_COMMAND_DECODE_ENABLE) {

상태나 ID를 분류하는 분기입니다. 이 조건의 참·거짓은 장치 register, firmware 기술 또는 CPU mode 중 하나에서 결정됩니다.

L363 pci_write_config_word(dev, PCI_COMMAND,

hardware 또는 공유 메모리에 상태를 게시합니다. write가 완료되는 지점과 다음 read/doorbell 사이의 ordering 조건을 확인합니다.

L364 orig_cmd & ~PCI_COMMAND_DECODE_ENABLE);

BAR all-ones sizing, type, 64-bit pair와 resource flag 해석의 한 단계입니다. 이 줄이 바꾸는 software 상태를 대응하는 controller register나 trace checkpoint와 연결해 확인합니다.

L365 }

제어 블록의 범위를 표시합니다. 이 괄호 안에서만 유효한 lock, CPU context, resource lifetime을 함께 표시해 둡니다.

L366 }

제어 블록의 범위를 표시합니다. 이 괄호 안에서만 유효한 lock, CPU context, resource lifetime을 함께 표시해 둡니다.

L367 (blank)

논리 구간을 나누는 빈 줄입니다. 바로 위에서 준비한 상태와 다음 제어 흐름을 분리해 읽습니다.

L368 __pci_size_stdbars(dev, howmany, PCI_BASE_ADDRESS_0, stdbars);

BAR all-ones sizing, type, 64-bit pair와 resource flag 해석의 한 단계입니다. 이 줄이 바꾸는 software 상태를 대응하는 controller register나 trace checkpoint와 연결해 확인합니다.

L369 if (rom)

상태나 ID를 분류하는 분기입니다. 이 조건의 참·거짓은 장치 register, firmware 기술 또는 CPU mode 중 하나에서 결정됩니다.

L370 __pci_size_rom(dev, rom, &rombar);

BAR all-ones sizing, type, 64-bit pair와 resource flag 해석의 한 단계입니다. 이 줄이 바꾸는 software 상태를 대응하는 controller register나 trace checkpoint와 연결해 확인합니다.

L371 (blank)

논리 구간을 나누는 빈 줄입니다. 바로 위에서 준비한 상태와 다음 제어 흐름을 분리해 읽습니다.

L372 if (!dev->mmio_always_on &&

상태나 ID를 분류하는 분기입니다. 이 조건의 참·거짓은 장치 register, firmware 기술 또는 CPU mode 중 하나에서 결정됩니다.

L373 (orig_cmd & PCI_COMMAND_DECODE_ENABLE))

BAR all-ones sizing, type, 64-bit pair와 resource flag 해석의 한 단계입니다. 이 줄이 바꾸는 software 상태를 대응하는 controller register나 trace checkpoint와 연결해 확인합니다.

L374 pci_write_config_word(dev, PCI_COMMAND, orig_cmd);

hardware 또는 공유 메모리에 상태를 게시합니다. write가 완료되는 지점과 다음 read/doorbell 사이의 ordering 조건을 확인합니다.

L375 (blank)

논리 구간을 나누는 빈 줄입니다. 바로 위에서 준비한 상태와 다음 제어 흐름을 분리해 읽습니다.

L376 for (pos = 0; pos < howmany; pos++) {

여러 pending source 또는 resource를 순회하는 구간입니다. 한 번의 진입에서 처리 가능한 양과 종료 조건이 interrupt latency에 영향을 줍니다.

L377 struct resource *res = &dev->resource[pos];

BAR all-ones sizing, type, 64-bit pair와 resource flag 해석의 한 단계입니다. 이 줄이 바꾸는 software 상태를 대응하는 controller register나 trace checkpoint와 연결해 확인합니다.

L378 reg = PCI_BASE_ADDRESS_0 + (pos << 2);

BAR all-ones sizing, type, 64-bit pair와 resource flag 해석의 한 단계입니다. 이 줄이 바꾸는 software 상태를 대응하는 controller register나 trace checkpoint와 연결해 확인합니다.

L379 pos += __pci_read_base(dev, pci_bar_unknown,

공유 상태나 MMIO를 읽습니다. 이 값은 단순 변수라기보다 hardware가 갱신한 시점과 read ordering을 포함해 해석해야 합니다.

L380 res, reg, &stdbars[pos]);

BAR all-ones sizing, type, 64-bit pair와 resource flag 해석의 한 단계입니다. 이 줄이 바꾸는 software 상태를 대응하는 controller register나 trace checkpoint와 연결해 확인합니다.

L381 }

제어 블록의 범위를 표시합니다. 이 괄호 안에서만 유효한 lock, CPU context, resource lifetime을 함께 표시해 둡니다.

L382 (blank)

논리 구간을 나누는 빈 줄입니다. 바로 위에서 준비한 상태와 다음 제어 흐름을 분리해 읽습니다.

L383 if (rom) {

상태나 ID를 분류하는 분기입니다. 이 조건의 참·거짓은 장치 register, firmware 기술 또는 CPU mode 중 하나에서 결정됩니다.

L384 struct resource *res = &dev->resource[PCI_ROM_RESOURCE];

BAR all-ones sizing, type, 64-bit pair와 resource flag 해석의 한 단계입니다. 이 줄이 바꾸는 software 상태를 대응하는 controller register나 trace checkpoint와 연결해 확인합니다.

L385 dev->rom_base_reg = rom;

BAR all-ones sizing, type, 64-bit pair와 resource flag 해석의 한 단계입니다. 이 줄이 바꾸는 software 상태를 대응하는 controller register나 trace checkpoint와 연결해 확인합니다.

L386 res->flags = IORESOURCE_MEM | IORESOURCE_PREFETCH |

BAR all-ones sizing, type, 64-bit pair와 resource flag 해석의 한 단계입니다. 이 줄이 바꾸는 software 상태를 대응하는 controller register나 trace checkpoint와 연결해 확인합니다.

L387 IORESOURCE_READONLY | IORESOURCE_SIZEALIGN;

BAR all-ones sizing, type, 64-bit pair와 resource flag 해석의 한 단계입니다. 이 줄이 바꾸는 software 상태를 대응하는 controller register나 trace checkpoint와 연결해 확인합니다.

L388 __pci_read_base(dev, pci_bar_mem32, res, rom, &rombar);

공유 상태나 MMIO를 읽습니다. 이 값은 단순 변수라기보다 hardware가 갱신한 시점과 read ordering을 포함해 해석해야 합니다.

L389 }

제어 블록의 범위를 표시합니다. 이 괄호 안에서만 유효한 lock, CPU context, resource lifetime을 함께 표시해 둡니다.

L390 }

제어 블록의 범위를 표시합니다. 이 괄호 안에서만 유효한 lock, CPU context, resource lifetime을 함께 표시해 둡니다.

L391 (blank)

논리 구간을 나누는 빈 줄입니다. 바로 위에서 준비한 상태와 다음 제어 흐름을 분리해 읽습니다.

L392 static void pci_read_bridge_io(struct pci_dev *dev, struct resource *res,

함수 경계입니다. 호출자가 넘긴 번호 공간과 현재 CPU/controller context가 BAR all-ones sizing, type, 64-bit pair와 resource flag 해석의 입력 계약을 만족하는지 확인합니다.

L393 bool log)

BAR all-ones sizing, type, 64-bit pair와 resource flag 해석의 한 단계입니다. 이 줄이 바꾸는 software 상태를 대응하는 controller register나 trace checkpoint와 연결해 확인합니다.

L394 {

제어 블록의 범위를 표시합니다. 이 괄호 안에서만 유효한 lock, CPU context, resource lifetime을 함께 표시해 둡니다.

L395 u8 io_base_lo, io_limit_lo;

BAR all-ones sizing, type, 64-bit pair와 resource flag 해석의 한 단계입니다. 이 줄이 바꾸는 software 상태를 대응하는 controller register나 trace checkpoint와 연결해 확인합니다.

L396 unsigned long io_mask, io_granularity, base, limit;

BAR all-ones sizing, type, 64-bit pair와 resource flag 해석의 한 단계입니다. 이 줄이 바꾸는 software 상태를 대응하는 controller register나 trace checkpoint와 연결해 확인합니다.

L397 struct pci_bus_region region;

BAR all-ones sizing, type, 64-bit pair와 resource flag 해석의 한 단계입니다. 이 줄이 바꾸는 software 상태를 대응하는 controller register나 trace checkpoint와 연결해 확인합니다.

L398 (blank)

논리 구간을 나누는 빈 줄입니다. 바로 위에서 준비한 상태와 다음 제어 흐름을 분리해 읽습니다.

L399 io_mask = PCI_IO_RANGE_MASK;

BAR all-ones sizing, type, 64-bit pair와 resource flag 해석의 한 단계입니다. 이 줄이 바꾸는 software 상태를 대응하는 controller register나 trace checkpoint와 연결해 확인합니다.

L400 io_granularity = 0x1000;

BAR all-ones sizing, type, 64-bit pair와 resource flag 해석의 한 단계입니다. 이 줄이 바꾸는 software 상태를 대응하는 controller register나 trace checkpoint와 연결해 확인합니다.

L401 if (dev->io_window_1k) {

상태나 ID를 분류하는 분기입니다. 이 조건의 참·거짓은 장치 register, firmware 기술 또는 CPU mode 중 하나에서 결정됩니다.

L402 /* Support 1K I/O space granularity */

원본 주석입니다. BAR all-ones sizing, type, 64-bit pair와 resource flag 해석에서 뒤따르는 코드가 전제하는 하드웨어 조건이나 예외를 먼저 확인합니다.

L403 io_mask = PCI_IO_1K_RANGE_MASK;

BAR all-ones sizing, type, 64-bit pair와 resource flag 해석의 한 단계입니다. 이 줄이 바꾸는 software 상태를 대응하는 controller register나 trace checkpoint와 연결해 확인합니다.

L404 io_granularity = 0x400;

BAR all-ones sizing, type, 64-bit pair와 resource flag 해석의 한 단계입니다. 이 줄이 바꾸는 software 상태를 대응하는 controller register나 trace checkpoint와 연결해 확인합니다.

L405 }

제어 블록의 범위를 표시합니다. 이 괄호 안에서만 유효한 lock, CPU context, resource lifetime을 함께 표시해 둡니다.

L406 (blank)

논리 구간을 나누는 빈 줄입니다. 바로 위에서 준비한 상태와 다음 제어 흐름을 분리해 읽습니다.

L407 pci_read_config_byte(dev, PCI_IO_BASE, &io_base_lo);

공유 상태나 MMIO를 읽습니다. 이 값은 단순 변수라기보다 hardware가 갱신한 시점과 read ordering을 포함해 해석해야 합니다.

L408 pci_read_config_byte(dev, PCI_IO_LIMIT, &io_limit_lo);

공유 상태나 MMIO를 읽습니다. 이 값은 단순 변수라기보다 hardware가 갱신한 시점과 read ordering을 포함해 해석해야 합니다.

L409 base = (io_base_lo & io_mask) << 8;

BAR all-ones sizing, type, 64-bit pair와 resource flag 해석의 한 단계입니다. 이 줄이 바꾸는 software 상태를 대응하는 controller register나 trace checkpoint와 연결해 확인합니다.

L410 limit = (io_limit_lo & io_mask) << 8;

BAR all-ones sizing, type, 64-bit pair와 resource flag 해석의 한 단계입니다. 이 줄이 바꾸는 software 상태를 대응하는 controller register나 trace checkpoint와 연결해 확인합니다.

L411 (blank)

논리 구간을 나누는 빈 줄입니다. 바로 위에서 준비한 상태와 다음 제어 흐름을 분리해 읽습니다.

L412 if ((io_base_lo & PCI_IO_RANGE_TYPE_MASK) == PCI_IO_RANGE_TYPE_32) {

상태나 ID를 분류하는 분기입니다. 이 조건의 참·거짓은 장치 register, firmware 기술 또는 CPU mode 중 하나에서 결정됩니다.

L413 u16 io_base_hi, io_limit_hi;

BAR all-ones sizing, type, 64-bit pair와 resource flag 해석의 한 단계입니다. 이 줄이 바꾸는 software 상태를 대응하는 controller register나 trace checkpoint와 연결해 확인합니다.

L414 (blank)

논리 구간을 나누는 빈 줄입니다. 바로 위에서 준비한 상태와 다음 제어 흐름을 분리해 읽습니다.

L415 pci_read_config_word(dev, PCI_IO_BASE_UPPER16, &io_base_hi);

공유 상태나 MMIO를 읽습니다. 이 값은 단순 변수라기보다 hardware가 갱신한 시점과 read ordering을 포함해 해석해야 합니다.

L416 pci_read_config_word(dev, PCI_IO_LIMIT_UPPER16, &io_limit_hi);

공유 상태나 MMIO를 읽습니다. 이 값은 단순 변수라기보다 hardware가 갱신한 시점과 read ordering을 포함해 해석해야 합니다.

L417 base |= ((unsigned long) io_base_hi << 16);

BAR all-ones sizing, type, 64-bit pair와 resource flag 해석의 한 단계입니다. 이 줄이 바꾸는 software 상태를 대응하는 controller register나 trace checkpoint와 연결해 확인합니다.

L418 limit |= ((unsigned long) io_limit_hi << 16);

BAR all-ones sizing, type, 64-bit pair와 resource flag 해석의 한 단계입니다. 이 줄이 바꾸는 software 상태를 대응하는 controller register나 trace checkpoint와 연결해 확인합니다.

L419 }

제어 블록의 범위를 표시합니다. 이 괄호 안에서만 유효한 lock, CPU context, resource lifetime을 함께 표시해 둡니다.

L420 (blank)

논리 구간을 나누는 빈 줄입니다. 바로 위에서 준비한 상태와 다음 제어 흐름을 분리해 읽습니다.

L421 res->flags = (io_base_lo & PCI_IO_RANGE_TYPE_MASK) | IORESOURCE_IO;

BAR all-ones sizing, type, 64-bit pair와 resource flag 해석의 한 단계입니다. 이 줄이 바꾸는 software 상태를 대응하는 controller register나 trace checkpoint와 연결해 확인합니다.

L422 (blank)

논리 구간을 나누는 빈 줄입니다. 바로 위에서 준비한 상태와 다음 제어 흐름을 분리해 읽습니다.

L423 if (base <= limit) {

상태나 ID를 분류하는 분기입니다. 이 조건의 참·거짓은 장치 register, firmware 기술 또는 CPU mode 중 하나에서 결정됩니다.

L424 region.start = base;

BAR all-ones sizing, type, 64-bit pair와 resource flag 해석의 한 단계입니다. 이 줄이 바꾸는 software 상태를 대응하는 controller register나 trace checkpoint와 연결해 확인합니다.

L425 region.end = limit + io_granularity - 1;

BAR all-ones sizing, type, 64-bit pair와 resource flag 해석의 한 단계입니다. 이 줄이 바꾸는 software 상태를 대응하는 controller register나 trace checkpoint와 연결해 확인합니다.

L426 pcibios_bus_to_resource(dev->bus, res, &region);

BAR all-ones sizing, type, 64-bit pair와 resource flag 해석의 한 단계입니다. 이 줄이 바꾸는 software 상태를 대응하는 controller register나 trace checkpoint와 연결해 확인합니다.

L427 if (log)

상태나 ID를 분류하는 분기입니다. 이 조건의 참·거짓은 장치 register, firmware 기술 또는 CPU mode 중 하나에서 결정됩니다.

L428 pci_info(dev, " bridge window %pR\n", res);

BAR all-ones sizing, type, 64-bit pair와 resource flag 해석의 한 단계입니다. 이 줄이 바꾸는 software 상태를 대응하는 controller register나 trace checkpoint와 연결해 확인합니다.

L429 } else {

BAR all-ones sizing, type, 64-bit pair와 resource flag 해석의 한 단계입니다. 이 줄이 바꾸는 software 상태를 대응하는 controller register나 trace checkpoint와 연결해 확인합니다.

L430 resource_set_range(res, 0, 0);

BAR all-ones sizing, type, 64-bit pair와 resource flag 해석의 한 단계입니다. 이 줄이 바꾸는 software 상태를 대응하는 controller register나 trace checkpoint와 연결해 확인합니다.

L431 res->flags |= IORESOURCE_UNSET | IORESOURCE_DISABLED;

BAR all-ones sizing, type, 64-bit pair와 resource flag 해석의 한 단계입니다. 이 줄이 바꾸는 software 상태를 대응하는 controller register나 trace checkpoint와 연결해 확인합니다.

L432 }

제어 블록의 범위를 표시합니다. 이 괄호 안에서만 유효한 lock, CPU context, resource lifetime을 함께 표시해 둡니다.

L433 }

제어 블록의 범위를 표시합니다. 이 괄호 안에서만 유효한 lock, CPU context, resource lifetime을 함께 표시해 둡니다.

L434 (blank)

논리 구간을 나누는 빈 줄입니다. 바로 위에서 준비한 상태와 다음 제어 흐름을 분리해 읽습니다.

L435 static void pci_read_bridge_mmio(struct pci_dev *dev, struct resource *res,

함수 경계입니다. 호출자가 넘긴 번호 공간과 현재 CPU/controller context가 BAR all-ones sizing, type, 64-bit pair와 resource flag 해석의 입력 계약을 만족하는지 확인합니다.

L436 bool log)

BAR all-ones sizing, type, 64-bit pair와 resource flag 해석의 한 단계입니다. 이 줄이 바꾸는 software 상태를 대응하는 controller register나 trace checkpoint와 연결해 확인합니다.

L437 {

제어 블록의 범위를 표시합니다. 이 괄호 안에서만 유효한 lock, CPU context, resource lifetime을 함께 표시해 둡니다.

L438 u16 mem_base_lo, mem_limit_lo;

BAR all-ones sizing, type, 64-bit pair와 resource flag 해석의 한 단계입니다. 이 줄이 바꾸는 software 상태를 대응하는 controller register나 trace checkpoint와 연결해 확인합니다.

L439 unsigned long base, limit;

BAR all-ones sizing, type, 64-bit pair와 resource flag 해석의 한 단계입니다. 이 줄이 바꾸는 software 상태를 대응하는 controller register나 trace checkpoint와 연결해 확인합니다.

Linux v6.18.37 · 실제 원본 코드

Root bus resource assignment

drivers/pci/setup-bus.c:2237-2307

2237	free_list(fail_head);
2238}
2239 
2240/*
2241 * First try will not touch PCI bridge res.
2242 * Second and later try will clear small leaf bridge res.
2243 * Will stop till to the max depth if can not find good one.
2244 */
2245void pci_assign_unassigned_root_bus_resources(struct pci_bus *bus)
2246{
2247	LIST_HEAD(realloc_head);
2248	/* List of resources that want additional resources */
2249	struct list_head *add_list = NULL;
2250	int tried_times = 0;
2251	enum release_type rel_type = leaf_only;
2252	LIST_HEAD(fail_head);
2253	int pci_try_num = 1;
2254	enum enable_type enable_local;
2255 
2256	/* Don't realloc if asked to do so */
2257	enable_local = pci_realloc_detect(bus, pci_realloc_enable);
2258	if (pci_realloc_enabled(enable_local)) {
2259		int max_depth = pci_bus_get_depth(bus);
2260 
2261		pci_try_num = max_depth + 1;
2262		dev_info(&bus->dev, "max bus depth: %d pci_try_num: %d\n",
2263			 max_depth, pci_try_num);
2264	}
2265 
2266	while (1) {
2267		/*
2268		 * Last try will use add_list, otherwise will try good to
2269		 * have as must have, so can realloc parent bridge resource
2270		 */
2271		if (tried_times + 1 == pci_try_num)
2272			add_list = &realloc_head;
2273		/*
2274		 * Depth first, calculate sizes and alignments of all
2275		 * subordinate buses.
2276		 */
2277		__pci_bus_size_bridges(bus, add_list);
2278 
2279		pci_root_bus_distribute_available_resources(bus, add_list);
2280 
2281		/* Depth last, allocate resources and update the hardware. */
2282		__pci_bus_assign_resources(bus, add_list, &fail_head);
2283		if (WARN_ON_ONCE(add_list && !list_empty(add_list)))
2284			free_list(add_list);
2285		tried_times++;
2286 
2287		/* Any device complain? */
2288		if (list_empty(&fail_head))
2289			break;
2290 
2291		if (tried_times >= pci_try_num) {
2292			if (enable_local == undefined) {
2293				dev_info(&bus->dev,
2294					 "Some PCI device resources are unassigned, try booting with pci=realloc\n");
2295			} else if (enable_local == auto_enabled) {
2296				dev_info(&bus->dev,
2297					 "Automatically enabled pci realloc, if you have problem, try booting with pci=realloc=off\n");
2298			}
2299			free_list(&fail_head);
2300			break;
2301		}
2302 
2303		/* Third times and later will not check if it is leaf */
2304		if (tried_times + 1 > 2)
2305			rel_type = whole_subtree;
2306 
2307		pci_prepare_next_assign_round(&fail_head, tried_times, rel_type);
L2237 free_list(fail_head);

Root bus resource assignment의 한 단계입니다. 이 줄이 바꾸는 software 상태를 대응하는 controller register나 trace checkpoint와 연결해 확인합니다.

L2238 }

제어 블록의 범위를 표시합니다. 이 괄호 안에서만 유효한 lock, CPU context, resource lifetime을 함께 표시해 둡니다.

L2239 (blank)

논리 구간을 나누는 빈 줄입니다. 바로 위에서 준비한 상태와 다음 제어 흐름을 분리해 읽습니다.

L2240 /*

원본 주석입니다. Root bus resource assignment에서 뒤따르는 코드가 전제하는 하드웨어 조건이나 예외를 먼저 확인합니다.

L2241 * First try will not touch PCI bridge res.

원본 주석입니다. Root bus resource assignment에서 뒤따르는 코드가 전제하는 하드웨어 조건이나 예외를 먼저 확인합니다.

L2242 * Second and later try will clear small leaf bridge res.

원본 주석입니다. Root bus resource assignment에서 뒤따르는 코드가 전제하는 하드웨어 조건이나 예외를 먼저 확인합니다.

L2243 * Will stop till to the max depth if can not find good one.

원본 주석입니다. Root bus resource assignment에서 뒤따르는 코드가 전제하는 하드웨어 조건이나 예외를 먼저 확인합니다.

L2244 */

원본 주석입니다. Root bus resource assignment에서 뒤따르는 코드가 전제하는 하드웨어 조건이나 예외를 먼저 확인합니다.

L2245 void pci_assign_unassigned_root_bus_resources(struct pci_bus *bus)

함수 경계입니다. 호출자가 넘긴 번호 공간과 현재 CPU/controller context가 Root bus resource assignment의 입력 계약을 만족하는지 확인합니다.

L2246 {

제어 블록의 범위를 표시합니다. 이 괄호 안에서만 유효한 lock, CPU context, resource lifetime을 함께 표시해 둡니다.

L2247 LIST_HEAD(realloc_head);

Root bus resource assignment의 한 단계입니다. 이 줄이 바꾸는 software 상태를 대응하는 controller register나 trace checkpoint와 연결해 확인합니다.

L2248 /* List of resources that want additional resources */

원본 주석입니다. Root bus resource assignment에서 뒤따르는 코드가 전제하는 하드웨어 조건이나 예외를 먼저 확인합니다.

L2249 struct list_head *add_list = NULL;

Root bus resource assignment의 한 단계입니다. 이 줄이 바꾸는 software 상태를 대응하는 controller register나 trace checkpoint와 연결해 확인합니다.

L2250 int tried_times = 0;

Root bus resource assignment의 한 단계입니다. 이 줄이 바꾸는 software 상태를 대응하는 controller register나 trace checkpoint와 연결해 확인합니다.

L2251 enum release_type rel_type = leaf_only;

Root bus resource assignment의 한 단계입니다. 이 줄이 바꾸는 software 상태를 대응하는 controller register나 trace checkpoint와 연결해 확인합니다.

L2252 LIST_HEAD(fail_head);

Root bus resource assignment의 한 단계입니다. 이 줄이 바꾸는 software 상태를 대응하는 controller register나 trace checkpoint와 연결해 확인합니다.

L2253 int pci_try_num = 1;

Root bus resource assignment의 한 단계입니다. 이 줄이 바꾸는 software 상태를 대응하는 controller register나 trace checkpoint와 연결해 확인합니다.

L2254 enum enable_type enable_local;

Root bus resource assignment의 한 단계입니다. 이 줄이 바꾸는 software 상태를 대응하는 controller register나 trace checkpoint와 연결해 확인합니다.

L2255 (blank)

논리 구간을 나누는 빈 줄입니다. 바로 위에서 준비한 상태와 다음 제어 흐름을 분리해 읽습니다.

L2256 /* Don't realloc if asked to do so */

원본 주석입니다. Root bus resource assignment에서 뒤따르는 코드가 전제하는 하드웨어 조건이나 예외를 먼저 확인합니다.

L2257 enable_local = pci_realloc_detect(bus, pci_realloc_enable);

Root bus resource assignment의 한 단계입니다. 이 줄이 바꾸는 software 상태를 대응하는 controller register나 trace checkpoint와 연결해 확인합니다.

L2258 if (pci_realloc_enabled(enable_local)) {

상태나 ID를 분류하는 분기입니다. 이 조건의 참·거짓은 장치 register, firmware 기술 또는 CPU mode 중 하나에서 결정됩니다.

L2259 int max_depth = pci_bus_get_depth(bus);

함수 경계입니다. 호출자가 넘긴 번호 공간과 현재 CPU/controller context가 Root bus resource assignment의 입력 계약을 만족하는지 확인합니다.

L2260 (blank)

논리 구간을 나누는 빈 줄입니다. 바로 위에서 준비한 상태와 다음 제어 흐름을 분리해 읽습니다.

L2261 pci_try_num = max_depth + 1;

Root bus resource assignment의 한 단계입니다. 이 줄이 바꾸는 software 상태를 대응하는 controller register나 trace checkpoint와 연결해 확인합니다.

L2262 dev_info(&bus->dev, "max bus depth: %d pci_try_num: %d\n",

Root bus resource assignment의 한 단계입니다. 이 줄이 바꾸는 software 상태를 대응하는 controller register나 trace checkpoint와 연결해 확인합니다.

L2263 max_depth, pci_try_num);

Root bus resource assignment의 한 단계입니다. 이 줄이 바꾸는 software 상태를 대응하는 controller register나 trace checkpoint와 연결해 확인합니다.

L2264 }

제어 블록의 범위를 표시합니다. 이 괄호 안에서만 유효한 lock, CPU context, resource lifetime을 함께 표시해 둡니다.

L2265 (blank)

논리 구간을 나누는 빈 줄입니다. 바로 위에서 준비한 상태와 다음 제어 흐름을 분리해 읽습니다.

L2266 while (1) {

여러 pending source 또는 resource를 순회하는 구간입니다. 한 번의 진입에서 처리 가능한 양과 종료 조건이 interrupt latency에 영향을 줍니다.

L2267 /*

원본 주석입니다. Root bus resource assignment에서 뒤따르는 코드가 전제하는 하드웨어 조건이나 예외를 먼저 확인합니다.

L2268 * Last try will use add_list, otherwise will try good to

원본 주석입니다. Root bus resource assignment에서 뒤따르는 코드가 전제하는 하드웨어 조건이나 예외를 먼저 확인합니다.

L2269 * have as must have, so can realloc parent bridge resource

원본 주석입니다. Root bus resource assignment에서 뒤따르는 코드가 전제하는 하드웨어 조건이나 예외를 먼저 확인합니다.

L2270 */

원본 주석입니다. Root bus resource assignment에서 뒤따르는 코드가 전제하는 하드웨어 조건이나 예외를 먼저 확인합니다.

L2271 if (tried_times + 1 == pci_try_num)

상태나 ID를 분류하는 분기입니다. 이 조건의 참·거짓은 장치 register, firmware 기술 또는 CPU mode 중 하나에서 결정됩니다.

L2272 add_list = &realloc_head;

Root bus resource assignment의 한 단계입니다. 이 줄이 바꾸는 software 상태를 대응하는 controller register나 trace checkpoint와 연결해 확인합니다.

L2273 /*

원본 주석입니다. Root bus resource assignment에서 뒤따르는 코드가 전제하는 하드웨어 조건이나 예외를 먼저 확인합니다.

L2274 * Depth first, calculate sizes and alignments of all

원본 주석입니다. Root bus resource assignment에서 뒤따르는 코드가 전제하는 하드웨어 조건이나 예외를 먼저 확인합니다.

L2275 * subordinate buses.

원본 주석입니다. Root bus resource assignment에서 뒤따르는 코드가 전제하는 하드웨어 조건이나 예외를 먼저 확인합니다.

L2276 */

원본 주석입니다. Root bus resource assignment에서 뒤따르는 코드가 전제하는 하드웨어 조건이나 예외를 먼저 확인합니다.

L2277 __pci_bus_size_bridges(bus, add_list);

Root bus resource assignment의 한 단계입니다. 이 줄이 바꾸는 software 상태를 대응하는 controller register나 trace checkpoint와 연결해 확인합니다.

L2278 (blank)

논리 구간을 나누는 빈 줄입니다. 바로 위에서 준비한 상태와 다음 제어 흐름을 분리해 읽습니다.

L2279 pci_root_bus_distribute_available_resources(bus, add_list);

Root bus resource assignment의 한 단계입니다. 이 줄이 바꾸는 software 상태를 대응하는 controller register나 trace checkpoint와 연결해 확인합니다.

L2280 (blank)

논리 구간을 나누는 빈 줄입니다. 바로 위에서 준비한 상태와 다음 제어 흐름을 분리해 읽습니다.

L2281 /* Depth last, allocate resources and update the hardware. */

원본 주석입니다. Root bus resource assignment에서 뒤따르는 코드가 전제하는 하드웨어 조건이나 예외를 먼저 확인합니다.

L2282 __pci_bus_assign_resources(bus, add_list, &fail_head);

Root bus resource assignment의 한 단계입니다. 이 줄이 바꾸는 software 상태를 대응하는 controller register나 trace checkpoint와 연결해 확인합니다.

L2283 if (WARN_ON_ONCE(add_list && !list_empty(add_list)))

상태나 ID를 분류하는 분기입니다. 이 조건의 참·거짓은 장치 register, firmware 기술 또는 CPU mode 중 하나에서 결정됩니다.

L2284 free_list(add_list);

Root bus resource assignment의 한 단계입니다. 이 줄이 바꾸는 software 상태를 대응하는 controller register나 trace checkpoint와 연결해 확인합니다.

L2285 tried_times++;

Root bus resource assignment의 한 단계입니다. 이 줄이 바꾸는 software 상태를 대응하는 controller register나 trace checkpoint와 연결해 확인합니다.

L2286 (blank)

논리 구간을 나누는 빈 줄입니다. 바로 위에서 준비한 상태와 다음 제어 흐름을 분리해 읽습니다.

L2287 /* Any device complain? */

원본 주석입니다. Root bus resource assignment에서 뒤따르는 코드가 전제하는 하드웨어 조건이나 예외를 먼저 확인합니다.

L2288 if (list_empty(&fail_head))

상태나 ID를 분류하는 분기입니다. 이 조건의 참·거짓은 장치 register, firmware 기술 또는 CPU mode 중 하나에서 결정됩니다.

L2289 break;

Root bus resource assignment의 한 단계입니다. 이 줄이 바꾸는 software 상태를 대응하는 controller register나 trace checkpoint와 연결해 확인합니다.

L2290 (blank)

논리 구간을 나누는 빈 줄입니다. 바로 위에서 준비한 상태와 다음 제어 흐름을 분리해 읽습니다.

L2291 if (tried_times >= pci_try_num) {

상태나 ID를 분류하는 분기입니다. 이 조건의 참·거짓은 장치 register, firmware 기술 또는 CPU mode 중 하나에서 결정됩니다.

L2292 if (enable_local == undefined) {

상태나 ID를 분류하는 분기입니다. 이 조건의 참·거짓은 장치 register, firmware 기술 또는 CPU mode 중 하나에서 결정됩니다.

L2293 dev_info(&bus->dev,

Root bus resource assignment의 한 단계입니다. 이 줄이 바꾸는 software 상태를 대응하는 controller register나 trace checkpoint와 연결해 확인합니다.

L2294 "Some PCI device resources are unassigned, try booting with pci=realloc\n");

Root bus resource assignment의 한 단계입니다. 이 줄이 바꾸는 software 상태를 대응하는 controller register나 trace checkpoint와 연결해 확인합니다.

L2295 } else if (enable_local == auto_enabled) {

Root bus resource assignment의 한 단계입니다. 이 줄이 바꾸는 software 상태를 대응하는 controller register나 trace checkpoint와 연결해 확인합니다.

L2296 dev_info(&bus->dev,

Root bus resource assignment의 한 단계입니다. 이 줄이 바꾸는 software 상태를 대응하는 controller register나 trace checkpoint와 연결해 확인합니다.

L2297 "Automatically enabled pci realloc, if you have problem, try booting with pci=realloc=off\n");

Root bus resource assignment의 한 단계입니다. 이 줄이 바꾸는 software 상태를 대응하는 controller register나 trace checkpoint와 연결해 확인합니다.

L2298 }

제어 블록의 범위를 표시합니다. 이 괄호 안에서만 유효한 lock, CPU context, resource lifetime을 함께 표시해 둡니다.

L2299 free_list(&fail_head);

Root bus resource assignment의 한 단계입니다. 이 줄이 바꾸는 software 상태를 대응하는 controller register나 trace checkpoint와 연결해 확인합니다.

L2300 break;

Root bus resource assignment의 한 단계입니다. 이 줄이 바꾸는 software 상태를 대응하는 controller register나 trace checkpoint와 연결해 확인합니다.

L2301 }

제어 블록의 범위를 표시합니다. 이 괄호 안에서만 유효한 lock, CPU context, resource lifetime을 함께 표시해 둡니다.

L2302 (blank)

논리 구간을 나누는 빈 줄입니다. 바로 위에서 준비한 상태와 다음 제어 흐름을 분리해 읽습니다.

L2303 /* Third times and later will not check if it is leaf */

원본 주석입니다. Root bus resource assignment에서 뒤따르는 코드가 전제하는 하드웨어 조건이나 예외를 먼저 확인합니다.

L2304 if (tried_times + 1 > 2)

상태나 ID를 분류하는 분기입니다. 이 조건의 참·거짓은 장치 register, firmware 기술 또는 CPU mode 중 하나에서 결정됩니다.

L2305 rel_type = whole_subtree;

Root bus resource assignment의 한 단계입니다. 이 줄이 바꾸는 software 상태를 대응하는 controller register나 trace checkpoint와 연결해 확인합니다.

L2306 (blank)

논리 구간을 나누는 빈 줄입니다. 바로 위에서 준비한 상태와 다음 제어 흐름을 분리해 읽습니다.

L2307 pci_prepare_next_assign_round(&fail_head, tried_times, rel_type);

Root bus resource assignment의 한 단계입니다. 이 줄이 바꾸는 software 상태를 대응하는 controller register나 trace checkpoint와 연결해 확인합니다.

Linux v6.18.37 · 실제 원본 코드

PCI device enable

drivers/pci/pci.c:2143-2183

2143 * pci_enable_device - Initialize device before it's used by a driver.
2144 * @dev: PCI device to be initialized
2145 *
2146 * Initialize device before it's used by a driver. Ask low-level code
2147 * to enable I/O and memory. Wake up the device if it was suspended.
2148 * Beware, this function can fail.
2149 *
2150 * Note we don't actually enable the device many times if we call
2151 * this function repeatedly (we just increment the count).
2152 */
2153int pci_enable_device(struct pci_dev *dev)
2154{
2155	return pci_enable_device_flags(dev, IORESOURCE_MEM | IORESOURCE_IO);
2156}
2157EXPORT_SYMBOL(pci_enable_device);
2158 
2159/*
2160 * pcibios_device_add - provide arch specific hooks when adding device dev
2161 * @dev: the PCI device being added
2162 *
2163 * Permits the platform to provide architecture specific functionality when
2164 * devices are added. This is the default implementation. Architecture
2165 * implementations can override this.
2166 */
2167int __weak pcibios_device_add(struct pci_dev *dev)
2168{
2169	return 0;
2170}
2171 
2172/**
2173 * pcibios_release_device - provide arch specific hooks when releasing
2174 *			    device dev
2175 * @dev: the PCI device being released
2176 *
2177 * Permits the platform to provide architecture specific functionality when
2178 * devices are released. This is the default implementation. Architecture
2179 * implementations can override this.
2180 */
2181void __weak pcibios_release_device(struct pci_dev *dev) {}
2182 
2183/**
L2143 * pci_enable_device - Initialize device before it's used by a driver.

원본 주석입니다. PCI device enable에서 뒤따르는 코드가 전제하는 하드웨어 조건이나 예외를 먼저 확인합니다.

L2144 * @dev: PCI device to be initialized

원본 주석입니다. PCI device enable에서 뒤따르는 코드가 전제하는 하드웨어 조건이나 예외를 먼저 확인합니다.

L2145 *

원본 주석입니다. PCI device enable에서 뒤따르는 코드가 전제하는 하드웨어 조건이나 예외를 먼저 확인합니다.

L2146 * Initialize device before it's used by a driver. Ask low-level code

원본 주석입니다. PCI device enable에서 뒤따르는 코드가 전제하는 하드웨어 조건이나 예외를 먼저 확인합니다.

L2147 * to enable I/O and memory. Wake up the device if it was suspended.

원본 주석입니다. PCI device enable에서 뒤따르는 코드가 전제하는 하드웨어 조건이나 예외를 먼저 확인합니다.

L2148 * Beware, this function can fail.

원본 주석입니다. PCI device enable에서 뒤따르는 코드가 전제하는 하드웨어 조건이나 예외를 먼저 확인합니다.

L2149 *

원본 주석입니다. PCI device enable에서 뒤따르는 코드가 전제하는 하드웨어 조건이나 예외를 먼저 확인합니다.

L2150 * Note we don't actually enable the device many times if we call

원본 주석입니다. PCI device enable에서 뒤따르는 코드가 전제하는 하드웨어 조건이나 예외를 먼저 확인합니다.

L2151 * this function repeatedly (we just increment the count).

원본 주석입니다. PCI device enable에서 뒤따르는 코드가 전제하는 하드웨어 조건이나 예외를 먼저 확인합니다.

L2152 */

원본 주석입니다. PCI device enable에서 뒤따르는 코드가 전제하는 하드웨어 조건이나 예외를 먼저 확인합니다.

L2153 int pci_enable_device(struct pci_dev *dev)

함수 경계입니다. 호출자가 넘긴 번호 공간과 현재 CPU/controller context가 PCI device enable의 입력 계약을 만족하는지 확인합니다.

L2154 {

제어 블록의 범위를 표시합니다. 이 괄호 안에서만 유효한 lock, CPU context, resource lifetime을 함께 표시해 둡니다.

L2155 return pci_enable_device_flags(dev, IORESOURCE_MEM | IORESOURCE_IO);

호출자에게 상태를 반환합니다. 반환값만 보지 말고 그 전에 controller ownership이나 active 상태가 정리되었는지 확인합니다.

L2156 }

제어 블록의 범위를 표시합니다. 이 괄호 안에서만 유효한 lock, CPU context, resource lifetime을 함께 표시해 둡니다.

L2157 EXPORT_SYMBOL(pci_enable_device);

PCI device enable의 한 단계입니다. 이 줄이 바꾸는 software 상태를 대응하는 controller register나 trace checkpoint와 연결해 확인합니다.

L2158 (blank)

논리 구간을 나누는 빈 줄입니다. 바로 위에서 준비한 상태와 다음 제어 흐름을 분리해 읽습니다.

L2159 /*

원본 주석입니다. PCI device enable에서 뒤따르는 코드가 전제하는 하드웨어 조건이나 예외를 먼저 확인합니다.

L2160 * pcibios_device_add - provide arch specific hooks when adding device dev

원본 주석입니다. PCI device enable에서 뒤따르는 코드가 전제하는 하드웨어 조건이나 예외를 먼저 확인합니다.

L2161 * @dev: the PCI device being added

원본 주석입니다. PCI device enable에서 뒤따르는 코드가 전제하는 하드웨어 조건이나 예외를 먼저 확인합니다.

L2162 *

원본 주석입니다. PCI device enable에서 뒤따르는 코드가 전제하는 하드웨어 조건이나 예외를 먼저 확인합니다.

L2163 * Permits the platform to provide architecture specific functionality when

원본 주석입니다. PCI device enable에서 뒤따르는 코드가 전제하는 하드웨어 조건이나 예외를 먼저 확인합니다.

L2164 * devices are added. This is the default implementation. Architecture

원본 주석입니다. PCI device enable에서 뒤따르는 코드가 전제하는 하드웨어 조건이나 예외를 먼저 확인합니다.

L2165 * implementations can override this.

원본 주석입니다. PCI device enable에서 뒤따르는 코드가 전제하는 하드웨어 조건이나 예외를 먼저 확인합니다.

L2166 */

원본 주석입니다. PCI device enable에서 뒤따르는 코드가 전제하는 하드웨어 조건이나 예외를 먼저 확인합니다.

L2167 int __weak pcibios_device_add(struct pci_dev *dev)

함수 경계입니다. 호출자가 넘긴 번호 공간과 현재 CPU/controller context가 PCI device enable의 입력 계약을 만족하는지 확인합니다.

L2168 {

제어 블록의 범위를 표시합니다. 이 괄호 안에서만 유효한 lock, CPU context, resource lifetime을 함께 표시해 둡니다.

L2169 return 0;

호출자에게 상태를 반환합니다. 반환값만 보지 말고 그 전에 controller ownership이나 active 상태가 정리되었는지 확인합니다.

L2170 }

제어 블록의 범위를 표시합니다. 이 괄호 안에서만 유효한 lock, CPU context, resource lifetime을 함께 표시해 둡니다.

L2171 (blank)

논리 구간을 나누는 빈 줄입니다. 바로 위에서 준비한 상태와 다음 제어 흐름을 분리해 읽습니다.

L2172 /**

원본 주석입니다. PCI device enable에서 뒤따르는 코드가 전제하는 하드웨어 조건이나 예외를 먼저 확인합니다.

L2173 * pcibios_release_device - provide arch specific hooks when releasing

원본 주석입니다. PCI device enable에서 뒤따르는 코드가 전제하는 하드웨어 조건이나 예외를 먼저 확인합니다.

L2174 * device dev

원본 주석입니다. PCI device enable에서 뒤따르는 코드가 전제하는 하드웨어 조건이나 예외를 먼저 확인합니다.

L2175 * @dev: the PCI device being released

원본 주석입니다. PCI device enable에서 뒤따르는 코드가 전제하는 하드웨어 조건이나 예외를 먼저 확인합니다.

L2176 *

원본 주석입니다. PCI device enable에서 뒤따르는 코드가 전제하는 하드웨어 조건이나 예외를 먼저 확인합니다.

L2177 * Permits the platform to provide architecture specific functionality when

원본 주석입니다. PCI device enable에서 뒤따르는 코드가 전제하는 하드웨어 조건이나 예외를 먼저 확인합니다.

L2178 * devices are released. This is the default implementation. Architecture

원본 주석입니다. PCI device enable에서 뒤따르는 코드가 전제하는 하드웨어 조건이나 예외를 먼저 확인합니다.

L2179 * implementations can override this.

원본 주석입니다. PCI device enable에서 뒤따르는 코드가 전제하는 하드웨어 조건이나 예외를 먼저 확인합니다.

L2180 */

원본 주석입니다. PCI device enable에서 뒤따르는 코드가 전제하는 하드웨어 조건이나 예외를 먼저 확인합니다.

L2181 void __weak pcibios_release_device(struct pci_dev *dev) {}

함수 경계입니다. 호출자가 넘긴 번호 공간과 현재 CPU/controller context가 PCI device enable의 입력 계약을 만족하는지 확인합니다.

L2182 (blank)

논리 구간을 나누는 빈 줄입니다. 바로 위에서 준비한 상태와 다음 제어 흐름을 분리해 읽습니다.

L2183 /**

원본 주석입니다. PCI device enable에서 뒤따르는 코드가 전제하는 하드웨어 조건이나 예외를 먼저 확인합니다.

Linux v6.18.37 · 실제 원본 코드

BAR mapping

drivers/pci/iomap.c:23-71

23 * the details if this is a MMIO or PIO address space and will just do what
24 * you expect from them in the correct way.
25 *
26 * @maxlen specifies the maximum length to map. If you want to get access to
27 * the complete BAR from offset to the end, pass %0 here.
28 * */
29void __iomem *pci_iomap_range(struct pci_dev *dev,
30			      int bar,
31			      unsigned long offset,
32			      unsigned long maxlen)
33{
34	resource_size_t start, len;
35	unsigned long flags;
36 
37	if (!pci_bar_index_is_valid(bar))
38		return NULL;
39 
40	start = pci_resource_start(dev, bar);
41	len = pci_resource_len(dev, bar);
42	flags = pci_resource_flags(dev, bar);
43 
44	if (len <= offset || !start)
45		return NULL;
46 
47	len -= offset;
48	start += offset;
49	if (maxlen && len > maxlen)
50		len = maxlen;
51	if (flags & IORESOURCE_IO)
52		return __pci_ioport_map(dev, start, len);
53	if (flags & IORESOURCE_MEM)
54		return ioremap(start, len);
55	/* What? */
56	return NULL;
57}
58EXPORT_SYMBOL(pci_iomap_range);
59 
60/**
61 * pci_iomap_wc_range - create a virtual WC mapping cookie for a PCI BAR
62 * @dev: PCI device that owns the BAR
63 * @bar: BAR number
64 * @offset: map memory at the given offset in BAR
65 * @maxlen: max length of the memory to map
66 *
67 * Using this function you will get a __iomem address to your device BAR.
68 * You can access it using ioread*() and iowrite*(). These functions hide
69 * the details if this is a MMIO or PIO address space and will just do what
70 * you expect from them in the correct way. When possible write combining
71 * is used.
L23 * the details if this is a MMIO or PIO address space and will just do what

원본 주석입니다. BAR mapping에서 뒤따르는 코드가 전제하는 하드웨어 조건이나 예외를 먼저 확인합니다.

L24 * you expect from them in the correct way.

원본 주석입니다. BAR mapping에서 뒤따르는 코드가 전제하는 하드웨어 조건이나 예외를 먼저 확인합니다.

L25 *

원본 주석입니다. BAR mapping에서 뒤따르는 코드가 전제하는 하드웨어 조건이나 예외를 먼저 확인합니다.

L26 * @maxlen specifies the maximum length to map. If you want to get access to

원본 주석입니다. BAR mapping에서 뒤따르는 코드가 전제하는 하드웨어 조건이나 예외를 먼저 확인합니다.

L27 * the complete BAR from offset to the end, pass %0 here.

원본 주석입니다. BAR mapping에서 뒤따르는 코드가 전제하는 하드웨어 조건이나 예외를 먼저 확인합니다.

L28 * */

원본 주석입니다. BAR mapping에서 뒤따르는 코드가 전제하는 하드웨어 조건이나 예외를 먼저 확인합니다.

L29 void __iomem *pci_iomap_range(struct pci_dev *dev,

함수 경계입니다. 호출자가 넘긴 번호 공간과 현재 CPU/controller context가 BAR mapping의 입력 계약을 만족하는지 확인합니다.

L30 int bar,

BAR mapping의 한 단계입니다. 이 줄이 바꾸는 software 상태를 대응하는 controller register나 trace checkpoint와 연결해 확인합니다.

L31 unsigned long offset,

BAR mapping의 한 단계입니다. 이 줄이 바꾸는 software 상태를 대응하는 controller register나 trace checkpoint와 연결해 확인합니다.

L32 unsigned long maxlen)

BAR mapping의 한 단계입니다. 이 줄이 바꾸는 software 상태를 대응하는 controller register나 trace checkpoint와 연결해 확인합니다.

L33 {

제어 블록의 범위를 표시합니다. 이 괄호 안에서만 유효한 lock, CPU context, resource lifetime을 함께 표시해 둡니다.

L34 resource_size_t start, len;

BAR mapping의 한 단계입니다. 이 줄이 바꾸는 software 상태를 대응하는 controller register나 trace checkpoint와 연결해 확인합니다.

L35 unsigned long flags;

BAR mapping의 한 단계입니다. 이 줄이 바꾸는 software 상태를 대응하는 controller register나 trace checkpoint와 연결해 확인합니다.

L36 (blank)

논리 구간을 나누는 빈 줄입니다. 바로 위에서 준비한 상태와 다음 제어 흐름을 분리해 읽습니다.

L37 if (!pci_bar_index_is_valid(bar))

상태나 ID를 분류하는 분기입니다. 이 조건의 참·거짓은 장치 register, firmware 기술 또는 CPU mode 중 하나에서 결정됩니다.

L38 return NULL;

호출자에게 상태를 반환합니다. 반환값만 보지 말고 그 전에 controller ownership이나 active 상태가 정리되었는지 확인합니다.

L39 (blank)

논리 구간을 나누는 빈 줄입니다. 바로 위에서 준비한 상태와 다음 제어 흐름을 분리해 읽습니다.

L40 start = pci_resource_start(dev, bar);

BAR mapping의 한 단계입니다. 이 줄이 바꾸는 software 상태를 대응하는 controller register나 trace checkpoint와 연결해 확인합니다.

L41 len = pci_resource_len(dev, bar);

BAR mapping의 한 단계입니다. 이 줄이 바꾸는 software 상태를 대응하는 controller register나 trace checkpoint와 연결해 확인합니다.

L42 flags = pci_resource_flags(dev, bar);

BAR mapping의 한 단계입니다. 이 줄이 바꾸는 software 상태를 대응하는 controller register나 trace checkpoint와 연결해 확인합니다.

L43 (blank)

논리 구간을 나누는 빈 줄입니다. 바로 위에서 준비한 상태와 다음 제어 흐름을 분리해 읽습니다.

L44 if (len <= offset || !start)

상태나 ID를 분류하는 분기입니다. 이 조건의 참·거짓은 장치 register, firmware 기술 또는 CPU mode 중 하나에서 결정됩니다.

L45 return NULL;

호출자에게 상태를 반환합니다. 반환값만 보지 말고 그 전에 controller ownership이나 active 상태가 정리되었는지 확인합니다.

L46 (blank)

논리 구간을 나누는 빈 줄입니다. 바로 위에서 준비한 상태와 다음 제어 흐름을 분리해 읽습니다.

L47 len -= offset;

BAR mapping의 한 단계입니다. 이 줄이 바꾸는 software 상태를 대응하는 controller register나 trace checkpoint와 연결해 확인합니다.

L48 start += offset;

BAR mapping의 한 단계입니다. 이 줄이 바꾸는 software 상태를 대응하는 controller register나 trace checkpoint와 연결해 확인합니다.

L49 if (maxlen && len > maxlen)

상태나 ID를 분류하는 분기입니다. 이 조건의 참·거짓은 장치 register, firmware 기술 또는 CPU mode 중 하나에서 결정됩니다.

L50 len = maxlen;

BAR mapping의 한 단계입니다. 이 줄이 바꾸는 software 상태를 대응하는 controller register나 trace checkpoint와 연결해 확인합니다.

L51 if (flags & IORESOURCE_IO)

상태나 ID를 분류하는 분기입니다. 이 조건의 참·거짓은 장치 register, firmware 기술 또는 CPU mode 중 하나에서 결정됩니다.

L52 return __pci_ioport_map(dev, start, len);

호출자에게 상태를 반환합니다. 반환값만 보지 말고 그 전에 controller ownership이나 active 상태가 정리되었는지 확인합니다.

L53 if (flags & IORESOURCE_MEM)

상태나 ID를 분류하는 분기입니다. 이 조건의 참·거짓은 장치 register, firmware 기술 또는 CPU mode 중 하나에서 결정됩니다.

L54 return ioremap(start, len);

호출자에게 상태를 반환합니다. 반환값만 보지 말고 그 전에 controller ownership이나 active 상태가 정리되었는지 확인합니다.

L55 /* What? */

원본 주석입니다. BAR mapping에서 뒤따르는 코드가 전제하는 하드웨어 조건이나 예외를 먼저 확인합니다.

L56 return NULL;

호출자에게 상태를 반환합니다. 반환값만 보지 말고 그 전에 controller ownership이나 active 상태가 정리되었는지 확인합니다.

L57 }

제어 블록의 범위를 표시합니다. 이 괄호 안에서만 유효한 lock, CPU context, resource lifetime을 함께 표시해 둡니다.

L58 EXPORT_SYMBOL(pci_iomap_range);

BAR mapping의 한 단계입니다. 이 줄이 바꾸는 software 상태를 대응하는 controller register나 trace checkpoint와 연결해 확인합니다.

L59 (blank)

논리 구간을 나누는 빈 줄입니다. 바로 위에서 준비한 상태와 다음 제어 흐름을 분리해 읽습니다.

L60 /**

원본 주석입니다. BAR mapping에서 뒤따르는 코드가 전제하는 하드웨어 조건이나 예외를 먼저 확인합니다.

L61 * pci_iomap_wc_range - create a virtual WC mapping cookie for a PCI BAR

원본 주석입니다. BAR mapping에서 뒤따르는 코드가 전제하는 하드웨어 조건이나 예외를 먼저 확인합니다.

L62 * @dev: PCI device that owns the BAR

원본 주석입니다. BAR mapping에서 뒤따르는 코드가 전제하는 하드웨어 조건이나 예외를 먼저 확인합니다.

L63 * @bar: BAR number

원본 주석입니다. BAR mapping에서 뒤따르는 코드가 전제하는 하드웨어 조건이나 예외를 먼저 확인합니다.

L64 * @offset: map memory at the given offset in BAR

원본 주석입니다. BAR mapping에서 뒤따르는 코드가 전제하는 하드웨어 조건이나 예외를 먼저 확인합니다.

L65 * @maxlen: max length of the memory to map

원본 주석입니다. BAR mapping에서 뒤따르는 코드가 전제하는 하드웨어 조건이나 예외를 먼저 확인합니다.

L66 *

원본 주석입니다. BAR mapping에서 뒤따르는 코드가 전제하는 하드웨어 조건이나 예외를 먼저 확인합니다.

L67 * Using this function you will get a __iomem address to your device BAR.

원본 주석입니다. BAR mapping에서 뒤따르는 코드가 전제하는 하드웨어 조건이나 예외를 먼저 확인합니다.

L68 * You can access it using ioread*() and iowrite*(). These functions hide

원본 주석입니다. BAR mapping에서 뒤따르는 코드가 전제하는 하드웨어 조건이나 예외를 먼저 확인합니다.

L69 * the details if this is a MMIO or PIO address space and will just do what

원본 주석입니다. BAR mapping에서 뒤따르는 코드가 전제하는 하드웨어 조건이나 예외를 먼저 확인합니다.

L70 * you expect from them in the correct way. When possible write combining

원본 주석입니다. BAR mapping에서 뒤따르는 코드가 전제하는 하드웨어 조건이나 예외를 먼저 확인합니다.

L71 * is used.

원본 주석입니다. BAR mapping에서 뒤따르는 코드가 전제하는 하드웨어 조건이나 예외를 먼저 확인합니다.

09 · VERIFICATION

실제 장비에서 확인하는 순서

  1. Hierarchy

    실행 `lspci -tv`와 각 bridge의 bus number/window를 저장한다.

    통과 기준 parent window가 child resource를 완전히 포함해야 한다.

  2. BAR

    실행 `cat /sys/bus/pci/devices/<BDF>/resource`를 driver map과 대조한다.

    통과 기준 BAR physical range와 ioremap 대상이 같은 resource여야 한다.

  3. DMA

    실행 IOMMU fault logging과 driver descriptor dump를 동시에 켠다.

    통과 기준 fault requester ID, IOVA, direction이 해당 queue entry와 일치해야 한다.

  4. MSI

    실행 `grep <driver> /proc/interrupts`와 affinity를 확인한다.

    통과 기준 queue activity와 vector count가 함께 증가해야 한다.

  5. Teardown

    실행 unbind/bind와 suspend/resume를 반복한다.

    통과 기준 resource, IOVA, vector가 누적되지 않고 동일 순서로 재획득돼야 한다.

10 · FAILURE MATRIX

처음 끊긴 경계로 원인을 좁히기

관측 결과우선 의심할 경계다음 증거
BAR no spacebridge/host window가 작거나 alignment 불가resource tree와 firmware ranges
MMIO all-onesdecode/power/reset/window 문제PCI_COMMAND, D-state, BAR와 bridge window
IOMMU fault잘못된 IOVA, size, direction 또는 lifetimerequester ID와 descriptor DMA address
MSI count 0message routing/table mask/vector 문제MSI-X entry, remapper, irqdomain
interrupt는 오나 완료 없음DMA visibility 또는 device status 해석 문제descriptor ownership, dma barrier, completion index

핵심은 마지막 오류 메시지가 아니라 처음 기대값과 달라진 checkpoint입니다. 그보다 아래의 통과 증거와 그 지점의 실패 증거를 한 묶음으로 남겨야 수정의 효과도 검증할 수 있습니다.