diff --git a/Makefile b/Makefile index 55aa90cf17..07539ca596 100644 --- a/Makefile +++ b/Makefile @@ -1830,7 +1830,7 @@ define filechk_defaultenv.h (grep -v '^#' | \ grep -v '^$$' | \ tr '\n' '\0' | \ - sed -e 's/\\\x0/\n/g' | \ + sed -e 's/\\\x0\s*//g' | \ xxd -i ; echo ", 0x00" ; ) endef diff --git a/doc/README.drivers.eth b/doc/README.drivers.eth deleted file mode 100644 index 1a9a23b51b..0000000000 --- a/doc/README.drivers.eth +++ /dev/null @@ -1,215 +0,0 @@ -!!! WARNING !!! - -This guide describes to the old way of doing things. No new Ethernet drivers -should be implemented this way. All new drivers should be written against the -U-Boot core driver model. See doc/driver-model/README.txt - ------------------------ - Ethernet Driver Guide ------------------------ - -The networking stack in Das U-Boot is designed for multiple network devices -to be easily added and controlled at runtime. This guide is meant for people -who wish to review the net driver stack with an eye towards implementing your -own ethernet device driver. Here we will describe a new pseudo 'APE' driver. - ------------------- - Driver Functions ------------------- - -All functions you will be implementing in this document have the return value -meaning of 0 for success and non-zero for failure. - - ---------- - Register - ---------- - -When U-Boot initializes, it will call the common function eth_initialize(). -This will in turn call the board-specific board_eth_init() (or if that fails, -the cpu-specific cpu_eth_init()). These board-specific functions can do random -system handling, but ultimately they will call the driver-specific register -function which in turn takes care of initializing that particular instance. - -Keep in mind that you should code the driver to avoid storing state in global -data as someone might want to hook up two of the same devices to one board. -Any such information that is specific to an interface should be stored in a -private, driver-defined data structure and pointed to by eth->priv (see below). - -So the call graph at this stage would look something like: -board_init() - eth_initialize() - board_eth_init() / cpu_eth_init() - driver_register() - initialize eth_device - eth_register() - -At this point in time, the only thing you need to worry about is the driver's -register function. The pseudo code would look something like: -int ape_register(bd_t *bis, int iobase) -{ - struct ape_priv *priv; - struct eth_device *dev; - struct mii_dev *bus; - - priv = malloc(sizeof(*priv)); - if (priv == NULL) - return -ENOMEM; - - dev = malloc(sizeof(*dev)); - if (dev == NULL) { - free(priv); - return -ENOMEM; - } - - /* setup whatever private state you need */ - - memset(dev, 0, sizeof(*dev)); - sprintf(dev->name, "APE"); - - /* - * if your device has dedicated hardware storage for the - * MAC, read it and initialize dev->enetaddr with it - */ - ape_mac_read(dev->enetaddr); - - dev->iobase = iobase; - dev->priv = priv; - dev->init = ape_init; - dev->halt = ape_halt; - dev->send = ape_send; - dev->recv = ape_recv; - dev->write_hwaddr = ape_write_hwaddr; - - eth_register(dev); - -#ifdef CONFIG_PHYLIB - bus = mdio_alloc(); - if (!bus) { - free(priv); - free(dev); - return -ENOMEM; - } - - bus->read = ape_mii_read; - bus->write = ape_mii_write; - mdio_register(bus); -#endif - - return 1; -} - -The exact arguments needed to initialize your device are up to you. If you -need to pass more/less arguments, that's fine. You should also add the -prototype for your new register function to include/netdev.h. - -The return value for this function should be as follows: -< 0 - failure (hardware failure, not probe failure) ->=0 - number of interfaces detected - -You might notice that many drivers seem to use xxx_initialize() rather than -xxx_register(). This is the old naming convention and should be avoided as it -causes confusion with the driver-specific init function. - -Other than locating the MAC address in dedicated hardware storage, you should -not touch the hardware in anyway. That step is handled in the driver-specific -init function. Remember that we are only registering the device here, we are -not checking its state or doing random probing. - - ----------- - Callbacks - ----------- - -Now that we've registered with the ethernet layer, we can start getting some -real work done. You will need five functions: - int ape_init(struct eth_device *dev, bd_t *bis); - int ape_send(struct eth_device *dev, volatile void *packet, int length); - int ape_recv(struct eth_device *dev); - int ape_halt(struct eth_device *dev); - int ape_write_hwaddr(struct eth_device *dev); - -The init function checks the hardware (probing/identifying) and gets it ready -for send/recv operations. You often do things here such as resetting the MAC -and/or PHY, and waiting for the link to autonegotiate. You should also take -the opportunity to program the device's MAC address with the dev->enetaddr -member. This allows the rest of U-Boot to dynamically change the MAC address -and have the new settings be respected. - -The send function does what you think -- transmit the specified packet whose -size is specified by length (in bytes). You should not return until the -transmission is complete, and you should leave the state such that the send -function can be called multiple times in a row. - -The recv function should process packets as long as the hardware has them -readily available before returning. i.e. you should drain the hardware fifo. -For each packet you receive, you should call the net_process_received_packet() function on it -along with the packet length. The common code sets up packet buffers for you -already in the .bss (net_rx_packets), so there should be no need to allocate your -own. This doesn't mean you must use the net_rx_packets array however; you're -free to call the net_process_received_packet() function with any buffer you wish. So the pseudo -code here would look something like: -int ape_recv(struct eth_device *dev) -{ - int length, i = 0; - ... - while (packets_are_available()) { - ... - length = ape_get_packet(&net_rx_packets[i]); - ... - net_process_received_packet(&net_rx_packets[i], length); - ... - if (++i >= PKTBUFSRX) - i = 0; - ... - } - ... - return 0; -} - -The halt function should turn off / disable the hardware and place it back in -its reset state. It can be called at any time (before any call to the related -init function), so make sure it can handle this sort of thing. - -The write_hwaddr function should program the MAC address stored in dev->enetaddr -into the Ethernet controller. - -So the call graph at this stage would look something like: -some net operation (ping / tftp / whatever...) - eth_init() - dev->init() - eth_send() - dev->send() - eth_rx() - dev->recv() - eth_halt() - dev->halt() - --------------------------------- - CONFIG_PHYLIB / CONFIG_CMD_MII --------------------------------- - -If your device supports banging arbitrary values on the MII bus (pretty much -every device does), you should add support for the mii command. Doing so is -fairly trivial and makes debugging mii issues a lot easier at runtime. - -After you have called eth_register() in your driver's register function, add -a call to mdio_alloc() and mdio_register() like so: - bus = mdio_alloc(); - if (!bus) { - free(priv); - free(dev); - return -ENOMEM; - } - - bus->read = ape_mii_read; - bus->write = ape_mii_write; - mdio_register(bus); - -And then define the mii_read and mii_write functions if you haven't already. -Their syntax is straightforward: - int mii_read(struct mii_dev *bus, int addr, int devad, int reg); - int mii_write(struct mii_dev *bus, int addr, int devad, int reg, - u16 val); - -The read function should read the register 'reg' from the phy at address 'addr' -and return the result to its caller. The implementation for the write function -should logically follow. diff --git a/doc/device-tree-bindings/net/ti,dp83867.txt b/doc/device-tree-bindings/net/ti,dp83867.txt deleted file mode 100644 index 268220964a..0000000000 --- a/doc/device-tree-bindings/net/ti,dp83867.txt +++ /dev/null @@ -1,35 +0,0 @@ -* Texas Instruments - dp83867 Giga bit ethernet phy - -Required properties: - - reg - The ID number for the phy, usually a small integer - - ti,rx-internal-delay - RGMII Recieve Clock Delay - see dt-bindings/net/ti-dp83867.h - for applicable values - - ti,tx-internal-delay - RGMII Transmit Clock Delay - see dt-bindings/net/ti-dp83867.h - for applicable values - - ti,fifo-depth - Transmitt FIFO depth- see dt-bindings/net/ti-dp83867.h - for applicable values - - enet-phy-lane-swap - Indicates that PHY will swap the TX/RX lanes to - compensate for the board being designed with the lanes swapped. - - enet-phy-no-lane-swap - Indicates that PHY will disable swap of the - TX/RX lanes. - - ti,clk-output-sel - Muxing option for CLK_OUT pin. See dt-bindings/net/ti-dp83867.h - for applicable values. The CLK_OUT pin can also - be disabled by this property. When omitted, the - PHY's default will be left as is. - -Default child nodes are standard Ethernet PHY device -nodes as described in doc/devicetree/bindings/net/ethernet.txt - -Example: - - ethernet-phy@0 { - reg = <0>; - ti,rx-internal-delay = ; - ti,tx-internal-delay = ; - ti,fifo-depth = ; - enet-phy-lane-no-swap; - ti,clk-output-sel = ; - }; - -Datasheet can be found: -http://www.ti.com/product/DP83867IR/datasheet diff --git a/doc/driver-model/ethernet.rst b/doc/driver-model/ethernet.rst new file mode 100644 index 0000000000..359a0523cf --- /dev/null +++ b/doc/driver-model/ethernet.rst @@ -0,0 +1,321 @@ +Ethernet Driver Guide +======================= + +The networking stack in Das U-Boot is designed for multiple network devices +to be easily added and controlled at runtime. This guide is meant for people +who wish to review the net driver stack with an eye towards implementing your +own ethernet device driver. Here we will describe a new pseudo 'APE' driver. + +Most existing drivers do already - and new network driver MUST - use the +U-Boot core driver model. Generic information about this can be found in +doc/driver-model/design.rst, this document will thus focus on the network +specific code parts. +Some drivers are still using the old Ethernet interface, differences between +the two and hints about porting will be handled at the end. + +Driver framework +------------------ + +A network driver following the driver model must declare itself using +the UCLASS_ETH .id field in the U-Boot driver struct: + +.. code-block:: c + + U_BOOT_DRIVER(eth_ape) = { + .name = "eth_ape", + .id = UCLASS_ETH, + .of_match = eth_ape_ids, + .ofdata_to_platdata = eth_ape_ofdata_to_platdata, + .probe = eth_ape_probe, + .ops = ð_ape_ops, + .priv_auto_alloc_size = sizeof(struct eth_ape_priv), + .platdata_auto_alloc_size = sizeof(struct eth_ape_pdata), + .flags = DM_FLAG_ALLOC_PRIV_DMA, + }; + +struct eth_ape_priv contains runtime per-instance data, like buffers, pointers +to current descriptors, current speed settings, pointers to PHY related data +(like struct mii_dev) and so on. Declaring its size in .priv_auto_alloc_size +will let the driver framework allocate it at the right time. +It can be retrieved using a dev_get_priv(dev) call. + +struct eth_ape_pdata contains static platform data, like the MMIO base address, +a hardware variant, the MAC address. ``struct eth_pdata eth_pdata`` +as the first member of this struct helps to avoid duplicated code. +If you don't need any more platform data beside the standard member, +just use sizeof(struct eth_pdata) for the platdata_auto_alloc_size. + +PCI devices add a line pointing to supported vendor/device ID pairs: + +.. code-block:: c + + static struct pci_device_id supported[] = { + { PCI_DEVICE(PCI_VENDOR_ID_APE, 0x4223) }, + {} + }; + + U_BOOT_PCI_DEVICE(eth_ape, supported); + +It is also possible to declare support for a whole class of PCI devices:: + + { PCI_DEVICE_CLASS(PCI_CLASS_SYSTEM_SDHCI << 8, 0xffff00) }, + +Device probing and instantiation will be handled by the driver model framework, +so follow the guidelines there. The probe() function would initialise the +platform specific parts of the hardware, like clocks, resets, GPIOs, the MDIO +bus. Also it would take care of any special PHY setup (power rails, enable +bits for internal PHYs, etc.). + +Driver methods +---------------- + +The real work will be done in the driver method functions the driver provides +by defining the members of struct eth_ops: + +.. code-block:: c + + struct eth_ops { + int (*start)(struct udevice *dev); + int (*send)(struct udevice *dev, void *packet, int length); + int (*recv)(struct udevice *dev, int flags, uchar **packetp); + int (*free_pkt)(struct udevice *dev, uchar *packet, int length); + void (*stop)(struct udevice *dev); + int (*mcast)(struct udevice *dev, const u8 *enetaddr, int join); + int (*write_hwaddr)(struct udevice *dev); + int (*read_rom_hwaddr)(struct udevice *dev); + }; + +An up-to-date version of this struct together with more information can be +found in include/net.h. + +Only start, stop, send and recv are required, the rest are optional and are +handled by generic code or ignored if not provided. + +The **start** function initialises the hardware and gets it ready for send/recv +operations. You often do things here such as resetting the MAC +and/or PHY, and waiting for the link to autonegotiate. You should also take +the opportunity to program the device's MAC address with the enetaddr member +of the generic struct eth_pdata (which would be the first member of your +own platdata struct). This allows the rest of U-Boot to dynamically change +the MAC address and have the new settings be respected. + +The **send** function does what you think -- transmit the specified packet +whose size is specified by length (in bytes). The packet buffer can (and +will!) be reused for subsequent calls to send(), so it must be no longer +used when the send() function returns. The easiest way to achieve this is +to wait until the transmission is complete. Alternatively, if supported by +the hardware, just waiting for the buffer to be consumed (by some DMA engine) +might be an option as well. +Another way of consuming the buffer could be to copy the data to be send, +then just queue the copied packet (for instance handing it over to a DMA +engine), and return immediately afterwards. +In any case you should leave the state such that the send function can be +called multiple times in a row. + +The **recv** function polls for availability of a new packet. If none is +available, it must return with -EAGAIN. +If a packet has been received, make sure it is accessible to the CPU +(invalidate caches if needed), then write its address to the packetp pointer, +and return the length. If there is an error (receive error, too short or too +long packet), return 0 if you require the packet to be cleaned up normally, +or a negative error code otherwise (cleanup not necessary or already done). +The U-Boot network stack will then process the packet. + +If **free_pkt** is defined, U-Boot will call it after a received packet has +been processed, so the packet buffer can be freed or recycled. Typically you +would hand it back to the hardware to acquire another packet. free_pkt() will +be called after recv(), for the same packet, so you don't necessarily need +to infer the buffer to free from the ``packet`` pointer, but can rely on that +being the last packet that recv() handled. +The common code sets up packet buffers for you already in the .bss +(net_rx_packets), so there should be no need to allocate your own. This doesn't +mean you must use the net_rx_packets array however; you're free to use any +buffer you wish. + +The **stop** function should turn off / disable the hardware and place it back +in its reset state. It can be called at any time (before any call to the +related start() function), so make sure it can handle this sort of thing. + +The (optional) **write_hwaddr** function should program the MAC address stored +in pdata->enetaddr into the Ethernet controller. + +So the call graph at this stage would look something like: + +.. code-block:: c + + (some net operation (ping / tftp / whatever...)) + eth_init() + ops->start() + eth_send() + ops->send() + eth_rx() + ops->recv() + (process packet) + if (ops->free_pkt) + ops->free_pkt() + eth_halt() + ops->stop() + + +CONFIG_PHYLIB / CONFIG_CMD_MII +-------------------------------- + +If your device supports banging arbitrary values on the MII bus (pretty much +every device does), you should add support for the mii command. Doing so is +fairly trivial and makes debugging mii issues a lot easier at runtime. + +In your driver's ``probe()`` function, add a call to mdio_alloc() and +mdio_register() like so: + +.. code-block:: c + + bus = mdio_alloc(); + if (!bus) { + ... + return -ENOMEM; + } + + bus->read = ape_mii_read; + bus->write = ape_mii_write; + mdio_register(bus); + +And then define the mii_read and mii_write functions if you haven't already. +Their syntax is straightforward:: + + int mii_read(struct mii_dev *bus, int addr, int devad, int reg); + int mii_write(struct mii_dev *bus, int addr, int devad, int reg, + u16 val); + +The read function should read the register 'reg' from the phy at address 'addr' +and return the result to its caller. The implementation for the write function +should logically follow. + +................................................................ + +Legacy network drivers +------------------------ + +!!! WARNING !!! + +This section below describes the old way of doing things. No new Ethernet +drivers should be implemented this way. All new drivers should be written +against the U-Boot core driver model, as described above. + +The actual callback functions are fairly similar, the differences are: + +- ``start()`` is called ``init()`` +- ``stop()`` is called ``halt()`` +- The ``recv()`` function must loop until all packets have been received, for + each packet it must call the net_process_received_packet() function, + handing it over the pointer and the length. Afterwards it should free + the packet, before checking for new data. + +For porting an old driver to the new driver model, split the existing recv() +function into the actual new recv() function, just fetching **one** packet, +remove the call to net_process_received_packet(), then move the packet +cleanup into the ``free_pkt()`` function. + +Registering the driver and probing a device is handled very differently, +follow the recommendations in the driver model design documentation for +instructions on how to port this over. For the records, the old way of +initialising a network driver is as follows: + +Old network driver registration +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +When U-Boot initializes, it will call the common function eth_initialize(). +This will in turn call the board-specific board_eth_init() (or if that fails, +the cpu-specific cpu_eth_init()). These board-specific functions can do random +system handling, but ultimately they will call the driver-specific register +function which in turn takes care of initializing that particular instance. + +Keep in mind that you should code the driver to avoid storing state in global +data as someone might want to hook up two of the same devices to one board. +Any such information that is specific to an interface should be stored in a +private, driver-defined data structure and pointed to by eth->priv (see below). + +So the call graph at this stage would look something like: + +.. code-block:: c + + board_init() + eth_initialize() + board_eth_init() / cpu_eth_init() + driver_register() + initialize eth_device + eth_register() + +At this point in time, the only thing you need to worry about is the driver's +register function. The pseudo code would look something like: + +.. code-block:: c + + int ape_register(bd_t *bis, int iobase) + { + struct ape_priv *priv; + struct eth_device *dev; + struct mii_dev *bus; + + priv = malloc(sizeof(*priv)); + if (priv == NULL) + return -ENOMEM; + + dev = malloc(sizeof(*dev)); + if (dev == NULL) { + free(priv); + return -ENOMEM; + } + + /* setup whatever private state you need */ + + memset(dev, 0, sizeof(*dev)); + sprintf(dev->name, "APE"); + + /* + * if your device has dedicated hardware storage for the + * MAC, read it and initialize dev->enetaddr with it + */ + ape_mac_read(dev->enetaddr); + + dev->iobase = iobase; + dev->priv = priv; + dev->init = ape_init; + dev->halt = ape_halt; + dev->send = ape_send; + dev->recv = ape_recv; + dev->write_hwaddr = ape_write_hwaddr; + + eth_register(dev); + + #ifdef CONFIG_PHYLIB + bus = mdio_alloc(); + if (!bus) { + free(priv); + free(dev); + return -ENOMEM; + } + + bus->read = ape_mii_read; + bus->write = ape_mii_write; + mdio_register(bus); + #endif + + return 1; + } + +The exact arguments needed to initialize your device are up to you. If you +need to pass more/less arguments, that's fine. You should also add the +prototype for your new register function to include/netdev.h. + +The return value for this function should be as follows: +< 0 - failure (hardware failure, not probe failure) +>=0 - number of interfaces detected + +You might notice that many drivers seem to use xxx_initialize() rather than +xxx_register(). This is the old naming convention and should be avoided as it +causes confusion with the driver-specific init function. + +Other than locating the MAC address in dedicated hardware storage, you should +not touch the hardware in anyway. That step is handled in the driver-specific +init function. Remember that we are only registering the device here, we are +not checking its state or doing random probing. diff --git a/doc/driver-model/index.rst b/doc/driver-model/index.rst index 6d55774b4c..b9df221627 100644 --- a/doc/driver-model/index.rst +++ b/doc/driver-model/index.rst @@ -8,6 +8,7 @@ Driver Model debugging design + ethernet fdt-fixup fs_firmware_loader i2c-howto diff --git a/drivers/net/phy/Kconfig b/drivers/net/phy/Kconfig index dceea1516f..d1f049e62a 100644 --- a/drivers/net/phy/Kconfig +++ b/drivers/net/phy/Kconfig @@ -267,4 +267,8 @@ config PHY_FIXED on, the link is always up with fixed speed and fixed duplex-setting. More information: doc/device-tree-bindings/net/fixed-link.txt +config PHY_NCSI + bool "NC-SI based PHY" + depends on DM_ETH + endif #PHYLIB diff --git a/drivers/net/phy/Makefile b/drivers/net/phy/Makefile index 78955c57a8..1d81516ecd 100644 --- a/drivers/net/phy/Makefile +++ b/drivers/net/phy/Makefile @@ -31,3 +31,4 @@ obj-$(CONFIG_PHY_XILINX_GMII2RGMII) += xilinx_gmii2rgmii.o obj-$(CONFIG_PHY_VITESSE) += vitesse.o obj-$(CONFIG_PHY_MSCC) += mscc.o obj-$(CONFIG_PHY_FIXED) += fixed.o +obj-$(CONFIG_PHY_NCSI) += ncsi.o diff --git a/drivers/net/phy/aquantia.c b/drivers/net/phy/aquantia.c index c4bd443001..8ece926dd3 100644 --- a/drivers/net/phy/aquantia.c +++ b/drivers/net/phy/aquantia.c @@ -306,30 +306,29 @@ struct { AQUANTIA_VND1_GSTART_RATE_1G}, [PHY_INTERFACE_MODE_SGMII_2500] = {0x144, AQUANTIA_VND1_GSYSCFG_2_5G, AQUANTIA_VND1_GSTART_RATE_2_5G}, - [PHY_INTERFACE_MODE_XGMII] = {0x100, AQUANTIA_VND1_GSYSCFG_10G, - AQUANTIA_VND1_GSTART_RATE_10G}, [PHY_INTERFACE_MODE_XFI] = {0x100, AQUANTIA_VND1_GSYSCFG_10G, AQUANTIA_VND1_GSTART_RATE_10G}, [PHY_INTERFACE_MODE_USXGMII] = {0x080, AQUANTIA_VND1_GSYSCFG_10G, AQUANTIA_VND1_GSTART_RATE_10G}, }; -static int aquantia_set_proto(struct phy_device *phydev) +static int aquantia_set_proto(struct phy_device *phydev, + phy_interface_t interface) { int i; - if (!aquantia_syscfg[phydev->interface].cnt) + if (!aquantia_syscfg[interface].cnt) return 0; /* set the default rate to enable the SI link */ phy_write(phydev, MDIO_MMD_VEND1, AQUANTIA_VND1_GSTART_RATE, - aquantia_syscfg[phydev->interface].start_rate); + aquantia_syscfg[interface].start_rate); /* set selected protocol for all relevant line side link speeds */ - for (i = 0; i <= aquantia_syscfg[phydev->interface].cnt; i++) + for (i = 0; i <= aquantia_syscfg[interface].cnt; i++) phy_write(phydev, MDIO_MMD_VEND1, AQUANTIA_VND1_GSYSCFG_BASE + i, - aquantia_syscfg[phydev->interface].syscfg); + aquantia_syscfg[interface].syscfg); return 0; } @@ -425,9 +424,9 @@ int aquantia_config(struct phy_device *phydev) fault = phy_read(phydev, MDIO_MMD_VEND1, GLOBAL_FAULT); if (id != 0) - printf("%s running firmware version %X.%X.%X\n", - phydev->dev->name, (id >> 8), id & 0xff, - (rstatus >> 4) & 0xf); + debug("%s running firmware version %X.%X.%X\n", + phydev->dev->name, (id >> 8), id & 0xff, + (rstatus >> 4) & 0xf); if (fault != 0) printf("%s fault 0x%04x detected\n", phydev->dev->name, fault); @@ -444,6 +443,8 @@ int aquantia_config(struct phy_device *phydev) * on FW config */ if (interface == PHY_INTERFACE_MODE_XGMII) { + debug("use XFI or USXGMII SI protos, XGMII is not valid\n"); + reg_val1 = phy_read(phydev, MDIO_MMD_PHYXS, AQUANTIA_SYSTEM_INTERFACE_SR); if ((reg_val1 & AQUANTIA_SI_IN_USE_MASK) == AQUANTIA_SI_USXGMII) @@ -466,7 +467,7 @@ int aquantia_config(struct phy_device *phydev) mdelay(10); /* configure protocol based on phydev->interface */ - aquantia_set_proto(phydev); + aquantia_set_proto(phydev, interface); /* apply custom configuration based on DT */ aquantia_dts_config(phydev); @@ -506,12 +507,12 @@ int aquantia_config(struct phy_device *phydev) if (usx_an) { reg_val1 |= AQUANTIA_USX_AUTONEG_CONTROL_ENA; - printf("%s: system interface USXGMII\n", - phydev->dev->name); + debug("%s: system interface USXGMII\n", + phydev->dev->name); } else { reg_val1 &= ~AQUANTIA_USX_AUTONEG_CONTROL_ENA; - printf("%s: system interface XFI\n", - phydev->dev->name); + debug("%s: system interface XFI\n", + phydev->dev->name); } phy_write(phydev, MDIO_MMD_PHYXS, @@ -538,11 +539,11 @@ int aquantia_config(struct phy_device *phydev) val = phy_read(phydev, MDIO_MMD_VEND1, AQUANTIA_RESERVED_STATUS); reg_val1 = phy_read(phydev, MDIO_MMD_VEND1, AQUANTIA_FIRMWARE_ID); - printf("%s: %s Firmware Version %x.%x.%x\n", phydev->dev->name, - phydev->drv->name, - (reg_val1 & AQUANTIA_FIRMWARE_MAJOR_MASK) >> 8, - reg_val1 & AQUANTIA_FIRMWARE_MINOR_MASK, - (val & AQUANTIA_FIRMWARE_BUILD_MASK) >> 4); + debug("%s: %s Firmware Version %x.%x.%x\n", phydev->dev->name, + phydev->drv->name, + (reg_val1 & AQUANTIA_FIRMWARE_MAJOR_MASK) >> 8, + reg_val1 & AQUANTIA_FIRMWARE_MINOR_MASK, + (val & AQUANTIA_FIRMWARE_BUILD_MASK) >> 4); return 0; } diff --git a/drivers/net/phy/dp83867.c b/drivers/net/phy/dp83867.c index 0098997c0c..50804c130e 100644 --- a/drivers/net/phy/dp83867.c +++ b/drivers/net/phy/dp83867.c @@ -29,6 +29,7 @@ #define DP83867_STRAP_STS2 0x006f #define DP83867_RGMIIDCTL 0x0086 #define DP83867_IO_MUX_CFG 0x0170 +#define DP83867_SGMIICTL 0x00D3 #define DP83867_SW_RESET BIT(15) #define DP83867_SW_RESTART BIT(14) @@ -101,6 +102,9 @@ /* CFG4 bits */ #define DP83867_CFG4_PORT_MIRROR_EN BIT(0) +/* SGMIICTL bits */ +#define DP83867_SGMII_TYPE BIT(14) + enum { DP83867_PORT_MIRRORING_KEEP, DP83867_PORT_MIRRORING_EN, @@ -116,6 +120,7 @@ struct dp83867_private { int port_mirroring; bool set_clk_output; unsigned int clk_output_sel; + bool sgmii_ref_clk_en; }; static int dp83867_config_port_mirroring(struct phy_device *phydev) @@ -236,6 +241,9 @@ static int dp83867_of_init(struct phy_device *phydev) if (ofnode_read_bool(node, "enet-phy-lane-no-swap")) dp83867->port_mirroring = DP83867_PORT_MIRRORING_DIS; + if (ofnode_read_bool(node, "ti,sgmii-ref-clock-output-enable")) + dp83867->sgmii_ref_clk_en = true; + return 0; } #else @@ -331,6 +339,10 @@ static int dp83867_config(struct phy_device *phydev) } if (phy_interface_is_sgmii(phydev)) { + if (dp83867->sgmii_ref_clk_en) + phy_write_mmd(phydev, DP83867_DEVADDR, DP83867_SGMIICTL, + DP83867_SGMII_TYPE); + phy_write(phydev, MDIO_DEVAD_NONE, MII_BMCR, (BMCR_ANENABLE | BMCR_FULLDPLX | BMCR_SPEED1000)); diff --git a/drivers/net/phy/marvell.c b/drivers/net/phy/marvell.c index efbbd31ff7..93cf44ad4c 100644 --- a/drivers/net/phy/marvell.c +++ b/drivers/net/phy/marvell.c @@ -303,9 +303,9 @@ static int m88e1111s_config(struct phy_device *phydev) } /** - * m88e1518_phy_writebits - write bits to a register + * m88e151x_phy_writebits - write bits to a register */ -void m88e1518_phy_writebits(struct phy_device *phydev, +void m88e151x_phy_writebits(struct phy_device *phydev, u8 reg_num, u16 offset, u16 len, u16 data) { u16 reg, mask; @@ -323,7 +323,7 @@ void m88e1518_phy_writebits(struct phy_device *phydev, phy_write(phydev, MDIO_DEVAD_NONE, reg_num, reg); } -static int m88e1518_config(struct phy_device *phydev) +static int m88e151x_config(struct phy_device *phydev) { u16 reg; @@ -350,11 +350,11 @@ static int m88e1518_config(struct phy_device *phydev) phy_write(phydev, MDIO_DEVAD_NONE, MIIM_88E1118_PHY_PAGE, 18); /* In reg 20, write MODE[2:0] = 0x1 (SGMII to Copper) */ - m88e1518_phy_writebits(phydev, MIIM_88E151x_GENERAL_CTRL, + m88e151x_phy_writebits(phydev, MIIM_88E151x_GENERAL_CTRL, 0, 3, MIIM_88E151x_MODE_SGMII); /* PHY reset is necessary after changing MODE[2:0] */ - m88e1518_phy_writebits(phydev, MIIM_88E151x_GENERAL_CTRL, + m88e151x_phy_writebits(phydev, MIIM_88E151x_GENERAL_CTRL, MIIM_88E151x_RESET_OFFS, 1, 1); /* Reset page selection */ @@ -401,33 +401,6 @@ static int m88e1518_config(struct phy_device *phydev) return 0; } -/* Marvell 88E1510 */ -static int m88e1510_config(struct phy_device *phydev) -{ - /* Select page 3 */ - phy_write(phydev, MDIO_DEVAD_NONE, MIIM_88E1118_PHY_PAGE, - MIIM_88E1118_PHY_LED_PAGE); - - /* Enable INTn output on LED[2] */ - m88e1518_phy_writebits(phydev, MIIM_88E151x_LED_TIMER_CTRL, - MIIM_88E151x_INT_EN_OFFS, 1, 1); - - /* Configure LEDs */ - /* LED[0]:0011 (ACT) */ - m88e1518_phy_writebits(phydev, MIIM_88E151x_LED_FUNC_CTRL, - MIIM_88E151x_LED0_OFFS, MIIM_88E151x_LED_FLD_SZ, - MIIM_88E151x_LED0_ACT); - /* LED[1]:0110 (LINK 100/1000 Mbps) */ - m88e1518_phy_writebits(phydev, MIIM_88E151x_LED_FUNC_CTRL, - MIIM_88E151x_LED1_OFFS, MIIM_88E151x_LED_FLD_SZ, - MIIM_88E151x_LED1_100_1000_LINK); - - /* Reset page selection */ - phy_write(phydev, MDIO_DEVAD_NONE, MIIM_88E1118_PHY_PAGE, 0); - - return m88e1518_config(phydev); -} - /* Marvell 88E1118 */ static int m88e1118_config(struct phy_device *phydev) { @@ -685,29 +658,12 @@ static struct phy_driver M88E1149S_driver = { .shutdown = &genphy_shutdown, }; -static struct phy_driver M88E1510_driver = { - .name = "Marvell 88E1510", +static struct phy_driver M88E151x_driver = { + .name = "Marvell 88E151x", .uid = 0x1410dd0, - .mask = 0xfffffff, + .mask = 0xffffff0, .features = PHY_GBIT_FEATURES, - .config = &m88e1510_config, - .startup = &m88e1011s_startup, - .shutdown = &genphy_shutdown, - .readext = &m88e1xxx_phy_extread, - .writeext = &m88e1xxx_phy_extwrite, -}; - -/* - * This supports: - * 88E1518, uid 0x1410dd1 - * 88E1512, uid 0x1410dd4 - */ -static struct phy_driver M88E1518_driver = { - .name = "Marvell 88E1518", - .uid = 0x1410dd0, - .mask = 0xffffffa, - .features = PHY_GBIT_FEATURES, - .config = &m88e1518_config, + .config = &m88e151x_config, .startup = &m88e1011s_startup, .shutdown = &genphy_shutdown, .readext = &m88e1xxx_phy_extread, @@ -744,8 +700,7 @@ int phy_marvell_init(void) phy_register(&M88E1118R_driver); phy_register(&M88E1111S_driver); phy_register(&M88E1011S_driver); - phy_register(&M88E1510_driver); - phy_register(&M88E1518_driver); + phy_register(&M88E151x_driver); phy_register(&M88E1680_driver); return 0; diff --git a/drivers/net/phy/ncsi.c b/drivers/net/phy/ncsi.c new file mode 100644 index 0000000000..adc3ac033e --- /dev/null +++ b/drivers/net/phy/ncsi.c @@ -0,0 +1,897 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * NC-SI protocol configuration + * + * Copyright (C) 2019, IBM Corporation. + */ + +#include +#include +#include +#include +#include +#include + +#define NCSI_PACKAGE_MAX 8 +#define NCSI_CHANNEL_MAX 31 + +#define NCSI_PACKAGE_SHIFT 5 +#define NCSI_PACKAGE_INDEX(c) (((c) >> NCSI_PACKAGE_SHIFT) & 0x7) +#define NCSI_RESERVED_CHANNEL 0x1f +#define NCSI_CHANNEL_INDEX(c) ((c) & ((1 << NCSI_PACKAGE_SHIFT) - 1)) +#define NCSI_TO_CHANNEL(p, c) (((p) << NCSI_PACKAGE_SHIFT) | (c)) + +#define NCSI_PKT_REVISION 0x01 + +#define NCSI_CAP_GENERIC_MASK 0x7f +#define NCSI_CAP_BC_MASK 0x0f +#define NCSI_CAP_MC_MASK 0x3f +#define NCSI_CAP_AEN_MASK 0x07 +#define NCSI_CAP_VLAN_MASK 0x07 + +static void ncsi_send_ebf(unsigned int np, unsigned int nc); +static void ncsi_send_ae(unsigned int np, unsigned int nc); +static void ncsi_send_gls(unsigned int np, unsigned int nc); +static int ncsi_send_command(unsigned int np, unsigned int nc, unsigned int cmd, + uchar *payload, int len, bool wait); + +struct ncsi_channel { + unsigned int id; + bool has_link; + + /* capabilities */ + u32 cap_generic; + u32 cap_bc; + u32 cap_mc; + u32 cap_buffer; + u32 cap_aen; + u32 cap_vlan; + + /* version information */ + struct { + u32 version; /* Supported BCD encoded NCSI version */ + u32 alpha2; /* Supported BCD encoded NCSI version */ + u8 fw_name[12]; /* Firmware name string */ + u32 fw_version; /* Firmware version */ + u16 pci_ids[4]; /* PCI identification */ + u32 mf_id; /* Manufacture ID */ + } version; + +}; + +struct ncsi_package { + unsigned int id; + unsigned int n_channels; + struct ncsi_channel *channels; +}; + +struct ncsi { + enum { + NCSI_PROBE_PACKAGE_SP, + NCSI_PROBE_PACKAGE_DP, + NCSI_PROBE_CHANNEL_SP, + NCSI_PROBE_CHANNEL, + NCSI_CONFIG, + } state; + + unsigned int pending_requests; + unsigned int requests[256]; + unsigned int last_request; + + unsigned int current_package; + unsigned int current_channel; + + unsigned int n_packages; + struct ncsi_package *packages; +}; + +struct ncsi *ncsi_priv; + +bool ncsi_active(void) +{ + unsigned int np, nc; + + if (!ncsi_priv) + return false; + + np = ncsi_priv->current_package; + nc = ncsi_priv->current_channel; + + if (ncsi_priv->state != NCSI_CONFIG) + return false; + + return np < NCSI_PACKAGE_MAX && nc < NCSI_CHANNEL_MAX && + ncsi_priv->packages[np].channels[nc].has_link; +} + +static unsigned int cmd_payload(int cmd) +{ + switch (cmd) { + case NCSI_PKT_CMD_CIS: + return 0; + case NCSI_PKT_CMD_SP: + return 4; + case NCSI_PKT_CMD_DP: + return 0; + case NCSI_PKT_CMD_EC: + return 0; + case NCSI_PKT_CMD_DC: + return 4; + case NCSI_PKT_CMD_RC: + return 4; + case NCSI_PKT_CMD_ECNT: + return 0; + case NCSI_PKT_CMD_DCNT: + return 0; + case NCSI_PKT_CMD_AE: + return 8; + case NCSI_PKT_CMD_SL: + return 8; + case NCSI_PKT_CMD_GLS: + return 0; + case NCSI_PKT_CMD_SVF: + return 8; + case NCSI_PKT_CMD_EV: + return 4; + case NCSI_PKT_CMD_DV: + return 0; + case NCSI_PKT_CMD_SMA: + return 8; + case NCSI_PKT_CMD_EBF: + return 4; + case NCSI_PKT_CMD_DBF: + return 0; + case NCSI_PKT_CMD_EGMF: + return 4; + case NCSI_PKT_CMD_DGMF: + return 0; + case NCSI_PKT_CMD_SNFC: + return 4; + case NCSI_PKT_CMD_GVI: + return 0; + case NCSI_PKT_CMD_GC: + return 0; + case NCSI_PKT_CMD_GP: + return 0; + case NCSI_PKT_CMD_GCPS: + return 0; + case NCSI_PKT_CMD_GNS: + return 0; + case NCSI_PKT_CMD_GNPTS: + return 0; + case NCSI_PKT_CMD_GPS: + return 0; + default: + printf("NCSI: Unknown command 0x%02x\n", cmd); + return 0; + } +} + +static u32 ncsi_calculate_checksum(unsigned char *data, int len) +{ + u32 checksum = 0; + int i; + + for (i = 0; i < len; i += 2) + checksum += (((u32)data[i] << 8) | data[i + 1]); + + checksum = (~checksum + 1); + return checksum; +} + +static int ncsi_validate_rsp(struct ncsi_rsp_pkt *pkt, int payload) +{ + struct ncsi_rsp_pkt_hdr *hdr = &pkt->rsp; + u32 checksum, c_offset; + __be32 pchecksum; + + if (hdr->common.revision != 1) { + printf("NCSI: 0x%02x response has unsupported revision 0x%x\n", + hdr->common.type, hdr->common.revision); + return -1; + } + + if (hdr->code != 0) { + printf("NCSI: 0x%02x response returns error %d\n", + hdr->common.type, __be16_to_cpu(hdr->code)); + if (ntohs(hdr->reason) == 0x05) + printf("(Invalid command length)\n"); + return -1; + } + + if (ntohs(hdr->common.length) != payload) { + printf("NCSI: 0x%02x response has incorrect length %d\n", + hdr->common.type, hdr->common.length); + return -1; + } + + c_offset = sizeof(struct ncsi_rsp_pkt_hdr) + payload - sizeof(checksum); + pchecksum = get_unaligned_be32((void *)hdr + c_offset); + if (pchecksum != 0) { + checksum = ncsi_calculate_checksum((unsigned char *)hdr, + c_offset); + if (pchecksum != checksum) { + printf("NCSI: 0x%02x response has invalid checksum\n", + hdr->common.type); + return -1; + } + } + + return 0; +} + +static void ncsi_rsp_ec(struct ncsi_rsp_pkt *pkt) +{ + struct ncsi_rsp_pkt_hdr *rsp = (struct ncsi_rsp_pkt_hdr *)&pkt->rsp; + unsigned int np, nc; + + np = NCSI_PACKAGE_INDEX(rsp->common.channel); + nc = NCSI_CHANNEL_INDEX(rsp->common.channel); + + if (ncsi_priv->packages[np].channels[nc].cap_aen != 0) + ncsi_send_ae(np, nc); + /* else, done */ +} + +static void ncsi_rsp_ecnt(struct ncsi_rsp_pkt *pkt) +{ + struct ncsi_rsp_pkt_hdr *rsp = (struct ncsi_rsp_pkt_hdr *)&pkt->rsp; + unsigned int np, nc; + + np = NCSI_PACKAGE_INDEX(rsp->common.channel); + nc = NCSI_CHANNEL_INDEX(rsp->common.channel); + + ncsi_send_command(np, nc, NCSI_PKT_CMD_EC, NULL, 0, true); +} + +static void ncsi_rsp_ebf(struct ncsi_rsp_pkt *pkt) +{ + struct ncsi_rsp_pkt_hdr *rsp = (struct ncsi_rsp_pkt_hdr *)&pkt->rsp; + unsigned int np, nc; + + np = NCSI_PACKAGE_INDEX(rsp->common.channel); + nc = NCSI_CHANNEL_INDEX(rsp->common.channel); + + ncsi_send_command(np, nc, NCSI_PKT_CMD_ECNT, NULL, 0, true); +} + +static void ncsi_rsp_sma(struct ncsi_rsp_pkt *pkt) +{ + struct ncsi_rsp_pkt_hdr *rsp = (struct ncsi_rsp_pkt_hdr *)&pkt->rsp; + unsigned int np, nc; + + np = NCSI_PACKAGE_INDEX(rsp->common.channel); + nc = NCSI_CHANNEL_INDEX(rsp->common.channel); + + ncsi_send_ebf(np, nc); +} + +static void ncsi_rsp_gc(struct ncsi_rsp_pkt *pkt) +{ + struct ncsi_rsp_gc_pkt *gc = (struct ncsi_rsp_gc_pkt *)pkt; + struct ncsi_rsp_pkt_hdr *rsp = (struct ncsi_rsp_pkt_hdr *)&gc->rsp; + struct ncsi_channel *c; + unsigned int np, nc; + + np = NCSI_PACKAGE_INDEX(rsp->common.channel); + nc = NCSI_CHANNEL_INDEX(rsp->common.channel); + + if (np >= ncsi_priv->n_packages || + nc >= ncsi_priv->packages[np].n_channels) { + printf("NCSI: Invalid package / channel (0x%02x, 0x%02x)\n", + np, nc); + return; + } + + c = &ncsi_priv->packages[np].channels[nc]; + c->cap_generic = ntohl(gc->cap) & NCSI_CAP_GENERIC_MASK; + c->cap_bc = ntohl(gc->bc_cap) & NCSI_CAP_BC_MASK; + c->cap_mc = ntohl(gc->mc_cap) & NCSI_CAP_MC_MASK; + c->cap_aen = ntohl(gc->aen_cap) & NCSI_CAP_AEN_MASK; + c->cap_vlan = ntohl(gc->vlan_mode) & NCSI_CAP_VLAN_MASK; + + /* End of probe for this channel */ +} + +static void ncsi_rsp_gvi(struct ncsi_rsp_pkt *pkt) +{ + struct ncsi_rsp_gvi_pkt *gvi = (struct ncsi_rsp_gvi_pkt *)pkt; + struct ncsi_rsp_pkt_hdr *rsp = (struct ncsi_rsp_pkt_hdr *)&gvi->rsp; + struct ncsi_channel *c; + unsigned int np, nc, i; + + np = NCSI_PACKAGE_INDEX(rsp->common.channel); + nc = NCSI_CHANNEL_INDEX(rsp->common.channel); + + if (np >= ncsi_priv->n_packages || + nc >= ncsi_priv->packages[np].n_channels) { + printf("NCSI: Invalid package / channel (0x%02x, 0x%02x)\n", + np, nc); + return; + } + + c = &ncsi_priv->packages[np].channels[nc]; + c->version.version = get_unaligned_be32(&gvi->ncsi_version); + c->version.alpha2 = gvi->alpha2; + memcpy(c->version.fw_name, gvi->fw_name, sizeof(c->version.fw_name)); + c->version.fw_version = get_unaligned_be32(&gvi->fw_version); + for (i = 0; i < ARRAY_SIZE(c->version.pci_ids); i++) + c->version.pci_ids[i] = get_unaligned_be16(gvi->pci_ids + i); + c->version.mf_id = get_unaligned_be32(&gvi->mf_id); + + if (ncsi_priv->state == NCSI_PROBE_CHANNEL) + ncsi_send_command(np, nc, NCSI_PKT_CMD_GC, NULL, 0, true); +} + +static void ncsi_rsp_gls(struct ncsi_rsp_pkt *pkt) +{ + struct ncsi_rsp_gls_pkt *gls = (struct ncsi_rsp_gls_pkt *)pkt; + struct ncsi_rsp_pkt_hdr *rsp = (struct ncsi_rsp_pkt_hdr *)&gls->rsp; + unsigned int np, nc; + + np = NCSI_PACKAGE_INDEX(rsp->common.channel); + nc = NCSI_CHANNEL_INDEX(rsp->common.channel); + + if (np >= ncsi_priv->n_packages || + nc >= ncsi_priv->packages[np].n_channels) { + printf("NCSI: Invalid package / channel (0x%02x, 0x%02x)\n", + np, nc); + return; + } + + ncsi_priv->packages[np].channels[nc].has_link = + !!(get_unaligned_be32(&gls->status)); + + if (ncsi_priv->state == NCSI_PROBE_CHANNEL) + ncsi_send_command(np, nc, NCSI_PKT_CMD_GVI, NULL, 0, true); +} + +static void ncsi_rsp_cis(struct ncsi_rsp_pkt *pkt) +{ + struct ncsi_rsp_pkt_hdr *rsp = (struct ncsi_rsp_pkt_hdr *)pkt; + struct ncsi_package *package; + unsigned int np, nc; + + np = NCSI_PACKAGE_INDEX(rsp->common.channel); + nc = NCSI_CHANNEL_INDEX(rsp->common.channel); + + if (np >= ncsi_priv->n_packages) { + printf("NCSI: Mystery package 0x%02x from CIS\n", np); + return; + } + + package = &ncsi_priv->packages[np]; + + if (nc < package->n_channels) { + /* + * This is fine in general but in the current design we + * don't send CIS commands to known channels. + */ + debug("NCSI: Duplicate channel 0x%02x\n", nc); + return; + } + + package->channels = realloc(package->channels, + sizeof(struct ncsi_channel) * + (package->n_channels + 1)); + if (!package->channels) { + printf("NCSI: Could not allocate memory for new channel\n"); + return; + } + + debug("NCSI: New channel 0x%02x\n", nc); + + package->channels[nc].id = nc; + package->channels[nc].has_link = false; + package->n_channels++; + + ncsi_send_gls(np, nc); +} + +static void ncsi_rsp_dp(struct ncsi_rsp_pkt *pkt) +{ + struct ncsi_rsp_pkt_hdr *rsp = (struct ncsi_rsp_pkt_hdr *)pkt; + unsigned int np; + + /* No action needed */ + + np = NCSI_PACKAGE_INDEX(rsp->common.channel); + if (np >= ncsi_priv->n_packages) + debug("NCSI: DP response from unknown package %d\n", np); +} + +static void ncsi_rsp_sp(struct ncsi_rsp_pkt *pkt) +{ + struct ncsi_rsp_pkt_hdr *rsp = (struct ncsi_rsp_pkt_hdr *)pkt; + unsigned int np; + + np = NCSI_PACKAGE_INDEX(rsp->common.channel); + + if (np < ncsi_priv->n_packages) { + /* Already know about this package */ + debug("NCSI: package 0x%02x selected\n", np); + return; + } + + debug("NCSI: adding new package %d\n", np); + + ncsi_priv->packages = realloc(ncsi_priv->packages, + sizeof(struct ncsi_package) * + (ncsi_priv->n_packages + 1)); + if (!ncsi_priv->packages) { + printf("NCSI: could not allocate memory for new package\n"); + return; + } + + ncsi_priv->packages[np].id = np; + ncsi_priv->packages[np].n_channels = 0; + ncsi_priv->packages[np].channels = NULL; + ncsi_priv->n_packages++; +} + +static void ncsi_update_state(struct ncsi_rsp_pkt_hdr *nh) +{ + bool timeout = !nh; + int np, nc; + + switch (ncsi_priv->state) { + case NCSI_PROBE_PACKAGE_SP: + if (!timeout && + ncsi_priv->current_package + 1 < NCSI_PACKAGE_MAX) { + ncsi_priv->current_package++; + } else { + ncsi_priv->state = NCSI_PROBE_PACKAGE_DP; + ncsi_priv->current_package = 0; + } + return ncsi_probe_packages(); + case NCSI_PROBE_PACKAGE_DP: + if (ncsi_priv->current_package + 1 < ncsi_priv->n_packages && + !timeout) { + ncsi_priv->current_package++; + } else { + if (!ncsi_priv->n_packages) { + printf("NCSI: no packages found\n"); + net_set_state(NETLOOP_FAIL); + return; + } + printf("NCSI: probing channels\n"); + ncsi_priv->state = NCSI_PROBE_CHANNEL_SP; + ncsi_priv->current_package = 0; + ncsi_priv->current_channel = 0; + } + return ncsi_probe_packages(); + case NCSI_PROBE_CHANNEL_SP: + if (!timeout && nh->common.type == NCSI_PKT_RSP_SP) { + ncsi_priv->state = NCSI_PROBE_CHANNEL; + return ncsi_probe_packages(); + } + printf("NCSI: failed to select package 0x%0x2 or timeout\n", + ncsi_priv->current_package); + net_set_state(NETLOOP_FAIL); + break; + case NCSI_PROBE_CHANNEL: + // TODO only does package 0 for now + if (ncsi_priv->pending_requests == 0) { + np = ncsi_priv->current_package; + nc = ncsi_priv->current_channel; + + /* Configure first channel that has link */ + if (ncsi_priv->packages[np].channels[nc].has_link) { + ncsi_priv->state = NCSI_CONFIG; + } else if (ncsi_priv->current_channel + 1 < + NCSI_CHANNEL_MAX) { + ncsi_priv->current_channel++; + } else { + // XXX As above only package 0 + printf("NCSI: no channel found with link\n"); + net_set_state(NETLOOP_FAIL); + return; + } + return ncsi_probe_packages(); + } + break; + case NCSI_CONFIG: + if (ncsi_priv->pending_requests == 0) { + printf("NCSI: configuration done!\n"); + net_set_state(NETLOOP_SUCCESS); + } else if (timeout) { + printf("NCSI: timeout during configure\n"); + net_set_state(NETLOOP_FAIL); + } + break; + default: + printf("NCSI: something went very wrong, nevermind\n"); + net_set_state(NETLOOP_FAIL); + break; + } +} + +static void ncsi_timeout_handler(void) +{ + if (ncsi_priv->pending_requests) + ncsi_priv->pending_requests--; + + ncsi_update_state(NULL); +} + +static int ncsi_send_command(unsigned int np, unsigned int nc, unsigned int cmd, + uchar *payload, int len, bool wait) +{ + struct ncsi_pkt_hdr *hdr; + __be32 *pchecksum; + int eth_hdr_size; + u32 checksum; + uchar *pkt, *start; + int final_len; + + pkt = calloc(1, PKTSIZE_ALIGN + PKTALIGN); + if (!pkt) + return -ENOMEM; + start = pkt; + + eth_hdr_size = net_set_ether(pkt, net_bcast_ethaddr, PROT_NCSI); + pkt += eth_hdr_size; + + /* Set NCSI command header fields */ + hdr = (struct ncsi_pkt_hdr *)pkt; + hdr->mc_id = 0; + hdr->revision = NCSI_PKT_REVISION; + hdr->id = ++ncsi_priv->last_request; + ncsi_priv->requests[ncsi_priv->last_request] = 1; + hdr->type = cmd; + hdr->channel = NCSI_TO_CHANNEL(np, nc); + hdr->length = htons(len); + + if (payload && len) + memcpy(pkt + sizeof(struct ncsi_pkt_hdr), payload, len); + + /* Calculate checksum */ + checksum = ncsi_calculate_checksum((unsigned char *)hdr, + sizeof(*hdr) + len); + pchecksum = (__be32 *)((void *)(hdr + 1) + len); + put_unaligned_be32(htonl(checksum), pchecksum); + + if (wait) { + net_set_timeout_handler(1000UL, ncsi_timeout_handler); + ncsi_priv->pending_requests++; + } + + if (len < 26) + len = 26; + /* frame header, packet header, payload, checksum */ + final_len = eth_hdr_size + sizeof(struct ncsi_cmd_pkt_hdr) + len + 4; + + net_send_packet(start, final_len); + free(start); + return 0; +} + +static void ncsi_handle_aen(struct ip_udp_hdr *ip, unsigned int len) +{ + struct ncsi_aen_pkt_hdr *hdr = (struct ncsi_aen_pkt_hdr *)ip; + int payload, i; + __be32 pchecksum; + u32 checksum; + + switch (hdr->type) { + case NCSI_PKT_AEN_LSC: + printf("NCSI: link state changed\n"); + payload = 12; + break; + case NCSI_PKT_AEN_CR: + printf("NCSI: re-configuration required\n"); + payload = 4; + break; + case NCSI_PKT_AEN_HNCDSC: + /* Host notifcation - N/A but weird */ + debug("NCSI: HNCDSC AEN received\n"); + return; + default: + printf("%s: Invalid type 0x%02x\n", __func__, hdr->type); + return; + } + + /* Validate packet */ + if (hdr->common.revision != 1) { + printf("NCSI: 0x%02x response has unsupported revision 0x%x\n", + hdr->common.type, hdr->common.revision); + return; + } + + if (ntohs(hdr->common.length) != payload) { + printf("NCSI: 0x%02x response has incorrect length %d\n", + hdr->common.type, hdr->common.length); + return; + } + + pchecksum = get_unaligned_be32((void *)(hdr + 1) + payload - 4); + if (pchecksum != 0) { + checksum = ncsi_calculate_checksum((unsigned char *)hdr, + sizeof(*hdr) + payload - 4); + if (pchecksum != checksum) { + printf("NCSI: 0x%02x response has invalid checksum\n", + hdr->common.type); + return; + } + } + + /* Link or configuration lost - just redo the discovery process */ + ncsi_priv->state = NCSI_PROBE_PACKAGE_SP; + for (i = 0; i < ncsi_priv->n_packages; i++) + free(ncsi_priv->packages[i].channels); + free(ncsi_priv->packages); + ncsi_priv->n_packages = 0; + + ncsi_priv->current_package = NCSI_PACKAGE_MAX; + ncsi_priv->current_channel = NCSI_CHANNEL_MAX; + + ncsi_probe_packages(); +} + +void ncsi_receive(struct ethernet_hdr *et, struct ip_udp_hdr *ip, + unsigned int len) +{ + struct ncsi_rsp_pkt *pkt = (struct ncsi_rsp_pkt *)ip; + struct ncsi_rsp_pkt_hdr *nh = (struct ncsi_rsp_pkt_hdr *)&pkt->rsp; + void (*handler)(struct ncsi_rsp_pkt *pkt) = NULL; + unsigned short payload; + + if (ncsi_priv->pending_requests) + ncsi_priv->pending_requests--; + + if (len < sizeof(struct ncsi_rsp_pkt_hdr)) { + printf("NCSI: undersized packet: %u bytes\n", len); + goto out; + } + + if (nh->common.type == NCSI_PKT_AEN) + return ncsi_handle_aen(ip, len); + + switch (nh->common.type) { + case NCSI_PKT_RSP_SP: + payload = 4; + handler = ncsi_rsp_sp; + break; + case NCSI_PKT_RSP_DP: + payload = 4; + handler = ncsi_rsp_dp; + break; + case NCSI_PKT_RSP_CIS: + payload = 4; + handler = ncsi_rsp_cis; + break; + case NCSI_PKT_RSP_GLS: + payload = 16; + handler = ncsi_rsp_gls; + break; + case NCSI_PKT_RSP_GVI: + payload = 40; + handler = ncsi_rsp_gvi; + break; + case NCSI_PKT_RSP_GC: + payload = 32; + handler = ncsi_rsp_gc; + break; + case NCSI_PKT_RSP_SMA: + payload = 4; + handler = ncsi_rsp_sma; + break; + case NCSI_PKT_RSP_EBF: + payload = 4; + handler = ncsi_rsp_ebf; + break; + case NCSI_PKT_RSP_ECNT: + payload = 4; + handler = ncsi_rsp_ecnt; + break; + case NCSI_PKT_RSP_EC: + payload = 4; + handler = ncsi_rsp_ec; + break; + case NCSI_PKT_RSP_AE: + payload = 4; + handler = NULL; + break; + default: + printf("NCSI: unsupported packet type 0x%02x\n", + nh->common.type); + goto out; + } + + if (ncsi_validate_rsp(pkt, payload) != 0) { + printf("NCSI: discarding invalid packet of type 0x%02x\n", + nh->common.type); + goto out; + } + + if (handler) + handler(pkt); +out: + ncsi_update_state(nh); +} + +static void ncsi_send_sp(unsigned int np) +{ + uchar payload[4] = {0}; + + ncsi_send_command(np, NCSI_RESERVED_CHANNEL, NCSI_PKT_CMD_SP, + (unsigned char *)&payload, + cmd_payload(NCSI_PKT_CMD_SP), true); +} + +static void ncsi_send_dp(unsigned int np) +{ + ncsi_send_command(np, NCSI_RESERVED_CHANNEL, NCSI_PKT_CMD_DP, NULL, 0, + true); +} + +static void ncsi_send_gls(unsigned int np, unsigned int nc) +{ + ncsi_send_command(np, nc, NCSI_PKT_CMD_GLS, NULL, 0, true); +} + +static void ncsi_send_cis(unsigned int np, unsigned int nc) +{ + ncsi_send_command(np, nc, NCSI_PKT_CMD_CIS, NULL, 0, true); +} + +static void ncsi_send_ae(unsigned int np, unsigned int nc) +{ + struct ncsi_cmd_ae_pkt cmd; + + memset(&cmd, 0, sizeof(cmd)); + cmd.mode = htonl(ncsi_priv->packages[np].channels[nc].cap_aen); + + ncsi_send_command(np, nc, NCSI_PKT_CMD_AE, + ((unsigned char *)&cmd) + + sizeof(struct ncsi_cmd_pkt_hdr), + cmd_payload(NCSI_PKT_CMD_AE), true); +} + +static void ncsi_send_ebf(unsigned int np, unsigned int nc) +{ + struct ncsi_cmd_ebf_pkt cmd; + + memset(&cmd, 0, sizeof(cmd)); + cmd.mode = htonl(ncsi_priv->packages[np].channels[nc].cap_bc); + + ncsi_send_command(np, nc, NCSI_PKT_CMD_EBF, + ((unsigned char *)&cmd) + + sizeof(struct ncsi_cmd_pkt_hdr), + cmd_payload(NCSI_PKT_CMD_EBF), true); +} + +static void ncsi_send_sma(unsigned int np, unsigned int nc) +{ + struct ncsi_cmd_sma_pkt cmd; + unsigned char *addr, i; + + addr = eth_get_ethaddr(); + if (!addr) { + printf("NCSI: no MAC address configured\n"); + return; + } + + memset(&cmd, 0, sizeof(cmd)); + for (i = 0; i < ARP_HLEN; i++) + cmd.mac[i] = addr[i]; + cmd.index = 1; + cmd.at_e = 1; + + ncsi_send_command(np, nc, NCSI_PKT_CMD_SMA, + ((unsigned char *)&cmd) + + sizeof(struct ncsi_cmd_pkt_hdr), + cmd_payload(NCSI_PKT_CMD_SMA), true); +} + +void ncsi_probe_packages(void) +{ + struct ncsi_package *package; + unsigned int np, nc; + + switch (ncsi_priv->state) { + case NCSI_PROBE_PACKAGE_SP: + if (ncsi_priv->current_package == NCSI_PACKAGE_MAX) + ncsi_priv->current_package = 0; + ncsi_send_sp(ncsi_priv->current_package); + break; + case NCSI_PROBE_PACKAGE_DP: + ncsi_send_dp(ncsi_priv->current_package); + break; + case NCSI_PROBE_CHANNEL_SP: + if (ncsi_priv->n_packages > 0) + ncsi_send_sp(ncsi_priv->current_package); + else + printf("NCSI: no packages discovered, configuration not possible\n"); + break; + case NCSI_PROBE_CHANNEL: + /* Kicks off chain of channel discovery */ + ncsi_send_cis(ncsi_priv->current_package, + ncsi_priv->current_channel); + break; + case NCSI_CONFIG: + for (np = 0; np < ncsi_priv->n_packages; np++) { + package = &ncsi_priv->packages[np]; + for (nc = 0; nc < package->n_channels; nc++) + if (package->channels[nc].has_link) + break; + if (nc < package->n_channels) + break; + } + if (np == ncsi_priv->n_packages) { + printf("NCSI: no link available\n"); + return; + } + + printf("NCSI: configuring channel %d\n", nc); + ncsi_priv->current_package = np; + ncsi_priv->current_channel = nc; + /* Kicks off rest of configure chain */ + ncsi_send_sma(np, nc); + break; + default: + printf("NCSI: unknown state 0x%x\n", ncsi_priv->state); + } +} + +int ncsi_probe(struct phy_device *phydev) +{ + if (!phydev->priv) { + phydev->priv = malloc(sizeof(struct ncsi)); + if (!phydev->priv) + return -ENOMEM; + memset(phydev->priv, 0, sizeof(struct ncsi)); + } + + ncsi_priv = phydev->priv; + + return 0; +} + +int ncsi_startup(struct phy_device *phydev) +{ + /* Set phydev parameters */ + phydev->speed = SPEED_100; + phydev->duplex = DUPLEX_FULL; + /* Normal phy reset is N/A */ + phydev->flags |= PHY_FLAG_BROKEN_RESET; + + /* Set initial probe state */ + ncsi_priv->state = NCSI_PROBE_PACKAGE_SP; + + /* No active package/channel yet */ + ncsi_priv->current_package = NCSI_PACKAGE_MAX; + ncsi_priv->current_channel = NCSI_CHANNEL_MAX; + + /* Pretend link works so the MAC driver sets final bits up */ + phydev->link = true; + + /* Set ncsi_priv so we can use it when called from net_loop() */ + ncsi_priv = phydev->priv; + + return 0; +} + +int ncsi_shutdown(struct phy_device *phydev) +{ + printf("NCSI: Disabling package %d\n", ncsi_priv->current_package); + ncsi_send_dp(ncsi_priv->current_package); + return 0; +} + +static struct phy_driver ncsi_driver = { + .uid = PHY_NCSI_ID, + .mask = 0xffffffff, + .name = "NC-SI", + .features = PHY_100BT_FEATURES | PHY_DEFAULT_FEATURES | + SUPPORTED_100baseT_Full | SUPPORTED_MII, + .probe = ncsi_probe, + .startup = ncsi_startup, + .shutdown = ncsi_shutdown, +}; + +int phy_ncsi_init(void) +{ + phy_register(&ncsi_driver); + return 0; +} diff --git a/drivers/net/phy/phy.c b/drivers/net/phy/phy.c index 80a7664e49..505d3ab659 100644 --- a/drivers/net/phy/phy.c +++ b/drivers/net/phy/phy.c @@ -244,7 +244,7 @@ int genphy_update_link(struct phy_device *phydev) /* * Timeout reached ? */ - if (i > PHY_ANEG_TIMEOUT) { + if (i > (PHY_ANEG_TIMEOUT / 50)) { printf(" TIMEOUT !\n"); phydev->link = 0; return -ETIMEDOUT; @@ -545,6 +545,9 @@ int phy_init(void) #ifdef CONFIG_PHY_FIXED phy_fixed_init(); #endif +#ifdef CONFIG_PHY_NCSI + phy_ncsi_init(); +#endif #ifdef CONFIG_PHY_XILINX_GMII2RGMII phy_xilinx_gmii2rgmii_init(); #endif @@ -1002,6 +1005,12 @@ struct phy_device *phy_connect(struct mii_dev *bus, int addr, #ifdef CONFIG_PHY_FIXED phydev = phy_connect_fixed(bus, dev, interface); #endif + +#ifdef CONFIG_PHY_NCSI + if (!phydev) + phydev = phy_device_create(bus, 0, PHY_NCSI_ID, false, interface); +#endif + #ifdef CONFIG_PHY_XILINX_GMII2RGMII if (!phydev) phydev = phy_connect_gmii2rgmii(bus, dev, interface); diff --git a/env/mmc.c b/env/mmc.c index b24c35cec9..251ad07d7c 100644 --- a/env/mmc.c +++ b/env/mmc.c @@ -353,6 +353,7 @@ static int env_mmc_load(void) int ret; int dev = mmc_get_env_dev(); const char *errmsg; + env_t *ep = NULL; mmc = find_mmc_device(dev); @@ -374,6 +375,10 @@ static int env_mmc_load(void) } ret = env_import(buf, 1); + if (!ret) { + ep = (env_t *)buf; + gd->env_addr = (ulong)&ep->data; + } fini: fini_mmc_for_env(mmc); diff --git a/include/net.h b/include/net.h index 8a02c923a4..82500eeb30 100644 --- a/include/net.h +++ b/include/net.h @@ -356,6 +356,7 @@ struct vlan_ethernet_hdr { #define PROT_VLAN 0x8100 /* IEEE 802.1q protocol */ #define PROT_IPV6 0x86dd /* IPv6 over bluebook */ #define PROT_PPP_SES 0x8864 /* PPPoE session messages */ +#define PROT_NCSI 0x88f8 /* NC-SI control packets */ #define IPPROTO_ICMP 1 /* Internet Control Message Protocol */ #define IPPROTO_UDP 17 /* User Datagram Protocol */ diff --git a/include/net/ncsi-pkt.h b/include/net/ncsi-pkt.h new file mode 100644 index 0000000000..a8e9def593 --- /dev/null +++ b/include/net/ncsi-pkt.h @@ -0,0 +1,442 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * Copyright Gavin Shan, IBM Corporation 2016. + */ + +#ifndef __NCSI_PKT_H__ +#define __NCSI_PKT_H__ + +struct ncsi_pkt_hdr { + unsigned char mc_id; /* Management controller ID */ + unsigned char revision; /* NCSI version - 0x01 */ + unsigned char reserved; /* Reserved */ + unsigned char id; /* Packet sequence number */ + unsigned char type; /* Packet type */ + unsigned char channel; /* Network controller ID */ + __be16 length; /* Payload length */ + __be32 reserved1[2]; /* Reserved */ +}; + +struct ncsi_cmd_pkt_hdr { + struct ncsi_pkt_hdr common; /* Common NCSI packet header */ +}; + +struct ncsi_rsp_pkt_hdr { + struct ncsi_pkt_hdr common; /* Common NCSI packet header */ + __be16 code; /* Response code */ + __be16 reason; /* Response reason */ +}; + +struct ncsi_aen_pkt_hdr { + struct ncsi_pkt_hdr common; /* Common NCSI packet header */ + unsigned char reserved2[3]; /* Reserved */ + unsigned char type; /* AEN packet type */ +}; + +/* NCSI common command packet */ +struct ncsi_cmd_pkt { + struct ncsi_cmd_pkt_hdr cmd; /* Command header */ + __be32 checksum; /* Checksum */ + unsigned char pad[26]; +}; + +struct ncsi_rsp_pkt { + struct ncsi_rsp_pkt_hdr rsp; /* Response header */ + __be32 checksum; /* Checksum */ + unsigned char pad[22]; +}; + +/* Select Package */ +struct ncsi_cmd_sp_pkt { + struct ncsi_cmd_pkt_hdr cmd; /* Command header */ + unsigned char reserved[3]; /* Reserved */ + unsigned char hw_arbitration; /* HW arbitration */ + __be32 checksum; /* Checksum */ + unsigned char pad[22]; +}; + +/* Disable Channel */ +struct ncsi_cmd_dc_pkt { + struct ncsi_cmd_pkt_hdr cmd; /* Command header */ + unsigned char reserved[3]; /* Reserved */ + unsigned char ald; /* Allow link down */ + __be32 checksum; /* Checksum */ + unsigned char pad[22]; +}; + +/* Reset Channel */ +struct ncsi_cmd_rc_pkt { + struct ncsi_cmd_pkt_hdr cmd; /* Command header */ + __be32 reserved; /* Reserved */ + __be32 checksum; /* Checksum */ + unsigned char pad[22]; +}; + +/* AEN Enable */ +struct ncsi_cmd_ae_pkt { + struct ncsi_cmd_pkt_hdr cmd; /* Command header */ + unsigned char reserved[3]; /* Reserved */ + unsigned char mc_id; /* MC ID */ + __be32 mode; /* AEN working mode */ + __be32 checksum; /* Checksum */ + unsigned char pad[18]; +}; + +/* Set Link */ +struct ncsi_cmd_sl_pkt { + struct ncsi_cmd_pkt_hdr cmd; /* Command header */ + __be32 mode; /* Link working mode */ + __be32 oem_mode; /* OEM link mode */ + __be32 checksum; /* Checksum */ + unsigned char pad[18]; +}; + +/* Set VLAN Filter */ +struct ncsi_cmd_svf_pkt { + struct ncsi_cmd_pkt_hdr cmd; /* Command header */ + __be16 reserved; /* Reserved */ + __be16 vlan; /* VLAN ID */ + __be16 reserved1; /* Reserved */ + unsigned char index; /* VLAN table index */ + unsigned char enable; /* Enable or disable */ + __be32 checksum; /* Checksum */ + unsigned char pad[18]; +}; + +/* Enable VLAN */ +struct ncsi_cmd_ev_pkt { + struct ncsi_cmd_pkt_hdr cmd; /* Command header */ + unsigned char reserved[3]; /* Reserved */ + unsigned char mode; /* VLAN filter mode */ + __be32 checksum; /* Checksum */ + unsigned char pad[22]; +}; + +/* Set MAC Address */ +struct ncsi_cmd_sma_pkt { + struct ncsi_cmd_pkt_hdr cmd; /* Command header */ + unsigned char mac[6]; /* MAC address */ + unsigned char index; /* MAC table index */ + unsigned char at_e; /* Addr type and operation */ + __be32 checksum; /* Checksum */ + unsigned char pad[18]; +}; + +/* Enable Broadcast Filter */ +struct ncsi_cmd_ebf_pkt { + struct ncsi_cmd_pkt_hdr cmd; /* Command header */ + __be32 mode; /* Filter mode */ + __be32 checksum; /* Checksum */ + unsigned char pad[22]; +}; + +/* Enable Global Multicast Filter */ +struct ncsi_cmd_egmf_pkt { + struct ncsi_cmd_pkt_hdr cmd; /* Command header */ + __be32 mode; /* Global MC mode */ + __be32 checksum; /* Checksum */ + unsigned char pad[22]; +}; + +/* Set NCSI Flow Control */ +struct ncsi_cmd_snfc_pkt { + struct ncsi_cmd_pkt_hdr cmd; /* Command header */ + unsigned char reserved[3]; /* Reserved */ + unsigned char mode; /* Flow control mode */ + __be32 checksum; /* Checksum */ + unsigned char pad[22]; +}; + +/* OEM Request Command as per NCSI Specification */ +struct ncsi_cmd_oem_pkt { + struct ncsi_cmd_pkt_hdr cmd; /* Command header */ + __be32 mfr_id; /* Manufacture ID */ + unsigned char data[]; /* OEM Payload Data */ +}; + +/* OEM Response Packet as per NCSI Specification */ +struct ncsi_rsp_oem_pkt { + struct ncsi_rsp_pkt_hdr rsp; /* Command header */ + __be32 mfr_id; /* Manufacture ID */ + unsigned char data[]; /* Payload data */ +}; + +/* Mellanox Response Data */ +struct ncsi_rsp_oem_mlx_pkt { + unsigned char cmd_rev; /* Command Revision */ + unsigned char cmd; /* Command ID */ + unsigned char param; /* Parameter */ + unsigned char optional; /* Optional data */ + unsigned char data[]; /* Data */ +}; + +/* Broadcom Response Data */ +struct ncsi_rsp_oem_bcm_pkt { + unsigned char ver; /* Payload Version */ + unsigned char type; /* OEM Command type */ + __be16 len; /* Payload Length */ + unsigned char data[]; /* Cmd specific Data */ +}; + +/* Get Link Status */ +struct ncsi_rsp_gls_pkt { + struct ncsi_rsp_pkt_hdr rsp; /* Response header */ + __be32 status; /* Link status */ + __be32 other; /* Other indications */ + __be32 oem_status; /* OEM link status */ + __be32 checksum; + unsigned char pad[10]; +}; + +/* Get Version ID */ +struct ncsi_rsp_gvi_pkt { + struct ncsi_rsp_pkt_hdr rsp; /* Response header */ + __be32 ncsi_version; /* NCSI version */ + unsigned char reserved[3]; /* Reserved */ + unsigned char alpha2; /* NCSI version */ + unsigned char fw_name[12]; /* f/w name string */ + __be32 fw_version; /* f/w version */ + __be16 pci_ids[4]; /* PCI IDs */ + __be32 mf_id; /* Manufacture ID */ + __be32 checksum; +}; + +/* Get Capabilities */ +struct ncsi_rsp_gc_pkt { + struct ncsi_rsp_pkt_hdr rsp; /* Response header */ + __be32 cap; /* Capabilities */ + __be32 bc_cap; /* Broadcast cap */ + __be32 mc_cap; /* Multicast cap */ + __be32 buf_cap; /* Buffering cap */ + __be32 aen_cap; /* AEN cap */ + unsigned char vlan_cnt; /* VLAN filter count */ + unsigned char mixed_cnt; /* Mix filter count */ + unsigned char mc_cnt; /* MC filter count */ + unsigned char uc_cnt; /* UC filter count */ + unsigned char reserved[2]; /* Reserved */ + unsigned char vlan_mode; /* VLAN mode */ + unsigned char channel_cnt; /* Channel count */ + __be32 checksum; /* Checksum */ +}; + +/* Get Parameters */ +struct ncsi_rsp_gp_pkt { + struct ncsi_rsp_pkt_hdr rsp; /* Response header */ + unsigned char mac_cnt; /* Number of MAC addr */ + unsigned char reserved[2]; /* Reserved */ + unsigned char mac_enable; /* MAC addr enable flags */ + unsigned char vlan_cnt; /* VLAN tag count */ + unsigned char reserved1; /* Reserved */ + __be16 vlan_enable; /* VLAN tag enable flags */ + __be32 link_mode; /* Link setting */ + __be32 bc_mode; /* BC filter mode */ + __be32 valid_modes; /* Valid mode parameters */ + unsigned char vlan_mode; /* VLAN mode */ + unsigned char fc_mode; /* Flow control mode */ + unsigned char reserved2[2]; /* Reserved */ + __be32 aen_mode; /* AEN mode */ + unsigned char mac[6]; /* Supported MAC addr */ + __be16 vlan; /* Supported VLAN tags */ + __be32 checksum; /* Checksum */ +}; + +/* Get Controller Packet Statistics */ +struct ncsi_rsp_gcps_pkt { + struct ncsi_rsp_pkt_hdr rsp; /* Response header */ + __be32 cnt_hi; /* Counter cleared */ + __be32 cnt_lo; /* Counter cleared */ + __be32 rx_bytes; /* Rx bytes */ + __be32 tx_bytes; /* Tx bytes */ + __be32 rx_uc_pkts; /* Rx UC packets */ + __be32 rx_mc_pkts; /* Rx MC packets */ + __be32 rx_bc_pkts; /* Rx BC packets */ + __be32 tx_uc_pkts; /* Tx UC packets */ + __be32 tx_mc_pkts; /* Tx MC packets */ + __be32 tx_bc_pkts; /* Tx BC packets */ + __be32 fcs_err; /* FCS errors */ + __be32 align_err; /* Alignment errors */ + __be32 false_carrier; /* False carrier detection */ + __be32 runt_pkts; /* Rx runt packets */ + __be32 jabber_pkts; /* Rx jabber packets */ + __be32 rx_pause_xon; /* Rx pause XON frames */ + __be32 rx_pause_xoff; /* Rx XOFF frames */ + __be32 tx_pause_xon; /* Tx XON frames */ + __be32 tx_pause_xoff; /* Tx XOFF frames */ + __be32 tx_s_collision; /* Single collision frames */ + __be32 tx_m_collision; /* Multiple collision frames */ + __be32 l_collision; /* Late collision frames */ + __be32 e_collision; /* Excessive collision frames */ + __be32 rx_ctl_frames; /* Rx control frames */ + __be32 rx_64_frames; /* Rx 64-bytes frames */ + __be32 rx_127_frames; /* Rx 65-127 bytes frames */ + __be32 rx_255_frames; /* Rx 128-255 bytes frames */ + __be32 rx_511_frames; /* Rx 256-511 bytes frames */ + __be32 rx_1023_frames; /* Rx 512-1023 bytes frames */ + __be32 rx_1522_frames; /* Rx 1024-1522 bytes frames */ + __be32 rx_9022_frames; /* Rx 1523-9022 bytes frames */ + __be32 tx_64_frames; /* Tx 64-bytes frames */ + __be32 tx_127_frames; /* Tx 65-127 bytes frames */ + __be32 tx_255_frames; /* Tx 128-255 bytes frames */ + __be32 tx_511_frames; /* Tx 256-511 bytes frames */ + __be32 tx_1023_frames; /* Tx 512-1023 bytes frames */ + __be32 tx_1522_frames; /* Tx 1024-1522 bytes frames */ + __be32 tx_9022_frames; /* Tx 1523-9022 bytes frames */ + __be32 rx_valid_bytes; /* Rx valid bytes */ + __be32 rx_runt_pkts; /* Rx error runt packets */ + __be32 rx_jabber_pkts; /* Rx error jabber packets */ + __be32 checksum; /* Checksum */ +}; + +/* Get NCSI Statistics */ +struct ncsi_rsp_gns_pkt { + struct ncsi_rsp_pkt_hdr rsp; /* Response header */ + __be32 rx_cmds; /* Rx NCSI commands */ + __be32 dropped_cmds; /* Dropped commands */ + __be32 cmd_type_errs; /* Command type errors */ + __be32 cmd_csum_errs; /* Command checksum errors */ + __be32 rx_pkts; /* Rx NCSI packets */ + __be32 tx_pkts; /* Tx NCSI packets */ + __be32 tx_aen_pkts; /* Tx AEN packets */ + __be32 checksum; /* Checksum */ +}; + +/* Get NCSI Pass-through Statistics */ +struct ncsi_rsp_gnpts_pkt { + struct ncsi_rsp_pkt_hdr rsp; /* Response header */ + __be32 tx_pkts; /* Tx packets */ + __be32 tx_dropped; /* Tx dropped packets */ + __be32 tx_channel_err; /* Tx channel errors */ + __be32 tx_us_err; /* Tx undersize errors */ + __be32 rx_pkts; /* Rx packets */ + __be32 rx_dropped; /* Rx dropped packets */ + __be32 rx_channel_err; /* Rx channel errors */ + __be32 rx_us_err; /* Rx undersize errors */ + __be32 rx_os_err; /* Rx oversize errors */ + __be32 checksum; /* Checksum */ +}; + +/* Get package status */ +struct ncsi_rsp_gps_pkt { + struct ncsi_rsp_pkt_hdr rsp; /* Response header */ + __be32 status; /* Hardware arbitration status */ + __be32 checksum; +}; + +/* Get package UUID */ +struct ncsi_rsp_gpuuid_pkt { + struct ncsi_rsp_pkt_hdr rsp; /* Response header */ + unsigned char uuid[16]; /* UUID */ + __be32 checksum; +}; + +/* AEN: Link State Change */ +struct ncsi_aen_lsc_pkt { + struct ncsi_aen_pkt_hdr aen; /* AEN header */ + __be32 status; /* Link status */ + __be32 oem_status; /* OEM link status */ + __be32 checksum; /* Checksum */ + unsigned char pad[14]; +}; + +/* AEN: Configuration Required */ +struct ncsi_aen_cr_pkt { + struct ncsi_aen_pkt_hdr aen; /* AEN header */ + __be32 checksum; /* Checksum */ + unsigned char pad[22]; +}; + +/* AEN: Host Network Controller Driver Status Change */ +struct ncsi_aen_hncdsc_pkt { + struct ncsi_aen_pkt_hdr aen; /* AEN header */ + __be32 status; /* Status */ + __be32 checksum; /* Checksum */ + unsigned char pad[18]; +}; + +/* NCSI packet revision */ +#define NCSI_PKT_REVISION 0x01 + +/* NCSI packet commands */ +#define NCSI_PKT_CMD_CIS 0x00 /* Clear Initial State */ +#define NCSI_PKT_CMD_SP 0x01 /* Select Package */ +#define NCSI_PKT_CMD_DP 0x02 /* Deselect Package */ +#define NCSI_PKT_CMD_EC 0x03 /* Enable Channel */ +#define NCSI_PKT_CMD_DC 0x04 /* Disable Channel */ +#define NCSI_PKT_CMD_RC 0x05 /* Reset Channel */ +#define NCSI_PKT_CMD_ECNT 0x06 /* Enable Channel Network Tx */ +#define NCSI_PKT_CMD_DCNT 0x07 /* Disable Channel Network Tx */ +#define NCSI_PKT_CMD_AE 0x08 /* AEN Enable */ +#define NCSI_PKT_CMD_SL 0x09 /* Set Link */ +#define NCSI_PKT_CMD_GLS 0x0a /* Get Link */ +#define NCSI_PKT_CMD_SVF 0x0b /* Set VLAN Filter */ +#define NCSI_PKT_CMD_EV 0x0c /* Enable VLAN */ +#define NCSI_PKT_CMD_DV 0x0d /* Disable VLAN */ +#define NCSI_PKT_CMD_SMA 0x0e /* Set MAC address */ +#define NCSI_PKT_CMD_EBF 0x10 /* Enable Broadcast Filter */ +#define NCSI_PKT_CMD_DBF 0x11 /* Disable Broadcast Filter */ +#define NCSI_PKT_CMD_EGMF 0x12 /* Enable Global Multicast Filter */ +#define NCSI_PKT_CMD_DGMF 0x13 /* Disable Global Multicast Filter */ +#define NCSI_PKT_CMD_SNFC 0x14 /* Set NCSI Flow Control */ +#define NCSI_PKT_CMD_GVI 0x15 /* Get Version ID */ +#define NCSI_PKT_CMD_GC 0x16 /* Get Capabilities */ +#define NCSI_PKT_CMD_GP 0x17 /* Get Parameters */ +#define NCSI_PKT_CMD_GCPS 0x18 /* Get Controller Packet Statistics */ +#define NCSI_PKT_CMD_GNS 0x19 /* Get NCSI Statistics */ +#define NCSI_PKT_CMD_GNPTS 0x1a /* Get NCSI Pass-throu Statistics */ +#define NCSI_PKT_CMD_GPS 0x1b /* Get package status */ +#define NCSI_PKT_CMD_OEM 0x50 /* OEM */ +#define NCSI_PKT_CMD_PLDM 0x51 /* PLDM request over NCSI over RBT */ +#define NCSI_PKT_CMD_GPUUID 0x52 /* Get package UUID */ + +/* NCSI packet responses */ +#define NCSI_PKT_RSP_CIS (NCSI_PKT_CMD_CIS + 0x80) +#define NCSI_PKT_RSP_SP (NCSI_PKT_CMD_SP + 0x80) +#define NCSI_PKT_RSP_DP (NCSI_PKT_CMD_DP + 0x80) +#define NCSI_PKT_RSP_EC (NCSI_PKT_CMD_EC + 0x80) +#define NCSI_PKT_RSP_DC (NCSI_PKT_CMD_DC + 0x80) +#define NCSI_PKT_RSP_RC (NCSI_PKT_CMD_RC + 0x80) +#define NCSI_PKT_RSP_ECNT (NCSI_PKT_CMD_ECNT + 0x80) +#define NCSI_PKT_RSP_DCNT (NCSI_PKT_CMD_DCNT + 0x80) +#define NCSI_PKT_RSP_AE (NCSI_PKT_CMD_AE + 0x80) +#define NCSI_PKT_RSP_SL (NCSI_PKT_CMD_SL + 0x80) +#define NCSI_PKT_RSP_GLS (NCSI_PKT_CMD_GLS + 0x80) +#define NCSI_PKT_RSP_SVF (NCSI_PKT_CMD_SVF + 0x80) +#define NCSI_PKT_RSP_EV (NCSI_PKT_CMD_EV + 0x80) +#define NCSI_PKT_RSP_DV (NCSI_PKT_CMD_DV + 0x80) +#define NCSI_PKT_RSP_SMA (NCSI_PKT_CMD_SMA + 0x80) +#define NCSI_PKT_RSP_EBF (NCSI_PKT_CMD_EBF + 0x80) +#define NCSI_PKT_RSP_DBF (NCSI_PKT_CMD_DBF + 0x80) +#define NCSI_PKT_RSP_EGMF (NCSI_PKT_CMD_EGMF + 0x80) +#define NCSI_PKT_RSP_DGMF (NCSI_PKT_CMD_DGMF + 0x80) +#define NCSI_PKT_RSP_SNFC (NCSI_PKT_CMD_SNFC + 0x80) +#define NCSI_PKT_RSP_GVI (NCSI_PKT_CMD_GVI + 0x80) +#define NCSI_PKT_RSP_GC (NCSI_PKT_CMD_GC + 0x80) +#define NCSI_PKT_RSP_GP (NCSI_PKT_CMD_GP + 0x80) +#define NCSI_PKT_RSP_GCPS (NCSI_PKT_CMD_GCPS + 0x80) +#define NCSI_PKT_RSP_GNS (NCSI_PKT_CMD_GNS + 0x80) +#define NCSI_PKT_RSP_GNPTS (NCSI_PKT_CMD_GNPTS + 0x80) +#define NCSI_PKT_RSP_GPS (NCSI_PKT_CMD_GPS + 0x80) +#define NCSI_PKT_RSP_OEM (NCSI_PKT_CMD_OEM + 0x80) +#define NCSI_PKT_RSP_PLDM (NCSI_PKT_CMD_PLDM + 0x80) +#define NCSI_PKT_RSP_GPUUID (NCSI_PKT_CMD_GPUUID + 0x80) + +/* NCSI response code/reason */ +#define NCSI_PKT_RSP_C_COMPLETED 0x0000 /* Command Completed */ +#define NCSI_PKT_RSP_C_FAILED 0x0001 /* Command Failed */ +#define NCSI_PKT_RSP_C_UNAVAILABLE 0x0002 /* Command Unavailable */ +#define NCSI_PKT_RSP_C_UNSUPPORTED 0x0003 /* Command Unsupported */ +#define NCSI_PKT_RSP_R_NO_ERROR 0x0000 /* No Error */ +#define NCSI_PKT_RSP_R_INTERFACE 0x0001 /* Interface not ready */ +#define NCSI_PKT_RSP_R_PARAM 0x0002 /* Invalid Parameter */ +#define NCSI_PKT_RSP_R_CHANNEL 0x0003 /* Channel not Ready */ +#define NCSI_PKT_RSP_R_PACKAGE 0x0004 /* Package not Ready */ +#define NCSI_PKT_RSP_R_LENGTH 0x0005 /* Invalid payload length */ +#define NCSI_PKT_RSP_R_UNKNOWN 0x7fff /* Command type unsupported */ + +/* NCSI AEN packet type */ +#define NCSI_PKT_AEN 0xFF /* AEN Packet */ +#define NCSI_PKT_AEN_LSC 0x00 /* Link status change */ +#define NCSI_PKT_AEN_CR 0x01 /* Configuration required */ +#define NCSI_PKT_AEN_HNCDSC 0x02 /* HNC driver status change */ + +#endif /* __NCSI_PKT_H__ */ diff --git a/include/net/ncsi.h b/include/net/ncsi.h new file mode 100644 index 0000000000..2800c842b7 --- /dev/null +++ b/include/net/ncsi.h @@ -0,0 +1,14 @@ +/* SPDX-License-Identifier: GPL-2.0+ */ +/* + * NC-SI PHY + * + * Copyright (C) 2019, IBM Corporation. + */ + +#include +#include + +bool ncsi_active(void); +void ncsi_receive(struct ethernet_hdr *et, struct ip_udp_hdr *ip, + unsigned int len); +void ncsi_probe_packages(void); diff --git a/include/phy.h b/include/phy.h index 42cfc59ec0..b5de14cbfc 100644 --- a/include/phy.h +++ b/include/phy.h @@ -15,9 +15,12 @@ #include #include #include +#include #include #define PHY_FIXED_ID 0xa5a55a5a +#define PHY_NCSI_ID 0xbeefcafe + /* * There is no actual id for this. * This is just a dummy id for gmii2rgmmi converter. @@ -171,6 +174,11 @@ static inline int phy_read(struct phy_device *phydev, int devad, int regnum) { struct mii_dev *bus = phydev->bus; + if (!bus || !bus->read) { + debug("%s: No bus configured\n", __func__); + return -1; + } + return bus->read(bus, phydev->addr, devad, regnum); } @@ -179,6 +187,11 @@ static inline int phy_write(struct phy_device *phydev, int devad, int regnum, { struct mii_dev *bus = phydev->bus; + if (!bus || !bus->read) { + debug("%s: No bus configured\n", __func__); + return -1; + } + return bus->write(bus, phydev->addr, devad, regnum, val); } @@ -247,10 +260,15 @@ static inline int phy_write_mmd(struct phy_device *phydev, int devad, #ifdef CONFIG_PHYLIB_10G extern struct phy_driver gen10g_driver; -/* For now, XGMII is the only 10G interface */ +/* + * List all 10G interfaces here, the assumption being that PHYs on these + * interfaces are C45 + */ static inline int is_10g_interface(phy_interface_t interface) { - return interface == PHY_INTERFACE_MODE_XGMII; + return interface == PHY_INTERFACE_MODE_XGMII || + interface == PHY_INTERFACE_MODE_USXGMII || + interface == PHY_INTERFACE_MODE_XFI; } #endif @@ -400,6 +418,7 @@ int phy_vitesse_init(void); int phy_xilinx_init(void); int phy_mscc_init(void); int phy_fixed_init(void); +int phy_ncsi_init(void); int phy_xilinx_gmii2rgmii_init(void); int board_phy_config(struct phy_device *phydev); diff --git a/include/phy_interface.h b/include/phy_interface.h index 73f3a3679c..31ca72a81f 100644 --- a/include/phy_interface.h +++ b/include/phy_interface.h @@ -31,6 +31,7 @@ typedef enum { PHY_INTERFACE_MODE_XLAUI, PHY_INTERFACE_MODE_CAUI2, PHY_INTERFACE_MODE_CAUI4, + PHY_INTERFACE_MODE_NCSI, PHY_INTERFACE_MODE_XFI, PHY_INTERFACE_MODE_USXGMII, PHY_INTERFACE_MODE_NONE, /* Must be last */ @@ -60,6 +61,7 @@ static const char * const phy_interface_strings[] = { [PHY_INTERFACE_MODE_XLAUI] = "xlaui4", [PHY_INTERFACE_MODE_CAUI2] = "caui2", [PHY_INTERFACE_MODE_CAUI4] = "caui4", + [PHY_INTERFACE_MODE_NCSI] = "NC-SI", [PHY_INTERFACE_MODE_XFI] = "xfi", [PHY_INTERFACE_MODE_USXGMII] = "usxgmii", [PHY_INTERFACE_MODE_NONE] = "", diff --git a/net/tftp.c b/net/tftp.c index 02401898c5..585eb6ef0c 100644 --- a/net/tftp.c +++ b/net/tftp.c @@ -223,7 +223,7 @@ static int load_block(unsigned block, uchar *dst, unsigned len) tosend = min(net_boot_file_size - offset, tosend); (void)memcpy(dst, (void *)(image_save_addr + offset), tosend); - debug("%s: block=%d, offset=%ld, len=%d, tosend=%ld\n", __func__, + debug("%s: block=%u, offset=%lu, len=%u, tosend=%lu\n", __func__, block, offset, len, tosend); return tosend; }