GPIO模拟MDIO

背景

CPU:AST2500

驱动里实现GPIO模拟MDIO驱动,参考内核驱动mdio-bitbang.c和mdio-gpio.c,当前项目不支持设备树,驱动需要改成platform注册

MDIO介绍

SMI接口

SMI是MAC内核访问PHY寄存器接口,它由两根线组成,双工,MDC为时钟,MDIO为双向数据通信,原理上跟I2C总线很类似,也可以通过总线访问多个不同的phy。

MDC/MDIO基本特性:

  • 两线制:MDC(时钟线)和MDIO(数据线)。
  • 时钟频率:2.5MHz
  • 通信方式:总线制,可同时接入的PHY数量为32个
  • 通过SMI接口,MAC芯片主动的轮询PHY层芯片,获得状态信息,并发出命令信息。

Clause22

Clause45

内核源码

修改后的源码不方便放上来,这里放的是内核源码

mdio-gpio.c

// SPDX-License-Identifier: GPL-2.0
/** GPIO based MDIO bitbang driver.* Supports OpenFirmware.** Copyright (c) 2008 CSE Semaphore Belgium.*  by Laurent Pinchart <laurentp@cse-semaphore.com>** Copyright (C) 2008, Paulius Zaleckas <paulius.zaleckas@teltonika.lt>** Based on earlier work by** Copyright (c) 2003 Intracom S.A.*  by Pantelis Antoniou <panto@intracom.gr>** 2005 (c) MontaVista Software, Inc.* Vitaly Bordug <vbordug@ru.mvista.com>*/#include <linux/module.h>
#include <linux/slab.h>
#include <linux/interrupt.h>
#include <linux/platform_device.h>
#include <linux/platform_data/mdio-gpio.h>
#include <linux/mdio-bitbang.h>
#include <linux/mdio-gpio.h>
#include <linux/gpio/consumer.h>
#include <linux/of_mdio.h>struct mdio_gpio_info {struct mdiobb_ctrl ctrl;struct gpio_desc *mdc, *mdio, *mdo;
};static int mdio_gpio_get_data(struct device *dev,struct mdio_gpio_info *bitbang)
{bitbang->mdc = devm_gpiod_get_index(dev, NULL, MDIO_GPIO_MDC,GPIOD_OUT_LOW);if (IS_ERR(bitbang->mdc))return PTR_ERR(bitbang->mdc);bitbang->mdio = devm_gpiod_get_index(dev, NULL, MDIO_GPIO_MDIO,GPIOD_IN);if (IS_ERR(bitbang->mdio))return PTR_ERR(bitbang->mdio);bitbang->mdo = devm_gpiod_get_index_optional(dev, NULL, MDIO_GPIO_MDO,GPIOD_OUT_LOW);return PTR_ERR_OR_ZERO(bitbang->mdo);
}static void mdio_dir(struct mdiobb_ctrl *ctrl, int dir)
{struct mdio_gpio_info *bitbang =container_of(ctrl, struct mdio_gpio_info, ctrl);if (bitbang->mdo) {/* Separate output pin. Always set its value to high* when changing direction. If direction is input,* assume the pin serves as pull-up. If direction is* output, the default value is high.*/gpiod_set_value_cansleep(bitbang->mdo, 1);return;}if (dir)gpiod_direction_output(bitbang->mdio, 1);elsegpiod_direction_input(bitbang->mdio);
}static int mdio_get(struct mdiobb_ctrl *ctrl)
{struct mdio_gpio_info *bitbang =container_of(ctrl, struct mdio_gpio_info, ctrl);return gpiod_get_value_cansleep(bitbang->mdio);
}static void mdio_set(struct mdiobb_ctrl *ctrl, int what)
{struct mdio_gpio_info *bitbang =container_of(ctrl, struct mdio_gpio_info, ctrl);if (bitbang->mdo)gpiod_set_value_cansleep(bitbang->mdo, what);elsegpiod_set_value_cansleep(bitbang->mdio, what);
}static void mdc_set(struct mdiobb_ctrl *ctrl, int what)
{struct mdio_gpio_info *bitbang =container_of(ctrl, struct mdio_gpio_info, ctrl);gpiod_set_value_cansleep(bitbang->mdc, what);
}static const struct mdiobb_ops mdio_gpio_ops = {.owner = THIS_MODULE,.set_mdc = mdc_set,.set_mdio_dir = mdio_dir,.set_mdio_data = mdio_set,.get_mdio_data = mdio_get,
};static struct mii_bus *mdio_gpio_bus_init(struct device *dev,struct mdio_gpio_info *bitbang,int bus_id)
{struct mdio_gpio_platform_data *pdata = dev_get_platdata(dev);struct mii_bus *new_bus;bitbang->ctrl.ops = &mdio_gpio_ops;new_bus = alloc_mdio_bitbang(&bitbang->ctrl);if (!new_bus)return NULL;new_bus->name = "GPIO Bitbanged MDIO";new_bus->parent = dev;if (bus_id != -1)snprintf(new_bus->id, MII_BUS_ID_SIZE, "gpio-%x", bus_id);elsestrncpy(new_bus->id, "gpio", MII_BUS_ID_SIZE);if (pdata) {new_bus->phy_mask = pdata->phy_mask;new_bus->phy_ignore_ta_mask = pdata->phy_ignore_ta_mask;}dev_set_drvdata(dev, new_bus);return new_bus;
}static void mdio_gpio_bus_deinit(struct device *dev)
{struct mii_bus *bus = dev_get_drvdata(dev);free_mdio_bitbang(bus);
}static void mdio_gpio_bus_destroy(struct device *dev)
{struct mii_bus *bus = dev_get_drvdata(dev);mdiobus_unregister(bus);mdio_gpio_bus_deinit(dev);
}static int mdio_gpio_probe(struct platform_device *pdev)
{struct mdio_gpio_info *bitbang;struct mii_bus *new_bus;int ret, bus_id;bitbang = devm_kzalloc(&pdev->dev, sizeof(*bitbang), GFP_KERNEL);if (!bitbang)return -ENOMEM;ret = mdio_gpio_get_data(&pdev->dev, bitbang);if (ret)return ret;if (pdev->dev.of_node) {bus_id = of_alias_get_id(pdev->dev.of_node, "mdio-gpio");if (bus_id < 0) {dev_warn(&pdev->dev, "failed to get alias id\n");bus_id = 0;}} else {bus_id = pdev->id;}new_bus = mdio_gpio_bus_init(&pdev->dev, bitbang, bus_id);if (!new_bus)return -ENODEV;ret = of_mdiobus_register(new_bus, pdev->dev.of_node);if (ret)mdio_gpio_bus_deinit(&pdev->dev);return ret;
}static int mdio_gpio_remove(struct platform_device *pdev)
{mdio_gpio_bus_destroy(&pdev->dev);return 0;
}static const struct of_device_id mdio_gpio_of_match[] = {{ .compatible = "virtual,mdio-gpio", },{ /* sentinel */ }
};
MODULE_DEVICE_TABLE(of, mdio_gpio_of_match);static struct platform_driver mdio_gpio_driver = {.probe = mdio_gpio_probe,.remove = mdio_gpio_remove,.driver		= {.name	= "mdio-gpio",.of_match_table = mdio_gpio_of_match,},
};module_platform_driver(mdio_gpio_driver);MODULE_ALIAS("platform:mdio-gpio");
MODULE_AUTHOR("Laurent Pinchart, Paulius Zaleckas");
MODULE_LICENSE("GPL v2");
MODULE_DESCRIPTION("Generic driver for MDIO bus emulation using GPIO");

 mdio-bitbang.c

