linux USB驱动分析(一)USB PHY驱动分析

关键词:linux、驱动、usb、phy

同以太网类似,USB芯片也分为Host Controller部分(主机控制器/设备控制器)和PHY部分(收发器) 两大部分组成。

USB 的 PHY 与以太网的 PHY 类似,用于数字信号和电气信号的转换。

主机控制器Controller部分主要实现USB的协议和控制,内部逻辑主要有 MAC层,CSR层,FIFO层等。

  1. MAC层实现安装USB协议进行数据包打包和解包,并把数据按照 UTMI/ULPI总线格式发送给PHY。
  2. FIFO控制层主要是和DDR进行数据交互,控制USB从DDR搬运数据的通道

USB PHY负责最底层的信号转换,作用类似于网口的PHY。主要实现 并转串的功能,把控制器通过 UTMI或ULPI总线传递过来的并行数据 转换为串行数据,再通过差分数据线输出到USB接口。或反之。

总之USB芯片内部实现的功能就是接受软件的控制,进而从内存搬运数据并按照USB协议进行数据打包,并串转换后输出到芯片外部。或者从芯片外部接收差分数据信号,串并转换后进行数据打包并写到内存中。

以Controller 和 PHY 都被封装到SOC为例,如图

  1. VBUS :电压线,主机利用VBUS给USB设备提供工作电压。
  2. D+ : 正向传送数据 数据线
  3. D-: 反向传送数据 数据线

一般来说如果芯片的usb phy封装在芯片内,基本采用UTMI+的接口。不封装到芯片内的一般采用ULPI接口,这样可以降低pin的数量。

如下图是嵌入式设备上的USB系统,其中SOC内嵌了USB控制器,USB 收发器(PHY)没有封装到芯片内。该控制器支持4条总线和3中操作模式。

  • 总线1工作在主机模式下,通过USB收发器(PHY) 和 A型接口( USB Type A)连接。USB Type A常用于个人电脑PC及消费类电子产品中,用于连接键盘,鼠标等外设
  • 总线2 也工作在主机模式下,只不过它的USB收发器(PHY)连接的是内嵌USB设备,如打印机等
  • 总线3工作在设备模式下,通过USB收发器(PHY) 和 B型接口(USB Type B)连接。B型接口通过一条 B-A线和主机连接。在这种模式下,该嵌入式设备可以当做USB从设备使用。同PC机相比,嵌入式设备(如MP3,手机等)作为USB的设备端,所以 大部分嵌入式设备 除了包含主机控制器之外,还包含USB设备控制器。
  • 总线4接的是 OTG(On-The-Go)控制器,既可以做主机,也可以作从机,与前三种总线不同,总线4的USB收发器是智能的,能够通过I2C 和处理器交换控制信息,USB OTG 收发器(PHY)的另一端和Mini-AB OTG接口相连。如果两个设备都支持OTG,他们不需要作为主机的计算机介入就可以直接通信。

USB 主机控制器分为以下几种:

  • UHCI(Universal Host Controller Interface 通用主机控制器接口) 该标准是英特尔提出。
  • OHCI(Open Host Controller Interface 开放主机控制器接口)该接口是康柏和微软等公司提出,兼容OHCI的控制器硬件智能程度比UHCI高。
  • EHCI(Enhanced Host Controller Interface 增强型主机控制器接口) 该主机控制器支持高速的USB2.0S设备。为支持低速的USB设备,该控制器通常同时包含UHCI 和 OHCI控制器。
  • USB OTG控制器,这类控制器在嵌入式微控制器领域越来越受欢迎,由于采用了OTG控制器,每个通信终端 即能作主机也能作从机,设备可以根据功能需要在主机模式和设备模式之间任意切换。

主机控制器内嵌了一个叫 根集线器 的硬件。很重要!!!!

USB集线器又称为USB Hub,用于拓展计算机USB接口。计算机主板上对外往往提供多个USB接口,这些接口往往都是通过主板上的USB集线器芯片来拓展出来的。在USB总线通信协议中,通过设备描述符和接口描述符来判断该USB是否为USB集线器。

USB 根集线器(USB Root Hub)指的是直接连接到USB主控制器上的USB Hub。USB根集线器供电与USB主控器供电来源相同。USB总线中只有USB主机和USB集线器可以向外部供电。

USB PHY驱动

