linux设备树-lcd触摸屏设备驱动(代码片段)

大奥特曼打小怪兽 大奥特曼打小怪兽     2023-05-06     755

关键词:

----------------------------------------------------------------------------------------------------------------------------
内核版本:linux 5.2.8
根文件系统:busybox 1.25.0
u-boot:2016.05
----------------------------------------------------------------------------------------------------------------------------

在上一节我们已经移植了LCD驱动,那么本节将会移植LCD触摸屏驱动。有关触摸屏的原理,以及硬件接线,我们在linux驱动移植-LCD触摸屏设备驱动章节已经介绍的非常清楚了。同时在这一篇博客,我们也详细介绍了触摸屏驱动的实现,并进行了代码演示。

linux 5.2.8内核已经自带了s3c2440触摸屏驱动,该驱动还依赖于ADC驱动,相当于把我们在linux驱动移植-LCD触摸屏设备驱动中写的驱动程序拆成了两个部分,但是代码整体逻辑大致是一样的。

这一节,我们将尝试引入设备树,通过设备树来实现触摸屏驱动程序。

一、触摸屏驱动

linux 5.2.8自带的s3c2440触摸屏驱动,其采用platform设备驱动模型。

1.1 platform device

名字为"s3c2410-ts"的platform device定义在arch/arm/plat-samsung/devs.c文件:

static struct resource s3c_ts_resource[] = 
        [0] = DEFINE_RES_MEM(S3C24XX_PA_ADC, S3C24XX_SZ_ADC),   // 0x58000000  SZ_1M
        [1] = DEFINE_RES_IRQ(IRQ_TC),                           // IRQ_TC为子中断 子中断控制器硬件中断号9,对应的主中断控制器硬件中断号31
;

struct platform_device s3c_device_ts = 
        .name           = "s3c2410-ts",
        .id             = -1,
        .dev.parent     = &s3c_device_adc.dev,
        .num_resources  = ARRAY_SIZE(s3c_ts_resource),
        .resource       = s3c_ts_resource,
;

void __init s3c24xx_ts_set_platdata(struct s3c2410_ts_mach_info *hard_s3c2410ts_info)

        s3c_set_platdata(hard_s3c2410ts_info,
                         sizeof(struct s3c2410_ts_mach_info), &s3c_device_ts);

其中函数s3c24xx_ts_set_platdata用于设置platform设备的私有数据,数据类型为struct s3c2410_ts_mach_info,s3c_device_ts.dev.platform_data会被设置为&default_ts_data :

static struct s3c2410_ts_mach_info default_ts_data __initdata = 
        .delay                  = 10000,
        .presc                  = 49,
        .oversampling_shift     = 2,
;
default_ts_data定义在arch/arm/plat-samsung/devs.c文件中的,我们需要把s3c_device_ts成员里初始化的这些常量数据抽离到设备树中。

1.2 platform driver

名字为"s3c2410-ts"的platform driver定义在drivers/input/touchscreen/s3c2410_ts.c文件:

static const struct dev_pm_ops s3c_ts_pmops = 
        .suspend        = s3c2410ts_suspend,
        .resume         = s3c2410ts_resume,
;
#endif

static const struct platform_device_id s3cts_driver_ids[] =   
         "s3c2410-ts", 0 ,
         "s3c2440-ts", 0 ,
         "s3c64xx-ts", FEAT_PEN_IRQ ,
         
;
MODULE_DEVICE_TABLE(platform, s3cts_driver_ids);

static struct platform_driver s3c_ts_driver = 
        .driver         = 
                .name   = "samsung-ts",
#ifdef CONFIG_PM
                .pm     = &s3c_ts_pmops,
#endif
        ,
        .id_table       = s3cts_driver_ids,
        .probe          = s3c2410ts_probe,
        .remove         = s3c2410ts_remove,
;

module_platform_driver(s3c_ts_driver);

1.3 s3c2410ts_probe

当platform设备和驱动匹配后,将会调用s3c2410ts_probe进行input设备的注册。函数定义在drivers/input/touchscreen/s3c2410_ts.c:

/**
 * s3c2410ts_probe - device core probe entry point
 * @pdev: The device we are being bound to.
 *
 * Initialise, find and allocate any resources we need to run and then
 * register with the ADC and input systems.
 */
