linux/drivers/input/misc/axp20x-pek.c
<<
>>
Prefs
   1/*
   2 * axp20x power button driver.
   3 *
   4 * Copyright (C) 2013 Carlo Caione <carlo@caione.org>
   5 *
   6 * This file is subject to the terms and conditions of the GNU General
   7 * Public License. See the file "COPYING" in the main directory of this
   8 * archive for more details.
   9 *
  10 * This program is distributed in the hope that it will be useful,
  11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
  12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  13 * GNU General Public License for more details.
  14 */
  15
  16#include <linux/acpi.h>
  17#include <linux/errno.h>
  18#include <linux/irq.h>
  19#include <linux/init.h>
  20#include <linux/input.h>
  21#include <linux/interrupt.h>
  22#include <linux/kernel.h>
  23#include <linux/mfd/axp20x.h>
  24#include <linux/module.h>
  25#include <linux/platform_device.h>
  26#include <linux/regmap.h>
  27#include <linux/slab.h>
  28
  29#define AXP20X_PEK_STARTUP_MASK         (0xc0)
  30#define AXP20X_PEK_SHUTDOWN_MASK        (0x03)
  31
  32struct axp20x_info {
  33        const struct axp20x_time *startup_time;
  34        unsigned int startup_mask;
  35        const struct axp20x_time *shutdown_time;
  36        unsigned int shutdown_mask;
  37};
  38
  39struct axp20x_pek {
  40        struct axp20x_dev *axp20x;
  41        struct input_dev *input;
  42        struct axp20x_info *info;
  43        int irq_dbr;
  44        int irq_dbf;
  45};
  46
  47struct axp20x_time {
  48        unsigned int time;
  49        unsigned int idx;
  50};
  51
  52static const struct axp20x_time startup_time[] = {
  53        { .time = 128,  .idx = 0 },
  54        { .time = 1000, .idx = 2 },
  55        { .time = 3000, .idx = 1 },
  56        { .time = 2000, .idx = 3 },
  57};
  58
  59static const struct axp20x_time axp221_startup_time[] = {
  60        { .time = 128,  .idx = 0 },
  61        { .time = 1000, .idx = 1 },
  62        { .time = 2000, .idx = 2 },
  63        { .time = 3000, .idx = 3 },
  64};
  65
  66static const struct axp20x_time shutdown_time[] = {
  67        { .time = 4000,  .idx = 0 },
  68        { .time = 6000,  .idx = 1 },
  69        { .time = 8000,  .idx = 2 },
  70        { .time = 10000, .idx = 3 },
  71};
  72
  73static const struct axp20x_info axp20x_info = {
  74        .startup_time = startup_time,
  75        .startup_mask = AXP20X_PEK_STARTUP_MASK,
  76        .shutdown_time = shutdown_time,
  77        .shutdown_mask = AXP20X_PEK_SHUTDOWN_MASK,
  78};
  79
  80static const struct axp20x_info axp221_info = {
  81        .startup_time = axp221_startup_time,
  82        .startup_mask = AXP20X_PEK_STARTUP_MASK,
  83        .shutdown_time = shutdown_time,
  84        .shutdown_mask = AXP20X_PEK_SHUTDOWN_MASK,
  85};
  86
  87static ssize_t axp20x_show_attr(struct device *dev,
  88                                const struct axp20x_time *time,
  89                                unsigned int mask, char *buf)
  90{
  91        struct axp20x_pek *axp20x_pek = dev_get_drvdata(dev);
  92        unsigned int val;
  93        int ret, i;
  94
  95        ret = regmap_read(axp20x_pek->axp20x->regmap, AXP20X_PEK_KEY, &val);
  96        if (ret != 0)
  97                return ret;
  98
  99        val &= mask;
 100        val >>= ffs(mask) - 1;
 101
 102        for (i = 0; i < 4; i++)
 103                if (val == time[i].idx)
 104                        val = time[i].time;
 105
 106        return sprintf(buf, "%u\n", val);
 107}
 108
 109static ssize_t axp20x_show_attr_startup(struct device *dev,
 110                                        struct device_attribute *attr,
 111                                        char *buf)
 112{
 113        struct axp20x_pek *axp20x_pek = dev_get_drvdata(dev);
 114
 115        return axp20x_show_attr(dev, axp20x_pek->info->startup_time,
 116                                axp20x_pek->info->startup_mask, buf);
 117}
 118
 119static ssize_t axp20x_show_attr_shutdown(struct device *dev,
 120                                         struct device_attribute *attr,
 121                                         char *buf)
 122{
 123        struct axp20x_pek *axp20x_pek = dev_get_drvdata(dev);
 124
 125        return axp20x_show_attr(dev, axp20x_pek->info->shutdown_time,
 126                                axp20x_pek->info->shutdown_mask, buf);
 127}
 128
 129static ssize_t axp20x_store_attr(struct device *dev,
 130                                 const struct axp20x_time *time,
 131                                 unsigned int mask, const char *buf,
 132                                 size_t count)
 133{
 134        struct axp20x_pek *axp20x_pek = dev_get_drvdata(dev);
 135        char val_str[20];
 136        size_t len;
 137        int ret, i;
 138        unsigned int val, idx = 0;
 139        unsigned int best_err = UINT_MAX;
 140
 141        val_str[sizeof(val_str) - 1] = '\0';
 142        strncpy(val_str, buf, sizeof(val_str) - 1);
 143        len = strlen(val_str);
 144
 145        if (len && val_str[len - 1] == '\n')
 146                val_str[len - 1] = '\0';
 147
 148        ret = kstrtouint(val_str, 10, &val);
 149        if (ret)
 150                return ret;
 151
 152        for (i = 3; i >= 0; i--) {
 153                unsigned int err;
 154
 155                err = abs(time[i].time - val);
 156                if (err < best_err) {
 157                        best_err = err;
 158                        idx = time[i].idx;
 159                }
 160
 161                if (!err)
 162                        break;
 163        }
 164
 165        idx <<= ffs(mask) - 1;
 166        ret = regmap_update_bits(axp20x_pek->axp20x->regmap, AXP20X_PEK_KEY,
 167                                 mask, idx);
 168        if (ret != 0)
 169                return -EINVAL;
 170
 171        return count;
 172}
 173
 174static ssize_t axp20x_store_attr_startup(struct device *dev,
 175                                         struct device_attribute *attr,
 176                                         const char *buf, size_t count)
 177{
 178        struct axp20x_pek *axp20x_pek = dev_get_drvdata(dev);
 179
 180        return axp20x_store_attr(dev, axp20x_pek->info->startup_time,
 181                                 axp20x_pek->info->startup_mask, buf, count);
 182}
 183
 184static ssize_t axp20x_store_attr_shutdown(struct device *dev,
 185                                          struct device_attribute *attr,
 186                                          const char *buf, size_t count)
 187{
 188        struct axp20x_pek *axp20x_pek = dev_get_drvdata(dev);
 189
 190        return axp20x_store_attr(dev, axp20x_pek->info->shutdown_time,
 191                                 axp20x_pek->info->shutdown_mask, buf, count);
 192}
 193
 194DEVICE_ATTR(startup, 0644, axp20x_show_attr_startup, axp20x_store_attr_startup);
 195DEVICE_ATTR(shutdown, 0644, axp20x_show_attr_shutdown,
 196            axp20x_store_attr_shutdown);
 197
 198static struct attribute *axp20x_attributes[] = {
 199        &dev_attr_startup.attr,
 200        &dev_attr_shutdown.attr,
 201        NULL,
 202};
 203
 204static const struct attribute_group axp20x_attribute_group = {
 205        .attrs = axp20x_attributes,
 206};
 207
 208static irqreturn_t axp20x_pek_irq(int irq, void *pwr)
 209{
 210        struct input_dev *idev = pwr;
 211        struct axp20x_pek *axp20x_pek = input_get_drvdata(idev);
 212
 213        /*
 214         * The power-button is connected to ground so a falling edge (dbf)
 215         * means it is pressed.
 216         */
 217        if (irq == axp20x_pek->irq_dbf)
 218                input_report_key(idev, KEY_POWER, true);
 219        else if (irq == axp20x_pek->irq_dbr)
 220                input_report_key(idev, KEY_POWER, false);
 221
 222        input_sync(idev);
 223
 224        return IRQ_HANDLED;
 225}
 226
 227static int axp20x_pek_probe_input_device(struct axp20x_pek *axp20x_pek,
 228                                         struct platform_device *pdev)
 229{
 230        struct axp20x_dev *axp20x = axp20x_pek->axp20x;
 231        struct input_dev *idev;
 232        int error;
 233
 234        axp20x_pek->irq_dbr = platform_get_irq_byname(pdev, "PEK_DBR");
 235        if (axp20x_pek->irq_dbr < 0) {
 236                dev_err(&pdev->dev, "No IRQ for PEK_DBR, error=%d\n",
 237                                axp20x_pek->irq_dbr);
 238                return axp20x_pek->irq_dbr;
 239        }
 240        axp20x_pek->irq_dbr = regmap_irq_get_virq(axp20x->regmap_irqc,
 241                                                  axp20x_pek->irq_dbr);
 242
 243        axp20x_pek->irq_dbf = platform_get_irq_byname(pdev, "PEK_DBF");
 244        if (axp20x_pek->irq_dbf < 0) {
 245                dev_err(&pdev->dev, "No IRQ for PEK_DBF, error=%d\n",
 246                                axp20x_pek->irq_dbf);
 247                return axp20x_pek->irq_dbf;
 248        }
 249        axp20x_pek->irq_dbf = regmap_irq_get_virq(axp20x->regmap_irqc,
 250                                                  axp20x_pek->irq_dbf);
 251
 252        axp20x_pek->input = devm_input_allocate_device(&pdev->dev);
 253        if (!axp20x_pek->input)
 254                return -ENOMEM;
 255
 256        idev = axp20x_pek->input;
 257
 258        idev->name = "axp20x-pek";
 259        idev->phys = "m1kbd/input2";
 260        idev->dev.parent = &pdev->dev;
 261
 262        input_set_capability(idev, EV_KEY, KEY_POWER);
 263
 264        input_set_drvdata(idev, axp20x_pek);
 265
 266        error = devm_request_any_context_irq(&pdev->dev, axp20x_pek->irq_dbr,
 267                                             axp20x_pek_irq, 0,
 268                                             "axp20x-pek-dbr", idev);
 269        if (error < 0) {
 270                dev_err(&pdev->dev, "Failed to request dbr IRQ#%d: %d\n",
 271                        axp20x_pek->irq_dbr, error);
 272                return error;
 273        }
 274
 275        error = devm_request_any_context_irq(&pdev->dev, axp20x_pek->irq_dbf,
 276                                          axp20x_pek_irq, 0,
 277                                          "axp20x-pek-dbf", idev);
 278        if (error < 0) {
 279                dev_err(&pdev->dev, "Failed to request dbf IRQ#%d: %d\n",
 280                        axp20x_pek->irq_dbf, error);
 281                return error;
 282        }
 283
 284        error = input_register_device(idev);
 285        if (error) {
 286                dev_err(&pdev->dev, "Can't register input device: %d\n",
 287                        error);
 288                return error;
 289        }
 290
 291        if (axp20x_pek->axp20x->variant == AXP288_ID)
 292                enable_irq_wake(axp20x_pek->irq_dbr);
 293
 294        return 0;
 295}
 296
 297#ifdef CONFIG_ACPI
 298static bool axp20x_pek_should_register_input(struct axp20x_pek *axp20x_pek,
 299                                             struct platform_device *pdev)
 300{
 301        unsigned long long hrv = 0;
 302        acpi_status status;
 303
 304        if (IS_ENABLED(CONFIG_INPUT_SOC_BUTTON_ARRAY) &&
 305            axp20x_pek->axp20x->variant == AXP288_ID) {
 306                status = acpi_evaluate_integer(ACPI_HANDLE(pdev->dev.parent),
 307                                               "_HRV", NULL, &hrv);
 308                if (ACPI_FAILURE(status))
 309                        dev_err(&pdev->dev, "Failed to get PMIC hardware revision\n");
 310
 311                /*
 312                 * On Cherry Trail platforms (hrv == 3), do not register the
 313                 * input device if there is an "INTCFD9" or "ACPI0011" gpio
 314                 * button ACPI device, as that handles the power button too,
 315                 * and otherwise we end up reporting all presses twice.
 316                 */
 317                if (hrv == 3 && (acpi_dev_present("INTCFD9", NULL, -1) ||
 318                                 acpi_dev_present("ACPI0011", NULL, -1)))
 319                        return false;
 320
 321        }
 322
 323        return true;
 324}
 325#else
 326static bool axp20x_pek_should_register_input(struct axp20x_pek *axp20x_pek,
 327                                             struct platform_device *pdev)
 328{
 329        return true;
 330}
 331#endif
 332
 333static int axp20x_pek_probe(struct platform_device *pdev)
 334{
 335        struct axp20x_pek *axp20x_pek;
 336        const struct platform_device_id *match = platform_get_device_id(pdev);
 337        int error;
 338
 339        if (!match) {
 340                dev_err(&pdev->dev, "Failed to get platform_device_id\n");
 341                return -EINVAL;
 342        }
 343
 344        axp20x_pek = devm_kzalloc(&pdev->dev, sizeof(struct axp20x_pek),
 345                                  GFP_KERNEL);
 346        if (!axp20x_pek)
 347                return -ENOMEM;
 348
 349        axp20x_pek->axp20x = dev_get_drvdata(pdev->dev.parent);
 350
 351        if (axp20x_pek_should_register_input(axp20x_pek, pdev)) {
 352                error = axp20x_pek_probe_input_device(axp20x_pek, pdev);
 353                if (error)
 354                        return error;
 355        }
 356
 357        axp20x_pek->info = (struct axp20x_info *)match->driver_data;
 358
 359        error = devm_device_add_group(&pdev->dev, &axp20x_attribute_group);
 360        if (error) {
 361                dev_err(&pdev->dev, "Failed to create sysfs attributes: %d\n",
 362                        error);
 363                return error;
 364        }
 365
 366        platform_set_drvdata(pdev, axp20x_pek);
 367
 368        return 0;
 369}
 370
 371static int __maybe_unused axp20x_pek_resume_noirq(struct device *dev)
 372{
 373        struct axp20x_pek *axp20x_pek = dev_get_drvdata(dev);
 374
 375        if (axp20x_pek->axp20x->variant != AXP288_ID)
 376                return 0;
 377
 378        /*
 379         * Clear interrupts from button presses during suspend, to avoid
 380         * a wakeup power-button press getting reported to userspace.
 381         */
 382        regmap_write(axp20x_pek->axp20x->regmap,
 383                     AXP20X_IRQ1_STATE + AXP288_IRQ_POKN / 8,
 384                     BIT(AXP288_IRQ_POKN % 8));
 385
 386        return 0;
 387}
 388
 389static const struct dev_pm_ops axp20x_pek_pm_ops = {
 390#ifdef CONFIG_PM_SLEEP
 391        .resume_noirq = axp20x_pek_resume_noirq,
 392#endif
 393};
 394
 395static const struct platform_device_id axp_pek_id_match[] = {
 396        {
 397                .name = "axp20x-pek",
 398                .driver_data = (kernel_ulong_t)&axp20x_info,
 399        },
 400        {
 401                .name = "axp221-pek",
 402                .driver_data = (kernel_ulong_t)&axp221_info,
 403        },
 404        { /* sentinel */ }
 405};
 406MODULE_DEVICE_TABLE(platform, axp_pek_id_match);
 407
 408static struct platform_driver axp20x_pek_driver = {
 409        .probe          = axp20x_pek_probe,
 410        .id_table       = axp_pek_id_match,
 411        .driver         = {
 412                .name           = "axp20x-pek",
 413                .pm             = &axp20x_pek_pm_ops,
 414        },
 415};
 416module_platform_driver(axp20x_pek_driver);
 417
 418MODULE_DESCRIPTION("axp20x Power Button");
 419MODULE_AUTHOR("Carlo Caione <carlo@caione.org>");
 420MODULE_LICENSE("GPL");
 421