i.MX8MP平台开发分享(GPC控制器篇)

1.概述

整体来说,i.MX8MP中的电源是由General Power Controller (GPC) 来控制的。GPC可以提供各种电源模式的控制,如低功耗模式、深度睡眠模式等等。GPC包含两个模块,一个是系统模式控制器(SMC),控制系统的电源模式;另一个是电源门控控制器(PGC),控制模块的电源开关。详细信息请参考RM。本文重点分析其中的PGC(Power Gating Controller )。

如下图所示,每个模块都有单独的PGC进行控制,从而实现对单个模块的电源开关功能。

image-20240414102646153

PGC的上下电控制逻辑如下,所有CPU外的MIX和PU_PGCs,PGC_CPU_MAPPING会被映射到A53/M7 domain。

  1. 一个PGC可以被映射到一个domain,或者A53和M7两个domain
  2. 对于硬件上电请求,如果PGC是被分配到任意CPU域内,则直接上电
  3. 对于软件上电请求,如果PGC是被分配到任意CPU域内,则可以被对应CPU域内的软件控制上电。对应MIX_PGC_SW_PUP_REQPU_PGC_SW_PUP_REQ
  4. 对于硬件下电请求,如果PGC是被分配到一个CPU域内,则可以被对应CPU域内的软件控制下电。如果PGC是被分配到两个CPU域内,若一个CPU处于LPM模式,另一个企图下电,则可以被对应CPU域内的软件控制下电。
  5. 对于软件下电请求,同硬件下电。
  6. PGC_CPU_MAPPING由RDC控制访问。

在设备树中,pgc的定义和上面的表格保持一致,其中reg是每个pgc的idex值,设备树和驱动中的这个数值一一对应。参考include/dt-bindings/power/imx8mp-power.h

image-20240415111041818

2.gpcv2驱动分析

2.1cpu资源结构体分析

每一个芯片都有一个imx_pgc_domain_data来描述pgc的信息。这个数据结构与手册中的pgc信息保持一致。

static const struct imx_pgc_domain_data imx8mp_pgc_domain_data = {
	.domains = imx8mp_pgc_domains,
	.domains_num = ARRAY_SIZE(imx8mp_pgc_domains),
	.reg_access_table = &imx8mp_access_table,
	.pgc_regs = &imx8mp_pgc_regs,
};

其中imx8mp_pgc_domains 定义如下所示,以mlmix为例,IMX8MP_POWER_DOMAIN_MLMIX和设备树中一致。.genpd保存这个电源域在内核中的名字。

static const struct imx_pgc_domain imx8mp_pgc_domains[] = {
	[IMX8MP_POWER_DOMAIN_MIPI_PHY1] = {
		.genpd = {
			.name = "mipi-phy1",
		},
		.bits = {
			.pxx = IMX8MP_MIPI_PHY1_SW_Pxx_REQ,
			.map = IMX8MP_MIPI_PHY1_A53_DOMAIN,
		},
		.pgc = BIT(IMX8MP_PGC_MIPI1),
	},

	[IMX8MP_POWER_DOMAIN_PCIE_PHY] = {
		.genpd = {
			.name = "pcie-phy1",
		},
		.bits = {
			.pxx = IMX8MP_PCIE_PHY_SW_Pxx_REQ,
			.map = IMX8MP_PCIE_PHY_A53_DOMAIN,
		},
		.pgc = BIT(IMX8MP_PGC_PCIE),
	},
..........
  [IMX8MP_POWER_DOMAIN_MLMIX] = {
		.genpd = {
			.name = "mlmix",
		},
		.bits = {
			.pxx = IMX8MP_MLMIX_Pxx_REQ,
			.map = IMX8MP_MLMIX_A53_DOMAIN,
			.hskreq = IMX8MP_MLMIX_PWRDNREQN,
			.hskack = IMX8MP_MLMIX_PWRDNACKN,
		},
		.pgc = BIT(IMX8MP_PGC_MLMIX),
		.keep_clocks = true,
		.noc_data = {
			&imx8mp_pgc_noc_data[IMX8MP_MLMIX],
		},
	},
  
};

