Blame view

drivers/char/tb0219.c 7.11 KB
1a59d1b8e   Thomas Gleixner   treewide: Replace...
1
  // SPDX-License-Identifier: GPL-2.0-or-later
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
2
3
4
  /*
   *  Driver for TANBAC TB0219 base board.
   *
ada8e9514   Yoichi Yuasa   Update Yoichi Yua...
5
   *  Copyright (C) 2005  Yoichi Yuasa <yuasa@linux-mips.org>
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
6
   */
d052d1bef   Russell King   Create platform_d...
7
  #include <linux/platform_device.h>
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
8
9
10
  #include <linux/fs.h>
  #include <linux/init.h>
  #include <linux/module.h>
29abfbd9c   Al Viro   mips: separate ex...
11
  #include <linux/uaccess.h>
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
12
13
14
  
  #include <asm/io.h>
  #include <asm/reboot.h>
6e498c108   Yoichi Yuasa   [PATCH] TB0219: a...
15
16
  #include <asm/vr41xx/giu.h>
  #include <asm/vr41xx/tb0219.h>
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
17

ada8e9514   Yoichi Yuasa   Update Yoichi Yua...
18
  MODULE_AUTHOR("Yoichi Yuasa <yuasa@linux-mips.org>");
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
19
20
21
22
23
24
25
26
27
  MODULE_DESCRIPTION("TANBAC TB0219 base board driver");
  MODULE_LICENSE("GPL");
  
  static int major;	/* default is dynamic major device number */
  module_param(major, int, 0);
  MODULE_PARM_DESC(major, "Major device number");
  
  static void (*old_machine_restart)(char *command);
  static void __iomem *tb0219_base;
