fs_s5pc_drv

 

s5pc_ts.h:

#ifndef __ASM_ARM_TS_H
#define __ASM_ARM_TS_H

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

#define AUTOPST (S3C2410_ADCTSC_AUTO_PST |\
S3C2410_ADCTSC_XY_PST(0))

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

struct s5pc_ts_mach_info{
int delay; //延迟时间
int presc; //ADC时钟频率的预分频比
int oversampling_shift; //采用次数
};

//extern static void s5pc_set_ts_platdata(struct s5pc_ts_mach_info *pd);

#endif

 

 s5pc_fs_dev.c

#include<linux/init.h>
#include<linux/module.h>
#include<linux/platform_device.h>
#include<linux/ioport.h>
#include<mach/irqs.h>
#include <mach/map.h>
#include "s5pc_ts.h"

static struct s5pc_ts_mach_info mach_cfgts ={
.delay =20000,
.presc =49,
};

static struct resource s5pc_ts_resource[]={
[0] ={
.start =SAMSUNG_PA_ADC,
.end =SAMSUNG_PA_ADC + SZ_256 - 1,
.flags =IORESOURCE_MEM,
},
[1]={
.start =IRQ_TC,
.end =IRQ_TC,
.flags =IORESOURCE_IRQ,
},
};

static void s5pc_release(struct device *dev)
{

}

static struct platform_device s5pc_ts_device={
.name ="s5pc-ts",
.id =-1,
.resource =s5pc_ts_resource,
.num_resources =ARRAY_SIZE(s5pc_ts_resource),
.dev ={
.release =s5pc_release,
},
};

static void s5pc_set_ts_platdata(struct s5pc_ts_mach_info *pd)
{
struct s5pc_ts_mach_info *npd;

npd = kmemdup(pd, sizeof(struct s5pc_ts_mach_info), GFP_KERNEL);
if (!npd)
printk(KERN_ERR "%s: no memory for platform data\n", __func__);

s5pc_ts_device.dev.platform_data = npd;
}

static int __init s5pc_ts_dev_init(void)
{
s5pc_set_ts_platdata(&mach_cfgts);
platform_device_register(&s5pc_ts_device);
return 0;
}

static void __exit s5pc_ts_dev_exit(void)
{
platform_device_unregister(&s5pc_ts_device);
}
module_init(s5pc_ts_dev_init);
module_exit(s5pc_ts_dev_exit);
MODULE_LICENSE("GPL");

 

 

s5pc_drv.c:

#include<linux/init.h>
#include<linux/module.h>
#include<linux/platform_device.h>
#include<linux/irqreturn.h>
#include<linux/slab.h>
#include<linux/input.h>
#include<asm/io.h>
#include<linux/interrupt.h>
#include<plat/regs-adc.h>
#include<plat/adc.h>
#include<linux/clk.h>
#include"s5pc_ts.h"

struct ts_dev{
struct s3c_adc_client *client;
struct input_dev *input_dev;
struct clk *ts_clk;
void __iomem *s5pc_ts_base;
unsigned int ts_irq;
int shift;
unsigned long xp;
unsigned long yp;
unsigned int count;
};

struct ts_dev *s5pc_ts_dev;

/* get_down - return the down state of the pen
* @data0: The data read from ADCDAT0 register.
* @data1: The data read from ADCDAT1 register.
* 如果pen按下,返回的是非0值
* Return non-zero if both readings show that the pen is down.
*/
static inline bool get_down(unsigned long data0, unsigned long data1)
{
/* returns true if both data values show stylus down */
return (!(data0 & S3C2410_ADCDAT0_UPDOWN) &&
!(data1 & S3C2410_ADCDAT0_UPDOWN));
}