.bits中的寄存器位分别来自对应上下电的寄存器(GPC_PU_PGC_SW_PUP_REQ/GPC_PU_PGC_SW_PDN_REQ),domain map控制寄存器GPC_PGC_CPU_A53_MAPPING,上下电握手请求寄存器GPC_PU_PWRHSK低16位,上下电握手响应寄存器GPC_PU_PWRHSK高16位对应这些寄存器中的功能位。

const struct {
		u32 pxx;
		u32 map;
		u32 hskreq;
		u32 hskack;
	} bits;

imx8mp_access_table存储寄存器映射之后的虚拟地址。

image-20240420154124803

imx8mp_pgc_regs存储的是寄存器偏移量,分别控制domain映射、软件上下电请求和握手。

#define IMX8MP_GPC_PGC_CPU_MAPPING	0x1cc
#define IMX8MP_GPC_PU_PGC_SW_PUP_REQ	0x0d8
#define IMX8MP_GPC_PU_PGC_SW_PDN_REQ	0x0e4
#define IMX8MP_GPC_PU_PWRHSK		0x190

static const struct imx_pgc_regs imx8mp_pgc_regs = {
	.map = IMX8MP_GPC_PGC_CPU_MAPPING,
	.pup = IMX8MP_GPC_PU_PGC_SW_PUP_REQ,
	.pdn = IMX8MP_GPC_PU_PGC_SW_PDN_REQ,
	.hsk = IMX8MP_GPC_PU_PWRHSK,
};

2.2NOC优先级

imx_pgc_noc_set使用 regmap_write 函数向 NOC 寄存器映射(regmap)中的特定偏移量写入参数值以配置 NOC 控制器的行为。

  • data[i]->off + 0x8:写入优先级(priority)。
  • data[i]->off + 0xc:写入模式(mode)。
  • data[i]->off + 0x18:写入扩展控制(extctrl)。
static const struct imx_pgc_noc_data imx8mp_pgc_noc_data[] = {
	[IMX8MP_MLMIX] = {
		.off = 0x180,
		.priority = 0x80000303,
	},
	[IMX8MP_GPU2D] = {
		.off = 0x500,
		.priority = 0x80000303,
	},
	[IMX8MP_GPU3D] = {
		.off = 0x580,
		.priority = 0x80000303,
	},
	[IMX8MP_AUDIO_DSP] = {
		.off = 0x200,
		.priority = 0x80000303,
	},
	[IMX8MP_AUDIO_SDMA2_PER] = {
		.off = 0x280,
		.priority = 0x80000404,
	},
	[IMX8MP_AUDIO_SDMA2_BURST] = {
		.off = 0x300,
		.priority = 0x80000404,
	},
	[IMX8MP_AUDIO_SDMA3_PER] = {
		.off = 0x380,
		.priority = 0x80000404,
	},
	[IMX8MP_AUDIO_SDMA3_BURST] = {
		.off = 0x400,
		.priority = 0x80000404,
	},
	[IMX8MP_AUDIO_EDMA3] = {
		.off = 0x480,
		.priority = 0x80000404,
	},
};

2.3驱动设置ops

在probe函数中,遍历设备树中的pgc单元,设置每个单元的domain信息,domain->regs获取imx8mp_pgc_regs中定义的寄存器偏移量。最后设置对于每个pgc单元,它们的上下电函数分别为imx_pgc_power_upimx_pgc_power_down。因此设备树中所有的pgc对应的上下电函数都一样。