// SPDX-License-Identifier: GPL-2.0
/** Bitbanged MDIO support.** Author: Scott Wood <scottwood@freescale.com>* Copyright (c) 2007 Freescale Semiconductor** Based on CPM2 MDIO code which is:** Copyright (c) 2003 Intracom S.A.*  by Pantelis Antoniou <panto@intracom.gr>** 2005 (c) MontaVista Software, Inc.* Vitaly Bordug <vbordug@ru.mvista.com>*/#include <linux/module.h>
#include <linux/mdio-bitbang.h>
#include <linux/types.h>
#include <linux/delay.h>#define MDIO_READ 2
#define MDIO_WRITE 1#define MDIO_C45 (1<<15)
#define MDIO_C45_ADDR (MDIO_C45 | 0)
#define MDIO_C45_READ (MDIO_C45 | 3)
#define MDIO_C45_WRITE (MDIO_C45 | 1)#define MDIO_SETUP_TIME 10
#define MDIO_HOLD_TIME 10/* Minimum MDC period is 400 ns, plus some margin for error.  MDIO_DELAY* is done twice per period.*/
#define MDIO_DELAY 250/* The PHY may take up to 300 ns to produce data, plus some margin* for error.*/
#define MDIO_READ_DELAY 350/* MDIO must already be configured as output. */
static void mdiobb_send_bit(struct mdiobb_ctrl *ctrl, int val)
{const struct mdiobb_ops *ops = ctrl->ops;ops->set_mdio_data(ctrl, val);ndelay(MDIO_DELAY);ops->set_mdc(ctrl, 1);ndelay(MDIO_DELAY);ops->set_mdc(ctrl, 0);
}/* MDIO must already be configured as input. */
static int mdiobb_get_bit(struct mdiobb_ctrl *ctrl)
{const struct mdiobb_ops *ops = ctrl->ops;ndelay(MDIO_DELAY);ops->set_mdc(ctrl, 1);ndelay(MDIO_READ_DELAY);ops->set_mdc(ctrl, 0);return ops->get_mdio_data(ctrl);
}/* MDIO must already be configured as output. */
static void mdiobb_send_num(struct mdiobb_ctrl *ctrl, u16 val, int bits)
{int i;for (i = bits - 1; i >= 0; i--)mdiobb_send_bit(ctrl, (val >> i) & 1);
}/* MDIO must already be configured as input. */
static u16 mdiobb_get_num(struct mdiobb_ctrl *ctrl, int bits)
{int i;u16 ret = 0;for (i = bits - 1; i >= 0; i--) {ret <<= 1;ret |= mdiobb_get_bit(ctrl);}return ret;
}/* Utility to send the preamble, address, and* register (common to read and write).*/
static void mdiobb_cmd(struct mdiobb_ctrl *ctrl, int op, u8 phy, u8 reg)
{const struct mdiobb_ops *ops = ctrl->ops;int i;ops->set_mdio_dir(ctrl, 1);/** Send a 32 bit preamble ('1's) with an extra '1' bit for good* measure.  The IEEE spec says this is a PHY optional* requirement.  The AMD 79C874 requires one after power up and* one after a MII communications error.  This means that we are* doing more preambles than we need, but it is safer and will be* much more robust.*/for (i = 0; i < 32; i++)mdiobb_send_bit(ctrl, 1);/* send the start bit (01) and the read opcode (10) or write (01).Clause 45 operation uses 00 for the start and 11, 10 forread/write */mdiobb_send_bit(ctrl, 0);if (op & MDIO_C45)mdiobb_send_bit(ctrl, 0);elsemdiobb_send_bit(ctrl, 1);mdiobb_send_bit(ctrl, (op >> 1) & 1);mdiobb_send_bit(ctrl, (op >> 0) & 1);mdiobb_send_num(ctrl, phy, 5);mdiobb_send_num(ctrl, reg, 5);
}/* In clause 45 mode all commands are prefixed by MDIO_ADDR to specify thelower 16 bits of the 21 bit address. This transfer is done identically to aMDIO_WRITE except for a different code. To enable clause 45 mode orMII_ADDR_C45 into the address. Theoretically clause 45 and normal devicescan exist on the same bus. Normal devices should ignore the MDIO_ADDRphase. */
static int mdiobb_cmd_addr(struct mdiobb_ctrl *ctrl, int phy, u32 addr)
{unsigned int dev_addr = (addr >> 16) & 0x1F;unsigned int reg = addr & 0xFFFF;mdiobb_cmd(ctrl, MDIO_C45_ADDR, phy, dev_addr);/* send the turnaround (10) */mdiobb_send_bit(ctrl, 1);mdiobb_send_bit(ctrl, 0);mdiobb_send_num(ctrl, reg, 16);ctrl->ops->set_mdio_dir(ctrl, 0);mdiobb_get_bit(ctrl);return dev_addr;
}static int mdiobb_read(struct mii_bus *bus, int phy, int reg)
{struct mdiobb_ctrl *ctrl = bus->priv;int ret, i;if (reg & MII_ADDR_C45) {reg = mdiobb_cmd_addr(ctrl, phy, reg);mdiobb_cmd(ctrl, MDIO_C45_READ, phy, reg);} elsemdiobb_cmd(ctrl, MDIO_READ, phy, reg);ctrl->ops->set_mdio_dir(ctrl, 0);/* check the turnaround bit: the PHY should be driving it to zero, if this* PHY is listed in phy_ignore_ta_mask as having broken TA, skip that*/if (mdiobb_get_bit(ctrl) != 0 &&!(bus->phy_ignore_ta_mask & (1 << phy))) {/* PHY didn't drive TA low -- flush any bits it* may be trying to send.*/for (i = 0; i < 32; i++)mdiobb_get_bit(ctrl);return 0xffff;}ret = mdiobb_get_num(ctrl, 16);mdiobb_get_bit(ctrl);return ret;
}static int mdiobb_write(struct mii_bus *bus, int phy, int reg, u16 val)
{struct mdiobb_ctrl *ctrl = bus->priv;if (reg & MII_ADDR_C45) {reg = mdiobb_cmd_addr(ctrl, phy, reg);mdiobb_cmd(ctrl, MDIO_C45_WRITE, phy, reg);} elsemdiobb_cmd(ctrl, MDIO_WRITE, phy, reg);/* send the turnaround (10) */mdiobb_send_bit(ctrl, 1);mdiobb_send_bit(ctrl, 0);mdiobb_send_num(ctrl, val, 16);ctrl->ops->set_mdio_dir(ctrl, 0);mdiobb_get_bit(ctrl);return 0;
}struct mii_bus *alloc_mdio_bitbang(struct mdiobb_ctrl *ctrl)
{struct mii_bus *bus;bus = mdiobus_alloc();if (!bus)return NULL;__module_get(ctrl->ops->owner);bus->read = mdiobb_read;bus->write = mdiobb_write;bus->priv = ctrl;return bus;
}
EXPORT_SYMBOL(alloc_mdio_bitbang);void free_mdio_bitbang(struct mii_bus *bus)
{struct mdiobb_ctrl *ctrl = bus->priv;module_put(ctrl->ops->owner);mdiobus_free(bus);
}
EXPORT_SYMBOL(free_mdio_bitbang);MODULE_LICENSE("GPL v2");

