123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345 |
- // SPDX-License-Identifier: GPL-2.0
- /*
- * Support for the four N64 controllers.
- *
- * Copyright (c) 2021 Lauri Kasanen
- */
- #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
- #include <linux/errno.h>
- #include <linux/init.h>
- #include <linux/input.h>
- #include <linux/limits.h>
- #include <linux/kernel.h>
- #include <linux/module.h>
- #include <linux/mutex.h>
- #include <linux/platform_device.h>
- #include <linux/slab.h>
- #include <linux/timer.h>
- MODULE_AUTHOR("Lauri Kasanen <[email protected]>");
- MODULE_DESCRIPTION("Driver for N64 controllers");
- MODULE_LICENSE("GPL");
- #define PIF_RAM 0x1fc007c0
- #define SI_DRAM_REG 0
- #define SI_READ_REG 1
- #define SI_WRITE_REG 4
- #define SI_STATUS_REG 6
- #define SI_STATUS_DMA_BUSY BIT(0)
- #define SI_STATUS_IO_BUSY BIT(1)
- #define N64_CONTROLLER_ID 0x0500
- #define MAX_CONTROLLERS 4
- static const char *n64joy_phys[MAX_CONTROLLERS] = {
- "n64joy/port0",
- "n64joy/port1",
- "n64joy/port2",
- "n64joy/port3",
- };
- struct n64joy_priv {
- u64 si_buf[8] ____cacheline_aligned;
- struct timer_list timer;
- struct mutex n64joy_mutex;
- struct input_dev *n64joy_dev[MAX_CONTROLLERS];
- u32 __iomem *reg_base;
- u8 n64joy_opened;
- };
- struct joydata {
- unsigned int: 16; /* unused */
- unsigned int err: 2;
- unsigned int: 14; /* unused */
- union {
- u32 data;
- struct {
- unsigned int a: 1;
- unsigned int b: 1;
- unsigned int z: 1;
- unsigned int start: 1;
- unsigned int up: 1;
- unsigned int down: 1;
- unsigned int left: 1;
- unsigned int right: 1;
- unsigned int: 2; /* unused */
- unsigned int l: 1;
- unsigned int r: 1;
- unsigned int c_up: 1;
- unsigned int c_down: 1;
- unsigned int c_left: 1;
- unsigned int c_right: 1;
- signed int x: 8;
- signed int y: 8;
- };
- };
- };
- static void n64joy_write_reg(u32 __iomem *reg_base, const u8 reg, const u32 value)
- {
- writel(value, reg_base + reg);
- }
- static u32 n64joy_read_reg(u32 __iomem *reg_base, const u8 reg)
- {
- return readl(reg_base + reg);
- }
- static void n64joy_wait_si_dma(u32 __iomem *reg_base)
- {
- while (n64joy_read_reg(reg_base, SI_STATUS_REG) &
- (SI_STATUS_DMA_BUSY | SI_STATUS_IO_BUSY))
- cpu_relax();
- }
- static void n64joy_exec_pif(struct n64joy_priv *priv, const u64 in[8])
- {
- unsigned long flags;
- dma_cache_wback_inv((unsigned long) in, 8 * 8);
- dma_cache_inv((unsigned long) priv->si_buf, 8 * 8);
- local_irq_save(flags);
- n64joy_wait_si_dma(priv->reg_base);
- barrier();
- n64joy_write_reg(priv->reg_base, SI_DRAM_REG, virt_to_phys(in));
- barrier();
- n64joy_write_reg(priv->reg_base, SI_WRITE_REG, PIF_RAM);
- barrier();
- n64joy_wait_si_dma(priv->reg_base);
- barrier();
- n64joy_write_reg(priv->reg_base, SI_DRAM_REG, virt_to_phys(priv->si_buf));
- barrier();
- n64joy_write_reg(priv->reg_base, SI_READ_REG, PIF_RAM);
- barrier();
- n64joy_wait_si_dma(priv->reg_base);
- local_irq_restore(flags);
- }
- static const u64 polldata[] ____cacheline_aligned = {
- 0xff010401ffffffff,
- 0xff010401ffffffff,
- 0xff010401ffffffff,
- 0xff010401ffffffff,
- 0xfe00000000000000,
- 0,
- 0,
- 1
- };
- static void n64joy_poll(struct timer_list *t)
- {
- const struct joydata *data;
- struct n64joy_priv *priv = container_of(t, struct n64joy_priv, timer);
- struct input_dev *dev;
- u32 i;
- n64joy_exec_pif(priv, polldata);
- data = (struct joydata *) priv->si_buf;
- for (i = 0; i < MAX_CONTROLLERS; i++) {
- if (!priv->n64joy_dev[i])
- continue;
- dev = priv->n64joy_dev[i];
- /* d-pad */
- input_report_key(dev, BTN_DPAD_UP, data[i].up);
- input_report_key(dev, BTN_DPAD_DOWN, data[i].down);
- input_report_key(dev, BTN_DPAD_LEFT, data[i].left);
- input_report_key(dev, BTN_DPAD_RIGHT, data[i].right);
- /* c buttons */
- input_report_key(dev, BTN_FORWARD, data[i].c_up);
- input_report_key(dev, BTN_BACK, data[i].c_down);
- input_report_key(dev, BTN_LEFT, data[i].c_left);
- input_report_key(dev, BTN_RIGHT, data[i].c_right);
- /* matching buttons */
- input_report_key(dev, BTN_START, data[i].start);
- input_report_key(dev, BTN_Z, data[i].z);
- /* remaining ones: a, b, l, r */
- input_report_key(dev, BTN_0, data[i].a);
- input_report_key(dev, BTN_1, data[i].b);
- input_report_key(dev, BTN_2, data[i].l);
- input_report_key(dev, BTN_3, data[i].r);
- input_report_abs(dev, ABS_X, data[i].x);
- input_report_abs(dev, ABS_Y, data[i].y);
- input_sync(dev);
- }
- mod_timer(&priv->timer, jiffies + msecs_to_jiffies(16));
- }
- static int n64joy_open(struct input_dev *dev)
- {
- struct n64joy_priv *priv = input_get_drvdata(dev);
- int err;
- err = mutex_lock_interruptible(&priv->n64joy_mutex);
- if (err)
- return err;
- if (!priv->n64joy_opened) {
- /*
- * We could use the vblank irq, but it's not important if
- * the poll point slightly changes.
- */
- timer_setup(&priv->timer, n64joy_poll, 0);
- mod_timer(&priv->timer, jiffies + msecs_to_jiffies(16));
- }
- priv->n64joy_opened++;
- mutex_unlock(&priv->n64joy_mutex);
- return err;
- }
- static void n64joy_close(struct input_dev *dev)
- {
- struct n64joy_priv *priv = input_get_drvdata(dev);
- mutex_lock(&priv->n64joy_mutex);
- if (!--priv->n64joy_opened)
- del_timer_sync(&priv->timer);
- mutex_unlock(&priv->n64joy_mutex);
- }
- static const u64 __initconst scandata[] ____cacheline_aligned = {
- 0xff010300ffffffff,
- 0xff010300ffffffff,
- 0xff010300ffffffff,
- 0xff010300ffffffff,
- 0xfe00000000000000,
- 0,
- 0,
- 1
- };
- /*
- * The target device is embedded and RAM-constrained. We save RAM
- * by initializing in __init code that gets dropped late in boot.
- * For the same reason there is no module or unloading support.
- */
- static int __init n64joy_probe(struct platform_device *pdev)
- {
- const struct joydata *data;
- struct n64joy_priv *priv;
- struct input_dev *dev;
- int err = 0;
- u32 i, j, found = 0;
- priv = kzalloc(sizeof(struct n64joy_priv), GFP_KERNEL);
- if (!priv)
- return -ENOMEM;
- mutex_init(&priv->n64joy_mutex);
- priv->reg_base = devm_platform_ioremap_resource(pdev, 0);
- if (IS_ERR(priv->reg_base)) {
- err = PTR_ERR(priv->reg_base);
- goto fail;
- }
- /* The controllers are not hotpluggable, so we can scan in init */
- n64joy_exec_pif(priv, scandata);
- data = (struct joydata *) priv->si_buf;
- for (i = 0; i < MAX_CONTROLLERS; i++) {
- if (!data[i].err && data[i].data >> 16 == N64_CONTROLLER_ID) {
- found++;
- dev = priv->n64joy_dev[i] = input_allocate_device();
- if (!priv->n64joy_dev[i]) {
- err = -ENOMEM;
- goto fail;
- }
- input_set_drvdata(dev, priv);
- dev->name = "N64 controller";
- dev->phys = n64joy_phys[i];
- dev->id.bustype = BUS_HOST;
- dev->id.vendor = 0;
- dev->id.product = data[i].data >> 16;
- dev->id.version = 0;
- dev->dev.parent = &pdev->dev;
- dev->open = n64joy_open;
- dev->close = n64joy_close;
- /* d-pad */
- input_set_capability(dev, EV_KEY, BTN_DPAD_UP);
- input_set_capability(dev, EV_KEY, BTN_DPAD_DOWN);
- input_set_capability(dev, EV_KEY, BTN_DPAD_LEFT);
- input_set_capability(dev, EV_KEY, BTN_DPAD_RIGHT);
- /* c buttons */
- input_set_capability(dev, EV_KEY, BTN_LEFT);
- input_set_capability(dev, EV_KEY, BTN_RIGHT);
- input_set_capability(dev, EV_KEY, BTN_FORWARD);
- input_set_capability(dev, EV_KEY, BTN_BACK);
- /* matching buttons */
- input_set_capability(dev, EV_KEY, BTN_START);
- input_set_capability(dev, EV_KEY, BTN_Z);
- /* remaining ones: a, b, l, r */
- input_set_capability(dev, EV_KEY, BTN_0);
- input_set_capability(dev, EV_KEY, BTN_1);
- input_set_capability(dev, EV_KEY, BTN_2);
- input_set_capability(dev, EV_KEY, BTN_3);
- for (j = 0; j < 2; j++)
- input_set_abs_params(dev, ABS_X + j,
- S8_MIN, S8_MAX, 0, 0);
- err = input_register_device(dev);
- if (err) {
- input_free_device(dev);
- goto fail;
- }
- }
- }
- pr_info("%u controller(s) connected\n", found);
- if (!found)
- return -ENODEV;
- return 0;
- fail:
- for (i = 0; i < MAX_CONTROLLERS; i++) {
- if (!priv->n64joy_dev[i])
- continue;
- input_unregister_device(priv->n64joy_dev[i]);
- }
- return err;
- }
- static struct platform_driver n64joy_driver = {
- .driver = {
- .name = "n64joy",
- },
- };
- static int __init n64joy_init(void)
- {
- return platform_driver_probe(&n64joy_driver, n64joy_probe);
- }
- module_init(n64joy_init);
|