static int s3c2410ts_probe(struct platform_device *pdev)

        struct s3c2410_ts_mach_info *info;
        struct device *dev = &pdev->dev;
        struct input_dev *input_dev;
        struct resource *res;
        int ret = -EINVAL;

        /* Initialise input stuff */
        memset(&ts, 0, sizeof(struct s3c2410ts));

        ts.dev = dev;

        info = dev_get_platdata(dev);          
        if (!info) 
                dev_err(dev, "no platform data, cannot attach\\n");
                return -EINVAL;
        

        dev_dbg(dev, "initialising touchscreen\\n");

        ts.clock = clk_get(dev, "adc");        
        if (IS_ERR(ts.clock)) 
                dev_err(dev, "cannot get adc clock source\\n");
                return -ENOENT;
        

        ret = clk_prepare_enable(ts.clock);    
        if (ret) 
                dev_err(dev, "Failed! to enabled clocks\\n");
                goto err_clk_get;
        
        dev_dbg(dev, "got and enabled clocks\\n");

        ts.irq_tc = ret = platform_get_irq(pdev, 0);     
        if (ret < 0) 
                dev_err(dev, "no resource for interrupt\\n");
                goto err_clk;
        

        res = platform_get_resource(pdev, IORESOURCE_MEM, 0);  
        if (!res) 
                dev_err(dev, "no resource for registers\\n");
                ret = -ENOENT;
                goto err_clk;
        

        ts.io = ioremap(res->start, resource_size(res));
        if (ts.io == NULL) 
                dev_err(dev, "cannot map registers\\n");
                ret = -ENOMEM;
                goto err_clk;
        

        /* inititalise the gpio */
        if (info->cfg_gpio)
                info->cfg_gpio(to_platform_device(ts.dev));

        ts.client = s3c_adc_register(pdev, s3c24xx_ts_select,
                                     s3c24xx_ts_conversion, 1);
        if (IS_ERR(ts.client)) 
                dev_err(dev, "failed to register adc client\\n");
                ret = PTR_ERR(ts.client);
                goto err_iomap;
        

        /* Initialise registers */
        if ((info->delay & 0xffff) > 0)
                writel(info->delay & 0xffff, ts.io + S3C2410_ADCDLY);

        writel(WAIT4INT | INT_DOWN, ts.io + S3C2410_ADCTSC);

        input_dev = input_allocate_device();
        if (!input_dev) 
                dev_err(dev, "Unable to allocate the input device !!\\n");
                ret = -ENOMEM;
                goto err_iomap;
        

        ts.input = input_dev;
        ts.input->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_ABS);
        ts.input->keybit[BIT_WORD(BTN_TOUCH)] = BIT_MASK(BTN_TOUCH);
        input_set_abs_params(ts.input, ABS_X, 0, 0x3FF, 0, 0);
        input_set_abs_params(ts.input, ABS_Y, 0, 0x3FF, 0, 0);

        ts.input->name = "S3C24XX TouchScreen";
        ts.input->id.bustype = BUS_HOST;
        ts.input->id.vendor = 0xDEAD;
        ts.input->id.product = 0xBEEF;
        ts.input->id.version = 0x0102;

        ts.shift = info->oversampling_shift;
        ts.features = platform_get_device_id(pdev)->driver_data;

        ret = request_irq(ts.irq_tc, stylus_irq, 0,
                          "s3c2410_ts_pen", ts.input);
        if (ret) 
                dev_err(dev, "cannot get TC interrupt\\n");
                goto err_inputdev;
        

        dev_info(dev, "driver attached, registering input device\\n");

        /* All went ok, so register to the input system */
        ret = input_register_device(ts.input);
        if (ret < 0) 
                dev_err(dev, "failed to register input device\\n");
                ret = -EIO;
                goto err_tcirq;
        

        return 0;

 err_tcirq:
        free_irq(ts.irq_tc, ts.input);
 err_inputdev:
        input_free_device(ts.input);
 err_iomap:
        iounmap(ts.io);
 err_clk:
        clk_disable_unprepare(ts.clock);
        del_timer_sync(&touch_timer);
 err_clk_get:
        clk_put(ts.clock);
        return ret;

全局变量ts定义如下:

#define TSC_SLEEP  (S3C2410_ADCTSC_PULL_UP_DISABLE | S3C2410_ADCTSC_XY_PST(0))

#define INT_DOWN        (0)
#define INT_UP          (1 << 8)

#define WAIT4INT        (S3C2410_ADCTSC_YM_SEN | \\
                         S3C2410_ADCTSC_YP_SEN | \\
                         S3C2410_ADCTSC_XP_SEN | \\
                         S3C2410_ADCTSC_XY_PST(3))

#define AUTOPST         (S3C2410_ADCTSC_YM_SEN | \\
                         S3C2410_ADCTSC_YP_SEN | \\
                         S3C2410_ADCTSC_XP_SEN | \\
                         S3C2410_ADCTSC_AUTO_PST | \\
                         S3C2410_ADCTSC_XY_PST(0))

#define FEAT_PEN_IRQ    (1 << 0)        /* HAS ADCCLRINTPNDNUP */


/**
 * struct s3c2410ts - driver touchscreen state.
 * @client: The ADC client we registered with the core driver.
 * @dev: The device we are bound to.
 * @input: The input device we registered with the input subsystem.
 * @clock: The clock for the adc.
 * @io: Pointer to the IO base.
 * @xp: The accumulated X position data.
 * @yp: The accumulated Y position data.
 * @irq_tc: The interrupt number for pen up/down interrupt
 * @count: The number of samples collected.
 * @shift: The log2 of the maximum count to read in one go.
 * @features: The features supported by the TSADC MOdule.
 */
struct s3c2410ts 
        struct s3c_adc_client *client;
        struct device *dev;
        struct input_dev *input;
        struct clk *clock;
        void __iomem *io;
        unsigned long xp;
        unsigned long yp;
        int irq_tc;
        int count;
        int shift;
        int features;
;

static struct s3c2410ts ts;

二、ADC驱动

触摸屏驱动依赖于ADC驱动,ADC驱动也是采用的其采用platform设备驱动模型。

2.1 platform device

名字为"s3c24xx-adc"的platform device定义在arch/arm/plat-samsung/devs.c文件:

static struct resource s3c_adc_resource[] = 
        [0] = DEFINE_RES_MEM(S3C24XX_PA_ADC, S3C24XX_SZ_ADC),     // 0x58000000  SZ_1M
        [1] = DEFINE_RES_IRQ(IRQ_TC),                             // IRQ_TC为子中断 子中断控制器硬件中断号9,对应的主中断控制器硬件中断号31  
        [2] = DEFINE_RES_IRQ(IRQ_ADC),                            // IRQ_ADC为子中断 子中断控制器硬件中断号10,对应的主中断控制器硬件中断号31 
;

struct platform_device s3c_device_adc = 
        .name           = "s3c24xx-adc",
        .id             = -1,
        .num_resources  = ARRAY_SIZE(s3c_adc_resource),
        .resource       = s3c_adc_resource,
;

2.2 platform driver

名字为"s3c24xx-adc"的platform driver定义在arch/arm/plat-samsung/adc.c文件:

static const struct platform_device_id s3c_adc_driver_ids[] = 
        
                .name           = "s3c24xx-adc",
                .driver_data    = TYPE_ADCV1,
        , 
                .name           = "s3c2443-adc",
                .driver_data    = TYPE_ADCV11,
        , 
                .name           = "s3c2416-adc",
                .driver_data    = TYPE_ADCV12,
        , 
                .name           = "s3c64xx-adc",
                .driver_data    = TYPE_ADCV2,
        , 
                .name           = "samsung-adc-v3",
                .driver_data    = TYPE_ADCV3,
        ,
         
