staticint oa_tc6_get_parity(u32 p)
{ /* Public domain code snippet, lifted from *http://www-graphics.stanford.edu/~seander/bithacks.html
*/
p ^= p >> 1;
p ^= p >> 2;
p = (p & 0x11111111U) * 0x11111111U;
/* Odd parity is used here */ return !((p >> 28) & 1);
}
/* The echoed control write must match with the one that was *transmitted.
*/ if (memcmp(tx_buf, rx_buf, size - OA_TC6_CTRL_IGNORED_SIZE)) return -EPROTO;
/* Prepare control command and copy to SPI control buffer */
oa_tc6_prepare_ctrl_spi_buf(tc6, address, value, length, reg_op);
size = oa_tc6_calculate_ctrl_buf_size(length);
/* Perform SPI transfer */
ret = oa_tc6_spi_transfer(tc6, OA_TC6_CTRL_HEADER, size); if (ret) {
dev_err(&tc6->spi->dev, "SPI transfer failed for control: %d\n",
ret); return ret;
}
/* Check echoed/received control write command reply for errors */ if (reg_op == OA_TC6_CTRL_REG_WRITE) return oa_tc6_check_ctrl_write_reply(tc6, size);
/* Check echoed/received control read command reply for errors */
ret = oa_tc6_check_ctrl_read_reply(tc6, size); if (ret) return ret;
staticint oa_tc6_phy_init(struct oa_tc6 *tc6)
{ int ret;
ret = oa_tc6_check_phy_reg_direct_access_capability(tc6); if (ret) {
netdev_err(tc6->netdev, "Direct PHY register access is not supported by the MAC-PHY\n"); return ret;
}
ret = oa_tc6_mdiobus_register(tc6); if (ret) return ret;
tc6->phydev = phy_find_first(tc6->mdiobus); if (!tc6->phydev) {
netdev_err(tc6->netdev, "No PHY found\n");
oa_tc6_mdiobus_unregister(tc6); return -ENODEV;
}
tc6->phydev->is_internal = true;
ret = phy_connect_direct(tc6->netdev, tc6->phydev,
&oa_tc6_handle_link_change,
PHY_INTERFACE_MODE_INTERNAL); if (ret) {
netdev_err(tc6->netdev, "Can't attach PHY to %s\n",
tc6->mdiobus->id);
oa_tc6_mdiobus_unregister(tc6); return ret;
}
ret = oa_tc6_write_register(tc6, OA_TC6_REG_RESET, regval); if (ret) return ret;
/* Poll for soft reset complete for every 1ms until 1s timeout */
ret = readx_poll_timeout(oa_tc6_read_status0, tc6, regval,
regval & STATUS0_RESETC,
STATUS0_RESETC_POLL_DELAY,
STATUS0_RESETC_POLL_TIMEOUT); if (ret) return -ENODEV;
/* Clear the reset complete status */ return oa_tc6_write_register(tc6, OA_TC6_REG_STATUS0, regval);
}
staticint oa_tc6_unmask_macphy_error_interrupts(struct oa_tc6 *tc6)
{
u32 regval; int ret;
ret = oa_tc6_read_register(tc6, OA_TC6_REG_INT_MASK0, ®val); if (ret) return ret;
staticint oa_tc6_process_extended_status(struct oa_tc6 *tc6)
{
u32 value; int ret;
ret = oa_tc6_read_register(tc6, OA_TC6_REG_STATUS0, &value); if (ret) {
netdev_err(tc6->netdev, "STATUS0 register read failed: %d\n",
ret); return ret;
}
/* Clear the error interrupts status */
ret = oa_tc6_write_register(tc6, OA_TC6_REG_STATUS0, value); if (ret) {
netdev_err(tc6->netdev, "STATUS0 register write failed: %d\n",
ret); return ret;
}
if (FIELD_GET(STATUS0_RX_BUFFER_OVERFLOW_ERROR, value)) {
tc6->rx_buf_overflow = true;
oa_tc6_cleanup_ongoing_rx_skb(tc6);
net_err_ratelimited("%s: Receive buffer overflow error\n",
tc6->netdev->name); return -EAGAIN;
} if (FIELD_GET(STATUS0_TX_PROTOCOL_ERROR, value)) {
netdev_err(tc6->netdev, "Transmit protocol error\n"); return -ENODEV;
} /* TODO: Currently loss of frame and header errors are treated as *non-recoverableerrors.Theywillbehandledinthenextversion.
*/ if (FIELD_GET(STATUS0_LOSS_OF_FRAME_ERROR, value)) {
netdev_err(tc6->netdev, "Loss of frame error\n"); return -ENODEV;
} if (FIELD_GET(STATUS0_HEADER_ERROR, value)) {
netdev_err(tc6->netdev, "Header error\n"); return -ENODEV;
}
return0;
}
staticint oa_tc6_process_rx_chunk_footer(struct oa_tc6 *tc6, u32 footer)
{ /* Process rx chunk footer for the following, *1.txcredits *2.errorsifanyfromMAC-PHY *3.receivechunksavailable
*/
tc6->tx_credits = FIELD_GET(OA_TC6_DATA_FOOTER_TX_CREDITS, footer);
tc6->rx_chunks_available = FIELD_GET(OA_TC6_DATA_FOOTER_RX_CHUNKS,
footer);
if (FIELD_GET(OA_TC6_DATA_FOOTER_EXTENDED_STS, footer)) { int ret = oa_tc6_process_extended_status(tc6);
if (ret) return ret;
}
/* TODO: Currently received header bad and configuration unsync errors *aretreatedasnon-recoverableerrors.Theywillbehandledinthe *nextversion.
*/ if (FIELD_GET(OA_TC6_DATA_FOOTER_RXD_HEADER_BAD, footer)) {
netdev_err(tc6->netdev, "Rxd header bad error\n"); return -ENODEV;
}
/* Restart the new rx frame after receiving rx buffer overflow error */ if (start_valid && tc6->rx_buf_overflow)
tc6->rx_buf_overflow = false;
if (tc6->rx_buf_overflow) return0;
/* Process the chunk with complete rx frame */ if (start_valid && end_valid && start_byte_offset < end_byte_offset) {
size = end_byte_offset + 1 - start_byte_offset; return oa_tc6_prcs_complete_rx_frame(tc6,
&data[start_byte_offset],
size);
}
/* Process the chunk with only rx frame start */ if (start_valid && !end_valid) {
size = OA_TC6_CHUNK_PAYLOAD_SIZE - start_byte_offset; return oa_tc6_prcs_rx_frame_start(tc6,
&data[start_byte_offset],
size);
}
/* Process the chunk with only rx frame end */ if (end_valid && !start_valid) {
size = end_byte_offset + 1;
oa_tc6_prcs_rx_frame_end(tc6, data, size); return0;
}
/* Process the chunk with previous rx frame end and next rx frame *start.
*/ if (start_valid && end_valid && start_byte_offset > end_byte_offset) { /* After rx buffer overflow error received, there might be a *possibilityofgettinganendvalidofapreviously *incompleterxframealongwiththenewrxframestartvalid.
*/ if (tc6->rx_skb) {
size = end_byte_offset + 1;
oa_tc6_prcs_rx_frame_end(tc6, data, size);
}
size = OA_TC6_CHUNK_PAYLOAD_SIZE - start_byte_offset; return oa_tc6_prcs_rx_frame_start(tc6,
&data[start_byte_offset],
size);
}
/* Process the chunk with ongoing rx frame data */
oa_tc6_prcs_ongoing_rx_frame(tc6, data, footer);
/* All the rx chunks in the receive SPI data buffer are examined here */ for (int i = 0; i < no_of_rx_chunks; i++) { /* Last 4 bytes in each received chunk consist footer info */
footer = oa_tc6_get_rx_chunk_footer(tc6, i * OA_TC6_CHUNK_SIZE +
OA_TC6_CHUNK_PAYLOAD_SIZE);
ret = oa_tc6_process_rx_chunk_footer(tc6, footer); if (ret) return ret;
/* If there is a data valid chunks then process it for the *informationneededtodeterminethevalidityandthelocation *ofthereceiveframedata.
*/ if (FIELD_GET(OA_TC6_DATA_FOOTER_DATA_VALID, footer)) {
u8 *payload = tc6->spi_data_rx_buf + i *
OA_TC6_CHUNK_SIZE;
ret = oa_tc6_prcs_rx_chunk_payload(tc6, payload,
footer); if (ret) return ret;
}
}
/* Initial value is assigned here to avoid more than 80 characters in *thedeclarationplace.
*/
start_valid = OA_TC6_DATA_START_INVALID;
/* Set start valid if the current tx chunk contains the start of the tx *ethernetframe.
*/ if (!tc6->tx_skb_offset)
start_valid = OA_TC6_DATA_START_VALID;
/* If the remaining tx skb length is more than the chunk payload size of *64bytesthencopyonly64bytesandleavetheongoingtxskbfor *nexttxchunk.
*/
length_to_copy = min_t(u16, remaining_len, OA_TC6_CHUNK_PAYLOAD_SIZE);
/* Copy the tx skb data to the tx chunk payload buffer */
memcpy(tx_buf + 1, tx_skb_data, length_to_copy);
tc6->tx_skb_offset += length_to_copy;
/* Set end valid if the current tx chunk contains the end of the tx *ethernetframe.
*/ if (tc6->ongoing_tx_skb->len == tc6->tx_skb_offset) {
end_valid = OA_TC6_DATA_END_VALID;
end_byte_offset = length_to_copy - 1;
tc6->tx_skb_offset = 0;
tc6->netdev->stats.tx_bytes += tc6->ongoing_tx_skb->len;
tc6->netdev->stats.tx_packets++;
kfree_skb(tc6->ongoing_tx_skb);
tc6->ongoing_tx_skb = NULL;
}
/* Get tx skbs and convert them into tx chunks based on the tx credits *available.
*/ for (used_tx_credits = 0; used_tx_credits < tc6->tx_credits;
used_tx_credits++) { if (!tc6->ongoing_tx_skb) {
spin_lock_bh(&tc6->tx_skb_lock);
tc6->ongoing_tx_skb = tc6->waiting_tx_skb;
tc6->waiting_tx_skb = NULL;
spin_unlock_bh(&tc6->tx_skb_lock);
} if (!tc6->ongoing_tx_skb) break;
oa_tc6_add_tx_skb_to_spi_buf(tc6);
}
/* If there are more chunks to receive than to transmit, we need to add *enoughemptytxchunkstoallowthereceptionoftheexcessrx *chunks.
*/ if (tx_chunks >= tc6->rx_chunks_available) return len;
while (likely(!kthread_should_stop())) { /* This kthread will be waken up if there is a tx skb or mac-phy *interrupttoperformspitransferwithtxchunks.
*/
wait_event_interruptible(tc6->spi_wq, tc6->int_flag ||
(tc6->waiting_tx_skb &&
tc6->tx_credits) ||
kthread_should_stop());
if (kthread_should_stop()) break;
ret = oa_tc6_try_spi_transfer(tc6); if (ret) return ret;
}
return0;
}
staticint oa_tc6_update_buffer_status_from_register(struct oa_tc6 *tc6)
{
u32 value; int ret;
/* Initially tx credits and rx chunks available to be updated from the *registerasthereisnodatatransferperformedyet.Latertheywill *beupdatedfromtherxfooter.
*/
ret = oa_tc6_read_register(tc6, OA_TC6_REG_BUFFER_STATUS, &value); if (ret) return ret;
/* MAC-PHY interrupt can occur for the following reasons. *-availabilityoftxcreditsifitwas0beforeandnotreportedin *thepreviousrxfooter. *-availabilityofrxchunksifitwas0beforeandnotreportedin *thepreviousrxfooter. *-extendedstatuseventnotreportedinthepreviousrxfooter.
*/
tc6->int_flag = true; /* Wake spi kthread to perform spi transfer */
wake_up_interruptible(&tc6->spi_wq);
return IRQ_HANDLED;
}
/** *oa_tc6_zero_align_receive_frame_enable-functiontoenablezeroalign *receiveframefeature. *@tc6:oa_tc6struct. * *Return:0onsuccessotherwisefailed.
*/ int oa_tc6_zero_align_receive_frame_enable(struct oa_tc6 *tc6)
{
u32 regval; int ret;
ret = oa_tc6_read_register(tc6, OA_TC6_REG_CONFIG0, ®val); if (ret) return ret;
/* Set Zero-Align Receive Frame Enable */
regval |= CONFIG0_ZARFE_ENABLE;
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.