【Qualcomm】Snapdragon X5 LTE Modem GPIO的深层理解&控制<1>

Linux的GPIO的控制有多种的方式,pinctrl或者gpiolib等方式,以上的方式有很多文档,可以自行搜索学习,此篇重点说明寄存器的相关控制方式,以EC20为例进行说明(该说明需要结合datasheet,针对datasheet中的相关说明进行提取在下面进行说明)

一、寄存器地址

GPIO配置寄存器地址的计算方式:gpio_n:0x01000000+0x1000*n

例如,GPIO25配置寄存器的计算方式为,0x01000000+0x1000*25 = 0x1019000

GPIO输入输出寄存器地址的计算方式:0x01000000+0x1000*n+0x04

例如,GPIO25输入输出寄存器的计算方式为,0x1019000+0x04 = 0x1019004

二、devmem工具的使用

Linux下可使用devmem工具来读取具体的内存地址,以读取GPIO25的内存地址为例进行说明

三、寄存器地址解析

十六进制数二进制数寄存器名
0x00000283001 010 0000 11配置寄存器
0x000000030011输入输出寄存器

                   

GPIO配置寄存器
Bit位配置功能具体含义
Bit1-0上下拉

0表示浮空

1表示下拉

2表示保持,

3表示上拉

Bit5-2功能位

0表示GPIO功能

非0表示复用功能

Bit8-6驱动能力

<0>:2mA

<1>:4mA

<2>:6mA

<3>:8mA

<4>:10mA

<5>:12mA

<6>:14mA

<7>:16mA

Bit9方向位1表示输出,0表述输入
输入输出寄存器
Bit位具体含义
Bit1输出方向对应的值,1表示高电平,0表示低电平
Bit0输入方面对应的值,1表示高电平,0表示低电平

根据不同寄存器的二进制数值可见GPIO为输出模式6mA高电平

通过查看/sys/kernel/debug/pinctrl/1000000.pinctrl/pinmux-pins下GPIO_25的复用信息,为GPIO功能

查看sys/kernel/debug/gpio的相关信息,为输出模式输出能力6mA 上拉

四、驱动中的操作

根据上述的寄存器地址结合Linux的特性,使用设备树编写一个简单的驱动程序,使用设备树初始化配置GPIO

驱动代码如下:

#include <linux/clk.h>
#include <linux/err.h>
#include <linux/init.h>
#include <linux/interrupt.h>
#include <linux/io.h>
#include <linux/irq.h>
#include <linux/irqdomain.h>
#include <linux/irqchip/chained_irq.h>
#include <linux/platform_device.h>
#include <linux/pm_runtime.h>
#include <linux/slab.h>
#include <linux/gpio/driver.h>
#include <linux/gpio.h>
#include <linux/of.h>
#include <linux/of_device.h>
#include <linux/bug.h>
#include <linux/module.h>
#include <linux/delay.h>
#include <asm/io.h>

static volatile uint32_t *gpio_cfg_vreg;
static volatile uint32_t *gpio_dir_lv_reg;


/*
 * DTS Config ways
 */
#define REGISTER_ADDR                     (0)
#define GPIO_NUM                          (1)

/*
 * Mdm9x07 gpio register address translation 
 */
#define DEV_MEM_VALUE(n)                  (0x01000000 + 0x1000 * (n))

/*
 * Mdm9x07 platform gpio pull registers
 */
#define GPIO_NO_PULL                      0x0
#define GPIO_PULL_DOWN                    0x1
#define GPIO_KEEPER                       0x2
#define GPIO_PULL_UP                      0x3

/*
 * Mdm9x07 platform gpio strength registers
 */
#define GPIO_DRV_2_MA                     0x0
#define GPIO_DRV_4_MA                     0x40
#define GPIO_DRV_6_MA                     0x80
#define GPIO_DRV_8_MA                     0xc0
#define GPIO_DRV_10_MA                    0x100
#define GPIO_DRV_12_MA                    0x140
#define GPIO_DRV_14_MA                    0x180
#define GPIO_DRV_16_MA                    0x1c0

enum mdm_gpio_drv_strength {
    DRV_2_MA = 0,	    /* drive strength to 2mA */
    DRV_4_MA,           /* drive strength to 4mA */
    DRV_6_MA,           /* drive strength to 6mA */
    DRV_8_MA,           /* drive strength to 8mA */
    DRV_10_MA,          /* drive strength to 10mA */
    DRV_12_MA,          /* drive strength to 12mA */
    DRV_14_MA,          /* drive strength to 14mA */
    DRV_16_MA,          /* drive strength to 16mA */
};

enum mdm_gpio_pull {
    NO_PULL = 0,        /* The pad configured NO_PULL */
    PULL_DOWN,          /* The pad configured PULL_DOWN */
    KEEPER,             /* The pad configured KEEPER */
    PULL_UP,            /* The pad configured PULL_UP */
};

enum mdm_gpio_direction {
    GPIO_IN = 0,        /* Allows you to read the Input value of the GPIO */
    GPIO_OUT,           /* Controls the value of the GPIO Output */
};