d2a7be0be   Thomas Gleixner   mips: Remove BKL ...
28
  static DEFINE_SPINLOCK(tb0219_lock);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
  
  #define tb0219_read(offset)		readw(tb0219_base + (offset))
  #define tb0219_write(offset, value)	writew((value), tb0219_base + (offset))
  
  #define TB0219_START	0x0a000000UL
  #define TB0219_SIZE	0x20UL
  
  #define TB0219_LED			0x00
  #define TB0219_GPIO_INPUT		0x02
  #define TB0219_GPIO_OUTPUT		0x04
  #define TB0219_DIP_SWITCH		0x06
  #define TB0219_MISC			0x08
  #define TB0219_RESET			0x0e
  #define TB0219_PCI_SLOT1_IRQ_STATUS	0x10
  #define TB0219_PCI_SLOT2_IRQ_STATUS	0x12
  #define TB0219_PCI_SLOT3_IRQ_STATUS	0x14
  
  typedef enum {
  	TYPE_LED,
  	TYPE_GPIO_OUTPUT,
  } tb0219_type_t;
  
  /*
   * Minor device number
   *	 0 = 7 segment LED
   *
   *	16 = GPIO IN 0
   *	17 = GPIO IN 1
   *	18 = GPIO IN 2
   *	19 = GPIO IN 3
   *	20 = GPIO IN 4
   *	21 = GPIO IN 5
   *	22 = GPIO IN 6
   *	23 = GPIO IN 7
   *
   *	32 = GPIO OUT 0
   *	33 = GPIO OUT 1
   *	34 = GPIO OUT 2
   *	35 = GPIO OUT 3
   *	36 = GPIO OUT 4
   *	37 = GPIO OUT 5
   *	38 = GPIO OUT 6
   *	39 = GPIO OUT 7
   *
   *	48 = DIP switch 1
   *	49 = DIP switch 2
   *	50 = DIP switch 3
   *	51 = DIP switch 4
   *	52 = DIP switch 5
   *	53 = DIP switch 6
   *	54 = DIP switch 7
   *	55 = DIP switch 8
   */
  
  static inline char get_led(void)
  {
  	return (char)tb0219_read(TB0219_LED);
  }
  
  static inline char get_gpio_input_pin(unsigned int pin)
  {
  	uint16_t values;
  
  	values = tb0219_read(TB0219_GPIO_INPUT);
  	if (values & (1 << pin))
  		return '1';
  
  	return '0';
  }
  
  static inline char get_gpio_output_pin(unsigned int pin)
  {
  	uint16_t values;
  
  	values = tb0219_read(TB0219_GPIO_OUTPUT);
  	if (values & (1 << pin))
  		return '1';
  
  	return '0';
  }
  
  static inline char get_dip_switch(unsigned int pin)
  {
  	uint16_t values;
  
  	values = tb0219_read(TB0219_DIP_SWITCH);
  	if (values & (1 << pin))
  		return '1';
  
  	return '0';
  }
  
  static inline int set_led(char command)
  {
  	tb0219_write(TB0219_LED, command);
  
  	return 0;
  }
  
  static inline int set_gpio_output_pin(unsigned int pin, char command)
  {
  	unsigned long flags;
  	uint16_t value;
  
  	if (command != '0' && command != '1')
  		return -EINVAL;
  
  	spin_lock_irqsave(&tb0219_lock, flags);
  	value = tb0219_read(TB0219_GPIO_OUTPUT);
  	if (command == '0')
  		value &= ~(1 << pin);
  	else
  		value |= 1 << pin;
  	tb0219_write(TB0219_GPIO_OUTPUT, value);
  	spin_unlock_irqrestore(&tb0219_lock, flags);
  
  	return 0;
  
  }
  
  static ssize_t tanbac_tb0219_read(struct file *file, char __user *buf, size_t len,
                                    loff_t *ppos)
  {
  	unsigned int minor;
  	char value;
496ad9aa8   Al Viro   new helper: file_...
154
  	minor = iminor(file_inode(file));
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
  	switch (minor) {
  	case 0:
  		value = get_led();
  		break;
  	case 16 ... 23:
  		value = get_gpio_input_pin(minor - 16);
  		break;
  	case 32 ... 39:
  		value = get_gpio_output_pin(minor - 32);
  		break;
  	case 48 ... 55:
  		value = get_dip_switch(minor - 48);
  		break;
  	default:
  		return -EBADF;
  	}
  
  	if (len <= 0)
  		return -EFAULT;
  
  	if (put_user(value, buf))
  		return -EFAULT;
  
  	return 1;
  }
  
  static ssize_t tanbac_tb0219_write(struct file *file, const char __user *data,
                                     size_t len, loff_t *ppos)
  {
  	unsigned int minor;
  	tb0219_type_t type;
  	size_t i;
  	int retval = 0;
  	char c;
496ad9aa8   Al Viro   new helper: file_...
189
  	minor = iminor(file_inode(file));
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
  	switch (minor) {
  	case 0:
  		type = TYPE_LED;
  		break;
  	case 32 ... 39:
  		type = TYPE_GPIO_OUTPUT;
  		break;
  	default:
  		return -EBADF;
  	}
  
  	for (i = 0; i < len; i++) {
  		if (get_user(c, data + i))
  			return -EFAULT;
  
  		switch (type) {
  		case TYPE_LED:
  			retval = set_led(c);
  			break;
  		case TYPE_GPIO_OUTPUT:
  			retval = set_gpio_output_pin(minor - 32, c);
  			break;
  		}
  
  		if (retval < 0)
  			break;
  	}
  
  	return i;
  }
  
  static int tanbac_tb0219_open(struct inode *inode, struct file *file)
  {
  	unsigned int minor;
  
  	minor = iminor(inode);
  	switch (minor) {
  	case 0:
  	case 16 ... 23:
  	case 32 ... 39:
  	case 48 ... 55:
c5bf68fe0   Kirill Smelkov   *: convert stream...
231
  		return stream_open(inode, file);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
232
233
234
235
236
237
238
239
240
241
242
  	default:
  		break;
  	}
  
  	return -EBADF;
  }
  
  static int tanbac_tb0219_release(struct inode *inode, struct file *file)
  {
  	return 0;
  }
62322d255   Arjan van de Ven   [PATCH] make more...
243
  static const struct file_operations tb0219_fops = {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
244
245
246
247
248
  	.owner		= THIS_MODULE,
  	.read		= tanbac_tb0219_read,
  	.write		= tanbac_tb0219_write,
  	.open		= tanbac_tb0219_open,
  	.release	= tanbac_tb0219_release,
6038f373a   Arnd Bergmann   llseek: automatic...
249
  	.llseek		= no_llseek,
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
250
251
252
253
254
255
  };
  
  static void tb0219_restart(char *command)
  {
  	tb0219_write(TB0219_RESET, 0);
  }