移植注意点

注意一

查看发送端和接收端的datasheet确认读写数据时是上升沿还是下降沿,AST2500是下降沿,phy端是上升沿,所以写的时候需要上升沿,读取数据时是下降沿采集

注意二

模拟MDIO的两个GPIO属于同一个寄存器,同一时间快速操作影响clk和data时序

注意三

实际读的时候从波形来看,发现phy端在TA时没有拉低,导致读取数据失败,workaround就是跳过这一时序

注意四

OS里是无法直接看到类似eth0这样的设备,所以不能用传统注册MDIO bus,直接模拟通信

用例展示

读取芯片型号和版本

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/221156.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

c++ qt 窗口开发中 俩按钮组合 配合 显影 已解决

在日常项目中&#xff0c;有这么需求&#xff0c;还想窗口移动&#xff0c;还想 右侧关闭 还能tab栏点击显影的需求&#xff0c;不得使用 qt模拟点击事件 进行功能优化 特大杯 大杯 控制 窗口显影&#xff0c; 咖啡 按钮 显示窗口 可乐 豆浆 不显示窗口 四个按钮的 互斥关…

Amazon SageMaker机器学习之旅的助推器

授权声明&#xff1a;本篇文章授权活动官方亚马逊云科技文章转发、改写权&#xff0c;包括不限于在 亚马逊云科技开发者社区, 知乎&#xff0c;自媒体平台&#xff0c;第三方开发者媒体等亚马逊云科技官方渠道。 一、前言 在当今的数字化时代&#xff0c;人工智能和机器学习已经…

