[Linux Kernel Network] PHY Abstraction Layer (PAL)

문연수·3일 전

Linux Kernel Network

목록 보기
3/3
post-thumbnail

0. 서론

 이하의 내용은 리눅스 커널의 phy 문서를 그대로 번역한 것이다. PAL 을 통한 실제 코드 분석은 다음 장에서 진행할 것이다. 1 ~ 4 는 배경 설명이고 실질적인 API 설명은 5 부터 시작된다.

 아마 이해가 안되는 내용도 엄청 많을 것이고 배경도 꽤나 필요한 편이다. 그냥 가볍게 읽고 가도 된다.

앞 장에서 설명한 PHY, MAC, xMII 가 아래의 PAL 인터페이스를 통해 구현된다. 이것만 기억하고 가면 된다. 다음 장에서 실제 드라이버 코드를 보면서 분석할 것이다.

1. PHY Abstraction Layer (PAL) 란?

 대부분의 네트워크 장치들은 MAC 계층과의 통신을 제공하는 레지스터의 집합과 이뤄지며, MAC 계층은 PHY 를 거쳐 실제 물리 계층(케이블/링크)와 통신한다. PHY 는 네트워크 연결의 반대편(보통 이더넷 케이블로 연결된 상대 장치)의 link partner 와 link parameter (speed, duplex, auto-negotiation, etc.)를 협상하는 역할을 한다. 또한 드라이버가 협상 결과로 어떤 설정이 선택되었지 확인하고, 허용할 설정 범위를 구성할 수 있도록 레지스터 인터페이스를 제공한다.

 이러한 장치(PHY)는 네트워크 장치(MAC/컨트롤러)와는 별개이며, 레지스터도 표준화된 레이아웃을 따름에도 불구하고, 관행적으로 PHY 관리 코드를 네트워크 드라이버에 통합해 왔다. 그 결과 중복 코드가 대량으로 생겼다. 또한 하나의 관리 버스에 여러 개(때로는 서로 상당히 다른)의 이더넷 컨트롤러가 연결된 임베디드 시스템에서는, 그 버스를 안전하게 사용하도록 보장하기가 어렵다.

 PHY 는 하나의 장치(device) 이고, PHY에 접근하기 위해 사용하는 관리 버스(management bus) 는 말 그대로 버스(bus) 이므로, PHY Abstraction Layer(PAL) 는 이들을 그대로 "장치와 버스" 로 취급한다. 그렇게 함으로써 PAL은 다음 목표를 가진다:

  • 코드 재사용성 증대
  • 전체 코드 유지보수성 향상
  • 신규 네트워크 드라이버 및 신규 시스템 개발 시간 단축

요약하면,

이 레이어(PAL)의 목적은 PHY 장치에 대한 인터페이스를 제공하여 네트워크 드라이버 작성자가 가능한 한 적은 코드만 작성해도 되게 하면서도, 완전한 기능 세트는 그대로 제공하는 데 있다.

2. MDIO 버스

 대부분의 네트워크 장치는 관리 버스를 통해 PHY에 접근한다. 장치에 따라 사용하는 버스는 서로 다르며(일부는 공통된 인터페이스를 공유하기도 한다). PAL을 활용하려면, 각 버스 인터페이스를 서로 구분되는(독립적인) 장치로 등록해야 한다.

  1. read/write 함수를 반드시 구현해야 하며 프로토타입은 다음과 같다:
    int write(struct mii_bus *bus, int mii_id, int regnum, u16 value);
    int read(struct mii_bus *bus, int mii_id, int regnum);
    mii_id 는 버스 상에서 PHY 의 주소이고, regnum 은 레지스터 번호이다. 위 함수들은 interrupt context 에서 호출되지 않는 것이 보장되므로, 작업 완료를 알리는 인터럽트를 기다리며 블로킹하는 것이 안전하다.
  2. reset 함수는 선택 사항이다. 이는 버스를 초기화된 상태로 되돌리는 데 사용된다.
  3. probe 함수는 필요하다. 이 함수는 버스 드라이버에 필요한 모든 것을 설정하고, mii_bus 구조체를 준비한 뒤, mdiobus_register 를 사용해 PAL 에 등록해야 한다. 마찬가지로 이를 되돌리는 remove 함수도 필요하며 (이때에는 mdiobus_unregister 사용), 설정한 것들을 모두 해제해야 한다.
  4. 다른 드라이버와 마찬가지로, device_driver 구조체를 구성해야 하며, 드라이버를 등록하기 위해 init/exit 함수도 사용해야 한다.
  5. 또한 버스 자체도 어딘가에서 디바이스로 선언되어야 하고, 등록되어야 한다.

