This commit is contained in:
Tom Rini 2020-03-10 07:51:56 -04:00
commit dd12b2f632
19 changed files with 1764 additions and 330 deletions

View File

@ -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

View File

@ -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.

View File

@ -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 = <DP83867_RGMIIDCTL_2_25_NS>;
ti,tx-internal-delay = <DP83867_RGMIIDCTL_2_75_NS>;
ti,fifo-depth = <DP83867_PHYCR_FIFO_DEPTH_4_B_NIB>;
enet-phy-lane-no-swap;
ti,clk-output-sel = <DP83867_CLK_O_SEL_CHN_A_TCLK>;
};
Datasheet can be found:
http://www.ti.com/product/DP83867IR/datasheet

View File

@ -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 = &eth_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.

View File

@ -8,6 +8,7 @@ Driver Model
debugging
design
ethernet
fdt-fixup
fs_firmware_loader
i2c-howto

View File

@ -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

View File

@ -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

View File

@ -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;
}

View File

@ -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));

View File

@ -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;

897
drivers/net/phy/ncsi.c Normal file
View File

@ -0,0 +1,897 @@
// SPDX-License-Identifier: GPL-2.0+
/*
* NC-SI protocol configuration
*
* Copyright (C) 2019, IBM Corporation.
*/
#include <common.h>
#include <malloc.h>
#include <phy.h>
#include <net/ncsi.h>
#include <net/ncsi-pkt.h>
#include <asm/unaligned.h>
#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;
}

View File

@ -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);

5
env/mmc.c vendored
View File

@ -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);

View File

@ -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 */

442
include/net/ncsi-pkt.h Normal file
View File

@ -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__ */

14
include/net/ncsi.h Normal file
View File

@ -0,0 +1,14 @@
/* SPDX-License-Identifier: GPL-2.0+ */
/*
* NC-SI PHY
*
* Copyright (C) 2019, IBM Corporation.
*/
#include <common.h>
#include <phy.h>
bool ncsi_active(void);
void ncsi_receive(struct ethernet_hdr *et, struct ip_udp_hdr *ip,
unsigned int len);
void ncsi_probe_packages(void);

View File

@ -15,9 +15,12 @@
#include <linux/mii.h>
#include <linux/ethtool.h>
#include <linux/mdio.h>
#include <log.h>
#include <phy_interface.h>
#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);

View File

@ -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] = "",

View File

@ -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;
}