已开启
HiSpark_AiFly开发板功能适配 #588
HiSpark_AiFly开发板功能适配 #588
已开启
jia_kongfeng创建于 8月31日
共 12 个文件变更+139599-0
MOAT.xml+1-0
@@ -60,6 +60,7 @@ Note:If the text contains special characters, please escape them according to th
60 <policylist>60 <policylist>
61 <policy name="projectPolicy" desc="">61 <policy name="projectPolicy" desc="">
62 <policyitem type="license" name="GPL" path=".*" desc="linux kernel adapter codes"/>62 <policyitem type="license" name="GPL" path=".*" desc="linux kernel adapter codes"/>
63+ <policyitem type="copyright" name="HiSilicon (Shanghai) Technologies Co., Ltd." path=".*" desc="HiSilicon copyright"/>
63 </policy>64 </policy>
64 </policylist> 65 </policylist>
65 </oatconfig>66 </oatconfig>
此文件变更行数或变更字符数较多,你可以直接 查看源码
此文件变更行数或变更字符数较多,你可以直接 查看源码
此文件变更行数或变更字符数较多,你可以直接 查看源码
@@ -0,0 +1,26 @@
1+diff --git a/arch/arm64/boot/dts/vendor/ss928v100-demb.dts b/arch/arm64/boot/dts/vendor/ss928v100-demb.dts
2+index d90c5d5cf..a39158451 100644
3+--- a/arch/arm64/boot/dts/vendor/ss928v100-demb.dts
4++++ b/arch/arm64/boot/dts/vendor/ss928v100-demb.dts
5+@@ -334,7 +334,7 @@ &gmac {
6+
7+ &gmac1 {
8+ phy-handle = <&ethphy1>;
9+- phy-mode = "rgmii-id";
10++ phy-mode = "rgmii";
11+ };
12+
13+ &pcie0 {
14+diff --git a/drivers/net/ethernet/vendor/gmac/gmac.c b/drivers/net/ethernet/vendor/gmac/gmac.c
15+index 30247bd46..40bfe2386 100644
16+--- a/drivers/net/ethernet/vendor/gmac/gmac.c
17++++ b/drivers/net/ethernet/vendor/gmac/gmac.c
18+@@ -1655,7 +1655,7 @@ static int gmac_netdev_preinit(struct gmac_netdev_local *priv)
19+ priv->msg_enable = netif_msg_init(debug, DEFAULT_MSG_ENABLE);
20+
21+ #if defined(CONFIG_GMAC_DDR_64BIT)
22+- ret = dma_set_mask_and_coherent(priv->dev, DMA_BIT_MASK(64)); /* 64: mask length */
23++ ret = dma_set_mask_and_coherent(priv->dev, DMA_BIT_MASK(32)); /* 32: mask length */
24+ if (ret) {
25+ pr_err("dma set mask bit 64 failed! ret=%d", ret);
lijiawei
lijiaweilijiawei9月1日

[linux-6.6/hispark_aifly_patch/0005-kernel-dhcp-support.patch:25] 错误消息与代码不一致:此处将 DMA_BIT_MASK(64) 改为 DMA_BIT_MASK(32),但下方的 pr_err 仍输出 "dma set mask bit 64 failed",应修改为 "dma set mask bit 32 failed" 以保持一致。

likedislike
26+ gmac_destroy_napi(priv);
@@ -0,0 +1,13 @@
1+diff --git a/arch/arm64/boot/dts/vendor/ss928v100.dtsi b/arch/arm64/boot/dts/vendor/ss928v100.dtsi
2+index 80f4dcfb2..d285dd0a8 100644
3+--- a/arch/arm64/boot/dts/vendor/ss928v100.dtsi
4++++ b/arch/arm64/boot/dts/vendor/ss928v100.dtsi
5+@@ -518,6 +518,8 @@ gpio_chip13: gpio_chip@1109D000 {
6+ clocks = <&clock SS928V100_FIXED_50M>;
7+ clock-names = "apb_pclk";
8+ status = "disabled";
9++ interrupt-controller;
10++ #interrupt-cells = <2>;
11+ };
12+
13+ gpio_chip14: gpio_chip@1109E000 {
@@ -0,0 +1,518 @@
1+diff --git a/arch/arm64/boot/dts/vendor/ss928v100-demb.dts b/arch/arm64/boot/dts/vendor/ss928v100-demb.dts
2+index d90c5d5cf..b488894f5 100644
3+--- a/arch/arm64/boot/dts/vendor/ss928v100-demb.dts
4++++ b/arch/arm64/boot/dts/vendor/ss928v100-demb.dts
5+@@ -157,6 +157,11 @@ &i2c_bus2 {
6+
7+ &i2c_bus3 {
8+ status = "okay";
9++ nca9555: ioexpander@20 {
10++ compatible = "nca9555";
lijiawei
lijiaweilijiawei9月1日

[linux-6.6/hispark_aifly_patch/0007-kernel-nca9555.patch:10] DTS 节点缩进使用了空格,但内核 DTS 文件约定使用制表符(tab)缩进。请将 nca9555: ioexpander@20 节点内的缩进改为制表符以保持与文件其余部分一致。

likedislike
11++ reg = <0x20>;
12++ status = "okay";
13++ };
14+ };
15+
16+ &i2c_bus4 {
17+diff --git a/drivers/gpio/Kconfig b/drivers/gpio/Kconfig
18+index ebd4e113d..7fbce8891 100644
19+--- a/drivers/gpio/Kconfig
20++++ b/drivers/gpio/Kconfig
21+@@ -1821,3 +1821,7 @@ config GPIO_SIM
22+ endmenu
23+
24+ endif
25++
26++config GPIO_NCA9555
lijiawei
lijiaweilijiawei9月1日

[linux-6.6/hispark_aifly_patch/0007-kernel-nca9555.patch:26] config GPIO_NCA9555 放在了 endif 之后,位于 if GPIOLIB 条件块之外,这意味着即使未启用 GPIOLIB 也会出现此配置选项。应将其移至 endmenu 之前、endif 之内。

likedislike
27++ tristate "NCA9555"
28++ default n
29+diff --git a/drivers/gpio/Makefile b/drivers/gpio/Makefile
30+index f1f998da4..69407b9c5 100644
31+--- a/drivers/gpio/Makefile
32++++ b/drivers/gpio/Makefile
33+@@ -196,3 +196,4 @@ obj-$(CONFIG_GPIO_ZEVIO) += gpio-zevio.o
34+ obj-$(CONFIG_GPIO_ZYNQ) += gpio-zynq.o
35+ obj-$(CONFIG_GPIO_ZYNQMP_MODEPIN) += gpio-zynqmp-modepin.o
36+ obj-$(CONFIG_ARCH_HISILICON) += vendor/
37++obj-$(CONFIG_GPIO_NCA9555) += gpio-nca9555.o
38+ 
lijiawei
lijiaweilijiawei9月1日

[linux-6.6/hispark_aifly_patch/0007-kernel-nca9555.patch:38] Makefile 末尾缺少换行符("No newline at end of file")。请在 obj-$(CONFIG_GPIO_NCA9555) 行末添加换行符,符合 POSIX 文本文件规范。

likedislike
39+diff --git a/drivers/gpio/gpio-nca9555.c b/drivers/gpio/gpio-nca9555.c
40+new file mode 100644
41+index 000000000..075c580da
42+--- /dev/null
43++++ b/drivers/gpio/gpio-nca9555.c
44+@@ -0,0 +1,207 @@
45++// SPDX-License-Identifier: GPL-2.0-or-later
46++/*
47++ * GPIO driver for NCA9555 MFD
48++ *
49++ */
50++
51++#include <linux/module.h>
52++#include <linux/slab.h>
53++#include <linux/kernel.h>
54++#include <linux/init.h>
55++#include <linux/platform_device.h>
56++#include <linux/mfd/nca9555.h>
57++#include <linux/gpio/driver.h>
58++
59++#define NCA9555_GPIO_MAX 16
60++
61++struct nca9555_gpio_dev {
62++ struct device *parent;
63++ struct gpio_chip gpio_chip;
64++ struct mutex lock;
65++ u8 dat_out[2];
66++ u8 dir[2];
67++};
68++
69++static int nca9555_gpio_reg_read(struct nca9555_gpio_dev *nca9555_gpio, u8 reg, u8 *val)
70++{
71++ struct nca9555_dev *nca9555;
72++ nca9555 = dev_get_drvdata(nca9555_gpio->parent);
73++
74++ return nca9555->read_reg(nca9555, reg, val);
75++}
76++
77++static int nca9555_gpio_reg_write(struct nca9555_gpio_dev *nca9555_gpio, u8 reg, u8 val)
78++{
79++ struct nca9555_dev *nca9555;
80++ nca9555 = dev_get_drvdata(nca9555_gpio->parent);
81++
82++ return nca9555->write_reg(nca9555, reg, val);
83++}
84++
85++static int nca9555_gpio_get_value(struct gpio_chip *chip, unsigned int off)
86++{
87++ struct nca9555_gpio_dev *nca9555_gpio;
88++ unsigned int bank, bit;
89++ u8 val;
90++
91++ nca9555_gpio = gpiochip_get_data(chip);
92++ bank = NCA9555_BANK(off);
93++ bit = NCA9555_BIT(off);
94++
95++ mutex_lock(&nca9555_gpio->lock);
96++ /* There are dedicated registers for GPIO IN/OUT. */
97++ if (nca9555_gpio->dir[bank] & bit)
lijiawei
lijiaweilijiawei9月1日

[linux-6.6/hispark_aifly_patch/0007-kernel-nca9555.patch:97] nca9555_gpio_get_value 中输入/输出方向判断逻辑反了:当 dir[bank] & bit 为真时(对应 CONFIG 寄存器 bit=1,即输入模式),代码返回 dat_out(输出缓存值);当 bit 为假时(CONFIG bit=0,即输出模式),代码读取输入寄存器。应交换两个分支:输入模式读输入寄存器,输出模式返回 dat_out 缓存值。

likedislike
98++ val = nca9555_gpio->dat_out[bank];
99++ else
100++ nca9555_gpio_reg_read(nca9555_gpio, GPIO_PORT0_INPUT_REG + bank, &val);
101++
102++ mutex_unlock(&nca9555_gpio->lock);
103++
104++ return !!(val & bit);
105++}
106++
107++static void nca9555_gpio_set_value(struct gpio_chip *chip, unsigned int off, int val)
108++{
109++ struct nca9555_gpio_dev *nca9555_gpio;
110++ unsigned int bank, bit;
111++
112++ nca9555_gpio = gpiochip_get_data(chip);
113++ bank = NCA9555_BANK(off);
114++ bit = NCA9555_BIT(off);
115++
116++ mutex_lock(&nca9555_gpio->lock);
117++ if (val)
118++ nca9555_gpio->dat_out[bank] |= bit;
119++ else
120++ nca9555_gpio->dat_out[bank] &= ~bit;
121++
122++ nca9555_gpio_reg_write(nca9555_gpio, GPIO_PORT0_OUTPUT_REG + bank,
123++ nca9555_gpio->dat_out[bank]);
124++ mutex_unlock(&nca9555_gpio->lock);
125++}
126++
127++static int nca9555_gpio_direction_input(struct gpio_chip *chip, unsigned int off)
128++{
129++ struct nca9555_gpio_dev *nca9555_gpio;
130++ unsigned int bank, bit;
131++ int ret;
132++
133++ nca9555_gpio = gpiochip_get_data(chip);
134++ bank = NCA9555_BANK(off);
135++ bit = NCA9555_BIT(off);
136++
137++ mutex_lock(&nca9555_gpio->lock);
138++ nca9555_gpio->dir[bank] |= bit;
139++ ret = nca9555_gpio_reg_write(nca9555_gpio, GPIO_PORT0_CONFIG_REG + bank,
140++ nca9555_gpio->dir[bank]);
141++ mutex_unlock(&nca9555_gpio->lock);
142++ return ret;
143++}
144++
145++static int nca9555_gpio_direction_output(struct gpio_chip *chip, unsigned int off, int val)
146++{
147++ struct nca9555_gpio_dev *nca9555_gpio;
148++ unsigned int bank, bit;
149++ int ret;
150++
151++ nca9555_gpio = gpiochip_get_data(chip);
152++
153++ bank = NCA9555_BANK(off);
154++ bit = NCA9555_BIT(off);
155++
156++ mutex_lock(&nca9555_gpio->lock);
157++ // 配置为输出:先更新输出电平寄存器,再设置方向寄存器
158++ // 设计目的:防止先切输出方向时引脚出现未知电平毛刺
159++ nca9555_gpio->dir[bank] &= ~bit;
160++
161++ if (val)
162++ nca9555_gpio->dat_out[bank] |= bit;
163++ else
164++ nca9555_gpio->dat_out[bank] &= ~bit;
165++
166++ // 1. 先写入目标输出电平
167++ ret = nca9555_gpio_reg_write(nca9555_gpio, GPIO_PORT0_OUTPUT_REG + bank,
168++ nca9555_gpio->dat_out[bank]);
169++ // 2. 再配置引脚为输出模式
170++ ret |= nca9555_gpio_reg_write(nca9555_gpio, GPIO_PORT0_CONFIG_REG + bank,
lijiawei
lijiaweilijiawei9月1日

[linux-6.6/hispark_aifly_patch/0007-kernel-nca9555.patch:170] 使用 ret |= 合并两次 reg_write 的返回值是错误的错误处理方式。若第一次写入成功(返回 0),第二次失败(返回 -EIO),0 | -EIO 的结果并非有效的错误码。建议对每次 reg_write 分别检查返回值,第一次失败时直接返回错误。

likedislike
171++ nca9555_gpio->dir[bank]);
172++
173++ mutex_unlock(&nca9555_gpio->lock);
174++
175++ return ret;
176++}
177++
178++static int nca9555_gpio_probe(struct platform_device *pdev)
179++{
180++ struct nca9555_gpio_dev *nca9555_gpio;
181++ struct device *dev = &pdev->dev;
182++ struct device_node *np = dev->of_node;
183++ struct gpio_chip *gc;
184++ int i;
185++ int ret;
186++
187++ dev_info(dev, "gpio-nca9555 probe start \n");
188++
189++ nca9555_gpio = devm_kzalloc(&pdev->dev, sizeof(struct nca9555_gpio_dev), GFP_KERNEL);
190++ if (!nca9555_gpio)
191++ return -ENOMEM;
192++
193++ nca9555_gpio->parent = pdev->dev.parent;
194++
195++ gc = &nca9555_gpio->gpio_chip;
196++ gc->fwnode = dev_fwnode(dev);
197++ gc->parent = dev;
198++ gc->direction_input = nca9555_gpio_direction_input;
199++ gc->direction_output = nca9555_gpio_direction_output;
200++ gc->get = nca9555_gpio_get_value;
201++ gc->set = nca9555_gpio_set_value;
202++ gc->can_sleep = true;
203++
204++ gc->base = -1;
205++ gc->ngpio = NCA9555_GPIO_MAX;
206++ gc->label = pdev->name;
207++ gc->owner = THIS_MODULE;
208++
209++ mutex_init(&nca9555_gpio->lock);
210++
211++ for (i = 0; i < 2; i++) {
212++ u8 *dat_out, *dir;
213++ dat_out = nca9555_gpio->dat_out;
214++ dir = nca9555_gpio->dir;
215++ ret = nca9555_gpio_reg_read(nca9555_gpio,
216++ GPIO_PORT0_OUTPUT_REG + i, dat_out + i);
217++ if (ret) {
218++ dev_err(dev, "read output port%d reg fail, ret=%d\n", i, ret);
219++ return ret;
220++ }
221++ ret = nca9555_gpio_reg_read(nca9555_gpio,
222++ GPIO_PORT0_CONFIG_REG + i, dir + i);
223++ if (ret) {
224++ dev_err(dev, "read config port%d reg fail, ret=%d\n", i, ret);
225++ return ret;
226++ }
227++ }
228++
229++ dev_info(dev, "gpio-nca9555 probe succes \n");
230++
231++ return devm_gpiochip_add_data(&pdev->dev, &nca9555_gpio->gpio_chip, nca9555_gpio);
232++}
233++
234++static const struct of_device_id nca9555_of_match[] = {
235++ {.compatible = "nca9555-gpio", },
236++ { /* sentinel */ }
237++};
238++MODULE_DEVICE_TABLE(of, nca9555_of_match);
239++
240++static struct platform_driver nca9555_gpio_driver = {
241++ .driver = {
242++ .name = "nca9555-gpio",
243++ .of_match_table = nca9555_of_match,
244++ },
245++ .probe = nca9555_gpio_probe,
246++};
247++
248++module_platform_driver(nca9555_gpio_driver);
249++
250++MODULE_DESCRIPTION("GPIO NCA9555 Driver");
251++MODULE_LICENSE("GPL");
252+diff --git a/drivers/mfd/Kconfig b/drivers/mfd/Kconfig
253+index 459e737c5..3f6687e8f 100644
254+--- a/drivers/mfd/Kconfig
255++++ b/drivers/mfd/Kconfig
256+@@ -2345,3 +2345,8 @@ config MFD_RSMU_SPI
257+
258+ endmenu
259+ endif
260++
261++config MFD_NCA9555
lijiawei
lijiaweilijiawei9月1日

[linux-6.6/hispark_aifly_patch/0007-kernel-nca9555.patch:261] config MFD_NCA9555 缺少 depends on I2C 依赖。MFD 核心驱动 nca9555.c 使用 I2C 通信,若未启用 I2C 子系统,编译将失败。同时该配置也位于 endmenu 和 endif 之外,应移入条件块内。

likedislike
262++ tristate "NCA9555"
263++ select MFD_CORE
264++ default n
265+diff --git a/drivers/mfd/Makefile b/drivers/mfd/Makefile
266+index 7e57a4983..f1ac14598 100644
267+--- a/drivers/mfd/Makefile
268++++ b/drivers/mfd/Makefile
269+@@ -285,3 +285,4 @@ obj-$(CONFIG_MFD_ATC260X_I2C) += atc260x-i2c.o
270+
271+ obj-$(CONFIG_MFD_RSMU_I2C) += rsmu_i2c.o rsmu_core.o
272+ obj-$(CONFIG_MFD_RSMU_SPI) += rsmu_spi.o rsmu_core.o
273++obj-$(CONFIG_MFD_NCA9555) += nca9555.o
274+diff --git a/drivers/mfd/nca9555.c b/drivers/mfd/nca9555.c
275+new file mode 100644
276+index 000000000..30f6de079
277+--- /dev/null
278++++ b/drivers/mfd/nca9555.c
279+@@ -0,0 +1,158 @@
280++/* SPDX-License-Identifier: GPL-2.0-or-later */
281++/* NCA9555 IO Expander, Key controller core driver.
282++ *
283++ * Copyright 2023 RPDZKJ
284++ */
285++
286++#include <linux/module.h>
287++#include <linux/moduleparam.h>
288++#include <linux/init.h>
289++#include <linux/slab.h>
290++#include <linux/i2c.h>
291++#include <linux/of.h>
292++#include <linux/mfd/core.h>
293++#include <linux/mfd/nca9555.h>
294++#include <linux/io.h>
295++static const struct mfd_cell nca9555_devs[] = {
296++ {
297++ .name = "nca9555-gpio",
298++ .of_compatible = "nca9555-gpio",
299++ }
300++};
301++
302++static int nca9555_i2c_read_reg(struct nca9555_dev *nca9555, u8 reg, u8 *val)
303++{
304++ struct i2c_client *i2c = nca9555->i2c_client;
305++ struct i2c_msg xfer[2];
306++ int ret;
307++
308++ /* Write register */
309++ xfer[0].addr = i2c->addr;
310++ xfer[0].flags = 0;
311++ xfer[0].len = 1;
312++ xfer[0].buf = &reg;
313++
314++ /* Read data */
315++ xfer[1].addr = i2c->addr;
316++ xfer[1].flags = I2C_M_RD;
317++ xfer[1].len = 1;
318++ xfer[1].buf = val;
319++
320++ ret = i2c_transfer(i2c->adapter, xfer, 2);
321++ if (ret == 2) {
322++ return 0;
323++ } else {
324++ dev_err(&i2c->dev,
325++ "Failed to read reg 0x%02x, ret is %d\n",
326++ reg, ret);
327++ if ( ret >=0)
328++ return -EIO;
329++ else
330++ return ret;
331++ }
332++}
333++
334++static int nca9555_i2c_write_reg(struct nca9555_dev *nca9555, u8 reg, u8 val)
335++{
336++ struct i2c_client *i2c = nca9555->i2c_client;
337++ u8 msg[2];
338++ int ret;
339++
340++ msg[0] = reg;
341++ msg[1] = val;
342++ ret = i2c_master_send(i2c, msg, 2);
343++ if (ret == 2) {
344++ return 0;
345++ } else {
346++ dev_err(&i2c->dev,
347++ "Failed to write reg 0x%02x, ret is %d\n",
348++ reg, ret);
349++ if ( ret >=0)
350++ return -EIO;
351++ else
352++ return ret;
353++ }
354++}
355++
356++static int nca9555_i2c_probe(struct i2c_client *i2c)
357++{
358++ struct nca9555_dev *nca9555;
359++ int ret;
360++ struct device *dev = &i2c->dev;
361++
362++ dev_info(dev, "Enter probe, i2c addr:0x%02x\n", i2c->addr);
363++
364++ nca9555 = devm_kzalloc(dev, sizeof(struct nca9555_dev), GFP_KERNEL);
365++ if (!nca9555) {
366++ dev_err(dev, "devm_kzalloc failed\n");
367++ return -ENOMEM;
368++ }
369++ dev_info(dev, "alloc nca9555 dev struct ok\n");
370++
371++ i2c_set_clientdata(i2c, nca9555);
372++ nca9555->dev = dev;
373++ nca9555->i2c_client = i2c;
374++ nca9555->read_reg = nca9555_i2c_read_reg;
375++ nca9555->write_reg = nca9555_i2c_write_reg;
376++ dev_info(dev, "bind reg ops complete\n");
377++
378++ if (!dev->of_node) {
379++ dev_err(dev, "no device tree node, reject probe\n");
380++ return -ENODEV;
381++ }
382++ u32 dts_addr;
lijiawei
lijiaweilijiawei9月1日

[linux-6.6/hispark_aifly_patch/0007-kernel-nca9555.patch:382] u32 dts_addr 变量声明位于 dev_err 语句之后,违反内核代码风格(C89 要求变量声明在块开头)。请将 u32 dts_addr 声明移至 nca9555_i2c_probe 函数开头的变量声明区域。

likedislike
383++ ret = of_property_read_u32(dev->of_node, "reg", &dts_addr);
384++ if (ret || dts_addr != i2c->addr) {
385++ dev_err(dev, "reg mismatch: dts=0x%x client=0x%02x, ret=%d\n",
386++ dts_addr, i2c->addr, ret);
387++ return -ENODEV;
388++ }
389++
390++ ret = devm_mfd_add_devices(dev, PLATFORM_DEVID_AUTO,
391++ nca9555_devs, ARRAY_SIZE(nca9555_devs),
392++ NULL, 0, NULL);
393++ if (ret) {
394++ dev_err(dev, "devm_mfd_add_devices failed, ret=%d, err=%pE\n", ret, ERR_PTR(ret));
395++ return ret;
396++ }
397++ dev_info(dev, "probe finished successfully\n");
398++
399++ return 0;
400++}
401++
402++static const struct i2c_device_id nca9555_i2c_id[] = {
403++ { "nca9555", 0 },
404++ { /* sentinel */ }
405++};
406++MODULE_DEVICE_TABLE(i2c, nca9555_i2c_id);
407++
408++static const struct of_device_id nca9555_of_match[] = {
409++ {.compatible = "nca9555", },
410++ { /* sentinel */ }
411++};
412++MODULE_DEVICE_TABLE(of, nca9555_of_match);
413++
414++static struct i2c_driver nca9555_i2c_driver = {
415++ .driver = {
416++ .name = "nca9555",
417++ .of_match_table = of_match_ptr(nca9555_of_match),
418++ },
419++ .probe = nca9555_i2c_probe,
420++ .id_table = nca9555_i2c_id,
421++};
422++
423++static int __init nca9555_i2c_init(void)
424++{
425++ return i2c_add_driver(&nca9555_i2c_driver);
426++}
427++/* init early so consumer devices can complete system boot */
428++subsys_initcall(nca9555_i2c_init);
lijiawei
lijiaweilijiawei9月1日

[linux-6.6/hispark_aifly_patch/0007-kernel-nca9555.patch:428] 使用 subsys_initcall 而非标准的 module_i2c_driver() 宏。I2C 驱动通常使用 module_i2c_driver() 注册,它能正确处理模块加载/卸载逻辑。若确实需要早期初始化,请添加注释说明原因;否则建议改用 module_i2c_driver(nca9555_i2c_driver) 并删除手动定义的 init/exit 函数。

likedislike
429++
430++static void __exit nca9555_i2c_exit(void)
431++{
432++ i2c_del_driver(&nca9555_i2c_driver);
433++}
434++module_exit(nca9555_i2c_exit);
435++
436++MODULE_DESCRIPTION("NCA9555 core driver");
437++MODULE_LICENSE("GPL");
438+diff --git a/include/dt-bindings/gpio/nca9555-gpio.h b/include/dt-bindings/gpio/nca9555-gpio.h
439+new file mode 100644
440+index 000000000..62fead2db
441+--- /dev/null
442++++ b/include/dt-bindings/gpio/nca9555-gpio.h
443+@@ -0,0 +1,29 @@
444++/* SPDX-License-Identifier: GPL-2.0-only */
445++/*
446++ * GPIO definitions for NCA9555
447++ *
448++ * Copyright (C) 2023 RPDZKJ
449++ */
450++
451++#ifndef _DT_BINDINGS_NCA9555_GPIO_H
452++#define _DT_BINDINGS_NCA9555_GPIO_H
453++
454++#define IO_00 0
455++#define IO_01 1
456++#define IO_02 2
457++#define IO_03 3
458++#define IO_04 4
459++#define IO_05 5
460++#define IO_06 6
461++#define IO_07 7
462++
463++#define IO_10 8
464++#define IO_11 9
465++#define IO_12 10
466++#define IO_13 11
467++#define IO_14 12
468++#define IO_15 13
469++#define IO_16 14
470++#define IO_17 15
471++
472++#endif /* _DT_BINDINGS_NCA9555_GPIO_H */
473+diff --git a/include/linux/mfd/nca9555.h b/include/linux/mfd/nca9555.h
474+new file mode 100644
475+index 000000000..321aea2bf
476+--- /dev/null
477++++ b/include/linux/mfd/nca9555.h
478+@@ -0,0 +1,40 @@
479++/* SPDX-License-Identifier: GPL-2.0-or-later */
480++/*
481++ * Analog Devices NCA9555 I/O Expander, keypad controller,
482++ * PWM contorller.
483++ */
484++
485++#ifndef __NCA9555_H_
486++#define __NCA9555_H_
487++
488++
489++#define GPIO_PORT0 0
490++#define GPIO_PORT1 1
491++
492++#define GPIO_PORT0_INPUT_REG 0x00
493++#define GPIO_PORT1_INPUT_REG 0x01
494++
495++#define GPIO_PORT0_OUTPUT_REG 0x02
496++#define GPIO_PORT1_OUTPUT_REG 0x03
497++
498++#define GPIO_PORT0_POLAR_REG 0x04
499++#define GPIO_PORT1_POLAR_REG 0x05
500++
501++#define GPIO_PORT0_CONFIG_REG 0x06
502++#define GPIO_PORT1_CONFIG_REG 0x07
503++
504++#define NCA9555_ID 0x00
505++
506++#define NCA9555_BANK(offs) ((offs) >= 8)
507++#define NCA9555_BIT(offs) ((offs) >= 8 ? \
508++ 1u << (offs - 8) : 1u << (offs))
509++
510++struct nca9555_dev {
511++ struct device *dev;
512++ struct i2c_client *i2c_client;
513++
514++ int (*read_reg)(struct nca9555_dev *nca9555, u8 reg, u8 *val);
515++ int (*write_reg)(struct nca9555_dev *nca9555, u8 reg, u8 val);
516++};
517++
518++#endif
@@ -0,0 +1,51 @@
1+diff --git a/arch/arm64/boot/dts/vendor/ss928v100-demb.dts b/arch/arm64/boot/dts/vendor/ss928v100-demb.dts
2+index 529e33fe0..bdffb0627 100644
3+--- a/arch/arm64/boot/dts/vendor/ss928v100-demb.dts
4++++ b/arch/arm64/boot/dts/vendor/ss928v100-demb.dts
5+@@ -22,6 +22,7 @@
6+ /* Modify this configuration according to the system framework */
7+ /memreserve/ 0x52fff000 0x01a02000;
8+ #include "ss928v100.dtsi"
9++#include <dt-bindings/input/input.h>
10+
11+ / {
12+ model = "Vendor SS928V100 DEMO Board";
13+@@ -114,6 +115,26 @@ memory {
14+ device_type = "memory";
15+ reg = <0x0 0x50000000 0x1 0xf0000000>; /* system memory base */
16+ };
17++
18++ gpio-keys {
19++ compatible = "gpio-keys";
20++ label = "board_gpio_key";
21++ status = "okay";
22++
23++ power-key {
24++ label = "Power Key";
25++ linux,code = <KEY_POWER>;
26++ gpios = <&gpio_chip11 2 GPIO_ACTIVE_HIGH>;
lijiawei
lijiaweilijiawei9月1日

[linux-6.6/hispark_aifly_patch/0008-power-key-dts.patch:26] 电源按键使用 GPIO_ACTIVE_HIGH,但大多数硬件设计中电源按键按下时将 GPIO 拉低,通常使用 GPIO_ACTIVE_LOW。请确认 HiSpark AiFly 开发板的电源按键硬件设计:若按键按下为低电平,应改为 GPIO_ACTIVE_LOW。

likedislike
27++ debounce-interval = <50>;
28++ };
29++
30++ backlight-key {
31++ label = "BackLight Key";
32++ linux,code = <KEY_BRIGHTNESSDOWN>;
33++ gpios = <&gpio_chip11 3 GPIO_ACTIVE_HIGH>;
34++ debounce-interval = <50>;
35++ };
36++ };
37+ };
38+
39+ &ipcm {
40+diff --git a/arch/arm64/boot/dts/vendor/ss928v100.dtsi b/arch/arm64/boot/dts/vendor/ss928v100.dtsi
41+index d285dd0a8..376dac2ac 100644
42+--- a/arch/arm64/boot/dts/vendor/ss928v100.dtsi
43++++ b/arch/arm64/boot/dts/vendor/ss928v100.dtsi
44+@@ -21,6 +21,7 @@
45+ #include <dt-bindings/interrupt-controller/irq.h>
46+ #include <dt-bindings/interrupt-controller/arm-gic.h>
47+ #include "ss928v100_family_usb.dtsi"
48++#include <dt-bindings/gpio/gpio.h>
49+
50+ / {
51+ #address-cells = <2>;
@@ -0,0 +1,8 @@
1+diff --git a/drivers/Makefile b/drivers/Makefile
2+--- a/drivers/Makefile
3++++ b/drivers/Makefile
4+@@ -205,3 +205,4 @@
5+ 
6+ obj-$(CONFIG_ACCESS_TOKENID) += accesstokenid/
7+ obj-$(CONFIG_HCK_VENDOR_HOOKS) += hck/
8++obj-$(CONFIG_ARCH_SS928V100) += hdf/soc_driver/
@@ -0,0 +1,412 @@
1+diff --git a/arch/arm64/boot/dts/vendor/ss928v100-demb.dts b/arch/arm64/boot/dts/vendor/ss928v100-demb.dts
2+index c202cdb08..de6f1d0d0 100644
3+--- a/arch/arm64/boot/dts/vendor/ss928v100-demb.dts
4++++ b/arch/arm64/boot/dts/vendor/ss928v100-demb.dts
5+@@ -135,6 +135,16 @@ backlight-key {
6+ debounce-interval = <50>;
7+ };
8+ };
9++
10++ /*
11++ * Quectel EC20 board PERST: idle/run level is low (legacy gpio-hog
12++ * used output-low). Driver pulses high >= 150ms then returns low.
13++ */
14++ ec20_4g {
lijiawei
lijiaweilijiawei9月2日

[linux-6.6/hispark_aifly_patch/0010-kernel-quectel-4g.patch:14] DTS ec20_4g 节点缩进使用了空格,但内核 DTS 文件约定使用制表符(tab)缩进。请将 ec20_4g 节点内的缩进改为制表符以保持与文件其余部分一致。

likedislike
15++ compatible = "quectel,ec20-4g";
16++ reset-gpios = <&gpio_chip1 5 GPIO_ACTIVE_HIGH>;
17++ status = "okay";
18++ };
19+ };
20+
21+ &ipcm {
22+diff --git a/drivers/usb/serial/Kconfig b/drivers/usb/serial/Kconfig
23+index ef8d1c73c..615dd8726 100644
24+--- a/drivers/usb/serial/Kconfig
25++++ b/drivers/usb/serial/Kconfig
26+@@ -561,6 +561,16 @@ config USB_SERIAL_OPTION
27+ If this driver doesn't recognize your device,
28+ it might be accessible via the FTDI_SIO driver.
29+
30++config USB_SERIAL_QUECTEL_SUPPORT
31++ bool "Quectel USB serial modem extensions"
lijiawei
lijiaweilijiawei9月2日

[linux-6.6/hispark_aifly_patch/0010-kernel-quectel-4g.patch:31] USB_SERIAL_QUECTEL_SUPPORT 声明为 bool(不可编译为模块),但其依赖的 USB_SERIAL_OPTION 是 tristate。当 USB_SERIAL_OPTION=m 且 USB_SERIAL_QUECTEL_SUPPORT=y 时,#ifdef 代码块会被编入模块化的 option.c,但 usb_wwan_quectel_use_zlp 由 EXPORT_SYMBOL_GPL 导出,模块间依赖可能出问题。建议将 USB_SERIAL_QUECTEL_SUPPORT 改为 tristate,或添加 depends on USB_SERIAL_OPTION=y 约束。

likedislike
32++ depends on USB_SERIAL_OPTION
33++ help
34++ Enable Quectel-specific extensions for option, usb_wwan and
35++ qcserial: extra device IDs, zero-length packet, probe quirks,
36++ termios init, and EC20 PERST reset sequence.
37++
38++ Say Y if your board uses a Quectel modem such as EC20.
39++
40+ config USB_SERIAL_OMNINET
41+ tristate "USB ZyXEL omni.net LCD Plus Driver"
42+ help
43+diff --git a/drivers/usb/serial/option.c b/drivers/usb/serial/option.c
44+index 6c6387d39..410f82e91 100644
45+--- a/drivers/usb/serial/option.c
46++++ b/drivers/usb/serial/option.c
47+@@ -639,6 +639,46 @@ static void option_instat_callback(struct urb *urb);
48+
49+
50+ static const struct usb_device_id option_ids[] = {
51++#ifdef CONFIG_USB_SERIAL_QUECTEL_SUPPORT
52++ { USB_DEVICE(0x05C6, 0x9090) }, /* Quectel UC15 */
lijiawei
lijiaweilijiawei9月2日

[linux-6.6/hispark_aifly_patch/0010-kernel-quectel-4g.patch:52] 新增的 Quectel USB 设备 ID 列表中有部分 ID 可能已存在于上游 option_ids 表中(如 0x2C7C, 0x0125 等)。重复添加会导致同一设备被匹配两次,可能引发驱动绑定问题。请逐一核对上游 option.c 中是否已包含这些 ID,移除重复项。

likedislike
53++ { USB_DEVICE(0x05C6, 0x9003) }, /* Quectel UC20 */
54++ { USB_DEVICE(0x05C6, 0x9215) }, /* Quectel EC20(MDM9215) */
55++ { USB_DEVICE(0x05C6, 0x9091) }, /* Quectel QCM6125 */
56++ { USB_DEVICE(0x05C6, 0x90DB) }, /* Quectel QCM6490 */
57++ { USB_DEVICE(0x2C7C, 0x0125) }, /* Quectel EC20(MDM9x07)/EC25/EG25 */
58++ { USB_DEVICE(0x2C7C, 0x0121) }, /* Quectel EC21 */
59++ { USB_DEVICE(0x2C7C, 0x0122) }, /* Quectel RG650V / RG650 SDX7X */
60++ { USB_DEVICE(0x2C7C, 0x030E) }, /* Quectel EM05G */
61++ { USB_DEVICE(0x2C7C, 0x0191) }, /* Quectel EG91 */
62++ { USB_DEVICE(0x2C7C, 0x0195) }, /* Quectel EG95 */
63++ { USB_DEVICE(0x2C7C, 0x0306) }, /* Quectel EG06/EP06/EM06 */
64++ { USB_DEVICE(0x2C7C, 0x030B) }, /* Quectel EG065K/EG060K */
65++ { USB_DEVICE(0x2C7C, 0x0514) }, /* Quectel BL EG060K RNDIS Only */
66++ { USB_DEVICE(0x2C7C, 0x0512) }, /* Quectel EG12/EP12/EM12/EG16/EG18 */
67++ { USB_DEVICE(0x2C7C, 0x0296) }, /* Quectel BG96 */
68++ { USB_DEVICE(0x2C7C, 0x0700) }, /* Quectel BG95/BG77/BG600L-M3/BC69 */
69++ { USB_DEVICE(0x2C7C, 0x0435) }, /* Quectel AG35 */
70++ { USB_DEVICE(0x2C7C, 0x0415) }, /* Quectel AG15 */
71++ { USB_DEVICE(0x2C7C, 0x0452) }, /* Quectel AG520/AG529 */
72++ { USB_DEVICE(0x2C7C, 0x0455) }, /* Quectel AG550 */
73++ { USB_DEVICE(0x2C7C, 0x0620) }, /* Quectel EG20 */
74++ { USB_DEVICE(0x2C7C, 0x0800) }, /* Quectel RG500/RM500/RG510/RM510 */
75++ { USB_DEVICE(0x2C7C, 0x0801) }, /* Quectel RG520/RM520/SG520 */
76++ { USB_DEVICE(0x2C7C, 0x0133) }, /* Quectel RG650 SDX7X RNDIS / RM551E */
77++ { USB_DEVICE(0x2C7C, 0x0134) }, /* Quectel SDX35 AG531C */
78++ { USB_DEVICE(0x2C7C, 0x0316) }, /* Quectel RG255 SDX35 */
79++ { USB_DEVICE(0x2C7C, 0x6026) }, /* Quectel EC200 */
80++ { USB_DEVICE(0x2C7C, 0x6120) }, /* Quectel UC200 */
81++ { USB_DEVICE(0x2C7C, 0x600B) }, /* Quectel ASR1903S */
82++ { USB_DEVICE(0x2C7C, 0x600C) }, /* Quectel RG255A ASR1903L */
83++ { USB_DEVICE(0x2C7C, 0x6000) }, /* Quectel EC200/UC200 */
84++ { USB_DEVICE(0x2C7C, 0x7003) }, /* Quectel T750(META) */
85++ { USB_DEVICE(0x2C7C, 0x7008) }, /* Quectel T750 */
86++ { USB_DEVICE(0x2C7C, 0x7005) }, /* Quectel T830(META) */
87++ { USB_DEVICE(0x2C7C, 0x7006) }, /* Quectel T830 */
88++ { USB_DEVICE(0x3763, 0x3C93) }, /* Quectel GW */
89++ { USB_DEVICE(0x3C93, 0xFFFF) }, /* Quectel GW */
90++#endif
91+ { USB_DEVICE(OPTION_VENDOR_ID, OPTION_PRODUCT_COLT) },
92+ { USB_DEVICE(OPTION_VENDOR_ID, OPTION_PRODUCT_RICOLA) },
93+ { USB_DEVICE(OPTION_VENDOR_ID, OPTION_PRODUCT_RICOLA_LIGHT) },
94+@@ -2504,6 +2544,56 @@ static int option_probe(struct usb_serial *serial,
95+ &serial->interface->cur_altsetting->desc;
96+ unsigned long device_flags = id->driver_info;
97+
98++#ifdef CONFIG_USB_SERIAL_QUECTEL_SUPPORT
99++ /* Quectel UC20: interface 4 is USB network */
100++ if (serial->dev->descriptor.idVendor == cpu_to_le16(0x05C6) &&
lijiawei
lijiaweilijiawei9月2日

[linux-6.6/hispark_aifly_patch/0010-kernel-quectel-4g.patch:100] option_probe 中使用 cpu_to_le16() 比较 USB 厂商/产品 ID 是非标准做法。内核常规模式是使用 le16_to_cpu() 将 descriptor 字段转为 CPU 字节序后再与常量比较:le16_to_cpu(serial->dev->descriptor.idVendor) == 0x05C6。当前写法在小端系统上功能正确,但在大端系统上可能导致误匹配。建议统一改为 le16_to_cpu() 模式。

likedislike
101++ serial->dev->descriptor.idProduct == cpu_to_le16(0x9003) &&
102++ serial->interface->cur_altsetting->desc.bInterfaceNumber >= 4)
103++ return -ENODEV;
104++
105++ /* Quectel EC20(MDM9215): interface 4 is USB network */
106++ if (serial->dev->descriptor.idVendor == cpu_to_le16(0x05C6) &&
107++ serial->dev->descriptor.idProduct == cpu_to_le16(0x9215) &&
108++ serial->interface->cur_altsetting->desc.bInterfaceNumber >= 4)
109++ return -ENODEV;
110++
111++ /* Quectel QCM6125/QCM6490: interface 2+ is USB network */
112++ if (serial->dev->descriptor.idVendor == cpu_to_le16(0x05C6) &&
113++ (serial->dev->descriptor.idProduct == cpu_to_le16(0x9091) ||
114++ serial->dev->descriptor.idProduct == cpu_to_le16(0x90DB)) &&
115++ serial->interface->cur_altsetting->desc.bInterfaceNumber >= 2)
116++ return -ENODEV;
117++
118++ /* Netprisma PCUL11-LD(MDM9205): interface 3+ is USB network */
119++ if (serial->dev->descriptor.idVendor == cpu_to_le16(0x3731) &&
120++ serial->dev->descriptor.idProduct == cpu_to_le16(0x0102) &&
121++ serial->interface->cur_altsetting->desc.bInterfaceNumber >= 3)
122++ return -ENODEV;
123++
124++ if (serial->dev->descriptor.idVendor == cpu_to_le16(0x2C7C)) {
125++ __u16 idProduct = le16_to_cpu(serial->dev->descriptor.idProduct);
126++ struct usb_interface_descriptor *intf =
127++ &serial->interface->cur_altsetting->desc;
128++
129++ if (intf->bInterfaceClass != 0xFF || intf->bInterfaceSubClass == 0x42)
130++ return -ENODEV;
131++
132++ if ((idProduct & 0xF000) == 0x0000) {
133++ /* MDM interface 4 is QMI; SDX35 uses interface 3 */
134++ if (idProduct == 0x0316 && intf->bInterfaceNumber == 3)
135++ return -ENODEV;
136++
137++ if (idProduct == 0x0121 && intf->bInterfaceNumber == 4)
138++ return -ENODEV;
139++
140++ if (intf->bInterfaceNumber == 4 && intf->bNumEndpoints == 3 &&
141++ intf->bInterfaceSubClass == 0xFF &&
142++ intf->bInterfaceProtocol == 0xFF)
143++ return -ENODEV;
144++ }
145++ }
146++#endif
147++
148+ /* Never bind to the CD-Rom emulation interface */
149+ if (iface_desc->bInterfaceClass == USB_CLASS_MASS_STORAGE)
150+ return -ENODEV;
151+@@ -2523,6 +2613,12 @@ static int option_probe(struct usb_serial *serial,
152+ if (device_flags & NUMEP2 && iface_desc->bNumEndpoints != 2)
153+ return -ENODEV;
154+
155++#ifdef CONFIG_USB_SERIAL_QUECTEL_SUPPORT
156++ /* Fold Quectel ZLP quirk into the generic use_zlp path. */
157++ if (usb_wwan_quectel_use_zlp(serial->dev))
158++ device_flags |= ZLP;
159++#endif
160++
161+ /* Store the device flags so we can use them during attach. */
162+ usb_set_serial_data(serial, (void *)device_flags);
163+
164+diff --git a/drivers/usb/serial/qcserial.c b/drivers/usb/serial/qcserial.c
165+index 061ff754b..d90e93291 100644
166+--- a/drivers/usb/serial/qcserial.c
167++++ b/drivers/usb/serial/qcserial.c
168+@@ -63,7 +63,9 @@ static const struct usb_device_id id_table[] = {
169+ {DEVICE_G1K(0x05c6, 0x9202)}, /* Generic Gobi Modem device */
170+ {DEVICE_G1K(0x05c6, 0x9203)}, /* Generic Gobi Modem device */
171+ {DEVICE_G1K(0x05c6, 0x9222)}, /* Generic Gobi Modem device */
172++#ifndef CONFIG_USB_SERIAL_QUECTEL_SUPPORT
173+ {DEVICE_G1K(0x05c6, 0x9008)}, /* Generic Gobi QDL device */
174++#endif
175+ {DEVICE_G1K(0x05c6, 0x9009)}, /* Generic Gobi Modem device */
176+ {DEVICE_G1K(0x05c6, 0x9201)}, /* Generic Gobi QDL device */
177+ {DEVICE_G1K(0x05c6, 0x9221)}, /* Generic Gobi QDL device */
178+@@ -439,6 +441,11 @@ static int qc_attach(struct usb_serial *serial)
179+ if (sendsetup)
180+ data->use_send_setup = 1;
181+
182++#ifdef CONFIG_USB_SERIAL_QUECTEL_SUPPORT
183++ if (usb_wwan_quectel_use_zlp(serial->dev))
184++ data->use_zlp = 1;
185++#endif
186++
187+ spin_lock_init(&data->susp_lock);
188+
189+ usb_set_serial_data(serial, data);
190+diff --git a/drivers/usb/serial/usb-wwan.h b/drivers/usb/serial/usb-wwan.h
191+index 519101945..91dff5a51 100644
192+--- a/drivers/usb/serial/usb-wwan.h
193++++ b/drivers/usb/serial/usb-wwan.h
194+@@ -6,6 +6,8 @@
195+ #ifndef __LINUX_USB_USB_WWAN
196+ #define __LINUX_USB_USB_WWAN
197+
198++/* Quectel extensions are gated by CONFIG_USB_SERIAL_QUECTEL_SUPPORT. */
199++
200+ extern void usb_wwan_dtr_rts(struct usb_serial_port *port, int on);
201+ extern int usb_wwan_open(struct tty_struct *tty, struct usb_serial_port *port);
202+ extern void usb_wwan_close(struct usb_serial_port *port);
203+@@ -22,6 +24,9 @@ extern unsigned int usb_wwan_chars_in_buffer(struct tty_struct *tty);
204+ extern int usb_wwan_suspend(struct usb_serial *serial, pm_message_t message);
205+ extern int usb_wwan_resume(struct usb_serial *serial);
206+ #endif
207++#ifdef CONFIG_USB_SERIAL_QUECTEL_SUPPORT
208++extern bool usb_wwan_quectel_use_zlp(struct usb_device *udev);
209++#endif
210+
211+ /* per port private data */
212+
213+diff --git a/drivers/usb/serial/usb_wwan.c b/drivers/usb/serial/usb_wwan.c
214+index fc87c4307..679a7c73d 100644
215+--- a/drivers/usb/serial/usb_wwan.c
216++++ b/drivers/usb/serial/usb_wwan.c
217+@@ -228,11 +228,7 @@ static void usb_wwan_indat_callback(struct urb *urb)
218+ __func__, status, endpoint);
219+
220+ /* don't resubmit on fatal errors */
221+-#ifdef CONFIG_ARCH_HISILICON
222+- if (status == -ESHUTDOWN || status == -ENOENT || status == -EPROTO)
223+-#else
224+ if (status == -ESHUTDOWN || status == -ENOENT)
lijiawei
lijiaweilijiawei9月2日

[linux-6.6/hispark_aifly_patch/0010-kernel-quectel-4g.patch:224] 移除 #ifdef CONFIG_ARCH_HISILICON 对 -EPROTO 的处理,会影响所有 HiSilicon 平台(不仅限 Quectel)。在 USB 物理断开时,-EPROTO 是常见错误码,移除此检查会导致 HiSilicon 平台上 URB 在 -EPROTO 时仍被重新提交,可能引发持续错误循环。此修改与 Quectel 4G 适配无关,应独立提交并说明原因,或保留原有条件编译。

likedislike
225+-#endif
226+ return;
227+ } else {
228+ if (urb->actual_length) {
229+@@ -657,6 +653,32 @@ int usb_wwan_resume(struct usb_serial *serial)
230+ EXPORT_SYMBOL(usb_wwan_resume);
231+ #endif
232+
233++#ifdef CONFIG_USB_SERIAL_QUECTEL_SUPPORT
234++/*
235++ * Quectel (and related) modems need URB_ZERO_PACKET on bulk OUT.
236++ * Callers set intfdata->use_zlp so usb_wwan_setup_urb applies it once.
237++ */
238++bool usb_wwan_quectel_use_zlp(struct usb_device *udev)
239++{
240++ u16 vid = le16_to_cpu(udev->descriptor.idVendor);
241++ u16 pid = le16_to_cpu(udev->descriptor.idProduct);
242++
243++ if (vid == 0x2c7c)
244++ return true;
245++
246++ if (vid == 0x05c6 &&
247++ (pid == 0x9090 || pid == 0x9003 || pid == 0x9215 ||
248++ pid == 0x9091 || pid == 0x90db))
249++ return true;
250++
251++ if (vid == 0x3731 && (pid == 0x0102 || pid == 0x0103))
252++ return true;
253++
254++ return false;
255++}
256++EXPORT_SYMBOL_GPL(usb_wwan_quectel_use_zlp);
257++#endif
258++
259+ MODULE_AUTHOR(DRIVER_AUTHOR);
260+ MODULE_DESCRIPTION(DRIVER_DESC);
261+ MODULE_LICENSE("GPL v2");
262+diff --git a/drivers/vendor/basedrv_clk/ss928v100/clk_ups.c b/drivers/vendor/basedrv_clk/ss928v100/clk_ups.c
263+index 45052a687..0f1186cc4 100644
264+--- a/drivers/vendor/basedrv_clk/ss928v100/clk_ups.c
265++++ b/drivers/vendor/basedrv_clk/ss928v100/clk_ups.c
266+@@ -19,6 +19,9 @@
267+ #include <dt-bindings/clock/basedrv-clock.h>
268+ #include <asm/io.h>
269+ #include <linux/delay.h>
270++#include <linux/gpio.h>
271++#include <linux/of.h>
272++#include <linux/of_gpio.h>
273+
274+ #include "../basedrv_clk.h"
275+
276+@@ -66,6 +69,19 @@
277+ #define PITOUT_CTRL1_PWREN_OFFSET 0X3C
278+ #define PITOUT_CTRL1_VBUS_OFFSET 0x38
279+ #define PINOUT_USB_VAL 0x1201
280++#ifdef CONFIG_USB_SERIAL_QUECTEL_SUPPORT
281++#define IO_CFG_REG13_OFFSET 0x34
282++#define IO_CFG_REG13_MUX_MASK 0xf
283++#define IO_CFG_REG13_GPIO1_5_MUX 0x2
284++/*
285++ * Board "4g-perst" idle/run level is low (see former gpio-hog output-low).
286++ * Assert reset by driving high for >= 150ms, then return low to run.
287++ */
288++#define EC20_PERST_HOLD_MS 150
289++#define GPIO1_REG_BASE 0x11091000
290++#define PL061_GPIODIR 0x400
291++#define EC20_PERST_PIN 5
292++#endif
293+
294+ static unsigned int basedrv_clk_readl(const void __iomem *addr)
295+ {
296+@@ -88,6 +104,106 @@ typedef enum mode {
297+ UNKOWN_MODE
298+ } combphy_mode;
299+
300++#ifdef CONFIG_USB_SERIAL_QUECTEL_SUPPORT
301++/*
302++ * Step 1 of PERST bring-up: mux GPIO1_5 to GPIO mode only.
303++ * Direction and level are set in ec20_4g_perst_reset_seq() / _mmio_seq().
304++ */
305++static void ec20_4g_perst_pinmux_cfg(void __iomem *pinout_base)
306++{
307++ u32 val;
308++
309++ val = basedrv_clk_readl(pinout_base + IO_CFG_REG13_OFFSET);
310++ val = (val & ~IO_CFG_REG13_MUX_MASK) | IO_CFG_REG13_GPIO1_5_MUX;
311++ basedrv_clk_writel(val, pinout_base + IO_CFG_REG13_OFFSET);
312++}
313++
314++/*
315++ * Step 2 (MMIO fallback): set GPIO1_5 as output and pulse PERST.
316++ *
317++ * Note: Quectel RESET_N datasheet is active-low (high = run). This board's
318++ * "4g-perst" line matches the former gpio-hog idle level: low = run,
319++ * high = reset. Verified with EC20F USB enumeration.
320++ */
321++static void ec20_4g_perst_mmio_seq(void)
322++{
323++ void __iomem *gpio;
324++ u32 dir;
325++
326++ gpio = ioremap(GPIO1_REG_BASE, 0x1000);
327++ if (!gpio) {
328++ basedrv_clk_info("map GPIO1 for EC20 PERST failed\n");
329++ return;
330++ }
331++
332++ /* Output direction for GPIO1_5 */
333++ dir = basedrv_clk_readl(gpio + PL061_GPIODIR);
334++ basedrv_clk_writel(dir | BIT(EC20_PERST_PIN), gpio + PL061_GPIODIR);
335++
336++ /* Assert reset (high), hold, then release to run (low). */
337++ basedrv_clk_writel(BIT(EC20_PERST_PIN), gpio + (BIT(EC20_PERST_PIN) << 2));
lijiawei
lijiaweilijiawei9月2日

[linux-6.6/hispark_aifly_patch/0010-kernel-quectel-4g.patch:337] ec20_4g_perst_mmio_seq 中 GPIO 寄存器写入模式 basedrv_clk_writel(BIT(EC20_PERST_PIN), gpio + (BIT(EC20_PERST_PIN) << 2)) 假设 PL061 控制器具有按位原子设置寄存器。但标准 PL061 IP 只有一个数据寄存器(偏移 0x0),写操作是全字写入而非按位操作。若此处依赖的是厂商扩展寄存器布局,请添加注释说明;否则应改为读-改-写模式操作数据寄存器。

likedislike
338++ msleep(EC20_PERST_HOLD_MS);
339++ basedrv_clk_writel(0, gpio + (BIT(EC20_PERST_PIN) << 2));
340++
341++ iounmap(gpio);
342++ basedrv_clk_info("EC20 PERST reset done via MMIO (high %u ms then low)\n",
343++ EC20_PERST_HOLD_MS);
344++}
345++
346++/*
347++ * Full EC20 PERST after VBUS is enabled:
348++ * 1) pinmux GPIO1_5
349++ * 2) request GPIO (or MMIO), set output
350++ * 3) pulse high >= 150ms, hold low to run
351++ */
352++static void ec20_4g_perst_reset_seq(void __iomem *pinout_base)
353++{
354++ static bool done;
lijiawei
lijiaweilijiawei9月2日

[linux-6.6/hispark_aifly_patch/0010-kernel-quectel-4g.patch:354] ec20_4g_perst_reset_seq 中使用 static bool done 控制只执行一次,但无任何锁保护。若 usb_pinout_cfg 可能在多线程上下文中被调用,存在竞态条件。建议使用 atomic_t 或 READ_ONCE/WRITE_ONCE 保护,或添加注释说明调用上下文保证串行执行。

likedislike
355++ struct device_node *np;
356++ int gpio;
357++ int ret;
358++
359++ if (done)
360++ return;
361++
362++ ec20_4g_perst_pinmux_cfg(pinout_base);
363++
364++ np = of_find_compatible_node(NULL, NULL, "quectel,ec20-4g");
365++ if (!np) {
366++ basedrv_clk_info("EC20 DT node missing, use MMIO PERST\n");
367++ ec20_4g_perst_mmio_seq();
368++ done = true;
369++ return;
370++ }
371++
372++ gpio = of_get_named_gpio(np, "reset-gpios", 0);
373++ of_node_put(np);
374++ if (!gpio_is_valid(gpio)) {
375++ basedrv_clk_info("EC20 PERST gpio invalid, use MMIO\n");
376++ ec20_4g_perst_mmio_seq();
377++ done = true;
378++ return;
379++ }
380++
381++ ret = gpio_request(gpio, "ec20-perst");
lijiawei
lijiaweilijiawei9月2日

[linux-6.6/hispark_aifly_patch/0010-kernel-quectel-4g.patch:381] gpio_request(gpio, "ec20-perst") 成功后,GPIO 永不释放。虽然注释说明是为了保持低电平驱动,但 done = true 标志也会阻止后续调用进入 MMIO 回退路径。若 GPIO request 失败后通过 MMIO 完成 reset,done = true 会阻止重试。建议在注释中明确说明 GPIO 和 done 标志的生命周期管理策略。

likedislike
382++ if (ret) {
383++ basedrv_clk_info("EC20 PERST gpio_request %d, use MMIO\n", ret);
384++ ec20_4g_perst_mmio_seq();
385++ done = true;
386++ return;
387++ }
388++
389++ /* Output + reset pulse: high (assert) then low (run). */
390++ gpio_direction_output(gpio, 1);
391++ msleep(EC20_PERST_HOLD_MS);
392++ gpio_set_value(gpio, 0);
393++ /* Keep ownership so line stays driven low (run). */
394++ done = true;
395++ basedrv_clk_info("EC20 PERST reset done (high %u ms then low)\n",
396++ EC20_PERST_HOLD_MS);
397++}
398++#endif
399++
400+ static void usb_pinout_cfg(void)
401+ {
402+ void __iomem *addr = NULL;
403+@@ -102,6 +218,9 @@ static void usb_pinout_cfg(void)
404+ basedrv_clk_writel(PINOUT_USB_VAL, addr + PITOUT_CTRL1_PWREN_OFFSET);
405+ basedrv_clk_writel(PINOUT_USB_VAL, addr + PITOUT_CTRL1_VBUS_OFFSET);
406+ udelay(200); /* delay 200 us to wait vbus stable */
407++#ifdef CONFIG_USB_SERIAL_QUECTEL_SUPPORT
408++ ec20_4g_perst_reset_seq(addr);
409++#endif
410+
411+ iounmap(addr);
412+ addr = NULL;
@@ -0,0 +1,67 @@
1+#!/bin/sh
2+# Copyright (c) 2026 HiSilicon (Shanghai) Technologies Co., Ltd.
3+# This program is free software; you can redistribute it and/or
4+# modify it under the terms of the GNU General Public License
5+# as published by the Free Software Foundation; either version 2
6+# of the License, or (at your option) any later version.
7+#
8+# This program is distributed in the hope that it will be useful,
9+# but WITHOUT ANY WARRANTY; without even the implied warranty of
10+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11+# GNU General Public License for more details.
12+#
13+# You should have received a copy of the GNU General Public License
14+# along with this program; if not, see
15+# <https://www.gnu.org/licenses/>.
16+ 
17+patch_dir=${1}
18+echo "${patch_dir}: ${1}"
19+echo "Current directory $(pwd)"
20+echo "patching aifly begin..."
21+ 
22+# Apply patch idempotently: skip if already applied, fail on real conflicts.
23+apply_patch() {
24+ patch_file="$1"
25+ name=$(basename "${patch_file}")
26+ 
27+ if patch -p1 --dry-run < "${patch_file}" >/dev/null 2>&1; then
28+ patch -p1 < "${patch_file}" || exit 1
29+ echo "Applied: ${name}"
30+ elif patch -p1 --dry-run -R < "${patch_file}" >/dev/null 2>&1; then
31+ echo "Already applied, skipping: ${name}"
32+ else
33+ echo "Failed to apply patch: ${name}" >&2
34+ exit 1
35+ fi
36+}
37+ 
38+# DTC resolves <dt-bindings/...> only under scripts/dtc/include-prefixes.
39+# Vendor patches add new headers under include/dt-bindings; hardlink any
40+# missing files into the DTC include tree.
41+sync_dt_bindings() {
42+ src_root="include/dt-bindings"
43+ dtc_root="scripts/dtc/include-prefixes/dt-bindings"
44+ 
45+ [ -d "${src_root}" ] || return 0
46+ 
47+ find "${src_root}" -type f | while read -r src; do
48+ rel="${src#${src_root}/}"
49+ dst="${dtc_root}/${rel}"
50+ [ -e "${dst}" ] && continue
51+ mkdir -p "$(dirname "${dst}")"
52+ ln "${src}" "${dst}"
lijiawei
lijiaweilijiawei9月1日

[linux-6.6/hispark_aifly_patch/patch_hispark_aifly.sh:52] 使用硬链接 ln 而非符号链接 ln -s。若 include/dt-bindings 和 scripts/dtc/include-prefixes 位于不同文件系统,硬链接将失败。建议改用 ln -s 或 cp 以增强兼容性。

likedislike
53+ done
54+}
55+ 
56+apply_patch "${patch_dir}/0001-kernel-hispark_aifly.patch"
57+apply_patch "${patch_dir}/0002-kernel-compile-support.patch"
58+apply_patch "${patch_dir}/0003-kernel-drm.patch"
59+apply_patch "${patch_dir}/0004-kernel-vo.patch"
60+apply_patch "${patch_dir}/0005-kernel-dhcp-support.patch"
61+apply_patch "${patch_dir}/0006-kernel-mipi-support.patch"
62+apply_patch "${patch_dir}/0007-kernel-nca9555.patch"
63+apply_patch "${patch_dir}/0008-power-key-dts.patch"
64+apply_patch "${patch_dir}/0009-hdf-soc-path.patch"
65+apply_patch "${patch_dir}/0010-kernel-quectel-4g.patch"
66+sync_dt_bindings
67+echo "patching aifly finished..."