static void s5pc_timer_fire(unsigned long data)
{
unsigned long data0;
unsigned long data1;
bool down;

data0 = readl(s5pc_ts_dev->s5pc_ts_base + S3C2410_ADCDAT0);
data1 = readl(s5pc_ts_dev->s5pc_ts_base + S3C2410_ADCDAT1);

down = get_down(data0, data1);

if(down){
/* 定时器时间到,而且转换次数跟预定的一样,则说明:
* 为长按或滑动
*/
if(s5pc_ts_dev->count==(1 << s5pc_ts_dev->shift)){
s5pc_ts_dev->xp >>=(s5pc_ts_dev->shift);
s5pc_ts_dev->yp >>=(s5pc_ts_dev->shift);
printk(KERN_INFO "%s: X=%lu, Y=%lu, count=%d\n",
__func__, s5pc_ts_dev->xp, s5pc_ts_dev->yp,
s5pc_ts_dev->count);

/* 上报事件 */
input_report_abs(s5pc_ts_dev->input_dev, ABS_X, s5pc_ts_dev->xp);
input_report_abs(s5pc_ts_dev->input_dev, ABS_Y, s5pc_ts_dev->yp);

input_report_key(s5pc_ts_dev->input_dev, BTN_TOUCH, 1);
input_report_abs(s5pc_ts_dev->input_dev, ABS_PRESSURE, 1);
input_sync(s5pc_ts_dev->input_dev);

/* 还原所有的计数状态 */
s5pc_ts_dev->xp = 0;
s5pc_ts_dev->yp = 0;
s5pc_ts_dev->count = 0;
}
s3c_adc_start(s5pc_ts_dev->client, 0, 1 << s5pc_ts_dev->shift);
}
else{
s5pc_ts_dev->xp = 0;
s5pc_ts_dev->yp = 0;
s5pc_ts_dev->count = 0;
input_report_key(s5pc_ts_dev->input_dev, BTN_TOUCH, 0);
input_report_abs(s5pc_ts_dev->input_dev, ABS_PRESSURE, 0);
input_sync(s5pc_ts_dev->input_dev);
writel(WAITTOINT | INT_DOWN, s5pc_ts_dev->s5pc_ts_base+ S3C2410_ADCTSC);
}
}

static DEFINE_TIMER(s5pc_timer, s5pc_timer_fire, 0, 0);

/* 进入等待触摸笔按下模式 */
static void enter_to_down_mode(void)
{
writel(WAITTOINT | INT_DOWN,s5pc_ts_dev->s5pc_ts_base + S3C2410_ADCTSC);
}

/* 进入等待触摸笔松开模式 */
static void enter_to_up_mode(void)
{
writel(WAITTOINT | INT_UP,s5pc_ts_dev->s5pc_ts_base + S3C2410_ADCTSC);
}

static void clear_pen_interrupt(void)
{
/* Clear pen down/up interrupt */
writel(0x0, s5pc_ts_dev->s5pc_ts_base + S3C64XX_ADCCLRINTPNDNUP);
}

static void s5pc_ts_select(struct s3c_adc_client *client,unsigned int selected)
{
if (selected)
{
/* 设置为自动X/Y坐标转换模式 */
writel(S3C2410_ADCTSC_PULL_UP_DISABLE | AUTOPST,s5pc_ts_dev->s5pc_ts_base + S3C2410_ADCTSC);
}

else
{

mod_timer(&s5pc_timer, jiffies+1);
enter_to_up_mode();
//writel(WAITTOINT | INT_UP, s5pc_ts_dev->s5pc_ts_base + S3C2410_ADCTSC);
}
}

/* s3c24xx_ts_conversion - ADC conversion callback
* @client: The client that was registered with the ADC core.
* @data0: The reading from ADCDAT0.
* @data1: The reading from ADCDAT1.
* @left: The number of samples left.
* Called when a conversion has finished.
*/
static void s5pc_ts_conversion(struct s3c_adc_client *client,unsigned data0, unsigned data1,unsigned *left)
{
//printk(KERN_INFO "%s(),%d,%d,",__func__,data0,data1);
s5pc_ts_dev->xp += data0;
s5pc_ts_dev->yp += data1;
/* 转换次数加加 */
s5pc_ts_dev->count++;
}

