File Index
Files and directories
Sign in
FILE

n64joy.c

File preview with syntax highlighting.

linux-6.19.14/drivers/input/joystick/n64joy.c C
  1// SPDX-License-Identifier: GPL-2.0
  2/*
  3 * Support for the four N64 controllers.
  4 *
  5 * Copyright (c) 2021 Lauri Kasanen
  6 */
  7
  8#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
  9
 10#include <linux/errno.h>
 11#include <linux/init.h>
 12#include <linux/input.h>
 13#include <linux/limits.h>
 14#include <linux/kernel.h>
 15#include <linux/module.h>
 16#include <linux/mutex.h>
 17#include <linux/platform_device.h>
 18#include <linux/slab.h>
 19#include <linux/timer.h>
 20
 21MODULE_AUTHOR("Lauri Kasanen <cand@gmx.com>");
 22MODULE_DESCRIPTION("Driver for N64 controllers");
 23MODULE_LICENSE("GPL");
 24
 25#define PIF_RAM 0x1fc007c0
 26
 27#define SI_DRAM_REG 0
 28#define SI_READ_REG 1
 29#define SI_WRITE_REG 4
 30#define SI_STATUS_REG 6
 31
 32#define SI_STATUS_DMA_BUSY  BIT(0)
 33#define SI_STATUS_IO_BUSY   BIT(1)
 34
 35#define N64_CONTROLLER_ID 0x0500
 36
 37#define MAX_CONTROLLERS 4
 38
 39static const char *n64joy_phys[MAX_CONTROLLERS] = {
 40	"n64joy/port0",
 41	"n64joy/port1",
 42	"n64joy/port2",
 43	"n64joy/port3",
 44};
 45
 46struct n64joy_priv {
 47	u64 si_buf[8] ____cacheline_aligned;
 48	struct timer_list timer;
 49	struct mutex n64joy_mutex;
 50	struct input_dev *n64joy_dev[MAX_CONTROLLERS];
 51	u32 __iomem *reg_base;
 52	u8 n64joy_opened;
 53};
 54
 55struct joydata {
 56	unsigned int: 16; /* unused */
 57	unsigned int err: 2;
 58	unsigned int: 14; /* unused */
 59
 60	union {
 61		u32 data;
 62
 63		struct {
 64			unsigned int a: 1;
 65			unsigned int b: 1;
 66			unsigned int z: 1;
 67			unsigned int start: 1;
 68			unsigned int up: 1;
 69			unsigned int down: 1;
 70			unsigned int left: 1;
 71			unsigned int right: 1;
 72			unsigned int: 2; /* unused */
 73			unsigned int l: 1;
 74			unsigned int r: 1;
 75			unsigned int c_up: 1;
 76			unsigned int c_down: 1;
 77			unsigned int c_left: 1;
 78			unsigned int c_right: 1;
 79			signed int x: 8;
 80			signed int y: 8;
 81		};
 82	};
 83};
 84
 85static void n64joy_write_reg(u32 __iomem *reg_base, const u8 reg, const u32 value)
 86{
 87	writel(value, reg_base + reg);
 88}
 89
 90static u32 n64joy_read_reg(u32 __iomem *reg_base, const u8 reg)
 91{
 92	return readl(reg_base + reg);
 93}
 94
 95static void n64joy_wait_si_dma(u32 __iomem *reg_base)
 96{
 97	while (n64joy_read_reg(reg_base, SI_STATUS_REG) &
 98	       (SI_STATUS_DMA_BUSY | SI_STATUS_IO_BUSY))
 99		cpu_relax();
100}
101
102static void n64joy_exec_pif(struct n64joy_priv *priv, const u64 in[8])
103{
104	unsigned long flags;
105
106	dma_cache_wback_inv((unsigned long) in, 8 * 8);
107	dma_cache_inv((unsigned long) priv->si_buf, 8 * 8);
108
109	local_irq_save(flags);
110
111	n64joy_wait_si_dma(priv->reg_base);
112
113	barrier();
114	n64joy_write_reg(priv->reg_base, SI_DRAM_REG, virt_to_phys(in));
115	barrier();
116	n64joy_write_reg(priv->reg_base, SI_WRITE_REG, PIF_RAM);
117	barrier();
118
119	n64joy_wait_si_dma(priv->reg_base);
120
121	barrier();
122	n64joy_write_reg(priv->reg_base, SI_DRAM_REG, virt_to_phys(priv->si_buf));
123	barrier();
124	n64joy_write_reg(priv->reg_base, SI_READ_REG, PIF_RAM);
125	barrier();
126
127	n64joy_wait_si_dma(priv->reg_base);
128
129	local_irq_restore(flags);
130}
131
132static const u64 polldata[] ____cacheline_aligned = {
133	0xff010401ffffffff,
134	0xff010401ffffffff,
135	0xff010401ffffffff,
136	0xff010401ffffffff,
137	0xfe00000000000000,
138	0,
139	0,
140	1
141};
142
143static void n64joy_poll(struct timer_list *t)
144{
145	const struct joydata *data;
146	struct n64joy_priv *priv = container_of(t, struct n64joy_priv, timer);
147	struct input_dev *dev;
148	u32 i;
149
150	n64joy_exec_pif(priv, polldata);
151
152	data = (struct joydata *) priv->si_buf;
153
154	for (i = 0; i < MAX_CONTROLLERS; i++) {
155		if (!priv->n64joy_dev[i])
156			continue;
157
158		dev = priv->n64joy_dev[i];
159
160		/* d-pad */
161		input_report_key(dev, BTN_DPAD_UP, data[i].up);
162		input_report_key(dev, BTN_DPAD_DOWN, data[i].down);
163		input_report_key(dev, BTN_DPAD_LEFT, data[i].left);
164		input_report_key(dev, BTN_DPAD_RIGHT, data[i].right);
165
166		/* c buttons */
167		input_report_key(dev, BTN_FORWARD, data[i].c_up);
168		input_report_key(dev, BTN_BACK, data[i].c_down);
169		input_report_key(dev, BTN_LEFT, data[i].c_left);
170		input_report_key(dev, BTN_RIGHT, data[i].c_right);
171
172		/* matching buttons */
173		input_report_key(dev, BTN_START, data[i].start);
174		input_report_key(dev, BTN_Z, data[i].z);
175
176		/* remaining ones: a, b, l, r */
177		input_report_key(dev, BTN_0, data[i].a);
178		input_report_key(dev, BTN_1, data[i].b);
179		input_report_key(dev, BTN_2, data[i].l);
180		input_report_key(dev, BTN_3, data[i].r);
181
182		input_report_abs(dev, ABS_X, data[i].x);
183		input_report_abs(dev, ABS_Y, data[i].y);
184
185		input_sync(dev);
186	}
187
188	mod_timer(&priv->timer, jiffies + msecs_to_jiffies(16));
189}
190
191static int n64joy_open(struct input_dev *dev)
192{
193	struct n64joy_priv *priv = input_get_drvdata(dev);
194
195	scoped_guard(mutex_intr, &priv->n64joy_mutex) {
196		if (!priv->n64joy_opened) {
197			/*
198			 * We could use the vblank irq, but it's not important
199			 * if the poll point slightly changes.
200			 */
201			timer_setup(&priv->timer, n64joy_poll, 0);
202			mod_timer(&priv->timer, jiffies + msecs_to_jiffies(16));
203		}
204
205		priv->n64joy_opened++;
206		return 0;
207	}
208
209	return -EINTR;
210}
211
212static void n64joy_close(struct input_dev *dev)
213{
214	struct n64joy_priv *priv = input_get_drvdata(dev);
215
216	guard(mutex)(&priv->n64joy_mutex);
217
218	if (!--priv->n64joy_opened)
219		timer_delete_sync(&priv->timer);
220}
221
222static const u64 __initconst scandata[] ____cacheline_aligned = {
223	0xff010300ffffffff,
224	0xff010300ffffffff,
225	0xff010300ffffffff,
226	0xff010300ffffffff,
227	0xfe00000000000000,
228	0,
229	0,
230	1
231};
232
233/*
234 * The target device is embedded and RAM-constrained. We save RAM
235 * by initializing in __init code that gets dropped late in boot.
236 * For the same reason there is no module or unloading support.
237 */
238static int __init n64joy_probe(struct platform_device *pdev)
239{
240	const struct joydata *data;
241	struct n64joy_priv *priv;
242	struct input_dev *dev;
243	int err = 0;
244	u32 i, j, found = 0;
245
246	priv = kzalloc(sizeof(*priv), GFP_KERNEL);
247	if (!priv)
248		return -ENOMEM;
249	mutex_init(&priv->n64joy_mutex);
250
251	priv->reg_base = devm_platform_ioremap_resource(pdev, 0);
252	if (IS_ERR(priv->reg_base)) {
253		err = PTR_ERR(priv->reg_base);
254		goto fail;
255	}
256
257	/* The controllers are not hotpluggable, so we can scan in init */
258	n64joy_exec_pif(priv, scandata);
259
260	data = (struct joydata *) priv->si_buf;
261
262	for (i = 0; i < MAX_CONTROLLERS; i++) {
263		if (!data[i].err && data[i].data >> 16 == N64_CONTROLLER_ID) {
264			found++;
265
266			dev = priv->n64joy_dev[i] = input_allocate_device();
267			if (!priv->n64joy_dev[i]) {
268				err = -ENOMEM;
269				goto fail;
270			}
271
272			input_set_drvdata(dev, priv);
273
274			dev->name = "N64 controller";
275			dev->phys = n64joy_phys[i];
276			dev->id.bustype = BUS_HOST;
277			dev->id.vendor = 0;
278			dev->id.product = data[i].data >> 16;
279			dev->id.version = 0;
280			dev->dev.parent = &pdev->dev;
281
282			dev->open = n64joy_open;
283			dev->close = n64joy_close;
284
285			/* d-pad */
286			input_set_capability(dev, EV_KEY, BTN_DPAD_UP);
287			input_set_capability(dev, EV_KEY, BTN_DPAD_DOWN);
288			input_set_capability(dev, EV_KEY, BTN_DPAD_LEFT);
289			input_set_capability(dev, EV_KEY, BTN_DPAD_RIGHT);
290			/* c buttons */
291			input_set_capability(dev, EV_KEY, BTN_LEFT);
292			input_set_capability(dev, EV_KEY, BTN_RIGHT);
293			input_set_capability(dev, EV_KEY, BTN_FORWARD);
294			input_set_capability(dev, EV_KEY, BTN_BACK);
295			/* matching buttons */
296			input_set_capability(dev, EV_KEY, BTN_START);
297			input_set_capability(dev, EV_KEY, BTN_Z);
298			/* remaining ones: a, b, l, r */
299			input_set_capability(dev, EV_KEY, BTN_0);
300			input_set_capability(dev, EV_KEY, BTN_1);
301			input_set_capability(dev, EV_KEY, BTN_2);
302			input_set_capability(dev, EV_KEY, BTN_3);
303
304			for (j = 0; j < 2; j++)
305				input_set_abs_params(dev, ABS_X + j,
306						     S8_MIN, S8_MAX, 0, 0);
307
308			err = input_register_device(dev);
309			if (err) {
310				input_free_device(dev);
311				goto fail;
312			}
313		}
314	}
315
316	pr_info("%u controller(s) connected\n", found);
317
318	if (!found)
319		return -ENODEV;
320
321	return 0;
322fail:
323	for (i = 0; i < MAX_CONTROLLERS; i++) {
324		if (!priv->n64joy_dev[i])
325			continue;
326		input_unregister_device(priv->n64joy_dev[i]);
327	}
328	return err;
329}
330
331static struct platform_driver n64joy_driver = {
332	.driver = {
333		.name = "n64joy",
334	},
335};
336
337static int __init n64joy_init(void)
338{
339	return platform_driver_probe(&n64joy_driver, n64joy_probe);
340}
341
342module_init(n64joy_init);
~/ Read-only file preview.