추천하는 MDIO 버스 드라이버 구현 예시: drivers/net/ethernet/freescale/fsl_pq_mdio.c

3. (RG)MII/electrical interface considerations

Reduced Gigabit Media Independent Interface(RGMII)는 동기식 125MHz 클록 신호와 여러 데이터 라인을 사용하는 12핀 전기 신호 인터페이스이다. 이러한 설계 결정 때문에, PHY(클록을 수신하는 쪽)가 데이터 라인을 올바르게 샘플링할 수 있을 만큼 충분한 셋업/홀드 타임을 확보하려면 클록 라인(RXC 또는 TXC)과 데이터 라인 사이에 1.5ns~2ns의 지연(delay)을 추가해야 한다.

 PHY 라이브러리는 PHY 드라이버가, 그리고 경우에 따라 MAC 드라이버도 이 지연을 구현할 수 있도록 PHY_INTERFACE_MODE_RGMII 형태의 여러 값을 제공한다. phy_interface_t 의 값은 PHY 디바이스 관점에서 이해해야 하며, 이에 따라 다음과 같은 의미를 갖는다:

  • PHY_INTERFACE_MODE_RGMII: PHY는 자체적으로 어떤 내부 지연도 삽입할 책임이 없으며, Ethernet MAC(가능한 경우) 또는 PCB 트레이스가 올바른 1.5~2ns 지연을 삽입한다고 가정한다.
  • PHY_INTERFACE_MODE_RGMII_TXID: PHY는 PHY 디바이스가 처리하는 송신 데이터 라인(TXD[3:0])에 대해 내부 지연을 삽입해야 한다.
  • PHY_INTERFACE_MODE_RGMII_RXID: PHY는 PHY 디바이스가 처리하는 수신 데이터 라인(RXD[3:0])에 대해 내부 지연을 삽입해야 한다.
  • PHY_INTERFACE_MODE_RGMII_ID: PHY는 PHY 디바이스로부터/로 향하는 송신 및 수신 데이터 라인 모두에 대해 내부 지연을 삽입해야 한다.

가능한 경우에는 다음과 같은 이유로 RGMII 지연을 PHY 쪽에서 제공하는 방식을 사용해야 한다:

  • PHY 디바이스는 수신/송신 측 지연을 설정할 때 서브 나노초 단위의 정밀도(예: 0.5ns, 1.0ns, 1.5ns)를 제공하는 경우가 있습니다. 이런 정밀도는 PCB 트레이스 길이 차이를 보정하기 위해 필요할 수 있다.
  • PHY 디바이스는 보통 산업/의료/자동차 등 매우 넓은 적용 범위를 대상으로 인증(qualification)되며, 온도/압력/전압(T/P/V) 변화 범위 전반에서 일정하고 신뢰할 수 있는 지연을 제공한다.
  • PHYLIB 의 PHY 디바이스 드라이버는 본질적으로 재사용 가능하므로, 지정된 지연을 올바르게 설정할 수 있으면 유사한 지연 요구 사항을 가진 더 많은 설계가 정상 동작할 수 있다.

 PHY 가 이 지연을 제공할 수 없지만 Ethernet MAC 드라이버가 제공할 수 있는 경우에는, 올바른 phy_interface_t 값은 PHY_INTERFACE_MODE_RGMII여야 하며, Ethernet MAC 드라이버는 PHY 디바이스 관점에서 필요한 송신 및/또는 수신 측 지연을 제공하도록 올바르게 설정되야한다. 반대로 Ethernet MAC 드라이버가 phy_interface_t 값을 참고한다면, PHY_INTERFACE_MODE_RGMII 가 아닌 다른 모드에서는 MAC 레벨 지연이 비활성화되어 있음을 보장해야 한다.