6e498c108   Yoichi Yuasa   [PATCH] TB0219: a...
256
257
258
259
260
261
262
263
264
265
266
267
268
269
  static void tb0219_pci_irq_init(void)
  {
  	/* PCI Slot 1 */
  	vr41xx_set_irq_trigger(TB0219_PCI_SLOT1_PIN, IRQ_TRIGGER_LEVEL, IRQ_SIGNAL_THROUGH);
  	vr41xx_set_irq_level(TB0219_PCI_SLOT1_PIN, IRQ_LEVEL_LOW);
  
  	/* PCI Slot 2 */
  	vr41xx_set_irq_trigger(TB0219_PCI_SLOT2_PIN, IRQ_TRIGGER_LEVEL, IRQ_SIGNAL_THROUGH);
  	vr41xx_set_irq_level(TB0219_PCI_SLOT2_PIN, IRQ_LEVEL_LOW);
  
  	/* PCI Slot 3 */
  	vr41xx_set_irq_trigger(TB0219_PCI_SLOT3_PIN, IRQ_TRIGGER_LEVEL, IRQ_SIGNAL_THROUGH);
  	vr41xx_set_irq_level(TB0219_PCI_SLOT3_PIN, IRQ_LEVEL_LOW);
  }
2223cbec3   Bill Pemberton   char: remove use ...
270
  static int tb0219_probe(struct platform_device *dev)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
  {
  	int retval;
  
  	if (request_mem_region(TB0219_START, TB0219_SIZE, "TB0219") == NULL)
  		return -EBUSY;
  
  	tb0219_base = ioremap(TB0219_START, TB0219_SIZE);
  	if (tb0219_base == NULL) {
  		release_mem_region(TB0219_START, TB0219_SIZE);
  		return -ENOMEM;
  	}
  
  	retval = register_chrdev(major, "TB0219", &tb0219_fops);
  	if (retval < 0) {
  		iounmap(tb0219_base);
  		tb0219_base = NULL;
  		release_mem_region(TB0219_START, TB0219_SIZE);
  		return retval;
  	}
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
290
291
  	old_machine_restart = _machine_restart;
  	_machine_restart = tb0219_restart;
6e498c108   Yoichi Yuasa   [PATCH] TB0219: a...
292
  	tb0219_pci_irq_init();
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
293
294
295
296
297
298
299
300
  	if (major == 0) {
  		major = retval;
  		printk(KERN_INFO "TB0219: major number %d
  ", major);
  	}
  
  	return 0;
  }
39af33fc4   Bill Pemberton   char: remove use ...
301
  static int tb0219_remove(struct platform_device *dev)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
302
303
304
305
306
307
308
309
310
311
312
313
  {
  	_machine_restart = old_machine_restart;
  
  	iounmap(tb0219_base);
  	tb0219_base = NULL;
  
  	release_mem_region(TB0219_START, TB0219_SIZE);
  
  	return 0;
  }
  
  static struct platform_device *tb0219_platform_device;
3ae5eaec1   Russell King   [DRIVER MODEL] Co...
314
  static struct platform_driver tb0219_device_driver = {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
315
  	.probe		= tb0219_probe,
4dde2d2f3   Bill Pemberton   char: remove use ...
316
  	.remove		= tb0219_remove,
3ae5eaec1   Russell King   [DRIVER MODEL] Co...
317
318
319
  	.driver		= {
  		.name	= "TB0219",
  	},
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
320
  };
d0497614e   Dmitry Torokhov   [PATCH] tb0219: c...
321
  static int __init tanbac_tb0219_init(void)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
322
323
  {
  	int retval;
d0497614e   Dmitry Torokhov   [PATCH] tb0219: c...
324
325
326
327
328
329
330
331
332
  	tb0219_platform_device = platform_device_alloc("TB0219", -1);
  	if (!tb0219_platform_device)
  		return -ENOMEM;
  
  	retval = platform_device_add(tb0219_platform_device);
  	if (retval < 0) {
  		platform_device_put(tb0219_platform_device);
  		return retval;
  	}
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
333

3ae5eaec1   Russell King   [DRIVER MODEL] Co...
334
  	retval = platform_driver_register(&tb0219_device_driver);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
335
336
337
338
339
  	if (retval < 0)
  		platform_device_unregister(tb0219_platform_device);
  
  	return retval;
  }
d0497614e   Dmitry Torokhov   [PATCH] tb0219: c...
340
  static void __exit tanbac_tb0219_exit(void)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
341
  {
3ae5eaec1   Russell King   [DRIVER MODEL] Co...
342
  	platform_driver_unregister(&tb0219_device_driver);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
343
344
345
346
347
  	platform_device_unregister(tb0219_platform_device);
  }
  
  module_init(tanbac_tb0219_init);
  module_exit(tanbac_tb0219_exit);