diff --git a/drivers/Config.uk b/drivers/Config.uk index 3c5afb5a29da6664828c13f2ecb417776d0c4fa4..8d4dd132a14a40b6e6cd47d1c35e6f34b4ee8089 100644 --- a/drivers/Config.uk +++ b/drivers/Config.uk @@ -15,3 +15,9 @@ menu "Virtio" source "$(shell,$(UK_BASE)/support/build/config-submenu.sh -q -o '$(KCONFIG_DIR)/drivers-virtio.uk' -r '$(KCONFIG_DRIV_BASE)/virtio' -l '$(KCONFIG_DRIV_BASE)/virtio' -e '$(KCONFIG_EXCLUDEDIRS)')" endmenu + +menu "NET" + +source "$(shell,$(UK_BASE)/support/build/config-submenu.sh -q -o '$(KCONFIG_DIR)/drivers-net.uk' -r '$(KCONFIG_DRIV_BASE)/net' -l '$(KCONFIG_DRIV_BASE)/net' -e '$(KCONFIG_EXCLUDEDIRS)')" + +endmenu diff --git a/drivers/Makefile.uk b/drivers/Makefile.uk index 3a409856c50bb4c6e19c814f361182b5eea2a29e..2183277ca018e6771ec61da187cf193cb8e4c459 100644 --- a/drivers/Makefile.uk +++ b/drivers/Makefile.uk @@ -11,3 +11,4 @@ $(eval $(call import_lib,$(UK_DRIV_BASE)/ukintctlr)) $(eval $(call import_lib,$(UK_DRIV_BASE)/uktty)) $(eval $(call import_lib,$(UK_DRIV_BASE)/virtio)) $(eval $(call import_lib,$(UK_DRIV_BASE)/sysclock)) +$(eval $(call import_lib,$(UK_DRIV_BASE)/net)) diff --git a/drivers/net/Makefile.uk b/drivers/net/Makefile.uk new file mode 100644 index 0000000000000000000000000000000000000000..cc2fd34ceb6350102eb5845e95d031a9814bfd7c --- /dev/null +++ b/drivers/net/Makefile.uk @@ -0,0 +1,3 @@ +UK_DRIV_NET_BASE := $(UK_DRIV_BASE)/net + +$(eval $(call import_lib,$(UK_DRIV_NET_BASE)/mac)) \ No newline at end of file diff --git a/drivers/net/mac/Config.uk b/drivers/net/mac/Config.uk new file mode 100644 index 0000000000000000000000000000000000000000..38a489b82a4b5e5ea9f9b781406e97eb269a72b2 --- /dev/null +++ b/drivers/net/mac/Config.uk @@ -0,0 +1,11 @@ +config LIBDRV_GMAC + bool "Designware Gmac Driver" + default y + depends on PLAT_RK3568 + +if LIBDRV_GMAC + config GMAC_TEST + bool "Enable gmac unit tests" + default n + select LIBUKTEST +endif diff --git a/drivers/net/mac/Makefile.uk b/drivers/net/mac/Makefile.uk new file mode 100644 index 0000000000000000000000000000000000000000..9683418dcf8bec7ee13b25d6af274f0d87bf134f --- /dev/null +++ b/drivers/net/mac/Makefile.uk @@ -0,0 +1,14 @@ +$(eval $(call addlib_s,libdrv_gmac,$(CONFIG_LIBDRV_GMAC))) + +CINCLUDES-$(CONFIG_LIBDRV_GMAC) += -I$(LIBDRV_GMAC_BASE)/include +LIBDRV_GMAC_CINCLUDES-y += -I$(UK_PLAT_COMMON_BASE)/include + +LIBDRV_GMAC_SRCS-y += \ + $(LIBDRV_GMAC_BASE)/dwgmac.c \ + $(LIBDRV_GMAC_BASE)/gmac.c \ + $(LIBDRV_GMAC_BASE)/mii.c \ + $(LIBDRV_GMAC_BASE)/phy.c + +ifneq ($(filter y,$(CONFIG_GMAC_TEST) $(CONFIG_LIBUKTEST_ALL)),) +LIBDRV_GMAC_SRCS-y += $(LIBDRV_GMAC_BASE)/tests/test_gmac.c +endif \ No newline at end of file diff --git a/drivers/net/mac/dwgmac.c b/drivers/net/mac/dwgmac.c new file mode 100644 index 0000000000000000000000000000000000000000..85dc74217e65712e446abcf4b6d3c646f9a25a62 --- /dev/null +++ b/drivers/net/mac/dwgmac.c @@ -0,0 +1,509 @@ +/* Copyright 2024 Hangzhou Yingyi Technology Co., Ltd + * + * 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 ssANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +UK_SLIST_HEAD(gmac_platdata_head, struct dwgmac_platdata); +static struct gmac_platdata_head gmac_pdata_list; +static struct uk_alloc *a; + +static const struct dwgmac_config dwgmac_config = { + .mdio_wait = 10000, + .swr_wait = 200, + .config_mac = MAC_RXQ_CTRL0_RXQ0EN_NOT_ENABLED, + .config_mac_mdio = MAC_MDIO_ADDRESS_CR_100_150, + .ops = &dwgmac_ops, +}; + +struct dwgmac_platdata *netdev_get_platdata(const struct uk_netdev *ndev) +{ + struct dwgmac_platdata *cur; + + UK_SLIST_FOREACH(cur, &gmac_pdata_list, next) { + if (cur->netdev == ndev) + return cur; + } + + UK_ASSERT(0); + return NULL; +} + +struct dwgmac_platdata *pfdev_get_platdata(const struct pf_device *pfdev) +{ + struct dwgmac_platdata *cur; + + UK_SLIST_FOREACH(cur, &gmac_pdata_list, next) { + if (cur->pfdev == pfdev) + return cur; + } + + UK_ASSERT(0); + return NULL; +} + + +struct dwgmac_platform_ops *platops; + +static int ndo_start(struct uk_netdev *dev) +{ + struct dwgmac_platdata *pdata = netdev_get_platdata(dev); + int ret; + + ret = dwgmac_init(pdata); + if (ret) + return ret; + + ret = platops->do_mac_speed_fix(pdata); + if (ret) + return ret; + + dwgmac_enable(pdata); + + return 0; +} + +/* + * 通过随机数生成mac地址, 可能存在很低的概率发生mac冲突 + * mac地址根据不同的soc,可通过emmc/flash/eprom等设备读取 + * 但目前不支持mmc驱动程序,所以通过随机生成的方式 + */ +static void generate_mac_address(struct uk_hwaddr *hwaddr) +{ + int ret; + /* 注意: 此处数据类型要是uint8_t,否则偏移会产生错误 */ + uint8_t *macaddr = hwaddr->addr_bytes; + + UK_ASSERT(sizeof(hwaddr->addr_bytes) == UK_NETDEV_HWADDR_LEN); + UK_ASSERT(sizeof(hwaddr->addr_bytes) - sizeof(int) == 2); + + srandom((unsigned int) time(NULL)); + ret = random(); + memcpy(macaddr, &ret, sizeof(int)); + ret = random(); + memcpy(macaddr + sizeof(int), &ret, + sizeof(hwaddr->addr_bytes) - sizeof(int)); +} + +static int do_pf_probe(struct pf_device *pfdev) +{ + struct dwgmac_platdata *pdata, *cur; + struct dwgmac_priv *dwpriv; + struct dwgmac_config *config; + int ret; + + UK_ASSERT(!PTRISERR(pfdev)); + + pdata = uk_zalloc(a, sizeof(*pdata)); + if (unlikely(PTRISERR(pdata))) + return -ENOMEM; + + dwpriv = &pdata->dwpriv; + dwpriv->a = a; + dwpriv->config = + (struct dwgmac_config *) uk_zalloc(dwpriv->a, sizeof(*dwpriv->config)); + if (unlikely(PTRISERR(dwpriv->config))) + return -ENOMEM; + + config = dwpriv->config; + + UK_SLIST_INSERT_HEAD(&gmac_pdata_list, pdata, next); + pdata->pfdev = pfdev; + ret = platops->do_plat_init(pdata); + if (ret) { + uk_pr_err("do_plat_init failed, err=%d\n", ret); + return -EINVAL; + } + + /* + * 由于发现过百兆和千兆网卡生成了相同的mac地址的情况 + * 所以此处进行两两比较,若网卡存在mac地址冲突,则重新生成 + */ + while (1) { + ret = 1; + + generate_mac_address(&pdata->enetaddr); + + UK_SLIST_FOREACH(cur, &gmac_pdata_list, next) { + if (cur->pfdev == pfdev) + continue; + + if (!memcmp(cur->enetaddr.addr_bytes, + pdata->enetaddr.addr_bytes, + sizeof(pdata->enetaddr.addr_bytes))) { + ret = 0; + break; + } + } + + if (ret) + break; + } + + memcpy(config, &dwgmac_config, sizeof(struct dwgmac_config)); + pdata->phyif = config->ops->do_phyif_get(pfdev); + + ret = platops->do_clks_set_default(pdata); + if (ret) + UK_CRASH("%s clk_set_defaults failed %d\n", __func__, ret); + + switch (pdata->phyif) { + case PHY_IF_MODE_RGMII: + case PHY_IF_MODE_RGMII_RXID: + if (!pdata->clock_input) { + ret = platops->do_src_clk_get(pdata); + if (ret != GMAC_RGMII_CLK_RATE) + return -EINVAL; + } + + if (pdata->phyif == PHY_IF_MODE_RGMII_RXID) + pdata->rx_delay = -1; + + UK_ASSERT(platops->do_mii_mode_set); + platops->do_mii_mode_set(pdata); + + break; + default: + UK_CRASH("Unsupported %s %s: %d\n", __FILE__, __func__, + pdata->phyif); + return -EOPNOTSUPP; + } + + return dwgmac_probe(pfdev, pdata); +} + +static int do_pf_init(struct uk_alloc *drv_allocator) +{ + UK_ASSERT(!PTRISERR(drv_allocator)); + + if (!a) { + a = drv_allocator; + UK_ASSERT(!gmac_pdata_list.slh_first); + UK_SLIST_INIT(&gmac_pdata_list); + } else if (drv_allocator != a) { + /* 不允许相同驱动使用不同的内存分配器 */ + UK_CRASH("%s %s drv_allocator != a\n", __FILE__, __func__); + } + + return 0; +} + +static int ndo_probe(struct uk_netdev *netdev __maybe_unused) +{ + return 0; +} + + +static void ndo_info_get(struct uk_netdev *ndev __maybe_unused, + struct uk_netdev_info *dev_info) +{ + UK_ASSERT(dev_info); + + /* 框架层只支持单队列形式,暂时不需要多队列的需求 */ + dev_info->max_rx_queues = 1; + dev_info->max_tx_queues = 1; + + /* 以太标准中,mtu大小规定为1500,其他的协议可能有所不同 */ + dev_info->max_mtu = 1500; + + /* + * 设置UK_NETDEV_F_RXQ_INTR标志位(中断-轮询机制): + * RX通过中断得知数据已达,关中断并轮询消费 + * RX缓冲区消费完成后,打开中断等待下次数据来临的中断 + * + * 不设置UK_NETDEV_F_RXQ_INTR标志位(轮询机制): + * uknetdev创建poll线程进行轮询消费 + * + * 若非网络设备,使用中断-轮询机制可以对网络性能和处理器负载有较好的平衡 + */ +#if 0 + dev_info->features = UK_NETDEV_F_RXQ_INTR; +#else + dev_info->features = 0; +#endif + + /* ioalign用于分配netbuf时的对齐参数,以cacheline size进行对齐 */ + dev_info->ioalign = ARCH_DMA_MINALIGN; + + /* + * netbuf预留的headroom大小 + * 对于以太网协议, vlan tag可能需要额外的空间 + * 目前不需要支持vlan, 所以配置成0, 不需要预留空间 + */ + dev_info->nb_encap_tx = 0; + dev_info->nb_encap_rx = 0; +} + + +static int ndo_configure(struct uk_netdev *n, const struct uk_netdev_conf *conf) +{ + UK_ASSERT(n); + UK_ASSERT(conf); + UK_ASSERT(conf->nb_rx_queues == 1 && conf->nb_tx_queues == 1); + return 0; +} + + +/* 获取是否使能混杂模式, 返回0:关闭混杂模式; 1:使能混杂模式 */ +static unsigned ndo_promiscuous_get(struct uk_netdev *ndev __maybe_unused) +{ + return 0; +} + +/* 获取MTU的大小, 以太网标准下MTU为1500 */ +static uint16_t ndo_mtu_get(struct uk_netdev *ndev __maybe_unused) +{ + return 1500; +} + +static const struct uk_hwaddr *ndo_hwaddr_get(struct uk_netdev *ndev) +{ + UK_ASSERT(ndev); + + struct uk_hwaddr *e = &netdev_get_platdata(ndev)->enetaddr; + + uk_pr_info("%s, mac: %x:%x:%x:%x:%x:%x\n", __func__, e->addr_bytes[0], + e->addr_bytes[1], e->addr_bytes[2], e->addr_bytes[3], + e->addr_bytes[4], e->addr_bytes[5]); + + return e; +} + +static int ndo_txq_info_get(struct uk_netdev *dev __maybe_unused, + uint16_t queue_id __maybe_unused, + struct uk_netdev_queue_info *qinfo) +{ + + UK_ASSERT(dev); + UK_ASSERT(qinfo); + UK_ASSERT(queue_id == 0); + + const int dma_tx_size = 512; + + qinfo->nb_min = 1; + qinfo->nb_max = dma_tx_size; + qinfo->nb_is_power_of_two = 8; + + return 0; +} + +static int ndo_rxq_info_get(struct uk_netdev *dev __maybe_unused, + uint16_t queue_id __maybe_unused, + struct uk_netdev_queue_info *qinfo) +{ + UK_ASSERT(dev); + UK_ASSERT(qinfo); + UK_ASSERT(queue_id == 0); + + const int dma_rx_size = 512; + + qinfo->nb_min = 1; + qinfo->nb_max = dma_rx_size; + qinfo->nb_is_power_of_two = 8; + + return 0; +} + +static const void *ndo_einfo_get(struct uk_netdev *ndev, + enum uk_netdev_einfo_type etype) +{ + UK_ASSERT(ndev); + struct dwgmac_platdata *pdata = netdev_get_platdata(ndev); + + switch (etype) { + case UK_NETDEV_IPV4_ADDR_STR: + return pdata->id == 0 ? "169.254.107.137" : NULL; + // return pdata->id == 0 ? "169.254.107.137" : "169.254.107.138"; + case UK_NETDEV_IPV4_MASK_STR: + // return "255.255.0.0"; + return pdata->id == 0 ? "255.255.0.0" : NULL; + case UK_NETDEV_IPV4_GW_STR: + // return "169.254.107.1"; + return pdata->id == 0 ? "169.254.107.1" : NULL; + default: + uk_pr_err("%s Unknown etype: %d\n", __func__, etype); + break; + } + + return NULL; +} + +/* TODO: intr enable */ +static int ndo_rxq_intr_enable(struct uk_netdev *ndev __maybe_unused, + struct uk_netdev_rx_queue *rx __maybe_unused) +{ + return 0; +} + +/* TODO: intr disable */ +static int ndo_rxq_intr_disable(struct uk_netdev *ndev __maybe_unused, + struct uk_netdev_rx_queue *rx __maybe_unused) +{ + return 0; +} + +static struct uk_netdev_rx_queue * +ndo_rxq_configure(struct uk_netdev *n, uint16_t qid, + uint16_t nb_desc __maybe_unused, + struct uk_netdev_rxqueue_conf *conf) +{ + struct dwgmac_platdata *cur; + + UK_ASSERT(n); + UK_ASSERT(qid == 0); + UK_ASSERT(conf); + UK_ASSERT(conf->alloc_rxpkts); + + cur = netdev_get_platdata(n); + cur->rxq.a = conf->a; + cur->rxq.alloc_rxpkts = conf->alloc_rxpkts; + cur->rxq.alloc_rxpkts_argp = conf->alloc_rxpkts_argp; + cur->rxq.qid = qid; + n->_rx_queue[0] = &cur->rxq; + + return &cur->rxq; +} + +static struct uk_netdev_tx_queue * +ndo_txq_configure(struct uk_netdev *n, uint16_t qid, + uint16_t nb_desc __unused, + struct uk_netdev_txqueue_conf *conf) +{ + struct dwgmac_platdata *cur; + + UK_ASSERT(qid == 0); + + cur = netdev_get_platdata(n); + n->_tx_queue[0] = &cur->txq; + cur->txq.qid = qid; + cur->txq.a = conf->a; + + return &cur->txq; +} + +static const struct uk_netdev_ops netops = { + .probe = ndo_probe, + .info_get = ndo_info_get, + .configure = ndo_configure, + .rxq_configure = ndo_rxq_configure, + .txq_configure = ndo_txq_configure, + .start = ndo_start, + .hwaddr_get = ndo_hwaddr_get, + .mtu_get = ndo_mtu_get, + .promiscuous_get = ndo_promiscuous_get, + .txq_info_get = ndo_txq_info_get, + .rxq_info_get = ndo_rxq_info_get, + .einfo_get = ndo_einfo_get, + .rxq_intr_enable = ndo_rxq_intr_enable, + .rxq_intr_disable = ndo_rxq_intr_disable, +}; + +static int do_pf_add_dev(struct pf_device *pfdev) +{ + struct dwgmac_platdata *cur; + struct uk_netdev *ndev; + const char fmt[] = "%s-%d"; + const int name_len = 30; + char *name = uk_zalloc(a, sizeof(char) * name_len); + int err; + + cur = pfdev_get_platdata(pfdev); + ndev = uk_zalloc(a, sizeof(*ndev)); + if (!ndev) + return -ENOMEM; + + cur->netdev = ndev; + dwgmac_register_netdev_ops(ndev); + ndev->ops = &netops; + + snprintf(name, name_len, fmt, platops->do_compatible_drvname_get(), cur->id); + err = uk_netdev_drv_register(ndev, a, name); + if (err < 0) + goto err; + + return 0; + +err: + uk_free(a, name); + uk_free(a, ndev); + cur->netdev = NULL; + return -ENOMEM; +} + +static int do_pf_match(const char *compatible) +{ + UK_ASSERT(platops->do_compatible_drvname_get); + return !strcmp(compatible, platops->do_compatible_drvname_get()) ? GMAC_ID : 0; +} + + +const struct pf_device_id device_ids = {.device_id = GMAC_ID}; + +static struct pf_driver eth_gmac_rockchip = {.device_ids = &device_ids, + .match = do_pf_match, + .init = do_pf_init, + .probe = do_pf_probe, + .add_dev = do_pf_add_dev}; + +PF_REGISTER_DRIVER(ð_gmac_rockchip); + + +#ifdef CONFIG_GMAC_TEST + +#include + +struct dwgmac_platdata *get_gmac_platdata_by_id(unsigned id) +{ + struct dwgmac_platdata *ptr; + + UK_SLIST_FOREACH(ptr, &gmac_pdata_list, next) + if (ptr->id == id) + return ptr; + + return NULL; +} + +int gmac_nr(void) +{ + struct dwgmac_platdata *ptr; + int cnt = 0; + + UK_SLIST_FOREACH(ptr, &gmac_pdata_list, next) + ++cnt; + + return cnt; +} + +#endif \ No newline at end of file diff --git a/drivers/net/mac/gmac.c b/drivers/net/mac/gmac.c new file mode 100644 index 0000000000000000000000000000000000000000..bbdbf6483f3c535e52c6eb49042f97a12a4f97ca --- /dev/null +++ b/drivers/net/mac/gmac.c @@ -0,0 +1,957 @@ +/* Copyright 2024 Hangzhou Yingyi Technology Co., Ltd + * + * 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 ssANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#define setbits_le32(addr, set) do { \ + ioreg_write32((addr), (ioreg_read32(addr) | (set))); \ +} while (0); + +#define clrsetbits_le32(addr, clear, set) do { \ + ioreg_write32((addr), (ioreg_read32(addr) & ~(clear)) | (set)); \ +} while (0); + +#define clrbits_le32(addr, clear) do { \ + ioreg_write32((addr), (ioreg_read32(addr) & ~(clear))); \ +} while (0); + + +extern void clean_and_invalidate_dcache_range(int start, int len); +extern void invalidate_dcache_range(int start, int len); + +struct mtl_regs { + uint32_t txq0_operation_mode; /* 0xd00 */ + uint32_t unused_d04; /* 0xd04 */ + uint32_t txq0_debug; /* 0xd08 */ + uint32_t unused_d0c[(0xd18 - 0xd0c) / 4]; /* 0xd0c */ + uint32_t txq0_quantum_weight; /* 0xd18 */ + uint32_t unused_d1c[(0xd30 - 0xd1c) / 4]; /* 0xd1c */ + uint32_t rxq0_operation_mode; /* 0xd30 */ + uint32_t unused_d34; /* 0xd34 */ + uint32_t rxq0_debug; /* 0xd38 */ +}; + +struct dma_regs { + uint32_t mode; /* 0x1000 */ + uint32_t sysbus_mode; /* 0x1004 */ + uint32_t unused_1008[(0x1100 - 0x1008) / 4]; /* 0x1008 */ + uint32_t ch0_control; /* 0x1100 */ + uint32_t ch0_tx_control; /* 0x1104 */ + uint32_t ch0_rx_control; /* 0x1108 */ + uint32_t unused_110c; /* 0x110c */ + uint32_t ch0_txdesc_list_haddress; /* 0x1110 */ + uint32_t ch0_txdesc_list_address; /* 0x1114 */ + uint32_t ch0_rxdesc_list_haddress; /* 0x1118 */ + uint32_t ch0_rxdesc_list_address; /* 0x111c */ + uint32_t ch0_txdesc_tail_pointer; /* 0x1120 */ + uint32_t unused_1124; /* 0x1124 */ + uint32_t ch0_rxdesc_tail_pointer; /* 0x1128 */ + uint32_t ch0_txdesc_ring_length; /* 0x112c */ + uint32_t ch0_rxdesc_ring_length; /* 0x1130 */ +}; + +/* + * 如果缓存行大小大于描述符大小, 驱动可能会失败. 因为CPU在重排 RX 缓冲区时需要刷新缓存 + * 因此硬件写入的描述符可能会被丢弃, 具有完全 IO 一致性的架构(例如 x86)不会遇到此问题 + */ +struct dma_desc { + uint32_t des0; + uint32_t des1; + uint32_t des2; + uint32_t des3; +}; + +/* + * TX和RX描述符均为16B. 缓存行大小超过描述符大小时,会导致 CPU 上的缓存维护出现问题 + * 将会发生的情况是, 当驱动程序收到数据包时, 它将立即重新排队以供硬件重用. + * 因此, CPU 需要刷新包含该描述符的缓存行, 这将导致同一缓存行中的所有其他描述符也随之刷新. + * 如果设备已写入这些描述符之一, 则这些更改(以及关联的数据包)将丢失. + * + * 可以使用非缓存内存来解决这个问题. 如果描述符未缓存地映射,则无需手动刷新它们或使它们无效. + * 这仅适用于描述符,数据包数据缓冲区没有相同的约束. 数据包缓冲区有1536B, 因此它们不会共享缓存行 + */ +static void *alloc_dma_descs(struct uk_alloc *a) +{ + return uk_memalign(a, DMA_DESC_ALIGN, DMA_DESCS_SIZE); +} + +static void free_dma_descs(struct uk_alloc *a, void *descs) +{ + uk_free(a, descs); +} + +static inline int wait_for_ready(const void *reg, const uint32_t mask, + const bool set, unsigned timeout_ms) +{ + uint32_t val; + struct timespec ts = { + .tv_nsec = 1000 * 1000 * 1, + .tv_sec = 0 + }; + + while (timeout_ms != UINT_MAX) { + val = ioreg_read32(reg); + if (!set) + val = ~val; + + if ((val & mask) == mask) + return 0; + + nanosleep(&ts, NULL); + --timeout_ms; + } + + uk_pr_err("%s: Timeout (reg=%p mask=%x wait_set=%i)\n", __func__, reg, + mask, set); + + return -ETIMEDOUT; +} + +static int do_mdio_idle_wait(struct dwgmac_priv *dwpriv) +{ + return wait_for_ready(&dwpriv->mac_regs->mdio_address, + MAC_MDIO_ADDRESS_GB, false, 1000000); +} + +static int do_mdio_read(struct mii_dev *bus, int mdio_addr, int mdio_reg) +{ + struct dwgmac_priv *dwpriv = bus->priv; + uint32_t val; + int ret; + const struct timespec ts = { + .tv_nsec = 1000 * dwpriv->config->mdio_wait, + .tv_sec = 0 + }; + + UK_ASSERT(1000 * dwpriv->config->mdio_wait > dwpriv->config->mdio_wait); + + uk_pr_debug("%s(dev=%p, addr=%x, reg=%d):\n", __func__, dwpriv->dev, + mdio_addr, mdio_reg); + + ret = do_mdio_idle_wait(dwpriv); + if (ret) { + uk_pr_err("MDIO is busy!\n"); + return ret; + } + + val = ioreg_read32(&dwpriv->mac_regs->mdio_address); + val &= MAC_MDIO_ADDRESS_SKAP | + MAC_MDIO_ADDRESS_C45E; + val |= (mdio_addr << MAC_MDIO_ADDRESS_PA_SHIFT) + | (mdio_reg << MAC_MDIO_ADDRESS_RDA_SHIFT) + | (dwpriv->config->config_mac_mdio << MAC_MDIO_ADDRESS_CR_SHIFT) + | (MAC_MDIO_ADDRESS_GOC_READ << MAC_MDIO_ADDRESS_GOC_SHIFT) + | MAC_MDIO_ADDRESS_GB; + ioreg_write32(&dwpriv->mac_regs->mdio_address, val); + + nanosleep(&ts, NULL); + + ret = do_mdio_idle_wait(dwpriv); + if (ret) { + uk_pr_err("MDIO read didn't finish\n"); + return ret; + } + + return ioreg_read32(&dwpriv->mac_regs->mdio_data) & 0xffff; +} + +static int do_mdio_write(struct mii_dev *bus, int mdio_addr, + int mdio_reg, uint16_t mdio_val) +{ + struct dwgmac_priv *dwpriv = bus->priv; + uint32_t val; + int ret; + const struct timespec ts = { + .tv_nsec = 1000 * dwpriv->config->mdio_wait, + .tv_sec = 0 + }; + + /* 避免整数回绕 */ + UK_ASSERT(1000 * dwpriv->config->mdio_wait > dwpriv->config->mdio_wait); + + uk_pr_debug("%s(dev=%p, addr=%x, reg=%d, val=%x):\n", + __func__, dwpriv->dev, mdio_addr, mdio_reg, mdio_val); + + ret = do_mdio_idle_wait(dwpriv); + if (ret) { + uk_pr_err("MDIO not idle at entry"); + return ret; + } + + ioreg_write32(&dwpriv->mac_regs->mdio_data, mdio_val); + + val = ioreg_read32(&dwpriv->mac_regs->mdio_address); + val &= MAC_MDIO_ADDRESS_SKAP | MAC_MDIO_ADDRESS_C45E; + val |= (mdio_addr << MAC_MDIO_ADDRESS_PA_SHIFT) + | (mdio_reg << MAC_MDIO_ADDRESS_RDA_SHIFT) + | (dwpriv->config->config_mac_mdio << MAC_MDIO_ADDRESS_CR_SHIFT) + | (MAC_MDIO_ADDRESS_GOC_WRITE << MAC_MDIO_ADDRESS_GOC_SHIFT) + | MAC_MDIO_ADDRESS_GB; + + ioreg_write32(&dwpriv->mac_regs->mdio_address, val); + + nanosleep(&ts, NULL); + + return do_mdio_idle_wait(dwpriv); +} + +static int do_resources_probe_core(struct dwgmac_platdata *pdata, + struct dwgmac_priv *dwpriv) +{ + int ret; + const void *upper_bound_4gb = (void *) 0x7fffffffU; + struct uk_netdev_rx_queue *rx = &pdata->rxq; + struct uk_netdev_tx_queue *tx = &pdata->txq; + + dwpriv->descs = alloc_dma_descs(dwpriv->a); + if (!dwpriv->descs) { + uk_pr_err("%s: alloc_dma_descs() failed\n", __func__); + ret = -ENOMEM; + goto err; + } + tx->tx_descs = (struct dma_desc *)dwpriv->descs; + rx->rx_descs = (tx->tx_descs + DMA_DESCS_TX); + + /* 希望在小于4GB的空间中分配DMA buffer, 否则会产生一次CPU的搬运开销 */ + tx->tx_dma_buf = uk_memalign(dwpriv->a, DMA_BUF_ALIGN, MAX_PACKET_SIZE); + UK_ASSERT(tx->tx_dma_buf <= upper_bound_4gb); + if (!tx->tx_dma_buf) { + uk_pr_err("%s: memalign(tx_dma_buf) failed\n", __func__); + ret = -ENOMEM; + goto err_free_descs; + } + + rx->rx_dma_buf = uk_memalign(dwpriv->a, DMA_BUF_ALIGN, DMA_RX_BUF_SIZE); + UK_ASSERT(rx->rx_dma_buf <= upper_bound_4gb); + if (!rx->rx_dma_buf) { + uk_pr_err("%s: memalign(rx_dma_buf) failed\n", __func__); + ret = -ENOMEM; + goto err_free_tx_dma_buf; + } + + uk_pr_debug( + "%s: tx_descs=%p, rx_descs=%p, tx_dma_buf=%p, rx_dma_buf=%p\n", + __func__, tx->tx_descs, rx->rx_descs, tx->tx_dma_buf, + rx->rx_dma_buf); + + dwpriv->config->ops->do_dma_buf_inval(rx->rx_dma_buf, + MAX_PACKET_SIZE * DMA_DESCS_RX); + + return 0; + +err_free_tx_dma_buf: + uk_free(dwpriv->a, tx->tx_dma_buf); +err_free_descs: + free_dma_descs(dwpriv->a, dwpriv->descs); +err: + return ret; +} + +static void do_dma_resources_free(struct dwgmac_platdata *plat) +{ + struct uk_netdev_rx_queue *rx = &plat->rxq; + struct uk_netdev_tx_queue *tx = &plat->txq; + struct dwgmac_priv *dwpriv = &plat->dwpriv; + + uk_free(dwpriv->a, rx->rx_dma_buf); + uk_free(dwpriv->a, tx->tx_dma_buf); + free_dma_descs(dwpriv->a, dwpriv->descs); +} + +static int do_gmac_hwaddr_write(struct dwgmac_platdata *plat) +{ + struct dwgmac_priv *dwpriv = &plat->dwpriv; + uint32_t val; + uint8_t *e = plat->enetaddr.addr_bytes; + + /* 更新mac地址 */ + val = (e[5] << 8) | (e[4]); + ioreg_write32(&dwpriv->mac_regs->address0_high, val); + val = (e[3] << 24) | (e[2] << 16) | (e[1] << 8) | (e[0]); + ioreg_write32(&dwpriv->mac_regs->address0_low, val); + + return 0; +} + +void dwgmac_enable(struct dwgmac_platdata *pdata) +{ + struct dwgmac_priv *dwpriv = &pdata->dwpriv; + uint32_t val, tx_fifo_sz, rx_fifo_sz, tqs, rqs, pbl; + unsigned long last_rx_desc; + int i; + struct uk_netdev_tx_queue *tx = &pdata->txq; + struct uk_netdev_rx_queue *rx = &pdata->rxq; + + tx->tx_desc_idx = 0; + rx->rx_desc_idx = 0; + + /* 配置MTL */ + ioreg_write32(&dwpriv->mtl_regs->txq0_quantum_weight - 0x100, 0x60); + + /* 使能TX存储-转发模式 */ + setbits_le32(&dwpriv->mtl_regs->txq0_operation_mode, + MTL_TXQ0_OPERATION_MODE_TSF | + (MTL_TXQ0_OPERATION_MODE_TXQEN_ENABLED << + MTL_TXQ0_OPERATION_MODE_TXQEN_SHIFT)); + + /* 配置TX权重 */ + ioreg_write32(&dwpriv->mtl_regs->txq0_quantum_weight, 0x10); + + /* 当没有巨帧时, 使用RX存储-转发模式 */ + setbits_le32(&dwpriv->mtl_regs->rxq0_operation_mode, + MTL_RXQ0_OPERATION_MODE_RSF | + MTL_RXQ0_OPERATION_MODE_FEP | + MTL_RXQ0_OPERATION_MODE_FUP); + + /* 配置收发队列fifo大小; 此处我们使用单收发队列 */ + val = ioreg_read32(&dwpriv->mac_regs->hw_feature1); + tx_fifo_sz = (val >> MAC_HW_FEATURE1_TXFIFOSIZE_SHIFT) & + MAC_HW_FEATURE1_TXFIFOSIZE_MASK; + rx_fifo_sz = (val >> MAC_HW_FEATURE1_RXFIFOSIZE_SHIFT) & + MAC_HW_FEATURE1_RXFIFOSIZE_MASK; + + /* + * r/tx_fifo_sz = log2(n / 128) + * r/tqs = (n / 256) - 1 + */ + tqs = (128 << tx_fifo_sz) / 256 - 1; + rqs = (128 << rx_fifo_sz) / 256 - 1; + + clrsetbits_le32(&dwpriv->mtl_regs->txq0_operation_mode, + MTL_TXQ0_OPERATION_MODE_TQS_MASK << + MTL_TXQ0_OPERATION_MODE_TQS_SHIFT, + tqs << MTL_TXQ0_OPERATION_MODE_TQS_SHIFT); + clrsetbits_le32(&dwpriv->mtl_regs->rxq0_operation_mode, + MTL_RXQ0_OPERATION_MODE_RQS_MASK << + MTL_RXQ0_OPERATION_MODE_RQS_SHIFT, + rqs << MTL_RXQ0_OPERATION_MODE_RQS_SHIFT); + + /* 当通道fifo有4KB及以上的数据时,使能流控 */ + if (rqs >= ((4096 / 256) - 1)) { + uint32_t rfd, rfa; + + setbits_le32(&dwpriv->mtl_regs->rxq0_operation_mode, + MTL_RXQ0_OPERATION_MODE_EHFC); + + if (rqs == ((4096 / 256) - 1)) { + rfd = 0x3; /* Full-3K */ + rfa = 0x1; /* Full-1.5K */ + } else if (rqs == ((8192 / 256) - 1)) { + rfd = 0x6; /* Full-4K */ + rfa = 0xa; /* Full-6K */ + } else if (rqs == ((16384 / 256) - 1)) { + rfd = 0x6; /* Full-4K */ + rfa = 0x12; /* Full-10K */ + } else { + rfd = 0x6; /* Full-4K */ + rfa = 0x1E; /* Full-16K */ + } + + clrsetbits_le32(&dwpriv->mtl_regs->rxq0_operation_mode, + (MTL_RXQ0_OPERATION_MODE_RFD_MASK << + MTL_RXQ0_OPERATION_MODE_RFD_SHIFT) | + (MTL_RXQ0_OPERATION_MODE_RFA_MASK << + MTL_RXQ0_OPERATION_MODE_RFA_SHIFT), + (rfd << + MTL_RXQ0_OPERATION_MODE_RFD_SHIFT) | + (rfa << + MTL_RXQ0_OPERATION_MODE_RFA_SHIFT)); + } + + /* 配置GMAC */ + clrsetbits_le32(&dwpriv->mac_regs->rxq_ctrl0, + MAC_RXQ_CTRL0_RXQ0EN_MASK << + MAC_RXQ_CTRL0_RXQ0EN_SHIFT, + dwpriv->config->config_mac << + MAC_RXQ_CTRL0_RXQ0EN_SHIFT); + + clrsetbits_le32(&dwpriv->mac_regs->rxq_ctrl0, + MAC_RXQ_CTRL0_RXQ0EN_MASK << + MAC_RXQ_CTRL0_RXQ0EN_SHIFT, + 0x2 << + MAC_RXQ_CTRL0_RXQ0EN_SHIFT); + + /* 使能多播和广播队列 */ + setbits_le32(&dwpriv->mac_regs->unused_0a4, 0x00100000); + /* 使能混杂模式 */ + setbits_le32(&dwpriv->mac_regs->unused_004[1], 0x1); + + /* 设置tx流控参数 */ + /* 设置停止帧时间 */ + setbits_le32(&dwpriv->mac_regs->q0_tx_flow_ctrl, + 0xffff << MAC_Q0_TX_FLOW_CTRL_PT_SHIFT); + /* 分配tx流控优先级 */ + clrbits_le32(&dwpriv->mac_regs->txq_prty_map0, + MAC_TXQ_PRTY_MAP0_PSTQ0_MASK << + MAC_TXQ_PRTY_MAP0_PSTQ0_SHIFT); + /* 分配rx流控优先级 */ + clrbits_le32(&dwpriv->mac_regs->rxq_ctrl2, + MAC_RXQ_CTRL2_PSRQ0_MASK << + MAC_RXQ_CTRL2_PSRQ0_SHIFT); + /* 使能流控 */ + setbits_le32(&dwpriv->mac_regs->q0_tx_flow_ctrl, + MAC_Q0_TX_FLOW_CTRL_TFE); + setbits_le32(&dwpriv->mac_regs->rx_flow_ctrl, + MAC_RX_FLOW_CTRL_RFE); + + clrsetbits_le32(&dwpriv->mac_regs->configuration, + MAC_CONFIGURATION_GPSLCE | + MAC_CONFIGURATION_WD | + MAC_CONFIGURATION_JD | + MAC_CONFIGURATION_JE, + MAC_CONFIGURATION_CST | + MAC_CONFIGURATION_ACS); + + do_gmac_hwaddr_write(pdata); + + /* 配置 DMA */ + /* 使能 OSP 模式 */ + setbits_le32(&dwpriv->dma_regs->ch0_tx_control, + DMA_CH0_TX_CONTROL_OSP); + + /* 配置 RX 缓冲区大小. 必须是总线宽度的整数倍 */ + clrsetbits_le32(&dwpriv->dma_regs->ch0_rx_control, + DMA_CH0_RX_CONTROL_RBSZ_MASK << + DMA_CH0_RX_CONTROL_RBSZ_SHIFT, + MAX_PACKET_SIZE << + DMA_CH0_RX_CONTROL_RBSZ_SHIFT); + + setbits_le32(&dwpriv->dma_regs->ch0_control, + DMA_CH0_CONTROL_PBLX8); + + /* + * 突发长度必须<1/2 FIFO大小 + * tqs 中的 FIFO 大小编码为 (n / 256) - 1 + * 每个突发为 n * 8 (PBLX8) * 16 (AXI 宽度) == 128 字节 + */ + pbl = tqs + 1; + if (pbl > 32) + pbl = 32; + clrsetbits_le32(&dwpriv->dma_regs->ch0_tx_control, + DMA_CH0_TX_CONTROL_TXPBL_MASK << + DMA_CH0_TX_CONTROL_TXPBL_SHIFT, + pbl << DMA_CH0_TX_CONTROL_TXPBL_SHIFT); + + clrsetbits_le32(&dwpriv->dma_regs->ch0_rx_control, + DMA_CH0_RX_CONTROL_RXPBL_MASK << + DMA_CH0_RX_CONTROL_RXPBL_SHIFT, + 8 << DMA_CH0_RX_CONTROL_RXPBL_SHIFT); + + /* 配置DMA AXI突发长度 */ + val = (2 << DMA_SYSBUS_MODE_RD_OSR_LMT_SHIFT) | + DMA_SYSBUS_MODE_EAME | DMA_SYSBUS_MODE_BLEN16 | + DMA_SYSBUS_MODE_BLEN8 | DMA_SYSBUS_MODE_BLEN4; + ioreg_write32(&dwpriv->dma_regs->sysbus_mode, val); + + /* 初始化dma描述符 */ + memset(dwpriv->descs, 0, DMA_DESCS_SIZE); + for (i = 0; i < DMA_DESCS_RX; i++) { + struct dma_desc *rx_desc = &(rx->rx_descs[i]); + rx_desc->des0 = + (uint32_t)(unsigned long)(rx->rx_dma_buf + + (i * MAX_PACKET_SIZE)); + rx_desc->des3 = DESC3_OWN | DESC3_BUF1V; + mb(); + dwpriv->config->ops->do_dma_desc_flush(rx_desc); + dwpriv->config->ops->do_dma_buf_inval( + rx->rx_dma_buf + (i * MAX_PACKET_SIZE), + MAX_PACKET_SIZE); + } + + ioreg_write32(&dwpriv->dma_regs->ch0_txdesc_list_haddress, 0); + ioreg_write32(&dwpriv->dma_regs->ch0_txdesc_list_address, + (unsigned long)tx->tx_descs); + ioreg_write32(&dwpriv->dma_regs->ch0_txdesc_ring_length, DMA_DESCS_TX - 1); + + ioreg_write32(&dwpriv->dma_regs->ch0_rxdesc_list_haddress, 0); + ioreg_write32(&dwpriv->dma_regs->ch0_rxdesc_list_address, + (unsigned long)rx->rx_descs); + ioreg_write32(&dwpriv->dma_regs->ch0_rxdesc_ring_length, + DMA_DESCS_RX - 1); + + setbits_le32(&dwpriv->dma_regs->ch0_tx_control, + DMA_CH0_TX_CONTROL_ST); + setbits_le32(&dwpriv->dma_regs->ch0_rx_control, + DMA_CH0_RX_CONTROL_SR); + setbits_le32(&dwpriv->mac_regs->configuration, + MAC_CONFIGURATION_TE | MAC_CONFIGURATION_RE); + + /* 要发送数据之前, TX tail pointer不会写入DMA控制器 */ + + /* rx tail pointer指向最后一个描述符,意味着所有描述符都处于可用的状态 */ + last_rx_desc = (unsigned long)&(rx->rx_descs[(DMA_DESCS_RX - 1)]); + ioreg_write32(&dwpriv->dma_regs->ch0_rxdesc_tail_pointer, last_rx_desc); + + dwpriv->started = true; +} + +static void set_gmii_speed(struct dwgmac_priv *dwpriv) +{ + clrbits_le32(&dwpriv->mac_regs->configuration, + MAC_CONFIGURATION_PS | MAC_CONFIGURATION_FES); +} + +static void set_mii_speed_100(struct dwgmac_priv *dwpriv) +{ + setbits_le32(&dwpriv->mac_regs->configuration, + MAC_CONFIGURATION_PS | MAC_CONFIGURATION_FES); +} + +static int do_gmac_link_adjust(struct dwgmac_platdata *pdata) +{ + struct dwgmac_priv *dwpriv = &pdata->dwpriv; + + if (dwpriv->phy->duplex_full) { + setbits_le32(&dwpriv->mac_regs->configuration, MAC_CONFIGURATION_DM); + } else { + clrbits_le32(&dwpriv->mac_regs->configuration, MAC_CONFIGURATION_DM); + setbits_le32(&dwpriv->mtl_regs->txq0_operation_mode, + MTL_TXQ0_OPERATION_MODE_FTQ); + } + + switch (dwpriv->phy->speed) { + case 1000: + set_gmii_speed(dwpriv); + break; + case 100: + set_mii_speed_100(dwpriv); + break; + default: + uk_pr_err("invalid speed %d", dwpriv->phy->speed); + return -EINVAL; + } + + return 0; +} + +int dwgmac_probe(struct pf_device *dev, struct dwgmac_platdata *pdata) +{ + int ret; + struct dwgmac_priv *dwpriv = &pdata->dwpriv; + + dwpriv->dev = dev; + dwpriv->mac_regs = (void *)(dwpriv->regs + MAC_REGS_BASE); + dwpriv->mtl_regs = (void *)(dwpriv->regs + MTL_REGS_BASE); + dwpriv->dma_regs = (void *)(dwpriv->regs + DMA_REGS_BASE); + + ret = do_resources_probe_core(pdata, dwpriv); + if (ret < 0) { + uk_pr_err("do_resources_probe_core() failed: %d", ret); + return ret; + } + + ret = dwpriv->config->ops->do_resources_probe(dev); + if (ret < 0) { + uk_pr_err("do_resources_probe() failed: %d", ret); + goto err_remove_resources_core; + } + + /* eth_phy_get_mdio_bus不会获取到,返回NULL */ + dwpriv->mii = NULL; + if (!dwpriv->mii) { + dwpriv->mii = mdio_alloc(dwpriv->a); + if (!dwpriv->mii) { + uk_pr_err("mdio_alloc() failed"); + ret = -ENOMEM; + goto err_remove_resources_core; + } + dwpriv->mii->read = do_mdio_read; + dwpriv->mii->write = do_mdio_write; + dwpriv->mii->priv = dwpriv; + + ret = mdio_register(dwpriv->mii); + if (ret < 0) { + uk_pr_err("mdio_register() failed: %d", ret); + goto err_free_mdio; + } + } + + return 0; + +err_free_mdio: + mdio_free(dwpriv->a, dwpriv->mii); +err_remove_resources_core: + do_dma_resources_free(pdata); + return ret; +} + +static phyif_t do_phyif_get(struct pf_device *pfdev) +{ + phyif_t interface = PHY_IF_MODE_NONE; + + int ret, offs = pfdev->fdt_offset; + const void *dtb = (const void *)ukplat_bootinfo_get()->dtb; + const fdt32_t *fdtp; + + fdtp = fdt_getprop(dtb, offs, "phy-mode", &ret); + if (fdtp && ret > 0 && !strncmp((const char *) fdtp, "rgmii", ret)) + return PHY_IF_MODE_RGMII; + else + UK_CRASH("Unsupported phy-mode: %s %d\n", + (const char *) fdtp, ret); + + return interface; +} + +int eth_phy_get_addr(struct pf_device *dev) +{ + const void *dtb = (const void *)ukplat_bootinfo_get()->dtb; + const fdt32_t *fdtp; + int ret; + + fdtp = fdt_getprop(dtb, dev->fdt_offset, "phy-handle", &ret); + UK_ASSERT(!PTRISERR(fdtp) && ret > 0); + ret = fdt32_to_cpu(fdtp[0]); + + ret = fdt_node_offset_by_phandle(dtb, ret); + UK_ASSERT(ret > 0); + + fdtp = fdt_getprop(dtb, ret, "reg", &ret); + return PTRISERR(fdtp) ? 0 : fdt32_to_cpu(fdtp[0]); +} + +int dwgmac_init(struct dwgmac_platdata *pdata) +{ + int ret = 0, limit = 10; + unsigned long rate; + uint32_t val; + struct dwgmac_priv *dwpriv = &pdata->dwpriv; + struct timespec ts = { + .tv_nsec = 10 * 1000, /* 10us */ + .tv_sec = 0 + }; + + if (!dwpriv->mii_reseted) { + ret = platops->do_phy_hw_reset(pdata); + if (ret < 0) { + uk_pr_err("do_phy_hw_reset() failed: %d\n", ret); + goto err; + } + + dwpriv->mii_reseted = true; + nanosleep(&ts, NULL); + } + + dwpriv->reg_access_ok = true; + + /* + * DMA SW reset + * + * 注1: 软复位后,必须等待4个CSR周期后才能读或操作寄存器 + * 不可以立即去读或操作,否则可能会导致不可预知的结果 + * 具体原因设计到总线协议,状态更新需要时间 + */ + val = ioreg_read32(&dwpriv->dma_regs->mode); + val |= DMA_MODE_SWR; + ioreg_write32(&dwpriv->dma_regs->mode, val); + ts.tv_nsec = 1000 * 1000 * 20; /* 20ms */ + do { + nanosleep(&ts, NULL); + + if (!(ioreg_read32(&dwpriv->dma_regs->mode) & DMA_MODE_SWR)) { + nanosleep(&ts, NULL); + break; + } + } while (limit--); + if (limit < 0) { + uk_pr_err("DMA_MODE_SWR stuck\n"); + goto err_stop_resets; + } + + rate = platops->do_src_clk_get(pdata); + val = (rate / 1000000) - 1; + ioreg_write32(&dwpriv->mac_regs->us_tic_counter, val); + + if (!dwpriv->phy) { + int addr = eth_phy_get_addr(pdata->pfdev); + + dwpriv->phy = + phy_connect(dwpriv->mii, addr, pdata->pfdev, + dwpriv->config->ops->do_phyif_get(pdata->pfdev)); + if (!dwpriv->phy) { + uk_pr_err("phy_connect() failed\n"); + goto err_stop_resets; + } + + if (dwpriv->max_speed) { + ret = phy_set_supported(dwpriv->phy, dwpriv->max_speed); + if (ret) { + uk_pr_err("phy_set_supported() failed: %d\n", ret); + goto err_shutdown_phy; + } + } + + ret = phy_config(dwpriv->phy); + if (ret < 0) { + uk_pr_err("phy_config() failed: %d\n", ret); + goto err_shutdown_phy; + } + } + + ret = phy_startup(dwpriv->phy); + if (ret < 0) { + uk_pr_err("phy_startup() failed: %d\n", ret); + goto err_shutdown_phy; + } + + if (!dwpriv->phy->link) { + uk_pr_err("No link\n"); + goto err_shutdown_phy; + } + + ret = do_gmac_link_adjust(pdata); + if (ret < 0) { + uk_pr_err("do_gmac_link_adjust() failed: %d\n", ret); + goto err_shutdown_phy; + } + + return 0; + +err_shutdown_phy: + phy_shutdown(dwpriv->phy); +err_stop_resets: + platops->do_phy_hw_reset_stop(pdata); + dwpriv->mii_reseted = false; +err: + uk_pr_err("FAILED: %d\n", ret); + return ret; +} + +static void do_dma_desc_inval(void *desc) +{ + unsigned long start = rounddown((unsigned long)desc, ARCH_DMA_MINALIGN); + unsigned long end = roundup((unsigned long)desc + DMA_DESC_SIZE, + ARCH_DMA_MINALIGN); + invalidate_dcache_range(start, end - start); +} + + +static void do_dma_desc_flush(void *desc) +{ + unsigned long start = rounddown((unsigned long)desc, ARCH_DMA_MINALIGN); + unsigned long end = roundup((unsigned long)desc + DMA_DESC_SIZE, + ARCH_DMA_MINALIGN); + clean_and_invalidate_dcache_range(start, end - start); +} + +static void do_dma_buf_inval(void *buf, size_t size) +{ + unsigned long start = rounddown((unsigned long)buf, ARCH_DMA_MINALIGN); + unsigned long end = roundup((unsigned long)buf + size, ARCH_DMA_MINALIGN); + invalidate_dcache_range(start, end - start); +} + +static void do_dma_buf_flush(void *buf, size_t size) +{ + unsigned long start = rounddown((unsigned long)buf, ARCH_DMA_MINALIGN); + unsigned long end = roundup((unsigned long)buf + size, ARCH_DMA_MINALIGN); + clean_and_invalidate_dcache_range(start, end - start); +} + + +static int do_resources_probe(struct pf_device *dev) +{ + struct dwgmac_platdata *pdata = pfdev_get_platdata(dev); + struct dwgmac_priv *dwpriv = &pdata->dwpriv; + int ret, offs = dev->fdt_offset; + void *dtb = (void *) ukplat_bootinfo_get()->dtb; + const fdt32_t *v; + + UK_ASSERT(offs >= 0); + + v = fdt_getprop(dtb, offs, "phy-mode", &ret); + if (!v || ret <= 0 || strncmp((const char *) v, "rgmii", ret) != 0) + return -EINVAL; + + v = fdt_getprop(dtb, offs, "max-speed", &ret); + dwpriv->max_speed = ret < 0 ? 0 : fdt32_to_cpu(v[0]); + + return 0; +} + +struct dwgmac_ops dwgmac_ops = { + .do_dma_desc_inval = do_dma_desc_inval, + .do_dma_desc_flush = do_dma_desc_flush, + .do_dma_buf_inval = do_dma_buf_inval, + .do_dma_buf_flush = do_dma_buf_flush, + .do_resources_probe = do_resources_probe, + .do_phyif_get = do_phyif_get +}; + +static int ndo_tx(struct uk_netdev *dev, + struct uk_netdev_tx_queue *tx, + struct uk_netbuf *pkt) +{ + struct dwgmac_platdata *pdata = netdev_get_platdata(dev); + struct dwgmac_priv *dwpriv = &pdata->dwpriv; + struct dma_desc *tx_desc; + int i; + struct timespec ts = { + .tv_nsec = 1000 * 1, /* 1us */ + .tv_sec = 0 + }; + + uk_pr_debug("%s(dev=%p, packet=%p, length=%d):\n", __func__, dev, + pkt->data, pkt->len); + + memcpy(tx->tx_dma_buf, pkt->data, pkt->len); + dwpriv->config->ops->do_dma_buf_flush(tx->tx_dma_buf, pkt->len); + + tx_desc = &(tx->tx_descs[tx->tx_desc_idx++]); + tx->tx_desc_idx %= DMA_DESCS_TX; + + tx_desc->des0 = (unsigned long)tx->tx_dma_buf; + tx_desc->des1 = 0; + tx_desc->des2 = pkt->len; + + mb(); + tx_desc->des3 = DESC3_OWN | DESC3_FD | DESC3_LD | pkt->len; + dwpriv->config->ops->do_dma_desc_flush(tx_desc); + + ioreg_write32(&dwpriv->dma_regs->ch0_txdesc_tail_pointer, + (unsigned long)(&(tx->tx_descs[tx->tx_desc_idx]))); + + for (i = 0; i < 1000000; i++) { + dwpriv->config->ops->do_dma_desc_inval(tx_desc); + if (!(ioreg_read32(&tx_desc->des3) & DESC3_OWN)) { + uk_pr_debug("%s(dev=%p, packet=%p, length=%d,tx_desc_idx=%d):\n", + __func__, dev, + pkt->data, pkt->len, tx->tx_desc_idx); + return UK_NETDEV_STATUS_SUCCESS; + } + nanosleep(&ts, NULL); + } + + uk_pr_warn("%s: eth TX timeout, tx desc3=0x%x\n", __func__, + ioreg_read32(&tx_desc->des3)); + + return UK_NETDEV_STATUS_UNDERRUN; +} + +static void do_pkt_free(struct dwgmac_priv *dwpriv, struct uk_netdev_rx_queue *rx, + struct uk_netbuf *nbuf) +{ + uint8_t *packet = nbuf->data; + struct dma_desc *rx_desc; + int length = nbuf->len; + + dwpriv->config->ops->do_dma_buf_inval(packet, length); + + rx_desc = &(rx->rx_descs[rx->rx_desc_idx]); + rx_desc->des0 = 0; + mb(); + dwpriv->config->ops->do_dma_desc_flush(rx_desc); + dwpriv->config->ops->do_dma_buf_inval(packet, length); + rx_desc->des0 = (uint32_t)(unsigned long)packet; + rx_desc->des1 = 0; + rx_desc->des2 = 0; + + mb(); + rx_desc->des3 = DESC3_OWN | DESC3_BUF1V; + dwpriv->config->ops->do_dma_desc_flush(rx_desc); + + ioreg_write32(&dwpriv->dma_regs->ch0_rxdesc_tail_pointer, (unsigned long)rx_desc); + + ++rx->rx_desc_idx; + rx->rx_desc_idx %= DMA_DESCS_RX; + + uk_pr_debug("%s: ch0_rxdesc_tail_pointer=%p, rx_desc_idx=%d\n", + __func__, rx_desc, rx->rx_desc_idx); +} + +static int do_recv(struct dwgmac_priv *dwpriv, struct uk_netdev_rx_queue *rx, + struct uk_netbuf **pkt, struct dma_desc *rx_desc) +{ + int length; + + *pkt = rx->nb[0]; + (*pkt)->data = rx->rx_dma_buf + (rx->rx_desc_idx * MAX_PACKET_SIZE); + length = rx_desc->des3 & 0x7fff; + (*pkt)->len = length; + dwpriv->config->ops->do_dma_buf_inval((*pkt)->data, length); + + uk_pr_debug("[%s]: *packetp=%p, length=%d, rx_desc_idx=%d, " + "rx_dma_buf[rx_desc_idx]=%p\n", + __func__, (*pkt)->data, length, rx->rx_desc_idx, + rx->rx_dma_buf + (rx->rx_desc_idx * MAX_PACKET_SIZE)); + + return length; +} + +static int ndo_rx(struct uk_netdev *dev, struct uk_netdev_rx_queue *rx, + struct uk_netbuf **pkt) +{ + int err , status = UK_NETDEV_STATUS_SUCCESS; + struct dwgmac_platdata *pdata = netdev_get_platdata(dev); + struct dwgmac_priv *dwpriv = &pdata->dwpriv; + + if (rx->flag & RXQ_FLAG_INTR_EN) { + /* TODO intr-poll mode */ + } else { + /* poll only mode */ + struct dma_desc *rx_desc = + &(rx->rx_descs[(rx->rx_desc_idx + 1) % DMA_DESCS_RX]); + dwpriv->config->ops->do_dma_desc_inval(rx_desc); + + if (rx_desc->des3 & DESC3_OWN) { + /* dma描述符未ready, 等待下次轮询 */ + return 0; + } + + rx->alloc_rxpkts(rx->alloc_rxpkts_argp, pdata->rxq.nb, 1); + if (rx->flag & RXQ_FLAG_NEED_FREE_PKT) + do_pkt_free(dwpriv, rx, pdata->rxq.nb[0]); + else + rx->flag |= RXQ_FLAG_NEED_FREE_PKT; + + + err = do_recv(dwpriv, rx, pkt, rx_desc); + if (err > 0) + uk_netdev_drv_rx_event(dev, rx->qid); + + /* 检查下一个描述符是否ready? */ + rx_desc = + &(rx->rx_descs[(rx->rx_desc_idx + 1) % DMA_DESCS_RX]); + if (!(rx_desc->des3 & DESC3_OWN)) + status |= UK_NETDEV_STATUS_MORE; + } + + return status; +} + +void dwgmac_register_netdev_ops(struct uk_netdev *ndev) +{ + ndev->tx_one = ndo_tx; + ndev->rx_one = ndo_rx; +} \ No newline at end of file diff --git a/drivers/net/mac/include/net/macdrv/bitops.h b/drivers/net/mac/include/net/macdrv/bitops.h new file mode 100755 index 0000000000000000000000000000000000000000..f739006d0e464083ede17b82d0e5c6e5c2526001 --- /dev/null +++ b/drivers/net/mac/include/net/macdrv/bitops.h @@ -0,0 +1,70 @@ +/* Copyright 2024 Hangzhou Yingyi Technology Co., Ltd + * + * 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 ssANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef __BITOPS_RK3568__ +#define __BITOPS_RK3568__ + +#include +#include +#include +#include +#include +#include + +#include + +#define rounddown(x, y) \ + ({ \ + typeof(x) __x = (x); \ + __x - (__x % (y)); \ + }) + + + +/* ffs: bitset中查找第一个bit + * TODO : uk的bitops.h中有ffs函数,但是无法直接include + */ +static inline int generic_ffs(int x) +{ + int r = 1; + + if (!x) + return 0; + if (!(x & 0xffff)) { + x >>= 16; + r += 16; + } + if (!(x & 0xff)) { + x >>= 8; + r += 8; + } + if (!(x & 0xf)) { + x >>= 4; + r += 4; + } + if (!(x & 3)) { + x >>= 2; + r += 2; + } + if (!(x & 1)) { + x >>= 1; + r += 1; + } + return r; +} + +#define ffs generic_ffs + +#endif \ No newline at end of file diff --git a/drivers/net/mac/include/net/macdrv/dwgmac.h b/drivers/net/mac/include/net/macdrv/dwgmac.h new file mode 100644 index 0000000000000000000000000000000000000000..9e84e186e11b4465f2bad0216e18cd3110609c25 --- /dev/null +++ b/drivers/net/mac/include/net/macdrv/dwgmac.h @@ -0,0 +1,298 @@ +/* Copyright 2024 Hangzhou Yingyi Technology Co., Ltd + * + * 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 ssANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#ifndef _DWGMAC_H_ +#define _DWGMAC_H_ + +#include +#include + +#include +#include +#include +#include +#include + +#include +#include + +/* CLOCK MACRO DEFINITION */ +#define GMAC_RGMII_CLK_RATE (125000000) +#define GMAC_RMII_CLK_RATE (50000000) +#define GMAC_100M_RX_TX_CLK_RATE (25000000) +#define GMAC_1000M_RX_TX_CLK_RATE (125000000) + +/* GMAC MACRO DEFINITION */ +#define MAC_REGS_BASE 0x000 +#define MAC_CONFIGURATION_GPSLCE UK_BIT(23) +#define MAC_CONFIGURATION_CST UK_BIT(21) +#define MAC_CONFIGURATION_ACS UK_BIT(20) +#define MAC_CONFIGURATION_WD UK_BIT(19) +#define MAC_CONFIGURATION_JD UK_BIT(17) +#define MAC_CONFIGURATION_JE UK_BIT(16) +#define MAC_CONFIGURATION_PS UK_BIT(15) +#define MAC_CONFIGURATION_FES UK_BIT(14) +#define MAC_CONFIGURATION_DM UK_BIT(13) +#define MAC_CONFIGURATION_TE UK_BIT(1) +#define MAC_CONFIGURATION_RE UK_BIT(0) + +#define MAC_Q0_TX_FLOW_CTRL_PT_SHIFT 16 +#define MAC_Q0_TX_FLOW_CTRL_TFE UK_BIT(1) + +#define MAC_RX_FLOW_CTRL_RFE UK_BIT(0) + +#define MAC_TXQ_PRTY_MAP0_PSTQ0_SHIFT 0 +#define MAC_TXQ_PRTY_MAP0_PSTQ0_MASK 0xff + +#define MAC_RXQ_CTRL0_RXQ0EN_SHIFT 0 +#define MAC_RXQ_CTRL0_RXQ0EN_MASK 3 + +#define MAC_RXQ_CTRL2_PSRQ0_SHIFT 0 +#define MAC_RXQ_CTRL2_PSRQ0_MASK 0xff + +#define MAC_HW_FEATURE1_TXFIFOSIZE_SHIFT 6 +#define MAC_HW_FEATURE1_TXFIFOSIZE_MASK 0x1f +#define MAC_HW_FEATURE1_RXFIFOSIZE_SHIFT 0 +#define MAC_HW_FEATURE1_RXFIFOSIZE_MASK 0x1f + +#define MAC_MDIO_ADDRESS_PA_SHIFT 21 +#define MAC_MDIO_ADDRESS_RDA_SHIFT 16 +#define MAC_MDIO_ADDRESS_CR_SHIFT 8 +#define MAC_MDIO_ADDRESS_SKAP UK_BIT(4) +#define MAC_MDIO_ADDRESS_GOC_SHIFT 2 +#define MAC_MDIO_ADDRESS_GOC_READ 3 +#define MAC_MDIO_ADDRESS_GOC_WRITE 1 +#define MAC_MDIO_ADDRESS_C45E UK_BIT(1) +#define MAC_MDIO_ADDRESS_GB UK_BIT(0) + +#define MAC_RXQ_CTRL0_RXQ0EN_NOT_ENABLED 0 +#define MAC_MDIO_ADDRESS_CR_100_150 1 + + +/* MTL MACRO DEFINITION */ +#define MTL_REGS_BASE 0xd00 + +#define MTL_TXQ0_OPERATION_MODE_TQS_SHIFT 16 +#define MTL_TXQ0_OPERATION_MODE_TQS_MASK 0x1ff +#define MTL_TXQ0_OPERATION_MODE_TXQEN_SHIFT 2 +#define MTL_TXQ0_OPERATION_MODE_TXQEN_ENABLED 2 +#define MTL_TXQ0_OPERATION_MODE_TSF UK_BIT(1) +#define MTL_TXQ0_OPERATION_MODE_FTQ UK_BIT(0) + +#define MTL_RXQ0_OPERATION_MODE_RQS_SHIFT 20 +#define MTL_RXQ0_OPERATION_MODE_RQS_MASK 0x3ff +#define MTL_RXQ0_OPERATION_MODE_RFD_SHIFT 14 +#define MTL_RXQ0_OPERATION_MODE_RFD_MASK 0x3f +#define MTL_RXQ0_OPERATION_MODE_RFA_SHIFT 8 +#define MTL_RXQ0_OPERATION_MODE_RFA_MASK 0x3f +#define MTL_RXQ0_OPERATION_MODE_EHFC UK_BIT(7) +#define MTL_RXQ0_OPERATION_MODE_RSF UK_BIT(5) +#define MTL_RXQ0_OPERATION_MODE_FEP UK_BIT(4) +#define MTL_RXQ0_OPERATION_MODE_FUP UK_BIT(3) + + +/* DMA MACRO DEFINITION */ +#define DMA_REGS_BASE 0x1000 + +#define DMA_MODE_SWR UK_BIT(0) +#define DMA_SYSBUS_MODE_RD_OSR_LMT_SHIFT 16 +#define DMA_SYSBUS_MODE_EAME UK_BIT(11) +#define DMA_SYSBUS_MODE_BLEN16 UK_BIT(3) +#define DMA_SYSBUS_MODE_BLEN8 UK_BIT(2) +#define DMA_SYSBUS_MODE_BLEN4 UK_BIT(1) + +#define DMA_CH0_CONTROL_PBLX8 UK_BIT(16) +#define DMA_CH0_TX_CONTROL_TXPBL_SHIFT 16 +#define DMA_CH0_TX_CONTROL_TXPBL_MASK 0x3f +#define DMA_CH0_TX_CONTROL_OSP UK_BIT(4) +#define DMA_CH0_TX_CONTROL_ST UK_BIT(0) + +#define DMA_CH0_RX_CONTROL_RXPBL_SHIFT 16 +#define DMA_CH0_RX_CONTROL_RXPBL_MASK 0x3f +#define DMA_CH0_RX_CONTROL_RBSZ_SHIFT 1 +#define DMA_CH0_RX_CONTROL_RBSZ_MASK 0x3fff +#define DMA_CH0_RX_CONTROL_SR UK_BIT(0) + + +/* DMA DESCRIPTORS MACRO DEFINITION */ +#define ARCH_DMA_MINALIGN (64) /* Arm64 dma minimum align is 64 */ + +/* 编译期检查DMA对齐必须是2的次方 */ +#if ((ARCH_DMA_MINALIGN == 0) || ((ARCH_DMA_MINALIGN & (ARCH_DMA_MINALIGN - 1)) != 0)) +#warning ARCH_DMA_MINALIGN must be a power of 2 +#endif + +#define DMA_DESC_WORDS 4 +#define DMA_DESC_SIZE (DMA_DESC_WORDS * 4) + +#define DMA_DESC_ALIGN ARCH_DMA_MINALIGN /* ARCH_DMA_MINALIGN必须>= 16B */ +#define DMA_DESCS_TX 512 +#define DMA_DESCS_RX 512 +#define DMA_DESCS_NUM (DMA_DESCS_TX + DMA_DESCS_RX) +#define DMA_DESCS_SIZE ALIGN_UP(DMA_DESCS_NUM *DMA_DESC_SIZE, ARCH_DMA_MINALIGN) +#define DMA_BUF_ALIGN ARCH_DMA_MINALIGN +#define MAX_PACKET_SIZE ALIGN_UP(1568, ARCH_DMA_MINALIGN) +#define DMA_RX_BUF_SIZE (DMA_DESCS_RX * MAX_PACKET_SIZE) + +#define DESC3_OWN UK_BIT(31) +#define DESC3_FD UK_BIT(29) +#define DESC3_LD UK_BIT(28) +#define DESC3_BUF1V UK_BIT(24) + +struct uk_netdev_rx_queue { + struct uk_alloc *a; + uk_netdev_alloc_rxpkts alloc_rxpkts; + void *alloc_rxpkts_argp; + struct uk_netbuf *nb[1]; + struct dma_desc *rx_descs; + void *rx_dma_buf; + + int rx_desc_idx; + uint16_t qid; + +#define RXQ_FLAG_INTR_EN UK_BIT(0) /* uk rxq 中断-轮询模式标志位 */ +#define RXQ_FLAG_NEED_FREE_PKT UK_BIT(1) /* 是否需要把pkt的空间进行释放 */ + uint16_t flag; +}; + +struct uk_netdev_tx_queue { + struct uk_alloc *a; + struct dma_desc *tx_descs; + void *tx_dma_buf; + + int tx_desc_idx; + uint16_t qid; +}; + +struct mac_regs { + uint32_t configuration; /* 0x000 */ + uint32_t unused_004[(0x070 - 0x004) / 4]; /* 0x004 */ + uint32_t q0_tx_flow_ctrl; /* 0x070 */ + uint32_t unused_070[(0x090 - 0x074) / 4]; /* 0x074 */ + uint32_t rx_flow_ctrl; /* 0x090 */ + uint32_t unused_094; /* 0x094 */ + uint32_t txq_prty_map0; /* 0x098 */ + uint32_t unused_09c; /* 0x09c */ + uint32_t rxq_ctrl0; /* 0x0a0 */ + uint32_t unused_0a4; /* 0x0a4 */ + uint32_t rxq_ctrl2; /* 0x0a8 */ + uint32_t unused_0ac[(0x0dc - 0x0ac) / 4]; /* 0x0ac */ + uint32_t us_tic_counter; /* 0x0dc */ + uint32_t unused_0e0[(0x11c - 0x0e0) / 4]; /* 0x0e0 */ + uint32_t hw_feature0; /* 0x11c */ + uint32_t hw_feature1; /* 0x120 */ + uint32_t hw_feature2; /* 0x124 */ + uint32_t unused_128[(0x200 - 0x128) / 4]; /* 0x128 */ + uint32_t mdio_address; /* 0x200 */ + uint32_t mdio_data; /* 0x204 */ + uint32_t unused_208[(0x300 - 0x208) / 4]; /* 0x208 */ + uint32_t address0_high; /* 0x300 */ + uint32_t address0_low; /* 0x304 */ +}; + +struct dwgmac_priv { + struct pf_device *dev; + struct dwgmac_config *config; + struct mac_regs *mac_regs; + struct mtl_regs *mtl_regs; + struct dma_regs *dma_regs; + + struct mii_dev *mii; + struct phy_device *phy; + void *descs; + struct uk_alloc *a; + + uint32_t max_speed; + unsigned long regs; + bool started; + bool reg_access_ok; + bool mii_reseted; +}; + +extern struct dwgmac_platform_ops *platops; + +struct dwgmac_platdata { + UK_SLIST_ENTRY(struct dwgmac_platdata) next; + struct pf_device *pfdev; + struct uk_netdev *netdev; + void *plat_priv; + + struct uk_hwaddr enetaddr; + bool integrated_phy; + bool clock_input; + int max_speed; + int phyif; + int tx_delay; + int rx_delay; + unsigned id; /* id同时表示phy id / mac id / phy addr */ + + struct uk_netdev_rx_queue rxq; + struct uk_netdev_tx_queue txq; + struct dwgmac_priv dwpriv; +}; + +/* + * dwgmac_platform_ops - 平台相关接口 + * 若无特殊说明,返回值统一失败: <0; 成功: 0; + * + * @do_compatible_drvname_get: 获取平台驱动名 + * @do_plat_init: 平台初始化, 完成私有数据的初始化和dts解析操作 + * @do_clk_tx_rx_set: tx/rx的时钟设置 + * @do_clk_src_set: gmac时钟源设置 + * @do_rgmii_speed_set: rgmii速率设置 + * @do_rgmii_set: 设置rgmii模式 + * @do_src_clk_get: 获取时钟频率Mhz + * 失败: <0; 成功: 时钟频率 + */ +struct dwgmac_platform_ops { + const char *(*do_compatible_drvname_get)(void); + int (*do_plat_init)(struct dwgmac_platdata *pdata); + int (*do_clk_tx_rx_set)(struct dwgmac_platdata *pdata, + unsigned long rate); + int (*do_mac_speed_fix)(struct dwgmac_platdata *pdata); + int (*do_mii_mode_set)(struct dwgmac_platdata *pdata); + int (*do_clks_set_default)(struct dwgmac_platdata *pdata); + int (*do_src_clk_get)(struct dwgmac_platdata *pdata); + int (*do_phy_hw_reset)(struct dwgmac_platdata *pdata); + int (*do_phy_hw_reset_stop)(struct dwgmac_platdata *pdata); +}; + +struct dwgmac_ops { + void (*do_dma_desc_inval)(void *desc); + void (*do_dma_desc_flush)(void *desc); + void (*do_dma_buf_inval)(void *buf, size_t size); + void (*do_dma_buf_flush)(void *buf, size_t size); + int (*do_resources_probe)(struct pf_device *dev); + phyif_t (*do_phyif_get)(struct pf_device *dev); + +}; + +struct dwgmac_config { + int mdio_wait; + int swr_wait; + int config_mac; + int config_mac_mdio; + struct dwgmac_ops *ops; +}; + +int dwgmac_init(struct dwgmac_platdata *pdata); +void dwgmac_enable(struct dwgmac_platdata *pdata); +int dwgmac_probe(struct pf_device *dev, struct dwgmac_platdata *pdata); +struct dwgmac_platdata *pfdev_get_platdata(const struct pf_device *pfdev); +void dwgmac_register_netdev_ops(struct uk_netdev *ndev); +struct dwgmac_platdata *netdev_get_platdata(const struct uk_netdev *ndev); + +extern struct dwgmac_ops dwgmac_ops; + +#endif diff --git a/drivers/net/mac/include/net/macdrv/mii.h b/drivers/net/mac/include/net/macdrv/mii.h new file mode 100644 index 0000000000000000000000000000000000000000..528ac3493c21c597e214fc6a9d65a1e8932aff3a --- /dev/null +++ b/drivers/net/mac/include/net/macdrv/mii.h @@ -0,0 +1,82 @@ +/* Copyright 2024 Hangzhou Yingyi Technology Co., Ltd + * + * 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 ssANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef __MII_H__ +#define __MII_H__ + +#include + +/* Generic MII registers. */ +#define MII_BMCR 0x00 +#define MII_BMSR 0x01 +#define MII_PHYSID1 0x02 +#define MII_PHYSID2 0x03 +#define MII_ADVERTISE 0x04 +#define MII_LPA 0x05 +#define MII_CTRL1000 0x09 +#define MII_STAT1000 0x0a +#define MII_ESTATUS 0x0f +#define MII_EXTENDED_ADDR 0x1e +#define MII_EXTENDED_DATA 0x1f + +/* Basic mode control register. */ +#define BMCR_SPEED1000 0x0040 +#define BMCR_FULLDPLX 0x0100 +#define BMCR_ANRESTART 0x0200 +#define BMCR_ISOLATE 0x0400 +#define BMCR_ANENABLE 0x1000 +#define BMCR_SPEED100 0x2000 +#define BMCR_RESET 0x8000 + +/* Basic mode status */ +#define BMSR_ERCAP 0x0001 +#define BMSR_LSTATUS 0x0004 +#define BMSR_ANEGCAPABLE 0x0008 +#define BMSR_ANEGCOMPLETE 0x0020 +#define BMSR_ESTATEN 0x0100 +#define BMSR_10HALF 0x0800 +#define BMSR_10FULL 0x1000 +#define BMSR_100HALF 0x2000 +#define BMSR_100FULL 0x4000 + +/* Advertisement control register. */ +#define ADVERTISE_10HALF 0x0020 +#define ADVERTISE_1000XFULL 0x0020 +#define ADVERTISE_10FULL 0x0040 +#define ADVERTISE_1000XHALF 0x0040 +#define ADVERTISE_100HALF 0x0080 +#define ADVERTISE_100FULL 0x0100 +#define ADVERTISE_100BASE4 0x0200 +#define ADVERTISE_PAUSE_CAP 0x0400 +#define ADVERTISE_PAUSE_ASYM 0x0800 + +#define ADVERTISE_ALL (ADVERTISE_10HALF | ADVERTISE_10FULL | \ + ADVERTISE_100HALF | ADVERTISE_100FULL) + +/* Link partner ability. */ +#define LPA_10FULL 0x0040 +#define LPA_100HALF 0x0080 +#define LPA_100FULL 0x0100 + +#define ESTATUS_1000_XFULL 0x8000 +#define ESTATUS_1000_XHALF 0x4000 +#define ESTATUS_1000_TFULL 0x2000 +#define ESTATUS_1000_THALF 0x1000 + +#define ADVERTISE_1000FULL 0x0200 +#define ADVERTISE_1000HALF 0x0100 + + +#endif /* __MII_H__ */ diff --git a/drivers/net/mac/include/net/macdrv/phy.h b/drivers/net/mac/include/net/macdrv/phy.h new file mode 100644 index 0000000000000000000000000000000000000000..2e00211ec2933439ac6d1e408873524c862fbc9c --- /dev/null +++ b/drivers/net/mac/include/net/macdrv/phy.h @@ -0,0 +1,157 @@ +/* Copyright 2024 Hangzhou Yingyi Technology Co., Ltd + * + * 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 ssANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#ifndef __PHY_H__ +#define __PHY_H__ + +#include +#include + +#include +#include + +#define FEATURE_10baseT_Half (1 << 0) +#define FEATURE_10baseT_Full (1 << 1) +#define FEATURE_100baseT_Half (1 << 2) +#define FEATURE_100baseT_Full (1 << 3) +#define FEATURE_1000baseT_Half (1 << 4) +#define FEATURE_1000baseT_Full (1 << 5) +#define FEATURE_AUTONEG (1 << 6) +#define FEATURE_TP (1 << 7) +#define FEATURE_AUI (1 << 8) +#define FEATURE_MII (1 << 9) +#define FEATURE_FIBRE (1 << 10) +#define FEATURE_BNC (1 << 11) +#define FEATURE_1000baseX_Half (1 << 21) +#define FEATURE_1000baseX_Full (1 << 22) + +#define ADVERTISED_10baseT_Half (1 << 0) +#define ADVERTISED_10baseT_Full (1 << 1) +#define ADVERTISED_100baseT_Half (1 << 2) +#define ADVERTISED_100baseT_Full (1 << 3) +#define ADVERTISED_Pause (1 << 13) +#define ADVERTISED_Asym_Pause (1 << 14) +#define ADVERTISED_1000baseX_Half (1 << 21) +#define ADVERTISED_1000baseX_Full (1 << 22) + +#define PHY_1000BTSR_1000FD 0x0800 +#define PHY_1000BTSR_1000HD 0x0400 + +#define PHY_MAX_ADDR 32 + +#define PHY_FLAG_BROKEN_RESET (1 << 0) /* soft reset not supported */ + +#define PHY_DEFAULT_FEATURES (FEATURE_AUTONEG | FEATURE_TP | FEATURE_MII) +#define PHY_10BT_FEATURES (FEATURE_10baseT_Half | FEATURE_10baseT_Full) +#define PHY_100BT_FEATURES (FEATURE_100baseT_Half | FEATURE_100baseT_Full) +#define PHY_1000BT_FEATURES \ + (FEATURE_1000baseT_Half | FEATURE_1000baseT_Full) +#define PHY_BASIC_FEATURES \ + (PHY_10BT_FEATURES | PHY_100BT_FEATURES | PHY_DEFAULT_FEATURES) +#define PHY_GBIT_FEATURES (PHY_BASIC_FEATURES | PHY_1000BT_FEATURES) + +#define PHY_ANEG_TIMEOUT 4000 + +typedef enum { + PHY_IF_MODE_RGMII, + PHY_IF_MODE_RGMII_ID, + PHY_IF_MODE_RGMII_RXID, + PHY_IF_MODE_RGMII_TXID, + PHY_IF_MODE_NONE, /* Must be last */ + + PHY_IF_MODE_COUNT, +} phyif_t; + +struct phy_device; + +struct mii_dev { + struct uk_list_head link; + void *priv; + struct uk_alloc *a; + int (*read)(struct mii_dev *bus, int addr, int reg); + int (*write)(struct mii_dev *bus, int addr, int reg, + uint16_t val); + int (*reset)(struct mii_dev *bus); + struct phy_device *phymap[PHY_MAX_ADDR]; + uint32_t phy_mask; +}; + +struct phy_driver { + char *name; + unsigned int uid; + unsigned int mask; + uint32_t features; + + int (*probe)(struct phy_device *phydev); + int (*config)(struct phy_device *phydev); + int (*startup)(struct phy_device *phydev); + int (*shutdown)(struct phy_device *phydev); + + struct uk_list_head list; +}; + +struct phy_device { + struct mii_dev *bus; + struct phy_driver *drv; + void *priv; + struct pf_device *dev; + + int speed; + int duplex_full; + + int link; + int port; + phyif_t interface; + + uint32_t advertising; + uint32_t supported; + + int autoneg_en; + int addr; + int pause; + int asym_pause; + uint32_t phy_id; + uint32_t flags; + bool is_c45; +}; + +/* phy_read - 通过mdio读取phy mii寄存器 + * @return: + * 失败: return < 0; + * 成功: 返回一个16位的mii寄存器数值, 意味着此数值必须>0 + */ +static inline int phy_read(struct phy_device *phydev, int regnum) +{ + struct mii_dev *bus = phydev->bus; + return bus->read(bus, phydev->addr, regnum); +} + +static inline int phy_write(struct phy_device *phydev, int regnum, uint16_t val) +{ + struct mii_dev *bus = phydev->bus; + return bus->write(bus, phydev->addr, regnum, val); +} + +struct mii_dev *mdio_alloc(struct uk_alloc *a); +void mdio_free(struct uk_alloc *a, struct mii_dev *bus); +int mdio_register(struct mii_dev *bus); + +struct phy_device *phy_connect(struct mii_dev *bus, int addr, + struct pf_device *dev, phyif_t interface); +int phy_set_supported(struct phy_device *phydev, uint32_t max_speed); +int phy_config(struct phy_device *phydev); +int phy_startup(struct phy_device *phydev); +int phy_shutdown(struct phy_device *phydev); + +#endif \ No newline at end of file diff --git a/drivers/net/mac/include/net/macdrv/test_gmac.h b/drivers/net/mac/include/net/macdrv/test_gmac.h new file mode 100644 index 0000000000000000000000000000000000000000..741ae709b8e8c8e71e67af2873c9057c7cf23612 --- /dev/null +++ b/drivers/net/mac/include/net/macdrv/test_gmac.h @@ -0,0 +1,43 @@ +/* Copyright 2024 Hangzhou Yingyi Technology Co., Ltd + * + * 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 ssANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifdef CONFIG_GMAC_TEST +#ifndef __TEST_GMAC_H__ +#define __TEST_GMAC_H__ + +#define TEST_EXPECT_SNUM_EQ(val1, val2) do { \ + if ((val1) != (val2)) { \ + uk_pr_err("(%s) != (%s)", #val1, #val2); \ + return 0; \ + } \ +} while (0); + +struct dwgmac_platform_test_ops { + // int (*test_gmac_cru_addr)(struct dwgmac_platdata *pdata); + // int (*test_gmac_src_clk)(struct dwgmac_platdata *pdata); + // int (*test_gmac_rx_tx_clk)(struct dwgmac_platdata *pdata); + // int (*test_gmac_out_clk)(struct dwgmac_platdata *pdata); + int (*test_gmac_gpio_configuration)(struct dwgmac_platdata *pdata); + int (*test_gmac_rgmii_configuration)(struct dwgmac_platdata *pdata); + int (*test_gmac_clk_configuration)(struct dwgmac_platdata *pdata); +}; + +extern struct dwgmac_platform_test_ops *plat_test_ops; +struct dwgmac_platdata *get_gmac_platdata_by_id(unsigned id); +int gmac_nr(void); + + +#endif /* __TEST_GMAC_H__ */ +#endif /* CONFIG_GMAC_TEST */ \ No newline at end of file diff --git a/drivers/net/mac/mii.c b/drivers/net/mac/mii.c new file mode 100644 index 0000000000000000000000000000000000000000..db79986e296aa5f95e6c94a4bed201fdbac37cdc --- /dev/null +++ b/drivers/net/mac/mii.c @@ -0,0 +1,74 @@ +/* Copyright 2024 Hangzhou Yingyi Technology Co., Ltd + * + * 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 ssANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include + +#include +#include +#include +#include + +#include + +static struct uk_list_head mii_devs; +static struct mii_dev *current_mii; + +struct mii_dev *mdio_alloc(struct uk_alloc *a) +{ + struct mii_dev *bus; + + bus = uk_malloc(a, sizeof(*bus)); + if (!bus) + return bus; + + memset(bus, 0, sizeof(*bus)); + + UK_INIT_LIST_HEAD(&bus->link); + + bus->a = a; + + return bus; +} + +void mdio_free(struct uk_alloc *a, struct mii_dev *bus) +{ + uk_free(a, bus); +} + +int mdio_register(struct mii_dev *bus) +{ + if (!bus || !bus->read || !bus->write) + return -1; + + uk_list_add_tail(&bus->link, &mii_devs); + + if (!current_mii) + current_mii = bus; + + return 0; +} + +static int eth_common_init(struct uk_init_ctx *ictx __unused) +{ + UK_INIT_LIST_HEAD(&mii_devs); + current_mii = NULL; + return 0; +} + +static void eth_common_init_term(const struct uk_term_ctx *tctx __unused) +{} + + +uk_early_initcall(eth_common_init, eth_common_init_term); \ No newline at end of file diff --git a/drivers/net/mac/phy.c b/drivers/net/mac/phy.c new file mode 100644 index 0000000000000000000000000000000000000000..47b8b2763905406647fc8a01ac886f4f7bc54e4c --- /dev/null +++ b/drivers/net/mac/phy.c @@ -0,0 +1,607 @@ +/* Copyright 2024 Hangzhou Yingyi Technology Co., Ltd + * + * 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 ssANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +static UK_LIST_HEAD(phy_drivers); + +/** + * do_genphy_advert_config - 配置通告寄存器以用于自协商 + * @return: <0: 错误; ==0: phy通告没有修改; >0: phy通告被修改 + */ +static int do_genphy_advert_config(struct phy_device *phydev) +{ + uint32_t advertise; + int oldadv, adv, bmsr; + int err, changed = 0; + + /* 通告参数应在phy支持的功能参数的前提下进行配置 */ + phydev->advertising &= phydev->supported; + advertise = phydev->advertising; + + adv = phy_read(phydev, MII_ADVERTISE); + oldadv = adv; + + if (adv < 0) + return adv; + + adv &= ~(ADVERTISE_ALL | ADVERTISE_100BASE4 | ADVERTISE_PAUSE_CAP | + ADVERTISE_PAUSE_ASYM); + if (advertise & ADVERTISED_10baseT_Half) + adv |= ADVERTISE_10HALF; + if (advertise & ADVERTISED_10baseT_Full) + adv |= ADVERTISE_10FULL; + if (advertise & ADVERTISED_100baseT_Half) + adv |= ADVERTISE_100HALF; + if (advertise & ADVERTISED_100baseT_Full) + adv |= ADVERTISE_100FULL; + if (advertise & ADVERTISED_Pause) + adv |= ADVERTISE_PAUSE_CAP; + if (advertise & ADVERTISED_Asym_Pause) + adv |= ADVERTISE_PAUSE_ASYM; + if (advertise & ADVERTISED_1000baseX_Half) + adv |= ADVERTISE_1000XHALF; + if (advertise & ADVERTISED_1000baseX_Full) + adv |= ADVERTISE_1000XFULL; + + if (adv != oldadv) { + err = phy_write(phydev, MII_ADVERTISE, adv); + if (err < 0) + return err; + changed = 1; + } + + bmsr = phy_read(phydev, MII_BMSR); + if (bmsr < 0) + return bmsr; + + /* + * 根据IEEE802.3 22.2.4.2.16 扩展状态寄存器 + * 所有千兆能力的phy需要设置BMSR_ESTATEN位为1 + */ + if (!(bmsr & BMSR_ESTATEN)) + return changed; + + adv = phy_read(phydev, MII_CTRL1000); + oldadv = adv; + + if (adv < 0) + return adv; + + adv &= ~(ADVERTISE_1000FULL | ADVERTISE_1000HALF); + + if (phydev->supported & (FEATURE_1000baseT_Half | + FEATURE_1000baseT_Full)) { + if (advertise & FEATURE_1000baseT_Half) + adv |= ADVERTISE_1000HALF; + if (advertise & FEATURE_1000baseT_Full) + adv |= ADVERTISE_1000FULL; + } + + if (adv != oldadv) + changed = 1; + + err = phy_write(phydev, MII_CTRL1000, adv); + if (err < 0) + return err; + + return changed; +} + +static int do_genphy_forced_setup(struct phy_device *phydev) +{ + int err; + int ctl = BMCR_ANRESTART; + + phydev->pause = phydev->asym_pause = 0; + + if (1000 == phydev->speed) + ctl |= BMCR_SPEED1000; + else if (100 == phydev->speed) + ctl |= BMCR_SPEED100; + + if (phydev->duplex_full) + ctl |= BMCR_FULLDPLX; + + err = phy_write(phydev, MII_BMCR, ctl); + + return err; +} + +static int do_phy_sw_reset(struct phy_device *phydev) +{ + int reg; + int timeout = 500; + struct timespec ts = { + .tv_nsec = 1000 * 1000 * 1, /* 1ms */ + .tv_sec = 0 + }; + + if (phydev->flags & PHY_FLAG_BROKEN_RESET) + return 0; + + if (phy_write(phydev, MII_BMCR, BMCR_RESET) < 0) { + uk_pr_err("PHY reset failed\n"); + return -1; + } + + reg = phy_read(phydev, MII_BMCR); + while ((reg & BMCR_RESET) && timeout--) { + reg = phy_read(phydev, MII_BMCR); + + if (reg < 0) { + uk_pr_err("PHY status read failed\n"); + return -1; + } + nanosleep(&ts, NULL); + } + + if (reg & BMCR_RESET) { + uk_pr_err("PHY reset timed out\n"); + return -1; + } + + return 0; +} + +/* genphy_restart_aneg - 使能重启自协商 */ +static int do_genphy_aneg_restart(struct phy_device *phydev) +{ + int ctl; + + ctl = phy_read(phydev, MII_BMCR); + if (ctl < 0) + return ctl; + + ctl |= (BMCR_ANENABLE | BMCR_ANRESTART); + ctl &= ~(BMCR_ISOLATE); + ctl = phy_write(phydev, MII_BMCR, ctl); + + return ctl; +} + + +/** + * do_genphy_aneg_config - 配置和使能自协商 + * + * 如果自协商没有使能,则配置公告寄存器并启动自协商 + * 如果自协商不可用,则通过BMCR寄存器进行配置 + */ +static int do_genphy_aneg_config(struct phy_device *phydev) +{ + int result; + + if (!phydev->autoneg_en) + return do_genphy_forced_setup(phydev); + + result = do_genphy_advert_config(phydev); + if (result < 0) + return result; + + if (result == 0) { + int ctl = phy_read(phydev, MII_BMCR); + + if (ctl < 0) + return ctl; + + if (!(ctl & BMCR_ANENABLE) || (ctl & BMCR_ISOLATE)) + result = 1; + } + + /* 如果通告配置发生了修改,则重启自协商流程 */ + if (result > 0) + result = do_genphy_aneg_restart(phydev); + + return result; +} + +/* do_genphy_link_update - 更新phy连接状态 */ +static int do_genphy_link_update(struct phy_device *phydev) +{ + unsigned int mii_reg; + struct timespec ts = { + .tv_nsec = 1000 * 1000 * 1, /* 1ms */ + .tv_sec = 0 + }; + + mii_reg = phy_read(phydev, MII_BMSR); + + /* phy已经上线了则直接返回 */ + if (phydev->link && mii_reg & BMSR_LSTATUS) + return 0; + + /* 若phy状态处于自协商,则等待自协商完成; 否则,根据BMSR来获取phy状态 */ + if (phydev->autoneg_en && !(mii_reg & BMSR_ANEGCOMPLETE)) { + int i = 0; + while (!(mii_reg & BMSR_ANEGCOMPLETE)) { + if (i++ > PHY_ANEG_TIMEOUT) { + uk_pr_err("auto negotiation TIMEOUT !\n"); + phydev->link = 0; + return -ETIMEDOUT; + } + + nanosleep(&ts, NULL); /* 1 ms */ + mii_reg = phy_read(phydev, MII_BMSR); + } + uk_pr_info("auto negotiation done!\n"); + phydev->link = 1; + } else { + mii_reg = phy_read(phydev, MII_BMSR); + + if (mii_reg & BMSR_LSTATUS) + phydev->link = 1; + else + phydev->link = 0; + } + + return 0; +} + +static int do_genphy_config(struct phy_device *phydev) +{ + int val; + uint32_t features; + + features = (FEATURE_TP | FEATURE_MII | FEATURE_AUI | FEATURE_FIBRE + | FEATURE_BNC); + + val = phy_read(phydev, MII_BMSR); + if (val < 0) + return val; + + if (val & BMSR_ANEGCAPABLE) + features |= FEATURE_AUTONEG; + + if (val & BMSR_100FULL) + features |= FEATURE_100baseT_Full; + if (val & BMSR_100HALF) + features |= FEATURE_100baseT_Half; + if (val & BMSR_10FULL) + features |= FEATURE_10baseT_Full; + if (val & BMSR_10HALF) + features |= FEATURE_10baseT_Half; + + if (val & BMSR_ESTATEN) { + val = phy_read(phydev, MII_ESTATUS); + + if (val < 0) + return val; + + if (val & ESTATUS_1000_TFULL) + features |= FEATURE_1000baseT_Full; + if (val & ESTATUS_1000_THALF) + features |= FEATURE_1000baseT_Half; + if (val & ESTATUS_1000_XFULL) + features |= FEATURE_1000baseX_Full; + if (val & ESTATUS_1000_XHALF) + features |= FEATURE_1000baseX_Half; + } + + phydev->supported &= features; + phydev->advertising &= features; + + do_genphy_aneg_config(phydev); + + return 0; +} + +static int genphy_parse_link(struct phy_device *phydev) +{ + int mii_reg = phy_read(phydev, MII_BMSR); + + if (phydev->autoneg_en) { + uint32_t lpa = 0; + int gblpa = 0; + uint32_t estatus = 0; + + if (phydev->supported & (FEATURE_1000baseT_Full | + FEATURE_1000baseT_Half)) { + + gblpa = phy_read(phydev, MII_STAT1000); + if (gblpa < 0) { + uk_pr_warn("Could not read MII_STAT1000. " + "Ignoring gigabit capability\n"); + gblpa = 0; + } + gblpa &= phy_read(phydev, MII_CTRL1000) << 2; + } + + phydev->speed = 10; + phydev->duplex_full = 0; + + /* 检查千兆能力 */ + if (gblpa & (PHY_1000BTSR_1000FD | PHY_1000BTSR_1000HD)) { + phydev->speed = 1000; + + if (gblpa & PHY_1000BTSR_1000FD) + phydev->duplex_full = 1; + + return 0; + } + + lpa = phy_read(phydev, MII_ADVERTISE); + lpa &= phy_read(phydev, MII_LPA); + + if (lpa & (LPA_100FULL | LPA_100HALF)) { + phydev->speed = 100; + + if (lpa & LPA_100FULL) + phydev->duplex_full = 1; + + } else if (lpa & LPA_10FULL) + phydev->duplex_full = 1; + + if ((mii_reg & BMSR_ESTATEN) && !(mii_reg & BMSR_ERCAP)) + estatus = phy_read(phydev, MII_ESTATUS); + + if (estatus & (ESTATUS_1000_XFULL | ESTATUS_1000_XHALF | + ESTATUS_1000_TFULL | ESTATUS_1000_THALF)) { + phydev->speed = 1000; + if (estatus & (ESTATUS_1000_XFULL | ESTATUS_1000_TFULL)) + phydev->duplex_full = 1; + } + + } else { + uint32_t bmcr = phy_read(phydev, MII_BMCR); + + phydev->speed = 10; + phydev->duplex_full = 0; + + if (bmcr & BMCR_FULLDPLX) + phydev->duplex_full = 1; + + if (bmcr & BMCR_SPEED1000) + phydev->speed = 1000; + else if (bmcr & BMCR_SPEED100) + phydev->speed = 100; + } + + return 0; +} + +static int do_genphy_startup(struct phy_device *phydev) +{ + int ret; + + ret = do_genphy_link_update(phydev); + if (ret) + return ret; + + return genphy_parse_link(phydev); +} + +static int do_genphy_shutdown(struct phy_device *phydev __maybe_unused) +{ + return 0; +} + +static struct phy_driver genphy_driver = { + .uid = 0xffffffff, + .mask = 0xffffffff, + .name = "Generic PHY", + .features = PHY_GBIT_FEATURES | FEATURE_MII | + FEATURE_AUI | FEATURE_FIBRE | + FEATURE_BNC, + .config = do_genphy_config, + .startup = do_genphy_startup, + .shutdown = do_genphy_shutdown, +}; + +/* + * get_phy_id - 读取第3/4号寄存器以获取phy id, 读取到的值存储在phy_id中 + * @return: 成功: 0; 失败: <0 + */ +static int get_phy_id(struct mii_dev *bus, int addr, int devad __unused, + uint32_t *phy_id) +{ + int phy_reg; + + phy_reg = bus->read(bus, addr, MII_PHYSID1); + if (phy_reg < 0) + return -EIO; + + *phy_id = (phy_reg & 0xffff) << 16; + + phy_reg = bus->read(bus, addr, MII_PHYSID2); + if (phy_reg < 0) + return -EIO; + + *phy_id |= (phy_reg & 0xffff); + + return 0; +} + +static struct phy_device *do_existing_phy_search(struct mii_dev *bus, + unsigned phy_mask, phyif_t interface) +{ + while (phy_mask) { + int addr = ffs(phy_mask) - 1; + if (bus->phymap[addr]) { + bus->phymap[addr]->interface = interface; + return bus->phymap[addr]; + } + phy_mask &= ~(1 << addr); + } + return NULL; +} + +static int phy_probe(struct phy_device *phydev) +{ + phydev->advertising = phydev->supported = phydev->drv->features; + return phydev->drv->probe ? phydev->drv->probe(phydev) : 0; +} + +static struct phy_device *do_phy_device_create(struct mii_dev *bus, int addr, + uint32_t phy_id, bool is_c45, + phyif_t interface, + struct uk_alloc *a) +{ + struct phy_device *dev; + + dev = uk_zalloc(a, sizeof(*dev)); + if (unlikely(!dev)) + return NULL; + + dev->duplex_full = -1; + dev->link = 0; + dev->interface = interface; + dev->autoneg_en = 1; + dev->addr = addr; + dev->phy_id = phy_id; + dev->is_c45 = is_c45; + dev->bus = bus; + dev->drv = &genphy_driver; + bus->phymap[addr] = dev; + + phy_probe(dev); + + return dev; +} + +static struct phy_device *do_phy_create_by_mask(struct mii_dev *bus, + unsigned phy_mask, int devad, + phyif_t phyif) +{ + uint32_t phy_id = 0xffffffff; + + while (phy_mask) { + int addr = ffs(phy_mask) - 1; + int r = get_phy_id(bus, addr, devad, &phy_id); + if (r == 0 && (phy_id & 0x1fffffff) != 0x1fffffff) { + /* 检查是否可以使用c45,不行则使用c22 */ + return do_phy_device_create( + bus, addr, phy_id, (devad == -1) ? false : true, + phyif, bus->a); + } + phy_mask &= ~(1 << addr); + } + return NULL; +} + +static struct phy_device * +get_phy_device_by_mask(struct mii_dev *bus, unsigned phy_mask, phyif_t phyif) +{ + int i; + struct phy_device *phydev; + + phydev = do_existing_phy_search(bus, phy_mask, phyif); + if (phydev) + return phydev; + + for (i = 0; i < 5; i++) { + phydev = do_phy_create_by_mask(bus, phy_mask, i ? i : -1, phyif); + if (PTRISERR(phydev)) + return NULL; + if (phydev) + return phydev; + } + + while (phy_mask) { + int addr = ffs(phy_mask) - 1; + uk_pr_info("PHY#%d not found\n", addr); + phy_mask &= ~(1 << addr); + } + + return NULL; +} + +static struct phy_device *phy_find_by_mask(struct mii_dev *bus, + unsigned phy_mask, + phyif_t interface) +{ + struct timespec ts = { + .tv_nsec = 15 * 1000, /* 15ms */ + .tv_sec = 0 + }; + + if (bus->reset) { + bus->reset(bus); + nanosleep(&ts, NULL); + } + + return get_phy_device_by_mask(bus, phy_mask, interface); +} + + +static void do_phydev_attach(struct phy_device *phydev, struct pf_device *dev) +{ + do_phy_sw_reset(phydev); + if (phydev->dev && phydev->dev != dev) { + uk_pr_warn("phyaddr: %d Do reconnecting....\n", phydev->addr); + } + phydev->dev = dev; +} + +struct phy_device *phy_connect(struct mii_dev *bus, int addr, + struct pf_device *dev, phyif_t interface) +{ + struct phy_device *phydev = phy_find_by_mask(bus, 1 << addr, interface); + + if (phydev) + do_phydev_attach(phydev, dev); + else + uk_pr_err("Could not get PHY for addr %d\n", addr); + + return phydev; +} + +int phy_set_supported(struct phy_device *phydev, uint32_t max_speed) +{ + phydev->supported &= PHY_DEFAULT_FEATURES; + + switch (max_speed) { + default: + return -EOPNOTSUPP; + case 1000: + phydev->supported |= PHY_1000BT_FEATURES; + /* fall through */ + case 100: + phydev->supported |= PHY_100BT_FEATURES; + /* fall through */ + case 10: + phydev->supported |= PHY_10BT_FEATURES; + } + + return 0; +} + +int phy_config(struct phy_device *phydev) +{ + return phydev->drv->config ? phydev->drv->config(phydev) : 0; +} + +int phy_startup(struct phy_device *phydev) +{ + return phydev->drv->startup ? phydev->drv->startup(phydev) : 0; +} + +int phy_shutdown(struct phy_device *phydev) +{ + return phydev->drv->shutdown ? phydev->drv->shutdown(phydev) : 0; +} \ No newline at end of file diff --git a/drivers/net/mac/tests/test_gmac.c b/drivers/net/mac/tests/test_gmac.c new file mode 100644 index 0000000000000000000000000000000000000000..19b08b83ee9aa77d0cae4e9005db92f05a25cbe3 --- /dev/null +++ b/drivers/net/mac/tests/test_gmac.c @@ -0,0 +1,116 @@ +/* Copyright 2024 Hangzhou Yingyi Technology Co., Ltd + * + * 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 ssANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + + +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +struct dwgmac_platform_test_ops *plat_test_ops; + +#define CONFIG_OK3568 +/* + * 测试所有可用的phy芯片mdio读写操作功能 + * + * 读取: + * phy identifier register 1/2 + * 通过判断phy id1/2是否和器件手册相符,若相符则mdio收通讯正常 + * + * 写入: + * 目前没有使用到第14号/第15号寄存器, 所以我们使用15号扩展数据寄存器进行写测试 + * 读取15号MII寄存器,并异或任意一位,再写入,最后回读,判断回读的值与写入是否一致 + */ +UK_TESTCASE(phy_driver_test, test_mdio_read_and_write) +{ + int mac_nr = gmac_nr(), phy_regid1, phy_regid2, reg, err; + struct phy_device *phy; + +#ifdef CONFIG_OK3568 + /* RTL8211FS phy芯片 */ + const int expect_regid1 = 0x1c, expect_regid2 = 0xc916; +#else + /* 裕太yt8531 phy芯片 */ + const int expect_regid1 = 0x4f51, expect_regid2 = 0xe91b; +#endif + + for (int i = 0; i < mac_nr; ++i) { + phy = get_gmac_platdata_by_id(i)->dwpriv.phy; + UK_TEST_ASSERT(phy != NULL); + + /* 测试mdio读 */ + phy_regid1 = phy_read(phy, MII_PHYSID1); + phy_regid2 = phy_read(phy, MII_PHYSID2); + UK_TEST_EXPECT_SNUM_EQ(phy_regid1, expect_regid1); + UK_TEST_EXPECT_SNUM_EQ(phy_regid2, expect_regid2); + + /* 测试mdio写 */ + reg = phy_read(phy, MII_EXTENDED_DATA); + UK_TEST_EXPECT_SNUM_GE(reg, 0); + + reg ^= UK_BIT(13); + err = phy_write(phy, MII_EXTENDED_DATA, reg); + UK_TEST_EXPECT_SNUM_EQ(err, 0); + + err = phy_read(phy, MII_EXTENDED_DATA); + UK_TEST_EXPECT_SNUM_EQ(err, reg); + } +} + +uk_testsuite_register(phy_driver_test, NULL); + +/* + * 测试gmac芯片时钟配置 + * 1. 检查gmac是否工作在RGMII的工作频率 + * 2. 检查rx/tx时钟频率是否配置正确,百兆25M,千兆125M + * 3. 检查gmac输出给phy芯片的时钟频率是否配置正确 + */ +UK_TESTCASE(gmac_driver_test, test_gmac_clock_configuration) +{ + int mac_nr = gmac_nr(); + UK_TEST_ASSERT(mac_nr == 2); + + for (int i = 0; i < mac_nr; ++i) + plat_test_ops->test_gmac_clk_configuration(get_gmac_platdata_by_id(i)); +} + +/* + * 检查gmac的pin引脚和gpio是否工作在正确的模式下 + * TODO 适配GMAC1 + */ +UK_TESTCASE(gmac_driver_test, test_gmac_gpio_configuration) +{ + UK_TEST_EXPECT_SNUM_EQ(plat_test_ops->test_gmac_gpio_configuration( + get_gmac_platdata_by_id(0)), + 1); +} + +UK_TESTCASE(gmac_driver_test, test_gmac_rgmii_configuration) +{ + UK_TEST_EXPECT_SNUM_EQ(plat_test_ops->test_gmac_rgmii_configuration( + get_gmac_platdata_by_id(0)), + 1); +} + +uk_testsuite_register(gmac_driver_test, NULL); diff --git a/drivers/ukbus/platform/include/uk/bus/platform.h b/drivers/ukbus/platform/include/uk/bus/platform.h index 8e9f94b69adce4e17add0f48fd604b8efc3f18f6..44929e934291195f0366fa95f4aca073b50862c2 100644 --- a/drivers/ukbus/platform/include/uk/bus/platform.h +++ b/drivers/ukbus/platform/include/uk/bus/platform.h @@ -48,8 +48,9 @@ extern "C" { #define PLATFORM_DEVICE_ID_START (0x100) #define VIRTIO_MMIO_ID (PLATFORM_DEVICE_ID_START) #define GEN_PCI_ID (PLATFORM_DEVICE_ID_START + 1) +#define GMAC_ID (PLATFORM_DEVICE_ID_START + 2) -#define PLATFORM_DEVICE_ID_END (GEN_PCI_ID + 1) +#define PLATFORM_DEVICE_ID_END (GMAC_ID + 1) #define UK_MAX_VIRTIO_MMIO_DEVICE (0x2) diff --git a/drivers/ukbus/platform/platform_bus.c b/drivers/ukbus/platform/platform_bus.c index d80db40e34cf14251331bdd3546063b59c82b989..86bc433c18335058d35e162d4017f82d13b7d542 100644 --- a/drivers/ukbus/platform/platform_bus.c +++ b/drivers/ukbus/platform/platform_bus.c @@ -52,6 +52,7 @@ static void *dtb; static const char *pf_device_compatible_list[] = { "virtio,mmio", "pci-host-ecam-generic", + "rockchip,rk3568-gmac", NULL }; #endif /* CONFIG_LIBUKBUS_PLATFORM_FDT */