2023一起益企广东省中小企业数字化赋能活动(深圳站)成功举办

12月12日&#xff0c;由广东工业和信息化厅指导&#xff0c;广东省中小企业服务中心、深圳市中小企业服务局主办&#xff0c;深圳联通承办的2023年“一起益企”广东省中小企业数字化赋能专项对接志愿服务活动&#xff08;深圳站&#xff09;在深圳成功举办。 本次活动涵盖中小企…

【AI底层逻辑】——“数学华尔兹”之一元线性回归

一元线性回归模型想必大家都耳熟能详&#xff0c;这里不再赘述。但在使用python中机器学习包时一定见过类似模型评价参数的输出&#xff0c;这一章我们就讲一讲回归分析里一些模型评价概念&#xff01; 一、方差分析ANOVA 方差分析是一种用于确定线性回归模型中不同变量对目标…

【改进YOLOv8】车辆测距预警系统:融合空间和通道重建卷积SCConv改进YOLOv8

1.研究背景与意义 项目参考AAAI Association for the Advancement of Artificial Intelligence 研究背景与意义&#xff1a; 随着交通工具的普及和道路交通的不断增加&#xff0c;车辆安全问题日益凸显。特别是在高速公路等高速道路上&#xff0c;车辆之间的距离和速度差异较…

