相關信息
硬件平臺:全志T507
系統版本:Android 10 / Linux 4.9.170
問題描述:PF4 無法通過標準接口設置為中斷模式,而 PF1、PF2、PF3、PF5 正常可用。
分析過程
一開始以為是引腳被其它驅動占用引起,或者該引腳不具備中斷功能,經過排查,已排除這兩種可能,因此通過從源碼分析來找問題的根因。
以下是以 gpio_keys.c 驅動為入口進行分析:
// drivers/input/keyboard/gpio_keys.c
static int gpio_keys_setup_key(struct platform_device *pdev,
struct input_dev *input,
struct gpio_button_data *bdata,
const struct gpio_keys_button *button)
{
......
error = devm_request_any_context_irq(&pdev- >dev, bdata- >irq,
isr, irqflags, desc, bdata);
}
// kernel/irq/devres.c
int devm_request_any_context_irq(struct device *dev, unsigned int irq,
irq_handler_t handler, unsigned long irqflags,
const char *devname, void *dev_id)
{
......
rc = request_any_context_irq(irq, handler, irqflags, devname, dev_id);
if (rc < 0) {
devres_free(dr);
return rc;
}
......
return rc;
}
// kernel/irq/manage.c
int request_any_context_irq(unsigned int irq, irq_handler_t handler,
unsigned long flags, const char *name, void *dev_id)
{
......
ret = request_irq(irq, handler, flags, name, dev_id);
return !ret ? IRQC_IS_HARDIRQ : ret;
}
// include/linux/interrupt.h
static inline int __must_check
request_irq(unsigned int irq, irq_handler_t handler, unsigned long flags,
const char *name, void *dev)
{
return request_threaded_irq(irq, handler, NULL, flags, name, dev);
}
// kernel/irq/manage.c
int request_threaded_irq(unsigned int irq, irq_handler_t handler,
irq_handler_t thread_fn, unsigned long irqflags,
const char *devname, void *dev_id)
{
......
chip_bus_lock(desc);
retval = __setup_irq(irq, desc, action);
chip_bus_sync_unlock(desc);
......
return retval;
}
// kernel/irq/manage.c
static int __setup_irq(unsigned int irq, struct irq_desc *desc, struct irqaction *new)
{
......
if (!shared) {
ret = irq_request_resources(desc);
if (ret) {
pr_err("Failed to request resources for %s (irq %d) on irqchip %sn",
new- >name, irq, desc- >irq_data.chip- >name);
goto out_mask;
}
......
}
......
}
// kernel/irq/manage.c
static int irq_request_resources(struct irq_desc *desc)
{
struct irq_data *d = &desc- >irq_data;
struct irq_chip *c = d- >chip;
return c- >irq_request_resources ? c- >irq_request_resources(d) : 0;
}
// drivers/pinctrl/sunxi/pinctrl-sunxi.c
static struct irq_chip sunxi_pinctrl_edge_irq_chip = {
.name = "sunxi_pio_edge",
.irq_ack = sunxi_pinctrl_irq_ack,
.irq_mask = sunxi_pinctrl_irq_mask,
.irq_unmask = sunxi_pinctrl_irq_unmask,
.irq_request_resources = sunxi_pinctrl_irq_request_resources,
.irq_release_resources = sunxi_pinctrl_irq_release_resources,
.irq_set_type = sunxi_pinctrl_irq_set_type,
.irq_set_wake = sunxi_pinctrl_irq_set_wake,
};
// drivers/pinctrl/sunxi/pinctrl-sunxi.c
static int sunxi_pinctrl_irq_request_resources(struct irq_data *d)
{
struct sunxi_pinctrl *pctl = irq_data_get_irq_chip_data(d);
struct sunxi_desc_function *func;
func = sunxi_pinctrl_desc_find_function_by_pin(pctl,
pctl- >irq_array[d- >hwirq], "irq");
if (!func)
return -EINVAL;
/* Change muxing to INT mode */
printk(KERN_EMERG"[lmx] irq:%d set int mode pin:%d d- >hwirq:%ld func- >muxval:%dn", d- >irq, pctl- >irq_array[d- >hwirq], d- >hwirq, func- >muxval);
sunxi_pmx_set(pctl- >pctl_dev, pctl- >irq_array[d- >hwirq], func- >muxval);
return 0;
}
// drivers/pinctrl/sunxi/pinctrl-sunxi.c
static void sunxi_pmx_set(struct pinctrl_dev *pctldev,
unsigned pin,
u8 config)
{
struct sunxi_pinctrl *pctl = pinctrl_dev_get_drvdata(pctldev);
unsigned long flags;
u32 val, mask;
raw_spin_lock_irqsave(&pctl- >lock, flags);
pin -= pctl- >desc- >pin_base;
val = readl(pctl- >membase + sunxi_mux_reg(pin));
mask = MUX_PINS_MASK < < sunxi_mux_offset(pin);
writel((val & ~mask) | config < < sunxi_mux_offset(pin),
pctl- >membase + sunxi_mux_reg(pin));
raw_spin_unlock_irqrestore(&pctl- >lock, flags);
}
無論有多復雜的代碼,最終都需要通過讀寫寄存器的方式來實現控制芯片,而通過上述代碼分析,即可發現 sunxi_pmx_set() 接口用于配置寄存器,是最底層的接口,可以通過打印輸出傳入的參數,來檢查是否有問題。
PF3 打印輸出為:
[ 10.683205] [lmx] irq:148 set int mode pin:163 d- >hwirq:131 func- >muxval:6
PF4 打印輸出為:
[ 10.683557] [lmx] irq:149 set int mode pin:196 d- >hwirq:132 func- >muxval:6
這里就能看出很奇怪的地方,PF3 的引腳編號是 163,而 PF4 卻是 196,跨度很大。
通過以下指令查詢 PF4 的正確引腳編號,也可以得知 196 引腳編號是哪一組:
mercury-demo:/ # cat /sys/kernel/debug/pinctrl/pio/pins
registered pins: 137
......
pin 160 (PF0)
pin 161 (PF1)
pin 162 (PF2)
pin 163 (PF3)
pin 164 (PF4)
pin 165 (PF5)
pin 166 (PF6)
......
pin 196 (PG4)
pin 197 (PG5)
......
確認 PF4 正確引腳編號是 164,而 196 對應是 PG4,實際生效的是 PG4,通過以下指令即可確認:
mercury-demo:/sys/kernel/debug/sunxi_pinctrl # echo PG4 > sunxi_pin
mercury-demo:/sys/kernel/debug/sunxi_pinctrl # cat *
pin[PG4] data: 1
pio
pin[PG4] dlevel: 1
pin[PG4] funciton: 6
NOMATCH
pin[PG4] pull: 1
PG4
pin[PG4] funciton: 6
pin[PG4] data: 1
pin[PG4] dlevel: 1
pin[PG4] pull: 1
根據代碼確定引腳編號來源于 pctl->irq_array 數組,通過 pctl->irq_array 賦值的地方進行打印輸出,是否一開始就出錯了:
// drivers/pinctrl/sunxi/pinctrl-sunxi.c
static int sunxi_pinctrl_build_state(struct platform_device *pdev)
{
......
/* Count functions associated groups */
for (i = 0; i pctl- >desc- >npins; i++) {
const struct sunxi_desc_pin *pin = pctl- >desc- >pins + i;
struct sunxi_desc_function *func = pin- >functions;
while (func- >name) {
/* Create interrupt mapping while we're at it */
if (!strcmp(func- >name, "irq")) {
int irqnum = func- >irqnum + func- >irqbank * IRQ_PER_BANK;
pctl- >irq_array[irqnum] = pin- >pin.number;
printk(KERN_EMERG"[lmx] pctl- >irq_array[%d] = %d (func- >irqnum:%d func- >irqbank:%d)n", irqnum, pin- >pin.number, func- >irqnum, func- >irqbank);
}
sunxi_pinctrl_add_function(pctl, func- >name);
func++;
}
}
......
return 0;
}
// drivers/pinctrl/sunxi/pinctrl-sunxi.h
#define IRQ_PER_BANK 32
可以發現,PF4(164)對應的索引是 132,原本被正確賦值為 164,但又被覆蓋為 PG4(196)。
不難發現,出現覆蓋的原因是因為 PG4 的 func->irqbank 數值錯誤(4),導致索引下標計算錯誤。
根據前后文來看,func->irqbank 的正確數值應該是 5,代入計算得到正確的值 164:
int irqnum(164) = func->irqnum(4) + func->irqbank(5) * IRQ_PER_BANK(32);
大概率硬件資源描述配置出錯,通過搜索 irqbank 被賦值的方法,來定位描述配置出錯的地方:
// drivers/pinctrl/sunxi/pinctrl-sunxi.h
#define SUNXI_FUNCTION_IRQ_BANK(_val, _bank, _irq)
{
.name = "irq",
.muxval = _val,
.irqbank = _bank,
.irqnum = _irq,
}
使用的是 SUNXI_FUNCTION_IRQ_BANK 宏,重點檢查第二個參數:
// drivers/pinctrl/sunxi/pinctrl-sun50iw9p1.c
static const struct sunxi_desc_pin sun50iw9p1_pins[] = {
......
SUNXI_PIN(SUNXI_PINCTRL_PIN(G, 3),
SUNXI_FUNCTION(0x0, "gpio_in"),
SUNXI_FUNCTION(0x1, "gpio_out"),
SUNXI_FUNCTION(0x2, "sdc1"), /* D1 */
SUNXI_FUNCTION_IRQ_BANK(0x6, 5, 3), /* PG_EINT3 */
SUNXI_FUNCTION(0x7, "io_disabled")),
SUNXI_PIN(SUNXI_PINCTRL_PIN(G, 4),
SUNXI_FUNCTION(0x0, "gpio_in"),
SUNXI_FUNCTION(0x1, "gpio_out"),
SUNXI_FUNCTION(0x2, "sdc1"), /* D2 */
// 可以發現第二個參數恰好是 4,根據分析結果,以及結合上下文,正確的應該是 5
SUNXI_FUNCTION_IRQ_BANK(0x6, 4, 4), /* PG_EINT4 */
SUNXI_FUNCTION(0x7, "io_disabled")),
SUNXI_PIN(SUNXI_PINCTRL_PIN(G, 5),
SUNXI_FUNCTION(0x0, "gpio_in"),
SUNXI_FUNCTION(0x1, "gpio_out"),
SUNXI_FUNCTION(0x2, "sdc1"), /* D3 */
SUNXI_FUNCTION_IRQ_BANK(0x6, 5, 5), /* PG_EINT5 */
SUNXI_FUNCTION(0x7, "io_disabled")),
......
};
修改之后的 pctl->irq_array 打印輸出正確:
進行實測,PF4 已經可以正常的被設置為中斷模式。
問題總結
全志原廠提供的 SoCs pinctrl driver 中的 PG4 中斷信息描述錯誤,導致覆蓋了 PF4 的引腳編號,因此只要修正 PG4 的描述信息,即可解決問題。
這個問題不僅僅會影響 PF4 無法使用,也會影響 PG4 引腳無法使用,從代碼來看,想要設置為 PG4 為中斷模式,實際修改的會 PA0(0)。
--- a/longan/kernel/linux-4.9/drivers/pinctrl/sunxi/pinctrl-sun50iw9p1.c
+++ b/longan/kernel/linux-4.9/drivers/pinctrl/sunxi/pinctrl-sun50iw9p1.c
@@ -693,7 +693,7 @@
SUNXI_FUNCTION(0x0, "gpio_in"),
SUNXI_FUNCTION(0x1, "gpio_out"),
SUNXI_FUNCTION(0x2, "sdc1"), /* D2 */
- SUNXI_FUNCTION_IRQ_BANK(0x6, 4, 4), /* PG_EINT4 */
+ SUNXI_FUNCTION_IRQ_BANK(0x6, 5, 4), /* PG_EINT4 */
SUNXI_FUNCTION(0x7, "io_disabled")),
SUNXI_PIN(SUNXI_PINCTRL_PIN(G, 5),
SUNXI_FUNCTION(0x0, "gpio_in"),
-
驅動器
+關注
關注
52文章
8227瀏覽量
146258 -
寄存器
+關注
關注
31文章
5336瀏覽量
120232 -
Linux系統
+關注
關注
4文章
593瀏覽量
27392 -
IRQ
+關注
關注
0文章
16瀏覽量
10761
發布評論請先 登錄
相關推薦
評論