usb: ehci-mx6: Add generic EHCI PHY support
[pandora-u-boot.git] / drivers / button / button-adc.c
1 // 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
8 #include <common.h>
9 #include <adc.h>
10 #include <button.h>
11 #include <log.h>
12 #include <dm.h>
13 #include <dm/lists.h>
14 #include <dm/of_access.h>
15 #include <dm/uclass-internal.h>
16
17 /**
18  * struct button_adc_priv - private data for button-adc driver.
19  *
20  * @adc: Analog to Digital Converter device to which button is connected.
21  * @channel: channel of the ADC device to probe the button state.
22  * @min: minimal uV value to consider button as pressed.
23  * @max: maximal uV value to consider button as pressed.
24  */
25 struct button_adc_priv {
26         struct udevice *adc;
27         int channel;
28         int min;
29         int max;
30 };
31
32 static enum button_state_t button_adc_get_state(struct udevice *dev)
33 {
34         struct button_adc_priv *priv = dev_get_priv(dev);
35         unsigned int val;
36         int ret, uV;
37
38         ret = adc_start_channel(priv->adc, priv->channel);
39         if (ret)
40                 return ret;
41
42         ret = adc_channel_data(priv->adc, priv->channel, &val);
43         if (ret)
44                 return ret;
45
46         ret = adc_raw_to_uV(priv->adc, val, &uV);
47         if (ret)
48                 return ret;
49
50         return (uV >= priv->min && uV < priv->max) ? BUTTON_ON : BUTTON_OFF;
51 }
52
53 static int button_adc_of_to_plat(struct udevice *dev)
54 {
55         struct button_uc_plat *uc_plat = dev_get_uclass_plat(dev);
56         struct button_adc_priv *priv = dev_get_priv(dev);
57         struct ofnode_phandle_args args;
58         u32 threshold, up_threshold, t;
59         ofnode node;
60         int ret;
61
62         /* Ignore the top-level button node */
63         if (!uc_plat->label)
64                 return 0;
65
66         ret = dev_read_phandle_with_args(dev->parent, "io-channels",
67                                          "#io-channel-cells", 0, 0, &args);
68         if (ret)
69                 return ret;
70
71         ret = uclass_get_device_by_ofnode(UCLASS_ADC, args.node, &priv->adc);
72         if (ret)
73                 return ret;
74
75         ret = ofnode_read_u32(dev_ofnode(dev->parent),
76                               "keyup-threshold-microvolt", &up_threshold);
77         if (ret)
78                 return ret;
79
80         ret = ofnode_read_u32(dev_ofnode(dev), "press-threshold-microvolt",
81                               &threshold);
82         if (ret)
83                 return ret;
84
85         dev_for_each_subnode(node, dev->parent) {
86                 ret = ofnode_read_u32(node, "press-threshold-microvolt", &t);
87                 if (ret)
88                         return ret;
89
90                 if (t > threshold)
91                         up_threshold = t;
92         }
93
94         priv->channel = args.args[0];
95         priv->min = threshold;
96         priv->max = up_threshold;
97
98         return ret;
99 }
100
101 static int button_adc_bind(struct udevice *parent)
102 {
103         struct udevice *dev;
104         ofnode node;
105         int ret;
106
107         dev_for_each_subnode(node, parent) {
108                 struct button_uc_plat *uc_plat;
109                 const char *label;
110
111                 label = ofnode_read_string(node, "label");
112                 if (!label) {
113                         debug("%s: node %s has no label\n", __func__,
114                               ofnode_get_name(node));
115                         return -EINVAL;
116                 }
117                 ret = device_bind_driver_to_node(parent, "button_adc",
118                                                  ofnode_get_name(node),
119                                                  node, &dev);
120                 if (ret)
121                         return ret;
122                 uc_plat = dev_get_uclass_plat(dev);
123                 uc_plat->label = label;
124         }
125
126         return 0;
127 }
128
129 static const struct button_ops button_adc_ops = {
130         .get_state      = button_adc_get_state,
131 };
132
133 static const struct udevice_id button_adc_ids[] = {
134         { .compatible = "adc-keys" },
135         { }
136 };
137
138 U_BOOT_DRIVER(button_adc) = {
139         .name           = "button_adc",
140         .id             = UCLASS_BUTTON,
141         .of_match       = button_adc_ids,
142         .ops            = &button_adc_ops,
143         .priv_auto      = sizeof(struct button_adc_priv),
144         .bind           = button_adc_bind,
145         .of_to_plat     = button_adc_of_to_plat,
146 };