;
MODULE_DEVICE_TABLE(platform, s3c_adc_driver_ids);

static const struct dev_pm_ops adc_pm_ops = 
        .suspend        = s3c_adc_suspend,
        .resume         = s3c_adc_resume,
;

static struct platform_driver s3c_adc_driver = 
        .id_table       = s3c_adc_driver_ids,
        .driver         = 
                .name   = "s3c-adc",
                .pm     = &adc_pm_ops,
        ,
        .probe          = s3c_adc_probe,
        .remove         = s3c_adc_remove,
;

2.3 s3c_adc_probe

当platform设备和驱动匹配后,将会调用s3c_adc_probe进行ADD相关的初始化工作。函数定义在arch/arm/plat-samsung/adc.c:

static int s3c_adc_probe(struct platform_device *pdev)

        struct device *dev = &pdev->dev;
        struct adc_device *adc;
        struct resource *regs;
        enum s3c_cpu_type cpu = platform_get_device_id(pdev)->driver_data;
        int ret;
        unsigned tmp;

        adc = devm_kzalloc(dev, sizeof(*adc), GFP_KERNEL);
        if (!adc)
                return -ENOMEM;

        spin_lock_init(&adc->lock);

        adc->pdev = pdev;
        adc->prescale = S3C2410_ADCCON_PRSCVL(49);

        adc->vdd = devm_regulator_get(dev, "vdd");
        if (IS_ERR(adc->vdd)) 
                dev_err(dev, "operating without regulator \\"vdd\\" .\\n");
                return PTR_ERR(adc->vdd);
        

        adc->irq = platform_get_irq(pdev, 1);
        if (adc->irq <= 0) 
                dev_err(dev, "failed to get adc irq\\n");
                return -ENOENT;
        

        ret = devm_request_irq(dev, adc->irq, s3c_adc_irq, 0, dev_name(dev),
                                adc);
        if (ret < 0) 
                dev_err(dev, "failed to attach adc irq\\n");
                return ret;
        

        adc->clk = devm_clk_get(dev, "adc");
        if (IS_ERR(adc->clk)) 
                dev_err(dev, "failed to get adc clock\\n");
                return PTR_ERR(adc->clk);
        

        regs = platform_get_resource(pdev, IORESOURCE_MEM, 0);
        adc->regs = devm_ioremap_resource(dev, regs);
        if (IS_ERR(adc->regs))
                return PTR_ERR(adc->regs);

        ret = regulator_enable(adc->vdd);
        if (ret)
                return ret;

        clk_prepare_enable(adc->clk);

        tmp = adc->prescale | S3C2410_ADCCON_PRSCEN;

        /* Enable 12-bit ADC resolution */
        if (cpu == TYPE_ADCV12)
                tmp |= S3C2416_ADCCON_RESSEL;
        if (cpu == TYPE_ADCV2 || cpu == TYPE_ADCV3)
                tmp |= S3C64XX_ADCCON_RESSEL;

        writel(tmp, adc->regs + S3C2410_ADCCON);

        dev_info(dev, "attached adc driver\\n");

        platform_set_drvdata(pdev, adc);
        adc_dev = adc;

        return 0;

三、修改驱动程序

由于linux 5.2.8已经自带了s3c2440触摸屏、以及ADC驱动,因此我们直接在内核源码上改造,使其支持设备树即可。

3.1 修改设备树

3.1.1 新增myts设备节点

在内核arch/arm/boot/dts/s3c2440-smdk2440.dts文件中添加myts设备节点,用于触摸屏驱动:

myts: myts@5800000 
    compatible = "myts";
    reg = <0x58000000 0x100>;
    reg-names = "adc_ts_physical";
    interrupts = <1 31 9 3>;
    interrupt-names = "int_tc";
    clocks = <&clocks PCLK_ADC>;
    clock-names = "adc";
    delay = <0xffff>;
    presc = <49>;
    oversampling_shift = <2>;     
;
3.1.2 新增myadc设备节点

在内核arch/arm/boot/dts/s3c2440-smdk2440.dts文件中添加myadc设备节点,用于ADC驱动:

myadc: myadc@5800000 
    compatible = "myadc";
    reg = <0x58000000 0x100>;
    reg-names = "adc_physical";
    interrupts = <1 31 9 3>,<1 31 10 3>;
    interrupt-names = "int_tc","int_adc_s";
    clocks = <&clocks PCLK_ADC>;
    clock-names = "adc";
;

3.2 修改触摸屏驱动

3.2.1 修改s3c_ts_driver

为了支持设备树,所以我们需要修改s3c_ts_driver变量添加设备树匹配项,变量定义在arch/arm/plat-samsung/devs.c文件。修改完成后代码如下:

static const struct dev_pm_ops s3c_ts_pmops = 
        .suspend        = s3c2410ts_suspend,
        .resume         = s3c2410ts_resume,
;
#endif

static const struct of_device_id  s3cts_dt_match[] =    // 用于设备树匹配
     .compatible = "myts", .data = (void *)0 ,
    ,
;

static const struct platform_device_id s3cts_driver_ids[] =  
         "s3c2410-ts", 0 ,
         "s3c2440-ts", 0 ,
         "s3c64xx-ts", FEAT_PEN_IRQ ,
         
;
MODULE_DEVICE_TABLE(platform, s3cts_driver_ids);

static struct platform_driver s3c_ts_driver = 
        .driver         = 
                .name   = "samsung-ts",
                .of_match_table = of_match_ptr(s3cts_dt_match),
#ifdef CONFIG_PM
                .pm     = &s3c_ts_pmops,
#endif
        ,
        .id_table       = s3cts_driver_ids,
        .probe          = s3c2410ts_probe,
        .remove         = s3c2410ts_remove,
;
3.2.2  修改s3c2410ts_probe

当platform设备和驱动匹配后,将会调用s3c2410ts_probe进行input设备的注册,函数位于drivers/input/touchscreen/s3c2410_ts.c文件,修改完之后代码如下:

