blob: da7ddf2a857778df902a90b9d94ba3addd372c1c [file] [log] [blame]
Marek Szyprowskid26c4082021-02-18 11:33:15 +01001// SPDX-License-Identifier: GPL-2.0
2/*
3 * Copyright (C) 2021 Samsung Electronics Co., Ltd.
4 * http://www.samsung.com
5 * Author: Marek Szyprowski <m.szyprowski@samsung.com>
6 */
7
Marek Szyprowskid26c4082021-02-18 11:33:15 +01008#include <adc.h>
9#include <button.h>
10#include <log.h>
11#include <dm.h>
12#include <dm/lists.h>
13#include <dm/of_access.h>
14#include <dm/uclass-internal.h>
15
16/**
17 * struct button_adc_priv - private data for button-adc driver.
18 *
19 * @adc: Analog to Digital Converter device to which button is connected.
20 * @channel: channel of the ADC device to probe the button state.
21 * @min: minimal uV value to consider button as pressed.
22 * @max: maximal uV value to consider button as pressed.
23 */
24struct button_adc_priv {
25 struct udevice *adc;
26 int channel;
27 int min;
28 int max;
29};
30
31static enum button_state_t button_adc_get_state(struct udevice *dev)
32{
33 struct button_adc_priv *priv = dev_get_priv(dev);
34 unsigned int val;
35 int ret, uV;
36
37 ret = adc_start_channel(priv->adc, priv->channel);
38 if (ret)
39 return ret;
40
41 ret = adc_channel_data(priv->adc, priv->channel, &val);
42 if (ret)
43 return ret;
44
45 ret = adc_raw_to_uV(priv->adc, val, &uV);
46 if (ret)
47 return ret;
48
49 return (uV >= priv->min && uV < priv->max) ? BUTTON_ON : BUTTON_OFF;
50}
51
52static int button_adc_of_to_plat(struct udevice *dev)
53{
54 struct button_uc_plat *uc_plat = dev_get_uclass_plat(dev);
55 struct button_adc_priv *priv = dev_get_priv(dev);
56 struct ofnode_phandle_args args;
Peter Cai6a0904b2022-02-02 13:04:04 -050057 u32 down_threshold = 0, up_threshold, voltage, t;
Marek Szyprowskid26c4082021-02-18 11:33:15 +010058 ofnode node;
59 int ret;
60
61 /* Ignore the top-level button node */
62 if (!uc_plat->label)
63 return 0;
64
65 ret = dev_read_phandle_with_args(dev->parent, "io-channels",
66 "#io-channel-cells", 0, 0, &args);
67 if (ret)
68 return ret;
69
70 ret = uclass_get_device_by_ofnode(UCLASS_ADC, args.node, &priv->adc);
71 if (ret)
72 return ret;
73
74 ret = ofnode_read_u32(dev_ofnode(dev->parent),
Neil Armstrongf22774c2021-02-23 16:07:51 +010075 "keyup-threshold-microvolt", &up_threshold);
Marek Szyprowskid26c4082021-02-18 11:33:15 +010076 if (ret)
77 return ret;
78
79 ret = ofnode_read_u32(dev_ofnode(dev), "press-threshold-microvolt",
Peter Cai6a0904b2022-02-02 13:04:04 -050080 &voltage);
Marek Szyprowskid26c4082021-02-18 11:33:15 +010081 if (ret)
82 return ret;
83
84 dev_for_each_subnode(node, dev->parent) {
85 ret = ofnode_read_u32(node, "press-threshold-microvolt", &t);
86 if (ret)
87 return ret;
88
Peter Cai6a0904b2022-02-02 13:04:04 -050089 if (t > voltage && t < up_threshold)
Neil Armstrongf22774c2021-02-23 16:07:51 +010090 up_threshold = t;
Peter Cai6a0904b2022-02-02 13:04:04 -050091 else if (t < voltage && t > down_threshold)
92 down_threshold = t;
Marek Szyprowskid26c4082021-02-18 11:33:15 +010093 }
94
95 priv->channel = args.args[0];
Peter Cai6a0904b2022-02-02 13:04:04 -050096
97 /*
98 * Define the voltage range such that the button is only pressed
99 * when the voltage is closest to its own press-threshold-microvolt
100 */
101 if (down_threshold == 0)
102 priv->min = 0;
103 else
104 priv->min = down_threshold + (voltage - down_threshold) / 2;
105
106 priv->max = voltage + (up_threshold - voltage) / 2;
Marek Szyprowskid26c4082021-02-18 11:33:15 +0100107
108 return ret;
109}
110
111static int button_adc_bind(struct udevice *parent)
112{
113 struct udevice *dev;
114 ofnode node;
115 int ret;
116
117 dev_for_each_subnode(node, parent) {
118 struct button_uc_plat *uc_plat;
119 const char *label;
120
121 label = ofnode_read_string(node, "label");
122 if (!label) {
123 debug("%s: node %s has no label\n", __func__,
124 ofnode_get_name(node));
125 return -EINVAL;
126 }
127 ret = device_bind_driver_to_node(parent, "button_adc",
128 ofnode_get_name(node),
129 node, &dev);
130 if (ret)
131 return ret;
132 uc_plat = dev_get_uclass_plat(dev);
133 uc_plat->label = label;
134 }
135
136 return 0;
137}
138
139static const struct button_ops button_adc_ops = {
140 .get_state = button_adc_get_state,
141};
142
143static const struct udevice_id button_adc_ids[] = {
144 { .compatible = "adc-keys" },
145 { }
146};
147
148U_BOOT_DRIVER(button_adc) = {
149 .name = "button_adc",
150 .id = UCLASS_BUTTON,
151 .of_match = button_adc_ids,
152 .ops = &button_adc_ops,
153 .priv_auto = sizeof(struct button_adc_priv),
154 .bind = button_adc_bind,
155 .of_to_plat = button_adc_of_to_plat,
156};