文章目录
1.原理
1.1.硬件电路
1.2.管脚信息
1.3.GPIO内存映射图
1.4 管脚中断号
2.实现
2.1.设备树配置
2.1.1.mt7621.dtsi
gpio@600 {
#address-cells = <1>;
#size-cells = <0>;
compatible = "mtk,mt7621-gpio";
reg = <0x600 0x100>;
interrupt-parent = <&gic>;
interrupts = <GIC_SHARED 12 IRQ_TYPE_LEVEL_HIGH>;
gpio0: bank@0 {
reg = <0>;
compatible = "mtk,mt7621-gpio-bank";
gpio-controller;
#gpio-cells = <2>;
};
gpio1: bank@1 {
reg = <1>;
compatible = "mtk,mt7621-gpio-bank";
gpio-controller;
#gpio-cells = <2>;
};
gpio2: bank@2 {
reg = <2>;
compatible = "mtk,mt7621-gpio-bank";
gpio-controller;
#gpio-cells = <2>;
};
};
2.1.2 ctunite-t2.dts
/ {
compatible = "mediatek,ctunite-t2", "mediatek,mt7621-soc";
model = "Ctunite-T2";
chosen {
bootargs = "console=ttyS0,115200";
};
gpio-keys-polled {
compatible = "gpio-keys-polled";
#address-cells = <1>;
#size-cells = <0>;
poll-interval = <20>;
reset {
label = "reset";
gpios = <&gpio0 18 1>;
linux,code = <KEY_RESTART>;
};
};
};
2.2.配置
make menuconfig
1、开启按键热插拔驱动
2.3.驱动
2.3.1.mt7621 gpio驱动源码
linux-4.4.198/drivers/gpio/gpio-mt7621.c
static int
mediatek_gpio_probe(struct platform_device *pdev)
{
struct device_node *bank, *np = pdev->dev.of_node;
struct resource *res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
mediatek_gpio_membase = devm_ioremap_resource(&pdev->dev, res);
if (IS_ERR(mediatek_gpio_membase))
return PTR_ERR(mediatek_gpio_membase);
mediatek_gpio_irq = irq_of_parse_and_map(np, 0);
if (mediatek_gpio_irq) {
mediatek_gpio_irq_domain = irq_domain_add_linear(np,
MTK_MAX_BANK * MTK_BANK_WIDTH,
&irq_domain_ops, NULL);
if (!mediatek_gpio_irq_domain)
dev_notice(&pdev->dev, "irq_domain_add_linear failed\n");
}
for_each_child_of_node(np, bank)
if (of_device_is_compatible(bank, "mtk,mt7621-gpio-bank"))
mediatek_gpio_bank_probe(pdev, bank);
if (mediatek_gpio_irq_domain)
irq_set_chained_handler(mediatek_gpio_irq, mediatek_gpio_irq_handler);
return 0;
}
static const struct of_device_id mediatek_gpio_match[] = {
{ .compatible = "mtk,mt7621-gpio" },
{},
};
MODULE_DEVICE_TABLE(of, mediatek_gpio_match);
static struct platform_driver mediatek_gpio_driver = {
.probe = mediatek_gpio_probe,
.driver = {
.name = "mt7621_gpio",
.owner = THIS_MODULE,
.of_match_table = mediatek_gpio_match,
},
};
static int __init
mediatek_gpio_init(void)
{
return platform_driver_register(&mediatek_gpio_driver);
}
arch_initcall(mediatek_gpio_init);
static void
mediatek_gpio_irq_handler(struct irq_desc *desc)
{
int i;
for (i = 0; i < MTK_MAX_BANK; i++) {
struct mtk_gc *rg = gc_map[i];
unsigned long pending;
int bit;
if (!rg)
continue;
pending = mtk_gpio_r32(rg, GPIO_REG_STAT);
for_each_set_bit(bit, &pending, MTK_BANK_WIDTH) {
u32 map = irq_find_mapping(mediatek_gpio_irq_domain, (MTK_BANK_WIDTH * i) + bit);
generic_handle_irq(map);
mtk_gpio_w32(rg, GPIO_REG_STAT, BIT(bit));
}
}
}
2.3.2 按键热插拔驱动
static struct platform_driver gpio_keys_driver = {
.probe = gpio_keys_probe,
.remove = gpio_keys_remove,
.driver = {
.name = "gpio-keys",
.owner = THIS_MODULE,
.of_match_table = of_match_ptr(gpio_keys_of_match),
},
};
static struct platform_driver gpio_keys_polled_driver = {
.probe = gpio_keys_polled_probe,
.remove = gpio_keys_remove,
.driver = {
.name = "gpio-keys-polled",
.owner = THIS_MODULE,
.of_match_table = of_match_ptr(gpio_keys_polled_of_match),
},
};
static int __init gpio_button_init(void)
{
int ret;
ret = platform_driver_register(&gpio_keys_driver);
if (ret)
return ret;
ret = platform_driver_register(&gpio_keys_polled_driver);
if (ret)
platform_driver_unregister(&gpio_keys_driver);
return ret;
}
static void __exit gpio_button_exit(void)
{
platform_driver_unregister(&gpio_keys_driver);
platform_driver_unregister(&gpio_keys_polled_driver);
}
module_init(gpio_button_init);
module_exit(gpio_button_exit);
按键中断服务函数
static irqreturn_t button_handle_irq(int irq, void *_bdata)
{
struct gpio_keys_button_data *bdata = (struct gpio_keys_button_data *) _bdata;
button_hotplug_event(bdata, bdata->b->type ?: EV_KEY, gpio_button_get_value(bdata));
return IRQ_HANDLED;
}
创建按键热插拔事件
static void button_hotplug_event(struct gpio_keys_button_data *data,
unsigned int type, int value)
{
struct bh_priv *priv = &data->bh;
unsigned long seen = jiffies;
int btn;
BH_DBG("event type=%u, code=%u, value=%d\n", type, data->b->code, value);
if ((type != EV_KEY) && (type != EV_SW))
return;
btn = button_get_index(data->b->code);
if (btn < 0)
return;
button_hotplug_create_event(button_map[btn].name, type,
(seen - priv->seen) / HZ, value);
priv->seen = seen;
}
热插拔事件类型
static struct bh_map button_map[] = {
BH_MAP(BTN_0, "BTN_0"),
BH_MAP(BTN_1, "BTN_1"),
BH_MAP(BTN_2, "BTN_2"),
BH_MAP(BTN_3, "BTN_3"),
BH_MAP(BTN_4, "BTN_4"),
BH_MAP(BTN_5, "BTN_5"),
BH_MAP(BTN_6, "BTN_6"),
BH_MAP(BTN_7, "BTN_7"),
BH_MAP(BTN_8, "BTN_8"),
BH_MAP(BTN_9, "BTN_9"),
BH_MAP(KEY_BRIGHTNESS_ZERO, "brightness_zero"),
BH_MAP(KEY_CONFIG, "config"),
BH_MAP(KEY_COPY, "copy"),
BH_MAP(KEY_EJECTCD, "eject"),
BH_MAP(KEY_HELP, "help"),
BH_MAP(KEY_LIGHTS_TOGGLE, "lights_toggle"),
BH_MAP(KEY_PHONE, "phone"),
BH_MAP(KEY_POWER, "power"),
BH_MAP(KEY_RESTART, "reset"),
BH_MAP(KEY_RFKILL, "rfkill"),
BH_MAP(KEY_VIDEO, "video"),
BH_MAP(KEY_WIMAX, "wwan"),
BH_MAP(KEY_WLAN, "wlan"),
BH_MAP(KEY_WPS_BUTTON, "wps"),
};
组装netlink报文,以广播形式发送netlink报文
static int button_hotplug_fill_event(struct bh_event *event)
{
int ret;
ret = bh_event_add_var(event, 0, "HOME=%s", "/");
if (ret)
return ret;
ret = bh_event_add_var(event, 0, "PATH=%s",
"/sbin:/bin:/usr/sbin:/usr/bin");
if (ret)
return ret;
ret = bh_event_add_var(event, 0, "SUBSYSTEM=%s", "button");
if (ret)
return ret;
ret = bh_event_add_var(event, 0, "ACTION=%s", event->action);
if (ret)
return ret;
ret = bh_event_add_var(event, 0, "BUTTON=%s", event->name);
if (ret)
return ret;
if (event->type == EV_SW) {
ret = bh_event_add_var(event, 0, "TYPE=%s", "switch");
if (ret)
return ret;
}
ret = bh_event_add_var(event, 0, "SEEN=%ld", event->seen);
if (ret)
return ret;
ret = bh_event_add_var(event, 0, "SEQNUM=%llu", uevent_next_seqnum());
return ret;
}
static void button_hotplug_work(struct work_struct *work)
{
struct bh_event *event = container_of(work, struct bh_event, work);
int ret = 0;
event->skb = alloc_skb(BH_SKB_SIZE, GFP_KERNEL);
if (!event->skb)
goto out_free_event;
ret = bh_event_add_var(event, 0, "%s@", event->action);
if (ret)
goto out_free_skb;
ret = button_hotplug_fill_event(event);
if (ret)
goto out_free_skb;
NETLINK_CB(event->skb).dst_group = 1;
broadcast_uevent(event->skb, 0, 1, GFP_KERNEL);
out_free_skb:
if (ret) {
BH_ERR("work error %d\n", ret);
kfree_skb(event->skb);
}
out_free_event:
kfree(event);
}
2.3.3.procd捕获按键热插拔驱动发送的netlink消息
procd在捕获到按键事件netlink消息后,解析出的数据,通过匹配/etc/hotplug.json中的json数据,进行相应脚本的调用。在/etc/hotplug.json中,包含对button事件的处理信息,其中%BUTTON%为reset,因此会执行/etc/rc.button/reset脚本。
[
...
[ "if",
[ "and",
[ "has", "BUTTON" ],
[ "eq", "SUBSYSTEM", "button" ],
],
[ "button", "/etc/rc.button/%BUTTON%" ]
],
...
]
3.参考文档
MT7621A.PDF
MT7621_ProgrammingGuide_Preliminary_Platform.pdf