/**
 * s3c2410ts_probe - device core probe entry point
 * @pdev: The device we are being bound to.
 *
 * Initialise, find and allocate any resources we need to run and then
 * register with the ADC and input systems.
 */
static int s3c2410ts_probe(struct platform_device *pdev)

        struct s3c2410_ts_mach_info *info;
        struct device *dev = &pdev->dev;
        struct input_dev *input_dev;
        struct resource *res;
        int ret = -EINVAL;
        struct device_node *np;
        
        
        np = dev->of_node;                // 获取myts设备节点
        if (!np) 
            dev_err(dev, "could not find device info\\n");
            return -EINVAL;
        

        /* Initialise input stuff */
        memset(&ts, 0, sizeof(struct s3c2410ts));

        ts.dev = dev;

        info = devm_kzalloc(dev, sizeof(*info), GFP_KERNEL);       // 动态分配struct s3c2410_ts_mach_info
        if (!info) 
                dev_err(dev, "no mem for info\\n");
                return -ENOMEM;
        
        
        // 获取设备节点中的各个属性值,用来设置驱动参数
        of_property_read_u32(np, "delay", &info->delay);
        of_property_read_u32(np, "presc", &info->presc);
        of_property_read_u32(np, "oversampling_shift", &info->oversampling_shift);

        dev_dbg(dev, "%s:                delay:  0x%lx\\n", __func__, info->delay);
        dev_dbg(dev, "%s:                presc:  0x%1x\\n", __func__, info->presc);
        dev_dbg(dev, "%s:   oversampling_shift:  0x%1x\\n", __func__, info->oversampling_shift);
        
        dev_dbg(dev, "initialising touchscreen\\n");

        ts.clock = clk_get(dev, "adc");        // 获取adc时钟   
        if (IS_ERR(ts.clock)) 
                dev_err(dev, "cannot get adc clock source\\n");
                return -ENOENT;
        

        ret = clk_prepare_enable(ts.clock);    // 使能时钟
        if (ret) 
                dev_err(dev, "Failed! to enabled clocks\\n");
                goto err_clk_get;
        
        dev_dbg(dev, "got and enabled clocks\\n");

        ts.irq_tc = ret = platform_get_irq(pdev, 0);     // 获取第一个IRQ编号 
        if (ret < 0) 
                dev_err(dev, "no resource for interrupt\\n");
                goto err_clk;
        

        res = platform_get_resource(pdev, IORESOURCE_MEM, 0);   // 获取第一个内存资源
        if (!res) 
                dev_err(dev, "no resource for registers\\n");
                ret = -ENOENT;
                goto err_clk;
        

        ts.io = ioremap(res->start, resource_size(res));      // 将ADC相关寄存器起始物理地址映射到虚拟地址,并返回虚拟地址
        if (ts.io == NULL) 
                dev_err(dev, "cannot map registers\\n");
                ret = -ENOMEM;
                goto err_clk;
        

        /* inititalise the gpio */
        if (info->cfg_gpio)
                info->cfg_gpio(to_platform_device(ts.dev));

        ts.client = s3c_adc_register(pdev, s3c24xx_ts_select,
                                     s3c24xx_ts_conversion, 1);
        if (IS_ERR(ts.client)) 
                dev_err(dev, "failed to register adc client\\n");
                ret = PTR_ERR(ts.client);
                goto err_iomap;
        

        /* Initialise registers */
        if ((info->delay & 0xffff) > 0)
                writel(info->delay & 0xffff, ts.io + S3C2410_ADCDLY);        // 设置ADC启动延时时间

        writel(WAIT4INT | INT_DOWN, ts.io + S3C2410_ADCTSC);        // 设置ADCTSC 1 << 7 | 1<<6 | 1 << 4 | (3&3) << 0 = 0xD3   
                                                                       开启INT_TC中断,笔尖按下触发

        input_dev = input_allocate_device();              // 向内核申请input_dev结构体
        if (!input_dev) 
                dev_err(dev, "Unable to allocate the input device !!\\n");
                ret = -ENOMEM;
                goto err_iomap;
        

        ts.input = input_dev;
        ts.input->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_ABS);  // 支持按键事件 支持绝对位移事件
        ts.input->keybit[BIT_WORD(BTN_TOUCH)] = BIT_MASK(BTN_TOUCH); // 触摸屏笔尖按下
        input_set_abs_params(ts.input, ABS_X, 0, 0x3FF, 0, 0);     // s3c2440手册ADC是10位,所以第四个参数设置为0x3FF
        input_set_abs_params(ts.input, ABS_Y, 0, 0x3FF, 0, 0);     // s3c2440手册ADC是10位,所以第四个参数设置为0x3FF 

        ts.input->name = "S3C24XX TouchScreen";
        ts.input->id.bustype = BUS_HOST;
        ts.input->id.vendor = 0xDEAD;
        ts.input->id.product = 0xBEEF;
        ts.input->id.version = 0x0102;

        ts.shift = info->oversampling_shift;
         //ts.features = platform_get_device_id(pdev)->driver_data;
         ts.features = of_device_get_match_data(&pdev->dev);    // 获取私有数据


        ret = request_irq(ts.irq_tc, stylus_irq, 0,                 // 申请中断   
                          "s3c2410_ts_pen", ts.input);
        if (ret) 
                dev_err(dev, "cannot get TC interrupt\\n");
                goto err_inputdev;
        

        dev_info(dev, "driver attached, registering input device\\n");

        /* All went ok, so register to the input system */
        ret = input_register_device(ts.input);                  // 注册input_dev
        if (ret < 0) 
                dev_err(dev, "failed to register input device\\n");
                ret = -EIO;
                goto err_tcirq;
        

        return 0;

 err_tcirq:
        free_irq(ts.irq_tc, ts.input);
 err_inputdev:
        input_free_device(ts.input);
 err_iomap:
        iounmap(ts.io);
 err_clk:
        clk_disable_unprepare(ts.clock);
        del_timer_sync(&touch_timer);
 err_clk_get:
        clk_put(ts.clock);
        return ret;

