0001
0002
0003
0004
0005
0006
0007 #include <linux/init.h>
0008 #include <linux/module.h>
0009 #include <linux/input.h>
0010 #include <linux/ioport.h>
0011 #include <linux/platform_device.h>
0012 #include <linux/dmi.h>
0013 #include <linux/io.h>
0014
0015 #define DRV_NAME "winmate-fm07keys"
0016
0017 #define PORT_CMD 0x6c
0018 #define PORT_DATA 0x68
0019
0020 #define EC_ADDR_KEYS 0x3b
0021 #define EC_CMD_READ 0x80
0022
0023 #define BASE_KEY KEY_F13
0024 #define NUM_KEYS 5
0025
0026
0027 #define LOOP_TIMEOUT 1000
0028
0029 static void fm07keys_poll(struct input_dev *input)
0030 {
0031 uint8_t k;
0032 int i;
0033
0034
0035 i = 0;
0036 while (inb(PORT_CMD) & 0x01) {
0037 if (++i >= LOOP_TIMEOUT)
0038 goto timeout;
0039 inb(PORT_DATA);
0040 }
0041
0042
0043 outb(EC_CMD_READ, PORT_CMD);
0044 i = 0;
0045 while (inb(PORT_CMD) & 0x02)
0046 if (++i >= LOOP_TIMEOUT)
0047 goto timeout;
0048
0049 outb(EC_ADDR_KEYS, PORT_DATA);
0050 i = 0;
0051 while (inb(PORT_CMD) & 0x02)
0052 if (++i >= LOOP_TIMEOUT)
0053 goto timeout;
0054
0055
0056 i = 0;
0057 while (!(inb(PORT_CMD) & 0x01))
0058 if (++i >= LOOP_TIMEOUT)
0059 goto timeout;
0060 k = inb(PORT_DATA);
0061
0062
0063 for (i = 0; i < NUM_KEYS; i++) {
0064 input_report_key(input, BASE_KEY + i, (~k) & 1);
0065 k >>= 1;
0066 }
0067
0068 input_sync(input);
0069 return;
0070
0071 timeout:
0072 dev_warn_ratelimited(&input->dev, "timeout polling IO memory\n");
0073 }
0074
0075 static int fm07keys_probe(struct platform_device *pdev)
0076 {
0077 struct device *dev = &pdev->dev;
0078 struct input_dev *input;
0079 int ret;
0080 int i;
0081
0082 input = devm_input_allocate_device(dev);
0083 if (!input) {
0084 dev_err(dev, "no memory for input device\n");
0085 return -ENOMEM;
0086 }
0087
0088 if (!devm_request_region(dev, PORT_CMD, 1, "Winmate FM07 EC"))
0089 return -EBUSY;
0090 if (!devm_request_region(dev, PORT_DATA, 1, "Winmate FM07 EC"))
0091 return -EBUSY;
0092
0093 input->name = "Winmate FM07 front-panel keys";
0094 input->phys = DRV_NAME "/input0";
0095
0096 input->id.bustype = BUS_HOST;
0097 input->id.vendor = 0x0001;
0098 input->id.product = 0x0001;
0099 input->id.version = 0x0100;
0100
0101 __set_bit(EV_KEY, input->evbit);
0102
0103 for (i = 0; i < NUM_KEYS; i++)
0104 __set_bit(BASE_KEY + i, input->keybit);
0105
0106 ret = input_setup_polling(input, fm07keys_poll);
0107 if (ret) {
0108 dev_err(dev, "unable to set up polling, err=%d\n", ret);
0109 return ret;
0110 }
0111
0112
0113
0114
0115
0116 input_set_poll_interval(input, 20);
0117
0118 ret = input_register_device(input);
0119 if (ret) {
0120 dev_err(dev, "unable to register polled device, err=%d\n",
0121 ret);
0122 return ret;
0123 }
0124
0125 input_sync(input);
0126 return 0;
0127 }
0128
0129 static struct platform_driver fm07keys_driver = {
0130 .probe = fm07keys_probe,
0131 .driver = {
0132 .name = DRV_NAME
0133 },
0134 };
0135
0136 static struct platform_device *dev;
0137
0138 static const struct dmi_system_id fm07keys_dmi_table[] __initconst = {
0139 {
0140
0141 .matches = {
0142 DMI_MATCH(DMI_SYS_VENDOR, "Winmate Inc."),
0143 DMI_MATCH(DMI_PRODUCT_NAME, "IP30"),
0144 },
0145 },
0146 { }
0147 };
0148
0149 MODULE_DEVICE_TABLE(dmi, fm07keys_dmi_table);
0150
0151 static int __init fm07keys_init(void)
0152 {
0153 int ret;
0154
0155 if (!dmi_check_system(fm07keys_dmi_table))
0156 return -ENODEV;
0157
0158 ret = platform_driver_register(&fm07keys_driver);
0159 if (ret) {
0160 pr_err("fm07keys: failed to register driver, err=%d\n", ret);
0161 return ret;
0162 }
0163
0164 dev = platform_device_register_simple(DRV_NAME, -1, NULL, 0);
0165 if (IS_ERR(dev)) {
0166 ret = PTR_ERR(dev);
0167 pr_err("fm07keys: failed to allocate device, err = %d\n", ret);
0168 goto fail_register;
0169 }
0170
0171 return 0;
0172
0173 fail_register:
0174 platform_driver_unregister(&fm07keys_driver);
0175 return ret;
0176 }
0177
0178 static void __exit fm07keys_exit(void)
0179 {
0180 platform_driver_unregister(&fm07keys_driver);
0181 platform_device_unregister(dev);
0182 }
0183
0184 module_init(fm07keys_init);
0185 module_exit(fm07keys_exit);
0186
0187 MODULE_AUTHOR("Daniel Beer <daniel.beer@tirotech.co.nz>");
0188 MODULE_DESCRIPTION("Winmate FM07 front-panel keys driver");
0189 MODULE_LICENSE("GPL");