Ethernet MAC 도 PHY 도 RGMII 표준에서 요구하는 지연을 제공할 수 없는 경우에는 다음과 같은 선택지가 있을 수 있다:

  • 일부 SoC는 핀 패드/멀티플렉서/컨트롤러를 통해 특정 핀들의 드라이브 강도, 지연, 전압을 설정할 수 있으며, 이를 이용해 기대되는 2ns RGMII 지연을 삽입하는 것이 적절한 옵션이 될 수 있다.
  • PCB 설계를 수정하여 고정 지연(예: 의도적으로 설계된 서펜타인 패턴 사용)을 포함시키는 방법이 있으며, 이 경우 소프트웨어 설정이 전혀 필요 없을 수도 있다.

4. RGMII 지연 불일치로 인해 흔히 발생하는 문제

 Ethernet MAC 과 PHY 사이에 RGMII 지연 불일치가 존재하면, PHY 또는 MAC 이 신호를 샘플링하여 이를 논리적 1 또는 0으로 변환하고 송수신 데이터를 재구성하는 과정에서 클록과 데이터 라인 신호가 불안정해질 가능성이 크다. 그 결과로 다음과 같은 전형적인 증상이 나타난다:

  • 송신 또는 수신이 부분적으로만 동작하며, 빈번하거나 간헐적인 패킷 손실이 관찰됨
  • Ethernet MAC 이 수신된 일부 또는 모든 패킷에 대해 FCS/CRC 오류를 보고하거나, 아예 모두 폐기(discard)해 버림
  • 속도를 10/100Mbps와 같은 낮은 값으로 낮추면 문제가 사라짐 (이 경우에는 셋업/홀드 타임이 충분히 확보되기 때문)

5. Connecting to a PHY

 시스템이 부팅되는 과정 중 어느 시점에서든, 네트워크 드라이버는 PHY 디바이스와 네트워크 디바이스 간의 연결을 설정해야 한다. 이 시점에서는 PHY가 연결된 버스와 PHY 드라이버가 모두 로드되어 있어, 연결을 수행할 준비가 되어 있어야 한다. 이 단계에서 PHY에 연결하는 방식에는 여러 가지가 있다:

  • PAL이 모든 것을 처리하고, 링크 상태가 변경될 때만 네트워크 드라이버를 호출하여 드라이버가 이에 대응하도록 하는 방식
  • PAL이 인터럽트를 제외한 모든 것을 처리하는 방식 (일반적으로 인터럽트 레지스터가 MAC 컨트롤러 쪽에 있는 경우)
  • PAL이 모든 것을 처리하되, 매 초마다 드라이버와 상태를 확인하여
  • PAL보다 네트워크 드라이버가 먼저 변경 사항에 대응할 수 있도록 하는 방식
  • PAL을 단순한 함수 라이브러리로만 사용하고, 네트워크 디바이스가 직접 함수들을 호출하여 상태를 갱신하고 PHY를 설정하는 방식

6. PHY Abstraction Layer(PAL) 에 "전부 맡기기"

 옵션 1을 선택한다면(이상적으로는 모든 드라이버가 가능하길 바라지만, 그렇지 못한 드라이버에도 유용하도록), PHY 연결은 비교적 단순하다.

먼저 링크 상태 변화에 반응하는 함수가 필요하다. 이 함수는 다음 프로토콜을 따른다:

static void adjust_link(struct net_device *dev);

 다음으로, 이 디바이스에 연결된 PHY 의 디바이스 이름을 알아야 한다. 이름은 보통 "0:00" 같은 형태이며, 앞 숫자는 버스 ID, 뒤 숫자는 해당 버스에서의 PHY 주소를 의미한다. 일반적으로 버스가 자신의 ID가 유일하도록 보장할 책임이 있다.

이제 연결은 아래 함수를 호출하면 된다:

phydev = phy_connect(dev, phy_name, &adjust_link, interface);

 여기서 phydev 는 PHY 를 나타내는 struct phy_device 에 대한 포인터이다. phy_connect() 가 성공하면 이 포인터를 반환한다. dev 는 당신의 net_device 포인터이다. 이 호출이 끝나면 PHY 의 소프트웨어 상태 머신이 시작되고 (software state machine), PHY 에 인터럽트가 있다면 해당 인터럽트 등록도 수행된다. phydev 구조체는 현재 상태에 대한 정보로 채워지지만, 이 시점에서는 PHY 가 아직 완전히 동작(operational)하는 상태는 아니다.

 PHY 에 특화된 플래그가 필요하다면, phy_connect() 호출 전에 phydev->dev_flags 에 설정해야 한다. 그러면 하위 PHY 드라이버가 이 플래그를 확인해 특정 동작을 수행할 수 있다. 이는 시스템이 PHY/컨트롤러에 하드웨어 제약을 걸어 두었고, PHY가 그 제약을 알아야 하는 경우에 유용하다.

interface 는 컨트롤러(MAC)와 PHY 사이의 연결 타입을 지정하는 u32 타입이다. 예를 들어 GMII, MII, RGMII, SGMII 등이 있다. 아래 "PHY interface mode" 절을 참고하라. 전체 목록은 include/linux/phy.h 에 있다.

 그 다음에는 phydev->supportedphydev->advertising 에서, 컨트롤러 관점에서 말이 안 되는 값들을 마스킹하여 제거(prune) 해야 한다. 예를 들어 10/100 컨트롤러가 기가비트 지원 PHY에 연결될 수 있으므로, 이 경우 SUPPORTED_1000baseT 같은 비트들은 꺼야 한다. 이 비트필드 정의는 include/linux/ethtool.h 에 있다. 중요한 점은, SUPPORTED_PauseSUPPORTED_AsymPause 비트(아래 설명) 를 제외하고는 비트를 새로 켜지 말아야 한다. 그렇지 않으면 PHY 가 지원하지 않는 상태로 설정될 수 있다.

 마지막으로 컨트롤러가 네트워크 트래픽을 처리할 준비가 되면 phy_start(phydev) 를 호출한다. 이는 PAL 에 "준비 완료"를 알리고, PHY 가 네트워크에 붙도록 설정한다. 만약 네트워크 드라이버의 MAC 인터럽트가 PHY 상태 변화도 함께 처리한다면, phy_start() 호출 전에 phydev->irqPHY_MAC_INTERRUPT 로 설정하고, 네트워크 드라이버에서 phy_mac_interrupt() 를 사용하면 된다. 인터럽트를 쓰고 싶지 않다면 phydev->irqPHY_POLL 로 설정하면 된다. phy_start() 는 (적용 가능하다면) PHY 인터럽트를 활성화하고 phylib 상태 머신을 시작한다.

 네트워크 연결을 끊고 싶을 때(잠깐이라도)에는 phy_stop(phydev) 를 호출한다. 이 함수는 phylib 상태 머신도 멈추고 PHY 인터럽트도 비활성화한다.

7. PHY interface modes

phy_connect() 계열 함수에 전달되는 PHY 인터페이스 모드는 PHY 인터페이스의 초기 동작 모드를 정의한다. 다만 이 값이 항상 고정된다고 보장되진 않는다. 일부 PHY 는 협상 결과에 따라 소프트웨어 개입 없이 인터페이스 모드를 동적으로 변경하기도 한다.