同时需要引入头文件:

#include <linux/of_device.h>

3.3 修改ADC驱动

3.3.1 修改s3c_adc_driver

为了支持设备树,所以我们需要修改s3c_adc_driver变量添加设备树匹配项,变量定义在arch/arm/plat-samsung/adc.c文件。修改完成后代码如下:

static const struct of_device_id  s3c_adc_dt_match[] =    // 用于设备树匹配
     .compatible = "myadc", .data = (void *)TYPE_ADCV1 ,
    ,
;

static const struct platform_device_id s3c_adc_driver_ids[] = 
        
                .name           = "s3c24xx-adc",
                .driver_data    = TYPE_ADCV1,
        , 
                .name           = "s3c2443-adc",
                .driver_data    = TYPE_ADCV11,
        , 
                .name           = "s3c2416-adc",
                .driver_data    = TYPE_ADCV12,
        , 
                .name           = "s3c64xx-adc",
                .driver_data    = TYPE_ADCV2,
        , 
                .name           = "samsung-adc-v3",
                .driver_data    = TYPE_ADCV3,
        ,
         
;
MODULE_DEVICE_TABLE(platform, s3c_adc_driver_ids);

static const struct dev_pm_ops adc_pm_ops = 
        .suspend        = s3c_adc_suspend,
        .resume         = s3c_adc_resume,
;

static struct platform_driver s3c_adc_driver = 
        .id_table       = s3c_adc_driver_ids,
        .driver         = 
                .name   = "s3c-adc",
                .of_match_table = of_match_ptr(s3c_adc_dt_match),
                .pm     = &adc_pm_ops,
        ,
        .probe          = s3c_adc_probe,
        .remove         = s3c_adc_remove,
;
3.3.2  修改s3c_adc_probe

当platform设备和驱动匹配后,将会调用s3c_adc_probe进行ADD相关的初始化工作。函数位于arch/arm/plat-samsung/adc.c文件,修改完之后代码如下:

static int s3c_adc_probe(struct platform_device *pdev)

        struct device *dev = &pdev->dev;
        struct adc_device *adc;
        struct resource *regs;
        //enum s3c_cpu_type cpu = platform_get_device_id(pdev)->driver_data;
        enum s3c_cpu_type cpu = (int)of_device_get_match_data(&pdev->dev);    // 获取私有数据
        int ret;
        unsigned tmp;

        adc = devm_kzalloc(dev, sizeof(*adc), GFP_KERNEL);
        if (!adc)
                return -ENOMEM;

        spin_lock_init(&adc->lock);

        adc->pdev = pdev;
        adc->prescale = S3C2410_ADCCON_PRSCVL(49);                     // 设置预分频器的值  (49&0xff) << 6           

        adc->vdd = devm_regulator_get(dev, "vdd");
        if (IS_ERR(adc->vdd)) 
                dev_err(dev, "operating without regulator \\"vdd\\" .\\n");
                return PTR_ERR(adc->vdd);
        

        adc->irq = platform_get_irq(pdev, 1);             // 获取第2个IRQ编号   IRQ_ADC  ADC转换成功后,会进入IRQ_ADC中断函数
        if (adc->irq <= 0) 
                dev_err(dev, "failed to get adc irq\\n");
                return -ENOENT;
        

        ret = devm_request_irq(dev, adc->irq, s3c_adc_irq, 0, dev_name(dev),  // 申请中断
                                adc);
        if (ret < 0) 
                dev_err(dev, "failed to attach adc irq\\n");
                return ret;
        

        adc->clk = devm_clk_get(dev, "adc");                   // 获取adc时钟
        if (IS_ERR(adc->clk)) 
                dev_err(dev, "failed to get adc clock\\n");
                return PTR_ERR(adc->clk);
        

        regs = platform_get_resource(pdev, IORESOURCE_MEM, 0);  // 获取第一个内存资源  ADC相关寄存器基地址
        adc->regs = devm_ioremap_resource(dev, regs);           // 将ADC相关寄存器起始物理地址映射到虚拟地址,并返回虚拟地址
        if (IS_ERR(adc->regs))
                return PTR_ERR(adc->regs);

        ret = regulator_enable(adc->vdd);
        if (ret)
                return ret;

        clk_prepare_enable(adc->clk);        // 使能时钟

        tmp = adc->prescale | S3C2410_ADCCON_PRSCEN;     // 49 <<6 | 1<<14

        /* Enable 12-bit ADC resolution */
        if (cpu == TYPE_ADCV12)
                tmp |= S3C2416_ADCCON_RESSEL;
        if (cpu == TYPE_ADCV2 || cpu == TYPE_ADCV3)
                tmp |= S3C64XX_ADCCON_RESSEL;

        writel(tmp, adc->regs + S3C2410_ADCCON);       // 预分频器使能,分频值设置为49   

        dev_info(dev, "attached adc driver\\n");

        platform_set_drvdata(pdev, adc);    // 保存驱动私有数据
        adc_dev = adc;

        return 0;

同时需要引入头文件:

#include <linux/of_device.h>

此外,还需要将查找文件中如下代码:

enum s3c_cpu_type cpu = platform_get_device_id(adc->pdev)->driver_data;

全部修改为:

enum s3c_cpu_type cpu = (int)of_device_get_match_data(&pdev->dev);    // 获取私有数据

四、烧录开发板测试

4.1 配置内核

执行如下命令:

root@zhengyang:/work/sambashare/linux-5.2.8-dt# make menuconfig

配置内核,将内核自带的触摸屏驱动编译进内核:

Device Drivers  --->
    Input device support  -->
        [*] Touchscreens  -->
               <*> Samsung S3C2410/generic touchscreen input driver
    Graphics support  --->
        [ ] Bootup logo  --->      

保存文件,输入文件名s3c2440_defconfig,在当前路径下生成s3c2440_defconfig:存档:

