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