I2C 장치 구동 분석 - imx 6 에서 tsc 2007 저항 터치 스크린 구동 기반
9379 단어 Linux 커 널 구동
1. 주요 데이터 구조 설명
1.of_device_id (include/linux/mod_devicetable.h)
/* */
struct of_device_id {
char name[32];
char type[32];
char compatible[128];
const void *data;
};
2. struct device_driver (include/linux/device.h)
struct device_driver {
/* */
const char *name;
/* device bus */
struct bus_type *bus;
/* THIS_MODULE */
struct module *owner;
const char *mod_name; /* used for built-in modules */
bool suppress_bind_attrs; /* disables bind/unbind via sysfs */
/* , */
const struct of_device_id *of_match_table;
const struct acpi_device_id *acpi_match_table;
int (*probe) (struct device *dev);
int (*remove) (struct device *dev);
void (*shutdown) (struct device *dev);
int (*suspend) (struct device *dev, pm_message_t state);
int (*resume) (struct device *dev);
const struct attribute_group **groups;
const struct dev_pm_ops *pm;
/* */
struct driver_private *p;
}
3. struct i2c_driver (include/linux/i2c.h)
i2c 장치 구동 방법 을 묘사 하 였 다.
struct i2c_driver {
unsigned int class;
/* attach_adapter */
int (*attach_adapter)(struct i2c_adapter *) __deprecated;
/* device driver */
int (*probe)(struct i2c_client *, const struct i2c_device_id *);
int (*remove)(struct i2c_client *);
void (*shutdown)(struct i2c_client *);
void (*alert)(struct i2c_client *, unsigned int data);
int (*command)(struct i2c_client *client, unsigned int cmd, void *arg);
struct device_driver driver;
/* ID */
const struct i2c_device_id *id_table;
int (*detect)(struct i2c_client *, struct i2c_board_info *);
const unsigned short *address_list;
struct list_head clients;
};
4. struct i2c_client() (include/linux/i2c.h)
이름, 주소 등 을 포함 한 실제 i2c 장 치 를 설명 합 니 다.
struct i2c_client {
unsigned short flags; /* div., see below */
unsigned short addr; /* chip address - NOTE: 7bit */
/* addresses are stored in the */
/* _LOWER_ 7 bits */
char name[I2C_NAME_SIZE];
struct i2c_adapter *adapter; /* the adapter we sit on */
struct device dev; /* the device structure */
int irq; /* irq issued by device */
struct list_head detected;
#if IS_ENABLED(CONFIG_I2C_SLAVE)
i2c_slave_cb_t slave_cb; /* callback for slave mode */
#endif
}
5. struct tsc2007
struct tsc2007 {
/* input_dev */
struct input_dev *input;
char phys[32];
/* */
struct i2c_client *client;
u16 model;
u16 x_plate_ohms;
/* */
u16 max_rt;
unsigned long poll_period; /* in jiffies */
int fuzzx;
int fuzzy;
int fuzzz;
unsigned gpio;
/* */
int irq;
/* */
wait_queue_head_t wait;
/* */
bool stopped;
/* */
int (*get_pendown_state)(struct device *);
};
2. 주요 함수 설명
1. probe 함수
probe 함 수 는 device 와 driver 가 일치 할 때 실 행 됩 니 다. 주로 구동 에 필요 한 메모리, 인 터 럽 트 등 자원 을 신청 하고 구동 관련 구조 체 구성원 을 초기 화 하 며 마지막 으로 input 을 신청 하고 등록 합 니 다.dev 장치.
/* */
static int tsc2007_probe_dt(struct i2c_client *client, struct tsc2007 *ts)
{
struct device_node *np = client->dev.of_node;
u32 val32;
u64 val64;
if (!np)
return -EINVAL;
if (!of_property_read_u32(np, "ti,max-rt", &val32))
ts->max_rt = val32;
else
ts->max_rt = MAX_12BIT;
if (!of_property_read_u32(np, "ti,fuzzx", &val32))
ts->fuzzx = val32;
if (!of_property_read_u32(np, "ti,fuzzy", &val32))
ts->fuzzy = val32;
if (!of_property_read_u32(np, "ti,fuzzz", &val32))
ts->fuzzz = val32;
if (!of_property_read_u64(np, "ti,poll-period", &val64))
ts->poll_period = msecs_to_jiffies(val64);
else
ts->poll_period = msecs_to_jiffies(1);
if (!of_property_read_u32(np, "ti,x-plate-ohms", &val32))
ts->x_plate_ohms = val32;
else
return -EINVAL;
/* GPIO */
ts->gpio = of_get_gpio(np, 0);
if (gpio_is_valid(ts->gpio))
ts->get_pendown_state = tsc2007_get_pendown_state_gpio;
return 0;
}
/* probe function */
static int tsc2007_probe(struct i2c_client *client,
const struct i2c_device_id *id)
{
struct tsc2007 *ts;
struct input_dev *input_dev;
int err;
/* ts ,devm_kzalloc */
ts = devm_kzalloc(&client->dev, sizeof(struct tsc2007), GFP_KERNEL);
if (!ts)
return -ENOMEM;
/* ts */
err = tsc2007_probe_dt(client, ts);
if (err)
return err;
/* input_dev */
input_dev = devm_input_allocate_device(&client->dev);
if (!input_dev)
return -ENOMEM;
i2c_set_clientdata(client, ts);
ts->client = client;
ts->irq = client->irq;
ts->input = input_dev;
/* */
init_waitqueue_head(&ts->wait);
input_dev->name = "TSC2007 TouchScreen";
input_dev->phys = ts->phys;
input_dev->id.bustype = BUS_I2C;
input_dev->open = tsc2007_open;
input_dev->close = tsc2007_close;
input_set_drvdata(input_dev, ts);
//
input_dev->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_ABS);
//
input_dev->keybit[BIT_WORD(BTN_TOUCH)] = BIT_MASK(BTN_TOUCH);
/* */
/* , */
input_set_abs_params(input_dev, ABS_X, 0, MAX_12BIT, ts->fuzzx, 0);
input_set_abs_params(input_dev, ABS_Y, 0, MAX_12BIT, ts->fuzzy, 0);
input_set_abs_params(input_dev, ABS_PRESSURE, 0, MAX_12BIT, ts->fuzzz, 0);
/* */
err = devm_request_threaded_irq(&client->dev, ts->irq, tsc2007_hard_irq,
tsc2007_soft_irq, IRQF_ONESHOT,
client->dev.driver->name, ts);
if (err)
return err;
tsc2007_stop(ts);
/* input */
err = input_register_device(input_dev);
if (err)
return err;
return 0;
}
2. open, close, stop 함수
open 과 close 함 수 는 구동 의 열 림 과 닫 기 를 책임 집 니 다.
/* open */
static int tsc2007_open(struct input_dev *input_dev)
{
struct tsc2007 *ts = input_get_drvdata(input_dev);
int err;
/* */
ts->stopped = false;
mb();
/* */
enable_irq(ts->irq);
/* tsc2007 , */
err = tsc2007_xfer(ts, PWRDOWN);
if (err < 0) {
tsc2007_stop(ts);
return err;
}
return 0;
}
/* close */
static void tsc2007_close(struct input_dev *input_dev)
{
struct tsc2007 *ts = input_get_drvdata(input_dev);
tsc2007_stop(ts);
}
/* stop */
static void tsc2007_stop(struct tsc2007 *ts)
{
/* */
ts->stopped = true;
mb();
wake_up(&ts->wait);
/* */
disable_irq(ts->irq);
}
3. 소프트 인 터 럽 트 와 하 드 인 터 럽 트
터치 가 눌 렸 을 때 외부 에 들 어가 하 드 인 터 럽 트 를 중단 하고 하 드 인 터 럽 트 에서 소프트 인 터 럽 트 를 활성화 하 며 ad 값 을 읽 고 보고 하 는 등 소프트 인 터 럽 트 에서 진행 합 니 다.
/* */
static irqreturn_t tsc2007_soft_irq(int irq, void *handler)
{
struct tsc2007 *ts = handler;
struct input_dev *input = ts->input;
struct ts_event tc;
u32 rt;
/* stop , */
while (!ts->stopped && tsc2007_is_pen_down(ts)) {
/* tsc2007 */
tsc2007_read_values(ts, &tc);
/* */
rt = tsc2007_calculate_pressure(ts, &tc);
if (!rt && !ts->get_pendown_state)
break;
if (rt <= ts->max_rt) {
input_report_key(input, BTN_TOUCH, 1);
input_report_abs(input, ABS_X, tc.x);
input_report_abs(input, ABS_Y, tc.y);
input_report_abs(input, ABS_PRESSURE, rt);
input_sync(input);
}
/* */
wait_event_timeout(ts->wait, ts->stopped, ts->poll_period);
}
input_report_key(input, BTN_TOUCH, 0);
input_report_abs(input, ABS_PRESSURE, 0);
input_sync(input);
return IRQ_HANDLED;
}
/* */
static irqreturn_t tsc2007_hard_irq(int irq, void *handle)
{
struct tsc2007 *ts = handle;
/* */
if (tsc2007_is_pen_down(ts))
return IRQ_WAKE_THREAD;
return IRQ_HANDLED;
}
4. 계산 압력
/* */
static u32 tsc2007_calculate_pressure(struct tsc2007 *tsc, struct ts_event *tc)
{
u32 rt = 0;
if (tc->x == MAX_12BIT)
tc->x = 0;
if (likely(tc->x && tc->z1)) {
rt = tc->z2 - tc->z1;
rt *= tc->x;
rt *= tsc->x_plate_ohms;
rt /= tc->z1;
rt = (rt + 2047) >> 12;
}
return rt;
}
5. I2C 로 좌표 읽 기
/* i2c */
static inline int tsc2007_xfer(struct tsc2007 *tsc, u8 cmd)
{
s32 data;
u16 val;
data = i2c_smbus_read_word_data(tsc->client, cmd);
if (data < 0) {
return data;
}
val = swab16(data) >> 4;
return val;
}
/* tsc2007 */
static void tsc2007_read_values(struct tsc2007 *tsc, struct ts_event *tc)
{
tc->y = tsc2007_xfer(tsc, READ_Y);
tc->x = tsc2007_xfer(tsc, READ_X);
tc->z1 = tsc2007_xfer(tsc, READ_Z1);
tc->z2 = tsc2007_xfer(tsc, READ_Z2);
tsc2007_xfer(tsc, PWRDOWN);
}
6. 터치 스크린 을 눌 렀 는 지 확인
/* */
static int tsc2007_get_pendown_state_gpio(struct device *dev)
{
struct i2c_client *client = to_i2c_client(dev);
struct tsc2007 *ts = i2c_get_clientdata(client);
/* 1 */
return !gpio_get_value(ts->gpio);
}
/* */
static bool tsc2007_is_pen_down(struct tsc2007 *ts)
{
/* , , true */
if(!ts->get_pendown_state)
return true;
else
/* */
return ts->get_pendown_state(&ts->client->dev);
}