Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-2.0-or-later
0002 /*
0003  * Driver for a keypad w/16 buttons connected to a PCF8574 I2C I/O expander
0004  *
0005  * Copyright 2005-2008 Analog Devices Inc.
0006  */
0007 
0008 #include <linux/module.h>
0009 #include <linux/input.h>
0010 #include <linux/interrupt.h>
0011 #include <linux/i2c.h>
0012 #include <linux/slab.h>
0013 #include <linux/workqueue.h>
0014 
0015 #define DRV_NAME "pcf8574_keypad"
0016 
0017 static const unsigned char pcf8574_kp_btncode[] = {
0018     [0] = KEY_RESERVED,
0019     [1] = KEY_ENTER,
0020     [2] = KEY_BACKSLASH,
0021     [3] = KEY_0,
0022     [4] = KEY_RIGHTBRACE,
0023     [5] = KEY_C,
0024     [6] = KEY_9,
0025     [7] = KEY_8,
0026     [8] = KEY_7,
0027     [9] = KEY_B,
0028     [10] = KEY_6,
0029     [11] = KEY_5,
0030     [12] = KEY_4,
0031     [13] = KEY_A,
0032     [14] = KEY_3,
0033     [15] = KEY_2,
0034     [16] = KEY_1
0035 };
0036 
0037 struct kp_data {
0038     unsigned short btncode[ARRAY_SIZE(pcf8574_kp_btncode)];
0039     struct input_dev *idev;
0040     struct i2c_client *client;
0041     char name[64];
0042     char phys[32];
0043     unsigned char laststate;
0044 };
0045 
0046 static short read_state(struct kp_data *lp)
0047 {
0048     unsigned char x, y, a, b;
0049 
0050     i2c_smbus_write_byte(lp->client, 240);
0051     x = 0xF & (~(i2c_smbus_read_byte(lp->client) >> 4));
0052 
0053     i2c_smbus_write_byte(lp->client, 15);
0054     y = 0xF & (~i2c_smbus_read_byte(lp->client));
0055 
0056     for (a = 0; x > 0; a++)
0057         x = x >> 1;
0058     for (b = 0; y > 0; b++)
0059         y = y >> 1;
0060 
0061     return ((a - 1) * 4) + b;
0062 }
0063 
0064 static irqreturn_t pcf8574_kp_irq_handler(int irq, void *dev_id)
0065 {
0066     struct kp_data *lp = dev_id;
0067     unsigned char nextstate = read_state(lp);
0068 
0069     if (lp->laststate != nextstate) {
0070         int key_down = nextstate < ARRAY_SIZE(lp->btncode);
0071         unsigned short keycode = key_down ?
0072             lp->btncode[nextstate] : lp->btncode[lp->laststate];
0073 
0074         input_report_key(lp->idev, keycode, key_down);
0075         input_sync(lp->idev);
0076 
0077         lp->laststate = nextstate;
0078     }
0079 
0080     return IRQ_HANDLED;
0081 }
0082 
0083 static int pcf8574_kp_probe(struct i2c_client *client, const struct i2c_device_id *id)
0084 {
0085     int i, ret;
0086     struct input_dev *idev;
0087     struct kp_data *lp;
0088 
0089     if (i2c_smbus_write_byte(client, 240) < 0) {
0090         dev_err(&client->dev, "probe: write fail\n");
0091         return -ENODEV;
0092     }
0093 
0094     lp = kzalloc(sizeof(*lp), GFP_KERNEL);
0095     if (!lp)
0096         return -ENOMEM;
0097 
0098     idev = input_allocate_device();
0099     if (!idev) {
0100         dev_err(&client->dev, "Can't allocate input device\n");
0101         ret = -ENOMEM;
0102         goto fail_allocate;
0103     }
0104 
0105     lp->idev = idev;
0106     lp->client = client;
0107 
0108     idev->evbit[0] = BIT_MASK(EV_KEY);
0109     idev->keycode = lp->btncode;
0110     idev->keycodesize = sizeof(lp->btncode[0]);
0111     idev->keycodemax = ARRAY_SIZE(lp->btncode);
0112 
0113     for (i = 0; i < ARRAY_SIZE(pcf8574_kp_btncode); i++) {
0114         if (lp->btncode[i] <= KEY_MAX) {
0115             lp->btncode[i] = pcf8574_kp_btncode[i];
0116             __set_bit(lp->btncode[i], idev->keybit);
0117         }
0118     }
0119     __clear_bit(KEY_RESERVED, idev->keybit);
0120 
0121     sprintf(lp->name, DRV_NAME);
0122     sprintf(lp->phys, "kp_data/input0");
0123 
0124     idev->name = lp->name;
0125     idev->phys = lp->phys;
0126     idev->id.bustype = BUS_I2C;
0127     idev->id.vendor = 0x0001;
0128     idev->id.product = 0x0001;
0129     idev->id.version = 0x0100;
0130 
0131     lp->laststate = read_state(lp);
0132 
0133     ret = request_threaded_irq(client->irq, NULL, pcf8574_kp_irq_handler,
0134                    IRQF_TRIGGER_LOW | IRQF_ONESHOT,
0135                    DRV_NAME, lp);
0136     if (ret) {
0137         dev_err(&client->dev, "IRQ %d is not free\n", client->irq);
0138         goto fail_free_device;
0139     }
0140 
0141     ret = input_register_device(idev);
0142     if (ret) {
0143         dev_err(&client->dev, "input_register_device() failed\n");
0144         goto fail_free_irq;
0145     }
0146 
0147     i2c_set_clientdata(client, lp);
0148     return 0;
0149 
0150  fail_free_irq:
0151     free_irq(client->irq, lp);
0152  fail_free_device:
0153     input_free_device(idev);
0154  fail_allocate:
0155     kfree(lp);
0156 
0157     return ret;
0158 }
0159 
0160 static int pcf8574_kp_remove(struct i2c_client *client)
0161 {
0162     struct kp_data *lp = i2c_get_clientdata(client);
0163 
0164     free_irq(client->irq, lp);
0165 
0166     input_unregister_device(lp->idev);
0167     kfree(lp);
0168 
0169     return 0;
0170 }
0171 
0172 #ifdef CONFIG_PM
0173 static int pcf8574_kp_resume(struct device *dev)
0174 {
0175     struct i2c_client *client = to_i2c_client(dev);
0176 
0177     enable_irq(client->irq);
0178 
0179     return 0;
0180 }
0181 
0182 static int pcf8574_kp_suspend(struct device *dev)
0183 {
0184     struct i2c_client *client = to_i2c_client(dev);
0185 
0186     disable_irq(client->irq);
0187 
0188     return 0;
0189 }
0190 
0191 static const struct dev_pm_ops pcf8574_kp_pm_ops = {
0192     .suspend    = pcf8574_kp_suspend,
0193     .resume     = pcf8574_kp_resume,
0194 };
0195 
0196 #else
0197 # define pcf8574_kp_resume  NULL
0198 # define pcf8574_kp_suspend NULL
0199 #endif
0200 
0201 static const struct i2c_device_id pcf8574_kp_id[] = {
0202     { DRV_NAME, 0 },
0203     { }
0204 };
0205 MODULE_DEVICE_TABLE(i2c, pcf8574_kp_id);
0206 
0207 static struct i2c_driver pcf8574_kp_driver = {
0208     .driver = {
0209         .name  = DRV_NAME,
0210 #ifdef CONFIG_PM
0211         .pm = &pcf8574_kp_pm_ops,
0212 #endif
0213     },
0214     .probe    = pcf8574_kp_probe,
0215     .remove   = pcf8574_kp_remove,
0216     .id_table = pcf8574_kp_id,
0217 };
0218 
0219 module_i2c_driver(pcf8574_kp_driver);
0220 
0221 MODULE_AUTHOR("Michael Hennerich");
0222 MODULE_DESCRIPTION("Keypad input driver for 16 keys connected to PCF8574");
0223 MODULE_LICENSE("GPL");