root@zhengyang:/work/sambashare/linux-5.2.8-dt# mv s3c2440_defconfig ./arch/arm/configs/

4.2 编译内核

此时重新执行:

root@zhengyang:/work/sambashare/linux-5.2.8-dt# make distclean
root@zhengyang:/work/sambashare/linux-5.2.8-dt# make s3c2440_defconfig    
root@zhengyang:/work/sambashare/linux-5.2.8-dt# make uImage V=1

将uImage复制到tftp服务器路径下:

root@zhengyang:/work/sambashare/linux-5.2.8-dt#  cp /work/sambashare/linux-5.2.8-dt/arch/arm/boot/uImage /work/tftpboot/

4.3  编译dts

在linux内核根目录执行如下命令:

root@zhengyang:/work/sambashare/linux-5.2.8-dt# make dtbs
  DTC     arch/arm/boot/dts/s3c2416-smdk2416.dtb
  DTC     arch/arm/boot/dts/s3c2440-smdk2440.dtb

编译设备树文件,把前面配置过的arch/arm/boot/dts里的dts文件编译成dtb文件。

将s3c2440-smdk2440.dtb复制到tftp服务器路径下:

root@zhengyang:/work/sambashare/linux-5.2.8-dt# cp /work/sambashare/linux-5.2.8-dt/arch/arm/boot/dts/s3c2440-smdk2440.dtb /work/tftpboot/

4.4 启动内核

uboot启动后,将dtb下载到内存地址0x30001000中:

SMDK2440 # tftp 0x30001000 s3c2440-smdk2440.dtb

注意:我们可以修改uboot源码,扩展一个device_tree分区,然后将dtb文件存储在该分组取中。

然后将内核镜像加载到内存0x30008000地址,并烧录内核到Nand Flash:

SMDK2440 # tftp 30008000 uImage
SMDK2440 # nand erase.part kernel
SMDK2440 # nand write 30008000 kernel

然后可以使用如下命令启动内核:

SMDK2440 # bootm 0x30008000 - 0x30001000   // 无设备树时,直接bootm 0x30008000
//bootm  uImage地址  ramdisk地址  设备树镜像地址

内核启动打印有关LCD触摸屏信息如下:

....
samsung-ts 58000000.myts: no pinctrl handle
OF: no dma-ranges found for node(/myts@5800000)
samsung-ts 58000000.myts: device is not dma coherent
samsung-ts 58000000.myts: device is not behind an iommu
samsung-ts 58000000.myts: s3c2410ts_probe:                delay:  0xffff
samsung-ts 58000000.myts: s3c2410ts_probe:                presc:  0x31
samsung-ts 58000000.myts: s3c2410ts_probe:   oversampling_shift:  0x2
samsung-ts 58000000.myts: initialising touchscreen
clock-names adc in index 0
samsung-ts 58000000.myts: got and enabled clocks
OF: of_irq_parse_one: dev=/myts@5800000, index=0
OF:  parent=/interrupt-controller@4a000000, intsize=4
OF:  intspec=1
of_irq_parse_raw:  /interrupt-controller@4a000000:00000001,0000001f,00000009,00000003
OF: of_irq_parse_raw: ipar=/interrupt-controller@4a000000, size=4
OF:  -> addrsize=1
OF:  -> got it !
samsung-ts 58000000.myts: driver attached, registering input device
PM: Adding info for No Bus:input0
input: S3C24XX TouchScreen as /devices/virtual/input/input0
....

内核启动打印有关ADC信息如下:

....
s3c-adc 58000000.myadc: no pinctrl handle
OF: no dma-ranges found for node(/myadc@5800000)
s3c-adc 58000000.myadc: device is not dma coherent
s3c-adc 58000000.myadc: device is not behind an iommu
OF: of_irq_parse_one: dev=/myadc@5800000, index=1
OF:  parent=/interrupt-controller@4a000000, intsize=4
OF:  intspec=1
of_irq_parse_raw:  /interrupt-controller@4a000000:00000001,0000001f,0000000a,00000003
OF: of_irq_parse_raw: ipar=/interrupt-controller@4a000000, size=4
OF:  -> addrsize=1
OF:  -> got it !
clock-names adc in index 0
s3c-adc 58000000.myadc: attached adc driver
....

运行命令cat /proc/interrupts可以查看当前系统有哪些中断服务:

[root@zy:/]# cat /proc/interrupts
           CPU0
  7:       1304  s3c-eint   7 Edge      eth0
  8:          0       s3c   8 Edge      s3c2410-rtc tick
 13:      10445       s3c  13 Edge      samsung_time_irq
 22:          0       s3c  16 Edge      4d000000.fb
 27:          0       s3c  27 Edge      54000000.i2c
 30:          0       s3c  30 Edge      s3c2410-rtc alarm
 35:          8  s3c-level  35 Level     50004000.serial
 36:         56  s3c-level  36 Level     50004000.serial
 41:          0  s3c-level  41 Edge      s3c2410_ts_pen             // IRQ_TC
 42:          0  s3c-level  42 Edge      58000000.myadc             // IRQ_ADC
 59:          0  s3c-level  59 Edge      53000000.watchdog

查看设备节点文件:

[root@zy:/]# ls /dev/input -l
total 0
crw-rw----    1 0        0          13,  64 Jan  1 00:00 event0

五、使用tslib进行测试

5.1 下载tslib

直接到github上下载:

root@zhengyang:/work/sambashare/drivers/#git clone https://github.com/kergoth/tslib

下载完成后,我直接上传到ubuntu服务器如下路径:/work/sambashare/drivers。

跳转到tslib文件夹:

root@zhengyang:/work/sambashare/drivers# cd tslib/

5.2 编译

首先运行:

root@zhengyang:/work/sambashare/drivers/tslib# ./autogen.sh  
root@zhengyang:/work/sambashare/drivers/tslib# mkdir tmp

然后配置:

