nic: support Zynq-7000

Ref #1667
This commit is contained in:
Timo Wischer 2015-09-03 12:52:28 +02:00 committed by Christian Helmuth
parent 020758a2f1
commit 9f4b77c5c2
10 changed files with 1685 additions and 2 deletions

View File

@ -79,7 +79,7 @@ append_if [expr [have_spec omap4] || [have_spec arndale]] config "
</config>
</start>"
append_if [expr ![have_spec omap4] && ![have_spec arndale]] config "
append_if [expr ![have_spec omap4] && ![have_spec platform_arndale]] config "
<start name=\"$network_driver\">
<resource name=\"RAM\" quantum=\"4M\"/>
<provides><service name=\"Nic\"/></provides>
@ -154,7 +154,7 @@ lappend_if [have_spec pci] boot_modules pci_drv
lappend_if [expr [have_spec omap4] || [have_spec arndale]] boot_modules usb_drv
lappend_if [expr [have_spec omap4] || [have_spec arndale]] boot_modules usb_drv_net_stat
lappend_if [expr ![have_spec omap4] && ![have_spec arndale]] boot_modules nic_drv
lappend_if [expr ![have_spec omap4] && ![have_spec arndale]] boot_modules nic_drv_stat
lappend_if [expr ![have_spec omap4] && ![have_spec arndale] && ![have_spec zynq]] boot_modules nic_drv_stat
lappend_if [have_spec nova] boot_modules pci_device_pd
build_boot_image $boot_modules

View File

@ -0,0 +1,103 @@
/*
* \brief Base EMAC driver for the Xilinx EMAC PS used on Zynq devices
* \author Timo Wischer
* \date 2015-03-10
*/
/*
* Copyright (C) 2015 Genode Labs GmbH
*
* This file is part of the Genode OS framework, which is distributed
* under the terms of the GNU General Public License version 2.
*/
#ifndef _INCLUDE__DRIVERS__NIC__GEM__BUFFER_DESCRIPTOR_H_
#define _INCLUDE__DRIVERS__NIC__GEM__BUFFER_DESCRIPTOR_H_
/* Genode includes */
#include <os/attached_ram_dataspace.h>
#include <util/mmio.h>
using namespace Genode;
class Buffer_descriptor : protected Attached_ram_dataspace, protected Mmio
{
public:
static const size_t BUFFER_DESC_SIZE = 0x08;
static const size_t MAX_PACKAGE_SIZE = 1600;
static const size_t BUFFER_SIZE = BUFFER_DESC_SIZE + MAX_PACKAGE_SIZE;
private:
const size_t _buffer_count;
const size_t _buffer_offset;
unsigned int _descriptor_index;
char* const _buffer;
protected:
typedef struct {
uint32_t addr;
uint32_t status;
} descriptor_t;
descriptor_t* const _descriptors;
void _increment_descriptor_index()
{
_descriptor_index++;
_descriptor_index %= _buffer_count;
}
descriptor_t& _current_descriptor()
{
return _descriptors[_descriptor_index];
}
char* const _current_buffer()
{
char* const buffer = &_buffer[MAX_PACKAGE_SIZE * _descriptor_index];
return buffer;
}
public:
/*
* start of the ram spave contains all buffer descriptors
* after that the data spaces for the ethernet packages are following
*/
Buffer_descriptor(const size_t buffer_count = 1) :
Attached_ram_dataspace(env()->ram_session(), BUFFER_SIZE * buffer_count, UNCACHED),
Genode::Mmio( reinterpret_cast<addr_t>(local_addr<void>()) ),
_buffer_count(buffer_count),
_buffer_offset(BUFFER_DESC_SIZE * buffer_count),
_descriptor_index(0),
_buffer(local_addr<char>() + _buffer_offset),
_descriptors(local_addr<descriptor_t>())
{
/*
* Save address of _buffer.
* Has to be the physical address and not the virtual addresse,
* because the dma controller of the nic will use it.
* Ignore lower two bits,
* because this are status bits.
*/
for (unsigned int i=0; i<buffer_count; i++) {
_descriptors[i].addr = (phys_addr_buffer(i)) & ~0x3;
}
}
addr_t phys_addr() { return Dataspace_client(cap()).phys_addr(); }
addr_t phys_addr_buffer(const unsigned int index)
{
return phys_addr() + _buffer_offset + MAX_PACKAGE_SIZE * index;
}
};
#endif /* _INCLUDE__DRIVERS__NIC__GEM__BUFFER_DESCRIPTOR_H_ */

View File