enum mdm_gpio_dir_lv {
    LOW_LEVEL = 0,      /* High level in terms of input and output */
    HIGH_LEVEL,         /* Low level in terms of input and output */
};

/**
* struct mdm_gpio_hwdata: gpio information
* @addr: pipe description.
* @drv_strength: Controls the GPIO pad drive strength. 
* @gpio_pull: The pad can be configured to employ an internal weak pull up, pulldown, or keeper function.
* @gpio_direction: Controls the Out and In on the GPIO
*/
struct mdm_gpio_hwdata {
    uint32_t gpio_num;
    uint32_t addr;
    enum mdm_gpio_drv_strength drv_strength;
    enum mdm_gpio_pull gpio_pull;
    enum mdm_gpio_direction gpio_direction;
    enum mdm_gpio_dir_lv gpio_level;
};

static struct mdm_gpio_hwdata *mdm_gpio_dt_to_hwdata(struct platform_device *pdev)
{
    int ret;
    struct mdm_gpio_hwdata *gpio_hwdata;
    struct device_node *node = pdev->dev.of_node;

    gpio_hwdata = devm_kzalloc(&pdev->dev, sizeof(*gpio_hwdata), GFP_KERNEL);
    if (!gpio_hwdata) {
        return NULL;
    }

    #if GPIO_NUM
    /* read gpio number */
    ret = of_property_read_u32(node, "qcom,gpio-num",
                &gpio_hwdata->gpio_num);
    if (ret) {
        dev_err(&pdev->dev, "Unable to get gpio number.\n");
        return NULL;
    }
    gpio_hwdata->addr = DEV_MEM_VALUE(gpio_hwdata->gpio_num);
    dev_info(&pdev->dev, "gpio num = %d, gpio reg addr = %x\n", gpio_hwdata->gpio_num, gpio_hwdata->addr);
    #endif

    #if REGISTER_ADDR
    /* read addr */
    ret = of_property_read_u32(node, "qcom,gpio-addr",
                &gpio_hwdata->addr);
    if (ret) {
        dev_err(&pdev->dev, "Unable to retrieve register base.\n");
        return NULL;
    }
    #endif

    /* read drv_strength */
    ret = of_property_read_u32(node, "qcom,drv_strength", &gpio_hwdata->drv_strength);
    if (ret)
        gpio_hwdata->drv_strength = DRV_2_MA;
    dev_info(&pdev->dev, "drv_strength = %d\n", gpio_hwdata->drv_strength);

    /* read gpio_pull */
    ret = of_property_read_u32(node, "qcom,gpio_pull", &gpio_hwdata->gpio_pull);
    if (ret)
        gpio_hwdata->gpio_pull = PULL_DOWN;
    dev_info(&pdev->dev, "gpio_pull = %d\n", gpio_hwdata->gpio_pull);

    /* read gpio direction */
    ret = of_property_read_u32(node, "qcom,gpio_dir", &gpio_hwdata->gpio_direction);
    if (ret)
        gpio_hwdata->gpio_direction = GPIO_IN;
    dev_info(&pdev->dev, "gpio_dir = %d\n", gpio_hwdata->gpio_direction);

    /* read gpio level */
    ret = of_property_read_u32(node, "qcom,gpio_level", &gpio_hwdata->gpio_level);
    if (ret)
        gpio_hwdata->gpio_level = LOW_LEVEL;
    dev_info(&pdev->dev, "gpio_level = %d\n", gpio_hwdata->gpio_level);

    return gpio_hwdata;
}

static void mdm_gpio_set_cfg(struct mdm_gpio_hwdata *gpio_hwdata)
{
    uint32_t reg = 0;
    switch (gpio_hwdata->drv_strength) {
        case DRV_2_MA:
            reg = reg | GPIO_DRV_2_MA;
            break;
        case DRV_4_MA:
            reg = reg | GPIO_DRV_4_MA;
            break;
        case DRV_6_MA:
            reg = reg | GPIO_DRV_6_MA;
            break;
        case DRV_8_MA:
            reg = reg | GPIO_DRV_8_MA;
            break;
        case DRV_10_MA:
            reg = reg | GPIO_DRV_10_MA;
            break;
        case DRV_12_MA:
            reg = reg | GPIO_DRV_12_MA;
            break;
        case DRV_14_MA:
            reg = reg | GPIO_DRV_14_MA;
            break;
        case DRV_16_MA:
            reg = reg | GPIO_DRV_16_MA;
            break;
        default:
            break;
    }

    switch (gpio_hwdata->gpio_pull) {
        case NO_PULL:
            reg = reg | GPIO_NO_PULL;
            break;
        case PULL_DOWN:
            reg = reg | GPIO_PULL_DOWN;
            break;
        case KEEPER:
            reg = reg | GPIO_KEEPER;
            break;
        case PULL_UP:
            reg = reg | GPIO_PULL_UP;
            break;
        default:
            break;
    }
    writel(reg, gpio_cfg_vreg);
}

