13bdffa8fSLauri Kasanen // SPDX-License-Identifier: GPL-2.0 23bdffa8fSLauri Kasanen /* 33bdffa8fSLauri Kasanen * Support for the four N64 controllers. 43bdffa8fSLauri Kasanen * 53bdffa8fSLauri Kasanen * Copyright (c) 2021 Lauri Kasanen 63bdffa8fSLauri Kasanen */ 73bdffa8fSLauri Kasanen 83bdffa8fSLauri Kasanen #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt 93bdffa8fSLauri Kasanen 103bdffa8fSLauri Kasanen #include <linux/errno.h> 113bdffa8fSLauri Kasanen #include <linux/init.h> 123bdffa8fSLauri Kasanen #include <linux/input.h> 133bdffa8fSLauri Kasanen #include <linux/limits.h> 143bdffa8fSLauri Kasanen #include <linux/kernel.h> 153bdffa8fSLauri Kasanen #include <linux/module.h> 163bdffa8fSLauri Kasanen #include <linux/mutex.h> 173bdffa8fSLauri Kasanen #include <linux/platform_device.h> 183bdffa8fSLauri Kasanen #include <linux/slab.h> 193bdffa8fSLauri Kasanen #include <linux/timer.h> 203bdffa8fSLauri Kasanen 213bdffa8fSLauri Kasanen MODULE_AUTHOR("Lauri Kasanen <cand@gmx.com>"); 223bdffa8fSLauri Kasanen MODULE_DESCRIPTION("Driver for N64 controllers"); 233bdffa8fSLauri Kasanen MODULE_LICENSE("GPL"); 243bdffa8fSLauri Kasanen 253bdffa8fSLauri Kasanen #define PIF_RAM 0x1fc007c0 263bdffa8fSLauri Kasanen 273bdffa8fSLauri Kasanen #define SI_DRAM_REG 0 283bdffa8fSLauri Kasanen #define SI_READ_REG 1 293bdffa8fSLauri Kasanen #define SI_WRITE_REG 4 303bdffa8fSLauri Kasanen #define SI_STATUS_REG 6 313bdffa8fSLauri Kasanen 323bdffa8fSLauri Kasanen #define SI_STATUS_DMA_BUSY BIT(0) 333bdffa8fSLauri Kasanen #define SI_STATUS_IO_BUSY BIT(1) 343bdffa8fSLauri Kasanen 353bdffa8fSLauri Kasanen #define N64_CONTROLLER_ID 0x0500 363bdffa8fSLauri Kasanen 373bdffa8fSLauri Kasanen #define MAX_CONTROLLERS 4 383bdffa8fSLauri Kasanen 393bdffa8fSLauri Kasanen static const char *n64joy_phys[MAX_CONTROLLERS] = { 403bdffa8fSLauri Kasanen "n64joy/port0", 413bdffa8fSLauri Kasanen "n64joy/port1", 423bdffa8fSLauri Kasanen "n64joy/port2", 433bdffa8fSLauri Kasanen "n64joy/port3", 443bdffa8fSLauri Kasanen }; 453bdffa8fSLauri Kasanen 463bdffa8fSLauri Kasanen struct n64joy_priv { 473bdffa8fSLauri Kasanen u64 si_buf[8] ____cacheline_aligned; 483bdffa8fSLauri Kasanen struct timer_list timer; 493bdffa8fSLauri Kasanen struct mutex n64joy_mutex; 503bdffa8fSLauri Kasanen struct input_dev *n64joy_dev[MAX_CONTROLLERS]; 513bdffa8fSLauri Kasanen u32 __iomem *reg_base; 523bdffa8fSLauri Kasanen u8 n64joy_opened; 533bdffa8fSLauri Kasanen }; 543bdffa8fSLauri Kasanen 553bdffa8fSLauri Kasanen struct joydata { 563bdffa8fSLauri Kasanen unsigned int: 16; /* unused */ 573bdffa8fSLauri Kasanen unsigned int err: 2; 583bdffa8fSLauri Kasanen unsigned int: 14; /* unused */ 593bdffa8fSLauri Kasanen 603bdffa8fSLauri Kasanen union { 613bdffa8fSLauri Kasanen u32 data; 623bdffa8fSLauri Kasanen 633bdffa8fSLauri Kasanen struct { 643bdffa8fSLauri Kasanen unsigned int a: 1; 653bdffa8fSLauri Kasanen unsigned int b: 1; 663bdffa8fSLauri Kasanen unsigned int z: 1; 673bdffa8fSLauri Kasanen unsigned int start: 1; 683bdffa8fSLauri Kasanen unsigned int up: 1; 693bdffa8fSLauri Kasanen unsigned int down: 1; 703bdffa8fSLauri Kasanen unsigned int left: 1; 713bdffa8fSLauri Kasanen unsigned int right: 1; 723bdffa8fSLauri Kasanen unsigned int: 2; /* unused */ 733bdffa8fSLauri Kasanen unsigned int l: 1; 743bdffa8fSLauri Kasanen unsigned int r: 1; 753bdffa8fSLauri Kasanen unsigned int c_up: 1; 763bdffa8fSLauri Kasanen unsigned int c_down: 1; 773bdffa8fSLauri Kasanen unsigned int c_left: 1; 783bdffa8fSLauri Kasanen unsigned int c_right: 1; 793bdffa8fSLauri Kasanen signed int x: 8; 803bdffa8fSLauri Kasanen signed int y: 8; 813bdffa8fSLauri Kasanen }; 823bdffa8fSLauri Kasanen }; 833bdffa8fSLauri Kasanen }; 843bdffa8fSLauri Kasanen 853bdffa8fSLauri Kasanen static void n64joy_write_reg(u32 __iomem *reg_base, const u8 reg, const u32 value) 863bdffa8fSLauri Kasanen { 873bdffa8fSLauri Kasanen writel(value, reg_base + reg); 883bdffa8fSLauri Kasanen } 893bdffa8fSLauri Kasanen 903bdffa8fSLauri Kasanen static u32 n64joy_read_reg(u32 __iomem *reg_base, const u8 reg) 913bdffa8fSLauri Kasanen { 923bdffa8fSLauri Kasanen return readl(reg_base + reg); 933bdffa8fSLauri Kasanen } 943bdffa8fSLauri Kasanen 953bdffa8fSLauri Kasanen static void n64joy_wait_si_dma(u32 __iomem *reg_base) 963bdffa8fSLauri Kasanen { 973bdffa8fSLauri Kasanen while (n64joy_read_reg(reg_base, SI_STATUS_REG) & 983bdffa8fSLauri Kasanen (SI_STATUS_DMA_BUSY | SI_STATUS_IO_BUSY)) 993bdffa8fSLauri Kasanen cpu_relax(); 1003bdffa8fSLauri Kasanen } 1013bdffa8fSLauri Kasanen 1023bdffa8fSLauri Kasanen static void n64joy_exec_pif(struct n64joy_priv *priv, const u64 in[8]) 1033bdffa8fSLauri Kasanen { 1043bdffa8fSLauri Kasanen unsigned long flags; 1053bdffa8fSLauri Kasanen 1063bdffa8fSLauri Kasanen dma_cache_wback_inv((unsigned long) in, 8 * 8); 1073bdffa8fSLauri Kasanen dma_cache_inv((unsigned long) priv->si_buf, 8 * 8); 1083bdffa8fSLauri Kasanen 1093bdffa8fSLauri Kasanen local_irq_save(flags); 1103bdffa8fSLauri Kasanen 1113bdffa8fSLauri Kasanen n64joy_wait_si_dma(priv->reg_base); 1123bdffa8fSLauri Kasanen 1133bdffa8fSLauri Kasanen barrier(); 1143bdffa8fSLauri Kasanen n64joy_write_reg(priv->reg_base, SI_DRAM_REG, virt_to_phys(in)); 1153bdffa8fSLauri Kasanen barrier(); 1163bdffa8fSLauri Kasanen n64joy_write_reg(priv->reg_base, SI_WRITE_REG, PIF_RAM); 1173bdffa8fSLauri Kasanen barrier(); 1183bdffa8fSLauri Kasanen 1193bdffa8fSLauri Kasanen n64joy_wait_si_dma(priv->reg_base); 1203bdffa8fSLauri Kasanen 1213bdffa8fSLauri Kasanen barrier(); 1223bdffa8fSLauri Kasanen n64joy_write_reg(priv->reg_base, SI_DRAM_REG, virt_to_phys(priv->si_buf)); 1233bdffa8fSLauri Kasanen barrier(); 1243bdffa8fSLauri Kasanen n64joy_write_reg(priv->reg_base, SI_READ_REG, PIF_RAM); 1253bdffa8fSLauri Kasanen barrier(); 1263bdffa8fSLauri Kasanen 1273bdffa8fSLauri Kasanen n64joy_wait_si_dma(priv->reg_base); 1283bdffa8fSLauri Kasanen 1293bdffa8fSLauri Kasanen local_irq_restore(flags); 1303bdffa8fSLauri Kasanen } 1313bdffa8fSLauri Kasanen 1323bdffa8fSLauri Kasanen static const u64 polldata[] ____cacheline_aligned = { 1333bdffa8fSLauri Kasanen 0xff010401ffffffff, 1343bdffa8fSLauri Kasanen 0xff010401ffffffff, 1353bdffa8fSLauri Kasanen 0xff010401ffffffff, 1363bdffa8fSLauri Kasanen 0xff010401ffffffff, 1373bdffa8fSLauri Kasanen 0xfe00000000000000, 1383bdffa8fSLauri Kasanen 0, 1393bdffa8fSLauri Kasanen 0, 1403bdffa8fSLauri Kasanen 1 1413bdffa8fSLauri Kasanen }; 1423bdffa8fSLauri Kasanen 1433bdffa8fSLauri Kasanen static void n64joy_poll(struct timer_list *t) 1443bdffa8fSLauri Kasanen { 1453bdffa8fSLauri Kasanen const struct joydata *data; 1463bdffa8fSLauri Kasanen struct n64joy_priv *priv = container_of(t, struct n64joy_priv, timer); 1473bdffa8fSLauri Kasanen struct input_dev *dev; 1483bdffa8fSLauri Kasanen u32 i; 1493bdffa8fSLauri Kasanen 1503bdffa8fSLauri Kasanen n64joy_exec_pif(priv, polldata); 1513bdffa8fSLauri Kasanen 1523bdffa8fSLauri Kasanen data = (struct joydata *) priv->si_buf; 1533bdffa8fSLauri Kasanen 1543bdffa8fSLauri Kasanen for (i = 0; i < MAX_CONTROLLERS; i++) { 1553bdffa8fSLauri Kasanen if (!priv->n64joy_dev[i]) 1563bdffa8fSLauri Kasanen continue; 1573bdffa8fSLauri Kasanen 1583bdffa8fSLauri Kasanen dev = priv->n64joy_dev[i]; 1593bdffa8fSLauri Kasanen 1603bdffa8fSLauri Kasanen /* d-pad */ 1613bdffa8fSLauri Kasanen input_report_key(dev, BTN_DPAD_UP, data[i].up); 1623bdffa8fSLauri Kasanen input_report_key(dev, BTN_DPAD_DOWN, data[i].down); 1633bdffa8fSLauri Kasanen input_report_key(dev, BTN_DPAD_LEFT, data[i].left); 1643bdffa8fSLauri Kasanen input_report_key(dev, BTN_DPAD_RIGHT, data[i].right); 1653bdffa8fSLauri Kasanen 1663bdffa8fSLauri Kasanen /* c buttons */ 1673bdffa8fSLauri Kasanen input_report_key(dev, BTN_FORWARD, data[i].c_up); 1683bdffa8fSLauri Kasanen input_report_key(dev, BTN_BACK, data[i].c_down); 1693bdffa8fSLauri Kasanen input_report_key(dev, BTN_LEFT, data[i].c_left); 1703bdffa8fSLauri Kasanen input_report_key(dev, BTN_RIGHT, data[i].c_right); 1713bdffa8fSLauri Kasanen 1723bdffa8fSLauri Kasanen /* matching buttons */ 1733bdffa8fSLauri Kasanen input_report_key(dev, BTN_START, data[i].start); 1743bdffa8fSLauri Kasanen input_report_key(dev, BTN_Z, data[i].z); 1753bdffa8fSLauri Kasanen 1763bdffa8fSLauri Kasanen /* remaining ones: a, b, l, r */ 1773bdffa8fSLauri Kasanen input_report_key(dev, BTN_0, data[i].a); 1783bdffa8fSLauri Kasanen input_report_key(dev, BTN_1, data[i].b); 1793bdffa8fSLauri Kasanen input_report_key(dev, BTN_2, data[i].l); 1803bdffa8fSLauri Kasanen input_report_key(dev, BTN_3, data[i].r); 1813bdffa8fSLauri Kasanen 1823bdffa8fSLauri Kasanen input_report_abs(dev, ABS_X, data[i].x); 1833bdffa8fSLauri Kasanen input_report_abs(dev, ABS_Y, data[i].y); 1843bdffa8fSLauri Kasanen 1853bdffa8fSLauri Kasanen input_sync(dev); 1863bdffa8fSLauri Kasanen } 1873bdffa8fSLauri Kasanen 1883bdffa8fSLauri Kasanen mod_timer(&priv->timer, jiffies + msecs_to_jiffies(16)); 1893bdffa8fSLauri Kasanen } 1903bdffa8fSLauri Kasanen 1913bdffa8fSLauri Kasanen static int n64joy_open(struct input_dev *dev) 1923bdffa8fSLauri Kasanen { 1933bdffa8fSLauri Kasanen struct n64joy_priv *priv = input_get_drvdata(dev); 1943bdffa8fSLauri Kasanen int err; 1953bdffa8fSLauri Kasanen 1963bdffa8fSLauri Kasanen err = mutex_lock_interruptible(&priv->n64joy_mutex); 1973bdffa8fSLauri Kasanen if (err) 1983bdffa8fSLauri Kasanen return err; 1993bdffa8fSLauri Kasanen 2003bdffa8fSLauri Kasanen if (!priv->n64joy_opened) { 2013bdffa8fSLauri Kasanen /* 2023bdffa8fSLauri Kasanen * We could use the vblank irq, but it's not important if 2033bdffa8fSLauri Kasanen * the poll point slightly changes. 2043bdffa8fSLauri Kasanen */ 2053bdffa8fSLauri Kasanen timer_setup(&priv->timer, n64joy_poll, 0); 2063bdffa8fSLauri Kasanen mod_timer(&priv->timer, jiffies + msecs_to_jiffies(16)); 2073bdffa8fSLauri Kasanen } 2083bdffa8fSLauri Kasanen 2093bdffa8fSLauri Kasanen priv->n64joy_opened++; 2103bdffa8fSLauri Kasanen 2113bdffa8fSLauri Kasanen mutex_unlock(&priv->n64joy_mutex); 2123bdffa8fSLauri Kasanen return err; 2133bdffa8fSLauri Kasanen } 2143bdffa8fSLauri Kasanen 2153bdffa8fSLauri Kasanen static void n64joy_close(struct input_dev *dev) 2163bdffa8fSLauri Kasanen { 2173bdffa8fSLauri Kasanen struct n64joy_priv *priv = input_get_drvdata(dev); 2183bdffa8fSLauri Kasanen 2193bdffa8fSLauri Kasanen mutex_lock(&priv->n64joy_mutex); 2203bdffa8fSLauri Kasanen if (!--priv->n64joy_opened) 2213bdffa8fSLauri Kasanen del_timer_sync(&priv->timer); 2223bdffa8fSLauri Kasanen mutex_unlock(&priv->n64joy_mutex); 2233bdffa8fSLauri Kasanen } 2243bdffa8fSLauri Kasanen 2253bdffa8fSLauri Kasanen static const u64 __initconst scandata[] ____cacheline_aligned = { 2263bdffa8fSLauri Kasanen 0xff010300ffffffff, 2273bdffa8fSLauri Kasanen 0xff010300ffffffff, 2283bdffa8fSLauri Kasanen 0xff010300ffffffff, 2293bdffa8fSLauri Kasanen 0xff010300ffffffff, 2303bdffa8fSLauri Kasanen 0xfe00000000000000, 2313bdffa8fSLauri Kasanen 0, 2323bdffa8fSLauri Kasanen 0, 2333bdffa8fSLauri Kasanen 1 2343bdffa8fSLauri Kasanen }; 2353bdffa8fSLauri Kasanen 2363bdffa8fSLauri Kasanen /* 2373bdffa8fSLauri Kasanen * The target device is embedded and RAM-constrained. We save RAM 2383bdffa8fSLauri Kasanen * by initializing in __init code that gets dropped late in boot. 2393bdffa8fSLauri Kasanen * For the same reason there is no module or unloading support. 2403bdffa8fSLauri Kasanen */ 2413bdffa8fSLauri Kasanen static int __init n64joy_probe(struct platform_device *pdev) 2423bdffa8fSLauri Kasanen { 2433bdffa8fSLauri Kasanen const struct joydata *data; 2443bdffa8fSLauri Kasanen struct n64joy_priv *priv; 2453bdffa8fSLauri Kasanen struct input_dev *dev; 2463bdffa8fSLauri Kasanen int err = 0; 2473bdffa8fSLauri Kasanen u32 i, j, found = 0; 2483bdffa8fSLauri Kasanen 2493bdffa8fSLauri Kasanen priv = kzalloc(sizeof(struct n64joy_priv), GFP_KERNEL); 2503bdffa8fSLauri Kasanen if (!priv) 2513bdffa8fSLauri Kasanen return -ENOMEM; 2523bdffa8fSLauri Kasanen mutex_init(&priv->n64joy_mutex); 2533bdffa8fSLauri Kasanen 2543bdffa8fSLauri Kasanen priv->reg_base = devm_platform_ioremap_resource(pdev, 0); 255*2d8aaa17SWei Yongjun if (IS_ERR(priv->reg_base)) { 256*2d8aaa17SWei Yongjun err = PTR_ERR(priv->reg_base); 2573bdffa8fSLauri Kasanen goto fail; 2583bdffa8fSLauri Kasanen } 2593bdffa8fSLauri Kasanen 2603bdffa8fSLauri Kasanen /* The controllers are not hotpluggable, so we can scan in init */ 2613bdffa8fSLauri Kasanen n64joy_exec_pif(priv, scandata); 2623bdffa8fSLauri Kasanen 2633bdffa8fSLauri Kasanen data = (struct joydata *) priv->si_buf; 2643bdffa8fSLauri Kasanen 2653bdffa8fSLauri Kasanen for (i = 0; i < MAX_CONTROLLERS; i++) { 2663bdffa8fSLauri Kasanen if (!data[i].err && data[i].data >> 16 == N64_CONTROLLER_ID) { 2673bdffa8fSLauri Kasanen found++; 2683bdffa8fSLauri Kasanen 2693bdffa8fSLauri Kasanen dev = priv->n64joy_dev[i] = input_allocate_device(); 2703bdffa8fSLauri Kasanen if (!priv->n64joy_dev[i]) { 2713bdffa8fSLauri Kasanen err = -ENOMEM; 2723bdffa8fSLauri Kasanen goto fail; 2733bdffa8fSLauri Kasanen } 2743bdffa8fSLauri Kasanen 2753bdffa8fSLauri Kasanen input_set_drvdata(dev, priv); 2763bdffa8fSLauri Kasanen 2773bdffa8fSLauri Kasanen dev->name = "N64 controller"; 2783bdffa8fSLauri Kasanen dev->phys = n64joy_phys[i]; 2793bdffa8fSLauri Kasanen dev->id.bustype = BUS_HOST; 2803bdffa8fSLauri Kasanen dev->id.vendor = 0; 2813bdffa8fSLauri Kasanen dev->id.product = data[i].data >> 16; 2823bdffa8fSLauri Kasanen dev->id.version = 0; 2833bdffa8fSLauri Kasanen dev->dev.parent = &pdev->dev; 2843bdffa8fSLauri Kasanen 2853bdffa8fSLauri Kasanen dev->open = n64joy_open; 2863bdffa8fSLauri Kasanen dev->close = n64joy_close; 2873bdffa8fSLauri Kasanen 2883bdffa8fSLauri Kasanen /* d-pad */ 2893bdffa8fSLauri Kasanen input_set_capability(dev, EV_KEY, BTN_DPAD_UP); 2903bdffa8fSLauri Kasanen input_set_capability(dev, EV_KEY, BTN_DPAD_DOWN); 2913bdffa8fSLauri Kasanen input_set_capability(dev, EV_KEY, BTN_DPAD_LEFT); 2923bdffa8fSLauri Kasanen input_set_capability(dev, EV_KEY, BTN_DPAD_RIGHT); 2933bdffa8fSLauri Kasanen /* c buttons */ 2943bdffa8fSLauri Kasanen input_set_capability(dev, EV_KEY, BTN_LEFT); 2953bdffa8fSLauri Kasanen input_set_capability(dev, EV_KEY, BTN_RIGHT); 2963bdffa8fSLauri Kasanen input_set_capability(dev, EV_KEY, BTN_FORWARD); 2973bdffa8fSLauri Kasanen input_set_capability(dev, EV_KEY, BTN_BACK); 2983bdffa8fSLauri Kasanen /* matching buttons */ 2993bdffa8fSLauri Kasanen input_set_capability(dev, EV_KEY, BTN_START); 3003bdffa8fSLauri Kasanen input_set_capability(dev, EV_KEY, BTN_Z); 3013bdffa8fSLauri Kasanen /* remaining ones: a, b, l, r */ 3023bdffa8fSLauri Kasanen input_set_capability(dev, EV_KEY, BTN_0); 3033bdffa8fSLauri Kasanen input_set_capability(dev, EV_KEY, BTN_1); 3043bdffa8fSLauri Kasanen input_set_capability(dev, EV_KEY, BTN_2); 3053bdffa8fSLauri Kasanen input_set_capability(dev, EV_KEY, BTN_3); 3063bdffa8fSLauri Kasanen 3073bdffa8fSLauri Kasanen for (j = 0; j < 2; j++) 3083bdffa8fSLauri Kasanen input_set_abs_params(dev, ABS_X + j, 3093bdffa8fSLauri Kasanen S8_MIN, S8_MAX, 0, 0); 3103bdffa8fSLauri Kasanen 3113bdffa8fSLauri Kasanen err = input_register_device(dev); 3123bdffa8fSLauri Kasanen if (err) { 3133bdffa8fSLauri Kasanen input_free_device(dev); 3143bdffa8fSLauri Kasanen goto fail; 3153bdffa8fSLauri Kasanen } 3163bdffa8fSLauri Kasanen } 3173bdffa8fSLauri Kasanen } 3183bdffa8fSLauri Kasanen 3193bdffa8fSLauri Kasanen pr_info("%u controller(s) connected\n", found); 3203bdffa8fSLauri Kasanen 3213bdffa8fSLauri Kasanen if (!found) 3223bdffa8fSLauri Kasanen return -ENODEV; 3233bdffa8fSLauri Kasanen 3243bdffa8fSLauri Kasanen return 0; 3253bdffa8fSLauri Kasanen fail: 3263bdffa8fSLauri Kasanen for (i = 0; i < MAX_CONTROLLERS; i++) { 3273bdffa8fSLauri Kasanen if (!priv->n64joy_dev[i]) 3283bdffa8fSLauri Kasanen continue; 3293bdffa8fSLauri Kasanen input_unregister_device(priv->n64joy_dev[i]); 3303bdffa8fSLauri Kasanen } 3313bdffa8fSLauri Kasanen return err; 3323bdffa8fSLauri Kasanen } 3333bdffa8fSLauri Kasanen 3343bdffa8fSLauri Kasanen static struct platform_driver n64joy_driver = { 3353bdffa8fSLauri Kasanen .driver = { 3363bdffa8fSLauri Kasanen .name = "n64joy", 3373bdffa8fSLauri Kasanen }, 3383bdffa8fSLauri Kasanen }; 3393bdffa8fSLauri Kasanen 3403bdffa8fSLauri Kasanen static int __init n64joy_init(void) 3413bdffa8fSLauri Kasanen { 3423bdffa8fSLauri Kasanen return platform_driver_probe(&n64joy_driver, n64joy_probe); 3433bdffa8fSLauri Kasanen } 3443bdffa8fSLauri Kasanen 3453bdffa8fSLauri Kasanen module_init(n64joy_init); 346