static irqreturn_t s5pc_ts_isr(int irq, void *dev_id)
{
struct ts_dev *ts_dev =(struct ts_dev *)dev_id;

unsigned long data0;
unsigned long data1;
bool down;

data0 =readl(ts_dev->s5pc_ts_base + S3C2410_ADCDAT0);
data1 =readl(ts_dev->s5pc_ts_base + S3C2410_ADCDAT1);

down = get_down(data0, data1);

if (down)
{
/* 按下 */
//printk(KERN_INFO "%s(),pen is down!\n",__func__);
//enter_to_up_mode(ts_dev);
//第二个参数代表使用的ADC通道,第三个参数指定采样次数,
s3c_adc_start(ts_dev->client, 0, 1 << ts_dev->shift);
}
else
{
/* 松开 */
//printk(KERN_INFO "%s(),pen is up!\n",__func__);
enter_to_down_mode();
}

clear_pen_interrupt();

return IRQ_HANDLED;
}

static int s5pc_ts_probe(struct platform_device *pdev)
{
int ret =0;
struct resource *res;
unsigned long irqflags;
struct s5pc_ts_mach_info *ts_mach_info;
unsigned int ts_delay;

/* 1. 创建一个struct input_dev */
ts_mach_info =pdev->dev.platform_data;

/* 2. 分配空间 */
s5pc_ts_dev =kzalloc(sizeof(struct ts_dev), GFP_KERNEL);
if(s5pc_ts_dev ==NULL){
dev_err(&pdev->dev,"unable to allocate for s5pc_ts_dev!\n");
ret =-ENOMEM;
goto err_alloc;
}

s5pc_ts_dev->input_dev =input_allocate_device();
if(!s5pc_ts_dev->input_dev){
dev_err(&pdev->dev, "unable to allocate input device !\n");
ret =-ENOMEM;
goto err_input_device;
}

s5pc_ts_dev->shift =ts_mach_info->oversampling_shift;

/* 3. 设置input_dev结构体能产生哪类事件及哪些事件 */
s5pc_ts_dev->input_dev->evbit[0]=BIT_MASK(EV_KEY) | BIT_MASK(EV_ABS);
s5pc_ts_dev->input_dev->keybit[BIT_WORD(BTN_TOUCH)] = BIT_MASK(BTN_TOUCH);
input_set_abs_params(s5pc_ts_dev->input_dev, ABS_X, 0, 0x3FF, 0, 0);
input_set_abs_params(s5pc_ts_dev->input_dev, ABS_Y, 0, 0x3FF, 0, 0);
input_set_abs_params(s5pc_ts_dev->input_dev, ABS_PRESSURE, 0, 1, 0, 0);

s5pc_ts_dev->input_dev->name = "S5PC100 TouchScreen";

/* 4. 使能时钟 */
s5pc_ts_dev->ts_clk =clk_get(&pdev->dev,"adc");
if(IS_ERR(s5pc_ts_dev->ts_clk)){
dev_err(&pdev->dev, "unable to get adc clk source!\n");
ret =-EBUSY;
goto err_get_clk;
}

clk_enable(s5pc_ts_dev->ts_clk);

/* 5. 获取资源,I/O内存资源,中断资源 */
res =platform_get_resource(pdev, IORESOURCE_MEM, 0);
if(res ==NULL){
dev_err(&pdev->dev,"unable to get memory resource!\n");
ret =-ENOSPC;
goto err_get_resource;
}

s5pc_ts_dev->s5pc_ts_base =ioremap(res->start, res->end-res->start+1);
if(!s5pc_ts_dev->s5pc_ts_base){
dev_err(&pdev->dev,"unable to mmap iomemory!\n");
ret =-ENOMEM;
goto err_mmap;
}

res =platform_get_resource(pdev, IORESOURCE_IRQ, 0);
if(res ==NULL){
dev_err(&pdev->dev,"unable to get irq resource!\n");
goto err_get_resource;
}
s5pc_ts_dev->ts_irq =res->start;

irqflags =IRQF_TRIGGER_FALLING | IRQF_TRIGGER_RISING;

/* 5.1 注册中断 */
ret =request_irq(s5pc_ts_dev->ts_irq, s5pc_ts_isr, irqflags,
"s5pc_ts", s5pc_ts_dev);
if(ret){
dev_err(&pdev->dev,"cannot request ts irq!\n");
goto err_irq;
}

/* 6. 申请ADC资源 */
s5pc_ts_dev->client=s3c_adc_register(pdev, s5pc_ts_select, s5pc_ts_conversion, 1);
if (IS_ERR(s5pc_ts_dev->client)) {
dev_err(&pdev->dev, "failed to register adc client\n");
ret = PTR_ERR(s5pc_ts_dev->client);
goto err_iomap;
}

/* 7. 硬件相关的操作 */
/* 7.1 配置ADCDLY,配置延迟时间,
* 触摸笔按下后等多长时间向CPU产生一个中断
*/
ts_delay =ts_mach_info->delay;
writel(ts_delay & 0xffff, s5pc_ts_dev->s5pc_ts_base + S3C2410_ADCDLY);

/* 7.2 配置ADCTSC,配置触摸屏控制寄存器,设置它为等待中断模式 */
enter_to_down_mode();

/* 8. 注册input_dev结构体 */
ret = input_register_device(s5pc_ts_dev->input_dev);
if (ret < 0) {
dev_err(&pdev->dev, "failed to register input device\n");
ret = -EIO;
goto err_tcirq;
}

platform_set_drvdata(pdev, s5pc_ts_dev);

return 0;

err_tcirq:
err_iomap:
free_irq(s5pc_ts_dev->ts_irq, s5pc_ts_dev);
err_irq:
iounmap(s5pc_ts_dev->s5pc_ts_base);
err_mmap:
err_get_resource:
clk_put(s5pc_ts_dev->ts_clk);
err_get_clk:
input_free_device(s5pc_ts_dev->input_dev);
err_input_device:
kfree(s5pc_ts_dev);
err_alloc:
return ret;

}