以 RK3128为例,其USB PHY 使用的是 USB2.0 PHY ,使用的是 Innosilicon 的IP ,对应的驱动文件为linux-4.4 \drivers\phy\rockchip\phy-rockchip-inno-usb2.c。

Innosilicon USB2.0 PHY 的硬件框架如下图 2-1 所示,主要包括五个子模块:Transceiver block,PLL clock multiplier,digital UTMI+ core,automatic test functionality,OTG Circuitry(optional)。

rk3128 USB PHY 设备树配置如下:

	grf: syscon@20008000 {	//System Controller Registers R/W driver,syscon参见内核文档:Documentation\devicetree\bindings\mfd\syscon.txt
		compatible = "rockchip,rk3128-grf", "syscon", "simple-mfd";
		reg = <0x20008000 0x1000>;
		#address-cells = <1>;
		#size-cells = <1>;
		u2phy: usb2-phy@17c {
			compatible = "rockchip,rk3128-usb2phy";
			reg = <0x017c 0x0c>;
			clocks = <&cru SCLK_OTGPHY0>;
			clock-names = "phyclk";
			#clock-cells = <0>;
			clock-output-names = "usb480m_phy";
			assigned-clocks = <&cru SCLK_USB480M>;
			assigned-clock-parents = <&u2phy>;
			status = "okay";

			u2phy_otg: otg-port {	#usb otg 的设备树配置
				#phy-cells = <0>;
				interrupts = <GIC_SPI 35 IRQ_TYPE_LEVEL_HIGH>,
					     <GIC_SPI 51 IRQ_TYPE_LEVEL_HIGH>,
					     <GIC_SPI 52 IRQ_TYPE_LEVEL_HIGH>;
				interrupt-names = "otg-bvalid", "otg-id",
						  "linestate";
				status = "okay";
			};

			u2phy_host: host-port {	#usb host 的设备树配置
				#phy-cells = <0>;
				interrupts = <GIC_SPI 53 IRQ_TYPE_LEVEL_HIGH>;
				interrupt-names = "linestate";
				status = "okay";
			};
		};
	};

 设备树 u2phy 对应 /sys/devices/platform/20008000.syscon/20008000.syscon:usb2-phy@17c :

root@mxlos:/sys/devices/platform/20008000.syscon/20008000.syscon:usb2-phy@17c# ll
total 0
drwxr-xr-x    5 root     root             0 Jan  1 00:53 ./
drwxr-xr-x    4 root     root             0 Jan  1 00:53 ../
lrwxrwxrwx    1 root     root             0 Jan  1 01:35 driver -> ../../../../bus/platform/drivers/rockchip-usb2phy/
-rw-r--r--    1 root     root          4096 Jan  1 01:35 driver_override
drwxr-xr-x    3 root     root             0 Jan  1 00:53 extcon/
-r--r--r--    1 root     root          4096 Jan  1 01:35 modalias
lrwxrwxrwx    1 root     root             0 Jan  1 01:35 of_node -> ../../../../firmware/devicetree/base/syscon@20008000/usb2-phy@17c/
-rw-r--r--    1 root     root          4096 Jan  1 01:35 otg_mode
drwxr-xr-x    4 root     root             0 Jan  1 00:53 phy/
drwxr-xr-x    2 root     root             0 Jan  1 01:35 power/
lrwxrwxrwx    1 root     root             0 Jan  1 01:35 subsystem -> ../../../../bus/platform/
-rw-r--r--    1 root     root          4096 Jan  1 00:53 uevent

root@mxlos:/sys/devices/platform/20008000.syscon/20008000.syscon:usb2-phy@17c# cat modalias 
of:Nusb2-phyT<NULL>Crockchip,rk3128-usb2phy
root@mxlos:/sys/devices/platform/20008000.syscon/20008000.syscon:usb2-phy@17c# cat otg_mode 
otg

root@mxlos:/sys/devices/platform/20008000.syscon/20008000.syscon:usb2-phy@17c# cat uevent 
DRIVER=rockchip-usb2phy
OF_NAME=usb2-phy
OF_FULLNAME=/syscon@20008000/usb2-phy@17c
OF_COMPATIBLE_0=rockchip,rk3128-usb2phy
OF_COMPATIBLE_N=1
MODALIAS=of:Nusb2-phyT<NULL>Crockchip,rk3128-usb2phy

root@mxlos:/sys/devices/platform/20008000.syscon/20008000.syscon:usb2-phy@17c# cd extcon/