@ -0,0 +1,538 @@
/*
* \brief Base EMAC driver for the Xilinx EMAC PS used on Zynq devices
* \author Timo Wischer
* \date 2015-03-10
*/
/*
* Copyright (C) 2015 Genode Labs GmbH
*
* This file is part of the Genode OS framework, which is distributed
* under the terms of the GNU General Public License version 2.
*/
#ifndef _INCLUDE__DRIVERS__NIC__XILINX_EMACPS_BASE_H_
#define _INCLUDE__DRIVERS__NIC__XILINX_EMACPS_BASE_H_
/* Genode includes */
#include <os/attached_mmio.h>
#include <nic_session/nic_session.h>
#include <nic/driver.h>
#include <timer_session/connection.h>
#include "system_control.h"
#include "tx_buffer_descriptor.h"
#include "rx_buffer_descriptor.h"
#include "marvell_phy.h"
namespace Genode
{
/**
* Base driver Xilinx EMAC PS module
*/
class Cadence_gem : private Genode::Attached_mmio, public Nic::Driver, public Phyio
{
private:
/**
* Control register
*/
struct Control : Register<0x00, 32>
{
struct Local_loopback : Bitfield<1, 1> {};
struct Rx_en : Bitfield<2, 1> {};
struct Tx_en : Bitfield<3, 1> {};
struct Mgmt_port_en : Bitfield<4, 1> {};
struct Clear_statistics : Bitfield<5, 1> {};
struct Start_tx : Bitfield<9, 1> {};
static access_t init() {
return Mgmt_port_en::bits(1) |
Tx_en::bits(1) |
Rx_en::bits(1);
}
static access_t start_tx() {
return (init() | Start_tx::bits(1));
}
};
/**
* Config register
*/
struct Config : Register<0x04, 32>
{
struct Speed_100 : Bitfield<0, 1> {};
struct Full_duplex : Bitfield<1, 1> {};
struct Copy_all : Bitfield<4, 1> {};
struct No_broadcast : Bitfield<5, 1> {};
struct Multi_hash_en : Bitfield<6, 1> {};
struct Gige_en : Bitfield<10, 1> {};
struct Fcs_remove : Bitfield<17, 1> {};
struct Mdc_clk_div : Bitfield<18, 3> {
enum {
DIV_32 = 0b010,
DIV_224 = 0b111,
};
};
struct Ignore_rx_fcs : Bitfield<26, 1> {};
};
/**
* Status register
*/
struct Status : Register<0x08, 32>
{
struct Phy_mgmt_idle : Bitfield<2, 1> {};
};
/**
* DMA Config register
*/
struct Dma_config : Register<0x10, 32>
{
struct Rx_pktbuf_memsz_sel : Bitfield<8, 2> {
enum {
SPACE_8KB = 0x3,
};
};
struct Tx_pktbuf_memsz_sel : Bitfield<10, 1> {
enum {
SPACE_4KB = 0x1,
};
};
struct Ahb_mem_rx_buf_size : Bitfield<16, 8> {
enum {
BUFFER_1600B = 0x19,
};
};
static access_t init()
{
return Ahb_mem_rx_buf_size::bits(Ahb_mem_rx_buf_size::BUFFER_1600B) |
Rx_pktbuf_memsz_sel::bits(Rx_pktbuf_memsz_sel::SPACE_8KB) |
Tx_pktbuf_memsz_sel::bits(Tx_pktbuf_memsz_sel::SPACE_4KB);
}
// TODO possibly enable transmition check sum offloading
};
/**
* Tx_Status register
*/
struct Tx_status : Register<0x14, 32>
{
struct Tx_complete : Bitfield<5, 1> {};
struct Tx_go : Bitfield<3, 1> {};
};
/**
* Receiving queue base addresse register
*/
struct Rx_qbar : Register<0x18, 32>
{
struct Addr : Bitfield<0, 32> {};
};
/**
* Transmition queue base addresse register
*/
struct Tx_qbar : Register<0x1C, 32>
{
struct Addr : Bitfield<0, 32> {};
};
/**
* Receive status register
*/
struct Rx_status : Register<0x20, 32>
{
struct Frame_reveived : Bitfield<1, 1> {};
struct Buffer_not_available : Bitfield<0, 1> {};
};
/**
* Interrupt status register
*/
struct Interrupt_status : Register<0x24, 32>
{
struct Rx_used_read : Bitfield<3, 1> {};
struct Rx_complete : Bitfield<1, 1> {};
};
/**
* Interrupt enable register
*/
struct Interrupt_enable : Register<0x28, 32>
{
struct Rx_complete : Bitfield<1, 1> {};
};
/**
* Interrupt disable register
*/
struct Interrupt_disable : Register<0x2C, 32>
{
struct Rx_complete : Bitfield<1, 1> {};
};
/**
* PHY maintenance register
*/
struct Phy_maintenance : Register<0x34, 32>
{
struct Clause_22 : Bitfield<30, 1> {};
struct Operation : Bitfield<28, 2> {
enum Type {
READ = 0b10,
WRITE = 0b01
};
};
struct Phy_addr : Bitfield<23, 5> {};
struct Reg_addr : Bitfield<18, 5> {};
struct Must_10 : Bitfield<16, 2> {
enum { INIT = 0b10 };
};
struct Data : Bitfield<0, 16> {};
};
/**
* MAC hash register
*/
struct Hash_register : Register<0x80, 64>
{
struct Low_hash : Bitfield<0, 32> { };
struct High_hash : Bitfield<32, 16> { };
};
/**
* MAC Addresse
*/
struct Mac_addr_1 : Register<0x88, 64>
{
struct Low_addr : Bitfield<0, 32> { };
struct High_addr : Bitfield<32, 16> { };
};
/**
* Counter for the successfully transmitted frames
*/
struct Frames_transmitted : Register<0x108, 32>
{
struct Counter : Bitfield<0, 32> { };
};
/**
* Counter for the successfully received frames
*/
struct Frames_received : Register<0x158, 32>
{
struct Counter : Bitfield<0, 32> { };
};
/**
* Counter for the successfully received frames
*/
struct Rx_overrun_errors : Register<0x1A4, 32>
{
struct Counter : Bitfield<0, 10> { };
};
class Phy_timeout_for_idle : public Genode::Exception {};
class Unkown_ethernet_speed : public Genode::Exception {};
Timer::Connection _timer;
System_control _sys_ctrl;
Tx_buffer_descriptor _tx_buffer;
Rx_buffer_descriptor _rx_buffer;
Nic::Rx_buffer_alloc& _rx_buffer_alloc;
Nic::Driver_notification &_notify;
enum { IRQ_STACK_SIZE = 4096 };
const Genode::Irq_activation _irq_activation;
Marvel_phy _phy;
void _init()
{
// TODO checksum offloading and pause frames
/* see 16.3.2 Configure the Controller */
/* 1. Program the Network Configuration register (gem.net_cfg) */
write<Config>(
Config::Speed_100::bits(1) |
Config::Full_duplex::bits(1) |
Config::Multi_hash_en::bits(1) |
Config::Mdc_clk_div::bits(Config::Mdc_clk_div::DIV_32) |
Config::Fcs_remove::bits(1)
);
/* 2. set mac address */
Nic::Mac_address mac;
mac.addr[5] = 0x12;
mac.addr[4] = 0x34;
mac.addr[3] = 0x56;
mac.addr[2] = 0x78;
mac.addr[1] = 0x9A;
mac.addr[0] = 0xBC;
mac_address(mac);
write<Rx_qbar>( _rx_buffer.phys_addr() );
write<Tx_qbar>( _tx_buffer.phys_addr() );
/* 3. Program the DMA Configuration register (gem.dma_cfg) */
write<Dma_config>( Dma_config::init() );
/*
* 4. Program the Network Control Register (gem.net_ctrl)
* Enable MDIO, transmitter and receiver
*/
write<Control>(Control::init());
_phy.init();
/* change emac clocks depending on phy autonegation result */
uint32_t rclk = 0;
uint32_t clk = 0;
switch (_phy.eth_speed()) {
case SPEED_1000:
write<Config::Gige_en>(1);
rclk = (0 << 4) | (1 << 0);
clk = (1 << 20) | (8 << 8) | (0 << 4) | (1 << 0);
break;
case SPEED_100:
write<Config::Gige_en>(0);
write<Config::Speed_100>(1);
rclk = 1 << 0;
clk = (5 << 20) | (8 << 8) | (0 << 4) | (1 << 0);
break;
case SPEED_10:
write<Config::Gige_en>(0);
write<Config::Speed_100>(0);
rclk = 1 << 0;
/* FIXME untested */
clk = (5 << 20) | (8 << 8) | (0 << 4) | (1 << 0);
break;
default:
throw Unkown_ethernet_speed();
}
_sys_ctrl.set_clk(clk, rclk);
/* 16.3.6 Configure Interrupts */
write<Interrupt_enable>(Interrupt_enable::Rx_complete::bits(1));
}
void _deinit()
{
/* 16.3.1 Initialize the Controller */
/* Disable all interrupts */
write<Interrupt_disable>(0x7FFFEFF);
/* Disable the receiver & transmitter */
write<Control>(0);
write<Control>(Control::Clear_statistics::bits(1));
write<Tx_status>(0xFF);
write<Rx_status>(0x0F);
write<Phy_maintenance>(0);
write<Rx_qbar>(0);
write<Tx_qbar>(0);
/* Clear the Hash registers for the mac address
* pointed by AddressPtr
*/
write<Hash_register>(0);
}
void _mdio_wait()
{
uint32_t timeout = 200;
/* Wait till MDIO interface is ready to accept a new transaction. */
while (!read<Status::Phy_mgmt_idle>()) {
if (timeout <= 0) {
PWRN("%s: Timeout\n", __func__);
throw Phy_timeout_for_idle();
}
_timer.msleep(1);
timeout--;
}
}
void _phy_setup_op(const uint8_t phyaddr, const uint8_t regnum, const uint16_t data, const Phy_maintenance::Operation::Type op)
{
_mdio_wait();
/* Write mgtcr and wait for completion */
write<Phy_maintenance>(
Phy_maintenance::Clause_22::bits(1) |
Phy_maintenance::Operation::bits(op) |
Phy_maintenance::Phy_addr::bits(phyaddr) |
Phy_maintenance::Reg_addr::bits(regnum) |
Phy_maintenance::Must_10::bits(Phy_maintenance::Must_10::INIT) |
Phy_maintenance::Data::bits(data) );
_mdio_wait();
}
public:
/**
* Constructor
*
* \param base MMIO base address
*/
Cadence_gem(addr_t const base, size_t const size, const int irq, Nic::Rx_buffer_alloc& alloc,
Nic::Driver_notification &notify) :
Genode::Attached_mmio(base, size),
_rx_buffer_alloc(alloc),
_notify(notify),
_irq_activation(irq, *this, IRQ_STACK_SIZE),
_phy(*this)
{
_deinit();
_init();
}
~Cadence_gem()
{
// TODO dsiable tx and rx and clean up irq registration
_deinit();
}
void phy_write(const uint8_t phyaddr, const uint8_t regnum, const uint16_t data)
{
_phy_setup_op(phyaddr, regnum, data, Phy_maintenance::Operation::WRITE);
}
void phy_read(const uint8_t phyaddr, const uint8_t regnum, uint16_t& data)
{
_phy_setup_op(phyaddr, regnum, 0, Phy_maintenance::Operation::READ);
data = read<Phy_maintenance::Data>();
}
void mac_address(const Nic::Mac_address mac)
{
const uint32_t* const low_addr_pointer = reinterpret_cast<const uint32_t*>(&mac.addr[0]);
const uint16_t* const high_addr_pointer = reinterpret_cast<const uint16_t*>(&mac.addr[4]);
write<Mac_addr_1::Low_addr>(*low_addr_pointer);
write<Mac_addr_1::High_addr>(*high_addr_pointer);
}
/***************************
** Nic::Driver interface **
***************************/
virtual Nic::Mac_address mac_address()
{
Nic::Mac_address mac;
uint32_t* const low_addr_pointer = reinterpret_cast<uint32_t*>(&mac.addr[0]);
uint16_t* const high_addr_pointer = reinterpret_cast<uint16_t*>(&mac.addr[4]);
*low_addr_pointer = read<Mac_addr_1::Low_addr>();
*high_addr_pointer = read<Mac_addr_1::High_addr>();
return mac;
}
virtual bool link_state()
{
/* XXX return always true for now */
return true;
}
virtual void tx(char const *packet, Genode::size_t size)
{
_tx_buffer.add_to_queue(packet, size);
write<Control>(Control::start_tx());
}
/******************************
** Irq_activation interface **
******************************/
virtual void handle_irq(int irq_number)
{
/* 16.3.9 Receiving Frames */
/* read interrupt status, to detect the interrupt reasone */
const Interrupt_status::access_t status = read<Interrupt_status>();
const Rx_status::access_t rxStatus = read<Rx_status>();
if ( Interrupt_status::Rx_complete::get(status) ) {
while (_rx_buffer.package_available()) {
// TODO use this buffer directly as the destination for the DMA controller
// to minimize the overrun errors
const size_t buffer_size = _rx_buffer.package_length();
char* const buffer = static_cast<char*>( _rx_buffer_alloc.alloc(buffer_size) );
/*
* copy data from rx buffer to new allocated buffer.
* Has to be copied,
* because the extern allocater possibly is using the cache.
*/
if ( _rx_buffer.get_package(buffer, buffer_size) != buffer_size ) {
PWRN("Package not fully copiied. Package ignored.");
return;
}
/* clearing error flags */
write<Interrupt_status::Rx_used_read>(1);
write<Rx_status::Buffer_not_available>(1);
/* comit buffer to system services */
_rx_buffer_alloc.submit();
}
/* check, if there was lost some packages */
const uint16_t lost_packages = read<Rx_overrun_errors::Counter>();
if (lost_packages > 0) {
PWRN("%d packages lost (%d packages successfully received)!",
lost_packages, read<Frames_received>());
}
/* reset reveive complete interrupt */
write<Rx_status>(Rx_status::Frame_reveived::bits(1));
write<Interrupt_status>(Interrupt_status::Rx_complete::bits(1));
} else {
PWRN("IRQ %d with unkown reasone recevied (status: %08x).", irq_number, status);
}
}
};
}
#endif /* _INCLUDE__DRIVERS__NIC__XILINX_EMACPS_BASE_H_ */

