operación GPIO del kernel de Linux

Aprendí el kernel de Linux hace unos 10 años, pero el funcionamiento de GPIO en ese momento era simple y grosero. Ahora el pin-ctrl gpio maneja un montón de dts. No sé por dónde empezar. Después de varios días de lanzar, tomé una nota para no olvidarla de nuevo. Otra vez.

Un registro operativo

Obtenga la dirección de registro relacionada con GPIO de acuerdo con el manual del chip (aquí s5p6818 es un ejemplo)

//这是芯片手册里给出的真实物理地址
#define PHYS_BASE_GPIOA_OUTDAT (0xC001A000)  //
#define PHYS_BASE_GPIOA_OUTEN  (0xC001A004)
#define PHYS_BASE_GPIOA_PAD	   (0xC001A018)
#define PHYS_BASE_GPIOA_FUNC0  (0xC001A020)
#define PHYS_BASE_GPIOA_FUNC1  (0xC001A024)

La dirección física no se puede manipular directamente y debe ser procesada por ioremap:
Parámetro 1: Dirección física real
Parámetro 2: La longitud del espacio de direcciones, en bytes, el registro del brazo es generalmente 32, entonces 4

void __iomem *gpioa_outdat_addr;
void __iomem *gpioa_outen_addr;
void __iomem *gpioa_pad_addr;
void __iomem *gpioa_func0_addr;
void __iomem *gpioa_func1_addr;
gpioa_outdat_addr = ioremap(PHYS_BASE_GPIOA_OUTDAT,4);
gpioa_outen_addr = ioremap(PHYS_BASE_GPIOA_OUTEN,4);
gpioa_pad_addr = ioremap(PHYS_BASE_GPIOA_PAD,4);
gpioa_func0_addr =ioremap(PHYS_BASE_GPIOA_FUNC0,4);
gpioa_func1_addr =ioremap(PHYS_BASE_GPIOA_FUNC1,4);

El valor __iomem * vacío obtenido se puede leer con readl (la dirección se puede leer) y escribir con writeel (se puede escribir la dirección)

u32 data_en = readl(gpioa_outen_addr);
data_en = 0x000FFFE2;
writel(data_en,gpioa_outen_addr);

Cómo utilizar estos registros,
consulte el manual del chip y recuerde liberarlos después de su uso:

iounmap(gpioa_outdat_addr);
iounmap(gpioa_outen_addr);
iounmap(gpioa_pad_addr);
iounmap(gpioa_func0_addr);
iounmap(gpioa_func1_addr);

La velocidad de los registros operativos de lectura y escritura es muy lenta y puede que no cumpla con los requisitos de velocidad durante la sincronización de la simulación GPIO, pero de hecho la velocidad de lectura y escritura del registro es muy rápida, pero estas dos funciones han agregado algunas medidas de seguridad. En busca de velocidad, puede usar:
__raw_writel
__raw_readl
Estas son dos funciones en línea, que solo buscan y asignan direcciones:

static inline void __raw_writel(u32 value, volatile void __iomem *addr)
{
	*(volatile u32 __force *)addr = value;
}
static inline u32 __raw_readl(const volatile void __iomem *addr)
{
	return *(const volatile u32 __force *)addr;
}

Para que pueda leer y escribir de forma más directa:

u32 data_en =*(const volatile u32 __force *)gpioa_outen_addr;
data_en = 0x000FFFE2;
*(volatile u32 __force *)gpioa_outen_addr = value;

La desventaja de esto es que, en algunos casos, puede ser optimizado por el compilador, lo que resulta en una asignación incorrecta

Dos usan el controlador gpio de linux

La ventaja de operar directamente el registro es que es rápido y puede operar múltiples pines gpio al mismo tiempo. La desventaja es que hay demasiados registros y la configuración del valor es muy engorrosa. En el ejemplo anterior, necesita configurar 5 registros para operar un GPIO, y necesita encontrar el bit correspondiente. Si no tiene cuidado, se equivocará y es posible que se dedique un día a operaciones de bits. Se refleja el papel del controlador gpio.
API de operación GPIO:

//申请GPIO,参数1:gpio 编号,参数2:gpio 名字 (自定义)
int gpio_request(unsigned gpio, const char *label)

//释放GPIO ,参数1:gpio编号
void gpio_free(unsigned gpio)

//设置GPIO为输出模式,同时将输出设为value 
//参数1:gpio编号,参数2:输出的值 (0或1)
static inline int gpio_direction_output(unsigned gpio, int value)

//设置GPIO为输入模式
static inline int gpio_direction_input(unsigned gpio)

//判断gpio是否有效,就是判断gpio的编号是否在范围内
//比如soc的所有gpio编号是0~128,如果输入编号在此范围则认为有效
//返回值:true 为有效,false为无效。
static inline int gpio_is_valid(unsigned int gpio)