root@mxlos:/sys/devices/platform/20008000.syscon/20008000.syscon:usb2-phy@17c/extcon# ls
extcon0

root@mxlos:/sys/devices/platform/20008000.syscon/20008000.syscon:usb2-phy@17c/extcon# cd extcon0/

root@mxlos:/sys/devices/platform/20008000.syscon/20008000.syscon:usb2-phy@17c/extcon/extcon0# ls
cable.0    cable.1    cable.2    cable.3    cable.4    cable.5    cable.6    device     name       power      state      subsystem  uevent

root@mxlos:/sys/devices/platform/20008000.syscon/20008000.syscon:usb2-phy@17c/extcon/extcon0# cd ../..

root@mxlos:/sys/devices/platform/20008000.syscon/20008000.syscon:usb2-phy@17c# ls
driver           driver_override  extcon           modalias         of_node          otg_mode         phy              power            subsystem        uevent

root@mxlos:/sys/devices/platform/20008000.syscon/20008000.syscon:usb2-phy@17c# cd phy/

root@mxlos:/sys/devices/platform/20008000.syscon/20008000.syscon:usb2-phy@17c/phy# ls
phy-20008000.syscon:usb2-phy@17c.0  phy-20008000.syscon:usb2-phy@17c.1

root@mxlos:/sys/devices/platform/20008000.syscon/20008000.syscon:usb2-phy@17c/phy# cd phy-20008000.syscon\:usb2-phy@17c.0/

root@mxlos:/sys/devices/platform/20008000.syscon/20008000.syscon:usb2-phy@17c/phy/phy-20008000.syscon:usb2-phy@17c.0# ls
device     of_node    power      subsystem  uevent

root@mxlos:/sys/devices/platform/20008000.syscon/20008000.syscon:usb2-phy@17c/phy/phy-20008000.syscon:usb2-phy@17c.0# cat uevent 
OF_NAME=otg-port
OF_FULLNAME=/syscon@20008000/usb2-phy@17c/otg-port
OF_COMPATIBLE_N=0

root@mxlos:/sys/devices/platform/20008000.syscon/20008000.syscon:usb2-phy@17c/phy/phy-20008000.syscon:usb2-phy@17c.0# cd ..

root@mxlos:/sys/devices/platform/20008000.syscon/20008000.syscon:usb2-phy@17c/phy# cd phy-20008000.syscon\:usb2-phy@17c.1/

root@mxlos:/sys/devices/platform/20008000.syscon/20008000.syscon:usb2-phy@17c/phy/phy-20008000.syscon:usb2-phy@17c.1# ls
device     of_node    power      subsystem  uevent

root@mxlos:/sys/devices/platform/20008000.syscon/20008000.syscon:usb2-phy@17c/phy/phy-20008000.syscon:usb2-phy@17c.1# cat uevent 
OF_NAME=host-port
OF_FULLNAME=/syscon@20008000/usb2-phy@17c/host-port
OF_COMPATIBLE_N=0

root@mxlos:/sys/devices/platform/20008000.syscon/20008000.syscon:usb2-phy@17c/phy/phy-20008000.syscon:usb2-phy@17c.1# 

以 RK3128为例,其USB PHY 使用的是 USB2.0 PHY ,使用的是 Innosilicon 的IP ,对应的驱动文件为linux-4.4 \drivers\phy\rockchip\phy-rockchip-inno-usb2.c,对应的平台驱动为 struct platform_driver rockchip_usb2phy_driver:

static struct platform_driver rockchip_usb2phy_driver = {
	.probe		= rockchip_usb2phy_probe,
	.driver		= {
		.name	= "rockchip-usb2phy",
		.pm	= ROCKCHIP_USB2PHY_DEV_PM,
		.of_match_table = rockchip_usb2phy_dt_match,
		/*
		static const struct of_device_id rockchip_usb2phy_dt_match[] = {
			{ .compatible = "rockchip,rk3128-usb2phy", .data = &rk312x_phy_cfgs },
			{ .compatible = "rockchip,rk3308-usb2phy", .data = &rk3308_phy_cfgs },
			{ .compatible = "rockchip,rk3399-usb2phy", .data = &rk3399_phy_cfgs },
			{}
		};		
		*/
	},
};
module_platform_driver(rockchip_usb2phy_driver);	
#define module_platform_driver(__platform_driver) \
	module_driver(__platform_driver, platform_driver_register, \
			platform_driver_unregister)

