Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

Merge branch 'add-support-for-open-alliance-10base-t1x-macphy-serial-interface'

Parthiban Veerasooran says:

====================
Add support for OPEN Alliance 10BASE-T1x MACPHY Serial Interface

This patch series contain the below updates,

- Adds support for OPEN Alliance 10BASE-T1x MACPHY Serial Interface
in the net/ethernet/oa_tc6.c.

Link to the spec:
-----------------
https://opensig.org/download/document/OPEN_Alliance_10BASET1x_MAC-PHY_Serial_Interface_V1.1.pdf

- Adds driver support for Microchip LAN8650/1 Rev.B1 10BASE-T1S MACPHY
Ethernet driver in the net/ethernet/microchip/lan865x/lan865x.c.

Link to the product:
--------------------
https://www.microchip.com/en-us/product/lan8650

Testing Details:
----------------
The driver performance was tested using iperf3 in the below two setups
separately.

Setup 1:
--------
Node 0 - Raspberry Pi 4 with LAN8650 MAC-PHY
Node 1 - Raspberry Pi 4 with EVB-LAN8670-USB USB Stick

Setup 2:
--------
Node 0 - SAMA7G54-EK with LAN8650 MAC-PHY
Node 1 - Raspberry Pi 4 with EVB-LAN8670-USB USB Stick

Achieved maximum of 9.4 Mbps.

Some systems like Raspberry Pi 4 need performance mode enabled to get the
proper clock speed for SPI. Refer below link for more details.

https://github.com/raspberrypi/linux/issues/3381#issuecomment-1144723750
====================

Link: https://patch.msgid.link/20240909082514.262942-1-Parthiban.Veerasooran@microchip.com
Signed-off-by: Jakub Kicinski <kuba@kernel.org>