View File

@ -0,0 +1,60 @@
/*
* \brief EMACPS NIC driver for Xilix Zynq-7000
* \author Timo Wischer
* \date 2015-03-10
*/
/*
* Copyright (C) 2015 Genode Labs GmbH
*
* This file is part of the Genode OS framework, which is distributed
* under the terms of the GNU General Public License version 2.
*/
/* Genode includes */
#include <base/sleep.h>
#include <cap_session/connection.h>
#include <nic/component.h>
#include <drivers/board_base.h>
#include "cadence_gem.h"
int main(int, char **)
{
using namespace Genode;
printf("--- Xilinx Ethernet MAC PS NIC driver started ---\n");
/**
* Factory used by 'Nic::Root' at session creation/destruction time
*/
struct Emacps_driver_factory : Nic::Driver_factory
{
Nic::Driver *create(Nic::Rx_buffer_alloc &alloc,
Nic::Driver_notification &notify)
{
return new (env()->heap())
Cadence_gem(Board_base::EMAC_0_MMIO_BASE,
Board_base::EMAC_0_MMIO_SIZE,
Board_base::EMAC_0_IRQ,
alloc, notify);
}
void destroy(Nic::Driver *driver)
{
Genode::destroy(env()->heap(), static_cast<Cadence_gem*>(driver));
}
} driver_factory;
enum { STACK_SIZE = 4096 };
static Cap_connection cap;
static Rpc_entrypoint ep(&cap, STACK_SIZE, "nic_ep");
static Nic::Root nic_root(&ep, env()->heap(), driver_factory);
env()->parent()->announce(ep.manage(&nic_root));
Genode::sleep_forever();
return 0;
}

