RK3568平台(网络篇)添加网络交换芯片RTL8306M

一.硬件原理图

分析:
该交换芯片支持I2C、SPI、mdio通信,但是看ast1520的uboot代码采用的是mdio去通信phy芯片的,所以暂时也先采用mdio的方式,需要配置相应的引脚才可以配置成mdio通信模式,具体的配置硬件工程师解决。

背景:

RTL8306M芯片上可能没有提供MDC/MDIO接口,可以通过GPIO(General Purpose Input/Output)来模拟,GPIO可实现串行输入输出,且一般CPU上会提供很多GPIO接口供用户自定义使用。每组SMI需要两个GPIO口分别来模拟MDC和MDIO,首先需要保证这两个GPIO口不作其他用途,且相应的复用模式设置为GPIO模式。

二.SMI[MDC/MDIO]协议

SMI:串行管理接口(Serial Management Interface),通常直接被称为MDIO接口(Management Data Input/Output Interface)。MDIO最早在IEEE 802.3的第22卷定义,后来在第45卷又定义了增强版本的MDIO,其主要被应用于以太网的MAC和PHY层之间,用于MAC层器件通过读写寄存器来实现对PHY层器件的操作与管理。

MDIO接口包括两条线,MDIO和MDC,其中MDIO是双向数据线,而MDC是由STA驱动的时钟线。MDC时钟的最高速率一般为2.5MHz,MDC也可以是非固定频率,甚至可以是非周期的。MDIO接口只是会在MDC时钟的上升沿进行采样,而并不在意MDC时钟的频率(类似于I2C接口)。MDIO是一个PHY的管理接口,用来读/写PHY的寄存器,以控制PHY的行为或获取PHY的状态,MDC为MDIO提供时钟。

MAC读取PHY的寄存器:

MAC向PHY的寄存器写入数据:

三.代码实现

原厂提供了芯片RTL8306M的读写逻辑,需要自己实现gpio模拟MDC/MDIO和驱动入口。

Kconfig:

config RTL8309
	tristate "RTL8309 driver"
	default m
	help
		Enable this driver will support network switch control

Makefile:

# SPDX-License-Identifier: GPL-2.0-only
#
# Makefile for the Realtek network device drivers.
#

obj-$(CONFIG_RTL8309)	+= rtl8309.o
rtl8309-objs := rtl8309_main.o mdcmdio.o rtk_api.o rtl8309n_asicdrv.

DTS配置:

rtl_8309: rtl-8309{
		status ="okay";
		compatible = "rtl8309";
        pinctrl-names = "default";
        pinctrl-0 = <&rtl8309_mdio_pin
                 &rtl8309_mdc_pin>;
        rtl8309-mdio-gpios = <&gpio3 RK_PC3 GPIO_ACTIVE_HIGH>;
        rtl8309-mdc-gpios = <&gpio3 RK_PC2 GPIO_ACTIVE_HIGH>;
	};

    rtl-8309 {
        rtl8309_mdio_pin:rtl8309-mdio-gpios{
            rockchip,pins = <3 RK_PC3 RK_FUNC_GPIO &pcfg_pull_up>;
        };
        rtl8309_mdc_pin:rtl8309-mdc-gpios{
            rockchip,pins = <3 RK_PC2 RK_FUNC_GPIO &pcfg_pull_up>;
        };
    };

 实现一个杂散类设备RTL8306M:

#include <linux/miscdevice.h>
#include <linux/cdev.h>
#include <linux/device.h>
#include <linux/fs.h>
#include <linux/i2c-dev.h>
#include <linux/i2c.h>
#include <linux/init.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/notifier.h>
#include <linux/slab.h>
#include <linux/uaccess.h>
#include <linux/compat.h>
#include <linux/printk.h>
#include <linux/kobject.h>
#include <linux/version.h>
#include <linux/kthread.h>
#include <linux/gpio.h>
#include <linux/of_gpio.h>
#include <linux/of.h>
#include <linux/of_device.h>
#include <linux/platform_device.h>

#include "rtk_api.h"
#include "rtl8309_main.h"

#include <linux/delay.h>

#define DRIVER_NAME "RTL8309"