+2471
+74
Documentation/devicetree/bindings/net/microchip,lan8650.yaml
··· 1 + # SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) 2 + %YAML 1.2 3 + --- 4 + $id: http://devicetree.org/schemas/net/microchip,lan8650.yaml# 5 + $schema: http://devicetree.org/meta-schemas/core.yaml# 6 + 7 + title: Microchip LAN8650/1 10BASE-T1S MACPHY Ethernet Controllers 8 + 9 + maintainers: 10 + - Parthiban Veerasooran <parthiban.veerasooran@microchip.com> 11 + 12 + description: 13 + The LAN8650/1 combines a Media Access Controller (MAC) and an Ethernet 14 + PHY to enable 10BASE‑T1S networks. The Ethernet Media Access Controller 15 + (MAC) module implements a 10 Mbps half duplex Ethernet MAC, compatible 16 + with the IEEE 802.3 standard and a 10BASE-T1S physical layer transceiver 17 + integrated into the LAN8650/1. The communication between the Host and 18 + the MAC-PHY is specified in the OPEN Alliance 10BASE-T1x MACPHY Serial 19 + Interface (TC6). 20 + 21 + allOf: 22 + - $ref: /schemas/net/ethernet-controller.yaml# 23 + - $ref: /schemas/spi/spi-peripheral-props.yaml# 24 + 25 + properties: 26 + compatible: 27 + oneOf: 28 + - const: microchip,lan8650 29 + - items: 30 + - const: microchip,lan8651 31 + - const: microchip,lan8650 32 + 33 + reg: 34 + maxItems: 1 35 + 36 + interrupts: 37 + description: 38 + Interrupt from MAC-PHY asserted in the event of Receive Chunks 39 + Available, Transmit Chunk Credits Available and Extended Status 40 + Event. 41 + maxItems: 1 42 + 43 + spi-max-frequency: 44 + minimum: 15000000 45 + maximum: 25000000 46 + 47 + required: 48 + - compatible 49 + - reg 50 + - interrupts 51 + - spi-max-frequency 52 + 53 + unevaluatedProperties: false 54 + 55 + examples: 56 + - | 57 + #include <dt-bindings/interrupt-controller/irq.h> 58 + #include <dt-bindings/gpio/gpio.h> 59 + 60 + spi { 61 + #address-cells = <1>; 62 + #size-cells = <0>; 63 + 64 + ethernet@0 { 65 + compatible = "microchip,lan8651", "microchip,lan8650"; 66 + reg = <0>; 67 + pinctrl-names = "default"; 68 + pinctrl-0 = <&eth0_pins>; 69 + interrupt-parent = <&gpio>; 70 + interrupts = <6 IRQ_TYPE_EDGE_FALLING>; 71 + local-mac-address = [04 05 06 01 02 03]; 72 + spi-max-frequency = <15000000>; 73 + }; 74 + };
+1
Documentation/networking/index.rst
··· 88 88 nexthop-group-resilient 89 89 nf_conntrack-sysctl 90 90 nf_flowtable 91 + oa-tc6-framework 91 92 openvswitch 92 93 operstates 93 94 packet_mmap
+497
Documentation/networking/oa-tc6-framework.rst
··· 1 + .. SPDX-License-Identifier: GPL-2.0+ 2 + 3 + ========================================================================= 4 + OPEN Alliance 10BASE-T1x MAC-PHY Serial Interface (TC6) Framework Support 5 + ========================================================================= 6 + 7 + Introduction 8 + ------------ 9 + 10 + The IEEE 802.3cg project defines two 10 Mbit/s PHYs operating over a 11 + single pair of conductors. The 10BASE-T1L (Clause 146) is a long reach 12 + PHY supporting full duplex point-to-point operation over 1 km of single 13 + balanced pair of conductors. The 10BASE-T1S (Clause 147) is a short reach 14 + PHY supporting full / half duplex point-to-point operation over 15 m of 15 + single balanced pair of conductors, or half duplex multidrop bus 16 + operation over 25 m of single balanced pair of conductors. 17 + 18 + Furthermore, the IEEE 802.3cg project defines the new Physical Layer 19 + Collision Avoidance (PLCA) Reconciliation Sublayer (Clause 148) meant to 20 + provide improved determinism to the CSMA/CD media access method. PLCA 21 + works in conjunction with the 10BASE-T1S PHY operating in multidrop mode. 22 + 23 + The aforementioned PHYs are intended to cover the low-speed / low-cost 24 + applications in industrial and automotive environment. The large number 25 + of pins (16) required by the MII interface, which is specified by the 26 + IEEE 802.3 in Clause 22, is one of the major cost factors that need to be 27 + addressed to fulfil this objective. 28 + 29 + The MAC-PHY solution integrates an IEEE Clause 4 MAC and a 10BASE-T1x PHY 30 + exposing a low pin count Serial Peripheral Interface (SPI) to the host 31 + microcontroller. This also enables the addition of Ethernet functionality 32 + to existing low-end microcontrollers which do not integrate a MAC 33 + controller. 34 + 35 + Overview 36 + -------- 37 + 38 + The MAC-PHY is specified to carry both data (Ethernet frames) and control 39 + (register access) transactions over a single full-duplex serial peripheral 40 + interface. 41 + 42 + Protocol Overview 43 + ----------------- 44 + 45 + Two types of transactions are defined in the protocol: data transactions 46 + for Ethernet frame transfers and control transactions for register 47 + read/write transfers. A chunk is the basic element of data transactions 48 + and is composed of 4 bytes of overhead plus 64 bytes of payload size for 49 + each chunk. Ethernet frames are transferred over one or more data chunks. 50 + Control transactions consist of one or more register read/write control 51 + commands. 52 + 53 + SPI transactions are initiated by the SPI host with the assertion of CSn 54 + low to the MAC-PHY and ends with the deassertion of CSn high. In between 55 + each SPI transaction, the SPI host may need time for additional 56 + processing and to setup the next SPI data or control transaction. 57 + 58 + SPI data transactions consist of an equal number of transmit (TX) and 59 + receive (RX) chunks. Chunks in both transmit and receive directions may 60 + or may not contain valid frame data independent from each other, allowing 61 + for the simultaneous transmission and reception of different length 62 + frames. 63 + 64 + Each transmit data chunk begins with a 32-bit data header followed by a 65 + data chunk payload on MOSI. The data header indicates whether transmit 66 + frame data is present and provides the information to determine which 67 + bytes of the payload contain valid frame data. 68 + 69 + In parallel, receive data chunks are received on MISO. Each receive data 70 + chunk consists of a data chunk payload ending with a 32-bit data footer. 71 + The data footer indicates if there is receive frame data present within 72 + the payload or not and provides the information to determine which bytes 73 + of the payload contain valid frame data. 74 + 75 + Reference 76 + --------- 77 + 78 + 10BASE-T1x MAC-PHY Serial Interface Specification, 79 + 80 + Link: https://opensig.org/download/document/OPEN_Alliance_10BASET1x_MAC-PHY_Serial_Interface_V1.1.pdf 81 + 82 + Hardware Architecture 83 + --------------------- 84 + 85 + .. code-block:: none 86 + 87 + +----------+ +-------------------------------------+ 88 + | | | MAC-PHY | 89 + | |<---->| +-----------+ +-------+ +-------+ | 90 + | SPI Host | | | SPI Slave | | MAC | | PHY | | 91 + | | | +-----------+ +-------+ +-------+ | 92 + +----------+ +-------------------------------------+ 93 + 94 + Software Architecture 95 + --------------------- 96 + 97 + .. code-block:: none 98 + 99 + +----------------------------------------------------------+ 100 + | Networking Subsystem | 101 + +----------------------------------------------------------+ 102 + / \ / \ 103 + | | 104 + | | 105 + \ / | 106 + +----------------------+ +-----------------------------+ 107 + | MAC Driver |<--->| OPEN Alliance TC6 Framework | 108 + +----------------------+ +-----------------------------+ 109 + / \ / \ 110 + | | 111 + | | 112 + | \ / 113 + +----------------------------------------------------------+ 114 + | SPI Subsystem | 115 + +----------------------------------------------------------+ 116 + / \ 117 + | 118 + | 119 + \ / 120 + +----------------------------------------------------------+ 121 + | 10BASE-T1x MAC-PHY Device | 122 + +----------------------------------------------------------+ 123 + 124 + Implementation 125 + -------------- 126 + 127 + MAC Driver 128 + ~~~~~~~~~~ 129 + 130 + - Probed by SPI subsystem. 131 + 132 + - Initializes OA TC6 framework for the MAC-PHY. 133 + 134 + - Registers and configures the network device. 135 + 136 + - Sends the tx ethernet frames from n/w subsystem to OA TC6 framework. 137 + 138 + OPEN Alliance TC6 Framework 139 + ~~~~~~~~~~~~~~~~~~~~~~~~~~~ 140 + 141 + - Initializes PHYLIB interface. 142 + 143 + - Registers mac-phy interrupt. 144 + 145 + - Performs mac-phy register read/write operation using the control 146 + transaction protocol specified in the OPEN Alliance 10BASE-T1x MAC-PHY 147 + Serial Interface specification. 148 + 149 + - Performs Ethernet frames transaction using the data transaction protocol 150 + for Ethernet frames specified in the OPEN Alliance 10BASE-T1x MAC-PHY 151 + Serial Interface specification. 152 + 153 + - Forwards the received Ethernet frame from 10Base-T1x MAC-PHY to n/w 154 + subsystem. 155 + 156 + Data Transaction 157 + ~~~~~~~~~~~~~~~~ 158 + 159 + The Ethernet frames that are typically transferred from the SPI host to 160 + the MAC-PHY will be converted into multiple transmit data chunks. Each 161 + transmit data chunk will have a 4 bytes header which contains the 162 + information needed to determine the validity and the location of the 163 + transmit frame data within the 64 bytes data chunk payload. 164 + 165 + .. code-block:: none 166 + 167 + +---------------------------------------------------+ 168 + | Tx Chunk | 169 + | +---------------------------+ +----------------+ | MOSI 170 + | | 64 bytes chunk payload | | 4 bytes header | |------------> 171 + | +---------------------------+ +----------------+ | 172 + +---------------------------------------------------+ 173 + 174 + 4 bytes header contains the below fields, 175 + 176 + DNC (Bit 31) - Data-Not-Control flag. This flag specifies the type of SPI 177 + transaction. For TX data chunks, this bit shall be ’1’. 178 + 0 - Control command 179 + 1 - Data chunk 180 + 181 + SEQ (Bit 30) - Data Chunk Sequence. This bit is used to indicate an 182 + even/odd transmit data chunk sequence to the MAC-PHY. 183 + 184 + NORX (Bit 29) - No Receive flag. The SPI host may set this bit to prevent 185 + the MAC-PHY from conveying RX data on the MISO for the 186 + current chunk (DV = 0 in the footer), indicating that the 187 + host would not process it. Typically, the SPI host should 188 + set NORX = 0 indicating that it will accept and process 189 + any receive frame data within the current chunk. 190 + 191 + RSVD (Bit 28..24) - Reserved: All reserved bits shall be ‘0’. 192 + 193 + VS (Bit 23..22) - Vendor Specific. These bits are implementation specific. 194 + If the MAC-PHY does not implement these bits, the host 195 + shall set them to ‘0’. 196 + 197 + DV (Bit 21) - Data Valid flag. The SPI host uses this bit to indicate 198 + whether the current chunk contains valid transmit frame data 199 + (DV = 1) or not (DV = 0). When ‘0’, the MAC-PHY ignores the 200 + chunk payload. Note that the receive path is unaffected by 201 + the setting of the DV bit in the data header. 202 + 203 + SV (Bit 20) - Start Valid flag. The SPI host shall set this bit when the 204 + beginning of an Ethernet frame is present in the current 205 + transmit data chunk payload. Otherwise, this bit shall be 206 + zero. This bit is not to be confused with the Start-of-Frame 207 + Delimiter (SFD) byte described in IEEE 802.3 [2]. 208 + 209 + SWO (Bit 19..16) - Start Word Offset. When SV = 1, this field shall 210 + contain the 32-bit word offset into the transmit data 211 + chunk payload that points to the start of a new 212 + Ethernet frame to be transmitted. The host shall write 213 + this field as zero when SV = 0. 214 + 215 + RSVD (Bit 15) - Reserved: All reserved bits shall be ‘0’. 216 + 217 + EV (Bit 14) - End Valid flag. The SPI host shall set this bit when the end 218 + of an Ethernet frame is present in the current transmit data 219 + chunk payload. Otherwise, this bit shall be zero. 220 + 221 + EBO (Bit 13..8) - End Byte Offset. When EV = 1, this field shall contain 222 + the byte offset into the transmit data chunk payload 223 + that points to the last byte of the Ethernet frame to 224 + transmit. This field shall be zero when EV = 0. 225 + 226 + TSC (Bit 7..6) - Timestamp Capture. Request a timestamp capture when the 227 + frame is transmitted onto the network. 228 + 00 - Do not capture a timestamp 229 + 01 - Capture timestamp into timestamp capture register A 230 + 10 - Capture timestamp into timestamp capture register B 231 + 11 - Capture timestamp into timestamp capture register C 232 + 233 + RSVD (Bit 5..1) - Reserved: All reserved bits shall be ‘0’. 234 + 235 + P (Bit 0) - Parity. Parity bit calculated over the transmit data header. 236 + Method used is odd parity. 237 + 238 + The number of buffers available in the MAC-PHY to store the incoming 239 + transmit data chunk payloads is represented as transmit credits. The 240 + available transmit credits in the MAC-PHY can be read either from the 241 + Buffer Status Register or footer (Refer below for the footer info) 242 + received from the MAC-PHY. The SPI host should not write more data chunks 243 + than the available transmit credits as this will lead to transmit buffer 244 + overflow error. 245 + 246 + In case the previous data footer had no transmit credits available and 247 + once the transmit credits become available for transmitting transmit data 248 + chunks, the MAC-PHY interrupt is asserted to SPI host. On reception of the 249 + first data header this interrupt will be deasserted and the received 250 + footer for the first data chunk will have the transmit credits available 251 + information. 252 + 253 + The Ethernet frames that are typically transferred from MAC-PHY to SPI 254 + host will be sent as multiple receive data chunks. Each receive data 255 + chunk will have 64 bytes of data chunk payload followed by 4 bytes footer 256 + which contains the information needed to determine the validity and the 257 + location of the receive frame data within the 64 bytes data chunk payload. 258 + 259 + .. code-block:: none 260 + 261 + +---------------------------------------------------+ 262 + | Rx Chunk | 263 + | +----------------+ +---------------------------+ | MISO 264 + | | 4 bytes footer | | 64 bytes chunk payload | |------------> 265 + | +----------------+ +---------------------------+ | 266 + +---------------------------------------------------+ 267 + 268 + 4 bytes footer contains the below fields, 269 + 270 + EXST (Bit 31) - Extended Status. This bit is set when any bit in the 271 + STATUS0 or STATUS1 registers are set and not masked. 272 + 273 + HDRB (Bit 30) - Received Header Bad. When set, indicates that the MAC-PHY 274 + received a control or data header with a parity error. 275 + 276 + SYNC (Bit 29) - Configuration Synchronized flag. This bit reflects the 277 + state of the SYNC bit in the CONFIG0 configuration 278 + register (see Table 12). A zero indicates that the MAC-PHY 279 + configuration may not be as expected by the SPI host. 280 + Following configuration, the SPI host sets the 281 + corresponding bitin the configuration register which is 282 + reflected in this field. 283 + 284 + RCA (Bit 28..24) - Receive Chunks Available. The RCA field indicates to 285 + the SPI host the minimum number of additional receive 286 + data chunks of frame data that are available for 287 + reading beyond the current receive data chunk. This 288 + field is zero when there is no receive frame data 289 + pending in the MAC-PHY’s buffer for reading. 290 + 291 + VS (Bit 23..22) - Vendor Specific. These bits are implementation specific. 292 + If not implemented, the MAC-PHY shall set these bits to 293 + ‘0’. 294 + 295 + DV (Bit 21) - Data Valid flag. The MAC-PHY uses this bit to indicate 296 + whether the current receive data chunk contains valid 297 + receive frame data (DV = 1) or not (DV = 0). When ‘0’, the 298 + SPI host shall ignore the chunk payload. 299 + 300 + SV (Bit 20) - Start Valid flag. The MAC-PHY sets this bit when the current 301 + chunk payload contains the start of an Ethernet frame. 302 + Otherwise, this bit is zero. The SV bit is not to be 303 + confused with the Start-of-Frame Delimiter (SFD) byte 304 + described in IEEE 802.3 [2]. 305 + 306 + SWO (Bit 19..16) - Start Word Offset. When SV = 1, this field contains the 307 + 32-bit word offset into the receive data chunk payload 308 + containing the first byte of a new received Ethernet 309 + frame. When a receive timestamp has been added to the 310 + beginning of the received Ethernet frame (RTSA = 1) 311 + then SWO points to the most significant byte of the 312 + timestamp. This field will be zero when SV = 0. 313 + 314 + FD (Bit 15) - Frame Drop. When set, this bit indicates that the MAC has 315 + detected a condition for which the SPI host should drop the 316 + received Ethernet frame. This bit is only valid at the end 317 + of a received Ethernet frame (EV = 1) and shall be zero at 318 + all other times. 319 + 320 + EV (Bit 14) - End Valid flag. The MAC-PHY sets this bit when the end of a 321 + received Ethernet frame is present in this receive data 322 + chunk payload. 323 + 324 + EBO (Bit 13..8) - End Byte Offset: When EV = 1, this field contains the 325 + byte offset into the receive data chunk payload that 326 + locates the last byte of the received Ethernet frame. 327 + This field is zero when EV = 0. 328 + 329 + RTSA (Bit 7) - Receive Timestamp Added. This bit is set when a 32-bit or 330 + 64-bit timestamp has been added to the beginning of the 331 + received Ethernet frame. The MAC-PHY shall set this bit to 332 + zero when SV = 0. 333 + 334 + RTSP (Bit 6) - Receive Timestamp Parity. Parity bit calculated over the 335 + 32-bit/64-bit timestamp added to the beginning of the 336 + received Ethernet frame. Method used is odd parity. The 337 + MAC-PHY shall set this bit to zero when RTSA = 0. 338 + 339 + TXC (Bit 5..1) - Transmit Credits. This field contains the minimum number 340 + of transmit data chunks of frame data that the SPI host 341 + can write in a single transaction without incurring a 342 + transmit buffer overflow error. 343 + 344 + P (Bit 0) - Parity. Parity bit calculated over the receive data footer. 345 + Method used is odd parity. 346 + 347 + SPI host will initiate the data receive transaction based on the receive 348 + chunks available in the MAC-PHY which is provided in the receive chunk 349 + footer (RCA - Receive Chunks Available). SPI host will create data invalid 350 + transmit data chunks (empty chunks) or data valid transmit data chunks in 351 + case there are valid Ethernet frames to transmit to the MAC-PHY. The 352 + receive chunks available in MAC-PHY can be read either from the Buffer 353 + Status Register or footer. 354 + 355 + In case the previous data footer had no receive data chunks available and 356 + once the receive data chunks become available again for reading, the 357 + MAC-PHY interrupt is asserted to SPI host. On reception of the first data 358 + header this interrupt will be deasserted and the received footer for the 359 + first data chunk will have the receive chunks available information. 360 + 361 + MAC-PHY Interrupt 362 + ~~~~~~~~~~~~~~~~~ 363 + 364 + The MAC-PHY interrupt is asserted when the following conditions are met. 365 + 366 + Receive chunks available - This interrupt is asserted when the previous 367 + data footer had no receive data chunks available and once the receive 368 + data chunks become available for reading. On reception of the first data 369 + header this interrupt will be deasserted. 370 + 371 + Transmit chunk credits available - This interrupt is asserted when the 372 + previous data footer indicated no transmit credits available and once the 373 + transmit credits become available for transmitting transmit data chunks. 374 + On reception of the first data header this interrupt will be deasserted. 375 + 376 + Extended status event - This interrupt is asserted when the previous data 377 + footer indicated no extended status and once the extended event become 378 + available. In this case the host should read status #0 register to know 379 + the corresponding error/event. On reception of the first data header this 380 + interrupt will be deasserted. 381 + 382 + Control Transaction 383 + ~~~~~~~~~~~~~~~~~~~ 384 + 385 + 4 bytes control header contains the below fields, 386 + 387 + DNC (Bit 31) - Data-Not-Control flag. This flag specifies the type of SPI 388 + transaction. For control commands, this bit shall be ‘0’. 389 + 0 - Control command 390 + 1 - Data chunk 391 + 392 + HDRB (Bit 30) - Received Header Bad. When set by the MAC-PHY, indicates 393 + that a header was received with a parity error. The SPI 394 + host should always clear this bit. The MAC-PHY ignores the 395 + HDRB value sent by the SPI host on MOSI. 396 + 397 + WNR (Bit 29) - Write-Not-Read. This bit indicates if data is to be written 398 + to registers (when set) or read from registers 399 + (when clear). 400 + 401 + AID (Bit 28) - Address Increment Disable. When clear, the address will be 402 + automatically post-incremented by one following each 403 + register read or write. When set, address auto increment is 404 + disabled allowing successive reads and writes to occur at 405 + the same register address. 406 + 407 + MMS (Bit 27..24) - Memory Map Selector. This field selects the specific 408 + register memory map to access. 409 + 410 + ADDR (Bit 23..8) - Address. Address of the first register within the 411 + selected memory map to access. 412 + 413 + LEN (Bit 7..1) - Length. Specifies the number of registers to read/write. 414 + This field is interpreted as the number of registers 415 + minus 1 allowing for up to 128 consecutive registers read 416 + or written starting at the address specified in ADDR. A 417 + length of zero shall read or write a single register. 418 + 419 + P (Bit 0) - Parity. Parity bit calculated over the control command header. 420 + Method used is odd parity. 421 + 422 + Control transactions consist of one or more control commands. Control 423 + commands are used by the SPI host to read and write registers within the 424 + MAC-PHY. Each control commands are composed of a 4 bytes control command 425 + header followed by register write data in case of control write command. 426 + 427 + The MAC-PHY ignores the final 4 bytes of data from the SPI host at the end 428 + of the control write command. The control write command is also echoed 429 + from the MAC-PHY back to the SPI host to identify which register write 430 + failed in case of any bus errors. The echoed Control write command will 431 + have the first 4 bytes unused value to be ignored by the SPI host 432 + followed by 4 bytes echoed control header followed by echoed register 433 + write data. Control write commands can write either a single register or 434 + multiple consecutive registers. When multiple consecutive registers are 435 + written, the address is automatically post-incremented by the MAC-PHY. 436 + Writing to any unimplemented or undefined registers shall be ignored and 437 + yield no effect. 438 + 439 + The MAC-PHY ignores all data from the SPI host following the control 440 + header for the remainder of the control read command. The control read 441 + command is also echoed from the MAC-PHY back to the SPI host to identify 442 + which register read is failed in case of any bus errors. The echoed 443 + Control read command will have the first 4 bytes of unused value to be 444 + ignored by the SPI host followed by 4 bytes echoed control header followed 445 + by register read data. Control read commands can read either a single 446 + register or multiple consecutive registers. When multiple consecutive 447 + registers are read, the address is automatically post-incremented by the 448 + MAC-PHY. Reading any unimplemented or undefined registers shall return 449 + zero. 450 + 451 + Device drivers API 452 + ================== 453 + 454 + The include/linux/oa_tc6.h defines the following functions: 455 + 456 + .. c:function:: struct oa_tc6 *oa_tc6_init(struct spi_device *spi, \ 457 + struct net_device *netdev) 458 + 459 + Initialize OA TC6 lib. 460 + 461 + .. c:function:: void oa_tc6_exit(struct oa_tc6 *tc6) 462 + 463 + Free allocated OA TC6 lib. 464 + 465 + .. c:function:: int oa_tc6_write_register(struct oa_tc6 *tc6, u32 address, \ 466 + u32 value) 467 + 468 + Write a single register in the MAC-PHY. 469 + 470 + .. c:function:: int oa_tc6_write_registers(struct oa_tc6 *tc6, u32 address, \ 471 + u32 value[], u8 length) 472 + 473 + Writing multiple consecutive registers starting from @address in the MAC-PHY. 474 + Maximum of 128 consecutive registers can be written starting at @address. 475 + 476 + .. c:function:: int oa_tc6_read_register(struct oa_tc6 *tc6, u32 address, \ 477 + u32 *value) 478 + 479 + Read a single register in the MAC-PHY. 480 + 481 + .. c:function:: int oa_tc6_read_registers(struct oa_tc6 *tc6, u32 address, \ 482 + u32 value[], u8 length) 483 + 484 + Reading multiple consecutive registers starting from @address in the MAC-PHY. 485 + Maximum of 128 consecutive registers can be read starting at @address. 486 + 487 + .. c:function:: netdev_tx_t oa_tc6_start_xmit(struct oa_tc6 *tc6, \ 488 + struct sk_buff *skb); 489 + 490 + The transmit Ethernet frame in the skb is or going to be transmitted through 491 + the MAC-PHY. 492 + 493 + .. c:function:: int oa_tc6_zero_align_receive_frame_enable(struct oa_tc6 *tc6); 494 + 495 + Zero align receive frame feature can be enabled to align all receive ethernet 496 + frames data to start at the beginning of any receive data chunk payload with a 497 + start word offset (SWO) of zero.
+15
MAINTAINERS
··· 14980 14980 S: Maintained 14981 14981 F: drivers/net/ethernet/microchip/lan743x_* 14982 14982 14983 + MICROCHIP LAN8650/1 10BASE-T1S MACPHY ETHERNET DRIVER 14984 + M: Parthiban Veerasooran <parthiban.veerasooran@microchip.com> 14985 + L: netdev@vger.kernel.org 14986 + S: Maintained 14987 + F: Documentation/devicetree/bindings/net/microchip,lan8650.yaml 14988 + F: drivers/net/ethernet/microchip/lan865x/lan865x.c 14989 + 14983 14990 MICROCHIP LAN87xx/LAN937x T1 PHY DRIVER 14984 14991 M: Arun Ramadoss <arun.ramadoss@microchip.com> 14985 14992 R: UNGLinuxDriver@microchip.com ··· 17119 17112 L: linux-rdma@vger.kernel.org 17120 17113 S: Supported 17121 17114 F: drivers/infiniband/ulp/opa_vnic 17115 + 17116 + OPEN ALLIANCE 10BASE-T1S MACPHY SERIAL INTERFACE FRAMEWORK 17117 + M: Parthiban Veerasooran <parthiban.veerasooran@microchip.com> 17118 + L: netdev@vger.kernel.org 17119 + S: Maintained 17120 + F: Documentation/networking/oa-tc6-framework.rst 17121 + F: drivers/include/linux/oa_tc6.h 17122 + F: drivers/net/ethernet/oa_tc6.c 17122 17123 17123 17124 OPEN FIRMWARE AND FLATTENED DEVICE TREE 17124 17125 M: Rob Herring <robh@kernel.org>
+11
drivers/net/ethernet/Kconfig
··· 158 158 help 159 159 Say Y here if you want to use the OpenCores 10/100 Mbps Ethernet MAC. 160 160 161 + config OA_TC6 162 + tristate "OPEN Alliance TC6 10BASE-T1x MAC-PHY support" 163 + depends on SPI 164 + select PHYLIB 165 + help 166 + This library implements OPEN Alliance TC6 10BASE-T1x MAC-PHY 167 + Serial Interface protocol for supporting 10BASE-T1x MAC-PHYs. 168 + 169 + To know the implementation details, refer documentation in 170 + <file:Documentation/networking/oa-tc6-framework.rst>. 171 + 161 172 source "drivers/net/ethernet/packetengines/Kconfig" 162 173 source "drivers/net/ethernet/pasemi/Kconfig" 163 174 source "drivers/net/ethernet/pensando/Kconfig"
+1
drivers/net/ethernet/Makefile
··· 105 105 obj-$(CONFIG_NET_VENDOR_XIRCOM) += xircom/ 106 106 obj-$(CONFIG_NET_VENDOR_SYNOPSYS) += synopsys/ 107 107 obj-$(CONFIG_NET_VENDOR_PENSANDO) += pensando/ 108 + obj-$(CONFIG_OA_TC6) += oa_tc6.o
+1
drivers/net/ethernet/microchip/Kconfig
··· 57 57 To compile this driver as a module, choose M here. The module will be 58 58 called lan743x. 59 59 60 + source "drivers/net/ethernet/microchip/lan865x/Kconfig" 60 61 source "drivers/net/ethernet/microchip/lan966x/Kconfig" 61 62 source "drivers/net/ethernet/microchip/sparx5/Kconfig" 62 63 source "drivers/net/ethernet/microchip/vcap/Kconfig"
+1
drivers/net/ethernet/microchip/Makefile
··· 9 9 10 10 lan743x-objs := lan743x_main.o lan743x_ethtool.o lan743x_ptp.o 11 11 12 + obj-$(CONFIG_LAN865X) += lan865x/ 12 13 obj-$(CONFIG_LAN966X_SWITCH) += lan966x/ 13 14 obj-$(CONFIG_SPARX5_SWITCH) += sparx5/ 14 15 obj-$(CONFIG_VCAP) += vcap/
+19
drivers/net/ethernet/microchip/lan865x/Kconfig
··· 1 + # SPDX-License-Identifier: GPL-2.0-only 2 + # 3 + # Microchip LAN865x Driver Support 4 + # 5 + 6 + if NET_VENDOR_MICROCHIP 7 + 8 + config LAN865X 9 + tristate "LAN865x support" 10 + depends on SPI 11 + select OA_TC6 12 + help 13 + Support for the Microchip LAN8650/1 Rev.B0/B1 MACPHY Ethernet chip. It 14 + uses OPEN Alliance 10BASE-T1x Serial Interface specification. 15 + 16 + To compile this driver as a module, choose M here. The module will be 17 + called lan865x. 18 + 19 + endif # NET_VENDOR_MICROCHIP
+6
drivers/net/ethernet/microchip/lan865x/Makefile
··· 1 + # SPDX-License-Identifier: GPL-2.0-only 2 + # 3 + # Makefile for the Microchip LAN865x Driver 4 + # 5 + 6 + obj-$(CONFIG_LAN865X) += lan865x.o
+429
drivers/net/ethernet/microchip/lan865x/lan865x.c
··· 1 + // SPDX-License-Identifier: GPL-2.0+ 2 + /* 3 + * Microchip's LAN865x 10BASE-T1S MAC-PHY driver 4 + * 5 + * Author: Parthiban Veerasooran <parthiban.veerasooran@microchip.com> 6 + */ 7 + 8 + #include <linux/module.h> 9 + #include <linux/kernel.h> 10 + #include <linux/phy.h> 11 + #include <linux/oa_tc6.h> 12 + 13 + #define DRV_NAME "lan8650" 14 + 15 + /* MAC Network Control Register */ 16 + #define LAN865X_REG_MAC_NET_CTL 0x00010000 17 + #define MAC_NET_CTL_TXEN BIT(3) /* Transmit Enable */ 18 + #define MAC_NET_CTL_RXEN BIT(2) /* Receive Enable */ 19 + 20 + /* MAC Network Configuration Reg */ 21 + #define LAN865X_REG_MAC_NET_CFG 0x00010001 22 + #define MAC_NET_CFG_PROMISCUOUS_MODE BIT(4) 23 + #define MAC_NET_CFG_MULTICAST_MODE BIT(6) 24 + #define MAC_NET_CFG_UNICAST_MODE BIT(7) 25 + 26 + /* MAC Hash Register Bottom */ 27 + #define LAN865X_REG_MAC_L_HASH 0x00010020 28 + /* MAC Hash Register Top */ 29 + #define LAN865X_REG_MAC_H_HASH 0x00010021 30 + /* MAC Specific Addr 1 Bottom Reg */ 31 + #define LAN865X_REG_MAC_L_SADDR1 0x00010022 32 + /* MAC Specific Addr 1 Top Reg */ 33 + #define LAN865X_REG_MAC_H_SADDR1 0x00010023 34 + 35 + struct lan865x_priv { 36 + struct work_struct multicast_work; 37 + struct net_device *netdev; 38 + struct spi_device *spi; 39 + struct oa_tc6 *tc6; 40 + }; 41 + 42 + static int lan865x_set_hw_macaddr_low_bytes(struct oa_tc6 *tc6, const u8 *mac) 43 + { 44 + u32 regval; 45 + 46 + regval = (mac[3] << 24) | (mac[2] << 16) | (mac[1] << 8) | mac[0]; 47 + 48 + return oa_tc6_write_register(tc6, LAN865X_REG_MAC_L_SADDR1, regval); 49 + } 50 + 51 + static int lan865x_set_hw_macaddr(struct lan865x_priv *priv, const u8 *mac) 52 + { 53 + int restore_ret; 54 + u32 regval; 55 + int ret; 56 + 57 + /* Configure MAC address low bytes */ 58 + ret = lan865x_set_hw_macaddr_low_bytes(priv->tc6, mac); 59 + if (ret) 60 + return ret; 61 + 62 + /* Prepare and configure MAC address high bytes */ 63 + regval = (mac[5] << 8) | mac[4]; 64 + ret = oa_tc6_write_register(priv->tc6, LAN865X_REG_MAC_H_SADDR1, 65 + regval); 66 + if (!ret) 67 + return 0; 68 + 69 + /* Restore the old MAC address low bytes from netdev if the new MAC 70 + * address high bytes setting failed. 71 + */ 72 + restore_ret = lan865x_set_hw_macaddr_low_bytes(priv->tc6, 73 + priv->netdev->dev_addr); 74 + if (restore_ret) 75 + return restore_ret; 76 + 77 + return ret; 78 + } 79 + 80 + static const struct ethtool_ops lan865x_ethtool_ops = { 81 + .get_link_ksettings = phy_ethtool_get_link_ksettings, 82 + .set_link_ksettings = phy_ethtool_set_link_ksettings, 83 + }; 84 + 85 + static int lan865x_set_mac_address(struct net_device *netdev, void *addr) 86 + { 87 + struct lan865x_priv *priv = netdev_priv(netdev); 88 + struct sockaddr *address = addr; 89 + int ret; 90 + 91 + ret = eth_prepare_mac_addr_change(netdev, addr); 92 + if (ret < 0) 93 + return ret; 94 + 95 + if (ether_addr_equal(address->sa_data, netdev->dev_addr)) 96 + return 0; 97 + 98 + ret = lan865x_set_hw_macaddr(priv, address->sa_data); 99 + if (ret) 100 + return ret; 101 + 102 + eth_commit_mac_addr_change(netdev, addr); 103 + 104 + return 0; 105 + } 106 + 107 + static u32 get_address_bit(u8 addr[ETH_ALEN], u32 bit) 108 + { 109 + return ((addr[bit / 8]) >> (bit % 8)) & 1; 110 + } 111 + 112 + static u32 lan865x_hash(u8 addr[ETH_ALEN]) 113 + { 114 + u32 hash_index = 0; 115 + 116 + for (int i = 0; i < 6; i++) { 117 + u32 hash = 0; 118 + 119 + for (int j = 0; j < 8; j++) 120 + hash ^= get_address_bit(addr, (j * 6) + i); 121 + 122 + hash_index |= (hash << i); 123 + } 124 + 125 + return hash_index; 126 + } 127 + 128 + static int lan865x_set_specific_multicast_addr(struct lan865x_priv *priv) 129 + { 130 + struct netdev_hw_addr *ha; 131 + u32 hash_lo = 0; 132 + u32 hash_hi = 0; 133 + int ret; 134 + 135 + netdev_for_each_mc_addr(ha, priv->netdev) { 136 + u32 bit_num = lan865x_hash(ha->addr); 137 + 138 + if (bit_num >= BIT(5)) 139 + hash_hi |= (1 << (bit_num - BIT(5))); 140 + else 141 + hash_lo |= (1 << bit_num); 142 + } 143 + 144 + /* Enabling specific multicast addresses */ 145 + ret = oa_tc6_write_register(priv->tc6, LAN865X_REG_MAC_H_HASH, hash_hi); 146 + if (ret) { 147 + netdev_err(priv->netdev, "Failed to write reg_hashh: %d\n", 148 + ret); 149 + return ret; 150 + } 151 + 152 + ret = oa_tc6_write_register(priv->tc6, LAN865X_REG_MAC_L_HASH, hash_lo); 153 + if (ret) 154 + netdev_err(priv->netdev, "Failed to write reg_hashl: %d\n", 155 + ret); 156 + 157 + return ret; 158 + } 159 + 160 + static int lan865x_set_all_multicast_addr(struct lan865x_priv *priv) 161 + { 162 + int ret; 163 + 164 + /* Enabling all multicast addresses */ 165 + ret = oa_tc6_write_register(priv->tc6, LAN865X_REG_MAC_H_HASH, 166 + 0xffffffff); 167 + if (ret) { 168 + netdev_err(priv->netdev, "Failed to write reg_hashh: %d\n", 169 + ret); 170 + return ret; 171 + } 172 + 173 + ret = oa_tc6_write_register(priv->tc6, LAN865X_REG_MAC_L_HASH, 174 + 0xffffffff); 175 + if (ret) 176 + netdev_err(priv->netdev, "Failed to write reg_hashl: %d\n", 177 + ret); 178 + 179 + return ret; 180 + } 181 + 182 + static int lan865x_clear_all_multicast_addr(struct lan865x_priv *priv) 183 + { 184 + int ret; 185 + 186 + ret = oa_tc6_write_register(priv->tc6, LAN865X_REG_MAC_H_HASH, 0); 187 + if (ret) { 188 + netdev_err(priv->netdev, "Failed to write reg_hashh: %d\n", 189 + ret); 190 + return ret; 191 + } 192 + 193 + ret = oa_tc6_write_register(priv->tc6, LAN865X_REG_MAC_L_HASH, 0); 194 + if (ret) 195 + netdev_err(priv->netdev, "Failed to write reg_hashl: %d\n", 196 + ret); 197 + 198 + return ret; 199 + } 200 + 201 + static void lan865x_multicast_work_handler(struct work_struct *work) 202 + { 203 + struct lan865x_priv *priv = container_of(work, struct lan865x_priv, 204 + multicast_work); 205 + u32 regval = 0; 206 + int ret; 207 + 208 + if (priv->netdev->flags & IFF_PROMISC) { 209 + /* Enabling promiscuous mode */ 210 + regval |= MAC_NET_CFG_PROMISCUOUS_MODE; 211 + regval &= (~MAC_NET_CFG_MULTICAST_MODE); 212 + regval &= (~MAC_NET_CFG_UNICAST_MODE); 213 + } else if (priv->netdev->flags & IFF_ALLMULTI) { 214 + /* Enabling all multicast mode */ 215 + if (lan865x_set_all_multicast_addr(priv)) 216 + return; 217 + 218 + regval &= (~MAC_NET_CFG_PROMISCUOUS_MODE); 219 + regval |= MAC_NET_CFG_MULTICAST_MODE; 220 + regval &= (~MAC_NET_CFG_UNICAST_MODE); 221 + } else if (!netdev_mc_empty(priv->netdev)) { 222 + /* Enabling specific multicast mode */ 223 + if (lan865x_set_specific_multicast_addr(priv)) 224 + return; 225 + 226 + regval &= (~MAC_NET_CFG_PROMISCUOUS_MODE); 227 + regval |= MAC_NET_CFG_MULTICAST_MODE; 228 + regval &= (~MAC_NET_CFG_UNICAST_MODE); 229 + } else { 230 + /* Enabling local mac address only */ 231 + if (lan865x_clear_all_multicast_addr(priv)) 232 + return; 233 + } 234 + ret = oa_tc6_write_register(priv->tc6, LAN865X_REG_MAC_NET_CFG, regval); 235 + if (ret) 236 + netdev_err(priv->netdev, "Failed to enable promiscuous/multicast/normal mode: %d\n", 237 + ret); 238 + } 239 + 240 + static void lan865x_set_multicast_list(struct net_device *netdev) 241 + { 242 + struct lan865x_priv *priv = netdev_priv(netdev); 243 + 244 + schedule_work(&priv->multicast_work); 245 + } 246 + 247 + static netdev_tx_t lan865x_send_packet(struct sk_buff *skb, 248 + struct net_device *netdev) 249 + { 250 + struct lan865x_priv *priv = netdev_priv(netdev); 251 + 252 + return oa_tc6_start_xmit(priv->tc6, skb); 253 + } 254 + 255 + static int lan865x_hw_disable(struct lan865x_priv *priv) 256 + { 257 + u32 regval; 258 + 259 + if (oa_tc6_read_register(priv->tc6, LAN865X_REG_MAC_NET_CTL, &regval)) 260 + return -ENODEV; 261 + 262 + regval &= ~(MAC_NET_CTL_TXEN | MAC_NET_CTL_RXEN); 263 + 264 + if (oa_tc6_write_register(priv->tc6, LAN865X_REG_MAC_NET_CTL, regval)) 265 + return -ENODEV; 266 + 267 + return 0; 268 + } 269 + 270 + static int lan865x_net_close(struct net_device *netdev) 271 + { 272 + struct lan865x_priv *priv = netdev_priv(netdev); 273 + int ret; 274 + 275 + netif_stop_queue(netdev); 276 + phy_stop(netdev->phydev); 277 + ret = lan865x_hw_disable(priv); 278 + if (ret) { 279 + netdev_err(netdev, "Failed to disable the hardware: %d\n", ret); 280 + return ret; 281 + } 282 + 283 + return 0; 284 + } 285 + 286 + static int lan865x_hw_enable(struct lan865x_priv *priv) 287 + { 288 + u32 regval; 289 + 290 + if (oa_tc6_read_register(priv->tc6, LAN865X_REG_MAC_NET_CTL, &regval)) 291 + return -ENODEV; 292 + 293 + regval |= MAC_NET_CTL_TXEN | MAC_NET_CTL_RXEN; 294 + 295 + if (oa_tc6_write_register(priv->tc6, LAN865X_REG_MAC_NET_CTL, regval)) 296 + return -ENODEV; 297 + 298 + return 0; 299 + } 300 + 301 + static int lan865x_net_open(struct net_device *netdev) 302 + { 303 + struct lan865x_priv *priv = netdev_priv(netdev); 304 + int ret; 305 + 306 + ret = lan865x_hw_enable(priv); 307 + if (ret) { 308 + netdev_err(netdev, "Failed to enable hardware: %d\n", ret); 309 + return ret; 310 + } 311 + 312 + phy_start(netdev->phydev); 313 + 314 + return 0; 315 + } 316 + 317 + static const struct net_device_ops lan865x_netdev_ops = { 318 + .ndo_open = lan865x_net_open, 319 + .ndo_stop = lan865x_net_close, 320 + .ndo_start_xmit = lan865x_send_packet, 321 + .ndo_set_rx_mode = lan865x_set_multicast_list, 322 + .ndo_set_mac_address = lan865x_set_mac_address, 323 + }; 324 + 325 + static int lan865x_probe(struct spi_device *spi) 326 + { 327 + struct net_device *netdev; 328 + struct lan865x_priv *priv; 329 + int ret; 330 + 331 + netdev = alloc_etherdev(sizeof(struct lan865x_priv)); 332 + if (!netdev) 333 + return -ENOMEM; 334 + 335 + priv = netdev_priv(netdev); 336 + priv->netdev = netdev; 337 + priv->spi = spi; 338 + spi_set_drvdata(spi, priv); 339 + INIT_WORK(&priv->multicast_work, lan865x_multicast_work_handler); 340 + 341 + priv->tc6 = oa_tc6_init(spi, netdev); 342 + if (!priv->tc6) { 343 + ret = -ENODEV; 344 + goto free_netdev; 345 + } 346 + 347 + /* As per the point s3 in the below errata, SPI receive Ethernet frame 348 + * transfer may halt when starting the next frame in the same data block 349 + * (chunk) as the end of a previous frame. The RFA field should be 350 + * configured to 01b or 10b for proper operation. In these modes, only 351 + * one receive Ethernet frame will be placed in a single data block. 352 + * When the RFA field is written to 01b, received frames will be forced 353 + * to only start in the first word of the data block payload (SWO=0). As 354 + * recommended, enable zero align receive frame feature for proper 355 + * operation. 356 + * 357 + * https://ww1.microchip.com/downloads/aemDocuments/documents/AIS/ProductDocuments/Errata/LAN8650-1-Errata-80001075.pdf 358 + */ 359 + ret = oa_tc6_zero_align_receive_frame_enable(priv->tc6); 360 + if (ret) { 361 + dev_err(&spi->dev, "Failed to set ZARFE: %d\n", ret); 362 + goto oa_tc6_exit; 363 + } 364 + 365 + /* Get the MAC address from the SPI device tree node */ 366 + if (device_get_ethdev_address(&spi->dev, netdev)) 367 + eth_hw_addr_random(netdev); 368 + 369 + ret = lan865x_set_hw_macaddr(priv, netdev->dev_addr); 370 + if (ret) { 371 + dev_err(&spi->dev, "Failed to configure MAC: %d\n", ret); 372 + goto oa_tc6_exit; 373 + } 374 + 375 + netdev->if_port = IF_PORT_10BASET; 376 + netdev->irq = spi->irq; 377 + netdev->netdev_ops = &lan865x_netdev_ops; 378 + netdev->ethtool_ops = &lan865x_ethtool_ops; 379 + 380 + ret = register_netdev(netdev); 381 + if (ret) { 382 + dev_err(&spi->dev, "Register netdev failed (ret = %d)", ret); 383 + goto oa_tc6_exit; 384 + } 385 + 386 + return 0; 387 + 388 + oa_tc6_exit: 389 + oa_tc6_exit(priv->tc6); 390 + free_netdev: 391 + free_netdev(priv->netdev); 392 + return ret; 393 + } 394 + 395 + static void lan865x_remove(struct spi_device *spi) 396 + { 397 + struct lan865x_priv *priv = spi_get_drvdata(spi); 398 + 399 + cancel_work_sync(&priv->multicast_work); 400 + unregister_netdev(priv->netdev); 401 + oa_tc6_exit(priv->tc6); 402 + free_netdev(priv->netdev); 403 + } 404 + 405 + static const struct spi_device_id spidev_spi_ids[] = { 406 + { .name = "lan8650" }, 407 + {}, 408 + }; 409 + 410 + static const struct of_device_id lan865x_dt_ids[] = { 411 + { .compatible = "microchip,lan8650" }, 412 + { /* Sentinel */ } 413 + }; 414 + MODULE_DEVICE_TABLE(of, lan865x_dt_ids); 415 + 416 + static struct spi_driver lan865x_driver = { 417 + .driver = { 418 + .name = DRV_NAME, 419 + .of_match_table = lan865x_dt_ids, 420 + }, 421 + .probe = lan865x_probe, 422 + .remove = lan865x_remove, 423 + .id_table = spidev_spi_ids, 424 + }; 425 + module_spi_driver(lan865x_driver); 426 + 427 + MODULE_DESCRIPTION(DRV_NAME " 10Base-T1S MACPHY Ethernet Driver"); 428 + MODULE_AUTHOR("Parthiban Veerasooran <parthiban.veerasooran@microchip.com>"); 429 + MODULE_LICENSE("GPL");
+1361
drivers/net/ethernet/oa_tc6.c
··· 1 + // SPDX-License-Identifier: GPL-2.0+ 2 + /* 3 + * OPEN Alliance 10BASE‑T1x MAC‑PHY Serial Interface framework 4 + * 5 + * Author: Parthiban Veerasooran <parthiban.veerasooran@microchip.com> 6 + */ 7 + 8 + #include <linux/bitfield.h> 9 + #include <linux/iopoll.h> 10 + #include <linux/mdio.h> 11 + #include <linux/phy.h> 12 + #include <linux/oa_tc6.h> 13 + 14 + /* OPEN Alliance TC6 registers */ 15 + /* Standard Capabilities Register */ 16 + #define OA_TC6_REG_STDCAP 0x0002 17 + #define STDCAP_DIRECT_PHY_REG_ACCESS BIT(8) 18 + 19 + /* Reset Control and Status Register */ 20 + #define OA_TC6_REG_RESET 0x0003 21 + #define RESET_SWRESET BIT(0) /* Software Reset */ 22 + 23 + /* Configuration Register #0 */ 24 + #define OA_TC6_REG_CONFIG0 0x0004 25 + #define CONFIG0_SYNC BIT(15) 26 + #define CONFIG0_ZARFE_ENABLE BIT(12) 27 + 28 + /* Status Register #0 */ 29 + #define OA_TC6_REG_STATUS0 0x0008 30 + #define STATUS0_RESETC BIT(6) /* Reset Complete */ 31 + #define STATUS0_HEADER_ERROR BIT(5) 32 + #define STATUS0_LOSS_OF_FRAME_ERROR BIT(4) 33 + #define STATUS0_RX_BUFFER_OVERFLOW_ERROR BIT(3) 34 + #define STATUS0_TX_PROTOCOL_ERROR BIT(0) 35 + 36 + /* Buffer Status Register */ 37 + #define OA_TC6_REG_BUFFER_STATUS 0x000B 38 + #define BUFFER_STATUS_TX_CREDITS_AVAILABLE GENMASK(15, 8) 39 + #define BUFFER_STATUS_RX_CHUNKS_AVAILABLE GENMASK(7, 0) 40 + 41 + /* Interrupt Mask Register #0 */ 42 + #define OA_TC6_REG_INT_MASK0 0x000C 43 + #define INT_MASK0_HEADER_ERR_MASK BIT(5) 44 + #define INT_MASK0_LOSS_OF_FRAME_ERR_MASK BIT(4) 45 + #define INT_MASK0_RX_BUFFER_OVERFLOW_ERR_MASK BIT(3) 46 + #define INT_MASK0_TX_PROTOCOL_ERR_MASK BIT(0) 47 + 48 + /* PHY Clause 22 registers base address and mask */ 49 + #define OA_TC6_PHY_STD_REG_ADDR_BASE 0xFF00 50 + #define OA_TC6_PHY_STD_REG_ADDR_MASK 0x1F 51 + 52 + /* Control command header */ 53 + #define OA_TC6_CTRL_HEADER_DATA_NOT_CTRL BIT(31) 54 + #define OA_TC6_CTRL_HEADER_WRITE_NOT_READ BIT(29) 55 + #define OA_TC6_CTRL_HEADER_MEM_MAP_SELECTOR GENMASK(27, 24) 56 + #define OA_TC6_CTRL_HEADER_ADDR GENMASK(23, 8) 57 + #define OA_TC6_CTRL_HEADER_LENGTH GENMASK(7, 1) 58 + #define OA_TC6_CTRL_HEADER_PARITY BIT(0) 59 + 60 + /* Data header */ 61 + #define OA_TC6_DATA_HEADER_DATA_NOT_CTRL BIT(31) 62 + #define OA_TC6_DATA_HEADER_DATA_VALID BIT(21) 63 + #define OA_TC6_DATA_HEADER_START_VALID BIT(20) 64 + #define OA_TC6_DATA_HEADER_START_WORD_OFFSET GENMASK(19, 16) 65 + #define OA_TC6_DATA_HEADER_END_VALID BIT(14) 66 + #define OA_TC6_DATA_HEADER_END_BYTE_OFFSET GENMASK(13, 8) 67 + #define OA_TC6_DATA_HEADER_PARITY BIT(0) 68 + 69 + /* Data footer */ 70 + #define OA_TC6_DATA_FOOTER_EXTENDED_STS BIT(31) 71 + #define OA_TC6_DATA_FOOTER_RXD_HEADER_BAD BIT(30) 72 + #define OA_TC6_DATA_FOOTER_CONFIG_SYNC BIT(29) 73 + #define OA_TC6_DATA_FOOTER_RX_CHUNKS GENMASK(28, 24) 74 + #define OA_TC6_DATA_FOOTER_DATA_VALID BIT(21) 75 + #define OA_TC6_DATA_FOOTER_START_VALID BIT(20) 76 + #define OA_TC6_DATA_FOOTER_START_WORD_OFFSET GENMASK(19, 16) 77 + #define OA_TC6_DATA_FOOTER_END_VALID BIT(14) 78 + #define OA_TC6_DATA_FOOTER_END_BYTE_OFFSET GENMASK(13, 8) 79 + #define OA_TC6_DATA_FOOTER_TX_CREDITS GENMASK(5, 1) 80 + 81 + /* PHY – Clause 45 registers memory map selector (MMS) as per table 6 in the 82 + * OPEN Alliance specification. 83 + */ 84 + #define OA_TC6_PHY_C45_PCS_MMS2 2 /* MMD 3 */ 85 + #define OA_TC6_PHY_C45_PMA_PMD_MMS3 3 /* MMD 1 */ 86 + #define OA_TC6_PHY_C45_VS_PLCA_MMS4 4 /* MMD 31 */ 87 + #define OA_TC6_PHY_C45_AUTO_NEG_MMS5 5 /* MMD 7 */ 88 + #define OA_TC6_PHY_C45_POWER_UNIT_MMS6 6 /* MMD 13 */ 89 + 90 + #define OA_TC6_CTRL_HEADER_SIZE 4 91 + #define OA_TC6_CTRL_REG_VALUE_SIZE 4 92 + #define OA_TC6_CTRL_IGNORED_SIZE 4 93 + #define OA_TC6_CTRL_MAX_REGISTERS 128 94 + #define OA_TC6_CTRL_SPI_BUF_SIZE (OA_TC6_CTRL_HEADER_SIZE +\ 95 + (OA_TC6_CTRL_MAX_REGISTERS *\ 96 + OA_TC6_CTRL_REG_VALUE_SIZE) +\ 97 + OA_TC6_CTRL_IGNORED_SIZE) 98 + #define OA_TC6_CHUNK_PAYLOAD_SIZE 64 99 + #define OA_TC6_DATA_HEADER_SIZE 4 100 + #define OA_TC6_CHUNK_SIZE (OA_TC6_DATA_HEADER_SIZE +\ 101 + OA_TC6_CHUNK_PAYLOAD_SIZE) 102 + #define OA_TC6_MAX_TX_CHUNKS 48 103 + #define OA_TC6_SPI_DATA_BUF_SIZE (OA_TC6_MAX_TX_CHUNKS *\ 104 + OA_TC6_CHUNK_SIZE) 105 + #define STATUS0_RESETC_POLL_DELAY 1000 106 + #define STATUS0_RESETC_POLL_TIMEOUT 1000000 107 + 108 + /* Internal structure for MAC-PHY drivers */ 109 + struct oa_tc6 { 110 + struct device *dev; 111 + struct net_device *netdev; 112 + struct phy_device *phydev; 113 + struct mii_bus *mdiobus; 114 + struct spi_device *spi; 115 + struct mutex spi_ctrl_lock; /* Protects spi control transfer */ 116 + void *spi_ctrl_tx_buf; 117 + void *spi_ctrl_rx_buf; 118 + void *spi_data_tx_buf; 119 + void *spi_data_rx_buf; 120 + struct sk_buff *ongoing_tx_skb; 121 + struct sk_buff *waiting_tx_skb; 122 + struct sk_buff *rx_skb; 123 + struct task_struct *spi_thread; 124 + wait_queue_head_t spi_wq; 125 + u16 tx_skb_offset; 126 + u16 spi_data_tx_buf_offset; 127 + u16 tx_credits; 128 + u8 rx_chunks_available; 129 + bool rx_buf_overflow; 130 + bool int_flag; 131 + }; 132 + 133 + enum oa_tc6_header_type { 134 + OA_TC6_CTRL_HEADER, 135 + OA_TC6_DATA_HEADER, 136 + }; 137 + 138 + enum oa_tc6_register_op { 139 + OA_TC6_CTRL_REG_READ = 0, 140 + OA_TC6_CTRL_REG_WRITE = 1, 141 + }; 142 + 143 + enum oa_tc6_data_valid_info { 144 + OA_TC6_DATA_INVALID, 145 + OA_TC6_DATA_VALID, 146 + }; 147 + 148 + enum oa_tc6_data_start_valid_info { 149 + OA_TC6_DATA_START_INVALID, 150 + OA_TC6_DATA_START_VALID, 151 + }; 152 + 153 + enum oa_tc6_data_end_valid_info { 154 + OA_TC6_DATA_END_INVALID, 155 + OA_TC6_DATA_END_VALID, 156 + }; 157 + 158 + static int oa_tc6_spi_transfer(struct oa_tc6 *tc6, 159 + enum oa_tc6_header_type header_type, u16 length) 160 + { 161 + struct spi_transfer xfer = { 0 }; 162 + struct spi_message msg; 163 + 164 + if (header_type == OA_TC6_DATA_HEADER) { 165 + xfer.tx_buf = tc6->spi_data_tx_buf; 166 + xfer.rx_buf = tc6->spi_data_rx_buf; 167 + } else { 168 + xfer.tx_buf = tc6->spi_ctrl_tx_buf; 169 + xfer.rx_buf = tc6->spi_ctrl_rx_buf; 170 + } 171 + xfer.len = length; 172 + 173 + spi_message_init(&msg); 174 + spi_message_add_tail(&xfer, &msg); 175 + 176 + return spi_sync(tc6->spi, &msg); 177 + } 178 + 179 + static int oa_tc6_get_parity(u32 p) 180 + { 181 + /* Public domain code snippet, lifted from 182 + * http://www-graphics.stanford.edu/~seander/bithacks.html 183 + */ 184 + p ^= p >> 1; 185 + p ^= p >> 2; 186 + p = (p & 0x11111111U) * 0x11111111U; 187 + 188 + /* Odd parity is used here */ 189 + return !((p >> 28) & 1); 190 + } 191 + 192 + static __be32 oa_tc6_prepare_ctrl_header(u32 addr, u8 length, 193 + enum oa_tc6_register_op reg_op) 194 + { 195 + u32 header; 196 + 197 + header = FIELD_PREP(OA_TC6_CTRL_HEADER_DATA_NOT_CTRL, 198 + OA_TC6_CTRL_HEADER) | 199 + FIELD_PREP(OA_TC6_CTRL_HEADER_WRITE_NOT_READ, reg_op) | 200 + FIELD_PREP(OA_TC6_CTRL_HEADER_MEM_MAP_SELECTOR, addr >> 16) | 201 + FIELD_PREP(OA_TC6_CTRL_HEADER_ADDR, addr) | 202 + FIELD_PREP(OA_TC6_CTRL_HEADER_LENGTH, length - 1); 203 + header |= FIELD_PREP(OA_TC6_CTRL_HEADER_PARITY, 204 + oa_tc6_get_parity(header)); 205 + 206 + return cpu_to_be32(header); 207 + } 208 + 209 + static void oa_tc6_update_ctrl_write_data(struct oa_tc6 *tc6, u32 value[], 210 + u8 length) 211 + { 212 + __be32 *tx_buf = tc6->spi_ctrl_tx_buf + OA_TC6_CTRL_HEADER_SIZE; 213 + 214 + for (int i = 0; i < length; i++) 215 + *tx_buf++ = cpu_to_be32(value[i]); 216 + } 217 + 218 + static u16 oa_tc6_calculate_ctrl_buf_size(u8 length) 219 + { 220 + /* Control command consists 4 bytes header + 4 bytes register value for 221 + * each register + 4 bytes ignored value. 222 + */ 223 + return OA_TC6_CTRL_HEADER_SIZE + OA_TC6_CTRL_REG_VALUE_SIZE * length + 224 + OA_TC6_CTRL_IGNORED_SIZE; 225 + } 226 + 227 + static void oa_tc6_prepare_ctrl_spi_buf(struct oa_tc6 *tc6, u32 address, 228 + u32 value[], u8 length, 229 + enum oa_tc6_register_op reg_op) 230 + { 231 + __be32 *tx_buf = tc6->spi_ctrl_tx_buf; 232 + 233 + *tx_buf = oa_tc6_prepare_ctrl_header(address, length, reg_op); 234 + 235 + if (reg_op == OA_TC6_CTRL_REG_WRITE) 236 + oa_tc6_update_ctrl_write_data(tc6, value, length); 237 + } 238 + 239 + static int oa_tc6_check_ctrl_write_reply(struct oa_tc6 *tc6, u8 size) 240 + { 241 + u8 *tx_buf = tc6->spi_ctrl_tx_buf; 242 + u8 *rx_buf = tc6->spi_ctrl_rx_buf; 243 + 244 + rx_buf += OA_TC6_CTRL_IGNORED_SIZE; 245 + 246 + /* The echoed control write must match with the one that was 247 + * transmitted. 248 + */ 249 + if (memcmp(tx_buf, rx_buf, size - OA_TC6_CTRL_IGNORED_SIZE)) 250 + return -EPROTO; 251 + 252 + return 0; 253 + } 254 + 255 + static int oa_tc6_check_ctrl_read_reply(struct oa_tc6 *tc6, u8 size) 256 + { 257 + u32 *rx_buf = tc6->spi_ctrl_rx_buf + OA_TC6_CTRL_IGNORED_SIZE; 258 + u32 *tx_buf = tc6->spi_ctrl_tx_buf; 259 + 260 + /* The echoed control read header must match with the one that was 261 + * transmitted. 262 + */ 263 + if (*tx_buf != *rx_buf) 264 + return -EPROTO; 265 + 266 + return 0; 267 + } 268 + 269 + static void oa_tc6_copy_ctrl_read_data(struct oa_tc6 *tc6, u32 value[], 270 + u8 length) 271 + { 272 + __be32 *rx_buf = tc6->spi_ctrl_rx_buf + OA_TC6_CTRL_IGNORED_SIZE + 273 + OA_TC6_CTRL_HEADER_SIZE; 274 + 275 + for (int i = 0; i < length; i++) 276 + value[i] = be32_to_cpu(*rx_buf++); 277 + } 278 + 279 + static int oa_tc6_perform_ctrl(struct oa_tc6 *tc6, u32 address, u32 value[], 280 + u8 length, enum oa_tc6_register_op reg_op) 281 + { 282 + u16 size; 283 + int ret; 284 + 285 + /* Prepare control command and copy to SPI control buffer */ 286 + oa_tc6_prepare_ctrl_spi_buf(tc6, address, value, length, reg_op); 287 + 288 + size = oa_tc6_calculate_ctrl_buf_size(length); 289 + 290 + /* Perform SPI transfer */ 291 + ret = oa_tc6_spi_transfer(tc6, OA_TC6_CTRL_HEADER, size); 292 + if (ret) { 293 + dev_err(&tc6->spi->dev, "SPI transfer failed for control: %d\n", 294 + ret); 295 + return ret; 296 + } 297 + 298 + /* Check echoed/received control write command reply for errors */ 299 + if (reg_op == OA_TC6_CTRL_REG_WRITE) 300 + return oa_tc6_check_ctrl_write_reply(tc6, size); 301 + 302 + /* Check echoed/received control read command reply for errors */ 303 + ret = oa_tc6_check_ctrl_read_reply(tc6, size); 304 + if (ret) 305 + return ret; 306 + 307 + oa_tc6_copy_ctrl_read_data(tc6, value, length); 308 + 309 + return 0; 310 + } 311 + 312 + /** 313 + * oa_tc6_read_registers - function for reading multiple consecutive registers. 314 + * @tc6: oa_tc6 struct. 315 + * @address: address of the first register to be read in the MAC-PHY. 316 + * @value: values to be read from the starting register address @address. 317 + * @length: number of consecutive registers to be read from @address. 318 + * 319 + * Maximum of 128 consecutive registers can be read starting at @address. 320 + * 321 + * Return: 0 on success otherwise failed. 322 + */ 323 + int oa_tc6_read_registers(struct oa_tc6 *tc6, u32 address, u32 value[], 324 + u8 length) 325 + { 326 + int ret; 327 + 328 + if (!length || length > OA_TC6_CTRL_MAX_REGISTERS) { 329 + dev_err(&tc6->spi->dev, "Invalid register length parameter\n"); 330 + return -EINVAL; 331 + } 332 + 333 + mutex_lock(&tc6->spi_ctrl_lock); 334 + ret = oa_tc6_perform_ctrl(tc6, address, value, length, 335 + OA_TC6_CTRL_REG_READ); 336 + mutex_unlock(&tc6->spi_ctrl_lock); 337 + 338 + return ret; 339 + } 340 + EXPORT_SYMBOL_GPL(oa_tc6_read_registers); 341 + 342 + /** 343 + * oa_tc6_read_register - function for reading a MAC-PHY register. 344 + * @tc6: oa_tc6 struct. 345 + * @address: register address of the MAC-PHY to be read. 346 + * @value: value read from the @address register address of the MAC-PHY. 347 + * 348 + * Return: 0 on success otherwise failed. 349 + */ 350 + int oa_tc6_read_register(struct oa_tc6 *tc6, u32 address, u32 *value) 351 + { 352 + return oa_tc6_read_registers(tc6, address, value, 1); 353 + } 354 + EXPORT_SYMBOL_GPL(oa_tc6_read_register); 355 + 356 + /** 357 + * oa_tc6_write_registers - function for writing multiple consecutive registers. 358 + * @tc6: oa_tc6 struct. 359 + * @address: address of the first register to be written in the MAC-PHY. 360 + * @value: values to be written from the starting register address @address. 361 + * @length: number of consecutive registers to be written from @address. 362 + * 363 + * Maximum of 128 consecutive registers can be written starting at @address. 364 + * 365 + * Return: 0 on success otherwise failed. 366 + */ 367 + int oa_tc6_write_registers(struct oa_tc6 *tc6, u32 address, u32 value[], 368 + u8 length) 369 + { 370 + int ret; 371 + 372 + if (!length || length > OA_TC6_CTRL_MAX_REGISTERS) { 373 + dev_err(&tc6->spi->dev, "Invalid register length parameter\n"); 374 + return -EINVAL; 375 + } 376 + 377 + mutex_lock(&tc6->spi_ctrl_lock); 378 + ret = oa_tc6_perform_ctrl(tc6, address, value, length, 379 + OA_TC6_CTRL_REG_WRITE); 380 + mutex_unlock(&tc6->spi_ctrl_lock); 381 + 382 + return ret; 383 + } 384 + EXPORT_SYMBOL_GPL(oa_tc6_write_registers); 385 + 386 + /** 387 + * oa_tc6_write_register - function for writing a MAC-PHY register. 388 + * @tc6: oa_tc6 struct. 389 + * @address: register address of the MAC-PHY to be written. 390 + * @value: value to be written in the @address register address of the MAC-PHY. 391 + * 392 + * Return: 0 on success otherwise failed. 393 + */ 394 + int oa_tc6_write_register(struct oa_tc6 *tc6, u32 address, u32 value) 395 + { 396 + return oa_tc6_write_registers(tc6, address, &value, 1); 397 + } 398 + EXPORT_SYMBOL_GPL(oa_tc6_write_register); 399 + 400 + static int oa_tc6_check_phy_reg_direct_access_capability(struct oa_tc6 *tc6) 401 + { 402 + u32 regval; 403 + int ret; 404 + 405 + ret = oa_tc6_read_register(tc6, OA_TC6_REG_STDCAP, &regval); 406 + if (ret) 407 + return ret; 408 + 409 + if (!(regval & STDCAP_DIRECT_PHY_REG_ACCESS)) 410 + return -ENODEV; 411 + 412 + return 0; 413 + } 414 + 415 + static void oa_tc6_handle_link_change(struct net_device *netdev) 416 + { 417 + phy_print_status(netdev->phydev); 418 + } 419 + 420 + static int oa_tc6_mdiobus_read(struct mii_bus *bus, int addr, int regnum) 421 + { 422 + struct oa_tc6 *tc6 = bus->priv; 423 + u32 regval; 424 + bool ret; 425 + 426 + ret = oa_tc6_read_register(tc6, OA_TC6_PHY_STD_REG_ADDR_BASE | 427 + (regnum & OA_TC6_PHY_STD_REG_ADDR_MASK), 428 + &regval); 429 + if (ret) 430 + return ret; 431 + 432 + return regval; 433 + } 434 + 435 + static int oa_tc6_mdiobus_write(struct mii_bus *bus, int addr, int regnum, 436 + u16 val) 437 + { 438 + struct oa_tc6 *tc6 = bus->priv; 439 + 440 + return oa_tc6_write_register(tc6, OA_TC6_PHY_STD_REG_ADDR_BASE | 441 + (regnum & OA_TC6_PHY_STD_REG_ADDR_MASK), 442 + val); 443 + } 444 + 445 + static int oa_tc6_get_phy_c45_mms(int devnum) 446 + { 447 + switch (devnum) { 448 + case MDIO_MMD_PCS: 449 + return OA_TC6_PHY_C45_PCS_MMS2; 450 + case MDIO_MMD_PMAPMD: 451 + return OA_TC6_PHY_C45_PMA_PMD_MMS3; 452 + case MDIO_MMD_VEND2: 453 + return OA_TC6_PHY_C45_VS_PLCA_MMS4; 454 + case MDIO_MMD_AN: 455 + return OA_TC6_PHY_C45_AUTO_NEG_MMS5; 456 + case MDIO_MMD_POWER_UNIT: 457 + return OA_TC6_PHY_C45_POWER_UNIT_MMS6; 458 + default: 459 + return -EOPNOTSUPP; 460 + } 461 + } 462 + 463 + static int oa_tc6_mdiobus_read_c45(struct mii_bus *bus, int addr, int devnum, 464 + int regnum) 465 + { 466 + struct oa_tc6 *tc6 = bus->priv; 467 + u32 regval; 468 + int ret; 469 + 470 + ret = oa_tc6_get_phy_c45_mms(devnum); 471 + if (ret < 0) 472 + return ret; 473 + 474 + ret = oa_tc6_read_register(tc6, (ret << 16) | regnum, &regval); 475 + if (ret) 476 + return ret; 477 + 478 + return regval; 479 + } 480 + 481 + static int oa_tc6_mdiobus_write_c45(struct mii_bus *bus, int addr, int devnum, 482 + int regnum, u16 val) 483 + { 484 + struct oa_tc6 *tc6 = bus->priv; 485 + int ret; 486 + 487 + ret = oa_tc6_get_phy_c45_mms(devnum); 488 + if (ret < 0) 489 + return ret; 490 + 491 + return oa_tc6_write_register(tc6, (ret << 16) | regnum, val); 492 + } 493 + 494 + static int oa_tc6_mdiobus_register(struct oa_tc6 *tc6) 495 + { 496 + int ret; 497 + 498 + tc6->mdiobus = mdiobus_alloc(); 499 + if (!tc6->mdiobus) { 500 + netdev_err(tc6->netdev, "MDIO bus alloc failed\n"); 501 + return -ENOMEM; 502 + } 503 + 504 + tc6->mdiobus->priv = tc6; 505 + tc6->mdiobus->read = oa_tc6_mdiobus_read; 506 + tc6->mdiobus->write = oa_tc6_mdiobus_write; 507 + /* OPEN Alliance 10BASE-T1x compliance MAC-PHYs will have both C22 and 508 + * C45 registers space. If the PHY is discovered via C22 bus protocol it 509 + * assumes it uses C22 protocol and always uses C22 registers indirect 510 + * access to access C45 registers. This is because, we don't have a 511 + * clean separation between C22/C45 register space and C22/C45 MDIO bus 512 + * protocols. Resulting, PHY C45 registers direct access can't be used 513 + * which can save multiple SPI bus access. To support this feature, PHY 514 + * drivers can set .read_mmd/.write_mmd in the PHY driver to call 515 + * .read_c45/.write_c45. Ex: drivers/net/phy/microchip_t1s.c 516 + */ 517 + tc6->mdiobus->read_c45 = oa_tc6_mdiobus_read_c45; 518 + tc6->mdiobus->write_c45 = oa_tc6_mdiobus_write_c45; 519 + tc6->mdiobus->name = "oa-tc6-mdiobus"; 520 + tc6->mdiobus->parent = tc6->dev; 521 + 522 + snprintf(tc6->mdiobus->id, ARRAY_SIZE(tc6->mdiobus->id), "%s", 523 + dev_name(&tc6->spi->dev)); 524 + 525 + ret = mdiobus_register(tc6->mdiobus); 526 + if (ret) { 527 + netdev_err(tc6->netdev, "Could not register MDIO bus\n"); 528 + mdiobus_free(tc6->mdiobus); 529 + return ret; 530 + } 531 + 532 + return 0; 533 + } 534 + 535 + static void oa_tc6_mdiobus_unregister(struct oa_tc6 *tc6) 536 + { 537 + mdiobus_unregister(tc6->mdiobus); 538 + mdiobus_free(tc6->mdiobus); 539 + } 540 + 541 + static int oa_tc6_phy_init(struct oa_tc6 *tc6) 542 + { 543 + int ret; 544 + 545 + ret = oa_tc6_check_phy_reg_direct_access_capability(tc6); 546 + if (ret) { 547 + netdev_err(tc6->netdev, 548 + "Direct PHY register access is not supported by the MAC-PHY\n"); 549 + return ret; 550 + } 551 + 552 + ret = oa_tc6_mdiobus_register(tc6); 553 + if (ret) 554 + return ret; 555 + 556 + tc6->phydev = phy_find_first(tc6->mdiobus); 557 + if (!tc6->phydev) { 558 + netdev_err(tc6->netdev, "No PHY found\n"); 559 + oa_tc6_mdiobus_unregister(tc6); 560 + return -ENODEV; 561 + } 562 + 563 + tc6->phydev->is_internal = true; 564 + ret = phy_connect_direct(tc6->netdev, tc6->phydev, 565 + &oa_tc6_handle_link_change, 566 + PHY_INTERFACE_MODE_INTERNAL); 567 + if (ret) { 568 + netdev_err(tc6->netdev, "Can't attach PHY to %s\n", 569 + tc6->mdiobus->id); 570 + oa_tc6_mdiobus_unregister(tc6); 571 + return ret; 572 + } 573 + 574 + phy_attached_info(tc6->netdev->phydev); 575 + 576 + return 0; 577 + } 578 + 579 + static void oa_tc6_phy_exit(struct oa_tc6 *tc6) 580 + { 581 + phy_disconnect(tc6->phydev); 582 + oa_tc6_mdiobus_unregister(tc6); 583 + } 584 + 585 + static int oa_tc6_read_status0(struct oa_tc6 *tc6) 586 + { 587 + u32 regval; 588 + int ret; 589 + 590 + ret = oa_tc6_read_register(tc6, OA_TC6_REG_STATUS0, &regval); 591 + if (ret) { 592 + dev_err(&tc6->spi->dev, "STATUS0 register read failed: %d\n", 593 + ret); 594 + return 0; 595 + } 596 + 597 + return regval; 598 + } 599 + 600 + static int oa_tc6_sw_reset_macphy(struct oa_tc6 *tc6) 601 + { 602 + u32 regval = RESET_SWRESET; 603 + int ret; 604 + 605 + ret = oa_tc6_write_register(tc6, OA_TC6_REG_RESET, regval); 606 + if (ret) 607 + return ret; 608 + 609 + /* Poll for soft reset complete for every 1ms until 1s timeout */ 610 + ret = readx_poll_timeout(oa_tc6_read_status0, tc6, regval, 611 + regval & STATUS0_RESETC, 612 + STATUS0_RESETC_POLL_DELAY, 613 + STATUS0_RESETC_POLL_TIMEOUT); 614 + if (ret) 615 + return -ENODEV; 616 + 617 + /* Clear the reset complete status */ 618 + return oa_tc6_write_register(tc6, OA_TC6_REG_STATUS0, regval); 619 + } 620 + 621 + static int oa_tc6_unmask_macphy_error_interrupts(struct oa_tc6 *tc6) 622 + { 623 + u32 regval; 624 + int ret; 625 + 626 + ret = oa_tc6_read_register(tc6, OA_TC6_REG_INT_MASK0, &regval); 627 + if (ret) 628 + return ret; 629 + 630 + regval &= ~(INT_MASK0_TX_PROTOCOL_ERR_MASK | 631 + INT_MASK0_RX_BUFFER_OVERFLOW_ERR_MASK | 632 + INT_MASK0_LOSS_OF_FRAME_ERR_MASK | 633 + INT_MASK0_HEADER_ERR_MASK); 634 + 635 + return oa_tc6_write_register(tc6, OA_TC6_REG_INT_MASK0, regval); 636 + } 637 + 638 + static int oa_tc6_enable_data_transfer(struct oa_tc6 *tc6) 639 + { 640 + u32 value; 641 + int ret; 642 + 643 + ret = oa_tc6_read_register(tc6, OA_TC6_REG_CONFIG0, &value); 644 + if (ret) 645 + return ret; 646 + 647 + /* Enable configuration synchronization for data transfer */ 648 + value |= CONFIG0_SYNC; 649 + 650 + return oa_tc6_write_register(tc6, OA_TC6_REG_CONFIG0, value); 651 + } 652 + 653 + static void oa_tc6_cleanup_ongoing_rx_skb(struct oa_tc6 *tc6) 654 + { 655 + if (tc6->rx_skb) { 656 + tc6->netdev->stats.rx_dropped++; 657 + kfree_skb(tc6->rx_skb); 658 + tc6->rx_skb = NULL; 659 + } 660 + } 661 + 662 + static void oa_tc6_cleanup_ongoing_tx_skb(struct oa_tc6 *tc6) 663 + { 664 + if (tc6->ongoing_tx_skb) { 665 + tc6->netdev->stats.tx_dropped++; 666 + kfree_skb(tc6->ongoing_tx_skb); 667 + tc6->ongoing_tx_skb = NULL; 668 + } 669 + } 670 + 671 + static int oa_tc6_process_extended_status(struct oa_tc6 *tc6) 672 + { 673 + u32 value; 674 + int ret; 675 + 676 + ret = oa_tc6_read_register(tc6, OA_TC6_REG_STATUS0, &value); 677 + if (ret) { 678 + netdev_err(tc6->netdev, "STATUS0 register read failed: %d\n", 679 + ret); 680 + return ret; 681 + } 682 + 683 + /* Clear the error interrupts status */ 684 + ret = oa_tc6_write_register(tc6, OA_TC6_REG_STATUS0, value); 685 + if (ret) { 686 + netdev_err(tc6->netdev, "STATUS0 register write failed: %d\n", 687 + ret); 688 + return ret; 689 + } 690 + 691 + if (FIELD_GET(STATUS0_RX_BUFFER_OVERFLOW_ERROR, value)) { 692 + tc6->rx_buf_overflow = true; 693 + oa_tc6_cleanup_ongoing_rx_skb(tc6); 694 + net_err_ratelimited("%s: Receive buffer overflow error\n", 695 + tc6->netdev->name); 696 + return -EAGAIN; 697 + } 698 + if (FIELD_GET(STATUS0_TX_PROTOCOL_ERROR, value)) { 699 + netdev_err(tc6->netdev, "Transmit protocol error\n"); 700 + return -ENODEV; 701 + } 702 + /* TODO: Currently loss of frame and header errors are treated as 703 + * non-recoverable errors. They will be handled in the next version. 704 + */ 705 + if (FIELD_GET(STATUS0_LOSS_OF_FRAME_ERROR, value)) { 706 + netdev_err(tc6->netdev, "Loss of frame error\n"); 707 + return -ENODEV; 708 + } 709 + if (FIELD_GET(STATUS0_HEADER_ERROR, value)) { 710 + netdev_err(tc6->netdev, "Header error\n"); 711 + return -ENODEV; 712 + } 713 + 714 + return 0; 715 + } 716 + 717 + static int oa_tc6_process_rx_chunk_footer(struct oa_tc6 *tc6, u32 footer) 718 + { 719 + /* Process rx chunk footer for the following, 720 + * 1. tx credits 721 + * 2. errors if any from MAC-PHY 722 + * 3. receive chunks available 723 + */ 724 + tc6->tx_credits = FIELD_GET(OA_TC6_DATA_FOOTER_TX_CREDITS, footer); 725 + tc6->rx_chunks_available = FIELD_GET(OA_TC6_DATA_FOOTER_RX_CHUNKS, 726 + footer); 727 + 728 + if (FIELD_GET(OA_TC6_DATA_FOOTER_EXTENDED_STS, footer)) { 729 + int ret = oa_tc6_process_extended_status(tc6); 730 + 731 + if (ret) 732 + return ret; 733 + } 734 + 735 + /* TODO: Currently received header bad and configuration unsync errors 736 + * are treated as non-recoverable errors. They will be handled in the 737 + * next version. 738 + */ 739 + if (FIELD_GET(OA_TC6_DATA_FOOTER_RXD_HEADER_BAD, footer)) { 740 + netdev_err(tc6->netdev, "Rxd header bad error\n"); 741 + return -ENODEV; 742 + } 743 + 744 + if (!FIELD_GET(OA_TC6_DATA_FOOTER_CONFIG_SYNC, footer)) { 745 + netdev_err(tc6->netdev, "Config unsync error\n"); 746 + return -ENODEV; 747 + } 748 + 749 + return 0; 750 + } 751 + 752 + static void oa_tc6_submit_rx_skb(struct oa_tc6 *tc6) 753 + { 754 + tc6->rx_skb->protocol = eth_type_trans(tc6->rx_skb, tc6->netdev); 755 + tc6->netdev->stats.rx_packets++; 756 + tc6->netdev->stats.rx_bytes += tc6->rx_skb->len; 757 + 758 + netif_rx(tc6->rx_skb); 759 + 760 + tc6->rx_skb = NULL; 761 + } 762 + 763 + static void oa_tc6_update_rx_skb(struct oa_tc6 *tc6, u8 *payload, u8 length) 764 + { 765 + memcpy(skb_put(tc6->rx_skb, length), payload, length); 766 + } 767 + 768 + static int oa_tc6_allocate_rx_skb(struct oa_tc6 *tc6) 769 + { 770 + tc6->rx_skb = netdev_alloc_skb_ip_align(tc6->netdev, tc6->netdev->mtu + 771 + ETH_HLEN + ETH_FCS_LEN); 772 + if (!tc6->rx_skb) { 773 + tc6->netdev->stats.rx_dropped++; 774 + return -ENOMEM; 775 + } 776 + 777 + return 0; 778 + } 779 + 780 + static int oa_tc6_prcs_complete_rx_frame(struct oa_tc6 *tc6, u8 *payload, 781 + u16 size) 782 + { 783 + int ret; 784 + 785 + ret = oa_tc6_allocate_rx_skb(tc6); 786 + if (ret) 787 + return ret; 788 + 789 + oa_tc6_update_rx_skb(tc6, payload, size); 790 + 791 + oa_tc6_submit_rx_skb(tc6); 792 + 793 + return 0; 794 + } 795 + 796 + static int oa_tc6_prcs_rx_frame_start(struct oa_tc6 *tc6, u8 *payload, u16 size) 797 + { 798 + int ret; 799 + 800 + ret = oa_tc6_allocate_rx_skb(tc6); 801 + if (ret) 802 + return ret; 803 + 804 + oa_tc6_update_rx_skb(tc6, payload, size); 805 + 806 + return 0; 807 + } 808 + 809 + static void oa_tc6_prcs_rx_frame_end(struct oa_tc6 *tc6, u8 *payload, u16 size) 810 + { 811 + oa_tc6_update_rx_skb(tc6, payload, size); 812 + 813 + oa_tc6_submit_rx_skb(tc6); 814 + } 815 + 816 + static void oa_tc6_prcs_ongoing_rx_frame(struct oa_tc6 *tc6, u8 *payload, 817 + u32 footer) 818 + { 819 + oa_tc6_update_rx_skb(tc6, payload, OA_TC6_CHUNK_PAYLOAD_SIZE); 820 + } 821 + 822 + static int oa_tc6_prcs_rx_chunk_payload(struct oa_tc6 *tc6, u8 *data, 823 + u32 footer) 824 + { 825 + u8 start_byte_offset = FIELD_GET(OA_TC6_DATA_FOOTER_START_WORD_OFFSET, 826 + footer) * sizeof(u32); 827 + u8 end_byte_offset = FIELD_GET(OA_TC6_DATA_FOOTER_END_BYTE_OFFSET, 828 + footer); 829 + bool start_valid = FIELD_GET(OA_TC6_DATA_FOOTER_START_VALID, footer); 830 + bool end_valid = FIELD_GET(OA_TC6_DATA_FOOTER_END_VALID, footer); 831 + u16 size; 832 + 833 + /* Restart the new rx frame after receiving rx buffer overflow error */ 834 + if (start_valid && tc6->rx_buf_overflow) 835 + tc6->rx_buf_overflow = false; 836 + 837 + if (tc6->rx_buf_overflow) 838 + return 0; 839 + 840 + /* Process the chunk with complete rx frame */ 841 + if (start_valid && end_valid && start_byte_offset < end_byte_offset) { 842 + size = end_byte_offset + 1 - start_byte_offset; 843 + return oa_tc6_prcs_complete_rx_frame(tc6, 844 + &data[start_byte_offset], 845 + size); 846 + } 847 + 848 + /* Process the chunk with only rx frame start */ 849 + if (start_valid && !end_valid) { 850 + size = OA_TC6_CHUNK_PAYLOAD_SIZE - start_byte_offset; 851 + return oa_tc6_prcs_rx_frame_start(tc6, 852 + &data[start_byte_offset], 853 + size); 854 + } 855 + 856 + /* Process the chunk with only rx frame end */ 857 + if (end_valid && !start_valid) { 858 + size = end_byte_offset + 1; 859 + oa_tc6_prcs_rx_frame_end(tc6, data, size); 860 + return 0; 861 + } 862 + 863 + /* Process the chunk with previous rx frame end and next rx frame 864 + * start. 865 + */ 866 + if (start_valid && end_valid && start_byte_offset > end_byte_offset) { 867 + /* After rx buffer overflow error received, there might be a 868 + * possibility of getting an end valid of a previously 869 + * incomplete rx frame along with the new rx frame start valid. 870 + */ 871 + if (tc6->rx_skb) { 872 + size = end_byte_offset + 1; 873 + oa_tc6_prcs_rx_frame_end(tc6, data, size); 874 + } 875 + size = OA_TC6_CHUNK_PAYLOAD_SIZE - start_byte_offset; 876 + return oa_tc6_prcs_rx_frame_start(tc6, 877 + &data[start_byte_offset], 878 + size); 879 + } 880 + 881 + /* Process the chunk with ongoing rx frame data */ 882 + oa_tc6_prcs_ongoing_rx_frame(tc6, data, footer); 883 + 884 + return 0; 885 + } 886 + 887 + static u32 oa_tc6_get_rx_chunk_footer(struct oa_tc6 *tc6, u16 footer_offset) 888 + { 889 + u8 *rx_buf = tc6->spi_data_rx_buf; 890 + __be32 footer; 891 + 892 + footer = *((__be32 *)&rx_buf[footer_offset]); 893 + 894 + return be32_to_cpu(footer); 895 + } 896 + 897 + static int oa_tc6_process_spi_data_rx_buf(struct oa_tc6 *tc6, u16 length) 898 + { 899 + u16 no_of_rx_chunks = length / OA_TC6_CHUNK_SIZE; 900 + u32 footer; 901 + int ret; 902 + 903 + /* All the rx chunks in the receive SPI data buffer are examined here */ 904 + for (int i = 0; i < no_of_rx_chunks; i++) { 905 + /* Last 4 bytes in each received chunk consist footer info */ 906 + footer = oa_tc6_get_rx_chunk_footer(tc6, i * OA_TC6_CHUNK_SIZE + 907 + OA_TC6_CHUNK_PAYLOAD_SIZE); 908 + 909 + ret = oa_tc6_process_rx_chunk_footer(tc6, footer); 910 + if (ret) 911 + return ret; 912 + 913 + /* If there is a data valid chunks then process it for the 914 + * information needed to determine the validity and the location 915 + * of the receive frame data. 916 + */ 917 + if (FIELD_GET(OA_TC6_DATA_FOOTER_DATA_VALID, footer)) { 918 + u8 *payload = tc6->spi_data_rx_buf + i * 919 + OA_TC6_CHUNK_SIZE; 920 + 921 + ret = oa_tc6_prcs_rx_chunk_payload(tc6, payload, 922 + footer); 923 + if (ret) 924 + return ret; 925 + } 926 + } 927 + 928 + return 0; 929 + } 930 + 931 + static __be32 oa_tc6_prepare_data_header(bool data_valid, bool start_valid, 932 + bool end_valid, u8 end_byte_offset) 933 + { 934 + u32 header = FIELD_PREP(OA_TC6_DATA_HEADER_DATA_NOT_CTRL, 935 + OA_TC6_DATA_HEADER) | 936 + FIELD_PREP(OA_TC6_DATA_HEADER_DATA_VALID, data_valid) | 937 + FIELD_PREP(OA_TC6_DATA_HEADER_START_VALID, start_valid) | 938 + FIELD_PREP(OA_TC6_DATA_HEADER_END_VALID, end_valid) | 939 + FIELD_PREP(OA_TC6_DATA_HEADER_END_BYTE_OFFSET, 940 + end_byte_offset); 941 + 942 + header |= FIELD_PREP(OA_TC6_DATA_HEADER_PARITY, 943 + oa_tc6_get_parity(header)); 944 + 945 + return cpu_to_be32(header); 946 + } 947 + 948 + static void oa_tc6_add_tx_skb_to_spi_buf(struct oa_tc6 *tc6) 949 + { 950 + enum oa_tc6_data_end_valid_info end_valid = OA_TC6_DATA_END_INVALID; 951 + __be32 *tx_buf = tc6->spi_data_tx_buf + tc6->spi_data_tx_buf_offset; 952 + u16 remaining_len = tc6->ongoing_tx_skb->len - tc6->tx_skb_offset; 953 + u8 *tx_skb_data = tc6->ongoing_tx_skb->data + tc6->tx_skb_offset; 954 + enum oa_tc6_data_start_valid_info start_valid; 955 + u8 end_byte_offset = 0; 956 + u16 length_to_copy; 957 + 958 + /* Initial value is assigned here to avoid more than 80 characters in 959 + * the declaration place. 960 + */ 961 + start_valid = OA_TC6_DATA_START_INVALID; 962 + 963 + /* Set start valid if the current tx chunk contains the start of the tx 964 + * ethernet frame. 965 + */ 966 + if (!tc6->tx_skb_offset) 967 + start_valid = OA_TC6_DATA_START_VALID; 968 + 969 + /* If the remaining tx skb length is more than the chunk payload size of 970 + * 64 bytes then copy only 64 bytes and leave the ongoing tx skb for 971 + * next tx chunk. 972 + */ 973 + length_to_copy = min_t(u16, remaining_len, OA_TC6_CHUNK_PAYLOAD_SIZE); 974 + 975 + /* Copy the tx skb data to the tx chunk payload buffer */ 976 + memcpy(tx_buf + 1, tx_skb_data, length_to_copy); 977 + tc6->tx_skb_offset += length_to_copy; 978 + 979 + /* Set end valid if the current tx chunk contains the end of the tx 980 + * ethernet frame. 981 + */ 982 + if (tc6->ongoing_tx_skb->len == tc6->tx_skb_offset) { 983 + end_valid = OA_TC6_DATA_END_VALID; 984 + end_byte_offset = length_to_copy - 1; 985 + tc6->tx_skb_offset = 0; 986 + tc6->netdev->stats.tx_bytes += tc6->ongoing_tx_skb->len; 987 + tc6->netdev->stats.tx_packets++; 988 + kfree_skb(tc6->ongoing_tx_skb); 989 + tc6->ongoing_tx_skb = NULL; 990 + } 991 + 992 + *tx_buf = oa_tc6_prepare_data_header(OA_TC6_DATA_VALID, start_valid, 993 + end_valid, end_byte_offset); 994 + tc6->spi_data_tx_buf_offset += OA_TC6_CHUNK_SIZE; 995 + } 996 + 997 + static u16 oa_tc6_prepare_spi_tx_buf_for_tx_skbs(struct oa_tc6 *tc6) 998 + { 999 + u16 used_tx_credits; 1000 + 1001 + /* Get tx skbs and convert them into tx chunks based on the tx credits 1002 + * available. 1003 + */ 1004 + for (used_tx_credits = 0; used_tx_credits < tc6->tx_credits; 1005 + used_tx_credits++) { 1006 + if (!tc6->ongoing_tx_skb) { 1007 + tc6->ongoing_tx_skb = tc6->waiting_tx_skb; 1008 + tc6->waiting_tx_skb = NULL; 1009 + } 1010 + if (!tc6->ongoing_tx_skb) 1011 + break; 1012 + oa_tc6_add_tx_skb_to_spi_buf(tc6); 1013 + } 1014 + 1015 + return used_tx_credits * OA_TC6_CHUNK_SIZE; 1016 + } 1017 + 1018 + static void oa_tc6_add_empty_chunks_to_spi_buf(struct oa_tc6 *tc6, 1019 + u16 needed_empty_chunks) 1020 + { 1021 + __be32 header; 1022 + 1023 + header = oa_tc6_prepare_data_header(OA_TC6_DATA_INVALID, 1024 + OA_TC6_DATA_START_INVALID, 1025 + OA_TC6_DATA_END_INVALID, 0); 1026 + 1027 + while (needed_empty_chunks--) { 1028 + __be32 *tx_buf = tc6->spi_data_tx_buf + 1029 + tc6->spi_data_tx_buf_offset; 1030 + 1031 + *tx_buf = header; 1032 + tc6->spi_data_tx_buf_offset += OA_TC6_CHUNK_SIZE; 1033 + } 1034 + } 1035 + 1036 + static u16 oa_tc6_prepare_spi_tx_buf_for_rx_chunks(struct oa_tc6 *tc6, u16 len) 1037 + { 1038 + u16 tx_chunks = len / OA_TC6_CHUNK_SIZE; 1039 + u16 needed_empty_chunks; 1040 + 1041 + /* If there are more chunks to receive than to transmit, we need to add 1042 + * enough empty tx chunks to allow the reception of the excess rx 1043 + * chunks. 1044 + */ 1045 + if (tx_chunks >= tc6->rx_chunks_available) 1046 + return len; 1047 + 1048 + needed_empty_chunks = tc6->rx_chunks_available - tx_chunks; 1049 + 1050 + oa_tc6_add_empty_chunks_to_spi_buf(tc6, needed_empty_chunks); 1051 + 1052 + return needed_empty_chunks * OA_TC6_CHUNK_SIZE + len; 1053 + } 1054 + 1055 + static int oa_tc6_try_spi_transfer(struct oa_tc6 *tc6) 1056 + { 1057 + int ret; 1058 + 1059 + while (true) { 1060 + u16 spi_len = 0; 1061 + 1062 + tc6->spi_data_tx_buf_offset = 0; 1063 + 1064 + if (tc6->ongoing_tx_skb || tc6->waiting_tx_skb) 1065 + spi_len = oa_tc6_prepare_spi_tx_buf_for_tx_skbs(tc6); 1066 + 1067 + spi_len = oa_tc6_prepare_spi_tx_buf_for_rx_chunks(tc6, spi_len); 1068 + 1069 + if (tc6->int_flag) { 1070 + tc6->int_flag = false; 1071 + if (spi_len == 0) { 1072 + oa_tc6_add_empty_chunks_to_spi_buf(tc6, 1); 1073 + spi_len = OA_TC6_CHUNK_SIZE; 1074 + } 1075 + } 1076 + 1077 + if (spi_len == 0) 1078 + break; 1079 + 1080 + ret = oa_tc6_spi_transfer(tc6, OA_TC6_DATA_HEADER, spi_len); 1081 + if (ret) { 1082 + netdev_err(tc6->netdev, "SPI data transfer failed: %d\n", 1083 + ret); 1084 + return ret; 1085 + } 1086 + 1087 + ret = oa_tc6_process_spi_data_rx_buf(tc6, spi_len); 1088 + if (ret) { 1089 + if (ret == -EAGAIN) 1090 + continue; 1091 + 1092 + oa_tc6_cleanup_ongoing_tx_skb(tc6); 1093 + oa_tc6_cleanup_ongoing_rx_skb(tc6); 1094 + netdev_err(tc6->netdev, "Device error: %d\n", ret); 1095 + return ret; 1096 + } 1097 + 1098 + if (!tc6->waiting_tx_skb && netif_queue_stopped(tc6->netdev)) 1099 + netif_wake_queue(tc6->netdev); 1100 + } 1101 + 1102 + return 0; 1103 + } 1104 + 1105 + static int oa_tc6_spi_thread_handler(void *data) 1106 + { 1107 + struct oa_tc6 *tc6 = data; 1108 + int ret; 1109 + 1110 + while (likely(!kthread_should_stop())) { 1111 + /* This kthread will be waken up if there is a tx skb or mac-phy 1112 + * interrupt to perform spi transfer with tx chunks. 1113 + */ 1114 + wait_event_interruptible(tc6->spi_wq, tc6->waiting_tx_skb || 1115 + tc6->int_flag || 1116 + kthread_should_stop()); 1117 + 1118 + if (kthread_should_stop()) 1119 + break; 1120 + 1121 + ret = oa_tc6_try_spi_transfer(tc6); 1122 + if (ret) 1123 + return ret; 1124 + } 1125 + 1126 + return 0; 1127 + } 1128 + 1129 + static int oa_tc6_update_buffer_status_from_register(struct oa_tc6 *tc6) 1130 + { 1131 + u32 value; 1132 + int ret; 1133 + 1134 + /* Initially tx credits and rx chunks available to be updated from the 1135 + * register as there is no data transfer performed yet. Later they will 1136 + * be updated from the rx footer. 1137 + */ 1138 + ret = oa_tc6_read_register(tc6, OA_TC6_REG_BUFFER_STATUS, &value); 1139 + if (ret) 1140 + return ret; 1141 + 1142 + tc6->tx_credits = FIELD_GET(BUFFER_STATUS_TX_CREDITS_AVAILABLE, value); 1143 + tc6->rx_chunks_available = FIELD_GET(BUFFER_STATUS_RX_CHUNKS_AVAILABLE, 1144 + value); 1145 + 1146 + return 0; 1147 + } 1148 + 1149 + static irqreturn_t oa_tc6_macphy_isr(int irq, void *data) 1150 + { 1151 + struct oa_tc6 *tc6 = data; 1152 + 1153 + /* MAC-PHY interrupt can occur for the following reasons. 1154 + * - availability of tx credits if it was 0 before and not reported in 1155 + * the previous rx footer. 1156 + * - availability of rx chunks if it was 0 before and not reported in 1157 + * the previous rx footer. 1158 + * - extended status event not reported in the previous rx footer. 1159 + */ 1160 + tc6->int_flag = true; 1161 + /* Wake spi kthread to perform spi transfer */ 1162 + wake_up_interruptible(&tc6->spi_wq); 1163 + 1164 + return IRQ_HANDLED; 1165 + } 1166 + 1167 + /** 1168 + * oa_tc6_zero_align_receive_frame_enable - function to enable zero align 1169 + * receive frame feature. 1170 + * @tc6: oa_tc6 struct. 1171 + * 1172 + * Return: 0 on success otherwise failed. 1173 + */ 1174 + int oa_tc6_zero_align_receive_frame_enable(struct oa_tc6 *tc6) 1175 + { 1176 + u32 regval; 1177 + int ret; 1178 + 1179 + ret = oa_tc6_read_register(tc6, OA_TC6_REG_CONFIG0, &regval); 1180 + if (ret) 1181 + return ret; 1182 + 1183 + /* Set Zero-Align Receive Frame Enable */ 1184 + regval |= CONFIG0_ZARFE_ENABLE; 1185 + 1186 + return oa_tc6_write_register(tc6, OA_TC6_REG_CONFIG0, regval); 1187 + } 1188 + EXPORT_SYMBOL_GPL(oa_tc6_zero_align_receive_frame_enable); 1189 + 1190 + /** 1191 + * oa_tc6_start_xmit - function for sending the tx skb which consists ethernet 1192 + * frame. 1193 + * @tc6: oa_tc6 struct. 1194 + * @skb: socket buffer in which the ethernet frame is stored. 1195 + * 1196 + * Return: NETDEV_TX_OK if the transmit ethernet frame skb added in the tx_skb_q 1197 + * otherwise returns NETDEV_TX_BUSY. 1198 + */ 1199 + netdev_tx_t oa_tc6_start_xmit(struct oa_tc6 *tc6, struct sk_buff *skb) 1200 + { 1201 + if (tc6->waiting_tx_skb) { 1202 + netif_stop_queue(tc6->netdev); 1203 + return NETDEV_TX_BUSY; 1204 + } 1205 + 1206 + if (skb_linearize(skb)) { 1207 + dev_kfree_skb_any(skb); 1208 + tc6->netdev->stats.tx_dropped++; 1209 + return NETDEV_TX_OK; 1210 + } 1211 + 1212 + tc6->waiting_tx_skb = skb; 1213 + 1214 + /* Wake spi kthread to perform spi transfer */ 1215 + wake_up_interruptible(&tc6->spi_wq); 1216 + 1217 + return NETDEV_TX_OK; 1218 + } 1219 + EXPORT_SYMBOL_GPL(oa_tc6_start_xmit); 1220 + 1221 + /** 1222 + * oa_tc6_init - allocates and initializes oa_tc6 structure. 1223 + * @spi: device with which data will be exchanged. 1224 + * @netdev: network device interface structure. 1225 + * 1226 + * Return: pointer reference to the oa_tc6 structure if the MAC-PHY 1227 + * initialization is successful otherwise NULL. 1228 + */ 1229 + struct oa_tc6 *oa_tc6_init(struct spi_device *spi, struct net_device *netdev) 1230 + { 1231 + struct oa_tc6 *tc6; 1232 + int ret; 1233 + 1234 + tc6 = devm_kzalloc(&spi->dev, sizeof(*tc6), GFP_KERNEL); 1235 + if (!tc6) 1236 + return NULL; 1237 + 1238 + tc6->spi = spi; 1239 + tc6->netdev = netdev; 1240 + SET_NETDEV_DEV(netdev, &spi->dev); 1241 + mutex_init(&tc6->spi_ctrl_lock); 1242 + 1243 + /* Set the SPI controller to pump at realtime priority */ 1244 + tc6->spi->rt = true; 1245 + spi_setup(tc6->spi); 1246 + 1247 + tc6->spi_ctrl_tx_buf = devm_kzalloc(&tc6->spi->dev, 1248 + OA_TC6_CTRL_SPI_BUF_SIZE, 1249 + GFP_KERNEL); 1250 + if (!tc6->spi_ctrl_tx_buf) 1251 + return NULL; 1252 + 1253 + tc6->spi_ctrl_rx_buf = devm_kzalloc(&tc6->spi->dev, 1254 + OA_TC6_CTRL_SPI_BUF_SIZE, 1255 + GFP_KERNEL); 1256 + if (!tc6->spi_ctrl_rx_buf) 1257 + return NULL; 1258 + 1259 + tc6->spi_data_tx_buf = devm_kzalloc(&tc6->spi->dev, 1260 + OA_TC6_SPI_DATA_BUF_SIZE, 1261 + GFP_KERNEL); 1262 + if (!tc6->spi_data_tx_buf) 1263 + return NULL; 1264 + 1265 + tc6->spi_data_rx_buf = devm_kzalloc(&tc6->spi->dev, 1266 + OA_TC6_SPI_DATA_BUF_SIZE, 1267 + GFP_KERNEL); 1268 + if (!tc6->spi_data_rx_buf) 1269 + return NULL; 1270 + 1271 + ret = oa_tc6_sw_reset_macphy(tc6); 1272 + if (ret) { 1273 + dev_err(&tc6->spi->dev, 1274 + "MAC-PHY software reset failed: %d\n", ret); 1275 + return NULL; 1276 + } 1277 + 1278 + ret = oa_tc6_unmask_macphy_error_interrupts(tc6); 1279 + if (ret) { 1280 + dev_err(&tc6->spi->dev, 1281 + "MAC-PHY error interrupts unmask failed: %d\n", ret); 1282 + return NULL; 1283 + } 1284 + 1285 + ret = oa_tc6_phy_init(tc6); 1286 + if (ret) { 1287 + dev_err(&tc6->spi->dev, 1288 + "MAC internal PHY initialization failed: %d\n", ret); 1289 + return NULL; 1290 + } 1291 + 1292 + ret = oa_tc6_enable_data_transfer(tc6); 1293 + if (ret) { 1294 + dev_err(&tc6->spi->dev, "Failed to enable data transfer: %d\n", 1295 + ret); 1296 + goto phy_exit; 1297 + } 1298 + 1299 + ret = oa_tc6_update_buffer_status_from_register(tc6); 1300 + if (ret) { 1301 + dev_err(&tc6->spi->dev, 1302 + "Failed to update buffer status: %d\n", ret); 1303 + goto phy_exit; 1304 + } 1305 + 1306 + init_waitqueue_head(&tc6->spi_wq); 1307 + 1308 + tc6->spi_thread = kthread_run(oa_tc6_spi_thread_handler, tc6, 1309 + "oa-tc6-spi-thread"); 1310 + if (IS_ERR(tc6->spi_thread)) { 1311 + dev_err(&tc6->spi->dev, "Failed to create SPI thread\n"); 1312 + goto phy_exit; 1313 + } 1314 + 1315 + sched_set_fifo(tc6->spi_thread); 1316 + 1317 + ret = devm_request_irq(&tc6->spi->dev, tc6->spi->irq, oa_tc6_macphy_isr, 1318 + IRQF_TRIGGER_FALLING, dev_name(&tc6->spi->dev), 1319 + tc6); 1320 + if (ret) { 1321 + dev_err(&tc6->spi->dev, "Failed to request macphy isr %d\n", 1322 + ret); 1323 + goto kthread_stop; 1324 + } 1325 + 1326 + /* oa_tc6_sw_reset_macphy() function resets and clears the MAC-PHY reset 1327 + * complete status. IRQ is also asserted on reset completion and it is 1328 + * remain asserted until MAC-PHY receives a data chunk. So performing an 1329 + * empty data chunk transmission will deassert the IRQ. Refer section 1330 + * 7.7 and 9.2.8.8 in the OPEN Alliance specification for more details. 1331 + */ 1332 + tc6->int_flag = true; 1333 + wake_up_interruptible(&tc6->spi_wq); 1334 + 1335 + return tc6; 1336 + 1337 + kthread_stop: 1338 + kthread_stop(tc6->spi_thread); 1339 + phy_exit: 1340 + oa_tc6_phy_exit(tc6); 1341 + return NULL; 1342 + } 1343 + EXPORT_SYMBOL_GPL(oa_tc6_init); 1344 + 1345 + /** 1346 + * oa_tc6_exit - exit function. 1347 + * @tc6: oa_tc6 struct. 1348 + */ 1349 + void oa_tc6_exit(struct oa_tc6 *tc6) 1350 + { 1351 + oa_tc6_phy_exit(tc6); 1352 + kthread_stop(tc6->spi_thread); 1353 + dev_kfree_skb_any(tc6->ongoing_tx_skb); 1354 + dev_kfree_skb_any(tc6->waiting_tx_skb); 1355 + dev_kfree_skb_any(tc6->rx_skb); 1356 + } 1357 + EXPORT_SYMBOL_GPL(oa_tc6_exit); 1358 + 1359 + MODULE_DESCRIPTION("OPEN Alliance 10BASE‑T1x MAC‑PHY Serial Interface Lib"); 1360 + MODULE_AUTHOR("Parthiban Veerasooran <parthiban.veerasooran@microchip.com>"); 1361 + MODULE_LICENSE("GPL");
+30
drivers/net/phy/microchip_t1s.c
··· 268 268 return 0; 269 269 } 270 270 271 + /* OPEN Alliance 10BASE-T1x compliance MAC-PHYs will have both C22 and 272 + * C45 registers space. If the PHY is discovered via C22 bus protocol it assumes 273 + * it uses C22 protocol and always uses C22 registers indirect access to access 274 + * C45 registers. This is because, we don't have a clean separation between 275 + * C22/C45 register space and C22/C45 MDIO bus protocols. Resulting, PHY C45 276 + * registers direct access can't be used which can save multiple SPI bus access. 277 + * To support this feature, set .read_mmd/.write_mmd in the PHY driver to call 278 + * .read_c45/.write_c45 in the OPEN Alliance framework 279 + * drivers/net/ethernet/oa_tc6.c 280 + */ 281 + static int lan865x_phy_read_mmd(struct phy_device *phydev, int devnum, 282 + u16 regnum) 283 + { 284 + struct mii_bus *bus = phydev->mdio.bus; 285 + int addr = phydev->mdio.addr; 286 + 287 + return __mdiobus_c45_read(bus, addr, devnum, regnum); 288 + } 289 + 290 + static int lan865x_phy_write_mmd(struct phy_device *phydev, int devnum, 291 + u16 regnum, u16 val) 292 + { 293 + struct mii_bus *bus = phydev->mdio.bus; 294 + int addr = phydev->mdio.addr; 295 + 296 + return __mdiobus_c45_write(bus, addr, devnum, regnum, val); 297 + } 298 + 271 299 static struct phy_driver microchip_t1s_driver[] = { 272 300 { 273 301 PHY_ID_MATCH_EXACT(PHY_ID_LAN867X_REVB1), ··· 313 285 .features = PHY_BASIC_T1S_P2MP_FEATURES, 314 286 .config_init = lan865x_revb0_config_init, 315 287 .read_status = lan86xx_read_status, 288 + .read_mmd = lan865x_phy_read_mmd, 289 + .write_mmd = lan865x_phy_write_mmd, 316 290 .get_plca_cfg = genphy_c45_plca_get_cfg, 317 291 .set_plca_cfg = genphy_c45_plca_set_cfg, 318 292 .get_plca_status = genphy_c45_plca_get_status,
+24
include/linux/oa_tc6.h
··· 1 + /* SPDX-License-Identifier: GPL-2.0+ */ 2 + /* 3 + * OPEN Alliance 10BASE‑T1x MAC‑PHY Serial Interface framework 4 + * 5 + * Link: https://opensig.org/download/document/OPEN_Alliance_10BASET1x_MAC-PHY_Serial_Interface_V1.1.pdf 6 + * 7 + * Author: Parthiban Veerasooran <parthiban.veerasooran@microchip.com> 8 + */ 9 + 10 + #include <linux/etherdevice.h> 11 + #include <linux/spi/spi.h> 12 + 13 + struct oa_tc6; 14 + 15 + struct oa_tc6 *oa_tc6_init(struct spi_device *spi, struct net_device *netdev); 16 + void oa_tc6_exit(struct oa_tc6 *tc6); 17 + int oa_tc6_write_register(struct oa_tc6 *tc6, u32 address, u32 value); 18 + int oa_tc6_write_registers(struct oa_tc6 *tc6, u32 address, u32 value[], 19 + u8 length); 20 + int oa_tc6_read_register(struct oa_tc6 *tc6, u32 address, u32 *value); 21 + int oa_tc6_read_registers(struct oa_tc6 *tc6, u32 address, u32 value[], 22 + u8 length); 23 + netdev_tx_t oa_tc6_start_xmit(struct oa_tc6 *tc6, struct sk_buff *skb); 24 + int oa_tc6_zero_align_receive_frame_enable(struct oa_tc6 *tc6);
+1
include/uapi/linux/mdio.h
··· 23 23 #define MDIO_MMD_DTEXS 5 /* DTE Extender Sublayer */ 24 24 #define MDIO_MMD_TC 6 /* Transmission Convergence */ 25 25 #define MDIO_MMD_AN 7 /* Auto-Negotiation */ 26 + #define MDIO_MMD_POWER_UNIT 13 /* PHY Power Unit */ 26 27 #define MDIO_MMD_C22EXT 29 /* Clause 22 extension */ 27 28 #define MDIO_MMD_VEND1 30 /* Vendor specific 1 */ 28 29 #define MDIO_MMD_VEND2 31 /* Vendor specific 2 */