View File

@ -0,0 +1,579 @@
/*
* \brief Phy driver for Marvell chips
* \author Johannes Schlatow
* \author Timo Wischer
* \date 2015-05-11
*/
/*
* Copyright (C) 2015 Genode Labs GmbH
*
* This file is part of the Genode OS framework, which is distributed
* under the terms of the GNU General Public License version 2.
*/
#ifndef _INCLUDE__DRIVERS__NIC__GEM__MARVELL_PHY_H_
#define _INCLUDE__DRIVERS__NIC__GEM__MARVELL_PHY_H_
/* Genode includes */
#include <os/attached_mmio.h>
#include <nic_session/nic_session.h>
#include <nic/driver.h>
#include <timer_session/connection.h>
#include "phyio.h"
namespace Genode
{
enum Eth_speed {
UNDEFINED,
SPEED_10 = 10,
SPEED_100 = 100,
SPEED_1000 = 1000
};
class MII_phy
{
protected:
/**
* \param _OFFSET Offset/number of the register.
*
* For further details See 'Genode::Register'.
*/
template <uint8_t _OFFSET>
struct Phy_register : public Genode::Register<16>
{
enum {
OFFSET = _OFFSET,
};
typedef Phy_register<_OFFSET>
Register_base;
/**
* A region within a register
*
* \param _SHIFT Bit shift of the first bit within the
* compound register.
* \param _WIDTH bit width of the region
*
* For details see 'Genode::Register::Bitfield'.
*/
template <unsigned long _SHIFT, unsigned long _WIDTH>
struct Bitfield : public Genode::Register<16>::
template Bitfield<_SHIFT, _WIDTH>
{
typedef Bitfield<_SHIFT, _WIDTH> Bitfield_base;
/* back reference to containing register */
typedef Phy_register<_OFFSET>
Compound_reg;
};
};
/*************************
* Generic MII registers *
*************************/
/* Basic mode control register */
struct Bmcr : Phy_register<0x00>
{
struct Resv : Bitfield<0, 6> { }; /* Unused... */
struct Speed1000 : Bitfield<6, 1> { }; /* MSB of Speed (1000) */
struct Ctst : Bitfield<7, 1> { }; /* Collision test */
struct Fulldplx : Bitfield<8, 1> { }; /* Full duplex */
struct Anrestart : Bitfield<9, 1> { }; /* Auto negotiation restart */
struct Isolate : Bitfield<10,1> { }; /* Disconnect DP83840 from MII */
struct Pdown : Bitfield<11,1> { }; /* Powerdown the DP83840 */
struct Anenable : Bitfield<12,1> { }; /* Enable auto negotiation */
struct Speed100 : Bitfield<13,1> { }; /* Select 100Mbps */
struct Loopback : Bitfield<14,1> { }; /* TXD loopback bits */
struct Reset : Bitfield<15,1> { }; /* Reset the DP83840 */
};
/* Basic mode status register */
struct Bmsr : Phy_register<0x01>
{
enum {
INVALID = 0xFFFF
};
struct Ercap : Bitfield<0, 1> { }; /* Ext-reg capability */
struct Jcd : Bitfield<1, 1> { }; /* Jabber detected */
struct Lstatus : Bitfield<2, 1> { }; /* Link status */
struct Anegcapable : Bitfield<3, 1> { }; /* Able to do auto-negotiation */
struct Rfault : Bitfield<4, 1> { }; /* Remote fault detected */
struct Anegcomplete: Bitfield<5, 1> { }; /* Auto-negotiation complete */
struct Resv : Bitfield<6, 1> { }; /* Unused... */
struct Estaten : Bitfield<7, 1> { }; /* Extended Status in R15 */
struct Half2_100 : Bitfield<8, 1> { }; /* Can do 100BASE-T2 HDX */
struct Full2_100 : Bitfield<9, 1> { }; /* Can do 100BASE-T2 FDX */
struct Half_10 : Bitfield<10,1> { }; /* Can do 10mbps, half-duplex */
struct Full_10 : Bitfield<11,1> { }; /* Can do 10mbps, full-duplex */
struct Half_100 : Bitfield<12,1> { }; /* Can do 100mbps, half-duplex */
struct Full_100 : Bitfield<13,1> { }; /* Can do 100mbps, full-duplex */
struct Base4_100 : Bitfield<14,1> { }; /* Can do 100mbps, 4k packets */
};
/* ID register 1 */
struct Idr1 : Phy_register<0x02> { };
/* ID register 2 */
struct Idr2 : Phy_register<0x03> { };
/* Advertisement control reg */
struct Advertise : Phy_register<0x04>
{
struct Csma : Bitfield<0, 1> { };
struct Half_10 : Bitfield<5, 1> { }; /* Try for 10mbps half-duplex */
struct FullX_1000 : Bitfield<5, 1> { }; /* Try for 1000BASE-X full-duplex */
struct Full_10 : Bitfield<6, 1> { }; /* Try for 10mbps full-duplex */
struct HalfX_1000 : Bitfield<6, 1> { }; /* Try for 1000BASE-X half-duplex */
struct Half_100 : Bitfield<7, 1> { }; /* Try for 100mbps half-duplex */
struct PauseX_1000 : Bitfield<7, 1> { }; /* Try for 1000BASE-X pause */
struct Full_100 : Bitfield<8, 1> { }; /* Try for 100mbps full-duplex */
struct AsymXPSE_1000: Bitfield<8, 1> { }; /* Try for 1000BASE-X asym pause */
struct Base4_100 : Bitfield<9, 1> { }; /* Try for 100mbps 4k packets */
struct Pause_cap : Bitfield<10,1> { }; /* Try for pause */
struct Pause_asym : Bitfield<11,1> { }; /* Try for asymetrict pause */
struct Rfault : Bitfield<13,1> { }; /* Say we can detect faults */
struct Lpack : Bitfield<14,1> { }; /* Ack link partners response */
struct Npage : Bitfield<15,1> { }; /* Next page bit */
};
/* 1000BASE-T control */
struct Ctrl1000 : Phy_register<0x09>
{
struct Half_1000 : Bitfield<8, 1> { }; /* Advertise 1000BASE-T full duplex */
struct Full_1000 : Bitfield<9, 1> { }; /* Advertise 1000BASE-T half duplex */
};
};
class Marvel_phy : public MII_phy
{
public:
class Phy_timeout_after_reset : public Genode::Exception {};
private:
enum {
PHY_AUTONEGOTIATE_TIMEOUT = 5000
};
Timer::Connection _timer;
Phyio& _phyio;
uint8_t _phyaddr;
bool _link_up;
Eth_speed _eth_speed;
/*************************
* 88E1310 PHY registers *
*************************/
struct Led_ctrl : Phy_register<16>
{
struct Data : Bitfield<0, 4> { };
};
struct Irq_en : Phy_register<18> { };
struct RGMII_ctrl : Phy_register<21> { };
struct Page_select : Phy_register<22> { };
/* 88E1011 PHY Status Register */
struct Phy_stat : Phy_register<0x11>
{
struct Link : Bitfield<10,1> { };
struct Spddone : Bitfield<11,1> { };
struct Duplex : Bitfield<13,1> { };
struct Speed_100 : Bitfield<14,1> { };
struct Speed_1000 : Bitfield<15,1> { };
};
/**
* Read register of detected PHY.
*/
inline uint16_t _phy_read(const uint8_t regnum) const
{
uint16_t val;
_phyio.phy_read(_phyaddr, regnum, val);
return val;
}
/**
* Write register of detected PHY.
*/
inline void _phy_write(const uint8_t regnum, const uint16_t data) const
{
_phyio.phy_write(_phyaddr, regnum, data);
}
/**
* Read PHY register 'T'
*/
template <typename T>
inline typename T::Register_base::access_t phy_read() const
{
typedef typename T::Register_base Register;
return _phy_read(Register::OFFSET);
}
/**
* Read the bitfield 'T' of PHY register
*/
template <typename T>
inline typename T::Bitfield_base::Compound_reg::access_t
phy_read() const
{
typedef typename T::Bitfield_base Bitfield;
typedef typename Bitfield::Compound_reg Register;
return Bitfield::get(_phy_read(Register::OFFSET));
}
/**
* Write PHY register 'T'
*/
template <typename T>
inline void phy_write(uint16_t const val) const
{
typedef typename T::Register_base Register;
return _phy_write(Register::OFFSET, val);
}
/**
* Detect PHY address.
*/
void phy_detection()
{
/* check _phyaddr */
if (_phyaddr != -1) {
Bmsr::access_t phyreg = phy_read<Bmsr>();
if ((phyreg != Bmsr::INVALID) &&
Bmsr::Full_10::get(phyreg) &&
Bmsr::Anegcapable::get(phyreg)) {
/* Found a valid PHY address */
PDBG("Default phy address %d is valid\n", _phyaddr);
return;
} else {
PDBG("PHY address is not setup correctly %d\n", _phyaddr);
_phyaddr = -1;
}
}
PDBG("detecting phy address\n");
if (_phyaddr == -1) {
/* detect the PHY address */
for (int i = 31; i >= 0; i--) {
_phyaddr = i;
Bmsr::access_t phyreg = phy_read<Bmsr>();
if ((phyreg != Bmsr::INVALID) &&
Bmsr::Full_10::get(phyreg) &&
Bmsr::Anegcapable::get(phyreg)) {
/* Found a valid PHY address */
PDBG("Found valid phy address, %d\n", i);
return;
}
}
}
PDBG("PHY is not detected\n");
_phyaddr = -1;
}
uint32_t get_phy_id()
{
uint32_t phy_id = 0;
/* Grab the bits from PHYIR1, and put them
* in the upper half */
phy_id = phy_read<Idr1>() << 16;
/* Grab the bits from PHYIR2, and put them in the lower half */
phy_id |= phy_read<Idr2>();
return phy_id;
}
void m88e1310_config()
{
/* LED link and activity */
phy_write<Page_select>(0x0003);
Led_ctrl::access_t led = phy_read<Led_ctrl>();
Led_ctrl::Data::set(led, 0x1);
phy_write<Led_ctrl>(led);
/* Set LED2/INT to INT mode, low active */
phy_write<Page_select>(0x0003);
Irq_en::access_t irq = phy_read<Irq_en>();
irq = (irq & 0x77ff) | 0x0880;
phy_write<Irq_en>(irq);
/* Set RGMII delay */
phy_write<Page_select>(0x0002);
RGMII_ctrl::access_t ctrl = phy_read<RGMII_ctrl>();
ctrl |= 0x0030;
phy_write<RGMII_ctrl>(ctrl);
/* Ensure to return to page 0 */
phy_write<Page_select>(0x0000);
genphy_config_aneg();
phy_reset();
}
int genphy_config_aneg()
{
/**
* Description: If auto-negotiation is enabled, we configure the
* advertising, and then restart auto-negotiation. If it is not
* enabled, then we write the BMCR.
*/
int result = genphy_config_advert();
if (result < 0) /* error */
return result;
if (result == 0) {
PDBG("Config not changed");
/* Advertisment hasn't changed, but maybe aneg was never on to
* begin with? Or maybe phy was isolated? */
uint16_t ctl = phy_read<Bmcr>();
if (!Bmcr::Anenable::get(ctl) || Bmcr::Isolate::get(ctl))
result = 1; /* do restart aneg */
} else {
PDBG("Config changed");
}
/* Only restart aneg if we are advertising something different
* than we were before. */
if (result > 0)
result = genphy_restart_aneg();
return result;
}
int genphy_config_advert()
{
/**
* Description: Writes MII_ADVERTISE with the appropriate values,
* after sanitizing the values to make sure we only advertise
* what is supported. Returns < 0 on error, 0 if the PHY's advertisement
* hasn't changed, and > 0 if it has changed.
*/
int changed = 0;
/* Setup standard advertisement */
Advertise::access_t adv = phy_read<Advertise>();
Advertise::access_t oldadv = adv;
Advertise::Base4_100::set(adv, 0);
Advertise::Pause_cap::set(adv, 1);
Advertise::Pause_asym::set(adv, 1);
Advertise::Half_10::set(adv, 1);
Advertise::Full_10::set(adv, 1);
Advertise::Half_100::set(adv, 1);
Advertise::Full_100::set(adv, 1);
if (adv != oldadv) {
phy_write<Advertise>(adv);
changed = 1;
}
/* Configure gigabit if it's supported */
adv = phy_read<Ctrl1000>();
oldadv = adv;
Ctrl1000::Full_1000::set(adv, 1);
Ctrl1000::Half_1000::set(adv, 1);
if (adv != oldadv) {
phy_write<Ctrl1000>(adv);
changed = 1;
}
return changed;
}
int genphy_restart_aneg()
{
Bmcr::access_t ctl = phy_read<Bmcr>();
Bmcr::Anenable::set(ctl, 1);
Bmcr::Anrestart::set(ctl, 1);
/* Don't isolate the PHY if we're negotiating */
Bmcr::Isolate::set(ctl, 0);
phy_write<Bmcr>(ctl);
return 0;
}
int phy_reset()
{
int timeout = 500;
Bmcr::access_t reg = phy_read<Bmcr>();
Bmcr::Reset::set(reg, 1);
phy_write<Bmcr>(reg);
/*
* Poll the control register for the reset bit to go to 0 (it is
* auto-clearing). This should happen within 0.5 seconds per the
* IEEE spec.
*/
while (phy_read<Bmcr::Reset>() && timeout--) {
_timer.msleep(1);
}
if (phy_read<Bmcr::Reset>()) {
PWRN("PHY reset timed out\n");
throw Phy_timeout_after_reset();
}
return 0;
}
int m88e1011s_startup()
{
genphy_update_link();
m88e1xxx_parse_status();
return 0;
}
int genphy_update_link()
{
/**
* Description: Update the value in phydev->link to reflect the
* current link value. In order to do this, we need to read
* the status register twice, keeping the second value.
*/
/*
* Wait if the link is up, and autonegotiation is in progress
* (ie - we're capable and it's not done)
*/
Bmsr::access_t mii_reg = phy_read<Bmsr>();
/*
* If we already saw the link up, and it hasn't gone down, then
* we don't need to wait for autoneg again
*/
if (_link_up && Bmsr::Lstatus::get(mii_reg))
return 0;
if ( Bmsr::Anegcapable::get(mii_reg) && !Bmsr::Anegcomplete::get(mii_reg) ) {
int i = 0;
Genode::printf("Waiting for PHY auto negotiation to complete");
while (!Bmsr::Anegcomplete::get(mii_reg)) {
/*
* Timeout reached ?
*/
if (i > PHY_AUTONEGOTIATE_TIMEOUT) {
PWRN(" TIMEOUT !\n");
_link_up = false;
return 0;
}
if ((i++ % 500) == 0)
Genode::printf(".");
_timer.msleep(1);
mii_reg = phy_read<Bmsr>();
}
Genode::printf(" done\n");
_link_up = true;
} else {
/* Read the link a second time to clear the latched state */
mii_reg = phy_read<Bmsr>();
if (Bmsr::Lstatus::get(mii_reg))
_link_up = true;
else
_link_up = false;
}
return 0;
}
int m88e1xxx_parse_status()
{
/**
* Parse the 88E1011's status register for speed and duplex
* information
*/
Phy_stat::access_t stat = phy_read<Phy_stat>();
if ( Phy_stat::Link::get(stat) &&
!Phy_stat::Spddone::get(stat)) {
int i = 0;
PDBG("Waiting for PHY realtime link");
while (!phy_read<Phy_stat::Spddone>()) {
/* Timeout reached ? */
if (i > PHY_AUTONEGOTIATE_TIMEOUT) {
PWRN(" TIMEOUT !");
_link_up = false;
break;
}
if ((i++ % 1000) == 0)
Genode::printf(".");
_timer.msleep(1);
}
PINF(" done");
_timer.msleep(500);
} else {
if (Phy_stat::Link::get(stat))
_link_up = true;
else
_link_up = false;
}
// TODO change emac configuration if half duplex
if (Phy_stat::Speed_1000::get(stat))
_eth_speed = SPEED_1000;
else if (Phy_stat::Speed_100::get(stat))
_eth_speed = SPEED_100;
else
_eth_speed = SPEED_10;
return 0;
}
public:
Marvel_phy(Phyio& phyio) :
_phyio(phyio),
_phyaddr(0),
_link_up(false),
_eth_speed(UNDEFINED)
{ }
void init()
{
phy_detection();
uint32_t phy_id = get_phy_id();
PDBG("The found phy has the id %08x", phy_id);
phy_reset();
m88e1310_config();
m88e1011s_startup();
}
Eth_speed eth_speed() { return _eth_speed; }
};
}
#endif /* _INCLUDE__DRIVERS__NIC__GEM__MARVELL_PHY_H_ */