for_each_child_of_node(pgc_np, np) {
		struct platform_device *pd_pdev;
		struct imx_pgc_domain *domain;
		u32 domain_index;
		....
		.....
		domain = pd_pdev->dev.platform_data;
		domain->regmap = regmap;
		domain->regs = domain_data->pgc_regs;
		for (i = 0; i < DOMAIN_MAX_NOC; i++)
			domain->noc_data[i] = domain_data->domains[domain_index].noc_data[i];

		domain->genpd.power_on  = imx_pgc_power_up;
		domain->genpd.power_off = imx_pgc_power_down;		
}

2.4imx_pgc_power_up

函数逻辑如下:

  1. 初始化:初始化一些变量,并从给定的 genpd 指针中获取 imx_pgc_domain 结构体。
  2. PM运行时管理::pm_runtime_get_sync获取设备的运行时 PM 引用,并确保该设备处于活动状态。它会阻塞当前线程,直到设备被激活为止。通常,在访问设备之前,需要确保设备处于活动状态,以便正常操作。
  3. 调节器启用:通过regulator框架启用对应的电源。
  4. 复位控制
    • reset_control_assert施加该域的复位控制。
    • 使用 clk_bulk_prepare_enable 为该域中的所有设备启用复位时钟。
  5. 通知:使用 raw_notifier_call_chain 通知电源通知者已经转换到“时钟已启用”状态。
  6. 延迟:使用 udelay 引入延迟,以便复位传播一段时间。
  7. 域上电
    • 如果 domain->bits.pxx 字段已设置,则通过更新寄存器中的对应位来为域上电
    • 使用 regmap_read_poll_timeout 等待寄存器中的特定位清除。
    • 对于 domain->pgc 位图中的每个设置的位,禁用电源控制
  8. 复位控制去除:解除该域的复位控制。
  9. 通知:使用 raw_notifier_call_chain 通知电源通知者已经转换到“ADB400”状态。
  10. ADB400 上电:如果 domain->bits.hskreq 已设置,则通过更新寄存器中的某些位来请求 ADB400 上电。
  11. 时钟禁用:如果 domain->keep_clocks 未设置,则使用 clk_bulk_disable_unprepare 禁用和取消准备域中所有设备的复位时钟。
  12. 配置NOC控制器:调用 imx_pgc_noc_set 函数,可能是为特定于电源域的配置设置一些参数。
static int imx_pgc_power_up(struct generic_pm_domain *genpd)
{
	struct imx_pgc_domain *domain = to_imx_pgc_domain(genpd);
	u32 reg_val, pgc;
	int ret;

	ret = pm_runtime_get_sync(domain->dev);
    ret = regulator_enable(domain->regulator);
	reset_control_assert(domain->reset);

	/* Enable reset clocks for all devices in the domain */
	ret = clk_bulk_prepare_enable(domain->num_clks, domain->clks);

	raw_notifier_call_chain(&genpd->power_notifiers, IMX_GPCV2_NOTIFY_ON_CLK_ENABLED, NULL);

	/* delays for reset to propagate */
	udelay(5);

	if (domain->bits.pxx) {
		/* request the domain to power up */
		regmap_update_bits(domain->regmap, domain->regs->pup,
				   domain->bits.pxx, domain->bits.pxx);
		/*
		 * As per "5.5.9.4 Example Code 4" in IMX7DRM.pdf wait
		 * for PUP_REQ/PDN_REQ bit to be cleared
		 */
		ret = regmap_read_poll_timeout(domain->regmap,
					       domain->regs->pup, reg_val,
					       !(reg_val & domain->bits.pxx),
					       0, USEC_PER_MSEC);

		/* disable power control */
		for_each_set_bit(pgc, &domain->pgc, 32) {
			regmap_clear_bits(domain->regmap, GPC_PGC_CTRL(pgc),
					  GPC_PGC_CTRL_PCR);
		}
	}

	/* delay for reset to propagate */
	udelay(5);

	reset_control_deassert(domain->reset);

	raw_notifier_call_chain(&genpd->power_notifiers, IMX_GPCV2_NOTIFY_ON_ADB400, NULL);

	/* request the ADB400 to power up */
	if (domain->bits.hskreq) {
		regmap_update_bits(domain->regmap, domain->regs->hsk,
				   domain->bits.hskreq, domain->bits.hskreq);

	}

	/* Disable reset clocks for all devices in the domain */
	if (!domain->keep_clocks)
		clk_bulk_disable_unprepare(domain->num_clks, domain->clks);

	imx_pgc_noc_set(domain);

	return 0;
}