//设置GPIO的状态,参数1:GPIO编号,参数2:设置的值(0或1)
static inline void gpio_set_value(unsigned int gpio, int value)

//获取GPIO的状态,参数1:GPIO编号
//返回值;GPIO 状态,0:GPIO低点位,1:GPIO高电位
static inline int gpio_get_value(unsigned int gpio)

El número GPIO está relacionado con el BSP de soc y se puede obtener leyendo el código correspondiente.
s5p6818 tiene 5 puertos GPIO (A, B, C, D, E) y cada puerto tiene 32 pines, un total de 160, por lo que están numerados secuencialmente de 0 a 159, (A0 = 0, A1 = 1 ---- --E31 = 159)
Esta regla de codificación debe estar unificada. Los nombres de los puertos GPIO son diferentes de un chip a otro. Algunos se llaman GPIO0 GPIO1 ------
El segundo parámetro de gpio_request es darle al gpio aplicado un nombre individual. El nombre se define sin ningún requisito, lo mejor es agregar algunos prefijos o sufijos de especialización para evitar repeticiones.

//这段代码将GPIOE_30 设置为输出模式并拉低,20us后再拉高
#define FPGA_POWER_GPIONUM     158     //GPIOE_30
//request PWOER GPIO
if(gpio_request(FPGA_POWER_GPIONUM,"FPGA_POWER")){
	gpio_direction_output(FPGA_POWER_GPIONUM,0);
	usleep(20);
	//因为已经设置为output模式,所以可以直接调用gpio_set_value
	//即使gpio为input模式也可以设置,只是不能作用到引脚
	gpio_set_value(FPGA_POWER_GPIONUM,1);
}

gpio_request Esto es principalmente para juzgar si está ocupado por otros módulos, si no, regístrate y dile al sistema GPIO que estoy ocupado, si esta función falla, básicamente otros módulos también usan este GPIO. Este problema es muy común al migrar el sistema, debido a que el cableado de su propia placa es muy diferente al de la demostración, GPIO a menudo está ocupado al migrar con la demostración, pero el código de demostración generalmente se establece en el pin dts gpio, así que solo verifique el DTS ¿Dónde está configurado este pin?

Tres interrupciones GPIO

También está implementado con la API del controlador GPIO. Es demasiado engorroso manipular directamente los registros. Si no lo haces, ¡se te acaba el pelo!
Interrupción sola, porque involucra la API de interrupción, (esto está más allá del alcance de * _ *)

//根据gpio编号得到irq编号
static inline int gpio_to_irq(unsigned int gpio)

//使能中断,参数:irq编号
void enable_irq(unsigned int irq)

//禁用中断,参数:irq编号
void disable_irq(unsigned int irq)

// 申请中断
//参数1:中断号,参数2:中断处理函数,参数3:触发方式, 参数4:中断名(自定义)
//参数5:传递给中断处理函数的参数
//返回值:不为0表示失败。
static inline int __must_check
request_irq(unsigned int irq, irq_handler_t handler, unsigned long flags,
	    const char *name, void *dev)
	    
//释放中断
//参数1 :中断号,参数2 :中断处理函数
void free_irq(unsigned int irq, void *dev_id)

El número de interrupción obtenido por gpio_to_irq se ejecuta a través de toda la operación de interrupción. El parámetro de entrada es el número GPIO mencionado anteriormente, siempre que el GPIO se pueda usar como un pin de interrupción. Esto está relacionado con soc. Consulte el manual del chip para obtener más detalles.
El segundo parámetro de request_irq es la función de manejo de interrupciones, que se define de la siguiente manera:

irqreturn_t bc_irq_handler(int irq, void *dev_id)

El primer parámetro es el número de interrupción. Diferentes funciones de procesamiento de interrupciones pueden ser iguales. La interrupción específica se distingue por el número de interrupción. El segundo parámetro y el último parámetro de request_irq.

El siguiente código establece el pin gpioa-20 en modo de interrupción, el flanco descendente se activa, la función de procesamiento es bc_irq_handler y el nombre de la función y el número de interrupción se imprimen en la función de procesamiento de interrupciones.

irqreturn_t bc_irq_handler(int irq, void *dev_id){
	printk("%s %d\n",__func__,irq);
}

#define FPGA_B_INT               20       // gpioa-20 intrupt
int b_irq = gpio_to_irq(FPGA_B_INT);
ret = request_irq(g_privdat->b_irq,bc_irq_handler,IRQF_TRIGGER_FALLING,"B int",NULL);
if(ret){
	printk("request_irq error: %d\n",b_irq);
	return;
}
enable_irq(b_irq);

Cuatro gpio y dts

Esta sección también está más allá del esquema, involucrará DTS (árbol de dispositivos), pero principalmente enumera algunos usos de API y no explicará en profundidad. Si todavía tiene suficiente cabello, escriba notas EDE.