View File

@ -0,0 +1,31 @@
/*
* \brief Phy driver for Marvell chips
* \author Timo Wischer
* \date 2015-05-11
*/
/*
* Copyright (C) 2015 Genode Labs GmbH
*
* This file is part of the Genode OS framework, which is distributed
* under the terms of the GNU General Public License version 2.
*/
#ifndef _INCLUDE__DRIVERS__NIC__GEM__PHYIO_H_
#define _INCLUDE__DRIVERS__NIC__GEM__PHYIO_H_
namespace Genode
{
class Phyio
{
public:
virtual void phy_write(const uint8_t phyaddr, const uint8_t regnum, const uint16_t data) = 0;
virtual void phy_read(const uint8_t phyaddr, const uint8_t regnum, uint16_t &data) = 0;
};
}
#endif /* _INCLUDE__DRIVERS__NIC__GEM__PHYIO_H_ */

View File

@ -0,0 +1,134 @@
/*
* \brief Base EMAC driver for the Xilinx EMAC PS used on Zynq devices
* \author Timo Wischer
* \date 2015-03-10
*/
/*
* Copyright (C) 2015 Genode Labs GmbH
*
* This file is part of the Genode OS framework, which is distributed
* under the terms of the GNU General Public License version 2.
*/
#ifndef _INCLUDE__DRIVERS__NIC__GEM__RX_BUFFER_DESCRIPTOR_H_
#define _INCLUDE__DRIVERS__NIC__GEM__RX_BUFFER_DESCRIPTOR_H_
#include "buffer_descriptor.h"
using namespace Genode;
class Rx_buffer_descriptor : public Buffer_descriptor
{
private:
struct Addr : Register<0x00, 32> {
struct Addr31to2 : Bitfield<2, 28> {};
struct Wrap : Bitfield<1, 1> {};
struct Package_available : Bitfield<0, 1> {};
};
struct Status : Register<0x04, 32> {
struct Length : Bitfield<0, 13> {};
struct Start_of_frame : Bitfield<14, 1> {};
struct End_of_frame : Bitfield<15, 1> {};
};
enum { BUFFER_COUNT = 16 };
/**
* @brief _set_buffer_processed resets the available flag
* So the DMA controller can use this buffer for an received package.
* The buffer index will be incremented, too.
* So the right sequenz of packages will be keeped.
*/
void _set_package_processed()
{
/* reset package available for new package */
_current_descriptor().addr &= ~Addr::Package_available::bits(1);
/* use next buffer descriptor for next package */
_increment_descriptor_index();
}
public:
Rx_buffer_descriptor() : Buffer_descriptor(BUFFER_COUNT)
{
/*
* mark the last buffer descriptor
* so the dma will start at the beginning again
*/
_descriptors[BUFFER_COUNT-1].addr |= Addr::Wrap::bits(1);
}
bool package_available()
{
for (unsigned int i=0; i<BUFFER_COUNT; i++) {
const bool available = Addr::Package_available::get(_current_descriptor().addr);
if (available) {
return true;
}
_increment_descriptor_index();
}
return false;
}
size_t package_length()
{
if (!package_available())
return 0;
return Status::Length::get(_current_descriptor().status);
}
size_t get_package(char* const package, const size_t max_length)
{
if (!package_available())
return 0;
const Status::access_t status = _current_descriptor().status;
if (!Status::Start_of_frame::get(status) || !Status::End_of_frame::get(status)) {
PWRN("Package splitted over more than one descriptor. Package ignored!");
_set_package_processed();
return 0;
}
const size_t length = Status::Length::get(status);
if (length > max_length) {
PWRN("Buffer for received package to small. Package ignored!");
_set_package_processed();
return 0;
}
const char* const src_buffer = _current_buffer();
memcpy(package, src_buffer, length);
_set_package_processed();
return length;
}
void show_mem_diffs()
{
static unsigned int old_data[0x1F];
PDBG("Rx buffer:");
const unsigned int* const cur_data = local_addr<unsigned int>();
for (unsigned i=0; i<sizeof(old_data)/sizeof(old_data[0]); i++) {
if (cur_data[i] != old_data[i]) {
PDBG("%04x: %08x -> %08x", i*4, old_data[i], cur_data[i]);
}
}
memcpy(old_data, cur_data, sizeof(old_data));
}
};
#endif /* _INCLUDE__DRIVERS__NIC__GEM__RX_BUFFER_DESCRIPTOR_H_ */

