19c92ab61SThomas Gleixner // SPDX-License-Identifier: GPL-2.0-only
26f2ac009SBrian Swetland /*
36f2ac009SBrian Swetland * Copyright (C) 2007 Google, Inc.
46f2ac009SBrian Swetland * Copyright (C) 2012 Intel, Inc.
56f2ac009SBrian Swetland */
66f2ac009SBrian Swetland
76f2ac009SBrian Swetland #include <linux/module.h>
86f2ac009SBrian Swetland #include <linux/interrupt.h>
96f2ac009SBrian Swetland #include <linux/types.h>
106f2ac009SBrian Swetland #include <linux/input.h>
116f2ac009SBrian Swetland #include <linux/kernel.h>
126f2ac009SBrian Swetland #include <linux/platform_device.h>
136f2ac009SBrian Swetland #include <linux/slab.h>
146f2ac009SBrian Swetland #include <linux/irq.h>
156f2ac009SBrian Swetland #include <linux/io.h>
160581ce09SJason Hu #include <linux/acpi.h>
176f2ac009SBrian Swetland
186f2ac009SBrian Swetland enum {
196f2ac009SBrian Swetland REG_READ = 0x00,
206f2ac009SBrian Swetland REG_SET_PAGE = 0x00,
216f2ac009SBrian Swetland REG_LEN = 0x04,
226f2ac009SBrian Swetland REG_DATA = 0x08,
236f2ac009SBrian Swetland
246f2ac009SBrian Swetland PAGE_NAME = 0x00000,
256f2ac009SBrian Swetland PAGE_EVBITS = 0x10000,
266f2ac009SBrian Swetland PAGE_ABSDATA = 0x20000 | EV_ABS,
276f2ac009SBrian Swetland };
286f2ac009SBrian Swetland
296f2ac009SBrian Swetland struct event_dev {
306f2ac009SBrian Swetland struct input_dev *input;
316f2ac009SBrian Swetland int irq;
326f2ac009SBrian Swetland void __iomem *addr;
33*cfb8d781SGustavo A. R. Silva char name[];
346f2ac009SBrian Swetland };
356f2ac009SBrian Swetland
events_interrupt(int irq,void * dev_id)366f2ac009SBrian Swetland static irqreturn_t events_interrupt(int irq, void *dev_id)
376f2ac009SBrian Swetland {
386f2ac009SBrian Swetland struct event_dev *edev = dev_id;
398f6a652aSRoman Kiryanov unsigned int type, code, value;
406f2ac009SBrian Swetland
416f2ac009SBrian Swetland type = __raw_readl(edev->addr + REG_READ);
426f2ac009SBrian Swetland code = __raw_readl(edev->addr + REG_READ);
436f2ac009SBrian Swetland value = __raw_readl(edev->addr + REG_READ);
446f2ac009SBrian Swetland
456f2ac009SBrian Swetland input_event(edev->input, type, code, value);
466f2ac009SBrian Swetland input_sync(edev->input);
476f2ac009SBrian Swetland return IRQ_HANDLED;
486f2ac009SBrian Swetland }
496f2ac009SBrian Swetland
events_import_bits(struct event_dev * edev,unsigned long bits[],unsigned int type,size_t count)506f2ac009SBrian Swetland static void events_import_bits(struct event_dev *edev,
518f6a652aSRoman Kiryanov unsigned long bits[], unsigned int type, size_t count)
526f2ac009SBrian Swetland {
536f2ac009SBrian Swetland void __iomem *addr = edev->addr;
546f2ac009SBrian Swetland int i, j;
556f2ac009SBrian Swetland size_t size;
566f2ac009SBrian Swetland uint8_t val;
576f2ac009SBrian Swetland
586f2ac009SBrian Swetland __raw_writel(PAGE_EVBITS | type, addr + REG_SET_PAGE);
596f2ac009SBrian Swetland
606f2ac009SBrian Swetland size = __raw_readl(addr + REG_LEN) * 8;
616f2ac009SBrian Swetland if (size < count)
626f2ac009SBrian Swetland count = size;
636f2ac009SBrian Swetland
646f2ac009SBrian Swetland addr += REG_DATA;
656f2ac009SBrian Swetland for (i = 0; i < count; i += 8) {
666f2ac009SBrian Swetland val = __raw_readb(addr++);
676f2ac009SBrian Swetland for (j = 0; j < 8; j++)
686f2ac009SBrian Swetland if (val & 1 << j)
696f2ac009SBrian Swetland set_bit(i + j, bits);
706f2ac009SBrian Swetland }
716f2ac009SBrian Swetland }
726f2ac009SBrian Swetland
events_import_abs_params(struct event_dev * edev)736f2ac009SBrian Swetland static void events_import_abs_params(struct event_dev *edev)
746f2ac009SBrian Swetland {
756f2ac009SBrian Swetland struct input_dev *input_dev = edev->input;
766f2ac009SBrian Swetland void __iomem *addr = edev->addr;
776f2ac009SBrian Swetland u32 val[4];
786f2ac009SBrian Swetland int count;
796f2ac009SBrian Swetland int i, j;
806f2ac009SBrian Swetland
816f2ac009SBrian Swetland __raw_writel(PAGE_ABSDATA, addr + REG_SET_PAGE);
826f2ac009SBrian Swetland
836f2ac009SBrian Swetland count = __raw_readl(addr + REG_LEN) / sizeof(val);
846f2ac009SBrian Swetland if (count > ABS_MAX)
856f2ac009SBrian Swetland count = ABS_MAX;
866f2ac009SBrian Swetland
876f2ac009SBrian Swetland for (i = 0; i < count; i++) {
886f2ac009SBrian Swetland if (!test_bit(i, input_dev->absbit))
896f2ac009SBrian Swetland continue;
906f2ac009SBrian Swetland
916f2ac009SBrian Swetland for (j = 0; j < ARRAY_SIZE(val); j++) {
926f2ac009SBrian Swetland int offset = (i * ARRAY_SIZE(val) + j) * sizeof(u32);
938f6a652aSRoman Kiryanov
946f2ac009SBrian Swetland val[j] = __raw_readl(edev->addr + REG_DATA + offset);
956f2ac009SBrian Swetland }
966f2ac009SBrian Swetland
976f2ac009SBrian Swetland input_set_abs_params(input_dev, i,
986f2ac009SBrian Swetland val[0], val[1], val[2], val[3]);
996f2ac009SBrian Swetland }
1006f2ac009SBrian Swetland }
1016f2ac009SBrian Swetland
events_probe(struct platform_device * pdev)1026f2ac009SBrian Swetland static int events_probe(struct platform_device *pdev)
1036f2ac009SBrian Swetland {
1046f2ac009SBrian Swetland struct input_dev *input_dev;
1056f2ac009SBrian Swetland struct event_dev *edev;
1066f2ac009SBrian Swetland struct resource *res;
1078f6a652aSRoman Kiryanov unsigned int keymapnamelen;
1086f2ac009SBrian Swetland void __iomem *addr;
1096f2ac009SBrian Swetland int irq;
1106f2ac009SBrian Swetland int i;
1116f2ac009SBrian Swetland int error;
1126f2ac009SBrian Swetland
1136f2ac009SBrian Swetland irq = platform_get_irq(pdev, 0);
1146f2ac009SBrian Swetland if (irq < 0)
1156f2ac009SBrian Swetland return -EINVAL;
1166f2ac009SBrian Swetland
1176f2ac009SBrian Swetland res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
1186f2ac009SBrian Swetland if (!res)
1196f2ac009SBrian Swetland return -EINVAL;
1206f2ac009SBrian Swetland
1216f2ac009SBrian Swetland addr = devm_ioremap(&pdev->dev, res->start, 4096);
1226f2ac009SBrian Swetland if (!addr)
1236f2ac009SBrian Swetland return -ENOMEM;
1246f2ac009SBrian Swetland
1256f2ac009SBrian Swetland __raw_writel(PAGE_NAME, addr + REG_SET_PAGE);
1266f2ac009SBrian Swetland keymapnamelen = __raw_readl(addr + REG_LEN);
1276f2ac009SBrian Swetland
1286f2ac009SBrian Swetland edev = devm_kzalloc(&pdev->dev,
1296f2ac009SBrian Swetland sizeof(struct event_dev) + keymapnamelen + 1,
1306f2ac009SBrian Swetland GFP_KERNEL);
1316f2ac009SBrian Swetland if (!edev)
1326f2ac009SBrian Swetland return -ENOMEM;
1336f2ac009SBrian Swetland
1346f2ac009SBrian Swetland input_dev = devm_input_allocate_device(&pdev->dev);
1356f2ac009SBrian Swetland if (!input_dev)
1366f2ac009SBrian Swetland return -ENOMEM;
1376f2ac009SBrian Swetland
1386f2ac009SBrian Swetland edev->input = input_dev;
1396f2ac009SBrian Swetland edev->addr = addr;
1406f2ac009SBrian Swetland edev->irq = irq;
1416f2ac009SBrian Swetland
1426f2ac009SBrian Swetland for (i = 0; i < keymapnamelen; i++)
1436f2ac009SBrian Swetland edev->name[i] = __raw_readb(edev->addr + REG_DATA + i);
1446f2ac009SBrian Swetland
1458f6a652aSRoman Kiryanov pr_debug("%s: keymap=%s\n", __func__, edev->name);
1466f2ac009SBrian Swetland
1476f2ac009SBrian Swetland input_dev->name = edev->name;
1486f2ac009SBrian Swetland input_dev->id.bustype = BUS_HOST;
1496f2ac009SBrian Swetland
1506f2ac009SBrian Swetland events_import_bits(edev, input_dev->evbit, EV_SYN, EV_MAX);
1516f2ac009SBrian Swetland events_import_bits(edev, input_dev->keybit, EV_KEY, KEY_MAX);
1526f2ac009SBrian Swetland events_import_bits(edev, input_dev->relbit, EV_REL, REL_MAX);
1536f2ac009SBrian Swetland events_import_bits(edev, input_dev->absbit, EV_ABS, ABS_MAX);
1546f2ac009SBrian Swetland events_import_bits(edev, input_dev->mscbit, EV_MSC, MSC_MAX);
1556f2ac009SBrian Swetland events_import_bits(edev, input_dev->ledbit, EV_LED, LED_MAX);
1566f2ac009SBrian Swetland events_import_bits(edev, input_dev->sndbit, EV_SND, SND_MAX);
1576f2ac009SBrian Swetland events_import_bits(edev, input_dev->ffbit, EV_FF, FF_MAX);
1586f2ac009SBrian Swetland events_import_bits(edev, input_dev->swbit, EV_SW, SW_MAX);
1596f2ac009SBrian Swetland
1606f2ac009SBrian Swetland events_import_abs_params(edev);
1616f2ac009SBrian Swetland
1626f2ac009SBrian Swetland error = devm_request_irq(&pdev->dev, edev->irq, events_interrupt, 0,
1636f2ac009SBrian Swetland "goldfish-events-keypad", edev);
1646f2ac009SBrian Swetland if (error)
1656f2ac009SBrian Swetland return error;
1666f2ac009SBrian Swetland
1676f2ac009SBrian Swetland error = input_register_device(input_dev);
1686f2ac009SBrian Swetland if (error)
1696f2ac009SBrian Swetland return error;
1706f2ac009SBrian Swetland
1716f2ac009SBrian Swetland return 0;
1726f2ac009SBrian Swetland }
1736f2ac009SBrian Swetland
1748c5dc5a1SGreg Hackmann static const struct of_device_id goldfish_events_of_match[] = {
1758c5dc5a1SGreg Hackmann { .compatible = "google,goldfish-events-keypad", },
1768c5dc5a1SGreg Hackmann {},
1778c5dc5a1SGreg Hackmann };
1788c5dc5a1SGreg Hackmann MODULE_DEVICE_TABLE(of, goldfish_events_of_match);
1798c5dc5a1SGreg Hackmann
1800581ce09SJason Hu #ifdef CONFIG_ACPI
1810581ce09SJason Hu static const struct acpi_device_id goldfish_events_acpi_match[] = {
1820581ce09SJason Hu { "GFSH0002", 0 },
1830581ce09SJason Hu { },
1840581ce09SJason Hu };
1850581ce09SJason Hu MODULE_DEVICE_TABLE(acpi, goldfish_events_acpi_match);
1860581ce09SJason Hu #endif
1870581ce09SJason Hu
1886f2ac009SBrian Swetland static struct platform_driver events_driver = {
1896f2ac009SBrian Swetland .probe = events_probe,
1906f2ac009SBrian Swetland .driver = {
1916f2ac009SBrian Swetland .name = "goldfish_events",
1928c5dc5a1SGreg Hackmann .of_match_table = goldfish_events_of_match,
1930581ce09SJason Hu .acpi_match_table = ACPI_PTR(goldfish_events_acpi_match),
1946f2ac009SBrian Swetland },
1956f2ac009SBrian Swetland };
1966f2ac009SBrian Swetland
1976f2ac009SBrian Swetland module_platform_driver(events_driver);
1986f2ac009SBrian Swetland
1996f2ac009SBrian Swetland MODULE_AUTHOR("Brian Swetland");
2006f2ac009SBrian Swetland MODULE_DESCRIPTION("Goldfish Event Device");
2016f2ac009SBrian Swetland MODULE_LICENSE("GPL");
202