下面的內(nèi)容均在imx6平臺上舉例钦听,這一次分析希望將整個GPIO子系統(tǒng)的所有細節(jié)整理清晰责语。
第一篇從gpiolib入手僻他,后面的邊分析邊寫.
開始之前給自己提幾個問題
- 驅(qū)動開發(fā)中的GPIO API 究竟是怎么實現(xiàn)的兽狭?
- GPIO的中斷又是怎么實現(xiàn)的萧朝?
- GPIO號和IRQ的號碼怎么映射的岔留?
1.芯片定義
我們在驅(qū)動程序中會用到gpio_request(x),這里的x便是gpio的編號,而GPIO通常會分組检柬,在原理圖經(jīng)常會看見GPIO2_5類似的標識贸诚,通常我們先會翻閱一下datesheet來了解一下這塊芯片的定義是如何。
在imx6芯片中的將GPIO分成了若干組厕吉,每組為32個管腳酱固。
我們簡單的閱讀一下芯片手冊:
IMX6的gpio控制結(jié)構(gòu):
我們的芯片會引出一堆引腳,而在芯片的內(nèi)部會集成很多control头朱,(我們稱這些control為block)其中的引腳可以功能復(fù)用运悲,比如說A1 A2腳支持I2C功能也支持GPIO功能,我可以將I2C1_control的管腳連接過來项钮,或者將GPIO1_1的管腳連接上來班眯,這個就是IOMUX的作用:提供PIN不同的功能切換。
所以我們在使用芯片之前會先根據(jù)主板的配置先將各個管腳配置為正確的功能的Pin.
寄存器:
- Data register (GPIO_DR)
- GPIO direction register (GPIO_GDIR)
- Pad sample register (GPIO_PSR)
- Interrupt control registers (GPIO_ICR1, GPIO_ICR2)
- Interrupt mask register (GPIO_IMR)
- Interrupt status register (GPIO_ISR)
- GPIO edge select register (GPIO_EDEG_SEL )
簡單介紹一下
- DR 當作為輸出時控制管腳高低電平
- GDIR 控制管腳作為輸入還是輸出
- PSR 作為輸出時獲取管腳的高低電平
- ICR1 ICR2 是配置中斷的觸發(fā)方式 高/低電平觸發(fā) 上升/下降沿 觸發(fā)
- IMR 中斷屏蔽寄存器
- ISR 中斷狀態(tài)寄存器 哪個管腳觸發(fā)了中斷
- EDEG_SEL 邊緣觸發(fā)模式(上/下沿都觸發(fā))
GPIO有7組,每組32個烁巫,最后一組14個
2.設(shè)備樹
我們了解了一下內(nèi)部的結(jié)構(gòu)署隘,接著就要開始看代碼了,在看代碼之前看看設(shè)備樹亚隙,才能定位到代碼在哪里磁餐。
這里只列出一個gpio-controller
aliases {
ethernet0 = &fec;
can0 = &can1;
can1 = &can2;
gpio0 = &gpio1;
gpio1 = &gpio2;
.....
}
gpio1: gpio@0209c000 {
compatible = "fsl,imx6q-gpio", "fsl,imx35-gpio";
reg = <0x0209c000 0x4000>;
interrupts = <0 66 IRQ_TYPE_LEVEL_HIGH>,
<0 67 IRQ_TYPE_LEVEL_HIGH>;
gpio-controller;
#gpio-cells = <2>;
interrupt-controller;
#interrupt-cells = <2>;
};
我們通過compatible的值可以找到對應(yīng)的驅(qū)動代碼路徑 driver/gpio/gpio-mxc.c
GPIO1控制寄存器的地址是0x0209c000,長度0x4000阿弃,使用66和67號中斷诊霹。
這里疑惑的地方是為什只用了兩個中斷號,而不是32個渣淳?
我們知道一個GPIO控制是通過總線連接到CPU柴墩,中斷連接到中斷控制器(GIC),那么中斷線太多就以為著GIC的數(shù)量也需要增加钟些,如果GPIO控制器內(nèi)部進行判斷是哪個管腳觸發(fā)的那么就可以避免GIC的數(shù)量。
而我們的芯片內(nèi)部只有一個GIC.
IRQ | Source | Interrupt Description |
---|---|---|
32 | IOMUXC | General Purpose Register 1 from IOMUXC. Used to notify cores on exception condition whileboot. |
33 | DAP | Debug Access Port interrupt request |
98 | GPIO1 | Combined interrupt indication for GPIO1 signals 0 - 15. |
99 | GPIO1 | Combined interrupt indication for GPIO1 signals 16 - 31. |
這里中斷號的來源是芯片手冊扶歪,因為GIC的連接是這樣的胀茵,這里0-31是內(nèi)部使用结洼,32開始用作外設(shè)枷畏。
驅(qū)動中用編號0直接開始編號敷硅。
地址參考寄存器MAP
Absolute | address | Register name Widt (bits) | Access | Reset value |
---|---|---|---|---|
209_C000 | GPIO data register (GPIO1_DR) | 32 | R/W | 0000_0000h |
209_C004 | GPIO direction register (GPIO1_GDIR) | 32 | R/W | 0000_0000h |
209_C008 | GPIO pad status register (GPIO1_PSR) | 32 | R | 0000_0000h |
209_C00C | GPIO interrupt configuration register1 (GPIO1_ICR1) | 32 | R/W | 0000_0000h |
209_C010 | GPIO interrupt configuration register2 (GPIO1_ICR2) | 32 | R/W | 0000_0000h |
209_C014 | GPIO interrupt mask register (GPIO1_IMR) | 32 | R/W | 0000_0000h |
209_C018 | GPIO interrupt status register (GPIO1_ISR) | 32 | w1c | 0000_0000h |
209_C018 | GPIO interrupt status register (GPIO1_ISR) | 32 | w1c | 0000_0000h |
209_C01C | GPIO edge select register (GPIO1_EDGE_SEL) | 32 | R/W | 0000_0000h |
20A_0000 | GPIO data register (GPIO2_DR) | 32 | R/W | 0000_0000h |
3.代碼
從設(shè)備樹中我們知道了代碼的路徑位置,我們先看看gpio這個目錄的Makefile:
obj-$(CONFIG_GPIO_DEVRES) += devres.o
obj-$(CONFIG_GPIOLIB) += gpiolib.o
obj-$(CONFIG_GPIOLIB) += gpiolib-legacy.o
obj-$(CONFIG_OF_GPIO) += gpiolib-of.o
obj-$(CONFIG_GPIO_SYSFS) += gpiolib-sysfs.o
obj-$(CONFIG_GPIO_ACPI) += gpiolib-acpi.o
# Device drivers. Generally keep list sorted alphabetically
obj-$(CONFIG_GPIO_GENERIC) += gpio-generic.o
obj-$(CONFIG_GPIO_74X164) += gpio-74x164.o
.....(省略一些無關(guān)緊要的內(nèi)容)
obj-$(CONFIG_GPIO_MXC) += gpio-mxc.o
這里分三類:
- gpiolib的代碼
- gpio-generic代碼
- gpio特定平臺的代碼
代碼的結(jié)構(gòu):
GPIOLIB 調(diào)用 GPIO_GENRIC 調(diào)用 GPIO_MXC
接著我們開始分析代碼:
代碼主要講了初始化GPIO控制器和GPIO的中斷控制器鞠值,這里先分析gpio控制器和如何與gpiolib聯(lián)系
static const struct of_device_id mxc_gpio_dt_ids[] = {
{ .compatible = "fsl,imx1-gpio", .data = &mxc_gpio_devtype[IMX1_GPIO], },
{ .compatible = "fsl,imx21-gpio", .data = &mxc_gpio_devtype[IMX21_GPIO], },
{ .compatible = "fsl,imx31-gpio", .data = &mxc_gpio_devtype[IMX31_GPIO], },
{ .compatible = "fsl,imx35-gpio", .data = &mxc_gpio_devtype[IMX35_GPIO], },
{ /* sentinel */ }
};
enum mxc_gpio_hwtype {
IMX1_GPIO, /* runs on i.mx1 */
IMX21_GPIO, /* runs on i.mx21 and i.mx27 */
IMX31_GPIO, /* runs on i.mx31 */
IMX35_GPIO, /* runs on all other i.mx */
};
static struct platform_device_id mxc_gpio_devtype[] = {
{
.name = "imx1-gpio",
.driver_data = IMX1_GPIO,
}, {
.name = "imx21-gpio",
.driver_data = IMX21_GPIO,
}, {
.name = "imx31-gpio",
.driver_data = IMX31_GPIO,
}, {
.name = "imx35-gpio",
.driver_data = IMX35_GPIO,
}, {
/* sentinel */
}
};
這里是為了兼容性考慮媚创,這個分支感覺寫的很有水準以后可以借鑒一下渗钉。
接著來看看porbe 的流程
static int mxc_gpio_probe(struct platform_device *pdev)
{
struct device_node *np = pdev->dev.of_node;
struct mxc_gpio_port *port;
struct resource *iores;
int irq_base;
int err;
mxc_gpio_get_hw(pdev); //1
port = devm_kzalloc(&pdev->dev, sizeof(*port), GFP_KERNEL);
if (!port)
return -ENOMEM;
//從設(shè)備樹中獲取寄存器地址彤恶,IOREMAP就是將物理地址轉(zhuǎn)化稱虛擬地址
iores = platform_get_resource(pdev, IORESOURCE_MEM, 0);
port->base = devm_ioremap_resource(&pdev->dev, iores);
if (IS_ERR(port->base))
return PTR_ERR(port->base);
//從設(shè)備樹中獲取中斷號,上面也解釋了為啥是2個钞钙。
port->irq_high = platform_get_irq(pdev, 1);
port->irq = platform_get_irq(pdev, 0);
if (port->irq < 0)
return port->irq;
//關(guān)中斷,清理中斷觸發(fā)狀態(tài)声离。
//中斷屏蔽位的作用就是是否要關(guān)心這個中斷
//中斷狀態(tài)位的作用就是芒炼,當中斷發(fā)生了,我們需要查一下是誰發(fā)生了中斷术徊。
/* disable the interrupt and clear the status */
writel(0, port->base + GPIO_IMR);
writel(~0, port->base + GPIO_ISR);
if (mxc_gpio_hwtype == IMX21_GPIO) {
/*
* Setup one handler for all GPIO interrupts. Actually setting
* the handler is needed only once, but doing it for every port
* is more robust and easier.
*/
irq_set_chained_handler(port->irq, mx2_gpio_irq_handler);
} else {
/* setup one handler for each entry */
//2
//函數(shù)修改父中斷的流控函數(shù)本刽,當發(fā)生中斷,中斷控制器會去調(diào)用這個函數(shù)
irq_set_chained_handler(port->irq, mx3_gpio_irq_handler);
irq_set_handler_data(port->irq, port);
if (port->irq_high > 0) {
/* setup handler for GPIO 16 to 31 */
irq_set_chained_handler(port->irq_high,
mx3_gpio_irq_handler);
irq_set_handler_data(port->irq_high, port);
}
}
//3
err = bgpio_init(&port->bgc, &pdev->dev, 4,
port->base + GPIO_PSR,
port->base + GPIO_DR, NULL,
port->base + GPIO_GDIR, NULL, 0);
if (err)
goto out_bgio;
//設(shè)置gpio和軟件中斷號映射的關(guān)系
port->bgc.gc.to_irq = mxc_gpio_to_irq;
//設(shè)置gpio的編號基數(shù)
port->bgc.gc.base = (pdev->id < 0) ? of_alias_get_id(np, "gpio") * 32 :
pdev->id * 32;
//將GPIO控制器計入到gpiolib驅(qū)動的鏈表中赠涮,也就是之后我們可以gpio_direction去操作io了
err = gpiochip_add(&port->bgc.gc);
if (err)
goto out_bgpio_remove;
irq_base = irq_alloc_descs(-1, 0, 32, numa_node_id());
if (irq_base < 0) {
err = irq_base;
goto out_gpiochip_remove;
}
//向中斷控制器注冊并創(chuàng)建映射關(guān)系
port->domain = irq_domain_add_legacy(np, 32, irq_base, 0,
&irq_domain_simple_ops, NULL);
if (!port->domain) {
err = -ENODEV;
goto out_irqdesc_free;
}
//初始化gpio中斷控制器
/* gpio-mxc can be a generic irq chip */
mxc_gpio_init_gc(port, irq_base);
list_add_tail(&port->node, &mxc_gpio_ports);
return 0;
out_irqdesc_free:
irq_free_descs(irq_base, 32);
out_gpiochip_remove:
gpiochip_remove(&port->bgc.gc);
out_bgpio_remove:
bgpio_remove(&port->bgc);
out_bgio:
dev_info(&pdev->dev, "%s failed with errno %d\n", __func__, err);
return err;
}
先看 mxc_gpio_get_hw吧子寓,其實也就是初始化了兩個全局變量:
static enum mxc_gpio_hwtype mxc_gpio_hwtype;
static struct mxc_gpio_hwdata *mxc_gpio_hwdata;
static void mxc_gpio_get_hw(struct platform_device *pdev)
{
const struct of_device_id *of_id =
of_match_device(mxc_gpio_dt_ids, &pdev->dev);
enum mxc_gpio_hwtype hwtype;
if (of_id)
pdev->id_entry = of_id->data;
hwtype = pdev->id_entry->driver_data;
if (mxc_gpio_hwtype) {
/*
* The driver works with a reasonable presupposition,
* that is all gpio ports must be the same type when
* running on one soc.
*/
BUG_ON(mxc_gpio_hwtype != hwtype);
return;
}
if (hwtype == IMX35_GPIO)
mxc_gpio_hwdata = &imx35_gpio_hwdata;
else if (hwtype == IMX31_GPIO)
mxc_gpio_hwdata = &imx31_gpio_hwdata;
else
mxc_gpio_hwdata = &imx1_imx21_gpio_hwdata;
mxc_gpio_hwtype = hwtype;
}
看一眼這兩個變量是何方神圣:
enum mxc_gpio_hwtype {
IMX1_GPIO, /* runs on i.mx1 */
IMX21_GPIO, /* runs on i.mx21 and i.mx27 */
IMX31_GPIO, /* runs on i.mx31 */
IMX35_GPIO, /* runs on all other i.mx */
};
struct mxc_gpio_hwdata {
unsigned dr_reg;
unsigned gdir_reg;
unsigned psr_reg;
unsigned icr1_reg;
unsigned icr2_reg;
unsigned imr_reg;
unsigned isr_reg;
int edge_sel_reg;
unsigned low_level;
unsigned high_level;
unsigned rise_edge;
unsigned fall_edge;
};
static struct mxc_gpio_hwdata imx35_gpio_hwdata = {
.dr_reg = 0x00,//寫如內(nèi)容驅(qū)動IO
.gdir_reg = 0x04,//控制IO的方向 input output
.psr_reg = 0x08,//讀IO口電平
.icr1_reg = 0x0c,//中斷1 0-15
.icr2_reg = 0x10,//中斷2 16-31 控制電平觸發(fā)方式 00 low 01 hig 10 rise 11 fall
.imr_reg = 0x14,//中斷屏蔽位
.isr_reg = 0x18,//中斷狀態(tài)寄存器,中斷是否發(fā)生
.edge_sel_reg = 0x1c,//覆蓋ICR寄存器笋除,選擇EDGE方式(上升沿下降沿均觸發(fā)這個意思把斜友?)
.low_level = 0x00,
.high_level = 0x01,
.rise_edge = 0x02,
.fall_edge = 0x03,
};
原來不就是記錄一下控制寄存器是 imx35,和對應(yīng)的寄存器的偏移位置而已垃它。
我們先看port這個變量的結(jié)構(gòu)鲜屏,這里就是為了初始化這個結(jié)構(gòu)體。
struct mxc_gpio_port {
struct list_head node;
void __iomem *base; //gpio寄存器基地址
int irq; //gpio中斷號0-15用這個
int irq_high; //gpio中斷號16-31用這個
struct irq_domain *domain; //irq_domain放到中斷里面再介紹吧
struct bgpio_chip bgc;//這個結(jié)構(gòu)存放了寄存器的信息主要在gpio-genirc中用
u32 both_edges;//雙邊沿觸發(fā)的標識位国拇,主要是為了兼容不帶邊雙沿觸發(fā)寄存器的芯片
};
struct bgpio_chip {
struct gpio_chip gc;
unsigned long (*read_reg)(void __iomem *reg);
void (*write_reg)(void __iomem *reg, unsigned long data);
void __iomem *reg_dat;
void __iomem *reg_set;
void __iomem *reg_clr;
void __iomem *reg_dir;
/* Number of bits (GPIOs): <register width> * 8. */
int bits;
/*
* Some GPIO controllers work with the big-endian bits notation,
* e.g. in a 8-bits register, GPIO7 is the least significant bit.
*/
unsigned long (*pin2mask)(struct bgpio_chip *bgc, unsigned int pin);
/*
* Used to lock bgpio_chip->data. Also, this is needed to keep
* shadowed and real data registers writes together.
*/
spinlock_t lock;
/* Shadowed data register to clear/set bits safely. */
unsigned long data;
/* Shadowed direction registers to clear/set direction safely. */
unsigned long dir;
};
bgpio_chip 我們可以在代碼bgpio_init那里讀代碼分析它洛史,暫時先不看了。
好酱吝!我們接著看probe中注釋2代碼:
static void mx3_gpio_irq_handler(u32 irq, struct irq_desc *desc)
{
u32 irq_stat;
struct mxc_gpio_port *port = irq_get_handler_data(irq);
struct irq_chip *chip = irq_get_chip(irq);
chained_irq_enter(chip, desc);
//讀中斷狀態(tài)寄存器確定哪個管腳發(fā)生中斷也殖,并且如何中斷屏蔽位為0,忽略這個中斷
irq_stat = readl(port->base + GPIO_ISR) & readl(port->base + GPIO_IMR);
//調(diào)用中斷處理程序
mxc_gpio_irq_handler(port, irq_stat);
chained_irq_exit(chip, desc);
}
/* handle 32 interrupts in one status register */
static void mxc_gpio_irq_handler(struct mxc_gpio_port *port, u32 irq_stat)
{
while (irq_stat != 0) {
//fls或去最高有效位(從低位往左數(shù)最后的有效bit位)
//這里就是得到哪個管腳觸發(fā)的中斷务热,一一處理毕源,而且優(yōu)先級就是按照這個規(guī)則來了。
int irqoffset = fls(irq_stat) - 1;
//這里的做法是為了讓沒有雙邊沿觸發(fā)的芯片陕习,用輪流高低電平觸發(fā)的方式解決霎褐。
//imx6芯片忽略就好,也看了半天才看清楚原來是這樣该镣。
if (port->both_edges & (1 << irqoffset))
mxc_flip_edge(port, irqoffset);
//irq_find_mapping 這個再說吧冻璃,先記一下
generic_handle_irq(irq_find_mapping(port->domain, irqoffset));
irq_stat &= ~(1 << irqoffset);
}
}
static void mxc_flip_edge(struct mxc_gpio_port *port, u32 gpio)
{
void __iomem *reg = port->base;
u32 bit, val;
int edge;
/*0-15 ICR1 16-31 IRC2*/
reg += GPIO_ICR1 + ((gpio & 0x10) >> 2); /* lower or upper register */
bit = gpio & 0xf;
val = readl(reg);
edge = (val >> (bit << 1)) & 3;
val &= ~(0x3 << (bit << 1));
if (edge == GPIO_INT_HIGH_LEV) {
edge = GPIO_INT_LOW_LEV;
pr_debug("mxc: switch GPIO %d to low trigger\n", gpio);
} else if (edge == GPIO_INT_LOW_LEV) {
edge = GPIO_INT_HIGH_LEV;
pr_debug("mxc: switch GPIO %d to high trigger\n", gpio);
} else {
pr_err("mxc: invalid configuration for GPIO %d: %x\n",
gpio, edge);
return;
}
writel(val | (edge << (bit << 1)), reg);
}
//中斷處理程序看完了,我們接著看注釋3
err = bgpio_init(&port->bgc, &pdev->dev, 4,
port->base + GPIO_PSR,
port->base + GPIO_DR, NULL,
port->base + GPIO_GDIR, NULL, 0);
int bgpio_init(struct bgpio_chip *bgc, struct device *dev,
unsigned long sz, void __iomem *dat, void __iomem *set,
void __iomem *clr, void __iomem *dirout, void __iomem *dirin,
unsigned long flags)
{
int ret;
if (!is_power_of_2(sz))
return -EINVAL;
bgc->bits = sz * 8; //寄存器位數(shù) 4*8=32
spin_lock_init(&bgc->lock);
bgc->gc.dev = dev;
bgc->gc.label = dev_name(dev);
bgc->gc.base = -1;
bgc->gc.ngpio = bgc->bits; //gpio數(shù)量
bgc->gc.request = bgpio_request;
//1
ret = bgpio_setup_io(bgc, dat, set, clr);
//2
ret = bgpio_setup_accessors(dev, bgc, flags & BGPIOF_BIG_ENDIAN,
flags & BGPIOF_BIG_ENDIAN_BYTE_ORDER);
//3
ret = bgpio_setup_direction(bgc, dirout, dirin);
bgc->data = bgc->read_reg(bgc->reg_dat);
if (bgc->gc.set == bgpio_set_set &&
!(flags & BGPIOF_UNREADABLE_REG_SET))
bgc->data = bgc->read_reg(bgc->reg_set);
if (bgc->reg_dir && !(flags & BGPIOF_UNREADABLE_REG_DIR))
bgc->dir = bgc->read_reg(bgc->reg_dir);
return ret;
我們這里主要對gc這個參數(shù)進行了初始化损合,這個結(jié)構(gòu)gpio控制器省艳,最重要的參io的數(shù)量,對應(yīng)的操作函數(shù)。
struct gpio_chip {
const char *label;
struct device *dev;
struct module *owner;
struct list_head list;
int (*request)(struct gpio_chip *chip, unsigned offset);
void (*free)(struct gpio_chip *chip, unsigned offset);
int (*get_direction)(struct gpio_chip *chip, unsigned offset);
int (*direction_input)(struct gpio_chip *chip, unsigned offset);
int (*direction_output)(struct gpio_chip *chip, unsigned offset, int value);
int (*get)(struct gpio_chip *chip, unsigned offset);
void (*set)(struct gpio_chip *chip, unsigned offset, int value);
void (*set_multiple)(struct gpio_chip *chip,
unsigned long *mask,
unsigned long *bits);
int (*set_debounce)(struct gpio_chip *chip,
unsigned offset,
unsigned debounce);
int (*get_direction)(struct gpio_chip *chip,
unsigned offset);
int (*direction_input)(struct gpio_chip *chip,
unsigned offset);
int (*direction_output)(struct gpio_chip *chip,
unsigned offset, int value);
int (*get)(struct gpio_chip *chip,
unsigned offset);
void (*set)(struct gpio_chip *chip,
unsigned offset, int value);
void (*set_multiple)(struct gpio_chip *chip,
unsigned long *mask,
unsigned long *bits);
int (*set_debounce)(struct gpio_chip *chip,
unsigned offset,
unsigned debounce);
int (*to_irq)(struct gpio_chip *chip,
unsigned offset);
void (*dbg_show)(struct seq_file *s,
struct gpio_chip *chip);
int base;
u16 ngpio;
struct gpio_desc *desc;
const char *const *names;
bool can_sleep;
bool irq_not_threaded;
bool exported;
}
接著我們就看看三個setup函數(shù)如何去初始化這些接口.
static int bgpio_setup_io(struct bgpio_chip *bgc,
void __iomem *dat,
void __iomem *set,
void __iomem *clr)
{
bgc->reg_dat = dat;
if (!bgc->reg_dat)
return -EINVAL;
if (set && clr) {
bgc->reg_set = set;
bgc->reg_clr = clr;
bgc->gc.set = bgpio_set_with_clear;
bgc->gc.set_multiple = bgpio_set_multiple_with_clear;
} else if (set && !clr) {
bgc->reg_set = set;
bgc->gc.set = bgpio_set_set;
bgc->gc.set_multiple = bgpio_set_multiple_set;
} else {
bgc->gc.set = bgpio_set;
bgc->gc.set_multiple = bgpio_set_multiple;
}
bgc->gc.get = bgpio_get;
return 0;
}
配置了get和set函數(shù)嫁审,這里我們沒有clr寄存器
static int bgpio_setup_accessors(struct device *dev,
struct bgpio_chip *bgc,
bool bit_be,
bool byte_be)
{
switch (bgc->bits) {
case 8:
bgc->read_reg = bgpio_read8;
bgc->write_reg = bgpio_write8;
break;
case 16:
if (byte_be) {
bgc->read_reg = bgpio_read16be;
bgc->write_reg = bgpio_write16be;
} else {
bgc->read_reg = bgpio_read16;
bgc->write_reg = bgpio_write16;
}
break;
case 32:
if (byte_be) {
bgc->read_reg = bgpio_read32be;
bgc->write_reg = bgpio_write32be;
} else {
bgc->read_reg = bgpio_read32;
bgc->write_reg = bgpio_write32;
}
break;
#if BITS_PER_LONG >= 64
case 64:
if (byte_be) {
dev_err(dev,
"64 bit big endian byte order unsupported\n");
return -EINVAL;
} else {
bgc->read_reg = bgpio_read64;
bgc->write_reg = bgpio_write64;
}
break;
#endif /* BITS_PER_LONG >= 64 */
default:
dev_err(dev, "unsupported data width %u bits\n", bgc->bits);
return -EINVAL;
}
bgc->pin2mask = bit_be ? bgpio_pin2mask_be : bgpio_pin2mask;
return 0;
}
這里配置寄存器讀寫大小端和位數(shù)的問題跋炕,像bgpio_get等函數(shù)都是會調(diào)用對應(yīng)寄存器讀,這里很簡單的流程就不在追下去了律适,接著是最后一個setup
static int bgpio_setup_direction(struct bgpio_chip *bgc,
void __iomem *dirout,
void __iomem *dirin)
{
if (dirout && dirin) {
return -EINVAL;
} else if (dirout) {
bgc->reg_dir = dirout;
bgc->gc.direction_output = bgpio_dir_out;
bgc->gc.direction_input = bgpio_dir_in;
} else if (dirin) {
bgc->reg_dir = dirin;
bgc->gc.direction_output = bgpio_dir_out_inv;
bgc->gc.direction_input = bgpio_dir_in_inv;
} else {
bgc->gc.direction_output = bgpio_simple_dir_out;
bgc->gc.direction_input = bgpio_simple_dir_in;
}
return 0;
}
這里我們只有一個dirout的寄存器辐烂,作為輸出只用將哪一個寄存器是能即可遏插。
這里init就分析完了,如何或去電平高低,設(shè)置io口方向纠修,和輸出電平高低的功能都OK了胳嘲。
最后probe 中一共最關(guān)鍵的函數(shù)gpiochip_add 我們再看看如何將GPIO控制器和gpiolib關(guān)聯(lián)。
關(guān)于中斷我們放到下一張講解扣草。
接著我們看一下gpiochip_add
int gpiochip_add(struct gpio_chip *chip)
{
unsigned long flags;
int status = 0;
unsigned id;
int base = chip->base;
struct gpio_desc *descs;
descs = kcalloc(chip->ngpio, sizeof(descs[0]), GFP_KERNEL);
if (!descs)
return -ENOMEM;
spin_lock_irqsave(&gpio_lock, flags);
if (base < 0) {
base = gpiochip_find_base(chip->ngpio);
if (base < 0) {
status = base;
spin_unlock_irqrestore(&gpio_lock, flags);
goto err_free_descs;
}
chip->base = base;
}
status = gpiochip_add_to_list(chip);
if (status) {
spin_unlock_irqrestore(&gpio_lock, flags);
goto err_free_descs;
}
for (id = 0; id < chip->ngpio; id++) {
struct gpio_desc *desc = &descs[id];
desc->chip = chip;
/* REVISIT: most hardware initializes GPIOs as inputs (often
* with pullups enabled) so power usage is minimized. Linux
* code should set the gpio direction first thing; but until
* it does, and in case chip->get_direction is not set, we may
* expose the wrong direction in sysfs.
*/
desc->flags = !chip->direction_input ? (1 << FLAG_IS_OUT) : 0;
}
chip->desc = descs;
spin_unlock_irqrestore(&gpio_lock, flags);
#ifdef CONFIG_PINCTRL
INIT_LIST_HEAD(&chip->pin_ranges);
#endif
of_gpiochip_add(chip);
acpi_gpiochip_add(chip);
status = gpiochip_export(chip);
if (status)
goto err_remove_chip;
pr_debug("%s: registered GPIOs %d to %d on device: %s\n", __func__,
chip->base, chip->base + chip->ngpio - 1,
chip->label ? : "generic");
return 0;
這里最關(guān)鍵的就是將控制器加入到鏈表中了牛,然后初始化了各個desc也就是管腳的信息
gpiochip_add_to_list(chip);
struct gpio_desc *gpio_to_desc(unsigned gpio)
{
struct gpio_chip *chip;
unsigned long flags;
spin_lock_irqsave(&gpio_lock, flags);
list_for_each_entry(chip, &gpio_chips, list) {
if (chip->base <= gpio && chip->base + chip->ngpio > gpio) {
spin_unlock_irqrestore(&gpio_lock, flags);
return &chip->desc[gpio - chip->base];
}
}
spin_unlock_irqrestore(&gpio_lock, flags);
if (!gpio_is_valid(gpio))
WARN(1, "invalid GPIO %d\n", gpio);
return NULL;
}
static bool _gpiod_get_raw_value(const struct gpio_desc *desc)
{
struct gpio_chip *chip;
bool value;
int offset;
chip = desc->chip;
offset = gpio_chip_hwgpio(desc);
value = chip->get ? chip->get(chip, offset) : false;
trace_gpio_value(desc_to_gpio(desc), 1, value);
return value;
}
int gpiod_get_value_cansleep(const struct gpio_desc *desc)
{
int value;
might_sleep_if(extra_checks);
if (!desc)
return 0;
value = _gpiod_get_raw_value(desc);
if (test_bit(FLAG_ACTIVE_LOW, &desc->flags))
value = !value;
return value;
}
這里發(fā)現(xiàn)gpio庫的變化非常大,類似gpio_direction_input的實現(xiàn)都消失了辰妙,并不了解為什么?
如果我們在驅(qū)動中需要獲得某個關(guān)鍵的電平:
int val =gpiod_get_value_cansleep(gpio_to_desc(37));
首先會通過鏈表查找到控制器鹰祸,然后調(diào)用控制器的get函數(shù)或去寄存器的值。
后來發(fā)現(xiàn)編譯時會自動生成gpiolib庫的頭文件:
include/asm-generic/gpio.h
static inline int gpio_direction_input(unsigned gpio)
{
return gpiod_direction_input(gpio_to_desc(gpio));
}
static inline int gpio_direction_output(unsigned gpio, int value)
{
return gpiod_direction_output_raw(gpio_to_desc(gpio), value);
}
4.小結(jié)
開始提了3個問題
1. 驅(qū)動開發(fā)中的GPIO API 究竟是怎么實現(xiàn)的密浑?
這里我們花了大力氣閱讀了芯片手冊和源碼.重點關(guān)注了三個問題 gpio-mxc.c gpio-genric.c gpiolib.c
gpio-mxc 重點在維護的結(jié)構(gòu)體mxc_gpio_port福荸。
最重要的兩個結(jié)構(gòu):
- struct bgpio_chip
- struct gpio_chip gc;
struct mxc_gpio_port {
struct list_head node;
void __iomem *base;
int irq;
int irq_high;
struct irq_domain *domain;
struct bgpio_chip bgc;
u32 both_edges;
};
struct bgpio_chip {
struct gpio_chip gc;
unsigned long (*read_reg)(void __iomem *reg);
void (*write_reg)(void __iomem *reg, unsigned long data);
void __iomem *reg_dat;
void __iomem *reg_set;
void __iomem *reg_clr;
void __iomem *reg_dir;
int bits;
unsigned long (*pin2mask)(struct bgpio_chip *bgc, unsigned int pin);
spinlock_t lock;
unsigned long data;
unsigned long dir;
};
通過bgpio_init() 完成了對struct bgpio_chip的初始化,尤其是和gpiolib交互的重要數(shù)據(jù)結(jié)構(gòu)struct gpio_chip,至此完成對寄存器操作的抽象.
最后使用gpiolib_add()將我們的GPIO控制器加入到了gpiolib.c的控制器鏈表中肴掷,完成了gpiolib庫的封裝
2. GPIO的中斷又是怎么實現(xiàn)的敬锐?
我們分析一部分代碼,中斷函數(shù)的注冊和當中斷發(fā)生后如何區(qū)分是哪個IO口發(fā)生的中斷呆瞻,中斷控制器的實現(xiàn)我們還沒分析到台夺,相信后面分析會將這個流程連接起來。
3. GPIO號和IRQ的號碼怎么映射的痴脾?
中斷號和IO后映射問題,關(guān)鍵在函數(shù)irq_find_mapping中颤介,我們暫時還沒有跳轉(zhuǎn)過代碼去研究這一部分。