2.5imx_pgc_power_down

  1. 初始化:初始化一些变量,并从给定的 genpd 指针中获取 imx_pgc_domain 结构体。
  2. 复位时钟启用:如果 domain->keep_clocks 未被设置,那么启用该域中所有设备的复位时钟,以准备关闭操作。
  3. 请求 ADB400 关闭:如果 domain->bits.hskreq 被设置,那么向 ADB400 发送关闭请求。等待 ADB400 确认关闭完成。
  4. 通知:使用 raw_notifier_call_chain 通知电源通知者已经将 ADB400 关闭。
  5. 关闭域:如果 domain->bits.pxx 被设置,那么启用电源控制,然后请求关闭该域。等待关闭请求的确认。
  6. 关闭复位时钟:禁用并取消准备该域中所有设备的复位时钟。
  7. 关闭调节器:如果 domain->regulator 非空,那么禁用该域关联的调节器。
  8. 释放运行时 PM 引用:使用 pm_runtime_put_sync_suspend 函数释放与该域关联的设备的运行时 PM 引用。
static int imx_pgc_power_down(struct generic_pm_domain *genpd)
{
	struct imx_pgc_domain *domain = to_imx_pgc_domain(genpd);
	u32 reg_val, pgc;
	int ret;

	/* Enable reset clocks for all devices in the domain */
	if (!domain->keep_clocks) {
		ret = clk_bulk_prepare_enable(domain->num_clks, domain->clks);
	}

	/* request the ADB400 to power down */
	if (domain->bits.hskreq) {
		regmap_clear_bits(domain->regmap, domain->regs->hsk,
				  domain->bits.hskreq);

		ret = regmap_read_poll_timeout(domain->regmap, domain->regs->hsk,
					       reg_val,
					       !(reg_val & domain->bits.hskack),
					       0, USEC_PER_MSEC);
	}

	raw_notifier_call_chain(&genpd->power_notifiers, IMX_GPCV2_NOTIFY_OFF_ADB400, NULL);

	if (domain->bits.pxx) {
		/* enable power control */
		for_each_set_bit(pgc, &domain->pgc, 32) {
			regmap_update_bits(domain->regmap, GPC_PGC_CTRL(pgc),
					   GPC_PGC_CTRL_PCR, GPC_PGC_CTRL_PCR);
		}

		/* request the domain to power down */
		regmap_update_bits(domain->regmap, domain->regs->pdn,
				   domain->bits.pxx, domain->bits.pxx);
		/*
		 * As per "5.5.9.4 Example Code 4" in IMX7DRM.pdf wait
		 * for PUP_REQ/PDN_REQ bit to be cleared
		 */
		ret = regmap_read_poll_timeout(domain->regmap,
					       domain->regs->pdn, reg_val,
					       !(reg_val & domain->bits.pxx),
					       0, USEC_PER_MSEC);
	}

	/* Disable reset clocks for all devices in the domain */
	clk_bulk_disable_unprepare(domain->num_clks, domain->clks);

	if (!IS_ERR(domain->regulator)) {
		ret = regulator_disable(domain->regulator);
	}

	pm_runtime_put_sync_suspend(domain->dev);

	return 0;
}

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

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

相关文章

项目优化方案之---实现邮箱用户登录

