mirror of
https://github.com/AsahiLinux/u-boot
synced 2025-02-17 22:49:02 +00:00
Merge branch 'master' of https://gitlab.denx.de/u-boot/custodians/u-boot-net
This commit is contained in:
commit
dd12b2f632
19 changed files with 1764 additions and 330 deletions
2
Makefile
2
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
|
||||
|
||||
|
|
|
@ -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.
|
|
@ -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
|
321
doc/driver-model/ethernet.rst
Normal file
321
doc/driver-model/ethernet.rst
Normal 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 = ð_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.
|
|
@ -8,6 +8,7 @@ Driver Model
|
|||
|
||||
debugging
|
||||
design
|
||||
ethernet
|
||||
fdt-fixup
|
||||
fs_firmware_loader
|
||||
i2c-howto
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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));
|
||||
|
||||
|
|
|
@ -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
897
drivers/net/phy/ncsi.c
Normal 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;
|
||||
}
|
|
@ -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
5
env/mmc.c
vendored
|
@ -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);
|
||||
|
|
|
@ -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
442
include/net/ncsi-pkt.h
Normal 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
14
include/net/ncsi.h
Normal 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);
|
|
@ -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);
|
||||
|
|
|
@ -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] = "",
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
Loading…
Add table
Reference in a new issue