RK3128 USB PHY 驱动 struct platform_driver rockchip_usb2phy_driver 与设备 /sys/devices/platform/20008000.syscon/20008000.syscon:usb2-phy@17c 匹配时调用 probe = struct platform_driver rockchip_usb2phy_driverb.probe= rockchip_usb2phy_probe,其执行过程如下:

static int rockchip_usb2phy_probe(struct platform_device *pdev)->
	struct rockchip_usb2phy *rphy = devm_kzalloc(dev, sizeof(*rphy), GFP_KERNEL);
	const struct of_device_id *match = of_match_device(dev->driver->of_match_table, dev);
	rphy->grf = syscon_node_to_regmap(dev->parent->of_node);
	of_property_read_u32(np, "reg", &reg);
	rphy->dev = dev;
	phy_cfgs = match->data;
	rphy->chg_state = USB_CHG_STATE_UNDEFINED;
	rphy->chg_type = POWER_SUPPLY_TYPE_UNKNOWN;
	rphy->edev_self = false;
	platform_set_drvdata(pdev, rphy);
	ret = rockchip_usb2phy_extcon_register(rphy)->
		struct extcon_dev *edev = devm_extcon_dev_allocate(rphy->dev, rockchip_usb2phy_extcon_cable);
		ret = devm_extcon_dev_register(rphy->dev, edev);
		rphy->edev = edev;
	struct rockchip_usb2phy *rphy->clk = of_clk_get_by_name(np, "phyclk");
	clk_prepare_enable(rphy->clk);	//使用能 PHY 时钟
	ret = rphy->phy_cfg->phy_tuning(rphy);
	index = 0;
	for_each_available_child_of_node(np, child_np) {
		struct rockchip_usb2phy_port *rport = &rphy->ports[index];
		struct phy *phy;

		/* This driver aims to support both otg-port and host-port */
		if (of_node_cmp(child_np->name, "host-port") && of_node_cmp(child_np->name, "otg-port"))
			goto next_child;
		/*
		static const struct phy_ops rockchip_usb2phy_ops = {
			.init		= rockchip_usb2phy_init,
			.exit		= rockchip_usb2phy_exit,
			.power_on	= rockchip_usb2phy_power_on,
			.power_off	= rockchip_usb2phy_power_off,
			.set_mode	= rockchip_usb2phy_set_mode,
			.owner		= THIS_MODULE,
		};		
		*/
		phy = devm_phy_create(dev, child_np, &rockchip_usb2phy_ops);	//创建 struct phy *phy
			ptr = devres_alloc(devm_phy_consume, sizeof(*ptr), GFP_KERNEL);
			struct phy *phy = phy_create(dev, node, ops)->
				struct phy *phy = kzalloc(sizeof(*phy), GFP_KERNEL);
				id = ida_simple_get(&phy_ida, 0, 0, GFP_KERNEL);
				device_initialize(&phy->dev);
				//phy_class = class_create(THIS_MODULE, "phy"); //对应目录 /sys/class/phy
				phy->dev.class = phy_class;//对应目录 /sys/class/phy
				phy->dev.parent = dev;
				phy->dev.of_node = node ?: dev->of_node;
				phy->id = id;
				phy->ops = ops= struct phy_ops rockchip_usb2phy_ops
				//对应 /sys/class/phy/phy-20008000.syscon:usb2-phy@17c.0 和 /sys/class/phy/phy-20008000.syscon:usb2-phy@17c.1
				ret = dev_set_name(&phy->dev, "phy-%s.%d", dev_name(dev), id);	
				phy->pwr = regulator_get_optional(&phy->dev, "phy");
				ret = device_add(&phy->dev);
			return phy;
		rport->phy = phy;
		phy_set_drvdata(rport->phy, rport);
		/* initialize otg/host port separately : 对 usb otg 和 usb host 分开处理 */
		if (!of_node_cmp(child_np->name, "host-port")) 
		{
			ret = rockchip_usb2phy_host_port_init(rphy, rport,child_np)->
				struct rockchip_usb2phy_port *rport->port_id = USB2PHY_PORT_HOST = 1;	
				rport->low_power_en = of_property_read_bool(child_np, "rockchip,low-power-mode");
				INIT_DELAYED_WORK(&rport->sm_work, rockchip_usb2phy_sm_work);
				rport->ls_irq = of_irq_get_byname(child_np, "linestate"); //中断
				ret = devm_request_threaded_irq(rphy->dev, rport->ls_irq, NULL,
								rockchip_usb2phy_linestate_irq,
								IRQF_ONESHOT,
								"rockchip_usb2phy", rport);
/*
root@mxlos:~# cat /proc/interrupts 
           CPU0       CPU1       CPU2       CPU3       
187:          0          0          0          0       GIC  67 Level     rockchip_usb2phy_bvalid
188:          0          0          0          0       GIC  84 Level     rockchip_usb2phy
189:          0          0          0          0       GIC  83 Level     rockchip_usb2phy_id
190:          1          0          0          0       GIC  85 Level     rockchip_usb2phy
root@mxlos:~#
*/
				ret = property_enable(base, &rport->port_cfg->phy_sus, true);
		} 
		else 
		{
			ret = rockchip_usb2phy_otg_port_init(rphy, rport,child_np)->
				struct rockchip_usb2phy_port *rport->port_id = USB2PHY_PORT_OTG = 0;	
				rport->state = OTG_STATE_UNDEFINED;
				rport->vbus = devm_regulator_get_optional(&rport->phy->dev, "vbus");
				rport->mode = of_usb_get_dr_mode_by_phy(child_np, -1);
				wake_lock_init(&rport->wakelock, WAKE_LOCK_SUSPEND, "rockchip_otg");
				INIT_DELAYED_WORK(&rport->bypass_uart_work, rockchip_usb_bypass_uart_work);
				INIT_DELAYED_WORK(&rport->chg_work, rockchip_chg_detect_work);
				INIT_DELAYED_WORK(&rport->otg_sm_work, rockchip_usb2phy_otg_sm_work);
				rport->bvalid_irq = of_irq_get_byname(child_np, "otg-bvalid");
				ret = devm_request_threaded_irq(rphy->dev, rport->bvalid_irq,
												NULL,
												rockchip_usb2phy_bvalid_irq,
												IRQF_ONESHOT,
												"rockchip_usb2phy_bvalid",
												rport);
				rport->ls_irq = of_irq_get_byname(child_np, "linestate");
				ret = devm_request_threaded_irq(rphy->dev, rport->ls_irq, NULL,
												rockchip_usb2phy_linestate_irq,
												IRQF_ONESHOT,
												"rockchip_usb2phy", rport);
				rport->id_irq = of_irq_get_byname(child_np, "otg-id");
				ret = extcon_register_notifier(rphy->edev, EXTCON_USB_HOST, &rport->event_nb);
		}
next_child:
		/* to prevent out of boundary */
		if (++index >= rphy->phy_cfg->num_ports)
			break;
	}
	provider = devm_of_phy_provider_register(dev, of_phy_simple_xlate)->
		__devm_of_phy_provider_register((dev), THIS_MODULE, (xlate))
			struct phy_provider *phy_provider = __of_phy_provider_register(dev, owner, of_xlate)->
				struct phy_provider *phy_provider = kzalloc(sizeof(*phy_provider), GFP_KERNEL);
				phy_provider->dev = dev;
				phy_provider->owner = owner;
				phy_provider->of_xlate = of_xlate;
				list_add_tail(&phy_provider->list, &phy_provider_list);		
				/*
				USB 主机控制器使用函数 usb_create_hcd() 和 usb_add_hcd() 创建并注册,
				在函数usb_add_hcd() 中会调用函数phy_get() 遍历链表phy_provider_list 获取 USB PHY驱动中注册的 struct phy *phy
					err = usb_add_hcd(hcd, irq, IRQF_SHARED)->
						struct phy *phy = phy_get(hcd->self.controller, "usb");
							index = of_property_match_string(dev->of_node, "phy-names", string);
							struct phy *phy = _of_phy_get(dev->of_node, index)->	//获取 usb phy 驱动中注册的 struct phy
								ret = of_parse_phandle_with_args(np, "phys", "#phy-cells", index, &args);
								struct phy_provider *phy_provider = of_phy_provider_lookup(args.np)->
									list_for_each_entry(phy_provider, &phy_provider_list, list) 
				*/				
	/* Attributes */
	ret = sysfs_create_group(&dev->kobj, &usb2_phy_attr_group);
	ret = rockchip_usb2phy_clk480m_register(rphy);
	if (of_property_read_bool(np, "wakeup-source"))
		device_init_wakeup(rphy->dev, true);
	else
		device_init_wakeup(rphy->dev, false);
	

  • 39
    点赞
  • 44
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值