【论文】 虚拟机 和 Linux容器 的 最新性能比较

虚拟机 和 Linux容器 的 最新性能比较 An Updated Performance Comparison of Virtual Machines and Linux Containers 借助DeepL辅助翻译 校准 摘要 云计算广泛使用虚拟机&#xff08;VM&#xff09;&#xff0c;因为它们允许工作负载相互隔离&#xff0c;并在一定程度上控…

springboot框架的客制化键盘个性化商城网站

客制化键盘网站是从客制化键盘的各部分统计和分析&#xff0c;在过程中会产生大量的、各种各样的数据。本文以客制化键盘管理为目标&#xff0c;采用B/S模式&#xff0c;以Java为开发语言&#xff0c;Jsp为开发技术、idea Eclipse为开发工具&#xff0c;MySQL为数据管理平台&am…

西南科技大学数字电子技术实验七(4行串行累加器设计及FPGA实现)FPGA部分

一、实验目的 1、掌握基于Verilog语言的diamond工具设计全流程。 2、熟悉、应用Verilog HDL描述数字电路。 3、掌握Verilog HDL的组合和时序逻辑电路的设计方法。 4、掌握“小脚丫”开发板的使用方法。 二、实验原理 三、程序清单&#xff08;每条语句必须包括注释或在开发…

Leetcode 491 递增子序列

题意理解&#xff1a; 输入&#xff1a;nums [4,6,7,7] 输出&#xff1a;[[4,6],[4,6,7],[4,6,7,7],[4,7],[4,7,7],[6,7],[6,7,7],[7,7]] 这里不止要找一个子序列&#xff0c;还要元素保证其在原来的集合中的前后顺序&#xff0c;且应为增序。 为保证一个增序序列&#xff0c;…

刘家窑中医院王忠主任:心脑血管健康知识的传播者和实践者

为普及心脑血管健康知识&#xff0c;呼吁市民注重心脑血管健康&#xff0c;王忠主任及其科室医护人员举办进社区义诊咨询的活动。王忠主任及多名中医心脑专家和护理人员为社区居民免费进行量血压、测血糖以及健康咨询等义诊活动。 王忠主任用了全身的心血&#xff0c;学习百家吸…