root@zhengyang:/work/sambashare/drivers/tslib# CC=arm-linux-gcc ./configure --host=arm-linux --cache-file=arm-linux.cache --prefix=$(pwd)/tmp CFLAGS="-march=armv4t -O2 -Wall -W"

编译安装:

root@zhengyang:/work/sambashare/drivers/tslib# make   //编译
root@zhengyang:/work/sambashare/drivers/tslib# make install  //安装到temp目录下   

需要注意的是编译所使用的的版本要和内核编译的版本保持一致,我是用的是arm-linux-gcc4.8.3版本。

可以通过如下命令查看可执行文件的平台属性信息:

root@zhengyang:/work/sambashare/drivers/tslib# cd tmp
root@zhengyang:/work/sambashare/drivers/tslib/tmp# arm-linux-readelf -A bin/ts_test
Attribute Section: aeabi
File Attributes
  Tag_CPU_name: "4T"
  Tag_CPU_arch: v4T
  Tag_ARM_ISA_use: Yes
  Tag_THUMB_ISA_use: Thumb-1
  Tag_ABI_PCS_wchar_t: 4
  Tag_ABI_FP_rounding: Needed
  Tag_ABI_FP_denormal: Needed
  Tag_ABI_FP_exceptions: Needed
  Tag_ABI_FP_number_model: IEEE 754
  Tag_ABI_align_needed: 8-byte
  Tag_ABI_align_preserved: 8-byte, except leaf SP
  Tag_ABI_enum_size: int

5.3 配置nfs文件系统

将tmp里面的bin ,etc,include,lib4个目录下的文件拷贝到文件系统的bin ,etc,include,lib4个目录下 :

root@zhengyang:/work/sambashare/drivers/tslib/tmp# cp * /work/nfs_root/rootfs/ -rfd

进入nfs文件系统,修改etc/inittab文件:

root@zhengyang:/work/sambashare/drivers/tslib/tmp# cd /work/nfs_root/rootfs/
root@zhengyang:/work/sambashare/drivers/tslib/tmp# vim etc/inittab

检查是否会启动:

tty1: tty1::askfirst:-/bin/sh  #在虚拟终端tty1启动askfirst动作的shell,也就是在LCD上会出现Please press Enter to active this console.

若有,前面加#,屏蔽掉,这条命令。

5.4  安配置LCD和触摸屏环境

[root@zy:/]# export TSLIB_TSDEVICE=/dev/input/event0    #ts设备文件(触摸屏):event0
[root@zy:/]# export TSLIB_CALIBFILE=/etc/pointercal     #校验文件(calibrate file),存放校验值
[root@zy:/]# export TSLIB_CONFFILE=/etc/ts.conf         #配置文件
[root@zy:/]# export TSLIB_PLUGINDIR=/lib/ts             #插件文件
[root@zy:/]# export TSLIB_CONSOLEDEVICE=none            #终端控制台设为NULL
[root@zy:/]# export TSLIB_FBDEVICE=/dev/fb0             #fb设备文件(LCD):fb0

或者直接写入nfs根文件系统路径下etc/profile文件中。

5.5 测试

运行校准程序,触摸屏依次出现5个点,依次点击之:

[root@zy:/]# ts_calibrate
xres = 240, yres = 320
Took 5 samples...
Top left : X =  267 Y =  783
Took 5 samples...
Top right : X =  782 Y =  774
Took 3 samples...
Bot right : X =  769 Y =  185
Took 2 samples...
Bot left : X =  289 Y =  212
Took 19 samples...
Center : X =  532 Y =  497
-29.351410 0.281104 0.002008
352.980225 -0.013406 -0.379243
Calibration constants: -1923574 18422 131 23132912 -878 -24854 65536

生成的校准文件名为pointercal,位于/etc目录下。

在开发板上执行如下名,就可以在lcd上绘图了:

[root@zy:/]# ts_test

屏幕最上方会出现三个按钮,分别为“Drag”、“Draw”和“Quit”,默认是第一个,因此,用触摸笔点击任何一处,十字光标便会到那里。

下面是点击Draw按钮并用触摸笔写字的效果图:

同时控制台输出大量信息,其中一小部分:

194.859177:    125    192    255
194.889177:    131    196    255
194.919155:    135    199    255
194.949226:    138    196    255
194.979155:    141    189    255
195.009152:    143    183    255
195.039166:    144    178    255
195.069162:    146    175    255
195.099161:    150    176    255
195.129166:    154    175    255
195.159257:    157    175    255
195.189178:    158    179    255
195.219176:    155    187    255
195.249176:    152    197    255
195.279150:    150    206    255
195.309160:    151    209    255
195.339161:    154    212    255
195.369205:    160    213      0

 第一列为timeval结构体的两个成员:tv_sec和tv_usec,中间两列分别是X和Y的坐标,最后为pressure,这里可以理解成“触摸事件”,为255表示触摸笔点击了(接触)屏幕,为0表示触摸笔离开了屏幕(这里出现很多的255是正常的,因为写字过程中笔没有离开触摸屏)。点击屏幕上“Quit”或按Ctrl+C可退出该程序。

参考文章

[1]linux驱动移植-LCD驱动基础

[2]linux驱动移植-LCD设备驱动

[3]linux驱动移植-LCD触摸屏设备驱动

[4]基于设备树的TQ2440触摸屏驱动移植

编程\_lcd驱动程序框架\_使用设备树(代码片段)

编程_LCD驱动程序框架_使用设备树参考资料,GIT仓库里:基于这个程序修改:IMX6ULL\\source\\03_LCD\\03_lcd_drv_qemu_okSTM32MP157\\source\\A7\\03_LCD\\03_lcd_drv_qemu_ok参考:内核自带的示例驱动程序Linux驱动源码:drivers/v 查看详情

基于瑞芯微rv1109linux触摸屏gt911驱动调试心得-设备树刷厂商给的触摸屏固件...(代码片段)

...标公众号”干货福利,第一时间送达!之前调试触摸屏的文章如下:基于瑞芯微RV1109Linux触摸屏GT911驱动调试心得由于之前调触摸屏一直都对不上点,然后厂商又给了我新的驱动代码。但是还是没能解决问题,... 查看详情

