// SPDX-License-Identifier: GPL-2.0+ /* FDDI network adapter driver for DEC FDDIcontroller 700/700-C devices. * * Copyright (c) 2018 Maciej W. Rozycki * * 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 * 2 of the License, or (at your option) any later version. * * References: * * Dave Sawyer & Phil Weeks & Frank Itkowsky, * "DEC FDDIcontroller 700 Port Specification", * Revision 1.1, Digital Equipment Corporation
*/
/* The number of transmit ring descriptors; either 0 for 512 or 1 for 1024. */ #define FZA_RING_TX_MODE 0
/* The number of receive ring descriptors; from 2 up to 256. */ #define FZA_RING_RX_SIZE 256
/* End of FZA configurable parameters. No need to change anything below. */ /* ------------------------------------------------------------------------- */
/* Shorthands for MMIO accesses that we require to be strongly ordered * WRT preceding MMIO accesses.
*/ #define readw_o readw_relaxed #define readl_o readl_relaxed
/* Shorthands for MMIO accesses that we are happy with being weakly ordered * WRT preceding MMIO accesses.
*/ #define readw_u readw_relaxed #define readl_u readl_relaxed #define readq_u readq_relaxed
staticinlinevoid fza_do_reset(struct fza_private *fp)
{ /* Reset the board. */
writew_o(FZA_RESET_INIT, &fp->regs->reset);
readw_o(&fp->regs->reset); /* Synchronize. */
readw_o(&fp->regs->reset); /* Read it back for a small delay. */
writew_o(FZA_RESET_CLR, &fp->regs->reset);
/* Enable all interrupt events we handle. */
writew_o(fp->int_mask, &fp->regs->int_mask);
readw_o(&fp->regs->int_mask); /* Synchronize. */
}
/* DEC says RESET needs up to 30 seconds to complete. My DEFZA-AA * rev. C03 happily finishes in 9.7 seconds. :-) But we need to * be on the safe side...
*/
t = wait_event_timeout(fp->state_chg_wait, fp->state_chg_flag,
45 * HZ);
status = readw_u(&fp->regs->status);
state = FZA_STATUS_GET_STATE(status); if (fp->state_chg_flag == 0) {
pr_err("%s: RESET timed out!, state %x\n", fp->name, state); return -EIO;
} if (state != FZA_STATE_UNINITIALIZED) {
pr_err("%s: RESET failed!, state %x, failure ID %x\n",
fp->name, state, FZA_STATUS_GET_TEST(status)); return -EIO;
}
pr_info("%s: OK\n", fp->name);
pr_debug("%s: RESET: %lums elapsed\n", fp->name,
(45 * HZ - t) * 1000 / HZ);
case FZA_RING_CMD_MODCAM:
i = 0;
fza_writes(&hw_addr_purger, &buf->cam.hw_addr[i++], sizeof(*buf->cam.hw_addr));
fza_writes(&hw_addr_beacon, &buf->cam.hw_addr[i++], sizeof(*buf->cam.hw_addr));
netdev_for_each_mc_addr(ha, dev) { if (i >= FZA_CMD_CAM_SIZE) break;
fza_writes(ha->addr, &buf->cam.hw_addr[i++], sizeof(*buf->cam.hw_addr));
} while (i < FZA_CMD_CAM_SIZE)
fza_zeros(&buf->cam.hw_addr[i++], sizeof(*buf->cam.hw_addr)); break;
case FZA_RING_CMD_PARAM:
writel_u(loopback, &buf->param.loop_mode);
writel_u(fp->t_max, &buf->param.t_max);
writel_u(fp->t_req, &buf->param.t_req);
writel_u(fp->tvx, &buf->param.tvx);
writel_u(fp->lem_threshold, &buf->param.lem_threshold);
fza_writes(&fp->station_id, &buf->param.station_id, sizeof(buf->param.station_id)); /* Convert to milliseconds due to buggy firmware. */
writel_u(fp->rtoken_timeout / 12500,
&buf->param.rtoken_timeout);
writel_u(fp->ring_purger, &buf->param.ring_purger); break;
spin_lock_irqsave(&fp->lock, flags);
fp->cmd_done_flag = 0;
ring = fza_cmd_send(dev, FZA_RING_CMD_INIT);
spin_unlock_irqrestore(&fp->lock, flags); if (!ring) /* This should never happen in the uninitialized state, * so do not try to recover and just consider it fatal.
*/ return -ENOBUFS;
/* INIT may take quite a long time (160ms for my C03). */
t = wait_event_timeout(fp->cmd_done_wait, fp->cmd_done_flag, 3 * HZ); if (fp->cmd_done_flag == 0) {
pr_err("%s: INIT command timed out!, state %x\n", fp->name,
FZA_STATUS_GET_STATE(readw_u(&fp->regs->status))); return -EIO;
}
stat = readl_u(&ring->stat); if (stat != FZA_RING_STAT_SUCCESS) {
pr_err("%s: INIT command failed!, status %02x, state %x\n",
fp->name, stat,
FZA_STATUS_GET_STATE(readw_u(&fp->regs->status))); return -EIO;
}
pr_debug("%s: INIT: %lums elapsed\n", fp->name,
(3 * HZ - t) * 1000 / HZ);
if (init)
*init = fp->mmio + readl_u(&ring->buffer); return 0;
}
staticvoid fza_rx_init(struct fza_private *fp)
{ int i;
/* Fill the host receive descriptor ring. */ for (i = 0; i < FZA_RING_RX_SIZE; i++) {
writel_o(0, &fp->ring_hst_rx[i].rmc);
writel_o((fp->rx_dma[i] + 0x1000) >> 9,
&fp->ring_hst_rx[i].buffer1);
writel_o(fp->rx_dma[i] >> 9 | FZA_RING_OWN_FZA,
&fp->ring_hst_rx[i].buf0_own);
}
}
left_len = len;
frag_len = FZA_TX_BUFFER_SIZE; /* First descriptor is relinquished last. */
own = FZA_RING_TX_OWN_HOST; /* First descriptor carries frame length; we don't use cut-through. */
rmc = FZA_RING_TX_SOP | FZA_RING_TX_VBC | len; do {
i = fp->ring_rmc_tx_index;
rmc_tx_ptr = &fp->buffer_tx[i];
if (left_len < FZA_TX_BUFFER_SIZE)
frag_len = left_len;
left_len -= frag_len;
/* Length must be a multiple of 4 as only word writes are * permitted!
*/
frag_len = (frag_len + 3) & ~3; if (smt)
fza_moves(ub.mmio_ptr, rmc_tx_ptr, frag_len); else
fza_writes(ub.data_ptr, rmc_tx_ptr, frag_len);
if (left_len == 0)
rmc |= FZA_RING_TX_EOP; /* Mark last frag. */
while (1) {
i = fp->ring_rmc_txd_index; if (i == fp->ring_rmc_tx_index) break;
own = readl_o(&fp->ring_rmc_tx[i].own); if ((own & FZA_RING_OWN_MASK) == FZA_RING_TX_OWN_RMC) break;
rmc = readl_u(&fp->ring_rmc_tx[i].rmc); /* Only process the first descriptor. */ if ((rmc & FZA_RING_TX_SOP) != 0) { if ((rmc & FZA_RING_TX_DCC_MASK) ==
FZA_RING_TX_DCC_SUCCESS) { int pkt_len = (rmc & FZA_RING_PBC_MASK) - 3; /* Omit PRH. */
fp->stats.tx_packets++;
fp->stats.tx_bytes += pkt_len;
} else {
fp->stats.tx_errors++; switch (rmc & FZA_RING_TX_DCC_MASK) { case FZA_RING_TX_DCC_DTP_SOP: case FZA_RING_TX_DCC_DTP: case FZA_RING_TX_DCC_ABORT:
fp->stats.tx_aborted_errors++; break; case FZA_RING_TX_DCC_UNDRRUN:
fp->stats.tx_fifo_errors++; break; case FZA_RING_TX_DCC_PARITY: default: break;
}
}
}
/* The RMC doesn't count the preamble and the starting * delimiter. We fix it up here for a total of 3 octets.
*/
dma_rmb();
len = (rmc & FZA_RING_PBC_MASK) + 3;
frame = (struct fza_fddihdr *)skb->data;
/* We need to get at real FC. */
dma_sync_single_for_cpu(fp->bdev,
dma +
((u8 *)&frame->hdr.fc - (u8 *)frame), sizeof(frame->hdr.fc),
DMA_FROM_DEVICE);
fc = frame->hdr.fc;
if (fza_rx_err(fp, rmc, fc)) goto err_rx;
/* We have to 512-byte-align RX buffers... */
newskb = fza_alloc_skb_irq(dev, FZA_RX_BUFFER_SIZE + 511); if (newskb) {
fza_skb_align(newskb, 512);
newdma = dma_map_single(fp->bdev, newskb->data,
FZA_RX_BUFFER_SIZE,
DMA_FROM_DEVICE); if (dma_mapping_error(fp->bdev, newdma)) {
dev_kfree_skb_irq(newskb);
newskb = NULL;
}
} if (newskb) { int pkt_len = len - 7; /* Omit P, SD and FCS. */ int is_multi; int rx_stat;
staticvoid fza_tx_smt(struct net_device *dev)
{ struct fza_private *fp = netdev_priv(dev); struct fza_buffer_tx __iomem *smt_tx_ptr; int i, len;
u32 own;
while (1) {
i = fp->ring_smt_tx_index;
own = readl_o(&fp->ring_smt_tx[i].own); if ((own & FZA_RING_OWN_MASK) == FZA_RING_OWN_FZA) break;
smt_tx_ptr = fp->mmio + readl_u(&fp->ring_smt_tx[i].buffer);
len = readl_u(&fp->ring_smt_tx[i].rmc) & FZA_RING_PBC_MASK;
if (!netif_queue_stopped(dev)) { if (dev_nit_active(dev)) { struct fza_buffer_tx *skb_data_ptr; struct sk_buff *skb;
/* Length must be a multiple of 4 as only word * reads are permitted!
*/
skb = fza_alloc_skb_irq(dev, (len + 3) & ~3); if (!skb) goto err_no_skb; /* Drop. */
/* Clean up the SMT TX ring. */
i = fp->ring_smt_tx_index; do {
writel_o(FZA_RING_OWN_FZA, &fp->ring_smt_tx[i].own);
fp->ring_smt_tx_index =
(fp->ring_smt_tx_index + 1) % fp->ring_smt_tx_size;
} while (i != fp->ring_smt_tx_index);
/* Clean up the RMC TX ring. */
i = fp->ring_rmc_tx_index; do {
own = readl_o(&fp->ring_rmc_tx[i].own); if ((own & FZA_RING_OWN_MASK) == FZA_RING_TX_OWN_RMC) {
u32 rmc = readl_u(&fp->ring_rmc_tx[i].rmc);
/* State change interrupt. */ if ((int_event & FZA_EVENT_STATE_CHG) != 0) {
uint status, state;
fp->irq_count_state_chg++;
status = readw_u(&fp->regs->status);
state = FZA_STATUS_GET_STATE(status);
pr_debug("%s: state change: %x\n", fp->name, state); switch (state) { case FZA_STATE_RESET: break;
/* Decode FC to set PRH. */
fc = skb->data[3];
skb->data[0] = 0;
skb->data[1] = 0;
skb->data[2] = FZA_PRH2_NORMAL; if ((fc & FDDI_FC_K_CLASS_MASK) == FDDI_FC_K_CLASS_SYNC)
skb->data[0] |= FZA_PRH0_FRAME_SYNC; switch (fc & FDDI_FC_K_FORMAT_MASK) { case FDDI_FC_K_FORMAT_MANAGEMENT: if ((fc & FDDI_FC_K_CONTROL_MASK) == 0) { /* Token. */
skb->data[0] |= FZA_PRH0_TKN_TYPE_IMM;
skb->data[1] |= FZA_PRH1_TKN_SEND_NONE;
} else { /* SMT or MAC. */
skb->data[0] |= FZA_PRH0_TKN_TYPE_UNR;
skb->data[1] |= FZA_PRH1_TKN_SEND_UNR;
}
skb->data[1] |= FZA_PRH1_CRC_NORMAL; break; case FDDI_FC_K_FORMAT_LLC: case FDDI_FC_K_FORMAT_FUTURE:
skb->data[0] |= FZA_PRH0_TKN_TYPE_UNR;
skb->data[1] |= FZA_PRH1_CRC_NORMAL | FZA_PRH1_TKN_SEND_UNR; break; case FDDI_FC_K_FORMAT_IMPLEMENTOR:
skb->data[0] |= FZA_PRH0_TKN_TYPE_UNR;
skb->data[1] |= FZA_PRH1_TKN_SEND_ORIG; break;
}
/* SMT transmit interrupts may sneak frames into the RMC * transmit ring. We disable them while queueing a frame * to maintain consistency.
*/
old_mask = fp->int_mask;
new_mask = old_mask & ~FZA_MASK_SMT_TX_POLL;
writew_u(new_mask, &fp->regs->int_mask);
readw_o(&fp->regs->int_mask); /* Synchronize. */
fp->int_mask = new_mask;
ret = fza_do_xmit((union fza_buffer_txp)
{ .data_ptr = (struct fza_buffer_tx *)skb->data },
skb->len, dev, 0);
fp->int_mask = old_mask;
writew_u(fp->int_mask, &fp->regs->int_mask);
if (ret) { /* Probably an SMT packet filled the remaining space, * so just stop the queue, but don't report it as an error.
*/
netif_stop_queue(dev);
pr_debug("%s: queue stopped\n", fp->name);
fp->stats.tx_dropped++;
}
/* Initialize the new device structure. */ switch (loopback) { case FZA_LOOP_NORMAL: case FZA_LOOP_INTERN: case FZA_LOOP_EXTERN: break; default:
loopback = FZA_LOOP_NORMAL;
}
fp->mmio = mmio;
dev->irq = tdev->interrupt;
pr_info("%s: DEC FDDIcontroller 700 or 700-C at 0x%08llx, irq %d\n",
fp->name, (longlong)tdev->resource.start, dev->irq);
pr_debug("%s: mapped at: 0x%p\n", fp->name, mmio);
/* Sanitize the board. */
fza_regs_dump(fp);
fza_do_shutdown(fp);
ret = request_irq(dev->irq, fza_interrupt, IRQF_SHARED, fp->name, dev); if (ret != 0) {
pr_err("%s: unable to get IRQ %d!\n", fp->name, dev->irq); goto err_out_map;
}
/* Enable the driver mode. */
writew_o(FZA_CONTROL_B_DRIVER, &fp->regs->control_b);
/* For some reason transmit done interrupts can trigger during * reset. This avoids a division error in the handler.
*/
fp->ring_rmc_tx_size = FZA_RING_TX_SIZE;
ret = fza_reset(fp); if (ret != 0) goto err_out_irq;
ret = fza_init_send(dev, &init); if (ret != 0) goto err_out_irq;
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.