linux/drivers/hid/i2c-hid/i2c-hid-of.c
<<
>>
Prefs
   1/*
   2 * HID over I2C Open Firmware Subclass
   3 *
   4 * Copyright (c) 2012 Benjamin Tissoires <benjamin.tissoires@gmail.com>
   5 * Copyright (c) 2012 Ecole Nationale de l'Aviation Civile, France
   6 * Copyright (c) 2012 Red Hat, Inc
   7 *
   8 * This code was forked out of the core code, which was partly based on
   9 * "USB HID support for Linux":
  10 *
  11 *  Copyright (c) 1999 Andreas Gal
  12 *  Copyright (c) 2000-2005 Vojtech Pavlik <vojtech@suse.cz>
  13 *  Copyright (c) 2005 Michael Haboustak <mike-@cinci.rr.com> for Concept2, Inc
  14 *  Copyright (c) 2007-2008 Oliver Neukum
  15 *  Copyright (c) 2006-2010 Jiri Kosina
  16 *
  17 * This file is subject to the terms and conditions of the GNU General Public
  18 * License.  See the file COPYING in the main directory of this archive for
  19 * more details.
  20 */
  21
  22#include <linux/delay.h>
  23#include <linux/device.h>
  24#include <linux/i2c.h>
  25#include <linux/kernel.h>
  26#include <linux/module.h>
  27#include <linux/of.h>
  28#include <linux/pm.h>
  29#include <linux/regulator/consumer.h>
  30
  31#include "i2c-hid.h"
  32
  33struct i2c_hid_of {
  34        struct i2chid_ops ops;
  35
  36        struct i2c_client *client;
  37        struct regulator_bulk_data supplies[2];
  38        int post_power_delay_ms;
  39};
  40
  41static int i2c_hid_of_power_up(struct i2chid_ops *ops)
  42{
  43        struct i2c_hid_of *ihid_of = container_of(ops, struct i2c_hid_of, ops);
  44        struct device *dev = &ihid_of->client->dev;
  45        int ret;
  46
  47        ret = regulator_bulk_enable(ARRAY_SIZE(ihid_of->supplies),
  48                                    ihid_of->supplies);
  49        if (ret) {
  50                dev_warn(dev, "Failed to enable supplies: %d\n", ret);
  51                return ret;
  52        }
  53
  54        if (ihid_of->post_power_delay_ms)
  55                msleep(ihid_of->post_power_delay_ms);
  56
  57        return 0;
  58}
  59
  60static void i2c_hid_of_power_down(struct i2chid_ops *ops)
  61{
  62        struct i2c_hid_of *ihid_of = container_of(ops, struct i2c_hid_of, ops);
  63
  64        regulator_bulk_disable(ARRAY_SIZE(ihid_of->supplies),
  65                               ihid_of->supplies);
  66}
  67
  68static int i2c_hid_of_probe(struct i2c_client *client,
  69                            const struct i2c_device_id *dev_id)
  70{
  71        struct device *dev = &client->dev;
  72        struct i2c_hid_of *ihid_of;
  73        u16 hid_descriptor_address;
  74        int ret;
  75        u32 val;
  76
  77        ihid_of = devm_kzalloc(&client->dev, sizeof(*ihid_of), GFP_KERNEL);
  78        if (!ihid_of)
  79                return -ENOMEM;
  80
  81        ihid_of->ops.power_up = i2c_hid_of_power_up;
  82        ihid_of->ops.power_down = i2c_hid_of_power_down;
  83
  84        ret = of_property_read_u32(dev->of_node, "hid-descr-addr", &val);
  85        if (ret) {
  86                dev_err(&client->dev, "HID register address not provided\n");
  87                return -ENODEV;
  88        }
  89        if (val >> 16) {
  90                dev_err(&client->dev, "Bad HID register address: 0x%08x\n",
  91                        val);
  92                return -EINVAL;
  93        }
  94        hid_descriptor_address = val;
  95
  96        if (!device_property_read_u32(&client->dev, "post-power-on-delay-ms",
  97                                      &val))
  98                ihid_of->post_power_delay_ms = val;
  99
 100        ihid_of->supplies[0].supply = "vdd";
 101        ihid_of->supplies[1].supply = "vddl";
 102        ret = devm_regulator_bulk_get(&client->dev,
 103                                      ARRAY_SIZE(ihid_of->supplies),
 104                                      ihid_of->supplies);
 105        if (ret)
 106                return ret;
 107
 108        return i2c_hid_core_probe(client, &ihid_of->ops,
 109                                  hid_descriptor_address);
 110}
 111
 112static const struct of_device_id i2c_hid_of_match[] = {
 113        { .compatible = "hid-over-i2c" },
 114        {},
 115};
 116MODULE_DEVICE_TABLE(of, i2c_hid_of_match);
 117
 118static const struct i2c_device_id i2c_hid_of_id_table[] = {
 119        { "hid", 0 },
 120        { "hid-over-i2c", 0 },
 121        { },
 122};
 123MODULE_DEVICE_TABLE(i2c, i2c_hid_of_id_table);
 124
 125static struct i2c_driver i2c_hid_of_driver = {
 126        .driver = {
 127                .name   = "i2c_hid_of",
 128                .pm     = &i2c_hid_core_pm,
 129                .probe_type = PROBE_PREFER_ASYNCHRONOUS,
 130                .of_match_table = of_match_ptr(i2c_hid_of_match),
 131        },
 132
 133        .probe          = i2c_hid_of_probe,
 134        .remove         = i2c_hid_core_remove,
 135        .shutdown       = i2c_hid_core_shutdown,
 136        .id_table       = i2c_hid_of_id_table,
 137};
 138
 139module_i2c_driver(i2c_hid_of_driver);
 140
 141MODULE_DESCRIPTION("HID over I2C OF driver");
 142MODULE_AUTHOR("Benjamin Tissoires <benjamin.tissoires@gmail.com>");
 143MODULE_LICENSE("GPL");
 144