mirror of
https://github.com/genodelabs/genode.git
synced 2025-01-31 00:24:51 +00:00
parent
020758a2f1
commit
9f4b77c5c2
@ -79,7 +79,7 @@ append_if [expr [have_spec omap4] || [have_spec arndale]] config "
|
|||||||
</config>
|
</config>
|
||||||
</start>"
|
</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\">
|
<start name=\"$network_driver\">
|
||||||
<resource name=\"RAM\" quantum=\"4M\"/>
|
<resource name=\"RAM\" quantum=\"4M\"/>
|
||||||
<provides><service name=\"Nic\"/></provides>
|
<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
|
||||||
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 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
|
||||||
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
|
lappend_if [have_spec nova] boot_modules pci_device_pd
|
||||||
|
|
||||||
build_boot_image $boot_modules
|
build_boot_image $boot_modules
|
||||||
|
103
repos/os/src/drivers/nic/gem/buffer_descriptor.h
Normal file
103
repos/os/src/drivers/nic/gem/buffer_descriptor.h
Normal 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_ */
|
538
repos/os/src/drivers/nic/gem/cadence_gem.h
Normal file
538
repos/os/src/drivers/nic/gem/cadence_gem.h
Normal 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 ¬ify) :
|
||||||
|
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_ */
|
||||||
|
|
60
repos/os/src/drivers/nic/gem/main.cc
Normal file
60
repos/os/src/drivers/nic/gem/main.cc
Normal 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 ¬ify)
|
||||||
|
{
|
||||||
|
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;
|
||||||
|
}
|
579
repos/os/src/drivers/nic/gem/marvell_phy.h
Normal file
579
repos/os/src/drivers/nic/gem/marvell_phy.h
Normal 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_ */
|
||||||
|
|
31
repos/os/src/drivers/nic/gem/phyio.h
Normal file
31
repos/os/src/drivers/nic/gem/phyio.h
Normal 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_ */
|
||||||
|
|
134
repos/os/src/drivers/nic/gem/rx_buffer_descriptor.h
Normal file
134
repos/os/src/drivers/nic/gem/rx_buffer_descriptor.h
Normal 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_ */
|
151
repos/os/src/drivers/nic/gem/system_control.h
Normal file
151
repos/os/src/drivers/nic/gem/system_control.h
Normal 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_ */
|
5
repos/os/src/drivers/nic/gem/target.mk
Normal file
5
repos/os/src/drivers/nic/gem/target.mk
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
REQUIRES = cadence_gem
|
||||||
|
TARGET = nic_drv
|
||||||
|
SRC_CC = main.cc
|
||||||
|
LIBS = base
|
||||||
|
INC_DIR += $(PRG_DIR)
|
82
repos/os/src/drivers/nic/gem/tx_buffer_descriptor.h
Normal file
82
repos/os/src/drivers/nic/gem/tx_buffer_descriptor.h
Normal 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_ */
|
Loading…
x
Reference in New Issue
Block a user