아래에 몇 가지 인터페이스 모드를 설명한다:

  • PHY_INTERFACE_MODE_SMII
    직렬 MII(SMII)로, 125MHz 클록으로 동작하며 100M 및 10M 속도를 지원합니다. 자세한 내용은 다음 문서를 참고하십시오. (https://opencores.org/ocsvn/smii/smii/trunk/doc/SMII.pdf)
  • PHY_INTERFACE_MODE_1000BASEX
    IEEE 802.3 표준 Clause 36에 정의된 1000BASE-X 단일 레인 serdes 링크를 의미합니다.
    링크는 1.25Gbaud 고정 비트레이트로 동작하며 8B/10B 인코딩을 사용해 결과적으로 1Gbps 데이터 속도를 제공합니다. 데이터 스트림에는 16비트 제어 워드가 포함되어 원격 장치와 듀플렉스 및 PAUSE 모드 협상에 사용됩니다. 2.5Gbps 같은 “업클록된” 변형은 포함하지 않습니다(아래 참조).
    -PHY_INTERFACE_MODE_2500BASEX
    802.3 표준의 1000BASE-X를 2.5배 빠른 클록으로 동작시키는 변형으로, 3.125Gbaud 고정 비트레이트를 제공합니다.
  • PHY_INTERFACE_MODE_SGMII: 이하 설명 생략
  • PHY_INTERFACE_MODE_5GBASER
  • PHY_INTERFACE_MODE_10GBASER
  • PHY_INTERFACE_MODE_10GKR
  • PHY_INTERFACE_MODE_25GBASER
  • PHY_INTERFACE_MODE_100BASEX
  • PHY_INTERFACE_MODE_QUSGMII
  • PHY_INTERFACE_MODE_1000BASEKX
  • PHY_INTERFACE_MODE_PSGMII
  • PHY_INTERFACE_MODE_10G_QXGMII
  • PHY_INTERFACE_MODE_MIILITE

8. PAUSE 프레임 / 플로우 컨트롤

  PHY 는 플로우 컨트롤/PAUSE 프레임 생성에 직접 관여하지 않는다. 다만 MII_ADVERTISESUPPORTED_PauseSUPPORTED_AsymPause 비트가 설정되어 링크 파트너에게 Ethernet MAC 컨트롤러가 해당 기능을 지원함을 알리는 역할을 한다.

 PAUSE 프레임의 실제 생성은 Ethernet MAC 드라이버가 담당하므로, 이 드라이버가 advertisement 와 지원 여부를 정확히 반영하도록 위 비트들을 적절히 설정하는 것이 권장된다. 이는 phy_connect() 이전 또는 이후, 혹은 ethtool::set_pauseparam 구현의 결과로 설정할 수 있습니다.

9. Keeping Close Tabs on the PAL

 네트워크 디바이스와 PHY를 정확히 동기화하기 위해, PAL에 내장된 상태 머신이 경우에 따라 약간의 도움이 필요할 수 있다. 이런 경우, PHY에 연결할 때 헬퍼 함수(helper function)를 등록할 수 있으며, 이 함수는 상태 머신이 변경 사항에 반응하기 전에 매 초마다 한 번 호출된다.

이를 위해서는 다음 절차를 수동으로 수행해야 한다:

  1. phy_attach() 를 호출
  2. phy_prepare_link() 를 호출
  3. phy_start_machine() 을 호출하되, 두 번째 인자로 특수 핸들러 함수의 포인터를 전달

 현재 이 기능을 사용하는 예제는 존재하지 않으며, 작성자 또한 이를 사용하는 드라이버를 보유하고 있지 않아 테스트가 제한적이다. 따라서 사용 시 주의(Caveat Emptor) 가 필요하다.

10. Doing it all yourself

 아주 드물긴 하지만, PAL 에 내장된 상태 머신이 PHY 와 네트워크 디바이스 사이의 복잡한 상호작용을 추적하지 못할 가능성이 있다. 그런 경우에는 phy_attach() 만 호출하고, phy_start_machine() 이나 phy_prepare_link() 는 호출하지 않는 방식으로 처리할 수 있다. 이렇게 하면 phydev->state 는 완전히 드라이버가 직접 관리해야 한다. (phy_start()phy_stop() 은 일부 상태 전환을 수행하므로, 이 방식을 사용할 때는 해당 함수들을 피해야 할 수도 있다.)

 상태 머신을 실행하지 않아도 유용한 기능들에 접근할 수 있도록 노력해 왔고, 그 대부분의 함수는 원래 복잡한 상태 머신과 상호작용하지 않던 함수들에서 발전한 것이다. 하지만 역시나, 상태 머신 없이 동작하는 시나리오는 아직 테스트가 거의 되지 않았으므로, 사용자는 주의해야 한다(tryer beware).

  • int phy_read(struct phy_device *phydev, u16 regnum);
  • int phy_write(struct phy_device *phydev, u16 regnum, u16 val);
    단순한 read/write 기본 함수이다. 버스의 read/write 함수 포인터를 호출한다.
  • void phy_print_status(struct phy_device *phydev);
    PHY 상태를 보기 좋게 출력하는 편의 함수이다.
  • void phy_request_interrupt(struct phy_device *phydev);
    PHY 인터럽트에 대한 IRQ 를 요청한다.
  • struct phy_device *phy_attach(struct net_device *dev, const char *phy_id, phy_interface_t interface);
    네트워크 디바이스를 특정 PHY 에 붙인다. 버스 초기화 과정에서 PHY 드라이버를 찾지 못한 경우에는, PHY 를 generic 드라이버에 바인딩한다.
  • int phy_start_aneg(struct phy_device *phydev);
    phydev 구조체 내부 변수들을 이용하여, advertising 을 설정하고 auto-negotiation 을 리셋하여 다시 시작하거나, auto-neg 를 비활성화하고 강제 설정(forced settings)을 구성한다.
  • static inline int phy_read_status(struct phy_device *phydev);
    PHY 의 현재 설정/상태를 읽어 phydev 구조체에 최신 정보로 채워 넣는다.
  • int phy_ethtool_ksettings_set(struct phy_device *phydev, const struct ethtool_link_ksettings *cmd);
    ethtool 설정을 위한 편의 함수들이다.
  • int phy_mii_ioctl(struct phy_device *phydev, struct mii_ioctl_data *mii_data, int cmd);
    MII ioctl 이다. 주의할 점은 BMCR, BMSR, ADVERTISE 등과 같은 레지스터를 이 ioctl 경로로 써버리면 상태 머신을 완전히 망가뜨릴 수 있다는 것이다. 따라서 이 함수는 표준 레지스터가 아닌 것(그리고 재협상을 트리거하지 않는 것)만 쓰는 용도로 제한하는 것이 좋다.

11. PHY 디바이스 드라이버

실제 davicom 드라이버 코드

PHY Abstraction Layer(PAL) 를 사용하면 새로운 PHY 지원을 추가하는 일은 상당히 쉽다. 어떤 경우에는 아무 작업도 필요 없을 수 있다. 하지만 많은 PHY 는 정상 동작을 위해 약간의 보정 작업이 필요하다.

 대상 PHY 에 errata, 특이 동작(quirk), 또는 지원해야 할 특별한 기능이 없다면, 별도의 PHY 드라이버를 추가하지 않고 PAL이 제공하는 Generic PHY Driver 에 맡기는 것이 가장 적절할 수 있다.

 PHY 드라이버를 작성해야 한다면, 가장 먼저 해야 할 일은 해당 PHY 디바이스와 드라이버가 매칭될 수 있도록 만드는 것이다. 이는 버스 초기화 과정에서 디바이스의 UID(레지스터 2, 3에 저장됨) 를 읽고, 각 드라이버의 phy_id 필드와 phy_id_mask 로 AND 연산하여 비교하는 방식으로 이루어진다. 또한 드라이버에는 이름(name) 이 필요하다. 예시는 다음과 같습니다:

static struct phy_driver dm9161_driver = {
        .phy_id         = 0x0181b880,
        .name           = "Davicom DM9161E",
        .phy_id_mask    = 0x0ffffff0,
        ...
};

 다음으로, 해당 PHY 디바이스와 드라이버가 어떤 기능을 지원하는지(속도, 듀플렉스, 자동 협상 등)를 지정해야 한다. 대부분의 PHY 는 PHY_BASIC_FEATURES 를 지원하지만, 다른 기능들은 include/mii.h 를 참고하면 된다.

 각 PHY 드라이버는 struct phy_driver 에 정의된 여러 함수 포인터로 구성되어 있으며, 이는 include/linux/phy.h 에 문서화되어 있다.

 이 중에서 드라이버 코드에서 반드시 구현해야 하는 것은 config_anegread_status 두 개뿐이며, 나머지는 선택 사항이다. 또한 가능하다면, 이 두 함수에 대해 Generic PHY Driver 가 제공하는 구현(genphy_config_aneg, genphy_read_status)을 사용하는 것이 권장된다. 만약 이것이 불가능하다면, 대개는 이 함수들을 호출 전/후에 약간의 처리를 덧붙이는 래퍼(wrapper) 형태로 구현하면 충분하다.

 구체적인 예시는 drivers/net/phy/ 디렉터리의 Marvell, Cicada, Davicom PHY 드라이버를 참고하라 (작성 시점 기준으로 lxtqsemi 드라이버는 테스트되지 않았다).

12. MMD 레지스터 접근

 PHY 의 MMD 레지스터 접근은 기본적으로 PAL 프레임워크가 처리하지만, 필요하다면 특정 PHY 드라이버에서 이를 오버라이드할 수 있다. 예를 들어, IEEE에서 MMD PHY 레지스터 정의를 표준화하기 전에 제조에 들어간 PHY의 경우 이런 처리가 필요할 수 있다.

 대부분의 최신 PHY는 PAL의 공용 MMD 접근 프레임워크를 그대로 사용할 수 있다. 대표적인 활용 예로 EEE(Energy Efficient Ethernet) 지원이 있으며, PAL은 PHY가 IEEE 표준 접근 메커니즘을 지원하는 경우 MMD 레지스터를 통해 EEE 질의 및 설정을 수행한다. 만약 PHY 드라이버에서 이를 오버라이드한다면, PHY 고유의 접근 인터페이스를 사용할 수도 있다. 이 구현 예시는 drivers/net/phy/ 의 Micrel PHY 드라이버를 참고하라.

MMD: MDIO Manageable Device

13. Board Fixups

 플랫폼과 PHY 간의 특정한 상호작용 때문에 특별한 처리가 필요한 경우도 있다. 예를 들어 PHY 의 클록 입력 위치를 바꾸거나, 데이터 경로 지연(latency)을 보정하기 위해 지연을 추가해야 하는 경우가 그렇다. 이를 지원하기 위해 PHY Layer 는 PHY 가 초기화되거나(또는 이후 리셋될 때) 실행될 fixup 을 플랫폼 코드에서 등록할 수 있도록 한다.

PHY Layer 가 PHY를 초기화할 때, 등록된 fixup이 있는지를 다음 두 기준으로 확인한다:

  • UID: PHY 디바이스의 phy_id 필드에 저장된 값
  • 버스 식별자: phydev->dev.bus_id 에 저장된 값

 두 조건이 모두 일치해야 하며, 예외적으로 와일드카드로 사용할 수 있는 상수 PHY_ANY_ID(버스 ID용)PHY_ANY_UID(UID용) 가 제공된다.

 일치하는 fixup 이 발견되면, PHY Layer 는 해당 fixup 에 연결된 run 함수를 호출한다. 이 함수는 관심 대상인 struct phy_device 에 대한 포인터를 인자로 받으며, 해당 PHY 에 대해서만 동작해야 한다.

플랫폼 코드는 다음 함수 중 하나를 사용해 fixup을 등록할 수 있다:

int phy_register_fixup(const char *phy_id,
        u32 phy_uid, u32 phy_uid_mask,
        int (*run)(struct phy_device *));

또는 다음 두 개의 헬퍼 함수를 사용할 수 있다:

int phy_register_fixup_for_uid(u32 phy_uid, u32 phy_uid_mask,
               int (*run)(struct phy_device *));
int phy_register_fixup_for_id(const char *phy_id,
               int (*run)(struct phy_device *));

이 헬퍼 함수들은 두 매칭 조건 중 하나만 설정하고, 나머지 하나는 모두 매칭되도록 설정한다.

phy_register_fixup() 또는 *_for_uid() / *_for_id() 가 모듈 로드 시점에 호출되었다면, 모듈 언로드 시에는 반드시 fixup을 해제하고 할당된 메모리를 정리해야 한다. 이를 위해 언로드 전에 아래 함수 중 하나를 호출해야 한다:

int phy_unregister_fixup(const char *phy_id, u32 phy_uid, u32 phy_uid_mask);
int phy_unregister_fixup_for_uid(u32 phy_uid, u32 phy_uid_mask);
int phy_unregister_fixup_for_id(const char *phy_id);

출처

https://docs.kernel.org/networking/phy.html

profile
2000.11.30

0개의 댓글