之前的项目中我写的基于SpringBoot和Vue的全栈项目已经实现了基本的用户接口开发&#xff0c; 不过其代码的功能单一&#xff0c;而且写的也是有不少漏洞&#xff08;基本就像刚接手的代码*山一样&#xff09; 那之后的几篇文章都来分享一下如何优化项目&#xff08;每一章都独…

【数据结构】链式二叉树(超详细)

文章目录 前言二叉树的链式结构二叉树的遍历方式二叉树的深度优先遍历前序遍历(先根遍历)中序遍历(中根遍历)后序遍历(后根遍历) 二叉树的广度优先遍历层序遍历 二叉树链式结构接口实现二叉树结点个数二叉树叶子结点个数二叉树的深度&#xff08;高度&#xff09;二叉树第k层结…

植物大战僵尸杂交版下载链接

前言 植物大战僵尸杂交版是 潜艇伟伟迷 制作并免费向大家开放畅玩并且持续更新关卡。 下载教程 1.打开作者主页&#xff1a;https://space.bilibili.com/97213827/dynamic 2.作者置顶发布的是最新版&#xff0c;直接打开链接安装就好了 3.下载链接&#xff1a;https://pan.qu…

DL-33G电流继电器 新型导轨安装 JOSEF约瑟

用途 DL-30系列电流继电器&#xff0c;用于电机、变压器和输电线的过负荷和短路保护线路中&#xff0c;作为起动元件。 技术参数 按整定值的范围来分:每整定值的动作误差不大于6% 继电器刻度极限误差不大于6%。 动作值的变差不大于6% 对于DL-31、32、33、34电流继电器的返…

【第3章】SpringBoot实战篇之登录接口(含JWT和拦截器)

文章目录 前言一、JWT1. 什么是JWT2. 使用场景3. 结构3.1 Header3.2 Payload3.3 Signature 4. 使用 二、案例1.引入库2.JwtUtils3. UserController14. ArticleController 三、拦截器1. 定义拦截器2. 注册拦截器 四、测试1. 登录2. 无token3. 有token4. 全局配置 总结 前言 前面…

vscode怎么点击路径直接跳转对应文件

在vue项目中经常要引入工具类、组件、模版等&#xff0c;想要直接去看对应文件&#xff0c;只能自己找到对应路径再去打开。 我们可用在js项目中创建一个 jsconfig.json文件&#xff0c;TS项目可以创建tsconfig.json 文件代码 {"compilerOptions": {"baseUrl&…

批量归一化(BN)和层归一化(LN)的区别

批量归一化&#xff08;Batch Normalization, BN&#xff09;和层归一化&#xff08;Layer Normalization, LN&#xff09;是深度学习中常用的两种归一化技术&#xff0c;它们主要用于解决训练过程中的内部协变量偏移问题&#xff0c;加速模型收敛和提高稳定性。 1. 为什么需要…

智能工厂生产设备实时监控技术的UI设计

智能工厂生产设备实时监控技术的UI设计

Java面试八股之死锁和饥饿的区别

死锁和饥饿的区别 定义与现象&#xff1a; 死锁&#xff08;Deadlock&#xff09;是指两个或多个线程互相等待对方持有的资源而无法继续执行的情况。每个线程至少持有一个资源&#xff0c;并尝试获取另一个由其他线程持有的资源&#xff0c;从而形成一个循环等待的僵局&#…

QAnything-1.4.01.4.1版本更新!使用指北!

久等了各位&#xff01;时隔一个多月&#xff0c;我们在4月26日和5月20日接连发布了v1.4.0和v1.4.1两个版本&#xff0c;带来了问答性能&#xff0c;解析效果等多方面的改进&#xff0c;并新增了大量的新功能和新特性 详见&#xff1a;releases 以及 使用说明 最新特性表 开发…

Android 调试桥_ADB命令

Android 调试桥 ADB全称 【Android Debug Bridge】 是Android SDK中的一个命令行工具&#xff0c;adb命令可以直接操作管理Android模拟器或真实的Android设备&#xff08;手机&#xff09; ADB的工作原理 启动一个 adb 客户端时&#xff0c;此客户端首先检查是否有已运行的 …

