pi1541/uspi/lib/smsc951x.c

475 lines
13 KiB
C
Raw Permalink Normal View History

2018-05-20 04:53:34 +00:00
//
// smsc951x.c
//
// Information to implement this driver was taken
// from the Linux smsc95xx driver which is:
// Copyright (C) 2007-2008 SMSC
// and the Embedded Xinu SMSC LAN9512 USB driver which is:
// Copyright (c) 2008, Douglas Comer and Dennis Brylow
// See the file lib/README for details!
//
// USPi - An USB driver for Raspberry Pi written in C
// Copyright (C) 2014-2016 R. Stange <rsta2@o2online.de>
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.
//
#include <uspi/smsc951x.h>
#include <uspios.h>
#include <uspi/usbhostcontroller.h>
#include <uspi/devicenameservice.h>
#include <uspi/util.h>
#include <uspi/assert.h>
// USB vendor requests
#define WRITE_REGISTER 0xA0
#define READ_REGISTER 0xA1
// Registers
#define ID_REV 0x00
#define INT_STS 0x08
#define RX_CFG 0x0C
#define TX_CFG 0x10
#define TX_CFG_ON 0x00000004
#define HW_CFG 0x14
#define HW_CFG_BIR 0x00001000
#define RX_FIFO_INF 0x18
#define PM_CTRL 0x20
#define LED_GPIO_CFG 0x24
#define LED_GPIO_CFG_SPD_LED 0x01000000
#define LED_GPIO_CFG_LNK_LED 0x00100000
#define LED_GPIO_CFG_FDX_LED 0x00010000
#define GPIO_CFG 0x28
#define AFC_CFG 0x2C
#define E2P_CMD 0x30
#define E2P_DATA 0x34
#define BURST_CAP 0x38
#define GPIO_WAKE 0x64
#define INT_EP_CTL 0x68
#define BULK_IN_DLY 0x6C
#define MAC_CR 0x100
#define MAC_CR_RCVOWN 0x00800000
#define MAC_CR_MCPAS 0x00080000
#define MAC_CR_PRMS 0x00040000
#define MAC_CR_BCAST 0x00000800
#define MAC_CR_TXEN 0x00000008
#define MAC_CR_RXEN 0x00000004
#define ADDRH 0x104
#define ADDRL 0x108
#define HASHH 0x10C
#define HASHL 0x110
#define MII_ADDR 0x114
#define MII_BUSY 0x01
#define MII_WRITE 0x02
#define PHY_ID_MASK 0x1F
#define PHY_ID_INTERNAL 0x01
#define REG_NUM_MASK 0x1F
#define MII_DATA 0x118
#define FLOW 0x11C
#define VLAN1 0x120
#define VLAN2 0x124
#define WUFF 0x128
#define WUCSR 0x12C
#define COE_CR 0x130
// TX commands (first two 32-bit words in buffer)
#define TX_CMD_A_DATA_OFFSET 0x001F0000
#define TX_CMD_A_FIRST_SEG 0x00002000
#define TX_CMD_A_LAST_SEG 0x00001000
#define TX_CMD_A_BUF_SIZE 0x000007FF
#define TX_CMD_B_CSUM_ENABLE 0x00004000
#define TX_CMD_B_ADD_CRC_DISABLE 0x00002000
#define TX_CMD_B_DISABLE_PADDING 0x00001000
#define TX_CMD_B_PKT_BYTE_LENGTH 0x000007FF
// RX status (first 32-bit word in buffer)
#define RX_STS_FF 0x40000000 // Filter Fail
#define RX_STS_FL 0x3FFF0000 // Frame Length
#define RX_STS_FRAMELEN(reg) (((reg) & RX_STS_FL) >> 16)
#define RX_STS_ES 0x00008000 // Error Summary
#define RX_STS_BF 0x00002000 // Broadcast Frame
#define RX_STS_LE 0x00001000 // Length Error
#define RX_STS_RF 0x00000800 // Runt Frame
#define RX_STS_MF 0x00000400 // Multicast Frame
#define RX_STS_TL 0x00000080 // Frame too long
#define RX_STS_CS 0x00000040 // Collision Seen
#define RX_STS_FT 0x00000020 // Frame Type
#define RX_STS_RW 0x00000010 // Receive Watchdog
#define RX_STS_ME 0x00000008 // Mii Error
#define RX_STS_DB 0x00000004 // Dribbling
#define RX_STS_CRC 0x00000002 // CRC Error
#define RX_STS_ERROR ( RX_STS_FF \
| RX_STS_ES \
| RX_STS_LE \
| RX_STS_TL \
| RX_STS_CS \
| RX_STS_RW \
| RX_STS_ME \
| RX_STS_DB \
| RX_STS_CRC)
static const char FromSMSC951x[] = "smsc951x";
static unsigned s_nDeviceNumber = 0;
boolean SMSC951xDeviceWriteReg (TSMSC951xDevice *pThis, u32 nIndex, u32 nValue);
boolean SMSC951xDeviceReadReg (TSMSC951xDevice *pThis, u32 nIndex, u32 *pValue);
#ifndef NDEBUG
void SMSC951xDeviceDumpReg (TSMSC951xDevice *pThis, const char *pName, u32 nIndex);
void SMSC951xDeviceDumpRegs (TSMSC951xDevice *pThis);
#endif
void SMSC951xDevice (TSMSC951xDevice *pThis, TUSBDevice *pDevice)
{
assert (pThis != 0);
USBDeviceCopy (&pThis->m_USBDevice, pDevice);
pThis->m_USBDevice.Configure = SMSC951xDeviceConfigure;
pThis->m_pEndpointBulkIn = 0;
pThis->m_pEndpointBulkOut = 0;
pThis->m_pTxBuffer = 0;
pThis->m_pTxBuffer = malloc (FRAME_BUFFER_SIZE);
assert (pThis->m_pTxBuffer != 0);
}
void _SMSC951xDevice (TSMSC951xDevice *pThis)
{
assert (pThis != 0);
if (pThis->m_pTxBuffer != 0)
{
free (pThis->m_pTxBuffer);
pThis->m_pTxBuffer = 0;
}
if (pThis->m_pEndpointBulkOut != 0)
{
_USBEndpoint (pThis->m_pEndpointBulkOut);
free (pThis->m_pEndpointBulkOut);
pThis->m_pEndpointBulkOut = 0;
}
if (pThis->m_pEndpointBulkIn != 0)
{
_USBEndpoint (pThis->m_pEndpointBulkIn);
free (pThis->m_pEndpointBulkIn);
pThis->m_pEndpointBulkIn = 0;
}
_USBDevice (&pThis->m_USBDevice);
}
boolean SMSC951xDeviceConfigure (TUSBDevice *pUSBDevice)
{
TSMSC951xDevice *pThis = (TSMSC951xDevice *) pUSBDevice;
assert (pThis != 0);
u8 MACAddress[MAC_ADDRESS_SIZE];
if (GetMACAddress (MACAddress))
{
MACAddressSet (&pThis->m_MACAddress, MACAddress);
}
else
{
LogWrite (FromSMSC951x, LOG_ERROR, "Cannot get MAC address");
return FALSE;
}
TString MACString;
String (&MACString);
MACAddressFormat (&pThis->m_MACAddress, &MACString);
LogWrite (FromSMSC951x, LOG_DEBUG, "MAC address is %s", StringGet (&MACString));
const TUSBConfigurationDescriptor *pConfigDesc =
(TUSBConfigurationDescriptor *) USBDeviceGetDescriptor (&pThis->m_USBDevice, DESCRIPTOR_CONFIGURATION);
if ( pConfigDesc == 0
|| pConfigDesc->bNumInterfaces != 1)
{
USBDeviceConfigurationError (&pThis->m_USBDevice, FromSMSC951x);
_String (&MACString);
return FALSE;
}
const TUSBInterfaceDescriptor *pInterfaceDesc =
(TUSBInterfaceDescriptor *) USBDeviceGetDescriptor (&pThis->m_USBDevice, DESCRIPTOR_INTERFACE);
if ( pInterfaceDesc == 0
|| pInterfaceDesc->bInterfaceNumber != 0x00
|| pInterfaceDesc->bAlternateSetting != 0x00
|| pInterfaceDesc->bNumEndpoints != 3)
{
USBDeviceConfigurationError (&pThis->m_USBDevice, FromSMSC951x);
_String (&MACString);
return FALSE;
}
const TUSBEndpointDescriptor *pEndpointDesc;
while ((pEndpointDesc = (TUSBEndpointDescriptor *) USBDeviceGetDescriptor (&pThis->m_USBDevice, DESCRIPTOR_ENDPOINT)) != 0)
{
if ((pEndpointDesc->bmAttributes & 0x3F) == 0x02) // Bulk
{
if ((pEndpointDesc->bEndpointAddress & 0x80) == 0x80) // Input
{
if (pThis->m_pEndpointBulkIn != 0)
{
USBDeviceConfigurationError (&pThis->m_USBDevice, FromSMSC951x);
_String (&MACString);
return FALSE;
}
pThis->m_pEndpointBulkIn = (TUSBEndpoint *) malloc (sizeof (TUSBEndpoint));
assert (pThis->m_pEndpointBulkIn);
USBEndpoint2 (pThis->m_pEndpointBulkIn, &pThis->m_USBDevice, pEndpointDesc);
}
else // Output
{
if (pThis->m_pEndpointBulkOut != 0)
{
USBDeviceConfigurationError (&pThis->m_USBDevice, FromSMSC951x);
_String (&MACString);
return FALSE;
}
pThis->m_pEndpointBulkOut = (TUSBEndpoint *) malloc (sizeof (TUSBEndpoint));
assert (pThis->m_pEndpointBulkOut);
USBEndpoint2 (pThis->m_pEndpointBulkOut, &pThis->m_USBDevice, pEndpointDesc);
}
}
}
if ( pThis->m_pEndpointBulkIn == 0
|| pThis->m_pEndpointBulkOut == 0)
{
USBDeviceConfigurationError (&pThis->m_USBDevice, FromSMSC951x);
_String (&MACString);
return FALSE;
}
if (!USBDeviceConfigure (&pThis->m_USBDevice))
{
LogWrite (FromSMSC951x, LOG_ERROR, "Cannot set configuration");
_String (&MACString);
return FALSE;
}
u8 MACAddressBuffer[MAC_ADDRESS_SIZE];
MACAddressCopyTo (&pThis->m_MACAddress, MACAddressBuffer);
u16 usMACAddressHigh = (u16) MACAddressBuffer[4]
| (u16) MACAddressBuffer[5] << 8;
u32 nMACAddressLow = (u32) MACAddressBuffer[0]
| (u32) MACAddressBuffer[1] << 8
| (u32) MACAddressBuffer[2] << 16
| (u32) MACAddressBuffer[3] << 24;
if ( !SMSC951xDeviceWriteReg (pThis, ADDRH, usMACAddressHigh)
|| !SMSC951xDeviceWriteReg (pThis, ADDRL, nMACAddressLow))
{
LogWrite (FromSMSC951x, LOG_ERROR, "Cannot set MAC address");
_String (&MACString);
return FALSE;
}
if ( !SMSC951xDeviceWriteReg (pThis, LED_GPIO_CFG, LED_GPIO_CFG_SPD_LED
| LED_GPIO_CFG_LNK_LED
| LED_GPIO_CFG_FDX_LED)
|| !SMSC951xDeviceWriteReg (pThis, MAC_CR, MAC_CR_RCVOWN
//| MAC_CR_PRMS // promiscous mode
| MAC_CR_TXEN
| MAC_CR_RXEN)
|| !SMSC951xDeviceWriteReg (pThis, TX_CFG, TX_CFG_ON))
{
LogWrite (FromSMSC951x, LOG_ERROR, "Cannot start device");
_String (&MACString);
return FALSE;
}
// TODO: check if PHY is up (wait for it)
TString DeviceName;
String (&DeviceName);
StringFormat (&DeviceName, "eth%u", s_nDeviceNumber++);
DeviceNameServiceAddDevice (DeviceNameServiceGet (), StringGet (&DeviceName), pThis, FALSE);
_String (&DeviceName);
_String (&MACString);
return TRUE;
}
TMACAddress *SMSC951xDeviceGetMACAddress (TSMSC951xDevice *pThis)
{
assert (pThis != 0);
return &pThis->m_MACAddress;
}
boolean SMSC951xDeviceSendFrame (TSMSC951xDevice *pThis, const void *pBuffer, unsigned nLength)
{
assert (pThis != 0);
if (nLength >= FRAME_BUFFER_SIZE-8)
{
return FALSE;
}
assert (pThis->m_pTxBuffer != 0);
assert (pBuffer != 0);
memcpy (pThis->m_pTxBuffer+8, pBuffer, nLength);
*(u32 *) &pThis->m_pTxBuffer[0] = TX_CMD_A_FIRST_SEG | TX_CMD_A_LAST_SEG | nLength;
*(u32 *) &pThis->m_pTxBuffer[4] = nLength;
assert (pThis->m_pEndpointBulkOut != 0);
return DWHCIDeviceTransfer (USBDeviceGetHost (&pThis->m_USBDevice), pThis->m_pEndpointBulkOut, pThis->m_pTxBuffer, nLength+8) >= 0;
}
boolean SMSC951xDeviceReceiveFrame (TSMSC951xDevice *pThis, void *pBuffer, unsigned *pResultLength)
{
assert (pThis != 0);
assert (pThis->m_pEndpointBulkIn != 0);
assert (pBuffer != 0);
TUSBRequest URB;
USBRequest (&URB, pThis->m_pEndpointBulkIn, pBuffer, FRAME_BUFFER_SIZE, 0);
if (!DWHCIDeviceSubmitBlockingRequest (USBDeviceGetHost (&pThis->m_USBDevice), &URB))
{
_USBRequest (&URB);
return FALSE;
}
u32 nResultLength = USBRequestGetResultLength (&URB);
if (nResultLength < 4) // should not happen with HW_CFG_BIR set
{
_USBRequest (&URB);
return FALSE;
}
u32 nRxStatus = *(u32 *) pBuffer;
if (nRxStatus & RX_STS_ERROR)
{
LogWrite (FromSMSC951x, LOG_WARNING, "RX error (status 0x%X)", nRxStatus);
_USBRequest (&URB);
return FALSE;
}
u32 nFrameLength = RX_STS_FRAMELEN (nRxStatus);
assert (nFrameLength == nResultLength-4);
assert (nFrameLength > 4);
if (nFrameLength <= 4)
{
_USBRequest (&URB);
return FALSE;
}
nFrameLength -= 4; // ignore CRC
//LogWrite (FromSMSC951x, LOG_DEBUG, "Frame received (status 0x%X)", nRxStatus);
memcpy (pBuffer, (u8 *) pBuffer + 4, nFrameLength); // overwrite RX status
assert (pResultLength != 0);
*pResultLength = nFrameLength;
_USBRequest (&URB);
return TRUE;
}
boolean SMSC951xDeviceWriteReg (TSMSC951xDevice *pThis, u32 nIndex, u32 nValue)
{
assert (pThis != 0);
return DWHCIDeviceControlMessage (USBDeviceGetHost (&pThis->m_USBDevice),
USBDeviceGetEndpoint0 (&pThis->m_USBDevice),
REQUEST_OUT | REQUEST_VENDOR, WRITE_REGISTER,
0, nIndex, &nValue, sizeof nValue) >= 0;
}
boolean SMSC951xDeviceReadReg (TSMSC951xDevice *pThis, u32 nIndex, u32 *pValue)
{
assert (pThis != 0);
return DWHCIDeviceControlMessage (USBDeviceGetHost (&pThis->m_USBDevice),
USBDeviceGetEndpoint0 (&pThis->m_USBDevice),
REQUEST_IN | REQUEST_VENDOR, READ_REGISTER,
0, nIndex, pValue, sizeof *pValue) == (int) sizeof *pValue;
}
#ifndef NDEBUG
void SMSC951xDeviceDumpReg (TSMSC951xDevice *pThis, const char *pName, u32 nIndex)
{
assert (pThis != 0);
u32 nValue;
if (!SMSC951xDeviceReadReg (pThis, nIndex, &nValue))
{
LogWrite (FromSMSC951x, LOG_ERROR, "Cannot read register 0x%X", nIndex);
return;
}
LogWrite (FromSMSC951x, LOG_DEBUG, "%08X %s", nValue, pName);
}
void SMSC951xDeviceDumpRegs (TSMSC951xDevice *pThis)
{
assert (pThis != 0);
SMSC951xDeviceDumpReg (pThis, "ID_REV", ID_REV);
SMSC951xDeviceDumpReg (pThis, "INT_STS", INT_STS);
SMSC951xDeviceDumpReg (pThis, "RX_CFG", RX_CFG);
SMSC951xDeviceDumpReg (pThis, "TX_CFG", TX_CFG);
SMSC951xDeviceDumpReg (pThis, "HW_CFG", HW_CFG);
SMSC951xDeviceDumpReg (pThis, "RX_FIFO_INF", RX_FIFO_INF);
SMSC951xDeviceDumpReg (pThis, "PM_CTRL", PM_CTRL);
SMSC951xDeviceDumpReg (pThis, "LED_GPIO_CFG", LED_GPIO_CFG);
SMSC951xDeviceDumpReg (pThis, "GPIO_CFG", GPIO_CFG);
SMSC951xDeviceDumpReg (pThis, "AFC_CFG", AFC_CFG);
SMSC951xDeviceDumpReg (pThis, "BURST_CAP", BURST_CAP);
SMSC951xDeviceDumpReg (pThis, "INT_EP_CTL", INT_EP_CTL);
SMSC951xDeviceDumpReg (pThis, "BULK_IN_DLY", BULK_IN_DLY);
SMSC951xDeviceDumpReg (pThis, "MAC_CR", MAC_CR);
SMSC951xDeviceDumpReg (pThis, "ADDRH", ADDRH);
SMSC951xDeviceDumpReg (pThis, "ADDRL", ADDRL);
SMSC951xDeviceDumpReg (pThis, "HASHH", HASHH);
SMSC951xDeviceDumpReg (pThis, "HASHL", HASHL);
SMSC951xDeviceDumpReg (pThis, "FLOW", FLOW);
SMSC951xDeviceDumpReg (pThis, "WUCSR", WUCSR);
}
#endif