Hable brevemente sobre la correspondencia entre la nota dts y el código.
Cada nota dts tiene un campo compatible, como se describe en la siguiente nota dts:

//
bt_bcm {
	compatible = "bluetooth-platdata";
	uart_rts_gpios = <&gpio_c 6 GPIO_ACTIVE_LOW>; 
	uart_cts_gpios = <&gpio_c 5 GPIO_ACTIVE_LOW>; 
	pinctrl-names = "default", "rts_gpio";
	pinctrl-0 = <&serial1_flow>;
	pinctrl-1 = <&bt_flow_gpio>;

	reset-gpios    = <&gpio_b 26 GPIO_ACTIVE_HIGH>; 
	wake-gpios     = <&gpio_b 27 GPIO_ACTIVE_HIGH>;
	status = "okay";
};

Compatible se define por sí mismo, corresponde al controlador uno a uno

static struct of_device_id bt_platdata_of_match[] = {
    { .compatible = "bluetooth-platdata" },
    { }
};

La estructura anterior se define en el controlador y se registra en el sistema, luego el sistema hará coincidir la nota dts y el controlador, y llamará a la función de sonda en el controlador:

static int rfkill_rk_probe(struct platform_device *pdev){
	......
}

El parámetro de entrada de la estructura de la función de la sonda platform_device tiene pdev-> dev-> of_node y dts note content
. Hay dos formas de obtener gpio de la nota:

//参数1 :对应的probe 函数输入参数下的pdev->dev->of_node 
//参数2 :note 中GPIO 列表的字段名
//参数3 :要取的gpio在列表中的下标
//参数4:gpio默认电位值
//返回值:GPIO编号
int of_get_named_gpio_flags(struct device_node *np, const char *list_name,
			    int index, enum of_gpio_flags *flags)

//参数1:probe函数输入参数中的设备结构体pdev->dev
//参数2:note中的字段名
//参数3:要将gpio设置为何种状态
//返回值:gpio_desc 结构体,该结构体是对GPIO编号的包装。
struct gpio_desc *__must_check devm_gpiod_get_optional(struct device *dev,
						       const char *con_id,
						       enum gpiod_flags flags)

Ejemplo: el
primer tipo:

enum of_gpio_flags flags;
gpio = of_get_named_gpio_flags(node, "BT,reset_gpio", 0, &flags);

No es necesario explicar la nota, "BT, reset_gpio" y los campos de la nota se pueden ver en el ejemplo de nota anterior.
0 significa el subíndice de la lista.
Para explicar aquí, el campo para configurar GPIO es en realidad una matriz, aquí solo se configura un GPIO
BT en la matriz "BT, reset_gpio" , reset_gpio = <& gpio_b 26 GPIO_ACTIVE_HIGH>;
Puede llenar varios gpio:

BT,reset_gpio    = <&gpio_b 26 GPIO_ACTIVE_HIGH  //index =0
					&gpio_c 10 GPIO_ACTIVE_HIGH             //index =1
					&gpio_a 11 GPIO_ACTIVE_HIGH>;         //index=2

En este caso, el número de gpio correspondiente se puede obtener por índice.
flags es un parámetro de salida, correspondiente a GPIO_ACTIVE_HIGH

El segundo tipo;

struct gpio_desc *bt_reset;
bt_reset = devm_gpiod_get_optional(&pdev->dev, "reset",
			GPIOD_OUT_LOW);

Este método es bastante diferente al primero. Los dos métodos obtienen el mismo gpio
1, pero el nombre de sustitución del segundo parámetro no es el mismo, esto se debe a que devm_gpiod_get_optional completará el nombre y buscará reset-gpios;
2 no sustituye el índice del parámetro, el valor predeterminado es index 0, por lo que no puede manejar múltiples configuraciones de gpio.3
El tercer parámetro es input, y establecerá gpio en este estado en lugar de obtener el estado establecido en DTS, es decir, ignora el estado del pin en DTS Configuración.
4 El valor de retorno es la estructura gpio_desc en lugar del número GPIO. Esta estructura está vinculada al GPIO correspondiente, y el número GPIO obtenido por el primer método apunta al mismo pin GPIO que el gpio_desc obtenido aquí

La estructura gpio_desc se establece con otro conjunto de api:

int gpiod_request(struct gpio_desc *desc, const char *label)
void gpiod_set_value(struct gpio_desc *desc, int value)
int gpiod_get_value(const struct gpio_desc *desc)
int gpiod_direction_input(struct gpio_desc *desc)
int gpiod_direction_output(struct gpio_desc *desc, int value)

Es diferente del método en la segunda sección en que el nombre de la función tiene un extra'd ', y el número GPIO de entrada se cambia a la estructura gpio_desc

Hay muchas API sobre GPIO, aquí hay algunas de uso común.

Supongo que te gusta

Origin blog.csdn.net/crazycat_dzw/article/details/109286002
Recomendado
Clasificación