1961. 检查字符串是否为数组前缀 - 力扣

1. 题目 给你一个字符串 s 和一个字符串数组 words &#xff0c;请你判断 s 是否为 words 的 前缀字符串 。 字符串 s 要成为 words 的 前缀字符串 &#xff0c;需要满足&#xff1a;s 可以由 words 中的前 k&#xff08;k 为 正数 &#xff09;个字符串按顺序相连得到&#xf…

kaggle竞赛系列基于图像对水稻分类代码案例

目录 依赖环境 代码 导入依赖包 定义数据集路径&#xff1a; 创建训练集、验证集和测试集的文件夹&#xff1a; 代码的作用&#xff1a; 设置新的数据集路径与类别名称 代码的作用&#xff1a; 定义数据预处理和增强变换&#xff1a; 代码的作用&#xff1a; 定义数…

Appium自动化环境搭建保姆级教程

APP自动化测试运行环境比较复杂&#xff0c;稍微不注意安装就会失败。我见过不少朋友&#xff0c;装了1个星期&#xff0c;Appium 的运行环境还没有搭好的。 搭建环境本身不是一个有难度的工作&#xff0c;但是 Appium 安装过程中确实存在不少隐藏的比较深的坑&#xff0c;如果…

kafka-集群搭建(在docker中搭建)

文章目录 1、kafka集群搭建1.1、下载镜像文件1.2、创建zookeeper容器并运行1.3、创建3个kafka容器并运行1.3.1、9095端口1.3.2、9096端口1.3.3、9097端口 1.4、重启kafka-eagle1.5、查看 efak1.5.1、查看 brokers1.5.2、查看 zookeeper 1、kafka集群搭建 1.1、下载镜像文件 d…

Vuforia AR篇(五)— 地平面检测

目录 前言一、什么是地平面识别&#xff1f;二、使用步骤三、示例代码四、效果五、总结 前言 在增强现实&#xff08;AR&#xff09;应用程序的开发中&#xff0c;地平面识别是一项关键技术&#xff0c;它允许虚拟对象与现实世界的地面进行互动。Vuforia 是一个功能强大的 AR …

javacv ffmpeg使用笔记 (补充中...)

javacv ffmpeg使用笔记 一、maven依赖二、示例代码1. 获取视频时长 三、小技巧 一、maven依赖 使用javacv ffmpeg并指定classifier之后&#xff0c;就不需要额外安装ffmpeg软件&#xff08;jar包中已经内置&#xff09;了。 全量依赖包&#xff08;不推荐&#xff09;安装包总大…

6、架构-服务端缓存

为系统引入缓存之前&#xff0c;第一件事情是确认系统是否真的需要缓 存。从开发角度来说&#xff0c;引入缓存会提 高系统复杂度&#xff0c;因为你要考虑缓存的失效、更新、一致性等问题&#xff1b;从运维角度来说&#xff0c;缓存会掩盖一些缺 陷&#xff0c;让问题在更久的…

HashMap的get和put方法

在 JDK 1.8 中&#xff0c;HashMap 是一个常用的实现了 Map 接口的哈希表&#xff0c;它允许存储键值对&#xff0c;并且键和值都可以为 null。HashMap 的主要特点是其基于哈希表的实现&#xff0c;提供了快速的查找和插入操作。以下是 HashMap 中 get 和 put 方法的介绍及其实…

Flink状态State | 大数据技术

⭐简单说两句⭐ ✨ 正在努力的小叮当~ &#x1f496; 超级爱分享&#xff0c;分享各种有趣干货&#xff01; &#x1f469;‍&#x1f4bb; 提供&#xff1a;模拟面试 | 简历诊断 | 独家简历模板 &#x1f308; 感谢关注&#xff0c;关注了你就是我的超级粉丝啦&#xff01; &a…