linuxmipidsi驱动调试笔记-设备树dcs格式序列之配置lcd初始化代码(代码片段)

...IPILCD初始化序列描述在上一篇文章中,我们看到荣品的屏设备树中有这么一段代码,但是里面的数据是什么含义呢?panel-init-sequence = [   05 78 01 11   05 78 01 29  ];今天来详... 查看详情

linux驱动开发:framebuffe(lcd)驱动开发(代码片段)

一、FrameBuffer帧缓冲设备的原理1.1概念在linux系统中LCD这类设备称为帧缓冲设备,英文frameBuffer设备。frameBuffer是出现在2.2.xx内核当中的一种驱动程序接口。帧缓冲(framebuffer)是Linux系统为显示设备提供的一个接口࿰... 查看详情

linux驱动开发lcd(代码片段)

...有跟显示有关的硬件和软件集合起来,虚拟出一个fb设备,LCD驱动生成/dev/fbx的设备,应用程序通过访问/dev/fbx设备即可以访问LCD。不同分辨率的LCD屏幕eLCDIF控制器驱动代码相同,只需要修改好对应的屏幕参数。Linu... 查看详情

linux设备树(代码片段)

目录什么是设备树DTS、DTB和DTCDTS语法.dtsi头文件设备节点标准属性根节点compatible属性向节点追加或修改内容创建小型模板设备树设备树在系统中的体现特殊节点aliases子节点chosen子节点Linux内核解析DTB文件绑定信息文档设备树常用... 查看详情

linuxmipidsi驱动调试笔记-设备树dcs格式序列之配置lcd初始化代码(代码片段)

...往里钻的话还是很难的,文章如下:LinuxMIPIDSILCD设备驱动开发调试细节学习笔记(一)1、MIPILCD 查看详情

linux设备树语法详解(代码片段)

Linux内核从3.x开始引入设备树的概念,用于实现驱动代码与设备信息相分离。在设备树出现以前,所有关于设备的具体信息都要写在驱动里,一旦外围设备变化,驱动代码就要重写。引入了设备树之后,驱动代... 查看详情

linux设备树——设备树格式和使用(代码片段)

一、设备树dts文件的语法规范 1.DTS文件布局(layout)/dts-v1/;[memoryreservations]//格式为:/memreserve/<address><length>;/[propertydefinitions][childnodes];(1)特殊的、默认的属性a.根节点的:#address-cells//在它的子节点的reg属性中 查看详情

基于瑞芯微rv1109linux触摸屏gt911驱动调试心得(代码片段)

1、确定I2C地址1.1、使用i2cdetect工具查看系统i2c节点的情况很明显这里可以看到系统已经配置了i2c-0、i2c-1、i2c-3、i2c-4、i2c-5,我们可以看下原厂在设备树里面的支持情况:gedit kernel/arch/arm/boot/dts/rv1126.dtsi &打开后我们可... 查看详情

修改飞凌ok6410的默认lcd显示设备(代码片段)

...烧入系统(Wince6.0系统)后,默认是采用4.3寸触摸屏显示,而我的项目中采用的是HDMI高清显示,因此每次烧完系统后,都要重新用DNW来配置为高清显示驱动,比较麻烦,所以我打算修改一下eboot的程... 查看详情

t5设备树(代码片段)

1.设备树1.1设备树引入背景Linux社区的大牛们参考了PowerPC等体系架构中使用的FlattenedDeviceTree(FDT),也采用了DeviceTree结构,许多硬件的细节可以直接透过它传递给Linux,而不再需要在kernel中进行大量的冗余编码设备树是一种描述... 查看详情

linux驱动|从0写一个设备树节点实例(代码片段)

一、前言设备树是每一个Linux驱动工程师都必须掌握的一个知识点,有很多之前做单片机的朋友刚接触Linux驱动时,会一脸懵!其实设备树的使用并没有大家想像的那么复杂,对于大部分工程师来说,只要会修... 查看详情

基于瑞芯微rv1109linux触摸屏gt911驱动调试心得-设备树刷厂商给的触摸屏固件...

之前调试触摸屏的文章如下:基于瑞芯微RV1109Linux触摸屏GT911驱动调试心得_Bruce.yang的嵌入式之旅-CSDN博客由于之前调触摸屏一直都对不上点,然后厂商又给了我新的驱动代码。但是还是没能解决问题,所以我初步怀疑是固件问题... 查看详情

linux设备树语法规范(代码片段)

设备树是一种描述硬件的数据结构,它起源于OpenFirmware(OF)。在Linux2.6中,ARM架构的板极硬件细节过多地被硬编码在arch/arm/plat-xxx和arch/arm/mach-xxx中,采用设备树后,许多硬件的细节可以直接通过它传递给L... 查看详情

[架构之路-34]:目标系统-系统软件-linuxos硬件电路的文本描述:设备树的构成属性解析使用(代码片段)

...1.2无处不在的树形结构1.3树形结构的好处第2章硬件系统设备树2.1什么是硬件设备树2.2Linux引入设备树的好处2.3Linux什么时候引入设备树与设备2.4Linux设备树的外观第3章Linux设备树的工作原理3.1Linux内核驱动的设备树3.2硬件电路设备... 查看详情

linuxinput子系统实验(代码片段)

...序源码简析自带按键驱动程序的使用按键、鼠标、键盘、触摸屏等都属于输入(input)设备,Linux内核为此专门做了一个叫做input 查看详情

i.mx6ull驱动开发|04-linux设备树基本语法与实例解析(代码片段)

文章目录一、设备树简介1.设备树在ARM架构的引入2.什么是设备树3.DTS、DTSI、DTB4.设备树编译二、设备树语法1.设备树版本2.设备树节点3.设备树节点标准属性4.特殊节点5.向节点追加内容三、设备树实例i.MX6ULL内部框图如何寻找开发... 查看详情