瑞芯微Rockchip开发者社区
直播中

莫联雪

7年用户 1075经验值
私信 关注
[问答]

RK3288的ADC按键驱动怎么匹配的呢

在DTS文件中如何去添加自己ADC的资源呢?
RK3288的ADC按键驱动怎么匹配的呢?

回帖(1)

谷雪阳

2022-3-4 11:07:21
  1、DTS修改
  DTS 节点在 kernel/arch/arm64/boot/dts/rockchip/rk3288.dtsi 文件中定义,如下所示:
  saradc: saradc@ff100000 {
  compatible = "rockchip,saradc";
  reg = 《0x0 0xff100000 0x0 0x100》;
  interrupts = 《GIC_SPI 36 IRQ_TYPE_LEVEL_HIGH》;
  #io-channel-cells = 《1》;
  clocks = 《&cru SCLK_SARADC》, 《&cru PCLK_SARADC》;
  clock-names = "saradc", "apb_pclk";
  resets = 《&cru SRST_SARADC》;
  reset-names = "saradc-apb";
  status = "disabled";
  };
  在DTS文件中添加自己ADC的资源描述:
  rk_key{
  compatible = "rockchip,key";//"rockchip,key"该名字用来匹配rk_key.c这个驱动
  status = "okay";
  io-channels = 《&saradc 1》;//根据硬件来选择adc 0/1/2
  vol-up-key {//添加子节点
  linux,code = 《114》;//驱动要上报的按键code
  label = "volume up";
  rockchip,adc_value = 《1》;//按键的adc对应电压值
  };
  vol-down-key {
  linux,code = 《115》;
  label = "volume down";
  rockchip,adc_value = 《170》;
  };
  power-key {
  //gpios = 《&gpio0 5 GPIO_ACTIVE_LOW》;
  linux,code = 《116》;
  label = "power";
  gpio-key,wakeup;
  rockchip,adc_value = 《509》;
  };
  menu-key {
  linux,code = 《59》;
  label = "menu";
  rockchip,adc_value = 《609》;
  };
  home-key {
  linux,code = 《102》;
  label = "home";
  rockchip,adc_value = 《842》;
  };
  back-key {
  linux,code = 《158》;
  label = "back";
  rockchip,adc_value = 《973》;
  };
  };
  2、匹配驱动:
  对应匹配的驱动:kerneldriversinputkeyboardrk_key.c
  怎么匹配的呢??看驱动的这个函数:
  static const struct of_device_id rk_key_match[] = {
  { .compatible = "rockchip,key", .data = NULL},
  {},
  };
  带有of_xxxxx开头的函数一般是用来解析dts的资源的,比如上面的这个函数中的.compatible = "rockchip,key"就是和dts匹配的,驱动的 "rockchip,key"与 "rockchip,key"名字是一样的,具体的怎么匹配的,怎么实现的不用太深入,知道这个是用来匹配的就好。
  2.1、驱动中解析dts设定的adc电压值的函数:
  注:rk_key_type_get函数可判断是io口还是adc按键
  static int rk_key_type_get(struct device_node *node,
  struct rk_keys_button *button)
  {
  u32 adc_value;
  if (!of_property_read_u32(node, "rockchip,adc_value", &adc_value))
  return TYPE_ADC;
  else if (of_get_gpio(node, 0) 》= 0)
  return TYPE_GPIO;
  else
  return -1;
  }
  /*************************************************************/
  》 of_property_read_u32(node, "rockchip,adc_value", &adc_value)
  》参数node:对于dts的节点
  》参数 "rockchip,adc_value":进行匹配
  》参数 adc_value:将dts设定的按键电压值赋给参数adc_value
  /*************************************************************/
  驱动中还有:of_property_read_u32(child_node, "linux,code", &code))、 of_get_property(child_node, "label", NULL);等是同一个道理分析的。
  2.2、获取ADC通道
  获取对应的通道 struct iio_channel *chan;
  定义 IIO 通道结构体chan = iio_channel_get(&pdev-》dev, NULL);
  获取 IIO 通道结构体
  static int rk_keys_parse_dt(struct rk_keys_drvdata *pdata,
  struct platform_device *pdev)
  {
  struct device_node *node = pdev-》dev.of_node;
  struct device_node *child_node;
  struct iio_channel *chan;
  int ret, gpio, i = 0;
  u32 code, adc_value, flags, drift;
  if (of_property_read_u32(node, "adc-drift", &drift))
  pdata-》drift_advalue = DRIFT_DEFAULT_ADVALUE;
  else
  pdata-》drift_advalue = (int)drift;
  chan = iio_channel_get(&pdev-》dev, NULL);
  if (IS_ERR(chan)) {
  dev_info(&pdev-》dev, "no io-channels definedn");
  chan = NULL;
  }
  2.3、获取ADC值
  在adc_key polling调用了iio_read_channel_raw()函数读取 AD 采集的原始数据,并存入 val
  static int rk_key_adc_iio_read(struct rk_keys_drvdata *data)
  {
  struct iio_channel *channel = data-》chan;
  int val, ret;
  if (!channel)
  return INVALID_ADVALUE;
  ret = iio_read_channel_raw(channel, &val);
  //printk("val=%dn",val);//打印按键的实际电压值
  if (ret 《 0) {
  pr_err("read channel() error: %dn", ret);
  return ret;
  }
  return val;
  }
  3、驱动是通过内核input子系统来将keys注册供用户空间使用
  static int keys_probe(struct platform_device *pdev)
  {
  ***
  input = devm_input_allocate_device(dev);
  input-》name = "rk29-keypad"; /* pdev-》name; */
  input-》phys = "gpio-keys/input0";
  input-》dev.parent = dev;
  input-》id.bustype = BUS_HOST;
  input-》id.vendor = 0x0001;
  input-》id.product = 0x0001;
  input-》id.version = 0x0100;
  ***
  for (i = 0; i 《 ddata-》nbuttons; i++) {
  struct rk_keys_button *button = &ddata-》button;
  if (button-》type == TYPE_GPIO) {
  int irq;
  //为io口申请这里的type即为TYPE_GPIO 而不是adc
  error =
  devm_gpio_request(dev, button-》gpio,
  button-》desc ? : "keys");
  if (error 《 0) {
  pr_err("gpio-keys: failed to request GPIO %d, error %dn",
  button-》gpio, error);
  goto fail1;
  }
  //设置为输入
  error = gpio_direction_input(button-》gpio);
  if (error 《 0) {
  pr_err("gpio-keys: failed to configure input direction for GPIO %d, error %dn",
  button-》gpio, error);
  gpio_free(button-》gpio);
  goto fail1;
  }
  irq = gpio_to_irq(button-》gpio);
  if (irq 《 0) {
  error = irq;
  pr_err("gpio-keys: Unable to get irq number for GPIO %d, error %dn",
  button-》gpio, error);
  gpio_free(button-》gpio);
  goto fail1;
  }
  //申请中断
  error = devm_request_irq(dev, irq, keys_isr,
  button-》active_low ?
  IRQF_TRIGGER_FALLING :
  IRQF_TRIGGER_RISING,
  button-》desc ?
  button-》desc : "keys",
  button);
  if (error) {
  pr_err("gpio-keys: Unable to claim irq %d; error %dn",
  irq, error);
  gpio_free(button-》gpio);
  goto fail1;
  }
  }
  }
  ***
  }
  4、rk_keys_parse_dt()实现
  每个key都会注册一个定时器函数来处理状态变化并通知用户空间。
  for (i = 0; i 《 ddata-》nbuttons; i++) {
  if (button-》code) {
  setup_timer(&button-》timer,
  keys_timer, (unsigned long)button);
  }
  }
  keys_timer():
  static void keys_timer(unsigned long _data)
  {
  //普通gpio直接读取
  if (button-》type == TYPE_GPIO)
  state = !!((gpio_get_value(button-》gpio) ? 1 : 0) ^
  button-》active_low);
  else
  //adc转成bool状态值
  state = !!button-》adc_state;
  //状态变化上报事件
  if (button-》state != state) {
  button-》state = state;
  input_event(input, EV_KEY, button-》code, button-》state);
  input_event(input, EV_KEY, button-》code, button-》state);
  input_sync(input);
  }
  //10ms后启动定时器
  if (state)
  mod_timer(&button-》timer, jiffies + DEBOUNCE_JIFFIES);
  }
  定时器会处理普通gpio和adc两种类型的按键,当状态变化时,会向用户空间上报当前事件、键值、状态。默认开机时,定时器处理函数因为检测不到状态变化而关闭退出。定时器的开启有两个地方会被调用:
  a.系统开机会启一个工作队列,每100ms周期性调用一次检测有没有按键触发
  static void adc_key_poll(struct work_struct *work)
  {
  if (!ddata-》in_suspend) {
  //读取adc电压
  result = rk_key_adc_iio_read(ddata);
  for (i = 0; i 《 ddata-》nbuttons; i++) {
  //允许值有一定范围的漂移
  if (result 《 button-》adc_value + DRIFT_ADVALUE &&
  result 》 button-》adc_value - DRIFT_ADVALUE)
  button-》adc_state = 1;
  else
  button-》adc_state = 0;
  if (button-》state != button-》adc_state)
  mod_timer(&button-》timer,
  jiffies + DEBOUNCE_JIFFIES);
  }
  }
  //周期性调用。ADC_SAMPLE_JIFFIES为100ms
  schedule_delayed_work(&ddata-》adc_poll_work, ADC_SAMPLE_JIFFIES);
  }
  b、 power key唤醒时中断处理会被触发
  static irqreturn_t keys_isr(int irq, void *dev_id)
  {
  //上报power key事件
  if (button-》wakeup && pdata-》in_suspend) {
  button-》state = 1;
  input_event(input, EV_KEY, button-》code, button-》state);
  input_sync(input);
  }
  if (button-》wakeup)
  wake_lock_timeout(&pdata-》wake_lock, WAKE_LOCK_JIFFIES);
  mod_timer(&button-》timer, jiffies + DEBOUNCE_JIFFIES);
  return IRQ_HANDLED;
  }
举报

更多回帖

发帖
×
20
完善资料,
赚取积分