View File

@ -0,0 +1,151 @@
/*
* \brief Base EMAC driver for the Xilinx EMAC PS used on Zynq devices
* \author Timo Wischer
* \date 2015-03-10
*/
/*
* Copyright (C) 2015 Genode Labs GmbH
*
* This file is part of the Genode OS framework, which is distributed
* under the terms of the GNU General Public License version 2.
*/
#ifndef _INCLUDE__DRIVERS__NIC__GEM__SYSTEM_CONTROL_H_
#define _INCLUDE__DRIVERS__NIC__GEM__SYSTEM_CONTROL_H_
/* Genode includes */
#include <os/attached_mmio.h>
#include <timer_session/connection.h>
#include <drivers/board_base.h>
using namespace Genode;
class System_control : private Genode::Attached_mmio
{
private:
struct Lock : Register<0x4, 32> {
enum { MAGIC = 0x767B };
};
struct Unlock : Register<0x8, 32> {
enum { MAGIC = 0xDF0D };
};
struct Gem0_rclk_ctrl : Register<0x138, 32> { };
struct Gem0_clk_ctrl : Register<0x140, 32> { };
struct Mio_pin_16 : Register<0x740, 32> {
struct Tri_state_enable : Bitfield<0, 1> {};
struct Level_0_mux : Bitfield<1, 1> {
enum { ETH0 = 0b1 };
};
struct Fast_cmos_edge : Bitfield<8, 1> {};
struct IO_type : Bitfield<9, 3> {
enum { LVCMOS18 = 0b001 };
};
static access_t FAST_LVCMOS18_ETH0() {
return Fast_cmos_edge::bits(1) |
IO_type::bits(IO_type::LVCMOS18) |
Level_0_mux::bits(Level_0_mux::ETH0);
}
static access_t FAST_LVCMOS18_ETH0_TRISTATE() {
return FAST_LVCMOS18_ETH0() |
Tri_state_enable::bits(1);
}
};
struct Mio_pin_17 : Register<0x744, 32> { };
struct Mio_pin_18 : Register<0x748, 32> { };
struct Mio_pin_19 : Register<0x74C, 32> { };
struct Mio_pin_20 : Register<0x750, 32> { };
struct Mio_pin_21 : Register<0x754, 32> { };
struct Mio_pin_22 : Register<0x758, 32> { };
struct Mio_pin_23 : Register<0x75C, 32> { };
struct Mio_pin_24 : Register<0x760, 32> { };
struct Mio_pin_25 : Register<0x764, 32> { };
struct Mio_pin_26 : Register<0x768, 32> { };
struct Mio_pin_27 : Register<0x76C, 32> { };
struct Mio_pin_52 : Register<0x7D0, 32> {
struct Level_3_mux : Bitfield<5, 3> {
enum { MDIO0 = 0b100 };
};
static access_t LVCMOS18_MDIO0() {
return Mio_pin_16::IO_type::bits(Mio_pin_16::IO_type::LVCMOS18) |
Level_3_mux::bits(Level_3_mux::MDIO0);
}
};
struct Mio_pin_53 : Register<0x7D4, 32> { };
struct Gpio_b_ctrl : Register<0xB00, 32> {
struct Vref_enable : Bitfield<0, 1> {};
};
class Lock_guard
{
private:
System_control& _sys_ctrl;
void _unlock() { _sys_ctrl.write<Unlock>(Unlock::MAGIC); }
void _lock() { _sys_ctrl.write<Lock>(Lock::MAGIC); }
public:
Lock_guard(System_control& sys_ctrl) : _sys_ctrl(sys_ctrl)
{
_unlock();
}
~Lock_guard()
{
_lock();
}
};
unsigned int old_data[0x300];
public:
System_control() :
Attached_mmio(Board_base::MMIO_1_BASE, 0xB80)
{
Lock_guard lock(*this);
write<Mio_pin_16>(Mio_pin_16::FAST_LVCMOS18_ETH0());
write<Mio_pin_17>(Mio_pin_16::FAST_LVCMOS18_ETH0());
write<Mio_pin_18>(Mio_pin_16::FAST_LVCMOS18_ETH0());
write<Mio_pin_19>(Mio_pin_16::FAST_LVCMOS18_ETH0());
write<Mio_pin_20>(Mio_pin_16::FAST_LVCMOS18_ETH0());
write<Mio_pin_21>(Mio_pin_16::FAST_LVCMOS18_ETH0());
write<Mio_pin_22>(Mio_pin_16::FAST_LVCMOS18_ETH0_TRISTATE());
write<Mio_pin_23>(Mio_pin_16::FAST_LVCMOS18_ETH0_TRISTATE());
write<Mio_pin_24>(Mio_pin_16::FAST_LVCMOS18_ETH0_TRISTATE());
write<Mio_pin_25>(Mio_pin_16::FAST_LVCMOS18_ETH0_TRISTATE());
write<Mio_pin_26>(Mio_pin_16::FAST_LVCMOS18_ETH0_TRISTATE());
write<Mio_pin_27>(Mio_pin_16::FAST_LVCMOS18_ETH0_TRISTATE());
write<Mio_pin_52>(Mio_pin_52::LVCMOS18_MDIO0());
write<Mio_pin_53>(Mio_pin_52::LVCMOS18_MDIO0());
// TODO possibly not needed because uboot do not enable this register
/* enable internel VRef */
write<Gpio_b_ctrl>(Gpio_b_ctrl::Vref_enable::bits(1));
}
void set_clk(uint32_t clk, uint32_t rclk)
{
Lock_guard lock(*this);
write<Gem0_clk_ctrl>(clk);
write<Gem0_rclk_ctrl>(rclk);
static Timer::Connection timer;
timer.msleep(100);
}
};
#endif /* _INCLUDE__DRIVERS__NIC__GEM__SYSTEM_CONTROL_H_ */