static int s5pc_ts_remove(struct platform_device *pdev)
{
struct ts_dev *s5pc_tsdev =platform_get_drvdata(pdev);
//input_free_device(s5pc_tsdev->input_dev);
clk_put(s5pc_tsdev->ts_clk);
iounmap(s5pc_tsdev->s5pc_ts_base);
free_irq(s5pc_tsdev->ts_irq, s5pc_tsdev);
kfree(s5pc_tsdev);
input_unregister_device(s5pc_tsdev->input_dev);
return 0;
}

static struct platform_driver s5pc_ts_driver={
.probe =s5pc_ts_probe,
.remove =s5pc_ts_remove,
.driver ={
.owner =THIS_MODULE,
.name ="s5pc-ts",
},
};

static int __init s5pc_ts_drv_init(void)
{
platform_driver_register(&s5pc_ts_driver);
return 0;
}

static void __exit s5pc_ts_drv_exit(void)
{
platform_driver_unregister(&s5pc_ts_driver);
}

module_init(s5pc_ts_drv_init);
module_exit(s5pc_ts_drv_exit);
MODULE_LICENSE("GPL");

 

Makefile:

ifeq ($(KERNELRELEASE),)

KERNELDIR ?=/home/farsight/Linux_source/linux-2.6.35.5

PWD := $(shell pwd)

modules:
$(MAKE) -C $(KERNELDIR) M=$(PWD) modules
cp s5pc_ts_drv.ko /opt/rootfs/s5pc100
cp s5pc_ts_dev.ko /opt/rootfs/s5pc100

modules_install:
$(MAKE) -C $(KERNELDIR) M=$(PWD) modules_install

 

clean:
rm -rf *.o *~ core .depend .*.cmd *.ko *.mod.c .tmp_versions Module* module* test

.PHONY: modules modules_install clean

else
obj-m :=s5pc_ts_drv.o s5pc_ts_dev.o
endif

 

 

posted on 2013-12-26 22:00  weat  阅读(331)  评论(0编辑  收藏  举报