kendryte-freertos-sdk/lib/drivers/src/network/dm9051.cpp

612 lines
18 KiB
C++
Raw Blame History

/* Copyright 2018 Canaan Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include <FreeRTOS.h>
#include <atomic.h>
#include <semphr.h>
#include <hal.h>
#include <kernel/driver_impl.hpp>
#include <stdlib.h>
#include <string.h>
#include <printf.h>
#include <sys/unistd.h>
#include "network/dm9051.h"
#include "task.h"
using namespace sys;
/* Private typedef -----------------------------------------------------------------------------------------*/
enum DM9051_PHY_mode
{
DM9051_10MHD = 0,
DM9051_100MHD = 1,
DM9051_10MFD = 4,
DM9051_100MFD = 5,
DM9051_10M = 6,
DM9051_AUTO = 8,
DM9051_1M_HPNA = 0x10
};
enum DM9051_TYPE
{
TYPE_DM9051E,
TYPE_DM9051A,
TYPE_DM9051B,
TYPE_DM9051
};
/* Private constants ---------------------------------------------------------------------------------------*/
#define DM9051_PHY (0x40) /* PHY address 0x01 */
/* Exported typedef ----------------------------------------------------------------------------------------*/
/* Exported constants --------------------------------------------------------------------------------------*/
#define DM9051_ID (0x90510A46) /* DM9051A ID */
#define DM9051_PKT_MAX (1536) /* Received packet max size */
#define DM9051_PKT_RDY (0x01) /* Packet ready to receive */
#define DM9051_NCR (0x00)
#define DM9051_NSR (0x01)
#define DM9051_TCR (0x02)
#define DM9051_TSR1 (0x03)
#define DM9051_TSR2 (0x04)
#define DM9051_RCR (0x05)
#define DM9051_RSR (0x06)
#define DM9051_ROCR (0x07)
#define DM9051_BPTR (0x08)
#define DM9051_FCTR (0x09)
#define DM9051_FCR (0x0A)
#define DM9051_EPCR (0x0B)
#define DM9051_EPAR (0x0C)
#define DM9051_EPDRL (0x0D)
#define DM9051_EPDRH (0x0E)
#define DM9051_WCR (0x0F)
#define DM9051_PAR (0x10)
#define DM9051_MAR (0x16)
#define DM9051_GPCR (0x1e)
#define DM9051_GPR (0x1f)
#define DM9051_TRPAL (0x22)
#define DM9051_TRPAH (0x23)
#define DM9051_RWPAL (0x24)
#define DM9051_RWPAH (0x25)
#define DM9051_VIDL (0x28)
#define DM9051_VIDH (0x29)
#define DM9051_PIDL (0x2A)
#define DM9051_PIDH (0x2B)
#define DM9051_CHIPR (0x2C)
#define DM9051_TCR2 (0x2D)
#define DM9051_OTCR (0x2E)
#define DM9051_SMCR (0x2F)
#define DM9051_ETCR (0x30) /* early transmit control/status register */
#define DM9051_CSCR (0x31) /* check sum control register */
#define DM9051_RCSSR (0x32) /* receive check sum status register */
#define DM9051_PBCR (0x38)
#define DM9051_INTR (0x39)
#define DM9051_MPCR (0x55)
#define DM9051_MRCMDX (0x70)
#define DM9051_MRCMDX1 (0x71)
#define DM9051_MRCMD (0x72)
#define DM9051_MRRL (0x74)
#define DM9051_MRRH (0x75)
#define DM9051_MWCMDX (0x76)
#define DM9051_MWCMD (0x78)
#define DM9051_MWRL (0x7A)
#define DM9051_MWRH (0x7B)
#define DM9051_TXPLL (0x7C)
#define DM9051_TXPLH (0x7D)
#define DM9051_ISR (0x7E)
#define DM9051_IMR (0x7F)
#define CHIPR_DM9051A (0x19)
#define CHIPR_DM9051B (0x1B)
#define DM9051_REG_RESET (0x01)
#define DM9051_IMR_OFF (0x80)
#define DM9051_TCR2_SET (0x90) /* set one packet */
#define DM9051_RCR_SET (0x31)
#define DM9051_BPTR_SET (0x37)
#define DM9051_FCTR_SET (0x38)
#define DM9051_FCR_SET (0x28)
#define DM9051_TCR_SET (0x01)
#define NCR_EXT_PHY (1 << 7)
#define NCR_WAKEEN (1 << 6)
#define NCR_FCOL (1 << 4)
#define NCR_FDX (1 << 3)
#define NCR_LBK (3 << 1)
#define NCR_RST (1 << 0)
#define NCR_DEFAULT (0x0) /* Disable Wakeup */
#define NSR_SPEED (1 << 7)
#define NSR_LINKST (1 << 6)
#define NSR_WAKEST (1 << 5)
#define NSR_TX2END (1 << 3)
#define NSR_TX1END (1 << 2)
#define NSR_RXOV (1 << 1)
#define NSR_CLR_STATUS (NSR_WAKEST | NSR_TX2END | NSR_TX1END)
#define TCR_TJDIS (1 << 6)
#define TCR_EXCECM (1 << 5)
#define TCR_PAD_DIS2 (1 << 4)
#define TCR_CRC_DIS2 (1 << 3)
#define TCR_PAD_DIS1 (1 << 2)
#define TCR_CRC_DIS1 (1 << 1)
#define TCR_TXREQ (1 << 0) /* Start TX */
#define TCR_DEFAULT (0x0)
#define TSR_TJTO (1 << 7)
#define TSR_LC (1 << 6)
#define TSR_NC (1 << 5)
#define TSR_LCOL (1 << 4)
#define TSR_COL (1 << 3)
#define TSR_EC (1 << 2)
#define RCR_WTDIS (1 << 6)
#define RCR_DIS_LONG (1 << 5)
#define RCR_DIS_CRC (1 << 4)
#define RCR_ALL (1 << 3)
#define RCR_RUNT (1 << 2)
#define RCR_PRMSC (1 << 1)
#define RCR_RXEN (1 << 0)
#define RCR_DEFAULT (RCR_DIS_LONG | RCR_DIS_CRC)
#define RSR_RF (1 << 7)
#define RSR_MF (1 << 6)
#define RSR_LCS (1 << 5)
#define RSR_RWTO (1 << 4)
#define RSR_PLE (1 << 3)
#define RSR_AE (1 << 2)
#define RSR_CE (1 << 1)
#define RSR_FOE (1 << 0)
#define BPTR_DEFAULT (0x3f)
#define FCTR_DEAFULT (0x38)
#define FCR_DEFAULT (0xFF)
#define SMCR_DEFAULT (0x0)
#define PBCR_MAXDRIVE (0x44)
//#define FCTR_HWOT(ot) ((ot & 0xF ) << 4 )
//#define FCTR_LWOT(ot) (ot & 0xF )
#define IMR_PAR (1 << 7)
#define IMR_LNKCHGI (1 << 5)
#define IMR_UDRUN (1 << 4)
#define IMR_ROOM (1 << 3)
#define IMR_ROM (1 << 2)
#define IMR_PTM (1 << 1)
#define IMR_PRM (1 << 0)
#define IMR_FULL (IMR_PAR | IMR_LNKCHGI | IMR_UDRUN | IMR_ROOM | IMR_ROM | IMR_PTM | IMR_PRM)
#define IMR_OFF (IMR_PAR)
#define IMR_DEFAULT (IMR_PAR | IMR_PRM | IMR_PTM)
#define ISR_ROOS (1 << 3)
#define ISR_ROS (1 << 2)
#define ISR_PTS (1 << 1)
#define ISR_PRS (1 << 0)
#define ISR_CLR_STATUS (0x80 | 0x3F)
#define EPCR_REEP (1 << 5)
#define EPCR_WEP (1 << 4)
#define EPCR_EPOS (1 << 3)
#define EPCR_ERPRR (1 << 2)
#define EPCR_ERPRW (1 << 1)
#define EPCR_ERRE (1 << 0)
#define GPCR_GEP_CNTL (1 << 0)
#define SPI_WR_BURST (0xF8)
#define SPI_RD_BURST (0x72)
#define SPI_READ (0x03)
#define SPI_WRITE (0x04)
#define SPI_WRITE_BUFFER (0x05) /* Send a series of bytes from the Master to the Slave */
#define SPI_READ_BUFFER (0x06) /* Send a series of bytes from the Slave to the Master */
class dm9051_driver : public network_adapter_driver, public heap_object, public exclusive_object_access
{
public:
dm9051_driver(handle_t spi_handle, uint32_t spi_cs_mask, handle_t int_gpio_handle, uint32_t int_gpio_pin, const mac_address_t &mac_address)
: spi_driver_(system_handle_to_object(spi_handle).get_object().as<spi_driver>())
, spi_cs_mask_(spi_cs_mask)
, mac_address_(mac_address)
, int_gpio_driver_(system_handle_to_object(int_gpio_handle).get_object().as<gpio_driver>())
, int_gpio_pin_(int_gpio_pin)
{
}
virtual void install() override
{
}
virtual void on_first_open() override
{
auto spi = make_accessor(spi_driver_);
spi_dev_ = make_accessor(spi->get_device(SPI_MODE_0, SPI_FF_STANDARD, spi_cs_mask_, 8));
spi_dev_->set_clock_rate(20000000);
int_gpio_ = make_accessor(int_gpio_driver_);
int_gpio_->set_drive_mode(int_gpio_pin_, GPIO_DM_INPUT);
int_gpio_->set_pin_edge(int_gpio_pin_, GPIO_PE_FALLING);
int_gpio_->set_on_changed(int_gpio_pin_, (gpio_on_changed_t)isr_handle, this);
uint32_t value = 0;
/* Read DM9051 PID / VID, Check MCU SPI Setting correct */
value |= (uint32_t)read(DM9051_VIDL);
value |= (uint32_t)read(DM9051_VIDH) << 8;
value |= (uint32_t)read(DM9051_PIDL) << 16;
value |= (uint32_t)read(DM9051_PIDH) << 24;
configASSERT(value == 0x90510a46);
}
virtual void on_last_close() override
{
spi_dev_.reset();
}
virtual void set_handler(network_adapter_handler *handler) override
{
handler_ = handler;
}
virtual mac_address_t get_mac_address() override
{
return mac_address_;
}
virtual bool is_packet_available() override
{
uint8_t rxbyte;
/* Check packet ready or not */
rxbyte = read(DM9051_MRCMDX);
rxbyte = read(DM9051_MRCMDX);
if ((rxbyte != 1) && (rxbyte != 0))
{
/* Reset RX FIFO pointer */
write(DM9051_RCR, RCR_DEFAULT); //RX disable
write(DM9051_MPCR, 0x01); //Reset RX FIFO pointer
usleep(2e3);
write(DM9051_RCR, (RCR_DEFAULT | RCR_RXEN)); //RX Enable
return false;
}
return (rxbyte & 0b1) == 0b1;
}
virtual void reset(SemaphoreHandle_t interrupt_event) override
{
interrupt_event_ = interrupt_event;
write(DM9051_NCR, DM9051_REG_RESET);
while (read(DM9051_NCR) & DM9051_REG_RESET)
;
write(DM9051_GPCR, GPCR_GEP_CNTL);
write(DM9051_GPR, 0x00); //Power on PHY
usleep(1e5);
set_phy_mode(DM9051_AUTO);
set_mac_address(mac_address_);
/* set multicast address */
for (size_t i = 0; i < 8; i++)
{ /* Clear Multicast set */
/* Set Broadcast */
write(DM9051_MAR + i, (7 == i) ? 0x80 : 0x00);
}
/************************************************
*** Activate DM9051 and Setup DM9051 Registers **
*************************************************/
/* Clear DM9051 Set and Disable Wakeup function */
write(DM9051_NCR, NCR_DEFAULT);
/* Clear TCR Register set */
write(DM9051_TCR, TCR_DEFAULT);
/* Discard long Packet and CRC error Packet */
write(DM9051_RCR, RCR_DEFAULT);
/* Set 1.15 ms Jam Pattern Timer */
write(DM9051_BPTR, BPTR_DEFAULT);
/* Open / Close Flow Control */
//DM9051_Write_Reg(DM9051_FCTR, FCTR_DEAFULT);
write(DM9051_FCTR, 0x3A);
write(DM9051_FCR, FCR_DEFAULT);
/* Set Memory Conttrol Register<65><72>TX = 3K<33><4B>RX = 13K */
write(DM9051_SMCR, SMCR_DEFAULT);
/* Set Send one or two command Packet*/
write(DM9051_TCR2, DM9051_TCR2_SET);
//DM9051_Write_Reg(DM9051_TCR2, 0x80);
write(DM9051_INTR, 0x1);
/* Clear status */
write(DM9051_NSR, NSR_CLR_STATUS);
write(DM9051_ISR, ISR_CLR_STATUS);
write(DM9051_IMR, IMR_PAR | IMR_PRM);
write(DM9051_RCR, (RCR_DEFAULT | RCR_RXEN)); /* Enable RX */
}
virtual void begin_send(size_t length) override
{
configASSERT(length <= std::numeric_limits<uint16_t>::max());
while (read(DM9051_TCR) & DM9051_TCR_SET)
{
usleep(5000);
}
write(DM9051_TXPLL, length & 0xff);
write(DM9051_TXPLH, (length >> 8) & 0xff);
}
virtual void send(gsl::span<const uint8_t> buffer) override
{
write_memory(buffer);
}
virtual void end_send() override
{
/* Issue TX polling command */
write(DM9051_TCR, TCR_TXREQ);
}
virtual size_t begin_receive() override
{
uint16_t len = 0;
uint16_t status;
read(DM9051_MRCMDX); // dummy read
uint8_t header[4];
read_memory({ header });
status = header[0] | (header[1] << 8);
len = header[2] | (header[3] << 8);
if (len > DM9051_PKT_MAX)
{ // read-error
len = 0; // read-error (keep no change to rx fifo)
}
if ((status & 0xbf00) || (len < 0x40) || (len > DM9051_PKT_MAX))
{
if (status & 0x8000)
{
printf("rx length error \r\n");
}
if (len > DM9051_PKT_MAX)
{
printf("rx length too big \r\n");
}
}
return len;
}
virtual void receive(gsl::span<uint8_t> buffer) override
{
read_memory(buffer);
}
virtual void end_receive() override
{
}
virtual void disable_rx() override
{
uint8_t rxchk;
/* Disable DM9051a interrupt */
write(DM9051_IMR, IMR_PAR);
/* Must rx packet available */
rxchk = read(DM9051_ISR);
if (!(rxchk & ISR_PRS))
{
/* restore receive interrupt */
//.DM9051_device.imr_all |= IMR_PRM;
//.DM9051_Write_Reg(DM9051_IMR, DM9051_device.imr_all);
//.return false;
}
/* clear the rx interrupt-event */
write(DM9051_ISR, rxchk);
}
virtual void enable_rx() override
{
/* restore receive interrupt */
write(DM9051_IMR, IMR_PAR | IMR_PRM);
}
virtual bool interface_check() override
{
uint8_t link_status = 0;
link_status = read(DM9051_NSR);
link_status = read(DM9051_NSR);
if ((link_status)&0x40)
{
return true;
}
else
{
return false;
}
}
private:
static void isr_handle(uint32_t pin, void *userdata)
{
auto &driver = *reinterpret_cast<dm9051_driver *>(userdata);
BaseType_t xHigherPriorityTaskWoken = pdFALSE;
xSemaphoreGiveFromISR(driver.interrupt_event_, &xHigherPriorityTaskWoken);
if (xHigherPriorityTaskWoken)
{
portYIELD_FROM_ISR();
}
}
uint8_t read(uint8_t addr)
{
const uint8_t to_write[1] = { addr };
uint8_t to_read[1] = { 0 };
spi_dev_->transfer_sequential({ to_write }, { to_read });
return to_read[0];
}
void write(uint8_t addr, uint8_t data)
{
const uint8_t to_write[] = { static_cast<uint8_t>(addr | 0x80), data };
spi_dev_->write({ to_write });
}
void write_phy(uint8_t addr, uint16_t data)
{
/* Fill the phyxcer register into REG_0C */
write(DM9051_EPAR, DM9051_PHY | addr);
/* Fill the written data into REG_0D & REG_0E */
write(DM9051_EPDRL, (data & 0xff));
write(DM9051_EPDRH, ((data >> 8) & 0xff));
/* Issue phyxcer write command */
write(DM9051_EPCR, 0xa);
/* Wait write complete */
//_DM9051_Delay_ms(500);
while (read(DM9051_EPCR) & 0x1)
{
usleep(1000);
}; //Wait complete
/* Clear phyxcer write command */
write(DM9051_EPCR, 0x0);
}
uint16_t read_phy(uint8_t addr)
{
/* Fill the phyxcer register into REG_0C */
write(DM9051_EPAR, DM9051_PHY | addr);
/* Issue phyxcer read command */
write(DM9051_EPCR, 0xc);
/* Wait read complete */
//_DM9051_Delay_ms(100);
while (read(DM9051_EPCR) & 0x1)
{
usleep(1000);
}; //Wait complete
/* Clear phyxcer read command */
write(DM9051_EPCR, 0x0);
return (read(DM9051_EPDRH) << 8) | read(DM9051_EPDRL);
}
void read_memory(gsl::span<uint8_t> buffer)
{
const uint8_t to_write[1] = { SPI_RD_BURST };
spi_dev_->transfer_sequential({ to_write }, buffer);
}
void write_memory(gsl::span<const uint8_t> buffer)
{
auto new_buffer = std::make_unique<uint8_t[]>(buffer.size_bytes() + 1);
std::copy(buffer.begin(), buffer.end(), new_buffer.get() + 1);
new_buffer[0] = SPI_WR_BURST;
spi_dev_->write({ new_buffer.get(), buffer.size_bytes() + 1 });
}
void set_mac_address(const mac_address_t &mac_addr)
{
write(DM9051_PAR + 0, mac_addr.data[0]);
write(DM9051_PAR + 1, mac_addr.data[1]);
write(DM9051_PAR + 2, mac_addr.data[2]);
write(DM9051_PAR + 3, mac_addr.data[3]);
write(DM9051_PAR + 4, mac_addr.data[4]);
write(DM9051_PAR + 5, mac_addr.data[5]);
configASSERT(read(DM9051_PAR) == mac_addr.data[0]);
}
void set_phy_mode(uint32_t media_mode)
{
uint16_t phy_reg4 = 0x01e1, phy_reg0 = 0x1000;
if (!(media_mode & DM9051_AUTO))
{
switch (media_mode)
{
case DM9051_10MHD:
phy_reg4 = 0x21;
phy_reg0 = 0x0000;
break;
case DM9051_10MFD:
phy_reg4 = 0x41;
phy_reg0 = 0x1100;
break;
case DM9051_100MHD:
phy_reg4 = 0x81;
phy_reg0 = 0x2000;
break;
case DM9051_100MFD:
phy_reg4 = 0x101;
phy_reg0 = 0x3100;
break;
case DM9051_10M:
phy_reg4 = 0x61;
phy_reg0 = 0x1200;
break;
}
/* Set PHY media mode */
write_phy(4, phy_reg4);
/* Write rphy_reg0 to Tmp */
write_phy(0, phy_reg0);
usleep(10000);
}
}
private:
object_ptr<spi_driver> spi_driver_;
uint32_t spi_cs_mask_;
mac_address_t mac_address_;
network_adapter_handler *handler_;
object_ptr<gpio_driver> int_gpio_driver_;
uint32_t int_gpio_pin_;
object_accessor<gpio_driver> int_gpio_;
object_accessor<spi_device_driver> spi_dev_;
SemaphoreHandle_t interrupt_event_;
};
handle_t dm9051_driver_install(handle_t spi_handle, uint32_t spi_cs_mask, handle_t int_gpio_handle, uint32_t int_gpio_pin, const mac_address_t *mac_address)
{
try
{
configASSERT(mac_address);
auto driver = make_object<dm9051_driver>(spi_handle, spi_cs_mask, int_gpio_handle, int_gpio_pin, * mac_address);
driver->install();
return system_alloc_handle(make_accessor(driver));
}
catch (...)
{
return NULL_HANDLE;
}
}