LTC是什么意思?CRM怎样帮助这一流程的实现?

在现代商业环境下&#xff0c;将潜在客户转化成实际销售是公司成功的基石之一。而CRM管理系统是完成LTC的有效工具。本文将向您介绍LTC是什么&#xff1f;公司怎样企业如何通过CRM实现这一流程的&#xff1f; LTC&#xff08;从线索到现金&#xff09;是企业运营管理中的一个重…

Python 使用 openpyxl 写表格

当前环境&#xff1a;Win10 x64 MS office 2016 Python3.7 openpyxl3.0.9 1 写入表格 from openpyxl import Workbook# 创建一个 workbook workbook_w Workbook()# 获取被激活的 worksheet worksheet_w workbook_w.active# 1 批量插入数据# 设置一行数据 worksheet_w.ap…

Mo 人工智能教学实训平台年终发布会——发现意外 创造可能

发布会视频回放 –发现意外 创造可能– 在技术迅猛发展的时代里&#xff0c;人工智能教育成为推动社会进步的关键力量&#xff0c;大模型更是各行业的必备技能。为了深度探索教育与人工智能的融合&#xff0c;Mo 人工智能教学实训平台于12月12日举行线上年终发布会&#xff0…

电子烟MOS的选型与要求分析

工作原理&#xff1a; 当用户在吸嘴处抽吸时&#xff0c;气流经过进气孔&#xff0c;穿 过电路板上方的咪头&#xff0c;咪头即产生电信号&#xff0c;驱 动芯片板&#xff0c;让电池供电给雾化芯&#xff0c;雾化芯中的 发热丝将电能转化成热能&#xff0c;当温度达到雾化液 …

K8s中pod詳解

目录 Yaml语法解析 Pod pod是如何被创建的 1.创建一个pod 2.创建一个多容器pod 进入容器 3.配置节点标签 4.Pod容器的交互 4.1创建pod&#xff0c;并做本地解析 4.2pod共享进程 4.3pod共享宿主机namespace 5.钩子函数lifecycle 基础指令 # 查看对应资源: 状态 $ kubectl…

家委会的职责

家委会&#xff0c;起着至关重要的作用。然而&#xff0c;而很多人对家委会的职责并不清楚。 家委会是家长与学校之间的沟通桥梁。家委会成员需要积极与学校沟通&#xff0c;了解学校的各项政策和规定&#xff0c;并及时向家长传达。同时&#xff0c;家委会也需要收集家长的意见…

leetcode算法题:省份数量

leetcode算法题547 链接&#xff1a;https://leetcode.cn/problems/number-of-provinces 题目 有 n 个城市&#xff0c;其中一些彼此相连&#xff0c;另一些没有相连。如果城市 a 与城市 b 直接相连&#xff0c;且城市 b 与城市 c 直接相连&#xff0c;那么城市 a 与城市 c 间…

立创EDA把三个单独的PCB合并成一个文件

[TOC](立创EDA把三个单独的PCB合并成一个文件 1.具体操作 1.具体操作 参考&#xff1a;立创社区 先选中PCB CTRLSHIFTC, CTRLSHIFTV** **

fcntl函数简介和使用

一、fcntl函数的作用 read函数是典型的阻塞模型&#xff0c;当缓冲区里的数据不就绪的时候&#xff0c;会一直阻塞等待。这是正常的&#xff0c;因为文件描述符默认是阻塞IO&#xff0c;而我们可以通过 fcntl 接口函数将文件描述符设置为非阻塞IO。 设置成非阻塞IO以后&#x…

发送java字节码的数据包

一些Java反序列化漏洞在利用时&#xff0c;要发送Java序列化值&#xff08;字节码&#xff09;到服务器。 我们在使用一些工具生成字节码后&#xff0c;可以通过python或者burp发送。 生成的字节码一般以两种形式存储&#xff1a; 1、二进制形式存储到 poc.ser 2、将字节码…