#define RTL8309_DEBUG	1
#if RTL8309_DEBUG
#define DBG(format, args...)					\
	printk(KERN_DEBUG "%s: " format, DRIVER_NAME, ##args)
#define ERR(format, args...)					\
	printk(KERN_ERR "%s: " format, DRIVER_NAME, ##args)
#define WARNING(format, args...)				\
	printk(KERN_WARN "%s: " format, DRIVER_NAME, ##args)
#define INFO(format, args...)					\
	printk(KERN_INFO "%s: " format, DRIVER_NAME, ##args)
#else
#define DBG(format, args...)
#define ERR(format, args...)
#define WARNING(format, args...)
#define INFO(format, args...)
#endif

#define LINK_UP			1		
#define LINK_DOWN		2
#define LINK_UNKNOW		3

int PRE_PORT_STATUS[5]={0,0,0,0,0};

struct rtl8309_dev {
	struct device *dev;
	struct device sys_dev;
	struct gpio_desc *rtl8309_mdio;
	struct gpio_desc *rtl8309_mdc;
    struct delayed_work check_status_work;
	struct mutex status_lock;
	u32 link_status;
};

struct rtl8309_dev *g_rtl8309;
struct rtl8309_dev *rtl8309;

void setGpioDirection(int gpio, uint32_t  dir)
{
	if(gpio == IST_GPIO_RTL8309_MDIO){
		if(dir){
			gpiod_direction_output(g_rtl8309->rtl8309_mdio,1);
		}else{
			gpiod_direction_input(g_rtl8309->rtl8309_mdio);
		}
	}else if(gpio == IST_GPIO_RTL8309_MDC){
		if(dir){
			gpiod_direction_output(g_rtl8309->rtl8309_mdc,1);
		}else{
			gpiod_direction_input(g_rtl8309->rtl8309_mdc);
		}
	}
}

void setGpioOutput(int gpio, uint32_t out )
{
	if(gpio == IST_GPIO_RTL8309_MDIO){
		gpiod_set_value(g_rtl8309->rtl8309_mdio, out);
	}else if(gpio == IST_GPIO_RTL8309_MDC){
		gpiod_set_value(g_rtl8309->rtl8309_mdc, out);
	}
}

void getGpioInput(int gpio,uint32_t *in)
{
	if(gpio == IST_GPIO_RTL8309_MDIO){
		*in = gpiod_get_value(g_rtl8309->rtl8309_mdio);
	}else if(gpio == IST_GPIO_RTL8309_MDC){
		*in = gpiod_get_value(g_rtl8309->rtl8309_mdc);
	}
}

static long rtl8309_ioctl(struct file *file, uint32_t cmd, unsigned long arg)
{
    return 0;
}

static ssize_t rtl8309_write(struct file *file, const char __user *buf,
                 size_t size, loff_t *ppos)
{
    return 1;
}

static ssize_t rtl8309_read(struct file *file, char __user *buf, size_t size,
                loff_t *ppos)
{
    return 1;
}

static void check_link_status(struct work_struct *work)
{
	struct delayed_work *dwork = to_delayed_work(work);
	struct rtl8309_dev *rtl8309 =
		container_of(dwork, struct rtl8309_dev, check_status_work);

	int PORT_ENABLE[5]={0,1,0,1,0};
	int PORT_STATUS[5]={0,0,0,0,0};
	int status = 0;
	int i = 0;
	rtk_port_linkStatus_t pLinkStatus = PORT_LINKDOWN;
	rtk_port_speed_t pSpeed = PORT_SPEED_10M;
	rtk_port_duplex_t pDuplex = PORT_HALF_DUPLEX;

    mutex_lock(&rtl8309->status_lock);
	for(i=0;i<5;i++){
		if(PORT_ENABLE[i] == 1){
			if(rtk_port_phyStatus_get((rtk_port_t)i, &pLinkStatus, &pSpeed, &pDuplex) !=0 ){
				printk("rtk_port_phyStatus_get error:%d",i);
				break;
			}
			printk("GetEthStatus, port= %d, pLinkStatus= %d, pSpeed= %d, pDuplex= %d \n",i, pLinkStatus, pSpeed, pDuplex);
			if(pLinkStatus == PORT_LINKUP){
				PORT_STATUS[i] = 1;
				printk("port_status\n");
				if(PRE_PORT_STATUS[i] != PORT_STATUS[i]){
					printk("port[%d] status changed, set led mode\n",i);
					if(pSpeed == PORT_SPEED_100M){
						printk("port_speed\n");
					}else{
						printk("port_speed error\n");
					}
				}
			}else{
				PORT_STATUS[i] = 0;
				printk("port_status\n");
			}
			PRE_PORT_STATUS[i] = PORT_STATUS[i];
			msleep(10);
		}
	}
	mutex_unlock(&rtl8309->status_lock);
	for(i=0;i<5;i++){
		status = status | PORT_STATUS[i];
	}
	if(status == 0)
		rtl8309->link_status =  LINK_DOWN;
	else if(status == 1)
		rtl8309->link_status =  LINK_UP;
	else 
		rtl8309->link_status =  LINK_UNKNOW;

	schedule_delayed_work(&rtl8309->check_status_work, msecs_to_jiffies(5000));
}

static ssize_t rtl8309_status_read(struct device *dev,
				      struct device_attribute *attr, char *buf)
{
	struct rtl8309_dev *rtl8309 = g_rtl8309;

	printk("rtl8309_status_read rtl8309_status_read:%d\n",rtl8309->link_status);

	return sprintf(buf, "%d\n", rtl8309->link_status);
}

static ssize_t rtl8309_status_write(struct device *dev,
				       struct device_attribute *attr,
				       const char *buf, size_t count)
{
    printk("rtl8309_status_write rtl8309_status_write");

	return count;
}

static DEVICE_ATTR(linkstatus, 0644,
		rtl8309_status_read, rtl8309_status_write);

static const struct file_operations rtl8309_fops = {
    .owner = THIS_MODULE,
    .read = rtl8309_read,
    .write = rtl8309_write,
    .unlocked_ioctl = rtl8309_ioctl,
};

struct miscdevice rtl8309_miscdev = {
    .minor = MISC_DYNAMIC_MINOR,
    .name = "rtl8309_dev",
    .fops = &rtl8309_fops,
};

static int rtl8309_probe(struct platform_device *pdev)
{
    printk("rtl8309_probe");

	struct rtl8309_dev *rtl8309;
	int ret = 0;
	
	rtl8309 = devm_kzalloc(&pdev->dev, sizeof(*rtl8309), GFP_KERNEL);
	if (!rtl8309)
		return -ENOMEM;

	rtl8309->dev = &pdev->dev;

	rtl8309->rtl8309_mdio = devm_gpiod_get_optional(rtl8309->dev,
						     "rtl8309-mdio", GPIOD_OUT_HIGH);
	if (IS_ERR(rtl8309->rtl8309_mdio)) {
		printk("Could not get rtl8367-mdio");
		rtl8309->rtl8309_mdio = NULL;
	}

	rtl8309->rtl8309_mdc = devm_gpiod_get_optional(rtl8309->dev,
						     "rtl8309-mdc", GPIOD_OUT_HIGH);
	if (IS_ERR(rtl8309->rtl8309_mdc)) {
		printk("Could not get rtl8367-mdc ");
		rtl8309->rtl8309_mdc = NULL;
	}

	g_rtl8309 = rtl8309;

    ret = misc_register(&rtl8309_miscdev);
    if (ret) {
        ERR("rtl8309_miscdev ERROR: could not register rtl8309_miscdev device\n");
        return ret;
    }

	ret = device_create_file(rtl8309_miscdev.this_device,
				&dev_attr_linkstatus);
	if (ret) {
		printk("failed to create attr linkstatus");
		return ret;
	}

	mutex_init(&rtl8309->status_lock);

	INIT_DELAYED_WORK(&rtl8309->check_status_work, check_link_status);
	schedule_delayed_work(&rtl8309->check_status_work, msecs_to_jiffies(5000));
		
    return 0;
}

static int rtl8309_remove(struct platform_device *client)
{

    return 0;
}

static const struct of_device_id rtl8309_of_match[] = {
    { .compatible = "rtl8309" },
    {}
};
MODULE_DEVICE_TABLE(of, rtl8309_of_match);

static struct platform_driver rtl8309_driver = {
    .probe = rtl8309_probe,
    .remove = rtl8309_remove,
    .driver = {
        .owner = THIS_MODULE,
        .name = DRIVER_NAME,
        .of_match_table = of_match_ptr(rtl8309_of_match),
    },
};

static int __init rtl8309_init(void)
{
    int ret = platform_driver_register(&rtl8309_driver);
    if (ret != 0) {
        pr_err("Failed to register example driver: %d\n", ret);
        return ret;
    }
    pr_info("Example driver initialized\n");
    return 0;
}

static void __exit rtl8309_exit(void)
{
    platform_driver_unregister(&rtl8309_driver);
    pr_info("Example driver exited\n");
}

module_init(rtl8309_init);
module_exit(rtl8309_exit);


MODULE_DESCRIPTION("rtl8309 GPIO Switch");
MODULE_AUTHOR("Zewei Ye <yezw@ist.com.hk>");
MODULE_LICENSE("GPL v2");

函数说明:rtk_port_phyStatus_get为原厂提供的读取RTL8306M芯片状态的接口,根据返回的状态判断是否有百兆网。

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

HarmonyOS开发实战:如何实现一个运动排名榜页面

HarmonyOS开发实战&#xff1a;如何实现一个运动排名榜页面 代码仓库&#xff1a; 运动排名榜页面 项目介绍 本项目使用声明式语法和组件化基础知识&#xff0c;搭建一个可刷新的排行榜页面。在排行榜页面中&#xff0c;使用循环渲染控制语法来实现列表数据渲染&#xff0c;…

linux添加环境变量

一、查看当前环境变量 echo $PATH 二、将工作空间添加到环境变量&#xff0c;vim是编辑器&#xff0c;可以换成别的编辑器&#xff0c;vim编辑器的使用法可以百度一下 vim ~/.bashrc编辑器添加&#xff1a; source ~/scan_ws/devel/setup.bash

os功能模板

【 一 】简介 os 就是 “operating system” 的缩写&#xff0c;顾名思义&#xff0c;os 模块提供的就是各种 Python 程序与操作系统进行交互的接口。通过使用 os 模块&#xff0c;一方面可以方便地与操作系统进行交互&#xff0c;另一方面页可以极大增强代码的可移植性。如果该…

深入理解JVM虚拟机第三十二篇:详解JVM当中本地方法栈

😉😉 欢迎加入我们的学习交流群呀! ✅✅1:这是孙哥suns给大家的福利! ✨✨2:我们免费分享Netty、Dubbo、k8s、Mybatis、Spring等等很多应用和源码级别的高质量视频和笔记资料,你想学的我们这里都有! 🥭🥭3:QQ群:583783824 📚📚 工作VX:BigTreeJava 拉你…

Python框架篇(5):FastApi-中间件使用

1.介绍 1.1 官网介绍 "中间件"是一个函数,它在每个请求被特定的路径操作处理之前,以及在每个响应返回之前工作. 它接收你的应用程序的每一个 请求. 然后它可以对这个 请求做一些事情或者执行任何需要的代码. 然后它将 请求传递给应用程序的其他部分 (通过某种 路径操…

全球汽车行业的数字化转型:产品和后端的渐进之旅

如何管理汽车行业的数字化转型?在我们本篇文章中了解更多有关如何设定长期目标的信息。 正在改变汽车行业的26个数字化主题 最近一篇关于汽车行业数字化转型的论文确定了26个数字技术主题&#xff08;论文详情请点击阅读原文&#xff09;&#xff0c;分为三个主要集群: 1)驾驶…

添加E1000网卡进行测试,只有VMXNET3性能的四分之一

正文共&#xff1a;1444 字 14 图&#xff0c;预估阅读时间&#xff1a;2 分钟 我们前面介绍了VMware ESXi 6.7中的适配器类型性能&#xff08;VMWare ESXi中&#xff0c;不同的虚拟网卡性能竟然能相差三倍&#xff01;&#xff09;&#xff0c;当时的配置项主要为E1000e和VMXN…

【LangChain学习之旅】—(3) LangChain快速构建本地知识库的智能问答系统

【LangChain学习之旅】—&#xff08;3&#xff09; LangChain快速构建本地知识库的智能问答系统 项目及实现框架开发框架核心实现机制数据准备及加载加载文本文本的分割向量数据库存储文本的“嵌入”概念向量数据库概念 相关信息获取RetrievalQA生成回答并展示示例小结 Refere…

MathBuddyGUI:MATLAB多功能计算器,2060行代码

是我做的一个MATLAB课设&#xff0c;是一个带画图、输出模式转换、简单控制系统仿真等功能的计算器。练习GUI编程用。 仓库链接&#xff1a; MathBuddyGUI: MATLAB课设&#xff0c;一个带画图、输出模式转换、简单控制系统仿真等功能的计算器&#xff0c;练习GUI编程用。 (gi…

C++——C++11(1)

时至今日&#xff0c;C标准已经到了C23&#xff0c;但是你要说哪一次提出的标准最经 典&#xff0c;那C11一定会被人提及&#xff0c;C11带来了数量可观的变化&#xff0c;其中包 含了约140个新特性&#xff0c;以及对C03标准中约600个缺陷的修正&#xff0c;这使得 C11更像是从…

在用 App 设计工具创建的 App 内共享数据

目录 定义属性 访问属性 示例 共享绘图数据和下拉列表选择 使用属性是在 App 内共享数据的最佳方法&#xff0c;因为属性可供 App 内的所有函数和回调访问。所有 UI 组件都是属性&#xff0c;因此可以使用以下语法来访问和更新回调中的 UI 组件&#xff1a; app.Component…

Java 第12章 异常 本章作业

1 编程 两数相除的异常处理 各自属于哪些异常&#xff1a; 数据格式不正确 NumberformatException 缺少命令行参数 ArrayIndexOutOfBoundsException 除0异常处理 ArithmeticException ArrayIndexOutOfBoundsException 为数组下标越界时会抛出的异常&#xff0c;可以在检测到命…

计算机网络知识点

计算机网络中的OSI模型 OSI模型是指“国际标准化组织(SO)”提出的使各种计算机在世界范围内互通互联的网络标准框架简称开放系统互联参考模型 (OSI)。 七层模型&#xff1a;应用层、表示层、会话层、传输层、网络层&#xff08;IP协议、RARP协议、ARP协议、CIDR协议&#xff0…

0x31 质数

0x31 质数 定义&#xff1a; 若一个正整数无法被除了1和它自身之外的任何自然数整除&#xff0c;则称该数为质数&#xff08;或素数&#xff09;&#xff0c;否则则称该正整数为合数。 在整个自然数集合中&#xff0c;质数的数量不多&#xff0c;分布比较稀疏&#xff0c;对…

机器学习项目精选 第一期:超完整数据科学资料合集

大噶吼&#xff0c;不说废话&#xff0c;分享一波我最近看过并觉得非常硬核的资源&#xff0c;包括Python、机器学习、深度学习、大模型等等。 1、超完整数据科学资料合集 地址&#xff1a;https://github.com/krishnaik06/The-Grand-Complete-Data-Science-Materials Pytho…

strlen的三种模拟实现方法

首先&#xff0c;我们要了解strlen函数的参数以及返回值&#xff0c;还有使用方法。 1. 计数器方法 #include <stdio.h>size_t my_strlen(const char* str) {int count 0;while (*str) {count;}return count; } int main() {char arr[] "abcdef";int len …

专科论文降重修改技巧 神码ai

大家好&#xff0c;今天来聊聊专科论文降重修改技巧&#xff0c;希望能给大家提供一点参考。 以下是针对论文重复率高的情况&#xff0c;提供一些修改建议和技巧&#xff0c;可以借助此类工具&#xff1a; 专科论文降重修改技巧 在专科论文的撰写过程中&#xff0c;查重率是一…

高质量C/C++代码心得

写出高质量的C/C代码是一个涉及多方面的任务&#xff0c;它要求程序员不仅具备扎实的语言基础&#xff0c;还需要掌握一系列的软件设计和开发原则。下面将详细介绍如何写出高质量的C/C代码&#xff0c;并通过10个例子进行具体阐述。 一、编码规范 编写高质量的代码&#xff…

时序预测 | Python实现LSTM-Attention-XGBoost组合模型电力需求预测

时序预测 | Python实现LSTM-Attention-XGBoost组合模型电力需求预测 目录 时序预测 | Python实现LSTM-Attention-XGBoost组合模型电力需求预测预测效果基本描述程序设计参考资料预测效果 基本描述 该数据集因其每小时的用电量数据以及 TSO 对消耗和定价的相应预测而值得注意,从…

力扣刷题-二叉树-二叉树左叶子之和

404 左叶子之和 给定二叉树的根节点 root &#xff0c;返回所有左叶子之和。 示例 1&#xff1a; 输入: root [3,9,20,null,null,15,7] 输出: 24 解释: 在这个二叉树中&#xff0c;有两个左叶子&#xff0c;分别是 9 和 15&#xff0c;所以返回 24 思路 迭代法 迭代法理解…