View File

@ -0,0 +1,5 @@
REQUIRES = cadence_gem
TARGET = nic_drv
SRC_CC = main.cc
LIBS = base
INC_DIR += $(PRG_DIR)

View File

@ -0,0 +1,82 @@
/*
* \brief Base EMAC driver for the Xilinx EMAC PS used on Zynq devices
* \author Timo Wischer
* \date 2015-03-10
*/
/*
* Copyright (C) 2015 Genode Labs GmbH
*
* This file is part of the Genode OS framework, which is distributed
* under the terms of the GNU General Public License version 2.
*/
#ifndef _INCLUDE__DRIVERS__NIC__GEM__TX_BUFFER_DESCRIPTOR_H_
#define _INCLUDE__DRIVERS__NIC__GEM__TX_BUFFER_DESCRIPTOR_H_
#include <timer_session/connection.h>
#include "buffer_descriptor.h"
using namespace Genode;
class Tx_buffer_descriptor : public Buffer_descriptor
{
private:
enum { BUFFER_COUNT = 2 };
struct Addr : Register<0x00, 32> {};
struct Status : Register<0x04, 32> {
struct Length : Bitfield<0, 14> {};
struct Last_buffer : Bitfield<15, 1> {};
struct Wrap : Bitfield<30, 1> {};
struct Used : Bitfield<31, 1> {};
};
class Package_send_timeout : public Genode::Exception {};
public:
Tx_buffer_descriptor() : Buffer_descriptor(BUFFER_COUNT)
{
for (unsigned int i=0; i<BUFFER_COUNT; i++) {
_descriptors[i].status = Status::Used::bits(1) | Status::Last_buffer::bits(1);
}
_descriptors[BUFFER_COUNT-1].status |= Status::Wrap::bits(1);
}
void add_to_queue(const char* const packet, const size_t size)
{
if (size > MAX_PACKAGE_SIZE) {
PWRN("Ethernet package to big. Not sent!");
return;
}
/* wait until the used bit is set (timeout after 200ms) */
uint32_t timeout = 200;
while ( !Status::Used::get(_current_descriptor().status) ) {
if (timeout <= 0) {
throw Package_send_timeout();
}
timeout--;
static Timer::Connection timer;
timer.msleep(1);
}
memcpy(_current_buffer(), packet, size);
_current_descriptor().status &= Status::Length::clear_mask();
_current_descriptor().status |= Status::Length::bits(size);
/* unset the unset bit */
_current_descriptor().status &= Status::Used::clear_mask();
_increment_descriptor_index();
}
};
#endif /* _INCLUDE__DRIVERS__NIC__GEM__TX_BUFFER_DESCRIPTOR_H_ */