static void mdm_gpio_set_direction(struct mdm_gpio_hwdata *gpio_hwdata)
{
    uint32_t reg = readl(gpio_cfg_vreg);
    if (gpio_hwdata->gpio_direction)
        reg |= (1 << 9);    // bit9 1:out
    else
        reg &= ~(1 << 9);   // bit9 0:in

    writel(reg, gpio_cfg_vreg);
}

static void mdm_gpio_set_dir_level(struct mdm_gpio_hwdata *gpio_hwdata)
{
    uint32_t reg = readl(gpio_dir_lv_reg);

    if (gpio_hwdata->gpio_direction == 0 && gpio_hwdata->gpio_level == 0)
        reg &= ~(1U << 0);  // bit0 = 0 (in low level)
    else if (gpio_hwdata->gpio_direction == 0 && gpio_hwdata->gpio_level == 1)
        reg |= (1U << 0);   // bit0 = 1 (in high level)
    else if (gpio_hwdata->gpio_direction == 1 && gpio_hwdata->gpio_level == 0)
        reg &= (1U << 1);   // bit1 = 0 (out low level)
    else if (gpio_hwdata->gpio_direction == 1 && gpio_hwdata->gpio_level == 1)
        reg |= (1U << 1);   // bit1 = 1 (out high level)

    writel(reg, gpio_dir_lv_reg);
}

static int mdm_gpio_probe(struct platform_device *pdev)
{
    int ret = 0;
    struct mdm_gpio_hwdata *gpio_hwdata;

    /* gpio dt to hwdata */
    gpio_hwdata = mdm_gpio_dt_to_hwdata(pdev);
    if (!gpio_hwdata){
        dev_err(&pdev->dev, "Failed dt to hwdata\n");
        ret = -EINVAL;
        goto err_exit;
    }


    /* gpio_cfg_vreg ioremap */
    gpio_cfg_vreg = (uint32_t *)devm_ioremap(&pdev->dev, gpio_hwdata->addr, 4);
    if(!gpio_cfg_vreg) {
        dev_err(&pdev->dev, "Failed to map gpio_cfg_vreg registers\n");
        ret = -ENOMEM;
        goto err_exit;
    }

    /* gpio_dir_lv_reg ioremap */
    gpio_dir_lv_reg = (uint32_t *)devm_ioremap(&pdev->dev, gpio_hwdata->addr + 4, 4);
    if(!gpio_dir_lv_reg) {
        dev_err(&pdev->dev, "Failed to map gpio_dir_lv_reg registers\n");
        ret = -ENOMEM;
        goto err_exit;
    }
    dev_info(&pdev->dev, "gpio_dir_lv_reg = %x \n", gpio_hwdata->addr + 4);

    mdm_gpio_set_cfg(gpio_hwdata);
    mdm_gpio_set_direction(gpio_hwdata);
    mdm_gpio_set_dir_level(gpio_hwdata);

    dev_info(&pdev->dev, "Registered msm_gpio.\n");
    return ret;

err_exit:
    return ret;
}

static const struct of_device_id mdm_gpio_dt_ids[] = {
    {	.compatible = "qcom,mdm9x07-gpio",
    },
    {}
};


static struct platform_driver mdm_gpio_driver = {
    .probe          = mdm_gpio_probe,
    .driver         = {
        .name   = "msm_gpio",
        .owner  = THIS_MODULE,
        .of_match_table = mdm_gpio_dt_ids,
    },
};


static int __init gpio_mdm_init(void)
{
    return platform_driver_register(&mdm_gpio_driver);
}
subsys_initcall(gpio_mdm_init);

static void __exit gpio_mdm_cleanup(void)
{
	platform_driver_unregister(&mdm_gpio_driver);
}
module_exit(gpio_mdm_cleanup);


MODULE_DESCRIPTION("MSM GPIO DRIVER");
MODULE_LICENSE("GPL v2");

 设备树配置如下:

	gpio_25: gpio@0x01019000 {
		compatible = "qcom,mdm9x07-gpio";

		#if 1
		/*
		gpio number
		*/
		qcom,gpio-num = <25>;
		#endif

		#if REGISTER_ADDR
		/*
		gpio register address
		*/
		qcom,gpio-addr = <0x01019000>;
		#endif

		/*
		<0>:2mA <1>:4mA <2>:6mA <3>:8mA <4>:10mA <5>:12mA <6>:14mA <7>:16mA
		*/
		qcom,drv_strength = <2>;

		/*
		<0>:no pull <1>:pull down <2>:keeper <3>:pull up
		*/
		qcom,gpio_pull = <3>;

		/*
		<0>: in <1>:out
		*/
		qcom,gpio_dir = <1>;

		/*
		<0>:low_level <1>:hight_level
		*/
		qcom,gpio_level = <1>;
		status = "okay";
	};

查看dmesg的打印信息,可见驱动正常匹配

经测试,改驱动代码在SDX65同样适用

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值