diff --git a/repos/os/run/network_test_nic.inc b/repos/os/run/network_test_nic.inc index 712039563..08d42adeb 100644 --- a/repos/os/run/network_test_nic.inc +++ b/repos/os/run/network_test_nic.inc @@ -79,7 +79,7 @@ append_if [expr [have_spec omap4] || [have_spec arndale]] config " " -append_if [expr ![have_spec omap4] && ![have_spec arndale]] config " +append_if [expr ![have_spec omap4] && ![have_spec platform_arndale]] config " @@ -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 diff --git a/repos/os/src/drivers/nic/gem/buffer_descriptor.h b/repos/os/src/drivers/nic/gem/buffer_descriptor.h new file mode 100644 index 000000000..70c55c3ef --- /dev/null +++ b/repos/os/src/drivers/nic/gem/buffer_descriptor.h @@ -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 +#include + +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(local_addr()) ), + _buffer_count(buffer_count), + _buffer_offset(BUFFER_DESC_SIZE * buffer_count), + _descriptor_index(0), + _buffer(local_addr() + _buffer_offset), + _descriptors(local_addr()) + { + /* + * 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 +#include +#include +#include + +#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::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_buffer.phys_addr() ); + write( _tx_buffer.phys_addr() ); + + + /* 3. Program the DMA Configuration register (gem.dma_cfg) */ + write( Dma_config::init() ); + + + /* + * 4. Program the Network Control Register (gem.net_ctrl) + * Enable MDIO, transmitter and receiver + */ + write(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(1); + rclk = (0 << 4) | (1 << 0); + clk = (1 << 20) | (8 << 8) | (0 << 4) | (1 << 0); + break; + case SPEED_100: + write(0); + write(1); + rclk = 1 << 0; + clk = (5 << 20) | (8 << 8) | (0 << 4) | (1 << 0); + break; + case SPEED_10: + write(0); + write(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::Rx_complete::bits(1)); + } + + void _deinit() + { + /* 16.3.1 Initialize the Controller */ + + /* Disable all interrupts */ + write(0x7FFFEFF); + + /* Disable the receiver & transmitter */ + write(0); + write(Control::Clear_statistics::bits(1)); + + write(0xFF); + write(0x0F); + write(0); + + write(0); + write(0); + + /* Clear the Hash registers for the mac address + * pointed by AddressPtr + */ + write(0); + } + + + void _mdio_wait() + { + uint32_t timeout = 200; + + /* Wait till MDIO interface is ready to accept a new transaction. */ + while (!read()) { + 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::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(); + } + + + void mac_address(const Nic::Mac_address mac) + { + const uint32_t* const low_addr_pointer = reinterpret_cast(&mac.addr[0]); + const uint16_t* const high_addr_pointer = reinterpret_cast(&mac.addr[4]); + + write(*low_addr_pointer); + write(*high_addr_pointer); + } + + /*************************** + ** Nic::Driver interface ** + ***************************/ + + virtual Nic::Mac_address mac_address() + { + Nic::Mac_address mac; + uint32_t* const low_addr_pointer = reinterpret_cast(&mac.addr[0]); + uint16_t* const high_addr_pointer = reinterpret_cast(&mac.addr[4]); + + *low_addr_pointer = read(); + *high_addr_pointer = read(); + + 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::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(); + const Rx_status::access_t rxStatus = read(); + + 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( _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(1); + write(1); + + /* comit buffer to system services */ + _rx_buffer_alloc.submit(); + } + + /* check, if there was lost some packages */ + const uint16_t lost_packages = read(); + if (lost_packages > 0) { + PWRN("%d packages lost (%d packages successfully received)!", + lost_packages, read()); + } + + /* reset reveive complete interrupt */ + write(Rx_status::Frame_reveived::bits(1)); + write(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_ */ + diff --git a/repos/os/src/drivers/nic/gem/main.cc b/repos/os/src/drivers/nic/gem/main.cc new file mode 100644 index 000000000..ff641f2bf --- /dev/null +++ b/repos/os/src/drivers/nic/gem/main.cc @@ -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 +#include +#include +#include + +#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(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; +} diff --git a/repos/os/src/drivers/nic/gem/marvell_phy.h b/repos/os/src/drivers/nic/gem/marvell_phy.h new file mode 100644 index 000000000..60ee49e35 --- /dev/null +++ b/repos/os/src/drivers/nic/gem/marvell_phy.h @@ -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 +#include +#include +#include + +#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 + 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 + 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 + 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 + 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 + 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(); + 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(); + 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() << 16; + + /* Grab the bits from PHYIR2, and put them in the lower half */ + phy_id |= phy_read(); + + return phy_id; + } + + void m88e1310_config() + { + /* LED link and activity */ + phy_write(0x0003); + Led_ctrl::access_t led = phy_read(); + Led_ctrl::Data::set(led, 0x1); + phy_write(led); + + /* Set LED2/INT to INT mode, low active */ + phy_write(0x0003); + Irq_en::access_t irq = phy_read(); + irq = (irq & 0x77ff) | 0x0880; + phy_write(irq); + + /* Set RGMII delay */ + phy_write(0x0002); + RGMII_ctrl::access_t ctrl = phy_read(); + ctrl |= 0x0030; + phy_write(ctrl); + + /* Ensure to return to page 0 */ + phy_write(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(); + + 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::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(adv); + changed = 1; + } + + /* Configure gigabit if it's supported */ + adv = phy_read(); + + oldadv = adv; + + Ctrl1000::Full_1000::set(adv, 1); + Ctrl1000::Half_1000::set(adv, 1); + + if (adv != oldadv) { + phy_write(adv); + changed = 1; + } + + return changed; + } + + int genphy_restart_aneg() + { + Bmcr::access_t ctl = phy_read(); + 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(ctl); + + return 0; + } + + int phy_reset() + { + int timeout = 500; + + Bmcr::access_t reg = phy_read(); + Bmcr::Reset::set(reg, 1); + phy_write(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() && timeout--) { + _timer.msleep(1); + } + + if (phy_read()) { + 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(); + + /* + * 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(); + } + Genode::printf(" done\n"); + _link_up = true; + } else { + /* Read the link a second time to clear the latched state */ + mii_reg = phy_read(); + + 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(); + + if ( Phy_stat::Link::get(stat) && + !Phy_stat::Spddone::get(stat)) { + int i = 0; + + PDBG("Waiting for PHY realtime link"); + while (!phy_read()) { + /* 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_ */ + diff --git a/repos/os/src/drivers/nic/gem/phyio.h b/repos/os/src/drivers/nic/gem/phyio.h new file mode 100644 index 000000000..22d39b797 --- /dev/null +++ b/repos/os/src/drivers/nic/gem/phyio.h @@ -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_ */ + diff --git a/repos/os/src/drivers/nic/gem/rx_buffer_descriptor.h b/repos/os/src/drivers/nic/gem/rx_buffer_descriptor.h new file mode 100644 index 000000000..4758b2d5c --- /dev/null +++ b/repos/os/src/drivers/nic/gem/rx_buffer_descriptor.h @@ -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 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(); + for (unsigned i=0; i %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_ */ diff --git a/repos/os/src/drivers/nic/gem/system_control.h b/repos/os/src/drivers/nic/gem/system_control.h new file mode 100644 index 000000000..001bdc225 --- /dev/null +++ b/repos/os/src/drivers/nic/gem/system_control.h @@ -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 +#include + +#include + + +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::MAGIC); } + void _lock() { _sys_ctrl.write(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::FAST_LVCMOS18_ETH0()); + write(Mio_pin_16::FAST_LVCMOS18_ETH0()); + write(Mio_pin_16::FAST_LVCMOS18_ETH0()); + write(Mio_pin_16::FAST_LVCMOS18_ETH0()); + write(Mio_pin_16::FAST_LVCMOS18_ETH0()); + write(Mio_pin_16::FAST_LVCMOS18_ETH0()); + + write(Mio_pin_16::FAST_LVCMOS18_ETH0_TRISTATE()); + write(Mio_pin_16::FAST_LVCMOS18_ETH0_TRISTATE()); + write(Mio_pin_16::FAST_LVCMOS18_ETH0_TRISTATE()); + write(Mio_pin_16::FAST_LVCMOS18_ETH0_TRISTATE()); + write(Mio_pin_16::FAST_LVCMOS18_ETH0_TRISTATE()); + write(Mio_pin_16::FAST_LVCMOS18_ETH0_TRISTATE()); + + write(Mio_pin_52::LVCMOS18_MDIO0()); + write(Mio_pin_52::LVCMOS18_MDIO0()); + + // TODO possibly not needed because uboot do not enable this register + /* enable internel VRef */ + write(Gpio_b_ctrl::Vref_enable::bits(1)); + } + + void set_clk(uint32_t clk, uint32_t rclk) + { + Lock_guard lock(*this); + + write(clk); + write(rclk); + + static Timer::Connection timer; + timer.msleep(100); + } +}; + +#endif /* _INCLUDE__DRIVERS__NIC__GEM__SYSTEM_CONTROL_H_ */ diff --git a/repos/os/src/drivers/nic/gem/target.mk b/repos/os/src/drivers/nic/gem/target.mk new file mode 100644 index 000000000..0c277f4f5 --- /dev/null +++ b/repos/os/src/drivers/nic/gem/target.mk @@ -0,0 +1,5 @@ +REQUIRES = cadence_gem +TARGET = nic_drv +SRC_CC = main.cc +LIBS = base +INC_DIR += $(PRG_DIR) diff --git a/repos/os/src/drivers/nic/gem/tx_buffer_descriptor.h b/repos/os/src/drivers/nic/gem/tx_buffer_descriptor.h new file mode 100644 index 000000000..e0053e983 --- /dev/null +++ b/repos/os/src/drivers/nic/gem/tx_buffer_descriptor.h @@ -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 + +#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 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_ */