summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--sys/dev/phy/rt8139.c315
-rw-r--r--sys/include/dev/phy/rt8139.h91
2 files changed, 406 insertions, 0 deletions
diff --git a/sys/dev/phy/rt8139.c b/sys/dev/phy/rt8139.c
new file mode 100644
index 0000000..e2f87e1
--- /dev/null
+++ b/sys/dev/phy/rt8139.c
@@ -0,0 +1,315 @@
+/*
+ * Copyright (c) 2023-2025 Ian Marco Moffett and the Osmora Team.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of Hyra nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <sys/types.h>
+#include <sys/errno.h>
+#include <sys/syslog.h>
+#include <sys/driver.h>
+#include <dev/pci/pci.h>
+#include <dev/phy/rt8139.h>
+#include <dev/timer.h>
+#include <dev/pci/pciregs.h>
+#include <vm/physmem.h>
+#include <vm/vm.h>
+#include <machine/pio.h>
+#include <string.h>
+
+/* TODO: Make this smoother */
+#if defined(__x86_64__)
+#include <machine/intr.h>
+#include <machine/ioapic.h>
+#include <machine/lapic.h>
+#include <machine/idt.h>
+#endif
+
+#define pr_trace(fmt, ...) kprintf("rt8139: " fmt, ##__VA_ARGS__)
+#define pr_error(...) pr_trace(__VA_ARGS__)
+
+#define RX_BUF_SIZE 3 /* In pages */
+#define RX_REAL_BUF_SIZE 8192 /* In bytes */
+
+#define RX_PTR_MASK (~3)
+
+/* Does our platform support PIO? */
+#if defined(_MACHINE_HAVE_PIO) || defined(__x86_64__)
+#define HAVE_PIO 1
+#else
+#define HAVE_PIO 0
+#endif /* _MACHINE_HAVE_PIO */
+
+static struct pci_device *dev;
+static struct timer tmr;
+static uint16_t ioport;
+static paddr_t rxbuf, txbuf;
+
+/*
+ * Write to an RTL8139 register
+ *
+ * @size: 1 (8-bits), 2 (16-bits), or 4 (32 bits)
+ */
+static inline void
+rt_write(uint8_t reg, uint8_t size, uint32_t val)
+{
+ switch (size) {
+ case 1:
+ outb(ioport + reg, (uint8_t)val);
+ break;
+ case 2:
+ outw(ioport + reg, (uint16_t)val);
+ break;
+ case 4:
+ outl(ioport + reg, val);
+ break;
+ default:
+ pr_error("rt_write: bad size %d\n", size);
+ break;
+ }
+}
+
+/*
+ * Read from an RTL8139 register
+ *
+ * @size: 1 (8-bits), 2 (16-bits), or 4 (32 bits)
+ */
+static inline uint32_t
+rt_read(uint8_t reg, uint8_t size)
+{
+ uint32_t retval;
+
+ switch (size) {
+ case 1:
+ retval = inb(ioport + reg);
+ break;
+ case 2:
+ retval = inw(ioport + reg);
+ break;
+ case 4:
+ retval = inl(ioport + reg);
+ break;
+ default:
+ pr_error("rt_read: bad size %d\n", size);
+ return MASK(sizeof(retval) * 8);
+ }
+
+ return retval;
+}
+
+/*
+ * Poll register to have 'bits' set/unset.
+ *
+ * @reg: Register to poll.
+ * @bits: Bits to be checked.
+ * @pollset: True to poll as set.
+ */
+static uint32_t
+rt_poll(uint8_t reg, uint8_t size, uint32_t bits, bool pollset)
+{
+ size_t usec_start, usec;
+ size_t elapsed_msec;
+ uint32_t val;
+ bool tmp;
+
+ usec_start = tmr.get_time_usec();
+ for (;;) {
+ val = rt_read(reg, size);
+ tmp = (pollset) ? ISSET(val, bits) : !ISSET(val, bits);
+
+ usec = tmr.get_time_usec();
+ elapsed_msec = (usec - usec_start) / 1000;
+
+ /* If tmp is set, the register updated in time */
+ if (tmp) {
+ break;
+ }
+
+ /* Exit with an error if we timeout */
+ if (elapsed_msec > RT_TIMEOUT_MSEC) {
+ return -ETIME;
+ }
+ }
+ return val;
+}
+
+#if defined(__x86_64__)
+__isr static void
+rt8139_pin_irq(void *sp)
+{
+ static uint32_t packet_ptr = 0;
+ uint16_t len;
+ uint16_t *p;
+ uint16_t status;
+
+ status = rt_read(RT_INTRSTATUS, 2);
+ p = (uint16_t *)(rxbuf + packet_ptr);
+ len = *(p + 1); /* Length after header */
+ p += 2; /* Points to data now */
+
+ if (status & RT_TOK) {
+ return;
+ }
+
+ /* Update rxbuf offset in CAPR */
+ packet_ptr = (packet_ptr + len + 4 + 3) & RX_PTR_MASK;
+ if (packet_ptr > RX_REAL_BUF_SIZE) {
+ packet_ptr -= RX_REAL_BUF_SIZE;
+ }
+ rt_write(RT_RXBUFTAIL, 2, packet_ptr - 0x10);
+ rt_write(RT_INTRSTATUS, 2, RT_ACKW);
+ lapic_eoi();
+}
+
+static int
+rtl8139_irq_init(void)
+{
+ int vec;
+
+ vec = intr_alloc_vector("rt8139", IPL_BIO);
+ if (vec < 0) {
+ return vec;
+ }
+
+ /* Map interrupt vector to IRQ */
+ idt_set_desc(vec, IDT_INT_GATE, ISR(rt8139_pin_irq), 0);
+ ioapic_set_vec(dev->irq_line, vec);
+ ioapic_irq_unmask(dev->irq_line);
+ return 0;
+}
+#else
+#define rtl8139_irq_init(...) -ENOTSUP
+#endif
+
+static void
+rt_init_pci(void)
+{
+ uint32_t tmp;
+
+ /* Enable bus mastering and MMIO */
+ tmp = pci_readl(dev, PCIREG_CMDSTATUS);
+ tmp |= (PCI_BUS_MASTERING | PCI_MEM_SPACE);
+ pci_writel(dev, PCIREG_CMDSTATUS, tmp);
+}
+
+static int
+rt_init_mac(void)
+{
+ /*
+ * First step is ensuring the MAC is in known
+ * and consistent state by resetting it. God
+ * knows what BIOS or UEFI did to it...
+ */
+ ioport = dev->bar[0] & ~1;
+ pr_trace("resetting MAC...\n");
+ rt_write(RT_CHIPCMD, 1, RT_RST);
+ rt_poll(RT_CHIPCMD, 1, RT_RST, 0);
+
+ rxbuf = vm_alloc_frame(RX_BUF_SIZE);
+ txbuf = vm_alloc_frame(RX_BUF_SIZE);
+
+ if (rxbuf == 0 || txbuf == 0) {
+ if (rxbuf != 0)
+ vm_free_frame(rxbuf, RX_BUF_SIZE);
+ if (txbuf != 0)
+ vm_free_frame(txbuf, RX_BUF_SIZE);
+
+ pr_error("failed to alloc TX/RX buffers\n");
+ return -ENOMEM;
+ }
+
+ /*
+ * Configure the chip:
+ *
+ * - Enable machdep IRQ
+ * - Point RX buffer
+ * - Setup RX buffer flags:
+ * * Accept broadcast
+ * * Accept multicast
+ * * Accept physical match
+ * * Accept all packets (promiscuous mode)
+ * ? (AB/AM/APM/AAP)
+ *
+ * TODO: ^ Some of these should be configurable ^
+ *
+ * - Enable interrupts through ROK/TOK
+ * - Enable RX state machines
+ *
+ * TODO: Support TX
+ *
+ */
+ rtl8139_irq_init();
+ rt_write(RT_RXBUF, 4, rxbuf);
+ rt_write(RT_RXCONFIG, 4, RT_AB | RT_AM | RT_APM | RT_AAP);
+ rt_write(RT_INTRMASK, 2, RT_ROK | RT_TOK);
+ rt_write(RT_CHIPCMD, 1, RT_RE);
+ return 0;
+}
+
+static int
+rt813l_init(void)
+{
+ struct pci_lookup lookup;
+
+ lookup.vendor_id = 0x10EC;
+ lookup.device_id = 0x8139;
+ dev = pci_get_device(lookup, PCI_VENDOR_ID | PCI_DEVICE_ID);
+ if (dev == NULL) {
+ return -ENODEV;
+ }
+
+ pr_trace("Realtek network ctrl <phy? at pci%d:%x.%x.%d>\n",
+ dev->bus, dev->device_id, dev->func,
+ dev->slot);
+
+ if (HAVE_PIO == 0) {
+ pr_error("port i/o not supported, bailing\n");
+ return -ENOTSUP;
+ }
+
+ /* Try to request a general purpose timer */
+ if (req_timer(TIMER_GP, &tmr) != TMRR_SUCCESS) {
+ pr_error("failed to fetch general purpose timer\n");
+ return -ENODEV;
+ }
+
+ /* Ensure it has get_time_usec() */
+ if (tmr.get_time_usec == NULL) {
+ pr_error("general purpose timer has no get_time_usec()\n");
+ return -ENODEV;
+ }
+
+ /* We also need msleep() */
+ if (tmr.msleep == NULL) {
+ pr_error("general purpose timer has no msleep()\n");
+ return -ENODEV;
+ }
+
+ rt_init_pci();
+ return rt_init_mac();
+}
+
+DRIVER_EXPORT(rt813l_init);
diff --git a/sys/include/dev/phy/rt8139.h b/sys/include/dev/phy/rt8139.h
new file mode 100644
index 0000000..ef7b127
--- /dev/null
+++ b/sys/include/dev/phy/rt8139.h
@@ -0,0 +1,91 @@
+/*
+ * Copyright (c) 2023-2025 Ian Marco Moffett and the Osmora Team.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of Hyra nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef _PHY_RT8139_H_
+#define _PHY_RT8139_H_
+
+#include <sys/types.h>
+#include <sys/param.h>
+
+#define RT_IDR0 0x00 /* MAC address */
+#define RT_MAR0 0x08 /* Multicast filter */
+#define RT_TXSTATUS0 0x10 /* Transmit status (4 32bit regs) */
+#define RT_TXADDR0 0x20 /* Tx descriptors (also 4 32bit) */
+#define RT_RXBUF 0x30 /* Receive buffer start address */
+#define RT_RXEARLYCNT 0x34 /* Early Rx byte count */
+#define RT_RXEARLYSTATUS 0x36 /* Early Rx status */
+#define RT_CHIPCMD 0x37 /* Command register */
+#define RT_RXBUFTAIL 0x38 /* Current address of packet read (queue tail) */
+#define RT_RXBUFHEAD 0x3A /* Current buffer address (queue head) */
+#define RT_INTRMASK 0x3C /* Interrupt mask */
+#define RT_INTRSTATUS 0x3E /* Interrupt status */
+#define RT_TXCONFIG 0x40 /* Tx config */
+#define RT_RXCONFIG 0x44 /* Rx config */
+#define RT_TIMER 0x48 /* A general purpose counter */
+#define RT_RXMISSED 0x4C /* 24 bits valid, write clears */
+#define RT_CFG9346 0x50 /* 93C46 command register */
+#define RT_CONFIG0 0x51 /* Configuration reg 0 */
+#define RT_CONFIG1 0x52 /* Configuration reg 1 */
+#define RT_TIMERINT 0x54 /* Timer interrupt register (32 bits) */
+#define RT_MEDIASTATUS 0x58 /* Media status register */
+#define RT_CONFIG3 0x59 /* Config register 3 */
+#define RT_CONFIG4 0x5A /* Config register 4 */
+#define RT_MULTIINTR 0x5C /* Multiple interrupt select */
+#define RT_MII_TSAD 0x60 /* Transmit status of all descriptors (16 bits) */
+#define RT_MII_BMCR 0x62 /* Basic Mode Control Register (16 bits) */
+#define RT_MII_BMSR 0x64 /* Basic Mode Status Register (16 bits) */
+#define RT_AS_ADVERT 0x66 /* Auto-negotiation advertisement reg (16 bits) */
+#define RT_AS_LPAR 0x68 /* Auto-negotiation link partner reg (16 bits) */
+#define RT_AS_EXPANSION 0x6A /* Auto-negotiation expansion reg (16 bits) */
+
+/* Command register bits */
+#define RT_BUFEN BIT(0) /* Buffer empty */
+#define RT_TE BIT(2) /* Transmitter enable */
+#define RT_RE BIT(3) /* Receiver enable */
+#define RT_RST BIT(4) /* Reset */
+
+/* Receive register bits */
+#define RT_AAP BIT(0) /* Accept all packets */
+#define RT_APM BIT(1) /* Accept physical match packets */
+#define RT_AM BIT(2) /* Accept multicast packets */
+#define RT_AB BIT(3) /* Accept brodcast packets */
+#define RT_AR BIT(4) /* Accept runt */
+#define RT_AER BIT(5) /* Accept error packet */
+#define RT_WRAP BIT(6) /* RX wrap */
+
+/* Interrupt mask bits */
+#define RT_ROK BIT(0) /* Receive OK */
+#define RT_RER BIT(1) /* Receive error */
+#define RT_TOK BIT(2) /* Transmit OK */
+#define RT_ACKW 0x0005 /* RX/TX ACK word */
+
+/* Register poll timeout */
+#define RT_TIMEOUT_MSEC 500
+
+#endif /* !_PHY_RT8139_H_ */